diff options
Diffstat (limited to 'drivers/i2c/busses')
-rw-r--r-- | drivers/i2c/busses/Kconfig | 24 | ||||
-rw-r--r-- | drivers/i2c/busses/Makefile | 3 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-keywest.c | 751 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-keywest.h | 108 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-pmac-smu.c | 315 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-powermac.c | 290 |
6 files changed, 298 insertions, 1193 deletions
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 4010fe9..08d5b8f 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -236,27 +236,17 @@ config I2C_IXP2000 This support is also available as a module. If so, the module will be called i2c-ixp2000. -config I2C_KEYWEST - tristate "Powermac Keywest I2C interface" +config I2C_POWERMAC + tristate "Powermac I2C interface" depends on I2C && PPC_PMAC + default y help - This supports the use of the I2C interface in the combo-I/O - chip on recent Apple machines. Say Y if you have such a machine. - - This support is also available as a module. If so, the module - will be called i2c-keywest. - -config I2C_PMAC_SMU - tristate "Powermac SMU I2C interface" - depends on I2C && PMAC_SMU - help - This supports the use of the I2C interface in the SMU - chip on recent Apple machines like the iMac G5. It is used - among others by the thermal control driver for those machines. - Say Y if you have such a machine. + This exposes the various PowerMac i2c interfaces to the linux i2c + layer and to userland. It is used by various drivers on the powemac + platform, thus should generally be enabled. This support is also available as a module. If so, the module - will be called i2c-pmac-smu. + will be called i2c-powermac. config I2C_MPC tristate "MPC107/824x/85xx/52xx" diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index f1df00f..b44831d 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -19,8 +19,7 @@ obj-$(CONFIG_I2C_ISA) += i2c-isa.o obj-$(CONFIG_I2C_ITE) += i2c-ite.o obj-$(CONFIG_I2C_IXP2000) += i2c-ixp2000.o obj-$(CONFIG_I2C_IXP4XX) += i2c-ixp4xx.o -obj-$(CONFIG_I2C_KEYWEST) += i2c-keywest.o -obj-$(CONFIG_I2C_PMAC_SMU) += i2c-pmac-smu.o +obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o obj-$(CONFIG_I2C_MPC) += i2c-mpc.o obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o diff --git a/drivers/i2c/busses/i2c-keywest.c b/drivers/i2c/busses/i2c-keywest.c deleted file mode 100644 index d61f748..0000000 --- a/drivers/i2c/busses/i2c-keywest.c +++ /dev/null @@ -1,751 +0,0 @@ -/* - 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. - 2003/09/21 BenH Rework state machine with Paulus help - 2004/01/21 BenH Merge in Greg KH changes, polled mode is back - 2004/02/05 BenH Merge 64 bits fixes from the g5 ppc64 tree - - 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/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 <asm/pmac_low_i2c.h> - -#include "i2c-keywest.h" - -#undef POLLED_MODE - -/* Some debug macros */ -#define WRONG_STATE(name) do {\ - pr_debug("KW: wrong state. Got %s, state: %s (isr: %02x)\n", \ - name, __kw_state_names[iface->state], isr); \ - } while(0) - -#ifdef DEBUG -static const char *__kw_state_names[] = { - "state_idle", - "state_addr", - "state_read", - "state_write", - "state_stop", - "state_dead" -}; -#endif /* DEBUG */ - -MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); -MODULE_DESCRIPTION("I2C driver for Apple's Keywest"); -MODULE_LICENSE("GPL"); - -#ifdef POLLED_MODE -/* Don't schedule, the g5 fan controller is too - * timing sensitive - */ -static u8 -wait_interrupt(struct keywest_iface* iface) -{ - int i; - u8 isr; - - for (i = 0; i < 200000; i++) { - isr = read_reg(reg_isr) & KW_I2C_IRQ_MASK; - if (isr != 0) - return isr; - udelay(10); - } - return isr; -} -#endif /* POLLED_MODE */ - -static void -do_stop(struct keywest_iface* iface, int result) -{ - write_reg(reg_control, KW_I2C_CTL_STOP); - iface->state = state_stop; - iface->result = result; -} - -/* Main state machine for standard & standard sub mode */ -static void -handle_interrupt(struct keywest_iface *iface, u8 isr) -{ - int ack; - - if (isr == 0) { - if (iface->state != state_stop) { - pr_debug("KW: Timeout !\n"); - do_stop(iface, -EIO); - } - if (iface->state == state_stop) { - ack = read_reg(reg_status); - if (!(ack & KW_I2C_STAT_BUSY)) { - iface->state = state_idle; - write_reg(reg_ier, 0x00); -#ifndef POLLED_MODE - complete(&iface->complete); -#endif /* POLLED_MODE */ - } - } - return; - } - - if (isr & KW_I2C_IRQ_ADDR) { - ack = read_reg(reg_status); - if (iface->state != state_addr) { - write_reg(reg_isr, KW_I2C_IRQ_ADDR); - WRONG_STATE("KW_I2C_IRQ_ADDR"); - do_stop(iface, -EIO); - return; - } - if ((ack & KW_I2C_STAT_LAST_AAK) == 0) { - iface->state = state_stop; - iface->result = -ENODEV; - pr_debug("KW: NAK on address\n"); - } else { - /* 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, KW_I2C_CTL_AAK); - } else { - iface->state = state_write; - write_reg(reg_data, *(iface->data++)); - iface->datalen--; - } - } - write_reg(reg_isr, KW_I2C_IRQ_ADDR); - } - - if (isr & KW_I2C_IRQ_DATA) { - if (iface->state == state_read) { - *(iface->data++) = read_reg(reg_data); - write_reg(reg_isr, KW_I2C_IRQ_DATA); - iface->datalen--; - if (iface->datalen == 0) - iface->state = state_stop; - else if (iface->datalen == 1) - write_reg(reg_control, 0); - } else if (iface->state == state_write) { - /* Check ack status */ - ack = read_reg(reg_status); - if ((ack & KW_I2C_STAT_LAST_AAK) == 0) { - pr_debug("KW: nack on data write (%x): %x\n", - iface->data[-1], ack); - do_stop(iface, -EIO); - } else if (iface->datalen) { - write_reg(reg_data, *(iface->data++)); - iface->datalen--; - } else { - write_reg(reg_control, KW_I2C_CTL_STOP); - iface->state = state_stop; - iface->result = 0; - } - write_reg(reg_isr, KW_I2C_IRQ_DATA); - } else { - write_reg(reg_isr, KW_I2C_IRQ_DATA); - WRONG_STATE("KW_I2C_IRQ_DATA"); - if (iface->state != state_stop) - do_stop(iface, -EIO); - } - } - - if (isr & KW_I2C_IRQ_STOP) { - write_reg(reg_isr, KW_I2C_IRQ_STOP); - if (iface->state != state_stop) { - WRONG_STATE("KW_I2C_IRQ_STOP"); - iface->result = -EIO; - } - iface->state = state_idle; - write_reg(reg_ier, 0x00); -#ifndef POLLED_MODE - complete(&iface->complete); -#endif /* POLLED_MODE */ - } - - if (isr & KW_I2C_IRQ_START) - write_reg(reg_isr, KW_I2C_IRQ_START); -} - -#ifndef POLLED_MODE - -/* 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; - unsigned long flags; - - spin_lock_irqsave(&iface->lock, flags); - 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_irqrestore(&iface->lock, flags); - return IRQ_HANDLED; -} - -static void -keywest_timeout(unsigned long data) -{ - struct keywest_iface *iface = (struct keywest_iface *)data; - unsigned long flags; - - pr_debug("timeout !\n"); - spin_lock_irqsave(&iface->lock, flags); - 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_irqrestore(&iface->lock, flags); -} - -#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 = 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 -ENXIO; - - /* 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; - } - - /* Turn a standardsub read into a combined mode access */ - if (read_write == I2C_SMBUS_READ - && (iface->cur_mode & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_STANDARDSUB) { - iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK; - iface->cur_mode |= KW_I2C_MODE_COMBINED; - } - - /* Original driver had this limitation */ - if (len > 32) - len = 32; - - if (pmac_low_i2c_lock(iface->node)) - return -ENXIO; - - pr_debug("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->read_write = read_write; - - /* Setup channel & clear pending irqs */ - write_reg(reg_isr, read_reg(reg_isr)); - write_reg(reg_mode, iface->cur_mode | (chan->chan_no << 4)); - 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); - -#ifndef POLLED_MODE - /* Arm timeout */ - iface->timeout_timer.expires = jiffies + POLL_TIMEOUT; - add_timer(&iface->timeout_timer); -#endif - - /* Start sending address & enable interrupt*/ - write_reg(reg_control, KW_I2C_CTL_XADDR); - write_reg(reg_ier, KW_I2C_IRQ_MASK); - -#ifdef POLLED_MODE - pr_debug("using polled mode...\n"); - /* State machine, to turn into an interrupt handler */ - while(iface->state != state_idle) { - unsigned long flags; - - u8 isr = wait_interrupt(iface); - spin_lock_irqsave(&iface->lock, flags); - handle_interrupt(iface, isr); - spin_unlock_irqrestore(&iface->lock, flags); - } -#else /* POLLED_MODE */ - pr_debug("using interrupt mode...\n"); - wait_for_completion(&iface->complete); -#endif /* POLLED_MODE */ - - rc = iface->result; - pr_debug("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 */ - pmac_low_i2c_unlock(iface->node); - - 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; - - if (iface->state == state_dead) - return -ENXIO; - - if (pmac_low_i2c_lock(iface->node)) - return -ENXIO; - - /* 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; - } - pr_debug("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; - 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)); - -#ifndef POLLED_MODE - /* Arm timeout */ - iface->timeout_timer.expires = jiffies + POLL_TIMEOUT; - add_timer(&iface->timeout_timer); -#endif - - /* Start sending address & enable interrupt*/ - write_reg(reg_ier, KW_I2C_IRQ_MASK); - write_reg(reg_control, KW_I2C_CTL_XADDR); - -#ifdef POLLED_MODE - pr_debug("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 */ - pr_debug("using interrupt mode...\n"); - wait_for_completion(&iface->complete); -#endif /* POLLED_MODE */ - - rc = iface->result; - if (rc == 0) - completed++; - pr_debug("transfer done, result: %d\n", rc); - } - - /* Release sem */ - pmac_low_i2c_unlock(iface->node); - - 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 = { - .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; - unsigned bsteps, tsize, i, nchan, addroffset; - struct keywest_iface* iface; - u32 *psteps, *prate; - int rc; - - if (np->n_intrs < 1 || np->n_addrs < 1) { - printk(KERN_ERR "%s: Missing interrupt or address !\n", - np->full_name); - return -ENODEV; - } - if (pmac_low_i2c_lock(np)) - return -ENODEV; - - psteps = (u32 *)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 (np->parent->name[0] == 'u') { - nchan = 2; - addroffset = 3; - } else { - addroffset = 0; - nchan = 1; - } - - tsize = sizeof(struct keywest_iface) + - (sizeof(struct keywest_chan) + 4) * nchan; - iface = kzalloc(tsize, GFP_KERNEL); - if (iface == NULL) { - printk(KERN_ERR "i2c-keywest: can't allocate inteface !\n"); - pmac_low_i2c_unlock(np); - return -ENOMEM; - } - spin_lock_init(&iface->lock); - init_completion(&iface->complete); - iface->node = of_node_get(np); - 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 = ioremap(np->addrs[0].address + addroffset, - np->addrs[0].size); - if (!iface->base) { - printk(KERN_ERR "i2c-keywest: can't map inteface !\n"); - kfree(iface); - pmac_low_i2c_unlock(np); - return -ENOMEM; - } - -#ifndef POLLED_MODE - init_timer(&iface->timeout_timer); - iface->timeout_timer.function = keywest_timeout; - iface->timeout_timer.data = (unsigned long)iface; -#endif - - /* Select interface rate */ - iface->cur_mode = KW_I2C_MODE_100KHZ; - prate = (u32 *)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", - (long)*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); - -#ifndef POLLED_MODE - /* Request chip interrupt */ - rc = request_irq(iface->irq, keywest_irq, SA_INTERRUPT, "keywest i2c", iface); - if (rc) { - printk(KERN_ERR "i2c-keywest: can't get IRQ %d !\n", iface->irq); - iounmap(iface->base); - kfree(iface); - pmac_low_i2c_unlock(np); - return -ENODEV; - } -#endif /* POLLED_MODE */ - - pmac_low_i2c_unlock(np); - dev_set_drvdata(dev, iface); - - for (i=0; i<nchan; i++) { - struct keywest_chan* chan = &iface->channels[i]; - - sprintf(chan->adapter.name, "%s %d", np->parent->name, i); - chan->iface = iface; - chan->chan_no = i; - 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); - } - } - - 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 */ - if (pmac_low_i2c_lock(iface->node)) - return -ENODEV; - -#ifndef POLLED_MODE - spin_lock_irq(&iface->lock); - while (iface->state != state_idle) { - spin_unlock_irq(&iface->lock); - msleep(100); - 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 */ - - pmac_low_i2c_unlock(iface->node); - - /* 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(iface->base); - dev_set_drvdata(dev, NULL); - of_node_put(iface->node); - kfree(iface); - - return 0; -} - -static int -create_iface_macio(struct macio_dev* dev, const struct of_device_id *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_device_id *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_device_id i2c_keywest_match[] = -{ - { - .type = "i2c", - .compatible = "keywest" - }, - {}, -}; - -static struct macio_driver i2c_keywest_macio_driver = -{ - .owner = THIS_MODULE, - .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 = -{ - .owner = THIS_MODULE, - .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) -{ - of_register_driver(&i2c_keywest_of_platform_driver); - macio_register_driver(&i2c_keywest_macio_driver); - - return 0; -} - -static void __exit -i2c_keywest_cleanup(void) -{ - of_unregister_driver(&i2c_keywest_of_platform_driver); - macio_unregister_driver(&i2c_keywest_macio_driver); -} - -module_init(i2c_keywest_init); -module_exit(i2c_keywest_cleanup); diff --git a/drivers/i2c/busses/i2c-keywest.h b/drivers/i2c/busses/i2c-keywest.h deleted file mode 100644 index c5022e1..0000000 --- a/drivers/i2c/busses/i2c-keywest.h +++ /dev/null @@ -1,108 +0,0 @@ -#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 -{ - struct device_node *node; - void __iomem * base; - unsigned bsteps; - int irq; - spinlock_t lock; - struct keywest_chan *channels; - unsigned chan_count; - u8 cur_mode; - char read_write; - u8 *data; - unsigned datalen; - int state; - int result; - struct timer_list timeout_timer; - struct completion complete; -}; - -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(iface->base - + (((unsigned)reg) << iface->bsteps)); -} - -static inline void __write_reg(struct keywest_iface *iface, reg_t reg, u8 val) -{ - out_8(iface->base - + (((unsigned)reg) << iface->bsteps), val); - (void)__read_reg(iface, reg_subaddr); -} - -#define write_reg(reg, val) __write_reg(iface, reg, val) -#define read_reg(reg) __read_reg(iface, reg) - - - -#endif /* __I2C_KEYWEST_H__ */ diff --git a/drivers/i2c/busses/i2c-pmac-smu.c b/drivers/i2c/busses/i2c-pmac-smu.c deleted file mode 100644 index bfefe7f..0000000 --- a/drivers/i2c/busses/i2c-pmac-smu.c +++ /dev/null @@ -1,315 +0,0 @@ -/* - i2c Support for Apple SMU Controller - - Copyright (c) 2005 Benjamin Herrenschmidt, IBM Corp. - <benh@kernel.crashing.org> - - 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. - -*/ - -#include <linux/config.h> -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/types.h> -#include <linux/i2c.h> -#include <linux/init.h> -#include <linux/completion.h> -#include <linux/device.h> -#include <asm/prom.h> -#include <asm/of_device.h> -#include <asm/smu.h> - -static int probe; - -MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); -MODULE_DESCRIPTION("I2C driver for Apple's SMU"); -MODULE_LICENSE("GPL"); -module_param(probe, bool, 0); - - -/* Physical interface */ -struct smu_iface -{ - struct i2c_adapter adapter; - struct completion complete; - u32 busid; -}; - -static void smu_i2c_done(struct smu_i2c_cmd *cmd, void *misc) -{ - struct smu_iface *iface = misc; - complete(&iface->complete); -} - -/* - * SMBUS-type transfer entrypoint - */ -static s32 smu_smbus_xfer( struct i2c_adapter* adap, - u16 addr, - unsigned short flags, - char read_write, - u8 command, - int size, - union i2c_smbus_data* data) -{ - struct smu_iface *iface = i2c_get_adapdata(adap); - struct smu_i2c_cmd cmd; - int rc = 0; - int read = (read_write == I2C_SMBUS_READ); - - cmd.info.bus = iface->busid; - cmd.info.devaddr = (addr << 1) | (read ? 0x01 : 0x00); - - /* Prepare datas & select mode */ - switch (size) { - case I2C_SMBUS_QUICK: - cmd.info.type = SMU_I2C_TRANSFER_SIMPLE; - cmd.info.datalen = 0; - break; - case I2C_SMBUS_BYTE: - cmd.info.type = SMU_I2C_TRANSFER_SIMPLE; - cmd.info.datalen = 1; - if (!read) - cmd.info.data[0] = data->byte; - break; - case I2C_SMBUS_BYTE_DATA: - cmd.info.type = SMU_I2C_TRANSFER_STDSUB; - cmd.info.datalen = 1; - cmd.info.sublen = 1; - cmd.info.subaddr[0] = command; - cmd.info.subaddr[1] = 0; - cmd.info.subaddr[2] = 0; - if (!read) - cmd.info.data[0] = data->byte; - break; - case I2C_SMBUS_WORD_DATA: - cmd.info.type = SMU_I2C_TRANSFER_STDSUB; - cmd.info.datalen = 2; - cmd.info.sublen = 1; - cmd.info.subaddr[0] = command; - cmd.info.subaddr[1] = 0; - cmd.info.subaddr[2] = 0; - if (!read) { - cmd.info.data[0] = data->byte & 0xff; - cmd.info.data[1] = (data->byte >> 8) & 0xff; - } - break; - /* Note that these are broken vs. the expected smbus API where - * on reads, the lenght is actually returned from the function, - * but I think the current API makes no sense and I don't want - * any driver that I haven't verified for correctness to go - * anywhere near a pmac i2c bus anyway ... - */ - case I2C_SMBUS_BLOCK_DATA: - cmd.info.type = SMU_I2C_TRANSFER_STDSUB; - cmd.info.datalen = data->block[0] + 1; - if (cmd.info.datalen > 6) - return -EINVAL; - if (!read) - memcpy(cmd.info.data, data->block, cmd.info.datalen); - cmd.info.sublen = 1; - cmd.info.subaddr[0] = command; - cmd.info.subaddr[1] = 0; - cmd.info.subaddr[2] = 0; - break; - case I2C_SMBUS_I2C_BLOCK_DATA: - cmd.info.type = SMU_I2C_TRANSFER_STDSUB; - cmd.info.datalen = data->block[0]; - if (cmd.info.datalen > 7) - return -EINVAL; - if (!read) - memcpy(cmd.info.data, &data->block[1], - cmd.info.datalen); - cmd.info.sublen = 1; - cmd.info.subaddr[0] = command; - cmd.info.subaddr[1] = 0; - cmd.info.subaddr[2] = 0; - break; - - default: - return -EINVAL; - } - - /* Turn a standardsub read into a combined mode access */ - if (read_write == I2C_SMBUS_READ && - cmd.info.type == SMU_I2C_TRANSFER_STDSUB) - cmd.info.type = SMU_I2C_TRANSFER_COMBINED; - - /* Finish filling command and submit it */ - cmd.done = smu_i2c_done; - cmd.misc = iface; - rc = smu_queue_i2c(&cmd); - if (rc < 0) - return rc; - wait_for_completion(&iface->complete); - rc = cmd.status; - - if (!read || rc < 0) - return rc; - - switch (size) { - case I2C_SMBUS_BYTE: - case I2C_SMBUS_BYTE_DATA: - data->byte = cmd.info.data[0]; - break; - case I2C_SMBUS_WORD_DATA: - data->word = ((u16)cmd.info.data[1]) << 8; - data->word |= cmd.info.data[0]; - break; - /* Note that these are broken vs. the expected smbus API where - * on reads, the lenght is actually returned from the function, - * but I think the current API makes no sense and I don't want - * any driver that I haven't verified for correctness to go - * anywhere near a pmac i2c bus anyway ... - */ - case I2C_SMBUS_BLOCK_DATA: - case I2C_SMBUS_I2C_BLOCK_DATA: - memcpy(&data->block[0], cmd.info.data, cmd.info.datalen); - break; - } - - return rc; -} - -static u32 -smu_smbus_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 smu_algorithm = { - .smbus_xfer = smu_smbus_xfer, - .functionality = smu_smbus_func, -}; - -static int create_iface(struct device_node *np, struct device *dev) -{ - struct smu_iface* iface; - u32 *reg, busid; - int rc; - - reg = (u32 *)get_property(np, "reg", NULL); - if (reg == NULL) { - printk(KERN_ERR "i2c-pmac-smu: can't find bus number !\n"); - return -ENXIO; - } - busid = *reg; - - iface = kzalloc(sizeof(struct smu_iface), GFP_KERNEL); - if (iface == NULL) { - printk(KERN_ERR "i2c-pmac-smu: can't allocate inteface !\n"); - return -ENOMEM; - } - init_completion(&iface->complete); - iface->busid = busid; - - dev_set_drvdata(dev, iface); - - sprintf(iface->adapter.name, "smu-i2c-%02x", busid); - iface->adapter.algo = &smu_algorithm; - iface->adapter.algo_data = NULL; - iface->adapter.client_register = NULL; - iface->adapter.client_unregister = NULL; - i2c_set_adapdata(&iface->adapter, iface); - iface->adapter.dev.parent = dev; - - rc = i2c_add_adapter(&iface->adapter); - if (rc) { - printk(KERN_ERR "i2c-pamc-smu.c: Adapter %s registration " - "failed\n", iface->adapter.name); - i2c_set_adapdata(&iface->adapter, NULL); - } - - if (probe) { - unsigned char addr; - printk("Probe: "); - for (addr = 0x00; addr <= 0x7f; addr++) { - if (i2c_smbus_xfer(&iface->adapter,addr, - 0,0,0,I2C_SMBUS_QUICK,NULL) >= 0) - printk("%02x ", addr); - } - printk("\n"); - } - - printk(KERN_INFO "SMU i2c bus %x registered\n", busid); - - return 0; -} - -static int dispose_iface(struct device *dev) -{ - struct smu_iface *iface = dev_get_drvdata(dev); - int rc; - - rc = i2c_del_adapter(&iface->adapter); - i2c_set_adapdata(&iface->adapter, NULL); - /* We aren't that prepared to deal with this... */ - if (rc) - printk("i2c-pmac-smu.c: Failed to remove bus %s !\n", - iface->adapter.name); - dev_set_drvdata(dev, NULL); - kfree(iface); - - return 0; -} - - -static int create_iface_of_platform(struct of_device* dev, - const struct of_device_id *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_device_id i2c_smu_match[] = -{ - { - .compatible = "smu-i2c", - }, - {}, -}; -static struct of_platform_driver i2c_smu_of_platform_driver = -{ - .name = "i2c-smu", - .match_table = i2c_smu_match, - .probe = create_iface_of_platform, - .remove = dispose_iface_of_platform -}; - - -static int __init i2c_pmac_smu_init(void) -{ - of_register_driver(&i2c_smu_of_platform_driver); - return 0; -} - - -static void __exit i2c_pmac_smu_cleanup(void) -{ - of_unregister_driver(&i2c_smu_of_platform_driver); -} - -module_init(i2c_pmac_smu_init); -module_exit(i2c_pmac_smu_cleanup); diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c new file mode 100644 index 0000000..df786eb --- /dev/null +++ b/drivers/i2c/busses/i2c-powermac.c @@ -0,0 +1,290 @@ +/* + i2c Support for Apple SMU Controller + + Copyright (c) 2005 Benjamin Herrenschmidt, IBM Corp. + <benh@kernel.crashing.org> + + 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. + +*/ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/completion.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <asm/prom.h> +#include <asm/pmac_low_i2c.h> + +MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); +MODULE_DESCRIPTION("I2C driver for Apple PowerMac"); +MODULE_LICENSE("GPL"); + +/* + * SMBUS-type transfer entrypoint + */ +static s32 i2c_powermac_smbus_xfer( struct i2c_adapter* adap, + u16 addr, + unsigned short flags, + char read_write, + u8 command, + int size, + union i2c_smbus_data* data) +{ + struct pmac_i2c_bus *bus = i2c_get_adapdata(adap); + int rc = 0; + int read = (read_write == I2C_SMBUS_READ); + int addrdir = (addr << 1) | read; + u8 local[2]; + + rc = pmac_i2c_open(bus, 0); + if (rc) + return rc; + + switch (size) { + case I2C_SMBUS_QUICK: + rc = pmac_i2c_setmode(bus, pmac_i2c_mode_std); + if (rc) + goto bail; + rc = pmac_i2c_xfer(bus, addrdir, 0, 0, NULL, 0); + break; + case I2C_SMBUS_BYTE: + rc = pmac_i2c_setmode(bus, pmac_i2c_mode_std); + if (rc) + goto bail; + rc = pmac_i2c_xfer(bus, addrdir, 0, 0, &data->byte, 1); + break; + case I2C_SMBUS_BYTE_DATA: + rc = pmac_i2c_setmode(bus, read ? + pmac_i2c_mode_combined : + pmac_i2c_mode_stdsub); + if (rc) + goto bail; + rc = pmac_i2c_xfer(bus, addrdir, 1, command, &data->byte, 1); + break; + case I2C_SMBUS_WORD_DATA: + rc = pmac_i2c_setmode(bus, read ? + pmac_i2c_mode_combined : + pmac_i2c_mode_stdsub); + if (rc) + goto bail; + if (!read) { + local[0] = data->word & 0xff; + local[1] = (data->word >> 8) & 0xff; + } + rc = pmac_i2c_xfer(bus, addrdir, 1, command, local, 2); + if (rc == 0 && read) { + data->word = ((u16)local[1]) << 8; + data->word |= local[0]; + } + break; + + /* Note that these are broken vs. the expected smbus API where + * on reads, the lenght is actually returned from the function, + * but I think the current API makes no sense and I don't want + * any driver that I haven't verified for correctness to go + * anywhere near a pmac i2c bus anyway ... + * + * I'm also not completely sure what kind of phases to do between + * the actual command and the data (what I am _supposed_ to do that + * is). For now, I assume writes are a single stream and reads have + * a repeat start/addr phase (but not stop in between) + */ + case I2C_SMBUS_BLOCK_DATA: + rc = pmac_i2c_setmode(bus, read ? + pmac_i2c_mode_combined : + pmac_i2c_mode_stdsub); + if (rc) + goto bail; + rc = pmac_i2c_xfer(bus, addrdir, 1, command, data->block, + data->block[0] + 1); + + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + rc = pmac_i2c_setmode(bus, read ? + pmac_i2c_mode_combined : + pmac_i2c_mode_stdsub); + if (rc) + goto bail; + rc = pmac_i2c_xfer(bus, addrdir, 1, command, + read ? data->block : &data->block[1], + data->block[0]); + break; + + default: + rc = -EINVAL; + } + bail: + pmac_i2c_close(bus); + return rc; +} + +/* + * Generic i2c master transfer entrypoint. This driver only support single + * messages (for "lame i2c" transfers). Anything else should use the smbus + * entry point + */ +static int i2c_powermac_master_xfer( struct i2c_adapter *adap, + struct i2c_msg *msgs, + int num) +{ + struct pmac_i2c_bus *bus = i2c_get_adapdata(adap); + int rc = 0; + int read; + int addrdir; + + if (num != 1) + return -EINVAL; + if (msgs->flags & I2C_M_TEN) + return -EINVAL; + read = (msgs->flags & I2C_M_RD) != 0; + addrdir = (msgs->addr << 1) | read; + if (msgs->flags & I2C_M_REV_DIR_ADDR) + addrdir ^= 1; + + rc = pmac_i2c_open(bus, 0); + if (rc) + return rc; + rc = pmac_i2c_setmode(bus, pmac_i2c_mode_std); + if (rc) + goto bail; + rc = pmac_i2c_xfer(bus, addrdir, 0, 0, msgs->buf, msgs->len); + bail: + pmac_i2c_close(bus); + return rc < 0 ? rc : msgs->len; +} + +static u32 i2c_powermac_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 | I2C_FUNC_I2C; +} + +/* For now, we only handle smbus */ +static struct i2c_algorithm i2c_powermac_algorithm = { + .smbus_xfer = i2c_powermac_smbus_xfer, + .master_xfer = i2c_powermac_master_xfer, + .functionality = i2c_powermac_func, +}; + + +static int i2c_powermac_remove(struct device *dev) +{ + struct i2c_adapter *adapter = dev_get_drvdata(dev); + struct pmac_i2c_bus *bus = i2c_get_adapdata(adapter); + int rc; + + rc = i2c_del_adapter(adapter); + pmac_i2c_detach_adapter(bus, adapter); + i2c_set_adapdata(adapter, NULL); + /* We aren't that prepared to deal with this... */ + if (rc) + printk("i2c-powermac.c: Failed to remove bus %s !\n", + adapter->name); + dev_set_drvdata(dev, NULL); + kfree(adapter); + + return 0; +} + + +static int i2c_powermac_probe(struct device *dev) +{ + struct pmac_i2c_bus *bus = dev->platform_data; + struct device_node *parent = NULL; + struct i2c_adapter *adapter; + char name[32], *basename; + int rc; + + if (bus == NULL) + return -EINVAL; + + /* Ok, now we need to make up a name for the interface that will + * match what we used to do in the past, that is basically the + * controller's parent device node for keywest. PMU didn't have a + * naming convention and SMU has a different one + */ + switch(pmac_i2c_get_type(bus)) { + case pmac_i2c_bus_keywest: + parent = of_get_parent(pmac_i2c_get_controller(bus)); + if (parent == NULL) + return -EINVAL; + basename = parent->name; + break; + case pmac_i2c_bus_pmu: + basename = "pmu"; + break; + case pmac_i2c_bus_smu: + /* This is not what we used to do but I'm fixing drivers at + * the same time as this change + */ + basename = "smu"; + break; + default: + return -EINVAL; + } + snprintf(name, 32, "%s %d", basename, pmac_i2c_get_channel(bus)); + of_node_put(parent); + + adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL); + if (adapter == NULL) { + printk(KERN_ERR "i2c-powermac: can't allocate inteface !\n"); + return -ENOMEM; + } + dev_set_drvdata(dev, adapter); + strcpy(adapter->name, name); + adapter->algo = &i2c_powermac_algorithm; + i2c_set_adapdata(adapter, bus); + adapter->dev.parent = dev; + pmac_i2c_attach_adapter(bus, adapter); + rc = i2c_add_adapter(adapter); + if (rc) { + printk(KERN_ERR "i2c-powermac: Adapter %s registration " + "failed\n", name); + i2c_set_adapdata(adapter, NULL); + pmac_i2c_detach_adapter(bus, adapter); + } + + printk(KERN_INFO "PowerMac i2c bus %s registered\n", name); + return rc; +} + + +static struct device_driver i2c_powermac_driver = { + .name = "i2c-powermac", + .bus = &platform_bus_type, + .probe = i2c_powermac_probe, + .remove = i2c_powermac_remove, +}; + +static int __init i2c_powermac_init(void) +{ + driver_register(&i2c_powermac_driver); + return 0; +} + + +static void __exit i2c_powermac_cleanup(void) +{ + driver_unregister(&i2c_powermac_driver); +} + +module_init(i2c_powermac_init); +module_exit(i2c_powermac_cleanup); |