/*
 * Copyright 2004 Freescale Semiconductor, Inc. All Rights Reserved.
 *
 * 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
 */

/*!
 * @file mxc_keyb.c
 *
 * @brief Driver for the Freescale Semiconductor MXC keypad port.  
 * 
 * The keypad driver is designed as a character driver which interacts with 
 * low level keypad port hardware. Upon opening, the Keypad driver initializes 
 * the keypad port. When the keypad interrupt happens the driver scans the 
 * keypad matrix for key press/release.  The scancode of the key press/release 
 * events are internally queued. When an application does a read on the keypad
 * driver, the scan codes from the keypad queue are copied to user. The user
 * can configure the keypad driver in raw mode or map mode. The user can also
 * configure the keypad matrix for upto 8 rows and 8 columns. 
 *
 * @ingroup keypad 
 */

#include <linux/config.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/arch/hardware.h>
#include <asm/arch/board.h>
#include <linux/kd.h>
#include <linux/fs.h>
#include <linux/kbd_kern.h>
#include <linux/ioctl.h>
#include <linux/poll.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/input.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <linux/delay.h>

/*
 * Module header file
 */
#include "mxc_keyb.h"
#include <asm/arch/mxc_keyb_ioctl.h>

/*!
 * This structure holds the keypad Queue for storing the scancode for key
 * press/release.
 */ 
static struct keypad_priv kpp_dev;

/*! Input device structure. */
static struct input_dev mxckbd_dev;

/*!
 * This variable holds the keypad module name.
 */
static char *mxckbd_name = "MXC keypad";

/*! This static variable indicates whether a key event is pressed/released. */
static unsigned short KPress;

/*! This static variable indicates Key mapcode generation. */
static unsigned Kmapevent = 0;

/*! cur_rcmap and prev_rcmap array is used to detect key press and release. */
static unsigned short cur_rcmap[MAXROW]; /* max 64 bits (8x8 matrix) */
static unsigned short prev_rcmap[MAXROW];

/*!
 * Debounce polling period(10ms) in system ticks.
 */
static unsigned short KScanRate = (10 * HZ) / 1000;

/*!
 *  Mapcode array contains the scan code to map code mapping. This is used
 *  when the keypad mode is XLATE. 
 */
static unsigned short key_map[] = {
        0, 1, 2, 3, 4, 5, 6, 7,
        8, 9, 10, 11, 12, 13, 14, 15,
        16, 17, 18, 19, 20, 21, 22, 23,
        24, 25, 26, 27, 28, 29, 30, 31,
        32, 33, 34, 35, 36, 37, 38, 39,
        40, 41, 42, 43, 44, 45, 46, 47,
        48, 49, 50, 51, 52, 53, 54, 55,
        56, 57, 58, 59, 60, 61, 62, 63,
        64, 65, 66, 67, 68, 69, 70, 71,
        72, 73, 74, 75, 76, 77, 78, 79,
        80, 81, 82, 83, 84, 85, 86, 87,
        88, 89, 90, 91, 92, 93, 94, 95, 
        96, 97, 98, 99, 100, 101, 102,
        103, 104, 105, 106, 107, 108, 
        109, 110, 111, 112, 113, 114,
        115, 116, 117, 118, 119, 120,
        121, 122, 123, 124, 125, 126,
        127, 128, 
};

/*
 * These functions are used to configure and the GPIO pins for keypad to
 * activate and deactivate it.
 */
extern void gpio_keypad_active(void);
extern void gpio_keypad_inactive(void);

/*!
 * This function initializes the keypad queue. This is called by keypad driver
 * open function.
 */
static inline void mxc_kpp_initq(void)
{
        kpp_dev.queue.head = kpp_dev.queue.tail = 0;
        kpp_dev.queue.len = 0;
        kpp_dev.queue.s_lock = SPIN_LOCK_UNLOCKED;
        init_waitqueue_head(&kpp_dev.queue.waitq);
}

/*!
 * This function fetches the next character available in keypad queue.
 * This is called by the keypad driver read function.
 *
 * @return  scancode from queue if a character is available, else it
 * returns 0.
 */
static inline unsigned char mxc_kpp_pullq(void)
{
        unsigned short result;
        unsigned long flags;

        spin_lock_irqsave(&kpp_dev.queue.s_lock, flags);
        if (!kpp_dev.queue.len) {
                spin_unlock_irqrestore(&kpp_dev.queue.s_lock, flags);
                return 0;
        }
        result = kpp_dev.queue.buf[kpp_dev.queue.head];
#ifdef KPP_DEBUG
        printk("KPP: data from queue %d\n", result);
        printk("Value of queue.head ... %d\n", kpp_dev.queue.head);
        printk("value of queue.tail ... %d\n", kpp_dev.queue.tail);
        printk("value of queue.len ... %d\n", kpp_dev.queue.len);
#endif
        kpp_dev.queue.head++;
        kpp_dev.queue.head &= (KPP_BUF_SIZE - 1);
        kpp_dev.queue.len--;
        spin_unlock_irqrestore(&kpp_dev.queue.s_lock, flags);
        return result;
}

