[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID:
<TY2PPF5CB9A1BE6E6353CEB66FFD0E498C1F29EA@TY2PPF5CB9A1BE6.apcprd06.prod.outlook.com>
Date: Thu, 29 Jan 2026 02:08:00 +0000
From: Ryan Chen <ryan_chen@...eedtech.com>
To: Jeremy Kerr <jk@...econstruct.com.au>, BMC-SW <BMC-SW@...eedtech.com>,
"andriy.shevchenko@...ux.intel.com" <andriy.shevchenko@...ux.intel.com>,
"robh@...nel.org" <robh@...nel.org>, "krzk+dt@...nel.org"
<krzk+dt@...nel.org>, "conor+dt@...nel.org" <conor+dt@...nel.org>,
"benh@...nel.crashing.org" <benh@...nel.crashing.org>, "joel@....id.au"
<joel@....id.au>, "andi.shyti@...nel.org" <andi.shyti@...nel.org>,
"andrew@...econstruct.com.au" <andrew@...econstruct.com.au>,
"p.zabel@...gutronix.de" <p.zabel@...gutronix.de>,
"naresh.solanki@...ements.com" <naresh.solanki@...ements.com>,
"linux-i2c@...r.kernel.org" <linux-i2c@...r.kernel.org>,
"openbmc@...ts.ozlabs.org" <openbmc@...ts.ozlabs.org>,
"devicetree@...r.kernel.org" <devicetree@...r.kernel.org>,
"linux-arm-kernel@...ts.infradead.org"
<linux-arm-kernel@...ts.infradead.org>, "linux-aspeed@...ts.ozlabs.org"
<linux-aspeed@...ts.ozlabs.org>, "linux-kernel@...r.kernel.org"
<linux-kernel@...r.kernel.org>
Subject: RE: [PATCH v24 3/4] i2c: ast2600: Add controller driver for new
register layout
Hello Jeremy,
Thanks the review.
> linux-arm-kernel@...ts.infradead.org; linux-aspeed@...ts.ozlabs.org;
> linux-kernel@...r.kernel.org
> Subject: Re: [PATCH v24 3/4] i2c: ast2600: Add controller driver for new
> register layout
>
> Hi Ryan,
>
> > Add i2c-ast2600 new register mode driver to support AST2600 i2c new
> > register mode. This i2c-ast2600 new driver and the legacy i2c-aspeed
> > driver both match the same compatible string "aspeed,ast2600-i2c-bus"
> > because they target the same I2C controller IP on AST2600.
>
> You may want to describe the differing requirements of the old vs new driver,
> in terms of DT node detection. More on that below, but probably something
> like:
>
> - the new driver is selected for DT nodes that comply with the current
> aspeed,ast2600-i2c-bus binding spec (specifically: where the required
> aspeed,global-regs property is present)
>
> - for backwards compatibility with old DTs, the old driver is selected
> for DT nodes that do not have the aspeed,global-regs property
>
Will update.
> > However, AST2600 SoCs may configure
> > the controller instances to operate either in the legacy register
> > layout or the new layout (via global register).
> > The new register mode support following.
> >
> > - Add new clock divider option for more flexible and accurate
> > clock rate generation
> > - Add tCKHighMin timing to guarantee SCL high pulse width.
> > - Add support dual pool buffer mode, split 32 bytes pool buffer
> > of each device into 2 x 16 bytes for Tx and Rx individually.
> > - Increase DMA buffer size to 4096 bytes and support byte alignment.
> > - Re-define the base address of BUS1 ~ BUS16 and Pool buffer.
> > - Re-define registers for separating controller and target
> > mode control.
> > - Support 4 individual DMA buffers for controller Tx and Rx,
> > target Tx and Rx.
> >
> > And following is new register set for package transfer sequence.
>
> s/package/packet/
Will update following.
>
> > - New Master operation mode:
> > S -> Aw -> P
S -> Aw -> packet TxD -> P
S -> Ar -> packet RxD -> P
S -> Aw -> packet TxD -> Sr -> Ar -> packet RxD -> P
> > - Bus SDA lock auto-release capability for new controller DMA
> > command mode.
> > - Bus auto timeout for new controller/target DMA mode.
> >
> > Since the register layout is selected via a global register at runtime
> > and both drivers bind to the same compatible string, this patch
> > defines the driver selection at build-time using Kconfig, ensuring
> > that only one driver is compiled into the kernel.
>
> This doesn't look to be correct, there are no Kconfig changes here.
Will remove it.
>
> Instead, you have introduced a top-level driver that selects one of the new or
> old drivers based on the compatible and presence of the aspeed,global-regs
> property.
Yes, will update
>
> You can simplify this into two independent drivers, with no need for the
> multiplexing. The overlap on the aspeed,ast2600-i2c-bus compatible can be
> resolved by ->probe() returning -ENODEV conditionally on the presence of
> aspeed,global-regs (as your intended method of distinguishing between new
> and backwards-compatible bindings).
>
> With that approach, all you need to change in the old driver is the
> (sufficiently-commented) -ENODEV return from ->probe().
>
> > This approach avoids ambiguity and ensures consistent behavior for
> > each platform configuration.
Will add in i2c-aspeed.c aspeed_i2c_probe_bus
if (of_device_is_compatible(pdev->dev.of_node, "aspeed,ast2600-i2c-bus") &&
device_property_present(&pdev->dev, "aspeed,global-regs"))
return -ENODEV;
> >
> > The following is two versus register layout.
> > Old register mode:
> > {I2CD00}: Function Control Register
> > {I2CD04}: Clock and AC Timing Control Register
> > {I2CD08}: Clock and AC Timing Control Register
> > {I2CD0C}: Interrupt Control Register
> > {I2CD10}: Interrupt Status Register
> > {I2CD14}: Command/Status Register
> > {I2CD18}: Slave Device Address Register
> > {I2CD1C}: Pool Buffer Control Register
> > {I2CD20}: Transmit/Receive Byte Buffer Register
> > {I2CD24}: DMA Mode Buffer Address Register
> > {I2CD28}: DMA Transfer Length Register
> > {I2CD2C}: Original DMA Mode Buffer Address Setting
> > {I2CD30}: Original DMA Transfer Length Setting and Final Status
> >
> > New Register mode
> > {I2CC00}: Master/Slave Function Control Register
> > {I2CC04}: Master/Slave Clock and AC Timing Control Register
> > {I2CC08}: Master/Slave Transmit/Receive Byte Buffer Register
> > {I2CC0C}: Master/Slave Pool Buffer Control Register
> > {I2CM10}: Master Interrupt Control Register
> > {I2CM14}: Master Interrupt Status Register
> > {I2CM18}: Master Command/Status Register
> > {I2CM1C}: Master DMA Buffer Length Register
> > {I2CS20}: Slave~ Interrupt Control Register
> > {I2CS24}: Slave~ Interrupt Status Register
> > {I2CS28}: Slave~ Command/Status Register
> > {I2CS2C}: Slave~ DMA Buffer Length Register
> > {I2CM30}: Master DMA Mode Tx Buffer Base Address
> > {I2CM34}: Master DMA Mode Rx Buffer Base Address
> > {I2CS38}: Slave~ DMA Mode Tx Buffer Base Address
> > {I2CS3C}: Slave~ DMA Mode Rx Buffer Base Address
> > {I2CS40}: Save Device Address Register
> > {I2CM48}: Master DMA Length Status Register
> > {I2CS4C}: Slave DMA Length Status Register
> > {I2CC50}: Current DMA Operating Address Status
> > {I2CC54}: Current DMA Operating Length Status
>
> ... I'm not sure we need a full register description in the changelog.
Will remove it .
>
> > Add a new core file(i2c-aspeed-core.c) to avoid device tree ABI break,
> > allow both old and new device trees using the same compatible string
> > "aspeed,ast2600-i2c-bus" to function correctly.
> >
> > Signed-off-by: Ryan Chen <ryan_chen@...eedtech.com>
> > ---
> > drivers/i2c/busses/Makefile | 2 +-
> > drivers/i2c/busses/i2c-aspeed-core.c | 89 +++
> > drivers/i2c/busses/i2c-aspeed-core.h | 19 +
> > drivers/i2c/busses/i2c-aspeed.c | 43 +-
> > drivers/i2c/busses/i2c-ast2600.c | 1018
> > ++++++++++++++++++++++++++
> > 5 files changed, 1136 insertions(+), 35 deletions(-)
> > create mode 100644 drivers/i2c/busses/i2c-aspeed-core.c
> > create mode 100644 drivers/i2c/busses/i2c-aspeed-core.h
> > create mode 100644 drivers/i2c/busses/i2c-ast2600.c
> >
> > diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> > index fb985769f5ff..606b35196960 100644
> > --- a/drivers/i2c/busses/Makefile
> > +++ b/drivers/i2c/busses/Makefile
> > @@ -37,7 +37,7 @@ obj-$(CONFIG_I2C_POWERMAC) +=
> i2c-powermac.o
> > obj-$(CONFIG_I2C_ALTERA) += i2c-altera.o
> > obj-$(CONFIG_I2C_AMD_MP2) += i2c-amd-mp2-pci.o
> > i2c-amd-mp2-plat.o
> > obj-$(CONFIG_I2C_AMD_ASF) += i2c-amd-asf-plat.o
> > -obj-$(CONFIG_I2C_ASPEED) += i2c-aspeed.o
> > +obj-$(CONFIG_I2C_ASPEED) += i2c-aspeed.o i2c-ast2600.o
> > +i2c-aspeed-core.o
> > obj-$(CONFIG_I2C_AT91) += i2c-at91.o
> > i2c-at91-y := i2c-at91-core.o
> i2c-at91-master.o
> > i2c-at91-$(CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL) += i2c-at91-slave.o
> > diff --git a/drivers/i2c/busses/i2c-aspeed-core.c
> > b/drivers/i2c/busses/i2c-aspeed-core.c
> > new file mode 100644
> > index 000000000000..a2878e1273ed
> > --- /dev/null
> > +++ b/drivers/i2c/busses/i2c-aspeed-core.c
> > @@ -0,0 +1,89 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * ASPEED I2C core driver
> > + *
> > + * Copyright (C) ASPEED Technology Inc.
> > + */
>
> As above, this "multiplexing" core driver may not be necessary.
Will remove i2c-aspeed-core.c
>
> > +
> > +#include <linux/module.h>
> > +#include <linux/of_device.h>
> > +#include <linux/of_platform.h>
> > +
> > +#include "i2c-aspeed-core.h"
> > +
> > +struct aspeed_i2c_core_priv {
> > + void (*remove)(struct platform_device *pdev);
> > + void *bus_data;
> > +};
> > +
> > +static const struct of_device_id aspeed_i2c_of_match[] = {
> > + {
> > + .compatible = "aspeed,ast2400-i2c-bus",
> > + .data = (const void *)AST2400_I2C
> > + },
> > + {
> > + .compatible = "aspeed,ast2500-i2c-bus",
> > + .data = (const void *)AST2500_I2C
> > + },
> > + {
> > + .compatible = "aspeed,ast2600-i2c-bus",
> > + .data = (const void *)AST2600_I2C
> > + },
> > + { }
> > +};
>
> Just being enum members, the .data fields seem pointless; you could just check
> against the actual compatible string to determine the device type?
Yes, no need. Will update.
>
> > +
> > +MODULE_DEVICE_TABLE(of, aspeed_i2c_of_match);
> > +
> > +static int aspeed_i2c_core_probe(struct platform_device *pdev) {
> > + struct device *dev = &pdev->dev;
> > + struct aspeed_i2c_core_priv *priv;
> > + const struct of_device_id *match;
> > + int ret;
> > +
> > + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> > + if (!priv)
> > + return -ENOMEM;
> > +
> > + match = of_match_device(aspeed_i2c_of_match, dev);
> > + if (!match)
> > + return -ENODEV;
> > +
> > + if (device_is_compatible(dev, "aspeed,ast2600-i2c-bus") &&
> > + device_property_present(dev, "aspeed,global-regs")) {
> > + ret = ast2600_i2c_probe(match, pdev);
> > + priv->remove = ast2600_i2c_remove;
> > + } else {
> > + ret = aspeed_i2c_probe_bus(match, pdev);
> > + priv->remove = aspeed_i2c_remove_bus;
> > + }
> > +
> > + priv->bus_data = platform_get_drvdata(pdev);
> > + platform_set_drvdata(pdev, priv);
> > + return ret;
> > +}
> > +
> > +static void aspeed_i2c_core_remove(struct platform_device *pdev) {
> > + struct aspeed_i2c_core_priv *priv =
> > +platform_get_drvdata(pdev);
> > +
> > + if (!priv || !priv->remove)
> > + return;
> > +
> > + platform_set_drvdata(pdev, priv->bus_data);
> > + return priv->remove(pdev);
> > +}
> > +
> > +static struct platform_driver aspeed_i2c_driver = {
> > + .probe = aspeed_i2c_core_probe,
> > + .remove = aspeed_i2c_core_remove,
> > + .driver = {
> > + .name = "i2c-aspeed-core",
> > + .of_match_table = aspeed_i2c_of_match,
> > + },
> > +};
> > +module_platform_driver(aspeed_i2c_driver);
> > +
> > +MODULE_AUTHOR("Ryan Chen <ryan_chen@...eedtech.com>");
> > +MODULE_DESCRIPTION("Unified ASPEED I2C driver
> > +(AST24xx/AST25xx/AST2600)"); MODULE_LICENSE("GPL");
> > diff --git a/drivers/i2c/busses/i2c-aspeed-core.h
> > b/drivers/i2c/busses/i2c-aspeed-core.h
> > new file mode 100644
> > index 000000000000..6e0091018985
> > --- /dev/null
> > +++ b/drivers/i2c/busses/i2c-aspeed-core.h
> > @@ -0,0 +1,19 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */ #ifndef
> > +_I2C_ASPEED_CORE_H #define _I2C_ASPEED_CORE_H
> > +
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +
> > +enum i2c_version {
> > + AST2400_I2C,
> > + AST2500_I2C,
> > + AST2600_I2C,
> > + AST2700_I2C,
> > +};
> > +
> > +int aspeed_i2c_probe_bus(const struct of_device_id *match, struct
> > +platform_device *pdev); void aspeed_i2c_remove_bus(struct
> > +platform_device *pdev); int ast2600_i2c_probe(const struct
> > +of_device_id *match, struct platform_device *pdev); void
> > +ast2600_i2c_remove(struct platform_device *pdev); #endif
> > diff --git a/drivers/i2c/busses/i2c-aspeed.c
> > b/drivers/i2c/busses/i2c-aspeed.c index a26b74c71206..8466c98b6c7b
> > 100644
> > --- a/drivers/i2c/busses/i2c-aspeed.c
> > +++ b/drivers/i2c/busses/i2c-aspeed.c
> > @@ -25,6 +25,8 @@
> > #include <linux/reset.h>
> > #include <linux/slab.h>
> >
> > +#include "i2c-aspeed-core.h"
> > +
> > /* I2C Register */
> > #define
> ASPEED_I2C_FUN_CTRL_REG
> 0x00
> > #define
> ASPEED_I2C_AC_TIMING_REG1 0x04 @@
> > -978,26 +980,9 @@ static int aspeed_i2c_reset(struct aspeed_i2c_bus
> > *bus)
> > return ret;
> > }
> >
> > -static const struct of_device_id aspeed_i2c_bus_of_table[] = {
> > - {
> > - .compatible = "aspeed,ast2400-i2c-bus",
> > - .data = aspeed_i2c_24xx_get_clk_reg_val,
> > - },
> > - {
> > - .compatible = "aspeed,ast2500-i2c-bus",
> > - .data = aspeed_i2c_25xx_get_clk_reg_val,
> > - },
> > - {
> > - .compatible = "aspeed,ast2600-i2c-bus",
> > - .data = aspeed_i2c_25xx_get_clk_reg_val,
> > - },
> > - { }
> > -};
> > -MODULE_DEVICE_TABLE(of, aspeed_i2c_bus_of_table);
> > -
> > -static int aspeed_i2c_probe_bus(struct platform_device *pdev)
> > +int aspeed_i2c_probe_bus(const struct of_device_id *match,
> > + struct platform_device *pdev)
> > {
> > - const struct of_device_id *match;
> > struct aspeed_i2c_bus *bus;
> > struct clk *parent_clk;
> > int irq, ret;
> > @@ -1033,12 +1018,10 @@ static int aspeed_i2c_probe_bus(struct
> > platform_device *pdev)
> > bus->bus_frequency =
> I2C_MAX_STANDARD_MODE_FREQ;
> > }
> >
> > - match = of_match_node(aspeed_i2c_bus_of_table,
> > pdev->dev.of_node);
> > - if (!match)
> > + if ((enum i2c_version)(uintptr_t)match->data == AST2400_I2C)
> > bus->get_clk_reg_val =
> > aspeed_i2c_24xx_get_clk_reg_val;
> > else
> > - bus->get_clk_reg_val = (u32 (*)(struct device *, u32))
> > - match->data;
> > + bus->get_clk_reg_val =
> > +aspeed_i2c_25xx_get_clk_reg_val;
> >
> > /* Initialize the I2C adapter */
> > spin_lock_init(&bus->lock);
> > @@ -1081,8 +1064,9 @@ static int aspeed_i2c_probe_bus(struct
> > platform_device *pdev)
> >
> > return 0;
> > }
> > +EXPORT_SYMBOL_GPL(aspeed_i2c_probe_bus);
> >
> > -static void aspeed_i2c_remove_bus(struct platform_device *pdev)
> > +void aspeed_i2c_remove_bus(struct platform_device *pdev)
> > {
> > struct aspeed_i2c_bus *bus = platform_get_drvdata(pdev);
> > unsigned long flags;
> > @@ -1099,16 +1083,7 @@ static void aspeed_i2c_remove_bus(struct
> > platform_device *pdev)
> >
> > i2c_del_adapter(&bus->adap);
> > }
> > -
> > -static struct platform_driver aspeed_i2c_bus_driver = {
> > - .probe = aspeed_i2c_probe_bus,
> > - .remove = aspeed_i2c_remove_bus,
> > - .driver = {
> > - .name = "aspeed-i2c-bus",
> > - .of_match_table = aspeed_i2c_bus_of_table,
> > - },
> > -};
> > -module_platform_driver(aspeed_i2c_bus_driver);
> > +EXPORT_SYMBOL_GPL(aspeed_i2c_remove_bus);
> >
> > MODULE_AUTHOR("Brendan Higgins <brendanhiggins@...gle.com>");
> > MODULE_DESCRIPTION("Aspeed I2C Bus Driver"); diff --git
> > a/drivers/i2c/busses/i2c-ast2600.c b/drivers/i2c/busses/i2c-ast2600.c
> > new file mode 100644
> > index 000000000000..04cb38e018a6
>
> To prevent painting yourself into a corner (and the reference to 2700 in this
> same file): is this named appropriately for future SoC revisions?
I intent to use i2c-aspeedv2.c or i2cv2-aspeed.c or i2c-v2-aspeed.c
Or you have any suggest?
>
> > --- /dev/null
> > +++ b/drivers/i2c/busses/i2c-ast2600.c
> > @@ -0,0 +1,1018 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * ASPEED AST2600 new register set I2C controller driver
> > + *
> > + * Copyright (C) ASPEED Technology Inc.
>
> You'll need a year here.
Will update Copyright (C) 2026 ASPEED Technology Inc.
>
> > +/* 0x10 : I2CM Controller Interrupt Control Register */ #define
> > +AST2600_I2CM_IER 0x10
> > +/* 0x14 : I2CM Controller Interrupt Status Register : WC */ #define
> > +AST2600_I2CM_ISR 0x14
> > +
> > +#define
> AST2600_I2CM_PKT_TIMEOUT BIT(18)
> > +#define AST2600_I2CM_PKT_ERROR BIT(17)
> #define
> > +AST2600_I2CM_PKT_DONE BIT(16)
> > +
> > +#define AST2600_I2CM_BUS_RECOVER_FAIL BIT(15) #define
> > +AST2600_I2CM_SDA_DL_TO BIT(14) #define
> > +AST2600_I2CM_BUS_RECOVER BIT(13)
> #define
> > +AST2600_I2CM_SMBUS_ALT BIT(12)
>
> _ALT implies "alternate", can you make this _ALERT?
Will Update AST2600_I2CM_SMBUS_ALERT
>
> > +enum xfer_mode {
> > + BYTE_MODE,
> > + BUFF_MODE,
> > + DMA_MODE,
> > +};
> > +
> > +struct ast2600_i2c_bus {
> > + struct i2c_adapter adap;
> > + struct device *dev;
> > + void __iomem *reg_base;
> > + struct regmap *global_regs;
> > + struct clk *clk;
> > + struct i2c_timings timing_info;
> > + struct completion cmd_complete;
> > + struct i2c_msg *msgs;
> > + u8 *controller_dma_safe_buf;
> > + dma_addr_t controller_dma_addr;
> > + u32 apb_clk;
> > + u32 timeout;
> > + int irq;
> > + int cmd_err;
> > + int msgs_index;
> > + int msgs_count;
> > + int controller_xfer_cnt;
> > + size_t buf_index;
> > + size_t buf_size;
> > + enum xfer_mode mode;
> > + bool multi_master;
> > + /* Buffer mode */
> > + void __iomem *buf_base;
> > + struct i2c_smbus_alert_setup alert_data;
>
> You don't seem to use alert_data?
Will remove it.
>
> > +static int ast2600_i2c_recover_bus(struct ast2600_i2c_bus *i2c_bus) {
> > + u32 state = readl(i2c_bus->reg_base +
> > +AST2600_I2CC_STS_AND_BUFF);
> > + int ret = 0;
> > + u32 ctrl;
> > + int r;
> > +
> > + dev_dbg(i2c_bus->dev, "%d-bus recovery bus [%x]\n",
> > +i2c_bus->adap.nr, state);
> > +
> > + ctrl = readl(i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
> > +
> > + /* Disable controller */
> > + writel(ctrl & ~(AST2600_I2CC_MASTER_EN |
> > +AST2600_I2CC_SLAVE_EN),
> > + i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
> > +
> > + writel(readl(i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL) |
> > +AST2600_I2CC_MASTER_EN,
> > + i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
>
> Maybe split the readl out to make this simpler (and not wrapped).
Will update it.
>
> > +
> > + reinit_completion(&i2c_bus->cmd_complete);
> > + i2c_bus->cmd_err = 0;
> > +
> > + /* Check 0x14's SDA and SCL status */
>
> What is 0x14?
Will update /* Check SDA and SCL status */
>
> > + state = readl(i2c_bus->reg_base +
> AST2600_I2CC_STS_AND_BUFF);
> > + if (!(state & AST2600_I2CC_SDA_LINE_STS) && (state &
> > +AST2600_I2CC_SCL_LINE_STS)) {
> > + writel(AST2600_I2CM_RECOVER_CMD_EN,
> i2c_bus->reg_base
> > ++ AST2600_I2CM_CMD_STS);
> > + r =
> > +wait_for_completion_timeout(&i2c_bus->cmd_complete,
> > +i2c_bus->adap.timeout);
> > + if (r == 0) {
> > + dev_dbg(i2c_bus->dev, "recovery timed
> out\n");
> > + writel(ctrl, i2c_bus->reg_base +
> > +AST2600_I2CC_FUN_CTRL);
> > + return -ETIMEDOUT;
> > + } else if (i2c_bus->cmd_err) {
> > + dev_dbg(i2c_bus->dev, "recovery
> error\n");
> > + ret = -EPROTO;
> > + }
>
> One of these paths restores controller state, the other does not. Is that okay?
>
> (if both should be restoring it, you may want to just set ret and use the
> common exit path)
>
Will modify
out_restore:
writel(ctrl, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
return ret;
> > + }
> > +
> > + /* Recovery done */
> > + state = readl(i2c_bus->reg_base +
> AST2600_I2CC_STS_AND_BUFF);
> > + if (state & AST2600_I2CC_BUS_BUSY_STS) {
> > + dev_dbg(i2c_bus->dev, "Can't recover bus [%x]\n",
> > +state);
> > + ret = -EPROTO;
> > + }
>
> This (including the SCL/SDA state detection above) only seems sensible if this
> controller is the only controller on the bus, as your conditions depend on
> sampled bus state.
>
> If the recovery command completed successfully, but the bus is coincidentally
> busy due to some other controller, this will fail.
Yes, that is expected, when after the recovery command completed still need return
the bus status, and the ast2600_i2c_controller_xfer will return to upper layer.
>
> > +
> > + /* restore original controller setting */
> > + writel(ctrl, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
> > + return ret;
> > +}
>
> [...]
>
> > +static int ast2600_i2c_do_start(struct ast2600_i2c_bus *i2c_bus) {
> > + struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];
> > +
> > + /* send start */
> > + dev_dbg(i2c_bus->dev, "[%d] %s %d byte%s %s 0x%02x\n",
> > + i2c_bus->msgs_index, str_read_write(msg->flags &
> > +I2C_M_RD),
> > + msg->len, str_plural(msg->len),
> > + msg->flags & I2C_M_RD ? "from" : "to", msg->addr);
> > +
> > + i2c_bus->controller_xfer_cnt = 0;
> > + i2c_bus->buf_index = 0;
> > +
> > + if (msg->flags & I2C_M_RD) {
> > + if (i2c_bus->mode == DMA_MODE)
> > + return
> > +ast2600_i2c_setup_dma_rx(AST2600_I2CM_START_CMD, i2c_bus);
> > + else if (i2c_bus->mode == BUFF_MODE)
> > + return
> > +ast2600_i2c_setup_buff_rx(AST2600_I2CM_START_CMD, i2c_bus);
> > + else
> > + return
> > +ast2600_i2c_setup_byte_rx(AST2600_I2CM_START_CMD, i2c_bus);
> > + } else {
> > + if (i2c_bus->mode == DMA_MODE)
> > + return
> > +ast2600_i2c_setup_dma_tx(AST2600_I2CM_START_CMD, i2c_bus);
> > + else if (i2c_bus->mode == BUFF_MODE)
> > + return
> > +ast2600_i2c_setup_buff_tx(AST2600_I2CM_START_CMD, i2c_bus);
> > + else
> > + return
> > +ast2600_i2c_setup_byte_tx(AST2600_I2CM_START_CMD, i2c_bus);
>
> Would ->setup_rx() and ->setup_tx() ops be helpful? You have quite a few
> instances of this same three-branch condition.
>
> If so, you may also want symmetric cleanup ops too.
Will add with following
static void ast2600_i2c_set_xfer_ops(struct ast2600_i2c_bus *i2c_bus)
{
switch (i2c_bus->mode) {
case DMA_MODE:
i2c_bus->setup_tx = ast2600_i2c_setup_dma_tx;
i2c_bus->setup_rx = ast2600_i2c_setup_dma_rx;
break;
case BUFF_MODE:
i2c_bus->setup_tx = ast2600_i2c_setup_buff_tx;
i2c_bus->setup_rx = ast2600_i2c_setup_buff_rx;
break;
case BYTE_MODE:
default:
i2c_bus->setup_tx = ast2600_i2c_setup_byte_tx;
i2c_bus->setup_rx = ast2600_i2c_setup_byte_rx;
break;
}
}
>
> > + }
> > +}
> > +
> > +static int ast2600_i2c_irq_err_to_errno(u32 irq_status) {
> > + if (irq_status & AST2600_I2CM_ARBIT_LOSS)
> > + return -EAGAIN;
> > + if (irq_status & (AST2600_I2CM_SDA_DL_TO |
> > +AST2600_I2CM_SCL_LOW_TO))
> > + return -EBUSY;
> > + if (irq_status & (AST2600_I2CM_ABNORMAL))
> > + return -EPROTO;
> > +
> > + return 0;
> > +}
> > +
> > +static void ast2600_i2c_controller_package_irq(struct ast2600_i2c_bus
> > +*i2c_bus, u32 sts)
>
> minor: package -> packet
Will modify.
>
> > +{
> > + struct i2c_msg *msg = &i2c_bus->msgs[i2c_bus->msgs_index];
> > + int xfer_len;
> > + int i;
> > +
> > + sts &= ~AST2600_I2CM_PKT_DONE;
> > + writel(AST2600_I2CM_PKT_DONE, i2c_bus->reg_base +
> > +AST2600_I2CM_ISR);
> > + switch (sts) {
> > + case AST2600_I2CM_PKT_ERROR:
> > + i2c_bus->cmd_err = -EAGAIN;
> > + complete(&i2c_bus->cmd_complete);
> > + break;
> > + case AST2600_I2CM_PKT_ERROR | AST2600_I2CM_TX_NAK: /*
> a0 fix
> > +for issue */
> > + fallthrough;
> > + case AST2600_I2CM_PKT_ERROR | AST2600_I2CM_TX_NAK |
> AST2600_I2CM_NORMAL_STOP:
> > + i2c_bus->cmd_err = -ENXIO;
> > + complete(&i2c_bus->cmd_complete);
> > + break;
> > + case AST2600_I2CM_NORMAL_STOP:
> > + /* write 0 byte only have stop isr */
> > + i2c_bus->msgs_index++;
> > + if (i2c_bus->msgs_index < i2c_bus->msgs_count) {
> > + if (ast2600_i2c_do_start(i2c_bus)) {
> > + i2c_bus->cmd_err =
> -ENOMEM;
> >
> + complete(&i2c_bus->cmd_co
> mplete);
> > + }
> > + } else {
> > + i2c_bus->cmd_err =
> i2c_bus->msgs_index;
> > + complete(&i2c_bus->cmd_complete);
> > + }
> > + break;
> > + case AST2600_I2CM_TX_ACK:
> > + case AST2600_I2CM_TX_ACK | AST2600_I2CM_NORMAL_STOP:
> > + if (i2c_bus->mode == DMA_MODE)
> > + xfer_len =
> > +AST2600_I2C_GET_TX_DMA_LEN(readl(i2c_bus->reg_base +
> >
> +
>
> > +AST2600_I2CM_DMA_LEN_STS));
> > + else if (i2c_bus->mode == BUFF_MODE)
> > + xfer_len =
> > +AST2600_I2CC_GET_TX_BUF_LEN(readl(i2c_bus->reg_base +
> >
> +
>
> > +AST2600_I2CC_BUFF_CTRL));
> > + else
> > + xfer_len = 1;
> > +
> > + i2c_bus->controller_xfer_cnt += xfer_len;
> > +
> > + if (i2c_bus->controller_xfer_cnt == msg->len) {
> > + if (i2c_bus->mode == DMA_MODE) {
> >
> + dma_unmap_single(i2c_bus->
> dev,
> > +i2c_bus->controller_dma_addr,
> >
> + msg
> ->len,
> > +DMA_TO_DEVICE);
> > +
> > +i2c_put_dma_safe_msg_buf(i2c_bus->controller_dma_safe_buf, msg,
> >
> +
> true);
> >
> + i2c_bus->controller_dma_saf
> e_buf =
> > +NULL;
> > + }
> > + i2c_bus->msgs_index++;
> > + if (i2c_bus->msgs_index ==
> > +i2c_bus->msgs_count) {
> > + i2c_bus->cmd_err =
> > +i2c_bus->msgs_index;
> >
> + complete(&i2c_bus->cmd_co
> mplete);
> > + } else {
> > + if
> (ast2600_i2c_do_start(i2c_bus)) {
> >
> + i2c_bus->cmd_er
> r = -ENOMEM;
> > +
> > +complete(&i2c_bus->cmd_complete);
> > + }
> > + }
> > + } else {
> > + if (i2c_bus->mode == DMA_MODE)
> >
> + ast2600_i2c_setup_dma_tx(0,
> i2c_bus);
> > + else if (i2c_bus->mode == BUFF_MODE)
> >
> + ast2600_i2c_setup_buff_tx(0,
> i2c_bus);
> > + else
> >
> + ast2600_i2c_setup_byte_tx(0,
> i2c_bus);
> > + }
>
> You have quite a bit of repetition across the start and the re-start cases.
> Perhaps you can combine these into helpers?
Yes will update
i2c_bus->setup_rx
>
> > + break;
> > + case AST2600_I2CM_RX_DONE:
> > + case AST2600_I2CM_RX_DONE |
> AST2600_I2CM_NORMAL_STOP:
> > + /* do next rx */
> > + if (i2c_bus->mode == DMA_MODE) {
> > + xfer_len =
> > +AST2600_I2C_GET_RX_DMA_LEN(readl(i2c_bus->reg_base +
> >
> +
>
> > +AST2600_I2CM_DMA_LEN_STS));
> > + } else if (i2c_bus->mode == BUFF_MODE) {
> > + xfer_len =
> > +AST2600_I2CC_GET_RX_BUF_LEN(readl(i2c_bus->reg_base +
> >
> +
>
> > +AST2600_I2CC_BUFF_CTRL));
> > + for (i = 0; i < xfer_len; i++)
> >
> + msg->buf[i2c_bus->controller
> _xfer_cnt
> > ++ i] =
> >
> + readb(i2c_bus->b
> uf_base + 0x10
> > ++ i);
> > + } else {
> > + xfer_len = 1;
> > + msg->buf[i2c_bus->controller_xfer_cnt]
> =
> > +
> > +AST2600_I2CC_GET_RX_BUFF(readl(i2c_bus->reg_base +
> >
> +
>
> > +AST2600_I2CC_STS_AND_BUFF));
> > + }
> > +
> > + if (msg->flags & I2C_M_RECV_LEN) {
> > + u8 recv_len =
> > +AST2600_I2CC_GET_RX_BUFF(readl(i2c_bus->reg_base
> >
> +
> +
> > +AST2600_I2CC_STS_AND_BUFF));
> > + msg->len = min_t(unsigned int,
> recv_len,
> > +I2C_SMBUS_BLOCK_MAX);
> > + msg->len += ((msg->flags &
> I2C_CLIENT_PEC) ? 2
> > +: 1);
> > + msg->flags &= ~I2C_M_RECV_LEN;
> > + if (!recv_len)
> >
> + i2c_bus->controller_xfer_cnt
> = 0;
> > + else
> >
> + i2c_bus->controller_xfer_cnt
> = 1;
> > + } else {
> > + i2c_bus->controller_xfer_cnt +=
> xfer_len;
> > + }
> > +
> > + if (i2c_bus->controller_xfer_cnt == msg->len) {
> > + if (i2c_bus->mode == DMA_MODE) {
> >
> + dma_unmap_single(i2c_bus->
> dev,
> > +i2c_bus->controller_dma_addr,
> >
> + msg
> ->len,
> > +DMA_FROM_DEVICE);
> > +
> > +i2c_put_dma_safe_msg_buf(i2c_bus->controller_dma_safe_buf,
> >
> +
> msg, true);
> >
> + i2c_bus->controller_dma_saf
> e_buf =
> > +NULL;
> > + }
> > +
> > + i2c_bus->msgs_index++;
> > + if (i2c_bus->msgs_index ==
> > +i2c_bus->msgs_count) {
> > + i2c_bus->cmd_err =
> > +i2c_bus->msgs_index;
> >
> + complete(&i2c_bus->cmd_co
> mplete);
> > + } else {
> > + if
> (ast2600_i2c_do_start(i2c_bus)) {
> >
> + i2c_bus->cmd_er
> r = -ENOMEM;
> > +
> > +complete(&i2c_bus->cmd_complete);
> > + }
> > + }
> > + } else {
> > + if (i2c_bus->mode == DMA_MODE)
> >
> + ast2600_i2c_setup_dma_rx(0,
> i2c_bus);
> > + else if (i2c_bus->mode == BUFF_MODE)
> >
> + ast2600_i2c_setup_buff_rx(0,
> i2c_bus);
> > + else
> >
> + ast2600_i2c_setup_byte_rx(0,
> i2c_bus);
> > + }
> > + break;
> > + default:
> > + dev_dbg(i2c_bus->dev, "unhandled sts %x\n", sts);
> > + break;
> > + }
> > +}
> > +
> > +static int ast2600_i2c_controller_irq(struct ast2600_i2c_bus
> > +*i2c_bus) {
> > + u32 sts = readl(i2c_bus->reg_base + AST2600_I2CM_ISR);
> > + u32 ctrl;
> > +
> > + sts &= ~AST2600_I2CM_SMBUS_ALT;
> > +
> > + if (AST2600_I2CM_BUS_RECOVER_FAIL & sts) {
>
> minor: these conditions seem inverted. 'sts & ...' would be more clear.
Will update.
>
> > + writel(AST2600_I2CM_BUS_RECOVER_FAIL,
> > +i2c_bus->reg_base + AST2600_I2CM_ISR);
> > + ctrl = readl(i2c_bus->reg_base +
> > +AST2600_I2CC_FUN_CTRL);
> > + writel(0, i2c_bus->reg_base +
> AST2600_I2CC_FUN_CTRL);
> > + writel(ctrl, i2c_bus->reg_base +
> > +AST2600_I2CC_FUN_CTRL);
> > + i2c_bus->cmd_err = -EPROTO;
> > + complete(&i2c_bus->cmd_complete);
> > + return 1;
> > + }
> > +
> > + if (AST2600_I2CM_BUS_RECOVER & sts) {
> > + writel(AST2600_I2CM_BUS_RECOVER,
> i2c_bus->reg_base +
> > +AST2600_I2CM_ISR);
> > + i2c_bus->cmd_err = 0;
> > + complete(&i2c_bus->cmd_complete);
> > + return 1;
> > + }
> > +
> > + i2c_bus->cmd_err = ast2600_i2c_irq_err_to_errno(sts);
> > + if (i2c_bus->cmd_err) {
> > + writel(AST2600_I2CM_PKT_DONE,
> i2c_bus->reg_base +
> > +AST2600_I2CM_ISR);
> > + complete(&i2c_bus->cmd_complete);
> > + return 1;
> > + }
> > +
> > + if (AST2600_I2CM_PKT_DONE & sts) {
> > + ast2600_i2c_controller_package_irq(i2c_bus, sts);
> > + return 1;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static irqreturn_t ast2600_i2c_bus_irq(int irq, void *dev_id) {
> > + struct ast2600_i2c_bus *i2c_bus = dev_id;
> > +
> > + return IRQ_RETVAL(ast2600_i2c_controller_irq(i2c_bus));
> > +}
> > +
> > +static int ast2600_i2c_controller_xfer(struct i2c_adapter *adap,
> > +struct i2c_msg *msgs, int num) {
> > + struct ast2600_i2c_bus *i2c_bus = i2c_get_adapdata(adap);
> > + unsigned long timeout;
> > + int ret;
> > +
> > + if (!i2c_bus->multi_master &&
> > + (readl(i2c_bus->reg_base + AST2600_I2CC_STS_AND_BUFF)
> &
> > +AST2600_I2CC_BUS_BUSY_STS)) {
> > + ret = ast2600_i2c_recover_bus(i2c_bus);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + i2c_bus->cmd_err = 0;
> > + i2c_bus->msgs = msgs;
> > + i2c_bus->msgs_index = 0;
> > + i2c_bus->msgs_count = num;
> > + reinit_completion(&i2c_bus->cmd_complete);
> > + ret = ast2600_i2c_do_start(i2c_bus);
> > + if (ret)
> > + goto controller_out;
> > + timeout =
> wait_for_completion_timeout(&i2c_bus->cmd_complete,
> > +i2c_bus->adap.timeout);
> > + if (timeout == 0) {
> > + u32 ctrl = readl(i2c_bus->reg_base +
> > +AST2600_I2CC_FUN_CTRL);
> > +
> > + dev_dbg(i2c_bus->dev, "timeout isr[%x], sts[%x]\n",
> > + readl(i2c_bus->reg_base +
> AST2600_I2CM_ISR),
> > + readl(i2c_bus->reg_base +
> > +AST2600_I2CC_STS_AND_BUFF));
> > + writel(0, i2c_bus->reg_base +
> AST2600_I2CC_FUN_CTRL);
> > + writel(ctrl, i2c_bus->reg_base +
> > +AST2600_I2CC_FUN_CTRL);
> > +
> > + if (i2c_bus->multi_master &&
> > + (readl(i2c_bus->reg_base +
> > +AST2600_I2CC_STS_AND_BUFF) &
> > + AST2600_I2CC_BUS_BUSY_STS))
> > + ast2600_i2c_recover_bus(i2c_bus);
>
> Can you add a comment about how this is safe (mainly: what conditions would
> have lead to this timeout)? Doing a bus recovery - by performing additional
> SCL cycles - may not be considered good controller behaviour on an
> entirely-local error condition.
/*
* A slave holding SCL low can stall the transfer and trigger
* a master timeout, then attempt bus recovery if the bus is
* still busy.
*/
>
> > +
> > + ret = -ETIMEDOUT;
> > + } else {
> > + ret = i2c_bus->cmd_err;
> > + }
> > +
> > + dev_dbg(i2c_bus->dev, "bus%d-m: %d end\n", i2c_bus->adap.nr,
> > +i2c_bus->cmd_err);
> > +
> > +controller_out:
> > + if (i2c_bus->mode == DMA_MODE) {
> > + if (i2c_bus->controller_dma_safe_buf) {
>
> Minor: you can combine these conditions. You also seem to only set
> controller_dma_safe_buf in DMA_MODE anyway, so maybe you can just check
> that.
Will update
>
>
> > + struct i2c_msg *msg =
> > +&i2c_bus->msgs[i2c_bus->msgs_index];
> > +
> > + if (msg->flags & I2C_M_RD)
> >
> + dma_unmap_single(i2c_bus->
> dev,
> > +i2c_bus->controller_dma_addr,
> >
> + msg
> ->len,
> > +DMA_FROM_DEVICE);
> > + else
> >
> + dma_unmap_single(i2c_bus->
> dev,
> > +i2c_bus->controller_dma_addr,
> >
> + msg
> ->len,
> > +DMA_TO_DEVICE);
> > +
> > +i2c_put_dma_safe_msg_buf(i2c_bus->controller_dma_safe_buf, msg,
> > +true);
> > + i2c_bus->controller_dma_safe_buf =
> NULL;
> > + }
> > + }
> > +
> > + return ret;
> > +}
> > +
> > +static void ast2600_i2c_init(struct ast2600_i2c_bus *i2c_bus) {
> > + struct platform_device *pdev =
> > +to_platform_device(i2c_bus->dev);
> > + u32 fun_ctrl = AST2600_I2CC_BUS_AUTO_RELEASE |
> > +AST2600_I2CC_MASTER_EN;
> > +
> > + /* I2C Reset */
> > + writel(0, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
> > +
> > + i2c_bus->multi_master =
> device_property_read_bool(&pdev->dev,
> > +"multi-master");
>
> I would suggest doing this in the same place as the other DT parsing.
Will update ~
>
> > + if (!i2c_bus->multi_master)
> > + fun_ctrl |= AST2600_I2CC_MULTI_MASTER_DIS;
> > +
> > + /* Enable Controller Mode */
> > + writel(fun_ctrl, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
> > + /* disable target address */
> > + writel(0, i2c_bus->reg_base + AST2600_I2CS_ADDR_CTRL);
> > +
> > + /* Set AC Timing */
> > + ast2600_i2c_ac_timing_config(i2c_bus);
> > +
> > + /* Clear Interrupt */
> > + writel(GENMASK(27, 0), i2c_bus->reg_base +
> AST2600_I2CM_ISR);
> > +}
> > +
> > +static u32 ast2600_i2c_functionality(struct i2c_adapter *adap) {
> > + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
> > +I2C_FUNC_SMBUS_BLOCK_DATA; }
> > +
> > +static const struct i2c_algorithm i2c_ast2600_algorithm = {
> > + .xfer = ast2600_i2c_controller_xfer,
> > + .functionality = ast2600_i2c_functionality, };
> > +
> > +int ast2600_i2c_probe(const struct of_device_id *match, struct
> > +platform_device *pdev) {
> > + struct device *dev = &pdev->dev;
> > + struct ast2600_i2c_bus *i2c_bus;
> > + struct reset_control *rst;
> > + const char *xfer_mode;
> > + struct resource *res;
> > + u32 global_ctrl;
> > + int ret;
> > +
> > + i2c_bus = devm_kzalloc(dev, sizeof(*i2c_bus), GFP_KERNEL);
> > + if (!i2c_bus)
> > + return -ENOMEM;
> > +
> > + i2c_bus->reg_base = devm_platform_ioremap_resource(pdev, 0);
> > + if (IS_ERR(i2c_bus->reg_base))
> > + return PTR_ERR(i2c_bus->reg_base);
> > +
> > + rst = devm_reset_control_get_shared_deasserted(dev, NULL);
> > + if (IS_ERR(rst))
> > + return dev_err_probe(dev, PTR_ERR(rst), "Missing
> reset
> > +ctrl\n");
> > +
> > + i2c_bus->global_regs =
> >
> + syscon_regmap_lookup_by_phandle(dev_of_node(dev
> ),
> > +"aspeed,global-regs");
> > + if (IS_ERR(i2c_bus->global_regs))
> > + return PTR_ERR(i2c_bus->global_regs);
> > +
> > + regmap_read(i2c_bus->global_regs, AST2600_I2CG_CTRL,
> > +&global_ctrl);
> > + if ((global_ctrl & AST2600_GLOBAL_INIT) !=
> > +AST2600_GLOBAL_INIT) {
> > + regmap_write(i2c_bus->global_regs,
> AST2600_I2CG_CTRL,
> > +AST2600_GLOBAL_INIT);
> > + regmap_write(i2c_bus->global_regs,
> > +AST2600_I2CG_CLK_DIV_CTRL, I2CCG_DIV_CTRL);
> > + }
> > +
> > + i2c_bus->dev = dev;
> > + i2c_bus->mode = BUFF_MODE;
> > + if (!device_property_read_string(dev, "aspeed,transfer-mode",
> > +&xfer_mode)) {
> > + if (!strcmp(xfer_mode, "dma"))
> > + i2c_bus->mode = DMA_MODE;
> > + else if (!strcmp(xfer_mode, "byte"))
> > + i2c_bus->mode = BYTE_MODE;
> > + else
> > + i2c_bus->mode = BUFF_MODE;
> > + }
> > +
> > + if (i2c_bus->mode == BUFF_MODE) {
> > + i2c_bus->buf_base =
> > +devm_platform_get_and_ioremap_resource(pdev, 1, &res);
> > + if (IS_ERR(i2c_bus->buf_base))
> > + i2c_bus->mode = BYTE_MODE;
> > + else
> > + i2c_bus->buf_size = resource_size(res) /
> 2;
> > + }
> > +
> > + /*
> > + * i2c timeout counter: use base clk4 1Mhz,
> > + * per unit: 1/(1000/1024) = 1024us
> > + */
> > + ret = device_property_read_u32(dev,
> > +"i2c-scl-clk-low-timeout-us", &i2c_bus->timeout);
> > + if (!ret)
> > + i2c_bus->timeout /= 1024;
> > +
> > + init_completion(&i2c_bus->cmd_complete);
> > +
> > + i2c_bus->irq = platform_get_irq(pdev, 0);
> > + if (i2c_bus->irq < 0)
> > + return i2c_bus->irq;
> > +
> > + platform_set_drvdata(pdev, i2c_bus);
> > +
> > + i2c_bus->clk = devm_clk_get(i2c_bus->dev, NULL);
> > + if (IS_ERR(i2c_bus->clk))
> > + return dev_err_probe(i2c_bus->dev,
> > +PTR_ERR(i2c_bus->clk), "Can't get clock\n");
> > +
> > + i2c_bus->apb_clk = clk_get_rate(i2c_bus->clk);
> > +
> > + i2c_parse_fw_timings(i2c_bus->dev, &i2c_bus->timing_info,
> > +true);
> > +
> > + /* Initialize the I2C adapter */
> > + i2c_bus->adap.owner = THIS_MODULE;
> > + i2c_bus->adap.algo = &i2c_ast2600_algorithm;
> > + i2c_bus->adap.retries = 0;
> > + i2c_bus->adap.dev.parent = i2c_bus->dev;
> > + device_set_node(&i2c_bus->adap.dev, dev_fwnode(dev));
> > + i2c_bus->adap.algo_data = i2c_bus;
> > + strscpy(i2c_bus->adap.name, pdev->name);
> > + i2c_set_adapdata(&i2c_bus->adap, i2c_bus);
> > +
> > + ast2600_i2c_init(i2c_bus);
>
> If anything past this point fails, we'll leave the controller enabled.
> Is that a problem?
Will add with err exit
err:
writel(0, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
writel(0, i2c_bus->reg_base + AST2600_I2CM_IER);
return ret;
>
> > +
> > + ret = devm_request_irq(dev, i2c_bus->irq, ast2600_i2c_bus_irq,
> > +0,
> > + dev_name(dev), i2c_bus);
> > + if (ret < 0)
> > + return dev_err_probe(dev, ret, "Unable to request irq
> > +%d\n", i2c_bus->irq);
> > +
> > + writel(AST2600_I2CM_PKT_DONE |
> AST2600_I2CM_BUS_RECOVER,
> > + i2c_bus->reg_base + AST2600_I2CM_IER);
> > +
> > + ret = devm_i2c_add_adapter(dev, &i2c_bus->adap);
> > + if (ret)
> > + return ret;
> > +
> > + return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(ast2600_i2c_probe);
> > +
> > +void ast2600_i2c_remove(struct platform_device *pdev) {
> > + struct ast2600_i2c_bus *i2c_bus = platform_get_drvdata(pdev);
> > +
> > + /* Disable everything. */
> > + writel(0, i2c_bus->reg_base + AST2600_I2CC_FUN_CTRL);
> > + writel(0, i2c_bus->reg_base + AST2600_I2CM_IER); }
> > +EXPORT_SYMBOL_GPL(ast2600_i2c_remove);
> > +
> > +MODULE_AUTHOR("Ryan Chen <ryan_chen@...eedtech.com>");
> > +MODULE_DESCRIPTION("ASPEED AST2600 I2C Controller Driver");
> > +MODULE_LICENSE("GPL");
>
> Cheers,
>
>
> Jeremy
Powered by blists - more mailing lists