diff --git a/kernel/busses/i2c-keywest.c b/kernel/busses/i2c-keywest.c index e044910e..7dfaadf5 100644 --- a/kernel/busses/i2c-keywest.c +++ b/kernel/busses/i2c-keywest.c @@ -1,9 +1,10 @@ /* - i2c-keywest.c - Part of lm_sensors, Linux kernel modules - for hardware monitoring - i2c Support for Apple Keywest I2C Bus Controller + Copyright (c) 2001 Benjamin Herrenschmidt + + Original work by + Copyright (c) 2000 Philip Edelbrock This program is free software; you can redistribute it and/or modify @@ -22,9 +23,24 @@ Changes: - 2001/7/14 Paul Harrison: - - got write working sufficient to bring Tumbler audio up, - read still untested + 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 @@ -38,580 +54,630 @@ #include #include #include +#include +#include +#include #include #include #include #include -/* The Tumbler audio equalizer can be really slow sometimes */ -#define POLL_SANITY 10000 +#include "i2c-keywest.h" -/* PCI device */ -#define VENDOR 0x106b -#define DEVICE 0x22 +#undef POLLED_MODE -/***** Protos ******/ - -s32 keywest_access(struct i2c_adapter *adap, u16 addr, - unsigned short flags, char read_write, - u8 command, int size, union i2c_smbus_data *data); -u32 keywest_func(struct i2c_adapter *adapter); - - /**/ - -struct keywest_iface { - void *base; - void *steps; - void *mode; - void *control; - void *status; - void *ISR; - void *IER; - void *addr; - void *subaddr; - void *data; - struct i2c_adapter *i2c_adapt; - struct keywest_iface *next; -}; - -static struct i2c_algorithm smbus_algorithm = { - /* name */ "Non-I2C SMBus adapter", - /* id */ I2C_ALGO_SMBUS, - /* master_xfer */ NULL, - /* smbus_access */ keywest_access, - /* slave_send */ NULL, - /* slave_rcv */ NULL, - /* algo_control */ NULL, - /* functionality */ keywest_func, -}; - -void dump_ifaces(struct keywest_iface **ifaces); -int cleanup(struct keywest_iface **ifaces); - -/***** End of Protos ******/ +#define DBG(x...) do {\ + if (debug > 0) \ + printk(KERN_DEBUG "KW:" x); \ + } while(0) -/** Vars **/ +MODULE_AUTHOR("Benjamin Herrenschmidt "); +MODULE_DESCRIPTION("I2C driver for Apple's Keywest"); +MODULE_LICENSE("GPL"); +MODULE_PARM(probe, "i"); +MODULE_PARM(debug, "i"); +EXPORT_NO_SYMBOLS; -struct keywest_iface *ifaces = NULL; +int probe = 0; +int debug = 0; +static struct keywest_iface *ifaces = NULL; -/** Functions **/ - -/* keywest needs a small delay to defuddle itself after changing a setting */ -void writeb_wait(int value, void *addr) +#ifdef POLLED_MODE +/* This isn't fast, but will go once I implement interrupt with + * proper timeout + */ +static u8 +wait_interrupt(struct keywest_iface* iface) { - writeb(value, addr); - udelay(10); -} - -int poll_interrupt(void *ISR) -{ - int i, res; - for (i = 0; i < POLL_SANITY; i++) { - udelay(100); - - res = readb(ISR) & 0x0F; - if (res > 0) { - /* printk("i2c-keywest: received interrupt: 0x%02X\n",res); */ - return res; - } - } - - if (i == POLL_SANITY) { - printk("i2c-keywest: Sanity check failed! Expected interrupt never happened.\n"); - return -1; - } - - return -1; /* Should never get here */ -} - -int poll_ack(void *status) -{ - int i, res; - for (i = 0; i < 100; i++) { - udelay(10); - - res = readb(status) & 0x02; - if (res > 0) { - printk("i2c-keywest: got ack: 0x%02X in 0x%02X iter\n",res,i); - return res; - } - } - - if (i == POLL_SANITY) { - printk("i2c-keywest: Sanity check failed! Expected ack never happened.\n"); - return 0; - } - - return 0; /* Should never get here */ -} - - -void keywest_reset(struct keywest_iface *ifaceptr) -{ - int interrupt_state; - - /* Clear all past interrupts */ - interrupt_state = readb(ifaceptr->ISR) & 0x0F; - if (interrupt_state) - writeb(interrupt_state,ifaceptr->ISR); -} - -s32 keywest_access(struct i2c_adapter *adap, u16 addr, - unsigned short flags, char read_write, - u8 command, int size, union i2c_smbus_data *data) -{ - - struct keywest_iface *ifaceptr; - int interrupt_state = 1; - int ack; - int error_state = 0; - int len, i; /* for block transfers */ - - ifaceptr = (struct keywest_iface *) adap->data; - - keywest_reset(ifaceptr); - - /* Set up address and r/w bit */ - writeb_wait(((addr << 1) | (read_write == I2C_SMBUS_READ ?1:0)), - (void *) ifaceptr->addr); - - /* Set up 'sub address' which I'm guessing is the command field? */ - writeb_wait(command, (void *) ifaceptr->subaddr); + int i; + u8 isr; - /* Start sending address */ - writeb_wait(readb(ifaceptr->control) | 2, ifaceptr->control); - interrupt_state = poll_interrupt(ifaceptr->ISR); + for (i = 0; i < POLL_TIMEOUT; i++) { + isr = read_reg(reg_isr) & KW_I2C_IRQ_MASK; + if (isr != 0) + return isr; + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(1); + } + return isr; +} +#endif /* POLLED_MODE */ - ack = readb(ifaceptr->status) & 0x0F; - if ((ack & 0x02) == 0) { - printk("i2c-keywest: Ack Status on addr expected but got: 0x%02X on addr: 0x%02X\n", - ack, addr); - return -1; - } +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; +} - /* Set ACK if reading */ - if (read_write == I2C_SMBUS_READ) - writeb_wait(1 | readb(ifaceptr->control), ifaceptr->control); +/* Main state machine for standard & standard sub mode */ +static void +handle_interrupt(struct keywest_iface *iface, u8 isr) +{ + int ack; + + 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; + } + 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 { + iface->state = state_idle; + write_reg(reg_control, 0x00); + write_reg(reg_ier, 0x00); +#ifndef POLLED_MODE + complete(&iface->complete); +#endif /* POLLED_MODE */ + } + break; + } + + write_reg(reg_isr, isr); +} + +#ifndef POLLED_MODE + +/* 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); + handle_interrupt(iface, read_reg(reg_isr)); + if (iface->state != state_idle) { + 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); + handle_interrupt(iface, read_reg(reg_isr)); + if (iface->state != state_idle) { + iface->timeout_timer.expires = jiffies + POLL_TIMEOUT; + add_timer(&iface->timeout_timer); + } + spin_unlock(&iface->lock); +} + +#endif /* POLLED_MODE */ + +/* + * 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: - /* Send stop */ - writeb_wait(readb(ifaceptr->control) | 4, ifaceptr->control); - writeb_wait(interrupt_state, ifaceptr->control); - interrupt_state = poll_interrupt(ifaceptr->ISR); - if (interrupt_state < 0) - error_state = -1; - writeb_wait(interrupt_state, ifaceptr->ISR); - break; /* we're done! */ + 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: - if (read_write == I2C_SMBUS_WRITE) { - writeb_wait(data->byte, ifaceptr->data); - - /* Clear interrupt and go */ - writeb_wait(interrupt_state, ifaceptr->ISR); - interrupt_state = poll_interrupt(ifaceptr->ISR); - if (interrupt_state < 0) - error_state = -1; - - if ((readb(ifaceptr->status) & 0x02) == 0) { - printk("i2c-keywest: Ack Expected by not received(2)!\n"); - error_state = -1; - } - - /* Send stop */ - writeb_wait(readb(ifaceptr->control) | 4, ifaceptr->control); - - writeb_wait(interrupt_state, ifaceptr->control); - - interrupt_state = poll_interrupt(ifaceptr->ISR); - if (interrupt_state < 0) - error_state = -1; - writeb_wait(interrupt_state, ifaceptr->ISR); - } else { - /* Clear interrupt and go */ - writeb_wait(interrupt_state, ifaceptr->ISR); - interrupt_state = poll_interrupt(ifaceptr->ISR); - data->byte = readb(ifaceptr->data); - - /* End read: clear ack */ - writeb_wait(0, ifaceptr->control); - } - break; - + 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: - if (read_write == I2C_SMBUS_WRITE) { - writeb_wait(data->word & 0x0ff, ifaceptr->data); - - /* Clear interrupt and go */ - writeb_wait(interrupt_state, ifaceptr->ISR); - interrupt_state = poll_interrupt(ifaceptr->ISR); - if (interrupt_state < 0) - error_state = -1; - - if ((readb(ifaceptr->status) & 0x02) == 0) { - printk("i2c-keywest: Ack Expected by not received(2)!\n"); - error_state = -1; - } - - writeb_wait((data->word & 0x0ff00) >> 8, - ifaceptr->data); - - /* Clear interrupt and go */ - writeb_wait(interrupt_state, ifaceptr->ISR); - interrupt_state = poll_interrupt(ifaceptr->ISR); - if (interrupt_state < 0) - error_state = -1; - - if ((readb(ifaceptr->status) & 0x02) == 0) { - printk("i2c-keywest: Ack Expected by not received(3)!\n"); - error_state = -1; - } - - /* Send stop */ - writeb_wait(readb(ifaceptr->control) | 4, ifaceptr->control); - - writeb_wait(interrupt_state, ifaceptr->control); - - interrupt_state = poll_interrupt(ifaceptr->ISR); - if (interrupt_state < 0) - error_state = -1; - writeb_wait(interrupt_state, ifaceptr->ISR); - } else { - /* Clear interrupt and go */ - writeb_wait(interrupt_state, ifaceptr->ISR); - interrupt_state = - poll_interrupt(ifaceptr->ISR); - data->word = - (readb(ifaceptr->data) << 8); - - /* Send ack */ - writeb_wait(1, ifaceptr->control); - - /* Clear interrupt and go */ - writeb_wait(interrupt_state, ifaceptr->ISR); - interrupt_state = poll_interrupt(ifaceptr->ISR); - data->word |= (readb(ifaceptr->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: - if (read_write == I2C_SMBUS_WRITE) { - len = data->block[0]; - if (len < 0) - len = 0; - if (len > 32) - len = 32; - - for(i=1; i<=len; i++) { - writeb_wait(data->block[i], - ifaceptr->data); - - /* Clear interrupt and go */ - writeb_wait(interrupt_state,ifaceptr->ISR); - - interrupt_state = poll_interrupt(ifaceptr->ISR); - if ((readb(ifaceptr->status) & 0x02) == 0) { - printk("i2c-keywest: Ack Expected by not received(block)!\n"); - error_state = -1; - } - } - - /* Send stop */ - writeb_wait(readb(ifaceptr->control) | 4, ifaceptr->control); - - writeb_wait(interrupt_state, ifaceptr->control); - - interrupt_state = poll_interrupt(ifaceptr->ISR); - if (interrupt_state < 0) - error_state = -1; - writeb_wait(interrupt_state, ifaceptr->ISR); - } else { - for(i=1; i<=data->block[0]; i++) { - /* Send ack */ - writeb_wait(1, ifaceptr->control); - - /* Clear interrupt and go */ - writeb_wait(interrupt_state, ifaceptr->ISR); - interrupt_state = poll_interrupt(ifaceptr->ISR); - data->block[i] = readb(ifaceptr->data); - } - } + 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: - printk("i2c-keywest: operation not supported\n"); - error_state = -1; + return -1; } - /* End read: clear ack */ - if (read_write == I2C_SMBUS_READ) - writeb_wait(0, ifaceptr->control); + /* Original driver had this limitation */ + if (len > 32) + len = 32; - return error_state; + 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); + +#ifdef POLLED_MODE + DBG("using polled mode...\n"); + /* State machine, to turn into an interrupt handler */ + while(iface->state != state_idle) { + u8 isr = wait_interrupt(iface); + handle_interrupt(iface, isr); + } +#else /* POLLED_MODE */ + DBG("using interrupt mode...\n"); + wait_for_completion(&iface->complete); +#endif /* POLLED_MODE */ + + 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; } -u32 keywest_func(struct i2c_adapter * adapter) +/* + * 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); + +#ifdef POLLED_MODE + DBG("using polled mode...\n"); + /* State machine, to turn into an interrupt handler */ + while(iface->state != state_idle) { + u8 isr = wait_interrupt(iface); + handle_interrupt(iface, isr); + } +#else /* POLLED_MODE */88 + DBG("using interrupt mode...\n"); + wait_for_completion(&iface->complete); +#endif /* POLLED_MODE */ + + 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; } -void keywest_inc(struct i2c_adapter *adapter) +static void +keywest_inc(struct i2c_adapter *adapter) { MOD_INC_USE_COUNT; } -void keywest_dec(struct i2c_adapter *adapter) +static void +keywest_dec(struct i2c_adapter *adapter) { MOD_DEC_USE_COUNT; } -/**** Internal Functions ****/ +/* 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, +}; -int init_iface(void *base, void *steps, struct keywest_iface **ifaces) + +static int +create_iface(struct device_node* np) { - struct i2c_adapter *keywest_adapter; - struct keywest_iface **temp_hdl = ifaces; - int res; + unsigned long steps, *psteps, *prate; + unsigned bsteps, tsize, i, nchan, addroffset; + struct keywest_iface* iface; + int rc; - if (ifaces == NULL) { - printk("Ah! Passed a null handle to init_iface"); - return -1; - } + psteps = (unsigned long *)get_property(np, "AAPL,address-step", NULL); + steps = psteps ? (*psteps) : 0x10; - while ((*temp_hdl) != NULL) { - temp_hdl = &(*temp_hdl)->next; - } + /* Hrm... maybe we can be smarter here */ + for (bsteps = 0; (steps & 0x01) == 0; bsteps++) + steps >>= 1; - *temp_hdl = - (struct keywest_iface *) kmalloc(sizeof(struct keywest_iface), - GFP_KERNEL); - if (*temp_hdl == NULL) { - printk("kmalloc failed on temp_hdl!"); - return -1; - } - - (*temp_hdl)->next = NULL; - base = ioremap((int) base, (int) steps * 8); - (*temp_hdl)->base = base; - (*temp_hdl)->steps = steps; - (*temp_hdl)->mode = (void *) base; - (*temp_hdl)->control = (void *) ((__u32) base + (__u32) steps); - (*temp_hdl)->status = (void *) ((__u32) base + (__u32) steps * 2); - (*temp_hdl)->ISR = (void *) ((__u32) base + (__u32) steps * 3); - (*temp_hdl)->IER = (void *) ((__u32) base + (__u32) steps * 4); - (*temp_hdl)->addr = (void *) ((__u32) base + (__u32) steps * 5); - (*temp_hdl)->subaddr = (void *) ((__u32) base + (__u32) steps * 6); - (*temp_hdl)->data = (void *) ((__u32) base + (__u32) steps * 7); - - keywest_adapter = - (struct i2c_adapter *) kmalloc(sizeof(struct i2c_adapter), - GFP_KERNEL); - if (keywest_adapter == NULL) { - printk("kmalloc failed on keywest_adapter!"); - return -1; - } - memset((void *) keywest_adapter, 0, (sizeof(struct i2c_adapter))); - - strcpy(keywest_adapter->name, "keywest i2c\0"); - keywest_adapter->id = I2C_ALGO_SMBUS; - keywest_adapter->algo = &smbus_algorithm; - keywest_adapter->algo_data = NULL; - keywest_adapter->inc_use = keywest_inc; - keywest_adapter->dec_use = keywest_dec; - keywest_adapter->client_register = NULL; - keywest_adapter->client_unregister = NULL; - keywest_adapter->data = (void *) (*temp_hdl); - - (*temp_hdl)->i2c_adapt = keywest_adapter; - - if ((res = i2c_add_adapter((*temp_hdl)->i2c_adapt)) != 0) { - printk("i2c-keywest.o: Adapter registration failed, module not inserted.\n"); - /* cleanup(); */ - } - - /* Now actually do the initialization of the device */ - - /* Select standard sub mode - - ie for
... style transactions - */ - writeb_wait(0x08, (*temp_hdl)->mode); - - /* Enable interrupts */ - writeb_wait(1 + 2 + 4 + 8, (*temp_hdl)->IER); - - keywest_reset(*temp_hdl); - - return res; -} - -void dump_ifaces(struct keywest_iface **ifaces) -{ - if (ifaces == NULL) { - printk("Ah! Passed null handle to dump!\n"); - return; - } - if (*ifaces != NULL) { - printk("Interface @%X,%X Locations:\n", (u32) (*ifaces)->base, - (u32) (*ifaces)->steps); - printk(" mode:%X control:%X status:%X ISR:%X IER:%X addr:%X subaddr:%X data:%X\n", - (u32) (*ifaces)->mode, (u32) (*ifaces)->control, - (u32) (*ifaces)->status, (u32) (*ifaces)->ISR, (u32) (*ifaces)->IER, - (u32) (*ifaces)->addr, (u32) (*ifaces)->subaddr, (u32) (*ifaces)->data); - printk("Contents:\n"); - printk(" mode:0x%02X control: 0x%02X status:0x%02X ISR:0x%02X IER:0x%02X addr:0x%02X subaddr:0x%02X data:0x%02X\n", - readb((*ifaces)->mode), readb((*ifaces)->control), - readb((*ifaces)->status), readb((*ifaces)->ISR), - readb((*ifaces)->IER), readb((*ifaces)->addr), - readb((*ifaces)->subaddr), readb((*ifaces)->data)); - printk("I2C-Adapter:\n"); - printk(" name:%s\n", (*ifaces)->i2c_adapt->name); - dump_ifaces(&(*ifaces)->next); + if (!strcmp(np->parent->name, "uni-n")) { + nchan = 2; + addroffset = 3; } else { - printk("End of ifaces.\n"); + addroffset = 0; + nchan = 1; } -} -int cleanup(struct keywest_iface **ifaces) -{ - int res = 0; + 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; + } - if (ifaces == NULL) { - printk("Ah! Passed null handle to cleanup!\n"); - return 0; + 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); } - if (*ifaces != NULL) { - if (cleanup(&(*ifaces)->next) != 0) - res = -1; - - printk("Cleaning up interface @%X,%X\n", (u32) (*ifaces)->base, - (u32) (*ifaces)->steps); - - if (i2c_del_adapter((*ifaces)->i2c_adapt) != 0) { - printk("i2c-keywest.o: i2c_del_adapter failed, module not removed\n"); - res = -1; - } - - kfree(*ifaces); - iounmap((*ifaces)->base); - (*ifaces) = NULL; - } - - return res; -} - -#ifdef DEBUG -static void scan_of(char *dev_type) -{ - struct device_node *np = NULL; - struct property *dp = NULL; - int i; - - np = find_devices(dev_type); - - if (np == 0) { - printk("No %s devices found.\n", dev_type); - } + /* Select standard mode by default */ + iface->cur_mode |= KW_I2C_MODE_STANDARD; - while (np != 0) { - printk("%s found: %s, with properties:\n", dev_type, - np->full_name); - dp = np->properties; - while (dp != 0) { - printk(" %s = %s [ ", dp->name, dp->value); - for (i = 0; i < dp->length; i++) { - printk("%02X", (char) dp->value[i]); - if (((i + 1) % 4) == 0) { - printk(" "); - } - } - printk("] (length=%d)\n", dp->length); - dp = dp->next; - } - np = np->next; - } -} -#endif - -static int find_keywest(void) -{ - struct device_node *i2c_device; - void **temp; - void *base = NULL; - void *steps = NULL; - -#ifdef DEBUG - scan_of("i2c"); -#endif - - i2c_device = find_compatible_devices("i2c", "keywest"); + /* Write mode */ + write_reg(reg_mode, iface->cur_mode); - if (i2c_device == 0) { - printk("No Keywest i2c devices found.\n"); - return -1; - } - - while (i2c_device != 0) { - printk("Keywest device found: %s\n", - i2c_device->full_name); - temp = - (void **) get_property(i2c_device, "AAPL,address", - NULL); - if (temp != NULL) { - base = *temp; - } else { - printk("no 'address' prop!\n"); - } + /* Switch interrupts off & clear them*/ + write_reg(reg_ier, 0x00); + write_reg(reg_isr, KW_I2C_IRQ_MASK); - temp = - (void **) get_property(i2c_device, "AAPL,address-step", - NULL); - if (temp != NULL) { - steps = *temp; - } else { - printk("no 'address-step' prop!\n"); - } +#ifndef POLLED_MODE + /* 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; + } +#endif /* POLLED_MODE */ + + for (i=0; ichannels[i]; + u8 addr; - /* printk("Device found. base: %X steps: %X\n", base, steps); */ + 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.inc_use = keywest_inc; + chan->adapter.dec_use = keywest_dec; + chan->adapter.client_register = NULL; + chan->adapter.client_unregister = NULL; + chan->adapter.data = chan; - if (init_iface(base, steps, &ifaces) != 0) { - cleanup(&ifaces); - return -ENODEV; + rc = i2c_add_adapter(&chan->adapter); + if (rc) { + 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"); + } + } - i2c_device = i2c_device->next; - } - - if (ifaces == NULL) { - printk("ifaces null! :'("); - } - - /* dump_ifaces(&ifaces); */ + 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 +dispose_iface(struct keywest_iface *iface) +{ + int i, rc; + + ifaces = iface->next; + + /* Make sure we stop all activity */ + down(&iface->sem); +#ifndef POLLED_MODE + spin_lock_irq(&iface->lock); + while (iface->state != state_idle) { + spin_unlock_irq(&iface->lock); + schedule(); + spin_lock_irq(&iface->lock); + } +#endif /* POLLED_MODE */ + iface->state = state_dead; +#ifndef POLLED_MODE + spin_unlock_irq(&iface->lock); + free_irq(iface->irq, iface); +#endif /* POLLED_MODE */ + up(&iface->sem); + + /* Release all channels */ + for (i=0; ichan_count; i++) { + struct keywest_chan* chan = &iface->channels[i]; + if (!chan->adapter.data) + continue; + rc = i2c_del_adapter(&chan->adapter); + chan->adapter.data = 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); + kfree(iface); +} + static int __init i2c_keywest_init(void) { - return find_keywest(); + struct device_node *np; + int rc = -ENODEV; + + np = find_compatible_devices("i2c", "keywest"); + while (np != 0) { + if (np->n_addrs >= 1 && np->n_intrs >= 1) + rc = create_iface(np); + np = np->next; + } + if (ifaces) + rc = 0; + return rc; } static void __exit i2c_keywest_cleanup(void) { - cleanup(&ifaces); + while(ifaces) + dispose_iface(ifaces); } module_init(i2c_keywest_init); module_exit(i2c_keywest_cleanup); - -MODULE_AUTHOR("Philip Edelbrock "); -MODULE_DESCRIPTION("I2C driver for Apple's Keywest"); -EXPORT_NO_SYMBOLS; diff --git a/kernel/busses/i2c-keywest.h b/kernel/busses/i2c-keywest.h new file mode 100644 index 00000000..9e384a5c --- /dev/null +++ b/kernel/busses/i2c-keywest.h @@ -0,0 +1,111 @@ +#ifndef __I2C_KEYWEST_H__ +#define __I2C_KEYWEST_H__ + +/* The Tumbler audio equalizer can be really slow sometimes */ +#define POLL_TIMEOUT (2*HZ) + +/* Register indices */ +typedef enum { + reg_mode = 0, + reg_control, + reg_status, + reg_isr, + reg_ier, + reg_addr, + reg_subaddr, + reg_data +} reg_t; + + +/* Mode register */ +#define KW_I2C_MODE_100KHZ 0x00 +#define KW_I2C_MODE_50KHZ 0x01 +#define KW_I2C_MODE_25KHZ 0x02 +#define KW_I2C_MODE_DUMB 0x00 +#define KW_I2C_MODE_STANDARD 0x04 +#define KW_I2C_MODE_STANDARDSUB 0x08 +#define KW_I2C_MODE_COMBINED 0x0C +#define KW_I2C_MODE_MODE_MASK 0x0C +#define KW_I2C_MODE_CHAN_MASK 0xF0 + +/* Control register */ +#define KW_I2C_CTL_AAK 0x01 +#define KW_I2C_CTL_XADDR 0x02 +#define KW_I2C_CTL_STOP 0x04 +#define KW_I2C_CTL_START 0x08 + +/* Status register */ +#define KW_I2C_STAT_BUSY 0x01 +#define KW_I2C_STAT_LAST_AAK 0x02 +#define KW_I2C_STAT_LAST_RW 0x04 +#define KW_I2C_STAT_SDA 0x08 +#define KW_I2C_STAT_SCL 0x10 + +/* IER & ISR registers */ +#define KW_I2C_IRQ_DATA 0x01 +#define KW_I2C_IRQ_ADDR 0x02 +#define KW_I2C_IRQ_STOP 0x04 +#define KW_I2C_IRQ_START 0x08 +#define KW_I2C_IRQ_MASK 0x0F + +/* Physical interface */ +struct keywest_iface +{ + unsigned long base; + unsigned bsteps; + int irq; + struct semaphore sem; + spinlock_t lock; + struct keywest_chan* channels; + unsigned chan_count; + u8 cur_mode; + char read_write; + u8* data; + unsigned datalen; + int state; + int result; + int stopretry; + struct timer_list timeout_timer; + struct completion complete; + struct keywest_iface* next; +}; + +enum { + state_idle, + state_addr, + state_read, + state_write, + state_stop, + state_dead +}; + +/* Channel on an interface */ +struct keywest_chan +{ + struct i2c_adapter adapter; + struct keywest_iface* iface; + unsigned chan_no; +}; + +/* Register access */ + +static inline u8 __read_reg(struct keywest_iface *iface, reg_t reg) +{ + return in_8(((volatile u8 *)iface->base) + + (((unsigned)reg) << iface->bsteps)); +} + +static inline void __write_reg(struct keywest_iface *iface, reg_t reg, u8 val) +{ + out_8(((volatile u8 *)iface->base) + + (((unsigned)reg) << iface->bsteps), val); + (void)__read_reg(iface, reg); + udelay(10); +} + +#define write_reg(reg, val) __write_reg(iface, reg, val) +#define read_reg(reg) __read_reg(iface, reg) + + + +#endif /* __I2C_KEYWEST_H__ */