Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] Virtio mmio i2c #1

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
29 changes: 28 additions & 1 deletion arch/arm/boot/dts/st/stm32mp157c-ed1.dts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,18 @@
no-map;
};

mmio0: mmio0@10050000 {
compatible = "shared-dma-pool";
reg = <0x10050000 0x200>;
no-map;
};

virtio0mem: virtio0mem@10050200 {
compatible = "shared-dma-pool";
reg = <0x10050200 0x7E00>;
no-map;
};

mcuram: mcuram@30000000 {
compatible = "shared-dma-pool";
reg = <0x30000000 0x40000>;
Expand Down Expand Up @@ -304,6 +316,8 @@

&ipcc {
status = "okay";
#interrupt-cells = <1>;
interrupt-controller;
};

&iwdg2 {
Expand All @@ -313,12 +327,25 @@

&m4_rproc {
memory-region = <&retram>, <&mcuram>, <&mcuram2>, <&vdev0vring0>,
<&vdev0vring1>, <&vdev0buffer>;
<&vdev0vring1>, <&vdev0buffer>, <&mmio0>;
mboxes = <&ipcc 0>, <&ipcc 1>, <&ipcc 2>, <&ipcc 3>;
mbox-names = "vq0", "vq1", "shutdown", "detach";
interrupt-parent = <&exti>;
interrupts = <68 1>;
status = "okay";

ranges;

#address-cells = <1>;
#size-cells = <1>;
mmio@10050000 {
compatible = "virtio,mmio-remoteproc";
reg = <0x10050000 0x100>;
interrupts-extended = <&ipcc 4>;
memory-region = <&virtio0mem>;
mboxes = <&ipcc 5>;
mbox-names = "sync_mb";
};
};

&pwr_regulators {
Expand Down
29 changes: 28 additions & 1 deletion arch/arm/boot/dts/st/stm32mp15xx-dkx.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,18 @@
no-map;
};

mmio0: mmio0@10050000 {
compatible = "shared-dma-pool";
reg = <0x10050000 0x200>;
no-map;
};

virtio0mem: virtio0mem@10050200 {
compatible = "shared-dma-pool";
reg = <0x10050200 0x7E00>;
no-map;
};

mcuram: mcuram@30000000 {
compatible = "shared-dma-pool";
reg = <0x30000000 0x40000>;
Expand Down Expand Up @@ -454,6 +466,8 @@

&ipcc {
status = "okay";
#interrupt-cells = <1>;
interrupt-controller;
};

&iwdg2 {
Expand All @@ -476,12 +490,25 @@

&m4_rproc {
memory-region = <&retram>, <&mcuram>, <&mcuram2>, <&vdev0vring0>,
<&vdev0vring1>, <&vdev0buffer>;
<&vdev0vring1>, <&vdev0buffer>, <&mmio0>;
mboxes = <&ipcc 0>, <&ipcc 1>, <&ipcc 2>, <&ipcc 3>;
mbox-names = "vq0", "vq1", "shutdown", "detach";
interrupt-parent = <&exti>;
interrupts = <68 1>;
status = "okay";

ranges;

#address-cells = <1>;
#size-cells = <1>;
mmio@10050000 {
compatible = "virtio,mmio-remoteproc";
reg = <0x10050000 0x100>;
interrupts-extended = <&ipcc 4>;
memory-region = <&virtio0mem>;
mboxes = <&ipcc 5>;
mbox-names = "sync_mb";
};
};

&pwr_regulators {
Expand Down
74 changes: 61 additions & 13 deletions drivers/i2c/busses/i2c-virtio.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <linux/virtio_ids.h>
#include <linux/virtio_config.h>
#include <linux/virtio_i2c.h>
#include <linux/dma-mapping.h>

/**
* struct virtio_i2c - virtio I2C data
Expand All @@ -39,7 +40,8 @@ struct virtio_i2c {
* @in_hdr: the IN header of the virtio I2C message
*/
struct virtio_i2c_req {
struct completion completion;
struct completion *completion;
dma_addr_t dma_handle;
struct virtio_i2c_out_hdr out_hdr ____cacheline_aligned;
uint8_t *buf ____cacheline_aligned;
struct virtio_i2c_in_hdr in_hdr ____cacheline_aligned;
Expand All @@ -51,7 +53,35 @@ static void virtio_i2c_msg_done(struct virtqueue *vq)
unsigned int len;

while ((req = virtqueue_get_buf(vq, &len)))
complete(&req->completion);
complete(req->completion);
}

static void virtio_sg_init(struct scatterlist *sg, void *cpu_addr, unsigned int len)
{
if (likely(is_vmalloc_addr(cpu_addr))) {
sg_init_table(sg, 1);
sg_set_page(sg, vmalloc_to_page(cpu_addr), len,
offset_in_page(cpu_addr));
} else {
WARN_ON(!virt_addr_valid(cpu_addr));
sg_init_one(sg, cpu_addr, len);
}
}

static void get_dma_buffer(struct device *dma_dev, struct virtio_i2c_req *req, struct i2c_msg *msg)
{
req->buf = dma_alloc_coherent(dma_dev, msg->len, &req->dma_handle, GFP_KERNEL);

if (req->buf)
memcpy(req->buf, msg->buf, msg->len);
}

static void put_dma_buffer(struct device *dma_dev, struct virtio_i2c_req *req, struct i2c_msg *msg)
{
if (msg->flags & I2C_M_RD)
memcpy(msg->buf, req->buf, msg->len);

dma_free_coherent(dma_dev, msg->len, req->buf, req->dma_handle);
}

static int virtio_i2c_prepare_reqs(struct virtqueue *vq,
Expand All @@ -64,7 +94,11 @@ static int virtio_i2c_prepare_reqs(struct virtqueue *vq,
for (i = 0; i < num; i++) {
int outcnt = 0, incnt = 0;

init_completion(&reqs[i].completion);
reqs[i].completion = kcalloc(1, sizeof(struct completion), GFP_KERNEL);
if (!reqs[i].completion)
break;

init_completion(reqs[i].completion);

/*
* Only 7-bit mode supported for this moment. For the address
Expand All @@ -78,27 +112,33 @@ static int virtio_i2c_prepare_reqs(struct virtqueue *vq,
if (i != num - 1)
reqs[i].out_hdr.flags |= cpu_to_le32(VIRTIO_I2C_FLAGS_FAIL_NEXT);

sg_init_one(&out_hdr, &reqs[i].out_hdr, sizeof(reqs[i].out_hdr));
virtio_sg_init(&out_hdr, &reqs[i].out_hdr, sizeof(reqs[i].out_hdr));
sgs[outcnt++] = &out_hdr;

if (msgs[i].len) {
reqs[i].buf = i2c_get_dma_safe_msg_buf(&msgs[i], 1);
if (!reqs[i].buf)
get_dma_buffer(vq->vdev->dev.parent, &reqs[i], &msgs[i]);

if (!reqs[i].buf) {
kfree(reqs[i].completion);
break;
}

sg_init_one(&msg_buf, reqs[i].buf, msgs[i].len);
virtio_sg_init(&msg_buf, reqs[i].buf, msgs[i].len);

if (msgs[i].flags & I2C_M_RD)
sgs[outcnt + incnt++] = &msg_buf;
else
sgs[outcnt++] = &msg_buf;
}

sg_init_one(&in_hdr, &reqs[i].in_hdr, sizeof(reqs[i].in_hdr));
virtio_sg_init(&in_hdr, &reqs[i].in_hdr, sizeof(reqs[i].in_hdr));
sgs[outcnt + incnt++] = &in_hdr;

if (virtqueue_add_sgs(vq, sgs, outcnt, incnt, &reqs[i], GFP_KERNEL)) {
i2c_put_dma_safe_msg_buf(reqs[i].buf, &msgs[i], false);
put_dma_buffer(vq->vdev->dev.parent, &reqs[i], &msgs[i]);

kfree(reqs[i].completion);

break;
}
}
Expand All @@ -116,12 +156,14 @@ static int virtio_i2c_complete_reqs(struct virtqueue *vq,
for (i = 0; i < num; i++) {
struct virtio_i2c_req *req = &reqs[i];

wait_for_completion(&req->completion);
wait_for_completion(req->completion);

if (!failed && req->in_hdr.status != VIRTIO_I2C_MSG_OK)
failed = true;

i2c_put_dma_safe_msg_buf(reqs[i].buf, &msgs[i], !failed);
put_dma_buffer(vq->vdev->dev.parent, req, &msgs[i]);

kfree(req->completion);

if (!failed)
j++;
Expand All @@ -137,8 +179,12 @@ static int virtio_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
struct virtqueue *vq = vi->vq;
struct virtio_i2c_req *reqs;
int count;
dma_addr_t dma_handle;

reqs = dma_alloc_coherent(vq->vdev->dev.parent,
sizeof(struct virtio_i2c_req) * num,
&dma_handle, GFP_KERNEL);

reqs = kcalloc(num, sizeof(*reqs), GFP_KERNEL);
if (!reqs)
return -ENOMEM;

Expand All @@ -159,7 +205,9 @@ static int virtio_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
count = virtio_i2c_complete_reqs(vq, reqs, msgs, count);

err_free:
kfree(reqs);
dma_free_coherent(vq->vdev->dev.parent,
sizeof(struct virtio_i2c_req) * num,
reqs, dma_handle);
return count;
}

Expand Down
Loading