sh_keysc.c 8.2 KB
Newer Older
M
Magnus Damm 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
 * SuperH KEYSC Keypad Driver
 *
 * Copyright (C) 2008 Magnus Damm
 *
 * Based on gpio_keys.c, Copyright 2005 Phil Blundell
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
M
Magnus Damm 已提交
21
#include <linux/input/sh_keysc.h>
22
#include <linux/bitmap.h>
23
#include <linux/pm_runtime.h>
M
Magnus Damm 已提交
24
#include <linux/io.h>
25
#include <linux/slab.h>
M
Magnus Damm 已提交
26 27 28 29 30 31 32

static const struct {
	unsigned char kymd, keyout, keyin;
} sh_keysc_mode[] = {
	[SH_KEYSC_MODE_1] = { 0, 6, 5 },
	[SH_KEYSC_MODE_2] = { 1, 5, 6 },
	[SH_KEYSC_MODE_3] = { 2, 4, 7 },
33 34
	[SH_KEYSC_MODE_4] = { 3, 6, 6 },
	[SH_KEYSC_MODE_5] = { 4, 6, 7 },
35
	[SH_KEYSC_MODE_6] = { 5, 7, 7 },
M
Magnus Damm 已提交
36 37 38 39
};

struct sh_keysc_priv {
	void __iomem *iomem_base;
40
	DECLARE_BITMAP(last_keys, SH_KEYSC_MAXKEYS);
M
Magnus Damm 已提交
41 42 43 44
	struct input_dev *input;
	struct sh_keysc_info pdata;
};

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
#define KYCR1 0
#define KYCR2 1
#define KYINDR 2
#define KYOUTDR 3

#define KYCR2_IRQ_LEVEL    0x10
#define KYCR2_IRQ_DISABLED 0x00

static unsigned long sh_keysc_read(struct sh_keysc_priv *p, int reg_nr)
{
	return ioread16(p->iomem_base + (reg_nr << 2));
}

static void sh_keysc_write(struct sh_keysc_priv *p, int reg_nr,
			   unsigned long value)
{
	iowrite16(value, p->iomem_base + (reg_nr << 2));
}

static void sh_keysc_level_mode(struct sh_keysc_priv *p,
				unsigned long keys_set)
{
	struct sh_keysc_info *pdata = &p->pdata;

	sh_keysc_write(p, KYOUTDR, 0);
	sh_keysc_write(p, KYCR2, KYCR2_IRQ_LEVEL | (keys_set << 8));

	if (pdata->kycr2_delay)
		udelay(pdata->kycr2_delay);
}

76 77 78 79 80 81 82 83 84
static void sh_keysc_map_dbg(struct device *dev, unsigned long *map,
			     const char *str)
{
	int k;

	for (k = 0; k < BITS_TO_LONGS(SH_KEYSC_MAXKEYS); k++)
		dev_dbg(dev, "%s[%d] 0x%lx\n", str, k, map[k]);
}

