| /******************************************************************************* |
| Marvell GPL License Option |
| |
| If you received this File from Marvell, you may opt to use, redistribute and/or |
| modify this File in accordance with the terms and conditions of the General |
| Public License Version 2, June 1991 (the "GPL License"), a copy of which is |
| available along with the File in the license.txt file or by writing to the Free |
| Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or |
| on the worldwide web at http://www.gnu.org/licenses/gpl.txt. |
| |
| THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED |
| WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY |
| DISCLAIMED. The GPL License provides additional details about this warranty |
| disclaimer. |
| *******************************************************************************/ |
| #include <linux/platform_device.h> |
| #include <linux/miscdevice.h> |
| #include "btns_dev.h" |
| #include "mvCommon.h" |
| #include "mvOs.h" |
| #include "gpp/mvGpp.h" |
| #include "gpp/mvGppRegs.h" |
| #include "btns_driver.h" |
| |
| |
| /* MACROS */ |
| #define GPP_GROUP(gpp) gpp/32 |
| #define GPP_ID(gpp) gpp%32 |
| #define GPP_BIT(gpp) 0x1 << GPP_ID(gpp) |
| |
| /* waiting Q */ |
| wait_queue_head_t btns_waitq; |
| |
| /* |
| * common debug for all |
| */ |
| #undef DEBUG |
| |
| #ifdef DEBUG |
| #define dprintk printk |
| #else |
| #define dprintk(a...) |
| #endif |
| |
| /* At GPP initialization, this strucure is filled with what |
| * operation will be monitored for each button (Push and/or |
| * Release) */ |
| BTN_OP btn_op_cfg[CONFIG_MV_GPP_MAX_PINS] = {BTN_NO_OP}; |
| |
| /* At GPP initialization, this strucure is filled with what |
| * operation will be monitored for each button (Push and/or |
| * Release) */ |
| u32 gpp_default_val_cfg[CONFIG_MV_GPP_MAX_PINS] = {-1}; |
| |
| /* This structures monitors how many time each button was |
| * Push/Released since the last time it was sampled */ |
| BTN btns_status[CONFIG_MV_GPP_MAX_PINS]; |
| |
| u32 is_opend = 0; |
| u32 gpp_changed = 0; |
| u32 gpp_changed_id = -1; |
| |
| |
| /* |
| * Get GPP real value.... |
| * When Gpp is input: |
| * Since reading GPP_DATA_IN_REG return the GPP real value after considering the active polarity |
| * active low = 1, active high = 0 |
| * we will use: val^0 -> val, val^1->not(val) |
| * when output don't consider the active polarity |
| */ |
| static unsigned int |
| mv_gpp_value_real_get(unsigned int gpp_group, unsigned int mask) |
| { |
| unsigned int temp, in_out, gpp_val; |
| /* in ->1, out -> 0 */ |
| in_out = MV_REG_READ(GPP_DATA_OUT_EN_REG(gpp_group)) & mask; |
| |
| gpp_val = MV_REG_READ(GPP_DATA_IN_REG(gpp_group)) & mask; |
| |
| /* outputs values */ |
| temp = (gpp_val & ~in_out); |
| |
| /* input */ |
| temp |= (( gpp_val ^ MV_REG_READ(GPP_DATA_IN_POL_REG(gpp_group)) ) & in_out) & mask; |
| |
| return temp; |
| } |
| |
| |
| static irqreturn_t |
| mv_btns_handler(int irq , void *dev_id) |
| { |
| u32 gpp = (u32) irq - IRQ_GPP_START; |
| u32 gpp_level, gppVal; |
| BTN_OP btn_op; |
| |
| /* get gpp real val */ |
| gppVal = mv_gpp_value_real_get(GPP_GROUP(gpp), GPP_BIT(gpp)); |
| |
| dprintk("Gpp value was changed: "); |
| if (btn_op_cfg[gpp] != BTN_NO_OP) { |
| dprintk("gpp %d has changed. now it's %x \n",gpp, |
| mv_gpp_value_real_get(GPP_GROUP(gpp), GPP_BIT(gpp)) ); |
| |
| /* Count button Pushes/Releases |
| * mv_gpp_value_real_get == gpp_default_val_cfg[gpp] --> Button push, |
| * else --> Button release |
| */ |
| btn_op = (gppVal == gpp_default_val_cfg[gpp]) ? BTN_PUSH : BTN_RELEASE; |
| |
| if(btn_op == BTN_RELEASE) |
| { |
| dprintk("button (of gpp %d) was released \n",gpp); |
| |
| btns_status[gpp].btn_release_cntr++; |
| } else { |
| dprintk("button (of gpp %d) was pressed \n",gpp); |
| |
| btns_status[gpp].btn_push_cntr++; |
| } |
| |
| /* change polarity */ |
| gpp_level = MV_REG_READ(GPP_DATA_IN_POL_REG(GPP_GROUP(gpp))); |
| gpp_level = gpp_level^GPP_BIT(gpp); |
| MV_REG_WRITE(GPP_DATA_IN_POL_REG(GPP_GROUP(gpp)), gpp_level); |
| |
| /* Check if current botton operation should be monitored */ |
| if(btn_op_cfg[gpp] == btn_op || btn_op_cfg[gpp] == BTN_CHANGE) |
| { |
| gpp_changed = 1; |
| gpp_changed_id = gpp; |
| wake_up_interruptible(&btns_waitq); |
| } |
| } |
| |
| return IRQ_HANDLED; |
| } |
| |
| static int |
| btnsdev_ioctl( |
| struct inode *inode, |
| struct file *filp, |
| unsigned int cmd, |
| unsigned long arg) |
| { |
| unsigned int btn_id; |
| BTN btn_sts; |
| BTN_PTR user_btn_sts_ptr; |
| unsigned int error = 0; |
| int i; |
| |
| dprintk("%s()\n", __FUNCTION__); |
| |
| switch (cmd) { |
| case CIOCWAIT_P: |
| /* Haim - Is the condition here correct? */ |
| error = wait_event_interruptible(btns_waitq, gpp_changed); |
| /* Reset Wait Q condition */ |
| gpp_changed = 0; |
| |
| if(error < 0) |
| dprintk("%s(CIOCWAIT_P) - got interrupted\n", __FUNCTION__); |
| |
| /* Set information for user*/ |
| btn_sts.btn_id = gpp_changed_id; |
| btn_sts.btn_push_cntr =btns_status[gpp_changed_id].btn_push_cntr; |
| btn_sts.btn_release_cntr=btns_status[gpp_changed_id].btn_release_cntr; |
| |
| dprintk("Button ID %d was pressed %d and released %d\n",gpp_changed_id, |
| btns_status[gpp_changed_id].btn_push_cntr,btns_status[gpp_changed_id].btn_release_cntr); |
| |
| user_btn_sts_ptr = &(((BTNS_STS_PTR)arg)->btns[0]); |
| if ( copy_to_user((void*)user_btn_sts_ptr, &btn_sts, sizeof(BTN)) ) { |
| dprintk("%s(CIOCWAIT_P) - bad copy\n", __FUNCTION__); |
| error = EFAULT; |
| } |
| |
| /* Reset changed button operations counters*/ |
| btns_status[gpp_changed_id].btn_push_cntr = 0; |
| btns_status[gpp_changed_id].btn_release_cntr = 0; |
| |
| break; |
| case CIOCNOWAIT_P: |
| /* Eventhough we don't monitor for a button status change, we need to |
| reset the indication of a change in case it happend */ |
| gpp_changed = 0; |
| |
| dprintk("There are %d buttons to be checked\n", ((BTNS_STS_PTR)arg)->btns_number); |
| |
| /* Set information for user*/ |
| for (i=0; i<((BTNS_STS_PTR)arg)->btns_number; i++) |
| { |
| btn_id = ((BTNS_STS_PTR)arg)->btns[i].btn_id; |
| |
| /* initialize temp strucure which will be copied to user */ |
| btn_sts.btn_id = btn_id; |
| btn_sts.btn_push_cntr = btns_status[btn_id].btn_push_cntr; |
| btn_sts.btn_release_cntr = btns_status[btn_id].btn_release_cntr; |
| |
| /* Reset button's operations counters*/ |
| btns_status[btn_id].btn_push_cntr = 0; |
| btns_status[btn_id].btn_release_cntr = 0; |
| |
| /* Copy temp structure to user */ |
| user_btn_sts_ptr = &(((BTNS_STS_PTR)arg)->btns[i]); |
| |
| if ( copy_to_user((void*)user_btn_sts_ptr, &btn_sts, sizeof(BTN)) ) { |
| dprintk("%s(CIOCNOWAIT_P) - bad copy\n", __FUNCTION__); |
| error = EFAULT; |
| } |
| } |
| |
| break; |
| default: |
| dprintk("%s(unknown ioctl 0x%x)\n", __FUNCTION__, cmd); |
| error = EINVAL; |
| break; |
| } |
| return(-error); |
| } |
| |
| /* |
| * btn_gpp_init |
| * initialize on button's GPP and registers its IRQ |
| * |
| */ |
| static int |
| btn_gpp_init(unsigned int gpp, unsigned int default_gpp_val, BTN_OP btn_op, char* btn_name) |
| { |
| unsigned int pol; |
| /* Set Polarity bit */ |
| pol = MV_REG_READ(GPP_DATA_IN_POL_REG(GPP_GROUP(gpp))); |
| pol |= GPP_BIT(gpp); |
| MV_REG_WRITE(GPP_DATA_IN_POL_REG(GPP_GROUP(gpp)), pol); |
| |
| /* Set which button operation should be monitored */ |
| btn_op_cfg[gpp] = btn_op; |
| |
| /* Set GPP default value*/ |
| gpp_default_val_cfg[gpp] = default_gpp_val; |
| |
| /* Register IRQ */ |
| if( request_irq( IRQ_GPP_START + gpp, mv_btns_handler, |
| IRQF_DISABLED, btn_name, NULL ) ) |
| { |
| printk( KERN_ERR "btnsdev: can't get irq for button %s (GPP %d)\n",btn_name,gpp ); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| btnsdev_open(struct inode *inode, struct file *filp) |
| { |
| dprintk("%s()\n", __FUNCTION__); |
| |
| if(!is_opend) { |
| is_opend = 1; |
| |
| /* Reset button operations counters*/ |
| memset(&btns_status,0,CONFIG_MV_GPP_MAX_PINS); |
| } |
| |
| return(0); |
| } |
| |
| static int |
| btnsdev_release(struct inode *inode, struct file *filp) |
| { |
| dprintk("%s()\n", __FUNCTION__); |
| return(0); |
| } |
| |
| |
| static struct file_operations btnsdev_fops = { |
| .open = btnsdev_open, |
| .release = btnsdev_release, |
| .ioctl = btnsdev_ioctl, |
| }; |
| |
| static struct miscdevice btnsdev = { |
| .minor = BTNSDEV_MINOR, |
| .name = "btns", |
| .fops = &btnsdev_fops, |
| }; |
| |
| |
| static int |
| btns_probe(struct platform_device *pdev) |
| { |
| struct btns_platform_data *btns_data = pdev->dev.platform_data; |
| int ret, i; |
| |
| dprintk("%s\n", __FUNCTION__); |
| printk(KERN_NOTICE "MV Buttons Driver Load\n"); |
| |
| for (i = 0; i < btns_data->btns_num; i++) { |
| ret = btn_gpp_init(btns_data->btns_data_arr[i].gpp_id, btns_data->btns_data_arr[i].default_gpp_val, |
| btns_data->btns_data_arr[i].btn_op, btns_data->btns_data_arr[i].btn_name); |
| |
| if (ret != 0) { |
| return ret; |
| } |
| } |
| |
| return 0; |
| } |
| |
| |
| static struct platform_driver btns_driver = { |
| .probe = btns_probe, |
| .driver = { |
| .name = MV_BTNS_NAME, |
| }, |
| }; |
| |
| |
| static int __init |
| btnsdev_init(void) |
| { |
| int rc; |
| |
| dprintk("%s\n", __FUNCTION__); |
| |
| /* Initialize Wait Q*/ |
| init_waitqueue_head(&btns_waitq); |
| |
| /* Register btns device */ |
| if (misc_register(&btnsdev)) |
| { |
| printk(KERN_ERR "btnsdev: registration of /dev/btnsdev failed\n"); |
| return -1; |
| } |
| |
| /* Register platform driver*/ |
| rc = platform_driver_register(&btns_driver); |
| if (rc) { |
| printk(KERN_ERR "btnsdev: registration of platform driver failed\n"); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| static void __exit |
| btnsdev_exit(void) |
| { |
| dprintk("%s() should never be called.\n", __FUNCTION__); |
| } |
| |
| module_init(btnsdev_init); |
| module_exit(btnsdev_exit); |
| |
| MODULE_LICENSE("GPL"); |
| MODULE_AUTHOR("Ronen Shitrit & Haim Boot"); |
| MODULE_DESCRIPTION("PH: Buttons press handling."); |
| |
| |