/** * @brief Get busy status of spi interface * @param[in] fd is interface number. * @return busy or not * @retval 0 not busy. * @retval 1 busy. */ uint8_t spiGetBusyStatus(int32_t fd) { spi_dev *dev; dev = (spi_dev *)((uint32_t)&spi_device[fd]); if(spi_in(dev, CNTRL) & (0x1 << 17)) return (!dev->intflag); else return (( spi_in(dev, CNTRL) & 0x1) == 0x1 ? 1:0); }
// read 32 bit ethernet status word from FPGA uint32_t user_io_eth_get_status(void) { uint32_t s; spi_uio_cmd_cont(UIO_ETH_STATUS); s = spi_in(); s = (s<<8) | spi_in(); s = (s<<8) | spi_in(); s = (s<<8) | spi_in(); DisableIO(); return s; }
/** * @brief Read data form spi interface * @param[in] fd is interface number. * @param[in] buff_id is buffer number. If transfer number is 4, application needs read 4 times (buff_id is from 0 to 3) from buffer. * @return data */ uint32_t spiRead(int32_t fd, uint8_t buff_id) { spi_dev *dev; dev = (spi_dev *)((uint32_t)&spi_device[fd]); return spi_in(dev, (RX0+4*buff_id)); }
static void readBuf(uint16_t len, uint8_t* data) { spi_enable_eth(); spi_out(ENC28J60_READ_BUF_MEM); while (len--) { *data++ = spi_in(); } spi_disable_eth(); }
// read 8+32 bit sd card status word from FPGA uint8_t user_io_sd_get_status(uint32_t *lba) { uint32_t s; uint8_t c; spi_uio_cmd_cont(UIO_GET_SDSTAT); c = spi_in(); s = spi_in(); s = (s<<8) | spi_in(); s = (s<<8) | spi_in(); s = (s<<8) | spi_in(); DisableIO(); if(lba) *lba = s; return c; }
// 8 bit cores have a config string telling the firmware how // to treat it char *user_io_8bit_get_string(char index) { unsigned char i, lidx = 0, j = 0; static char buffer[32+1]; // max 32 bytes per config item // clear buffer buffer[0] = 0; spi_uio_cmd_cont(UIO_GET_STRING); i = spi_in(); // the first char returned will be 0xff if the core doesn't support // config strings. atari 800 returns 0xa4 which is the status byte if((i == 0xff) || (i == 0xa4)) { DisableIO(); return NULL; } // iprintf("String: "); while ((i != 0) && (i!=0xff) && (j<sizeof(buffer))) { if(i == ';') { if(lidx == index) buffer[j++] = 0; lidx++; } else { if(lidx == index) buffer[j++] = i; } // iprintf("%c", i); i = spi_in(); } DisableIO(); // iprintf("\n"); // if this was the last string in the config string list, then it still // needs to be terminated if(lidx == index) buffer[j] = 0; // also return NULL for empty strings if(!buffer[0]) return NULL; return buffer; }
static uint8_t readOp (uint8_t op, uint8_t address) { spi_enable_eth(); spi_out(op | (address & ADDR_MASK)); if (address & 0x80) spi_out(0x00); uint8_t result = spi_in(); spi_disable_eth(); return result; }
char user_io_serial_status(serial_status_t *status_in, uint8_t status_out) { uint8_t i, *p = (uint8_t*)status_in; spi_uio_cmd_cont(UIO_SERIAL_STAT); // first byte returned by core must be "magic". otherwise the // core doesn't support this request if(SPI(status_out) != 0xa5) { DisableIO(); return 0; } // read the whole structure for(i=0;i<sizeof(serial_status_t);i++) *p++ = spi_in(); DisableIO(); return 1; }
static int sc8800_tx(struct sc8800_data *sc8800) { int ret = 0, len; char *buf = NULL; sc8800->write_tmo = 0; buf = kzalloc(WR_BUF_SIZE + BP_PACKET_SIZE, GFP_KERNEL); if(!buf){ dev_err(sc8800->dev, "ERR: no memery for buf\n"); return -ENOMEM; } while(!bp_rts(sc8800)){ if(sc8800->write_tmo){ sc8800_dbg(sc8800->dev, "bp_rts = 0\n"); kfree(buf); return -1; } schedule(); } mutex_lock(&sc8800s_lock); #if defined(CONFIG_ARCH_RK30) ap_rts(sc8800,1); #else ap_rts(sc8800,0); #endif #if 1 while(bp_rdy(sc8800)){ if(sc8800->write_tmo){ #if defined(CONFIG_ARCH_RK30) ap_rts(sc8800,0); #else ap_rts(sc8800,1); #endif sc8800_dbg(sc8800->dev, "ERR: %s write timeout ->bp not ready (bp_rdy = 1)\n", __func__); msleep(1); kfree(buf); mutex_unlock(&sc8800s_lock); return -1; } schedule(); } #endif len = sc8800_data_packet(buf, sc8800->tx_buf, sc8800->tx_len); spi_in(sc8800, buf, len, &ret); if(ret < 0) dev_err(sc8800->dev, "ERR: %s spi in err = %d\n", __func__, ret); #if defined(CONFIG_ARCH_RK30) ap_rts(sc8800,0); #else ap_rts(sc8800,1); #endif if(buf){ kfree(buf); buf = NULL; } mutex_unlock(&sc8800s_lock); #if 1 while(!bp_rdy(sc8800)){ if(sc8800->write_tmo){ dev_err(sc8800->dev, "ERR: %s write timeout -> bp receiving (bp_rdy = 0)\n", __func__); ret = -1; } schedule(); } #endif return ret; }
void user_io_poll() { if(user_io_dip_switch1()) { // check of core has changed from a good one to a not supported on // as this likely means that the user is reloading the core via jtag unsigned char ct; static unsigned char ct_cnt = 0; EnableIO(); ct = SPI(0xff); DisableIO(); SPI(0xff); // needed for old minimig core if(ct == core_type) ct_cnt = 0; // same core type, everything is fine else { // core type has changed if(++ct_cnt == 255) { // wait for a new valid core id to appear while((ct & 0xf0) != 0xa0) { EnableIO(); ct = SPI(0xff); DisableIO(); SPI(0xff); // needed for old minimig core } // reset io controller to cope with new core *AT91C_RSTC_RCR = 0xA5 << 24 | AT91C_RSTC_PERRST | AT91C_RSTC_PROCRST; // restart for(;;); } } } if((core_type != CORE_TYPE_MINIMIG) && (core_type != CORE_TYPE_MINIMIG2) && (core_type != CORE_TYPE_PACE) && (core_type != CORE_TYPE_MIST) && (core_type != CORE_TYPE_ARCHIE) && (core_type != CORE_TYPE_8BIT)) { return; // no user io for the installed core } if(core_type == CORE_TYPE_MIST) { char redirect = tos_get_cdc_control_redirect(); ikbd_poll(); // check for input data on usart USART_Poll(); unsigned char c = 0; // check for incoming serial data. this is directly forwarded to the // arm rs232 and mixes with debug output. Useful for debugging only of // e.g. the diagnostic cartridge if(!pl2303_is_blocked()) { spi_uio_cmd_cont(UIO_SERIAL_IN); while(spi_in() && !pl2303_is_blocked()) { c = spi_in(); // if a serial/usb adapter is connected it has precesence over // any other sink if(pl2303_present()) pl2303_tx_byte(c); else { if(c != 0xff) putchar(c); // forward to USB if redirection via USB/CDC enabled if(redirect == CDC_REDIRECT_RS232) cdc_control_tx(c); } } DisableIO(); } // check for incoming parallel/midi data if((redirect == CDC_REDIRECT_PARALLEL) || (redirect == CDC_REDIRECT_MIDI)) { spi_uio_cmd_cont((redirect == CDC_REDIRECT_PARALLEL)?UIO_PARALLEL_IN:UIO_MIDI_IN); // character 0xff is returned if FPGA isn't configured c = 0; while(spi_in() && (c!= 0xff)) { c = spi_in(); cdc_control_tx(c); } DisableIO(); // always flush when doing midi to reduce latencies if(redirect == CDC_REDIRECT_MIDI) cdc_control_flush(); } } // poll db9 joysticks static int joy0_state = JOY0; if((*AT91C_PIOA_PDSR & JOY0) != joy0_state) { joy0_state = *AT91C_PIOA_PDSR & JOY0; unsigned char joy_map = 0; if(!(joy0_state & JOY0_UP)) joy_map |= JOY_UP; if(!(joy0_state & JOY0_DOWN)) joy_map |= JOY_DOWN; if(!(joy0_state & JOY0_LEFT)) joy_map |= JOY_LEFT; if(!(joy0_state & JOY0_RIGHT)) joy_map |= JOY_RIGHT; if(!(joy0_state & JOY0_BTN1)) joy_map |= JOY_BTN1; if(!(joy0_state & JOY0_BTN2)) joy_map |= JOY_BTN2; user_io_joystick(joystick_renumber(0), joy_map); } static int joy1_state = JOY1; if((*AT91C_PIOA_PDSR & JOY1) != joy1_state) { joy1_state = *AT91C_PIOA_PDSR & JOY1; unsigned char joy_map = 0; if(!(joy1_state & JOY1_UP)) joy_map |= JOY_UP; if(!(joy1_state & JOY1_DOWN)) joy_map |= JOY_DOWN; if(!(joy1_state & JOY1_LEFT)) joy_map |= JOY_LEFT; if(!(joy1_state & JOY1_RIGHT)) joy_map |= JOY_RIGHT; if(!(joy1_state & JOY1_BTN1)) joy_map |= JOY_BTN1; if(!(joy1_state & JOY1_BTN2)) joy_map |= JOY_BTN2; user_io_joystick(joystick_renumber(1), joy_map); } user_io_send_buttons(0); // mouse movement emulation is continous if(emu_mode == EMU_MOUSE) { if(CheckTimer(emu_timer)) { emu_timer = GetTimer(EMU_MOUSE_FREQ); if(emu_state & JOY_MOVE) { unsigned char b = 0; char x = 0, y = 0; if((emu_state & (JOY_LEFT | JOY_RIGHT)) == JOY_LEFT) x = -1; if((emu_state & (JOY_LEFT | JOY_RIGHT)) == JOY_RIGHT) x = +1; if((emu_state & (JOY_UP | JOY_DOWN)) == JOY_UP) y = -1; if((emu_state & (JOY_UP | JOY_DOWN)) == JOY_DOWN) y = +1; if(emu_state & JOY_BTN1) b |= 1; if(emu_state & JOY_BTN2) b |= 2; user_io_mouse(b, x, y); } } } if((core_type == CORE_TYPE_MINIMIG) || (core_type == CORE_TYPE_MINIMIG2)) { kbd_fifo_poll(); // frequently check mouse for events if(CheckTimer(mouse_timer)) { mouse_timer = GetTimer(MOUSE_FREQ); // has ps2 mouse data been updated in the meantime if(mouse_flags & 0x80) { spi_uio_cmd_cont(UIO_MOUSE); // ----- X axis ------- if(mouse_pos[X] < -128) { spi8(-128); mouse_pos[X] += 128; } else if(mouse_pos[X] > 127) { spi8(127); mouse_pos[X] -= 127; } else { spi8(mouse_pos[X]); mouse_pos[X] = 0; } // ----- Y axis ------- if(mouse_pos[Y] < -128) { spi8(-128); mouse_pos[Y] += 128; } else if(mouse_pos[Y] > 127) { spi8(127); mouse_pos[Y] -= 127; } else { spi8(mouse_pos[Y]); mouse_pos[Y] = 0; } spi8(mouse_flags & 0x03); DisableIO(); // reset flags mouse_flags = 0; } } } if(core_type == CORE_TYPE_MIST) { // do some tos specific monitoring here tos_poll(); } if(core_type == CORE_TYPE_8BIT) { unsigned char c = 1, f, p=0; // check for input data on usart USART_Poll(); // check for serial data to be sent // check for incoming serial data. this is directly forwarded to the // arm rs232 and mixes with debug output. spi_uio_cmd_cont(UIO_SIO_IN); // status byte is 1000000A with A=1 if data is available if((f = spi_in(0)) == 0x81) { iprintf("\033[1;36m"); // character 0xff is returned if FPGA isn't configured while((f == 0x81) && (c!= 0xff) && (c != 0x00) && (p < 8)) { c = spi_in(); if(c != 0xff && c != 0x00) iprintf("%c", c); f = spi_in(); p++; } iprintf("\033[0m"); } DisableIO(); // sd card emulation { static char buffer[512]; static uint32_t buffer_lba = 0xffffffff; uint32_t lba; uint8_t c = user_io_sd_get_status(&lba); // valid sd commands start with "5x" to avoid problems with // cores that don't implement this command if((c & 0xf0) == 0x50) { // debug: If the io controller reports and non-sdhc card, then // the core should never set the sdhc flag if((c & 3) && !MMC_IsSDHC() && (c & 0x04)) iprintf("WARNING: SDHC access to non-sdhc card\n"); // check if core requests configuration if(c & 0x08) { iprintf("core requests SD config\n"); user_io_sd_set_config(); } // check if system is trying to access a sdhc card from // a sd/mmc setup // check if an SDHC card is inserted if(MMC_IsSDHC()) { static char using_sdhc = 1; // SD request and if((c & 0x03) && !(c & 0x04)) { if(using_sdhc) { // we have not been using sdhc so far? // -> complain! ErrorMessage(" This core does not support\n" " SDHC cards. Using them may\n" " lead to data corruption.\n\n" " Please use an SD card <2GB!", 0); using_sdhc = 0; } } else // SDHC request from core is always ok using_sdhc = 1; } if((c & 0x03) == 0x02) { // only write if the inserted card is not sdhc or // if the core uses sdhc if((!MMC_IsSDHC()) || (c & 0x04)) { uint8_t wr_buf[512]; if(user_io_dip_switch1()) iprintf("SD WR %d\n", lba); // if we write the sector stored in the read buffer, then // update the read buffer with the new contents if(buffer_lba == lba) memcpy(buffer, wr_buf, 512); buffer_lba = 0xffffffff; // Fetch sector data from FPGA ... spi_uio_cmd_cont(UIO_SECTOR_WR); spi_block_read(wr_buf); DisableIO(); // ... and write it to disk DISKLED_ON; if(sd_image.size) { FileSeek(&sd_image, lba, SEEK_SET); FileWrite(&sd_image, wr_buf); } else MMC_Write(lba, wr_buf); DISKLED_OFF; } } if((c & 0x03) == 0x01) { if(user_io_dip_switch1()) iprintf("SD RD %d\n", lba); // are we using a file as the sd card image? // (C64 floppy does that ...) if(buffer_lba != lba) { DISKLED_ON; if(sd_image.size) { FileSeek(&sd_image, lba, SEEK_SET); FileRead(&sd_image, buffer); } else { // sector read // read sector from sd card if it is not already present in // the buffer MMC_Read(lba, buffer); } buffer_lba = lba; DISKLED_OFF; } if(buffer_lba == lba) { // data is now stored in buffer. send it to fpga spi_uio_cmd_cont(UIO_SECTOR_RD); spi_block_write(buffer); DisableIO(); // the end of this transfer acknowledges the FPGA internal // sd card emulation } // just load the next sector now, so it may be prefetched // for the next request already DISKLED_ON; if(sd_image.size) { FileSeek(&sd_image, lba+1, SEEK_SET); FileRead(&sd_image, buffer); } else { // sector read // read sector from sd card if it is not already present in // the buffer MMC_Read(lba+1, buffer); } buffer_lba = lba+1; DISKLED_OFF; } } } // frequently check ps2 mouse for events if(CheckTimer(mouse_timer)) { mouse_timer = GetTimer(MOUSE_FREQ); // has ps2 mouse data been updated in the meantime if(mouse_flags & 0x08) { unsigned char ps2_mouse[3]; // PS2 format: // YOvfl, XOvfl, dy8, dx8, 1, mbtn, rbtn, lbtn // dx[7:0] // dy[7:0] ps2_mouse[0] = mouse_flags; // ------ X axis ----------- // store sign bit in first byte ps2_mouse[0] |= (mouse_pos[X] < 0)?0x10:0x00; if(mouse_pos[X] < -255) { // min possible value + overflow flag ps2_mouse[0] |= 0x40; ps2_mouse[1] = -128; } else if(mouse_pos[X] > 255) { // max possible value + overflow flag ps2_mouse[0] |= 0x40; ps2_mouse[1] = 255; } else ps2_mouse[1] = mouse_pos[X]; // ------ Y axis ----------- // store sign bit in first byte ps2_mouse[0] |= (mouse_pos[Y] < 0)?0x20:0x00; if(mouse_pos[Y] < -255) { // min possible value + overflow flag ps2_mouse[0] |= 0x80; ps2_mouse[2] = -128; } else if(mouse_pos[Y] > 255) { // max possible value + overflow flag ps2_mouse[0] |= 0x80; ps2_mouse[2] = 255; } else ps2_mouse[2] = mouse_pos[Y]; // collect movement info and send at predefined rate iprintf("PS2 MOUSE: %x %d %d\n", ps2_mouse[0], ps2_mouse[1], ps2_mouse[2]); spi_uio_cmd_cont(UIO_MOUSE); spi8(ps2_mouse[0]); spi8(ps2_mouse[1]); spi8(ps2_mouse[2]); DisableIO(); // reset counters mouse_flags = 0; mouse_pos[X] = mouse_pos[Y] = 0; } } // --------------- THE FOLLOWING IS DEPRECATED AND WILL BE REMOVED ------------ // ------------------------ USE SD CARD EMULATION INSTEAD --------------------- // raw sector io for the atari800 core which include a full // file system driver usually implemented using a second cpu static unsigned long bit8_status = 0; unsigned long status; /* read status byte */ EnableFpga(); SPI(UIO_GET_STATUS); status = SPI(0); status = (status << 8) | SPI(0); status = (status << 8) | SPI(0); status = (status << 8) | SPI(0); DisableFpga(); if(status != bit8_status) { unsigned long sector = (status>>8)&0xffffff; char buffer[512]; bit8_status = status; // sector read testing DISKLED_ON; // sector read if(((status & 0xff) == 0xa5) || ((status & 0x3f) == 0x29)) { // extended command with 26 bits (for 32GB SDHC) if((status & 0x3f) == 0x29) sector = (status>>6)&0x3ffffff; bit8_debugf("SECIO rd %ld", sector); if(MMC_Read(sector, buffer)) { // data is now stored in buffer. send it to fpga EnableFpga(); SPI(UIO_SECTOR_SND); // send sector data IO->FPGA spi_block_write(buffer); DisableFpga(); } else bit8_debugf("rd %ld fail", sector); } // sector write if(((status & 0xff) == 0xa6) || ((status & 0x3f) == 0x2a)) { // extended command with 26 bits (for 32GB SDHC) if((status & 0x3f) == 0x2a) sector = (status>>6)&0x3ffffff; bit8_debugf("SECIO wr %ld", sector); // read sector from FPGA EnableFpga(); SPI(UIO_SECTOR_RCV); // receive sector data FPGA->IO spi_block_read(buffer); DisableFpga(); if(!MMC_Write(sector, buffer)) bit8_debugf("wr %ld fail", sector); } DISKLED_OFF; }
// read ethernet frame from FPGAs ethernet tx buffer void user_io_eth_receive_tx_frame(uint8_t *d, uint16_t len) { spi_uio_cmd_cont(UIO_ETH_FRM_IN); while(len--) *d++=spi_in(); DisableIO(); }
/** * @brief SPI-1 Interrupt handler * @param None * @return None */ static void spi1ISR(void) { // clear interrupt flag outpw(REG_SPI1_CNTRL, spi_in((spi_dev *)((uint32_t)&spi_device[1]), CNTRL) | 0x1 << 16); spi_device[1].intflag = 1; }
/** * @brief Support some spi driver commands for application. * @param[in] fd is interface number. * @param[in] cmd is command. * @param[in] arg0 is the first argument of command. * @param[in] arg1 is the second argument of command. * @return command status. * @retval 0 Success otherwise fail. Fail value could be * - \ref SPI_ERR_NODEV * - \ref SPI_ERR_IO * - \ref SPI_ERR_ARG */ int32_t spiIoctl(int32_t fd, uint32_t cmd, uint32_t arg0, uint32_t arg1) { spi_dev *dev; if(fd != 0 && fd != 1) return(SPI_ERR_NODEV); dev = (spi_dev *)((uint32_t)&spi_device[fd]); if(dev->openflag == 0) return(SPI_ERR_IO); switch(cmd) { case SPI_IOC_TRIGGER: dev->intflag = 0; spi_out(dev, spi_in(dev, CNTRL) | 0x1 ,CNTRL); break; case SPI_IOC_SET_INTERRUPT: if(arg0 == SPI_ENABLE_INTERRUPT) spi_out(dev, spi_in(dev, CNTRL) | (0x1<<17) ,CNTRL); else spi_out(dev, spi_in(dev, CNTRL) & ~(0x1<<17) ,CNTRL); break; case SPI_IOC_SET_SPEED: spiSetSpeed(dev, (uint32_t)arg0); break; case SPI_IOC_SET_DUAL_QUAD_MODE: if(arg0 == SPI_DISABLE_DUAL_QUAD) { spi_out(dev, (spi_in(dev, CNTRL) & ~(0x3 << 21)) ,CNTRL); break; } if(arg0 == SPI_DUAL_MODE) spi_out(dev, (spi_in(dev, CNTRL) & ~(0x3 << 21)) | (0x1 << 22) ,CNTRL); else spi_out(dev, (spi_in(dev, CNTRL) & ~(0x3 << 21)) | (0x1 << 21) ,CNTRL); break; case SPI_IOC_SET_DUAL_QUAD_DIR: if(arg0 == SPI_DUAL_QUAD_INPUT) spi_out(dev, spi_in(dev, CNTRL) & ~(0x1 << 20) ,CNTRL); else spi_out(dev, spi_in(dev, CNTRL) | (0x1 << 20) ,CNTRL); break; case SPI_IOC_SET_LSB_MSB: if(arg0 == SPI_MSB) spi_out(dev, spi_in(dev, CNTRL) & ~(0x1 << 10) ,CNTRL); else spi_out(dev, spi_in(dev, CNTRL) | (0x1 << 10) ,CNTRL); break; case SPI_IOC_SET_TX_NUM: if(arg0 < 4) spi_out(dev, (spi_in(dev, CNTRL) & ~(0x3 << 8)) | (arg0 << 8) ,CNTRL); else return SPI_ERR_ARG; break; case SPI_IOC_SET_TX_BITLEN: if(arg0 < 32) spi_out(dev, (spi_in(dev, CNTRL) & ~(0x1f << 3)) | (arg0 << 3) ,CNTRL); else return SPI_ERR_ARG; break; case SPI_IOC_SET_MODE: if(arg0 > SPI_MODE_3) return SPI_ERR_ARG; if(arg0 == SPI_MODE_0) spi_out(dev, (spi_in(dev, CNTRL) & ~((0x3<<1) | (1UL<<31))) | (1<<2) ,CNTRL); else if(arg0 == SPI_MODE_1) spi_out(dev, (spi_in(dev, CNTRL) & ~((0x3<<1) | (1UL<<31))) | (1<<1) ,CNTRL); else if(arg0 == SPI_MODE_2) spi_out(dev, (spi_in(dev, CNTRL) & ~((0x3<<1) | (1UL<<31))) | ((1UL<<31) | (1<<2)) ,CNTRL); else spi_out(dev, (spi_in(dev, CNTRL) & ~((0x3<<1) | (1UL<<31))) | ((1UL<<31) | (1<<1)) ,CNTRL); break; case SPI_IOC_ENABLE_SS: if(arg0 == SPI_SS_SS0) spi_out(dev, (spi_in(dev, SSR) & ~(0x3)) | 0x1 ,SSR); else if(arg0 == SPI_SS_SS1) spi_out(dev, (spi_in(dev, SSR) & ~(0x3)) | 0x2 ,SSR); else if(arg0 == SPI_SS_BOTH) spi_out(dev, (spi_in(dev, SSR) & ~(0x3)) | 0x3 ,SSR); else return SPI_ERR_ARG; break; case SPI_IOC_DISABLE_SS: if(arg0 == SPI_SS_SS0) spi_out(dev, (spi_in(dev, SSR) & ~(0x1)) ,SSR); else if(arg0 == SPI_SS_SS1) spi_out(dev, (spi_in(dev, SSR) & ~(0x2)) ,SSR); else if(arg0 == SPI_SS_BOTH) spi_out(dev, (spi_in(dev, SSR) & ~(0x3)) ,SSR); else return SPI_ERR_ARG; break; case SPI_IOC_SET_AUTOSS: if(arg0 == SPI_DISABLE_AUTOSS) spi_out(dev, spi_in(dev, SSR) & ~(0x1 << 3) ,SSR); else spi_out(dev, spi_in(dev, SSR) | (0x1 << 3) ,SSR); break; case SPI_IOC_SET_SS_ACTIVE_LEVEL: if(arg0 == SPI_SS_ACTIVE_LOW) spi_out(dev, spi_in(dev, SSR) & ~(0x1 << 2) ,SSR); else spi_out(dev, spi_in(dev, SSR) | (0x1 << 2) ,SSR); default: break; } return 0; }