forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Input: add driver for Altera PS/2 controller
This patch adds a new SERIO driver to support the Altera University Program PS/2 controller. [[email protected]: assorted cleanups] Signed-off-by: Thomas Chou <[email protected]> Signed-off-by: Dmitry Torokhov <[email protected]>
- Loading branch information
Showing
3 changed files
with
209 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
/* | ||
* Altera University Program PS2 controller driver | ||
* | ||
* Copyright (C) 2008 Thomas Chou <[email protected]> | ||
* | ||
* Based on sa1111ps2.c, which is: | ||
* Copyright (C) 2002 Russell King | ||
* | ||
* 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/module.h> | ||
#include <linux/init.h> | ||
#include <linux/input.h> | ||
#include <linux/serio.h> | ||
#include <linux/interrupt.h> | ||
#include <linux/platform_device.h> | ||
#include <linux/io.h> | ||
|
||
#define DRV_NAME "altera_ps2" | ||
|
||
struct ps2if { | ||
struct serio *io; | ||
struct resource *iomem_res; | ||
void __iomem *base; | ||
unsigned irq; | ||
}; | ||
|
||
/* | ||
* Read all bytes waiting in the PS2 port. There should be | ||
* at the most one, but we loop for safety. | ||
*/ | ||
static irqreturn_t altera_ps2_rxint(int irq, void *dev_id) | ||
{ | ||
struct ps2if *ps2if = dev_id; | ||
unsigned int status; | ||
int handled = IRQ_NONE; | ||
|
||
while ((status = readl(ps2if->base)) & 0xffff0000) { | ||
serio_interrupt(ps2if->io, status & 0xff, 0); | ||
handled = IRQ_HANDLED; | ||
} | ||
|
||
return handled; | ||
} | ||
|
||
/* | ||
* Write a byte to the PS2 port. | ||
*/ | ||
static int altera_ps2_write(struct serio *io, unsigned char val) | ||
{ | ||
struct ps2if *ps2if = io->port_data; | ||
|
||
writel(val, ps2if->base); | ||
return 0; | ||
} | ||
|
||
static int altera_ps2_open(struct serio *io) | ||
{ | ||
struct ps2if *ps2if = io->port_data; | ||
|
||
/* clear fifo */ | ||
while (readl(ps2if->base) & 0xffff0000) | ||
/* empty */; | ||
|
||
writel(1, ps2if->base + 4); /* enable rx irq */ | ||
return 0; | ||
} | ||
|
||
static void altera_ps2_close(struct serio *io) | ||
{ | ||
struct ps2if *ps2if = io->port_data; | ||
|
||
writel(0, ps2if->base); /* disable rx irq */ | ||
} | ||
|
||
/* | ||
* Add one device to this driver. | ||
*/ | ||
static int altera_ps2_probe(struct platform_device *pdev) | ||
{ | ||
struct ps2if *ps2if; | ||
struct serio *serio; | ||
int error; | ||
|
||
ps2if = kzalloc(sizeof(struct ps2if), GFP_KERNEL); | ||
serio = kzalloc(sizeof(struct serio), GFP_KERNEL); | ||
if (!ps2if || !serio) { | ||
error = -ENOMEM; | ||
goto err_free_mem; | ||
} | ||
|
||
serio->id.type = SERIO_8042; | ||
serio->write = altera_ps2_write; | ||
serio->open = altera_ps2_open; | ||
serio->close = altera_ps2_close; | ||
strlcpy(serio->name, dev_name(&pdev->dev), sizeof(serio->name)); | ||
strlcpy(serio->phys, dev_name(&pdev->dev), sizeof(serio->phys)); | ||
serio->port_data = ps2if; | ||
serio->dev.parent = &pdev->dev; | ||
ps2if->io = serio; | ||
|
||
ps2if->iomem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
if (ps2if->iomem_res == NULL) { | ||
error = -ENOENT; | ||
goto err_free_mem; | ||
} | ||
|
||
ps2if->irq = platform_get_irq(pdev, 0); | ||
if (ps2if->irq < 0) { | ||
error = -ENXIO; | ||
goto err_free_mem; | ||
} | ||
|
||
if (!request_mem_region(ps2if->iomem_res->start, | ||
resource_size(ps2if->iomem_res), pdev->name)) { | ||
error = -EBUSY; | ||
goto err_free_mem; | ||
} | ||
|
||
ps2if->base = ioremap(ps2if->iomem_res->start, | ||
resource_size(ps2if->iomem_res)); | ||
if (!ps2if->base) { | ||
error = -ENOMEM; | ||
goto err_free_res; | ||
} | ||
|
||
error = request_irq(ps2if->irq, altera_ps2_rxint, 0, pdev->name, ps2if); | ||
if (error) { | ||
dev_err(&pdev->dev, "could not allocate IRQ %d: %d\n", | ||
ps2if->irq, error); | ||
goto err_unmap; | ||
} | ||
|
||
dev_info(&pdev->dev, "base %p, irq %d\n", ps2if->base, ps2if->irq); | ||
|
||
serio_register_port(ps2if->io); | ||
platform_set_drvdata(pdev, ps2if); | ||
|
||
return 0; | ||
|
||
err_unmap: | ||
iounmap(ps2if->base); | ||
err_free_res: | ||
release_mem_region(ps2if->iomem_res->start, | ||
resource_size(ps2if->iomem_res)); | ||
err_free_mem: | ||
kfree(ps2if); | ||
kfree(serio); | ||
return error; | ||
} | ||
|
||
/* | ||
* Remove one device from this driver. | ||
*/ | ||
static int altera_ps2_remove(struct platform_device *pdev) | ||
{ | ||
struct ps2if *ps2if = platform_get_drvdata(pdev); | ||
|
||
platform_set_drvdata(pdev, NULL); | ||
serio_unregister_port(ps2if->io); | ||
free_irq(ps2if->irq, ps2if); | ||
iounmap(ps2if->base); | ||
release_mem_region(ps2if->iomem_res->start, | ||
resource_size(ps2if->iomem_res)); | ||
kfree(ps2if); | ||
|
||
return 0; | ||
} | ||
|
||
/* | ||
* Our device driver structure | ||
*/ | ||
static struct platform_driver altera_ps2_driver = { | ||
.probe = altera_ps2_probe, | ||
.remove = altera_ps2_remove, | ||
.driver = { | ||
.name = DRV_NAME, | ||
}, | ||
}; | ||
|
||
static int __init altera_ps2_init(void) | ||
{ | ||
return platform_driver_register(&altera_ps2_driver); | ||
} | ||
|
||
static void __exit altera_ps2_exit(void) | ||
{ | ||
platform_driver_unregister(&altera_ps2_driver); | ||
} | ||
|
||
module_init(altera_ps2_init); | ||
module_exit(altera_ps2_exit); | ||
|
||
MODULE_DESCRIPTION("Altera University Program PS2 controller driver"); | ||
MODULE_AUTHOR("Thomas Chou <[email protected]>"); | ||
MODULE_LICENSE("GPL"); | ||
MODULE_ALIAS("platform:" DRV_NAME); |