From: Masami Hiramatsu <masami.hiramatsu@linaro.org>
To: Peng Fan <peng.fan@nxp.com>, Simon Glass <sjg@chromium.org>,
Tom Rini <trini@konsulko.com>,
Heinrich Schuchardt <xypron.glpk@gmx.de>,
Alexander Graf <agraf@csgraf.de>,
Jagan Teki <jagan@amarulasolutions.com>,
Vignesh R <vigneshr@ti.com>,
Joe Hershberger <joe.hershberger@ni.com>,
Ramon Fried <rfried.dev@gmail.com>,
Sughosh Ganu <sughosh.ganu@linaro.org>
Cc: Masami Hiramatsu <masami.hiramatsu@linaro.org>,
Jassi Brar <jaswinder.singh@linaro.org>,
Ilias Apalodimas <ilias.apalodimas@linaro.org>,
Masahisa Kojima <masahisa.kojima@linaro.org>,
Takahiro Akashi <takahiro.akashi@linaro.org>,
Tim Harvey <tharvey@gateworks.com>, Bin Meng <bmeng.cn@gmail.com>,
u-boot@lists.denx.de, Jaehoon Chung <jh80.chung@samsung.com>
Subject: [PATCH v5 09/13] i2c: synquacer: SNI Synquacer I2C controller
Date: Fri, 4 Jun 2021 18:44:48 +0900 [thread overview]
Message-ID: <162279988869.518877.4865728880819811292.stgit@localhost> (raw)
In-Reply-To: <162279979193.518877.11119025270138617301.stgit@localhost>
From: Jassi Brar <jaswinder.singh@linaro.org>
Add driver for class of I2C controllers found on
Socionext Synquacer platform.
Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>
---
drivers/i2c/Kconfig | 7 +
drivers/i2c/Makefile | 1
drivers/i2c/synquacer_i2c.c | 338 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 346 insertions(+)
create mode 100644 drivers/i2c/synquacer_i2c.c
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index 57a4efb88e..ce0d796d09 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -455,6 +455,13 @@ config SYS_I2C_STM32F7
_ Optional clock stretching
_ Software reset
+config SYS_I2C_SYNQUACER
+ bool "Socionext SynQuacer I2C controller"
+ depends on ARCH_SYNQUACER && DM_I2C
+ help
+ Support for Socionext Synquacer I2C controller. This I2C controller
+ will be used for RTC and LS-connector on DeveloperBox.
+
config SYS_I2C_TEGRA
bool "NVIDIA Tegra internal I2C controller"
depends on ARCH_TEGRA
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index 8c9f1fcd8b..06a1150f03 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_SYS_I2C_SANDBOX) += sandbox_i2c.o i2c-emul-uclass.o
obj-$(CONFIG_SYS_I2C_SH) += sh_i2c.o
obj-$(CONFIG_SYS_I2C_SOFT) += soft_i2c.o
obj-$(CONFIG_SYS_I2C_STM32F7) += stm32f7_i2c.o
+obj-$(CONFIG_SYS_I2C_SYNQUACER) += synquacer_i2c.o
obj-$(CONFIG_SYS_I2C_TEGRA) += tegra_i2c.o
obj-$(CONFIG_SYS_I2C_UNIPHIER) += i2c-uniphier.o
obj-$(CONFIG_SYS_I2C_UNIPHIER_F) += i2c-uniphier-f.o
diff --git a/drivers/i2c/synquacer_i2c.c b/drivers/i2c/synquacer_i2c.c
new file mode 100644
index 0000000000..6672d9435e
--- /dev/null
+++ b/drivers/i2c/synquacer_i2c.c
@@ -0,0 +1,338 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ */
+
+#include <dm/device_compat.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/sizes.h>
+#include <linux/types.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <i2c.h>
+#include <clk.h>
+
+#define REG_BSR 0x0
+#define REG_BCR 0x4
+#define REG_CCR 0x8
+#define REG_ADR 0xc
+#define REG_DAR 0x10
+#define REG_CSR 0x14
+#define REG_FSR 0x18
+#define REG_BC2R 0x1c
+
+/* I2C register bit definitions */
+#define BSR_FBT BIT(0) // First Byte Transfer
+#define BSR_GCA BIT(1) // General Call Address
+#define BSR_AAS BIT(2) // Address as Slave
+#define BSR_TRX BIT(3) // Transfer/Receive
+#define BSR_LRB BIT(4) // Last Received Bit
+#define BSR_AL BIT(5) // Arbitration Lost
+#define BSR_RSC BIT(6) // Repeated Start Cond.
+#define BSR_BB BIT(7) // Bus Busy
+
+#define BCR_INT BIT(0) // Interrupt
+#define BCR_INTE BIT(1) // Interrupt Enable
+#define BCR_GCAA BIT(2) // Gen. Call Access Ack.
+#define BCR_ACK BIT(3) // Acknowledge
+#define BCR_MSS BIT(4) // Master Slave Select
+#define BCR_SCC BIT(5) // Start Condition Cont.
+#define BCR_BEIE BIT(6) // Bus Error Int Enable
+#define BCR_BER BIT(7) // Bus Error
+
+#define CCR_CS_MASK (0x1f) // CCR Clock Period Sel.
+#define CCR_EN BIT(5) // Enable
+#define CCR_FM BIT(6) // Speed Mode Select
+
+#define CSR_CS_MASK (0x3f) // CSR Clock Period Sel.
+
+#define BC2R_SCLL BIT(0) // SCL Low Drive
+#define BC2R_SDAL BIT(1) // SDA Low Drive
+#define BC2R_SCLS BIT(4) // SCL Status
+#define BC2R_SDAS BIT(5) // SDA Status
+
+/* PCLK frequency */
+#define BUS_CLK_FR(rate) (((rate) / 20000000) + 1)
+
+#define I2C_CLK_DEF 62500000
+
+/* STANDARD MODE frequency */
+#define CLK_MASTER_STD(rate) \
+ DIV_ROUND_UP(DIV_ROUND_UP((rate), I2C_SPEED_STANDARD_RATE) - 2, 2)
+/* FAST MODE frequency */
+#define CLK_MASTER_FAST(rate) \
+ DIV_ROUND_UP((DIV_ROUND_UP((rate), I2C_SPEED_FAST_RATE) - 2) * 2, 3)
+
+/* (clkrate <= 18000000) */
+/* calculate the value of CS bits in CCR register on standard mode */
+#define CCR_CS_STD_MAX_18M(rate) \
+ ((CLK_MASTER_STD(rate) - 65) \
+ & CCR_CS_MASK)
+
+/* calculate the value of CS bits in CSR register on standard mode */
+#define CSR_CS_STD_MAX_18M(rate) 0x00
+
+/* calculate the value of CS bits in CCR register on fast mode */
+#define CCR_CS_FAST_MAX_18M(rate) \
+ ((CLK_MASTER_FAST(rate) - 1) \
+ & CCR_CS_MASK)
+
+/* calculate the value of CS bits in CSR register on fast mode */
+#define CSR_CS_FAST_MAX_18M(rate) 0x00
+
+/* (clkrate > 18000000) */
+/* calculate the value of CS bits in CCR register on standard mode */
+#define CCR_CS_STD_MIN_18M(rate) \
+ ((CLK_MASTER_STD(rate) - 1) \
+ & CCR_CS_MASK)
+
+/* calculate the value of CS bits in CSR register on standard mode */
+#define CSR_CS_STD_MIN_18M(rate) \
+ (((CLK_MASTER_STD(rate) - 1) >> 5) \
+ & CSR_CS_MASK)
+
+/* calculate the value of CS bits in CCR register on fast mode */
+#define CCR_CS_FAST_MIN_18M(rate) \
+ ((CLK_MASTER_FAST(rate) - 1) \
+ & CCR_CS_MASK)
+
+/* calculate the value of CS bits in CSR register on fast mode */
+#define CSR_CS_FAST_MIN_18M(rate) \
+ (((CLK_MASTER_FAST(rate) - 1) >> 5) \
+ & CSR_CS_MASK)
+
+/* min I2C clock frequency 14M */
+#define MIN_CLK_RATE (14 * 1000000)
+/* max I2C clock frequency 200M */
+#define MAX_CLK_RATE (200 * 1000000)
+/* I2C clock frequency 18M */
+#define CLK_RATE_18M (18 * 1000000)
+
+#define SPEED_FM 400 // Fast Mode
+#define SPEED_SM 100 // Standard Mode
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct synquacer_i2c {
+ void __iomem *base;
+ unsigned long pclkrate;
+ unsigned long speed_khz;
+};
+
+static int wait_irq(struct udevice *dev)
+{
+ struct synquacer_i2c *i2c = dev_get_priv(dev);
+ int timeout = 500000;
+
+ do {
+ if (readb(i2c->base + REG_BCR) & BCR_INT)
+ return 0;
+ } while (timeout--);
+
+ pr_err("%s: timeout\n", __func__);
+ return -1;
+}
+
+static int synquacer_i2c_xfer_start(struct synquacer_i2c *i2c,
+ int addr, int read)
+{
+ u8 bsr, bcr;
+
+ writeb((addr << 1) | (read ? 1 : 0), i2c->base + REG_DAR);
+
+ bsr = readb(i2c->base + REG_BSR);
+ bcr = readb(i2c->base + REG_BCR);
+
+ if ((bsr & BSR_BB) && !(bcr & BCR_MSS))
+ return -EBUSY;
+
+ if (bsr & BSR_BB) {
+ writeb(bcr | BCR_SCC, i2c->base + REG_BCR);
+ } else {
+ if (bcr & BCR_MSS)
+ return -EAGAIN;
+ /* Start Condition + Enable Interrupts */
+ writeb(bcr | BCR_MSS | BCR_INTE | BCR_BEIE, i2c->base + REG_BCR);
+ }
+
+ udelay(100);
+ return 0;
+}
+
+static int synquacer_i2c_xfer(struct udevice *bus,
+ struct i2c_msg *msg, int nmsgs)
+{
+ struct synquacer_i2c *i2c = dev_get_priv(bus);
+ u8 bsr, bcr;
+ int idx;
+
+ for (; nmsgs > 0; nmsgs--, msg++) {
+ synquacer_i2c_xfer_start(i2c, msg->addr, msg->flags & I2C_M_RD);
+ if (wait_irq(bus))
+ return -EREMOTEIO;
+
+ bsr = readb(i2c->base + REG_BSR);
+ if (bsr & BSR_LRB) {
+ debug("%s: No ack received\n", __func__);
+ return -EREMOTEIO;
+ }
+
+ idx = 0;
+ do {
+ bsr = readb(i2c->base + REG_BSR);
+ bcr = readb(i2c->base + REG_BCR);
+ if (bcr & BCR_BER) {
+ debug("%s: Bus error detected\n", __func__);
+ return -EREMOTEIO;
+ }
+ if ((bsr & BSR_AL) || !(bcr & BCR_MSS)) {
+ debug("%s: Arbitration lost\n", __func__);
+ return -EREMOTEIO;
+ }
+
+ if (msg->flags & I2C_M_RD) {
+ bcr = BCR_MSS | BCR_INTE | BCR_BEIE;
+ if (idx < msg->len - 1)
+ bcr |= BCR_ACK;
+ writeb(bcr, i2c->base + REG_BCR);
+ if (wait_irq(bus))
+ return -EREMOTEIO;
+ bsr = readb(i2c->base + REG_BSR);
+ if (!(bsr & BSR_FBT))
+ msg->buf[idx++] = readb(i2c->base + REG_DAR);
+ } else {
+ writeb(msg->buf[idx++], i2c->base + REG_DAR);
+ bcr = BCR_MSS | BCR_INTE | BCR_BEIE;
+ writeb(bcr, i2c->base + REG_BCR);
+ if (wait_irq(bus))
+ return -EREMOTEIO;
+ bsr = readb(i2c->base + REG_BSR);
+ if (bsr & BSR_LRB) {
+ debug("%s: no ack\n", __func__);
+ return -EREMOTEIO;
+ }
+ }
+ } while (idx < msg->len);
+ }
+
+ /* Force bus state to idle, terminating any ongoing transfer */
+ writeb(0, i2c->base + REG_BCR);
+ udelay(100);
+
+ return 0;
+}
+
+static void synquacer_i2c_hw_reset(struct synquacer_i2c *i2c)
+{
+ /* Disable clock */
+ writeb(0, i2c->base + REG_CCR);
+ writeb(0, i2c->base + REG_CSR);
+
+ /* Set own Address */
+ writeb(0, i2c->base + REG_ADR);
+
+ /* Set PCLK frequency */
+ writeb(BUS_CLK_FR(i2c->pclkrate), i2c->base + REG_FSR);
+
+ /* clear IRQ (INT=0, BER=0), Interrupt Disable */
+ writeb(0, i2c->base + REG_BCR);
+ writeb(0, i2c->base + REG_BC2R);
+}
+
+static int synquacer_i2c_get_bus_speed(struct udevice *bus)
+{
+ struct synquacer_i2c *i2c = dev_get_priv(bus);
+
+ return i2c->speed_khz * 1000;
+}
+
+static int synquacer_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
+{
+ struct synquacer_i2c *i2c = dev_get_priv(bus);
+ u32 rt = i2c->pclkrate;
+ u8 ccr_cs, csr_cs;
+
+ /* Set PCLK frequency */
+ writeb(BUS_CLK_FR(i2c->pclkrate), i2c->base + REG_FSR);
+
+ if (speed >= SPEED_FM * 1000) {
+ i2c->speed_khz = SPEED_FM;
+ if (i2c->pclkrate <= CLK_RATE_18M) {
+ ccr_cs = CCR_CS_FAST_MAX_18M(rt);
+ csr_cs = CSR_CS_FAST_MAX_18M(rt);
+ } else {
+ ccr_cs = CCR_CS_FAST_MIN_18M(rt);
+ csr_cs = CSR_CS_FAST_MIN_18M(rt);
+ }
+
+ /* Set Clock and enable, Set fast mode */
+ writeb(ccr_cs | CCR_FM | CCR_EN, i2c->base + REG_CCR);
+ writeb(csr_cs, i2c->base + REG_CSR);
+ } else {
+ i2c->speed_khz = SPEED_SM;
+ if (i2c->pclkrate <= CLK_RATE_18M) {
+ ccr_cs = CCR_CS_STD_MAX_18M(rt);
+ csr_cs = CSR_CS_STD_MAX_18M(rt);
+ } else {
+ ccr_cs = CCR_CS_STD_MIN_18M(rt);
+ csr_cs = CSR_CS_STD_MIN_18M(rt);
+ }
+
+ /* Set Clock and enable, Set standard mode */
+ writeb(ccr_cs | CCR_EN, i2c->base + REG_CCR);
+ writeb(csr_cs, i2c->base + REG_CSR);
+ }
+
+ return 0;
+}
+
+static int synquacer_i2c_of_to_plat(struct udevice *bus)
+{
+ struct synquacer_i2c *priv = dev_get_priv(bus);
+ struct clk ck;
+ int ret;
+
+ ret = clk_get_by_index(bus, 0, &ck);
+ if (ret < 0) {
+ priv->pclkrate = I2C_CLK_DEF;
+ } else {
+ clk_enable(&ck);
+ priv->pclkrate = clk_get_rate(&ck);
+ }
+
+ return 0;
+}
+
+static int synquacer_i2c_probe(struct udevice *bus)
+{
+ struct synquacer_i2c *i2c = dev_get_priv(bus);
+
+ i2c->base = dev_read_addr_ptr(bus);
+ synquacer_i2c_hw_reset(i2c);
+ synquacer_i2c_set_bus_speed(bus, 400000); /* set default speed */
+ return 0;
+}
+
+static const struct dm_i2c_ops synquacer_i2c_ops = {
+ .xfer = synquacer_i2c_xfer,
+ .set_bus_speed = synquacer_i2c_set_bus_speed,
+ .get_bus_speed = synquacer_i2c_get_bus_speed,
+};
+
+static const struct udevice_id synquacer_i2c_ids[] = {
+ {
+ .compatible = "socionext,synquacer-i2c",
+ },
+ { }
+};
+
+U_BOOT_DRIVER(sni_synquacer_i2c) = {
+ .name = "sni_synquacer_i2c",
+ .id = UCLASS_I2C,
+ .of_match = synquacer_i2c_ids,
+ .of_to_plat = synquacer_i2c_of_to_plat,
+ .probe = synquacer_i2c_probe,
+ .priv_auto = sizeof(struct synquacer_i2c),
+ .ops = &synquacer_i2c_ops,
+};
next prev parent reply other threads:[~2021-06-04 9:45 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-06-04 9:43 [PATCH v5 00/13] arm64: synquacer: Add SynQuacer/DeveloperBox support Masami Hiramatsu
2021-06-04 9:43 ` [PATCH v5 01/13] ata: ahci-pci: Use scsi_ops to initialize ops Masami Hiramatsu
2021-07-06 22:50 ` Tom Rini
2021-07-07 22:47 ` Masami Hiramatsu
2021-06-04 9:43 ` [PATCH v5 02/13] dm: pci: Skip setting VGA bridge bits if parent device is the host bus Masami Hiramatsu
2021-06-04 9:43 ` [PATCH v5 03/13] efi: Fix to use null handle to create new handle for efi_fmp_raw Masami Hiramatsu
2021-06-04 9:43 ` [PATCH v5 04/13] gpio: Introduce CONFIG_GPIO_EXTRA_HEADER to cleanup #ifdefs Masami Hiramatsu
2021-06-04 9:44 ` [PATCH v5 05/13] pci: synquacer: Add SynQuacer ECAM based PCIe driver Masami Hiramatsu
2021-06-04 9:44 ` [PATCH v5 06/13] mmc: synquacer: Add SynQuacer F_SDH30 SDHCI driver Masami Hiramatsu
2021-06-04 9:44 ` [PATCH v5 07/13] spi: synquacer: Add HSSPI SPI controller driver for SynQuacer Masami Hiramatsu
2021-06-04 9:44 ` [PATCH v5 08/13] net: synquacer: Add netsec driver Masami Hiramatsu
2021-06-12 18:32 ` Ramon Fried
2021-06-12 18:39 ` Ramon Fried
2021-06-04 9:44 ` Masami Hiramatsu [this message]
2021-06-04 9:44 ` [PATCH v5 10/13] ARM: dts: synquacer: Add device trees for DeveloperBox Masami Hiramatsu
2021-06-04 9:45 ` [PATCH v5 11/13] board: synquacer: Add DeveloperBox 96boards EE support Masami Hiramatsu
2021-06-04 9:45 ` [PATCH v5 12/13] doc: qemu: arm64: Fix the documentation of capsule update Masami Hiramatsu
2021-06-04 9:45 ` [PATCH v5 13/13] configs: synquacer: Enable EFI capsule update support Masami Hiramatsu
2021-06-11 7:25 ` [PATCH v5 00/13] arm64: synquacer: Add SynQuacer/DeveloperBox support Masami Hiramatsu
2021-06-11 8:05 ` Ilias Apalodimas
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=162279988869.518877.4865728880819811292.stgit@localhost \
--to=masami.hiramatsu@linaro.org \
--cc=agraf@csgraf.de \
--cc=bmeng.cn@gmail.com \
--cc=ilias.apalodimas@linaro.org \
--cc=jagan@amarulasolutions.com \
--cc=jaswinder.singh@linaro.org \
--cc=jh80.chung@samsung.com \
--cc=joe.hershberger@ni.com \
--cc=masahisa.kojima@linaro.org \
--cc=peng.fan@nxp.com \
--cc=rfried.dev@gmail.com \
--cc=sjg@chromium.org \
--cc=sughosh.ganu@linaro.org \
--cc=takahiro.akashi@linaro.org \
--cc=tharvey@gateworks.com \
--cc=trini@konsulko.com \
--cc=u-boot@lists.denx.de \
--cc=vigneshr@ti.com \
--cc=xypron.glpk@gmx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).