Re: [PATCH 3/5] spi: spi-qpic: Add qpic spi nand driver support

From: Mark Brown
Date: Thu Feb 15 2024 - 09:15:36 EST


On Thu, Feb 15, 2024 at 07:18:54PM +0530, Md Sadre Alam wrote:

> +config SPI_QPIC_SNAND
> + tristate "QPIC SNAND controller"
> + default y

Why is this driver so special it should be enabled by default?

> + depends on ARCH_QCOM

Please add an || COMPILE_TEST so this gets some build coverage.

> + help
> + QPIC_SNAND (QPIC SPI NAND) driver for Qualcomm QPIC controller.
> + QPIC controller supports both parallel nand and serial nand.
> + This config will enable serial nand driver for QPIC controller.
> +
> config SPI_QUP
> tristate "Qualcomm SPI controller with QUP interface"
> depends on ARCH_QCOM || COMPILE_TEST
> diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
> index 4ff8d725ba5e..1ac3bac35007 100644
> --- a/drivers/spi/Makefile
> +++ b/drivers/spi/Makefile
> @@ -153,6 +153,7 @@ obj-$(CONFIG_SPI_XTENSA_XTFPGA) += spi-xtensa-xtfpga.o
> obj-$(CONFIG_SPI_ZYNQ_QSPI) += spi-zynq-qspi.o
> obj-$(CONFIG_SPI_ZYNQMP_GQSPI) += spi-zynqmp-gqspi.o
> obj-$(CONFIG_SPI_AMD) += spi-amd.o
> +obj-$(CONFIG_SPI_QPIC_SNAND) += spi-qpic-snand.o

Please keep this sorted.

> --- /dev/null
> +++ b/drivers/spi/spi-qpic-snand.c
> @@ -0,0 +1,1025 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved.

Please make the entire comment a C++ one so things look more
intentional.

> +#define snandc_set_read_loc_first(snandc, reg, cw_offset, read_size, is_last_read_loc) \
> +snandc_set_reg(snandc, reg, \
> + ((cw_offset) << READ_LOCATION_OFFSET) | \
> + ((read_size) << READ_LOCATION_SIZE) | \
> + ((is_last_read_loc) << READ_LOCATION_LAST))
> +
> +#define snandc_set_read_loc_last(snandc, reg, cw_offset, read_size, is_last_read_loc) \
> +snandc_set_reg(snandc, reg, \
> + ((cw_offset) << READ_LOCATION_OFFSET) | \
> + ((read_size) << READ_LOCATION_SIZE) | \
> + ((is_last_read_loc) << READ_LOCATION_LAST))

For type safety and legibility please write these as functions, mark
them as static inline if needed.

> +void snandc_set_reg(struct qcom_nand_controller *snandc, int offset, u32 val)
> +{
> + struct nandc_regs *regs = snandc->regs;
> + __le32 *reg;
> +
> + reg = offset_to_nandc_reg(regs, offset);
> +
> + if (reg)
> + *reg = cpu_to_le32(val);
> +}

This silently ignores writes to invalid registers, that doesn't seem
great.

> + return snandc->ecc_stats.failed ? -EBADMSG : snandc->ecc_stats.bitflips;

For legibility please just write normal conditional statements.

> +static int qpic_snand_program_execute(struct qcom_nand_controller *snandc,
> + const struct spi_mem_op *op)
> +{

> + int num_cw = 4;

> + data_buf = (u8 *)snandc->wbuf;

Why the cast? If it's needed that smells like it's masking a bug, it
looks like it's casting from a u8 * to a u8 *.

> + for (i = 0; i < num_cw; i++) {
> + int data_size;

All these functions appear to hard code "num_cw" to 4. What is "num_cw"
and why are we doing this per function?

> +static int qpic_snand_program_execute(struct qcom_nand_controller *snandc,
> + const struct spi_mem_op *op)

> + if (op->cmd.opcode == SPINAND_READID) {
> + snandc->buf_count = 4;
> + read_reg_dma(snandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL);
> +
> + ret = submit_descs(snandc);
> + if (ret)
> + dev_err(snandc->dev, "failure in submitting descriptor for readid\n");
> +
> + nandc_read_buffer_sync(snandc, true);
> + memcpy(op->data.buf.in, snandc->reg_read_buf, snandc->buf_count);

These memcpy()s don't seem great, why aren't we just reading directly
into the output buffer?

> + if (op->cmd.opcode == SPINAND_GET_FEATURE) {

This function looks like it should be a switch statement.

> +static bool qpic_snand_is_page_op(const struct spi_mem_op *op)
> +{

> + if (op->data.dir == SPI_MEM_DATA_IN) {
> + if (op->addr.buswidth == 4 && op->data.buswidth == 4)
> + return true;
> +
> + if (op->addr.nbytes == 2 && op->addr.buswidth == 1)
> + return true;
> +
> + } else if (op->data.dir == SPI_MEM_DATA_OUT) {
> + if (op->data.buswidth == 4)
> + return true;
> + if (op->addr.nbytes == 2 && op->addr.buswidth == 1)
> + return true;
> + }

Again looks like a switch statement.

> + ctlr = devm_spi_alloc_master(dev, sizeof(*snandc));
> + if (!ctlr)
> + return -ENOMEM;

Please use _alloc_controller.

> +static int qcom_snand_remove(struct platform_device *pdev)
> +{
> + struct spi_controller *ctlr = platform_get_drvdata(pdev);
> +
> + spi_unregister_controller(ctlr);
> +
> + return 0;
> +}

We don't disable any of the clocks in the remove path.

Attachment: signature.asc
Description: PGP signature