]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
Merge branch 'i2c-for-2630-v2' of git://aeryn.fluff.org.uk/bjdooks/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 7 Apr 2009 15:45:12 +0000 (08:45 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 7 Apr 2009 15:45:12 +0000 (08:45 -0700)
* 'i2c-for-2630-v2' of git://aeryn.fluff.org.uk/bjdooks/linux:
  i2c: imx: Make disable_delay a per-device variable
  i2c: xtensa s6000 i2c driver
  powerpc/85xx: i2c-mpc: use new I2C bindings for the Socates board
  i2c: i2c-mpc: make I2C bus speed configurable
  i2c: i2c-mpc: use dev based printout function
  i2c: i2c-mpc: various coding style fixes
  i2c: imx: Add missing request_mem_region in probe()
  i2c: i2c-s3c2410: Initialise Samsung I2C controller early
  i2c-s3c2410: Simplify bus frequency calculation
  i2c-s3c2410: sda_delay should be in ns, not clock ticks
  i2c: iMX/MXC support

16 files changed:
arch/arm/mach-s3c2410/mach-bast.c
arch/arm/mach-s3c2410/mach-n30.c
arch/arm/mach-s3c2412/mach-jive.c
arch/arm/plat-mxc/include/mach/i2c.h [new file with mode: 0644]
arch/arm/plat-s3c/dev-i2c0.c
arch/arm/plat-s3c/dev-i2c1.c
arch/arm/plat-s3c/include/plat/iic.h
arch/powerpc/boot/dts/socrates.dts
drivers/i2c/busses/Kconfig
drivers/i2c/busses/Makefile
drivers/i2c/busses/i2c-imx.c [new file with mode: 0644]
drivers/i2c/busses/i2c-mpc.c
drivers/i2c/busses/i2c-s3c2410.c
drivers/i2c/busses/i2c-s6000.c [new file with mode: 0644]
drivers/i2c/busses/i2c-s6000.h [new file with mode: 0644]
include/linux/i2c/s6000.h [new file with mode: 0644]

