Qualcomm Universal Peripheral (QUP) I2C controller
v2 cover Qualcomm Universal Peripheral (QUP) I2C controller
Picking up Ivans i2c-qup submission from a few months back, as I've now tested and corrected some minor things. I also introduced changes based on the review of v1.
Changes from v1:
- Cleaned up device tree binding example.
- Refrased device tree bindings.
- Following changes in the i2c framework.
- Use the core clock to calculate divider for the bus clock, instead of explicitly setting it.
- Remove explicit pinctrl settting.
- Split/renamed qup_i2c_enable(bool) into enable/disable functions.
- Return value was overwritten on error in write_one/read_one.
- Initialize the i2c core every time, so that we actually can execute more than 1 transmission per xfer.
Ivan T. Ivanov (2):
i2c: qup: Add device tree bindings information
i2c: New bus driver for the QUP I2C controller
.../devicetree/bindings/i2c/qcom,i2c-qup.txt | 41 + drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-qup.c | 897 +++++++++++++++++++++ 4 files changed, 949 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt create mode 100644 drivers/i2c/busses/i2c-qup.c
Signature · 1 line
1.8.2.2
PATCH v2 1/2 i2c: qup: Add device tree bindings information
From: "Ivan T. Ivanov" <[email protected]>
The Qualcomm Universal Peripherial (QUP) wraps I2C mini-core and provide input and output FIFO's for it. I2C controller can operate as master with supported bus speeds of 100Kbps and 400Kbps.
--- .../devicetree/bindings/i2c/qcom,i2c-qup.txt | 41 ++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt
Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt new file +41 -0
@@ -0,0 +1,41 @@ +Qualcomm Universal Peripheral (QUP) I2C controller + +Required properties: + - compatible: Should be "qcom,i2c-qup". + - reg: Should contain QUP register address and length. + - interrupts: Should contain I2C interrupt. + + - clocks: Should contain the core clock and the AHB clock. + - clock-names: Should be "core" for the core clock and "iface" for the + AHB clock. + + - #address-cells: Should be <1> Address cells for i2c device address + - #size-cells: Should be <0> as i2c addresses have no size component + +Optional properties: + - clock-frequency: Should specify the desired i2c bus clock frequency in Hz, + default is 100kHz if omitted. + +Child nodes should conform to i2c bus binding. + +Example: + + i2c2: i2c@f9924000 { + compatible = "qcom,i2c-qup"; + reg = <0xf9924000 0x1000>; + interrupts = <0 96 0>; + + clocks = <&gcc_blsp1_qup2_i2c_apps_clk>, <&gcc_blsp1_ahb_clk>; + clock-names = "core", "iface"; + + clock-frequency = <355000>; + + #address-cells = <1>; + #size-cells = <0>; + + dummy@60 { + compatible = "dummy"; + reg = <0x60>; + }; + }; +
Signature · 1 line
1.8.2.2
Re: [PATCH v2 1/2] i2c: qup: Add device tree bindings information
Thanks Bjorn,
I have prepared second version, but never send it out :-). One thing suggested by Mark was missed in this version.
Quoted 20 lines · Bjorn Andersson
From: "Ivan T. Ivanov" <[email protected]>
The Qualcomm Universal Peripherial (QUP) wraps I2C mini-core and provide input and output FIFO's for it. I2C controller can operate as master with supported bus speeds of 100Kbps and 400Kbps.
Signed-off-by Ivan T. Ivanov <[email protected]>[bjorn: reformulated part of binding description and cleaned up example]Signed-off-by Bjorn Andersson <[email protected]>--- .../devicetree/bindings/i2c/qcom,i2c-qup.txt | 41 ++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txtDocumentation/devicetree/bindings/i2c/qcom,i2c-qup.txt new file +0
-0
Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt line 8
Quoted 8 lines
Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt new file +8
-0@@ -0,0 +1,41 @@ +Qualcomm Universal Peripheral (QUP) I2C controller + +Required properties: + - compatible: Should be "qcom,i2c-qup". + - reg: Should contain QUP register address and length. + - interrupts: Should contain I2C interrupt. + + - clocks: Should contain the core clock and the AHB clock.
+ - clocks: a list of phandle + clock-specifier pairs for each entry in
+ clock-names
Quoted 10 lines
Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt new file +10
-0+ - clock-names: Should be "core" for the core clock and "iface" for the + AHB clock. + + - #address-cells: Should be <1> Address cells for i2c device address + - #size-cells: Should be <0> as i2c addresses have no size component + +Optional properties: + - clock-frequency: Should specify the desired i2c bus clock frequency in Hz, + default is 100kHz if omitted. +
Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt line 28
Quoted 10 lines
Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt new file +10
-0+Child nodes should conform to i2c bus binding. + +Example: + + i2c2: i2c at f9924000 { + compatible = "qcom,i2c-qup"; + reg = <0xf9924000 0x1000>; + interrupts = <0 96 0>; + + clocks = <&gcc_blsp1_qup2_i2c_apps_clk>, <&gcc_blsp1_ahb_clk>;
In the light of the latest patches from Stephen, this could be
+ clocks = <&gcc GCC_BLSP1_QUP2_I2C_APPS_CLK>, <&gcc GCC_BLSP1_AHB_CLK>;
Regards,
Ivan
Re: [PATCH v2 1/2] i2c: qup: Add device tree bindings information
Quoted 5 lines · Ivan T. Ivanov
Thanks Bjorn,
I have prepared second version, but never send it out :-). One thing suggested by Mark was missed in this version.
Yeah, Mattew told me you we're assigned to other things and asked me to send out an update as I had gotten it to work on our boards.
I did modify the wording of most of these to match how it is written in the other Qualcomm definitions.
@Mark, would you rather have me change this to your suggested wording?
Quoted 5 lines · Bjorn Andersson
Quoted 2 lines
From: "Ivan T. Ivanov" <[email protected]>
[snip]
Quoted 5 lines
Quoted 1 line
+ - clocks: Should contain the core clock and the AHB clock.
+ - clocks: a list of phandle + clock-specifier pairs for each entry in
+ clock-names
This is in line with how it's written in other drivers, so if the DT maintainers doesn't disagree I would like to keep phandles out of the description. This specific line is a verbatime copy of the msm_serial documentation (same block, different mode)...
Quoted 23 lines
Quoted 10 lines
+ - clock-names: Should be "core" for the core clock and "iface" for the
Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt new file +9
-0+ AHB clock. + + - #address-cells: Should be <1> Address cells for i2c device address + - #size-cells: Should be <0> as i2c addresses have no size component + +Optional properties: + - clock-frequency: Should specify the desired i2c bus clock frequency in Hz, + default is 100kHz if omitted. +Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt line 28
Quoted 10 lines
Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt new file +10
-0+Child nodes should conform to i2c bus binding. + +Example: + + i2c2: i2c at f9924000 { + compatible = "qcom,i2c-qup"; + reg = <0xf9924000 0x1000>; + interrupts = <0 96 0>; + + clocks = <&gcc_blsp1_qup2_i2c_apps_clk>, <&gcc_blsp1_ahb_clk>;In the light of the latest patches from Stephen, this could be
Review
Quoted 1 line
(quoted diff) +1
-0+ clocks = <&gcc GCC_BLSP1_QUP2_I2C_APPS_CLK>, <&gcc GCC_BLSP1_AHB_CLK>;
Yes, that's exactly what I have in my dts. However as this is just an example I didn't feel it was worth tainting the documentation with all those capital letters ;)
So unless DT maintainers disagree I would like to just keep it as an example.
Regards,
Bjorn
Re: [PATCH v2 1/2] i2c: qup: Add device tree bindings information
Hi,
Quoted 56 lines · Bjorn Andersson
Quoted 5 lines · Ivan T. Ivanov
Thanks Bjorn,
I have prepared second version, but never send it out :-). One thing suggested by Mark was missed in this version.
Yeah, Mattew told me you we're assigned to other things and asked me to send out an update as I had gotten it to work on our boards.
I did modify the wording of most of these to match how it is written in the other Qualcomm definitions.
@Mark, would you rather have me change this to your suggested wording?
Quoted 5 lines · Bjorn Andersson
Quoted 2 lines
From: "Ivan T. Ivanov" <[email protected]>
[snip]
Quoted 5 lines
Quoted 1 line
+ - clocks: Should contain the core clock and the AHB clock.
+ - clocks: a list of phandle + clock-specifier pairs for each entry in
+ clock-namesThis is in line with how it's written in other drivers, so if the DT maintainers doesn't disagree I would like to keep phandles out of the description. This specific line is a verbatime copy of the
msm_serialdocumentation (same block, different mode)...Quoted 23 lines
Quoted 10 lines
+ - clock-names: Should be "core" for the core clock and "iface" for the
Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt new file +9
-0+ AHB clock. + + - #address-cells: Should be <1> Address cells for i2c device address + - #size-cells: Should be <0> as i2c addresses have no size component + +Optional properties: + - clock-frequency: Should specify the desired i2c bus clock frequency in Hz, + default is 100kHz if omitted. +Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt line 28
Quoted 10 lines
Documentation/devicetree/bindings/i2c/qcom,i2c-qup.txt new file +10
-0+Child nodes should conform to i2c bus binding. + +Example: + + i2c2: i2c at f9924000 { + compatible = "qcom,i2c-qup"; + reg = <0xf9924000 0x1000>; + interrupts = <0 96 0>; + + clocks = <&gcc_blsp1_qup2_i2c_apps_clk>, <&gcc_blsp1_ahb_clk>;In the light of the latest patches from Stephen, this could be
Quoted 6 lines
Review
Quoted 1 line
(quoted diff) +1
-0+ clocks = <&gcc GCC_BLSP1_QUP2_I2C_APPS_CLK>, <&gcc GCC_BLSP1_AHB_CLK>;Yes, that's exactly what I have in my dts. However as this is just an example I didn't feel it was worth tainting the documentation with all those capital letters ;)
So unless DT maintainers disagree I would like to just keep it as an example.
Until, we get some meaningful board DTS files for Qualcomm platforms, it is easy for people which will like to test or use drivers to just copy and paste these definitions.
Regards,
Ivan
Quoted 8 lines
Regards,
Bjorn_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v2 2/2 i2c: New bus driver for the QUP I2C controller
In reply to Bjorn AnderssonFrom: "Ivan T. Ivanov" <[email protected]>
This bus driver supports the QUP i2c hardware controller in the Qualcomm MSM SOCs. The Qualcomm Universal Peripheral Engine (QUP) is a general purpose data path engine with input/output FIFOs and an embedded i2c
mini-core. The driver supports FIFO mode (for low bandwidth applications)
and block mode (interrupt generated for each block-size data transfer). The driver currently does not support DMA transfers.
Shamelessly based on codeaurora version of the driver.
[bjorn: updated to reflect i2c framework changes
splited up qup_i2c_enable() in enable/disable don't overwrite ret value on error in xfer functions initilize core for each transfer
remove explicit pinctrl selection
use existing clock instead of setting new core clock]
--- drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-qup.c | 897 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 908 insertions(+) create mode 100644 drivers/i2c/busses/i2c-qup.c
drivers/i2c/busses/Kconfig +10 -0
@@ -648,6 +648,16 @@ config I2C_PXA_SLAVE is necessary for systems where the PXA may be a target on the I2C bus. +config I2C_QUP + tristate "Qualcomm QUP based I2C controller" + depends on ARCH_MSM + help + If you say yes to this option, support will be included for the + built-in I2C interface on the MSM family processors. + + This driver can also be built as a module. If so, the module + will be called i2c-qup. + config HAVE_S3C2410_I2C bool help
drivers/i2c/busses/Makefile +1 -0
@@ -63,6 +63,7 @@ obj-$(CONFIG_I2C_PNX) += i2c-pnx.o obj-$(CONFIG_I2C_PUV3) += i2c-puv3.o obj-$(CONFIG_I2C_PXA) += i2c-pxa.o obj-$(CONFIG_I2C_PXA_PCI) += i2c-pxa-pci.o +obj-$(CONFIG_I2C_QUP) += i2c-qup.o obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o obj-$(CONFIG_I2C_S6000) += i2c-s6000.o obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o
drivers/i2c/busses/i2c-qup.c new file +897 -0
897 lines not on this page · raw message
Signature · 1 line
1.8.2.2
Re: [PATCH v2 2/2] i2c: New bus driver for the QUP I2C controller
Hi Bjorn,
Just two comments.
Quoted 20 lines · Bjorn Andersson
From: "Ivan T. Ivanov" <[email protected]>
This bus driver supports the QUP i2c hardware controller in the Qualcomm MSM SOCs. The Qualcomm Universal Peripheral Engine (QUP) is a general purpose data path engine with input/output FIFOs and an embedded i2c
mini-core. The driver supports FIFO mode (for low bandwidth applications)
and block mode (interrupt generated for each block-size data transfer). The driver currently does not support DMA transfers.Shamelessly based on codeaurora version of the driver.
Signed-off-by Ivan T. Ivanov <[email protected]>[bjorn: updated to reflect i2c framework changes
splited upqup_i2c_enable()in enable/disable don't overwrite ret value on error in xfer functions initilize core for each transfer
remove explicit pinctrl selection
use existing clock instead of setting new core clock]Signed-off-by Bjorn Andersson <[email protected]>---
<snip>
Quoted 9 lines
drivers/i2c/busses/i2c-qup.c new file +9
-0+ +static int qup_i2c_probe (struct platform_device *pdev) +{ +struct device_node *node = pdev->dev.of_node; +struct qup_i2c_dev *qup; +struct resource *res; +u32 val, io_mode, hw_ver, size; +int ret, fs_div, hs_div; +int src_clk_freq;
drivers/i2c/busses/i2c-qup.c line 692
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c new file +10
-0+ + qup =devm_kzalloc (&pdev->dev,sizeof (*qup),GFP_KERNEL ); +if (!qup) +return -ENOMEM ; + +platform_set_drvdata (pdev, qup); + + ret =of_alias_get_id (node,"i2c" ); +if (ret >=0 ) + pdev->id = ret;
This is part of the i2c_add_adapter() and have to be dropped.
Quoted 97 lines
drivers/i2c/busses/i2c-qup.c new file +97
-0+ + qup->dev = &pdev->dev; + + qup->clk_freq =100000 ; +if (!of_property_read_u32 (node,"clock-frequency" , &val)) + qup->clk_freq = val; + +/* We support frequencies up to FAST Mode(400KHz) */ +if (qup->clk_freq <=0 || qup->clk_freq >400000 ) { +dev_err (qup->dev,"clock frequency not supported %d\n" , + qup->clk_freq); +return -EIO ; + } + + res =platform_get_resource (pdev,IORESOURCE_MEM ,0 ); + qup->base =devm_ioremap_resource (qup->dev, res); +if (IS_ERR (qup->base)) +return PTR_ERR (qup->base); + + qup->irq =platform_get_irq (pdev,0 ); +if (qup->irq <0 ) { +dev_err (qup->dev,"No IRQ defined\n" ); +return qup->irq; + } + + qup->clk =devm_clk_get (qup->dev,"core" ); +if (IS_ERR (qup->clk)) { +dev_err (qup->dev,"Could not get core clock\n" ); +return PTR_ERR (qup->clk); + } + + qup->pclk =devm_clk_get (qup->dev,"iface" ); +if (IS_ERR (qup->pclk)) { +dev_err (qup->dev,"Could not get iface clock\n" ); +return PTR_ERR (qup->pclk); + } + +init_completion (&qup->xfer); + +qup_i2c_enable_clocks (qup); + +/* +* Bootloaders might leave a pending interrupt on certain QUP's, +* so we reset the core before registering for interrupts. +*/ +writel (1 , qup->base +QUP_SW_RESET ); + ret =qup_i2c_poll_state (qup,0 , true); +if (ret) + goto fail; + + ret =devm_request_irq (qup->dev, qup->irq, qup_i2c_interrupt, +IRQF_TRIGGER_HIGH ,"i2c_qup" , qup); +if (ret) { +dev_err (qup->dev,"Request %d IRQ failed\n" , qup->irq); + goto fail; + } +disable_irq (qup->irq); + + hw_ver =readl (qup->base +QUP_HW_VERSION ); +dev_dbg (qup->dev,"%d Revision %x\n" , pdev->id, hw_ver); + + src_clk_freq =clk_get_rate (qup->clk); + fs_div = ((src_clk_freq / qup->clk_freq) /2 ) -3 ; + hs_div =3 ; + qup->clk_ctl = (hs_div <<8 ) | (fs_div &0xff ); + qup->one_bit_t = (USEC_PER_SEC / qup->clk_freq) +1 ; + + io_mode =readl (qup->base +QUP_IO_MODE ); + + size =QUP_OUTPUT_BLOCK_SIZE (io_mode); +if (size) + qup->out_blk_sz = size *16 ; +else + qup->out_blk_sz =16 ; + + size =QUP_INPUT_BLOCK_SIZE (io_mode); +if (size) + qup->in_blk_sz = size *16 ; +else + qup->in_blk_sz =16 ; + + qup->xfer_time =msecs_to_jiffies (qup->out_fifo_sz); + +/* +* The block/fifo size w.r.t. 'actual data' is 1/2 due to 'tag' +* associated with each byte written/received +*/ + qup->out_blk_sz /=2 ; + qup->in_blk_sz /=2 ; + + size =QUP_OUTPUT_FIFO_SIZE (io_mode); + qup->out_fifo_sz = qup->out_blk_sz * (2 << size); + + size =QUP_INPUT_FIFO_SIZE (io_mode); + qup->in_fifo_sz = qup->in_blk_sz * (2 << size); + +/*
drivers/i2c/busses/i2c-qup.c line 799
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c new file +10
-0+* Wait for FIFO number of bytes to be absolutely sure +* that I2C write state machine is not idle. Each byte +* takes 9 clock cycles. (8 bits + 1 ack) +*/ + qup->wait_idle = qup->one_bit_t *9 ; + qup->wait_idle *= qup->out_fifo_sz; + +dev_info (qup->dev,"IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n" , + qup->in_blk_sz, qup->in_fifo_sz, + qup->out_blk_sz, qup->out_fifo_sz);
This could be lowered to dbg, I think.
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c new file +10
-0+ +i2c_set_adapdata (&qup->adap, qup); + qup->adap.algo = &qup_i2c_algo; + qup->adap.nr = pdev->id; + qup->adap.dev.parent = qup->dev; + qup->adap.dev.of_node = pdev->dev.of_node; +strlcpy (qup->adap.name,"QUP I2C adapter" ,sizeof (qup->adap.name)); + + ret =i2c_add_numbered_adapter (&qup->adap); +if (!ret) {
drivers/i2c/busses/i2c-qup.c line 819
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c new file +10
-0+pm_runtime_set_autosuspend_delay (qup->dev,MSEC_PER_SEC ); +pm_runtime_use_autosuspend (qup->dev); +pm_runtime_enable (qup->dev); +return 0 ; + } +fail: +qup_i2c_disable_clocks (qup); +return ret; +} +
Thanks,
Ivan
Re: [PATCH v2 2/2] i2c: New bus driver for the QUP I2C controller
In reply to Bjorn AnderssonQuoted 4 lines · Bjorn Andersson
+/*
+ * QUP driver for Qualcomm MSM platforms
+ *
+ */
This comment seems redundant, we know what file we're looking at.
drivers/i2c/busses/i2c-qup.c line 116
Quoted 5 lines
drivers/i2c/busses/i2c-qup.c new file +5
-0+ +struct qup_i2c_dev { +struct device *dev; +void __iomem *base; +struct pinctrl *pctrl;
This is unused.
drivers/i2c/busses/i2c-qup.c line 122
Quoted 6 lines
drivers/i2c/busses/i2c-qup.c new file +6
-0+int irq; +struct clk *clk; +struct clk *pclk; +struct i2c_adapter adap; + +int clk_freq;
This is only ever used in probe, so why do we need to store it away?
drivers/i2c/busses/i2c-qup.c line 133
Quoted 11 lines
drivers/i2c/busses/i2c-qup.c new file +11
-0+int clk_ctl; +int one_bit_t; +int out_fifo_sz; +int in_fifo_sz; +int out_blk_sz; +int in_blk_sz; +unsigned long xfer_time; +unsigned long wait_idle; + +struct i2c_msg *msg; +/* Current possion in user message buffer */
s/possion/position/?
drivers/i2c/busses/i2c-qup.c line 142
Quoted 9 lines
drivers/i2c/busses/i2c-qup.c new file +9
-0+int pos; +/* Keep number of bytes left to be transmitted */ +int cnt; +/* I2C protocol errors */ +u32 bus_err; +/* QUP core errors */ +u32 qup_err; +/* +* maximum bytes that could be send (per iterration). could be
s/iterration/iteration/?
Quoted 13 lines
drivers/i2c/busses/i2c-qup.c new file +13
-0+* equal of fifo size or block size (in block mode) +*/ +int chunk_sz; +struct completion xfer; +}; + +static irqreturn_t qup_i2c_interrupt (int irq,void *dev) +{ +struct qup_i2c_dev *qup = dev; +u32 bus_err; +u32 qup_err; +u32 opflags; +
[...]
drivers/i2c/busses/i2c-qup.c line 185
Quoted 5 lines
drivers/i2c/busses/i2c-qup.c new file +5
-0+ +if (opflags &QUP_OUT_SVC_FLAG ) +writel (QUP_OUT_SVC_FLAG , qup->base +QUP_OPERATIONAL ); + +if (!(qup->msg->flags ==I2C_M_RD ))
Should this be?
if (!(qup->msg->flags & I2C_M_RD))
Otherwise it should be
if (qup->msg->flags != I2C_M_RD)
Quoted 15 lines
drivers/i2c/busses/i2c-qup.c new file +15
-0+return IRQ_HANDLED ; + +if ((opflags &QUP_MX_INPUT_DONE ) || (opflags &QUP_IN_SVC_FLAG )) +writel (QUP_IN_SVC_FLAG , qup->base +QUP_OPERATIONAL ); + +done: + qup->qup_err = qup_err; + qup->bus_err = bus_err; +complete (&qup->xfer); +return IRQ_HANDLED ; +} + +static int +qup_i2c_poll_state (struct qup_i2c_dev *qup,u32 req_state,bool only_valid) +{
drivers/i2c/busses/i2c-qup.c line 210
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c new file +10
-0+int retries =0 ; +u32 state; + + do { + state = readl(qup->base + QUP_STATE); + + /* + * If only valid bit needs to be checked, requested state is + * 'don't care' + */
It looks like req_state == 0 means only_valid == true. Can we drop the only_valid argument to this function?
Quoted 4 lines
drivers/i2c/busses/i2c-qup.c new file +4
-0+if (state &QUP_STATE_VALID ) { +if (only_valid) +return 0 ; + if ((req_state & QUP_I2C_MAST_GEN)
drivers/i2c/busses/i2c-qup.c line 224
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c new file +10
-0+ && (state & QUP_I2C_MAST_GEN)) +return 0 ; +if ((state &QUP_STATE_MASK ) == req_state) +return 0 ; + } + +if (retries++ ==1000 ) +udelay (100 ); + + }while (retries !=2000 );
Please makes #defines for 1000 and 2000.
Quoted 4 lines
drivers/i2c/busses/i2c-qup.c new file +4
-0+ +return -ETIMEDOUT ; +} +
[...]
Quoted 16 lines
drivers/i2c/busses/i2c-qup.c new file +16
-0+static void qup_i2c_issue_write (struct qup_i2c_dev *qup,struct i2c_msg *msg) +{ +u32 addr = msg->addr <<1 ; +u32 val, qup_tag; +int idx, entries; + +if (qup->pos ==0 ) { + val =QUP_OUT_START | addr; + }else { +/* +* Avoid setup time issue by adding 1 NOP when number of bytes +* are more than FIFO/BLOCK size. setup time issue can't appear +* otherwise since next byte to be written will always be ready +*/ + val = (QUP_OUT_NOP |1 ); + }
drivers/i2c/busses/i2c-qup.c line 430
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c new file +10
-0+ + entries = qup->cnt +1 ; + +if (entries > qup->chunk_sz) + entries = qup->chunk_sz; + + qup_tag =QUP_OUT_DATA ; + +/* Reserve one entry for STOP */ +for (idx =1 ; idx < (entries -1 ); idx++, qup->pos++) {
Unnecessary () here around entries.
Quoted 7 lines
drivers/i2c/busses/i2c-qup.c new file +7
-0+ +if (idx &1 ) { + val |= (qup_tag | msg->buf[qup->pos]) <<QUP_MSW_SHIFT ; +writel (val, qup->base +QUP_OUT_FIFO_BASE ); + }else { + val = qup_tag | msg->buf[qup->pos]; + }
[...]
drivers/i2c/busses/i2c-qup.c line 832
Quoted 2 lines
drivers/i2c/busses/i2c-qup.c new file +2
-0+ +#ifdef CONFIG_PM
This ifdef is probably wrong considering that you can disable CONFIG_PM_RUNTIME without disabling CONFIG_PM and then these runtime PM functions would be unused.
Quoted 36 lines
drivers/i2c/busses/i2c-qup.c new file +36
-0+static int qup_i2c_pm_suspend_runtime (struct device *device) +{ +struct qup_i2c_dev *qup =dev_get_drvdata (device); + +dev_dbg (device,"pm_runtime: suspending...\n" ); +qup_i2c_disable_clocks (qup); +return 0 ; +} + +static int qup_i2c_pm_resume_runtime (struct device *device) +{ +struct qup_i2c_dev *qup =dev_get_drvdata (device); + +dev_dbg (device,"pm_runtime: resuming...\n" ); +qup_i2c_enable_clocks (qup); +return 0 ; +} + +static int qup_i2c_suspend (struct device *device) +{ +dev_dbg (device,"system suspend" ); +qup_i2c_pm_suspend_runtime (device); +return 0 ; +} + +static int qup_i2c_resume (struct device *device) +{ +dev_dbg (device,"system resume" ); +qup_i2c_pm_resume_runtime (device); +pm_runtime_mark_last_busy (device); +pm_request_autosuspend (device); +return 0 ; +} +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops qup_i2c_qup_pm_ops = {
drivers/i2c/busses/i2c-qup.c line 878
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c new file +10
-0+SET_SYSTEM_SLEEP_PM_OPS ( + qup_i2c_suspend, + qup_i2c_resume) +SET_RUNTIME_PM_OPS ( + qup_i2c_pm_suspend_runtime, + qup_i2c_pm_resume_runtime, +NULL ) +}; + +static struct of_device_id qup_i2c_dt_match[] = {
const?
drivers/i2c/busses/i2c-qup.c line 881
Quoted 3 lines
drivers/i2c/busses/i2c-qup.c new file +3
-0+ {.compatible = "qcom,i2c-qup"}, + {} +};
MODULE_DEVICE_TABLE(of, qup_i2c_dt_match)?
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Re: [PATCH v2 2/2] i2c: New bus driver for the QUP I2C controller
Hi,
Quoted 74 lines · Stephen Boyd
Quoted 4 lines · Bjorn Andersson
+/*
+ * QUP driver for Qualcomm MSM platforms
+ *
+ */This comment seems redundant, we know what file we're looking at.
drivers/i2c/busses/i2c-qup.c line 116
Quoted 5 lines
drivers/i2c/busses/i2c-qup.c new file +5
-0+ +struct qup_i2c_dev { +struct device *dev; +void __iomem *base; +struct pinctrl *pctrl;This is unused.
drivers/i2c/busses/i2c-qup.c line 122
Quoted 6 lines
drivers/i2c/busses/i2c-qup.c new file +6
-0+int irq; +struct clk *clk; +struct clk *pclk; +struct i2c_adapter adap; + +int clk_freq;This is only ever used in probe, so why do we need to store it away?
drivers/i2c/busses/i2c-qup.c line 133
Quoted 11 lines
drivers/i2c/busses/i2c-qup.c new file +11
-0+int clk_ctl; +int one_bit_t; +int out_fifo_sz; +int in_fifo_sz; +int out_blk_sz; +int in_blk_sz; +unsigned long xfer_time; +unsigned long wait_idle; + +struct i2c_msg *msg; +/* Current possion in user message buffer */ s/possion/position/?
drivers/i2c/busses/i2c-qup.c line 142
Quoted 9 lines
drivers/i2c/busses/i2c-qup.c new file +9
-0+int pos; +/* Keep number of bytes left to be transmitted */ +int cnt; +/* I2C protocol errors */ +u32 bus_err; +/* QUP core errors */ +u32 qup_err; +/* +* maximum bytes that could be send (per iterration). could be s/iterration/iteration/?
Quoted 13 lines
drivers/i2c/busses/i2c-qup.c new file +13
-0+* equal of fifo size or block size (in block mode) +*/ +int chunk_sz; +struct completion xfer; +}; + +static irqreturn_t qup_i2c_interrupt (int irq,void *dev) +{ +struct qup_i2c_dev *qup = dev; +u32 bus_err; +u32 qup_err; +u32 opflags; +[...]
drivers/i2c/busses/i2c-qup.c line 185
Quoted 5 lines
drivers/i2c/busses/i2c-qup.c new file +5
-0+ +if (opflags &QUP_OUT_SVC_FLAG ) +writel (QUP_OUT_SVC_FLAG , qup->base +QUP_OPERATIONAL ); + +if (!(qup->msg->flags ==I2C_M_RD ))Should this be?
Quoted 6 lines
if (!(qup->msg->flags & I2C_M_RD))Otherwise it should be
if (qup->msg->flags != I2C_M_RD)
This check is actually broken. Intention was that if this is read transaction and there is no QUP_MX_INPUT_DONE or QUP_IN_SVC_FLAG to exit without wakeup transfer thread. As is it now it will never complete write transactions.
Regards,
Ivan
Quoted 12 lines
Quoted 12 lines
drivers/i2c/busses/i2c-qup.c new file +12
-0+return IRQ_HANDLED ; + +if ((opflags &QUP_MX_INPUT_DONE ) || (opflags &QUP_IN_SVC_FLAG )) +writel (QUP_IN_SVC_FLAG , qup->base +QUP_OPERATIONAL ); + +done: + qup->qup_err = qup_err; + qup->bus_err = bus_err; +complete (&qup->xfer); +return IRQ_HANDLED ; +} +
PATCH v2 2/2 i2c: New bus driver for the QUP I2C controller
In reply to Stephen BoydQuoted 8 lines · Stephen Boyd
Quoted 4 lines · Bjorn Andersson
+/*
+ * QUP driver for Qualcomm MSM platforms
+ *
+ */This comment seems redundant, we know what file we're looking at.
Indeed.
Quoted 8 lines
drivers/i2c/busses/i2c-qup.c line 116
Quoted 5 lines
drivers/i2c/busses/i2c-qup.c new file +5
-0+ +struct qup_i2c_dev { +struct device *dev; +void __iomem *base; +struct pinctrl *pctrl;This is unused.
Removed.
Quoted 10 lines
drivers/i2c/busses/i2c-qup.c line 122
Quoted 6 lines
drivers/i2c/busses/i2c-qup.c new file +6
-0+int irq; +struct clk *clk; +struct clk *pclk; +struct i2c_adapter adap; + +int clk_freq;This is only ever used in probe, so why do we need to store it away?
Moved to probe()
Quoted 5 lines
Quoted 5 lines
drivers/i2c/busses/i2c-qup.c new file +5
-0+int clk_ctl; +int one_bit_t; +int out_fifo_sz; +int in_fifo_sz; +int out_blk_sz;
Quoted 9 lines
drivers/i2c/busses/i2c-qup.c line 133
Quoted 6 lines
drivers/i2c/busses/i2c-qup.c new file +6
-0+int in_blk_sz; +unsigned long xfer_time; +unsigned long wait_idle; + +struct i2c_msg *msg; +/* Current possion in user message buffer */ s/possion/position/?
Thanks.
Quoted 12 lines
drivers/i2c/busses/i2c-qup.c line 142
Quoted 9 lines
drivers/i2c/busses/i2c-qup.c new file +9
-0+int pos; +/* Keep number of bytes left to be transmitted */ +int cnt; +/* I2C protocol errors */ +u32 bus_err; +/* QUP core errors */ +u32 qup_err; +/* +* maximum bytes that could be send (per iterration). could be s/iterration/iteration/?
Thanks
Quoted 22 lines
Quoted 13 lines
drivers/i2c/busses/i2c-qup.c new file +13
-0+* equal of fifo size or block size (in block mode) +*/ +int chunk_sz; +struct completion xfer; +}; + +static irqreturn_t qup_i2c_interrupt (int irq,void *dev) +{ +struct qup_i2c_dev *qup = dev; +u32 bus_err; +u32 qup_err; +u32 opflags; +[...]
drivers/i2c/busses/i2c-qup.c line 185
Quoted 5 lines
drivers/i2c/busses/i2c-qup.c new file +5
-0+ +if (opflags &QUP_OUT_SVC_FLAG ) +writel (QUP_OUT_SVC_FLAG , qup->base +QUP_OPERATIONAL ); + +if (!(qup->msg->flags ==I2C_M_RD ))Should this be?
Quoted 6 lines
if (!(qup->msg->flags & I2C_M_RD))Otherwise it should be
if (qup->msg->flags != I2C_M_RD)
In the codeaurora implementation this seem to be:
if (qup->msg->flags == I2C_M_RD) {
if ((opflags & QUP_MX_INPUT_DONE) || (opflags & QUP_IN_SVC_FLAG))
writel(QUP_IN_SVC_FLAG, qup->base + QUP_OPERATIONAL);
else
return IRQ_HANDLED;
}So, do not complete the xfer unless there's something for us. I'll verify this and update the next version.
Thanks.
Quoted 20 lines
Quoted 20 lines
drivers/i2c/busses/i2c-qup.c new file +20
-0+return IRQ_HANDLED ; + +if ((opflags &QUP_MX_INPUT_DONE ) || (opflags &QUP_IN_SVC_FLAG )) +writel (QUP_IN_SVC_FLAG , qup->base +QUP_OPERATIONAL ); + +done: + qup->qup_err = qup_err; + qup->bus_err = bus_err; +complete (&qup->xfer); +return IRQ_HANDLED ; +} + +static int +qup_i2c_poll_state (struct qup_i2c_dev *qup,u32 req_state,bool only_valid) +{ +int retries =0 ; +u32 state; + + do { + state = readl(qup->base + QUP_STATE);
Quoted 9 lines
drivers/i2c/busses/i2c-qup.c line 210
Quoted 5 lines
drivers/i2c/busses/i2c-qup.c new file +5
-0+ +/* +* If only valid bit needs to be checked, requested state is +* 'don't care' +*/ It looks like
req_state== 0 meansonly_valid== true. Can we drop theonly_validargument to this function?
In all cases but the reset in the beginning of qup_i2c_xfer, so it seems that it has to stay.
Quoted 8 lines
Quoted 8 lines
drivers/i2c/busses/i2c-qup.c new file +8
-0+if (state &QUP_STATE_VALID ) { +if (only_valid) +return 0 ; +if ((req_state &QUP_I2C_MAST_GEN ) + && (state &QUP_I2C_MAST_GEN )) +return 0 ; +if ((state &QUP_STATE_MASK ) == req_state) +return 0 ;
Quoted 9 lines
drivers/i2c/busses/i2c-qup.c line 224
Quoted 6 lines
drivers/i2c/busses/i2c-qup.c new file +6
-0+ } + +if (retries++ ==1000 ) +udelay (100 ); + + }while (retries !=2000 );Please makes #defines for 1000 and 2000.
This retry loop looks completely arbitrary to me now that you say it. I'll revise this.
Quoted 25 lines
Quoted 4 lines
drivers/i2c/busses/i2c-qup.c new file +4
-0+ +return -ETIMEDOUT ; +} +[...]
Quoted 20 lines
drivers/i2c/busses/i2c-qup.c new file +20
-0+static void qup_i2c_issue_write (struct qup_i2c_dev *qup,struct i2c_msg *msg) +{ +u32 addr = msg->addr <<1 ; +u32 val, qup_tag; +int idx, entries; + +if (qup->pos ==0 ) { + val =QUP_OUT_START | addr; + }else { +/* +* Avoid setup time issue by adding 1 NOP when number of bytes +* are more than FIFO/BLOCK size. setup time issue can't appear +* otherwise since next byte to be written will always be ready +*/ + val = (QUP_OUT_NOP |1 ); + } + + entries = qup->cnt +1 ; + +if (entries > qup->chunk_sz)
Quoted 9 lines
drivers/i2c/busses/i2c-qup.c line 430
Quoted 6 lines
drivers/i2c/busses/i2c-qup.c new file +6
-0+ entries = qup->chunk_sz; + + qup_tag =QUP_OUT_DATA ; + +/* Reserve one entry for STOP */ +for (idx =1 ; idx < (entries -1 ); idx++, qup->pos++) {Unnecessary () here around entries.
Sure.
Quoted 8 lines
Quoted 7 lines
drivers/i2c/busses/i2c-qup.c new file +7
-0+ +if (idx &1 ) { + val |= (qup_tag | msg->buf[qup->pos]) <<QUP_MSW_SHIFT ; +writel (val, qup->base +QUP_OUT_FIFO_BASE ); + }else { + val = qup_tag | msg->buf[qup->pos]; + }[...]
Quoted 7 lines
drivers/i2c/busses/i2c-qup.c line 832
Quoted 2 lines
drivers/i2c/busses/i2c-qup.c new file +2
-0+ +#ifdef CONFIG_PM This ifdef is probably wrong considering that you can disable
CONFIG_PM_RUNTIMEwithout disablingCONFIG_PMand then these runtime PM functions would be unused.
Will fix.
Quoted 40 lines
Quoted 40 lines
drivers/i2c/busses/i2c-qup.c new file +40
-0+static int qup_i2c_pm_suspend_runtime (struct device *device) +{ +struct qup_i2c_dev *qup =dev_get_drvdata (device); + +dev_dbg (device,"pm_runtime: suspending...\n" ); +qup_i2c_disable_clocks (qup); +return 0 ; +} + +static int qup_i2c_pm_resume_runtime (struct device *device) +{ +struct qup_i2c_dev *qup =dev_get_drvdata (device); + +dev_dbg (device,"pm_runtime: resuming...\n" ); +qup_i2c_enable_clocks (qup); +return 0 ; +} + +static int qup_i2c_suspend (struct device *device) +{ +dev_dbg (device,"system suspend" ); +qup_i2c_pm_suspend_runtime (device); +return 0 ; +} + +static int qup_i2c_resume (struct device *device) +{ +dev_dbg (device,"system resume" ); +qup_i2c_pm_resume_runtime (device); +pm_runtime_mark_last_busy (device); +pm_request_autosuspend (device); +return 0 ; +} +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops qup_i2c_qup_pm_ops = { +SET_SYSTEM_SLEEP_PM_OPS ( + qup_i2c_suspend, + qup_i2c_resume) + SET_RUNTIME_PM_OPS(
Quoted 9 lines
drivers/i2c/busses/i2c-qup.c line 878
Quoted 6 lines
drivers/i2c/busses/i2c-qup.c new file +6
-0+ qup_i2c_pm_suspend_runtime, + qup_i2c_pm_resume_runtime, + NULL) +}; + +static struct of_device_id qup_i2c_dt_match[] = {const?
Sure
Quoted 6 lines
drivers/i2c/busses/i2c-qup.c line 881
Quoted 3 lines
drivers/i2c/busses/i2c-qup.c new file +3
-0+ {.compatible = "qcom,i2c-qup"}, + {} +};
MODULE_DEVICE_TABLE(of, qup_i2c_dt_match)?
OK
Thanks Stephen!
// Bjorn
PATCH v2 2/2 i2c: New bus driver for the QUP I2C controller
Quoted 22 lines · Bjorn Andersson
Quoted 10 lines · Stephen Boyd
Quoted 9 lines · Bjorn Andersson
drivers/i2c/busses/i2c-qup.c new file +9
-0+ +static int +qup_i2c_poll_state (struct qup_i2c_dev *qup,u32 req_state,bool only_valid) +{ +int retries =0 ; +u32 state; + + do { + state = readl(qup->base + QUP_STATE);Quoted 9 lines
drivers/i2c/busses/i2c-qup.c line 210
Quoted 5 lines
drivers/i2c/busses/i2c-qup.c new file +5
-0+ +/* +* If only valid bit needs to be checked, requested state is +* 'don't care' +*/ It looks like
req_state== 0 meansonly_valid== true. Can we drop theonly_validargument to this function?
Quoted 2 lines
In all cases but the reset in the beginning of
qup_i2c_xfer, so it seems that it has to stay.
Oh that's because QUP_RESET_STATE is equal to 0? It looks like bits 0 and 1 are the state field and bit 2 is a flag indicating that bits 0 and 1 are valid. Why not OR in the QUP_STATE_VALID flag into the macros that are passed to this function? Then the logic would simply be looping looking for a match of the req_state (which is really a mask now).
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v2 2/2 i2c: New bus driver for the QUP I2C controller
Quoted 26 lines · Stephen Boyd
Quoted 22 lines · Bjorn Andersson
Quoted 10 lines · Stephen Boyd
Quoted 9 lines · Bjorn Andersson
drivers/i2c/busses/i2c-qup.c new file +9
-0+ +static int +qup_i2c_poll_state (struct qup_i2c_dev *qup,u32 req_state,bool only_valid) +{ +int retries =0 ; +u32 state; + + do { + state = readl(qup->base + QUP_STATE);Quoted 9 lines
drivers/i2c/busses/i2c-qup.c line 210
Quoted 5 lines
drivers/i2c/busses/i2c-qup.c new file +5
-0+ +/* +* If only valid bit needs to be checked, requested state is +* 'don't care' +*/ It looks like
req_state== 0 meansonly_valid== true. Can we drop theonly_validargument to this function?Quoted 2 lines
In all cases but the reset in the beginning of
qup_i2c_xfer, so it seems that it has to stay.
Quoted 6 lines
Oh that's because
QUP_RESET_STATEis equal to 0? It looks like bits 0 and 1 are the state field and bit 2 is a flag indicating that bits 0 and 1 are valid. Why not OR in theQUP_STATE_VALIDflag into the macros that are passed to this function? Then the logic would simply be looping looking for a match of thereq_state(which is really a mask now).
While it's true that req_state == 0 means only_valid, it is not true that only_valid means req_state == 0. So there would be no way to differentiate between "valid and reset" and "valid and don't care about run/pause/reset".
So I could move the logic around, but I don't see how we could reduce the number of parameters to the function.
// Bjorn