* [PATCH V9 0/4] i2c: ls2x: Add support for the Loongson-2K/LS7A I2C controller
@ 2023-01-03 7:00 Binbin Zhou
2023-01-03 7:00 ` [PATCH V9 1/4] i2c: gpio: Add support on ACPI-based system Binbin Zhou
` (3 more replies)
0 siblings, 4 replies; 6+ messages in thread
From: Binbin Zhou @ 2023-01-03 7:00 UTC (permalink / raw)
To: Wolfram Sang, Wolfram Sang, Andy Shevchenko, Mika Westerberg,
linux-i2c
Cc: loongarch, devicetree, Huacai Chen, WANG Xuerui, Andy Shevchenko,
Arnd Bergmann, Rob Herring, Krzysztof Kozlowski, Jianmin Lv,
Binbin Zhou
Hi all:
This patch series adds support for the I2C module found on various
Loongson systems with the Loongson-2K SoC or the Loongson LS7A bridge chip.
For now, the I2C driver is suitable for DT-based or ACPI-based systems.
I have tested on Loongson-3A5000LA+LS7A1000/LS7A2000, Loongson-2K1000LA
and Loongson-2K0500.
Thanks.
Changes since V8:
- patch (3/4)
- Refactor the error handling process of the ls2x_i2c_xfer_one();
- Split comment and code.
Changes since V7:
- patch (3/4)
- I2C_LS2X should be added to the Kconfig/Makefile in Latin
alphabetical order.
- To avoid repeated type conversions, 'struct ls2x_i2c_priv *priv'
replaces 'struct i2c_adapter *adap' as a function parameter, e.g.
ls2x_i2c_start();
- Refactor ls2x_i2c_stop() with readb_poll_timeout(), mainly with
LS2X_SR_BUSY to indicate the status (success/timeout) of the stop
command;
- Remove extra parentheses;
- For consistency, the variable 'r' in ls2x_i2c_probe() is renamed to
'ret'.
Changes since V6:
- patch (1/4)
- Add Reviewed-by tag.
- patch (3/4)
- GPL-2.0 -> GPL-2.0-only;
- Add property.h;
- writew() should be used to operate I2C_LS2X_PRER, and drop the
suffix of I2C_LS2X_PRER_LO/I2C_LS2X_PRER_HI;
- Drop ls2x_i2c_priv->dev, for it can be completely replaced by
'adapter.dev.parent';
- Reasonable return value handling in ls2x_i2c_xfer_one();
- Dropping the I2C_M_STOP flag judgment, the stop parameter of
ls2x_i2c_xfer_one() represents the last msg;
- Add comments for subsys_initcall();
- Code formatting, such as dropping unnecessary blank lines.
Changes since V5:
- patch (1/4)
- Add property.h.
- patch (3/4)
- Put the ls2x_i2c_reginit() in front of irq requst;
- Refact ls2x_i2c_adjust_bus_speed: discard the magic value of
the divider register and LS2X_I2C_FREQ_STD is used to calculate
the frequency;
- Drop useless parameters: priv->suspended, and also disable I2C
interrupts during suspend;
- Drop ls2x_i2c_remove(), for the adapter will be auto deleted on
driver detach;
- Drop MODULE_ALIAS;
- Code formatting, such as alignment.
Thanks Andy for your comments.
Changes since V4:
- patch (1/4)
- Drop unneeded headers: of.h;
- xxx_props -> xxx_properties.
- patch (2/4)
- Add interrupt headers to fix syntax error found by Rob.
- patch (3/4)
- Drop atmoic loop in ls2x_i2c_master_xfer(), I have tested it on the
appropriate environment with no problems;
- Define the corresponding bits in I2C_LS2X_CTR to avoid magic
numbers;
- dev_get_drvdata() is used to get ls2x_i2c_priv() in
ls2x_i2c_suspend();
- i2c_add_adapter() -> devm_i2c_add_adapter();
- SET_SYSTEM_SLEEP_PM_OPS() -> DEFINE_RUNTIME_DEV_PM_OPS();
- Code formatting, such as alignment.
Details: https://lore.kernel.org/all/Y4e%2F6KewuHjAluSZ@smile.fi.intel.com/
Changes since V3:
- Addressed all review comments from v3
- Change the changelog text to make it clearer (1/5);
- Fix some minor bugs, such as formatting issues (2/5);
- Fix some formatting issues (3/5);
- Deep refactoring of code for clarity (4/5).
Details: https://lore.kernel.org/all/Y4S2cnlAm3YYvZ8E@smile.fi.intel.com/
Thanks to all for their suggestions.
Changes since V2:
- Addressed all review comments from v2
- Drop of_match_ptr() in i2c-gpio to avoid potential unused warnings
(1/5);
- Introduce i2c_gpio_get_props() function as the generic interface
to get i2c-gpio props from DT or ACPI table (2/5);
- Refact ls2x i2c code, similar to removing excessive goto tags (4/5).
Thanks to Andy and Mika for their suggestions.
Changes since V1:
- Remove the function of getting the static i2c bus number from ACPI "_UID";
- Fix build warning from kernel test robot.
Binbin Zhou (4):
i2c: gpio: Add support on ACPI-based system
dt-bindings: i2c: add Loongson LS2X I2C controller
i2c: ls2x: Add driver for Loongson-2K/LS7A I2C controller
LoongArch: Enable LS2X I2C in loongson3_defconfig
.../bindings/i2c/loongson,ls2x-i2c.yaml | 51 +++
arch/loongarch/configs/loongson3_defconfig | 1 +
drivers/i2c/busses/Kconfig | 11 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-gpio.c | 28 +-
drivers/i2c/busses/i2c-ls2x.c | 381 ++++++++++++++++++
6 files changed, 463 insertions(+), 10 deletions(-)
create mode 100644 Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml
create mode 100644 drivers/i2c/busses/i2c-ls2x.c
--
2.31.1
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH V9 1/4] i2c: gpio: Add support on ACPI-based system
2023-01-03 7:00 [PATCH V9 0/4] i2c: ls2x: Add support for the Loongson-2K/LS7A I2C controller Binbin Zhou
@ 2023-01-03 7:00 ` Binbin Zhou
2023-01-03 7:00 ` [PATCH V9 2/4] dt-bindings: i2c: Add Loongson LS2X I2C controller Binbin Zhou
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Binbin Zhou @ 2023-01-03 7:00 UTC (permalink / raw)
To: Wolfram Sang, Wolfram Sang, Andy Shevchenko, Mika Westerberg,
linux-i2c
Cc: loongarch, devicetree, Huacai Chen, WANG Xuerui, Andy Shevchenko,
Arnd Bergmann, Rob Herring, Krzysztof Kozlowski, Jianmin Lv,
Binbin Zhou
Add support for the ACPI-based device registration, so that the driver
can be also enabled through ACPI table.
Reviewed-by: Andy Shevchenko <andy@kernel.org>
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
drivers/i2c/busses/i2c-gpio.c | 28 ++++++++++++++++++----------
1 file changed, 18 insertions(+), 10 deletions(-)
diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c
index 0e4385a9bcf7..680936234ef8 100644
--- a/drivers/i2c/busses/i2c-gpio.c
+++ b/drivers/i2c/busses/i2c-gpio.c
@@ -13,9 +13,9 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
-#include <linux/of.h>
#include <linux/platform_data/i2c-gpio.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/slab.h>
struct i2c_gpio_private_data {
@@ -300,22 +300,23 @@ static inline void i2c_gpio_fault_injector_init(struct platform_device *pdev) {}
static inline void i2c_gpio_fault_injector_exit(struct platform_device *pdev) {}
#endif /* CONFIG_I2C_GPIO_FAULT_INJECTOR*/
-static void of_i2c_gpio_get_props(struct device_node *np,
- struct i2c_gpio_platform_data *pdata)
+/* Get i2c-gpio properties from DT or ACPI table */
+static void i2c_gpio_get_properties(struct device *dev,
+ struct i2c_gpio_platform_data *pdata)
{
u32 reg;
- of_property_read_u32(np, "i2c-gpio,delay-us", &pdata->udelay);
+ device_property_read_u32(dev, "i2c-gpio,delay-us", &pdata->udelay);
- if (!of_property_read_u32(np, "i2c-gpio,timeout-ms", ®))
+ if (!device_property_read_u32(dev, "i2c-gpio,timeout-ms", ®))
pdata->timeout = msecs_to_jiffies(reg);
pdata->sda_is_open_drain =
- of_property_read_bool(np, "i2c-gpio,sda-open-drain");
+ device_property_read_bool(dev, "i2c-gpio,sda-open-drain");
pdata->scl_is_open_drain =
- of_property_read_bool(np, "i2c-gpio,scl-open-drain");
+ device_property_read_bool(dev, "i2c-gpio,scl-open-drain");
pdata->scl_is_output_only =
- of_property_read_bool(np, "i2c-gpio,scl-output-only");
+ device_property_read_bool(dev, "i2c-gpio,scl-output-only");
}
static struct gpio_desc *i2c_gpio_get_desc(struct device *dev,
@@ -373,8 +374,8 @@ static int i2c_gpio_probe(struct platform_device *pdev)
bit_data = &priv->bit_data;
pdata = &priv->pdata;
- if (np) {
- of_i2c_gpio_get_props(np, pdata);
+ if (dev_fwnode(dev)) {
+ i2c_gpio_get_properties(dev, pdata);
} else {
/*
* If all platform data settings are zero it is OK
@@ -489,10 +490,17 @@ static const struct of_device_id i2c_gpio_dt_ids[] = {
MODULE_DEVICE_TABLE(of, i2c_gpio_dt_ids);
+static const struct acpi_device_id i2c_gpio_acpi_match[] = {
+ { "LOON0005" }, /* LoongArch */
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, i2c_gpio_acpi_match);
+
static struct platform_driver i2c_gpio_driver = {
.driver = {
.name = "i2c-gpio",
.of_match_table = i2c_gpio_dt_ids,
+ .acpi_match_table = i2c_gpio_acpi_match,
},
.probe = i2c_gpio_probe,
.remove = i2c_gpio_remove,
--
2.31.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH V9 2/4] dt-bindings: i2c: Add Loongson LS2X I2C controller
2023-01-03 7:00 [PATCH V9 0/4] i2c: ls2x: Add support for the Loongson-2K/LS7A I2C controller Binbin Zhou
2023-01-03 7:00 ` [PATCH V9 1/4] i2c: gpio: Add support on ACPI-based system Binbin Zhou
@ 2023-01-03 7:00 ` Binbin Zhou
2023-01-03 7:00 ` [PATCH V9 3/4] i2c: ls2x: Add driver for Loongson-2K/LS7A " Binbin Zhou
2023-01-03 7:01 ` [PATCH V9 4/4] LoongArch: Enable LS2X I2C in loongson3_defconfig Binbin Zhou
3 siblings, 0 replies; 6+ messages in thread
From: Binbin Zhou @ 2023-01-03 7:00 UTC (permalink / raw)
To: Wolfram Sang, Wolfram Sang, Andy Shevchenko, Mika Westerberg,
linux-i2c
Cc: loongarch, devicetree, Huacai Chen, WANG Xuerui, Andy Shevchenko,
Arnd Bergmann, Rob Herring, Krzysztof Kozlowski, Jianmin Lv,
Binbin Zhou, Krzysztof Kozlowski
Add Loongson LS2X I2C controller binding with DT schema format using
json-schema.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
.../bindings/i2c/loongson,ls2x-i2c.yaml | 51 +++++++++++++++++++
1 file changed, 51 insertions(+)
create mode 100644 Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml
diff --git a/Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml b/Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml
new file mode 100644
index 000000000000..67882ec6e06a
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml
@@ -0,0 +1,51 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/i2c/loongson,ls2x-i2c.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Loongson LS2X I2C Controller
+
+maintainers:
+ - Binbin Zhou <zhoubinbin@loongson.cn>
+
+allOf:
+ - $ref: /schemas/i2c/i2c-controller.yaml#
+
+properties:
+ compatible:
+ enum:
+ - loongson,ls2k-i2c
+ - loongson,ls7a-i2c
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - interrupts
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ i2c0: i2c@1fe21000 {
+ compatible = "loongson,ls2k-i2c";
+ reg = <0x1fe21000 0x8>;
+ interrupt-parent = <&extioiic>;
+ interrupts = <22 IRQ_TYPE_LEVEL_LOW>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ eeprom@57 {
+ compatible = "atmel,24c16";
+ reg = <0x57>;
+ pagesize = <16>;
+ };
+ };
--
2.31.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH V9 3/4] i2c: ls2x: Add driver for Loongson-2K/LS7A I2C controller
2023-01-03 7:00 [PATCH V9 0/4] i2c: ls2x: Add support for the Loongson-2K/LS7A I2C controller Binbin Zhou
2023-01-03 7:00 ` [PATCH V9 1/4] i2c: gpio: Add support on ACPI-based system Binbin Zhou
2023-01-03 7:00 ` [PATCH V9 2/4] dt-bindings: i2c: Add Loongson LS2X I2C controller Binbin Zhou
@ 2023-01-03 7:00 ` Binbin Zhou
2023-01-03 12:12 ` Andy Shevchenko
2023-01-03 7:01 ` [PATCH V9 4/4] LoongArch: Enable LS2X I2C in loongson3_defconfig Binbin Zhou
3 siblings, 1 reply; 6+ messages in thread
From: Binbin Zhou @ 2023-01-03 7:00 UTC (permalink / raw)
To: Wolfram Sang, Wolfram Sang, Andy Shevchenko, Mika Westerberg,
linux-i2c
Cc: loongarch, devicetree, Huacai Chen, WANG Xuerui, Andy Shevchenko,
Arnd Bergmann, Rob Herring, Krzysztof Kozlowski, Jianmin Lv,
Binbin Zhou
This I2C module is integrated into the Loongson-2K SoCs and Loongson
LS7A bridge chip.
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
drivers/i2c/busses/Kconfig | 11 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-ls2x.c | 381 ++++++++++++++++++++++++++++++++++
3 files changed, 393 insertions(+)
create mode 100644 drivers/i2c/busses/i2c-ls2x.c
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index e50f9603d189..d80c938142f5 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -761,6 +761,17 @@ config I2C_LPC2K
This driver can also be built as a module. If so, the module
will be called i2c-lpc2k.
+config I2C_LS2X
+ tristate "Loongson LS2X I2C adapter"
+ depends on MACH_LOONGSON64 || COMPILE_TEST
+ help
+ If you say yes to this option, support will be included for the
+ I2C interface on the Loongson-2K SoCs and Loongson LS7A bridge
+ chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-ls2x.
+
config I2C_MLXBF
tristate "Mellanox BlueField I2C controller"
depends on MELLANOX_PLATFORM && ARM64
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index e73cdb1d2b5a..0584fe160824 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -77,6 +77,7 @@ obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o
obj-$(CONFIG_I2C_JZ4780) += i2c-jz4780.o
obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o
obj-$(CONFIG_I2C_LPC2K) += i2c-lpc2k.o
+obj-$(CONFIG_I2C_LS2X) += i2c-ls2x.o
obj-$(CONFIG_I2C_MESON) += i2c-meson.o
obj-$(CONFIG_I2C_MICROCHIP_CORE) += i2c-microchip-corei2c.o
obj-$(CONFIG_I2C_MPC) += i2c-mpc.o
diff --git a/drivers/i2c/busses/i2c-ls2x.c b/drivers/i2c/busses/i2c-ls2x.c
new file mode 100644
index 000000000000..2215aa04233b
--- /dev/null
+++ b/drivers/i2c/busses/i2c-ls2x.c
@@ -0,0 +1,382 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Loongson-2K/Loongson LS7A I2C master mode driver
+ *
+ * Copyright (C) 2013 Loongson Technology Corporation Limited.
+ * Copyright (C) 2014-2017 Lemote, Inc.
+ * Copyright (C) 2018-2022 Loongson Technology Corporation Limited.
+ *
+ * Originally written by liushaozong
+ * Rewritten for mainline by Binbin Zhou <zhoubinbin@loongson.cn>
+ */
+
+#include <linux/bits.h>
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/iopoll.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/units.h>
+
+/* I2C Registers */
+#define I2C_LS2X_PRER 0x0 /* Freq Division Register(16 bits) */
+#define I2C_LS2X_CTR 0x2 /* Control Register */
+#define I2C_LS2X_TXR 0x3 /* Transport Data Register */
+#define I2C_LS2X_RXR 0x3 /* Receive Data Register */
+#define I2C_LS2X_CR 0x4 /* Command Control Register */
+#define I2C_LS2X_SR 0x4 /* State Register */
+
+/* Command Control Register Bit */
+#define LS2X_CR_START BIT(7) /* Start signal */
+#define LS2X_CR_STOP BIT(6) /* Stop signal */
+#define LS2X_CR_READ BIT(5) /* Read signal */
+#define LS2X_CR_WRITE BIT(4) /* Write signal */
+#define LS2X_CR_ACK BIT(3) /* Response signal */
+#define LS2X_CR_IACK BIT(0) /* Interrupt response signal */
+
+/* State Register Bit */
+#define LS2X_SR_NOACK BIT(7) /* Receive NACK */
+#define LS2X_SR_BUSY BIT(6) /* Bus busy state */
+#define LS2X_SR_AL BIT(5) /* Arbitration lost */
+#define LS2X_SR_TIP BIT(1) /* Transmission state */
+#define LS2X_SR_IF BIT(0) /* Interrupt flag */
+
+/* Control Register Bit */
+#define LS2X_CTR_EN BIT(7) /* 0: I2c frequency setting 1: Normal */
+#define LS2X_CTR_IEN BIT(6) /* Enable i2c interrupt */
+#define LS2X_CTR_MST BIT(5) /* 0: Slave mode 1: Master mode */
+#define CTR_FREQ_MASK GENMASK(7, 6)
+#define CTR_READY_MASK GENMASK(7, 5)
+
+/* The PCLK frequency from LPB */
+#define LS2X_I2C_PCLK_FREQ (50 * HZ_PER_MHZ)
+
+/* The default bus frequency, which is an empirical value */
+#define LS2X_I2C_FREQ_STD (33 * HZ_PER_KHZ)
+
+struct ls2x_i2c_priv {
+ struct i2c_adapter adapter;
+ void __iomem *base;
+ struct i2c_timings i2c_t;
+ struct completion cmd_complete;
+};
+
+/*
+ * Interrupt service routine.
+ * This gets called whenever an I2C interrupt occurs.
+ */
+static irqreturn_t ls2x_i2c_isr(int this_irq, void *dev_id)
+{
+ struct ls2x_i2c_priv *priv = dev_id;
+
+ if (!(readb(priv->base + I2C_LS2X_SR) & LS2X_SR_IF))
+ return IRQ_NONE;
+
+ writeb(LS2X_CR_IACK, priv->base + I2C_LS2X_CR);
+ complete(&priv->cmd_complete);
+ return IRQ_HANDLED;
+}
+
+/*
+ * The ls2x i2c controller supports standard mode and fast mode, so the
+ * maximum bus frequency is '400kHz'.
+ * The bus frequency is set to the empirical value of '33KHz' by default,
+ * but it can also be taken from ACPI or FDT for compatibility with more
+ * devices.
+ */
+static void ls2x_i2c_adjust_bus_speed(struct ls2x_i2c_priv *priv)
+{
+ struct i2c_timings *t = &priv->i2c_t;
+ struct device *dev = priv->adapter.dev.parent;
+ u32 acpi_speed = i2c_acpi_find_bus_speed(dev);
+
+ i2c_parse_fw_timings(dev, t, false);
+
+ if (acpi_speed || t->bus_freq_hz)
+ t->bus_freq_hz = max(t->bus_freq_hz, acpi_speed);
+ else
+ t->bus_freq_hz = LS2X_I2C_FREQ_STD;
+
+ /* Calculate and set i2c frequency. */
+ writew(LS2X_I2C_PCLK_FREQ / (5 * t->bus_freq_hz) - 1,
+ priv->base + I2C_LS2X_PRER);
+}
+
+static void ls2x_i2c_init(struct ls2x_i2c_priv *priv)
+{
+ /* Set i2c frequency setting mode and disable interrupts. */
+ writeb(readb(priv->base + I2C_LS2X_CTR) & ~CTR_FREQ_MASK,
+ priv->base + I2C_LS2X_CTR);
+
+ ls2x_i2c_adjust_bus_speed(priv);
+
+ /* Set i2c normal operating mode and enable interrupts. */
+ writeb(readb(priv->base + I2C_LS2X_CTR) | CTR_READY_MASK,
+ priv->base + I2C_LS2X_CTR);
+}
+
+static int ls2x_i2c_xfer_byte(struct ls2x_i2c_priv *priv, u8 txdata, u8 *rxdatap)
+{
+ u8 rxdata;
+ unsigned long time_left;
+
+ writeb(txdata, priv->base + I2C_LS2X_CR);
+
+ time_left = wait_for_completion_timeout(&priv->cmd_complete,
+ priv->adapter.timeout);
+ if (!time_left)
+ return -ETIMEDOUT;
+
+ rxdata = readb(priv->base + I2C_LS2X_SR);
+ if (rxdatap)
+ *rxdatap = rxdata;
+
+ return 0;
+}
+
+static int ls2x_i2c_send_byte(struct ls2x_i2c_priv *priv, u8 txdata)
+{
+ int ret;
+ u8 rxdata;
+
+ ret = ls2x_i2c_xfer_byte(priv, txdata, &rxdata);
+ if (ret)
+ return ret;
+
+ if (rxdata & LS2X_SR_AL)
+ return -EAGAIN;
+
+ if (rxdata & LS2X_SR_NOACK)
+ return -ENXIO;
+
+ return 0;
+}
+
+static int ls2x_i2c_stop(struct ls2x_i2c_priv *priv)
+{
+ u8 value;
+
+ writeb(LS2X_CR_STOP, priv->base + I2C_LS2X_CR);
+ return readb_poll_timeout(priv->base + I2C_LS2X_SR, value,
+ !(value & LS2X_SR_BUSY), 100,
+ jiffies_to_usecs(priv->adapter.timeout));
+}
+
+static int ls2x_i2c_start(struct ls2x_i2c_priv *priv, struct i2c_msg *msgs)
+{
+ reinit_completion(&priv->cmd_complete);
+
+ writeb(i2c_8bit_addr_from_msg(msgs), priv->base + I2C_LS2X_TXR);
+ return ls2x_i2c_send_byte(priv, LS2X_CR_START | LS2X_CR_WRITE);
+}
+
+static int ls2x_i2c_rx(struct ls2x_i2c_priv *priv, struct i2c_msg *msg)
+{
+ int ret;
+ u8 rxdata, *buf = msg->buf;
+ u16 len = msg->len;
+
+ /* Contains steps to send start condition and address. */
+ ret = ls2x_i2c_start(priv, msg);
+ if (ret)
+ return ret;
+
+ while (len--) {
+ ret = ls2x_i2c_xfer_byte(priv,
+ LS2X_CR_READ | (len ? 0 : LS2X_CR_ACK),
+ &rxdata);
+ if (ret)
+ return ret;
+
+ *buf++ = readb(priv->base + I2C_LS2X_RXR);
+ }
+
+ return 0;
+}
+
+static int ls2x_i2c_tx(struct ls2x_i2c_priv *priv, struct i2c_msg *msg)
+{
+ int ret;
+ u8 *buf = msg->buf;
+ u16 len = msg->len;
+
+ /* Contains steps to send start condition and address. */
+ ret = ls2x_i2c_start(priv, msg);
+ if (ret)
+ return ret;
+
+ while (len--) {
+ writeb(*buf++, priv->base + I2C_LS2X_TXR);
+
+ ret = ls2x_i2c_send_byte(priv, LS2X_CR_WRITE);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ls2x_i2c_xfer_one(struct ls2x_i2c_priv *priv,
+ struct i2c_msg *msg, bool stop)
+{
+ int ret;
+
+ if (msg->flags & I2C_M_RD)
+ ret = ls2x_i2c_rx(priv, msg);
+ else
+ ret = ls2x_i2c_tx(priv, msg);
+
+ if (ret < 0) {
+ /* Fatel error. Needs reinit. */
+ if (ret == -ETIMEDOUT)
+ ls2x_i2c_init(priv);
+
+ return ret;
+ }
+
+ if (stop) {
+ /* Failed to issue STOP. Needs reinit. */
+ ret = ls2x_i2c_stop(priv);
+ if (ret)
+ ls2x_i2c_init(priv);
+ }
+
+ return ret;
+}
+
+static int ls2x_i2c_master_xfer(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ int ret;
+ struct i2c_msg *msg, *emsg = msgs + num;
+ struct ls2x_i2c_priv *priv = i2c_get_adapdata(adap);
+
+ for (msg = msgs; msg < emsg; msg++) {
+ ret = ls2x_i2c_xfer_one(priv, msg, msg == emsg - 1);
+ if (ret)
+ return ret;
+ }
+
+ return num;
+}
+
+static unsigned int ls2x_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm ls2x_i2c_algo = {
+ .master_xfer = ls2x_i2c_master_xfer,
+ .functionality = ls2x_i2c_func,
+};
+
+static int ls2x_i2c_probe(struct platform_device *pdev)
+{
+ int ret, irq;
+ struct i2c_adapter *adap;
+ struct ls2x_i2c_priv *priv;
+ struct device *dev = &pdev->dev;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /* Map hardware registers */
+ priv->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ /* Add the i2c adapter */
+ adap = &priv->adapter;
+ adap->retries = 5;
+ adap->nr = pdev->id;
+ adap->dev.parent = dev;
+ adap->owner = THIS_MODULE;
+ adap->algo = &ls2x_i2c_algo;
+ adap->timeout = msecs_to_jiffies(100);
+ device_set_node(&adap->dev, dev_fwnode(dev));
+ i2c_set_adapdata(adap, priv);
+ strscpy(adap->name, pdev->name, sizeof(adap->name));
+ init_completion(&priv->cmd_complete);
+ platform_set_drvdata(pdev, priv);
+
+ ls2x_i2c_init(priv);
+
+ ret = devm_request_irq(dev, irq, ls2x_i2c_isr, IRQF_SHARED, "ls2x-i2c",
+ priv);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Unable to request irq %d\n", irq);
+
+ return devm_i2c_add_adapter(dev, adap);
+}
+
+static int ls2x_i2c_suspend(struct device *dev)
+{
+ struct ls2x_i2c_priv *priv = dev_get_drvdata(dev);
+
+ /* Disable interrupts */
+ writeb(readb(priv->base + I2C_LS2X_CTR) & ~LS2X_CTR_IEN,
+ priv->base + I2C_LS2X_CTR);
+
+ return 0;
+}
+
+static int ls2x_i2c_resume(struct device *dev)
+{
+ ls2x_i2c_init(dev_get_drvdata(dev));
+ return 0;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(ls2x_i2c_pm_ops,
+ ls2x_i2c_suspend, ls2x_i2c_resume, NULL);
+
+static const struct of_device_id ls2x_i2c_id_table[] = {
+ { .compatible = "loongson,ls2k-i2c" },
+ { .compatible = "loongson,ls7a-i2c" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ls2x_i2c_id_table);
+
+static const struct acpi_device_id ls2x_i2c_acpi_match[] = {
+ { "LOON0004" }, /* Loongson LS7A */
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, ls2x_i2c_acpi_match);
+
+static struct platform_driver ls2x_i2c_driver = {
+ .probe = ls2x_i2c_probe,
+ .driver = {
+ .name = "ls2x-i2c",
+ .pm = pm_sleep_ptr(&ls2x_i2c_pm_ops),
+ .of_match_table = ls2x_i2c_id_table,
+ .acpi_match_table = ls2x_i2c_acpi_match,
+ },
+};
+
+/* The DC subsystem depends on it, we should initialize it earlier. */
+static int __init ls2x_i2c_init_driver(void)
+{
+ return platform_driver_register(&ls2x_i2c_driver);
+}
+subsys_initcall(ls2x_i2c_init_driver);
+
+static void __exit ls2x_i2c_exit_driver(void)
+{
+ platform_driver_unregister(&ls2x_i2c_driver);
+}
+module_exit(ls2x_i2c_exit_driver);
+
+MODULE_DESCRIPTION("Loongson LS2X I2C Bus driver");
+MODULE_AUTHOR("Loongson Technology Corporation Limited");
+MODULE_LICENSE("GPL");
--
2.31.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH V9 4/4] LoongArch: Enable LS2X I2C in loongson3_defconfig
2023-01-03 7:00 [PATCH V9 0/4] i2c: ls2x: Add support for the Loongson-2K/LS7A I2C controller Binbin Zhou
` (2 preceding siblings ...)
2023-01-03 7:00 ` [PATCH V9 3/4] i2c: ls2x: Add driver for Loongson-2K/LS7A " Binbin Zhou
@ 2023-01-03 7:01 ` Binbin Zhou
3 siblings, 0 replies; 6+ messages in thread
From: Binbin Zhou @ 2023-01-03 7:01 UTC (permalink / raw)
To: Wolfram Sang, Wolfram Sang, Andy Shevchenko, Mika Westerberg,
linux-i2c
Cc: loongarch, devicetree, Huacai Chen, WANG Xuerui, Andy Shevchenko,
Arnd Bergmann, Rob Herring, Krzysztof Kozlowski, Jianmin Lv,
Binbin Zhou
This is now supported, enable for Loongson-3 systems.
Other systems are unaffected.
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
arch/loongarch/configs/loongson3_defconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig
index cb52774c80e8..5677c4f8576e 100644
--- a/arch/loongarch/configs/loongson3_defconfig
+++ b/arch/loongarch/configs/loongson3_defconfig
@@ -602,6 +602,7 @@ CONFIG_HW_RANDOM_VIRTIO=m
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_PIIX4=y
CONFIG_I2C_GPIO=y
+CONFIG_I2C_LS2X=y
CONFIG_SPI=y
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_LOONGSON=y
--
2.31.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH V9 3/4] i2c: ls2x: Add driver for Loongson-2K/LS7A I2C controller
2023-01-03 7:00 ` [PATCH V9 3/4] i2c: ls2x: Add driver for Loongson-2K/LS7A " Binbin Zhou
@ 2023-01-03 12:12 ` Andy Shevchenko
0 siblings, 0 replies; 6+ messages in thread
From: Andy Shevchenko @ 2023-01-03 12:12 UTC (permalink / raw)
To: Binbin Zhou
Cc: Wolfram Sang, Wolfram Sang, Mika Westerberg, linux-i2c, loongarch,
devicetree, Huacai Chen, WANG Xuerui, Arnd Bergmann, Rob Herring,
Krzysztof Kozlowski, Jianmin Lv
On Tue, Jan 03, 2023 at 03:00:47PM +0800, Binbin Zhou wrote:
> This I2C module is integrated into the Loongson-2K SoCs and Loongson
> LS7A bridge chip.
I believe that you have tested this on real hardware and it works as expected.
From the code perspective it is good to me,
Reviewed-by: Andy Shevchenko <andy@kernel.org>
> Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
> ---
> drivers/i2c/busses/Kconfig | 11 +
> drivers/i2c/busses/Makefile | 1 +
> drivers/i2c/busses/i2c-ls2x.c | 381 ++++++++++++++++++++++++++++++++++
> 3 files changed, 393 insertions(+)
> create mode 100644 drivers/i2c/busses/i2c-ls2x.c
>
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index e50f9603d189..d80c938142f5 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -761,6 +761,17 @@ config I2C_LPC2K
> This driver can also be built as a module. If so, the module
> will be called i2c-lpc2k.
>
> +config I2C_LS2X
> + tristate "Loongson LS2X I2C adapter"
> + depends on MACH_LOONGSON64 || COMPILE_TEST
> + help
> + If you say yes to this option, support will be included for the
> + I2C interface on the Loongson-2K SoCs and Loongson LS7A bridge
> + chip.
> +
> + This driver can also be built as a module. If so, the module
> + will be called i2c-ls2x.
> +
> config I2C_MLXBF
> tristate "Mellanox BlueField I2C controller"
> depends on MELLANOX_PLATFORM && ARM64
> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> index e73cdb1d2b5a..0584fe160824 100644
> --- a/drivers/i2c/busses/Makefile
> +++ b/drivers/i2c/busses/Makefile
> @@ -77,6 +77,7 @@ obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o
> obj-$(CONFIG_I2C_JZ4780) += i2c-jz4780.o
> obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o
> obj-$(CONFIG_I2C_LPC2K) += i2c-lpc2k.o
> +obj-$(CONFIG_I2C_LS2X) += i2c-ls2x.o
> obj-$(CONFIG_I2C_MESON) += i2c-meson.o
> obj-$(CONFIG_I2C_MICROCHIP_CORE) += i2c-microchip-corei2c.o
> obj-$(CONFIG_I2C_MPC) += i2c-mpc.o
> diff --git a/drivers/i2c/busses/i2c-ls2x.c b/drivers/i2c/busses/i2c-ls2x.c
> new file mode 100644
> index 000000000000..2215aa04233b
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-ls2x.c
> @@ -0,0 +1,382 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Loongson-2K/Loongson LS7A I2C master mode driver
> + *
> + * Copyright (C) 2013 Loongson Technology Corporation Limited.
> + * Copyright (C) 2014-2017 Lemote, Inc.
> + * Copyright (C) 2018-2022 Loongson Technology Corporation Limited.
> + *
> + * Originally written by liushaozong
> + * Rewritten for mainline by Binbin Zhou <zhoubinbin@loongson.cn>
> + */
> +
> +#include <linux/bits.h>
> +#include <linux/completion.h>
> +#include <linux/device.h>
> +#include <linux/iopoll.h>
> +#include <linux/i2c.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/platform_device.h>
> +#include <linux/property.h>
> +#include <linux/units.h>
> +
> +/* I2C Registers */
> +#define I2C_LS2X_PRER 0x0 /* Freq Division Register(16 bits) */
> +#define I2C_LS2X_CTR 0x2 /* Control Register */
> +#define I2C_LS2X_TXR 0x3 /* Transport Data Register */
> +#define I2C_LS2X_RXR 0x3 /* Receive Data Register */
> +#define I2C_LS2X_CR 0x4 /* Command Control Register */
> +#define I2C_LS2X_SR 0x4 /* State Register */
> +
> +/* Command Control Register Bit */
> +#define LS2X_CR_START BIT(7) /* Start signal */
> +#define LS2X_CR_STOP BIT(6) /* Stop signal */
> +#define LS2X_CR_READ BIT(5) /* Read signal */
> +#define LS2X_CR_WRITE BIT(4) /* Write signal */
> +#define LS2X_CR_ACK BIT(3) /* Response signal */
> +#define LS2X_CR_IACK BIT(0) /* Interrupt response signal */
> +
> +/* State Register Bit */
> +#define LS2X_SR_NOACK BIT(7) /* Receive NACK */
> +#define LS2X_SR_BUSY BIT(6) /* Bus busy state */
> +#define LS2X_SR_AL BIT(5) /* Arbitration lost */
> +#define LS2X_SR_TIP BIT(1) /* Transmission state */
> +#define LS2X_SR_IF BIT(0) /* Interrupt flag */
> +
> +/* Control Register Bit */
> +#define LS2X_CTR_EN BIT(7) /* 0: I2c frequency setting 1: Normal */
> +#define LS2X_CTR_IEN BIT(6) /* Enable i2c interrupt */
> +#define LS2X_CTR_MST BIT(5) /* 0: Slave mode 1: Master mode */
> +#define CTR_FREQ_MASK GENMASK(7, 6)
> +#define CTR_READY_MASK GENMASK(7, 5)
> +
> +/* The PCLK frequency from LPB */
> +#define LS2X_I2C_PCLK_FREQ (50 * HZ_PER_MHZ)
> +
> +/* The default bus frequency, which is an empirical value */
> +#define LS2X_I2C_FREQ_STD (33 * HZ_PER_KHZ)
> +
> +struct ls2x_i2c_priv {
> + struct i2c_adapter adapter;
> + void __iomem *base;
> + struct i2c_timings i2c_t;
> + struct completion cmd_complete;
> +};
> +
> +/*
> + * Interrupt service routine.
> + * This gets called whenever an I2C interrupt occurs.
> + */
> +static irqreturn_t ls2x_i2c_isr(int this_irq, void *dev_id)
> +{
> + struct ls2x_i2c_priv *priv = dev_id;
> +
> + if (!(readb(priv->base + I2C_LS2X_SR) & LS2X_SR_IF))
> + return IRQ_NONE;
> +
> + writeb(LS2X_CR_IACK, priv->base + I2C_LS2X_CR);
> + complete(&priv->cmd_complete);
> + return IRQ_HANDLED;
> +}
> +
> +/*
> + * The ls2x i2c controller supports standard mode and fast mode, so the
> + * maximum bus frequency is '400kHz'.
> + * The bus frequency is set to the empirical value of '33KHz' by default,
> + * but it can also be taken from ACPI or FDT for compatibility with more
> + * devices.
> + */
> +static void ls2x_i2c_adjust_bus_speed(struct ls2x_i2c_priv *priv)
> +{
> + struct i2c_timings *t = &priv->i2c_t;
> + struct device *dev = priv->adapter.dev.parent;
> + u32 acpi_speed = i2c_acpi_find_bus_speed(dev);
> +
> + i2c_parse_fw_timings(dev, t, false);
> +
> + if (acpi_speed || t->bus_freq_hz)
> + t->bus_freq_hz = max(t->bus_freq_hz, acpi_speed);
> + else
> + t->bus_freq_hz = LS2X_I2C_FREQ_STD;
> +
> + /* Calculate and set i2c frequency. */
> + writew(LS2X_I2C_PCLK_FREQ / (5 * t->bus_freq_hz) - 1,
> + priv->base + I2C_LS2X_PRER);
> +}
> +
> +static void ls2x_i2c_init(struct ls2x_i2c_priv *priv)
> +{
> + /* Set i2c frequency setting mode and disable interrupts. */
> + writeb(readb(priv->base + I2C_LS2X_CTR) & ~CTR_FREQ_MASK,
> + priv->base + I2C_LS2X_CTR);
> +
> + ls2x_i2c_adjust_bus_speed(priv);
> +
> + /* Set i2c normal operating mode and enable interrupts. */
> + writeb(readb(priv->base + I2C_LS2X_CTR) | CTR_READY_MASK,
> + priv->base + I2C_LS2X_CTR);
> +}
> +
> +static int ls2x_i2c_xfer_byte(struct ls2x_i2c_priv *priv, u8 txdata, u8 *rxdatap)
> +{
> + u8 rxdata;
> + unsigned long time_left;
> +
> + writeb(txdata, priv->base + I2C_LS2X_CR);
> +
> + time_left = wait_for_completion_timeout(&priv->cmd_complete,
> + priv->adapter.timeout);
> + if (!time_left)
> + return -ETIMEDOUT;
> +
> + rxdata = readb(priv->base + I2C_LS2X_SR);
> + if (rxdatap)
> + *rxdatap = rxdata;
> +
> + return 0;
> +}
> +
> +static int ls2x_i2c_send_byte(struct ls2x_i2c_priv *priv, u8 txdata)
> +{
> + int ret;
> + u8 rxdata;
> +
> + ret = ls2x_i2c_xfer_byte(priv, txdata, &rxdata);
> + if (ret)
> + return ret;
> +
> + if (rxdata & LS2X_SR_AL)
> + return -EAGAIN;
> +
> + if (rxdata & LS2X_SR_NOACK)
> + return -ENXIO;
> +
> + return 0;
> +}
> +
> +static int ls2x_i2c_stop(struct ls2x_i2c_priv *priv)
> +{
> + u8 value;
> +
> + writeb(LS2X_CR_STOP, priv->base + I2C_LS2X_CR);
> + return readb_poll_timeout(priv->base + I2C_LS2X_SR, value,
> + !(value & LS2X_SR_BUSY), 100,
> + jiffies_to_usecs(priv->adapter.timeout));
> +}
> +
> +static int ls2x_i2c_start(struct ls2x_i2c_priv *priv, struct i2c_msg *msgs)
> +{
> + reinit_completion(&priv->cmd_complete);
> +
> + writeb(i2c_8bit_addr_from_msg(msgs), priv->base + I2C_LS2X_TXR);
> + return ls2x_i2c_send_byte(priv, LS2X_CR_START | LS2X_CR_WRITE);
> +}
> +
> +static int ls2x_i2c_rx(struct ls2x_i2c_priv *priv, struct i2c_msg *msg)
> +{
> + int ret;
> + u8 rxdata, *buf = msg->buf;
> + u16 len = msg->len;
> +
> + /* Contains steps to send start condition and address. */
> + ret = ls2x_i2c_start(priv, msg);
> + if (ret)
> + return ret;
> +
> + while (len--) {
> + ret = ls2x_i2c_xfer_byte(priv,
> + LS2X_CR_READ | (len ? 0 : LS2X_CR_ACK),
> + &rxdata);
> + if (ret)
> + return ret;
> +
> + *buf++ = readb(priv->base + I2C_LS2X_RXR);
> + }
> +
> + return 0;
> +}
> +
> +static int ls2x_i2c_tx(struct ls2x_i2c_priv *priv, struct i2c_msg *msg)
> +{
> + int ret;
> + u8 *buf = msg->buf;
> + u16 len = msg->len;
> +
> + /* Contains steps to send start condition and address. */
> + ret = ls2x_i2c_start(priv, msg);
> + if (ret)
> + return ret;
> +
> + while (len--) {
> + writeb(*buf++, priv->base + I2C_LS2X_TXR);
> +
> + ret = ls2x_i2c_send_byte(priv, LS2X_CR_WRITE);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int ls2x_i2c_xfer_one(struct ls2x_i2c_priv *priv,
> + struct i2c_msg *msg, bool stop)
> +{
> + int ret;
> +
> + if (msg->flags & I2C_M_RD)
> + ret = ls2x_i2c_rx(priv, msg);
> + else
> + ret = ls2x_i2c_tx(priv, msg);
> +
> + if (ret < 0) {
> + /* Fatel error. Needs reinit. */
> + if (ret == -ETIMEDOUT)
> + ls2x_i2c_init(priv);
> +
> + return ret;
> + }
> +
> + if (stop) {
> + /* Failed to issue STOP. Needs reinit. */
> + ret = ls2x_i2c_stop(priv);
> + if (ret)
> + ls2x_i2c_init(priv);
> + }
> +
> + return ret;
> +}
> +
> +static int ls2x_i2c_master_xfer(struct i2c_adapter *adap,
> + struct i2c_msg *msgs, int num)
> +{
> + int ret;
> + struct i2c_msg *msg, *emsg = msgs + num;
> + struct ls2x_i2c_priv *priv = i2c_get_adapdata(adap);
> +
> + for (msg = msgs; msg < emsg; msg++) {
> + ret = ls2x_i2c_xfer_one(priv, msg, msg == emsg - 1);
> + if (ret)
> + return ret;
> + }
> +
> + return num;
> +}
> +
> +static unsigned int ls2x_i2c_func(struct i2c_adapter *adap)
> +{
> + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> +}
> +
> +static const struct i2c_algorithm ls2x_i2c_algo = {
> + .master_xfer = ls2x_i2c_master_xfer,
> + .functionality = ls2x_i2c_func,
> +};
> +
> +static int ls2x_i2c_probe(struct platform_device *pdev)
> +{
> + int ret, irq;
> + struct i2c_adapter *adap;
> + struct ls2x_i2c_priv *priv;
> + struct device *dev = &pdev->dev;
> +
> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + /* Map hardware registers */
> + priv->base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(priv->base))
> + return PTR_ERR(priv->base);
> +
> + irq = platform_get_irq(pdev, 0);
> + if (irq < 0)
> + return irq;
> +
> + /* Add the i2c adapter */
> + adap = &priv->adapter;
> + adap->retries = 5;
> + adap->nr = pdev->id;
> + adap->dev.parent = dev;
> + adap->owner = THIS_MODULE;
> + adap->algo = &ls2x_i2c_algo;
> + adap->timeout = msecs_to_jiffies(100);
> + device_set_node(&adap->dev, dev_fwnode(dev));
> + i2c_set_adapdata(adap, priv);
> + strscpy(adap->name, pdev->name, sizeof(adap->name));
> + init_completion(&priv->cmd_complete);
> + platform_set_drvdata(pdev, priv);
> +
> + ls2x_i2c_init(priv);
> +
> + ret = devm_request_irq(dev, irq, ls2x_i2c_isr, IRQF_SHARED, "ls2x-i2c",
> + priv);
> + if (ret < 0)
> + return dev_err_probe(dev, ret, "Unable to request irq %d\n", irq);
> +
> + return devm_i2c_add_adapter(dev, adap);
> +}
> +
> +static int ls2x_i2c_suspend(struct device *dev)
> +{
> + struct ls2x_i2c_priv *priv = dev_get_drvdata(dev);
> +
> + /* Disable interrupts */
> + writeb(readb(priv->base + I2C_LS2X_CTR) & ~LS2X_CTR_IEN,
> + priv->base + I2C_LS2X_CTR);
> +
> + return 0;
> +}
> +
> +static int ls2x_i2c_resume(struct device *dev)
> +{
> + ls2x_i2c_init(dev_get_drvdata(dev));
> + return 0;
> +}
> +
> +static DEFINE_RUNTIME_DEV_PM_OPS(ls2x_i2c_pm_ops,
> + ls2x_i2c_suspend, ls2x_i2c_resume, NULL);
> +
> +static const struct of_device_id ls2x_i2c_id_table[] = {
> + { .compatible = "loongson,ls2k-i2c" },
> + { .compatible = "loongson,ls7a-i2c" },
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, ls2x_i2c_id_table);
> +
> +static const struct acpi_device_id ls2x_i2c_acpi_match[] = {
> + { "LOON0004" }, /* Loongson LS7A */
> + { }
> +};
> +MODULE_DEVICE_TABLE(acpi, ls2x_i2c_acpi_match);
> +
> +static struct platform_driver ls2x_i2c_driver = {
> + .probe = ls2x_i2c_probe,
> + .driver = {
> + .name = "ls2x-i2c",
> + .pm = pm_sleep_ptr(&ls2x_i2c_pm_ops),
> + .of_match_table = ls2x_i2c_id_table,
> + .acpi_match_table = ls2x_i2c_acpi_match,
> + },
> +};
> +
> +/* The DC subsystem depends on it, we should initialize it earlier. */
> +static int __init ls2x_i2c_init_driver(void)
> +{
> + return platform_driver_register(&ls2x_i2c_driver);
> +}
> +subsys_initcall(ls2x_i2c_init_driver);
> +
> +static void __exit ls2x_i2c_exit_driver(void)
> +{
> + platform_driver_unregister(&ls2x_i2c_driver);
> +}
> +module_exit(ls2x_i2c_exit_driver);
> +
> +MODULE_DESCRIPTION("Loongson LS2X I2C Bus driver");
> +MODULE_AUTHOR("Loongson Technology Corporation Limited");
> +MODULE_LICENSE("GPL");
> --
> 2.31.1
>
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2023-01-03 12:13 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-01-03 7:00 [PATCH V9 0/4] i2c: ls2x: Add support for the Loongson-2K/LS7A I2C controller Binbin Zhou
2023-01-03 7:00 ` [PATCH V9 1/4] i2c: gpio: Add support on ACPI-based system Binbin Zhou
2023-01-03 7:00 ` [PATCH V9 2/4] dt-bindings: i2c: Add Loongson LS2X I2C controller Binbin Zhou
2023-01-03 7:00 ` [PATCH V9 3/4] i2c: ls2x: Add driver for Loongson-2K/LS7A " Binbin Zhou
2023-01-03 12:12 ` Andy Shevchenko
2023-01-03 7:01 ` [PATCH V9 4/4] LoongArch: Enable LS2X I2C in loongson3_defconfig Binbin Zhou
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).