sh_keysc.c 8.3 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/clk.h>
M
Magnus Damm 已提交
24 25 26 27 28 29 30 31
#include <linux/io.h>

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 },
32 33
	[SH_KEYSC_MODE_4] = { 3, 6, 6 },
	[SH_KEYSC_MODE_5] = { 4, 6, 7 },
M
Magnus Damm 已提交
34 35 36 37
};

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

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
#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);
}

75 76 77 78 79 80 81 82 83
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 已提交
84 85 86 87 88
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;
89 90 91 92 93
	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 已提交
94
	unsigned char keyin_set, tmp;
95
	int i, k, n;
M
Magnus Damm 已提交
96 97 98

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

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

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

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

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

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

116 117 118 119 120 121 122 123
			/* 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 已提交
124 125
		}

126
		sh_keysc_level_mode(priv, keyin_set);
127

128 129 130
		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 已提交
131

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

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

136 137 138
	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 已提交
139 140 141 142 143 144

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

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

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

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

	}
	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;
170
	char clk_name[8];
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 215 216 217 218
	snprintf(clk_name, sizeof(clk_name), "keysc%d", pdev->id);
	priv->clk = clk_get(&pdev->dev, clk_name);
	if (IS_ERR(priv->clk)) {
		dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
		error = PTR_ERR(priv->clk);
		goto err2;
	}

M
Magnus Damm 已提交
219 220 221 222
	priv->input = input_allocate_device();
	if (!priv->input) {
		dev_err(&pdev->dev, "failed to allocate input device\n");
		error = -ENOMEM;
223
		goto err3;
M
Magnus Damm 已提交
224 225 226 227 228 229 230 231 232 233 234 235 236 237
	}

	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;

238 239 240 241
	input->keycode = pdata->keycodes;
	input->keycodesize = sizeof(pdata->keycodes[0]);
	input->keycodemax = ARRAY_SIZE(pdata->keycodes);

M
Magnus Damm 已提交
242 243 244
	error = request_irq(irq, sh_keysc_isr, 0, pdev->name, pdev);
	if (error) {
		dev_err(&pdev->dev, "failed to request IRQ\n");
245
		goto err4;
M
Magnus Damm 已提交
246 247
	}

248 249 250
	for (i = 0; i < SH_KEYSC_MAXKEYS; i++)
		__set_bit(pdata->keycodes[i], input->keybit);
	__clear_bit(KEY_RESERVED, input->keybit);
M
Magnus Damm 已提交
251 252 253 254

	error = input_register_device(input);
	if (error) {
		dev_err(&pdev->dev, "failed to register input device\n");
255
		goto err5;
M
Magnus Damm 已提交
256 257
	}

258 259
	clk_enable(priv->clk);

260 261 262
	sh_keysc_write(priv, KYCR1, (sh_keysc_mode[pdata->mode].kymd << 8) |
		       pdata->scan_timing);
	sh_keysc_level_mode(priv, 0);
263 264

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

M
Magnus Damm 已提交
266
	return 0;
267

268
 err5:
269
	free_irq(irq, pdev);
270
 err4:
271
	input_free_device(input);
272 273
 err3:
	clk_put(priv->clk);
M
Magnus Damm 已提交
274
 err2:
275
	iounmap(priv->iomem_base);
M
Magnus Damm 已提交
276 277 278 279 280 281 282 283 284 285 286
 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);

287
	sh_keysc_write(priv, KYCR2, KYCR2_IRQ_DISABLED);
M
Magnus Damm 已提交
288 289 290 291 292

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

293 294 295
	clk_disable(priv->clk);
	clk_put(priv->clk);

M
Magnus Damm 已提交
296 297
	platform_set_drvdata(pdev, NULL);
	kfree(priv);
298

M
Magnus Damm 已提交
299 300 301
	return 0;
}

302 303
static int sh_keysc_suspend(struct device *dev)
{
304 305
	struct platform_device *pdev = to_platform_device(dev);
	struct sh_keysc_priv *priv = platform_get_drvdata(pdev);
306
	int irq = platform_get_irq(pdev, 0);
307 308
	unsigned short value;

309
	value = sh_keysc_read(priv, KYCR1);
M
Magnus Damm 已提交
310

311
	if (device_may_wakeup(dev)) {
312
		value |= 0x80;
313
		enable_irq_wake(irq);
314
	} else {
315
		value &= ~0x80;
316
	}
317

318
	sh_keysc_write(priv, KYCR1, value);
319

320 321 322
	return 0;
}

323 324 325 326 327 328 329 330 331 332 333
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);

	return 0;
}

334
static const struct dev_pm_ops sh_keysc_dev_pm_ops = {
335
	.suspend = sh_keysc_suspend,
336
	.resume = sh_keysc_resume,
337
};
M
Magnus Damm 已提交
338 339 340 341 342 343

struct platform_driver sh_keysc_device_driver = {
	.probe		= sh_keysc_probe,
	.remove		= __devexit_p(sh_keysc_remove),
	.driver		= {
		.name	= "sh_keysc",
344
		.pm	= &sh_keysc_dev_pm_ops,
M
Magnus Damm 已提交
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
	}
};

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");