/*!
 * This function puts the input scancode into the keypad queue. This is called
 * by the keypad driver when it recognizes a new key press/release
 * event on the keypad. 
 *
 * @param   scancode      character to be queued
 */   
static inline void mxc_kpp_pushq(unsigned short scancode)
{
        unsigned long flags;

        spin_lock_irqsave(&kpp_dev.queue.s_lock, flags);

        if (kpp_dev.queue.len < KPP_BUF_SIZE) {
                kpp_dev.queue.buf[kpp_dev.queue.tail] = scancode;
                kpp_dev.queue.tail++;
                kpp_dev.queue.tail &= (KPP_BUF_SIZE - 1);
                kpp_dev.queue.len++;
                wake_up_interruptible(&kpp_dev.queue.waitq);
#ifdef KPP_DEBUG
                printk("KPP: pressed key added %u\n", 
                        kpp_dev.queue.buf[kpp_dev.queue.tail]);
                printk("Value of queue.head  %d\n", kpp_dev.queue.head);
                printk("value of queue.tail  %d\n", kpp_dev.queue.tail);
                printk("value of queue.len  %d\n", kpp_dev.queue.len);
#endif
        }
        spin_unlock_irqrestore(&kpp_dev.queue.s_lock, flags);
}

/*!
 * This function checks if the keypad queue is empty. 
 *
 * @return      0 if queue is empty, non-zero if queue is not empty. 
 */   
static inline int mxc_kpp_q_empty(void)
{
        int result;
        unsigned long flags;

        spin_lock_irqsave(&kpp_dev.queue.s_lock, flags);
        result = (kpp_dev.queue.len == 0);
#ifdef KPP_DEBUG
        printk("value of queue.len  %d\n", kpp_dev.queue.len);
#endif
        spin_unlock_irqrestore(&kpp_dev.queue.s_lock, flags);
        return result;
}

/*!
 * This function takes scancode as input, checks if the keypad mode is RAW or
 * XLATE and pushes scancode or mapcode into the keypad queue accordingly.
 * This is called by the keypad driver when a key press/release event is
 * recognized on the keypad.
 *
 * @param   scancode        scancode of the key pressed/released
 */
void mxc_kpp_handle_mode(unsigned short scancode)
{
        unsigned short mapcode;

        /*
         * kpp_mode variable is checked for mode of keypad.  The keypad mode
         * can be set by the user using ioctl call.  If the keypad mode is in
         * RAW mode, then the raw scancode generated is pushed into keypad
         * queue.  Else if the keypad mode is in map mode, then the mapcode
         * for the scancode is generated and pushed into keypad queue.
         */
        if (kpp_dev.kpp_mode == KEYPAD_RAW) {
                mxc_kpp_pushq(scancode);
                input_report_key(&mxckbd_dev, scancode, 1);
                input_report_key(&mxckbd_dev, scancode, 0);
                input_sync(&mxckbd_dev);
#ifdef KPP_DEBUG
                printk("KPP: scancode %d\n", scancode);
#endif
        } else if (kpp_dev.kpp_mode == KEYPAD_XLATE) {
                if (Kmapevent == 1) {
                        mapcode = key_map[scancode];
                } else {
                        scancode = scancode - 64;
                        mapcode = key_map[scancode];
                }
#ifdef KPP_DEBUG
                printk("KPP: mapcode %d\n", mapcode);
#endif
                mxc_kpp_pushq(mapcode);
                input_report_key(&mxckbd_dev, mapcode, 1);
                input_report_key(&mxckbd_dev, mapcode, 0);
                input_sync(&mxckbd_dev);
        }
}

/*!
 * This function is called to scan the keypad matrix to find out the key press
 * and key release events. Make scancode and break scancode are generated for
 * key press and key release events. 
 *
 * The following scanning sequence are done for 
 * keypad row and column scanning,
 * -# Write 1's to KPDR[15:8], setting column data to 1's 
 * -# Configure columns as totem pole outputs(for quick discharging of keypad
 * capacitance)
 * -# Configure columns as open-drain
 * -# Write a single column to 0, others to 1.  
 * -# Sample row inputs and save data. Multiple key presses can be detected on
 * a single column.
 * -# Repeat steps the above steps for remaining columns.
 * -# Return all columns to 0 in preparation for standby mode.
 * -# Clear KPKD and KPKR status bit(s) by writing to a 1,
 *    Set the KPKR synchronizer chain by writing "1" to KRSS register,
 *    Clear the KPKD synchronizer chain by writing "1" to KDSC register
 *    
 * @result    Number of key pressed/released.
 */ 
