Linux for the PlayStation 2
Subsystems (8)
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
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -115,6 +115,8 @@ search_module_dbetables(unsigned long addr)#define MODULE_PROC_FAMILY "R5432 "#elif definedCONFIG_CPU_R5500 #define MODULE_PROC_FAMILY "R5500 " +#elif definedCONFIG_CPU_R5900 +#define MODULE_PROC_FAMILY "R5900 "#elif definedCONFIG_CPU_NEVADA #define MODULE_PROC_FAMILY "NEVADA "#elif definedCONFIG_CPU_R8000
arch/mips/kernel/cpu-probe.c +8 -0
@@ -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
@@ -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
@@ -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
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@@ -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
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_TX49has been misplaced, 18 years ago with
commitb8c80cd25ffe("Add missing PrId values for TX39 / TX 49 CPUs."), however I suggest thatPRID_IMP_R5900does not make things worse. I.e. please place it betweenPRID_IMP_NEVADAandPRID_IMP_RM9000.
Done, thanks!
Fredrik
PATCH 2/120 MIPS: R5900: Trap the RDHWR instruction as an SQ address exception
In reply to Fredrik NoringOn 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
@@ -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
@@ -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
@@ -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
Re: [PATCH 002/120] MIPS: R5900: Trap the RDHWR instruction as an SQ address exception
Quoted 2 lines · Fredrik Noring
CONFIG_DEFAULT_MMAP_MIN_ADDRmust not be less thanPAGE_SIZEto reliably trap and emulate RDHWR, so this is made aBUILD_BUG_ONfor 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@@ -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
Re: [PATCH 002/120] MIPS: R5900: Trap the RDHWR instruction as an SQ address exception
In reply to Fredrik NoringHi 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_ADDRmust not be less thanPAGE_SIZEto reliably trap and emulate RDHWR, so this is made aBUILD_BUG_ONfor 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.pdfSigned-off-by Fredrik Noring <[email protected]>--- 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@@ -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@@ -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@@ -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
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=f850220be6MIPS16 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=85bd816a60Shouldn'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
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
Re: [PATCH 002/120] MIPS: R5900: Trap the RDHWR instruction as an SQ address exception
In reply to Maciej W. RozyckiHi 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-tradlittlemipsDisassembly of section .text:
00000000 <.text>:
0: 7c03e83b rdhwr v1,$29 ... $ objdump -m mips:5900 -d rdhwr.ordhwr.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.
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 */
#defineMIPS_HWR_CPUNUM0 /* CPU number */
#defineMIPS_HWR_SYNCISTEP1 /* SYNCI step size */
#defineMIPS_HWR_CC2 /* Cycle counter */
#defineMIPS_HWR_CCRES3 /* Cycle counter resolution */
#defineMIPS_HWR_ULR29 /* UserLocal */
#defineMIPS_HWR_IMPL130 /* Implementation dependent */
#defineMIPS_HWR_IMPL231 /* 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
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 isSYNCI_Stepfor 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
Re: [PATCH 002/120] MIPS: R5900: Trap the RDHWR instruction as an SQ address exception
In reply to Maciej W. RozyckiQuoted 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
@@ -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 ))
PATCH 3/120 MIPS: R5900: Sign-extend o32 system call registers
In reply to Fredrik NoringThe 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
@@ -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
PATCH 4/120 MIPS: R5900: Reset bits 127..64 of GPRs in RESTORE_SOME
In reply to Fredrik NoringBits 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
@@ -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
PATCH 5/120 MIPS: R5900: Reset the funnel shift amount (SA) register in RESTORE_SOME
In reply to Fredrik NoringThe 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
@@ -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
PATCH 6/120 MIPS: R5900: Workaround for the short loop bug
In reply to Fredrik NoringThe 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
@@ -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
@@ -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
@@ -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
@@ -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
PATCH 7/120 MIPS: R5900: Add the SYNC.P instruction
In reply to Fredrik NoringThe 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
@@ -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
@@ -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
@@ -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
PATCH 8/120 MIPS: R5900: Add implicit SYNC.P to the UASM_i_M[FT]C0 macros
In reply to Fredrik NoringThe 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
@@ -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
PATCH 9/120 MIPS: R5900: Add mandatory SYNC.P to all M[FT]C0 instructions
In reply to Fredrik NoringThe 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
PATCH 10/120 MIPS: R5900: Workaround exception NOP execution bug (FLX05)
In reply to Fredrik NoringFor 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
@@ -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
@@ -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
@@ -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
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.pdfSigned-off-by Fredrik Noring <[email protected]>
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@@ -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.parch/mips/kernel/traps.c +6
-0@@ -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@@ -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);
PATCH 11/120 MIPS: R5900: Avoid pipeline hazard with the TLBP instruction
In reply to Fredrik NoringOn 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
@@ -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
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.pdfSigned-off-by Fredrik Noring <[email protected]>--- arch/mips/mm/tlbex.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)arch/mips/mm/tlbex.c +0
-0
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
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
PATCH 12/120 MIPS: R5900: Avoid pipeline hazards with the TLBW[IR] instructions
In reply to Fredrik NoringOn 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
@@ -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
PATCH 13/120 MIPS: R5900: Avoid pipeline hazard with the TLBR instruction
In reply to Fredrik NoringOn 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
@@ -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
@@ -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
PATCH 14/120 MIPS: R5900: Install final length of TLB refill handler rather than 256 bytes
In reply to Fredrik NoringThe 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
@@ -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
PATCH 15/120 MIPS: R5900: Verify that the TLB refill handler does not overflow
In reply to Fredrik NoringThe 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
@@ -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
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
@@ -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
PATCH 17/120 MIPS: R5900: Define CACHE instruction operation field encodings
In reply to Fredrik NoringThe 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
@@ -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
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
@@ -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
PATCH 19/120 MIPS: R5900: Use SYNC.L for data cache and SYNC.P for instruction cache
In reply to Fredrik NoringToshiba 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
@@ -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_sstatic 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
@@ -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
PATCH 20/120 MIPS: R5900: Define CP0.Config register fields
In reply to Fredrik NoringThe 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
@@ -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
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.pdfSigned-off-by Fredrik Noring <[email protected]>--- arch/mips/include/asm/mipsregs.h | 8 ++++++++ 1 file changed, 8 insertions(+)arch/mips/include/asm/mipsregs.h +1
-0@@ -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)
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
@@ -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
PATCH 22/120 MIPS: R5900: Support 64-bit inq() and outq() macros in 32-bit kernels
In reply to Fredrik NoringPlayStation 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
@@ -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
Jiaxun Yang
maintainerRe: [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.
Signed-off-by Fredrik Noring<[email protected]>---
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
PATCH 23/120 MIPS: R5900: Add MFSA and MTSA instructions for the special SA register
In reply to Fredrik NoringThe 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
@@ -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
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
@@ -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
@@ -0,0 +1 @@ +obj-y += memory.o
arch/mips/ps2/memory.c new file +27 -0
@@ -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
PATCH 25/120 MIPS: PS2: Define interrupt controller, DMA and timer IRQs
In reply to Fredrik NoringThe 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
@@ -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
@@ -1 +1,2 @@ +obj-y += irq.o obj-y += memory.o
arch/mips/ps2/irq.c new file +35 -0
@@ -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
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
@@ -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
@@ -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
@@ -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
PATCH 27/120 MIPS: PS2: DMAC: Define DMA controller registers
In reply to Fredrik NoringThe 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
@@ -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
PATCH 28/120 MIPS: PS2: DMAC: Define tag structures
In reply to Fredrik NoringThe 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
@@ -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
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
@@ -72,5 +72,6 @@#define IRQ_C0_IRQ7 55int __init intc_irq_init (void ); +int __init dmac_irq_init (void );#endif /* __ASM_MACH_PS2_IRQ_H */
arch/mips/ps2/Makefile +1 -0
@@ -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
@@ -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
PATCH 30/120 MIPS: PS2: Timer support
In reply to Fredrik NoringThe 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
@@ -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
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
@@ -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
@@ -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
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
@@ -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
@@ -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
PATCH 33/120 MIPS: PS2: SCMD: Read system machine name command
In reply to Fredrik NoringAn 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
@@ -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
PATCH 34/120 MIPS: PS2: SCMD: Read system command for the real-time clock (RTC)
In reply to Fredrik NoringThe 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
@@ -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
PATCH 35/120 MIPS: PS2: SCMD: Set system command for the real-time clock (RTC)
In reply to Fredrik NoringThe 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
@@ -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
PATCH 36/120 MIPS: PS2: ROM: Iterate over the files in a given ROM directory
In reply to Fredrik NoringAll 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
@@ -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
@@ -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
PATCH 37/120 MIPS: PS2: ROM: Find ROM files with a given name, if they exist
In reply to Fredrik Noringrom_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
@@ -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
PATCH 38/120 MIPS: PS2: ROM: Read data for a given ROM file name
In reply to Fredrik NoringReading 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
@@ -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
@@ -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
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.Signed-off-by Fredrik Noring <[email protected]>
[...]
Quoted 24 lines
index
12a57f24bd63..840d37a199d8100644arch/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 toDocumentation/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
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.Signed-off-by Fredrik Noring <[email protected]>[...]
Quoted 27 lines
index
12a57f24bd63..840d37a199d8100644arch/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
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
PATCH 39/120 MIPS: PS2: ROM: Read extended information for a given ROM file
In reply to Fredrik NoringThe 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
@@ -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
PATCH 40/120 MIPS: PS2: ROM: Read and decode the ROMVER file
In reply to Fredrik NoringThe 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
@@ -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
@@ -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
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
@@ -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
@@ -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
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
@@ -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
@@ -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
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
@@ -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
@@ -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
PATCH 44/120 MIPS: PS2: ROM: Sysfs module to inspect ROM files
In reply to Fredrik NoringThe 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
@@ -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
PATCH 45/120 MIPS: PS2: ROM: Provide extended file information via sysfs
In reply to Fredrik NoringThe 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
@@ -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
PATCH 46/120 MIPS: PS2: Identify the machine by model name
In reply to Fredrik NoringROM 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
@@ -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
PATCH 47/120 MIPS: PS2: Let the system type be Sony PlayStation 2
In reply to Fredrik NoringThe 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
@@ -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
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/cpuinfofile:# grep system' 'type /proc/cpuinfo system type : Sony PlayStation 2Signed-off-by Fredrik Noring <[email protected]>--- arch/mips/ps2/identify.c | 6 ++++++ 1 file changed, 6 insertions(+)arch/mips/ps2/identify.c +1
-0@@ -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 ();
PATCH 48/120 MIPS: Define and use cpu_relax_forever() for various halting loops
In reply to Fredrik NoringUsing 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
@@ -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
@@ -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 ) - asmvolatile ( -" .set mips32 \n" -" wait \n" -" .set mips0 \n" ); +cpu_relax_forever (); }void __init board_setup (void )
arch/mips/alchemy/board-xxs1500.c +2 -5
@@ -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 ) - asmvolatile ( -" .set mips32 \n" -" wait \n" -" .set mips0 \n" ); +cpu_relax_forever (); }void __init board_setup (void )
arch/mips/alchemy/devboards/platform.c +2 -2
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
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
@@ -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
@@ -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
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
@@ -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
@@ -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
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
@@ -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
@@ -0,0 +1 @@ +obj-m += iop-registers.o
drivers/ps2/iop-registers.c new file +39 -0
@@ -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 (®_lock, flags); +outl (inl (IOP_DMA_DPCR2 ) | mask,IOP_DMA_DPCR2 ); +spin_unlock_irqrestore (®_lock, flags); +} +EXPORT_SYMBOL_GPL (iop_set_dma_dpcr2); + +void iop_clr_dma_dpcr2 (const u32 mask) +{ +unsigned long flags; + +spin_lock_irqsave (®_lock, flags); +outl (inl (IOP_DMA_DPCR2 ) & ~mask,IOP_DMA_DPCR2 ); +spin_unlock_irqrestore (®_lock, flags); +} +EXPORT_SYMBOL_GPL (iop_clr_dma_dpcr2); + +MODULE_LICENSE ("GPL" );
Signature · 1 line
2.21.0
PATCH 52/120 MIPS: PS2: SIF: Sub-system interface reset of the I/O processor (IOP)
In reply to Fredrik NoringThe 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
@@ -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
@@ -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
PATCH 53/120 MIPS: PS2: IOP: Define error numbers, descriptions and errno mapping
In reply to Fredrik NoringIOP 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
@@ -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
@@ -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
PATCH 54/120 MIPS: PS2: SIF: SIF register write command support
In reply to Fredrik NoringThe 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
@@ -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
PATCH 55/120 MIPS: PS2: SIF: Respond to remote procedure call (RPC) bind command
In reply to Fredrik NoringThe 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
@@ -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
PATCH 56/120 MIPS: PS2: SIF: Respond to RPC bind end command
In reply to Fredrik NoringThe 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
@@ -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
PATCH 57/120 MIPS: PS2: SIF: Reset the SIF0 (sub-to-main) DMA controller
In reply to Fredrik NoringPut 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
@@ -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
PATCH 58/120 MIPS: PS2: SIF: Handle SIF0 (sub-to-main) RPCs via interrupts
In reply to Fredrik NoringThe 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
@@ -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
PATCH 59/120 MIPS: PS2: SIF: Enable the IOP to issue SIF commands
In reply to Fredrik NoringSend 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
@@ -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
PATCH 60/120 MIPS: PS2: SIF: Enable the IOP to issue SIF RPCs
In reply to Fredrik NoringSend 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
@@ -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
PATCH 61/120 MIPS: PS2: SIF: sif_rpc_bind() to request an RPC server connection
In reply to Fredrik NoringThis 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
@@ -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
@@ -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
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
@@ -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
@@ -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
PATCH 63/120 MIPS: PS2: SIF: sif_rpc() to issue a remote procedure call
In reply to Fredrik NoringThis 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
@@ -88,4 +88,7 @@ struct sif_rpc_clientint 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
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
@@ -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
@@ -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
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
@@ -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
@@ -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
PATCH 66/120 FIXME: Export _dma_cache_{wback,wback_inv,inv}
In reply to Fredrik NoringCommit 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
@@ -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
PATCH 67/120 MIPS: PS2: IOP: Module linking support
In reply to Fredrik NoringIOP 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
@@ -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
@@ -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
@@ -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
PATCH 68/120 MIPS: PS2: IOP: Verify that modules are IRX objects
In reply to Fredrik NoringIOP 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
@@ -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
PATCH 69/120 MIPS: PS2: IOP: Module version compatibility verification
In reply to Fredrik NoringIOP 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
@@ -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
PATCH 70/120 MIPS: PS2: IOP: Avoid linking already linked library modules
In reply to Fredrik NoringAs 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
@@ -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
PATCH 71/120 MIPS: PS2: IOP: Resolve module dependencies
In reply to Fredrik NoringAs 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
@@ -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
PATCH 72/120 MIPS: PS2: IOP: SIF printk command support
In reply to Fredrik NoringAllow 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
@@ -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
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.
Signed-off-by Fredrik Noring <[email protected]>--- drivers/ps2/iop-module.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+)drivers/ps2/iop-module.c +0
-0
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
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
PATCH 73/120 MIPS: PS2: IOP: Heap memory allocate and free
In reply to Fredrik NoringThe 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
@@ -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
@@ -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
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
@@ -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
@@ -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
@@ -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
PATCH 75/120 MIPS: PS2: IOP: IRQ support
In reply to Fredrik NoringIOP 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
@@ -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
@@ -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
PATCH 76/120 MIPS: PS2: GS: Define privileged Graphics Synthesizer registers
In reply to Fredrik NoringAll 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
@@ -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
PATCH 77/120 MIPS: PS2: GS: Write privileged registers
In reply to Fredrik NoringAll 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
@@ -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
@@ -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
PATCH 78/120 MIPS: PS2: GS: Read privileged registers
In reply to Fredrik NoringAll 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
@@ -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
@@ -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
PATCH 79/120 MIPS: PS2: GS: Define privileged register structures
In reply to Fredrik NoringRead 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
@@ -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
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
@@ -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
@@ -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
PATCH 81/120 MIPS: PS2: GS: Privileged register write macros with named fields
In reply to Fredrik NoringThe 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
@@ -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
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
@@ -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
@@ -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
PATCH 83/120 MIPS: PS2: GS: Define Graphics Synthesizer primitive structures
In reply to Fredrik NoringThe 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
@@ -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
PATCH 84/120 MIPS: PS2: GIF: Define Graphics Synthesizer interface structures
In reply to Fredrik NoringThe 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
@@ -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
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
@@ -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
@@ -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
PATCH 86/120 MIPS: PS2: GS: Graphics Synthesizer device init and video clock
In reply to Fredrik NoringSeveral 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
@@ -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
@@ -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
@@ -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
PATCH 87/120 MIPS: PS2: GS: Compute block count and indices
In reply to Fredrik NoringThe 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
@@ -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
PATCH 88/120 MIPS: PS2: GS: Primitive and texel coordinate transformations
In reply to Fredrik NoringThe 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
@@ -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
PATCH 89/120 MIPS: PS2: GS: Approximate video region with ROM region
In reply to Fredrik NoringPlayStation 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
@@ -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
@@ -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
PATCH 90/120 macro: Extend COUNT_ARGS() from 12 to 32 arguments
In reply to Fredrik NoringThis 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
@@ -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
PATCH 91/120 MIPS: PS2: GS: Show privileged registers with sysfs
In reply to Fredrik NoringReading 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 0The 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
@@ -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
PATCH 92/120 MIPS: PS2: GS: Store privileged registers with sysfs
In reply to Fredrik NoringOne 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
@@ -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
PATCH 93/120 fbdev: Add fb_warn_once() variant that only prints a warning once
In reply to Fredrik Noringfb_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
@@ -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
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 offb_warn(), to print a warning only once.Signed-off-by Fredrik Noring <[email protected]>--- include/linux/fb.h | 2 ++ 1 file changed, 2 insertions(+)include/linux/fb.h +0
-0
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, ...) \
PATCH 94/120 MIPS: PS2: FB: Frame buffer driver for the PlayStation 2
In reply to Fredrik NoringThe 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
@@ -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
@@ -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
@@ -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
@@ -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
Jiaxun Yang
maintainerRe: [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_opsis accelerated with GS texture sprites that are fast (GS local copy) for the kernel via simple DMA GS commands via the GIF.Signed-off-by Fredrik Noring <[email protected]>
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.cdrivers/video/fbdev/Kconfig +12
-0@@ -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_PS3AVdrivers/video/fbdev/Makefile +1
-0@@ -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.odrivers/video/fbdev/ps2fb.c new file +533
-0@@ -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@@ -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@@ -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 */
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
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.
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
Jiaxun Yang
maintainerRe: [PATCH 094/120] MIPS: PS2: FB: Frame buffer driver for the PlayStation 2
In reply to Fredrik NoringQuoted 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
Re: [PATCH 094/120] MIPS: PS2: FB: Frame buffer driver for the PlayStation 2
Quoted 4 lines
DRM do have
fb_helperproviding 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
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, displayingzimage 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
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
Re: [PATCH 094/120] MIPS: PS2: FB: Frame buffer driver for the PlayStation 2
In reply to Jiaxun YangQuoted 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