Merge branch 'for-next/i2c' into for-next/at91
This commit is contained in:
commit
18f044436e
|
@ -47,6 +47,7 @@ mem_initcall(vpb_mem_init);
|
|||
static int vpb_devices_init(void)
|
||||
{
|
||||
add_cfi_flash_device(DEVICE_ID_DYNAMIC, VERSATILE_FLASH_BASE, VERSATILE_FLASH_SIZE, 0);
|
||||
versatile_register_i2c();
|
||||
devfs_add_partition("nor0", 0x00000, 0x40000, DEVFS_PARTITION_FIXED, "self");
|
||||
devfs_add_partition("nor0", 0x40000, 0x20000, DEVFS_PARTITION_FIXED, "env0");
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ CONFIG_CMD_GO=y
|
|||
CONFIG_CMD_TIMEOUT=y
|
||||
CONFIG_CMD_PARTITION=y
|
||||
CONFIG_CMD_UNCOMPRESS=y
|
||||
CONFIG_CMD_I2C=y
|
||||
CONFIG_NET=y
|
||||
CONFIG_NET_DHCP=y
|
||||
CONFIG_NET_NFS=y
|
||||
|
@ -43,6 +44,8 @@ CONFIG_NET_NETCONSOLE=y
|
|||
CONFIG_NET_RESOLV=y
|
||||
CONFIG_SERIAL_AMBA_PL011=y
|
||||
CONFIG_DRIVER_NET_SMC91111=y
|
||||
CONFIG_I2C=y
|
||||
CONFIG_I2C_VERSATILE=y
|
||||
CONFIG_FS_CRAMFS=y
|
||||
CONFIG_SHA1=y
|
||||
CONFIG_SHA256=y
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <init.h>
|
||||
#include <clock.h>
|
||||
#include <debug_ll.h>
|
||||
#include <sizes.h>
|
||||
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk.h>
|
||||
|
@ -182,6 +183,12 @@ void versatile_register_uart(unsigned id)
|
|||
amba_apb_device_add(NULL, "uart-pl011", id, start, 4096, NULL, 0);
|
||||
}
|
||||
|
||||
void versatile_register_i2c(void)
|
||||
{
|
||||
add_generic_device("versatile-i2c", DEVICE_ID_DYNAMIC, NULL,
|
||||
VERSATILE_I2C_BASE, SZ_4K, IORESOURCE_MEM, NULL);
|
||||
}
|
||||
|
||||
void __noreturn reset_cpu (unsigned long ignored)
|
||||
{
|
||||
u32 val;
|
||||
|
|
|
@ -4,5 +4,6 @@
|
|||
|
||||
void versatile_add_sdram(u32 size);
|
||||
void versatile_register_uart(unsigned id);
|
||||
void versatile_register_i2c(void);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -8,4 +8,23 @@ config EEPROM_AT25
|
|||
after you configure the board init code to know about each eeprom
|
||||
on your target board.
|
||||
|
||||
config EEPROM_AT24
|
||||
bool "at24 based eeprom"
|
||||
depends on I2C
|
||||
help
|
||||
Enable this driver to get read/write support to most I2C EEPROMs,
|
||||
after you configure the driver to know about each EEPROM on
|
||||
your target board. Use these generic chip names, instead of
|
||||
vendor-specific ones like at24c64 or 24lc02:
|
||||
|
||||
24c00, 24c01, 24c02, spd (readonly 24c02), 24c04, 24c08,
|
||||
24c16, 24c32, 24c64, 24c128, 24c256, 24c512, 24c1024
|
||||
|
||||
Unless you like data loss puzzles, always be sure that any chip
|
||||
you configure as a 24c32 (32 kbit) or larger is NOT really a
|
||||
24c16 (16 kbit) or smaller, and vice versa. Marking the chip
|
||||
as read-only won't help recover from this. Also, if your chip
|
||||
has any software write-protect mechanism you may want to review the
|
||||
code to make sure this driver won't turn it on by accident.
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
obj-$(CONFIG_EEPROM_AT25) += at25.o
|
||||
obj-$(CONFIG_EEPROM_AT24) += at24.o
|
||||
|
|
|
@ -0,0 +1,460 @@
|
|||
/*
|
||||
* at24.c - handle most I2C EEPROMs
|
||||
*
|
||||
* Copyright (C) 2005-2007 David Brownell
|
||||
* Copyright (C) 2008 Wolfram Sang, Pengutronix
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <init.h>
|
||||
#include <malloc.h>
|
||||
#include <clock.h>
|
||||
#include <driver.h>
|
||||
#include <xfuncs.h>
|
||||
#include <errno.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/log2.h>
|
||||
#include <i2c/i2c.h>
|
||||
#include <i2c/at24.h>
|
||||
|
||||
/*
|
||||
* I2C EEPROMs from most vendors are inexpensive and mostly interchangeable.
|
||||
* Differences between different vendor product lines (like Atmel AT24C or
|
||||
* MicroChip 24LC, etc) won't much matter for typical read/write access.
|
||||
* There are also I2C RAM chips, likewise interchangeable. One example
|
||||
* would be the PCF8570, which acts like a 24c02 EEPROM (256 bytes).
|
||||
*
|
||||
* However, misconfiguration can lose data. "Set 16-bit memory address"
|
||||
* to a part with 8-bit addressing will overwrite data. Writing with too
|
||||
* big a page size also loses data. And it's not safe to assume that the
|
||||
* conventional addresses 0x50..0x57 only hold eeproms; a PCF8563 RTC
|
||||
* uses 0x51, for just one example.
|
||||
*
|
||||
* So this driver uses "new style" I2C driver binding, expecting to be
|
||||
* told what devices exist. That may be in arch/X/mach-Y/board-Z.c or
|
||||
* similar kernel-resident tables; or, configuration data coming from
|
||||
* a bootloader.
|
||||
*
|
||||
* Other than binding model, current differences from "eeprom" driver are
|
||||
* that this one handles write access and isn't restricted to 24c02 devices.
|
||||
* It also handles larger devices (32 kbit and up) with two-byte addresses,
|
||||
* which won't work on pure SMBus systems.
|
||||
*/
|
||||
|
||||
struct at24_data {
|
||||
struct at24_platform_data chip;
|
||||
|
||||
struct cdev cdev;
|
||||
struct file_operations fops;
|
||||
|
||||
u8 *writebuf;
|
||||
unsigned write_max;
|
||||
unsigned num_addresses;
|
||||
|
||||
/*
|
||||
* Some chips tie up multiple I2C addresses; dummy devices reserve
|
||||
* them for us.
|
||||
*/
|
||||
struct i2c_client *client[];
|
||||
};
|
||||
|
||||
/*
|
||||
* This parameter is to help this driver avoid blocking other drivers out
|
||||
* of I2C for potentially troublesome amounts of time. With a 100 kHz I2C
|
||||
* clock, one 256 byte read takes about 1/43 second which is excessive;
|
||||
* but the 1/170 second it takes at 400 kHz may be quite reasonable; and
|
||||
* at 1 MHz (Fm+) a 1/430 second delay could easily be invisible.
|
||||
*
|
||||
* This value is forced to be a power of two so that writes align on pages.
|
||||
*/
|
||||
static unsigned io_limit = 128;
|
||||
|
||||
/*
|
||||
* Specs often allow 5 msec for a page write, sometimes 20 msec;
|
||||
* it's important to recover from write timeouts.
|
||||
*/
|
||||
static unsigned write_timeout = 25;
|
||||
|
||||
#define AT24_SIZE_BYTELEN 5
|
||||
#define AT24_SIZE_FLAGS 8
|
||||
|
||||
#define AT24_BITMASK(x) (BIT(x) - 1)
|
||||
|
||||
/* create non-zero magic value for given eeprom parameters */
|
||||
#define AT24_DEVICE_MAGIC(_len, _flags) \
|
||||
((1 << AT24_SIZE_FLAGS | (_flags)) \
|
||||
<< AT24_SIZE_BYTELEN | ilog2(_len))
|
||||
|
||||
static struct platform_device_id at24_ids[] = {
|
||||
/* needs 8 addresses as A0-A2 are ignored */
|
||||
{ "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) },
|
||||
/* old variants can't be handled with this generic entry! */
|
||||
{ "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) },
|
||||
{ "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) },
|
||||
/* spd is a 24c02 in memory DIMMs */
|
||||
{ "spd", AT24_DEVICE_MAGIC(2048 / 8,
|
||||
AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },
|
||||
{ "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) },
|
||||
/* 24rf08 quirk is handled at i2c-core */
|
||||
{ "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) },
|
||||
{ "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) },
|
||||
{ "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },
|
||||
{ "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) },
|
||||
{ "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) },
|
||||
{ "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) },
|
||||
{ "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) },
|
||||
{ "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) },
|
||||
{ "at24", 0 },
|
||||
{ /* END OF LIST */ }
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* This routine supports chips which consume multiple I2C addresses. It
|
||||
* computes the addressing information to be used for a given r/w request.
|
||||
* Assumes that sanity checks for offset happened at sysfs-layer.
|
||||
*/
|
||||
static struct i2c_client *at24_translate_offset(struct at24_data *at24,
|
||||
unsigned *offset)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
if (at24->chip.flags & AT24_FLAG_ADDR16) {
|
||||
i = *offset >> 16;
|
||||
*offset &= 0xffff;
|
||||
} else {
|
||||
i = *offset >> 8;
|
||||
*offset &= 0xff;
|
||||
}
|
||||
|
||||
return at24->client[i];
|
||||
}
|
||||
|
||||
static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
|
||||
unsigned offset, size_t count)
|
||||
{
|
||||
struct i2c_msg msg[2];
|
||||
u8 msgbuf[2];
|
||||
struct i2c_client *client;
|
||||
int status, i;
|
||||
uint64_t start, read_time;
|
||||
|
||||
memset(msg, 0, sizeof(msg));
|
||||
|
||||
/*
|
||||
* REVISIT some multi-address chips don't rollover page reads to
|
||||
* the next slave address, so we may need to truncate the count.
|
||||
* Those chips might need another quirk flag.
|
||||
*
|
||||
* If the real hardware used four adjacent 24c02 chips and that
|
||||
* were misconfigured as one 24c08, that would be a similar effect:
|
||||
* one "eeprom" file not four, but larger reads would fail when
|
||||
* they crossed certain pages.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Slave address and byte offset derive from the offset. Always
|
||||
* set the byte address; on a multi-master board, another master
|
||||
* may have changed the chip's "current" address pointer.
|
||||
*/
|
||||
client = at24_translate_offset(at24, &offset);
|
||||
|
||||
if (count > io_limit)
|
||||
count = io_limit;
|
||||
|
||||
i = 0;
|
||||
if (at24->chip.flags & AT24_FLAG_ADDR16)
|
||||
msgbuf[i++] = offset >> 8;
|
||||
msgbuf[i++] = offset;
|
||||
|
||||
msg[0].addr = client->addr;
|
||||
msg[0].buf = msgbuf;
|
||||
msg[0].len = i;
|
||||
|
||||
msg[1].addr = client->addr;
|
||||
msg[1].flags = I2C_M_RD;
|
||||
msg[1].buf = buf;
|
||||
msg[1].len = count;
|
||||
|
||||
/*
|
||||
* Reads fail if the previous write didn't complete yet. We may
|
||||
* loop a few times until this one succeeds, waiting at least
|
||||
* long enough for one entire page write to work.
|
||||
*/
|
||||
start = get_time_ns();
|
||||
do {
|
||||
read_time = get_time_ns();
|
||||
status = i2c_transfer(client->adapter, msg, 2);
|
||||
if (status == 2)
|
||||
status = count;
|
||||
dev_dbg(&client->dev, "read %zu@%d --> %d (%llu)\n",
|
||||
count, offset, status, read_time);
|
||||
|
||||
if (status == count)
|
||||
return count;
|
||||
|
||||
/* REVISIT: at HZ=100, this is sloooow */
|
||||
mdelay(1);
|
||||
} while (!is_timeout(start, write_timeout * MSECOND));
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static ssize_t at24_read(struct at24_data *at24,
|
||||
char *buf, loff_t off, size_t count)
|
||||
{
|
||||
ssize_t retval = 0;
|
||||
|
||||
if (unlikely(!count))
|
||||
return count;
|
||||
|
||||
/*
|
||||
* Read data from chip, protecting against concurrent updates
|
||||
* from this host, but not from other I2C masters.
|
||||
*/
|
||||
while (count) {
|
||||
ssize_t status;
|
||||
|
||||
status = at24_eeprom_read(at24, buf, off, count);
|
||||
if (status <= 0) {
|
||||
if (retval == 0)
|
||||
retval = status;
|
||||
break;
|
||||
}
|
||||
buf += status;
|
||||
off += status;
|
||||
count -= status;
|
||||
retval += status;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static ssize_t at24_cdev_read(struct cdev *cdev, void *buf, size_t count,
|
||||
loff_t off, ulong flags)
|
||||
{
|
||||
struct at24_data *at24 = cdev->priv;
|
||||
|
||||
return at24_read(at24, buf, off, count);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that if the hardware write-protect pin is pulled high, the whole
|
||||
* chip is normally write protected. But there are plenty of product
|
||||
* variants here, including OTP fuses and partial chip protect.
|
||||
*
|
||||
* We only use page mode writes; the alternative is sloooow. This routine
|
||||
* writes at most one page.
|
||||
*/
|
||||
static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,
|
||||
unsigned offset, size_t count)
|
||||
{
|
||||
struct i2c_client *client;
|
||||
struct i2c_msg msg;
|
||||
ssize_t status;
|
||||
uint64_t start, write_time;
|
||||
unsigned next_page;
|
||||
int i = 0;
|
||||
|
||||
/* Get corresponding I2C address and adjust offset */
|
||||
client = at24_translate_offset(at24, &offset);
|
||||
|
||||
/* write_max is at most a page */
|
||||
if (count > at24->write_max)
|
||||
count = at24->write_max;
|
||||
|
||||
/* Never roll over backwards, to the start of this page */
|
||||
next_page = roundup(offset + 1, at24->chip.page_size);
|
||||
if (offset + count > next_page)
|
||||
count = next_page - offset;
|
||||
|
||||
|
||||
msg.addr = client->addr;
|
||||
msg.flags = 0;
|
||||
|
||||
/* msg.buf is u8 and casts will mask the values */
|
||||
msg.buf = at24->writebuf;
|
||||
if (at24->chip.flags & AT24_FLAG_ADDR16)
|
||||
msg.buf[i++] = offset >> 8;
|
||||
|
||||
msg.buf[i++] = offset;
|
||||
memcpy(&msg.buf[i], buf, count);
|
||||
msg.len = i + count;
|
||||
|
||||
/*
|
||||
* Writes fail if the previous one didn't complete yet. We may
|
||||
* loop a few times until this one succeeds, waiting at least
|
||||
* long enough for one entire page write to work.
|
||||
*/
|
||||
start = get_time_ns();
|
||||
do {
|
||||
write_time = get_time_ns();
|
||||
status = i2c_transfer(client->adapter, &msg, 1);
|
||||
if (status == 1)
|
||||
status = count;
|
||||
dev_dbg(&client->dev, "write %zu@%d --> %zd (%llu)\n",
|
||||
count, offset, status, write_time);
|
||||
|
||||
if (status == count)
|
||||
return count;
|
||||
|
||||
/* REVISIT: at HZ=100, this is sloooow */
|
||||
mdelay(1);
|
||||
} while (!is_timeout(start, write_timeout * MSECOND));
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static ssize_t at24_write(struct at24_data *at24, const char *buf, loff_t off,
|
||||
size_t count)
|
||||
{
|
||||
ssize_t retval = 0;
|
||||
|
||||
if (unlikely(!count))
|
||||
return count;
|
||||
|
||||
while (count) {
|
||||
ssize_t status;
|
||||
|
||||
status = at24_eeprom_write(at24, buf, off, count);
|
||||
if (status <= 0) {
|
||||
if (retval == 0)
|
||||
retval = status;
|
||||
break;
|
||||
}
|
||||
buf += status;
|
||||
off += status;
|
||||
count -= status;
|
||||
retval += status;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static ssize_t at24_cdev_write(struct cdev *cdev, const void *buf, size_t count,
|
||||
loff_t off, ulong flags)
|
||||
{
|
||||
struct at24_data *at24 = cdev->priv;
|
||||
|
||||
return at24_write(at24, buf, off, count);
|
||||
}
|
||||
|
||||
static int at24_probe(struct device_d *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct at24_platform_data chip;
|
||||
bool writable;
|
||||
struct at24_data *at24;
|
||||
int err;
|
||||
unsigned i, num_addresses;
|
||||
|
||||
if (dev->platform_data) {
|
||||
chip = *(struct at24_platform_data *)dev->platform_data;
|
||||
} else {
|
||||
unsigned long magic;
|
||||
|
||||
err = dev_get_drvdata(dev, (unsigned long *)&magic);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));
|
||||
magic >>= AT24_SIZE_BYTELEN;
|
||||
chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS);
|
||||
/*
|
||||
* This is slow, but we can't know all eeproms, so we better
|
||||
* play safe. Specifying custom eeprom-types via platform_data
|
||||
* is recommended anyhow.
|
||||
*/
|
||||
chip.page_size = 1;
|
||||
}
|
||||
|
||||
if (!is_power_of_2(chip.byte_len))
|
||||
dev_warn(&client->dev,
|
||||
"byte_len looks suspicious (no power of 2)!\n");
|
||||
if (!chip.page_size) {
|
||||
dev_err(&client->dev, "page_size must not be 0!\n");
|
||||
err = -EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
if (!is_power_of_2(chip.page_size))
|
||||
dev_warn(&client->dev,
|
||||
"page_size looks suspicious (no power of 2)!\n");
|
||||
|
||||
if (chip.flags & AT24_FLAG_TAKE8ADDR)
|
||||
num_addresses = 8;
|
||||
else
|
||||
num_addresses = DIV_ROUND_UP(chip.byte_len,
|
||||
(chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);
|
||||
|
||||
at24 = xzalloc(sizeof(struct at24_data) +
|
||||
num_addresses * sizeof(struct i2c_client *));
|
||||
|
||||
at24->chip = chip;
|
||||
at24->num_addresses = num_addresses;
|
||||
at24->cdev.name = asprintf("eeprom%d", dev->id);
|
||||
at24->cdev.priv = at24;
|
||||
at24->cdev.dev = dev;
|
||||
at24->cdev.ops = &at24->fops;
|
||||
at24->fops.lseek = dev_lseek_default;
|
||||
at24->fops.read = at24_cdev_read,
|
||||
at24->cdev.size = chip.byte_len;
|
||||
|
||||
writable = !(chip.flags & AT24_FLAG_READONLY);
|
||||
if (writable) {
|
||||
unsigned write_max = chip.page_size;
|
||||
|
||||
at24->fops.write = at24_cdev_write;
|
||||
|
||||
if (write_max > io_limit)
|
||||
write_max = io_limit;
|
||||
at24->write_max = write_max;
|
||||
|
||||
/* buffer (data + address at the beginning) */
|
||||
at24->writebuf = xmalloc(write_max + 2);
|
||||
}
|
||||
|
||||
at24->client[0] = client;
|
||||
|
||||
/* use dummy devices for multiple-address chips */
|
||||
for (i = 1; i < num_addresses; i++) {
|
||||
at24->client[i] = i2c_new_dummy(client->adapter,
|
||||
client->addr + i);
|
||||
if (!at24->client[i]) {
|
||||
dev_err(&client->dev, "address 0x%02x unavailable\n",
|
||||
client->addr + i);
|
||||
err = -EADDRINUSE;
|
||||
goto err_clients;
|
||||
}
|
||||
}
|
||||
|
||||
devfs_create(&at24->cdev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_clients:
|
||||
kfree(at24->writebuf);
|
||||
kfree(at24);
|
||||
err_out:
|
||||
dev_dbg(&client->dev, "probe error %d\n", err);
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
static struct driver_d at24_driver = {
|
||||
.name = "at24",
|
||||
.probe = at24_probe,
|
||||
.id_table = at24_ids,
|
||||
};
|
||||
|
||||
static int at24_init(void)
|
||||
{
|
||||
i2c_register_driver(&at24_driver);
|
||||
return 0;
|
||||
}
|
||||
device_initcall(at24_init);
|
|
@ -3,6 +3,7 @@ menuconfig I2C
|
|||
|
||||
if I2C
|
||||
|
||||
source drivers/i2c/algos/Kconfig
|
||||
source drivers/i2c/busses/Kconfig
|
||||
|
||||
endif
|
||||
|
|
|
@ -1 +1 @@
|
|||
obj-$(CONFIG_I2C) += i2c.o busses/
|
||||
obj-$(CONFIG_I2C) += i2c.o busses/ algos/
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
#
|
||||
# I2C algorithm drivers configuration
|
||||
#
|
||||
|
||||
config I2C_ALGOBIT
|
||||
bool
|
|
@ -0,0 +1,5 @@
|
|||
#
|
||||
# Makefile for the i2c algorithms
|
||||
#
|
||||
|
||||
obj-$(CONFIG_I2C_ALGOBIT) += i2c-algo-bit.o
|
|
@ -0,0 +1,605 @@
|
|||
/* -------------------------------------------------------------------------
|
||||
* i2c-algo-bit.c i2c driver algorithms for bit-shift adapters
|
||||
* -------------------------------------------------------------------------
|
||||
* Copyright (C) 1995-2000 Simon G. Vogl
|
||||
|
||||
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 Street, Fifth Floor, Boston,
|
||||
MA 02110-1301 USA.
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/* With some changes from Frodo Looijaard <frodol@dds.nl>, Kyösti Mälkki
|
||||
<kmalkki@cc.hut.fi> and Jean Delvare <khali@linux-fr.org> */
|
||||
|
||||
#include <common.h>
|
||||
#include <init.h>
|
||||
#include <errno.h>
|
||||
#include <clock.h>
|
||||
#include <i2c/i2c.h>
|
||||
#include <i2c/i2c-algo-bit.h>
|
||||
|
||||
/* ----- global defines ----------------------------------------------- */
|
||||
|
||||
#ifdef DEBUG
|
||||
#define bit_dbg(level, dev, format, args...) \
|
||||
do { \
|
||||
if (i2c_debug >= level) \
|
||||
dev_dbg(dev, format, ##args); \
|
||||
} while (0)
|
||||
#else
|
||||
#define bit_dbg(level, dev, format, args...) \
|
||||
do {} while (0)
|
||||
#endif /* DEBUG */
|
||||
|
||||
/* ----- global variables --------------------------------------------- */
|
||||
|
||||
static int bit_test; /* see if the line-setting functions work */
|
||||
|
||||
#ifdef DEBUG
|
||||
static int i2c_debug = 1;
|
||||
#endif
|
||||
|
||||
/* --- setting states on the bus with the right timing: --------------- */
|
||||
|
||||
#define setsda(adap, val) adap->setsda(adap->data, val)
|
||||
#define setscl(adap, val) adap->setscl(adap->data, val)
|
||||
#define getsda(adap) adap->getsda(adap->data)
|
||||
#define getscl(adap) adap->getscl(adap->data)
|
||||
|
||||
static inline void sdalo(struct i2c_algo_bit_data *adap)
|
||||
{
|
||||
setsda(adap, 0);
|
||||
udelay((adap->udelay + 1) / 2);
|
||||
}
|
||||
|
||||
static inline void sdahi(struct i2c_algo_bit_data *adap)
|
||||
{
|
||||
setsda(adap, 1);
|
||||
udelay((adap->udelay + 1) / 2);
|
||||
}
|
||||
|
||||
static inline void scllo(struct i2c_algo_bit_data *adap)
|
||||
{
|
||||
setscl(adap, 0);
|
||||
udelay(adap->udelay / 2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Raise scl line, and do checking for delays. This is necessary for slower
|
||||
* devices.
|
||||
*/
|
||||
static int sclhi(struct i2c_algo_bit_data *adap)
|
||||
{
|
||||
uint64_t start;
|
||||
|
||||
setscl(adap, 1);
|
||||
|
||||
/* Not all adapters have scl sense line... */
|
||||
if (!adap->getscl)
|
||||
goto done;
|
||||
|
||||
start = get_time_ns();
|
||||
while (!getscl(adap)) {
|
||||
/* This hw knows how to read the clock line, so we wait
|
||||
* until it actually gets high. This is safer as some
|
||||
* chips may hold it low ("clock stretching") while they
|
||||
* are processing data internally.
|
||||
*/
|
||||
if (is_timeout(start, adap->timeout_ms * MSECOND)) {
|
||||
/* Test one last time, as we may have been preempted
|
||||
* between last check and timeout test.
|
||||
*/
|
||||
if (getscl(adap))
|
||||
break;
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG
|
||||
if (jiffies != start && i2c_debug >= 3)
|
||||
pr_debug("i2c-algo-bit: needed %ld jiffies for SCL to go "
|
||||
"high\n", jiffies - start);
|
||||
#endif
|
||||
|
||||
done:
|
||||
udelay(adap->udelay);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* --- other auxiliary functions -------------------------------------- */
|
||||
static void i2c_start(struct i2c_algo_bit_data *adap)
|
||||
{
|
||||
/* assert: scl, sda are high */
|
||||
setsda(adap, 0);
|
||||
udelay(adap->udelay);
|
||||
scllo(adap);
|
||||
}
|
||||
|
||||
static void i2c_repstart(struct i2c_algo_bit_data *adap)
|
||||
{
|
||||
/* assert: scl is low */
|
||||
sdahi(adap);
|
||||
sclhi(adap);
|
||||
setsda(adap, 0);
|
||||
udelay(adap->udelay);
|
||||
scllo(adap);
|
||||
}
|
||||
|
||||
|
||||
static void i2c_stop(struct i2c_algo_bit_data *adap)
|
||||
{
|
||||
/* assert: scl is low */
|
||||
sdalo(adap);
|
||||
sclhi(adap);
|
||||
setsda(adap, 1);
|
||||
udelay(adap->udelay);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* send a byte without start cond., look for arbitration,
|
||||
check ackn. from slave */
|
||||
/* returns:
|
||||
* 1 if the device acknowledged
|
||||
* 0 if the device did not ack
|
||||
* -ETIMEDOUT if an error occurred (while raising the scl line)
|
||||
*/
|
||||
static int i2c_outb(struct i2c_adapter *i2c_adap, unsigned char c)
|
||||
{
|
||||
int i;
|
||||
int sb;
|
||||
int ack;
|
||||
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
|
||||
|
||||
/* assert: scl is low */
|
||||
for (i = 7; i >= 0; i--) {
|
||||
sb = (c >> i) & 1;
|
||||
setsda(adap, sb);
|
||||
udelay((adap->udelay + 1) / 2);
|
||||
if (sclhi(adap) < 0) { /* timed out */
|
||||
bit_dbg(1, &i2c_adap->dev, "i2c_outb: 0x%02x, "
|
||||
"timeout at bit #%d\n", (int)c, i);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
/* FIXME do arbitration here:
|
||||
* if (sb && !getsda(adap)) -> ouch! Get out of here.
|
||||
*
|
||||
* Report a unique code, so higher level code can retry
|
||||
* the whole (combined) message and *NOT* issue STOP.
|
||||
*/
|
||||
scllo(adap);
|
||||
}
|
||||
sdahi(adap);
|
||||
if (sclhi(adap) < 0) { /* timeout */
|
||||
bit_dbg(1, &i2c_adap->dev, "i2c_outb: 0x%02x, "
|
||||
"timeout at ack\n", (int)c);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* read ack: SDA should be pulled down by slave, or it may
|
||||
* NAK (usually to report problems with the data we wrote).
|
||||
*/
|
||||
ack = !getsda(adap); /* ack: sda is pulled low -> success */
|
||||
bit_dbg(2, &i2c_adap->dev, "i2c_outb: 0x%02x %s\n", (int)c,
|
||||
ack ? "A" : "NA");
|
||||
|
||||
scllo(adap);
|
||||
return ack;
|
||||
/* assert: scl is low (sda undef) */
|
||||
}
|
||||
|
||||
|
||||
static int i2c_inb(struct i2c_adapter *i2c_adap)
|
||||
{
|
||||
/* read byte via i2c port, without start/stop sequence */
|
||||
/* acknowledge is sent in i2c_read. */
|
||||
int i;
|
||||
unsigned char indata = 0;
|
||||
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
|
||||
|
||||
/* assert: scl is low */
|
||||
sdahi(adap);
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (sclhi(adap) < 0) { /* timeout */
|
||||
bit_dbg(1, &i2c_adap->dev, "i2c_inb: timeout at bit "
|
||||
"#%d\n", 7 - i);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
indata *= 2;
|
||||
if (getsda(adap))
|
||||
indata |= 0x01;
|
||||
setscl(adap, 0);
|
||||
udelay(i == 7 ? adap->udelay / 2 : adap->udelay);
|
||||
}
|
||||
/* assert: scl is low */
|
||||
return indata;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sanity check for the adapter hardware - check the reaction of
|
||||
* the bus lines only if it seems to be idle.
|
||||
*/
|
||||
static int test_bus(struct i2c_adapter *i2c_adap)
|
||||
{
|
||||
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
|
||||
struct device_d *dev = &i2c_adap->dev;
|
||||
int scl, sda, ret;
|
||||
|
||||
if (adap->pre_xfer) {
|
||||
ret = adap->pre_xfer(i2c_adap);
|
||||
if (ret < 0)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (adap->getscl == NULL)
|
||||
dev_info(dev, "Testing SDA only, SCL is not readable\n");
|
||||
|
||||
sda = getsda(adap);
|
||||
scl = (adap->getscl == NULL) ? 1 : getscl(adap);
|
||||
if (!scl || !sda) {
|
||||
dev_warn(dev, "bus seems to be busy (scl=%d, sda=%d)\n", scl, sda);
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
sdalo(adap);
|
||||
sda = getsda(adap);
|
||||
scl = (adap->getscl == NULL) ? 1 : getscl(adap);
|
||||
if (sda) {
|
||||
dev_warn(dev, "SDA stuck high!\n");
|
||||
goto bailout;
|
||||
}
|
||||
if (!scl) {
|
||||
dev_warn(dev, "SCL unexpected low while pulling SDA low!\n");
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
sdahi(adap);
|
||||
sda = getsda(adap);
|
||||
scl = (adap->getscl == NULL) ? 1 : getscl(adap);
|
||||
if (!sda) {
|
||||
dev_warn(dev, "SDA stuck low!\n");
|
||||
goto bailout;
|
||||
}
|
||||
if (!scl) {
|
||||
dev_warn(dev, "SCL unexpected low while pulling SDA high!\n");
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
scllo(adap);
|
||||
sda = getsda(adap);
|
||||
scl = (adap->getscl == NULL) ? 0 : getscl(adap);
|
||||
if (scl) {
|
||||
dev_warn(dev, "SCL stuck high!\n");
|
||||
goto bailout;
|
||||
}
|
||||
if (!sda) {
|
||||
dev_warn(dev, "SDA unexpected low while pulling SCL low!\n");
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
sclhi(adap);
|
||||
sda = getsda(adap);
|
||||
scl = (adap->getscl == NULL) ? 1 : getscl(adap);
|
||||
if (!scl) {
|
||||
dev_warn(dev, "SCL stuck low!\n");
|
||||
goto bailout;
|
||||
}
|
||||
if (!sda) {
|
||||
dev_warn(dev, "SDA unexpected low while pulling SCL high!\n");
|
||||
goto bailout;
|
||||
}
|
||||
|
||||
if (adap->post_xfer)
|
||||
adap->post_xfer(i2c_adap);
|
||||
|
||||
dev_info(dev, "Test OK\n");
|
||||
return 0;
|
||||
bailout:
|
||||
sdahi(adap);
|
||||
sclhi(adap);
|
||||
|
||||
if (adap->post_xfer)
|
||||
adap->post_xfer(i2c_adap);
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* ----- Utility functions
|
||||
*/
|
||||
|
||||
/* try_address tries to contact a chip for a number of
|
||||
* times before it gives up.
|
||||
* return values:
|
||||
* 1 chip answered
|
||||
* 0 chip did not answer
|
||||
* -x transmission error
|
||||
*/
|
||||
static int try_address(struct i2c_adapter *i2c_adap,
|
||||
unsigned char addr, int retries)
|
||||
{
|
||||
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
|
||||
int i, ret = 0;
|
||||
|
||||
for (i = 0; i <= retries; i++) {
|
||||
ret = i2c_outb(i2c_adap, addr);
|
||||
if (ret == 1 || i == retries)
|
||||
break;
|
||||
bit_dbg(3, &i2c_adap->dev, "emitting stop condition\n");
|
||||
i2c_stop(adap);
|
||||
udelay(adap->udelay);
|
||||
bit_dbg(3, &i2c_adap->dev, "emitting start condition\n");
|
||||
i2c_start(adap);
|
||||
}
|
||||
if (i && ret)
|
||||
bit_dbg(1, &i2c_adap->dev, "Used %d tries to %s client at "
|
||||
"0x%02x: %s\n", i + 1,
|
||||
addr & 1 ? "read from" : "write to", addr >> 1,
|
||||
ret == 1 ? "success" : "failed, timeout?");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
|
||||
{
|
||||
const unsigned char *temp = msg->buf;
|
||||
int count = msg->len;
|
||||
unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK;
|
||||
int retval;
|
||||
int wrcount = 0;
|
||||
|
||||
while (count > 0) {
|
||||
retval = i2c_outb(i2c_adap, *temp);
|
||||
|
||||
/* OK/ACK; or ignored NAK */
|
||||
if ((retval > 0) || (nak_ok && (retval == 0))) {
|
||||
count--;
|
||||
temp++;
|
||||
wrcount++;
|
||||
|
||||
/* A slave NAKing the master means the slave didn't like
|
||||
* something about the data it saw. For example, maybe
|
||||
* the SMBus PEC was wrong.
|
||||
*/
|
||||
} else if (retval == 0) {
|
||||
dev_err(&i2c_adap->dev, "sendbytes: NAK bailout.\n");
|
||||
return -EIO;
|
||||
|
||||
/* Timeout; or (someday) lost arbitration
|
||||
*
|
||||
* FIXME Lost ARB implies retrying the transaction from
|
||||
* the first message, after the "winning" master issues
|
||||
* its STOP. As a rule, upper layer code has no reason
|
||||
* to know or care about this ... it is *NOT* an error.
|
||||
*/
|
||||
} else {
|
||||
dev_err(&i2c_adap->dev, "sendbytes: error %d\n",
|
||||
retval);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
return wrcount;
|
||||
}
|
||||
|
||||
static int acknak(struct i2c_adapter *i2c_adap, int is_ack)
|
||||
{
|
||||
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
|
||||
|
||||
/* assert: sda is high */
|
||||
if (is_ack) /* send ack */
|
||||
setsda(adap, 0);
|
||||
udelay((adap->udelay + 1) / 2);
|
||||
if (sclhi(adap) < 0) { /* timeout */
|
||||
dev_err(&i2c_adap->dev, "readbytes: ack/nak timeout\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
scllo(adap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
|
||||
{
|
||||
int inval;
|
||||
int rdcount = 0; /* counts bytes read */
|
||||
unsigned char *temp = msg->buf;
|
||||
int count = msg->len;
|
||||
|
||||
while (count > 0) {
|
||||
inval = i2c_inb(i2c_adap);
|
||||
if (inval >= 0) {
|
||||
*temp = inval;
|
||||
rdcount++;
|
||||
} else { /* read timed out */
|
||||
break;
|
||||
}
|
||||
|
||||
temp++;
|
||||
count--;
|
||||
|
||||
bit_dbg(2, &i2c_adap->dev, "readbytes: 0x%02x %s\n",
|
||||
inval,
|
||||
(flags & I2C_M_NO_RD_ACK)
|
||||
? "(no ack/nak)"
|
||||
: (count ? "A" : "NA"));
|
||||
|
||||
inval = acknak(i2c_adap, count);
|
||||
if (inval < 0)
|
||||
return inval;
|
||||
}
|
||||
return rdcount;
|
||||
}
|
||||
|
||||
/* doAddress initiates the transfer by generating the start condition (in
|
||||
* try_address) and transmits the address in the necessary format to handle
|
||||
* reads, writes as well as 10bit-addresses.
|
||||
* returns:
|
||||
* 0 everything went okay, the chip ack'ed, or IGNORE_NAK flag was set
|
||||
* -x an error occurred (like: -ENXIO if the device did not answer, or
|
||||
* -ETIMEDOUT, for example if the lines are stuck...)
|
||||
*/
|
||||
static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
|
||||
{
|
||||
unsigned short flags = msg->flags;
|
||||
unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK;
|
||||
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
|
||||
|
||||
unsigned char addr;
|
||||
int ret, retries;
|
||||
|
||||
retries = nak_ok ? 0 : i2c_adap->retries;
|
||||
|
||||
if (flags & I2C_M_TEN) {
|
||||
/* a ten bit address */
|
||||
addr = 0xf0 | ((msg->addr >> 7) & 0x06);
|
||||
bit_dbg(2, &i2c_adap->dev, "addr0: %d\n", addr);
|
||||
/* try extended address code...*/
|
||||
ret = try_address(i2c_adap, addr, retries);
|
||||
if ((ret != 1) && !nak_ok) {
|
||||
dev_err(&i2c_adap->dev,
|
||||
"died at extended address code\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
/* the remaining 8 bit address */
|
||||
ret = i2c_outb(i2c_adap, msg->addr & 0xff);
|
||||
if ((ret != 1) && !nak_ok) {
|
||||
/* the chip did not ack / xmission error occurred */
|
||||
dev_err(&i2c_adap->dev, "died at 2nd address code\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
if (flags & I2C_M_RD) {
|
||||
bit_dbg(3, &i2c_adap->dev, "emitting repeated "
|
||||
"start condition\n");
|
||||
i2c_repstart(adap);
|
||||
/* okay, now switch into reading mode */
|
||||
addr |= 0x01;
|
||||
ret = try_address(i2c_adap, addr, retries);
|
||||
if ((ret != 1) && !nak_ok) {
|
||||
dev_err(&i2c_adap->dev,
|
||||
"died at repeated address code\n");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
} else { /* normal 7bit address */
|
||||
addr = msg->addr << 1;
|
||||
if (flags & I2C_M_RD)
|
||||
addr |= 1;
|
||||
ret = try_address(i2c_adap, addr, retries);
|
||||
if ((ret != 1) && !nak_ok)
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bit_xfer(struct i2c_adapter *i2c_adap,
|
||||
struct i2c_msg msgs[], int num)
|
||||
{
|
||||
struct i2c_msg *pmsg;
|
||||
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
|
||||
int i, ret;
|
||||
unsigned short nak_ok;
|
||||
|
||||
if (adap->pre_xfer) {
|
||||
ret = adap->pre_xfer(i2c_adap);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
bit_dbg(3, &i2c_adap->dev, "emitting start condition\n");
|
||||
i2c_start(adap);
|
||||
for (i = 0; i < num; i++) {
|
||||
pmsg = &msgs[i];
|
||||
nak_ok = pmsg->flags & I2C_M_IGNORE_NAK;
|
||||
if (i) {
|
||||
bit_dbg(3, &i2c_adap->dev, "emitting "
|
||||
"repeated start condition\n");
|
||||
i2c_repstart(adap);
|
||||
}
|
||||
ret = bit_doAddress(i2c_adap, pmsg);
|
||||
if ((ret != 0) && !nak_ok) {
|
||||
bit_dbg(1, &i2c_adap->dev, "NAK from "
|
||||
"device addr 0x%02x msg #%d\n",
|
||||
msgs[i].addr, i);
|
||||
goto bailout;
|
||||
}
|
||||
if (pmsg->flags & I2C_M_RD) {
|
||||
/* read bytes into buffer*/
|
||||
ret = readbytes(i2c_adap, pmsg);
|
||||
if (ret >= 1)
|
||||
bit_dbg(2, &i2c_adap->dev, "read %d byte%s\n",
|
||||
ret, ret == 1 ? "" : "s");
|
||||
if (ret < pmsg->len) {
|
||||
if (ret >= 0)
|
||||
ret = -EIO;
|
||||
goto bailout;
|
||||
}
|
||||
} else {
|
||||
/* write bytes from buffer */
|
||||
ret = sendbytes(i2c_adap, pmsg);
|
||||
if (ret >= 1)
|
||||
bit_dbg(2, &i2c_adap->dev, "wrote %d byte%s\n",
|
||||
ret, ret == 1 ? "" : "s");
|
||||
if (ret < pmsg->len) {
|
||||
if (ret >= 0)
|
||||
ret = -EIO;
|
||||
goto bailout;
|
||||
}
|
||||
}
|
||||
}
|
||||
ret = i;
|
||||
|
||||
bailout:
|
||||
bit_dbg(3, &i2c_adap->dev, "emitting stop condition\n");
|
||||
i2c_stop(adap);
|
||||
|
||||
if (adap->post_xfer)
|
||||
adap->post_xfer(i2c_adap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* registering functions to load algorithms at runtime
|
||||
*/
|
||||
static int __i2c_bit_add_bus(struct i2c_adapter *adap,
|
||||
int (*add_adapter)(struct i2c_adapter *))
|
||||
{
|
||||
struct i2c_algo_bit_data *bit_adap = adap->algo_data;
|
||||
int ret;
|
||||
|
||||
if (bit_test) {
|
||||
ret = test_bus(adap);
|
||||
if (bit_test >= 2 && ret < 0)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* register new adapter to i2c module... */
|
||||
//adap->algo = &i2c_bit_algo;
|
||||
adap->retries = 3;
|
||||
adap->master_xfer = bit_xfer;
|
||||
|
||||
ret = add_adapter(adap);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Complain if SCL can't be read */
|
||||
if (bit_adap->getscl == NULL) {
|
||||
dev_warn(&adap->dev, "Not I2C compliant: can't read SCL\n");
|
||||
dev_warn(&adap->dev, "Bus may be unreliable\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2c_bit_add_numbered_bus(struct i2c_adapter *adap)
|
||||
{
|
||||
return __i2c_bit_add_bus(adap, i2c_add_numbered_adapter);
|
||||
}
|
||||
EXPORT_SYMBOL(i2c_bit_add_numbered_bus);
|
|
@ -4,6 +4,14 @@
|
|||
|
||||
menu "I2C Hardware Bus support"
|
||||
|
||||
config I2C_GPIO
|
||||
tristate "GPIO-based bitbanging I2C"
|
||||
depends on GENERIC_GPIO
|
||||
select I2C_ALGOBIT
|
||||
help
|
||||
This is a very simple bitbanging I2C driver utilizing the
|
||||
arch-neutral GPIO API to control the SCL and SDA lines.
|
||||
|
||||
config I2C_IMX
|
||||
bool "MPC85xx/i.MX I2C Master driver"
|
||||
depends on (ARCH_IMX && !ARCH_IMX1) || ARCH_MPC85XX
|
||||
|
@ -12,4 +20,12 @@ config I2C_OMAP
|
|||
bool "OMAP I2C Master driver"
|
||||
depends on ARCH_OMAP
|
||||
|
||||
config I2C_VERSATILE
|
||||
tristate "ARM Versatile/Realview I2C bus support"
|
||||
depends on ARCH_VERSATILE
|
||||
select I2C_ALGOBIT
|
||||
help
|
||||
Say yes if you want to support the I2C serial bus on ARMs Versatile
|
||||
range of platforms.
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
|
||||
obj-$(CONFIG_I2C_IMX) += i2c-imx.o
|
||||
obj-$(CONFIG_I2C_OMAP) += i2c-omap.o
|
||||
obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* Bitbanging I2C bus driver using the GPIO API
|
||||
*
|
||||
* Copyright (C) 2007 Atmel Corporation
|
||||
*
|
||||
* 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
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <driver.h>
|
||||
#include <i2c/i2c.h>
|
||||
#include <i2c/i2c-algo-bit.h>
|
||||
#include <i2c/i2c-gpio.h>
|
||||
#include <init.h>
|
||||
#include <gpio.h>
|
||||
|
||||
struct i2c_gpio_private_data {
|
||||
struct i2c_adapter adap;
|
||||
struct i2c_algo_bit_data bit_data;
|
||||
struct i2c_gpio_platform_data pdata;
|
||||
};
|
||||
|
||||
/* Toggle SDA by changing the direction of the pin */
|
||||
static void i2c_gpio_setsda_dir(void *data, int state)
|
||||
{
|
||||
struct i2c_gpio_platform_data *pdata = data;
|
||||
|
||||
if (state)
|
||||
gpio_direction_input(pdata->sda_pin);
|
||||
else
|
||||
gpio_direction_output(pdata->sda_pin, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Toggle SDA by changing the output value of the pin. This is only
|
||||
* valid for pins configured as open drain (i.e. setting the value
|
||||
* high effectively turns off the output driver.)
|
||||
*/
|
||||
static void i2c_gpio_setsda_val(void *data, int state)
|
||||
{
|
||||
struct i2c_gpio_platform_data *pdata = data;
|
||||
|
||||
gpio_set_value(pdata->sda_pin, state);
|
||||
}
|
||||
|
||||
/* Toggle SCL by changing the direction of the pin. */
|
||||
static void i2c_gpio_setscl_dir(void *data, int state)
|
||||
{
|
||||
struct i2c_gpio_platform_data *pdata = data;
|
||||
|
||||
if (state)
|
||||
gpio_direction_input(pdata->scl_pin);
|
||||
else
|
||||
gpio_direction_output(pdata->scl_pin, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Toggle SCL by changing the output value of the pin. This is used
|
||||
* for pins that are configured as open drain and for output-only
|
||||
* pins. The latter case will break the i2c protocol, but it will
|
||||
* often work in practice.
|
||||
*/
|
||||
static void i2c_gpio_setscl_val(void *data, int state)
|
||||
{
|
||||
struct i2c_gpio_platform_data *pdata = data;
|
||||
|
||||
gpio_set_value(pdata->scl_pin, state);
|
||||
}
|
||||
|
||||
static int i2c_gpio_getsda(void *data)
|
||||
{
|
||||
struct i2c_gpio_platform_data *pdata = data;
|
||||
|
||||
return gpio_get_value(pdata->sda_pin);
|
||||
}
|
||||
|
||||
static int i2c_gpio_getscl(void *data)
|
||||
{
|
||||
struct i2c_gpio_platform_data *pdata = data;
|
||||
|
||||
return gpio_get_value(pdata->scl_pin);
|
||||
}
|
||||
|
||||
static int i2c_gpio_probe(struct device_d *dev)
|
||||
{
|
||||
struct i2c_gpio_private_data *priv;
|
||||
struct i2c_gpio_platform_data *pdata;
|
||||
struct i2c_algo_bit_data *bit_data;
|
||||
struct i2c_adapter *adap;
|
||||
int ret;
|
||||
|
||||
priv = xzalloc(sizeof(*priv));
|
||||
|
||||
adap = &priv->adap;
|
||||
bit_data = &priv->bit_data;
|
||||
pdata = &priv->pdata;
|
||||
|
||||
if (!dev->platform_data)
|
||||
return -ENXIO;
|
||||
memcpy(pdata, dev->platform_data, sizeof(*pdata));
|
||||
|
||||
ret = gpio_request(pdata->sda_pin, "sda");
|
||||
if (ret)
|
||||
goto err_request_sda;
|
||||
ret = gpio_request(pdata->scl_pin, "scl");
|
||||
if (ret)
|
||||
goto err_request_scl;
|
||||
|
||||
if (pdata->sda_is_open_drain) {
|
||||
gpio_direction_output(pdata->sda_pin, 1);
|
||||
bit_data->setsda = i2c_gpio_setsda_val;
|
||||
} else {
|
||||
gpio_direction_input(pdata->sda_pin);
|
||||
bit_data->setsda = i2c_gpio_setsda_dir;
|
||||
}
|
||||
|
||||
if (pdata->scl_is_open_drain || pdata->scl_is_output_only) {
|
||||
gpio_direction_output(pdata->scl_pin, 1);
|
||||
bit_data->setscl = i2c_gpio_setscl_val;
|
||||
} else {
|
||||
gpio_direction_input(pdata->scl_pin);
|
||||
bit_data->setscl = i2c_gpio_setscl_dir;
|
||||
}
|
||||
|
||||
if (!pdata->scl_is_output_only)
|
||||
bit_data->getscl = i2c_gpio_getscl;
|
||||
bit_data->getsda = i2c_gpio_getsda;
|
||||
|
||||
if (pdata->udelay)
|
||||
bit_data->udelay = pdata->udelay;
|
||||
else if (pdata->scl_is_output_only)
|
||||
bit_data->udelay = 50; /* 10 kHz */
|
||||
else
|
||||
bit_data->udelay = 5; /* 100 kHz */
|
||||
|
||||
if (pdata->timeout_ms)
|
||||
bit_data->timeout_ms = pdata->timeout_ms;
|
||||
else
|
||||
bit_data->timeout_ms = 100; /* 100 ms */
|
||||
|
||||
bit_data->data = pdata;
|
||||
|
||||
adap->algo_data = bit_data;
|
||||
adap->dev.parent = dev;
|
||||
|
||||
adap->nr = dev->id;
|
||||
ret = i2c_bit_add_numbered_bus(adap);
|
||||
if (ret)
|
||||
goto err_add_bus;
|
||||
|
||||
dev_info(dev, "using pins %u (SDA) and %u (SCL%s)\n",
|
||||
pdata->sda_pin, pdata->scl_pin,
|
||||
pdata->scl_is_output_only
|
||||
? ", no clock stretching" : "");
|
||||
|
||||
return 0;
|
||||
|
||||
err_add_bus:
|
||||
gpio_free(pdata->scl_pin);
|
||||
err_request_scl:
|
||||
gpio_free(pdata->sda_pin);
|
||||
err_request_sda:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct driver_d i2c_gpio_driver = {
|
||||
.name = "i2c-gpio",
|
||||
.probe = i2c_gpio_probe,
|
||||
};
|
||||
|
||||
static int __init i2c_gpio_init(void)
|
||||
{
|
||||
return platform_driver_register(&i2c_gpio_driver);
|
||||
}
|
||||
device_initcall(i2c_gpio_init);
|
|
@ -154,7 +154,7 @@ static int i2c_fsl_bus_busy(struct i2c_adapter *adapter, int for_busy)
|
|||
if (!for_busy && !(temp & I2SR_IBB))
|
||||
break;
|
||||
if (is_timeout(start, 500 * MSECOND)) {
|
||||
dev_err(adapter->dev,
|
||||
dev_err(&adapter->dev,
|
||||
"<%s> timeout waiting for I2C bus %s\n",
|
||||
__func__,for_busy ? "busy" : "not busy");
|
||||
return -EIO;
|
||||
|
@ -177,7 +177,7 @@ static int i2c_fsl_trx_complete(struct i2c_adapter *adapter)
|
|||
break;
|
||||
|
||||
if (is_timeout(start, 100 * MSECOND)) {
|
||||
dev_err(adapter->dev, "<%s> TXR timeout\n", __func__);
|
||||
dev_err(&adapter->dev, "<%s> TXR timeout\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
@ -199,7 +199,7 @@ static int i2c_fsl_acked(struct i2c_adapter *adapter)
|
|||
break;
|
||||
|
||||
if (is_timeout(start, MSECOND)) {
|
||||
dev_dbg(adapter->dev, "<%s> No ACK\n", __func__);
|
||||
dev_dbg(&adapter->dev, "<%s> No ACK\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
@ -296,7 +296,7 @@ static void i2c_fsl_set_clk(struct fsl_i2c_struct *i2c_fsl,
|
|||
* Translate to dfsr = 5 * Frequency / 100,000,000
|
||||
*/
|
||||
dfsr = (5 * (i2c_clk / 1000)) / 100000;
|
||||
dev_dbg(i2c_fsl->adapter.dev,
|
||||
dev_dbg(&i2c_fsl->adapter.dev,
|
||||
"<%s> requested speed:%d, i2c_clk:%d\n", __func__,
|
||||
rate, i2c_clk);
|
||||
if (!dfsr)
|
||||
|
@ -315,12 +315,12 @@ static void i2c_fsl_set_clk(struct fsl_i2c_struct *i2c_fsl,
|
|||
bin_ga = (ga & 0x3) | ((ga & 0x4) << 3);
|
||||
fdr = bin_gb | bin_ga;
|
||||
rate = i2c_clk / est_div;
|
||||
dev_dbg(i2c_fsl->adapter.dev,
|
||||
dev_dbg(&i2c_fsl->adapter.dev,
|
||||
"FDR:0x%.2x, div:%ld, ga:0x%x, gb:0x%x,"
|
||||
" a:%d, b:%d, speed:%d\n", fdr, est_div,
|
||||
ga, gb, a, b, rate);
|
||||
/* Condition 2 not accounted for */
|
||||
dev_dbg(i2c_fsl->adapter.dev,
|
||||
dev_dbg(&i2c_fsl->adapter.dev,
|
||||
"Tr <= %d ns\n", (b - 3 * dfsr) *
|
||||
1000000 / (i2c_clk / 1000));
|
||||
}
|
||||
|
@ -330,9 +330,9 @@ static void i2c_fsl_set_clk(struct fsl_i2c_struct *i2c_fsl,
|
|||
if (a == 24)
|
||||
a += 4;
|
||||
}
|
||||
dev_dbg(i2c_fsl->adapter.dev,
|
||||
dev_dbg(&i2c_fsl->adapter.dev,
|
||||
"divider:%d, est_div:%ld, DFSR:%d\n", divider, est_div, dfsr);
|
||||
dev_dbg(i2c_fsl->adapter.dev, "FDR:0x%.2x, speed:%d\n", fdr, rate);
|
||||
dev_dbg(&i2c_fsl->adapter.dev, "FDR:0x%.2x, speed:%d\n", fdr, rate);
|
||||
i2c_fsl->ifdr = fdr;
|
||||
i2c_fsl->dfsrr = dfsr;
|
||||
}
|
||||
|
@ -368,9 +368,9 @@ static void i2c_fsl_set_clk(struct fsl_i2c_struct *i2c_fsl,
|
|||
(500000U * i2c_clk_div[i][0] + (i2c_clk_rate / 2) - 1) /
|
||||
(i2c_clk_rate / 2);
|
||||
|
||||
dev_dbg(i2c_fsl->adapter.dev, "<%s> I2C_CLK=%d, REQ DIV=%d\n",
|
||||
dev_dbg(&i2c_fsl->adapter.dev, "<%s> I2C_CLK=%d, REQ DIV=%d\n",
|
||||
__func__, i2c_clk_rate, div);
|
||||
dev_dbg(i2c_fsl->adapter.dev, "<%s> IFDR[IC]=0x%x, REAL DIV=%d\n",
|
||||
dev_dbg(&i2c_fsl->adapter.dev, "<%s> IFDR[IC]=0x%x, REAL DIV=%d\n",
|
||||
__func__, i2c_clk_div[i][1], i2c_clk_div[i][0]);
|
||||
}
|
||||
#endif
|
||||
|
@ -382,7 +382,7 @@ static int i2c_fsl_write(struct i2c_adapter *adapter, struct i2c_msg *msgs)
|
|||
int i, result;
|
||||
|
||||
if ( !(msgs->flags & I2C_M_DATA_ONLY) ) {
|
||||
dev_dbg(adapter->dev,
|
||||
dev_dbg(&adapter->dev,
|
||||
"<%s> write slave address: addr=0x%02x\n",
|
||||
__func__, msgs->addr << 1);
|
||||
|
||||
|
@ -399,7 +399,7 @@ static int i2c_fsl_write(struct i2c_adapter *adapter, struct i2c_msg *msgs)
|
|||
|
||||
/* write data */
|
||||
for (i = 0; i < msgs->len; i++) {
|
||||
dev_dbg(adapter->dev,
|
||||
dev_dbg(&adapter->dev,
|
||||
"<%s> write byte: B%d=0x%02X\n",
|
||||
__func__, i, msgs->buf[i]);
|
||||
writeb(msgs->buf[i], base + FSL_I2C_I2DR);
|
||||
|
@ -425,7 +425,7 @@ static int i2c_fsl_read(struct i2c_adapter *adapter, struct i2c_msg *msgs)
|
|||
writeb(0x0, base + FSL_I2C_I2SR);
|
||||
|
||||
if ( !(msgs->flags & I2C_M_DATA_ONLY) ) {
|
||||
dev_dbg(adapter->dev,
|
||||
dev_dbg(&adapter->dev,
|
||||
"<%s> write slave address: addr=0x%02x\n",
|
||||
__func__, (msgs->addr << 1) | 0x01);
|
||||
|
||||
|
@ -478,7 +478,7 @@ static int i2c_fsl_read(struct i2c_adapter *adapter, struct i2c_msg *msgs)
|
|||
}
|
||||
msgs->buf[i] = readb(base + FSL_I2C_I2DR);
|
||||
|
||||
dev_dbg(adapter->dev, "<%s> read byte: B%d=0x%02X\n",
|
||||
dev_dbg(&adapter->dev, "<%s> read byte: B%d=0x%02X\n",
|
||||
__func__, i, msgs->buf[i]);
|
||||
}
|
||||
return 0;
|
||||
|
@ -544,7 +544,7 @@ static int __init i2c_fsl_probe(struct device_d *pdev)
|
|||
/* Setup i2c_fsl driver structure */
|
||||
i2c_fsl->adapter.master_xfer = i2c_fsl_xfer;
|
||||
i2c_fsl->adapter.nr = pdev->id;
|
||||
i2c_fsl->adapter.dev = pdev;
|
||||
i2c_fsl->adapter.dev.parent = pdev;
|
||||
i2c_fsl->base = dev_request_mem_region(pdev, 0);
|
||||
i2c_fsl->dfsrr = -1;
|
||||
|
||||
|
|
|
@ -318,7 +318,7 @@ static int omap_i2c_init(struct omap_i2c_struct *i2c_omap)
|
|||
while (!(omap_i2c_read_reg(i2c_omap, OMAP_I2C_SYSS_REG) &
|
||||
SYSS_RESETDONE_MASK)) {
|
||||
if (is_timeout(start, MSECOND)) {
|
||||
dev_warn(i2c_omap->adapter.dev, "timeout waiting "
|
||||
dev_warn(&i2c_omap->adapter.dev, "timeout waiting "
|
||||
"for controller reset\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
@ -453,7 +453,7 @@ static int omap_i2c_wait_for_bb(struct i2c_adapter *adapter)
|
|||
start = get_time_ns();
|
||||
while (omap_i2c_read_reg(i2c_omap, OMAP_I2C_STAT_REG) & OMAP_I2C_STAT_BB) {
|
||||
if (is_timeout(start, MSECOND)) {
|
||||
dev_warn(adapter->dev, "timeout waiting for bus ready\n");
|
||||
dev_warn(&adapter->dev, "timeout waiting for bus ready\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
@ -476,9 +476,9 @@ omap_i2c_isr(struct omap_i2c_struct *dev)
|
|||
|
||||
bits = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG);
|
||||
while ((stat = (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG))) & bits) {
|
||||
dev_dbg(dev->adapter.dev, "IRQ (ISR = 0x%04x)\n", stat);
|
||||
dev_dbg(&dev->adapter.dev, "IRQ (ISR = 0x%04x)\n", stat);
|
||||
if (count++ == 100) {
|
||||
dev_warn(dev->adapter.dev, "Too much work in one IRQ\n");
|
||||
dev_warn(&dev->adapter.dev, "Too much work in one IRQ\n");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -499,7 +499,7 @@ complete:
|
|||
OMAP_I2C_CON_STP);
|
||||
}
|
||||
if (stat & OMAP_I2C_STAT_AL) {
|
||||
dev_err(dev->adapter.dev, "Arbitration lost\n");
|
||||
dev_err(&dev->adapter.dev, "Arbitration lost\n");
|
||||
err |= OMAP_I2C_STAT_AL;
|
||||
}
|
||||
if (stat & (OMAP_I2C_STAT_ARDY | OMAP_I2C_STAT_NACK |
|
||||
|
@ -536,11 +536,11 @@ complete:
|
|||
}
|
||||
} else {
|
||||
if (stat & OMAP_I2C_STAT_RRDY)
|
||||
dev_err(dev->adapter.dev,
|
||||
dev_err(&dev->adapter.dev,
|
||||
"RRDY IRQ while no data"
|
||||
" requested\n");
|
||||
if (stat & OMAP_I2C_STAT_RDR)
|
||||
dev_err(dev->adapter.dev,
|
||||
dev_err(&dev->adapter.dev,
|
||||
"RDR IRQ while no data"
|
||||
" requested\n");
|
||||
break;
|
||||
|
@ -577,11 +577,11 @@ complete:
|
|||
}
|
||||
} else {
|
||||
if (stat & OMAP_I2C_STAT_XRDY)
|
||||
dev_err(dev->adapter.dev,
|
||||
dev_err(&dev->adapter.dev,
|
||||
"XRDY IRQ while no "
|
||||
"data to send\n");
|
||||
if (stat & OMAP_I2C_STAT_XDR)
|
||||
dev_err(dev->adapter.dev,
|
||||
dev_err(&dev->adapter.dev,
|
||||
"XDR IRQ while no "
|
||||
"data to send\n");
|
||||
break;
|
||||
|
@ -613,11 +613,11 @@ complete:
|
|||
continue;
|
||||
}
|
||||
if (stat & OMAP_I2C_STAT_ROVR) {
|
||||
dev_err(dev->adapter.dev, "Receive overrun\n");
|
||||
dev_err(&dev->adapter.dev, "Receive overrun\n");
|
||||
dev->cmd_err |= OMAP_I2C_STAT_ROVR;
|
||||
}
|
||||
if (stat & OMAP_I2C_STAT_XUDF) {
|
||||
dev_err(dev->adapter.dev, "Transmit underflow\n");
|
||||
dev_err(&dev->adapter.dev, "Transmit underflow\n");
|
||||
dev->cmd_err |= OMAP_I2C_STAT_XUDF;
|
||||
}
|
||||
}
|
||||
|
@ -639,7 +639,7 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adapter,
|
|||
int ret = 0;
|
||||
|
||||
|
||||
dev_dbg(adapter->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n",
|
||||
dev_dbg(&adapter->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n",
|
||||
msg->addr, msg->len, msg->flags, stop);
|
||||
|
||||
if (msg->len == 0)
|
||||
|
@ -687,7 +687,7 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adapter,
|
|||
|
||||
/* Let the user know if i2c is in a bad state */
|
||||
if (is_timeout(start, MSECOND)) {
|
||||
dev_err(adapter->dev, "controller timed out "
|
||||
dev_err(&adapter->dev, "controller timed out "
|
||||
"waiting for start condition to finish\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
@ -707,7 +707,7 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adapter,
|
|||
while (ret){
|
||||
ret = omap_i2c_isr(i2c_omap);
|
||||
if (is_timeout(start, MSECOND)) {
|
||||
dev_err(adapter->dev,
|
||||
dev_err(&adapter->dev,
|
||||
"timed out on polling for "
|
||||
"open i2c message handling\n");
|
||||
return -ETIMEDOUT;
|
||||
|
@ -835,7 +835,7 @@ i2c_omap_probe(struct device_d *pdev)
|
|||
|
||||
i2c_omap->adapter.master_xfer = omap_i2c_xfer,
|
||||
i2c_omap->adapter.nr = pdev->id;
|
||||
i2c_omap->adapter.dev = pdev;
|
||||
i2c_omap->adapter.dev.parent = pdev;
|
||||
|
||||
/* i2c device drivers may be active on return from add_adapter() */
|
||||
r = i2c_add_numbered_adapter(&i2c_omap->adapter);
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* i2c-versatile.c
|
||||
*
|
||||
* Copyright (C) 2006 ARM Ltd.
|
||||
* written by Russell King, Deep Blue Solutions Ltd.
|
||||
*
|
||||
* 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
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <driver.h>
|
||||
#include <i2c/i2c.h>
|
||||
#include <i2c/i2c-algo-bit.h>
|
||||
#include <init.h>
|
||||
#include <malloc.h>
|
||||
#include <io.h>
|
||||
|
||||
#define I2C_CONTROL 0x00
|
||||
#define I2C_CONTROLS 0x00
|
||||
#define I2C_CONTROLC 0x04
|
||||
#define SCL (1 << 0)
|
||||
#define SDA (1 << 1)
|
||||
|
||||
struct i2c_versatile {
|
||||
struct i2c_adapter adap;
|
||||
struct i2c_algo_bit_data algo;
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
static void i2c_versatile_setsda(void *data, int state)
|
||||
{
|
||||
struct i2c_versatile *i2c = data;
|
||||
|
||||
writel(SDA, i2c->base + (state ? I2C_CONTROLS : I2C_CONTROLC));
|
||||
}
|
||||
|
||||
static void i2c_versatile_setscl(void *data, int state)
|
||||
{
|
||||
struct i2c_versatile *i2c = data;
|
||||
|
||||
writel(SCL, i2c->base + (state ? I2C_CONTROLS : I2C_CONTROLC));
|
||||
}
|
||||
|
||||
static int i2c_versatile_getsda(void *data)
|
||||
{
|
||||
struct i2c_versatile *i2c = data;
|
||||
return !!(readl(i2c->base + I2C_CONTROL) & SDA);
|
||||
}
|
||||
|
||||
static int i2c_versatile_getscl(void *data)
|
||||
{
|
||||
struct i2c_versatile *i2c = data;
|
||||
return !!(readl(i2c->base + I2C_CONTROL) & SCL);
|
||||
}
|
||||
|
||||
static struct i2c_algo_bit_data i2c_versatile_algo = {
|
||||
.setsda = i2c_versatile_setsda,
|
||||
.setscl = i2c_versatile_setscl,
|
||||
.getsda = i2c_versatile_getsda,
|
||||
.getscl = i2c_versatile_getscl,
|
||||
.udelay = 30,
|
||||
.timeout_ms = 100,
|
||||
};
|
||||
|
||||
static int i2c_versatile_probe(struct device_d *dev)
|
||||
{
|
||||
struct i2c_versatile *i2c;
|
||||
int ret;
|
||||
|
||||
i2c = kzalloc(sizeof(struct i2c_versatile), GFP_KERNEL);
|
||||
if (!i2c) {
|
||||
ret = -ENOMEM;
|
||||
goto err_release;
|
||||
}
|
||||
|
||||
i2c->base = dev_request_mem_region(dev, 0);
|
||||
if (!i2c->base) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
writel(SCL | SDA, i2c->base + I2C_CONTROLS);
|
||||
|
||||
i2c->adap.algo_data = &i2c->algo;
|
||||
i2c->adap.dev.parent = dev;
|
||||
i2c->algo = i2c_versatile_algo;
|
||||
i2c->algo.data = i2c;
|
||||
|
||||
i2c->adap.nr = dev->id;
|
||||
ret = i2c_bit_add_numbered_bus(&i2c->adap);
|
||||
if (ret >= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
err_free:
|
||||
kfree(i2c);
|
||||
err_release:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct driver_d i2c_versatile_driver = {
|
||||
.name = "versatile-i2c",
|
||||
.probe = i2c_versatile_probe,
|
||||
};
|
||||
|
||||
static int __init i2c_versatile_init(void)
|
||||
{
|
||||
return platform_driver_register(&i2c_versatile_driver);
|
||||
}
|
||||
|
||||
device_initcall(i2c_versatile_init);
|
|
@ -80,7 +80,7 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
|||
*/
|
||||
|
||||
for (ret = 0; ret < num; ret++) {
|
||||
dev_dbg(adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
|
||||
dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
|
||||
"len=%d\n", ret, (msgs[ret].flags & I2C_M_RD)
|
||||
? 'R' : 'W', msgs[ret].addr, msgs[ret].len);
|
||||
}
|
||||
|
@ -256,6 +256,9 @@ struct i2c_client *i2c_new_device(struct i2c_adapter *adapter,
|
|||
client->adapter = adapter;
|
||||
client->addr = chip->addr;
|
||||
|
||||
client->dev.parent = &adapter->dev;
|
||||
dev_add_child(client->dev.parent, &client->dev);
|
||||
|
||||
status = register_device(&client->dev);
|
||||
|
||||
#if 0
|
||||
|
@ -278,6 +281,34 @@ struct i2c_client *i2c_new_device(struct i2c_adapter *adapter,
|
|||
}
|
||||
EXPORT_SYMBOL(i2c_new_device);
|
||||
|
||||
/**
|
||||
* i2c_new_dummy - return a new i2c device bound to a dummy driver
|
||||
* @adapter: the adapter managing the device
|
||||
* @address: seven bit address to be used
|
||||
* Context: can sleep
|
||||
*
|
||||
* This returns an I2C client bound to the "dummy" driver, intended for use
|
||||
* with devices that consume multiple addresses. Examples of such chips
|
||||
* include various EEPROMS (like 24c04 and 24c08 models).
|
||||
*
|
||||
* These dummy devices have two main uses. First, most I2C and SMBus calls
|
||||
* except i2c_transfer() need a client handle; the dummy will be that handle.
|
||||
* And second, this prevents the specified address from being bound to a
|
||||
* different driver.
|
||||
*
|
||||
* This returns the new i2c client, which should be saved for later use with
|
||||
* i2c_unregister_device(); or NULL to indicate an error.
|
||||
*/
|
||||
struct i2c_client *i2c_new_dummy(struct i2c_adapter *adapter, u16 address)
|
||||
{
|
||||
struct i2c_board_info info = {
|
||||
I2C_BOARD_INFO("dummy", address),
|
||||
};
|
||||
|
||||
return i2c_new_device(adapter, &info);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_new_dummy);
|
||||
|
||||
/**
|
||||
* i2c_register_board_info - register I2C devices for a given board
|
||||
*
|
||||
|
@ -363,9 +394,21 @@ struct i2c_adapter *i2c_get_adapter(int busnum)
|
|||
*/
|
||||
int i2c_add_numbered_adapter(struct i2c_adapter *adapter)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (i2c_get_adapter(adapter->nr))
|
||||
return -EBUSY;
|
||||
|
||||
adapter->dev.id = adapter->nr;
|
||||
strcpy(adapter->dev.name, "i2c");
|
||||
|
||||
if (adapter->dev.parent)
|
||||
dev_add_child(adapter->dev.parent, &adapter->dev);
|
||||
|
||||
ret = register_device(&adapter->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
list_add_tail(&adapter->list, &adapter_list);
|
||||
|
||||
/* populate children from any i2c device tables */
|
||||
|
@ -377,7 +420,22 @@ EXPORT_SYMBOL(i2c_add_numbered_adapter);
|
|||
|
||||
static int i2c_match(struct device_d *dev, struct driver_d *drv)
|
||||
{
|
||||
return strcmp(dev->name, drv->name) ? -1 : 0;
|
||||
if (!strcmp(dev->name, drv->name))
|
||||
return 0;
|
||||
|
||||
if (drv->id_table) {
|
||||
struct platform_device_id *id = drv->id_table;
|
||||
|
||||
while (id->name) {
|
||||
if (!strcmp(id->name, dev->name)) {
|
||||
dev->id_entry = id;
|
||||
return 0;
|
||||
}
|
||||
id++;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int i2c_probe(struct device_d *dev)
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* at24.h - platform_data for the at24 (generic eeprom) driver
|
||||
* (C) Copyright 2008 by Pengutronix
|
||||
* (C) Copyright 2012 by Wolfram Sang
|
||||
* same license as the driver
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_AT24_H
|
||||
#define _LINUX_AT24_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/**
|
||||
* struct at24_platform_data - data to set up at24 (generic eeprom) driver
|
||||
* @byte_len: size of eeprom in byte
|
||||
* @page_size: number of byte which can be written in one go
|
||||
* @flags: tunable options, check AT24_FLAG_* defines
|
||||
*
|
||||
* If you set up a custom eeprom type, please double-check the parameters.
|
||||
* Especially page_size needs extra care, as you risk data loss if your value
|
||||
* is bigger than what the chip actually supports!
|
||||
*
|
||||
*/
|
||||
|
||||
struct at24_platform_data {
|
||||
u32 byte_len; /* size (sum of all addr) */
|
||||
u16 page_size; /* for writes */
|
||||
u8 flags;
|
||||
#define AT24_FLAG_ADDR16 0x80 /* address pointer is 16 bit */
|
||||
#define AT24_FLAG_READONLY 0x40 /* sysfs-entry will be read-only */
|
||||
#define AT24_FLAG_IRUGO 0x20 /* sysfs-entry will be world-readable */
|
||||
#define AT24_FLAG_TAKE8ADDR 0x10 /* take always 8 addresses (24c00) */
|
||||
};
|
||||
|
||||
#endif /* _LINUX_AT24_H */
|
|
@ -0,0 +1,55 @@
|
|||
/* ------------------------------------------------------------------------- */
|
||||
/* i2c-algo-bit.h i2c driver algorithms for bit-shift adapters */
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Copyright (C) 1995-99 Simon G. Vogl
|
||||
|
||||
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 Street, Fifth Floor, Boston,
|
||||
MA 02110-1301 USA. */
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
|
||||
Frodo Looijaard <frodol@dds.nl> */
|
||||
|
||||
#ifndef _LINUX_I2C_ALGO_BIT_H
|
||||
#define _LINUX_I2C_ALGO_BIT_H
|
||||
|
||||
/* --- Defines for bit-adapters --------------------------------------- */
|
||||
/*
|
||||
* This struct contains the hw-dependent functions of bit-style adapters to
|
||||
* manipulate the line states, and to init any hw-specific features. This is
|
||||
* only used if you have more than one hw-type of adapter running.
|
||||
*/
|
||||
struct i2c_algo_bit_data {
|
||||
void *data; /* private data for lowlevel routines */
|
||||
void (*setsda) (void *data, int state);
|
||||
void (*setscl) (void *data, int state);
|
||||
int (*getsda) (void *data);
|
||||
int (*getscl) (void *data);
|
||||
int (*pre_xfer) (struct i2c_adapter *);
|
||||
void (*post_xfer) (struct i2c_adapter *);
|
||||
|
||||
/* local settings */
|
||||
int udelay; /* half clock cycle time in us,
|
||||
minimum 2 us for fast-mode I2C,
|
||||
minimum 5 us for standard-mode I2C and SMBus,
|
||||
maximum 50 us for SMBus */
|
||||
int timeout_ms; /* in ms */
|
||||
};
|
||||
|
||||
int i2c_bit_add_bus(struct i2c_adapter *);
|
||||
int i2c_bit_add_numbered_bus(struct i2c_adapter *);
|
||||
extern const struct i2c_algorithm i2c_bit_algo;
|
||||
|
||||
#endif /* _LINUX_I2C_ALGO_BIT_H */
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* i2c-gpio interface to platform code
|
||||
*
|
||||
* Copyright (C) 2007 Atmel Corporation
|
||||
*
|
||||
* 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
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef _LINUX_I2C_GPIO_H
|
||||
#define _LINUX_I2C_GPIO_H
|
||||
|
||||
/**
|
||||
* struct i2c_gpio_platform_data - Platform-dependent data for i2c-gpio
|
||||
* @sda_pin: GPIO pin ID to use for SDA
|
||||
* @scl_pin: GPIO pin ID to use for SCL
|
||||
* @udelay: signal toggle delay. SCL frequency is (500 / udelay) kHz
|
||||
* @timeout_ms: clock stretching timeout in ms. If the slave keeps
|
||||
* SCL low for longer than this, the transfer will time out.
|
||||
* @sda_is_open_drain: SDA is configured as open drain, i.e. the pin
|
||||
* isn't actively driven high when setting the output value high.
|
||||
* gpio_get_value() must return the actual pin state even if the
|
||||
* pin is configured as an output.
|
||||
* @scl_is_open_drain: SCL is set up as open drain. Same requirements
|
||||
* as for sda_is_open_drain apply.
|
||||
* @scl_is_output_only: SCL output drivers cannot be turned off.
|
||||
*/
|
||||
struct i2c_gpio_platform_data {
|
||||
unsigned int sda_pin;
|
||||
unsigned int scl_pin;
|
||||
int udelay;
|
||||
int timeout_ms;
|
||||
unsigned int sda_is_open_drain:1;
|
||||
unsigned int scl_is_open_drain:1;
|
||||
unsigned int scl_is_output_only:1;
|
||||
};
|
||||
|
||||
#endif /* _LINUX_I2C_GPIO_H */
|
|
@ -66,10 +66,12 @@ struct i2c_msg {
|
|||
*
|
||||
*/
|
||||
struct i2c_adapter {
|
||||
struct device_d *dev; /* ptr to device */
|
||||
struct device_d dev; /* ptr to device */
|
||||
int nr; /* bus number */
|
||||
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
|
||||
struct list_head list;
|
||||
int retries;
|
||||
void *algo_data;
|
||||
};
|
||||
|
||||
|
||||
|
@ -127,6 +129,14 @@ static inline int i2c_register_board_info(int busnum,
|
|||
extern int i2c_add_numbered_adapter(struct i2c_adapter *adapter);
|
||||
struct i2c_adapter *i2c_get_adapter(int busnum);
|
||||
|
||||
/* For devices that use several addresses, use i2c_new_dummy() to make
|
||||
* client handles for the extra addresses.
|
||||
*/
|
||||
extern struct i2c_client *
|
||||
i2c_new_dummy(struct i2c_adapter *adap, u16 address);
|
||||
|
||||
|
||||
|
||||
extern int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
|
||||
extern int i2c_master_send(struct i2c_client *client, const char *buf, int count);
|
||||
extern int i2c_master_recv(struct i2c_client *client, char *buf, int count);
|
||||
|
|
|
@ -90,5 +90,20 @@
|
|||
__val = __val < __min ? __min: __val; \
|
||||
__val > __max ? __max: __val; })
|
||||
|
||||
|
||||
/* The `const' in roundup() prevents gcc-3.3 from calling __divdi3 */
|
||||
#define roundup(x, y) ( \
|
||||
{ \
|
||||
const typeof(y) __y = y; \
|
||||
(((x) + (__y - 1)) / __y) * __y; \
|
||||
} \
|
||||
)
|
||||
#define rounddown(x, y) ( \
|
||||
{ \
|
||||
typeof(x) __x = (x); \
|
||||
__x - (__x % (y)); \
|
||||
} \
|
||||
)
|
||||
|
||||
#endif /* _LINUX_KERNEL_H */
|
||||
|
||||
|
|
|
@ -0,0 +1,190 @@
|
|||
/* Integer base 2 logarithm calculation
|
||||
*
|
||||
* Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_LOG2_H
|
||||
#define _LINUX_LOG2_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
/*
|
||||
* deal with unrepresentable constant logarithms
|
||||
*/
|
||||
extern __attribute__((const, noreturn))
|
||||
int ____ilog2_NaN(void);
|
||||
|
||||
/*
|
||||
* non-constant log of base 2 calculators
|
||||
* - the arch may override these in asm/bitops.h if they can be implemented
|
||||
* more efficiently than using fls() and fls64()
|
||||
* - the arch is not required to handle n==0 if implementing the fallback
|
||||
*/
|
||||
#ifndef CONFIG_ARCH_HAS_ILOG2_U32
|
||||
static inline __attribute__((const))
|
||||
int __ilog2_u32(u32 n)
|
||||
{
|
||||
return fls(n) - 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_ARCH_HAS_ILOG2_U64
|
||||
static inline __attribute__((const))
|
||||
int __ilog2_u64(u64 n)
|
||||
{
|
||||
return fls64(n) - 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Determine whether some value is a power of two, where zero is
|
||||
* *not* considered a power of two.
|
||||
*/
|
||||
|
||||
static inline __attribute__((const))
|
||||
bool is_power_of_2(unsigned long n)
|
||||
{
|
||||
return (n != 0 && ((n & (n - 1)) == 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* ilog2 - log of base 2 of 32-bit or a 64-bit unsigned value
|
||||
* @n - parameter
|
||||
*
|
||||
* constant-capable log of base 2 calculation
|
||||
* - this can be used to initialise global variables from constant data, hence
|
||||
* the massive ternary operator construction
|
||||
*
|
||||
* selects the appropriately-sized optimised version depending on sizeof(n)
|
||||
*/
|
||||
#define ilog2(n) \
|
||||
( \
|
||||
__builtin_constant_p(n) ? ( \
|
||||
(n) < 1 ? ____ilog2_NaN() : \
|
||||
(n) & (1ULL << 63) ? 63 : \
|
||||
(n) & (1ULL << 62) ? 62 : \
|
||||
(n) & (1ULL << 61) ? 61 : \
|
||||
(n) & (1ULL << 60) ? 60 : \
|
||||
(n) & (1ULL << 59) ? 59 : \
|
||||
(n) & (1ULL << 58) ? 58 : \
|
||||
(n) & (1ULL << 57) ? 57 : \
|
||||
(n) & (1ULL << 56) ? 56 : \
|
||||
(n) & (1ULL << 55) ? 55 : \
|
||||
(n) & (1ULL << 54) ? 54 : \
|
||||
(n) & (1ULL << 53) ? 53 : \
|
||||
(n) & (1ULL << 52) ? 52 : \
|
||||
(n) & (1ULL << 51) ? 51 : \
|
||||
(n) & (1ULL << 50) ? 50 : \
|
||||
(n) & (1ULL << 49) ? 49 : \
|
||||
(n) & (1ULL << 48) ? 48 : \
|
||||
(n) & (1ULL << 47) ? 47 : \
|
||||
(n) & (1ULL << 46) ? 46 : \
|
||||
(n) & (1ULL << 45) ? 45 : \
|
||||
(n) & (1ULL << 44) ? 44 : \
|
||||
(n) & (1ULL << 43) ? 43 : \
|
||||
(n) & (1ULL << 42) ? 42 : \
|
||||
(n) & (1ULL << 41) ? 41 : \
|
||||
(n) & (1ULL << 40) ? 40 : \
|
||||
(n) & (1ULL << 39) ? 39 : \
|
||||
(n) & (1ULL << 38) ? 38 : \
|
||||
(n) & (1ULL << 37) ? 37 : \
|
||||
(n) & (1ULL << 36) ? 36 : \
|
||||
(n) & (1ULL << 35) ? 35 : \
|
||||
(n) & (1ULL << 34) ? 34 : \
|
||||
(n) & (1ULL << 33) ? 33 : \
|
||||
(n) & (1ULL << 32) ? 32 : \
|
||||
(n) & (1ULL << 31) ? 31 : \
|
||||
(n) & (1ULL << 30) ? 30 : \
|
||||
(n) & (1ULL << 29) ? 29 : \
|
||||
(n) & (1ULL << 28) ? 28 : \
|
||||
(n) & (1ULL << 27) ? 27 : \
|
||||
(n) & (1ULL << 26) ? 26 : \
|
||||
(n) & (1ULL << 25) ? 25 : \
|
||||
(n) & (1ULL << 24) ? 24 : \
|
||||
(n) & (1ULL << 23) ? 23 : \
|
||||
(n) & (1ULL << 22) ? 22 : \
|
||||
(n) & (1ULL << 21) ? 21 : \
|
||||
(n) & (1ULL << 20) ? 20 : \
|
||||
(n) & (1ULL << 19) ? 19 : \
|
||||
(n) & (1ULL << 18) ? 18 : \
|
||||
(n) & (1ULL << 17) ? 17 : \
|
||||
(n) & (1ULL << 16) ? 16 : \
|
||||
(n) & (1ULL << 15) ? 15 : \
|
||||
(n) & (1ULL << 14) ? 14 : \
|
||||
(n) & (1ULL << 13) ? 13 : \
|
||||
(n) & (1ULL << 12) ? 12 : \
|
||||
(n) & (1ULL << 11) ? 11 : \
|
||||
(n) & (1ULL << 10) ? 10 : \
|
||||
(n) & (1ULL << 9) ? 9 : \
|
||||
(n) & (1ULL << 8) ? 8 : \
|
||||
(n) & (1ULL << 7) ? 7 : \
|
||||
(n) & (1ULL << 6) ? 6 : \
|
||||
(n) & (1ULL << 5) ? 5 : \
|
||||
(n) & (1ULL << 4) ? 4 : \
|
||||
(n) & (1ULL << 3) ? 3 : \
|
||||
(n) & (1ULL << 2) ? 2 : \
|
||||
(n) & (1ULL << 1) ? 1 : \
|
||||
(n) & (1ULL << 0) ? 0 : \
|
||||
____ilog2_NaN() \
|
||||
) : \
|
||||
(sizeof(n) <= 4) ? \
|
||||
__ilog2_u32(n) : \
|
||||
__ilog2_u64(n) \
|
||||
)
|
||||
|
||||
/**
|
||||
* roundup_pow_of_two - round the given value up to nearest power of two
|
||||
* @n - parameter
|
||||
*
|
||||
* round the given value up to the nearest power of two
|
||||
* - the result is undefined when n == 0
|
||||
* - this can be used to initialise global variables from constant data
|
||||
*/
|
||||
#define roundup_pow_of_two(n) \
|
||||
( \
|
||||
__builtin_constant_p(n) ? ( \
|
||||
(n == 1) ? 1 : \
|
||||
(1UL << (ilog2((n) - 1) + 1)) \
|
||||
) : \
|
||||
__roundup_pow_of_two(n) \
|
||||
)
|
||||
|
||||
/**
|
||||
* rounddown_pow_of_two - round the given value down to nearest power of two
|
||||
* @n - parameter
|
||||
*
|
||||
* round the given value down to the nearest power of two
|
||||
* - the result is undefined when n == 0
|
||||
* - this can be used to initialise global variables from constant data
|
||||
*/
|
||||
#define rounddown_pow_of_two(n) \
|
||||
( \
|
||||
__builtin_constant_p(n) ? ( \
|
||||
(1UL << ilog2(n))) : \
|
||||
__rounddown_pow_of_two(n) \
|
||||
)
|
||||
|
||||
/**
|
||||
* order_base_2 - calculate the (rounded up) base 2 order of the argument
|
||||
* @n: parameter
|
||||
*
|
||||
* The first few values calculated by this routine:
|
||||
* ob2(0) = 0
|
||||
* ob2(1) = 0
|
||||
* ob2(2) = 1
|
||||
* ob2(3) = 2
|
||||
* ob2(4) = 2
|
||||
* ob2(5) = 3
|
||||
* ... and so on.
|
||||
*/
|
||||
|
||||
#define order_base_2(n) ilog2(roundup_pow_of_two(n))
|
||||
|
||||
#endif /* _LINUX_LOG2_H */
|
|
@ -0,0 +1,121 @@
|
|||
#ifndef _LINUX_MATH64_H
|
||||
#define _LINUX_MATH64_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <asm-generic/div64.h>
|
||||
|
||||
#if BITS_PER_LONG == 64
|
||||
|
||||
#define div64_long(x,y) div64_s64((x),(y))
|
||||
|
||||
/**
|
||||
* div_u64_rem - unsigned 64bit divide with 32bit divisor with remainder
|
||||
*
|
||||
* This is commonly provided by 32bit archs to provide an optimized 64bit
|
||||
* divide.
|
||||
*/
|
||||
static inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder)
|
||||
{
|
||||
*remainder = dividend % divisor;
|
||||
return dividend / divisor;
|
||||
}
|
||||
|
||||
/**
|
||||
* div_s64_rem - signed 64bit divide with 32bit divisor with remainder
|
||||
*/
|
||||
static inline s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder)
|
||||
{
|
||||
*remainder = dividend % divisor;
|
||||
return dividend / divisor;
|
||||
}
|
||||
|
||||
/**
|
||||
* div64_u64 - unsigned 64bit divide with 64bit divisor
|
||||
*/
|
||||
static inline u64 div64_u64(u64 dividend, u64 divisor)
|
||||
{
|
||||
return dividend / divisor;
|
||||
}
|
||||
|
||||
/**
|
||||
* div64_s64 - signed 64bit divide with 64bit divisor
|
||||
*/
|
||||
static inline s64 div64_s64(s64 dividend, s64 divisor)
|
||||
{
|
||||
return dividend / divisor;
|
||||
}
|
||||
|
||||
#elif BITS_PER_LONG == 32
|
||||
|
||||
#define div64_long(x,y) div_s64((x),(y))
|
||||
|
||||
#ifndef div_u64_rem
|
||||
static inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder)
|
||||
{
|
||||
*remainder = do_div(dividend, divisor);
|
||||
return dividend;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef div_s64_rem
|
||||
extern s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder);
|
||||
#endif
|
||||
|
||||
#ifndef div64_u64
|
||||
extern u64 div64_u64(u64 dividend, u64 divisor);
|
||||
#endif
|
||||
|
||||
#ifndef div64_s64
|
||||
extern s64 div64_s64(s64 dividend, s64 divisor);
|
||||
#endif
|
||||
|
||||
#endif /* BITS_PER_LONG */
|
||||
|
||||
/**
|
||||
* div_u64 - unsigned 64bit divide with 32bit divisor
|
||||
*
|
||||
* This is the most common 64bit divide and should be used if possible,
|
||||
* as many 32bit archs can optimize this variant better than a full 64bit
|
||||
* divide.
|
||||
*/
|
||||
#ifndef div_u64
|
||||
static inline u64 div_u64(u64 dividend, u32 divisor)
|
||||
{
|
||||
u32 remainder;
|
||||
return div_u64_rem(dividend, divisor, &remainder);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* div_s64 - signed 64bit divide with 32bit divisor
|
||||
*/
|
||||
#ifndef div_s64
|
||||
static inline s64 div_s64(s64 dividend, s32 divisor)
|
||||
{
|
||||
s32 remainder;
|
||||
return div_s64_rem(dividend, divisor, &remainder);
|
||||
}
|
||||
#endif
|
||||
|
||||
u32 iter_div_u64_rem(u64 dividend, u32 divisor, u64 *remainder);
|
||||
|
||||
static __always_inline u32
|
||||
__iter_div_u64_rem(u64 dividend, u32 divisor, u64 *remainder)
|
||||
{
|
||||
u32 ret = 0;
|
||||
|
||||
while (dividend >= divisor) {
|
||||
/* The following asm() prevents the compiler from
|
||||
optimising this loop into a modulo operation. */
|
||||
asm("" : "+rm"(dividend));
|
||||
|
||||
dividend -= divisor;
|
||||
ret++;
|
||||
}
|
||||
|
||||
*remainder = dividend;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* _LINUX_MATH64_H */
|
Loading…
Reference in New Issue