static int mxc_kpp_scan_matrix(void)
{
        unsigned short reg_val;
#ifdef KPP_DEBUG
        unsigned short tmp_sts;
#endif
        int col, row;
        static unsigned short scancode = 0;
        int keycnt = 0;     /* How many keys are still pressed */

        /* 
         * wmb() linux kernel function which guarantees orderings in write
         * operations 
         */
        wmb();

        /* save cur keypad matrix to prev */
        memcpy(prev_rcmap, cur_rcmap, sizeof(prev_rcmap));
        memset(cur_rcmap, 0, sizeof(cur_rcmap));
        
        for (col = 0; col < kpp_dev.kpp_cols; col++) {        /* Col */
                /* 2. Write 1.s to KPDR[15:8] setting column data to 1.s */
                reg_val = readw(KPDR);
                reg_val |= 0xff00;
                writew(reg_val, KPDR);

                /*
                 * 3. Configure columns as totem pole outputs(for quick 
                 * discharging of keypad capacitance)
                 * 4. Configure columns as open-drain
                 */
                reg_val = readw(KPCR);
                reg_val &= 0x00ff;
                writew(reg_val, KPCR);
                reg_val |= 0xff00;
                writew(reg_val, KPCR);

                /*
                 * 5. Write a single column to 0, others to 1.
                 * 6. Sample row inputs and save data. Multiple key presses
                 * can be detected on a single column.
                 * 7. Repeat steps 2 - 6 for remaining columns.
                 */

                /* Col bit starts at 8th bit in KPDR */
                reg_val = readw(KPDR);
                reg_val &= ~(1 << (8 + col));
                writew(reg_val, KPDR);

                /* Delay added to aviod propagating the 0 from column to row
                 * when scanning. */

                udelay(2);

                /* Read row input */
                reg_val = readw(KPDR);
#ifdef KPP_DEBUG
                printk("KPP: data register 0x%x\n", reg_val);
                tmp_sts = readw(KPSR);
                printk("KPP: status register 0x%x\n", tmp_sts);
#endif
                for (row = 0; row < kpp_dev.kpp_rows; row++) { /* sample row */
                        if (TEST_BIT(reg_val, row) == 0) {
                                cur_rcmap[row] = BITSET(cur_rcmap[row], col);
                                keycnt++;
#ifdef KPP_DEBUG
                                printk("KPP: key pressed (row %d, col %d)\n", 
                                        row, col);
#endif
                        } 
                }
        }

#ifdef KPP_DEBUG
        tmp_sts = readw(KPDR);
        printk("KPP: data reg value 0x%x\n", tmp_sts);
        tmp_sts = readw(KPSR);
        printk("KPP: status regs value %x\n", tmp_sts);
#endif

        /*
         * 8. Return all columns to 0 in preparation for standby mode.
         * 9. Clear KPKD and KPKR status bit(s) by writing to a .1.,
         * set the KPKR synchronizer chain by writing "1" to KRSS register,
         * clear the KPKD synchronizer chain by writing "1" to KDSC register
         */
        reg_val = 0x00; 
        writew(reg_val, KPDR);
        reg_val = readw(KPDR);
        reg_val = readw(KPSR);
        reg_val |= KBD_STAT_KPKD | KBD_STAT_KPKR | KBD_STAT_KRSS | 
                   KBD_STAT_KDSC;
        writew(reg_val, KPSR);

        /* Check key press status change */

        /*
         * prev_rcmap array will contain the previous status of the keypad
         * matrix.  cur_rcmap array will contains the present status of the
         * keypad matrix. If a bit is set in the array, that (row, col) bit is
         * pressed, else it is not pressed. 
         *
         * XORing these two variables will give us the change in bit for
         * particular row and column.  If a bit is set in XOR output, then that
         * (row, col) has a change of status from the previous state.  From
         * the diff variable the key press and key release of row and column
         * are found out. 
         *
         * If the key press is determined then scancode for key pressed 
         * can be generated using the following statement:
         *    scancode = ((row * 8) + col);
         *
         * If the key release is determined then scancode for key release 
         * can be generated using the following statement:
         *    scancode = ((row * 8) + col) + 128;
         */
        for (row = 0; row < kpp_dev.kpp_rows; row++) {
                unsigned char diff;

                /*
                 * Calculate the change in the keypad row status
                 */
                diff = prev_rcmap[row] ^ cur_rcmap[row];

                for (col = 0; col < kpp_dev.kpp_cols; col++) {
                        if ((diff >> col) & 0x1) {
                                /* There is a status change on col */
                                if ((prev_rcmap[row] & BITSET(0, col)) == 0) {
                                        /* 
                                         * Previous state is 0, so now
                                         * a key is pressed 
                                         */
                                        scancode = ((row *
                                                   kpp_dev.kpp_cols) + col);
                                        Kmapevent = 1;
                                        /* Indicates key press */
                                        KPress = 1;
                                        /* Change to state KStateUp for key 
                                           press debounce. */
                                        kpp_dev.iKeyState = KStateUp;
#ifdef KPP_DEBUG
                                        printk("Press   (%d, %d)" 
                                                "scan=%d\n", 
                                                row, col, scancode);
#endif
                                } else {

                                        /* 
                                         * Previous state is not 0, so
                                         * now a key is released 
                                         */
                                        scancode = (row *  
                                                  kpp_dev.kpp_cols) +
                                                  col + 128;
                                        Kmapevent = 0;
                                        /* Indicates key release */
                                        KPress = 0;
                                        /* Change to state KStateDown for key
                                           release debounce */     
                                        kpp_dev.iKeyState = KStateDown;
#ifdef KPP_DEBUG
                                        printk("Release (%d, %d) "
                                                 "scan=%d\n", row, col, 
                                                  scancode);
#endif                                                
                                }
                        }
                }
        }
        /* 
         * This switch case statement is the 
         * implementation of state machine of debounce 
         * logic for key press/release.
         * The explaination of state machine is as 
         * follows:
         *
         * KStateUp State:         
         * This is in intial state of the state machine
         * this state it checks for any key presses.
         * The key press can be checked using the 
         * variable KPress. If KPress is set, then key
         * press is identified and switches the to
         * KStateFirstDown state for key press to 
         * debounce.
         *
         * KStateFirstDown:
         * After debounce delay(10ms), if the KPress is
         * still set then pass scancode generated to
         * mxc_kpp_handle_mode and change the state to
         * KStateDown, else key press debounce is not
         * satisfied so change the state to KStateUp.
         *
         * KStateDown:                              
         * In this state it checks for any key release.
         * If KPress variable is cleared, then key 
         * release is indicated and so, switch the
         * state to KStateFirstUp else to state
         * KStateDown.
         *
         * KStateFirstUp:
         * After debounce delay(10ms), if the KPress is
         * still reset then pass the key release 
         * scancode to mxc_kpp_handle_mode and change
         * the state to KStateUp else key release is
         * not satisfied so change the state to 
         * KStateDown.
         */         
        switch (kpp_dev.iKeyState) {
        case KStateUp:
#ifdef KPP_DEBUg
                printk("KPP: In KStateUp state. \n");
#endif                        
                if (KPress) {
                        /* First Down (must debounce).*/
                        kpp_dev.iKeyState = KStateFirstDown;
                } else {
                        /* Still UP.(NO Changes) */
                        kpp_dev.iKeyState = KStateUp;
                }
        break;
               
        case KStateFirstDown:
#ifdef KPP_DEBUG
                printk("KPP: In KStateFirstDown. \n");
#endif                        
                if (KPress) { 
                        /* Still Down, so add scancode*/
                        mxc_kpp_handle_mode(scancode);
                        kpp_dev.iKeyState = KStateDown;
                } else  {
                        /* Just a bounce */
                        kpp_dev.iKeyState = KStateUp;
                }
        break;
                                
        case KStateDown:  
#ifdef KPP_DEBUG
                printk("KPP: In KStateDown. \n");
#endif                        
                if (KPress){
                        /* Still down (no change) */
                        kpp_dev.iKeyState = KStateDown;
                } else {
                        /* First Up. Must debounce */
                        kpp_dev.iKeyState = KStateFirstUp;
                }
        break;
                                
        case KStateFirstUp:
#ifdef KPP_DEBUG
                printk("KPP: In KStateFirstUp. \n");
#endif                        
                if (KPress) {
                        /* Just a bounce */
                        kpp_dev.iKeyState = KStateDown;
                } else {
                        /* Still up, so add scancode */
                        mxc_kpp_handle_mode(scancode);
                        kpp_dev.iKeyState = KStateUp; 
                }
        break;

        default:
#ifdef KPP_DEBUG
                printk("KPP : in Default \n");
#endif                        
                return -EBADRQC;
        break;                                
        }

#ifdef KPP_DEBUG
        printk("KPP: cur_rcmap 0x%X%X%X%X%X%X%X%X\n", 
                cur_rcmap[0], cur_rcmap[1], cur_rcmap[2],cur_rcmap[3], 
                cur_rcmap[4],cur_rcmap[5],cur_rcmap[6],cur_rcmap[7]);
        printk("keycnt: %d\n", keycnt);
#endif
        return keycnt;
}

