* [PATCH 2/2] m68k: add coldfire mcf5307 i2c support
@ 2016-10-02 0:18 Angelo Dureghello
2016-10-05 11:47 ` Greg Ungerer
0 siblings, 1 reply; 3+ messages in thread
From: Angelo Dureghello @ 2016-10-02 0:18 UTC (permalink / raw)
To: linux-m68k; +Cc: gerg, Angelo Dureghello
Add Steven King i2c driver for Coldfire, and reviewed to build
and run properly with the actual kernel version.
Signed-off-by: Angelo Dureghello <angelo@sysam.it>
---
arch/m68k/coldfire/device.c | 191 ++++++++++++++++
arch/m68k/coldfire/m5307.c | 12 +
arch/m68k/include/asm/m5307sim.h | 8 +
arch/m68k/include/asm/mcfi2c.h | 29 +++
drivers/i2c/busses/Kconfig | 12 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-mcf.c | 463 +++++++++++++++++++++++++++++++++++++++
7 files changed, 716 insertions(+)
create mode 100644 arch/m68k/include/asm/mcfi2c.h
create mode 100644 drivers/i2c/busses/i2c-mcf.c
diff --git a/arch/m68k/coldfire/device.c b/arch/m68k/coldfire/device.c
index a0fc0c1..a1ed057 100644
--- a/arch/m68k/coldfire/device.c
+++ b/arch/m68k/coldfire/device.c
@@ -19,6 +19,7 @@
#include <asm/mcfsim.h>
#include <asm/mcfuart.h>
#include <asm/mcfqspi.h>
+#include <asm/mcfi2c.h>
/*
* All current ColdFire parts contain from 2, 3, 4 or 10 UARTS.
@@ -327,6 +328,178 @@ static struct platform_device mcf_qspi = {
};
#endif /* IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) */
+#if IS_ENABLED(CONFIG_I2C_COLDFIRE)
+static struct resource mcf_i2c0_resources[] = {
+ {
+ .start = MCFI2C_BASE0,
+ .end = MCFI2C_BASE0 + MCFI2C_SIZE0 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MCF_IRQ_I2C0,
+ .end = MCF_IRQ_I2C0,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mcfi2c_platform_data mcf_i2c0_platform_data = {
+ .bitrate = 100000,
+};
+
+static struct platform_device mcf_i2c0 = {
+ .name = "mcfi2c",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(mcf_i2c0_resources),
+ .resource = mcf_i2c0_resources,
+ .dev.platform_data = &mcf_i2c0_platform_data,
+};
+#ifdef MCFI2C_BASE1
+
+static struct resource mcf_i2c1_resources[] = {
+ {
+ .start = MCFI2C_BASE1,
+ .end = MCFI2C_BASE1 + MCFI2C_SIZE1 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MCF_IRQ_I2C1,
+ .end = MCF_IRQ_I2C1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mcfi2c_platform_data mcf_i2c1_platform_data = {
+ .bitrate = 100000,
+};
+
+static struct platform_device mcf_i2c1 = {
+ .name = "mcfi2c",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(mcf_i2c1_resources),
+ .resource = mcf_i2c1_resources,
+ .dev.platform_data = &mcf_i2c1_platform_data,
+};
+
+#endif /* MCFI2C_BASE1 */
+
+#ifdef MCFI2C_BASE2
+
+static struct resource mcf_i2c2_resources[] = {
+ {
+ .start = MCFI2C_BASE2,
+ .end = MCFI2C_BASE2 + MCFI2C_SIZE2 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MCF_IRQ_I2C2,
+ .end = MCF_IRQ_I2C2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mcfi2c_platform_data mcf_i2c2_platform_data = {
+ .bitrate = 100000,
+};
+
+static struct platform_device mcf_i2c2 = {
+ .name = "mcfi2c",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(mcf_i2c2_resources),
+ .resource = mcf_i2c2_resources,
+ .dev.platform_data = &mcf_i2c2_platform_data,
+};
+
+#endif /* MCFI2C_BASE2 */
+
+#ifdef MCFI2C_BASE3
+
+static struct resource mcf_i2c3_resources[] = {
+ {
+ .start = MCFI2C_BASE3,
+ .end = MCFI2C_BASE3 + MCFI2C_SIZE3 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MCF_IRQ_I2C3,
+ .end = MCF_IRQ_I2C3,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mcfi2c_platform_data mcf_i2c3_platform_data = {
+ .bitrate = 100000,
+};
+
+static struct platform_device mcf_i2c3 = {
+ .name = "mcfi2c",
+ .id = 3,
+ .num_resources = ARRAY_SIZE(mcf_i2c3_resources),
+ .resource = mcf_i2c3_resources,
+ .dev.platform_data = &mcf_i2c3_platform_data,
+};
+
+#endif /* MCFI2C_BASE3 */
+
+#ifdef MCFI2C_BASE4
+
+static struct resource mcf_i2c4_resources[] = {
+ {
+ .start = MCFI2C_BASE4,
+ .end = MCFI2C_BASE4 + MCFI2C_SIZE4 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MCF_IRQ_I2C4,
+ .end = MCF_IRQ_I2C4,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mcfi2c_platform_data mcf_i2c4_platform_data = {
+ .bitrate = 100000,
+};
+
+static struct platform_device mcf_i2c4 = {
+ .name = "mcfi2c",
+ .id = 4,
+ .num_resources = ARRAY_SIZE(mcf_i2c4_resources),
+ .resource = mcf_i2c4_resources,
+ .dev.platform_data = &mcf_i2c4_platform_data,
+};
+
+#endif /* MCFI2C_BASE4 */
+
+#ifdef MCFI2C_BASE5
+
+static struct resource mcf_i2c5_resources[] = {
+ {
+ .start = MCFI2C_BASE5,
+ .end = MCFI2C_BASE5 + MCFI2C_SIZE5 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MCF_IRQ_I2C5,
+ .end = MCF_IRQ_I2C5,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mcfi2c_platform_data mcf_i2c5_platform_data = {
+ .bitrate = 100000,
+};
+
+static struct platform_device mcf_i2c5 = {
+ .name = "mcfi2c",
+ .id = 5,
+ .num_resources = ARRAY_SIZE(mcf_i2c5_resources),
+ .resource = mcf_i2c5_resources,
+ .dev.platform_data = &mcf_i2c5_platform_data,
+};
+
+#endif /* MCFI2C_BASE5 */
+
+#endif /* IS_ENABLED(CONFIG_I2C_COLDFIRE) */
+
static struct platform_device *mcf_devices[] __initdata = {
&mcf_uart,
#if IS_ENABLED(CONFIG_FEC)
@@ -338,6 +511,24 @@ static struct platform_device *mcf_devices[] __initdata = {
#if IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI)
&mcf_qspi,
#endif
+#if IS_ENABLED(CONFIG_I2C_COLDFIRE)
+ &mcf_i2c0,
+#ifdef MCFI2C_BASE1
+ &mcf_i2c1,
+#endif
+#ifdef MCFI2C_BASE2
+ &mcf_i2c2,
+#endif
+#ifdef MCFI2C_BASE3
+ &mcf_i2c3,
+#endif
+#ifdef MCFI2C_BASE4
+ &mcf_i2c4,
+#endif
+#ifdef MCFI2C_BASE5
+ &mcf_i2c5,
+#endif
+#endif
};
/*
diff --git a/arch/m68k/coldfire/m5307.c b/arch/m68k/coldfire/m5307.c
index 2da1d14..159485e 100644
--- a/arch/m68k/coldfire/m5307.c
+++ b/arch/m68k/coldfire/m5307.c
@@ -35,6 +35,7 @@ DEFINE_CLK(mcftmr0, "mcftmr.0", MCF_BUSCLK);
DEFINE_CLK(mcftmr1, "mcftmr.1", MCF_BUSCLK);
DEFINE_CLK(mcfuart0, "mcfuart.0", MCF_BUSCLK);
DEFINE_CLK(mcfuart1, "mcfuart.1", MCF_BUSCLK);
+DEFINE_CLK(mcfi2c0, "mcfi2c.0", MCF_BUSCLK);
struct clk *mcf_clks[] = {
&clk_pll,
@@ -43,9 +44,19 @@ struct clk *mcf_clks[] = {
&clk_mcftmr1,
&clk_mcfuart0,
&clk_mcfuart1,
+ &clk_mcfi2c0,
NULL
};
+static void __init m5307_i2c_init(void)
+{
+#if IS_ENABLED(CONFIG_I2C_COLDFIRE)
+ writeb(MCFSIM_ICR_AUTOVEC | MCFSIM_ICR_LEVEL5 | MCFSIM_ICR_PRI0,
+ MCFSIM_I2CICR);
+ mcf_mapirq2imr(MCF_IRQ_I2C0, MCFINTC_I2C);
+#endif /* IS_ENABLED(CONFIG_I2C_COLDFIRE) */
+}
+
/***************************************************************************/
void __init config_BSP(char *commandp, int size)
@@ -73,6 +84,7 @@ void __init config_BSP(char *commandp, int size)
*/
wdebug(MCFDEBUG_CSR, MCFDEBUG_CSR_PSTCLK);
#endif
+ m5307_i2c_init();
}
/***************************************************************************/
diff --git a/arch/m68k/include/asm/m5307sim.h b/arch/m68k/include/asm/m5307sim.h
index 5d0bb7e..608e04c 100644
--- a/arch/m68k/include/asm/m5307sim.h
+++ b/arch/m68k/include/asm/m5307sim.h
@@ -131,6 +131,12 @@
#define MCFGPIO_IRQ_MAX -1
#define MCFGPIO_IRQ_VECBASE -1
+/*
+ * I2C module.
+ */
+#define MCFI2C_BASE0 (MCF_MBAR + 0x280)
+#define MCFI2C_SIZE0 0x40
+
/* Definition offset address for CS2-7 -- old mask 5307 */
@@ -148,6 +154,7 @@
#define MCFSIM_SWDICR MCFSIM_ICR0 /* Watchdog timer ICR */
#define MCFSIM_TIMER1ICR MCFSIM_ICR1 /* Timer 1 ICR */
#define MCFSIM_TIMER2ICR MCFSIM_ICR2 /* Timer 2 ICR */
+#define MCFSIM_I2CICR MCFSIM_ICR3 /* I2C ICR */
#define MCFSIM_UART1ICR MCFSIM_ICR4 /* UART 1 ICR */
#define MCFSIM_UART2ICR MCFSIM_ICR5 /* UART 2 ICR */
#define MCFSIM_DMA0ICR MCFSIM_ICR6 /* DMA 0 ICR */
@@ -174,6 +181,7 @@
/*
* Define system peripheral IRQ usage.
*/
+#define MCF_IRQ_I2C0 29 /* I2C */
#define MCF_IRQ_TIMER 30 /* Timer0, Level 6 */
#define MCF_IRQ_PROFILER 31 /* Timer1, Level 7 */
#define MCF_IRQ_UART0 73 /* UART0 */
diff --git a/arch/m68k/include/asm/mcfi2c.h b/arch/m68k/include/asm/mcfi2c.h
new file mode 100644
index 0000000..a0c69e1
--- /dev/null
+++ b/arch/m68k/include/asm/mcfi2c.h
@@ -0,0 +1,29 @@
+/*
+ * Definitions for Coldfire I2C interface
+ */
+#ifndef mcfi2c_h
+#define mcfi2c_h
+
+#if defined(CONFIG_M5206) || defined(CONFIG_M5206e)
+#define MCFI2C_IOBASE (MCF_MBAR + 0x1e0)
+#elif defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x)
+#define MCFI2C_IOBASE (MCF_IPSBAR + 0x300)
+#elif defined(CONFIG_M5249) || defined(CONFIG_M5307) || defined(CONFIG_M5407)
+#define MCFI2C_IOBASE (MCF_MBAR + 0x280)
+#ifdef CONFIG_M5249
+#define MCFI2C_IOBASE2 (MCF_MBAR2 + 0x440)
+#endif
+#elif defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_M5445x)
+#define MCFI2C_IOBASE 0xFC058000
+#endif
+#define MCFI2C_IOSIZE 0x40
+
+/*
+ * struct mcfi2c_platform_data - platform data for the coldfire i2c driver
+ * @bitrate: bitrate to use for this i2c controller.
+ */
+struct mcfi2c_platform_data {
+ u32 bitrate;
+};
+
+#endif /* mcfi2c_h */
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 5c3993b..5c3e326 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -597,6 +597,18 @@ config I2C_IMX
This driver can also be built as a module. If so, the module
will be called i2c-imx.
+config I2C_COLDFIRE
+ tristate "Freescale Coldfire I2C driver"
+ depends on M5206 || M5206e || M520x || M523x || M5249 || \
+ M527x || M528x || M5307 || M532x || M5407
+ help
+ This driver supports the I2C interface availible on some Freescale
+ Coldfire processors (M520x, M523x, M5249, M5271, M5275, M528x,
+ M5307, M532x, M5407).
+
+ This driver can be built as a module. If so, the module
+ will be called i2c-mcf.
+
config I2C_IOP3XX
tristate "Intel IOPx3xx and IXP4xx on-chip I2C interface"
depends on ARCH_IOP32X || ARCH_IOP33X || ARCH_IXP4XX || ARCH_IOP13XX
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 37f2819..17ad0c8 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -96,6 +96,7 @@ obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o
obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
+obj-$(CONFIG_I2C_COLDFIRE) += i2c-mcf.o
# External I2C/SMBus adapter drivers
obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
diff --git a/drivers/i2c/busses/i2c-mcf.c b/drivers/i2c/busses/i2c-mcf.c
new file mode 100644
index 0000000..320531c
--- /dev/null
+++ b/drivers/i2c/busses/i2c-mcf.c
@@ -0,0 +1,463 @@
+/*
+ * Freescale/Motorola Coldfire I2C driver.
+ *
+ * Copyright 2010 Steven King <sfking@fdwdc.com>
+ *
+ * Updated by Angelo Dureghello <angelo@sysam.it> Sep 2016
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+
+#include <asm/mcfi2c.h>
+
+#define DRIVER_NAME "mcfi2c"
+
+#define MCFI2C_ADR 0x00
+#define MCFI2C_FDR 0x04
+#define MCFI2C_CR 0x08
+#define MCFI2C_CR_IEN 0x80
+#define MCFI2C_CR_IIEN 0x40
+#define MCFI2C_CR_MSTA 0x20
+#define MCFI2C_CR_MTX 0x10
+#define MCFI2C_CR_TXAK 0x08
+#define MCFI2C_CR_RSTA 0x04
+#define MCFI2C_DR 0x10
+#define MCFI2C_SR 0x0C
+#define MCFI2C_SR_ICF 0x80
+#define MCFI2C_SR_IAAS 0x40
+#define MCFI2C_SR_IBB 0x20
+#define MCFI2C_SR_IAL 0x10
+#define MCFI2C_SR_SRW 0x04
+#define MCFI2C_SR_IIF 0x02
+#define MCFI2C_SR_RXAK 0x01
+
+#define DEFAULT_I2C_BUS_SPEED 100000
+
+struct mcfi2c {
+ struct i2c_adapter adapter;
+ void __iomem *iobase;
+ int irq;
+ struct clk *clk;
+ struct completion completion;
+};
+
+static u8 mcfi2c_rd_cr(struct mcfi2c *mcfi2c)
+{
+ return readb(mcfi2c->iobase + MCFI2C_CR);
+}
+
+static void mcfi2c_wr_cr(struct mcfi2c *mcfi2c, u8 val)
+{
+ writeb(val, mcfi2c->iobase + MCFI2C_CR);
+}
+
+static u8 mcfi2c_rd_sr(struct mcfi2c *mcfi2c)
+{
+ return readb(mcfi2c->iobase + MCFI2C_SR);
+}
+
+static void mcfi2c_wr_sr(struct mcfi2c *mcfi2c, u8 val)
+{
+ writeb(val, mcfi2c->iobase + MCFI2C_SR);
+}
+
+static u8 mcfi2c_rd_dr(struct mcfi2c *mcfi2c)
+{
+ return readb(mcfi2c->iobase + MCFI2C_DR);
+}
+
+static void mcfi2c_wr_dr(struct mcfi2c *mcfi2c, u8 val)
+{
+ writeb(val, mcfi2c->iobase + MCFI2C_DR);
+}
+
+static void mcfi2c_start(struct mcfi2c *mcfi2c)
+{
+ mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_IIEN | MCFI2C_CR_MSTA |
+ MCFI2C_CR_MTX);
+}
+
+static void mcfi2c_repeat_start(struct mcfi2c *mcfi2c)
+{
+ mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_IIEN | MCFI2C_CR_MSTA |
+ MCFI2C_CR_MTX | MCFI2C_CR_RSTA);
+}
+
+static void mcfi2c_stop(struct mcfi2c *mcfi2c)
+{
+ mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN);
+}
+
+static void mcfi2c_tx_ack(struct mcfi2c *mcfi2c)
+{
+ mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_IIEN | MCFI2C_CR_MSTA);
+}
+
+static void mcfi2c_tx_nak(struct mcfi2c *mcfi2c)
+{
+ mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_IIEN | MCFI2C_CR_MSTA |
+ MCFI2C_CR_TXAK);
+}
+
+static irqreturn_t mcfi2c_irq_handler(int this_irq, void *dev_id)
+{
+ struct mcfi2c *mcfi2c = dev_id;
+
+ /* clear interrupt */
+ mcfi2c_wr_sr(mcfi2c, 0);
+ complete(&mcfi2c->completion);
+
+ return IRQ_HANDLED;
+}
+
+static void mcfi2c_reset(struct mcfi2c *mcfi2c)
+{
+ mcfi2c_wr_cr(mcfi2c, 0);
+ mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_MSTA);
+ mcfi2c_rd_dr(mcfi2c);
+ mcfi2c_wr_sr(mcfi2c, 0);
+ mcfi2c_wr_cr(mcfi2c, 0);
+ mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN);
+}
+
+static void mcfi2c_wait_for_bus_idle(struct mcfi2c *mcfi2c)
+{
+ if (mcfi2c_rd_sr(mcfi2c) & MCFI2C_SR_IBB) {
+ unsigned long timeout = jiffies + HZ / 2;
+
+ do {
+ cond_resched();
+ if (time_after(jiffies, timeout)) {
+ mcfi2c_reset(mcfi2c);
+ break;
+ }
+ } while (mcfi2c_rd_sr(mcfi2c) & MCFI2C_SR_IBB);
+ }
+}
+
+static int mcfi2c_wait_for_bus_busy(struct mcfi2c *mcfi2c)
+{
+ u8 sr;
+
+ while (!((sr = mcfi2c_rd_sr(mcfi2c)) & MCFI2C_SR_IBB))
+ if (sr & MCFI2C_SR_IAL) {
+ mcfi2c_reset(mcfi2c);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int mcfi2c_xmit(struct mcfi2c *mcfi2c, u16 addr, u16 flags, u8 *buf,
+ u16 len, int timeout, int more)
+{
+ if (!(mcfi2c_rd_cr(mcfi2c) & MCFI2C_CR_MSTA)) {
+ mcfi2c_wait_for_bus_idle(mcfi2c);
+
+ reinit_completion(&mcfi2c->completion);
+ mcfi2c_start(mcfi2c);
+
+ if (mcfi2c_wait_for_bus_busy(mcfi2c))
+ return -EIO;
+ }
+
+ mcfi2c_wr_dr(mcfi2c, (addr << 1) | (flags & I2C_M_RD));
+
+ while (wait_for_completion_timeout(&mcfi2c->completion, timeout)) {
+ u8 sr = mcfi2c_rd_sr(mcfi2c);
+
+ if (sr & MCFI2C_SR_IAL) {
+ mcfi2c_wr_sr(mcfi2c, ~MCFI2C_SR_IAL);
+ return -EIO;
+ } else if (mcfi2c_rd_cr(mcfi2c) & MCFI2C_CR_MTX) {
+ if (sr & MCFI2C_SR_RXAK) {
+ mcfi2c_stop(mcfi2c);
+ return -EIO;
+ } else if (flags & I2C_M_RD) {
+ if (len > 1)
+ mcfi2c_tx_ack(mcfi2c);
+ else
+ mcfi2c_tx_nak(mcfi2c);
+ /* dummy read */
+ mcfi2c_rd_dr(mcfi2c);
+ } else if (len--) {
+ mcfi2c_wr_dr(mcfi2c, *buf++);
+ } else {
+ if (more)
+ mcfi2c_repeat_start(mcfi2c);
+ else
+ mcfi2c_stop(mcfi2c);
+ return 0;
+ }
+ } else if (--len) {
+ if (!(len > 1))
+ mcfi2c_tx_nak(mcfi2c);
+ *buf++ = mcfi2c_rd_dr(mcfi2c);
+ } else {
+ if (more)
+ mcfi2c_repeat_start(mcfi2c);
+ else
+ mcfi2c_stop(mcfi2c);
+ *buf++ = mcfi2c_rd_dr(mcfi2c);
+ return 0;
+ }
+ }
+
+ mcfi2c_stop(mcfi2c);
+
+ return -ETIMEDOUT;
+}
+
+static int mcfi2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
+ int num)
+{
+ struct mcfi2c *mcfi2c = i2c_get_adapdata(adapter);
+ int cnt = 0;
+ int status;
+ int retries;
+
+ while (num--) {
+ retries = adapter->retries;
+ if (msgs->flags & ~I2C_M_RD)
+ return -EINVAL;
+
+ do {
+ status = mcfi2c_xmit(mcfi2c, msgs->addr, msgs->flags,
+ msgs->buf, msgs->len,
+ adapter->timeout, num);
+ } while (status && retries--);
+
+ if (status)
+ return status;
+ ++cnt;
+ ++msgs;
+ }
+
+ return cnt;
+}
+
+static u32 mcfi2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm mcfi2c_algo = {
+ .master_xfer = mcfi2c_xfer,
+ .functionality = mcfi2c_func,
+};
+
+static const u16 mcfi2c_fdr[] = {
+ 28, 30, 34, 40, 44, 48, 56, 68,
+ 80, 88, 104, 128, 144, 160, 192, 240,
+ 288, 320, 384, 480, 576, 640, 768, 960,
+ 1152, 1280, 1536, 1920, 2304, 2560, 3072, 3840,
+ 20, 22, 24, 26, 28, 32, 36, 40,
+ 48, 56, 64, 72, 80, 96, 112, 128,
+ 160, 192, 224, 256, 320, 384, 448, 512,
+ 640, 768, 896, 1024, 1280, 1536, 1792, 2048
+};
+
+static u8 mcfi2c_calc_fdr(struct mcfi2c *mcfi2c,
+ struct mcfi2c_platform_data *pdata)
+{
+ u32 bitrate = (pdata && pdata->bitrate) ?
+ pdata->bitrate : DEFAULT_I2C_BUS_SPEED;
+ int div = clk_get_rate(mcfi2c->clk)/bitrate;
+ int r = 0, i = 0;
+
+ do {
+ if (abs(mcfi2c_fdr[i] - div) < abs(mcfi2c_fdr[r] - div))
+ r = i;
+ } while (++i < ARRAY_SIZE(mcfi2c_fdr));
+
+ return r;
+}
+
+static int mcfi2c_probe(struct platform_device *pdev)
+{
+ struct mcfi2c *mcfi2c;
+ struct resource *res;
+ int status;
+
+ mcfi2c = kzalloc(sizeof(*mcfi2c), GFP_KERNEL);
+ if (!mcfi2c)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_dbg(&pdev->dev, "platform_get_resource failed\n");
+ status = -ENXIO;
+ goto fail0;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
+ dev_dbg(&pdev->dev, "request_mem_region failed\n");
+ status = -EBUSY;
+ goto fail0;
+ }
+
+ mcfi2c->iobase = ioremap(res->start, resource_size(res));
+ if (!mcfi2c->iobase) {
+ dev_dbg(&pdev->dev, "ioremap failed\n");
+ status = -ENOMEM;
+ goto fail1;
+ }
+
+ mcfi2c->irq = platform_get_irq(pdev, 0);
+ if (mcfi2c->irq < 0) {
+ dev_dbg(&pdev->dev, "platform_get_irq failed\n");
+ status = -ENXIO;
+ goto fail2;
+ }
+ status = request_irq(mcfi2c->irq, mcfi2c_irq_handler, 0,
+ pdev->name, mcfi2c);
+ if (status) {
+ dev_dbg(&pdev->dev, "request_irq failed\n");
+ goto fail2;
+ }
+
+ mcfi2c->clk = clk_get(&pdev->dev, "i2c_clk");
+ if (IS_ERR(mcfi2c->clk)) {
+ dev_dbg(&pdev->dev, "clk_get failed\n");
+ status = PTR_ERR(mcfi2c->clk);
+ goto fail3;
+ }
+ clk_enable(mcfi2c->clk);
+
+ platform_set_drvdata(pdev, mcfi2c);
+
+ init_completion(&mcfi2c->completion);
+
+ writeb(mcfi2c_calc_fdr(mcfi2c, pdev->dev.platform_data),
+ mcfi2c->iobase + MCFI2C_FDR);
+
+ writeb(0x00, mcfi2c->iobase + MCFI2C_ADR);
+
+ mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN);
+
+ /* if the bus busy (IBB) is set, reset the controller */
+ if (mcfi2c_rd_sr(mcfi2c) & MCFI2C_SR_IBB)
+ mcfi2c_reset(mcfi2c);
+
+ mcfi2c->adapter.algo = &mcfi2c_algo;
+ mcfi2c->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+ mcfi2c->adapter.dev.parent = &pdev->dev;
+ mcfi2c->adapter.nr = pdev->id;
+ mcfi2c->adapter.retries = 2;
+ snprintf(mcfi2c->adapter.name, sizeof(mcfi2c->adapter.name),
+ DRIVER_NAME ".%d", pdev->id);
+
+ i2c_set_adapdata(&mcfi2c->adapter, mcfi2c);
+
+ status = i2c_add_numbered_adapter(&mcfi2c->adapter);
+ if (status < 0) {
+ dev_dbg(&pdev->dev, "i2c_add_numbered_adapter failed\n");
+ goto fail4;
+ }
+ dev_info(&pdev->dev, "Coldfire I2C bus driver\n");
+
+ return 0;
+
+fail4:
+ clk_disable(mcfi2c->clk);
+ clk_put(mcfi2c->clk);
+fail3:
+ free_irq(mcfi2c->irq, mcfi2c);
+fail2:
+ iounmap(mcfi2c->iobase);
+fail1:
+ release_mem_region(res->start, resource_size(res));
+fail0:
+ kfree(mcfi2c);
+
+ return status;
+}
+
+static int mcfi2c_remove(struct platform_device *pdev)
+{
+ struct mcfi2c *mcfi2c = platform_get_drvdata(pdev);
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ /* disable the hardware */
+ mcfi2c_wr_cr(mcfi2c, 0);
+
+ platform_set_drvdata(pdev, NULL);
+ i2c_del_adapter(&mcfi2c->adapter);
+ clk_disable(mcfi2c->clk);
+ clk_put(mcfi2c->clk);
+ free_irq(mcfi2c->irq, mcfi2c);
+ iounmap(mcfi2c->iobase);
+ release_mem_region(res->start, resource_size(res));
+ kfree(mcfi2c);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int mcfi2c_suspend(struct device *dev)
+{
+ struct mcfi2c *mcfi2c = platform_get_drvdata(to_platform_device(dev));
+
+ mcfi2c_wr_cr(mcfi2c, 0);
+ clk_disable(mcfi2c->clk);
+
+ return 0;
+}
+
+static int mcfi2c_resume(struct device *dev)
+{
+ struct mcfi2c *mcfi2c = platform_get_drvdata(to_platform_device(dev));
+
+ clk_enable(mcfi2c->clk);
+ mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN);
+
+ return 0;
+}
+
+static const struct dev_pm_ops mcfi2c_dev_pm_ops = {
+ .suspend = mcfi2c_suspend,
+ .resume = mcfi2c_resume,
+};
+
+#define MCFI2C_DEV_PM_OPS (&mcfi2c_dev_pm_ops)
+#else
+#define MCFI2C_DEV_PM_OPS NULL
+#endif
+
+static struct platform_driver mcfi2c_driver = {
+ .driver.name = DRIVER_NAME,
+ .driver.owner = THIS_MODULE,
+ .driver.pm = MCFI2C_DEV_PM_OPS,
+ .remove = mcfi2c_remove,
+};
+
+static int __init mcfi2c_init(void)
+{
+ return platform_driver_probe(&mcfi2c_driver, mcfi2c_probe);
+}
+module_init(mcfi2c_init);
+
+static void __exit mcfi2c_exit(void)
+{
+ platform_driver_unregister(&mcfi2c_driver);
+}
+module_exit(mcfi2c_exit);
+
+MODULE_AUTHOR("Steven King <sfking@fdwdc.com>");
+MODULE_DESCRIPTION("I2C-Bus support for Freescale Coldfire processors");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
+
--
2.8.1
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH 2/2] m68k: add coldfire mcf5307 i2c support
2016-10-02 0:18 [PATCH 2/2] m68k: add coldfire mcf5307 i2c support Angelo Dureghello
@ 2016-10-05 11:47 ` Greg Ungerer
2016-10-05 12:52 ` Angelo Dureghello
0 siblings, 1 reply; 3+ messages in thread
From: Greg Ungerer @ 2016-10-05 11:47 UTC (permalink / raw)
To: Angelo Dureghello, linux-m68k
Hi Angelo,
On 02/10/16 10:18, Angelo Dureghello wrote:
> Add Steven King i2c driver for Coldfire, and reviewed to build
> and run properly with the actual kernel version.
This won't be able to be merged in this form.
I can take the m68k/coldfire specific parts - but the i2c
driver would need to go via the i2c maintainers (so you
would need a patch to be sent and reveiwed on the i2c
email list).
I would prefer to merge the existing ColdFire i2c platform
support I have for all platforms in any case.
Regards
Greg
> Signed-off-by: Angelo Dureghello <angelo@sysam.it>
> ---
> arch/m68k/coldfire/device.c | 191 ++++++++++++++++
> arch/m68k/coldfire/m5307.c | 12 +
> arch/m68k/include/asm/m5307sim.h | 8 +
> arch/m68k/include/asm/mcfi2c.h | 29 +++
> drivers/i2c/busses/Kconfig | 12 +
> drivers/i2c/busses/Makefile | 1 +
> drivers/i2c/busses/i2c-mcf.c | 463 +++++++++++++++++++++++++++++++++++++++
> 7 files changed, 716 insertions(+)
> create mode 100644 arch/m68k/include/asm/mcfi2c.h
> create mode 100644 drivers/i2c/busses/i2c-mcf.c
>
> diff --git a/arch/m68k/coldfire/device.c b/arch/m68k/coldfire/device.c
> index a0fc0c1..a1ed057 100644
> --- a/arch/m68k/coldfire/device.c
> +++ b/arch/m68k/coldfire/device.c
> @@ -19,6 +19,7 @@
> #include <asm/mcfsim.h>
> #include <asm/mcfuart.h>
> #include <asm/mcfqspi.h>
> +#include <asm/mcfi2c.h>
>
> /*
> * All current ColdFire parts contain from 2, 3, 4 or 10 UARTS.
> @@ -327,6 +328,178 @@ static struct platform_device mcf_qspi = {
> };
> #endif /* IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) */
>
> +#if IS_ENABLED(CONFIG_I2C_COLDFIRE)
> +static struct resource mcf_i2c0_resources[] = {
> + {
> + .start = MCFI2C_BASE0,
> + .end = MCFI2C_BASE0 + MCFI2C_SIZE0 - 1,
> + .flags = IORESOURCE_MEM,
> + },
> + {
> + .start = MCF_IRQ_I2C0,
> + .end = MCF_IRQ_I2C0,
> + .flags = IORESOURCE_IRQ,
> + },
> +};
> +
> +static struct mcfi2c_platform_data mcf_i2c0_platform_data = {
> + .bitrate = 100000,
> +};
> +
> +static struct platform_device mcf_i2c0 = {
> + .name = "mcfi2c",
> + .id = 0,
> + .num_resources = ARRAY_SIZE(mcf_i2c0_resources),
> + .resource = mcf_i2c0_resources,
> + .dev.platform_data = &mcf_i2c0_platform_data,
> +};
> +#ifdef MCFI2C_BASE1
> +
> +static struct resource mcf_i2c1_resources[] = {
> + {
> + .start = MCFI2C_BASE1,
> + .end = MCFI2C_BASE1 + MCFI2C_SIZE1 - 1,
> + .flags = IORESOURCE_MEM,
> + },
> + {
> + .start = MCF_IRQ_I2C1,
> + .end = MCF_IRQ_I2C1,
> + .flags = IORESOURCE_IRQ,
> + },
> +};
> +
> +static struct mcfi2c_platform_data mcf_i2c1_platform_data = {
> + .bitrate = 100000,
> +};
> +
> +static struct platform_device mcf_i2c1 = {
> + .name = "mcfi2c",
> + .id = 1,
> + .num_resources = ARRAY_SIZE(mcf_i2c1_resources),
> + .resource = mcf_i2c1_resources,
> + .dev.platform_data = &mcf_i2c1_platform_data,
> +};
> +
> +#endif /* MCFI2C_BASE1 */
> +
> +#ifdef MCFI2C_BASE2
> +
> +static struct resource mcf_i2c2_resources[] = {
> + {
> + .start = MCFI2C_BASE2,
> + .end = MCFI2C_BASE2 + MCFI2C_SIZE2 - 1,
> + .flags = IORESOURCE_MEM,
> + },
> + {
> + .start = MCF_IRQ_I2C2,
> + .end = MCF_IRQ_I2C2,
> + .flags = IORESOURCE_IRQ,
> + },
> +};
> +
> +static struct mcfi2c_platform_data mcf_i2c2_platform_data = {
> + .bitrate = 100000,
> +};
> +
> +static struct platform_device mcf_i2c2 = {
> + .name = "mcfi2c",
> + .id = 2,
> + .num_resources = ARRAY_SIZE(mcf_i2c2_resources),
> + .resource = mcf_i2c2_resources,
> + .dev.platform_data = &mcf_i2c2_platform_data,
> +};
> +
> +#endif /* MCFI2C_BASE2 */
> +
> +#ifdef MCFI2C_BASE3
> +
> +static struct resource mcf_i2c3_resources[] = {
> + {
> + .start = MCFI2C_BASE3,
> + .end = MCFI2C_BASE3 + MCFI2C_SIZE3 - 1,
> + .flags = IORESOURCE_MEM,
> + },
> + {
> + .start = MCF_IRQ_I2C3,
> + .end = MCF_IRQ_I2C3,
> + .flags = IORESOURCE_IRQ,
> + },
> +};
> +
> +static struct mcfi2c_platform_data mcf_i2c3_platform_data = {
> + .bitrate = 100000,
> +};
> +
> +static struct platform_device mcf_i2c3 = {
> + .name = "mcfi2c",
> + .id = 3,
> + .num_resources = ARRAY_SIZE(mcf_i2c3_resources),
> + .resource = mcf_i2c3_resources,
> + .dev.platform_data = &mcf_i2c3_platform_data,
> +};
> +
> +#endif /* MCFI2C_BASE3 */
> +
> +#ifdef MCFI2C_BASE4
> +
> +static struct resource mcf_i2c4_resources[] = {
> + {
> + .start = MCFI2C_BASE4,
> + .end = MCFI2C_BASE4 + MCFI2C_SIZE4 - 1,
> + .flags = IORESOURCE_MEM,
> + },
> + {
> + .start = MCF_IRQ_I2C4,
> + .end = MCF_IRQ_I2C4,
> + .flags = IORESOURCE_IRQ,
> + },
> +};
> +
> +static struct mcfi2c_platform_data mcf_i2c4_platform_data = {
> + .bitrate = 100000,
> +};
> +
> +static struct platform_device mcf_i2c4 = {
> + .name = "mcfi2c",
> + .id = 4,
> + .num_resources = ARRAY_SIZE(mcf_i2c4_resources),
> + .resource = mcf_i2c4_resources,
> + .dev.platform_data = &mcf_i2c4_platform_data,
> +};
> +
> +#endif /* MCFI2C_BASE4 */
> +
> +#ifdef MCFI2C_BASE5
> +
> +static struct resource mcf_i2c5_resources[] = {
> + {
> + .start = MCFI2C_BASE5,
> + .end = MCFI2C_BASE5 + MCFI2C_SIZE5 - 1,
> + .flags = IORESOURCE_MEM,
> + },
> + {
> + .start = MCF_IRQ_I2C5,
> + .end = MCF_IRQ_I2C5,
> + .flags = IORESOURCE_IRQ,
> + },
> +};
> +
> +static struct mcfi2c_platform_data mcf_i2c5_platform_data = {
> + .bitrate = 100000,
> +};
> +
> +static struct platform_device mcf_i2c5 = {
> + .name = "mcfi2c",
> + .id = 5,
> + .num_resources = ARRAY_SIZE(mcf_i2c5_resources),
> + .resource = mcf_i2c5_resources,
> + .dev.platform_data = &mcf_i2c5_platform_data,
> +};
> +
> +#endif /* MCFI2C_BASE5 */
> +
> +#endif /* IS_ENABLED(CONFIG_I2C_COLDFIRE) */
> +
> static struct platform_device *mcf_devices[] __initdata = {
> &mcf_uart,
> #if IS_ENABLED(CONFIG_FEC)
> @@ -338,6 +511,24 @@ static struct platform_device *mcf_devices[] __initdata = {
> #if IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI)
> &mcf_qspi,
> #endif
> +#if IS_ENABLED(CONFIG_I2C_COLDFIRE)
> + &mcf_i2c0,
> +#ifdef MCFI2C_BASE1
> + &mcf_i2c1,
> +#endif
> +#ifdef MCFI2C_BASE2
> + &mcf_i2c2,
> +#endif
> +#ifdef MCFI2C_BASE3
> + &mcf_i2c3,
> +#endif
> +#ifdef MCFI2C_BASE4
> + &mcf_i2c4,
> +#endif
> +#ifdef MCFI2C_BASE5
> + &mcf_i2c5,
> +#endif
> +#endif
> };
>
> /*
> diff --git a/arch/m68k/coldfire/m5307.c b/arch/m68k/coldfire/m5307.c
> index 2da1d14..159485e 100644
> --- a/arch/m68k/coldfire/m5307.c
> +++ b/arch/m68k/coldfire/m5307.c
> @@ -35,6 +35,7 @@ DEFINE_CLK(mcftmr0, "mcftmr.0", MCF_BUSCLK);
> DEFINE_CLK(mcftmr1, "mcftmr.1", MCF_BUSCLK);
> DEFINE_CLK(mcfuart0, "mcfuart.0", MCF_BUSCLK);
> DEFINE_CLK(mcfuart1, "mcfuart.1", MCF_BUSCLK);
> +DEFINE_CLK(mcfi2c0, "mcfi2c.0", MCF_BUSCLK);
>
> struct clk *mcf_clks[] = {
> &clk_pll,
> @@ -43,9 +44,19 @@ struct clk *mcf_clks[] = {
> &clk_mcftmr1,
> &clk_mcfuart0,
> &clk_mcfuart1,
> + &clk_mcfi2c0,
> NULL
> };
>
> +static void __init m5307_i2c_init(void)
> +{
> +#if IS_ENABLED(CONFIG_I2C_COLDFIRE)
> + writeb(MCFSIM_ICR_AUTOVEC | MCFSIM_ICR_LEVEL5 | MCFSIM_ICR_PRI0,
> + MCFSIM_I2CICR);
> + mcf_mapirq2imr(MCF_IRQ_I2C0, MCFINTC_I2C);
> +#endif /* IS_ENABLED(CONFIG_I2C_COLDFIRE) */
> +}
> +
> /***************************************************************************/
>
> void __init config_BSP(char *commandp, int size)
> @@ -73,6 +84,7 @@ void __init config_BSP(char *commandp, int size)
> */
> wdebug(MCFDEBUG_CSR, MCFDEBUG_CSR_PSTCLK);
> #endif
> + m5307_i2c_init();
> }
>
> /***************************************************************************/
> diff --git a/arch/m68k/include/asm/m5307sim.h b/arch/m68k/include/asm/m5307sim.h
> index 5d0bb7e..608e04c 100644
> --- a/arch/m68k/include/asm/m5307sim.h
> +++ b/arch/m68k/include/asm/m5307sim.h
> @@ -131,6 +131,12 @@
> #define MCFGPIO_IRQ_MAX -1
> #define MCFGPIO_IRQ_VECBASE -1
>
> +/*
> + * I2C module.
> + */
> +#define MCFI2C_BASE0 (MCF_MBAR + 0x280)
> +#define MCFI2C_SIZE0 0x40
> +
>
> /* Definition offset address for CS2-7 -- old mask 5307 */
>
> @@ -148,6 +154,7 @@
> #define MCFSIM_SWDICR MCFSIM_ICR0 /* Watchdog timer ICR */
> #define MCFSIM_TIMER1ICR MCFSIM_ICR1 /* Timer 1 ICR */
> #define MCFSIM_TIMER2ICR MCFSIM_ICR2 /* Timer 2 ICR */
> +#define MCFSIM_I2CICR MCFSIM_ICR3 /* I2C ICR */
> #define MCFSIM_UART1ICR MCFSIM_ICR4 /* UART 1 ICR */
> #define MCFSIM_UART2ICR MCFSIM_ICR5 /* UART 2 ICR */
> #define MCFSIM_DMA0ICR MCFSIM_ICR6 /* DMA 0 ICR */
> @@ -174,6 +181,7 @@
> /*
> * Define system peripheral IRQ usage.
> */
> +#define MCF_IRQ_I2C0 29 /* I2C */
> #define MCF_IRQ_TIMER 30 /* Timer0, Level 6 */
> #define MCF_IRQ_PROFILER 31 /* Timer1, Level 7 */
> #define MCF_IRQ_UART0 73 /* UART0 */
> diff --git a/arch/m68k/include/asm/mcfi2c.h b/arch/m68k/include/asm/mcfi2c.h
> new file mode 100644
> index 0000000..a0c69e1
> --- /dev/null
> +++ b/arch/m68k/include/asm/mcfi2c.h
> @@ -0,0 +1,29 @@
> +/*
> + * Definitions for Coldfire I2C interface
> + */
> +#ifndef mcfi2c_h
> +#define mcfi2c_h
> +
> +#if defined(CONFIG_M5206) || defined(CONFIG_M5206e)
> +#define MCFI2C_IOBASE (MCF_MBAR + 0x1e0)
> +#elif defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x)
> +#define MCFI2C_IOBASE (MCF_IPSBAR + 0x300)
> +#elif defined(CONFIG_M5249) || defined(CONFIG_M5307) || defined(CONFIG_M5407)
> +#define MCFI2C_IOBASE (MCF_MBAR + 0x280)
> +#ifdef CONFIG_M5249
> +#define MCFI2C_IOBASE2 (MCF_MBAR2 + 0x440)
> +#endif
> +#elif defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_M5445x)
> +#define MCFI2C_IOBASE 0xFC058000
> +#endif
> +#define MCFI2C_IOSIZE 0x40
> +
> +/*
> + * struct mcfi2c_platform_data - platform data for the coldfire i2c driver
> + * @bitrate: bitrate to use for this i2c controller.
> + */
> +struct mcfi2c_platform_data {
> + u32 bitrate;
> +};
> +
> +#endif /* mcfi2c_h */
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index 5c3993b..5c3e326 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -597,6 +597,18 @@ config I2C_IMX
> This driver can also be built as a module. If so, the module
> will be called i2c-imx.
>
> +config I2C_COLDFIRE
> + tristate "Freescale Coldfire I2C driver"
> + depends on M5206 || M5206e || M520x || M523x || M5249 || \
> + M527x || M528x || M5307 || M532x || M5407
> + help
> + This driver supports the I2C interface availible on some Freescale
> + Coldfire processors (M520x, M523x, M5249, M5271, M5275, M528x,
> + M5307, M532x, M5407).
> +
> + This driver can be built as a module. If so, the module
> + will be called i2c-mcf.
> +
> config I2C_IOP3XX
> tristate "Intel IOPx3xx and IXP4xx on-chip I2C interface"
> depends on ARCH_IOP32X || ARCH_IOP33X || ARCH_IXP4XX || ARCH_IOP13XX
> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> index 37f2819..17ad0c8 100644
> --- a/drivers/i2c/busses/Makefile
> +++ b/drivers/i2c/busses/Makefile
> @@ -96,6 +96,7 @@ obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
> obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
> obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o
> obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
> +obj-$(CONFIG_I2C_COLDFIRE) += i2c-mcf.o
>
> # External I2C/SMBus adapter drivers
> obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
> diff --git a/drivers/i2c/busses/i2c-mcf.c b/drivers/i2c/busses/i2c-mcf.c
> new file mode 100644
> index 0000000..320531c
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-mcf.c
> @@ -0,0 +1,463 @@
> +/*
> + * Freescale/Motorola Coldfire I2C driver.
> + *
> + * Copyright 2010 Steven King <sfking@fdwdc.com>
> + *
> + * Updated by Angelo Dureghello <angelo@sysam.it> Sep 2016
> + *
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License. See the file COPYING in the main directory of this archive
> + * for more details.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/errno.h>
> +#include <linux/platform_device.h>
> +#include <linux/io.h>
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +#include <linux/i2c.h>
> +#include <linux/slab.h>
> +
> +#include <asm/mcfi2c.h>
> +
> +#define DRIVER_NAME "mcfi2c"
> +
> +#define MCFI2C_ADR 0x00
> +#define MCFI2C_FDR 0x04
> +#define MCFI2C_CR 0x08
> +#define MCFI2C_CR_IEN 0x80
> +#define MCFI2C_CR_IIEN 0x40
> +#define MCFI2C_CR_MSTA 0x20
> +#define MCFI2C_CR_MTX 0x10
> +#define MCFI2C_CR_TXAK 0x08
> +#define MCFI2C_CR_RSTA 0x04
> +#define MCFI2C_DR 0x10
> +#define MCFI2C_SR 0x0C
> +#define MCFI2C_SR_ICF 0x80
> +#define MCFI2C_SR_IAAS 0x40
> +#define MCFI2C_SR_IBB 0x20
> +#define MCFI2C_SR_IAL 0x10
> +#define MCFI2C_SR_SRW 0x04
> +#define MCFI2C_SR_IIF 0x02
> +#define MCFI2C_SR_RXAK 0x01
> +
> +#define DEFAULT_I2C_BUS_SPEED 100000
> +
> +struct mcfi2c {
> + struct i2c_adapter adapter;
> + void __iomem *iobase;
> + int irq;
> + struct clk *clk;
> + struct completion completion;
> +};
> +
> +static u8 mcfi2c_rd_cr(struct mcfi2c *mcfi2c)
> +{
> + return readb(mcfi2c->iobase + MCFI2C_CR);
> +}
> +
> +static void mcfi2c_wr_cr(struct mcfi2c *mcfi2c, u8 val)
> +{
> + writeb(val, mcfi2c->iobase + MCFI2C_CR);
> +}
> +
> +static u8 mcfi2c_rd_sr(struct mcfi2c *mcfi2c)
> +{
> + return readb(mcfi2c->iobase + MCFI2C_SR);
> +}
> +
> +static void mcfi2c_wr_sr(struct mcfi2c *mcfi2c, u8 val)
> +{
> + writeb(val, mcfi2c->iobase + MCFI2C_SR);
> +}
> +
> +static u8 mcfi2c_rd_dr(struct mcfi2c *mcfi2c)
> +{
> + return readb(mcfi2c->iobase + MCFI2C_DR);
> +}
> +
> +static void mcfi2c_wr_dr(struct mcfi2c *mcfi2c, u8 val)
> +{
> + writeb(val, mcfi2c->iobase + MCFI2C_DR);
> +}
> +
> +static void mcfi2c_start(struct mcfi2c *mcfi2c)
> +{
> + mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_IIEN | MCFI2C_CR_MSTA |
> + MCFI2C_CR_MTX);
> +}
> +
> +static void mcfi2c_repeat_start(struct mcfi2c *mcfi2c)
> +{
> + mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_IIEN | MCFI2C_CR_MSTA |
> + MCFI2C_CR_MTX | MCFI2C_CR_RSTA);
> +}
> +
> +static void mcfi2c_stop(struct mcfi2c *mcfi2c)
> +{
> + mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN);
> +}
> +
> +static void mcfi2c_tx_ack(struct mcfi2c *mcfi2c)
> +{
> + mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_IIEN | MCFI2C_CR_MSTA);
> +}
> +
> +static void mcfi2c_tx_nak(struct mcfi2c *mcfi2c)
> +{
> + mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_IIEN | MCFI2C_CR_MSTA |
> + MCFI2C_CR_TXAK);
> +}
> +
> +static irqreturn_t mcfi2c_irq_handler(int this_irq, void *dev_id)
> +{
> + struct mcfi2c *mcfi2c = dev_id;
> +
> + /* clear interrupt */
> + mcfi2c_wr_sr(mcfi2c, 0);
> + complete(&mcfi2c->completion);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static void mcfi2c_reset(struct mcfi2c *mcfi2c)
> +{
> + mcfi2c_wr_cr(mcfi2c, 0);
> + mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_MSTA);
> + mcfi2c_rd_dr(mcfi2c);
> + mcfi2c_wr_sr(mcfi2c, 0);
> + mcfi2c_wr_cr(mcfi2c, 0);
> + mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN);
> +}
> +
> +static void mcfi2c_wait_for_bus_idle(struct mcfi2c *mcfi2c)
> +{
> + if (mcfi2c_rd_sr(mcfi2c) & MCFI2C_SR_IBB) {
> + unsigned long timeout = jiffies + HZ / 2;
> +
> + do {
> + cond_resched();
> + if (time_after(jiffies, timeout)) {
> + mcfi2c_reset(mcfi2c);
> + break;
> + }
> + } while (mcfi2c_rd_sr(mcfi2c) & MCFI2C_SR_IBB);
> + }
> +}
> +
> +static int mcfi2c_wait_for_bus_busy(struct mcfi2c *mcfi2c)
> +{
> + u8 sr;
> +
> + while (!((sr = mcfi2c_rd_sr(mcfi2c)) & MCFI2C_SR_IBB))
> + if (sr & MCFI2C_SR_IAL) {
> + mcfi2c_reset(mcfi2c);
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +
> +static int mcfi2c_xmit(struct mcfi2c *mcfi2c, u16 addr, u16 flags, u8 *buf,
> + u16 len, int timeout, int more)
> +{
> + if (!(mcfi2c_rd_cr(mcfi2c) & MCFI2C_CR_MSTA)) {
> + mcfi2c_wait_for_bus_idle(mcfi2c);
> +
> + reinit_completion(&mcfi2c->completion);
> + mcfi2c_start(mcfi2c);
> +
> + if (mcfi2c_wait_for_bus_busy(mcfi2c))
> + return -EIO;
> + }
> +
> + mcfi2c_wr_dr(mcfi2c, (addr << 1) | (flags & I2C_M_RD));
> +
> + while (wait_for_completion_timeout(&mcfi2c->completion, timeout)) {
> + u8 sr = mcfi2c_rd_sr(mcfi2c);
> +
> + if (sr & MCFI2C_SR_IAL) {
> + mcfi2c_wr_sr(mcfi2c, ~MCFI2C_SR_IAL);
> + return -EIO;
> + } else if (mcfi2c_rd_cr(mcfi2c) & MCFI2C_CR_MTX) {
> + if (sr & MCFI2C_SR_RXAK) {
> + mcfi2c_stop(mcfi2c);
> + return -EIO;
> + } else if (flags & I2C_M_RD) {
> + if (len > 1)
> + mcfi2c_tx_ack(mcfi2c);
> + else
> + mcfi2c_tx_nak(mcfi2c);
> + /* dummy read */
> + mcfi2c_rd_dr(mcfi2c);
> + } else if (len--) {
> + mcfi2c_wr_dr(mcfi2c, *buf++);
> + } else {
> + if (more)
> + mcfi2c_repeat_start(mcfi2c);
> + else
> + mcfi2c_stop(mcfi2c);
> + return 0;
> + }
> + } else if (--len) {
> + if (!(len > 1))
> + mcfi2c_tx_nak(mcfi2c);
> + *buf++ = mcfi2c_rd_dr(mcfi2c);
> + } else {
> + if (more)
> + mcfi2c_repeat_start(mcfi2c);
> + else
> + mcfi2c_stop(mcfi2c);
> + *buf++ = mcfi2c_rd_dr(mcfi2c);
> + return 0;
> + }
> + }
> +
> + mcfi2c_stop(mcfi2c);
> +
> + return -ETIMEDOUT;
> +}
> +
> +static int mcfi2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
> + int num)
> +{
> + struct mcfi2c *mcfi2c = i2c_get_adapdata(adapter);
> + int cnt = 0;
> + int status;
> + int retries;
> +
> + while (num--) {
> + retries = adapter->retries;
> + if (msgs->flags & ~I2C_M_RD)
> + return -EINVAL;
> +
> + do {
> + status = mcfi2c_xmit(mcfi2c, msgs->addr, msgs->flags,
> + msgs->buf, msgs->len,
> + adapter->timeout, num);
> + } while (status && retries--);
> +
> + if (status)
> + return status;
> + ++cnt;
> + ++msgs;
> + }
> +
> + return cnt;
> +}
> +
> +static u32 mcfi2c_func(struct i2c_adapter *adapter)
> +{
> + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> +}
> +
> +static const struct i2c_algorithm mcfi2c_algo = {
> + .master_xfer = mcfi2c_xfer,
> + .functionality = mcfi2c_func,
> +};
> +
> +static const u16 mcfi2c_fdr[] = {
> + 28, 30, 34, 40, 44, 48, 56, 68,
> + 80, 88, 104, 128, 144, 160, 192, 240,
> + 288, 320, 384, 480, 576, 640, 768, 960,
> + 1152, 1280, 1536, 1920, 2304, 2560, 3072, 3840,
> + 20, 22, 24, 26, 28, 32, 36, 40,
> + 48, 56, 64, 72, 80, 96, 112, 128,
> + 160, 192, 224, 256, 320, 384, 448, 512,
> + 640, 768, 896, 1024, 1280, 1536, 1792, 2048
> +};
> +
> +static u8 mcfi2c_calc_fdr(struct mcfi2c *mcfi2c,
> + struct mcfi2c_platform_data *pdata)
> +{
> + u32 bitrate = (pdata && pdata->bitrate) ?
> + pdata->bitrate : DEFAULT_I2C_BUS_SPEED;
> + int div = clk_get_rate(mcfi2c->clk)/bitrate;
> + int r = 0, i = 0;
> +
> + do {
> + if (abs(mcfi2c_fdr[i] - div) < abs(mcfi2c_fdr[r] - div))
> + r = i;
> + } while (++i < ARRAY_SIZE(mcfi2c_fdr));
> +
> + return r;
> +}
> +
> +static int mcfi2c_probe(struct platform_device *pdev)
> +{
> + struct mcfi2c *mcfi2c;
> + struct resource *res;
> + int status;
> +
> + mcfi2c = kzalloc(sizeof(*mcfi2c), GFP_KERNEL);
> + if (!mcfi2c)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res) {
> + dev_dbg(&pdev->dev, "platform_get_resource failed\n");
> + status = -ENXIO;
> + goto fail0;
> + }
> +
> + if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
> + dev_dbg(&pdev->dev, "request_mem_region failed\n");
> + status = -EBUSY;
> + goto fail0;
> + }
> +
> + mcfi2c->iobase = ioremap(res->start, resource_size(res));
> + if (!mcfi2c->iobase) {
> + dev_dbg(&pdev->dev, "ioremap failed\n");
> + status = -ENOMEM;
> + goto fail1;
> + }
> +
> + mcfi2c->irq = platform_get_irq(pdev, 0);
> + if (mcfi2c->irq < 0) {
> + dev_dbg(&pdev->dev, "platform_get_irq failed\n");
> + status = -ENXIO;
> + goto fail2;
> + }
> + status = request_irq(mcfi2c->irq, mcfi2c_irq_handler, 0,
> + pdev->name, mcfi2c);
> + if (status) {
> + dev_dbg(&pdev->dev, "request_irq failed\n");
> + goto fail2;
> + }
> +
> + mcfi2c->clk = clk_get(&pdev->dev, "i2c_clk");
> + if (IS_ERR(mcfi2c->clk)) {
> + dev_dbg(&pdev->dev, "clk_get failed\n");
> + status = PTR_ERR(mcfi2c->clk);
> + goto fail3;
> + }
> + clk_enable(mcfi2c->clk);
> +
> + platform_set_drvdata(pdev, mcfi2c);
> +
> + init_completion(&mcfi2c->completion);
> +
> + writeb(mcfi2c_calc_fdr(mcfi2c, pdev->dev.platform_data),
> + mcfi2c->iobase + MCFI2C_FDR);
> +
> + writeb(0x00, mcfi2c->iobase + MCFI2C_ADR);
> +
> + mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN);
> +
> + /* if the bus busy (IBB) is set, reset the controller */
> + if (mcfi2c_rd_sr(mcfi2c) & MCFI2C_SR_IBB)
> + mcfi2c_reset(mcfi2c);
> +
> + mcfi2c->adapter.algo = &mcfi2c_algo;
> + mcfi2c->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
> + mcfi2c->adapter.dev.parent = &pdev->dev;
> + mcfi2c->adapter.nr = pdev->id;
> + mcfi2c->adapter.retries = 2;
> + snprintf(mcfi2c->adapter.name, sizeof(mcfi2c->adapter.name),
> + DRIVER_NAME ".%d", pdev->id);
> +
> + i2c_set_adapdata(&mcfi2c->adapter, mcfi2c);
> +
> + status = i2c_add_numbered_adapter(&mcfi2c->adapter);
> + if (status < 0) {
> + dev_dbg(&pdev->dev, "i2c_add_numbered_adapter failed\n");
> + goto fail4;
> + }
> + dev_info(&pdev->dev, "Coldfire I2C bus driver\n");
> +
> + return 0;
> +
> +fail4:
> + clk_disable(mcfi2c->clk);
> + clk_put(mcfi2c->clk);
> +fail3:
> + free_irq(mcfi2c->irq, mcfi2c);
> +fail2:
> + iounmap(mcfi2c->iobase);
> +fail1:
> + release_mem_region(res->start, resource_size(res));
> +fail0:
> + kfree(mcfi2c);
> +
> + return status;
> +}
> +
> +static int mcfi2c_remove(struct platform_device *pdev)
> +{
> + struct mcfi2c *mcfi2c = platform_get_drvdata(pdev);
> + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +
> + /* disable the hardware */
> + mcfi2c_wr_cr(mcfi2c, 0);
> +
> + platform_set_drvdata(pdev, NULL);
> + i2c_del_adapter(&mcfi2c->adapter);
> + clk_disable(mcfi2c->clk);
> + clk_put(mcfi2c->clk);
> + free_irq(mcfi2c->irq, mcfi2c);
> + iounmap(mcfi2c->iobase);
> + release_mem_region(res->start, resource_size(res));
> + kfree(mcfi2c);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int mcfi2c_suspend(struct device *dev)
> +{
> + struct mcfi2c *mcfi2c = platform_get_drvdata(to_platform_device(dev));
> +
> + mcfi2c_wr_cr(mcfi2c, 0);
> + clk_disable(mcfi2c->clk);
> +
> + return 0;
> +}
> +
> +static int mcfi2c_resume(struct device *dev)
> +{
> + struct mcfi2c *mcfi2c = platform_get_drvdata(to_platform_device(dev));
> +
> + clk_enable(mcfi2c->clk);
> + mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN);
> +
> + return 0;
> +}
> +
> +static const struct dev_pm_ops mcfi2c_dev_pm_ops = {
> + .suspend = mcfi2c_suspend,
> + .resume = mcfi2c_resume,
> +};
> +
> +#define MCFI2C_DEV_PM_OPS (&mcfi2c_dev_pm_ops)
> +#else
> +#define MCFI2C_DEV_PM_OPS NULL
> +#endif
> +
> +static struct platform_driver mcfi2c_driver = {
> + .driver.name = DRIVER_NAME,
> + .driver.owner = THIS_MODULE,
> + .driver.pm = MCFI2C_DEV_PM_OPS,
> + .remove = mcfi2c_remove,
> +};
> +
> +static int __init mcfi2c_init(void)
> +{
> + return platform_driver_probe(&mcfi2c_driver, mcfi2c_probe);
> +}
> +module_init(mcfi2c_init);
> +
> +static void __exit mcfi2c_exit(void)
> +{
> + platform_driver_unregister(&mcfi2c_driver);
> +}
> +module_exit(mcfi2c_exit);
> +
> +MODULE_AUTHOR("Steven King <sfking@fdwdc.com>");
> +MODULE_DESCRIPTION("I2C-Bus support for Freescale Coldfire processors");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:" DRIVER_NAME);
> +
>
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH 2/2] m68k: add coldfire mcf5307 i2c support
2016-10-05 11:47 ` Greg Ungerer
@ 2016-10-05 12:52 ` Angelo Dureghello
0 siblings, 0 replies; 3+ messages in thread
From: Angelo Dureghello @ 2016-10-05 12:52 UTC (permalink / raw)
To: Greg Ungerer, linux-m68k
Hi Greg,
thanks for the review,
On 05/10/2016 13:47, Greg Ungerer wrote:
> Hi Angelo,
>
> On 02/10/16 10:18, Angelo Dureghello wrote:
>> Add Steven King i2c driver for Coldfire, and reviewed to build
>> and run properly with the actual kernel version.
>
> This won't be able to be merged in this form.
> I can take the m68k/coldfire specific parts - but the i2c
> driver would need to go via the i2c maintainers (so you
> would need a patch to be sent and reveiwed on the i2c
> email list).
>
> I would prefer to merge the existing ColdFire i2c platform
> support I have for all platforms in any case.
>
mainly, i can test things to work properly only on mcf5307,
even i f i am developing some other coldfire boards, they are not
still ready.
Ok i leave i2c/rtc entirely out for now. But i promise to work on it
step by step to have it accepted if board part is accepted,
so at least i have a starting point.
> Regards
> Greg
>
>
Best regards,
Angelo Dureghello
>
>> Signed-off-by: Angelo Dureghello <angelo@sysam.it>
>> ---
>> arch/m68k/coldfire/device.c | 191 ++++++++++++++++
>> arch/m68k/coldfire/m5307.c | 12 +
>> arch/m68k/include/asm/m5307sim.h | 8 +
>> arch/m68k/include/asm/mcfi2c.h | 29 +++
>> drivers/i2c/busses/Kconfig | 12 +
>> drivers/i2c/busses/Makefile | 1 +
>> drivers/i2c/busses/i2c-mcf.c | 463 +++++++++++++++++++++++++++++++++++++++
>> 7 files changed, 716 insertions(+)
>> create mode 100644 arch/m68k/include/asm/mcfi2c.h
>> create mode 100644 drivers/i2c/busses/i2c-mcf.c
>>
>> diff --git a/arch/m68k/coldfire/device.c b/arch/m68k/coldfire/device.c
>> index a0fc0c1..a1ed057 100644
>> --- a/arch/m68k/coldfire/device.c
>> +++ b/arch/m68k/coldfire/device.c
>> @@ -19,6 +19,7 @@
>> #include <asm/mcfsim.h>
>> #include <asm/mcfuart.h>
>> #include <asm/mcfqspi.h>
>> +#include <asm/mcfi2c.h>
>>
>> /*
>> * All current ColdFire parts contain from 2, 3, 4 or 10 UARTS.
>> @@ -327,6 +328,178 @@ static struct platform_device mcf_qspi = {
>> };
>> #endif /* IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) */
>>
>> +#if IS_ENABLED(CONFIG_I2C_COLDFIRE)
>> +static struct resource mcf_i2c0_resources[] = {
>> + {
>> + .start = MCFI2C_BASE0,
>> + .end = MCFI2C_BASE0 + MCFI2C_SIZE0 - 1,
>> + .flags = IORESOURCE_MEM,
>> + },
>> + {
>> + .start = MCF_IRQ_I2C0,
>> + .end = MCF_IRQ_I2C0,
>> + .flags = IORESOURCE_IRQ,
>> + },
>> +};
>> +
>> +static struct mcfi2c_platform_data mcf_i2c0_platform_data = {
>> + .bitrate = 100000,
>> +};
>> +
>> +static struct platform_device mcf_i2c0 = {
>> + .name = "mcfi2c",
>> + .id = 0,
>> + .num_resources = ARRAY_SIZE(mcf_i2c0_resources),
>> + .resource = mcf_i2c0_resources,
>> + .dev.platform_data = &mcf_i2c0_platform_data,
>> +};
>> +#ifdef MCFI2C_BASE1
>> +
>> +static struct resource mcf_i2c1_resources[] = {
>> + {
>> + .start = MCFI2C_BASE1,
>> + .end = MCFI2C_BASE1 + MCFI2C_SIZE1 - 1,
>> + .flags = IORESOURCE_MEM,
>> + },
>> + {
>> + .start = MCF_IRQ_I2C1,
>> + .end = MCF_IRQ_I2C1,
>> + .flags = IORESOURCE_IRQ,
>> + },
>> +};
>> +
>> +static struct mcfi2c_platform_data mcf_i2c1_platform_data = {
>> + .bitrate = 100000,
>> +};
>> +
>> +static struct platform_device mcf_i2c1 = {
>> + .name = "mcfi2c",
>> + .id = 1,
>> + .num_resources = ARRAY_SIZE(mcf_i2c1_resources),
>> + .resource = mcf_i2c1_resources,
>> + .dev.platform_data = &mcf_i2c1_platform_data,
>> +};
>> +
>> +#endif /* MCFI2C_BASE1 */
>> +
>> +#ifdef MCFI2C_BASE2
>> +
>> +static struct resource mcf_i2c2_resources[] = {
>> + {
>> + .start = MCFI2C_BASE2,
>> + .end = MCFI2C_BASE2 + MCFI2C_SIZE2 - 1,
>> + .flags = IORESOURCE_MEM,
>> + },
>> + {
>> + .start = MCF_IRQ_I2C2,
>> + .end = MCF_IRQ_I2C2,
>> + .flags = IORESOURCE_IRQ,
>> + },
>> +};
>> +
>> +static struct mcfi2c_platform_data mcf_i2c2_platform_data = {
>> + .bitrate = 100000,
>> +};
>> +
>> +static struct platform_device mcf_i2c2 = {
>> + .name = "mcfi2c",
>> + .id = 2,
>> + .num_resources = ARRAY_SIZE(mcf_i2c2_resources),
>> + .resource = mcf_i2c2_resources,
>> + .dev.platform_data = &mcf_i2c2_platform_data,
>> +};
>> +
>> +#endif /* MCFI2C_BASE2 */
>> +
>> +#ifdef MCFI2C_BASE3
>> +
>> +static struct resource mcf_i2c3_resources[] = {
>> + {
>> + .start = MCFI2C_BASE3,
>> + .end = MCFI2C_BASE3 + MCFI2C_SIZE3 - 1,
>> + .flags = IORESOURCE_MEM,
>> + },
>> + {
>> + .start = MCF_IRQ_I2C3,
>> + .end = MCF_IRQ_I2C3,
>> + .flags = IORESOURCE_IRQ,
>> + },
>> +};
>> +
>> +static struct mcfi2c_platform_data mcf_i2c3_platform_data = {
>> + .bitrate = 100000,
>> +};
>> +
>> +static struct platform_device mcf_i2c3 = {
>> + .name = "mcfi2c",
>> + .id = 3,
>> + .num_resources = ARRAY_SIZE(mcf_i2c3_resources),
>> + .resource = mcf_i2c3_resources,
>> + .dev.platform_data = &mcf_i2c3_platform_data,
>> +};
>> +
>> +#endif /* MCFI2C_BASE3 */
>> +
>> +#ifdef MCFI2C_BASE4
>> +
>> +static struct resource mcf_i2c4_resources[] = {
>> + {
>> + .start = MCFI2C_BASE4,
>> + .end = MCFI2C_BASE4 + MCFI2C_SIZE4 - 1,
>> + .flags = IORESOURCE_MEM,
>> + },
>> + {
>> + .start = MCF_IRQ_I2C4,
>> + .end = MCF_IRQ_I2C4,
>> + .flags = IORESOURCE_IRQ,
>> + },
>> +};
>> +
>> +static struct mcfi2c_platform_data mcf_i2c4_platform_data = {
>> + .bitrate = 100000,
>> +};
>> +
>> +static struct platform_device mcf_i2c4 = {
>> + .name = "mcfi2c",
>> + .id = 4,
>> + .num_resources = ARRAY_SIZE(mcf_i2c4_resources),
>> + .resource = mcf_i2c4_resources,
>> + .dev.platform_data = &mcf_i2c4_platform_data,
>> +};
>> +
>> +#endif /* MCFI2C_BASE4 */
>> +
>> +#ifdef MCFI2C_BASE5
>> +
>> +static struct resource mcf_i2c5_resources[] = {
>> + {
>> + .start = MCFI2C_BASE5,
>> + .end = MCFI2C_BASE5 + MCFI2C_SIZE5 - 1,
>> + .flags = IORESOURCE_MEM,
>> + },
>> + {
>> + .start = MCF_IRQ_I2C5,
>> + .end = MCF_IRQ_I2C5,
>> + .flags = IORESOURCE_IRQ,
>> + },
>> +};
>> +
>> +static struct mcfi2c_platform_data mcf_i2c5_platform_data = {
>> + .bitrate = 100000,
>> +};
>> +
>> +static struct platform_device mcf_i2c5 = {
>> + .name = "mcfi2c",
>> + .id = 5,
>> + .num_resources = ARRAY_SIZE(mcf_i2c5_resources),
>> + .resource = mcf_i2c5_resources,
>> + .dev.platform_data = &mcf_i2c5_platform_data,
>> +};
>> +
>> +#endif /* MCFI2C_BASE5 */
>> +
>> +#endif /* IS_ENABLED(CONFIG_I2C_COLDFIRE) */
>> +
>> static struct platform_device *mcf_devices[] __initdata = {
>> &mcf_uart,
>> #if IS_ENABLED(CONFIG_FEC)
>> @@ -338,6 +511,24 @@ static struct platform_device *mcf_devices[] __initdata = {
>> #if IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI)
>> &mcf_qspi,
>> #endif
>> +#if IS_ENABLED(CONFIG_I2C_COLDFIRE)
>> + &mcf_i2c0,
>> +#ifdef MCFI2C_BASE1
>> + &mcf_i2c1,
>> +#endif
>> +#ifdef MCFI2C_BASE2
>> + &mcf_i2c2,
>> +#endif
>> +#ifdef MCFI2C_BASE3
>> + &mcf_i2c3,
>> +#endif
>> +#ifdef MCFI2C_BASE4
>> + &mcf_i2c4,
>> +#endif
>> +#ifdef MCFI2C_BASE5
>> + &mcf_i2c5,
>> +#endif
>> +#endif
>> };
>>
>> /*
>> diff --git a/arch/m68k/coldfire/m5307.c b/arch/m68k/coldfire/m5307.c
>> index 2da1d14..159485e 100644
>> --- a/arch/m68k/coldfire/m5307.c
>> +++ b/arch/m68k/coldfire/m5307.c
>> @@ -35,6 +35,7 @@ DEFINE_CLK(mcftmr0, "mcftmr.0", MCF_BUSCLK);
>> DEFINE_CLK(mcftmr1, "mcftmr.1", MCF_BUSCLK);
>> DEFINE_CLK(mcfuart0, "mcfuart.0", MCF_BUSCLK);
>> DEFINE_CLK(mcfuart1, "mcfuart.1", MCF_BUSCLK);
>> +DEFINE_CLK(mcfi2c0, "mcfi2c.0", MCF_BUSCLK);
>>
>> struct clk *mcf_clks[] = {
>> &clk_pll,
>> @@ -43,9 +44,19 @@ struct clk *mcf_clks[] = {
>> &clk_mcftmr1,
>> &clk_mcfuart0,
>> &clk_mcfuart1,
>> + &clk_mcfi2c0,
>> NULL
>> };
>>
>> +static void __init m5307_i2c_init(void)
>> +{
>> +#if IS_ENABLED(CONFIG_I2C_COLDFIRE)
>> + writeb(MCFSIM_ICR_AUTOVEC | MCFSIM_ICR_LEVEL5 | MCFSIM_ICR_PRI0,
>> + MCFSIM_I2CICR);
>> + mcf_mapirq2imr(MCF_IRQ_I2C0, MCFINTC_I2C);
>> +#endif /* IS_ENABLED(CONFIG_I2C_COLDFIRE) */
>> +}
>> +
>> /***************************************************************************/
>>
>> void __init config_BSP(char *commandp, int size)
>> @@ -73,6 +84,7 @@ void __init config_BSP(char *commandp, int size)
>> */
>> wdebug(MCFDEBUG_CSR, MCFDEBUG_CSR_PSTCLK);
>> #endif
>> + m5307_i2c_init();
>> }
>>
>> /***************************************************************************/
>> diff --git a/arch/m68k/include/asm/m5307sim.h b/arch/m68k/include/asm/m5307sim.h
>> index 5d0bb7e..608e04c 100644
>> --- a/arch/m68k/include/asm/m5307sim.h
>> +++ b/arch/m68k/include/asm/m5307sim.h
>> @@ -131,6 +131,12 @@
>> #define MCFGPIO_IRQ_MAX -1
>> #define MCFGPIO_IRQ_VECBASE -1
>>
>> +/*
>> + * I2C module.
>> + */
>> +#define MCFI2C_BASE0 (MCF_MBAR + 0x280)
>> +#define MCFI2C_SIZE0 0x40
>> +
>>
>> /* Definition offset address for CS2-7 -- old mask 5307 */
>>
>> @@ -148,6 +154,7 @@
>> #define MCFSIM_SWDICR MCFSIM_ICR0 /* Watchdog timer ICR */
>> #define MCFSIM_TIMER1ICR MCFSIM_ICR1 /* Timer 1 ICR */
>> #define MCFSIM_TIMER2ICR MCFSIM_ICR2 /* Timer 2 ICR */
>> +#define MCFSIM_I2CICR MCFSIM_ICR3 /* I2C ICR */
>> #define MCFSIM_UART1ICR MCFSIM_ICR4 /* UART 1 ICR */
>> #define MCFSIM_UART2ICR MCFSIM_ICR5 /* UART 2 ICR */
>> #define MCFSIM_DMA0ICR MCFSIM_ICR6 /* DMA 0 ICR */
>> @@ -174,6 +181,7 @@
>> /*
>> * Define system peripheral IRQ usage.
>> */
>> +#define MCF_IRQ_I2C0 29 /* I2C */
>> #define MCF_IRQ_TIMER 30 /* Timer0, Level 6 */
>> #define MCF_IRQ_PROFILER 31 /* Timer1, Level 7 */
>> #define MCF_IRQ_UART0 73 /* UART0 */
>> diff --git a/arch/m68k/include/asm/mcfi2c.h b/arch/m68k/include/asm/mcfi2c.h
>> new file mode 100644
>> index 0000000..a0c69e1
>> --- /dev/null
>> +++ b/arch/m68k/include/asm/mcfi2c.h
>> @@ -0,0 +1,29 @@
>> +/*
>> + * Definitions for Coldfire I2C interface
>> + */
>> +#ifndef mcfi2c_h
>> +#define mcfi2c_h
>> +
>> +#if defined(CONFIG_M5206) || defined(CONFIG_M5206e)
>> +#define MCFI2C_IOBASE (MCF_MBAR + 0x1e0)
>> +#elif defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x)
>> +#define MCFI2C_IOBASE (MCF_IPSBAR + 0x300)
>> +#elif defined(CONFIG_M5249) || defined(CONFIG_M5307) || defined(CONFIG_M5407)
>> +#define MCFI2C_IOBASE (MCF_MBAR + 0x280)
>> +#ifdef CONFIG_M5249
>> +#define MCFI2C_IOBASE2 (MCF_MBAR2 + 0x440)
>> +#endif
>> +#elif defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_M5445x)
>> +#define MCFI2C_IOBASE 0xFC058000
>> +#endif
>> +#define MCFI2C_IOSIZE 0x40
>> +
>> +/*
>> + * struct mcfi2c_platform_data - platform data for the coldfire i2c driver
>> + * @bitrate: bitrate to use for this i2c controller.
>> + */
>> +struct mcfi2c_platform_data {
>> + u32 bitrate;
>> +};
>> +
>> +#endif /* mcfi2c_h */
>> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
>> index 5c3993b..5c3e326 100644
>> --- a/drivers/i2c/busses/Kconfig
>> +++ b/drivers/i2c/busses/Kconfig
>> @@ -597,6 +597,18 @@ config I2C_IMX
>> This driver can also be built as a module. If so, the module
>> will be called i2c-imx.
>>
>> +config I2C_COLDFIRE
>> + tristate "Freescale Coldfire I2C driver"
>> + depends on M5206 || M5206e || M520x || M523x || M5249 || \
>> + M527x || M528x || M5307 || M532x || M5407
>> + help
>> + This driver supports the I2C interface availible on some Freescale
>> + Coldfire processors (M520x, M523x, M5249, M5271, M5275, M528x,
>> + M5307, M532x, M5407).
>> +
>> + This driver can be built as a module. If so, the module
>> + will be called i2c-mcf.
>> +
>> config I2C_IOP3XX
>> tristate "Intel IOPx3xx and IXP4xx on-chip I2C interface"
>> depends on ARCH_IOP32X || ARCH_IOP33X || ARCH_IXP4XX || ARCH_IOP13XX
>> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
>> index 37f2819..17ad0c8 100644
>> --- a/drivers/i2c/busses/Makefile
>> +++ b/drivers/i2c/busses/Makefile
>> @@ -96,6 +96,7 @@ obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
>> obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
>> obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o
>> obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
>> +obj-$(CONFIG_I2C_COLDFIRE) += i2c-mcf.o
>>
>> # External I2C/SMBus adapter drivers
>> obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
>> diff --git a/drivers/i2c/busses/i2c-mcf.c b/drivers/i2c/busses/i2c-mcf.c
>> new file mode 100644
>> index 0000000..320531c
>> --- /dev/null
>> +++ b/drivers/i2c/busses/i2c-mcf.c
>> @@ -0,0 +1,463 @@
>> +/*
>> + * Freescale/Motorola Coldfire I2C driver.
>> + *
>> + * Copyright 2010 Steven King <sfking@fdwdc.com>
>> + *
>> + * Updated by Angelo Dureghello <angelo@sysam.it> Sep 2016
>> + *
>> + * This file is subject to the terms and conditions of the GNU General Public
>> + * License. See the file COPYING in the main directory of this archive
>> + * for more details.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/errno.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/io.h>
>> +#include <linux/clk.h>
>> +#include <linux/err.h>
>> +#include <linux/i2c.h>
>> +#include <linux/slab.h>
>> +
>> +#include <asm/mcfi2c.h>
>> +
>> +#define DRIVER_NAME "mcfi2c"
>> +
>> +#define MCFI2C_ADR 0x00
>> +#define MCFI2C_FDR 0x04
>> +#define MCFI2C_CR 0x08
>> +#define MCFI2C_CR_IEN 0x80
>> +#define MCFI2C_CR_IIEN 0x40
>> +#define MCFI2C_CR_MSTA 0x20
>> +#define MCFI2C_CR_MTX 0x10
>> +#define MCFI2C_CR_TXAK 0x08
>> +#define MCFI2C_CR_RSTA 0x04
>> +#define MCFI2C_DR 0x10
>> +#define MCFI2C_SR 0x0C
>> +#define MCFI2C_SR_ICF 0x80
>> +#define MCFI2C_SR_IAAS 0x40
>> +#define MCFI2C_SR_IBB 0x20
>> +#define MCFI2C_SR_IAL 0x10
>> +#define MCFI2C_SR_SRW 0x04
>> +#define MCFI2C_SR_IIF 0x02
>> +#define MCFI2C_SR_RXAK 0x01
>> +
>> +#define DEFAULT_I2C_BUS_SPEED 100000
>> +
>> +struct mcfi2c {
>> + struct i2c_adapter adapter;
>> + void __iomem *iobase;
>> + int irq;
>> + struct clk *clk;
>> + struct completion completion;
>> +};
>> +
>> +static u8 mcfi2c_rd_cr(struct mcfi2c *mcfi2c)
>> +{
>> + return readb(mcfi2c->iobase + MCFI2C_CR);
>> +}
>> +
>> +static void mcfi2c_wr_cr(struct mcfi2c *mcfi2c, u8 val)
>> +{
>> + writeb(val, mcfi2c->iobase + MCFI2C_CR);
>> +}
>> +
>> +static u8 mcfi2c_rd_sr(struct mcfi2c *mcfi2c)
>> +{
>> + return readb(mcfi2c->iobase + MCFI2C_SR);
>> +}
>> +
>> +static void mcfi2c_wr_sr(struct mcfi2c *mcfi2c, u8 val)
>> +{
>> + writeb(val, mcfi2c->iobase + MCFI2C_SR);
>> +}
>> +
>> +static u8 mcfi2c_rd_dr(struct mcfi2c *mcfi2c)
>> +{
>> + return readb(mcfi2c->iobase + MCFI2C_DR);
>> +}
>> +
>> +static void mcfi2c_wr_dr(struct mcfi2c *mcfi2c, u8 val)
>> +{
>> + writeb(val, mcfi2c->iobase + MCFI2C_DR);
>> +}
>> +
>> +static void mcfi2c_start(struct mcfi2c *mcfi2c)
>> +{
>> + mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_IIEN | MCFI2C_CR_MSTA |
>> + MCFI2C_CR_MTX);
>> +}
>> +
>> +static void mcfi2c_repeat_start(struct mcfi2c *mcfi2c)
>> +{
>> + mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_IIEN | MCFI2C_CR_MSTA |
>> + MCFI2C_CR_MTX | MCFI2C_CR_RSTA);
>> +}
>> +
>> +static void mcfi2c_stop(struct mcfi2c *mcfi2c)
>> +{
>> + mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN);
>> +}
>> +
>> +static void mcfi2c_tx_ack(struct mcfi2c *mcfi2c)
>> +{
>> + mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_IIEN | MCFI2C_CR_MSTA);
>> +}
>> +
>> +static void mcfi2c_tx_nak(struct mcfi2c *mcfi2c)
>> +{
>> + mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_IIEN | MCFI2C_CR_MSTA |
>> + MCFI2C_CR_TXAK);
>> +}
>> +
>> +static irqreturn_t mcfi2c_irq_handler(int this_irq, void *dev_id)
>> +{
>> + struct mcfi2c *mcfi2c = dev_id;
>> +
>> + /* clear interrupt */
>> + mcfi2c_wr_sr(mcfi2c, 0);
>> + complete(&mcfi2c->completion);
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> +static void mcfi2c_reset(struct mcfi2c *mcfi2c)
>> +{
>> + mcfi2c_wr_cr(mcfi2c, 0);
>> + mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_MSTA);
>> + mcfi2c_rd_dr(mcfi2c);
>> + mcfi2c_wr_sr(mcfi2c, 0);
>> + mcfi2c_wr_cr(mcfi2c, 0);
>> + mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN);
>> +}
>> +
>> +static void mcfi2c_wait_for_bus_idle(struct mcfi2c *mcfi2c)
>> +{
>> + if (mcfi2c_rd_sr(mcfi2c) & MCFI2C_SR_IBB) {
>> + unsigned long timeout = jiffies + HZ / 2;
>> +
>> + do {
>> + cond_resched();
>> + if (time_after(jiffies, timeout)) {
>> + mcfi2c_reset(mcfi2c);
>> + break;
>> + }
>> + } while (mcfi2c_rd_sr(mcfi2c) & MCFI2C_SR_IBB);
>> + }
>> +}
>> +
>> +static int mcfi2c_wait_for_bus_busy(struct mcfi2c *mcfi2c)
>> +{
>> + u8 sr;
>> +
>> + while (!((sr = mcfi2c_rd_sr(mcfi2c)) & MCFI2C_SR_IBB))
>> + if (sr & MCFI2C_SR_IAL) {
>> + mcfi2c_reset(mcfi2c);
>> + return -EIO;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int mcfi2c_xmit(struct mcfi2c *mcfi2c, u16 addr, u16 flags, u8 *buf,
>> + u16 len, int timeout, int more)
>> +{
>> + if (!(mcfi2c_rd_cr(mcfi2c) & MCFI2C_CR_MSTA)) {
>> + mcfi2c_wait_for_bus_idle(mcfi2c);
>> +
>> + reinit_completion(&mcfi2c->completion);
>> + mcfi2c_start(mcfi2c);
>> +
>> + if (mcfi2c_wait_for_bus_busy(mcfi2c))
>> + return -EIO;
>> + }
>> +
>> + mcfi2c_wr_dr(mcfi2c, (addr << 1) | (flags & I2C_M_RD));
>> +
>> + while (wait_for_completion_timeout(&mcfi2c->completion, timeout)) {
>> + u8 sr = mcfi2c_rd_sr(mcfi2c);
>> +
>> + if (sr & MCFI2C_SR_IAL) {
>> + mcfi2c_wr_sr(mcfi2c, ~MCFI2C_SR_IAL);
>> + return -EIO;
>> + } else if (mcfi2c_rd_cr(mcfi2c) & MCFI2C_CR_MTX) {
>> + if (sr & MCFI2C_SR_RXAK) {
>> + mcfi2c_stop(mcfi2c);
>> + return -EIO;
>> + } else if (flags & I2C_M_RD) {
>> + if (len > 1)
>> + mcfi2c_tx_ack(mcfi2c);
>> + else
>> + mcfi2c_tx_nak(mcfi2c);
>> + /* dummy read */
>> + mcfi2c_rd_dr(mcfi2c);
>> + } else if (len--) {
>> + mcfi2c_wr_dr(mcfi2c, *buf++);
>> + } else {
>> + if (more)
>> + mcfi2c_repeat_start(mcfi2c);
>> + else
>> + mcfi2c_stop(mcfi2c);
>> + return 0;
>> + }
>> + } else if (--len) {
>> + if (!(len > 1))
>> + mcfi2c_tx_nak(mcfi2c);
>> + *buf++ = mcfi2c_rd_dr(mcfi2c);
>> + } else {
>> + if (more)
>> + mcfi2c_repeat_start(mcfi2c);
>> + else
>> + mcfi2c_stop(mcfi2c);
>> + *buf++ = mcfi2c_rd_dr(mcfi2c);
>> + return 0;
>> + }
>> + }
>> +
>> + mcfi2c_stop(mcfi2c);
>> +
>> + return -ETIMEDOUT;
>> +}
>> +
>> +static int mcfi2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
>> + int num)
>> +{
>> + struct mcfi2c *mcfi2c = i2c_get_adapdata(adapter);
>> + int cnt = 0;
>> + int status;
>> + int retries;
>> +
>> + while (num--) {
>> + retries = adapter->retries;
>> + if (msgs->flags & ~I2C_M_RD)
>> + return -EINVAL;
>> +
>> + do {
>> + status = mcfi2c_xmit(mcfi2c, msgs->addr, msgs->flags,
>> + msgs->buf, msgs->len,
>> + adapter->timeout, num);
>> + } while (status && retries--);
>> +
>> + if (status)
>> + return status;
>> + ++cnt;
>> + ++msgs;
>> + }
>> +
>> + return cnt;
>> +}
>> +
>> +static u32 mcfi2c_func(struct i2c_adapter *adapter)
>> +{
>> + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
>> +}
>> +
>> +static const struct i2c_algorithm mcfi2c_algo = {
>> + .master_xfer = mcfi2c_xfer,
>> + .functionality = mcfi2c_func,
>> +};
>> +
>> +static const u16 mcfi2c_fdr[] = {
>> + 28, 30, 34, 40, 44, 48, 56, 68,
>> + 80, 88, 104, 128, 144, 160, 192, 240,
>> + 288, 320, 384, 480, 576, 640, 768, 960,
>> + 1152, 1280, 1536, 1920, 2304, 2560, 3072, 3840,
>> + 20, 22, 24, 26, 28, 32, 36, 40,
>> + 48, 56, 64, 72, 80, 96, 112, 128,
>> + 160, 192, 224, 256, 320, 384, 448, 512,
>> + 640, 768, 896, 1024, 1280, 1536, 1792, 2048
>> +};
>> +
>> +static u8 mcfi2c_calc_fdr(struct mcfi2c *mcfi2c,
>> + struct mcfi2c_platform_data *pdata)
>> +{
>> + u32 bitrate = (pdata && pdata->bitrate) ?
>> + pdata->bitrate : DEFAULT_I2C_BUS_SPEED;
>> + int div = clk_get_rate(mcfi2c->clk)/bitrate;
>> + int r = 0, i = 0;
>> +
>> + do {
>> + if (abs(mcfi2c_fdr[i] - div) < abs(mcfi2c_fdr[r] - div))
>> + r = i;
>> + } while (++i < ARRAY_SIZE(mcfi2c_fdr));
>> +
>> + return r;
>> +}
>> +
>> +static int mcfi2c_probe(struct platform_device *pdev)
>> +{
>> + struct mcfi2c *mcfi2c;
>> + struct resource *res;
>> + int status;
>> +
>> + mcfi2c = kzalloc(sizeof(*mcfi2c), GFP_KERNEL);
>> + if (!mcfi2c)
>> + return -ENOMEM;
>> +
>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + if (!res) {
>> + dev_dbg(&pdev->dev, "platform_get_resource failed\n");
>> + status = -ENXIO;
>> + goto fail0;
>> + }
>> +
>> + if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
>> + dev_dbg(&pdev->dev, "request_mem_region failed\n");
>> + status = -EBUSY;
>> + goto fail0;
>> + }
>> +
>> + mcfi2c->iobase = ioremap(res->start, resource_size(res));
>> + if (!mcfi2c->iobase) {
>> + dev_dbg(&pdev->dev, "ioremap failed\n");
>> + status = -ENOMEM;
>> + goto fail1;
>> + }
>> +
>> + mcfi2c->irq = platform_get_irq(pdev, 0);
>> + if (mcfi2c->irq < 0) {
>> + dev_dbg(&pdev->dev, "platform_get_irq failed\n");
>> + status = -ENXIO;
>> + goto fail2;
>> + }
>> + status = request_irq(mcfi2c->irq, mcfi2c_irq_handler, 0,
>> + pdev->name, mcfi2c);
>> + if (status) {
>> + dev_dbg(&pdev->dev, "request_irq failed\n");
>> + goto fail2;
>> + }
>> +
>> + mcfi2c->clk = clk_get(&pdev->dev, "i2c_clk");
>> + if (IS_ERR(mcfi2c->clk)) {
>> + dev_dbg(&pdev->dev, "clk_get failed\n");
>> + status = PTR_ERR(mcfi2c->clk);
>> + goto fail3;
>> + }
>> + clk_enable(mcfi2c->clk);
>> +
>> + platform_set_drvdata(pdev, mcfi2c);
>> +
>> + init_completion(&mcfi2c->completion);
>> +
>> + writeb(mcfi2c_calc_fdr(mcfi2c, pdev->dev.platform_data),
>> + mcfi2c->iobase + MCFI2C_FDR);
>> +
>> + writeb(0x00, mcfi2c->iobase + MCFI2C_ADR);
>> +
>> + mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN);
>> +
>> + /* if the bus busy (IBB) is set, reset the controller */
>> + if (mcfi2c_rd_sr(mcfi2c) & MCFI2C_SR_IBB)
>> + mcfi2c_reset(mcfi2c);
>> +
>> + mcfi2c->adapter.algo = &mcfi2c_algo;
>> + mcfi2c->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
>> + mcfi2c->adapter.dev.parent = &pdev->dev;
>> + mcfi2c->adapter.nr = pdev->id;
>> + mcfi2c->adapter.retries = 2;
>> + snprintf(mcfi2c->adapter.name, sizeof(mcfi2c->adapter.name),
>> + DRIVER_NAME ".%d", pdev->id);
>> +
>> + i2c_set_adapdata(&mcfi2c->adapter, mcfi2c);
>> +
>> + status = i2c_add_numbered_adapter(&mcfi2c->adapter);
>> + if (status < 0) {
>> + dev_dbg(&pdev->dev, "i2c_add_numbered_adapter failed\n");
>> + goto fail4;
>> + }
>> + dev_info(&pdev->dev, "Coldfire I2C bus driver\n");
>> +
>> + return 0;
>> +
>> +fail4:
>> + clk_disable(mcfi2c->clk);
>> + clk_put(mcfi2c->clk);
>> +fail3:
>> + free_irq(mcfi2c->irq, mcfi2c);
>> +fail2:
>> + iounmap(mcfi2c->iobase);
>> +fail1:
>> + release_mem_region(res->start, resource_size(res));
>> +fail0:
>> + kfree(mcfi2c);
>> +
>> + return status;
>> +}
>> +
>> +static int mcfi2c_remove(struct platform_device *pdev)
>> +{
>> + struct mcfi2c *mcfi2c = platform_get_drvdata(pdev);
>> + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +
>> + /* disable the hardware */
>> + mcfi2c_wr_cr(mcfi2c, 0);
>> +
>> + platform_set_drvdata(pdev, NULL);
>> + i2c_del_adapter(&mcfi2c->adapter);
>> + clk_disable(mcfi2c->clk);
>> + clk_put(mcfi2c->clk);
>> + free_irq(mcfi2c->irq, mcfi2c);
>> + iounmap(mcfi2c->iobase);
>> + release_mem_region(res->start, resource_size(res));
>> + kfree(mcfi2c);
>> +
>> + return 0;
>> +}
>> +
>> +#ifdef CONFIG_PM
>> +static int mcfi2c_suspend(struct device *dev)
>> +{
>> + struct mcfi2c *mcfi2c = platform_get_drvdata(to_platform_device(dev));
>> +
>> + mcfi2c_wr_cr(mcfi2c, 0);
>> + clk_disable(mcfi2c->clk);
>> +
>> + return 0;
>> +}
>> +
>> +static int mcfi2c_resume(struct device *dev)
>> +{
>> + struct mcfi2c *mcfi2c = platform_get_drvdata(to_platform_device(dev));
>> +
>> + clk_enable(mcfi2c->clk);
>> + mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct dev_pm_ops mcfi2c_dev_pm_ops = {
>> + .suspend = mcfi2c_suspend,
>> + .resume = mcfi2c_resume,
>> +};
>> +
>> +#define MCFI2C_DEV_PM_OPS (&mcfi2c_dev_pm_ops)
>> +#else
>> +#define MCFI2C_DEV_PM_OPS NULL
>> +#endif
>> +
>> +static struct platform_driver mcfi2c_driver = {
>> + .driver.name = DRIVER_NAME,
>> + .driver.owner = THIS_MODULE,
>> + .driver.pm = MCFI2C_DEV_PM_OPS,
>> + .remove = mcfi2c_remove,
>> +};
>> +
>> +static int __init mcfi2c_init(void)
>> +{
>> + return platform_driver_probe(&mcfi2c_driver, mcfi2c_probe);
>> +}
>> +module_init(mcfi2c_init);
>> +
>> +static void __exit mcfi2c_exit(void)
>> +{
>> + platform_driver_unregister(&mcfi2c_driver);
>> +}
>> +module_exit(mcfi2c_exit);
>> +
>> +MODULE_AUTHOR("Steven King <sfking@fdwdc.com>");
>> +MODULE_DESCRIPTION("I2C-Bus support for Freescale Coldfire processors");
>> +MODULE_LICENSE("GPL");
>> +MODULE_ALIAS("platform:" DRIVER_NAME);
>> +
>>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-m68k" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2016-10-05 12:52 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-10-02 0:18 [PATCH 2/2] m68k: add coldfire mcf5307 i2c support Angelo Dureghello
2016-10-05 11:47 ` Greg Ungerer
2016-10-05 12:52 ` Angelo Dureghello
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox