// vim: set et: #ifndef SERPROG_H_ #define SERPROG_H_ enum serprog_cmd { S_CMD_NOP = 0x00, S_CMD_Q_IFACE = 0x01, S_CMD_Q_CMDMAP = 0x02, S_CMD_Q_PGMNAME = 0x03, S_CMD_Q_SERBUF = 0x04, S_CMD_Q_BUSTYPE = 0x05, S_CMD_Q_CHIPSIZE = 0x06, S_CMD_Q_OPBUF = 0x07, S_CMD_Q_WRNMAXLEN = 0x08, S_CMD_R_BYTE = 0x09, S_CMD_R_NBYTES = 0x0a, S_CMD_O_INIT = 0x0b, S_CMD_O_WRITEB = 0x0c, S_CMD_O_WRITEN = 0x0d, S_CMD_O_DELAY = 0x0e, S_CMD_O_EXEC = 0x0f, S_CMD_SYNCNOP = 0x10, S_CMD_Q_RDNMAXLEN = 0x11, S_CMD_S_BUSTYPE = 0x12, S_CMD_SPIOP = 0x13, S_CMD_S_SPI_FREQ = 0x14, S_CMD_S_PINSTATE = 0x15, // TODO: upstream this to flashrom? could be useful to others maybe S_CMD_Q_SPI_CAPS = 0x40, // number of chip (well, bitflags) to use when asserting/deasserting the chip select line S_CMD_S_SPI_CHIPN = 0x41, // sets chip select line high or low (device does no translation wrt. S_FLG_CSACHI!) S_CMD_S_SPI_SETCS = 0x42, S_CMD_S_SPI_FLAGS = 0x43, S_CMD_S_SPI_BPW = 0x44, // set bits per word S_CMD_SPI_READ = 0x45, S_CMD_SPI_WRITE = 0x46, // as opposed to S_CMD_SPIOP, this one is full-duplex instead of half-duplex S_CMD_SPI_RDWR = 0x47, }; enum serprog_response { S_ACK = 0x06, S_NAK = 0x15 }; enum serprog_flags { S_FLG_CPHA = 1<<0, // 1: clock phase 1, else clkpha 1 S_FLG_CPOL = 1<<1, // 1: clock polarity 1, else clkpol 0 S_FLG_STDSPI = 0<<2, // frame format S_FLG_TISSP = 1<<2, // frame format S_FLG_MICROW = 2<<2, // frame format S_FLG_MSBFST = 0<<4, // MSBit sent first S_FLG_LSBFST = 1<<4, // LSBit sent first S_FLG_CSACLO = 0<<5, // chip select active-low (common) S_FLG_CSACHI = 1<<5, // chip select active-high S_FLG_3WIRE = 1<<6, }; enum serprog_caps { S_CAP_CPHA_HI = 1<<0, S_CAP_CPHA_LO = 1<<1, S_CAP_CPOL_HI = 1<<2, S_CAP_CPOL_LO = 1<<3, S_CAP_STDSPI = 1<<4, // standard SPI S_CAP_TISSP = 1<<5, // synchronous serial protocol from TI S_CAP_MICROW = 1<<6, // microwire S_CAP_MSBFST = 1<<7, S_CAP_LSBFST = 1<<8, S_CAP_CSACHI = 1<<9, S_CAP_3WIRE = 1<<10, }; #define SERPROG_IFACE_VERSION 0x0001 /* It’s easy to be confused here, and the vendor documentation you’ll find isn’t necessarily helpful. The four modes combine two mode bits: CPOL indicates the initial clock polarity. CPOL=0 means the clock starts low, so the first (leading) edge is rising, and the second (trailing) edge is falling. CPOL=1 means the clock starts high, so the first (leading) edge is falling. CPHA indicates the clock phase used to sample data; CPHA=0 says sample on the leading edge, CPHA=1 means the trailing edge. Since the signal needs to stablize before it’s sampled, CPHA=0 implies that its data is written half a clock before the first clock edge. The chipselect may have made it become available. Chip specs won’t always say “uses SPI mode X” in as many words, but their timing diagrams will make the CPOL and CPHA modes clear. In the SPI mode number, CPOL is the high order bit and CPHA is the low order bit. So when a chip’s timing diagram shows the clock starting low (CPOL=0) and data stabilized for sampling during the trailing clock edge (CPHA=1), that’s SPI mode 1. Note that the clock mode is relevant as soon as the chipselect goes active. So the master must set the clock to inactive before selecting a slave, and the slave can tell the chosen polarity by sampling the clock level when its select line goes active. That’s why many devices support for example both modes 0 and 3: they don’t care about polarity, and always clock data in/out on rising clock edges. - Linux kernel docs */ #ifdef DBOARD_HAS_SPI /* functions to be implemented by the BSP */ uint32_t /*freq_applied*/ sp_spi_set_freq(uint32_t freq_wanted); enum serprog_flags sp_spi_set_flags(enum serprog_flags flags); uint8_t sp_spi_set_bpw(uint8_t bpw); struct sp_spi_caps { uint32_t freq_min, freq_max; uint16_t caps; uint8_t num_cs, min_bpw, max_bpw; }; __attribute__((__const__)) const struct sp_spi_caps* sp_spi_get_caps(void); void sp_spi_init(void); void sp_spi_deinit(void); void sp_spi_cs_deselect(uint8_t csflags); void sp_spi_cs_select(uint8_t csflags); void sp_spi_op_write(uint32_t write_len, const void* write_data); void sp_spi_op_read(uint32_t read_len, void* read_data); void sp_spi_op_read_write(uint32_t len, void* read_data, const void* write_data); /* serprog-specific */ void sp_spi_op_begin(uint8_t csflags); void sp_spi_op_end(uint8_t csflags); // half-duplex /*static inline void sp_spi_op_do(uint32_t write_len, const uint8_t* write_data, uint32_t read_len, uint8_t* read_data) { sp_spi_op_begin(); sp_spi_op_write(write_len, write_data); sp_spi_op_write(read_len, read_data); sp_spi_op_end(); }*/ /* protocol handling functions */ __attribute__((__const__)) uint32_t sp_spi_get_buf_limit(void); // rdnmaxlen, wrnmaxlen void cdc_serprog_init(void); void cdc_serprog_deinit(void); void cdc_serprog_task(void); void sp_spi_bulk_cmd(void); #endif #endif