From patchwork Mon Jul 17 05:10:05 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wenyou Yang X-Patchwork-Id: 789227 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 3x9s1j3jDXz9sBR for ; Mon, 17 Jul 2017 15:15:21 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id 1EB75C21D88; Mon, 17 Jul 2017 05:15:17 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.3 required=5.0 tests=RCVD_IN_DNSWL_MED autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 7B2C9C21D90; Mon, 17 Jul 2017 05:15:13 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 2B11AC21D7D; Mon, 17 Jul 2017 05:15:00 +0000 (UTC) Received: from DVREDG01.corp.atmel.com (nasmtp01.atmel.com [192.199.1.245]) by lists.denx.de (Postfix) with ESMTPS id 5D6B0C21DCE for ; Mon, 17 Jul 2017 05:14:56 +0000 (UTC) Received: from apsmtp01.atmel.com (10.168.254.31) by DVREDG01.corp.atmel.com (10.42.103.30) with Microsoft SMTP Server (TLS) id 14.3.235.1; Sun, 16 Jul 2017 23:14:53 -0600 Received: from shaarm01.corp.atmel.com (10.168.254.13) by apsmtp01.atmel.com (10.168.254.31) with Microsoft SMTP Server id 14.3.235.1; Mon, 17 Jul 2017 13:16:46 +0800 From: Wenyou Yang To: U-Boot Mailing List Date: Mon, 17 Jul 2017 13:10:05 +0800 Message-ID: <20170717051012.19193-2-wenyou.yang@microchip.com> X-Mailer: git-send-email 2.13.0 In-Reply-To: <20170717051012.19193-1-wenyou.yang@microchip.com> References: <20170717051012.19193-1-wenyou.yang@microchip.com> MIME-Version: 1.0 Cc: Marek Vasut , Cyrille Pitchen , Jagan Teki Subject: [U-Boot] [RESEND PATCH 1/8] spi: add support of SPI flash commands X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" From: Cyrille Pitchen This patch introduces 'struct spi_flash_command' and functions spi_is_flash_command_supported() / spi_exec_flash_command(). The 'struct spi_flash_command' describes all the relevant parameters to execute any SPI flash command: - the instruction op code - the number of bytes used to send the address: 0, 3 or 4 bytes - the number of mode and wait-state clock cycles, also called dummy cycles - the number and values of data bytes to be sent or received - the SPI x-y-z protocol [1] - the flash command type [2] [1] SPI x-y-z protocol: - x is the number of I/O lines used to send the instruction op code. - y is the number of I/O lines used during address, mode and wait-state clock cycles. - z is the number of I/O lines used to send or received data. [2] Flash command type: The flash command type is provided to differenciate "memory" read/write/erase operations from "flash internal register" read/write operations. Indeed some SPI controller drivers handle those command type in different ways. However SPI controller drivers should not check the value of the instruction op code to guess the actual kind of flash command to perform. Many instruction op codes are SPI flash manufacturer specific and only drivers/mtd/spi/spi_flash.c should have the knowledge of all of them. Besides, more and more QSPI controllers, like those of TI and Candence, have special way to support (Fast) Read operations using some "memory like" area mapped into the system bus. Hence, if those drivers choose to override the default implementation of spi_is_flash_command_supported() so that their own functions return true only for a "memory read" flash command type, then spi_exec_flash_command() might be used to implement the read from the "memory like" area mapped into the system bus. It means that spi_exec_flash_command() could be used to supersede the actual flash->memory_map mechanism; spi_is_flash_command_supported() / spi_exec_flash_command() being more generic and covering more use cases. For instance, the Atmel QSPI hardware controller uses its "memory like" area mapped ino the system to perform not only (Fast) Read operations but actually all other types of flash commands. Hence the regular SPI API based on the spi_xfer() function is not suited to support the Atmel QSPI controller. Signed-off-by: Cyrille Pitchen Signed-off-by: Wenyou Yang --- drivers/spi/spi-uclass.c | 40 +++++++++++ drivers/spi/spi.c | 13 ++++ include/spi.h | 168 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 221 insertions(+) diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c index c061c05443..b8092538e9 100644 --- a/drivers/spi/spi-uclass.c +++ b/drivers/spi/spi-uclass.c @@ -92,6 +92,30 @@ int dm_spi_xfer(struct udevice *dev, unsigned int bitlen, return spi_get_ops(bus)->xfer(dev, bitlen, dout, din, flags); } +bool dm_spi_is_flash_command_supported(struct udevice *dev, + const struct spi_flash_command *cmd) +{ + struct udevice *bus = dev->parent; + struct dm_spi_ops *ops = spi_get_ops(bus); + + if (ops->is_flash_command_supported) + return ops->is_flash_command_supported(dev, cmd); + + return false; +} + +int dm_spi_exec_flash_command(struct udevice *dev, + const struct spi_flash_command *cmd) +{ + struct udevice *bus = dev->parent; + struct dm_spi_ops *ops = spi_get_ops(bus); + + if (ops->exec_flash_command) + return ops->exec_flash_command(dev, cmd); + + return -EINVAL; +} + int spi_claim_bus(struct spi_slave *slave) { return dm_spi_claim_bus(slave->dev); @@ -108,6 +132,18 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, return dm_spi_xfer(slave->dev, bitlen, dout, din, flags); } +bool spi_is_flash_command_supported(struct spi_slave *slave, + const struct spi_flash_command *cmd) +{ + return dm_spi_is_flash_command_supported(slave->dev, cmd); +} + +int spi_exec_flash_command(struct spi_slave *slave, + const struct spi_flash_command *cmd) +{ + return dm_spi_exec_flash_command(slave->dev, cmd); +} + #if !CONFIG_IS_ENABLED(OF_PLATDATA) static int spi_child_post_bind(struct udevice *dev) { @@ -147,6 +183,10 @@ static int spi_post_probe(struct udevice *bus) ops->set_mode += gd->reloc_off; if (ops->cs_info) ops->cs_info += gd->reloc_off; + if (ops->is_flash_command_supported) + ops->is_flash_command_supported += gd->reloc_off; + if (ops->exec_flash_command) + ops->exec_flash_command += gd->reloc_off; #endif return 0; diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 7d81fbd7f8..e47acdc9e4 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -58,3 +59,15 @@ struct spi_slave *spi_base_setup_slave_fdt(const void *blob, int busnum, return spi_setup_slave(busnum, cs, max_hz, mode); } #endif + +__weak bool spi_is_flash_command_supported(struct spi_slave *slave, + const struct spi_flash_command *cmd) +{ + return false; +} + +__weak int spi_exec_flash_command(struct spi_slave *slave, + const struct spi_flash_command *cmd) +{ + return -EINVAL; +} diff --git a/include/spi.h b/include/spi.h index deb65efdfb..eec36b4689 100644 --- a/include/spi.h +++ b/include/spi.h @@ -10,6 +10,8 @@ #ifndef _SPI_H_ #define _SPI_H_ +#include /* memset() */ + /* SPI mode flags */ #define SPI_CPHA BIT(0) /* clock phase */ #define SPI_CPOL BIT(1) /* clock polarity */ @@ -64,6 +66,116 @@ struct dm_spi_slave_platdata { #endif /* CONFIG_DM_SPI */ /** + * enum spi_flash_protocol - SPI flash command protocol + */ +#define SPI_FPROTO_INST_SHIFT 16 +#define SPI_FPROTO_INST_MASK GENMASK(23, 16) +#define SPI_FPROTO_INST(nbits) \ + ((((unsigned long)(nbits)) << SPI_FPROTO_INST_SHIFT) & \ + SPI_FPROTO_INST_MASK) + +#define SPI_FPROTO_ADDR_SHIFT 8 +#define SPI_FPROTO_ADDR_MASK GENMASK(15, 8) +#define SPI_FPROTO_ADDR(nbits) \ + ((((unsigned long)(nbits)) << SPI_FPROTO_ADDR_SHIFT) & \ + SPI_FPROTO_ADDR_MASK) + +#define SPI_FPROTO_DATA_SHIFT 0 +#define SPI_FPROTO_DATA_MASK GENMASK(7, 0) +#define SPI_FPROTO_DATA(nbits) \ + ((((unsigned long)(nbits)) << SPI_FPROTO_DATA_SHIFT) & \ + SPI_FPROTO_DATA_MASK) + +#define SPI_FPROTO(inst_nbits, addr_nbits, data_nbits) \ + (SPI_FPROTO_INST(inst_nbits) | \ + SPI_FPROTO_ADDR(addr_nbits) | \ + SPI_FPROTO_DATA(data_nbits)) + +enum spi_flash_protocol { + SPI_FPROTO_1_1_1 = SPI_FPROTO(1, 1, 1), + SPI_FPROTO_1_1_2 = SPI_FPROTO(1, 1, 2), + SPI_FPROTO_1_1_4 = SPI_FPROTO(1, 1, 4), + SPI_FPROTO_1_2_2 = SPI_FPROTO(1, 2, 2), + SPI_FPROTO_1_4_4 = SPI_FPROTO(1, 4, 4), + SPI_FPROTO_2_2_2 = SPI_FPROTO(2, 2, 2), + SPI_FPROTO_4_4_4 = SPI_FPROTO(4, 4, 4), +}; + +static inline +u8 spi_flash_protocol_get_inst_nbits(enum spi_flash_protocol proto) +{ + return ((unsigned long)(proto & SPI_FPROTO_INST_MASK)) >> + SPI_FPROTO_INST_SHIFT; +} + +static inline +u8 spi_flash_protocol_get_addr_nbits(enum spi_flash_protocol proto) +{ + return ((unsigned long)(proto & SPI_FPROTO_ADDR_MASK)) >> + SPI_FPROTO_ADDR_SHIFT; +} + +static inline +u8 spi_flash_protocol_get_data_nbits(enum spi_flash_protocol proto) +{ + return ((unsigned long)(proto & SPI_FPROTO_DATA_MASK)) >> + SPI_FPROTO_DATA_SHIFT; +} + +/** + * struct spi_flash_command - SPI flash command structure + * + * @instr: Opcode sent to the SPI slave during instr clock cycles. + * @mode: Value sent to the SPI slave during mode clock cycles. + * @num_mode_cycles: Number of mode clock cycles. + * @num_wait_states: Number of wait-state clock cycles. + * @addr_len: Number of bytes sent during address clock cycles: + * should be 0, 3, or 4. + * @addr: Value sent to the SPI slave during address clock cycles. + * @data_len: Number of bytes to be sent during data clock cycles. + * @tx_data: Data sent to the SPI slave during data clock cycles. + * @rx_data: Data read from the SPI slave during data clock cycles. + */ +struct spi_flash_command { + enum spi_flash_protocol proto; + u8 flags; +#define SPI_FCMD_TYPE GENMASK(2, 0) +#define SPI_FCMD_READ (0x0U << 0) +#define SPI_FCMD_WRITE (0x1U << 0) +#define SPI_FCMD_ERASE (0x2U << 0) +#define SPI_FCMD_READ_REG (0x3U << 0) +#define SPI_FCMD_WRITE_REG (0x4U << 0) + + u8 inst; + u8 mode; + u8 num_mode_cycles; + u8 num_wait_states; + u8 addr_len; + u32 addr; + size_t data_len; + const void *tx_data; + void *rx_data; +}; + +/** + * Initialize a 'struct spi_flash_command'. + * + * @cmd: Pointer to the 'struct spi_flash_command' to initialize. + * @instr: Instruction opcode. + * @addr_len: Number of address bytes. + */ +static inline void +spi_flash_command_init(struct spi_flash_command *cmd, + u8 inst, u8 addr_len, u8 flags) +{ + memset(cmd, 0, sizeof(*cmd)); + cmd->proto = SPI_FPROTO_1_1_1; + cmd->inst = inst; + cmd->addr_len = addr_len; + cmd->flags = flags; +} + +/** * struct spi_slave - Representation of a SPI slave * * For driver model this is the per-child data used by the SPI bus. It can @@ -252,6 +364,24 @@ int spi_set_wordlen(struct spi_slave *slave, unsigned int wordlen); int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, void *din, unsigned long flags); +/** + * Check whether the given SPI flash command is supported + * + * @slave: The SPI slave + * @cmd: The SPI flash command to check. + */ +bool spi_is_flash_command_supported(struct spi_slave *slave, + const struct spi_flash_command *cmd); + +/** + * Execute SPI flash command + * + * @slave: The SPI slave which will execute the give SPI flash command. + * @cmd: The SPI flash command to execute. + */ +int spi_exec_flash_command(struct spi_slave *slave, + const struct spi_flash_command *cmd); + /* Copy memory mapped data */ void spi_flash_copy_mmap(void *data, void *offset, size_t len); @@ -464,6 +594,26 @@ struct dm_spi_ops { * is invalid, other -ve value on error */ int (*cs_info)(struct udevice *bus, uint cs, struct spi_cs_info *info); + + /** + * Check whether the given SPI flash command is supported. + * + * @bus: The SPI bus + * @cmd: The SPI flash command to check. + * @return: true if supported, false otherwise + */ + bool (*is_flash_command_supported)(struct udevice *bus, + const struct spi_flash_command *cmd); + + /** + * Execute a SPI flash command + * + * @bus: The SPI bus + * @cmd: The SPI flash command to execute. + * @return 0 if OK, -ve on error + */ + int (*exec_flash_command)(struct udevice *bus, + const struct spi_flash_command *cmd); }; struct dm_spi_emul_ops { @@ -651,6 +801,24 @@ void dm_spi_release_bus(struct udevice *dev); int dm_spi_xfer(struct udevice *dev, unsigned int bitlen, const void *dout, void *din, unsigned long flags); +/** + * Check whether the given SPI flash command is supported. + * + * @dev: The SPI slave device + * @cmd: The SPI flash command + */ +bool dm_spi_is_flash_command_supported(struct udevice *dev, + const struct spi_flash_command *cmd); + +/** + * Execute the given SPI flash command. + * + * @dev: The SPI slave device + * @cmd: The SPI flash command + */ +int dm_spi_exec_flash_command(struct udevice *dev, + const struct spi_flash_command *cmd); + /* Access the operations for a SPI device */ #define spi_get_ops(dev) ((struct dm_spi_ops *)(dev)->driver->ops) #define spi_emul_get_ops(dev) ((struct dm_spi_emul_ops *)(dev)->driver->ops)