index 01bd76725b920040a6b7ea1687d1c84770f32f54..4389c160f7d08f920d884b3f974ea2474d44fa09 100644 (file)
@@ -409,8 +409,7 @@ static struct platform_device bast_sio = {
 static struct s3c2410_platform_i2c __initdata bast_i2c_info = {
        .flags          = 0,
        .slave_addr     = 0x10,
-       .bus_freq       = 100*1000,
-       .max_freq       = 130*1000,
+       .frequency      = 100*1000,
 };
 
 /* Asix AX88796 10/100 ethernet controller */
index 05a5e877b49b73b7ccfa20734544b8f1f089e2c7..2b83f87077100b310f8a9502663c75451bacaca0 100644 (file)
@@ -340,8 +340,7 @@ static struct platform_device *n35_devices[] __initdata = {
 static struct s3c2410_platform_i2c n30_i2ccfg = {
        .flags          = 0,
        .slave_addr     = 0x10,
-       .bus_freq       = 10*1000,
-       .max_freq       = 10*1000,
+       .frequency      = 10*1000,
 };
 
 /* Lots of hardcoded stuff, but it sets up the hardware in a useful
index 72c266aee1410ab73ab085b65c1dbec1c00919aa..332bd3263eafd18134791a95b6fd041b564370e6 100644 (file)
@@ -453,8 +453,7 @@ static struct spi_board_info __initdata jive_spi_devs[] = {
 /* I2C bus and device configuration. */
 
 static struct s3c2410_platform_i2c jive_i2c_cfg __initdata = {
-       .max_freq       = 80 * 1000,
-       .bus_freq       = 50 * 1000,
+       .frequency      = 80 * 1000,
        .flags          = S3C_IICFLG_FILTER,
        .sda_delay      = 2,
 };
diff --git a/arch/arm/plat-mxc/include/mach/i2c.h b/arch/arm/plat-mxc/include/mach/i2c.h
new file mode 100644 (file)
index 0000000..4a5dc5c
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * i2c.h - i.MX I2C driver header file
+ *
+ * Copyright (c) 2008, Darius Augulis <augulis.darius@gmail.com>
+ *
+ * This file is released under the GPLv2
+ */
+
+#ifndef __ASM_ARCH_I2C_H_
+#define __ASM_ARCH_I2C_H_
+
+/**
+ * struct imxi2c_platform_data - structure of platform data for MXC I2C driver
+ * @init:      Initialise gpio's and other board specific things
+ * @exit:      Free everything initialised by @init
+ * @bitrate:   Bus speed measured in Hz
+ *
+ **/
+struct imxi2c_platform_data {
+       int (*init)(struct device *dev);
+       void (*exit)(struct device *dev);
+       int bitrate;
+};
+
+#endif /* __ASM_ARCH_I2C_H_ */
index fe327074037ea05d4d996cbe52be7ab9a2deae78..428372868fbba66408c1fc2d290de9d138399397 100644 (file)
@@ -1,6 +1,6 @@
 /* linux/arch/arm/plat-s3c/dev-i2c0.c
  *
- * Copyright 2008 Simtec Electronics
+ * Copyright 2008,2009 Simtec Electronics
  *     Ben Dooks <ben@simtec.co.uk>
  *     http://armlinux.simtec.co.uk/
  *
@@ -50,9 +50,8 @@ struct platform_device s3c_device_i2c0 = {
 static struct s3c2410_platform_i2c default_i2c_data0 __initdata = {
        .flags          = 0,
        .slave_addr     = 0x10,
-       .bus_freq       = 100*1000,
-       .max_freq       = 400*1000,
-       .sda_delay      = S3C2410_IICLC_SDA_DELAY5 | S3C2410_IICLC_FILTER_ON,
+       .frequency      = 100*1000,
+       .sda_delay      = 100,
 };
 
 void __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd)
index 2387fbf57af6da984b49dce28fcf08a55a6afee5..8349c462788c9e2f41eb0977b2f491f79bbb6f9a 100644 (file)
@@ -1,6 +1,6 @@
 /* linux/arch/arm/plat-s3c/dev-i2c1.c
  *
- * Copyright 2008 Simtec Electronics
+ * Copyright 2008,2009 Simtec Electronics
  *     Ben Dooks <ben@simtec.co.uk>
  *     http://armlinux.simtec.co.uk/
  *
@@ -47,9 +47,8 @@ static struct s3c2410_platform_i2c default_i2c_data1 __initdata = {
        .flags          = 0,
        .bus_num        = 1,
        .slave_addr     = 0x10,
-       .bus_freq       = 100*1000,
-       .max_freq       = 400*1000,
-       .sda_delay      = S3C2410_IICLC_SDA_DELAY5 | S3C2410_IICLC_FILTER_ON,
+       .frequency      = 100*1000,
+       .sda_delay      = 100,
 };
 
 void __init s3c_i2c1_set_platdata(struct s3c2410_platform_i2c *pd)
index dc1dfcb9bc6c63034e0a42750ab334ed72ed06d0..67450f115748641da0f8b889d4d19da185b13cd4 100644 (file)
@@ -1,9 +1,9 @@
-/* arch/arm/mach-s3c2410/include/mach/iic.h
+/* arch/arm/plat-s3c/include/plat/iic.h
  *
- * Copyright (c) 2004 Simtec Electronics
+ * Copyright 2004,2009 Simtec Electronics
  *     Ben Dooks <ben@simtec.co.uk>
  *
- * S3C2410 - I2C Controller platfrom_device info
+ * S3C - I2C Controller platform_device info
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
 
 #define S3C_IICFLG_FILTER      (1<<0)  /* enable s3c2440 filter */
 
-/* Notes:
- *     1) All frequencies are expressed in Hz
- *     2) A value of zero is `do not care`
-*/
-
+/**
+ *     struct s3c2410_platform_i2c - Platform data for s3c I2C.
+ *     @bus_num: The bus number to use (if possible).
+ *     @flags: Any flags for the I2C bus (E.g. S3C_IICFLK_FILTER).
+ *     @slave_addr: The I2C address for the slave device (if enabled).
+ *     @frequency: The desired frequency in Hz of the bus.  This is
+ *                  guaranteed to not be exceeded.  If the caller does
+ *                  not care, use zero and the driver will select a
+ *                  useful default.
+ *     @sda_delay: The delay (in ns) applied to SDA edges.
+ *     @cfg_gpio: A callback to configure the pins for I2C operation.
+ */
 struct s3c2410_platform_i2c {
-       int             bus_num;        /* bus number to use */
+       int             bus_num;
        unsigned int    flags;
-       unsigned int    slave_addr;     /* slave address for controller */
-       unsigned long   bus_freq;       /* standard bus frequency */
-       unsigned long   max_freq;       /* max frequency for the bus */
-       unsigned long   min_freq;       /* min frequency for the bus */
-       unsigned int    sda_delay;      /* pclks (s3c2440 only) */
+       unsigned int    slave_addr;
+       unsigned long   frequency;
+       unsigned int    sda_delay;
 
        void    (*cfg_gpio)(struct platform_device *dev);
 };
index 04c398862e03fb8a5b2268c4c6c1112cd93ef12f..7a6ae75a1e573a14afac948038fa469a662b012f 100644 (file)
                        #address-cells = <1>;
                        #size-cells = <0>;
                        cell-index = <0>;
-                       compatible = "fsl-i2c";
+                       compatible = "fsl,mpc8544-i2c", "fsl-i2c";
                        reg = <0x3000 0x100>;
                        interrupts = <43 2>;
                        interrupt-parent = <&mpic>;
-                       dfsrr;
+                       fsl,preserve-clocking;
 
                        dtt@28 {
                                compatible = "winbond,w83782d";
                        #address-cells = <1>;
                        #size-cells = <0>;
                        cell-index = <1>;
-                       compatible = "fsl-i2c";
+                       compatible = "fsl,mpc8544-i2c", "fsl-i2c";
                        reg = <0x3100 0x100>;
                        interrupts = <43 2>;
                        interrupt-parent = <&mpic>;
-                       dfsrr;
+                       fsl,preserve-clocking;
                };
 
                enet0: ethernet@24000 {
index da809ad0996aaa1ea010a13900fa466be9e92ec0..94eae5c3cbc7d3417d2273569f89164d45efea5b 100644 (file)
@@ -356,6 +356,16 @@ config I2C_IBM_IIC
          This driver can also be built as a module.  If so, the module
          will be called i2c-ibm_iic.
 
+config I2C_IMX
+       tristate "IMX I2C interface"
+       depends on ARCH_MXC
+       help
+         Say Y here if you want to use the IIC bus controller on
+         the Freescale i.MX/MXC processors.
+
+         This driver can also be built as a module.  If so, the module
+         will be called i2c-imx.
+
 config I2C_IOP3XX
        tristate "Intel IOPx3xx and IXP4xx on-chip I2C interface"
        depends on ARCH_IOP32X || ARCH_IOP33X || ARCH_IXP4XX || ARCH_IOP13XX
@@ -462,6 +472,16 @@ config I2C_S3C2410
          Say Y here to include support for I2C controller in the
          Samsung S3C2410 based System-on-Chip devices.
 
+config I2C_S6000
+       tristate "S6000 I2C support"
+       depends on XTENSA_VARIANT_S6000
+       help
+         This driver supports the on chip I2C device on the
+         S6000 xtensa processor family.
+
+         To compile this driver as a module, choose M here. The module
+         will be called i2c-s6000.
+
 config I2C_SH7760
        tristate "Renesas SH7760 I2C Controller"
        depends on CPU_SUBTYPE_SH7760
index 0c2c4b26cdf1ff4d5bac686a40a4a306bf0e730f..776acb6403a783bed7bcab070ae93d7384221c50 100644 (file)
@@ -33,6 +33,7 @@ obj-$(CONFIG_I2C_DAVINCI)     += i2c-davinci.o
 obj-$(CONFIG_I2C_GPIO)         += i2c-gpio.o
 obj-$(CONFIG_I2C_HIGHLANDER)   += i2c-highlander.o
 obj-$(CONFIG_I2C_IBM_IIC)      += i2c-ibm_iic.o
+obj-$(CONFIG_I2C_IMX)          += i2c-imx.o
 obj-$(CONFIG_I2C_IOP3XX)       += i2c-iop3xx.o
 obj-$(CONFIG_I2C_IXP2000)      += i2c-ixp2000.o
 obj-$(CONFIG_I2C_MPC)          += i2c-mpc.o
@@ -43,6 +44,7 @@ obj-$(CONFIG_I2C_PASEMI)      += i2c-pasemi.o
 obj-$(CONFIG_I2C_PNX)          += i2c-pnx.o
 obj-$(CONFIG_I2C_PXA)          += i2c-pxa.o
 obj-$(CONFIG_I2C_S3C2410)      += i2c-s3c2410.o
+obj-$(CONFIG_I2C_S6000)                += i2c-s6000.o
 obj-$(CONFIG_I2C_SH7760)       += i2c-sh7760.o
 obj-$(CONFIG_I2C_SH_MOBILE)    += i2c-sh_mobile.o
 obj-$(CONFIG_I2C_SIMTEC)       += i2c-simtec.o
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
new file mode 100644 (file)
index 0000000..0b486a6
--- /dev/null
@@ -0,0 +1,624 @@
+/*
+ *     Copyright (C) 2002 Motorola GSG-China
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License
+ *     as published by the Free Software Foundation; either version 2
+ *     of the License, or (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ *     USA.
+ *
+ * Author:
+ *     Darius Augulis, Teltonika Inc.
+ *
+ * Desc.:
+ *     Implementation of I2C Adapter/Algorithm Driver
+ *     for I2C Bus integrated in Freescale i.MX/MXC processors
+ *
+ *     Derived from Motorola GSG China I2C example driver
+ *
+ *     Copyright (C) 2005 Torsten Koschorrek <koschorrek at synertronixx.de
+ *     Copyright (C) 2005 Matthias Blaschke <blaschke at synertronixx.de
+ *     Copyright (C) 2007 RightHand Technologies, Inc.
+ *     Copyright (C) 2008 Darius Augulis <darius.augulis at teltonika.lt>
+ *
+ */
+
+/** Includes *******************************************************************
+*******************************************************************************/
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#include <mach/irqs.h>
+#include <mach/hardware.h>
+#include <mach/i2c.h>
+
+/** Defines ********************************************************************
+*******************************************************************************/
+
+/* This will be the driver name the kernel reports */
+#define DRIVER_NAME "imx-i2c"
+
+/* Default value */
+#define IMX_I2C_BIT_RATE       100000  /* 100kHz */
+
+/* IMX I2C registers */
+#define IMX_I2C_IADR   0x00    /* i2c slave address */
+#define IMX_I2C_IFDR   0x04    /* i2c frequency divider */
+#define IMX_I2C_I2CR   0x08    /* i2c control */
+#define IMX_I2C_I2SR   0x0C    /* i2c status */
+#define IMX_I2C_I2DR   0x10    /* i2c transfer data */
+
+/* Bits of IMX I2C registers */
+#define I2SR_RXAK      0x01
+#define I2SR_IIF       0x02
+#define I2SR_SRW       0x04
+#define I2SR_IAL       0x10
+#define I2SR_IBB       0x20
+#define I2SR_IAAS      0x40
+#define I2SR_ICF       0x80
+#define I2CR_RSTA      0x04
+#define I2CR_TXAK      0x08
+#define I2CR_MTX       0x10
+#define I2CR_MSTA      0x20
+#define I2CR_IIEN      0x40
+#define I2CR_IEN       0x80
+
+/** Variables ******************************************************************
+*******************************************************************************/
+
+/*
+ * sorted list of clock divider, register value pairs
+ * taken from table 26-5, p.26-9, Freescale i.MX
+ * Integrated Portable System Processor Reference Manual
+ * Document Number: MC9328MXLRM, Rev. 5.1, 06/2007
+ *
+ * Duplicated divider values removed from list
+ */
+
+static u16 __initdata i2c_clk_div[50][2] = {
+       { 22,   0x20 }, { 24,   0x21 }, { 26,   0x22 }, { 28,   0x23 },
+       { 30,   0x00 }, { 32,   0x24 }, { 36,   0x25 }, { 40,   0x26 },
+       { 42,   0x03 }, { 44,   0x27 }, { 48,   0x28 }, { 52,   0x05 },
+       { 56,   0x29 }, { 60,   0x06 }, { 64,   0x2A }, { 72,   0x2B },
+       { 80,   0x2C }, { 88,   0x09 }, { 96,   0x2D }, { 104,  0x0A },
+       { 112,  0x2E }, { 128,  0x2F }, { 144,  0x0C }, { 160,  0x30 },
+       { 192,  0x31 }, { 224,  0x32 }, { 240,  0x0F }, { 256,  0x33 },
+       { 288,  0x10 }, { 320,  0x34 }, { 384,  0x35 }, { 448,  0x36 },
+       { 480,  0x13 }, { 512,  0x37 }, { 576,  0x14 }, { 640,  0x38 },
+       { 768,  0x39 }, { 896,  0x3A }, { 960,  0x17 }, { 1024, 0x3B },
+       { 1152, 0x18 }, { 1280, 0x3C }, { 1536, 0x3D }, { 1792, 0x3E },
+       { 1920, 0x1B }, { 2048, 0x3F }, { 2304, 0x1C }, { 2560, 0x1D },
+       { 3072, 0x1E }, { 3840, 0x1F }
+};
+
+struct imx_i2c_struct {
+       struct i2c_adapter      adapter;
+       struct resource         *res;
+       struct clk              *clk;
+       void __iomem            *base;
+       int                     irq;
+       wait_queue_head_t       queue;
+       unsigned long           i2csr;
+       unsigned int            disable_delay;
+};
+
+/** Functions for IMX I2C adapter driver ***************************************
+*******************************************************************************/
+
+static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx)
+{
+       unsigned long orig_jiffies = jiffies;
+
+       dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
+
+       /* wait for bus not busy */
+       while (readb(i2c_imx->base + IMX_I2C_I2SR) & I2SR_IBB) {
+               if (signal_pending(current)) {
+                       dev_dbg(&i2c_imx->adapter.dev,
+                               "<%s> I2C Interrupted\n", __func__);
+                       return -EINTR;
+               }
+               if (time_after(jiffies, orig_jiffies + HZ / 1000)) {
+                       dev_dbg(&i2c_imx->adapter.dev,
+                               "<%s> I2C bus is busy\n", __func__);
+                       return -EIO;
+               }
+               schedule();
+       }
+
+       return 0;
+}
+
+static int i2c_imx_trx_complete(struct imx_i2c_struct *i2c_imx)
+{
+       int result;
+
+       result = wait_event_interruptible_timeout(i2c_imx->queue,
+               i2c_imx->i2csr & I2SR_IIF, HZ / 10);
+
+       if (unlikely(result < 0)) {
+               dev_dbg(&i2c_imx->adapter.dev, "<%s> result < 0\n", __func__);
+               return result;
+       } else if (unlikely(!(i2c_imx->i2csr & I2SR_IIF))) {
+               dev_dbg(&i2c_imx->adapter.dev, "<%s> Timeout\n", __func__);
+               return -ETIMEDOUT;
+       }
+       dev_dbg(&i2c_imx->adapter.dev, "<%s> TRX complete\n", __func__);
+       i2c_imx->i2csr = 0;
+       return 0;
+}
+
+static int i2c_imx_acked(struct imx_i2c_struct *i2c_imx)
+{
+       if (readb(i2c_imx->base + IMX_I2C_I2SR) & I2SR_RXAK) {
+               dev_dbg(&i2c_imx->adapter.dev, "<%s> No ACK\n", __func__);
+               return -EIO;  /* No ACK */
+       }
+
+       dev_dbg(&i2c_imx->adapter.dev, "<%s> ACK received\n", __func__);
+       return 0;
+}
+
+static void i2c_imx_start(struct imx_i2c_struct *i2c_imx)
+{
+       unsigned int temp = 0;
+
+       dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
+
+       /* Enable I2C controller */
+       writeb(I2CR_IEN, i2c_imx->base + IMX_I2C_I2CR);
+       /* Start I2C transaction */
+       temp = readb(i2c_imx->base + IMX_I2C_I2CR);
+       temp |= I2CR_MSTA;
+       writeb(temp, i2c_imx->base + IMX_I2C_I2CR);
+       temp |= I2CR_IIEN | I2CR_MTX | I2CR_TXAK;
+       writeb(temp, i2c_imx->base + IMX_I2C_I2CR);
+}
+
+static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx)
+{
+       unsigned int temp = 0;
+
+       /* Stop I2C transaction */
+       dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
+       temp = readb(i2c_imx->base + IMX_I2C_I2CR);
+       temp &= ~I2CR_MSTA;
+       writeb(temp, i2c_imx->base + IMX_I2C_I2CR);
+       /* setup chip registers to defaults */
+       writeb(I2CR_IEN, i2c_imx->base + IMX_I2C_I2CR);
+       writeb(0, i2c_imx->base + IMX_I2C_I2SR);
+       /*
+        * This delay caused by an i.MXL hardware bug.
+        * If no (or too short) delay, no "STOP" bit will be generated.
+        */
+       udelay(i2c_imx->disable_delay);
+       /* Disable I2C controller */
+       writeb(0, i2c_imx->base + IMX_I2C_I2CR);
+}
+
+static void __init i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx,
+                                                       unsigned int rate)
+{
+       unsigned int i2c_clk_rate;
+       unsigned int div;
+       int i;
+
+       /* Divider value calculation */
+       i2c_clk_rate = clk_get_rate(i2c_imx->clk);
+       div = (i2c_clk_rate + rate - 1) / rate;
+       if (div < i2c_clk_div[0][0])
+               i = 0;
+       else if (div > i2c_clk_div[ARRAY_SIZE(i2c_clk_div) - 1][0])
+               i = ARRAY_SIZE(i2c_clk_div) - 1;
+       else
+               for (i = 0; i2c_clk_div[i][0] < div; i++);
+
+       /* Write divider value to register */
+       writeb(i2c_clk_div[i][1], i2c_imx->base + IMX_I2C_IFDR);
+
+       /*
+        * There dummy delay is calculated.
+        * It should be about one I2C clock period long.
+        * This delay is used in I2C bus disable function
+        * to fix chip hardware bug.
+        */
+       i2c_imx->disable_delay = (500000U * i2c_clk_div[i][0]
+               + (i2c_clk_rate / 2) - 1) / (i2c_clk_rate / 2);
+
+       /* dev_dbg() can't be used, because adapter is not yet registered */
+#ifdef CONFIG_I2C_DEBUG_BUS
+       printk(KERN_DEBUG "I2C: <%s> I2C_CLK=%d, REQ DIV=%d\n",
+               __func__, i2c_clk_rate, div);
+       printk(KERN_DEBUG "I2C: <%s> IFDR[IC]=0x%x, REAL DIV=%d\n",
+               __func__, i2c_clk_div[i][1], i2c_clk_div[i][0]);
+#endif
+}
+
+static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
+{
+       struct imx_i2c_struct *i2c_imx = dev_id;
+       unsigned int temp;
+
+       temp = readb(i2c_imx->base + IMX_I2C_I2SR);
+       if (temp & I2SR_IIF) {
+               /* save status register */
+               i2c_imx->i2csr = temp;
+               temp &= ~I2SR_IIF;
+               writeb(temp, i2c_imx->base + IMX_I2C_I2SR);
+               wake_up_interruptible(&i2c_imx->queue);
+               return IRQ_HANDLED;
+       }
+
+       return IRQ_NONE;
+}
+
+static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs)
+{
+       int i, result;
+
+       dev_dbg(&i2c_imx->adapter.dev, "<%s> write slave address: addr=0x%x\n",
+               __func__, msgs->addr << 1);
+
+       /* write slave address */
+       writeb(msgs->addr << 1, i2c_imx->base + IMX_I2C_I2DR);
+       result = i2c_imx_trx_complete(i2c_imx);
+       if (result)
+               return result;
+       result = i2c_imx_acked(i2c_imx);
+       if (result)
+               return result;
+       dev_dbg(&i2c_imx->adapter.dev, "<%s> write data\n", __func__);
+
+       /* write data */
+       for (i = 0; i < msgs->len; i++) {
+               dev_dbg(&i2c_imx->adapter.dev,
+                       "<%s> write byte: B%d=0x%X\n",
+                       __func__, i, msgs->buf[i]);
+               writeb(msgs->buf[i], i2c_imx->base + IMX_I2C_I2DR);
+               result = i2c_imx_trx_complete(i2c_imx);
+               if (result)
+                       return result;
+               result = i2c_imx_acked(i2c_imx);
+               if (result)
+                       return result;
+       }
+       return 0;
+}
+
+static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs)
+{
+       int i, result;
+       unsigned int temp;
+
+       dev_dbg(&i2c_imx->adapter.dev,
+               "<%s> write slave address: addr=0x%x\n",
+               __func__, (msgs->addr << 1) | 0x01);
+
+       /* write slave address */
+       writeb((msgs->addr << 1) | 0x01, i2c_imx->base + IMX_I2C_I2DR);
+       result = i2c_imx_trx_complete(i2c_imx);
+       if (result)
+               return result;
+       result = i2c_imx_acked(i2c_imx);
+       if (result)
+               return result;
+
+       dev_dbg(&i2c_imx->adapter.dev, "<%s> setup bus\n", __func__);
+
+       /* setup bus to read data */
+       temp = readb(i2c_imx->base + IMX_I2C_I2CR);
+       temp &= ~I2CR_MTX;
+       if (msgs->len - 1)
+               temp &= ~I2CR_TXAK;
+       writeb(temp, i2c_imx->base + IMX_I2C_I2CR);
+       readb(i2c_imx->base + IMX_I2C_I2DR); /* dummy read */
+
+       dev_dbg(&i2c_imx->adapter.dev, "<%s> read data\n", __func__);
+
+       /* read data */
+       for (i = 0; i < msgs->len; i++) {
+               result = i2c_imx_trx_complete(i2c_imx);
+               if (result)
+                       return result;
+               if (i == (msgs->len - 1)) {
+                       dev_dbg(&i2c_imx->adapter.dev,
+                               "<%s> clear MSTA\n", __func__);
+                       temp = readb(i2c_imx->base + IMX_I2C_I2CR);
+                       temp &= ~I2CR_MSTA;
+                       writeb(temp, i2c_imx->base + IMX_I2C_I2CR);
+               } else if (i == (msgs->len - 2)) {
+                       dev_dbg(&i2c_imx->adapter.dev,
+                               "<%s> set TXAK\n", __func__);
+                       temp = readb(i2c_imx->base + IMX_I2C_I2CR);
+                       temp |= I2CR_TXAK;
+                       writeb(temp, i2c_imx->base + IMX_I2C_I2CR);
+               }
+               msgs->buf[i] = readb(i2c_imx->base + IMX_I2C_I2DR);
+               dev_dbg(&i2c_imx->adapter.dev,
+                       "<%s> read byte: B%d=0x%X\n",
+                       __func__, i, msgs->buf[i]);
+       }
+       return 0;
+}
+
+static int i2c_imx_xfer(struct i2c_adapter *adapter,
+                                               struct i2c_msg *msgs, int num)
+{
+       unsigned int i, temp;
+       int result;
+       struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);
+
+       dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
+
+       /* Check if i2c bus is not busy */
+       result = i2c_imx_bus_busy(i2c_imx);
+       if (result)
+               goto fail0;
+
+       /* Start I2C transfer */
+       i2c_imx_start(i2c_imx);
+
+       /* read/write data */
+       for (i = 0; i < num; i++) {
+               if (i) {
+                       dev_dbg(&i2c_imx->adapter.dev,
+                               "<%s> repeated start\n", __func__);
+                       temp = readb(i2c_imx->base + IMX_I2C_I2CR);
+                       temp |= I2CR_RSTA;
+                       writeb(temp, i2c_imx->base + IMX_I2C_I2CR);
+               }
+               dev_dbg(&i2c_imx->adapter.dev,
+                       "<%s> transfer message: %d\n", __func__, i);
+               /* write/read data */
+#ifdef CONFIG_I2C_DEBUG_BUS
+               temp = readb(i2c_imx->base + IMX_I2C_I2CR);
+               dev_dbg(&i2c_imx->adapter.dev, "<%s> CONTROL: IEN=%d, IIEN=%d, "
+                       "MSTA=%d, MTX=%d, TXAK=%d, RSTA=%d\n", __func__,
+                       (temp & I2CR_IEN ? 1 : 0), (temp & I2CR_IIEN ? 1 : 0),
+                       (temp & I2CR_MSTA ? 1 : 0), (temp & I2CR_MTX ? 1 : 0),
+                       (temp & I2CR_TXAK ? 1 : 0), (temp & I2CR_RSTA ? 1 : 0));
+               temp = readb(i2c_imx->base + IMX_I2C_I2SR);
+               dev_dbg(&i2c_imx->adapter.dev,
+                       "<%s> STATUS: ICF=%d, IAAS=%d, IBB=%d, "
+                       "IAL=%d, SRW=%d, IIF=%d, RXAK=%d\n", __func__,
+                       (temp & I2SR_ICF ? 1 : 0), (temp & I2SR_IAAS ? 1 : 0),
+                       (temp & I2SR_IBB ? 1 : 0), (temp & I2SR_IAL ? 1 : 0),
+                       (temp & I2SR_SRW ? 1 : 0), (temp & I2SR_IIF ? 1 : 0),
+                       (temp & I2SR_RXAK ? 1 : 0));
+#endif
+               if (msgs[i].flags & I2C_M_RD)
+                       result = i2c_imx_read(i2c_imx, &msgs[i]);
+               else
+                       result = i2c_imx_write(i2c_imx, &msgs[i]);
+       }
+
+fail0:
+       /* Stop I2C transfer */
+       i2c_imx_stop(i2c_imx);
+
+       dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,
+               (result < 0) ? "error" : "success msg",
+                       (result < 0) ? result : num);
+       return (result < 0) ? result : num;
+}
+
+static u32 i2c_imx_func(struct i2c_adapter *adapter)
+{
+       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm i2c_imx_algo = {
+       .master_xfer    = i2c_imx_xfer,
+       .functionality  = i2c_imx_func,
+};
+
+static int __init i2c_imx_probe(struct platform_device *pdev)
+{
+       struct imx_i2c_struct *i2c_imx;
+       struct resource *res;
+       struct imxi2c_platform_data *pdata;
+       void __iomem *base;
+       resource_size_t res_size;
+       int irq;
+       int ret;
+
+       dev_dbg(&pdev->dev, "<%s>\n", __func__);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "can't get device resources\n");
+               return -ENOENT;
+       }
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(&pdev->dev, "can't get irq number\n");
+               return -ENOENT;
+       }
+
+       pdata = pdev->dev.platform_data;
+
+       if (pdata && pdata->init) {
+               ret = pdata->init(&pdev->dev);
+               if (ret)
+                       return ret;
+       }
+
+       res_size = resource_size(res);
+       base = ioremap(res->start, res_size);
+       if (!base) {
+               dev_err(&pdev->dev, "ioremap failed\n");
+               ret = -EIO;
+               goto fail0;
+       }
+
+       i2c_imx = kzalloc(sizeof(struct imx_i2c_struct), GFP_KERNEL);
+       if (!i2c_imx) {
+               dev_err(&pdev->dev, "can't allocate interface\n");
+               ret = -ENOMEM;
+               goto fail1;
+       }
+
+       if (!request_mem_region(res->start, res_size, DRIVER_NAME)) {
+               ret = -EBUSY;
+               goto fail2;
+       }
+
+       /* Setup i2c_imx driver structure */
+       strcpy(i2c_imx->adapter.name, pdev->name);
+       i2c_imx->adapter.owner          = THIS_MODULE;
+       i2c_imx->adapter.algo           = &i2c_imx_algo;
+       i2c_imx->adapter.dev.parent     = &pdev->dev;
+       i2c_imx->adapter.nr             = pdev->id;
+       i2c_imx->irq                    = irq;
+       i2c_imx->base                   = base;
+       i2c_imx->res                    = res;
+
+       /* Get I2C clock */
+       i2c_imx->clk = clk_get(&pdev->dev, "i2c_clk");
+       if (IS_ERR(i2c_imx->clk)) {
+               ret = PTR_ERR(i2c_imx->clk);
+               dev_err(&pdev->dev, "can't get I2C clock\n");
+               goto fail3;
+       }
+       clk_enable(i2c_imx->clk);
+
+       /* Request IRQ */
+       ret = request_irq(i2c_imx->irq, i2c_imx_isr, 0, pdev->name, i2c_imx);
+       if (ret) {
+               dev_err(&pdev->dev, "can't claim irq %d\n", i2c_imx->irq);
+               goto fail4;
+       }
+
+       /* Init queue */
+       init_waitqueue_head(&i2c_imx->queue);
+
+       /* Set up adapter data */
+       i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);
+
+       /* Set up clock divider */
+       if (pdata && pdata->bitrate)
+               i2c_imx_set_clk(i2c_imx, pdata->bitrate);
+       else
+               i2c_imx_set_clk(i2c_imx, IMX_I2C_BIT_RATE);
+
+       /* Set up chip registers to defaults */
+       writeb(0, i2c_imx->base + IMX_I2C_I2CR);
+       writeb(0, i2c_imx->base + IMX_I2C_I2SR);
+
+       /* Add I2C adapter */
+       ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "registration failed\n");
+               goto fail5;
+       }
+
+       /* Set up platform driver data */
+       platform_set_drvdata(pdev, i2c_imx);
+
+       dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", i2c_imx->irq);
+       dev_dbg(&i2c_imx->adapter.dev, "device resources from 0x%x to 0x%x\n",
+               i2c_imx->res->start, i2c_imx->res->end);
+       dev_dbg(&i2c_imx->adapter.dev, "allocated %d bytes at 0x%x \n",
+               res_size, i2c_imx->res->start);
+       dev_dbg(&i2c_imx->adapter.dev, "adapter name: \"%s\"\n",
+               i2c_imx->adapter.name);
+       dev_dbg(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n");
+
+       return 0;   /* Return OK */
+
+fail5:
+       free_irq(i2c_imx->irq, i2c_imx);
+fail4:
+       clk_disable(i2c_imx->clk);
+       clk_put(i2c_imx->clk);
+fail3:
+       release_mem_region(i2c_imx->res->start, resource_size(res));
+fail2:
+       kfree(i2c_imx);
+fail1:
+       iounmap(base);
+fail0:
+       if (pdata && pdata->exit)
+               pdata->exit(&pdev->dev);
+       return ret; /* Return error number */
+}
+
+static int __exit i2c_imx_remove(struct platform_device *pdev)
+{
+       struct imx_i2c_struct *i2c_imx = platform_get_drvdata(pdev);
+       struct imxi2c_platform_data *pdata = pdev->dev.platform_data;
+
+       /* remove adapter */
+       dev_dbg(&i2c_imx->adapter.dev, "adapter removed\n");
+       i2c_del_adapter(&i2c_imx->adapter);
+       platform_set_drvdata(pdev, NULL);
+
+       /* free interrupt */
+       free_irq(i2c_imx->irq, i2c_imx);
+
+       /* setup chip registers to defaults */
+       writeb(0, i2c_imx->base + IMX_I2C_IADR);
+       writeb(0, i2c_imx->base + IMX_I2C_IFDR);
+       writeb(0, i2c_imx->base + IMX_I2C_I2CR);
+       writeb(0, i2c_imx->base + IMX_I2C_I2SR);
+
+       /* Shut down hardware */
+       if (pdata && pdata->exit)
+               pdata->exit(&pdev->dev);
+
+       /* Disable I2C clock */
+       clk_disable(i2c_imx->clk);
+       clk_put(i2c_imx->clk);
+
+       release_mem_region(i2c_imx->res->start, resource_size(i2c_imx->res));
+       iounmap(i2c_imx->base);
+       kfree(i2c_imx);
+       return 0;
+}
+
+static struct platform_driver i2c_imx_driver = {
+       .probe          = i2c_imx_probe,
+       .remove         = __exit_p(i2c_imx_remove),
+       .driver = {
+               .name   = DRIVER_NAME,
+               .owner  = THIS_MODULE,
+       }
+};
+
+static int __init i2c_adap_imx_init(void)
+{
+       return platform_driver_probe(&i2c_imx_driver, i2c_imx_probe);
+}
+
+static void __exit i2c_adap_imx_exit(void)
+{
+       platform_driver_unregister(&i2c_imx_driver);
+}
+
+module_init(i2c_adap_imx_init);
+module_exit(i2c_adap_imx_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Darius Augulis");
+MODULE_DESCRIPTION("I2C adapter driver for IMX I2C bus");
+MODULE_ALIAS("platform:" DRIVER_NAME);
index 26bf3701058648ad338847631122101eb37c691f..4af5c09f0e8fe3def51810746652f6d70da52717 100644 (file)
 #include <linux/of_platform.h>
 #include <linux/of_i2c.h>
 
-#include <asm/io.h>
+#include <linux/io.h>
 #include <linux/fsl_devices.h>
 #include <linux/i2c.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 
+#include <asm/mpc52xx.h>
+#include <sysdev/fsl_soc.h>
+
 #define DRV_NAME "mpc-i2c"
 
-#define MPC_I2C_FDR    0x04
-#define MPC_I2C_CR     0x08
-#define MPC_I2C_SR     0x0c
-#define MPC_I2C_DR     0x10
+#define MPC_I2C_FDR   0x04
+#define MPC_I2C_CR    0x08
+#define MPC_I2C_SR    0x0c
+#define MPC_I2C_DR    0x10
 #define MPC_I2C_DFSRR 0x14
 
 #define CCR_MEN  0x80
 #define CSR_RXAK 0x01
 
 struct mpc_i2c {
+       struct device *dev;
        void __iomem *base;
        u32 interrupt;
        wait_queue_head_t queue;
        struct i2c_adapter adap;
        int irq;
-       u32 flags;
 };
 
-static __inline__ void writeccr(struct mpc_i2c *i2c, u32 x)
+struct mpc_i2c_divider {
+       u16 divider;
+       u16 fdr;        /* including dfsrr */
+};
+
+struct mpc_i2c_match_data {
+       void (*setclock)(struct device_node *node,
+                        struct mpc_i2c *i2c,
+                        u32 clock, u32 prescaler);
+       u32 prescaler;
+};
+
+static inline void writeccr(struct mpc_i2c *i2c, u32 x)
 {
        writeb(x, i2c->base + MPC_I2C_CR);
 }
@@ -100,12 +115,11 @@ static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing)
        u32 x;
        int result = 0;
 
