1bab7614dSEric Miao /* 2bab7614dSEric Miao * GPIO driven matrix keyboard driver 3bab7614dSEric Miao * 4bab7614dSEric Miao * Copyright (c) 2008 Marek Vasut <marek.vasut@gmail.com> 5bab7614dSEric Miao * 6bab7614dSEric Miao * Based on corgikbd.c 7bab7614dSEric Miao * 8bab7614dSEric Miao * This program is free software; you can redistribute it and/or modify 9bab7614dSEric Miao * it under the terms of the GNU General Public License version 2 as 10bab7614dSEric Miao * published by the Free Software Foundation. 11bab7614dSEric Miao * 12bab7614dSEric Miao */ 13bab7614dSEric Miao 14bab7614dSEric Miao #include <linux/types.h> 15bab7614dSEric Miao #include <linux/delay.h> 16bab7614dSEric Miao #include <linux/platform_device.h> 17bab7614dSEric Miao #include <linux/init.h> 18bab7614dSEric Miao #include <linux/input.h> 19bab7614dSEric Miao #include <linux/irq.h> 20bab7614dSEric Miao #include <linux/interrupt.h> 21bab7614dSEric Miao #include <linux/jiffies.h> 22bab7614dSEric Miao #include <linux/module.h> 23bab7614dSEric Miao #include <linux/gpio.h> 24bab7614dSEric Miao #include <linux/input/matrix_keypad.h> 255a0e3ad6STejun Heo #include <linux/slab.h> 264a83eecfSAnilKumar Ch #include <linux/of.h> 274a83eecfSAnilKumar Ch #include <linux/of_gpio.h> 284a83eecfSAnilKumar Ch #include <linux/of_platform.h> 29bab7614dSEric Miao 30bab7614dSEric Miao struct matrix_keypad { 31bab7614dSEric Miao const struct matrix_keypad_platform_data *pdata; 32bab7614dSEric Miao struct input_dev *input_dev; 33d82f1c35SEric Miao unsigned int row_shift; 34bab7614dSEric Miao 35dd219234SDmitry Torokhov DECLARE_BITMAP(disabled_gpios, MATRIX_MAX_ROWS); 36dd219234SDmitry Torokhov 37bab7614dSEric Miao uint32_t last_key_state[MATRIX_MAX_COLS]; 38bab7614dSEric Miao struct delayed_work work; 39dd219234SDmitry Torokhov spinlock_t lock; 40bab7614dSEric Miao bool scan_pending; 41bab7614dSEric Miao bool stopped; 42fb76dd10SLuotao Fu bool gpio_all_disabled; 43bab7614dSEric Miao }; 44bab7614dSEric Miao 45bab7614dSEric Miao /* 46bab7614dSEric Miao * NOTE: normally the GPIO has to be put into HiZ when de-activated to cause 47bab7614dSEric Miao * minmal side effect when scanning other columns, here it is configured to 48bab7614dSEric Miao * be input, and it should work on most platforms. 49bab7614dSEric Miao */ 50bab7614dSEric Miao static void __activate_col(const struct matrix_keypad_platform_data *pdata, 51bab7614dSEric Miao int col, bool on) 52bab7614dSEric Miao { 53bab7614dSEric Miao bool level_on = !pdata->active_low; 54bab7614dSEric Miao 55bab7614dSEric Miao if (on) { 56bab7614dSEric Miao gpio_direction_output(pdata->col_gpios[col], level_on); 57bab7614dSEric Miao } else { 58bab7614dSEric Miao gpio_set_value_cansleep(pdata->col_gpios[col], !level_on); 59bab7614dSEric Miao gpio_direction_input(pdata->col_gpios[col]); 60bab7614dSEric Miao } 61bab7614dSEric Miao } 62bab7614dSEric Miao 63bab7614dSEric Miao static void activate_col(const struct matrix_keypad_platform_data *pdata, 64bab7614dSEric Miao int col, bool on) 65bab7614dSEric Miao { 66bab7614dSEric Miao __activate_col(pdata, col, on); 67bab7614dSEric Miao 68bab7614dSEric Miao if (on && pdata->col_scan_delay_us) 69bab7614dSEric Miao udelay(pdata->col_scan_delay_us); 70bab7614dSEric Miao } 71bab7614dSEric Miao 72bab7614dSEric Miao static void activate_all_cols(const struct matrix_keypad_platform_data *pdata, 73bab7614dSEric Miao bool on) 74bab7614dSEric Miao { 75bab7614dSEric Miao int col; 76bab7614dSEric Miao 77bab7614dSEric Miao for (col = 0; col < pdata->num_col_gpios; col++) 78bab7614dSEric Miao __activate_col(pdata, col, on); 79bab7614dSEric Miao } 80bab7614dSEric Miao 81bab7614dSEric Miao static bool row_asserted(const struct matrix_keypad_platform_data *pdata, 82bab7614dSEric Miao int row) 83bab7614dSEric Miao { 84bab7614dSEric Miao return gpio_get_value_cansleep(pdata->row_gpios[row]) ? 85bab7614dSEric Miao !pdata->active_low : pdata->active_low; 86bab7614dSEric Miao } 87bab7614dSEric Miao 88bab7614dSEric Miao static void enable_row_irqs(struct matrix_keypad *keypad) 89bab7614dSEric Miao { 90bab7614dSEric Miao const struct matrix_keypad_platform_data *pdata = keypad->pdata; 91bab7614dSEric Miao int i; 92bab7614dSEric Miao 93fb76dd10SLuotao Fu if (pdata->clustered_irq > 0) 94fb76dd10SLuotao Fu enable_irq(pdata->clustered_irq); 95fb76dd10SLuotao Fu else { 96bab7614dSEric Miao for (i = 0; i < pdata->num_row_gpios; i++) 97bab7614dSEric Miao enable_irq(gpio_to_irq(pdata->row_gpios[i])); 98bab7614dSEric Miao } 99fb76dd10SLuotao Fu } 100bab7614dSEric Miao 101bab7614dSEric Miao static void disable_row_irqs(struct matrix_keypad *keypad) 102bab7614dSEric Miao { 103bab7614dSEric Miao const struct matrix_keypad_platform_data *pdata = keypad->pdata; 104bab7614dSEric Miao int i; 105bab7614dSEric Miao 106fb76dd10SLuotao Fu if (pdata->clustered_irq > 0) 107fb76dd10SLuotao Fu disable_irq_nosync(pdata->clustered_irq); 108fb76dd10SLuotao Fu else { 109bab7614dSEric Miao for (i = 0; i < pdata->num_row_gpios; i++) 110bab7614dSEric Miao disable_irq_nosync(gpio_to_irq(pdata->row_gpios[i])); 111bab7614dSEric Miao } 112fb76dd10SLuotao Fu } 113bab7614dSEric Miao 114bab7614dSEric Miao /* 115bab7614dSEric Miao * This gets the keys from keyboard and reports it to input subsystem 116bab7614dSEric Miao */ 117bab7614dSEric Miao static void matrix_keypad_scan(struct work_struct *work) 118bab7614dSEric Miao { 119bab7614dSEric Miao struct matrix_keypad *keypad = 120bab7614dSEric Miao container_of(work, struct matrix_keypad, work.work); 121bab7614dSEric Miao struct input_dev *input_dev = keypad->input_dev; 1224a83eecfSAnilKumar Ch const unsigned short *keycodes = input_dev->keycode; 123bab7614dSEric Miao const struct matrix_keypad_platform_data *pdata = keypad->pdata; 124bab7614dSEric Miao uint32_t new_state[MATRIX_MAX_COLS]; 125bab7614dSEric Miao int row, col, code; 126bab7614dSEric Miao 127bab7614dSEric Miao /* de-activate all columns for scanning */ 128bab7614dSEric Miao activate_all_cols(pdata, false); 129bab7614dSEric Miao 130bab7614dSEric Miao memset(new_state, 0, sizeof(new_state)); 131bab7614dSEric Miao 132bab7614dSEric Miao /* assert each column and read the row status out */ 133bab7614dSEric Miao for (col = 0; col < pdata->num_col_gpios; col++) { 134bab7614dSEric Miao 135bab7614dSEric Miao activate_col(pdata, col, true); 136bab7614dSEric Miao 137bab7614dSEric Miao for (row = 0; row < pdata->num_row_gpios; row++) 138bab7614dSEric Miao new_state[col] |= 139bab7614dSEric Miao row_asserted(pdata, row) ? (1 << row) : 0; 140bab7614dSEric Miao 141bab7614dSEric Miao activate_col(pdata, col, false); 142bab7614dSEric Miao } 143bab7614dSEric Miao 144bab7614dSEric Miao for (col = 0; col < pdata->num_col_gpios; col++) { 145bab7614dSEric Miao uint32_t bits_changed; 146bab7614dSEric Miao 147bab7614dSEric Miao bits_changed = keypad->last_key_state[col] ^ new_state[col]; 148bab7614dSEric Miao if (bits_changed == 0) 149bab7614dSEric Miao continue; 150bab7614dSEric Miao 151bab7614dSEric Miao for (row = 0; row < pdata->num_row_gpios; row++) { 152bab7614dSEric Miao if ((bits_changed & (1 << row)) == 0) 153bab7614dSEric Miao continue; 154bab7614dSEric Miao 155d82f1c35SEric Miao code = MATRIX_SCAN_CODE(row, col, keypad->row_shift); 156bab7614dSEric Miao input_event(input_dev, EV_MSC, MSC_SCAN, code); 157bab7614dSEric Miao input_report_key(input_dev, 1584a83eecfSAnilKumar Ch keycodes[code], 159bab7614dSEric Miao new_state[col] & (1 << row)); 160bab7614dSEric Miao } 161bab7614dSEric Miao } 162bab7614dSEric Miao input_sync(input_dev); 163bab7614dSEric Miao 164bab7614dSEric Miao memcpy(keypad->last_key_state, new_state, sizeof(new_state)); 165bab7614dSEric Miao 166bab7614dSEric Miao activate_all_cols(pdata, true); 167bab7614dSEric Miao 168bab7614dSEric Miao /* Enable IRQs again */ 169bab7614dSEric Miao spin_lock_irq(&keypad->lock); 170bab7614dSEric Miao keypad->scan_pending = false; 171bab7614dSEric Miao enable_row_irqs(keypad); 172bab7614dSEric Miao spin_unlock_irq(&keypad->lock); 173bab7614dSEric Miao } 174bab7614dSEric Miao 175bab7614dSEric Miao static irqreturn_t matrix_keypad_interrupt(int irq, void *id) 176bab7614dSEric Miao { 177bab7614dSEric Miao struct matrix_keypad *keypad = id; 178bab7614dSEric Miao unsigned long flags; 179bab7614dSEric Miao 180bab7614dSEric Miao spin_lock_irqsave(&keypad->lock, flags); 181bab7614dSEric Miao 182bab7614dSEric Miao /* 183bab7614dSEric Miao * See if another IRQ beaten us to it and scheduled the 184bab7614dSEric Miao * scan already. In that case we should not try to 185bab7614dSEric Miao * disable IRQs again. 186bab7614dSEric Miao */ 187bab7614dSEric Miao if (unlikely(keypad->scan_pending || keypad->stopped)) 188bab7614dSEric Miao goto out; 189bab7614dSEric Miao 190bab7614dSEric Miao disable_row_irqs(keypad); 191bab7614dSEric Miao keypad->scan_pending = true; 192bab7614dSEric Miao schedule_delayed_work(&keypad->work, 193bab7614dSEric Miao msecs_to_jiffies(keypad->pdata->debounce_ms)); 194bab7614dSEric Miao 195bab7614dSEric Miao out: 196bab7614dSEric Miao spin_unlock_irqrestore(&keypad->lock, flags); 197bab7614dSEric Miao return IRQ_HANDLED; 198bab7614dSEric Miao } 199bab7614dSEric Miao 200bab7614dSEric Miao static int matrix_keypad_start(struct input_dev *dev) 201bab7614dSEric Miao { 202bab7614dSEric Miao struct matrix_keypad *keypad = input_get_drvdata(dev); 203bab7614dSEric Miao 204bab7614dSEric Miao keypad->stopped = false; 205bab7614dSEric Miao mb(); 206bab7614dSEric Miao 207bab7614dSEric Miao /* 208bab7614dSEric Miao * Schedule an immediate key scan to capture current key state; 209bab7614dSEric Miao * columns will be activated and IRQs be enabled after the scan. 210bab7614dSEric Miao */ 211bab7614dSEric Miao schedule_delayed_work(&keypad->work, 0); 212bab7614dSEric Miao 213bab7614dSEric Miao return 0; 214bab7614dSEric Miao } 215bab7614dSEric Miao 216bab7614dSEric Miao static void matrix_keypad_stop(struct input_dev *dev) 217bab7614dSEric Miao { 218bab7614dSEric Miao struct matrix_keypad *keypad = input_get_drvdata(dev); 219bab7614dSEric Miao 220bab7614dSEric Miao keypad->stopped = true; 221bab7614dSEric Miao mb(); 222bab7614dSEric Miao flush_work(&keypad->work.work); 223bab7614dSEric Miao /* 224bab7614dSEric Miao * matrix_keypad_scan() will leave IRQs enabled; 225bab7614dSEric Miao * we should disable them now. 226bab7614dSEric Miao */ 227bab7614dSEric Miao disable_row_irqs(keypad); 228bab7614dSEric Miao } 229bab7614dSEric Miao 2300508c19aSDmitry Torokhov #ifdef CONFIG_PM_SLEEP 231fb76dd10SLuotao Fu static void matrix_keypad_enable_wakeup(struct matrix_keypad *keypad) 232bab7614dSEric Miao { 233bab7614dSEric Miao const struct matrix_keypad_platform_data *pdata = keypad->pdata; 234fb76dd10SLuotao Fu unsigned int gpio; 235bab7614dSEric Miao int i; 236bab7614dSEric Miao 237fb76dd10SLuotao Fu if (pdata->clustered_irq > 0) { 238fb76dd10SLuotao Fu if (enable_irq_wake(pdata->clustered_irq) == 0) 239fb76dd10SLuotao Fu keypad->gpio_all_disabled = true; 240fb76dd10SLuotao Fu } else { 241bab7614dSEric Miao 242dd219234SDmitry Torokhov for (i = 0; i < pdata->num_row_gpios; i++) { 243dd219234SDmitry Torokhov if (!test_bit(i, keypad->disabled_gpios)) { 244fb76dd10SLuotao Fu gpio = pdata->row_gpios[i]; 245dd219234SDmitry Torokhov 246dd219234SDmitry Torokhov if (enable_irq_wake(gpio_to_irq(gpio)) == 0) 247dd219234SDmitry Torokhov __set_bit(i, keypad->disabled_gpios); 248dd219234SDmitry Torokhov } 249dd219234SDmitry Torokhov } 250dd219234SDmitry Torokhov } 251fb76dd10SLuotao Fu } 252fb76dd10SLuotao Fu 253fb76dd10SLuotao Fu static void matrix_keypad_disable_wakeup(struct matrix_keypad *keypad) 254fb76dd10SLuotao Fu { 255fb76dd10SLuotao Fu const struct matrix_keypad_platform_data *pdata = keypad->pdata; 256fb76dd10SLuotao Fu unsigned int gpio; 257fb76dd10SLuotao Fu int i; 258fb76dd10SLuotao Fu 259fb76dd10SLuotao Fu if (pdata->clustered_irq > 0) { 260fb76dd10SLuotao Fu if (keypad->gpio_all_disabled) { 261fb76dd10SLuotao Fu disable_irq_wake(pdata->clustered_irq); 262fb76dd10SLuotao Fu keypad->gpio_all_disabled = false; 263fb76dd10SLuotao Fu } 264fb76dd10SLuotao Fu } else { 265fb76dd10SLuotao Fu for (i = 0; i < pdata->num_row_gpios; i++) { 266fb76dd10SLuotao Fu if (test_and_clear_bit(i, keypad->disabled_gpios)) { 267fb76dd10SLuotao Fu gpio = pdata->row_gpios[i]; 268fb76dd10SLuotao Fu disable_irq_wake(gpio_to_irq(gpio)); 269fb76dd10SLuotao Fu } 270fb76dd10SLuotao Fu } 271fb76dd10SLuotao Fu } 272fb76dd10SLuotao Fu } 273fb76dd10SLuotao Fu 274fb76dd10SLuotao Fu static int matrix_keypad_suspend(struct device *dev) 275fb76dd10SLuotao Fu { 276fb76dd10SLuotao Fu struct platform_device *pdev = to_platform_device(dev); 277fb76dd10SLuotao Fu struct matrix_keypad *keypad = platform_get_drvdata(pdev); 278fb76dd10SLuotao Fu 279fb76dd10SLuotao Fu matrix_keypad_stop(keypad->input_dev); 280fb76dd10SLuotao Fu 281fb76dd10SLuotao Fu if (device_may_wakeup(&pdev->dev)) 282fb76dd10SLuotao Fu matrix_keypad_enable_wakeup(keypad); 283bab7614dSEric Miao 284bab7614dSEric Miao return 0; 285bab7614dSEric Miao } 286bab7614dSEric Miao 287f72a28abSDmitry Torokhov static int matrix_keypad_resume(struct device *dev) 288bab7614dSEric Miao { 289f72a28abSDmitry Torokhov struct platform_device *pdev = to_platform_device(dev); 290bab7614dSEric Miao struct matrix_keypad *keypad = platform_get_drvdata(pdev); 291bab7614dSEric Miao 292fb76dd10SLuotao Fu if (device_may_wakeup(&pdev->dev)) 293fb76dd10SLuotao Fu matrix_keypad_disable_wakeup(keypad); 294bab7614dSEric Miao 295bab7614dSEric Miao matrix_keypad_start(keypad->input_dev); 296bab7614dSEric Miao 297bab7614dSEric Miao return 0; 298bab7614dSEric Miao } 299bab7614dSEric Miao #endif 300bab7614dSEric Miao 3010508c19aSDmitry Torokhov static SIMPLE_DEV_PM_OPS(matrix_keypad_pm_ops, 3020508c19aSDmitry Torokhov matrix_keypad_suspend, matrix_keypad_resume); 3030508c19aSDmitry Torokhov 304b83643ebSDmitry Torokhov static int __devinit matrix_keypad_init_gpio(struct platform_device *pdev, 305bab7614dSEric Miao struct matrix_keypad *keypad) 306bab7614dSEric Miao { 307bab7614dSEric Miao const struct matrix_keypad_platform_data *pdata = keypad->pdata; 308b83643ebSDmitry Torokhov int i, err; 309bab7614dSEric Miao 310bab7614dSEric Miao /* initialized strobe lines as outputs, activated */ 311bab7614dSEric Miao for (i = 0; i < pdata->num_col_gpios; i++) { 312bab7614dSEric Miao err = gpio_request(pdata->col_gpios[i], "matrix_kbd_col"); 313bab7614dSEric Miao if (err) { 314bab7614dSEric Miao dev_err(&pdev->dev, 315bab7614dSEric Miao "failed to request GPIO%d for COL%d\n", 316bab7614dSEric Miao pdata->col_gpios[i], i); 317bab7614dSEric Miao goto err_free_cols; 318bab7614dSEric Miao } 319bab7614dSEric Miao 320bab7614dSEric Miao gpio_direction_output(pdata->col_gpios[i], !pdata->active_low); 321bab7614dSEric Miao } 322bab7614dSEric Miao 323bab7614dSEric Miao for (i = 0; i < pdata->num_row_gpios; i++) { 324bab7614dSEric Miao err = gpio_request(pdata->row_gpios[i], "matrix_kbd_row"); 325bab7614dSEric Miao if (err) { 326bab7614dSEric Miao dev_err(&pdev->dev, 327bab7614dSEric Miao "failed to request GPIO%d for ROW%d\n", 328bab7614dSEric Miao pdata->row_gpios[i], i); 329bab7614dSEric Miao goto err_free_rows; 330bab7614dSEric Miao } 331bab7614dSEric Miao 332bab7614dSEric Miao gpio_direction_input(pdata->row_gpios[i]); 333bab7614dSEric Miao } 334bab7614dSEric Miao 335fb76dd10SLuotao Fu if (pdata->clustered_irq > 0) { 336fb76dd10SLuotao Fu err = request_irq(pdata->clustered_irq, 337fb76dd10SLuotao Fu matrix_keypad_interrupt, 338fb76dd10SLuotao Fu pdata->clustered_irq_flags, 339fb76dd10SLuotao Fu "matrix-keypad", keypad); 340fb76dd10SLuotao Fu if (err) { 341fb76dd10SLuotao Fu dev_err(&pdev->dev, 342fb76dd10SLuotao Fu "Unable to acquire clustered interrupt\n"); 343fb76dd10SLuotao Fu goto err_free_rows; 344fb76dd10SLuotao Fu } 345fb76dd10SLuotao Fu } else { 346bab7614dSEric Miao for (i = 0; i < pdata->num_row_gpios; i++) { 347bab7614dSEric Miao err = request_irq(gpio_to_irq(pdata->row_gpios[i]), 348bab7614dSEric Miao matrix_keypad_interrupt, 349fb76dd10SLuotao Fu IRQF_TRIGGER_RISING | 350fb76dd10SLuotao Fu IRQF_TRIGGER_FALLING, 351bab7614dSEric Miao "matrix-keypad", keypad); 352bab7614dSEric Miao if (err) { 353bab7614dSEric Miao dev_err(&pdev->dev, 354b83643ebSDmitry Torokhov "Unable to acquire interrupt for GPIO line %i\n", 355bab7614dSEric Miao pdata->row_gpios[i]); 356bab7614dSEric Miao goto err_free_irqs; 357bab7614dSEric Miao } 358bab7614dSEric Miao } 359fb76dd10SLuotao Fu } 360bab7614dSEric Miao 361bab7614dSEric Miao /* initialized as disabled - enabled by input->open */ 362bab7614dSEric Miao disable_row_irqs(keypad); 363bab7614dSEric Miao return 0; 364bab7614dSEric Miao 365bab7614dSEric Miao err_free_irqs: 366bab7614dSEric Miao while (--i >= 0) 367bab7614dSEric Miao free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad); 368bab7614dSEric Miao i = pdata->num_row_gpios; 369bab7614dSEric Miao err_free_rows: 370bab7614dSEric Miao while (--i >= 0) 371bab7614dSEric Miao gpio_free(pdata->row_gpios[i]); 372bab7614dSEric Miao i = pdata->num_col_gpios; 373bab7614dSEric Miao err_free_cols: 374bab7614dSEric Miao while (--i >= 0) 375bab7614dSEric Miao gpio_free(pdata->col_gpios[i]); 376bab7614dSEric Miao 377bab7614dSEric Miao return err; 378bab7614dSEric Miao } 379bab7614dSEric Miao 380b83643ebSDmitry Torokhov static void matrix_keypad_free_gpio(struct matrix_keypad *keypad) 381b83643ebSDmitry Torokhov { 382b83643ebSDmitry Torokhov const struct matrix_keypad_platform_data *pdata = keypad->pdata; 383b83643ebSDmitry Torokhov int i; 384b83643ebSDmitry Torokhov 385b83643ebSDmitry Torokhov if (pdata->clustered_irq > 0) { 386b83643ebSDmitry Torokhov free_irq(pdata->clustered_irq, keypad); 387b83643ebSDmitry Torokhov } else { 388b83643ebSDmitry Torokhov for (i = 0; i < pdata->num_row_gpios; i++) 389b83643ebSDmitry Torokhov free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad); 390b83643ebSDmitry Torokhov } 391b83643ebSDmitry Torokhov 392b83643ebSDmitry Torokhov for (i = 0; i < pdata->num_row_gpios; i++) 393b83643ebSDmitry Torokhov gpio_free(pdata->row_gpios[i]); 394b83643ebSDmitry Torokhov 395b83643ebSDmitry Torokhov for (i = 0; i < pdata->num_col_gpios; i++) 396b83643ebSDmitry Torokhov gpio_free(pdata->col_gpios[i]); 397b83643ebSDmitry Torokhov } 398b83643ebSDmitry Torokhov 3994a83eecfSAnilKumar Ch #ifdef CONFIG_OF 4004a83eecfSAnilKumar Ch static struct matrix_keypad_platform_data * __devinit 4014a83eecfSAnilKumar Ch matrix_keypad_parse_dt(struct device *dev) 4024a83eecfSAnilKumar Ch { 4034a83eecfSAnilKumar Ch struct matrix_keypad_platform_data *pdata; 4044a83eecfSAnilKumar Ch struct device_node *np = dev->of_node; 4054a83eecfSAnilKumar Ch unsigned int *gpios; 4064a83eecfSAnilKumar Ch int i; 4074a83eecfSAnilKumar Ch 4084a83eecfSAnilKumar Ch if (!np) { 4094a83eecfSAnilKumar Ch dev_err(dev, "device lacks DT data\n"); 4104a83eecfSAnilKumar Ch return ERR_PTR(-ENODEV); 4114a83eecfSAnilKumar Ch } 4124a83eecfSAnilKumar Ch 4134a83eecfSAnilKumar Ch pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); 4144a83eecfSAnilKumar Ch if (!pdata) { 4154a83eecfSAnilKumar Ch dev_err(dev, "could not allocate memory for platform data\n"); 4164a83eecfSAnilKumar Ch return ERR_PTR(-ENOMEM); 4174a83eecfSAnilKumar Ch } 4184a83eecfSAnilKumar Ch 4194a83eecfSAnilKumar Ch pdata->num_row_gpios = of_gpio_named_count(np, "row-gpios"); 4204a83eecfSAnilKumar Ch pdata->num_col_gpios = of_gpio_named_count(np, "col-gpios"); 4214a83eecfSAnilKumar Ch if (!pdata->num_row_gpios || !pdata->num_col_gpios) { 4224a83eecfSAnilKumar Ch dev_err(dev, "number of keypad rows/columns not specified\n"); 4234a83eecfSAnilKumar Ch return ERR_PTR(-EINVAL); 4244a83eecfSAnilKumar Ch } 4254a83eecfSAnilKumar Ch 4264a83eecfSAnilKumar Ch if (of_get_property(np, "linux,no-autorepeat", NULL)) 4274a83eecfSAnilKumar Ch pdata->no_autorepeat = true; 4284a83eecfSAnilKumar Ch if (of_get_property(np, "linux,wakeup", NULL)) 4294a83eecfSAnilKumar Ch pdata->wakeup = true; 4304a83eecfSAnilKumar Ch if (of_get_property(np, "gpio-activelow", NULL)) 4314a83eecfSAnilKumar Ch pdata->active_low = true; 4324a83eecfSAnilKumar Ch 4334a83eecfSAnilKumar Ch of_property_read_u32(np, "debounce-delay-ms", &pdata->debounce_ms); 4344a83eecfSAnilKumar Ch of_property_read_u32(np, "col-scan-delay-us", 4354a83eecfSAnilKumar Ch &pdata->col_scan_delay_us); 4364a83eecfSAnilKumar Ch 4374a83eecfSAnilKumar Ch gpios = devm_kzalloc(dev, 4384a83eecfSAnilKumar Ch sizeof(unsigned int) * 4394a83eecfSAnilKumar Ch (pdata->num_row_gpios + pdata->num_col_gpios), 4404a83eecfSAnilKumar Ch GFP_KERNEL); 4414a83eecfSAnilKumar Ch if (!gpios) { 4424a83eecfSAnilKumar Ch dev_err(dev, "could not allocate memory for gpios\n"); 4434a83eecfSAnilKumar Ch return ERR_PTR(-ENOMEM); 4444a83eecfSAnilKumar Ch } 4454a83eecfSAnilKumar Ch 4464a83eecfSAnilKumar Ch for (i = 0; i < pdata->num_row_gpios; i++) 4474a83eecfSAnilKumar Ch gpios[i] = of_get_named_gpio(np, "row-gpios", i); 4484a83eecfSAnilKumar Ch 4494a83eecfSAnilKumar Ch for (i = 0; i < pdata->num_col_gpios; i++) 4504a83eecfSAnilKumar Ch gpios[pdata->num_row_gpios + i] = 4514a83eecfSAnilKumar Ch of_get_named_gpio(np, "col-gpios", i); 4524a83eecfSAnilKumar Ch 4534a83eecfSAnilKumar Ch pdata->row_gpios = gpios; 4544a83eecfSAnilKumar Ch pdata->col_gpios = &gpios[pdata->num_row_gpios]; 4554a83eecfSAnilKumar Ch 4564a83eecfSAnilKumar Ch return pdata; 4574a83eecfSAnilKumar Ch } 4584a83eecfSAnilKumar Ch #else 4594a83eecfSAnilKumar Ch static inline struct matrix_keypad_platform_data * 4604a83eecfSAnilKumar Ch matrix_keypad_parse_dt(struct device *dev) 4614a83eecfSAnilKumar Ch { 4624a83eecfSAnilKumar Ch dev_err(dev, "no platform data defined\n"); 4634a83eecfSAnilKumar Ch 4644a83eecfSAnilKumar Ch return ERR_PTR(-EINVAL); 4654a83eecfSAnilKumar Ch } 4664a83eecfSAnilKumar Ch #endif 4674a83eecfSAnilKumar Ch 468bab7614dSEric Miao static int __devinit matrix_keypad_probe(struct platform_device *pdev) 469bab7614dSEric Miao { 470bab7614dSEric Miao const struct matrix_keypad_platform_data *pdata; 471bab7614dSEric Miao struct matrix_keypad *keypad; 472bab7614dSEric Miao struct input_dev *input_dev; 473bab7614dSEric Miao int err; 474bab7614dSEric Miao 4754a83eecfSAnilKumar Ch pdata = dev_get_platdata(&pdev->dev); 476bab7614dSEric Miao if (!pdata) { 4774a83eecfSAnilKumar Ch pdata = matrix_keypad_parse_dt(&pdev->dev); 4784a83eecfSAnilKumar Ch if (IS_ERR(pdata)) { 479bab7614dSEric Miao dev_err(&pdev->dev, "no platform data defined\n"); 4804a83eecfSAnilKumar Ch return PTR_ERR(pdata); 481bab7614dSEric Miao } 4824a83eecfSAnilKumar Ch } else if (!pdata->keymap_data) { 483bab7614dSEric Miao dev_err(&pdev->dev, "no keymap data defined\n"); 484bab7614dSEric Miao return -EINVAL; 485bab7614dSEric Miao } 486bab7614dSEric Miao 4874a83eecfSAnilKumar Ch keypad = kzalloc(sizeof(struct matrix_keypad), GFP_KERNEL); 488bab7614dSEric Miao input_dev = input_allocate_device(); 48901111fcdSDmitry Torokhov if (!keypad || !input_dev) { 490bab7614dSEric Miao err = -ENOMEM; 491bab7614dSEric Miao goto err_free_mem; 492bab7614dSEric Miao } 493bab7614dSEric Miao 494bab7614dSEric Miao keypad->input_dev = input_dev; 495bab7614dSEric Miao keypad->pdata = pdata; 4964a83eecfSAnilKumar Ch keypad->row_shift = get_count_order(pdata->num_col_gpios); 497bab7614dSEric Miao keypad->stopped = true; 498bab7614dSEric Miao INIT_DELAYED_WORK(&keypad->work, matrix_keypad_scan); 499bab7614dSEric Miao spin_lock_init(&keypad->lock); 500bab7614dSEric Miao 501bab7614dSEric Miao input_dev->name = pdev->name; 502bab7614dSEric Miao input_dev->id.bustype = BUS_HOST; 503bab7614dSEric Miao input_dev->dev.parent = &pdev->dev; 504bab7614dSEric Miao input_dev->open = matrix_keypad_start; 505bab7614dSEric Miao input_dev->close = matrix_keypad_stop; 506bab7614dSEric Miao 5074a83eecfSAnilKumar Ch err = matrix_keypad_build_keymap(pdata->keymap_data, NULL, 5081932811fSDmitry Torokhov pdata->num_row_gpios, 5091932811fSDmitry Torokhov pdata->num_col_gpios, 5104a83eecfSAnilKumar Ch NULL, input_dev); 5114a83eecfSAnilKumar Ch if (err) { 5124a83eecfSAnilKumar Ch dev_err(&pdev->dev, "failed to build keymap\n"); 5131932811fSDmitry Torokhov goto err_free_mem; 5144a83eecfSAnilKumar Ch } 515bab7614dSEric Miao 5161932811fSDmitry Torokhov if (!pdata->no_autorepeat) 5171932811fSDmitry Torokhov __set_bit(EV_REP, input_dev->evbit); 518bab7614dSEric Miao input_set_capability(input_dev, EV_MSC, MSC_SCAN); 519bab7614dSEric Miao input_set_drvdata(input_dev, keypad); 520bab7614dSEric Miao 521b83643ebSDmitry Torokhov err = matrix_keypad_init_gpio(pdev, keypad); 522bab7614dSEric Miao if (err) 523bab7614dSEric Miao goto err_free_mem; 524bab7614dSEric Miao 525bab7614dSEric Miao err = input_register_device(keypad->input_dev); 526bab7614dSEric Miao if (err) 527b83643ebSDmitry Torokhov goto err_free_gpio; 528bab7614dSEric Miao 529bab7614dSEric Miao device_init_wakeup(&pdev->dev, pdata->wakeup); 530bab7614dSEric Miao platform_set_drvdata(pdev, keypad); 531bab7614dSEric Miao 532bab7614dSEric Miao return 0; 533bab7614dSEric Miao 534b83643ebSDmitry Torokhov err_free_gpio: 535b83643ebSDmitry Torokhov matrix_keypad_free_gpio(keypad); 536bab7614dSEric Miao err_free_mem: 537bab7614dSEric Miao input_free_device(input_dev); 538bab7614dSEric Miao kfree(keypad); 539bab7614dSEric Miao return err; 540bab7614dSEric Miao } 541bab7614dSEric Miao 542bab7614dSEric Miao static int __devexit matrix_keypad_remove(struct platform_device *pdev) 543bab7614dSEric Miao { 544bab7614dSEric Miao struct matrix_keypad *keypad = platform_get_drvdata(pdev); 545bab7614dSEric Miao 546bab7614dSEric Miao device_init_wakeup(&pdev->dev, 0); 547bab7614dSEric Miao 548b83643ebSDmitry Torokhov matrix_keypad_free_gpio(keypad); 549bab7614dSEric Miao input_unregister_device(keypad->input_dev); 550bab7614dSEric Miao kfree(keypad); 551bab7614dSEric Miao 552b83643ebSDmitry Torokhov platform_set_drvdata(pdev, NULL); 553b83643ebSDmitry Torokhov 554bab7614dSEric Miao return 0; 555bab7614dSEric Miao } 556bab7614dSEric Miao 5574a83eecfSAnilKumar Ch #ifdef CONFIG_OF 5584a83eecfSAnilKumar Ch static const struct of_device_id matrix_keypad_dt_match[] = { 5594a83eecfSAnilKumar Ch { .compatible = "gpio-matrix-keypad" }, 5604a83eecfSAnilKumar Ch { } 5614a83eecfSAnilKumar Ch }; 5624a83eecfSAnilKumar Ch MODULE_DEVICE_TABLE(of, matrix_keypad_dt_match); 5634a83eecfSAnilKumar Ch #endif 5644a83eecfSAnilKumar Ch 565bab7614dSEric Miao static struct platform_driver matrix_keypad_driver = { 566bab7614dSEric Miao .probe = matrix_keypad_probe, 5671cb0aa88SBill Pemberton .remove = matrix_keypad_remove, 568bab7614dSEric Miao .driver = { 569bab7614dSEric Miao .name = "matrix-keypad", 570bab7614dSEric Miao .owner = THIS_MODULE, 571f72a28abSDmitry Torokhov .pm = &matrix_keypad_pm_ops, 5724a83eecfSAnilKumar Ch .of_match_table = of_match_ptr(matrix_keypad_dt_match), 573bab7614dSEric Miao }, 574bab7614dSEric Miao }; 5755146c84fSJJ Ding module_platform_driver(matrix_keypad_driver); 576bab7614dSEric Miao 577bab7614dSEric Miao MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); 578bab7614dSEric Miao MODULE_DESCRIPTION("GPIO Driven Matrix Keypad Driver"); 579bab7614dSEric Miao MODULE_LICENSE("GPL v2"); 580bab7614dSEric Miao MODULE_ALIAS("platform:matrix-keypad"); 581