blob: 8f89f04efe1a4f8624f6824a64f21ec6ae53e513 [file] [log] [blame]
/*******************************************************************************
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.");