[PATCH v10] mtd: spi-nor: add hisilicon spi-nor flash controller driver

Jiancheng Xue xuejiancheng at hisilicon.com
Thu Apr 28 19:13:20 PDT 2016


Hi Brian,

On 2016/4/27 23:38, Brian Norris wrote:
> Hi Jiancheng,
> 
> Cyrille hit on one of my concerns; if posible we'd like not to have you
> sniffing the opcodes in read_reg()/write_reg(). But let's keep the
> discussion on that thread.
> 
OK. I'll look into this issue seriously and reply on that thread later.

> Two other comments below.
> 
> On Tue, Apr 19, 2016 at 03:27:19PM +0800, Jiancheng Xue wrote:
>> From: Jiancheng Xue <xuejiancheng at huawei.com>
>>
>> Add hisilicon spi-nor flash controller driver
>>
>> Signed-off-by: Binquan Peng <pengbinquan at hisilicon.com>
>> Signed-off-by: Jiancheng Xue <xuejiancheng at hisilicon.com>
>> Acked-by: Rob Herring <robh at kernel.org>
>> Reviewed-by: Ezequiel Garcia <ezequiel at vanguardiasur.com.ar>
>> ---
[...]
>> diff --git a/drivers/mtd/spi-nor/hisi-sfc.c b/drivers/mtd/spi-nor/hisi-sfc.c
>> new file mode 100644
>> index 0000000..942dafd
>> --- /dev/null
>> +++ b/drivers/mtd/spi-nor/hisi-sfc.c
>> @@ -0,0 +1,539 @@
> 
> [...]
> 
>> +static void hisi_spi_nor_dma_transfer(struct spi_nor *nor, loff_t start_off,
>> +		dma_addr_t dma_buf, size_t len, u8 op_type)
>> +{
>> +	struct hifmc_priv *priv = nor->priv;
>> +	struct hifmc_host *host = priv->host;
>> +	u8 if_type = 0, dummy = 0;
>> +	u8 w_cmd = 0, r_cmd = 0;
>> +	u32 reg;
>> +
>> +	writel(start_off, host->regbase + FMC_ADDRL);
>> +
>> +	if (op_type == FMC_OP_READ) {
>> +		if_type = get_if_type(nor->flash_read);
>> +		dummy = nor->read_dummy >> 3;
>> +		r_cmd = nor->read_opcode;
>> +	} else {
>> +		w_cmd = nor->program_opcode;
>> +	}
>> +
>> +	reg = OP_CFG_FM_CS(priv->chipselect)
>> +		| OP_CFG_MEM_IF_TYPE(if_type)
>> +		| OP_CFG_ADDR_NUM(nor->addr_width)
>> +		| OP_CFG_DUMMY_NUM(dummy);
>> +	writel(reg, host->regbase + FMC_OP_CFG);
>> +
>> +	reg = FMC_DMA_LEN_SET(len);
>> +	writel(reg, host->regbase + FMC_DMA_LEN);
>> +	writel(dma_buf, host->regbase + FMC_DMA_SADDR_D0);
>> +
>> +	reg = OP_CTRL_RD_OPCODE(r_cmd)
>> +		| OP_CTRL_WR_OPCODE(w_cmd)
>> +		| OP_CTRL_RW_OP(op_type)
>> +		| OP_CTRL_DMA_OP_READY;
>> +	writel(0xff, host->regbase + FMC_INT_CLR);
>> +	writel(reg, host->regbase + FMC_OP_DMA);
>> +	wait_op_finish(host);
> 
> Do you want to return the status here, so we can handle errors?
> 

OK. I'll fix it in v11.

>> +}
>> +
>> +static int hisi_spi_nor_read(struct spi_nor *nor, loff_t from, size_t len,
>> +		size_t *retlen, u_char *read_buf)
>> +{
>> +	struct hifmc_priv *priv = nor->priv;
>> +	struct hifmc_host *host = priv->host;
>> +	int offset;
>> +
>> +	/* read all bytes in only one read */
>> +	if (len <= HIFMC_DMA_MAX_LEN) {
>> +		hisi_spi_nor_dma_transfer(nor, from, host->dma_buffer,
>> +				len, FMC_OP_READ);
>> +		memcpy(read_buf, host->buffer, len);
> 
> Why do you have a special case for "all in one read"? This is just a
> basic loop...
> 
See below.
>> +	} else {
>> +		/* read HIFMC_DMA_MAX_LEN bytes at a time */
>> +		for (offset = 0; offset < len; offset += HIFMC_DMA_MAX_LEN) {
>> +			hisi_spi_nor_dma_transfer(nor, from + offset,
>> +				host->dma_buffer,
>> +				HIFMC_DMA_MAX_LEN, FMC_OP_READ);
>> +			memcpy(read_buf + offset,
>> +				host->buffer, HIFMC_DMA_MAX_LEN);
>> +		}
>> +		/* read remaining bytes */
>> +		offset -= HIFMC_DMA_MAX_LEN;
>> +		hisi_spi_nor_dma_transfer(nor, from + offset, host->dma_buffer,
>> +				len - offset, FMC_OP_READ);
>> +		memcpy(read_buf + offset, host->buffer, len - offset);
>> +	}
> 
> I think the entire if/else can be rewritten as:
> 
> 	for (offset = 0; offset < len; offset += HIFMC_DMA_MAX_LEN) {
> 		size_t trans = min(HIFMC_DMA_MAX_LEN, len - offset);
> 
> 		hisi_spi_nor_dma_transfer(nor, from + offset, host->dma_buffer,
> 					  trans, FMC_OP_READ);
> 		memcpy(read_buf + offset, host->buffer, trans);
> 	}
> 
I had referred to the implementation of spi_nor_write. But now it seems much
simpler and clearer above. I'll apply this code in v11. Thank you very much.

>> +	*retlen = len;
>> +
>> +	return 0;
>> +}
>> +
>> +static void hisi_spi_nor_write(struct spi_nor *nor, loff_t to,
>> +			size_t len, size_t *retlen, const u_char *write_buf)
>> +{
>> +	struct hifmc_priv *priv = nor->priv;
>> +	struct hifmc_host *host = priv->host;
>> +
>> +	/* len is smaller than HIFMC_DMA_MAX_LEN*/
> 
> Where do you get that assumption? This function must handle whatever
> 'len' is passed to it, and that may be large. (I suspect your error is
> inspired by the fact that mtd-utils *usually* does smaller write
> transfers.)
> 
In spi_nor_write, nor->write is called with the len not bigger than nor->page_size.
I wrongly thought nor->page_size was always smaller than HIFMC_DMA_MAX_LEN.
I'll rewrite this function referring to _read function. Thank you!

Regards,
Jiancheng




More information about the linux-mtd mailing list