M
Magnus Damm 已提交
85 86 87 88 89
static irqreturn_t sh_keysc_isr(int irq, void *dev_id)
{
	struct platform_device *pdev = dev_id;
	struct sh_keysc_priv *priv = platform_get_drvdata(pdev);
	struct sh_keysc_info *pdata = &priv->pdata;
90 91 92 93 94
	int keyout_nr = sh_keysc_mode[pdata->mode].keyout;
	int keyin_nr = sh_keysc_mode[pdata->mode].keyin;
	DECLARE_BITMAP(keys, SH_KEYSC_MAXKEYS);
	DECLARE_BITMAP(keys0, SH_KEYSC_MAXKEYS);
	DECLARE_BITMAP(keys1, SH_KEYSC_MAXKEYS);
M
Magnus Damm 已提交
95
	unsigned char keyin_set, tmp;
96
	int i, k, n;
M
Magnus Damm 已提交
97 98 99

	dev_dbg(&pdev->dev, "isr!\n");

100 101
	bitmap_fill(keys1, SH_KEYSC_MAXKEYS);
	bitmap_zero(keys0, SH_KEYSC_MAXKEYS);
M
Magnus Damm 已提交
102 103

	do {
104
		bitmap_zero(keys, SH_KEYSC_MAXKEYS);
M
Magnus Damm 已提交
105 106
		keyin_set = 0;

107
		sh_keysc_write(priv, KYCR2, KYCR2_IRQ_DISABLED);
M
Magnus Damm 已提交
108

109 110 111 112
		for (i = 0; i < keyout_nr; i++) {
			n = keyin_nr * i;

			/* drive one KEYOUT pin low, read KEYIN pins */
113
			sh_keysc_write(priv, KYOUTDR, 0xffff ^ (3 << (i * 2)));
M
Magnus Damm 已提交
114
			udelay(pdata->delay);
115 116
			tmp = sh_keysc_read(priv, KYINDR);

117 118 119 120 121 122 123 124
			/* set bit if key press has been detected */
			for (k = 0; k < keyin_nr; k++) {
				if (tmp & (1 << k))
					__set_bit(n + k, keys);
			}

			/* keep track of which KEYIN bits that have been set */
			keyin_set |= tmp ^ ((1 << keyin_nr) - 1);
M
Magnus Damm 已提交
125 126
		}

127
		sh_keysc_level_mode(priv, keyin_set);
128

129 130 131
		bitmap_complement(keys, keys, SH_KEYSC_MAXKEYS);
		bitmap_and(keys1, keys1, keys, SH_KEYSC_MAXKEYS);
		bitmap_or(keys0, keys0, keys, SH_KEYSC_MAXKEYS);
M
Magnus Damm 已提交
132

133
		sh_keysc_map_dbg(&pdev->dev, keys, "keys");
M
Magnus Damm 已提交
134

135
	} while (sh_keysc_read(priv, KYCR2) & 0x01);
M
Magnus Damm 已提交
136

137 138 139
	sh_keysc_map_dbg(&pdev->dev, priv->last_keys, "last_keys");
	sh_keysc_map_dbg(&pdev->dev, keys0, "keys0");
	sh_keysc_map_dbg(&pdev->dev, keys1, "keys1");
M
Magnus Damm 已提交
140 141 142 143 144 145

	for (i = 0; i < SH_KEYSC_MAXKEYS; i++) {
		k = pdata->keycodes[i];
		if (!k)
			continue;

146
		if (test_bit(i, keys0) == test_bit(i, priv->last_keys))
M
Magnus Damm 已提交
147 148
			continue;

149
		if (test_bit(i, keys1) || test_bit(i, keys0)) {
M
Magnus Damm 已提交
150
			input_event(priv->input, EV_KEY, k, 1);
151
			__set_bit(i, priv->last_keys);
M
Magnus Damm 已提交
152 153
		}

154
		if (!test_bit(i, keys1)) {
M
Magnus Damm 已提交
155
			input_event(priv->input, EV_KEY, k, 0);
156
			__clear_bit(i, priv->last_keys);
M
Magnus Damm 已提交
157 158 159 160 161 162 163 164 165 166 167 168 169 170
		}

	}
	input_sync(priv->input);

	return IRQ_HANDLED;
}

static int __devinit sh_keysc_probe(struct platform_device *pdev)
{
	struct sh_keysc_priv *priv;
	struct sh_keysc_info *pdata;
	struct resource *res;
	struct input_dev *input;
171
	int i;
M
Magnus Damm 已提交
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
	int irq, error;

	if (!pdev->dev.platform_data) {
		dev_err(&pdev->dev, "no platform data defined\n");
		error = -EINVAL;
		goto err0;
	}

	error = -ENXIO;
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (res == NULL) {
		dev_err(&pdev->dev, "failed to get I/O memory\n");
		goto err0;
	}

	irq = platform_get_irq(pdev, 0);
	if (irq < 0) {
		dev_err(&pdev->dev, "failed to get irq\n");
		goto err0;
	}

	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
	if (priv == NULL) {
		dev_err(&pdev->dev, "failed to allocate driver data\n");
		error = -ENOMEM;
		goto err0;
	}

	platform_set_drvdata(pdev, priv);
	memcpy(&priv->pdata, pdev->dev.platform_data, sizeof(priv->pdata));
	pdata = &priv->pdata;

204
	priv->iomem_base = ioremap_nocache(res->start, resource_size(res));
M
Magnus Damm 已提交
205 206 207
	if (priv->iomem_base == NULL) {
		dev_err(&pdev->dev, "failed to remap I/O memory\n");
		error = -ENXIO;
208
		goto err1;
M
Magnus Damm 已提交
209 210 211 212 213 214
	}

	priv->input = input_allocate_device();
	if (!priv->input) {
		dev_err(&pdev->dev, "failed to allocate input device\n");
		error = -ENOMEM;
215
		goto err2;
M
Magnus Damm 已提交
216 217 218 219 220 221 222 223 224 225 226 227 228 229
	}

	input = priv->input;
	input->evbit[0] = BIT_MASK(EV_KEY);

	input->name = pdev->name;
	input->phys = "sh-keysc-keys/input0";
	input->dev.parent = &pdev->dev;

	input->id.bustype = BUS_HOST;
	input->id.vendor = 0x0001;
	input->id.product = 0x0001;
	input->id.version = 0x0100;

230 231 232 233
	input->keycode = pdata->keycodes;
	input->keycodesize = sizeof(pdata->keycodes[0]);
	input->keycodemax = ARRAY_SIZE(pdata->keycodes);

M
Magnus Damm 已提交
234 235 236
	error = request_irq(irq, sh_keysc_isr, 0, pdev->name, pdev);
	if (error) {
		dev_err(&pdev->dev, "failed to request IRQ\n");
237
		goto err3;
M
Magnus Damm 已提交
238 239
	}

240 241 242
	for (i = 0; i < SH_KEYSC_MAXKEYS; i++)
		__set_bit(pdata->keycodes[i], input->keybit);
	__clear_bit(KEY_RESERVED, input->keybit);
M
Magnus Damm 已提交
243 244 245 246

	error = input_register_device(input);
	if (error) {
		dev_err(&pdev->dev, "failed to register input device\n");
247
		goto err4;
M
Magnus Damm 已提交
248 249
	}

250 251
	pm_runtime_enable(&pdev->dev);
	pm_runtime_get_sync(&pdev->dev);
252

253 254 255
	sh_keysc_write(priv, KYCR1, (sh_keysc_mode[pdata->mode].kymd << 8) |
		       pdata->scan_timing);
	sh_keysc_level_mode(priv, 0);
256 257

	device_init_wakeup(&pdev->dev, 1);
258

M
Magnus Damm 已提交
259
	return 0;
260

261
 err4:
262
	free_irq(irq, pdev);
263
 err3:
264
	input_free_device(input);
M
Magnus Damm 已提交
265
 err2:
266
	iounmap(priv->iomem_base);
M
Magnus Damm 已提交
267 268 269 270 271 272 273 274 275 276 277
 err1:
	platform_set_drvdata(pdev, NULL);
	kfree(priv);
 err0:
	return error;
}

