i2c-s3c2410: Simplify bus frequency calculation
The platform data for the i2c-s3c2410 driver used to allow a min, max and desired frequency for the I2C bus. This patch reduces it to simply a desired frequency ceiling and corrects all the uses of the platform data appropriately. This means, for example, that on a system with a 66MHz fclk, a request for 100KHz will achieve 65KHz which is safe and acceptable, rather than 378KHz which it would have achieved without this change. Signed-off-by: Simtec Linux Team <linux@simtec.co.uk> Signed-off-by: Daniel Silverstone <dsilvers@simtec.co.uk> [ben-linux@fluff.org: tidy subject and description] Signed-off-by: Ben Dooks <ben-linux@fluff.org>
This commit is contained in:
committed by
Ben Dooks
parent
a192f7153b
commit
c564e6ae6c
@@ -409,8 +409,7 @@ static struct platform_device bast_sio = {
|
|||||||
static struct s3c2410_platform_i2c __initdata bast_i2c_info = {
|
static struct s3c2410_platform_i2c __initdata bast_i2c_info = {
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
.slave_addr = 0x10,
|
.slave_addr = 0x10,
|
||||||
.bus_freq = 100*1000,
|
.frequency = 100*1000,
|
||||||
.max_freq = 130*1000,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Asix AX88796 10/100 ethernet controller */
|
/* Asix AX88796 10/100 ethernet controller */
|
||||||
|
@@ -340,8 +340,7 @@ static struct platform_device *n35_devices[] __initdata = {
|
|||||||
static struct s3c2410_platform_i2c n30_i2ccfg = {
|
static struct s3c2410_platform_i2c n30_i2ccfg = {
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
.slave_addr = 0x10,
|
.slave_addr = 0x10,
|
||||||
.bus_freq = 10*1000,
|
.frequency = 10*1000,
|
||||||
.max_freq = 10*1000,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Lots of hardcoded stuff, but it sets up the hardware in a useful
|
/* 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. */
|
/* I2C bus and device configuration. */
|
||||||
|
|
||||||
static struct s3c2410_platform_i2c jive_i2c_cfg __initdata = {
|
static struct s3c2410_platform_i2c jive_i2c_cfg __initdata = {
|
||||||
.max_freq = 80 * 1000,
|
.frequency = 80 * 1000,
|
||||||
.bus_freq = 50 * 1000,
|
|
||||||
.flags = S3C_IICFLG_FILTER,
|
.flags = S3C_IICFLG_FILTER,
|
||||||
.sda_delay = 2,
|
.sda_delay = 2,
|
||||||
};
|
};
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
/* linux/arch/arm/plat-s3c/dev-i2c0.c
|
/* linux/arch/arm/plat-s3c/dev-i2c0.c
|
||||||
*
|
*
|
||||||
* Copyright 2008 Simtec Electronics
|
* Copyright 2008,2009 Simtec Electronics
|
||||||
* Ben Dooks <ben@simtec.co.uk>
|
* Ben Dooks <ben@simtec.co.uk>
|
||||||
* http://armlinux.simtec.co.uk/
|
* http://armlinux.simtec.co.uk/
|
||||||
*
|
*
|
||||||
@@ -50,8 +50,7 @@ struct platform_device s3c_device_i2c0 = {
|
|||||||
static struct s3c2410_platform_i2c default_i2c_data0 __initdata = {
|
static struct s3c2410_platform_i2c default_i2c_data0 __initdata = {
|
||||||
.flags = 0,
|
.flags = 0,
|
||||||
.slave_addr = 0x10,
|
.slave_addr = 0x10,
|
||||||
.bus_freq = 100*1000,
|
.frequency = 100*1000,
|
||||||
.max_freq = 400*1000,
|
|
||||||
.sda_delay = 100,
|
.sda_delay = 100,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
/* linux/arch/arm/plat-s3c/dev-i2c1.c
|
/* linux/arch/arm/plat-s3c/dev-i2c1.c
|
||||||
*
|
*
|
||||||
* Copyright 2008 Simtec Electronics
|
* Copyright 2008,2009 Simtec Electronics
|
||||||
* Ben Dooks <ben@simtec.co.uk>
|
* Ben Dooks <ben@simtec.co.uk>
|
||||||
* http://armlinux.simtec.co.uk/
|
* http://armlinux.simtec.co.uk/
|
||||||
*
|
*
|
||||||
@@ -47,8 +47,7 @@ static struct s3c2410_platform_i2c default_i2c_data1 __initdata = {
|
|||||||
.flags = 0,
|
.flags = 0,
|
||||||
.bus_num = 1,
|
.bus_num = 1,
|
||||||
.slave_addr = 0x10,
|
.slave_addr = 0x10,
|
||||||
.bus_freq = 100*1000,
|
.frequency = 100*1000,
|
||||||
.max_freq = 400*1000,
|
|
||||||
.sda_delay = 100,
|
.sda_delay = 100,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -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>
|
* 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
|
* 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
|
* 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 */
|
#define S3C_IICFLG_FILTER (1<<0) /* enable s3c2440 filter */
|
||||||
|
|
||||||
/* Notes:
|
/**
|
||||||
* 1) All frequencies are expressed in Hz
|
* struct s3c2410_platform_i2c - Platform data for s3c I2C.
|
||||||
* 2) A value of zero is `do not care`
|
* @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 {
|
struct s3c2410_platform_i2c {
|
||||||
int bus_num; /* bus number to use */
|
int bus_num;
|
||||||
unsigned int flags;
|
unsigned int flags;
|
||||||
unsigned int slave_addr; /* slave address for controller */
|
unsigned int slave_addr;
|
||||||
unsigned long bus_freq; /* standard bus frequency */
|
unsigned long frequency;
|
||||||
unsigned long max_freq; /* max frequency for the bus */
|
unsigned int sda_delay;
|
||||||
unsigned long min_freq; /* min frequency for the bus */
|
|
||||||
unsigned int sda_delay; /* pclks (s3c2440 only) */
|
|
||||||
|
|
||||||
void (*cfg_gpio)(struct platform_device *dev);
|
void (*cfg_gpio)(struct platform_device *dev);
|
||||||
};
|
};
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
/* linux/drivers/i2c/busses/i2c-s3c2410.c
|
/* 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>
|
* Ben Dooks <ben@simtec.co.uk>
|
||||||
*
|
*
|
||||||
* S3C2410 I2C Controller
|
* S3C2410 I2C Controller
|
||||||
@@ -590,18 +590,6 @@ static int s3c24xx_i2c_calcdivisor(unsigned long clkin, unsigned int wanted,
|
|||||||
return clkin / (calc_divs * calc_div1);
|
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
|
/* s3c24xx_i2c_clockrate
|
||||||
*
|
*
|
||||||
* work out a divisor for the user requested frequency setting,
|
* 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;
|
struct s3c2410_platform_i2c *pdata = i2c->dev->platform_data;
|
||||||
unsigned long clkin = clk_get_rate(i2c->clk);
|
unsigned long clkin = clk_get_rate(i2c->clk);
|
||||||
unsigned int divs, div1;
|
unsigned int divs, div1;
|
||||||
|
unsigned long target_frequency;
|
||||||
u32 iiccon;
|
u32 iiccon;
|
||||||
int freq;
|
int freq;
|
||||||
int start, end;
|
|
||||||
|
|
||||||
i2c->clkrate = clkin;
|
i2c->clkrate = clkin;
|
||||||
clkin /= 1000; /* clkin now in KHz */
|
clkin /= 1000; /* clkin now in KHz */
|
||||||
|
|
||||||
dev_dbg(i2c->dev, "pdata %p, freq %lu %lu..%lu\n",
|
dev_dbg(i2c->dev, "pdata desired frequency %lu\n", pdata->frequency);
|
||||||
pdata, pdata->bus_freq, pdata->min_freq, pdata->max_freq);
|
|
||||||
|
|
||||||
if (pdata->bus_freq != 0) {
|
target_frequency = pdata->frequency ? pdata->frequency : 100000;
|
||||||
freq = s3c24xx_i2c_calcdivisor(clkin, pdata->bus_freq/1000,
|
|
||||||
&div1, &divs);
|
target_frequency /= 1000; /* Target frequency now in KHz */
|
||||||
if (freq_acceptable(freq, pdata->bus_freq/1000))
|
|
||||||
goto found;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ok, we may have to search for something suitable... */
|
|
||||||
|
|
||||||
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 */
|
|
||||||
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
found:
|
|
||||||
*got = freq;
|
*got = freq;
|
||||||
|
|
||||||
iiccon = readl(i2c->regs + S3C2410_IICCON);
|
iiccon = readl(i2c->regs + S3C2410_IICCON);
|
||||||
|
Reference in New Issue
Block a user