-       if (i2c->irq == NO_IRQ)
-       {
+       if (i2c->irq == NO_IRQ) {
                while (!(readb(i2c->base + MPC_I2C_SR) & CSR_MIF)) {
                        schedule();
                        if (time_after(jiffies, orig_jiffies + timeout)) {
-                               pr_debug("I2C: timeout\n");
+                               dev_dbg(i2c->dev, "timeout\n");
                                writeccr(i2c, 0);
                                result = -EIO;
                                break;
@@ -119,7 +133,7 @@ static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing)
                        (i2c->interrupt & CSR_MIF), timeout);
 
                if (unlikely(!(i2c->interrupt & CSR_MIF))) {
-                       pr_debug("I2C: wait timeout\n");
+                       dev_dbg(i2c->dev, "wait timeout\n");
                        writeccr(i2c, 0);
                        result = -ETIMEDOUT;
                }
@@ -132,17 +146,17 @@ static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing)
                return result;
 
        if (!(x & CSR_MCF)) {
-               pr_debug("I2C: unfinished\n");
+               dev_dbg(i2c->dev, "unfinished\n");
                return -EIO;
        }
 
        if (x & CSR_MAL) {
-               pr_debug("I2C: MAL\n");
+               dev_dbg(i2c->dev, "MAL\n");
                return -EIO;
        }
 
        if (writing && (x & CSR_RXAK)) {
-               pr_debug("I2C: No RXAK\n");
+               dev_dbg(i2c->dev, "No RXAK\n");
                /* generate stop */
                writeccr(i2c, CCR_MEN);
                return -EIO;
@@ -150,18 +164,181 @@ static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing)
        return 0;
 }
 
