/**************************************************************************** * * Copyright (c) 2004 - 2006 Winbond Electronics Corp. All rights reserved. * ****************************************************************************/ /**************************************************************************** * * FILENAME * w90n745_usi.c * * VERSION * 1.0 * * DESCRIPTION * USI driver supported for W90n710. * * FUNCTIONS * all functions, if they has return value, return 0 if they success, others failed. * * HISTORY * 2006/01/10 Created by QFu * * REMARK * None *************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "w90n745_usi.h" //#define USI_DEBUG //#define USI_DEBUG_ENABLE_ENTER_LEAVE //#define USI_DEBUG_ENABLE_MSG //#define USI_DEBUG_ENABLE_MSG2 #ifdef USI_DEBUG #define PDEBUG(fmt, arg...) printk(fmt, ##arg) #else #define PDEBUG(fmt, arg...) #endif #ifdef USI_DEBUG_ENABLE_ENTER_LEAVE #define ENTER() PDEBUG("[%-20s] : Enter...\n", __FUNCTION__) #define LEAVE() PDEBUG("[%-20s] : Leave...\n", __FUNCTION__) #else #define ENTER() #define LEAVE() #endif #ifdef USI_DEBUG_ENABLE_MSG #define MSG(msg) PDEBUG("[%-20s] : %s\n", __FUNCTION__, msg) #else #define MSG(msg) #endif #ifdef USI_DEBUG_ENABLE_MSG2 #define MSG2(fmt, arg...) PDEBUG("[%-20s] : "fmt, __FUNCTION__, ##arg) #define PRNBUF(buf, count) {int i;MSG2("Data: ");for(i=0;ibit_len) - 1; MSG2("bit_len : %d, mask : %x\n", data_ptr->bit_len, mask); usi_outl(data_ptr->write_data & mask , USI_TX0); /* write data to hardware buffer */ MSG2("-> %x\n", data_ptr->write_data & mask); reg = (global_parameter.sleep << 12) | (global_parameter.lsb << 10) | (data_ptr->bit_len << 3) | (global_parameter.tx_neg << 2) | (global_parameter.rx_neg << 1) | 0x20001; trans_finish = 0; usi_outl(reg, USI_CNTRL); /* start */ wait_event_interruptible(wq, trans_finish != 0); data_ptr->read_data = usi_inl(USI_RX0) & mask; MSG2("<- %x\n", data_ptr->read_data & mask); LEAVE(); return 0; } static int usi_ioctl(struct inode *inode, struct file *flip, unsigned int cmd, unsigned long arg) { int err = 0; struct usi_parameter tmp_parameter; struct usi_data tmp_data; ENTER(); if(_IOC_TYPE(cmd) != USI_IOC_MAGIC) return -ENOTTY; if(_IOC_NR(cmd) > USI_IOC_MAXNR) return -ENOTTY; if(_IOC_DIR(cmd) & _IOC_READ) err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd)); else if(_IOC_DIR(cmd) & _IOC_WRITE) err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)); if(err) return -EFAULT; switch(cmd) { case USI_IOC_GETPARAMETER: if (copy_to_user((void *)arg, &global_parameter, sizeof(struct usi_parameter))) return -EFAULT; break; case USI_IOC_SETPARAMETER: if (copy_from_user(&tmp_parameter, (void *)arg, sizeof(struct usi_parameter))) return -EFAULT; memcpy(&global_parameter, &tmp_parameter, sizeof(struct usi_parameter)); usi_outl(global_parameter.divider, USI_DIVIDER); /* update clock */ break; case USI_IOC_SELECTSLAVE: if (arg < -1 && arg > 1) return -EPERM; if (arg == -1) usi_deselect_slave(); else usi_select_slave(arg); break; case USI_IOC_TRANSIT: if (copy_from_user(&tmp_data, (void *)arg, sizeof(tmp_data))) return -EFAULT; err = usi_transit(&tmp_data); if (err) return err; if (copy_to_user((void *)arg, &tmp_data, sizeof(tmp_data))) return -EFAULT; break; default: return -ENOTTY; } LEAVE(); return 0; } static int usi_open(struct inode *inode, struct file *filp) { u32 reg; int retval = -EBUSY; ENTER(); if (! atomic_dec_and_test (&usi_available)) goto failed; global_parameter.active_level = 0; global_parameter.lsb = 0; global_parameter.rx_neg = 0; global_parameter.tx_neg = 1; global_parameter.divider = 0x1; global_parameter.sleep = 0; slave_select = 0; reg = usi_inl(0xFFF83050) & 0xfff00fff; reg |= 0xaa000; usi_outl(reg, 0xFFF83050); reg = usi_inl(USI_CNTRL); reg |= 0x20000; usi_outl(reg, USI_CNTRL); MSG2("GPIO 5 : %x\n", usi_inl(0xFFF83050)); usi_outl(global_parameter.divider, USI_DIVIDER); /* set clock */ if ((retval = request_irq(INT_SPI, usi_interrupt, SA_INTERRUPT, "usi", NULL)) < 0) { printk("usi : request irq error\n"); goto failed; } LEAVE(); return 0; /* success */ failed: atomic_inc(&usi_available); /* release the device */ return retval; } static int usi_release(struct inode *inode, struct file *flip) { u32 reg; ENTER(); reg = usi_inl(USI_CNTRL); reg &= 0xffff; usi_outl(reg, USI_CNTRL); free_irq(INT_SPI, NULL); usi_deselect_slave(); atomic_inc(&usi_available); /* release the device */ LEAVE(); return 0; } struct file_operations usi_fops = { owner: THIS_MODULE, open: usi_open, release: usi_release, ioctl: usi_ioctl, }; static int __init usi_init(void) { u32 reg; int result; init_waitqueue_head(&wq); /* every things ok, now, we can register char device safely */ result = register_chrdev(USI_MAJOR, "usi", &usi_fops); if( result < 0){ unregister_chrdev(USI_MAJOR, "usi"); printk("usi : can't get major %d\n", USI_MAJOR); goto failed; } printk("USI driver has been installed successfully!\n"); failed: return result; } static void __exit usi_exit(void) { unregister_chrdev(USI_MAJOR, "usi"); } module_init(usi_init); module_exit(usi_exit);