Updates for the Sharp MZ TTY driver

This commit is contained in:
Philip Smart
2023-03-03 20:02:26 +00:00
parent 09c9f0dadc
commit fe53a4c871
9 changed files with 6108 additions and 131 deletions

View File

@@ -130,137 +130,6 @@ static t_asciiMap asciiMap[] = {
{ 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, // 0XEF
{ 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 }, { 0x20 } // 0XFF
};
static t_dispCodeMap dispCodeMap[] = {
{ 0xCC }, // NUL '\0' (null character)
{ 0xE0 }, // SOH (start of heading)
{ 0xF2 }, // STX (start of text)
{ 0xF3 }, // ETX (end of text)
{ 0xCE }, // EOT (end of transmission)
{ 0xCF }, // ENQ (enquiry)
{ 0xF6 }, // ACK (acknowledge)
{ 0xF7 }, // BEL '\a' (bell)
{ 0xF8 }, // BS '\b' (backspace)
{ 0xF9 }, // HT '\t' (horizontal tab)
{ 0xFA }, // LF '\n' (new line)
{ 0xFB }, // VT '\v' (vertical tab)
{ 0xFC }, // FF '\f' (form feed)
{ 0xFD }, // CR '\r' (carriage ret)
{ 0xFE }, // SO (shift out)
{ 0xFF }, // SI (shift in)
{ 0xE1 }, // DLE (data link escape)
{ 0xC1 }, // DC1 (device control 1)
{ 0xC2 }, // DC2 (device control 2)
{ 0xC3 }, // DC3 (device control 3)
{ 0xC4 }, // DC4 (device control 4)
{ 0xC5 }, // NAK (negative ack.)
{ 0xC6 }, // SYN (synchronous idle)
{ 0xE2 }, // ETB (end of trans. blk)
{ 0xE3 }, // CAN (cancel)
{ 0xE4 }, // EM (end of medium)
{ 0xE5 }, // SUB (substitute)
{ 0xE6 }, // ESC (escape)
{ 0xEB }, // FS (file separator)
{ 0xEE }, // GS (group separator)
{ 0xEF }, // RS (record separator)
{ 0xF4 }, // US (unit separator)
{ 0x00 }, // SPACE
{ 0x61 }, // !
{ 0x62 }, // "
{ 0x63 }, // #
{ 0x64 }, // $
{ 0x65 }, // %
{ 0x66 }, // &
{ 0x67 }, // '
{ 0x68 }, // (
{ 0x69 }, // )
{ 0x6B }, // *
{ 0x6A }, // +
{ 0x2F }, // ,
{ 0x2A }, // -
{ 0x2E }, // .
{ 0x2D }, // /
{ 0x20 }, // 0
{ 0x21 }, // 1
{ 0x22 }, // 2
{ 0x23 }, // 3
{ 0x24 }, // 4
{ 0x25 }, // 5
{ 0x26 }, // 6
{ 0x27 }, // 7
{ 0x28 }, // 8
{ 0x29 }, // 9
{ 0x4F }, // :
{ 0x2C }, // ;
{ 0x51 }, // <
{ 0x2B }, // =
{ 0x57 }, // >
{ 0x49 }, // ?
{ 0x55 }, // @
{ 0x01 }, // A
{ 0x02 }, // B
{ 0x03 }, // C
{ 0x04 }, // D
{ 0x05 }, // E
{ 0x06 }, // F
{ 0x07 }, // G
{ 0x08 }, // H
{ 0x09 }, // I
{ 0x0A }, // J
{ 0x0B }, // K
{ 0x0C }, // L
{ 0x0D }, // M
{ 0x0E }, // N
{ 0x0F }, // O
{ 0x10 }, // P
{ 0x11 }, // Q
{ 0x12 }, // R
{ 0x13 }, // S
{ 0x14 }, // T
{ 0x15 }, // U
{ 0x16 }, // V
{ 0x17 }, // W
{ 0x18 }, // X
{ 0x19 }, // Y
{ 0x1A }, // Z
{ 0x52 }, // [
{ 0x59 }, // \ '\\'
{ 0x54 }, // ]
{ 0xBE }, // ^
{ 0x3C }, // _
{ 0xC7 }, // `
{ 0x81 }, // a
{ 0x82 }, // b
{ 0x83 }, // c
{ 0x84 }, // d
{ 0x85 }, // e
{ 0x86 }, // f
{ 0x87 }, // g
{ 0x88 }, // h
{ 0x89 }, // i
{ 0x8A }, // j
{ 0x8B }, // k
{ 0x8C }, // l
{ 0x8D }, // m
{ 0x8E }, // n
{ 0x8F }, // o
{ 0x90 }, // p
{ 0x91 }, // q
{ 0x92 }, // r
{ 0x93 }, // s
{ 0x94 }, // t
{ 0x95 }, // u
{ 0x96 }, // v
{ 0x97 }, // w
{ 0x98 }, // x
{ 0x99 }, // y
{ 0x9A }, // z
{ 0xBC }, // {
{ 0x80 }, // |
{ 0x40 }, // }
{ 0xA5 }, // ~
{ 0xC0 } // DEL
};
// Method to obtain and return the output screen width.
//

33
software/FusionX/src/tty/Makefile vendored Normal file
View File

@@ -0,0 +1,33 @@
# Select the target host.
#MODEL := MZ2000
#MODEL := MZ700
#DEBUG := y
MODEL := MZ80A
KERNEL := $(PWD)/../../../linux/kernel
FUSIONX := $(PWD)/../..
CROSS := arm-linux-gnueabihf-
ifeq ($(DEBUG),y)
ccflags-y += -DTTYMZ_DEBUG
else
ccflags-y += -O2 -I${KERNEL}/drivers/sstar/include -I${KERNEL}/drivers/sstar/include/infinity2m -I${KERNEL}/drivers/sstar/gpio/infinity2m -D__KERNEL_DRIVER__
endif
obj-m += ttymzdrv.o
ttymzdrv-objs += ttymz.o z80io.o sharpmz.o
ttymzdrv-objs += ../../../linux/kernel/drivers/sstar/gpio/infinity2m/gpio_table.o
ttymzdrv-objs += ../../../linux/kernel/drivers/sstar/gpio/infinity2m/mhal_gpio.o
ttymzdrv-objs += ../../../linux/kernel/drivers/sstar/gpio/infinity2m/mhal_pinmux.o
ttymzdrv-objs += ../../../linux/kernel/drivers/sstar/gpio/infinity2m/padmux_tables.o
all:
@echo ""
@echo "Build TTYMZ driver for host: $(MODEL)"
make -C $(KERNEL) ARCH=arm CROSS_COMPILE=$(CROSS) M="$(PWD)" modules
@echo ""
install:
@echo "Copy kernel driver..."
@cp ttymz.ko $(FUSIONX)/modules/
clean:
make -C $(KERNEL) M=$(PWD) clean

File diff suppressed because it is too large Load Diff

412
software/FusionX/src/tty/sharpmz.h vendored Executable file
View File

@@ -0,0 +1,412 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: sharpmz.c
// Created: February 2023
// Version: v1.0
// Author(s): Philip Smart
// Description: Sharp MZ Interface Library.
// This file contains methods which allow the Linux TTY driver to access and control the
// Sharp MZ series computer hardware.
//
// Credits:
// Copyright: (c) 2019-2023 Philip Smart <philip.smart@net2net.org>
//
// History: v1.0 Feb 2023 - Initial write of the Sharp MZ series hardware interface software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file is free software: you can redistribute it and#or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef SHARPMZ_H
#define SHARPMZ_H
#ifdef __cplusplus
extern "C" {
#endif
#define TARGET_HOST_MZ700 0 // Target compilation for an MZ700
#define TARGET_HOST_MZ2000 0 // MZ2000
#define TARGET_HOST_MZ80A 1 // MZ80A
// Video display constants.
#define VC_MAX_ROWS 25 // Maximum number of rows on display.
#define VC_MAX_COLUMNS 80 // Maximum number of columns on display.
#define VC_MAX_BUFFER_ROWS 50 // Maximum number of backing store rows for scrollback feature.
#define VC_DISPLAY_BUFFER_SIZE VC_MAX_COLUMNS * VC_MAX_BUFFER_ROWS // Size of the display buffer for scrollback.
// Keyboard constants.
#define KEYB_AUTOREPEAT_INITIAL_TIME 800 // Time in milliseconds before starting autorepeat.
#define KEYB_AUTOREPEAT_TIME 100 // Time in milliseconds between auto repeating characters.
#define KEYB_FLASH_TIME 350 // Time in milliseconds for the cursor flash change.
#define CURSOR_THICK_BLOCK 0x43 // Thick block cursor for lower case CAPS OFF
#define CURSOR_BLOCK 0xEF // Block cursor for SHIFT Lock.
#define CURSOR_UNDERLINE 0x3E // Thick underscore for CAPS Lock.
#define MAX_KEYB_BUFFER_SIZE 32 // Maximum size of the keyboard buffer.
// Audio constants.
#define TIMER_8253_MZ80A_FREQ 2000000 // Base input frequency of Timer 0 for square wave generation.
#define TIMER_8253_MZ700 768000 // Base input frequency of Timer 0 for square wave generation.
// Base addresses and sizes within the Video Controller.
#define VIDEO_BASE_ADDR 0x000000 // Base address of the Video Controller.
#define VIDEO_VRAM_BASE_ADDR VIDEO_BASE_ADDR + 0x00D000 // Base address of the character video RAM using direct addressing.
#define VIDEO_VRAM_SIZE 0x800 // Size of the video RAM.
#define VIDEO_ARAM_BASE_ADDR VIDEO_BASE_ADDR + 0x00D800 // Base address of the character attribute RAM using direct addressing.
#define VIDEO_ARAM_SIZE 0x800 // Size of the attribute RAM.
// Video Module control bits.
#define VMMODE_MASK 0xF8 // Mask to mask out video mode.
#define VMMODE_MZ80K 0x00 // Video mode = MZ80K
#define VMMODE_MZ80C 0x01 // Video mode = MZ80C
#define VMMODE_MZ1200 0x02 // Video mode = MZ1200
#define VMMODE_MZ80A 0x03 // Video mode = MZ80A
#define VMMODE_MZ700 0x04 // Video mode = MZ700
#define VMMODE_MZ800 0x05 // Video mode = MZ800
#define VMMODE_MZ1500 0x06 // Video mode = MZ1500
#define VMMODE_MZ80B 0x07 // Video mode = MZ80B
#define VMMODE_MZ2000 0x08 // Video mode = MZ2000
#define VMMODE_MZ2200 0x09 // Video mode = MZ2200
#define VMMODE_MZ2500 0x0A // Video mode = MZ2500
#define VMMODE_80CHAR 0x80 // Enable 80 character display.
#define VMMODE_80CHAR_MASK 0x7F // Mask to filter out display width control bit.
#define VMMODE_COLOUR 0x20 // Enable colour display.
#define VMMODE_COLOUR_MASK 0xDF // Mask to filter out colour control bit.
// Sharp MZ colour attributes.
#define VMATTR_FG_BLACK 0x00 // Foreground black character attribute.
#define VMATTR_FG_BLUE 0x10 // Foreground blue character attribute.
#define VMATTR_FG_RED 0x20 // Foreground red character attribute.
#define VMATTR_FG_PURPLE 0x30 // Foreground purple character attribute.
#define VMATTR_FG_GREEN 0x40 // Foreground green character attribute.
#define VMATTR_FG_CYAN 0x50 // Foreground cyan character attribute.
#define VMATTR_FG_YELLOW 0x60 // Foreground yellow character attribute.
#define VMATTR_FG_WHITE 0x70 // Foreground white character attribute.
#define VMATTR_FG_MASKOUT 0x8F // Mask to filter out foreground attribute.
#define VMATTR_FG_MASKIN 0x70 // Mask to filter out foreground attribute.
#define VMATTR_BG_BLACK 0x00 // Background black character attribute.
#define VMATTR_BG_BLUE 0x01 // Background blue character attribute.
#define VMATTR_BG_RED 0x02 // Background red character attribute.
#define VMATTR_BG_PURPLE 0x03 // Background purple character attribute.
#define VMATTR_BG_GREEN 0x04 // Background green character attribute.
#define VMATTR_BG_CYAN 0x05 // Background cyan character attribute.
#define VMATTR_BG_YELLOW 0x06 // Background yellow character attribute.
#define VMATTR_BG_WHITE 0x07 // Background white character attribute.
#define VMATTR_BG_MASKOUT 0xF8 // Mask to filter out background attribute.
#define VMATTR_BG_MASKIN 0x07 // Mask to filter out background attribute.
// Sharp MZ constants.
//
#define MBADDR_KEYPA 0xE000 // Mainboard 8255 Port A
#define MBADDR_KEYPB 0xE001 // Mainboard 8255 Port B
#define MBADDR_KEYPC 0xE002 // Mainboard 8255 Port C
#define MBADDR_KEYPF 0xE003 // Mainboard 8255 Mode Control
#define MBADDR_CSTR 0xE002 // Mainboard 8255 Port C
#define MBADDR_CSTPT 0xE003 // Mainboard 8255 Mode Control
#define MBADDR_CONT0 0xE004 // Mainboard 8253 Counter 0
#define MBADDR_CONT1 0xE005 // Mainboard 8253 Counter 1
#define MBADDR_CONT2 0xE006 // Mainboard 8253 Counter 1
#define MBADDR_CONTF 0xE007 // Mainboard 8253 Mode Control
#define MBADDR_SUNDG 0xE008 // Register for reading the tempo timer status (cursor flash). horizontal blank and switching sound on/off.
#define MBADDR_TEMP 0xE008 // As above, different name used in original source when writing.
#define MBADDR_MEMSW 0xE00C // Memory swap, 0000->C000, C000->0000
#define MBADDR_MEMSWR 0xE010 // Reset memory swap.
#define MBADDR_INVDSP 0xE014 // Invert display.
#define MBADDR_NRMDSP 0xE015 // Return display to normal.
#define MBADDR_SCLDSP 0xE200 // Hardware scroll, a read to each location adds 8 to the start of the video access address therefore creating hardware scroll. 00 - reset to power up
#define MBADDR_SCLBASE 0xE2 // High byte scroll base.
#define MBADDR_DSPCTL 0xDFFF // Display 40/80 select register (bit 7)
//Common character definitions.
#define SCROLL 0x01 // Set scroll direction UP.
#define BELL 0x07
#define ENQ 0x05
#define SPACE 0x20
#define TAB 0x09 // TAB ACROSS (8 SPACES FOR SD-BOARD)
#define CR 0x0D
#define LF 0x0A
#define FF 0x0C
#define DELETE 0x7F
#define BACKS 0x08
#define SOH 0x01 // For XModem etc.
#define EOT 0x04
#define ACK 0x06
#define NAK 0x15
#define NUL 0x00
//#define NULL 0x00
#define CTRL_A 0x01
#define CTRL_B 0x02
#define CTRL_C 0x03
#define CTRL_D 0x04
#define CTRL_E 0x05
#define CTRL_F 0x06
#define CTRL_G 0x07
#define CTRL_H 0x08
#define CTRL_I 0x09
#define CTRL_J 0x0A
#define CTRL_K 0x0B
#define CTRL_L 0x0C
#define CTRL_M 0x0D
#define CTRL_N 0x0E
#define CTRL_O 0x0F
#define CTRL_P 0x10
#define CTRL_Q 0x11
#define CTRL_R 0x12
#define CTRL_S 0x13
#define CTRL_T 0x14
#define CTRL_U 0x15
#define CTRL_V 0x16
#define CTRL_W 0x17
#define CTRL_X 0x18
#define CTRL_Y 0x19
#define CTRL_Z 0x1A
#define ESC 0x1B
#define CTRL_SLASH 0x1C
#define CTRL_LB 0x1B
#define CTRL_RB 0x1D
#define CTRL_CAPPA 0x1E
#define CTRL_UNDSCR 0x1F
#define CTRL_AT 0x00
#define FUNC1 0x80
#define FUNC2 0x81
#define FUNC3 0x82
#define FUNC4 0x83
#define FUNC5 0x84
#define FUNC6 0x85
#define FUNC7 0x86
#define FUNC8 0x87
#define FUNC9 0x88
#define FUNC10 0x89
#define PAGEUP 0xE0
#define PAGEDOWN 0xE1
#define CURHOMEKEY 0xE2
#define ALPHAGRAPHKEY 0xE3
#define NOKEY 0xF0
#define CURSRIGHT 0xF1
#define CURSLEFT 0xF2
#define CURSUP 0xF3
#define CURSDOWN 0xF4
#define DBLZERO 0xF5
#define INSERT 0xF6
#define CLRKEY 0xF7
#define HOMEKEY 0xF8
#define ENDKEY 0xF9
#define ANSITGLKEY 0xFA
#define BREAKKEY 0xFB
#define GRAPHKEY 0xFC
#define ALPHAKEY 0xFD
#define DEBUGKEY 0xFE // Special key to enable debug features such as the ANSI emulation.
// Macros.
//
// The read/write hardware macros are created in order to allow this module to be used with the zSoft/zOS platform
// as well as the FusionX platform. The ZPU writes direct to memory, the FusionX sends via SPI.
#define WRITE_HARDWARE(__force__,__addr__,__data__)\
{\
if(!ctrl.suspendIO || __force__ == 1)\
{\
SPI_SEND32((uint32_t)__addr__ << 16 | __data__ << 8 | CPLD_CMD_WRITE_ADDR);\
}\
}
#define READ_HARDWARE_INIT(__force__,__addr__)\
{\
if(!ctrl.suspendIO || __force__ == 1)\
{\
SPI_SEND32((uint32_t)__addr__ << 16 | 0x00 << 8 | CPLD_CMD_READ_ADDR);\
while(CPLD_READY() == 0);\
}\
}
#define READ_HARDWARE() (\
z80io_PRL_Read8(1)\
)
// Cursor flash mechanism control states.
//
enum CURSOR_STATES {
CURSOR_OFF = 0x00, // Turn the cursor off.
CURSOR_ON = 0x01, // Turn the cursor on.
CURSOR_RESTORE = 0x02, // Restore the saved cursor character.
CURSOR_FLASH = 0x03 // If enabled, flash the cursor.
};
// Cursor positioning states.
enum CURSOR_POSITION {
CURSOR_UP = 0x00, // Move the cursor up.
CURSOR_DOWN = 0x01, // Move the cursor down.
CURSOR_LEFT = 0x02, // Move the cursor left.
CURSOR_RIGHT = 0x03, // Move the cursor right.
CURSOR_COLUMN = 0x04, // Set cursor column to absolute value.
CURSOR_NEXT_LINE = 0x05, // Move the cursor to the beginning of the next line.
CURSOR_PREV_LINE = 0x06, // Move the cursor to the beginning of the previous line.
};
// Keyboard operating states according to buttons pressed.
//
enum KEYBOARD_MODES {
KEYB_LOWERCASE = 0x00, // Keyboard in lower case mode.
KEYB_CAPSLOCK = 0x01, // Keyboard in CAPS lock mode.
KEYB_SHIFTLOCK = 0x02, // Keyboard in SHIFT lock mode.
KEYB_CTRL = 0x03, // Keyboard in Control mode.
KEYB_GRAPHMODE = 0x04, // Keyboard in Graphics mode.
};
// Keyboard dual key modes. This is for hosts whose keyboards dont support the basic key set or when a key needs to have dual functionality.
enum KEYBOARD_DUALMODES {
KEYB_DUAL_NONE = 0x00, // No dual key modes active.
KEYB_DUAL_GRAPH = 0x01, // MZ-80A, no Alpha key, only Graph, so double function required.
};
// Mapping table from Sharp MZ80A Ascii to real Ascii.
//
typedef struct {
uint8_t asciiCode;
} t_asciiMap;
// Mapping table from Ascii to Sharp MZ display code.
//
typedef struct {
uint8_t dispCode;
} t_dispCodeMap;
// Mapping table from keyboard scan codes to Sharp MZ-700 keys.
//
typedef struct {
uint8_t scanCode[80];
} t_scanCodeMap;
// Mapping table of a sharp keycode to an ANSI escape sequence string.
//
typedef struct {
uint8_t key;
const char* ansiKeySequence;
} t_ansiKeyMap;
// Structure to maintain the Sharp MZ display output parameters and data.
//
typedef struct {
uint8_t displayAttr; // Attributes for each character in the display.
uint16_t backingRow; // Maximum backing RAM row, allows for a larger virtual backing display with the physical display acting as a window.
// Location on the physical display to output data. displayCol is also used in the backing store.
uint8_t displayRow;
uint8_t displayCol;
// History and backing display store. The physical display outputs a portion of this backing store.
uint8_t displayCharBuf[VC_DISPLAY_BUFFER_SIZE];
uint8_t displayAttrBuf[VC_DISPLAY_BUFFER_SIZE];
// Maxims, dynamic to allow for future changes.
uint8_t maxBackingRow;
uint8_t maxDisplayRow;
uint8_t maxBackingCol;
// Features.
uint8_t lineWrap; // Wrap line at display edge (1) else stop printing at display edge.
uint8_t useAnsiTerm; // Enable (1) Ansi Terminal Emulator, (0) disable.
uint8_t inDebug; // Prevent recursion when outputting debug information.
} t_displayBuffer;
// Structure for maintaining the Sharp MZ keyboard parameters and data. Used to retrieve and map a key along with associated
// attributes such as cursor flashing.
//
typedef struct {
uint8_t scanbuf[2][10];
uint8_t keydown[10];
uint8_t keyup[10];
uint8_t keyhold[10];
uint32_t holdTimer;
uint8_t breakKey; // Break key pressed.
uint8_t ctrlKey; // Ctrl key pressed.
uint8_t shiftKey; // Shift key pressed.
uint8_t repeatKey;
uint8_t autorepeat;
enum KEYBOARD_MODES mode; // Keyboard mode and index into mapping table for a specific map set.
enum KEYBOARD_DUALMODES dualmode; // Keyboard dual key override modes.
uint8_t keyBuf[MAX_KEYB_BUFFER_SIZE]; // Keyboard buffer.
uint8_t keyBufPtr; // Pointer into the keyboard buffer for stored key,
uint8_t cursorOn; // Flag to indicate Cursor is switched on.
uint8_t displayCursor; // Cursor being displayed = 1
uint32_t flashTimer; // Timer to indicate next flash time for cursor.
} t_keyboard;
// Structure for maintaining the Sharp MZ Audio parameters and data.
typedef struct {
uint32_t audioStopTimer; // Timer to disable audio once elapsed period has expired. Time in ms.
} t_audio;
// Structure for module control parameters.
typedef struct {
uint8_t suspendIO; // Suspend physical I/O.
uint8_t debug; // Enable debugging features.
} t_control;
// Structure to maintain the Ansi Terminal Emulator state and parameters.
//
typedef struct {
enum {
ANSITERM_ESC,
ANSITERM_BRACKET,
ANSITERM_PARSE,
} state; // States and current state of the FSM parser.
uint8_t charcnt; // Number of characters read into the buffer.
uint8_t paramcnt; // Number of parameters parsed and stored.
uint8_t setDisplayMode; // Display mode command detected.
uint8_t setExtendedMode; // Extended mode command detected.
uint8_t charbuf[80]; // Storage for the parameter characters as they are received.
uint16_t param[10]; // Parsed paraemters.
uint8_t saveRow; // Store the current row when requested.
uint8_t saveCol; // Store the current column when requested.
uint8_t saveDisplayRow; // Store the current display buffer row when requested.
} t_AnsiTerm;
// Application execution constants.
//
// Prototypes.
//
uint8_t mzInitMBHardware(void);
uint8_t mzInit(void);
void mzBeep(uint32_t, uint32_t);
uint8_t mzMoveCursor(enum CURSOR_POSITION, uint8_t);
uint8_t mzSetCursor(uint8_t, uint8_t);
int mzPutChar(char);
int mzPutRaw(char);
uint8_t mzSetAnsiAttribute(uint8_t);
int mzAnsiTerm(char);
int mzPrintChar(char);
uint8_t mzFlashCursor(enum CURSOR_STATES);
uint8_t mzPushKey(char *);
int mzGetKey(uint8_t);
int mzGetChar(void);
void mzClearDisplay(uint8_t, uint8_t);
void mzClearLine(int, int, int, uint8_t);
uint8_t mzGetDisplayWidth(void);
uint8_t mzSetDisplayWidth(uint8_t);
uint8_t mzSetMachineVideoMode(uint8_t);
void mzRefreshDisplay(void);
uint8_t mzScrollUp(uint8_t, uint8_t, uint8_t);
uint8_t mzScrollDown(uint8_t);
void mzDebugOut(uint8_t, uint8_t);
void mzSuspendIO(void);
void mzResumeIO(void);
void mzWriteString(uint8_t, uint8_t, char *, int);
void mzService(void);
// Getter/Setter methods!
#ifdef __cplusplus
}
#endif
#endif // SHARPMZ_H

