All of lore.kernel.org
 help / color / mirror / Atom feed
From: Andy Shevchenko <andriy.shevchenko@intel.com>
To: Haixu Cui <quic_haixcui@quicinc.com>
Cc: harald.mommer@oss.qualcomm.com, quic_msavaliy@quicinc.com,
	broonie@kernel.org, virtio-dev@lists.linux.dev,
	viresh.kumar@linaro.org, linux-spi@vger.kernel.org,
	linux-kernel@vger.kernel.org, hdanton@sina.com,
	qiang4.zhang@linux.intel.com, alex.bennee@linaro.org,
	quic_ztu@quicinc.com, virtualization@lists.linux-foundation.org
Subject: Re: [PATCH v9 3/3] SPI: Add virtio SPI driver
Date: Mon, 1 Sep 2025 15:07:00 +0300	[thread overview]
Message-ID: <aLWMZH3NTfM8qOUy@smile.fi.intel.com> (raw)
In-Reply-To: <20250828093451.2401448-4-quic_haixcui@quicinc.com>

On Thu, Aug 28, 2025 at 05:34:51PM +0800, Haixu Cui wrote:
> This is the virtio SPI Linux kernel driver.

...

> +#include <linux/completion.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/spi/spi.h>
> +#include <linux/stddef.h>

A lot of headers are still missing. See below.

...


> +struct virtio_spi_priv {
> +	/* The virtio device we're associated with */
> +	struct virtio_device *vdev;
> +	/* Pointer to the virtqueue */
> +	struct virtqueue *vq;
> +	/* Copy of config space mode_func_supported */
> +	u32 mode_func_supported;

uXX (in particular u32) is defined in types.h.

> +	/* Copy of config space max_freq_hz */
> +	u32 max_freq_hz;
> +};

...

> +static int virtio_spi_set_delays(struct spi_transfer_head *th,
> +				 struct spi_device *spi,
> +				 struct spi_transfer *xfer)
> +{
> +	int cs_setup;
> +	int cs_word_delay_xfer;
> +	int cs_word_delay_spi;
> +	int delay;
> +	int cs_hold;
> +	int cs_inactive;
> +	int cs_change_delay;
> +
> +	cs_setup = spi_delay_to_ns(&spi->cs_setup, xfer);
> +	if (cs_setup < 0) {
> +		dev_warn(&spi->dev, "Cannot convert cs_setup\n");

dev_warn() et al. are defined in dev_printk.h.

> +		return cs_setup;
> +	}
> +	th->cs_setup_ns = cpu_to_le32(cs_setup);

This requires

#include <asm/byteorder.h>

> +	cs_word_delay_xfer = spi_delay_to_ns(&xfer->word_delay, xfer);
> +	if (cs_word_delay_xfer < 0) {
> +		dev_warn(&spi->dev, "Cannot convert cs_word_delay_xfer\n");
> +		return cs_word_delay_xfer;
> +	}
> +	cs_word_delay_spi = spi_delay_to_ns(&spi->word_delay, xfer);
> +	if (cs_word_delay_spi < 0) {
> +		dev_warn(&spi->dev, "Cannot convert cs_word_delay_spi\n");
> +		return cs_word_delay_spi;
> +	}
> +
> +	th->word_delay_ns = cpu_to_le32(max(cs_word_delay_spi, cs_word_delay_xfer));

max() is defined in math.h.

> +	delay = spi_delay_to_ns(&xfer->delay, xfer);
> +	if (delay < 0) {
> +		dev_warn(&spi->dev, "Cannot convert delay\n");
> +		return delay;
> +	}
> +	cs_hold = spi_delay_to_ns(&spi->cs_hold, xfer);
> +	if (cs_hold < 0) {
> +		dev_warn(&spi->dev, "Cannot convert cs_hold\n");
> +		return cs_hold;
> +	}
> +	th->cs_delay_hold_ns = cpu_to_le32(delay + cs_hold);
> +
> +	cs_inactive = spi_delay_to_ns(&spi->cs_inactive, xfer);
> +	if (cs_inactive < 0) {
> +		dev_warn(&spi->dev, "Cannot convert cs_inactive\n");
> +		return cs_inactive;
> +	}
> +	cs_change_delay = spi_delay_to_ns(&xfer->cs_change_delay, xfer);
> +	if (cs_change_delay < 0) {
> +		dev_warn(&spi->dev, "Cannot convert cs_change_delay\n");
> +		return cs_change_delay;
> +	}
> +	th->cs_change_delay_inactive_ns =
> +		cpu_to_le32(cs_inactive + cs_change_delay);
> +
> +	return 0;
> +}

...

