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@xxxxxxxx> --- 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@xxxxxxxxx> + * + * Updated by Angelo Dureghello <angelo@xxxxxxxx> 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@xxxxxxxxx>"); +MODULE_DESCRIPTION("I2C-Bus support for Freescale Coldfire processors"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRIVER_NAME); + -- 2.8.1 -- To unsubscribe from this list: send the line "unsubscribe linux-m68k" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html