Linux-MIPS Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH 000/120] Linux for the PlayStation 2
@ 2019-09-01 15:35 Fredrik Noring
  2019-09-01 15:35 ` [PATCH 001/120] MIPS: R5900: Initial support for the Emotion Engine in " Fredrik Noring
                   ` (120 more replies)
  0 siblings, 121 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:35 UTC (permalink / raw)
  To: Paul Burton, linux-mips; +Cc: Maciej W. Rozycki, Jürgen Urban

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

-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 001/120] MIPS: R5900: Initial support for the Emotion Engine in the PlayStation 2
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
@ 2019-09-01 15:35 ` " Fredrik Noring
  2019-09-01 21:14   ` Maciej W. Rozycki
  2019-09-01 15:36 ` [PATCH 002/120] MIPS: R5900: Trap the RDHWR instruction as an SQ address exception Fredrik Noring
                   ` (119 subsequent siblings)
  120 siblings, 1 reply; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:35 UTC (permalink / raw)
  To: linux-mips

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.

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 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(-)

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index d50fafd7bf3a..88d6c13260e1 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -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
diff --git a/arch/mips/Makefile b/arch/mips/Makefile
index eceff9b75b22..81cd620d32f7 100644
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -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) \
diff --git a/arch/mips/include/asm/cpu-type.h b/arch/mips/include/asm/cpu-type.h
index a45af3de075d..7d35100e0e18 100644
--- a/arch/mips/include/asm/cpu-type.h
+++ b/arch/mips/include/asm/cpu-type.h
@@ -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
diff --git a/arch/mips/include/asm/cpu.h b/arch/mips/include/asm/cpu.h
index 290369fa44a4..05a0f5a07f08 100644
--- a/arch/mips/include/asm/cpu.h
+++ b/arch/mips/include/asm/cpu.h
@@ -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
diff --git a/arch/mips/include/asm/module.h b/arch/mips/include/asm/module.h
index 6dc0b21b8acd..d6fbc3aad2a1 100644
--- a/arch/mips/include/asm/module.h
+++ b/arch/mips/include/asm/module.h
@@ -115,6 +115,8 @@ search_module_dbetables(unsigned long addr)
 #define MODULE_PROC_FAMILY "R5432 "
 #elif defined CONFIG_CPU_R5500
 #define MODULE_PROC_FAMILY "R5500 "
+#elif defined CONFIG_CPU_R5900
+#define MODULE_PROC_FAMILY "R5900 "
 #elif defined CONFIG_CPU_NEVADA
 #define MODULE_PROC_FAMILY "NEVADA "
 #elif defined CONFIG_CPU_R8000
diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c
index 9635c1db3ae6..629391948ab4 100644
--- a/arch/mips/kernel/cpu-probe.c
+++ b/arch/mips/kernel/cpu-probe.c
@@ -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";
diff --git a/arch/mips/mm/Makefile b/arch/mips/mm/Makefile
index 1e8d335025d7..08db8f27d125 100644
--- a/arch/mips/mm/Makefile
+++ b/arch/mips/mm/Makefile
@@ -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
diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c
index 5166e38cd1c6..4f2cecbec722 100644
--- a/arch/mips/mm/c-r4k.c
+++ b/arch/mips/mm/c-r4k.c
@@ -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);
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 002/120] MIPS: R5900: Trap the RDHWR instruction as an SQ address exception
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
  2019-09-01 15:35 ` [PATCH 001/120] MIPS: R5900: Initial support for the Emotion Engine in " Fredrik Noring
@ 2019-09-01 15:36 ` Fredrik Noring
  2019-09-01 22:00   ` Maciej W. Rozycki
  2019-09-01 15:36 ` [PATCH 003/120] MIPS: R5900: Sign-extend o32 system call registers Fredrik Noring
                   ` (118 subsequent siblings)
  120 siblings, 1 reply; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:36 UTC (permalink / raw)
  To: linux-mips

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

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

References:

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 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(-)

diff --git a/arch/mips/include/asm/traps.h b/arch/mips/include/asm/traps.h
index 6a0864bb604d..ed6449f2967b 100644
--- a/arch/mips/include/asm/traps.h
+++ b/arch/mips/include/asm/traps.h
@@ -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 */
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index 342e41de9d64..9423b9a2eb67 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -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);
 
diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c
index 92bd2b0f0548..f490944d73cf 100644
--- a/arch/mips/kernel/unaligned.c
+++ b/arch/mips/kernel/unaligned.c
@@ -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
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 003/120] MIPS: R5900: Sign-extend o32 system call registers
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
  2019-09-01 15:35 ` [PATCH 001/120] MIPS: R5900: Initial support for the Emotion Engine in " Fredrik Noring
  2019-09-01 15:36 ` [PATCH 002/120] MIPS: R5900: Trap the RDHWR instruction as an SQ address exception Fredrik Noring
@ 2019-09-01 15:36 ` Fredrik Noring
  2019-09-01 15:37 ` [PATCH 004/120] MIPS: R5900: Reset bits 127..64 of GPRs in RESTORE_SOME Fredrik Noring
                   ` (117 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:36 UTC (permalink / raw)
  To: linux-mips

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

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

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

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

References:

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

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/kernel/scall32-o32.S | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/arch/mips/kernel/scall32-o32.S b/arch/mips/kernel/scall32-o32.S
index d9434cd0f568..b5ad6798978c 100644
--- a/arch/mips/kernel/scall32-o32.S
+++ b/arch/mips/kernel/scall32-o32.S
@@ -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
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 004/120] MIPS: R5900: Reset bits 127..64 of GPRs in RESTORE_SOME
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (2 preceding siblings ...)
  2019-09-01 15:36 ` [PATCH 003/120] MIPS: R5900: Sign-extend o32 system call registers Fredrik Noring
@ 2019-09-01 15:37 ` Fredrik Noring
  2019-09-01 15:38 ` [PATCH 005/120] MIPS: R5900: Reset the funnel shift amount (SA) register " Fredrik Noring
                   ` (116 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:37 UTC (permalink / raw)
  To: Paul Burton, linux-mips; +Cc: Maciej W. Rozycki, Jürgen Urban

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
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(+)

diff --git a/arch/mips/include/asm/stackframe.h b/arch/mips/include/asm/stackframe.h
index 4d6ad907ae54..aaed9b522220 100644
--- a/arch/mips/include/asm/stackframe.h
+++ b/arch/mips/include/asm/stackframe.h
@@ -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
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 005/120] MIPS: R5900: Reset the funnel shift amount (SA) register in RESTORE_SOME
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (3 preceding siblings ...)
  2019-09-01 15:37 ` [PATCH 004/120] MIPS: R5900: Reset bits 127..64 of GPRs in RESTORE_SOME Fredrik Noring
@ 2019-09-01 15:38 ` " Fredrik Noring
  2019-09-01 15:38 ` [PATCH 006/120] MIPS: R5900: Workaround for the short loop bug Fredrik Noring
                   ` (115 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:38 UTC (permalink / raw)
  To: Paul Burton, linux-mips; +Cc: Maciej W. Rozycki, Jürgen Urban

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

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

References:

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
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(+)

diff --git a/arch/mips/include/asm/stackframe.h b/arch/mips/include/asm/stackframe.h
index aaed9b522220..2fbead2e86d1 100644
--- a/arch/mips/include/asm/stackframe.h
+++ b/arch/mips/include/asm/stackframe.h
@@ -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
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 006/120] MIPS: R5900: Workaround for the short loop bug
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (4 preceding siblings ...)
  2019-09-01 15:38 ` [PATCH 005/120] MIPS: R5900: Reset the funnel shift amount (SA) register " Fredrik Noring
@ 2019-09-01 15:38 ` Fredrik Noring
  2019-09-01 15:39 ` [PATCH 007/120] MIPS: R5900: Add the SYNC.P instruction Fredrik Noring
                   ` (114 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:38 UTC (permalink / raw)
  To: linux-mips

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

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

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

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

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

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

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 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(-)

diff --git a/arch/mips/boot/compressed/head.S b/arch/mips/boot/compressed/head.S
index 409cb483a9ff..e3dc831e2616 100644
--- a/arch/mips/boot/compressed/head.S
+++ b/arch/mips/boot/compressed/head.S
@@ -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 */
diff --git a/arch/mips/include/asm/asmmacro.h b/arch/mips/include/asm/asmmacro.h
index feb069cbf44e..aa58474c739f 100644
--- a/arch/mips/include/asm/asmmacro.h
+++ b/arch/mips/include/asm/asmmacro.h
@@ -654,4 +654,45 @@
 	.set	pop
 	.endm
 
+#ifdef CONFIG_CPU_R5900
+/*
+ * Workaround for the R5900 short loop bug
+ *
+ * The short loop bug under certain conditions causes loops to execute only
+ * once or twice. The Gnu assembler (GAS) has the following note about it:
+ *
+ *     On the R5900 short loops need to be fixed by inserting a NOP in the
+ *     branch delay slot.
+ *
+ *     The short loop bug under certain conditions causes loops to execute
+ *     only once or twice. We must ensure that the assembler never
+ *     generates loops that satisfy all of the following conditions:
+ *
+ *     - a loop consists of less than or equal to six instructions
+ *       (including the branch delay slot);
+ *     - a loop contains only one conditional branch instruction at the
+ *       end of the loop;
+ *     - a loop does not contain any other branch or jump instructions;
+ *     - a branch delay slot of the loop is not NOP (EE 2.9 or later).
+ *
+ *     We need to do this because of a hardware bug in the R5900 chip.
+ *
+ * GAS handles the short loop bug in most cases. However, GAS is unable to
+ * adjust machine code having the noreorder directive, as used by the kernel
+ * on several occasions. The short_loop_war macro defined here can be used
+ * to insert necessary NOPs by placing it just before the jump instruction.
+ */
+	.macro	short_loop_war loop_target
+	.set	instruction_count,2 + (. - \loop_target) / 4
+	.ifgt	7 - instruction_count
+	.rept	7 - instruction_count
+	nop
+	.endr
+	.endif
+	.endm
+#else
+	.macro	short_loop_war loop_target
+	.endm
+#endif
+
 #endif /* _ASM_ASMMACRO_H */
diff --git a/arch/mips/include/asm/string.h b/arch/mips/include/asm/string.h
index 29030cb398ee..35c9dc3815f4 100644
--- a/arch/mips/include/asm/string.h
+++ b/arch/mips/include/asm/string.h
@@ -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
diff --git a/arch/mips/lib/delay.c b/arch/mips/lib/delay.c
index 68c495ed71e3..0b370b831708 100644
--- a/arch/mips/lib/delay.c
+++ b/arch/mips/lib/delay.c
@@ -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));
 }
diff --git a/arch/mips/lib/memset.S b/arch/mips/lib/memset.S
index 418611ef13cf..70db395159f1 100644
--- a/arch/mips/lib/memset.S
+++ b/arch/mips/lib/memset.S
@@ -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
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 007/120] MIPS: R5900: Add the SYNC.P instruction
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (5 preceding siblings ...)
  2019-09-01 15:38 ` [PATCH 006/120] MIPS: R5900: Workaround for the short loop bug Fredrik Noring
@ 2019-09-01 15:39 ` Fredrik Noring
  2019-09-01 15:39 ` [PATCH 008/120] MIPS: R5900: Add implicit SYNC.P to the UASM_i_M[FT]C0 macros Fredrik Noring
                   ` (113 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:39 UTC (permalink / raw)
  To: linux-mips

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

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

References:

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 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(-)

diff --git a/arch/mips/include/asm/uasm.h b/arch/mips/include/asm/uasm.h
index f7effca791a5..71ddf155ef85 100644
--- a/arch/mips/include/asm/uasm.h
+++ b/arch/mips/include/asm/uasm.h
@@ -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);
diff --git a/arch/mips/mm/uasm-mips.c b/arch/mips/mm/uasm-mips.c
index 7154a1d99aad..725c6fe1e317 100644
--- a/arch/mips/mm/uasm-mips.c
+++ b/arch/mips/mm/uasm-mips.c
@@ -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},
diff --git a/arch/mips/mm/uasm.c b/arch/mips/mm/uasm.c
index c56f129c9a4b..32c7a5827ba8 100644
--- a/arch/mips/mm/uasm.c
+++ b/arch/mips/mm/uasm.c
@@ -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)
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 008/120] MIPS: R5900: Add implicit SYNC.P to the UASM_i_M[FT]C0 macros
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (6 preceding siblings ...)
  2019-09-01 15:39 ` [PATCH 007/120] MIPS: R5900: Add the SYNC.P instruction Fredrik Noring
@ 2019-09-01 15:39 ` Fredrik Noring
  2019-09-01 15:39 ` [PATCH 009/120] MIPS: R5900: Add mandatory SYNC.P to all M[FT]C0 instructions Fredrik Noring
                   ` (112 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:39 UTC (permalink / raw)
  To: Paul Burton, linux-mips; +Cc: Maciej W. Rozycki, Jürgen Urban

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

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

References:

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
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(+)

diff --git a/arch/mips/include/asm/uasm.h b/arch/mips/include/asm/uasm.h
index 71ddf155ef85..34b61fb53da5 100644
--- a/arch/mips/include/asm/uasm.h
+++ b/arch/mips/include/asm/uasm.h
@@ -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)
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 009/120] MIPS: R5900: Add mandatory SYNC.P to all M[FT]C0 instructions
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (7 preceding siblings ...)
  2019-09-01 15:39 ` [PATCH 008/120] MIPS: R5900: Add implicit SYNC.P to the UASM_i_M[FT]C0 macros Fredrik Noring
@ 2019-09-01 15:39 ` Fredrik Noring
  2019-09-01 15:39 ` [PATCH 010/120] MIPS: R5900: Workaround exception NOP execution bug (FLX05) Fredrik Noring
                   ` (111 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:39 UTC (permalink / raw)
  To: Paul Burton, linux-mips; +Cc: Maciej W. Rozycki, Jürgen Urban

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

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

References:

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
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(-)

diff --git a/arch/mips/include/asm/asmmacro.h b/arch/mips/include/asm/asmmacro.h
index aa58474c739f..67e77c0b64aa 100644
--- a/arch/mips/include/asm/asmmacro.h
+++ b/arch/mips/include/asm/asmmacro.h
@@ -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)
diff --git a/arch/mips/include/asm/irqflags.h b/arch/mips/include/asm/irqflags.h
index f0b862a83816..e0c0265b2719 100644
--- a/arch/mips/include/asm/irqflags.h
+++ b/arch/mips/include/asm/irqflags.h
@@ -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));
diff --git a/arch/mips/include/asm/mipsregs.h b/arch/mips/include/asm/mipsregs.h
index 1e6966e8527e..2aa947b3d0d1 100644
--- a/arch/mips/include/asm/mipsregs.h
+++ b/arch/mips/include/asm/mipsregs.h
@@ -1345,6 +1345,29 @@ do {								\
  * Macros to access the system control coprocessor
  */
 
+#ifdef CONFIG_CPU_R5900
+#define ___read_32bit_c0_register(source, sel, vol)			\
+({ int __res;								\
+	if (sel == 0)							\
+		__asm__ vol(						\
+			".set push\n\t"					\
+			".set noreorder\n\t"				\
+			"sync.p\n\t"					\
+			"mfc0\t%0, " #source "\n\t"			\
+			".set pop\n\t"					\
+			: "=r" (__res));				\
+	else								\
+		__asm__ vol(						\
+			".set push\n\t"					\
+			".set noreorder\n\t"				\
+			".set\tmips32\n\t"				\
+			"sync.p\n\t"					\
+			"mfc0\t%0, " #source ", " #sel "\n\t"		\
+			".set pop\n\t"					\
+			: "=r" (__res));				\
+	__res;								\
+})
+#else
 #define ___read_32bit_c0_register(source, sel, vol)			\
 ({ unsigned int __res;							\
 	if (sel == 0)							\
@@ -1360,6 +1383,7 @@ do {								\
 			: "=r" (__res));				\
 	__res;								\
 })
+#endif
 
 #define ___read_64bit_c0_register(source, sel, vol)			\
 ({ unsigned long long __res;						\
@@ -1394,6 +1418,28 @@ do {								\
 #define __read_const_64bit_c0_register(source, sel)			\
 	___read_64bit_c0_register(source, sel,)
 
+#ifdef CONFIG_CPU_R5900
+#define __write_32bit_c0_register(register, sel, value)			\
+do {									\
+	if (sel == 0)							\
+		__asm__ __volatile__(					\
+			".set push\n\t"					\
+			".set noreorder\n\t"				\
+			"mtc0\t%z0, " #register "\n\t"			\
+			"sync.p\n\t"					\
+			".set pop\n\t"					\
+			: : "Jr" ((unsigned int)(value)));		\
+	else								\
+		__asm__ __volatile__(					\
+			".set push\n\t"					\
+			".set noreorder\n\t"				\
+			".set\tmips32\n\t"				\
+			"mtc0\t%z0, " #register ", " #sel "\n\t"	\
+			"sync.p\n\t"					\
+			".set pop\n\t"					\
+			: : "Jr" ((unsigned int)(value)));		\
+} while (0)
+#else
 #define __write_32bit_c0_register(register, sel, value)			\
 do {									\
 	if (sel == 0)							\
@@ -1408,6 +1454,7 @@ do {									\
 			".set\tpop"					\
 			: : "Jr" ((unsigned int)(value)));		\
 } while (0)
+#endif
 
 #define __write_64bit_c0_register(register, sel, value)			\
 do {									\
@@ -2649,6 +2696,14 @@ static inline void tlb_probe(void)
 	__asm__ __volatile__(
 		".set noreorder\n\t"
 		"tlbp\n\t"
+#ifdef CONFIG_CPU_R5900
+		/* No memory access behind the tlbp instruction. */
+		"sync.p\n\t"
+		"nop\n\t"
+		"nop\n\t"
+		"nop\n\t"
+		"nop\n\t"
+#endif
 		".set reorder");
 }
 
@@ -2674,6 +2729,14 @@ static inline void tlb_read(void)
 	__asm__ __volatile__(
 		".set noreorder\n\t"
 		"tlbr\n\t"
+#ifdef CONFIG_CPU_R5900
+		"sync.p\n\t"
+		/* No branch behind tlbr. */
+		"nop\n\t"
+		"nop\n\t"
+		"nop\n\t"
+		"nop\n\t"
+#endif
 		".set reorder");
 
 #if MIPS34K_MISSED_ITLB_WAR
@@ -2694,6 +2757,9 @@ static inline void tlb_write_indexed(void)
 	__asm__ __volatile__(
 		".set noreorder\n\t"
 		"tlbwi\n\t"
+#ifdef CONFIG_CPU_R5900
+		"sync.p\n\t"
+#endif
 		".set reorder");
 }
 
@@ -2702,6 +2768,9 @@ static inline void tlb_write_random(void)
 	__asm__ __volatile__(
 		".set noreorder\n\t"
 		"tlbwr\n\t"
+#ifdef CONFIG_CPU_R5900
+		"sync.p\n\t"
+#endif
 		".set reorder");
 }
 
diff --git a/arch/mips/include/asm/stackframe.h b/arch/mips/include/asm/stackframe.h
index 2fbead2e86d1..4a0f6639fde8 100644
--- a/arch/mips/include/asm/stackframe.h
+++ b/arch/mips/include/asm/stackframe.h
@@ -116,6 +116,9 @@
 
 		/* SMP variation */
 		.macro	get_saved_sp docfi=0 tosp=0
+#ifdef CONFIG_CPU_R5900
+		sync.p
+#endif
 		ASM_CPUID_MFC0	k0, ASM_SMP_CPUID_REG
 #if defined(CONFIG_32BIT) || defined(KBUILD_64BIT_SYM32)
 		lui	k1, %hi(kernelsp)
@@ -140,6 +143,9 @@
 		.endm
 
 		.macro	set_saved_sp stackp temp temp2
+#ifdef CONFIG_CPU_R5900
+		sync.p
+#endif
 		ASM_CPUID_MFC0	\temp, ASM_SMP_CPUID_REG
 		LONG_SRL	\temp, SMP_CPUID_PTRSHIFT
 		LONG_S	\stackp, kernelsp(\temp)
@@ -165,6 +171,9 @@
 1:		move	ra, k0
 		li	k0, 3
 		mtc0	k0, $22
+#ifdef CONFIG_CPU_R5900
+		sync.p
+#endif
 #endif /* CONFIG_CPU_JUMP_WORKAROUNDS */
 #if defined(CONFIG_32BIT) || defined(KBUILD_64BIT_SYM32)
 		lui	k1, %hi(kernelsp)
@@ -195,6 +204,9 @@
 		.set	push
 		.set	noat
 		.set	reorder
+#ifdef CONFIG_CPU_R5900
+		sync.p
+#endif
 		mfc0	k0, CP0_STATUS
 		sll	k0, 3		/* extract cu0 bit */
 		.set	noreorder
@@ -251,15 +263,24 @@
 		 * need it to operate correctly
 		 */
 		LONG_S	$0, PT_R0(sp)
+#ifdef CONFIG_CPU_R5900
+		sync.p
+#endif
 		mfc0	v1, CP0_STATUS
 		cfi_st	v0, PT_R2, \docfi
 		LONG_S	v1, PT_STATUS(sp)
 		cfi_st	$4, PT_R4, \docfi
+#ifdef CONFIG_CPU_R5900
+		sync.p
+#endif
 		mfc0	v1, CP0_CAUSE
 		cfi_st	$5, PT_R5, \docfi
 		LONG_S	v1, PT_CAUSE(sp)
 		cfi_st	$6, PT_R6, \docfi
 		cfi_st	ra, PT_R31, \docfi
+#ifdef CONFIG_CPU_R5900
+		sync.p
+#endif
 		MFC0	ra, CP0_EPC
 		cfi_st	$7, PT_R7, \docfi
 #ifdef CONFIG_64BIT
@@ -273,6 +294,9 @@
 		cfi_st	$25, PT_R25, \docfi
 		cfi_st	$28, PT_R28, \docfi
 
+#ifdef CONFIG_CPU_R5900
+		sync.p
+#endif
 		/* Set thread_info if we're coming from user mode */
 		mfc0	k0, CP0_STATUS
 		sll	k0, 3		/* extract cu0 bit */
@@ -447,10 +471,16 @@
 		.set	reorder
 		.set	noat
 		RESET_MMR
+#ifdef CONFIG_CPU_R5900
+		sync.p
+#endif
 		mfc0	a0, CP0_STATUS
 		ori	a0, STATMASK
 		xori	a0, STATMASK
 		mtc0	a0, CP0_STATUS
+#ifdef CONFIG_CPU_R5900
+		sync.p
+#endif
 		li	v1, ST0_CU1 | ST0_FR | ST0_IM
 		and	a0, v1
 		LONG_L	v0, PT_STATUS(sp)
@@ -458,8 +488,14 @@
 		and	v0, v1
 		or	v0, a0
 		mtc0	v0, CP0_STATUS
+#ifdef CONFIG_CPU_R5900
+		sync.p
+#endif
 		LONG_L	v1, PT_EPC(sp)
 		MTC0	v1, CP0_EPC
+#ifdef CONFIG_CPU_R5900
+		sync.p
+#endif
 		cfi_ld	$31, PT_R31, \docfi
 		cfi_ld	$28, PT_R28, \docfi
 		cfi_ld	$25, PT_R25, \docfi
@@ -503,11 +539,17 @@
  * Set cp0 enable bit as sign that we're running on the kernel stack
  */
 		.macro	CLI
+#ifdef CONFIG_CPU_R5900
+		sync.p
+#endif
 		mfc0	t0, CP0_STATUS
 		li	t1, ST0_CU0 | STATMASK
 		or	t0, t1
 		xori	t0, STATMASK
 		mtc0	t0, CP0_STATUS
+#ifdef CONFIG_CPU_R5900
+		sync.p
+#endif
 		irq_disable_hazard
 		.endm
 
@@ -516,11 +558,17 @@
  * Set cp0 enable bit as sign that we're running on the kernel stack
  */
 		.macro	STI
+#ifdef CONFIG_CPU_R5900
+		sync.p
+#endif
 		mfc0	t0, CP0_STATUS
 		li	t1, ST0_CU0 | STATMASK
 		or	t0, t1
 		xori	t0, STATMASK & ~1
 		mtc0	t0, CP0_STATUS
+#ifdef CONFIG_CPU_R5900
+		sync.p
+#endif
 		irq_enable_hazard
 		.endm
 
@@ -530,6 +578,9 @@
  * Set cp0 enable bit as sign that we're running on the kernel stack
  */
 		.macro	KMODE
+#ifdef CONFIG_CPU_R5900
+		sync.p
+#endif
 		mfc0	t0, CP0_STATUS
 		li	t1, ST0_CU0 | (STATMASK & ~1)
 #if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
@@ -540,6 +591,9 @@
 		or	t0, t1
 		xori	t0, STATMASK & ~1
 		mtc0	t0, CP0_STATUS
+#ifdef CONFIG_CPU_R5900
+		sync.p
+#endif
 		irq_disable_hazard
 		.endm
 
diff --git a/arch/mips/kernel/genex.S b/arch/mips/kernel/genex.S
index 398b905b027d..6eaf057e5d95 100644
--- a/arch/mips/kernel/genex.S
+++ b/arch/mips/kernel/genex.S
@@ -33,7 +33,13 @@ NESTED(except_vec3_generic, 0, sp)
 	.set	push
 	.set	noat
 #if R5432_CP0_INTERRUPT_WAR
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	mfc0	k0, CP0_INDEX
+#endif
+#ifdef CONFIG_CPU_R5900
+	sync.p
 #endif
 	mfc0	k1, CP0_CAUSE
 	andi	k1, k1, 0x7c
@@ -55,6 +61,9 @@ NESTED(except_vec3_r4000, 0, sp)
 	.set	push
 	.set	arch=r4000
 	.set	noat
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	mfc0	k1, CP0_CAUSE
 	li	k0, 31<<2
 	andi	k1, k1, 0x7c
@@ -78,10 +87,16 @@ NESTED(except_vec3_r4000, 0, sp)
 	 * load / store will be re-executed.
 	 */
 handle_vced:
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	MFC0	k0, CP0_BADVADDR
 	li	k1, -4					# Is this ...
 	and	k0, k1					# ... really needed?
 	mtc0	zero, CP0_TAGLO
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	cache	Index_Store_Tag_D, (k0)
 	cache	Hit_Writeback_Inv_SD, (k0)
 #ifdef CONFIG_PROC_FS
@@ -93,6 +108,9 @@ handle_vced:
 	eret
 
 handle_vcei:
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	MFC0	k0, CP0_BADVADDR
 	cache	Hit_Writeback_Inv_SD, (k0)		# also cleans pi
 #ifdef CONFIG_PROC_FS
@@ -138,12 +156,18 @@ LEAF(__r4k_wait)
 	FEXPORT(rollback_\handler)
 	.set	push
 	.set	noat
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	MFC0	k0, CP0_EPC
 	PTR_LA	k1, __r4k_wait
 	ori	k0, 0x1f	/* 32 byte rollback region */
 	xori	k0, 0x1f
 	bne	k0, k1, \handler
 	MTC0	k0, CP0_EPC
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	.set pop
 	.endm
 
@@ -164,11 +188,17 @@ NESTED(handle_int, PT_SIZE, sp)
 	 */
 	.set	push
 	.set	noat
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	mfc0	k0, CP0_STATUS
 #if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
 	and	k0, ST0_IEP
 	bnez	k0, 1f
 
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	mfc0	k0, CP0_EPC
 	.set	noreorder
 	j	k0
@@ -349,6 +379,9 @@ NESTED(ejtag_debug_handler, PT_SIZE, sp)
 	.set	push
 	.set	noat
 	MTC0	k0, CP0_DESAVE
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	mfc0	k0, CP0_DEBUG
 
 	sll	k0, k0, 30	# Check for SDBBP.
@@ -404,6 +437,9 @@ NESTED(ejtag_debug_handler, PT_SIZE, sp)
 
 ejtag_return:
 	back_to_back_c0_hazard
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	MFC0	k0, CP0_DESAVE
 	.set	mips32
 	deret
@@ -494,6 +530,9 @@ NESTED(nmi_handler, PT_SIZE, sp)
 	.endm
 
 	.macro	__build_clear_ade
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	MFC0	t0, CP0_BADVADDR
 	PTR_S	t0, PT_BVADDR(sp)
 	KMODE
@@ -579,16 +618,28 @@ NESTED(nmi_handler, PT_SIZE, sp)
 	.set	noat
 	.set	noreorder
 	/* check if TLB contains a entry for EPC */
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	MFC0	k1, CP0_ENTRYHI
 	andi	k1, MIPS_ENTRYHI_ASID | MIPS_ENTRYHI_ASIDX
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	MFC0	k0, CP0_EPC
 	PTR_SRL	k0, _PAGE_SHIFT + 1
 	PTR_SLL	k0, _PAGE_SHIFT + 1
 	or	k1, k0
 	MTC0	k1, CP0_ENTRYHI
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	mtc0_tlbw_hazard
 	tlbp
 	tlb_probe_hazard
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	mfc0	k1, CP0_INDEX
 	.set	pop
 	bltz	k1, handle_ri	/* slow path */
@@ -601,6 +652,9 @@ NESTED(nmi_handler, PT_SIZE, sp)
 	.set	noreorder
 	/* MIPS32:    0x7c03e83b: rdhwr v1,$29 */
 	/* microMIPS: 0x007d6b3c: rdhwr v1,$29 */
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	MFC0	k1, CP0_EPC
 #if defined(CONFIG_CPU_MICROMIPS) || defined(CONFIG_CPU_MIPS32_R2) || defined(CONFIG_CPU_MIPS64_R2)
 	and	k0, k1, 1
@@ -631,6 +685,9 @@ isrdhwr:
 	/* The insn is rdhwr.  No need to check CAUSE.BD here. */
 	get_saved_sp	/* k1 := current_thread_info */
 	.set	noreorder
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	MFC0	k0, CP0_EPC
 #if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
 	ori	k1, _THREAD_MASK
@@ -648,6 +705,9 @@ isrdhwr:
 	.set	noat
 #endif
 	MTC0	k0, CP0_EPC
+#ifdef CONFIG_CPU_R5900
+	sync.p
+#endif
 	/* I hope three instructions between MTC0 and ERET are enough... */
 	ori	k1, _THREAD_MASK
 	xori	k1, _THREAD_MASK
diff --git a/arch/mips/kernel/head.S b/arch/mips/kernel/head.S
index 351d40fe0859..aae4b17a33d0 100644
--- a/arch/mips/kernel/head.S
+++ b/arch/mips/kernel/head.S
@@ -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
diff --git a/arch/mips/kernel/r4k_switch.S b/arch/mips/kernel/r4k_switch.S
index 58232ae6cfae..de4a1880e9a8 100644
--- a/arch/mips/kernel/r4k_switch.S
+++ b/arch/mips/kernel/r4k_switch.S
@@ -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)
diff --git a/arch/mips/mm/cex-gen.S b/arch/mips/mm/cex-gen.S
index 45dff5cd4b8e..c5075651229c 100644
--- a/arch/mips/mm/cex-gen.S
+++ b/arch/mips/mm/cex-gen.S
@@ -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
diff --git a/arch/mips/mm/tlbex-fault.S b/arch/mips/mm/tlbex-fault.S
index 77db401fc620..fe2b2c61cca7 100644
--- a/arch/mips/mm/tlbex-fault.S
+++ b/arch/mips/mm/tlbex-fault.S
@@ -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
diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c
index 144ceb0fba88..543ddb22b0d9 100644
--- a/arch/mips/mm/tlbex.c
+++ b/arch/mips/mm/tlbex.c
@@ -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 */
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 010/120] MIPS: R5900: Workaround exception NOP execution bug (FLX05)
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (8 preceding siblings ...)
  2019-09-01 15:39 ` [PATCH 009/120] MIPS: R5900: Add mandatory SYNC.P to all M[FT]C0 instructions Fredrik Noring
@ 2019-09-01 15:39 ` Fredrik Noring
  2019-09-01 23:01   ` Philippe Mathieu-Daudé
  2019-09-01 15:40 ` [PATCH 011/120] MIPS: R5900: Avoid pipeline hazard with the TLBP instruction Fredrik Noring
                   ` (110 subsequent siblings)
  120 siblings, 1 reply; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:39 UTC (permalink / raw)
  To: linux-mips

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

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

References:

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/kernel/genex.S | 5 +++++
 arch/mips/kernel/traps.c | 6 ++++++
 arch/mips/mm/tlbex.c     | 6 ++++++
 3 files changed, 17 insertions(+)

diff --git a/arch/mips/kernel/genex.S b/arch/mips/kernel/genex.S
index 6eaf057e5d95..f57842b785b2 100644
--- a/arch/mips/kernel/genex.S
+++ b/arch/mips/kernel/genex.S
@@ -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
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index 9423b9a2eb67..9c98475c7dc6 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -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);
diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c
index 543ddb22b0d9..82136c346885 100644
--- a/arch/mips/mm/tlbex.c
+++ b/arch/mips/mm/tlbex.c
@@ -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);
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 011/120] MIPS: R5900: Avoid pipeline hazard with the TLBP instruction
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (9 preceding siblings ...)
  2019-09-01 15:39 ` [PATCH 010/120] MIPS: R5900: Workaround exception NOP execution bug (FLX05) Fredrik Noring
@ 2019-09-01 15:40 ` Fredrik Noring
  2019-09-01 17:15   ` Sergei Shtylyov
  2019-09-01 15:40 ` [PATCH 012/120] MIPS: R5900: Avoid pipeline hazards with the TLBW[IR] instructions Fredrik Noring
                   ` (109 subsequent siblings)
  120 siblings, 1 reply; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:40 UTC (permalink / raw)
  To: linux-mips

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

References:

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/mm/tlbex.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c
index 82136c346885..0519e2eafbb8 100644
--- a/arch/mips/mm/tlbex.c
+++ b/arch/mips/mm/tlbex.c
@@ -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;
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 012/120] MIPS: R5900: Avoid pipeline hazards with the TLBW[IR] instructions
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (10 preceding siblings ...)
  2019-09-01 15:40 ` [PATCH 011/120] MIPS: R5900: Avoid pipeline hazard with the TLBP instruction Fredrik Noring
@ 2019-09-01 15:40 ` Fredrik Noring
  2019-09-01 15:40 ` [PATCH 013/120] MIPS: R5900: Avoid pipeline hazard with the TLBR instruction Fredrik Noring
                   ` (108 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:40 UTC (permalink / raw)
  To: linux-mips

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

References:

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

[2] Ibid. p. C-40.

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/mm/tlbex.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c
index 0519e2eafbb8..89ff0eae5397 100644
--- a/arch/mips/mm/tlbex.c
+++ b/arch/mips/mm/tlbex.c
@@ -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);
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 013/120] MIPS: R5900: Avoid pipeline hazard with the TLBR instruction
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (11 preceding siblings ...)
  2019-09-01 15:40 ` [PATCH 012/120] MIPS: R5900: Avoid pipeline hazards with the TLBW[IR] instructions Fredrik Noring
@ 2019-09-01 15:40 ` Fredrik Noring
  2019-09-01 15:41 ` [PATCH 014/120] MIPS: R5900: Install final length of TLB refill handler rather than 256 bytes Fredrik Noring
                   ` (107 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:40 UTC (permalink / raw)
  To: linux-mips

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

References:

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/mipsregs.h |  4 ++++
 arch/mips/mm/tlbex.c             | 24 ++++++++++++++++++++++++
 2 files changed, 28 insertions(+)

diff --git a/arch/mips/include/asm/mipsregs.h b/arch/mips/include/asm/mipsregs.h
index 2aa947b3d0d1..ec22406c800f 100644
--- a/arch/mips/include/asm/mipsregs.h
+++ b/arch/mips/include/asm/mipsregs.h
@@ -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"
diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c
index 89ff0eae5397..1caa0214d57a 100644
--- a/arch/mips/mm/tlbex.c
+++ b/arch/mips/mm/tlbex.c
@@ -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) {
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 014/120] MIPS: R5900: Install final length of TLB refill handler rather than 256 bytes
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (12 preceding siblings ...)
  2019-09-01 15:40 ` [PATCH 013/120] MIPS: R5900: Avoid pipeline hazard with the TLBR instruction Fredrik Noring
@ 2019-09-01 15:41 ` Fredrik Noring
  2019-09-01 15:41 ` [PATCH 015/120] MIPS: R5900: Verify that the TLB refill handler does not overflow Fredrik Noring
                   ` (106 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:41 UTC (permalink / raw)
  To: linux-mips

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

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

        Table 5-2. Exception Vectors for Level 1 exceptions

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

        Table 5-3. Exception Vectors for Level 2 exceptions

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

References:

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/mm/tlbex.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c
index 1caa0214d57a..d356953509e6 100644
--- a/arch/mips/mm/tlbex.c
+++ b/arch/mips/mm/tlbex.c
@@ -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)
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 015/120] MIPS: R5900: Verify that the TLB refill handler does not overflow
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (13 preceding siblings ...)
  2019-09-01 15:41 ` [PATCH 014/120] MIPS: R5900: Install final length of TLB refill handler rather than 256 bytes Fredrik Noring
@ 2019-09-01 15:41 ` Fredrik Noring
  2019-09-01 15:41 ` [PATCH 016/120] MIPS: R5900: The ERET instruction has issues with delay slot and CACHE Fredrik Noring
                   ` (105 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:41 UTC (permalink / raw)
  To: linux-mips

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

        Table 5-2. Exception Vectors for Level 1 exceptions

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

        Table 5-3. Exception Vectors for Level 2 exceptions

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

References:

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/mm/tlbex.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c
index d356953509e6..1bd134b6f033 100644
--- a/arch/mips/mm/tlbex.c
+++ b/arch/mips/mm/tlbex.c
@@ -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:
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 016/120] MIPS: R5900: The ERET instruction has issues with delay slot and CACHE
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (14 preceding siblings ...)
  2019-09-01 15:41 ` [PATCH 015/120] MIPS: R5900: Verify that the TLB refill handler does not overflow Fredrik Noring
@ 2019-09-01 15:41 ` Fredrik Noring
  2019-09-01 15:42 ` [PATCH 017/120] MIPS: R5900: Define CACHE instruction operation field encodings Fredrik Noring
                   ` (104 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:41 UTC (permalink / raw)
  To: linux-mips

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
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(+)

diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c
index 1bd134b6f033..9d5864b20e9f 100644
--- a/arch/mips/mm/tlbex.c
+++ b/arch/mips/mm/tlbex.c
@@ -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);
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 017/120] MIPS: R5900: Define CACHE instruction operation field encodings
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (15 preceding siblings ...)
  2019-09-01 15:41 ` [PATCH 016/120] MIPS: R5900: The ERET instruction has issues with delay slot and CACHE Fredrik Noring
@ 2019-09-01 15:42 ` Fredrik Noring
  2019-09-01 15:42 ` [PATCH 018/120] MIPS: R5900: Workaround where MSB must be 0 for the instruction cache Fredrik Noring
                   ` (103 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:42 UTC (permalink / raw)
  To: linux-mips

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

    Table C-1. CACHE Instruction Op Field Encoding

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

References:

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/cacheops.h | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/arch/mips/include/asm/cacheops.h b/arch/mips/include/asm/cacheops.h
index 8031fbc6b69a..3a6b34be1122 100644
--- a/arch/mips/include/asm/cacheops.h
+++ b/arch/mips/include/asm/cacheops.h
@@ -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
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 018/120] MIPS: R5900: Workaround where MSB must be 0 for the instruction cache
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (16 preceding siblings ...)
  2019-09-01 15:42 ` [PATCH 017/120] MIPS: R5900: Define CACHE instruction operation field encodings Fredrik Noring
@ 2019-09-01 15:42 ` Fredrik Noring
  2019-09-01 15:42 ` [PATCH 019/120] MIPS: R5900: Use SYNC.L for data cache and SYNC.P for " Fredrik Noring
                   ` (102 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:42 UTC (permalink / raw)
  To: linux-mips

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
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(+)

diff --git a/arch/mips/include/asm/r4kcache.h b/arch/mips/include/asm/r4kcache.h
index 7f4a32d3345a..e00087db9d74 100644
--- a/arch/mips/include/asm/r4kcache.h
+++ b/arch/mips/include/asm/r4kcache.h
@@ -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__(						\
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 019/120] MIPS: R5900: Use SYNC.L for data cache and SYNC.P for instruction cache
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (17 preceding siblings ...)
  2019-09-01 15:42 ` [PATCH 018/120] MIPS: R5900: Workaround where MSB must be 0 for the instruction cache Fredrik Noring
@ 2019-09-01 15:42 ` " Fredrik Noring
  2019-09-01 15:43 ` [PATCH 020/120] MIPS: R5900: Define CP0.Config register fields Fredrik Noring
                   ` (101 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:42 UTC (permalink / raw)
  To: Paul Burton, linux-mips; +Cc: Maciej W. Rozycki, Jürgen Urban

Toshiba TX79 manual programming notes[1]:

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

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

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

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

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

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

References:

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
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(-)

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


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 020/120] MIPS: R5900: Define CP0.Config register fields
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (18 preceding siblings ...)
  2019-09-01 15:42 ` [PATCH 019/120] MIPS: R5900: Use SYNC.L for data cache and SYNC.P for " Fredrik Noring
@ 2019-09-01 15:43 ` Fredrik Noring
  2019-09-01 23:04   ` Philippe Mathieu-Daudé
  2019-09-01 15:43 ` [PATCH 021/120] MIPS: R5900: Workaround for CACHE instruction near branch delay slot Fredrik Noring
                   ` (100 subsequent siblings)
  120 siblings, 1 reply; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:43 UTC (permalink / raw)
  To: linux-mips

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

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

References:

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/mipsregs.h | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/arch/mips/include/asm/mipsregs.h b/arch/mips/include/asm/mipsregs.h
index ec22406c800f..a3b3ee011539 100644
--- a/arch/mips/include/asm/mipsregs.h
+++ b/arch/mips/include/asm/mipsregs.h
@@ -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)
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 021/120] MIPS: R5900: Workaround for CACHE instruction near branch delay slot
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (19 preceding siblings ...)
  2019-09-01 15:43 ` [PATCH 020/120] MIPS: R5900: Define CP0.Config register fields Fredrik Noring
@ 2019-09-01 15:43 ` Fredrik Noring
  2019-09-01 15:46 ` [PATCH 022/120] MIPS: R5900: Support 64-bit inq() and outq() macros in 32-bit kernels Fredrik Noring
                   ` (99 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:43 UTC (permalink / raw)
  To: linux-mips

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
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(+)

diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index 9c98475c7dc6..647a1990163a 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -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;
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 022/120] MIPS: R5900: Support 64-bit inq() and outq() macros in 32-bit kernels
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (20 preceding siblings ...)
  2019-09-01 15:43 ` [PATCH 021/120] MIPS: R5900: Workaround for CACHE instruction near branch delay slot Fredrik Noring
@ 2019-09-01 15:46 ` Fredrik Noring
  2019-09-04  1:04   ` Jiaxun Yang
  2019-09-01 15:46 ` [PATCH 023/120] MIPS: R5900: Add MFSA and MTSA instructions for the special SA register Fredrik Noring
                   ` (98 subsequent siblings)
  120 siblings, 1 reply; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:46 UTC (permalink / raw)
  To: Paul Burton, linux-mips; +Cc: Maciej W. Rozycki, Jürgen Urban

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

References:

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/io.h | 54 +++++++++++++++++++++++++++++++++-----
 1 file changed, 47 insertions(+), 7 deletions(-)

diff --git a/arch/mips/include/asm/io.h b/arch/mips/include/asm/io.h
index 97a280640daf..f7115472f530 100644
--- a/arch/mips/include/asm/io.h
+++ b/arch/mips/include/asm/io.h
@@ -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
 
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 023/120] MIPS: R5900: Add MFSA and MTSA instructions for the special SA register
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (21 preceding siblings ...)
  2019-09-01 15:46 ` [PATCH 022/120] MIPS: R5900: Support 64-bit inq() and outq() macros in 32-bit kernels Fredrik Noring
@ 2019-09-01 15:46 ` Fredrik Noring
  2019-09-01 15:46 ` [PATCH 024/120] MIPS: PS2: Define PlayStation 2 I/O port, ROM and RAM address spaces Fredrik Noring
                   ` (97 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:46 UTC (permalink / raw)
  To: linux-mips

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

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

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

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

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

References:

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

[2] Ibid. p. B-20.

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/mipsregs.h | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/arch/mips/include/asm/mipsregs.h b/arch/mips/include/asm/mipsregs.h
index a3b3ee011539..d8c1ffac2824 100644
--- a/arch/mips/include/asm/mipsregs.h
+++ b/arch/mips/include/asm/mipsregs.h
@@ -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;						\
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 024/120] MIPS: PS2: Define PlayStation 2 I/O port, ROM and RAM address spaces
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (22 preceding siblings ...)
  2019-09-01 15:46 ` [PATCH 023/120] MIPS: R5900: Add MFSA and MTSA instructions for the special SA register Fredrik Noring
@ 2019-09-01 15:46 ` Fredrik Noring
  2019-09-01 15:47 ` [PATCH 025/120] MIPS: PS2: Define interrupt controller, DMA and timer IRQs Fredrik Noring
                   ` (96 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:46 UTC (permalink / raw)
  To: linux-mips

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 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

diff --git a/arch/mips/include/asm/mach-ps2/rom.h b/arch/mips/include/asm/mach-ps2/rom.h
new file mode 100644
index 000000000000..6760be183696
--- /dev/null
+++ b/arch/mips/include/asm/mach-ps2/rom.h
@@ -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 */
diff --git a/arch/mips/ps2/Makefile b/arch/mips/ps2/Makefile
new file mode 100644
index 000000000000..24d537d2fb9f
--- /dev/null
+++ b/arch/mips/ps2/Makefile
@@ -0,0 +1 @@
+obj-y		+= memory.o
diff --git a/arch/mips/ps2/memory.c b/arch/mips/ps2/memory.c
new file mode 100644
index 000000000000..66ca37f38330
--- /dev/null
+++ b/arch/mips/ps2/memory.c
@@ -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 */
+}
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 025/120] MIPS: PS2: Define interrupt controller, DMA and timer IRQs
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (23 preceding siblings ...)
  2019-09-01 15:46 ` [PATCH 024/120] MIPS: PS2: Define PlayStation 2 I/O port, ROM and RAM address spaces Fredrik Noring
@ 2019-09-01 15:47 ` Fredrik Noring
  2019-09-01 15:47 ` [PATCH 026/120] MIPS: PS2: Interrupt controller (INTC) IRQ support Fredrik Noring
                   ` (95 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:47 UTC (permalink / raw)
  To: linux-mips

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

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

References:

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

[2] Ibid. p. 55.

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 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

diff --git a/arch/mips/include/asm/mach-ps2/irq.h b/arch/mips/include/asm/mach-ps2/irq.h
new file mode 100644
index 000000000000..d6f72a7e37a3
--- /dev/null
+++ b/arch/mips/include/asm/mach-ps2/irq.h
@@ -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 */
diff --git a/arch/mips/ps2/Makefile b/arch/mips/ps2/Makefile
index 24d537d2fb9f..d5d089c61381 100644
--- a/arch/mips/ps2/Makefile
+++ b/arch/mips/ps2/Makefile
@@ -1 +1,2 @@
+obj-y		+= irq.o
 obj-y		+= memory.o
diff --git a/arch/mips/ps2/irq.c b/arch/mips/ps2/irq.c
new file mode 100644
index 000000000000..09047e128ce8
--- /dev/null
+++ b/arch/mips/ps2/irq.c
@@ -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 */
+}
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 026/120] MIPS: PS2: Interrupt controller (INTC) IRQ support
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (24 preceding siblings ...)
  2019-09-01 15:47 ` [PATCH 025/120] MIPS: PS2: Define interrupt controller, DMA and timer IRQs Fredrik Noring
@ 2019-09-01 15:47 ` Fredrik Noring
  2019-09-01 15:47 ` [PATCH 027/120] MIPS: PS2: DMAC: Define DMA controller registers Fredrik Noring
                   ` (94 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:47 UTC (permalink / raw)
  To: linux-mips

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
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

diff --git a/arch/mips/include/asm/mach-ps2/irq.h b/arch/mips/include/asm/mach-ps2/irq.h
index d6f72a7e37a3..071c8139dabe 100644
--- a/arch/mips/include/asm/mach-ps2/irq.h
+++ b/arch/mips/include/asm/mach-ps2/irq.h
@@ -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 */
diff --git a/arch/mips/ps2/Makefile b/arch/mips/ps2/Makefile
index d5d089c61381..ccdfb80c9f03 100644
--- a/arch/mips/ps2/Makefile
+++ b/arch/mips/ps2/Makefile
@@ -1,2 +1,3 @@
+obj-y		+= intc-irq.o
 obj-y		+= irq.o
 obj-y		+= memory.o
diff --git a/arch/mips/ps2/intc-irq.c b/arch/mips/ps2/intc-irq.c
new file mode 100644
index 000000000000..36cdc3dd31ca
--- /dev/null
+++ b/arch/mips/ps2/intc-irq.c
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 Interrupt controller (INTC) IRQs
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include <asm/io.h>
+#include <asm/irq_cpu.h>
+#include <asm/mipsregs.h>
+
+#include <asm/mach-ps2/irq.h>
+
+static void intc_reverse_mask(struct irq_data *data)
+{
+	outl(BIT(data->irq - IRQ_INTC), INTC_MASK);
+}
+
+static void intc_mask_ack(struct irq_data *data)
+{
+	const unsigned int bit = BIT(data->irq - IRQ_INTC);
+
+	outl(bit, INTC_MASK);
+	outl(bit, INTC_STAT);
+}
+
+#define INTC_IRQ_TYPE(irq_, name_)				\
+	{							\
+		.irq = irq_,					\
+		.irq_chip = {					\
+			.name = name_,				\
+			.irq_unmask = intc_reverse_mask,	\
+			.irq_mask = intc_reverse_mask,		\
+			.irq_mask_ack = intc_mask_ack,		\
+		}						\
+	}
+
+static struct {
+	unsigned int irq;
+	struct irq_chip irq_chip;
+} intc_irqs[] = {
+	INTC_IRQ_TYPE(IRQ_INTC_GS,     "INTC GS"),
+	INTC_IRQ_TYPE(IRQ_INTC_SBUS,   "INTC SBUS"),
+	INTC_IRQ_TYPE(IRQ_INTC_VB_ON,  "INTC VB on"),
+	INTC_IRQ_TYPE(IRQ_INTC_VB_OFF, "INTC VB off"),
+	INTC_IRQ_TYPE(IRQ_INTC_VIF0,   "INTC VIF0"),
+	INTC_IRQ_TYPE(IRQ_INTC_VIF1,   "INTC VIF1"),
+	INTC_IRQ_TYPE(IRQ_INTC_VU0,    "INTC VU0"),
+	INTC_IRQ_TYPE(IRQ_INTC_VU1,    "INTC VU1"),
+	INTC_IRQ_TYPE(IRQ_INTC_IPU,    "INTC IPU"),
+	INTC_IRQ_TYPE(IRQ_INTC_TIMER0, "INTC timer0"),
+	INTC_IRQ_TYPE(IRQ_INTC_TIMER1, "INTC timer1"),
+	INTC_IRQ_TYPE(IRQ_INTC_TIMER2, "INTC timer2"),
+	INTC_IRQ_TYPE(IRQ_INTC_TIMER3, "INTC timer3"),
+	INTC_IRQ_TYPE(IRQ_INTC_SFIFO,  "INTC SFIFO"),
+	INTC_IRQ_TYPE(IRQ_INTC_VU0WD,  "INTC VU0WD"),
+	INTC_IRQ_TYPE(IRQ_INTC_PGPU,   "INTC PGPU"),
+};
+
+static irqreturn_t intc_cascade(int irq, void *data)
+{
+	unsigned int pending, irq_intc;
+	irqreturn_t status = IRQ_NONE;
+
+	for (pending = inl(INTC_STAT); pending; pending &= ~BIT(irq_intc)) {
+		irq_intc = __fls(pending);
+
+		if (generic_handle_irq(irq_intc + IRQ_INTC) < 0)
+			spurious_interrupt();
+		else
+			status = IRQ_HANDLED;
+	}
+
+	return status;
+}
+
+static struct irqaction cascade_intc_irqaction = {
+	.name = "INTC cascade",
+	.handler = intc_cascade,
+};
+
+int __init intc_irq_init(void)
+{
+	size_t i;
+	int err;
+
+	/* Clear mask and status registers */
+	outl(inl(INTC_MASK), INTC_MASK);
+	outl(inl(INTC_STAT), INTC_STAT);
+
+	for (i = 0; i < ARRAY_SIZE(intc_irqs); i++)
+		irq_set_chip_and_handler(intc_irqs[i].irq,
+			&intc_irqs[i].irq_chip, handle_level_irq);
+
+	/* FIXME: Is HARDIRQS_SW_RESEND needed? Are these edge types needed? */
+	irq_set_irq_type(IRQ_INTC_GS, IRQ_TYPE_EDGE_FALLING);
+	irq_set_irq_type(IRQ_INTC_SBUS, IRQ_TYPE_EDGE_FALLING);
+	irq_set_irq_type(IRQ_INTC_VB_ON, IRQ_TYPE_EDGE_RISING);
+	irq_set_irq_type(IRQ_INTC_VB_OFF, IRQ_TYPE_EDGE_FALLING);
+
+	err = setup_irq(IRQ_C0_INTC, &cascade_intc_irqaction);
+	if (err)
+		pr_err("irq: Failed to setup INTC IRQs (err = %d)\n", err);
+
+	return err;
+}
diff --git a/arch/mips/ps2/irq.c b/arch/mips/ps2/irq.c
index 09047e128ce8..935171a1e3bd 100644
--- a/arch/mips/ps2/irq.c
+++ b/arch/mips/ps2/irq.c
@@ -17,6 +17,8 @@
 void __init arch_init_irq(void)
 {
 	mips_cpu_irq_init();
+
+	intc_irq_init();
 }
 
 asmlinkage void plat_irq_dispatch(void)
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 027/120] MIPS: PS2: DMAC: Define DMA controller registers
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (25 preceding siblings ...)
  2019-09-01 15:47 ` [PATCH 026/120] MIPS: PS2: Interrupt controller (INTC) IRQ support Fredrik Noring
@ 2019-09-01 15:47 ` Fredrik Noring
  2019-09-01 15:47 ` [PATCH 028/120] MIPS: PS2: DMAC: Define tag structures Fredrik Noring
                   ` (93 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:47 UTC (permalink / raw)
  To: linux-mips

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

References:

[1] "EE User's Manual", version 6.0, Sony Computer Entertainment Inc.,
    pp. 23-24, 41-80.

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 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

diff --git a/arch/mips/include/asm/mach-ps2/dmac.h b/arch/mips/include/asm/mach-ps2/dmac.h
new file mode 100644
index 000000000000..30a0f72eeab3
--- /dev/null
+++ b/arch/mips/include/asm/mach-ps2/dmac.h
@@ -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 */
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 028/120] MIPS: PS2: DMAC: Define tag structures
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (26 preceding siblings ...)
  2019-09-01 15:47 ` [PATCH 027/120] MIPS: PS2: DMAC: Define DMA controller registers Fredrik Noring
@ 2019-09-01 15:47 ` Fredrik Noring
  2019-09-01 15:48 ` [PATCH 029/120] MIPS: PS2: DMAC: IRQ support Fredrik Noring
                   ` (92 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:47 UTC (permalink / raw)
  To: linux-mips

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

References:

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/mach-ps2/dmac.h | 65 +++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)

diff --git a/arch/mips/include/asm/mach-ps2/dmac.h b/arch/mips/include/asm/mach-ps2/dmac.h
index 30a0f72eeab3..fb4c52ad9ad5 100644
--- a/arch/mips/include/asm/mach-ps2/dmac.h
+++ b/arch/mips/include/asm/mach-ps2/dmac.h
@@ -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 */
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 029/120] MIPS: PS2: DMAC: IRQ support
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (27 preceding siblings ...)
  2019-09-01 15:47 ` [PATCH 028/120] MIPS: PS2: DMAC: Define tag structures Fredrik Noring
@ 2019-09-01 15:48 ` Fredrik Noring
  2019-09-01 15:48 ` [PATCH 030/120] MIPS: PS2: Timer support Fredrik Noring
                   ` (91 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:48 UTC (permalink / raw)
  To: linux-mips

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 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

diff --git a/arch/mips/include/asm/mach-ps2/irq.h b/arch/mips/include/asm/mach-ps2/irq.h
index 071c8139dabe..16c96aa7ca09 100644
--- a/arch/mips/include/asm/mach-ps2/irq.h
+++ b/arch/mips/include/asm/mach-ps2/irq.h
@@ -72,5 +72,6 @@
 #define IRQ_C0_IRQ7	55
 
 int __init intc_irq_init(void);
+int __init dmac_irq_init(void);
 
 #endif /* __ASM_MACH_PS2_IRQ_H */
diff --git a/arch/mips/ps2/Makefile b/arch/mips/ps2/Makefile
index ccdfb80c9f03..1e6406f42b3a 100644
--- a/arch/mips/ps2/Makefile
+++ b/arch/mips/ps2/Makefile
@@ -1,3 +1,4 @@
+obj-y		+= dmac-irq.o
 obj-y		+= intc-irq.o
 obj-y		+= irq.o
 obj-y		+= memory.o
diff --git a/arch/mips/ps2/dmac-irq.c b/arch/mips/ps2/dmac-irq.c
new file mode 100644
index 000000000000..8bb75034fd32
--- /dev/null
+++ b/arch/mips/ps2/dmac-irq.c
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 DMA controller (DMAC) IRQs
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include <asm/io.h>
+#include <asm/irq_cpu.h>
+#include <asm/mipsregs.h>
+
+#include <asm/mach-ps2/dmac.h>
+#include <asm/mach-ps2/irq.h>
+
+static void dmac_reverse_mask(struct irq_data *data)
+{
+	outl(BIT(16 + data->irq - IRQ_DMAC), DMAC_STAT_MASK);
+}
+
+static void dmac_mask_ack(struct irq_data *data)
+{
+	const unsigned int bit = BIT(data->irq - IRQ_DMAC);
+
+	outl((bit << 16) | bit, DMAC_STAT_MASK);
+}
+
+#define DMAC_IRQ_TYPE(irq_, name_)				\
+	{							\
+		.irq = irq_,					\
+		.irq_chip = {					\
+			.name = name_,				\
+			.irq_unmask = dmac_reverse_mask,	\
+			.irq_mask = dmac_reverse_mask,		\
+			.irq_mask_ack = dmac_mask_ack		\
+		}						\
+	}
+
+static struct {
+	unsigned int irq;
+	struct irq_chip irq_chip;
+} dmac_irqs[] = {
+	DMAC_IRQ_TYPE(IRQ_DMAC_VIF0, "DMAC VIF0"),
+	DMAC_IRQ_TYPE(IRQ_DMAC_VIF1, "DMAC VIF1"),
+	DMAC_IRQ_TYPE(IRQ_DMAC_GIF,  "DMAC GIF"),
+	DMAC_IRQ_TYPE(IRQ_DMAC_FIPU, "DMAC fromIPU"),
+	DMAC_IRQ_TYPE(IRQ_DMAC_TIPU, "DMAC toIPU"),
+	DMAC_IRQ_TYPE(IRQ_DMAC_SIF0, "DMAC SIF0"),
+	DMAC_IRQ_TYPE(IRQ_DMAC_SIF1, "DMAC SIF1"),
+	DMAC_IRQ_TYPE(IRQ_DMAC_SIF2, "DMAC SIF2"),
+	DMAC_IRQ_TYPE(IRQ_DMAC_FSPR, "DMAC fromSPR"),
+	DMAC_IRQ_TYPE(IRQ_DMAC_TSPR, "DMAC toSPR"),
+	DMAC_IRQ_TYPE(IRQ_DMAC_S,    "DMAC stall"),
+	DMAC_IRQ_TYPE(IRQ_DMAC_ME,   "DMAC MFIFO empty"),
+	DMAC_IRQ_TYPE(IRQ_DMAC_BE,   "DMAC bus error"),
+};
+
+static irqreturn_t dmac_cascade(int irq, void *data)
+{
+	unsigned int pending = inl(DMAC_STAT_MASK) & 0xffff;
+
+	if (!pending)
+		return IRQ_NONE;
+
+	while (pending) {
+		const unsigned int irq_dmac = __fls(pending);
+
+		if (generic_handle_irq(irq_dmac + IRQ_DMAC) < 0)
+			spurious_interrupt();
+		pending &= ~BIT(irq_dmac);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static struct irqaction cascade_dmac_irqaction = {
+	.name = "DMAC cascade",
+	.handler = dmac_cascade,
+};
+
+int __init dmac_irq_init(void)
+{
+	size_t i;
+	int err;
+
+	outl(inl(DMAC_STAT_MASK), DMAC_STAT_MASK); /* Clear status register */
+
+	for (i = 0; i < ARRAY_SIZE(dmac_irqs); i++)
+		irq_set_chip_and_handler(dmac_irqs[i].irq,
+			&dmac_irqs[i].irq_chip, handle_level_irq);
+
+	err = setup_irq(IRQ_C0_DMAC, &cascade_dmac_irqaction);
+	if (err)
+		pr_err("irq: Failed to setup DMAC IRQs (err = %d)\n", err);
+
+	return err;
+}
diff --git a/arch/mips/ps2/irq.c b/arch/mips/ps2/irq.c
index 935171a1e3bd..7c656e3735a1 100644
--- a/arch/mips/ps2/irq.c
+++ b/arch/mips/ps2/irq.c
@@ -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)
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 030/120] MIPS: PS2: Timer support
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (28 preceding siblings ...)
  2019-09-01 15:48 ` [PATCH 029/120] MIPS: PS2: DMAC: IRQ support Fredrik Noring
@ 2019-09-01 15:48 ` Fredrik Noring
  2019-09-01 15:48 ` [PATCH 031/120] MIPS: PS2: SCMD: System command support Fredrik Noring
                   ` (90 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:48 UTC (permalink / raw)
  To: linux-mips

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

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

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

References:

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/ps2/Makefile |   1 +
 arch/mips/ps2/time.c   | 153 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 154 insertions(+)
 create mode 100644 arch/mips/ps2/time.c

diff --git a/arch/mips/ps2/Makefile b/arch/mips/ps2/Makefile
index 1e6406f42b3a..2015870f9fe7 100644
--- a/arch/mips/ps2/Makefile
+++ b/arch/mips/ps2/Makefile
@@ -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
diff --git a/arch/mips/ps2/time.c b/arch/mips/ps2/time.c
new file mode 100644
index 000000000000..4979679bc909
--- /dev/null
+++ b/arch/mips/ps2/time.c
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 timer functions
+ *
+ * Copyright (C) 2010-2013 Jürgen Urban
+ * Copyright (C) 2017-2019 Fredrik Noring
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel_stat.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+
+#include <asm/bootinfo.h>
+#include <asm/time.h>
+#include <asm/mipsregs.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/mach-ps2/irq.h>
+
+#define CPU_FREQ		294912000	/* CPU clock frequency (Hz) */
+#define BUS_CLOCK		(CPU_FREQ/2)	/* Bus clock frequency (Hz) */
+#define TM_COMPARE_VALUE	(BUS_CLOCK/256/HZ)  /* To generate HZ event */
+
+/*
+ * The Emotion Engine has four independent timers with 16-bit counters. The
+ * bus clock or an external (H-BLANK or V-BLANK) clock performs 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.
+ */
+
+#define T0_COUNT		0x10000000	/* Timer 0 counter value */
+#define T0_MODE			0x10000010	/* Timer 0 mode/status */
+#define T0_COMP			0x10000020	/* Timer 0 compare value */
+#define T0_HOLD			0x10000030	/* Timer 0 hold value */
+
+#define T1_COUNT		0x10000800	/* Timer 1 counter value */
+#define T1_MODE			0x10000810	/* Timer 1 mode/status */
+#define T1_COMP			0x10000820	/* Timer 1 compare value */
+#define T1_HOLD			0x10000830	/* Timer 1 hold value */
+
+#define T2_COUNT		0x10001000	/* Timer 2 counter value */
+#define T2_MODE			0x10001010	/* Timer 2 mode/status */
+#define T2_COMP			0x10001020	/* Timer 2 compare value */
+
+#define T3_COUNT		0x10001800	/* Timer 3 counter value */
+#define T3_MODE			0x10001810	/* Timer 3 mode/status */
+#define T3_COMP			0x10001820	/* Timer 3 compare value */
+
+#define TM_MODE_CLKS_BUSCLK	(0 << 0) /* BUSCLK (147.456 MHz) */
+#define TM_MODE_CLKS_BUSCLK_16	(1 << 0) /* 1/16 of BUSCLK */
+#define TM_MODE_CLKS_BUSCLK_256	(2 << 0) /* 1/256 of BUSCLK */
+#define TM_MODE_CLKS_EXTERNAL	(3 << 0) /* External clock (V-BLANK) */
+#define TM_MODE_GATE_DISABLE	(0 << 2) /* Gate function is not used */
+#define TM_MODE_GATE_ENABLE	(1 << 2) /* Gate function is used */
+#define TM_MODE_GATS_H_BLANK	(0 << 3) /* H-BLANK (disabled if CLKS is 3) */
+#define TM_MODE_GATS_V_BLANK	(1 << 3) /* V-BLANK */
+#define TM_MODE_GATM_WHILE_LOW	(0 << 4) /* Count while gate signal is low */
+#define TM_MODE_GATM_RESET_RISE	(1 << 4) /* Reset and start on rising edge */
+#define TM_MODE_GATM_RESET_FALL	(2 << 4) /* Reset and start on falling edge */
+#define TM_MODE_GATM_RESET_BOTH	(3 << 4) /* Reset and start on both edges */
+#define TM_MODE_ZRET_KEEP	(0 << 6) /* Keep counting ignoring reference */
+#define TM_MODE_ZRET_CLEAR	(1 << 6) /* Zero counter reaching reference */
+#define TM_MODE_CUE_STOP	(0 << 7) /* Stop counting */
+#define TM_MODE_CUE_START	(1 << 7) /* Start counting */
+#define TM_MODE_CMPE_DISABLE	(0 << 8) /* Disable compare interrupts */
+#define TM_MODE_CMPE_ENABLE	(1 << 8) /* Interrupt reaching reference */
+#define TM_MODE_OVFE_DISABLE	(0 << 9) /* Disable overflow interrupts */
+#define TM_MODE_OVFE_ENABLE	(1 << 9) /* Interrupt on overflow */
+
+/*
+ * The equal status flag bit is 1 when a compare-interrupt
+ * has occured. Write 1 to clear.
+ */
+#define TM_MODE_EQUAL_FLAG	(1 << 10)
+
+/*
+ * The overflow status flag bit is 1 when an overflow-interrupt
+ * has occured. Write 1 to clear.
+ */
+#define TM_MODE_OVERFLOW_FLAG	(1 << 11)
+
+static irqreturn_t ps2_timer_interrupt(int irq, void *dev_id)
+{
+	struct clock_event_device *cd = dev_id;
+
+	outl(inl(T0_MODE), T0_MODE); /* Clear the interrupt */
+
+	cd->event_handler(cd);
+
+	return IRQ_HANDLED;
+}
+
+static int timer0_periodic(struct clock_event_device *evt)
+{
+	outl(0, T0_COUNT);
+	outl(TM_COMPARE_VALUE, T0_COMP);
+	outl(TM_MODE_CLKS_BUSCLK_256 | TM_MODE_ZRET_CLEAR | TM_MODE_CUE_START |
+		TM_MODE_CMPE_ENABLE | TM_MODE_EQUAL_FLAG, T0_MODE);
+
+	return 0;
+}
+
+static int timer0_shutdown(struct clock_event_device *evt)
+{
+	outl(0, T0_MODE); /* Stop timer */
+
+	return 0;
+}
+
+static struct irqaction timer0_irqaction = {
+	.handler	= ps2_timer_interrupt,
+	.flags		= IRQF_PERCPU | IRQF_TIMER,
+	.name		= "intc-timer0",
+};
+
+static struct clock_event_device timer0_clockevent_device = {
+	.name		= "timer0",
+	/* FIXME: Timer is also able to provide CLOCK_EVT_FEAT_ONESHOT. */
+	.features	= CLOCK_EVT_FEAT_PERIODIC,
+
+	/* FIXME: .mult, .shift, .max_delta_ns and .min_delta_ns left uninitialized */
+
+	.rating		= 300, /* FIXME: Check value. */
+	.irq		= IRQ_INTC_TIMER0,
+	.set_state_periodic	= timer0_periodic,
+	.set_state_shutdown	= timer0_shutdown,
+};
+
+void __init plat_time_init(void)
+{
+	/* Add timer 0 as clock event source. */
+	timer0_clockevent_device.cpumask = cpumask_of(smp_processor_id());
+	clockevents_register_device(&timer0_clockevent_device);
+	timer0_irqaction.dev_id = &timer0_clockevent_device;
+	setup_irq(IRQ_INTC_TIMER0, &timer0_irqaction);
+
+	/* FIXME: Timer 1 is free and can also be configured as clock event source. */
+
+	/* Setup frequency for IP7 timer interrupt. */
+	mips_hpt_frequency = CPU_FREQ;
+}
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 031/120] MIPS: PS2: SCMD: System command support
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (29 preceding siblings ...)
  2019-09-01 15:48 ` [PATCH 030/120] MIPS: PS2: Timer support Fredrik Noring
@ 2019-09-01 15:48 ` Fredrik Noring
  2019-09-01 15:48 ` [PATCH 032/120] MIPS: PS2: SCMD: System power off command Fredrik Noring
                   ` (89 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:48 UTC (permalink / raw)
  To: linux-mips

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 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

diff --git a/arch/mips/include/asm/mach-ps2/scmd.h b/arch/mips/include/asm/mach-ps2/scmd.h
new file mode 100644
index 000000000000..b2b98dbaec9b
--- /dev/null
+++ b/arch/mips/include/asm/mach-ps2/scmd.h
@@ -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 */
diff --git a/arch/mips/ps2/Makefile b/arch/mips/ps2/Makefile
index 2015870f9fe7..d90d3e06387f 100644
--- a/arch/mips/ps2/Makefile
+++ b/arch/mips/ps2/Makefile
@@ -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
diff --git a/arch/mips/ps2/scmd.c b/arch/mips/ps2/scmd.c
new file mode 100644
index 000000000000..0516766ffdba
--- /dev/null
+++ b/arch/mips/ps2/scmd.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 system commands
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#include <linux/build_bug.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/sched/signal.h>
+
+#include <asm/mach-ps2/scmd.h>
+
+/**
+ * completed - poll for condition to happen, or timeout
+ * @condition: function to poll for condition
+ *
+ * Return: %true if condition happened, else %false on timeout
+ */
+static bool completed(bool (*condition)(void))
+{
+	const unsigned long timeout = jiffies + 5*HZ;
+
+	do {
+		if (condition())
+			return true;
+
+		msleep(1);
+	} while (time_is_after_jiffies(timeout));
+
+	return false;
+}
+
+/**
+ * scmd_status - read system command status register
+ *
+ * Return: system command status register value
+ */
+static u8 scmd_status(void)
+{
+	return inb(SCMD_STATUS);
+}
+
+/**
+ * scmd_write - write system command data
+ * @data: pointer to data to write
+ * @size: number of bytes to write
+ */
+static void scmd_write(const u8 *data, size_t size)
+{
+	size_t i;
+
+	for (i = 0; i < size; i++)
+		outb(data[i], SCMD_SEND);
+}
+
+/**
+ * scmd_ready - can the system receive a command or has finished processing?
+ *
+ * Return: %true if the system is ready to receive a command, or has finished
+ * 	processing a previous command, otherwise %false
+ */
+static bool scmd_ready(void)
+{
+	return (scmd_status() & SCMD_STATUS_BUSY) == 0;
+}
+
+/**
+ * scmd_wait - wait for the system command to become ready
+ *
+ * Return: %true if the system command is ready, else %false on timeout
+ */
+static bool scmd_wait(void)
+{
+	return completed(scmd_ready);
+}
+
+/**
+ * scmd_data - is command data available to be read from the system?
+ *
+ * Return: %true if system data is readable, else %false
+ */
+static bool scmd_data(void)
+{
+	return (scmd_status() & SCMD_STATUS_EMPTY) == 0;
+}
+
+/**
+ * scmd_flush - read and discard all available command data from the system
+ *
+ * Return: %true if something was read, else %false
+ */
+static bool scmd_flush(void)
+{
+	bool flushed;
+
+	for (flushed = false; scmd_data(); flushed = true)
+		inb(SCMD_RECV);
+
+	return flushed;
+}
+
+/**
+ * scmd_read - read command data from the system
+ * @data: pointer to data to read
+ * @size: maximum number of bytes to read
+ *
+ * Return: actual number of bytes read
+ */
+static size_t scmd_read(u8 *data, size_t size)
+{
+	size_t r;
+
+	for (r = 0; r < size && scmd_data(); r++)
+		data[r] = inb(SCMD_RECV);
+
+	return r;
+}
+
+/**
+ * scmd - general system command function
+ * @cmd: system command
+ * @send: pointer to command data to send
+ * @send_size: size in bytes of command data to send
+ * @recv: pointer to command data to receive
+ * @recv_size: exact size in bytes of command data to receive
+ *
+ * Context: sleep
+ * Return: 0 on success, else a negative error number
+ */
+int scmd(enum scmd_cmd cmd,
+	const void *send, size_t send_size,
+	void *recv, size_t recv_size)
+{
+	static DEFINE_MUTEX(scmd_lock);
+	int err = 0;
+	size_t r;
+
+	mutex_lock(&scmd_lock);
+
+	if (!scmd_ready()) {
+		pr_warn("%s: Unexpectedly busy preceding command %d\n",
+			__func__, cmd);
+
+		if (!scmd_wait()) {
+			err = -EBUSY;
+			goto out_err;
+		}
+	}
+	if (scmd_flush())
+		pr_warn("%s: Unexpected data preceding command %d\n",
+			__func__, cmd);
+
+	scmd_write(send, send_size);
+	outb(cmd, SCMD_COMMAND);
+
+	if (!scmd_wait()) {
+		err = -EIO;
+		goto out_err;
+	}
+	r = scmd_read(recv, recv_size);
+	if (r == recv_size && scmd_flush())
+		pr_warn("%s: Unexpected data following command %d\n",
+			__func__, cmd);
+	if (r != recv_size)
+		err = -EIO;
+
+out_err:
+	mutex_unlock(&scmd_lock);
+	return err;
+}
+EXPORT_SYMBOL_GPL(scmd);
+
+MODULE_DESCRIPTION("PlayStation 2 system commands");
+MODULE_AUTHOR("Fredrik Noring");
+MODULE_LICENSE("GPL");
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 032/120] MIPS: PS2: SCMD: System power off command
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (30 preceding siblings ...)
  2019-09-01 15:48 ` [PATCH 031/120] MIPS: PS2: SCMD: System command support Fredrik Noring
@ 2019-09-01 15:48 ` Fredrik Noring
  2019-09-01 15:48 ` [PATCH 033/120] MIPS: PS2: SCMD: Read system machine name command Fredrik Noring
                   ` (88 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:48 UTC (permalink / raw)
  To: linux-mips

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/mach-ps2/scmd.h |  2 ++
 arch/mips/ps2/scmd.c                  | 29 +++++++++++++++++++++++++++
 2 files changed, 31 insertions(+)

diff --git a/arch/mips/include/asm/mach-ps2/scmd.h b/arch/mips/include/asm/mach-ps2/scmd.h
index b2b98dbaec9b..9e0135655ce8 100644
--- a/arch/mips/include/asm/mach-ps2/scmd.h
+++ b/arch/mips/include/asm/mach-ps2/scmd.h
@@ -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 */
diff --git a/arch/mips/ps2/scmd.c b/arch/mips/ps2/scmd.c
index 0516766ffdba..5544c7dfb7b4 100644
--- a/arch/mips/ps2/scmd.c
+++ b/arch/mips/ps2/scmd.c
@@ -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");
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 033/120] MIPS: PS2: SCMD: Read system machine name command
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (31 preceding siblings ...)
  2019-09-01 15:48 ` [PATCH 032/120] MIPS: PS2: SCMD: System power off command Fredrik Noring
@ 2019-09-01 15:48 ` Fredrik Noring
  2019-09-01 15:49 ` [PATCH 034/120] MIPS: PS2: SCMD: Read system command for the real-time clock (RTC) Fredrik Noring
                   ` (87 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:48 UTC (permalink / raw)
  To: linux-mips

An example of machine name is SCPH-50004.

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/mach-ps2/scmd.h | 12 ++++++
 arch/mips/ps2/scmd.c                  | 62 +++++++++++++++++++++++++++
 2 files changed, 74 insertions(+)

diff --git a/arch/mips/include/asm/mach-ps2/scmd.h b/arch/mips/include/asm/mach-ps2/scmd.h
index 9e0135655ce8..2389b937eec0 100644
--- a/arch/mips/include/asm/mach-ps2/scmd.h
+++ b/arch/mips/include/asm/mach-ps2/scmd.h
@@ -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 */
diff --git a/arch/mips/ps2/scmd.c b/arch/mips/ps2/scmd.c
index 5544c7dfb7b4..aecd4f35e312 100644
--- a/arch/mips/ps2/scmd.c
+++ b/arch/mips/ps2/scmd.c
@@ -175,6 +175,12 @@ int scmd(enum scmd_cmd cmd,
 }
 EXPORT_SYMBOL_GPL(scmd);
 
+static int scmd_send_byte(enum scmd_cmd cmd, u8 send_byte,
+	void *recv, size_t recv_size)
+{
+	return scmd(cmd, &send_byte, sizeof(send_byte), recv, recv_size);
+}
+
 /**
  * scmd_power_off - system command to power off the system
  *
@@ -204,6 +210,62 @@ int scmd_power_off(void)
 }
 EXPORT_SYMBOL_GPL(scmd_power_off);
 
+/**
+ * scmd_read_machine_name - system command to read the machine name
+ *
+ * An example of machine name is SCPH-50004.
+ *
+ * Machines SCPH-10000 and SCPH-15000 do not implement this command. Late
+ * SCPH-10000 and all SCPH-15000 have the name in rom0:OSDSYS instead.
+ *
+ * Context: sleep
+ * Return: the machine name, or the empty string on failure
+ */
+struct scmd_machine_name scmd_read_machine_name(void)
+{
+	struct scmd_machine_name machine = { .name = "" };
+	struct __attribute__ ((packed)) {
+		u8 status;
+		char name[8];
+	} buffer0, buffer8;
+	int err;
+
+	BUILD_BUG_ON(sizeof(buffer0) != 9 ||
+		     sizeof(buffer8) != 9);
+
+	/* The machine name comes in two halves that need to be combined. */
+
+	err = scmd_send_byte(scmd_cmd_read_machine_name, 0,
+		&buffer0, sizeof(buffer0));
+	if (err < 0) {
+		pr_debug("%s: Read failed with %d at 0\n", __func__, err);
+		goto out_err;
+	}
+
+	err = scmd_send_byte(scmd_cmd_read_machine_name, 8,
+		&buffer8, sizeof(buffer8));
+	if (err < 0) {
+		pr_debug("%s: Read failed with %d at 8\n", __func__, err);
+		goto out_err;
+	}
+
+	if (buffer0.status != 0 ||
+	    buffer8.status != 0) {
+		pr_debug("%s: Invalid results with statuses 0x%x and 0x%x\n",
+			__func__, buffer0.status, buffer8.status);
+		goto out_err;
+	}
+
+	BUILD_BUG_ON(sizeof(machine.name) < 16);
+	memcpy(&machine.name[0], buffer0.name, 8);
+	memcpy(&machine.name[8], buffer8.name, 8);
+	machine.name[15] = '\0';
+
+out_err:
+	return machine;
+}
+EXPORT_SYMBOL_GPL(scmd_read_machine_name);
+
 MODULE_DESCRIPTION("PlayStation 2 system commands");
 MODULE_AUTHOR("Fredrik Noring");
 MODULE_LICENSE("GPL");
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 034/120] MIPS: PS2: SCMD: Read system command for the real-time clock (RTC)
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (32 preceding siblings ...)
  2019-09-01 15:48 ` [PATCH 033/120] MIPS: PS2: SCMD: Read system machine name command Fredrik Noring
@ 2019-09-01 15:49 ` Fredrik Noring
  2019-09-01 15:49 ` [PATCH 035/120] MIPS: PS2: SCMD: Set " Fredrik Noring
                   ` (86 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:49 UTC (permalink / raw)
  To: linux-mips

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/mach-ps2/scmd.h |  5 +++
 arch/mips/ps2/scmd.c                  | 55 +++++++++++++++++++++++++++
 2 files changed, 60 insertions(+)

diff --git a/arch/mips/include/asm/mach-ps2/scmd.h b/arch/mips/include/asm/mach-ps2/scmd.h
index 2389b937eec0..352a921181b6 100644
--- a/arch/mips/include/asm/mach-ps2/scmd.h
+++ b/arch/mips/include/asm/mach-ps2/scmd.h
@@ -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 */
diff --git a/arch/mips/ps2/scmd.c b/arch/mips/ps2/scmd.c
index aecd4f35e312..34f0fe36bd3d 100644
--- a/arch/mips/ps2/scmd.c
+++ b/arch/mips/ps2/scmd.c
@@ -13,8 +13,14 @@
 #include <linux/sched.h>
 #include <linux/sched/signal.h>
 
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+
 #include <asm/mach-ps2/scmd.h>
 
+#define UTC_TO_JST (9*60*60)		/* UTC to Japan standard time */
+#define JST_TO_UTC (-UTC_TO_JST)	/* Japan standard time to UTC */
+
 /**
  * completed - poll for condition to happen, or timeout
  * @condition: function to poll for condition
@@ -266,6 +272,55 @@ struct scmd_machine_name scmd_read_machine_name(void)
 }
 EXPORT_SYMBOL_GPL(scmd_read_machine_name);
 
+/**
+ * scmd_read_rtc - system command to read the real-time clock (RTC)
+ * @t: pointer to store the time on a successful reading
+ *
+ * The hardware clock is designed to keep Japan standard time (JST), regardless
+ * of the region of the machine. This is adjusted in the driver so that the
+ * clock to the kernel appears to be kept in coordinated universal time (UTC).
+ * Tools such as hwclock should therefore read the clock in the UTC timescale.
+ *
+ * Context: sleep
+ * Return: 0 on success, else a negative error number
+ */
+int scmd_read_rtc(time64_t *t)
+{
+	struct __attribute__ ((packed)) {
+		u8 status;
+		u8 second;
+		u8 minute;
+		u8 hour;
+		u8 pad;
+		u8 day;
+		u8 month;
+		u8 year;
+	} rtc;
+	int err;
+
+	BUILD_BUG_ON(sizeof(rtc) != 8);
+	err = scmd(scmd_cmd_read_rtc, NULL, 0, &rtc, sizeof(rtc));
+	if (err < 0) {
+		pr_debug("%s: Read failed with %d at 0\n", __func__, err);
+		return err;
+	}
+	if (rtc.status != 0) {
+		pr_debug("%s: Invalid result with status 0x%x\n",
+			__func__, rtc.status);
+		return -EIO;
+	}
+
+	*t = mktime64(bcd2bin(rtc.year) + 2000,
+		      bcd2bin(rtc.month),
+		      bcd2bin(rtc.day),
+		      bcd2bin(rtc.hour),
+		      bcd2bin(rtc.minute),
+		      bcd2bin(rtc.second)) + JST_TO_UTC;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(scmd_read_rtc);
+
 MODULE_DESCRIPTION("PlayStation 2 system commands");
 MODULE_AUTHOR("Fredrik Noring");
 MODULE_LICENSE("GPL");
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 035/120] MIPS: PS2: SCMD: Set system command for the real-time clock (RTC)
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (33 preceding siblings ...)
  2019-09-01 15:49 ` [PATCH 034/120] MIPS: PS2: SCMD: Read system command for the real-time clock (RTC) Fredrik Noring
@ 2019-09-01 15:49 ` " Fredrik Noring
  2019-09-01 15:49 ` [PATCH 036/120] MIPS: PS2: ROM: Iterate over the files in a given ROM directory Fredrik Noring
                   ` (85 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:49 UTC (permalink / raw)
  To: linux-mips

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/mach-ps2/scmd.h |  4 ++
 arch/mips/ps2/scmd.c                  | 53 +++++++++++++++++++++++++++
 2 files changed, 57 insertions(+)

diff --git a/arch/mips/include/asm/mach-ps2/scmd.h b/arch/mips/include/asm/mach-ps2/scmd.h
index 352a921181b6..3ca106ec7da4 100644
--- a/arch/mips/include/asm/mach-ps2/scmd.h
+++ b/arch/mips/include/asm/mach-ps2/scmd.h
@@ -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 */
diff --git a/arch/mips/ps2/scmd.c b/arch/mips/ps2/scmd.c
index 34f0fe36bd3d..a9efb0e0a76e 100644
--- a/arch/mips/ps2/scmd.c
+++ b/arch/mips/ps2/scmd.c
@@ -321,6 +321,59 @@ int scmd_read_rtc(time64_t *t)
 }
 EXPORT_SYMBOL_GPL(scmd_read_rtc);
 
+/**
+ * scmd_set_rtc - system command to set the real-time clock (RTC)
+ * @t: the time to set
+ *
+ * The hardware clock is designed to keep Japan standard time (JST), regardless
+ * of the region of the machine. This is adjusted in the driver so that the
+ * clock to the kernel appears to be kept in coordinated universal time (UTC).
+ * Tools such as hwclock should therefore set the clock in the UTC timescale.
+
+ * Context: sleep
+ * Return: 0 on success, else a negative error number
+ */
+int scmd_set_rtc(time64_t t)
+{
+	struct __attribute__ ((packed)) {
+		u8 second;
+		u8 minute;
+		u8 hour;
+		u8 pad;
+		u8 day;
+		u8 month;
+		u8 year;
+	} rtc = { };
+	struct rtc_time tm;
+	u8 status;
+	int err;
+
+	rtc_time_to_tm(t + UTC_TO_JST, &tm);
+	rtc.second = bin2bcd(tm.tm_sec);
+	rtc.minute = bin2bcd(tm.tm_min);
+	rtc.hour   = bin2bcd(tm.tm_hour);
+	rtc.day    = bin2bcd(tm.tm_mday);
+	rtc.month  = bin2bcd(tm.tm_mon + 1);
+	rtc.year   = bin2bcd(tm.tm_year - 100);
+
+	BUILD_BUG_ON(sizeof(rtc) != 7);
+	err = scmd(scmd_cmd_write_rtc, &rtc, sizeof(rtc),
+		&status, sizeof(status));
+	if (err < 0) {
+		pr_debug("%s: Write failed with %d\n", __func__, err);
+		return err;
+	}
+
+	if (status != 0) {
+		pr_debug("%s: Invalid result with status 0x%x\n",
+			__func__, status);
+		return -EIO;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(scmd_set_rtc);
+
 MODULE_DESCRIPTION("PlayStation 2 system commands");
 MODULE_AUTHOR("Fredrik Noring");
 MODULE_LICENSE("GPL");
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 036/120] MIPS: PS2: ROM: Iterate over the files in a given ROM directory
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (34 preceding siblings ...)
  2019-09-01 15:49 ` [PATCH 035/120] MIPS: PS2: SCMD: Set " Fredrik Noring
@ 2019-09-01 15:49 ` Fredrik Noring
  2019-09-01 15:49 ` [PATCH 037/120] MIPS: PS2: ROM: Find ROM files with a given name, if they exist Fredrik Noring
                   ` (84 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:49 UTC (permalink / raw)
  To: linux-mips

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

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

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 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

diff --git a/arch/mips/include/asm/mach-ps2/rom.h b/arch/mips/include/asm/mach-ps2/rom.h
index 6760be183696..063c8b6acf15 100644
--- a/arch/mips/include/asm/mach-ps2/rom.h
+++ b/arch/mips/include/asm/mach-ps2/rom.h
@@ -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 */
diff --git a/arch/mips/ps2/Makefile b/arch/mips/ps2/Makefile
index d90d3e06387f..1101ad0d702b 100644
--- a/arch/mips/ps2/Makefile
+++ b/arch/mips/ps2/Makefile
@@ -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
diff --git a/arch/mips/ps2/rom.c b/arch/mips/ps2/rom.c
new file mode 100644
index 000000000000..12a57f24bd63
--- /dev/null
+++ b/arch/mips/ps2/rom.c
@@ -0,0 +1,501 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 read-only memory (ROM)
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+/**
+ * DOC: PlayStation 2 read-only memory (ROM) layout and handling
+ *
+ * All PlayStation 2 machines have at least two ROMs called ROM0 and ROM1.
+ * A ROM consists of concatenated files. All known ROMs have the three files
+ * named RESET, ROMDIR and EXTINFO at the very beginning of the ROM:
+ *
+ *	- RESET: The internal structure of this file is currently unknown.
+ *	- ROMDIR: Array of 16-byte &rom_dir_entry directory entries. The file
+ *		name of the last entry is empty and this file is designated
+ *		the terminating file.
+ *	- EXTINFO: Concatenated variable length extended information entries,
+ *		where &rom_dir_entry.extinfo.size gives the size in bytes of
+ *		each entry.
+ *
+ * Since the internal layout of the RESET file is unknown, this implementation
+ * searches for the ROMDIR file by looking after the ``"RESET"`` string that
+ * is always the first entry of the ROMDIR file directory structure.
+ *
+ * Then the ROMDIR and EXTINFO files, and finally the whole ROM, are validated.
+ * The ROM is accepted if no structural errors are found. Otherwise the ROM is
+ * discarded and an error message is printed.
+ *
+ * Example of a ROM0 directory structure, as decoded from the ROMDIR file:
+ *
+ *	=================== =========================== ===================
+ *	&rom_dir_entry.name &rom_dir_entry.extinfo.size &rom_dir_entry.size
+ *	              RESET                          12               10048
+ *	             ROMDIR                          80                1584
+ *	            EXTINFO                           0                2028
+ *	             ROMVER                           0                  16
+ *	               SBIN                          12               28576
+ *	               LOGO                          12               83604
+ *	          IOPBTCONF                           8                 234
+ *	          IOPBTCON2                           8                 195
+ *	             SYSMEM                          40                4625
+ *	           LOADCORE                          32                9597
+ *	                ...                         ...                 ...
+ *	            PS2LOGO                          12              216260
+ *	             OSDSYS                          12              336808
+ *	             KERNEL                          12               93736
+ *	                                              0                   0
+ *	=================== =========================== ===================
+ *
+ * In this example the RESET file is 10048 bytes, which means that the offset
+ * to the ROMDIR file is 10048 bytes. This is also the location of the
+ * ``"RESET"`` string. The size of the ROMDIR file is 1584 bytes, which means
+ * there are 1584 / 16 = 99 files, including the terminating file. Thus there
+ * are 98 normal files, excluding the terminating file.
+ *
+ * The sum of all &rom_dir_entry.extinfo.size correspond to the size of the
+ * EXTINFO file, which is 2028 bytes in this case. The sum of all
+ * &rom_dir_entry.size is the size of the ROM, which is 3900816 bytes in this
+ * case.
+ *
+ * Files are padded to align with 16 byte boundaries. The EXTINFO file, in
+ * this example, is therefore padded to 2032 bytes. Some files named ``-``
+ * contain zeros only and seem to be padding to align the following file to
+ * a specific address.
+ */
+
+#include <linux/build_bug.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#include <asm/io.h>
+
+#include <asm/mach-ps2/rom.h>
+
+struct rom_dir rom0_dir;
+struct rom_dir rom1_dir;
+EXPORT_SYMBOL_GPL(rom0_dir);
+EXPORT_SYMBOL_GPL(rom1_dir);
+
+/**
+ * struct rom - ROM object
+ * @name: name of ROM, for example "rom0" or "rom1"
+ * @base: virtual address of ROM
+ * @size: ROM size in bytes
+ */
+struct rom {
+	const char *name;
+	const void *base;
+	size_t size;
+};
+
+/**
+ * struct rom_dir_entry - raw 16-byte ROM directory entry of the ROMDIR file
+ * @name: file name; the empty string for the last terminating file
+ * @extinfo: extended ROM file information
+ * @extinfo.size: size in bytes of extended ROM file information
+ * @size: file size in bytes
+ */
+struct rom_dir_entry {
+	char name[10];
+	struct {
+		u16 size;
+	} extinfo;
+	u32 size;
+};
+
+/**
+ * rom_align_file_size - align ROM file size to 16 byte boundaries
+ * @size: possibly unaligned ROM size in bytes
+ *
+ * The aligned file size is used to obtain the offset to the subsequent file
+ * in a ROM.
+ *
+ * Return: padded file size in bytes, aligned to 16 byte boundaries
+ */
+static size_t rom_align_file_size(size_t size)
+{
+	return ALIGN(size, 16);
+}
+
+/**
+ * rom_next_extinfo_data - pointer to the EXTINFO entry for the subsequent file
+ * @file: file to find the next EXTINFO entry pointer for
+ *
+ * Return: pointer to the following EXTINFO entry
+ */
+static const void *rom_next_extinfo_data(const struct rom_file *file)
+{
+	const u8 *d = file->extinfo.data;
+
+	return &d[file->extinfo.size];
+}
+
+/**
+ * rom_next_data - pointer to the data for the subsequent file
+ * @file: file to find the next data pointer for
+ *
+ * Return: pointer to the data for the subsequent file
+ */
+static const void *rom_next_data(const struct rom_file *file)
+{
+	const u8 *d = file->data;
+
+	return &d[rom_align_file_size(file->size)];
+}
+
+/**
+ * rom_empty_dir - is the ROM directory empty?
+ * @dir: ROM directory to check
+ *
+ * Context: any
+ * Return: %true if the ROM directory is empty, else %false
+ */
+bool rom_empty_dir(const struct rom_dir dir)
+{
+	return !dir.size;
+}
+EXPORT_SYMBOL_GPL(rom_empty_dir);
+
+/**
+ * rom_terminating_file - is this a terminating ROM file?
+ * @file: ROM file to check
+ *
+ * Context: any
+ * Return: %true if the ROM file is a terminating entry, else %false
+ */
+bool rom_terminating_file(const struct rom_file file)
+{
+	return file.name[0] == '\0';
+}
+EXPORT_SYMBOL_GPL(rom_terminating_file);
+
+/**
+ * rom_next_file - advance to next ROM file, unless terminating file is given
+ * @file: ROM file to advance from
+ *
+ * Context: any
+ * Return: next ROM file, or a terminating file
+ */
+struct rom_file rom_next_file(const struct rom_file file)
+{
+	if (rom_terminating_file(file))
+		return file;
+
+	return (struct rom_file) {
+			.name = file.next->name,
+			.size = file.next->size,
+			.data = rom_next_data(&file),
+			.extinfo = {
+				.size = file.next->extinfo.size,
+				.data = rom_next_extinfo_data(&file)
+			},
+			.next = &file.next[file.next->name[0] != '\0' ? 1 : 0]
+		};
+}
+EXPORT_SYMBOL_GPL(rom_next_file);
+
+/**
+ * rom_first_file - first ROM file of a ROM directory
+ * @dir: ROM directory to retrieve the first file for
+ *
+ * Context: any
+ * Return: first ROM file, or a terminating file if the directory is empty
+ */
+struct rom_file rom_first_file(const struct rom_dir dir)
+{
+	if (rom_empty_dir(dir))
+		return (struct rom_file) { .name = "" };
+
+	return (struct rom_file) {
+			.name = dir.entries->name,
+			.size = dir.entries->size,
+			.data = dir.data,
+			.extinfo = {
+				.size = dir.entries->extinfo.size,
+				.data = dir.extinfo.data
+			},
+			.next = &dir.entries[1]
+		};
+}
+EXPORT_SYMBOL_GPL(rom_first_file);
+
+/**
+ * find_reset_string - find the offset to the ``"RESET"`` string, if it exists
+ * @rom: ROM to search in
+ *
+ * The ``"RESET"`` string that is always the first entry of the ROMDIR file
+ * directory structure.
+ *
+ * Return: byte offset to ``"RESET"``, or the size of the ROM on failure
+ */
+static loff_t __init find_reset_string(const struct rom rom)
+{
+	const char *s = rom.base;
+	size_t i;
+
+	for (i = 0; i + sizeof(struct rom_dir_entry) <= rom.size; i++)
+		if (s[i + 0] == 'R' &&
+		    s[i + 1] == 'E' &&
+		    s[i + 2] == 'S' &&
+		    s[i + 3] == 'E' &&
+		    s[i + 4] == 'T' &&
+		    s[i + 5] == '\0')
+			return i;
+
+	return rom.size;
+}
+
+/**
+ * rom_addr - pointer at offset within a given ROM object
+ * @rom: ROM object
+ * @offset: offset in bytes for the pointer location
+ * @size: size in bytes of the data at the given offset
+ *
+ * Return: ROM pointer, or %NULL if @size at the @offset is outside of the ROM
+ */
+static const void * __init rom_addr(
+	const struct rom rom, loff_t offset, size_t size)
+{
+	const u8 *b = rom.base;
+
+	return offset + size <= rom.size ? &b[offset] : NULL;
+}
+
+/**
+ * valid_rom_dir_entry_name - does the ROM directory entry has a valid name?
+ * @entry: ROM directory entry
+ *
+ * Return: %true if the ROM directory entry has a valid name, otherwise %false
+ */
+static bool __init valid_rom_dir_entry_name(const struct rom_dir_entry *entry)
+{
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(entry->name); i++)
+		if (entry->name[i] == '\0')
+			return true;
+
+	return false;
+}
+
+/**
+ * rom_dir_entry - ROM directory entry for a given ROM index
+ * @rom: ROM object
+ * @reset_offset: offset in bytes from ROM object start to the RESET file
+ * @entry_index: file index starting from zero
+ *
+ * Return: ROM directory entry for the given index
+ */
+static const struct rom_dir_entry * __init rom_dir_entry(
+	const struct rom rom, loff_t reset_offset, size_t entry_index)
+{
+	return rom_addr(rom,
+		reset_offset + entry_index * sizeof(struct rom_dir_entry),
+		sizeof(struct rom_dir_entry));
+}
+
+/**
+ * valid_rom_dir_header - is the ROM object valid?
+ * @rom: ROM object
+ * @reset_offset: offset in bytes from ROM object start to the RESET file
+ * @reset: the RESET ROM file
+ * @romdir: the ROMDIR ROM file
+ * @extinfo: the EXTINFO ROM file
+ *
+ * This verifies some of the assumptions that are made about read-only memories.
+ *
+ * Return: %true if the ROM appears to have a valid header, otherwise %false
+ */
+static bool __init valid_rom_dir_header(const struct rom rom,
+	const loff_t reset_offset,
+	const struct rom_dir_entry *reset,
+	const struct rom_dir_entry *romdir,
+	const struct rom_dir_entry *extinfo)
+{
+	size_t aligned_rom_header_size;
+
+	if (!reset || !romdir || !extinfo) {
+		pr_debug("%s: Missing RESET, ROMDIR or EXTINFO\n", rom.name);
+		return false;
+	}
+
+	if (strcmp(reset->name, "RESET") != 0 ||
+	    strcmp(romdir->name, "ROMDIR") != 0 ||
+	    strcmp(extinfo->name, "EXTINFO") != 0) {
+		pr_debug("%s: Misnamed RESET, ROMDIR or EXTINFO\n", rom.name);
+		return false;
+	}
+
+	if (rom_align_file_size(reset->size) != reset_offset) {
+		pr_debug("%s: Unaligned RESET %zu != %llu\n", rom.name,
+			rom_align_file_size(reset->size), reset_offset);
+		return false;
+	}
+
+	if (romdir->size % sizeof(struct rom_dir_entry) != 0) {
+		pr_debug("%s: ROMDIR unaligned %zu\n", rom.name, romdir->size);
+		return false;
+	}
+
+	aligned_rom_header_size =
+		rom_align_file_size(reset->size) +
+		rom_align_file_size(romdir->size) +
+		rom_align_file_size(extinfo->size);
+	if (aligned_rom_header_size > rom.size) {
+		pr_debug("%s: ROM header to large %zu > %zu\n", rom.name,
+			aligned_rom_header_size, rom.size);
+		return false;
+	}
+
+	return true;
+}
+
+/**
+ * rom_dir_size - total ROM object file size in bytes
+ * @rom: ROM object
+ * @reset: the RESET ROM file
+ * @romdir: the ROMDIR ROM file
+ * @extinfo: the EXTINFO ROM file
+ *
+ * Return: total size in bytes of all files for the ROM object
+ */
+static size_t __init rom_dir_size(const struct rom rom,
+	const struct rom_dir_entry *reset,
+	const struct rom_dir_entry *romdir,
+	const struct rom_dir_entry *extinfo)
+{
+	const size_t n = romdir->size / sizeof(struct rom_dir_entry);
+	const struct rom_dir_entry *entries = reset;
+	size_t extinfo_size = 0;
+	size_t size = 0;
+	size_t i;
+
+	if (!n) {
+		pr_debug("%s: Missing terminating entry\n", rom.name);
+		return 0;
+	}
+
+	if (entries[n - 1].name[0] != '\0') {
+		pr_debug("%s: Nonterminating name: \"%s\"\n", rom.name,
+			entries[n - 1].name);
+		return 0;
+	}
+
+	for (i = 0; i < n; i++) {
+		if (!valid_rom_dir_entry_name(&entries[i])) {
+			pr_debug("%s: Invalid entry name\n", rom.name);
+			return 0;
+		}
+
+		extinfo_size += entries[i].extinfo.size;
+		size += rom_align_file_size(entries[i].size);
+	}
+
+	if (extinfo_size > extinfo->size) {
+		pr_debug("%s: EXTINFO too large %zu > %zu\n", rom.name,
+			extinfo_size, extinfo->size);
+		return 0;
+	}
+
+	if (size > rom.size) {
+		pr_debug("%s: ROM size too large %zu > %zu\n", rom.name,
+			size, rom.size);
+		return 0;
+	}
+
+	pr_info("%s: Found %zu files in %zu bytes\n", rom.name, n - 1, size);
+
+	return size;
+}
+
+/**
+ * extinfo_data - data for the EXTINFO ROM file
+ * @rom: read-only memory object
+ * @reset: the RESET ROM file
+ * @romdir: the ROMDIR ROM file
+ *
+ * The EXTINFO file comes as the third file after RESET and ROMDIR.
+ *
+ * Return: EXTINFO ROM file data
+ */
+static const void * __init extinfo_data(const struct rom rom,
+	const struct rom_dir_entry *reset,
+	const struct rom_dir_entry *romdir)
+{
+	const u8 *b = rom.base;
+
+	return &b[rom_align_file_size(reset->size) +
+		  rom_align_file_size(romdir->size)];
+}
+
+/**
+ * rom_dir_for_rom - the ROM directory for a given ROM object
+ * @rom: ROM object
+ *
+ * Return: ROM directory
+ */
+static struct rom_dir __init rom_dir_for_rom(const struct rom rom)
+{
+	const loff_t reset_offset = find_reset_string(rom);
+	struct rom_dir dir = { };
+	size_t size;
+
+	/* RESET, ROMDIR and EXTINFO always come at indices 0, 1, 2. */
+	const struct rom_dir_entry
+		*reset   = rom_dir_entry(rom, reset_offset, 0),
+		*romdir  = rom_dir_entry(rom, reset_offset, 1),
+		*extinfo = rom_dir_entry(rom, reset_offset, 2);
+
+	if (!valid_rom_dir_header(rom, reset_offset, reset, romdir, extinfo))
+		return dir;
+
+	size = rom_dir_size(rom, reset, romdir, extinfo);
+	if (!size)
+		return dir;
+
+	dir.size = size;
+	dir.data = rom.base;
+	dir.extinfo.size = extinfo->size;
+	dir.extinfo.data = extinfo_data(rom, reset, romdir);
+	dir.entries = reset;
+
+	return dir;
+}
+
+/**
+ * rom_dir_init - the ROM directory for a given physical address
+ * @name: name of the ROM object
+ * @rom_phys_base: physical address of the ROM
+ * @rom_size: maximum size in bytes of the ROM
+ *
+ * The ROM directory is adjusted to the actual total size of the ROM files.
+ *
+ * Return: ROM directory
+ */
+static struct rom_dir __init rom_dir_init(const char *name,
+	phys_addr_t rom_phys_base, size_t rom_size)
+{
+	const struct rom rom = {
+		.name = name,
+		.base = phys_to_virt(rom_phys_base),
+		.size = rom_size
+	};
+
+	return rom_dir_for_rom(rom);
+}
+
+static int __init ps2_rom_init(void)
+{
+	BUILD_BUG_ON(sizeof(struct rom_dir_entry) != 16);
+
+	rom0_dir = rom_dir_init("rom0", ROM0_BASE, ROM0_SIZE);
+	rom1_dir = rom_dir_init("rom1", ROM1_BASE, ROM1_SIZE);
+
+	return 0;
+}
+arch_initcall(ps2_rom_init);
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 037/120] MIPS: PS2: ROM: Find ROM files with a given name, if they exist
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (35 preceding siblings ...)
  2019-09-01 15:49 ` [PATCH 036/120] MIPS: PS2: ROM: Iterate over the files in a given ROM directory Fredrik Noring
@ 2019-09-01 15:49 ` Fredrik Noring
  2019-09-01 15:50 ` [PATCH 038/120] MIPS: PS2: ROM: Read data for a given ROM file name Fredrik Noring
                   ` (83 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:49 UTC (permalink / raw)
  To: linux-mips

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

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/mach-ps2/rom.h | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/arch/mips/include/asm/mach-ps2/rom.h b/arch/mips/include/asm/mach-ps2/rom.h
index 063c8b6acf15..8794bd13184e 100644
--- a/arch/mips/include/asm/mach-ps2/rom.h
+++ b/arch/mips/include/asm/mach-ps2/rom.h
@@ -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);
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 038/120] MIPS: PS2: ROM: Read data for a given ROM file name
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (36 preceding siblings ...)
  2019-09-01 15:49 ` [PATCH 037/120] MIPS: PS2: ROM: Find ROM files with a given name, if they exist Fredrik Noring
@ 2019-09-01 15:50 ` Fredrik Noring
  2019-09-02  9:05   ` Sergei Shtylyov
  2019-09-01 15:50 ` [PATCH 039/120] MIPS: PS2: ROM: Read extended information for a given ROM file Fredrik Noring
                   ` (82 subsequent siblings)
  120 siblings, 1 reply; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:50 UTC (permalink / raw)
  To: linux-mips

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 <noring@nocrew.org>
---
 arch/mips/include/asm/mach-ps2/rom.h |  3 +++
 arch/mips/ps2/rom.c                  | 33 ++++++++++++++++++++++++++++
 2 files changed, 36 insertions(+)

diff --git a/arch/mips/include/asm/mach-ps2/rom.h b/arch/mips/include/asm/mach-ps2/rom.h
index 8794bd13184e..f2c35788ddfb 100644
--- a/arch/mips/include/asm/mach-ps2/rom.h
+++ b/arch/mips/include/asm/mach-ps2/rom.h
@@ -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);
diff --git a/arch/mips/ps2/rom.c b/arch/mips/ps2/rom.c
index 12a57f24bd63..840d37a199d8 100644
--- a/arch/mips/ps2/rom.c
+++ b/arch/mips/ps2/rom.c
@@ -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
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 039/120] MIPS: PS2: ROM: Read extended information for a given ROM file
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (37 preceding siblings ...)
  2019-09-01 15:50 ` [PATCH 038/120] MIPS: PS2: ROM: Read data for a given ROM file name Fredrik Noring
@ 2019-09-01 15:50 ` Fredrik Noring
  2019-09-01 15:50 ` [PATCH 040/120] MIPS: PS2: ROM: Read and decode the ROMVER file Fredrik Noring
                   ` (81 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:50 UTC (permalink / raw)
  To: linux-mips

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/mach-ps2/rom.h |  22 ++++++
 arch/mips/ps2/rom.c                  | 106 +++++++++++++++++++++++++++
 2 files changed, 128 insertions(+)

diff --git a/arch/mips/include/asm/mach-ps2/rom.h b/arch/mips/include/asm/mach-ps2/rom.h
index f2c35788ddfb..4cc3fcffaa59 100644
--- a/arch/mips/include/asm/mach-ps2/rom.h
+++ b/arch/mips/include/asm/mach-ps2/rom.h
@@ -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);
diff --git a/arch/mips/ps2/rom.c b/arch/mips/ps2/rom.c
index 840d37a199d8..a510832e26d7 100644
--- a/arch/mips/ps2/rom.c
+++ b/arch/mips/ps2/rom.c
@@ -66,6 +66,7 @@
  * a specific address.
  */
 
+#include <linux/bcd.h>
 #include <linux/build_bug.h>
 #include <linux/errno.h>
 #include <linux/init.h>
@@ -108,6 +109,32 @@ struct rom_dir_entry {
 	u32 size;
 };
 
+/**
+ * enum rom_extinfo_entry_type - EXTINFO &rom_extinfo_entry.type for a ROM file
+ * @rom_extinfo_entry_date: BCD of day, month and year follows as 4 byte data
+ * @rom_extinfo_entry_version: version number is in &rom_extinfo_entry.value
+ * @rom_extinfo_entry_comment: NUL terminated comment string follows as data
+ * @rom_extinfo_entry_unknown: Unclear, but seems to indicate file is aligned
+ */
+enum rom_extinfo_entry_type {
+	rom_extinfo_entry_date		= 1,
+	rom_extinfo_entry_version	= 2,
+	rom_extinfo_entry_comment	= 3,
+	rom_extinfo_entry_unknown	= 0x7f
+};
+
+/**
+ * struct rom_extinfo_entry - raw 4-byte EXTINFO entry for a ROM file
+ * @value: only known use is the version number for &rom_extinfo_entry_version
+ * @size: size in bytes of following data
+ * @type: &rom_extinfo_entry_type type
+ */
+struct rom_extinfo_entry {
+	u16 value;
+	u8 size;
+	u8 type;
+};
+
 /**
  * rom_align_file_size - align ROM file size to 16 byte boundaries
  * @size: possibly unaligned ROM size in bytes
@@ -257,6 +284,84 @@ ssize_t rom_read_file(const struct rom_dir dir,
 }
 EXPORT_SYMBOL_GPL(rom_read_file);
 
+/**
+ * rom_read_extinfo - read EXTINFO for a ROM file
+ * @name: name of ROM file, used for error reporting
+ * @buffer: pointer to EXTINFO data
+ * @size: size of EXTINFO data
+ *
+ * Return: EXTINFO for ROM file, where undefined members are zero or the empty
+ * 	string in the case of the comment
+ */
+struct rom_extinfo rom_read_extinfo(const char *name,
+	const void *buffer, size_t size)
+{
+	struct rom_extinfo ei = { .comment = "" };
+	struct rom_extinfo_entry entry;
+	const u8 *buf = buffer;
+	size_t i = 0;
+
+	/*
+	 * As an example, three EXTINFO entries for a ROM file might look
+	 * like this in binary form:
+	 *
+	 * 00 00 04 01 03 04 02 20 01 01 00 02 00 00 08 03  ................
+	 * 53 74 64 69 6f 00 00 00                          Stdio...
+	 *
+	 * The first entry is the date 2002-04-03, the second entry is the
+	 * version 0x101, and the last entry is the comment "Stdio".
+	 */
+
+	while (i + sizeof(entry) <= size) {
+		const u8 *data = &buf[i + sizeof(entry)];
+
+		memcpy(&entry, &buf[i], sizeof(entry));
+		i += sizeof(entry) + entry.size;
+
+		if (i > size) {
+			pr_debug("%s: %s: Invalid entry size %zu > %zu\n",
+				__func__, name, i, size);
+			break;
+		}
+
+		switch (entry.type) {
+		case rom_extinfo_entry_date:
+			if (entry.size == 4) {
+				ei.date.day   = bcd2bin(data[0]);
+				ei.date.month = bcd2bin(data[1]);
+				ei.date.year  = bcd2bin(data[2]) +
+						bcd2bin(data[3]) * 100;
+			} else
+				pr_debug("%s: %s: Invalid date size %u\n",
+					__func__, name, entry.size);
+			break;
+
+		case rom_extinfo_entry_version:
+			ei.version = entry.value;
+			break;
+
+		case rom_extinfo_entry_comment:
+			if (entry.size > 0 && data[entry.size - 1] == '\0') {
+				ei.comment = (const char *)data;
+			} else
+				pr_debug("%s: %s: Malformed comment\n",
+					__func__, name);
+			break;
+
+		case rom_extinfo_entry_unknown:
+			/* Ignore */
+			break;
+
+		default:
+			pr_debug("%s: %s: Invalid type %d\n",
+				__func__, name, entry.type);
+		}
+	}
+
+	return ei;
+}
+EXPORT_SYMBOL_GPL(rom_read_extinfo);
+
 /**
  * find_reset_string - find the offset to the ``"RESET"`` string, if it exists
  * @rom: ROM to search in
@@ -525,6 +630,7 @@ static struct rom_dir __init rom_dir_init(const char *name,
 static int __init ps2_rom_init(void)
 {
 	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);
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 040/120] MIPS: PS2: ROM: Read and decode the ROMVER file
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (38 preceding siblings ...)
  2019-09-01 15:50 ` [PATCH 039/120] MIPS: PS2: ROM: Read extended information for a given ROM file Fredrik Noring
@ 2019-09-01 15:50 ` Fredrik Noring
  2019-09-01 15:52 ` [PATCH 041/120] MIPS: PS2: ROM: Resolve the name for the type in " Fredrik Noring
                   ` (80 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:50 UTC (permalink / raw)
  To: linux-mips

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/mach-ps2/rom.h | 30 ++++++++++++++++++++++++++++
 arch/mips/ps2/rom.c                  | 28 ++++++++++++++++++++++++++
 2 files changed, 58 insertions(+)

diff --git a/arch/mips/include/asm/mach-ps2/rom.h b/arch/mips/include/asm/mach-ps2/rom.h
index 4cc3fcffaa59..42e520f32f65 100644
--- a/arch/mips/include/asm/mach-ps2/rom.h
+++ b/arch/mips/include/asm/mach-ps2/rom.h
@@ -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);
diff --git a/arch/mips/ps2/rom.c b/arch/mips/ps2/rom.c
index a510832e26d7..32ae8ec839c4 100644
--- a/arch/mips/ps2/rom.c
+++ b/arch/mips/ps2/rom.c
@@ -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);
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 041/120] MIPS: PS2: ROM: Resolve the name for the type in the ROMVER file
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (39 preceding siblings ...)
  2019-09-01 15:50 ` [PATCH 040/120] MIPS: PS2: ROM: Read and decode the ROMVER file Fredrik Noring
@ 2019-09-01 15:52 ` " Fredrik Noring
  2019-09-01 15:52 ` [PATCH 042/120] MIPS: PS2: ROM: Resolve the name for the region " Fredrik Noring
                   ` (79 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:52 UTC (permalink / raw)
  To: linux-mips

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/mach-ps2/rom.h |  4 ++++
 arch/mips/ps2/rom.c                  | 20 ++++++++++++++++++--
 2 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/arch/mips/include/asm/mach-ps2/rom.h b/arch/mips/include/asm/mach-ps2/rom.h
index 42e520f32f65..0b0e7c4162f5 100644
--- a/arch/mips/include/asm/mach-ps2/rom.h
+++ b/arch/mips/include/asm/mach-ps2/rom.h
@@ -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);
diff --git a/arch/mips/ps2/rom.c b/arch/mips/ps2/rom.c
index 32ae8ec839c4..9adea705cbca 100644
--- a/arch/mips/ps2/rom.c
+++ b/arch/mips/ps2/rom.c
@@ -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;
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 042/120] MIPS: PS2: ROM: Resolve the name for the region in the ROMVER file
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (40 preceding siblings ...)
  2019-09-01 15:52 ` [PATCH 041/120] MIPS: PS2: ROM: Resolve the name for the type in " Fredrik Noring
@ 2019-09-01 15:52 ` " Fredrik Noring
  2019-09-01 15:53 ` [PATCH 043/120] MIPS: PS2: ROM: Permit /dev/mem to access read-only memory Fredrik Noring
                   ` (78 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:52 UTC (permalink / raw)
  To: linux-mips

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/mach-ps2/rom.h |  5 ++++-
 arch/mips/ps2/rom.c                  | 22 ++++++++++++++++++++--
 2 files changed, 24 insertions(+), 3 deletions(-)

diff --git a/arch/mips/include/asm/mach-ps2/rom.h b/arch/mips/include/asm/mach-ps2/rom.h
index 0b0e7c4162f5..43770fd62a2e 100644
--- a/arch/mips/include/asm/mach-ps2/rom.h
+++ b/arch/mips/include/asm/mach-ps2/rom.h
@@ -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);
diff --git a/arch/mips/ps2/rom.c b/arch/mips/ps2/rom.c
index 9adea705cbca..7cdc4962069e 100644
--- a/arch/mips/ps2/rom.c
+++ b/arch/mips/ps2/rom.c
@@ -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;
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 043/120] MIPS: PS2: ROM: Permit /dev/mem to access read-only memory
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (41 preceding siblings ...)
  2019-09-01 15:52 ` [PATCH 042/120] MIPS: PS2: ROM: Resolve the name for the region " Fredrik Noring
@ 2019-09-01 15:53 ` Fredrik Noring
  2019-09-01 15:53 ` [PATCH 044/120] MIPS: PS2: ROM: Sysfs module to inspect ROM files Fredrik Noring
                   ` (77 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:53 UTC (permalink / raw)
  To: linux-mips

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/io.h |  6 ++++++
 arch/mips/ps2/memory.c     | 16 ++++++++++++++++
 2 files changed, 22 insertions(+)

diff --git a/arch/mips/include/asm/io.h b/arch/mips/include/asm/io.h
index f7115472f530..f449a27c2b79 100644
--- a/arch/mips/include/asm/io.h
+++ b/arch/mips/include/asm/io.h
@@ -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 */
diff --git a/arch/mips/ps2/memory.c b/arch/mips/ps2/memory.c
index 66ca37f38330..c513b6912bb0 100644
--- a/arch/mips/ps2/memory.c
+++ b/arch/mips/ps2/memory.c
@@ -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)
 {
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 044/120] MIPS: PS2: ROM: Sysfs module to inspect ROM files
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (42 preceding siblings ...)
  2019-09-01 15:53 ` [PATCH 043/120] MIPS: PS2: ROM: Permit /dev/mem to access read-only memory Fredrik Noring
@ 2019-09-01 15:53 ` Fredrik Noring
  2019-09-01 15:54 ` [PATCH 045/120] MIPS: PS2: ROM: Provide extended file information via sysfs Fredrik Noring
                   ` (76 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:53 UTC (permalink / raw)
  To: linux-mips

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

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

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

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

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

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

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 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

diff --git a/arch/mips/ps2/Makefile b/arch/mips/ps2/Makefile
index 1101ad0d702b..a56ea782120e 100644
--- a/arch/mips/ps2/Makefile
+++ b/arch/mips/ps2/Makefile
@@ -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
diff --git a/arch/mips/ps2/rom-sysfs.c b/arch/mips/ps2/rom-sysfs.c
new file mode 100644
index 000000000000..158d36763af5
--- /dev/null
+++ b/arch/mips/ps2/rom-sysfs.c
@@ -0,0 +1,288 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 read-only memory (ROM) sysfs
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ *
+ * FIXME: Is /sys/rom the proper placement?
+ */
+
+///
+// DOC:
+//
+// ROM0 and ROM1 contain simple file systems that can be inspected with this
+// sysfs module. 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.
+//
+
+#include <linux/ctype.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+
+#include <asm/page.h>
+#include <asm/uaccess.h>
+
+#include <asm/mach-ps2/rom.h>
+
+static int rom0_sysfs(struct kobject *romn_kobj);
+
+/* FIXME */
+static const struct {
+	const char *name;
+	const struct rom_dir *dir;
+	int (*func)(struct kobject *romn_kobj);
+} rom_dirs[] = {
+	{ "rom0", &rom0_dir, rom0_sysfs },
+	{ "rom1", &rom1_dir, NULL },
+};
+
+static struct rom_dir rom_dir_from_kobj(const struct kobject *kobj)
+{
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(rom_dirs); i++)
+		if (strcmp(rom_dirs[i].name, kobj->parent->name) == 0)
+			return *rom_dirs[i].dir;
+
+	pr_err("%s: ROM dir for \"%s\" does not exist\n", __func__,
+		kobj->parent->name);
+
+	return (struct rom_dir) { };
+}
+
+static struct rom_file rom_file_from_kobj(const struct kobject *kobj)
+{
+	const size_t rom_id = simple_strtoull(kobj->name, NULL, 0);
+	const struct rom_dir dir = rom_dir_from_kobj(kobj->parent);
+	struct rom_file file;
+	size_t id = 0;
+
+	rom_for_each_file(file, dir)
+		if (id++ == rom_id)
+			return file;
+
+	pr_err("%s: ROM id %zu for \"%s\" does not exist\n", __func__,
+		rom_id, kobj->parent->name);
+
+	return (struct rom_file) { .name = "<undefined>" };
+}
+
+static ssize_t rom_version_number_show(struct kobject *kobj,
+	struct kobj_attribute *attr, char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "0x%04x\n", rom_version().number);
+}
+
+static ssize_t rom_version_region_show(struct kobject *kobj,
+	struct kobj_attribute *attr, char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "%s\n",
+		rom_region_name(rom_version().region));
+}
+
+static ssize_t rom_version_type_show(struct kobject *kobj,
+	struct kobj_attribute *attr, char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "%s\n",
+		rom_type_name(rom_version().type));
+}
+
+static ssize_t rom_version_date_show(struct kobject *kobj,
+	struct kobj_attribute *attr, char *buf)
+{
+	const struct rom_ver v = rom_version();
+
+	return scnprintf(buf, PAGE_SIZE, "%04d-%02d-%02d\n",
+		v.date.year, v.date.month, v.date.day);
+}
+
+static ssize_t rom_file_name_show(struct kobject *kobj,
+	struct kobj_attribute *attr, char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "%s\n",
+		rom_file_from_kobj(kobj).name);
+}
+
+static ssize_t rom_file_size_show(struct kobject *kobj,
+	struct kobj_attribute *attr, char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "%zu\n",
+		rom_file_from_kobj(kobj).size);
+}
+
+static ssize_t rom_file_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).data));
+}
+
+#define DEFINE_ROM_FIELD_ATTR(prefix, field)				\
+	static struct kobj_attribute rom_attribute_##prefix##_##field =	\
+		__ATTR(field, S_IRUGO, rom_##prefix##_##field##_show, NULL)
+
+DEFINE_ROM_FIELD_ATTR(version, number);
+DEFINE_ROM_FIELD_ATTR(version, region);
+DEFINE_ROM_FIELD_ATTR(version, type);
+DEFINE_ROM_FIELD_ATTR(version, date);
+
+static struct attribute *rom_version_attributes[] = {
+	&rom_attribute_version_number.attr,
+	&rom_attribute_version_region.attr,
+	&rom_attribute_version_type.attr,
+	&rom_attribute_version_date.attr,
+	NULL
+};
+
+static struct attribute_group rom_version_attribute_group = {
+	.attrs = rom_version_attributes
+};
+
+DEFINE_ROM_FIELD_ATTR(file, name);
+DEFINE_ROM_FIELD_ATTR(file, size);
+DEFINE_ROM_FIELD_ATTR(file, data);
+
+static struct attribute *rom_file_attributes[] = {
+	&rom_attribute_file_name.attr,
+	&rom_attribute_file_size.attr,
+	&rom_attribute_file_data.attr,
+	NULL
+};
+
+static struct attribute_group rom_file_attribute_group = {
+	.attrs = rom_file_attributes
+};
+
+static int rom0_sysfs(struct kobject *rom0_kobj)
+{
+	struct kobject *version_kobj;
+
+	version_kobj = kobject_create_and_add("version", rom0_kobj);
+	if (!version_kobj)
+		return -ENOMEM;
+
+	return sysfs_create_group(version_kobj, &rom_version_attribute_group);
+}
+
+static int __init rom_init_file(struct kobject *file_kobj, size_t index,
+	const struct rom_file file)
+{
+	struct kobject *index_kobj;
+	char index_string[20];
+	int err;
+
+	scnprintf(index_string, sizeof(index_string), "%zu", index);
+
+	index_kobj = kobject_create_and_add(index_string, file_kobj);
+	if (!index_kobj)
+		return -ENOMEM;
+
+	return sysfs_create_group(index_kobj, &rom_file_attribute_group);
+}
+
+static int __init rom_init_dir(struct kobject *romn_kobj,
+	const struct rom_dir dir)
+{
+	struct kobject *file_kobj = kobject_create_and_add("file", romn_kobj);
+	struct rom_file file;
+	size_t i = 0;
+
+	if (!file_kobj)
+		return -ENOMEM;
+
+	rom_for_each_file(file, dir) {
+		int err = rom_init_file(file_kobj, i++, file);
+
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static int __init rom_init_rom(struct kobject *rom_kobj,
+	const char *name, const struct rom_dir dir,
+	int (*func)(struct kobject *romn_kobj))
+{
+	struct kobject *romn_kobj = kobject_create_and_add(name, rom_kobj);
+	int err;
+
+	if (!romn_kobj)
+		return -ENOMEM;
+
+	err = rom_init_dir(romn_kobj, dir);
+	if (!err && func)
+		err = func(romn_kobj);
+
+	return err;
+}
+
+static struct kobject *rom_kobj;
+
+static int __init rom_sysfs_init(void)
+{
+	size_t i;
+
+	rom_kobj = kobject_create_and_add("rom", NULL);
+	if (!rom_kobj)
+		return -ENOMEM;
+
+	for (i = 0; i < ARRAY_SIZE(rom_dirs); i++) {
+		int err = rom_init_rom(rom_kobj, rom_dirs[i].name,
+			*rom_dirs[i].dir, rom_dirs[i].func);
+
+		if (err) {
+			kobject_del(rom_kobj);
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+static void __exit rom_sysfs_exit(void)
+{
+	kobject_del(rom_kobj);
+}
+
+module_init(rom_sysfs_init);
+module_exit(rom_sysfs_exit);
+
+MODULE_DESCRIPTION("PlayStation 2 read-only memory (ROM) sysfs");
+MODULE_AUTHOR("Fredrik Noring");
+MODULE_LICENSE("GPL");
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 045/120] MIPS: PS2: ROM: Provide extended file information via sysfs
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (43 preceding siblings ...)
  2019-09-01 15:53 ` [PATCH 044/120] MIPS: PS2: ROM: Sysfs module to inspect ROM files Fredrik Noring
@ 2019-09-01 15:54 ` Fredrik Noring
  2019-09-01 15:54 ` [PATCH 046/120] MIPS: PS2: Identify the machine by model name Fredrik Noring
                   ` (75 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:54 UTC (permalink / raw)
  To: linux-mips

The extended ROM file information can be viewed with sysfs:

	# ls /sys/rom/rom0/file/8/extinfo
	comment  data     date     size     version
	# cat /sys/rom/rom0/file/8/extinfo/*
	System_Memory_Manager
	0x1fc02df4
	2002-04-03
	40
	0x0101

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/ps2/rom-sysfs.c | 104 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 104 insertions(+)

diff --git a/arch/mips/ps2/rom-sysfs.c b/arch/mips/ps2/rom-sysfs.c
index 158d36763af5..804644a931e8 100644
--- a/arch/mips/ps2/rom-sysfs.c
+++ b/arch/mips/ps2/rom-sysfs.c
@@ -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);
 }
 
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 046/120] MIPS: PS2: Identify the machine by model name
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (44 preceding siblings ...)
  2019-09-01 15:54 ` [PATCH 045/120] MIPS: PS2: ROM: Provide extended file information via sysfs Fredrik Noring
@ 2019-09-01 15:54 ` Fredrik Noring
  2019-09-01 15:54 ` [PATCH 047/120] MIPS: PS2: Let the system type be Sony PlayStation 2 Fredrik Noring
                   ` (74 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:54 UTC (permalink / raw)
  To: linux-mips

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

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

	# grep machine /proc/cpuinfo
	machine			: SCPH-37000 L

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/ps2/Makefile   |  1 +
 arch/mips/ps2/identify.c | 80 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 81 insertions(+)
 create mode 100644 arch/mips/ps2/identify.c

diff --git a/arch/mips/ps2/Makefile b/arch/mips/ps2/Makefile
index a56ea782120e..b53bddcc8c01 100644
--- a/arch/mips/ps2/Makefile
+++ b/arch/mips/ps2/Makefile
@@ -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
diff --git a/arch/mips/ps2/identify.c b/arch/mips/ps2/identify.c
new file mode 100644
index 000000000000..264fdc13dc43
--- /dev/null
+++ b/arch/mips/ps2/identify.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 identification
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/printk.h>
+
+#include <asm/prom.h>
+
+#include <asm/mach-ps2/rom.h>
+#include <asm/mach-ps2/scmd.h>
+
+static int __init set_machine_name_by_scmd(void)
+{
+	const struct scmd_machine_name machine = scmd_read_machine_name();
+
+	if (machine.name[0] == '\0') {
+		pr_err("identify: %s: Reading failed\n", __func__);
+		return -EIO;
+	}
+
+	mips_set_machine_name(machine.name);
+
+	return 0;
+}
+
+static int __init set_machine_name_by_osdsys(void)
+{
+	char name[12] = { };
+	int err = rom_read_file(rom0_dir, "OSDSYS",
+		name, sizeof(name) - 1, 0x8c808);
+
+	if (err) {
+		pr_err("identify: %s: Reading failed with %d\n", __func__, err);
+		return err;
+	}
+
+	mips_set_machine_name(name);
+
+	return 0;
+}
+
+static void __init set_machine_name(void)
+{
+	const int rom_version_number = rom_version().number;
+	int err = 0;
+
+	/*
+	 * ROM version 1.00 is always SCPH-10000. Later machines with
+	 * ROM version 1.0x have the machine name in the ROM0 file OSDSYS
+	 * at offset 0x8c808. These are late SCPH-10000 and all SCPH-15000.
+	 * Even later machines have a system command (SCMD) to read the
+	 * machine name.
+	 */
+
+	if (rom_version_number >= 0x110)
+		err = set_machine_name_by_scmd();	/* ver >= 1.10 */
+	else if (rom_version_number > 0x100)
+		err = set_machine_name_by_osdsys();	/* 1.10 > ver > 1.00 */
+	else if (rom_version_number == 0x100)
+		mips_set_machine_name("SCPH-10000");	/* ver = 1.00 */
+	else
+		err = -ENODEV;
+
+	if (err)
+		pr_err("identify: Determining machine name for ROM %04x failed with %d\n",
+			rom_version_number, err);
+}
+
+static int __init ps2_identify_init(void)
+{
+	set_machine_name();
+
+	return 0;
+}
+subsys_initcall(ps2_identify_init);
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 047/120] MIPS: PS2: Let the system type be Sony PlayStation 2
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (45 preceding siblings ...)
  2019-09-01 15:54 ` [PATCH 046/120] MIPS: PS2: Identify the machine by model name Fredrik Noring
@ 2019-09-01 15:54 ` Fredrik Noring
  2019-09-01 23:09   ` Philippe Mathieu-Daudé
  2019-09-01 15:55 ` [PATCH 048/120] MIPS: Define and use cpu_relax_forever() for various halting loops Fredrik Noring
                   ` (73 subsequent siblings)
  120 siblings, 1 reply; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:54 UTC (permalink / raw)
  To: linux-mips

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

	# grep system' 'type /proc/cpuinfo
	system type		: Sony PlayStation 2

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/ps2/identify.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/arch/mips/ps2/identify.c b/arch/mips/ps2/identify.c
index 264fdc13dc43..579148fb79c4 100644
--- a/arch/mips/ps2/identify.c
+++ b/arch/mips/ps2/identify.c
@@ -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();
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 048/120] MIPS: Define and use cpu_relax_forever() for various halting loops
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (46 preceding siblings ...)
  2019-09-01 15:54 ` [PATCH 047/120] MIPS: PS2: Let the system type be Sony PlayStation 2 Fredrik Noring
@ 2019-09-01 15:55 ` Fredrik Noring
  2019-09-01 15:55 ` [PATCH 049/120] MIPS: PS2: Power off support Fredrik Noring
                   ` (72 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:55 UTC (permalink / raw)
  To: linux-mips

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 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(-)

diff --git a/arch/mips/alchemy/board-gpr.c b/arch/mips/alchemy/board-gpr.c
index 6c47318946e4..5247dfc1e0ed 100644
--- a/arch/mips/alchemy/board-gpr.c
+++ b/arch/mips/alchemy/board-gpr.c
@@ -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)
diff --git a/arch/mips/alchemy/board-mtx1.c b/arch/mips/alchemy/board-mtx1.c
index 23093535399f..2025ba4ebfdd 100644
--- a/arch/mips/alchemy/board-mtx1.c
+++ b/arch/mips/alchemy/board-mtx1.c
@@ -18,6 +18,7 @@
 #include <linux/mtd/physmap.h>
 #include <mtd/mtd-abi.h>
 #include <asm/bootinfo.h>
+#include <asm/processor.h>
 #include <asm/reboot.h>
 #include <asm/setup.h>
 #include <asm/mach-au1x00/au1000.h>
@@ -60,11 +61,7 @@ static void mtx1_reset(char *c)
 
 static void mtx1_power_off(void)
 {
-	while (1)
-		asm volatile (
-		"	.set	mips32					\n"
-		"	wait						\n"
-		"	.set	mips0					\n");
+	cpu_relax_forever();
 }
 
 void __init board_setup(void)
diff --git a/arch/mips/alchemy/board-xxs1500.c b/arch/mips/alchemy/board-xxs1500.c
index c67dfe1f4997..6189efe70e65 100644
--- a/arch/mips/alchemy/board-xxs1500.c
+++ b/arch/mips/alchemy/board-xxs1500.c
@@ -15,6 +15,7 @@
 #include <linux/delay.h>
 #include <linux/pm.h>
 #include <asm/bootinfo.h>
+#include <asm/processor.h>
 #include <asm/reboot.h>
 #include <asm/setup.h>
 #include <asm/mach-au1x00/au1000.h>
@@ -56,11 +57,7 @@ static void xxs1500_reset(char *c)
 
 static void xxs1500_power_off(void)
 {
-	while (1)
-		asm volatile (
-		"	.set	mips32					\n"
-		"	wait						\n"
-		"	.set	mips0					\n");
+	cpu_relax_forever();
 }
 
 void __init board_setup(void)
diff --git a/arch/mips/alchemy/devboards/platform.c b/arch/mips/alchemy/devboards/platform.c
index 8d4b65c3268a..2203fddc42d8 100644
--- a/arch/mips/alchemy/devboards/platform.c
+++ b/arch/mips/alchemy/devboards/platform.c
@@ -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)
diff --git a/arch/mips/ar7/setup.c b/arch/mips/ar7/setup.c
index b3ffe7c898eb..2649ee848921 100644
--- a/arch/mips/ar7/setup.c
+++ b/arch/mips/ar7/setup.c
@@ -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)
diff --git a/arch/mips/ath79/setup.c b/arch/mips/ath79/setup.c
index ea385a865781..ad1d0fab5dad 100644
--- a/arch/mips/ath79/setup.c
+++ b/arch/mips/ath79/setup.c
@@ -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)
diff --git a/arch/mips/bcm47xx/setup.c b/arch/mips/bcm47xx/setup.c
index 82627c264964..7ae2462ff3ae 100644
--- a/arch/mips/bcm47xx/setup.c
+++ b/arch/mips/bcm47xx/setup.c
@@ -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
diff --git a/arch/mips/bcm63xx/setup.c b/arch/mips/bcm63xx/setup.c
index e28ee9a7cc7e..342ca32c0ae2 100644
--- a/arch/mips/bcm63xx/setup.c
+++ b/arch/mips/bcm63xx/setup.c
@@ -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)
diff --git a/arch/mips/boot/compressed/decompress.c b/arch/mips/boot/compressed/decompress.c
index 88f5d637b1c4..27e9c13614ee 100644
--- a/arch/mips/boot/compressed/decompress.c
+++ b/arch/mips/boot/compressed/decompress.c
@@ -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 */
diff --git a/arch/mips/cavium-octeon/smp.c b/arch/mips/cavium-octeon/smp.c
index 076db9a06b5e..cb397b413967 100644
--- a/arch/mips/cavium-octeon/smp.c
+++ b/arch/mips/cavium-octeon/smp.c
@@ -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)
diff --git a/arch/mips/cobalt/reset.c b/arch/mips/cobalt/reset.c
index 4eedd481dd00..13f67197193a 100644
--- a/arch/mips/cobalt/reset.c
+++ b/arch/mips/cobalt/reset.c
@@ -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)
diff --git a/arch/mips/emma/markeins/setup.c b/arch/mips/emma/markeins/setup.c
index c8a91c2a63bc..c12e4e6ceb76 100644
--- a/arch/mips/emma/markeins/setup.c
+++ b/arch/mips/emma/markeins/setup.c
@@ -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] = {
diff --git a/arch/mips/fw/arc/init.c b/arch/mips/fw/arc/init.c
index 008555969534..90fa8ed75a50 100644
--- a/arch/mips/fw/arc/init.c
+++ b/arch/mips/fw/arc/init.c
@@ -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();
diff --git a/arch/mips/include/asm/processor.h b/arch/mips/include/asm/processor.h
index aca909bd7841..e1e4cb7833a6 100644
--- a/arch/mips/include/asm/processor.h
+++ b/arch/mips/include/asm/processor.h
@@ -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
diff --git a/arch/mips/jz4740/reset.c b/arch/mips/jz4740/reset.c
index 1f9f02e54085..c6703d32edb9 100644
--- a/arch/mips/jz4740/reset.c
+++ b/arch/mips/jz4740/reset.c
@@ -3,19 +3,14 @@
  *  Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
  */
 
+#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)
diff --git a/arch/mips/loongson64/common/reset.c b/arch/mips/loongson64/common/reset.c
index ce39e918e4d5..e91137922373 100644
--- a/arch/mips/loongson64/common/reset.c
+++ b/arch/mips/loongson64/common/reset.c
@@ -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)
diff --git a/arch/mips/mm/cerr-sb1.c b/arch/mips/mm/cerr-sb1.c
index a3c02df19f6f..c0a6acea532c 100644
--- a/arch/mips/mm/cerr-sb1.c
+++ b/arch/mips/mm/cerr-sb1.c
@@ -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
diff --git a/arch/mips/pic32/common/reset.c b/arch/mips/pic32/common/reset.c
index a5fd7a8e2800..32dcda98aa15 100644
--- a/arch/mips/pic32/common/reset.c
+++ b/arch/mips/pic32/common/reset.c
@@ -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)
diff --git a/arch/mips/pnx833x/common/reset.c b/arch/mips/pnx833x/common/reset.c
index b48e83bf912b..8f8b79994dae 100644
--- a/arch/mips/pnx833x/common/reset.c
+++ b/arch/mips/pnx833x/common/reset.c
@@ -10,6 +10,7 @@
  *	Nikita Youshchenko <yoush@debian.org>, 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)
diff --git a/arch/mips/sgi-ip22/ip22-reset.c b/arch/mips/sgi-ip22/ip22-reset.c
index c374f3ceec38..5355c96a2b89 100644
--- a/arch/mips/sgi-ip22/ip22-reset.c
+++ b/arch/mips/sgi-ip22/ip22-reset.c
@@ -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)
diff --git a/arch/mips/sgi-ip27/ip27-berr.c b/arch/mips/sgi-ip27/ip27-berr.c
index 73ad29b180fb..81cd434cb9ae 100644
--- a/arch/mips/sgi-ip27/ip27-berr.c
+++ b/arch/mips/sgi-ip27/ip27-berr.c
@@ -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)
diff --git a/arch/mips/sgi-ip27/ip27-reset.c b/arch/mips/sgi-ip27/ip27-reset.c
index e44a15d4f573..bc8c2507ba91 100644
--- a/arch/mips/sgi-ip27/ip27-reset.c
+++ b/arch/mips/sgi-ip27/ip27-reset.c
@@ -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)
diff --git a/arch/mips/sgi-ip32/ip32-berr.c b/arch/mips/sgi-ip32/ip32-berr.c
index c860f95ab7ed..daa2036aeecc 100644
--- a/arch/mips/sgi-ip32/ip32-berr.c
+++ b/arch/mips/sgi-ip32/ip32-berr.c
@@ -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)
diff --git a/arch/mips/sgi-ip32/ip32-irq.c b/arch/mips/sgi-ip32/ip32-irq.c
index a6a0ff7f5aed..d34a6b4f2096 100644
--- a/arch/mips/sgi-ip32/ip32-irq.c
+++ b/arch/mips/sgi-ip32/ip32-irq.c
@@ -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 linux-mips@linux-mips.org\n");
 	printk("Spinning...");
-	while(1) ;
+	cpu_relax_forever();
 }
 
 /* CRIME 1.1 appears to deliver all interrupts to this one pin. */
diff --git a/arch/mips/sibyte/common/cfe.c b/arch/mips/sibyte/common/cfe.c
index cbf5939ed53a..33143172a644 100644
--- a/arch/mips/sibyte/common/cfe.c
+++ b/arch/mips/sibyte/common/cfe.c
@@ -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)
diff --git a/arch/mips/txx9/rbtx4939/setup.c b/arch/mips/txx9/rbtx4939/setup.c
index ef29a9c2ffd6..ec32d8a63d2e 100644
--- a/arch/mips/txx9/rbtx4939/setup.c
+++ b/arch/mips/txx9/rbtx4939/setup.c
@@ -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)
diff --git a/arch/mips/vr41xx/common/pmu.c b/arch/mips/vr41xx/common/pmu.c
index 93cc7e0b30b1..d73178e3d910 100644
--- a/arch/mips/vr41xx/common/pmu.c
+++ b/arch/mips/vr41xx/common/pmu.c
@@ -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)
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 049/120] MIPS: PS2: Power off support
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (47 preceding siblings ...)
  2019-09-01 15:55 ` [PATCH 048/120] MIPS: Define and use cpu_relax_forever() for various halting loops Fredrik Noring
@ 2019-09-01 15:55 ` Fredrik Noring
  2019-09-01 15:55 ` [PATCH 050/120] MIPS: PS2: Real-time clock (RTC) driver Fredrik Noring
                   ` (71 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:55 UTC (permalink / raw)
  To: linux-mips

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/ps2/Makefile |  1 +
 arch/mips/ps2/reboot.c | 29 +++++++++++++++++++++++++++++
 2 files changed, 30 insertions(+)
 create mode 100644 arch/mips/ps2/reboot.c

diff --git a/arch/mips/ps2/Makefile b/arch/mips/ps2/Makefile
index b53bddcc8c01..6f671112fbcb 100644
--- a/arch/mips/ps2/Makefile
+++ b/arch/mips/ps2/Makefile
@@ -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
diff --git a/arch/mips/ps2/reboot.c b/arch/mips/ps2/reboot.c
new file mode 100644
index 000000000000..b2a3ada5268b
--- /dev/null
+++ b/arch/mips/ps2/reboot.c
@@ -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);
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 050/120] MIPS: PS2: Real-time clock (RTC) driver
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (48 preceding siblings ...)
  2019-09-01 15:55 ` [PATCH 049/120] MIPS: PS2: Power off support Fredrik Noring
@ 2019-09-01 15:55 ` Fredrik Noring
  2019-09-01 15:56 ` [PATCH 051/120] MIPS: PS2: IOP: I/O processor DMA register PCR2 set and clear Fredrik Noring
                   ` (70 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:55 UTC (permalink / raw)
  To: linux-mips

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 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

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index e72f65b61176..aaac22576a26 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -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
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 6b09c21dc1b6..5bc300f1040a 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -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
diff --git a/drivers/rtc/rtc-ps2.c b/drivers/rtc/rtc-ps2.c
new file mode 100644
index 000000000000..d0c732a052df
--- /dev/null
+++ b/drivers/rtc/rtc-ps2.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 real-time clock (RTC)
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+
+#include "asm/mach-ps2/scmd.h"
+
+#define DRVNAME "rtc-ps2"
+
+static int read_time(struct device *dev, struct rtc_time *tm)
+{
+	time64_t t;
+	int err = scmd_read_rtc(&t);
+
+	if (!err)
+		rtc_time64_to_tm(t, tm);
+
+	return err;
+}
+
+static int set_time(struct device *dev, struct rtc_time *tm)
+{
+	return scmd_set_rtc(rtc_tm_to_time64(tm));
+}
+
+static const struct rtc_class_ops ps2_rtc_ops = {
+	.read_time = read_time,
+	.set_time = set_time,
+};
+
+static int ps2_rtc_probe(struct platform_device *pdev)
+{
+	struct rtc_device *rtc;
+
+	rtc = devm_rtc_device_register(&pdev->dev,
+		DRVNAME, &ps2_rtc_ops, THIS_MODULE);
+	if (IS_ERR(rtc))
+		return PTR_ERR(rtc);
+
+	rtc->uie_unsupported = 1;
+
+	return 0;
+}
+
+static struct platform_driver ps2_rtc_driver = {
+	.probe = ps2_rtc_probe,
+	.driver = {
+		.name = DRVNAME,
+	},
+};
+
+static int __init ps2_rtc_init(void)
+{
+	return platform_driver_register(&ps2_rtc_driver);
+}
+
+static void __exit ps2_rtc_exit(void)
+{
+	platform_driver_unregister(&ps2_rtc_driver);
+}
+
+module_init(ps2_rtc_init);
+module_exit(ps2_rtc_exit);
+
+MODULE_DESCRIPTION("PlayStation 2 real-time clock (RTC)");
+MODULE_AUTHOR("Fredrik Noring");
+MODULE_LICENSE("GPL");
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 051/120] MIPS: PS2: IOP: I/O processor DMA register PCR2 set and clear
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (49 preceding siblings ...)
  2019-09-01 15:55 ` [PATCH 050/120] MIPS: PS2: Real-time clock (RTC) driver Fredrik Noring
@ 2019-09-01 15:56 ` Fredrik Noring
  2019-09-01 15:57 ` [PATCH 052/120] MIPS: PS2: SIF: Sub-system interface reset of the I/O processor (IOP) Fredrik Noring
                   ` (69 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:56 UTC (permalink / raw)
  To: linux-mips

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
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

diff --git a/arch/mips/include/asm/mach-ps2/iop-registers.h b/arch/mips/include/asm/mach-ps2/iop-registers.h
new file mode 100644
index 000000000000..b4db423150de
--- /dev/null
+++ b/arch/mips/include/asm/mach-ps2/iop-registers.h
@@ -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 */
diff --git a/drivers/ps2/Makefile b/drivers/ps2/Makefile
new file mode 100644
index 000000000000..e53976ddb3e4
--- /dev/null
+++ b/drivers/ps2/Makefile
@@ -0,0 +1 @@
+obj-m				+= iop-registers.o
diff --git a/drivers/ps2/iop-registers.c b/drivers/ps2/iop-registers.c
new file mode 100644
index 000000000000..0ea7603b91a3
--- /dev/null
+++ b/drivers/ps2/iop-registers.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 I/O processor (IOP) registers
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+#include <asm/io.h>
+
+#include <asm/mach-ps2/iop-registers.h>
+
+#define IOP_DMA_DPCR2	0x1f801570
+
+static DEFINE_SPINLOCK(reg_lock);
+
+void iop_set_dma_dpcr2(const u32 mask)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&reg_lock, flags);
+	outl(inl(IOP_DMA_DPCR2) | mask, IOP_DMA_DPCR2);
+	spin_unlock_irqrestore(&reg_lock, flags);
+}
+EXPORT_SYMBOL_GPL(iop_set_dma_dpcr2);
+
+void iop_clr_dma_dpcr2(const u32 mask)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&reg_lock, flags);
+	outl(inl(IOP_DMA_DPCR2) & ~mask, IOP_DMA_DPCR2);
+	spin_unlock_irqrestore(&reg_lock, flags);
+}
+EXPORT_SYMBOL_GPL(iop_clr_dma_dpcr2);
+
+MODULE_LICENSE("GPL");
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 052/120] MIPS: PS2: SIF: Sub-system interface reset of the I/O processor (IOP)
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (50 preceding siblings ...)
  2019-09-01 15:56 ` [PATCH 051/120] MIPS: PS2: IOP: I/O processor DMA register PCR2 set and clear Fredrik Noring
@ 2019-09-01 15:57 ` Fredrik Noring
  2019-09-01 15:57 ` [PATCH 053/120] MIPS: PS2: IOP: Define error numbers, descriptions and errno mapping Fredrik Noring
                   ` (68 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:57 UTC (permalink / raw)
  To: linux-mips

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

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

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

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

The IOP can assert interrupts via IRQ_INTC_SBUS.

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

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

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

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

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

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

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

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

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

References:

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 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

diff --git a/arch/mips/include/asm/mach-ps2/sif.h b/arch/mips/include/asm/mach-ps2/sif.h
new file mode 100644
index 000000000000..94da96bd21d1
--- /dev/null
+++ b/arch/mips/include/asm/mach-ps2/sif.h
@@ -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 */
diff --git a/drivers/ps2/Makefile b/drivers/ps2/Makefile
index e53976ddb3e4..ef561a802bdd 100644
--- a/drivers/ps2/Makefile
+++ b/drivers/ps2/Makefile
@@ -1 +1,2 @@
 obj-m				+= iop-registers.o
+obj-m				+= sif.o
diff --git a/drivers/ps2/sif.c b/drivers/ps2/sif.c
new file mode 100644
index 000000000000..6564af245880
--- /dev/null
+++ b/drivers/ps2/sif.c
@@ -0,0 +1,414 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 sub-system interface (SIF)
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+/**
+ * DOC: The sub-system interface (SIF) for the input/output processor (IOP)
+ *
+ * The 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. 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.
+ */
+
+#include <linux/build_bug.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/sched/signal.h>
+#include <linux/types.h>
+
+#include <asm/io.h>
+
+#include <asm/mach-ps2/dmac.h>
+#include <asm/mach-ps2/irq.h>
+#include <asm/mach-ps2/sif.h>
+
+#define IOP_RESET_ARGS "rom0:UDNL rom0:OSDCNF"
+
+#define SIF0_BUFFER_SIZE	PAGE_SIZE
+#define SIF1_BUFFER_SIZE	PAGE_SIZE
+
+static iop_addr_t iop_buffer; /* Address of IOP SIF DMA receive address */
+static void *sif0_buffer;
+static void *sif1_buffer;
+
+/**
+ * sif_write_msflag - write to set main-to-sub flag register bits
+ * @mask: MSFLAG register bit values to set
+ */
+static void sif_write_msflag(u32 mask)
+{
+	outl(mask, SIF_MSFLAG);
+}
+
+/**
+ * sif_write_smflag - write to clear sub-to-main flag register bits
+ * @mask: SMFLAG register bit values to clear
+ */
+static void sif_write_smflag(u32 mask)
+{
+	outl(mask, SIF_SMFLAG);
+}
+
+/**
+ * sif_read_smflag - read the sub-to-main flag register
+ *
+ * Return: SMFLAG register value
+ */
+static u32 sif_read_smflag(void)
+{
+	u32 a = inl(SIF_SMFLAG);
+	u32 b;
+
+	do {
+		b = a;
+
+		a = inl(SIF_SMFLAG);
+	} while (a != b);	/* Ensure SMFLAG reading is stable */
+
+	return a;
+}
+
+static bool completed(bool (*condition)(void))
+{
+	const unsigned long timeout = jiffies + 5*HZ;
+
+	do {
+		if (condition())
+			return true;
+
+		msleep(1);
+	} while (time_is_after_jiffies(timeout));
+
+	return false;
+}
+
+static bool sif_smflag_cmdinit(void)
+{
+	return (sif_read_smflag() & SIF_STATUS_CMDINIT) != 0;
+}
+
+static bool sif_smflag_bootend(void)
+{
+	return (sif_read_smflag() & SIF_STATUS_BOOTEND) != 0;
+}
+
+static bool sif1_busy(void)
+{
+	return (inl(DMAC_SIF1_CHCR) & DMAC_CHCR_BUSY) != 0;
+}
+
+static bool sif1_ready(void)
+{
+	size_t countout = 50000;	/* About 5 s */
+
+	while (sif1_busy() && countout > 0) {
+		udelay(100);
+		countout--;
+	}
+
+	return countout > 0;
+}
+
+/* Bytes to 32-bit word count. */
+static u32 nbytes_to_wc(size_t nbytes)
+{
+	const u32 wc = nbytes / 4;
+
+	BUG_ON(nbytes & 0x3);	/* Word count must align */
+	BUG_ON(nbytes != (size_t)wc * 4);
+
+	return wc;
+}
+
+/* Bytes to 128-bit quadword count. */
+static u32 nbytes_to_qwc(size_t nbytes)
+{
+	const size_t qwc = nbytes / 16;
+
+	BUG_ON(nbytes & 0xf);	/* Quadword count must align */
+	BUG_ON(qwc > 0xffff);	/* QWC DMA field is only 16 bits */
+
+	return qwc;
+}
+
+static int sif1_write_ert_int_0(const struct sif_cmd_header *header,
+	bool ert, bool int_0, iop_addr_t dst, const void *src, size_t nbytes)
+{
+	const size_t header_size = header != NULL ? sizeof(*header) : 0;
+	const size_t aligned_size = ALIGN(header_size + nbytes, 16);
+	const struct iop_dma_tag iop_dma_tag = {
+		.ert	= ert,
+		.int_0	= int_0,
+		.addr	= dst,
+		.wc	= nbytes_to_wc(aligned_size)
+	};
+	const size_t dma_nbytes = sizeof(iop_dma_tag) + aligned_size;
+	u8 *dma_buffer = sif1_buffer;
+	dma_addr_t madr;
+
+	if (!aligned_size)
+		return 0;
+	if (dma_nbytes > SIF1_BUFFER_SIZE)
+		return -EINVAL;
+	if (!sif1_ready())
+		return -EBUSY;
+
+	memcpy(&dma_buffer[0], &iop_dma_tag, sizeof(iop_dma_tag));
+	memcpy(&dma_buffer[sizeof(iop_dma_tag)], header, header_size);
+	memcpy(&dma_buffer[sizeof(iop_dma_tag) + header_size], src, nbytes);
+
+	madr = virt_to_phys(dma_buffer);
+	dma_cache_wback((unsigned long)dma_buffer, dma_nbytes);
+
+	outl(madr, DMAC_SIF1_MADR);
+	outl(nbytes_to_qwc(dma_nbytes), DMAC_SIF1_QWC);
+	outl(DMAC_CHCR_SENDN_TIE, DMAC_SIF1_CHCR);
+
+	return 0;
+}
+
+static int sif1_write(const struct sif_cmd_header *header,
+	iop_addr_t dst, const void *src, size_t nbytes)
+{
+	return sif1_write_ert_int_0(header, false, false, dst, src, nbytes);
+}
+
+static int sif1_write_irq(const struct sif_cmd_header *header,
+	iop_addr_t dst, const void *src, size_t nbytes)
+{
+	return sif1_write_ert_int_0(header, true, true, dst, src, nbytes);
+}
+
+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)
+{
+	const struct sif_cmd_header header = {
+		.packet_size = sizeof(header) + pktsize,
+		.data_size   = nbytes,
+		.data_addr   = dst,
+		.cmd         = cmd_id,
+		.opt         = opt
+	};
+	int err;
+
+	if (pktsize > SIF_CMD_PACKET_DATA_MAX)
+		return -EINVAL;
+
+	err = sif1_write(NULL, dst, src, nbytes);
+	if (!err)
+		err = sif1_write_irq(&header, iop_buffer, pkt, pktsize);
+
+	return err;
+}
+
+static int sif_cmd_copy(u32 cmd_id, const void *pkt, size_t pktsize,
+	iop_addr_t dst, const void *src, size_t nbytes)
+{
+	return sif_cmd_opt_copy(cmd_id, 0, pkt, pktsize, dst, src, nbytes);
+}
+
+static int sif_cmd(u32 cmd_id, const void *pkt, size_t pktsize)
+{
+	return sif_cmd_copy(cmd_id, pkt, pktsize, 0, NULL, 0);
+}
+
+static int iop_reset_arg(const char *arg)
+{
+	const size_t arglen = strlen(arg) + 1;
+	struct {
+		u32 arglen;
+		u32 mode;
+		char arg[79 + 1];	/* Including NUL */
+	} reset_pkt = {
+		.arglen = arglen,
+		.mode = 0
+	};
+	int err;
+
+	if (arglen > sizeof(reset_pkt.arg))
+		return -EINVAL;
+	memcpy(reset_pkt.arg, arg, arglen);
+
+	sif_write_smflag(SIF_STATUS_BOOTEND);
+
+	err = sif_cmd(SIF_CMD_RESET_CMD, &reset_pkt, sizeof(reset_pkt));
+	if (err)
+		return err;
+
+	sif_write_smflag(SIF_STATUS_SIFINIT | SIF_STATUS_CMDINIT);
+
+	return completed(sif_smflag_bootend) ? 0 : -EIO;
+}
+
+static int iop_reset(void)
+{
+	return iop_reset_arg(IOP_RESET_ARGS);
+}
+
+static int sif_read_subaddr(dma_addr_t *subaddr)
+{
+	if (!completed(sif_smflag_cmdinit))
+		return -EIO;
+
+	*subaddr = inl(SIF_SUBADDR);
+
+	return 0;
+}
+
+static void sif_write_mainaddr_bootend(dma_addr_t mainaddr)
+{
+	outl(0xff, SIF_UNKNF260);
+	outl(mainaddr, SIF_MAINADDR);
+	sif_write_msflag(SIF_STATUS_CMDINIT | SIF_STATUS_BOOTEND);
+}
+
+static void put_dma_buffers(void)
+{
+	free_page((unsigned long)sif1_buffer);
+	free_page((unsigned long)sif0_buffer);
+}
+
+static int get_dma_buffers(void)
+{
+	sif0_buffer = (void *)__get_free_page(GFP_DMA);
+	sif1_buffer = (void *)__get_free_page(GFP_DMA);
+
+	if (sif0_buffer == NULL ||
+	    sif1_buffer == NULL) {
+		put_dma_buffers();
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void sif_disable_dma(void)
+{
+	outl(DMAC_CHCR_STOP, DMAC_SIF0_CHCR);
+	outl(0, DMAC_SIF0_MADR);
+	outl(0, DMAC_SIF0_QWC);
+	inl(DMAC_SIF0_QWC);
+
+	outl(DMAC_CHCR_STOP, DMAC_SIF1_CHCR);
+}
+
+/**
+ * sif_init - initialise the SIF with a reset
+ *
+ * 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.
+ *
+ * Return: 0 on success, otherwise a negative error number
+ */
+static int __init sif_init(void)
+{
+	int err;
+
+	BUILD_BUG_ON(sizeof(struct sif_cmd_header) != 16);
+
+	sif_disable_dma();
+
+	err = get_dma_buffers();
+	if (err) {
+		pr_err("sif: Failed to allocate DMA buffers with %d\n", err);
+		goto err_dma_buffers;
+	}
+
+	/* Read provisional SUBADDR in preparation for the IOP reset. */
+	err = sif_read_subaddr(&iop_buffer);
+	if (err) {
+		pr_err("sif: Failed to read provisional SUBADDR with %d\n",
+			err);
+		goto err_provisional_subaddr;
+	}
+
+	/* Write provisional MAINADDR in preparation for the IOP reset. */
+	sif_write_mainaddr_bootend(virt_to_phys(sif0_buffer));
+
+	err = iop_reset();
+	if (err) {
+		pr_err("sif: Failed to reset the IOP with %d\n", err);
+		goto err_iop_reset;
+	}
+
+	/* Write final MAINADDR and indicate end of boot. */
+	sif_write_mainaddr_bootend(virt_to_phys(sif0_buffer));
+
+	/* Read final SUBADDR. */
+	err = sif_read_subaddr(&iop_buffer);
+	if (err) {
+		pr_err("sif: Failed to read final SUBADDR with %d\n", err);
+		goto err_final_subaddr;
+	}
+
+	return 0;
+
+err_final_subaddr:
+err_iop_reset:
+err_provisional_subaddr:
+	put_dma_buffers();
+
+err_dma_buffers:
+	return err;
+}
+
+static void __exit sif_exit(void)
+{
+	sif_disable_dma();
+
+	put_dma_buffers();
+}
+
+module_init(sif_init);
+module_exit(sif_exit);
+
+MODULE_DESCRIPTION("PlayStation 2 sub-system interface (SIF)");
+MODULE_AUTHOR("Fredrik Noring");
+MODULE_LICENSE("GPL");
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 053/120] MIPS: PS2: IOP: Define error numbers, descriptions and errno mapping
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (51 preceding siblings ...)
  2019-09-01 15:57 ` [PATCH 052/120] MIPS: PS2: SIF: Sub-system interface reset of the I/O processor (IOP) Fredrik Noring
@ 2019-09-01 15:57 ` Fredrik Noring
  2019-09-01 15:58 ` [PATCH 054/120] MIPS: PS2: SIF: SIF register write command support Fredrik Noring
                   ` (67 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:57 UTC (permalink / raw)
  To: linux-mips

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 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

diff --git a/arch/mips/include/asm/mach-ps2/iop-error.h b/arch/mips/include/asm/mach-ps2/iop-error.h
new file mode 100644
index 000000000000..c01bcad28a99
--- /dev/null
+++ b/arch/mips/include/asm/mach-ps2/iop-error.h
@@ -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 */
diff --git a/drivers/ps2/sif.c b/drivers/ps2/sif.c
index 6564af245880..8320c5b68d17 100644
--- a/drivers/ps2/sif.c
+++ b/drivers/ps2/sif.c
@@ -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
  *
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 054/120] MIPS: PS2: SIF: SIF register write command support
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (52 preceding siblings ...)
  2019-09-01 15:57 ` [PATCH 053/120] MIPS: PS2: IOP: Define error numbers, descriptions and errno mapping Fredrik Noring
@ 2019-09-01 15:58 ` Fredrik Noring
  2019-09-01 15:58 ` [PATCH 055/120] MIPS: PS2: SIF: Respond to remote procedure call (RPC) bind command Fredrik Noring
                   ` (66 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:58 UTC (permalink / raw)
  To: linux-mips

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/mach-ps2/sif.h |  5 ++
 drivers/ps2/sif.c                    | 75 ++++++++++++++++++++++++++++
 2 files changed, 80 insertions(+)

diff --git a/arch/mips/include/asm/mach-ps2/sif.h b/arch/mips/include/asm/mach-ps2/sif.h
index 94da96bd21d1..9d660155cc5f 100644
--- a/arch/mips/include/asm/mach-ps2/sif.h
+++ b/arch/mips/include/asm/mach-ps2/sif.h
@@ -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 */
diff --git a/drivers/ps2/sif.c b/drivers/ps2/sif.c
index 8320c5b68d17..0edcda0b166d 100644
--- a/drivers/ps2/sif.c
+++ b/drivers/ps2/sif.c
@@ -51,10 +51,29 @@
 #define SIF0_BUFFER_SIZE	PAGE_SIZE
 #define SIF1_BUFFER_SIZE	PAGE_SIZE
 
+static DEFINE_SPINLOCK(sregs_lock);
+static s32 sregs[32];
+
 static iop_addr_t iop_buffer; /* Address of IOP SIF DMA receive address */
 static void *sif0_buffer;
 static void *sif1_buffer;
 
+static void cmd_write_sreg(const struct sif_cmd_header *header,
+	const void *data, void *arg)
+{
+	unsigned long flags;
+	const struct {
+		u32 reg;
+		s32 val;
+	} *packet = data;
+
+	BUG_ON(packet->reg >= ARRAY_SIZE(sregs));
+
+	spin_lock_irqsave(&sregs_lock, flags);
+	sregs[packet->reg] = packet->val;
+	spin_unlock_irqrestore(&sregs_lock, flags);
+}
+
 /**
  * sif_write_msflag - write to set main-to-sub flag register bits
  * @mask: MSFLAG register bit values to set
@@ -236,6 +255,34 @@ static int sif_cmd(u32 cmd_id, const void *pkt, size_t pktsize)
 	return sif_cmd_copy(cmd_id, pkt, pktsize, 0, NULL, 0);
 }
 
+static struct sif_cmd_handler *handler_from_cid(u32 cmd_id)
+{
+	enum { CMD_HANDLER_MAX = 64 };
+
+	static struct sif_cmd_handler sys_cmds[CMD_HANDLER_MAX];
+	static struct sif_cmd_handler usr_cmds[CMD_HANDLER_MAX];
+
+	const u32 id = cmd_id & ~SIF_CMD_ID_SYS;
+	struct sif_cmd_handler *cmd_handlers =
+		(cmd_id & SIF_CMD_ID_SYS) != 0 ?  sys_cmds : usr_cmds;
+
+	return id < CMD_HANDLER_MAX ? &cmd_handlers[id] : NULL;
+}
+
+int sif_request_cmd(u32 cmd_id, sif_cmd_cb cb, void *arg)
+{
+	struct sif_cmd_handler *handler = handler_from_cid(cmd_id);
+
+	if (handler == NULL)
+		return -EINVAL;
+
+	handler->cb = cb;
+	handler->arg = arg;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sif_request_cmd);
+
 static int iop_reset_arg(const char *arg)
 {
 	const size_t arglen = strlen(arg) + 1;
@@ -306,6 +353,25 @@ static int get_dma_buffers(void)
 	return 0;
 }
 
+static int sif_request_cmds(void)
+{
+	const struct {
+		u32 cmd_id;
+		sif_cmd_cb cb;
+		struct cmd_data *arg;
+	} cmds[] = {
+		{ SIF_CMD_WRITE_SREG, cmd_write_sreg, NULL },
+	};
+	int err = 0;
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(cmds) && err == 0; i++)
+		err = sif_request_cmd(cmds[i].cmd_id,
+			cmds[i].cb, cmds[i].arg);
+
+	return err;
+}
+
 static void sif_disable_dma(void)
 {
 	outl(DMAC_CHCR_STOP, DMAC_SIF0_CHCR);
@@ -384,6 +450,8 @@ EXPORT_SYMBOL_GPL(iop_error_message);
  *  8. The kernel reads the final SUBADDR register to obtain the command
  *     buffer for the IOP.
  *
+ *  9. Register SIF commands to enable remote procedure calls (RPCs).
+ *
  * Return: 0 on success, otherwise a negative error number
  */
 static int __init sif_init(void)
@@ -427,8 +495,15 @@ static int __init sif_init(void)
 		goto err_final_subaddr;
 	}
 
+	err = sif_request_cmds();
+	if (err) {
+		pr_err("sif: Failed to request commands with %d\n", err);
+		goto err_request_commands;
+	}
+
 	return 0;
 
+err_request_commands:
 err_final_subaddr:
 err_iop_reset:
 err_provisional_subaddr:
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 055/120] MIPS: PS2: SIF: Respond to remote procedure call (RPC) bind command
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (53 preceding siblings ...)
  2019-09-01 15:58 ` [PATCH 054/120] MIPS: PS2: SIF: SIF register write command support Fredrik Noring
@ 2019-09-01 15:58 ` Fredrik Noring
  2019-09-01 15:58 ` [PATCH 056/120] MIPS: PS2: SIF: Respond to RPC bind end command Fredrik Noring
                   ` (65 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:58 UTC (permalink / raw)
  To: linux-mips

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

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/mach-ps2/sif.h | 11 +++++++
 drivers/ps2/sif.c                    | 49 ++++++++++++++++++++++++++++
 2 files changed, 60 insertions(+)

diff --git a/arch/mips/include/asm/mach-ps2/sif.h b/arch/mips/include/asm/mach-ps2/sif.h
index 9d660155cc5f..3d163980a4be 100644
--- a/arch/mips/include/asm/mach-ps2/sif.h
+++ b/arch/mips/include/asm/mach-ps2/sif.h
@@ -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 */
diff --git a/drivers/ps2/sif.c b/drivers/ps2/sif.c
index 0edcda0b166d..7d0b120398d1 100644
--- a/drivers/ps2/sif.c
+++ b/drivers/ps2/sif.c
@@ -51,6 +51,35 @@
 #define SIF0_BUFFER_SIZE	PAGE_SIZE
 #define SIF1_BUFFER_SIZE	PAGE_SIZE
 
+struct sif_rpc_packet_header {
+	u32 rec_id;
+	void *pkt_addr;
+	u32 rpc_id;
+};
+
+struct sif_rpc_request_end_packet {
+	struct sif_rpc_packet_header header;
+	struct sif_rpc_client *client;
+	u32 client_id;
+
+	iop_addr_t server;
+	iop_addr_t server_buffer;
+
+	void *client_buffer;
+};
+
+struct sif_rpc_bind_packet {
+	struct sif_rpc_packet_header header;
+	struct sif_rpc_client *client;
+	u32 server_id;
+};
+
+struct sif_cmd_handler
+{
+	sif_cmd_cb cb;
+	void *arg;
+};
+
 static DEFINE_SPINLOCK(sregs_lock);
 static s32 sregs[32];
 
@@ -269,6 +298,21 @@ static struct sif_cmd_handler *handler_from_cid(u32 cmd_id)
 	return id < CMD_HANDLER_MAX ? &cmd_handlers[id] : NULL;
 }
 
+static void cmd_rpc_bind(const struct sif_cmd_header *header,
+	const void *data, void *arg)
+{
+	const struct sif_rpc_bind_packet *bind = data;
+	const struct sif_rpc_request_end_packet packet = {
+		.client    = bind->client,
+		.client_id = SIF_CMD_RPC_BIND,
+	};
+	int err;
+
+	err = sif_cmd(SIF_CMD_RPC_END, &packet, sizeof(packet));
+	if (err)
+		pr_err_once("sif: cmd_rpc_bind failed with %d\n", err);
+}
+
 int sif_request_cmd(u32 cmd_id, sif_cmd_cb cb, void *arg)
 {
 	struct sif_cmd_handler *handler = handler_from_cid(cmd_id);
@@ -361,6 +405,8 @@ static int sif_request_cmds(void)
 		struct cmd_data *arg;
 	} cmds[] = {
 		{ SIF_CMD_WRITE_SREG, cmd_write_sreg, NULL },
+
+		{ SIF_CMD_RPC_BIND,   cmd_rpc_bind,   NULL },
 	};
 	int err = 0;
 	size_t i;
@@ -458,6 +504,9 @@ static int __init sif_init(void)
 {
 	int err;
 
+	BUILD_BUG_ON(sizeof(struct sif_rpc_packet_header) != 12);
+	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);
 
 	sif_disable_dma();
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 056/120] MIPS: PS2: SIF: Respond to RPC bind end command
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (54 preceding siblings ...)
  2019-09-01 15:58 ` [PATCH 055/120] MIPS: PS2: SIF: Respond to remote procedure call (RPC) bind command Fredrik Noring
@ 2019-09-01 15:58 ` Fredrik Noring
  2019-09-01 15:59 ` [PATCH 057/120] MIPS: PS2: SIF: Reset the SIF0 (sub-to-main) DMA controller Fredrik Noring
                   ` (64 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:58 UTC (permalink / raw)
  To: linux-mips

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/ps2/sif.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/drivers/ps2/sif.c b/drivers/ps2/sif.c
index 7d0b120398d1..095d11810499 100644
--- a/drivers/ps2/sif.c
+++ b/drivers/ps2/sif.c
@@ -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;
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 057/120] MIPS: PS2: SIF: Reset the SIF0 (sub-to-main) DMA controller
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (55 preceding siblings ...)
  2019-09-01 15:58 ` [PATCH 056/120] MIPS: PS2: SIF: Respond to RPC bind end command Fredrik Noring
@ 2019-09-01 15:59 ` Fredrik Noring
  2019-09-01 15:59 ` [PATCH 058/120] MIPS: PS2: SIF: Handle SIF0 (sub-to-main) RPCs via interrupts Fredrik Noring
                   ` (63 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:59 UTC (permalink / raw)
  To: linux-mips

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/ps2/sif.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/drivers/ps2/sif.c b/drivers/ps2/sif.c
index 095d11810499..d077a0a97e02 100644
--- a/drivers/ps2/sif.c
+++ b/drivers/ps2/sif.c
@@ -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:
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 058/120] MIPS: PS2: SIF: Handle SIF0 (sub-to-main) RPCs via interrupts
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (56 preceding siblings ...)
  2019-09-01 15:59 ` [PATCH 057/120] MIPS: PS2: SIF: Reset the SIF0 (sub-to-main) DMA controller Fredrik Noring
@ 2019-09-01 15:59 ` Fredrik Noring
  2019-09-01 15:59 ` [PATCH 059/120] MIPS: PS2: SIF: Enable the IOP to issue SIF commands Fredrik Noring
                   ` (62 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:59 UTC (permalink / raw)
  To: linux-mips

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/ps2/sif.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)

diff --git a/drivers/ps2/sif.c b/drivers/ps2/sif.c
index d077a0a97e02..fac1a5117d1c 100644
--- a/drivers/ps2/sif.c
+++ b/drivers/ps2/sif.c
@@ -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();
 }
 
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 059/120] MIPS: PS2: SIF: Enable the IOP to issue SIF commands
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (57 preceding siblings ...)
  2019-09-01 15:59 ` [PATCH 058/120] MIPS: PS2: SIF: Handle SIF0 (sub-to-main) RPCs via interrupts Fredrik Noring
@ 2019-09-01 15:59 ` Fredrik Noring
  2019-09-01 16:00 ` [PATCH 060/120] MIPS: PS2: SIF: Enable the IOP to issue SIF RPCs Fredrik Noring
                   ` (61 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 15:59 UTC (permalink / raw)
  To: linux-mips

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/ps2/sif.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/drivers/ps2/sif.c b/drivers/ps2/sif.c
index fac1a5117d1c..b6c348974bdb 100644
--- a/drivers/ps2/sif.c
+++ b/drivers/ps2/sif.c
@@ -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();
 
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 060/120] MIPS: PS2: SIF: Enable the IOP to issue SIF RPCs
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (58 preceding siblings ...)
  2019-09-01 15:59 ` [PATCH 059/120] MIPS: PS2: SIF: Enable the IOP to issue SIF commands Fredrik Noring
@ 2019-09-01 16:00 ` Fredrik Noring
  2019-09-01 16:01 ` [PATCH 061/120] MIPS: PS2: SIF: sif_rpc_bind() to request an RPC server connection Fredrik Noring
                   ` (60 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:00 UTC (permalink / raw)
  To: linux-mips

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/ps2/sif.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)

diff --git a/drivers/ps2/sif.c b/drivers/ps2/sif.c
index b6c348974bdb..7a5aab785237 100644
--- a/drivers/ps2/sif.c
+++ b/drivers/ps2/sif.c
@@ -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);
 
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 061/120] MIPS: PS2: SIF: sif_rpc_bind() to request an RPC server connection
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (59 preceding siblings ...)
  2019-09-01 16:00 ` [PATCH 060/120] MIPS: PS2: SIF: Enable the IOP to issue SIF RPCs Fredrik Noring
@ 2019-09-01 16:01 ` Fredrik Noring
  2019-09-01 16:02 ` [PATCH 062/120] MIPS: PS2: SIF: sif_rpc_unbind() to release " Fredrik Noring
                   ` (59 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:01 UTC (permalink / raw)
  To: linux-mips

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

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/mach-ps2/sif.h |  2 ++
 drivers/ps2/sif.c                    | 38 ++++++++++++++++++++++++++++
 2 files changed, 40 insertions(+)

diff --git a/arch/mips/include/asm/mach-ps2/sif.h b/arch/mips/include/asm/mach-ps2/sif.h
index 3d163980a4be..1d9a7ede2fb5 100644
--- a/arch/mips/include/asm/mach-ps2/sif.h
+++ b/arch/mips/include/asm/mach-ps2/sif.h
@@ -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 */
diff --git a/drivers/ps2/sif.c b/drivers/ps2/sif.c
index 7a5aab785237..ecb26239518c 100644
--- a/drivers/ps2/sif.c
+++ b/drivers/ps2/sif.c
@@ -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)
 {
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 062/120] MIPS: PS2: SIF: sif_rpc_unbind() to release an RPC server connection
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (60 preceding siblings ...)
  2019-09-01 16:01 ` [PATCH 061/120] MIPS: PS2: SIF: sif_rpc_bind() to request an RPC server connection Fredrik Noring
@ 2019-09-01 16:02 ` " Fredrik Noring
  2019-09-01 16:02 ` [PATCH 063/120] MIPS: PS2: SIF: sif_rpc() to issue a remote procedure call Fredrik Noring
                   ` (58 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:02 UTC (permalink / raw)
  To: linux-mips

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/mach-ps2/sif.h |  1 +
 drivers/ps2/sif.c                    | 16 ++++++++++++++++
 2 files changed, 17 insertions(+)

diff --git a/arch/mips/include/asm/mach-ps2/sif.h b/arch/mips/include/asm/mach-ps2/sif.h
index 1d9a7ede2fb5..5a3128920c9a 100644
--- a/arch/mips/include/asm/mach-ps2/sif.h
+++ b/arch/mips/include/asm/mach-ps2/sif.h
@@ -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 */
diff --git a/drivers/ps2/sif.c b/drivers/ps2/sif.c
index ecb26239518c..3c098a46832c 100644
--- a/drivers/ps2/sif.c
+++ b/drivers/ps2/sif.c
@@ -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)
 {
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 063/120] MIPS: PS2: SIF: sif_rpc() to issue a remote procedure call
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (61 preceding siblings ...)
  2019-09-01 16:02 ` [PATCH 062/120] MIPS: PS2: SIF: sif_rpc_unbind() to release " Fredrik Noring
@ 2019-09-01 16:02 ` Fredrik Noring
  2019-09-01 16:03 ` [PATCH 064/120] MIPS: PS2: IOP: Permit /dev/mem to access IOP memory Fredrik Noring
                   ` (57 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:02 UTC (permalink / raw)
  To: linux-mips

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/mach-ps2/sif.h |  3 ++
 drivers/ps2/sif.c                    | 78 ++++++++++++++++++++++++++++
 2 files changed, 81 insertions(+)

diff --git a/arch/mips/include/asm/mach-ps2/sif.h b/arch/mips/include/asm/mach-ps2/sif.h
index 5a3128920c9a..d0e59692c3c3 100644
--- a/arch/mips/include/asm/mach-ps2/sif.h
+++ b/arch/mips/include/asm/mach-ps2/sif.h
@@ -88,4 +88,7 @@ struct sif_rpc_client
 int sif_rpc_bind(struct sif_rpc_client *client, u32 server_id);
 void sif_rpc_unbind(struct sif_rpc_client *client);
 
+int sif_rpc(struct sif_rpc_client *client, u32 rpc_id,
+	const void *send, size_t send_size, void *recv, size_t recv_size);
+
 #endif /* __ASM_MACH_PS2_SIF_H */
diff --git a/drivers/ps2/sif.c b/drivers/ps2/sif.c
index 3c098a46832c..5c3866c460af 100644
--- a/drivers/ps2/sif.c
+++ b/drivers/ps2/sif.c
@@ -76,6 +76,20 @@ struct sif_rpc_bind_packet {
 	u32 server_id;
 };
 
+struct sif_rpc_call_packet {
+	struct sif_rpc_packet_header header;
+	struct sif_rpc_client *client;
+	u32 rpc_id;
+
+	u32 send_size;
+
+	dma_addr_t recv_addr;
+	u32 recv_size;
+	u32 recv_mode;
+
+	iop_addr_t server;
+};
+
 struct sif_cmd_change_addr_packet {
 	iop_addr_t addr;
 };
@@ -441,6 +455,68 @@ void sif_rpc_unbind(struct sif_rpc_client *client)
 }
 EXPORT_SYMBOL_GPL(sif_rpc_unbind);
 
+static int sif_rpc_dma(struct sif_rpc_client *client, u32 rpc_id,
+	const void *send, size_t send_size, size_t recv_size)
+{
+	const struct sif_rpc_call_packet call = {
+		.rpc_id    = rpc_id,
+		.send_size = send_size,
+		.recv_addr = virt_to_phys(client->client_buffer),
+		.recv_size = recv_size,
+		.recv_mode = 1,
+		.client    = client,
+		.server    = client->server
+	};
+	int err;
+
+	if (call.send_size != send_size)
+		return -EINVAL;
+	if (recv_size > client->client_size_max)
+		return -EINVAL;
+
+	reinit_completion(&client->done);
+
+	err = sif_cmd_copy(SIF_CMD_RPC_CALL, &call, sizeof(call),
+		client->server_buffer, send, send_size);
+	if (err)
+		return err;
+
+	wait_for_completion(&client->done);
+
+	if (recv_size > 0)
+		dma_cache_inv((unsigned long)client->client_buffer, recv_size);
+
+	return 0;
+}
+
+/**
+ * sif_rpc - issue a remote procedure call (RPC)
+ * @client: RPC client object initialised with sif_rpc_bind()
+ * @rpc_id: identification number of remote procedure to call
+ * @send: data to send with the RPC via DMA, or %NULL if @send_size is zero
+ * @send_size: size in bytes of the data to send
+ * @recv: data to receive from the RPC via DMA, or %NULL if @recv_size is zero
+ * @recv_size: size in bytes of the data to receive
+ *
+ * Due to DMA hardware restrictions, the @send buffer must align with 16-byte
+ * memory boundaries and @send_size is rounded up to a 16-byte multiple.
+ *
+ * FIXME: Lift these send restrictions and use memcpy similar to receive?
+ *
+ * Return: 0 on success, otherwise a negative error number
+ */
+int sif_rpc(struct sif_rpc_client *client, u32 rpc_id,
+	const void *send, size_t send_size, void *recv, size_t recv_size)
+{
+	int err = sif_rpc_dma(client, rpc_id, send, send_size, recv_size);
+
+	if (err == 0)
+		memcpy(recv, client->client_buffer, recv_size);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(sif_rpc);
+
 static void cmd_rpc_end(const struct sif_cmd_header *header,
 	const void *data, void *arg)
 {
@@ -699,6 +775,8 @@ static int __init sif_init(void)
 	BUILD_BUG_ON(sizeof(struct sif_rpc_packet_header) != 12);
 	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_rpc_call_packet) != 40);
+
 	BUILD_BUG_ON(sizeof(struct sif_cmd_header) != 16);
 	BUILD_BUG_ON(sizeof(struct sif_cmd_change_addr_packet) != 4);
 
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 064/120] MIPS: PS2: IOP: Permit /dev/mem to access IOP memory
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (62 preceding siblings ...)
  2019-09-01 16:02 ` [PATCH 063/120] MIPS: PS2: SIF: sif_rpc() to issue a remote procedure call Fredrik Noring
@ 2019-09-01 16:03 ` Fredrik Noring
  2019-09-01 16:03 ` [PATCH 065/120] MIPS: PS2: IOP: I/O processor memory support Fredrik Noring
                   ` (56 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:03 UTC (permalink / raw)
  To: linux-mips

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 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

diff --git a/arch/mips/include/asm/mach-ps2/iop.h b/arch/mips/include/asm/mach-ps2/iop.h
new file mode 100644
index 000000000000..65585451e743
--- /dev/null
+++ b/arch/mips/include/asm/mach-ps2/iop.h
@@ -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 */
diff --git a/arch/mips/ps2/memory.c b/arch/mips/ps2/memory.c
index c513b6912bb0..51614b6d0515 100644
--- a/arch/mips/ps2/memory.c
+++ b/arch/mips/ps2/memory.c
@@ -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);
 }
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 065/120] MIPS: PS2: IOP: I/O processor memory support
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (63 preceding siblings ...)
  2019-09-01 16:03 ` [PATCH 064/120] MIPS: PS2: IOP: Permit /dev/mem to access IOP memory Fredrik Noring
@ 2019-09-01 16:03 ` Fredrik Noring
  2019-09-01 16:10 ` [PATCH 066/120] FIXME: Export _dma_cache_{wback,wback_inv,inv} Fredrik Noring
                   ` (55 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:03 UTC (permalink / raw)
  To: linux-mips

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 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

diff --git a/arch/mips/include/asm/mach-ps2/iop-memory.h b/arch/mips/include/asm/mach-ps2/iop-memory.h
new file mode 100644
index 000000000000..6efdb490c7b9
--- /dev/null
+++ b/arch/mips/include/asm/mach-ps2/iop-memory.h
@@ -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 */
diff --git a/drivers/ps2/Makefile b/drivers/ps2/Makefile
index ef561a802bdd..6f193007ebc6 100644
--- a/drivers/ps2/Makefile
+++ b/drivers/ps2/Makefile
@@ -1,2 +1,3 @@
+obj-m				+= iop-memory.o
 obj-m				+= iop-registers.o
 obj-m				+= sif.o
diff --git a/drivers/ps2/iop-memory.c b/drivers/ps2/iop-memory.c
new file mode 100644
index 000000000000..829d7174da0e
--- /dev/null
+++ b/drivers/ps2/iop-memory.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 input/output processor (IOP) memory
+ *
+ * Copyright (C) 2018 Fredrik Noring
+ */
+
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/mach-ps2/iop-memory.h>
+
+/**
+ * iop_phys_to_bus - kernel physical to I/O processor (IOP) bus address
+ * @paddr: kernel physical address
+ *
+ * Context: any
+ * Return: I/O processor (IOP) bus address
+ */
+iop_addr_t iop_phys_to_bus(phys_addr_t paddr)
+{
+	return (u32)paddr - IOP_RAM_BASE;
+}
+EXPORT_SYMBOL(iop_phys_to_bus);
+
+/**
+ * iop_bus_to_phys - I/O processor (IOP) bus address to kernel physical
+ * @baddr: I/O processor (IOP) bus address
+ *
+ * Context: any
+ * Return: kernel physical address
+ */
+phys_addr_t iop_bus_to_phys(iop_addr_t baddr)
+{
+	return (u32)baddr + IOP_RAM_BASE;
+}
+EXPORT_SYMBOL(iop_bus_to_phys);
+
+/**
+ * iop_bus_to_virt - I/O processor (IOP) bus address to kernel virtual
+ * @baddr: I/O processor (IOP) bus address
+ *
+ * Context: any
+ * Return: kernel virtual address
+ */
+void *iop_bus_to_virt(iop_addr_t baddr)
+{
+	return phys_to_virt(iop_bus_to_phys(baddr));
+}
+EXPORT_SYMBOL(iop_bus_to_virt);
+
+MODULE_DESCRIPTION("PlayStation 2 input/output processor (IOP) memory");
+MODULE_AUTHOR("Fredrik Noring");
+MODULE_LICENSE("GPL");
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 066/120] FIXME: Export _dma_cache_{wback,wback_inv,inv}
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (64 preceding siblings ...)
  2019-09-01 16:03 ` [PATCH 065/120] MIPS: PS2: IOP: I/O processor memory support Fredrik Noring
@ 2019-09-01 16:10 ` Fredrik Noring
  2019-09-01 16:10 ` [PATCH 067/120] MIPS: PS2: IOP: Module linking support Fredrik Noring
                   ` (54 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:10 UTC (permalink / raw)
  To: Paul Burton, linux-mips; +Cc: Maciej W. Rozycki, Jürgen Urban

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

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/mm/cache.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/mips/mm/cache.c b/arch/mips/mm/cache.c
index 33b409391ddb..a01923b30086 100644
--- a/arch/mips/mm/cache.c
+++ b/arch/mips/mm/cache.c
@@ -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 */
 
 /*
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 067/120] MIPS: PS2: IOP: Module linking support
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (65 preceding siblings ...)
  2019-09-01 16:10 ` [PATCH 066/120] FIXME: Export _dma_cache_{wback,wback_inv,inv} Fredrik Noring
@ 2019-09-01 16:10 ` Fredrik Noring
  2019-09-01 16:11 ` [PATCH 068/120] MIPS: PS2: IOP: Verify that modules are IRX objects Fredrik Noring
                   ` (53 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:10 UTC (permalink / raw)
  To: linux-mips

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

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

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

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 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

diff --git a/arch/mips/include/asm/mach-ps2/iop-module.h b/arch/mips/include/asm/mach-ps2/iop-module.h
new file mode 100644
index 000000000000..f61141031de0
--- /dev/null
+++ b/arch/mips/include/asm/mach-ps2/iop-module.h
@@ -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 */
diff --git a/drivers/Makefile b/drivers/Makefile
index 6d37564e783c..1ed5b83dc528 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -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/
diff --git a/drivers/ps2/Makefile b/drivers/ps2/Makefile
index 6f193007ebc6..b04e4d3c3374 100644
--- a/drivers/ps2/Makefile
+++ b/drivers/ps2/Makefile
@@ -1,3 +1,4 @@
 obj-m				+= iop-memory.o
+obj-m				+= iop-module.o
 obj-m				+= iop-registers.o
 obj-m				+= sif.o
diff --git a/drivers/ps2/iop-module.c b/drivers/ps2/iop-module.c
new file mode 100644
index 000000000000..0e4a2173c001
--- /dev/null
+++ b/drivers/ps2/iop-module.c
@@ -0,0 +1,277 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 input/output processor (IOP) module linker
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+/**
+ * DOC: PlayStation 2 input/output processor (IOP) module linker
+ *
+ * IOP modules are IRX objects based on the executable and linkable format
+ * (ELF). All valid IOP modules have a special `.iopmod` section containing
+ * the module name, version, etc.
+ *
+ * When the IOP is reset, a set of modules are automatically linked from
+ * read-only memory (ROM). Non-ROM modules are handled as firmware by the
+ * IOP module linker.
+ *
+ * IOP modules may import and export any number of library functions,
+ * including non at all. Imported libraries must be resolved and prelinked
+ * before the given module is allowed to link itself. Other modules can link
+ * with its exported libraries.
+ *
+ * IOP modules begin to execute their entry function immediately after linking.
+ * The modules can either stay resident in the IOP, and provide services, or
+ * unlink themselves when exiting the entry function. Many modules provide
+ * remote procedure call (RPC) services via the sub-system interface (SIF).
+ */
+
+#include <linux/bcd.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "uapi/linux/elf.h"
+
+#include <asm/mach-ps2/iop-error.h>
+#include <asm/mach-ps2/iop-heap.h>
+#include <asm/mach-ps2/iop-memory.h>
+#include <asm/mach-ps2/iop-module.h>
+#include <asm/mach-ps2/rom.h>
+#include <asm/mach-ps2/sif.h>
+
+enum iop_module_rpc_ops {
+	rpo_mod_load = 0,
+	rpo_elf_load = 1,
+	rpo_set_addr = 2,
+	rpo_get_addr = 3,
+	rpo_mg_mod_load = 4,
+	rpo_mg_elf_load = 5,
+	rpo_mod_buf_load = 6,
+	rpo_mod_stop = 7,
+	rpo_mod_unload = 8,
+	rpo_search_mod_by_name = 9,
+	rpo_search_mod_by_address = 10
+};
+
+/** The @iop_module_lock must be taken for all IOP module linking operations */
+static DEFINE_MUTEX(iop_module_lock);
+
+static struct device *iop_module_device;
+static struct sif_rpc_client load_file_rpc_client;
+
+#define IOPMOD_MAX_PATH		252
+#define IOPMOD_MAX_ARG		252
+
+/**
+ * major_version - major version of version in BCD
+ * @version: 16-bit version in BCD, with 8-bit minor and 8-bit major
+ *
+ * Return: major version
+ */
+static unsigned int major_version(unsigned int version)
+{
+	return bcd2bin((version >> 8) & 0xff);
+}
+
+/**
+ * minor_version - minor version of version in BCD
+ * @version: 16-bit version in BCD, with 8-bit minor and 8-bit major
+ *
+ * Return: minor version
+ */
+static unsigned int minor_version(unsigned int version)
+{
+	return bcd2bin(version & 0xff);
+}
+
+/**
+ * iop_module_link_buffer - link IOP module given in buffer
+ * @buf: buffer containing the IOP module to link
+ * @nbyte: size in bytes of given buffer
+ * @arg: arguments to the IOP module entry function, or %NULL
+ *
+ * Return: 0 on success, otherwise a negative error number
+ */
+static int iop_module_link_buffer(const void *buf, size_t nbyte,
+	const char *arg)
+{
+	const char * const arg_ = arg ? arg : "";
+	const size_t arg_size = strlen(arg_) + 1;
+	struct {
+		u32 addr;
+		u32 arg_size;
+		char filepath[IOPMOD_MAX_PATH];
+		char arg[IOPMOD_MAX_ARG];
+	} link = {
+		.addr = iop_alloc(nbyte),
+		.arg_size = arg_size
+	};
+	struct {
+		s32 status;
+		u32 modres;
+	} result;
+	int err;
+
+	BUILD_BUG_ON(sizeof(link) != 512);
+
+	if (!link.addr)
+		return -ENOMEM;
+
+	/* Copy the module to IOP memory. */
+	memcpy(iop_bus_to_virt(link.addr), buf, nbyte);
+
+	/* Make the module visible to the IOP. */
+	dma_cache_wback((unsigned long)iop_bus_to_virt(link.addr), nbyte);
+
+	if (arg_size >= sizeof(link.arg)) {
+		err = -EOVERFLOW;
+		goto err_out;
+	}
+	memcpy(link.arg, arg_, arg_size);
+
+	err = sif_rpc(&load_file_rpc_client, rpo_mod_buf_load,
+		&link, sizeof(link), &result, sizeof(result));
+	if (err < 0)
+		goto err_out;
+
+	if (result.status < 0) {
+		pr_err("iop-module: %s: sif_rpc failed with %d: %s\n", __func__,
+			result.status, iop_error_message(result.status));
+		err = errno_for_iop_error(result.status);
+		goto err_out;
+	}
+
+	iop_free(link.addr);
+	return 0;
+
+err_out:
+	iop_free(link.addr);
+	return err;
+}
+
+static int iop_module_request_firmware(
+	const char *name, int version, const char *arg);
+
+/**
+ * iop_module_request_firmware - link IOP module as firmware
+ * @name: name of requested module
+ * @version: requested version in BCD, where major must match with a least
+ * 	the same minor
+ * @arg: module arguments or %NULL
+ *
+ * Return: 0 on success, otherwise a negative error number
+ */
+static int iop_module_request_firmware(
+	const char *name, int version, const char *arg)
+{
+	const struct firmware *fw = NULL;
+	const struct elf32_hdr *ehdr;
+	char filepath[32];
+	int err;
+
+	pr_debug("iop-module: %s module linking as firmware\n", name);
+
+	if (snprintf(filepath, sizeof(filepath),
+			"ps2/%s.irx", name) == sizeof(filepath) - 1) {
+		err = -ENAMETOOLONG;
+		goto err_name;
+	}
+
+	err = request_firmware(&fw, filepath, iop_module_device);
+	if (err < 0)
+		goto err_request;
+
+	ehdr = (const struct elf32_hdr *)fw->data;
+
+	err = iop_module_link_buffer(fw->data, fw->size, arg);
+
+err_request:
+err_name:
+
+	if (err < 0)
+		pr_err("iop-module: %s module version %u.%u request failed with %d\n",
+			filepath, major_version(version),
+			minor_version(version), err);
+
+	release_firmware(fw);
+
+	return err;
+}
+
+/**
+ * iop_module_request - link requested IOP module unless it is already linked
+ * @name: name of requested module
+ * @version: requested version in BCD, where major must match with a least
+ * 	the same minor
+ * @arg: module arguments or %NULL
+ *
+ * Module library dependencies are resolved and prelinked as necessary. Module
+ * files are handled as firmware by the IOP module linker.
+ *
+ * IOP module link requests are only permitted if the major versions match
+ * and the version is at least of the same minor as the requested version.
+ *
+ * Context: mutex
+ * Return: 0 on success, otherwise a negative error number
+ */
+int iop_module_request(const char *name, int version, const char *arg)
+{
+	int err;
+
+	mutex_lock(&iop_module_lock);
+
+	pr_debug("iop-module: %s module version %u.%u requested%s%s\n",
+		name, major_version(version), minor_version(version),
+		arg ? " with argument " : "", arg ? arg : "");
+
+	err = iop_module_request_firmware(name, version, arg);
+
+	if (err)
+		pr_debug("iop-module: %s module request resulted in %d\n",
+			name, err);
+	else
+		pr_debug("iop-module: %s module request successful\n", name);
+
+	mutex_unlock(&iop_module_lock);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(iop_module_request);
+
+static int __init iop_module_init(void)
+{
+	int err;
+
+	iop_module_device = root_device_register("iop-module");
+	if (!iop_module_device) {
+		pr_err("iop-module: Failed to register root device\n");
+		return -ENOMEM;
+	}
+
+	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);
+		goto err_bind;
+	}
+
+	return 0;
+
+err_bind:
+	root_device_unregister(iop_module_device);
+
+	return err;
+}
+
+module_init(iop_module_init);
+
+MODULE_DESCRIPTION("PlayStation 2 input/output processor (IOP) module linker");
+MODULE_AUTHOR("Fredrik Noring");
+MODULE_LICENSE("GPL");
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 068/120] MIPS: PS2: IOP: Verify that modules are IRX objects
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (66 preceding siblings ...)
  2019-09-01 16:10 ` [PATCH 067/120] MIPS: PS2: IOP: Module linking support Fredrik Noring
@ 2019-09-01 16:11 ` Fredrik Noring
  2019-09-01 16:11 ` [PATCH 069/120] MIPS: PS2: IOP: Module version compatibility verification Fredrik Noring
                   ` (52 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:11 UTC (permalink / raw)
  To: linux-mips

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/ps2/iop-module.c | 154 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 154 insertions(+)

diff --git a/drivers/ps2/iop-module.c b/drivers/ps2/iop-module.c
index 0e4a2173c001..d332a7d1af60 100644
--- a/drivers/ps2/iop-module.c
+++ b/drivers/ps2/iop-module.c
@@ -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:
 
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 069/120] MIPS: PS2: IOP: Module version compatibility verification
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (67 preceding siblings ...)
  2019-09-01 16:11 ` [PATCH 068/120] MIPS: PS2: IOP: Verify that modules are IRX objects Fredrik Noring
@ 2019-09-01 16:11 ` Fredrik Noring
  2019-09-01 16:11 ` [PATCH 070/120] MIPS: PS2: IOP: Avoid linking already linked library modules Fredrik Noring
                   ` (51 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:11 UTC (permalink / raw)
  To: linux-mips

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

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/ps2/iop-module.c | 44 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/drivers/ps2/iop-module.c b/drivers/ps2/iop-module.c
index d332a7d1af60..532b3cce91c0 100644
--- a/drivers/ps2/iop-module.c
+++ b/drivers/ps2/iop-module.c
@@ -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:
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 070/120] MIPS: PS2: IOP: Avoid linking already linked library modules
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (68 preceding siblings ...)
  2019-09-01 16:11 ` [PATCH 069/120] MIPS: PS2: IOP: Module version compatibility verification Fredrik Noring
@ 2019-09-01 16:11 ` Fredrik Noring
  2019-09-01 16:12 ` [PATCH 071/120] MIPS: PS2: IOP: Resolve module dependencies Fredrik Noring
                   ` (50 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:11 UTC (permalink / raw)
  To: linux-mips

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

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/ps2/iop-module.c | 282 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 282 insertions(+)

diff --git a/drivers/ps2/iop-module.c b/drivers/ps2/iop-module.c
index 532b3cce91c0..ef7b254bb2cf 100644
--- a/drivers/ps2/iop-module.c
+++ b/drivers/ps2/iop-module.c
@@ -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),
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 071/120] MIPS: PS2: IOP: Resolve module dependencies
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (69 preceding siblings ...)
  2019-09-01 16:11 ` [PATCH 070/120] MIPS: PS2: IOP: Avoid linking already linked library modules Fredrik Noring
@ 2019-09-01 16:12 ` Fredrik Noring
  2019-09-01 16:12 ` [PATCH 072/120] MIPS: PS2: IOP: SIF printk command support Fredrik Noring
                   ` (49 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:12 UTC (permalink / raw)
  To: linux-mips

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

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/ps2/iop-module.c | 166 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 166 insertions(+)

diff --git a/drivers/ps2/iop-module.c b/drivers/ps2/iop-module.c
index ef7b254bb2cf..bb4814b5d3c4 100644
--- a/drivers/ps2/iop-module.c
+++ b/drivers/ps2/iop-module.c
@@ -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:
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 072/120] MIPS: PS2: IOP: SIF printk command support
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (70 preceding siblings ...)
  2019-09-01 16:12 ` [PATCH 071/120] MIPS: PS2: IOP: Resolve module dependencies Fredrik Noring
@ 2019-09-01 16:12 ` Fredrik Noring
  2019-09-01 17:44   ` Sergei Shtylyov
  2019-09-01 16:16 ` [PATCH 073/120] MIPS: PS2: IOP: Heap memory allocate and free Fredrik Noring
                   ` (48 subsequent siblings)
  120 siblings, 1 reply; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:12 UTC (permalink / raw)
  To: linux-mips

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 <noring@nocrew.org>
---
 drivers/ps2/iop-module.c | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/drivers/ps2/iop-module.c b/drivers/ps2/iop-module.c
index bb4814b5d3c4..18020b3673d3 100644
--- a/drivers/ps2/iop-module.c
+++ b/drivers/ps2/iop-module.c
@@ -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);
 
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 073/120] MIPS: PS2: IOP: Heap memory allocate and free
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (71 preceding siblings ...)
  2019-09-01 16:12 ` [PATCH 072/120] MIPS: PS2: IOP: SIF printk command support Fredrik Noring
@ 2019-09-01 16:16 ` Fredrik Noring
  2019-09-01 16:16 ` [PATCH 074/120] MIPS: PS2: SIF: Request RPC IRQ command Fredrik Noring
                   ` (47 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:16 UTC (permalink / raw)
  To: linux-mips

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 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

diff --git a/arch/mips/include/asm/mach-ps2/iop-heap.h b/arch/mips/include/asm/mach-ps2/iop-heap.h
new file mode 100644
index 000000000000..ee96e2ffba83
--- /dev/null
+++ b/arch/mips/include/asm/mach-ps2/iop-heap.h
@@ -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 */
diff --git a/drivers/ps2/Makefile b/drivers/ps2/Makefile
index b04e4d3c3374..02a5756cb059 100644
--- a/drivers/ps2/Makefile
+++ b/drivers/ps2/Makefile
@@ -1,3 +1,4 @@
+obj-m				+= iop-heap.o
 obj-m				+= iop-memory.o
 obj-m				+= iop-module.o
 obj-m				+= iop-registers.o
diff --git a/drivers/ps2/iop-heap.c b/drivers/ps2/iop-heap.c
new file mode 100644
index 000000000000..f803401f59e5
--- /dev/null
+++ b/drivers/ps2/iop-heap.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 input/output processor (IOP) heap memory
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/module.h>
+
+#include <asm/mach-ps2/iop-error.h>
+#include <asm/mach-ps2/iop-heap.h>
+#include <asm/mach-ps2/sif.h>
+
+/**
+ * enum iop_heap_rpc_ops - I/O processor (IOP) heap RPC operations
+ * @rpo_alloc: allocate IOP memory
+ * @rpo_free: free IOP memory
+ * @rpo_load: FIXME
+ */
+enum iop_heap_rpc_ops {
+	rpo_alloc = 1,
+	rpo_free  = 2,
+	rpo_load  = 3,
+};
+
+static struct sif_rpc_client iop_heap_rpc;
+
+/**
+ * iop_alloc - allocate IOP memory
+ * @nbyte: number of bytes to allocate
+ *
+ * Context: sleep
+ * Return: IOP address, or zero if the allocation failed
+ */
+iop_addr_t iop_alloc(size_t nbyte)
+{
+	const u32 size_arg = nbyte;
+	u32 iop_addr;
+
+	if (size_arg != nbyte)
+		return 0;
+
+	return sif_rpc(&iop_heap_rpc, rpo_alloc,
+		&size_arg, sizeof(size_arg),
+		&iop_addr, sizeof(iop_addr)) < 0 ? 0 : iop_addr;
+}
+EXPORT_SYMBOL(iop_alloc);
+
+/**
+ * iop_free - free previously allocated IOP memory
+ * @baddr: IOP address, or zero
+ *
+ * Context: sleep
+ * Return: 0 on success, otherwise a negative error number
+ */
+int iop_free(iop_addr_t baddr)
+{
+	const u32 addr_arg = baddr;
+	s32 status;
+	int err;
+
+	if (!baddr)
+		return 0;
+
+	err = sif_rpc(&iop_heap_rpc, rpo_free,
+		&addr_arg, sizeof(addr_arg),
+		&status, sizeof(status));
+
+	return err < 0 ? err : errno_for_iop_error(status);
+}
+EXPORT_SYMBOL(iop_free);
+
+static int __init iop_heap_init(void)
+{
+	return sif_rpc_bind(&iop_heap_rpc, SIF_SID_HEAP);
+}
+
+static void __exit iop_heap_exit(void)
+{
+	sif_rpc_unbind(&iop_heap_rpc);
+}
+
+module_init(iop_heap_init);
+module_exit(iop_heap_exit);
+
+MODULE_DESCRIPTION("PlayStation 2 input/output processor (IOP) heap memory");
+MODULE_AUTHOR("Fredrik Noring");
+MODULE_LICENSE("GPL");
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 074/120] MIPS: PS2: SIF: Request RPC IRQ command
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (72 preceding siblings ...)
  2019-09-01 16:16 ` [PATCH 073/120] MIPS: PS2: IOP: Heap memory allocate and free Fredrik Noring
@ 2019-09-01 16:16 ` Fredrik Noring
  2019-09-01 16:17 ` [PATCH 075/120] MIPS: PS2: IOP: IRQ support Fredrik Noring
                   ` (46 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:16 UTC (permalink / raw)
  To: linux-mips

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 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(+)

diff --git a/arch/mips/include/asm/mach-ps2/irq.h b/arch/mips/include/asm/mach-ps2/irq.h
index 16c96aa7ca09..64d3fbf4789e 100644
--- a/arch/mips/include/asm/mach-ps2/irq.h
+++ b/arch/mips/include/asm/mach-ps2/irq.h
@@ -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 */
diff --git a/arch/mips/ps2/intc-irq.c b/arch/mips/ps2/intc-irq.c
index 36cdc3dd31ca..08659758e85f 100644
--- a/arch/mips/ps2/intc-irq.c
+++ b/arch/mips/ps2/intc-irq.c
@@ -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;
diff --git a/drivers/ps2/sif.c b/drivers/ps2/sif.c
index 5c3866c460af..a7f4f00a539c 100644
--- a/drivers/ps2/sif.c
+++ b/drivers/ps2/sif.c
@@ -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 },
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 075/120] MIPS: PS2: IOP: IRQ support
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (73 preceding siblings ...)
  2019-09-01 16:16 ` [PATCH 074/120] MIPS: PS2: SIF: Request RPC IRQ command Fredrik Noring
@ 2019-09-01 16:17 ` Fredrik Noring
  2019-09-01 16:17 ` [PATCH 076/120] MIPS: PS2: GS: Define privileged Graphics Synthesizer registers Fredrik Noring
                   ` (45 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:17 UTC (permalink / raw)
  To: linux-mips

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

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 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

diff --git a/arch/mips/include/asm/mach-ps2/irq.h b/arch/mips/include/asm/mach-ps2/irq.h
index 64d3fbf4789e..57b3e539ad92 100644
--- a/arch/mips/include/asm/mach-ps2/irq.h
+++ b/arch/mips/include/asm/mach-ps2/irq.h
@@ -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);
 
diff --git a/drivers/ps2/Makefile b/drivers/ps2/Makefile
index 02a5756cb059..dd00eff52ef3 100644
--- a/drivers/ps2/Makefile
+++ b/drivers/ps2/Makefile
@@ -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
diff --git a/drivers/ps2/iop-irq.c b/drivers/ps2/iop-irq.c
new file mode 100644
index 000000000000..859a3f64dd00
--- /dev/null
+++ b/drivers/ps2/iop-irq.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 input/output processor (IOP) IRQs
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include <asm/mach-ps2/iop-module.h>
+#include <asm/mach-ps2/irq.h>
+#include <asm/mach-ps2/sif.h>
+
+/**
+ * enum iop_irq_relay_rpc_ops - IOP IRQ relay RPC operations
+ * @rpo_request_irq: request IRQ mapping
+ * @rpo_release_irq: release IRQ mapping
+ * @rpo_remap_irq: remap existing IRQ mapping
+ */
+enum iop_irq_relay_rpc_ops {
+	rpo_request_irq = 1,
+	rpo_release_irq = 2,
+	rpo_remap_irq   = 3,
+};
+
+/**
+ * struct iop_rpc_relay_map - IOP IRQ relay mapping
+ * @u8 iop: IOP IRQ map source
+ * @u8 map: main IRQ map target
+ * @u8 rpc: %true for RPC relay, %false for SMFLAG relay
+ */
+struct iop_rpc_relay_map {
+	u8 iop;
+	u8 map;
+	u8 rpc;
+};
+
+/**
+ * struct iop_rpc_relay_release - IOP IRQ relay to release
+ * @iop: IOP IRQ to release mapping for
+ */
+struct iop_rpc_relay_release {
+	u8 iop;
+};
+
+static struct sif_rpc_client iop_irq_rpc;
+
+static unsigned int iop_irq_startup(struct irq_data *data)
+{
+	static bool irq_relay = false;
+
+	const struct iop_rpc_relay_map arg = {
+		.iop = data->irq - IOP_IRQ_BASE,
+		.map = data->irq,
+		.rpc = true,	/* FIXME: Also implement SMFLAG relay */
+	};
+	s32 status;
+	int err;
+
+	BUG_ON(in_irq());
+
+	if (!irq_relay) {
+		int id;
+
+		/*
+		 * The main reason for requesting the IOP IRQ relay module here
+		 * instead of in irqrelay_init() is that now the console may be
+		 * visible to print messages if there are problems.
+		 */
+		id = iop_module_request("irqrelay", 0x0100, NULL);
+		if (id < 0)
+			return id;
+
+		err = sif_rpc_bind(&iop_irq_rpc, SIF_SID_IRQ_RELAY);
+		if (err < 0) {
+			pr_err("%s: sif_rpc_bind failed with %d\n",
+				__func__, err);
+			return err;
+		}
+
+		irq_relay = true;
+	}
+
+	err = sif_rpc(&iop_irq_rpc, rpo_request_irq,
+		&arg, sizeof(arg),
+		&status, sizeof(status));
+
+	pr_debug("%s: err %d status %d\n", __func__, err, status);
+
+	return err < 0 ? err : status;
+}
+
+static void iop_irq_shutdown(struct irq_data *data)
+{
+	const struct iop_rpc_relay_release arg = {
+		.iop = data->irq - IOP_IRQ_BASE,
+	};
+	s32 status;
+	int err;
+
+	BUG_ON(in_irq());
+
+	err = sif_rpc(&iop_irq_rpc, rpo_release_irq,
+		&arg, sizeof(arg),
+		&status, sizeof(status));
+
+	pr_debug("%s: err %d status %d\n", __func__, err, status);
+}
+
+#define IOP_IRQ_TYPE(irq_, name_)					\
+	{								\
+		.irq = irq_,						\
+		.irq_chip = {						\
+			.name = name_,					\
+			.irq_startup = iop_irq_startup,			\
+			.irq_shutdown = iop_irq_shutdown,		\
+		}							\
+	}
+
+static struct {
+	unsigned int irq;
+	struct irq_chip irq_chip;
+} iop_irqs[] = {
+	IOP_IRQ_TYPE(IRQ_IOP_VBLANK,        "IOP VBLANK"),
+	IOP_IRQ_TYPE(IRQ_IOP_SBUS,          "IOP SBUS"),
+	IOP_IRQ_TYPE(IRQ_IOP_CDVD,          "IOP CDVD"),
+	IOP_IRQ_TYPE(IRQ_IOP_DMA,           "IOP DMA"),
+	IOP_IRQ_TYPE(IRQ_IOP_RTC0,          "IOP RTC0"),
+	IOP_IRQ_TYPE(IRQ_IOP_RTC1,          "IOP RTC1"),
+	IOP_IRQ_TYPE(IRQ_IOP_RTC2,          "IOP RTC2"),
+	IOP_IRQ_TYPE(IRQ_IOP_SIO0,          "IOP SIO0"),
+	IOP_IRQ_TYPE(IRQ_IOP_SIO1,          "IOP SIO1"),
+	IOP_IRQ_TYPE(IRQ_IOP_SPU,           "IOP SPU"),
+	IOP_IRQ_TYPE(IRQ_IOP_PIO,           "IOP PIO"),
+	IOP_IRQ_TYPE(IRQ_IOP_EVBLANK,       "IOP EVBLANK"),
+	IOP_IRQ_TYPE(IRQ_IOP_DVD,           "IOP DVD"),
+	IOP_IRQ_TYPE(IRQ_IOP_DEV9,          "IOP DEV9"),
+	IOP_IRQ_TYPE(IRQ_IOP_RTC3,          "IOP RTC3"),
+	IOP_IRQ_TYPE(IRQ_IOP_RTC4,          "IOP RTC4"),
+	IOP_IRQ_TYPE(IRQ_IOP_RTC5,          "IOP RTC5"),
+	IOP_IRQ_TYPE(IRQ_IOP_SIO2,          "IOP SIO2"),
+	IOP_IRQ_TYPE(IRQ_IOP_HTR0,          "IOP HTR0"),
+	IOP_IRQ_TYPE(IRQ_IOP_HTR1,          "IOP HTR1"),
+	IOP_IRQ_TYPE(IRQ_IOP_HTR2,          "IOP HTR2"),
+	IOP_IRQ_TYPE(IRQ_IOP_HTR3,          "IOP HTR3"),
+	IOP_IRQ_TYPE(IRQ_IOP_USB,           "IOP USB"),
+	IOP_IRQ_TYPE(IRQ_IOP_EXTR,          "IOP EXTR"),
+	IOP_IRQ_TYPE(IRQ_IOP_ILINK,         "IOP iLink"),
+	IOP_IRQ_TYPE(IRQ_IOP_ILNKDMA,       "IOP ILink DMA"),
+	IOP_IRQ_TYPE(IRQ_IOP_DMAC_MDEC_IN,  "IOP DMAC MDEC IN"),
+	IOP_IRQ_TYPE(IRQ_IOP_DMAC_MDEC_OUT, "IOP DMAC MDEC OUT"),
+	IOP_IRQ_TYPE(IRQ_IOP_DMAC_SIF2,     "IOP DMAC SIF2"),
+	IOP_IRQ_TYPE(IRQ_IOP_DMAC_CDVD,     "IOP DMAC CDVD"),
+	IOP_IRQ_TYPE(IRQ_IOP_DMAC_SPU,      "IOP DMAC SPU"),
+	IOP_IRQ_TYPE(IRQ_IOP_DMAC_PIO,      "IOP DMAC PIO"),
+	IOP_IRQ_TYPE(IRQ_IOP_DMAC_GPU_OTC,  "IOP DMAC GPU OTC"),
+	IOP_IRQ_TYPE(IRQ_IOP_DMAC_BE,       "IOP DMAC BE"),
+	IOP_IRQ_TYPE(IRQ_IOP_DMAC_SPU2,     "IOP DMAC SPU2"),
+	IOP_IRQ_TYPE(IRQ_IOP_DMAC_DEV9,     "IOP DMAC DEV9"),
+	IOP_IRQ_TYPE(IRQ_IOP_DMAC_SIF0,     "IOP DMAC SIF0"),
+	IOP_IRQ_TYPE(IRQ_IOP_DMAC_SIF1,     "IOP DMAC SIF1"),
+	IOP_IRQ_TYPE(IRQ_IOP_DMAC_SIO2_IN,  "IOP DMAC SIO2 IN"),
+	IOP_IRQ_TYPE(IRQ_IOP_DMAC_SIO2_OUT, "IOP DMAC SIO2 OUT"),
+	IOP_IRQ_TYPE(IRQ_IOP_SW1,           "IOP SW1"),
+	IOP_IRQ_TYPE(IRQ_IOP_SW2,           "IOP SW2"),
+};
+
+static int __init iop_irq_init(void)
+{
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(iop_irqs); i++)
+		irq_set_chip_and_handler(iop_irqs[i].irq,
+			&iop_irqs[i].irq_chip, handle_level_irq);
+
+	return 0;
+}
+// FIXME: subsys_initcall(iop_irq_init);
+module_init(iop_irq_init);
+
+MODULE_DESCRIPTION("PlayStation 2 input/output processor (IOP) IRQs");
+MODULE_AUTHOR("Fredrik Noring");
+MODULE_LICENSE("GPL");
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 076/120] MIPS: PS2: GS: Define privileged Graphics Synthesizer registers
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (74 preceding siblings ...)
  2019-09-01 16:17 ` [PATCH 075/120] MIPS: PS2: IOP: IRQ support Fredrik Noring
@ 2019-09-01 16:17 ` Fredrik Noring
  2019-09-01 16:18 ` [PATCH 077/120] MIPS: PS2: GS: Write privileged registers Fredrik Noring
                   ` (44 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:17 UTC (permalink / raw)
  To: linux-mips

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

The following privileged registers are available:

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

References:

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

[2] "GS User's Manual", version 6.0, Sony Computer Entertainment Inc.,
    pp. 142-157, 159.

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 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

diff --git a/arch/mips/include/asm/mach-ps2/gs-registers.h b/arch/mips/include/asm/mach-ps2/gs-registers.h
new file mode 100644
index 000000000000..ab59c751190f
--- /dev/null
+++ b/arch/mips/include/asm/mach-ps2/gs-registers.h
@@ -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 */
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 077/120] MIPS: PS2: GS: Write privileged registers
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (75 preceding siblings ...)
  2019-09-01 16:17 ` [PATCH 076/120] MIPS: PS2: GS: Define privileged Graphics Synthesizer registers Fredrik Noring
@ 2019-09-01 16:18 ` Fredrik Noring
  2019-09-01 16:18 ` [PATCH 078/120] MIPS: PS2: GS: Read " Fredrik Noring
                   ` (43 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:18 UTC (permalink / raw)
  To: linux-mips

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

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

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

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 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

diff --git a/arch/mips/include/asm/mach-ps2/gs-registers.h b/arch/mips/include/asm/mach-ps2/gs-registers.h
index ab59c751190f..291b503447e4 100644
--- a/arch/mips/include/asm/mach-ps2/gs-registers.h
+++ b/arch/mips/include/asm/mach-ps2/gs-registers.h
@@ -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 */
diff --git a/drivers/ps2/Makefile b/drivers/ps2/Makefile
index dd00eff52ef3..533ab86d6a58 100644
--- a/drivers/ps2/Makefile
+++ b/drivers/ps2/Makefile
@@ -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
diff --git a/drivers/ps2/gs-registers.c b/drivers/ps2/gs-registers.c
new file mode 100644
index 000000000000..781604b874b5
--- /dev/null
+++ b/drivers/ps2/gs-registers.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 privileged Graphics Synthesizer (GS) registers
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#include <linux/build_bug.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include <asm/io.h>
+
+#include <asm/mach-ps2/gs-registers.h>
+
+/* Shadow write-only privileged Graphics Synthesizer registers. */
+static struct {
+	spinlock_t lock;	/* Must be taken to access shadow registers. */
+
+	struct {
+		u64 value;
+		bool valid;	/* True if value is written at least once. */
+	} pmode,
+	  smode1,
+	  smode2,
+	  srfsh,
+	  synch1,
+	  synch2,
+	  syncv,
+	  dispfb1,
+	  display1,
+	  dispfb2,
+	  display2,
+	  extbuf,
+	  extdata,
+	  extwrite,
+	  bgcolor,
+	  imr,
+	  busdir;
+} gs_registers = {
+	.lock = __SPIN_LOCK_UNLOCKED(gs_registers.lock),
+};
+
+/* Read-write registers are not shadowed and are always valid. */
+#define GS_DEFINE_VALID_RW_REG(reg, addr)				\
+	bool gs_valid_##reg(void)					\
+	{								\
+		return true;						\
+	}								\
+	EXPORT_SYMBOL_GPL(gs_valid_##reg)
+
+/* Read-write registers are not shadowed and trivially write. */
+#define GS_DEFINE_WQ_RW_REG(reg, addr)					\
+	void gs_writeq_##reg(u64 value)					\
+	{								\
+		outq(value, addr);					\
+	}								\
+	EXPORT_SYMBOL_GPL(gs_writeq_##reg)
+
+/* Write-only registers are shadowed and valid only if previously written. */
+#define GS_DEFINE_VALID_WO_REG(reg, addr)				\
+	bool gs_valid_##reg(void)					\
+	{								\
+		unsigned long flags;					\
+		bool valid;						\
+		spin_lock_irqsave(&gs_registers.lock, flags);		\
+		valid = gs_registers.reg.valid;				\
+		spin_unlock_irqrestore(&gs_registers.lock, flags);	\
+		return valid;						\
+	}								\
+	EXPORT_SYMBOL_GPL(gs_valid_##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)					\
+	{								\
+		unsigned long flags;					\
+		spin_lock_irqsave(&gs_registers.lock, flags);		\
+		gs_registers.reg.value = value;				\
+		gs_registers.reg.valid = true;				\
+		outq(value, addr);					\
+		spin_unlock_irqrestore(&gs_registers.lock, flags);	\
+	}								\
+	EXPORT_SYMBOL_GPL(gs_writeq_##reg)
+
+/* 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_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_WQ_WO_REG(reg, addr)
+
+GS_DEFINE_WO_REG(pmode,    GS_PMODE);
+GS_DEFINE_WO_REG(smode1,   GS_SMODE1);
+GS_DEFINE_WO_REG(smode2,   GS_SMODE2);
+GS_DEFINE_WO_REG(srfsh,    GS_SRFSH);
+GS_DEFINE_WO_REG(synch1,   GS_SYNCH1);
+GS_DEFINE_WO_REG(synch2,   GS_SYNCH2);
+GS_DEFINE_WO_REG(syncv,    GS_SYNCV);
+GS_DEFINE_WO_REG(dispfb1,  GS_DISPFB1);
+GS_DEFINE_WO_REG(display1, GS_DISPLAY1);
+GS_DEFINE_WO_REG(dispfb2,  GS_DISPFB2);
+GS_DEFINE_WO_REG(display2, GS_DISPLAY2);
+GS_DEFINE_WO_REG(extbuf,   GS_EXTBUF);
+GS_DEFINE_WO_REG(extdata,  GS_EXTDATA);
+GS_DEFINE_WO_REG(extwrite, GS_EXTWRITE);
+GS_DEFINE_WO_REG(bgcolor,  GS_BGCOLOR);
+GS_DEFINE_RW_REG(csr,      GS_CSR);		/* Read-write */
+GS_DEFINE_WO_REG(imr,      GS_IMR);
+GS_DEFINE_WO_REG(busdir,   GS_BUSDIR);
+GS_DEFINE_RW_REG(siglblid, GS_SIGLBLID);	/* Read-write */
+
+MODULE_DESCRIPTION("PlayStation 2 privileged Graphics Synthesizer registers");
+MODULE_AUTHOR("Fredrik Noring");
+MODULE_LICENSE("GPL");
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 078/120] MIPS: PS2: GS: Read privileged registers
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (76 preceding siblings ...)
  2019-09-01 16:18 ` [PATCH 077/120] MIPS: PS2: GS: Write privileged registers Fredrik Noring
@ 2019-09-01 16:18 ` " Fredrik Noring
  2019-09-01 16:18 ` [PATCH 079/120] MIPS: PS2: GS: Define privileged register structures Fredrik Noring
                   ` (42 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:18 UTC (permalink / raw)
  To: linux-mips

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

    u64 gs_readq_csr(void);

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/mach-ps2/gs-registers.h |  5 ++++
 drivers/ps2/gs-registers.c                    | 24 +++++++++++++++++++
 2 files changed, 29 insertions(+)

diff --git a/arch/mips/include/asm/mach-ps2/gs-registers.h b/arch/mips/include/asm/mach-ps2/gs-registers.h
index 291b503447e4..315a25c102c9 100644
--- a/arch/mips/include/asm/mach-ps2/gs-registers.h
+++ b/arch/mips/include/asm/mach-ps2/gs-registers.h
@@ -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,
diff --git a/drivers/ps2/gs-registers.c b/drivers/ps2/gs-registers.c
index 781604b874b5..debaf0153fbe 100644
--- a/drivers/ps2/gs-registers.c
+++ b/drivers/ps2/gs-registers.c
@@ -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);
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 079/120] MIPS: PS2: GS: Define privileged register structures
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (77 preceding siblings ...)
  2019-09-01 16:18 ` [PATCH 078/120] MIPS: PS2: GS: Read " Fredrik Noring
@ 2019-09-01 16:18 ` Fredrik Noring
  2019-09-01 16:19 ` [PATCH 080/120] MIPS: PS2: GS: Define gs_xorq_imr() Fredrik Noring
                   ` (41 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:18 UTC (permalink / raw)
  To: linux-mips

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

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/mach-ps2/gs-registers.h | 53 +++++++------
 drivers/ps2/gs-registers.c                    | 75 +++++++++++++------
 2 files changed, 83 insertions(+), 45 deletions(-)

diff --git a/arch/mips/include/asm/mach-ps2/gs-registers.h b/arch/mips/include/asm/mach-ps2/gs-registers.h
index 315a25c102c9..596e277a6940 100644
--- a/arch/mips/include/asm/mach-ps2/gs-registers.h
+++ b/arch/mips/include/asm/mach-ps2/gs-registers.h
@@ -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 */
diff --git a/drivers/ps2/gs-registers.c b/drivers/ps2/gs-registers.c
index debaf0153fbe..1bd1d072f4cf 100644
--- a/drivers/ps2/gs-registers.c
+++ b/drivers/ps2/gs-registers.c
@@ -108,37 +108,64 @@ static struct {
 	}								\
 	EXPORT_SYMBOL_GPL(gs_writeq_##reg)
 
+/* Read registers as structures, which simplifies notation. */
+#define GS_DEFINE_RS_REG(reg, str)					\
+	struct gs_##str gs_read_##reg(void)				\
+	{								\
+		const u64 v = gs_readq_##reg();				\
+		struct gs_##str value;					\
+		BUILD_BUG_ON(sizeof(v) != sizeof(value));		\
+		memcpy(&value, &v, sizeof(v));				\
+		return value;						\
+	}								\
+	EXPORT_SYMBOL_GPL(gs_read_##reg)
+
+/* Write registers as structures, which simplifies notation. */
+#define GS_DEFINE_WS_REG(reg, str)					\
+	void gs_write_##reg(struct gs_##str value)			\
+	{								\
+		u64 v;							\
+		BUILD_BUG_ON(sizeof(v) != sizeof(value));		\
+		memcpy(&v, &value, sizeof(v));				\
+		gs_writeq_##reg(v);					\
+	}								\
+	EXPORT_SYMBOL_GPL(gs_write_##reg)
+
 /* Only CSR and SIGLBLID are read-write (RW) with hardware. */
-#define GS_DEFINE_RW_REG(reg, addr)					\
+#define GS_DEFINE_RW_REG(reg, str, addr)				\
 	GS_DEFINE_VALID_RW_REG(reg, addr);				\
 	GS_DEFINE_RQ_RW_REG(reg, addr);					\
-	GS_DEFINE_WQ_RW_REG(reg, addr)
+	GS_DEFINE_WQ_RW_REG(reg, addr);					\
+	GS_DEFINE_RS_REG(reg, str);					\
+	GS_DEFINE_WS_REG(reg, str)
 
 /* The rest are write-only (WO) with reading emulated by shadow registers. */
-#define GS_DEFINE_WO_REG(reg, addr)					\
+#define GS_DEFINE_WO_REG(reg, str, 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);
-GS_DEFINE_WO_REG(smode1,   GS_SMODE1);
-GS_DEFINE_WO_REG(smode2,   GS_SMODE2);
-GS_DEFINE_WO_REG(srfsh,    GS_SRFSH);
-GS_DEFINE_WO_REG(synch1,   GS_SYNCH1);
-GS_DEFINE_WO_REG(synch2,   GS_SYNCH2);
-GS_DEFINE_WO_REG(syncv,    GS_SYNCV);
-GS_DEFINE_WO_REG(dispfb1,  GS_DISPFB1);
-GS_DEFINE_WO_REG(display1, GS_DISPLAY1);
-GS_DEFINE_WO_REG(dispfb2,  GS_DISPFB2);
-GS_DEFINE_WO_REG(display2, GS_DISPLAY2);
-GS_DEFINE_WO_REG(extbuf,   GS_EXTBUF);
-GS_DEFINE_WO_REG(extdata,  GS_EXTDATA);
-GS_DEFINE_WO_REG(extwrite, GS_EXTWRITE);
-GS_DEFINE_WO_REG(bgcolor,  GS_BGCOLOR);
-GS_DEFINE_RW_REG(csr,      GS_CSR);		/* Read-write */
-GS_DEFINE_WO_REG(imr,      GS_IMR);
-GS_DEFINE_WO_REG(busdir,   GS_BUSDIR);
-GS_DEFINE_RW_REG(siglblid, GS_SIGLBLID);	/* Read-write */
+	GS_DEFINE_WQ_WO_REG(reg, addr);					\
+	GS_DEFINE_RS_REG(reg, str);					\
+	GS_DEFINE_WS_REG(reg, str)
+
+GS_DEFINE_WO_REG(pmode,    pmode,    GS_PMODE);
+GS_DEFINE_WO_REG(smode1,   smode1,   GS_SMODE1);
+GS_DEFINE_WO_REG(smode2,   smode2,   GS_SMODE2);
+GS_DEFINE_WO_REG(srfsh,    srfsh,    GS_SRFSH);
+GS_DEFINE_WO_REG(synch1,   synch1,   GS_SYNCH1);
+GS_DEFINE_WO_REG(synch2,   synch2,   GS_SYNCH2);
+GS_DEFINE_WO_REG(syncv,    syncv,    GS_SYNCV);
+GS_DEFINE_WO_REG(dispfb1,  dispfb,   GS_DISPFB1);
+GS_DEFINE_WO_REG(display1, display,  GS_DISPLAY1);
+GS_DEFINE_WO_REG(dispfb2,  dispfb,   GS_DISPFB2);
+GS_DEFINE_WO_REG(display2, display,  GS_DISPLAY2);
+GS_DEFINE_WO_REG(extbuf,   extbuf,   GS_EXTBUF);
+GS_DEFINE_WO_REG(extdata,  extdata,  GS_EXTDATA);
+GS_DEFINE_WO_REG(extwrite, extwrite, GS_EXTWRITE);
+GS_DEFINE_WO_REG(bgcolor,  bgcolor,  GS_BGCOLOR);
+GS_DEFINE_RW_REG(csr,      csr,      GS_CSR);		/* Read-write */
+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 */
 
 MODULE_DESCRIPTION("PlayStation 2 privileged Graphics Synthesizer registers");
 MODULE_AUTHOR("Fredrik Noring");
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 080/120] MIPS: PS2: GS: Define gs_xorq_imr()
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (78 preceding siblings ...)
  2019-09-01 16:18 ` [PATCH 079/120] MIPS: PS2: GS: Define privileged register structures Fredrik Noring
@ 2019-09-01 16:19 ` Fredrik Noring
  2019-09-01 16:20 ` [PATCH 081/120] MIPS: PS2: GS: Privileged register write macros with named fields Fredrik Noring
                   ` (40 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:19 UTC (permalink / raw)
  To: linux-mips

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
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(+)

diff --git a/arch/mips/include/asm/mach-ps2/gs-registers.h b/arch/mips/include/asm/mach-ps2/gs-registers.h
index 596e277a6940..badcb3cce5c2 100644
--- a/arch/mips/include/asm/mach-ps2/gs-registers.h
+++ b/arch/mips/include/asm/mach-ps2/gs-registers.h
@@ -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 */
diff --git a/drivers/ps2/gs-registers.c b/drivers/ps2/gs-registers.c
index 1bd1d072f4cf..8d2bdd26438d 100644
--- a/drivers/ps2/gs-registers.c
+++ b/drivers/ps2/gs-registers.c
@@ -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");
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 081/120] MIPS: PS2: GS: Privileged register write macros with named fields
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (79 preceding siblings ...)
  2019-09-01 16:19 ` [PATCH 080/120] MIPS: PS2: GS: Define gs_xorq_imr() Fredrik Noring
@ 2019-09-01 16:20 ` Fredrik Noring
  2019-09-01 16:20 ` [PATCH 082/120] MIPS: PS2: GS: IRQ support Fredrik Noring
                   ` (39 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:20 UTC (permalink / raw)
  To: linux-mips

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.

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/mach-ps2/gs-registers.h | 26 +++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/arch/mips/include/asm/mach-ps2/gs-registers.h b/arch/mips/include/asm/mach-ps2/gs-registers.h
index badcb3cce5c2..95243a35e26d 100644
--- a/arch/mips/include/asm/mach-ps2/gs-registers.h
+++ b/arch/mips/include/asm/mach-ps2/gs-registers.h
@@ -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
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 082/120] MIPS: PS2: GS: IRQ support
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (80 preceding siblings ...)
  2019-09-01 16:20 ` [PATCH 081/120] MIPS: PS2: GS: Privileged register write macros with named fields Fredrik Noring
@ 2019-09-01 16:20 ` Fredrik Noring
  2019-09-01 16:21 ` [PATCH 083/120] MIPS: PS2: GS: Define Graphics Synthesizer primitive structures Fredrik Noring
                   ` (38 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:20 UTC (permalink / raw)
  To: linux-mips

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 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

diff --git a/arch/mips/include/asm/mach-ps2/irq.h b/arch/mips/include/asm/mach-ps2/irq.h
index 57b3e539ad92..132ad329de6b 100644
--- a/arch/mips/include/asm/mach-ps2/irq.h
+++ b/arch/mips/include/asm/mach-ps2/irq.h
@@ -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).
diff --git a/drivers/ps2/Makefile b/drivers/ps2/Makefile
index 533ab86d6a58..7f108d4ee451 100644
--- a/drivers/ps2/Makefile
+++ b/drivers/ps2/Makefile
@@ -1,3 +1,4 @@
+obj-$(CONFIG_PS2_GS)		+= gs-irq.o
 obj-$(CONFIG_PS2_GS)		+= gs-registers.o
 
 obj-m				+= iop-heap.o
diff --git a/drivers/ps2/gs-irq.c b/drivers/ps2/gs-irq.c
new file mode 100644
index 000000000000..55116a8b5582
--- /dev/null
+++ b/drivers/ps2/gs-irq.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 Graphics Synthesizer (GS) IRQs
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include <asm/irq_cpu.h>
+
+#include <asm/mach-ps2/irq.h>
+#include <asm/mach-ps2/gs.h>
+#include <asm/mach-ps2/gs-registers.h>
+
+static void gs_reverse_mask(struct irq_data *data)
+{
+	gs_xorq_imr(BIT(8 + data->irq - IRQ_GS));
+}
+
+static void gs_ack(struct irq_data *data)
+{
+	gs_writeq_csr(BIT(data->irq - IRQ_GS));
+}
+
+#define GS_IRQ_TYPE(irq_, name_)			\
+	{						\
+		.irq = irq_,				\
+		.irq_chip = {				\
+			.name = name_,			\
+			.irq_unmask = gs_reverse_mask,	\
+			.irq_mask = gs_reverse_mask,	\
+			.irq_ack = gs_ack		\
+		}					\
+	}
+
+static struct {
+	unsigned int irq;
+	struct irq_chip irq_chip;
+} gs_irqs[] = {
+	GS_IRQ_TYPE(IRQ_GS_SIGNAL,  "GS SIGNAL"),
+	GS_IRQ_TYPE(IRQ_GS_FINISH,  "GS FINISH"),
+	GS_IRQ_TYPE(IRQ_GS_HSYNC,   "GS HSYNC"),
+	GS_IRQ_TYPE(IRQ_GS_VSYNC,   "GS VSYNC"),
+	GS_IRQ_TYPE(IRQ_GS_EDW,     "GS EDW"),
+	GS_IRQ_TYPE(IRQ_GS_EXHSYNC, "GS EXHSYNC"),
+	GS_IRQ_TYPE(IRQ_GS_EXVSYNC, "GS EXVSYNC"),
+};
+
+static irqreturn_t gs_cascade(int irq, void *data)
+{
+	unsigned int pending = gs_readq_csr() & 0x1f;
+
+	if (!pending)
+		return IRQ_NONE;
+
+	while (pending) {
+		const unsigned int irq_gs = __fls(pending);
+
+		if (generic_handle_irq(irq_gs + IRQ_GS) < 0)
+			spurious_interrupt();
+		pending &= ~BIT(irq_gs);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static struct irqaction cascade_gs_irqaction = {
+	.name = "GS cascade",
+	.handler = gs_cascade,
+};
+
+int gs_irq_init(void)
+{
+	size_t i;
+	int err;
+
+	gs_writeq_imr(0x7f00);	/* Disable GS interrupts */
+	gs_writeq_csr(0x00ff);	/* Clear GS events */
+
+	for (i = 0; i < ARRAY_SIZE(gs_irqs); i++)
+		irq_set_chip_and_handler(gs_irqs[i].irq,
+			&gs_irqs[i].irq_chip, handle_level_irq);
+
+	err = setup_irq(IRQ_INTC_GS, &cascade_gs_irqaction);
+	if (err)
+		pr_err("gs-irq: Failed to setup GS IRQs (err = %d)\n", err);
+
+	return err;
+}
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 083/120] MIPS: PS2: GS: Define Graphics Synthesizer primitive structures
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (81 preceding siblings ...)
  2019-09-01 16:20 ` [PATCH 082/120] MIPS: PS2: GS: IRQ support Fredrik Noring
@ 2019-09-01 16:21 ` Fredrik Noring
  2019-09-01 16:21 ` [PATCH 084/120] MIPS: PS2: GIF: Define Graphics Synthesizer interface structures Fredrik Noring
                   ` (37 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:21 UTC (permalink / raw)
  To: linux-mips

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

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

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

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

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

References:

[1] "GS User's Manual", version 6.0, Sony Computer Entertainment Inc.,
    pp. 158, 161-175.

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/uapi/asm/gs.h | 723 ++++++++++++++++++++++++++++++++
 1 file changed, 723 insertions(+)
 create mode 100644 arch/mips/include/uapi/asm/gs.h

diff --git a/arch/mips/include/uapi/asm/gs.h b/arch/mips/include/uapi/asm/gs.h
new file mode 100644
index 000000000000..074bb62a4bd0
--- /dev/null
+++ b/arch/mips/include/uapi/asm/gs.h
@@ -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 */
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 084/120] MIPS: PS2: GIF: Define Graphics Synthesizer interface structures
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (82 preceding siblings ...)
  2019-09-01 16:21 ` [PATCH 083/120] MIPS: PS2: GS: Define Graphics Synthesizer primitive structures Fredrik Noring
@ 2019-09-01 16:21 ` Fredrik Noring
  2019-09-01 16:22 ` [PATCH 085/120] MIPS: PS2: GIF: Graphics Synthesizer interface support Fredrik Noring
                   ` (36 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:21 UTC (permalink / raw)
  To: linux-mips

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

The GIF has three kinds of general data transfer paths:

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

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

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

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

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

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

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

References:

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
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

diff --git a/arch/mips/include/uapi/asm/gif.h b/arch/mips/include/uapi/asm/gif.h
new file mode 100644
index 000000000000..a952ed879de4
--- /dev/null
+++ b/arch/mips/include/uapi/asm/gif.h
@@ -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 */
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 085/120] MIPS: PS2: GIF: Graphics Synthesizer interface support
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (83 preceding siblings ...)
  2019-09-01 16:21 ` [PATCH 084/120] MIPS: PS2: GIF: Define Graphics Synthesizer interface structures Fredrik Noring
@ 2019-09-01 16:22 ` Fredrik Noring
  2019-09-01 16:22 ` [PATCH 086/120] MIPS: PS2: GS: Graphics Synthesizer device init and video clock Fredrik Noring
                   ` (35 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:22 UTC (permalink / raw)
  To: linux-mips

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 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

diff --git a/arch/mips/include/asm/mach-ps2/gif.h b/arch/mips/include/asm/mach-ps2/gif.h
new file mode 100644
index 000000000000..2a3965bd5544
--- /dev/null
+++ b/arch/mips/include/asm/mach-ps2/gif.h
@@ -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 */
diff --git a/drivers/ps2/Makefile b/drivers/ps2/Makefile
index 7f108d4ee451..9671bbe40294 100644
--- a/drivers/ps2/Makefile
+++ b/drivers/ps2/Makefile
@@ -1,3 +1,4 @@
+obj-$(CONFIG_PS2_GS)		+= gif.o
 obj-$(CONFIG_PS2_GS)		+= gs-irq.o
 obj-$(CONFIG_PS2_GS)		+= gs-registers.o
 
diff --git a/drivers/ps2/gif.c b/drivers/ps2/gif.c
new file mode 100644
index 000000000000..b7707047e470
--- /dev/null
+++ b/drivers/ps2/gif.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 Graphics Synthesizer interface (GIF)
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/vmalloc.h>
+
+#include <asm/cacheflush.h>
+#include <asm/io.h>
+#include <asm/page.h>
+#include <asm/uaccess.h>
+
+#include <asm/mach-ps2/dmac.h>
+#include <asm/mach-ps2/gif.h>
+#include <asm/mach-ps2/gs.h>
+
+#include <uapi/asm/gif.h>
+#include <uapi/asm/gs.h>
+
+void gif_writel_ctrl(u32 value)
+{
+	outl(value, GIF_CTRL);
+}
+EXPORT_SYMBOL_GPL(gif_writel_ctrl);
+
+void gif_write_ctrl(struct gif_ctrl value)
+{
+	u32 v;
+	memcpy(&v, &value, sizeof(v));
+	gif_writel_ctrl(v);
+}
+EXPORT_SYMBOL_GPL(gif_write_ctrl);
+
+void gif_reset(void)
+{
+	gif_write_ctrl((struct gif_ctrl) { .rst = 1 });
+
+	udelay(100);		/* 100 us */
+}
+EXPORT_SYMBOL_GPL(gif_reset);
+
+bool gif_busy(void)
+{
+	return (inl(DMAC_GIF_CHCR) & DMAC_CHCR_BUSY) != 0;
+}
+EXPORT_SYMBOL_GPL(gif_busy);
+
+bool gif_wait(void)
+{
+	size_t countout = 1000000;
+
+	while (gif_busy() && countout > 0)
+		countout--;
+
+	return countout > 0;
+}
+EXPORT_SYMBOL_GPL(gif_wait);
+
+void gif_write(union gif_data *base_package, size_t package_count)
+{
+	const size_t size = package_count * sizeof(*base_package);
+	const dma_addr_t madr = virt_to_phys(base_package);
+
+	if (!package_count)
+		return;
+
+	dma_cache_wback((unsigned long)base_package, size);
+
+	/* Wait for previous transmissions to finish. */
+	while (gif_busy())
+		;
+
+	outl(madr, DMAC_GIF_MADR);
+	outl(package_count, DMAC_GIF_QWC);
+	outl(DMAC_CHCR_SENDN, DMAC_GIF_CHCR);
+}
+EXPORT_SYMBOL_GPL(gif_write);
+
+static int __init gif_init(void)
+{
+	return 0;
+}
+
+static void __exit gif_exit(void)
+{
+}
+
+module_init(gif_init);
+module_exit(gif_exit);
+
+MODULE_DESCRIPTION("PlayStation 2 Graphics Synthesizer interface (GIF)");
+MODULE_AUTHOR("Fredrik Noring");
+MODULE_LICENSE("GPL");
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 086/120] MIPS: PS2: GS: Graphics Synthesizer device init and video clock
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (84 preceding siblings ...)
  2019-09-01 16:22 ` [PATCH 085/120] MIPS: PS2: GIF: Graphics Synthesizer interface support Fredrik Noring
@ 2019-09-01 16:22 ` Fredrik Noring
  2019-09-01 16:23 ` [PATCH 087/120] MIPS: PS2: GS: Compute block count and indices Fredrik Noring
                   ` (34 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:22 UTC (permalink / raw)
  To: linux-mips

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 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

diff --git a/arch/mips/include/asm/mach-ps2/gs.h b/arch/mips/include/asm/mach-ps2/gs.h
new file mode 100644
index 000000000000..9cb1b909ae9b
--- /dev/null
+++ b/arch/mips/include/asm/mach-ps2/gs.h
@@ -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 */
diff --git a/arch/mips/ps2/Kconfig b/arch/mips/ps2/Kconfig
new file mode 100644
index 000000000000..f782d61cfaac
--- /dev/null
+++ b/arch/mips/ps2/Kconfig
@@ -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.
diff --git a/drivers/ps2/Makefile b/drivers/ps2/Makefile
index 9671bbe40294..28fb55803199 100644
--- a/drivers/ps2/Makefile
+++ b/drivers/ps2/Makefile
@@ -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
 
diff --git a/drivers/ps2/gs.c b/drivers/ps2/gs.c
new file mode 100644
index 000000000000..00bb36304ee6
--- /dev/null
+++ b/drivers/ps2/gs.c
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 Graphics Synthesizer (GS)
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#include <linux/ctype.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+#include <asm/mach-ps2/gif.h>
+#include <asm/mach-ps2/gs.h>
+#include <asm/mach-ps2/irq.h>
+#include <asm/mach-ps2/rom.h>
+
+#include <uapi/asm/gs.h>
+
+static struct device *gs_dev;
+
+/**
+ * gs_video_clock - video clock (VCK) frequency given SMODE1 bit fields
+ * @t1248 - &gs_smode1.t1248 PLL output divider
+ * @lc - &gs_smode1.lc PLL loop divider
+ * @rc - &gs_smode1.rc PLL reference divider
+ *
+ * Return: video clock (VCK)
+ */
+u32 gs_video_clock(const u32 t1248, const u32 lc, const u32 rc)
+{
+	return (13500000 * lc) / ((t1248 + 1) * rc);
+}
+EXPORT_SYMBOL_GPL(gs_video_clock);
+
+/**
+ * gs_video_clock_for_smode1 - video clock (VCK) frequency given SMODE1
+ * 	register value
+ * @smode1: SMODE1 register value
+ *
+ * Return: video clock (VCK)
+ */
+u32 gs_video_clock_for_smode1(const struct gs_smode1 smode1)
+{
+	return gs_video_clock(smode1.t1248, smode1.lc, smode1.rc);
+}
+EXPORT_SYMBOL_GPL(gs_video_clock_for_smode1);
+
+static u32 div_round_ps(u32 a, u32 b)
+{
+	return DIV_ROUND_CLOSEST_ULL(a * 1000000000000ll, b);
+}
+
+static u32 vck_to_pix_clock(const u32 vck, const u32 spml)
+{
+	return div_round_ps(spml, vck);
+}
+
+/**
+ * gs_synch_gen_for_vck - determine video synchronization register fields for
+ * 	a given video clock
+ * @vck: video clock to compute video synchronization register fields for
+ *
+ * Some combinations of registers appear to generate equivalent video clock
+ * frequencies. For the standard ones, the combinations used by Sony are
+ * preferred and tabulated. For all others, a search is performed.
+ *
+ * Return: video synchronization register fields RC, LC, T1248 and SPML
+ **/
+struct gs_synch_gen gs_synch_gen_for_vck(const u32 vck)
+{
+	static const struct gs_synch_gen preferred[] = {
+		{ .spml = 2, .t1248 = 1, .lc = 15, .rc = 2 }, /*  50.625 MHz */
+		{ .spml = 2, .t1248 = 1, .lc = 32, .rc = 4 }, /*  54.000 MHz */
+		{ .spml = 4, .t1248 = 1, .lc = 32, .rc = 4 }, /*  54.000 MHz */
+		{ .spml = 2, .t1248 = 1, .lc = 28, .rc = 3 }, /*  63.000 MHz */
+		{ .spml = 1, .t1248 = 1, .lc = 22, .rc = 2 }, /*  74.250 MHz */
+		{ .spml = 1, .t1248 = 1, .lc = 35, .rc = 3 }, /*  78.750 MHz */
+		{ .spml = 2, .t1248 = 1, .lc = 71, .rc = 6 }, /*  79.875 MHz */
+		{ .spml = 2, .t1248 = 1, .lc = 44, .rc = 3 }, /*  99.000 MHz */
+		{ .spml = 1, .t1248 = 0, .lc =  8, .rc = 1 }, /* 108.000 MHz */
+		{ .spml = 2, .t1248 = 0, .lc = 58, .rc = 6 }, /* 130.500 MHz */
+		{ .spml = 1, .t1248 = 0, .lc = 10, .rc = 1 }, /* 135.000 MHz */
+		{ .spml = 1, .t1248 = 1, .lc = 22, .rc = 1 }  /* 148.500 MHz */
+	};
+
+	struct gs_synch_gen sg = { };
+	u32 spml, t1248, lc, rc;
+	int best = -1;
+	int diff, i;
+
+	for (i = 0; i < ARRAY_SIZE(preferred); i++) {
+		spml  = preferred[i].spml;
+		t1248 = preferred[i].t1248;
+		lc    = preferred[i].lc;
+		rc    = preferred[i].rc;
+
+		diff = abs(vck - vck_to_pix_clock(
+					gs_video_clock(t1248, lc, rc), spml));
+
+		if (best == -1 || diff < best) {
+			best = diff;
+			sg = (struct gs_synch_gen) {
+				.rc = rc,
+				.lc = lc,
+				.t1248 = t1248,
+				.spml = spml
+			};
+		}
+	}
+
+	for (spml  = 1; spml  <   5; spml++)
+	for (t1248 = 0; t1248 <   2; t1248++)
+	for (lc    = 1; lc    < 128; lc++)
+	for (rc    = 1; rc    <   7; rc++) {
+		diff = abs(vck - vck_to_pix_clock(
+					gs_video_clock(t1248, lc, rc), spml));
+
+		if (best == -1 || diff < best) {
+			best = diff;
+			sg = (struct gs_synch_gen) {
+				.rc = rc,
+				.lc = lc,
+				.t1248 = t1248,
+				.spml = spml
+			};
+		}
+	}
+
+	return sg;
+}
+EXPORT_SYMBOL_GPL(gs_synch_gen_for_vck);
+
+/**
+ * gs_rfsh_from_synch_gen - DRAM refresh for the given synchronization registers
+ *
+ * Return: DRAM refresh register value
+ */
+u32 gs_rfsh_from_synch_gen(const struct gs_synch_gen sg)
+{
+	const u32 pck = gs_video_clock(sg.t1248, sg.lc, sg.rc) / sg.spml;
+
+	return pck < 20000000 ? 8 :
+	       pck < 70000000 ? 4 : 2;
+}
+EXPORT_SYMBOL_GPL(gs_rfsh_from_synch_gen);
+
+struct device *gs_device_driver(void)
+{
+	return gs_dev;
+}
+EXPORT_SYMBOL_GPL(gs_device_driver);
+
+static int gs_probe(struct platform_device *pdev)
+{
+	gs_dev = &pdev->dev;
+
+	gs_irq_init();
+
+	gif_reset();
+
+	return 0;
+}
+
+static int gs_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static struct platform_driver gs_driver = {
+	.probe		= gs_probe,
+	.remove		= gs_remove,
+	.driver = {
+		.name	= "gs",
+	},
+};
+
+static int __init gs_init(void)
+{
+	return platform_driver_register(&gs_driver);
+}
+
+static void __exit gs_exit(void)
+{
+	platform_driver_unregister(&gs_driver);
+}
+
+module_init(gs_init);
+module_exit(gs_exit);
+
+MODULE_DESCRIPTION("PlayStation 2 Graphics Synthesizer device driver");
+MODULE_AUTHOR("Fredrik Noring");
+MODULE_LICENSE("GPL");
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 087/120] MIPS: PS2: GS: Compute block count and indices
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (85 preceding siblings ...)
  2019-09-01 16:22 ` [PATCH 086/120] MIPS: PS2: GS: Graphics Synthesizer device init and video clock Fredrik Noring
@ 2019-09-01 16:23 ` Fredrik Noring
  2019-09-01 16:23 ` [PATCH 088/120] MIPS: PS2: GS: Primitive and texel coordinate transformations Fredrik Noring
                   ` (33 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:23 UTC (permalink / raw)
  To: linux-mips

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

References:

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/mach-ps2/gs.h |  8 +++
 drivers/ps2/gs.c                    | 94 +++++++++++++++++++++++++++++
 2 files changed, 102 insertions(+)

diff --git a/arch/mips/include/asm/mach-ps2/gs.h b/arch/mips/include/asm/mach-ps2/gs.h
index 9cb1b909ae9b..5429f52a4518 100644
--- a/arch/mips/include/asm/mach-ps2/gs.h
+++ b/arch/mips/include/asm/mach-ps2/gs.h
@@ -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);
diff --git a/drivers/ps2/gs.c b/drivers/ps2/gs.c
index 00bb36304ee6..a3cd1a6adfb7 100644
--- a/drivers/ps2/gs.c
+++ b/drivers/ps2/gs.c
@@ -48,6 +48,100 @@ u32 gs_video_clock_for_smode1(const struct gs_smode1 smode1)
 }
 EXPORT_SYMBOL_GPL(gs_video_clock_for_smode1);
 
+/**
+ * gs_psm_ct16_block_count - number of blocks for 16-bit pixel storage
+ * @fbw: buffer width/64
+ * @fbh: buffer height
+ *
+ * Return: number of blocks for 16-bit pixel storage of given width and height
+ */
+u32 gs_psm_ct16_block_count(const u32 fbw, const u32 fbh)
+{
+	const u32 block_cols = fbw * GS_PSM_CT16_PAGE_COLS;
+	const u32 block_rows = (fbh + GS_PSM_CT16_BLOCK_HEIGHT - 1) /
+		GS_PSM_CT16_BLOCK_HEIGHT;
+
+	return block_cols * block_rows;
+}
+EXPORT_SYMBOL_GPL(gs_psm_ct16_block_count);
+
+/**
+ * gs_psm_ct32_block_count - number of blocks for 32-bit pixel storage
+ * @fbw: buffer width/64
+ * @fbh: buffer height
+ *
+ * Return: number of blocks for 32-bit pixel storage of given width and height
+ */
+u32 gs_psm_ct32_block_count(const u32 fbw, const u32 fbh)
+{
+	const u32 block_cols = fbw * GS_PSM_CT32_PAGE_COLS;
+	const u32 block_rows = (fbh + GS_PSM_CT32_BLOCK_HEIGHT - 1) /
+		GS_PSM_CT32_BLOCK_HEIGHT;
+
+	return block_cols * block_rows;
+}
+EXPORT_SYMBOL_GPL(gs_psm_ct32_block_count);
+
+/**
+ * gs_psm_ct16_block_address - 16-bit block address given a block index
+ * @fbw: buffer width/64
+ * @block_index: block index starting at the top left corner
+ *
+ * Return: block address for a given block index
+ */
+u32 gs_psm_ct16_block_address(const u32 fbw, const u32 block_index)
+{
+	static const u32 block[GS_PSM_CT16_PAGE_ROWS][GS_PSM_CT16_PAGE_COLS] = {
+		{  0,  2,  8, 10 },
+		{  1,  3,  9, 11 },
+		{  4,  6, 12, 14 },
+		{  5,  7, 13, 15 },
+		{ 16, 18, 24, 26 },
+		{ 17, 19, 25, 27 },
+		{ 20, 22, 28, 30 },
+		{ 21, 23, 29, 31 }
+	};
+
+	const u32 fw = GS_PSM_CT16_PAGE_COLS * fbw;
+	const u32 fc = block_index % fw;
+	const u32 fr = block_index / fw;
+	const u32 bc = fc % GS_PSM_CT16_PAGE_COLS;
+	const u32 br = fr % GS_PSM_CT16_PAGE_ROWS;
+	const u32 pc = fc / GS_PSM_CT16_PAGE_COLS;
+	const u32 pr = fr / GS_PSM_CT16_PAGE_ROWS;
+
+	return GS_BLOCKS_PER_PAGE * (fbw * pr + pc) + block[br][bc];
+}
+EXPORT_SYMBOL_GPL(gs_psm_ct16_block_address);
+
+/**
+ * gs_psm_ct32_block_address - 32-bit block address given a block index
+ * @fbw: buffer width/64
+ * @block_index: block index starting at the top left corner
+ *
+ * Return: block address for a given block index
+ */
+u32 gs_psm_ct32_block_address(const u32 fbw, const u32 block_index)
+{
+	static const u32 block[GS_PSM_CT32_PAGE_ROWS][GS_PSM_CT32_PAGE_COLS] = {
+		{  0,  1,  4,  5, 16, 17, 20, 21 },
+		{  2,  3,  6,  7, 18, 19, 22, 23 },
+		{  8,  9, 12, 13, 24, 25, 28, 29 },
+		{ 10, 11, 14, 15, 26, 27, 30, 31 }
+	};
+
+	const u32 fw = GS_PSM_CT32_PAGE_COLS * fbw;
+	const u32 fc = block_index % fw;
+	const u32 fr = block_index / fw;
+	const u32 bc = fc % GS_PSM_CT32_PAGE_COLS;
+	const u32 br = fr % GS_PSM_CT32_PAGE_ROWS;
+	const u32 pc = fc / GS_PSM_CT32_PAGE_COLS;
+	const u32 pr = fr / GS_PSM_CT32_PAGE_ROWS;
+
+	return GS_BLOCKS_PER_PAGE * (fbw * pr + pc) + block[br][bc];
+}
+EXPORT_SYMBOL_GPL(gs_psm_ct32_block_address);
+
 static u32 div_round_ps(u32 a, u32 b)
 {
 	return DIV_ROUND_CLOSEST_ULL(a * 1000000000000ll, b);
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 088/120] MIPS: PS2: GS: Primitive and texel coordinate transformations
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (86 preceding siblings ...)
  2019-09-01 16:23 ` [PATCH 087/120] MIPS: PS2: GS: Compute block count and indices Fredrik Noring
@ 2019-09-01 16:23 ` Fredrik Noring
  2019-09-01 16:23 ` [PATCH 089/120] MIPS: PS2: GS: Approximate video region with ROM region Fredrik Noring
                   ` (32 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:23 UTC (permalink / raw)
  To: linux-mips

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

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

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

References:

[1] "GS User's Manual", version 6.0, Sony Computer Entertainment Inc.,
    pp. 23-24,  28.

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/mach-ps2/gs.h | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/arch/mips/include/asm/mach-ps2/gs.h b/arch/mips/include/asm/mach-ps2/gs.h
index 5429f52a4518..935d03007680 100644
--- a/arch/mips/include/asm/mach-ps2/gs.h
+++ b/arch/mips/include/asm/mach-ps2/gs.h
@@ -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);
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 089/120] MIPS: PS2: GS: Approximate video region with ROM region
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (87 preceding siblings ...)
  2019-09-01 16:23 ` [PATCH 088/120] MIPS: PS2: GS: Primitive and texel coordinate transformations Fredrik Noring
@ 2019-09-01 16:23 ` Fredrik Noring
  2019-09-01 16:24 ` [PATCH 090/120] macro: Extend COUNT_ARGS() from 12 to 32 arguments Fredrik Noring
                   ` (31 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:23 UTC (permalink / raw)
  To: linux-mips

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/mach-ps2/gs.h |  4 ++++
 drivers/ps2/gs.c                    | 30 +++++++++++++++++++++++++++++
 2 files changed, 34 insertions(+)

diff --git a/arch/mips/include/asm/mach-ps2/gs.h b/arch/mips/include/asm/mach-ps2/gs.h
index 935d03007680..e719ca0531ba 100644
--- a/arch/mips/include/asm/mach-ps2/gs.h
+++ b/arch/mips/include/asm/mach-ps2/gs.h
@@ -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);
diff --git a/drivers/ps2/gs.c b/drivers/ps2/gs.c
index a3cd1a6adfb7..c380dfa358b5 100644
--- a/drivers/ps2/gs.c
+++ b/drivers/ps2/gs.c
@@ -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
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 090/120] macro: Extend COUNT_ARGS() from 12 to 32 arguments
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (88 preceding siblings ...)
  2019-09-01 16:23 ` [PATCH 089/120] MIPS: PS2: GS: Approximate video region with ROM region Fredrik Noring
@ 2019-09-01 16:24 ` Fredrik Noring
  2019-09-01 16:25 ` [PATCH 091/120] MIPS: PS2: GS: Show privileged registers with sysfs Fredrik Noring
                   ` (30 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:24 UTC (permalink / raw)
  To: linux-mips

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

SYSFS_RW_REG(smode1, smode1,
	SYSFS_DECNUM_FIELD(rc),
	SYSFS_DECNUM_FIELD(lc),
	SYSFS_DECNUM_FIELD(t1248),
	SYSFS_DECNUM_FIELD(slck),
	SYSFS_SYMBOL_FIELD(cmod, vesa, ntsc, pal),
	SYSFS_DECNUM_FIELD(ex),
	SYSFS_DECNUM_FIELD(prst),
	SYSFS_DECNUM_FIELD(sint),
	SYSFS_DECNUM_FIELD(xpck),
	SYSFS_DECNUM_FIELD(pck2),
	SYSFS_DECNUM_FIELD(spml),
	SYSFS_SYMBOL_FIELD(gcont, rgbyc, ycrcb),
	SYSFS_DECNUM_FIELD(phs),
	SYSFS_DECNUM_FIELD(pvs),
	SYSFS_DECNUM_FIELD(pehs),
	SYSFS_DECNUM_FIELD(pevs),
	SYSFS_DECNUM_FIELD(clksel),
	SYSFS_DECNUM_FIELD(nvck),
	SYSFS_DECNUM_FIELD(slck2),
	SYSFS_DECNUM_FIELD(vcksel),
	SYSFS_DECNUM_FIELD(vhp));

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 include/linux/kernel.h | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 4fa360a13c1e..a9d57f6ed1d3 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -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)
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 091/120] MIPS: PS2: GS: Show privileged registers with sysfs
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (89 preceding siblings ...)
  2019-09-01 16:24 ` [PATCH 090/120] macro: Extend COUNT_ARGS() from 12 to 32 arguments Fredrik Noring
@ 2019-09-01 16:25 ` Fredrik Noring
  2019-09-01 16:25 ` [PATCH 092/120] MIPS: PS2: GS: Store " Fredrik Noring
                   ` (29 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:25 UTC (permalink / raw)
  To: Paul Burton, linux-mips; +Cc: Maciej W. Rozycki, Jürgen Urban

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

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

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

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

It relies on the following enum and struct definitions:

    enum gs_pmode_mmod { gs_mmod_circuit1, gs_mmod_alp };
    enum gs_pmode_amod { gs_amod_circuit1, gs_amod_circuit2 };
    enum gs_pmode_slbg { gs_slbg_circuit2, gs_slbg_bgcolor };
    struct gs_pmode {
    	u64 en1 : 1;		/* Enable read circuit 1 */
    	u64 en2 : 1;		/* Enable read circuit 2 */
    	u64 crtmd : 3;		/* CRT output switching (always 001) */
    	u64 mmod : 1;		/* Alpha blending value */
    	u64 amod : 1;		/* OUT1 alpha output */
    	u64 slbg : 1;		/* Alpha blending method */
    	u64 alp : 8;		/* Fixed alpha (0xff = 1.0) */
    	u64 zero : 1;		/* Must be zero */
    	u64 : 47;
    };

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/ps2/Makefile   |   1 +
 drivers/ps2/gs-sysfs.c | 457 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 458 insertions(+)
 create mode 100644 drivers/ps2/gs-sysfs.c

diff --git a/drivers/ps2/Makefile b/drivers/ps2/Makefile
index 28fb55803199..86ba1d3908dd 100644
--- a/drivers/ps2/Makefile
+++ b/drivers/ps2/Makefile
@@ -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
diff --git a/drivers/ps2/gs-sysfs.c b/drivers/ps2/gs-sysfs.c
new file mode 100644
index 000000000000..2429de3e0094
--- /dev/null
+++ b/drivers/ps2/gs-sysfs.c
@@ -0,0 +1,457 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 Graphics Synthesizer (GS) sysfs driver
+ *
+ * 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:
+ *
+ *	# cat /sys/devices/platform/gs/registers/pmode
+ *	en1 1
+ *	en2 0
+ *	crtmd 1
+ *	mmod circuit1
+ *	amod circuit1
+ *	slbg circuit2
+ *	alp 0
+ *
+ * The implementation uses a fair amount of macro expansions. This greatly
+ * simplifies register definitions, which in the case of PMODE is:
+ *
+ *	SYSFS_RW_REG(pmode, pmode,
+ *		SYSFS_DECNUM_FIELD(en1),
+ *		SYSFS_DECNUM_FIELD(en2),
+ *		SYSFS_DECNUM_FIELD(crtmd),
+ *		SYSFS_SYMBOL_FIELD(mmod, circuit1, alp),
+ *		SYSFS_SYMBOL_FIELD(amod, circuit1, circuit2),
+ *		SYSFS_SYMBOL_FIELD(slbg, circuit2, bgcolor),
+ *		SYSFS_DECNUM_FIELD(alp));
+ *
+ * It relies on the following enum and struct definitions:
+ *
+ *	enum gs_pmode_mmod { gs_mmod_circuit1, gs_mmod_alp };
+ *	enum gs_pmode_amod { gs_amod_circuit1, gs_amod_circuit2 };
+ *	enum gs_pmode_slbg { gs_slbg_circuit2, gs_slbg_bgcolor };
+ *	struct gs_pmode {
+ *		u64 en1 : 1;		// Enable read circuit 1
+ *		u64 en2 : 1;		// Enable read circuit 2
+ *		u64 crtmd : 3;		// CRT output switching (always 001)
+ *		u64 mmod : 1;		// Alpha blending value
+ *		u64 amod : 1;		// OUT1 alpha output
+ *		u64 slbg : 1;		// Alpha blending method
+ *		u64 alp : 8;		// Fixed alpha (0xff = 1.0)
+ *		u64 zero : 1;		// Must be zero
+ *		u64 : 47;
+ *	};
+ */
+
+#include <linux/ctype.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+
+#include <asm/page.h>
+#include <asm/uaccess.h>
+
+#include <asm/mach-ps2/gs.h>
+#include <asm/mach-ps2/gs-registers.h>
+
+#include <uapi/asm/gs.h>
+
+static struct kobject *registers_kobj;
+
+#define SYSFS_STATEMENT1(prefix_, macro_)				\
+	prefix_##macro_;
+
+#define SYSFS_STATEMENT2(prefix_, macro_, ...)				\
+	prefix_##macro_;						\
+	SYSFS_STATEMENT1(prefix_, __VA_ARGS__)
+
+#define SYSFS_STATEMENT3(prefix_, macro_, ...)				\
+	prefix_##macro_;						\
+	SYSFS_STATEMENT2(prefix_, __VA_ARGS__)
+
+#define SYSFS_STATEMENT4(prefix_, macro_, ...)				\
+	prefix_##macro_;						\
+	SYSFS_STATEMENT3(prefix_, __VA_ARGS__)
+
+#define SYSFS_STATEMENT5(prefix_, macro_, ...)				\
+	prefix_##macro_;						\
+	SYSFS_STATEMENT4(prefix_, __VA_ARGS__)
+
+#define SYSFS_STATEMENT6(prefix_, macro_, ...)				\
+	prefix_##macro_;						\
+	SYSFS_STATEMENT5(prefix_, __VA_ARGS__)
+
+#define SYSFS_STATEMENT7(prefix_, macro_, ...)				\
+	prefix_##macro_;						\
+	SYSFS_STATEMENT6(prefix_, __VA_ARGS__)
+
+#define SYSFS_STATEMENT8(prefix_, macro_, ...)				\
+	prefix_##macro_;						\
+	SYSFS_STATEMENT7(prefix_, __VA_ARGS__)
+
+#define SYSFS_STATEMENT9(prefix_, macro_, ...)				\
+	prefix_##macro_;						\
+	SYSFS_STATEMENT8(prefix_, __VA_ARGS__)
+
+#define SYSFS_STATEMENT10(prefix_, macro_, ...)				\
+	prefix_##macro_;						\
+	SYSFS_STATEMENT9(prefix_, __VA_ARGS__)
+
+#define SYSFS_STATEMENT11(prefix_, macro_, ...)				\
+	prefix_##macro_;						\
+	SYSFS_STATEMENT10(prefix_, __VA_ARGS__)
+
+#define SYSFS_STATEMENT12(prefix_, macro_, ...)				\
+	prefix_##macro_;						\
+	SYSFS_STATEMENT11(prefix_, __VA_ARGS__)
+
+#define SYSFS_STATEMENT13(prefix_, macro_, ...)				\
+	prefix_##macro_;						\
+	SYSFS_STATEMENT12(prefix_, __VA_ARGS__)
+
+#define SYSFS_STATEMENT14(prefix_, macro_, ...)				\
+	prefix_##macro_;						\
+	SYSFS_STATEMENT13(prefix_, __VA_ARGS__)
+
+#define SYSFS_STATEMENT15(prefix_, macro_, ...)				\
+	prefix_##macro_;						\
+	SYSFS_STATEMENT14(prefix_, __VA_ARGS__)
+
+#define SYSFS_STATEMENT16(prefix_, macro_, ...)				\
+	prefix_##macro_;						\
+	SYSFS_STATEMENT15(prefix_, __VA_ARGS__)
+
+#define SYSFS_STATEMENT17(prefix_, macro_, ...)				\
+	prefix_##macro_;						\
+	SYSFS_STATEMENT16(prefix_, __VA_ARGS__)
+
+#define SYSFS_STATEMENT18(prefix_, macro_, ...)				\
+	prefix_##macro_;						\
+	SYSFS_STATEMENT17(prefix_, __VA_ARGS__)
+
+#define SYSFS_STATEMENT19(prefix_, macro_, ...)				\
+	prefix_##macro_;						\
+	SYSFS_STATEMENT18(prefix_, __VA_ARGS__)
+
+#define SYSFS_STATEMENT20(prefix_, macro_, ...)				\
+	prefix_##macro_;						\
+	SYSFS_STATEMENT19(prefix_, __VA_ARGS__)
+
+#define SYSFS_STATEMENT21(prefix_, macro_, ...)				\
+	prefix_##macro_;						\
+	SYSFS_STATEMENT20(prefix_, __VA_ARGS__)
+
+#define SYSFS_STATEMENT22(prefix_, macro_, ...)				\
+	prefix_##macro_;						\
+	SYSFS_STATEMENT21(prefix_, __VA_ARGS__)
+
+#define SYSFS_STATEMENT23(prefix_, macro_, ...)				\
+	prefix_##macro_;						\
+	SYSFS_STATEMENT22(prefix_, __VA_ARGS__)
+
+#define SYSFS_STATEMENT24(prefix_, macro_, ...)				\
+	prefix_##macro_;						\
+	SYSFS_STATEMENT23(prefix_, __VA_ARGS__)
+
+#define SYSFS_STATEMENT25(prefix_, macro_, ...)				\
+	prefix_##macro_;						\
+	SYSFS_STATEMENT24(prefix_, __VA_ARGS__)
+
+#define SHOW_SYSFS_DECNUM_FIELD(field_, ...)				\
+	do {								\
+		if (valid)						\
+			n += scnprintf(&buf[n], PAGE_SIZE - n,		\
+				#field_ " %u\n", value.field_);		\
+		else							\
+			n += scnprintf(&buf[n], PAGE_SIZE - n,		\
+				#field_ "\n");				\
+	} while (false)
+
+#define SHOW_SYSFS_HEXNUM_FIELD(field_, ...)				\
+	do {								\
+		if (valid)						\
+			n += scnprintf(&buf[n], PAGE_SIZE - n,		\
+				#field_ " 0x%x\n", value.field_);	\
+		else							\
+			n += scnprintf(&buf[n], PAGE_SIZE - n,		\
+				#field_ "\n");				\
+	} while (false)
+
+#define SHOW_SYSFS_SYMBOL_STRING_ENTRY(field_, prefix_, value_)		\
+	value.field_ == prefix_##_##value_ ? " " #value_ :
+
+#define SHOW_SYSFS_SYMBOL_STRING1(field_, prefix_, value_)		\
+	SHOW_SYSFS_SYMBOL_STRING_ENTRY(field_, prefix_, value_) "-"
+
+#define SHOW_SYSFS_SYMBOL_STRING2(field_, prefix_, value_, ...)		\
+	SHOW_SYSFS_SYMBOL_STRING_ENTRY(field_, prefix_, value_)		\
+	SHOW_SYSFS_SYMBOL_STRING1(field_, prefix_, __VA_ARGS__)
+
+#define SHOW_SYSFS_SYMBOL_STRING3(field_, prefix_, value_, ...)		\
+	SHOW_SYSFS_SYMBOL_STRING_ENTRY(field_, prefix_, value_)		\
+	SHOW_SYSFS_SYMBOL_STRING2(field_, prefix_, __VA_ARGS__)
+
+#define SHOW_SYSFS_SYMBOL_STRING4(field_, prefix_, value_, ...)		\
+	SHOW_SYSFS_SYMBOL_STRING_ENTRY(field_, prefix_, value_)		\
+	SHOW_SYSFS_SYMBOL_STRING3(field_, prefix_, __VA_ARGS__)
+
+#define SHOW_SYSFS_SYMBOL_STRING5(field_, prefix_, value_, ...)		\
+	SHOW_SYSFS_SYMBOL_STRING_ENTRY(field_, prefix_, value_)		\
+	SHOW_SYSFS_SYMBOL_STRING4(field_, prefix_, __VA_ARGS__)
+
+#define SHOW_SYSFS_SYMBOL_STRING(field_, prefix_, ...)			\
+	!valid ? "" :							\
+	CONCATENATE(SHOW_SYSFS_SYMBOL_STRING,				\
+		COUNT_ARGS(__VA_ARGS__))(field_, prefix_, __VA_ARGS__)
+
+#define SHOW_SYSFS_SYMBOL_FIELD(field_, ...)			\
+	n += scnprintf(&buf[n], PAGE_SIZE - n, #field_ "%s\n",		\
+		SHOW_SYSFS_SYMBOL_STRING(field_, gs_##field_, __VA_ARGS__))
+
+#define SHOW_SYSFS_REGISTER(reg, str, ...)				\
+	static ssize_t show_##reg(struct device *device,		\
+		struct device_attribute *attr, char *buf)		\
+	{								\
+		const bool valid = gs_valid_##reg();			\
+		const struct gs_##str value = valid ?			\
+			gs_read_##reg() : (struct gs_##str) { };	\
+		size_t n = 0;						\
+		__VA_ARGS__;						\
+		return n;						\
+	}
+
+#define SYSFS_SHOW(reg, str, ...)					\
+	SHOW_SYSFS_REGISTER(reg, str,					\
+		CONCATENATE(SYSFS_STATEMENT,				\
+			COUNT_ARGS(__VA_ARGS__))(SHOW_, __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_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));
+
+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));
+
+SYSFS_RW_REG(smode2, smode2,
+	SYSFS_SYMBOL_FIELD(intm, progressive, interlace),
+	SYSFS_SYMBOL_FIELD(ffmd, field, frame),
+	SYSFS_SYMBOL_FIELD(dpms, on, standby, suspend, off));
+
+SYSFS_RW_REG(srfsh, srfsh,
+	SYSFS_DECNUM_FIELD(rfsh));
+
+SYSFS_RW_REG(synch1, synch1,
+	SYSFS_DECNUM_FIELD(hfp),
+	SYSFS_DECNUM_FIELD(hbp),
+	SYSFS_DECNUM_FIELD(hseq),
+	SYSFS_DECNUM_FIELD(hsvs),
+	SYSFS_DECNUM_FIELD(hs));
+
+SYSFS_RW_REG(synch2, synch2,
+	SYSFS_DECNUM_FIELD(hf),
+	SYSFS_DECNUM_FIELD(hb));
+
+SYSFS_RW_REG(syncv, syncv,
+	SYSFS_DECNUM_FIELD(vfp),
+	SYSFS_DECNUM_FIELD(vfpe),
+	SYSFS_DECNUM_FIELD(vbp),
+	SYSFS_DECNUM_FIELD(vbpe),
+	SYSFS_DECNUM_FIELD(vdp),
+	SYSFS_DECNUM_FIELD(vs));
+
+SYSFS_RW_REG(dispfb1, dispfb,
+	SYSFS_DECNUM_FIELD(fbp),
+	SYSFS_DECNUM_FIELD(fbw),
+	SYSFS_SYMBOL_FIELD(psm, ct32, ct24, ct16, ct16s, gpu24),
+	SYSFS_DECNUM_FIELD(dbx),
+	SYSFS_DECNUM_FIELD(dby));
+
+SYSFS_RW_REG(display1, display,
+	SYSFS_DECNUM_FIELD(dx),
+	SYSFS_DECNUM_FIELD(dy),
+	SYSFS_DECNUM_FIELD(magh),
+	SYSFS_DECNUM_FIELD(magv),
+	SYSFS_DECNUM_FIELD(dw),
+	SYSFS_DECNUM_FIELD(dh));
+
+SYSFS_RW_REG(dispfb2, dispfb,
+	SYSFS_DECNUM_FIELD(fbp),
+	SYSFS_DECNUM_FIELD(fbw),
+	SYSFS_SYMBOL_FIELD(psm, ct32, ct24, ct16, ct16s),
+	SYSFS_DECNUM_FIELD(dbx),
+	SYSFS_DECNUM_FIELD(dby));
+
+SYSFS_RW_REG(display2, display,
+	SYSFS_DECNUM_FIELD(dx),
+	SYSFS_DECNUM_FIELD(dy),
+	SYSFS_DECNUM_FIELD(magh),
+	SYSFS_DECNUM_FIELD(magv),
+	SYSFS_DECNUM_FIELD(dw),
+	SYSFS_DECNUM_FIELD(dh));
+
+SYSFS_RW_REG(extbuf, extbuf,
+	SYSFS_DECNUM_FIELD(exbp),
+	SYSFS_DECNUM_FIELD(exbw),
+	SYSFS_SYMBOL_FIELD(fbin, out1, out2),
+	SYSFS_SYMBOL_FIELD(wffmd, field, frame),
+	SYSFS_SYMBOL_FIELD(emoda, alpha, y, yhalf, zero),
+	SYSFS_SYMBOL_FIELD(emodc, rgb, y, ycbcr, alpha),
+	SYSFS_DECNUM_FIELD(wdx),
+	SYSFS_DECNUM_FIELD(wdy));
+
+SYSFS_RW_REG(extdata, extdata,
+	SYSFS_DECNUM_FIELD(sx),
+	SYSFS_DECNUM_FIELD(sy),
+	SYSFS_DECNUM_FIELD(smph),
+	SYSFS_DECNUM_FIELD(smpv),
+	SYSFS_DECNUM_FIELD(ww),
+	SYSFS_DECNUM_FIELD(wh));
+
+SYSFS_RW_REG(extwrite, extwrite,
+	SYSFS_SYMBOL_FIELD(write, complete_current, start_next));
+
+SYSFS_RW_REG(bgcolor, bgcolor,
+	SYSFS_DECNUM_FIELD(r),
+	SYSFS_DECNUM_FIELD(g),
+	SYSFS_DECNUM_FIELD(b));
+
+SYSFS_RO_REG(csr, csr,
+	SYSFS_DECNUM_FIELD(signal),
+	SYSFS_DECNUM_FIELD(finish),
+	SYSFS_DECNUM_FIELD(hsint),
+	SYSFS_DECNUM_FIELD(vsint),
+	SYSFS_DECNUM_FIELD(edwint),
+	SYSFS_DECNUM_FIELD(flush),
+	SYSFS_DECNUM_FIELD(reset),
+	SYSFS_DECNUM_FIELD(nfield),
+	SYSFS_SYMBOL_FIELD(field, even, odd),
+	SYSFS_SYMBOL_FIELD(fifo, neither, empty, almost_full),
+	SYSFS_HEXNUM_FIELD(rev),
+	SYSFS_HEXNUM_FIELD(id));
+
+SYSFS_RO_REG(imr, imr,
+	SYSFS_DECNUM_FIELD(sigmsk),
+	SYSFS_DECNUM_FIELD(finishmsk),
+	SYSFS_DECNUM_FIELD(hsmsk),
+	SYSFS_DECNUM_FIELD(vsmsk),
+	SYSFS_DECNUM_FIELD(edwmsk),
+	SYSFS_DECNUM_FIELD(ones));
+
+SYSFS_RO_REG(busdir, busdir,
+	SYSFS_SYMBOL_FIELD(dir, host_to_local, local_to_host));
+
+SYSFS_RW_REG(siglblid, siglblid,
+	SYSFS_DECNUM_FIELD(sigid),
+	SYSFS_DECNUM_FIELD(lblid));
+
+static struct attribute *gs_registers_attributes[] = {
+	&dev_attr_pmode.attr,
+	&dev_attr_smode1.attr,
+	&dev_attr_smode2.attr,
+	&dev_attr_srfsh.attr,
+	&dev_attr_synch1.attr,
+	&dev_attr_synch2.attr,
+	&dev_attr_syncv.attr,
+	&dev_attr_dispfb1.attr,
+	&dev_attr_display1.attr,
+	&dev_attr_dispfb2.attr,
+	&dev_attr_display2.attr,
+	&dev_attr_extbuf.attr,
+	&dev_attr_extdata.attr,
+	&dev_attr_extwrite.attr,
+	&dev_attr_bgcolor.attr,
+	&dev_attr_csr.attr,		/* FIXME: Too lowlevel for sysfs? */
+	&dev_attr_imr.attr,		/* FIXME: Too lowlevel for sysfs? */
+	&dev_attr_busdir.attr,		/* FIXME: Too lowlevel for sysfs? */
+	&dev_attr_siglblid.attr,	/* FIXME: Too lowlevel for sysfs? */
+	NULL
+};
+
+static struct attribute_group gs_registers_attribute_group = {
+	.attrs = gs_registers_attributes
+};
+
+static int __init gs_sysfs_init(void)
+{
+	struct device *gs_dev = gs_device();	/* FIXME: Is this method appropriate? */
+	int err;
+
+	if (!gs_dev) {
+		pr_err("gs-sysfs: Failed to retrieve gs device\n");
+		err = -ENXIO;
+		goto gs_dev_err;
+	}
+
+	registers_kobj = kobject_create_and_add("registers", &gs_dev->kobj);
+	if (!registers_kobj) {
+		pr_err("gs-sysfs: Failed to create and add register kernel object\n");
+		err = -ENOMEM;
+		goto kobj_err;
+	}
+
+	err = sysfs_create_group(registers_kobj, &gs_registers_attribute_group);
+	if (err) {
+		pr_err("gs-sysfs: Failed to create register sysfs group\n");
+		goto group_err;
+	}
+
+	return 0;
+
+group_err:
+	kobject_del(registers_kobj);
+
+kobj_err:
+gs_dev_err:
+	return err;
+}
+
+static void __exit gs_sysfs_exit(void)
+{
+	kobject_del(registers_kobj);
+}
+
+module_init(gs_sysfs_init);
+module_exit(gs_sysfs_exit);
+
+MODULE_DESCRIPTION("PlayStation 2 Graphics Synthesizer sysfs driver");
+MODULE_AUTHOR("Fredrik Noring");
+MODULE_LICENSE("GPL");
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 092/120] MIPS: PS2: GS: Store privileged registers with sysfs
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (90 preceding siblings ...)
  2019-09-01 16:25 ` [PATCH 091/120] MIPS: PS2: GS: Show privileged registers with sysfs Fredrik Noring
@ 2019-09-01 16:25 ` " Fredrik Noring
  2019-09-01 16:25 ` [PATCH 093/120] fbdev: Add fb_warn_once() variant that only prints a warning once Fredrik Noring
                   ` (28 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:25 UTC (permalink / raw)
  To: Paul Burton, linux-mips; +Cc: Maciej W. Rozycki, Jürgen Urban

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

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

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/ps2/gs-sysfs.c | 145 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 140 insertions(+), 5 deletions(-)

diff --git a/drivers/ps2/gs-sysfs.c b/drivers/ps2/gs-sysfs.c
index 2429de3e0094..3c749375aee0 100644
--- a/drivers/ps2/gs-sysfs.c
+++ b/drivers/ps2/gs-sysfs.c
@@ -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) {
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 093/120] fbdev: Add fb_warn_once() variant that only prints a warning once
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (91 preceding siblings ...)
  2019-09-01 16:25 ` [PATCH 092/120] MIPS: PS2: GS: Store " Fredrik Noring
@ 2019-09-01 16:25 ` Fredrik Noring
  2019-09-01 23:12   ` Philippe Mathieu-Daudé
  2019-09-01 16:26 ` [PATCH 094/120] MIPS: PS2: FB: Frame buffer driver for the PlayStation 2 Fredrik Noring
                   ` (27 subsequent siblings)
  120 siblings, 1 reply; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:25 UTC (permalink / raw)
  To: linux-mips

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 include/linux/fb.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/include/linux/fb.h b/include/linux/fb.h
index 303771264644..19f5118e34ea 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -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, ...)					\
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 094/120] MIPS: PS2: FB: Frame buffer driver for the PlayStation 2
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (92 preceding siblings ...)
  2019-09-01 16:25 ` [PATCH 093/120] fbdev: Add fb_warn_once() variant that only prints a warning once Fredrik Noring
@ 2019-09-01 16:26 ` Fredrik Noring
  2019-09-02  1:12   ` Jiaxun Yang
  2019-09-01 16:30 ` [PATCH 095/120] MIPS: PS2: FB: fb_set_par() standard-definition television support Fredrik Noring
                   ` (26 subsequent siblings)
  120 siblings, 1 reply; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:26 UTC (permalink / raw)
  To: linux-mips

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

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

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 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

diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
index 6b2de93bd302..cc93cbd67b01 100644
--- a/drivers/video/fbdev/Kconfig
+++ b/drivers/video/fbdev/Kconfig
@@ -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
diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile
index 7dc4861a93e6..1e55fa8ca4af 100644
--- a/drivers/video/fbdev/Makefile
+++ b/drivers/video/fbdev/Makefile
@@ -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
diff --git a/drivers/video/fbdev/ps2fb.c b/drivers/video/fbdev/ps2fb.c
new file mode 100644
index 000000000000..7bfbc3c2aa4d
--- /dev/null
+++ b/drivers/video/fbdev/ps2fb.c
@@ -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");
diff --git a/include/linux/console_struct.h b/include/linux/console_struct.h
index 24d4c16e3ae0..cb562672cc3a 100644
--- a/include/linux/console_struct.h
+++ b/include/linux/console_struct.h
@@ -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;
diff --git a/include/uapi/linux/fb.h b/include/uapi/linux/fb.h
index b6aac7ee1f67..38d88eebf651 100644
--- a/include/uapi/linux/fb.h
+++ b/include/uapi/linux/fb.h
@@ -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		*/
 
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 095/120] MIPS: PS2: FB: fb_set_par() standard-definition television support
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (93 preceding siblings ...)
  2019-09-01 16:26 ` [PATCH 094/120] MIPS: PS2: FB: Frame buffer driver for the PlayStation 2 Fredrik Noring
@ 2019-09-01 16:30 ` Fredrik Noring
  2019-09-01 16:30 ` [PATCH 096/120] MIPS: PS2: FB: fb_set_par() high-definition " Fredrik Noring
                   ` (25 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:30 UTC (permalink / raw)
  To: linux-mips

Most of the computations for the video synchronisation registers are
based on trial an error, as their bit fields are undocumented. A small
set of standard video modes are supplied with the Graphics Synthesizer
user manual[1], and for these modes the corresponding register values
are known.

This frame buffer driver allows arbitrary top, bottom, left and right
video display margin (border) adjustments, hence registers are computed
instead of tabulated. This is useful to for example precisely center the
image for a given analogue video display.

The SDTV modes are designed to work with S-video, SCART and component
video cables, in addition to the PS2 HDMI adapter based on the Macro
Silicon MS9282 chip.

The MAGV and MAGH fields for vertical and horizontal magnification in
the DISPLAY registers could be used to support lower resolution video
modes, for example 320x200 that was popular with many 8-bit and 16-bit
computers, but that is left for a future extension.

References:

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/video/fbdev/ps2fb.c | 328 ++++++++++++++++++++++++++++++++++++
 1 file changed, 328 insertions(+)

diff --git a/drivers/video/fbdev/ps2fb.c b/drivers/video/fbdev/ps2fb.c
index 7bfbc3c2aa4d..3fb31719459c 100644
--- a/drivers/video/fbdev/ps2fb.c
+++ b/drivers/video/fbdev/ps2fb.c
@@ -133,6 +133,97 @@ struct ps2fb_par {
 	} package;
 };
 
+/**
+ * struct gs_sync_param - Graphics Synthesizer registers for video modes
+ * @smode1: SMODE1 register
+ * @smode2: SMODE2 register
+ * @srfsh: SRFSH register
+ * @synch1: SYNCH1 register
+ * @synch2: SYNCH2 register
+ * @syncv: SYNCV register
+ * @display: DISPLAY1 or DISPLAY2 register
+ *
+ * These are the essential Graphics Synthesizer video synchronisation register
+ * parameters.
+ */
+struct gs_sync_param {
+	struct gs_smode1 smode1;
+	struct gs_smode2 smode2;
+	struct gs_srfsh srfsh;
+	struct gs_synch1 synch1;
+	struct gs_synch2 synch2;
+	struct gs_syncv syncv;
+	struct gs_display display;
+};
+
+/**
+ * var_to_fbw - frame buffer width for a given virtual x resolution
+ * @var: screen info object to compute FBW for
+ *
+ * Return: frame buffer width (FBW) in 64-pixel unit
+ */
+static u32 var_to_fbw(const struct fb_var_screeninfo *var)
+{
+	/*
+	 * Round up to nearest GS_FB_PAGE_WIDTH (64 px) since there are
+	 * valid resolutions such as 720 px that do not divide 64 properly.
+	 */
+	return (var->xres_virtual + GS_FB_PAGE_WIDTH - 1) / GS_FB_PAGE_WIDTH;
+}
+
+/**
+ * var_to_psm - frame buffer pixel storage mode for a given bits per pixel
+ * @var: screen info object to compute PSM for
+ * @info: frame buffer info object
+ *
+ * Return: frame buffer pixel storage mode
+ */
+static enum gs_psm var_to_psm(const struct fb_var_screeninfo *var,
+	const struct fb_info *info)
+{
+	if (var->bits_per_pixel == 1)
+		return gs_psm_ct16;
+	if (var->bits_per_pixel == 16)
+		return gs_psm_ct16;
+	if (var->bits_per_pixel == 32)
+		return gs_psm_ct32;
+
+	fb_warn_once(info, "%s: Unsupported bits per pixel %u\n",
+		__func__, var->bits_per_pixel);
+	return gs_psm_ct32;
+}
+
+/**
+ * var_to_block_count - number of frame buffer blocks for a given video mode
+ * @var: screen info object to compute the number of blocks for
+ *
+ * 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 PSMCT16 and PSMCT32. Blocks are further
+ * subdivided into columns, which are finally subdivided into pixels.
+ *
+ * The video display buffer, textures and palettes share the same frame buffer.
+ * This function can be used to compute the first free block after the video
+ * display buffer.
+ *
+ * Return: number of blocks, or zero for unsupported pixel storage modes
+ */
+static u32 var_to_block_count(const struct fb_info *info)
+{
+	const struct fb_var_screeninfo *var = &info->var;
+	const enum gs_psm psm = var_to_psm(var, info);
+	const u32 fbw = var_to_fbw(var);
+
+	if (psm == gs_psm_ct16)
+		return gs_psm_ct16_block_count(fbw, var->yres_virtual);
+	if (psm == gs_psm_ct32)
+		return gs_psm_ct32_block_count(fbw, var->yres_virtual);
+
+	fb_warn_once(info, "%s: Unsupported pixel storage mode %u\n",
+		__func__, psm);
+	return 0;
+}
+
 /**
  * texture_least_power_of_2 - round up to a power of 2, not less than 8
  * @n: integer to round up
@@ -330,6 +421,242 @@ static int ps2fb_cb_check_var(
 	return err;
 }
 
+/**
+ * refresh_for_var - display refresh frequency for a given screen info
+ * @var: screen info object to compute the display frequency for
+ *
+ * Return: display refresh frequency in hertz
+ */
+static u32 refresh_for_var(const struct fb_var_screeninfo *var)
+{
+	const u32 htotal = var->hsync_len +
+		var->left_margin + var->xres + var->right_margin;
+	const u32 vtotal = var->vsync_len +
+		var->upper_margin + var->yres + var->lower_margin;
+	const u32 ptotal = htotal * vtotal;
+
+	return DIV_ROUND_CLOSEST_ULL(DIV_ROUND_CLOSEST_ULL(
+		1000000000000ull * ((var->vmode & FB_VMODE_INTERLACED) ? 2 : 1),
+		var->pixclock), ptotal);
+}
+
+/**
+ * vm_to_cmod - determine the CMOD field for the SMODE1 register
+ * @vm: video mode object to compute CMOD for
+ *
+ * Result: PAL, NTSC or VESA
+ */
+static enum gs_smode1_cmod vm_to_cmod(const struct fb_videomode *vm)
+{
+	const u32 htotal = vm->hsync_len +
+		vm->left_margin + vm->xres + vm->right_margin;
+	const u32 vtotal = vm->vsync_len +
+		vm->upper_margin + vm->yres + vm->lower_margin;
+	const u32 ptotal = htotal * vtotal;
+	const u32 refresh = DIV_ROUND_CLOSEST_ULL(DIV_ROUND_CLOSEST_ULL(
+		1000000000000ull * ((vm->vmode & FB_VMODE_INTERLACED) ? 2 : 1),
+		vm->pixclock), ptotal);
+
+	if (vm->sync & FB_SYNC_BROADCAST)
+		return refresh < 55 ? gs_cmod_pal :
+		       refresh < 65 ? gs_cmod_ntsc :
+				      gs_cmod_vesa;
+
+	return gs_cmod_vesa;
+}
+
+/**
+ * vm_to_sp_sdtv - standard-definition television video synch parameters
+ * @vm: video mode object to compute synchronisation parameters for
+ *
+ * The numeric register field constants come from fixed SDTV video modes made
+ * by Sony. The main complication is that these values are the basis to compute
+ * arbitrary top, bottom, left and right display margin (border) settings for
+ * both PAL and NTSC. This is useful to for example precisely center the image
+ * for a given analogue video display.
+ *
+ * The SDTV modes are designed to work with S-video, SCART and component video
+ * cables, in addition to the PS2 HDMI adapter based on the Macro Silicon
+ * MS9282 chip.
+ *
+ * The MAGV and MAGH fields for vertical and horizontal magnification in the
+ * DISPLAY registers could be used to support lower resolution video modes,
+ * for example 320x200 that was popular with many 8-bit and 16-bit computers,
+ * but that is left for a future extension.
+ *
+ * Return: Graphics Synthesizer SDTV video mode synchronisation parameters
+ */
+static struct gs_sync_param vm_to_sp_sdtv(const struct fb_videomode *vm)
+{
+	const u32 cmod = vm_to_cmod(vm);
+	const u32 intm = (vm->vmode & FB_VMODE_INTERLACED) ? 1 : 0;
+	const u32 vs   = cmod == gs_cmod_pal ? 5 : 6;
+	const u32 hb   = cmod == gs_cmod_pal ? 1680 : 1652;
+	const u32 hf   = 2892 - hb;
+	const u32 hs   = 254;
+	const u32 hbp  = cmod == gs_cmod_pal ? 262 : 222;
+	const u32 hfp  = cmod == gs_cmod_pal ? 48 : 64;
+	const u32 vdp  = cmod == gs_cmod_pal ? 576 : 480;
+	const u32 vbpe = vs;
+	const u32 vbp  = cmod == gs_cmod_pal ? 33 : 26;
+	const u32 vfpe = vs;
+	const u32 vfp  = (vm->vmode & FB_VMODE_INTERLACED) ? 1 :
+		cmod == gs_cmod_pal ? 4 : 2;
+	const u32 tw = hb + hf;
+	const u32 th = vdp;
+	const u32 dw = min_t(u32, vm->xres * 4, tw);
+	const u32 dh = min_t(u32, vm->yres * (intm ? 1 : 2), th);
+	const u32 dx = hs + hbp + (tw - dw)/2 - 1;
+	const u32 dy = (vs + vbp + vbpe + (th - dh)/2) / (intm ? 1 : 2) - 1;
+
+	return (struct gs_sync_param) {
+		.smode1 = {
+			.vhp    =    0, .vcksel = 1, .slck2 = 1, .nvck = 1,
+			.clksel =    1, .pevs   = 0, .pehs  = 0, .pvs  = 0,
+			.phs    =    0, .gcont  = 0, .spml  = 4, .pck2 = 0,
+			.xpck   =    0, .sint   = 1, .prst  = 0, .ex   = 0,
+			.cmod   = cmod, .slck   = 0, .t1248 = 1,
+			.lc     =   32, .rc     = 4
+		},
+		.smode2 = {
+			.intm = intm
+		},
+		.srfsh = {
+			.rfsh = 8
+		},
+		.synch1 = {
+			.hs   = hs,
+			.hsvs = cmod == gs_cmod_pal ? 1474 : 1462,
+			.hseq = cmod == gs_cmod_pal ? 127 : 124,
+			.hbp  = hbp,
+			.hfp  = hfp
+		},
+		.synch2 = {
+			.hb = hb,
+			.hf = hf
+		},
+		.syncv = {
+			.vs   = vs,
+			.vdp  = vdp,
+			.vbpe = vbpe,
+			.vbp  = vbp,
+			.vfpe = vfpe,
+			.vfp  = vfp
+		},
+		.display = {
+			.dh   = vm->yres - 1,
+			.dw   = vm->xres * 4 - 1,
+			.magv = 0,
+			.magh = 3,
+			.dy   = dy,
+			.dx   = dx
+		}
+	};
+}
+
+static struct gs_sync_param vm_to_sp_for_synch_gen(
+	const struct fb_videomode *vm, const struct gs_synch_gen sg)
+{
+	struct gs_sync_param sp = vm_to_sp_sdtv(vm);
+
+	sp.smode1.gcont = gs_gcont_ycrcb;
+	sp.smode1.sint = 1;
+	sp.smode1.prst = 0;
+
+	return sp;
+}
+
+static struct gs_sync_param vm_to_sp(const struct fb_videomode *vm)
+{
+	return vm_to_sp_for_synch_gen(vm, gs_synch_gen_for_vck(vm->pixclock));
+}
+
+static int ps2fb_set_par(struct fb_info *info)
+{
+	struct ps2fb_par *par = info->par;
+	const struct fb_var_screeninfo *var = &info->var;
+	const struct fb_videomode *mm = fb_match_mode(var, &info->modelist);
+	const struct fb_videomode vm = (struct fb_videomode) {
+		.refresh      = refresh_for_var(var),
+		.xres         = var->xres,
+		.yres         = var->yres,
+		.pixclock     = var->pixclock,
+		.left_margin  = var->left_margin,
+		.right_margin = var->right_margin,
+		.upper_margin = var->upper_margin,
+		.lower_margin = var->lower_margin,
+		.hsync_len    = var->hsync_len,
+		.vsync_len    = var->vsync_len,
+		.sync         = var->sync,
+		.vmode        = var->vmode,
+		.flag         = mm != NULL ? mm->flag : 0
+	};
+	const struct gs_sync_param sp = vm_to_sp(&vm);
+	struct gs_smode1 smode1 = sp.smode1;
+
+	par->mode = vm;
+
+	info->fix.type = FB_TYPE_PACKED_PIXELS;
+	info->fix.visual = FB_VISUAL_TRUECOLOR;
+	info->fix.xpanstep = 0;
+	info->fix.ypanstep = 0;
+	info->fix.ywrapstep = 1;
+	info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
+
+	gs_write_smode1(smode1);
+	gs_write_smode2(sp.smode2);
+	gs_write_srfsh(sp.srfsh);
+	gs_write_synch1(sp.synch1);
+	gs_write_synch2(sp.synch2);
+	gs_write_syncv(sp.syncv);
+	gs_write_display1(sp.display);
+
+	GS_WRITE_DISPFB1(
+		.fbw = var_to_fbw(var),
+		.psm = var_to_psm(var, info),
+		.dbx = var->xoffset,
+		.dby = var->yoffset,
+	);
+
+	GS_WRITE_PMODE(
+		.en1 = 1,
+		.crtmd = 1
+	);
+
+	smode1.prst = 1;
+	gs_write_smode1(smode1);
+
+	udelay(2500);
+
+	smode1.sint = 0;
+	smode1.prst = 0;
+	gs_write_smode1(smode1);
+
+	return 0;
+}
+
+static int ps2fb_cb_set_par(struct fb_info *info)
+{
+	struct ps2fb_par *par = info->par;
+	unsigned long flags;
+	int err;
+
+	spin_lock_irqsave(&par->lock, flags);
+
+	err = ps2fb_set_par(info);
+	if (!err)
+		par->cb.block_count = var_to_block_count(info);
+
+	spin_unlock_irqrestore(&par->lock, flags);
+
+	if (!err && info->tileops)
+		fb_info(info, "%d tiles maximum for %ux%u font\n",
+			info->tileops->fb_get_tilemax(info),
+			par->cb.tile.width, par->cb.tile.height);
+
+	return err;
+}
+
 static u32 block_dimensions(u32 dim, u32 alignment)
 {
 	u32 mask = 0;
@@ -347,6 +674,7 @@ static int init_console_buffer(struct platform_device *pdev,
 {
 	static struct fb_ops fbops = {
 		.owner		= THIS_MODULE,
+		.fb_set_par	= ps2fb_cb_set_par,
 		.fb_check_var	= ps2fb_cb_check_var,
 	};
 
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 096/120] MIPS: PS2: FB: fb_set_par() high-definition television support
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (94 preceding siblings ...)
  2019-09-01 16:30 ` [PATCH 095/120] MIPS: PS2: FB: fb_set_par() standard-definition television support Fredrik Noring
@ 2019-09-01 16:30 ` " Fredrik Noring
  2019-09-01 16:31 ` [PATCH 097/120] MIPS: PS2: FB: fb_set_par() VESA computer display mode support Fredrik Noring
                   ` (24 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:30 UTC (permalink / raw)
  To: linux-mips

Some of the numeric register field constants come from fixed DTV video
modes made by Sony[1]. The main complication is that these values are
the basis to compute arbitrary top, bottom, left and right display
margin (border) settings. This is useful to for example precisely
center the image for a given analogue video display.

The HDTV modes are designed to work with component video cables and the
PS2 HDMI adapter based on the Macro Silicon MS9282 chip.

The MAGV and MAGH fields for vertical and horizontal magnification in
the DISPLAY registers could be used to emulate SDTV resolution video
modes such as 320x200 that was popular with many 8-bit and 16-bit
computers, but that is left for a future extension.

References:

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/video/fbdev/ps2fb.c | 86 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 85 insertions(+), 1 deletion(-)

diff --git a/drivers/video/fbdev/ps2fb.c b/drivers/video/fbdev/ps2fb.c
index 3fb31719459c..f9061ccc5755 100644
--- a/drivers/video/fbdev/ps2fb.c
+++ b/drivers/video/fbdev/ps2fb.c
@@ -554,10 +554,94 @@ static struct gs_sync_param vm_to_sp_sdtv(const struct fb_videomode *vm)
 	};
 }
 
+/**
+ * vm_to_sp_hdtv - high-definition television video synch parameters
+ * @vm: video mode object to compute synchronisation parameters for
+ * @sg: Graphics Synthesizer SMODE1 register video clock fields
+ *
+ * Some of the numeric register field constants come from fixed DTV video
+ * modes made by Sony. The main complication is that these values are the
+ * basis to compute arbitrary top, bottom, left and right display margin
+ * (border) settings. This is useful to for example precisely center the
+ * image for a given analogue video display.
+ *
+ * The HDTV modes are designed to work with component video cables and the
+ * PS2 HDMI adapter based on the Macro Silicon MS9282 chip.
+ *
+ * The MAGV and MAGH fields for vertical and horizontal magnification in the
+ * DISPLAY registers could be used to emulate SDTV resolution video modes such
+ * as 320x200 that was popular with many 8-bit and 16-bit computers, but that
+ * is left for a future extension.
+ *
+ * Return: Graphics Synthesizer HDTV video mode synchronisation parameters
+ */
+static struct gs_sync_param vm_to_sp_hdtv(
+	const struct fb_videomode *vm, const struct gs_synch_gen sg)
+{
+	const u32 spml  = sg.spml;
+	const u32 t1248 = sg.t1248;
+	const u32 lc    = sg.lc;
+	const u32 rc    = sg.rc;
+	const u32 vc    = vm->yres <= 576 ? 1 : 0;
+	const u32 hadj  = spml / 2;
+	const u32 vhp   = (vm->vmode & FB_VMODE_INTERLACED) ? 0 : 1;
+	const u32 hb    = vm->xres * spml * 3 / 5;
+
+	return (struct gs_sync_param) {
+		.smode1 = {
+			.vhp    = vhp, .vcksel = vc, .slck2 =     1, .nvck = 1,
+			.clksel =   1, .pevs   =  0, .pehs  =     0, .pvs  = 0,
+			.phs    =   0, .gcont  =  0, .spml  =  spml, .pck2 = 0,
+			.xpck   =   0, .sint   =  1, .prst  =     0, .ex   = 0,
+			.cmod   =   0, .slck   =  0, .t1248 = t1248,
+			.lc     =  lc, .rc     = rc
+		},
+		.smode2 = {
+			.intm = (vm->vmode & FB_VMODE_INTERLACED) ? 1 : 0
+		},
+		.srfsh = {
+			.rfsh = gs_rfsh_from_synch_gen(sg)
+		},
+		.synch1 = {
+			.hs   = vm->hsync_len * spml,
+			.hsvs = (vm->left_margin + vm->xres +
+				 vm->right_margin - vm->hsync_len) * spml / 2,
+			.hseq = vm->hsync_len * spml,
+			.hbp  = vm->left_margin * spml - hadj,
+			.hfp  = vm->right_margin * spml + hadj
+		},
+		.synch2 = {
+			.hb = hb,
+			.hf = vm->xres * spml - hb
+		},
+		.syncv = {
+			.vs   = vm->vsync_len,
+			.vdp  = vm->yres,
+			.vbpe = 0,
+			.vbp  = vm->upper_margin,
+			.vfpe = 0,
+			.vfp  = vm->lower_margin
+		},
+		.display = {
+			.dh   = vm->yres - 1,
+			.dw   = vm->xres * spml - 1,
+			.magv = 0,
+			.magh = spml - 1,
+			.dy   = vm->vsync_len + vm->upper_margin - 1,
+			.dx   = (vm->hsync_len + vm->left_margin) * spml - 1 - hadj
+		}
+	};
+}
+
 static struct gs_sync_param vm_to_sp_for_synch_gen(
 	const struct fb_videomode *vm, const struct gs_synch_gen sg)
 {
-	struct gs_sync_param sp = vm_to_sp_sdtv(vm);
+	const bool bc = vm->sync & FB_SYNC_BROADCAST;
+	const bool il = vm->vmode & FB_VMODE_INTERLACED;
+	struct gs_sync_param sp =
+		vm->yres <= 288 &&       bc ? vm_to_sp_sdtv(vm) :
+		vm->yres <= 576 && il && bc ? vm_to_sp_sdtv(vm) :
+					      vm_to_sp_hdtv(vm, sg);
 
 	sp.smode1.gcont = gs_gcont_ycrcb;
 	sp.smode1.sint = 1;
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 097/120] MIPS: PS2: FB: fb_set_par() VESA computer display mode support
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (95 preceding siblings ...)
  2019-09-01 16:30 ` [PATCH 096/120] MIPS: PS2: FB: fb_set_par() high-definition " Fredrik Noring
@ 2019-09-01 16:31 ` Fredrik Noring
  2019-09-01 16:31 ` [PATCH 098/120] MIPS: PS2: FB: Preconfigure standard PAL, NTSC and VESA display modes Fredrik Noring
                   ` (23 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:31 UTC (permalink / raw)
  To: linux-mips

Some of the numeric register field constants come from fixed VESA video
modes made by Sony[1]. The main complication is that these values are
the basis to compute arbitrary top, bottom, left and right display
margin (border) settings. This is useful to for example precisely
center the image for a given analogue video display.

The VESA modes are designed to work with the synch-on-green (SOG) VGA
cable that Sony distributed with the Linux kit for the PlayStation 2.
Modern computer displays typically do not support synch-on-green, so
an adapter is most likely necessary for these modes.

References:

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/video/fbdev/ps2fb.c | 78 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 77 insertions(+), 1 deletion(-)

diff --git a/drivers/video/fbdev/ps2fb.c b/drivers/video/fbdev/ps2fb.c
index f9061ccc5755..dd8b468ab72b 100644
--- a/drivers/video/fbdev/ps2fb.c
+++ b/drivers/video/fbdev/ps2fb.c
@@ -633,6 +633,81 @@ static struct gs_sync_param vm_to_sp_hdtv(
 	};
 }
 
+/**
+ * vm_to_sp_vesa - VESA computer display mode synch parameters
+ * @vm: video mode object to compute synchronisation parameters for
+ * @sg: Graphics Synthesizer SMODE1 register video clock fields
+ *
+ * Some of the numeric register field constants come from fixed VESA video
+ * modes made by Sony. The main complication is that these values are the
+ * basis to compute arbitrary top, bottom, left and right display margin
+ * (border) settings. This is useful to for example precisely center the
+ * image for a given analogue video display.
+ *
+ * The VESA modes are designed to work with the synch-on-green (SOG) VGA cable
+ * that Sony distributed with the Linux kit for the PlayStation 2. Modern
+ * computer displays typically do not support synch-on-green, so an adapter
+ * is most likely necessary for these modes.
+ *
+ * Return: Graphics Synthesizer VESA display mode synchronisation parameters
+ */
+static struct gs_sync_param vm_to_sp_vesa(
+	const struct fb_videomode *vm, const struct gs_synch_gen sg)
+{
+	const u32 spml  = sg.spml;
+	const u32 t1248 = sg.t1248;
+	const u32 lc    = sg.lc;
+	const u32 rc    = sg.rc;
+	const u32 hadj  = spml / 2;
+	const u32 vhp   = (vm->vmode & FB_VMODE_INTERLACED) ? 0 : 1;
+	const u32 hb    = vm->xres * spml * 3 / 5;
+
+	return (struct gs_sync_param) {
+		.smode1 = {
+			.vhp    = vhp, .vcksel =  0, .slck2 =     1, .nvck = 1,
+			.clksel =   1, .pevs   =  0, .pehs  =     0, .pvs  = 0,
+			.phs    =   0, .gcont  =  0, .spml  =  spml, .pck2 = 0,
+			.xpck   =   0, .sint   =  1, .prst  =     0, .ex   = 0,
+			.cmod   =   0, .slck   =  0, .t1248 = t1248,
+			.lc     =  lc, .rc     = rc
+		},
+		.smode2 = {
+			.intm = (vm->vmode & FB_VMODE_INTERLACED) ? 1 : 0
+		},
+		.srfsh = {
+			.rfsh = gs_rfsh_from_synch_gen(sg)
+		},
+		.synch1 = {
+			.hs   = vm->hsync_len * spml,
+			.hsvs = (vm->left_margin + vm->xres +
+				 vm->right_margin - vm->hsync_len) * spml / 2,
+			.hseq = vm->hsync_len * spml,
+			.hbp  = vm->left_margin * spml - hadj,
+			.hfp  = vm->right_margin * spml + hadj
+		},
+		.synch2 = {
+			.hb = hb,
+			.hf = vm->xres * spml - hb
+		},
+		.syncv = {
+			.vs   = vm->vsync_len,
+			.vdp  = vm->yres,
+			.vbpe = 0,
+			.vbp  = vm->upper_margin,
+			.vfpe = 0,
+			.vfp  = vm->lower_margin
+		},
+		.display = {
+			.dh   = vm->yres - 1,
+			.dw   = vm->xres * spml - 1,
+			.magv = 0,
+			.magh = spml - 1,
+			.dy   = vm->vsync_len + vm->upper_margin - 1,
+			.dx   = (vm->hsync_len + vm->left_margin) * spml - 1 - hadj
+		}
+	};
+}
+
 static struct gs_sync_param vm_to_sp_for_synch_gen(
 	const struct fb_videomode *vm, const struct gs_synch_gen sg)
 {
@@ -641,7 +716,8 @@ static struct gs_sync_param vm_to_sp_for_synch_gen(
 	struct gs_sync_param sp =
 		vm->yres <= 288 &&       bc ? vm_to_sp_sdtv(vm) :
 		vm->yres <= 576 && il && bc ? vm_to_sp_sdtv(vm) :
-					      vm_to_sp_hdtv(vm, sg);
+					 bc ? vm_to_sp_hdtv(vm, sg) :
+					      vm_to_sp_vesa(vm, sg);
 
 	sp.smode1.gcont = gs_gcont_ycrcb;
 	sp.smode1.sint = 1;
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 098/120] MIPS: PS2: FB: Preconfigure standard PAL, NTSC and VESA display modes
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (96 preceding siblings ...)
  2019-09-01 16:31 ` [PATCH 097/120] MIPS: PS2: FB: fb_set_par() VESA computer display mode support Fredrik Noring
@ 2019-09-01 16:31 ` Fredrik Noring
  2019-09-01 16:31 ` [PATCH 099/120] MIPS: PS2: FB: Reset the Graphics Synthesizer drawing environment Fredrik Noring
                   ` (22 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:31 UTC (permalink / raw)
  To: linux-mips

These display mode configurations are a combination of modes made by
Sony[1] and modes from the PlayStation 3 frame buffer driver (ps3fb.c).

The VESA modes are designed to work with the synch-on-green (SOG) VGA
cable that Sony distributed with the Linux kit for the PlayStation 2.
Modern computer displays typically do not support synch-on-green, so
an adapter is most likely necessary for these modes.

References:

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/video/fbdev/ps2fb.c | 110 ++++++++++++++++++++++++++++++++++++
 1 file changed, 110 insertions(+)

diff --git a/drivers/video/fbdev/ps2fb.c b/drivers/video/fbdev/ps2fb.c
index dd8b468ab72b..a28263474665 100644
--- a/drivers/video/fbdev/ps2fb.c
+++ b/drivers/video/fbdev/ps2fb.c
@@ -156,6 +156,91 @@ struct gs_sync_param {
 	struct gs_display display;
 };
 
+static const struct fb_videomode standard_modes[] = {
+	/* PAL */
+	{ "256p", 50, 640, 256, 74074, 100, 61, 34, 22, 63, 2,
+	  FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED, FB_MODE_IS_STANDARD },
+	{ "288p", 50, 720, 288, 74074, 70, 11, 19, 3, 63, 3,
+	  FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED, FB_MODE_IS_STANDARD },
+	{ "512i", 50, 640, 512, 74074, 100, 61, 67, 41, 63, 5,
+	  FB_SYNC_BROADCAST, FB_VMODE_INTERLACED, FB_MODE_IS_STANDARD },
+	{ "576i", 50, 720, 576, 74074, 70, 11, 39, 5, 63, 5,
+	  FB_SYNC_BROADCAST, FB_VMODE_INTERLACED, FB_MODE_IS_STANDARD },
+	{ "576p", 50, 720, 576, 37037, 70, 11, 39, 5, 63, 5,
+	  FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED, FB_MODE_IS_STANDARD },
+	{ "720p", 50, 1280, 720, 13468, 220, 400, 19, 6, 80, 5,
+	  FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED, FB_MODE_IS_STANDARD },
+	{ "1080i", 50, 1920, 1080, 13468, 148, 484, 36, 4, 88, 5,
+	  FB_SYNC_BROADCAST, FB_VMODE_INTERLACED, FB_MODE_IS_STANDARD },
+	{ "1080p", 50, 1920, 1080, 6734, 148, 484, 36, 4, 88, 5,
+	  FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED, FB_MODE_IS_STANDARD },
+
+	/* PAL with borders to ensure that the whole screen is visible */
+	{ "460i", 50, 576, 460, 74074, 142, 83, 97, 63, 63, 5,
+	  FB_SYNC_BROADCAST, FB_VMODE_INTERLACED },
+	{ "460p", 50, 576, 460, 37037, 142, 83, 97, 63, 63, 5,
+	  FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED },
+	{ "644p", 50, 1124, 644, 13468, 298, 478, 57, 44, 80, 5,
+	  FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED },
+	{ "964i", 50, 1688, 964, 13468, 264, 600, 94, 62, 88, 5,
+	  FB_SYNC_BROADCAST, FB_VMODE_INTERLACED },
+	{ "964p", 50, 1688, 964, 6734, 264, 600, 94, 62, 88, 5,
+	  FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED },
+
+	/* NTSC */
+	{ "224p", 60, 640, 224, 74074, 95, 60, 22, 14, 63, 3,
+	  FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED, FB_MODE_IS_STANDARD },
+	{ "240p", 60, 720, 240, 74074, 58, 17, 15, 5, 63, 3,
+	  FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED, FB_MODE_IS_STANDARD },
+	{ "448i", 60, 640, 448, 74074, 95, 60, 44, 27, 63, 6,
+	  FB_SYNC_BROADCAST, FB_VMODE_INTERLACED, FB_MODE_IS_STANDARD },
+	{ "480i", 60, 720, 480, 74074, 58, 17, 30, 9, 63, 6,
+	  FB_SYNC_BROADCAST, FB_VMODE_INTERLACED, FB_MODE_IS_STANDARD },
+	{ "480p", 60, 720, 480, 37037, 58, 17, 30, 9, 63, 6,
+	  FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED, FB_MODE_IS_STANDARD },
+	{ "720p", 60, 1280, 720, 13481, 220, 70, 19, 6, 80, 5,
+	  FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED, FB_MODE_IS_STANDARD },
+	{ "1080i", 60, 1920, 1080, 13481, 148, 44, 36, 4, 88, 5,
+	  FB_SYNC_BROADCAST, FB_VMODE_INTERLACED, FB_MODE_IS_STANDARD },
+	{ "1080p", 60, 1920, 1080, 6741, 148, 44, 36, 4, 88, 5,
+	  FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED, FB_MODE_IS_STANDARD },
+
+	/* NTSC with borders to ensure that the whole screen is visible */
+	{ "384i", 60, 576, 384, 74074, 130, 89, 78, 57, 63, 6,
+	  FB_SYNC_BROADCAST, FB_VMODE_INTERLACED },
+	{ "384p", 60, 576, 384, 37037, 130, 89, 78, 57, 63, 6,
+	  FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED },
+	{ "644p", 60, 1124, 644, 13481, 298, 148, 57, 44, 80, 5,
+	  FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED },
+	{ "964i", 60, 1688, 964, 13481, 264, 160, 94, 62, 88, 5,
+	  FB_SYNC_BROADCAST, FB_VMODE_INTERLACED },
+	{ "964p", 60, 1688, 964, 6741, 264, 160, 94, 62, 88, 5,
+	  FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED },
+
+	/* VESA */
+	{ "vesa-1a", 60, 640, 480, 39682,  48, 16, 33, 10, 96, 2,
+	  0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+	{ "vesa-1c", 75, 640, 480, 31746, 120, 16, 16, 1, 64, 3,
+	  0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+	{ "vesa-2b", 60, 800, 600, 25000, 88, 40, 23, 1, 128, 4,
+	  FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	  FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+	{ "vesa-2d", 75, 800, 600, 20202, 160, 16, 21, 1, 80, 3,
+	  FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	  FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+	{ "vesa-3b", 60, 1024, 768, 15384, 160, 24, 29, 3, 136, 6,
+	  0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+	{ "vesa-3d", 75, 1024, 768, 12690, 176, 16, 28, 1, 96, 3,
+	  FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	  FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+	{ "vesa-4a", 60, 1280, 1024, 9259, 248, 48, 38, 1, 112, 3,
+	  FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	  FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+	{ "vesa-4b", 75, 1280, 1024, 7407, 248, 16, 38, 1, 144, 3,
+	  FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	  FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }
+};
+
 /**
  * var_to_fbw - frame buffer width for a given virtual x resolution
  * @var: screen info object to compute FBW for
@@ -829,6 +914,17 @@ static u32 block_dimensions(u32 dim, u32 alignment)
 	return mask;
 }
 
+static void fill_modes(struct device *dev, struct list_head *head)
+{
+	int i;
+
+	INIT_LIST_HEAD(head);
+
+	for (i = 0; i < ARRAY_SIZE(standard_modes); i++)
+		if (fb_add_videomode(&standard_modes[i], head) < 0)
+			dev_err(dev, "fb_add_videomode failed\n");
+}
+
 static int init_console_buffer(struct platform_device *pdev,
 	struct fb_info *info)
 {
@@ -897,6 +993,8 @@ static int ps2fb_probe(struct platform_device *pdev)
 	}
 	par->package.capacity = PAGE_SIZE / sizeof(union package);
 
+	fill_modes(&pdev->dev, &info->modelist);
+
 	strlcpy(info->fix.id, "PS2 GS", ARRAY_SIZE(info->fix.id));
 	info->fix.accel = FB_ACCEL_PLAYSTATION_2;
 
@@ -904,6 +1002,17 @@ static int ps2fb_probe(struct platform_device *pdev)
 	if (err < 0)
 		goto err_init_buffer;
 
+	fb_info(info, "Mode option is \"%s\"\n", mode_option);
+
+	info->var = (struct fb_var_screeninfo) { };
+	if (!fb_find_mode(&info->var, info, mode_option,
+			standard_modes, ARRAY_SIZE(standard_modes), NULL, 32)) {
+		fb_err(info, "Failed to find video mode \"%s\"\n",
+			mode_option);
+		err = -EINVAL;
+		goto err_find_mode;
+	}
+
 	info->mode = &par->mode;
 
 	if (register_framebuffer(info) < 0) {
@@ -917,6 +1026,7 @@ static int ps2fb_probe(struct platform_device *pdev)
 	return 0;
 
 err_register_framebuffer:
+err_find_mode:
 err_init_buffer:
 	free_page((unsigned long)par->package.buffer);
 err_package_buffer:
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 099/120] MIPS: PS2: FB: Reset the Graphics Synthesizer drawing environment
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (97 preceding siblings ...)
  2019-09-01 16:31 ` [PATCH 098/120] MIPS: PS2: FB: Preconfigure standard PAL, NTSC and VESA display modes Fredrik Noring
@ 2019-09-01 16:31 ` Fredrik Noring
  2019-09-01 16:32 ` [PATCH 100/120] MIPS: PS2: FB: Clear the display buffer when changing video modes Fredrik Noring
                   ` (21 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:31 UTC (permalink / raw)
  To: linux-mips

Various parameters are used to draw Graphics Synthesizer primitives, for
example texture information and drawing attributes set by the PRIM
register[1]. These parameters are called the drawing environment. The
environment remains in effect for multiple primitives until it is reset.

Some environment registers such as XYOFFSET_1 and XYOFFSET_2 represent
the same function and can be chosen with the CTXT primitive attribute.

This driver is currently only using the first drawing environment.

References:

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/video/fbdev/ps2fb.c | 198 +++++++++++++++++++++++++++++++++++-
 1 file changed, 197 insertions(+), 1 deletion(-)

diff --git a/drivers/video/fbdev/ps2fb.c b/drivers/video/fbdev/ps2fb.c
index a28263474665..90ddcbe27d43 100644
--- a/drivers/video/fbdev/ps2fb.c
+++ b/drivers/video/fbdev/ps2fb.c
@@ -59,6 +59,11 @@
 
 #define PALETTE_BLOCK_COUNT 1	/* One block is used for the indexed colors */
 
+#define GIF_PACKAGE_TAG(package) ((package)++)->gif.tag = (struct gif_tag)
+#define GIF_PACKAGE_REG(package) ((package)++)->gif.reg = (struct gif_data_reg)
+#define GIF_PACKAGE_AD(package)  ((package)++)->gif.packed.ad = (struct gif_packed_ad)
+#define DMA_PACKAGE_TAG(package) ((package)++)->dma = (struct dma_tag)
+
 /* Module parameters */
 static char *mode_option;
 
@@ -361,6 +366,194 @@ static u32 display_buffer_size(const u32 xres_virtual, const u32 yres_virtual,
 	return (xres_virtual * yres_virtual * bits_per_pixel) / 8;
 }
 
+/**
+ * struct environment - Graphics Synthesizer drawing environment context
+ * @xres: x resolution in pixels
+ * @yres: y resolution in pixels
+ * @fbw: frame buffer width in 64-pixel unit
+ * @psm: pixel storage mode
+ * @fbp: frame buffer base pointer in 2048-pixel unit
+ */
+struct environment {
+	u32 xres;
+	u32 yres;
+	u32 fbw;
+	enum gs_psm psm;
+	u32 fbp;
+};
+
+/**
+ * var_to_env - Graphics Synthesizer drawing environment for a given video mode
+ * @var: screen info object
+ * @info: frame buffer info object
+ *
+ * Return: Graphics Synthesizer drawing environment parameters
+ */
+static struct environment var_to_env(const struct fb_var_screeninfo *var,
+	const struct fb_info *info)
+{
+	return (struct environment) {
+		.xres = var->xres,
+		.yres = var->yres,
+		.fbw  = var_to_fbw(var),
+		.psm  = var_to_psm(var, info)
+	};
+}
+
+/**
+ * package_environment - package drawing environment tags and data for the GIF
+ * @package: DMA buffer to put packages in
+ * @env: drawing environment to package
+ *
+ * Various parameters are used to draw Graphics Synthesizer primitives, for
+ * example texture information and drawing attributes set by the PRIM register.
+ * These parameters are called the drawing environment. The environment remains
+ * in effect for multiple primitives until it is reset.
+ *
+ * Some environment registers such as XYOFFSET_1 and XYOFFSET_2 represent the
+ * same function and can be chosen with the CTXT primitive attribute.
+ *
+ * The following registers are available:
+ *
+ * ============ =============================================================
+ * XYOFFSET_1/2 offset value of vertex coordinates
+ * PRMODECONT   PRIM attributes enabled/disabled
+ * TEX0_1/2     attributes of texture buffer and texture mapping
+ * TEX1_1/2     attributes of texture mapping
+ * TEX2_1/2     colour lookup table entry
+ * CLAMP_1/2    wrap mode of texture mapping
+ * TEXCLUT      colour lookup table setting
+ * SCANMSK      drawing control with y coordinate of pixel
+ * MIPTBP1_1/2  base pointer for MIPMAP on each level
+ * MIPTBP2_1/2  base pointer for MIPMAP on each level
+ * TEXA         reference value when expanding alpha value of TEX16 and TEX24
+ * FOGCOL       fogging distant color
+ * SCISSOR_1/2  scissoring area
+ * ALPHA_1/2    alpha-blending attributes
+ * DIMX         dither matrix
+ * DTHE         dithering enabled/disabled
+ * COLCLAMP     color clamp/mask
+ * TEST_1/2     pixel operation
+ * PABE         alpha-blending in pixel units enabled/disabled
+ * FBA_1/2      alpha correction value
+ * FRAME_1/2    frame buffer setting
+ * ZBUF_1/2     z buffer setting
+ * ============ =============================================================
+ *
+ * Return: number of generated GIF packages in 16-byte unit
+ */
+static size_t package_environment(union package *package,
+	const struct environment env)
+{
+	union package * const base_package = package;
+
+	GIF_PACKAGE_TAG(package) {
+		.flg = gif_packed_mode,
+		.reg0 = gif_reg_ad,
+		.nreg = 1,
+		.nloop = 11
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_frame_1,
+		.data.frame_1 = {
+			.fbw = env.fbw,
+			.fbp = env.fbp,
+			.psm = env.psm
+		}
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_xyoffset_1,
+		.data.xyoffset_1 = {
+			.ofx = 0,
+			.ofy = 0,
+		}
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_scissor_1,
+		.data.scissor_1 = {
+			.scax0 = 0, .scax1 = env.xres,
+			.scay0 = 0, .scay1 = env.yres
+		}
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_scanmsk,
+		.data.scanmsk = {
+			.msk = gs_scanmsk_normal
+		}
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_prmode,
+		.data.prmode = { }	/* Reset PRMODE to a known value */
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_prmodecont,
+		.data.prmodecont = {
+			.ac = 1
+		}
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_test_1,
+		.data.test_1 = {
+			.zte  = gs_depth_test_on,	/* Must always be ON */
+			.ztst = gs_depth_pass		/* Emulate OFF */
+		}
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_texa,
+		.data.texa = {
+			.ta0 = GS_ALPHA_ONE,
+			.aem = gs_aem_normal,
+			.ta1 = GS_ALPHA_ONE
+		}
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_tex1_1,
+		.data.tex1 = {
+			.lcm = gs_lcm_fixed,
+			.mmag = gs_lod_nearest,
+			.mmin = gs_lod_nearest,
+			.k = 0
+		}
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_zbuf_1,
+		.data.zbuf = {
+			.zmsk = gs_zbuf_off
+		}
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_dthe,
+		.data.dthe = {
+			.dthe = gs_dthe_off
+		}
+	};
+
+	return package - base_package;
+}
+
+/**
+ * write_cb_environment - write console buffer GS drawing environment to the GIF
+ * @info: frame buffer info object
+ *
+ * Write various parameters used to draw Graphics Synthesizer primitives, for
+ * example texture information and drawing attributes set by the PRIM register.
+ * The environment remains in effect for multiple primitives until it is reset.
+ */
+void write_cb_environment(struct fb_info *info)
+{
+        if (gif_wait()) {
+		struct ps2fb_par *par = info->par;
+		union package * const base_package = par->package.buffer;
+		union package *package = base_package;
+
+		package += package_environment(package,
+			var_to_env(&info->var, info));
+
+		gif_write(&base_package->gif, package - base_package);
+	} else
+		fb_err(info, "Failed to write GS environment, GIF is busy\n");
+}
+
 /**
  * ps2fb_cb_get_tilemax - maximum number of tiles
  * @info: frame buffer info object
@@ -889,9 +1082,12 @@ static int ps2fb_cb_set_par(struct fb_info *info)
 	spin_lock_irqsave(&par->lock, flags);
 
 	err = ps2fb_set_par(info);
-	if (!err)
+	if (!err) {
 		par->cb.block_count = var_to_block_count(info);
 
+		write_cb_environment(info);
+	}
+
 	spin_unlock_irqrestore(&par->lock, flags);
 
 	if (!err && info->tileops)
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 100/120] MIPS: PS2: FB: Clear the display buffer when changing video modes
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (98 preceding siblings ...)
  2019-09-01 16:31 ` [PATCH 099/120] MIPS: PS2: FB: Reset the Graphics Synthesizer drawing environment Fredrik Noring
@ 2019-09-01 16:32 ` Fredrik Noring
  2019-09-01 16:32 ` [PATCH 101/120] MIPS: PS2: FB: fb_setcolreg() 256 colour pseudo palette support Fredrik Noring
                   ` (20 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:32 UTC (permalink / raw)
  To: linux-mips

This avoids displaying leftover graphics that appear as garbage from a
previous mode.

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/video/fbdev/ps2fb.c | 45 +++++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/drivers/video/fbdev/ps2fb.c b/drivers/video/fbdev/ps2fb.c
index 90ddcbe27d43..e5e603db7671 100644
--- a/drivers/video/fbdev/ps2fb.c
+++ b/drivers/video/fbdev/ps2fb.c
@@ -572,6 +572,49 @@ static int ps2fb_cb_get_tilemax(struct fb_info *info)
 	return blocks_available > 0 ? blocks_available * block_tile_count : 0;
 }
 
+/**
+ * clear_screen - clear the displayed buffer screen
+ * @info: frame buffer info object
+ */
+static void clear_screen(struct fb_info *info)
+{
+	struct ps2fb_par *par = info->par;
+	union package * const base_package = par->package.buffer;
+	union package *package = base_package;
+
+	if (!gif_wait()) {
+		fb_err(info, "Failed to clear the screen, GIF is busy\n");
+		return;
+	}
+
+	GIF_PACKAGE_TAG(package) {
+		.flg = gif_reglist_mode,
+		.reg0 = gif_reg_prim,
+		.reg1 = gif_reg_rgbaq,
+		.reg2 = gif_reg_xyz2,
+		.reg3 = gif_reg_xyz2,
+		.nreg = 4,
+		.nloop = 1,
+		.eop = 1
+	};
+	GIF_PACKAGE_REG(package) {
+		.lo.prim = { .prim = gs_sprite },
+		.hi.rgbaq = { .a = GS_ALPHA_ONE }
+	};
+	GIF_PACKAGE_REG(package) {
+		.lo.xyz2 = {
+			.x = gs_fbcs_to_pcs(0),
+			.y = gs_fbcs_to_pcs(0)
+		},
+		.hi.xyz2 = {
+			.x = gs_fbcs_to_pcs(info->var.xres_virtual),
+			.y = gs_fbcs_to_pcs(info->var.yres_virtual)
+		}
+	};
+
+	gif_write(&base_package->gif, package - base_package);
+}
+
 /**
  * bits_per_pixel_fits - does the given resolution fit the given buffer size?
  * @xres_virtual: virtual x resolution in pixels
@@ -1086,6 +1129,8 @@ static int ps2fb_cb_set_par(struct fb_info *info)
 		par->cb.block_count = var_to_block_count(info);
 
 		write_cb_environment(info);
+
+		clear_screen(info);
 	}
 
 	spin_unlock_irqrestore(&par->lock, flags);
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 101/120] MIPS: PS2: FB: fb_setcolreg() 256 colour pseudo palette support
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (99 preceding siblings ...)
  2019-09-01 16:32 ` [PATCH 100/120] MIPS: PS2: FB: Clear the display buffer when changing video modes Fredrik Noring
@ 2019-09-01 16:32 ` Fredrik Noring
  2019-09-01 16:32 ` [PATCH 102/120] MIPS: PS2: FB: fb_settile() with font stored as palette indexed textures Fredrik Noring
                   ` (19 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:32 UTC (permalink / raw)
  To: linux-mips

The Graphics Synthesizer display buffer is either 16-bit or 32-bit true
colour. The font will be represented by 4-bit palette indexed texture
tiles in a forthcoming change.

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/video/fbdev/ps2fb.c | 57 +++++++++++++++++++++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/drivers/video/fbdev/ps2fb.c b/drivers/video/fbdev/ps2fb.c
index e5e603db7671..c852341ec19a 100644
--- a/drivers/video/fbdev/ps2fb.c
+++ b/drivers/video/fbdev/ps2fb.c
@@ -57,6 +57,7 @@
 
 #define DEVICE_NAME "ps2fb"
 
+#define PALETTE_SIZE 256
 #define PALETTE_BLOCK_COUNT 1	/* One block is used for the indexed colors */
 
 #define GIF_PACKAGE_TAG(package) ((package)++)->gif.tag = (struct gif_tag)
@@ -122,6 +123,8 @@ struct console_buffer {
 /**
  * struct ps2fb_par - driver specific structure
  * @lock: spin lock to be taken for all structure operations
+ * @mode: frame buffer video mode
+ * @pseudo_palette: pseudo palette, used for texture colouring
  * @cb: console buffer definition
  * @package: tags and datafor the GIF
  * @package.capacity: maximum number of GIF packages in 16-byte unit
@@ -130,6 +133,9 @@ struct console_buffer {
 struct ps2fb_par {
 	spinlock_t lock;
 
+	struct fb_videomode mode;
+	struct gs_rgba32 pseudo_palette[PALETTE_SIZE];
+
 	struct console_buffer cb;
 
 	struct {
@@ -572,6 +578,44 @@ static int ps2fb_cb_get_tilemax(struct fb_info *info)
 	return blocks_available > 0 ? blocks_available * block_tile_count : 0;
 }
 
+/**
+ * invalidate_palette - set invalid palette indices to force update
+ * @par: driver specific object
+ *
+ * The background and foreground palette indices will mismatch in the next
+ * tile file, and thereby force a palette update.
+ */
+static void invalidate_palette(struct ps2fb_par *par)
+{
+	par->cb.bg = ~0;
+	par->cb.fg = ~0;
+}
+
+static int ps2fb_setcolreg(unsigned regno, unsigned red, unsigned green,
+	unsigned blue, unsigned transp, struct fb_info *info)
+{
+	const struct gs_rgba32 color = {
+		.r = red    >> 8,
+		.g = green  >> 8,
+		.b = blue   >> 8,
+		.a = transp >> 8
+	};
+	struct ps2fb_par *par = info->par;
+	unsigned long flags;
+
+	if (regno >= PALETTE_SIZE)
+		return -EINVAL;
+
+	spin_lock_irqsave(&par->lock, flags);
+
+	par->pseudo_palette[regno] = color;
+	invalidate_palette(par);
+
+	spin_unlock_irqrestore(&par->lock, flags);
+
+	return 0;
+}
+
 /**
  * clear_screen - clear the displayed buffer screen
  * @info: frame buffer info object
@@ -1076,6 +1120,7 @@ static int ps2fb_set_par(struct fb_info *info)
 	struct gs_smode1 smode1 = sp.smode1;
 
 	par->mode = vm;
+	invalidate_palette(par);
 
 	info->fix.type = FB_TYPE_PACKED_PIXELS;
 	info->fix.visual = FB_VISUAL_TRUECOLOR;
@@ -1171,6 +1216,7 @@ static int init_console_buffer(struct platform_device *pdev,
 {
 	static struct fb_ops fbops = {
 		.owner		= THIS_MODULE,
+		.fb_setcolreg	= ps2fb_setcolreg,
 		.fb_set_par	= ps2fb_cb_set_par,
 		.fb_check_var	= ps2fb_cb_check_var,
 	};
@@ -1203,6 +1249,8 @@ static int init_console_buffer(struct platform_device *pdev,
 	info->pixmap.blit_x = block_dimensions(GS_PSMT4_BLOCK_WIDTH, 4);
 	info->pixmap.blit_y = block_dimensions(GS_PSMT4_BLOCK_HEIGHT, 1);
 
+	info->pseudo_palette = par->pseudo_palette;
+
 	/* 8x8 default font tile size for fb_get_tilemax */
 	par->cb.tile = cb_tile(8, 8);
 
@@ -1256,6 +1304,13 @@ static int ps2fb_probe(struct platform_device *pdev)
 
 	info->mode = &par->mode;
 
+	if (fb_alloc_cmap(&info->cmap, PALETTE_SIZE, 0) < 0) {
+		fb_err(info, "fb_alloc_cmap failed\n");
+		err = -ENOMEM;
+		goto err_alloc_cmap;
+	}
+	fb_set_cmap(&info->cmap, info);
+
 	if (register_framebuffer(info) < 0) {
 		fb_err(info, "register_framebuffer failed\n");
 		err = -EINVAL;
@@ -1267,6 +1322,8 @@ static int ps2fb_probe(struct platform_device *pdev)
 	return 0;
 
 err_register_framebuffer:
+	fb_dealloc_cmap(&info->cmap);
+err_alloc_cmap:
 err_find_mode:
 err_init_buffer:
 	free_page((unsigned long)par->package.buffer);
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 102/120] MIPS: PS2: FB: fb_settile() with font stored as palette indexed textures
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (100 preceding siblings ...)
  2019-09-01 16:32 ` [PATCH 101/120] MIPS: PS2: FB: fb_setcolreg() 256 colour pseudo palette support Fredrik Noring
@ 2019-09-01 16:32 ` Fredrik Noring
  2019-09-01 16:32 ` [PATCH 103/120] MIPS: PS2: FB: Hardware accelerated fb_tilecopy() support Fredrik Noring
                   ` (18 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:32 UTC (permalink / raw)
  To: linux-mips

Storing the font map locally in Graphics Synthesizer memory is very
efficient when drawing. A 4-bit palette indexed texture is used so
that both the background and foreground colours can be updated without
reinstalling the font map.

Before using converted data from host-to-local or local-to-local
transmissions as texture or colour lookup tables for the first time,
the texture buffer must be disabled with the TEXFLUSH register.

The register write waits for the completion of the current drawing
process, and then disables the texture data read to the texture page
buffer. Any data can be written to it. A drawing process succeeding
the write to the register is started after the texture page buffer
is disabled[1].

References:

[1] "GS User's Manual", version 6.0, Sony Computer Entertainment Inc.,
    pp. 43, 51, 55, 63, 77, 131.

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/video/fbdev/ps2fb.c | 285 ++++++++++++++++++++++++++++++++++++
 1 file changed, 285 insertions(+)

diff --git a/drivers/video/fbdev/ps2fb.c b/drivers/video/fbdev/ps2fb.c
index c852341ec19a..2f7477dc69c1 100644
--- a/drivers/video/fbdev/ps2fb.c
+++ b/drivers/video/fbdev/ps2fb.c
@@ -320,6 +320,47 @@ static u32 var_to_block_count(const struct fb_info *info)
 	return 0;
 }
 
+/**
+ * block_address_for_index - frame buffer block address for given block index
+ * @block_index: index of block to compute the address of
+ * @info: frame buffer info object
+ *
+ * Return: block address, or zero for unsupported pixel storage modes
+ */
+static u32 block_address_for_index(const u32 block_index,
+	const struct fb_info *info)
+{
+	const struct fb_var_screeninfo *var = &info->var;
+	const enum gs_psm psm = var_to_psm(var, info);
+	const u32 fbw = var_to_fbw(var);
+
+	if (psm == gs_psm_ct16)
+		return gs_psm_ct16_block_address(fbw, block_index);
+	if (psm == gs_psm_ct32)
+		return gs_psm_ct32_block_address(fbw, block_index);
+
+	fb_warn_once(info, "%s: Unsupported pixel storage format %u\n",
+		__func__, psm);
+	return 0;
+}
+
+/**
+ * texture_address_for_index - frame buffer texture address for given index
+ * @block_index: index of block to compute the address of
+ * @info: frame buffer info object
+ *
+ * Return: texture address, or zero for unsupported pixel storage modes
+ */
+static u32 texture_address_for_index(const u32 block_index,
+	const struct fb_info *info)
+{
+	const struct ps2fb_par *par = info->par;
+
+	return block_address_for_index(
+		par->cb.block_count + PALETTE_BLOCK_COUNT + block_index,
+		info);
+}
+
 /**
  * texture_least_power_of_2 - round up to a power of 2, not less than 8
  * @n: integer to round up
@@ -357,6 +398,34 @@ static struct cb_tile cb_tile(u32 width, u32 height)
 	};
 }
 
+/**
+ * texture_for_tile - texture base pointer and texel coordinates for tile index
+ * @tile_index: index of tile to compute the texture for
+ * @info: frame buffer info object
+ *
+ * Returns texture base pointer and texel coordinates
+ */
+static struct tile_texture texture_for_tile(const u32 tile_index,
+	const struct fb_info *info)
+{
+	const struct ps2fb_par *par = info->par;
+
+	const u32 texture_tile_count =
+		par->cb.tile.block.cols * par->cb.tile.block.rows;
+	const u32 block_tile = tile_index / texture_tile_count;
+	const u32 texture_tile = tile_index % texture_tile_count;
+	const u32 block_address = texture_address_for_index(block_tile, info);
+
+	const u32 row = texture_tile / par->cb.tile.block.cols;
+	const u32 col = texture_tile % par->cb.tile.block.cols;
+
+	return (struct tile_texture) {
+		.tbp	= block_address,
+		.u	= col * par->cb.tile.width2,
+		.v	= row * par->cb.tile.height2
+	};
+}
+
 /**
  * display_buffer_size - display buffer size for a given video resolution
  *
@@ -560,6 +629,221 @@ void write_cb_environment(struct fb_info *info)
 		fb_err(info, "Failed to write GS environment, GIF is busy\n");
 }
 
+/**
+ * pixel - background or foreground pixel palette index for given coordinates
+ * @image: image to sample pixel from
+ * @x: x coordinate, relative to the top left corner
+ * @y: y coordinate, relative to the top left corner
+ * @info: frame buffer info object
+ *
+ * The background palette index is given for coordinates outside of the image.
+ *
+ * Return: background or foreground palette index
+ */
+static u32 pixel(const struct fb_image * const image,
+	const int x, const int y, struct fb_info *info)
+{
+	if (x < 0 || x >= image->width ||
+	    y < 0 || y >= image->height)
+		return image->bg_color;
+
+	if (image->depth == 1)
+		return (image->data[y*((image->width + 7) >> 3) + (x >> 3)] &
+			(0x80 >> (x & 0x7))) ?
+			image->fg_color : image->bg_color;
+
+	fb_warn_once(info, "%s: Unsupported image depth %u\n",
+		__func__, image->depth);
+	return 0;
+}
+
+/**
+ * ps2fb_cb_texflush - flush texture buffer after palette or texture updates
+ * @info: frame buffer info object
+ *
+ * Before using converted data from host-to-local or local-to-local
+ * transmissions as texture or colour lookup tables for the first time, the
+ * texture buffer must be disabled with the TEXFLUSH register.
+ *
+ * The register write waits for the completion of the current drawing process,
+ * and then disables the texture data read to the texture page buffer. Any
+ * data can be written to it. A drawing process succeeding the write to the
+ * register is started after the texture page buffer is disabled.
+ */
+static void ps2fb_cb_texflush(struct fb_info *info)
+{
+	struct ps2fb_par *par = info->par;
+	union package * const base_package = par->package.buffer;
+	union package *package = base_package;
+	unsigned long flags;
+
+	if (info->state != FBINFO_STATE_RUNNING)
+		return;
+
+	spin_lock_irqsave(&par->lock, flags);
+
+        if (!gif_wait())
+		goto timeout;
+
+	GIF_PACKAGE_TAG(package) {
+		.flg = gif_packed_mode,
+		.reg0 = gif_reg_ad,
+		.nreg = 1,
+		.nloop = 1
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_texflush
+	};
+
+	gif_write(&base_package->gif, package - base_package);
+
+timeout:
+	spin_unlock_irqrestore(&par->lock, flags);
+}
+
+/**
+ * package_psmt4_texture - package PSMT4 texture tags and data for the GIF
+ * @package: DMA buffer to put packages in
+ * @image: image to copy
+ * @info: frame buffer info object
+ *
+ * Return: number of generated GIF packages in 16-byte unit
+ */
+static size_t package_psmt4_texture(union package *package,
+	const struct fb_image *image, struct fb_info *info)
+{
+	union package * const base_package = package;
+	const u32 width2 = texture_least_power_of_2(image->width);
+	const u32 height2 = texture_least_power_of_2(image->height);
+	const u32 texels_per_quadword = 32;	/* PSMT4 are 4 bit texels */
+	const u32 nloop = (width2 * height2 + texels_per_quadword - 1) /
+		texels_per_quadword;
+	u32 x, y;
+
+	GIF_PACKAGE_TAG(package) {
+		.flg = gif_image_mode,
+		.nloop = nloop,
+		.eop = 1
+	};
+
+	for (y = 0; y < height2; y++)
+	for (x = 0; x < width2; x += 2) {
+		const int p0 = pixel(image, x + 0, y, info);
+		const int p1 = pixel(image, x + 1, y, info);
+		const int i = 4*y + x/2;
+
+		package[i/16].gif.image[i%16] =
+			(p1 ? 0x10 : 0) | (p0 ? 0x01 : 0);
+	}
+
+	package += nloop;
+
+	return package - base_package;
+}
+
+/**
+ * write_cb_tile - write console buffer tile to the GIF
+ * @tile_index: index of the tile, starting from zero for the first glyph
+ * @image: the image of the tile to write
+ * @info: frame buffer info object
+ */
+static void write_cb_tile(const int tile_index,
+	const struct fb_image *image, struct fb_info *info)
+{
+	struct ps2fb_par *par = info->par;
+	const struct tile_texture tt = texture_for_tile(tile_index, info);
+	union package * const base_package = par->package.buffer;
+	union package *package = base_package;
+
+        if (!gif_wait())
+		return;
+
+	GIF_PACKAGE_TAG(package) {
+		.flg = gif_packed_mode,
+		.reg0 = gif_reg_ad,
+		.nreg = 1,
+		.nloop = 4
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_bitbltbuf,
+		.data.bitbltbuf = {
+			.dpsm = gs_psm_t4,
+			.dbw = GS_PSMT4_BLOCK_WIDTH / 64,
+			.dbp = tt.tbp
+		}
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_trxpos,
+		.data.trxpos = {
+			.dsax = tt.u,
+			.dsay = tt.v
+		}
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_trxreg,
+		.data.trxreg = {
+			.rrw = texture_least_power_of_2(image->width),
+			.rrh = texture_least_power_of_2(image->height)
+		}
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_trxdir,
+		.data.trxdir = { .xdir = gs_trxdir_host_to_local }
+	};
+
+	package += package_psmt4_texture(package, image, info);
+
+	gif_write(&base_package->gif, package - base_package);
+}
+
+/**
+ * ps2fb_cb_settile - set console buffer font tiles
+ * @info: frame buffer info object
+ * @map: font map use as tiles
+*/
+static void ps2fb_cb_settile(struct fb_info *info, struct fb_tilemap *map)
+{
+	const u32 glyph_size = ALIGN(map->width, 8) * map->height / 8;
+	struct ps2fb_par *par = info->par;
+	const u8 *font = map->data;
+	int i;
+
+	if (!font)
+		return;	/* FIXME: Why is fb_settile called with a NULL font? */
+
+	if (info->state != FBINFO_STATE_RUNNING)
+		return;
+
+	if (map->width > GS_PSMT4_BLOCK_WIDTH ||
+	    map->height > GS_PSMT4_BLOCK_HEIGHT ||
+	    map->depth != 1) {
+		fb_err(info, "Unsupported font parameters: "
+			"width %d height %d depth %d length %d\n",
+			map->width, map->height, map->depth, map->length);
+		return;
+	}
+
+	par->cb.tile = cb_tile(map->width, map->height);
+
+	for (i = 0; i < map->length; i++) {
+		const struct fb_image image = {
+			.width = map->width,
+			.height = map->height,
+			.fg_color = 1,
+			.bg_color = 0,
+			.depth = 1,
+			.data = &font[i * glyph_size],
+		};
+		unsigned long flags;
+
+		spin_lock_irqsave(&par->lock, flags);
+		write_cb_tile(i, &image, info);
+		spin_unlock_irqrestore(&par->lock, flags);
+	}
+
+	ps2fb_cb_texflush(info);
+}
+
 /**
  * ps2fb_cb_get_tilemax - maximum number of tiles
  * @info: frame buffer info object
@@ -1222,6 +1506,7 @@ static int init_console_buffer(struct platform_device *pdev,
 	};
 
 	static struct fb_tile_ops tileops = {
+		.fb_settile	= ps2fb_cb_settile,
 		.fb_get_tilemax = ps2fb_cb_get_tilemax
 	};
 
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 103/120] MIPS: PS2: FB: Hardware accelerated fb_tilecopy() support
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (101 preceding siblings ...)
  2019-09-01 16:32 ` [PATCH 102/120] MIPS: PS2: FB: fb_settile() with font stored as palette indexed textures Fredrik Noring
@ 2019-09-01 16:32 ` Fredrik Noring
  2019-09-01 16:33 ` [PATCH 104/120] MIPS: PS2: FB: Hardware accelerated fb_tilefill() support Fredrik Noring
                   ` (17 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:32 UTC (permalink / raw)
  To: linux-mips

In local-to-local BITBLTBUF transmissions, the following restrictions
to the TRXREG register width field apply depending on the pixel storage
mode[1]:

- multiple of 2: PSMCT32, PSMZ32
- multiple of 4: PSMCT16, PSMCT16S, PSMZ16, PSMZ16S
- multiple of 8: PSMCT24, PSMZ24, PSMT8, PSMT8H, PSMT4, PSMT4HL, PSMT4HH

References:

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/video/fbdev/ps2fb.c | 148 ++++++++++++++++++++++++++++++++++++
 1 file changed, 148 insertions(+)

diff --git a/drivers/video/fbdev/ps2fb.c b/drivers/video/fbdev/ps2fb.c
index 2f7477dc69c1..2123686af013 100644
--- a/drivers/video/fbdev/ps2fb.c
+++ b/drivers/video/fbdev/ps2fb.c
@@ -426,6 +426,33 @@ static struct tile_texture texture_for_tile(const u32 tile_index,
 	};
 }
 
+/**
+  * valid_bitbltbuf_width - is the BITBLTBUF width valid?
+  * @width: width in pixels to check
+  * @psm: pixel storage mode
+  *
+  * In local-to-local BITBLTBUF transmissions, the following restrictions
+  * to the TRXREG register width field apply depending on the pixel storage
+  * mode[1]:
+  *
+  * - multiple of 2: PSMCT32, PSMZ32
+  * - multiple of 4: PSMCT16, PSMCT16S, PSMZ16, PSMZ16S
+  * - multiple of 8: PSMCT24, PSMZ24, PSMT8, PSMT8H, PSMT4, PSMT4HL, PSMT4HH
+  *
+  * Return: %true if the given width is valid, otherwise %false
+  */
+static bool valid_bitbltbuf_width(int width, enum gs_psm psm)
+{
+	if (width < 1)
+		return false;
+	if (psm == gs_psm_ct32 && (width & 1) != 0)
+		return false;
+	if (psm == gs_psm_ct16 && (width & 3) != 0)
+		return false;
+
+	return true;
+}
+
 /**
  * display_buffer_size - display buffer size for a given video resolution
  *
@@ -629,6 +656,103 @@ void write_cb_environment(struct fb_info *info)
 		fb_err(info, "Failed to write GS environment, GIF is busy\n");
 }
 
+/**
+ * package_copyarea - package copy area tags and data for the GIF
+ * @package: DMA buffer to put packages in
+ * @area: area to copy
+ * @info: frame buffer info object
+ *
+ * Return: number of generated GIF packages in 16-byte unit
+ */
+static size_t package_copyarea(union package *package,
+	const struct fb_copyarea *area, const struct fb_info *info)
+{
+	const struct fb_var_screeninfo *var = &info->var;
+	union package * const base_package = package;
+	const int psm = var_to_psm(var, info);
+	const int fbw = var_to_fbw(var);
+
+	GIF_PACKAGE_TAG(package) {
+		.flg = gif_packed_mode,
+		.reg0 = gif_reg_ad,
+		.nreg = 1,
+		.nloop = 4
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_bitbltbuf,
+		.data.bitbltbuf = {
+			.spsm = psm, .sbw = fbw,
+			.dpsm = psm, .dbw = fbw
+		}
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_trxpos,
+		.data.trxpos = {
+			.ssax = area->sx, .ssay = area->sy,
+			.dsax = area->dx, .dsay = area->dy,
+			.dir  = area->dy < area->sy ||
+				(area->dy == area->sy && area->dx < area->sx) ?
+				gs_trxpos_dir_ul_lr : gs_trxpos_dir_lr_ul
+		}
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_trxreg,
+		.data.trxreg = {
+			.rrw = area->width,
+			.rrh = area->height
+		}
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_trxdir,
+		.data.trxdir = { .xdir = gs_trxdir_local_to_local }
+	};
+
+	return package - base_package;
+}
+
+/**
+ * ps2fb_cb_copyarea - copy console buffer area
+ * @area: area to copy
+ * @info: frame buffer info object
+ */
+void ps2fb_cb_copyarea(const struct fb_copyarea *area, struct fb_info *info)
+{
+	const enum gs_psm psm = var_to_psm(&info->var, info);
+	struct ps2fb_par *par = info->par;
+	unsigned long flags;
+
+	if (info->state != FBINFO_STATE_RUNNING)
+		return;
+	if (area->width < 1 || area->height < 1)
+		return;
+	if (!valid_bitbltbuf_width(area->width, psm)) {
+		/*
+		 * Some widths are not entirely supported with BITBLTBUF,
+		 * but there will be more graphical glitches by refusing
+		 * to proceed. Besides, info->pixmap.blit_x says that
+		 * they are unsupported so unless someone wants to have
+		 * odd fonts we will not end up here anyway. Warn once
+		 * here for the protocol.
+		 */
+		fb_warn_once(info, "%s: "
+			"Unsupported width %u for pixel storage format %u\n",
+			__func__, area->width, psm);
+	}
+
+	spin_lock_irqsave(&par->lock, flags);
+
+        if (gif_wait()) {
+		union package * const base_package = par->package.buffer;
+		union package *package = base_package;
+
+		package += package_copyarea(package, area, info);
+
+		gif_write(&base_package->gif, package - base_package);
+	}
+
+	spin_unlock_irqrestore(&par->lock, flags);
+}
+
 /**
  * pixel - background or foreground pixel palette index for given coordinates
  * @image: image to sample pixel from
@@ -844,6 +968,28 @@ static void ps2fb_cb_settile(struct fb_info *info, struct fb_tilemap *map)
 	ps2fb_cb_texflush(info);
 }
 
+/**
+ * ps2fb_cb_tilecopy - copy console buffer tiles
+ * @info: frame buffer info object
+ * @area: tile area to copy
+ */
+static void ps2fb_cb_tilecopy(struct fb_info *info, struct fb_tilearea *area)
+{
+	const struct ps2fb_par *par = info->par;
+	const u32 tw = par->cb.tile.width;
+	const u32 th = par->cb.tile.height;
+	const struct fb_copyarea a = {
+		.dx	= tw * area->dx,
+		.dy	= th * area->dy,
+		.width	= tw * area->width,
+		.height	= th * area->height,
+		.sx	= tw * area->sx,
+		.sy	= th * area->sy
+	};
+
+	ps2fb_cb_copyarea(&a, info);
+}
+
 /**
  * ps2fb_cb_get_tilemax - maximum number of tiles
  * @info: frame buffer info object
@@ -1507,6 +1653,7 @@ static int init_console_buffer(struct platform_device *pdev,
 
 	static struct fb_tile_ops tileops = {
 		.fb_settile	= ps2fb_cb_settile,
+		.fb_tilecopy	= ps2fb_cb_tilecopy,
 		.fb_get_tilemax = ps2fb_cb_get_tilemax
 	};
 
@@ -1522,6 +1669,7 @@ static int init_console_buffer(struct platform_device *pdev,
 
 	info->fbops = &fbops;
 	info->flags = FBINFO_DEFAULT |
+		      FBINFO_HWACCEL_COPYAREA |
 		      FBINFO_READS_FAST;
 
 	info->flags |= FBINFO_MISC_TILEBLITTING;
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 104/120] MIPS: PS2: FB: Hardware accelerated fb_tilefill() support
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (102 preceding siblings ...)
  2019-09-01 16:32 ` [PATCH 103/120] MIPS: PS2: FB: Hardware accelerated fb_tilecopy() support Fredrik Noring
@ 2019-09-01 16:33 ` Fredrik Noring
  2019-09-01 16:33 ` [PATCH 105/120] MIPS: PS2: FB: Simplified fb_tileblit() support Fredrik Noring
                   ` (16 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:33 UTC (permalink / raw)
  To: linux-mips

The local Graphics Synthesizer palette is updated if either the
background or the foreground colour index have changed. A single
BITBLTBUF operation performs the tile fill.

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/video/fbdev/ps2fb.c | 233 ++++++++++++++++++++++++++++++++++++
 1 file changed, 233 insertions(+)

diff --git a/drivers/video/fbdev/ps2fb.c b/drivers/video/fbdev/ps2fb.c
index 2123686af013..c00c0f6a4a49 100644
--- a/drivers/video/fbdev/ps2fb.c
+++ b/drivers/video/fbdev/ps2fb.c
@@ -252,6 +252,23 @@ static const struct fb_videomode standard_modes[] = {
 	  FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }
 };
 
+/**
+ * console_pseudo_palette - Graphics Synthesizer RGBA for a palette index
+ * @regno: pseudo palette index number
+ * @par: driver specific object
+ *
+ * Return: RGBA colour object for the Graphics Synthesizer
+ */
+static struct gs_rgbaq console_pseudo_palette(const u32 regno,
+	const struct ps2fb_par *par)
+{
+	const struct gs_rgba32 c = regno < PALETTE_SIZE ?
+		par->pseudo_palette[regno] : (struct gs_rgba32) { };
+	const u32 a = (c.a + 1) / 2;	/* 0x80 = GS_ALPHA_ONE = 1.0 */
+
+	return (struct gs_rgbaq) { .r = c.r, .g = c.g, .b = c.b, .a = a };
+}
+
 /**
  * var_to_fbw - frame buffer width for a given virtual x resolution
  * @var: screen info object to compute FBW for
@@ -344,6 +361,19 @@ static u32 block_address_for_index(const u32 block_index,
 	return 0;
 }
 
+/**
+ * color_base_pointer - colour base pointer
+ * @info: frame buffer info object
+ *
+ * Return: block index of the colour palette, that follows the display buffer
+ */
+static u32 color_base_pointer(const struct fb_info *info)
+{
+	const struct ps2fb_par *par = info->par;
+
+	return par->cb.block_count;
+}
+
 /**
  * texture_address_for_index - frame buffer texture address for given index
  * @block_index: index of block to compute the address of
@@ -968,6 +998,187 @@ static void ps2fb_cb_settile(struct fb_info *info, struct fb_tilemap *map)
 	ps2fb_cb_texflush(info);
 }
 
+/**
+ * package_palette - package palette tags and data for the GIF
+ * @package: DMA buffer to put packages in
+ * @bg: background colour palette index
+ * @fg: foreground colour palette index
+ * @info: frame buffer info object
+ *
+ * Return: number of generated GIF packages in 16-byte unit
+ */
+static size_t package_palette(union package *package,
+	const int bg, const int fg, struct fb_info *info)
+{
+	struct ps2fb_par *par = info->par;
+	union package * const base_package = par->package.buffer;
+	const struct gs_rgbaq bg_rgbaq = console_pseudo_palette(bg, par);
+	const struct gs_rgbaq fg_rgbaq = console_pseudo_palette(fg, par);
+
+	GIF_PACKAGE_TAG(package) {
+		.flg = gif_packed_mode,
+		.reg0 = gif_reg_ad,
+		.nreg = 1,
+		.nloop = 4
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_bitbltbuf,
+		.data.bitbltbuf = {
+			.dpsm = gs_psm_ct32,
+			.dbw = 1,	/* Palette is one block wide */
+			.dbp = color_base_pointer(info)
+		}
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_trxpos,
+		.data.trxpos = {
+			.dsax = 0,
+			.dsay = 0
+		}
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_trxreg,
+		.data.trxreg = {
+			.rrw = 2,	/* Background and foreground color */
+			.rrh = 1
+		}
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_trxdir,
+		.data.trxdir = { .xdir = gs_trxdir_host_to_local }
+	};
+
+	GIF_PACKAGE_TAG(package) {
+		.flg = gif_image_mode,
+		.nloop = 1,
+		.eop = 1
+	};
+	package->gif.rgba32[0] = (struct gs_rgba32) {
+		.r = bg_rgbaq.r,
+		.g = bg_rgbaq.g,
+		.b = bg_rgbaq.b,
+		.a = bg_rgbaq.a
+	};
+	package->gif.rgba32[1] = (struct gs_rgba32) {
+		.r = fg_rgbaq.r,
+		.g = fg_rgbaq.g,
+		.b = fg_rgbaq.b,
+		.a = fg_rgbaq.a
+	};
+	package++;
+
+	GIF_PACKAGE_TAG(package) {
+		.flg = gif_packed_mode,
+		.reg0 = gif_reg_ad,
+		.nreg = 1,
+		.nloop = 1
+	};
+	GIF_PACKAGE_AD(package) {
+		.addr = gs_addr_texflush
+	};
+
+	return package - base_package;
+}
+
+/**
+ * write_tilefill - write console buffer tile fill operation to the GIF
+ * @info: frame buffer info object
+ * @rect: rectangle to fill with tiles
+ */
+static void write_tilefill(struct fb_info *info, const struct fb_tilerect rect)
+{
+	const struct tile_texture tt = texture_for_tile(rect.index, info);
+	struct ps2fb_par *par = info->par;
+	union package * const base_package = par->package.buffer;
+	union package *package = base_package;
+	const u32 cbp = color_base_pointer(info);
+	const u32 dsax = par->cb.tile.width * rect.sx;
+	const u32 dsay = par->cb.tile.height * rect.sy;
+	const u32 rrw = par->cb.tile.width * rect.width;
+	const u32 rrh = par->cb.tile.height * rect.height;
+	const u32 tw2 = par->cb.tile.width2;
+	const u32 th2 = par->cb.tile.height2;
+
+	/* Determine whether background or foreground color needs update. */
+	const bool cld = (par->cb.bg != rect.bg || par->cb.fg != rect.fg);
+
+        if (!gif_wait())
+		return;
+
+	if (cld) {
+		package += package_palette(package, rect.bg, rect.fg, info);
+		par->cb.bg = rect.bg;
+		par->cb.fg = rect.fg;
+	}
+
+	GIF_PACKAGE_TAG(package) {
+		.flg = gif_reglist_mode,
+		.reg0 = gif_reg_prim,
+		.reg1 = gif_reg_nop,
+		.reg2 = gif_reg_tex0_1,
+		.reg3 = gif_reg_clamp_1,
+		.reg4 = gif_reg_uv,
+		.reg5 = gif_reg_xyz2,
+		.reg6 = gif_reg_uv,
+		.reg7 = gif_reg_xyz2,
+		.nreg = 8,
+		.nloop = 1,
+		.eop = 1
+	};
+	GIF_PACKAGE_REG(package) {
+		.lo.prim = {
+			.prim = gs_sprite,
+			.tme = gs_texturing_on,
+			.fst = gs_texturing_uv
+		}
+	};
+	GIF_PACKAGE_REG(package) {
+		.lo.tex0 = {
+			.tbp0 = tt.tbp,
+			.tbw = GS_PSMT4_BLOCK_WIDTH / 64,
+			.psm = gs_psm_t4,
+			.tw = 5,	/* 2^5 = 32 texels wide PSMT4 block */
+			.th = 4,	/* 2^4 = 16 texels high PSMT4 block */
+			.tcc = gs_tcc_rgba,
+			.tfx = gs_tfx_decal,
+			.cbp = cbp,
+			.cpsm = gs_psm_ct32,
+			.csm = gs_csm1,
+			.cld = cld ? 1 : 0
+		},
+		.hi.clamp_1 = {
+			.wms = gs_clamp_region_repeat,
+			.wmt = gs_clamp_region_repeat,
+			.minu = tw2 - 1,  /* Mask, tw is always a power of 2 */
+			.maxu = tt.u,
+			.minv = th2 - 1,  /* Mask, th is always a power of 2 */
+			.maxv = tt.v
+		}
+	};
+	GIF_PACKAGE_REG(package) {
+		.lo.uv = {
+			.u = gs_pxcs_to_tcs(tt.u),
+			.v = gs_pxcs_to_tcs(tt.v)
+		},
+		.hi.xyz2 = {
+			.x = gs_fbcs_to_pcs(dsax),
+			.y = gs_fbcs_to_pcs(dsay)
+		}
+	};
+	GIF_PACKAGE_REG(package) {
+		.lo.uv = {
+			.u = gs_pxcs_to_tcs(tt.u + rrw),
+			.v = gs_pxcs_to_tcs(tt.v + rrh)
+		},
+		.hi.xyz2 = {
+			.x = gs_fbcs_to_pcs(dsax + rrw),
+			.y = gs_fbcs_to_pcs(dsay + rrh)
+		}
+	};
+
+	gif_write(&base_package->gif, package - base_package);
+}
+
 /**
  * ps2fb_cb_tilecopy - copy console buffer tiles
  * @info: frame buffer info object
@@ -990,6 +1201,26 @@ static void ps2fb_cb_tilecopy(struct fb_info *info, struct fb_tilearea *area)
 	ps2fb_cb_copyarea(&a, info);
 }
 
+/**
+ * ps2fb_cb_tilefill - tile fill operation
+ * @info: frame buffer info object
+ * @rect: rectangle to fill with tiles
+ */
+static void ps2fb_cb_tilefill(struct fb_info *info, struct fb_tilerect *rect)
+{
+	struct ps2fb_par *par = info->par;
+	unsigned long flags;
+
+	if (info->state != FBINFO_STATE_RUNNING)
+		return;
+
+	spin_lock_irqsave(&par->lock, flags);
+
+	write_tilefill(info, *rect);
+
+	spin_unlock_irqrestore(&par->lock, flags);
+}
+
 /**
  * ps2fb_cb_get_tilemax - maximum number of tiles
  * @info: frame buffer info object
@@ -1654,6 +1885,7 @@ static int init_console_buffer(struct platform_device *pdev,
 	static struct fb_tile_ops tileops = {
 		.fb_settile	= ps2fb_cb_settile,
 		.fb_tilecopy	= ps2fb_cb_tilecopy,
+		.fb_tilefill    = ps2fb_cb_tilefill,
 		.fb_get_tilemax = ps2fb_cb_get_tilemax
 	};
 
@@ -1670,6 +1902,7 @@ static int init_console_buffer(struct platform_device *pdev,
 	info->fbops = &fbops;
 	info->flags = FBINFO_DEFAULT |
 		      FBINFO_HWACCEL_COPYAREA |
+		      FBINFO_HWACCEL_FILLRECT |
 		      FBINFO_READS_FAST;
 
 	info->flags |= FBINFO_MISC_TILEBLITTING;
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 105/120] MIPS: PS2: FB: Simplified fb_tileblit() support
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (103 preceding siblings ...)
  2019-09-01 16:33 ` [PATCH 104/120] MIPS: PS2: FB: Hardware accelerated fb_tilefill() support Fredrik Noring
@ 2019-09-01 16:33 ` Fredrik Noring
  2019-09-01 16:33 ` [PATCH 106/120] MIPS: PS2: FB: fb_tilecursor() placeholder Fredrik Noring
                   ` (15 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:33 UTC (permalink / raw)
  To: linux-mips

The tile blit operation is implemented as a series of tile fill
operations. This is very simple but not particularly efficient.

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/video/fbdev/ps2fb.c | 39 +++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/drivers/video/fbdev/ps2fb.c b/drivers/video/fbdev/ps2fb.c
index c00c0f6a4a49..c63215efd07d 100644
--- a/drivers/video/fbdev/ps2fb.c
+++ b/drivers/video/fbdev/ps2fb.c
@@ -1221,6 +1221,43 @@ static void ps2fb_cb_tilefill(struct fb_info *info, struct fb_tilerect *rect)
 	spin_unlock_irqrestore(&par->lock, flags);
 }
 
+/**
+ * ps2fb_cb_tileblit - tile bit block transfer operation
+ * @info: frame buffer info object
+ * @blit: tile bit block to transfer
+ */
+static void ps2fb_cb_tileblit(struct fb_info *info, struct fb_tileblit *blit)
+{
+	struct ps2fb_par *par = info->par;
+	int i = 0, dx, dy;
+
+	if (info->state != FBINFO_STATE_RUNNING)
+		return;
+
+	/*
+	 * Note: This could be done faster, possibly often as a single
+	 * set of GIF packages.
+	 */
+	for (dy = 0; i < blit->length && dy < blit->height; dy++)
+	for (dx = 0; i < blit->length && dx < blit->width; dx++, i++) {
+		unsigned long flags;
+
+		spin_lock_irqsave(&par->lock, flags);
+
+		write_tilefill(info, (struct fb_tilerect) {
+			.sx = blit->sx + dx,
+			.sy = blit->sy + dy,
+			.width = 1,
+			.height = 1,
+			.index = blit->indices[i],
+			.fg = blit->fg,
+			.bg = blit->bg
+		});
+
+		spin_unlock_irqrestore(&par->lock, flags);
+	}
+}
+
 /**
  * ps2fb_cb_get_tilemax - maximum number of tiles
  * @info: frame buffer info object
@@ -1886,6 +1923,7 @@ static int init_console_buffer(struct platform_device *pdev,
 		.fb_settile	= ps2fb_cb_settile,
 		.fb_tilecopy	= ps2fb_cb_tilecopy,
 		.fb_tilefill    = ps2fb_cb_tilefill,
+		.fb_tileblit    = ps2fb_cb_tileblit,
 		.fb_get_tilemax = ps2fb_cb_get_tilemax
 	};
 
@@ -1903,6 +1941,7 @@ static int init_console_buffer(struct platform_device *pdev,
 	info->flags = FBINFO_DEFAULT |
 		      FBINFO_HWACCEL_COPYAREA |
 		      FBINFO_HWACCEL_FILLRECT |
+		      FBINFO_HWACCEL_IMAGEBLIT |
 		      FBINFO_READS_FAST;
 
 	info->flags |= FBINFO_MISC_TILEBLITTING;
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 106/120] MIPS: PS2: FB: fb_tilecursor() placeholder
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (104 preceding siblings ...)
  2019-09-01 16:33 ` [PATCH 105/120] MIPS: PS2: FB: Simplified fb_tileblit() support Fredrik Noring
@ 2019-09-01 16:33 ` Fredrik Noring
  2019-09-01 16:33 ` [PATCH 107/120] MIPS: PS2: FB: Hardware accelerated fb_pan_display() support Fredrik Noring
                   ` (14 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:33 UTC (permalink / raw)
  To: linux-mips

Drawing the cursor seems to require composition such as xor, which is
not possible here. If the character under the cursor was known, a simple
change of foreground and background colours could be implemented to
achieve the same effect.

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/video/fbdev/ps2fb.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/drivers/video/fbdev/ps2fb.c b/drivers/video/fbdev/ps2fb.c
index c63215efd07d..bbf9e1d9e3c1 100644
--- a/drivers/video/fbdev/ps2fb.c
+++ b/drivers/video/fbdev/ps2fb.c
@@ -1258,6 +1258,17 @@ static void ps2fb_cb_tileblit(struct fb_info *info, struct fb_tileblit *blit)
 	}
 }
 
+static void ps2fb_cb_tilecursor(struct fb_info *info,
+	struct fb_tilecursor *cursor)
+{
+	/*
+	 * FIXME: Drawing the cursor seems to require composition such as
+	 * xor, which is not possible here. If the character under the
+	 * cursor was known, a simple change of foreground and background
+	 * colors could be implemented to achieve the same effect.
+	 */
+}
+
 /**
  * ps2fb_cb_get_tilemax - maximum number of tiles
  * @info: frame buffer info object
@@ -1924,6 +1935,7 @@ static int init_console_buffer(struct platform_device *pdev,
 		.fb_tilecopy	= ps2fb_cb_tilecopy,
 		.fb_tilefill    = ps2fb_cb_tilefill,
 		.fb_tileblit    = ps2fb_cb_tileblit,
+		.fb_tilecursor  = ps2fb_cb_tilecursor,
 		.fb_get_tilemax = ps2fb_cb_get_tilemax
 	};
 
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 107/120] MIPS: PS2: FB: Hardware accelerated fb_pan_display() support
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (105 preceding siblings ...)
  2019-09-01 16:33 ` [PATCH 106/120] MIPS: PS2: FB: fb_tilecursor() placeholder Fredrik Noring
@ 2019-09-01 16:33 ` Fredrik Noring
  2019-09-01 16:34 ` [PATCH 108/120] MIPS: PS2: FB: fb_blank() display power management signaling (DPMS) Fredrik Noring
                   ` (13 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:33 UTC (permalink / raw)
  To: linux-mips

XPAN, YPAN and YWRAP are hardware accelerated. YWRAP is implemented
using the two independent rectangular area read output circuits of the
Graphics Synthesizer[1]. The DISPLAY1 register outputs the upper part
and the DISPLAY2 register outputs the lower part.

References:

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/video/fbdev/ps2fb.c | 120 ++++++++++++++++++++++++++++++++++--
 1 file changed, 116 insertions(+), 4 deletions(-)

diff --git a/drivers/video/fbdev/ps2fb.c b/drivers/video/fbdev/ps2fb.c
index bbf9e1d9e3c1..73fc1fd3f4eb 100644
--- a/drivers/video/fbdev/ps2fb.c
+++ b/drivers/video/fbdev/ps2fb.c
@@ -1368,6 +1368,109 @@ static void clear_screen(struct fb_info *info)
 	gif_write(&base_package->gif, package - base_package);
 }
 
+/**
+ * write_cb_pan_display - write console buffer pan operation to the GIF
+ * @var: screen info object
+ * @info: frame buffer info object
+ *
+ * XPAN, YPAN and YWRAP are hardware accelerated. YWRAP is implemented using
+ * the two independent rectangular area read output circuits of the Graphics
+ * Synthesizer. The DISPLAY1 register outputs the upper part and the DISPLAY2
+ * register outputs the lower part.
+ */
+static void write_cb_pan_display(const struct fb_var_screeninfo *var,
+	const struct fb_info *info)
+{
+	const struct gs_display display = gs_read_display1();
+	const int psm = var_to_psm(var, info);
+	const int fbw = var_to_fbw(var);
+	const int yo = var->yoffset % var->yres_virtual;
+	const int dh1 = min_t(int, var->yres_virtual - yo, var->yres);
+	const int dh2 = var->yres - dh1;
+
+	GS_WRITE_DISPLAY1(
+		.dh = dh1 - 1,
+		.dw = display.dw,
+		.magv = display.magv,
+		.magh = display.magh,
+		.dy = display.dy,
+		.dx = display.dx,
+	);
+
+	GS_WRITE_DISPLAY2(
+		.dh = dh2 - 1,
+		.dw = display.dw,
+		.magv = display.magv,
+		.magh = display.magh,
+		.dy = display.dy + dh1,
+		.dx = display.dx,
+	);
+
+	GS_WRITE_DISPFB1(
+		.fbw = fbw,
+		.psm = psm,
+		.dbx = var->xoffset,
+		.dby = yo
+	);
+
+	GS_WRITE_DISPFB2(
+		.fbw = fbw,
+		.psm = psm,
+		.dbx = var->xoffset,
+		.dby = 0,
+	);
+
+	GS_WRITE_PMODE(
+		.en1 = 1,
+		.en2 = dh2 ? 1 : 0,
+		.crtmd = 1
+	);
+}
+
+/**
+ * changed_cb_pan_display - is display panning needed?
+ * @var: screen info object
+ *
+ * Panning is undefined until the DISPFB1 register has been set.
+ *
+ * Return: %true if the horizontal or vertical panning is needed, otherwise
+ *	%false
+ */
+static bool changed_cb_pan_display(const struct fb_var_screeninfo *var)
+{
+	if (gs_valid_dispfb1()) {
+		const struct gs_dispfb dspfb12 = gs_read_dispfb1();
+		const int yo = var->yoffset % var->yres_virtual;
+
+		return dspfb12.dbx != var->xoffset || dspfb12.dby != yo;
+	}
+
+	return false;	/* DISPFB1 is incomparable before video mode is set. */
+}
+
+/**
+ * ps2fb_cb_pan_display - pan the display
+ * @var: screen info object
+ * @info: frame buffer info object
+ *
+ * Return: 0 on success, otherwise a negative error number
+ */
+static int ps2fb_cb_pan_display(struct fb_var_screeninfo *var,
+	struct fb_info *info)
+{
+	struct ps2fb_par *par = info->par;
+	unsigned long flags;
+
+	spin_lock_irqsave(&par->lock, flags);
+
+	if (changed_cb_pan_display(var))
+		write_cb_pan_display(var, info);
+
+	spin_unlock_irqrestore(&par->lock, flags);
+
+	return 0;
+}
+
 /**
  * bits_per_pixel_fits - does the given resolution fit the given buffer size?
  * @xres_virtual: virtual x resolution in pixels
@@ -1438,8 +1541,11 @@ static int ps2fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
 	    var->yres < 1 || 2048 < var->yres)
 		return -EINVAL;
 
-	var->xres_virtual = var->xres;
-	var->yres_virtual = var->yres;
+        /* Check virtual resolution. */
+        if (var->xres_virtual < var->xres)
+           var->xres_virtual = var->xres;
+        if (var->yres_virtual < var->yres)
+           var->yres_virtual = var->yres;
 	var->xoffset = 0;
 	var->yoffset = 0;
 
@@ -1833,8 +1939,8 @@ static int ps2fb_set_par(struct fb_info *info)
 
 	info->fix.type = FB_TYPE_PACKED_PIXELS;
 	info->fix.visual = FB_VISUAL_TRUECOLOR;
-	info->fix.xpanstep = 0;
-	info->fix.ypanstep = 0;
+	info->fix.xpanstep = var->xres_virtual > var->xres ? 1 : 0;
+	info->fix.ypanstep = var->yres_virtual > var->yres ? 1 : 0;
 	info->fix.ywrapstep = 1;
 	info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
 
@@ -1883,6 +1989,7 @@ static int ps2fb_cb_set_par(struct fb_info *info)
 		par->cb.block_count = var_to_block_count(info);
 
 		write_cb_environment(info);
+		write_cb_pan_display(&info->var, info);
 
 		clear_screen(info);
 	}
@@ -1928,6 +2035,7 @@ static int init_console_buffer(struct platform_device *pdev,
 		.fb_setcolreg	= ps2fb_setcolreg,
 		.fb_set_par	= ps2fb_cb_set_par,
 		.fb_check_var	= ps2fb_cb_check_var,
+		.fb_pan_display	= ps2fb_cb_pan_display,
 	};
 
 	static struct fb_tile_ops tileops = {
@@ -1954,6 +2062,10 @@ static int init_console_buffer(struct platform_device *pdev,
 		      FBINFO_HWACCEL_COPYAREA |
 		      FBINFO_HWACCEL_FILLRECT |
 		      FBINFO_HWACCEL_IMAGEBLIT |
+		      FBINFO_HWACCEL_XPAN |
+		      FBINFO_HWACCEL_YPAN |
+		      FBINFO_HWACCEL_YWRAP |
+		      FBINFO_PARTIAL_PAN_OK |
 		      FBINFO_READS_FAST;
 
 	info->flags |= FBINFO_MISC_TILEBLITTING;
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 108/120] MIPS: PS2: FB: fb_blank() display power management signaling (DPMS)
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (106 preceding siblings ...)
  2019-09-01 16:33 ` [PATCH 107/120] MIPS: PS2: FB: Hardware accelerated fb_pan_display() support Fredrik Noring
@ 2019-09-01 16:34 ` Fredrik Noring
  2019-09-01 16:34 ` [PATCH 109/120] MIPS: PS2: FB: Disable GIF DMA completion interrupts Fredrik Noring
                   ` (12 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:34 UTC (permalink / raw)
  To: linux-mips

The Graphics Synthesizer implements four DPMS levels: on, standby,
suspend and off. The level is set with the SMODE2 register DPMS
field[1].

References:

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

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/video/fbdev/ps2fb.c | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/drivers/video/fbdev/ps2fb.c b/drivers/video/fbdev/ps2fb.c
index 73fc1fd3f4eb..92957797045f 100644
--- a/drivers/video/fbdev/ps2fb.c
+++ b/drivers/video/fbdev/ps2fb.c
@@ -2004,6 +2004,38 @@ static int ps2fb_cb_set_par(struct fb_info *info)
 	return err;
 }
 
+/**
+ * ps2fb_blank - VESA display power management signaling (DPMS)
+ * @blank: frame buffer blanking level
+ * @info: frame buffer info object
+ *
+ * Return: 0 on success, otherwise a negative error number
+ */
+static int ps2fb_blank(int blank, struct fb_info *info)
+{
+	struct ps2fb_par *par = info->par;
+	struct gs_smode2 smode2;
+	unsigned long flags;
+
+	if (!gs_valid_smode2())
+		return -EIO;	/* Blanking before video mode is set. */
+
+	spin_lock_irqsave(&par->lock, flags);
+
+	smode2 = gs_read_smode2();
+
+	smode2.dpms = blank == FB_BLANK_POWERDOWN     ? gs_dpms_off :
+		      blank == FB_BLANK_NORMAL        ? gs_dpms_standby :
+		      blank == FB_BLANK_VSYNC_SUSPEND ? gs_dpms_suspend :
+		      blank == FB_BLANK_HSYNC_SUSPEND ? gs_dpms_suspend :
+							gs_dpms_on;
+	gs_write_smode2(smode2);
+
+	spin_unlock_irqrestore(&par->lock, flags);
+
+	return 0;
+}
+
 static u32 block_dimensions(u32 dim, u32 alignment)
 {
 	u32 mask = 0;
@@ -2032,6 +2064,7 @@ static int init_console_buffer(struct platform_device *pdev,
 {
 	static struct fb_ops fbops = {
 		.owner		= THIS_MODULE,
+		.fb_blank	= ps2fb_blank,
 		.fb_setcolreg	= ps2fb_setcolreg,
 		.fb_set_par	= ps2fb_cb_set_par,
 		.fb_check_var	= ps2fb_cb_check_var,
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 109/120] MIPS: PS2: FB: Disable GIF DMA completion interrupts
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (107 preceding siblings ...)
  2019-09-01 16:34 ` [PATCH 108/120] MIPS: PS2: FB: fb_blank() display power management signaling (DPMS) Fredrik Noring
@ 2019-09-01 16:34 ` Fredrik Noring
  2019-09-01 16:34 ` [PATCH 110/120] MIPS: PS2: FB: PAL and NTSC grayscale support Fredrik Noring
                   ` (11 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:34 UTC (permalink / raw)
  To: linux-mips

The GIF asserts DMA completion interrupts that are uninteresting
because DMA operations are fast enough to busy-wait for.

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/video/fbdev/ps2fb.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/video/fbdev/ps2fb.c b/drivers/video/fbdev/ps2fb.c
index 92957797045f..d7a54108f37e 100644
--- a/drivers/video/fbdev/ps2fb.c
+++ b/drivers/video/fbdev/ps2fb.c
@@ -2173,6 +2173,12 @@ static int ps2fb_probe(struct platform_device *pdev)
 	}
 	fb_set_cmap(&info->cmap, info);
 
+	/*
+	 * The GIF asserts DMA completion interrupts that are uninteresting
+	 * because DMA operations are fast enough to busy-wait for.
+	 */
+	disable_irq(IRQ_DMAC_GIF);
+
 	if (register_framebuffer(info) < 0) {
 		fb_err(info, "register_framebuffer failed\n");
 		err = -EINVAL;
@@ -2184,6 +2190,7 @@ static int ps2fb_probe(struct platform_device *pdev)
 	return 0;
 
 err_register_framebuffer:
+	enable_irq(IRQ_DMAC_GIF);
 	fb_dealloc_cmap(&info->cmap);
 err_alloc_cmap:
 err_find_mode:
@@ -2212,6 +2219,7 @@ static int ps2fb_remove(struct platform_device *pdev)
 		fb_err(info, "Failed to complete GIF DMA transfer\n");
 		err = -EBUSY;
 	}
+	enable_irq(IRQ_DMAC_GIF);
 	free_page((unsigned long)par->package.buffer);
 
 	return err;
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 110/120] MIPS: PS2: FB: PAL and NTSC grayscale support
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (108 preceding siblings ...)
  2019-09-01 16:34 ` [PATCH 109/120] MIPS: PS2: FB: Disable GIF DMA completion interrupts Fredrik Noring
@ 2019-09-01 16:34 ` Fredrik Noring
  2019-09-01 16:34 ` [PATCH 111/120] MIPS: PS2: FB: Analogue display mode adjustment module parameter Fredrik Noring
                   ` (10 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:34 UTC (permalink / raw)
  To: linux-mips

Construct luminance[1] Y' = 0.299R' + 0.587G' + 0.114B' with fixed-point
integer arithmetic, where 77 + 150 + 29 = 256.

References:

[1] "Studio encoding parameters of digital television for standard 4:3
    and wide-screen 16:9 aspect ratios", recommendation ITU-R BT.601-7,
    International Telecommunication Union - Radiocommunication sector,
    p. 2.

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/video/fbdev/ps2fb.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/drivers/video/fbdev/ps2fb.c b/drivers/video/fbdev/ps2fb.c
index d7a54108f37e..4975f9adb5d0 100644
--- a/drivers/video/fbdev/ps2fb.c
+++ b/drivers/video/fbdev/ps2fb.c
@@ -125,6 +125,7 @@ struct console_buffer {
  * @lock: spin lock to be taken for all structure operations
  * @mode: frame buffer video mode
  * @pseudo_palette: pseudo palette, used for texture colouring
+ * @grayscale: perform grayscale colour conversion if %true
  * @cb: console buffer definition
  * @package: tags and datafor the GIF
  * @package.capacity: maximum number of GIF packages in 16-byte unit
@@ -135,6 +136,7 @@ struct ps2fb_par {
 
 	struct fb_videomode mode;
 	struct gs_rgba32 pseudo_palette[PALETTE_SIZE];
+	bool grayscale;
 
 	struct console_buffer cb;
 
@@ -266,6 +268,16 @@ static struct gs_rgbaq console_pseudo_palette(const u32 regno,
 		par->pseudo_palette[regno] : (struct gs_rgba32) { };
 	const u32 a = (c.a + 1) / 2;	/* 0x80 = GS_ALPHA_ONE = 1.0 */
 
+	if (par->grayscale) {
+		/*
+		 * Construct luminance Y' = 0.299R' + 0.587G' + 0.114B' with
+		 * fixed-point integer arithmetic, where 77 + 150 + 29 = 256.
+		 */
+		const u32 y = (c.r*77 + c.g*150 + c.b*29) >> 8;
+
+		return (struct gs_rgbaq) { .r = y, .g = y, .b = y, .a = a };
+	}
+
 	return (struct gs_rgbaq) { .r = c.r, .g = c.g, .b = c.b, .a = a };
 }
 
@@ -1935,6 +1947,7 @@ static int ps2fb_set_par(struct fb_info *info)
 	struct gs_smode1 smode1 = sp.smode1;
 
 	par->mode = vm;
+	par->grayscale = (var->grayscale == 1);
 	invalidate_palette(par);
 
 	info->fix.type = FB_TYPE_PACKED_PIXELS;
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 111/120] MIPS: PS2: FB: Analogue display mode adjustment module parameter
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (109 preceding siblings ...)
  2019-09-01 16:34 ` [PATCH 110/120] MIPS: PS2: FB: PAL and NTSC grayscale support Fredrik Noring
@ 2019-09-01 16:34 ` Fredrik Noring
  2019-09-01 16:35 ` [PATCH 112/120] USB: OHCI: Support for the PlayStation 2 Fredrik Noring
                   ` (9 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:34 UTC (permalink / raw)
  To: linux-mips

Analogue devices are frequently a few pixels off. Use this mode_margin
option to make necessary device dependent adjustments to the built-in
modes.

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/video/fbdev/ps2fb.c | 58 ++++++++++++++++++++++++++++++++++---
 1 file changed, 54 insertions(+), 4 deletions(-)

diff --git a/drivers/video/fbdev/ps2fb.c b/drivers/video/fbdev/ps2fb.c
index 4975f9adb5d0..121c271aa826 100644
--- a/drivers/video/fbdev/ps2fb.c
+++ b/drivers/video/fbdev/ps2fb.c
@@ -67,6 +67,7 @@
 
 /* Module parameters */
 static char *mode_option;
+static char *mode_margin = "";
 
 union package {
 	union gif_data gif;
@@ -1923,9 +1924,45 @@ static struct gs_sync_param vm_to_sp(const struct fb_videomode *vm)
 	return vm_to_sp_for_synch_gen(vm, gs_synch_gen_for_vck(vm->pixclock));
 }
 
+/**
+ * struct margin_adjustment - move display screen relative given display mode
+ * @dx: positive, negative or zero horizontal x adjustment in pixels
+ * @dy: positive, negative or zero vertical y adjustment in pixels
+ */
+struct margin_adjustment {
+	int dx;
+	int dy;
+};
+
+static struct margin_adjustment margin_adjustment(struct fb_info *info)
+{
+	char sx = '+', sy = '+';
+	int dx = 0, dy = 0;
+
+	bool valid =
+		sscanf(mode_margin, "%c%d%c%d", &sx, &dx, &sy, &dy) == 4 &&
+		(sx == '-' || sx == '+') &&
+		(sy == '-' || sy == '+');
+
+	if (sx == '-')
+		dx = -dx;
+	if (sy == '-')
+		dy = -dy;
+
+	if (mode_margin[0] != '\0')
+		fb_info(info, "Mode margin \"%s\" with %d dx %d dy is %s\n",
+			mode_margin, dx, dy, valid ? "valid" : "invalid");
+
+	if (!valid)
+		return (struct margin_adjustment) { };
+
+	return (struct margin_adjustment) { .dx = dx, .dy = dy };
+}
+
 static int ps2fb_set_par(struct fb_info *info)
 {
 	struct ps2fb_par *par = info->par;
+	const struct margin_adjustment margin_adjust = margin_adjustment(info);
 	const struct fb_var_screeninfo *var = &info->var;
 	const struct fb_videomode *mm = fb_match_mode(var, &info->modelist);
 	const struct fb_videomode vm = (struct fb_videomode) {
@@ -1933,10 +1970,10 @@ static int ps2fb_set_par(struct fb_info *info)
 		.xres         = var->xres,
 		.yres         = var->yres,
 		.pixclock     = var->pixclock,
-		.left_margin  = var->left_margin,
-		.right_margin = var->right_margin,
-		.upper_margin = var->upper_margin,
-		.lower_margin = var->lower_margin,
+		.left_margin  = var->left_margin  + margin_adjust.dx,
+		.right_margin = var->right_margin - margin_adjust.dx,
+		.upper_margin = var->upper_margin + margin_adjust.dy,
+		.lower_margin = var->lower_margin - margin_adjust.dy,
 		.hsync_len    = var->hsync_len,
 		.vsync_len    = var->vsync_len,
 		.sync         = var->sync,
@@ -2198,6 +2235,9 @@ static int ps2fb_probe(struct platform_device *pdev)
 		goto err_register_framebuffer;
 	}
 
+	/* Clear the mode adjustment after setting the initial mode. */
+	mode_margin = "";
+
 	platform_set_drvdata(pdev, info);
 
 	return 0;
@@ -2269,6 +2309,8 @@ static int __init ps2fb_init(void)
 			mode_option = &this_opt[12];
 		else if ('0' <= this_opt[0] && this_opt[0] <= '9')
 			mode_option = this_opt;
+		else if (!strncmp(this_opt, "mode_margin:", 12))
+			mode_margin = &this_opt[12];
 		else
 			pr_warn(DEVICE_NAME ": Unrecognized option \"%s\"\n",
 				this_opt);
@@ -2307,6 +2349,14 @@ module_param(mode_option, charp, 0);
 MODULE_PARM_DESC(mode_option,
 	"Specify initial video mode as \"<xres>x<yres>[-<bpp>][@<refresh>]\"");
 
+/*
+ * Analogue devices are frequently a few pixels off. Use this mode_margin
+ * option to make necessary device dependent adjustments to the built-in modes.
+ */
+module_param(mode_margin, charp, 0);
+MODULE_PARM_DESC(mode_margin,
+	"Adjust initial video mode margin as \"<-|+><dx><-|+><dy>\"");
+
 MODULE_DESCRIPTION("PlayStation 2 frame buffer driver");
 MODULE_AUTHOR("Fredrik Noring");
 MODULE_LICENSE("GPL");
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 112/120] USB: OHCI: Support for the PlayStation 2
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (110 preceding siblings ...)
  2019-09-01 16:34 ` [PATCH 111/120] MIPS: PS2: FB: Analogue display mode adjustment module parameter Fredrik Noring
@ 2019-09-01 16:35 ` Fredrik Noring
  2019-09-01 16:35 ` [PATCH 113/120] USB: OHCI: OHCI_INTR_MIE workaround for freeze on " Fredrik Noring
                   ` (8 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:35 UTC (permalink / raw)
  To: linux-mips

Support the USB 1.1 OHCI for the PlayStation 2. The controller is
connected to the input/output processor (IOP) which is separate from
the main (R5900) processor that runs the kernel.

IOP DMA memory and OHCI registers are mapped into kernel address space.
OHCI interrupts are asserted on the IOP and forwarded to the kernel by
an IOP IRQ relay module.

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/usb/host/Kconfig    |   8 ++
 drivers/usb/host/Makefile   |   1 +
 drivers/usb/host/ohci-ps2.c | 245 ++++++++++++++++++++++++++++++++++++
 3 files changed, 254 insertions(+)
 create mode 100644 drivers/usb/host/ohci-ps2.c

diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 40b5de597112..5b3b138006cd 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -552,6 +552,14 @@ config USB_OHCI_EXYNOS
 	help
 	 Enable support for the Samsung Exynos SOC's on-chip OHCI controller.
 
+config USB_OHCI_HCD_PS2
+	tristate "OHCI support for the Sony PlayStation 2"
+	depends on SONY_PS2
+	select GENERIC_ALLOCATOR
+	default y
+	help
+	 Enable support for the Sony PlayStation 2 OHCI controller.
+
 config USB_CNS3XXX_OHCI
 	bool "Cavium CNS3XXX OHCI Module (DEPRECATED)"
 	depends on ARCH_CNS3XXX
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 84514f71ae44..9ed4e0fa6657 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -69,6 +69,7 @@ obj-$(CONFIG_USB_OHCI_HCD_S3C2410)	+= ohci-s3c2410.o
 obj-$(CONFIG_USB_OHCI_HCD_LPC32XX)	+= ohci-nxp.o
 obj-$(CONFIG_USB_OHCI_HCD_PXA27X)	+= ohci-pxa27x.o
 obj-$(CONFIG_USB_OHCI_HCD_DAVINCI)	+= ohci-da8xx.o
+obj-$(CONFIG_USB_OHCI_HCD_PS2)	+= ohci-ps2.o
 
 obj-$(CONFIG_USB_UHCI_HCD)	+= uhci-hcd.o
 obj-$(CONFIG_USB_FHCI_HCD)	+= fhci.o
diff --git a/drivers/usb/host/ohci-ps2.c b/drivers/usb/host/ohci-ps2.c
new file mode 100644
index 000000000000..a1d446313c13
--- /dev/null
+++ b/drivers/usb/host/ohci-ps2.c
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 USB 1.1 OHCI host controller (HCD)
+ *
+ * Copyright (C) 2017 Jürgen Urban
+ * Copyright (C) 2018 Fredrik Noring
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include <asm/mach-ps2/iop-heap.h>
+#include <asm/mach-ps2/iop-memory.h>
+#include <asm/mach-ps2/iop-registers.h>
+
+#include "ohci.h"
+
+#define DRIVER_DESC "PlayStation 2 USB OHCI host controller"
+#define DRV_NAME "ohci-ps2"
+
+/* Size allocated from IOP heap (maximum size of DMA memory). */
+#define DMA_BUFFER_SIZE (256 * 1024)
+
+#define hcd_to_priv(hcd) ((struct ps2_hcd *)(hcd_to_ohci(hcd)->priv))
+
+/**
+ * struct ps2_hcd - private device driver structure
+ * @iop_dma_addr: input/output processor (IOP) DMA buffer address
+ */
+struct ps2_hcd {
+	dma_addr_t iop_dma_addr;
+};
+
+static struct hc_driver __read_mostly ohci_ps2_hc_driver;
+
+static void ohci_ps2_enable(struct usb_hcd *hcd)
+{
+	struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+
+	ohci_writel(ohci, 1, &ohci->regs->roothub.portstatus[11]);
+}
+
+static void ohci_ps2_disable(struct usb_hcd *hcd)
+{
+	struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+
+	ohci_writel(ohci, 0, &ohci->regs->roothub.portstatus[11]);
+}
+
+static void ohci_ps2_start_hc(struct usb_hcd *hcd)
+{
+	iop_set_dma_dpcr2(IOP_DMA_DPCR2_OHCI);
+
+	outw(1, IOP_OHCI_BASE + 0x80);
+}
+
+static void ohci_ps2_stop_hc(struct usb_hcd *hcd)
+{
+	iop_clr_dma_dpcr2(IOP_DMA_DPCR2_OHCI);
+}
+
+static int ohci_ps2_reset(struct usb_hcd *hcd)
+{
+	int err;
+
+	ohci_ps2_start_hc(hcd);
+
+	err = ohci_setup(hcd);
+	if (err) {
+		ohci_ps2_stop_hc(hcd);
+		return err;
+	}
+
+	ohci_ps2_enable(hcd);
+
+	return 0;
+}
+
+static int iopheap_alloc_dma_buffer(struct platform_device *pdev, size_t size)
+{
+	struct device *dev = &pdev->dev;
+	struct usb_hcd *hcd = platform_get_drvdata(pdev);
+	struct ps2_hcd *ps2priv = hcd_to_priv(hcd);
+	int err;
+
+	ps2priv->iop_dma_addr = iop_alloc(size);
+	if (!ps2priv->iop_dma_addr) {
+		dev_err(dev, "iop_alloc failed\n");
+		return -ENOMEM;
+	}
+
+	err = usb_hcd_setup_local_mem(hcd,
+		iop_bus_to_phys(ps2priv->iop_dma_addr),
+		ps2priv->iop_dma_addr, size);
+	if (err) {
+		dev_err(dev, "usb_hcd_setup_local_mem failed with %d\n", err);
+		iop_free(ps2priv->iop_dma_addr);
+		ps2priv->iop_dma_addr = 0;
+		return err;
+	}
+
+	return 0;
+}
+
+static void iopheap_free_dma_buffer(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd = platform_get_drvdata(pdev);
+	struct ps2_hcd *ps2priv = hcd_to_priv(hcd);
+
+	if (!ps2priv->iop_dma_addr)
+		return;
+
+	iop_free(ps2priv->iop_dma_addr);
+	ps2priv->iop_dma_addr = 0;
+}
+
+static int ohci_hcd_ps2_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *regs;
+	struct usb_hcd *hcd;
+	struct ps2_hcd *ps2priv;
+	int irq;
+	int err;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(dev, "platform_get_irq failed\n");
+		return irq;
+	}
+
+	hcd = usb_create_hcd(&ohci_ps2_hc_driver, dev, dev_name(dev));
+	if (!hcd) {
+		dev_err(dev, "usb_create_hcd failed\n");
+		return -ENOMEM;
+	}
+
+	ps2priv = hcd_to_priv(hcd);
+	memset(ps2priv, 0, sizeof(*ps2priv));
+
+	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!regs) {
+		dev_err(dev, "platform_get_resource 0 failed\n");
+		err = -ENOENT;
+		goto err_platform;
+	}
+
+	hcd->rsrc_start = regs->start;
+	hcd->rsrc_len = resource_size(regs);
+	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+	if (IS_ERR(hcd->regs)) {
+		err = PTR_ERR(hcd->regs);
+		dev_err(dev, "ioremap failed with %d\n", err);
+		goto err_ioremap;
+	}
+
+	err = iopheap_alloc_dma_buffer(pdev, DMA_BUFFER_SIZE);
+	if (err)
+		goto err_alloc_dma_buffer;
+
+	err = usb_add_hcd(hcd, irq, IRQF_SHARED);
+	if (err) {
+		dev_err(dev, "usb_add_hcd failed with %d\n", err);
+		goto err_add_hcd;
+	}
+
+	err = device_wakeup_enable(hcd->self.controller);
+	if (err) {
+		dev_err(dev, "device_wakeup_enable failed with %d\n", err);
+		goto err_wakeup;
+	}
+
+	return 0;
+
+err_wakeup:
+	usb_remove_hcd(hcd);
+err_add_hcd:
+	iopheap_free_dma_buffer(pdev);
+err_alloc_dma_buffer:
+	iounmap(hcd->regs);
+err_ioremap:
+err_platform:
+	usb_put_hcd(hcd);
+	return err;
+}
+
+static int ohci_hcd_ps2_remove(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+	usb_remove_hcd(hcd);
+
+	ohci_ps2_disable(hcd);
+	ohci_ps2_stop_hc(hcd);
+
+	iopheap_free_dma_buffer(pdev);
+	iounmap(hcd->regs);
+
+	usb_put_hcd(hcd);
+
+	return 0;
+}
+
+static struct platform_driver ohci_hcd_ps2_driver = {
+	.probe		= ohci_hcd_ps2_probe,
+	.remove		= ohci_hcd_ps2_remove,
+	.shutdown	= usb_hcd_platform_shutdown,
+	.driver		= {
+		.name	= DRV_NAME,
+	},
+};
+
+static const struct ohci_driver_overrides ps2_overrides __initconst = {
+	.reset		= ohci_ps2_reset,
+	.product_desc	= DRIVER_DESC,
+	.extra_priv_size = sizeof(struct ps2_hcd),
+};
+
+static int __init ohci_ps2_init(void)
+{
+	if (usb_disabled()) {
+		pr_err(DRV_NAME ": Initialization failed: USB is disabled\n");
+		return -ENODEV;
+	}
+
+	ohci_init_driver(&ohci_ps2_hc_driver, &ps2_overrides);
+
+	return platform_driver_register(&ohci_hcd_ps2_driver);
+}
+module_init(ohci_ps2_init);
+
+static void __exit ohci_ps2_exit(void)
+{
+	platform_driver_unregister(&ohci_hcd_ps2_driver);
+}
+module_exit(ohci_ps2_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Fredrik Noring");
+MODULE_AUTHOR("Jürgen Urban");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 113/120] USB: OHCI: OHCI_INTR_MIE workaround for freeze on the PlayStation 2
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (111 preceding siblings ...)
  2019-09-01 16:35 ` [PATCH 112/120] USB: OHCI: Support for the PlayStation 2 Fredrik Noring
@ 2019-09-01 16:35 ` " Fredrik Noring
  2019-09-01 16:35 ` [PATCH 114/120] MIPS: PS2: Workaround for unexpected uLaunchELF CP0 Status user mode Fredrik Noring
                   ` (7 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:35 UTC (permalink / raw)
  To: linux-mips

OHCI_INTR_MIE needs to be disabled, most likely due to a hardware bug.
Without it, reading a large amount of data (> 1 GB) from a mass storage
device results in a freeze.

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 drivers/usb/host/ohci-ps2.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/drivers/usb/host/ohci-ps2.c b/drivers/usb/host/ohci-ps2.c
index a1d446313c13..cdbcbe5ff34e 100644
--- a/drivers/usb/host/ohci-ps2.c
+++ b/drivers/usb/host/ohci-ps2.c
@@ -35,6 +35,7 @@ struct ps2_hcd {
 };
 
 static struct hc_driver __read_mostly ohci_ps2_hc_driver;
+static irqreturn_t (*ohci_irq)(struct usb_hcd *hcd);
 
 static void ohci_ps2_enable(struct usb_hcd *hcd)
 {
@@ -79,6 +80,21 @@ static int ohci_ps2_reset(struct usb_hcd *hcd)
 	return 0;
 }
 
+static irqreturn_t ohci_ps2_irq(struct usb_hcd *hcd)
+{
+	struct ohci_hcd	*ohci = hcd_to_ohci(hcd);
+	struct ohci_regs __iomem *regs = ohci->regs;
+
+	/*
+	 * OHCI_INTR_MIE needs to be disabled, most likely due to a
+	 * hardware bug. Without it, reading a large amount of data
+	 * (> 1 GB) from a mass storage device results in a freeze.
+	 */
+	ohci_writel(ohci, OHCI_INTR_MIE, &regs->intrdisable);
+
+	return ohci_irq(hcd);	/* Call normal IRQ handler. */
+}
+
 static int iopheap_alloc_dma_buffer(struct platform_device *pdev, size_t size)
 {
 	struct device *dev = &pdev->dev;
@@ -228,6 +244,9 @@ static int __init ohci_ps2_init(void)
 
 	ohci_init_driver(&ohci_ps2_hc_driver, &ps2_overrides);
 
+	ohci_irq = ohci_ps2_hc_driver.irq;	/* Save normal IRQ handler. */
+	ohci_ps2_hc_driver.irq = ohci_ps2_irq;	/* Install IRQ workaround. */
+
 	return platform_driver_register(&ohci_hcd_ps2_driver);
 }
 module_init(ohci_ps2_init);
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 114/120] MIPS: PS2: Workaround for unexpected uLaunchELF CP0 Status user mode
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (112 preceding siblings ...)
  2019-09-01 16:35 ` [PATCH 113/120] USB: OHCI: OHCI_INTR_MIE workaround for freeze on " Fredrik Noring
@ 2019-09-01 16:35 ` Fredrik Noring
  2019-09-01 16:35 ` [PATCH 115/120] MIPS: PS2: Define initial PlayStation 2 devices Fredrik Noring
                   ` (6 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:35 UTC (permalink / raw)
  To: linux-mips

The PlayStation 2 uLaunchELF program, used as a kernel boot loader,
starts in user mode with CP0 access enabled (CP0 Status 0x70030c11).
This is unexpected and causes a boot freeze.

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/boot/compressed/head.S | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/arch/mips/boot/compressed/head.S b/arch/mips/boot/compressed/head.S
index e3dc831e2616..6dc728d6ebea 100644
--- a/arch/mips/boot/compressed/head.S
+++ b/arch/mips/boot/compressed/head.S
@@ -24,6 +24,15 @@ start:
 	move	s2, a2
 	move	s3, a3
 
+#ifdef CONFIG_CPU_R5900
+	# The PlayStation 2 uLaunchELF starts in user mode with
+	# CP0 access enabled (CP0 Status 0x70030c11), which is
+	# unexpected. This is corrected here:
+	li	k0, 0x30010000
+	mtc0	k0, $12
+	sync.p
+#endif /* CONFIG_CPU_R5900 */
+
 	/* Clear BSS */
 	PTR_LA	a0, _edata - 4
 	PTR_LA	a2, _end
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 115/120] MIPS: PS2: Define initial PlayStation 2 devices
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (113 preceding siblings ...)
  2019-09-01 16:35 ` [PATCH 114/120] MIPS: PS2: Workaround for unexpected uLaunchELF CP0 Status user mode Fredrik Noring
@ 2019-09-01 16:35 ` Fredrik Noring
  2019-09-01 16:35 ` [PATCH 116/120] MIPS: PS2: Define workarounds related to the PlayStation 2 Fredrik Noring
                   ` (5 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:35 UTC (permalink / raw)
  To: linux-mips

Only the real-time clock (RTC), the input/output processor (IOP),
USB OHCI and the Graphics Synthesizer are supported at the moment.

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/ps2/Makefile  |  1 +
 arch/mips/ps2/devices.c | 98 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 99 insertions(+)
 create mode 100644 arch/mips/ps2/devices.c

diff --git a/arch/mips/ps2/Makefile b/arch/mips/ps2/Makefile
index 6f671112fbcb..925952a83625 100644
--- a/arch/mips/ps2/Makefile
+++ b/arch/mips/ps2/Makefile
@@ -1,3 +1,4 @@
+obj-y		+= devices.o
 obj-y		+= dmac-irq.o
 obj-y		+= identify.o
 obj-y		+= intc-irq.o
diff --git a/arch/mips/ps2/devices.c b/arch/mips/ps2/devices.c
new file mode 100644
index 000000000000..67aa1b158e29
--- /dev/null
+++ b/arch/mips/ps2/devices.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 devices
+ *
+ * Copyright (C) 2019 Fredrik Noring
+ */
+
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach-ps2/gs.h>
+#include <asm/mach-ps2/iop.h>
+#include <asm/mach-ps2/irq.h>
+
+static struct resource iop_resources[] = {
+	[0] = {
+		.name	= "IOP RAM",
+		.start	= IOP_RAM_BASE,
+		.end	= IOP_RAM_BASE + IOP_RAM_SIZE - 1,
+		.flags	= IORESOURCE_MEM,	/* 2 MiB IOP RAM */
+	},
+};
+
+static struct platform_device iop_device = {
+	.name		= "iop",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(iop_resources),
+	.resource	= iop_resources,
+};
+
+static struct resource ohci_resources[] = {	/* FIXME: Subresource to IOP */
+	[0] = {
+		.name	= "USB OHCI",
+		.start	= IOP_OHCI_BASE,
+		.end	= IOP_OHCI_BASE + 0xff,
+		.flags	= IORESOURCE_MEM, 	/* 256 byte HCCA. */
+	},
+	[1] = {
+		.start	= IRQ_IOP_USB,
+		.end	= IRQ_IOP_USB,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device ohci_device = {
+	.name		= "ohci-ps2",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(ohci_resources),
+	.resource	= ohci_resources,
+};
+
+static struct resource gs_resources[] = {
+	[0] = {
+		.name	= "Graphics Synthesizer",
+		.start	= GS_REG_BASE,
+		.end	= GS_REG_BASE + 0x1ffffff,
+		.flags	= IORESOURCE_MEM,	/* FIXME: IORESOURCE_REG? */
+	},
+	[1] = {
+		.start	= IRQ_DMAC_GIF,
+		.end	= IRQ_DMAC_GIF,
+		.flags	= IORESOURCE_IRQ,
+	},
+	[2] = {
+		.start	= IRQ_GS_SIGNAL,
+		.end	= IRQ_GS_EXVSYNC,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device gs_device = {
+	.name           = "gs",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(gs_resources),
+	.resource	= gs_resources,
+};
+
+static struct platform_device rtc_device = {
+	.name		= "rtc-ps2",
+	.id		= -1,
+};
+
+static struct platform_device *ps2_platform_devices[] __initdata = {
+	&iop_device,
+	&ohci_device,
+	&gs_device,
+	&rtc_device,
+};
+
+static int __init ps2_device_setup(void)
+{
+	return platform_add_devices(ps2_platform_devices,
+		ARRAY_SIZE(ps2_platform_devices));
+}
+device_initcall(ps2_device_setup);
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 116/120] MIPS: PS2: Define workarounds related to the PlayStation 2
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (114 preceding siblings ...)
  2019-09-01 16:35 ` [PATCH 115/120] MIPS: PS2: Define initial PlayStation 2 devices Fredrik Noring
@ 2019-09-01 16:35 ` Fredrik Noring
  2019-09-01 16:36 ` [PATCH 117/120] MIPS: PS2: Define R5900 feature overrides Fredrik Noring
                   ` (4 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:35 UTC (permalink / raw)
  To: linux-mips

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/mach-ps2/war.h | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)
 create mode 100644 arch/mips/include/asm/mach-ps2/war.h

diff --git a/arch/mips/include/asm/mach-ps2/war.h b/arch/mips/include/asm/mach-ps2/war.h
new file mode 100644
index 000000000000..f2ebbd31e9ee
--- /dev/null
+++ b/arch/mips/include/asm/mach-ps2/war.h
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 workarounds
+ *
+ * Copyright (C) 2010-2013 Jürgen Urban
+ */
+
+#ifndef __ASM_MIPS_MACH_PS2_WAR_H
+#define __ASM_MIPS_MACH_PS2_WAR_H
+
+#define R4600_V1_INDEX_ICACHEOP_WAR	0
+#define R4600_V1_HIT_CACHEOP_WAR	0
+#define R4600_V2_HIT_CACHEOP_WAR	0
+#define R5432_CP0_INTERRUPT_WAR		0
+#define BCM1250_M3_WAR			0
+#define SIBYTE_1956_WAR			0
+#define MIPS4K_ICACHE_REFILL_WAR	0
+#define MIPS_CACHE_SYNC_WAR		0
+#define TX49XX_ICACHE_INDEX_INV_WAR	0
+#define ICACHE_REFILLS_WORKAROUND_WAR	0
+#define R10000_LLSC_WAR			0
+#define MIPS34K_MISSED_ITLB_WAR		0
+
+#endif /* __ASM_MIPS_MACH_PS2_WAR_H */
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 117/120] MIPS: PS2: Define R5900 feature overrides
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (115 preceding siblings ...)
  2019-09-01 16:35 ` [PATCH 116/120] MIPS: PS2: Define workarounds related to the PlayStation 2 Fredrik Noring
@ 2019-09-01 16:36 ` Fredrik Noring
  2019-09-01 16:36 ` [PATCH 118/120] MIPS: PS2: Define the PlayStation 2 platform Fredrik Noring
                   ` (3 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:36 UTC (permalink / raw)
  To: linux-mips

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 .../asm/mach-ps2/cpu-feature-overrides.h      | 35 +++++++++++++++++++
 1 file changed, 35 insertions(+)
 create mode 100644 arch/mips/include/asm/mach-ps2/cpu-feature-overrides.h

diff --git a/arch/mips/include/asm/mach-ps2/cpu-feature-overrides.h b/arch/mips/include/asm/mach-ps2/cpu-feature-overrides.h
new file mode 100644
index 000000000000..e9b218e657f7
--- /dev/null
+++ b/arch/mips/include/asm/mach-ps2/cpu-feature-overrides.h
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 CPU features
+ *
+ * Copyright (C) 2010-2013 Jürgen Urban
+ */
+
+#ifndef __ASM_MACH_PS2_CPU_FEATURE_OVERRIDES_H
+#define __ASM_MACH_PS2_CPU_FEATURE_OVERRIDES_H
+
+#define cpu_has_llsc			0
+#define cpu_has_4k_cache		1
+#define cpu_has_divec			1
+#define cpu_has_4kex			1
+#define cpu_has_counter			1
+#define cpu_has_cache_cdex_p		0
+#define cpu_has_cache_cdex_s		0
+#define cpu_has_mcheck			0
+#define cpu_has_nofpuex			1
+#define cpu_has_mipsmt			0
+#define cpu_has_vce			0
+#define cpu_has_dsp			0
+#define cpu_has_userlocal		0
+#define cpu_has_64bit_addresses		0
+#define cpu_has_64bit   		1	/* FIXME */
+#define cpu_has_64bit_gp_regs		0	/* FIXME */
+#define cpu_has_64bit_zero_reg		0	/* FIXME */
+#define cpu_vmbits			31
+#define cpu_has_clo_clz			0
+#define cpu_has_ejtag			0
+#define cpu_has_ic_fills_f_dc		0
+#define cpu_has_inclusive_pcaches	0
+#define cpu_has_fpu 			0	/* FIXME */
+
+#endif /* __ASM_MACH_PS2_CPU_FEATURE_OVERRIDES_H */
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 118/120] MIPS: PS2: Define the PlayStation 2 platform
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (116 preceding siblings ...)
  2019-09-01 16:36 ` [PATCH 117/120] MIPS: PS2: Define R5900 feature overrides Fredrik Noring
@ 2019-09-01 16:36 ` Fredrik Noring
  2019-09-01 16:36 ` [PATCH 119/120] MIPS: PS2: Initial support for the Sony PlayStation 2 Fredrik Noring
                   ` (2 subsequent siblings)
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:36 UTC (permalink / raw)
  To: linux-mips

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/Kbuild.platforms |  1 +
 arch/mips/ps2/Makefile     |  1 +
 arch/mips/ps2/Platform     |  7 +++++++
 arch/mips/ps2/prom.c       | 18 ++++++++++++++++++
 4 files changed, 27 insertions(+)
 create mode 100644 arch/mips/ps2/Platform
 create mode 100644 arch/mips/ps2/prom.c

diff --git a/arch/mips/Kbuild.platforms b/arch/mips/Kbuild.platforms
index 0de839882106..84be263f44d2 100644
--- a/arch/mips/Kbuild.platforms
+++ b/arch/mips/Kbuild.platforms
@@ -24,6 +24,7 @@ platforms += netlogic
 platforms += paravirt
 platforms += pic32
 platforms += pistachio
+platforms += ps2
 platforms += pmcs-msp71xx
 platforms += pnx833x
 platforms += ralink
diff --git a/arch/mips/ps2/Makefile b/arch/mips/ps2/Makefile
index 925952a83625..dbc1bdbac2a8 100644
--- a/arch/mips/ps2/Makefile
+++ b/arch/mips/ps2/Makefile
@@ -4,6 +4,7 @@ obj-y		+= identify.o
 obj-y		+= intc-irq.o
 obj-y		+= irq.o
 obj-y		+= memory.o
+obj-y		+= prom.o
 obj-y		+= reboot.o
 obj-y		+= rom.o
 obj-m		+= rom-sysfs.o
diff --git a/arch/mips/ps2/Platform b/arch/mips/ps2/Platform
new file mode 100644
index 000000000000..5e9695f86b99
--- /dev/null
+++ b/arch/mips/ps2/Platform
@@ -0,0 +1,7 @@
+#
+# PlayStation 2
+#
+platform-$(CONFIG_SONY_PS2)	+= ps2/
+cflags-$(CONFIG_SONY_PS2)	+= -I$(srctree)/arch/mips/include/asm/mach-ps2
+load-$(CONFIG_SONY_PS2)		+= 0x80010000
+zload-$(CONFIG_SONY_PS2)	+= 0x00800000
diff --git a/arch/mips/ps2/prom.c b/arch/mips/ps2/prom.c
new file mode 100644
index 000000000000..f4d594c4457e
--- /dev/null
+++ b/arch/mips/ps2/prom.c
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PlayStation 2 PROM handling
+ */
+
+#include <linux/module.h>
+
+void prom_putchar(char c)
+{
+}
+
+void __init prom_init(void)
+{
+}
+
+void __init prom_free_prom_memory(void)
+{
+}
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 119/120] MIPS: PS2: Initial support for the Sony PlayStation 2
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (117 preceding siblings ...)
  2019-09-01 16:36 ` [PATCH 118/120] MIPS: PS2: Define the PlayStation 2 platform Fredrik Noring
@ 2019-09-01 16:36 ` Fredrik Noring
  2019-09-01 16:37 ` [PATCH 120/120] MIPS: Fix name of BOOT_MEM_ROM_DATA Fredrik Noring
  2019-09-04 14:19 ` [PATCH 000/120] Linux for the PlayStation 2 Paul Burton
  120 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:36 UTC (permalink / raw)
  To: linux-mips

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/Kconfig | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 88d6c13260e1..f90af3fd31d3 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -1011,6 +1011,27 @@ config MIPS_PARAVIRT
 	help
 	  This option supports guest running under ????
 
+config SONY_PS2
+	bool "Sony PlayStation 2"
+	select BOOT_ELF32
+	select CEVT_R4K
+	select CSRC_R4K
+	select DMA_NONCOHERENT
+	select HARDIRQS_SW_RESEND
+	select HAVE_PATA_PLATFORM
+	select IRQ_MIPS_CPU
+	select MIPS_L1_CACHE_SHIFT_6
+	select NO_IOPORT
+	select SYS_HAS_CPU_R5900
+	select SYS_HAS_EARLY_PRINTK
+	select SYS_SUPPORTS_32BIT_KERNEL
+	select SYS_SUPPORTS_LITTLE_ENDIAN
+	select SYS_SUPPORTS_ZBOOT
+	select USB_ARCH_HAS_OHCI
+	select USB_OHCI_LITTLE_ENDIAN
+	help
+	  This enables support for the Sony PlayStation 2.
+
 endchoice
 
 source "arch/mips/alchemy/Kconfig"
@@ -1037,6 +1058,7 @@ source "arch/mips/loongson32/Kconfig"
 source "arch/mips/loongson64/Kconfig"
 source "arch/mips/netlogic/Kconfig"
 source "arch/mips/paravirt/Kconfig"
+source "arch/mips/ps2/Kconfig"
 
 endmenu
 
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* [PATCH 120/120] MIPS: Fix name of BOOT_MEM_ROM_DATA
  2019-09-01 15:35 [PATCH 000/120] Linux for the PlayStation 2 Fredrik Noring
                   ` (118 preceding siblings ...)
  2019-09-01 16:36 ` [PATCH 119/120] MIPS: PS2: Initial support for the Sony PlayStation 2 Fredrik Noring
@ 2019-09-01 16:37 ` Fredrik Noring
  2019-09-01 23:15   ` Philippe Mathieu-Daudé
  2019-09-02  1:02   ` Jiaxun Yang
  2019-09-04 14:19 ` [PATCH 000/120] Linux for the PlayStation 2 Paul Burton
  120 siblings, 2 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 16:37 UTC (permalink / raw)
  To: linux-mips

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/kernel/setup.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c
index ab349d2381c3..7d5bf8cb513b 100644
--- a/arch/mips/kernel/setup.c
+++ b/arch/mips/kernel/setup.c
@@ -870,14 +870,16 @@ static void __init resource_init(void)
 		switch (boot_mem_map.map[i].type) {
 		case BOOT_MEM_RAM:
 		case BOOT_MEM_INIT_RAM:
-		case BOOT_MEM_ROM_DATA:
 			res->name = "System RAM";
 			res->flags |= IORESOURCE_SYSRAM;
 			break;
+		case BOOT_MEM_ROM_DATA:
+			res->name = "System ROM";
+			break;
 		case BOOT_MEM_RESERVED:
 		case BOOT_MEM_NOMAP:
 		default:
-			res->name = "reserved";
+			res->name = "Reserved";
 		}
 
 		request_resource(&iomem_resource, res);
-- 
2.21.0


^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH 011/120] MIPS: R5900: Avoid pipeline hazard with the TLBP instruction
  2019-09-01 15:40 ` [PATCH 011/120] MIPS: R5900: Avoid pipeline hazard with the TLBP instruction Fredrik Noring
@ 2019-09-01 17:15   ` Sergei Shtylyov
  2019-09-01 17:36     ` Fredrik Noring
  0 siblings, 1 reply; 152+ messages in thread
From: Sergei Shtylyov @ 2019-09-01 17:15 UTC (permalink / raw)
  To: Fredrik Noring, linux-mips

Hello!

On 01.09.2019 18:40, Fredrik Noring wrote:

> On the R5900, the TLBP instruction must be immediately followed by an
> ERET or a SYNC.P instruction[1].
> 
> References:
> 
> [1] "TX System RISC TX79 Core Architecture" manual, revision 2.0,
>      Toshiba Corporation, p. C-37, https://wiki.qemu.org/File:C790.pdf
> 
> Signed-off-by: Fredrik Noring <noring@nocrew.org>
> ---
>   arch/mips/mm/tlbex.c | 13 +++++++++++++
>   1 file changed, 13 insertions(+)
> 
> diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c
> index 82136c346885..0519e2eafbb8 100644
> --- a/arch/mips/mm/tlbex.c
> +++ b/arch/mips/mm/tlbex.c
> @@ -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?

> +		 * 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

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH 011/120] MIPS: R5900: Avoid pipeline hazard with the TLBP instruction
  2019-09-01 17:15   ` Sergei Shtylyov
@ 2019-09-01 17:36     ` Fredrik Noring
  0 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 17:36 UTC (permalink / raw)
  To: Sergei Shtylyov; +Cc: linux-mips

Hi Sergei,

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

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH 072/120] MIPS: PS2: IOP: SIF printk command support
  2019-09-01 16:12 ` [PATCH 072/120] MIPS: PS2: IOP: SIF printk command support Fredrik Noring
@ 2019-09-01 17:44   ` Sergei Shtylyov
  2019-09-01 18:08     ` Fredrik Noring
  0 siblings, 1 reply; 152+ messages in thread
From: Sergei Shtylyov @ 2019-09-01 17:44 UTC (permalink / raw)
  To: Fredrik Noring, linux-mips

On 01.09.2019 19:12, Fredrik Noring wrote:

> 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 <noring@nocrew.org>
> ---
>   drivers/ps2/iop-module.c | 31 +++++++++++++++++++++++++++++++
>   1 file changed, 31 insertions(+)
> 
> diff --git a/drivers/ps2/iop-module.c b/drivers/ps2/iop-module.c
> index bb4814b5d3c4..18020b3673d3 100644
> --- a/drivers/ps2/iop-module.c
> +++ b/drivers/ps2/iop-module.c
> @@ -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?

> + * @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;
[...]

MBR, Sergei

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH 072/120] MIPS: PS2: IOP: SIF printk command support
  2019-09-01 17:44   ` Sergei Shtylyov
@ 2019-09-01 18:08     ` Fredrik Noring
  0 siblings, 0 replies; 152+ messages in thread
From: Fredrik Noring @ 2019-09-01 18:08 UTC (permalink / raw)
  To: Sergei Shtylyov; +Cc: linux-mips

Hi Sergei,

> > +/**
> > + * cmd_printk - IOP module kernel log printk command
> > + * @header: SIF command header
> > + * @void: message to print
> 
>     @data, maybe?

Indeed, thanks!

Fredrik

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH 001/120] MIPS: R5900: Initial support for the Emotion Engine in the PlayStation 2
  2019-09-01 15:35 ` [PATCH 001/120] MIPS: R5900: Initial support for the Emotion Engine in " Fredrik Noring
@ 2019-09-01 21:14   ` Maciej W. Rozycki
  2019-09-02 15:09     ` Fredrik Noring
  0 siblings, 1 reply; 152+ messages in thread
From: Maciej W. Rozycki @ 2019-09-01 21:14 UTC (permalink / raw)
  To: Fredrik Noring; +Cc: linux-mips

On Sun, 1 Sep 2019, Fredrik Noring wrote:

> diff --git a/arch/mips/include/asm/cpu.h b/arch/mips/include/asm/cpu.h
> index 290369fa44a4..05a0f5a07f08 100644
> --- a/arch/mips/include/asm/cpu.h
> +++ b/arch/mips/include/asm/cpu.h
> @@ -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

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH 002/120] MIPS: R5900: Trap the RDHWR instruction as an SQ address exception
  2019-09-01 15:36 ` [PATCH 002/120] MIPS: R5900: Trap the RDHWR instruction as an SQ address exception Fredrik Noring
@ 2019-09-01 22:00   ` Maciej W. Rozycki
  0 siblings, 0 replies; 152+ messages in thread
From: Maciej W. Rozycki @ 2019-09-01 22:00 UTC (permalink / raw)
  To: Fredrik Noring; +Cc: linux-mips

On Sun, 1 Sep 2019, Fredrik Noring wrote:

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

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

> diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c
> index 92bd2b0f0548..f490944d73cf 100644
> --- a/arch/mips/kernel/unaligned.c
> +++ b/arch/mips/kernel/unaligned.c
> @@ -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

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH 010/120] MIPS: R5900: Workaround exception NOP execution bug (FLX05)
  2019-09-01 15:39 ` [PATCH 010/120] MIPS: R5900: Workaround exception NOP execution bug (FLX05) Fredrik Noring
@ 2019-09-01 23:01   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 152+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-09-01 23:01 UTC (permalink / raw)
  To: Fredrik Noring, linux-mips

On 9/1/19 5:39 PM, Fredrik Noring wrote:
> For the R5900, there are cases in which the first two instructions
> in an exception handler are executed as NOP instructions, when
> certain exceptions occur and then a bus error occurs immediately
> before jumping to the exception handler (FLX05)[1].
> 
> The corrective measure is to place NOP in the first two instruction
> locations in all exception handlers.
> 
> References:
> 
> [1] "TX System RISC TX79 Core Architecture" manual, revision 2.0,
>     Toshiba Corporation, p. 1-11, https://wiki.qemu.org/File:C790.pdf
> 
> Signed-off-by: Fredrik Noring <noring@nocrew.org>

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

> ---
>  arch/mips/kernel/genex.S | 5 +++++
>  arch/mips/kernel/traps.c | 6 ++++++
>  arch/mips/mm/tlbex.c     | 6 ++++++
>  3 files changed, 17 insertions(+)
> 
> diff --git a/arch/mips/kernel/genex.S b/arch/mips/kernel/genex.S
> index 6eaf057e5d95..f57842b785b2 100644
> --- a/arch/mips/kernel/genex.S
> +++ b/arch/mips/kernel/genex.S
> @@ -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
> diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
> index 9423b9a2eb67..9c98475c7dc6 100644
> --- a/arch/mips/kernel/traps.c
> +++ b/arch/mips/kernel/traps.c
> @@ -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);
> diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c
> index 543ddb22b0d9..82136c346885 100644
> --- a/arch/mips/mm/tlbex.c
> +++ b/arch/mips/mm/tlbex.c
> @@ -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);
> 

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH 020/120] MIPS: R5900: Define CP0.Config register fields
  2019-09-01 15:43 ` [PATCH 020/120] MIPS: R5900: Define CP0.Config register fields Fredrik Noring
@ 2019-09-01 23:04   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 152+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-09-01 23:04 UTC (permalink / raw)
  To: Fredrik Noring, linux-mips

On 9/1/19 5:43 PM, Fredrik Noring wrote:
> The following CP0.Config fields are specific to the R5900[1]:
> 
> 	Field | Bit | Type | Description
> 	------+-----+------+-----------------------------
> 	  DIE |  18 |   RW | Enable double issue
> 	  ICE |  17 |   RW | Enable the instruction cache
> 	  DCE |  16 |   RW | Enable the data cache
> 	  BE  |  15 |   RO | Enable big-endian
> 	  NBE |  13 |   RW | Enable nonblocking load
> 	  BPE |  12 |   RW | Enable branch prediction
> 	------+-----+------+-----------------------------
> 
> References:
> 
> [1] "TX System RISC TX79 Core Architecture" manual, revision 2.0,
>     Toshiba Corporation, p. 4-23, https://wiki.qemu.org/File:C790.pdf
> 
> Signed-off-by: Fredrik Noring <noring@nocrew.org>
> ---
>  arch/mips/include/asm/mipsregs.h | 8 ++++++++
>  1 file changed, 8 insertions(+)
> 
> diff --git a/arch/mips/include/asm/mipsregs.h b/arch/mips/include/asm/mipsregs.h
> index ec22406c800f..a3b3ee011539 100644
> --- a/arch/mips/include/asm/mipsregs.h
> +++ b/arch/mips/include/asm/mipsregs.h
> @@ -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)
> 

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH 047/120] MIPS: PS2: Let the system type be Sony PlayStation 2
  2019-09-01 15:54 ` [PATCH 047/120] MIPS: PS2: Let the system type be Sony PlayStation 2 Fredrik Noring
@ 2019-09-01 23:09   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 152+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-09-01 23:09 UTC (permalink / raw)
  To: Fredrik Noring, linux-mips

On 9/1/19 5:54 PM, Fredrik Noring wrote:
> The system type is shown in the /proc/cpuinfo file:
> 
> 	# grep system' 'type /proc/cpuinfo
> 	system type		: Sony PlayStation 2
> 
> Signed-off-by: Fredrik Noring <noring@nocrew.org>
> ---
>  arch/mips/ps2/identify.c | 6 ++++++
>  1 file changed, 6 insertions(+)
> 
> diff --git a/arch/mips/ps2/identify.c b/arch/mips/ps2/identify.c
> index 264fdc13dc43..579148fb79c4 100644
> --- a/arch/mips/ps2/identify.c
> +++ b/arch/mips/ps2/identify.c
> @@ -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();
> 

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH 093/120] fbdev: Add fb_warn_once() variant that only prints a warning once
  2019-09-01 16:25 ` [PATCH 093/120] fbdev: Add fb_warn_once() variant that only prints a warning once Fredrik Noring
@ 2019-09-01 23:12   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 152+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-09-01 23:12 UTC (permalink / raw)
  To: Fredrik Noring, linux-mips

On 9/1/19 6:25 PM, Fredrik Noring wrote:
> fb_warn_once() is a variant of fb_warn(), to print a warning only once.
> 
> Signed-off-by: Fredrik Noring <noring@nocrew.org>
> ---
>  include/linux/fb.h | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/include/linux/fb.h b/include/linux/fb.h
> index 303771264644..19f5118e34ea 100644
> --- a/include/linux/fb.h
> +++ b/include/linux/fb.h
> @@ -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, ...)					\
> 

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH 120/120] MIPS: Fix name of BOOT_MEM_ROM_DATA
  2019-09-01 16:37 ` [PATCH 120/120] MIPS: Fix name of BOOT_MEM_ROM_DATA Fredrik Noring
@ 2019-09-01 23:15   ` Philippe Mathieu-Daudé
  2019-09-02  1:02   ` Jiaxun Yang
  1 sibling, 0 replies; 152+ messages in thread
From: Philippe Mathieu-Daudé @ 2019-09-01 23:15 UTC (permalink / raw)
  To: Fredrik Noring, linux-mips

On 9/1/19 6:37 PM, Fredrik Noring wrote:
> Signed-off-by: Fredrik Noring <noring@nocrew.org>
> ---
>  arch/mips/kernel/setup.c | 6 ++++--
>  1 file changed, 4 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c
> index ab349d2381c3..7d5bf8cb513b 100644
> --- a/arch/mips/kernel/setup.c
> +++ b/arch/mips/kernel/setup.c
> @@ -870,14 +870,16 @@ static void __init resource_init(void)
>  		switch (boot_mem_map.map[i].type) {
>  		case BOOT_MEM_RAM:
>  		case BOOT_MEM_INIT_RAM:
> -		case BOOT_MEM_ROM_DATA:
>  			res->name = "System RAM";
>  			res->flags |= IORESOURCE_SYSRAM;
>  			break;
> +		case BOOT_MEM_ROM_DATA:
> +			res->name = "System ROM";
> +			break;
>  		case BOOT_MEM_RESERVED:
>  		case BOOT_MEM_NOMAP:
>  		default:
> -			res->name = "reserved";
> +			res->name = "Reserved";

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

>  		}
>  
>  		request_resource(&iomem_resource, res);
> 

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH 120/120] MIPS: Fix name of BOOT_MEM_ROM_DATA
  2019-09-01 16:37 ` [PATCH 120/120] MIPS: Fix name of BOOT_MEM_ROM_DATA Fredrik Noring
  2019-09-01 23:15   ` Philippe Mathieu-Daudé
@ 2019-09-02  1:02   ` Jiaxun Yang
  2019-09-02 15:26     ` Fredrik Noring
  1 sibling, 1 reply; 152+ messages in thread
From: Jiaxun Yang @ 2019-09-02  1:02 UTC (permalink / raw)
  To: Fredrik Noring, linux-mips


在 2019/9/2 0:37, Fredrik Noring 写道:
> Signed-off-by: Fredrik Noring <noring@nocrew.org>
> ---
>   arch/mips/kernel/setup.c | 6 ++++--
>   1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c
> index ab349d2381c3..7d5bf8cb513b 100644
> --- a/arch/mips/kernel/setup.c
> +++ b/arch/mips/kernel/setup.c
> @@ -870,14 +870,16 @@ static void __init resource_init(void)
>   		switch (boot_mem_map.map[i].type) {
>   		case BOOT_MEM_RAM:
>   		case BOOT_MEM_INIT_RAM:
> -		case BOOT_MEM_ROM_DATA:
>   			res->name = "System RAM";
>   			res->flags |= IORESOURCE_SYSRAM;
>   			break;
> +		case BOOT_MEM_ROM_DATA:
> +			res->name = "System ROM";
> +			break;

Hi Fredik,

ROM_DATA meant memory used by ROM(Bootloader) to store data in some 
machines, is that name right?

Btw, boot_mem_map had been droped recently, see mips-next tree, please 
rebase.

Thanks

--

Jiaxun Yang

>   		case BOOT_MEM_RESERVED:
>   		case BOOT_MEM_NOMAP:
>   		default:
> -			res->name = "reserved";
> +			res->name = "Reserved";
>   		}
>   
>   		request_resource(&iomem_resource, res);

^ permalink raw reply	[flat|nested] 152+ messages in thread

* Re: [PATCH 094/120] MIPS: PS2: FB: Frame buffer driver for the PlayStation 2
  2019-09-01 16:26 ` [PATCH 094/120] MIPS: PS2: FB: Frame buffer driver for the PlayStation 2 Fredrik Noring
@ 2019-09-02  1:12   ` Jiaxun Yang
  2019-09-02 14:40     ` Fredrik Noring
  0 siblings, 1 reply; 152+ messages in thread
From: Jiaxun Yang @ 2019-09-02  1:12 UTC (permalink / raw)
  To: Fredrik Noring, linux-mips


在 2019/9/2 0:26, Fredrik Noring 写道:
> The main limitation is the lack of mmap, since the Graphics Synthesizer
> has local frame buffer memory that is not directly accessible from the
> main bus. The GS has 4 MiB of local memory.
>
> The console drawing primitives are synchronous to allow printk at any
> time. This is highly useful for debugging but it is not the fastest
> possible implementation. The console is nevertheless very fast and
> makes use of several hardware accelerated features of the Graphics
> Synthesizer.
>
> The maximum practical resolution is 1920x1080p at 16 bits per pixel that
> requires 4147200 bytes of local memory, leaving 47104 bytes for a tiled
> font, which at 8x8 pixels and a minimum 4 bits indexed texture palette is
> at most 1464 characters. The indexed palette makes switching colours easy.
> &struct fb_tile_ops is accelerated with GS texture sprites that are fast
> (GS local copy) for the kernel via simple DMA GS commands via the GIF.
>
> Signed-off-by: Fredrik Noring <noring@nocrew.org>

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

> ---
>   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
>
> diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
> index 6b2de93bd302..cc93cbd67b01 100644
> --- a/drivers/video/fbdev/Kconfig
> +++ b/drivers/video/fbdev/Kconfig
> @@ -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
> diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile
> index 7dc4861a93e6..1e55fa8ca4af 100644
> --- a/drivers/video/fbdev/Makefile
> +++ b/drivers/video/fbdev/Makefile
> @@ -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
> diff --git a/drivers/video/fbdev/ps2fb.c b/drivers/video/fbdev/ps2fb.c
> new file mode 100644
> index 000000000000..7bfbc3c2aa4d
> --- /dev/null
> +++ b/drivers/video/fbdev/ps2fb.c
> @@ -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 te