xref: /openbmc/linux/drivers/input/serio/altera_ps2.c (revision b34e08d5)
1 /*
2  * Altera University Program PS2 controller driver
3  *
4  * Copyright (C) 2008 Thomas Chou <thomas@wytron.com.tw>
5  *
6  * Based on sa1111ps2.c, which is:
7  * Copyright (C) 2002 Russell King
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License version 2 as
11  * published by the Free Software Foundation.
12  */
13 
14 #include <linux/module.h>
15 #include <linux/input.h>
16 #include <linux/serio.h>
17 #include <linux/interrupt.h>
18 #include <linux/platform_device.h>
19 #include <linux/io.h>
20 #include <linux/slab.h>
21 #include <linux/of.h>
22 
23 #define DRV_NAME "altera_ps2"
24 
25 struct ps2if {
26 	struct serio *io;
27 	struct resource *iomem_res;
28 	void __iomem *base;
29 	unsigned irq;
30 };
31 
32 /*
33  * Read all bytes waiting in the PS2 port.  There should be
34  * at the most one, but we loop for safety.
35  */
36 static irqreturn_t altera_ps2_rxint(int irq, void *dev_id)
37 {
38 	struct ps2if *ps2if = dev_id;
39 	unsigned int status;
40 	int handled = IRQ_NONE;
41 
42 	while ((status = readl(ps2if->base)) & 0xffff0000) {
43 		serio_interrupt(ps2if->io, status & 0xff, 0);
44 		handled = IRQ_HANDLED;
45 	}
46 
47 	return handled;
48 }
49 
50 /*
51  * Write a byte to the PS2 port.
52  */
53 static int altera_ps2_write(struct serio *io, unsigned char val)
54 {
55 	struct ps2if *ps2if = io->port_data;
56 
57 	writel(val, ps2if->base);
58 	return 0;
59 }
60 
61 static int altera_ps2_open(struct serio *io)
62 {
63 	struct ps2if *ps2if = io->port_data;
64 
65 	/* clear fifo */
66 	while (readl(ps2if->base) & 0xffff0000)
67 		/* empty */;
68 
69 	writel(1, ps2if->base + 4); /* enable rx irq */
70 	return 0;
71 }
72 
73 static void altera_ps2_close(struct serio *io)
74 {
75 	struct ps2if *ps2if = io->port_data;
76 
77 	writel(0, ps2if->base); /* disable rx irq */
78 }
79 
80 /*
81  * Add one device to this driver.
82  */
83 static int altera_ps2_probe(struct platform_device *pdev)
84 {
85 	struct ps2if *ps2if;
86 	struct serio *serio;
87 	int error, irq;
88 
89 	ps2if = kzalloc(sizeof(struct ps2if), GFP_KERNEL);
90 	serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
91 	if (!ps2if || !serio) {
92 		error = -ENOMEM;
93 		goto err_free_mem;
94 	}
95 
96 	serio->id.type		= SERIO_8042;
97 	serio->write		= altera_ps2_write;
98 	serio->open		= altera_ps2_open;
99 	serio->close		= altera_ps2_close;
100 	strlcpy(serio->name, dev_name(&pdev->dev), sizeof(serio->name));
101 	strlcpy(serio->phys, dev_name(&pdev->dev), sizeof(serio->phys));
102 	serio->port_data	= ps2if;
103 	serio->dev.parent	= &pdev->dev;
104 	ps2if->io		= serio;
105 
106 	ps2if->iomem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
107 	if (ps2if->iomem_res == NULL) {
108 		error = -ENOENT;
109 		goto err_free_mem;
110 	}
111 
112 
113 	irq = platform_get_irq(pdev, 0);
114 	if (irq < 0) {
115 		error = -ENXIO;
116 		goto err_free_mem;
117 	}
118 	ps2if->irq = irq;
119 
120 	if (!request_mem_region(ps2if->iomem_res->start,
121 				resource_size(ps2if->iomem_res), pdev->name)) {
122 		error = -EBUSY;
123 		goto err_free_mem;
124 	}
125 
126 	ps2if->base = ioremap(ps2if->iomem_res->start,
127 			      resource_size(ps2if->iomem_res));
128 	if (!ps2if->base) {
129 		error = -ENOMEM;
130 		goto err_free_res;
131 	}
132 
133 	error = request_irq(ps2if->irq, altera_ps2_rxint, 0, pdev->name, ps2if);
134 	if (error) {
135 		dev_err(&pdev->dev, "could not allocate IRQ %d: %d\n",
136 			ps2if->irq, error);
137 		goto err_unmap;
138 	}
139 
140 	dev_info(&pdev->dev, "base %p, irq %d\n", ps2if->base, ps2if->irq);
141 
142 	serio_register_port(ps2if->io);
143 	platform_set_drvdata(pdev, ps2if);
144 
145 	return 0;
146 
147  err_unmap:
148 	iounmap(ps2if->base);
149  err_free_res:
150 	release_mem_region(ps2if->iomem_res->start,
151 			   resource_size(ps2if->iomem_res));
152  err_free_mem:
153 	kfree(ps2if);
154 	kfree(serio);
155 	return error;
156 }
157 
158 /*
159  * Remove one device from this driver.
160  */
161 static int altera_ps2_remove(struct platform_device *pdev)
162 {
163 	struct ps2if *ps2if = platform_get_drvdata(pdev);
164 
165 	serio_unregister_port(ps2if->io);
166 	free_irq(ps2if->irq, ps2if);
167 	iounmap(ps2if->base);
168 	release_mem_region(ps2if->iomem_res->start,
169 			   resource_size(ps2if->iomem_res));
170 	kfree(ps2if);
171 
172 	return 0;
173 }
174 
175 #ifdef CONFIG_OF
176 static const struct of_device_id altera_ps2_match[] = {
177 	{ .compatible = "ALTR,ps2-1.0", },
178 	{ .compatible = "altr,ps2-1.0", },
179 	{},
180 };
181 MODULE_DEVICE_TABLE(of, altera_ps2_match);
182 #endif /* CONFIG_OF */
183 
184 /*
185  * Our device driver structure
186  */
187 static struct platform_driver altera_ps2_driver = {
188 	.probe		= altera_ps2_probe,
189 	.remove		= altera_ps2_remove,
190 	.driver	= {
191 		.name	= DRV_NAME,
192 		.owner	= THIS_MODULE,
193 		.of_match_table = of_match_ptr(altera_ps2_match),
194 	},
195 };
196 module_platform_driver(altera_ps2_driver);
197 
198 MODULE_DESCRIPTION("Altera University Program PS2 controller driver");
199 MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
200 MODULE_LICENSE("GPL");
201 MODULE_ALIAS("platform:" DRV_NAME);
202