View File

@@ -0,0 +1,826 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: ttymz.c
// Created: Feb 2023
// Author(s): Philip Smart
// Description: Sharp MZ TTY
// This file contains the methods used to create a linux tty device driver using the
// host Sharp keyboard and screen as input/output.
// This driver forms part of the FusionX developments and allows a user sitting at the
// Sharp MZ Console to access the underlying FusionX Linux SOM.
// Credits: Credits to tiny_tty Greg Kroah-Hartman (greg@kroah.com) and Linux pty which were
// used as base and reference in creating this driver.
//
// Copyright: (c) 2019-2023 Philip Smart <philip.smart@net2net.org>
//
// History: Feb 2023 - v1.0 Initial write of the Sharp MZ tty driver software.
// Added suspend I/O logic. This is necessary as I want to enable
// switching between a Linux session and a Z80 session, the idea
// being the TTY will continue to run within the mirrored framebuffer
// and when reselected, refresh the hardware screen.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file is free software: you can redistribute it and#or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#define _GNU_SOURCE
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/fcntl.h>
#include <linux/kprobes.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/major.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/bitops.h>
#include <linux/devpts_fs.h>
#include <linux/mutex.h>
#include <linux/poll.h>
#include <linux/seq_file.h>
#include <linux/version.h>
#include "z80io.h"
#include "sharpmz.h"
#include "ttymz.h"
// Meta Information.
MODULE_LICENSE(DRIVER_LICENSE);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
MODULE_VERSION(DRIVER_VERSION);
MODULE_INFO(versiondate, DRIVER_VERSION_DATE);
MODULE_INFO(copyright, DRIVER_COPYRIGHT);
// Device control variables.
static t_TTYMZ *ttymzConnections[SHARPMZ_TTY_MINORS]; // Initially all NULL, no devices connected.
static struct tty_port ttymzPort[SHARPMZ_TTY_MINORS];
static struct tty_driver *ttymzDriver;
// Read method. Keys entered on the host keyboard are sent to the user process via this method.
//
static void ttymz_read(struct tty_struct *tty, char data)
{
// Locals.
struct tty_port *port;
// Sanity check.
if (!tty)
return;
// Get the port, needed to push data onto the ringbuffer for delivery to the user.
port = tty->port;
// If there is no room, push the characters to the user. Add the new character and push again.
if (!tty_buffer_request_room(port, 1))
tty_flip_buffer_push(port);
tty_insert_flip_char(port, data, TTY_NORMAL);
tty_flip_buffer_push(port);
return;
}
// Method to receive data from the user application to be written onto the Sharp or SSD202 framebuffer.
//
static int ttymz_write(struct tty_struct *tty, const unsigned char *buffer, int count)
{
t_TTYMZ *ttymz = tty->driver_data;
int idx;
int retval = count;
if (!ttymz)
return -ENODEV;
// Lock out other processes.
mutex_lock(&ttymz->mutex);
// Sanity check, ensure port is open.
if (!ttymz->open_count)
goto exit;
// Send the characters to the Sharp MZ interface for display.
for (idx = 0; idx < count; ++idx)
{
mzPrintChar(buffer[idx]);
}
exit:
mutex_unlock(&ttymz->mutex);
return retval;
}
// Method to indicate to the kernel how much buffer space is left in the device.
//
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0))
static int ttymz_write_room(struct tty_struct *tty)
#else
static unsigned int ttymz_write_room(struct tty_struct *tty)
#endif
{
// Locals.
t_TTYMZ *ttymz = tty->driver_data;
int room = -EINVAL;
// Sanity check.
if(!ttymz)
return -ENODEV;
if(tty->stopped)
return 0;
mutex_lock(&ttymz->mutex);
if(!ttymz->open_count)
{
// Port was not opened
goto exit;
}
// Calculate how much room is left in the device
room = 255;
exit:
mutex_unlock(&ttymz->mutex);
return room;
}
// Timer methods
//
// Timer to scan the Sharp MZ host keyboard and detect key presses. Any key press detected results
// in a character being pushed into the kernel ringbuffer for delivery to the user application.
//
static void ttymz_keyboardTimer(unsigned long timerAddr)
{
// Locals.
t_TTYMZ *ttymz = (t_TTYMZ *)timerAddr;
struct tty_struct *tty;
int key;
// Sanity check.
if (!ttymz)
return;
// Once sanitised, get the tty struct from the given physical address, needed to reset the timer and to
// push data to the client.
tty = ttymz->tty;
// Scan the Sharp MZ host keyboard, push any character received. Mode 2 = Ansi scan without wait.
if((key = mzGetKey(2)) != -1)
{
//pr_info("%d ", key);
ttymz_read(tty, (char)key);
}
// Resubmit the timer again.
ttymz->timerKeyboard.expires = jiffies + 1;
add_timer(&ttymz->timerKeyboard);
}
//
// Display service timer, used for scheduling tasks within the display driver.
//
static void ttymz_displayTimer(unsigned long timerAddr)
{
// Locals.
t_TTYMZ *ttymz = (t_TTYMZ *)timerAddr;
struct tty_struct *tty;
// Sanity check.
if (!ttymz)
return;
// Once sanitised, get the tty struct from the given physical address, needed to reset the timer and to
// push data to the client.
tty = ttymz->tty;
// Call the display service routine.
mzService();
// Resubmit the timer again.
ttymz->timerDisplay.expires = jiffies + 1;
add_timer(&ttymz->timerDisplay);
}
// Device open.
//
// When a user space application open's the tty device driver, this function is called
// to initialise and allocate any required memory or hardware prior to servicing requests from the
// user space application.
static int ttymz_open(struct tty_struct *tty, struct file *file)
{
// Locals.
t_TTYMZ *ttymz;
int index;
int ret = 0;
struct winsize ws;
// Initialize the pointer in case something fails
tty->driver_data = NULL;
// Get the serial object associated with this tty pointer
index = tty->index;
ttymz = ttymzConnections[index];
if(ttymz == NULL)
{
// First time accessing this device, let's create it
ttymz = kmalloc(sizeof(*ttymz), GFP_KERNEL);
if (!ttymz)
return -ENOMEM;
mutex_init(&ttymz->mutex);
ttymz->open_count = 0;
ttymzConnections[index] = ttymz;
}
mutex_lock(&ttymz->mutex);
// Save our structure within the tty structure
tty->driver_data = ttymz;
ttymz->tty = tty;
// Setup the default terminal size based on compilation (ie. 40/80 cols).
ws.ws_row = VC_MAX_ROWS;
ws.ws_col = VC_MAX_COLUMNS;
tty->winsize = ws;
// Port opened, perform initialisation.
//
if(++ttymz->open_count == 1)
{
// Create the keyboard sweep timer and submit it
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
init_timer(&ttymz->timerKeyboard);
ttymz->timerKeyboard.data = (unsigned long)ttymz;
ttymz->timerKeyboard.function = ttymz_keyboardTimer;
#else
timer_setup(timer, (void *)ttymz_keyboardTimer, (unsigned long)ttymz);
timer->function = (void *)ttymz_keyboardTimer;
#endif
// 10 ms sweep timer.
ttymz->timerKeyboard.expires = jiffies + 1;
add_timer(&ttymz->timerKeyboard);
// Create the display periodic timer and submit it.
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
init_timer(&ttymz->timerDisplay);
ttymz->timerDisplay.data = (unsigned long)ttymz;
ttymz->timerDisplay.function = ttymz_displayTimer;
#else
timer_setup(timer, (void *)ttymz_keyboardTimer, (unsigned long)ttymz);
timer->function = (void *)ttymz_displayTimer;
#endif
// 10 ms service interval.
ttymz->timerDisplay.expires = jiffies + 1;
add_timer(&ttymz->timerDisplay);
} else
{
// Not allowed to open the port more than once.
ret = -EBUSY;
}
mutex_unlock(&ttymz->mutex);
return(ret);
}
// Close helper method, performs the actual deallocation of resources for the open port.
//
static void do_close(t_TTYMZ *ttymz)
{
// Locals.
mutex_lock(&ttymz->mutex);
if(!ttymz->open_count)
{
// Port was never opened
goto exit;
}
// Shutdown hardware tasks to exit.
if(--ttymz->open_count <= 0)
{
// Shut down our timers.
del_timer(&ttymz->timerKeyboard);
del_timer(&ttymz->timerDisplay);
}
exit:
mutex_unlock(&ttymz->mutex);
}
// Device close.
//
// When a user space application terminates or closes the tty device driver, this function is called
// to close any open connections, memory and variables required to handle the user space application
// requests.
static void ttymz_close(struct tty_struct *tty, struct file *file)
{
// Locals.
t_TTYMZ *ttymz = tty->driver_data;
if (ttymz)
do_close(ttymz);
}
#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
static void ttymz_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
{
// Locals.
unsigned int cflag;
//PRINT_PROC_START();
cflag = tty->termios.c_cflag;
// Check that they really want us to change something
if (old_termios)
{
if ((cflag == old_termios->c_cflag) && (RELEVANT_IFLAG(tty->termios.c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag)))
{
return;
}
}
// Get the byte size
switch(cflag & CSIZE)
{
case CS5:
pr_info(" - data bits = 5\n");
break;
case CS6:
pr_info(" - data bits = 6\n");
break;
case CS7:
pr_info(" - data bits = 7\n");
break;
default:
case CS8:
pr_info(" - data bits = 8\n");
break;
}
// Determine the parity
if (cflag & PARENB)
if (cflag & PARODD)
pr_info(" - parity = odd\n");
else
pr_info(" - parity = even\n");
else
pr_info(" - parity = none\n");
// Figure out the stop bits requested
if (cflag & CSTOPB)
pr_info(" - stop bits = 2\n");
else
pr_info(" - stop bits = 1\n");
// Figure out the hardware flow control settings
if (cflag & CRTSCTS)
pr_info(" - RTS/CTS is enabled\n");
else
pr_info(" - RTS/CTS is disabled\n");
// Determine software flow control
// if we are implementing XON/XOFF, set the start and
// stop character in the device
if (I_IXOFF(tty) || I_IXON(tty))
{
unsigned char stop_char = STOP_CHAR(tty);
unsigned char start_char = START_CHAR(tty);
// If we are implementing INBOUND XON/XOFF
if (I_IXOFF(tty))
pr_info(" - INBOUND XON/XOFF is enabled, "
"XON = %2x, XOFF = %2x", start_char, stop_char);
else
pr_info(" - INBOUND XON/XOFF is disabled");
// if we are implementing OUTBOUND XON/XOFF
if (I_IXON(tty))
pr_info(" - OUTBOUND XON/XOFF is enabled, "
"XON = %2x, XOFF = %2x", start_char, stop_char);
else
pr_info(" - OUTBOUND XON/XOFF is disabled");
}
// Get the baud rate wanted
pr_info(" - baud rate = %d", tty_get_baud_rate(tty));
}
static int ttymz_tiocmget(struct tty_struct *tty)
{
// Locals.
t_TTYMZ *ttymz = tty->driver_data;
unsigned int result = 0;
unsigned int msr = ttymz->msr;
unsigned int mcr = ttymz->mcr;
//PRINT_PROC_START();
result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0) | // DTR is set
((mcr & MCR_RTS) ? TIOCM_RTS : 0) | // RTS is set
((mcr & MCR_LOOP) ? TIOCM_LOOP : 0) | // LOOP is set
((msr & MSR_CTS) ? TIOCM_CTS : 0) | // CTS is set
((msr & MSR_CD) ? TIOCM_CAR : 0) | // Carrier detect is set
((msr & MSR_RI) ? TIOCM_RI : 0) | // Ring Indicator is set
((msr & MSR_DSR) ? TIOCM_DSR : 0); // DSR is set
return result;
}
static int ttymz_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear)
{
// Locals.
t_TTYMZ *ttymz = tty->driver_data;
unsigned int mcr = ttymz->mcr;
//PRINT_PROC_START();
if (set & TIOCM_RTS)
mcr |= MCR_RTS;
if (set & TIOCM_DTR)
mcr |= MCR_RTS;
if (clear & TIOCM_RTS)
mcr &= ~MCR_RTS;
if (clear & TIOCM_DTR)
mcr &= ~MCR_RTS;
// Set the new MCR value in the device
ttymz->mcr = mcr;
return 0;
}
#define ttymz_ioctl ttymz_ioctl_tiocgserial
static int ttymz_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
{
// Locals.
struct serial_struct tmp;
t_TTYMZ *ttymz = tty->driver_data;
//PRINT_PROC_START();
if(cmd == TIOCGSERIAL)
{
if (!arg)
return -EFAULT;
memset(&tmp, 0, sizeof(tmp));
tmp.type = ttymz->serial.type;
tmp.line = ttymz->serial.line;
tmp.port = ttymz->serial.port;
tmp.irq = ttymz->serial.irq;
tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
tmp.xmit_fifo_size = ttymz->serial.xmit_fifo_size;
tmp.baud_base = ttymz->serial.baud_base;
tmp.close_delay = 5*HZ;
tmp.closing_wait = 30*HZ;
tmp.custom_divisor = ttymz->serial.custom_divisor;
tmp.hub6 = ttymz->serial.hub6;
tmp.io_type = ttymz->serial.io_type;
if (copy_to_user((void __user *)arg, &tmp, sizeof(struct serial_struct)))
return -EFAULT;
return 0;
}
return -ENOIOCTLCMD;
}
#undef ttymz_ioctl
#define ttymz_ioctl ttymz_ioctl_tiocmiwait
static int ttymz_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
{
// Locals.
DECLARE_WAITQUEUE(wait, current);
struct async_icount cnow;
struct async_icount cprev;
t_TTYMZ *ttymz = tty->driver_data;
//PRINT_PROC_START();
if (cmd == TIOCMIWAIT)
{
cprev = ttymz->icount;
while (1)
{
add_wait_queue(&ttymz->wait, &wait);
set_current_state(TASK_INTERRUPTIBLE);
schedule();
remove_wait_queue(&ttymz->wait, &wait);
// See if a signal woke us up
if (signal_pending(current))
return -ERESTARTSYS;
cnow = ttymz->icount;
if(cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
return -EIO; // no change => error
if(((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)))
{
return 0;
}
cprev = cnow;
}
}
return -ENOIOCTLCMD;
}
#undef ttymz_ioctl
#define ttymz_ioctl ttymz_ioctl_tiocgicount
static int ttymz_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
{
// Locals.
t_TTYMZ *ttymz = tty->driver_data;
struct async_icount cnow = ttymz->icount;
struct serial_icounter_struct icount;
//PRINT_PROC_START();
if(cmd == TIOCGICOUNT)
{
icount.cts = cnow.cts;
icount.dsr = cnow.dsr;
icount.rng = cnow.rng;
icount.dcd = cnow.dcd;
icount.rx = cnow.rx;
icount.tx = cnow.tx;
icount.frame = cnow.frame;
icount.overrun = cnow.overrun;
icount.parity = cnow.parity;
icount.brk = cnow.brk;
icount.buf_overrun = cnow.buf_overrun;
if (copy_to_user((void __user *)arg, &icount, sizeof(icount)))
return -EFAULT;
return 0;
}
return -ENOIOCTLCMD;
}
#undef ttymz_ioctl
// IOCTL Method
// This method allows User Space application to control the SharpMZ TTY device driver internal functionality.
//
static int ttymz_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
{
// Locals.
//PRINT_PROC_START();
switch(cmd)
{
case TIOCGSERIAL:
return ttymz_ioctl_tiocgserial(tty, cmd, arg);
case TIOCMIWAIT:
return ttymz_ioctl_tiocmiwait(tty, cmd, arg);
case TIOCGICOUNT:
return ttymz_ioctl_tiocgicount(tty, cmd, arg);
// Suspend control. This method stops all physical hardware updates of the host framebuffer and keyboard
// scan whilst maintining the functionality of the tty within the mirrored framebuffer. This mode is
// necessary if the user wishes to switch into a Z80 driver and use the host as original.
case IOCTL_CMD_SUSPEND_IO:
mzSuspendIO();
return(0);
// Resume control. This method re-initialises host hardware, updates the host framebuffer from the mirror
// (refresh) and re-enabled hardware access and keyboard scanning.
case IOCTL_CMD_RESUME_IO:
mzResumeIO();
return(0);
}
return -ENOIOCTLCMD;
}
// Window resize method.
// On the Sharp framebuffer this could be 40/80 chars wide.
// On the SSD202 framebuffer, tba.
static int ttymz_resize(struct tty_struct *tty, struct winsize *ws)
{
PRINT_PROC_START();
pr_info("Resize to:%d,%d\n", ws->ws_row, ws->ws_col);
// Check columns and setup the display according to requirement.
if(ws->ws_col == 40)
{
ws->ws_row = VC_MAX_ROWS;
} else
if(ws->ws_col == 80)
{
ws->ws_row = VC_MAX_ROWS;
} else
{
// Ignore all other values.
return -EINVAL;
}
// Setup the hardware to accommodate new column width.
mzSetDisplayWidth(ws->ws_col);
tty->winsize = *ws;
return 0;
}
//static void ttymz_remove(struct tty_driver *driver, struct tty_struct *tty)
//{
// // Locals.
//
// PRINT_PROC_START();
//}
static void ttymz_cleanup(struct tty_struct *tty)
{
// tty_port_put(tty->port);
}
static void ttymz_flush_buffer(struct tty_struct *tty)
{
// Locals.
//PRINT_PROC_START();
}
// pty_chars_in_buffer - characters currently in our tx queue
//
// Report how much we have in the transmit queue. As everything is
// instantly at the other end this is easy to implement.
//
static int ttymz_chars_in_buffer(struct tty_struct *tty)
{
return 0;
}
// The unthrottle routine is called by the line discipline to signal
// that it can receive more characters. For PTY's, the TTY_THROTTLED
// flag is always set, to force the line discipline to always call the
// unthrottle routine when there are fewer than TTY_THRESHOLD_UNTHROTTLE
// characters in the queue. This is necessary since each time this
// happens, we need to wake up any sleeping processes that could be
// (1) trying to send data to the pty, or (2) waiting in wait_until_sent()
// for the pty buffer to be drained.
//
static void ttymz_unthrottle(struct tty_struct *tty)
{
//PRINT_PROC_START();
tty_wakeup(tty->link);
set_bit(TTY_THROTTLED, &tty->flags);
}
// Structure to declare public API methods.
// Standard Linux device driver structure to declare accessible methods within the driver.
static const struct tty_operations serial_ops = {
//.install = ttymz_install,
.open = ttymz_open,
.close = ttymz_close,
.write = ttymz_write,
.write_room = ttymz_write_room,
.flush_buffer = ttymz_flush_buffer,
.chars_in_buffer = ttymz_chars_in_buffer,
.unthrottle = ttymz_unthrottle,
.set_termios = ttymz_set_termios,
.tiocmget = ttymz_tiocmget,
.tiocmset = ttymz_tiocmset,
.ioctl = ttymz_ioctl,
.cleanup = ttymz_cleanup,
.resize = ttymz_resize,
// .remove = ttymz_remove
};
// Initialisation.
// This is the entry point into the device driver when loaded into the kernel.
// The method intialises any required hardware (ie. GPIO's, SPI etc), memory.
// It also allocates the Major and Minor device numbers and sets up the device in /dev.
static int __init ttymz_init(void)
{
// Locals.
int retval;
int i;
char buf[80];
// Allocate the tty driver handles, one per potential device.
ttymzDriver = alloc_tty_driver(SHARPMZ_TTY_MINORS);
if(!ttymzDriver)
return -ENOMEM;
// Initialize the tty driver
ttymzDriver->owner = THIS_MODULE;
ttymzDriver->driver_name = DRIVER_NAME;
ttymzDriver->name = DEVICE_NAME;
ttymzDriver->major = SHARPMZ_TTY_MAJOR,
ttymzDriver->type = TTY_DRIVER_TYPE_SERIAL,
ttymzDriver->subtype = SERIAL_TYPE_NORMAL,
ttymzDriver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV,
ttymzDriver->init_termios = tty_std_termios;
ttymzDriver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
tty_set_operations(ttymzDriver, &serial_ops);
for (i = 0; i < SHARPMZ_TTY_MINORS; i++)
{
tty_port_init(ttymzPort + i);
tty_port_link_device(ttymzPort + i, ttymzDriver, i);
}
// Register the tty driver
retval = tty_register_driver(ttymzDriver);
if(retval)
{
pr_err("Failed to register SharpMZ tty driver");
put_tty_driver(ttymzDriver);
return retval;
}
// Register the devices.
for (i = 0; i < SHARPMZ_TTY_MINORS; ++i)
tty_register_device(ttymzDriver, i, NULL);
// Initialise the hardware to host interface.
z80io_init();
// Initialise the Sharp MZ interface.
mzInit();
// Sign on.
sprintf(buf, "%s %s", DRIVER_DESCRIPTION, DRIVER_VERSION); mzWriteString(0, 0, buf, -1);
sprintf(buf, "%s %s\n", DRIVER_COPYRIGHT, DRIVER_AUTHOR); mzWriteString(0, 1, buf, -1);
pr_info(DRIVER_DESCRIPTION " " DRIVER_VERSION "\n");
return retval;
}
// Exit
// This method is called when the device driver is removed from the kernel with the rmmod command.
// It is responsible for closing and freeing all allocated memory, disabling hardware and removing
// the device from the /dev directory.
static void __exit ttymz_exit(void)
{
// Locals.
t_TTYMZ *ttymz;
int idx;
// De-register the devices and driver.
for(idx = 0; idx < SHARPMZ_TTY_MINORS; ++idx)
{
tty_unregister_device(ttymzDriver, idx);
tty_port_destroy(ttymzPort + idx);
}
tty_unregister_driver(ttymzDriver);
// Shut down all of the timers and free the memory.
for(idx = 0; idx < SHARPMZ_TTY_MINORS; ++idx)
{
ttymz = ttymzConnections[idx];
if (ttymz)
{
// Close the port.
while (ttymz->open_count)
do_close(ttymz);
// Shut down our timer and free the memory.
del_timer(&ttymz->timerKeyboard);
del_timer(&ttymz->timerDisplay);
kfree(ttymz);
ttymzConnections[idx] = NULL;
}
}
pr_info("ttymz: unregistered!\n");
}
module_init(ttymz_init);
module_exit(ttymz_exit);

99
software/FusionX/src/tty/ttymz.h vendored Normal file
View File

@@ -0,0 +1,99 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: ttymz.h
// Created: Feb 2023
// Author(s): Philip Smart
// Description: Sharp MZ TTY
// This file contains the definitions required by the linux tty device driver ttymz.c.
// This driver forms part of the FusionX developments and allows a user sitting at the
// Sharp MZ Console to access the underlying FusionX Linux SOM.
// Credits:
// Copyright: (c) 2019-2023 Philip Smart <philip.smart@net2net.org>
//
// History: Feb 2023 - v1.0 Initial write of the Sharp MZ tty driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file is free software: you can redistribute it and#or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef TTYMZ_H
#define TTYMZ_H
// Constants.
#define DRIVER_LICENSE "GPL"
#define DRIVER_AUTHOR "Philip D Smart"
#define DRIVER_DESCRIPTION "Sharp MZ TTY Driver"
#define DRIVER_VERSION "v1.01"
#define DRIVER_VERSION_DATE "Mar 2023"
#define DRIVER_COPYRIGHT "(C) 2018-2023"
#define DEVICE_NAME "ttymz"
#define DRIVER_NAME "SharpMZ_tty"
#define DEBUG_ENABLED 0 // 0 = disabled, 1 .. debug level.
// Fake UART values
#define MCR_DTR 0x01
#define MCR_RTS 0x02
#define MCR_LOOP 0x04
#define MSR_CTS 0x08
#define MSR_CD 0x10
#define MSR_RI 0x20
#define MSR_DSR 0x40
// IOCTL commands. Passed from user space using the IOCTL method to command the driver to perform an action.
#define IOCTL_CMD_SUSPEND_IO _IOW('s', 's', int32_t *)
#define IOCTL_CMD_RESUME_IO _IOW('r', 'r', int32_t *)
#define SHARPMZ_TTY_MAJOR 240 // Experimental range
#define SHARPMZ_TTY_MINORS 2 // Assign 2 devices, Sharp VRAM and SigmaStar SSD202 Framebuffer.
// Macros.
#define from_timer(var, callback_timer, timer_fieldname) container_of(callback_timer, typeof(*var), timer_fieldname)
#define PRINT_PROC_START() do { pr_info("Start: %s\n", __func__); } while (0)
#define PRINT_PROC_EXIT() do { pr_info("Finish: %s\n", __func__); } while (0)
typedef struct {
struct tty_struct *tty; // pointer to the tty for this device
int open_count; // number of times this port has been opened
struct mutex mutex; // locks this structure
struct timer_list timerKeyboard; // Keyboard sweep timer.
struct timer_list timerDisplay; // Display service timer.
/* for tiocmget and tiocmset functions */
int msr; // MSR shadow
int mcr; // MCR shadow
/* for ioctl fun */
struct serial_struct serial;
wait_queue_head_t wait;
struct async_icount icount;
} t_TTYMZ;
#if(DEBUG_ENABLED != 0)
struct debug {
uint8_t level;
};
#endif
struct ioctlCmd {
int32_t cmd;
union {
#if(DEBUG_ENABLED != 0)
struct debug debug;
#endif
};
};
// Prototypes.
#endif

View File

@@ -0,0 +1,452 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80io.c
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 IO Interface
// This file contains the methods used in interfacing the SOM to the Z80 socket
// and host hardware via a CPLD.
// Credits:
// Copyright: (c) 2019-2023 Philip Smart <philip.smart@net2net.org>
//
// History: Oct 2022 v1.0 - Initial write of the z80 kernel driver software.
// Jan 2023 v1.1 - Numerous new tries at increasing throughput to the CPLD failed.
// Maximum read throughput of an 8bit byte due to the SSD202 GPIO
// structure is approx 2MB/s - or 512K/s for a needed 32bit word.
// Write is slower as you have to clock the data so sticking with SPI.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file is free software: you can redistribute it and#or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//#include <stdio.h>
//#include <stdlib.h>
//#include <string.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/time.h>
#include "z80io.h"
#include <gpio_table.h>
#include <asm/io.h>
#include <infinity2m/gpio.h>
#include <infinity2m/registers.h>
//-------------------------------------------------------------------------------------------------------------------------------
//
// User space driver access.
//
//-------------------------------------------------------------------------------------------------------------------------------
// Initialise the SOM hardware used to communicate with the z80 socket and host hardware.
// The SOM interfaces to a CPLD which provides voltage level translation and also encapsulates the Z80 timing cycles as recreating
// them within the SOM is much more tricky.
//
// As this is an embedded device and performance/latency are priorities, minimal structured code is used to keep call stack and
// generated code to a mimimum without relying on the optimiser.
int z80io_init(void)
{
// Locals.
int ret = 0;
// Initialise GPIO. We call the HAL api to minimise time but for actual bit set/reset and read we go directly to registers to save time, increase throughput and minimise latency.
// Initialise the HAL.
MHal_GPIO_Init();
// Set the pads as GPIO devices. The HAL takes care of allocating and deallocating the padmux resources.
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_0); // Word (16bit) bidirectional bus. Default is read with data set.
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_1);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_2);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_3);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_4);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_5);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_6);
MHal_GPIO_Pad_Set(PAD_Z80IO_IN_DATA_7);
MHal_GPIO_Pad_Set(PAD_Z80IO_HIGH_BYTE);
//MHal_GPIO_Pad_Set(PAD_GPIO8); // SPIO 4wire control lines setup by the spidev driver but controlled directly in this driver.
//MHal_GPIO_Pad_Set(PAD_GPIO9);
//MHal_GPIO_Pad_Set(PAD_GPIO10);
//MHal_GPIO_Pad_Set(PAD_GPIO11);
MHal_GPIO_Pad_Set(PAD_Z80IO_READY);
MHal_GPIO_Pad_Set(PAD_Z80IO_LTSTATE);
MHal_GPIO_Pad_Set(PAD_Z80IO_BUSRQ);
MHal_GPIO_Pad_Set(PAD_Z80IO_BUSACK);
MHal_GPIO_Pad_Set(PAD_Z80IO_INT);
MHal_GPIO_Pad_Set(PAD_Z80IO_NMI);
MHal_GPIO_Pad_Set(PAD_Z80IO_WAIT);
MHal_GPIO_Pad_Set(PAD_Z80IO_RESET);
MHal_GPIO_Pad_Set(PAD_Z80IO_RSV1);
#ifdef NOTNEEDED
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_0);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_1);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_2);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_3);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_4);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_5);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_6);
MHal_GPIO_Pad_Set(PAD_Z80IO_OUT_DATA_7);
MHal_GPIO_Pad_Set(PAD_Z80IO_WRITE);
#endif
// Set required input pads.
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_0);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_1);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_2);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_3);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_4);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_5);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_6);
MHal_GPIO_Pad_Odn(PAD_Z80IO_IN_DATA_7);
MHal_GPIO_Pad_Odn(PAD_Z80IO_READY);
MHal_GPIO_Pad_Odn(PAD_Z80IO_LTSTATE);
MHal_GPIO_Pad_Odn(PAD_Z80IO_BUSRQ);
MHal_GPIO_Pad_Odn(PAD_Z80IO_BUSACK);
MHal_GPIO_Pad_Odn(PAD_Z80IO_INT);
MHal_GPIO_Pad_Odn(PAD_Z80IO_NMI);
MHal_GPIO_Pad_Odn(PAD_Z80IO_WAIT);
MHal_GPIO_Pad_Odn(PAD_Z80IO_RESET);
MHal_GPIO_Pad_Odn(PAD_Z80IO_RSV1);
// Set required output pads.
#ifdef NOTNEEDED
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_0);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_1);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_2);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_3);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_4);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_5);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_6);
MHal_GPIO_Pad_Oen(PAD_Z80IO_OUT_DATA_7);
MHal_GPIO_Pad_Oen(PAD_Z80IO_WRITE);
MHal_GPIO_Pull_High(PAD_Z80IO_WRITE);
#endif
// Control signals.
MHal_GPIO_Pad_Oen(PAD_Z80IO_HIGH_BYTE);
MHal_GPIO_Pull_High(PAD_Z80IO_HIGH_BYTE);
// Setup the MSPI0 device.
//
// Setup control, interrupts are not used.
MSPI_WRITE(MSPI_CTRL_OFFSET, MSPI_CPU_CLOCK_1_2 | MSPI_CTRL_CPOL_LOW | MSPI_CTRL_CPHA_HIGH | MSPI_CTRL_RESET | MSPI_CTRL_ENABLE_SPI);
// Setup LSB First mode.
MSPI_WRITE(MSPI_LSB_FIRST_OFFSET, 0x0);
// Setup clock.
CLK_WRITE(MSPI0_CLK_CFG, 0x1100)
// Setup the frame size (all buffers to 8bits).
MSPI_WRITE(MSPI_FRAME_WBIT_OFFSET, 0xfff);
MSPI_WRITE(MSPI_FRAME_WBIT_OFFSET+1, 0xfff);
MSPI_WRITE(MSPI_FRAME_RBIT_OFFSET, 0xfff);
MSPI_WRITE(MSPI_FRAME_RBIT_OFFSET+1, 0xfff);
// Setup Chip Selects to inactive.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE);
// Switch Video and Audio to host.
z80io_SPI_Send16(0x00f0, NULL);
return ret;
}
//--------------------------------------------------------
// Parallel bus Methods.
//--------------------------------------------------------
// Methods to read data from the parallel bus.
// The CPLD returns status and Z80 data on the 8bit bus as it is marginally quicker than retrieving it over the SPI bus.
//
inline uint8_t z80io_PRL_Read8(uint8_t dataFlag)
{
// Locals.
volatile uint8_t result = 0;
// Byte according to flag.
if(dataFlag)
SET_CPLD_READ_DATA()
else
SET_CPLD_READ_STATUS()
// Read the input registers and set value accordingly.
result = READ_CPLD_DATA_IN();
// Return 16bit value read from CPLD.
return(result);
}
inline uint8_t z80io_PRL_Read(void)
{
// Locals.
volatile uint8_t result = 0;
volatile uint32_t b7, b6, b5, b4, b3, b2, b1, b0;
// Read the input registers and set value accordingly. Quicker to read registers and then apply shift/logical operators. The I/O Bus is very slow!
b7 = READ_LONG(((gRIUBaseAddr) + (((PAD_Z80IO_IN_DATA_7_ADDR) & ~1)<<1) + (PAD_Z80IO_IN_DATA_7_ADDR & 1)));
b6 = READ_LONG(((gRIUBaseAddr) + (((PAD_Z80IO_IN_DATA_6_ADDR) & ~1)<<1) + (PAD_Z80IO_IN_DATA_6_ADDR & 1)));
b5 = READ_LONG(((gRIUBaseAddr) + (((PAD_Z80IO_IN_DATA_5_ADDR) & ~1)<<1) + (PAD_Z80IO_IN_DATA_5_ADDR & 1)));
b4 = READ_LONG(((gRIUBaseAddr) + (((PAD_Z80IO_IN_DATA_4_ADDR) & ~1)<<1) + (PAD_Z80IO_IN_DATA_4_ADDR & 1)));
b3 = READ_LONG(((gRIUBaseAddr) + (((PAD_Z80IO_IN_DATA_3_ADDR) & ~1)<<1) + (PAD_Z80IO_IN_DATA_3_ADDR & 1)));
b2 = READ_LONG(((gRIUBaseAddr) + (((PAD_Z80IO_IN_DATA_2_ADDR) & ~1)<<1) + (PAD_Z80IO_IN_DATA_2_ADDR & 1)));
b1 = READ_LONG(((gRIUBaseAddr) + (((PAD_Z80IO_IN_DATA_1_ADDR) & ~1)<<1) + (PAD_Z80IO_IN_DATA_1_ADDR & 1)));
b0 = READ_LONG(((gRIUBaseAddr) + (((PAD_Z80IO_IN_DATA_0_ADDR) & ~1)<<1) + (PAD_Z80IO_IN_DATA_0_ADDR & 1)));
result = (b7 & 0x1) << 7 | (b6 & 0x1) << 6 | (b5 & 0x1) << 5 | (b4 & 0x1) << 4 | (b3 & 0x1) << 3 | (b2 & 0x1) << 2 | (b1 & 0x1) << 1 | (b0 & 0x1);
// Return 16bit value read from CPLD.
return(result);
}
inline uint16_t z80io_PRL_Read16(void)
{
// Locals.
volatile uint16_t result = 0;
// Low byte first.
CLEAR_CPLD_HIGH_BYTE();
// Read the input registers and set value accordingly.
result = (uint16_t)READ_CPLD_DATA_IN();
// High byte next.
SET_CPLD_HIGH_BYTE();
// Read the input registers and set value accordingly.
result |= (uint16_t)(READ_CPLD_DATA_IN() << 8);
// Return 16bit value read from CPLD.
return(result);
}
// Parallel Bus methods were tried and tested but due to the GPIO bits being controlled by individual registers per bit, the setup time was longer
// than the transmission time of SPI. These methods are thus deprecated and a fusion of SPI and 8bit parallel is now used.
#ifdef NOTNEEDED
inline uint8_t z80io_PRL_Send8(uint8_t txData)
{
// Locals.
//
// Low byte only.
MHal_RIU_REG(gpio_table[PAD_Z80IO_HIGH_BYTE].r_out) &= (~gpio_table[PAD_Z80IO_HIGH_BYTE ].m_out);
// Setup data.
if(txData & 0x0080) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_7].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_7].m_out); }
if(txData & 0x0040) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_6].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_6].m_out); }
if(txData & 0x0020) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_5].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_5].m_out); }
if(txData & 0x0010) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_4].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_4].m_out); }
if(txData & 0x0008) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_3].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_3].m_out); }
if(txData & 0x0004) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_2].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_2].m_out); }
if(txData & 0x0002) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_1].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_1].m_out); }
if(txData & 0x0001) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_0].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_0].m_out); }
// Clock data.
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) &= (~gpio_table[PAD_Z80IO_WRITE ].m_out);
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) |= gpio_table[PAD_Z80IO_WRITE ].m_out;
return(0);
}
inline uint8_t z80io_PRL_Send16(uint16_t txData)
{
// Locals.
//
// Low byte first.
MHal_RIU_REG(gpio_table[PAD_Z80IO_HIGH_BYTE].r_out) &= (~gpio_table[PAD_Z80IO_HIGH_BYTE ].m_out);
// Setup data.
if(txData & 0x0080) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_7].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_7].m_out); }
if(txData & 0x0040) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_6].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_6].m_out); }
if(txData & 0x0020) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_5].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_5].m_out); }
if(txData & 0x0010) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_4].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_4].m_out); }
if(txData & 0x0008) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_3].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_3].m_out); }
if(txData & 0x0004) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_2].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_2].m_out); }
if(txData & 0x0002) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_1].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_1].m_out); }
if(txData & 0x0001) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_0].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_0].m_out); }
// Clock data.
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) &= (~gpio_table[PAD_Z80IO_WRITE ].m_out);
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) |= gpio_table[PAD_Z80IO_WRITE ].m_out;
// High byte next.
MHal_RIU_REG(gpio_table[PAD_Z80IO_HIGH_BYTE ].r_out) |= gpio_table[PAD_Z80IO_HIGH_BYTE ].m_out;
// Setup high byte.
if(txData & 0x8000) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_7].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_7].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_7].m_out); }
if(txData & 0x4000) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_6].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_6].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_6].m_out); }
if(txData & 0x2000) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_5].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_5].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_5].m_out); }
if(txData & 0x1000) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_4].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_4].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_4].m_out); }
if(txData & 0x0800) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_3].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_3].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_3].m_out); }
if(txData & 0x0400) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_2].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_2].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_2].m_out); }
if(txData & 0x0200) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_1].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_1].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_1].m_out); }
if(txData & 0x0100) { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) |= gpio_table[PAD_Z80IO_OUT_DATA_0].m_out; } else { MHal_RIU_REG(gpio_table[PAD_Z80IO_OUT_DATA_0].r_out) &= (~gpio_table[PAD_Z80IO_OUT_DATA_0].m_out); }
// Clock data.
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) &= (~gpio_table[PAD_Z80IO_WRITE ].m_out);
MHal_RIU_REG(gpio_table[PAD_Z80IO_WRITE].r_out) |= gpio_table[PAD_Z80IO_WRITE ].m_out;
return(0);
}
#endif
//--------------------------------------------------------
// SPI Methods.
//--------------------------------------------------------
// Methods to send 8,16 or 32 bits. Each method is seperate to minimise logic and execution time, 8bit being most sensitive.
// Macros have also been defined for inline inclusion which dont read back the response data.
//
uint8_t z80io_SPI_Send8(uint8_t txData, uint8_t *rxData)
{
// Locals.
uint32_t timeout = MAX_CHECK_CNT;
// Insert data into write buffers.
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)txData);
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 1);
// Enable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE);
// Send.
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER);
// Wait for completion.
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0)
{
if(--timeout == 0)
break;
}
// Disable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE);
// Clear flag.
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);
// Fetch data.
if(rxData != NULL) *rxData = (uint8_t)MSPI_READ(MSPI_FULL_DEPLUX_RD00);
// Done.
return(timeout == 0);
}
uint8_t z80io_SPI_Send16(uint16_t txData, uint16_t *rxData)
{
// Locals.
uint32_t timeout = MAX_CHECK_CNT;
// Insert data into write buffers.
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, txData);
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2);
// Enable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE);
// Send.
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER);
// Wait for completion.
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0)
{
if(--timeout == 0)
break;
}
// Disable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE);
// Clear flag.
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);
// Fetch data.
if(rxData != NULL) *rxData = MSPI_READ(MSPI_FULL_DEPLUX_RD00);
// Done.
return(timeout == 0);
}
uint8_t z80io_SPI_Send32(uint32_t txData, uint32_t *rxData)
{
// Locals.
uint32_t timeout = MAX_CHECK_CNT;
// Insert data into write buffers.
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)txData);
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)(txData >> 16));
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4);
// Enable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE);
// Send.
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER);
// Wait for completion.
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0)
{
if(--timeout == 0)
break;
}
// Disable SPI select.
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE);
// Clear flag.
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);
// Fetch data.
if(rxData != NULL) *rxData = (uint32_t)(MSPI_READ(MSPI_FULL_DEPLUX_RD00) | (MSPI_READ(MSPI_FULL_DEPLUX_RD02) << 16));
// Done.
return(timeout == 0);
}
//--------------------------------------------------------
// Test Methods.
//--------------------------------------------------------
#ifdef INCLUDE_TEST_METHODS
#include "z80io_test.c"
#else
uint8_t z80io_Z80_TestMemory(void)
{
pr_info("Z80 Test Memory functionality not built-in.\n");
return(0);
}
uint8_t z80io_SPI_Test(void)
{
pr_info("SPI Test functionality not built-in.\n");
return(0);
}
uint8_t z80io_PRL_Test(void)
{
pr_info("Parallel Bus Test functionality not built-in.\n");
return(0);
}
#endif

