From: Bjorn Andersson <bjorn.andersson@linaro.org>
To: Vinod Koul <vkoul@kernel.org>
Cc: Wolfram Sang <wsa+renesas@sang-engineering.com>,
linux-i2c@vger.kernel.org, linux-arm-msm@vger.kernel.org,
Rob Herring <robh+dt@kernel.org>,
devicetree@vger.kernel.org, Todor Tomov <todor.tomov@linaro.org>
Subject: Re: [PATCH v3 2/2] i2c: Add Qualcomm CCI I2C driver
Date: Mon, 6 Aug 2018 11:45:55 -0700 [thread overview]
Message-ID: <20180806184555.GC21235@tuxbook-pro> (raw)
In-Reply-To: <20180806110416.4288-3-vkoul@kernel.org>
On Mon 06 Aug 04:04 PDT 2018, Vinod Koul wrote:
[..]
> diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c
[..]
> +struct hw_params {
> + u16 thigh;
> + u16 tlow;
> + u16 tsu_sto;
> + u16 tsu_sta;
> + u16 thd_dat;
> + u16 thd_sta;
> + u16 tbuf;
> + u8 scl_stretch_en;
> + u16 trdhld;
> + u16 tsp;
> +};
> +
> +struct cci_clock {
This is now unused.
> + struct clk *clk;
> + const char *name;
> + u32 freq;
> +};
> +
> +struct cci;
> +
> +struct cci_master {
> + struct i2c_adapter adap;
> + u16 master;
> + u8 mode;
> + int status;
> + bool complete_pending;
> + struct completion irq_complete;
> + struct cci *cci;
> +
Empty line.
> +};
> +
> +struct cci_data {
> + unsigned int num_masters;
> + struct i2c_adapter_quirks quirks;
> + u16 queue_size[NUM_QUEUES];
> + struct cci_res res;
> + struct hw_params params[3];
> +};
> +
> +struct cci {
> + struct device *dev;
> + void __iomem *base;
> + u32 irq;
Use unsigned int rather than a type of specific size
> + const struct cci_data *data;
> + struct clk_bulk_data *clock;
> + u32 *clock_freq;
> + int nclocks;
> + struct cci_master master[NUM_MASTERS];
> +};
> +
> +/**
> + * cci_clock_set_rate() - Set clock frequency rates
> + * @nclocks: Number of clocks
> + * @clock: Clock array
> + * @clock_freq: Clock frequency rate array
> + * @dev: Device
> + *
> + * Return 0 on success or a negative error code otherwise
> + */
> +static int cci_clock_set_rate(int nclocks, struct clk_bulk_data *clock,
> + u32 *clock_freq, struct device *dev)
> +{
> + int i, ret;
> + long rate;
> +
> + for (i = 0; i < nclocks; i++)
Multiline loop deserves {}
> + if (clock_freq[i]) {
> + rate = clk_round_rate(clock[i].clk, clock_freq[i]);
> + if (rate < 0) {
> + dev_err(dev, "clk round rate failed: %ld\n",
> + rate);
> + return rate;
> + }
> +
> + ret = clk_set_rate(clock[i].clk, clock_freq[i]);
> + if (ret < 0) {
> + dev_err(dev, "clk set rate failed: %d\n", ret);
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
[..]
[..]
> +static int cci_reset(struct cci *cci)
> +{
> + unsigned long time;
> +
> + cci->master[0].complete_pending = true;
> + writel(CCI_RESET_CMD_MASK, cci->base + CCI_RESET_CMD);
> +
> + time = wait_for_completion_timeout
> + (&cci->master[0].irq_complete,
> + msecs_to_jiffies(CCI_TIMEOUT_MS));
Please rework the indentation of this.
Also CCI_TIMEOUT_MS is converted to jiffies in all the places, define it
in jiffies instead.
> + if (!time) {
> + dev_err(cci->dev, "CCI reset timeout\n");
> + return -ETIMEDOUT;
> + }
> +
> + return 0;
> +}
[..]
> +static int cci_validate_queue(struct cci *cci, u8 master, u8 queue)
> +{
> + int ret = 0;
> + u32 val;
> +
> + val = readl(cci->base + CCI_I2C_Mm_Qn_CUR_WORD_CNT(master, queue));
> +
> + if (val == cci->data->queue_size[queue])
> + return -EINVAL;
> +
> + if (val) {
> + val = CCI_I2C_REPORT | BIT(8);
Can we get a define (or a comment) for BIT(8) as well?
> + writel(val, cci->base + CCI_I2C_Mm_Qn_LOAD_DATA(master, queue));
> +
> + ret = cci_run_queue(cci, master, queue);
> + }
> +
> + return ret;
Rather than wrapping the second half of the function in a check for val,
return early on !val and then you can end with return cci_run_queue()
and drop the "ret".
> +}
> +
> +static int cci_i2c_read(struct cci *cci, u16 master,
> + u16 addr, u8 *buf, u16 len)
> +{
> + u32 val, words_read, words_exp;
> + u8 queue = QUEUE_1;
> + int i, index = 0, ret;
> + bool first = 0;
s/0/false/
> +
> + /*
> + * Call validate queue to make sure queue is empty before starting.
> + * This is to avoid overflow / underflow of queue.
> + */
> + ret = cci_validate_queue(cci, master, queue);
> + if (ret < 0)
> + return ret;
> +
> + val = CCI_I2C_SET_PARAM | (addr & 0x7f) << 4;
> + writel(val, cci->base + CCI_I2C_Mm_Qn_LOAD_DATA(master, queue));
> +
> + val = CCI_I2C_READ | len << 4;
> + writel(val, cci->base + CCI_I2C_Mm_Qn_LOAD_DATA(master, queue));
> +
> + ret = cci_run_queue(cci, master, queue);
> + if (ret < 0)
> + return ret;
> +
> + words_read = readl(cci->base + CCI_I2C_Mm_READ_BUF_LEVEL(master));
> + words_exp = len / 4 + 1;
> + if (words_read != words_exp) {
> + dev_err(cci->dev, "words read = %d, words expected = %d\n",
> + words_read, words_exp);
> + return -EIO;
> + }
I thought that an i2c read could return less data than was requested...
> +
> + do {
> + val = readl(cci->base + CCI_I2C_Mm_READ_DATA(master));
> +
> + for (i = 0; i < 4 && index < len; i++) {
> + if (first) {
> + first = false;
> + continue;
> + }
> + buf[index++] = (val >> (i * 8)) & 0xff;
> + }
> + } while (--words_read);
> +
> + return 0;
> +}
[..]
> +static int cci_probe(struct platform_device *pdev)
> +{
[..]
> + /* Clocks */
> +
> + cci->nclocks = 0;
> + while (cci->data->res.clock[cci->nclocks])
> + cci->nclocks++;
> +
> + cci->clock = devm_kcalloc(dev, cci->nclocks,
> + sizeof(*cci->clock), GFP_KERNEL);
> + if (!cci->clock)
> + return -ENOMEM;
> +
> + cci->clock_freq = devm_kcalloc(dev, cci->nclocks,
You're just using this temporarily to create a copy of the
res.clock_rate array, how about just passing the res.clock_rate into
cci_clock_set_rate() ?
> + sizeof(*cci->clock_freq), GFP_KERNEL);
> + if (!cci->clock_freq)
> + return -ENOMEM;
> +
> + for (i = 0; i < cci->nclocks; i++) {
> + struct clk_bulk_data *clock = &cci->clock[i];
> +
> + clock->clk = devm_clk_get(dev, cci->data->res.clock[i]);
> + if (IS_ERR(clock->clk))
> + return PTR_ERR(clock->clk);
> +
> + clock->id = cci->data->res.clock[i];
> + cci->clock_freq[i] = cci->data->res.clock_rate[i];
> + }
Fill out cci->clock[*].id and call clk_bulk_get()
> +
> + ret = cci_clock_set_rate(cci->nclocks, cci->clock,
> + cci->clock_freq, dev);
> + if (ret < 0)
> + return ret;
> +
> + ret = clk_bulk_prepare_enable(cci->nclocks, cci->clock);
It seems a little bit excessive to keep the clocks on while the driver
is probed, but this could be improved in a follow up patch...
> + if (ret < 0)
> + return ret;
> +
> + val = readl_relaxed(cci->base + CCI_HW_VERSION);
> + dev_dbg(dev, "%s: CCI HW version = 0x%08x", __func__, val);
> +
> + enable_irq(cci->irq);
> +
> + ret = cci_reset(cci);
> + if (ret < 0)
> + goto error;
> +
> + for (i = 0; i < cci->data->num_masters; i++) {
> + ret = cci_init(cci, &cci->data->params[cci->master[i].mode]);
> + if (ret < 0)
> + goto error;
> +
> + ret = i2c_add_adapter(&cci->master[i].adap);
> + if (ret < 0)
> + goto error_i2c;
> + }
> +
> + return 0;
> +
> +error_i2c:
> + for (; i >= 0; i--)
> + i2c_del_adapter(&cci->master[i].adap);
> +error:
> + disable_irq(cci->irq);
> + clk_bulk_disable_unprepare(cci->nclocks, cci->clock);
> +
> + return ret;
> +}
> +
> +static int cci_remove(struct platform_device *pdev)
> +{
> + struct cci *cci = platform_get_drvdata(pdev);
> + int i;
> +
> + disable_irq(cci->irq);
> + clk_bulk_disable_unprepare(cci->nclocks, cci->clock);
i2c clients might be communicating with their clients until you call
i2c_del_adapter(), so better pull the resources after you have removed
the adaptor.
Maybe even a cci_halt() call before we cut the clocks?
> +
> + for (i = 0; i < cci->data->num_masters; i++)
> + i2c_del_adapter(&cci->master[i].adap);
> +
> + return 0;
> +}
[..]
> +static const struct cci_data cci_v1_4_0_data = {
[..]
> + {
> + /* I2C_MODE_FAST_PLUS */
> + .thigh = 16,
> + .tlow = 22,
> + .tsu_sto = 17,
> + .tsu_sta = 18,
> + .thd_dat = 16,
> + .thd_sta = 15,
> + .tbuf = 24,
> + .scl_stretch_en = 0,
> + .trdhld = 3,
> + .tsp = 3
> + }
> +
Empty line.
> + },
> +
> +};
The dt binding mentions that a power domain is required for v1.4, but
there's no support for this in the driver.
Regards,
Bjorn
next prev parent reply other threads:[~2018-08-06 18:45 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-08-06 11:04 [PATCH v3 0/2] i2c: Add support for Qcom CCI I2C controller Vinod Koul
2018-08-06 11:04 ` [PATCH v3 1/2] dt-bindings: i2c: Add binding for Qualcomm " Vinod Koul
2018-08-06 18:03 ` Bjorn Andersson
2018-08-07 4:18 ` Vinod
2018-08-07 17:39 ` Rob Herring
2018-08-07 18:07 ` Bjorn Andersson
2018-08-08 14:03 ` Todor Tomov
2018-08-08 14:48 ` Rob Herring
2018-08-08 16:07 ` Vinod
2018-08-06 11:04 ` [PATCH v3 2/2] i2c: Add Qualcomm CCI I2C driver Vinod Koul
2018-08-06 18:45 ` Bjorn Andersson [this message]
2018-08-07 4:30 ` Vinod
2018-08-07 5:11 ` Bjorn Andersson
2018-08-17 12:41 ` Vinod
2018-08-17 18:33 ` Bjorn Andersson
2018-08-18 4:45 ` Vinod
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=20180806184555.GC21235@tuxbook-pro \
--to=bjorn.andersson@linaro.org \
--cc=devicetree@vger.kernel.org \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-i2c@vger.kernel.org \
--cc=robh+dt@kernel.org \
--cc=todor.tomov@linaro.org \
--cc=vkoul@kernel.org \
--cc=wsa+renesas@sang-engineering.com \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).