> +static int virtio_spi_transfer_one(struct spi_controller *ctrl,
> +				   struct spi_device *spi,
> +				   struct spi_transfer *xfer)
> +{
> +	struct virtio_spi_priv *priv = spi_controller_get_devdata(ctrl);

> +	struct virtio_spi_req *spi_req __free(kfree);

This is incorrect template. It's one of the exceptions when we mix code and
definitions...

> +	struct spi_transfer_head *th;
> +	struct scatterlist sg_out_head, sg_out_payload;
> +	struct scatterlist sg_in_result, sg_in_payload;

+ scatterlist.h

> +	struct scatterlist *sgs[4];
> +	unsigned int outcnt = 0;
> +	unsigned int incnt = 0;
> +	int ret;


> +	spi_req = kzalloc(sizeof(*spi_req), GFP_KERNEL);

...so this should be

	struct virtio_spi_req *spi_req __free(kfree) =
		kzalloc(sizeof(*spi_req), GFP_KERNEL);

(or on one line if you are okay with a 100 limit).

And do not forget to include cleanup.h (__free() macro) and
slab.h (kzalloc() API).

> +	if (!spi_req)
> +		return -ENOMEM;

+ errno.h

But since you already have IS_ERR()/PTR_ERR() use, just

+ err.h

> +	init_completion(&spi_req->completion);
> +
> +	th = &spi_req->transfer_head;
> +
> +	/* Fill struct spi_transfer_head */
> +	th->chip_select_id = spi_get_chipselect(spi, 0);
> +	th->bits_per_word = spi->bits_per_word;
> +	th->cs_change = xfer->cs_change;
> +	th->tx_nbits = xfer->tx_nbits;
> +	th->rx_nbits = xfer->rx_nbits;
> +	th->reserved[0] = 0;
> +	th->reserved[1] = 0;
> +	th->reserved[2] = 0;
> +
> +	static_assert(VIRTIO_SPI_CPHA == SPI_CPHA,
> +		      "VIRTIO_SPI_CPHA must match SPI_CPHA");
> +	static_assert(VIRTIO_SPI_CPOL == SPI_CPOL,
> +		      "VIRTIO_SPI_CPOL must match SPI_CPOL");
> +	static_assert(VIRTIO_SPI_CS_HIGH == SPI_CS_HIGH,
> +		      "VIRTIO_SPI_CS_HIGH must match SPI_CS_HIGH");
> +	static_assert(VIRTIO_SPI_MODE_LSB_FIRST == SPI_LSB_FIRST,
> +		      "VIRTIO_SPI_MODE_LSB_FIRST must match SPI_LSB_FIRST");
> +
> +	th->mode = cpu_to_le32(spi->mode & VIRTIO_SPI_MODE_MASK);
> +	if (spi->mode & SPI_LOOP)
> +		th->mode |= cpu_to_le32(VIRTIO_SPI_MODE_LOOP);
> +
> +	th->freq = cpu_to_le32(xfer->speed_hz);
> +
> +	ret = virtio_spi_set_delays(th, spi, xfer);
> +	if (ret)
> +		goto msg_done;
> +
> +	/* Set buffers */
> +	spi_req->tx_buf = xfer->tx_buf;
> +	spi_req->rx_buf = xfer->rx_buf;
> +
> +	/* Prepare sending of virtio message */
> +	init_completion(&spi_req->completion);
> +
> +	sg_init_one(&sg_out_head, th, sizeof(*th));
> +	sgs[outcnt] = &sg_out_head;
> +	outcnt++;
> +
> +	if (spi_req->tx_buf) {
> +		sg_init_one(&sg_out_payload, spi_req->tx_buf, xfer->len);
> +		sgs[outcnt] = &sg_out_payload;
> +		outcnt++;
> +	}
> +
> +	if (spi_req->rx_buf) {
> +		sg_init_one(&sg_in_payload, spi_req->rx_buf, xfer->len);
> +		sgs[outcnt] = &sg_in_payload;
> +		incnt++;
> +	}
> +
> +	sg_init_one(&sg_in_result, &spi_req->result,
> +		    sizeof(struct spi_transfer_result));
> +	sgs[outcnt + incnt] = &sg_in_result;
> +	incnt++;
> +
> +	ret = virtqueue_add_sgs(priv->vq, sgs, outcnt, incnt, spi_req,
> +				GFP_KERNEL);
> +	if (ret)
> +		goto msg_done;
> +
> +	/* Simple implementation: There can be only one transfer in flight */
> +	virtqueue_kick(priv->vq);
> +
> +	wait_for_completion(&spi_req->completion);
> +
> +	/* Read result from message and translate return code */
> +	switch (spi_req->result.result) {
> +	case VIRTIO_SPI_TRANS_OK:
> +		break;
> +	case VIRTIO_SPI_PARAM_ERR:
> +		ret = -EINVAL;
> +		break;
> +	case VIRTIO_SPI_TRANS_ERR:
> +		ret = -EIO;
> +		break;
> +	default:
> +		ret = -EIO;
> +		break;
> +	}
> +
> +msg_done:
> +	if (ret)
> +		ctrl->cur_msg->status = ret;
> +
> +	return ret;
> +}

