Files
lm-sensors/kernel/busses/i2c-keywest.c
T
Kyosti Malkki 1cfe63804a (Kyösti) Cleanups
Copy sysctl enums to chip drivers from sensors.h for now,
   as seen in drivers included in 2.5 tree. File no longer included
   from kernel side.

   Apply i2c-proc change in CVS tagged -km2.

   Partial clean and sort of includes everywhere.

   Add i2c-dev.h, as a partial copy from i2c.
   Add to sensors.h from i2c-proc.h to compile things.
   Remove i2c-isa.h.

   Reflect header file changes to lib/ and prog/.


git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@1705 7894878c-1315-0410-8ee3-d5d059ff63e0
2003-01-21 20:01:27 +00:00

616 lines
15 KiB
C

/*
i2c Support for Apple Keywest I2C Bus Controller
Copyright (c) 2001 Benjamin Herrenschmidt <benh@kernel.crashing.org>
Original work by
Copyright (c) 2000 Philip Edelbrock <phil@stimpy.netroedge.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.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
Changes:
2001/12/13 BenH New implementation
2001/12/15 BenH Add support for "byte" and "quick"
transfers. Add i2c_xfer routine.
My understanding of the various modes supported by keywest are:
- Dumb mode : not implemented, probably direct tweaking of lines
- Standard mode : simple i2c transaction of type
S Addr R/W A Data A Data ... T
- Standard sub mode : combined 8 bit subaddr write with data read
S Addr R/W A SubAddr A Data A Data ... T
- Combined mode : Subaddress and Data sequences appended with no stop
S Addr R/W A SubAddr S Addr R/W A Data A Data ... T
Currently, this driver uses only Standard mode for i2c xfer, and
smbus byte & quick transfers ; and uses StandardSub mode for
other smbus transfers instead of combined as we need that for the
sound driver to be happy
*/
#include <linux/module.h>
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/pci.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/timer.h>
#include <linux/spinlock.h>
#include <linux/completion.h>
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/pmac_feature.h>
#include "i2c-keywest.h"
#define DBG(x...) do {\
if (debug > 0) \
printk(KERN_DEBUG "KW:" x); \
} while(0)
MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
MODULE_DESCRIPTION("I2C driver for Apple's Keywest");
MODULE_LICENSE("GPL");
MODULE_PARM(probe, "i");
MODULE_PARM(debug, "i");
int probe = 0;
int debug = 0;
static struct keywest_iface *ifaces = NULL;
static void
do_stop(struct keywest_iface* iface, int result)
{
write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_STOP);
iface->state = state_stop;
iface->result = result;
}
/* Main state machine for standard & standard sub mode */
static int
handle_interrupt(struct keywest_iface *iface, u8 isr)
{
int ack;
int rearm_timer = 1;
DBG("handle_interrupt(), got: %x, status: %x, state: %d\n",
isr, read_reg(reg_status), iface->state);
if (isr == 0 && iface->state != state_stop) {
do_stop(iface, -1);
return rearm_timer;
}
if (isr & KW_I2C_IRQ_STOP && iface->state != state_stop) {
iface->result = -1;
iface->state = state_stop;
}
switch(iface->state) {
case state_addr:
if (!(isr & KW_I2C_IRQ_ADDR)) {
do_stop(iface, -1);
break;
}
ack = read_reg(reg_status);
DBG("ack on set address: %x\n", ack);
if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {
do_stop(iface, -1);
break;
}
/* Handle rw "quick" mode */
if (iface->datalen == 0)
do_stop(iface, 0);
else if (iface->read_write == I2C_SMBUS_READ) {
iface->state = state_read;
if (iface->datalen > 1)
write_reg(reg_control, read_reg(reg_control)
| KW_I2C_CTL_AAK);
} else {
iface->state = state_write;
DBG("write byte: %x\n", *(iface->data));
write_reg(reg_data, *(iface->data++));
iface->datalen--;
}
break;
case state_read:
if (!(isr & KW_I2C_IRQ_DATA)) {
do_stop(iface, -1);
break;
}
*(iface->data++) = read_reg(reg_data);
DBG("read byte: %x\n", *(iface->data-1));
iface->datalen--;
if (iface->datalen == 0)
iface->state = state_stop;
else
write_reg(reg_control, 0);
break;
case state_write:
if (!(isr & KW_I2C_IRQ_DATA)) {
do_stop(iface, -1);
break;
}
/* Check ack status */
ack = read_reg(reg_status);
DBG("ack on data write: %x\n", ack);
if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {
do_stop(iface, -1);
break;
}
if (iface->datalen) {
DBG("write byte: %x\n", *(iface->data));
write_reg(reg_data, *(iface->data++));
iface->datalen--;
} else
do_stop(iface, 0);
break;
case state_stop:
if (!(isr & KW_I2C_IRQ_STOP) && (++iface->stopretry) < 10)
do_stop(iface, -1);
else {
rearm_timer = 0;
iface->state = state_idle;
write_reg(reg_control, 0x00);
write_reg(reg_ier, 0x00);
complete(&iface->complete);
}
break;
}
write_reg(reg_isr, isr);
return rearm_timer;
}
/* Interrupt handler */
static void
keywest_irq(int irq, void *dev_id, struct pt_regs *regs)
{
struct keywest_iface *iface = (struct keywest_iface *)dev_id;
spin_lock(&iface->lock);
del_timer(&iface->timeout_timer);
if (handle_interrupt(iface, read_reg(reg_isr))) {
iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
add_timer(&iface->timeout_timer);
}
spin_unlock(&iface->lock);
}
static void
keywest_timeout(unsigned long data)
{
struct keywest_iface *iface = (struct keywest_iface *)data;
DBG("timeout !\n");
spin_lock_irq(&iface->lock);
if (iface->state != state_idle) {
iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
add_timer(&iface->timeout_timer);
}
spin_unlock(&iface->lock);
}
/*
* SMBUS-type transfer entrypoint
*/
static s32
keywest_smbus_xfer( struct i2c_adapter* adap,
u16 addr,
unsigned short flags,
char read_write,
u8 command,
int size,
union i2c_smbus_data* data)
{
struct keywest_chan* chan = (struct keywest_chan*)adap->data;
struct keywest_iface* iface = chan->iface;
int len;
u8* buffer;
u16 cur_word;
int rc = 0;
if (iface->state == state_dead)
return -1;
/* Prepare datas & select mode */
iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK;
switch (size) {
case I2C_SMBUS_QUICK:
len = 0;
buffer = NULL;
iface->cur_mode |= KW_I2C_MODE_STANDARD;
break;
case I2C_SMBUS_BYTE:
len = 1;
buffer = &data->byte;
iface->cur_mode |= KW_I2C_MODE_STANDARD;
break;
case I2C_SMBUS_BYTE_DATA:
len = 1;
buffer = &data->byte;
iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
//iface->cur_mode |= KW_I2C_MODE_COMBINED;
break;
case I2C_SMBUS_WORD_DATA:
len = 2;
cur_word = cpu_to_le16(data->word);
buffer = (u8 *)&cur_word;
iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
//iface->cur_mode |= KW_I2C_MODE_COMBINED;
break;
case I2C_SMBUS_BLOCK_DATA:
len = data->block[0];
buffer = &data->block[1];
iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
//iface->cur_mode |= KW_I2C_MODE_COMBINED;
break;
default:
return -1;
}
/* Original driver had this limitation */
if (len > 32)
len = 32;
down(&iface->sem);
DBG("chan: %d, addr: 0x%x, transfer len: %d, read: %d\n",
chan->chan_no, addr, len, read_write == I2C_SMBUS_READ);
iface->data = buffer;
iface->datalen = len;
iface->state = state_addr;
iface->result = 0;
iface->stopretry = 0;
iface->read_write = read_write;
/* Setup channel & clear pending irqs */
write_reg(reg_mode, iface->cur_mode | (chan->chan_no << 4));
write_reg(reg_isr, read_reg(reg_isr));
write_reg(reg_status, 0);
/* Set up address and r/w bit */
write_reg(reg_addr,
(addr << 1) | ((read_write == I2C_SMBUS_READ) ? 0x01 : 0x00));
/* Set up the sub address */
if ((iface->cur_mode & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_STANDARDSUB
|| (iface->cur_mode & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_COMBINED)
write_reg(reg_subaddr, command);
/* Arm timeout */
iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
add_timer(&iface->timeout_timer);
/* Start sending address & enable interrupt*/
write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_XADDR);
write_reg(reg_ier, KW_I2C_IRQ_MASK);
wait_for_completion(&iface->complete);
rc = iface->result;
DBG("transfer done, result: %d\n", rc);
if (rc == 0 && size == I2C_SMBUS_WORD_DATA && read_write == I2C_SMBUS_READ)
data->word = le16_to_cpu(cur_word);
/* Release sem */
up(&iface->sem);
return rc;
}
/*
* Generic i2c master transfer entrypoint
*/
static int
keywest_xfer( struct i2c_adapter *adap,
struct i2c_msg msgs[],
int num)
{
struct keywest_chan* chan = (struct keywest_chan*)adap->data;
struct keywest_iface* iface = chan->iface;
struct i2c_msg *pmsg;
int i, completed;
int rc = 0;
down(&iface->sem);
/* Set adapter to standard mode */
iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK;
iface->cur_mode |= KW_I2C_MODE_STANDARD;
completed = 0;
for (i = 0; rc >= 0 && i < num;) {
u8 addr;
pmsg = &msgs[i++];
addr = pmsg->addr;
if (pmsg->flags & I2C_M_TEN) {
printk(KERN_ERR "i2c-keywest: 10 bits addr not supported !\n");
rc = -EINVAL;
break;
}
DBG("xfer: chan: %d, doing %s %d bytes to 0x%02x - %d of %d messages\n",
chan->chan_no,
pmsg->flags & I2C_M_RD ? "read" : "write",
pmsg->len, addr, i, num);
/* Setup channel & clear pending irqs */
write_reg(reg_mode, iface->cur_mode | (chan->chan_no << 4));
write_reg(reg_isr, read_reg(reg_isr));
write_reg(reg_status, 0);
iface->data = pmsg->buf;
iface->datalen = pmsg->len;
iface->state = state_addr;
iface->result = 0;
iface->stopretry = 0;
if (pmsg->flags & I2C_M_RD)
iface->read_write = I2C_SMBUS_READ;
else
iface->read_write = I2C_SMBUS_WRITE;
/* Set up address and r/w bit */
if (pmsg->flags & I2C_M_REV_DIR_ADDR)
addr ^= 1;
write_reg(reg_addr,
(addr << 1) |
((iface->read_write == I2C_SMBUS_READ) ? 0x01 : 0x00));
/* Arm timeout */
iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
add_timer(&iface->timeout_timer);
/* Start sending address & enable interrupt*/
write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_XADDR);
write_reg(reg_ier, KW_I2C_IRQ_MASK);
wait_for_completion(&iface->complete);
rc = iface->result;
if (rc == 0)
completed++;
DBG("transfer done, result: %d\n", rc);
}
/* Release sem */
up(&iface->sem);
return completed;
}
static u32
keywest_func(struct i2c_adapter * adapter)
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA;
}
/* For now, we only handle combined mode (smbus) */
static struct i2c_algorithm keywest_algorithm = {
.name = "Keywest i2c",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = keywest_smbus_xfer,
.master_xfer = keywest_xfer,
.functionality = keywest_func,
};
static int __init
create_iface(struct device_node* np)
{
unsigned long steps, *psteps, *prate;
unsigned bsteps, tsize, i, nchan, addroffset;
struct keywest_iface* iface;
int error;
psteps = (unsigned long *)get_property(np, "AAPL,address-step", NULL);
steps = psteps ? (*psteps) : 0x10;
/* Hrm... maybe we can be smarter here */
for (bsteps = 0; (steps & 0x01) == 0; bsteps++)
steps >>= 1;
if (!strcmp(np->parent->name, "uni-n")) {
nchan = 2;
addroffset = 3;
} else {
addroffset = 0;
nchan = 1;
}
tsize = sizeof(struct keywest_iface) +
(sizeof(struct keywest_chan) + 4) * nchan;
iface = (struct keywest_iface *) kmalloc(tsize, GFP_KERNEL);
if (iface == NULL) {
printk(KERN_ERR "i2c-keywest: can't allocate inteface !\n");
return -ENOMEM;
}
memset(iface, 0, tsize);
init_MUTEX(&iface->sem);
spin_lock_init(&iface->lock);
init_completion(&iface->complete);
iface->bsteps = bsteps;
iface->chan_count = nchan;
iface->state = state_idle;
iface->irq = np->intrs[0].line;
iface->channels = (struct keywest_chan *)
(((unsigned long)(iface + 1) + 3UL) & ~3UL);
iface->base = (unsigned long)ioremap(np->addrs[0].address + addroffset,
np->addrs[0].size);
if (iface->base == 0) {
printk(KERN_ERR "i2c-keywest: can't map inteface !\n");
kfree(iface);
return -ENOMEM;
}
init_timer(&iface->timeout_timer);
iface->timeout_timer.function = keywest_timeout;
iface->timeout_timer.data = (unsigned long)iface;
/* Select interface rate */
iface->cur_mode = KW_I2C_MODE_100KHZ;
prate = (unsigned long *)get_property(np, "AAPL,i2c-rate", NULL);
if (prate) switch(*prate) {
case 100:
iface->cur_mode = KW_I2C_MODE_100KHZ;
break;
case 50:
iface->cur_mode = KW_I2C_MODE_50KHZ;
break;
case 25:
iface->cur_mode = KW_I2C_MODE_25KHZ;
break;
default:
printk(KERN_WARNING "i2c-keywest: unknown rate %ldKhz, using 100KHz\n",
*prate);
}
/* Select standard mode by default */
iface->cur_mode |= KW_I2C_MODE_STANDARD;
/* Write mode */
write_reg(reg_mode, iface->cur_mode);
/* Switch interrupts off & clear them*/
write_reg(reg_ier, 0x00);
write_reg(reg_isr, KW_I2C_IRQ_MASK);
/* Request chip interrupt */
error = request_irq(iface->irq, keywest_irq, 0, "keywest i2c", iface);
if (error) {
printk(KERN_ERR "i2c-keywest: can't get IRQ %d !\n", iface->irq);
iounmap((void *)iface->base);
kfree(iface);
return -ENODEV;
}
for (i=0; i<nchan; i++) {
struct keywest_chan* chan = &iface->channels[i];
u8 addr;
sprintf(chan->adapter.name, "%s %d", np->parent->name, i);
chan->iface = iface;
chan->chan_no = i;
chan->adapter.id = I2C_ALGO_SMBUS;
chan->adapter.algo = &keywest_algorithm;
chan->adapter.algo_data = NULL;
chan->adapter.client_register = NULL;
chan->adapter.client_unregister = NULL;
chan->adapter.data = chan;
error = i2c_add_adapter(&chan->adapter);
if (error) {
printk("i2c-keywest.c: Adapter %s registration failed\n",
chan->adapter.name);
chan->adapter.data = NULL;
}
if (probe) {
printk("Probe: ");
for (addr = 0x00; addr <= 0x7f; addr++) {
if (i2c_smbus_xfer(&chan->adapter,addr,
0,0,0,I2C_SMBUS_QUICK,NULL) >= 0)
printk("%02x ", addr);
}
printk("\n");
}
}
printk(KERN_INFO "Found KeyWest i2c on \"%s\", %d channel%s, stepping: %d bits\n",
np->parent->name, nchan, nchan > 1 ? "s" : "", bsteps);
iface->next = ifaces;
ifaces = iface;
return 0;
}
static void __exit
dispose_iface(struct keywest_iface *iface)
{
int i, error;
ifaces = iface->next;
/* Make sure we stop all activity */
down(&iface->sem);
spin_lock_irq(&iface->lock);
while (iface->state != state_idle) {
spin_unlock_irq(&iface->lock);
set_task_state(current,TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ/10);
spin_lock_irq(&iface->lock);
}
iface->state = state_dead;
spin_unlock_irq(&iface->lock);
free_irq(iface->irq, iface);
up(&iface->sem);
/* Release all channels */
for (i=0; i<iface->chan_count; i++) {
struct keywest_chan* chan = &iface->channels[i];
if (!chan->adapter.data)
continue;
i2c_del_adapter(&chan->adapter);
chan->adapter.data = NULL;
/* We aren't that prepared to deal with this... */
if (error)
printk("i2c-keywest.c: i2c_del_adapter failed, that's bad !\n");
}
iounmap((void *)iface->base);
kfree(iface);
}
static int __init i2c_keywest_init(void)
{
struct device_node *np;
int error = -ENODEV;
np = find_compatible_devices("i2c", "keywest");
while (np != 0) {
if (np->n_addrs >= 1 && np->n_intrs >= 1)
error = create_iface(np);
np = np->next;
}
if (ifaces)
error = 0;
return error;
}
static void __exit i2c_keywest_exit(void)
{
while(ifaces)
dispose_iface(ifaces);
}
module_init(i2c_keywest_init);
module_exit(i2c_keywest_exit);