mirror of
https://git.planet-casio.com/Lephenixnoir/gint.git
synced 2024-12-28 20:43:36 +01:00
More timers, RTC, basic overclock-resistant keyboard, CPG, PFC, driver levels.
This commit is contained in:
parent
2f0e049c33
commit
298338f633
26 changed files with 1326 additions and 149 deletions
12
fx9860g.ld
12
fx9860g.ld
|
@ -19,9 +19,9 @@ MEMORY
|
|||
/* This is mapped to RAM; 8k on SH3, apparently 32k on SH4 */
|
||||
ram (rw): o = 0x08100000, l = 8k
|
||||
/* gint's VBR space, mentioned here for completeness */
|
||||
vbr (rwx): o = 0x8800e000, l = 4k
|
||||
vbr (rwx): o = 0x8800e000, l = 5k
|
||||
/* Some RAM region from P1 area; gint's data will reside here */
|
||||
rram (rwx): o = 0x8800f000, l = 4k
|
||||
rram (rwx): o = 0x8800f400, l = 3k
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
|
@ -76,7 +76,13 @@ SECTIONS
|
|||
driver, even if the symbols are not referenced */
|
||||
.gint.drivers : {
|
||||
_bdrv = . ;
|
||||
KEEP(*(.gint.drivers));
|
||||
KEEP(*(.gint.drivers.0));
|
||||
KEEP(*(.gint.drivers.1));
|
||||
KEEP(*(.gint.drivers.2));
|
||||
KEEP(*(.gint.drivers.3));
|
||||
KEEP(*(.gint.drivers.4));
|
||||
KEEP(*(.gint.drivers.5));
|
||||
KEEP(*(.gint.drivers.6));
|
||||
_edrv = . ;
|
||||
} > rom
|
||||
|
||||
|
|
12
fxcg50.ld
12
fxcg50.ld
|
@ -18,9 +18,9 @@ MEMORY
|
|||
The first 0x2000 bytes are reserved by gint, see below */
|
||||
ram (rw): o = 0x08102000, l = 512k
|
||||
/* gint's VBR space, mentioned here for completeness */
|
||||
vbr (rwx): o = 0x8c160000, l = 4k
|
||||
vbr (rwx): o = 0x8c160000, l = 5k
|
||||
/* Some RAM region from P1 area; gint's data will reside here */
|
||||
rram (rwx): o = 0x8c161000, l = 4k
|
||||
rram (rwx): o = 0x8c161400, l = 3k
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
|
@ -68,7 +68,13 @@ SECTIONS
|
|||
driver, even if the symbols are not referenced */
|
||||
.gint.drivers : {
|
||||
_bdrv = . ;
|
||||
KEEP(*(.gint.drivers));
|
||||
KEEP(*(.gint.drivers.0));
|
||||
KEEP(*(.gint.drivers.1));
|
||||
KEEP(*(.gint.drivers.2));
|
||||
KEEP(*(.gint.drivers.3));
|
||||
KEEP(*(.gint.drivers.4));
|
||||
KEEP(*(.gint.drivers.5));
|
||||
KEEP(*(.gint.drivers.6));
|
||||
_edrv = . ;
|
||||
} > rom
|
||||
|
||||
|
|
80
include/core/cpg.h
Normal file
80
include/core/cpg.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
//---
|
||||
// gint:core:cpg - Clock Pulse Generator
|
||||
//---
|
||||
|
||||
#ifndef GINT_CORE_CPG
|
||||
#define GINT_CORE_CPG
|
||||
|
||||
#include <defs/types.h>
|
||||
|
||||
//---
|
||||
// SH7705 Clock Pulse Generator. Refer to:
|
||||
// "Renesas SH7705 Group Hardware Manual"
|
||||
// Section 9: "Interrupt Controller (INTC)"
|
||||
//---
|
||||
|
||||
/* sh7705_cpg_t - Clock Pulse Generator registers */
|
||||
typedef volatile struct
|
||||
{
|
||||
word_union(FRQCR,
|
||||
uint16_t :3;
|
||||
uint16_t CKOEN :1; /* Clock Output Enable */
|
||||
uint16_t :2;
|
||||
uint16_t STC :2; /* PLL multiplication ratio */
|
||||
uint16_t :2;
|
||||
uint16_t IFC :2; /* Internal clock divider */
|
||||
uint16_t :2;
|
||||
uint16_t PFC :2; /* Peripheral clock divider */
|
||||
);
|
||||
|
||||
} PACKED(4) sh7705_cpg_t;
|
||||
|
||||
#define SH7705_CPG (*((sh7705_cpg_t *)0xffffff80))
|
||||
|
||||
//---
|
||||
// SH7305 Clock Pulse Generator. Refer to:
|
||||
// "Renesas SH7724 User's Manual: Hardware"
|
||||
// Section 17: "Clock Pulse Generator (CPG)"
|
||||
//---
|
||||
|
||||
/* sh7305_cpg_t - Clock Pulse Generator registers
|
||||
Fields marked with [*] don't have the meaning described in the SH7724
|
||||
documentation. */
|
||||
typedef volatile struct
|
||||
{
|
||||
lword_union(FRQCRA,
|
||||
uint32_t KICK :1; /* Flush FRQCRA modifications */
|
||||
uint32_t :1;
|
||||
uint32_t STC :6; /* PLL multiplication [*] */
|
||||
uint32_t IFC :4; /* Iphi divider 1 [*] */
|
||||
uint32_t :4;
|
||||
uint32_t SFC :4; /* Sphi divider 1 [*] */
|
||||
uint32_t BFC :4; /* Bphi divider 1 [*] */
|
||||
uint32_t :4;
|
||||
uint32_t P1FC :4; /* Pphi divider 1 [*] */
|
||||
);
|
||||
pad(0x20);
|
||||
|
||||
lword_union(PLLCR,
|
||||
uint32_t :17;
|
||||
uint32_t PLLE :1; /* PLL Enable */
|
||||
uint32_t :1;
|
||||
uint32_t FLLE :1; /* FLL Enable */
|
||||
uint32_t :10;
|
||||
uint32_t CKOFF :1; /* CKO Output Stop */
|
||||
uint32_t :1;
|
||||
);
|
||||
pad(0x28);
|
||||
|
||||
lword_union(FLLFRQ,
|
||||
uint32_t :16;
|
||||
uint32_t SELXM :2; /* FLL output division */
|
||||
uint32_t :3;
|
||||
uint32_t FLF :11; /* FLL Multiplication Ratio */
|
||||
);
|
||||
|
||||
} PACKED(4) sh7305_cpg_t;
|
||||
|
||||
#define SH7305_CPG (*((sh7305_cpg_t *)0xa4150000))
|
||||
|
||||
#endif /* GINT_CORE_CPG */
|
|
@ -3,13 +3,10 @@
|
|||
//
|
||||
// This component detects the architecture and MPU type of the underlying
|
||||
// hardware by relying on version registers and/or side-information. It
|
||||
// provides macros isSH3() and isSH4(), but the best way of performing
|
||||
// MPU-dependent jobs is to use mpuSwitch():
|
||||
// provides macros isSH3() and isSH4() for MPU-dependent jobs
|
||||
//
|
||||
// mpuSwitch(
|
||||
// print("SH3 code"),
|
||||
// print("SH4 code")
|
||||
// );
|
||||
// if(isSH3()) print("SH3 code");
|
||||
// else print("SH4 code");
|
||||
//---
|
||||
|
||||
#ifndef GINT_CORE_MPU
|
||||
|
@ -40,8 +37,6 @@ typedef enum
|
|||
#define isSH3() (mpu_id() & 1)
|
||||
#define isSH4() (!isSH3())
|
||||
|
||||
#define mpuSwitch(a, b) do { if(isSH3()) { a; } else { b; } } while(0)
|
||||
|
||||
/* mpu_init() - probe the MPU type
|
||||
This function must be executed before mpu_id() can be used. */
|
||||
void mpu_init(void);
|
||||
|
@ -53,8 +48,6 @@ typedef enum
|
|||
#define isSH3() 0
|
||||
#define isSH4() 1
|
||||
|
||||
#define mpuSwitch(a, b) do { b; } while(0)
|
||||
|
||||
#endif /* FX9860G */
|
||||
|
||||
#endif /* GINT_CORE_MPU */
|
||||
|
|
77
include/core/pfc.h
Normal file
77
include/core/pfc.h
Normal file
|
@ -0,0 +1,77 @@
|
|||
//---
|
||||
// gint:core:pfc - Pin Function Controller
|
||||
//
|
||||
// The Pin Function Controller has a simple register interface, the main
|
||||
// difficulty is still understanding the role of its pins.
|
||||
//---
|
||||
|
||||
#ifndef GINT_CORE_PFC
|
||||
#define GINT_CORE_PFC
|
||||
|
||||
#include <defs/types.h>
|
||||
|
||||
//---
|
||||
// SH7705 Pin Function Controller. Refer to:
|
||||
// "Renesas SH7705 Group Hardware Manual"
|
||||
// Section 19: "Pin Function Controller"
|
||||
//---
|
||||
|
||||
typedef volatile struct
|
||||
{
|
||||
/* Control registers */
|
||||
uint16_t PACR;
|
||||
uint16_t PBCR;
|
||||
uint16_t PCCR;
|
||||
uint16_t PDCR;
|
||||
uint16_t PECR;
|
||||
uint16_t PFCR;
|
||||
uint16_t PGCR;
|
||||
uint16_t PHCR;
|
||||
uint16_t PJCR;
|
||||
uint16_t PKCR;
|
||||
uint16_t PLCR;
|
||||
uint16_t SCPCR; /* Port SC control register */
|
||||
uint16_t PMCR;
|
||||
uint16_t PNCR;
|
||||
|
||||
pad(4);
|
||||
|
||||
/* Data registers */
|
||||
uint8_t PADR;
|
||||
pad(1);
|
||||
uint8_t PBDR;
|
||||
pad(1);
|
||||
uint8_t PCDR;
|
||||
pad(1);
|
||||
uint8_t PDDR;
|
||||
pad(1);
|
||||
uint8_t PEDR;
|
||||
pad(1);
|
||||
uint8_t PFDR;
|
||||
pad(1);
|
||||
uint8_t PGDR;
|
||||
pad(1);
|
||||
uint8_t PHDR;
|
||||
pad(1);
|
||||
uint8_t PJDR;
|
||||
pad(1);
|
||||
uint8_t PKDR;
|
||||
pad(1);
|
||||
uint8_t PLDR;
|
||||
pad(1);
|
||||
uint8_t SCPDR; /* Port SC data register */
|
||||
pad(1);
|
||||
uint8_t PMDR;
|
||||
pad(1);
|
||||
uint8_t PNDR;
|
||||
pad(1);
|
||||
|
||||
} PACKED(4) sh7705_pfc_t;
|
||||
|
||||
#define SH7705_PFC (*((sh7705_pfc_t *)0xa4000100))
|
||||
|
||||
//---
|
||||
// TODO: Document the SH7305 Pin Function Controller
|
||||
//---
|
||||
|
||||
#endif /* GINT_CORE_PFC */
|
|
@ -30,6 +30,9 @@
|
|||
#define CTOR(x) __attribute__((constructor ((x) + 101))) PRETEXT
|
||||
#define DTOR(x) __attribute__((destructor ((x) + 101)))
|
||||
|
||||
/* Aligned variables */
|
||||
#define ALIGNED(x) __attribute__((aligned(x)))
|
||||
|
||||
/* Packed structures. I require explicit alignment because if it's unspecified,
|
||||
GCC cannot optimize access size, and reads to memory-mapped I/O with invalid
|
||||
access sizes silently fail - honestly you don't want this to happen */
|
||||
|
|
|
@ -1,8 +1,82 @@
|
|||
//---
|
||||
// gint:clock - Clock signals
|
||||
// gint:clock - Clock signals, overclock, and standby modes
|
||||
//---
|
||||
|
||||
#ifndef GINT_CLOCK
|
||||
#define GINT_CLOCK
|
||||
|
||||
#include <defs/types.h>
|
||||
|
||||
//---
|
||||
// Clock signals
|
||||
//---
|
||||
|
||||
/* clock_frequency_t
|
||||
A dump of the Clock Pulse Generator's (CPG) configuration. Use the MPU
|
||||
detection functions from <gint/mpu.h> to use the correct fields. */
|
||||
typedef struct
|
||||
{
|
||||
union {
|
||||
int PLL1;
|
||||
int FLL;
|
||||
};
|
||||
union {
|
||||
int PLL2;
|
||||
int PLL;
|
||||
};
|
||||
|
||||
int Bphi_div;
|
||||
int Iphi_div;
|
||||
int Pphi_div;
|
||||
|
||||
union {
|
||||
int CKIO_f;
|
||||
int RTCCLK_f;
|
||||
};
|
||||
|
||||
int Bphi_f;
|
||||
int Iphi_f;
|
||||
int Pphi_f;
|
||||
|
||||
} clock_frequency_t;
|
||||
|
||||
/* clock_freq() - get the frequency of the main clocks
|
||||
This function returns the address of a static object which is used by the
|
||||
module; this address never changes. */
|
||||
const clock_frequency_t *clock_freq(void);
|
||||
|
||||
//---
|
||||
// Overclock
|
||||
//---
|
||||
|
||||
/* TODO: All overclock */
|
||||
|
||||
//---
|
||||
// Sleep functions
|
||||
//---
|
||||
|
||||
/* sleep() - halt the processor until an event occurs
|
||||
The function stops the processor until an interrupt is accepted; the
|
||||
duration is not known in advance. This function should be used when the
|
||||
add-in is idle, for instance while waiting for keyboard input. */
|
||||
void sleep(void);
|
||||
|
||||
/* sleep_us() - sleep for a definite duration in microseconds
|
||||
|
||||
Stops the processor until [delay_us] microseconds have elapsed. Interrupts
|
||||
may occur during that time (especially timers firing), in which case the
|
||||
events will be treated as usual. The processor will resume sleeping after
|
||||
handling them.
|
||||
|
||||
The user may choose the timer used to time out the sleep. Remember that only
|
||||
timers 0 to 2 have microsecond-level resolution; other timers count in units
|
||||
of about 30 us.
|
||||
|
||||
@timer Which timer to use to time out the sleep
|
||||
@us_delay How long to sleep (in microseconds) */
|
||||
void sleep_us(int timer, int us_delay);
|
||||
|
||||
/* sleep_ms() - sleep for a definite duration in milliseconds */
|
||||
#define sleep_ms(timer, ms_delay) sleep_us(timer, (ms_delay) * 1000)
|
||||
|
||||
#endif /* GINT_CLOCK */
|
||||
|
|
|
@ -39,6 +39,11 @@ typedef struct
|
|||
may be set to NULL */
|
||||
void (*init)(void);
|
||||
|
||||
/* unload() - unitialize the driver
|
||||
This function is called before ctx_restore() when gint is unloaded.
|
||||
If there is no unload function, the field may be set to NULL */
|
||||
void (*unload)(void);
|
||||
|
||||
/* Size of a context object for the driver */
|
||||
uint ctx_size;
|
||||
|
||||
|
@ -63,11 +68,17 @@ typedef struct
|
|||
} PACKED(4) gint_driver_t;
|
||||
|
||||
/* GINT_DECLARE_DRIVER() - make a driver visible to gint
|
||||
|
||||
Use this macro to expose a driver by passing it the name of a gint_driver_t
|
||||
structure. This macro moves the structure to the .gint.drivers section,
|
||||
which is automatically traversed at startup */
|
||||
#define GINT_DECLARE_DRIVER(name) \
|
||||
SECTION(".gint.drivers") extern gint_driver_t name;
|
||||
structure. This macro moves the structure to the .gint.drivers.* sections,
|
||||
which are automatically traversed at startup.
|
||||
|
||||
The @level argument represents the priority level: lower numbers mean that
|
||||
drivers will be loaded sooner. This numbering allows a primitive form of
|
||||
dependency for drivers. You need to specifiy a level which is strictly
|
||||
higher than the level of all the drivers you depend on. */
|
||||
#define GINT_DECLARE_DRIVER(level, name) \
|
||||
SECTION(".gint.drivers." #level) extern gint_driver_t name;
|
||||
|
||||
/* GINT_DRIVER_SH3() - declare a function for SH3-rectification
|
||||
This macros allows the argument function to not exist on fxcg50. */
|
||||
|
|
|
@ -92,23 +92,27 @@ int gint_intlevel(int intid, int level);
|
|||
When an interrupt request is accepted, the hardware jumps to a specific
|
||||
interrupt handler at an address that depends on the interrupt source.
|
||||
|
||||
Each interrupt handler should only refer to data within its own block
|
||||
because the relative displacement between blocks is MPU-dependent. There are
|
||||
a few exceptions to this, such as timer handlers, which are contiguous on
|
||||
all currently-used platforms. Be careful.
|
||||
For safety, interrupt handlers should avoir referring to data from other
|
||||
blocks because the arrangement of blocks at runtime depends on event codes.
|
||||
The assembler program will assume that consecutive blocks in the source code
|
||||
will be consecutive in memory, which is not always true. Avoiding cross-
|
||||
references is a practical rule to avoid problems. (gint breaks this rule
|
||||
very often but does it carefully... I guess?)
|
||||
|
||||
This function allows anyone to replace any interrupt handler so make sure
|
||||
you're not interfering with usual interrupt assignments.
|
||||
|
||||
The first parameter 'event_code' represents the event_code associated with
|
||||
the interrupt. These codes are normally platform-dependent, but gint always
|
||||
uses the SH7305 codes: SH3 platforms have a translation table. See the
|
||||
the interrupt. The codes are normally platform-dependent, but gint always
|
||||
uses SH7305 codes: SH3 platforms have a translation table. See the
|
||||
documentation for a list of event codes and their associated interrupts.
|
||||
|
||||
The handler function must be an interrupt handler: it should not raise
|
||||
exceptions, must end with 'rte', uses the kernel register bank... and it
|
||||
must fit within 32 bytes. If it's not written in assembler, then you're
|
||||
likely doing something wrong.
|
||||
The handler function must be an interrupt handler: it must not raise
|
||||
exceptions, must end with 'rte', and it will use the kernel register bank.
|
||||
For convenience I allow any block size to be loaded as an interrupt handler,
|
||||
but it should really be a multiple of 32 bytes and not override other
|
||||
handlers. If it's not written in assembler, then you're likely doing
|
||||
something wrong.
|
||||
|
||||
It is common for interrupt handlers to have a few bytes of data, such as the
|
||||
address of a callback function. gint often stores this data in the last
|
||||
|
@ -117,7 +121,8 @@ int gint_intlevel(int intid, int level);
|
|||
|
||||
@event_code Identifier of the interrupt block
|
||||
@handler Address of handler function
|
||||
@size How many bytes to copy
|
||||
Returns the VBR address where the handlers was installed. */
|
||||
void *gint_inthandler(int event_code, const void *handler);
|
||||
void *gint_inthandler(int event_code, const void *handler, size_t size);
|
||||
|
||||
#endif /* GINT_GINT */
|
||||
|
|
67
include/gint/keyboard.h
Normal file
67
include/gint/keyboard.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
//---
|
||||
// gint:keyboard - Keyboard input
|
||||
//---
|
||||
|
||||
#ifndef GINT_KEYBOARD
|
||||
#define GINT_KEYBOARD
|
||||
|
||||
#include <defs/types.h>
|
||||
|
||||
/* key_event_t - any keyboard event
|
||||
This structure represents an event that occurs on the keyboard. This is a
|
||||
low-level structure that is produced by the keyboard scanner. It reports key
|
||||
presses, key releases, and key repetitions.
|
||||
|
||||
These events are detected and reported each time the keyboard is scanned,
|
||||
which is 16 Hz by default, so you'll get 16 repeat events by second if a key
|
||||
is kept pressed. We can filter the events to emit one only every second, for
|
||||
example, but it's difficult to do it for all keys at the same time. Thus the
|
||||
control of repetition delays is restricted to getkey().
|
||||
|
||||
When mod = 1, shift and alpha indicate whether the key has been modified.
|
||||
This is only possible for key press events returned by getkey(). Note that
|
||||
you can't have key = shift and mod = 1 at the same time.
|
||||
|
||||
The time attribute indicates when the event occurred. This value increases
|
||||
at each keyboard scan and *it wraps around every 4 minutes* (at 16 Hz).
|
||||
I expect this attribute to be useful to analyze combo sequences in games.
|
||||
Make sure you are aware of the two nitpicks:
|
||||
- Don't keep the time values for too long because the wrap-around effect.
|
||||
- 0xfff is just before 0x000, not long after. */
|
||||
typedef struct
|
||||
{
|
||||
uint time :12; /* Time of event, unique over short periods */
|
||||
|
||||
uint :7; /* Reserved for future use */
|
||||
|
||||
uint mod :1; /* Whether modifiers are used */
|
||||
uint shift :1; /* If mod=1, whether SHIFT was pressed */
|
||||
uint alpha :1; /* If mod=1, whether ALPHA was pressed */
|
||||
|
||||
uint type :2; /* Type of key event */
|
||||
uint key :8; /* Hit key */
|
||||
|
||||
} PACKED(4) key_event_t;
|
||||
|
||||
/* Keyboard event types, as in the type field of key_event_t */
|
||||
enum
|
||||
{
|
||||
KEYEV_NONE = 0, /* No event available (poll() only) */
|
||||
KEYEV_DOWN = 1, /* Key was pressed */
|
||||
KEYEV_UP = 2, /* Key was released */
|
||||
KEYEV_HOLD = 3, /* A key that was pressed has been held down */
|
||||
};
|
||||
|
||||
/* Size of the buffer event queue, can be customized using gint's configure
|
||||
script before compiling the library. Better be a power of 2. */
|
||||
#ifndef KEYBOARD_QUEUE_SIZE
|
||||
#define KEYBOARD_QUEUE_SIZE 64
|
||||
#endif
|
||||
|
||||
//---
|
||||
// Keyboard functions
|
||||
//---
|
||||
|
||||
key_event_t key_poll(void);
|
||||
|
||||
#endif /* GINT_KEYBOARD */
|
82
include/gint/rtc.h
Normal file
82
include/gint/rtc.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
//---
|
||||
// gint:rtc - Real-Time Clock
|
||||
//---
|
||||
|
||||
#ifndef GINT_RTC
|
||||
#define GINT_RTC
|
||||
|
||||
#include <defs/types.h>
|
||||
|
||||
//---
|
||||
// Time management
|
||||
//---
|
||||
|
||||
/* rtc_time_t - a point in time, representable in the RTC registers */
|
||||
typedef struct
|
||||
{
|
||||
uint16_t year; /* Years (exact value, e.g. 2018) */
|
||||
uint8_t week_day; /* Day of week, (0=Sunday, 6=Saturday) */
|
||||
uint8_t month; /* Month (0..11) */
|
||||
uint8_t month_day; /* Day of month (1..31) */
|
||||
uint8_t hours; /* Hour (0..23) */
|
||||
uint8_t minutes; /* Minute (0..59) */
|
||||
uint8_t seconds; // Second (0..59) */
|
||||
|
||||
} rtc_time_t;
|
||||
|
||||
/* rtc_get_time() - read the current time from the RTC
|
||||
@time Pointer to rtc_time_t structure (needs not be initialized) */
|
||||
void rtc_get_time(rtc_time_t *time);
|
||||
|
||||
/* rtc_set_time() - write a new current time to the RTC
|
||||
If [time->week_day] is not in the valid range, it is set to 0. Other fields
|
||||
are not checked.
|
||||
@time Pointer to new time */
|
||||
void rtc_set_time(const rtc_time_t *time);
|
||||
|
||||
//---
|
||||
// RTC timer
|
||||
// The real-time clock produces a regular interrupt which may be used as a
|
||||
// timer with a maximum frequency of 256 Hz. It is also useful to check
|
||||
// that the clock settings (see <gint/clock.h>) are properly detected, by
|
||||
// comparing the detected frequencies with the RTC.
|
||||
//---
|
||||
|
||||
/* rtc_frequency_t - possible frequency settings for the RTC's interrupt */
|
||||
typedef enum
|
||||
{
|
||||
rtc_500mHz = 7,
|
||||
rtc_1Hz = 6,
|
||||
rtc_2Hz = 5,
|
||||
rtc_4Hz = 4,
|
||||
rtc_16Hz = 3,
|
||||
rtc_64Hz = 2,
|
||||
rtc_256Hz = 1,
|
||||
rtc_none = 0,
|
||||
|
||||
} rtc_frequency_t;
|
||||
|
||||
/* rtc_start_timer() - configure the RTC timer
|
||||
|
||||
This function sets up the RTC timer to invoke the provided callback function
|
||||
with its argument regularly. This works like normal timers (<gint/timer.h>);
|
||||
[callback] is passed [arg] as argument and the RTC timer is stopped if it
|
||||
returns non-zero.
|
||||
|
||||
This function will replace any existing callback!
|
||||
|
||||
Note that, as opposed to timers, it is not possible to know how much time
|
||||
will elapse before the callback will first be called. For instance, setting
|
||||
up a 1 Hz-callback when the current time ends in 950 ms will result in the
|
||||
callback being called after 50 ms, then every second. This is not a problem
|
||||
for most uses. */
|
||||
void rtc_start_timer(rtc_frequency_t freq, int (*callback)(void *arg),
|
||||
void *arg);
|
||||
|
||||
/* rtc_stop_timer() - stop the RTC timer
|
||||
This function stops the RTC timer that was set up with rtc_start_timer(). If
|
||||
the decision of stopping the timer comes from the callback, it is preferable
|
||||
to return non-zero. */
|
||||
void rtc_stop_timer(void);
|
||||
|
||||
#endif /* GINT_RTC */
|
|
@ -29,13 +29,7 @@
|
|||
what you intended. */
|
||||
|
||||
/* timer_count() - tells how many timers are available on the platform */
|
||||
HDRFUNC int timer_count(void)
|
||||
{
|
||||
mpuSwitch(
|
||||
return 4, /* SH3-based */
|
||||
return 9 /* SH4-based */
|
||||
);
|
||||
}
|
||||
#define timer_count() (isSH3() ? 4 : 9)
|
||||
|
||||
/* Clock input
|
||||
|
||||
|
@ -123,21 +117,15 @@ uint32_t timer_delay(int timer, int delay_us);
|
|||
@timer Timer id, as returned by timer_setup() */
|
||||
void timer_start(int timer);
|
||||
|
||||
/* timer_reload() - change a timer's delay and source
|
||||
/* timer_reload() - change a timer's delay constant for next interrupts
|
||||
|
||||
Changes the delay and source of the given timer. This function does not
|
||||
start the timer if it stopped. The timer will start counting down from the
|
||||
new delay when it is started (or immediately if it was running), no matter
|
||||
how much time has elapsed since it last fired. Accumulated events are not
|
||||
dropped.
|
||||
|
||||
In cases where choosing a delay and input clock is difficult (such as when
|
||||
using overclocking), use the timer_delay() function.
|
||||
Changes the delay constant of the given timer. Nothing will happen until the
|
||||
next callback; then the timer will update its delay to reflect the new
|
||||
constant. The new delay can be calculated by the timer_delay() function.
|
||||
|
||||
@timer Timer id, as returned by timer_setup()
|
||||
@delay New delay (unit depends on the clock source)
|
||||
@clock New clock source to be used by the timer */
|
||||
void timer_reload(int timer, uint32_t delay, timer_input_t clock);
|
||||
@delay New delay (unit depends on the clock source) */
|
||||
void timer_reload(int timer, uint32_t delay);
|
||||
|
||||
/* timer_pause() - stop a running timer
|
||||
The specified timer will be paused; its counter will not be reset. A stopped
|
||||
|
@ -147,11 +135,11 @@ void timer_reload(int timer, uint32_t delay, timer_input_t clock);
|
|||
@timer Timer id, as returned by timer_setup() */
|
||||
void timer_pause(int timer);
|
||||
|
||||
/* timer_free() - free a timer
|
||||
/* timer_stop() - stop and free a timer
|
||||
Stops and destroys a timer, making its id free for re-use. The id must not
|
||||
be used anymore until it is returned by a further call to timer_setup().
|
||||
|
||||
@timer Timer id, as returned by timer_setup() */
|
||||
void timer_free(int timer);
|
||||
void timer_stop(int timer);
|
||||
|
||||
#endif /* GINT_TIMER */
|
||||
|
|
76
include/mod/rtc.h
Normal file
76
include/mod/rtc.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
//---
|
||||
// gint:mod:rtc - Real-Time Clock
|
||||
//---
|
||||
|
||||
#ifndef GINT_CORE_RTC
|
||||
#define GINT_CORE_RTC
|
||||
|
||||
//---
|
||||
// Hybrid SH7705-SH7305 Real-Time Clock. Refer to:
|
||||
// "Renesas SH7705 Group Hardware Manual"
|
||||
// Section 15: "Real-Time Clock (RTC)"
|
||||
// "Renesas SH7724 User's Manual: Hardware"
|
||||
// Section 28: "Real-Time Clock (RTC)"
|
||||
//---
|
||||
|
||||
/* rtc_BCD2_t - a 2-digit BCD counter with a 1-byte gap */
|
||||
typedef struct
|
||||
{
|
||||
byte_union(,
|
||||
uint8_t TENS :4;
|
||||
uint8_t ONES :4;
|
||||
);
|
||||
pad(1);
|
||||
|
||||
} PACKED(2) rtc_BCD2_t;
|
||||
|
||||
/* sh7705_rtc_t, sh7305_rtc_t - Date and time access, RTC control */
|
||||
typedef volatile struct
|
||||
{
|
||||
uint8_t const R64CNT; /* A 64-Hz counter */
|
||||
pad(1);
|
||||
|
||||
rtc_BCD2_t RSECCNT; /* Second count */
|
||||
rtc_BCD2_t RMINCNT; /* Minute count */
|
||||
rtc_BCD2_t RHRCNT; /* Hour count */
|
||||
|
||||
uint8_t RWKCNT; /* Day of week, must be in [0..6] */
|
||||
pad(1);
|
||||
|
||||
rtc_BCD2_t RDAYCNT; /* Day count */
|
||||
rtc_BCD2_t RMONCNT; /* Month count */
|
||||
|
||||
word_union(RYRCNT, /* Year count */
|
||||
uint THOUSANDS :4;
|
||||
uint HUNDREDS :4;
|
||||
uint TENS :4;
|
||||
uint ONES :4;
|
||||
);
|
||||
pad(12); /* Alarm registers... */
|
||||
|
||||
byte_union(RCR1,
|
||||
uint8_t CF :1; /* Carry flag */
|
||||
uint8_t :2;
|
||||
uint8_t CIE :1; /* Carry interrupt enable */
|
||||
uint8_t AIE :1; /* Alarm interrupt enable */
|
||||
uint8_t :2;
|
||||
uint8_t AF :1; /* Alarm flag */
|
||||
);
|
||||
pad(1);
|
||||
|
||||
byte_union(RCR2,
|
||||
uint8_t PEF :1; /* Periodic interrupt flag */
|
||||
uint8_t PES :3; /* Periodic interrupt interval */
|
||||
uint8_t :1;
|
||||
uint8_t ADJ :1; /* 30-second adjustment */
|
||||
uint8_t RESET :1; /* Reset trigger */
|
||||
uint8_t START :1; /* Start bit */
|
||||
);
|
||||
pad(1);
|
||||
|
||||
} PACKED(4) rtc_t;
|
||||
|
||||
#define SH7705_RTC (*((rtc_t *)0xfffffec0))
|
||||
#define SH7305_RTC (*((rtc_t *)0xa413fec0))
|
||||
|
||||
#endif /* GINT_CORE_RTC */
|
136
src/clock/freq.c
136
src/clock/freq.c
|
@ -0,0 +1,136 @@
|
|||
//---
|
||||
// gint:core:freq - Clock frequency management
|
||||
//---
|
||||
|
||||
#include <gint/drivers.h>
|
||||
#include <gint/clock.h>
|
||||
|
||||
#include <defs/types.h>
|
||||
#include <core/mpu.h>
|
||||
#include <core/cpg.h>
|
||||
|
||||
//---
|
||||
// Driver storage
|
||||
//---
|
||||
|
||||
/* Local copy of the CPG settings */
|
||||
GBSS static clock_frequency_t freq;
|
||||
|
||||
/* clock_freq() - get the frequency of the main clocks */
|
||||
const clock_frequency_t *clock_freq(void)
|
||||
{
|
||||
return &freq;
|
||||
}
|
||||
|
||||
//---
|
||||
// SH7705 Clock signals
|
||||
//---
|
||||
|
||||
#ifdef FX9860G
|
||||
#define CPG SH7705_CPG
|
||||
|
||||
static void sh7705_probe(void)
|
||||
{
|
||||
/* According to Sentaro21 in the sources of Ftune 1.0.1, the clock mode
|
||||
is thought to be 5, which means that:
|
||||
- CPG input is XTAL (14.745'600 MHz)
|
||||
- PLL2 is active and *2 (29.491'200 MHz)
|
||||
- CKIO is output from PLL2 (29.491'200 MHz) */
|
||||
int xtal = 14745600;
|
||||
int pll2 = 2;
|
||||
int ckio = xtal * pll2;
|
||||
|
||||
/* This signal is multiplied by the PLL1 circuit */
|
||||
int pll1 = CPG.FRQCR.STC + 1;
|
||||
|
||||
/* Iphi and Pphi have dividers (Bphi is always equal to CKIO) */
|
||||
int idiv = CPG.FRQCR.IFC;
|
||||
int pdiv = CPG.FRQCR.PFC;
|
||||
|
||||
/* Fill in the setting structure */
|
||||
freq.PLL1 = pll1;
|
||||
freq.PLL2 = pll2;
|
||||
freq.Bphi_div = 1;
|
||||
freq.Iphi_div = idiv + 1;
|
||||
freq.Pphi_div = pdiv + 1;
|
||||
|
||||
/* Deduce the frequency of the main clocks. The following piece of code
|
||||
hardcodes ckio / 3 and avoids using the division operator */
|
||||
int ckio_3 = 9830400;
|
||||
|
||||
/* Exchange the setting values 2 and 3 */
|
||||
idiv = idiv ^ (idiv >> 1);
|
||||
pdiv = pdiv ^ (pdiv >> 1);
|
||||
|
||||
freq.CKIO_f = ckio;
|
||||
freq.Bphi_f = ckio;
|
||||
freq.Iphi_f = (idiv == 3) ? ckio_3 : ckio >> idiv;
|
||||
freq.Pphi_f = (pdiv == 3) ? ckio_3 : ckio >> pdiv;
|
||||
}
|
||||
|
||||
#undef CPG
|
||||
#endif /* FX9860G */
|
||||
|
||||
//---
|
||||
// SH7305 clock signals
|
||||
//---
|
||||
|
||||
#define CPG SH7305_CPG
|
||||
|
||||
static void sh7305_probe(void)
|
||||
{
|
||||
/* The meaning of the PLL setting on SH7305 differs from the
|
||||
documentation of SH7224; the setting must not be doubled. */
|
||||
int pll = CPG.FRQCRA.STC + 1;
|
||||
freq.PLL = pll;
|
||||
|
||||
/* The FLL ratio is the value of the setting, possibly halved */
|
||||
int fll = CPG.FLLFRQ.FLF;
|
||||
if(CPG.FLLFRQ.SELXM == 1) fll >>= 1;
|
||||
freq.FLL = fll;
|
||||
|
||||
/* On SH7724, the divider ratio is given by 1 / (setting + 1), but here
|
||||
it's actually 1 / (2^setting + 1). */
|
||||
|
||||
int divb = CPG.FRQCRA.BFC;
|
||||
int divi = CPG.FRQCRA.IFC;
|
||||
int divp = CPG.FRQCRA.P1FC;
|
||||
|
||||
freq.Bphi_div = 1 << (divb + 1);
|
||||
freq.Iphi_div = 1 << (divi + 1);
|
||||
freq.Pphi_div = 1 << (divp + 1);
|
||||
|
||||
/* Deduce the input frequency of divider 1 */
|
||||
int base = 32768;
|
||||
if(CPG.PLLCR.FLLE) base *= fll;
|
||||
if(CPG.PLLCR.PLLE) base *= pll;
|
||||
|
||||
/* And the frequency of all other input clocks */
|
||||
freq.RTCCLK_f = 32768;
|
||||
freq.Bphi_f = base >> (divb + 1);
|
||||
freq.Iphi_f = base >> (divi + 1);
|
||||
freq.Pphi_f = base >> (divp + 1);
|
||||
}
|
||||
|
||||
#undef CPG
|
||||
|
||||
//---
|
||||
// Other driver stuff
|
||||
//---
|
||||
|
||||
static void init(void)
|
||||
{
|
||||
isSH3() ? sh7705_probe()
|
||||
: sh7305_probe();
|
||||
}
|
||||
|
||||
gint_driver_t drv_cpg = {
|
||||
.name = "Clock Pulse Generator",
|
||||
.init = init,
|
||||
.ctx_size = 0,
|
||||
.sys_ctx = NULL,
|
||||
.ctx_save = NULL,
|
||||
.ctx_restore = NULL,
|
||||
};
|
||||
|
||||
GINT_DECLARE_DRIVER(1, drv_cpg);
|
0
src/clock/sleep.c
Normal file
0
src/clock/sleep.c
Normal file
|
@ -84,8 +84,8 @@ void bootlog_kernel(void)
|
|||
{
|
||||
print(15, 1, " Kernel");
|
||||
|
||||
mpuSwitch({
|
||||
/* SH3-based */
|
||||
if(isSH3())
|
||||
{
|
||||
print(1, 5, "ABCD");
|
||||
print_hex( 6, 5, INTC3._.IPRA->word, 4);
|
||||
print_hex(10, 5, INTC3._.IPRB->word, 4);
|
||||
|
@ -96,8 +96,9 @@ void bootlog_kernel(void)
|
|||
print_hex(10, 6, INTC3._.IPRF->word, 4);
|
||||
print_hex(14, 6, INTC3._.IPRG->word, 4);
|
||||
print_hex(18, 6, INTC3._.IPRH->word, 4);
|
||||
},{
|
||||
/* SH4-based */
|
||||
}
|
||||
else
|
||||
{
|
||||
print(1, 5, "ACFG");
|
||||
print_hex( 6, 5, INTC4._->IPRA.word, 4);
|
||||
print_hex(10, 5, INTC4._->IPRC.word, 4);
|
||||
|
@ -108,7 +109,7 @@ void bootlog_kernel(void)
|
|||
print_hex(10, 6, INTC4._->IPRJ.word, 4);
|
||||
print_hex(14, 6, INTC4._->IPRK.word, 4);
|
||||
print_hex(18, 6, INTC4._->IPRL.word, 4);
|
||||
});
|
||||
}
|
||||
|
||||
Bdisp_PutDisp_DD();
|
||||
}
|
||||
|
|
|
@ -36,10 +36,9 @@ int gint_intlevel(int intid, int level)
|
|||
volatile uint16_t *ipr;
|
||||
level &= 0xf;
|
||||
|
||||
mpuSwitch(
|
||||
ipr = INTC3.IPRS[intid >> 2], /* SH3-based */
|
||||
ipr = &INTC4.IPRS[2 * (intid >> 2)] /* SH4-based */
|
||||
);
|
||||
ipr = isSH3()
|
||||
? INTC3.IPRS[intid >> 2] /* SH3-based */
|
||||
: &INTC4.IPRS[2 * (intid >> 2)]; /* SH4-based */
|
||||
|
||||
int oldlevel = (*ipr >> shift) & 0xf;
|
||||
*ipr = (*ipr & ~(0xf << shift)) | (level << shift);
|
||||
|
@ -48,7 +47,7 @@ int gint_intlevel(int intid, int level)
|
|||
}
|
||||
|
||||
/* gint_inthandler() - configure interrupt handlers */
|
||||
void *gint_inthandler(int event_code, const void *handler)
|
||||
void *gint_inthandler(int event_code, const void *handler, size_t size)
|
||||
{
|
||||
extern char gint_vbr;
|
||||
|
||||
|
@ -60,5 +59,5 @@ void *gint_inthandler(int event_code, const void *handler)
|
|||
if(event_code < 0) return NULL;
|
||||
|
||||
void *dest = (void *)&gint_vbr + event_code + 0x620;
|
||||
return memcpy(dest, handler, 32);
|
||||
return memcpy(dest, handler, size);
|
||||
}
|
||||
|
|
|
@ -30,24 +30,28 @@ typedef struct
|
|||
@arg ctx gint core context object */
|
||||
static void gint_ctx_save(gint_core_ctx *ctx)
|
||||
{
|
||||
mpuSwitch(
|
||||
/* SH3-based */
|
||||
for(int i = 0; i < 8; i++) ctx->iprs[i] = *(INTC3.IPRS[i]),
|
||||
/* SH4-based */
|
||||
for(int i = 0; i < 12; i++) ctx->iprs[i] = INTC4.IPRS[2 * i]
|
||||
);
|
||||
if(isSH3())
|
||||
{
|
||||
for(int i = 0; i < 8; i++) ctx->iprs[i] = *(INTC3.IPRS[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < 12; i++) ctx->iprs[i] = INTC4.IPRS[2 * i];
|
||||
}
|
||||
}
|
||||
|
||||
/* gint_ctx_restore() - restore interrupt controller configuration
|
||||
@arg ctx gint core context object */
|
||||
static void gint_ctx_restore(gint_core_ctx *ctx)
|
||||
{
|
||||
mpuSwitch(
|
||||
/* SH3-based */
|
||||
for(int i = 0; i < 8; i++) *(INTC3.IPRS[i]) = ctx->iprs[i],
|
||||
/* SH4-based */
|
||||
for(int i = 0; i < 12; i++) INTC4.IPRS[2 * i] = ctx->iprs[i]
|
||||
);
|
||||
if(isSH3())
|
||||
{
|
||||
for(int i = 0; i < 8; i++) *(INTC3.IPRS[i]) = ctx->iprs[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < 12; i++) INTC4.IPRS[2 * i] = ctx->iprs[i];
|
||||
}
|
||||
}
|
||||
|
||||
//---
|
||||
|
@ -61,12 +65,14 @@ GBSS static gint_core_ctx sys_ctx;
|
|||
static void lock(void)
|
||||
{
|
||||
/* Just disable everything, drivers will enable what they support */
|
||||
mpuSwitch(
|
||||
/* SH3-based */
|
||||
for(int i = 0; i < 8; i++) *(INTC3.IPRS[i]) = 0x0000,
|
||||
/* SH4-based */
|
||||
for(int i = 0; i < 12; i++) INTC4.IPRS[2 * i] = 0x0000
|
||||
);
|
||||
if(isSH3())
|
||||
{
|
||||
for(int i = 0; i < 8; i++) *(INTC3.IPRS[i]) = 0x0000;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < 12; i++) INTC4.IPRS[2 * i] = 0x0000;
|
||||
}
|
||||
}
|
||||
|
||||
/* gint_install() - install and start gint */
|
||||
|
@ -87,19 +93,20 @@ void gint_install(void)
|
|||
/* Load the event handler entry points into memory */
|
||||
/* TODO: Load an exception handler and a TLB miss handler */
|
||||
|
||||
mpuSwitch({
|
||||
/* SH3-based */
|
||||
if(isSH3())
|
||||
{
|
||||
extern void exch_entry_7705(void);
|
||||
extern void inth_entry_7705(void);
|
||||
exch_entry = exch_entry_7705;
|
||||
inth_entry = inth_entry_7705;
|
||||
},{
|
||||
/* SH4-based */
|
||||
}
|
||||
else
|
||||
{
|
||||
extern void exch_entry_7305(void);
|
||||
extern void inth_entry_7305(void);
|
||||
exch_entry = exch_entry_7305;
|
||||
inth_entry = inth_entry_7305;
|
||||
});
|
||||
}
|
||||
|
||||
memcpy((void *)(vbr + 0x100), exch_entry, 32);
|
||||
memcpy((void *)(vbr + 0x600), inth_entry, 32);
|
||||
|
@ -113,10 +120,12 @@ static void unlock(void)
|
|||
{
|
||||
gint_ctx_restore(&sys_ctx);
|
||||
|
||||
/* Restore all driver settings */
|
||||
for(gint_driver_t *drv = &bdrv; drv < &edrv; drv++)
|
||||
/* Restore all driver settings, but do it in reverse order of loading
|
||||
to honor the dependency system */
|
||||
for(gint_driver_t *drv = &edrv; (--drv) >= &edrv;)
|
||||
{
|
||||
drv->ctx_restore(drv->sys_ctx);
|
||||
if(drv->unload) drv->unload();
|
||||
if(drv->ctx_restore) drv->ctx_restore(drv->sys_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -133,10 +133,8 @@ int start(int isappli, int optnum)
|
|||
|
||||
/* Count how much memory got mapped from this process */
|
||||
uint32_t rom, ram;
|
||||
mpuSwitch(
|
||||
tlb_mapped_memory(&rom, &ram), /* SH3-based */
|
||||
utlb_mapped_memory(&rom, &ram) /* SH4-based */
|
||||
);
|
||||
isSH3() ? tlb_mapped_memory(&rom, &ram)
|
||||
: utlb_mapped_memory(&rom, &ram);
|
||||
|
||||
#ifdef GINT_BOOT_LOG
|
||||
bootlog_mapped(rom, ram);
|
||||
|
@ -155,7 +153,8 @@ int start(int isappli, int optnum)
|
|||
print(1, 3, "<");
|
||||
print_hex(1, 4, (uint32_t)&srom, 8);
|
||||
Bdisp_PutDisp_DD();
|
||||
delay(20);
|
||||
while(1);
|
||||
// delay(20);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -178,7 +177,7 @@ int start(int isappli, int optnum)
|
|||
for(drv = &bdrv; drv < &edrv; drv++)
|
||||
{
|
||||
if(isSH3() && drv->driver_sh3) drv->driver_sh3();
|
||||
drv->ctx_save(drv->sys_ctx);
|
||||
if(drv->ctx_save) drv->ctx_save(drv->sys_ctx);
|
||||
if(drv->init) drv->init();
|
||||
}
|
||||
|
||||
|
@ -197,8 +196,8 @@ int start(int isappli, int optnum)
|
|||
|
||||
/* Before leaving the application, we need to clean up our mess. We
|
||||
have changed many OS settings while accessing the peripheral
|
||||
modules. The OS is bound to be confused (and crash) if we don't
|
||||
restore them */
|
||||
modules. The OS is bound to be confused (and hang, or crash, or any
|
||||
other kind of giving up) if we don't restore them */
|
||||
|
||||
/* Unload gint and give back control to the system. Driver settings
|
||||
will be restored while interrupts are disabled */
|
||||
|
|
114
src/keysc/iokbd.c
Normal file
114
src/keysc/iokbd.c
Normal file
|
@ -0,0 +1,114 @@
|
|||
//---
|
||||
// gint:keysc:iokbd - I/O-based keyboard input
|
||||
//---
|
||||
|
||||
#include <defs/types.h>
|
||||
#include <core/pfc.h>
|
||||
|
||||
/* This file is SH7705-only. */
|
||||
#ifdef FX9860G
|
||||
#define PFC SH7705_PFC
|
||||
|
||||
/* iokbd_delay() - wait a bit so that I/O can keep up
|
||||
May use the watchdog timer, but the keyboard driver will need to save it. */
|
||||
static void iokbd_delay(void)
|
||||
{
|
||||
__asm__(
|
||||
"nop\n\t"
|
||||
"nop\n\t"
|
||||
"nop\n\t"
|
||||
"nop\n\t"
|
||||
);
|
||||
|
||||
#if 0
|
||||
/* Watchdog delay version */
|
||||
const int delay = 0xf4;
|
||||
|
||||
/* Disable the watchdog timer interrupt and reset configuration */
|
||||
INTC.IPRB.BIT._WDT = 0;
|
||||
WDT.WTCSR.WRITE = 0xa500;
|
||||
|
||||
/* Set the delay, input on Pphi / 256 and start counting */
|
||||
WDT.WTCNT.WRITE = 0x5a00 | (delay & 0xff);
|
||||
WDT.WTCSR.WRITE = 0xa505;
|
||||
WDT.WTCSR.WRITE = 0xa585;
|
||||
|
||||
/* Actively wait for overflow, then clear the interrupt flag */
|
||||
while((WDT.WTCSR.READ.BYTE & 0x08) == 0);
|
||||
WDT.WTCSR.WRITE = 0xa500 | (WDT.WTCSR.READ.BYTE & 0xf7);
|
||||
|
||||
/* Reset configuration, counter, and re-enabled interrupt */
|
||||
WDT.WTCSR.WRITE = 0xa500;
|
||||
WDT.WTCSR.WRITE = 0x5a00;
|
||||
INTC.IPRB.BIT._WDT = GINT_INTP_WDT;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* iokbd_row() - acquire hit status of a single row from the I/O ports
|
||||
@row Requested row number, 0..9
|
||||
Returns 8 bits of key state. */
|
||||
uint8_t iokbd_row(int row)
|
||||
{
|
||||
if((unsigned)row > 9) return 0x00;
|
||||
row ^= 1;
|
||||
|
||||
/* This will enable output (01) on @row, input (10) everywhere else */
|
||||
uint16_t ctrl_mask = 0x0003 << ((row & 7) * 2);
|
||||
/* Enable output (0) on @row, input (1) everywhere else */
|
||||
uint8_t data_mask = ~(1 << (row & 7));
|
||||
|
||||
/* When row < 8, the associated bits are in port B */
|
||||
if(row < 8)
|
||||
{
|
||||
/* Set @row as output in port B; port M is unused */
|
||||
PFC.PBCR = 0xaaaa ^ ctrl_mask;
|
||||
PFC.PMCR = (PFC.PMCR & 0xff00) | 0x00aa;
|
||||
iokbd_delay();
|
||||
|
||||
/* Set @row to 0, everything else to 1 */
|
||||
PFC.PBDR = data_mask;
|
||||
PFC.PMDR = (PFC.PMDR & 0xf0) | 0x0f;
|
||||
iokbd_delay();
|
||||
}
|
||||
/* When row >= 8, the associated bits are in port M */
|
||||
else
|
||||
{
|
||||
/* Set @row as output in port M; port B is unused */
|
||||
PFC.PBCR = 0xaaaa;
|
||||
PFC.PMCR = (PFC.PMCR & 0xff00) | (0x00aa ^ ctrl_mask);
|
||||
iokbd_delay();
|
||||
|
||||
/* Set @row to 0, everything else to 1 */
|
||||
PFC.PBDR = 0xff;
|
||||
PFC.PMDR = PFC.PMDR & data_mask;
|
||||
}
|
||||
|
||||
/* Now read the input data from the keyboard! */
|
||||
uint8_t input = ~PFC.PADR;
|
||||
iokbd_delay();
|
||||
|
||||
/* Reset the port configuration. I don't know why the intermediate step
|
||||
is necessary; refer to SimLo's documentation. */
|
||||
PFC.PBCR = 0xaaaa;
|
||||
PFC.PMCR = (PFC.PMCR & 0xff00) | 0x00aa;
|
||||
iokbd_delay();
|
||||
PFC.PBCR = 0x5555;
|
||||
PFC.PMCR = (PFC.PMCR & 0xff00) | 0x0055;
|
||||
iokbd_delay();
|
||||
|
||||
/* Now also reset the data registers. This was forgotten from SimLo's
|
||||
CheckKeyRow() and blows up everything. */
|
||||
PFC.PBDR = 0x00;
|
||||
PFC.PMDR = PFC.PMDR & 0xf0;
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
void iokbd_scan(uint8_t *scan)
|
||||
{
|
||||
/* Scan each row independently; the gain from scanning them altogether
|
||||
is probably not worth it */
|
||||
for(int i = 0; i < 12; i++) scan[i] = iokbd_row(i);
|
||||
}
|
||||
|
||||
#endif /* FX9860G */
|
188
src/keysc/keysc.c
Normal file
188
src/keysc/keysc.c
Normal file
|
@ -0,0 +1,188 @@
|
|||
//---
|
||||
// gint:keysc - The SH7305 and I/O Key Scan Interfaces
|
||||
//---
|
||||
|
||||
#include <gint/drivers.h>
|
||||
#include <gint/gint.h>
|
||||
#include <gint/timer.h>
|
||||
#include <gint/keyboard.h>
|
||||
|
||||
#include <core/mpu.h>
|
||||
#include <defs/attributes.h>
|
||||
|
||||
//---
|
||||
// Keyboard buffer
|
||||
//---
|
||||
|
||||
/* The driver's internal state. At each step of time, this file compares the
|
||||
internal state with the hardware state and generates events accordingly.
|
||||
Events can be seen as a delta-encoding of the state of the keyboard over
|
||||
time, and this buffer must be the sum of all events. This means that if an
|
||||
event cannot be generated, f.i. because the buffer is full, this state must
|
||||
*not* be updated. */
|
||||
GDATA volatile uint8_t state[12] = { 0 };
|
||||
|
||||
/* A driver event, which is a change in a full row, not a key. */
|
||||
typedef struct
|
||||
{
|
||||
uint time :12; /* Locally unique time identifier */
|
||||
uint row :4; /* Row number */
|
||||
uint old :8; /* Key status for the old row */
|
||||
uint new :8; /* Key status for the new row */
|
||||
|
||||
} driver_event_t;
|
||||
|
||||
/* The keyboard event buffer. This is a circular list defined by [buffer_start]
|
||||
and [buffer_end]. To avoid an ambiguity when start = end, the buffer is not
|
||||
allowed to be full (at least one free cell must be remaining). */
|
||||
GBSS static driver_event_t buffer[KEYBOARD_QUEUE_SIZE];
|
||||
/* Buffer bounds */
|
||||
GDATA static int buffer_start = 0;
|
||||
GDATA static int buffer_end = 0;
|
||||
|
||||
/* Current time, in keyboard-scanning ticks */
|
||||
GDATA int time = 0;
|
||||
GDATA int full_release = 2;
|
||||
|
||||
/* buffer_push() - add an event in the keyboard buffer
|
||||
Returns non-zero if the event cannot be pushed. */
|
||||
static int buffer_push(driver_event_t ev)
|
||||
{
|
||||
int next = (buffer_end + 1) % KEYBOARD_QUEUE_SIZE;
|
||||
if(next == buffer_start) return 1;
|
||||
|
||||
buffer[buffer_end] = ev;
|
||||
buffer_end = next;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* buffer_poll() - generate key events from the buffer
|
||||
Sets [ev] and returns zero on success, otherwise non-zero. */
|
||||
static int buffer_poll(driver_event_t *ev)
|
||||
{
|
||||
if(buffer_start == buffer_end) return 1;
|
||||
|
||||
*ev = buffer[buffer_start];
|
||||
buffer_start = (buffer_start + 1) % KEYBOARD_QUEUE_SIZE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* keysc_frame() - generate a round of interrupts for the current frame */
|
||||
void keysc_frame(void)
|
||||
{
|
||||
ALIGNED(2) uint8_t scan[12] = { 0 };
|
||||
time++;
|
||||
|
||||
/* First scan the key matrix: from I/O ports on SH3, KEYSC on SH4 */
|
||||
if(isSH3()) iokbd_scan(&scan);
|
||||
else
|
||||
{
|
||||
volatile uint16_t *KEYSC = (void *)0xa44b0000;
|
||||
uint16_t *array = (void *)&scan;
|
||||
|
||||
for(int i = 0; i < 6; i++) array[i] = KEYSC[i];
|
||||
}
|
||||
|
||||
for(int row = 0; row < 12; row++)
|
||||
{
|
||||
/* Compare new data with the internal state. */
|
||||
int old = state[row];
|
||||
int new = scan[row];
|
||||
if(old == new && !new) continue;
|
||||
|
||||
driver_event_t ev = {
|
||||
.time = time,
|
||||
.row = row,
|
||||
.old = old,
|
||||
.new = new,
|
||||
};
|
||||
|
||||
/* Update internal status if the event could be pushed */
|
||||
if(!buffer_push(ev)) state[row] = new;
|
||||
}
|
||||
}
|
||||
|
||||
/* key_poll() - poll the next keyboard event from the buffer */
|
||||
key_event_t key_poll(void)
|
||||
{
|
||||
/* Every time a driver event is unqueued, its key events are generated
|
||||
and put in this small buffer */
|
||||
static key_event_t events[8];
|
||||
/* Number of pending events in the previous buffer */
|
||||
static int events_pending = 0;
|
||||
|
||||
/* If there are pending events, use them */
|
||||
if(events_pending > 0) return events[--events_pending];
|
||||
|
||||
/* Unqueue something from the driver buffer, if possible */
|
||||
driver_event_t ev;
|
||||
if(buffer_poll(&ev)) return (key_event_t){ .type = KEYEV_NONE };
|
||||
|
||||
/* Generate new key events */
|
||||
int old = ev.old << 1;
|
||||
int new = ev.new;
|
||||
|
||||
for(int code = ((ev.row ^ 1) << 4) | 0x7; code & 0x7; code--)
|
||||
{
|
||||
int kind = (old & 2) | (new & 1);
|
||||
old >>= 1;
|
||||
new >>= 1;
|
||||
|
||||
if(!kind) continue;
|
||||
|
||||
key_event_t keyev = {
|
||||
.time = ev.time,
|
||||
.type = kind,
|
||||
.key = code,
|
||||
};
|
||||
events[events_pending++] = keyev;
|
||||
}
|
||||
|
||||
/* Return the first of these generated events */
|
||||
return events[--events_pending];
|
||||
}
|
||||
|
||||
//---
|
||||
// Driver initialization
|
||||
//---
|
||||
|
||||
static int callback(UNUSED void *arg)
|
||||
{
|
||||
keysc_frame();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* init() - setup the support timer */
|
||||
static void init(void)
|
||||
{
|
||||
int tid = isSH3() ? 4 : 7;
|
||||
|
||||
/* 32768 Hz divided by 16 is 2048 */
|
||||
/* TODO: Use I/O port scanning on SH3 */
|
||||
timer_setup(tid, 2048, 0, callback, NULL);
|
||||
timer_start(tid);
|
||||
}
|
||||
|
||||
/* unload() - stop the support timer */
|
||||
static void unload(void)
|
||||
{
|
||||
int tid = isSH3() ? 4 : 7;
|
||||
timer_stop(tid);
|
||||
}
|
||||
|
||||
//---
|
||||
// Driver structure definition
|
||||
//---
|
||||
|
||||
gint_driver_t drv_keysc = {
|
||||
.name = "KEYSC",
|
||||
.init = init,
|
||||
.unload = unload,
|
||||
.ctx_size = 0,
|
||||
.sys_ctx = NULL,
|
||||
.ctx_save = NULL,
|
||||
.ctx_restore = NULL,
|
||||
};
|
||||
|
||||
GINT_DECLARE_DRIVER(4, drv_keysc);
|
|
@ -309,6 +309,6 @@ gint_driver_t drv_r61524 = {
|
|||
.ctx_restore = ctx_restore,
|
||||
};
|
||||
|
||||
GINT_DECLARE_DRIVER(drv_r61524);
|
||||
GINT_DECLARE_DRIVER(5, drv_r61524);
|
||||
|
||||
#endif /* FXCG50 */
|
||||
|
|
60
src/rtc/inth.s
Normal file
60
src/rtc/inth.s
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
** gint:rtc:inth - Interrupt handler for the Real-Time Clock
|
||||
** This one is fairly simple, just a flag to clear and potentially a timer to
|
||||
** stop if the callback returns non-zero.
|
||||
*/
|
||||
|
||||
.global _inth_rtc_pri
|
||||
.global _inth_rtc_pri_helper
|
||||
.section .gint.blocks, "ax"
|
||||
.align 4
|
||||
|
||||
/* RTC PERIODIC INTERRUPT HANDLER - 56 BYTES */
|
||||
|
||||
_inth_rtc_pri:
|
||||
/* Invoke the callback function with its argument */
|
||||
sts.l pr, @-r15
|
||||
mov.l 1f, r0
|
||||
mov.l 2f, r4
|
||||
jsr @r0
|
||||
nop
|
||||
|
||||
/* Save the return value */
|
||||
mov r0, r3
|
||||
|
||||
/* Prepare to clear the interrupt flag */
|
||||
mov.l 3f, r1
|
||||
|
||||
/* Jump to another gate to finish the work:
|
||||
- 0xc is the size of storage below
|
||||
- 0x20 is the size of the next gate (alarm interrupt) */
|
||||
mov #0x2c, r2
|
||||
braf r2
|
||||
nop
|
||||
|
||||
1: .long 0 /* Callback function: edited dynamically */
|
||||
2: .long 0 /* Argument to callback function */
|
||||
3: .long 0xa413fede /* RCR2 address, edited at startup on SH3 */
|
||||
|
||||
_inth_rtc_pri_helper:
|
||||
|
||||
.clear:
|
||||
/* Clear the interrupt flag */
|
||||
mov.b @r1, r0
|
||||
tst #0x80, r0
|
||||
and #0x7f, r0
|
||||
bf.s .clear
|
||||
mov.b r0, @r1
|
||||
|
||||
/* Stop the timer if the return value of the callback was non-zero */
|
||||
tst r3, r3
|
||||
bt .end
|
||||
and #0x8f, r0
|
||||
mov.b r0, @r1
|
||||
|
||||
.end:
|
||||
lds.l @r15+, pr
|
||||
rte
|
||||
nop
|
||||
|
||||
.zero 8
|
191
src/rtc/rtc.c
Normal file
191
src/rtc/rtc.c
Normal file
|
@ -0,0 +1,191 @@
|
|||
//---
|
||||
// gint:rtc - Real-Time Clock
|
||||
//---
|
||||
|
||||
#include <gint/rtc.h>
|
||||
#include <gint/drivers.h>
|
||||
#include <gint/gint.h>
|
||||
#include <mod/rtc.h>
|
||||
|
||||
#include <core/mpu.h>
|
||||
#include <defs/types.h>
|
||||
|
||||
//---
|
||||
// Real-Time Clock peripheral registers
|
||||
//---
|
||||
|
||||
/* RTC address on SH7305, adjusted at startup on SH7337 and SH7355 */
|
||||
GDATA static rtc_t *RTC = &SH7305_RTC;
|
||||
/* Address of interrupt handler parameters */
|
||||
GBSS struct {
|
||||
int (*callback)(void *arg);
|
||||
void *arg;
|
||||
volatile uint8_t *RCR2;
|
||||
} PACKED(4) *params;
|
||||
|
||||
//---
|
||||
// Time management
|
||||
//---
|
||||
|
||||
/* int8(), int16()
|
||||
Converts BCD values to integers */
|
||||
static int int8(uint8_t bcd)
|
||||
{
|
||||
return (bcd & 0x0f) + 10 * (bcd >> 4);
|
||||
}
|
||||
static int int16(uint16_t bcd)
|
||||
{
|
||||
return (bcd & 0xf) + 10 * ((bcd >> 4) & 0xf) + 100 * ((bcd >> 8) & 0xf)
|
||||
+ 1000 * (bcd >> 12);
|
||||
}
|
||||
|
||||
/* bcd8(), bcd16()
|
||||
Converts integer values to BCD
|
||||
TODO: Use some kind of qdiv() for bcd8() and bcd16()? */
|
||||
static uint8_t bcd8(int integer)
|
||||
{
|
||||
integer %= 100;
|
||||
return ((integer / 10) << 4) | (integer % 10);
|
||||
}
|
||||
static uint16_t bcd16(int integer)
|
||||
{
|
||||
integer %= 10000;
|
||||
return (bcd8(integer / 100) << 8) | bcd8(integer % 100);
|
||||
}
|
||||
|
||||
/* rtc_get_time() - read the current time from the RTC */
|
||||
void rtc_get_time(rtc_time_t *time)
|
||||
{
|
||||
if(!time) return;
|
||||
|
||||
do {
|
||||
RTC->RCR1.CF = 0;
|
||||
|
||||
time->seconds = int8(RTC->RSECCNT.byte);
|
||||
time->minutes = int8(RTC->RMINCNT.byte);
|
||||
time->hours = int8(RTC->RHRCNT.byte);
|
||||
time->month_day = int8(RTC->RDAYCNT.byte);
|
||||
time->month = int8(RTC->RMONCNT.byte);
|
||||
time->year = int16(RTC->RYRCNT.word);
|
||||
time->week_day = RTC->RWKCNT;
|
||||
|
||||
} while(RTC->RCR1.CF != 0);
|
||||
}
|
||||
|
||||
/* rtc_set_time() - write a new current time to the RTC */
|
||||
void rtc_set_time(const rtc_time_t *time)
|
||||
{
|
||||
if(!time) return;
|
||||
int wday = (time->week_day < 7) ? (time->week_day) : (0);
|
||||
|
||||
do {
|
||||
RTC->RCR1.CF = 0;
|
||||
|
||||
RTC->RSECCNT.byte = bcd8(time->seconds);
|
||||
RTC->RMINCNT.byte = bcd8(time->minutes);
|
||||
RTC->RHRCNT.byte = bcd8(time->hours);
|
||||
RTC->RDAYCNT.byte = bcd8(time->month_day);
|
||||
RTC->RMONCNT.byte = bcd8(time->month);
|
||||
RTC->RYRCNT.word = bcd16(time->year);
|
||||
RTC->RWKCNT = wday;
|
||||
|
||||
} while(RTC->RCR1.CF != 0);
|
||||
}
|
||||
|
||||
//---
|
||||
// RTC timer
|
||||
//---
|
||||
|
||||
/* rtc_start_timer() - configure the RTC timer */
|
||||
void rtc_start_timer(rtc_frequency_t freq, int (*callback)(void*arg), void*arg)
|
||||
{
|
||||
/* Set up the callback */
|
||||
params->callback = callback;
|
||||
params->arg = arg;
|
||||
|
||||
/* Clear the interrupt flag */
|
||||
do RTC->RCR2.PEF = 0;
|
||||
while(RTC->RCR2.PEF);
|
||||
|
||||
/* Enable the interrupt */
|
||||
RTC->RCR2.PES = freq;
|
||||
}
|
||||
|
||||
/* rtc_stop_timer() - stop the RTC timer */
|
||||
void rtc_stop_timer(void)
|
||||
{
|
||||
RTC->RCR2.PES = rtc_none;
|
||||
}
|
||||
|
||||
//---
|
||||
// Driver initialization
|
||||
//---
|
||||
|
||||
#ifdef FX9860G
|
||||
static void driver_sh3(void)
|
||||
{
|
||||
/* Adjust the address of the RTC */
|
||||
RTC = &SH7705_RTC;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void init(void)
|
||||
{
|
||||
/* Interrupt handlers provided by rtc/inth.s */
|
||||
extern void inth_rtc_pri(void);
|
||||
extern void inth_rtc_pri_helper(void);
|
||||
|
||||
/* Install the RTC interrupt handler */
|
||||
UNUSED void *h0, *h1;
|
||||
h0 = gint_inthandler(0xaa0, inth_rtc_pri, 32);
|
||||
h1 = gint_inthandler(0xae0, inth_rtc_pri_helper, 32);
|
||||
|
||||
params = h0 + 20;
|
||||
params->RCR2 = &RTC->RCR2.byte;
|
||||
|
||||
/* Disable the periodic interrupt for now, but give it priority 5 */
|
||||
RTC->RCR2.PES = rtc_none;
|
||||
gint_intlevel(isSH3() ? 3 : 40, 5);
|
||||
}
|
||||
|
||||
//---
|
||||
// Context system for this driver
|
||||
//---
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t RCR1;
|
||||
uint8_t RCR2;
|
||||
} ctx_t;
|
||||
|
||||
GBSS static ctx_t sys_ctx;
|
||||
|
||||
static void ctx_save(void *buf)
|
||||
{
|
||||
ctx_t *ctx = buf;
|
||||
ctx->RCR1 = RTC->RCR1.byte;
|
||||
ctx->RCR2 = RTC->RCR2.byte;
|
||||
}
|
||||
|
||||
static void ctx_restore(void *buf)
|
||||
{
|
||||
ctx_t *ctx = buf;
|
||||
ctx->RCR1 = RTC->RCR1.byte;
|
||||
ctx->RCR2 = RTC->RCR2.byte;
|
||||
}
|
||||
|
||||
//---
|
||||
// Driver structure definition
|
||||
//---
|
||||
|
||||
gint_driver_t drv_rtc = {
|
||||
.name = "Real-Time Clock",
|
||||
.driver_sh3 = GINT_DRIVER_SH3(driver_sh3),
|
||||
.init = init,
|
||||
.ctx_size = sizeof(ctx_t),
|
||||
.sys_ctx = &sys_ctx,
|
||||
.ctx_save = ctx_save,
|
||||
.ctx_restore = ctx_restore,
|
||||
};
|
||||
|
||||
GINT_DECLARE_DRIVER(2, drv_rtc);
|
|
@ -140,15 +140,16 @@ void t6k11_backlight(int setting)
|
|||
/* This setting is mapped to an I/O port:
|
||||
- On SH3, bit 7 of port G
|
||||
- On SH4, bit 4 of port N */
|
||||
mpuSwitch({
|
||||
/* SH3-based */
|
||||
if(isSH3())
|
||||
{
|
||||
port = (void *)0xa400012c;
|
||||
mask = 0x80;
|
||||
},{
|
||||
/* SH4-based */
|
||||
}
|
||||
else
|
||||
{
|
||||
port = (void *)0xa4050138;
|
||||
mask = 0x10;
|
||||
});
|
||||
}
|
||||
|
||||
if(!setting) *port &= ~mask;
|
||||
if(setting > 0) *port |= mask;
|
||||
|
@ -204,6 +205,6 @@ gint_driver_t drv_t6k11 = {
|
|||
.ctx_restore = ctx_restore,
|
||||
};
|
||||
|
||||
GINT_DECLARE_DRIVER(drv_t6k11);
|
||||
GINT_DECLARE_DRIVER(5, drv_t6k11);
|
||||
|
||||
#endif /* FX9860G */
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <gint/timer.h>
|
||||
#include <gint/drivers.h>
|
||||
#include <gint/gint.h>
|
||||
#include <gint/clock.h>
|
||||
|
||||
#include <core/intc.h>
|
||||
|
||||
|
@ -66,7 +67,6 @@ typedef struct
|
|||
uint16_t event; /* Interrupt event code */
|
||||
} timer_t;
|
||||
|
||||
|
||||
//---
|
||||
// Driver storage
|
||||
//---
|
||||
|
@ -133,9 +133,12 @@ int timer_setup(int tid, uint32_t delay, timer_input_t clock,
|
|||
t->TCR.UNIE = 1;
|
||||
}
|
||||
|
||||
/* Register the callback and its argument */
|
||||
timers[tid].data->callback = callback;
|
||||
timers[tid].data->arg = arg;
|
||||
/* Register the callback and its argument (TMU-owned timers only) */
|
||||
if(timers[tid].data)
|
||||
{
|
||||
timers[tid].data->callback = callback;
|
||||
timers[tid].data->arg = arg;
|
||||
}
|
||||
|
||||
/* Return the timer id, since configuration was successful */
|
||||
return tid;
|
||||
|
@ -145,11 +148,14 @@ int timer_setup(int tid, uint32_t delay, timer_input_t clock,
|
|||
uint32_t timer_delay(int tid, int delay_us)
|
||||
{
|
||||
/* TODO: Proper timer_delay() */
|
||||
#ifdef FX9860G
|
||||
uint64_t freq = 14750000 >> 2; /* 14.75 MHz / 4 */
|
||||
#else
|
||||
uint64_t freq = 29020000 >> 2; /* 29.49 MHz / 4 */
|
||||
#endif
|
||||
const clock_frequency_t *clock = clock_freq();
|
||||
uint64_t freq;
|
||||
|
||||
freq = isSH3() ? 14750000 >> 2 : clock->Pphi_f >> 2;
|
||||
|
||||
/* fxcg50: Calculated = 29491200 but it's too low */
|
||||
// uint64_t freq = 29020000 >> 2;
|
||||
|
||||
if(tid >= 3) freq = 32768; /* 32768 Hz */
|
||||
|
||||
uint64_t product = freq * (uint64_t)delay_us;
|
||||
|
@ -180,8 +186,8 @@ void timer_start(int tid)
|
|||
timer_control(tid, 0);
|
||||
}
|
||||
|
||||
/* timer_reload() - change a timer's constant register for next interrupts */
|
||||
void timer_reload(int tid, uint32_t delay, timer_input_t clock)
|
||||
/* timer_reload() - change a timer's delay constant for next interrupts */
|
||||
void timer_reload(int tid, uint32_t delay)
|
||||
{
|
||||
if(tid < 3) ((tmu_t *)timers[tid].tmu)->TCOR = delay;
|
||||
else ((tmu_extra_t *)timers[tid].tmu)->TCOR = delay;
|
||||
|
@ -193,9 +199,11 @@ void timer_pause(int tid)
|
|||
timer_control(tid, 1);
|
||||
}
|
||||
|
||||
/* timer_free() - free a timer */
|
||||
void timer_free(int tid)
|
||||
/* timer_stp() - stop and free a timer */
|
||||
void timer_stop(int tid)
|
||||
{
|
||||
return;
|
||||
|
||||
/* Stop the timer and disable UNIE to indicate that it's free */
|
||||
timer_pause(tid);
|
||||
|
||||
|
@ -234,15 +242,29 @@ extern void inth_tmu_extra2(void);
|
|||
extern void inth_tmu_extra_help(void);
|
||||
extern void inth_tmu_extra_others(void);
|
||||
|
||||
#ifdef FX9860G
|
||||
static void driver_sh3(void)
|
||||
{
|
||||
timers[0].tmu = (void *)0xfffffe94;
|
||||
timers[1].tmu = (void *)0xfffffea0;
|
||||
timers[2].tmu = (void *)0xfffffeac;
|
||||
/* We don't need to change the event code of ETMU0 since it's
|
||||
translated to the SH4 code by the interrupt handler */
|
||||
timers[3].tmu = (void *)0xa44c0030;
|
||||
|
||||
TSTR = (void *)0xfffffe92;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void init(void)
|
||||
{
|
||||
/* Install the standard's TMU interrupt handlers. By chance TMU gates
|
||||
use the same event codes on SH7705 and SH7305 */
|
||||
UNUSED void *h0, *h1, *h2, *hs;
|
||||
h0 = gint_inthandler(0x400, inth_tmu_0);
|
||||
h1 = gint_inthandler(0x420, inth_tmu_1);
|
||||
h2 = gint_inthandler(0x440, inth_tmu_2);
|
||||
hs = gint_inthandler(0x460, inth_tmu_storage);
|
||||
h0 = gint_inthandler(0x400, inth_tmu_0, 32);
|
||||
h1 = gint_inthandler(0x420, inth_tmu_1, 32);
|
||||
h2 = gint_inthandler(0x440, inth_tmu_2, 32);
|
||||
hs = gint_inthandler(0x460, inth_tmu_storage, 32);
|
||||
|
||||
/* User information in interrupt handlers */
|
||||
timers[0].data = h2 + 20;
|
||||
|
@ -296,14 +318,14 @@ static void init(void)
|
|||
void *handler = (i == 5)
|
||||
? inth_tmu_extra2
|
||||
: inth_tmu_extra_others;
|
||||
void *h = gint_inthandler(timers[i].event, handler);
|
||||
void *h = gint_inthandler(timers[i].event, handler, 32);
|
||||
|
||||
timers[i].data = h + 20;
|
||||
timers[i].data->structure = timers[i].tmu;
|
||||
}
|
||||
|
||||
/* Also install the helper handler */
|
||||
gint_inthandler(0xc60, inth_tmu_extra_help);
|
||||
gint_inthandler(0xc60, inth_tmu_extra_help, 32);
|
||||
|
||||
/* Enable TMU0 at level 13, TMU1 at level 11, TMU2 at level 9 */
|
||||
gint_intlevel(0, 13);
|
||||
|
@ -311,11 +333,12 @@ static void init(void)
|
|||
gint_intlevel(2, 9);
|
||||
|
||||
/* Enable the extra TMUs at level 7 */
|
||||
mpuSwitch({
|
||||
/* SH3-based */
|
||||
if(isSH3())
|
||||
{
|
||||
gint_intlevel(23, 7);
|
||||
},{
|
||||
/* SH4-based */
|
||||
}
|
||||
else
|
||||
{
|
||||
gint_intlevel(36, 7);
|
||||
gint_intlevel(25, 7);
|
||||
gint_intlevel(26, 7);
|
||||
|
@ -323,12 +346,12 @@ static void init(void)
|
|||
gint_intlevel(32, 7);
|
||||
gint_intlevel(44, 7);
|
||||
|
||||
/* Unmask the interrupts */
|
||||
/* Unmask the extra timers' interrupts */
|
||||
INTC4.MSKCLR->IMR2 = 0x01;
|
||||
INTC4.MSKCLR->IMR5 = 0x06;
|
||||
INTC4.MSKCLR->IMR6 = 0x18;
|
||||
INTC4.MSKCLR->IMR8 = 0x02;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//---
|
||||
|
@ -346,20 +369,6 @@ typedef struct
|
|||
/* Allocate a system buffer in gint's BSS area */
|
||||
GBSS static ctx_t sys_ctx;
|
||||
|
||||
#ifdef FX9860G
|
||||
static void driver_sh3(void)
|
||||
{
|
||||
timers[0].tmu = (void *)0xfffffe94;
|
||||
timers[1].tmu = (void *)0xfffffea0;
|
||||
timers[2].tmu = (void *)0xfffffeac;
|
||||
/* We don't need to change the event code of ETMU0 since it's
|
||||
translated to the SH4 code by the interrupt handler */
|
||||
timers[3].tmu = (void *)0xa44c0030;
|
||||
|
||||
TSTR = (void *)0xfffffe92;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ctx_save(void *buf)
|
||||
{
|
||||
ctx_t *ctx = buf;
|
||||
|
@ -401,10 +410,12 @@ static void ctx_restore(void *buf)
|
|||
for(int i = 0; i < timer_count() - 3; i++)
|
||||
{
|
||||
tmu_extra_t *t = timers[i + 3].tmu;
|
||||
/* Avoid some interrupts that occur when TCNT = 0 */
|
||||
t->TSTR = 0;
|
||||
t->TCNT = 0xffffffff;
|
||||
|
||||
/* This thing is being unloaded while interrupts are disabled,
|
||||
so we don't have to heed for ctx->extra[i].TCNT = 0, which
|
||||
can generate interrupts. I tried to do t->TCNT = 0xffffffff
|
||||
then restore ctx->extra[i], but it causes hangs on SH3 when
|
||||
the overclock level is too high. */
|
||||
t->TCNT = ctx->extra[i].TCNT;
|
||||
t->TCOR = ctx->extra[i].TCOR;
|
||||
t->TCR.byte = ctx->extra[i].TCR.byte;
|
||||
|
@ -418,12 +429,12 @@ static void ctx_restore(void *buf)
|
|||
|
||||
gint_driver_t drv_tmu = {
|
||||
.name = "Timer Unit",
|
||||
.driver_sh3 = GINT_DRIVER_SH3(driver_sh3),
|
||||
.init = init,
|
||||
.ctx_size = sizeof(ctx_t),
|
||||
.sys_ctx = &sys_ctx,
|
||||
.driver_sh3 = GINT_DRIVER_SH3(driver_sh3),
|
||||
.ctx_save = ctx_save,
|
||||
.ctx_restore = ctx_restore,
|
||||
};
|
||||
|
||||
GINT_DECLARE_DRIVER(drv_tmu);
|
||||
GINT_DECLARE_DRIVER(2, drv_tmu);
|
||||
|
|
Loading…
Reference in a new issue