/*
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 <linux/interrupt.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 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 irqreturn_t
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
)))
mod_timer
(&iface
->timeout_timer
, jiffies
+ POLL_TIMEOUT
);
spin_unlock
(&iface
->lock
);
return IRQ_HANDLED
;
}
static void
keywest_timeout
(unsigned long data
)
{
struct keywest_iface
*iface
= (struct keywest_iface
*)data
;
DBG
("timeout !\n");
spin_lock_irq
(&iface
->lock
);
if (handle_interrupt
(iface
, read_reg
(reg_isr
)))
mod_timer
(&iface
->timeout_timer
, jiffies
+ POLL_TIMEOUT
);
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
= i2c_get_adapdata
(adap
);
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
;
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
;
break;
case I2C_SMBUS_BLOCK_DATA
:
len
= data
->block
[0];
buffer
= &data
->block
[1];
iface
->cur_mode
|= KW_I2C_MODE_STANDARDSUB
;
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 */
mod_timer
(&iface
->timeout_timer
, jiffies
+ POLL_TIMEOUT
);
/* 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 interrupt operations completion */
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
= i2c_get_adapdata
(adap
);
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 */
mod_timer
(&iface
->timeout_timer
, jiffies
+ POLL_TIMEOUT
);
/* 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 interrupt operations completion */
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
create_iface
(struct device_node
*np
, struct device
*dev
)
{
unsigned long steps
, *psteps
, *prate
;
unsigned bsteps
, tsize
, i
, nchan
, addroffset
;
struct keywest_iface
* iface
;
int rc
;
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 sub mode */
iface
->cur_mode
|= KW_I2C_MODE_STANDARDSUB
;
/* 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 */
rc
= request_irq
(iface
->irq
, keywest_irq
, 0, "keywest i2c", iface
);
if (rc
) {
printk
(KERN_ERR
"i2c-keywest: can't get IRQ %d !\n", iface
->irq
);
iounmap
((void *)iface
->base
);
kfree
(iface
);
return -ENODEV
;
}
dev_set_drvdata
(dev
, iface
);
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
;
i2c_set_adapdata
(&chan
->adapter
, chan
);
chan
->adapter.
dev.
parent = dev
;
rc
= i2c_add_adapter
(&chan
->adapter
);
if (rc
) {
printk
("i2c-keywest.c: Adapter %s registration failed\n",
chan
->adapter.
name);
i2c_set_adapdata
(&chan
->adapter
, 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
);
return 0;
}
static int
dispose_iface
(struct device
*dev
)
{
struct keywest_iface
*iface
= dev_get_drvdata
(dev
);
int i
, rc
;
/* 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 (i2c_get_adapdata
(&chan
->adapter
) == NULL
)
continue;
rc
= i2c_del_adapter
(&chan
->adapter
);
i2c_set_adapdata
(&chan
->adapter
, NULL
);
/* We aren't that prepared to deal with this... */
if (rc
)
printk
("i2c-keywest.c: i2c_del_adapter failed, that's bad !\n");
}
iounmap
((void *)iface
->base
);
dev_set_drvdata
(dev
, NULL
);
kfree
(iface
);
return 0;
}
static int
create_iface_macio
(struct macio_dev
* dev
, const struct of_match
*match
)
{
return create_iface
(dev
->ofdev.
node, &dev
->ofdev.
dev);
}
static int
dispose_iface_macio
(struct macio_dev
* dev
)
{
return dispose_iface
(&dev
->ofdev.
dev);
}
static int
create_iface_of_platform
(struct of_device
* dev
, const struct of_match
*match
)
{
return create_iface
(dev
->node
, &dev
->dev
);
}
static int
dispose_iface_of_platform
(struct of_device
* dev
)
{
return dispose_iface
(&dev
->dev
);
}
static struct of_match i2c_keywest_match
[] =
{
{
.
name = OF_ANY_MATCH
,
.
type = "i2c",
.
compatible = "keywest"
},
{},
};
static struct macio_driver i2c_keywest_macio_driver
=
{
.
name = "i2c-keywest",
.
match_table = i2c_keywest_match
,
.
probe = create_iface_macio
,
.
remove = dispose_iface_macio
};
static struct of_platform_driver i2c_keywest_of_platform_driver
=
{
.
name = "i2c-keywest",
.
match_table = i2c_keywest_match
,
.
probe = create_iface_of_platform
,
.
remove = dispose_iface_of_platform
};
static int __init
i2c_keywest_init
(void)
{
macio_register_driver
(&i2c_keywest_macio_driver
);
of_register_driver
(&i2c_keywest_of_platform_driver
);
return 0;
}
static void __exit
i2c_keywest_cleanup
(void)
{
macio_unregister_driver
(&i2c_keywest_macio_driver
);
of_unregister_driver
(&i2c_keywest_of_platform_driver
);
}
module_init
(i2c_keywest_init
);
module_exit
(i2c_keywest_cleanup
);