Re: [RFC PATCH 4/5] spi: qpic: Add support for qpic spi nand driver

From: Mark Brown
Date: Tue Oct 31 2023 - 10:23:29 EST


On Tue, Oct 31, 2023 at 05:33:06PM +0530, Md Sadre Alam wrote:

> +config SPI_QPIC_SNAND
> + tristate "QPIC SNAND controller"
> + default y
> + depends on ARCH_QCOM
> + help
> + QPIC_SNAND(Quad SPI) driver for Qualcomm QPIC_SNAND controller.
> +

I don't see any build dependencies on anything QC specific so please add
an || COMPILE_TEST here, this makes it much easier to do generic changes
without having to build some specific config.

> +++ 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 alphabetically sorted (there are some mistakes there
but no need to add to them).

> + * Sricharan R <quic_srichara@xxxxxxxxxxx>
> + */
> +
> +#include <linux/mtd/spinand.h>
> +#include <linux/mtd/nand-qpic-common.h>
> +

This should be including the SPI API, and other API headers that are
used directly like the platform and clock APIs.

> +static int qcom_snand_init(struct qcom_nand_controller *snandc)
> +{
> + u32 snand_cfg_val = 0x0;
> + int ret;

...

> + ret = submit_descs(snandc);
> + if (ret)
> + dev_err(snandc->dev, "failure in sbumitting spiinit descriptor\n");
> +
> + free_descs(snandc);

This seems to be doing a bit more than I would expect an init function
to, and it's very surprising to see the descriptors freed immediately
after something called a submit (which suggests that the descriptors are
still in flight).

> +static int qpic_snand_read_page(struct qcom_nand_controller *snandc,
> + const struct spi_mem_op *op)
> +{
> + return 0;
> +}
> +
> +static int qpic_snand_write_page(struct qcom_nand_controller *snandc,
> + const struct spi_mem_op *op)
> +{
> + return 0;
> +}

...

> +static int qpic_snand_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
> +{
> + struct qcom_nand_controller *snandc = spi_controller_get_devdata(mem->spi->master);
> + dev_dbg(snandc->dev, "OP %02x ADDR %08llX@%d:%u DATA %d:%u", op->cmd.opcode,
> + op->addr.val, op->addr.buswidth, op->addr.nbytes,
> + op->data.buswidth, op->data.nbytes);
> +
> + /*
> + * Check for page ops or normal ops
> + */
> + if (qpic_snand_is_page_op(op)) {
> + if (op->data.dir == SPI_MEM_DATA_IN)
> + return qpic_snand_read_page(snandc, op);
> + else
> + return qpic_snand_write_page(snandc, op);

So does the device actually support page operations? The above looks
like the driver will silently noop them.

> + snandc->base_phys = res->start;
> + snandc->base_dma = dma_map_resource(dev, res->start,
> + resource_size(res),
> + DMA_BIDIRECTIONAL, 0);
> + if (dma_mapping_error(dev, snandc->base_dma))
> + return -ENXIO;
> +
> + ret = clk_prepare_enable(snandc->core_clk);
> + if (ret)
> + goto err_core_clk;

The DMA mapping and clock enables only get undone in error handling,
they're not undone in the normal device release path.

> +
> + ret = clk_prepare_enable(snandc->aon_clk);
> + if (ret)
> + goto err_aon_clk;
> +
> + ret = clk_prepare_enable(snandc->iomacro_clk);
> + if (ret)
> + goto err_snandc_alloc;
> +
> + ret = qcom_nandc_alloc(snandc);
> + if (ret)
> + goto err_snandc_alloc;
> +
> + ret = qcom_snand_init(snandc);
> + if (ret)
> + goto err_init;
> +
> + // setup ECC engine
> + snandc->ecc_eng.dev = &pdev->dev;
> + snandc->ecc_eng.integration = NAND_ECC_ENGINE_INTEGRATION_PIPELINED;
> + snandc->ecc_eng.ops = &qcom_snand_ecc_engine_ops;
> + snandc->ecc_eng.priv = snandc;
> +
> + ret = nand_ecc_register_on_host_hw_engine(&snandc->ecc_eng);
> + if (ret) {
> + dev_err(&pdev->dev, "failed to register ecc engine.\n");
> + goto err_init;
> + }
> +
> + ctlr->num_chipselect = QPIC_QSPI_NUM_CS;
> + ctlr->mem_ops = &qcom_spi_mem_ops;
> + ctlr->mem_caps = &qcom_snand_mem_caps;
> + ctlr->dev.of_node = pdev->dev.of_node;
> + ctlr->mode_bits = SPI_TX_DUAL | SPI_RX_DUAL |
> + SPI_TX_QUAD | SPI_RX_QUAD;
> +
> + ret = spi_register_master(ctlr);
> + if (ret) {
> + dev_err(&pdev->dev, "spi_register_controller failed.\n");
> + goto err_init;
> + }
> +
> + return 0;
> +err_init:
> + qcom_nandc_unalloc(snandc);
> +err_snandc_alloc:
> + clk_disable_unprepare(snandc->aon_clk);
> +err_aon_clk:
> + clk_disable_unprepare(snandc->core_clk);
> +err_core_clk:
> + dma_unmap_resource(dev, res->start, resource_size(res),
> + DMA_BIDIRECTIONAL, 0);
> +
> + return ret;
> +}
> +
> +static int qcom_snand_remove(struct platform_device *pdev)
> +{
> + struct spi_controller *ctlr = platform_get_drvdata(pdev);
> + spi_unregister_master(ctlr);
> +
> + return 0;
> +}
> +
> +static const struct qcom_nandc_props ipq9574_snandc_props = {
> + .dev_cmd_reg_start = 0x7000,
> + .is_bam = true,
> + .qpic_v2 = true,
> +};
> +
> +static const struct of_device_id qcom_snandc_of_match[] = {
> + {
> + .compatible = "qcom,ipq9574-nand",
> + .data = &ipq9574_snandc_props,
> + },
> + {}
> +}
> +MODULE_DEVICE_TABLE(of, qcom_snandc_of_match);
> +
> +static struct platform_driver qcom_snand_driver = {
> + .driver = {
> + .name = "qcom_snand",
> + .of_match_table = qcom_snandc_of_match,
> + },
> + .probe = qcom_snand_probe,
> + .remove = qcom_snand_remove,
> +};
> +module_platform_driver(qcom_snand_driver);
> +
> +MODULE_DESCRIPTION("SPI driver for QPIC QSPI cores");
> +MODULE_AUTHOR("Md Sadre Alam <quic_mdalam@xxxxxxxxxxx>");
> +MODULE_LICENSE("GPL");
> --
> 2.34.1
>

Attachment: signature.asc
Description: PGP signature