/*!
 * This function is called to start the timer for scanning the keypad if the
 * keypad has any key pressed. When the keypad has any key pressed then we are
 * in polling mode and the timer is set for next keypad scan. Currently this
 * interval is set to 10 ms. When there are no keys pressed on the keypad we
 * return back to interrupt mode, waiting for a keypad key press event. 
 *
 * @param data  Opaque data passed back by kernel. Not used.
 */
static void mxc_kpp_handle_timer(unsigned long data)
{
        unsigned short reg_val;
        if (mxc_kpp_scan_matrix() == 0) {
                /* 
                 * Stop scanning and wait for interrupt. 
                 * Enable press interrupt and disable release interrupt.
                 */
                reg_val = readw(KPSR);
                reg_val |= KBD_STAT_KDIE;
                reg_val &= ~KBD_STAT_KRIE;
                reg_val |= KBD_STAT_KPKR | KBD_STAT_KPKD;
                writew(reg_val, KPSR);
        }
        /* 
         * There are still some keys pressed, continue to scan.
         * We shall scan again in 10 ms. This has to be tuned according
         * to the requirement.
         */
        kpp_dev.poll_timer.expires = jiffies + KScanRate;
        kpp_dev.poll_timer.function = mxc_kpp_handle_timer;
        add_timer(&kpp_dev.poll_timer);  
}

