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 commit c8c35e5f51,
 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 commit c8c35e5f51,
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:
Peter Maydell 2020-06-23 13:55:52 +01:00
commit d88d5a3806
29 changed files with 2520 additions and 13 deletions

View File

@ -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

View File

@ -1,2 +1,3 @@
# Default configuration for rx-softmmu
CONFIG_RX_GDBSIM=y

36
docs/system/target-rx.rst Normal file
View File

@ -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"

View File

@ -18,3 +18,4 @@ Contents:
target-m68k
target-xtensa
target-s390x
target-rx

View File

@ -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

View File

@ -46,3 +46,6 @@ config SCLPCONSOLE
config TERMINAL3270
bool
config RENESAS_SCI
bool

View File

@ -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

350
hw/char/renesas_sci.c Normal file
View File

@ -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)

View File

@ -61,3 +61,6 @@ config S390_FLIC_KVM
config OMPIC
bool
config RX_ICU
bool

View File

@ -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

397
hw/intc/rx_icu.c Normal file
View File

@ -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)

10
hw/rx/Kconfig Normal file
View File

@ -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

2
hw/rx/Makefile.objs Normal file
View File

@ -0,0 +1,2 @@
obj-$(CONFIG_RX62N_MCU) += rx62n.o
obj-$(CONFIG_RX_GDBSIM) += rx-gdbsim.o

198
hw/rx/rx-gdbsim.c Normal file
View File

@ -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)

323
hw/rx/rx62n.c Normal file
View File

@ -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)

View File

@ -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"

View File

@ -35,3 +35,9 @@ config CMSDK_APB_TIMER
config CMSDK_APB_DUALTIMER
bool
select PTIMER
config RENESAS_TMR
bool
config RENESAS_CMT
bool

View File

@ -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

283
hw/timer/renesas_cmt.c Normal file
View File

@ -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)

477
hw/timer/renesas_tmr.c Normal file
View File

@ -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)

View File

@ -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

View File

@ -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

76
include/hw/intc/rx_icu.h Normal file
View File

@ -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 */

76
include/hw/rx/rx62n.h Normal file
View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

23
include/hw/timer/tmu012.h Normal file
View File

@ -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

View File

@ -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')