/*++ drivers/i2c/busses/wmt-i2c-slave-bus.c Copyright (c) 2008 WonderMedia Technologies, 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, see . WonderMedia Technologies, Inc. 10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C. History: 2010/03/11 First Version --*/ #include #define WMT_I2C_SLAVE_C #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "./wmt-i2c-slave-bus.h" /*#define DEBUG*/ #ifdef DEBUG #define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __func__ , ## args) #else #define DPRINTK(fmt, args...) #endif #define DEVICE_NAME "WMT_I2C_SLAVE-0" #define PMC_ClOCK_ENABLE_LOWER 0xd8130250 #define CTRL_GPIO21 0xD8110055 #define PU_EN_GPIO21 0xD8110495 #define PU_CTRL_GPIO21 0xD81104D5 #define DISABLE_I2C_SLAVE BIT15 struct i2c_slave_msg { __u16 addr; /* slave address */ __u16 flags; #define I2C_M_RD 0x01 __u16 len; /* data length */ __u8 *buf; /* pointer to data */ }; struct slave_i2c_dev_s { /* module parameters */ char *buf; /* char dev struct */ struct cdev cdev; struct class *class_slave_i2c; }; struct wmt_slave_i2c_s { struct i2c_regs_s *regs; int irq_no ; enum i2c_mode_e i2c_mode ; int isr_nack ; int isr_byte_end ; int isr_timeout ; int isr_int_pending ; struct compat_semaphore tx_sem; struct compat_semaphore rx_sem; }; static struct i2c_regs_s regs_backup; extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); static unsigned int is_master = 1;/*master:1, slave:0*/ static DEFINE_SPINLOCK(slave_i2c_lock); static int slave_i2c_dev_major = SLAVE_I2C_MAJOR; static int slave_i2c_dev_minor; static int slave_i2c_dev_nr = 1; static struct slave_i2c_dev_s slave_i2c_dev; static struct wmt_slave_i2c_s slave_i2c_port; static unsigned char slave_i2c_addr = 0x31; #ifdef CONFIG_PROC_FS static struct proc_dir_entry *slave_i2c_proc; static int slave_i2c_reg_read(char *buf, char **start, off_t offset, int len) { char *p = buf; p += sprintf(p, "reg : value\n"); p += sprintf(p, "cr : 0x%.4x\n", slave_i2c_port.regs->cr_reg); p += sprintf(p, "tcr : 0x%.4x\n", slave_i2c_port.regs->tcr_reg); p += sprintf(p, "csr : 0x%.4x\n", slave_i2c_port.regs->csr_reg); p += sprintf(p, "isr : 0x%.4x\n", slave_i2c_port.regs->isr_reg); p += sprintf(p, "imr : 0x%.4x\n", slave_i2c_port.regs->imr_reg); p += sprintf(p, "cdr : 0x%.4x\n", slave_i2c_port.regs->cdr_reg); p += sprintf(p, "tr : 0x%.4x\n", slave_i2c_port.regs->tr_reg); p += sprintf(p, "scr : 0x%.4x\n", slave_i2c_port.regs->scr_reg); p += sprintf(p, "cssr : 0x%.4x\n", slave_i2c_port.regs->cssr_reg); p += sprintf(p, "simr : 0x%.4x\n", slave_i2c_port.regs->simr_reg); p += sprintf(p, "sisr : 0x%.4x\n", slave_i2c_port.regs->sisr_reg); p += sprintf(p, "csdr : 0x%.4x\n", slave_i2c_port.regs->csdr_reg); p += sprintf(p, "str : 0x%.4x\n", slave_i2c_port.regs->str_reg); return p - buf; } static int slave_i2c_addr_read(char *buf, char **start, off_t offset, int len) { char *p = buf; p += sprintf(p, "i2c-slave address : 0x%.2x\n", slave_i2c_addr); return p - buf; } #endif static irqreturn_t slave_i2c_isr( int irq, /*!<; // IRQ number */ void *dev, /*!<; // Private paramater(i.e. pointer of spi port) */ struct pt_regs *regs /*!<; // interrupt register */ ) { unsigned short isr_status = slave_i2c_port.regs->sisr_reg; unsigned short ssr_status = slave_i2c_port.regs->cssr_reg; DPRINTK("slave_i2c isr sisr = %x\n", isr_status); DPRINTK("slave_i2c isr cssr = %x\n", ssr_status); if (isr_status & I2C_SISR_DAT_REQ) { slave_i2c_port.regs->sisr_reg |= I2C_SISR_DAT_REQ_WRITE_CLEAR; slave_i2c_port.isr_nack = ssr_status & I2C_SRCV_NACK_MASK; compat_up(&slave_i2c_port.tx_sem); } else if (isr_status & I2C_SISR_BYTE_END) { slave_i2c_port.regs->sisr_reg |= I2C_SISR_BYTE_END_WRITE_CLEAR; slave_i2c_port.isr_byte_end = 1; slave_i2c_port.isr_nack = ssr_status & I2C_SRCV_NACK_MASK; compat_up(&slave_i2c_port.rx_sem); } else if (isr_status & I2C_SISR_SCL_TIME_OUT) { slave_i2c_port.regs->sisr_reg |= I2C_SISR_SCL_TIME_OUT_WRITE_CLEAR; slave_i2c_port.isr_timeout = 1 ; } return IRQ_HANDLED; } static int wmt_slave_i2c_read_data(unsigned short addr , unsigned char *buf, int size, int flags) { int ret = 0; int xfer_len = 0; int sleep_count = 1; DPRINTK("ssr = %x, isr = %x\n", slave_i2c_port.regs->cssr_reg, slave_i2c_port.regs->sisr_reg); while (xfer_len < size) { compat_down_interruptible(&slave_i2c_port.rx_sem); buf[xfer_len] = ((slave_i2c_port.regs->csdr_reg & I2C_SLAVE_READ_DATA_MASK) >> I2C_SLAVE_READ_DATA_SHIFT); DPRINTK("data = %x\n", buf[xfer_len]); xfer_len++; } ret = xfer_len; while (slave_i2c_port.regs->cssr_reg & I2C_SLAVE_ACTIVE) { if (compat_sema_count(&slave_i2c_port.rx_sem) > 0) {/*receive data was longer than request*/ ret = -1; break; } msleep(sleep_count); if (sleep_count < 16) sleep_count *= 2; } DPRINTK("ssr = %x, isr = %x\n", slave_i2c_port.regs->cssr_reg, slave_i2c_port.regs->sisr_reg); return ret; } static int wmt_slave_i2c_write_data(unsigned short addr, unsigned char *buf, int size, int flags) { int ret = 0; int xfer_len = 0; int sleep_count = 1; DPRINTK("tx ssr = %x, isr = %x\n", slave_i2c_port.regs->cssr_reg, slave_i2c_port.regs->sisr_reg); while (xfer_len < size) { compat_down_interruptible(&slave_i2c_port.tx_sem); slave_i2c_port.regs->csdr_reg = buf[xfer_len]; DPRINTK("data = %x\n", buf[xfer_len]); ++xfer_len; } ret = xfer_len; while (slave_i2c_port.regs->cssr_reg & I2C_SLAVE_ACTIVE) { msleep(sleep_count); if (sleep_count < 16) sleep_count *= 2; } DPRINTK("2.tx ssr = %x, isr = %x\n", slave_i2c_port.regs->cssr_reg, slave_i2c_port.regs->sisr_reg); return ret; } static int slave_i2c_do_xfer(unsigned short addr, unsigned char *buf, int size, int flags) { int ret; if (flags & I2C_M_RD) ret = wmt_slave_i2c_read_data(addr , buf, size, flags); else ret = wmt_slave_i2c_write_data(addr , buf, size, flags); return ret; } int wmt_i2cslave_transfer0(struct i2c_slave_msg slave_msg) { int flags = slave_msg.flags; unsigned char *buf = slave_msg.buf; unsigned short addr = slave_msg.addr; int size = slave_msg.len; int xfer_len; if (is_master == 1) return 0; spin_lock(&slave_i2c_lock); xfer_len = slave_i2c_do_xfer(addr, buf, size, flags); spin_unlock(&slave_i2c_lock); return xfer_len; } EXPORT_SYMBOL(wmt_i2cslave_transfer0); void wmt_i2cslave_setaddr0(struct i2c_slave_msg msg) { if (is_master == 1) return; spin_lock(&slave_i2c_lock); slave_i2c_addr = msg.addr; if (msg.addr & DISABLE_I2C_SLAVE) slave_i2c_port.regs->cr_reg &= ~I2C_CR_ENABLE; else { slave_i2c_port.regs->cr_reg = 0; slave_i2c_port.regs->scr_reg = 0; slave_i2c_port.regs->cr_reg = (I2C_SLAV_MODE_SEL|I2C_CR_ENABLE); slave_i2c_port.regs->sisr_reg = I2C_SISR_ALL_WRITE_CLEAR; if (slave_i2c_port.i2c_mode == I2C_STANDARD_MODE) slave_i2c_port.regs->scr_reg = slave_i2c_addr; else if (slave_i2c_port.i2c_mode == I2C_FAST_MODE) slave_i2c_port.regs->scr_reg = slave_i2c_addr; else slave_i2c_port.regs->scr_reg = (slave_i2c_addr|I2C_SLAVE_HS_MODE); slave_i2c_port.regs->simr_reg = I2C_SIMR_ALL_ENABLE; } spin_unlock(&slave_i2c_lock); } EXPORT_SYMBOL(wmt_i2cslave_setaddr0); static ssize_t slave_i2c_read( struct file *filp, char __user *buf, size_t count, loff_t *f_pos ) { int ret = 0; struct i2c_slave_msg slave_msg; if (is_master == 1) return ret; slave_msg.buf = (char *)kmalloc(count * sizeof(unsigned char), GFP_KERNEL); slave_msg.flags = 0; slave_msg.flags |= I2C_M_RD; slave_msg.len = count; slave_msg.addr = slave_i2c_addr; ret = wmt_i2cslave_transfer0(slave_msg); if (copy_to_user(buf, slave_msg.buf, count)) { kfree(slave_msg.buf); return -EFAULT; } return ret; } static ssize_t slave_i2c_write( struct file *filp, const char __user *buf, size_t count, loff_t *f_pos ) { int ret = 0; struct i2c_slave_msg slave_msg; if (is_master == 1) return ret; slave_msg.buf = (char *)kmalloc(count * sizeof(unsigned char), GFP_KERNEL); slave_msg.flags = 0; slave_msg.flags &= ~I2C_M_RD; slave_msg.len = count; slave_msg.addr = slave_i2c_addr; if (copy_from_user(slave_msg.buf, buf, count)) { kfree(slave_msg.buf); return -EFAULT; } ret = wmt_i2cslave_transfer0(slave_msg); return ret; /* return Write out data size*/ } static int slave_i2c_open( struct inode *inode, struct file *filp ) { struct slave_i2c_dev_s *dev; char name[40]; int minor_no; dev = container_of(inode->i_cdev, struct slave_i2c_dev_s, cdev); filp->private_data = dev; minor_no = iminor(inode); /* get */ /* Create user name*/ memset(name, 0x0, 8); sprintf(name, "slave-i2c%d", minor_no); return 0; } static int slave_i2c_release( struct inode *inode, struct file *filp ) { struct slave_i2c_dev_s *dev; int minor_no; dev = container_of(inode->i_cdev, struct slave_i2c_dev_s, cdev); minor_no = iminor(inode); return 0; } static int slave_i2c_ioctl( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) { int ret = 0; struct i2c_slave_msg slave_msg[1]; unsigned char *data_ptr; unsigned char __user *usr_ptr; switch (cmd) { case IOCTL_DO_TRANSFER: if (copy_from_user(slave_msg, (struct i2c_slave_msg *)arg, sizeof(struct i2c_slave_msg))) return -EFAULT; data_ptr = (unsigned char *)kmalloc(slave_msg->len*sizeof(unsigned char), GFP_KERNEL); usr_ptr = (unsigned char __user *)slave_msg->buf; if (copy_from_user(data_ptr, (unsigned char *)slave_msg->buf, slave_msg->len*sizeof(unsigned char))) { kfree(data_ptr); return -EFAULT; } slave_msg->buf = data_ptr; ret = wmt_i2cslave_transfer0((struct i2c_slave_msg)*slave_msg); if (slave_msg->flags & I2C_M_RD) { if (copy_to_user( usr_ptr, data_ptr, slave_msg->len)) ret = -EFAULT; } kfree(data_ptr); break; case IOCTL_SET_ADDR: if (copy_from_user(slave_msg, (struct i2c_slave_msg *)arg, sizeof(struct i2c_slave_msg))) return -EFAULT; wmt_i2cslave_setaddr0((struct i2c_slave_msg) *slave_msg); break; case IOCTL_QUERY_DATA: break; case IOCTL_SET_SPEED_MODE: break; default: break; } return ret; } /*!************************************************************************* driver file operations struct define ****************************************************************************/ static struct file_operations i2c_slave_fops = { .owner = THIS_MODULE, .open = slave_i2c_open, .read = slave_i2c_read, .write = slave_i2c_write, .ioctl = slave_i2c_ioctl, .release = slave_i2c_release, }; static int slave_i2c_probe( struct device *dev ) { int ret = 0; dev_t dev_no; struct cdev *cdev; char name[40]; char buf[80]; unsigned int port_num; int idx = 0; char varname1[] = "wmt.bus.i2c.slave_port"; int ret_val = 0; int varlen = 80; memset(name, 0, 40); dev_no = MKDEV(slave_i2c_dev_major, slave_i2c_dev_minor); sprintf(name, "wmt_i2cslave%d",slave_i2c_dev_minor); cdev = &slave_i2c_dev.cdev; cdev_init(cdev, &i2c_slave_fops); ret = cdev_add(cdev, dev_no, 8); slave_i2c_dev.class_slave_i2c = class_create(THIS_MODULE, "wmt_i2cslave"); device_create(slave_i2c_dev.class_slave_i2c, NULL , MKDEV(slave_i2c_dev_major,slave_i2c_dev_minor), NULL, name); if (ret) { printk(KERN_ALERT "*E* register char dev \n"); return ret; } #ifdef CONFIG_PROC_FS struct proc_dir_entry *res; slave_i2c_proc = proc_mkdir("driver/wmt_i2cslave0", NULL); /* slave_i2c_proc->owner = THIS_MODULE; */ res = create_proc_entry("registers", 0, slave_i2c_proc); if (res) { res->read_proc = slave_i2c_reg_read; } res = create_proc_entry("address", 0, slave_i2c_proc); if (res) { res->read_proc = slave_i2c_addr_read; } #endif slave_i2c_port.regs = (struct i2c_regs_s *)I2C0_BASE_ADDR; slave_i2c_port.irq_no = IRQ_I2C0; slave_i2c_port.i2c_mode = I2C_STANDARD_MODE; slave_i2c_port.isr_nack = 0; slave_i2c_port.isr_byte_end = 0; slave_i2c_port.isr_timeout = 0; slave_i2c_port.isr_int_pending = 0; compat_sema_init(&slave_i2c_port.tx_sem, 0); compat_sema_init(&slave_i2c_port.rx_sem, 0); ret_val = wmt_getsyspara(varname1, buf, &varlen); is_master = 1; if (ret_val == 0) { ret_val = sscanf(buf, "%x", &port_num); while (ret_val) { if (port_num != 0) is_master = 1; else { is_master = 0; break; } idx += ret_val; ret_val = sscanf(buf + idx, ",%x", &port_num); } } else is_master = 1; /**/ /* hardware initial*/ /**/ if (is_master == 0) { *(volatile unsigned int *)PMC_ClOCK_ENABLE_LOWER |= (BIT5); *(volatile unsigned int *)CTRL_GPIO21 &= ~(BIT0 | BIT1); *(volatile unsigned int *)PU_EN_GPIO21 |= (BIT0 | BIT1); *(volatile unsigned int *)PU_CTRL_GPIO21 |= (BIT0 | BIT1); /*set i2c slave register*/ slave_i2c_port.regs->cr_reg = 0; slave_i2c_port.regs->scr_reg = 0; slave_i2c_port.regs->cr_reg = (I2C_SLAV_MODE_SEL|I2C_CR_ENABLE); slave_i2c_port.regs->sisr_reg = I2C_SISR_ALL_WRITE_CLEAR; if (slave_i2c_port.i2c_mode == I2C_STANDARD_MODE) slave_i2c_port.regs->scr_reg = slave_i2c_addr; else if (slave_i2c_port.i2c_mode == I2C_FAST_MODE) slave_i2c_port.regs->scr_reg = slave_i2c_addr; else slave_i2c_port.regs->scr_reg = (slave_i2c_addr|I2C_SLAVE_HS_MODE); slave_i2c_port.regs->simr_reg = I2C_SIMR_ALL_ENABLE; slave_i2c_port.regs->cr_reg &= ~I2C_CR_ENABLE; if (request_irq(slave_i2c_port.irq_no , &slave_i2c_isr, IRQF_DISABLED, "i2c-slave", 0) < 0) { DPRINTK(KERN_INFO "I2C-SLAVE: Failed to register I2C-SLAVE irq %i\n", slave_i2c_port.irq_no); return -ENODEV; } } return ret; } static int slave_i2c_remove( struct device *dev /*!<; // please add parameters description her*/ ) { struct cdev *cdev; cdev = &slave_i2c_dev.cdev; cdev_del(cdev); return 0; } /* End of spi_remove() */ static void slave_i2c_backup(void) { regs_backup.cr_reg = slave_i2c_port.regs->cr_reg; regs_backup.tcr_reg = slave_i2c_port.regs->tcr_reg; regs_backup.scr_reg = slave_i2c_port.regs->scr_reg; regs_backup.simr_reg = slave_i2c_port.regs->simr_reg; regs_backup.str_reg = slave_i2c_port.regs->str_reg; } static void slave_i2c_restore(void) { slave_i2c_port.regs->cr_reg = 0; slave_i2c_port.regs->cr_reg = regs_backup.cr_reg; slave_i2c_port.regs->tcr_reg = regs_backup.tcr_reg; slave_i2c_port.regs->scr_reg = regs_backup.scr_reg; slave_i2c_port.regs->simr_reg = regs_backup.simr_reg; slave_i2c_port.regs->str_reg = regs_backup.str_reg; } static int slave_i2c_suspend( struct device *dev, /*!<; // please add parameters description her*/ pm_message_t state /*!<; // please add parameters description her*/ ) { if (is_master == 1) return 0; slave_i2c_backup(); compat_sema_init(&slave_i2c_port.tx_sem, 0); compat_sema_init(&slave_i2c_port.rx_sem, 0); return 0; } static int slave_i2c_resume( struct device *dev ) { if (is_master == 1) return 0; *(volatile unsigned int *)PMC_ClOCK_ENABLE_LOWER |= (BIT5); *(volatile unsigned int *)CTRL_GPIO21 &= ~(BIT0 | BIT1); *(volatile unsigned int *)PU_EN_GPIO21 |= (BIT0 | BIT1); *(volatile unsigned int *)PU_CTRL_GPIO21 |= (BIT0 | BIT1); slave_i2c_restore(); return 0; } /*!************************************************************************* device driver struct define ****************************************************************************/ static struct device_driver slave_i2c_driver = { .name = "wmt_i2c_slave_0", /* This name should equal to platform device name.*/ .bus = &platform_bus_type, .probe = slave_i2c_probe, .remove = slave_i2c_remove, .suspend = slave_i2c_suspend, .resume = slave_i2c_resume }; static void slave_i2c_platform_release( struct device *device ) { } /*!************************************************************************* platform device struct define ****************************************************************************/ static struct platform_device slave_i2c_device = { .name = "wmt_i2c_slave_0", .id = 0, .dev = { .release = slave_i2c_platform_release, }, .num_resources = 0, .resource = NULL, }; static int slave_i2c_init(void) { int ret; dev_t dev_no; char dev_name[40]; memset(dev_name, 0, 40); sprintf(dev_name, "wmt_i2c_slave%d", slave_i2c_device.id); if (slave_i2c_dev_major) { dev_no = MKDEV(slave_i2c_dev_major, slave_i2c_dev_minor); ret = register_chrdev_region(dev_no, slave_i2c_dev_nr, dev_name); } else { ret = alloc_chrdev_region(&dev_no, slave_i2c_dev_minor, slave_i2c_dev_nr, dev_name); slave_i2c_dev_major = MAJOR(dev_no); } if (ret < 0) { printk(KERN_ALERT "*E* can't get major %d\n", slave_i2c_dev_major); return ret; } platform_device_register(&slave_i2c_device); ret = driver_register(&slave_i2c_driver); return ret; } module_init(slave_i2c_init); static void slave_i2c_exit(void) { dev_t dev_no; driver_unregister(&slave_i2c_driver); platform_device_unregister(&slave_i2c_device); dev_no = MKDEV(slave_i2c_dev_major, slave_i2c_dev_minor); unregister_chrdev_region(dev_no, slave_i2c_dev_nr); return; } module_exit(slave_i2c_exit); MODULE_AUTHOR("WMT SW Team"); MODULE_DESCRIPTION("WMT_slave_i2c device driver"); MODULE_LICENSE("GPL"); #undef WMT_I2C_SLAVE_C