1/* 2 * Copyright (c) 2008-2009 Nuvoton technology corporation. 3 * 4 * Wan ZongShun <mcuos.com@gmail.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation;version 2 of the License. 9 * 10 */ 11 12#include <linux/kernel.h> 13#include <linux/module.h> 14#include <linux/interrupt.h> 15#include <linux/input.h> 16#include <linux/device.h> 17#include <linux/platform_device.h> 18#include <linux/clk.h> 19#include <linux/err.h> 20#include <linux/io.h> 21#include <linux/slab.h> 22 23#include <linux/platform_data/keypad-w90p910.h> 24 25/* Keypad Interface Control Registers */ 26#define KPI_CONF 0x00 27#define KPI_3KCONF 0x04 28#define KPI_LPCONF 0x08 29#define KPI_STATUS 0x0C 30 31#define IS1KEY (0x01 << 16) 32#define INTTR (0x01 << 21) 33#define KEY0R (0x0f << 3) 34#define KEY0C 0x07 35#define DEBOUNCE_BIT 0x08 36#define KSIZE0 (0x01 << 16) 37#define KSIZE1 (0x01 << 17) 38#define KPSEL (0x01 << 19) 39#define ENKP (0x01 << 18) 40 41#define KGET_RAW(n) (((n) & KEY0R) >> 3) 42#define KGET_COLUMN(n) ((n) & KEY0C) 43 44#define W90P910_NUM_ROWS 8 45#define W90P910_NUM_COLS 8 46#define W90P910_ROW_SHIFT 3 47 48struct w90p910_keypad { 49 const struct w90p910_keypad_platform_data *pdata; 50 struct clk *clk; 51 struct input_dev *input_dev; 52 void __iomem *mmio_base; 53 int irq; 54 unsigned short keymap[W90P910_NUM_ROWS * W90P910_NUM_COLS]; 55}; 56 57static void w90p910_keypad_scan_matrix(struct w90p910_keypad *keypad, 58 unsigned int status) 59{ 60 struct input_dev *input_dev = keypad->input_dev; 61 unsigned int row = KGET_RAW(status); 62 unsigned int col = KGET_COLUMN(status); 63 unsigned int code = MATRIX_SCAN_CODE(row, col, W90P910_ROW_SHIFT); 64 unsigned int key = keypad->keymap[code]; 65 66 input_event(input_dev, EV_MSC, MSC_SCAN, code); 67 input_report_key(input_dev, key, 1); 68 input_sync(input_dev); 69 70 input_event(input_dev, EV_MSC, MSC_SCAN, code); 71 input_report_key(input_dev, key, 0); 72 input_sync(input_dev); 73} 74 75static irqreturn_t w90p910_keypad_irq_handler(int irq, void *dev_id) 76{ 77 struct w90p910_keypad *keypad = dev_id; 78 unsigned int kstatus, val; 79 80 kstatus = __raw_readl(keypad->mmio_base + KPI_STATUS); 81 82 val = INTTR | IS1KEY; 83 84 if (kstatus & val) 85 w90p910_keypad_scan_matrix(keypad, kstatus); 86 87 return IRQ_HANDLED; 88} 89 90static int w90p910_keypad_open(struct input_dev *dev) 91{ 92 struct w90p910_keypad *keypad = input_get_drvdata(dev); 93 const struct w90p910_keypad_platform_data *pdata = keypad->pdata; 94 unsigned int val, config; 95 96 /* Enable unit clock */ 97 clk_enable(keypad->clk); 98 99 val = __raw_readl(keypad->mmio_base + KPI_CONF); 100 val |= (KPSEL | ENKP); 101 val &= ~(KSIZE0 | KSIZE1); 102 103 config = pdata->prescale | (pdata->debounce << DEBOUNCE_BIT); 104 105 val |= config; 106 107 __raw_writel(val, keypad->mmio_base + KPI_CONF); 108 109 return 0; 110} 111 112static void w90p910_keypad_close(struct input_dev *dev) 113{ 114 struct w90p910_keypad *keypad = input_get_drvdata(dev); 115 116 /* Disable clock unit */ 117 clk_disable(keypad->clk); 118} 119 120static int w90p910_keypad_probe(struct platform_device *pdev) 121{ 122 const struct w90p910_keypad_platform_data *pdata = 123 dev_get_platdata(&pdev->dev); 124 const struct matrix_keymap_data *keymap_data; 125 struct w90p910_keypad *keypad; 126 struct input_dev *input_dev; 127 struct resource *res; 128 int irq; 129 int error; 130 131 if (!pdata) { 132 dev_err(&pdev->dev, "no platform data defined\n"); 133 return -EINVAL; 134 } 135 136 keymap_data = pdata->keymap_data; 137 138 irq = platform_get_irq(pdev, 0); 139 if (irq < 0) { 140 dev_err(&pdev->dev, "failed to get keypad irq\n"); 141 return -ENXIO; 142 } 143 144 keypad = kzalloc(sizeof(struct w90p910_keypad), GFP_KERNEL); 145 input_dev = input_allocate_device(); 146 if (!keypad || !input_dev) { 147 dev_err(&pdev->dev, "failed to allocate driver data\n"); 148 error = -ENOMEM; 149 goto failed_free; 150 } 151 152 keypad->pdata = pdata; 153 keypad->input_dev = input_dev; 154 keypad->irq = irq; 155 156 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 157 if (res == NULL) { 158 dev_err(&pdev->dev, "failed to get I/O memory\n"); 159 error = -ENXIO; 160 goto failed_free; 161 } 162 163 res = request_mem_region(res->start, resource_size(res), pdev->name); 164 if (res == NULL) { 165 dev_err(&pdev->dev, "failed to request I/O memory\n"); 166 error = -EBUSY; 167 goto failed_free; 168 } 169 170 keypad->mmio_base = ioremap(res->start, resource_size(res)); 171 if (keypad->mmio_base == NULL) { 172 dev_err(&pdev->dev, "failed to remap I/O memory\n"); 173 error = -ENXIO; 174 goto failed_free_res; 175 } 176 177 keypad->clk = clk_get(&pdev->dev, NULL); 178 if (IS_ERR(keypad->clk)) { 179 dev_err(&pdev->dev, "failed to get keypad clock\n"); 180 error = PTR_ERR(keypad->clk); 181 goto failed_free_io; 182 } 183 184 /* set multi-function pin for w90p910 kpi. */ 185 mfp_set_groupi(&pdev->dev); 186 187 input_dev->name = pdev->name; 188 input_dev->id.bustype = BUS_HOST; 189 input_dev->open = w90p910_keypad_open; 190 input_dev->close = w90p910_keypad_close; 191 input_dev->dev.parent = &pdev->dev; 192 193 error = matrix_keypad_build_keymap(keymap_data, NULL, 194 W90P910_NUM_ROWS, W90P910_NUM_COLS, 195 keypad->keymap, input_dev); 196 if (error) { 197 dev_err(&pdev->dev, "failed to build keymap\n"); 198 goto failed_put_clk; 199 } 200 201 error = request_irq(keypad->irq, w90p910_keypad_irq_handler, 202 0, pdev->name, keypad); 203 if (error) { 204 dev_err(&pdev->dev, "failed to request IRQ\n"); 205 goto failed_put_clk; 206 } 207 208 __set_bit(EV_REP, input_dev->evbit); 209 input_set_capability(input_dev, EV_MSC, MSC_SCAN); 210 input_set_drvdata(input_dev, keypad); 211 212 /* Register the input device */ 213 error = input_register_device(input_dev); 214 if (error) { 215 dev_err(&pdev->dev, "failed to register input device\n"); 216 goto failed_free_irq; 217 } 218 219 platform_set_drvdata(pdev, keypad); 220 return 0; 221 222failed_free_irq: 223 free_irq(irq, keypad); 224failed_put_clk: 225 clk_put(keypad->clk); 226failed_free_io: 227 iounmap(keypad->mmio_base); 228failed_free_res: 229 release_mem_region(res->start, resource_size(res)); 230failed_free: 231 input_free_device(input_dev); 232 kfree(keypad); 233 return error; 234} 235 236static int w90p910_keypad_remove(struct platform_device *pdev) 237{ 238 struct w90p910_keypad *keypad = platform_get_drvdata(pdev); 239 struct resource *res; 240 241 free_irq(keypad->irq, keypad); 242 243 clk_put(keypad->clk); 244 245 input_unregister_device(keypad->input_dev); 246 247 iounmap(keypad->mmio_base); 248 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 249 release_mem_region(res->start, resource_size(res)); 250 251 kfree(keypad); 252 253 return 0; 254} 255 256static struct platform_driver w90p910_keypad_driver = { 257 .probe = w90p910_keypad_probe, 258 .remove = w90p910_keypad_remove, 259 .driver = { 260 .name = "nuc900-kpi", 261 }, 262}; 263module_platform_driver(w90p910_keypad_driver); 264 265MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); 266MODULE_DESCRIPTION("w90p910 keypad driver"); 267MODULE_LICENSE("GPL"); 268MODULE_ALIAS("platform:nuc900-keypad"); 269