/*!
 * This function is the keypad Interrupt handler.
 * This function checks for keypad status register (KPSR) for key press 
 * and key release interrupt. If key press interrupt has occurred, then the key
 * press and key release interrupt in the KPSR are disabled. If the key
 * release interrupt has occurred, then key release interrupt is disabled and
 * key press interrupt is enabled in the KPSR.
 * It then calls mxc_kpp_scan_matrix to check for any key pressed/released.
 * If any key is found to be pressed, then a timer is set to call
 * mxc_kpp_scan_matrix function for every 10 ms.
 *
 * @param   irq      The Interrupt number
 * @param   dev_id   Driver private data
 * @param   regs     Holds a snapshot of the processors context before the
 *                   processor entered the interrupt code
 *
 * @result    The function returns \b IRQ_RETVAL(1) if interrupt was handled,
 *            returns \b IRQ_RETVAL(0) if the interrupt was not handled. 
 *            \b IRQ_RETVAL is defined in include/linux/interrupt.h.
 */ 
static irqreturn_t mxc_kpp_interrupt(int irq, void *dev_id,
                                      struct pt_regs *regs)
{
        unsigned short reg_val;

        /* Delete the polling timer */
        del_timer(&kpp_dev.poll_timer);
        reg_val = readw(KPSR);

        /* Check if it is key press interrupt */
        if (reg_val & KBD_STAT_KPKD) {
#ifdef KPP_DEBUG
                printk("KPP: Key press interrupt\n");
#endif
                /*
                 * Disable key press(KDIE status bit) interrupt
                 * clear  key release(KRIE status bit) interrupt
                 */
                reg_val &= ~KBD_STAT_KRIE;
                reg_val &= ~KBD_STAT_KDIE;
                writew(reg_val, KPSR);
#ifdef KPP_DEBUG
                reg_val = readw(KPSR); 
                printk("KPP: Status reg value after disable 0x%x\n", reg_val);
#endif
        } else if (reg_val & KBD_STAT_KPKR) {
                /* Check if it is key release interrupt */
#ifdef KPP_DEBUG
                printk("KPP: Key release interrupt occurred (not expected)\n");
#endif
                /*
                 * set key press(KDIE status bit) interrupt.
                 * clear key release(KRIE status bit) interrupt.
                 */ 
                reg_val |= KBD_STAT_KDIE;
                reg_val &= ~KBD_STAT_KRIE;
                writew(reg_val, KPSR);
        } else {
                /* spurious interrupt */
                return IRQ_RETVAL(0);
        }

        /*
         * Check if any keys are pressed, if so start polling.
         */
        mxc_kpp_handle_timer(0);

        return IRQ_RETVAL(1);
}

/*!
 * This function is called when the keypad driver is opened. This function does
 * the initialization of the keypad device.
 *
 * The following steps are used for keypad configuration,\n
 * -# Enable number of rows in the keypad control register (KPCR[7:0}).\n
 * -# Write 0's to KPDR[15:8]\n
 * -# Configure keypad columns as open-drain (KPCR[15:8])\n
 * -# Configure columns as output, rows as input (KDDR[15:0])\n
 * -# Clear the KPKD status flag (write 1 to it) and synchronizer chain\n
 * -# Set KDIE control bit, clear KRIE control bit\n
 * In this function the keypad queue initialization is done. 
 * The keypad IOMUX configuration are done here.
 *
 * @param    inode    Pointer to device inode 
 * @param    file     Pointer to device file structure
 *
 * @result    The function returns 0 on success and a non-zero value on
 *            failure.
 */
