/** * @file * * @ingroup arm_beagle * * @brief BeagleBoard SPI bus initialization and API Support. * * Based on bsps/m68k/gen68360/spi/m360_spi.c */ /* * Copyright (c) 2018 Pierre-Louis Garnier * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.org/license/LICENSE. */ #include #include #include #include #include #include #include #include #include // #define DEBUG // #define TRACE #define EVENT_TXEMPTY RTEMS_EVENT_0 #define EVENT_RXFULL RTEMS_EVENT_1 static void SPI0ModuleClkConfig(void) { /* Writing to MODULEMODE field of AM335X_CM_PER_SPI0_CLKCTRL register. */ REG( AM335X_CM_PER_ADDR + AM335X_CM_PER_SPI0_CLKCTRL ) |= AM335X_CM_PER_SPI0_CLKCTRL_MODULEMODE_ENABLE; /* Waiting for MODULEMODE field to reflect the written value. */ while ( AM335X_CM_PER_SPI0_CLKCTRL_MODULEMODE_ENABLE != ( REG( AM335X_CM_PER_ADDR + AM335X_CM_PER_SPI0_CLKCTRL ) & AM335X_CM_PER_SPI0_CLKCTRL_MODULEMODE ) ) continue; /* * Waiting for IDLEST field in AM335X_CM_PER_SPI0_CLKCTRL * register to attain desired value. */ while ( ( AM335X_CM_PER_CONTROL_CLKCTRL_IDLEST_FUNC << AM335X_CM_PER_CONTROL_CLKCTRL_IDLEST_SHIFT ) != ( REG( AM335X_CM_PER_ADDR + AM335X_CM_PER_SPI0_CLKCTRL ) & AM335X_CM_PER_CONTROL_CLKCTRL_IDLEST ) ) continue; } static inline void am335x_spi_clear_irqstatus(uint32_t reg_base, uint32_t irqs) { REG(reg_base + AM335X_SPI_SYST) &= ~AM335X_SPI_SYST_SSB; REG(reg_base + AM335X_SPI_IRQSTATUS) = irqs; } static void am335x_spi0_pinmux(void) { REG(AM335X_PADCONF_BASE + AM335X_CONF_SPI0_SCLK) = (BBB_RXACTIVE | BBB_SLEWCTRL | BBB_PU_EN); REG(AM335X_PADCONF_BASE + AM335X_CONF_SPI0_D0) = (BBB_RXACTIVE | BBB_SLEWCTRL | BBB_PU_EN); REG(AM335X_PADCONF_BASE + AM335X_CONF_SPI0_D1) = (BBB_RXACTIVE | BBB_SLEWCTRL | BBB_PU_EN); REG(AM335X_PADCONF_BASE + AM335X_CONF_SPI0_CS0) = (BBB_RXACTIVE | BBB_PU_EN); } static void am335x_spi_reset(uint32_t reg_base) { int timeout = BBB_SPI_TIMEOUT; REG(reg_base + AM335X_SPI_SYSCONFIG) |= AM335X_SPI_SYSCONFIG_SOFTRESET; while ((REG(reg_base + AM335X_SPI_SYSSTATUS) & AM335X_SPI_SYSSTATUS_RESETDONE) == 0 && timeout--) { if (timeout <= 0) { puts("ERROR: Timeout in soft-reset\n"); return; } udelay(1000); } } static void beagle_spi_irq_handler(void *arg) { const uint32_t handled_irqs = AM335X_SPI_IRQSTATUS_TX0_EMPTY | AM335X_SPI_IRQSTATUS_RX0_FULL; beagle_spi_softc_t *softc_ptr = arg; rtems_status_code sc = -1; uint32_t irq = 0; uint32_t events = 0; uint32_t tmp; while ((tmp = (REG(softc_ptr->regs_base + AM335X_SPI_IRQSTATUS)) & handled_irqs) != 0) { irq |= tmp; am335x_spi_clear_irqstatus(softc_ptr->regs_base, tmp); } #if defined(TRACE) printk("beagle_spi_irq_handler: AM335X_SPI_IRQSTATUS = 0x%04lx\r\n", irq); #endif if (irq & AM335X_SPI_IRQSTATUS_TX0_EMPTY) { #if defined(TRACE) printk("beagle_spi_irq_handler: sending event TXEMPTY to task_id = %ld\r\n", softc_ptr->task_id); #endif events |= EVENT_TXEMPTY; } if (irq & AM335X_SPI_IRQSTATUS_RX0_FULL) { #if defined(TRACE) printk("beagle_spi_irq_handler: sending event RXFULL to task_id = %ld\r\n", softc_ptr->task_id); #endif events |= EVENT_RXFULL; } sc = rtems_event_send(softc_ptr->task_id, events); _Assert(sc == RTEMS_SUCCESSFUL); (void)sc; } /* Initialize the driver * * Returns: o = ok or error code */ rtems_status_code beagle_spi_init ( rtems_libi2c_bus_t *bh /* bus specifier structure */ ) { beagle_spi_softc_t *softc_ptr = &(((beagle_spi_desc_t *)(bh))->softc); rtems_status_code rc = RTEMS_SUCCESSFUL; #if defined(DEBUG) printk("beagle_spi_init called...\r\n"); #endif SPI0ModuleClkConfig(); am335x_spi0_pinmux(); am335x_spi_reset(softc_ptr->regs_base); REG(softc_ptr->regs_base + AM335X_SPI_MODULCTRL) &= ~AM335X_SPI_MODULCTRL_PIN34; REG(softc_ptr->regs_base + AM335X_SPI_MODULCTRL) &= ~AM335X_SPI_MODULCTRL_MS; // Master mode REG(softc_ptr->regs_base + AM335X_SPI_MODULCTRL) |= AM335X_SPI_MODULCTRL_SINGLE; // Single channel REG(softc_ptr->regs_base + AM335X_SPI_MODULCTRL) &= ~AM335X_SPI_MODULCTRL_PIN34; // SPIEN is usedas a chip select // REG(softc_ptr->regs_base + AM335X_SPI_MODULCTRL) |= (1 << 3); // Test mode // REG(softc_ptr->regs_base + AM335X_SPI_SYST) |= AM335X_SPI_SYST_SPIEN_0; // Not sure about this // REG(softc_ptr->regs_base + AM335X_SPI_SYST) |= AM335X_SPI_SYST_SPIDATDIR0; // Input // REG(softc_ptr->regs_base + AM335X_SPI_SYST) &= ~AM335X_SPI_SYST_SPIDATDIR1; // Output REG(softc_ptr->regs_base + AM335X_SPI_CH0CONF) &= ~AM335X_SPI_CH0CONF_TRM_MASK; REG(softc_ptr->regs_base + AM335X_SPI_CH0CONF) |= AM335X_SPI_CH0CONF_DPE0; REG(softc_ptr->regs_base + AM335X_SPI_CH0CONF) &= ~AM335X_SPI_CH0CONF_DPE1; REG(softc_ptr->regs_base + AM335X_SPI_CH0CONF) &= ~AM335X_SPI_CH0CONF_IS; // REG(softc_ptr->regs_base + AM335X_SPI_CH0CONF) |= AM335X_SPI_CH0CONF_FFEW; // REG(softc_ptr->regs_base + AM335X_SPI_CH0CONF) |= AM335X_SPI_CH0CONF_FFER; REG(softc_ptr->regs_base + AM335X_SPI_CH0CONF) |= AM335X_SPI_CH0CONF_WL(8 - 1); REG(softc_ptr->regs_base + AM335X_SPI_CH0CONF) &= ~AM335X_SPI_CH0CONF_PHA; REG(softc_ptr->regs_base + AM335X_SPI_CH0CONF) &= ~AM335X_SPI_CH0CONF_POL; REG(softc_ptr->regs_base + AM335X_SPI_CH0CONF) |= AM335X_SPI_CH0CONF_EPOL; REG(softc_ptr->regs_base + AM335X_SPI_CH0CONF) |= AM335X_SPI_CH0CONF_CLKD(0x1); // Setup interrupt rc = rtems_interrupt_handler_install( softc_ptr->irq, NULL, RTEMS_INTERRUPT_UNIQUE, (rtems_interrupt_handler)beagle_spi_irq_handler, softc_ptr ); #if defined(DEBUG) printk("beagle_spi_init done\r\n"); #endif if (rc == RTEMS_SUCCESSFUL) { softc_ptr->initialized = TRUE; } return rc; } static int beagle_spi_read_write_bytes( rtems_libi2c_bus_t *bh, /* bus specifier structure */ unsigned char *rx_buf, /* buffer to store bytes */ unsigned char *tx_buf, /* buffer to send */ int len /* number of bytes to send */ ) { beagle_spi_softc_t *softc_ptr = &(((beagle_spi_desc_t *)(bh))->softc); rtems_status_code sc; rtems_event_set received_events; #if defined(TRACE) printk("beagle_spi_read_write_bytes called...\r\n"); #endif softc_ptr->task_id = rtems_task_self(); // Enable IRQs if (rx_buf) { am335x_spi_clear_irqstatus(softc_ptr->regs_base, AM335X_SPI_IRQSTATUS_RX0_FULL); REG(softc_ptr->regs_base + AM335X_SPI_IRQENABLE) |= AM335X_SPI_IRQENABLE_RX0_FULL; } if (tx_buf) { am335x_spi_clear_irqstatus(softc_ptr->regs_base, AM335X_SPI_IRQSTATUS_TX0_EMPTY); REG(softc_ptr->regs_base + AM335X_SPI_IRQENABLE) |= AM335X_SPI_IRQENABLE_TX0_EMPTY; } // Enable Channel REG(softc_ptr->regs_base + AM335X_SPI_CH0CONF) |= AM335X_SPI_CH0CONF_FORCE; REG(softc_ptr->regs_base + AM335X_SPI_CH0CTRL) |= AM335X_SPI_CH0CTRL_EN; // Main loop for (int i = 0; i < len; i++) { received_events = 0; if (tx_buf) { // Wait for IRQ to wake us up (room in TX FIFO) #if defined(TRACE) printk("beagle_spi_read_write_bytes: waiting (task_id = %ld)\r\n", softc_ptr->task_id); #endif sc = rtems_event_receive(EVENT_TXEMPTY, RTEMS_WAIT, BBB_SPI_TIMEOUT, &received_events); if (sc != RTEMS_SUCCESSFUL) { printk("ERROR: beagle_spi_read_write_bytes timed out on tx byte number %d\n", i); return i > 0 ? i : -RTEMS_TIMEOUT; } _Assert(received_events == EVENT_TXEMPTY); #if defined(TRACE) printk("beagle_spi_read_write_bytes: sending byte: i = %d, tx_buf[i] = 0x%x\r\n", i, tx_buf[i]); #endif REG(softc_ptr->regs_base + AM335X_SPI_TX0) = tx_buf[i]; } if (rx_buf) { // Wait for IRQ to wake us up (data in RX FIFO) if ((received_events & EVENT_RXFULL) == 0) { sc = rtems_event_receive(EVENT_RXFULL, RTEMS_WAIT, BBB_SPI_TIMEOUT, &received_events); if (sc != RTEMS_SUCCESSFUL) { printk("ERROR: beagle_spi_read_write_bytes timed out on rx byte number %d\n", i); return i > 0 ? i : -RTEMS_TIMEOUT; } _Assert(received_events == EVENT_RXFULL); } rx_buf[i] = REG(softc_ptr->regs_base + AM335X_SPI_RX0); #if defined(TRACE) printk("beagle_spi_read_write_bytes: received byte: i = %d, rx_buf[i] = 0x%x\r\n", i, rx_buf[i]); #endif } } // REG(softc_ptr->regs_base + AM335X_SPI_IRQENABLE) &= ~AM335X_SPI_IRQENABLE_RX0_FULL; // REG(softc_ptr->regs_base + AM335X_SPI_IRQENABLE) &= ~AM335X_SPI_IRQENABLE_TX0_EMPTY; REG(softc_ptr->regs_base + AM335X_SPI_CH0CTRL) &= ~AM335X_SPI_CH0CTRL_EN; REG(softc_ptr->regs_base + AM335X_SPI_CH0CONF) &= ~AM335X_SPI_CH0CONF_FORCE; #if defined(TRACE) printk("beagle_spi_read_write_bytes done\r\n"); #endif return len; } /* * Receive some bytes from SPI device * * Returns: number of bytes received or (negative) error code */ int beagle_spi_read_bytes ( rtems_libi2c_bus_t *bh, /* bus specifier structure */ unsigned char *buf, /* buffer to store bytes */ int len /* number of bytes to receive */ ) { // FIXME #if defined(DEBUG) printk("beagle_spi_read_bytes called...\r\n"); #endif int n = beagle_spi_read_write_bytes(bh, buf, NULL, len); #if defined(DEBUG) printk("beagle_spi_read_bytes done\r\n"); #endif return n; } /* * Send some bytes to SPI device * * Returns: number of bytes sent or (negative) error code */ int beagle_spi_write_bytes ( rtems_libi2c_bus_t *bh, /* bus specifier structure */ unsigned char *buf, /* buffer to send */ int len /* number of bytes to send */ ) { #if defined(DEBUG) printk("beagle_spi_write_bytes called...\r\n"); #endif int n = beagle_spi_read_write_bytes(bh, NULL, buf, len); #if defined(DEBUG) printk("beagle_spi_write_bytes done\r\n"); #endif return n; } /* * Perform selected ioctl function for SPI * * Returns: rtems_status_code */ int beagle_spi_ioctl ( rtems_libi2c_bus_t *bh, /* bus specifier structure */ int cmd, /* ioctl command code */ void *arg /* additional argument array */ ) { int ret_val = -1; #if defined(DEBUG) printk("beagle_spi_ioctl called...\r\n"); #endif switch(cmd) { // FIXME: other ioctls case RTEMS_LIBI2C_IOCTL_SET_TFRMODE: #if defined(TRACE) printk("cmd == RTEMS_LIBI2C_IOCTL_SET_TFRMODE\r\n"); #endif // FIXME // ret_val = // -m360_spi_set_tfr_mode(bh, // (const rtems_libi2c_tfr_mode_t *)arg); break; case RTEMS_LIBI2C_IOCTL_READ_WRITE: { #if defined(TRACE) printk("cmd == RTEMS_LIBI2C_IOCTL_READ_WRITE\r\n"); #endif const rtems_libi2c_read_write_t *cmd = (const rtems_libi2c_read_write_t *)arg; ret_val = beagle_spi_read_write_bytes( bh, (unsigned char *)cmd->rd_buf, (unsigned char *)cmd->wr_buf, cmd->byte_cnt ); break; } default: ret_val = -RTEMS_NOT_DEFINED; break; } #if defined(DEBUG) printk("beagle_spi_ioctl done\r\n"); #endif return ret_val; } /*=========================================================================*\ | Board-specific adaptation functions | \*=========================================================================*/ /* * Address a slave device on the bus * * Returns: o = ok or error code */ static rtems_status_code bsp_spi_sel_addr ( rtems_libi2c_bus_t *bh, /* bus specifier structure */ uint32_t addr, /* address to send on bus */ int rw /* 0=write,1=read */ ) { if (addr != 0) return RTEMS_NOT_IMPLEMENTED; return RTEMS_SUCCESSFUL; } /* * Dummy function, SPI has no start condition * * Returns: o = ok or error code */ static rtems_status_code bsp_spi_send_start_dummy ( rtems_libi2c_bus_t *bh /* bus specifier structure */ ) { #if defined(DEBUG) printk("bsp_spi_send_start_dummy OK\r\n"); #endif return 0; } /* * Deselect SPI * * Returns: o = ok or error code */ static rtems_status_code bsp_spi_send_stop ( rtems_libi2c_bus_t *bh /* bus specifier structure */ ) { #if defined(DEBUG) printk("bsp_spi_send_stop called... "); #endif // FIXME #if defined(DEBUG) printk("... exit OK\r\n"); #endif return 0; } /*=========================================================================*\ | list of handlers | \*=========================================================================*/ rtems_libi2c_bus_ops_t bsp_spi_ops = { init: beagle_spi_init, send_start: bsp_spi_send_start_dummy, send_stop: bsp_spi_send_stop, send_addr: bsp_spi_sel_addr, read_bytes: beagle_spi_read_bytes, write_bytes: beagle_spi_write_bytes, ioctl: beagle_spi_ioctl }; static beagle_spi_desc_t bsp_spi_bus_desc = { {/* public fields */ ops: &bsp_spi_ops, size: sizeof(bsp_spi_bus_desc) }, { /* our private fields */ initialized: FALSE, } }; /*=========================================================================*\ | initialization | \*=========================================================================*/ /* * Register SPI bus and devices * * Returns: Bus number or error code */ rtems_status_code bsp_register_spi ( const char *bus_path, uintptr_t register_base, rtems_vector_number irq ) { int ret_code; int spi_busno; beagle_spi_softc_t *softc_ptr = &bsp_spi_bus_desc.softc; if (softc_ptr->initialized) { printk("ERROR: Only one SPI bus at a time is supported\n"); return -RTEMS_RESOURCE_IN_USE; } softc_ptr->regs_base = register_base; softc_ptr->irq = irq; /* * init I2C library (if not already done) */ rtems_libi2c_initialize(); /* * register SPI bus */ ret_code = rtems_libi2c_register_bus(bus_path, &(bsp_spi_bus_desc.bus_desc)); if (ret_code < 0) { return -ret_code; } spi_busno = ret_code; #if IS_AM335X // TODO: register board devices #endif return spi_busno; }