/* * linux/drivers/davinci/i2c-davinci-client.c * * Copyright (C) 2006 Texas Instruments Inc * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include static DEFINE_MUTEX(expander_lock); static struct i2c_client *client_handle; /* This function is used for internal initialization */ int davinci_i2c_read(u8 size, u8 * val, u16 client_addr) { int err; struct i2c_client *client = client_handle; struct i2c_msg msg[1]; if (!client->adapter) return -ENODEV; msg->addr = client_addr; msg->flags = I2C_M_RD; msg->len = size; msg->buf = val; err = i2c_transfer(client->adapter, msg, 1); /* printk("[davinci_i2c_read size:%d addr:0x%02x -> %d\n",size,client_addr,err ); */ if (err >= 0) { return 0; } return err; } EXPORT_SYMBOL(davinci_i2c_read); /* This function is used for internal initialization */ int davinci_i2c_write(u8 size, u8 * val, u16 client_addr) { int err; struct i2c_client *client = client_handle; struct i2c_msg msg[1]; if (!client->adapter) return -ENODEV; msg->addr = client_addr; msg->flags = 0; msg->len = size; msg->buf = val; err = i2c_transfer(client->adapter, msg, 1); /* printk("[davinci_i2c_write] size:%d addr:0x%02x -> %d\n",size,client_addr,err ); */ if (err >= 0) return 0; return err; } EXPORT_SYMBOL(davinci_i2c_write); int davinci_i2c_expander_op(u16 client_addr, u35_expander_ops pin, u8 val) { int err = 0; char cmd[4] = { 4, 6, 0x00, 0x09 }; u8 data_to_u35 = 0; if(davinci_revision[1] > 0) { printk("[davinci_i2c_expander_op] no io_expander on hw revision %d.%d.%d.%d.%d\n", davinci_revision[0], davinci_revision[1], davinci_revision[2], davinci_revision[3], davinci_revision[4]); return 0; } if (val > 1) return -1; mutex_lock(&expander_lock); err = davinci_i2c_read(1, &data_to_u35, CONFIG_DAVINCI_I2C_EXPANDER_ADDR); if(err < 0) { printk(KERN_ERR "[davinci_i2c_expander_op] register read failed (status %d)\n", err); return -1; } printk("[davinci_i2c_expander_op] read 0x%x\n", data_to_u35); if (client_addr == CONFIG_DAVINCI_I2C_EXPANDER_ADDR) { unsigned int bit = (unsigned int)-1; unsigned int reverse = 0; switch (pin) { case USB_DRVVBUS: bit = 0; break; case VDDIMX_EN: bit = 1; break; case VLYNQ_ON: bit = 2; break; case EPHY_RESET: bit = 3; reverse = 1; break; case WLAN_RESET: bit = 5; break; case ATA_SEL: bit = 6; break; case CF_SEL: davinci_i2c_write(4, cmd, 0x23); bit = 7; break; default: break; } if(bit != (unsigned int)-1) { if(reverse) val = !val; if(val) data_to_u35 |= 1 << bit; else { data_to_u35 &= ~(1 << bit); } } } else { printk("Only IO Expander at address 0x%x is suuported\n", CONFIG_DAVINCI_I2C_EXPANDER_ADDR); mutex_unlock(&expander_lock); return -EINVAL; } printk("[davinci_i2c_expander_op] write 0x%x\n", data_to_u35); err = davinci_i2c_write(1, &data_to_u35, CONFIG_DAVINCI_I2C_EXPANDER_ADDR); mutex_unlock(&expander_lock); return err; } EXPORT_SYMBOL(davinci_i2c_expander_op); static struct i2c_driver davinci_i2c_client_driver; static int davinci_i2c_attach_client(struct i2c_adapter *adap, int addr) { struct i2c_client *client; int err; u8 data_to_u35 = 0xf6; if (!(client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL))) { err = -ENOMEM; goto exit; } client_handle = client; if (client->adapter) return -EBUSY; /* our client is already attached */ client->addr = addr; client->flags = 0; client->driver = &davinci_i2c_client_driver; client->adapter = adap; strlcpy(client->name, client->driver->driver.name, I2C_NAME_SIZE); err = i2c_attach_client(client); if (err) { client->adapter = NULL; goto exit_kfree; } err = davinci_i2c_write(1, &data_to_u35, CONFIG_DAVINCI_I2C_EXPANDER_ADDR); printk("[davinci_i2c_attach_client] write 0x%x status 0x%x\n", data_to_u35, err); return 0; exit_kfree: kfree(client); exit: return err; } static int davinci_i2c_detach_client(struct i2c_client *client) { int err; if (!client->adapter) return -ENODEV; /* our client isn't attached */ err = i2c_detach_client(client); client->adapter = NULL; return err; } static int davinci_i2c_probe_adapter(struct i2c_adapter *adap) { return davinci_i2c_attach_client(adap, CONFIG_DAVINCI_I2C_EXPANDER_ADDR); } /* This is the driver that will be inserted */ static struct i2c_driver davinci_i2c_client_driver = { .driver = { /* there are 3 expanders, one is leds-only ... */ .name = "davinci_evm_expander1", }, .attach_adapter = davinci_i2c_probe_adapter, .detach_client = davinci_i2c_detach_client, }; static int __init davinci_i2c_client_init(void) { return i2c_add_driver(&davinci_i2c_client_driver); } static void __exit davinci_i2c_client_exit(void) { i2c_del_driver(&davinci_i2c_client_driver); } module_init(davinci_i2c_client_init); module_exit(davinci_i2c_client_exit);