Updates for the Sharp MZ TTY driver
This commit is contained in:
@@ -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
33
software/FusionX/src/tty/Makefile
vendored
Normal 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
|
||||
3238
software/FusionX/src/tty/sharpmz.c
Normal file
3238
software/FusionX/src/tty/sharpmz.c
Normal file
File diff suppressed because it is too large
Load Diff
412
software/FusionX/src/tty/sharpmz.h
vendored
Executable file
412
software/FusionX/src/tty/sharpmz.h
vendored
Executable 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
|
||||
826
software/FusionX/src/tty/ttymz.c
Normal file
826
software/FusionX/src/tty/ttymz.c
Normal 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
99
software/FusionX/src/tty/ttymz.h
vendored
Normal 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
|
||||
452
software/FusionX/src/tty/z80io.c
Normal file
452
software/FusionX/src/tty/z80io.c
Normal 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
507
software/FusionX/src/tty/z80io.h
vendored
Executable 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
|
||||
541
software/FusionX/src/tty/z80io_test.c
Normal file
541
software/FusionX/src/tty/z80io_test.c
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user