Merge branch 'i2c-for-2630-v2' of git://aeryn.fluff.org.uk/bjdooks/linux
* '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
This commit is contained in:
commit
0e26da0f22
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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_ */
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
@ -15,19 +15,24 @@
|
|||
|
||||
#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);
|
||||
};
|
||||
|
|
|
@ -79,11 +79,11 @@
|
|||
#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";
|
||||
|
@ -111,11 +111,11 @@
|
|||
#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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
|
@ -20,12 +20,15 @@
|
|||
#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
|
||||
|
@ -50,15 +53,27 @@
|
|||
#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)
|
||||
{
|
||||
/* 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);
|
||||
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)
|
||||
{
|
||||
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 */
|
||||
|
@ -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,7 +457,8 @@ 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",
|
||||
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)
|
||||
|
@ -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);
|
||||
|
@ -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 = "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);
|
||||
|
||||
|
||||
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
dev_dbg(i2c->dev, "pdata desired frequency %lu\n", pdata->frequency);
|
||||
|
||||
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;
|
||||
}
|
||||
target_frequency = pdata->frequency ? pdata->frequency : 100000;
|
||||
|
||||
/* ok, we may have to search for something suitable... */
|
||||
target_frequency /= 1000; /* Target frequency now in KHz */
|
||||
|
||||
start = (pdata->max_freq == 0) ? pdata->bus_freq : pdata->max_freq;
|
||||
end = pdata->min_freq;
|
||||
|
||||
start /= 1000;
|
||||
end /= 1000;
|
||||
|
||||
/* search loop... */
|
||||
|
||||
for (; start > end; start--) {
|
||||
freq = s3c24xx_i2c_calcdivisor(clkin, start, &div1, &divs);
|
||||
if (freq_acceptable(freq, start))
|
||||
goto found;
|
||||
}
|
||||
|
||||
/* cannot find frequency spec */
|
||||
freq = s3c24xx_i2c_calcdivisor(clkin, target_frequency, &div1, &divs);
|
||||
|
||||
if (freq > target_frequency) {
|
||||
dev_err(i2c->dev,
|
||||
"Unable to achieve desired frequency %luKHz." \
|
||||
" Lowest achievable %dKHz\n", target_frequency, freq);
|
||||
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");
|
||||
|
|
|
@ -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);
|
|
@ -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
|
|
@ -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
|
||||
|
Loading…
Reference in New Issue