mirror of https://gitee.com/openkylin/qemu.git
Renesas hardware patches
- Add a common entry for Renesas hardware in MAINTAINERS - Trivial SH4 cleanups - Add RX GDB simulator from Yoshinori Sato The Renesas RX target emulation was added in commitc8c35e5f51
, these patches complete the target by adding the hardware emulation. Tests included: $ avocado --show=app,console run -t arch:rx tests/acceptance/ Fetching asset from tests/acceptance/machine_rx_gdbsim.py:RxGdbSimMachine.test_uboot Fetching asset from tests/acceptance/machine_rx_gdbsim.py:RxGdbSimMachine.test_linux_sash (1/2) tests/acceptance/machine_rx_gdbsim.py:RxGdbSimMachine.test_uboot: console: U-Boot 2016.05-rc3-23705-ga1ef3c71cb-dirty (Feb 05 2019 - 21:56:06 +0900) PASS (0.26 s) (2/2) tests/acceptance/machine_rx_gdbsim.py:RxGdbSimMachine.test_linux_sash: console: Linux version 4.19.0+ (yo-satoh@yo-satoh-debian) (gcc version 9.0.0 20181105 (experimental) (GCC)) #137 Wed Feb 20 23:20:02 JST 2019 console: Built 1 zonelists, mobility grouping on. Total pages: 8128 console: Kernel command line: console: Dentry cache hash table entries: 4096 (order: 2, 16384 bytes) console: Inode-cache hash table entries: 2048 (order: 1, 8192 bytes) console: Memory: 14648K/32768K available (871K kernel code, 95K rwdata, 140K rodata, 96K init, 175K bss, 18120K reserved, 0K cma-reserved) console: NR_IRQS: 256 console: rx-cmt: used for periodic clock events console: clocksource: rx-tpu: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 1274173631191 ns console: 96.00 BogoMIPS (lpj=480000) console: pid_max: default: 4096 minimum: 301 console: Mount-cache hash table entries: 1024 (order: 0, 4096 bytes) console: Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes) console: clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns console: clocksource: Switched to clocksource rx-tpu console: workingset: timestamp_bits=30 max_order=12 bucket_order=0 console: SuperH (H)SCI(F) driver initialized console: 88240.serial: ttySC0 at MMIO 0x88240 (irq = 215, base_baud = 0) is a sci console: console [ttySC0] enabled console: 88248.serial: ttySC1 at MMIO 0x88248 (irq = 219, base_baud = 0) is a sci console: random: get_random_bytes called from 0x01002e48 with crng_init=0 console: Freeing unused kernel memory: 96K console: This architecture does not have kernel memory protection. console: Run /sbin/init as init process console: Run /etc/init as init process console: Run /bin/init as init process console: Run /bin/sh as init process console: Sash command shell (version 1.1.1) console: /> printenv console: HOME=/ console: TERM=linux PASS (0.73 s) RESULTS : PASS 2 | ERROR 0 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT 0 | CANCEL 0 JOB TIME : 1.47 s CI results: . https://cirrus-ci.com/build/6140199509950464 . https://travis-ci.org/github/philmd/qemu/builds/700954881 . https://app.shippable.com/github/philmd/qemu/runs/812/summary/console -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE+qvnXhKRciHc/Wuy4+MsLN6twN4FAl7w/dkACgkQ4+MsLN6t wN6tHw//Xt+n/PMXOWdPfAA6Ezfc+4k/CKXMXG+PM2bq2wwmrpMLybxJMVBb7Q3H 83F5nSdDziEd0T7XzT2lzh3Qg6wcVmGXvvgMInyL8VNoLWeQoOllIKKY35FikhPH Vx5W3OEPSgzcqb2//fUVha+VaKW8mv+AmMp/fmI0woHe8qBYq641QNbQUyQ/0L4E iJR6Tyl/2KE/9V6iED+qQ9qa5nY/INve73dj1/hyBKB0WgsjTH5qCipxlx8BCQHS vGQ/IbAJl/CpXUN/dhQ8Uxaq97QBdeu1rBefGETj2TJrv+5jbmOVj/Ac/VFojMz1 iBIrYDtHU8h3msceDwZHeJvhWUgdqzuEWXYl5UKgt+FkzXZvDlc6Fkill01DYrDL OINVUhlvkpN+sTJYkA8GrcNKpI7dEK/qrfh7utuvgyblZVOaJJV6ldPQ1378ENmc QsdtWfkmcVR704ATZk6tYNPI3d8xvepPnjAGFdFISrBEC92Rj2jCQX6zwGu7M7Zg M9okNv8krx4bMub0XaK8MQzPpBkWEPzdCo5b11lOZeyvJ5NgJsZRiCdCwX08vmji NJzrIaFoWs5qLh3+kwY3b6fvku+6xn5iuYhKO6AdMTDxduWKslE0XU45dv6+NBsA NriEIagcb9R2vTYug4I2vfzCOrWwJqP5DDKX2iqT84Pa++KC3C8= =Ij2z -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/philmd-gitlab/tags/renesas-hw-20200622' into staging Renesas hardware patches - Add a common entry for Renesas hardware in MAINTAINERS - Trivial SH4 cleanups - Add RX GDB simulator from Yoshinori Sato The Renesas RX target emulation was added in commitc8c35e5f51
, these patches complete the target by adding the hardware emulation. Tests included: $ avocado --show=app,console run -t arch:rx tests/acceptance/ Fetching asset from tests/acceptance/machine_rx_gdbsim.py:RxGdbSimMachine.test_uboot Fetching asset from tests/acceptance/machine_rx_gdbsim.py:RxGdbSimMachine.test_linux_sash (1/2) tests/acceptance/machine_rx_gdbsim.py:RxGdbSimMachine.test_uboot: console: U-Boot 2016.05-rc3-23705-ga1ef3c71cb-dirty (Feb 05 2019 - 21:56:06 +0900) PASS (0.26 s) (2/2) tests/acceptance/machine_rx_gdbsim.py:RxGdbSimMachine.test_linux_sash: console: Linux version 4.19.0+ (yo-satoh@yo-satoh-debian) (gcc version 9.0.0 20181105 (experimental) (GCC)) #137 Wed Feb 20 23:20:02 JST 2019 console: Built 1 zonelists, mobility grouping on. Total pages: 8128 console: Kernel command line: console: Dentry cache hash table entries: 4096 (order: 2, 16384 bytes) console: Inode-cache hash table entries: 2048 (order: 1, 8192 bytes) console: Memory: 14648K/32768K available (871K kernel code, 95K rwdata, 140K rodata, 96K init, 175K bss, 18120K reserved, 0K cma-reserved) console: NR_IRQS: 256 console: rx-cmt: used for periodic clock events console: clocksource: rx-tpu: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 1274173631191 ns console: 96.00 BogoMIPS (lpj=480000) console: pid_max: default: 4096 minimum: 301 console: Mount-cache hash table entries: 1024 (order: 0, 4096 bytes) console: Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes) console: clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns console: clocksource: Switched to clocksource rx-tpu console: workingset: timestamp_bits=30 max_order=12 bucket_order=0 console: SuperH (H)SCI(F) driver initialized console: 88240.serial: ttySC0 at MMIO 0x88240 (irq = 215, base_baud = 0) is a sci console: console [ttySC0] enabled console: 88248.serial: ttySC1 at MMIO 0x88248 (irq = 219, base_baud = 0) is a sci console: random: get_random_bytes called from 0x01002e48 with crng_init=0 console: Freeing unused kernel memory: 96K console: This architecture does not have kernel memory protection. console: Run /sbin/init as init process console: Run /etc/init as init process console: Run /bin/init as init process console: Run /bin/sh as init process console: Sash command shell (version 1.1.1) console: /> printenv console: HOME=/ console: TERM=linux PASS (0.73 s) RESULTS : PASS 2 | ERROR 0 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT 0 | CANCEL 0 JOB TIME : 1.47 s CI results: . https://cirrus-ci.com/build/6140199509950464 . https://travis-ci.org/github/philmd/qemu/builds/700954881 . https://app.shippable.com/github/philmd/qemu/runs/812/summary/console # gpg: Signature made Mon 22 Jun 2020 19:52:09 BST # gpg: using RSA key FAABE75E12917221DCFD6BB2E3E32C2CDEADC0DE # gpg: Good signature from "Philippe Mathieu-Daudé (F4BUG) <f4bug@amsat.org>" [full] # Primary key fingerprint: FAAB E75E 1291 7221 DCFD 6BB2 E3E3 2C2C DEAD C0DE * remotes/philmd-gitlab/tags/renesas-hw-20200622: docs: Document the RX target BootLinuxConsoleTest: Test the RX GDB simulator hw/rx: Add RX GDB simulator hw/rx: Register R5F562N7 and R5F562N8 MCUs hw/rx: Honor -accel qtest hw/rx: RX62N microcontroller (MCU) hw/char: RX62N serial communication interface (SCI) hw/timer: RX62N compare match timer (CMT) hw/timer: RX62N 8-Bit timer (TMR) hw/intc: RX62N interrupt controller (ICUa) hw/timer/sh_timer: Remove unused 'qemu/timer.h' include hw/sh4: Extract timer definitions to 'hw/timer/tmu012.h' hw/sh4: Use MemoryRegion typedef MAINTAINERS: Add an entry for common Renesas peripherals MAINTAINERS: Cover sh_intc files in the R2D/Shix machine sections Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
d88d5a3806
33
MAINTAINERS
33
MAINTAINERS
|
@ -1256,6 +1256,15 @@ F: include/hw/riscv/opentitan.h
|
|||
F: include/hw/char/ibex_uart.h
|
||||
F: include/hw/intc/ibex_plic.h
|
||||
|
||||
RX Machines
|
||||
-----------
|
||||
rx-gdbsim
|
||||
M: Yoshinori Sato <ysato@users.sourceforge.jp>
|
||||
S: Maintained
|
||||
F: docs/system/target-rx.rst
|
||||
F: hw/rx/rx-gdbsim.c
|
||||
F: tests/acceptance/machine_rx_gdbsim.py
|
||||
|
||||
SH4 Machines
|
||||
------------
|
||||
R2D
|
||||
|
@ -1264,13 +1273,15 @@ R: Magnus Damm <magnus.damm@gmail.com>
|
|||
S: Maintained
|
||||
F: hw/sh4/r2d.c
|
||||
F: hw/intc/sh_intc.c
|
||||
F: hw/timer/sh_timer.c
|
||||
F: include/hw/sh4/sh_intc.h
|
||||
|
||||
Shix
|
||||
M: Yoshinori Sato <ysato@users.sourceforge.jp>
|
||||
R: Magnus Damm <magnus.damm@gmail.com>
|
||||
S: Odd Fixes
|
||||
F: hw/sh4/shix.c
|
||||
F: hw/intc/sh_intc.c
|
||||
F: include/hw/sh4/sh_intc.h
|
||||
|
||||
SPARC Machines
|
||||
--------------
|
||||
|
@ -1965,6 +1976,26 @@ F: hw/*/*xive*
|
|||
F: include/hw/*/*xive*
|
||||
F: docs/*/*xive*
|
||||
|
||||
Renesas peripherals
|
||||
M: Yoshinori Sato <ysato@users.sourceforge.jp>
|
||||
R: Magnus Damm <magnus.damm@gmail.com>
|
||||
S: Maintained
|
||||
F: hw/char/renesas_sci.c
|
||||
F: hw/char/sh_serial.c
|
||||
F: hw/timer/renesas_*.c
|
||||
F: hw/timer/sh_timer.c
|
||||
F: include/hw/char/renesas_sci.h
|
||||
F: include/hw/sh4/sh.h
|
||||
F: include/hw/timer/renesas_*.h
|
||||
|
||||
Renesas RX peripherals
|
||||
M: Yoshinori Sato <ysato@users.sourceforge.jp>
|
||||
S: Maintained
|
||||
F: hw/intc/rx_icu.c
|
||||
F: hw/rx/
|
||||
F: include/hw/intc/rx_icu.h
|
||||
F: include/hw/rx/
|
||||
|
||||
Subsystems
|
||||
----------
|
||||
Audio
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
# Default configuration for rx-softmmu
|
||||
|
||||
CONFIG_RX_GDBSIM=y
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
.. _RX-System-emulator:
|
||||
|
||||
RX System emulator
|
||||
--------------------
|
||||
|
||||
Use the executable ``qemu-system-rx`` to simulate RX target (GDB simulator).
|
||||
This target emulated following devices.
|
||||
|
||||
- R5F562N8 MCU
|
||||
|
||||
- On-chip memory (ROM 512KB, RAM 96KB)
|
||||
- Interrupt Control Unit (ICUa)
|
||||
- 8Bit Timer x 1CH (TMR0,1)
|
||||
- Compare Match Timer x 2CH (CMT0,1)
|
||||
- Serial Communication Interface x 1CH (SCI0)
|
||||
|
||||
- External memory 16MByte
|
||||
|
||||
Example of ``qemu-system-rx`` usage for RX is shown below:
|
||||
|
||||
Download ``<u-boot_image_file>`` from
|
||||
https://osdn.net/users/ysato/pf/qemu/dl/u-boot.bin.gz
|
||||
|
||||
Start emulation of rx-virt::
|
||||
qemu-system-rx -M gdbsim-r5f562n8 -bios <u-boot_image_file>
|
||||
|
||||
Download ``kernel_image_file`` from
|
||||
https://osdn.net/users/ysato/pf/qemu/dl/zImage
|
||||
|
||||
Download ``device_tree_blob`` from
|
||||
https://osdn.net/users/ysato/pf/qemu/dl/rx-virt.dtb
|
||||
|
||||
Start emulation of rx-virt::
|
||||
qemu-system-rx -M gdbsim-r5f562n8 \
|
||||
-kernel <kernel_image_file> -dtb <device_tree_blob> \
|
||||
-append "earlycon"
|
|
@ -18,3 +18,4 @@ Contents:
|
|||
target-m68k
|
||||
target-xtensa
|
||||
target-s390x
|
||||
target-rx
|
||||
|
|
|
@ -55,6 +55,7 @@ source nios2/Kconfig
|
|||
source openrisc/Kconfig
|
||||
source ppc/Kconfig
|
||||
source riscv/Kconfig
|
||||
source rx/Kconfig
|
||||
source s390x/Kconfig
|
||||
source sh4/Kconfig
|
||||
source sparc/Kconfig
|
||||
|
|
|
@ -46,3 +46,6 @@ config SCLPCONSOLE
|
|||
|
||||
config TERMINAL3270
|
||||
bool
|
||||
|
||||
config RENESAS_SCI
|
||||
bool
|
||||
|
|
|
@ -21,6 +21,7 @@ common-obj-$(CONFIG_SH4) += sh_serial.o
|
|||
common-obj-$(CONFIG_DIGIC) += digic-uart.o
|
||||
common-obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o
|
||||
common-obj-$(CONFIG_RASPI) += bcm2835_aux.o
|
||||
common-obj-$(CONFIG_RENESAS_SCI) += renesas_sci.o
|
||||
|
||||
common-obj-$(CONFIG_CMSDK_APB_UART) += cmsdk-apb-uart.o
|
||||
common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o
|
||||
|
|
|
@ -0,0 +1,350 @@
|
|||
/*
|
||||
* Renesas Serial Communication Interface
|
||||
*
|
||||
* Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
|
||||
* (Rev.1.40 R01UH0033EJ0140)
|
||||
*
|
||||
* Copyright (c) 2019 Yoshinori Sato
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/registerfields.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/char/renesas_sci.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
||||
/* SCI register map */
|
||||
REG8(SMR, 0)
|
||||
FIELD(SMR, CKS, 0, 2)
|
||||
FIELD(SMR, MP, 2, 1)
|
||||
FIELD(SMR, STOP, 3, 1)
|
||||
FIELD(SMR, PM, 4, 1)
|
||||
FIELD(SMR, PE, 5, 1)
|
||||
FIELD(SMR, CHR, 6, 1)
|
||||
FIELD(SMR, CM, 7, 1)
|
||||
REG8(BRR, 1)
|
||||
REG8(SCR, 2)
|
||||
FIELD(SCR, CKE, 0, 2)
|
||||
FIELD(SCR, TEIE, 2, 1)
|
||||
FIELD(SCR, MPIE, 3, 1)
|
||||
FIELD(SCR, RE, 4, 1)
|
||||
FIELD(SCR, TE, 5, 1)
|
||||
FIELD(SCR, RIE, 6, 1)
|
||||
FIELD(SCR, TIE, 7, 1)
|
||||
REG8(TDR, 3)
|
||||
REG8(SSR, 4)
|
||||
FIELD(SSR, MPBT, 0, 1)
|
||||
FIELD(SSR, MPB, 1, 1)
|
||||
FIELD(SSR, TEND, 2, 1)
|
||||
FIELD(SSR, ERR, 3, 3)
|
||||
FIELD(SSR, PER, 3, 1)
|
||||
FIELD(SSR, FER, 4, 1)
|
||||
FIELD(SSR, ORER, 5, 1)
|
||||
FIELD(SSR, RDRF, 6, 1)
|
||||
FIELD(SSR, TDRE, 7, 1)
|
||||
REG8(RDR, 5)
|
||||
REG8(SCMR, 6)
|
||||
FIELD(SCMR, SMIF, 0, 1)
|
||||
FIELD(SCMR, SINV, 2, 1)
|
||||
FIELD(SCMR, SDIR, 3, 1)
|
||||
FIELD(SCMR, BCP2, 7, 1)
|
||||
REG8(SEMR, 7)
|
||||
FIELD(SEMR, ACS0, 0, 1)
|
||||
FIELD(SEMR, ABCS, 4, 1)
|
||||
|
||||
static int can_receive(void *opaque)
|
||||
{
|
||||
RSCIState *sci = RSCI(opaque);
|
||||
if (sci->rx_next > qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) {
|
||||
return 0;
|
||||
} else {
|
||||
return FIELD_EX8(sci->scr, SCR, RE);
|
||||
}
|
||||
}
|
||||
|
||||
static void receive(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
RSCIState *sci = RSCI(opaque);
|
||||
sci->rx_next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + sci->trtime;
|
||||
if (FIELD_EX8(sci->ssr, SSR, RDRF) || size > 1) {
|
||||
sci->ssr = FIELD_DP8(sci->ssr, SSR, ORER, 1);
|
||||
if (FIELD_EX8(sci->scr, SCR, RIE)) {
|
||||
qemu_set_irq(sci->irq[ERI], 1);
|
||||
}
|
||||
} else {
|
||||
sci->rdr = buf[0];
|
||||
sci->ssr = FIELD_DP8(sci->ssr, SSR, RDRF, 1);
|
||||
if (FIELD_EX8(sci->scr, SCR, RIE)) {
|
||||
qemu_irq_pulse(sci->irq[RXI]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void send_byte(RSCIState *sci)
|
||||
{
|
||||
if (qemu_chr_fe_backend_connected(&sci->chr)) {
|
||||
qemu_chr_fe_write_all(&sci->chr, &sci->tdr, 1);
|
||||
}
|
||||
timer_mod(&sci->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + sci->trtime);
|
||||
sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 0);
|
||||
sci->ssr = FIELD_DP8(sci->ssr, SSR, TDRE, 1);
|
||||
qemu_set_irq(sci->irq[TEI], 0);
|
||||
if (FIELD_EX8(sci->scr, SCR, TIE)) {
|
||||
qemu_irq_pulse(sci->irq[TXI]);
|
||||
}
|
||||
}
|
||||
|
||||
static void txend(void *opaque)
|
||||
{
|
||||
RSCIState *sci = RSCI(opaque);
|
||||
if (!FIELD_EX8(sci->ssr, SSR, TDRE)) {
|
||||
send_byte(sci);
|
||||
} else {
|
||||
sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 1);
|
||||
if (FIELD_EX8(sci->scr, SCR, TEIE)) {
|
||||
qemu_set_irq(sci->irq[TEI], 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void update_trtime(RSCIState *sci)
|
||||
{
|
||||
/* char per bits */
|
||||
sci->trtime = 8 - FIELD_EX8(sci->smr, SMR, CHR);
|
||||
sci->trtime += FIELD_EX8(sci->smr, SMR, PE);
|
||||
sci->trtime += FIELD_EX8(sci->smr, SMR, STOP) + 1;
|
||||
/* x bit transmit time (32 * divrate * brr) / base freq */
|
||||
sci->trtime *= 32 * sci->brr;
|
||||
sci->trtime *= 1 << (2 * FIELD_EX8(sci->smr, SMR, CKS));
|
||||
sci->trtime *= NANOSECONDS_PER_SECOND;
|
||||
sci->trtime /= sci->input_freq;
|
||||
}
|
||||
|
||||
static bool sci_is_tr_enabled(RSCIState *sci)
|
||||
{
|
||||
return FIELD_EX8(sci->scr, SCR, TE) || FIELD_EX8(sci->scr, SCR, RE);
|
||||
}
|
||||
|
||||
static void sci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
|
||||
{
|
||||
RSCIState *sci = RSCI(opaque);
|
||||
|
||||
switch (offset) {
|
||||
case A_SMR:
|
||||
if (!sci_is_tr_enabled(sci)) {
|
||||
sci->smr = val;
|
||||
update_trtime(sci);
|
||||
}
|
||||
break;
|
||||
case A_BRR:
|
||||
if (!sci_is_tr_enabled(sci)) {
|
||||
sci->brr = val;
|
||||
update_trtime(sci);
|
||||
}
|
||||
break;
|
||||
case A_SCR:
|
||||
sci->scr = val;
|
||||
if (FIELD_EX8(sci->scr, SCR, TE)) {
|
||||
sci->ssr = FIELD_DP8(sci->ssr, SSR, TDRE, 1);
|
||||
sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 1);
|
||||
if (FIELD_EX8(sci->scr, SCR, TIE)) {
|
||||
qemu_irq_pulse(sci->irq[TXI]);
|
||||
}
|
||||
}
|
||||
if (!FIELD_EX8(sci->scr, SCR, TEIE)) {
|
||||
qemu_set_irq(sci->irq[TEI], 0);
|
||||
}
|
||||
if (!FIELD_EX8(sci->scr, SCR, RIE)) {
|
||||
qemu_set_irq(sci->irq[ERI], 0);
|
||||
}
|
||||
break;
|
||||
case A_TDR:
|
||||
sci->tdr = val;
|
||||
if (FIELD_EX8(sci->ssr, SSR, TEND)) {
|
||||
send_byte(sci);
|
||||
} else {
|
||||
sci->ssr = FIELD_DP8(sci->ssr, SSR, TDRE, 0);
|
||||
}
|
||||
break;
|
||||
case A_SSR:
|
||||
sci->ssr = FIELD_DP8(sci->ssr, SSR, MPBT,
|
||||
FIELD_EX8(val, SSR, MPBT));
|
||||
sci->ssr = FIELD_DP8(sci->ssr, SSR, ERR,
|
||||
FIELD_EX8(val, SSR, ERR) & 0x07);
|
||||
if (FIELD_EX8(sci->read_ssr, SSR, ERR) &&
|
||||
FIELD_EX8(sci->ssr, SSR, ERR) == 0) {
|
||||
qemu_set_irq(sci->irq[ERI], 0);
|
||||
}
|
||||
break;
|
||||
case A_RDR:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "reneas_sci: RDR is read only.\n");
|
||||
break;
|
||||
case A_SCMR:
|
||||
sci->scmr = val; break;
|
||||
case A_SEMR: /* SEMR */
|
||||
sci->semr = val; break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "renesas_sci: Register 0x%" HWADDR_PRIX " "
|
||||
"not implemented\n",
|
||||
offset);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t sci_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
RSCIState *sci = RSCI(opaque);
|
||||
|
||||
switch (offset) {
|
||||
case A_SMR:
|
||||
return sci->smr;
|
||||
case A_BRR:
|
||||
return sci->brr;
|
||||
case A_SCR:
|
||||
return sci->scr;
|
||||
case A_TDR:
|
||||
return sci->tdr;
|
||||
case A_SSR:
|
||||
sci->read_ssr = sci->ssr;
|
||||
return sci->ssr;
|
||||
case A_RDR:
|
||||
sci->ssr = FIELD_DP8(sci->ssr, SSR, RDRF, 0);
|
||||
return sci->rdr;
|
||||
case A_SCMR:
|
||||
return sci->scmr;
|
||||
case A_SEMR:
|
||||
return sci->semr;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "renesas_sci: Register 0x%" HWADDR_PRIX
|
||||
" not implemented.\n", offset);
|
||||
}
|
||||
return UINT64_MAX;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps sci_ops = {
|
||||
.write = sci_write,
|
||||
.read = sci_read,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.impl.max_access_size = 1,
|
||||
.valid.max_access_size = 1,
|
||||
};
|
||||
|
||||
static void rsci_reset(DeviceState *dev)
|
||||
{
|
||||
RSCIState *sci = RSCI(dev);
|
||||
sci->smr = sci->scr = 0x00;
|
||||
sci->brr = 0xff;
|
||||
sci->tdr = 0xff;
|
||||
sci->rdr = 0x00;
|
||||
sci->ssr = 0x84;
|
||||
sci->scmr = 0x00;
|
||||
sci->semr = 0x00;
|
||||
sci->rx_next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
}
|
||||
|
||||
static void sci_event(void *opaque, QEMUChrEvent event)
|
||||
{
|
||||
RSCIState *sci = RSCI(opaque);
|
||||
if (event == CHR_EVENT_BREAK) {
|
||||
sci->ssr = FIELD_DP8(sci->ssr, SSR, FER, 1);
|
||||
if (FIELD_EX8(sci->scr, SCR, RIE)) {
|
||||
qemu_set_irq(sci->irq[ERI], 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rsci_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
RSCIState *sci = RSCI(dev);
|
||||
|
||||
if (sci->input_freq == 0) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"renesas_sci: input-freq property must be set.");
|
||||
return;
|
||||
}
|
||||
qemu_chr_fe_set_handlers(&sci->chr, can_receive, receive,
|
||||
sci_event, NULL, sci, NULL, true);
|
||||
}
|
||||
|
||||
static void rsci_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *d = SYS_BUS_DEVICE(obj);
|
||||
RSCIState *sci = RSCI(obj);
|
||||
int i;
|
||||
|
||||
memory_region_init_io(&sci->memory, OBJECT(sci), &sci_ops,
|
||||
sci, "renesas-sci", 0x8);
|
||||
sysbus_init_mmio(d, &sci->memory);
|
||||
|
||||
for (i = 0; i < SCI_NR_IRQ; i++) {
|
||||
sysbus_init_irq(d, &sci->irq[i]);
|
||||
}
|
||||
timer_init_ns(&sci->timer, QEMU_CLOCK_VIRTUAL, txend, sci);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_rsci = {
|
||||
.name = "renesas-sci",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_INT64(trtime, RSCIState),
|
||||
VMSTATE_INT64(rx_next, RSCIState),
|
||||
VMSTATE_UINT8(smr, RSCIState),
|
||||
VMSTATE_UINT8(brr, RSCIState),
|
||||
VMSTATE_UINT8(scr, RSCIState),
|
||||
VMSTATE_UINT8(tdr, RSCIState),
|
||||
VMSTATE_UINT8(ssr, RSCIState),
|
||||
VMSTATE_UINT8(rdr, RSCIState),
|
||||
VMSTATE_UINT8(scmr, RSCIState),
|
||||
VMSTATE_UINT8(semr, RSCIState),
|
||||
VMSTATE_UINT8(read_ssr, RSCIState),
|
||||
VMSTATE_TIMER(timer, RSCIState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static Property rsci_properties[] = {
|
||||
DEFINE_PROP_UINT64("input-freq", RSCIState, input_freq, 0),
|
||||
DEFINE_PROP_CHR("chardev", RSCIState, chr),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void rsci_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = rsci_realize;
|
||||
dc->vmsd = &vmstate_rsci;
|
||||
dc->reset = rsci_reset;
|
||||
device_class_set_props(dc, rsci_properties);
|
||||
}
|
||||
|
||||
static const TypeInfo rsci_info = {
|
||||
.name = TYPE_RENESAS_SCI,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(RSCIState),
|
||||
.instance_init = rsci_init,
|
||||
.class_init = rsci_class_init,
|
||||
};
|
||||
|
||||
static void rsci_register_types(void)
|
||||
{
|
||||
type_register_static(&rsci_info);
|
||||
}
|
||||
|
||||
type_init(rsci_register_types)
|
|
@ -61,3 +61,6 @@ config S390_FLIC_KVM
|
|||
|
||||
config OMPIC
|
||||
bool
|
||||
|
||||
config RX_ICU
|
||||
bool
|
||||
|
|
|
@ -20,6 +20,7 @@ common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_dist.o
|
|||
common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_redist.o
|
||||
common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_its_common.o
|
||||
common-obj-$(CONFIG_OPENPIC) += openpic.o
|
||||
common-obj-$(CONFIG_RX_ICU) += rx_icu.o
|
||||
common-obj-y += intc.o
|
||||
|
||||
obj-$(CONFIG_APIC) += apic.o apic_common.o
|
||||
|
|
|
@ -0,0 +1,397 @@
|
|||
/*
|
||||
* RX Interrupt Control Unit
|
||||
*
|
||||
* Warning: Only ICUa is supported.
|
||||
*
|
||||
* Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
|
||||
* (Rev.1.40 R01UH0033EJ0140)
|
||||
*
|
||||
* Copyright (c) 2019 Yoshinori Sato
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/registerfields.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/intc/rx_icu.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
||||
REG8(IR, 0)
|
||||
FIELD(IR, IR, 0, 1)
|
||||
REG8(DTCER, 0x100)
|
||||
FIELD(DTCER, DTCE, 0, 1)
|
||||
REG8(IER, 0x200)
|
||||
REG8(SWINTR, 0x2e0)
|
||||
FIELD(SWINTR, SWINT, 0, 1)
|
||||
REG16(FIR, 0x2f0)
|
||||
FIELD(FIR, FVCT, 0, 8)
|
||||
FIELD(FIR, FIEN, 15, 1)
|
||||
REG8(IPR, 0x300)
|
||||
FIELD(IPR, IPR, 0, 4)
|
||||
REG8(DMRSR, 0x400)
|
||||
REG8(IRQCR, 0x500)
|
||||
FIELD(IRQCR, IRQMD, 2, 2)
|
||||
REG8(NMISR, 0x580)
|
||||
FIELD(NMISR, NMIST, 0, 1)
|
||||
FIELD(NMISR, LVDST, 1, 1)
|
||||
FIELD(NMISR, OSTST, 2, 1)
|
||||
REG8(NMIER, 0x581)
|
||||
FIELD(NMIER, NMIEN, 0, 1)
|
||||
FIELD(NMIER, LVDEN, 1, 1)
|
||||
FIELD(NMIER, OSTEN, 2, 1)
|
||||
REG8(NMICLR, 0x582)
|
||||
FIELD(NMICLR, NMICLR, 0, 1)
|
||||
FIELD(NMICLR, OSTCLR, 2, 1)
|
||||
REG8(NMICR, 0x583)
|
||||
FIELD(NMICR, NMIMD, 3, 1)
|
||||
|
||||
static void set_irq(RXICUState *icu, int n_IRQ, int req)
|
||||
{
|
||||
if ((icu->fir & R_FIR_FIEN_MASK) &&
|
||||
(icu->fir & R_FIR_FVCT_MASK) == n_IRQ) {
|
||||
qemu_set_irq(icu->_fir, req);
|
||||
} else {
|
||||
qemu_set_irq(icu->_irq, req);
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t rxicu_level(RXICUState *icu, unsigned n)
|
||||
{
|
||||
return (icu->ipr[icu->map[n]] << 8) | n;
|
||||
}
|
||||
|
||||
static void rxicu_request(RXICUState *icu, int n_IRQ)
|
||||
{
|
||||
int enable;
|
||||
|
||||
enable = icu->ier[n_IRQ / 8] & (1 << (n_IRQ & 7));
|
||||
if (n_IRQ > 0 && enable != 0 && atomic_read(&icu->req_irq) < 0) {
|
||||
atomic_set(&icu->req_irq, n_IRQ);
|
||||
set_irq(icu, n_IRQ, rxicu_level(icu, n_IRQ));
|
||||
}
|
||||
}
|
||||
|
||||
static void rxicu_set_irq(void *opaque, int n_IRQ, int level)
|
||||
{
|
||||
RXICUState *icu = opaque;
|
||||
struct IRQSource *src;
|
||||
int issue;
|
||||
|
||||
if (n_IRQ >= NR_IRQS) {
|
||||
error_report("%s: IRQ %d out of range", __func__, n_IRQ);
|
||||
return;
|
||||
}
|
||||
|
||||
src = &icu->src[n_IRQ];
|
||||
|
||||
level = (level != 0);
|
||||
switch (src->sense) {
|
||||
case TRG_LEVEL:
|
||||
/* level-sensitive irq */
|
||||
issue = level;
|
||||
src->level = level;
|
||||
break;
|
||||
case TRG_NEDGE:
|
||||
issue = (level == 0 && src->level == 1);
|
||||
src->level = level;
|
||||
break;
|
||||
case TRG_PEDGE:
|
||||
issue = (level == 1 && src->level == 0);
|
||||
src->level = level;
|
||||
break;
|
||||
case TRG_BEDGE:
|
||||
issue = ((level ^ src->level) & 1);
|
||||
src->level = level;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
if (issue == 0 && src->sense == TRG_LEVEL) {
|
||||
icu->ir[n_IRQ] = 0;
|
||||
if (atomic_read(&icu->req_irq) == n_IRQ) {
|
||||
/* clear request */
|
||||
set_irq(icu, n_IRQ, 0);
|
||||
atomic_set(&icu->req_irq, -1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (issue) {
|
||||
icu->ir[n_IRQ] = 1;
|
||||
rxicu_request(icu, n_IRQ);
|
||||
}
|
||||
}
|
||||
|
||||
static void rxicu_ack_irq(void *opaque, int no, int level)
|
||||
{
|
||||
RXICUState *icu = opaque;
|
||||
int i;
|
||||
int n_IRQ;
|
||||
int max_pri;
|
||||
|
||||
n_IRQ = atomic_read(&icu->req_irq);
|
||||
if (n_IRQ < 0) {
|
||||
return;
|
||||
}
|
||||
atomic_set(&icu->req_irq, -1);
|
||||
if (icu->src[n_IRQ].sense != TRG_LEVEL) {
|
||||
icu->ir[n_IRQ] = 0;
|
||||
}
|
||||
|
||||
max_pri = 0;
|
||||
n_IRQ = -1;
|
||||
for (i = 0; i < NR_IRQS; i++) {
|
||||
if (icu->ir[i]) {
|
||||
if (max_pri < icu->ipr[icu->map[i]]) {
|
||||
n_IRQ = i;
|
||||
max_pri = icu->ipr[icu->map[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (n_IRQ >= 0) {
|
||||
rxicu_request(icu, n_IRQ);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t icu_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
RXICUState *icu = opaque;
|
||||
int reg = addr & 0xff;
|
||||
|
||||
if ((addr != A_FIR && size != 1) ||
|
||||
(addr == A_FIR && size != 2)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "rx_icu: Invalid read size 0x%"
|
||||
HWADDR_PRIX "\n",
|
||||
addr);
|
||||
return UINT64_MAX;
|
||||
}
|
||||
switch (addr) {
|
||||
case A_IR ... A_IR + 0xff:
|
||||
return icu->ir[reg] & R_IR_IR_MASK;
|
||||
case A_DTCER ... A_DTCER + 0xff:
|
||||
return icu->dtcer[reg] & R_DTCER_DTCE_MASK;
|
||||
case A_IER ... A_IER + 0x1f:
|
||||
return icu->ier[reg];
|
||||
case A_SWINTR:
|
||||
return 0;
|
||||
case A_FIR:
|
||||
return icu->fir & (R_FIR_FIEN_MASK | R_FIR_FVCT_MASK);
|
||||
case A_IPR ... A_IPR + 0x8f:
|
||||
return icu->ipr[reg] & R_IPR_IPR_MASK;
|
||||
case A_DMRSR:
|
||||
case A_DMRSR + 4:
|
||||
case A_DMRSR + 8:
|
||||
case A_DMRSR + 12:
|
||||
return icu->dmasr[reg >> 2];
|
||||
case A_IRQCR ... A_IRQCR + 0x1f:
|
||||
return icu->src[64 + reg].sense << R_IRQCR_IRQMD_SHIFT;
|
||||
case A_NMISR:
|
||||
case A_NMICLR:
|
||||
return 0;
|
||||
case A_NMIER:
|
||||
return icu->nmier;
|
||||
case A_NMICR:
|
||||
return icu->nmicr;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "rx_icu: Register 0x%" HWADDR_PRIX " "
|
||||
"not implemented.\n",
|
||||
addr);
|
||||
break;
|
||||
}
|
||||
return UINT64_MAX;
|
||||
}
|
||||
|
||||
static void icu_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
RXICUState *icu = opaque;
|
||||
int reg = addr & 0xff;
|
||||
|
||||
if ((addr != A_FIR && size != 1) ||
|
||||
(addr == A_FIR && size != 2)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "rx_icu: Invalid write size at "
|
||||
"0x%" HWADDR_PRIX "\n",
|
||||
addr);
|
||||
return;
|
||||
}
|
||||
switch (addr) {
|
||||
case A_IR ... A_IR + 0xff:
|
||||
if (icu->src[reg].sense != TRG_LEVEL && val == 0) {
|
||||
icu->ir[reg] = 0;
|
||||
}
|
||||
break;
|
||||
case A_DTCER ... A_DTCER + 0xff:
|
||||
icu->dtcer[reg] = val & R_DTCER_DTCE_MASK;
|
||||
qemu_log_mask(LOG_UNIMP, "rx_icu: DTC not implemented\n");
|
||||
break;
|
||||
case A_IER ... A_IER + 0x1f:
|
||||
icu->ier[reg] = val;
|
||||
break;
|
||||
case A_SWINTR:
|
||||
if (val & R_SWINTR_SWINT_MASK) {
|
||||
qemu_irq_pulse(icu->_swi);
|
||||
}
|
||||
break;
|
||||
case A_FIR:
|
||||
icu->fir = val & (R_FIR_FIEN_MASK | R_FIR_FVCT_MASK);
|
||||
break;
|
||||
case A_IPR ... A_IPR + 0x8f:
|
||||
icu->ipr[reg] = val & R_IPR_IPR_MASK;
|
||||
break;
|
||||
case A_DMRSR:
|
||||
case A_DMRSR + 4:
|
||||
case A_DMRSR + 8:
|
||||
case A_DMRSR + 12:
|
||||
icu->dmasr[reg >> 2] = val;
|
||||
qemu_log_mask(LOG_UNIMP, "rx_icu: DMAC not implemented\n");
|
||||
break;
|
||||
case A_IRQCR ... A_IRQCR + 0x1f:
|
||||
icu->src[64 + reg].sense = val >> R_IRQCR_IRQMD_SHIFT;
|
||||
break;
|
||||
case A_NMICLR:
|
||||
break;
|
||||
case A_NMIER:
|
||||
icu->nmier |= val & (R_NMIER_NMIEN_MASK |
|
||||
R_NMIER_LVDEN_MASK |
|
||||
R_NMIER_OSTEN_MASK);
|
||||
break;
|
||||
case A_NMICR:
|
||||
if ((icu->nmier & R_NMIER_NMIEN_MASK) == 0) {
|
||||
icu->nmicr = val & R_NMICR_NMIMD_MASK;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "rx_icu: Register 0x%" HWADDR_PRIX " "
|
||||
"not implemented\n",
|
||||
addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps icu_ops = {
|
||||
.write = icu_write,
|
||||
.read = icu_read,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 2,
|
||||
},
|
||||
.valid = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 2,
|
||||
},
|
||||
};
|
||||
|
||||
static void rxicu_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
RXICUState *icu = RX_ICU(dev);
|
||||
int i, j;
|
||||
|
||||
if (icu->init_sense == NULL) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"rx_icu: trigger-level property must be set.");
|
||||
return;
|
||||
}
|
||||
for (i = j = 0; i < NR_IRQS; i++) {
|
||||
if (icu->init_sense[j] == i) {
|
||||
icu->src[i].sense = TRG_LEVEL;
|
||||
if (j < icu->nr_sense) {
|
||||
j++;
|
||||
}
|
||||
} else {
|
||||
icu->src[i].sense = TRG_PEDGE;
|
||||
}
|
||||
}
|
||||
icu->req_irq = -1;
|
||||
}
|
||||
|
||||
static void rxicu_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *d = SYS_BUS_DEVICE(obj);
|
||||
RXICUState *icu = RX_ICU(obj);
|
||||
|
||||
memory_region_init_io(&icu->memory, OBJECT(icu), &icu_ops,
|
||||
icu, "rx-icu", 0x600);
|
||||
sysbus_init_mmio(d, &icu->memory);
|
||||
|
||||
qdev_init_gpio_in(DEVICE(d), rxicu_set_irq, NR_IRQS);
|
||||
qdev_init_gpio_in_named(DEVICE(d), rxicu_ack_irq, "ack", 1);
|
||||
sysbus_init_irq(d, &icu->_irq);
|
||||
sysbus_init_irq(d, &icu->_fir);
|
||||
sysbus_init_irq(d, &icu->_swi);
|
||||
}
|
||||
|
||||
static void rxicu_fini(Object *obj)
|
||||
{
|
||||
RXICUState *icu = RX_ICU(obj);
|
||||
g_free(icu->map);
|
||||
g_free(icu->init_sense);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_rxicu = {
|
||||
.name = "rx-icu",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT8_ARRAY(ir, RXICUState, NR_IRQS),
|
||||
VMSTATE_UINT8_ARRAY(dtcer, RXICUState, NR_IRQS),
|
||||
VMSTATE_UINT8_ARRAY(ier, RXICUState, NR_IRQS / 8),
|
||||
VMSTATE_UINT8_ARRAY(ipr, RXICUState, 142),
|
||||
VMSTATE_UINT8_ARRAY(dmasr, RXICUState, 4),
|
||||
VMSTATE_UINT16(fir, RXICUState),
|
||||
VMSTATE_UINT8(nmisr, RXICUState),
|
||||
VMSTATE_UINT8(nmier, RXICUState),
|
||||
VMSTATE_UINT8(nmiclr, RXICUState),
|
||||
VMSTATE_UINT8(nmicr, RXICUState),
|
||||
VMSTATE_INT16(req_irq, RXICUState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static Property rxicu_properties[] = {
|
||||
DEFINE_PROP_ARRAY("ipr-map", RXICUState, nr_irqs, map,
|
||||
qdev_prop_uint8, uint8_t),
|
||||
DEFINE_PROP_ARRAY("trigger-level", RXICUState, nr_sense, init_sense,
|
||||
qdev_prop_uint8, uint8_t),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void rxicu_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = rxicu_realize;
|
||||
dc->vmsd = &vmstate_rxicu;
|
||||
device_class_set_props(dc, rxicu_properties);
|
||||
}
|
||||
|
||||
static const TypeInfo rxicu_info = {
|
||||
.name = TYPE_RX_ICU,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(RXICUState),
|
||||
.instance_init = rxicu_init,
|
||||
.instance_finalize = rxicu_fini,
|
||||
.class_init = rxicu_class_init,
|
||||
};
|
||||
|
||||
static void rxicu_register_types(void)
|
||||
{
|
||||
type_register_static(&rxicu_info);
|
||||
}
|
||||
|
||||
type_init(rxicu_register_types)
|
|
@ -0,0 +1,10 @@
|
|||
config RX62N_MCU
|
||||
bool
|
||||
select RX_ICU
|
||||
select RENESAS_TMR
|
||||
select RENESAS_CMT
|
||||
select RENESAS_SCI
|
||||
|
||||
config RX_GDBSIM
|
||||
bool
|
||||
select RX62N_MCU
|
|
@ -0,0 +1,2 @@
|
|||
obj-$(CONFIG_RX62N_MCU) += rx62n.o
|
||||
obj-$(CONFIG_RX_GDBSIM) += rx-gdbsim.o
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* RX QEMU GDB simulator
|
||||
*
|
||||
* Copyright (c) 2019 Yoshinori Sato
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "cpu.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/loader.h"
|
||||
#include "hw/rx/rx62n.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "sysemu/device_tree.h"
|
||||
#include "hw/boards.h"
|
||||
|
||||
/* Same address of GDB integrated simulator */
|
||||
#define SDRAM_BASE EXT_CS_BASE
|
||||
|
||||
typedef struct RxGdbSimMachineClass {
|
||||
/*< private >*/
|
||||
MachineClass parent_class;
|
||||
/*< public >*/
|
||||
const char *mcu_name;
|
||||
uint32_t xtal_freq_hz;
|
||||
} RxGdbSimMachineClass;
|
||||
|
||||
typedef struct RxGdbSimMachineState {
|
||||
/*< private >*/
|
||||
MachineState parent_obj;
|
||||
/*< public >*/
|
||||
RX62NState mcu;
|
||||
} RxGdbSimMachineState;
|
||||
|
||||
#define TYPE_RX_GDBSIM_MACHINE MACHINE_TYPE_NAME("rx62n-common")
|
||||
|
||||
#define RX_GDBSIM_MACHINE(obj) \
|
||||
OBJECT_CHECK(RxGdbSimMachineState, (obj), TYPE_RX_GDBSIM_MACHINE)
|
||||
|
||||
#define RX_GDBSIM_MACHINE_CLASS(klass) \
|
||||
OBJECT_CLASS_CHECK(RxGdbSimMachineClass, (klass), TYPE_RX_GDBSIM_MACHINE)
|
||||
#define RX_GDBSIM_MACHINE_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(RxGdbSimMachineClass, (obj), TYPE_RX_GDBSIM_MACHINE)
|
||||
|
||||
static void rx_load_image(RXCPU *cpu, const char *filename,
|
||||
uint32_t start, uint32_t size)
|
||||
{
|
||||
static uint32_t extable[32];
|
||||
long kernel_size;
|
||||
int i;
|
||||
|
||||
kernel_size = load_image_targphys(filename, start, size);
|
||||
if (kernel_size < 0) {
|
||||
fprintf(stderr, "qemu: could not load kernel '%s'\n", filename);
|
||||
exit(1);
|
||||
}
|
||||
cpu->env.pc = start;
|
||||
|
||||
/* setup exception trap trampoline */
|
||||
/* linux kernel only works little-endian mode */
|
||||
for (i = 0; i < ARRAY_SIZE(extable); i++) {
|
||||
extable[i] = cpu_to_le32(0x10 + i * 4);
|
||||
}
|
||||
rom_add_blob_fixed("extable", extable, sizeof(extable), VECTOR_TABLE_BASE);
|
||||
}
|
||||
|
||||
static void rx_gdbsim_init(MachineState *machine)
|
||||
{
|
||||
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
||||
RxGdbSimMachineState *s = RX_GDBSIM_MACHINE(machine);
|
||||
RxGdbSimMachineClass *rxc = RX_GDBSIM_MACHINE_GET_CLASS(machine);
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
const char *kernel_filename = machine->kernel_filename;
|
||||
const char *dtb_filename = machine->dtb;
|
||||
|
||||
if (machine->ram_size < mc->default_ram_size) {
|
||||
char *sz = size_to_str(mc->default_ram_size);
|
||||
error_report("Invalid RAM size, should be more than %s", sz);
|
||||
g_free(sz);
|
||||
}
|
||||
|
||||
/* Allocate memory space */
|
||||
memory_region_add_subregion(sysmem, SDRAM_BASE, machine->ram);
|
||||
|
||||
/* Initialize MCU */
|
||||
object_initialize_child(OBJECT(machine), "mcu", &s->mcu, rxc->mcu_name);
|
||||
object_property_set_link(OBJECT(&s->mcu), OBJECT(sysmem),
|
||||
"main-bus", &error_abort);
|
||||
object_property_set_uint(OBJECT(&s->mcu), rxc->xtal_freq_hz,
|
||||
"xtal-frequency-hz", &error_abort);
|
||||
object_property_set_bool(OBJECT(&s->mcu), kernel_filename != NULL,
|
||||
"load-kernel", &error_abort);
|
||||
qdev_realize(DEVICE(&s->mcu), NULL, &error_abort);
|
||||
|
||||
/* Load kernel and dtb */
|
||||
if (kernel_filename) {
|
||||
ram_addr_t kernel_offset;
|
||||
|
||||
/*
|
||||
* The kernel image is loaded into
|
||||
* the latter half of the SDRAM space.
|
||||
*/
|
||||
kernel_offset = machine->ram_size / 2;
|
||||
rx_load_image(RXCPU(first_cpu), kernel_filename,
|
||||
SDRAM_BASE + kernel_offset, kernel_offset);
|
||||
if (dtb_filename) {
|
||||
ram_addr_t dtb_offset;
|
||||
int dtb_size;
|
||||
void *dtb;
|
||||
|
||||
dtb = load_device_tree(dtb_filename, &dtb_size);
|
||||
if (dtb == NULL) {
|
||||
error_report("Couldn't open dtb file %s", dtb_filename);
|
||||
exit(1);
|
||||
}
|
||||
if (machine->kernel_cmdline &&
|
||||
qemu_fdt_setprop_string(dtb, "/chosen", "bootargs",
|
||||
machine->kernel_cmdline) < 0) {
|
||||
error_report("Couldn't set /chosen/bootargs");
|
||||
exit(1);
|
||||
}
|
||||
/* DTB is located at the end of SDRAM space. */
|
||||
dtb_offset = machine->ram_size - dtb_size;
|
||||
rom_add_blob_fixed("dtb", dtb, dtb_size,
|
||||
SDRAM_BASE + dtb_offset);
|
||||
/* Set dtb address to R1 */
|
||||
RXCPU(first_cpu)->env.regs[1] = SDRAM_BASE + dtb_offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rx_gdbsim_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
|
||||
mc->init = rx_gdbsim_init;
|
||||
mc->default_cpu_type = TYPE_RX62N_CPU;
|
||||
mc->default_ram_size = 16 * MiB;
|
||||
mc->default_ram_id = "ext-sdram";
|
||||
}
|
||||
|
||||
static void rx62n7_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
RxGdbSimMachineClass *rxc = RX_GDBSIM_MACHINE_CLASS(oc);
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
|
||||
rxc->mcu_name = TYPE_R5F562N7_MCU;
|
||||
rxc->xtal_freq_hz = 12 * 1000 * 1000;
|
||||
mc->desc = "gdb simulator (R5F562N7 MCU and external RAM)";
|
||||
};
|
||||
|
||||
static void rx62n8_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
RxGdbSimMachineClass *rxc = RX_GDBSIM_MACHINE_CLASS(oc);
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
|
||||
rxc->mcu_name = TYPE_R5F562N8_MCU;
|
||||
rxc->xtal_freq_hz = 12 * 1000 * 1000;
|
||||
mc->desc = "gdb simulator (R5F562N8 MCU and external RAM)";
|
||||
};
|
||||
|
||||
static const TypeInfo rx_gdbsim_types[] = {
|
||||
{
|
||||
.name = MACHINE_TYPE_NAME("gdbsim-r5f562n7"),
|
||||
.parent = TYPE_RX_GDBSIM_MACHINE,
|
||||
.class_init = rx62n7_class_init,
|
||||
}, {
|
||||
.name = MACHINE_TYPE_NAME("gdbsim-r5f562n8"),
|
||||
.parent = TYPE_RX_GDBSIM_MACHINE,
|
||||
.class_init = rx62n8_class_init,
|
||||
}, {
|
||||
.name = TYPE_RX_GDBSIM_MACHINE,
|
||||
.parent = TYPE_MACHINE,
|
||||
.instance_size = sizeof(RxGdbSimMachineState),
|
||||
.class_size = sizeof(RxGdbSimMachineClass),
|
||||
.class_init = rx_gdbsim_class_init,
|
||||
.abstract = true,
|
||||
}
|
||||
};
|
||||
|
||||
DEFINE_TYPES(rx_gdbsim_types)
|
|
@ -0,0 +1,323 @@
|
|||
/*
|
||||
* RX62N Microcontroller
|
||||
*
|
||||
* Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
|
||||
* (Rev.1.40 R01UH0033EJ0140)
|
||||
*
|
||||
* Copyright (c) 2019 Yoshinori Sato
|
||||
* Copyright (c) 2020 Philippe Mathieu-Daudé
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/rx/rx62n.h"
|
||||
#include "hw/loader.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "cpu.h"
|
||||
|
||||
/*
|
||||
* RX62N Internal Memory
|
||||
*/
|
||||
#define RX62N_IRAM_BASE 0x00000000
|
||||
#define RX62N_DFLASH_BASE 0x00100000
|
||||
#define RX62N_CFLASH_BASE 0xfff80000
|
||||
|
||||
/*
|
||||
* RX62N Peripheral Address
|
||||
* See users manual section 5
|
||||
*/
|
||||
#define RX62N_ICU_BASE 0x00087000
|
||||
#define RX62N_TMR_BASE 0x00088200
|
||||
#define RX62N_CMT_BASE 0x00088000
|
||||
#define RX62N_SCI_BASE 0x00088240
|
||||
|
||||
/*
|
||||
* RX62N Peripheral IRQ
|
||||
* See users manual section 11
|
||||
*/
|
||||
#define RX62N_TMR_IRQ 174
|
||||
#define RX62N_CMT_IRQ 28
|
||||
#define RX62N_SCI_IRQ 214
|
||||
|
||||
#define RX62N_XTAL_MIN_HZ (8 * 1000 * 1000)
|
||||
#define RX62N_XTAL_MAX_HZ (14 * 1000 * 1000)
|
||||
#define RX62N_PCLK_MAX_HZ (50 * 1000 * 1000)
|
||||
|
||||
typedef struct RX62NClass {
|
||||
/*< private >*/
|
||||
DeviceClass parent_class;
|
||||
/*< public >*/
|
||||
const char *name;
|
||||
uint64_t ram_size;
|
||||
uint64_t rom_flash_size;
|
||||
uint64_t data_flash_size;
|
||||
} RX62NClass;
|
||||
|
||||
#define RX62N_MCU_CLASS(klass) \
|
||||
OBJECT_CLASS_CHECK(RX62NClass, (klass), TYPE_RX62N_MCU)
|
||||
#define RX62N_MCU_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(RX62NClass, (obj), TYPE_RX62N_MCU)
|
||||
|
||||
/*
|
||||
* IRQ -> IPR mapping table
|
||||
* 0x00 - 0x91: IPR no (IPR00 to IPR91)
|
||||
* 0xff: IPR not assigned
|
||||
* See "11.3.1 Interrupt Vector Table" in hardware manual.
|
||||
*/
|
||||
static const uint8_t ipr_table[NR_IRQS] = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 15 */
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0x01, 0xff, 0x02,
|
||||
0xff, 0xff, 0xff, 0x03, 0x04, 0x05, 0x06, 0x07, /* 31 */
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x14, 0x14, 0x14, /* 47 */
|
||||
0x15, 0x15, 0x15, 0x15, 0xff, 0xff, 0xff, 0xff,
|
||||
0x18, 0x18, 0x18, 0x18, 0x18, 0x1d, 0x1e, 0x1f, /* 63 */
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
|
||||
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 79 */
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0x3a, 0x3b, 0x3c, 0xff, 0xff, 0xff, /* 95 */
|
||||
0x40, 0xff, 0x44, 0x45, 0xff, 0xff, 0x48, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 111 */
|
||||
0xff, 0xff, 0x51, 0x51, 0x51, 0x51, 0x52, 0x52,
|
||||
0x52, 0x53, 0x53, 0x54, 0x54, 0x55, 0x55, 0x56, /* 127 */
|
||||
0x56, 0x57, 0x57, 0x57, 0x57, 0x58, 0x59, 0x59,
|
||||
0x59, 0x59, 0x5a, 0x5b, 0x5b, 0x5b, 0x5c, 0x5c, /* 143 */
|
||||
0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5e, 0x5e, 0x5f,
|
||||
0x5f, 0x60, 0x60, 0x61, 0x61, 0x62, 0x62, 0x62, /* 159 */
|
||||
0x62, 0x63, 0x64, 0x64, 0x64, 0x64, 0x65, 0x66,
|
||||
0x66, 0x66, 0x67, 0x67, 0x67, 0x67, 0x68, 0x68, /* 175 */
|
||||
0x68, 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6b,
|
||||
0x6b, 0x6b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 191 */
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x70, 0x71,
|
||||
0x72, 0x73, 0x74, 0x75, 0xff, 0xff, 0xff, 0xff, /* 207 */
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x80,
|
||||
0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, /* 223 */
|
||||
0x82, 0x82, 0x83, 0x83, 0x83, 0x83, 0xff, 0xff,
|
||||
0xff, 0xff, 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, /* 239 */
|
||||
0x86, 0x86, 0xff, 0xff, 0xff, 0xff, 0x88, 0x89,
|
||||
0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, /* 255 */
|
||||
};
|
||||
|
||||
/*
|
||||
* Level triggerd IRQ list
|
||||
* Not listed IRQ is Edge trigger.
|
||||
* See "11.3.1 Interrupt Vector Table" in hardware manual.
|
||||
*/
|
||||
static const uint8_t levelirq[] = {
|
||||
16, 21, 32, 44, 47, 48, 51, 64, 65, 66,
|
||||
67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
|
||||
77, 78, 79, 90, 91, 170, 171, 172, 173, 214,
|
||||
217, 218, 221, 222, 225, 226, 229, 234, 237, 238,
|
||||
241, 246, 249, 250, 253,
|
||||
};
|
||||
|
||||
static void register_icu(RX62NState *s)
|
||||
{
|
||||
int i;
|
||||
SysBusDevice *icu;
|
||||
|
||||
object_initialize_child(OBJECT(s), "icu", &s->icu, TYPE_RX_ICU);
|
||||
icu = SYS_BUS_DEVICE(&s->icu);
|
||||
qdev_prop_set_uint32(DEVICE(icu), "len-ipr-map", NR_IRQS);
|
||||
for (i = 0; i < NR_IRQS; i++) {
|
||||
char propname[32];
|
||||
snprintf(propname, sizeof(propname), "ipr-map[%d]", i);
|
||||
qdev_prop_set_uint32(DEVICE(icu), propname, ipr_table[i]);
|
||||
}
|
||||
qdev_prop_set_uint32(DEVICE(icu), "len-trigger-level",
|
||||
ARRAY_SIZE(levelirq));
|
||||
for (i = 0; i < ARRAY_SIZE(levelirq); i++) {
|
||||
char propname[32];
|
||||
snprintf(propname, sizeof(propname), "trigger-level[%d]", i);
|
||||
qdev_prop_set_uint32(DEVICE(icu), propname, levelirq[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < NR_IRQS; i++) {
|
||||
s->irq[i] = qdev_get_gpio_in(DEVICE(icu), i);
|
||||
}
|
||||
sysbus_realize(icu, &error_abort);
|
||||
sysbus_connect_irq(icu, 0, qdev_get_gpio_in(DEVICE(&s->cpu), RX_CPU_IRQ));
|
||||
sysbus_connect_irq(icu, 1, qdev_get_gpio_in(DEVICE(&s->cpu), RX_CPU_FIR));
|
||||
sysbus_connect_irq(icu, 2, s->irq[SWI]);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(icu), 0, RX62N_ICU_BASE);
|
||||
}
|
||||
|
||||
static void register_tmr(RX62NState *s, int unit)
|
||||
{
|
||||
SysBusDevice *tmr;
|
||||
int i, irqbase;
|
||||
|
||||
object_initialize_child(OBJECT(s), "tmr[*]",
|
||||
&s->tmr[unit], TYPE_RENESAS_TMR);
|
||||
tmr = SYS_BUS_DEVICE(&s->tmr[unit]);
|
||||
qdev_prop_set_uint64(DEVICE(tmr), "input-freq", s->pclk_freq_hz);
|
||||
sysbus_realize(tmr, &error_abort);
|
||||
|
||||
irqbase = RX62N_TMR_IRQ + TMR_NR_IRQ * unit;
|
||||
for (i = 0; i < TMR_NR_IRQ; i++) {
|
||||
sysbus_connect_irq(tmr, i, s->irq[irqbase + i]);
|
||||
}
|
||||
sysbus_mmio_map(tmr, 0, RX62N_TMR_BASE + unit * 0x10);
|
||||
}
|
||||
|
||||
static void register_cmt(RX62NState *s, int unit)
|
||||
{
|
||||
SysBusDevice *cmt;
|
||||
int i, irqbase;
|
||||
|
||||
object_initialize_child(OBJECT(s), "cmt[*]",
|
||||
&s->cmt[unit], TYPE_RENESAS_CMT);
|
||||
cmt = SYS_BUS_DEVICE(&s->cmt[unit]);
|
||||
qdev_prop_set_uint64(DEVICE(cmt), "input-freq", s->pclk_freq_hz);
|
||||
sysbus_realize(cmt, &error_abort);
|
||||
|
||||
irqbase = RX62N_CMT_IRQ + CMT_NR_IRQ * unit;
|
||||
for (i = 0; i < CMT_NR_IRQ; i++) {
|
||||
sysbus_connect_irq(cmt, i, s->irq[irqbase + i]);
|
||||
}
|
||||
sysbus_mmio_map(cmt, 0, RX62N_CMT_BASE + unit * 0x10);
|
||||
}
|
||||
|
||||
static void register_sci(RX62NState *s, int unit)
|
||||
{
|
||||
SysBusDevice *sci;
|
||||
int i, irqbase;
|
||||
|
||||
object_initialize_child(OBJECT(s), "sci[*]",
|
||||
&s->sci[unit], TYPE_RENESAS_SCI);
|
||||
sci = SYS_BUS_DEVICE(&s->sci[unit]);
|
||||
qdev_prop_set_chr(DEVICE(sci), "chardev", serial_hd(unit));
|
||||
qdev_prop_set_uint64(DEVICE(sci), "input-freq", s->pclk_freq_hz);
|
||||
sysbus_realize(sci, &error_abort);
|
||||
|
||||
irqbase = RX62N_SCI_IRQ + SCI_NR_IRQ * unit;
|
||||
for (i = 0; i < SCI_NR_IRQ; i++) {
|
||||
sysbus_connect_irq(sci, i, s->irq[irqbase + i]);
|
||||
}
|
||||
sysbus_mmio_map(sci, 0, RX62N_SCI_BASE + unit * 0x08);
|
||||
}
|
||||
|
||||
static void rx62n_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
RX62NState *s = RX62N_MCU(dev);
|
||||
RX62NClass *rxc = RX62N_MCU_GET_CLASS(dev);
|
||||
|
||||
if (s->xtal_freq_hz == 0) {
|
||||
error_setg(errp, "\"xtal-frequency-hz\" property must be provided.");
|
||||
return;
|
||||
}
|
||||
/* XTAL range: 8-14 MHz */
|
||||
if (s->xtal_freq_hz < RX62N_XTAL_MIN_HZ
|
||||
|| s->xtal_freq_hz > RX62N_XTAL_MAX_HZ) {
|
||||
error_setg(errp, "\"xtal-frequency-hz\" property in incorrect range.");
|
||||
return;
|
||||
}
|
||||
/* Use a 4x fixed multiplier */
|
||||
s->pclk_freq_hz = 4 * s->xtal_freq_hz;
|
||||
/* PCLK range: 8-50 MHz */
|
||||
assert(s->pclk_freq_hz <= RX62N_PCLK_MAX_HZ);
|
||||
|
||||
memory_region_init_ram(&s->iram, OBJECT(dev), "iram",
|
||||
rxc->ram_size, &error_abort);
|
||||
memory_region_add_subregion(s->sysmem, RX62N_IRAM_BASE, &s->iram);
|
||||
memory_region_init_rom(&s->d_flash, OBJECT(dev), "flash-data",
|
||||
rxc->data_flash_size, &error_abort);
|
||||
memory_region_add_subregion(s->sysmem, RX62N_DFLASH_BASE, &s->d_flash);
|
||||
memory_region_init_rom(&s->c_flash, OBJECT(dev), "flash-code",
|
||||
rxc->rom_flash_size, &error_abort);
|
||||
memory_region_add_subregion(s->sysmem, RX62N_CFLASH_BASE, &s->c_flash);
|
||||
|
||||
if (!s->kernel) {
|
||||
if (bios_name) {
|
||||
rom_add_file_fixed(bios_name, RX62N_CFLASH_BASE, 0);
|
||||
} else if (!qtest_enabled()) {
|
||||
error_report("No bios or kernel specified");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize CPU */
|
||||
object_initialize_child(OBJECT(s), "cpu", &s->cpu, TYPE_RX62N_CPU);
|
||||
qdev_realize(DEVICE(&s->cpu), NULL, &error_abort);
|
||||
|
||||
register_icu(s);
|
||||
s->cpu.env.ack = qdev_get_gpio_in_named(DEVICE(&s->icu), "ack", 0);
|
||||
register_tmr(s, 0);
|
||||
register_tmr(s, 1);
|
||||
register_cmt(s, 0);
|
||||
register_cmt(s, 1);
|
||||
register_sci(s, 0);
|
||||
}
|
||||
|
||||
static Property rx62n_properties[] = {
|
||||
DEFINE_PROP_LINK("main-bus", RX62NState, sysmem, TYPE_MEMORY_REGION,
|
||||
MemoryRegion *),
|
||||
DEFINE_PROP_BOOL("load-kernel", RX62NState, kernel, false),
|
||||
DEFINE_PROP_UINT32("xtal-frequency-hz", RX62NState, xtal_freq_hz, 0),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void rx62n_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = rx62n_realize;
|
||||
device_class_set_props(dc, rx62n_properties);
|
||||
}
|
||||
|
||||
static void r5f562n7_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
RX62NClass *rxc = RX62N_MCU_CLASS(oc);
|
||||
|
||||
rxc->ram_size = 64 * KiB;
|
||||
rxc->rom_flash_size = 384 * KiB;
|
||||
rxc->data_flash_size = 32 * KiB;
|
||||
};
|
||||
|
||||
static void r5f562n8_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
RX62NClass *rxc = RX62N_MCU_CLASS(oc);
|
||||
|
||||
rxc->ram_size = 96 * KiB;
|
||||
rxc->rom_flash_size = 512 * KiB;
|
||||
rxc->data_flash_size = 32 * KiB;
|
||||
};
|
||||
|
||||
static const TypeInfo rx62n_types[] = {
|
||||
{
|
||||
.name = TYPE_R5F562N7_MCU,
|
||||
.parent = TYPE_RX62N_MCU,
|
||||
.class_init = r5f562n7_class_init,
|
||||
}, {
|
||||
.name = TYPE_R5F562N8_MCU,
|
||||
.parent = TYPE_RX62N_MCU,
|
||||
.class_init = r5f562n8_class_init,
|
||||
}, {
|
||||
.name = TYPE_RX62N_MCU,
|
||||
.parent = TYPE_DEVICE,
|
||||
.instance_size = sizeof(RX62NState),
|
||||
.class_size = sizeof(RX62NClass),
|
||||
.class_init = rx62n_class_init,
|
||||
.abstract = true,
|
||||
}
|
||||
};
|
||||
|
||||
DEFINE_TYPES(rx62n_types)
|
|
@ -30,6 +30,7 @@
|
|||
#include "sh7750_regs.h"
|
||||
#include "sh7750_regnames.h"
|
||||
#include "hw/sh4/sh_intc.h"
|
||||
#include "hw/timer/tmu012.h"
|
||||
#include "cpu.h"
|
||||
#include "exec/exec-all.h"
|
||||
|
||||
|
|
|
@ -35,3 +35,9 @@ config CMSDK_APB_TIMER
|
|||
config CMSDK_APB_DUALTIMER
|
||||
bool
|
||||
select PTIMER
|
||||
|
||||
config RENESAS_TMR
|
||||
bool
|
||||
|
||||
config RENESAS_CMT
|
||||
bool
|
||||
|
|
|
@ -23,6 +23,8 @@ common-obj-$(CONFIG_OMAP) += omap_gptimer.o
|
|||
common-obj-$(CONFIG_OMAP) += omap_synctimer.o
|
||||
common-obj-$(CONFIG_PXA2XX) += pxa2xx_timer.o
|
||||
common-obj-$(CONFIG_SH4) += sh_timer.o
|
||||
common-obj-$(CONFIG_RENESAS_TMR) += renesas_tmr.o
|
||||
common-obj-$(CONFIG_RENESAS_CMT) += renesas_cmt.o
|
||||
common-obj-$(CONFIG_DIGIC) += digic-timer.o
|
||||
common-obj-$(CONFIG_MIPS_CPS) += mips_gictimer.o
|
||||
|
||||
|
|
|
@ -0,0 +1,283 @@
|
|||
/*
|
||||
* Renesas 16bit Compare-match timer
|
||||
*
|
||||
* Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
|
||||
* (Rev.1.40 R01UH0033EJ0140)
|
||||
*
|
||||
* Copyright (c) 2019 Yoshinori Sato
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/registerfields.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/timer/renesas_cmt.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
||||
/*
|
||||
* +0 CMSTR - common control
|
||||
* +2 CMCR - ch0
|
||||
* +4 CMCNT - ch0
|
||||
* +6 CMCOR - ch0
|
||||
* +8 CMCR - ch1
|
||||
* +10 CMCNT - ch1
|
||||
* +12 CMCOR - ch1
|
||||
* If we think that the address of CH 0 has an offset of +2,
|
||||
* we can treat it with the same address as CH 1, so define it like that.
|
||||
*/
|
||||
REG16(CMSTR, 0)
|
||||
FIELD(CMSTR, STR0, 0, 1)
|
||||
FIELD(CMSTR, STR1, 1, 1)
|
||||
FIELD(CMSTR, STR, 0, 2)
|
||||
/* This addeess is channel offset */
|
||||
REG16(CMCR, 0)
|
||||
FIELD(CMCR, CKS, 0, 2)
|
||||
FIELD(CMCR, CMIE, 6, 1)
|
||||
REG16(CMCNT, 2)
|
||||
REG16(CMCOR, 4)
|
||||
|
||||
static void update_events(RCMTState *cmt, int ch)
|
||||
{
|
||||
int64_t next_time;
|
||||
|
||||
if ((cmt->cmstr & (1 << ch)) == 0) {
|
||||
/* count disable, so not happened next event. */
|
||||
return ;
|
||||
}
|
||||
next_time = cmt->cmcor[ch] - cmt->cmcnt[ch];
|
||||
next_time *= NANOSECONDS_PER_SECOND;
|
||||
next_time /= cmt->input_freq;
|
||||
/*
|
||||
* CKS -> div rate
|
||||
* 0 -> 8 (1 << 3)
|
||||
* 1 -> 32 (1 << 5)
|
||||
* 2 -> 128 (1 << 7)
|
||||
* 3 -> 512 (1 << 9)
|
||||
*/
|
||||
next_time *= 1 << (3 + FIELD_EX16(cmt->cmcr[ch], CMCR, CKS) * 2);
|
||||
next_time += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
timer_mod(&cmt->timer[ch], next_time);
|
||||
}
|
||||
|
||||
static int64_t read_cmcnt(RCMTState *cmt, int ch)
|
||||
{
|
||||
int64_t delta, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
|
||||
if (cmt->cmstr & (1 << ch)) {
|
||||
delta = (now - cmt->tick[ch]);
|
||||
delta /= NANOSECONDS_PER_SECOND;
|
||||
delta /= cmt->input_freq;
|
||||
delta /= 1 << (3 + FIELD_EX16(cmt->cmcr[ch], CMCR, CKS) * 2);
|
||||
cmt->tick[ch] = now;
|
||||
return cmt->cmcnt[ch] + delta;
|
||||
} else {
|
||||
return cmt->cmcnt[ch];
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t cmt_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
RCMTState *cmt = opaque;
|
||||
int ch = offset / 0x08;
|
||||
uint64_t ret;
|
||||
|
||||
if (offset == A_CMSTR) {
|
||||
ret = 0;
|
||||
ret = FIELD_DP16(ret, CMSTR, STR,
|
||||
FIELD_EX16(cmt->cmstr, CMSTR, STR));
|
||||
return ret;
|
||||
} else {
|
||||
offset &= 0x07;
|
||||
if (ch == 0) {
|
||||
offset -= 0x02;
|
||||
}
|
||||
switch (offset) {
|
||||
case A_CMCR:
|
||||
ret = 0;
|
||||
ret = FIELD_DP16(ret, CMCR, CKS,
|
||||
FIELD_EX16(cmt->cmstr, CMCR, CKS));
|
||||
ret = FIELD_DP16(ret, CMCR, CMIE,
|
||||
FIELD_EX16(cmt->cmstr, CMCR, CMIE));
|
||||
return ret;
|
||||
case A_CMCNT:
|
||||
return read_cmcnt(cmt, ch);
|
||||
case A_CMCOR:
|
||||
return cmt->cmcor[ch];
|
||||
}
|
||||
}
|
||||
qemu_log_mask(LOG_UNIMP, "renesas_cmt: Register 0x%" HWADDR_PRIX " "
|
||||
"not implemented\n",
|
||||
offset);
|
||||
return UINT64_MAX;
|
||||
}
|
||||
|
||||
static void start_stop(RCMTState *cmt, int ch, int st)
|
||||
{
|
||||
if (st) {
|
||||
update_events(cmt, ch);
|
||||
} else {
|
||||
timer_del(&cmt->timer[ch]);
|
||||
}
|
||||
}
|
||||
|
||||
static void cmt_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
|
||||
{
|
||||
RCMTState *cmt = opaque;
|
||||
int ch = offset / 0x08;
|
||||
|
||||
if (offset == A_CMSTR) {
|
||||
cmt->cmstr = FIELD_EX16(val, CMSTR, STR);
|
||||
start_stop(cmt, 0, FIELD_EX16(cmt->cmstr, CMSTR, STR0));
|
||||
start_stop(cmt, 1, FIELD_EX16(cmt->cmstr, CMSTR, STR1));
|
||||
} else {
|
||||
offset &= 0x07;
|
||||
if (ch == 0) {
|
||||
offset -= 0x02;
|
||||
}
|
||||
switch (offset) {
|
||||
case A_CMCR:
|
||||
cmt->cmcr[ch] = FIELD_DP16(cmt->cmcr[ch], CMCR, CKS,
|
||||
FIELD_EX16(val, CMCR, CKS));
|
||||
cmt->cmcr[ch] = FIELD_DP16(cmt->cmcr[ch], CMCR, CMIE,
|
||||
FIELD_EX16(val, CMCR, CMIE));
|
||||
break;
|
||||
case 2:
|
||||
cmt->cmcnt[ch] = val;
|
||||
break;
|
||||
case 4:
|
||||
cmt->cmcor[ch] = val;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "renesas_cmt: Register 0x%" HWADDR_PRIX " "
|
||||
"not implemented\n",
|
||||
offset);
|
||||
return;
|
||||
}
|
||||
if (FIELD_EX16(cmt->cmstr, CMSTR, STR) & (1 << ch)) {
|
||||
update_events(cmt, ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps cmt_ops = {
|
||||
.write = cmt_write,
|
||||
.read = cmt_read,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.impl = {
|
||||
.min_access_size = 2,
|
||||
.max_access_size = 2,
|
||||
},
|
||||
.valid = {
|
||||
.min_access_size = 2,
|
||||
.max_access_size = 2,
|
||||
},
|
||||
};
|
||||
|
||||
static void timer_events(RCMTState *cmt, int ch)
|
||||
{
|
||||
cmt->cmcnt[ch] = 0;
|
||||
cmt->tick[ch] = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
update_events(cmt, ch);
|
||||
if (FIELD_EX16(cmt->cmcr[ch], CMCR, CMIE)) {
|
||||
qemu_irq_pulse(cmt->cmi[ch]);
|
||||
}
|
||||
}
|
||||
|
||||
static void timer_event0(void *opaque)
|
||||
{
|
||||
RCMTState *cmt = opaque;
|
||||
|
||||
timer_events(cmt, 0);
|
||||
}
|
||||
|
||||
static void timer_event1(void *opaque)
|
||||
{
|
||||
RCMTState *cmt = opaque;
|
||||
|
||||
timer_events(cmt, 1);
|
||||
}
|
||||
|
||||
static void rcmt_reset(DeviceState *dev)
|
||||
{
|
||||
RCMTState *cmt = RCMT(dev);
|
||||
cmt->cmstr = 0;
|
||||
cmt->cmcr[0] = cmt->cmcr[1] = 0;
|
||||
cmt->cmcnt[0] = cmt->cmcnt[1] = 0;
|
||||
cmt->cmcor[0] = cmt->cmcor[1] = 0xffff;
|
||||
}
|
||||
|
||||
static void rcmt_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *d = SYS_BUS_DEVICE(obj);
|
||||
RCMTState *cmt = RCMT(obj);
|
||||
int i;
|
||||
|
||||
memory_region_init_io(&cmt->memory, OBJECT(cmt), &cmt_ops,
|
||||
cmt, "renesas-cmt", 0x10);
|
||||
sysbus_init_mmio(d, &cmt->memory);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cmt->cmi); i++) {
|
||||
sysbus_init_irq(d, &cmt->cmi[i]);
|
||||
}
|
||||
timer_init_ns(&cmt->timer[0], QEMU_CLOCK_VIRTUAL, timer_event0, cmt);
|
||||
timer_init_ns(&cmt->timer[1], QEMU_CLOCK_VIRTUAL, timer_event1, cmt);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_rcmt = {
|
||||
.name = "rx-cmt",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT16(cmstr, RCMTState),
|
||||
VMSTATE_UINT16_ARRAY(cmcr, RCMTState, CMT_CH),
|
||||
VMSTATE_UINT16_ARRAY(cmcnt, RCMTState, CMT_CH),
|
||||
VMSTATE_UINT16_ARRAY(cmcor, RCMTState, CMT_CH),
|
||||
VMSTATE_INT64_ARRAY(tick, RCMTState, CMT_CH),
|
||||
VMSTATE_TIMER_ARRAY(timer, RCMTState, CMT_CH),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static Property rcmt_properties[] = {
|
||||
DEFINE_PROP_UINT64("input-freq", RCMTState, input_freq, 0),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void rcmt_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->vmsd = &vmstate_rcmt;
|
||||
dc->reset = rcmt_reset;
|
||||
device_class_set_props(dc, rcmt_properties);
|
||||
}
|
||||
|
||||
static const TypeInfo rcmt_info = {
|
||||
.name = TYPE_RENESAS_CMT,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(RCMTState),
|
||||
.instance_init = rcmt_init,
|
||||
.class_init = rcmt_class_init,
|
||||
};
|
||||
|
||||
static void rcmt_register_types(void)
|
||||
{
|
||||
type_register_static(&rcmt_info);
|
||||
}
|
||||
|
||||
type_init(rcmt_register_types)
|
|
@ -0,0 +1,477 @@
|
|||
/*
|
||||
* Renesas 8bit timer
|
||||
*
|
||||
* Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
|
||||
* (Rev.1.40 R01UH0033EJ0140)
|
||||
*
|
||||
* Copyright (c) 2019 Yoshinori Sato
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/log.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/registerfields.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/timer/renesas_tmr.h"
|
||||
#include "migration/vmstate.h"
|
||||
|
||||
REG8(TCR, 0)
|
||||
FIELD(TCR, CCLR, 3, 2)
|
||||
FIELD(TCR, OVIE, 5, 1)
|
||||
FIELD(TCR, CMIEA, 6, 1)
|
||||
FIELD(TCR, CMIEB, 7, 1)
|
||||
REG8(TCSR, 2)
|
||||
FIELD(TCSR, OSA, 0, 2)
|
||||
FIELD(TCSR, OSB, 2, 2)
|
||||
FIELD(TCSR, ADTE, 4, 2)
|
||||
REG8(TCORA, 4)
|
||||
REG8(TCORB, 6)
|
||||
REG8(TCNT, 8)
|
||||
REG8(TCCR, 10)
|
||||
FIELD(TCCR, CKS, 0, 3)
|
||||
FIELD(TCCR, CSS, 3, 2)
|
||||
FIELD(TCCR, TMRIS, 7, 1)
|
||||
|
||||
#define INTERNAL 0x01
|
||||
#define CASCADING 0x03
|
||||
#define CCLR_A 0x01
|
||||
#define CCLR_B 0x02
|
||||
|
||||
static const int clkdiv[] = {0, 1, 2, 8, 32, 64, 1024, 8192};
|
||||
|
||||
static uint8_t concat_reg(uint8_t *reg)
|
||||
{
|
||||
return (reg[0] << 8) | reg[1];
|
||||
}
|
||||
|
||||
static void update_events(RTMRState *tmr, int ch)
|
||||
{
|
||||
uint16_t diff[TMR_NR_EVENTS], min;
|
||||
int64_t next_time;
|
||||
int i, event;
|
||||
|
||||
if (tmr->tccr[ch] == 0) {
|
||||
return ;
|
||||
}
|
||||
if (FIELD_EX8(tmr->tccr[ch], TCCR, CSS) == 0) {
|
||||
/* external clock mode */
|
||||
/* event not happened */
|
||||
return ;
|
||||
}
|
||||
if (FIELD_EX8(tmr->tccr[0], TCCR, CSS) == CASCADING) {
|
||||
/* cascading mode */
|
||||
if (ch == 1) {
|
||||
tmr->next[ch] = none;
|
||||
return ;
|
||||
}
|
||||
diff[cmia] = concat_reg(tmr->tcora) - concat_reg(tmr->tcnt);
|
||||
diff[cmib] = concat_reg(tmr->tcorb) - concat_reg(tmr->tcnt);
|
||||
diff[ovi] = 0x10000 - concat_reg(tmr->tcnt);
|
||||
} else {
|
||||
/* separate mode */
|
||||
diff[cmia] = tmr->tcora[ch] - tmr->tcnt[ch];
|
||||
diff[cmib] = tmr->tcorb[ch] - tmr->tcnt[ch];
|
||||
diff[ovi] = 0x100 - tmr->tcnt[ch];
|
||||
}
|
||||
/* Search for the most recently occurring event. */
|
||||
for (event = 0, min = diff[0], i = 1; i < none; i++) {
|
||||
if (min > diff[i]) {
|
||||
event = i;
|
||||
min = diff[i];
|
||||
}
|
||||
}
|
||||
tmr->next[ch] = event;
|
||||
next_time = diff[event];
|
||||
next_time *= clkdiv[FIELD_EX8(tmr->tccr[ch], TCCR, CKS)];
|
||||
next_time *= NANOSECONDS_PER_SECOND;
|
||||
next_time /= tmr->input_freq;
|
||||
next_time += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
timer_mod(&tmr->timer[ch], next_time);
|
||||
}
|
||||
|
||||
static int elapsed_time(RTMRState *tmr, int ch, int64_t delta)
|
||||
{
|
||||
int divrate = clkdiv[FIELD_EX8(tmr->tccr[ch], TCCR, CKS)];
|
||||
int et;
|
||||
|
||||
tmr->div_round[ch] += delta;
|
||||
if (divrate > 0) {
|
||||
et = tmr->div_round[ch] / divrate;
|
||||
tmr->div_round[ch] %= divrate;
|
||||
} else {
|
||||
/* disble clock. so no update */
|
||||
et = 0;
|
||||
}
|
||||
return et;
|
||||
}
|
||||
|
||||
static uint16_t read_tcnt(RTMRState *tmr, unsigned size, int ch)
|
||||
{
|
||||
int64_t delta, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
int elapsed, ovf = 0;
|
||||
uint16_t tcnt[2];
|
||||
uint32_t ret;
|
||||
|
||||
delta = (now - tmr->tick) * NANOSECONDS_PER_SECOND / tmr->input_freq;
|
||||
if (delta > 0) {
|
||||
tmr->tick = now;
|
||||
|
||||
if (FIELD_EX8(tmr->tccr[1], TCCR, CSS) == INTERNAL) {
|
||||
/* timer1 count update */
|
||||
elapsed = elapsed_time(tmr, 1, delta);
|
||||
if (elapsed >= 0x100) {
|
||||
ovf = elapsed >> 8;
|
||||
}
|
||||
tcnt[1] = tmr->tcnt[1] + (elapsed & 0xff);
|
||||
}
|
||||
switch (FIELD_EX8(tmr->tccr[0], TCCR, CSS)) {
|
||||
case INTERNAL:
|
||||
elapsed = elapsed_time(tmr, 0, delta);
|
||||
tcnt[0] = tmr->tcnt[0] + elapsed;
|
||||
break;
|
||||
case CASCADING:
|
||||
if (ovf > 0) {
|
||||
tcnt[0] = tmr->tcnt[0] + ovf;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
tcnt[0] = tmr->tcnt[0];
|
||||
tcnt[1] = tmr->tcnt[1];
|
||||
}
|
||||
if (size == 1) {
|
||||
return tcnt[ch];
|
||||
} else {
|
||||
ret = 0;
|
||||
ret = deposit32(ret, 0, 8, tcnt[1]);
|
||||
ret = deposit32(ret, 8, 8, tcnt[0]);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t read_tccr(uint8_t r)
|
||||
{
|
||||
uint8_t tccr = 0;
|
||||
tccr = FIELD_DP8(tccr, TCCR, TMRIS,
|
||||
FIELD_EX8(r, TCCR, TMRIS));
|
||||
tccr = FIELD_DP8(tccr, TCCR, CSS,
|
||||
FIELD_EX8(r, TCCR, CSS));
|
||||
tccr = FIELD_DP8(tccr, TCCR, CKS,
|
||||
FIELD_EX8(r, TCCR, CKS));
|
||||
return tccr;
|
||||
}
|
||||
|
||||
static uint64_t tmr_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
RTMRState *tmr = opaque;
|
||||
int ch = addr & 1;
|
||||
uint64_t ret;
|
||||
|
||||
if (size == 2 && (ch != 0 || addr == A_TCR || addr == A_TCSR)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "renesas_tmr: Invalid read size 0x%"
|
||||
HWADDR_PRIX "\n",
|
||||
addr);
|
||||
return UINT64_MAX;
|
||||
}
|
||||
switch (addr & 0x0e) {
|
||||
case A_TCR:
|
||||
ret = 0;
|
||||
ret = FIELD_DP8(ret, TCR, CCLR,
|
||||
FIELD_EX8(tmr->tcr[ch], TCR, CCLR));
|
||||
ret = FIELD_DP8(ret, TCR, OVIE,
|
||||
FIELD_EX8(tmr->tcr[ch], TCR, OVIE));
|
||||
ret = FIELD_DP8(ret, TCR, CMIEA,
|
||||
FIELD_EX8(tmr->tcr[ch], TCR, CMIEA));
|
||||
ret = FIELD_DP8(ret, TCR, CMIEB,
|
||||
FIELD_EX8(tmr->tcr[ch], TCR, CMIEB));
|
||||
return ret;
|
||||
case A_TCSR:
|
||||
ret = 0;
|
||||
ret = FIELD_DP8(ret, TCSR, OSA,
|
||||
FIELD_EX8(tmr->tcsr[ch], TCSR, OSA));
|
||||
ret = FIELD_DP8(ret, TCSR, OSB,
|
||||
FIELD_EX8(tmr->tcsr[ch], TCSR, OSB));
|
||||
switch (ch) {
|
||||
case 0:
|
||||
ret = FIELD_DP8(ret, TCSR, ADTE,
|
||||
FIELD_EX8(tmr->tcsr[ch], TCSR, ADTE));
|
||||
break;
|
||||
case 1: /* CH1 ADTE unimplement always 1 */
|
||||
ret = FIELD_DP8(ret, TCSR, ADTE, 1);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
case A_TCORA:
|
||||
if (size == 1) {
|
||||
return tmr->tcora[ch];
|
||||
} else if (ch == 0) {
|
||||
return concat_reg(tmr->tcora);
|
||||
}
|
||||
case A_TCORB:
|
||||
if (size == 1) {
|
||||
return tmr->tcorb[ch];
|
||||
} else {
|
||||
return concat_reg(tmr->tcorb);
|
||||
}
|
||||
case A_TCNT:
|
||||
return read_tcnt(tmr, size, ch);
|
||||
case A_TCCR:
|
||||
if (size == 1) {
|
||||
return read_tccr(tmr->tccr[ch]);
|
||||
} else {
|
||||
return read_tccr(tmr->tccr[0]) << 8 | read_tccr(tmr->tccr[1]);
|
||||
}
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "renesas_tmr: Register 0x%" HWADDR_PRIX
|
||||
" not implemented\n",
|
||||
addr);
|
||||
break;
|
||||
}
|
||||
return UINT64_MAX;
|
||||
}
|
||||
|
||||
static void tmr_write_count(RTMRState *tmr, int ch, unsigned size,
|
||||
uint8_t *reg, uint64_t val)
|
||||
{
|
||||
if (size == 1) {
|
||||
reg[ch] = val;
|
||||
update_events(tmr, ch);
|
||||
} else {
|
||||
reg[0] = extract32(val, 8, 8);
|
||||
reg[1] = extract32(val, 0, 8);
|
||||
update_events(tmr, 0);
|
||||
update_events(tmr, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void tmr_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
RTMRState *tmr = opaque;
|
||||
int ch = addr & 1;
|
||||
|
||||
if (size == 2 && (ch != 0 || addr == A_TCR || addr == A_TCSR)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"renesas_tmr: Invalid write size 0x%" HWADDR_PRIX "\n",
|
||||
addr);
|
||||
return;
|
||||
}
|
||||
switch (addr & 0x0e) {
|
||||
case A_TCR:
|
||||
tmr->tcr[ch] = val;
|
||||
break;
|
||||
case A_TCSR:
|
||||
tmr->tcsr[ch] = val;
|
||||
break;
|
||||
case A_TCORA:
|
||||
tmr_write_count(tmr, ch, size, tmr->tcora, val);
|
||||
break;
|
||||
case A_TCORB:
|
||||
tmr_write_count(tmr, ch, size, tmr->tcorb, val);
|
||||
break;
|
||||
case A_TCNT:
|
||||
tmr_write_count(tmr, ch, size, tmr->tcnt, val);
|
||||
break;
|
||||
case A_TCCR:
|
||||
tmr_write_count(tmr, ch, size, tmr->tccr, val);
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "renesas_tmr: Register 0x%" HWADDR_PRIX
|
||||
" not implemented\n",
|
||||
addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps tmr_ops = {
|
||||
.write = tmr_write,
|
||||
.read = tmr_read,
|
||||
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 2,
|
||||
},
|
||||
.valid = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 2,
|
||||
},
|
||||
};
|
||||
|
||||
static void timer_events(RTMRState *tmr, int ch);
|
||||
|
||||
static uint16_t issue_event(RTMRState *tmr, int ch, int sz,
|
||||
uint16_t tcnt, uint16_t tcora, uint16_t tcorb)
|
||||
{
|
||||
uint16_t ret = tcnt;
|
||||
|
||||
switch (tmr->next[ch]) {
|
||||
case none:
|
||||
break;
|
||||
case cmia:
|
||||
if (tcnt >= tcora) {
|
||||
if (FIELD_EX8(tmr->tcr[ch], TCR, CCLR) == CCLR_A) {
|
||||
ret = tcnt - tcora;
|
||||
}
|
||||
if (FIELD_EX8(tmr->tcr[ch], TCR, CMIEA)) {
|
||||
qemu_irq_pulse(tmr->cmia[ch]);
|
||||
}
|
||||
if (sz == 8 && ch == 0 &&
|
||||
FIELD_EX8(tmr->tccr[1], TCCR, CSS) == CASCADING) {
|
||||
tmr->tcnt[1]++;
|
||||
timer_events(tmr, 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case cmib:
|
||||
if (tcnt >= tcorb) {
|
||||
if (FIELD_EX8(tmr->tcr[ch], TCR, CCLR) == CCLR_B) {
|
||||
ret = tcnt - tcorb;
|
||||
}
|
||||
if (FIELD_EX8(tmr->tcr[ch], TCR, CMIEB)) {
|
||||
qemu_irq_pulse(tmr->cmib[ch]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ovi:
|
||||
if ((tcnt >= (1 << sz)) && FIELD_EX8(tmr->tcr[ch], TCR, OVIE)) {
|
||||
qemu_irq_pulse(tmr->ovi[ch]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void timer_events(RTMRState *tmr, int ch)
|
||||
{
|
||||
uint16_t tcnt;
|
||||
|
||||
tmr->tcnt[ch] = read_tcnt(tmr, 1, ch);
|
||||
if (FIELD_EX8(tmr->tccr[0], TCCR, CSS) != CASCADING) {
|
||||
tmr->tcnt[ch] = issue_event(tmr, ch, 8,
|
||||
tmr->tcnt[ch],
|
||||
tmr->tcora[ch],
|
||||
tmr->tcorb[ch]) & 0xff;
|
||||
} else {
|
||||
if (ch == 1) {
|
||||
return ;
|
||||
}
|
||||
tcnt = issue_event(tmr, ch, 16,
|
||||
concat_reg(tmr->tcnt),
|
||||
concat_reg(tmr->tcora),
|
||||
concat_reg(tmr->tcorb));
|
||||
tmr->tcnt[0] = (tcnt >> 8) & 0xff;
|
||||
tmr->tcnt[1] = tcnt & 0xff;
|
||||
}
|
||||
update_events(tmr, ch);
|
||||
}
|
||||
|
||||
static void timer_event0(void *opaque)
|
||||
{
|
||||
RTMRState *tmr = opaque;
|
||||
|
||||
timer_events(tmr, 0);
|
||||
}
|
||||
|
||||
static void timer_event1(void *opaque)
|
||||
{
|
||||
RTMRState *tmr = opaque;
|
||||
|
||||
timer_events(tmr, 1);
|
||||
}
|
||||
|
||||
static void rtmr_reset(DeviceState *dev)
|
||||
{
|
||||
RTMRState *tmr = RTMR(dev);
|
||||
tmr->tcr[0] = tmr->tcr[1] = 0x00;
|
||||
tmr->tcsr[0] = 0x00;
|
||||
tmr->tcsr[1] = 0x10;
|
||||
tmr->tcnt[0] = tmr->tcnt[1] = 0x00;
|
||||
tmr->tcora[0] = tmr->tcora[1] = 0xff;
|
||||
tmr->tcorb[0] = tmr->tcorb[1] = 0xff;
|
||||
tmr->tccr[0] = tmr->tccr[1] = 0x00;
|
||||
tmr->next[0] = tmr->next[1] = none;
|
||||
tmr->tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
}
|
||||
|
||||
static void rtmr_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *d = SYS_BUS_DEVICE(obj);
|
||||
RTMRState *tmr = RTMR(obj);
|
||||
int i;
|
||||
|
||||
memory_region_init_io(&tmr->memory, OBJECT(tmr), &tmr_ops,
|
||||
tmr, "renesas-tmr", 0x10);
|
||||
sysbus_init_mmio(d, &tmr->memory);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tmr->ovi); i++) {
|
||||
sysbus_init_irq(d, &tmr->cmia[i]);
|
||||
sysbus_init_irq(d, &tmr->cmib[i]);
|
||||
sysbus_init_irq(d, &tmr->ovi[i]);
|
||||
}
|
||||
timer_init_ns(&tmr->timer[0], QEMU_CLOCK_VIRTUAL, timer_event0, tmr);
|
||||
timer_init_ns(&tmr->timer[1], QEMU_CLOCK_VIRTUAL, timer_event1, tmr);
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_rtmr = {
|
||||
.name = "rx-tmr",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_INT64(tick, RTMRState),
|
||||
VMSTATE_UINT8_ARRAY(tcnt, RTMRState, TMR_CH),
|
||||
VMSTATE_UINT8_ARRAY(tcora, RTMRState, TMR_CH),
|
||||
VMSTATE_UINT8_ARRAY(tcorb, RTMRState, TMR_CH),
|
||||
VMSTATE_UINT8_ARRAY(tcr, RTMRState, TMR_CH),
|
||||
VMSTATE_UINT8_ARRAY(tccr, RTMRState, TMR_CH),
|
||||
VMSTATE_UINT8_ARRAY(tcor, RTMRState, TMR_CH),
|
||||
VMSTATE_UINT8_ARRAY(tcsr, RTMRState, TMR_CH),
|
||||
VMSTATE_INT64_ARRAY(div_round, RTMRState, TMR_CH),
|
||||
VMSTATE_UINT8_ARRAY(next, RTMRState, TMR_CH),
|
||||
VMSTATE_TIMER_ARRAY(timer, RTMRState, TMR_CH),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static Property rtmr_properties[] = {
|
||||
DEFINE_PROP_UINT64("input-freq", RTMRState, input_freq, 0),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void rtmr_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->vmsd = &vmstate_rtmr;
|
||||
dc->reset = rtmr_reset;
|
||||
device_class_set_props(dc, rtmr_properties);
|
||||
}
|
||||
|
||||
static const TypeInfo rtmr_info = {
|
||||
.name = TYPE_RENESAS_TMR,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(RTMRState),
|
||||
.instance_init = rtmr_init,
|
||||
.class_init = rtmr_class_init,
|
||||
};
|
||||
|
||||
static void rtmr_register_types(void)
|
||||
{
|
||||
type_register_static(&rtmr_info);
|
||||
}
|
||||
|
||||
type_init(rtmr_register_types)
|
|
@ -9,10 +9,11 @@
|
|||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "exec/memory.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/sh4/sh.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/timer/tmu012.h"
|
||||
#include "hw/ptimer.h"
|
||||
|
||||
//#define DEBUG_TIMER
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Renesas Serial Communication Interface
|
||||
*
|
||||
* Copyright (c) 2018 Yoshinori Sato
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef HW_CHAR_RENESAS_SCI_H
|
||||
#define HW_CHAR_RENESAS_SCI_H
|
||||
|
||||
#include "chardev/char-fe.h"
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
#define TYPE_RENESAS_SCI "renesas-sci"
|
||||
#define RSCI(obj) OBJECT_CHECK(RSCIState, (obj), TYPE_RENESAS_SCI)
|
||||
|
||||
enum {
|
||||
ERI = 0,
|
||||
RXI = 1,
|
||||
TXI = 2,
|
||||
TEI = 3,
|
||||
SCI_NR_IRQ = 4
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
/*< public >*/
|
||||
|
||||
MemoryRegion memory;
|
||||
QEMUTimer timer;
|
||||
CharBackend chr;
|
||||
qemu_irq irq[SCI_NR_IRQ];
|
||||
|
||||
uint8_t smr;
|
||||
uint8_t brr;
|
||||
uint8_t scr;
|
||||
uint8_t tdr;
|
||||
uint8_t ssr;
|
||||
uint8_t rdr;
|
||||
uint8_t scmr;
|
||||
uint8_t semr;
|
||||
|
||||
uint8_t read_ssr;
|
||||
int64_t trtime;
|
||||
int64_t rx_next;
|
||||
uint64_t input_freq;
|
||||
} RSCIState;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* RX Interrupt Control Unit
|
||||
*
|
||||
* Copyright (c) 2019 Yoshinori Sato
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef HW_INTC_RX_ICU_H
|
||||
#define HW_INTC_RX_ICU_H
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
enum TRG_MODE {
|
||||
TRG_LEVEL = 0,
|
||||
TRG_NEDGE = 1, /* Falling */
|
||||
TRG_PEDGE = 2, /* Raising */
|
||||
TRG_BEDGE = 3, /* Both */
|
||||
};
|
||||
|
||||
struct IRQSource {
|
||||
enum TRG_MODE sense;
|
||||
int level;
|
||||
};
|
||||
|
||||
enum {
|
||||
/* Software interrupt request */
|
||||
SWI = 27,
|
||||
NR_IRQS = 256
|
||||
};
|
||||
|
||||
struct RXICUState {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
/*< public >*/
|
||||
|
||||
MemoryRegion memory;
|
||||
struct IRQSource src[NR_IRQS];
|
||||
uint32_t nr_irqs;
|
||||
uint8_t *map;
|
||||
uint32_t nr_sense;
|
||||
uint8_t *init_sense;
|
||||
|
||||
uint8_t ir[NR_IRQS];
|
||||
uint8_t dtcer[NR_IRQS];
|
||||
uint8_t ier[NR_IRQS / 8];
|
||||
uint8_t ipr[142];
|
||||
uint8_t dmasr[4];
|
||||
uint16_t fir;
|
||||
uint8_t nmisr;
|
||||
uint8_t nmier;
|
||||
uint8_t nmiclr;
|
||||
uint8_t nmicr;
|
||||
int16_t req_irq;
|
||||
qemu_irq _irq;
|
||||
qemu_irq _fir;
|
||||
qemu_irq _swi;
|
||||
};
|
||||
typedef struct RXICUState RXICUState;
|
||||
|
||||
#define TYPE_RX_ICU "rx-icu"
|
||||
#define RX_ICU(obj) OBJECT_CHECK(RXICUState, (obj), TYPE_RX_ICU)
|
||||
|
||||
#endif /* RX_ICU_H */
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* RX62N MCU Object
|
||||
*
|
||||
* Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
|
||||
* (Rev.1.40 R01UH0033EJ0140)
|
||||
*
|
||||
* Copyright (c) 2019 Yoshinori Sato
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef HW_RX_RX62N_MCU_H
|
||||
#define HW_RX_RX62N_MCU_H
|
||||
|
||||
#include "target/rx/cpu.h"
|
||||
#include "hw/intc/rx_icu.h"
|
||||
#include "hw/timer/renesas_tmr.h"
|
||||
#include "hw/timer/renesas_cmt.h"
|
||||
#include "hw/char/renesas_sci.h"
|
||||
#include "qemu/units.h"
|
||||
|
||||
#define TYPE_RX62N_MCU "rx62n-mcu"
|
||||
#define RX62N_MCU(obj) OBJECT_CHECK(RX62NState, (obj), TYPE_RX62N_MCU)
|
||||
|
||||
#define TYPE_R5F562N7_MCU "r5f562n7-mcu"
|
||||
#define TYPE_R5F562N8_MCU "r5f562n8-mcu"
|
||||
|
||||
#define EXT_CS_BASE 0x01000000
|
||||
#define VECTOR_TABLE_BASE 0xffffff80
|
||||
#define RX62N_CFLASH_BASE 0xfff80000
|
||||
|
||||
#define RX62N_NR_TMR 2
|
||||
#define RX62N_NR_CMT 2
|
||||
#define RX62N_NR_SCI 6
|
||||
|
||||
typedef struct RX62NState {
|
||||
/*< private >*/
|
||||
DeviceState parent_obj;
|
||||
/*< public >*/
|
||||
|
||||
RXCPU cpu;
|
||||
RXICUState icu;
|
||||
RTMRState tmr[RX62N_NR_TMR];
|
||||
RCMTState cmt[RX62N_NR_CMT];
|
||||
RSCIState sci[RX62N_NR_SCI];
|
||||
|
||||
MemoryRegion *sysmem;
|
||||
bool kernel;
|
||||
|
||||
MemoryRegion iram;
|
||||
MemoryRegion iomem1;
|
||||
MemoryRegion d_flash;
|
||||
MemoryRegion iomem2;
|
||||
MemoryRegion iomem3;
|
||||
MemoryRegion c_flash;
|
||||
qemu_irq irq[NR_IRQS];
|
||||
|
||||
/* Input Clock (XTAL) frequency */
|
||||
uint32_t xtal_freq_hz;
|
||||
/* Peripheral Module Clock frequency */
|
||||
uint32_t pclk_freq_hz;
|
||||
} RX62NState;
|
||||
|
||||
#endif
|
|
@ -10,9 +10,8 @@
|
|||
|
||||
/* sh7750.c */
|
||||
struct SH7750State;
|
||||
struct MemoryRegion;
|
||||
|
||||
struct SH7750State *sh7750_init(SuperHCPU *cpu, struct MemoryRegion *sysmem);
|
||||
struct SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem);
|
||||
|
||||
typedef struct {
|
||||
/* The callback will be triggered if any of the designated lines change */
|
||||
|
@ -28,15 +27,6 @@ typedef struct {
|
|||
|
||||
int sh7750_register_io_device(struct SH7750State *s,
|
||||
sh7750_io_device * device);
|
||||
/* sh_timer.c */
|
||||
#define TMU012_FEAT_TOCR (1 << 0)
|
||||
#define TMU012_FEAT_3CHAN (1 << 1)
|
||||
#define TMU012_FEAT_EXTCLK (1 << 2)
|
||||
void tmu012_init(struct MemoryRegion *sysmem, hwaddr base,
|
||||
int feat, uint32_t freq,
|
||||
qemu_irq ch0_irq, qemu_irq ch1_irq,
|
||||
qemu_irq ch2_irq0, qemu_irq ch2_irq1);
|
||||
|
||||
|
||||
/* sh_serial.c */
|
||||
#define SH_SERIAL_FEAT_SCIF (1 << 0)
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Renesas Compare-match timer Object
|
||||
*
|
||||
* Copyright (c) 2019 Yoshinori Sato
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef HW_TIMER_RENESAS_CMT_H
|
||||
#define HW_TIMER_RENESAS_CMT_H
|
||||
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
#define TYPE_RENESAS_CMT "renesas-cmt"
|
||||
#define RCMT(obj) OBJECT_CHECK(RCMTState, (obj), TYPE_RENESAS_CMT)
|
||||
|
||||
enum {
|
||||
CMT_CH = 2,
|
||||
CMT_NR_IRQ = 1 * CMT_CH
|
||||
};
|
||||
|
||||
typedef struct RCMTState {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
/*< public >*/
|
||||
|
||||
uint64_t input_freq;
|
||||
MemoryRegion memory;
|
||||
|
||||
uint16_t cmstr;
|
||||
uint16_t cmcr[CMT_CH];
|
||||
uint16_t cmcnt[CMT_CH];
|
||||
uint16_t cmcor[CMT_CH];
|
||||
int64_t tick[CMT_CH];
|
||||
qemu_irq cmi[CMT_CH];
|
||||
QEMUTimer timer[CMT_CH];
|
||||
} RCMTState;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Renesas 8bit timer Object
|
||||
*
|
||||
* Copyright (c) 2018 Yoshinori Sato
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef HW_TIMER_RENESAS_TMR_H
|
||||
#define HW_TIMER_RENESAS_TMR_H
|
||||
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
#define TYPE_RENESAS_TMR "renesas-tmr"
|
||||
#define RTMR(obj) OBJECT_CHECK(RTMRState, (obj), TYPE_RENESAS_TMR)
|
||||
|
||||
enum timer_event {
|
||||
cmia = 0,
|
||||
cmib = 1,
|
||||
ovi = 2,
|
||||
none = 3,
|
||||
TMR_NR_EVENTS = 4
|
||||
};
|
||||
|
||||
enum {
|
||||
TMR_CH = 2,
|
||||
TMR_NR_IRQ = 3 * TMR_CH
|
||||
};
|
||||
|
||||
typedef struct RTMRState {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
/*< public >*/
|
||||
|
||||
uint64_t input_freq;
|
||||
MemoryRegion memory;
|
||||
|
||||
int64_t tick;
|
||||
uint8_t tcnt[TMR_CH];
|
||||
uint8_t tcora[TMR_CH];
|
||||
uint8_t tcorb[TMR_CH];
|
||||
uint8_t tcr[TMR_CH];
|
||||
uint8_t tccr[TMR_CH];
|
||||
uint8_t tcor[TMR_CH];
|
||||
uint8_t tcsr[TMR_CH];
|
||||
int64_t div_round[TMR_CH];
|
||||
uint8_t next[TMR_CH];
|
||||
qemu_irq cmia[TMR_CH];
|
||||
qemu_irq cmib[TMR_CH];
|
||||
qemu_irq ovi[TMR_CH];
|
||||
QEMUTimer timer[TMR_CH];
|
||||
} RTMRState;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* SuperH Timer
|
||||
*
|
||||
* Copyright (c) 2007 Magnus Damm
|
||||
*
|
||||
* This code is licensed under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef HW_TIMER_TMU012_H
|
||||
#define HW_TIMER_TMU012_H
|
||||
|
||||
#include "exec/hwaddr.h"
|
||||
|
||||
#define TMU012_FEAT_TOCR (1 << 0)
|
||||
#define TMU012_FEAT_3CHAN (1 << 1)
|
||||
#define TMU012_FEAT_EXTCLK (1 << 2)
|
||||
|
||||
void tmu012_init(MemoryRegion *sysmem, hwaddr base,
|
||||
int feat, uint32_t freq,
|
||||
qemu_irq ch0_irq, qemu_irq ch1_irq,
|
||||
qemu_irq ch2_irq0, qemu_irq ch2_irq1);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,68 @@
|
|||
# Functional test that boots a Linux kernel and checks the console
|
||||
#
|
||||
# Copyright (c) 2018 Red Hat, Inc.
|
||||
#
|
||||
# Author:
|
||||
# Cleber Rosa <crosa@redhat.com>
|
||||
#
|
||||
# This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
# later. See the COPYING file in the top-level directory.
|
||||
|
||||
from avocado_qemu import Test
|
||||
from avocado_qemu import exec_command_and_wait_for_pattern
|
||||
from avocado_qemu import wait_for_console_pattern
|
||||
from avocado.utils import archive
|
||||
|
||||
|
||||
class RxGdbSimMachine(Test):
|
||||
|
||||
timeout = 30
|
||||
KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 '
|
||||
|
||||
def test_uboot(self):
|
||||
"""
|
||||
U-Boot and checks that the console is operational.
|
||||
|
||||
:avocado: tags=arch:rx
|
||||
:avocado: tags=machine:gdbsim-r5f562n8
|
||||
:avocado: tags=endian:little
|
||||
"""
|
||||
uboot_url = ('https://acc.dl.osdn.jp/users/23/23888/u-boot.bin.gz')
|
||||
uboot_hash = '9b78dbd43b40b2526848c0b1ce9de02c24f4dcdb'
|
||||
uboot_path = self.fetch_asset(uboot_url, asset_hash=uboot_hash)
|
||||
uboot_path = archive.uncompress(uboot_path, self.workdir)
|
||||
|
||||
self.vm.set_console()
|
||||
self.vm.add_args('-bios', uboot_path,
|
||||
'-no-reboot')
|
||||
self.vm.launch()
|
||||
uboot_version = 'U-Boot 2016.05-rc3-23705-ga1ef3c71cb-dirty'
|
||||
wait_for_console_pattern(self, uboot_version)
|
||||
gcc_version = 'rx-unknown-linux-gcc (GCC) 9.0.0 20181105 (experimental)'
|
||||
# FIXME limit baudrate on chardev, else we type too fast
|
||||
#exec_command_and_wait_for_pattern(self, 'version', gcc_version)
|
||||
|
||||
def test_linux_sash(self):
|
||||
"""
|
||||
Boots a Linux kernel and checks that the console is operational.
|
||||
|
||||
:avocado: tags=arch:rx
|
||||
:avocado: tags=machine:gdbsim-r5f562n7
|
||||
:avocado: tags=endian:little
|
||||
"""
|
||||
dtb_url = ('https://acc.dl.osdn.jp/users/23/23887/rx-qemu.dtb')
|
||||
dtb_hash = '7b4e4e2c71905da44e86ce47adee2210b026ac18'
|
||||
dtb_path = self.fetch_asset(dtb_url, asset_hash=dtb_hash)
|
||||
kernel_url = ('http://acc.dl.osdn.jp/users/23/23845/zImage')
|
||||
kernel_hash = '39a81067f8d72faad90866ddfefa19165d68fc99'
|
||||
kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
|
||||
|
||||
self.vm.set_console()
|
||||
kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'earlycon'
|
||||
self.vm.add_args('-kernel', kernel_path,
|
||||
'-dtb', dtb_path,
|
||||
'-no-reboot')
|
||||
self.vm.launch()
|
||||
wait_for_console_pattern(self, 'Sash command shell (version 1.1.1)',
|
||||
failure_message='Kernel panic - not syncing')
|
||||
exec_command_and_wait_for_pattern(self, 'printenv', 'TERM=linux')
|
Loading…
Reference in New Issue