xref: /openbmc/linux/drivers/input/keyboard/sh_keysc.c (revision 6f84981772535e670e4e2df051a672af229b6694)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2795e6bf3SMagnus Damm /*
3795e6bf3SMagnus Damm  * SuperH KEYSC Keypad Driver
4795e6bf3SMagnus Damm  *
5795e6bf3SMagnus Damm  * Copyright (C) 2008 Magnus Damm
6795e6bf3SMagnus Damm  *
7795e6bf3SMagnus Damm  * Based on gpio_keys.c, Copyright 2005 Phil Blundell
8795e6bf3SMagnus Damm  */
9795e6bf3SMagnus Damm 
10795e6bf3SMagnus Damm #include <linux/kernel.h>
11795e6bf3SMagnus Damm #include <linux/module.h>
12795e6bf3SMagnus Damm #include <linux/interrupt.h>
13795e6bf3SMagnus Damm #include <linux/irq.h>
14795e6bf3SMagnus Damm #include <linux/delay.h>
15795e6bf3SMagnus Damm #include <linux/platform_device.h>
16795e6bf3SMagnus Damm #include <linux/input.h>
17fc1d003dSMagnus Damm #include <linux/input/sh_keysc.h>
18324e5adeSMagnus Damm #include <linux/bitmap.h>
19b6d2a3e6SMagnus Damm #include <linux/pm_runtime.h>
20795e6bf3SMagnus Damm #include <linux/io.h>
215a0e3ad6STejun Heo #include <linux/slab.h>
22795e6bf3SMagnus Damm 
23795e6bf3SMagnus Damm static const struct {
24795e6bf3SMagnus Damm 	unsigned char kymd, keyout, keyin;
25795e6bf3SMagnus Damm } sh_keysc_mode[] = {
26795e6bf3SMagnus Damm 	[SH_KEYSC_MODE_1] = { 0, 6, 5 },
27795e6bf3SMagnus Damm 	[SH_KEYSC_MODE_2] = { 1, 5, 6 },
28795e6bf3SMagnus Damm 	[SH_KEYSC_MODE_3] = { 2, 4, 7 },
293bf12763SMagnus Damm 	[SH_KEYSC_MODE_4] = { 3, 6, 6 },
303bf12763SMagnus Damm 	[SH_KEYSC_MODE_5] = { 4, 6, 7 },
31cca23d0bSMagnus Damm 	[SH_KEYSC_MODE_6] = { 5, 8, 8 },
32795e6bf3SMagnus Damm };
33795e6bf3SMagnus Damm 
34795e6bf3SMagnus Damm struct sh_keysc_priv {
35795e6bf3SMagnus Damm 	void __iomem *iomem_base;
36324e5adeSMagnus Damm 	DECLARE_BITMAP(last_keys, SH_KEYSC_MAXKEYS);
37795e6bf3SMagnus Damm 	struct input_dev *input;
38795e6bf3SMagnus Damm 	struct sh_keysc_info pdata;
39795e6bf3SMagnus Damm };
40795e6bf3SMagnus Damm 
412b14a808SMagnus Damm #define KYCR1 0
422b14a808SMagnus Damm #define KYCR2 1
432b14a808SMagnus Damm #define KYINDR 2
442b14a808SMagnus Damm #define KYOUTDR 3
452b14a808SMagnus Damm 
462b14a808SMagnus Damm #define KYCR2_IRQ_LEVEL    0x10
472b14a808SMagnus Damm #define KYCR2_IRQ_DISABLED 0x00
482b14a808SMagnus Damm 
sh_keysc_read(struct sh_keysc_priv * p,int reg_nr)492b14a808SMagnus Damm static unsigned long sh_keysc_read(struct sh_keysc_priv *p, int reg_nr)
502b14a808SMagnus Damm {
512b14a808SMagnus Damm 	return ioread16(p->iomem_base + (reg_nr << 2));
522b14a808SMagnus Damm }
532b14a808SMagnus Damm 
sh_keysc_write(struct sh_keysc_priv * p,int reg_nr,unsigned long value)542b14a808SMagnus Damm static void sh_keysc_write(struct sh_keysc_priv *p, int reg_nr,
552b14a808SMagnus Damm 			   unsigned long value)
562b14a808SMagnus Damm {
572b14a808SMagnus Damm 	iowrite16(value, p->iomem_base + (reg_nr << 2));
582b14a808SMagnus Damm }
592b14a808SMagnus Damm 
sh_keysc_level_mode(struct sh_keysc_priv * p,unsigned long keys_set)602b14a808SMagnus Damm static void sh_keysc_level_mode(struct sh_keysc_priv *p,
612b14a808SMagnus Damm 				unsigned long keys_set)
622b14a808SMagnus Damm {
632b14a808SMagnus Damm 	struct sh_keysc_info *pdata = &p->pdata;
642b14a808SMagnus Damm 
652b14a808SMagnus Damm 	sh_keysc_write(p, KYOUTDR, 0);
662b14a808SMagnus Damm 	sh_keysc_write(p, KYCR2, KYCR2_IRQ_LEVEL | (keys_set << 8));
672b14a808SMagnus Damm 
682b14a808SMagnus Damm 	if (pdata->kycr2_delay)
692b14a808SMagnus Damm 		udelay(pdata->kycr2_delay);
702b14a808SMagnus Damm }
712b14a808SMagnus Damm 
sh_keysc_map_dbg(struct device * dev,unsigned long * map,const char * str)72324e5adeSMagnus Damm static void sh_keysc_map_dbg(struct device *dev, unsigned long *map,
73324e5adeSMagnus Damm 			     const char *str)
74324e5adeSMagnus Damm {
75324e5adeSMagnus Damm 	int k;
76324e5adeSMagnus Damm 
77324e5adeSMagnus Damm 	for (k = 0; k < BITS_TO_LONGS(SH_KEYSC_MAXKEYS); k++)
78324e5adeSMagnus Damm 		dev_dbg(dev, "%s[%d] 0x%lx\n", str, k, map[k]);
79324e5adeSMagnus Damm }
80324e5adeSMagnus Damm 
sh_keysc_isr(int irq,void * dev_id)81795e6bf3SMagnus Damm static irqreturn_t sh_keysc_isr(int irq, void *dev_id)
82795e6bf3SMagnus Damm {
83795e6bf3SMagnus Damm 	struct platform_device *pdev = dev_id;
84795e6bf3SMagnus Damm 	struct sh_keysc_priv *priv = platform_get_drvdata(pdev);
85795e6bf3SMagnus Damm 	struct sh_keysc_info *pdata = &priv->pdata;
86324e5adeSMagnus Damm 	int keyout_nr = sh_keysc_mode[pdata->mode].keyout;
87324e5adeSMagnus Damm 	int keyin_nr = sh_keysc_mode[pdata->mode].keyin;
88324e5adeSMagnus Damm 	DECLARE_BITMAP(keys, SH_KEYSC_MAXKEYS);
89324e5adeSMagnus Damm 	DECLARE_BITMAP(keys0, SH_KEYSC_MAXKEYS);
90324e5adeSMagnus Damm 	DECLARE_BITMAP(keys1, SH_KEYSC_MAXKEYS);
91795e6bf3SMagnus Damm 	unsigned char keyin_set, tmp;
92324e5adeSMagnus Damm 	int i, k, n;
93795e6bf3SMagnus Damm 
94795e6bf3SMagnus Damm 	dev_dbg(&pdev->dev, "isr!\n");
95795e6bf3SMagnus Damm 
96324e5adeSMagnus Damm 	bitmap_fill(keys1, SH_KEYSC_MAXKEYS);
97324e5adeSMagnus Damm 	bitmap_zero(keys0, SH_KEYSC_MAXKEYS);
98795e6bf3SMagnus Damm 
99795e6bf3SMagnus Damm 	do {
100324e5adeSMagnus Damm 		bitmap_zero(keys, SH_KEYSC_MAXKEYS);
101795e6bf3SMagnus Damm 		keyin_set = 0;
102795e6bf3SMagnus Damm 
1032b14a808SMagnus Damm 		sh_keysc_write(priv, KYCR2, KYCR2_IRQ_DISABLED);
104795e6bf3SMagnus Damm 
105324e5adeSMagnus Damm 		for (i = 0; i < keyout_nr; i++) {
106324e5adeSMagnus Damm 			n = keyin_nr * i;
107324e5adeSMagnus Damm 
108324e5adeSMagnus Damm 			/* drive one KEYOUT pin low, read KEYIN pins */
1098f8be243SMagnus Damm 			sh_keysc_write(priv, KYOUTDR, 0xffff ^ (3 << (i * 2)));
110795e6bf3SMagnus Damm 			udelay(pdata->delay);
1112b14a808SMagnus Damm 			tmp = sh_keysc_read(priv, KYINDR);
1122b14a808SMagnus Damm 
113324e5adeSMagnus Damm 			/* set bit if key press has been detected */
114324e5adeSMagnus Damm 			for (k = 0; k < keyin_nr; k++) {
115324e5adeSMagnus Damm 				if (tmp & (1 << k))
116324e5adeSMagnus Damm 					__set_bit(n + k, keys);
117324e5adeSMagnus Damm 			}
118324e5adeSMagnus Damm 
119324e5adeSMagnus Damm 			/* keep track of which KEYIN bits that have been set */
120324e5adeSMagnus Damm 			keyin_set |= tmp ^ ((1 << keyin_nr) - 1);
121795e6bf3SMagnus Damm 		}
122795e6bf3SMagnus Damm 
1232b14a808SMagnus Damm 		sh_keysc_level_mode(priv, keyin_set);
1241f85d381SKuninori Morimoto 
125324e5adeSMagnus Damm 		bitmap_complement(keys, keys, SH_KEYSC_MAXKEYS);
126324e5adeSMagnus Damm 		bitmap_and(keys1, keys1, keys, SH_KEYSC_MAXKEYS);
127324e5adeSMagnus Damm 		bitmap_or(keys0, keys0, keys, SH_KEYSC_MAXKEYS);
128795e6bf3SMagnus Damm 
129324e5adeSMagnus Damm 		sh_keysc_map_dbg(&pdev->dev, keys, "keys");
130795e6bf3SMagnus Damm 
1312b14a808SMagnus Damm 	} while (sh_keysc_read(priv, KYCR2) & 0x01);
132795e6bf3SMagnus Damm 
133324e5adeSMagnus Damm 	sh_keysc_map_dbg(&pdev->dev, priv->last_keys, "last_keys");
134324e5adeSMagnus Damm 	sh_keysc_map_dbg(&pdev->dev, keys0, "keys0");
135324e5adeSMagnus Damm 	sh_keysc_map_dbg(&pdev->dev, keys1, "keys1");
136795e6bf3SMagnus Damm 
137795e6bf3SMagnus Damm 	for (i = 0; i < SH_KEYSC_MAXKEYS; i++) {
138795e6bf3SMagnus Damm 		k = pdata->keycodes[i];
139795e6bf3SMagnus Damm 		if (!k)
140795e6bf3SMagnus Damm 			continue;
141795e6bf3SMagnus Damm 
142324e5adeSMagnus Damm 		if (test_bit(i, keys0) == test_bit(i, priv->last_keys))
143795e6bf3SMagnus Damm 			continue;
144795e6bf3SMagnus Damm 
145324e5adeSMagnus Damm 		if (test_bit(i, keys1) || test_bit(i, keys0)) {
146795e6bf3SMagnus Damm 			input_event(priv->input, EV_KEY, k, 1);
147324e5adeSMagnus Damm 			__set_bit(i, priv->last_keys);
148795e6bf3SMagnus Damm 		}
149795e6bf3SMagnus Damm 
150324e5adeSMagnus Damm 		if (!test_bit(i, keys1)) {
151795e6bf3SMagnus Damm 			input_event(priv->input, EV_KEY, k, 0);
152324e5adeSMagnus Damm 			__clear_bit(i, priv->last_keys);
153795e6bf3SMagnus Damm 		}
154795e6bf3SMagnus Damm 
155795e6bf3SMagnus Damm 	}
156795e6bf3SMagnus Damm 	input_sync(priv->input);
157795e6bf3SMagnus Damm 
158795e6bf3SMagnus Damm 	return IRQ_HANDLED;
159795e6bf3SMagnus Damm }
160795e6bf3SMagnus Damm 
sh_keysc_probe(struct platform_device * pdev)1615298cc4cSBill Pemberton static int sh_keysc_probe(struct platform_device *pdev)
162795e6bf3SMagnus Damm {
163795e6bf3SMagnus Damm 	struct sh_keysc_priv *priv;
164795e6bf3SMagnus Damm 	struct sh_keysc_info *pdata;
165795e6bf3SMagnus Damm 	struct resource *res;
166795e6bf3SMagnus Damm 	struct input_dev *input;
16724d01c06SDmitry Torokhov 	int i;
168795e6bf3SMagnus Damm 	int irq, error;
169795e6bf3SMagnus Damm 
170c838cb3dSJingoo Han 	if (!dev_get_platdata(&pdev->dev)) {
171795e6bf3SMagnus Damm 		dev_err(&pdev->dev, "no platform data defined\n");
172795e6bf3SMagnus Damm 		error = -EINVAL;
173795e6bf3SMagnus Damm 		goto err0;
174795e6bf3SMagnus Damm 	}
175795e6bf3SMagnus Damm 
176795e6bf3SMagnus Damm 	error = -ENXIO;
177795e6bf3SMagnus Damm 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
178795e6bf3SMagnus Damm 	if (res == NULL) {
179795e6bf3SMagnus Damm 		dev_err(&pdev->dev, "failed to get I/O memory\n");
180795e6bf3SMagnus Damm 		goto err0;
181795e6bf3SMagnus Damm 	}
182795e6bf3SMagnus Damm 
183795e6bf3SMagnus Damm 	irq = platform_get_irq(pdev, 0);
1840bec8b7eSStephen Boyd 	if (irq < 0)
185795e6bf3SMagnus Damm 		goto err0;
186795e6bf3SMagnus Damm 
187795e6bf3SMagnus Damm 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
188795e6bf3SMagnus Damm 	if (priv == NULL) {
189795e6bf3SMagnus Damm 		dev_err(&pdev->dev, "failed to allocate driver data\n");
190795e6bf3SMagnus Damm 		error = -ENOMEM;
191795e6bf3SMagnus Damm 		goto err0;
192795e6bf3SMagnus Damm 	}
193795e6bf3SMagnus Damm 
194795e6bf3SMagnus Damm 	platform_set_drvdata(pdev, priv);
195c838cb3dSJingoo Han 	memcpy(&priv->pdata, dev_get_platdata(&pdev->dev), sizeof(priv->pdata));
196795e6bf3SMagnus Damm 	pdata = &priv->pdata;
197795e6bf3SMagnus Damm 
1984bdc0d67SChristoph Hellwig 	priv->iomem_base = ioremap(res->start, resource_size(res));
199795e6bf3SMagnus Damm 	if (priv->iomem_base == NULL) {
200795e6bf3SMagnus Damm 		dev_err(&pdev->dev, "failed to remap I/O memory\n");
201795e6bf3SMagnus Damm 		error = -ENXIO;
202d3aa43a9STetsuya Mukawa 		goto err1;
203795e6bf3SMagnus Damm 	}
204795e6bf3SMagnus Damm 
205795e6bf3SMagnus Damm 	priv->input = input_allocate_device();
206795e6bf3SMagnus Damm 	if (!priv->input) {
207795e6bf3SMagnus Damm 		dev_err(&pdev->dev, "failed to allocate input device\n");
208795e6bf3SMagnus Damm 		error = -ENOMEM;
209b6d2a3e6SMagnus Damm 		goto err2;
210795e6bf3SMagnus Damm 	}
211795e6bf3SMagnus Damm 
212795e6bf3SMagnus Damm 	input = priv->input;
213795e6bf3SMagnus Damm 	input->evbit[0] = BIT_MASK(EV_KEY);
214795e6bf3SMagnus Damm 
215795e6bf3SMagnus Damm 	input->name = pdev->name;
216795e6bf3SMagnus Damm 	input->phys = "sh-keysc-keys/input0";
217795e6bf3SMagnus Damm 	input->dev.parent = &pdev->dev;
218795e6bf3SMagnus Damm 
219795e6bf3SMagnus Damm 	input->id.bustype = BUS_HOST;
220795e6bf3SMagnus Damm 	input->id.vendor = 0x0001;
221795e6bf3SMagnus Damm 	input->id.product = 0x0001;
222795e6bf3SMagnus Damm 	input->id.version = 0x0100;
223795e6bf3SMagnus Damm 
22424d01c06SDmitry Torokhov 	input->keycode = pdata->keycodes;
22524d01c06SDmitry Torokhov 	input->keycodesize = sizeof(pdata->keycodes[0]);
22624d01c06SDmitry Torokhov 	input->keycodemax = ARRAY_SIZE(pdata->keycodes);
22724d01c06SDmitry Torokhov 
2282dfb9a85SMagnus Damm 	error = request_threaded_irq(irq, NULL, sh_keysc_isr, IRQF_ONESHOT,
2292dfb9a85SMagnus Damm 				     dev_name(&pdev->dev), pdev);
230795e6bf3SMagnus Damm 	if (error) {
231795e6bf3SMagnus Damm 		dev_err(&pdev->dev, "failed to request IRQ\n");
232b6d2a3e6SMagnus Damm 		goto err3;
233795e6bf3SMagnus Damm 	}
234795e6bf3SMagnus Damm 
23524d01c06SDmitry Torokhov 	for (i = 0; i < SH_KEYSC_MAXKEYS; i++)
23624d01c06SDmitry Torokhov 		__set_bit(pdata->keycodes[i], input->keybit);
23724d01c06SDmitry Torokhov 	__clear_bit(KEY_RESERVED, input->keybit);
238795e6bf3SMagnus Damm 
239795e6bf3SMagnus Damm 	error = input_register_device(input);
240795e6bf3SMagnus Damm 	if (error) {
241795e6bf3SMagnus Damm 		dev_err(&pdev->dev, "failed to register input device\n");
242b6d2a3e6SMagnus Damm 		goto err4;
243795e6bf3SMagnus Damm 	}
244795e6bf3SMagnus Damm 
245b6d2a3e6SMagnus Damm 	pm_runtime_enable(&pdev->dev);
246b6d2a3e6SMagnus Damm 	pm_runtime_get_sync(&pdev->dev);
247090d951bSMagnus Damm 
2482b14a808SMagnus Damm 	sh_keysc_write(priv, KYCR1, (sh_keysc_mode[pdata->mode].kymd << 8) |
2492b14a808SMagnus Damm 		       pdata->scan_timing);
2502b14a808SMagnus Damm 	sh_keysc_level_mode(priv, 0);
251a29b99ecSMagnus Damm 
252a29b99ecSMagnus Damm 	device_init_wakeup(&pdev->dev, 1);
25324d01c06SDmitry Torokhov 
254795e6bf3SMagnus Damm 	return 0;
25524d01c06SDmitry Torokhov 
256090d951bSMagnus Damm  err4:
257b6d2a3e6SMagnus Damm 	free_irq(irq, pdev);
258090d951bSMagnus Damm  err3:
259b6d2a3e6SMagnus Damm 	input_free_device(input);
260795e6bf3SMagnus Damm  err2:
261d3aa43a9STetsuya Mukawa 	iounmap(priv->iomem_base);
262795e6bf3SMagnus Damm  err1:
263795e6bf3SMagnus Damm 	kfree(priv);
264795e6bf3SMagnus Damm  err0:
265795e6bf3SMagnus Damm 	return error;
266795e6bf3SMagnus Damm }
267795e6bf3SMagnus Damm 
sh_keysc_remove(struct platform_device * pdev)268e2619cf7SBill Pemberton static int sh_keysc_remove(struct platform_device *pdev)
269795e6bf3SMagnus Damm {
270795e6bf3SMagnus Damm 	struct sh_keysc_priv *priv = platform_get_drvdata(pdev);
271795e6bf3SMagnus Damm 
2722b14a808SMagnus Damm 	sh_keysc_write(priv, KYCR2, KYCR2_IRQ_DISABLED);
273795e6bf3SMagnus Damm 
274795e6bf3SMagnus Damm 	input_unregister_device(priv->input);
275795e6bf3SMagnus Damm 	free_irq(platform_get_irq(pdev, 0), pdev);
276795e6bf3SMagnus Damm 	iounmap(priv->iomem_base);
277795e6bf3SMagnus Damm 
278b6d2a3e6SMagnus Damm 	pm_runtime_put_sync(&pdev->dev);
279b6d2a3e6SMagnus Damm 	pm_runtime_disable(&pdev->dev);
280090d951bSMagnus Damm 
281795e6bf3SMagnus Damm 	kfree(priv);
28224d01c06SDmitry Torokhov 
283795e6bf3SMagnus Damm 	return 0;
284795e6bf3SMagnus Damm }
285795e6bf3SMagnus Damm 
sh_keysc_suspend(struct device * dev)286a29b99ecSMagnus Damm static int sh_keysc_suspend(struct device *dev)
287a29b99ecSMagnus Damm {
28849976927SMagnus Damm 	struct platform_device *pdev = to_platform_device(dev);
28949976927SMagnus Damm 	struct sh_keysc_priv *priv = platform_get_drvdata(pdev);
2904ba50df6SMagnus Damm 	int irq = platform_get_irq(pdev, 0);
291a29b99ecSMagnus Damm 	unsigned short value;
292795e6bf3SMagnus Damm 
2932b14a808SMagnus Damm 	value = sh_keysc_read(priv, KYCR1);
294a29b99ecSMagnus Damm 
2954ba50df6SMagnus Damm 	if (device_may_wakeup(dev)) {
296b6d2a3e6SMagnus Damm 		sh_keysc_write(priv, KYCR1, value | 0x80);
2974ba50df6SMagnus Damm 		enable_irq_wake(irq);
29824d01c06SDmitry Torokhov 	} else {
299b6d2a3e6SMagnus Damm 		sh_keysc_write(priv, KYCR1, value & ~0x80);
300b6d2a3e6SMagnus Damm 		pm_runtime_put_sync(dev);
30124d01c06SDmitry Torokhov 	}
302a29b99ecSMagnus Damm 
303a29b99ecSMagnus Damm 	return 0;
304a29b99ecSMagnus Damm }
305a29b99ecSMagnus Damm 
sh_keysc_resume(struct device * dev)3064ba50df6SMagnus Damm static int sh_keysc_resume(struct device *dev)
3074ba50df6SMagnus Damm {
3084ba50df6SMagnus Damm 	struct platform_device *pdev = to_platform_device(dev);
3094ba50df6SMagnus Damm 	int irq = platform_get_irq(pdev, 0);
3104ba50df6SMagnus Damm 
3114ba50df6SMagnus Damm 	if (device_may_wakeup(dev))
3124ba50df6SMagnus Damm 		disable_irq_wake(irq);
313b6d2a3e6SMagnus Damm 	else
314b6d2a3e6SMagnus Damm 		pm_runtime_get_sync(dev);
3154ba50df6SMagnus Damm 
3164ba50df6SMagnus Damm 	return 0;
3174ba50df6SMagnus Damm }
3184ba50df6SMagnus Damm 
319*aebc2380SJonathan Cameron static DEFINE_SIMPLE_DEV_PM_OPS(sh_keysc_dev_pm_ops,
3209cc7c80bSDmitry Torokhov 				sh_keysc_suspend, sh_keysc_resume);
321795e6bf3SMagnus Damm 
32212d0cef7SDmitry Torokhov static struct platform_driver sh_keysc_device_driver = {
323795e6bf3SMagnus Damm 	.probe		= sh_keysc_probe,
3241cb0aa88SBill Pemberton 	.remove		= sh_keysc_remove,
325795e6bf3SMagnus Damm 	.driver		= {
326795e6bf3SMagnus Damm 		.name	= "sh_keysc",
327*aebc2380SJonathan Cameron 		.pm	= pm_sleep_ptr(&sh_keysc_dev_pm_ops),
328795e6bf3SMagnus Damm 	}
329795e6bf3SMagnus Damm };
3305146c84fSJJ Ding module_platform_driver(sh_keysc_device_driver);
331795e6bf3SMagnus Damm 
332795e6bf3SMagnus Damm MODULE_AUTHOR("Magnus Damm");
333795e6bf3SMagnus Damm MODULE_DESCRIPTION("SuperH KEYSC Keypad Driver");
334795e6bf3SMagnus Damm MODULE_LICENSE("GPL");
335