static int mxc_kpp_open(struct inode *inode, struct file *file)
{
        unsigned short reg_val;

#ifdef KPP_DEBUG
        printk("KPP: kdb_open() called... \n");
#endif
   
        /* IOMUX configuration for keypad */
        gpio_keypad_active();

        /* Configure for non blocking read */
        file->f_flags |= O_NONBLOCK;

        /* Configure keypad */

        /* Enable number of rows in keypad (KPCR[7:0])
         * Configure keypad columns as open-drain (KPCR[15:8])
         * 
         * Configure the rows/cols in KPP 
         * LSB nibble in KPP is for 8 rows
         * MSB nibble in KPP is for 8 cols
         */
        reg_val = readw(KPCR);
        reg_val |= (1 << MAXROW) - 1;          /* LSB */
        reg_val |= ((1 << MAXCOL) - 1) << 8;  /* MSB */
        writew(reg_val, KPCR);

        /* Write 0's to KPDR[15:8] */
        reg_val = readw(KPDR);
        reg_val &= 0x00ff;
        writew(reg_val, KPDR);

        /* Configure columns as output, rows as input (KDDR[15:0]) */
        reg_val = readw(KDDR);
        reg_val |= 0xff00;
        reg_val &= 0xff00;
        writew(reg_val, KDDR);

        /* 
         * Clear the KPKD status flag (write 1 to it) and synchronizer chain.
         * Set KDIE control bit, clear KRIE control bit (avoid false release
         * events.
         */
        reg_val = readw(KPSR);
        reg_val |= KBD_STAT_KPKD;
        reg_val &= ~KBD_STAT_KRSS;
        reg_val |= KBD_STAT_KDIE;
        reg_val &= ~KBD_STAT_KRIE;
        writew(reg_val, KPSR);

        memset(cur_rcmap, 0, sizeof(cur_rcmap));
        memset(prev_rcmap, 0, sizeof(prev_rcmap));
        /* Clear the input queue */
        mxc_kpp_initq();
        return 0;
}

/*!
 * This function is called close the keypad device. This function puts the
 * keypad to standby mode. The keypad interrupts are disabled. It calls
 * gpio_keypad_inactive function to switch gpio configuration into default
 * state. 
 *
 * @param    inode    Pointer to device inode 
 * @param    file     Pointer to device file structure
 *
 * @result    The function returns 0 on success and a non-zero value on 
 *            failure.
 *
 */
static int mxc_kpp_close(struct inode *inode, struct file *file)
{
        unsigned short reg_val;

#ifdef KPP_DEBUG
        printk("Kpp: Keypad close function is executed \n");
#endif

        /* 
         * Clear the KPKD status flag (write 1 to it) and synchronizer chain.
         * Set KDIE control bit, clear KRIE control bit (avoid false release
         * events. Disable the keypad GPIO pins.
         */
        writew(0x00, KPCR);
        writew(0x00, KPDR);
        writew(0x00, KDDR);
        reg_val = readw(KPSR);
        reg_val |= KBD_STAT_KPKD;
        reg_val &= ~KBD_STAT_KRSS;
        reg_val |= KBD_STAT_KDIE;
        reg_val &= ~KBD_STAT_KRIE;
        writew(reg_val, KPSR);
        gpio_keypad_inactive();
        KPress = 0;
        memset(cur_rcmap, 0, sizeof(cur_rcmap));
        memset(prev_rcmap, 0, sizeof(prev_rcmap));
        return 0;
}

/*!
 * This function is called when a ioctl call is made from user space.
 *
 * @param    inode    Pointer to device inode 
 * @param    file     Pointer to device file structure
 * @param    cmd      Ioctl command
 * @param    arg      Ioctl argument
 *
 *                    Following are the ioctl commands for user to use:\n
 *                    KDSKBMODE :  Sets the current keypad mode(RAW or XLATE
 *                    mode).The arguments can be K_RAW or K_XLATE.\n 
 *                    KDROWCOL  :  To configure keypad matrix for upto 8 * 8
 *                    rows and columns. The arguments are number of rows(0-8) 
 *                    and number of columns(0-8)\n 
 *                    KDSCANRATE:  Sets the keypad debounce delay value.  
 *                    KDSBLOCK  :  Sets for blocking and non-blocking read. 
 *                    The arguments can be BLOCK or NONBLOCK.\n
 *                    KDSCANRATE:  Sets debounce delay.\n          
 *
 * @result    The function returns 0 on success and a non-zero value on 
 *            failure.
 */
