diff options
Diffstat (limited to 'firmware/target/mips/ingenic_x1000/sfc-x1000.h')
-rw-r--r-- | firmware/target/mips/ingenic_x1000/sfc-x1000.h | 152 |
1 files changed, 86 insertions, 66 deletions
diff --git a/firmware/target/mips/ingenic_x1000/sfc-x1000.h b/firmware/target/mips/ingenic_x1000/sfc-x1000.h index 283f171697..afb4aa3ce6 100644 --- a/firmware/target/mips/ingenic_x1000/sfc-x1000.h +++ b/firmware/target/mips/ingenic_x1000/sfc-x1000.h @@ -19,87 +19,107 @@ * ****************************************************************************/ +#ifndef __SFC_X1000_H__ +#define __SFC_X1000_H__ + +#include "x1000/sfc.h" #include <stdint.h> #include <stdbool.h> -#include "clk-x1000.h" -#include "x1000/sfc.h" -/* SPI flash controller interface -- this is a low-level driver upon which - * you can build NAND/NOR flash drivers. The main function is sfc_exec(), - * used to issue commands, transfer data, etc. +/* SPI transfer mode. SFC_TMODE_X_Y_Z means: + * + * - X lines for command phase + * - Y lines for address+dummy phase + * - Z lines for data phase + */ +#define SFC_TMODE_1_1_1 0 +#define SFC_TMODE_1_1_2 1 +#define SFC_TMODE_1_2_2 2 +#define SFC_TMODE_2_2_2 3 +#define SFC_TMODE_1_1_4 4 +#define SFC_TMODE_1_4_4 5 +#define SFC_TMODE_4_4_4 6 + +/* Phase format + * _____________________ + * / SFC_PFMT_ADDR_FIRST \ + * +-----+-------+-------+------+ + * | cmd | addr | dummy | data | + * +-----+-------+-------+------+ + * ______________________ + * / SFC_PFMT_DUMMY_FIRST \ + * +-----+-------+-------+------+ + * | cmd | dummy | addr | data | + * +-----+-------+-------+------+ */ +#define SFC_PFMT_ADDR_FIRST 0 +#define SFC_PFMT_DUMMY_FIRST 1 -#define SFC_FLAG_READ 0x01 /* Read data */ -#define SFC_FLAG_WRITE 0x02 /* Write data */ -#define SFC_FLAG_DUMMYFIRST 0x04 /* Do dummy bits before sending address. - * Default is dummy bits after address. - */ +/* Direction of transfer flag */ +#define SFC_READ 0 +#define SFC_WRITE (1 << 31) -/* SPI transfer mode. If in doubt, check with the X1000 manual and confirm - * the transfer format is what you expect. +/** \brief Macro to generate an SFC command for use with sfc_exec() + * \param cmd Command number (up to 16 bits) + * \param tmode SPI transfer mode + * \param awidth Number of address bytes + * \param dwidth Number of dummy cycles (1 cycle = 1 bit) + * \param pfmt Phase format (address first or dummy first) + * \param data_en 1 to enable data phase, 0 to omit it */ -#define SFC_MODE_STANDARD 0 -#define SFC_MODE_DUAL_IN_DUAL_OUT 1 -#define SFC_MODE_DUAL_IO 2 -#define SFC_MODE_FULL_DUAL_IO 3 -#define SFC_MODE_QUAD_IN_QUAD_OUT 4 -#define SFC_MODE_QUAD_IO 5 -#define SFC_MODE_FULL_QUAD_IO 6 +#define SFC_CMD(cmd, tmode, awidth, dwidth, pfmt, data_en) \ + jz_orf(SFC_TRAN_CONF, COMMAND(cmd), CMD_EN(1), \ + MODE(tmode), ADDR_WIDTH(awidth), DUMMY_BITS(dwidth), \ + PHASE_FMT(pfmt), DATA_EN(data_en)) -/* Return status codes for sfc_exec() */ -#define SFC_STATUS_OK 0 -#define SFC_STATUS_OVERFLOW 1 -#define SFC_STATUS_UNDERFLOW 2 -#define SFC_STATUS_LOCKUP 3 +/* Open/close SFC hardware */ +extern void sfc_open(void); +extern void sfc_close(void); -typedef struct sfc_op { - int command; /* Command number */ - int mode; /* SPI transfer mode */ - int flags; /* Flags for this op */ - int addr_bytes; /* Number of address bytes */ - int dummy_bits; /* Number of dummy bits (yes: bits, not bytes) */ - uint32_t addr_lo; /* Lower 32 bits of address */ - uint32_t addr_hi; /* Upper 32 bits of address */ - int data_bytes; /* Number of data bytes to read/write */ - void* buffer; /* Data buffer -- MUST be word-aligned */ -} sfc_op; +/* Enable IRQ mode, instead of busy waiting for operations to complete. + * Needs to be called separately after sfc_open(), because the SPL has to + * use busy waiting, but we cannot #ifdef it for the SPL due to limitations + * of the build system. */ +extern void sfc_irq_begin(void); +extern void sfc_irq_end(void); -/* One-time driver init for mutexes/etc needed for handling interrupts. - * This can be safely called multiple times; only the first call will - * actually perform the init. - */ -extern void sfc_init(void); +/* Change the SFC clock frequency */ +extern void sfc_set_clock(uint32_t freq); + +/* Set the device configuration register */ +static inline void sfc_set_dev_conf(uint32_t conf) +{ + REG_SFC_DEV_CONF = conf; +} -/* Controller mutex -- lock before touching the driver */ -extern void sfc_lock(void); -extern void sfc_unlock(void); +/* Control the state of the write protect pin */ +static inline void sfc_set_wp_enable(bool en) +{ + jz_writef(SFC_GLB, WP_EN(en ? 1 : 0)); +} -/* Open/close the driver. The driver must be open in order to do operations. - * Closing the driver shuts off the hardware; the driver can be re-opened at - * a later time when it's needed again. +/** \brief Execute a command + * \param cmd Command encoded by `SFC_CMD` macro. + * \param addr Address up to 32 bits; pass 0 if the command doesn't need it + * \param data Buffer for data transfer commands, must be cache-aligned + * \param size Number of data bytes / direction of transfer flag + * \returns SFC status code: 0 on success and < 0 on failure. * - * After opening the driver, you must also program a valid device configuration - * and clock rate using sfc_set_dev_conf() and sfc_set_clock(). + * - Non-data commands must pass `data = NULL` and `size = 0` in order to + * get correct results. + * + * - Data commands must specify a direction of transfer using the high bit + * of the `size` argument by OR'ing in `SFC_READ` or `SFC_WRITE`. */ -extern void sfc_open(void); -extern void sfc_close(void); +extern void sfc_exec(uint32_t cmd, uint32_t addr, void* data, uint32_t size); -/* These functions can be called at any time while the driver is open, but - * must not be called while there is an operation in progress. It's the - * caller's job to ensure the configuration will work with the device and - * be capable of reading back data correctly. +/* NOTE: the above will need to be changed if we need better performance + * The hardware can do multiple commands in a sequence, including polling, + * and emit an interrupt only at the end. * - * - sfc_set_dev_conf() writes its argument to the SFC_DEV_CONF register. - * - sfc_set_wp_enable() sets the state of the write-protect pin (WP). - * - sfc_set_clock() sets the controller clock frequency (in Hz). + * Also, some chips need more than 4 address bytes even though the block + * and page numbers would still fit in 32 bits; the current API cannot + * handle this. */ -#define sfc_set_dev_conf(dev_conf) \ - do { REG_SFC_DEV_CONF = (dev_conf); } while(0) - -#define sfc_set_wp_enable(en) \ - jz_writef(SFC_GLB, WP_EN((en) ? 1 : 0)) - -extern void sfc_set_clock(x1000_clk_t clksrc, uint32_t freq); -/* Execute an operation. Returns zero on success, nonzero on failure. */ -extern int sfc_exec(const sfc_op* op); +#endif /* __SFC_X1000_H__ */ |