-static void mpc_i2c_setclock(struct mpc_i2c *i2c)
+#ifdef CONFIG_PPC_52xx
+static const struct mpc_i2c_divider mpc_i2c_dividers_52xx[] = {
+       {20, 0x20}, {22, 0x21}, {24, 0x22}, {26, 0x23},
+       {28, 0x24}, {30, 0x01}, {32, 0x25}, {34, 0x02},
+       {36, 0x26}, {40, 0x27}, {44, 0x04}, {48, 0x28},
+       {52, 0x63}, {56, 0x29}, {60, 0x41}, {64, 0x2a},
+       {68, 0x07}, {72, 0x2b}, {80, 0x2c}, {88, 0x09},
+       {96, 0x2d}, {104, 0x0a}, {112, 0x2e}, {120, 0x81},
+       {128, 0x2f}, {136, 0x47}, {144, 0x0c}, {160, 0x30},
+       {176, 0x49}, {192, 0x31}, {208, 0x4a}, {224, 0x32},
+       {240, 0x0f}, {256, 0x33}, {272, 0x87}, {288, 0x10},
+       {320, 0x34}, {352, 0x89}, {384, 0x35}, {416, 0x8a},
+       {448, 0x36}, {480, 0x13}, {512, 0x37}, {576, 0x14},
+       {640, 0x38}, {768, 0x39}, {896, 0x3a}, {960, 0x17},
+       {1024, 0x3b}, {1152, 0x18}, {1280, 0x3c}, {1536, 0x3d},
+       {1792, 0x3e}, {1920, 0x1b}, {2048, 0x3f}, {2304, 0x1c},
+       {2560, 0x1d}, {3072, 0x1e}, {3584, 0x7e}, {3840, 0x1f},
+       {4096, 0x7f}, {4608, 0x5c}, {5120, 0x5d}, {6144, 0x5e},
+       {7168, 0xbe}, {7680, 0x5f}, {8192, 0xbf}, {9216, 0x9c},
+       {10240, 0x9d}, {12288, 0x9e}, {15360, 0x9f}
+};
+
+int mpc_i2c_get_fdr_52xx(struct device_node *node, u32 clock, int prescaler)
+{
+       const struct mpc52xx_i2c_divider *div = NULL;
+       unsigned int pvr = mfspr(SPRN_PVR);
+       u32 divider;
+       int i;
+
+       if (!clock)
+               return -EINVAL;
+
+       /* Determine divider value */
+       divider = mpc52xx_find_ipb_freq(node) / clock;
+
+       /*
+        * We want to choose an FDR/DFSR that generates an I2C bus speed that
+        * is equal to or lower than the requested speed.
+        */
+       for (i = 0; i < ARRAY_SIZE(mpc52xx_i2c_dividers); i++) {
+               div = &mpc_i2c_dividers_52xx[i];
+               /* Old MPC5200 rev A CPUs do not support the high bits */
+               if (div->fdr & 0xc0 && pvr == 0x80822011)
+                       continue;
+               if (div->divider >= divider)
+                       break;
+       }
+
+       return div ? (int)div->fdr : -EINVAL;
+}
+
+static void mpc_i2c_setclock_52xx(struct device_node *node,
+                                 struct mpc_i2c *i2c,
+                                 u32 clock, u32 prescaler)
 {
-       /* Set clock and filters */
-       if (i2c->flags & FSL_I2C_DEV_SEPARATE_DFSRR) {
-               writeb(0x31, i2c->base + MPC_I2C_FDR);
-               writeb(0x10, i2c->base + MPC_I2C_DFSRR);
-       } else if (i2c->flags & FSL_I2C_DEV_CLOCK_5200)
-               writeb(0x3f, i2c->base + MPC_I2C_FDR);
-       else
-               writel(0x1031, i2c->base + MPC_I2C_FDR);
+       int fdr = mpc52xx_i2c_get_fdr(node, clock, prescaler);
+
+       if (fdr < 0)
+               fdr = 0x3f; /* backward compatibility */
+       writeb(fdr & 0xff, i2c->base + MPC_I2C_FDR);
+       dev_info(i2c->dev, "clock %d Hz (fdr=%d)\n", clock, fdr);
+}
+#else /* !CONFIG_PPC_52xx */
+static void mpc_i2c_setclock_52xx(struct device_node *node,
+                                 struct mpc_i2c *i2c,
+                                 u32 clock, u32 prescaler)
+{
+}
+#endif /* CONFIG_PPC_52xx*/
+
+#ifdef CONFIG_FSL_SOC
+static const struct mpc_i2c_divider mpc_i2c_dividers_8xxx[] = {
+       {160, 0x0120}, {192, 0x0121}, {224, 0x0122}, {256, 0x0123},
+       {288, 0x0100}, {320, 0x0101}, {352, 0x0601}, {384, 0x0102},
+       {416, 0x0602}, {448, 0x0126}, {480, 0x0103}, {512, 0x0127},
+       {544, 0x0b03}, {576, 0x0104}, {608, 0x1603}, {640, 0x0105},
+       {672, 0x2003}, {704, 0x0b05}, {736, 0x2b03}, {768, 0x0106},
+       {800, 0x3603}, {832, 0x0b06}, {896, 0x012a}, {960, 0x0107},
+       {1024, 0x012b}, {1088, 0x1607}, {1152, 0x0108}, {1216, 0x2b07},
+       {1280, 0x0109}, {1408, 0x1609}, {1536, 0x010a}, {1664, 0x160a},
+       {1792, 0x012e}, {1920, 0x010b}, {2048, 0x012f}, {2176, 0x2b0b},
+       {2304, 0x010c}, {2560, 0x010d}, {2816, 0x2b0d}, {3072, 0x010e},
+       {3328, 0x2b0e}, {3584, 0x0132}, {3840, 0x010f}, {4096, 0x0133},
+       {4608, 0x0110}, {5120, 0x0111}, {6144, 0x0112}, {7168, 0x0136},
+       {7680, 0x0113}, {8192, 0x0137}, {9216, 0x0114}, {10240, 0x0115},
+       {12288, 0x0116}, {14336, 0x013a}, {15360, 0x0117}, {16384, 0x013b},
+       {18432, 0x0118}, {20480, 0x0119}, {24576, 0x011a}, {28672, 0x013e},
+       {30720, 0x011b}, {32768, 0x013f}, {36864, 0x011c}, {40960, 0x011d},
+       {49152, 0x011e}, {61440, 0x011f}
+};
+
+u32 mpc_i2c_get_sec_cfg_8xxx(void)
+{
+       struct device_node *node = NULL;
+       u32 __iomem *reg;
+       u32 val = 0;
+
+       node = of_find_node_by_name(NULL, "global-utilities");
+       if (node) {
+               const u32 *prop = of_get_property(node, "reg", NULL);
+               if (prop) {
+                       /*
+                        * Map and check POR Device Status Register 2
+                        * (PORDEVSR2) at 0xE0014
+                        */
+                       reg = ioremap(get_immrbase() + *prop + 0x14, 0x4);
+                       if (!reg)
+                               printk(KERN_ERR
+                                      "Error: couldn't map PORDEVSR2\n");
+                       else
+                               val = in_be32(reg) & 0x00000080; /* sec-cfg */
+                       iounmap(reg);
+               }
+       }
+       if (node)
+               of_node_put(node);
+
+       return val;
 }
 