int mxc_kpp_ioctl(struct inode *inode, struct file * file, 
                       unsigned int cmd, unsigned long arg)
{
        struct kbd_arg *conf = (struct kbd_arg *)arg;
        unsigned short reg_val;

#ifdef KPP_DEBUG
        printk("KPP: Keypad ioctl function called (cmd %x arg 0x%08lX\n", 
        cmd, arg);
#endif
        if (!conf) {
#ifdef KPP_DEBUG
                printk("KPP: ioctl: NULL input\n");
#endif
                return -EINVAL;
        }
        switch (cmd) {
        case KDSKBMODE:
                switch(conf->arg1) {
                case K_RAW:
                        kpp_dev.kpp_mode = KEYPAD_RAW;
#ifdef KPP_DEBUG
                        printk("KPP: RAW mode kpp_mode %d\n",
                                kpp_dev.kpp_mode);
#endif
                        break;

                case K_XLATE:
                        kpp_dev.kpp_mode = KEYPAD_XLATE;
#ifdef KPP_DEBUG
                        printk("KPP: XLATE mode kpp_mode %d\n",
                                kpp_dev.kpp_mode);
#endif
                        break;

                default:
#ifdef KPP_DEBUG
                        printk("KPP: ioctl: KDSKBMODE: bad args %d\n",
                                kpp_dev.kpp_mode);
#endif
                        return -EINVAL;
                        break;
                }
                break;  

        case KDROWCOL:
                if ((conf->arg1 > MAXROW) || (conf->arg2 > MAXCOL)
                   || (conf->arg1 == 0) || (conf->arg2 == 0)) {
#ifdef KPP_DEBUG
                        printk("KPP: ioctl: KDROWCOL: bad args row=0x%08lX," \
                                "col= 0x%08lX\n", conf->arg1, conf->arg2);
#endif
                        return -EINVAL;
                }

                kpp_dev.kpp_rows = conf->arg1;
                kpp_dev.kpp_cols = conf->arg2;

                /* 
                 * Configure the rows/cols in KPP 
                 * LSB nibble in KPP is for max 8 rows
                 * MSB nibble in KPP is for max 8 cols
                 */
                reg_val = (1 << kpp_dev.kpp_rows) - 1;       /* LSB for rows */
                reg_val |= ((1 << kpp_dev.kpp_cols) - 1) << 8;/* MSB for cols*/
                writew(reg_val, KPCR);
#ifdef KPP_DEBUG
                printk("KPP: ioctl: KDROWCOL: row=0x%08lX,col=0x%08lX" \
                       "reg_val=0x%X\n", conf->arg1, conf->arg2, reg_val);
#endif
                break;

        case KDSCANRATE:
#ifdef KPP_DEBUG
                printk("KPP: ioctl: Pass user keypad scan rate. \n");
#endif                        
                KScanRate = conf->arg1;
                break;                                               

        case KDSBLOCK:
                if (conf->arg1 == BLOCK) {
                        file->f_flags &= ~O_NONBLOCK;
#ifdef KPP_DEBUG
                        printk("KPP: set to BLOCK mode:");
                        printk(" file->f_flags=0x%08X,"\
                                "O_NONBLOCK bit=0x%08X\n", \
                                file->f_flags, O_NONBLOCK);
#endif
                } else if (conf->arg1 == NONBLOCK) {
                        file->f_flags |= O_NONBLOCK;
#ifdef KPP_DEBUG
                        printk("KPP: set to NONBLOCK mode:");
                        printk(" file->f_flags=0x%08X," \
                                "O_NONBLOCK bit=0x%08X\n",\
                                file->f_flags, O_NONBLOCK);
#endif
                }
                break;    

        default:
#ifdef KPP_DEBUG
                printk("KPP: ioctl: unknown cmd=%d,\n", cmd);
#endif
                return  -EINVAL;
                break;
        }

#ifdef KPP_DEBUG
        printk("KPP: ioctl done .....\n");
#endif
        return 0;
}

/*!
 * This function is called when read system call is executed.
 *
 *  @param  file        Pointer to device file structure
 *  @param  buf         User buffer, where read data to be placed.
 *  @param  count       Size of the requested data transfer
 *  @param  pos         File position where the user is accessing.
 *
 *  @result      Number of bytes read.
 */
static ssize_t mxc_kpp_read(struct file * file, char * buf, 
                                  size_t count, loff_t *pos)
{
        ssize_t i = count;
        unsigned char c;

#ifdef KPP_DEBUG
        printk("KPP: read called....\n");
        printk("value of read count %d\n", i);
#endif
        while (i > 0) {

                /* 
                 * If we don't have any data in queue and if we are in 
                 * NONBLOCK mode return error to user.  If we are not
                 * in NONBLOCK mode, go to interruptible sleep until data 
                 * is available.
                 */
                if (mxc_kpp_q_empty() && (file->f_flags & O_NONBLOCK)) {
                        return -EAGAIN;
                }

                /* No data and blocking read */
                while (mxc_kpp_q_empty()) {
                            if (wait_event_interruptible(
                                    kpp_dev.queue.waitq,
                                    (kpp_dev.queue.head !=
                                    kpp_dev.queue.tail))) {
                                    return -ERESTARTSYS; 
                            }
                }

                /*
                 * Read data till the number of bytes user has requested
                 */
                while (i > 0 && !mxc_kpp_q_empty()) {
                        c = mxc_kpp_pullq();
#ifdef KPP_DEBUG
                        printk("KPP: data from queue %d\n", c);
#endif
                        put_user(c, buf++);
                        i--;
                } 

                /*
                 * If we read less than requested data, indicate it to user.
                 */
                if (i && (file->f_flags & O_NONBLOCK)) {
                            file->f_dentry->d_inode->i_atime = CURRENT_TIME;
                            return count-i;
                }
                if (signal_pending(current))    {
                        return -ERESTARTSYS;
                }
        }
        /* Change the file access time */
        file->f_dentry->d_inode->i_atime = CURRENT_TIME;
        return count;
}