static int __devexit sh_keysc_remove(struct platform_device *pdev)
{
	struct sh_keysc_priv *priv = platform_get_drvdata(pdev);

278
	sh_keysc_write(priv, KYCR2, KYCR2_IRQ_DISABLED);
M
Magnus Damm 已提交
279 280 281 282 283

	input_unregister_device(priv->input);
	free_irq(platform_get_irq(pdev, 0), pdev);
	iounmap(priv->iomem_base);

284 285
	pm_runtime_put_sync(&pdev->dev);
	pm_runtime_disable(&pdev->dev);
286

M
Magnus Damm 已提交
287 288
	platform_set_drvdata(pdev, NULL);
	kfree(priv);
289

M
Magnus Damm 已提交
290 291 292
	return 0;
}

293 294
static int sh_keysc_suspend(struct device *dev)
{
295 296
	struct platform_device *pdev = to_platform_device(dev);
	struct sh_keysc_priv *priv = platform_get_drvdata(pdev);
297
	int irq = platform_get_irq(pdev, 0);
298 299
	unsigned short value;

300
	value = sh_keysc_read(priv, KYCR1);
M
Magnus Damm 已提交
301

302
	if (device_may_wakeup(dev)) {
303
		sh_keysc_write(priv, KYCR1, value | 0x80);
304
		enable_irq_wake(irq);
305
	} else {
306 307
		sh_keysc_write(priv, KYCR1, value & ~0x80);
		pm_runtime_put_sync(dev);
308
	}
309 310 311 312

	return 0;
}

313 314 315 316 317 318 319
static int sh_keysc_resume(struct device *dev)
{
	struct platform_device *pdev = to_platform_device(dev);
	int irq = platform_get_irq(pdev, 0);

	if (device_may_wakeup(dev))
		disable_irq_wake(irq);
320 321
	else
		pm_runtime_get_sync(dev);
322 323 324 325

	return 0;
}

326
static const struct dev_pm_ops sh_keysc_dev_pm_ops = {
327
	.suspend = sh_keysc_suspend,
328
	.resume = sh_keysc_resume,
329
};
M
Magnus Damm 已提交
330 331 332 333 334 335

struct platform_driver sh_keysc_device_driver = {
	.probe		= sh_keysc_probe,
	.remove		= __devexit_p(sh_keysc_remove),
	.driver		= {
		.name	= "sh_keysc",
336
		.pm	= &sh_keysc_dev_pm_ops,
M
Magnus Damm 已提交
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
	}
};

static int __init sh_keysc_init(void)
{
	return platform_driver_register(&sh_keysc_device_driver);
}

static void __exit sh_keysc_exit(void)
{
	platform_driver_unregister(&sh_keysc_device_driver);
}

module_init(sh_keysc_init);
module_exit(sh_keysc_exit);

MODULE_AUTHOR("Magnus Damm");
MODULE_DESCRIPTION("SuperH KEYSC Keypad Driver");
MODULE_LICENSE("GPL");