+int mpc_i2c_get_fdr_8xxx(struct device_node *node, u32 clock, u32 prescaler)
+{
+       const struct mpc_i2c_divider *div = NULL;
+       u32 divider;
+       int i;
+
+       if (!clock)
+               return -EINVAL;
+
+       /* Determine proper divider value */
+       if (of_device_is_compatible(node, "fsl,mpc8544-i2c"))
+               prescaler = mpc_i2c_get_sec_cfg_8xxx() ? 3 : 2;
+       if (!prescaler)
+               prescaler = 1;
+
+       divider = fsl_get_sys_freq() / clock / prescaler;
+
+       pr_debug("I2C: src_clock=%d clock=%d divider=%d\n",
+                fsl_get_sys_freq(), clock, divider);
+
+       /*
+        * We want to choose an FDR/DFSR that generates an I2C bus speed that
+        * is equal to or lower than the requested speed.
+        */
+       for (i = 0; i < ARRAY_SIZE(mpc_i2c_dividers_8xxx); i++) {
+               div = &mpc_i2c_dividers_8xxx[i];
+               if (div->divider >= divider)
+                       break;
+       }
+
+       return div ? (int)div->fdr : -EINVAL;
+}
+
+static void mpc_i2c_setclock_8xxx(struct device_node *node,
+                                 struct mpc_i2c *i2c,
+                                 u32 clock, u32 prescaler)
+{
+       int fdr = mpc_i2c_get_fdr_8xxx(node, clock, prescaler);
+
+       if (fdr < 0)
+               fdr = 0x1031; /* backward compatibility */
+       writeb(fdr & 0xff, i2c->base + MPC_I2C_FDR);
+       writeb((fdr >> 8) & 0xff, i2c->base + MPC_I2C_DFSRR);
+       dev_info(i2c->dev, "clock %d Hz (dfsrr=%d fdr=%d)\n",
+                clock, fdr >> 8, fdr & 0xff);
+}
+
+#else /* !CONFIG_FSL_SOC */
+static void mpc_i2c_setclock_8xxx(struct device_node *node,
+                                 struct mpc_i2c *i2c,
+                                 u32 clock, u32 prescaler)
+{
+}
+#endif /* CONFIG_FSL_SOC */
+
 static void mpc_i2c_start(struct mpc_i2c *i2c)
 {
        /* Clear arbitration */
@@ -176,7 +353,7 @@ static void mpc_i2c_stop(struct mpc_i2c *i2c)
 }
 
 static int mpc_write(struct mpc_i2c *i2c, int target,
-                    const u8 * data, int length, int restart)
+                    const u8 *data, int length, int restart)
 {
        int i, result;
        unsigned timeout = i2c->adap.timeout;
@@ -207,7 +384,7 @@ static int mpc_write(struct mpc_i2c *i2c, int target,
 }
 
 static int mpc_read(struct mpc_i2c *i2c, int target,
-                   u8 * data, int length, int restart)
+                   u8 *data, int length, int restart)
 {
        unsigned timeout = i2c->adap.timeout;
        int i, result;
@@ -264,12 +441,12 @@ static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
        /* Allow bus up to 1s to become not busy */
        while (readb(i2c->base + MPC_I2C_SR) & CSR_MBB) {
                if (signal_pending(current)) {
-                       pr_debug("I2C: Interrupted\n");
+                       dev_dbg(i2c->dev, "Interrupted\n");
                        writeccr(i2c, 0);
                        return -EINTR;
                }
                if (time_after(jiffies, orig_jiffies + HZ)) {
-                       pr_debug("I2C: timeout\n");
+                       dev_dbg(i2c->dev, "timeout\n");
                        if (readb(i2c->base + MPC_I2C_SR) ==
                            (CSR_MCF | CSR_MBB | CSR_RXAK))
                                mpc_i2c_fixup(i2c);
@@ -280,9 +457,10 @@ static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
 
        for (i = 0; ret >= 0 && i < num; i++) {
                pmsg = &msgs[i];
-               pr_debug("Doing %s %d bytes to 0x%02x - %d of %d messages\n",
-                        pmsg->flags & I2C_M_RD ? "read" : "write",
-                        pmsg->len, pmsg->addr, i + 1, num);
+               dev_dbg(i2c->dev,
+                       "Doing %s %d bytes to 0x%02x - %d of %d messages\n",
+                       pmsg->flags & I2C_M_RD ? "read" : "write",
+                       pmsg->len, pmsg->addr, i + 1, num);
                if (pmsg->flags & I2C_M_RD)
                        ret =
                            mpc_read(i2c, pmsg->addr, pmsg->buf, pmsg->len, i);
@@ -311,27 +489,26 @@ static struct i2c_adapter mpc_ops = {
        .timeout = HZ,
 };
 
-static int __devinit fsl_i2c_probe(struct of_device *op, const struct of_device_id *match)
+static int __devinit fsl_i2c_probe(struct of_device *op,
+                                  const struct of_device_id *match)
 {
-       int result = 0;
        struct mpc_i2c *i2c;
+       const u32 *prop;
+       u32 clock = 0;
+       int result = 0;
+       int plen;
 
        i2c = kzalloc(sizeof(*i2c), GFP_KERNEL);
        if (!i2c)
                return -ENOMEM;
 
-       if (of_get_property(op->node, "dfsrr", NULL))
-               i2c->flags |= FSL_I2C_DEV_SEPARATE_DFSRR;
-
-       if (of_device_is_compatible(op->node, "fsl,mpc5200-i2c") ||
-                       of_device_is_compatible(op->node, "mpc5200-i2c"))
-               i2c->flags |= FSL_I2C_DEV_CLOCK_5200;
+       i2c->dev = &op->dev; /* for debug and error output */
 
        init_waitqueue_head(&i2c->queue);
 
        i2c->base = of_iomap(op->node, 0);
        if (!i2c->base) {
-               printk(KERN_ERR "i2c-mpc - failed to map controller\n");
+               dev_err(i2c->dev, "failed to map controller\n");
                result = -ENOMEM;
                goto fail_map;
        }
@@ -341,12 +518,27 @@ static int __devinit fsl_i2c_probe(struct of_device *op, const struct of_device_
                result = request_irq(i2c->irq, mpc_i2c_isr,
                                     IRQF_SHARED, "i2c-mpc", i2c);
                if (result < 0) {
-                       printk(KERN_ERR "i2c-mpc - failed to attach interrupt\n");
+                       dev_err(i2c->dev, "failed to attach interrupt\n");
                        goto fail_request;
                }
        }
-       
-       mpc_i2c_setclock(i2c);
+
+       if (!of_get_property(op->node, "fsl,preserve-clocking", NULL)) {
+               prop = of_get_property(op->node, "clock-frequency", &plen);
+               if (prop && plen == sizeof(u32))
+                       clock = *prop;
+
+               if (match->data) {
+                       struct mpc_i2c_match_data *data =
+                               (struct mpc_i2c_match_data *)match->data;
+                       data->setclock(op->node, i2c, clock, data->prescaler);
+               } else {
+                       /* Backwards compatibility */
+                       if (of_get_property(op->node, "dfsrr", NULL))
+                               mpc_i2c_setclock_8xxx(op->node, i2c,
+                                                     clock, 0);
+               }
+       }
 
        dev_set_drvdata(&op->dev, i2c);
 
@@ -356,7 +548,7 @@ static int __devinit fsl_i2c_probe(struct of_device *op, const struct of_device_
 
        result = i2c_add_adapter(&i2c->adap);
        if (result < 0) {
-               printk(KERN_ERR "i2c-mpc - failed to add adapter\n");
+               dev_err(i2c->dev, "failed to add adapter\n");
                goto fail_add;
        }
        of_register_i2c_devices(&i2c->adap, op->node);
@@ -368,7 +560,7 @@ static int __devinit fsl_i2c_probe(struct of_device *op, const struct of_device_
        free_irq(i2c->irq, i2c);
  fail_request:
        irq_dispose_mapping(i2c->irq);
-       iounmap(i2c->base);
+       iounmap(i2c->base);
  fail_map:
        kfree(i2c);
        return result;
@@ -391,9 +583,43 @@ static int __devexit fsl_i2c_remove(struct of_device *op)
 };
 
 static const struct of_device_id mpc_i2c_of_match[] = {
-       {.compatible = "fsl-i2c",},
+       {.compatible = "mpc5200-i2c",
+        .data = &(struct mpc_i2c_match_data) {
+                       .setclock = mpc_i2c_setclock_52xx,
+               },
+       },
+       {.compatible = "fsl,mpc5200b-i2c",
+        .data = &(struct mpc_i2c_match_data) {
+                       .setclock = mpc_i2c_setclock_52xx,
+               },
+       },
+       {.compatible = "fsl,mpc5200-i2c",
+        .data = &(struct mpc_i2c_match_data) {
+                       .setclock = mpc_i2c_setclock_52xx,
+               },
+       },
+       {.compatible = "fsl,mpc8313-i2c",
+        .data = &(struct mpc_i2c_match_data) {
+                       .setclock = mpc_i2c_setclock_8xxx,
+               },
+       },
+       {.compatible = "fsl,mpc8543-i2c",
+        .data = &(struct mpc_i2c_match_data) {
+                       .setclock = mpc_i2c_setclock_8xxx,
+                       .prescaler = 2,
+               },
+       },
+       {.compatible = "fsl,mpc8544-i2c",
+        .data = &(struct mpc_i2c_match_data) {
+                       .setclock = mpc_i2c_setclock_8xxx,
+                       .prescaler = 3,
+               },
+       /* Backward compatibility */
+       },
+       {.compatible = "fsl-i2c", },
        {},
 };
+
 MODULE_DEVICE_TABLE(of, mpc_i2c_of_match);
 
 
@@ -414,7 +640,7 @@ static int __init fsl_i2c_init(void)
 
        rv = of_register_platform_driver(&mpc_i2c_driver);
        if (rv)
-               printk(KERN_ERR DRV_NAME 
+               printk(KERN_ERR DRV_NAME
                       " of_register_platform_driver failed (%i)\n", rv);
        return rv;
 }
@@ -428,6 +654,6 @@ module_init(fsl_i2c_init);
 module_exit(fsl_i2c_exit);
 
 MODULE_AUTHOR("Adrian Cox <adrian@humboldt.co.uk>");
-MODULE_DESCRIPTION
-    ("I2C-Bus adapter for MPC107 bridge and MPC824x/85xx/52xx processors");
+MODULE_DESCRIPTION("I2C-Bus adapter for MPC107 bridge and "
+                  "MPC824x/85xx/52xx processors");
 MODULE_LICENSE("GPL");
index 5b7f95641ba48119d17fe71c7fc02b8c3b16fd2d..1691ef0f1ee1ee64dcd9352cf785849b26592623 100644 (file)
@@ -1,6 +1,6 @@
 /* linux/drivers/i2c/busses/i2c-s3c2410.c
  *
- * Copyright (C) 2004,2005 Simtec Electronics
+ * Copyright (C) 2004,2005,2009 Simtec Electronics
  *     Ben Dooks <ben@simtec.co.uk>
  *
  * S3C2410 I2C Controller
@@ -590,18 +590,6 @@ static int s3c24xx_i2c_calcdivisor(unsigned long clkin, unsigned int wanted,
        return clkin / (calc_divs * calc_div1);
 }
 
-/* freq_acceptable
- *
- * test wether a frequency is within the acceptable range of error
-*/
-
-static inline int freq_acceptable(unsigned int freq, unsigned int wanted)
-{
-       int diff = freq - wanted;
-
-       return diff >= -2 && diff <= 2;
-}
-
 /* s3c24xx_i2c_clockrate
  *
  * work out a divisor for the user requested frequency setting,
@@ -614,44 +602,28 @@ static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got)
        struct s3c2410_platform_i2c *pdata = i2c->dev->platform_data;
        unsigned long clkin = clk_get_rate(i2c->clk);
        unsigned int divs, div1;
+       unsigned long target_frequency;
        u32 iiccon;
        int freq;
-       int start, end;
 
        i2c->clkrate = clkin;
        clkin /= 1000;          /* clkin now in KHz */
 
-       dev_dbg(i2c->dev, "pdata %p, freq %lu %lu..%lu\n",
-                pdata, pdata->bus_freq, pdata->min_freq, pdata->max_freq);
-
-       if (pdata->bus_freq != 0) {
-               freq = s3c24xx_i2c_calcdivisor(clkin, pdata->bus_freq/1000,
-                                              &div1, &divs);
-               if (freq_acceptable(freq, pdata->bus_freq/1000))
-                       goto found;
-       }
-
-       /* ok, we may have to search for something suitable... */
+       dev_dbg(i2c->dev, "pdata desired frequency %lu\n", pdata->frequency);
 
-       start = (pdata->max_freq == 0) ? pdata->bus_freq : pdata->max_freq;
-       end = pdata->min_freq;
+       target_frequency = pdata->frequency ? pdata->frequency : 100000;
 
-       start /= 1000;
-       end /= 1000;
+       target_frequency /= 1000; /* Target frequency now in KHz */
 
-       /* search loop... */
+       freq = s3c24xx_i2c_calcdivisor(clkin, target_frequency, &div1, &divs);
 
-       for (; start > end; start--) {
-               freq = s3c24xx_i2c_calcdivisor(clkin, start, &div1, &divs);
-               if (freq_acceptable(freq, start))
-                       goto found;
+       if (freq > target_frequency) {
+               dev_err(i2c->dev,
+                       "Unable to achieve desired frequency %luKHz."   \
+                       " Lowest achievable %dKHz\n", target_frequency, freq);
+               return -EINVAL;
        }
 
-       /* cannot find frequency spec */
-
-       return -EINVAL;
-
- found:
        *got = freq;
 
        iiccon = readl(i2c->regs + S3C2410_IICCON);
@@ -663,6 +635,23 @@ static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got)
 
        writel(iiccon, i2c->regs + S3C2410_IICCON);
 
+       if (s3c24xx_i2c_is2440(i2c)) {
+               unsigned long sda_delay;
+
+               if (pdata->sda_delay) {
+                       sda_delay = (freq / 1000) * pdata->sda_delay;
+                       sda_delay /= 1000000;
+                       sda_delay = DIV_ROUND_UP(sda_delay, 5);
+                       if (sda_delay > 3)
+                               sda_delay = 3;
+                       sda_delay |= S3C2410_IICLC_FILTER_ON;
+               } else
+                       sda_delay = 0;
+
+               dev_dbg(i2c->dev, "IICLC=%08lx\n", sda_delay);
+               writel(sda_delay, i2c->regs + S3C2440_IICLC);
+       }
+
        return 0;
 }
 
@@ -769,11 +758,8 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
 
        /* check for s3c2440 i2c controller  */
 
-       if (s3c24xx_i2c_is2440(i2c)) {
-               dev_dbg(i2c->dev, "S3C2440_IICLC=%08x\n", pdata->sda_delay);
-
-               writel(pdata->sda_delay, i2c->regs + S3C2440_IICLC);
-       }
+       if (s3c24xx_i2c_is2440(i2c))
+               writel(0x0, i2c->regs + S3C2440_IICLC);
 
        return 0;
 }
@@ -1018,14 +1004,13 @@ static int __init i2c_adap_s3c_init(void)
 
        return ret;
 }
+subsys_initcall(i2c_adap_s3c_init);
 
 static void __exit i2c_adap_s3c_exit(void)
 {
        platform_driver_unregister(&s3c2410_i2c_driver);
        platform_driver_unregister(&s3c2440_i2c_driver);
 }
-
-module_init(i2c_adap_s3c_init);
 module_exit(i2c_adap_s3c_exit);
 
 MODULE_DESCRIPTION("S3C24XX I2C Bus driver");
diff --git a/drivers/i2c/busses/i2c-s6000.c b/drivers/i2c/busses/i2c-s6000.c
new file mode 100644 (file)
index 0000000..c91359f
--- /dev/null
@@ -0,0 +1,407 @@
+/*
+ * drivers/i2c/busses/i2c-s6000.c
+ *
+ * Description: Driver for S6000 Family I2C Interface
+ * Copyright (c) 2008 emlix GmbH
+ * Author:     Oskar Schirmer <os@emlix.com>
+ *
+ * Partially based on i2c-bfin-twi.c driver by <sonic.zhang@analog.com>
+ * Copyright (c) 2005-2007 Analog Devices, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/i2c/s6000.h>
+#include <linux/timer.h>
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+#include "i2c-s6000.h"
+
+#define DRV_NAME "i2c-s6000"
+
+#define POLL_TIMEOUT   (2 * HZ)
+
+struct s6i2c_if {
+       u8 __iomem              *reg; /* memory mapped registers */
+       int                     irq;
+       spinlock_t              lock;
+       struct i2c_msg          *msgs; /* messages currently handled */
+       int                     msgs_num; /* nb of msgs to do */
+       int                     msgs_push; /* nb of msgs read/written */
+       int                     msgs_done; /* nb of msgs finally handled */
+       unsigned                push; /* nb of bytes read/written in msg */
+       unsigned                done; /* nb of bytes finally handled */
+       int                     timeout_count; /* timeout retries left */
+       struct timer_list       timeout_timer;
+       struct i2c_adapter      adap;
+       struct completion       complete;
+       struct clk              *clk;
+       struct resource         *res;
+};
+
+static inline u16 i2c_rd16(struct s6i2c_if *iface, unsigned n)
+{
+       return readw(iface->reg + (n));
+}
+
+static inline void i2c_wr16(struct s6i2c_if *iface, unsigned n, u16 v)
+{
+       writew(v, iface->reg + (n));
+}
+
+static inline u32 i2c_rd32(struct s6i2c_if *iface, unsigned n)
+{
+       return readl(iface->reg + (n));
+}
+
+static inline void i2c_wr32(struct s6i2c_if *iface, unsigned n, u32 v)
+{
+       writel(v, iface->reg + (n));
+}
+
+static struct s6i2c_if s6i2c_if;
+
+static void s6i2c_handle_interrupt(struct s6i2c_if *iface)
+{
+       if (i2c_rd16(iface, S6_I2C_INTRSTAT) & (1 << S6_I2C_INTR_TXABRT)) {
+               i2c_rd16(iface, S6_I2C_CLRTXABRT);
+               i2c_wr16(iface, S6_I2C_INTRMASK, 0);
+               complete(&iface->complete);
+               return;
+       }
+       if (iface->msgs_done >= iface->msgs_num) {
+               dev_err(&iface->adap.dev, "s6i2c: spurious I2C irq: %04x\n",
+                       i2c_rd16(iface, S6_I2C_INTRSTAT));
+               i2c_wr16(iface, S6_I2C_INTRMASK, 0);
+               return;
+       }
+       while ((iface->msgs_push < iface->msgs_num)
+           && (i2c_rd16(iface, S6_I2C_STATUS) & (1 << S6_I2C_STATUS_TFNF))) {
+               struct i2c_msg *m = &iface->msgs[iface->msgs_push];
+               if (!(m->flags & I2C_M_RD))
+                       i2c_wr16(iface, S6_I2C_DATACMD, m->buf[iface->push]);
+               else
+                       i2c_wr16(iface, S6_I2C_DATACMD,
+                                1 << S6_I2C_DATACMD_READ);
+               if (++iface->push >= m->len) {
+                       iface->push = 0;
+                       iface->msgs_push += 1;
+               }
+       }
+       do {
+               struct i2c_msg *m = &iface->msgs[iface->msgs_done];
+               if (!(m->flags & I2C_M_RD)) {
+                       if (iface->msgs_done < iface->msgs_push)
+                               iface->msgs_done += 1;
+                       else
+                               break;
+               } else if (i2c_rd16(iface, S6_I2C_STATUS)
+                               & (1 << S6_I2C_STATUS_RFNE)) {
+                       m->buf[iface->done] = i2c_rd16(iface, S6_I2C_DATACMD);
+                       if (++iface->done >= m->len) {
+                               iface->done = 0;
+                               iface->msgs_done += 1;
+                       }
+               } else{
+                       break;
+               }
+       } while (iface->msgs_done < iface->msgs_num);
+       if (iface->msgs_done >= iface->msgs_num) {
+               i2c_wr16(iface, S6_I2C_INTRMASK, 1 << S6_I2C_INTR_TXABRT);
+               complete(&iface->complete);
+       } else if (iface->msgs_push >= iface->msgs_num) {
+               i2c_wr16(iface, S6_I2C_INTRMASK, (1 << S6_I2C_INTR_TXABRT) |
+                                                (1 << S6_I2C_INTR_RXFULL));
+       } else {
+               i2c_wr16(iface, S6_I2C_INTRMASK, (1 << S6_I2C_INTR_TXABRT) |
+                                                (1 << S6_I2C_INTR_TXEMPTY) |
+                                                (1 << S6_I2C_INTR_RXFULL));
+       }
+}
+
+static irqreturn_t s6i2c_interrupt_entry(int irq, void *dev_id)
+{
+       struct s6i2c_if *iface = dev_id;
+       if (!(i2c_rd16(iface, S6_I2C_STATUS) & ((1 << S6_I2C_INTR_RXUNDER)
+                                             | (1 << S6_I2C_INTR_RXOVER)
+                                             | (1 << S6_I2C_INTR_RXFULL)
+                                             | (1 << S6_I2C_INTR_TXOVER)
+                                             | (1 << S6_I2C_INTR_TXEMPTY)
+                                             | (1 << S6_I2C_INTR_RDREQ)
+                                             | (1 << S6_I2C_INTR_TXABRT)
+                                             | (1 << S6_I2C_INTR_RXDONE)
+                                             | (1 << S6_I2C_INTR_ACTIVITY)
+                                             | (1 << S6_I2C_INTR_STOPDET)
+                                             | (1 << S6_I2C_INTR_STARTDET)
+                                             | (1 << S6_I2C_INTR_GENCALL))))
+               return IRQ_NONE;
+
+       spin_lock(&iface->lock);
+       del_timer(&iface->timeout_timer);
+       s6i2c_handle_interrupt(iface);
+       spin_unlock(&iface->lock);
+       return IRQ_HANDLED;
+}
+
+static void s6i2c_timeout(unsigned long data)
+{
+       struct s6i2c_if *iface = (struct s6i2c_if *)data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&iface->lock, flags);
+       s6i2c_handle_interrupt(iface);
+       if (--iface->timeout_count > 0) {
+               iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+               add_timer(&iface->timeout_timer);
+       } else {
+               complete(&iface->complete);
+               i2c_wr16(iface, S6_I2C_INTRMASK, 0);
+       }
+       spin_unlock_irqrestore(&iface->lock, flags);
+}
+
+static int s6i2c_master_xfer(struct i2c_adapter *adap,
+                               struct i2c_msg *msgs, int num)
+{
+       struct s6i2c_if *iface = adap->algo_data;
+       int i;
+       if (num == 0)
+               return 0;
+       if (i2c_rd16(iface, S6_I2C_STATUS) & (1 << S6_I2C_STATUS_ACTIVITY))
+               yield();
+       i2c_wr16(iface, S6_I2C_INTRMASK, 0);
+       i2c_rd16(iface, S6_I2C_CLRINTR);
+       for (i = 0; i < num; i++) {
+               if (msgs[i].flags & I2C_M_TEN) {
+                       dev_err(&adap->dev,
+                               "s6i2c: 10 bits addr not supported\n");
+                       return -EINVAL;
+               }
+               if (msgs[i].len == 0) {
+                       dev_err(&adap->dev,
+                               "s6i2c: zero length message not supported\n");
+                       return -EINVAL;
+               }
+               if (msgs[i].addr != msgs[0].addr) {
+                       dev_err(&adap->dev,
+                               "s6i2c: multiple xfer cannot change target\n");
+                       return -EINVAL;
+               }
+       }
+
+       iface->msgs = msgs;
+       iface->msgs_num = num;
+       iface->msgs_push = 0;
+       iface->msgs_done = 0;
+       iface->push = 0;
+       iface->done = 0;
+       iface->timeout_count = 10;
+       i2c_wr16(iface, S6_I2C_TAR, msgs[0].addr);
+       i2c_wr16(iface, S6_I2C_ENABLE, 1);
+       i2c_wr16(iface, S6_I2C_INTRMASK, (1 << S6_I2C_INTR_TXEMPTY) |
+                                        (1 << S6_I2C_INTR_TXABRT));
+
+       iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+       add_timer(&iface->timeout_timer);
+       wait_for_completion(&iface->complete);
+       del_timer_sync(&iface->timeout_timer);
+       while (i2c_rd32(iface, S6_I2C_TXFLR) > 0)
+               schedule();
+       while (i2c_rd16(iface, S6_I2C_STATUS) & (1 << S6_I2C_STATUS_ACTIVITY))
+               schedule();
+
+       i2c_wr16(iface, S6_I2C_INTRMASK, 0);
+       i2c_wr16(iface, S6_I2C_ENABLE, 0);
+       return iface->msgs_done;
+}
+
+static u32 s6i2c_functionality(struct i2c_adapter *adap)
+{
+       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm s6i2c_algorithm = {
+       .master_xfer   = s6i2c_master_xfer,
+       .functionality = s6i2c_functionality,
+};
+
+static u16 __devinit nanoseconds_on_clk(struct s6i2c_if *iface, u32 ns)
+{
+       u32 dividend = ((clk_get_rate(iface->clk) / 1000) * ns) / 1000000;
+       if (dividend > 0xffff)
+               return 0xffff;
+       return dividend;
+}
+
+static int __devinit s6i2c_probe(struct platform_device *dev)
+{
+       struct s6i2c_if *iface = &s6i2c_if;
+       struct i2c_adapter *p_adap;
+       const char *clock;
+       int bus_num, rc;
+       spin_lock_init(&iface->lock);
+       init_completion(&iface->complete);
+       iface->irq = platform_get_irq(dev, 0);
+       if (iface->irq < 0) {
+               rc = iface->irq;
+               goto err_out;
+       }
+       iface->res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+       if (!iface->res) {
+               rc = -ENXIO;
+               goto err_out;
+       }
+       iface->res = request_mem_region(iface->res->start,
+                                       resource_size(iface->res),
+                                       dev->dev.bus_id);
+       if (!iface->res) {
+               rc = -EBUSY;
+               goto err_out;
+       }
+       iface->reg = ioremap_nocache(iface->res->start,
+                                    resource_size(iface->res));
+       if (!iface->reg) {
+               rc = -ENOMEM;
+               goto err_reg;
+       }
+
+       clock = 0;
+       bus_num = -1;
+       if (dev->dev.platform_data) {
+               struct s6_i2c_platform_data *pdata = dev->dev.platform_data;
+               bus_num = pdata->bus_num;
+               clock = pdata->clock;
+       }
+       iface->clk = clk_get(&dev->dev, clock);
+       if (IS_ERR(iface->clk)) {
+               rc = PTR_ERR(iface->clk);
+               goto err_map;
+       }
+       rc = clk_enable(iface->clk);
+       if (rc < 0)
+               goto err_clk_put;
+       init_timer(&iface->timeout_timer);
+       iface->timeout_timer.function = s6i2c_timeout;
+       iface->timeout_timer.data = (unsigned long)iface;
+
+       p_adap = &iface->adap;
+       strlcpy(p_adap->name, dev->name, sizeof(p_adap->name));
+       p_adap->algo = &s6i2c_algorithm;
+       p_adap->algo_data = iface;
+       p_adap->nr = bus_num;
+       p_adap->class = 0;
+       p_adap->dev.parent = &dev->dev;
+       i2c_wr16(iface, S6_I2C_INTRMASK, 0);
+       rc = request_irq(iface->irq, s6i2c_interrupt_entry,
+                        IRQF_SHARED, dev->name, iface);
+       if (rc) {
+               dev_err(&p_adap->dev, "s6i2c: cant get IRQ %d\n", iface->irq);
+               goto err_clk_dis;
+       }
+
+       i2c_wr16(iface, S6_I2C_ENABLE, 0);
+       udelay(1);
+       i2c_wr32(iface, S6_I2C_SRESET, 1 << S6_I2C_SRESET_IC_SRST);
+       i2c_wr16(iface, S6_I2C_CLRTXABRT, 1);
+       i2c_wr16(iface, S6_I2C_CON,
+                       (1 << S6_I2C_CON_MASTER) |
+                       (S6_I2C_CON_SPEED_NORMAL << S6_I2C_CON_SPEED) |
+                       (0 << S6_I2C_CON_10BITSLAVE) |
+                       (0 << S6_I2C_CON_10BITMASTER) |
+                       (1 << S6_I2C_CON_RESTARTENA) |
+                       (1 << S6_I2C_CON_SLAVEDISABLE));
+       i2c_wr16(iface, S6_I2C_SSHCNT, nanoseconds_on_clk(iface, 4000));
+       i2c_wr16(iface, S6_I2C_SSLCNT, nanoseconds_on_clk(iface, 4700));
+       i2c_wr16(iface, S6_I2C_FSHCNT, nanoseconds_on_clk(iface, 600));
+       i2c_wr16(iface, S6_I2C_FSLCNT, nanoseconds_on_clk(iface, 1300));
+       i2c_wr16(iface, S6_I2C_RXTL, 0);
+       i2c_wr16(iface, S6_I2C_TXTL, 0);
+
+       platform_set_drvdata(dev, iface);
+       if (bus_num < 0)
+               rc = i2c_add_adapter(p_adap);
+       else
+               rc = i2c_add_numbered_adapter(p_adap);
+       if (rc)
+               goto err_irq_free;
+       return 0;
+
+err_irq_free:
+       free_irq(iface->irq, iface);
+err_clk_dis:
+       clk_disable(iface->clk);
+err_clk_put:
+       clk_put(iface->clk);
+err_map:
+       iounmap(iface->reg);
+err_reg:
+       release_mem_region(iface->res->start,
+                          resource_size(iface->res));
+err_out:
+       return rc;
+}
+
+static int __devexit s6i2c_remove(struct platform_device *pdev)
+{
+       struct s6i2c_if *iface = platform_get_drvdata(pdev);
+       i2c_wr16(iface, S6_I2C_ENABLE, 0);
+       platform_set_drvdata(pdev, NULL);
+       i2c_del_adapter(&iface->adap);
+       free_irq(iface->irq, iface);
+       clk_disable(iface->clk);
+       clk_put(iface->clk);
+       iounmap(iface->reg);
+       release_mem_region(iface->res->start,
+                          resource_size(iface->res));
+       return 0;
+}
+
+static struct platform_driver s6i2c_driver = {
+       .probe          = s6i2c_probe,
+       .remove         = __devexit_p(s6i2c_remove),
+       .driver         = {
+               .name   = DRV_NAME,
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init s6i2c_init(void)
+{
+       pr_info("I2C: S6000 I2C driver\n");
+       return platform_driver_register(&s6i2c_driver);
+}
+
+static void __exit s6i2c_exit(void)
+{
+       platform_driver_unregister(&s6i2c_driver);
+}
+
+MODULE_DESCRIPTION("I2C-Bus adapter routines for S6000 I2C");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
+
+subsys_initcall(s6i2c_init);
+module_exit(s6i2c_exit);
diff --git a/drivers/i2c/busses/i2c-s6000.h b/drivers/i2c/busses/i2c-s6000.h
new file mode 100644 (file)
index 0000000..ff23b81
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * drivers/i2c/busses/i2c-s6000.h
+ *
+ * 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.
+ *
+ * Copyright (C) 2008 Emlix GmbH <info@emlix.com>
+ * Author:     Oskar Schirmer <os@emlix.com>
+ */
+
+#ifndef __DRIVERS_I2C_BUSSES_I2C_S6000_H
+#define __DRIVERS_I2C_BUSSES_I2C_S6000_H
+
+#define S6_I2C_CON             0x000
+#define S6_I2C_CON_MASTER              0
+#define S6_I2C_CON_SPEED               1
+#define S6_I2C_CON_SPEED_NORMAL                        1
+#define S6_I2C_CON_SPEED_FAST                  2
+#define S6_I2C_CON_SPEED_MASK                  3
+#define S6_I2C_CON_10BITSLAVE          3
+#define S6_I2C_CON_10BITMASTER         4
+#define S6_I2C_CON_RESTARTENA          5
+#define S6_I2C_CON_SLAVEDISABLE                6
+#define S6_I2C_TAR             0x004
+#define S6_I2C_TAR_GCORSTART           10
+#define S6_I2C_TAR_SPECIAL             11
+#define S6_I2C_SAR             0x008
+#define S6_I2C_HSMADDR         0x00C
+#define S6_I2C_DATACMD         0x010
+#define S6_I2C_DATACMD_READ            8
+#define S6_I2C_SSHCNT          0x014
+#define S6_I2C_SSLCNT          0x018
+#define S6_I2C_FSHCNT          0x01C
+#define S6_I2C_FSLCNT          0x020
+#define S6_I2C_INTRSTAT                0x02C
+#define S6_I2C_INTRMASK                0x030
+#define S6_I2C_RAWINTR         0x034
+#define S6_I2C_INTR_RXUNDER            0
+#define S6_I2C_INTR_RXOVER             1
+#define S6_I2C_INTR_RXFULL             2
+#define S6_I2C_INTR_TXOVER             3
+#define S6_I2C_INTR_TXEMPTY            4
+#define S6_I2C_INTR_RDREQ              5
+#define S6_I2C_INTR_TXABRT             6
+#define S6_I2C_INTR_RXDONE             7
+#define S6_I2C_INTR_ACTIVITY           8
+#define S6_I2C_INTR_STOPDET            9
+#define S6_I2C_INTR_STARTDET           10
+#define S6_I2C_INTR_GENCALL            11
+#define S6_I2C_RXTL            0x038
+#define S6_I2C_TXTL            0x03C
+#define S6_I2C_CLRINTR         0x040
+#define S6_I2C_CLRRXUNDER      0x044
+#define S6_I2C_CLRRXOVER       0x048
+#define S6_I2C_CLRTXOVER       0x04C
+#define S6_I2C_CLRRDREQ                0x050
+#define S6_I2C_CLRTXABRT       0x054
+#define S6_I2C_CLRRXDONE       0x058
+#define S6_I2C_CLRACTIVITY     0x05C
+#define S6_I2C_CLRSTOPDET      0x060
+#define S6_I2C_CLRSTARTDET     0x064
+#define S6_I2C_CLRGENCALL      0x068
+#define S6_I2C_ENABLE          0x06C
+#define S6_I2C_STATUS          0x070
+#define S6_I2C_STATUS_ACTIVITY         0
+#define S6_I2C_STATUS_TFNF             1
+#define S6_I2C_STATUS_TFE              2
+#define S6_I2C_STATUS_RFNE             3
+#define S6_I2C_STATUS_RFF              4
+#define S6_I2C_TXFLR           0x074
+#define S6_I2C_RXFLR           0x078
+#define S6_I2C_SRESET          0x07C
+#define S6_I2C_SRESET_IC_SRST          0
+#define S6_I2C_SRESET_IC_MASTER_SRST   1
+#define S6_I2C_SRESET_IC_SLAVE_SRST    2
+#define S6_I2C_TXABRTSOURCE    0x080
+
+#endif
diff --git a/include/linux/i2c/s6000.h b/include/linux/i2c/s6000.h
new file mode 100644 (file)
index 0000000..d9b34bf
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef __LINUX_I2C_S6000_H
+#define __LINUX_I2C_S6000_H
+
+struct s6_i2c_platform_data {
+       const char *clock; /* the clock to use */
+       int bus_num; /* the bus number to register */
+};
+
+#endif
+