nuclear@2: /* nuclear@2: gbasys - a gameboy advance hardware abstraction library nuclear@2: Copyright (C) 2005-2012 John Tsiombikas nuclear@2: nuclear@2: This program is free software: you can redistribute it and/or modify nuclear@2: it under the terms of the GNU General Public License as published by nuclear@2: the Free Software Foundation, either version 3 of the License, or nuclear@2: (at your option) any later version. nuclear@2: nuclear@2: This program is distributed in the hope that it will be useful, nuclear@2: but WITHOUT ANY WARRANTY; without even the implied warranty of nuclear@2: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nuclear@2: GNU General Public License for more details. nuclear@2: nuclear@2: You should have received a copy of the GNU General Public License nuclear@2: along with this program. If not, see . nuclear@2: */ nuclear@2: nuclear@2: #include "comm.h" nuclear@2: #include "error.h" nuclear@2: #include "signal.h" nuclear@2: #include "intr.h" nuclear@2: nuclear@2: #define REG_SIODATA32 *((volatile unsigned short*)0x4000120) nuclear@2: #define REG_SIOCNT *((volatile unsigned short*)0x4000128) nuclear@2: #define REG_SIODATA8 *((volatile unsigned short*)0x400012a) nuclear@2: #define REG_RCNT *((volatile unsigned short*)0x4000134) nuclear@2: nuclear@2: /* REG_SIOCNT bits */ nuclear@2: #define SIOCNT_CLK_INTERN (1 << 0) nuclear@2: #define SIOCNT_CLK_2MHZ (1 << 1) nuclear@2: #define SIOCNT_RECV_ENABLE (1 << 2) nuclear@2: #define SIOCNT_SEND_ENABLE (1 << 3) nuclear@2: #define SIOCNT_START (1 << 7) nuclear@2: #define SIOCNT_XFER_LEN (1 << 12) nuclear@2: #define SIOCNT_INT_ENABLE (1 << 14) nuclear@2: nuclear@2: /* only in 16bit multi player mode */ nuclear@2: #define SIOCNT_BAUD_38400 1 nuclear@2: #define SIOCNT_BAUD_57600 2 nuclear@2: #define SIOCNT_BAUD_115200 3 nuclear@2: #define SIOCNT_SI_STATUS (1 << 2) nuclear@2: #define SIOCNT_SD_STATUS (1 << 3) nuclear@2: #define SIOCNT_SLAVE1 (1 << 4) nuclear@2: #define SIOCNT_SLAVE2 (2 << 4) nuclear@2: #define SIOCNT_SLAVE3 (3 << 4) nuclear@2: #define SIOCNT_ERROR (1 << 6) nuclear@2: nuclear@2: /* REG_RCNT bits */ nuclear@2: #define RCNT_GPIO_DATA_MASK 0x000f nuclear@2: #define RCNT_GPIO_DIR_MASK 0x00f0 nuclear@2: #define RCNT_GPIO_INT_ENABLE (1 << 8) nuclear@2: #define RCNT_MODE_GPIO 0x8000 nuclear@2: #define RCNT_MODE_JOYBUS 0xc000 nuclear@2: nuclear@2: nuclear@2: static void setup_sio(int bits, int ms); nuclear@2: static void setup_gpio(void); nuclear@2: static void comm_intr(void); nuclear@2: nuclear@2: static int sio_bits, sio_master; nuclear@2: static void *sio_buf; nuclear@2: nuclear@2: nuclear@2: void comm_setup(int mode) nuclear@2: { nuclear@2: int master = 0; nuclear@2: nuclear@2: mask(INTR_COMM); nuclear@2: interrupt(INTR_COMM, comm_intr); nuclear@2: unmask(INTR_COMM); nuclear@2: nuclear@2: switch(mode) { nuclear@2: case COMM_SIO8_MASTER: nuclear@2: master = 1; nuclear@2: case COMM_SIO8_SLAVE: nuclear@2: setup_sio(8, master); nuclear@2: break; nuclear@2: nuclear@2: case COMM_SIO32_MASTER: nuclear@2: master = 1; nuclear@2: case COMM_SIO32_SLAVE: nuclear@2: setup_sio(32, master); nuclear@2: break; nuclear@2: nuclear@2: case COMM_GPIO: nuclear@2: setup_gpio(); nuclear@2: break; nuclear@2: nuclear@2: default: nuclear@2: panic("unimplemented comm mode\n"); nuclear@2: } nuclear@2: } nuclear@2: nuclear@2: static void setup_sio(int bits, int ms) nuclear@2: { nuclear@2: REG_RCNT = 0; /* serial mode */ nuclear@2: nuclear@2: sio_bits = bits; nuclear@2: sio_master = ms; nuclear@2: } nuclear@2: nuclear@2: void sio_transfer(void *in, const void *out) nuclear@2: { nuclear@2: /* load outgoing data */ nuclear@2: if(sio_bits <= 8) { nuclear@2: REG_SIODATA8 = *(unsigned char*)out; nuclear@2: } else { nuclear@2: REG_SIODATA32 = *(unsigned long*)out; nuclear@2: } nuclear@2: sio_buf = in; nuclear@2: nuclear@2: /* IE=1, external clock, send enable */ nuclear@2: REG_SIOCNT = SIOCNT_INT_ENABLE | SIOCNT_SEND_ENABLE; nuclear@2: nuclear@2: /* start transfer */ nuclear@2: REG_SIOCNT |= SIOCNT_START; nuclear@2: nuclear@2: /* wait until the transfer is complete */ nuclear@2: while(REG_SIOCNT & SIOCNT_START); nuclear@2: } nuclear@2: nuclear@2: void sio_transfer_async(void *in, const void *out) nuclear@2: { nuclear@2: /* load outgoing data */ nuclear@2: if(sio_bits <= 8) { nuclear@2: REG_SIODATA8 = *(unsigned char*)out; nuclear@2: } else { nuclear@2: REG_SIODATA32 = *(unsigned long*)out; nuclear@2: } nuclear@2: sio_buf = in; nuclear@2: nuclear@2: /* IE=1, external clock, send enable */ nuclear@2: REG_SIOCNT = SIOCNT_INT_ENABLE | SIOCNT_SEND_ENABLE; nuclear@2: nuclear@2: /* start transfer */ nuclear@2: REG_SIOCNT |= SIOCNT_START; nuclear@2: } nuclear@2: nuclear@2: nuclear@2: static void setup_gpio(void) nuclear@2: { nuclear@2: REG_RCNT = RCNT_MODE_GPIO | RCNT_GPIO_INT_ENABLE; nuclear@2: } nuclear@2: nuclear@2: void gpio_dir(int dir_so, int dir_si, int dir_sd, int dir_sc) nuclear@2: { nuclear@2: unsigned char mask; nuclear@2: nuclear@2: mask = ((dir_so & 1) << 7) | ((dir_si & 1) << 6) | ((dir_sd & 1) << 5) | nuclear@2: ((dir_sc & 1) << 4); nuclear@2: gpio_dir_mask(mask); nuclear@2: } nuclear@2: nuclear@2: void gpio_dir_mask(unsigned char dir) nuclear@2: { nuclear@2: REG_RCNT = (REG_RCNT & 0xff0f) | ((dir & 0xf) << 4); nuclear@2: } nuclear@2: nuclear@2: void gpio_set(unsigned char val) nuclear@2: { nuclear@2: REG_RCNT = (REG_RCNT & 0xfff0) | (val & 0xf); nuclear@2: } nuclear@2: nuclear@2: unsigned char gpio_get(void) nuclear@2: { nuclear@2: return REG_RCNT & 0xf; nuclear@2: } nuclear@2: nuclear@2: static void comm_intr(void) nuclear@2: { nuclear@2: if(sio_buf) { nuclear@2: if(REG_SIOCNT & SIOCNT_START) { nuclear@2: panic("asio: interrupt with start bit == 1"); nuclear@2: } nuclear@2: if(sio_bits <= 8) { nuclear@2: *(unsigned char*)sio_buf = REG_SIODATA8; nuclear@2: } else { nuclear@2: *(unsigned long*)sio_buf = REG_SIODATA32; nuclear@2: } nuclear@2: sio_buf = 0; nuclear@2: } nuclear@2: nuclear@2: if(signal_func(SIGIO)) { nuclear@2: raise(SIGIO); nuclear@2: } nuclear@2: }