...

> +static int virtio_spi_find_vqs(struct virtio_spi_priv *priv)
> +{
> +	struct virtqueue *vq;
> +
> +	vq = virtio_find_single_vq(priv->vdev, virtio_spi_msg_done, "spi-rq");
> +	if (IS_ERR(vq))
> +		return PTR_ERR(vq);

See above.

> +	priv->vq = vq;
> +	return 0;
> +}

...

> +static int virtio_spi_probe(struct virtio_device *vdev)
> +{
> +	struct virtio_spi_priv *priv;
> +	struct spi_controller *ctrl;
> +	int ret;
> +	u32 bus_num;
> +
> +	ctrl = devm_spi_alloc_host(&vdev->dev, sizeof(*priv));
> +	if (!ctrl)
> +		return -ENOMEM;
> +
> +	priv = spi_controller_get_devdata(ctrl);
> +	priv->vdev = vdev;
> +	vdev->priv = priv;

> +	device_set_node(&ctrl->dev, dev_fwnode(&vdev->dev));
> +	dev_set_drvdata(&vdev->dev, ctrl);
> +	ret = device_property_read_u32(&vdev->dev, "spi,bus-num", &bus_num);

+ device.h
+ property.h

> +	if (ret || bus_num > S16_MAX)

+ limits.h

> +		ctrl->bus_num = -1;
> +	else
> +		ctrl->bus_num = bus_num;

But why do we need this property at all? And where is it documented in the
device tree bindings?

> +	virtio_spi_read_config(vdev);
> +
> +	ctrl->transfer_one = virtio_spi_transfer_one;
> +
> +	ret = virtio_spi_find_vqs(priv);
> +	if (ret)
> +		return dev_err_probe(&vdev->dev, ret, "Cannot setup virtqueues\n");
> +
> +	/* Register cleanup for virtqueues using devm */
> +	ret = devm_add_action_or_reset(&vdev->dev, virtio_spi_del_vq, vdev);
> +	if (ret)
> +		return dev_err_probe(&vdev->dev, ret, "Cannot register virtqueue cleanup\n");
> +
> +	/* Use devm version to register controller */
> +	ret = devm_spi_register_controller(&vdev->dev, ctrl);
> +	if (ret)
> +		return dev_err_probe(&vdev->dev, ret, "Cannot register controller\n");
> +
> +	return 0;
> +}

...

> +static struct virtio_device_id virtio_spi_id_table[] = {

The type is or should be defined in mod_devicetable.h.

> +	{ VIRTIO_ID_SPI, VIRTIO_DEV_ANY_ID },
> +	{}
> +};

...

> +static const struct dev_pm_ops virtio_spi_pm_ops = {

The type is defined in pm.h.

> +	.freeze = pm_sleep_ptr(virtio_spi_freeze),
> +	.restore = pm_sleep_ptr(virtio_spi_restore),
> +};


-- 
With Best Regards,
Andy Shevchenko



  reply	other threads:[~2025-09-01 12:07 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-08-28  9:34 [PATCH v9 0/3] Virtio SPI Linux driver Haixu Cui
2025-08-28  9:34 ` [PATCH v9 1/3] virtio: Add ID for virtio SPI Haixu Cui
2025-09-10  9:39   ` Alex Bennée
2025-08-28  9:34 ` [PATCH v9 2/3] virtio-spi: Add virtio-spi.h Haixu Cui
2025-09-10  9:43   ` Alex Bennée
2025-08-28  9:34 ` [PATCH v9 3/3] SPI: Add virtio SPI driver Haixu Cui
2025-09-01 12:07   ` Andy Shevchenko [this message]
2025-09-02 15:54     ` Harald Mommer
2025-09-02 16:28       ` Mark Brown
2025-09-03  9:04     ` Haixu Cui
2025-09-03 10:27       ` Andy Shevchenko
2025-09-10  3:51         ` Haixu Cui
2025-09-11  7:48           ` Andy Shevchenko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=aLWMZH3NTfM8qOUy@smile.fi.intel.com \
    --to=andriy.shevchenko@intel.com \
    --cc=alex.bennee@linaro.org \
    --cc=broonie@kernel.org \
    --cc=harald.mommer@oss.qualcomm.com \
    --cc=hdanton@sina.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-spi@vger.kernel.org \
    --cc=qiang4.zhang@linux.intel.com \
    --cc=quic_haixcui@quicinc.com \
    --cc=quic_msavaliy@quicinc.com \
    --cc=quic_ztu@quicinc.com \
    --cc=viresh.kumar@linaro.org \
    --cc=virtio-dev@lists.linux.dev \
    --cc=virtualization@lists.linux-foundation.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.