/*!
 * This function is called to check if any data is available. This is called
 * by the kernel poll functionality.
 *
 * @param    file      Pointer to device file structure
 * @param    wait      Poll table structure is used within the kernel to 
 *                     implement the poll and select system calls; it is 
 *                     declared in linux/poll.h.
 *
 * @result      Bit mask events POLLIN or POLLRDNORM if data is available in
 *              keypad queue, else returns zero.
 */
static unsigned int mxc_kpp_poll(struct file *file, poll_table *wait)
{
#ifdef KPP_DEBUG
        printk("Kpp:polling function called .....\n");
        printk("Value of queue.head ... %d\n", kpp_dev.queue.head);
        printk("value of queue.tail ... %d\n", kpp_dev.queue.tail);
#endif
        poll_wait(file, &kpp_dev.queue.waitq, wait);
        return (kpp_dev.queue.head != kpp_dev.queue.tail) ? 
                (POLLIN | POLLRDNORM) : 0;
}

/*!
 * This structure contains pointers for device driver entry point. 
 * The driver register function in init module will call this
 * structure.
 */
static struct file_operations keypad_fops = {
        .owner          = THIS_MODULE,
        .open           = mxc_kpp_open,
        .release        = mxc_kpp_close,
        .read           = mxc_kpp_read,
        .ioctl          = mxc_kpp_ioctl,
        .poll           = mxc_kpp_poll,
};

static struct miscdevice mxc_keyb_misc_device = {
        .minor = MISC_DYNAMIC_MINOR,
        .name = MOD_NAME,
        .fops = &keypad_fops,
        .devfs_name = "mxckpd"
};

/*! 
 * This function is called for module initialization.
 * It registers keypad char driver and requests for KPP irq number.
 *
 * @return      0 on success and a non-zero value on failure.
 */
static int __init mxc_kpp_init(void)
{
        int retval, ret;
#ifdef KPP_DEBUG
        unsigned int reg_val;
        printk("KPP: mxc_kpp_init() called \n");
#endif
        kpp_dev.kpp_cols = MAXCOL;
        kpp_dev.kpp_rows = MAXROW;

        if ((ret = misc_register(&mxc_keyb_misc_device))) {
                printk(KERN_ERR "KPP: misc_register failed\n");
                goto OUT;
        }  

        /* 
         * Request for IRQ number for keypad port. The Interrupt handler
         * function (mxc_kpp_interrupt) is called when ever interrupt occurs on
         * keypad port.
         */
        retval = request_irq(INT_KPP, mxc_kpp_interrupt, 
                             0, MOD_NAME, MOD_NAME);

        if (retval) {
#ifdef KPP_DEBUG
                printk("KPP: request_irq(%d) returned error %d\n", INT_KPP,
                        retval);
#endif
                return -1;
        }  

#ifdef KPP_DEBUG
        printk("KPP: irq enabled: %d\n", INT_KPP);
#endif

#ifdef KPP_DEBUG
        printk("KPP: base address  :  %08lX\n", (unsigned long)KPP_BASE_ADDR);
        printk("KPP: status reg address:  %08lX\n", (unsigned long)KPSR);
        printk("KPP: control reg address :  %08lX\n", (unsigned long)KPCR);
        printk("KPP: data direction  reg address : %08lX\n", \
                (unsigned long)KDDR);
        printk("KPP: data reg address : %08lX\n", (unsigned long)KPDR);
        /* Read the value of registers */
        reg_val = readw(KPSR);
        printk("KPP: status reg: %08X\n", reg_val);
        reg_val = readw(KPCR);
        printk("KPP: control reg: %08X\n", reg_val);
        reg_val = readw(KPDR);
        printk("KPP: data reg: %08X\n", reg_val);
        reg_val = readw(KDDR);
        printk("KPP: data direction reg : %08X\n", reg_val);
#endif  /* KPP_DEBUG */

        mxckbd_dev.name = mxckbd_name;
        mxckbd_dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);

        input_register_device(&mxckbd_dev);
        /* Initialize the polling timer */
        init_timer(&kpp_dev.poll_timer);
        return 0;

OUT:
        return ret;
}

/*!
 * This function is called whenever the module is removed from the kernel. It
 * unregisters the keypad driver from kernel and frees the irq number.
 */
static void __exit mxc_kpp_cleanup(void)    
{
#ifdef KPP_DEBUG
        printk("KPP: module cleanup is called \n");
#endif
        del_timer(&kpp_dev.poll_timer);
        free_irq(INT_KPP, MOD_NAME);
        misc_deregister(&mxc_keyb_misc_device);
        input_unregister_device(&mxckbd_dev);
}

module_init(mxc_kpp_init);
module_exit(mxc_kpp_cleanup);