507
software/FusionX/src/tty/z80io.h vendored Executable file
View File

@@ -0,0 +1,507 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80io.h
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 IO Interface
// This file contains the declarations used in interfacing the SOM to the Z80 socket
// and host hardware via a CPLD.
// Credits:
// Copyright: (c) 2019-2023 Philip Smart <philip.smart@net2net.org>
//
// History: Oct 2022 v1.0 - Initial write of the z80 kernel driver software.
// Jan 2023 v1.1 - Numerous new tries at increasing throughput to the CPLD failed.
// Maximum read throughput of an 8bit byte due to the SSD202 GPIO
// structure is approx 2MB/s - or 512K/s for a needed 32bit word.
// Write is slower as you have to clock the data so sticking with SPI.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file is free software: you can redistribute it and#or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef Z80IO_H
#define Z80IO_H
#ifdef __cplusplus
extern "C" {
#endif
// Definitions to control compilation.
//#define INCLUDE_TEST_METHODS 0
// CPLD Commands.
#define CPLD_CMD_FETCH_ADDR 0x10
#define CPLD_CMD_FETCH_ADDR_P1 0x11
#define CPLD_CMD_FETCH_ADDR_P2 0x12
#define CPLD_CMD_FETCH_ADDR_P3 0x13
#define CPLD_CMD_FETCH_ADDR_P4 0x14
#define CPLD_CMD_FETCH_ADDR_P5 0x15
#define CPLD_CMD_FETCH_ADDR_P6 0x16
#define CPLD_CMD_FETCH_ADDR_P7 0x17
#define CPLD_CMD_WRITE_ADDR 0x18
#define CPLD_CMD_WRITE_ADDR_P1 0x19
#define CPLD_CMD_WRITE_ADDR_P2 0x1A
#define CPLD_CMD_WRITE_ADDR_P3 0x1B
#define CPLD_CMD_WRITE_ADDR_P4 0x1C
#define CPLD_CMD_WRITE_ADDR_P5 0x1D
#define CPLD_CMD_WRITE_ADDR_P6 0x1E
#define CPLD_CMD_WRITE_ADDR_P7 0x1F
#define CPLD_CMD_READ_ADDR 0x20
#define CPLD_CMD_READ_ADDR_P1 0x21
#define CPLD_CMD_READ_ADDR_P2 0x22
#define CPLD_CMD_READ_ADDR_P3 0x23
#define CPLD_CMD_READ_ADDR_P4 0x24
#define CPLD_CMD_READ_ADDR_P5 0x25
#define CPLD_CMD_READ_ADDR_P6 0x26
#define CPLD_CMD_READ_ADDR_P7 0x27
#define CPLD_CMD_WRITEIO_ADDR 0x28
#define CPLD_CMD_WRITEIO_ADDR_P1 0x29
#define CPLD_CMD_WRITEIO_ADDR_P2 0x2A
#define CPLD_CMD_WRITEIO_ADDR_P3 0x2B
#define CPLD_CMD_WRITEIO_ADDR_P4 0x2C
#define CPLD_CMD_WRITEIO_ADDR_P5 0x2D
#define CPLD_CMD_WRITEIO_ADDR_P6 0x2E
#define CPLD_CMD_WRITEIO_ADDR_P7 0x2F
#define CPLD_CMD_READIO_ADDR 0x30
#define CPLD_CMD_READIO_ADDR_P1 0x31
#define CPLD_CMD_READIO_ADDR_P2 0x32
#define CPLD_CMD_READIO_ADDR_P3 0x33
#define CPLD_CMD_READIO_ADDR_P4 0x34
#define CPLD_CMD_READIO_ADDR_P5 0x35
#define CPLD_CMD_READIO_ADDR_P6 0x36
#define CPLD_CMD_READIO_ADDR_P7 0x37
#define CPLD_CMD_HALT 0x50
#define CPLD_CMD_REFRESH 0x51
#define CPLD_CMD_SET_SIGROUP1 0xF0
#define CPLD_CMD_SET_AUTO_REFRESH 0xF1
#define CPLD_CMD_CLEAR_AUTO_REFRESH 0xF2
#define CPLD_CMD_SET_SPI_LOOPBACK 0xFE
#define CPLD_CMD_NOP1 0x00
#define CPLD_CMD_NOP2 0xFF
// Pad numbers for using the MHal GPIO library.
#define PAD_Z80IO_IN_DATA_0 PAD_GPIO0
#define PAD_Z80IO_IN_DATA_1 PAD_GPIO1
#define PAD_Z80IO_IN_DATA_2 PAD_GPIO2
#define PAD_Z80IO_IN_DATA_3 PAD_GPIO3
#define PAD_Z80IO_IN_DATA_4 PAD_GPIO4
#define PAD_Z80IO_IN_DATA_5 PAD_GPIO5
#define PAD_Z80IO_IN_DATA_6 PAD_GPIO6
#define PAD_Z80IO_IN_DATA_7 PAD_GPIO7
#define PAD_SPIO_0 PAD_GPIO8
#define PAD_SPIO_1 PAD_GPIO9
#define PAD_SPIO_2 PAD_GPIO10
#define PAD_SPIO_3 PAD_GPIO11
#define PAD_Z80IO_HIGH_BYTE PAD_SAR_GPIO2 // Byte requiured, 0 = Low Byte, 1 = High Byte.
#define PAD_Z80IO_READY PAD_GPIO12
#define PAD_Z80IO_LTSTATE PAD_UART0_RX // GPIO47
#define PAD_Z80IO_BUSRQ PAD_GPIO13
#define PAD_Z80IO_BUSACK PAD_GPIO14
#define PAD_Z80IO_INT PAD_PM_IRIN // IRIN
#define PAD_Z80IO_NMI PAD_UART0_TX // GPIO48
#define PAD_Z80IO_WAIT PAD_HSYNC_OUT // GPIO85
#define PAD_Z80IO_RESET PAD_VSYNC_OUT // GPIO86
#define PAD_Z80IO_RSV1 PAD_SATA_GPIO // GPIO90
// Physical register addresses.
#define PAD_Z80IO_IN_DATA_0_ADDR 0x103C00
#define PAD_Z80IO_IN_DATA_1_ADDR 0x103C02
#define PAD_Z80IO_IN_DATA_2_ADDR 0x103C04
#define PAD_Z80IO_IN_DATA_3_ADDR 0x103C06
#define PAD_Z80IO_IN_DATA_4_ADDR 0x103C08
#define PAD_Z80IO_IN_DATA_5_ADDR 0x103C0A
#define PAD_Z80IO_IN_DATA_6_ADDR 0x103C0C
#define PAD_Z80IO_IN_DATA_7_ADDR 0x103C0E
#define PAD_SPIO_0_ADDR 0x103C10
#define PAD_SPIO_1_ADDR 0x103C12
#define PAD_SPIO_2_ADDR 0x103C14
#define PAD_SPIO_3_ADDR 0x103C16
#define PAD_Z80IO_HIGH_BYTE_ADDR 0x1425
#define PAD_Z80IO_READY_ADDR 0x103C18
#define PAD_Z80IO_LTSTATE_ADDR 0x103C30 // GPIO47
#define PAD_Z80IO_BUSRQ_ADDR 0x103C1A
#define PAD_Z80IO_BUSACK_ADDR 0x103C1C
#define PAD_Z80IO_INT_ADDR 0xF28 // IRIN
#define PAD_Z80IO_NMI_ADDR 0x103C32 // GPIO48
#define PAD_Z80IO_WAIT_ADDR 0x103C80 // GPIO85
#define PAD_Z80IO_RESET_ADDR 0x103C82 // GPIO86
#define PAD_Z80IO_RSV1_ADDR 0x103C8A // GPIO90
#ifdef NOTNEEDED
#define PAD_Z80IO_OUT_DATA_0 PAD_GPIO12
#define PAD_Z80IO_OUT_DATA_1 PAD_GPIO13
#define PAD_Z80IO_OUT_DATA_2 PAD_GPIO14
#define PAD_Z80IO_OUT_DATA_3 PAD_UART0_RX // GPIO47
#define PAD_Z80IO_OUT_DATA_4 PAD_UART0_TX // GPIO48
#define PAD_Z80IO_OUT_DATA_5 PAD_HSYNC_OUT // GPIO85
#define PAD_Z80IO_OUT_DATA_6 PAD_VSYNC_OUT // GPIO86
#define PAD_Z80IO_OUT_DATA_7 PAD_SATA_GPIO // GPIO90
#define PAD_Z80IO_WRITE PAD_PM_IRIN // Write data clock.
#endif
//-------------------------------------------------------------------------------------------------
// The definitions below come from SigmaStar kernel drivers. No header file exists hence the
// duplication.
//-------------------------------------------------------------------------------------------------
#define SUPPORT_SPI_1 0
#define MAX_SUPPORT_BITS 16
#define BANK_TO_ADDR32(b) (b<<9)
#define BANK_SIZE 0x200
#define MS_BASE_REG_RIU_PA 0x1F000000
#define gChipBaseAddr 0xFD203C00
#define gPmSleepBaseAddr 0xFD001C00
#define gSarBaseAddr 0xFD002800
#define gRIUBaseAddr 0xFD000000
#define gMOVDMAAddr 0xFD201600
#define gClkBaseAddr 0xFD207000
#define gMspBaseAddr 0xfd222000
#define MHal_CHIPTOP_REG(addr) (*(volatile U8*)((gChipBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_PM_SLEEP_REG(addr) (*(volatile U8*)((gPmSleepBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_SAR_GPIO_REG(addr) (*(volatile U8*)((gSarBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_RIU_REG(addr) (*(volatile U8*)((gRIUBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MSPI0_BANK_ADDR 0x1110
#define MSPI1_BANK_ADDR 0x1111
#define CLK__BANK_ADDR 0x1038
#define CHIPTOP_BANK_ADDR 0x101E
#define MOVDMA_BANK_ADDR 0x100B
#define BASE_REG_MSPI0_ADDR MSPI0_BANK_ADDR*0x200 //GET_BASE_ADDR_BY_BANK(IO_ADDRESS(MS_BASE_REG_RIU_PA), 0x111000)
#define BASE_REG_MSPI1_ADDR MSPI1_BANK_ADDR*0x200 //GET_BASE_ADDR_BY_BANK(IO_ADDRESS(MS_BASE_REG_RIU_PA), 0x111100)
#define BASE_REG_CLK_ADDR CLK__BANK_ADDR*0x200 //GET_BASE_ADDR_BY_BANK(IO_ADDRESS(MS_BASE_REG_RIU_PA), 0x103800)
#define BASE_REG_CHIPTOP_ADDR CHIPTOP_BANK_ADDR*0x200 //GET_BASE_ADDR_BY_BANK(IO_ADDRESS(MS_BASE_REG_RIU_PA), 0x101E00)
//-------------------------------------------------------------------------------------------------
// Hardware Register Capability
//-------------------------------------------------------------------------------------------------
#define MSPI_WRITE_BUF_OFFSET 0x40
#define MSPI_READ_BUF_OFFSET 0x44
#define MSPI_WBF_SIZE_OFFSET 0x48
#define MSPI_RBF_SIZE_OFFSET 0x48
// read/ write buffer size
#define MSPI_RWSIZE_MASK 0xFF
#define MSPI_RSIZE_BIT_OFFSET 0x8
#define MAX_READ_BUF_SIZE 0x8
#define MAX_WRITE_BUF_SIZE 0x8
// CLK config
#define MSPI_CTRL_OFFSET 0x49
#define MSPI_CLK_CLOCK_OFFSET 0x49
#define MSPI_CLK_CLOCK_BIT_OFFSET 0x08
#define MSPI_CLK_CLOCK_MASK 0xFF
#define MSPI_CLK_PHASE_MASK 0x40
#define MSPI_CLK_PHASE_BIT_OFFSET 0x06
#define MSPI_CLK_POLARITY_MASK 0x80
#define MSPI_CLK_POLARITY_BIT_OFFSET 0x07
#define MSPI_CLK_PHASE_MAX 0x1
#define MSPI_CLK_POLARITY_MAX 0x1
#define MSPI_CLK_CLOCK_MAX 0x7
#define MSPI_CTRL_CPOL_LOW 0x00
#define MSPI_CTRL_CPOL_HIGH 0x80
#define MSPI_CTRL_CPHA_LOW 0x00
#define MSPI_CTRL_CPHA_HIGH 0x40
#define MSPI_CTRL_3WIRE 0x10
#define MSPI_CTRL_INTEN 0x04
#define MSPI_CTRL_RESET 0x02
#define MSPI_CTRL_ENABLE_SPI 0x01
// DC config
#define MSPI_DC_MASK 0xFF
#define MSPI_DC_BIT_OFFSET 0x08
#define MSPI_DC_TR_START_OFFSET 0x4A
#define MSPI_DC_TRSTART_MAX 0xFF
#define MSPI_DC_TR_END_OFFSET 0x4A
#define MSPI_DC_TREND_MAX 0xFF
#define MSPI_DC_TB_OFFSET 0x4B
#define MSPI_DC_TB_MAX 0xFF
#define MSPI_DC_TRW_OFFSET 0x4B
#define MSPI_DC_TRW_MAX 0xFF
// Frame Config
#define MSPI_FRAME_WBIT_OFFSET 0x4C
#define MSPI_FRAME_RBIT_OFFSET 0x4E
#define MSPI_FRAME_BIT_MAX 0x07
#define MSPI_FRAME_BIT_MASK 0x07
#define MSPI_FRAME_BIT_FIELD 0x03
#define MSPI_LSB_FIRST_OFFSET 0x50
#define MSPI_TRIGGER_OFFSET 0x5A
#define MSPI_DONE_OFFSET 0x5B
#define MSPI_DONE_CLEAR_OFFSET 0x5C
#define MSPI_CHIP_SELECT_OFFSET 0x5F
#define MSPI_CS1_DISABLE 0x01
#define MSPI_CS1_ENABLE 0x00
#define MSPI_CS2_DISABLE 0x02
#define MSPI_CS2_ENABLE 0x00
#define MSPI_CS3_DISABLE 0x04
#define MSPI_CS3_ENABLE 0x00
#define MSPI_CS4_DISABLE 0x08
#define MSPI_CS4_ENABLE 0x00
#define MSPI_CS5_DISABLE 0x10
#define MSPI_CS5_ENABLE 0x00
#define MSPI_CS6_DISABLE 0x20
#define MSPI_CS6_ENABLE 0x00
#define MSPI_CS7_DISABLE 0x40
#define MSPI_CS7_ENABLE 0x00
#define MSPI_CS8_DISABLE 0x80
#define MSPI_CS8_ENABLE 0x00
#define MSPI_FULL_DEPLUX_RD_CNT (0x77)
#define MSPI_FULL_DEPLUX_RD00 (0x78)
#define MSPI_FULL_DEPLUX_RD01 (0x78)
#define MSPI_FULL_DEPLUX_RD02 (0x79)
#define MSPI_FULL_DEPLUX_RD03 (0x79)
#define MSPI_FULL_DEPLUX_RD04 (0x7a)
#define MSPI_FULL_DEPLUX_RD05 (0x7a)
#define MSPI_FULL_DEPLUX_RD06 (0x7b)
#define MSPI_FULL_DEPLUX_RD07 (0x7b)
#define MSPI_FULL_DEPLUX_RD08 (0x7c)
#define MSPI_FULL_DEPLUX_RD09 (0x7c)
#define MSPI_FULL_DEPLUX_RD10 (0x7d)
#define MSPI_FULL_DEPLUX_RD11 (0x7d)
#define MSPI_FULL_DEPLUX_RD12 (0x7e)
#define MSPI_FULL_DEPLUX_RD13 (0x7e)
#define MSPI_FULL_DEPLUX_RD14 (0x7f)
#define MSPI_FULL_DEPLUX_RD15 (0x7f)
//chip select bit map
#define MSPI_CHIP_SELECT_MAX 0x07
// control bit
#define MSPI_DONE_FLAG 0x01
#define MSPI_TRIGGER 0x01
#define MSPI_CLEAR_DONE 0x01
#define MSPI_INT_ENABLE 0x04
#define MSPI_RESET 0x02
#define MSPI_ENABLE 0x01
// clk_mspi0
#define MSPI0_CLK_CFG 0x33 //bit 2 ~bit 3
#define MSPI0_CLK_108M 0x00
#define MSPI0_CLK_54M 0x04
#define MSPI0_CLK_12M 0x08
#define MSPI0_CLK_MASK 0x0F
// clk_mspi1
#define MSPI1_CLK_CFG 0x33 //bit 10 ~bit 11
#define MSPI1_CLK_108M 0x0000
#define MSPI1_CLK_54M 0x0400
#define MSPI1_CLK_12M 0x0800
#define MSPI1_CLK_MASK 0x0F00
// clk_mspi
#define MSPI_CLK_CFG 0x33
#define MSPI_SELECT_0 0x0000
#define MSPI_SELECT_1 0x4000
#define MSPI_CLK_MASK 0xF000
// Clock settings
#define MSPI_CPU_CLOCK_1_2 0x0000
#define MSPI_CPU_CLOCK_1_4 0x0100
#define MSPI_CPU_CLOCK_1_8 0x0200
#define MSPI_CPU_CLOCK_1_16 0x0300
#define MSPI_CPU_CLOCK_1_32 0x0400
#define MSPI_CPU_CLOCK_1_64 0x0500
#define MSPI_CPU_CLOCK_1_128 0x0600
#define MSPI_CPU_CLOCK_1_256 0x0700
//CHITOP 101E mspi mode select
#define MSPI0_MODE 0x0C //bit0~bit1
#define MSPI0_MODE_MASK 0x07
#define MSPI1_MODE 0x0C //bit4~bit5
#define MSPI1_MODE_MASK 0x70
#define EJTAG_MODE 0xF
#define EJTAG_MODE_1 0x01
#define EJTAG_MODE_2 0x02
#define EJTAG_MODE_3 0x03
#define EJTAG_MODE_MASK 0x03
//MOVDMA 100B
#define MOV_DMA_SRC_ADDR_L 0x03
#define MOV_DMA_SRC_ADDR_H 0x04
#define MOV_DMA_DST_ADDR_L 0x05
#define MOV_DMA_DST_ADDR_H 0x06
#define MOV_DMA_BYTE_CNT_L 0x07
#define MOV_DMA_BYTE_CNT_H 0x08
#define DMA_MOVE0_IRQ_CLR 0x28
#define MOV_DMA_IRQ_FINAL_STATUS 0x2A
#define DMA_MOVE0_ENABLE 0x00
#define DMA_RW 0x50 //0 for dma write to device, 1 for dma read from device
#define DMA_READ 0x01
#define DMA_WRITE 0x00
#define DMA_DEVICE_MODE 0x51
#define DMA_DEVICE_SEL 0x52
//spi dma
#define MSPI_DMA_DATA_LENGTH_L 0x30
#define MSPI_DMA_DATA_LENGTH_H 0x31
#define MSPI_DMA_ENABLE 0x32
#define MSPI_DMA_RW_MODE 0x33
#define MSPI_DMA_WRITE 0x00
#define MSPI_DMA_READ 0x01
#define MSTAR_SPI_TIMEOUT_MS 30000
#define MSTAR_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA /*| SPI_CS_HIGH | SPI_NO_CS | SPI_LSB_FIRST*/)
//-------------------------------------------------------------------------------------------------
// Macros
//-------------------------------------------------------------------------------------------------
#define MHal_CHIPTOP_REG(addr) (*(volatile U8*)((gChipBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_PM_SLEEP_REG(addr) (*(volatile U8*)((gPmSleepBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_SAR_GPIO_REG(addr) (*(volatile U8*)((gSarBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define MHal_RIU_REG(addr) (*(volatile U8*)((gRIUBaseAddr) + (((addr) & ~1)<<1) + (addr & 1)))
#define READ_BYTE(_reg) (*(volatile u8*)(_reg))
#define READ_WORD(_reg) (*(volatile u16*)(_reg))
#define READ_LONG(_reg) (*(volatile u32*)(_reg))
#define WRITE_BYTE(_reg, _val) {(*((volatile u8*)(_reg))) = (u8)(_val); }
#define WRITE_WORD(_reg, _val) {(*((volatile u16*)(_reg))) = (u16)(_val); }
#define WRITE_LONG(_reg, _val) {(*((volatile u32*)(_reg))) = (u32)(_val); }
#define WRITE_WORD_MASK(_reg, _val, _mask) {(*((volatile u16*)(_reg))) = ((*((volatile u16*)(_reg))) & ~(_mask)) | ((u16)(_val) & (_mask)); }
#define READ_CPLD_DATA_IN() ((MHal_RIU_REG(PAD_Z80IO_IN_DATA_7_ADDR) & 0x1) << 7 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_6_ADDR) & 0x1) << 6 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_5_ADDR) & 0x1) << 5 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_4_ADDR) & 0x1) << 4 |\
(MHal_RIU_REG(PAD_Z80IO_IN_DATA_3_ADDR) & 0x1) << 3 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_2_ADDR) & 0x1) << 2 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_1_ADDR) & 0x1) << 1 | (MHal_RIU_REG(PAD_Z80IO_IN_DATA_0_ADDR) & 0x1))
#define SET_CPLD_READ_DATA() {MHal_RIU_REG(PAD_Z80IO_HIGH_BYTE_ADDR) |= 0x4;}
#define SET_CPLD_READ_STATUS() {MHal_RIU_REG(PAD_Z80IO_HIGH_BYTE_ADDR) &= ~0x4;}
#define SET_CPLD_HIGH_BYTE() {MHal_RIU_REG(PAD_Z80IO_HIGH_BYTE_ADDR) |= 0x4;}
#define CLEAR_CPLD_HIGH_BYTE() {MHal_RIU_REG(PAD_Z80IO_HIGH_BYTE_ADDR) &= ~0x4;}
#define CPLD_READY() (MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1)
#define CPLD_RESET() (MHal_RIU_REG(PAD_Z80IO_RESET_ADDR) & 0x1)
#define CPLD_LAST_TSTATE() (MHal_RIU_REG(PAD_Z80IO_LTSTATE_ADDR) & 0x4)
#define CPLD_Z80_INT() (MHal_RIU_REG(PAD_Z80IO_INT_ADDR) & 0x4)
#define CPLD_Z80_NMI() (MHal_RIU_REG(PAD_Z80IO_NMI_ADDR) & 0x4)
#define SPI_SEND8(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 1); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE);\
}
#define SPI_SEND16(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 2); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
}
#define SPI_SEND32(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)((_d_) >> 16)); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; } \
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
}
#define SPI_SEND32i(_d_) { uint32_t timeout = MAX_CHECK_CNT; \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET, (uint16_t)(_d_)); \
MSPI_WRITE(MSPI_WRITE_BUF_OFFSET+1, (uint16_t)((_d_) >> 16)); \
MSPI_WRITE(MSPI_WBF_SIZE_OFFSET, 4); \
pr_info("Stage 0");\
while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0);\
pr_info("Stage 1");\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_ENABLE); \
MSPI_WRITE(MSPI_TRIGGER_OFFSET, MSPI_TRIGGER); \
pr_info("Stage 2");\
timeout = MAX_CHECK_CNT; \
while((MSPI_READ(MSPI_DONE_OFFSET) & MSPI_DONE_FLAG) == 0) { if(--timeout == 0) break; }; \
pr_info("Stage 3");\
MSPI_WRITE(MSPI_CHIP_SELECT_OFFSET, MSPI_CS8_DISABLE | MSPI_CS7_DISABLE | MSPI_CS6_DISABLE | MSPI_CS5_DISABLE | MSPI_CS4_DISABLE | MSPI_CS3_DISABLE | MSPI_CS2_DISABLE | MSPI_CS1_DISABLE); \
MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET, MSPI_CLEAR_DONE); \
}
// while((MHal_RIU_REG(PAD_Z80IO_READY_ADDR) & 0x1) == 0) { if(--timeout == 0) break; };
// read 2 byte
#define MSPI_READ(_reg_) READ_WORD(gMspBaseAddr + ((_reg_)<<2))
// write 2 byte
//#define MSPI_WRITE(_reg_, _val_) {pr_info("PDS: MSPI_WRITE(0x%x, 0x%x, 0x%x)\n", _reg_, _val_, gMspBaseAddr + ((_reg_)<<2)); WRITE_WORD(gMspBaseAddr + ((_reg_)<<2), (_val_)); }
#define MSPI_WRITE(_reg_, _val_) WRITE_WORD(gMspBaseAddr + ((_reg_)<<2), (_val_));
//write 2 byte mask
//#define MSPI_WRITE_MASK(_reg_, _val_, mask) {pr_info("PDS: WRITE_LONG(0x%x, 0x%x, mask=0x%x)\n", _reg_, _val_, mask); WRITE_WORD_MASK(gMspBaseAddr + ((_reg_)<<2), (_val_), (mask)); }
#define MSPI_WRITE_MASK(_reg_, _val_, mask) WRITE_WORD_MASK(gMspBaseAddr + ((_reg_)<<2), (_val_), (mask));
#define CLK_READ(_reg_) READ_WORD(gClkBaseAddr + ((_reg_)<<2))
//#define CLK_WRITE(_reg_, _val_) {pr_info("PDS: CLK_WRITE(0x%x, 0x%x)\n", _reg_, _val_); WRITE_WORD(gClkBaseAddr + ((_reg_)<<2), (_val_)); }
#define CLK_WRITE(_reg_, _val_) WRITE_WORD(gClkBaseAddr + ((_reg_)<<2), (_val_));
#define CHIPTOP_READ(_reg_) READ_WORD(gChipBaseAddr + ((_reg_)<<2))
//#define CHIPTOP_WRITE(_reg_, _val_) {pr_info("PDS: CHIPTOP_WRITE(0x%x, 0x%x)\n", _reg_, _val_); WRITE_WORD(gChipBaseAddr + ((_reg_)<<2), (_val_)); }
#define CHIPTOP_WRITE(_reg_, _val_) WRITE_WORD(gChipBaseAddr + ((_reg_)<<2), (_val_));
#define MOVDMA_READ(_reg_) READ_WORD(gMOVDMAAddr + ((_reg_)<<2))
//#define MOVDMA_WRITE(_reg_, _val_) {pr_info("PDS: MOVDMA_WRITE(0x%x, 0x%x)\n", _reg_, _val_); WRITE_WORD(gMOVDMAAddr + ((_reg_)<<2), (_val_)); }
#define MOVDMA_WRITE(_reg_, _val_) WRITE_WORD(gMOVDMAAddr + ((_reg_)<<2), (_val_));
#define _HAL_MSPI_ClearDone() MSPI_WRITE(MSPI_DONE_CLEAR_OFFSET,MSPI_CLEAR_DONE)
#define MAX_CHECK_CNT 5000
#define MSPI_READ_INDEX 0x0
#define MSPI_WRITE_INDEX 0x1
#define SPI_MIU0_BUS_BASE 0x20000000
#define SPI_MIU1_BUS_BASE 0xFFFFFFFF
// Function definitions.
//
int z80io_init(void);
uint8_t z80io_SPI_Send8(uint8_t txData, uint8_t *rxData);
uint8_t z80io_SPI_Send16(uint16_t txData, uint16_t *rxData);
uint8_t z80io_SPI_Send32(uint32_t txData, uint32_t *rxData);
#ifdef NOTNEEDED
uint8_t z80io_PRL_Send8(uint8_t txData);
uint8_t z680io_PRL_Send16(uint16_t txData);
#endif
uint8_t z80io_PRL_Read(void);
uint8_t z80io_PRL_Read8(uint8_t dataFlag);
uint16_t z80io_PRL_Read16(void);
uint8_t z80io_SPI_Test(void);
uint8_t z80io_PRL_Test(void);
uint8_t z80io_Z80_TestMemory(void);
extern void MHal_GPIO_Init(void);
extern void MHal_GPIO_Pad_Set(uint8_t u8IndexGPIO);
extern int MHal_GPIO_PadGroupMode_Set(uint32_t u32PadMode);
extern int MHal_GPIO_PadVal_Set(uint8_t u8IndexGPIO, uint32_t u32PadMode);
extern void MHal_GPIO_Pad_Oen(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Pad_Odn(uint8_t u8IndexGPIO);
extern uint8_t MHal_GPIO_Pad_Level(uint8_t u8IndexGPIO);
extern uint8_t MHal_GPIO_Pad_InOut(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Pull_High(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Pull_Low(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Set_High(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Set_Low(uint8_t u8IndexGPIO);
extern void MHal_Enable_GPIO_INT(uint8_t u8IndexGPIO);
extern int MHal_GPIO_To_Irq(uint8_t u8IndexGPIO);
extern void MHal_GPIO_Set_POLARITY(uint8_t u8IndexGPIO, uint8_t reverse);
extern void MHal_GPIO_Set_Driving(uint8_t u8IndexGPIO, uint8_t setHigh);
extern void MHal_GPIO_PAD_32K_OUT(uint8_t u8Enable);
#ifdef __cplusplus
}
#endif
#endif // Z80IO_H

View File

@@ -0,0 +1,541 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: z80io_test.c
// Created: Oct 2022
// Author(s): Philip Smart
// Description: Z80 IO Interface Test Methods
// This file contains the methods used to test the SOM to CPLD interface and evaluate
// it's performance. Production builds wont include these methods.
// Credits:
// Copyright: (c) 2019-2022 Philip Smart <philip.smart@net2net.org>
//
// History: Oct 2022 - Initial write of the z80 kernel driver software.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file is free software: you can redistribute it and#or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/semaphore.h>
#include <linux/kthread.h>
#include <linux/sched.h>
//--------------------------------------------------------
// Test Methods.
//--------------------------------------------------------
uint8_t z80io_Z80_TestMemory(void)
{
// Locals.
//
uint32_t addr;
uint32_t fullCmd;
uint8_t cmd;
struct timeval start, stop;
uint32_t iterations = 100;
uint32_t errorCount;
uint32_t idx;
long totalTime;
long bytesMSec;
uint8_t result;
spinlock_t spinLock;
unsigned long flags;
SPI_SEND8(CPLD_CMD_CLEAR_AUTO_REFRESH);
SPI_SEND32(0x00E30000 | (0x07 << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00E80000 | (0x82 << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00E20000 | (0x58 << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00E00000 | (0xF7 << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00E90000 | (0x0F << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00EB0000 | (0xCF << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
SPI_SEND32(0x00EB0000 | (0xFF << 8) | CPLD_CMD_WRITEIO_ADDR);
udelay(100);
pr_info("Z80 Host Test - IO.\n");
// for(idx=0; idx < 1000000; idx++)
// {
// SPI_SEND32(0x00E80000 | (0xD3 << 8) | CPLD_CMD_WRITEIO_ADDR);
// SPI_SEND32(0xD0000000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
// SPI_SEND32(0xD0100000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
// SPI_SEND32(0xD0200000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
// SPI_SEND32(0xD0300000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
// SPI_SEND32(0xD0400000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
// SPI_SEND32(0xD0500000 | (0x41 << 8) | CPLD_CMD_WRITE_ADDR);
// }
spin_lock_init(&spinLock);
pr_info("Z80 Host Test - Testing IO Write performance.\n");
do_gettimeofday(&start);
spin_lock_irqsave(&spinLock, flags);
for(idx=0; idx < iterations; idx++)
{
for(addr=0x0000; addr < 0x10000; addr++)
{
fullCmd = 0x00000000| ((uint8_t)addr) << 8 | CPLD_CMD_WRITEIO_ADDR;
SPI_SEND32(fullCmd);
}
}
spin_unlock_irqrestore(&spinLock, flags);
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
spin_lock_init(&spinLock);
pr_info("Z80 Host Test - Testing IO Read performance.\n");
do_gettimeofday(&start);
spin_lock_irqsave(&spinLock, flags);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible IO ports and write to it.
for(addr=0x0000; addr < 0x10000; addr++)
{
fullCmd = 0x00000000 | ((uint8_t)addr) << 8 | CPLD_CMD_READIO_ADDR;
SPI_SEND32(fullCmd);
}
}
spin_unlock_irqrestore(&spinLock, flags);
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
spin_lock_init(&spinLock);
pr_info("Z80 Host Test - Testing RAM Write performance.\n");
do_gettimeofday(&start);
spin_lock_irqsave(&spinLock, flags);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and write to it.
for(addr=0x1000; addr < 0xD000; addr++)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x18;
SPI_SEND32(fullCmd);
}
}
spin_unlock_irqrestore(&spinLock, flags);
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
pr_info("Z80 Host Test - Testing RAM Write performance (opt).\n");
do_gettimeofday(&start);
spin_lock_irqsave(&spinLock, flags);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and write to it.
for(addr=0x1000; addr < 0xD000; addr++)
{
if(addr == 0x1000)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x18;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x19;
SPI_SEND16(((uint8_t)addr) << 8 | cmd);
}
}
}
spin_unlock_irqrestore(&spinLock, flags);
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
pr_info("Z80 Host Test - Testing RAM Write/Fetch performance (opt).\n");
errorCount = 0;
SET_CPLD_READ_DATA();
//MHal_RIU_REG(gpio_table[PAD_Z80IO_HIGH_BYTE ].r_out) |= gpio_table[PAD_Z80IO_HIGH_BYTE ].m_out;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and write to it.
for(addr=0x8000; addr < 0xD000; addr++)
{
if(addr == 0x8000)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x18;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x19;
SPI_SEND16(((uint8_t)addr) << 8 | cmd);
}
// Read back the same byte.
cmd = 0x10;
SPI_SEND8(cmd);
while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN();
if(result != (uint8_t)addr)
{
if(errorCount < 50) pr_info("Read byte:0x%x, Written:0x%x\n", result, (uint8_t)addr);
errorCount++;
}
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, errorCount=%d, %ldBytes/sec\n", totalTime/1000, errorCount, (bytesMSec*1000));
pr_info("Z80 Host Test - Testing RAM Write/Read performance (opt).\n");
errorCount = 0;
SET_CPLD_READ_DATA();
//MHal_RIU_REG(gpio_table[PAD_Z80IO_HIGH_BYTE ].r_out) |= gpio_table[PAD_Z80IO_HIGH_BYTE ].m_out;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and write to it.
for(addr=0x8000; addr < 0xD000; addr++)
{
if(addr == 0x8000)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x18;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x19;
SPI_SEND16(((uint8_t)addr) << 8 | cmd);
}
// Read back the same byte.
cmd = 0x20;
SPI_SEND8(cmd);
while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN();
if(result != (uint8_t)addr)
{
if(errorCount < 50) pr_info("Read byte:0x%x, Written:0x%x\n", result, (uint8_t)addr);
errorCount++;
}
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, errorCount=%d, %ldBytes/sec\n", totalTime/1000, errorCount, (bytesMSec*1000));
pr_info("Z80 Host Test - Testing RAM Fetch performance.\n");
SET_CPLD_READ_DATA();
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and read from it.
for(addr=0x1000; addr < 0xD000; addr++)
{
if(addr == 0x1000)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x10;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x11;
SPI_SEND8(cmd);
}
while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN();
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
pr_info("Z80 Host Test - Testing RAM Read performance (opt).\n");
SET_CPLD_READ_DATA();
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible RAM and read from it.
for(addr=0x1000; addr < 0xD000; addr++)
{
if(addr == 0x1000)
{
fullCmd = (addr << 16) | ((uint8_t)addr) << 8 | 0x20;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x21;
SPI_SEND8(cmd);
}
while(CPLD_READY() == 0);
result = READ_CPLD_DATA_IN();
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations*0xC000)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
// Go through all the accessible attribute VRAM and initialise it.
pr_info("Z80 Host Test - Testing VRAM Write performance.\n");
SPI_SEND32(0x00E80000 | (0xD3 << 8) | CPLD_CMD_WRITEIO_ADDR);
iterations = 256*10;
do_gettimeofday(&start);
for(addr=0xD800; addr < 0xE000; addr++)
{
//while(CPLD_READY() == 0);
if(addr == 0xD800)
{
fullCmd = (addr << 16) |(0x71 << 8) | 0x18;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x19;
SPI_SEND8(cmd);
}
}
for(idx=0; idx < iterations; idx++)
{
// Go through all the accessible VRAM and write to it.
for(addr=0xD000; addr < 0xD800; addr++)
{
//while(CPLD_READY() == 0);
if(addr == 0xD000)
{
fullCmd = (addr << 16) | ((uint8_t)idx << 8) | 0x18;
SPI_SEND32(fullCmd);
} else
{
cmd = 0x19;
SPI_SEND8(cmd);
}
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)((1*iterations*0x800)+0x800)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
return(0);
}
// A simple test to verify the SOM to CPLD SPI connectivity and give an estimate of its performance.
// The performance is based on the SPI setup and transmit time along with the close and received data processing.
// In real use, the driver will just send a command and generally ignore received data so increased throughput can be achieved.
//
uint8_t z80io_SPI_Test(void)
{
// Locals.
//
struct timeval start, stop;
uint32_t iterations = 10000000;
uint32_t idx;
uint8_t rxData8;
uint16_t rxData16;
uint16_t rxData16Last;
uint32_t rxData32;
uint32_t rxData32Last;
uint32_t errorCount;
long totalTime;
long bytesMSec;
// Place the CPLD into echo test mode.
z80io_SPI_Send8(0xfe, &rxData8);
// 1st. test, 8bit.
pr_info("SPI Test - Testing 8 bit performance.\n");
errorCount=0;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
z80io_SPI_Send8((uint8_t)idx, &rxData8);
if(idx > 1 && (uint8_t)(idx-1) != rxData8)
{
if(errorCount < 20)
pr_info("0x%x: Last(0x%x) /= New(0x%x)\n",idx, (uint8_t)(idx-1), rxData8 );
errorCount++;
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(1*iterations)/((long)totalTime/1000);
pr_info("Loop mode errorCount: %d, time=%ldms, %ldBytes/sec\n", errorCount, totalTime/1000, (bytesMSec*1000));
// 2nd. test, 16bit.
pr_info("SPI Test - Testing 16 bit performance.\n");
errorCount=0;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Byte re-ordering required as the CPLD echo's back the last 8bits received, it doesnt know if a transmission is 8/16/32bits.
z80io_SPI_Send16((uint16_t)idx, &rxData16);
if(idx > 0 && (uint16_t)(idx-1) != (uint16_t)(((rxData16&0x00ff) << 8) | ((rxData16Last & 0xff00) >> 8)))
{
if(errorCount < 20)
pr_info("0x%x: Last(0x%x) /= New(0x%x)\n",idx, (uint16_t)(idx-1), (uint16_t)(((rxData16&0x00ff) << 8) | ((rxData16Last & 0xff00) >> 8)));
errorCount++;
}
rxData16Last = rxData16;
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(2*iterations)/((long)totalTime/1000);
pr_info("Loop mode errorCount: %d, time=%ldms, %ldBytes/sec\n", errorCount, totalTime/1000, (bytesMSec*1000));
// 3rd. test, 32bit.
pr_info("SPI Test - Testing 32 bit performance.\n");
errorCount=0;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
z80io_SPI_Send32((uint32_t)idx, &rxData32);
if(idx > 0 && (uint32_t)(idx-1) != (uint32_t)(((rxData32&0x00ff) << 8) | ((rxData32Last & 0xff000000) >> 8) | ((rxData32Last & 0xff0000) >> 8) | ((rxData32Last & 0xff00) >> 8)))
{
if(errorCount < 20)
pr_info("0x%x: Last(0x%x) /= New(0x%x)\n",idx, (uint32_t)(idx-1), (uint32_t)(((rxData32&0x00ff) << 8) | ((rxData32Last & 0xff000000) >> 8) | ((rxData32Last & 0xff0000) >> 8) | ((rxData32Last & 0xff00) >> 8)));
errorCount++;
}
rxData32Last = rxData32;
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(4*iterations)/((long)totalTime/1000);
pr_info("Loop mode errorCount: %d, time=%ldms, %ldBytes/sec\n", errorCount, totalTime/1000, (bytesMSec*1000));
pr_info("Press host RESET button Once to reset the CPLD.\n");
return(0);
}
// Method to test the parallel bus, verifying integrity and assessing performance.
uint8_t z80io_PRL_Test(void)
{
// Locals.
//
struct timeval start, stop;
uint32_t iterations = 10000000;
uint32_t idx;
uint8_t rxData8;
uint16_t rxData16;
long totalTime;
long bytesMSec;
#ifdef NOTNEEDED
uint32_t errorCount;
#endif
// Place the CPLD into echo test mode.
// 1st. test, 8bit RW.
#ifdef NOTNEEDED
pr_info("Parallel Test - Testing 8 bit r/w performance.\n");
errorCount=0;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Write byte and readback to compare.
z80io_PRL_Send8((uint8_t)idx);
rxData8 = z80io_PRL_Read8();
if((uint8_t)idx != rxData8)
{
pr_info("0x%x: Written(0x%x) /= Read(0x%x)\n", idx, (uint8_t)(idx), rxData8);
errorCount++;
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(iterations)/((long)totalTime/1000);
pr_info("Loop mode errorCount: %d, time=%ldms, %ldBytes/sec\n", errorCount, totalTime/1000, (bytesMSec*1000));
// 2nd. test, 8bit Write.
pr_info("Parallel Test - Testing 8 bit write performance.\n");
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Write byte.
z80io_PRL_Send8((uint8_t)idx);
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(iterations)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
#endif
// 3rd. test, 8bit Read.
pr_info("Parallel Test - Testing 8 bit read performance.\n");
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Read byte.
rxData8 = z80io_PRL_Read8(0);
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(iterations)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
#ifdef NOTNEEDED
// 4th test, 16bit.
pr_info("Parallel Test - Testing 16 bit r/w performance.\n");
errorCount=0;
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Byte re-ordering required as the CPLD echo's back the last 8bits received, it doesnt know if a transmission is 8/16/32bits.
z80io_PRL_Send16((uint16_t)idx);
rxData16 = z80io_PRL_Read16();
if((uint16_t)idx != rxData16)
{
pr_info("0x%x: Written(0x%x) /= Read(0x%x)\n", idx, (uint16_t)(idx), rxData16);
errorCount++;
}
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(2*iterations)/((long)totalTime/1000);
pr_info("Loop mode errorCount: %d, time=%ldms, %ldBytes/sec\n", errorCount, totalTime/1000, (bytesMSec*1000));
// 5th test, 16bit Write.
pr_info("Parallel Test - Testing 16 bit write performance.\n");
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Write word.
z80io_PRL_Send16((uint16_t)idx);
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(2*iterations)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
#endif
// 6th test, 16bit Read.
pr_info("Parallel Test - Testing 16 bit read performance.\n");
do_gettimeofday(&start);
for(idx=0; idx < iterations; idx++)
{
// Read word.
rxData16 = z80io_PRL_Read16();
}
do_gettimeofday(&stop);
totalTime = (stop.tv_sec - start.tv_sec) * 1000000 + stop.tv_usec - start.tv_usec;
bytesMSec = (long)(2*iterations)/((long)totalTime/1000);
pr_info("Loop mode time=%ldms, %ldBytes/sec\n", totalTime/1000, (bytesMSec*1000));
pr_info("Press host RESET button Once to reset the CPLD.\n");
return(0);
}