From: Andi Shyti <andi.shyti@kernel.org>
To: Hans Hu <hanshu-oc@zhaoxin.com>
Cc: krzk@kernel.org, linux-i2c@vger.kernel.org, cobechen@zhaoxin.com,
TonyWWang@zhaoxin.com
Subject: Re: [PATCH v4] i2c: add support for Zhaoxin I2C controller
Date: Fri, 9 Jun 2023 14:24:14 +0200 [thread overview]
Message-ID: <20230609122414.l2pcuwv5foclkxea@intel.intel> (raw)
In-Reply-To: <20230609031625.6928-1-hanshu-oc@zhaoxin.com>
Hi Hans,
> +static int zxi2c_byte_xfer(struct zxi2c *i2c, struct i2c_msg *msgs, int num)
> +{
> + u16 i, finished;
> + u8 index;
> + int ret = 0;
> + struct i2c_msg *msg;
> + void __iomem *regs = i2c->regs;
> +
> + zxi2c_en_byte_mode(regs);
> + zxi2c_enable_irq(regs, ZXI2C_EN_BYTEEND, true);
> +
> + for (index = 0; index < num; index++) {
> + int err = 0;
> +
> + msg = msgs + index;
> + i2c->addr = msg->addr;
> + i2c->is_read = !!(msg->flags & I2C_M_RD);
> + i2c->byte_left = i2c->len = msg->len;
> +
> + zxi2c_set_wr(regs, i2c->is_read);
> + if (i2c->is_read) {
> + zxi2c_prepare_next_read(regs, i2c->byte_left);
> + zxi2c_start(i2c);
> + /* create restart for non-first msg */
> + if (index)
> + zxi2c_continue(i2c);
> +
> + for (i = 1; i <= msg->len; i++) {
> + err = zxi2c_wait_event(i2c, ZXI2C_STS_BYTEEND);
> + if (err)
> + break;
> +
> + msg->buf[i - 1] = zxi2c_get_byte(regs);
why don't you start the for loop from '0' to avoid "i - 1"?
> + if (i2c->byte_left == 0)
> + break;
> +
> + zxi2c_prepare_next_read(regs, i2c->byte_left);
> + zxi2c_continue(i2c);
> + }
> + } else {
> + zxi2c_set_byte(regs, msg->buf[0]);
> + /* mark whether this is the last msg */
> + i2c->is_last_msg = index == !!(num - 1);
> + zxi2c_start(i2c);
> + /* create restart for non-first msg */
> + if (index)
> + zxi2c_continue(i2c);
> +
> + for (i = 1; i <= msg->len; i++) {
> + err = zxi2c_wait_event(i2c, ZXI2C_STS_BYTEEND);
> + if (err)
> + break;
> +
> + if (i2c->byte_left == 0)
> + break;
> + zxi2c_set_byte(regs, msg->buf[i]);
> + zxi2c_continue(i2c);
> + }
> + }
> +
> + if (err) {
> + /* check if NACK during transmitting */
> + finished = msg->len - i2c->byte_left;
> + if (finished)
> + dev_err(i2c->dev,
> + "%s: %s finished %d bytes: %*ph\n",
> + __func__,
> + i2c->is_read ? "read" : "write",
> + finished, finished, msg->buf);
> + return err;
do you need to disable irq's here?
> + }
> + ret++;
> + }
> +
> + zxi2c_enable_irq(regs, ZXI2C_EN_BYTEEND, false);
> + return ret;
> +}
> +
> +static int zxi2c_fifo_xfer(struct zxi2c *i2c, struct i2c_msg *msgs)
> +{
> + void __iomem *regs = i2c->regs;
> + struct i2c_msg *msg = msgs;
> + int i;
> + u8 finished;
> + int err;
> +
> + i2c->addr = msg->addr;
> + i2c->is_read = !!(msg->flags & I2C_M_RD);
> + i2c->len = msg->len;
> +
> + zxi2c_reset_fifo(i2c);
> + zxi2c_en_fifo_mode(regs);
> + zxi2c_enable_irq(regs, ZXI2C_EN_FIFOEND, true);
> +
> + zxi2c_set_wr(regs, i2c->is_read);
> + if (i2c->is_read) {
> + zxi2c_set_fifo_rd_len(regs, msg->len - 1);
> + } else {
> + zxi2c_set_fifo_wr_len(regs, msg->len - 1);
> + for (i = 0; i < msg->len; i++)
> + zxi2c_set_fifo_byte(regs, msg->buf[i]);
> + }
> +
> + zxi2c_start(i2c);
> + err = zxi2c_wait_event(i2c, ZXI2C_STS_FIFOEND);
> + if (err)
> + return err;
> +
> + if (i2c->is_read) {
> + finished = zxi2c_get_fifo_rd_cnt(regs);
> + for (i = 0; i < finished; i++)
> + msg->buf[i] = zxi2c_get_fifo_byte(regs);
> + } else {
> + finished = zxi2c_get_fifo_wr_cnt(regs);
> + }
> +
> + /* check if NACK during transmitting */
> + if (finished != msg->len) {
> + if (finished)
> + dev_err(i2c->dev,
> + "%s: %s only finished %d/%d bytes: %*ph\n",
> + __func__, i2c->is_read ? "read" : "write",
> + finished, msg->len, finished, msg->buf);
> + return -EAGAIN;
do you need to disable irq's here?
> + }
> +
> + zxi2c_enable_irq(regs, ZXI2C_EN_FIFOEND, false);
> + return 1;
> +}
> +
> +static int zxi2c_master_xfer(struct i2c_adapter *adap,
> + struct i2c_msg *msgs, int num)
> +{
> + int err;
> + struct zxi2c *i2c = (struct zxi2c *)i2c_get_adapdata(adap);
> +
> + if (!zxi2c_is_ready(i2c->regs)) {
one question: what are the cases that the controller is not
ready. What might the controller be doing now?
> + dev_warn(i2c->dev, "controller not ready\n");
> + zxi2c_module_reset(i2c);
> + if (!zxi2c_is_ready(i2c->regs)) {
> + dev_err(i2c->dev, "controller can't reset to ready\n");
> + return -EBUSY;
> + }
> + zxi2c_set_bus_speed(i2c);
> + }
> +
> + if (num == 1 && msgs->len <= ZXI2C_FIFO_SIZE && msgs->len >= 3)
> + err = zxi2c_fifo_xfer(i2c, msgs);
> + else
> + err = zxi2c_byte_xfer(i2c, msgs, num);
> +
> + return err;
> +}
> +
> +static u32 zxi2c_func(struct i2c_adapter *adap)
> +{
> + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> +}
> +
> +static const struct i2c_algorithm zxi2c_algorithm = {
> + .master_xfer = zxi2c_master_xfer,
> + .functionality = zxi2c_func,
> +};
> +
> +static const struct i2c_adapter_quirks zxi2c_quirks = {
> + .flags = I2C_AQ_NO_ZERO_LEN | I2C_AQ_COMB_WRITE_THEN_READ,
> +};
> +
> +static int zxi2c_parse_resources(struct zxi2c *i2c)
> +{
> + struct platform_device *pdev = to_platform_device(i2c->dev);
> + struct resource *res;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + i2c->regs = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(i2c->regs))
> + return PTR_ERR(i2c->regs);
> +
> + if (res->start & 0x20)
do you mind giving this 0x20 a define?
> + i2c->reset_bitmask = BIT(4);
> + else
> + i2c->reset_bitmask = BIT(5);
I'm not strong on these, but would be nice to have also these two
bits defined, as well.
> +
> + i2c->irq = platform_get_irq(pdev, 0);
> + if (i2c->irq < 0)
> + return i2c->irq;
> +
> + i2c->hrv = zxi2c_get_revison(i2c->regs);
> + i2c->dynamic = zxi2c_get_dyn_clk(i2c->regs);
> +
> + i2c->fstp = zxi2c_get_fstp_value(i2c->regs);
> + zxi2c_get_bus_speed(i2c);
> + zxi2c_set_bus_speed(i2c);
> +
> + return 0;
> +}
> +
> +static int zxi2c_probe(struct platform_device *pdev)
> +{
> + int err = 0;
> + struct zxi2c *i2c;
> + struct pci_dev *pci;
> + struct device *dev;
> +
> + /* make sure this is zhaoxin platform */
Krzysztof is asking what does this mean? I guess here you are
checking if the device connected is really the zhaixin plaform of
this driver. Makes sense. Just to avoid misunderstandings we
could rephrase this with something like:
/*
* Check if vendor and device ID match the expected
* values for the zhaoxin platform
*/
would it work?
> + dev = pdev->dev.parent;
> + if (dev && dev_is_pci(dev)) {
> + pci = to_pci_dev(dev);
> + if (pci->vendor != PCI_VENDOR_ID_ZHAOXIN ||
> + pci->device != ZXI2C_PARENT_PCI_DID)
> + return -ENODEV;
> + } else {
> + return -ENODEV;
> + }
> +
> + i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
> + if (IS_ERR(i2c))
devm_kzalloc doesn't report an error, just check for NULL if (!i2c)
Rest looks good.
Thanks,
Andi
> + return -ENOMEM;
> +
> + i2c->pci = pci;
> + i2c->dev = &pdev->dev;
> + err = zxi2c_parse_resources(i2c);
> + if (err)
> + return err;
> +
> + platform_set_drvdata(pdev, (void *)i2c);
> +
> + err = devm_request_irq(&pdev->dev, i2c->irq, zxi2c_irq_handle,
> + IRQF_SHARED, pdev->name, i2c);
> + if (err)
> + return err;
> +
> + init_waitqueue_head(&i2c->waitq);
> +
> + i2c->adap.owner = THIS_MODULE;
> + i2c->adap.algo = &zxi2c_algorithm;
> + i2c->adap.retries = 2;
> + i2c->adap.quirks = &zxi2c_quirks;
> + i2c->adap.dev.parent = &pdev->dev;
> + ACPI_COMPANION_SET(&i2c->adap.dev, ACPI_COMPANION(&pdev->dev));
> + snprintf(i2c->adap.name, sizeof(i2c->adap.name), "%s-%s-%s",
> + ZXI2C_NAME, dev_name(&pci->dev), dev_name(i2c->dev));
> + i2c_set_adapdata(&i2c->adap, i2c);
> +
> + return devm_i2c_add_adapter(&pdev->dev, &i2c->adap);
> +}
> +
> +static int zxi2c_resume(struct device *dev)
> +{
> + struct zxi2c *i2c = dev_get_drvdata(dev);
> +
> + zxi2c_module_reset(i2c);
> + zxi2c_set_bus_speed(i2c);
> +
> + return 0;
> +}
> +
> +static const struct dev_pm_ops zxi2c_pm = {
> + SET_SYSTEM_SLEEP_PM_OPS(NULL, zxi2c_resume)
> +};
> +
> +static const struct acpi_device_id zxi2c_acpi_match[] = {
> + {"IIC1D17", 0},
> + {},
> +};
> +MODULE_DEVICE_TABLE(acpi, zxi2c_acpi_match);
> +
> +static struct platform_driver zxi2c_driver = {
> + .probe = zxi2c_probe,
> + .driver = {
> + .name = ZXI2C_NAME,
> + .acpi_match_table = zxi2c_acpi_match,
> + .pm = &zxi2c_pm,
> + },
> +};
> +module_platform_driver(zxi2c_driver);
> +
> +MODULE_AUTHOR("HansHu@zhaoxin.com");
> +MODULE_DESCRIPTION("Shanghai Zhaoxin IIC driver");
> +MODULE_LICENSE("GPL");
> --
> 2.17.1
>
next prev parent reply other threads:[~2023-06-09 12:26 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-06-09 3:16 [PATCH v4] i2c: add support for Zhaoxin I2C controller Hans Hu
2023-06-09 9:33 ` Krzysztof Kozlowski
2023-06-12 3:31 ` Hans Hu
2023-06-09 12:24 ` Andi Shyti [this message]
2023-06-09 13:11 ` Krzysztof Kozlowski
2023-06-12 8:24 ` Hans Hu
2023-06-09 12:47 ` Andi Shyti
2023-06-14 7:34 ` [PATCH v5] " Hans Hu
2023-06-14 8:21 ` Andi Shyti
2023-06-14 9:48 ` [PATCH v6] " Hans Hu
2023-06-23 8:47 ` Wolfram Sang
2023-06-25 10:57 ` Hans Hu
2023-06-30 8:01 ` Andi Shyti
[not found] ` <6f3479fb79944d5a8b4a562d67c82a4b@zhaoxin.com>
[not found] ` <1abb34e5cbe74514911598a5a522045b@zhaoxin.com>
2023-07-06 7:25 ` Hans Hu
-- strict thread matches above, loose matches on Subject: below --
2023-06-09 2:55 [PATCH v4] " Hans Hu
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=20230609122414.l2pcuwv5foclkxea@intel.intel \
--to=andi.shyti@kernel.org \
--cc=TonyWWang@zhaoxin.com \
--cc=cobechen@zhaoxin.com \
--cc=hanshu-oc@zhaoxin.com \
--cc=krzk@kernel.org \
--cc=linux-i2c@vger.kernel.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.