/* i2c-keywest.c - Part of lm_sensors, Linux kernel modules for hardware monitoring i2c Support for Apple Keywest I2C Bus Controller Copyright (c) 2000 Philip Edelbrock 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 #include #include #include #include #include #include #include #include #include #include #include // #include // #include // #include #include #include // #include #define POLL_SANITY 100 /* PCI device */ #define VENDOR 0x106b #define DEVICE 0x22 /***** 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); /***** End of Protos ******/ /** Vars **/ struct keywest_iface *ifaces=NULL; /** Functions **/ int poll_interrupt(void *ISR) { int i,res; for (i=0; i < POLL_SANITY; i++) { udelay(100); if (res=readb(ISR) & 0x0F > 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 */ } void keywest_reset(struct keywest_iface *ifaceptr) { int i; int interrupt_state=1; /* Let's see if we can clear some busy conditions */ // writeb(0xFF,ifaceptr->status); writeb(0x04,ifaceptr->control); writeb(0xFF,ifaceptr->ISR); // printk("Initalizing. Ignore: "); for (i=0;(i<10) & (interrupt_state > 0);i++) { interrupt_state=poll_interrupt(ifaceptr->ISR); 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; // printk("Keywest access called with ref to iface: 0x%X\n",ifaceptr->base); // printk("Access request: addr:%X flags:%X rw:%X command:%X size:%X\n", // addr, flags, read_write, command, size); keywest_reset(ifaceptr); printk("start of transaction\n"); dump_ifaces(&ifaces); /* see if bus is busy */ if ((readb(ifaceptr->status) & 1) > 0) { printk("i2c-keywest: Busy!\n"); dump_ifaces(&ifaces); return -1; } /* Set up address and r/w bit */ writeb(((addr << 1) | (read_write == I2C_SMBUS_READ)),(void *)ifaceptr->addr); /* Set up 'sub address' which I'm guessing is the command field? */ writeb(command,(void *)ifaceptr->subaddr); /* Start sending address */ writeb(readb(ifaceptr->control) | 2,ifaceptr->control); interrupt_state=poll_interrupt(ifaceptr->ISR); ack=readb(ifaceptr->status) & 0x0F; // printk("status:0x%02X ack:0x%02X\n",ack,interrupt_state); 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; } else { if (size & (I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA)) { if (read_write == I2C_SMBUS_READ) writeb(1 | readb(ifaceptr->control),ifaceptr->control); /* Set ACK if reading */ switch (size) { case I2C_SMBUS_BYTE_DATA: if (read_write == I2C_SMBUS_WRITE) { writeb(data->byte,ifaceptr->data); writeb(interrupt_state,ifaceptr->ISR); /* Clear interrupt and go */ interrupt_state=poll_interrupt(ifaceptr->ISR); if ((readb(ifaceptr->status) & 0x02) == 0) { printk("i2c-keywest: Ack Expected by not received(2)!\n"); error_state=-1; } writeb(readb(ifaceptr->control) | 4,ifaceptr->control); /* Send stop */ } else { writeb(interrupt_state,ifaceptr->ISR); /* Clear interrupt and go */ interrupt_state=poll_interrupt(ifaceptr->ISR); data->byte=readb(ifaceptr->data); writeb(0,ifaceptr->control); /* End read: clear ack */ } break; case I2C_SMBUS_WORD_DATA: if (read_write == I2C_SMBUS_WRITE) { writeb(data->word & 0x0ff,ifaceptr->data); writeb(interrupt_state,ifaceptr->ISR); /* Clear interrupt and go */ interrupt_state=poll_interrupt(ifaceptr->ISR); if ((readb(ifaceptr->status) & 0x02) == 0) { printk("i2c-keywest: Ack Expected by not received(2)!\n"); error_state=-1; } writeb((data->word & 0x0ff00) >> 8,ifaceptr->data); writeb(interrupt_state,ifaceptr->ISR); /* Clear interrupt and go */ interrupt_state=poll_interrupt(ifaceptr->ISR); if ((readb(ifaceptr->status) & 0x02) == 0) { printk("i2c-keywest: Ack Expected by not received(3)!\n"); error_state=-1; } writeb(readb(ifaceptr->control) | 4,ifaceptr->control); /* Send stop */ } else { writeb(interrupt_state,ifaceptr->ISR); /* Clear interrupt and go */ interrupt_state=poll_interrupt(ifaceptr->ISR); data->word=(readb(ifaceptr->data) << 8); writeb(1,ifaceptr->control); /* Send ack */ writeb(interrupt_state,ifaceptr->ISR); /* Clear interrupt and go */ interrupt_state=poll_interrupt(ifaceptr->ISR); data->word|=(readb(ifaceptr->data)); writeb(0,ifaceptr->control); /* End read: clear ack */ } 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(data->byte,ifaceptr->data); writeb(interrupt_state,ifaceptr->ISR); /* Clear interrupt and go */ 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; } } writeb(readb(ifaceptr->control) | 4,ifaceptr->control); /* Send stop */ } else { for (i = 1; i <= data->block[0]; i++) { writeb(1,ifaceptr->control); /* Send ack */ writeb(interrupt_state,ifaceptr->ISR); /* Clear interrupt and go */ interrupt_state=poll_interrupt(ifaceptr->ISR); data->block[i] =readb(ifaceptr->data); } } break; } /* switch */ } /* if size */ } /* if ack */ writeb(0,ifaceptr->control); /* End read: clear ack */ writeb(readb(ifaceptr->control) | 4,ifaceptr->control); /* Send stop */ writeb(interrupt_state,ifaceptr->ISR); /* Clear interrupt and go */ if (((interrupt_state=poll_interrupt(ifaceptr->ISR)) & 0x04) > 0) printk("i2c-keywest: Expected stop interrupt but got: 0x%02X\n",interrupt_state); error_state=-1; writeb(interrupt_state,ifaceptr->ISR); /* Clear interrupt*/ printk("Transaction complete.\n"); dump_ifaces(&ifaces); return error_state; } 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) { MOD_INC_USE_COUNT; } void keywest_dec(struct i2c_adapter *adapter) { MOD_DEC_USE_COUNT; } /**** Internal Functions ****/ int init_iface (void *base,void *steps,struct keywest_iface **ifaces) { struct i2c_adapter *keywest_adapter; char *name; struct keywest_iface **temp_hdl=ifaces; int res,temp; if (ifaces == NULL) { printk("Ah! Passed a null handle to init_iface"); return -1; } while ((*temp_hdl) != NULL) { printk("found an entry, skipping.\n"); temp_hdl=&(*temp_hdl)->next; } *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); memset((void *)keywest_adapter,0,(sizeof(struct i2c_adapter))); if (keywest_adapter == NULL) { printk("kmalloc failed on keywest_adapter!"); return -1; } 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)) { printk ("i2c-keywest.o: Adapter registration failed, module not inserted.\n"); cleanup(); } /* Now actually do the initialization of the device */ writeb(0x08,(*temp_hdl)->mode); /* So-called 'standard sub-mode' */ writeb(0,(*temp_hdl)->status); writeb(0x0FF,(*temp_hdl)->ISR); printk("Done creating iface entry\n"); keywest_reset(*temp_hdl); // Test access to SDA and SCK printk("Serial settings: control:%02X status:%02X\n",readb((*temp_hdl)->control),readb((*temp_hdl)->status)); writeb( 0x10,(*temp_hdl)->control); temp=readb((*temp_hdl)->control); printk("Serial settings: %02X\n",temp); udelay(10); keywest_reset(*temp_hdl); writeb(0x10,(*temp_hdl)->control); temp=readb((*temp_hdl)->control); printk("Serial settings: control:%02X status:%02X\n",readb((*temp_hdl)->control),readb((*temp_hdl)->status)); udelay(10); keywest_reset(*temp_hdl); writeb(0x08,(*temp_hdl)->control); temp=readb((*temp_hdl)->control); printk("Serial settings: control:%02X status:%02X\n",readb((*temp_hdl)->control),readb((*temp_hdl)->status)); udelay(10); keywest_reset(*temp_hdl); writeb(0x08,(*temp_hdl)->control); temp=readb((*temp_hdl)->control); printk("Serial settings: control:%02X status:%02X\n",readb((*temp_hdl)->control),readb((*temp_hdl)->status)); 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",(*ifaces)->base,(*ifaces)->steps); printk(" mode:%X control:%X status:%X ISR:%X IER:%X addr:%X subaddr:%X data:%X\n", (*ifaces)->mode,(*ifaces)->control,(*ifaces)->status,(*ifaces)->ISR, (*ifaces)->IER,(*ifaces)->addr,(*ifaces)->subaddr,(*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); } else { printk("End of ifaces.\n"); } } int cleanup (struct keywest_iface **ifaces) { int res=0; if (ifaces == NULL) { printk("Ah! Passed null handle to cleanup!\n"); return; } if (*ifaces != NULL) { printk("Cleaning up interface @%X,%X\n",(*ifaces)->base,(*ifaces)->steps); cleanup(&(*ifaces)->next); if (res = i2c_del_adapter((*ifaces)->i2c_adapt)) { printk ("i2c-keywest.o: i2c_del_adapter failed, module not removed\n"); } kfree(*ifaces); iounmap((*ifaces)->base); (*ifaces)=NULL; } return res; } 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); } 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; } } static int find_keywest(void) { struct pci_dev *s_bridge; u8 rev; struct device_node *i2c_device; struct device_node *np; struct property *dp; void **temp; int i; void *base=NULL; void *steps=NULL; #ifdef DEBUG scan_of("i2c"); #endif // scan_of("backlight"); // scan_of("sound"); // scan_of("ethernet"); scan_of("i2c"); i2c_device = find_compatible_devices("i2c","keywest"); 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");} // printk("address= [%X]\n",base); temp=(void **)get_property(i2c_device, "AAPL,address-step", NULL); if (temp != NULL) {steps=*temp;} else {printk("no 'address-step' prop!\n");} printk("Device found. base: %X steps: %X\n",base,steps); if (init_iface(base,steps,&ifaces) !=0 ) { cleanup(&ifaces); return -ENODEV; } i2c_device=i2c_device->next; } if (ifaces == NULL) { printk("ifaces null! :'("); } dump_ifaces(&ifaces); return 0; } #ifdef MODULE static #else extern #endif int __init i2c_keywest_init(void) { return find_keywest(); } EXPORT_NO_SYMBOLS; #ifdef MODULE MODULE_AUTHOR("Philip Edelbrock