Skip to contentSkip to conversation

Linux for the PlayStation 2

PATCH In review Fredrik Noring 7 participants 161 messages Sep 1, 2019 – Jan 29, 2022 Page 1 of 2

Subsystems (8)
lore
Share & tools
lore
#0 lore raw

cover Linux for the PlayStation 2

Hi Paul,

This is the initial patch submission for the PlayStation 2. The series implements support for USB devices and a frame buffer console, making Linux usable with unmodified hardware having a working boot loader installed on a memory card.

There are eight major parts:

1. The R5900 is the main processor that runs the kernel[1]. It implements the 64-bit MIPS III instruction set except LL, SC, LLD and SCD, with additional PREFETCH and conditional move instructions from MIPS IV as well as three-operand multiply and multiply-accumulate instructions. It has a set of R5900 specific 128-bit multimedia instructions (MMIs).

The FPU implements single-precision floating-point operations but it is not entirely IEEE 754 compatible. The FPU is therefore emulated in software for normal ELF executables. The R5900 has several significant hardware bugs. Perhaps the most important bug affecting applications is the short loop bug that under certain conditions causes loops to execute only once or twice.

R5900 specific handling of SYNC.P, SYNC.L, ERET, etc. are implemented.

2. Interrupts, a timer and minimal DMA support are implemented.

3. System commands (SCMDs) are implemented to identify the hardware model, read and set the real-time clock (RTC), and power off the machine.

4. Read-only memory (ROM) operations are implemented. These are used to identify the hardware. ROM operations are also used to resolve IOP libraries linked as firmware, as explained below.

6. The input/output processor (IOP) and its sub-system interface (SIF) are supported[2]. The IOP is a MIPS R3000A, or in later models a PowerPC 405GP emulating a MIPS R3000A, sometimes referred to as the sub processor. It runs a separate minikernel implemented in ROM. Most peripherals require, in whole or in part, handling by the IOP.

Two IOP services are required to support USB devices: IOP memory allocation and IOP interrupt relay. The latter requires linking a custom IOP module, handled as firmware by the kernel, with library dependency resolution. Remote procedure calls (RPCs) are used to issue bidirectional commands over the SIF, via DMA.

7. The Graphics Synthesizer (GS) is the video hardware that displays the frame buffer console. The GS is highly configurable with a substantial set of hardware accelerated features. Its local memory is not directly accessible from the main bus. All drawing primitives are transmitted via DMA[3].

8. The frame buffer driver implements console support. The font is stored as textures in local GS memory and several hardware accelerated operations are implemented, for example XPAN, YPAN and YWRAP.

The o32 ABI is supported, but not yet n32 as it involves some additional complications. I would very much like to add support for the 128-bit R5900 GPRs, for two primary reasons:

- The 128-bit multimedia instruction set is one of the defining features of the R5900. A respectable PlayStation 2 application ought to use them.

- The MMIs cannot be disabled by the kernel. MMIs will therefore appear to mostly work, with some occasional register corruption due to context switches, unless the kernel properly saves and restores the 128-bit registers.

This patch series has been tested with the PlayStation 2 models SCPH-30004, SCPH-30004 R, SCPH-37000 L, SCPH-39004, SCPH-50004, SCPH-70004, SCPH-75004 and SCPH-77004.

Please consider it for inclusion.

Fredrik

References:

[1] "TX System RISC TX79 Core Architecture" manual, revision 2.0, Toshiba Corporation, https://wiki.qemu.org/File:C790.pdf

[2] "EE Overview", version 6.0, Sony Computer Entertainment Inc.

[3] "GS User's Manual", version 6.0, Sony Computer Entertainment Inc.

Fredrik Noring (120):
MIPS: R5900: Initial support for the Emotion Engine in the PlayStation 2
MIPS: R5900: Trap the RDHWR instruction as an SQ address exception
MIPS: R5900: Sign-extend o32 system call registers
MIPS: R5900: Reset bits 127..64 of GPRs in RESTORE_SOME
MIPS: R5900: Reset the funnel shift amount (SA) register in RESTORE_SOME
MIPS: R5900: Workaround for the short loop bug
MIPS: R5900: Add the SYNC.P instruction
MIPS: R5900: Add implicit SYNC.P to the UASM_i_M[FT]C0 macros
MIPS: R5900: Add mandatory SYNC.P to all M[FT]C0 instructions
MIPS: R5900: Workaround exception NOP execution bug (FLX05)
MIPS: R5900: Avoid pipeline hazard with the TLBP instruction
MIPS: R5900: Avoid pipeline hazards with the TLBW[IR] instructions
MIPS: R5900: Avoid pipeline hazard with the TLBR instruction
MIPS: R5900: Install final length of TLB refill handler rather than 256 bytes
MIPS: R5900: Verify that the TLB refill handler does not overflow
MIPS: R5900: The ERET instruction has issues with delay slot and CACHE
MIPS: R5900: Define CACHE instruction operation field encodings
MIPS: R5900: Workaround where MSB must be 0 for the instruction cache
MIPS: R5900: Use SYNC.L for data cache and SYNC.P for instruction cache
MIPS: R5900: Define CP0.Config register fields
MIPS: R5900: Workaround for CACHE instruction near branch delay slot
MIPS: R5900: Support 64-bit inq() and outq() macros in 32-bit kernels
MIPS: R5900: Add MFSA and MTSA instructions for the special SA register
MIPS: PS2: Define PlayStation 2 I/O port, ROM and RAM address spaces
MIPS: PS2: Define interrupt controller, DMA and timer IRQs
MIPS: PS2: Interrupt controller (INTC) IRQ support
MIPS: PS2: DMAC: Define DMA controller registers
MIPS: PS2: DMAC: Define tag structures
MIPS: PS2: DMAC: IRQ support
MIPS: PS2: Timer support
MIPS: PS2: SCMD: System command support
MIPS: PS2: SCMD: System power off command
MIPS: PS2: SCMD: Read system machine name command
MIPS: PS2: SCMD: Read system command for the real-time clock (RTC)
MIPS: PS2: SCMD: Set system command for the real-time clock (RTC)
MIPS: PS2: ROM: Iterate over the files in a given ROM directory
MIPS: PS2: ROM: Find ROM files with a given name, if they exist
MIPS: PS2: ROM: Read data for a given ROM file name
MIPS: PS2: ROM: Read extended information for a given ROM file
MIPS: PS2: ROM: Read and decode the ROMVER file
MIPS: PS2: ROM: Resolve the name for the type in the ROMVER file
MIPS: PS2: ROM: Resolve the name for the region in the ROMVER file
MIPS: PS2: ROM: Permit /dev/mem to access read-only memory
MIPS: PS2: ROM: Sysfs module to inspect ROM files
MIPS: PS2: ROM: Provide extended file information via sysfs
MIPS: PS2: Identify the machine by model name
MIPS: PS2: Let the system type be Sony PlayStation 2
MIPS: Define and use cpu_relax_forever() for various halting loops
MIPS: PS2: Power off support
MIPS: PS2: Real-time clock (RTC) driver
MIPS: PS2: IOP: I/O processor DMA register PCR2 set and clear
MIPS: PS2: SIF: Sub-system interface reset of the I/O processor (IOP)
MIPS: PS2: IOP: Define error numbers, descriptions and errno mapping
MIPS: PS2: SIF: SIF register write command support
MIPS: PS2: SIF: Respond to remote procedure call (RPC) bind command
MIPS: PS2: SIF: Respond to RPC bind end command
MIPS: PS2: SIF: Reset the SIF0 (sub-to-main) DMA controller
MIPS: PS2: SIF: Handle SIF0 (sub-to-main) RPCs via interrupts
MIPS: PS2: SIF: Enable the IOP to issue SIF commands
MIPS: PS2: SIF: Enable the IOP to issue SIF RPCs
MIPS: PS2: SIF: sif_rpc_bind() to request an RPC server connection
MIPS: PS2: SIF: sif_rpc_unbind() to release an RPC server connection
MIPS: PS2: SIF: sif_rpc() to issue a remote procedure call
MIPS: PS2: IOP: Permit /dev/mem to access IOP memory
MIPS: PS2: IOP: I/O processor memory support
FIXME: Export _dma_cache_{wback,wback_inv,inv}
MIPS: PS2: IOP: Module linking support
MIPS: PS2: IOP: Verify that modules are IRX objects
MIPS: PS2: IOP: Module version compatibility verification
MIPS: PS2: IOP: Avoid linking already linked library modules
MIPS: PS2: IOP: Resolve module dependencies
MIPS: PS2: IOP: SIF printk command support
MIPS: PS2: IOP: Heap memory allocate and free
MIPS: PS2: SIF: Request RPC IRQ command
MIPS: PS2: IOP: IRQ support
MIPS: PS2: GS: Define privileged Graphics Synthesizer registers
MIPS: PS2: GS: Write privileged registers
MIPS: PS2: GS: Read privileged registers
MIPS: PS2: GS: Define privileged register structures
MIPS: PS2: GS: Define gs_xorq_imr()
MIPS: PS2: GS: Privileged register write macros with named fields
MIPS: PS2: GS: IRQ support
MIPS: PS2: GS: Define Graphics Synthesizer primitive structures
MIPS: PS2: GIF: Define Graphics Synthesizer interface structures
MIPS: PS2: GIF: Graphics Synthesizer interface support
MIPS: PS2: GS: Graphics Synthesizer device init and video clock
MIPS: PS2: GS: Compute block count and indices
MIPS: PS2: GS: Primitive and texel coordinate transformations
MIPS: PS2: GS: Approximate video region with ROM region
macro: Extend COUNT_ARGS() from 12 to 32 arguments
MIPS: PS2: GS: Show privileged registers with sysfs
MIPS: PS2: GS: Store privileged registers with sysfs
fbdev: Add fb_warn_once() variant that only prints a warning once
MIPS: PS2: FB: Frame buffer driver for the PlayStation 2
MIPS: PS2: FB: fb_set_par() standard-definition television support
MIPS: PS2: FB: fb_set_par() high-definition television support
MIPS: PS2: FB: fb_set_par() VESA computer display mode support
MIPS: PS2: FB: Preconfigure standard PAL, NTSC and VESA display modes
MIPS: PS2: FB: Reset the Graphics Synthesizer drawing environment
MIPS: PS2: FB: Clear the display buffer when changing video modes
MIPS: PS2: FB: fb_setcolreg() 256 colour pseudo palette support
MIPS: PS2: FB: fb_settile() with font stored as palette indexed textures
MIPS: PS2: FB: Hardware accelerated fb_tilecopy() support
MIPS: PS2: FB: Hardware accelerated fb_tilefill() support
MIPS: PS2: FB: Simplified fb_tileblit() support
MIPS: PS2: FB: fb_tilecursor() placeholder
MIPS: PS2: FB: Hardware accelerated fb_pan_display() support
MIPS: PS2: FB: fb_blank() display power management signaling (DPMS)
MIPS: PS2: FB: Disable GIF DMA completion interrupts
MIPS: PS2: FB: PAL and NTSC grayscale support
MIPS: PS2: FB: Analogue display mode adjustment module parameter
USB: OHCI: Support for the PlayStation 2
USB: OHCI: OHCI_INTR_MIE workaround for freeze on the PlayStation 2
MIPS: PS2: Workaround for unexpected uLaunchELF CP0 Status user mode
MIPS: PS2: Define initial PlayStation 2 devices
MIPS: PS2: Define workarounds related to the PlayStation 2
MIPS: PS2: Define R5900 feature overrides
MIPS: PS2: Define the PlayStation 2 platform
MIPS: PS2: Initial support for the Sony PlayStation 2
MIPS: Fix name of BOOT_MEM_ROM_DATA

 arch/mips/Kbuild.platforms                    |    1 +
 arch/mips/Kconfig                             |   35 +-
 arch/mips/Makefile                            |    1 +
 arch/mips/alchemy/board-gpr.c                 |    7 +-
 arch/mips/alchemy/board-mtx1.c                |    7 +-
 arch/mips/alchemy/board-xxs1500.c             |    7 +-
 arch/mips/alchemy/devboards/platform.c        |    4 +-
 arch/mips/ar7/setup.c                         |    4 +-
 arch/mips/ath79/setup.c                       |    8 +-
 arch/mips/bcm47xx/setup.c                     |    9 +-
 arch/mips/bcm63xx/setup.c                     |   12 +-
 arch/mips/boot/compressed/decompress.c        |    4 +-
 arch/mips/boot/compressed/head.S              |   16 +-
 arch/mips/cavium-octeon/smp.c                 |    4 +-
 arch/mips/cobalt/reset.c                      |    5 +-
 arch/mips/emma/markeins/setup.c               |    5 +-
 arch/mips/fw/arc/init.c                       |    4 +-
 arch/mips/include/asm/asmmacro.h              |   53 +
 arch/mips/include/asm/cacheops.h              |   17 +
 arch/mips/include/asm/cpu-type.h              |    4 +
 arch/mips/include/asm/cpu.h                   |    3 +-
 arch/mips/include/asm/io.h                    |   60 +-
 arch/mips/include/asm/irqflags.h              |   15 +
 .../asm/mach-ps2/cpu-feature-overrides.h      |   35 +
 arch/mips/include/asm/mach-ps2/dmac.h         |  254 ++
 arch/mips/include/asm/mach-ps2/gif.h          |   77 +
 arch/mips/include/asm/mach-ps2/gs-registers.h |  646 +++++
 arch/mips/include/asm/mach-ps2/gs.h           |   81 +
 arch/mips/include/asm/mach-ps2/iop-error.h    |   76 +
 arch/mips/include/asm/mach-ps2/iop-heap.h     |   19 +
 arch/mips/include/asm/mach-ps2/iop-memory.h   |   21 +
 arch/mips/include/asm/mach-ps2/iop-module.h   |   13 +
 .../mips/include/asm/mach-ps2/iop-registers.h |   19 +
 arch/mips/include/asm/mach-ps2/iop.h          |   21 +
 arch/mips/include/asm/mach-ps2/irq.h          |  130 +
 arch/mips/include/asm/mach-ps2/rom.h          |  171 ++
 arch/mips/include/asm/mach-ps2/scmd.h         |   56 +
 arch/mips/include/asm/mach-ps2/sif.h          |   94 +
 arch/mips/include/asm/mach-ps2/war.h          |   24 +
 arch/mips/include/asm/mipsregs.h              |  103 +
 arch/mips/include/asm/module.h                |    2 +
 arch/mips/include/asm/processor.h             |    6 +
 arch/mips/include/asm/r4kcache.h              |  244 +-
 arch/mips/include/asm/stackframe.h            |  108 +
 arch/mips/include/asm/string.h                |    3 +
 arch/mips/include/asm/traps.h                 |    2 +
 arch/mips/include/asm/uasm.h                  |   23 +
 arch/mips/include/uapi/asm/gif.h              |  164 ++
 arch/mips/include/uapi/asm/gs.h               |  723 +++++
 arch/mips/jz4740/reset.c                      |    9 +-
 arch/mips/kernel/cpu-probe.c                  |    8 +
 arch/mips/kernel/genex.S                      |   65 +
 arch/mips/kernel/head.S                       |    9 +
 arch/mips/kernel/r4k_switch.S                 |    9 +
 arch/mips/kernel/scall32-o32.S                |   12 +
 arch/mips/kernel/setup.c                      |    6 +-
 arch/mips/kernel/traps.c                      |   19 +-
 arch/mips/kernel/unaligned.c                  |   36 +-
 arch/mips/lib/delay.c                         |    7 +-
 arch/mips/lib/memset.S                        |    2 +
 arch/mips/loongson64/common/reset.c           |   16 +-
 arch/mips/mm/Makefile                         |    1 +
 arch/mips/mm/c-r4k.c                          |   27 +-
 arch/mips/mm/cache.c                          |    4 +
 arch/mips/mm/cerr-sb1.c                       |    4 +-
 arch/mips/mm/cex-gen.S                        |    6 +
 arch/mips/mm/tlbex-fault.S                    |    3 +
 arch/mips/mm/tlbex.c                          |   94 +-
 arch/mips/mm/uasm-mips.c                      |    1 +
 arch/mips/mm/uasm.c                           |    5 +-
 arch/mips/pic32/common/reset.c                |    9 +-
 arch/mips/pnx833x/common/reset.c              |    5 +-
 arch/mips/ps2/Kconfig                         |   16 +
 arch/mips/ps2/Makefile                        |   12 +
 arch/mips/ps2/Platform                        |    7 +
 arch/mips/ps2/devices.c                       |   98 +
 arch/mips/ps2/dmac-irq.c                      |  102 +
 arch/mips/ps2/identify.c                      |   86 +
 arch/mips/ps2/intc-irq.c                      |  118 +
 arch/mips/ps2/irq.c                           |   38 +
 arch/mips/ps2/memory.c                        |   45 +
 arch/mips/ps2/prom.c                          |   18 +
 arch/mips/ps2/reboot.c                        |   29 +
 arch/mips/ps2/rom-sysfs.c                     |  392 +++
 arch/mips/ps2/rom.c                           |  702 +++++
 arch/mips/ps2/scmd.c                          |  379 +++
 arch/mips/ps2/time.c                          |  153 ++
 arch/mips/sgi-ip22/ip22-reset.c               |    3 +-
 arch/mips/sgi-ip27/ip27-berr.c                |    4 +-
 arch/mips/sgi-ip27/ip27-reset.c               |   13 +-
 arch/mips/sgi-ip32/ip32-berr.c                |    4 +-
 arch/mips/sgi-ip32/ip32-irq.c                 |    3 +-
 arch/mips/sibyte/common/cfe.c                 |    3 +-
 arch/mips/txx9/rbtx4939/setup.c               |    4 +-
 arch/mips/vr41xx/common/pmu.c                 |    2 +-
 drivers/Makefile                              |    1 +
 drivers/ps2/Makefile                          |   12 +
 drivers/ps2/gif.c                             |  106 +
 drivers/ps2/gs-irq.c                          |   93 +
 drivers/ps2/gs-registers.c                    |  189 ++
 drivers/ps2/gs-sysfs.c                        |  592 +++++
 drivers/ps2/gs.c                              |  319 +++
 drivers/ps2/iop-heap.c                        |   90 +
 drivers/ps2/iop-irq.c                         |  186 ++
 drivers/ps2/iop-memory.c                      |   56 +
 drivers/ps2/iop-module.c                      |  954 +++++++
 drivers/ps2/iop-registers.c                   |   39 +
 drivers/ps2/sif.c                             |  888 +++++++
 drivers/rtc/Kconfig                           |   10 +
 drivers/rtc/Makefile                          |    1 +
 drivers/rtc/rtc-ps2.c                         |   74 +
 drivers/usb/host/Kconfig                      |    8 +
 drivers/usb/host/Makefile                     |    1 +
 drivers/usb/host/ohci-ps2.c                   |  264 ++
 drivers/video/fbdev/Kconfig                   |   12 +
 drivers/video/fbdev/Makefile                  |    1 +
 drivers/video/fbdev/ps2fb.c                   | 2362 +++++++++++++++++
 include/linux/console_struct.h                |    2 +
 include/linux/fb.h                            |    2 +
 include/linux/kernel.h                        |   12 +-
 include/uapi/linux/fb.h                       |    1 +
 121 files changed, 12077 insertions(+), 186 deletions(-)
 create mode 100644 arch/mips/include/asm/mach-ps2/cpu-feature-overrides.h
 create mode 100644 arch/mips/include/asm/mach-ps2/dmac.h
 create mode 100644 arch/mips/include/asm/mach-ps2/gif.h
 create mode 100644 arch/mips/include/asm/mach-ps2/gs-registers.h
 create mode 100644 arch/mips/include/asm/mach-ps2/gs.h
 create mode 100644 arch/mips/include/asm/mach-ps2/iop-error.h
 create mode 100644 arch/mips/include/asm/mach-ps2/iop-heap.h
 create mode 100644 arch/mips/include/asm/mach-ps2/iop-memory.h
 create mode 100644 arch/mips/include/asm/mach-ps2/iop-module.h
 create mode 100644 arch/mips/include/asm/mach-ps2/iop-registers.h
 create mode 100644 arch/mips/include/asm/mach-ps2/iop.h
 create mode 100644 arch/mips/include/asm/mach-ps2/irq.h
 create mode 100644 arch/mips/include/asm/mach-ps2/rom.h
 create mode 100644 arch/mips/include/asm/mach-ps2/scmd.h
 create mode 100644 arch/mips/include/asm/mach-ps2/sif.h
 create mode 100644 arch/mips/include/asm/mach-ps2/war.h
 create mode 100644 arch/mips/include/uapi/asm/gif.h
 create mode 100644 arch/mips/include/uapi/asm/gs.h
 create mode 100644 arch/mips/ps2/Kconfig
 create mode 100644 arch/mips/ps2/Makefile
 create mode 100644 arch/mips/ps2/Platform
 create mode 100644 arch/mips/ps2/devices.c
 create mode 100644 arch/mips/ps2/dmac-irq.c
 create mode 100644 arch/mips/ps2/identify.c
 create mode 100644 arch/mips/ps2/intc-irq.c
 create mode 100644 arch/mips/ps2/irq.c
 create mode 100644 arch/mips/ps2/memory.c
 create mode 100644 arch/mips/ps2/prom.c
 create mode 100644 arch/mips/ps2/reboot.c
 create mode 100644 arch/mips/ps2/rom-sysfs.c
 create mode 100644 arch/mips/ps2/rom.c
 create mode 100644 arch/mips/ps2/scmd.c
 create mode 100644 arch/mips/ps2/time.c
 create mode 100644 drivers/ps2/Makefile
 create mode 100644 drivers/ps2/gif.c
 create mode 100644 drivers/ps2/gs-irq.c
 create mode 100644 drivers/ps2/gs-registers.c
 create mode 100644 drivers/ps2/gs-sysfs.c
 create mode 100644 drivers/ps2/gs.c
 create mode 100644 drivers/ps2/iop-heap.c
 create mode 100644 drivers/ps2/iop-irq.c
 create mode 100644 drivers/ps2/iop-memory.c
 create mode 100644 drivers/ps2/iop-module.c
 create mode 100644 drivers/ps2/iop-registers.c
 create mode 100644 drivers/ps2/sif.c
 create mode 100644 drivers/rtc/rtc-ps2.c
 create mode 100644 drivers/usb/host/ohci-ps2.c
 create mode 100644 drivers/video/fbdev/ps2fb.c
Signature · 1 line
2.21.0
#1 lore raw

PATCH 1/120 MIPS: R5900: Initial support for the Emotion Engine in the PlayStation 2

The R5900 implements the 64-bit MIPS III instruction set except LL, SC, LLD and SCD, with additional PREFETCH and conditional move instructions from MIPS IV as well as three-operand multiply and multiply-accumulate instructions[1]. It also has a set of R5900 specific 128-bit multimedia instructions. Endianness is not configurable and taken to be little- endian. The R5900 does not implement CP0.Status.{UX,SX,KX,PX}.

The COP1 FPU implements single-precision floating-point operations but is not entirely IEEE 754 compatible. In particular,

- NaN (not a number) and plus/minus infinities are not supported;
- exception mechanisms are not fully supported;
- denormalized numbers are not supported;
- rounding towards nearest and plus/minus infinities are not supported;
- computed results usually differs in the least significant bit;
- saturating instructions can differ more than the least significant bit.

Since only rounding towards zero is supported, the two least significant bits of FCR31 are hardwired to 01. To support IEEE 754 in applications the FPU is emulated in software by the kernel.

The VPU0 is a vector processor of the Emotion Engine. In macro mode, it functions as COP2 (coprocessor) and instructions execute simultaneously in the main integer pipeline I1 and the COP2 pipeline[2]. In micro mode, the VPU0 functions as a stand-alone processor. The VPU1 is an additional vector processor that operates independently of both the R5900 and the VPU0. It primarily acts as a preprocessor to the Graphics Synthesizer[3]. The vector processors are initially disabled.

The scratch pad RAM (SPRAM) of the Emotion Engine is 16 KiB of very fast static RAM organised in 128-bit quadwords[4]. Both the DMA controller and the R5900 can access the SPRAM. The SPRAM is initially disabled.

The R5900 has several significant hardware bugs. Perhaps the most important bug affecting applications is the short loop bug that under certain conditions causes loops to execute only once or twice.

References:

[1] "TX System RISC TX79 Core Architecture" manual, revision 2.0, Toshiba Corporation, https://wiki.qemu.org/File:C790.pdf

[2] "VU User's Manual", version 6.0, Sony Computer Entertainment Inc.

[3] "GS User's Manual", version 6.0, Sony Computer Entertainment Inc.

[4] "EE Core User's Manual", version 6.0, Sony Computer Entertainment Inc.

---
 arch/mips/Kconfig                | 13 ++++++++++++-
 arch/mips/Makefile               |  1 +
 arch/mips/include/asm/cpu-type.h |  4 ++++
 arch/mips/include/asm/cpu.h      |  3 ++-
 arch/mips/include/asm/module.h   |  2 ++
 arch/mips/kernel/cpu-probe.c     |  8 ++++++++
 arch/mips/mm/Makefile            |  1 +
 arch/mips/mm/c-r4k.c             | 19 +++++++++++++++++++
 8 files changed, 49 insertions(+), 2 deletions(-)
arch/mips/Kconfig +12 -1
index d50fafd7bf3a..88d6c13260e1 100644
@@ -1749,6 +1749,14 @@ config CPU_BMIPS
 	help
 	  Support for BMIPS32/3300/4350/4380 and BMIPS5000 processors.
 
+config CPU_R5900
+	bool "R5900"
+	depends on SYS_HAS_CPU_R5900
+	select CPU_SUPPORTS_32BIT_KERNEL
+	select IRQ_MIPS_CPU
+	help
+	  Toshiba R5900 processor (Emotion Engine in the Sony PlayStation 2).
+
 config CPU_XLR
 	bool "Netlogic XLR SoC"
 	depends on SYS_HAS_CPU_XLR
@@ -1987,6 +1995,9 @@ config SYS_HAS_CPU_R5432
 config SYS_HAS_CPU_R5500
 	bool
 
+config SYS_HAS_CPU_R5900
+	bool
+
 config SYS_HAS_CPU_NEVADA
 	bool
 
@@ -2309,7 +2320,7 @@ config MIPS_FP_SUPPORT
 config CPU_R2300_FPU
 	bool
 	depends on MIPS_FP_SUPPORT
-	default y if CPU_R3000 || CPU_TX39XX
+	default y if CPU_R3000 || CPU_TX39XX || CPU_R5900
 
 config CPU_R4K_FPU
 	bool
arch/mips/Makefile +1 -0
index eceff9b75b22..81cd620d32f7 100644
@@ -178,6 +178,7 @@ cflags-$(CONFIG_CPU_R5432)	+= $(call cc-option,-march=r5400,-march=r5000) \
 			-Wa,--trap
 cflags-$(CONFIG_CPU_R5500)	+= $(call cc-option,-march=r5500,-march=r5000) \
 			-Wa,--trap
+cflags-$(CONFIG_CPU_R5900)	+= -march=r5900 -Wa,--trap
 cflags-$(CONFIG_CPU_NEVADA)	+= $(call cc-option,-march=rm5200,-march=r5000) \
 			-Wa,--trap
 cflags-$(CONFIG_CPU_RM7000)	+= $(call cc-option,-march=rm7000,-march=r5000) \
arch/mips/include/asm/cpu-type.h +4 -0
index a45af3de075d..7d35100e0e18 100644
@@ -151,6 +151,10 @@ static inline int __pure __get_cpu_type(const int cpu_type)
 	case CPU_R5500:
 #endif
 
+#ifdef CONFIG_SYS_HAS_CPU_R5900
+	case CPU_R5900:
+#endif
+
 #ifdef CONFIG_SYS_HAS_CPU_NEVADA
 	case CPU_NEVADA:
 #endif
arch/mips/include/asm/cpu.h +2 -1
index 290369fa44a4..05a0f5a07f08 100644
@@ -83,6 +83,7 @@
 #define PRID_IMP_R4650		0x2200		/* Same as R4640 */
 #define PRID_IMP_R5000		0x2300
 #define PRID_IMP_TX49		0x2d00
+#define PRID_IMP_R5900		0x2e00		/* PlayStation 2 */
 #define PRID_IMP_SONIC		0x2400
 #define PRID_IMP_MAGIC		0x2500
 #define PRID_IMP_RM7000		0x2700
@@ -298,7 +299,7 @@ enum cpu_type_enum {
 	CPU_R4700, CPU_R5000, CPU_R5500, CPU_NEVADA, CPU_R5432, CPU_R10000,
 	CPU_R12000, CPU_R14000, CPU_R16000, CPU_VR41XX, CPU_VR4111, CPU_VR4121,
 	CPU_VR4122, CPU_VR4131, CPU_VR4133, CPU_VR4181, CPU_VR4181A, CPU_RM7000,
-	CPU_SR71000, CPU_TX49XX,
+	CPU_SR71000, CPU_TX49XX, CPU_R5900,
 
 	/*
 	 * R8000 class processors
arch/mips/include/asm/module.h +2 -0
index 6dc0b21b8acd..d6fbc3aad2a1 100644
@@ -115,6 +115,8 @@ search_module_dbetables(unsigned long addr)
 #define MODULE_PROC_FAMILY "R5432 "
 #elif defined CONFIG_CPU_R5500
 #define MODULE_PROC_FAMILY "R5500 "
+#elif defined CONFIG_CPU_R5900
+#define MODULE_PROC_FAMILY "R5900 "
 #elif defined CONFIG_CPU_NEVADA
 #define MODULE_PROC_FAMILY "NEVADA "
 #elif defined CONFIG_CPU_R8000
arch/mips/kernel/cpu-probe.c +8 -0
index 9635c1db3ae6..629391948ab4 100644
@@ -1484,6 +1484,14 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
 			     MIPS_CPU_WATCH | MIPS_CPU_LLSC;
 		c->tlbsize = 48;
 		break;
+	case PRID_IMP_R5900:
+		c->cputype = CPU_R5900;
+		__cpu_name[cpu] = "R5900";
+		c->isa_level = MIPS_CPU_ISA_III;
+		c->fpu_msk31 |= FPU_CSR_CONDX;
+		c->options = R4K_OPTS | MIPS_CPU_DIVEC;
+		c->tlbsize = 48;
+		break;
 	case PRID_IMP_NEVADA:
 		c->cputype = CPU_NEVADA;
 		__cpu_name[cpu] = "Nevada";
arch/mips/mm/Makefile +1 -0
index 1e8d335025d7..08db8f27d125 100644
@@ -30,6 +30,7 @@ obj-$(CONFIG_DMA_NONCOHERENT)	+= dma-noncoherent.o
 
 obj-$(CONFIG_CPU_R4K_CACHE_TLB) += c-r4k.o cex-gen.o tlb-r4k.o
 obj-$(CONFIG_CPU_R3000)		+= c-r3k.o tlb-r3k.o
+obj-$(CONFIG_CPU_R5900)		+= c-r4k.o cex-gen.o tlb-r4k.o
 obj-$(CONFIG_CPU_R8000)		+= c-r4k.o cex-gen.o tlb-r8k.o
 obj-$(CONFIG_CPU_SB1)		+= c-r4k.o cerr-sb1.o cex-sb1.o tlb-r4k.o
 obj-$(CONFIG_CPU_TX39XX)	+= c-tx39.o tlb-r3k.o
arch/mips/mm/c-r4k.c +19 -0
index 5166e38cd1c6..4f2cecbec722 100644
@@ -1113,6 +1113,18 @@ static void probe_pcache(void)
 		c->options |= MIPS_CPU_CACHE_CDEX_P | MIPS_CPU_PREFETCH;
 		break;
 
+	case CPU_R5900:
+		icache_size = 1 << (12 + ((config & CONF_IC) >> 9));
+		c->icache.linesz = 64;
+		c->icache.ways = 2;
+		c->icache.waybit = 0;
+
+		dcache_size = 1 << (12 + ((config & CONF_DC) >> 6));
+		c->dcache.linesz = 64;
+		c->dcache.ways = 2;
+		c->dcache.waybit = 0;
+		break;
+
 	case CPU_TX49XX:
 		icache_size = 1 << (12 + ((config & CONF_IC) >> 9));
 		c->icache.linesz = 16 << ((config & CONF_IB) >> 5);
@@ -1388,6 +1400,13 @@ static void probe_pcache(void)
 	case CPU_R16000:
 		break;
 
+	case CPU_R5900:
+		if (c->icache.waysize > PAGE_SIZE)
+			c->dcache.flags |= MIPS_CACHE_ALIASES;
+		if (c->dcache.waysize > PAGE_SIZE)
+			c->dcache.flags |= MIPS_CACHE_ALIASES;
+		break;
+
 	case CPU_74K:
 	case CPU_1074K:
 		has_74k_erratum = alias_74k_erratum(c);
Signature · 1 line
2.21.0
#2 lore raw

Re: [PATCH 001/120] MIPS: R5900: Initial support for the Emotion Engine in the PlayStation 2

arch/mips/include/asm/cpu.h line 89

Quoted 12 lines · Fredrik Noring
arch/mips/include/asm/cpu.h +1 -0
index 290369fa44a4..05a0f5a07f08 100644
@@ -83,6 +83,7 @@
 #define PRID_IMP_R4650		0x2200		/* Same as R4640 */
 #define PRID_IMP_R5000		0x2300
 #define PRID_IMP_TX49		0x2d00
+#define PRID_IMP_R5900		0x2e00		/* PlayStation 2 */
 #define PRID_IMP_SONIC		0x2400
 #define PRID_IMP_MAGIC		0x2500
 #define PRID_IMP_RM7000		0x2700

Hmm, I don't know why PRID_IMP_TX49 has been misplaced, 18 years ago with
commit b8c80cd25ffe ("Add missing PrId values for TX39 / TX 49 CPUs."), however I suggest that PRID_IMP_R5900 does not make things worse. I.e. please place it between PRID_IMP_NEVADA and PRID_IMP_RM9000.

Maciej

#3 lore raw

Re: [PATCH 001/120] MIPS: R5900: Initial support for the Emotion Engine in the PlayStation 2

Quoted 4 lines

Hmm, I don't know why PRID_IMP_TX49 has been misplaced, 18 years ago with
commit b8c80cd25ffe ("Add missing PrId values for TX39 / TX 49 CPUs."), however I suggest that PRID_IMP_R5900 does not make things worse. I.e. please place it between PRID_IMP_NEVADA and PRID_IMP_RM9000.

Done, thanks!

Fredrik

#4 lore raw

PATCH 2/120 MIPS: R5900: Trap the RDHWR instruction as an SQ address exception

In reply to Fredrik Noring

On the R5900, the RDHWR instruction is interpreted as the R5900 specific SQ instruction[1] that traps into a zero page address exception. Hence RDHWR can be emulated by emulate_load_store_insn().

CONFIG_DEFAULT_MMAP_MIN_ADDR must not be less than PAGE_SIZE to reliably trap and emulate RDHWR, so this is made a BUILD_BUG_ON for the R5900.

References:

[1] "TX System RISC TX79 Core Architecture" manual, revision 2.0,

    Toshiba Corporation, p. B-162, https://wiki.qemu.org/File:C790.pdf
---
 arch/mips/include/asm/traps.h |  2 ++
 arch/mips/kernel/traps.c      |  2 +-
 arch/mips/kernel/unaligned.c  | 36 ++++++++++++++++++++++++++++++++++-
 3 files changed, 38 insertions(+), 2 deletions(-)
arch/mips/include/asm/traps.h +2 -0
index 6a0864bb604d..ed6449f2967b 100644
@@ -35,4 +35,6 @@ extern int register_nmi_notifier(struct notifier_block *nb);
 	register_nmi_notifier(&fn##_nb);				\
 })
 
+int simulate_rdhwr(struct pt_regs *regs, int rd, int rt);
+
 #endif /* _ASM_TRAPS_H */
arch/mips/kernel/traps.c +1 -1
index 342e41de9d64..9423b9a2eb67 100644
@@ -625,7 +625,7 @@ static int simulate_llsc(struct pt_regs *regs, unsigned int opcode)
  * Simulate trapping 'rdhwr' instructions to provide user accessible
  * registers not implemented in hardware.
  */
-static int simulate_rdhwr(struct pt_regs *regs, int rd, int rt)
+int simulate_rdhwr(struct pt_regs *regs, int rd, int rt)
 {
 	struct thread_info *ti = task_thread_info(current);
 
arch/mips/kernel/unaligned.c +35 -1
index 92bd2b0f0548..f490944d73cf 100644
@@ -90,6 +90,7 @@
 #include <asm/fpu_emulator.h>
 #include <asm/inst.h>
 #include <asm/mmu_context.h>
+#include <asm/traps.h>
 #include <linux/uaccess.h>
 
 #define STR(x)	__STR(x)
@@ -934,7 +935,39 @@ static void emulate_load_store_insn(struct pt_regs *regs,
 		 * interest.
 		 */
 	case spec3_op:
-		if (insn.dsp_format.func == lx_op) {
+		if (IS_ENABLED(CONFIG_CPU_R5900)) {
+			/*
+			 * On the R5900, the RDHWR instruction
+			 *
+			 *     +--------+-------+----+----+-------+--------+
+			 *     | 011111 | 00000 | rt | rd | 00000 | 111011 |
+			 *     +--------+-------+----+----+-------+--------+
+			 *          6       5      5    5     5        6
+			 *
+			 * is interpreted as the R5900 specific SQ instruction
+			 *
+			 *     +--------+-------+----+---------------------+
+			 *     | 011111 |  base | rt |        offset       |
+			 *     +--------+-------+----+---------------------+
+			 *          6       5      5            16
+			 *
+			 * that asserts a zero page address exception. Hence
+			 * RDHWR can be trapped and emulated here, provided
+			 * DEFAULT_MMAP_MIN_ADDR isn't zero.
+			 */
+			BUILD_BUG_ON(IS_ENABLED(CONFIG_CPU_R5900) &&
+				CONFIG_DEFAULT_MMAP_MIN_ADDR < PAGE_SIZE);
+			if (insn.r_format.func == rdhwr_op &&
+			    insn.r_format.rs == 0 &&
+			    insn.r_format.re == 0) {
+				if (compute_return_epc(regs) < 0 ||
+				    simulate_rdhwr(regs, insn.r_format.rd,
+						   insn.r_format.rt) < 0)
+					goto sigill;
+				return;
+			}
+			goto sigbus;
+		} else if (insn.dsp_format.func == lx_op) {
 			switch (insn.dsp_format.op) {
 			case lwx_op:
 				if (!access_ok(addr, 4))
@@ -1342,6 +1375,7 @@ static void emulate_load_store_insn(struct pt_regs *regs,
 		cu2_notifier_call_chain(CU2_SDC2_OP, regs);
 		break;
 #endif
+
 	default:
 		/*
 		 * Pheeee...  We encountered an yet unknown instruction or
Signature · 1 line
2.21.0
#5 lore raw

Re: [PATCH 002/120] MIPS: R5900: Trap the RDHWR instruction as an SQ address exception

Quoted 2 lines · Fredrik Noring

CONFIG_DEFAULT_MMAP_MIN_ADDR must not be less than PAGE_SIZE to reliably trap and emulate RDHWR, so this is made a BUILD_BUG_ON for the R5900.

I think a more complex solution is required as the value can be changed
at run time, via /proc/sys/vm/mmap_min_addr, defeating this protection. E.g. by introducing an ARCH_MIN_MMAP_MIN_ADDR minimum value, by default 0 unless overridden by the architecture selected, and then using it for both the default DEFAULT_MMAP_MIN_ADDR value and the minimum accepted via /proc/sys/vm/mmap_min_addr.

arch/mips/kernel/unaligned.c static void emulate_load_store_insn(struct pt_regs *regs, · line 1381

Quoted 12 lines
arch/mips/kernel/unaligned.c +1 -0
index 92bd2b0f0548..f490944d73cf 100644
@@ -1342,6 +1375,7 @@ static void emulate_load_store_insn(struct pt_regs *regs,
 		cu2_notifier_call_chain(CU2_SDC2_OP, regs);
 		break;
 #endif
+
 	default:
 		/*
 		 * Pheeee...  We encountered an yet unknown instruction or

Extraneous change.

Maciej

#6 lore raw

Re: [PATCH 002/120] MIPS: R5900: Trap the RDHWR instruction as an SQ address exception

In reply to Fredrik Noring

Hi Fredrik,

Cc'ing glibc folks in case...

Quoted 3 lines · Fredrik Noring

On the R5900, the RDHWR instruction is interpreted as the R5900 specific SQ instruction[1] that traps into a zero page address exception. Hence RDHWR can be emulated by emulate_load_store_insn().

Please bare with me because this is not my area, but this does not look the correct way to fix this problem to me.

The R5900 is a MIPS-III, so I don't understand why we have to care about RDHWR.

IIRC MIPS TLS support has been added in glibc with MIPS32 ISA in mind in 2005:
https://sourceware.org/git/?p=glibc.git;a=commit;h=f850220be6

MIPS16 is handled differently:
https://sourceware.org/git/?p=glibc.git;a=blobdiff;f=ports/sysdeps/mips/tls-macros.h;h=3e87e42ea;hp=8fe2e4a150d;hb=43301bd3c2;hpb=85bd816a60

Shouldn't we have a similar set of macros for the R5900?

The problem I have is this break running the binaries built by the Sony Linux Toolkit for Playstation 2 which use the LQ/SQ instruction:

 1b00d5c:       0c6c00d4        jal     0x1b00350
 1b00d60:       7fa90020        sq      t1,32(sp)
 1b00d64:       a2710000        sb      s1,0(s3)
 1b00d68:       26730001        addiu   s3,s3,1
 1b00d6c:       10000004        b       0x1b00d80
 1b00d70:       7ba90020        lq      t1,32(sp)
 1b00d74:       00000000        nop
 1b00d78:       2617ffff        addiu   s7,s0,-1
 1b00d7c:       25360001        addiu   s6,t1,1
 1b00d80:       91220000        lbu     v0,0(t1)
 1b00d84:       02e0802d        move    s0,s7
 1b00d88:       02c0482d        move    t1,s6
 1b00d8c:       a2620000        sb      v0,0(s3)
 1b00d90:       1600fff9        bnez    s0,0x1b00d78
 1b00d94:       26730001        addiu   s3,s3,1
 1b00d98:       10000003        b       0x1b00da8
 1b00d9c:       01342026        xor     a0,t1,s4
 1b00da0:       253e0003        addiu   s8,t1,3
 1b00da4:       01342026        xor     a0,t1,s4
 1b00da8:       7fa90020        sq      t1,32(sp)
 1b00dac:       2c840001        sltiu   a0,a0,1
 1b00db0:       0c6c00d4        jal     0x1b00350

What do you think?

Regards,

Phil.

Quoted 101 lines

CONFIG_DEFAULT_MMAP_MIN_ADDR must not be less than PAGE_SIZE to reliably trap and emulate RDHWR, so this is made a BUILD_BUG_ON for the R5900.

References:

[1] "TX System RISC TX79 Core Architecture" manual, revision 2.0,

    Toshiba Corporation, p. B-162, https://wiki.qemu.org/File:C790.pdf
---
 arch/mips/include/asm/traps.h |  2 ++
 arch/mips/kernel/traps.c      |  2 +-
 arch/mips/kernel/unaligned.c  | 36 ++++++++++++++++++++++++++++++++++-
 3 files changed, 38 insertions(+), 2 deletions(-)
arch/mips/include/asm/traps.h +2 -0
index 6a0864bb604d..ed6449f2967b 100644
@@ -35,4 +35,6 @@ extern int register_nmi_notifier(struct notifier_block *nb);
 	register_nmi_notifier(&fn##_nb);				\
 })
 
+int simulate_rdhwr(struct pt_regs *regs, int rd, int rt);
+
 #endif /* _ASM_TRAPS_H */
arch/mips/kernel/traps.c +1 -1
index 342e41de9d64..9423b9a2eb67 100644
@@ -625,7 +625,7 @@ static int simulate_llsc(struct pt_regs *regs, unsigned int opcode)
  * Simulate trapping 'rdhwr' instructions to provide user accessible
  * registers not implemented in hardware.
  */
-static int simulate_rdhwr(struct pt_regs *regs, int rd, int rt)
+int simulate_rdhwr(struct pt_regs *regs, int rd, int rt)
 {
 	struct thread_info *ti = task_thread_info(current);
 
arch/mips/kernel/unaligned.c +35 -1
index 92bd2b0f0548..f490944d73cf 100644
@@ -90,6 +90,7 @@
 #include <asm/fpu_emulator.h>
 #include <asm/inst.h>
 #include <asm/mmu_context.h>
+#include <asm/traps.h>
 #include <linux/uaccess.h>
 
 #define STR(x)	__STR(x)
@@ -934,7 +935,39 @@ static void emulate_load_store_insn(struct pt_regs *regs,
 		 * interest.
 		 */
 	case spec3_op:
-		if (insn.dsp_format.func == lx_op) {
+		if (IS_ENABLED(CONFIG_CPU_R5900)) {
+			/*
+			 * On the R5900, the RDHWR instruction
+			 *
+			 *     +--------+-------+----+----+-------+--------+
+			 *     | 011111 | 00000 | rt | rd | 00000 | 111011 |
+			 *     +--------+-------+----+----+-------+--------+
+			 *          6       5      5    5     5        6
+			 *
+			 * is interpreted as the R5900 specific SQ instruction
+			 *
+			 *     +--------+-------+----+---------------------+
+			 *     | 011111 |  base | rt |        offset       |
+			 *     +--------+-------+----+---------------------+
+			 *          6       5      5            16
+			 *
+			 * that asserts a zero page address exception. Hence
+			 * RDHWR can be trapped and emulated here, provided
+			 * DEFAULT_MMAP_MIN_ADDR isn't zero.
+			 */
+			BUILD_BUG_ON(IS_ENABLED(CONFIG_CPU_R5900) &&
+				CONFIG_DEFAULT_MMAP_MIN_ADDR < PAGE_SIZE);
+			if (insn.r_format.func == rdhwr_op &&
+			    insn.r_format.rs == 0 &&
+			    insn.r_format.re == 0) {
+				if (compute_return_epc(regs) < 0 ||
+				    simulate_rdhwr(regs, insn.r_format.rd,
+						   insn.r_format.rt) < 0)
+					goto sigill;
+				return;
+			}
+			goto sigbus;
+		} else if (insn.dsp_format.func == lx_op) {
 			switch (insn.dsp_format.op) {
 			case lwx_op:
 				if (!access_ok(addr, 4))
@@ -1342,6 +1375,7 @@ static void emulate_load_store_insn(struct pt_regs *regs,
 		cu2_notifier_call_chain(CU2_SDC2_OP, regs);
 		break;
 #endif
+
 	default:
 		/*
 		 * Pheeee...  We encountered an yet unknown instruction or
#7 lore raw

Re: [PATCH 002/120] MIPS: R5900: Trap the RDHWR instruction as an SQ address exception

Quoted 9 lines · Philippe Mathieu-Daudé
Quoted 3 lines

On the R5900, the RDHWR instruction is interpreted as the R5900 specific SQ instruction[1] that traps into a zero page address exception. Hence RDHWR can be emulated by emulate_load_store_insn().

Please bare with me because this is not my area, but this does not look the correct way to fix this problem to me.

The R5900 is a MIPS-III, so I don't understand why we have to care about RDHWR.

The use of RDHWR, actual or emulated, is a part of the MIPS TLS psABI,
see: <https://www.linux-mips.org/wiki/NPTL>, in particular starting from:
<https://www.linux-mips.org/wiki/NPTL#Design_Choices> and throughout (the expired certificate of the web site is a known issue, but there is currently no way to get it fixed as nobody knows where Ralf Baechle has gone).

Quoted 8 lines

IIRC MIPS TLS support has been added in glibc with MIPS32 ISA in mind in 2005:
https://sourceware.org/git/?p=glibc.git;a=commit;h=f850220be6

MIPS16 is handled differently:
https://sourceware.org/git/?p=glibc.git;a=blobdiff;f=ports/sysdeps/mips/tls-macros.h;h=3e87e42ea;hp=8fe2e4a150d;hb=43301bd3c2;hpb=85bd816a60

Shouldn't we have a similar set of macros for the R5900?

The macros are there to support different instructions used to access
data within TLS. However the TLS pointer still has to be fetched with RDHWR, and READ_THREAD_POINTER does exactly that, by calling the `__builtin_thread_pointer' intrinsic in the MIPS16 case. How it works is explained in GCC sources, in gcc/config/mips/mips.md:

;; In MIPS16 mode, the TLS base pointer is accessed by a ;; libgcc helper function __mips16_rdhwr(), as 'rdhwr' is not ;; accessible in MIPS16.
;;
;; This is not represented as a call insn, to avoid the ;; unnecesarry clobbering of caller-save registers by a ;; function consisting only of: "rdhwr $3,$29; j $31; nop;" ;;
;; A $25 clobber is added to cater for a $25 load stub added by the ;; linker to __mips16_rdhwr when the call is made from non-PIC code.

-- that is a small piece of regular MIPS code is called, which executes RDHWR.

Quoted 18 lines

The problem I have is this break running the binaries built by the Sony Linux Toolkit for Playstation 2 which use the LQ/SQ instruction:

 1b00d5c:       0c6c00d4        jal     0x1b00350
 1b00d60:       7fa90020        sq      t1,32(sp)
 1b00d64:       a2710000        sb      s1,0(s3)
 1b00d68:       26730001        addiu   s3,s3,1
 1b00d6c:       10000004        b       0x1b00d80
 1b00d70:       7ba90020        lq      t1,32(sp)
 1b00d74:       00000000        nop
 1b00d78:       2617ffff        addiu   s7,s0,-1
 1b00d7c:       25360001        addiu   s6,t1,1
 1b00d80:       91220000        lbu     v0,0(t1)
 1b00d84:       02e0802d        move    s0,s7
 1b00d88:       02c0482d        move    t1,s6
 1b00d8c:       a2620000        sb      v0,0(s3)
 1b00d90:       1600fff9        bnez    s0,0x1b00d78
Quoted 8 lines
 1b00d94:       26730001        addiu   s3,s3,1
 1b00d98:       10000003        b       0x1b00da8
 1b00d9c:       01342026        xor     a0,t1,s4
 1b00da0:       253e0003        addiu   s8,t1,3
 1b00da4:       01342026        xor     a0,t1,s4
 1b00da8:       7fa90020        sq      t1,32(sp)
 1b00dac:       2c840001        sltiu   a0,a0,1
 1b00db0:       0c6c00d4        jal     0x1b00350

Where exactly do you see a problem, what are the symptoms? A bug in the
emulation cannot be ruled out of course.

While there indeed is an unfortunate opcode overlap between RDHWR and SQ,
the encoding of the specific operands chosen for TLS pointer access is luckily guaranteed to always trap in the user mode, because the address requested when the encoding is interpreted as SQ rather than RDHWR is within the kernel KSEG2 segment:

$ cat rdhwr.s
	rdhwr	$3, $29
$ gcc -march=mips32r2 -c rdhwr.s
$ objdump -d rdhwr.o
rdhwr.o:     file format elf32-tradlittlemips

Disassembly of section .text:

00000000 <.text>:
0: 7c03e83b rdhwr v1,$29

	...
$ objdump -m mips:5900 -d rdhwr.o

rdhwr.o: file format elf32-tradlittlemips

Disassembly of section .text:

00000000 <.text>:
0: 7c03e83b sq v1,-6085(zero)

	...

$

This is because the HWR read is encoded in bits 15:11 of the instruction word and $29 has the highest bit set, which lands in bit 15 of the instruction word, meaning that the offset encoded in bits 15:0 used where the instruction word is interpreted as SQ is negative. And then bits 25:21 are hardwired to 0 in the encoding of RDHWR and they correspond to the base register encoding with SQ, meaning that it will be interpreted as $zero. So the address ultimately requested is -6085 => 0xffffffffffffe83b (the R5900 uses 32-bit addressing and ignores address bits 63:32, but that does not matter here; this is KSEG2 either way).

Consequently an AdES exception is triggered which we can trap and handle,
reinterpreting the encoding according to our needs and return the TLS pointer in $v1 rather than issuing a SIGBUS. So you are not expected to see any issue unless there is a security erratum in the R5900 as well and the encoding does not cause an exception for some reason.

This of course disagrees with what Fredrik wrote in the quotation above,
as it's the last page rather than the zeroth that is accessed, but the net effect is the same, or even better.

Maciej

#8 lore raw

Re: [PATCH 002/120] MIPS: R5900: Trap the RDHWR instruction as an SQ address exception

Quoted 3 lines · Maciej W. Rozycki

Consequently an AdES exception is triggered which we can trap and handle,
reinterpreting the encoding according to our needs and return the TLS pointer in $v1 rather than issuing a SIGBUS.

... while keeping regular SQ instruction use intact, I should have added.

Maciej

#9 lore raw

Re: [PATCH 002/120] MIPS: R5900: Trap the RDHWR instruction as an SQ address exception

In reply to Maciej W. Rozycki

Hi Maciej,

Quoted 6 lines · Maciej W. Rozycki

The use of RDHWR, actual or emulated, is a part of the MIPS TLS psABI,
see: <https://www.linux-mips.org/wiki/NPTL>, in particular starting from:
<https://www.linux-mips.org/wiki/NPTL#Design_Choices> and throughout (the expired certificate of the web site is a known issue, but there is currently no way to get it fixed as nobody knows where Ralf Baechle has gone).

Can the site be wrapped up and published elsewhere?

Quoted 46 lines

While there indeed is an unfortunate opcode overlap between RDHWR and SQ,
the encoding of the specific operands chosen for TLS pointer access is luckily guaranteed to always trap in the user mode, because the address requested when the encoding is interpreted as SQ rather than RDHWR is within the kernel KSEG2 segment:

$ cat rdhwr.s
	rdhwr	$3, $29
$ gcc -march=mips32r2 -c rdhwr.s
$ objdump -d rdhwr.o
rdhwr.o:     file format elf32-tradlittlemips

Disassembly of section .text:

00000000 <.text>:

   0:	7c03e83b 	rdhwr	v1,$29
	...
$ objdump -m mips:5900 -d rdhwr.o

rdhwr.o: file format elf32-tradlittlemips

Disassembly of section .text:

00000000 <.text>:

   0:	7c03e83b 	sq	v1,-6085(zero)
	...

$

This is because the HWR read is encoded in bits 15:11 of the instruction word and $29 has the highest bit set, which lands in bit 15 of the instruction word, meaning that the offset encoded in bits 15:0 used where the instruction word is interpreted as SQ is negative. And then bits 25:21 are hardwired to 0 in the encoding of RDHWR and they correspond to the base register encoding with SQ, meaning that it will be interpreted as $zero. So the address ultimately requested is -6085 => 0xffffffffffffe83b (the R5900 uses 32-bit addressing and ignores address bits 63:32, but that does not matter here; this is KSEG2 either way).

Consequently an AdES exception is triggered which we can trap and handle,
reinterpreting the encoding according to our needs and return the TLS pointer in $v1 rather than issuing a SIGBUS. So you are not expected to see any issue unless there is a security erratum in the R5900 as well and the encoding does not cause an exception for some reason.

Quoted 3 lines

This of course disagrees with what Fredrik wrote in the quotation above,
as it's the last page rather than the zeroth that is accessed, but the net effect is the same, or even better.

The first page could be mapped by the application itself, but what about RDHWR registers $0-$3 having mnemonics CPUNum, SYNC1_Step, CC and CCRes[1], or in Linux

/* RDHWR register numbers */
#define MIPS_HWR_CPUNUM 0 /* CPU number */
#define MIPS_HWR_SYNCISTEP 1 /* SYNCI step size */
#define MIPS_HWR_CC 2 /* Cycle counter */
#define MIPS_HWR_CCRES 3 /* Cycle counter resolution */
#define MIPS_HWR_ULR 29 /* UserLocal */
#define MIPS_HWR_IMPL1 30 /* Implementation dependent */
#define MIPS_HWR_IMPL2 31 /* Implementation dependent */

in arch/mips/include/asm/mipsregs.h? They land on the first page, no?

Fredrik

References:

[1] "MIPS Architecture for programmers Volume II-A: The MIPS32 Instruction

    Set", revision 5.04, 11 December 2013, p. 248.
#10 lore raw

Re: [PATCH 002/120] MIPS: R5900: Trap the RDHWR instruction as an SQ address exception

Quoted 8 lines · Fredrik Noring
Quoted 6 lines

The use of RDHWR, actual or emulated, is a part of the MIPS TLS psABI,
see: <https://www.linux-mips.org/wiki/NPTL>, in particular starting from:
<https://www.linux-mips.org/wiki/NPTL#Design_Choices> and throughout (the expired certificate of the web site is a known issue, but there is currently no way to get it fixed as nobody knows where Ralf Baechle has gone).

Can the site be wrapped up and published elsewhere?

Well if someone does that, sure! I guess archive.org will have it too.

Quoted 5 lines
Quoted 3 lines

This of course disagrees with what Fredrik wrote in the quotation above,
as it's the last page rather than the zeroth that is accessed, but the net effect is the same, or even better.

The first page could be mapped by the application itself, but what about

It doesn't matter given that `rdhwr $3, $29' maps to an R5900 instruction
that does not access it.

Quoted 12 lines

RDHWR registers $0-$3 having mnemonics CPUNum, SYNC1_Step, CC and CCRes[1], or in Linux

/* RDHWR register numbers */
#define MIPS_HWR_CPUNUM 0 /* CPU number */
#define MIPS_HWR_SYNCISTEP 1 /* SYNCI step size */
#define MIPS_HWR_CC 2 /* Cycle counter */
#define MIPS_HWR_CCRES 3 /* Cycle counter resolution */
#define MIPS_HWR_ULR 29 /* UserLocal */
#define MIPS_HWR_IMPL1 30 /* Implementation dependent */
#define MIPS_HWR_IMPL2 31 /* Implementation dependent */

Quoted 1 line

in arch/mips/include/asm/mipsregs.h? They land on the first page, no?

Unlike TLS pointer access, specifically using $3 as rt, which has been
made a part of the Linux ABI, they're not supposed to be referred with pre-R2 code. After all RDHWR was only introduced with R2.

Indeed there's emulation code in the kernel for those encodings even with
pre-R2 hardware, but it is not guaranteed to give sensible results, and given the circumstances I'm not sure what it really is for, e.g. what is SYNCI_Step for with processors that do not implement SYNCI? The cycle counter register may be absent too, so even emulated accesses will return rubbish; gettimeofday(2) is the standard interface.

So I think we can safely ignore them, just as we can any ULR access with
rt != $3. Unlike standardised TLS pointer accesses such instructions won't appear in compiler-generated code and whoever uses them in handcoded assembly or otherwise generated machine code will have to make sure they make sense for their application (yes, there's rubbish code out there, e.g. Firefox has a JIT that unconditionally produces MIPS R2 code even if the piece of software has been compiled for an older ISA revision, and having not verified that the CPU it runs on actually supports the R2 ISA, but that's just the usual imperfection of the world; you just can't fix it all).

Maciej

#11 lore raw

Re: [PATCH 002/120] MIPS: R5900: Trap the RDHWR instruction as an SQ address exception

Quoted 11 lines · Maciej W. Rozycki

Unlike TLS pointer access, specifically using $3 as rt, which has been
made a part of the Linux ABI, they're not supposed to be referred with pre-R2 code. After all RDHWR was only introduced with R2.

Indeed there's emulation code in the kernel for those encodings even with
pre-R2 hardware, but it is not guaranteed to give sensible results, and given the circumstances I'm not sure what it really is for, e.g. what is SYNCI_Step for with processors that do not implement SYNCI? The cycle counter register may be absent too, so even emulated accesses will return rubbish; gettimeofday(2) is the standard interface.

Quoted 10 lines

So I think we can safely ignore them, just as we can any ULR access with
rt != $3. Unlike standardised TLS pointer accesses such instructions won't appear in compiler-generated code and whoever uses them in handcoded assembly or otherwise generated machine code will have to make sure they make sense for their application (yes, there's rubbish code out there, e.g. Firefox has a JIT that unconditionally produces MIPS R2 code even if the piece of software has been compiled for an older ISA revision, and having not verified that the CPU it runs on actually supports the R2 ISA, but that's just the usual imperfection of the world; you just can't fix it all).

Many thanks, that resolves it! I'll write it up in the commit message.

Fredrik

#12 lore raw

Re: [PATCH 002/120] MIPS: R5900: Trap the RDHWR instruction as an SQ address exception

In reply to Maciej W. Rozycki
Quoted 2 lines

So I think we can safely ignore them, just as we can any ULR access with
rt != $3.

The comment is corrected and the conditions on rd and rt are now strict, as shown in the patch below.

Fredrik

arch/mips/kernel/unaligned.c +41 -1
index 92bd2b0f0548..89ce42c60c6f 100644
@@ -90,6 +90,7 @@
 #include <asm/fpu_emulator.h>
 #include <asm/inst.h>
 #include <asm/mmu_context.h>
+#include <asm/traps.h>
 #include <linux/uaccess.h>
 
 #define STR(x)	__STR(x)
@@ -934,7 +935,46 @@ static void emulate_load_store_insn(struct pt_regs *regs,
 		 * interest.
 		 */
 	case spec3_op:
-		if (insn.dsp_format.func == lx_op) {
+		if (IS_ENABLED(CONFIG_CPU_R5900)) {
+			/*
+			 * On the R5900, a valid RDHWR instruction
+			 *
+			 *     +--------+-------+----+----+-------+--------+
+			 *     | 011111 | 00000 | rt | rd | 00000 | 111011 |
+			 *     +--------+-------+----+----+-------+--------+
+			 *          6       5      5    5     5        6
+			 *
+			 * having rt $3 (v1) and rd $29 (MIPS_HWR_ULR) is
+			 * interpreted as the R5900 specific SQ instruction
+			 *
+			 *     +--------+-------+----+---------------------+
+			 *     | 011111 |  base | rt |        offset       |
+			 *     +--------+-------+----+---------------------+
+			 *          6       5      5            16
+			 *
+			 * with
+			 *
+			 *     sq v1,-6085(zero)
+			 *
+			 * that asserts an address exception since -6085(zero)
+			 * always resolves to 0xffffe83b in 32-bit KSEG2.
+			 *
+			 * Other legacy values of rd, such as MIPS_HWR_CPUNUM,
+			 * are ignored.
+			 */
+			if (insn.r_format.func == rdhwr_op &&
+			    insn.r_format.rd == MIPS_HWR_ULR &&
+			    insn.r_format.rt == 3 &&
+			    insn.r_format.rs == 0 &&
+			    insn.r_format.re == 0) {
+				if (compute_return_epc(regs) < 0 ||
+				    simulate_rdhwr(regs, insn.r_format.rd,
+						   insn.r_format.rt) < 0)
+					goto sigill;
+				return;
+			}
+			goto sigbus;
+		} else if (insn.dsp_format.func == lx_op) {
 			switch (insn.dsp_format.op) {
 			case lwx_op:
 				if (!access_ok(addr, 4))
#13 lore raw

PATCH 3/120 MIPS: R5900: Sign-extend o32 system call registers

In reply to Fredrik Noring

The R5900 has 64-bit instructions but does not implement CP0.Status.UX so a 32-bit kernel cannot assume o32 registers are sign-extended[1].

CP0.Status.{UX,SX,KX} are read-only and always read as zero.

CP0.Status.PX (bit 23) is called CP0.Status.DEV and controls the location of performance counter and debug/SIO exception vectors.

In other MIPS ISA implementations, attempting to execute 64-bit operations in 32-bit user or supervisor mode may cause the reserved instruction exception. In the Emotion Engine core, however, 64-bit operations are always valid, regardless of the operation mode[2].

References:

[1] "TX System RISC TX79 Core Architecture" manual, revision 2.0,

    Toshiba Corporation, p. 4-16, https://wiki.qemu.org/File:C790.pdf

[2] "EE Core User's Manual", version 6.0, Sony Computer Entertainment Inc.,

    p. 107.
---
 arch/mips/kernel/scall32-o32.S | 12 ++++++++++++
 1 file changed, 12 insertions(+)
arch/mips/kernel/scall32-o32.S +12 -0
index d9434cd0f568..b5ad6798978c 100644
@@ -25,6 +25,18 @@
 	.align	5
 NESTED(handle_sys, PT_SIZE, sp)
 	.set	noat
+#ifdef CONFIG_CPU_R5900
+	/*
+	 * We don't want to stumble over broken sign extensions from
+	 * userland. O32 does never use the upper half, but since the
+	 * R5900 does not implement CP0.Status.UX it cannot enforce it.
+	 */
+	sll	v0, v0, 0
+	sll	a0, a0, 0
+	sll	a1, a1, 0
+	sll	a2, a2, 0
+	sll	a3, a3, 0
+#endif
 	SAVE_SOME
 	TRACE_IRQS_ON_RELOAD
 	STI
Signature · 1 line
2.21.0
#14 lore raw

PATCH 4/120 MIPS: R5900: Reset bits 127..64 of GPRs in RESTORE_SOME

In reply to Fredrik Noring

Bits 127..64 are not used by the kernel but can be modified by applications using the R5900 specific multimedia instructions. Clearing them in RESTORE_SOME prevents leaking information between processes. This is a provisional measure until full 128-bit registers are saved/restored, possibly using SQ/LQ.

---
As mentioned in the cover letter, I think it would be much better to save and restore 128-bit GPRs here.

---
 arch/mips/include/asm/stackframe.h | 53 ++++++++++++++++++++++++++++++
 1 file changed, 53 insertions(+)
arch/mips/include/asm/stackframe.h +53 -0
index 4d6ad907ae54..aaed9b522220 100644
@@ -349,6 +349,58 @@
 		cfi_ld	sp, PT_R29, \docfi
 		.endm
 
+#ifdef CONFIG_CPU_R5900
+		/*
+		 * Reset bits 127..64 of 128-bit multimedia registers.
+		 *
+		 * Bits 127..64 are not used by the kernel but can be modified
+		 * by applications using the R5900 specific multimedia
+		 * instructions. Clearing them prevents leaking information
+		 * between processes. This is a provisional measure until full
+		 * 128-bit registers are saved/restored, possibly using SQ/LQ.
+		 */
+		.macro	RESET_MMR
+		.set	push
+		.set	noreorder
+		.set	noat
+		pcpyld	$1, $0, $1
+		pcpyld	$2, $0, $2
+		pcpyld	$3, $0, $3
+		pcpyld	$4, $0, $4
+		pcpyld	$5, $0, $5
+		pcpyld	$6, $0, $6
+		pcpyld	$7, $0, $7
+		pcpyld	$8, $0, $8
+		pcpyld	$9, $0, $9
+		pcpyld	$10, $0, $10
+		pcpyld	$11, $0, $11
+		pcpyld	$12, $0, $12
+		pcpyld	$13, $0, $13
+		pcpyld	$14, $0, $14
+		pcpyld	$15, $0, $15
+		pcpyld	$16, $0, $16
+		pcpyld	$17, $0, $17
+		pcpyld	$18, $0, $18
+		pcpyld	$19, $0, $19
+		pcpyld	$20, $0, $20
+		pcpyld	$21, $0, $21
+		pcpyld	$22, $0, $22
+		pcpyld	$23, $0, $23
+		pcpyld	$24, $0, $24
+		pcpyld	$25, $0, $25
+		pcpyld	$26, $0, $26
+		pcpyld	$27, $0, $27
+		pcpyld	$28, $0, $28
+		pcpyld	$29, $0, $29
+		pcpyld	$30, $0, $30
+		pcpyld	$31, $0, $31
+		.set	pop
+		.endm
+#else
+		.macro	RESET_MMR
+		.endm
+#endif
+
 #if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
 
 		.macro	RESTORE_SOME docfi=0
@@ -393,6 +445,7 @@
 		.set	push
 		.set	reorder
 		.set	noat
+		RESET_MMR
 		mfc0	a0, CP0_STATUS
 		ori	a0, STATMASK
 		xori	a0, STATMASK
Signature · 1 line
2.21.0
#15 lore raw

PATCH 5/120 MIPS: R5900: Reset the funnel shift amount (SA) register in RESTORE_SOME

In reply to Fredrik Noring

The shift amount (SA) register is a 64-bit special register storing the funnel shift amount[1]. It is used by the QFSRV (quadword funnel shift right variable) 256-bit multimedia instruction. This is a provisional measure until the SA register is saved/restored properly.

The R5900 specific MTSAB (move byte count to shift amount register) instruction is used to reset the SA register.

References:

[1] "TX System RISC TX79 Core Architecture" manual, revision 2.0,

    Toshiba Corporation, p. B-161, https://wiki.qemu.org/File:C790.pdf

---
The shift amount (SA) register ought to be saved and restored properly too, along with the 128-bit GPRs, I think.

---
 arch/mips/include/asm/stackframe.h | 1 +
 1 file changed, 1 insertion(+)
arch/mips/include/asm/stackframe.h +1 -0
index aaed9b522220..2fbead2e86d1 100644
@@ -394,6 +394,7 @@
 		pcpyld	$29, $0, $29
 		pcpyld	$30, $0, $30
 		pcpyld	$31, $0, $31
+		mtsab	$0, 0 /* Reset the funnel shift (SA) register. */
 		.set	pop
 		.endm
 #else
Signature · 1 line
2.21.0
#16 lore raw

PATCH 6/120 MIPS: R5900: Workaround for the short loop bug

In reply to Fredrik Noring

The short loop bug under certain conditions causes loops to execute only once or twice. The Gnu assembler (GAS) has the following note about it:

On the R5900 short loops need to be fixed by inserting a NOP in the branch delay slot.

The short loop bug under certain conditions causes loops to execute only once or twice. We must ensure that the assembler never generates loops that satisfy all of the following conditions:

- a loop consists of less than or equal to six instructions (including the branch delay slot);
- a loop contains only one conditional branch instruction at the end of the loop;
- a loop does not contain any other branch or jump instructions;
- a branch delay slot of the loop is not NOP (EE 2.9 or later).

We need to do this because of a hardware bug in the R5900 chip.

GAS handles the short loop bug in most cases. However, GAS is unable to adjust machine code having the noreorder directive, as used by the kernel on several occasions.

The short loop bug also affects user space code, which is why generic MIPS code cannot execute unadjusted on the R5900. The GAS and GCC option -mfix-r5900 is recommended for such cases.

---
 arch/mips/boot/compressed/head.S |  7 +++---
 arch/mips/include/asm/asmmacro.h | 41 ++++++++++++++++++++++++++++++++
 arch/mips/include/asm/string.h   |  3 +++
 arch/mips/lib/delay.c            |  7 +++---
 arch/mips/lib/memset.S           |  2 ++
 5 files changed, 52 insertions(+), 8 deletions(-)
arch/mips/boot/compressed/head.S +3 -4
index 409cb483a9ff..e3dc831e2616 100644
@@ -15,7 +15,6 @@
 #include <asm/asm.h>
 #include <asm/regdef.h>
 
-	.set noreorder
 	.cprestore
 	LEAF(start)
 start:
@@ -26,11 +25,11 @@ start:
 	move	s3, a3
 
 	/* Clear BSS */
-	PTR_LA	a0, _edata
+	PTR_LA	a0, _edata - 4
 	PTR_LA	a2, _end
-1:	sw	zero, 0(a0)
+1:	addiu	a0, a0, 4
+	sw	zero, 0(a0)
 	bne	a2, a0, 1b
-	 addiu	a0, a0, 4
 
 	PTR_LA	a0, (.heap)	     /* heap address */
 	PTR_LA	sp, (.stack + 8192)  /* stack address */
arch/mips/include/asm/asmmacro.h +41 -0

45 lines not on this page · raw message

arch/mips/include/asm/string.h +3 -0
index 29030cb398ee..35c9dc3815f4 100644
@@ -10,6 +10,9 @@
 #ifndef _ASM_STRING_H
 #define _ASM_STRING_H
 
+#if defined(CONFIG_CPU_R5900)
+#define IN_STRING_C
+#endif
 
 /*
  * Most of the inline functions are rather naive implementations so I just
arch/mips/lib/delay.c +3 -4
index 68c495ed71e3..0b370b831708 100644
@@ -27,11 +27,10 @@
 void __delay(unsigned long loops)
 {
 	__asm__ __volatile__ (
-	"	.set	noreorder				\n"
 	"	.align	3					\n"
-	"1:	bnez	%0, 1b					\n"
-	"	 " __stringify(LONG_SUBU) "	%0, %1		\n"
-	"	.set	reorder					\n"
+	"	" __stringify(LONG_ADDU) "	%0, %1		\n"
+	"1:	" __stringify(LONG_SUBU) "	%0, %1		\n"
+	"	bnez	%0, 1b					\n"
 	: "=r" (loops)
 	: GCC_DADDI_IMM_ASM() (1), "0" (loops));
 }
arch/mips/lib/memset.S +2 -0
index 418611ef13cf..70db395159f1 100644
@@ -9,6 +9,7 @@
  * Copyright (C) 2011, 2012 MIPS Technologies, Inc.
  */
 #include <asm/asm.h>
+#include <asm/asmmacro.h>
 #include <asm/asm-offsets.h>
 #include <asm/export.h>
 #include <asm/regdef.h>
@@ -222,6 +223,7 @@
 1:	PTR_ADDIU	a0, 1			/* fill bytewise */
 	R10KCBARRIER(0(ra))
 	.set		noreorder
+	short_loop_war(1b)
 	bne		t1, a0, 1b
 	 EX(sb, a1, -1(a0), .Lsmall_fixup\@)
 	.set		reorder
Signature · 1 line
2.21.0
#17 lore raw

PATCH 7/120 MIPS: R5900: Add the SYNC.P instruction

In reply to Fredrik Noring

The SYNC.P instruction is a pipeline barrier. All instructions prior to the barrier are completed before the instructions following the barrier are fetched[1].

However, the barrier operation doesn't wait for any prior instructions to retire, for example multiply, divide, multicycle COP1 operations or a pending load issued before the barrier operation.

References:

[1] "TX System RISC TX79 Core Architecture" manual, revision 2.0,

    Toshiba Corporation, p. A-125, https://wiki.qemu.org/File:C790.pdf
---
 arch/mips/include/asm/uasm.h | 1 +
 arch/mips/mm/uasm-mips.c     | 1 +
 arch/mips/mm/uasm.c          | 5 +++--
 3 files changed, 5 insertions(+), 2 deletions(-)
arch/mips/include/asm/uasm.h +1 -0
index f7effca791a5..71ddf155ef85 100644
@@ -171,6 +171,7 @@ Ip_u3u2u1(_srlv);
 Ip_u3u1u2(_subu);
 Ip_u2s3u1(_sw);
 Ip_u1(_sync);
+Ip_0(_syncp);
 Ip_u1(_syscall);
 Ip_0(_tlbp);
 Ip_0(_tlbr);
arch/mips/mm/uasm-mips.c +1 -0
index 7154a1d99aad..725c6fe1e317 100644
@@ -191,6 +191,7 @@ static const struct insn insn_table[insn_invalid] = {
 	[insn_subu]	= {M(spec_op, 0, 0, 0, 0, subu_op),	RS | RT | RD},
 	[insn_sw]	= {M(sw_op, 0, 0, 0, 0, 0),  RS | RT | SIMM},
 	[insn_sync]	= {M(spec_op, 0, 0, 0, 0, sync_op), RE},
+	[insn_syncp]	= {M(spec_op, 0, 0, 0, 0x10, sync_op), 0},
 	[insn_syscall]	= {M(spec_op, 0, 0, 0, 0, syscall_op), SCIMM},
 	[insn_tlbp]	= {M(cop0_op, cop_op, 0, 0, 0, tlbp_op),  0},
 	[insn_tlbr]	= {M(cop0_op, cop_op, 0, 0, 0, tlbr_op),  0},
arch/mips/mm/uasm.c +3 -2
index c56f129c9a4b..32c7a5827ba8 100644
@@ -64,8 +64,8 @@ enum opcode {
 	insn_scd, insn_seleqz, insn_selnez, insn_sd, insn_sh, insn_sll,
 	insn_sllv, insn_slt, insn_slti, insn_sltiu, insn_sltu, insn_sra,
 	insn_srav, insn_srl, insn_srlv, insn_subu, insn_sw, insn_sync,
-	insn_syscall, insn_tlbp, insn_tlbr, insn_tlbwi, insn_tlbwr, insn_wait,
-	insn_wsbh, insn_xor, insn_xori, insn_yield,
+	insn_syncp, insn_syscall, insn_tlbp, insn_tlbr, insn_tlbwi, insn_tlbwr,
+	insn_wait, insn_wsbh, insn_xor, insn_xori, insn_yield,
 	insn_invalid /* insn_invalid must be last */
 };
 
@@ -369,6 +369,7 @@ I_u2u1u3(_rotr)
 I_u3u1u2(_subu)
 I_u2s3u1(_sw)
 I_u1(_sync)
+I_0(_syncp)
 I_0(_tlbp)
 I_0(_tlbr)
 I_0(_tlbwi)
Signature · 1 line
2.21.0
#18 lore raw

PATCH 8/120 MIPS: R5900: Add implicit SYNC.P to the UASM_i_M[FT]C0 macros

In reply to Fredrik Noring

The Toshiba TX79 manual specifies that all MTC0 instructions must be followed by a SYNC.P instruction as a barrier to guarantee COP0 register updates[1]. There is one exception to this rule:

An MTC0 instruction which loads the EntryHi COP0 register can be followed by a TLBWI or a TLBWR instruction without having an intervening SYNC.P. This special case is handled by a hardware interlock.

References:

[1] "TX System RISC TX79 Core Architecture" manual, revision 2.0,

    Toshiba Corporation, p. C-28, https://wiki.qemu.org/File:C790.pdf

---
The Linux 2.6 port to the PlayStation 2 has SYNC.P preceding all MFC0 instructions, but I have not found any documentation stating that is necessary. Perhaps this case can be removed?

---
 arch/mips/include/asm/uasm.h | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)
arch/mips/include/asm/uasm.h +22 -0
index 71ddf155ef85..34b61fb53da5 100644
@@ -214,8 +214,19 @@ static inline void uasm_l##lb(struct uasm_label **lab, u32 *addr)	\
 # define UASM_i_LL(buf, rs, rt, off) uasm_i_lld(buf, rs, rt, off)
 # define UASM_i_LW(buf, rs, rt, off) uasm_i_ld(buf, rs, rt, off)
 # define UASM_i_LWX(buf, rs, rt, rd) uasm_i_ldx(buf, rs, rt, rd)
+#ifndef CONFIG_CPU_R5900
 # define UASM_i_MFC0(buf, rt, rd...) uasm_i_dmfc0(buf, rt, rd)
 # define UASM_i_MTC0(buf, rt, rd...) uasm_i_dmtc0(buf, rt, rd)
+#else
+# define UASM_i_MFC0(buf, rt, rd...) do { \
+		uasm_i_syncp(buf); \
+		uasm_i_mfc0(buf, rt, rd); \
+	} while(0)
+# define UASM_i_MTC0(buf, rt, rd...) do { \
+		uasm_i_mtc0(buf, rt, rd); \
+		uasm_i_syncp(buf); \
+	} while(0)
+#endif
 # define UASM_i_ROTR(buf, rs, rt, sh) uasm_i_drotr(buf, rs, rt, sh)
 # define UASM_i_SC(buf, rs, rt, off) uasm_i_scd(buf, rs, rt, off)
 # define UASM_i_SLL(buf, rs, rt, sh) uasm_i_dsll(buf, rs, rt, sh)
@@ -230,8 +241,19 @@ static inline void uasm_l##lb(struct uasm_label **lab, u32 *addr)	\
 # define UASM_i_LL(buf, rs, rt, off) uasm_i_ll(buf, rs, rt, off)
 # define UASM_i_LW(buf, rs, rt, off) uasm_i_lw(buf, rs, rt, off)
 # define UASM_i_LWX(buf, rs, rt, rd) uasm_i_lwx(buf, rs, rt, rd)
+#ifndef CONFIG_CPU_R5900
 # define UASM_i_MFC0(buf, rt, rd...) uasm_i_mfc0(buf, rt, rd)
 # define UASM_i_MTC0(buf, rt, rd...) uasm_i_mtc0(buf, rt, rd)
+#else
+# define UASM_i_MFC0(buf, rt, rd...) do { \
+		uasm_i_syncp(buf); \
+		uasm_i_mfc0(buf, rt, rd); \
+	} while(0)
+# define UASM_i_MTC0(buf, rt, rd...) do { \
+		uasm_i_mtc0(buf, rt, rd); \
+		uasm_i_syncp(buf); \
+	} while(0)
+#endif
 # define UASM_i_ROTR(buf, rs, rt, sh) uasm_i_rotr(buf, rs, rt, sh)
 # define UASM_i_SC(buf, rs, rt, off) uasm_i_sc(buf, rs, rt, off)
 # define UASM_i_SLL(buf, rs, rt, sh) uasm_i_sll(buf, rs, rt, sh)
Signature · 1 line
2.21.0
#19 lore raw

PATCH 9/120 MIPS: R5900: Add mandatory SYNC.P to all M[FT]C0 instructions

In reply to Fredrik Noring

The Toshiba TX79 manual specifies that all MTC0 instructions must be followed by a SYNC.P instruction as a barrier to guarantee COP0 register updates[1]. There is one exception to this rule:

An MTC0 instruction which loads the EntryHi COP0 register can be followed by a TLBWI or a TLBWR instruction without having an intervening SYNC.P. This special case is handled by a hardware interlock.

References:

[1] "TX System RISC TX79 Core Architecture" manual, revision 2.0,

    Toshiba Corporation, p. C-28, https://wiki.qemu.org/File:C790.pdf

---
The Linux 2.6 port to the PlayStation 2 has SYNC.P preceding all MFC0 instructions, but I have not found any documentation stating that is necessary. Perhaps this case can be removed?

---
 arch/mips/include/asm/asmmacro.h   | 12 ++++++
 arch/mips/include/asm/irqflags.h   | 15 +++++++
 arch/mips/include/asm/mipsregs.h   | 69 ++++++++++++++++++++++++++++++
 arch/mips/include/asm/stackframe.h | 54 +++++++++++++++++++++++
 arch/mips/kernel/genex.S           | 60 ++++++++++++++++++++++++++
 arch/mips/kernel/head.S            |  9 ++++
 arch/mips/kernel/r4k_switch.S      |  9 ++++
 arch/mips/mm/cex-gen.S             |  6 +++
 arch/mips/mm/tlbex-fault.S         |  3 ++
 arch/mips/mm/tlbex.c               | 14 ++++--
 10 files changed, 247 insertions(+), 4 deletions(-)
arch/mips/include/asm/asmmacro.h +12 -0
index aa58474c739f..67e77c0b64aa 100644
@@ -56,9 +56,15 @@
 	.endm
 #else
 	.macro	local_irq_enable reg=t0
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	mfc0	\reg, CP0_STATUS
 	ori	\reg, \reg, 1
 	mtc0	\reg, CP0_STATUS
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	irq_enable_hazard
 	.endm
 
@@ -67,11 +73,17 @@
 	lw      \reg, TI_PRE_COUNT($28)
 	addi    \reg, \reg, 1
 	sw      \reg, TI_PRE_COUNT($28)
+#endif
+#ifdef CONFIG_CPU_R5900
+	sync.p
 #endif
 	mfc0	\reg, CP0_STATUS
 	ori	\reg, \reg, 1
 	xori	\reg, \reg, 1
 	mtc0	\reg, CP0_STATUS
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	irq_disable_hazard
 #ifdef CONFIG_PREEMPT
 	lw      \reg, TI_PRE_COUNT($28)
arch/mips/include/asm/irqflags.h +15 -0
index f0b862a83816..e0c0265b2719 100644
@@ -78,9 +78,15 @@ static inline void arch_local_irq_restore(unsigned long flags)
 	/*
 	 * Fast, dangerous.  Life is fun, life is good.
 	 */
+#ifdef CONFIG_CPU_R5900
+	"	sync.p							\n"
+#endif
 	"	mfc0	$1, $12						\n"
 	"	ins	$1, %[flags], 0, 1				\n"
 	"	mtc0	$1, $12						\n"
+#ifdef CONFIG_CPU_R5900
+	"	sync.p							\n"
+#endif
 #endif
 	"	" __stringify(__irq_disable_hazard) "			\n"
 	"	.set	pop						\n"
@@ -105,10 +111,16 @@ static inline void arch_local_irq_enable(void)
 #if   defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_CPU_MIPSR6)
 	"	ei							\n"
 #else
+#ifdef CONFIG_CPU_R5900
+	"	sync.p							\n"
+#endif
 	"	mfc0	$1,$12						\n"
 	"	ori	$1,0x1f						\n"
 	"	xori	$1,0x1e						\n"
 	"	mtc0	$1,$12						\n"
+#ifdef CONFIG_CPU_R5900
+	"	sync.p							\n"
+#endif
 #endif
 	"	" __stringify(__irq_enable_hazard) "			\n"
 	"	.set	pop						\n"
@@ -124,6 +136,9 @@ static inline unsigned long arch_local_save_flags(void)
 	asm __volatile__(
 	"	.set	push						\n"
 	"	.set	reorder						\n"
+#ifdef CONFIG_CPU_R5900
+	"	sync.p							\n"
+#endif
 	"	mfc0	%[flags], $12					\n"
 	"	.set	pop						\n"
 	: [flags] "=r" (flags));
arch/mips/include/asm/mipsregs.h +69 -0

117 lines not on this page · raw message

arch/mips/include/asm/stackframe.h +54 -0

151 lines not on this page · raw message

arch/mips/kernel/genex.S +60 -0

164 lines not on this page · raw message

arch/mips/kernel/head.S +9 -0
index 351d40fe0859..aae4b17a33d0 100644
@@ -34,10 +34,16 @@
 	 */
 	.macro	setup_c0_status set clr
 	.set	push
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	mfc0	t0, CP0_STATUS
 	or	t0, ST0_CU0|\set|0x1f|\clr
 	xor	t0, 0x1f|\clr
 	mtc0	t0, CP0_STATUS
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	.set	noreorder
 	sll	zero,3				# ehb
 	.set	pop
@@ -132,6 +138,9 @@ dtb_found:
 #endif
 
 	MTC0		zero, CP0_CONTEXT	# clear context register
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	PTR_LA		$28, init_thread_union
 	/* Set the SP after an empty pt_regs.  */
 	PTR_LI		sp, _THREAD_SIZE - 32 - PT_SIZE
arch/mips/kernel/r4k_switch.S +9 -0
index 58232ae6cfae..de4a1880e9a8 100644
@@ -26,6 +26,9 @@
  */
 	.align	5
 	LEAF(resume)
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	mfc0	t1, CP0_STATUS
 	LONG_S	t1, THREAD_STATUS(a0)
 	cpu_save_nonscratch a0
@@ -46,6 +49,9 @@
 
 	PTR_ADDU	t0, $28, _THREAD_SIZE - 32
 	set_saved_sp	t0, t1, t2
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	mfc0	t1, CP0_STATUS		/* Do we really need this? */
 	li	a3, 0xff01
 	and	t1, a3
@@ -54,6 +60,9 @@
 	and	a2, a3
 	or	a2, t1
 	mtc0	a2, CP0_STATUS
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	move	v0, a0
 	jr	ra
 	END(resume)
arch/mips/mm/cex-gen.S +6 -0
index 45dff5cd4b8e..c5075651229c 100644
@@ -27,11 +27,17 @@
 	 * in the cache, we may not be able to recover.	 As a
 	 * first-order desperate measure, turn off KSEG0 cacheing.
 	 */
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	mfc0	k0,CP0_CONFIG
 	li	k1,~CONF_CM_CMASK
 	and	k0,k0,k1
 	ori	k0,k0,CONF_CM_UNCACHED
 	mtc0	k0,CP0_CONFIG
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	/* Give it a few cycles to sink in... */
 	nop
 	nop
arch/mips/mm/tlbex-fault.S +3 -0
index 77db401fc620..fe2b2c61cca7 100644
@@ -14,6 +14,9 @@
 	NESTED(tlb_do_page_fault_\write, PT_SIZE, sp)
 	.cfi_signal_frame
 	SAVE_ALL docfi=1
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	MFC0	a2, CP0_BADVADDR
 	KMODE
 	move	a0, sp
arch/mips/mm/tlbex.c +10 -4
index 144ceb0fba88..543ddb22b0d9 100644
@@ -691,6 +691,9 @@ static void build_restore_pagemask(u32 **p, struct uasm_reloc **r,
 			uasm_i_mtc0(p, 0, C0_PAGEMASK);
 		}
 	}
+#ifdef CONFIG_CPU_R5900
+	uasm_i_syncp(p);
+#endif
 }
 
 static void build_huge_tlb_write_entry(u32 **p, struct uasm_label **l,
@@ -703,6 +706,9 @@ static void build_huge_tlb_write_entry(u32 **p, struct uasm_label **l,
 	uasm_i_lui(p, tmp, PM_HUGE_MASK >> 16);
 	uasm_i_ori(p, tmp, tmp, PM_HUGE_MASK & 0xffff);
 	uasm_i_mtc0(p, tmp, C0_PAGEMASK);
+#ifdef CONFIG_CPU_R5900
+	uasm_i_syncp(p);
+#endif
 
 	build_tlb_write_entry(p, l, r, wmode);
 
@@ -963,21 +969,21 @@ void build_get_pgde32(u32 **p, unsigned int tmp, unsigned int ptr)
 {
 	if (pgd_reg != -1) {
 		/* pgd is in pgd_reg */
-		uasm_i_mfc0(p, ptr, c0_kscratch(), pgd_reg);
-		uasm_i_mfc0(p, tmp, C0_BADVADDR); /* get faulting address */
+		UASM_i_MFC0(p, ptr, c0_kscratch(), pgd_reg);
+		UASM_i_MFC0(p, tmp, C0_BADVADDR); /* get faulting address */
 	} else {
 		long pgdc = (long)pgd_current;
 
 		/* 32 bit SMP has smp_processor_id() stored in CONTEXT. */
 #ifdef CONFIG_SMP
-		uasm_i_mfc0(p, ptr, SMP_CPUID_REG);
+		UASM_i_MFC0(p, ptr, SMP_CPUID_REG);
 		UASM_i_LA_mostly(p, tmp, pgdc);
 		uasm_i_srl(p, ptr, ptr, SMP_CPUID_PTRSHIFT);
 		uasm_i_addu(p, ptr, tmp, ptr);
 #else
 		UASM_i_LA_mostly(p, ptr, pgdc);
 #endif
-		uasm_i_mfc0(p, tmp, C0_BADVADDR); /* get faulting address */
+		UASM_i_MFC0(p, tmp, C0_BADVADDR); /* get faulting address */
 		uasm_i_lw(p, ptr, uasm_rel_lo(pgdc), ptr);
 	}
 	uasm_i_srl(p, tmp, tmp, PGDIR_SHIFT); /* get pgd only bits */
Signature · 1 line
2.21.0
#20 lore raw

PATCH 10/120 MIPS: R5900: Workaround exception NOP execution bug (FLX05)

In reply to Fredrik Noring

For the R5900, there are cases in which the first two instructions in an exception handler are executed as NOP instructions, when certain exceptions occur and then a bus error occurs immediately before jumping to the exception handler (FLX05)[1].

The corrective measure is to place NOP in the first two instruction locations in all exception handlers.

References:

[1] "TX System RISC TX79 Core Architecture" manual, revision 2.0,

    Toshiba Corporation, p. 1-11, https://wiki.qemu.org/File:C790.pdf
---
 arch/mips/kernel/genex.S | 5 +++++
 arch/mips/kernel/traps.c | 6 ++++++
 arch/mips/mm/tlbex.c     | 6 ++++++
 3 files changed, 17 insertions(+)
arch/mips/kernel/genex.S +5 -0
index 6eaf057e5d95..f57842b785b2 100644
@@ -32,6 +32,11 @@
 NESTED(except_vec3_generic, 0, sp)
 	.set	push
 	.set	noat
+#ifdef CONFIG_CPU_R5900
+	/* Workaround for R5900 exception execution bug (FLX05). */
+	nop
+	nop
+#endif
 #if R5432_CP0_INTERRUPT_WAR
 #ifdef CONFIG_CPU_R5900
 	sync.p
arch/mips/kernel/traps.c +6 -0
index 9423b9a2eb67..9c98475c7dc6 100644
@@ -1953,6 +1953,12 @@ void __init *set_except_vector(int n, void *addr)
 #endif
 		u32 *buf = (u32 *)(ebase + 0x200);
 		unsigned int k0 = 26;
+
+#ifdef CONFIG_CPU_R5900
+		/* Workaround for R5900 exception execution bug (FLX05). */
+		uasm_i_nop(&buf);
+		uasm_i_nop(&buf);
+#endif
 		if ((handler & jump_mask) == ((ebase + 0x200) & jump_mask)) {
 			uasm_i_j(&buf, handler & ~jump_mask);
 			uasm_i_nop(&buf);
arch/mips/mm/tlbex.c +6 -0
index 543ddb22b0d9..82136c346885 100644
@@ -1313,6 +1313,12 @@ static void build_r4000_tlb_refill_handler(void)
 	memset(relocs, 0, sizeof(relocs));
 	memset(final_handler, 0, sizeof(final_handler));
 
+#ifdef CONFIG_CPU_R5900
+	/* Workaround for R5900 exception execution bug (FLX05). */
+	uasm_i_nop(&p);
+	uasm_i_nop(&p);
+#endif
+
 	if (IS_ENABLED(CONFIG_64BIT) && (scratch_reg >= 0 || scratchpad_available()) && use_bbit_insns()) {
 		htlb_info = build_fast_tlb_refill_handler(&p, &l, &r, K0, K1,
 							  scratch_reg);
Signature · 1 line
2.21.0
Reviewed-by on v1 10/120
#21 lore raw

Re: [PATCH 010/120] MIPS: R5900: Workaround exception NOP execution bug (FLX05)

Quoted 8 lines · Fredrik Noring

For the R5900, there are cases in which the first two instructions in an exception handler are executed as NOP instructions, when certain exceptions occur and then a bus error occurs immediately before jumping to the exception handler (FLX05)[1].

The corrective measure is to place NOP in the first two instruction locations in all exception handlers.

Quoted 6 lines

References:

[1] "TX System RISC TX79 Core Architecture" manual, revision 2.0,

    Toshiba Corporation, p. 1-11, https://wiki.qemu.org/File:C790.pdf
Quoted 57 lines
---
 arch/mips/kernel/genex.S | 5 +++++
 arch/mips/kernel/traps.c | 6 ++++++
 arch/mips/mm/tlbex.c     | 6 ++++++
 3 files changed, 17 insertions(+)
arch/mips/kernel/genex.S +5 -0
index 6eaf057e5d95..f57842b785b2 100644
@@ -32,6 +32,11 @@
 NESTED(except_vec3_generic, 0, sp)
 	.set	push
 	.set	noat
+#ifdef CONFIG_CPU_R5900
+	/* Workaround for R5900 exception execution bug (FLX05). */
+	nop
+	nop
+#endif
 #if R5432_CP0_INTERRUPT_WAR
 #ifdef CONFIG_CPU_R5900
 	sync.p
arch/mips/kernel/traps.c +6 -0
index 9423b9a2eb67..9c98475c7dc6 100644
@@ -1953,6 +1953,12 @@ void __init *set_except_vector(int n, void *addr)
 #endif
 		u32 *buf = (u32 *)(ebase + 0x200);
 		unsigned int k0 = 26;
+
+#ifdef CONFIG_CPU_R5900
+		/* Workaround for R5900 exception execution bug (FLX05). */
+		uasm_i_nop(&buf);
+		uasm_i_nop(&buf);
+#endif
 		if ((handler & jump_mask) == ((ebase + 0x200) & jump_mask)) {
 			uasm_i_j(&buf, handler & ~jump_mask);
 			uasm_i_nop(&buf);
arch/mips/mm/tlbex.c +6 -0
index 543ddb22b0d9..82136c346885 100644
@@ -1313,6 +1313,12 @@ static void build_r4000_tlb_refill_handler(void)
 	memset(relocs, 0, sizeof(relocs));
 	memset(final_handler, 0, sizeof(final_handler));
 
+#ifdef CONFIG_CPU_R5900
+	/* Workaround for R5900 exception execution bug (FLX05). */
+	uasm_i_nop(&p);
+	uasm_i_nop(&p);
+#endif
+
 	if (IS_ENABLED(CONFIG_64BIT) && (scratch_reg >= 0 || scratchpad_available()) && use_bbit_insns()) {
 		htlb_info = build_fast_tlb_refill_handler(&p, &l, &r, K0, K1,
 							  scratch_reg);
#22 lore raw

PATCH 11/120 MIPS: R5900: Avoid pipeline hazard with the TLBP instruction

In reply to Fredrik Noring

On the R5900, the TLBP instruction must be immediately followed by an ERET or a SYNC.P instruction[1].

References:

[1] "TX System RISC TX79 Core Architecture" manual, revision 2.0,

    Toshiba Corporation, p. C-37, https://wiki.qemu.org/File:C790.pdf
---
 arch/mips/mm/tlbex.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)
arch/mips/mm/tlbex.c +13 -0
index 82136c346885..0519e2eafbb8 100644
@@ -489,6 +489,19 @@ static void __maybe_unused build_tlb_probe_entry(u32 **p)
 		uasm_i_tlbp(p);
 		break;
 
+	case CPU_R5900:
+		/*
+		 * On the R5900, the TLBWP instruction must be immediately
+		 * followed by an ERET or a SYNC.P instruction.
+		 */
+		uasm_i_tlbp(p);
+		uasm_i_syncp(p);
+		uasm_i_nop(p);
+		uasm_i_nop(p);
+		uasm_i_nop(p);
+		uasm_i_nop(p);
+		break;
+
 	default:
 		uasm_i_tlbp(p);
 		break;
Signature · 1 line
2.21.0
#23 lore raw

Re: [PATCH 011/120] MIPS: R5900: Avoid pipeline hazard with the TLBP instruction

Hello!

Quoted 18 lines · Fredrik Noring

On the R5900, the TLBP instruction must be immediately followed by an ERET or a SYNC.P instruction[1].

References:

[1] "TX System RISC TX79 Core Architecture" manual, revision 2.0,

     Toshiba Corporation, p. C-37, https://wiki.qemu.org/File:C790.pdf
---
  arch/mips/mm/tlbex.c | 13 +++++++++++++
  1 file changed, 13 insertions(+)
arch/mips/mm/tlbex.c +0 -0
index 82136c346885..0519e2eafbb8 100644

arch/mips/mm/tlbex.c static void __maybe_unused build_tlb_probe_entry(u32 **p) · line 494

Quoted 6 lines
arch/mips/mm/tlbex.c +3 -0
@@ -489,6 +489,19 @@ static void __maybe_unused build_tlb_probe_entry(u32 **p)
  		uasm_i_tlbp(p);
  		break;
 
+	case CPU_R5900:
+		/*
+		 * On the R5900, the TLBWP instruction must be immediately

So is it TLBP or TLBWP?

arch/mips/mm/tlbex.c static void __maybe_unused build_tlb_probe_entry(u32 **p) · line 507

Quoted 13 lines
arch/mips/mm/tlbex.c +10 -0
+		 * followed by an ERET or a SYNC.P instruction.
+		 */
+		uasm_i_tlbp(p);
+		uasm_i_syncp(p);
+		uasm_i_nop(p);
+		uasm_i_nop(p);
+		uasm_i_nop(p);
+		uasm_i_nop(p);
+		break;
+
  	default:
  		uasm_i_tlbp(p);
  		break;

MBR, Sergei

#24 lore raw

Re: [PATCH 011/120] MIPS: R5900: Avoid pipeline hazard with the TLBP instruction

Hi Sergei,

Quoted 5 lines

arch/mips/mm/tlbex.c static void __maybe_unused build_tlb_probe_entry(u32 **p) · line 494

Quoted 3 lines
arch/mips/mm/tlbex.c +3 -0
+	case CPU_R5900:
+		/*
+		 * On the R5900, the TLBWP instruction must be immediately

So is it TLBP or TLBWP?

TLBWP does not exist, so it must be TLBP. Thanks! :)

Fredrik

#25 lore raw

PATCH 12/120 MIPS: R5900: Avoid pipeline hazards with the TLBW[IR] instructions

In reply to Fredrik Noring

On the R5900, the TLBWI[1] and TLBWR[2] instructions must be followed by an ERET or a SYNC.P instruction to ensure a TLB update.

References:

[1] "TX System RISC TX79 Core Architecture" manual, revision 2.0,

    Toshiba Corporation, p. C-39, https://wiki.qemu.org/File:C790.pdf

[2] Ibid. p. C-40.

---
 arch/mips/mm/tlbex.c | 9 +++++++++
 1 file changed, 9 insertions(+)
arch/mips/mm/tlbex.c +9 -0
index 0519e2eafbb8..89ff0eae5397 100644
@@ -622,6 +622,15 @@ void build_tlb_write_entry(u32 **p, struct uasm_label **l,
 		uasm_i_nop(p);
 		tlbw(p);
 		break;
+	case CPU_R5900:
+		/*
+		 * On the R5900, the TLBWI and TLBWR instructions must be
+		 * followed by an ERET or a SYNC.P instruction to ensure a
+		 * TLB update.
+		 */
+		tlbw(p);
+		uasm_i_syncp(p);
+		break;
 
 	case CPU_JZRISC:
 		tlbw(p);
Signature · 1 line
2.21.0
#26 lore raw

PATCH 13/120 MIPS: R5900: Avoid pipeline hazard with the TLBR instruction

In reply to Fredrik Noring

On the R5900, the TLBR instruction must be immediately followed by an ERET or a SYNC.P instruction[1].

References:

[1] "TX System RISC TX79 Core Architecture" manual, revision 2.0,

    Toshiba Corporation, p. C-38, https://wiki.qemu.org/File:C790.pdf
---
 arch/mips/include/asm/mipsregs.h |  4 ++++
 arch/mips/mm/tlbex.c             | 24 ++++++++++++++++++++++++
 2 files changed, 28 insertions(+)
arch/mips/include/asm/mipsregs.h +4 -0
index 2aa947b3d0d1..ec22406c800f 100644
@@ -2728,6 +2728,10 @@ static inline void tlb_read(void)
 
 	__asm__ __volatile__(
 		".set noreorder\n\t"
+#ifdef CONFIG_CPU_R5900
+		/* instruction must not be at the end of a page. */
+		".align 8\n\t"
+#endif
 		"tlbr\n\t"
 #ifdef CONFIG_CPU_R5900
 		"sync.p\n\t"
arch/mips/mm/tlbex.c +24 -0
index 89ff0eae5397..1caa0214d57a 100644
@@ -2185,6 +2185,18 @@ static void build_r4000_tlb_load_handler(void)
 
 		uasm_i_tlbr(&p);
 
+#ifdef CONFIG_CPU_R5900
+		/*
+		 * On the R5900, the TLBR instruction must be immediately
+		 * followed by an ERET or a SYNC.P instruction.
+		 */
+		uasm_i_syncp(&p);
+		uasm_i_nop(&p);
+		uasm_i_nop(&p);
+		uasm_i_nop(&p);
+		uasm_i_nop(&p);
+#endif
+
 		switch (current_cpu_type()) {
 		default:
 			if (cpu_has_mips_r2_exec_hazard) {
@@ -2260,6 +2272,18 @@ static void build_r4000_tlb_load_handler(void)
 
 		uasm_i_tlbr(&p);
 
+#ifdef CONFIG_CPU_R5900
+		/*
+		 * On the R5900, the TLBR instruction must be immediately
+		 * followed by an ERET or a SYNC.P instruction.
+		 */
+		uasm_i_syncp(&p);
+		uasm_i_nop(&p);
+		uasm_i_nop(&p);
+		uasm_i_nop(&p);
+		uasm_i_nop(&p);
+#endif
+
 		switch (current_cpu_type()) {
 		default:
 			if (cpu_has_mips_r2_exec_hazard) {
Signature · 1 line
2.21.0
#27 lore raw

PATCH 14/120 MIPS: R5900: Install final length of TLB refill handler rather than 256 bytes

In reply to Fredrik Noring

The R5900 TLB refill handler is limited to 128 bytes, corresponding to 32 instructions.

Installing a 256 byte TLB refill handler for the R5900 at address 0x80000000 overwrites the performance counter handler at address 0x80000080, according to the TX79 manual[1]:

        Table 5-2. Exception Vectors for Level 1 exceptions

             Exceptions      |      Vector Address
                             |   BEV = 0  |   BEV = 1
        ---------------------+------------+-----------
        TLB Refill (EXL = 0) | 0x80000000 | 0xBFC00200
        TLB Refill (EXL = 1) | 0x80000180 | 0xBFC00380
        Interrupt            | 0x80000200 | 0xBFC00400
        Others               | 0x80000180 | 0xBFC00380
        ---------------------+------------+-----------

        Table 5-3. Exception Vectors for Level 2 exceptions

             Exceptions      |      Vector Address
                             |   DEV = 0  |   DEV = 1
        ---------------------+------------+-----------
        Reset, NMI           | 0xBFC00000 | 0xBFC00000
        Performance Counter  | 0x80000080 | 0xBFC00280
        Debug, SIO           | 0x80000100 | 0xBFC00300
        ---------------------+------------+-----------

References:

[1] "TX System RISC TX79 Core Architecture" manual, revision 2.0,

    Toshiba Corporation, p. 5-7, https://wiki.qemu.org/File:C790.pdf
---
 arch/mips/mm/tlbex.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
arch/mips/mm/tlbex.c +3 -3
index 1caa0214d57a..d356953509e6 100644
@@ -1502,9 +1502,9 @@ static void build_r4000_tlb_refill_handler(void)
 	pr_debug("Wrote TLB refill handler (%u instructions).\n",
 		 final_len);
 
-	memcpy((void *)ebase, final_handler, 0x100);
-	local_flush_icache_range(ebase, ebase + 0x100);
-	dump_handler("r4000_tlb_refill", (u32 *)ebase, (u32 *)(ebase + 0x100));
+	memcpy((void *)ebase, final_handler, 4 * final_len);
+	local_flush_icache_range(ebase, ebase + 4 * final_len);
+	dump_handler("r4000_tlb_refill", (u32 *)ebase, (u32 *)(ebase + 4 * final_len));
 }
 
 static void setup_pw(void)
Signature · 1 line
2.21.0
#28 lore raw

PATCH 15/120 MIPS: R5900: Verify that the TLB refill handler does not overflow

In reply to Fredrik Noring

The R5900 TLB refill handler is limited to 32 instructions (0x80 bytes, which is 0x80000080 - 0x80000000) according to the TX79 manual[1]:

        Table 5-2. Exception Vectors for Level 1 exceptions

             Exceptions      |      Vector Address
                             |   BEV = 0  |   BEV = 1
        ---------------------+------------+-----------
        TLB Refill (EXL = 0) | 0x80000000 | 0xBFC00200
        TLB Refill (EXL = 1) | 0x80000180 | 0xBFC00380
        Interrupt            | 0x80000200 | 0xBFC00400
        Others               | 0x80000180 | 0xBFC00380
        ---------------------+------------+-----------

        Table 5-3. Exception Vectors for Level 2 exceptions

             Exceptions      |      Vector Address
                             |   DEV = 0  |   DEV = 1
        ---------------------+------------+-----------
        Reset, NMI           | 0xBFC00000 | 0xBFC00000
        Performance Counter  | 0x80000080 | 0xBFC00280
        Debug, SIO           | 0x80000100 | 0xBFC00300
        ---------------------+------------+-----------

References:

[1] "TX System RISC TX79 Core Architecture" manual, revision 2.0,

    Toshiba Corporation, p. 5-7, https://wiki.qemu.org/File:C790.pdf
---
 arch/mips/mm/tlbex.c | 4 ++++
 1 file changed, 4 insertions(+)
arch/mips/mm/tlbex.c +4 -0
index d356953509e6..1bd134b6f033 100644
@@ -1404,6 +1404,10 @@ static void build_r4000_tlb_refill_handler(void)
 	 * unused.
 	 */
 	switch (boot_cpu_type()) {
+	case CPU_R5900:
+		if ((p - tlb_handler) > 32)
+			panic("TLB refill handler space exceeded");
+		/* Fallthrough */
 	default:
 		if (sizeof(long) == 4) {
 	case CPU_LOONGSON2:
Signature · 1 line
2.21.0
#29 lore raw

PATCH 16/120 MIPS: R5900: The ERET instruction has issues with delay slot and CACHE

In reply to Fredrik Noring

---
The Linux 2.6 port to the PlayStation 2 has this remark. I don't know where it comes from.

---
 arch/mips/mm/tlbex.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
arch/mips/mm/tlbex.c +18 -0
index 1bd134b6f033..9d5864b20e9f 100644
@@ -1383,6 +1383,16 @@ static void build_r4000_tlb_refill_handler(void)
 		uasm_l_leave(&l, p);
 		uasm_i_eret(&p); /* return from trap */
 	}
+
+#ifdef CONFIG_CPU_R5900
+	/* There should be nothing which can be interpreted as cache instruction. */
+	uasm_i_nop(&p);
+	uasm_i_nop(&p);
+	uasm_i_nop(&p);
+	uasm_i_nop(&p);
+	uasm_i_nop(&p);
+#endif
+
 #ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT
 	uasm_l_tlb_huge_update(&l, p);
 	if (htlb_info.need_reload_pte)
@@ -2129,6 +2139,14 @@ build_r4000_tlbchange_handler_tail(u32 **p, struct uasm_label **l,
 	uasm_l_leave(l, *p);
 	build_restore_work_registers(p);
 	uasm_i_eret(p); /* return from trap */
+#ifdef CONFIG_CPU_R5900
+	/* There should be nothing which can be interpreted as cache instruction. */
+	uasm_i_nop(p);
+	uasm_i_nop(p);
+	uasm_i_nop(p);
+	uasm_i_nop(p);
+	uasm_i_nop(p);
+#endif
 
 #ifdef CONFIG_64BIT
 	build_get_pgd_vmalloc64(p, l, r, tmp, ptr, not_refill);
Signature · 1 line
2.21.0
#30 lore raw

PATCH 17/120 MIPS: R5900: Define CACHE instruction operation field encodings

In reply to Fredrik Noring

The CACHE instruction operation field encodings are listed in the TX79 manual[1]:

Table C-1. CACHE Instruction Op Field Encoding

Mnemonic | OpCode | CACHE Instruction | Target
---------+--------+------------------------------+------------------
IXIN | 00111 | INDEX INVALIDATE | Instruction Cache
IXLTG | 00000 | INDEX LOAD TAG | Instruction Cache
IXSTG | 00100 | INDEX STORE TAG | Instruction Cache
IHIN | 01011 | HIT INVALIDATE | Instruction Cache
IFL | 01110 | FILL | Instruction Cache
IXLDT | 00001 | INDEX LOAD DATA | Instruction Cache
IXSDT | 00101 | INDEX STORE DATA | Instruction Cache
---------+--------+------------------------------+------------------
BXLBT | 00010 | INDEX LOAD BTACC | BTAC
BXSBT | 00110 | INDEX STORE BTAC | BTAC
BFH | 01100 | BTAC FLUSH | BTAC
BHINBT | 01010 | HIT INVALIDATE BTAC | BTAC
---------+--------+------------------------------+------------------
DXWBIN | 10100 | INDEX WRITE BACK INVALIDATE | Data Cache
DXLTG | 10000 | INDEX LOAD TAG | Data Cache
DXSTG | 10010 | INDEX STORE TAG | Data Cache
DXIN | 10110 | INDEX INVALIDATE | Data Cache
DHIN | 11010 | HIT INVALIDATE | Data Cache
DHWBIN | 11000 | HIT WRITEBACK INVALIDATE | Data Cache
DXLDT | 10001 | INDEX LOAD DATA | Data Cache
DXSDT | 10011 | INDEX STORE DATA | Data Cache
DHWOIN | 11100 | HIT WRITEBACK W/O INVALIDATE | Data Cache
---------+--------+------------------------------+------------------

References:

[1] "TX System RISC TX79 Core Architecture" manual, revision 2.0, Toshiba Corporation, p. C-6, https://wiki.qemu.org/File:C790.pdf

---
 arch/mips/include/asm/cacheops.h | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)
arch/mips/include/asm/cacheops.h +17 -0
index 8031fbc6b69a..3a6b34be1122 100644
@@ -34,6 +34,17 @@
 /*
  * Cache Operations available on all MIPS processors with R4000-style caches
  */
+#ifdef CONFIG_CPU_R5900
+#define Index_Invalidate_I		0x07
+#define Index_Writeback_Inv_D		0x14
+#define Index_Load_Tag_I		0x00
+#define Index_Load_Tag_D		0x10
+#define Index_Store_Tag_I		0x04
+#define Index_Store_Tag_D		0x12
+#define Hit_Invalidate_I		0x0b
+#define Hit_Invalidate_D		0x1a
+#define Hit_Writeback_Inv_D		0x18
+#else
 #define Index_Invalidate_I		(Cache_I | Index_Writeback_Inv)
 #define Index_Writeback_Inv_D		(Cache_D | Index_Writeback_Inv)
 #define Index_Load_Tag_I		(Cache_I | Index_Load_Tag)
@@ -43,14 +54,20 @@
 #define Hit_Invalidate_I		(Cache_I | Hit_Invalidate)
 #define Hit_Invalidate_D		(Cache_D | Hit_Invalidate)
 #define Hit_Writeback_Inv_D		(Cache_D | Hit_Writeback_Inv)
+#endif
 
 /*
  * R4000-specific cacheops
  */
 #define Create_Dirty_Excl_D		(Cache_D | 0x0c)
+#ifdef CONFIG_CPU_R5900
+#define Fill				0x0e
+#define Hit_Writeback_D			0x1c
+#else
 #define Fill				(Cache_I | 0x14)
 #define Hit_Writeback_I			(Cache_I | Hit_Writeback)
 #define Hit_Writeback_D			(Cache_D | Hit_Writeback)
+#endif
 
 /*
  * R4000SC and R4400SC-specific cacheops
Signature · 1 line
2.21.0
#31 lore raw

PATCH 18/120 MIPS: R5900: Workaround where MSB must be 0 for the instruction cache

In reply to Fredrik Noring

---
The Linux 2.6 port to the PlayStation 2 has this remark. I don't know where it comes from.

---
 arch/mips/include/asm/r4kcache.h | 5 +++++
 1 file changed, 5 insertions(+)
arch/mips/include/asm/r4kcache.h +5 -0
index 7f4a32d3345a..e00087db9d74 100644
@@ -37,7 +37,12 @@ extern void (*r4k_blast_icache)(void);
  *  - We need a properly sign extended address for 64-bit code.	 To get away
  *    without ifdefs we let the compiler do it by a type cast.
  */
+#ifdef CONFIG_CPU_R5900
+/* MSB must be 0 for the instruction cache due to an R5900 bug. */
+#define INDEX_BASE	0
+#else
 #define INDEX_BASE	CKSEG0
+#endif
 
 #define cache_op(op,addr)						\
 	__asm__ __volatile__(						\
Signature · 1 line
2.21.0
#32 lore raw

PATCH 19/120 MIPS: R5900: Use SYNC.L for data cache and SYNC.P for instruction cache

In reply to Fredrik Noring

Toshiba TX79 manual programming notes[1]:

For all CACHE sub-operations which operate on the instruction cache the following programming restrictions have to be followed:

1. A sequence of CACHE instructions has to be directly preceded and followed by a SYNC.P instruction.
2. Each individual FILL sub-operation has to be followed by a SYNC.L instruction.

For all CACHE sub-operations which operate on the data cache the following programming restrictions have to be followed:

1. A sequence of CACHE instructions have to be directly preceded and followed by a SYNC.L instruction.
2. Each of the three WRITEBACK sub-operations have to be individually followed by a SYNC.L instruction.

For all CACHE sub-operations which operate on the BTAC the following programming restrictions have to be followed:

1. A sequence of CACHE instructions have to be directly preceded and followed by a SYNC.P instruction.

References:

[1] "TX System RISC TX79 Core Architecture" manual, revision 2.0,

    Toshiba Corporation, p. C-13, https://wiki.qemu.org/File:C790.pdf

---
This is perhaps the most complex change for the R5900. The code comes from the Linux 2.6 port to the PlayStation 2.

---
 arch/mips/include/asm/r4kcache.h | 239 ++++++++++++++++++++++++-------
 arch/mips/mm/c-r4k.c             |   8 +-
 2 files changed, 189 insertions(+), 58 deletions(-)
arch/mips/include/asm/r4kcache.h +185 -54
index e00087db9d74..da9f55f36fcf 100644
@@ -44,7 +44,7 @@ extern void (*r4k_blast_icache)(void);
 #define INDEX_BASE	CKSEG0
 #endif
 
-#define cache_op(op,addr)						\
+#define cache_op_s(op,addr)						\
 	__asm__ __volatile__(						\
 	"	.set	push					\n"	\
 	"	.set	noreorder				\n"	\
@@ -53,56 +53,85 @@ extern void (*r4k_blast_icache)(void);
 	"	.set	pop					\n"	\
 	:								\
 	: "i" (op), "R" (*(unsigned char *)(addr)))
+#ifdef CONFIG_CPU_R5900
+#define cache_op_d(op,addr)						\
+	__asm__ __volatile__(						\
+	"	.set	push					\n"	\
+	"	.set	noreorder				\n"	\
+	"	.set	mips3\n\t				\n"	\
+	"	sync.l						\n"	\
+	"	cache	%0, %1					\n"	\
+	"	sync.l						\n"	\
+	"	.set	pop					\n"	\
+	:								\
+	: "i" (op), "R" (*(unsigned char *)(addr)))
+#define cache_op_i(op,addr)						\
+	__asm__ __volatile__(						\
+	"	.set	push					\n"	\
+	"	.set	noreorder				\n"	\
+	"	.set	mips3\n\t				\n"	\
+	"	sync.p						\n"	\
+	"	cache	%0, %1					\n"	\
+	"	sync.p						\n"	\
+	"	.set	pop					\n"	\
+	:								\
+	: "i" (op), "R" (*(unsigned char *)(addr)))
+#else
+#define cache_op_d cache_op_s
+#define cache_op_i cache_op_s
+#define cache_op cache_op_s
+#endif
+#define cache_op_t cache_op_s
 
 static inline void flush_icache_line_indexed(unsigned long addr)
 {
-	cache_op(Index_Invalidate_I, addr);
+	cache_op_i(Index_Invalidate_I, addr);
 }
 
 static inline void flush_dcache_line_indexed(unsigned long addr)
 {
-	cache_op(Index_Writeback_Inv_D, addr);
+	cache_op_d(Index_Writeback_Inv_D, addr);
 }
 
 static inline void flush_scache_line_indexed(unsigned long addr)
 {
-	cache_op(Index_Writeback_Inv_SD, addr);
+	cache_op_s(Index_Writeback_Inv_SD, addr);
 }
 
 static inline void flush_icache_line(unsigned long addr)
 {
 	switch (boot_cpu_type()) {
 	case CPU_LOONGSON2:
-		cache_op(Hit_Invalidate_I_Loongson2, addr);
+		cache_op_i(Hit_Invalidate_I_Loongson2, addr);
 		break;
 
 	default:
-		cache_op(Hit_Invalidate_I, addr);
+		cache_op_i(Hit_Invalidate_I, addr);
 		break;
 	}
 }
 
 static inline void flush_dcache_line(unsigned long addr)
 {
-	cache_op(Hit_Writeback_Inv_D, addr);
+	cache_op_d(Hit_Writeback_Inv_D, addr);
 }
 
 static inline void invalidate_dcache_line(unsigned long addr)
 {
-	cache_op(Hit_Invalidate_D, addr);
+	cache_op_d(Hit_Invalidate_D, addr);
 }
 
 static inline void invalidate_scache_line(unsigned long addr)
 {
-	cache_op(Hit_Invalidate_SD, addr);
+	cache_op_s(Hit_Invalidate_SD, addr);
 }
 
 static inline void flush_scache_line(unsigned long addr)
 {
-	cache_op(Hit_Writeback_Inv_SD, addr);
+	cache_op_s(Hit_Writeback_Inv_SD, addr);
 }
 
-#define protected_cache_op(op,addr)				\
+#define protected_cache_op_s(op,addr)				\
 ({								\
 	int __err = 0;						\
 	__asm__ __volatile__(					\
@@ -124,6 +153,49 @@ static inline void flush_scache_line(unsigned long addr)
 	__err;							\
 })
 
+#ifdef CONFIG_CPU_R5900
+#define protected_cache_op_d(op,addr)				\
+({								\
+	int __err = 0;						\
+	__asm__ __volatile__(					\
+	"	.set	push			\n"		\
+	"	.set	noreorder		\n"		\
+	"	.set	mips3			\n"		\
+	"	sync.l				\n"		\
+	"1:	cache	%0, (%1)		\n"		\
+	"	sync.l				\n"		\
+	"2:	.set	pop			\n"		\
+	"	.section __ex_table,\"a\"	\n"		\
+	"	"STR(PTR)" 1b, 2b		\n"		\
+	"	.previous"					\
+	:							\
+	: "i" (op), "r" (addr));				\
+	__err;							\
+})
+
+#define protected_cache_op_i(op,addr)				\
+({								\
+	int __err = 0;						\
+	__asm__ __volatile__(					\
+	"	.set	push			\n"		\
+	"	.set	noreorder		\n"		\
+	"	.set	mips3			\n"		\
+	"	sync.p				\n"		\
+	"1:	cache	%0, (%1)		\n"		\
+	"	sync.p				\n"		\
+	"2:	.set	pop			\n"		\
+	"	.section __ex_table,\"a\"	\n"		\
+	"	"STR(PTR)" 1b, 2b		\n"		\
+	"	.previous"					\
+	:							\
+	: "i" (op), "r" (addr));				\
+	__err;							\
+})
+#else
+#define protected_cache_op_i protected_cache_op_s
+#define protected_cache_op_d protected_cache_op_s
+#define protected_cache_op protected_cache_op_s
+#endif
 
 #define protected_cachee_op(op,addr)				\
 ({								\
@@ -155,13 +227,13 @@ static inline int protected_flush_icache_line(unsigned long addr)
 {
 	switch (boot_cpu_type()) {
 	case CPU_LOONGSON2:
-		return protected_cache_op(Hit_Invalidate_I_Loongson2, addr);
+		return protected_cache_op_i(Hit_Invalidate_I_Loongson2, addr);
 
 	default:
 #ifdef CONFIG_EVA
-		return protected_cachee_op(Hit_Invalidate_I, addr);
+		return protected_cachee_op_i(Hit_Invalidate_I, addr);
 #else
-		return protected_cache_op(Hit_Invalidate_I, addr);
+		return protected_cache_op_i(Hit_Invalidate_I, addr);
 #endif
 	}
 }
@@ -175,18 +247,18 @@ static inline int protected_flush_icache_line(unsigned long addr)
 static inline int protected_writeback_dcache_line(unsigned long addr)
 {
 #ifdef CONFIG_EVA
-	return protected_cachee_op(Hit_Writeback_Inv_D, addr);
+	return protected_cachee_op_d(Hit_Writeback_Inv_D, addr);
 #else
-	return protected_cache_op(Hit_Writeback_Inv_D, addr);
+	return protected_cache_op_d(Hit_Writeback_Inv_D, addr);
 #endif
 }
 
 static inline int protected_writeback_scache_line(unsigned long addr)
 {
 #ifdef CONFIG_EVA
-	return protected_cachee_op(Hit_Writeback_Inv_SD, addr);
+	return protected_cachee_op_s(Hit_Writeback_Inv_SD, addr);
 #else
-	return protected_cache_op(Hit_Writeback_Inv_SD, addr);
+	return protected_cache_op_s(Hit_Writeback_Inv_SD, addr);
 #endif
 }
 
@@ -195,7 +267,7 @@ static inline int protected_writeback_scache_line(unsigned long addr)
  */
 static inline void invalidate_tcache_page(unsigned long addr)
 {
-	cache_op(Page_Invalidate_T, addr);
+	cache_op_t(Page_Invalidate_T, addr);
 }
 
 #ifndef CONFIG_CPU_MIPSR6
@@ -276,6 +348,65 @@ static inline void invalidate_tcache_page(unsigned long addr)
 		:							\
 		: "r" (base),						\
 		  "i" (op));
+#ifdef CONFIG_CPU_R5900
+#define cache64_unroll32_d(base,op)					\
+	__asm__ __volatile__(						\
+	"	.set push					\n"	\
+	"	.set noreorder					\n"	\
+	"	.set mips3					\n"	\
+	"	sync.l						\n"	\
+	"	cache %1, 0x000(%0); sync.l; cache %1, 0x040(%0); sync.l	\n"	\
+	"	cache %1, 0x080(%0); sync.l; cache %1, 0x0c0(%0); sync.l	\n"	\
+	"	cache %1, 0x100(%0); sync.l; cache %1, 0x140(%0); sync.l	\n"	\
+	"	cache %1, 0x180(%0); sync.l; cache %1, 0x1c0(%0); sync.l	\n"	\
+	"	cache %1, 0x200(%0); sync.l; cache %1, 0x240(%0); sync.l	\n"	\
+	"	cache %1, 0x280(%0); sync.l; cache %1, 0x2c0(%0); sync.l	\n"	\
+	"	cache %1, 0x300(%0); sync.l; cache %1, 0x340(%0); sync.l	\n"	\
+	"	cache %1, 0x380(%0); sync.l; cache %1, 0x3c0(%0); sync.l	\n"	\
+	"	cache %1, 0x400(%0); sync.l; cache %1, 0x440(%0); sync.l	\n"	\
+	"	cache %1, 0x480(%0); sync.l; cache %1, 0x4c0(%0); sync.l	\n"	\
+	"	cache %1, 0x500(%0); sync.l; cache %1, 0x540(%0); sync.l	\n"	\
+	"	cache %1, 0x580(%0); sync.l; cache %1, 0x5c0(%0); sync.l	\n"	\
+	"	cache %1, 0x600(%0); sync.l; cache %1, 0x640(%0); sync.l	\n"	\
+	"	cache %1, 0x680(%0); sync.l; cache %1, 0x6c0(%0); sync.l	\n"	\
+	"	cache %1, 0x700(%0); sync.l; cache %1, 0x740(%0); sync.l	\n"	\
+	"	cache %1, 0x780(%0); sync.l; cache %1, 0x7c0(%0); sync.l	\n"	\
+	"	.set pop					\n"	\
+		:							\
+		: "r" (base),						\
+		  "i" (op));
+
+#define cache64_unroll32_i(base,op)					\
+	__asm__ __volatile__(						\
+	"	.set push					\n"	\
+	"	.set noreorder					\n"	\
+	"	.set mips3					\n"	\
+	"	sync.p						\n"	\
+	"	cache %1, 0x000(%0); cache %1, 0x040(%0)	\n"	\
+	"	cache %1, 0x080(%0); cache %1, 0x0c0(%0)	\n"	\
+	"	cache %1, 0x100(%0); cache %1, 0x140(%0)	\n"	\
+	"	cache %1, 0x180(%0); cache %1, 0x1c0(%0)	\n"	\
+	"	cache %1, 0x200(%0); cache %1, 0x240(%0)	\n"	\
+	"	cache %1, 0x280(%0); cache %1, 0x2c0(%0)	\n"	\
+	"	cache %1, 0x300(%0); cache %1, 0x340(%0)	\n"	\
+	"	cache %1, 0x380(%0); cache %1, 0x3c0(%0)	\n"	\
+	"	cache %1, 0x400(%0); cache %1, 0x440(%0)	\n"	\
+	"	cache %1, 0x480(%0); cache %1, 0x4c0(%0)	\n"	\
+	"	cache %1, 0x500(%0); cache %1, 0x540(%0)	\n"	\
+	"	cache %1, 0x580(%0); cache %1, 0x5c0(%0)	\n"	\
+	"	cache %1, 0x600(%0); cache %1, 0x640(%0)	\n"	\
+	"	cache %1, 0x680(%0); cache %1, 0x6c0(%0)	\n"	\
+	"	cache %1, 0x700(%0); cache %1, 0x740(%0)	\n"	\
+	"	cache %1, 0x780(%0); cache %1, 0x7c0(%0)	\n"	\
+	"	sync.p						\n"	\
+	"	.set pop					\n"	\
+		:							\
+		: "r" (base),						\
+		  "i" (op));
+#else
+#define cache64_unroll32_i cache64_unroll32
+#define cache64_unroll32_d cache64_unroll32
+#endif
 
 #define cache128_unroll32(base,op)					\
 	__asm__ __volatile__(						\
@@ -532,7 +663,7 @@ static inline void invalidate_tcache_page(unsigned long addr)
 		  "i" (op));
 
 /* build blast_xxx, blast_xxx_page, blast_xxx_page_indexed */
-#define __BUILD_BLAST_CACHE(pfx, desc, indexop, hitop, lsize, extra)	\
+#define __BUILD_BLAST_CACHE(fn_pfx, pfx, desc, indexop, hitop, lsize, extra)	\
 static inline void extra##blast_##pfx##cache##lsize(void)		\
 {									\
 	unsigned long start = INDEX_BASE;				\
@@ -544,7 +675,7 @@ static inline void extra##blast_##pfx##cache##lsize(void)		\
 									\
 	for (ws = 0; ws < ws_end; ws += ws_inc)				\
 		for (addr = start; addr < end; addr += lsize * 32)	\
-			cache##lsize##_unroll32(addr|ws, indexop);	\
+			cache##lsize##_unroll32##fn_pfx(addr|ws, indexop);	\
 }									\
 									\
 static inline void extra##blast_##pfx##cache##lsize##_page(unsigned long page) \
@@ -553,7 +684,7 @@ static inline void extra##blast_##pfx##cache##lsize##_page(unsigned long page) \
 	unsigned long end = page + PAGE_SIZE;				\
 									\
 	do {								\
-		cache##lsize##_unroll32(start, hitop);			\
+		cache##lsize##_unroll32##fn_pfx(start, hitop);			\
 		start += lsize * 32;					\
 	} while (start < end);						\
 }									\
@@ -570,29 +701,29 @@ static inline void extra##blast_##pfx##cache##lsize##_page_indexed(unsigned long
 									\
 	for (ws = 0; ws < ws_end; ws += ws_inc)				\
 		for (addr = start; addr < end; addr += lsize * 32)	\
-			cache##lsize##_unroll32(addr|ws, indexop);	\
+			cache##lsize##_unroll32##fn_pfx(addr|ws, indexop);	\
 }
 
-__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 16, )
-__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 16, )
-__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 16, )
-__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 32, )
-__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 32, )
-__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I_Loongson2, 32, loongson2_)
-__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 32, )
-__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 64, )
-__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 64, )
-__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 64, )
-__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 128, )
-__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 128, )
-__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 128, )
-
-__BUILD_BLAST_CACHE(inv_d, dcache, Index_Writeback_Inv_D, Hit_Invalidate_D, 16, )
-__BUILD_BLAST_CACHE(inv_d, dcache, Index_Writeback_Inv_D, Hit_Invalidate_D, 32, )
-__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 16, )
-__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 32, )
-__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 64, )
-__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 128, )
+__BUILD_BLAST_CACHE(, d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 16, )
+__BUILD_BLAST_CACHE(, i, icache, Index_Invalidate_I, Hit_Invalidate_I, 16, )
+__BUILD_BLAST_CACHE(, s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 16, )
+__BUILD_BLAST_CACHE(, d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 32, )
+__BUILD_BLAST_CACHE(, i, icache, Index_Invalidate_I, Hit_Invalidate_I, 32, )
+__BUILD_BLAST_CACHE(, i, icache, Index_Invalidate_I, Hit_Invalidate_I_Loongson2, 32, loongson2_)
+__BUILD_BLAST_CACHE(, s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 32, )
+__BUILD_BLAST_CACHE(_d, d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 64, )
+__BUILD_BLAST_CACHE(_i, i, icache, Index_Invalidate_I, Hit_Invalidate_I, 64, )
+__BUILD_BLAST_CACHE(, s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 64, )
+__BUILD_BLAST_CACHE(, d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 128, )
+__BUILD_BLAST_CACHE(, i, icache, Index_Invalidate_I, Hit_Invalidate_I, 128, )
+__BUILD_BLAST_CACHE(, s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 128, )
+
+__BUILD_BLAST_CACHE(, inv_d, dcache, Index_Writeback_Inv_D, Hit_Invalidate_D, 16, )
+__BUILD_BLAST_CACHE(, inv_d, dcache, Index_Writeback_Inv_D, Hit_Invalidate_D, 32, )
+__BUILD_BLAST_CACHE(, inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 16, )
+__BUILD_BLAST_CACHE(, inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 32, )
+__BUILD_BLAST_CACHE(, inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 64, )
+__BUILD_BLAST_CACHE(, inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 128, )
 
 #define __BUILD_BLAST_USER_CACHE(pfx, desc, indexop, hitop, lsize) \
 static inline void blast_##pfx##cache##lsize##_user_page(unsigned long page) \
@@ -617,7 +748,7 @@ __BUILD_BLAST_USER_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D,
 __BUILD_BLAST_USER_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 64)
 
 /* build blast_xxx_range, protected_blast_xxx_range */
-#define __BUILD_BLAST_CACHE_RANGE(pfx, desc, hitop, prot, extra)	\
+#define __BUILD_BLAST_CACHE_RANGE(fn_pfx, pfx, desc, hitop, prot, extra)	\
 static inline void prot##extra##blast_##pfx##cache##_range(unsigned long start, \
 						    unsigned long end)	\
 {									\
@@ -626,7 +757,7 @@ static inline void prot##extra##blast_##pfx##cache##_range(unsigned long start,
 	unsigned long aend = (end - 1) & ~(lsize - 1);			\
 									\
 	while (1) {							\
-		prot##cache_op(hitop, addr);				\
+		prot##cache_op##fn_pfx(hitop, addr);			\
 		if (addr == aend)					\
 			break;						\
 		addr += lsize;						\
@@ -635,8 +766,8 @@ static inline void prot##extra##blast_##pfx##cache##_range(unsigned long start,
 
 #ifndef CONFIG_EVA
 
-__BUILD_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D, protected_, )
-__BUILD_BLAST_CACHE_RANGE(i, icache, Hit_Invalidate_I, protected_, )
+__BUILD_BLAST_CACHE_RANGE(_d, d, dcache, Hit_Writeback_Inv_D, protected_, )
+__BUILD_BLAST_CACHE_RANGE(_i, i, icache, Hit_Invalidate_I, protected_, )
 
 #else
 
@@ -670,15 +801,15 @@ __BUILD_PROT_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D)
 __BUILD_PROT_BLAST_CACHE_RANGE(i, icache, Hit_Invalidate_I)
 
 #endif
-__BUILD_BLAST_CACHE_RANGE(s, scache, Hit_Writeback_Inv_SD, protected_, )
-__BUILD_BLAST_CACHE_RANGE(i, icache, Hit_Invalidate_I_Loongson2, \
+__BUILD_BLAST_CACHE_RANGE(_s, s, scache, Hit_Writeback_Inv_SD, protected_, )
+__BUILD_BLAST_CACHE_RANGE(_i, i, icache, Hit_Invalidate_I_Loongson2, \
 	protected_, loongson2_)
-__BUILD_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D, , )
-__BUILD_BLAST_CACHE_RANGE(i, icache, Hit_Invalidate_I, , )
-__BUILD_BLAST_CACHE_RANGE(s, scache, Hit_Writeback_Inv_SD, , )
+__BUILD_BLAST_CACHE_RANGE(_d, d, dcache, Hit_Writeback_Inv_D, , )
+__BUILD_BLAST_CACHE_RANGE(_i, i, icache, Hit_Invalidate_I, , )
+__BUILD_BLAST_CACHE_RANGE(_s, s, scache, Hit_Writeback_Inv_SD, , )
 /* blast_inv_dcache_range */
-__BUILD_BLAST_CACHE_RANGE(inv_d, dcache, Hit_Invalidate_D, , )
-__BUILD_BLAST_CACHE_RANGE(inv_s, scache, Hit_Invalidate_SD, , )
+__BUILD_BLAST_CACHE_RANGE(_d, inv_d, dcache, Hit_Invalidate_D, , )
+__BUILD_BLAST_CACHE_RANGE(_d, inv_s, scache, Hit_Invalidate_SD, , )
 
 /* Currently, this is very specific to Loongson-3 */
 #define __BUILD_BLAST_CACHE_NODE(pfx, desc, indexop, hitop, lsize)	\
arch/mips/mm/c-r4k.c +4 -4
index 4f2cecbec722..ae79a78f3002 100644
@@ -1557,14 +1557,14 @@ static int probe_scache(void)
 	write_c0_taglo(0);
 	write_c0_taghi(0);
 	__asm__ __volatile__("nop; nop; nop; nop;"); /* avoid the hazard */
-	cache_op(Index_Store_Tag_I, begin);
-	cache_op(Index_Store_Tag_D, begin);
-	cache_op(Index_Store_Tag_SD, begin);
+	cache_op_i(Index_Store_Tag_I, begin);
+	cache_op_d(Index_Store_Tag_D, begin);
+	cache_op_s(Index_Store_Tag_SD, begin);
 
 	/* Now search for the wrap around point. */
 	pow2 = (128 * 1024);
 	for (addr = begin + (128 * 1024); addr < end; addr = begin + pow2) {
-		cache_op(Index_Load_Tag_SD, addr);
+		cache_op_s(Index_Load_Tag_SD, addr);
 		__asm__ __volatile__("nop; nop; nop; nop;"); /* hazard... */
 		if (!read_c0_taglo())
 			break;
Signature · 1 line
2.21.0
#33 lore raw

PATCH 20/120 MIPS: R5900: Define CP0.Config register fields

In reply to Fredrik Noring

The following CP0.Config fields are specific to the R5900[1]:

Field | Bit | Type | Description
------+-----+------+-----------------------------
DIE | 18 | RW | Enable double issue
ICE | 17 | RW | Enable the instruction cache
DCE | 16 | RW | Enable the data cache
BE | 15 | RO | Enable big-endian
NBE | 13 | RW | Enable nonblocking load
BPE | 12 | RW | Enable branch prediction
------+-----+------+-----------------------------

References:

[1] "TX System RISC TX79 Core Architecture" manual, revision 2.0,

    Toshiba Corporation, p. 4-23, https://wiki.qemu.org/File:C790.pdf
---
 arch/mips/include/asm/mipsregs.h | 8 ++++++++
 1 file changed, 8 insertions(+)
arch/mips/include/asm/mipsregs.h +8 -0
index ec22406c800f..a3b3ee011539 100644
@@ -511,6 +511,14 @@
 #define R5K_CONF_SE		(_ULCAST_(1) << 12)
 #define R5K_CONF_SS		(_ULCAST_(3) << 20)
 
+/* Bits specific to the R5900.	*/
+#define R5900_CONF_BPE		(_ULCAST_(1) << 12)	/* Enable branch prediction. */
+#define R5900_CONF_NBE		(_ULCAST_(1) << 13)	/* Enable non-blocking load. */
+#define R5900_CONF_BE		(_ULCAST_(1) << 15)	/* Enable big-endian (read-only). */
+#define R5900_CONF_DCE		(_ULCAST_(1) << 16)	/* Enable the data cache. */
+#define R5900_CONF_ICE		(_ULCAST_(1) << 17)	/* Enable the instruction cache. */
+#define R5900_CONF_DIE		(_ULCAST_(1) << 18)	/* Enable double issue. */
+
 /* Bits specific to the RM7000.	 */
 #define RM7K_CONF_SE		(_ULCAST_(1) <<	 3)
 #define RM7K_CONF_TE		(_ULCAST_(1) << 12)
Signature · 1 line
2.21.0
Reviewed-by on v1 20/120
#34 lore raw

Re: [PATCH 020/120] MIPS: R5900: Define CP0.Config register fields

Quoted 32 lines · Fredrik Noring

The following CP0.Config fields are specific to the R5900[1]:

Field | Bit | Type | Description
------+-----+------+-----------------------------
DIE | 18 | RW | Enable double issue
ICE | 17 | RW | Enable the instruction cache
DCE | 16 | RW | Enable the data cache
BE | 15 | RO | Enable big-endian
NBE | 13 | RW | Enable nonblocking load
BPE | 12 | RW | Enable branch prediction
------+-----+------+-----------------------------

References:

[1] "TX System RISC TX79 Core Architecture" manual, revision 2.0,

    Toshiba Corporation, p. 4-23, https://wiki.qemu.org/File:C790.pdf
---
 arch/mips/include/asm/mipsregs.h | 8 ++++++++
 1 file changed, 8 insertions(+)
arch/mips/include/asm/mipsregs.h +1 -0
index ec22406c800f..a3b3ee011539 100644
@@ -511,6 +511,14 @@
 #define R5K_CONF_SE		(_ULCAST_(1) << 12)
 #define R5K_CONF_SS		(_ULCAST_(3) << 20)
 
+/* Bits specific to the R5900.	*/

arch/mips/include/asm/mipsregs.h line 524

Quoted 10 lines
arch/mips/include/asm/mipsregs.h +7 -0
+#define R5900_CONF_BPE		(_ULCAST_(1) << 12)	/* Enable branch prediction. */
+#define R5900_CONF_NBE		(_ULCAST_(1) << 13)	/* Enable non-blocking load. */
+#define R5900_CONF_BE		(_ULCAST_(1) << 15)	/* Enable big-endian (read-only). */
+#define R5900_CONF_DCE		(_ULCAST_(1) << 16)	/* Enable the data cache. */
+#define R5900_CONF_ICE		(_ULCAST_(1) << 17)	/* Enable the instruction cache. */
+#define R5900_CONF_DIE		(_ULCAST_(1) << 18)	/* Enable double issue. */
+
 /* Bits specific to the RM7000.	 */
 #define RM7K_CONF_SE		(_ULCAST_(1) <<	 3)
 #define RM7K_CONF_TE		(_ULCAST_(1) << 12)
#35 lore raw

PATCH 21/120 MIPS: R5900: Workaround for CACHE instruction near branch delay slot

In reply to Fredrik Noring

---
The Linux 2.6 port to the PlayStation 2 has this remark. I don't know where it comes from.

---
 arch/mips/kernel/traps.c | 11 +++++++++++
 1 file changed, 11 insertions(+)
arch/mips/kernel/traps.c +11 -0
index 9c98475c7dc6..647a1990163a 100644
@@ -1967,6 +1967,17 @@ void __init *set_except_vector(int n, void *addr)
 			uasm_i_jr(&buf, k0);
 			uasm_i_nop(&buf);
 		}
+#ifdef CONFIG_CPU_R5900
+		/*
+		 * Data that could be interpreted as cache instructions
+		 * is not allowed after the jump.
+		 */
+		uasm_i_nop(&buf);
+		uasm_i_nop(&buf);
+		uasm_i_nop(&buf);
+		uasm_i_nop(&buf);
+		uasm_i_nop(&buf);
+#endif
 		local_flush_icache_range(ebase + 0x200, (unsigned long)buf);
 	}
 	return (void *)old_handler;
Signature · 1 line
2.21.0
#36 lore raw

PATCH 22/120 MIPS: R5900: Support 64-bit inq() and outq() macros in 32-bit kernels

In reply to Fredrik Noring

PlayStation 2 hardware such as the Graphics Synthesizer requires 64-bit register reads and writes[1], also in 32-bit kernels. Interrupts must be disabled when manipulating 64-bit registers unless the kernel saves and restores 64-bit registers in the interrupt and context switch handlers.

References:

[1] "EE User's Manual", version 6.0, Sony Computer Entertainment Inc., p. 26: GS privileged registers must be accessed using LD/SD instructions.

---
 arch/mips/include/asm/io.h | 54 +++++++++++++++++++++++++++++++++-----
 1 file changed, 47 insertions(+), 7 deletions(-)
arch/mips/include/asm/io.h +47 -7
index 97a280640daf..f7115472f530 100644
@@ -420,10 +420,32 @@ static inline void pfx##out##bwlq##p(type val, unsigned long port)	\
 									\
 	__val = pfx##ioswab##bwlq(__addr, val);				\
 									\
-	/* Really, we want this to be atomic */				\
-	BUILD_BUG_ON(sizeof(type) > sizeof(unsigned long));		\
+	if (sizeof(type) != sizeof(u64) || sizeof(u64) == sizeof(long))	{ \
+		*__addr = __val;					\
+	} else if (cpu_has_64bits) {					\
+		unsigned long __flags;					\
+		type __tmp;						\
 									\
-	*__addr = __val;						\
+		/* Manipulating 64-bit registers in a 32-bit kernel */	\
+		/* requires disabling interrupts, since only 32-bit */	\
+		/* registers are saved/restored by interrupts. */	\
+		local_irq_save(__flags);				\
+		__asm__ __volatile__(					\
+			".set	push"		"\t\t# __writeq""\n\t"	\
+			".set	mips3"				"\n\t"	\
+			"dsll32	%L0, %L0, 0"			"\n\t"	\
+			"dsrl32	%L0, %L0, 0"			"\n\t"	\
+			"dsll32	%M0, %M0, 0"			"\n\t"	\
+			"or	%L0, %L0, %M0"			"\n\t"	\
+			"sd	%L0, %2"			"\n\t"	\
+			"sll	%L0, %L0, 0"			"\n\t"	\
+			"sll	%M0, %M0, 0"			"\n\t"	\
+			".set	pop"				"\n"	\
+			: "=r" (__tmp)					\
+			: "0" (__val), "m" (*__addr));			\
+		local_irq_restore(__flags);				\
+	} else								\
+		BUG();							\
 }									\
 									\
 static inline type pfx##in##bwlq##p(unsigned long port)			\
@@ -433,12 +455,30 @@ static inline type pfx##in##bwlq##p(unsigned long port)			\
 									\
 	__addr = (void *)__swizzle_addr_##bwlq(mips_io_port_base + port); \
 									\
-	BUILD_BUG_ON(sizeof(type) > sizeof(unsigned long));		\
-									\
 	if (barrier)							\
 		iobarrier_rw();						\
 									\
-	__val = *__addr;						\
+	if (sizeof(type) != sizeof(u64) || sizeof(u64) == sizeof(long))	{ \
+		__val = *__addr;					\
+	} else if (cpu_has_64bits) {					\
+		unsigned long __flags;					\
+									\
+		/* Manipulating 64-bit registers in a 32-bit kernel */	\
+		/* requires disabling interrupts, since only 32-bit */	\
+		/* registers are saved/restored by interrupts. */	\
+		local_irq_save(__flags);				\
+		__asm__ __volatile__(					\
+			".set	push"		"\t\t# __outq"	"\n\t"	\
+			".set	mips3"				"\n\t"	\
+			"ld	%L0, %1"			"\n\t"	\
+			"dsra32	%M0, %L0, 0"			"\n\t"	\
+			"sll	%L0, %L0, 0"			"\n\t"	\
+			".set	pop"				"\n"	\
+			: "=r" (__val)					\
+			: "m" (*__addr));				\
+		local_irq_restore(__flags);				\
+	} else								\
+		BUG();							\
 									\
 	/* prevent prefetching of coherent DMA data prematurely */	\
 	if (!relax)							\
@@ -478,7 +518,7 @@ __BUILD_MEMORY_PFX(__mem_, q, u64, 0)
 BUILDIO_IOPORT(b, u8)
 BUILDIO_IOPORT(w, u16)
 BUILDIO_IOPORT(l, u32)
-#ifdef CONFIG_64BIT
+#if defined(CONFIG_64BIT) || defined(CONFIG_CPU_R5900)
 BUILDIO_IOPORT(q, u64)
 #endif
 
Signature · 1 line
2.21.0
maintainer
#37 lore raw

Re: [PATCH 022/120] MIPS: R5900: Support 64-bit inq() and outq() macros in 32-bit kernels

Quoted 4 lines · Fredrik Noring

PlayStation 2 hardware such as the Graphics Synthesizer requires 64-bit register reads and writes[1], also in 32-bit kernels. Interrupts must be disabled when manipulating 64-bit registers unless the kernel saves and restores 64-bit registers in the interrupt and context switch handlers.

Hi Fredrik,

Why don't we just build a 64bit kernel rather than do these hacks?

Any hardware/firmware issue blocks 64bit kernel?

--

Jiaxun Yang

Quoted 8 lines

References:

[1] "EE User's Manual", version 6.0, Sony Computer Entertainment Inc., p. 26: GS privileged registers must be accessed using LD/SD instructions.

---

#38 lore raw

Re: [PATCH 022/120] MIPS: R5900: Support 64-bit inq() and outq() macros in 32-bit kernels

Quoted 10 lines · Jiaxun Yang
Quoted 4 lines

PlayStation 2 hardware such as the Graphics Synthesizer requires 64-bit register reads and writes[1], also in 32-bit kernels. Interrupts must be disabled when manipulating 64-bit registers unless the kernel saves and restores 64-bit registers in the interrupt and context switch handlers.

Hi Fredrik,

Why don't we just build a 64bit kernel rather than do these hacks?

Any hardware/firmware issue blocks 64bit kernel?

One issue I recall with the R5900 is $pc is 32-bit (as if with
CP0.Status.PX=1 on modern hardware) and is zero- rather than sign-extended when copied to a GPR with instructions like JALR, BLTZAL, etc. This is contrary to MIPS architecture requirements and unlike other 64-bit MIPS processors.

This does not cause an issue in the user mode (because USEG addresses
have bit #31 set to 0 anyway) or with the JR and JALR instructions (because these ignore the upper 32 bits in the source GPR). This however breaks in the kernel mode whenever the value retrieved from $pc is used for any kind of calculation, meaning that the kernel will have to be audited for such use before we can run 64-bit kernel code.

This means it's much easier to have a 32-bit kernel running, with a small
and simple sanitanisation change posted as 003/120, and have all the platform and driver infrastructure verified before this processor quirk is addressed.

Maciej

#39 lore raw

PATCH 23/120 MIPS: R5900: Add MFSA and MTSA instructions for the special SA register

In reply to Fredrik Noring

The shift amount (SA) register is a 64-bit special register storing the funnel shift amount. The SA is encoded an implementation-defined manner. It is therefore not meaningful for software to operate on this value. Use the MTSAB and MTSAH instructions to set a new funnel shift amount.

The SA is used by the QFSRV (quadword funnel shift right variable) 256-bit multimedia instruction.

MFSA copies the SA register to a 64-bit GPR[1]. The sole purpose of this instruction is to permit the shift amount to be saved during a context switch.

MTSA copies a 64-bit GPR rs to the SA register[2]. Note that rs must contain a value that was originally generated by MFSA. If some other user-generated value is in rs, the shifting action performed by the funnel shifter is not defined; that is, MTSA cannot be used to by a program to set a new funnel shift amount. The sole purpose of this instruction is to permit the shift amount to be restored during a context switch. Restrictions:

The three instructions statically preceding a MTSA instruction must not read or write the SA register; that is, they cannot be either of the instructions MFSA, QFSRV, or MTSAx.

References:

[1] "TX System RISC TX79 Core Architecture" manual, revision 2.0,

    Toshiba Corporation, p. B-17, https://wiki.qemu.org/File:C790.pdf

[2] Ibid. p. B-20.

---
 arch/mips/include/asm/mipsregs.h | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)
arch/mips/include/asm/mipsregs.h +22 -0
index a3b3ee011539..d8c1ffac2824 100644
@@ -2594,6 +2594,28 @@ do {									\
 
 #else
 
+#ifdef CONFIG_CPU_R5900
+#define mfsa()								\
+({									\
+	unsigned long __treg;	/* FIXME: __u64? */			\
+									\
+	__asm__ __volatile__(						\
+	"	mfsa	%0\n"						\
+	: "=r" (__treg));						\
+	__treg;								\
+})
+
+#define mtsa(x)								\
+do {									\
+	unsigned long __treg = (x);/* FIXME: __u64? */			\
+									\
+	__asm__ __volatile__(						\
+	"	mtsa	%0\n"						\
+	:								\
+	: "r" (__treg));						\
+} while (0)
+#endif
+
 #define rddsp(mask)							\
 ({									\
 	unsigned int __res;						\
Signature · 1 line
2.21.0
#40 lore raw

PATCH 24/120 MIPS: PS2: Define PlayStation 2 I/O port, ROM and RAM address spaces

In reply to Fredrik Noring
---
 arch/mips/include/asm/mach-ps2/rom.h | 17 +++++++++++++++++
 arch/mips/ps2/Makefile               |  1 +
 arch/mips/ps2/memory.c               | 27 +++++++++++++++++++++++++++
 3 files changed, 45 insertions(+)
 create mode 100644 arch/mips/include/asm/mach-ps2/rom.h
 create mode 100644 arch/mips/ps2/Makefile
 create mode 100644 arch/mips/ps2/memory.c
arch/mips/include/asm/mach-ps2/rom.h new file +17 -0
new file mode 100644
index 000000000000..6760be183696
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 read-only memory (ROM)
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#ifndef __ASM_MACH_PS2_ROM_H
+#define __ASM_MACH_PS2_ROM_H
+
+#define ROM0_BASE	0x1fc00000	/* ROM0 base address (boot) */
+#define ROM0_SIZE	0x400000	/* ROM0 maximum size */
+
+#define ROM1_BASE	0x1e000000	/* ROM1 base address (DVD) */
+#define ROM1_SIZE	0x100000	/* ROM1 maximum size */
+
+#endif /* __ASM_MACH_PS2_ROM_H */
arch/mips/ps2/Makefile new file +1 -0
new file mode 100644
index 000000000000..24d537d2fb9f
@@ -0,0 +1 @@
+obj-y		+= memory.o
arch/mips/ps2/memory.c new file +27 -0
new file mode 100644
index 000000000000..66ca37f38330
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 memory
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+
+#include <asm/bootinfo.h>
+
+void __init plat_mem_setup(void)
+{
+	ioport_resource.start = 0x10000000;
+	ioport_resource.end   = 0x1fffffff;
+
+	iomem_resource.start = 0x00000000;
+	iomem_resource.end   = KSEG2 - 1;
+
+	add_memory_region(0x00000000, 0x02000000, BOOT_MEM_RAM);
+	add_memory_region(ROM0_BASE, ROM0_SIZE, BOOT_MEM_ROM_DATA);
+	add_memory_region(ROM1_BASE, ROM1_SIZE, BOOT_MEM_ROM_DATA);
+
+	set_io_port_base(CKSEG1);	/* KSEG1 is uncached */
+}
Signature · 1 line
2.21.0
#41 lore raw

PATCH 25/120 MIPS: PS2: Define interrupt controller, DMA and timer IRQs

In reply to Fredrik Noring

The interrupt controller (INTC) arbitrates 15 interrupt requests from multiple processors and asserts INT0[1].

The DMA controller (DMAC) asserts INT1[2].

References:

[1] "EE User's Manual", version 6.0, Sony Computer Entertainment Inc.,

    pp. 27-31.

[2] Ibid. p. 55.

---
 arch/mips/include/asm/mach-ps2/irq.h | 74 ++++++++++++++++++++++++++++
 arch/mips/ps2/Makefile               |  1 +
 arch/mips/ps2/irq.c                  | 35 +++++++++++++
 3 files changed, 110 insertions(+)
 create mode 100644 arch/mips/include/asm/mach-ps2/irq.h
 create mode 100644 arch/mips/ps2/irq.c
arch/mips/include/asm/mach-ps2/irq.h new file +74 -0
new file mode 100644
index 000000000000..d6f72a7e37a3
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 IRQs
+ *
+ * Copyright (C) 2000-2002 Sony Computer Entertainment Inc.
+ * Copyright (C) 2010-2013 Jürgen Urban
+ * Copyright (C)      2019 Fredrik Noring
+ */
+
+#ifndef __ASM_MACH_PS2_IRQ_H
+#define __ASM_MACH_PS2_IRQ_H
+
+#define INTC_STAT	0x1000f000	/* Flags are cleared by writing 1 */
+#define INTC_MASK	0x1000f010	/* Bits are reversed by writing 1 */
+
+#define NR_IRQS		56
+
+/*
+ * The interrupt controller (INTC) arbitrates interrupts from peripheral
+ * devices, except for the DMAC.
+ */
+#define IRQ_INTC	0
+#define IRQ_INTC_GS	0	/* Graphics Synthesizer */
+#define IRQ_INTC_SBUS	1	/* Bus connecting the Emotion Engine to the
+				   I/O processor (IOP) via the sub-system
+				   interface (SIF) */
+#define IRQ_INTC_VB_ON	2	/* Vertical blank start */
+#define IRQ_INTC_VB_OFF	3	/* Vertical blank end */
+#define IRQ_INTC_VIF0	4	/* VPU0 interface packet expansion engine */
+#define IRQ_INTC_VIF1	5	/* VPU1 interface packet expansion engine */
+#define IRQ_INTC_VU0	6	/* Vector core operation unit 0 */
+#define IRQ_INTC_VU1	7	/* Vector core operation unit 1 */
+#define IRQ_INTC_IPU	8	/* Image processor unit (MPEG 2 video etc.) */
+#define IRQ_INTC_TIMER0	9	/* Independent screen timer 0 */
+#define IRQ_INTC_TIMER1	10	/* Independent screen timer 1 */
+#define IRQ_INTC_TIMER2	11	/* Independent screen timer 2 */
+#define IRQ_INTC_TIMER3	12	/* Independent screen timer 3 */
+#define IRQ_INTC_SFIFO	13	/* Error detected during SFIFO transfers */
+#define IRQ_INTC_VU0WD	14	/* VU0 watch dog for RUN (sends force break) */
+#define IRQ_INTC_PGPU	15
+
+/* DMA controller */
+#define IRQ_DMAC	16
+#define IRQ_DMAC_VIF0	16	/* Ch0 VPU0 interface (VIF0) */
+#define IRQ_DMAC_VIF1	17	/* Ch1 VPU1 interface (VIF1) */
+#define IRQ_DMAC_GIF	18	/* Ch2 Graphics Synthesizer interface (GIF) */
+#define IRQ_DMAC_FIPU	19	/* Ch3 from image processor unit (IPU) */
+#define IRQ_DMAC_TIPU	20	/* Ch4 to image processor unit (IPU) */
+#define IRQ_DMAC_SIF0	21	/* Ch5 sub-system interface 0 (SIF0) */
+#define IRQ_DMAC_SIF1	22	/* Ch6 sub-system interface 1 (SIF1) */
+#define IRQ_DMAC_SIF2	23	/* Ch7 Sub-system interface 2 (SIF2) */
+#define IRQ_DMAC_FSPR	24	/* Ch8 from scratch-pad RAM (SPR) */
+#define IRQ_DMAC_TSPR	25	/* Ch9 to scratch-pad RAM (SPR) */
+#define IRQ_DMAC_S	29	/* DMA stall */
+#define IRQ_DMAC_ME	30	/* MFIFO empty */
+#define IRQ_DMAC_BE	31	/* Bus error */
+
+/* Graphics Synthesizer */
+#define IRQ_GS		32
+#define IRQ_GS_SIGNAL	32	/* GS signal event control */
+#define IRQ_GS_FINISH	33	/* GS finish event control */
+#define IRQ_GS_HSYNC	34	/* GS horizontal synch interrupt control */
+#define IRQ_GS_VSYNC	35	/* GS vertical synch interrupt control */
+#define IRQ_GS_EDW	36	/* GS rectangular area write termination */
+#define IRQ_GS_EXHSYNC	37
+#define IRQ_GS_EXVSYNC	38
+
+/* MIPS IRQs */
+#define MIPS_CPU_IRQ_BASE 48
+#define IRQ_C0_INTC	50
+#define IRQ_C0_DMAC	51
+#define IRQ_C0_IRQ7	55
+
+#endif /* __ASM_MACH_PS2_IRQ_H */
arch/mips/ps2/Makefile +1 -0
index 24d537d2fb9f..d5d089c61381 100644
@@ -1 +1,2 @@
+obj-y		+= irq.o
 obj-y		+= memory.o
arch/mips/ps2/irq.c new file +35 -0
new file mode 100644
index 000000000000..09047e128ce8
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 IRQs
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+
+#include <asm/irq_cpu.h>
+#include <asm/mipsregs.h>
+
+#include <asm/mach-ps2/irq.h>
+
+void __init arch_init_irq(void)
+{
+	mips_cpu_irq_init();
+}
+
+asmlinkage void plat_irq_dispatch(void)
+{
+	const unsigned int pending = read_c0_status() & read_c0_cause();
+
+	if (!(pending & (CAUSEF_IP2 | CAUSEF_IP3 | CAUSEF_IP7)))
+		return spurious_interrupt();
+
+	if (pending & CAUSEF_IP2)
+		do_IRQ(IRQ_C0_INTC);	/* INTC interrupt */
+	if (pending & CAUSEF_IP3)
+		do_IRQ(IRQ_C0_DMAC);	/* DMAC interrupt */
+	if (pending & CAUSEF_IP7)
+		do_IRQ(IRQ_C0_IRQ7);	/* Timer interrupt */
+}
Signature · 1 line
2.21.0
#42 lore raw

PATCH 26/120 MIPS: PS2: Interrupt controller (INTC) IRQ support

In reply to Fredrik Noring

---
I'm not sure edges or HARDIRQS_SW_RESEND etc. are needed.

---
 arch/mips/include/asm/mach-ps2/irq.h |   2 +
 arch/mips/ps2/Makefile               |   1 +
 arch/mips/ps2/intc-irq.c             | 112 +++++++++++++++++++++++++++
 arch/mips/ps2/irq.c                  |   2 +
 4 files changed, 117 insertions(+)
 create mode 100644 arch/mips/ps2/intc-irq.c
arch/mips/include/asm/mach-ps2/irq.h +2 -0
index d6f72a7e37a3..071c8139dabe 100644
@@ -71,4 +71,6 @@
 #define IRQ_C0_DMAC	51
 #define IRQ_C0_IRQ7	55
 
+int __init intc_irq_init(void);
+
 #endif /* __ASM_MACH_PS2_IRQ_H */
arch/mips/ps2/Makefile +1 -0
index d5d089c61381..ccdfb80c9f03 100644
@@ -1,2 +1,3 @@
+obj-y		+= intc-irq.o
 obj-y		+= irq.o
 obj-y		+= memory.o
arch/mips/ps2/intc-irq.c new file +112 -0

112 lines not on this page · raw message

arch/mips/ps2/irq.c +2 -0
index 09047e128ce8..935171a1e3bd 100644
@@ -17,6 +17,8 @@
 void __init arch_init_irq(void)
 {
 	mips_cpu_irq_init();
+
+	intc_irq_init();
 }
 
 asmlinkage void plat_irq_dispatch(void)
Signature · 1 line
2.21.0
#43 lore raw

PATCH 27/120 MIPS: PS2: DMAC: Define DMA controller registers

In reply to Fredrik Noring

The DMA controller handles transfers between main memory and peripheral devices or the scratch-pad RAM (SPR)[1].

References:

[1] "EE User's Manual", version 6.0, Sony Computer Entertainment Inc.,

    pp. 23-24, 41-80.
---
 arch/mips/include/asm/mach-ps2/dmac.h | 189 ++++++++++++++++++++++++++
 1 file changed, 189 insertions(+)
 create mode 100644 arch/mips/include/asm/mach-ps2/dmac.h
arch/mips/include/asm/mach-ps2/dmac.h new file +189 -0
new file mode 100644
index 000000000000..30a0f72eeab3
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 DMA controller (DMAC)
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+/**
+ * DOC:
+ *
+ * The DMA controller handles transfers between main memory and peripheral
+ * devices or the scratch-pad RAM (SPR).
+ *
+ * The DMAC arbitrates the main bus at the same time, and supports chain
+ * mode which switches transfer addresses according to DMA tags attached to
+ * the transfer. The stall control synchronises two-channel transfers with
+ * priority control.
+ *
+ * Data is transferred in 128-bit words that must be aligned. Bus snooping
+ * is not performed.
+ */
+
+#ifndef __ASM_MACH_PS2_DMAC_H
+#define __ASM_MACH_PS2_DMAC_H
+
+#include <asm/types.h>
+
+/* Channel 0: Vector core operation unit 0 (VU0) interface (VIF0) */
+#define DMAC_VIF0_CHCR		0x10008000	/* VIF0 channel control */
+#define DMAC_VIF0_MADR		0x10008010	/* VIF0 memory address */
+#define DMAC_VIF0_QWC		0x10008020	/* VIF0 quadword count */
+#define DMAC_VIF0_TADR		0x10008030	/* VIF0 tag address */
+#define DMAC_VIF0_ASR0		0x10008040	/* VIF0 address stack 0 */
+#define DMAC_VIF0_ASR1		0x10008050	/* VIF0 address stack 1 */
+
+/* Channel 1: Vector core operation unit 1 (VU1) interface (VIF1) */
+#define DMAC_VIF1_CHCR		0x10009000	/* VIF1 channel control */
+#define DMAC_VIF1_MADR		0x10009010	/* VIF1 memory address */
+#define DMAC_VIF1_QWC		0x10009020	/* VIF1 quadword count */
+#define DMAC_VIF1_TADR		0x10009030	/* VIF1 tag address */
+#define DMAC_VIF1_ASR0		0x10009040	/* VIF1 address stack 0 */
+#define DMAC_VIF1_ASR1		0x10009050	/* VIF1 address stack 1 */
+
+/* Channel 2: Graphics Synthesizer interface (GIF) */
+#define DMAC_GIF_CHCR		0x1000a000	/* GIF channel control */
+#define DMAC_GIF_MADR		0x1000a010	/* GIF memory address */
+#define DMAC_GIF_QWC		0x1000a020	/* GIF quadword count */
+#define DMAC_GIF_TADR		0x1000a030	/* GIF tag address */
+#define DMAC_GIF_ASR0		0x1000a040	/* GIF address stack 0 */
+#define DMAC_GIF_ASR1		0x1000a050	/* GIF address stack 1 */
+
+/* Channel 3: From image processor unit (IPU) */
+#define DMAC_FIPU_CHCR		0x1000b000	/* From IPU channel control */
+#define DMAC_FIPU_MADR		0x1000b010	/* From IPU memory address */
+#define DMAC_FIPU_QWC		0x1000b020	/* From IPU quadword count */
+
+/* Channel 4: To image processor unit (IPU) */
+#define DMAC_TIPU_CHCR		0x1000b400	/* To IPU channel control */
+#define DMAC_TIPU_MADR		0x1000b410	/* To IPU memory address */
+#define DMAC_TIPU_QWC		0x1000b420	/* To IPU quadword count */
+#define DMAC_TIPU_TADR		0x1000b430	/* To IPU tag address */
+
+/* Channel 5: Sub-system interface 0 (SIF0) */
+#define DMAC_SIF0_CHCR		0x1000c000	/* SIF0 channel control */
+#define DMAC_SIF0_MADR		0x1000c010	/* SIF0 memory address */
+#define DMAC_SIF0_QWC		0x1000c020	/* SIF0 quadword count */
+
+/* Channel 6: Sub-system interface 1 (SIF1) */
+#define DMAC_SIF1_CHCR		0x1000c400	/* SIF1 channel control */
+#define DMAC_SIF1_MADR		0x1000c410	/* SIF1 memory address */
+#define DMAC_SIF1_QWC		0x1000c420	/* SIF1 quadword count */
+#define DMAC_SIF1_TADR		0x1000c430	/* SIF1 tag address */
+
+/* Channel 7: Sub-system interface 2 (SIF2) */
+#define DMAC_SIF2_CHCR		0x1000c800	/* SIF2 channel control */
+#define DMAC_SIF2_MADR		0x1000c810	/* SIF2 memory address */
+#define DMAC_SIF2_QWC		0x1000c820	/* SIF2 quadword count */
+
+/* Channel 8: From scratch-pad RAM (SPR) */
+#define DMAC_FSPR_CHCR		0x1000d000	/* From SPR channel control */
+#define DMAC_FSPR_MADR		0x1000d010	/* From SPR memory address */
+#define DMAC_FSPR_QWC		0x1000d020	/* From SPR quadword count */
+#define DMAC_FSPR_SADR		0x1000d080	/* From SPR address */
+
+/* Channel 9: To scratch-pad RAM (SPR) */
+#define DMAC_TSPR_CHCR		0x1000d400	/* To SPR channel control */
+#define DMAC_TSPR_MADR		0x1000d410	/* To SPR memory address */
+#define DMAC_TSPR_QWC		0x1000d420	/* To SPR quadword count */
+#define DMAC_TSPR_TADR		0x1000d430	/* To SPR tag address */
+#define DMAC_TSPR_SADR		0x1000d480	/* To SPR address */
+
+#define DMAC_CHCR_DIR_TOMEM	(0 << 0)	/* Direction to memory */
+#define DMAC_CHCR_DIR_FROMMEM	(1 << 0)	/* Direction from memory */
+#define DMAC_CHCR_MOD_NORMAL	(0 << 2)	/* Mode normal */
+#define DMAC_CHCR_MOD_CHAIN	(1 << 2)	/* Mode chain */
+#define DMAC_CHCR_MOD_ILEAVE	(2 << 2)	/* Mode interleave */
+#define DMAC_CHCR_ASP_NONE	(0 << 4)	/* 0 address stack pointer */
+#define DMAC_CHCR_ASP_1ADDR	(1 << 4)	/* 1 address stack pointer */
+#define DMAC_CHCR_ASP_2ADDR	(2 << 4)	/* 2 address stack pointer */
+#define DMAC_CHCR_TTE_OFF	(0 << 6)	/* Tag transfer enable off */
+#define DMAC_CHCR_TTE_ON	(1 << 6)	/* Tag transfer enable on */
+#define DMAC_CHCR_TIE_OFF	(0 << 7)	/* Tag interrupt enable off */
+#define DMAC_CHCR_TIE_ON	(1 << 7)	/* Tag interrupt enable on */
+#define DMAC_CHCR_STR_STOP	(0 << 8)	/* Stop DMA */
+#define DMAC_CHCR_STR_START	(1 << 8)	/* Start DMA */
+
+#define DMAC_CHCR_STOP		DMAC_CHCR_STR_STOP
+#define DMAC_CHCR_BUSY		DMAC_CHCR_STR_START
+#define DMAC_CHCR_SENDN		(DMAC_CHCR_DIR_FROMMEM	| \
+				 DMAC_CHCR_MOD_NORMAL	| \
+				 DMAC_CHCR_ASP_NONE	| \
+				 DMAC_CHCR_TTE_OFF	| \
+				 DMAC_CHCR_TIE_OFF	| \
+				 DMAC_CHCR_STR_START)
+#define DMAC_CHCR_SENDN_TIE	(DMAC_CHCR_DIR_FROMMEM	| \
+				 DMAC_CHCR_MOD_NORMAL	| \
+				 DMAC_CHCR_ASP_NONE	| \
+				 DMAC_CHCR_TTE_OFF	| \
+				 DMAC_CHCR_TIE_ON	| \
+				 DMAC_CHCR_STR_START)
+#define DMAC_CHCR_SENDC		(DMAC_CHCR_DIR_FROMMEM	| \
+				 DMAC_CHCR_MOD_CHAIN	| \
+				 DMAC_CHCR_ASP_NONE	| \
+				 DMAC_CHCR_TTE_OFF	| \
+				 DMAC_CHCR_TIE_OFF	| \
+				 DMAC_CHCR_STR_START)
+#define DMAC_CHCR_SENDC_TTE	(DMAC_CHCR_DIR_FROMMEM	| \
+				 DMAC_CHCR_MOD_CHAIN	| \
+				 DMAC_CHCR_ASP_NONE	| \
+				 DMAC_CHCR_TTE_ON	| \
+				 DMAC_CHCR_TIE_OFF	| \
+				 DMAC_CHCR_STR_START)
+#define DMAC_CHCR_RECVN		(DMAC_CHCR_DIR_TOMEM	| \
+				 DMAC_CHCR_MOD_NORMAL	| \
+				 DMAC_CHCR_ASP_NONE	| \
+				 DMAC_CHCR_TTE_OFF	| \
+				 DMAC_CHCR_TIE_OFF	| \
+				 DMAC_CHCR_STR_START)
+#define DMAC_CHCR_RECVC_TIE	(DMAC_CHCR_DIR_TOMEM	| \
+				 DMAC_CHCR_MOD_CHAIN	| \
+				 DMAC_CHCR_ASP_NONE	| \
+				 DMAC_CHCR_TTE_OFF	| \
+				 DMAC_CHCR_TIE_ON	| \
+				 DMAC_CHCR_STR_START)
+
+#define DMAC_CTRL	0x1000e000	/* DMAC control */
+#define DMAC_STAT	0x1000e010	/* DMAC status */
+#define DMAC_PCR	0x1000e020	/* DMAC priority control */
+#define DMAC_SQWC	0x1000e030	/* DMAC skip quadword */
+#define DMAC_RBSR	0x1000e040	/* DMAC ring buffer size */
+#define DMAC_RBOR	0x1000e050	/* DMAC ring buffer offset */
+#define DMAC_STADR	0x1000e060	/* DMAC stall address */
+
+/*
+ * The lower 16 bits are status bits and the upper 16 bits are mask bits.
+ * Status bit cleared by writing 1. Mask bits are reversed by writing 1.
+ */
+#define DMAC_STAT_MASK	0x1000e010
+
+#define DMAC_STAT_VIF0S	(1 << 0)	/* Ch0 interrupt status VIF0 */
+#define DMAC_STAT_VIF1S	(1 << 1)	/* Ch1 interrupt status VIF1 */
+#define DMAC_STAT_GIFS	(1 << 2)	/* Ch2 interrupt status GIF */
+#define DMAC_STAT_FIPUS	(1 << 3)	/* Ch3 interrupt status from IPU */
+#define DMAC_STAT_TIPUS	(1 << 4)	/* Ch4 interrupt status to IPU */
+#define DMAC_STAT_SIF0S	(1 << 5)	/* Ch5 interrupt status SIF0 */
+#define DMAC_STAT_SIF1S	(1 << 6)	/* Ch6 interrupt status SIF1 */
+#define DMAC_STAT_SIF2S	(1 << 7)	/* Ch7 interrupt status SIF2 */
+#define DMAC_STAT_FSPRS	(1 << 8)	/* Ch8 interrupt status from SPR */
+#define DMAC_STAT_TSPRS	(1 << 9)	/* Ch9 interrupt status to SPR */
+#define DMAC_STAT_SIS	(1 << 13)	/* DMA stall interrupt status */
+#define DMAC_STAT_MEIS	(1 << 14)	/* MFIFO empty interrupt status */
+#define DMAC_STAT_BEIS	(1 << 15)	/* BUSERR interrupt status */
+#define DMAC_STAT_VIF0M	(1 << 16)	/* Ch0 interrupt mask VIF0 */
+#define DMAC_STAT_VIF1M	(1 << 17)	/* Ch1 interrupt mask VIF1 */
+#define DMAC_STAT_GIFM	(1 << 18)	/* Ch2 interrupt mask GIF */
+#define DMAC_STAT_FIPUM	(1 << 19)	/* Ch3 interrupt mask from IPU */
+#define DMAC_STAT_TIPUM	(1 << 20)	/* Ch4 interrupt mask to IPU */
+#define DMAC_STAT_SIF0M	(1 << 21)	/* Ch5 interrupt mask SIF0 */
+#define DMAC_STAT_SIF1M	(1 << 22)	/* Ch6 interrupt mask SIF1 */
+#define DMAC_STAT_SIF2M	(1 << 23)	/* Ch7 interrupt mask SIF2 */
+#define DMAC_STAT_FSPRM	(1 << 24)	/* Ch8 interrupt mask from SPR */
+#define DMAC_STAT_TSPRM	(1 << 25)	/* Ch9 interrupt mask to SPR */
+#define DMAC_STAT_SIM	(1 << 29)	/* DMA stall interrupt mask */
+#define DMAC_STAT_MEIM	(1 << 30)	/* MFIFO empty interrupt mask */
+
+#define DMAC_ENABLER	0x1000f520	/* Acquisition of DMA suspend status */
+#define DMAC_ENABLEW	0x1000f590	/* DMA suspend control */
+
+#endif /* __ASM_MACH_PS2_DMAC_H */
Signature · 1 line
2.21.0
#44 lore raw

PATCH 28/120 MIPS: PS2: DMAC: Define tag structures

In reply to Fredrik Noring

The DMA tag is used in chain mode to control the destination and source memory addresses, transfer size, etc. There are two types of tag: source chain tag and destination chain tag[1].

References:

[1] "EE User's Manual", version 6.0, Sony Computer Entertainment Inc.,

    pp. 58-61.
---
 arch/mips/include/asm/mach-ps2/dmac.h | 65 +++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)
arch/mips/include/asm/mach-ps2/dmac.h +65 -0
index 30a0f72eeab3..fb4c52ad9ad5 100644
@@ -186,4 +186,69 @@
 #define DMAC_ENABLER	0x1000f520	/* Acquisition of DMA suspend status */
 #define DMAC_ENABLEW	0x1000f590	/* DMA suspend control */
 
+enum dma_tag_reg {		/* Data start address:	Next tag address: */
+	dma_tag_id_refe = 0,	/* ADDR			(none) */
+	dma_tag_id_cnts = 0,	/* ADDR			(none) */
+	dma_tag_id_cnt,		/* next to tag		next to transfer data */
+	dma_tag_id_next,	/* next to tag		ADDR */
+	dma_tag_id_ref,		/* ADDR			next to tag */
+	dma_tag_id_refs,	/* ADDR			next to tag */
+	dma_tag_id_call,	/* next to tag		ADDR */
+	dma_tag_id_ret,		/* next to tag		Dn_ASR */
+	dma_tag_id_end		/* next to tag		(none) */
+};
+
+/**
+ * enum dma_tag_spr - memory or scratch-pad RAM
+ * @dma_tag_spr_memory: select memory
+ * @dma_tag_spr_scratchpad: select scratch-pad RAM
+ */
+enum dma_tag_spr {
+	dma_tag_spr_memory,
+	dma_tag_spr_scratchpad
+};
+
+/**
+ * struct dma_tag - DMA tag
+ * @qwc: 128-bit quadword count
+ * @pce: priority control enable
+ * @id: &enum dma_tag_reg tag identifier
+ * @irq: interrupt request
+ * @addr: address with lower 4 bits zero
+ * @spr: &enum dma_tag_spr memory or scratch-pad RAM
+ *
+ * The DMA tag must be aligned with 16 byte boundaries.
+ */
+struct dma_tag {
+	u64 qwc : 16;
+	u64 : 10;
+	u64 pce : 2;
+	u64 id : 3;
+	u64 irq : 1;
+	u64 addr : 31;
+	u64 spr : 1;
+
+	u64 : 64;
+} __attribute__((aligned(16)));
+
+/**
+ * struct iop_dma_tag - I/O processor (IOP) DMA tag
+ * @addr: IOP address
+ * @int_0: assert IOP interupt on completion
+ * @ert: FIXME
+ * @wc: 32-bit word count
+ *
+ * The IOP DMA tag must be aligned with 16 byte boundaries.
+ */
+struct iop_dma_tag {
+	u32 addr : 24;
+	u32 : 6;
+	u32 int_0 : 1;
+	u32 ert : 1;
+
+	u32 wc;
+
+	u64 : 64;
+} __attribute__((aligned(16)));
+
 #endif /* __ASM_MACH_PS2_DMAC_H */
Signature · 1 line
2.21.0
#45 lore raw

PATCH 29/120 MIPS: PS2: DMAC: IRQ support

In reply to Fredrik Noring
---
 arch/mips/include/asm/mach-ps2/irq.h |   1 +
 arch/mips/ps2/Makefile               |   1 +
 arch/mips/ps2/dmac-irq.c             | 102 +++++++++++++++++++++++++++
 arch/mips/ps2/irq.c                  |   1 +
 4 files changed, 105 insertions(+)
 create mode 100644 arch/mips/ps2/dmac-irq.c
arch/mips/include/asm/mach-ps2/irq.h +1 -0
index 071c8139dabe..16c96aa7ca09 100644
@@ -72,5 +72,6 @@
 #define IRQ_C0_IRQ7	55
 
 int __init intc_irq_init(void);
+int __init dmac_irq_init(void);
 
 #endif /* __ASM_MACH_PS2_IRQ_H */
arch/mips/ps2/Makefile +1 -0
index ccdfb80c9f03..1e6406f42b3a 100644
@@ -1,3 +1,4 @@
+obj-y		+= dmac-irq.o
 obj-y		+= intc-irq.o
 obj-y		+= irq.o
 obj-y		+= memory.o
arch/mips/ps2/dmac-irq.c new file +102 -0

102 lines not on this page · raw message

arch/mips/ps2/irq.c +1 -0
index 935171a1e3bd..7c656e3735a1 100644
@@ -19,6 +19,7 @@ void __init arch_init_irq(void)
 	mips_cpu_irq_init();
 
 	intc_irq_init();
+	dmac_irq_init();
 }
 
 asmlinkage void plat_irq_dispatch(void)
Signature · 1 line
2.21.0
#46 lore raw

PATCH 30/120 MIPS: PS2: Timer support

In reply to Fredrik Noring

The Emotion Engine has 4 independent timers with 16-bit counters[1]. The bus clock or an external (H-BLANK or V-BLANK) clock perform the counting. When a counter reaches a specified reference value, or when it overflows, an interrupt is asserted. The timer status register indicates the cause.

Timers 0 and 1 have hold registers for recording the counter value when an SBUS interrupt occurs.

Timer registers are 32-bit long and only word-accessible.

References:

[1] "EE User's Manual", version 6.0, Sony Computer Entertainment Inc.,

    pp. 33-39.
---
 arch/mips/ps2/Makefile |   1 +
 arch/mips/ps2/time.c   | 153 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 154 insertions(+)
 create mode 100644 arch/mips/ps2/time.c
arch/mips/ps2/Makefile +1 -0
index 1e6406f42b3a..2015870f9fe7 100644
@@ -2,3 +2,4 @@ obj-y		+= dmac-irq.o
 obj-y		+= intc-irq.o
 obj-y		+= irq.o
 obj-y		+= memory.o
+obj-y		+= time.o
arch/mips/ps2/time.c new file +153 -0

153 lines not on this page · raw message

Signature · 1 line
2.21.0
#47 lore raw

PATCH 31/120 MIPS: PS2: SCMD: System command support

In reply to Fredrik Noring
---
 arch/mips/include/asm/mach-ps2/scmd.h |  33 +++++
 arch/mips/ps2/Makefile                |   1 +
 arch/mips/ps2/scmd.c                  | 180 ++++++++++++++++++++++++++
 3 files changed, 214 insertions(+)
 create mode 100644 arch/mips/include/asm/mach-ps2/scmd.h
 create mode 100644 arch/mips/ps2/scmd.c
arch/mips/include/asm/mach-ps2/scmd.h new file +33 -0
new file mode 100644
index 000000000000..b2b98dbaec9b
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 system commands
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#ifndef __ASM_MACH_PS2_SCMD_H
+#define __ASM_MACH_PS2_SCMD_H
+
+#include <linux/types.h>
+
+#define SCMD_COMMAND	0x1f402016
+#define SCMD_STATUS	0x1f402017
+#define SCMD_SEND	0x1f402017
+#define SCMD_RECV	0x1f402018
+
+#define SCMD_STATUS_EMPTY	0x40	/* Data is unavailable */
+#define SCMD_STATUS_BUSY	0x80	/* Command is processing */
+
+/**
+ * enum scmd_cmd - system commands
+ * @scmd_cmd_power_off: power off the system
+ */
+enum scmd_cmd {
+	scmd_cmd_power_off = 15,
+};
+
+int scmd(enum scmd_cmd cmd,
+	const void *send, size_t send_size,
+	void *recv, size_t recv_size);
+
+#endif /* __ASM_MACH_PS2_SCMD_H */
arch/mips/ps2/Makefile +1 -0
index 2015870f9fe7..d90d3e06387f 100644
@@ -2,4 +2,5 @@ obj-y		+= dmac-irq.o
 obj-y		+= intc-irq.o
 obj-y		+= irq.o
 obj-y		+= memory.o
+obj-y		+= scmd.o
 obj-y		+= time.o
arch/mips/ps2/scmd.c new file +180 -0

180 lines not on this page · raw message

Signature · 1 line
2.21.0
#48 lore raw

PATCH 32/120 MIPS: PS2: SCMD: System power off command

In reply to Fredrik Noring
---
 arch/mips/include/asm/mach-ps2/scmd.h |  2 ++
 arch/mips/ps2/scmd.c                  | 29 +++++++++++++++++++++++++++
 2 files changed, 31 insertions(+)
arch/mips/include/asm/mach-ps2/scmd.h +2 -0
index b2b98dbaec9b..9e0135655ce8 100644
@@ -30,4 +30,6 @@ int scmd(enum scmd_cmd cmd,
 	const void *send, size_t send_size,
 	void *recv, size_t recv_size);
 
+int scmd_power_off(void);
+
 #endif /* __ASM_MACH_PS2_SCMD_H */
arch/mips/ps2/scmd.c +29 -0
index 0516766ffdba..5544c7dfb7b4 100644
@@ -175,6 +175,35 @@ int scmd(enum scmd_cmd cmd,
 }
 EXPORT_SYMBOL_GPL(scmd);
 
+/**
+ * scmd_power_off - system command to power off the system
+ *
+ * On success, the processor will have to wait for the shut down to take effect.
+ *
+ * Context: sleep
+ * Return: 0 on success, else a negative error number
+ */
+int scmd_power_off(void)
+{
+	u8 status;
+	int err;
+
+	err = scmd(scmd_cmd_power_off, NULL, 0, &status, sizeof(status));
+	if (err < 0) {
+		pr_err("%s: Write failed with %d\n", __func__, err);
+		return err;
+	}
+
+	if (status != 0) {
+		pr_err("%s: Invalid result with status 0x%x\n",
+			__func__, status);
+		return -EIO;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(scmd_power_off);
+
 MODULE_DESCRIPTION("PlayStation 2 system commands");
 MODULE_AUTHOR("Fredrik Noring");
 MODULE_LICENSE("GPL");
Signature · 1 line
2.21.0
#49 lore raw

PATCH 33/120 MIPS: PS2: SCMD: Read system machine name command

In reply to Fredrik Noring

An example of machine name is SCPH-50004.

Machines SCPH-10000 and SCPH-15000 do not implement this command. Late SCPH-10000 and all SCPH-15000 have the name in rom0:OSDSYS instead.

---
 arch/mips/include/asm/mach-ps2/scmd.h | 12 ++++++
 arch/mips/ps2/scmd.c                  | 62 +++++++++++++++++++++++++++
 2 files changed, 74 insertions(+)
arch/mips/include/asm/mach-ps2/scmd.h +12 -0
index 9e0135655ce8..2389b937eec0 100644
@@ -21,9 +21,11 @@
 /**
  * enum scmd_cmd - system commands
  * @scmd_cmd_power_off: power off the system
+ * @scmd_cmd_read_machine_name: read machine name
  */
 enum scmd_cmd {
 	scmd_cmd_power_off = 15,
+	scmd_cmd_read_machine_name = 23,
 };
 
 int scmd(enum scmd_cmd cmd,
@@ -32,4 +34,14 @@ int scmd(enum scmd_cmd cmd,
 
 int scmd_power_off(void);
 
+/**
+ * struct scmd_machine_name - machine name, or the empty string
+ * @name: NUL terminated string, for example ``"SCPH-50004"``
+ */
+struct scmd_machine_name {
+	char name[16];
+};
+
+struct scmd_machine_name scmd_read_machine_name(void);
+
 #endif /* __ASM_MACH_PS2_SCMD_H */
arch/mips/ps2/scmd.c +62 -0

74 lines not on this page · raw message

Signature · 1 line
2.21.0
#50 lore raw

PATCH 34/120 MIPS: PS2: SCMD: Read system command for the real-time clock (RTC)

In reply to Fredrik Noring

The hardware clock is designed to keep Japan standard time (JST), regardless of the region of the machine. This is adjusted in the driver so that the clock to the kernel appears to be kept in coordinated universal time (UTC). Tools such as hwclock should therefore read the clock in the UTC timescale.

---
 arch/mips/include/asm/mach-ps2/scmd.h |  5 +++
 arch/mips/ps2/scmd.c                  | 55 +++++++++++++++++++++++++++
 2 files changed, 60 insertions(+)
arch/mips/include/asm/mach-ps2/scmd.h +5 -0
index 2389b937eec0..352a921181b6 100644
@@ -8,6 +8,7 @@
 #ifndef __ASM_MACH_PS2_SCMD_H
 #define __ASM_MACH_PS2_SCMD_H
 
+#include <linux/time64.h>
 #include <linux/types.h>
 
 #define SCMD_COMMAND	0x1f402016
@@ -20,10 +21,12 @@
 
 /**
  * enum scmd_cmd - system commands
+ * @scmd_cmd_read_rtc: read the real-time clock (RTC)
  * @scmd_cmd_power_off: power off the system
  * @scmd_cmd_read_machine_name: read machine name
  */
 enum scmd_cmd {
+	scmd_cmd_read_rtc = 8,
 	scmd_cmd_power_off = 15,
 	scmd_cmd_read_machine_name = 23,
 };
@@ -44,4 +47,6 @@ struct scmd_machine_name {
 
 struct scmd_machine_name scmd_read_machine_name(void);
 
+int scmd_read_rtc(time64_t *t);
+
 #endif /* __ASM_MACH_PS2_SCMD_H */
arch/mips/ps2/scmd.c +55 -0

69 lines not on this page · raw message

Signature · 1 line
2.21.0
#51 lore raw

PATCH 35/120 MIPS: PS2: SCMD: Set system command for the real-time clock (RTC)

In reply to Fredrik Noring

The hardware clock is designed to keep Japan standard time (JST), regardless of the region of the machine. This is adjusted in the driver so that the clock to the kernel appears to be kept in coordinated universal time (UTC). Tools such as hwclock should therefore set the clock in the UTC timescale.

---
 arch/mips/include/asm/mach-ps2/scmd.h |  4 ++
 arch/mips/ps2/scmd.c                  | 53 +++++++++++++++++++++++++++
 2 files changed, 57 insertions(+)
arch/mips/include/asm/mach-ps2/scmd.h +4 -0
index 352a921181b6..3ca106ec7da4 100644
@@ -22,11 +22,13 @@
 /**
  * enum scmd_cmd - system commands
  * @scmd_cmd_read_rtc: read the real-time clock (RTC)
+ * @scmd_cmd_write_rtc: set the real-time clock (RTC)
  * @scmd_cmd_power_off: power off the system
  * @scmd_cmd_read_machine_name: read machine name
  */
 enum scmd_cmd {
 	scmd_cmd_read_rtc = 8,
+	scmd_cmd_write_rtc = 9,
 	scmd_cmd_power_off = 15,
 	scmd_cmd_read_machine_name = 23,
 };
@@ -49,4 +51,6 @@ struct scmd_machine_name scmd_read_machine_name(void);
 
 int scmd_read_rtc(time64_t *t);
 
+int scmd_set_rtc(time64_t t);
+
 #endif /* __ASM_MACH_PS2_SCMD_H */
arch/mips/ps2/scmd.c +53 -0

59 lines not on this page · raw message

Signature · 1 line
2.21.0
#52 lore raw

PATCH 36/120 MIPS: PS2: ROM: Iterate over the files in a given ROM directory

In reply to Fredrik Noring

All PlayStation 2 machines have at least two read-only memories (ROMs) called ROM0 and ROM1. A ROM consists of concatenated files forming a directory.

The rom_for_each_file() iterator will be used to search for certain ROM files in subsequent changes. The rom0:ROMVER file, for example, contains information on the ROM version, the machine region, the machine type (CEX for retail, DEX for debug, or TOOL), etc.

The input/output processor (IOP) of the PlayStation 2 links several of its modules by reading ROM files. When the kernel links additional modules to handle relayed interrupt services, for example, the kernel module linker must resolve dependencies by identifying whether libraries are linked as ROM or firmware files.

---
 arch/mips/include/asm/mach-ps2/rom.h |  79 +++++
 arch/mips/ps2/Makefile               |   1 +
 arch/mips/ps2/rom.c                  | 501 +++++++++++++++++++++++++++
 3 files changed, 581 insertions(+)
 create mode 100644 arch/mips/ps2/rom.c
arch/mips/include/asm/mach-ps2/rom.h +79 -0
index 6760be183696..063c8b6acf15 100644
@@ -8,10 +8,89 @@
 #ifndef __ASM_MACH_PS2_ROM_H
 #define __ASM_MACH_PS2_ROM_H
 
+#include <linux/types.h>
+
 #define ROM0_BASE	0x1fc00000	/* ROM0 base address (boot) */
 #define ROM0_SIZE	0x400000	/* ROM0 maximum size */
 
 #define ROM1_BASE	0x1e000000	/* ROM1 base address (DVD) */
 #define ROM1_SIZE	0x100000	/* ROM1 maximum size */
 
+struct rom_dir_entry;
+
+/**
+ * struct rom_dir - ROM directory
+ * @size: size in bytes of all files combined
+ * @data: pointer to data of all files combined
+ * @extinfo: extended information of all files combined
+ * @extinfo.size: size in bytes of all extended information combined
+ * @extinfo.data: pointer to data of all extended information combined
+ * @entries: pointer to array of ROM directory entries, with the terminating
+ * 	file as the last entry
+ *
+ * A directory is considered to be empty if @size is zero, in which case all
+ * members are zero.
+ */
+struct rom_dir {
+	size_t size;
+	const void *data;
+
+	struct {
+		size_t size;
+		const void *data;
+	} extinfo;
+
+	const struct rom_dir_entry *entries;
+};
+
+/**
+ * struct rom_file - ROM file
+ * @name: name of file, or the empty string for the terminating file
+ * @size: size in bytes of file
+ * @data: pointer to data of file
+ * @extinfo: extended ROM file information
+ * @extinfo.size: size in bytes of extended file information
+ * @extinfo.data: pointer to data of extended file information
+ * @next: pointer to next file, unless this is the terminating file
+ *
+ * A file is considered to be a terminating file if @name is the empty string.
+ * A terminating file is the last file in a &struct rom_dir directory.
+ */
+struct rom_file {
+	const char *name;
+	size_t size;
+	const void *data;
+
+	struct {
+		size_t size;
+		const void *data;
+	} extinfo;
+
+	const struct rom_dir_entry *next;
+};
+
+extern struct rom_dir rom0_dir;		/* ROM0 directory (boot) */
+extern struct rom_dir rom1_dir;		/* ROM1 directory (DVD) */
+
+/**
+ * rom_for_each_file - iterate over files in given ROM directory
+ * @file: &struct rom_file to use as a ROM file loop cursor
+ * @dir: &struct rom_dir with ROM directory to iterate over
+ *
+ * The statement following the macro is executed for ROM files in the
+ * directory.
+ */
+#define rom_for_each_file(file, dir)					\
+	for ((file) = rom_first_file(dir);				\
+	     !rom_terminating_file(file);				\
+	     (file) = rom_next_file(file))
+
+bool rom_empty_dir(const struct rom_dir dir);
+
+bool rom_terminating_file(const struct rom_file file);
+
+struct rom_file rom_next_file(const struct rom_file file);
+
+struct rom_file rom_first_file(const struct rom_dir dir);
+
 #endif /* __ASM_MACH_PS2_ROM_H */
arch/mips/ps2/Makefile +1 -0
index d90d3e06387f..1101ad0d702b 100644
@@ -2,5 +2,6 @@ obj-y		+= dmac-irq.o
 obj-y		+= intc-irq.o
 obj-y		+= irq.o
 obj-y		+= memory.o
+obj-y		+= rom.o
 obj-y		+= scmd.o
 obj-y		+= time.o
arch/mips/ps2/rom.c new file +501 -0

501 lines not on this page · raw message

Signature · 1 line
2.21.0
#53 lore raw

PATCH 37/120 MIPS: PS2: ROM: Find ROM files with a given name, if they exist

In reply to Fredrik Noring

rom_find_files() searches for files in the given ROM directory. Most ROM files have unique names, but certain special files named for example "-" appear multiple times.

The "continue; else" construction is used to avoid an if-else ambiguity warning from GCC, when the loop has an if-else statement.

---
 arch/mips/include/asm/mach-ps2/rom.h | 13 +++++++++++++
 1 file changed, 13 insertions(+)
arch/mips/include/asm/mach-ps2/rom.h +13 -0
index 063c8b6acf15..8794bd13184e 100644
@@ -85,6 +85,19 @@ extern struct rom_dir rom1_dir;		/* ROM1 directory (DVD) */
 	     !rom_terminating_file(file);				\
 	     (file) = rom_next_file(file))
 
+/**
+ * rom_find_files - find ROM files with the given name, if they exist
+ * @file: &struct rom_file to use as a ROM file match cursor
+ * @dir: &struct rom_dir with ROM directory to search in
+ * @filename: file name to look for
+ *
+ * The statement following the macro is executed for ROM files having the
+ * given name.
+ */
+#define rom_find_files(file, dir, filename)				\
+	rom_for_each_file((file), (dir))				\
+		if (strcmp((file).name, filename) != 0) continue; else
+
 bool rom_empty_dir(const struct rom_dir dir);
 
 bool rom_terminating_file(const struct rom_file file);
Signature · 1 line
2.21.0
#54 lore raw

PATCH 38/120 MIPS: PS2: ROM: Read data for a given ROM file name

In reply to Fredrik Noring

Reading ROM files is trivial since they are permanently available in memory. Having rom_read_file() is a convenient when for example resolving the machine region in subsequent changes.

---
 arch/mips/include/asm/mach-ps2/rom.h |  3 +++
 arch/mips/ps2/rom.c                  | 33 ++++++++++++++++++++++++++++
 2 files changed, 36 insertions(+)
arch/mips/include/asm/mach-ps2/rom.h +3 -0
index 8794bd13184e..f2c35788ddfb 100644
@@ -98,6 +98,9 @@ extern struct rom_dir rom1_dir;		/* ROM1 directory (DVD) */
 	rom_for_each_file((file), (dir))				\
 		if (strcmp((file).name, filename) != 0) continue; else
 
+ssize_t rom_read_file(const struct rom_dir dir,
+	const char *name, void *buffer, size_t size, loff_t offset);
+
 bool rom_empty_dir(const struct rom_dir dir);
 
 bool rom_terminating_file(const struct rom_file file);
arch/mips/ps2/rom.c +33 -0
index 12a57f24bd63..840d37a199d8 100644
@@ -224,6 +224,39 @@ struct rom_file rom_first_file(const struct rom_dir dir)
 }
 EXPORT_SYMBOL_GPL(rom_first_file);
 
+/**
+ * rom_read_file - read ROM file data
+ * @dir: directory to read the file from
+ * @name: file name to read
+ * @buffer: pointer to buffer to store data that is read
+ * @size: size in bytes to read
+ * @offset: offset in bytes to start reading
+ *
+ * Context: any
+ * Return: on successful completion, a nonnegative integer indicating the
+ * 	number of bytes actually read; otherwise, a negative error number
+ */
+ssize_t rom_read_file(const struct rom_dir dir,
+	const char *name, void *buffer, size_t size, loff_t offset)
+{
+	struct rom_file file;
+
+	rom_find_files(file, dir, name)
+		if (offset < file.size) {
+			const u8 *b = file.data;
+			size_t remaining = file.size - offset;
+			size_t n = min(size, remaining);
+
+			memcpy(buffer, &b[offset], n);
+
+			return n;
+		} else
+			return 0;
+
+	return -ENOENT;
+}
+EXPORT_SYMBOL_GPL(rom_read_file);
+
 /**
  * find_reset_string - find the offset to the ``"RESET"`` string, if it exists
  * @rom: ROM to search in
Signature · 1 line
2.21.0
#55 lore raw

Re: [PATCH 038/120] MIPS: PS2: ROM: Read data for a given ROM file name

Hello!

Quoted 5 lines · Fredrik Noring

Reading ROM files is trivial since they are permanently available in memory. Having rom_read_file() is a convenient when for example resolving the machine region in subsequent changes.

[...]

Quoted 24 lines

index 12a57f24bd63..840d37a199d8 100644

arch/mips/ps2/rom.c +17 -0
@@ -224,6 +224,39 @@ struct rom_file rom_first_file(const struct rom_dir dir)
  }
  EXPORT_SYMBOL_GPL(rom_first_file);
 
+/**
+ * rom_read_file - read ROM file data
+ * @dir: directory to read the file from
+ * @name: file name to read
+ * @buffer: pointer to buffer to store data that is read
+ * @size: size in bytes to read
+ * @offset: offset in bytes to start reading
+ *
+ * Context: any
+ * Return: on successful completion, a nonnegative integer indicating the
+ * 	number of bytes actually read; otherwise, a negative error number
+ */
+ssize_t rom_read_file(const struct rom_dir dir,
+	const char *name, void *buffer, size_t size, loff_t offset)
+{
+	struct rom_file file;
+

arch/mips/ps2/rom.c struct rom_file rom_first_file(const struct rom_dir dir) · line 253

Quoted 10 lines
arch/mips/ps2/rom.c +10 -0
+	rom_find_files(file, dir, name)
+		if (offset < file.size) {
+			const u8 *b = file.data;
+			size_t remaining = file.size - offset;
+			size_t n = min(size, remaining);
+
+			memcpy(buffer, &b[offset], n);
+
+			return n;
+		} else

The *else* branch also needs {} if the *if* branch has 'emn, according to
Documentation/process/coding-style.rst.

arch/mips/ps2/rom.c struct rom_file rom_first_file(const struct rom_dir dir) · line 262

Quoted 9 lines
arch/mips/ps2/rom.c +6 -0
+			return 0;
+
+	return -ENOENT;
+}
+EXPORT_SYMBOL_GPL(rom_read_file);
+
  /**
   * find_reset_string - find the offset to the ``"RESET"`` string, if it exists
   * @rom: ROM to search in

MBR, Sergei

#56 lore raw

Re: [PATCH 038/120] MIPS: PS2: ROM: Read data for a given ROM file name

Hello!

Quoted 33 lines · Sergei Shtylyov
Quoted 5 lines

Reading ROM files is trivial since they are permanently available in memory. Having rom_read_file() is a convenient when for example resolving the machine region in subsequent changes.

[...]

Quoted 27 lines

index 12a57f24bd63..840d37a199d8 100644

arch/mips/ps2/rom.c +20 -0
@@ -224,6 +224,39 @@ struct rom_file rom_first_file(const struct rom_dir dir)
  }
  EXPORT_SYMBOL_GPL(rom_first_file);
  +/**
+ * rom_read_file - read ROM file data
+ * @dir: directory to read the file from
+ * @name: file name to read
+ * @buffer: pointer to buffer to store data that is read
+ * @size: size in bytes to read
+ * @offset: offset in bytes to start reading
+ *
+ * Context: any
+ * Return: on successful completion, a nonnegative integer indicating the
+ *     number of bytes actually read; otherwise, a negative error number
+ */
+ssize_t rom_read_file(const struct rom_dir dir,
+    const char *name, void *buffer, size_t size, loff_t offset)
+{
+    struct rom_file file;
+
+    rom_find_files(file, dir, name)
+        if (offset < file.size) {
+            const u8 *b = file.data;
+            size_t remaining = file.size - offset;
Quoted 8 lines

arch/mips/ps2/rom.c struct rom_file rom_first_file(const struct rom_dir dir) · line 253

Quoted 6 lines
arch/mips/ps2/rom.c +6 -0
+            size_t n = min(size, remaining);
+
+            memcpy(buffer, &b[offset], n);
+
+            return n;
+        } else

The *else* branch also needs {} if the *if* branch has 'emn, according to Documentation/process/coding-style.rst.

Just realized that we don't need *else* after *return*...

Quoted 9 lines

arch/mips/ps2/rom.c struct rom_file rom_first_file(const struct rom_dir dir) · line 262

Quoted 9 lines
arch/mips/ps2/rom.c +6 -0
+            return 0;
+
+    return -ENOENT;
+}
+EXPORT_SYMBOL_GPL(rom_read_file);
+
  /**
   * find_reset_string - find the offset to the ``"RESET"`` string, if it exists
   * @rom: ROM to search in

MBR, Sergei

#57 lore raw

Re: [PATCH 038/120] MIPS: PS2: ROM: Read data for a given ROM file name

Quoted 15 lines
Quoted 6 lines
Quoted 6 lines
arch/mips/ps2/rom.c +6 -0
+    struct rom_file file;
+
+    rom_find_files(file, dir, name)
+        if (offset < file.size) {
+            const u8 *b = file.data;
+            size_t remaining = file.size - offset;
Quoted 8 lines

arch/mips/ps2/rom.c struct rom_file rom_first_file(const struct rom_dir dir) · line 253

Quoted 6 lines
arch/mips/ps2/rom.c +6 -0
+            size_t n = min(size, remaining);
+
+            memcpy(buffer, &b[offset], n);
+
+            return n;
+        } else

The *else* branch also needs {} if the *if* branch has 'emn, according to Documentation/process/coding-style.rst.

Quoted 3 lines

Just realized that we don't need *else* after *return*...

Quoted 1 line
Quoted 1 line

+ return 0;

There is a style rule that says "use braces when a loop contains more than a single simple statement", and with those braces we could remove the else:

	rom_find_files (file, dir, name) {
		if (offset < file.size) {
			const u8 *b = file.data;
			size_t remaining = file.size - offset;
			size_t n = min(size, remaining);

			memcpy(buffer, &b[offset], n);

			return n;
		}

		return 0;
	}

[ rom_find_files() is a cursor based loop macro similar to list_for_each_entry() and others of the same kind. Perhaps the name could be a bit better, for example rom_for_each_file_with_name() or something like that. Patches 036/120 and 037/120 have the details. ]

Fredrik

#58 lore raw

PATCH 39/120 MIPS: PS2: ROM: Read extended information for a given ROM file

In reply to Fredrik Noring

The extended information (EXTINFO) is metadata about ROM files, often but not always containing file date, version and brief comments.

---
 arch/mips/include/asm/mach-ps2/rom.h |  22 ++++++
 arch/mips/ps2/rom.c                  | 106 +++++++++++++++++++++++++++
 2 files changed, 128 insertions(+)
arch/mips/include/asm/mach-ps2/rom.h +22 -0
index f2c35788ddfb..4cc3fcffaa59 100644
@@ -69,6 +69,25 @@ struct rom_file {
 	const struct rom_dir_entry *next;
 };
 
+/**
+ * struct rom_extinfo - extended ROM file information
+ * @version: version number
+ * @date: date ROM was created
+ * @date.year: year ROM was created
+ * @date.month: month ROM was created
+ * @date.day: day ROM was created
+ * @comment: comment or the empty string
+ */
+struct rom_extinfo {
+	int version;
+	struct {
+		int year;
+		int month;
+		int day;
+	} date;
+	const char *comment;
+};
+
 extern struct rom_dir rom0_dir;		/* ROM0 directory (boot) */
 extern struct rom_dir rom1_dir;		/* ROM1 directory (DVD) */
 
@@ -101,6 +120,9 @@ extern struct rom_dir rom1_dir;		/* ROM1 directory (DVD) */
 ssize_t rom_read_file(const struct rom_dir dir,
 	const char *name, void *buffer, size_t size, loff_t offset);
 
+struct rom_extinfo rom_read_extinfo(const char *name,
+	const void *buffer, size_t size);
+
 bool rom_empty_dir(const struct rom_dir dir);
 
 bool rom_terminating_file(const struct rom_file file);
arch/mips/ps2/rom.c +106 -0

130 lines not on this page · raw message

Signature · 1 line
2.21.0
#59 lore raw

PATCH 40/120 MIPS: PS2: ROM: Read and decode the ROMVER file

In reply to Fredrik Noring

The ROMVER file contains information on the ROM version, the machine region, the machine type (CEX for retail, DEX for debug, or TOOL) and the date the ROM was created.

---
 arch/mips/include/asm/mach-ps2/rom.h | 30 ++++++++++++++++++++++++++++
 arch/mips/ps2/rom.c                  | 28 ++++++++++++++++++++++++++
 2 files changed, 58 insertions(+)
arch/mips/include/asm/mach-ps2/rom.h +30 -0
index 4cc3fcffaa59..42e520f32f65 100644
@@ -123,6 +123,36 @@ ssize_t rom_read_file(const struct rom_dir dir,
 struct rom_extinfo rom_read_extinfo(const char *name,
 	const void *buffer, size_t size);
 
+/**
+ * struct rom_ver - ROM version
+ * @number: ROM version number
+ * @region: ROM region with ``'J'`` for Japan, ``'E'`` for Europe,
+ * 	``'C'`` for China, and ``'A'`` or ``'H'`` for the USA
+ * @type: ROM type with ``'C'`` for retail (CEX), ``'D'`` for debug (DEX),
+ * 	and `'T'`` for TOOL, or ``'-'`` for undefined
+ * @date: date ROM was created
+ * @date.year: year ROM was created
+ * @date.month: month ROM was created
+ * @date.day: day ROM was created
+ *
+ * Note that the Namco System 246 arcade systems are TOOL types as well.
+ *
+ * A ROM version is considered to be invalid if @number is zero, in which
+ * case all members are zero except @region and @type that are ``'-'``.
+ */
+struct rom_ver {
+	int number;
+	char region;
+	char type;
+	struct {
+		int year;
+		int month;
+		int day;
+	} date;
+};
+
+struct rom_ver rom_version(void);
+
 bool rom_empty_dir(const struct rom_dir dir);
 
 bool rom_terminating_file(const struct rom_file file);
arch/mips/ps2/rom.c +28 -0
index a510832e26d7..32ae8ec839c4 100644
@@ -362,6 +362,27 @@ struct rom_extinfo rom_read_extinfo(const char *name,
 }
 EXPORT_SYMBOL_GPL(rom_read_extinfo);
 
+/**
+ * rom_version - read the ROMVER file in ROM0
+ *
+ * Context: any
+ * Return: ROM version; or, if reading failed, all members zeroed except
+ *      @region and @type that are set to ``'-'``
+ */
+struct rom_ver rom_version(void)
+{
+	struct rom_ver v = { };
+	char buffer[20] = { };
+	ssize_t r = rom_read_file(rom0_dir, "ROMVER",
+		buffer, sizeof(buffer) - 1, 0);
+
+	return r > 0 && sscanf(buffer, "%4x%c%c%4d%2d%2d",
+				&v.number, &v.region, &v.type,
+				&v.date.year, &v.date.month, &v.date.day) == 6 ?
+		v : (struct rom_ver) { .region = '-', .type = '-' };
+}
+EXPORT_SYMBOL_GPL(rom_version);
+
 /**
  * find_reset_string - find the offset to the ``"RESET"`` string, if it exists
  * @rom: ROM to search in
@@ -629,12 +650,19 @@ static struct rom_dir __init rom_dir_init(const char *name,
 
 static int __init ps2_rom_init(void)
 {
+	struct rom_ver v;
+
 	BUILD_BUG_ON(sizeof(struct rom_dir_entry) != 16);
 	BUILD_BUG_ON(sizeof(struct rom_extinfo_entry) != 4);
 
 	rom0_dir = rom_dir_init("rom0", ROM0_BASE, ROM0_SIZE);
 	rom1_dir = rom_dir_init("rom1", ROM1_BASE, ROM1_SIZE);
 
+	v = rom_version();
+	pr_info("rom0: Version %04x %c %c %04d-%02d-%02d\n",
+		v.number, v.region, v.type,
+		v.date.year, v.date.month, v.date.day);
+
 	return 0;
 }
 arch_initcall(ps2_rom_init);
Signature · 1 line
2.21.0
#60 lore raw

PATCH 41/120 MIPS: PS2: ROM: Resolve the name for the type in the ROMVER file

In reply to Fredrik Noring

'C' indicates CEX for retail, 'D' indicates DEX for debug, and 'T' indicates a TOOL machine.

---
 arch/mips/include/asm/mach-ps2/rom.h |  4 ++++
 arch/mips/ps2/rom.c                  | 20 ++++++++++++++++++--
 2 files changed, 22 insertions(+), 2 deletions(-)
arch/mips/include/asm/mach-ps2/rom.h +4 -0
index 42e520f32f65..0b0e7c4162f5 100644
@@ -135,6 +135,8 @@ struct rom_extinfo rom_read_extinfo(const char *name,
  * @date.month: month ROM was created
  * @date.day: day ROM was created
  *
+ * The @type name can be resolved with rom_type_name().
+ *
  * Note that the Namco System 246 arcade systems are TOOL types as well.
  *
  * A ROM version is considered to be invalid if @number is zero, in which
@@ -153,6 +155,8 @@ struct rom_ver {
 
 struct rom_ver rom_version(void);
 
+const char *rom_type_name(char type);
+
 bool rom_empty_dir(const struct rom_dir dir);
 
 bool rom_terminating_file(const struct rom_file file);
arch/mips/ps2/rom.c +18 -2
index 32ae8ec839c4..9adea705cbca 100644
@@ -383,6 +383,22 @@ struct rom_ver rom_version(void)
 }
 EXPORT_SYMBOL_GPL(rom_version);
 
+/**
+ * rom_type_name - name for the ROM type character in the ROMVER file in ROM0
+ * @type: &rom_ver.type type character.
+ *
+ * Context: any
+ * Return: ROM type name, ``"-"`` if undefined or ``"?"`` if unrecognised
+ */
+const char *rom_type_name(char type)
+{
+	return type == 'C' ? "CEX" :
+	       type == 'D' ? "DEX" :
+	       type == 'T' ? "TOOL" :
+	       type == '-' ? "-" : "?";
+}
+EXPORT_SYMBOL_GPL(rom_type_name);
+
 /**
  * find_reset_string - find the offset to the ``"RESET"`` string, if it exists
  * @rom: ROM to search in
@@ -659,8 +675,8 @@ static int __init ps2_rom_init(void)
 	rom1_dir = rom_dir_init("rom1", ROM1_BASE, ROM1_SIZE);
 
 	v = rom_version();
-	pr_info("rom0: Version %04x %c %c %04d-%02d-%02d\n",
-		v.number, v.region, v.type,
+	pr_info("rom0: Version %04x %c %s %04d-%02d-%02d\n",
+		v.number, v.region, rom_type_name(v.type),
 		v.date.year, v.date.month, v.date.day);
 
 	return 0;
Signature · 1 line
2.21.0
#61 lore raw

PATCH 42/120 MIPS: PS2: ROM: Resolve the name for the region in the ROMVER file

In reply to Fredrik Noring

'J' indicates Japan, 'E' indicates Europe, 'C' indicates China and 'A' and 'H' indicate the USA.

---
 arch/mips/include/asm/mach-ps2/rom.h |  5 ++++-
 arch/mips/ps2/rom.c                  | 22 ++++++++++++++++++++--
 2 files changed, 24 insertions(+), 3 deletions(-)
arch/mips/include/asm/mach-ps2/rom.h +4 -1
index 0b0e7c4162f5..43770fd62a2e 100644
@@ -135,7 +135,8 @@ struct rom_extinfo rom_read_extinfo(const char *name,
  * @date.month: month ROM was created
  * @date.day: day ROM was created
  *
- * The @type name can be resolved with rom_type_name().
+ * The @region and @type names can be resolved with rom_region_name() and
+ * rom_type_name().
  *
  * Note that the Namco System 246 arcade systems are TOOL types as well.
  *
@@ -155,6 +156,8 @@ struct rom_ver {
 
 struct rom_ver rom_version(void);
 
+const char *rom_region_name(char region);
+
 const char *rom_type_name(char type);
 
 bool rom_empty_dir(const struct rom_dir dir);
arch/mips/ps2/rom.c +20 -2
index 9adea705cbca..7cdc4962069e 100644
@@ -383,6 +383,24 @@ struct rom_ver rom_version(void)
 }
 EXPORT_SYMBOL_GPL(rom_version);
 
+/**
+ * rom_region_name - name for the ROM region character in the ROMVER file
+ * @region: &rom_ver.region region character.
+ *
+ * Context: any
+ * Return: ROM region name, ``"-"`` if undefined or ``"?"`` if unrecognised
+ */
+const char *rom_region_name(char region)
+{
+	return region == 'J' ? "Japan" :
+	       region == 'E' ? "Europe" :
+	       region == 'C' ? "China" :
+	       region == 'A' ? "USA" :
+	       region == 'H' ? "USA" :
+	       region == '-' ? "-" : "?";
+}
+EXPORT_SYMBOL_GPL(rom_region_name);
+
 /**
  * rom_type_name - name for the ROM type character in the ROMVER file in ROM0
  * @type: &rom_ver.type type character.
@@ -675,8 +693,8 @@ static int __init ps2_rom_init(void)
 	rom1_dir = rom_dir_init("rom1", ROM1_BASE, ROM1_SIZE);
 
 	v = rom_version();
-	pr_info("rom0: Version %04x %c %s %04d-%02d-%02d\n",
-		v.number, v.region, rom_type_name(v.type),
+	pr_info("rom0: Version %04x %s %s %04d-%02d-%02d\n",
+		v.number, rom_region_name(v.region), rom_type_name(v.type),
 		v.date.year, v.date.month, v.date.day);
 
 	return 0;
Signature · 1 line
2.21.0
#62 lore raw

PATCH 43/120 MIPS: PS2: ROM: Permit /dev/mem to access read-only memory

In reply to Fredrik Noring
---
 arch/mips/include/asm/io.h |  6 ++++++
 arch/mips/ps2/memory.c     | 16 ++++++++++++++++
 2 files changed, 22 insertions(+)
arch/mips/include/asm/io.h +6 -0
index f7115472f530..f449a27c2b79 100644
@@ -709,4 +709,10 @@ extern void (*_dma_cache_inv)(unsigned long start, unsigned long size);
 
 void __ioread64_copy(void *to, const void __iomem *from, size_t count);
 
+#ifdef CONFIG_SONY_PS2
+#define ARCH_HAS_VALID_PHYS_ADDR_RANGE
+extern int valid_phys_addr_range(phys_addr_t addr, size_t size);
+extern int valid_mmap_phys_addr_range(unsigned long pfn, size_t size);
+#endif /* CONFIG_SONY_PS2 */
+
 #endif /* _ASM_IO_H */
arch/mips/ps2/memory.c +16 -0
index 66ca37f38330..c513b6912bb0 100644
@@ -7,9 +7,25 @@
 
 #include <linux/init.h>
 #include <linux/ioport.h>
+#include <linux/mm.h>
 #include <linux/types.h>
 
 #include <asm/bootinfo.h>
+#include <asm/io.h>
+
+#include <asm/mach-ps2/rom.h>
+
+int valid_phys_addr_range(phys_addr_t addr, size_t size)
+{
+	return addr + size <= __pa(high_memory) ||
+	       (ROM0_BASE <= addr && addr + size <= ROM0_BASE + ROM0_SIZE) ||
+	       (ROM1_BASE <= addr && addr + size <= ROM1_BASE + ROM1_SIZE);
+}
+
+int valid_mmap_phys_addr_range(unsigned long pfn, size_t size)
+{
+	return 1;
+}
 
 void __init plat_mem_setup(void)
 {
Signature · 1 line
2.21.0
#63 lore raw

PATCH 44/120 MIPS: PS2: ROM: Sysfs module to inspect ROM files

In reply to Fredrik Noring

The ROM sysfs module gives information on the ROM file names, sizes, and addresses. For example, listing all files of ROM0:

	# cat /sys/rom/rom0/file/*/name | column
	RESET       VBLANK      RMRESET     MCSERV      XSIFCMD
	ROMDIR      IOMAN       OSDVER      PADMAN      XCDVDMAN
	EXTINFO     MODLOAD     -           CDVDMAN     XCDVDFSV
	ROMVER      ROMDRV      IOPBOOT     CDVDFSV     XFILEIO
	SBIN        ADDDRV      OSDCNF      FILEIO      XSIO2MAN
	LOGO        STDIO       -           CLEARSPU    XMTAPMAN
	IOPBTCONF   SIFMAN      TBIN        UDNL        XMCMAN
	...

Viewing for example the contents of the ROM file ROMVER using /dev/mem:

	# grep -l ROMVER /sys/rom/rom0/file/*/name
	/sys/rom/rom0/file/3/name
	# cd /sys/rom/rom0/file/3
	# dd if=/dev/mem bs=$(cat size) iflag=skip_bytes \
		skip=$(( $(cat data) )) count=1 status=none
	0170EC20030227

For convenience, the ROMVER file is also available directly in sysfs:

	# ls /sys/rom/rom0/version
	date    number  region  type
	# cat /sys/rom/rom0/version/*
	2003-02-27
	0x0170
	Europe
	CEX

The CEX type indicates that it is a retail machine, as opposed to for example DEX that would be a debug machine.

---
 arch/mips/ps2/Makefile    |   1 +
 arch/mips/ps2/rom-sysfs.c | 288 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 289 insertions(+)
 create mode 100644 arch/mips/ps2/rom-sysfs.c
arch/mips/ps2/Makefile +1 -0
index 1101ad0d702b..a56ea782120e 100644
@@ -3,5 +3,6 @@ obj-y		+= intc-irq.o
 obj-y		+= irq.o
 obj-y		+= memory.o
 obj-y		+= rom.o
+obj-m		+= rom-sysfs.o
 obj-y		+= scmd.o
 obj-y		+= time.o
arch/mips/ps2/rom-sysfs.c new file +288 -0

288 lines not on this page · raw message

Signature · 1 line
2.21.0
#64 lore raw

PATCH 45/120 MIPS: PS2: ROM: Provide extended file information via sysfs

In reply to Fredrik Noring

The extended ROM file information can be viewed with sysfs:

	# ls /sys/rom/rom0/file/8/extinfo
	comment  data     date     size     version
	# cat /sys/rom/rom0/file/8/extinfo/*
	System_Memory_Manager
	0x1fc02df4
	2002-04-03
	40
	0x0101
---
 arch/mips/ps2/rom-sysfs.c | 104 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 104 insertions(+)
arch/mips/ps2/rom-sysfs.c +104 -0
index 158d36763af5..804644a931e8 100644
@@ -28,6 +28,8 @@
 //	# grep -l ROMVER /sys/rom/rom0/file/*/name
 //	/sys/rom/rom0/file/3/name
 //	# cd /sys/rom/rom0/file/3
+//	# ls
+//	data     extinfo  name     size
 //	# dd if=/dev/mem bs=$(cat size) iflag=skip_bytes
 //		skip=$(( $(cat data) )) count=1 status=none
 //	0170EC20030227
@@ -45,6 +47,17 @@
 // The CEX type indicates that it is a retail machine, as opposed to for
 // example DEX that would be a debug machine.
 //
+// The extended ROM file information can also be viewed with sysfs::
+//
+// 	# ls /sys/rom/rom0/file/8/extinfo
+// 	comment  data     date     size     version
+// 	# cat /sys/rom/rom0/file/8/extinfo/*
+// 	System_Memory_Manager
+// 	0x1fc02df4
+// 	2002-04-03
+// 	40
+// 	0x0101
+//
 
 #include <linux/ctype.h>
 #include <linux/device.h>
@@ -102,6 +115,14 @@ static struct rom_file rom_file_from_kobj(const struct kobject *kobj)
 	return (struct rom_file) { .name = "<undefined>" };
 }
 
+static struct rom_extinfo rom_extinfo_from_kobj(const struct kobject *kobj)
+{
+	const struct rom_file file = rom_file_from_kobj(kobj->parent);
+
+	return rom_read_extinfo(file.name,
+		file.extinfo.data, file.extinfo.size);
+}
+
 static ssize_t rom_version_number_show(struct kobject *kobj,
 	struct kobj_attribute *attr, char *buf)
 {
@@ -131,6 +152,54 @@ static ssize_t rom_version_date_show(struct kobject *kobj,
 		v.date.year, v.date.month, v.date.day);
 }
 
+static ssize_t rom_extinfo_size_show(struct kobject *kobj,
+	struct kobj_attribute *attr, char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "%zu\n",
+		rom_file_from_kobj(kobj->parent).extinfo.size);
+}
+
+static ssize_t rom_extinfo_data_show(struct kobject *kobj,
+	struct kobj_attribute *attr, char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "0x%lx\n",
+		virt_to_phys(rom_file_from_kobj(kobj->parent).extinfo.data));
+}
+
+static ssize_t rom_extinfo_version_show(struct kobject *kobj,
+	struct kobj_attribute *attr, char *buf)
+{
+	const struct rom_extinfo ei = rom_extinfo_from_kobj(kobj);
+
+	if (!ei.version)
+		return 0;
+
+	return scnprintf(buf, PAGE_SIZE, "0x%04x\n", ei.version);
+}
+
+static ssize_t rom_extinfo_date_show(struct kobject *kobj,
+	struct kobj_attribute *attr, char *buf)
+{
+	const struct rom_extinfo ei = rom_extinfo_from_kobj(kobj);
+
+	if (!ei.date.year && !ei.date.month && !ei.date.day)
+		return 0;
+
+	return scnprintf(buf, PAGE_SIZE, "%04d-%02d-%02d\n",
+		ei.date.year, ei.date.month, ei.date.day);
+}
+
+static ssize_t rom_extinfo_comment_show(struct kobject *kobj,
+	struct kobj_attribute *attr, char *buf)
+{
+	const struct rom_extinfo ei = rom_extinfo_from_kobj(kobj);
+
+	if (ei.comment[0] == '\0')
+		return 0;
+
+	return scnprintf(buf, PAGE_SIZE, "%s\n", ei.comment);
+}
+
 static ssize_t rom_file_name_show(struct kobject *kobj,
 	struct kobj_attribute *attr, char *buf)
 {
@@ -173,6 +242,25 @@ static struct attribute_group rom_version_attribute_group = {
 	.attrs = rom_version_attributes
 };
 
+DEFINE_ROM_FIELD_ATTR(extinfo, size);
+DEFINE_ROM_FIELD_ATTR(extinfo, data);
+DEFINE_ROM_FIELD_ATTR(extinfo, version);
+DEFINE_ROM_FIELD_ATTR(extinfo, date);
+DEFINE_ROM_FIELD_ATTR(extinfo, comment);
+
+static struct attribute *rom_extinfo_attributes[] = {
+	&rom_attribute_extinfo_size.attr,
+	&rom_attribute_extinfo_data.attr,
+	&rom_attribute_extinfo_version.attr,
+	&rom_attribute_extinfo_date.attr,
+	&rom_attribute_extinfo_comment.attr,
+	NULL
+};
+
+static struct attribute_group rom_extinfo_attribute_group = {
+	.attrs = rom_extinfo_attributes
+};
+
 DEFINE_ROM_FIELD_ATTR(file, name);
 DEFINE_ROM_FIELD_ATTR(file, size);
 DEFINE_ROM_FIELD_ATTR(file, data);
@@ -199,6 +287,18 @@ static int rom0_sysfs(struct kobject *rom0_kobj)
 	return sysfs_create_group(version_kobj, &rom_version_attribute_group);
 }
 
+static int __init rom_init_file_extinfo(struct kobject *index_kobj,
+	const struct rom_file file)
+{
+	struct kobject *extinfo_kobj;
+
+	extinfo_kobj = kobject_create_and_add("extinfo", index_kobj);
+	if (!extinfo_kobj)
+		return -ENOMEM;
+
+	return sysfs_create_group(extinfo_kobj, &rom_extinfo_attribute_group);
+}
+
 static int __init rom_init_file(struct kobject *file_kobj, size_t index,
 	const struct rom_file file)
 {
@@ -212,6 +312,10 @@ static int __init rom_init_file(struct kobject *file_kobj, size_t index,
 	if (!index_kobj)
 		return -ENOMEM;
 
+	err = rom_init_file_extinfo(index_kobj, file);
+	if (err)
+		return err;
+
 	return sysfs_create_group(index_kobj, &rom_file_attribute_group);
 }
 
Signature · 1 line
2.21.0
#65 lore raw

PATCH 46/120 MIPS: PS2: Identify the machine by model name

In reply to Fredrik Noring

ROM version 1.00 is always SCPH-10000. Later machines with ROM version 1.0x have the machine name in the ROM0 file OSDSYS at offset 0x8c808. These are late SCPH-10000 and all SCPH-15000. Even later machines have a system command (SCMD) to read the machine name.

The machine name is shown in the /proc/cpuinfo file, for example:

	# grep machine /proc/cpuinfo
	machine			: SCPH-37000 L
---
 arch/mips/ps2/Makefile   |  1 +
 arch/mips/ps2/identify.c | 80 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 81 insertions(+)
 create mode 100644 arch/mips/ps2/identify.c
arch/mips/ps2/Makefile +1 -0
index a56ea782120e..b53bddcc8c01 100644
@@ -1,4 +1,5 @@
 obj-y		+= dmac-irq.o
+obj-y		+= identify.o
 obj-y		+= intc-irq.o
 obj-y		+= irq.o
 obj-y		+= memory.o
arch/mips/ps2/identify.c new file +80 -0

80 lines not on this page · raw message

Signature · 1 line
2.21.0
#66 lore raw

PATCH 47/120 MIPS: PS2: Let the system type be Sony PlayStation 2

In reply to Fredrik Noring

The system type is shown in the /proc/cpuinfo file:

	# grep system' 'type /proc/cpuinfo
	system type		: Sony PlayStation 2
---
 arch/mips/ps2/identify.c | 6 ++++++
 1 file changed, 6 insertions(+)
arch/mips/ps2/identify.c +6 -0
index 264fdc13dc43..579148fb79c4 100644
@@ -9,11 +9,17 @@
 #include <linux/init.h>
 #include <linux/printk.h>
 
+#include <asm/bootinfo.h>
 #include <asm/prom.h>
 
 #include <asm/mach-ps2/rom.h>
 #include <asm/mach-ps2/scmd.h>
 
+const char *get_system_type(void)
+{
+	return "Sony PlayStation 2";
+}
+
 static int __init set_machine_name_by_scmd(void)
 {
 	const struct scmd_machine_name machine = scmd_read_machine_name();
Signature · 1 line
2.21.0
Reviewed-by on v1 47/120
#67 lore raw

Re: [PATCH 047/120] MIPS: PS2: Let the system type be Sony PlayStation 2

Quoted 23 lines · Fredrik Noring

The system type is shown in the /proc/cpuinfo file:

	# grep system' 'type /proc/cpuinfo
	system type		: Sony PlayStation 2
---
 arch/mips/ps2/identify.c | 6 ++++++
 1 file changed, 6 insertions(+)
arch/mips/ps2/identify.c +1 -0
index 264fdc13dc43..579148fb79c4 100644
@@ -9,11 +9,17 @@
 #include <linux/init.h>
 #include <linux/printk.h>
 
+#include <asm/bootinfo.h>
 #include <asm/prom.h>
 
 #include <asm/mach-ps2/rom.h>

arch/mips/ps2/identify.c line 25

Quoted 10 lines
arch/mips/ps2/identify.c +5 -0
 #include <asm/mach-ps2/scmd.h>
 
+const char *get_system_type(void)
+{
+	return "Sony PlayStation 2";
+}
+
 static int __init set_machine_name_by_scmd(void)
 {
 	const struct scmd_machine_name machine = scmd_read_machine_name();
#68 lore raw

PATCH 48/120 MIPS: Define and use cpu_relax_forever() for various halting loops

In reply to Fredrik Noring

Using cpu_relax_forever() can lower processor energy consumption when halting indefinitely.

---
 arch/mips/alchemy/board-gpr.c          |  7 +++----
 arch/mips/alchemy/board-mtx1.c         |  7 ++-----
 arch/mips/alchemy/board-xxs1500.c      |  7 ++-----
 arch/mips/alchemy/devboards/platform.c |  4 ++--
 arch/mips/ar7/setup.c                  |  4 ++--
 arch/mips/ath79/setup.c                |  8 +++-----
 arch/mips/bcm47xx/setup.c              |  9 +++++----
 arch/mips/bcm63xx/setup.c              | 12 ++++++------
 arch/mips/boot/compressed/decompress.c |  4 ++--
 arch/mips/cavium-octeon/smp.c          |  4 ++--
 arch/mips/cobalt/reset.c               |  5 +----
 arch/mips/emma/markeins/setup.c        |  5 +++--
 arch/mips/fw/arc/init.c                |  4 ++--
 arch/mips/include/asm/processor.h      |  6 ++++++
 arch/mips/jz4740/reset.c               |  9 ++-------
 arch/mips/loongson64/common/reset.c    | 16 ++++------------
 arch/mips/mm/cerr-sb1.c                |  4 ++--
 arch/mips/pic32/common/reset.c         |  9 ++-------
 arch/mips/pnx833x/common/reset.c       |  5 ++---
 arch/mips/sgi-ip22/ip22-reset.c        |  3 ++-
 arch/mips/sgi-ip27/ip27-berr.c         |  4 ++--
 arch/mips/sgi-ip27/ip27-reset.c        | 13 ++++---------
 arch/mips/sgi-ip32/ip32-berr.c         |  4 ++--
 arch/mips/sgi-ip32/ip32-irq.c          |  3 ++-
 arch/mips/sibyte/common/cfe.c          |  3 ++-
 arch/mips/txx9/rbtx4939/setup.c        |  4 ++--
 arch/mips/vr41xx/common/pmu.c          |  2 +-
 27 files changed, 70 insertions(+), 95 deletions(-)
arch/mips/alchemy/board-gpr.c +3 -4
index 6c47318946e4..5247dfc1e0ed 100644
@@ -20,6 +20,7 @@
 #include <linux/gpio/machine.h>
 #include <asm/bootinfo.h>
 #include <asm/idle.h>
+#include <asm/processor.h>
 #include <asm/reboot.h>
 #include <asm/setup.h>
 #include <asm/mach-au1x00/au1000.h>
@@ -65,14 +66,12 @@ static void gpr_reset(char *c)
 	alchemy_gpio_direction_output(1, 0);
 	udelay(1);
 	alchemy_gpio_set_value(1, 1);
-	while (1)
-		cpu_wait();
+	cpu_relax_forever();
 }
 
 static void gpr_power_off(void)
 {
-	while (1)
-		cpu_wait();
+	cpu_relax_forever();
 }
 
 void __init board_setup(void)
arch/mips/alchemy/board-mtx1.c +2 -5
index 23093535399f..2025ba4ebfdd 100644
@@ -18,6 +18,7 @@
 #include <linux/mtd/physmap.h>
 #include <mtd/mtd-abi.h>
 #include <asm/bootinfo.h>
+#include <asm/processor.h>
 #include <asm/reboot.h>
 #include <asm/setup.h>
 #include <asm/mach-au1x00/au1000.h>
@@ -60,11 +61,7 @@ static void mtx1_reset(char *c)
 
 static void mtx1_power_off(void)
 {
-	while (1)
-		asm volatile (
-		"	.set	mips32					\n"
-		"	wait						\n"
-		"	.set	mips0					\n");
+	cpu_relax_forever();
 }
 
 void __init board_setup(void)
arch/mips/alchemy/board-xxs1500.c +2 -5
index c67dfe1f4997..6189efe70e65 100644
@@ -15,6 +15,7 @@
 #include <linux/delay.h>
 #include <linux/pm.h>
 #include <asm/bootinfo.h>
+#include <asm/processor.h>
 #include <asm/reboot.h>
 #include <asm/setup.h>
 #include <asm/mach-au1x00/au1000.h>
@@ -56,11 +57,7 @@ static void xxs1500_reset(char *c)
 
 static void xxs1500_power_off(void)
 {
-	while (1)
-		asm volatile (
-		"	.set	mips32					\n"
-		"	wait						\n"
-		"	.set	mips0					\n");
+	cpu_relax_forever();
 }
 
 void __init board_setup(void)
arch/mips/alchemy/devboards/platform.c +2 -2
index 8d4b65c3268a..2203fddc42d8 100644
@@ -13,6 +13,7 @@
 
 #include <asm/bootinfo.h>
 #include <asm/idle.h>
+#include <asm/processor.h>
 #include <asm/reboot.h>
 #include <asm/setup.h>
 #include <asm/mach-au1x00/au1000.h>
@@ -56,8 +57,7 @@ static void db1x_power_off(void)
 {
 	bcsr_write(BCSR_RESETS, 0);
 	bcsr_write(BCSR_SYSTEM, BCSR_SYSTEM_PWROFF | BCSR_SYSTEM_RESET);
-	while (1)		/* sit and spin */
-		cpu_wait();
+	cpu_relax_forever();	/* sit and spin */
 }
 
 static void db1x_reset(char *c)
arch/mips/ar7/setup.c +2 -2
index b3ffe7c898eb..2649ee848921 100644
@@ -8,6 +8,7 @@
 #include <linux/pm.h>
 #include <linux/time.h>
 
+#include <asm/processor.h>
 #include <asm/reboot.h>
 #include <asm/mach-ar7/ar7.h>
 #include <asm/mach-ar7/prom.h>
@@ -21,8 +22,7 @@ static void ar7_machine_restart(char *command)
 
 static void ar7_machine_halt(void)
 {
-	while (1)
-		;
+	cpu_relax_forever();
 }
 
 static void ar7_machine_power_off(void)
arch/mips/ath79/setup.c +3 -5
index ea385a865781..ad1d0fab5dad 100644
@@ -24,6 +24,7 @@
 #include <asm/time.h>		/* for mips_hpt_frequency */
 #include <asm/reboot.h>		/* for _machine_{restart,halt} */
 #include <asm/mips_machine.h>
+#include <asm/processor.h>
 #include <asm/prom.h>
 #include <asm/fw/fw.h>
 
@@ -39,15 +40,12 @@ static void ath79_restart(char *command)
 {
 	local_irq_disable();
 	ath79_device_reset_set(AR71XX_RESET_FULL_CHIP);
-	for (;;)
-		if (cpu_wait)
-			cpu_wait();
+	cpu_relax_forever();
 }
 
 static void ath79_halt(void)
 {
-	while (1)
-		cpu_wait();
+	cpu_relax_forever();
 }
 
 static void __init ath79_detect_sys_type(void)
arch/mips/bcm47xx/setup.c +5 -4
index 82627c264964..7ae2462ff3ae 100644
@@ -39,6 +39,7 @@
 #include <linux/bcma/bcma_soc.h>
 #include <asm/bootinfo.h>
 #include <asm/idle.h>
+#include <asm/processor.h>
 #include <asm/prom.h>
 #include <asm/reboot.h>
 #include <asm/time.h>
@@ -77,8 +78,8 @@ static void bcm47xx_machine_restart(char *command)
 		break;
 #endif
 	}
-	while (1)
-		cpu_relax();
+
+	cpu_relax_forever();
 }
 
 static void bcm47xx_machine_halt(void)
@@ -97,8 +98,8 @@ static void bcm47xx_machine_halt(void)
 		break;
 #endif
 	}
-	while (1)
-		cpu_relax();
+
+	cpu_relax_forever();
 }
 
 #ifdef CONFIG_BCM47XX_SSB
arch/mips/bcm63xx/setup.c +6 -6
index e28ee9a7cc7e..342ca32c0ae2 100644
@@ -14,6 +14,7 @@
 #include <linux/pm.h>
 #include <asm/bootinfo.h>
 #include <asm/time.h>
+#include <asm/processor.h>
 #include <asm/reboot.h>
 #include <asm/cacheflush.h>
 #include <bcm63xx_board.h>
@@ -25,8 +26,8 @@
 void bcm63xx_machine_halt(void)
 {
 	pr_info("System halted\n");
-	while (1)
-		;
+
+	cpu_relax_forever();
 }
 
 static void bcm6348_a1_reboot(void)
@@ -58,8 +59,8 @@ static void bcm6348_a1_reboot(void)
 		"jr\t%0"
 		:
 		: "r" (0xbfc00000));
-	while (1)
-		;
+
+	cpu_relax_forever();
 }
 
 void bcm63xx_machine_reboot(void)
@@ -118,8 +119,7 @@ void bcm63xx_machine_reboot(void)
 		reg |= SYS_PLL_SOFT_RESET;
 		bcm_perf_writel(reg, PERF_SYS_PLL_CTL_REG);
 	}
-	while (1)
-		;
+	cpu_relax_forever();
 }
 
 static void __bcm63xx_machine_reboot(char *p)
arch/mips/boot/compressed/decompress.c +2 -2
index 88f5d637b1c4..27e9c13614ee 100644
@@ -13,6 +13,7 @@
 #include <linux/libfdt.h>
 
 #include <asm/addrspace.h>
+#include <asm/processor.h>
 
 /*
  * These two variables specify the free mem region
@@ -41,8 +42,7 @@ void error(char *x)
 	puts(x);
 	puts("\n\n -- System halted");
 
-	while (1)
-		;	/* Halt */
+	cpu_relax_forever();	/* Halt */
 }
 
 /* activate the code for pre-boot environment */
arch/mips/cavium-octeon/smp.c +2 -2
index 076db9a06b5e..cb397b413967 100644
@@ -18,6 +18,7 @@
 #include <linux/kexec.h>
 
 #include <asm/mmu_context.h>
+#include <asm/processor.h>
 #include <asm/time.h>
 #include <asm/setup.h>
 
@@ -354,8 +355,7 @@ void play_dead(void)
 
 	mb();
 
-	while (1)	/* core will be reset here */
-		;
+	cpu_relax_forever();	/* core will be reset here */
 }
 
 static void start_after_reset(void)
arch/mips/cobalt/reset.c +1 -4
index 4eedd481dd00..13f67197193a 100644
@@ -37,10 +37,7 @@ void cobalt_machine_halt(void)
 	led_trigger_event(power_off_led_trigger, LED_FULL);
 
 	local_irq_disable();
-	while (1) {
-		if (cpu_wait)
-			cpu_wait();
-	}
+	cpu_relax_forever();
 }
 
 void cobalt_machine_restart(char *command)
arch/mips/emma/markeins/setup.c +3 -2
index c8a91c2a63bc..c12e4e6ceb76 100644
@@ -10,6 +10,7 @@
 #include <linux/kernel.h>
 #include <linux/types.h>
 
+#include <asm/processor.h>
 #include <asm/time.h>
 #include <asm/reboot.h>
 
@@ -34,13 +35,13 @@ static void markeins_machine_halt(void)
 {
 	printk("EMMA2RH Mark-eins halted.\n");
 	markeins_led("halted.");
-	while (1) ;
+	cpu_relax_forever();
 }
 
 static void markeins_machine_power_off(void)
 {
 	markeins_led("poweroff.");
-	while (1) ;
+	cpu_relax_forever();
 }
 
 static unsigned long __initdata emma2rh_clock[4] = {
arch/mips/fw/arc/init.c +2 -2
index 008555969534..90fa8ed75a50 100644
@@ -11,6 +11,7 @@
 #include <linux/kernel.h>
 
 #include <asm/bootinfo.h>
+#include <asm/processor.h>
 #include <asm/sgialib.h>
 #include <asm/smp-ops.h>
 
@@ -34,8 +35,7 @@ void __init prom_init(void)
 	if (pb->magic != 0x53435241) {
 		printk(KERN_CRIT "Aieee, bad prom vector magic %08lx\n",
 		       (unsigned long) pb->magic);
-		while(1)
-			;
+		cpu_relax_forever();
 	}
 
 	prom_init_cmdline();
arch/mips/include/asm/processor.h +6 -0
index aca909bd7841..e1e4cb7833a6 100644
@@ -405,6 +405,12 @@ unsigned long get_wchan(struct task_struct *p);
 #define cpu_relax()	barrier()
 #endif
 
+static inline void __noreturn cpu_relax_forever(void)
+{
+	for (;;)
+		cpu_relax();
+}
+
 /*
  * Return_address is a replacement for __builtin_return_address(count)
  * which on certain architectures cannot reasonably be implemented in GCC
arch/mips/jz4740/reset.c +2 -7
index 1f9f02e54085..c6703d32edb9 100644
@@ -3,19 +3,14 @@
  *  Copyright (C) 2010, Lars-Peter Clausen <[email protected]>
  */
 
+#include <asm/processor.h>
 #include <asm/reboot.h>
 
 #include "reset.h"
 
 static void jz4740_halt(void)
 {
-	while (1) {
-		__asm__(".set push;\n"
-			".set mips3;\n"
-			"wait;\n"
-			".set pop;\n"
-		);
-	}
+	cpu_relax_forever();
 }
 
 void jz4740_reset_init(void)
arch/mips/loongson64/common/reset.c +4 -12
index ce39e918e4d5..e91137922373 100644
@@ -10,6 +10,7 @@
 #include <linux/pm.h>
 
 #include <asm/idle.h>
+#include <asm/processor.h>
 #include <asm/reboot.h>
 
 #include <loongson.h>
@@ -45,10 +46,7 @@ static void loongson_restart(char *command)
 	void (*fw_restart)(void) = (void *)loongson_sysconf.restart_addr;
 
 	fw_restart();
-	while (1) {
-		if (cpu_wait)
-			cpu_wait();
-	}
+	cpu_relax_forever();
 #endif
 }
 
@@ -66,20 +64,14 @@ static void loongson_poweroff(void)
 	void (*fw_poweroff)(void) = (void *)loongson_sysconf.poweroff_addr;
 
 	fw_poweroff();
-	while (1) {
-		if (cpu_wait)
-			cpu_wait();
-	}
+	cpu_relax_forever();
 #endif
 }
 
 static void loongson_halt(void)
 {
 	pr_notice("\n\n** You can safely turn off the power now **\n\n");
-	while (1) {
-		if (cpu_wait)
-			cpu_wait();
-	}
+	cpu_relax_forever();
 }
 
 static int __init mips_reboot_setup(void)
arch/mips/mm/cerr-sb1.c +2 -2
index a3c02df19f6f..c0a6acea532c 100644
@@ -4,6 +4,7 @@
  */
 #include <linux/sched.h>
 #include <asm/mipsregs.h>
+#include <asm/processor.h>
 #include <asm/sibyte/sb1250.h>
 #include <asm/sibyte/sb1250_regs.h>
 
@@ -244,8 +245,7 @@ asmlinkage void sb1_cache_error(void)
 	 * undesirable.
 	 */
 #ifdef CONFIG_SB1_CERR_STALL
-	while (1)
-		;
+	cpu_relax_forever();
 #else
 	panic("unhandled cache error");
 #endif
arch/mips/pic32/common/reset.c +2 -7
index a5fd7a8e2800..32dcda98aa15 100644
@@ -5,6 +5,7 @@
  */
 #include <linux/init.h>
 #include <linux/pm.h>
+#include <asm/processor.h>
 #include <asm/reboot.h>
 #include <asm/mach-pic32/pic32.h>
 
@@ -12,13 +13,7 @@
 
 static void pic32_halt(void)
 {
-	while (1) {
-		__asm__(".set push;\n"
-			".set arch=r4000;\n"
-			"wait;\n"
-			".set pop;\n"
-		);
-	}
+	cpu_relax_forever();
 }
 
 static void pic32_machine_restart(char *command)
arch/mips/pnx833x/common/reset.c +2 -3
index b48e83bf912b..8f8b79994dae 100644
@@ -10,6 +10,7 @@
  *	Nikita Youshchenko <[email protected]>, based on PNX8550 code.
  */
 #include <linux/reboot.h>
+#include <asm/processor.h>
 #include <pnx833x.h>
 
 void pnx833x_machine_restart(char *command)
@@ -20,9 +21,7 @@ void pnx833x_machine_restart(char *command)
 
 void pnx833x_machine_halt(void)
 {
-	while (1)
-		__asm__ __volatile__ ("wait");
-
+	cpu_relax_forever();
 }
 
 void pnx833x_machine_power_off(void)
arch/mips/sgi-ip22/ip22-reset.c +2 -1
index c374f3ceec38..5355c96a2b89 100644
@@ -17,6 +17,7 @@
 
 #include <asm/io.h>
 #include <asm/irq.h>
+#include <asm/processor.h>
 #include <asm/reboot.h>
 #include <asm/sgialib.h>
 #include <asm/sgi/ioc.h>
@@ -72,7 +73,7 @@ static void __noreturn sgi_machine_restart(char *command)
 	if (machine_state & MACHINE_SHUTTING_DOWN)
 		sgi_machine_power_off();
 	sgimc->cpuctrl0 |= SGIMC_CCTRL0_SYSINIT;
-	while (1);
+	cpu_relax_forever();
 }
 
 static void __noreturn sgi_machine_halt(void)
arch/mips/sgi-ip27/ip27-berr.c +2 -2
index 73ad29b180fb..81cd434cb9ae 100644
@@ -14,6 +14,7 @@
 #include <linux/sched/debug.h>
 #include <linux/sched/signal.h>
 
+#include <asm/processor.h>
 #include <asm/ptrace.h>
 #include <asm/sn/addrs.h>
 #include <asm/sn/arch.h>
@@ -73,8 +74,7 @@ int ip27_be_handler(struct pt_regs *regs, int is_fixup)
 	dump_hub_information(errst0, errst1);
 	show_regs(regs);
 	dump_tlb_all();
-	while(1);
-	force_sig(SIGBUS);
+	cpu_relax_forever();
 }
 
 void __init ip27_be_init(void)
arch/mips/sgi-ip27/ip27-reset.c +4 -9
index e44a15d4f573..bc8c2507ba91 100644
@@ -19,6 +19,7 @@
 
 #include <asm/io.h>
 #include <asm/irq.h>
+#include <asm/processor.h>
 #include <asm/reboot.h>
 #include <asm/sgialib.h>
 #include <asm/sn/addrs.h>
@@ -26,12 +27,6 @@
 #include <asm/sn/gda.h>
 #include <asm/sn/sn0/hub.h>
 
-void machine_restart(char *command) __noreturn;
-void machine_halt(void) __noreturn;
-void machine_power_off(void) __noreturn;
-
-#define noreturn while(1);				/* Silence gcc.	 */
-
 /* XXX How to pass the reboot command to the firmware??? */
 static void ip27_machine_restart(char *command)
 {
@@ -50,7 +45,7 @@ static void ip27_machine_restart(char *command)
 #else
 	LOCAL_HUB_S(NI_PORT_RESET, NPR_PORTRESET | NPR_LOCALRESET);
 #endif
-	noreturn;
+	cpu_relax_forever();
 }
 
 static void ip27_machine_halt(void)
@@ -64,13 +59,13 @@ static void ip27_machine_halt(void)
 		REMOTE_HUB_S(COMPACT_TO_NASID_NODEID(i), PROMOP_REG,
 							PROMOP_RESTART);
 	LOCAL_HUB_S(NI_PORT_RESET, NPR_PORTRESET | NPR_LOCALRESET);
-	noreturn;
+	cpu_relax_forever();
 }
 
 static void ip27_machine_power_off(void)
 {
 	/* To do ...  */
-	noreturn;
+	cpu_relax_forever();
 }
 
 void ip27_reboot_setup(void)
arch/mips/sgi-ip32/ip32-berr.c +2 -2
index c860f95ab7ed..daa2036aeecc 100644
@@ -15,6 +15,7 @@
 #include <asm/traps.h>
 #include <linux/uaccess.h>
 #include <asm/addrspace.h>
+#include <asm/processor.h>
 #include <asm/ptrace.h>
 #include <asm/tlbdebug.h>
 
@@ -28,8 +29,7 @@ static int ip32_be_handler(struct pt_regs *regs, int is_fixup)
 	printk("Got %cbe at 0x%lx\n", data ? 'd' : 'i', regs->cp0_epc);
 	show_regs(regs);
 	dump_tlb_all();
-	while(1);
-	force_sig(SIGBUS);
+	cpu_relax_forever();
 }
 
 void __init ip32_be_init(void)
arch/mips/sgi-ip32/ip32-irq.c +2 -1
index a6a0ff7f5aed..d34a6b4f2096 100644
@@ -22,6 +22,7 @@
 
 #include <asm/irq_cpu.h>
 #include <asm/mipsregs.h>
+#include <asm/processor.h>
 #include <asm/signal.h>
 #include <asm/time.h>
 #include <asm/ip32/crime.h>
@@ -355,7 +356,7 @@ static void ip32_unknown_interrupt(void)
 
 	printk("Please mail this report to [email protected]\n");
 	printk("Spinning...");
-	while(1) ;
+	cpu_relax_forever();
 }
 
 /* CRIME 1.1 appears to deliver all interrupts to this one pin. */
arch/mips/sibyte/common/cfe.c +2 -1
index cbf5939ed53a..33143172a644 100644
@@ -13,6 +13,7 @@
 #include <linux/smp.h>
 
 #include <asm/bootinfo.h>
+#include <asm/processor.h>
 #include <asm/reboot.h>
 #include <asm/setup.h>
 #include <asm/sibyte/board.h>
@@ -65,7 +66,7 @@ static void __noreturn cfe_linux_exit(void *arg)
 		cfe_exit(warm, 0);
 		printk("cfe_exit returned??\n");
 	}
-	while (1);
+	cpu_relax_forever();
 }
 
 static void __noreturn cfe_linux_restart(char *command)
arch/mips/txx9/rbtx4939/setup.c +2 -2
index ef29a9c2ffd6..ec32d8a63d2e 100644
@@ -21,6 +21,7 @@
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/map.h>
+#include <asm/processor.h>
 #include <asm/reboot.h>
 #include <asm/txx9/generic.h>
 #include <asm/txx9/pci.h>
@@ -31,8 +32,7 @@ static void rbtx4939_machine_restart(char *command)
 	local_irq_disable();
 	writeb(1, rbtx4939_reseten_addr);
 	writeb(1, rbtx4939_softreset_addr);
-	while (1)
-		;
+	cpu_relax_forever();
 }
 
 static void __init rbtx4939_time_init(void)
arch/mips/vr41xx/common/pmu.c +1 -1
index 93cc7e0b30b1..d73178e3d910 100644
@@ -72,7 +72,7 @@ static void vr41xx_restart(char *command)
 {
 	local_irq_disable();
 	software_reset();
-	while (1) ;
+	cpu_relax_forever();
 }
 
 static void vr41xx_halt(void)
Signature · 1 line
2.21.0
#69 lore raw

PATCH 49/120 MIPS: PS2: Power off support

In reply to Fredrik Noring
---
 arch/mips/ps2/Makefile |  1 +
 arch/mips/ps2/reboot.c | 29 +++++++++++++++++++++++++++++
 2 files changed, 30 insertions(+)
 create mode 100644 arch/mips/ps2/reboot.c
arch/mips/ps2/Makefile +1 -0
index b53bddcc8c01..6f671112fbcb 100644
@@ -3,6 +3,7 @@ obj-y		+= identify.o
 obj-y		+= intc-irq.o
 obj-y		+= irq.o
 obj-y		+= memory.o
+obj-y		+= reboot.o
 obj-y		+= rom.o
 obj-m		+= rom-sysfs.o
 obj-y		+= scmd.o
arch/mips/ps2/reboot.c new file +29 -0
new file mode 100644
index 000000000000..b2a3ada5268b
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 power off
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#include <linux/init.h>
+#include <linux/pm.h>
+
+#include <asm/processor.h>
+#include <asm/reboot.h>
+
+#include <asm/mach-ps2/scmd.h>
+
+static void __noreturn power_off(void)
+{
+	scmd_power_off();
+
+	cpu_relax_forever();
+}
+
+static int __init ps2_init_reboot(void)
+{
+	pm_power_off = power_off;
+
+	return 0;
+}
+subsys_initcall(ps2_init_reboot);
Signature · 1 line
2.21.0
#70 lore raw

PATCH 50/120 MIPS: PS2: Real-time clock (RTC) driver

In reply to Fredrik Noring
---
 drivers/rtc/Kconfig   | 10 ++++++
 drivers/rtc/Makefile  |  1 +
 drivers/rtc/rtc-ps2.c | 74 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 85 insertions(+)
 create mode 100644 drivers/rtc/rtc-ps2.c
drivers/rtc/Kconfig +10 -0
index e72f65b61176..aaac22576a26 100644
@@ -1896,4 +1896,14 @@ config RTC_DRV_WILCO_EC
 	  This can also be built as a module. If so, the module will
 	  be named "rtc_wilco_ec".
 
+config RTC_DRV_PS2
+	tristate "PlayStation 2 RTC"
+	depends on SONY_PS2
+	default y
+	help
+	  Say yes here to get support for the PlayStation 2 real-time clock.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called rtc-ps2.
+
 endif # RTC_CLASS
drivers/rtc/Makefile +1 -0
index 6b09c21dc1b6..5bc300f1040a 100644
@@ -130,6 +130,7 @@ obj-$(CONFIG_RTC_DRV_PIC32)	+= rtc-pic32.o
 obj-$(CONFIG_RTC_DRV_PL030)	+= rtc-pl030.o
 obj-$(CONFIG_RTC_DRV_PL031)	+= rtc-pl031.o
 obj-$(CONFIG_RTC_DRV_PM8XXX)	+= rtc-pm8xxx.o
+obj-$(CONFIG_RTC_DRV_PS2)	+= rtc-ps2.o
 obj-$(CONFIG_RTC_DRV_PS3)	+= rtc-ps3.o
 obj-$(CONFIG_RTC_DRV_PUV3)	+= rtc-puv3.o
 obj-$(CONFIG_RTC_DRV_PXA)	+= rtc-pxa.o
drivers/rtc/rtc-ps2.c new file +74 -0

74 lines not on this page · raw message

Signature · 1 line
2.21.0
#71 lore raw

PATCH 51/120 MIPS: PS2: IOP: I/O processor DMA register PCR2 set and clear

In reply to Fredrik Noring

---
The documentation for the DEV9 is rather poor, unfortunately.

---
 .../mips/include/asm/mach-ps2/iop-registers.h | 19 +++++++++
 drivers/ps2/Makefile                          |  1 +
 drivers/ps2/iop-registers.c                   | 39 +++++++++++++++++++
 3 files changed, 59 insertions(+)
 create mode 100644 arch/mips/include/asm/mach-ps2/iop-registers.h
 create mode 100644 drivers/ps2/Makefile
 create mode 100644 drivers/ps2/iop-registers.c
arch/mips/include/asm/mach-ps2/iop-registers.h new file +19 -0
new file mode 100644
index 000000000000..b4db423150de
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 I/O processor (IOP) registers
+ *
+ * Copyright (C) 2017-2019 Fredrik Noring
+ */
+
+#ifndef __ASM_MACH_PS2_IOP_REGISTERS_H
+#define __ASM_MACH_PS2_IOP_REGISTERS_H
+
+#include <linux/types.h>
+
+#define IOP_DMA_DPCR2_OHCI	0x08000000	/* USB OHCI */
+#define IOP_DMA_DPCR2_DEV9	0x00000080	/* DEV9 (Expansion Bay, USB) */
+
+void iop_set_dma_dpcr2(const u32 mask);
+void iop_clr_dma_dpcr2(const u32 mask);
+
+#endif /* __ASM_MACH_PS2_IOP_REGISTERS_H */
drivers/ps2/Makefile new file +1 -0
new file mode 100644
index 000000000000..e53976ddb3e4
@@ -0,0 +1 @@
+obj-m				+= iop-registers.o
drivers/ps2/iop-registers.c new file +39 -0
new file mode 100644
index 000000000000..0ea7603b91a3
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 I/O processor (IOP) registers
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+#include <asm/io.h>
+
+#include <asm/mach-ps2/iop-registers.h>
+
+#define IOP_DMA_DPCR2	0x1f801570
+
+static DEFINE_SPINLOCK(reg_lock);
+
+void iop_set_dma_dpcr2(const u32 mask)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&reg_lock, flags);
+	outl(inl(IOP_DMA_DPCR2) | mask, IOP_DMA_DPCR2);
+	spin_unlock_irqrestore(&reg_lock, flags);
+}
+EXPORT_SYMBOL_GPL(iop_set_dma_dpcr2);
+
+void iop_clr_dma_dpcr2(const u32 mask)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&reg_lock, flags);
+	outl(inl(IOP_DMA_DPCR2) & ~mask, IOP_DMA_DPCR2);
+	spin_unlock_irqrestore(&reg_lock, flags);
+}
+EXPORT_SYMBOL_GPL(iop_clr_dma_dpcr2);
+
+MODULE_LICENSE("GPL");
Signature · 1 line
2.21.0
#72 lore raw

PATCH 52/120 MIPS: PS2: SIF: Sub-system interface reset of the I/O processor (IOP)

In reply to Fredrik Noring

The sub-system interface (SIF) is the interface to exchange data between the sub (input/output) processor (IOP) and the main (R5900) processor and other devices connected to the main bus[1]. The IOP handles, in whole or in part, most of the peripheral devices, including for example USB OHCI interrupts.

DMA controllers (DMACs) for the IOP and the R5900 operate in cooperation through a bidirectional FIFO in the SIF. There are three DMA channels:
SIF0 (sub-to-main), SIF1 (main-to-sub) and SIF2 (bidirectional).

Data is transferred in packets with a tag attached to each packet. The tag contains the memory addresses in the IOP and R5900 address spaces and the size of the data to transfer.

There are two mailbox type registers, the SMFLAG (sub-to-main) and MSFLAG (main-to-sub), used to indicate certain events. The MAINADDR and SUBADDR registers indicate the R5900 and IOP addresses where SIF commands are transferred by the DMAC.

The IOP can assert interrupts via IRQ_INTC_SBUS.

This SIF kernel module resets the IOP during initialisation. The IOP follows a certain boot protocol, with the following steps:

1. The kernel allocates a DMA memory buffer that the IOP can use to send commands. The kernel advertises this buffer by writing to the MAINADDR register.

2. The kernel reads the provisional SUBADDR register to obtain the corresponding command buffer for the IOP.

3. The kernel clears the SIF_STATUS_BOOTEND flag in the SMFLAG register.

4. The kernel issues the SIF_CMD_RESET_CMD command to the IOP.

5. The kernel indicates that the SIF and system commands are ready by setting the SIF_STATUS_SIFINIT and SIF_STATUS_CMDINIT flags in the SMFLAG register.

6. The kernel waits for the IOP to set the SIF_STATUS_BOOTEND flag in the SMFLAG register.

7. The kernel indicates that the boot is completed by writing updating its MAINADDR and setting the SIF_STATUS_CMDINIT and SIF_STATUS_BOOTEND flags in the MSFLAG register. The SIF_UNKNF260 register is set to 0xff.

8. The kernel reads the final SUBADDR register to obtain the command buffer for the IOP.

References:

[1] "EE Overview", version 6.0, Sony Computer Entertainment Inc., p. 47.

---
 arch/mips/include/asm/mach-ps2/sif.h |  72 +++++
 drivers/ps2/Makefile                 |   1 +
 drivers/ps2/sif.c                    | 414 +++++++++++++++++++++++++++
 3 files changed, 487 insertions(+)
 create mode 100644 arch/mips/include/asm/mach-ps2/sif.h
 create mode 100644 drivers/ps2/sif.c
arch/mips/include/asm/mach-ps2/sif.h new file +72 -0
new file mode 100644
index 000000000000..94da96bd21d1
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 sub-system interface (SIF)
+ *
+ * The SIF is an interface unit to the input/output processor (IOP).
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#ifndef __ASM_MACH_PS2_SIF_H
+#define __ASM_MACH_PS2_SIF_H
+
+#include <linux/types.h>
+#include <linux/completion.h>
+
+#include "iop-memory.h"
+
+#define SIF_MAINADDR 		0x1000f200	/* EE to IOP command buffer */
+#define SIF_SUBADDR  		0x1000f210	/* IOP to EE command buffer */
+#define SIF_MSFLAG   		0x1000f220	/* EE to IOP flag */
+#define SIF_SMFLAG   		0x1000f230	/* IOP to EE flag */
+#define SIF_SUBCTRL  		0x1000f240
+#define SIF_UNKNF260		0x1000f260
+
+/* Status flags for the sub-to-main (SM) and main-to-sub (MS) SIF registers. */
+#define SIF_STATUS_SIFINIT	0x10000		/* SIF initialised */
+#define SIF_STATUS_CMDINIT	0x20000		/* SIF CMD initialised */
+#define SIF_STATUS_BOOTEND	0x40000		/* IOP bootup completed */
+
+#define	SIF_CMD_ID_SYS		0x80000000
+#define	SIF_CMD_ID_USR		0x00000000
+
+#define SIF_CMD_CHANGE_SADDR	(SIF_CMD_ID_SYS | 0x00)
+#define SIF_CMD_WRITE_SREG	(SIF_CMD_ID_SYS | 0x01)
+#define SIF_CMD_INIT_CMD	(SIF_CMD_ID_SYS | 0x02)
+#define SIF_CMD_RESET_CMD	(SIF_CMD_ID_SYS | 0x03)
+#define SIF_CMD_RPC_END		(SIF_CMD_ID_SYS | 0x08)
+#define SIF_CMD_RPC_BIND	(SIF_CMD_ID_SYS | 0x09)
+#define SIF_CMD_RPC_CALL	(SIF_CMD_ID_SYS | 0x0a)
+#define SIF_CMD_RPC_RDATA	(SIF_CMD_ID_SYS | 0x0c)
+#define SIF_CMD_IRQ_RELAY	(SIF_CMD_ID_SYS | 0x20)
+#define SIF_CMD_PRINTK		(SIF_CMD_ID_SYS | 0x21)
+
+#define	SIF_SID_ID_SYS		0x80000000
+#define	SIF_SID_ID_USR		0x00000000
+
+#define SIF_SID_FILE_IO		(SIF_SID_ID_SYS | 0x01)
+#define SIF_SID_HEAP		(SIF_SID_ID_SYS | 0x03)
+#define SIF_SID_LOAD_MODULE	(SIF_SID_ID_SYS | 0x06)
+#define SIF_SID_IRQ_RELAY	(SIF_SID_ID_SYS | 0x20)
+
+#define SIF_CMD_PACKET_MAX	112
+#define SIF_CMD_PACKET_DATA_MAX 96
+
+/**
+ * struct sif_cmd_header - 16-byte SIF command header
+ * @packet_size: min 1x16 for header only, max 7*16 bytes
+ * @data_size: data size in bytes
+ * @data_addr: data address or zero
+ * @cmd: command number
+ * @opt: optional argument
+ */
+struct sif_cmd_header
+{
+	u32 packet_size : 8;
+	u32 data_size : 24;
+	u32 data_addr;
+	u32 cmd;
+	u32 opt;
+};
+
+#endif /* __ASM_MACH_PS2_SIF_H */
drivers/ps2/Makefile +1 -0
index e53976ddb3e4..ef561a802bdd 100644
@@ -1 +1,2 @@
 obj-m				+= iop-registers.o
+obj-m				+= sif.o
drivers/ps2/sif.c new file +414 -0

414 lines not on this page · raw message

Signature · 1 line
2.21.0
#73 lore raw

PATCH 53/120 MIPS: PS2: IOP: Define error numbers, descriptions and errno mapping

In reply to Fredrik Noring

IOP ROM modules use a set of special numbers to indicate error conditions of various kinds. These are translated into approximate kernel error numbers. Some of the IOP error numbers are unclear or unknown.

---
 arch/mips/include/asm/mach-ps2/iop-error.h | 76 ++++++++++++++++++++++
 drivers/ps2/sif.c                          | 39 +++++++++++
 2 files changed, 115 insertions(+)
 create mode 100644 arch/mips/include/asm/mach-ps2/iop-error.h
arch/mips/include/asm/mach-ps2/iop-error.h new file +76 -0
new file mode 100644
index 000000000000..c01bcad28a99
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 input/output processor (IOP) error codes
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#ifndef __ASM_MACH_PS2_IOP_ERROR_H
+#define __ASM_MACH_PS2_IOP_ERROR_H
+
+#define IOP_ERRORS(E)							\
+	E(BADEXCE,	50,	EINVAL,	"Bad exception")		\
+	E(NOEXCE,	51,	ENOENT,	"Exception not found")		\
+	E(BUSYEXCE,	52,	EBUSY,	"Exception in use")		\
+									\
+	E(INIRQ,	100,	EPERM,	"In IRQ context")		\
+	E(BADIRQ,	101,	EINVAL,	"Bad IRQ")			\
+	E(CPUINTD,	102,	EPERM,	"CPU interrupts disabled")	\
+	E(INTD,		103,	EPERM,	"Interrupts disabled")		\
+	E(BUSYHAND,	104,	EBUSY,	"Handler in use")		\
+	E(NOHAND,	105,	ENOENT,	"Handler not found")		\
+									\
+	E(NOTIMER,	150,	ENOENT,	"Timer not found")		\
+	E(BADTIMER,	151,	EINVAL,	"Bad timer")			\
+									\
+	E(BUSYUNIT,	160,	EBUSY,	"Unit in use")			\
+	E(NOUNIT,	161,	ENOENT,	"Unit not found")		\
+	E(NOROMDIR,	162,	ENOENT,	"ROM directory not found")	\
+									\
+	E(LINK,		200,	ENOEXEC, "Module linking error")	\
+	E(BADOBJ,	201,	ENOEXEC, "Object not module")		\
+	E(NOMOD,	202,	ENOENT,	"Module not found")		\
+	E(NOENT,	203,	ENOENT,	"No such file")			\
+	E(FILE,		204,	EIO,	"File error")			\
+	E(BUSYMEM,	205,	EBUSY,	"Memory in use")		\
+									\
+	E(NOMEM,	400,	ENOMEM,	"Out of memory")		\
+	E(BADATTR,	401,	EINVAL,	"Bad attribute")		\
+	E(BADENTRY,	402,	EINVAL,	"Bad entry")			\
+	E(BADPRIO,	403,	EINVAL,	"Bad priority")			\
+	E(BADSTSZ,	404,	EINVAL,	"Bad stack size")		\
+	E(BADMODE,	405,	EINVAL,	"Bad mode")			\
+	E(BADTHR,	406,	EINVAL,	"Bad thread")			\
+	E(NOTHR,	407,	ESRCH,	"Thread not found")		\
+	E(NOSEM,	408,	ENOENT,	"Semaphore not found")		\
+	E(NOEVF,	409,	ENXIO,	"ENOEVF")	/* FIXME */	\
+	E(NOMBX,	410,	ENXIO,	"ENOMBX")	/* FIXME */	\
+	E(NOVPL,	411,	ENXIO,	"ENOVPL")	/* FIXME */	\
+	E(NOFPL,	412,	ENXIO,	"ENOFPL")	/* FIXME */	\
+	E(DORM,		413,	EINVAL,	"EDORM")	/* FIXME */	\
+	E(NODORM,	414,	ENOENT,	"ENODORM")	/* FIXME */	\
+	E(NOSUSP,	415,	ENOENT,	"ENOSUSP")	/* FIXME */	\
+	E(BADWAIT,	416,	EINVAL,	"EBADWAIT")	/* FIXME */	\
+	E(NOWAIT,	417,	ENOENT,	"ENOWAIT")	/* FIXME */	\
+	E(RELWAIT,	418,	EINVAL,	"ERELWAIT")	/* FIXME */	\
+	E(SEMZERO,	419,	EINVAL,	"ESEMZERO")	/* FIXME */	\
+	E(SEMOVF,	420,	EINVAL,	"ESEMOVF")	/* FIXME */	\
+	E(EVFCOND,	421,	EINVAL,	"EEVFCOND")	/* FIXME */	\
+	E(EVFMULTI,	422,	EINVAL,	"EEVFMULTI")	/* FIXME */	\
+	E(EVFILPAT,	423,	EINVAL,	"EEVFILPAT")	/* FIXME */	\
+	E(MBOXNOMSG,	424,	ENOENT,	"EMBOXNOMSG")	/* FIXME */	\
+	E(WAITDEL,	425,	EINVAL,	"EWAITDEL")	/* FIXME */	\
+	E(INVMEMBLK,	426,	EINVAL,	"EINVMEMBLK")	/* FIXME */	\
+	E(INVMEMSZ,	427,	ENOSPC,	"EINVMEMSZ")	/* FIXME */
+
+enum iop_error {
+#define IOP_ERROR_ENUM(identifier, number, errno, description)		\
+	IOP_E##identifier = number,
+	IOP_ERRORS(IOP_ERROR_ENUM)
+};
+
+int errno_for_iop_error(int ioperr);
+
+const char *iop_error_message(int ioperr);
+
+#endif /* __ASM_MACH_PS2_IOP_ERROR_H */
drivers/ps2/sif.c +39 -0
index 6564af245880..8320c5b68d17 100644
@@ -42,6 +42,7 @@
 #include <asm/io.h>
 
 #include <asm/mach-ps2/dmac.h>
+#include <asm/mach-ps2/iop-error.h>
 #include <asm/mach-ps2/irq.h>
 #include <asm/mach-ps2/sif.h>
 
@@ -315,6 +316,44 @@ static void sif_disable_dma(void)
 	outl(DMAC_CHCR_STOP, DMAC_SIF1_CHCR);
 }
 
+/**
+ * errno_for_iop_error - kernel error number corresponding to a given IOP error
+ * @ioperr: IOP error number
+ *
+ * Return: approximative kernel error number
+ */
+int errno_for_iop_error(int ioperr)
+{
+	switch (ioperr) {
+#define IOP_ERROR_ERRNO(identifier, number, errno, description)		\
+	case -IOP_E##identifier: return -errno;
+	IOP_ERRORS(IOP_ERROR_ERRNO)
+	}
+
+	return -1000 < ioperr && ioperr < 0 ? -EINVAL : ioperr;
+}
+EXPORT_SYMBOL_GPL(errno_for_iop_error);
+
+/**
+ * iop_error_message - message corresponding to a given IOP error
+ * @ioperr: IOP error number
+ *
+ * Return: error message string
+ */
+const char *iop_error_message(int ioperr)
+{
+	switch (ioperr) {
+	case 0:              return "Success";
+	case 1:              return "Error";
+#define IOP_ERROR_MSG(identifier, number, errno, description)		\
+	case IOP_E##identifier: return description;
+	IOP_ERRORS(IOP_ERROR_MSG)
+	}
+
+	return "Unknown error";
+}
+EXPORT_SYMBOL_GPL(iop_error_message);
+
 /**
  * sif_init - initialise the SIF with a reset
  *
Signature · 1 line
2.21.0
#74 lore raw

PATCH 54/120 MIPS: PS2: SIF: SIF register write command support

In reply to Fredrik Noring

The SIF register write command is used to establish the remote procedure call (RPC) protocol between the IOP and the kernel. Its full use is unclear. Only register zero (SIF_SREG_RPCINIT) will be used in a subsequent change.

---
 arch/mips/include/asm/mach-ps2/sif.h |  5 ++
 drivers/ps2/sif.c                    | 75 ++++++++++++++++++++++++++++
 2 files changed, 80 insertions(+)
arch/mips/include/asm/mach-ps2/sif.h +5 -0
index 94da96bd21d1..9d660155cc5f 100644
@@ -69,4 +69,9 @@ struct sif_cmd_header
 	u32 opt;
 };
 
+typedef void (*sif_cmd_cb)(const struct sif_cmd_header *header,
+	const void *data, void *arg);
+
+int sif_request_cmd(u32 cmd_id, sif_cmd_cb cb, void *arg);
+
 #endif /* __ASM_MACH_PS2_SIF_H */
drivers/ps2/sif.c +75 -0

111 lines not on this page · raw message

Signature · 1 line
2.21.0
#75 lore raw

PATCH 55/120 MIPS: PS2: SIF: Respond to remote procedure call (RPC) bind command

In reply to Fredrik Noring

The IOP can service remote procedure calls (RPCs) from the main (R5900) processor. The first step is to send a bind command that the IOP will respond to, to establish a client connection. The main processor responds by issuing a bind end command to the IOP.

Allocating IOP memory and linking IOP modules are examples of IOP RPC services that will be used in subsequent changes. IOP memory is needed for USB OHCI support.

---
 arch/mips/include/asm/mach-ps2/sif.h | 11 +++++++
 drivers/ps2/sif.c                    | 49 ++++++++++++++++++++++++++++
 2 files changed, 60 insertions(+)
arch/mips/include/asm/mach-ps2/sif.h +11 -0
index 9d660155cc5f..3d163980a4be 100644
@@ -74,4 +74,15 @@ typedef void (*sif_cmd_cb)(const struct sif_cmd_header *header,
 
 int sif_request_cmd(u32 cmd_id, sif_cmd_cb cb, void *arg);
 
+struct sif_rpc_client
+{
+	iop_addr_t server;
+	iop_addr_t server_buffer;
+
+	size_t client_size_max;
+	void *client_buffer;
+
+	struct completion done;
+};
+
 #endif /* __ASM_MACH_PS2_SIF_H */
drivers/ps2/sif.c +49 -0

73 lines not on this page · raw message

Signature · 1 line
2.21.0
#76 lore raw

PATCH 56/120 MIPS: PS2: SIF: Respond to RPC bind end command

In reply to Fredrik Noring

The IOP responds with a bind end to the main processor's corresponding command, containing the IOP server memory address to store RPC data.

---
 drivers/ps2/sif.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)
drivers/ps2/sif.c +23 -0
index 7d0b120398d1..095d11810499 100644
@@ -298,6 +298,28 @@ static struct sif_cmd_handler *handler_from_cid(u32 cmd_id)
 	return id < CMD_HANDLER_MAX ? &cmd_handlers[id] : NULL;
 }
 
+static void cmd_rpc_end(const struct sif_cmd_header *header,
+	const void *data, void *arg)
+{
+	const struct sif_rpc_request_end_packet *packet = data;
+	struct sif_rpc_client *client = packet->client;
+
+	switch (packet->client_id) {
+	case SIF_CMD_RPC_CALL:
+		break;
+
+	case SIF_CMD_RPC_BIND:
+		client->server = packet->server;
+		client->server_buffer = packet->server_buffer;
+		break;
+
+	default:
+		BUG();
+	}
+
+	complete_all(&client->done);
+}
+
 static void cmd_rpc_bind(const struct sif_cmd_header *header,
 	const void *data, void *arg)
 {
@@ -406,6 +428,7 @@ static int sif_request_cmds(void)
 	} cmds[] = {
 		{ SIF_CMD_WRITE_SREG, cmd_write_sreg, NULL },
 
+		{ SIF_CMD_RPC_END,    cmd_rpc_end,    NULL },
 		{ SIF_CMD_RPC_BIND,   cmd_rpc_bind,   NULL },
 	};
 	int err = 0;
Signature · 1 line
2.21.0
#77 lore raw

PATCH 57/120 MIPS: PS2: SIF: Reset the SIF0 (sub-to-main) DMA controller

In reply to Fredrik Noring

Put the SIF0 DMA controller in a known state with a reset.

---
 drivers/ps2/sif.c | 11 +++++++++++
 1 file changed, 11 insertions(+)
drivers/ps2/sif.c +11 -0
index 095d11810499..d077a0a97e02 100644
@@ -251,6 +251,13 @@ static int sif1_write_irq(const struct sif_cmd_header *header,
 	return sif1_write_ert_int_0(header, true, true, dst, src, nbytes);
 }
 
+static void sif0_reset_dma(void)
+{
+	outl(0, DMAC_SIF0_QWC);
+	outl(0, DMAC_SIF0_MADR);
+	outl(DMAC_CHCR_RECVC_TIE, DMAC_SIF0_CHCR);
+}
+
 static int sif_cmd_opt_copy(u32 cmd_id, u32 opt, const void *pkt,
 	size_t pktsize, iop_addr_t dst, const void *src, size_t nbytes)
 {
@@ -521,6 +528,8 @@ EXPORT_SYMBOL_GPL(iop_error_message);
  *
  *  9. Register SIF commands to enable remote procedure calls (RPCs).
  *
+ * 10. Reset the SIF0 (sub-to-main) DMA controller.
+ *
  * Return: 0 on success, otherwise a negative error number
  */
 static int __init sif_init(void)
@@ -573,6 +582,8 @@ static int __init sif_init(void)
 		goto err_request_commands;
 	}
 
+	sif0_reset_dma();
+
 	return 0;
 
 err_request_commands:
Signature · 1 line
2.21.0
#78 lore raw

PATCH 58/120 MIPS: PS2: SIF: Handle SIF0 (sub-to-main) RPCs via interrupts

In reply to Fredrik Noring

The SIF0 DMA controller asserts an interrupt on RPC completion. The kernel invokes the corresponding callback in the command table, and then resets the SIF0 DMA to receive the next command.

---
 drivers/ps2/sif.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)
drivers/ps2/sif.c +65 -0
index d077a0a97e02..fac1a5117d1c 100644
@@ -164,11 +164,21 @@ static bool sif_smflag_bootend(void)
 	return (sif_read_smflag() & SIF_STATUS_BOOTEND) != 0;
 }
 
+static bool sif0_busy(void)
+{
+	return (inl(DMAC_SIF0_CHCR) & DMAC_CHCR_BUSY) != 0;
+}
+
 static bool sif1_busy(void)
 {
 	return (inl(DMAC_SIF1_CHCR) & DMAC_CHCR_BUSY) != 0;
 }
 
+/*
+ * sif1_ready may be called via cmd_rpc_bind that is a response from
+ * SIF_CMD_RPC_BIND via sif0_dma_handler from IRQ_DMAC_SIF0. Thus we
+ * currently have to busy-wait here if SIF1 is busy.
+ */
 static bool sif1_ready(void)
 {
 	size_t countout = 50000;	/* About 5 s */
@@ -305,6 +315,48 @@ static struct sif_cmd_handler *handler_from_cid(u32 cmd_id)
 	return id < CMD_HANDLER_MAX ? &cmd_handlers[id] : NULL;
 }
 
+static void cmd_call_handler(
+	const struct sif_cmd_header *header, const void *data)
+{
+	const struct sif_cmd_handler *handler = handler_from_cid(header->cmd);
+
+	if (!handler || !handler->cb) {
+		pr_err_once("sif: Invalid command 0x%x ignored\n", header->cmd);
+		return;
+	}
+
+	handler->cb(header, data, handler->arg);
+}
+
+static irqreturn_t sif0_dma_handler(int irq, void *dev_id)
+{
+	const struct sif_cmd_header *header = sif0_buffer;
+	const void *payload = &header[1];
+
+	if (sif0_busy())
+		return IRQ_NONE;
+
+	dma_cache_inv((unsigned long)sif0_buffer, SIF_CMD_PACKET_MAX);
+
+	if (header->data_size)
+		dma_cache_inv((unsigned long)phys_to_virt(header->data_addr),
+				header->data_size);
+
+	if (header->packet_size < sizeof(*header) ||
+	    header->packet_size > SIF_CMD_PACKET_MAX) {
+		pr_err_once("sif: Invalid command header size %u bytes\n",
+			header->packet_size);
+		goto err;
+	}
+
+	cmd_call_handler(header, payload);
+
+err:
+	sif0_reset_dma();	/* Reset DMA for the next incoming packet. */
+
+	return IRQ_HANDLED;
+}
+
 static void cmd_rpc_end(const struct sif_cmd_header *header,
 	const void *data, void *arg)
 {
@@ -530,6 +582,8 @@ EXPORT_SYMBOL_GPL(iop_error_message);
  *
  * 10. Reset the SIF0 (sub-to-main) DMA controller.
  *
+ * 11. Service SIF0 RPCs via interrupts.
+ *
  * Return: 0 on success, otherwise a negative error number
  */
 static int __init sif_init(void)
@@ -584,8 +638,17 @@ static int __init sif_init(void)
 
 	sif0_reset_dma();
 
+	err = request_irq(IRQ_DMAC_SIF0, sif0_dma_handler, 0, "SIF0 DMA", NULL);
+	if (err) {
+		pr_err("sif: Failed to setup SIF0 handler with %d\n", err);
+		goto err_irq_sif0;
+	}
+
 	return 0;
 
+err_irq_sif0:
+	sif_disable_dma();
+
 err_request_commands:
 err_final_subaddr:
 err_iop_reset:
@@ -600,6 +663,8 @@ static void __exit sif_exit(void)
 {
 	sif_disable_dma();
 
+	free_irq(IRQ_DMAC_SIF0, NULL);
+
 	put_dma_buffers();
 }
 
Signature · 1 line
2.21.0
#79 lore raw

PATCH 59/120 MIPS: PS2: SIF: Enable the IOP to issue SIF commands

In reply to Fredrik Noring

Send the SIF_CMD_INIT_CMD command with option argument 0 to let the IOP issue SIF commands. The supplied address is the MAINADDR.

---
 drivers/ps2/sif.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
drivers/ps2/sif.c +18 -0
index fac1a5117d1c..b6c348974bdb 100644
@@ -441,6 +441,13 @@ static int iop_reset(void)
 	return iop_reset_arg(IOP_RESET_ARGS);
 }
 
+static int sif_cmd_init(dma_addr_t cmd_buffer)
+{
+	const struct sif_cmd_change_addr_packet cmd = { .addr = cmd_buffer };
+
+	return sif_cmd_opt(SIF_CMD_INIT_CMD, 0, &cmd, sizeof(cmd));
+}
+
 static int sif_read_subaddr(dma_addr_t *subaddr)
 {
 	if (!completed(sif_smflag_cmdinit))
@@ -584,6 +591,8 @@ EXPORT_SYMBOL_GPL(iop_error_message);
  *
  * 11. Service SIF0 RPCs via interrupts.
  *
+ * 12. Enable the IOP to issue SIF commands.
+ *
  * Return: 0 on success, otherwise a negative error number
  */
 static int __init sif_init(void)
@@ -644,8 +653,17 @@ static int __init sif_init(void)
 		goto err_irq_sif0;
 	}
 
+	err = sif_cmd_init(virt_to_phys(sif0_buffer));
+	if (err) {
+		pr_err("sif: Failed to initialise commands with %d\n", err);
+		goto err_cmd_init;
+	}
+
 	return 0;
 
+err_cmd_init:
+	free_irq(IRQ_DMAC_SIF0, NULL);
+
 err_irq_sif0:
 	sif_disable_dma();
 
Signature · 1 line
2.21.0
#80 lore raw

PATCH 60/120 MIPS: PS2: SIF: Enable the IOP to issue SIF RPCs

In reply to Fredrik Noring

Send the SIF_CMD_INIT_CMD command with option argument 1 to let the IOP issue remote procedure calls (RPCs). Wait for the IOP to acknowledge by writing a nonzero value to the SIF_SREG_RPCINIT SIF register.

---
 drivers/ps2/sif.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)
drivers/ps2/sif.c +51 -0
index b6c348974bdb..7a5aab785237 100644
@@ -51,6 +51,8 @@
 #define SIF0_BUFFER_SIZE	PAGE_SIZE
 #define SIF1_BUFFER_SIZE	PAGE_SIZE
 
+#define SIF_SREG_RPCINIT	0
+
 struct sif_rpc_packet_header {
 	u32 rec_id;
 	void *pkt_addr;
@@ -74,6 +76,10 @@ struct sif_rpc_bind_packet {
 	u32 server_id;
 };
 
+struct sif_cmd_change_addr_packet {
+	iop_addr_t addr;
+};
+
 struct sif_cmd_handler
 {
 	sif_cmd_cb cb;
@@ -103,6 +109,25 @@ static void cmd_write_sreg(const struct sif_cmd_header *header,
 	spin_unlock_irqrestore(&sregs_lock, flags);
 }
 
+static s32 read_sreg(u32 reg)
+{
+	unsigned long flags;
+	s32 val;
+
+	BUG_ON(reg >= ARRAY_SIZE(sregs));
+
+	spin_lock_irqsave(&sregs_lock, flags);
+	val = sregs[reg];
+	spin_unlock_irqrestore(&sregs_lock, flags);
+
+	return val;
+}
+
+static bool sif_sreg_rpcinit(void)
+{
+	return read_sreg(SIF_SREG_RPCINIT) != 0;
+}
+
 /**
  * sif_write_msflag - write to set main-to-sub flag register bits
  * @mask: MSFLAG register bit values to set
@@ -296,6 +321,11 @@ static int sif_cmd_copy(u32 cmd_id, const void *pkt, size_t pktsize,
 	return sif_cmd_opt_copy(cmd_id, 0, pkt, pktsize, dst, src, nbytes);
 }
 
+static int sif_cmd_opt(u32 cmd_id, u32 opt, const void *pkt, size_t pktsize)
+{
+	return sif_cmd_opt_copy(cmd_id, opt, pkt, pktsize, 0, NULL, 0);
+}
+
 static int sif_cmd(u32 cmd_id, const void *pkt, size_t pktsize)
 {
 	return sif_cmd_copy(cmd_id, pkt, pktsize, 0, NULL, 0);
@@ -448,6 +478,17 @@ static int sif_cmd_init(dma_addr_t cmd_buffer)
 	return sif_cmd_opt(SIF_CMD_INIT_CMD, 0, &cmd, sizeof(cmd));
 }
 
+static int sif_rpc_init(void)
+{
+	int err;
+
+	err = sif_cmd_opt(SIF_CMD_INIT_CMD, 1, NULL, 0);
+	if (err)
+		return err;
+
+	return completed(sif_sreg_rpcinit) ? 0 : -EIO;
+}
+
 static int sif_read_subaddr(dma_addr_t *subaddr)
 {
 	if (!completed(sif_smflag_cmdinit))
@@ -593,6 +634,8 @@ EXPORT_SYMBOL_GPL(iop_error_message);
  *
  * 12. Enable the IOP to issue SIF commands.
  *
+ * 13. Enable the IOP to issue SIF RPCs.
+ *
  * Return: 0 on success, otherwise a negative error number
  */
 static int __init sif_init(void)
@@ -603,6 +646,7 @@ static int __init sif_init(void)
 	BUILD_BUG_ON(sizeof(struct sif_rpc_request_end_packet) != 32);
 	BUILD_BUG_ON(sizeof(struct sif_rpc_bind_packet) != 20);
 	BUILD_BUG_ON(sizeof(struct sif_cmd_header) != 16);
+	BUILD_BUG_ON(sizeof(struct sif_cmd_change_addr_packet) != 4);
 
 	sif_disable_dma();
 
@@ -659,8 +703,15 @@ static int __init sif_init(void)
 		goto err_cmd_init;
 	}
 
+	err = sif_rpc_init();
+	if (err) {
+		pr_err("sif: Failed to initialise RPC with %d\n", err);
+		goto err_rpc_init;
+	}
+
 	return 0;
 
+err_rpc_init:
 err_cmd_init:
 	free_irq(IRQ_DMAC_SIF0, NULL);
 
Signature · 1 line
2.21.0
#81 lore raw

PATCH 61/120 MIPS: PS2: SIF: sif_rpc_bind() to request an RPC server connection

In reply to Fredrik Noring

This is the first initialisation step to perform remote procedure calls.

A PAGE_SIZE buffer is allocated to store RPC data. A future improvement is to make the buffer size adjustable.

---
 arch/mips/include/asm/mach-ps2/sif.h |  2 ++
 drivers/ps2/sif.c                    | 38 ++++++++++++++++++++++++++++
 2 files changed, 40 insertions(+)
arch/mips/include/asm/mach-ps2/sif.h +2 -0
index 3d163980a4be..1d9a7ede2fb5 100644
@@ -85,4 +85,6 @@ struct sif_rpc_client
 	struct completion done;
 };
 
+int sif_rpc_bind(struct sif_rpc_client *client, u32 server_id);
+
 #endif /* __ASM_MACH_PS2_SIF_H */
drivers/ps2/sif.c +38 -0
index 7a5aab785237..ecb26239518c 100644
@@ -387,6 +387,44 @@ static irqreturn_t sif0_dma_handler(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+/**
+ * sif_rpc_bind - request a connection to an IOP RPC server
+ * @client: RPC client object to initialise
+ * @server_id: identification number for the requested IOP RPC server
+ *
+ * A %PAGE_SIZE buffer is allocated to store RPC data. A future improvement is
+ * to make its size adjustable.
+ *
+ * Return: 0 on success, otherwise a negative error number
+ */
+int sif_rpc_bind(struct sif_rpc_client *client, u32 server_id)
+{
+	const struct sif_rpc_bind_packet bind = {
+		.client    = client,
+		.server_id = server_id,
+	};
+	int err;
+
+	memset(client, 0, sizeof(*client));
+	init_completion(&client->done);
+
+	client->client_size_max = SIF0_BUFFER_SIZE;
+	client->client_buffer = (void *)__get_free_page(GFP_DMA);
+	if (client->client_buffer == NULL)
+		return -ENOMEM;
+
+	err = sif_cmd(SIF_CMD_RPC_BIND, &bind, sizeof(bind));
+	if (err) {
+		free_page((unsigned long)client->client_buffer);
+		return err;
+	}
+
+	wait_for_completion(&client->done);
+
+	return client->server ? 0 : -ENXIO;
+}
+EXPORT_SYMBOL_GPL(sif_rpc_bind);
+
 static void cmd_rpc_end(const struct sif_cmd_header *header,
 	const void *data, void *arg)
 {
Signature · 1 line
2.21.0
#82 lore raw

PATCH 62/120 MIPS: PS2: SIF: sif_rpc_unbind() to release an RPC server connection

In reply to Fredrik Noring
---
 arch/mips/include/asm/mach-ps2/sif.h |  1 +
 drivers/ps2/sif.c                    | 16 ++++++++++++++++
 2 files changed, 17 insertions(+)
arch/mips/include/asm/mach-ps2/sif.h +1 -0
index 1d9a7ede2fb5..5a3128920c9a 100644
@@ -86,5 +86,6 @@ struct sif_rpc_client
 };
 
 int sif_rpc_bind(struct sif_rpc_client *client, u32 server_id);
+void sif_rpc_unbind(struct sif_rpc_client *client);
 
 #endif /* __ASM_MACH_PS2_SIF_H */
drivers/ps2/sif.c +16 -0
index ecb26239518c..3c098a46832c 100644
@@ -395,6 +395,8 @@ static irqreturn_t sif0_dma_handler(int irq, void *dev_id)
  * A %PAGE_SIZE buffer is allocated to store RPC data. A future improvement is
  * to make its size adjustable.
  *
+ * The connection can be released with sif_rpc_unbind().
+ *
  * Return: 0 on success, otherwise a negative error number
  */
 int sif_rpc_bind(struct sif_rpc_client *client, u32 server_id)
@@ -425,6 +427,20 @@ int sif_rpc_bind(struct sif_rpc_client *client, u32 server_id)
 }
 EXPORT_SYMBOL_GPL(sif_rpc_bind);
 
+/**
+ * sif_rpc_unbind - release a connection to an IOP RPC server
+ * @client: RPC client object to release
+ *
+ * The connection must have been established with sif_rpc_bind().
+ */
+void sif_rpc_unbind(struct sif_rpc_client *client)
+{
+	free_page((unsigned long)client->client_buffer);
+
+	/* FIXME: Release the IOP RPC server part */
+}
+EXPORT_SYMBOL_GPL(sif_rpc_unbind);
+
 static void cmd_rpc_end(const struct sif_cmd_header *header,
 	const void *data, void *arg)
 {
Signature · 1 line
2.21.0
#83 lore raw

PATCH 63/120 MIPS: PS2: SIF: sif_rpc() to issue a remote procedure call

In reply to Fredrik Noring

This is the actual function to perform IOP procedure calls. For simplicity a call is synchronous. It is certainly possible to make calls more efficient, asynchronous, and so on, but ease of use is often more important than performance, for example when allocating IOP memory during initialisation of a driver.

---
 arch/mips/include/asm/mach-ps2/sif.h |  3 ++
 drivers/ps2/sif.c                    | 78 ++++++++++++++++++++++++++++
 2 files changed, 81 insertions(+)
arch/mips/include/asm/mach-ps2/sif.h +3 -0
index 5a3128920c9a..d0e59692c3c3 100644
@@ -88,4 +88,7 @@ struct sif_rpc_client
 int sif_rpc_bind(struct sif_rpc_client *client, u32 server_id);
 void sif_rpc_unbind(struct sif_rpc_client *client);
 
+int sif_rpc(struct sif_rpc_client *client, u32 rpc_id,
+	const void *send, size_t send_size, void *recv, size_t recv_size);
+
 #endif /* __ASM_MACH_PS2_SIF_H */
drivers/ps2/sif.c +78 -0

96 lines not on this page · raw message

Signature · 1 line
2.21.0
#84 lore raw

PATCH 64/120 MIPS: PS2: IOP: Permit /dev/mem to access IOP memory

In reply to Fredrik Noring
---
 arch/mips/include/asm/mach-ps2/iop.h | 21 +++++++++++++++++++++
 arch/mips/ps2/memory.c               |  2 ++
 2 files changed, 23 insertions(+)
 create mode 100644 arch/mips/include/asm/mach-ps2/iop.h
arch/mips/include/asm/mach-ps2/iop.h new file +21 -0
new file mode 100644
index 000000000000..65585451e743
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 input/output processor (IOP)
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#ifndef __ASM_MACH_PS2_IOP_H
+#define __ASM_MACH_PS2_IOP_H
+
+#define IOP_RAM_BASE	0x1c000000
+#define IOP_RAM_SIZE	0x200000
+
+#define IOP_OHCI_BASE	0x1f801600
+
+/**
+ * iop_addr_t - I/O processor (IOP) bus address
+ */
+typedef u32 iop_addr_t;
+
+#endif /* __ASM_MACH_PS2_IOP_H */
arch/mips/ps2/memory.c +2 -0
index c513b6912bb0..51614b6d0515 100644
@@ -13,11 +13,13 @@
 #include <asm/bootinfo.h>
 #include <asm/io.h>
 
+#include <asm/mach-ps2/iop.h>
 #include <asm/mach-ps2/rom.h>
 
 int valid_phys_addr_range(phys_addr_t addr, size_t size)
 {
 	return addr + size <= __pa(high_memory) ||
+	       (IOP_RAM_BASE <= addr && addr + size <= IOP_RAM_BASE + IOP_RAM_SIZE) ||
 	       (ROM0_BASE <= addr && addr + size <= ROM0_BASE + ROM0_SIZE) ||
 	       (ROM1_BASE <= addr && addr + size <= ROM1_BASE + ROM1_SIZE);
 }
Signature · 1 line
2.21.0
#85 lore raw

PATCH 65/120 MIPS: PS2: IOP: I/O processor memory support

In reply to Fredrik Noring
---
 arch/mips/include/asm/mach-ps2/iop-memory.h | 21 ++++++++
 drivers/ps2/Makefile                        |  1 +
 drivers/ps2/iop-memory.c                    | 56 +++++++++++++++++++++
 3 files changed, 78 insertions(+)
 create mode 100644 arch/mips/include/asm/mach-ps2/iop-memory.h
 create mode 100644 drivers/ps2/iop-memory.c
arch/mips/include/asm/mach-ps2/iop-memory.h new file +21 -0
new file mode 100644
index 000000000000..6efdb490c7b9
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 I/O processor (IOP) memory
+ *
+ * Copyright (C) 2018 Fredrik Noring
+ */
+
+#ifndef __ASM_MACH_PS2_IOP_MEMORY_H
+#define __ASM_MACH_PS2_IOP_MEMORY_H
+
+#include <linux/types.h>
+
+#include <asm/mach-ps2/iop.h>
+
+iop_addr_t iop_phys_to_bus(phys_addr_t paddr);
+
+phys_addr_t iop_bus_to_phys(iop_addr_t baddr);
+
+void *iop_bus_to_virt(iop_addr_t baddr);
+
+#endif /* __ASM_MACH_PS2_IOP_MEMORY_H */
drivers/ps2/Makefile +1 -0
index ef561a802bdd..6f193007ebc6 100644
@@ -1,2 +1,3 @@
+obj-m				+= iop-memory.o
 obj-m				+= iop-registers.o
 obj-m				+= sif.o
drivers/ps2/iop-memory.c new file +56 -0

56 lines not on this page · raw message

Signature · 1 line
2.21.0
#86 lore raw

PATCH 66/120 FIXME: Export _dma_cache_{wback,wback_inv,inv}

In reply to Fredrik Noring

Commit e58cfbfb32d1 ("MIPS: remove the _dma_cache_wback_inv export") removes EXPORT_SYMBOL(_dma_cache_wback_inv) but what are the acceptable alternatives? Streaming DMA mappings?

dma_cache_wback_inv() and dma_cache_inv() are used in subsequent changes, but I suppose these must be changed to something appropriate. The current DMA handling is very simple, although the hardware is quite capable (for example, with scatter-gather lists, chaining and so on).

---
 arch/mips/mm/cache.c | 4 ++++
 1 file changed, 4 insertions(+)
arch/mips/mm/cache.c +4 -0
index 33b409391ddb..a01923b30086 100644
@@ -62,6 +62,10 @@ void (*_dma_cache_wback_inv)(unsigned long start, unsigned long size);
 void (*_dma_cache_wback)(unsigned long start, unsigned long size);
 void (*_dma_cache_inv)(unsigned long start, unsigned long size);
 
+EXPORT_SYMBOL(_dma_cache_wback_inv);
+EXPORT_SYMBOL(_dma_cache_wback);
+EXPORT_SYMBOL(_dma_cache_inv);
+
 #endif /* CONFIG_DMA_NONCOHERENT */
 
 /*
Signature · 1 line
2.21.0
#87 lore raw

PATCH 67/120 MIPS: PS2: IOP: Module linking support

In reply to Fredrik Noring

IOP modules are IRX objects based on the executable and linkable format (ELF). All valid IOP modules have a special .iopmod section containing the module name, version, etc.

When the IOP is reset, a set of modules are automatically linked from read-only memory (ROM). Non-ROM modules are handled as firmware by the IOP module linker.

IOP modules may import and export any number of library functions, including non at all. Imported libraries must be resolved and prelinked before the given module is allowed to link itself. Other modules can link with its exported libraries.

IOP modules begin to execute their entry function immediately after linking. The modules can either stay resident in the IOP, and provide services, or unlink themselves when exiting the entry function. Many modules provide remote procedure call (RPC) services via the sub-system interface (SIF).

---
 arch/mips/include/asm/mach-ps2/iop-module.h |  13 +
 drivers/Makefile                            |   1 +
 drivers/ps2/Makefile                        |   1 +
 drivers/ps2/iop-module.c                    | 277 ++++++++++++++++++++
 4 files changed, 292 insertions(+)
 create mode 100644 arch/mips/include/asm/mach-ps2/iop-module.h
 create mode 100644 drivers/ps2/iop-module.c
arch/mips/include/asm/mach-ps2/iop-module.h new file +13 -0
new file mode 100644
index 000000000000..f61141031de0
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 input/output processor (IOP) module linker
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#ifndef __ASM_MACH_PS2_IOP_MODULE_H
+#define __ASM_MACH_PS2_IOP_MODULE_H
+
+int iop_module_request(const char *name, int version, const char *arg);
+
+#endif /* __ASM_MACH_PS2_IOP_MODULE_H */
drivers/Makefile +1 -0
index 6d37564e783c..1ed5b83dc528 100644
@@ -141,6 +141,7 @@ obj-y				+= clocksource/
 endif
 obj-$(CONFIG_DCA)		+= dca/
 obj-$(CONFIG_HID)		+= hid/
+obj-$(CONFIG_SONY_PS2)		+= ps2/
 obj-$(CONFIG_PPC_PS3)		+= ps3/
 obj-$(CONFIG_OF)		+= of/
 obj-$(CONFIG_SSB)		+= ssb/
drivers/ps2/Makefile +1 -0
index 6f193007ebc6..b04e4d3c3374 100644
@@ -1,3 +1,4 @@
 obj-m				+= iop-memory.o
+obj-m				+= iop-module.o
 obj-m				+= iop-registers.o
 obj-m				+= sif.o
drivers/ps2/iop-module.c new file +277 -0

277 lines not on this page · raw message

Signature · 1 line
2.21.0
#88 lore raw

PATCH 68/120 MIPS: PS2: IOP: Verify that modules are IRX objects

In reply to Fredrik Noring

IOP modules are checked, mainly to print helpful error messages in case of mistakes. IOP modules must have an .iopmod section with the special ELF type SHT_LOPROC+0x80.

---
 drivers/ps2/iop-module.c | 154 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 154 insertions(+)
drivers/ps2/iop-module.c +154 -0
index 0e4a2173c001..d332a7d1af60 100644
@@ -69,6 +69,140 @@ static struct sif_rpc_client load_file_rpc_client;
 
 #define IOPMOD_MAX_PATH		252
 #define IOPMOD_MAX_ARG		252
+#define IOPMOD_MAX_LIBRARY_NAME	8
+
+#define IOPMOD_NO_ID		0xffffffff
+
+#define SHT_IOPMOD		(SHT_LOPROC + 0x80)
+
+/**
+ * struct irx_iopmod - special .iopmod section with module name, version, etc.
+ * @id_addr: address of a special identification structure, or %IOPMOD_NO_ID
+ * @entry_addr: module entry address to begin executing code
+ * @unknown: FIXME
+ * @text_size: size in bytes of text section
+ * @data_size: size in bytes of data section
+ * @bss_size: size in bytes of BSS section
+ * @version: module version in BCD
+ * @name: NUL-terminated name of module
+ */
+struct irx_iopmod {
+	u32 id_addr;
+	u32 entry_addr;
+	u32 unknown;
+	u32 text_size;
+	u32 data_size;
+	u32 bss_size;
+	u16 version;
+	char name[0];
+};
+
+/**
+ * elf_ent_for_offset - pointer given an ELF offset
+ * @offset: ELF offset
+ * @ehdr: ELF header of module
+ *
+ * Return: pointer for a given ELF offset
+ */
+static const void *elf_ent_for_offset(Elf32_Off offset,
+	const struct elf32_hdr *ehdr)
+{
+	return &((const u8 *)ehdr)[offset];
+}
+
+/**
+ * elf_first_section - first ELF section
+ * @ehdr: ELF header of module
+ *
+ * Return: pointer to the first ELF section, or %NULL if it does not exist
+ */
+static const struct elf32_shdr *elf_first_section(const struct elf32_hdr *ehdr)
+{
+	return ehdr->e_shnum ? elf_ent_for_offset(ehdr->e_shoff, ehdr) : NULL;
+}
+
+/**
+ * elf_next_section - next ELF section
+ * @shdr: header of current section
+ * @ehdr: ELF header of module
+ *
+ * Return: section following the current section, or %NULL
+ */
+static const struct elf32_shdr *elf_next_section(
+	const struct elf32_shdr *shdr, const struct elf32_hdr *ehdr)
+{
+	const struct elf32_shdr *next = &shdr[1];
+	const struct elf32_shdr *past = &elf_first_section(ehdr)[ehdr->e_shnum];
+
+	return next == past ? NULL: next;
+}
+
+/**
+ * elf_for_each_section - iterate over all ELF sections
+ * @shdr: &struct elf32_shdr loop cursor
+ * @ehdr: ELF header of module to iterate
+ */
+#define elf_for_each_section(shdr, ehdr)				\
+	for ((shdr) = elf_first_section((ehdr));			\
+	     (shdr);							\
+	     (shdr) = elf_next_section((shdr), (ehdr)))
+
+/**
+ * elf_first_section_with_type - first section with given type
+ * @type: type of section to search for
+ * @ehdr: ELF header of module to search
+ *
+ * Return: pointer to the first occurrence of the section, or %NULL if it does
+ * 	not exist
+ */
+static const struct elf32_shdr *elf_first_section_with_type(
+	Elf32_Word type, const struct elf32_hdr *ehdr)
+{
+	const struct elf32_shdr *shdr;
+
+	elf_for_each_section (shdr, ehdr)
+		if (shdr->sh_type == type)
+			return shdr;
+
+	return NULL;
+}
+
+/**
+ * elf_identify - does the buffer contain an ELF object?
+ * @buffer: pointer to data to identify
+ * @size: size in bytes of buffer
+ *
+ * Return: %true if the buffer looks like an ELF object, otherwise %false
+ */
+static bool elf_identify(const void *buffer, size_t size)
+{
+	const struct elf32_hdr *ehdr = buffer;
+
+	if (size < sizeof(*ehdr))
+		return false;
+
+	return ehdr->e_ident[EI_MAG0] == ELFMAG0 &&
+	       ehdr->e_ident[EI_MAG1] == ELFMAG1 &&
+	       ehdr->e_ident[EI_MAG2] == ELFMAG2 &&
+	       ehdr->e_ident[EI_MAG3] == ELFMAG3 &&
+	       ehdr->e_ident[EI_VERSION] == EV_CURRENT;
+}
+
+/**
+ * irx_iopmod - give .iopmod section pointer, if it exists
+ * @ehdr: ELF header of module
+ *
+ * The .iopmod section is specific to IOP (IRX) modules.
+ *
+ * Return: .iopmod section pointer, or %NULL
+ */
+static const struct irx_iopmod *irx_iopmod(const struct elf32_hdr *ehdr)
+{
+	const struct elf32_shdr *shdr =
+		elf_first_section_with_type(SHT_IOPMOD, ehdr);
+
+	return shdr ? elf_ent_for_offset(shdr->sh_offset, ehdr) : NULL;
+}
 
 /**
  * major_version - major version of version in BCD
@@ -92,6 +226,18 @@ static unsigned int minor_version(unsigned int version)
 	return bcd2bin(version & 0xff);
 }
 
+/**
+ * irx_identify - does the buffer contain an IRX object?
+ * @buffer: pointer to data to identify
+ * @size: size in bytes of buffer
+ *
+ * Return: %true if the buffer looks like an IRX object, otherwise %false
+ */
+static bool irx_identify(const void *buffer, size_t size)
+{
+	return elf_identify(buffer, size) && irx_iopmod(buffer) != NULL;
+}
+
 /**
  * iop_module_link_buffer - link IOP module given in buffer
  * @buf: buffer containing the IOP module to link
@@ -189,10 +335,18 @@ static int iop_module_request_firmware(
 	if (err < 0)
 		goto err_request;
 
+	if (!irx_identify(fw->data, fw->size)) {
+		pr_err("iop-module: %s module is not an IRX object\n",
+			filepath);
+		err = -ENOEXEC;
+		goto err_identify;
+	}
+
 	ehdr = (const struct elf32_hdr *)fw->data;
 
 	err = iop_module_link_buffer(fw->data, fw->size, arg);
 
+err_identify:
 err_request:
 err_name:
 
Signature · 1 line
2.21.0
#89 lore raw

PATCH 69/120 MIPS: PS2: IOP: Module version compatibility verification

In reply to Fredrik Noring

IOP module link requests are only permitted if the major versions match and the module version is at least of the same minor as the requested version.

The IOP module version is read from the special .iopmod section.

---
 drivers/ps2/iop-module.c | 44 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)
drivers/ps2/iop-module.c +44 -0
index d332a7d1af60..532b3cce91c0 100644
@@ -12,6 +12,10 @@
  * (ELF). All valid IOP modules have a special `.iopmod` section containing
  * the module name, version, etc.
  *
+ * IOP module link requests are only permitted if the major versions match
+ * and the module version is at least of the same minor as the requested
+ * version.
+ *
  * When the IOP is reset, a set of modules are automatically linked from
  * read-only memory (ROM). Non-ROM modules are handled as firmware by the
  * IOP module linker.
@@ -226,6 +230,34 @@ static unsigned int minor_version(unsigned int version)
 	return bcd2bin(version & 0xff);
 }
 
+/**
+ * version_compatible - is the version compatible with the requested version?
+ * @version: version to check
+ * @version_request: requested version
+ *
+ * Return: %true if the major versions match and the version to check is at
+ * 	least of the same minor as the requested version, otherwise %false
+ */
+static bool version_compatible(int version, int requested_version)
+{
+	return major_version(version) == major_version(requested_version) &&
+	       minor_version(version) >= minor_version(requested_version);
+}
+
+/**
+ * irx_version_compatible - is the module compatible with the requested version?
+ * @ehdr: ELF header of module to check
+ * @requested_version: request version
+ *
+ * Return: %true if the major versions match and the module version is at
+ * 	least of the same minor as the requested version, otherwise %false
+ */
+static bool irx_version_compatible(const struct elf32_hdr *ehdr,
+	int requested_version)
+{
+	return version_compatible(irx_iopmod(ehdr)->version, requested_version);
+}
+
 /**
  * irx_identify - does the buffer contain an IRX object?
  * @buffer: pointer to data to identify
@@ -344,8 +376,20 @@ static int iop_module_request_firmware(
 
 	ehdr = (const struct elf32_hdr *)fw->data;
 
+	if (!irx_version_compatible(ehdr, version)) {
+		pr_err("iop-module: %s module version %u.%u is incompatible with requested version %u.%u\n",
+			filepath,
+			major_version(irx_iopmod(ehdr)->version),
+			minor_version(irx_iopmod(ehdr)->version),
+			major_version(version),
+			minor_version(version));
+		err = -ENOEXEC;
+		goto err_incompatible;
+	}
+
 	err = iop_module_link_buffer(fw->data, fw->size, arg);
 
+err_incompatible:
 err_identify:
 err_request:
 err_name:
Signature · 1 line
2.21.0
#90 lore raw

PATCH 70/120 MIPS: PS2: IOP: Avoid linking already linked library modules

In reply to Fredrik Noring

As a simplification the IOP module linker assumes that modules with exported libraries are resident. The primary effect is that it refuses to link modules exporting libraries with the same names more than once, even if the they may have been unlinked in the IOP. This is because the IOP module linker maintains its own list of exported libraries, rather than asking the IOP about them, which remains to be implemented. For now it is assumed that modules do not unlink themselves.

The IOP module linker also assumes that if a module exports a library it has the same name as the module. The simplifies resolving dependencies. In general, and with most ROM modules, the module name is not the same as the exported library names. A single module may export multiple libraries, but that is currently not not fully supported with IOP modules for Linux, due to dependency resolving limitations.

---
 drivers/ps2/iop-module.c | 282 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 282 insertions(+)
drivers/ps2/iop-module.c +282 -0
index 532b3cce91c0..ef7b254bb2cf 100644
@@ -29,6 +29,20 @@
  * The modules can either stay resident in the IOP, and provide services, or
  * unlink themselves when exiting the entry function. Many modules provide
  * remote procedure call (RPC) services via the sub-system interface (SIF).
+ *
+ * As a simplification the IOP module linker assumes that modules with
+ * exported libraries are resident. The primary effect is that it refuses to
+ * link modules exporting libraries with the same names more than once, even
+ * if the they may have been unlinked in the IOP. This is because the IOP
+ * module linker maintains its own list of exported libraries, rather than
+ * asking the IOP about them, which remains to be implemented.
+ *
+ * The IOP module linker also assumes that if a module exports a library it
+ * has the same name as the module. The simplifies resolving dependencies. In
+ * general, and with most ROM modules, the module name is not the same as the
+ * exported library names. A single module may export multiple libraries, but
+ * that is currently not not fully supported with IOP modules for Linux, due
+ * to dependency resolving limitations.
  */
 
 #include <linux/bcd.h>
@@ -70,12 +84,15 @@ static DEFINE_MUTEX(iop_module_lock);
 
 static struct device *iop_module_device;
 static struct sif_rpc_client load_file_rpc_client;
+static LIST_HEAD(linked_libraries);
 
 #define IOPMOD_MAX_PATH		252
 #define IOPMOD_MAX_ARG		252
 #define IOPMOD_MAX_LIBRARY_NAME	8
 
 #define IOPMOD_NO_ID		0xffffffff
+#define IOPMOD_IMPORT_MAGIC	0x41e00000
+#define IOPMOD_EXPORT_MAGIC	0x41c00000
 
 #define SHT_IOPMOD		(SHT_LOPROC + 0x80)
 
@@ -101,6 +118,93 @@ struct irx_iopmod {
 	char name[0];
 };
 
+/**
+ * struct irx_import_link - link entry for an imported library
+ * @jr: unconditional MIPS I jump to return address instruction
+ * @jr.target: jump target, to be resolved by the IOP linker
+ * @jr.op: operation code for the jump register instruction
+ * @li: 16-bit load immediate pseudo-instruction
+ * @li.imm: index of the imported library link entry
+ * @li.rt: `$0` register
+ * @li.rs: `$0` register
+ * @li.op: operation code for the load immediate pseudo-instruction
+ */
+struct irx_import_link {
+	struct {
+		u32 target : 26;
+		u32 op : 6;
+	} jr;
+	struct {
+		u32 imm : 16;
+		u32 rt : 5;
+		u32 rs : 5;
+		u32 op : 6;
+	} li;
+};
+
+/**
+ * struct irx_import_library - link entry table for an imported library
+ * @magic: %IOPMOD_IMPORT_MAGIC marks the beginning of the link entry table
+ * @zero: always zero
+ * @version: 16-bit version in BCD, with 8-bit minor and 8-bit major
+ * @name: library name, not NUL terminated unless shorter than
+ * 	%IOPMOD_MAX_LIBRARY_NAME characters
+ * @link: array of imported link entries, with the terminating entry zero
+ *
+ * The &struct irx_import_library resides in the .text section of the module.
+ */
+struct irx_import_library {
+	u32 magic;
+	u32 zero;
+	u32 version;
+	char name[IOPMOD_MAX_LIBRARY_NAME];
+	struct irx_import_link link[0];
+};
+
+/**
+ * struct irx_export_link - link entry for an exported library
+ * @addr: address to link
+ */
+struct irx_export_link {
+	u32 addr;
+};
+
+/**
+ * struct irx_export_library - link entry table for an exported library
+ * @magic: %IOPMOD_EXPORT_MAGIC marks the beginning of the link entry table
+ * @zero: always zero
+ * @version: 16-bit version in BCD, with 8-bit minor and 8-bit major
+ * @name: library name, not NUL terminated unless shorter than
+ * 	%IOPMOD_MAX_LIBRARY_NAME characters
+ * @link: array of exported link entries, with the terminating entry zero
+ *
+ * The &struct irx_export_library resides in the .text section of the module.
+ */
+struct irx_export_library {
+	u32 magic;
+	u32 zero;
+	u32 version;
+	char name[IOPMOD_MAX_LIBRARY_NAME];
+	struct irx_export_link link[0];
+};
+
+/**
+ * struct library_entry - list of linked libraries
+ * @list: linked list of library entries
+ * @name: library name, not NUL terminated unless shorter than
+ * 	%IOPMOD_MAX_LIBRARY_NAME characters
+ * @version: 16-bit version in BCD, with 8-bit minor and 8-bit major
+ *
+ * The IOP module linker maintains its own list of linked libraries, rather
+ * than asking the IOP about them, which remains to be implemented.
+ */
+struct library_entry {
+	struct list_head list;
+
+	char name[IOPMOD_MAX_LIBRARY_NAME];
+	int version;
+};
+
 /**
  * elf_ent_for_offset - pointer given an ELF offset
  * @offset: ELF offset
@@ -151,6 +255,37 @@ static const struct elf32_shdr *elf_next_section(
 	     (shdr);							\
 	     (shdr) = elf_next_section((shdr), (ehdr)))
 
+/**
+ * elf_strings - base of ELF module string table
+ * @ehdr: ELF header of module
+ *
+ * Return: pointer to base of ELF module table
+ */
+static const char *elf_strings(const struct elf32_hdr *ehdr)
+{
+	const struct elf32_shdr *shdr;
+
+	if (ehdr->e_shstrndx == SHN_UNDEF)
+		return NULL;
+
+	shdr = &elf_first_section(ehdr)[ehdr->e_shstrndx];
+
+	return elf_ent_for_offset(shdr->sh_offset, ehdr);
+}
+
+/**
+ * elf_section_name - name of section
+ * @shdr: header of section to provide name for
+ * @ehdr: ELF header of module for section
+ *
+ * Return: name of given section
+ */
+static const char *elf_section_name(const struct elf32_shdr *shdr,
+	const struct elf32_hdr *ehdr)
+{
+	return &elf_strings(ehdr)[shdr->sh_name];
+}
+
 /**
  * elf_first_section_with_type - first section with given type
  * @type: type of section to search for
@@ -171,6 +306,26 @@ static const struct elf32_shdr *elf_first_section_with_type(
 	return NULL;
 }
 
+/**
+ * elf_first_section_with_name - first section with given name
+ * @name: name of section to search for
+ * @ehdr: ELF header of module to search
+ *
+ * Return: pointer to the first occurrence of the section, or %NULL if it does
+ * 	not exist
+ */
+static const struct elf32_shdr *elf_first_section_with_name(
+	const char *name, const struct elf32_hdr *ehdr)
+{
+	const struct elf32_shdr *shdr;
+
+	elf_for_each_section (shdr, ehdr)
+		if (strcmp(elf_section_name(shdr, ehdr), name) == 0)
+			return shdr;
+
+	return NULL;
+}
+
 /**
  * elf_identify - does the buffer contain an ELF object?
  * @buffer: pointer to data to identify
@@ -192,6 +347,69 @@ static bool elf_identify(const void *buffer, size_t size)
 	       ehdr->e_ident[EI_VERSION] == EV_CURRENT;
 }
 
+/**
+ * library_entry - find occurrence of 4-byte magic integer
+ * @library: import or export library starting pointer
+ * @magic: 4-byte magic integer to search for
+ * @ehdr: ELF header of module to search
+ *
+ * The %IOPMOD_IMPORT_MAGIC and %IOPMOD_EXPORT_MAGIC 4-byte integers are used
+ * to mark libraries that are imported and exported by the module.
+ *
+ * Return: library entry following the current library entry, or %NULL
+ */
+static const void *library_entry(const void *library, u32 magic,
+	const struct elf32_hdr *ehdr)
+{
+	const struct elf32_shdr *shdr =
+		elf_first_section_with_name(".text", ehdr);
+	const u32 *text = elf_ent_for_offset(shdr->sh_offset, ehdr);
+	const size_t length = shdr->sh_size / sizeof(*text);
+	const size_t index = library ? ((u32 *)library - text) + 1 : 0;
+	size_t i;
+
+	for (i = index; i < length; i++)
+		if (text[i] == magic)
+			return &text[i];
+
+	return NULL;
+}
+
+/**
+ * irx_first_export_library - first exported library entry
+ * @ehdr: ELF header of module
+ *
+ * Return: first exported library entry, or %NULL
+ */
+static const struct irx_export_library *irx_first_export_library(
+	const struct elf32_hdr *ehdr)
+{
+	return library_entry(NULL, IOPMOD_EXPORT_MAGIC, ehdr);
+}
+
+/**
+ * irx_next_export_library - next exported library entry
+ * @library: current library entry
+ * @ehdr: ELF header of module
+ *
+ * Return: exported library entry following the current library entry, or %NULL
+ */
+static const struct irx_export_library *irx_next_export_library(
+	const struct irx_export_library *library, const struct elf32_hdr *ehdr)
+{
+	return library_entry(library, IOPMOD_EXPORT_MAGIC, ehdr);
+}
+
+/**
+ * irx_for_each_export_library - iterate over exported libraries
+ * @library: &struct irx_export_library loop cursor
+ * @ehdr: ELF header of module to iterate
+ */
+#define irx_for_each_export_library(library, ehdr)			\
+	for ((library) = irx_first_export_library(ehdr);		\
+	     (library);							\
+	     (library) = irx_next_export_library((library), (ehdr)))
+
 /**
  * irx_iopmod - give .iopmod section pointer, if it exists
  * @ehdr: ELF header of module
@@ -270,6 +488,58 @@ static bool irx_identify(const void *buffer, size_t size)
 	return elf_identify(buffer, size) && irx_iopmod(buffer) != NULL;
 }
 
+/**
+ * library_provided_by_firmware - is the library provided by linked firmware?
+ * @name: name of the library to search for
+ *
+ * Return: %true if some previously linked firmware provides the library,
+ * 	otherwise %false
+ */
+static bool library_provided_by_firmware(const char *name)
+{
+	const struct library_entry *library;
+
+	list_for_each_entry (library, &linked_libraries, list)
+		if (strncmp(name, library->name, sizeof(library->name)) == 0)
+			return true;
+
+	return false;
+}
+
+/**
+ * register_libraries - register libraries provided by the given module
+ * @ehdr: IOP module
+ *
+ * FIXME: Libraries are maintained in a linked list by the kernel as a
+ * simplification. This list is not updated if the modules providing the
+ * libraries are unlinked. In principle one could query the IOP about its
+ * modules, but that has not been implemented yet. For now it is assumed
+ * the modules do not unlink themselves.
+ *
+ * Return: 0 on success, otherwise a negative error number
+ */
+static int register_libraries(const struct elf32_hdr *ehdr)
+{
+	const struct irx_export_library *library;
+
+	irx_for_each_export_library (library, ehdr) {
+		struct library_entry *entry =
+			kmalloc(sizeof(*entry), GFP_KERNEL);
+
+		if (!entry)
+			return -ENOMEM;
+
+		*entry = (struct library_entry) {
+			.version = library->version,
+		};
+		memcpy(entry->name, library->name, IOPMOD_MAX_LIBRARY_NAME);
+
+		list_add(&entry->list, &linked_libraries);
+	}
+
+	return 0;
+}
+
 /**
  * iop_module_link_buffer - link IOP module given in buffer
  * @buf: buffer containing the IOP module to link
@@ -327,9 +597,16 @@ static int iop_module_link_buffer(const void *buf, size_t nbyte,
 		goto err_out;
 	}
 
+	err = register_libraries(buf);
+	if (err < 0)
+		goto err_libraries;
+
 	iop_free(link.addr);
 	return 0;
 
+err_libraries:
+	/* FIXME: Unlink module here */
+
 err_out:
 	iop_free(link.addr);
 	return err;
@@ -355,6 +632,11 @@ static int iop_module_request_firmware(
 	char filepath[32];
 	int err;
 
+	if (library_provided_by_firmware(name)) {
+		pr_debug("iop-module: %s module is already provided\n", name);
+		return 0;
+	}
+
 	pr_debug("iop-module: %s module linking as firmware\n", name);
 
 	if (snprintf(filepath, sizeof(filepath),
Signature · 1 line
2.21.0
#91 lore raw

PATCH 71/120 MIPS: PS2: IOP: Resolve module dependencies

In reply to Fredrik Noring

As a simplification it is assumed that if a ROM file provides the given library dependency, then the ROM file has been prelinked into the IOP.

Hopefully, ROM file dependencies will be kept to a minimum, and new modules are instead tailored for Linux.

---
 drivers/ps2/iop-module.c | 166 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 166 insertions(+)
drivers/ps2/iop-module.c +166 -0
index ef7b254bb2cf..bb4814b5d3c4 100644
@@ -375,6 +375,64 @@ static const void *library_entry(const void *library, u32 magic,
 	return NULL;
 }
 
+/**
+ * irx_first_import_library - first imported library entry
+ * @ehdr: ELF header of module
+ *
+ * Return: first imported library entry, or %NULL
+ */
+static const struct irx_import_library *irx_first_import_library(
+	const struct elf32_hdr *ehdr)
+{
+	return library_entry(NULL, IOPMOD_IMPORT_MAGIC, ehdr);
+}
+
+/**
+ * same_import_library - are the two imported libraries the same?
+ * @a: first imported library
+ * @b: second imported library
+ *
+ * Return: %true if the libraries have the same name, otherwise %false
+ */
+static bool same_import_library(
+	const struct irx_import_library *a,
+	const struct irx_import_library *b)
+{
+	return strncmp(a->name, b->name, sizeof(a->name)) == 0;
+}
+
+/**
+ * irx_next_import_library - next imported library entry
+ * @library: current library entry
+ * @ehdr: ELF header of module
+ *
+ * Return: imported library entry following the current library entry, or %NULL
+ */
+static const struct irx_import_library *irx_next_import_library(
+	const struct irx_import_library *library, const struct elf32_hdr *ehdr)
+{
+	const struct irx_import_library *next = library;
+
+	while (next) {
+		if (!same_import_library(next, library))
+			return next;
+
+		next = library_entry(next, IOPMOD_IMPORT_MAGIC, ehdr);
+	}
+
+	return next;
+}
+
+/**
+ * irx_for_each_import_library - iterate over imported libraries
+ * @library: &struct irx_import_library loop cursor
+ * @ehdr: ELF header of module to iterate
+ */
+#define irx_for_each_import_library(library, ehdr)			\
+	for ((library) = irx_first_import_library((ehdr));		\
+	     (library);							\
+	     (library) = irx_next_import_library((library), (ehdr)))
+
 /**
  * irx_first_export_library - first exported library entry
  * @ehdr: ELF header of module
@@ -488,6 +546,45 @@ static bool irx_identify(const void *buffer, size_t size)
 	return elf_identify(buffer, size) && irx_iopmod(buffer) != NULL;
 }
 
+/**
+ * library_provided_by_module - is the library provided by the module?
+ * @name: name of the library to search for
+ * @ehdr: ELF header of module
+ *
+ * Return: %true if the module provides the library, otherwise %false
+ */
+static bool library_provided_by_module(const char *name,
+	const struct elf32_hdr *ehdr)
+{
+	const struct irx_export_library *library;
+
+	irx_for_each_export_library (library, ehdr)
+		if (strncmp(name, library->name, sizeof(library->name)) == 0)
+			return true;
+
+	return false;
+}
+
+/**
+ * library_provided_by_rom - is the library provided by the ROM directory?
+ * @name: name of the library to search for
+ * @dir: ROM directory
+ *
+ * Return: %true if some ROM file in the ROM directory provides the library,
+ * 	otherwise %false
+ */
+static bool library_provided_by_rom(const char *name, const struct rom_dir dir)
+{
+	struct rom_file file;
+
+	rom_for_each_file (file, dir)
+		if (irx_identify(file.data, file.size))
+			if (library_provided_by_module(name, file.data))
+				return true;
+
+	return false;
+}
+
 /**
  * library_provided_by_firmware - is the library provided by linked firmware?
  * @name: name of the library to search for
@@ -506,6 +603,24 @@ static bool library_provided_by_firmware(const char *name)
 	return false;
 }
 
+/**
+ * library_provided - is the library provided?
+ * @name: name of the library to search for
+ *
+ * As a simplification it is assumed that if a ROM file provides the given
+ * library, then the ROM file has been prelinked into the IOP.
+ *
+ * The IOP linker will deal with issues related to version compatibility.
+ *
+ * Return: %true if the library is provided, otherwise %false
+ */
+static bool library_provided(const char *name)
+{
+	return library_provided_by_rom(name, rom0_dir) ||
+	       library_provided_by_rom(name, rom1_dir) ||
+	       library_provided_by_firmware(name);
+}
+
 /**
  * register_libraries - register libraries provided by the given module
  * @ehdr: IOP module
@@ -615,6 +730,52 @@ static int iop_module_link_buffer(const void *buf, size_t nbyte,
 static int iop_module_request_firmware(
 	const char *name, int version, const char *arg);
 
+/**
+ * iop_module_request_dependencies - resolve and prelink dependencies for
+ * 	requested IOP module
+ * @ehdr: IOP module to request dependencies for
+ * @requesting_name: name of IOP module requesting dependencies
+ *
+ * Return: 0 on success, otherwise a negative error number
+ */
+static int iop_module_request_dependencies(const struct elf32_hdr *ehdr,
+	const char *requesting_name)
+{
+	const struct irx_import_library *library;
+
+	irx_for_each_import_library (library, ehdr) {
+		char library_name[sizeof(library->name) + 1] = { };
+		int err;
+
+		/* Convert the library name to a NUL-terminated string. */
+		memcpy(library_name, library->name, sizeof(library->name));
+
+		if (library_provided(library_name)) {
+			pr_debug("iop-module: %s dependency on %s provided\n",
+				requesting_name, library_name);
+			continue;
+		}
+
+		pr_debug("iop-module: %s module depends on %s library version %u.%u\n",
+			requesting_name, library_name,
+			major_version(library->version),
+			minor_version(library->version));
+
+		err = iop_module_request_firmware(
+			library_name, library->version, NULL);
+		if (err < 0) {
+			pr_err("iop-module: %s dependency on %s failed to resolve with %d\n",
+				requesting_name, library_name, err);
+			return err;
+		}
+
+		pr_debug("iop-module: %s dependency on %s resolved\n",
+			requesting_name, library_name);
+	}
+
+	return 0;
+}
+
 /**
  * iop_module_request_firmware - link IOP module as firmware
  * @name: name of requested module
@@ -669,8 +830,13 @@ static int iop_module_request_firmware(
 		goto err_incompatible;
 	}
 
+	err = iop_module_request_dependencies(ehdr, name);
+	if (err < 0)
+		goto err_dependency;
+
 	err = iop_module_link_buffer(fw->data, fw->size, arg);
 
+err_dependency:
 err_incompatible:
 err_identify:
 err_request:
Signature · 1 line
2.21.0
#92 lore raw

PATCH 72/120 MIPS: PS2: IOP: SIF printk command support

In reply to Fredrik Noring

Allow IOP modules to print kernel messages, with kernel log levels. This greatly simplifies debugging of subsequent IOP modules.

IOP messages are prefixed with "iop: " in the kernel log.

---
 drivers/ps2/iop-module.c | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)
drivers/ps2/iop-module.c +31 -0
index bb4814b5d3c4..18020b3673d3 100644
@@ -892,6 +892,30 @@ int iop_module_request(const char *name, int version, const char *arg)
 }
 EXPORT_SYMBOL_GPL(iop_module_request);
 
+/**
+ * cmd_printk - IOP module kernel log printk command
+ * @header: SIF command header
+ * @void: message to print
+ * @arg: optional argument to sif_request_cmd, set to %NULL
+ *
+ * The command allows IOP modules to print kernel messages, with kernel log
+ * levels. This greatly simplifies debugging of subsequent IOP modules. IOP
+ * messages are prefixed with "iop: " in the kernel log.
+ */
+static void cmd_printk(const struct sif_cmd_header *header,
+	const void *data, void *arg)
+{
+	const char *msg = data;
+
+	if (msg[0] == KERN_SOH[0]) {
+		const char fmt[] = { msg[0], msg[1],
+			'i', 'o', 'p', ':', ' ', '%', 's', '\0' };
+
+		printk(fmt, &msg[2]);
+	} else
+		printk("iop: %s", msg);
+}
+
 static int __init iop_module_init(void)
 {
 	int err;
@@ -902,6 +926,12 @@ static int __init iop_module_init(void)
 		return -ENOMEM;
 	}
 
+	err = sif_request_cmd(SIF_CMD_PRINTK, cmd_printk, NULL);
+	if (err < 0) {
+		pr_err("iop-module: Failed request printk cmd with %d\n", err);
+		goto err_printk;
+	}
+
 	err = sif_rpc_bind(&load_file_rpc_client, SIF_SID_LOAD_MODULE);
 	if (err < 0) {
 		pr_err("iop-module: Failed to bind load module with %d\n", err);
@@ -910,6 +940,7 @@ static int __init iop_module_init(void)
 
 	return 0;
 
+err_printk:
 err_bind:
 	root_device_unregister(iop_module_device);
 
Signature · 1 line
2.21.0
#93 lore raw

Re: [PATCH 072/120] MIPS: PS2: IOP: SIF printk command support

Quoted 15 lines · Fredrik Noring

Allow IOP modules to print kernel messages, with kernel log levels. This greatly simplifies debugging of subsequent IOP modules.

IOP messages are prefixed with "iop: " in the kernel log.

---
  drivers/ps2/iop-module.c | 31 +++++++++++++++++++++++++++++++
  1 file changed, 31 insertions(+)
drivers/ps2/iop-module.c +0 -0
index bb4814b5d3c4..18020b3673d3 100644

drivers/ps2/iop-module.c int iop_module_request(const char *name, int version, const char *arg) · line 898

Quoted 7 lines
drivers/ps2/iop-module.c +4 -0
@@ -892,6 +892,30 @@ int iop_module_request(const char *name, int version, const char *arg)
  }
  EXPORT_SYMBOL_GPL(iop_module_request);
 
+/**
+ * cmd_printk - IOP module kernel log printk command
+ * @header: SIF command header
+ * @void: message to print

@data, maybe?

Quoted 13 lines
drivers/ps2/iop-module.c +13 -0
+ * @arg: optional argument to sif_request_cmd, set to %NULL
+ *
+ * The command allows IOP modules to print kernel messages, with kernel log
+ * levels. This greatly simplifies debugging of subsequent IOP modules. IOP
+ * messages are prefixed with "iop: " in the kernel log.
+ */
+static void cmd_printk(const struct sif_cmd_header *header,
+	const void *data, void *arg)
+{
+	const char *msg = data;
+
+	if (msg[0] == KERN_SOH[0]) {
+		const char fmt[] = { msg[0], msg[1],

drivers/ps2/iop-module.c int iop_module_request(const char *name, int version, const char *arg) · line 921

Quoted 10 lines
drivers/ps2/iop-module.c +7 -0
+			'i', 'o', 'p', ':', ' ', '%', 's', '\0' };
+
+		printk(fmt, &msg[2]);
+	} else
+		printk("iop: %s", msg);
+}
+
  static int __init iop_module_init(void)
  {
  	int err;

[...]

MBR, Sergei

#94 lore raw

Re: [PATCH 072/120] MIPS: PS2: IOP: SIF printk command support

Hi Sergei,

Quoted 6 lines
Quoted 4 lines

+/**
+ * cmd_printk - IOP module kernel log printk command
+ * @header: SIF command header
+ * @void: message to print

@data, maybe?

Indeed, thanks!

Fredrik

#95 lore raw

PATCH 73/120 MIPS: PS2: IOP: Heap memory allocate and free

In reply to Fredrik Noring

The IOP heap memory operations are serviced by an IOP module linked from read-only memory (ROM).

---
 arch/mips/include/asm/mach-ps2/iop-heap.h | 19 +++++
 drivers/ps2/Makefile                      |  1 +
 drivers/ps2/iop-heap.c                    | 90 +++++++++++++++++++++++
 3 files changed, 110 insertions(+)
 create mode 100644 arch/mips/include/asm/mach-ps2/iop-heap.h
 create mode 100644 drivers/ps2/iop-heap.c
arch/mips/include/asm/mach-ps2/iop-heap.h new file +19 -0
new file mode 100644
index 000000000000..ee96e2ffba83
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 input/output processor (IOP) heap memory
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#ifndef __ASM_MACH_PS2_IOP_HEAP_H
+#define __ASM_MACH_PS2_IOP_HEAP_H
+
+#include <linux/types.h>
+
+#include <asm/mach-ps2/iop.h>
+
+iop_addr_t iop_alloc(size_t nbyte);
+
+int iop_free(iop_addr_t baddr);
+
+#endif /* __ASM_MACH_PS2_IOP_HEAP_H */
drivers/ps2/Makefile +1 -0
index b04e4d3c3374..02a5756cb059 100644
@@ -1,3 +1,4 @@
+obj-m				+= iop-heap.o
 obj-m				+= iop-memory.o
 obj-m				+= iop-module.o
 obj-m				+= iop-registers.o
drivers/ps2/iop-heap.c new file +90 -0

90 lines not on this page · raw message

Signature · 1 line
2.21.0
#96 lore raw

PATCH 74/120 MIPS: PS2: SIF: Request RPC IRQ command

In reply to Fredrik Noring
---
 arch/mips/include/asm/mach-ps2/irq.h |  5 +++++
 arch/mips/ps2/intc-irq.c             |  6 ++++++
 drivers/ps2/sif.c                    | 11 +++++++++++
 3 files changed, 22 insertions(+)
arch/mips/include/asm/mach-ps2/irq.h +5 -0
index 16c96aa7ca09..64d3fbf4789e 100644
@@ -74,4 +74,9 @@
 int __init intc_irq_init(void);
 int __init dmac_irq_init(void);
 
+/*
+ * IRQs asserted by the I/O processor (IOP) via the sub-system interface (SIF).
+ */
+void intc_sif_irq(unsigned int irq);
+
 #endif /* __ASM_MACH_PS2_IRQ_H */
arch/mips/ps2/intc-irq.c +6 -0
index 36cdc3dd31ca..08659758e85f 100644
@@ -85,6 +85,12 @@ static struct irqaction cascade_intc_irqaction = {
 	.handler = intc_cascade,
 };
 
+void intc_sif_irq(unsigned int irq)
+{
+	do_IRQ(irq);
+}
+EXPORT_SYMBOL_GPL(intc_sif_irq);
+
 int __init intc_irq_init(void)
 {
 	size_t i;
drivers/ps2/sif.c +11 -0
index 5c3866c460af..a7f4f00a539c 100644
@@ -568,6 +568,16 @@ int sif_request_cmd(u32 cmd_id, sif_cmd_cb cb, void *arg)
 }
 EXPORT_SYMBOL_GPL(sif_request_cmd);
 
+static void cmd_irq_relay(const struct sif_cmd_header *header,
+	const void *data, void *arg)
+{
+	const struct {
+		u32 irq;
+	} *packet = data;
+
+	intc_sif_irq(packet->irq);
+}
+
 static int iop_reset_arg(const char *arg)
 {
 	const size_t arglen = strlen(arg) + 1;
@@ -664,6 +674,7 @@ static int sif_request_cmds(void)
 		struct cmd_data *arg;
 	} cmds[] = {
 		{ SIF_CMD_WRITE_SREG, cmd_write_sreg, NULL },
+		{ SIF_CMD_IRQ_RELAY,  cmd_irq_relay,  NULL },
 
 		{ SIF_CMD_RPC_END,    cmd_rpc_end,    NULL },
 		{ SIF_CMD_RPC_BIND,   cmd_rpc_bind,   NULL },
Signature · 1 line
2.21.0
#97 lore raw

PATCH 75/120 MIPS: PS2: IOP: IRQ support

In reply to Fredrik Noring

IOP interrupts are not directly available to the kernel. IOP IRQs are instead serviced by an IRQ relay module, and then forwarded to the kernel by remote procedure calls (RPCs) via the sub-system interface (SIF).

The IRQ relay module can also forward IRQs via the SMFLAG (sub-to-main) register. This is more efficient, but the number of flags is limited. Currently only RPC forwarding is implemented in the kernel.

---
 arch/mips/include/asm/mach-ps2/irq.h |  49 ++++++-
 drivers/ps2/Makefile                 |   1 +
 drivers/ps2/iop-irq.c                | 186 +++++++++++++++++++++++++++
 3 files changed, 235 insertions(+), 1 deletion(-)
 create mode 100644 drivers/ps2/iop-irq.c
arch/mips/include/asm/mach-ps2/irq.h +48 -1
index 64d3fbf4789e..57b3e539ad92 100644
@@ -13,7 +13,7 @@
 #define INTC_STAT	0x1000f000	/* Flags are cleared by writing 1 */
 #define INTC_MASK	0x1000f010	/* Bits are reversed by writing 1 */
 
-#define NR_IRQS		56
+#define NR_IRQS		128
 
 /*
  * The interrupt controller (INTC) arbitrates interrupts from peripheral
@@ -71,6 +71,53 @@
 #define IRQ_C0_DMAC	51
 #define IRQ_C0_IRQ7	55
 
+/* Input/output processor (IOP) */
+#define IOP_IRQ_BASE	64
+#define IRQ_IOP_VBLANK	64
+#define IRQ_IOP_SBUS	65
+#define IRQ_IOP_CDVD	66
+#define IRQ_IOP_DMA	67
+#define IRQ_IOP_RTC0	68
+#define IRQ_IOP_RTC1	69
+#define IRQ_IOP_RTC2	70
+#define IRQ_IOP_SIO0	71
+#define IRQ_IOP_SIO1	72
+#define IRQ_IOP_SPU	73
+#define IRQ_IOP_PIO	74
+#define IRQ_IOP_EVBLANK	75
+#define IRQ_IOP_DVD	76
+#define IRQ_IOP_DEV9	77
+#define IRQ_IOP_RTC3	78
+#define IRQ_IOP_RTC4	79
+#define IRQ_IOP_RTC5	80
+#define IRQ_IOP_SIO2	81
+#define IRQ_IOP_HTR0	82
+#define IRQ_IOP_HTR1	83
+#define IRQ_IOP_HTR2	84
+#define IRQ_IOP_HTR3	85
+#define IRQ_IOP_USB	86
+#define IRQ_IOP_EXTR	87
+#define IRQ_IOP_ILINK	88
+#define IRQ_IOP_ILNKDMA	89
+
+#define IRQ_IOP_DMAC_MDEC_IN	96	/* Ch 0 */
+#define IRQ_IOP_DMAC_MDEC_OUT	97	/* Ch 1 */
+#define IRQ_IOP_DMAC_SIF2	98	/* Ch 2 */
+#define IRQ_IOP_DMAC_CDVD	99	/* Ch 3 */
+#define IRQ_IOP_DMAC_SPU	100	/* Ch 4 */
+#define IRQ_IOP_DMAC_PIO	101	/* Ch 5 */
+#define IRQ_IOP_DMAC_GPU_OTC	102	/* Ch 6 */
+#define IRQ_IOP_DMAC_BE		103	/* Bus error */
+#define IRQ_IOP_DMAC_SPU2	104	/* Ch 7 */
+#define IRQ_IOP_DMAC_DEV9	105	/* Ch 8 */
+#define IRQ_IOP_DMAC_SIF0	106	/* Ch 9 */
+#define IRQ_IOP_DMAC_SIF1	107	/* Ch 10 */
+#define IRQ_IOP_DMAC_SIO2_IN	108	/* Ch 11 */
+#define IRQ_IOP_DMAC_SIO2_OUT	109	/* Ch 12 */
+
+#define IRQ_IOP_SW1	126	/* R3000A Software Interrupt 1 */
+#define IRQ_IOP_SW2	127	/* R3000A Software Interrupt 2 */
+
 int __init intc_irq_init(void);
 int __init dmac_irq_init(void);
 
drivers/ps2/Makefile +1 -0
index 02a5756cb059..dd00eff52ef3 100644
@@ -1,4 +1,5 @@
 obj-m				+= iop-heap.o
+obj-m				+= iop-irq.o
 obj-m				+= iop-memory.o
 obj-m				+= iop-module.o
 obj-m				+= iop-registers.o
drivers/ps2/iop-irq.c new file +186 -0

186 lines not on this page · raw message

Signature · 1 line
2.21.0
#98 lore raw

PATCH 76/120 MIPS: PS2: GS: Define privileged Graphics Synthesizer registers

In reply to Fredrik Noring

All privileged GS registers are write-only except CSR (system status) and SIGLBLID (signal and label id)[1][2]. Reading write-only registers is emulated by shadow registers in memory. Reading unwritten registers is not permitted. Predicate functions indicate whether registers are readable.

The following privileged registers are available:

Register | Description
---------+---------------------------------------------------

PMODE    | PCRTC mode setting
SMODE1   | Mode setting related to video synchronisation
SMODE2   | Mode setting related to video synchronisation
SYNCH1   | Mode setting related to video synchronisation
SYNCH2   | Mode setting related to video synchronisation
SYNCHV   | Mode setting related to video synchronisation
SRFSH    | DRAM refresh
DISPFB1  | Setting for rectangular area read output circuit 1
DISPLAY1 | Setting for rectangular area read output circuit 1
DISPFB2  | Setting for rectangular area read output circuit 2
DISPLAY2 | Setting for rectangular area read output circuit 2
EXTBUF   | Feedback write buffer setting
EXTDATA  | Feedback write setting
EXTWRITE | Feedback write control
BGCOLOR  | Background colour setting
CSR      | System status
IMR      | Interrupt mask control
BUSDIR   | Host interface bus switching
SIGLBLID | Signal and label identification value read

---------+---------------------------------------------------

References:

[1] "EE User's Manual", version 6.0, Sony Computer Entertainment Inc.,

    p. 26.

[2] "GS User's Manual", version 6.0, Sony Computer Entertainment Inc.,

    pp. 142-157, 159.
---
 arch/mips/include/asm/mach-ps2/gs-registers.h | 548 ++++++++++++++++++
 1 file changed, 548 insertions(+)
 create mode 100644 arch/mips/include/asm/mach-ps2/gs-registers.h
arch/mips/include/asm/mach-ps2/gs-registers.h new file +548 -0
new file mode 100644
index 000000000000..ab59c751190f
@@ -0,0 +1,548 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 privileged Graphics Synthesizer (GS) registers
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+/**
+ * DOC: Privileged Graphics Synthesizer (GS) registers
+ *
+ * All privileged GS registers are write-only except CSR (system status)
+ * and SIGLBLID (signal and label id). Reading write-only registers is
+ * emulated by shadow registers in memory. Reading unwritten registers
+ * is not permitted. Predicate functions indicate whether registers are
+ * readable.
+ */
+
+#ifndef __ASM_MACH_PS2_GS_REGISTERS_H
+#define __ASM_MACH_PS2_GS_REGISTERS_H
+
+#include <asm/types.h>
+
+/* Privileged GS registers must be accessed using LD/SD instructions. */
+
+#define GS_PMODE	0x12000000  /* (WO) PCRTC mode setting */
+#define GS_SMODE1	0x12000010  /* (WO) Sync */
+#define GS_SMODE2	0x12000020  /* (WO) Sync */
+#define GS_SRFSH	0x12000030  /* (WO) DRAM refresh */
+#define GS_SYNCH1	0x12000040  /* (WO) Sync */
+#define GS_SYNCH2	0x12000050  /* (WO) Sync */
+#define GS_SYNCV	0x12000060  /* (WO) Sync */
+#define GS_DISPFB1	0x12000070  /* (WO) Rectangle read output circuit 1 */
+#define GS_DISPLAY1	0x12000080  /* (WO) Rectangle read output circuit 1 */
+#define GS_DISPFB2	0x12000090  /* (WO) Rectangle read output circuit 2 */
+#define GS_DISPLAY2	0x120000a0  /* (WO) Rectangle read output circuit 2 */
+#define GS_EXTBUF	0x120000b0  /* (WO) Feedback write buffer */
+#define GS_EXTDATA	0x120000c0  /* (WO) Feedback write setting */
+#define GS_EXTWRITE	0x120000d0  /* (WO) Feedback write function control */
+#define GS_BGCOLOR	0x120000e0  /* (WO) Background color setting */
+#define GS_CSR		0x12001000  /* (RW) System status */
+#define GS_IMR		0x12001010  /* (WO) Interrupt mask control */
+#define GS_BUSDIR	0x12001040  /* (WO) Host interface bus switching */
+#define GS_SIGLBLID	0x12001080  /* (RW) Signal and label id */
+
+/**
+ * enum gs_pmode_mmod - &gs_pmode.mmod alpha blending value
+ * @gs_mmod_circuit1: FIXME
+ * @gs_mmod_alp: FIXME
+ */
+enum gs_pmode_mmod {
+	gs_mmod_circuit1,
+	gs_mmod_alp
+};
+
+/**
+ * enum gs_pmode_amod - &gs_pmode.amod OUT1 alpha output
+ * @gs_amod_circuit1: FIXME
+ * @gs_amod_circuit2: FIXME
+ */
+enum gs_pmode_amod {
+	gs_amod_circuit1,
+	gs_amod_circuit2
+};
+
+/**
+ * enum gs_pmode_slbg - &gs_pmode.slbg alpha blending method
+ * @gs_slbg_circuit2: FIXME
+ * @gs_slbg_bgcolor: FIXME
+ */
+enum gs_pmode_slbg {
+	gs_slbg_circuit2,
+	gs_slbg_bgcolor
+};
+
+/**
+ * struct gs_pmode - PMODE privileged Graphics Synthesizer register
+ * @en1: enable read circuit 1
+ * @en2: enable read circuit 2
+ * @crtmd: CRT output switching (always 001)
+ * @mmod: alpha blending value
+ * @amod: OUT1 alpha output
+ * @slbg: alpha blending method
+ * @alp: fixed alpha (0xff = 1.0)
+ * @zero: must be zero
+ */
+struct gs_pmode {
+	u64 en1 : 1;
+	u64 en2 : 1;
+	u64 crtmd : 3;
+	u64 mmod : 1;
+	u64 amod : 1;
+	u64 slbg : 1;
+	u64 alp : 8;
+	u64 zero : 1;
+	u64 : 47;
+};
+
+/**
+ * enum gs_smode1_cmod - &gs_smode1.cmod value
+ * @gs_cmod_vesa: VESA
+ * @gs_cmod_ntsc: NTSC broadcast
+ * @gs_cmod_pal: PAL broadcast
+ */
+enum gs_smode1_cmod {
+	gs_cmod_vesa,
+	/* Reserved */
+	gs_cmod_ntsc = 2,
+	gs_cmod_pal
+};
+
+/**
+ * enum gs_smode1_gcont - &gs_smode1.gcont value
+ * @gs_gcont_rgbyc: Output RGBYc
+ * @gs_gcont_ycrcb: Output YCrCb
+ */
+enum gs_smode1_gcont {
+	gs_gcont_rgbyc,
+	gs_gcont_ycrcb
+};
+
+/**
+ * struct gs_smode1 - SMODE1 privileged Graphics Synthesizer register
+ * @rc: PLL reference divider
+ * @lc: PLL loop divider
+ * @t1248: PLL output divider
+ * @slck: FIXME
+ * @cmod: @enum gs_smode1_cmod display mode (PAL, NTSC or VESA)
+ * @ex: FIXME
+ * @prst: PLL reset
+ * @sint: PLL (phase-locked loop)
+ * @xpck: FIXME
+ * @pck2: FIXME
+ * @spml: FIXME
+ * @gcont: @enum gs_smode1_gcont select RGBYC or YCrCb
+ * @phs: HSync output
+ * @pvs: VSync output
+ * @pehs: FIXME
+ * @pevs: FIXME
+ * @clksel: FIXME
+ * @nvck: FIXME
+ * @slck2: FIXME
+ * @vcksel: FIXME
+ * @vhp: FIXME
+ *
+ * The video clock VCK = (13500000 * @lc) / ((@t1248 + 1) * @spml * @rc).
+ */
+struct gs_smode1 {
+	u64 rc : 3;
+	u64 lc : 7;
+	u64 t1248 : 2;
+	u64 slck : 1;
+	u64 cmod : 2;
+	u64 ex : 1;
+	u64 prst : 1;
+	u64 sint : 1;
+	u64 xpck : 1;
+	u64 pck2 : 2;
+	u64 spml : 4;
+	u64 gcont : 1;
+	u64 phs : 1;
+	u64 pvs : 1;
+	u64 pehs : 1;
+	u64 pevs : 1;
+	u64 clksel : 2;
+	u64 nvck : 1;
+	u64 slck2 : 1;
+	u64 vcksel : 2;
+	u64 vhp : 1;
+	u64 : 27;
+};
+
+/**
+ * enum gs_smode2_intm - &gs_smode2.intm interlace mode
+ * @gs_intm_progressive: progressive (noninterlace) mode
+ * @gs_intm_interlace: interlace mode
+ */
+enum gs_smode2_intm {
+	gs_intm_progressive,
+	gs_intm_interlace
+};
+
+/**
+ * enum gs_smode2_ffmd - &gs_smode2.ffmd FIELD or FRAME mode
+ * @gs_ffmd_field:
+ * @gs_ffmd_frame:
+ *
+ * In FIELD mode every other line is read: 0, 2, 4, ... / 1, 3, 5, ...
+ *
+ * In FRAME mode every line is read: 1, 2, 3, 4, 5, ...
+ */
+enum gs_smode2_ffmd {
+	gs_ffmd_field,
+	gs_ffmd_frame
+};
+
+/**
+ * enum gs_smode2_dpms - &gs_smode2.dpms VESA display power management
+ *      signaling (DPMS) levels
+ * @gs_dpms_on: in use
+ * @gs_dpms_standby: blanked, low power
+ * @gs_dpms_suspend: blanked, lower power
+ * @gs_dpms_off: shut off, awaiting activity
+ */
+enum gs_smode2_dpms {
+	gs_dpms_on,
+	gs_dpms_standby,
+	gs_dpms_suspend,
+	gs_dpms_off
+};
+
+/**
+ * struct gs_smode2 - SMODE2 privileged Graphics Synthesizer register
+ * @intm: &enum gs_smode2_intm progressive or interlace mode
+ * @ffmd: &enum gs_smode2_ffmd FIELD or FRAME mode
+ * @dpms: &enum gs_smode2_dpms VESA display power management signaling (DPMS)
+ *      level
+ */
+struct gs_smode2 {
+	u64 intm : 1;
+	u64 ffmd : 1;
+	u64 dpms : 2;
+	u64 : 60;
+};
+
+/**
+ * struct gs_srfsh - DRAM refresh privileged Graphics Synthesizer register
+ * @rfsh: FIXME
+ */
+struct gs_srfsh {
+	u64 rfsh : 4;		/* FIXME: Number of bits? */
+	u64 : 60;
+};
+
+/**
+ * struct gs_synch1 - SYNCH1 privileged Graphics Synthesizer register
+ * @hfp: horizontal front porch
+ * @hbp: horizontal back porch
+ * @hseq: FIXME
+ * @hsvs: FIXME
+ * @hs: FIXME
+ */
+struct gs_synch1 {
+	u64 hfp : 11;
+	u64 hbp : 11;
+	u64 hseq : 10;
+	u64 hsvs : 11;
+	u64 hs : 21;		/* FIXME: Number of bits? */
+};
+
+/**
+ * struct gs_synch2 - SYNCH2 privileged Graphics Synthesizer register
+ * @hf: FIXME
+ * @hb: FIXME
+ */
+struct gs_synch2 {
+	u64 hf : 11;
+	u64 hb : 11;
+	u64 : 42;
+};
+
+/**
+ * struct gs_syncv - SYNCHV privileged Graphics Synthesizer register
+ * @vfp: vertical front porch, halflines with color burst after video data
+ * @vfpe: halflines without color burst after @vfp
+ * @vbp: vertical back porch, halflines with color burst after @vbpe
+ * @vbpe: halflines without color burst after @vbp
+ * @vdp: halflines with with video data
+ * @vs: halflines with VSYNC
+ */
+struct gs_syncv {
+	u64 vfp : 10;
+	u64 vfpe : 10;
+	u64 vbp : 12;
+	u64 vbpe : 10;
+	u64 vdp : 11;
+	u64 vs : 11;
+};
+
+/**
+ * struct gs_dispfb - DISPFB privileged Graphics Synthesizer register
+ * @fbp: base pointer address/2048
+ * @fbw: buffer width/64
+ * @psm: pixel storage format FIXME
+ * @dbx: upper left x position
+ * @dby: upper left y position
+ */
+struct gs_dispfb {
+	u64 fbp : 9;
+	u64 fbw : 6;
+	u64 psm : 5;
+	u64 : 12;
+	u64 dbx : 11;
+	u64 dby : 11;
+	u64 : 10;
+};
+
+/**
+ * struct gs_display - DISPLAY privileged Graphics Synthesizer register
+ * @dx: display x position (VCK)
+ * @dy: display y position (px)
+ * @magh: horizontal magnification
+ * @magv: vertical magnification
+ * @dw: display area width-1 (VCK)
+ * @dh: display area height-1 (px)
+ *
+ * @magh and @magv are factor-1, so 0 is 1x, 1 is 2x, 2 is 3x, etc.
+ */
+struct gs_display {
+	u64 dx : 12;
+	u64 dy : 11;
+	u64 magh : 4;
+	u64 magv : 5;
+	u64 dw : 12;
+	u64 dh : 11;
+	u64 : 9;
+};
+
+/**
+ * enum gs_extbuf_fbin - &&gs_extbuf.fbin selection of input source
+ * @gs_fbin_out1: FIXME
+ * @gs_fbin_out2: FIXME
+ */
+enum gs_extbuf_fbin {
+	gs_fbin_out1,
+	gs_fbin_out2
+};
+
+/*
+ * enum gs_extbuf_wffmd - &gs_extbuf.wffmd interlace mode
+ * @gs_wffmd_field: write to every other raster
+ * @gs_wffmd_frame: write to every raster
+ */
+enum gs_extbuf_wffmd {
+	gs_wffmd_field,
+	gs_wffmd_frame
+};
+
+/**
+ * enum gs_extbuf_emoda - &gs_extbuf.emoda processing of input alpha
+ * @gs_emoda_alpha: input alpha is written as it is
+ * @gs_emoda_y: FIXME
+ * @gs_emoda_yhalf: FIXME
+ * @gs_emoda_zero: FIXME
+ */
+enum gs_extbuf_emoda {
+	gs_emoda_alpha,
+	gs_emoda_y,
+	gs_emoda_yhalf,
+	gs_emoda_zero
+};
+
+/**
+ * enum gs_extbuf_emodc - &gs_extbuf.emodc processing of input color
+ * @gs_emodc_rgb: FIXME
+ * @gs_emodc_y: FIXME
+ * @gs_emodc_ycbcr: FIXME
+ * @gs_emodc_alpha: FIXME
+ */
+enum gs_extbuf_emodc {
+	gs_emodc_rgb,
+	gs_emodc_y,
+	gs_emodc_ycbcr,
+	gs_emodc_alpha
+};
+
+/**
+ * struct gs_extbuf - EXTBUF privileged Graphics Synthesizer register
+ * @exbp: buffer base pointer/64
+ * @exbw: width of buffer/64
+ * @fbin: @enum gs_extbuf_fbin selection of input source
+ * @wffmd: @enum gs_extbuf_wffmd interlace mode
+ * @emoda: @enum gs_extbuf_emoda processing of input alpha
+ * @emodc: @enum gs_extbuf_emodc processing of input color
+ * @wdx: upper left x position
+ * @wdy: upper left y position
+ */
+struct gs_extbuf {
+	u64 exbp : 14;
+	u64 exbw : 6;
+	u64 fbin : 2;
+	u64 wffmd : 1;
+	u64 emoda : 2;
+	u64 emodc : 2;
+	u64 : 5;
+	u64 wdx : 11;
+	u64 wdy : 11;
+	u64 : 10;
+};
+
+/**
+ * struct gs_extdata - EXTDATA privileged Graphics Synthesizer register
+ * @sx: upper left x position (VCK)
+ * @sy: upper left y position (VCK)
+ * @smph: horizontal sampling rate interval (VCK)
+ * @smpv: vertical sampling rate interval (VCK)
+ * @ww: rectangular area width-1
+ * @wh: rectangular area height-1
+ */
+struct gs_extdata {
+	u64 sx : 12;
+	u64 sy : 11;
+	u64 smph : 4;
+	u64 smpv : 2;
+	u64 : 3;
+	u64 ww : 12;
+	u64 wh : 11;
+	u64 : 9;
+};
+
+/**
+ * enum gs_extwrite_write - &gs_extwrite.write enable feedback write
+ * @gs_write_complete_current: FIXME
+ * @gs_write_start_next: FIXME
+ */
+enum gs_extwrite_write {
+	gs_write_complete_current,
+	gs_write_start_next
+};
+
+/**
+ * struct gs_extwrite - EXTWRITE privileged Graphics Synthesizer register
+ * @write: &enum gs_extwrite_write enable feedback write
+ */
+struct gs_extwrite {
+	u64 write : 1;
+	u64 : 63;
+};
+
+/**
+ * struct gs_bgcolor - BGCOLOR privileged Graphics Synthesizer register
+ * @r: red background color
+ * @g: green background color
+ * @b: blue background color
+ */
+struct gs_bgcolor {
+	u64 r : 8;
+	u64 g : 8;
+	u64 b : 8;
+	u64 : 40;
+};
+
+/**
+ * enum gs_csr_fifo - &gs_csr.fifo host interface FIFO status
+ * @gs_fifo_neither: neither empty nor almost full
+ * @gs_fifo_empty: FIXME
+ * @gs_fifo_almost_full: FIXME
+ */
+enum gs_csr_fifo {
+	gs_fifo_neither,
+	gs_fifo_empty,
+	gs_fifo_almost_full
+};
+
+/**
+ * enum gs_csr_field - &gs_csr.field field display currently FIXME
+ * @gs_field_even: FIXME
+ * @gs_field_odd: FIXME
+ */
+enum gs_csr_field {
+	gs_field_even,
+	gs_field_odd
+};
+
+/**
+ * struct gs_csr - CSR privileged Graphics Synthesizer register FIXME
+ * @signal: SIGNAL event control
+ * @finish: FINISH event control
+ * @hsint: HSync interrupt control
+ * @vsint: VSync interrupt control
+ * @edwint: rectangular area write termination interrupt control
+ * @zero: must be zero
+ * @flush: drawing suspend and FIFO clear (enabled during data write)
+ * @reset: Graphics Synthesizer reset (enabled during data write)
+ * @nfield: VSync sampled FIELD
+ * @field: &enum gs_csr_field field display currently
+ * @fifo: &enum gs_csr_fifo host interface FIFO status
+ * @rev: Graphics Synthesizer revision (hex)
+ * @id: Graphics Synthesizer id (hex)
+ */
+struct gs_csr {
+	u64 signal : 1;
+	u64 finish : 1;
+	u64 hsint : 1;
+	u64 vsint : 1;
+	u64 edwint : 1;
+	u64 zero : 2;
+	u64 : 1;
+	u64 flush : 1;
+	u64 reset : 1;
+	u64 : 2;
+	u64 nfield : 1;
+	u64 field : 1;
+	u64 fifo : 2;
+	u64 rev : 8;
+	u64 id : 8;
+	u64 : 32;
+};
+
+/**
+ * struct gs_imr - IMR privileged Graphics Synthesizer register FIXME
+ * @sigmsk: SIGNAL event interrupt mask
+ * @finishmsk: FINISH event interrupt mask
+ * @hsmsk: HSync interrupt mask
+ * @vsmsk: VSync interrupt mask
+ * @edwmsk: rectangular area write termination interrupt mask
+ * @ones: should be set to all ones (= 3)
+ */
+struct gs_imr {
+	u64 : 8;
+	u64 sigmsk : 1;
+	u64 finishmsk : 1;
+	u64 hsmsk : 1;
+	u64 vsmsk : 1;
+	u64 edwmsk : 1;
+	u64 ones : 2;
+	u64 : 49;
+};
+
+/**
+ * enum gs_busdir_dir - &gs_busdir.dir host to local direction, or vice versa
+ * @gs_dir_host_to_local: host to local bus transfer direction
+ * @gs_dir_local_to_host: local to host bus transfer direction
+ */
+enum gs_busdir_dir {
+	gs_dir_host_to_local,
+	gs_dir_local_to_host
+};
+
+/**
+ * struct gs_busdir - BUSDIR privileged Graphics Synthesizer register FIXME
+ * @dir: &enum gs_busdir_dir host to local direction, or vice versa
+ */
+struct gs_busdir {
+	u64 dir : 1;
+	u64 : 63;
+};
+
+/**
+ * struct gs_siglblid - SIGLBLID privileged Graphics Synthesizer register FIXME
+ * @sigid: id value set by SIGNAL register
+ * @lblid: id value set by LABEL register
+ */
+struct gs_siglblid {
+	u64 sigid : 32;
+	u64 lblid : 32;
+};
+
+#endif /* __ASM_MACH_PS2_GS_REGISTERS_H */
Signature · 1 line
2.21.0
#99 lore raw

PATCH 77/120 MIPS: PS2: GS: Write privileged registers

In reply to Fredrik Noring

All privileged Graphics Synthesizer register functions follow the same pattern. For example, the CSR register has the following functions:

    bool gs_valid_csr(void);
    void gs_writeq_csr(u64 value);

gs_valid_csr() indicates whether CSR is readable, which is always true, since CSR is read-write in hardware. The IMR register however is write- only in hardware, so its shadow register is only valid and readable once it is written at least once. Reading nonvalid registers is not permitted.

The following registers have functions: PMODE, SMODE1, SMODE2, SYNCH1, SYNCH2, SYNCV, SRFSH, DISPFB1, DISPLAY1, DISPFB2, DISPLAY2, EXTBUF, EXTDATA, EXTWRITE, BGCOLOR, CSR, IMR, BUSDIR and SIGLBLID.

---
 arch/mips/include/asm/mach-ps2/gs-registers.h |  48 +++++++
 drivers/ps2/Makefile                          |   2 +
 drivers/ps2/gs-registers.c                    | 121 ++++++++++++++++++
 3 files changed, 171 insertions(+)
 create mode 100644 drivers/ps2/gs-registers.c
arch/mips/include/asm/mach-ps2/gs-registers.h +48 -0
index ab59c751190f..291b503447e4 100644
@@ -545,4 +545,52 @@ struct gs_siglblid {
 	u64 lblid : 32;
 };
 
+#define GS_DECLARE_VALID_REG(reg)			\
+	bool gs_valid_##reg(void)
+
+#define GS_DECLARE_WQ_REG(reg)				\
+	void gs_writeq_##reg(u64 value)
+
+#define GS_DECLARE_RW_REG(reg)				\
+	GS_DECLARE_VALID_REG(reg);			\
+	GS_DECLARE_WQ_REG(reg)
+
+/**
+ * DOC:
+ *
+ * All privileged Graphics Synthesizer register functions follow the same
+ * pattern. For example, the CSR register has the following functions::
+ *
+ *	bool gs_valid_csr(void);
+ *	void gs_writeq_csr(u64 value);
+ *
+ * gs_valid_csr() indicates whether CSR is readable, which is always true,
+ * since CSR is read-write in hardware. The IMR register however is write-
+ * only in hardware, so its shadow register is only valid and readable once
+ * it is written at least once. Reading nonvalid registers is not permitted.
+ *
+ * The following registers have functions: PMODE, SMODE1, SMODE2, SRFSH,
+ * SYNCH1, SYNCH2, SYNCV, DISPFB1 , DISPLAY1, DISPFB2, DISPLAY2, EXTBUF,
+ * EXTDATA, EXTWRITE, BGCOLOR, CSR, IMR, BUSDIR and SIGLBLID.
+ */
+GS_DECLARE_RW_REG(pmode);
+GS_DECLARE_RW_REG(smode1);
+GS_DECLARE_RW_REG(smode2);
+GS_DECLARE_RW_REG(srfsh);
+GS_DECLARE_RW_REG(synch1);
+GS_DECLARE_RW_REG(synch2);
+GS_DECLARE_RW_REG(syncv);
+GS_DECLARE_RW_REG(dispfb1 );
+GS_DECLARE_RW_REG(display1);
+GS_DECLARE_RW_REG(dispfb2);
+GS_DECLARE_RW_REG(display2);
+GS_DECLARE_RW_REG(extbuf);
+GS_DECLARE_RW_REG(extdata);
+GS_DECLARE_RW_REG(extwrite);
+GS_DECLARE_RW_REG(bgcolor);
+GS_DECLARE_RW_REG(csr);
+GS_DECLARE_RW_REG(imr);
+GS_DECLARE_RW_REG(busdir);
+GS_DECLARE_RW_REG(siglblid);
+
 #endif /* __ASM_MACH_PS2_GS_REGISTERS_H */
drivers/ps2/Makefile +2 -0
index dd00eff52ef3..533ab86d6a58 100644
@@ -1,3 +1,5 @@
+obj-$(CONFIG_PS2_GS)		+= gs-registers.o
+
 obj-m				+= iop-heap.o
 obj-m				+= iop-irq.o
 obj-m				+= iop-memory.o
drivers/ps2/gs-registers.c new file +121 -0

121 lines not on this page · raw message

Signature · 1 line
2.21.0
#100 lore raw

PATCH 78/120 MIPS: PS2: GS: Read privileged registers

In reply to Fredrik Noring

All privileged Graphics Synthesizer register functions follow the same pattern. For example, the reading the CSR register:

    u64 gs_readq_csr(void);
---
 arch/mips/include/asm/mach-ps2/gs-registers.h |  5 ++++
 drivers/ps2/gs-registers.c                    | 24 +++++++++++++++++++
 2 files changed, 29 insertions(+)
arch/mips/include/asm/mach-ps2/gs-registers.h +5 -0
index 291b503447e4..315a25c102c9 100644
@@ -548,11 +548,15 @@ struct gs_siglblid {
 #define GS_DECLARE_VALID_REG(reg)			\
 	bool gs_valid_##reg(void)
 
+#define GS_DECLARE_RQ_REG(reg)				\
+	u64 gs_readq_##reg(void)
+
 #define GS_DECLARE_WQ_REG(reg)				\
 	void gs_writeq_##reg(u64 value)
 
 #define GS_DECLARE_RW_REG(reg)				\
 	GS_DECLARE_VALID_REG(reg);			\
+	GS_DECLARE_RQ_REG(reg);				\
 	GS_DECLARE_WQ_REG(reg)
 
 /**
@@ -562,6 +566,7 @@ struct gs_siglblid {
  * pattern. For example, the CSR register has the following functions::
  *
  *	bool gs_valid_csr(void);
+ *	u64 gs_readq_csr(void);
  *	void gs_writeq_csr(u64 value);
  *
  * gs_valid_csr() indicates whether CSR is readable, which is always true,
drivers/ps2/gs-registers.c +24 -0
index 781604b874b5..debaf0153fbe 100644
@@ -52,6 +52,14 @@ static struct {
 	}								\
 	EXPORT_SYMBOL_GPL(gs_valid_##reg)
 
+/* Read-write registers are not shadowed and trivially read. */
+#define GS_DEFINE_RQ_RW_REG(reg, addr)					\
+	u64 gs_readq_##reg(void)					\
+	{								\
+		return inq(addr);					\
+	}								\
+	EXPORT_SYMBOL_GPL(gs_readq_##reg)
+
 /* Read-write registers are not shadowed and trivially write. */
 #define GS_DEFINE_WQ_RW_REG(reg, addr)					\
 	void gs_writeq_##reg(u64 value)					\
@@ -73,6 +81,20 @@ static struct {
 	}								\
 	EXPORT_SYMBOL_GPL(gs_valid_##reg)
 
+/* Write-only registers are shadowed and reading requires a previous write. */
+#define GS_DEFINE_RQ_WO_REG(reg, addr)					\
+	u64 gs_readq_##reg(void)					\
+	{								\
+		unsigned long flags;					\
+		u64 value;						\
+		spin_lock_irqsave(&gs_registers.lock, flags);		\
+		WARN_ON_ONCE(!gs_registers.reg.valid);			\
+		value = gs_registers.reg.value;				\
+		spin_unlock_irqrestore(&gs_registers.lock, flags);	\
+		return value;						\
+	}								\
+	EXPORT_SYMBOL_GPL(gs_readq_##reg)
+
 /* Write-only registers are shadowed and reading requires a previous write. */
 #define GS_DEFINE_WQ_WO_REG(reg, addr)					\
 	void gs_writeq_##reg(u64 value)					\
@@ -89,11 +111,13 @@ static struct {
 /* Only CSR and SIGLBLID are read-write (RW) with hardware. */
 #define GS_DEFINE_RW_REG(reg, addr)					\
 	GS_DEFINE_VALID_RW_REG(reg, addr);				\
+	GS_DEFINE_RQ_RW_REG(reg, addr);					\
 	GS_DEFINE_WQ_RW_REG(reg, addr)
 
 /* The rest are write-only (WO) with reading emulated by shadow registers. */
 #define GS_DEFINE_WO_REG(reg, addr)					\
 	GS_DEFINE_VALID_WO_REG(reg, addr);				\
+	GS_DEFINE_RQ_WO_REG(reg, addr);					\
 	GS_DEFINE_WQ_WO_REG(reg, addr)
 
 GS_DEFINE_WO_REG(pmode,    GS_PMODE);
Signature · 1 line
2.21.0
#101 lore raw

PATCH 79/120 MIPS: PS2: GS: Define privileged register structures

In reply to Fredrik Noring

Read and write registers as structures, which simplifies notation. For example, the CSR register can be read and written with struct gs_csr:

struct gs_csr gs_read_csr(void);
void gs_write_csr(struct gs_csr value);

---
 arch/mips/include/asm/mach-ps2/gs-registers.h | 53 +++++++------
 drivers/ps2/gs-registers.c                    | 75 +++++++++++++------
 2 files changed, 83 insertions(+), 45 deletions(-)
arch/mips/include/asm/mach-ps2/gs-registers.h +32 -21
index 315a25c102c9..596e277a6940 100644
@@ -554,10 +554,18 @@ struct gs_siglblid {
 #define GS_DECLARE_WQ_REG(reg)				\
 	void gs_writeq_##reg(u64 value)
 
-#define GS_DECLARE_RW_REG(reg)				\
+#define GS_DECLARE_RS_REG(reg, str)			\
+	struct gs_##str gs_read_##reg(void)
+
+#define GS_DECLARE_WS_REG(reg, str)			\
+	void gs_write_##reg(struct gs_##str value)
+
+#define GS_DECLARE_RW_REG(reg, str)			\
 	GS_DECLARE_VALID_REG(reg);			\
 	GS_DECLARE_RQ_REG(reg);				\
-	GS_DECLARE_WQ_REG(reg)
+	GS_DECLARE_WQ_REG(reg);				\
+	GS_DECLARE_RS_REG(reg, str);			\
+	GS_DECLARE_WS_REG(reg, str)
 
 /**
  * DOC:
@@ -569,6 +577,9 @@ struct gs_siglblid {
  *	u64 gs_readq_csr(void);
  *	void gs_writeq_csr(u64 value);
  *
+ *	struct gs_csr gs_read_csr(void);
+ *	void gs_write_csr(struct gs_csr value);
+ *
  * gs_valid_csr() indicates whether CSR is readable, which is always true,
  * since CSR is read-write in hardware. The IMR register however is write-
  * only in hardware, so its shadow register is only valid and readable once
@@ -578,24 +589,24 @@ struct gs_siglblid {
  * SYNCH1, SYNCH2, SYNCV, DISPFB1 , DISPLAY1, DISPFB2, DISPLAY2, EXTBUF,
  * EXTDATA, EXTWRITE, BGCOLOR, CSR, IMR, BUSDIR and SIGLBLID.
  */
-GS_DECLARE_RW_REG(pmode);
-GS_DECLARE_RW_REG(smode1);
-GS_DECLARE_RW_REG(smode2);
-GS_DECLARE_RW_REG(srfsh);
-GS_DECLARE_RW_REG(synch1);
-GS_DECLARE_RW_REG(synch2);
-GS_DECLARE_RW_REG(syncv);
-GS_DECLARE_RW_REG(dispfb1 );
-GS_DECLARE_RW_REG(display1);
-GS_DECLARE_RW_REG(dispfb2);
-GS_DECLARE_RW_REG(display2);
-GS_DECLARE_RW_REG(extbuf);
-GS_DECLARE_RW_REG(extdata);
-GS_DECLARE_RW_REG(extwrite);
-GS_DECLARE_RW_REG(bgcolor);
-GS_DECLARE_RW_REG(csr);
-GS_DECLARE_RW_REG(imr);
-GS_DECLARE_RW_REG(busdir);
-GS_DECLARE_RW_REG(siglblid);
+GS_DECLARE_RW_REG(pmode,    pmode);
+GS_DECLARE_RW_REG(smode1,   smode1);
+GS_DECLARE_RW_REG(smode2,   smode2);
+GS_DECLARE_RW_REG(srfsh,    srfsh);
+GS_DECLARE_RW_REG(synch1,   synch1);
+GS_DECLARE_RW_REG(synch2,   synch2);
+GS_DECLARE_RW_REG(syncv,    syncv);
+GS_DECLARE_RW_REG(dispfb1 , dispfb);
+GS_DECLARE_RW_REG(display1, display);
+GS_DECLARE_RW_REG(dispfb2,  dispfb);
+GS_DECLARE_RW_REG(display2, display);
+GS_DECLARE_RW_REG(extbuf,   extbuf);
+GS_DECLARE_RW_REG(extdata,  extdata);
+GS_DECLARE_RW_REG(extwrite, extwrite);
+GS_DECLARE_RW_REG(bgcolor,  bgcolor);
+GS_DECLARE_RW_REG(csr,      csr);
+GS_DECLARE_RW_REG(imr,      imr);
+GS_DECLARE_RW_REG(busdir,   busdir);
+GS_DECLARE_RW_REG(siglblid, siglblid);
 
 #endif /* __ASM_MACH_PS2_GS_REGISTERS_H */
drivers/ps2/gs-registers.c +51 -24

88 lines not on this page · raw message

Signature · 1 line
2.21.0
#102 lore raw

PATCH 80/120 MIPS: PS2: GS: Define gs_xorq_imr()

In reply to Fredrik Noring

---
Perhaps this function could have a better name?

---
 arch/mips/include/asm/mach-ps2/gs-registers.h |  8 ++++++++
 drivers/ps2/gs-registers.c                    | 17 +++++++++++++++++
 2 files changed, 25 insertions(+)
arch/mips/include/asm/mach-ps2/gs-registers.h +8 -0
index 596e277a6940..badcb3cce5c2 100644
@@ -609,4 +609,12 @@ GS_DECLARE_RW_REG(imr,      imr);
 GS_DECLARE_RW_REG(busdir,   busdir);
 GS_DECLARE_RW_REG(siglblid, siglblid);
 
+/**
+ * gs_xorq_imr - exclusive or (XOR) value with the IMR register
+ * @value: value to XOR with the IMR register
+ *
+ * Return: the resulting IMR value
+ */
+u64 gs_xorq_imr(u64 value);
+
 #endif /* __ASM_MACH_PS2_GS_REGISTERS_H */
drivers/ps2/gs-registers.c +17 -0
index 1bd1d072f4cf..8d2bdd26438d 100644
@@ -167,6 +167,23 @@ GS_DEFINE_WO_REG(imr,      imr,      GS_IMR);
 GS_DEFINE_WO_REG(busdir,   busdir,   GS_BUSDIR);
 GS_DEFINE_RW_REG(siglblid, siglblid, GS_SIGLBLID);	/* Read-write */
 
+u64 gs_xorq_imr(u64 value)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&gs_registers.lock, flags);
+
+	WARN_ON_ONCE(!gs_registers.imr.valid);
+	value = value ^ gs_registers.imr.value;
+	gs_registers.imr.value = value;
+	outq(value, GS_IMR);
+
+	spin_unlock_irqrestore(&gs_registers.lock, flags);
+
+	return value;
+}
+EXPORT_SYMBOL_GPL(gs_xorq_imr);
+
 MODULE_DESCRIPTION("PlayStation 2 privileged Graphics Synthesizer registers");
 MODULE_AUTHOR("Fredrik Noring");
 MODULE_LICENSE("GPL");
Signature · 1 line
2.21.0
#103 lore raw

PATCH 81/120 MIPS: PS2: GS: Privileged register write macros with named fields

In reply to Fredrik Noring

The Graphics Synthesizer write macros simplifies register writing, allowing named fields in statements such as GS_WRITE_CSR( .flush = 1 ).

The following registers have macros: PMODE, SMODE1, SMODE2, SRFSH, SYNCH1, SYNCH2, SYNCV, DISPLAY1, DISPLAY2, DISPFB1, DISPFB2, CSR and BUSDIR.

---
 arch/mips/include/asm/mach-ps2/gs-registers.h | 26 +++++++++++++++++++
 1 file changed, 26 insertions(+)
arch/mips/include/asm/mach-ps2/gs-registers.h +26 -0
index badcb3cce5c2..95243a35e26d 100644
@@ -609,6 +609,32 @@ GS_DECLARE_RW_REG(imr,      imr);
 GS_DECLARE_RW_REG(busdir,   busdir);
 GS_DECLARE_RW_REG(siglblid, siglblid);
 
+#define GS_WS_REG(reg, str, ...)			\
+	gs_write_##reg((struct gs_##str) { __VA_ARGS__ })
+
+/**
+ * DOC:
+ *
+ * The Graphics Synthesizer write macros simplifies register writing,
+ * allowing named fields in statements such as GS_WRITE_CSR( .flush = 1 ).
+ *
+ * The following registers have macros: PMODE, SMODE1, SMODE2, SRFSH, SYNCH1,
+ * SYNCH2, SYNCV, DISPLAY1, DISPLAY2, DISPFB1, DISPFB2, CSR and BUSDIR.
+ */
+#define GS_WRITE_PMODE(...)    GS_WS_REG(pmode,    pmode,   __VA_ARGS__)
+#define GS_WRITE_SMODE1(...)   GS_WS_REG(smode1,   smode1,  __VA_ARGS__)
+#define GS_WRITE_SMODE2(...)   GS_WS_REG(smode2,   smode2,  __VA_ARGS__)
+#define GS_WRITE_SRFSH(...)    GS_WS_REG(srfsh,    srfsh,   __VA_ARGS__)
+#define GS_WRITE_SYNCH1(...)   GS_WS_REG(synch1,   synch1,  __VA_ARGS__)
+#define GS_WRITE_SYNCH2(...)   GS_WS_REG(synch2,   synch2,  __VA_ARGS__)
+#define GS_WRITE_SYNCV(...)    GS_WS_REG(syncv,    syncv,   __VA_ARGS__)
+#define GS_WRITE_DISPLAY1(...) GS_WS_REG(display1, display, __VA_ARGS__)
+#define GS_WRITE_DISPLAY2(...) GS_WS_REG(display2, display, __VA_ARGS__)
+#define GS_WRITE_DISPFB1(...)  GS_WS_REG(dispfb1,  dispfb,  __VA_ARGS__)
+#define GS_WRITE_DISPFB2(...)  GS_WS_REG(dispfb2,  dispfb,  __VA_ARGS__)
+#define GS_WRITE_CSR(...)      GS_WS_REG(csr,      csr,     __VA_ARGS__)
+#define GS_WRITE_BUSDIR(...)   GS_WS_REG(busdir,   busdir,  __VA_ARGS__)
+
 /**
  * gs_xorq_imr - exclusive or (XOR) value with the IMR register
  * @value: value to XOR with the IMR register
Signature · 1 line
2.21.0
#104 lore raw

PATCH 82/120 MIPS: PS2: GS: IRQ support

In reply to Fredrik Noring
---
 arch/mips/include/asm/mach-ps2/irq.h |  1 +
 drivers/ps2/Makefile                 |  1 +
 drivers/ps2/gs-irq.c                 | 93 ++++++++++++++++++++++++++++
 3 files changed, 95 insertions(+)
 create mode 100644 drivers/ps2/gs-irq.c
arch/mips/include/asm/mach-ps2/irq.h +1 -0
index 57b3e539ad92..132ad329de6b 100644
@@ -120,6 +120,7 @@
 
 int __init intc_irq_init(void);
 int __init dmac_irq_init(void);
+int gs_irq_init(void);
 
 /*
  * IRQs asserted by the I/O processor (IOP) via the sub-system interface (SIF).
drivers/ps2/Makefile +1 -0
index 533ab86d6a58..7f108d4ee451 100644
@@ -1,3 +1,4 @@
+obj-$(CONFIG_PS2_GS)		+= gs-irq.o
 obj-$(CONFIG_PS2_GS)		+= gs-registers.o
 
 obj-m				+= iop-heap.o
drivers/ps2/gs-irq.c new file +93 -0

93 lines not on this page · raw message

Signature · 1 line
2.21.0
#105 lore raw

PATCH 83/120 MIPS: PS2: GS: Define Graphics Synthesizer primitive structures

In reply to Fredrik Noring

The Graphics Synthesizer has 4 MiB of local frame buffer memory that is not directly accessible from the main bus. All drawing operations must be performed with serialized drawing primitives via the Graphics Synthesizer interface (GIF), either by DMA or a vector processor[1].

The GS frame buffer is subdivided into rectangular pages, from left to right, top to bottom. Pages are further subdivided into blocks, with different arrangements for the PSMCT16 and PSMCT32 pixel storage modes. Blocks are further subdivided into columns that are finally subdivided into pixels.

The general purpose GS registers are used to set vertex information, drawing environment and transmission between buffers for the drawing primitives. All of them are write-only registers.

The following registers are available. Registers with "_1" and "_2" suffixes are designed to operate in one of the two drawing contexts.

Register | Description
-----------+--------------------------------------------------------

PRIM       | Drawing primitive setting
RGBAQ      | Vertex colour setting
ST         | Vertex texture coordinate setting (texture coordinates)
UV         | Vertex texture coordinate setting (texel coordinates)
XYZF2      | Vertex coordinate value setting
XYZ2       | Vertex coordinate value setting
TEX0_1     | Texture information setting
TEX0_2     | Texture information setting
CLAMP_1    | Texture wrap mode
CLAMP_2    | Texture wrap mode
FOG        | Vertex fog value setting
XYZF3      | Vertex coordinate value setting (without drawing kick)
XYZ3       | Vertex coordinate value setting (without drawing kick)
TEX1_1     | Texture information setting
TEX1_2     | Texture information setting
TEX2_1     | Texture information setting
TEX2_2     | Texture information setting
XYOFFSET_1 | Offset value setting
XYOFFSET_2 | Offset value setting
PRMODECONT | Specification of primitive attribute setting method
PRMODE     | Drawing primitive attribute setting
TEXCLUT    | Colour lookup table position setting
SCANMSK    | Raster address mask setting
MIPTBP1_1  | MIPMAP information setting (Level 1 – 3)
MIPTBP1_2  | MIPMAP information setting (Level 1 – 3)
MIPTBP2_1  | MIPMAP information setting (Level 4 – 6)
MIPTBP2_2  | MIPMAP information setting (Level 4 – 6)
TEXA       | Texture alpha value setting
FOGCOL     | Distant fog colour setting
TEXFLUSH   | Texture page buffer disabling
SCISSOR_1  | Scissoring area setting
SCISSOR_2  | Scissoring area setting
ALPHA_1    | Alpha blending setting
ALPHA_2    | Alpha blending setting
DIMX       | Dither matrix setting
DTHE       | Dither control
COLCLAMP   | Colour clamp control
TEST_1     | Pixel test control
TEST_2     | Pixel test control
PABE       | Alpha blending control in pixel units
FBA_1      | Alpha correction value
FBA_2      | Alpha correction value
FRAME_1    | Frame buffer setting
FRAME_2    | Frame buffer setting
ZBUF_1     | Z buffer setting
ZBUF_2     | Z buffer setting
BITBLTBUF  | Setting for transmission between buffers
TRXPOS     | Specification for transmission area in buffers
TRXREG     | Specification for transmission area in buffers
TRXDIR     | Activation of transmission between buffers
HWREG      | Data port for transmission between buffers
SIGNAL     | SIGNAL event occurrence request
FINISH     | FINISH event occurrence request
LABEL      | LABEL event occurrence request

-----------+--------------------------------------------------------

References:

[1] "GS User's Manual", version 6.0, Sony Computer Entertainment Inc.,

    pp. 158, 161-175.
---
 arch/mips/include/uapi/asm/gs.h | 723 ++++++++++++++++++++++++++++++++
 1 file changed, 723 insertions(+)
 create mode 100644 arch/mips/include/uapi/asm/gs.h
arch/mips/include/uapi/asm/gs.h new file +723 -0
new file mode 100644
index 000000000000..074bb62a4bd0
@@ -0,0 +1,723 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 Graphics Synthesizer (GS)
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#ifndef _UAPI_MIPS_ASM_GS_H
+#define _UAPI_MIPS_ASM_GS_H
+
+#include <asm/bitfield.h>
+
+/**
+ * DOC: The PlayStation 2 Graphics Synthesizer
+ *
+ * The Graphics Synthesizer frame buffer is subdivided into rectangular pages,
+ * from left to right, top to bottom. Pages are further subdivided into blocks,
+ * with different arrangements for the PSMCT16 and PSMCT32 pixel storage modes.
+ * Blocks are further subdivided into columns that are finally subdivided into
+ * pixels.
+ *
+ * The frame buffer width (FBW) is defined as the pixel width divided by 64,
+ * where 64 is the width of a page for PSMCT16 and PSMCT32.
+ *
+ * The texture base pointer (TBP), color base pointer (CBP), source base
+ * pointer (SBP) and destination base pointer (DBP) are all addressed by block.
+ *
+ * As an example, consider the frame buffer resolution 1920x1080@16 that uses
+ * 1920*1080*2 = 4147200 bytes of the 4 MiB = 4194304 bytes available memory.
+ * There are 4194304-4147200 = 47104 bytes of free memory. The FBW is 1920/64
+ * = 30, which means it is 30 pages wide. However, 1080/GS_PSM_CT32_PAGE_HEIGHT
+ * = 1080/64 = 16.875, which means that the last row of blocks are unused in
+ * the last page row. These make up 30*4 = 120 blocks of the 47104/256 = 184
+ * blocks available. To make effective use of free memory for this resolution
+ * in particular, it is essential to compute accurate block addresses.
+ */
+
+#define GS_COLUMNS_PER_BLOCK	  4
+#define GS_BLOCKS_PER_PAGE	 32
+
+#define GS_PAGE_COUNT		512
+#define GS_BLOCK_COUNT	(GS_PAGE_COUNT * GS_BLOCKS_PER_PAGE)
+#define GS_COLUMN_COUNT	(GS_BLOCK_COUNT * GS_COLUMNS_PER_PAGE)
+
+#define GS_COLUMN_SIZE		 64
+#define GS_BLOCK_SIZE	(GS_COLUMNS_PER_BLOCK * GS_COLUMN_SIZE)
+#define GS_PAGE_SIZE	(GS_BLOCKS_PER_PAGE * GS_BLOCK_SIZE)
+#define GS_MEMORY_SIZE	(GS_PAGE_COUNT * GS_PAGE_SIZE)	/* 4 MiB of memory */
+
+#define GS_FB_PAGE_WIDTH	  64
+#define GS_FB_BASE_DIVISOR	2048	/* FIXME: GS_FB_PAGE_WORDS */
+
+/* All pixel storage formats have 1 block column and 4 block rows. */
+#define GS_BLOCK_COLS			 1
+#define GS_BLOCK_ROWS			 4
+
+/* 4 bit (16 color) texture pixel storage format. */
+#define GS_PSMT4_PAGE_COLS		 4
+#define GS_PSMT4_PAGE_ROWS		 8
+#define GS_PSMT4_COLUMN_WIDTH		32
+#define GS_PSMT4_COLUMN_HEIGHT		 4
+#define GS_PSMT4_BLOCK_WIDTH	 (GS_PSMT4_COLUMN_WIDTH * GS_BLOCK_COLS)
+#define GS_PSMT4_BLOCK_HEIGHT	 (GS_PSMT4_COLUMN_HEIGHT * GS_BLOCK_ROWS)
+#define GS_PSMT4_PAGE_WIDTH	 (GS_PSMT4_BLOCK_WIDTH * GS_PSMT4_PAGE_COLS)
+#define GS_PSMT4_PAGE_HEIGHT	 (GS_PSMT4_BLOCK_HEIGHT * GS_PSMT4_PAGE_ROWS)
+
+/* 8 bit (256 color) texture pixel storage format. */
+#define GS_PSMT8_PAGE_COLS		 8
+#define GS_PSMT8_PAGE_ROWS		 4
+#define GS_PSMT8_COLUMN_WIDTH		16
+#define GS_PSMT8_COLUMN_HEIGHT		 4
+#define GS_PSMT8_BLOCK_WIDTH	 (GS_PSMT8_COLUMN_WIDTH * GS_BLOCK_COLS)
+#define GS_PSMT8_BLOCK_HEIGHT	 (GS_PSMT8_COLUMN_HEIGHT * GS_BLOCK_ROWS)
+#define GS_PSMT8_PAGE_WIDTH	 (GS_PSMT8_BLOCK_WIDTH * GS_PSMT8_PAGE_COLS)
+#define GS_PSMT8_PAGE_HEIGHT	 (GS_PSMT8_BLOCK_HEIGHT * GS_PSMT8_PAGE_ROWS)
+
+/* 16 bit (true color) frame buffer and texture pixel storage format. */
+#define GS_PSM_CT16_PAGE_COLS		 4
+#define GS_PSM_CT16_PAGE_ROWS		 8
+#define GS_PSM_CT16_COLUMN_WIDTH	 8
+#define GS_PSM_CT16_COLUMN_HEIGHT	 2
+#define GS_PSM_CT16_BLOCK_WIDTH	 (GS_PSM_CT16_COLUMN_WIDTH * GS_BLOCK_COLS)
+#define GS_PSM_CT16_BLOCK_HEIGHT (GS_PSM_CT16_COLUMN_HEIGHT * GS_BLOCK_ROWS)
+#define GS_PSM_CT16_PAGE_WIDTH	 (GS_PSM_CT16_BLOCK_WIDTH * GS_PSM_CT16_PAGE_COLS)
+#define GS_PSM_CT16_PAGE_HEIGHT	 (GS_PSM_CT16_BLOCK_HEIGHT * GS_PSM_CT16_PAGE_ROWS)
+
+/* 32 bit (true color) frame buffer and texture pixel storage format. */
+#define GS_PSM_CT32_PAGE_COLS		 8
+#define GS_PSM_CT32_PAGE_ROWS		 4
+#define GS_PSM_CT32_COLUMN_WIDTH	16
+#define GS_PSM_CT32_COLUMN_HEIGHT	 2
+#define GS_PSM_CT32_BLOCK_WIDTH	 (GS_PSM_CT32_COLUMN_WIDTH * GS_BLOCK_COLS)
+#define GS_PSM_CT32_BLOCK_HEIGHT (GS_PSM_CT32_COLUMN_HEIGHT * GS_BLOCK_ROWS)
+#define GS_PSM_CT32_PAGE_WIDTH	 (GS_PSM_CT32_BLOCK_WIDTH * GS_PSM_CT32_PAGE_COLS)
+#define GS_PSM_CT32_PAGE_HEIGHT	 (GS_PSM_CT32_BLOCK_HEIGHT * GS_PSM_CT32_PAGE_ROWS)
+
+#define GS_ALPHA_ONE 	0x80	/* Alpha 0x80 = 1.0 */
+
+/**
+ * struct gs_rgba16 - 16-bit red, green, blue colour and alpha transparency
+ * @a: 1-bit alpha transparency
+ * @b: 5-bit blue colour component
+ * @g: 5-bit green colour component
+ * @r: 5-bit red colour component
+ *
+ * Alpha is treated as 0 when @a is 0, and as %GS_ALPHA_ONE when @a is 1.
+ */
+struct gs_rgba16 {
+	__BITFIELD_FIELD(__u16 a : 1,
+	__BITFIELD_FIELD(__u16 b : 5,
+	__BITFIELD_FIELD(__u16 g : 5,
+	__BITFIELD_FIELD(__u16 r : 5,
+	;))))
+};
+
+/**
+ * struct gs_rgba32 - 32-bit red, green, blue colour and alpha transparency
+ * @a: 8-bit alpha transparency, with %GS_ALPHA_ONE representing 1.0
+ * @b: 8-bit blue colour component
+ * @g: 8-bit green colour component
+ * @r: 8-bit red colour component
+ */
+struct gs_rgba32 {
+	__BITFIELD_FIELD(__u32 a : 8,
+	__BITFIELD_FIELD(__u32 b : 8,
+	__BITFIELD_FIELD(__u32 g : 8,
+	__BITFIELD_FIELD(__u32 r : 8,
+	;))))
+};
+
+enum gs_register_address {
+	gs_addr_prim       = 0x00, gs_addr_rgbaq      = 0x01,
+	gs_addr_st         = 0x02, gs_addr_uv         = 0x03,
+	gs_addr_xyzf2      = 0x04, gs_addr_xyz2       = 0x05,
+	gs_addr_tex0_1     = 0x06, gs_addr_tex0_2     = 0x07,
+	gs_addr_clamp_1    = 0x08, gs_addr_clamp_2    = 0x09,
+	gs_addr_fog        = 0x0a, gs_addr_xyzf3      = 0x0c,
+	gs_addr_xyz3       = 0x0d, gs_addr_tex1_1     = 0x14,
+	gs_addr_tex1_2     = 0x15, gs_addr_tex2_1     = 0x16,
+	gs_addr_tex2_2     = 0x17, gs_addr_xyoffset_1 = 0x18,
+	gs_addr_xyoffset_2 = 0x19, gs_addr_prmodecont = 0x1a,
+	gs_addr_prmode     = 0x1b, gs_addr_texclut    = 0x1c,
+	gs_addr_scanmsk    = 0x22, gs_addr_miptbp1_1  = 0x34,
+	gs_addr_miptbp1_2  = 0x35, gs_addr_miptbp2_1  = 0x36,
+	gs_addr_miptbp2_2  = 0x37, gs_addr_texa       = 0x3b,
+	gs_addr_fogcol     = 0x3d, gs_addr_texflush   = 0x3f,
+	gs_addr_scissor_1  = 0x40, gs_addr_scissor_2  = 0x41,
+	gs_addr_alpha_1    = 0x42, gs_addr_alpha_2    = 0x43,
+	gs_addr_dimx       = 0x44, gs_addr_dthe       = 0x45,
+	gs_addr_colclamp   = 0x46, gs_addr_test_1     = 0x47,
+	gs_addr_test_2     = 0x48, gs_addr_pabe       = 0x49,
+	gs_addr_fba_1      = 0x4a, gs_addr_fba_2      = 0x4b,
+	gs_addr_frame_1    = 0x4c, gs_addr_frame_2    = 0x4d,
+	gs_addr_zbuf_1     = 0x4e, gs_addr_zbuf_2     = 0x4f,
+	gs_addr_bitbltbuf  = 0x50, gs_addr_trxpos     = 0x51,
+	gs_addr_trxreg     = 0x52, gs_addr_trxdir     = 0x53,
+	gs_addr_hwreg      = 0x54, gs_addr_signal     = 0x60,
+	gs_addr_finish     = 0x61, gs_addr_label      = 0x62,
+	gs_addr_nop        = 0x7f
+};
+
+enum gs_prim_fix { gs_fragment_unfixed, gs_fragment_fixed };
+enum gs_prim_ctxt { gs_context_1, gs_context_2 };
+enum gs_prim_fst { gs_texturing_stq, gs_texturing_uv };
+enum gs_prim_aa1 { gs_pass_antialiasing_off, gs_pass_antialiasing_on };
+enum gs_prim_abe { gs_blendning_off, gs_blendning_on };
+enum gs_prim_fge { gs_fogging_off, gs_fogging_on };
+enum gs_prim_tme { gs_texturing_off, gs_texturing_on };
+enum gs_prim_iip { gs_flat_shading, gs_gouraud_shading };
+enum gs_prim_type {
+	gs_point, gs_line, gs_linestrip, gs_triangle,
+	gs_trianglestrip, gs_trianglefan, gs_sprite
+};
+
+/**
+ * struct gs_prim - drawing primitive
+ * @fix: fragment value control
+ * @ctxt: context
+ * @fst: texture coordinate method
+ * @aa1: pass antialiasing
+ * @abe: alpha blendning
+ * @fge: fogging
+ * @tme: texture mapping
+ * @iip: shading method
+ * @prim: type of drawing primitive
+ *
+ * The IIP/TME/FGE/ABE/AA1/FST/CTXT/FIX fields are only enabled when AC is 1
+ * in the PRMODECONT register.
+ */
+struct gs_prim {
+	__BITFIELD_FIELD(__u64 : 53,
+	__BITFIELD_FIELD(__u64 fix : 1,
+	__BITFIELD_FIELD(__u64 ctxt : 1,
+	__BITFIELD_FIELD(__u64 fst : 1,
+	__BITFIELD_FIELD(__u64 aa1 : 1,
+	__BITFIELD_FIELD(__u64 abe : 1,
+	__BITFIELD_FIELD(__u64 fge : 1,
+	__BITFIELD_FIELD(__u64 tme : 1,
+	__BITFIELD_FIELD(__u64 iip : 1,
+	__BITFIELD_FIELD(__u64 prim : 3,
+	;))))))))))
+};
+
+/**
+ * struct gs_prmode - attributes of drawing primitives
+ * @fix: fragment value control
+ * @ctxt: context
+ * @fst: texture coordinate method
+ * @aa1: pass antialiasing
+ * @abe: alpha blendning
+ * @fge: fogging
+ * @tme: texture mapping
+ * @iip: shading method
+ *
+ * The IIP/TME/FGE/ABE/AA1/FST/CTXT/FIX fields are only enabled when AC is 0
+ * in the PRMODECONT register.
+ */
+struct gs_prmode {
+	__BITFIELD_FIELD(__u64 : 53,
+	__BITFIELD_FIELD(__u64 fix : 1,
+	__BITFIELD_FIELD(__u64 ctxt : 1,
+	__BITFIELD_FIELD(__u64 fst : 1,
+	__BITFIELD_FIELD(__u64 aa1 : 1,
+	__BITFIELD_FIELD(__u64 abe : 1,
+	__BITFIELD_FIELD(__u64 fge : 1,
+	__BITFIELD_FIELD(__u64 tme : 1,
+	__BITFIELD_FIELD(__u64 iip : 1,
+	__BITFIELD_FIELD(__u64 : 3,
+	;))))))))))
+};
+
+/**
+ * struct gs_prmodecont - primitive attribute setting method
+ * @ac: enable PRMODE (= 0) or PRIM (= 1)
+ */
+struct gs_prmodecont {
+	__BITFIELD_FIELD(__u64 : 63,
+	__BITFIELD_FIELD(__u64 ac : 1,
+	;))
+};
+
+enum gs_psm {
+	gs_psm_ct32  = 0x00, gs_psm_ct24  = 0x01, gs_psm_ct16 = 0x02,
+	gs_psm_ct16s = 0x0a, gs_psm_gpu24 = 0x12, gs_psm_t8   = 0x13,
+	gs_psm_t4    = 0x14, gs_psm_t8h   = 0x1b, gs_psm_t4hl = 0x24,
+	gs_psm_t4hh  = 0x2c, gs_psm_z32   = 0x30, gs_psm_z24  = 0x31,
+	gs_psm_z16   = 0x32, gs_psm_z16s  = 0x3a
+};
+
+/**
+ * struct gs_bitbltbuf - transmission between buffers
+ * @dpsm: destination pixel format
+ * @dbw: destination width/64
+ * @dbp: destination base word/64
+ * @spsm: source pixel format
+ * @sbw: source width/64
+ * @sbp: source base word/64
+ *
+ * Host -> local: Only destination fields are used.
+ *
+ * Local -> host: Only source fields are used. The pixel formats PSMT4,
+ * PSMT4HL and PSMT4HH cannot be used.
+ *
+ * Local -> local: Both source and destination fields are used. The bits
+ * per pixel for source and destination must be equal.
+ *
+ * The rectangular area wraps around when exceeding the buffer width.
+ *
+ * Limitations on TRXPOS start x coordinate transmissions (not applicable
+ * for local -> host):
+ *
+ *     Multiple of 2: PSMT8, PSMT8H
+ *     Multiple of 4: PSMT4, PSMT4HL, PSMT4HH
+ *
+ * Limitations on TRXREG width (not applicable for local -> host):
+ *
+ *     Multiple of 2: PSMCT32, PSMZ32
+ *     Multiple of 4: PSMCT16, PSMCT16S, PSMZ16, PSMZ16S
+ *     Multiple of 8: PSMCT24, PSMZ24, PSMT8, PSMT8H, PSMT4, PSMT4HL, PSMT4HH
+ */
+struct gs_bitbltbuf {
+	__BITFIELD_FIELD(__u64 : 2,
+	__BITFIELD_FIELD(__u64 dpsm : 6,
+	__BITFIELD_FIELD(__u64 : 2,
+	__BITFIELD_FIELD(__u64 dbw : 6,
+	__BITFIELD_FIELD(__u64 : 2,
+	__BITFIELD_FIELD(__u64 dbp : 14,
+	__BITFIELD_FIELD(__u64 : 2,
+	__BITFIELD_FIELD(__u64 spsm : 6,
+	__BITFIELD_FIELD(__u64 : 2,
+	__BITFIELD_FIELD(__u64 sbw : 6,
+	__BITFIELD_FIELD(__u64 : 2,
+	__BITFIELD_FIELD(__u64 sbp : 14,
+	;))))))))))))
+};
+
+enum gs_clamp_mode {
+	gs_clamp_repeat, gs_clamp_clamp,
+	gs_clamp_region_clamp, gs_clamp_region_repeat
+};
+
+/**
+ * struct gs_clamp - texture wrap mode
+ * @maxv: upper v clamp parameter
+ * @minv: lower v clamp parameter
+ * @maxu: upper u clamp parameter
+ * @minu: lower u clamp parameter
+ * @wmt: vertical wrap mode
+ * @wms: horizontal wrap mode
+ */
+struct gs_clamp {
+	__BITFIELD_FIELD(__u64 : 20,
+	__BITFIELD_FIELD(__u64 maxv : 10,
+	__BITFIELD_FIELD(__u64 minv : 10,
+	__BITFIELD_FIELD(__u64 maxu : 10,
+	__BITFIELD_FIELD(__u64 minu : 10,
+	__BITFIELD_FIELD(__u64 wmt : 2,
+	__BITFIELD_FIELD(__u64 wms : 2,
+	;)))))))
+};
+
+/**
+ * struct gs_frame_12 - frame buffer
+ * @fbmsk: frame buffer drawing mask
+ * @psm: frame buffer pixel format
+ * @fbw: frame buffer width/64
+ * @fbp: frame buffer base/2048
+ */
+struct gs_frame_12 {
+	__BITFIELD_FIELD(__u64 fbmsk : 32,
+	__BITFIELD_FIELD(__u64 : 2,
+	__BITFIELD_FIELD(__u64 psm : 6,
+	__BITFIELD_FIELD(__u64 : 2,
+	__BITFIELD_FIELD(__u64 fbw : 6,
+	__BITFIELD_FIELD(__u64 : 7,
+	__BITFIELD_FIELD(__u64 fbp : 9,
+	;)))))))
+};
+
+/**
+ * enum gs_scanmsk - raster address mask
+ * @gs_scanmsk_normal: normal drawing (not masked)
+ * @gs_scanmsk_even: drawing of pixel with even y coordinate is prohibited
+ * @gs_scanmsk_odd: drawing of pixel with odd y coordinate is prohibited
+ */
+enum gs_scanmsk {
+	gs_scanmsk_normal,
+	/* Reserved */
+	gs_scanmsk_even = 2,
+	gs_scanmsk_odd
+};
+
+/**
+ * struct gs_scanmsk_12 - raster address mask
+ * @msk: raster address mask
+ */
+struct gs_scanmsk_12 {
+	__BITFIELD_FIELD(__u64 : 62,
+	__BITFIELD_FIELD(__u64 msk : 2,
+	;))
+};
+
+/**
+ * struct gs_scissor_12 - scissoring area
+ * @scay1: lower right y
+ * @scay0: upper left y
+ * @scax1: lower right x
+ * @scax0: upper left x
+ *
+ * All SCISSOR coordinates are in the window coordinate system.
+ */
+struct gs_scissor_12 {
+	__BITFIELD_FIELD(__u64 : 5,
+	__BITFIELD_FIELD(__u64 scay1 : 11,
+	__BITFIELD_FIELD(__u64 : 5,
+	__BITFIELD_FIELD(__u64 scay0 : 11,
+	__BITFIELD_FIELD(__u64 : 5,
+	__BITFIELD_FIELD(__u64 scax1 : 11,
+	__BITFIELD_FIELD(__u64 : 5,
+	__BITFIELD_FIELD(__u64 scax0 : 11,
+	;))))))))
+};
+
+/**
+ * enum gs_trxpos_dir - transmission direction
+ * @gs_trxpos_dir_ul_lr: upper left -> lower right
+ * @gs_trxpos_dir_ll_ur: lower left -> upper right
+ * @gs_trxpos_dir_ur_ll: upper right -> lower left
+ * @gs_trxpos_dir_lr_ul: lower right -> upper left
+ *
+ * The pixel transmission order is enabled only for local -> local.
+ */
+enum gs_trxpos_dir {
+	gs_trxpos_dir_ul_lr,
+	gs_trxpos_dir_ll_ur,
+	gs_trxpos_dir_ur_ll,
+	gs_trxpos_dir_lr_ul,
+};
+
+/**
+ * struct gs_trxpos - transmission areass in buffers
+ * @dir: Pixel transmission order
+ * @dsay: Destination start y
+ * @dsax: Destination start x
+ * @ssay: Source start y
+ * @ssax: Source start x
+ *
+ * Host -> local: Only destination fields are used. DIR is ignored and the
+ * pixel transmission order is always left to right and top to bottom.
+ *
+ * Local -> host: Only source fields are used. DIR is ignored and the pixel
+ * transmission order is always left to right and top to bottom.
+ *
+ * Local -> local: Both source and destination fields are used. The pixel
+ * transmission order DIR is used.
+ */
+struct gs_trxpos {
+	__BITFIELD_FIELD(__u64 : 3,
+	__BITFIELD_FIELD(__u64 dir : 2,
+	__BITFIELD_FIELD(__u64 dsay : 11,
+	__BITFIELD_FIELD(__u64 : 5,
+	__BITFIELD_FIELD(__u64 dsax : 11,
+	__BITFIELD_FIELD(__u64 : 5,
+	__BITFIELD_FIELD(__u64 ssay : 11,
+	__BITFIELD_FIELD(__u64 : 5,
+	__BITFIELD_FIELD(__u64 ssax : 11,
+	;)))))))))
+};
+
+/**
+ * struct gs_trxreg - transmission areas in buffers
+ * @rrh: transmission area height
+ * @rrw: transmission area width
+ *
+ * The transmission coordinates are modulo 2048 (wrap around).
+ */
+struct gs_trxreg {
+	__BITFIELD_FIELD(__u64 : 20,
+	__BITFIELD_FIELD(__u64 rrh : 12,
+	__BITFIELD_FIELD(__u64 : 20,
+	__BITFIELD_FIELD(__u64 rrw : 12,
+	;))))
+};
+
+enum gs_trxdir_xdir {
+	gs_trxdir_host_to_local,
+	gs_trxdir_local_to_host,
+	gs_trxdir_local_to_local,
+	gs_trxdir_nil,				/* Deactivated transmission */
+};
+
+/**
+ * struct gs_trxdir - activation of transmission between buffers
+ * @xdir: transmission direction
+ *
+ * The TRXDIR register specifies the transmission
+ * direction and activates the transmission.
+ */
+struct gs_trxdir {
+	__BITFIELD_FIELD(__u64 : 62,
+	__BITFIELD_FIELD(__u64 xdir : 2,
+	;))
+};
+
+enum gs_alpha_test { gs_alpha_test_off, gs_alpha_test_on };
+enum gs_alpha_method {
+	gs_alpha_method_fail, gs_alpha_method_pass, gs_alpha_method_less,
+	gs_alpha_method_lequal, gs_alpha_method_equal, gs_alpha_method_gequal,
+	gs_alpha_method_greater, gs_alpha_method_notequal
+};
+enum gs_alpha_failed {
+	gs_alpha_failed_keep, gs_alpha_failed_fb_only,
+	gs_alpha_failed_zb_only, gs_alpha_failed_rgb_only
+};
+enum gs_alpha_dst_test { gs_alpha_dst_test_off, gs_alpha_dst_test_on };
+enum gs_alpha_dst_method { gs_alpha_dst_pass0, gs_alpha_dst_pass1 };
+enum gs_depth_test { gs_depth_test_off, gs_depth_test_on };
+enum gs_depth_method {
+	gs_depth_fail, gs_depth_pass, gs_depth_gequal, gs_depth_greater
+};
+
+/**
+ * struct gs_test_12 - pixel test control
+ * @ztst: depth test method
+ * @zte: depth test (must be 1)
+ * @datm: destination alpha test mode
+ * @date: depth test
+ * @afail: destination alpha test
+ * @aref: alpha reference comparison
+ * @atst: alpha test method
+ * @ate: alpha test
+ *
+ * The ZTE field must at all times be ON (OFF is not allowed). To emulate
+ * ZTE OFF, set ZTST to PASS.
+ */
+struct gs_test_12 {
+	__BITFIELD_FIELD(__u64 : 45,
+	__BITFIELD_FIELD(__u64 ztst : 2,
+	__BITFIELD_FIELD(__u64 zte : 1,
+	__BITFIELD_FIELD(__u64 datm : 1,
+	__BITFIELD_FIELD(__u64 date : 1,
+	__BITFIELD_FIELD(__u64 afail : 2,
+	__BITFIELD_FIELD(__u64 aref : 8,
+	__BITFIELD_FIELD(__u64 atst : 3,
+	__BITFIELD_FIELD(__u64 ate : 1,
+	;)))))))))
+};
+
+/**
+ * struct gs_rgbaq - vertex color
+ * @q: normalized texture coordinate
+ * @a: alpha vertex value (0x80 = 1.0)
+ * @b: blue luminance element of vertex
+ * @g: green luminance element of vertex
+ * @r: red luminance element of vertex
+ *
+ * The Q field is used both for calculating texture coordinates and deciding
+ * level of detail (LOD).
+ */
+struct gs_rgbaq {
+	__BITFIELD_FIELD(__u64 q : 32,
+	__BITFIELD_FIELD(__u64 a : 8,
+	__BITFIELD_FIELD(__u64 b : 8,
+	__BITFIELD_FIELD(__u64 g : 8,
+	__BITFIELD_FIELD(__u64 r : 8,
+	;)))))
+};
+
+/**
+ * struct gs_uv - vertex texture coordinates
+ * @v: texel coordinate v*16
+ * @u: texel coordinate u*16
+ */
+struct gs_uv {
+	__BITFIELD_FIELD(__u64 : 34,
+	__BITFIELD_FIELD(__u64 v : 14,
+	__BITFIELD_FIELD(__u64 : 2,
+	__BITFIELD_FIELD(__u64 u : 14,
+	;))))
+};
+
+/**
+ * struct gs_xyz23 - vertex coordinates without a drawing kick
+ * @z: vertext coordinate z
+ * @y: vertext coordinate y*16
+ * @x: vertext coordinate x*16
+ *
+ * Assigning XYZ2 moves the vertex queue one step forward. Drawing is not
+ * started with XYZ3 (no Drawing Kick).
+ *
+ * X and Y are specified as fixed-point (4-bit scaling factor) in the
+ * primitive coordinate system.
+ */
+struct gs_xyz23 {
+	__BITFIELD_FIELD(__u64 z : 32,
+	__BITFIELD_FIELD(__u64 y : 16,
+	__BITFIELD_FIELD(__u64 x : 16,
+	;)))
+};
+
+/**
+ * struct gs_xyoffset_12 - primitive to window coordinate offsets
+ * @ofy: offset y*16
+ * @ofx: offset x*16
+ *
+ * Coordinate offsets from the primitive coordinate system to the window
+ * coordinate system.
+ */
+struct gs_xyoffset_12 {
+	__BITFIELD_FIELD(__u64 : 16,
+	__BITFIELD_FIELD(__u64 ofy : 16,
+	__BITFIELD_FIELD(__u64 : 16,
+	__BITFIELD_FIELD(__u64 ofx : 16,
+	;))))
+};
+
+enum gs_tfx {
+	gs_tfx_modulate, gs_tfx_decal,
+	gs_tfx_highlight, gs_tfx_highlight2
+};
+
+enum gs_tcc { gs_tcc_rgb, gs_tcc_rgba };
+enum gs_csm { gs_csm1, gs_csm2 };
+
+/**
+ * struct gs_tex0 - texture setting
+ * @cld: CLUT buffer load control
+ * @csa: CLUT entry offset
+ * @csm: CLUT storage mode
+ * @cpsm: CLUT pixel storage format
+ * @cbp: CLUT buffer base pointer
+ * @tfx: texture function
+ * @tcc: texture color component
+ * @th: texture height (2^h)
+ * @tw: texture width (2^w)
+ * @psm: texture storage format
+ * @tbw: texture buffer width
+ * @tbp0: texture base pointer
+ */
+struct gs_tex0 {
+	__BITFIELD_FIELD(__u64 cld : 3,
+	__BITFIELD_FIELD(__u64 csa : 5,
+	__BITFIELD_FIELD(__u64 csm : 1,
+	__BITFIELD_FIELD(__u64 cpsm : 4,
+	__BITFIELD_FIELD(__u64 cbp : 14,
+	__BITFIELD_FIELD(__u64 tfx : 2,
+	__BITFIELD_FIELD(__u64 tcc : 1,
+	__BITFIELD_FIELD(__u64 th : 4,
+	__BITFIELD_FIELD(__u64 tw : 4,
+	__BITFIELD_FIELD(__u64 psm : 6,
+	__BITFIELD_FIELD(__u64 tbw : 6,
+	__BITFIELD_FIELD(__u64 tbp0 : 14,
+	;))))))))))))
+};
+
+enum gs_lcm { gs_lcm_formula, gs_lcm_fixed };
+
+enum gs_lod {
+	gs_lod_nearest,
+	gs_lod_linear,
+	gs_lod_nearest_mipmap_nearest,
+	gs_lod_nearest_mipmap_linear,
+	gs_lod_linear_mipmap_nearest,
+	gs_lod_linear_mipmap_linear
+};
+
+enum gs_aem { gs_aem_normal, gs_aem_transparent };
+
+/**
+ * struct gs_texa - texture alpha value setting
+ * @ta1: alpha when A=1 in RGBA16
+ * @aem: alpha expanding method
+ * @ta0: alpha when A=0 in RGBA16
+ */
+struct gs_texa {
+	__BITFIELD_FIELD(__u64 : 24,
+	__BITFIELD_FIELD(__u64 ta1 : 8,
+	__BITFIELD_FIELD(__u64 : 16,
+	__BITFIELD_FIELD(__u64 aem : 1,
+	__BITFIELD_FIELD(__u64 : 7,
+	__BITFIELD_FIELD(__u64 ta0 : 8,
+	;))))))
+};
+
+/**
+ * struct gs_tex1 - texture setting
+ * @k: LOD parameter K
+ * @l: LOD parameter L
+ * @mtba: MIPMAP base address specification of level 1 or more
+ * @mmin: reduced texture filter
+ * @mmag: expanded texture filter
+ * @mxl: maximum MIP level
+ * @lcm: LOD calculation method
+ */
+struct gs_tex1 {
+	__BITFIELD_FIELD(__u64 k : 11,
+	__BITFIELD_FIELD(__u64 : 11,
+	__BITFIELD_FIELD(__u64 l : 2,
+	__BITFIELD_FIELD(__u64 : 9,
+	__BITFIELD_FIELD(__u64 mtba : 1,
+	__BITFIELD_FIELD(__u64 mmin : 3,
+	__BITFIELD_FIELD(__u64 mmag : 1,
+	__BITFIELD_FIELD(__u64 mxl : 3,
+	__BITFIELD_FIELD(__u64 : 1,
+	__BITFIELD_FIELD(__u64 lcm : 1,
+	;))))))))))
+};
+
+/**
+ * struct gs_tex2 - texture setting
+ * @cld: CLUT buffer load control
+ * @csa: CLUT entry offset
+ * @csm: CLUT storage mode
+ * @cpsm: CLUT pixel storage format
+ * @cbp: CLUT buffer base pointer
+ * @psm: Texture storage format
+ */
+struct gs_tex2 {
+	__BITFIELD_FIELD(__u64 cld : 3,
+	__BITFIELD_FIELD(__u64 csa : 5,
+	__BITFIELD_FIELD(__u64 csm : 1,
+	__BITFIELD_FIELD(__u64 cpsm : 4,
+	__BITFIELD_FIELD(__u64 cbp : 14,
+	__BITFIELD_FIELD(__u64 : 11,
+	__BITFIELD_FIELD(__u64 psm : 6,
+	__BITFIELD_FIELD(__u64 : 20,
+	;))))))))
+};
+
+enum gs_zmsk { gs_zbuf_on, gs_zbuf_off };
+
+/**
+ * struct gs_zbuf - z buffer setting
+ * @zmsk: z value drawing mask
+ * @psm: z value storage format (PSMZ32, PSMZ24, PSMZ16 or PSMZ16S)
+ * @zbp: z buffer base pointer/2048
+ */
+struct gs_zbuf {
+	__BITFIELD_FIELD(__u64 : 31,
+	__BITFIELD_FIELD(__u64 zmsk : 1,
+	__BITFIELD_FIELD(__u64 : 4,
+	__BITFIELD_FIELD(__u64 psm : 4,
+	__BITFIELD_FIELD(__u64 : 15,
+	__BITFIELD_FIELD(__u64 zbp : 9,
+	;))))))
+};
+
+enum gs_dthe_mode { gs_dthe_off, gs_dthe_on };
+
+/**
+ * struct gs_dthe - dither control
+ * @dthe: dithering off=0 or on=1
+ *
+ * If the pixel storage mode of the frame buffer is PSMCT32 or PSMCT24,
+ * dithering should be turned off.
+ */
+struct gs_dthe {
+	__BITFIELD_FIELD(__u64 : 63,
+	__BITFIELD_FIELD(__u64 dthe : 1,
+	;))
+};
+
+#endif /* _UAPI_MIPS_ASM_GS_H */
Signature · 1 line
2.21.0
#106 lore raw

PATCH 84/120 MIPS: PS2: GIF: Define Graphics Synthesizer interface structures

In reply to Fredrik Noring

The Graphics Synthesizer interface (GIF) formats data based on the GIF tags at the start of display list packets, and then transfer formatted data to the GS as drawing commands[1].

The GIF has three kinds of general data transfer paths:

- PATH1: data from vector processor unit 1 (VPU1) memory;
- PATH2: data via the VPU1 interface packet expansion engine (VIF1) FIFO;
- PATH3: data directly from the main bus to the Graphics Synthesizer.

The GIF tag is a 16-byte fixed length object that specifies the size and structure of subsequent data. Up to 16 register descriptors can be specified in one GIF tag, specifying to which registers of the GS the input data following the GIF tag should be output and the packing format mode:

- in PACKED mode, the data of each quadword is interpreted and packed according to the register descriptor in the GIF tag, and is output to the address specified in the same way by the register descriptor;

- in REGLIST mode, the data following the GIF tag is considered to be data strings of 2x64 bits and output as is without packing by setting the register descriptor value as the output destination address;

- in IMAGE mode, the data following the GIF tag is considered to be data strings of 2x64 bits and output to the host-local transfer register HWREG of the GS, used when transferring image data such as textures.

The privileged registers of the GS are directly mapped in the address space of the main bus, and are not accessible via the GIF regardless of the state of the general data transfer path. The GIF monitors access to the privileged registers.

It is nontrivial to transfer data from GS local memory by reversing the direction with the privileged BUSDIR register.

References:

[1] "EE User's Manual", version 6.0, Sony Computer Entertainment Inc.,

    pp. 147-160.

---
I think a /dev/gs device for the Graphics Synthesizer could be very useful, which is why the GIF header file is put in the uapi directory, but no such device driver exists at the moment so perhaps the header file could be kernel private until then?

---
 arch/mips/include/uapi/asm/gif.h | 164 +++++++++++++++++++++++++++++++
 1 file changed, 164 insertions(+)
 create mode 100644 arch/mips/include/uapi/asm/gif.h
arch/mips/include/uapi/asm/gif.h new file +164 -0
new file mode 100644
index 000000000000..a952ed879de4
@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 Graphics Synthesizer interface (GIF)
+ *
+ * Copyright (C) 2018 Fredrik Noring
+ */
+
+/**
+ * DOC: Graphics Synthesizer interface (GIF) structures
+ *
+ * The Graphics Synthesizer interface (GIF) formats data based on the GIF tags
+ * at the start of display list packets, and then transfer formatted data to
+ * the GS as drawing commands.
+ *
+ * The GIF has three kinds of general data transfer paths:
+ *
+ * - PATH1: data from vector processor unit 1 (VPU1) memory;
+ * - PATH2: data via the VPU1 interface packet expansion engine (VIF1) FIFO;
+ * - PATH3: data directly from the main bus to the Graphics Synthesizer.
+ *
+ * The GIF tag is a 16-byte fixed length object that specifies the size and
+ * structure of subsequent data. Up to 16 register descriptors can be specified
+ * in one GIF tag, specifying to which registers of the GS the input data
+ * following the GIF tag should be output and the packing format mode.
+ *
+ * The privileged registers of the GS are directly mapped to the address space
+ * of the main bus, and are not accessible via the GIF regardless of the state
+ * of the general data transfer path. The GIF monitors access to the privileged
+ * registers.
+ */
+
+#ifndef _UAPI_MIPS_ASM_GIF_H
+#define _UAPI_MIPS_ASM_GIF_H
+
+#include <asm/bitfield.h>
+
+#include "gs.h"
+
+#define GIF_TAG_SIZE 16		/* 128 bits */
+#define gif_quadword_count(x) (((x) + GIF_TAG_SIZE-1) >> 4)
+
+/*
+ * When transferring data via PATH1 and PATH3 in parallel, do not apply the
+ * A+D packing format to the data on PATH3. This may cause the GS to hang.
+ */
+enum gif_tag_reg {
+	gif_reg_prim,        gif_reg_rgbaq,   gif_reg_st,     gif_reg_uv,
+	gif_reg_xyzf2,       gif_reg_xyz2,    gif_reg_tex0_1, gif_reg_tex0_2,
+	gif_reg_clamp_1,     gif_reg_clamp_2, gif_reg_fog,    /* Reserved */
+	gif_reg_xyzf3 = 0xc, gif_reg_xyz3,    gif_reg_ad,     gif_reg_nop
+};
+
+/**
+ * enum gif_tag_flg - GIF tag data packing format mode
+ * @gif_packed_mode: in PACKED mode, each quadword is interpreted and packed
+ * 	according to the register descriptor in the GIF tag, and is output to
+ * 	the address specified in the same way by the register descriptor
+ * @gif_reglist_mode: in REGLIST mode, the data following the GIF tag is
+ * 	considered to be data strings of 2x64 bits and output as is without
+ * 	packing by setting the register descriptor value as the output
+ * 	destination address
+ * @gif_image_mode: in IMAGE mode, the data following the GIF tag is considered
+ * 	to be data strings of 2x64 bits and output to the host-local transfer
+ * 	register HWREG of the GS, used when transferring image data such as
+ * 	textures
+ * @gif_disabled_mode: disable (same operation as the IMAGE mode)
+ */
+enum gif_tag_flg {
+	gif_packed_mode,
+	gif_reglist_mode,
+	gif_image_mode,
+	gif_disabled_mode
+};
+
+#define GIF_TAG_NLOOP_MAX 0x7fff	/* The NLOOP field is 15 bits */
+
+/*
+ * The REGS, NREG, PRIM and PRE fields of the GIF tag are ignored in IMAGE mode.
+ */
+struct gif_tag {
+	__BITFIELD_FIELD(__u64 reg15 : 4,
+	__BITFIELD_FIELD(__u64 reg14 : 4,
+	__BITFIELD_FIELD(__u64 reg13 : 4,
+	__BITFIELD_FIELD(__u64 reg12 : 4,
+	__BITFIELD_FIELD(__u64 reg11 : 4,
+	__BITFIELD_FIELD(__u64 reg10 : 4,
+	__BITFIELD_FIELD(__u64 reg9 : 4,
+	__BITFIELD_FIELD(__u64 reg8 : 4,
+	__BITFIELD_FIELD(__u64 reg7 : 4,
+	__BITFIELD_FIELD(__u64 reg6 : 4,
+	__BITFIELD_FIELD(__u64 reg5 : 4,
+	__BITFIELD_FIELD(__u64 reg4 : 4,
+	__BITFIELD_FIELD(__u64 reg3 : 4,
+	__BITFIELD_FIELD(__u64 reg2 : 4,
+	__BITFIELD_FIELD(__u64 reg1 : 4,
+	__BITFIELD_FIELD(__u64 reg0 : 4,	/* First register descriptor */
+	__BITFIELD_FIELD(__u64 nreg : 4,	/* 0 means 16 registers */
+	__BITFIELD_FIELD(__u64 flg : 2,		/* Data format mode */
+	__BITFIELD_FIELD(__u64 prim : 11,	/* PRIM register */
+	__BITFIELD_FIELD(__u64 pre : 1,		/* Enable PRIM field */
+	__BITFIELD_FIELD(__u64 : 30,
+	__BITFIELD_FIELD(__u64 eop : 1,		/* End of packet */
+	__BITFIELD_FIELD(__u64 nloop : 15,
+	;)))))))))))))))))))))))
+} __attribute__((aligned(GIF_TAG_SIZE)));
+
+union gif_reg {
+	struct gs_bitbltbuf bitbltbuf;
+	struct gs_clamp clamp_1;
+	struct gs_clamp clamp_2;
+	struct gs_dthe dthe;
+	struct gs_frame_12 frame_1;
+	struct gs_frame_12 frame_2;
+	struct gs_prim prim;
+	struct gs_prmode prmode;
+	struct gs_prmodecont prmodecont;
+	struct gs_rgbaq rgbaq;
+	struct gs_scanmsk_12 scanmsk;
+	struct gs_scissor_12 scissor_1;
+	struct gs_scissor_12 scissor_2;
+	struct gs_test_12 test_1;
+	struct gs_test_12 test_2;
+	struct gs_texa texa;
+	struct gs_tex0 tex0;
+	struct gs_tex1 tex1;
+	struct gs_tex2 tex2;
+	struct gs_trxdir trxdir;
+	struct gs_trxpos trxpos;
+	struct gs_trxreg trxreg;
+	struct gs_uv uv;
+	struct gs_xyz23 xyz2;
+	struct gs_xyz23 xyz3;
+	struct gs_xyoffset_12 xyoffset_1;
+	struct gs_xyoffset_12 xyoffset_2;
+	struct gs_zbuf zbuf;
+};
+
+struct gif_packed_ad {
+	union gif_reg data;			/* Register data */
+	__BITFIELD_FIELD(__u64 : 56,
+	__BITFIELD_FIELD(__u64 addr : 8,	/* Register address */
+	;))
+};
+
+struct gif_data_reg {
+	union gif_reg lo;
+	union gif_reg hi;
+};
+
+union gif_data {
+	struct gif_tag tag;
+	union {
+		struct gif_packed_ad ad;
+	} packed;
+	struct gif_data_reg reg;
+	struct gs_rgba32 rgba32[4];
+	__u8 image[16];	/* All pixel formats pack data without padding */
+	__u64 doubleword[2];
+	__u32 word[4];
+	__u16 halfword[8];
+	__u8 byte[16];
+} __attribute__((aligned(GIF_TAG_SIZE)));
+
+#endif /* _UAPI_MIPS_ASM_GIF_H */
Signature · 1 line
2.21.0
#107 lore raw

PATCH 85/120 MIPS: PS2: GIF: Graphics Synthesizer interface support

In reply to Fredrik Noring
---
 arch/mips/include/asm/mach-ps2/gif.h |  77 +++++++++++++++++++
 drivers/ps2/Makefile                 |   1 +
 drivers/ps2/gif.c                    | 106 +++++++++++++++++++++++++++
 3 files changed, 184 insertions(+)
 create mode 100644 arch/mips/include/asm/mach-ps2/gif.h
 create mode 100644 drivers/ps2/gif.c
arch/mips/include/asm/mach-ps2/gif.h new file +77 -0
new file mode 100644
index 000000000000..2a3965bd5544
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 Graphics Synthesizer interface (GIF)
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#ifndef __ASM_MACH_PS2_GIF_H
+#define __ASM_MACH_PS2_GIF_H
+
+#include <asm/types.h>
+
+#include <uapi/asm/gif.h>
+
+#define GIF_CTRL	0x10003000	/* (W) GIF control */
+#define GIF_MODE	0x10003010	/* (W) GIF mode */
+#define GIF_STAT	0x10003020	/* (R) GIF status */
+
+/*
+ * These GIF registers are accessible only when stopped by the
+ * &gif_ctrl.pse flag.
+ */
+
+#define GIF_TAG0	0x10003040	/* (R) GIF tag 31:0 */
+#define GIF_TAG1	0x10003050	/* (R) GIF tag 63:32 */
+#define GIF_TAG2	0x10003060	/* (R) GIF tag 95:54 */
+#define GIF_TAG3	0x10003070	/* (R) GIF tag 127:96 */
+#define GIF_CNT		0x10003080	/* (R) GIF transfer status counter */
+#define GIF_P3CNT	0x10003090	/* (R) PATH3 transfer status counter */
+#define GIF_P3TAG	0x100030a0	/* (R) PATH3 GIF tag value */
+
+/**
+ * struct gif_ctrl - GIF control register
+ * @rst: GIF reset
+ * @pse: temporary transfer stop
+ *
+ * Writing 1 to PSE temporarily stops GIF transfers and makes it possible
+ * to read GIF registers for debugging. Writing 0 to PSE resumes transfers.
+ */
+struct gif_ctrl {
+	u32 rst : 1;
+	u32 : 2;
+	u32 pse : 1;
+	u32 : 28;
+};
+
+/**
+ * gif_writel_ctrl - write word to the GIF_CTRL register
+ * @value - 32-bit word to write
+ */
+void gif_writel_ctrl(u32 value);
+
+/**
+ * gif_write_ctrl - write structure to the GIF_CTRL register
+ * @value - structure to write
+ */
+void gif_write_ctrl(struct gif_ctrl value);
+
+/**
+ * gif_reset- reset the GIF
+ *
+ * The reset includes a delay of 100 us.
+ */
+void gif_reset(void);
+
+/**
+ * gif_wait - is the GIF ready to transfer data?
+ *
+ * FIXME: Move to ps2fb.c
+ *
+ * Return: %true if ready to transfer data, otherwise %false
+ */
+bool gif_wait(void);
+
+void gif_write(union gif_data *base_package, size_t package_count);
+
+#endif /* __ASM_MACH_PS2_GIF_H */
drivers/ps2/Makefile +1 -0
index 7f108d4ee451..9671bbe40294 100644
@@ -1,3 +1,4 @@
+obj-$(CONFIG_PS2_GS)		+= gif.o
 obj-$(CONFIG_PS2_GS)		+= gs-irq.o
 obj-$(CONFIG_PS2_GS)		+= gs-registers.o
 
drivers/ps2/gif.c new file +106 -0

106 lines not on this page · raw message

Signature · 1 line
2.21.0
#108 lore raw

PATCH 86/120 MIPS: PS2: GS: Graphics Synthesizer device init and video clock

In reply to Fredrik Noring

Several combinations of video clock register values appear to generate equivalent frequencies. For the standard ones, the combinations used by Sony are preferred and tabulated. For all others, a search is performed.

---
 arch/mips/include/asm/mach-ps2/gs.h |  47 +++++++
 arch/mips/ps2/Kconfig               |  16 +++
 drivers/ps2/Makefile                |   1 +
 drivers/ps2/gs.c                    | 195 ++++++++++++++++++++++++++++
 4 files changed, 259 insertions(+)
 create mode 100644 arch/mips/include/asm/mach-ps2/gs.h
 create mode 100644 arch/mips/ps2/Kconfig
 create mode 100644 drivers/ps2/gs.c
arch/mips/include/asm/mach-ps2/gs.h new file +47 -0
new file mode 100644
index 000000000000..9cb1b909ae9b
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 Graphics Synthesizer (GS)
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#ifndef __ASM_MACH_PS2_GS_H
+#define __ASM_MACH_PS2_GS_H
+
+#include <asm/types.h>
+
+#include <asm/mach-ps2/gs-registers.h>
+
+#define GS_REG_BASE	0x12000000
+
+/**
+ * struct gs_synch_gen - Graphics Synthesizer SMODE1 register video clock fields
+ * @rc: PLL reference divider
+ * @lc: PLL loop divider
+ * @t1248: PLL output divider
+ * @spml: FIXME
+ *
+ * These fields determine the Graphics Synthesizer video clock
+ *
+ * 	VCK = (13500000 * @lc) / ((@t1248 + 1) * @spml * @rc).
+ *
+ * See also &struct gs_smode1.
+ */
+struct gs_synch_gen {
+	u32 rc : 3;
+	u32 lc : 7;
+	u32 t1248 : 2;
+	u32 spml : 4;
+};
+
+u32 gs_video_clock(const u32 t1248, const u32 lc, const u32 rc);
+
+u32 gs_video_clock_for_smode1(const struct gs_smode1 smode1);
+
+struct gs_synch_gen gs_synch_gen_for_vck(const u32 vck);
+
+u32 gs_rfsh_from_synch_gen(const struct gs_synch_gen sg);
+
+struct device *gs_device_driver(void);	/* FIXME: Is this method appropriate? */
+
+#endif /* __ASM_MACH_PS2_GS_H */
arch/mips/ps2/Kconfig new file +16 -0
new file mode 100644
index 000000000000..f782d61cfaac
@@ -0,0 +1,16 @@
+config PS2_GS
+	tristate "PlayStation 2 Graphics Synthesizer"
+	depends on SONY_PS2
+	default y
+	help
+	  Say yes to enable the PlayStation 2 Graphics Synthesizer. The GS
+	  draws primitives such as triangles and sprites to its 4 MiB local
+	  frame buffer. It can handle shading, texture mapping,
+	  z-buffering, alpha blending, edge antialiasing and fogging.
+
+	  PAL, NTSC and VESA video modes are supported. Interlace and
+	  noninterlaced can be switched. The resolution is variable from
+	  256x224 to 1920x1080.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called gs.
drivers/ps2/Makefile +1 -0
index 9671bbe40294..28fb55803199 100644
@@ -1,4 +1,5 @@
 obj-$(CONFIG_PS2_GS)		+= gif.o
+obj-$(CONFIG_PS2_GS)		+= gs.o
 obj-$(CONFIG_PS2_GS)		+= gs-irq.o
 obj-$(CONFIG_PS2_GS)		+= gs-registers.o
 
drivers/ps2/gs.c new file +195 -0

195 lines not on this page · raw message

Signature · 1 line
2.21.0
#109 lore raw

PATCH 87/120 MIPS: PS2: GS: Compute block count and indices

In reply to Fredrik Noring

The GS frame buffer is subdivided into rectangular pages, from left to right, top to bottom. Pages are further subdivided into blocks, with different arrangements for the PSMCT16 and PSMCT32 pixel storage modes. Blocks are further subdivided into columns that are finally subdivided into pixels[1].

References:

[1] "GS User's Manual", version 6.0, Sony Computer Entertainment Inc.,

    pp. 161-175.
---
 arch/mips/include/asm/mach-ps2/gs.h |  8 +++
 drivers/ps2/gs.c                    | 94 +++++++++++++++++++++++++++++
 2 files changed, 102 insertions(+)
arch/mips/include/asm/mach-ps2/gs.h +8 -0
index 9cb1b909ae9b..5429f52a4518 100644
@@ -38,6 +38,14 @@ u32 gs_video_clock(const u32 t1248, const u32 lc, const u32 rc);
 
 u32 gs_video_clock_for_smode1(const struct gs_smode1 smode1);
 
+u32 gs_psm_ct16_block_count(const u32 fbw, const u32 fbh);
+
+u32 gs_psm_ct32_block_count(const u32 fbw, const u32 fbh);
+
+u32 gs_psm_ct32_block_address(const u32 fbw, const u32 block_index);
+
+u32 gs_psm_ct16_block_address(const u32 fbw, const u32 block_index);
+
 struct gs_synch_gen gs_synch_gen_for_vck(const u32 vck);
 
 u32 gs_rfsh_from_synch_gen(const struct gs_synch_gen sg);
drivers/ps2/gs.c +94 -0

100 lines not on this page · raw message

Signature · 1 line
2.21.0
#110 lore raw

PATCH 88/120 MIPS: PS2: GS: Primitive and texel coordinate transformations

In reply to Fredrik Noring

The frame buffer coordinate system is the pixel drawing space, with integer coordinates.

The primitive coordinate system is the drawing space used for vertices, with 4-bit fractional parts.

The texel coordinate system is used for textures, with 4-bit fractional parts, centered on the position where the fractional parts are 0.5[1].

References:

[1] "GS User's Manual", version 6.0, Sony Computer Entertainment Inc.,

    pp. 23-24,  28.
---
 arch/mips/include/asm/mach-ps2/gs.h | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)
arch/mips/include/asm/mach-ps2/gs.h +22 -0
index 5429f52a4518..935d03007680 100644
@@ -46,6 +46,28 @@ u32 gs_psm_ct32_block_address(const u32 fbw, const u32 block_index);
 
 u32 gs_psm_ct16_block_address(const u32 fbw, const u32 block_index);
 
+/**
+ * gs_fbcs_to_pcs - frame buffer coordinate to primitive coordinate
+ * @c: frame buffer coordinate
+ *
+ * Return: primitive coordinate
+ */
+static inline int gs_fbcs_to_pcs(const int c)
+{
+	return c * 16;	/* The 4 least significant bits are fractional. */
+}
+
+/**
+ * gs_pxcs_to_tcs - pixel coordinate to texel coordinate
+ * @c: pixel coordinate
+ *
+ * Return: texel coordinate
+ */
+static inline int gs_pxcs_to_tcs(const int c)
+{
+	return c * 16 + 8;  /* The 4 least significant bits are fractional. */
+}
+
 struct gs_synch_gen gs_synch_gen_for_vck(const u32 vck);
 
 u32 gs_rfsh_from_synch_gen(const struct gs_synch_gen sg);
Signature · 1 line
2.21.0
#111 lore raw

PATCH 89/120 MIPS: PS2: GS: Approximate video region with ROM region

In reply to Fredrik Noring

PlayStation 2 hardware indicates regions in multiple ways. There are regions for the ROM, discs, CSS, video mode and Magic Gate. Currently only the ROM region is implemented.

---
 arch/mips/include/asm/mach-ps2/gs.h |  4 ++++
 drivers/ps2/gs.c                    | 30 +++++++++++++++++++++++++++++
 2 files changed, 34 insertions(+)
arch/mips/include/asm/mach-ps2/gs.h +4 -0
index 935d03007680..e719ca0531ba 100644
@@ -34,6 +34,10 @@ struct gs_synch_gen {
 	u32 spml : 4;
 };
 
+bool gs_region_pal(void);
+
+bool gs_region_ntsc(void);
+
 u32 gs_video_clock(const u32 t1248, const u32 lc, const u32 rc);
 
 u32 gs_video_clock_for_smode1(const struct gs_smode1 smode1);
drivers/ps2/gs.c +30 -0
index a3cd1a6adfb7..c380dfa358b5 100644
@@ -21,6 +21,36 @@
 
 static struct device *gs_dev;
 
+/**
+ * gs_region_pal - is the machine for a PAL video mode region?
+ *
+ * See also gs_region_ntsc(). The system region is determined by rom_version(),
+ * which is an approximation because the ROM region does not always correspdond
+ * to the video region.
+ *
+ * Return: %true if PAL video mode is appropriate for the region, else %false
+ */
+bool gs_region_pal(void)
+{
+	return rom_version().region == 'E';
+}
+EXPORT_SYMBOL_GPL(gs_region_pal);
+
+/**
+ * gs_region_ntsc - is the machine for an NTSC video mode region?
+ *
+ * See also gs_region_pal(). The system region is determined by rom_version(),
+ * which is an approximation because the ROM region does not always correspdond
+ * to the video region.
+ *
+ * Return: %true if NTSC video mode is appropriate for the region, else %false
+ */
+bool gs_region_ntsc(void)
+{
+	return !gs_region_pal();
+}
+EXPORT_SYMBOL_GPL(gs_region_ntsc);
+
 /**
  * gs_video_clock - video clock (VCK) frequency given SMODE1 bit fields
  * @t1248 - &gs_smode1.t1248 PLL output divider
Signature · 1 line
2.21.0
#112 lore raw

PATCH 90/120 macro: Extend COUNT_ARGS() from 12 to 32 arguments

In reply to Fredrik Noring

This is useful to define sysfs bit-fields, such as this one in a forthcoming change:

SYSFS_RW_REG(smode1, smode1,

	SYSFS_DECNUM_FIELD(rc),
	SYSFS_DECNUM_FIELD(lc),
	SYSFS_DECNUM_FIELD(t1248),
	SYSFS_DECNUM_FIELD(slck),
	SYSFS_SYMBOL_FIELD(cmod, vesa, ntsc, pal),
	SYSFS_DECNUM_FIELD(ex),
	SYSFS_DECNUM_FIELD(prst),
	SYSFS_DECNUM_FIELD(sint),
	SYSFS_DECNUM_FIELD(xpck),
	SYSFS_DECNUM_FIELD(pck2),
	SYSFS_DECNUM_FIELD(spml),
	SYSFS_SYMBOL_FIELD(gcont, rgbyc, ycrcb),
	SYSFS_DECNUM_FIELD(phs),
	SYSFS_DECNUM_FIELD(pvs),
	SYSFS_DECNUM_FIELD(pehs),
	SYSFS_DECNUM_FIELD(pevs),
	SYSFS_DECNUM_FIELD(clksel),
	SYSFS_DECNUM_FIELD(nvck),
	SYSFS_DECNUM_FIELD(slck2),
	SYSFS_DECNUM_FIELD(vcksel),
	SYSFS_DECNUM_FIELD(vhp));
---
 include/linux/kernel.h | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)
include/linux/kernel.h +9 -3
index 4fa360a13c1e..a9d57f6ed1d3 100644
@@ -954,9 +954,15 @@ static inline void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) { }
 #define swap(a, b) \
 	do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0)
 
-/* This counts to 12. Any more, it will return 13th argument. */
-#define __COUNT_ARGS(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _n, X...) _n
-#define COUNT_ARGS(X...) __COUNT_ARGS(, ##X, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
+/* This counts to 32. Any more, it will return the 33rd argument. */
+#define __COUNT_ARGS(							\
+	 _0,  _1,  _2,  _3,  _4,  _5,  _6,  _7,  _8,  _9, _10,		\
+	_11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21,		\
+	_22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _n, X...) _n
+#define COUNT_ARGS(X...) __COUNT_ARGS(, ##X,				\
+	32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22,			\
+	21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11,			\
+	10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0)
 
 #define __CONCAT(a, b) a ## b
 #define CONCATENATE(a, b) __CONCAT(a, b)
Signature · 1 line
2.21.0
#113 lore raw

PATCH 91/120 MIPS: PS2: GS: Show privileged registers with sysfs

In reply to Fredrik Noring

Reading arbitrary numerical and symbolical privileged GS register bit fields is supported. For example, the PMODE register can be inspected with:

    # cat /sys/devices/platform/gs/registers/pmode
    en1 1
    en2 0
    crtmd 1
    mmod circuit1
    amod circuit1
    slbg circuit2
    alp 0

The implementation uses a fair amount of macro expansions. This greatly simplifies register definitions, which in the case of PMODE is:

SYSFS_RW_REG(pmode, pmode,
SYSFS_DECNUM_FIELD(en1), SYSFS_DECNUM_FIELD(en2), SYSFS_DECNUM_FIELD(crtmd), SYSFS_SYMBOL_FIELD(mmod, circuit1, alp), SYSFS_SYMBOL_FIELD(amod, circuit1, circuit2), SYSFS_SYMBOL_FIELD(slbg, circuit2, bgcolor), SYSFS_DECNUM_FIELD(alp));

It relies on the following enum and struct definitions:

    enum gs_pmode_mmod { gs_mmod_circuit1, gs_mmod_alp };
    enum gs_pmode_amod { gs_amod_circuit1, gs_amod_circuit2 };
    enum gs_pmode_slbg { gs_slbg_circuit2, gs_slbg_bgcolor };
    struct gs_pmode {
    	u64 en1 : 1;		/* Enable read circuit 1 */
    	u64 en2 : 1;		/* Enable read circuit 2 */
    	u64 crtmd : 3;		/* CRT output switching (always 001) */
    	u64 mmod : 1;		/* Alpha blending value */
    	u64 amod : 1;		/* OUT1 alpha output */
    	u64 slbg : 1;		/* Alpha blending method */
    	u64 alp : 8;		/* Fixed alpha (0xff = 1.0) */
    	u64 zero : 1;		/* Must be zero */
    	u64 : 47;
    };
---
 drivers/ps2/Makefile   |   1 +
 drivers/ps2/gs-sysfs.c | 457 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 458 insertions(+)
 create mode 100644 drivers/ps2/gs-sysfs.c
drivers/ps2/Makefile +1 -0
index 28fb55803199..86ba1d3908dd 100644
@@ -2,6 +2,7 @@ obj-$(CONFIG_PS2_GS)		+= gif.o
 obj-$(CONFIG_PS2_GS)		+= gs.o
 obj-$(CONFIG_PS2_GS)		+= gs-irq.o
 obj-$(CONFIG_PS2_GS)		+= gs-registers.o
+obj-$(CONFIG_PS2_GS)		+= gs-sysfs.o
 
 obj-m				+= iop-heap.o
 obj-m				+= iop-irq.o
drivers/ps2/gs-sysfs.c new file +457 -0

457 lines not on this page · raw message

Signature · 1 line
2.21.0
#114 lore raw

PATCH 92/120 MIPS: PS2: GS: Store privileged registers with sysfs

In reply to Fredrik Noring

One or several bit fields can be written simultaneously. For example, the PMODE slbg field can be changed with:

    # echo "slbg bgcolor" >/sys/devices/platform/gs/registers/pmode

Written values are integers or enum symbols, depending on the field.

---
 drivers/ps2/gs-sysfs.c | 145 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 140 insertions(+), 5 deletions(-)
drivers/ps2/gs-sysfs.c +140 -5
index 2429de3e0094..3c749375aee0 100644
@@ -4,9 +4,9 @@
  *
  * Copyright (C) 2019 Fredrik Noring
  *
- * Reading arbitrary numerical and symbolical privileged GS register
- * bit fields is supported. For example, the PMODE register can be
- * inspected with:
+ * Reading and writing arbitrary numerical and symbolical privileged
+ * GS register bit fields is supported. For example, the PMODE register
+ * can be inspected with:
  *
  *	# cat /sys/devices/platform/gs/registers/pmode
  *	en1 1
@@ -17,6 +17,11 @@
  *	slbg circuit2
  *	alp 0
  *
+ * One or several bit fields can be written simultaneously. For example,
+ * the PMODE slbg field can be changed with:
+ *
+ * 	# echo "slbg bgcolor" >/sys/devices/platform/gs/registers/pmode
+ *
  * The implementation uses a fair amount of macro expansions. This greatly
  * simplifies register definitions, which in the case of PMODE is:
  *
@@ -64,6 +69,76 @@
 
 static struct kobject *registers_kobj;
 
+static size_t line_size(const char *s)
+{
+	const size_t n = strchrnul(s, '\n') - s;
+
+	return s[n] == '\n' ? n + 1 : n;
+}
+
+static const char *trim_line_space(const char *s)
+{
+	while (isspace(*s) && *s != '\n')
+		s++;
+
+	return s;
+}
+
+static bool symbol_match(const char **s, const char *symbol)
+{
+	const size_t length = strlen(symbol);
+	const char *t = *s;
+
+	t = trim_line_space(t);
+	if (strncmp(t, symbol, length) != 0)
+		return false;
+	t += length;
+	t = trim_line_space(t);
+
+	*s = t;
+
+	return true;
+}
+
+static bool number_match(const char **s, u64 *value)
+{
+	const char *t = *s;
+	char *e;
+
+	t = trim_line_space(t);
+	*value = simple_strtoull(t, &e, 0);
+	if (t == e)
+		return false;
+	t = e;
+	t = trim_line_space(t);
+
+	*s = t;
+
+	return true;
+}
+
+static bool end_of_field(const char *s)
+{
+	return *s == '\n' || *s == '\0';
+}
+
+static bool symbol_field(const char *s, const char *field, const char *value)
+{
+	return symbol_match(&s, field) &&
+	       symbol_match(&s, value) &&
+	       end_of_field(s);
+}
+
+static bool number_field(const char *s, const char *field, u64 *value)
+{
+	return symbol_match(&s, field) &&
+	       number_match(&s, value) &&
+	       end_of_field(s);
+}
+
+#define for_each_line(s, n)						\
+	for (n = 0; (s)[n] != '\0'; n += line_size(&(s)[n]))
+
 #define SYSFS_STATEMENT1(prefix_, macro_)				\
 	prefix_##macro_;
 
@@ -231,13 +306,73 @@ static struct kobject *registers_kobj;
 		CONCATENATE(SYSFS_STATEMENT,				\
 			COUNT_ARGS(__VA_ARGS__))(SHOW_, __VA_ARGS__))
 
+#define STORE_SYSFS_DECNUM_FIELD(field_)				\
+	do {								\
+		u64 value_;						\
+		if (number_field(&buf[n], #field_, &value_))		\
+			value.field_ = value_;				\
+	} while (false)
+
+#define STORE_SYSFS_HEXNUM_FIELD(field_)				\
+	STORE_SYSFS_DECNUM_FIELD(field_)
+
+#define STORE_SYSFS_SYMBOL_FIELD_ENTRY(field_, prefix_, value_)		\
+	do {								\
+		if (symbol_field(&buf[n], #field_, #value_))		\
+			value.field_ = prefix_##_##value_;		\
+	} while (false)
+
+#define STORE_SYSFS_SYMBOL_FIELD1(field_, prefix_, value_)		\
+	STORE_SYSFS_SYMBOL_FIELD_ENTRY(field_, prefix_, value_)
+
+#define STORE_SYSFS_SYMBOL_FIELD2(field_, prefix_, value_, ...)		\
+	STORE_SYSFS_SYMBOL_FIELD_ENTRY(field_, prefix_, value_);	\
+	STORE_SYSFS_SYMBOL_FIELD1(field_, prefix_, __VA_ARGS__)
+
+#define STORE_SYSFS_SYMBOL_FIELD3(field_, prefix_, value_, ...)		\
+	STORE_SYSFS_SYMBOL_FIELD_ENTRY(field_, prefix_, value_);	\
+	STORE_SYSFS_SYMBOL_FIELD2(field_, prefix_, __VA_ARGS__)
+
+#define STORE_SYSFS_SYMBOL_FIELD4(field_, prefix_, value_, ...)		\
+	STORE_SYSFS_SYMBOL_FIELD_ENTRY(field_, prefix_, value_);	\
+	STORE_SYSFS_SYMBOL_FIELD3(field_, prefix_, __VA_ARGS__)
+
+#define STORE_SYSFS_SYMBOL_FIELD5(field_, prefix_, value_, ...)		\
+	STORE_SYSFS_SYMBOL_FIELD_ENTRY(field_, prefix_, value_);	\
+	STORE_SYSFS_SYMBOL_FIELD4(field_, prefix_, __VA_ARGS__)
+
+#define STORE_SYSFS_SYMBOL_FIELD(field_, ...)			\
+	CONCATENATE(STORE_SYSFS_SYMBOL_FIELD,				\
+		COUNT_ARGS(__VA_ARGS__))(field_, gs_##field_, __VA_ARGS__)
+
+#define STORE_SYSFS_REGISTER(reg, str, ...)				\
+	static ssize_t store_##reg(struct device *device,		\
+	       struct device_attribute *attr, const char *buf, size_t size) \
+	{								\
+		const bool valid = gs_valid_##reg();			\
+		struct gs_##str value = valid ?			\
+			gs_read_##reg() : (struct gs_##str) { };	\
+		size_t n;						\
+		for_each_line(buf, n) {					\
+			__VA_ARGS__;					\
+		}							\
+		gs_write_##reg(value);					\
+		return size;						\
+	}
+
+#define SYSFS_STORE(reg, str, ...)					\
+	STORE_SYSFS_REGISTER(reg, str,				\
+		CONCATENATE(SYSFS_STATEMENT,				\
+			COUNT_ARGS(__VA_ARGS__))(STORE_, __VA_ARGS__))
+
 #define SYSFS_RO_REG(reg, str, ...)					\
 	SYSFS_SHOW(reg, str, __VA_ARGS__)				\
 	static DEVICE_ATTR(reg, S_IRUGO, show_##reg, NULL)
 
 #define SYSFS_RW_REG(reg, str, ...)					\
 	SYSFS_SHOW(reg, str, __VA_ARGS__)				\
-	static DEVICE_ATTR(reg, S_IRUGO, show_##reg, NULL)
+	SYSFS_STORE(reg, str, __VA_ARGS__)				\
+	static DEVICE_ATTR(reg, S_IRUGO | S_IWUSR, show_##reg, store_##reg)
 
 SYSFS_RW_REG(pmode, pmode,
 	SYSFS_DECNUM_FIELD(en1),
@@ -412,7 +547,7 @@ static struct attribute_group gs_registers_attribute_group = {
 
 static int __init gs_sysfs_init(void)
 {
-	struct device *gs_dev = gs_device();	/* FIXME: Is this method appropriate? */
+	struct device *gs_dev = gs_device_driver();	/* FIXME: Is this method appropriate? */
 	int err;
 
 	if (!gs_dev) {
Signature · 1 line
2.21.0
#115 lore raw

PATCH 93/120 fbdev: Add fb_warn_once() variant that only prints a warning once

In reply to Fredrik Noring

fb_warn_once() is a variant of fb_warn(), to print a warning only once.

---
 include/linux/fb.h | 2 ++
 1 file changed, 2 insertions(+)
include/linux/fb.h +2 -0
index 303771264644..19f5118e34ea 100644
@@ -818,6 +818,8 @@ extern int fb_find_mode(struct fb_var_screeninfo *var,
 	pr_notice("fb%d: " fmt, (fb_info)->node, ##__VA_ARGS__)
 #define fb_warn(fb_info, fmt, ...)					\
 	pr_warn("fb%d: " fmt, (fb_info)->node, ##__VA_ARGS__)
+#define fb_warn_once(fb_info, fmt, ...)					\
+	pr_warn_once("fb%d: " fmt, (fb_info)->node, ##__VA_ARGS__)
 #define fb_info(fb_info, fmt, ...)					\
 	pr_info("fb%d: " fmt, (fb_info)->node, ##__VA_ARGS__)
 #define fb_dbg(fb_info, fmt, ...)					\
Signature · 1 line
2.21.0
Reviewed-by on v1 93/120
#116 lore raw

Re: [PATCH 093/120] fbdev: Add fb_warn_once() variant that only prints a warning once

Quoted 13 lines · Fredrik Noring

fb_warn_once() is a variant of fb_warn(), to print a warning only once.

---
 include/linux/fb.h | 2 ++
 1 file changed, 2 insertions(+)
include/linux/fb.h +0 -0
index 303771264644..19f5118e34ea 100644

include/linux/fb.h extern int fb_find_mode(struct fb_var_screeninfo *var, · line 825

Quoted 8 lines
include/linux/fb.h +2 -0
@@ -818,6 +818,8 @@ extern int fb_find_mode(struct fb_var_screeninfo *var,
 	pr_notice("fb%d: " fmt, (fb_info)->node, ##__VA_ARGS__)
 #define fb_warn(fb_info, fmt, ...)					\
 	pr_warn("fb%d: " fmt, (fb_info)->node, ##__VA_ARGS__)
+#define fb_warn_once(fb_info, fmt, ...)					\
+	pr_warn_once("fb%d: " fmt, (fb_info)->node, ##__VA_ARGS__)
 #define fb_info(fb_info, fmt, ...)					\
 	pr_info("fb%d: " fmt, (fb_info)->node, ##__VA_ARGS__)
 #define fb_dbg(fb_info, fmt, ...)					\
#117 lore raw

PATCH 94/120 MIPS: PS2: FB: Frame buffer driver for the PlayStation 2

In reply to Fredrik Noring

The main limitation is the lack of mmap, since the Graphics Synthesizer has local frame buffer memory that is not directly accessible from the main bus. The GS has 4 MiB of local memory.

The console drawing primitives are synchronous to allow printk at any time. This is highly useful for debugging but it is not the fastest possible implementation. The console is nevertheless very fast and makes use of several hardware accelerated features of the Graphics Synthesizer.

The maximum practical resolution is 1920x1080p at 16 bits per pixel that requires 4147200 bytes of local memory, leaving 47104 bytes for a tiled font, which at 8x8 pixels and a minimum 4 bits indexed texture palette is at most 1464 characters. The indexed palette makes switching colours easy. &struct fb_tile_ops is accelerated with GS texture sprites that are fast (GS local copy) for the kernel via simple DMA GS commands via the GIF.

---
 drivers/video/fbdev/Kconfig    |  12 +
 drivers/video/fbdev/Makefile   |   1 +
 drivers/video/fbdev/ps2fb.c    | 533 +++++++++++++++++++++++++++++++++
 include/linux/console_struct.h |   2 +
 include/uapi/linux/fb.h        |   1 +
 5 files changed, 549 insertions(+)
 create mode 100644 drivers/video/fbdev/ps2fb.c
drivers/video/fbdev/Kconfig +12 -0
index 6b2de93bd302..cc93cbd67b01 100644
@@ -1999,6 +1999,18 @@ config FB_IBM_GXT4500
 	  doesn't use Geometry Engine GT1000. This driver also supports
 	  AGP Fire GL2/3/4 cards on x86.
 
+# FIXME FB_SYS_*
+config FB_PS2
+	tristate "Frame buffer driver for Sony Playstation 2"
+	depends on FB && SONY_PS2
+	select PS2_GS
+	select FB_TILEBLITTING
+	default y
+	help
+	  Frame buffer driver for the Sony Playstation 2 Graphics Synthesizer.
+	  Memory mapping is not supported since the frame buffer is local to
+	  the GS.
+
 config FB_PS3
 	tristate "PS3 GPU framebuffer driver"
 	depends on FB && PS3_PS3AV
drivers/video/fbdev/Makefile +1 -0
index 7dc4861a93e6..1e55fa8ca4af 100644
@@ -105,6 +105,7 @@ obj-$(CONFIG_FB_S3C2410)	  += s3c2410fb.o
 obj-$(CONFIG_FB_FSL_DIU)	  += fsl-diu-fb.o
 obj-$(CONFIG_FB_COBALT)           += cobalt_lcdfb.o
 obj-$(CONFIG_FB_IBM_GXT4500)	  += gxt4500.o
+obj-$(CONFIG_FB_PS2)		  += ps2fb.o
 obj-$(CONFIG_FB_PS3)		  += ps3fb.o
 obj-$(CONFIG_FB_SM501)            += sm501fb.o
 obj-$(CONFIG_FB_UDL)		  += udlfb.o
drivers/video/fbdev/ps2fb.c new file +533 -0

533 lines not on this page · raw message

include/linux/console_struct.h +2 -0
index 24d4c16e3ae0..cb562672cc3a 100644
@@ -13,9 +13,11 @@
 #ifndef _LINUX_CONSOLE_STRUCT_H
 #define _LINUX_CONSOLE_STRUCT_H
 
+#include <linux/tty.h>
 #include <linux/wait.h>
 #include <linux/vt.h>
 #include <linux/workqueue.h>
+#include <uapi/linux/kd.h>
 
 struct uni_pagedir;
 struct uni_screen;
include/uapi/linux/fb.h +1 -0
index b6aac7ee1f67..38d88eebf651 100644
@@ -149,6 +149,7 @@
 #define FB_ACCEL_SUPERSAVAGE    0x8c    /* S3 Supersavage               */
 #define FB_ACCEL_PROSAVAGE_DDR  0x8d	/* S3 ProSavage DDR             */
 #define FB_ACCEL_PROSAVAGE_DDRK 0x8e	/* S3 ProSavage DDR-K           */
+#define FB_ACCEL_PLAYSTATION_2  0x8f	/* PlayStation 2                */
 
 #define FB_ACCEL_PUV3_UNIGFX	0xa0	/* PKUnity-v3 Unigfx		*/
 
Signature · 1 line
2.21.0
maintainer
#118 lore raw

Re: [PATCH 094/120] MIPS: PS2: FB: Frame buffer driver for the PlayStation 2

Quoted 10 lines · Fredrik Noring

The main limitation is the lack of mmap, since the Graphics Synthesizer has local frame buffer memory that is not directly accessible from the main bus. The GS has 4 MiB of local memory.

The console drawing primitives are synchronous to allow printk at any time. This is highly useful for debugging but it is not the fastest possible implementation. The console is nevertheless very fast and makes use of several hardware accelerated features of the Graphics Synthesizer.

Quoted 8 lines

The maximum practical resolution is 1920x1080p at 16 bits per pixel that requires 4147200 bytes of local memory, leaving 47104 bytes for a tiled font, which at 8x8 pixels and a minimum 4 bits indexed texture palette is at most 1464 characters. The indexed palette makes switching colours easy. &struct fb_tile_ops is accelerated with GS texture sprites that are fast (GS local copy) for the kernel via simple DMA GS commands via the GIF.

Hi Fredik,

According to kernel policy[1] no more new FBDev driver would be accepted.

Please refactor it to DRM.

Thanks.

[1] http://lkml.iu.edu/hypermail/linux/kernel/1509.3/00253.html

--

Jiaxun Yang

Quoted 611 lines
---
  drivers/video/fbdev/Kconfig    |  12 +
  drivers/video/fbdev/Makefile   |   1 +
  drivers/video/fbdev/ps2fb.c    | 533 +++++++++++++++++++++++++++++++++
  include/linux/console_struct.h |   2 +
  include/uapi/linux/fb.h        |   1 +
  5 files changed, 549 insertions(+)

create mode 100644 drivers/video/fbdev/ps2fb.c

drivers/video/fbdev/Kconfig +12 -0
index 6b2de93bd302..cc93cbd67b01 100644
@@ -1999,6 +1999,18 @@ config FB_IBM_GXT4500
  	  doesn't use Geometry Engine GT1000. This driver also supports
  	  AGP Fire GL2/3/4 cards on x86.
 
+# FIXME FB_SYS_*
+config FB_PS2
+	tristate "Frame buffer driver for Sony Playstation 2"
+	depends on FB && SONY_PS2
+	select PS2_GS
+	select FB_TILEBLITTING
+	default y
+	help
+	  Frame buffer driver for the Sony Playstation 2 Graphics Synthesizer.
+	  Memory mapping is not supported since the frame buffer is local to
+	  the GS.
+
  config FB_PS3
  	tristate "PS3 GPU framebuffer driver"
  	depends on FB && PS3_PS3AV
drivers/video/fbdev/Makefile +1 -0
index 7dc4861a93e6..1e55fa8ca4af 100644
@@ -105,6 +105,7 @@ obj-$(CONFIG_FB_S3C2410)	  += s3c2410fb.o
  obj-$(CONFIG_FB_FSL_DIU)	  += fsl-diu-fb.o
  obj-$(CONFIG_FB_COBALT)           += cobalt_lcdfb.o
  obj-$(CONFIG_FB_IBM_GXT4500)	  += gxt4500.o
+obj-$(CONFIG_FB_PS2)		  += ps2fb.o
  obj-$(CONFIG_FB_PS3)		  += ps3fb.o
  obj-$(CONFIG_FB_SM501)            += sm501fb.o
  obj-$(CONFIG_FB_UDL)		  += udlfb.o
drivers/video/fbdev/ps2fb.c new file +533 -0
new file mode 100644
index 000000000000..7bfbc3c2aa4d
@@ -0,0 +1,533 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 frame buffer driver
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+/**
+ * DOC: The PlayStation 2 frame buffer console
+ *
+ * The frame buffer supports a tiled frame buffer console. The main limitation
+ * is the lack of memory mapping (mmap), since the Graphics Synthesizer has
+ * local frame buffer memory that is not directly accessible from the main bus.
+ * The GS has 4 MiB of local memory.
+ *
+ * The console drawing primitives are synchronous to allow printk at any time.
+ * This is highly useful for debugging but it is not the fastest possible
+ * implementation. The console is nevertheless very fast and makes use of
+ * several hardware accelerated features of the Graphics Synthesizer.
+ *
+ * The maximum practical resolution is 1920x1080p at 16 bits per pixel that
+ * requires 4147200 bytes of local memory, leaving 47104 bytes for a tiled
+ * font, which at 8x8 pixels and a minimum 4 bits indexed texture palette is
+ * at most 1464 characters. The indexed palette makes switching colours easy.
+ * &struct fb_tile_ops is accelerated with GS texture sprites that are fast
+ * (GS local copy) for the kernel via simple DMA GS commands via the GIF.
+ *
+ * The local memory is organised as follows: first comes the display buffer,
+ * then one block of a palette, and finally the font installed as a texture.
+ *
+ * All frame buffer transmissions are done by DMA via GIF PATH3.
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+
+#include <asm/io.h>
+
+#include <asm/mach-ps2/dmac.h>
+#include <asm/mach-ps2/gif.h>
+#include <asm/mach-ps2/gs.h>
+#include <asm/mach-ps2/gs-registers.h>
+
+#include <uapi/asm/gif.h>
+#include <uapi/asm/gs.h>
+
+#define DEVICE_NAME "ps2fb"
+
+#define PALETTE_BLOCK_COUNT 1	/* One block is used for the indexed colors */
+
+/* Module parameters */
+static char *mode_option;
+
+union package {
+	union gif_data gif;
+	struct dma_tag dma;
+};
+
+/**
+ * struct tile_texture - texture representing a tile
+ * @tbp: texture base pointer
+ * @u: texel u coordinate (x coordinate)
+ * @v: texel v coordinate (y coordinate)
+ */
+struct tile_texture {
+	u32 tbp;
+	u32 u;
+	u32 v;
+};
+
+/**
+ * struct console_buffer - console buffer
+ * @block_count: number of frame buffer blocks
+ * @bg: background color index
+ * @fg: foreground color index
+ * @tile: tile dimensions
+ * @tile.width: width in pixels
+ * @tile.height: height in pixels
+ * @tile.width2: least width in pixels, power of 2
+ * @tile.height2: least height in pixels, power of 2
+ * @tile.block: tiles are stored as textures in the PSMT4 pixel storage format
+ * 	with both cols and rows as powers of 2
+ * @tile.block.cols: tile columns per GS block
+ * @tile.block.rows: tile rows per GS block
+ */
+struct console_buffer {
+	u32 block_count;
+
+	u32 bg;
+	u32 fg;
+
+	struct cb_tile {
+		u32 width;
+		u32 height;
+
+		u32 width2;
+		u32 height2;
+
+		struct {
+			u32 cols;
+			u32 rows;
+		} block;
+	} tile;
+};
+
+/**
+ * struct ps2fb_par - driver specific structure
+ * @lock: spin lock to be taken for all structure operations
+ * @cb: console buffer definition
+ * @package: tags and datafor the GIF
+ * @package.capacity: maximum number of GIF packages in 16-byte unit
+ * @package.buffer: DMA buffer for GIF packages
+ */
+struct ps2fb_par {
+	spinlock_t lock;
+
+	struct console_buffer cb;
+
+	struct {
+		size_t capacity;
+		union package *buffer;
+	} package;
+};
+
+/**
+ * texture_least_power_of_2 - round up to a power of 2, not less than 8
+ * @n: integer to round up
+ *
+ * Return: least integer that is a power of 2 and not less than @n or 8
+ */
+static u32 texture_least_power_of_2(u32 n)
+{
+	return max(1 << get_count_order(n), 8);
+}
+
+/**
+ * cb_tile - create a console buffer tile object
+ * @width: width of tile in pixels
+ * @height: height of tile in pixels
+ *
+ * Return: a console buffer tile object with the given width and height
+ */
+static struct cb_tile cb_tile(u32 width, u32 height)
+{
+	const u32 width2 = texture_least_power_of_2(width);
+	const u32 height2 = texture_least_power_of_2(height);
+
+	return (struct cb_tile) {
+		.width = width,
+		.height = height,
+
+		.width2 = width2,
+		.height2 = height2,
+
+		.block = {
+			.cols = GS_PSMT4_BLOCK_WIDTH / width2,
+			.rows = GS_PSMT4_BLOCK_HEIGHT / height2,
+		},
+	};
+}
+
+/**
+ * display_buffer_size - display buffer size for a given video resolution
+ *
+ * This calculation is a lower bound estimate. A precise calculation would have
+ * to take memory pages, blocks and column arrangements into account. To choose
+ * the appropriate standard video mode such details can be disregarded, though.
+ *
+ * Return: the size in bytes of the display buffer
+ */
+static u32 display_buffer_size(const u32 xres_virtual, const u32 yres_virtual,
+      const u32 bits_per_pixel)
+{
+	return (xres_virtual * yres_virtual * bits_per_pixel) / 8;
+}
+
+/**
+ * ps2fb_cb_get_tilemax - maximum number of tiles
+ * @info: frame buffer info object
+ *
+ * Return: the maximum number of tiles
+ */
+static int ps2fb_cb_get_tilemax(struct fb_info *info)
+{
+	const struct ps2fb_par *par = info->par;
+	const u32 block_tile_count =
+		par->cb.tile.block.cols *
+		par->cb.tile.block.rows;
+	const s32 blocks_available =
+		GS_BLOCK_COUNT - par->cb.block_count - PALETTE_BLOCK_COUNT;
+
+	return blocks_available > 0 ? blocks_available * block_tile_count : 0;
+}
+
+/**
+ * bits_per_pixel_fits - does the given resolution fit the given buffer size?
+ * @xres_virtual: virtual x resolution in pixels
+ * @yres_virtual: virtual y resolution in pixels
+ * @bits_per_pixel: number of bits per pixel
+ * @buffer_size: size in bytes of display buffer
+ *
+ * The size calculation is approximate, but accurate enough for the standard
+ * video modes.
+ *
+ * Return: %true if the resolution fits the given buffer size, otherwise %false
+ */
+static bool bits_per_pixel_fits(const u32 xres_virtual, const u32 yres_virtual,
+      const int bits_per_pixel, const size_t buffer_size)
+{
+	return display_buffer_size(xres_virtual, yres_virtual,
+		bits_per_pixel) <= buffer_size;
+}
+
+/**
+ * default_bits_per_pixel - choose either 16 or 32 bits per pixel
+ * @xres_virtual: virtual x resolution in pixels
+ * @yres_virtual: virtual y resolution in pixels
+ * @buffer_size: size in bytes of display buffer
+ *
+ * 32 bits per pixel is returned unless this does not fit the given buffer size.
+ *
+ * The size calculation is approximate, but accurate enough for the standard
+ * video modes.
+ *
+ * Return: 16 or 32 bits per pixel
+ */
+static int default_bits_per_pixel(
+	const u32 xres_virtual, const u32 yres_virtual,
+	const size_t buffer_size)
+{
+	return bits_per_pixel_fits(xres_virtual, yres_virtual,
+		32, buffer_size) ? 32 : 16;
+}
+
+/**
+ * filled_var_videomode - is the screen info video mode filled in?
+ * @var: screen info object to check
+ *
+ * Return: %true if the video mode is filled in, otherwise %false
+ */
+static bool filled_var_videomode(const struct fb_var_screeninfo *var)
+{
+	return var->xres > 0 && var->hsync_len > 0 &&
+	       var->yres > 0 && var->vsync_len > 0 && var->pixclock > 0;
+}
+
+static int ps2fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+	/* Check whether video mode defaults are needed. */
+	if (!filled_var_videomode(var)) {
+		const struct fb_videomode *vm =
+			fb_find_best_mode(var, &info->modelist);
+
+		if (!vm)
+			return -EINVAL;
+
+		fb_videomode_to_var(var, vm);
+	}
+
+        /* GS video register resolution is limited to 2048. */
+        if (var->xres < 1 || 2048 < var->xres ||
+	    var->yres < 1 || 2048 < var->yres)
+		return -EINVAL;
+
+	var->xres_virtual = var->xres;
+	var->yres_virtual = var->yres;
+	var->xoffset = 0;
+	var->yoffset = 0;
+
+        /* Check bits per pixel. */
+        if (!var->bits_per_pixel)
+		var->bits_per_pixel = default_bits_per_pixel(
+		     var->xres_virtual, var->yres_virtual, info->fix.smem_len);
+	else if (var->bits_per_pixel != 16 &&
+		 var->bits_per_pixel != 32)
+		return -EINVAL;
+        if (!bits_per_pixel_fits(var->xres_virtual, var->yres_virtual,
+			var->bits_per_pixel, info->fix.smem_len))
+		var->bits_per_pixel = default_bits_per_pixel(
+		     var->xres_virtual, var->yres_virtual, info->fix.smem_len);
+        if (!bits_per_pixel_fits(var->xres_virtual, var->yres_virtual,
+			var->bits_per_pixel, info->fix.smem_len))
+		return -ENOMEM;
+	if (var->bits_per_pixel == 16) {
+		var->red    = (struct fb_bitfield){ .offset =  0, .length = 5 };
+		var->green  = (struct fb_bitfield){ .offset =  5, .length = 5 };
+		var->blue   = (struct fb_bitfield){ .offset = 10, .length = 5 };
+		var->transp = (struct fb_bitfield){ .offset = 15, .length = 1 };
+	} else if (var->bits_per_pixel == 32) {
+		var->red    = (struct fb_bitfield){ .offset =  0, .length = 8 };
+		var->green  = (struct fb_bitfield){ .offset =  8, .length = 8 };
+		var->blue   = (struct fb_bitfield){ .offset = 16, .length = 8 };
+		var->transp = (struct fb_bitfield){ .offset = 24, .length = 8 };
+	} else
+		return -EINVAL;		/* Unsupported bits per pixel. */
+
+        /* Screen rotations are not supported. */
+	if (var->rotate)
+		return -EINVAL;
+
+        return 0;
+}
+
+static int ps2fb_cb_check_var(
+	struct fb_var_screeninfo *var, struct fb_info *info)
+{
+	struct ps2fb_par *par = info->par;
+	unsigned long flags;
+	int err;
+
+	spin_lock_irqsave(&par->lock, flags);
+	err = ps2fb_check_var(var, info);
+	spin_unlock_irqrestore(&par->lock, flags);
+
+	if (!err && info->tileops)
+		if (info->tileops->fb_get_tilemax(info) < 256)
+			err = -ENOMEM;
+
+	return err;
+}
+
+static u32 block_dimensions(u32 dim, u32 alignment)
+{
+	u32 mask = 0;
+	u32 d;
+
+	for (d = 1; d <= dim; d++)
+		if (d % alignment == 0)
+			mask |= 1 << (d - 1);
+
+	return mask;
+}
+
+static int init_console_buffer(struct platform_device *pdev,
+	struct fb_info *info)
+{
+	static struct fb_ops fbops = {
+		.owner		= THIS_MODULE,
+		.fb_check_var	= ps2fb_cb_check_var,
+	};
+
+	static struct fb_tile_ops tileops = {
+		.fb_get_tilemax = ps2fb_cb_get_tilemax
+	};
+
+	struct ps2fb_par *par = info->par;
+
+	fb_info(info, "Graphics Synthesizer console frame buffer device\n");
+
+	info->screen_size = 0;
+	info->screen_base = NULL;	/* mmap is unsupported by hardware */
+
+	info->fix.smem_start = 0;	/* GS frame buffer is local memory */
+	info->fix.smem_len = GS_MEMORY_SIZE;
+
+	info->fbops = &fbops;
+	info->flags = FBINFO_DEFAULT |
+		      FBINFO_READS_FAST;
+
+	info->flags |= FBINFO_MISC_TILEBLITTING;
+	info->tileops = &tileops;
+
+	/*
+	 * BITBLTBUF for pixel format CT32 requires divisibility by 2,
+	 * and CT16 requires divisibility by 4. So 4 is a safe choice.
+	 */
+	info->pixmap.blit_x = block_dimensions(GS_PSMT4_BLOCK_WIDTH, 4);
+	info->pixmap.blit_y = block_dimensions(GS_PSMT4_BLOCK_HEIGHT, 1);
+
+	/* 8x8 default font tile size for fb_get_tilemax */
+	par->cb.tile = cb_tile(8, 8);
+
+	return 0;
+}
+
+static int ps2fb_probe(struct platform_device *pdev)
+{
+	struct ps2fb_par *par;
+	struct fb_info *info;
+	int err;
+
+	info = framebuffer_alloc(sizeof(*par), &pdev->dev);
+	if (info == NULL) {
+		dev_err(&pdev->dev, "framebuffer_alloc failed\n");
+		err = -ENOMEM;
+		goto err_framebuffer_alloc;
+	}
+
+	par = info->par;
+
+	spin_lock_init(&par->lock);
+
+	par->package.buffer = (union package *)__get_free_page(GFP_DMA);
+	if (!par->package.buffer) {
+		dev_err(&pdev->dev, "Failed to allocate package buffer\n");
+		err = -ENOMEM;
+		goto err_package_buffer;
+	}
+	par->package.capacity = PAGE_SIZE / sizeof(union package);
+
+	strlcpy(info->fix.id, "PS2 GS", ARRAY_SIZE(info->fix.id));
+	info->fix.accel = FB_ACCEL_PLAYSTATION_2;
+
+	err = init_console_buffer(pdev, info);
+	if (err < 0)
+		goto err_init_buffer;
+
+	info->mode = &par->mode;
+
+	if (register_framebuffer(info) < 0) {
+		fb_err(info, "register_framebuffer failed\n");
+		err = -EINVAL;
+		goto err_register_framebuffer;
+	}
+
+	platform_set_drvdata(pdev, info);
+
+	return 0;
+
+err_register_framebuffer:
+err_init_buffer:
+	free_page((unsigned long)par->package.buffer);
+err_package_buffer:
+	framebuffer_release(info);
+err_framebuffer_alloc:
+	return err;
+}
+
+static int ps2fb_remove(struct platform_device *pdev)
+{
+	struct fb_info *info = platform_get_drvdata(pdev);
+	struct ps2fb_par *par = info->par;
+	int err = 0;
+
+	if (info != NULL) {
+		unregister_framebuffer(info);
+		fb_dealloc_cmap(&info->cmap);
+
+		framebuffer_release(info);
+	}
+
+	if (!gif_wait()) {
+		fb_err(info, "Failed to complete GIF DMA transfer\n");
+		err = -EBUSY;
+	}
+	free_page((unsigned long)par->package.buffer);
+
+	return err;
+}
+
+static struct platform_driver ps2fb_driver = {
+	.probe		= ps2fb_probe,
+	.remove		= ps2fb_remove,
+	.driver = {
+		.name	= DEVICE_NAME,
+	},
+};
+
+static struct platform_device *ps2fb_device;
+
+static int __init ps2fb_init(void)
+{
+	int err;
+
+#ifndef MODULE
+	char *options = NULL;
+	char *this_opt;
+
+	if (fb_get_options(DEVICE_NAME, &options))
+		return -ENODEV;
+	if (!options || !*options)
+		goto no_options;
+
+	while ((this_opt = strsep(&options, ",")) != NULL) {
+		if (!*this_opt)
+			continue;
+
+		if (!strncmp(this_opt, "mode_option:", 12))
+			mode_option = &this_opt[12];
+		else if ('0' <= this_opt[0] && this_opt[0] <= '9')
+			mode_option = this_opt;
+		else
+			pr_warn(DEVICE_NAME ": Unrecognized option \"%s\"\n",
+				this_opt);
+	}
+
+no_options:
+#endif /* !MODULE */
+
+	/* Default to a suitable PAL or NTSC broadcast mode. */
+	if (!mode_option)
+		mode_option = gs_region_pal() ? "576x460i@50" : "576x384i@60";
+
+	ps2fb_device = platform_device_alloc("ps2fb", 0);
+	if (!ps2fb_device)
+		return -ENOMEM;
+
+	err = platform_device_add(ps2fb_device);
+	if (err < 0) {
+		platform_device_put(ps2fb_device);
+		return err;
+	}
+
+	return platform_driver_register(&ps2fb_driver);
+}
+
+static void __exit ps2fb_exit(void)
+{
+	platform_driver_unregister(&ps2fb_driver);
+	platform_device_unregister(ps2fb_device);
+}
+
+module_init(ps2fb_init);
+module_exit(ps2fb_exit);
+
+module_param(mode_option, charp, 0);
+MODULE_PARM_DESC(mode_option,
+	"Specify initial video mode as \"<xres>x<yres>[-<bpp>][@<refresh>]\"");
+
+MODULE_DESCRIPTION("PlayStation 2 frame buffer driver");
+MODULE_AUTHOR("Fredrik Noring");
+MODULE_LICENSE("GPL");
include/linux/console_struct.h +2 -0
index 24d4c16e3ae0..cb562672cc3a 100644
@@ -13,9 +13,11 @@
  #ifndef _LINUX_CONSOLE_STRUCT_H
  #define _LINUX_CONSOLE_STRUCT_H
 
+#include <linux/tty.h>
  #include <linux/wait.h>
  #include <linux/vt.h>
  #include <linux/workqueue.h>
+#include <uapi/linux/kd.h>
 
  struct uni_pagedir;
  struct uni_screen;
include/uapi/linux/fb.h +1 -0
index b6aac7ee1f67..38d88eebf651 100644
@@ -149,6 +149,7 @@
  #define FB_ACCEL_SUPERSAVAGE    0x8c    /* S3 Supersavage               */
  #define FB_ACCEL_PROSAVAGE_DDR  0x8d	/* S3 ProSavage DDR             */
  #define FB_ACCEL_PROSAVAGE_DDRK 0x8e	/* S3 ProSavage DDR-K           */
+#define FB_ACCEL_PLAYSTATION_2  0x8f	/* PlayStation 2                */
 
  #define FB_ACCEL_PUV3_UNIGFX	0xa0	/* PKUnity-v3 Unigfx		*/
 
#119 lore raw

Re: [PATCH 094/120] MIPS: PS2: FB: Frame buffer driver for the PlayStation 2

Hi Jiaxun Yang,

Quoted 3 lines

According to kernel policy[1] no more new FBDev driver would be accepted.

Please refactor it to DRM.

I had the impression that the DRM does not support a kernel console?

Standard hardware, on its part, does not have a serial port or similar alternatives that could easily function as a console, unfortunately.

Fredrik

#120 lore raw

Re: [PATCH 094/120] MIPS: PS2: FB: Frame buffer driver for the PlayStation 2

Hi,

Quoted 7 lines · Fredrik Noring

Hi Jiaxun Yang,

Quoted 3 lines

According to kernel policy[1] no more new FBDev driver would be accepted.

Please refactor it to DRM.

I had the impression that the DRM does not support a kernel console?

DRM drivers do provide FB console.

A.

#121 lore raw

Re: [PATCH 094/120] MIPS: PS2: FB: Frame buffer driver for the PlayStation 2

Hi Aaro,

Quoted 1 line

DRM drivers do provide FB console.

Great, thanks! I will look into that.

Fredrik

maintainer
#122 lore raw

Re: [PATCH 094/120] MIPS: PS2: FB: Frame buffer driver for the PlayStation 2

In reply to Fredrik Noring
Quoted 6 lines · Fredrik Noring

Hi Jiaxun Yang,

Quoted 3 lines

According to kernel policy[1] no more new FBDev driver would be accepted.

Please refactor it to DRM.

I had the impression that the DRM does not support a kernel console?

Hi Fredik,

DRM do have fb_helper providing FB console.

I think refactor your current FB driver with drm gem cma helper won't be very hard.

Quoted 3 lines

Standard hardware, on its part, does not have a serial port or similar alternatives that could easily function as a console, unfortunately.

Ahh, the guy runs Linux on that gaming console is really cool! How do you debug your code?

Thanks.

--
Jiaxun Yang

#123 lore raw

Re: [PATCH 094/120] MIPS: PS2: FB: Frame buffer driver for the PlayStation 2

Quoted 4 lines

DRM do have fb_helper providing FB console.

I think refactor your current FB driver with drm gem cma helper won't be very hard.

Thanks! I will update that for v2.

Quoted 2 lines

Ahh, the guy runs Linux on that gaming console is really cool! How do you debug your code?

The video screen is used for the earliest stages, even before the kernel is decompressed in arch/mips/boot/compressed/decompress.c. I have a simple patch that sets up the Graphics Synthesizer and uses DMA to render a trivial frame buffer console, displaying

zimage at: 00803BE0 00BDDE8C
Uncompressing Linux at load address 80010000 Now, booting the kernel...

and so on as a video image. This is functionally equivalent to the serial port drivers for prom_putchar() that other MIPS platforms have, and essential to debug early boot and kexec.

[ I assume this code will not be accepted, since the approach is rather nontraditional, which is why I didn't include it in the patch set. ]

Somewhat later the fbcon driver takes over the video screen console. Then USB is configured with a wifi device, an ssh daemon is started etc. for remote login and scripting.

I have QEMU patches for R5900 user mode emulation, that can be used for development and debugging. I mainly use it to compile Gentoo for the PlayStation 2.

Fredrik

#124 lore raw

Re: [PATCH 094/120] MIPS: PS2: FB: Frame buffer driver for the PlayStation 2

Quoted 9 lines · Fredrik Noring

The video screen is used for the earliest stages, even before the kernel is decompressed in arch/mips/boot/compressed/decompress.c. I have a simple patch that sets up the Graphics Synthesizer and uses DMA to render a trivial frame buffer console, displaying

zimage at: 00803BE0 00BDDE8C
Uncompressing Linux at load address 80010000 Now, booting the kernel...

Quoted 6 lines

and so on as a video image. This is functionally equivalent to the serial port drivers for prom_putchar() that other MIPS platforms have, and essential to debug early boot and kexec.

[ I assume this code will not be accepted, since the approach is rather nontraditional, which is why I didn't include it in the patch set. ]

Hmm, why do you think this would be unacceptable? Do you expect your
code to cause unjustified maintenance burden once integrated?

Maciej

#125 lore raw

Re: [PATCH 094/120] MIPS: PS2: FB: Frame buffer driver for the PlayStation 2

Quoted 2 lines

Hmm, why do you think this would be unacceptable? Do you expect your
code to cause unjustified maintenance burden once integrated?

I suppose it can be implemented quite nicely, with a bit of effort. Steps to be taken:

1. A font needs to be linked with the early boot code. I'm using acorndata_8x8 from lib/fonts/font_acorn_8x8.c, but any font would do.

2. A single, or small set of, video resolutions need to be selected. I chose 1920x1080p and wrote those values to the Graphics Synthesizer video clock registers. A complete video mode line parser seems excessive, unless that code can be shared with the boot code somehow.

3. Implement clear_screen() and plot(x, y) for a single pixel using DMA commands to the Graphics Synthesizer. [ Clearing the screen can be done by plotting the whole screen, but it was somewhat slow. ]

4. Implement prom_putchar() using plot() for the given font.

Steps (1) and (4) could be generic code.

Fredrik

#126 lore raw

Re: [PATCH 094/120] MIPS: PS2: FB: Frame buffer driver for the PlayStation 2

In reply to Jiaxun Yang
Quoted 3 lines · Jiaxun Yang

According to kernel policy[1] no more new FBDev driver would be accepted.

Please refactor it to DRM.

I obtained a functional DRM driver a while back, but unfortunately it's a poor fit on the hardware, for the following reasons, in brief:

The DRM subsystem (in general) breaks user-space in significant ways:

- fbset command timing mode settings are ignored;
- attempts to set modes with /sys/class/graphics/fb0/mode are ignored;
- /sys/class/graphics/fb0/modes outputs incorrect refresh rates;
- relevant modes are missing in /sys/class/graphics/fb0/modes;
- the standard DRM video modes seem to mix up the sync and front porch timings in struct drm_display_mode but this remains to be confirmed.

DRM seems to rely heavily on EDID to negotiate a video resolution, but EDID isn't available with vintage analogue display hardware.

The Graphics Synthesizer (GS) hardware is essentially a serial device that accepts sequences of commands with data via DMA. Notably, it cannot be memory mapped.

Can the GS be modelled, in a reasonable way, on existing kernel video interfaces, when taking memory efficiency and performance into account?

For anyone who is interested:

In addition to this patch series and its documentation references, some background information and details on the Graphics Synthesizer are available here:

https://github.com/frno7/linux/issues/10

Fredrik