Path: blob/master/drivers/input/serio/altera_ps2.c
15111 views
/*1* Altera University Program PS2 controller driver2*3* Copyright (C) 2008 Thomas Chou <[email protected]>4*5* Based on sa1111ps2.c, which is:6* Copyright (C) 2002 Russell King7*8* This program is free software; you can redistribute it and/or modify9* it under the terms of the GNU General Public License version 2 as10* published by the Free Software Foundation.11*/1213#include <linux/module.h>14#include <linux/init.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>2223#define DRV_NAME "altera_ps2"2425struct ps2if {26struct serio *io;27struct resource *iomem_res;28void __iomem *base;29unsigned irq;30};3132/*33* Read all bytes waiting in the PS2 port. There should be34* at the most one, but we loop for safety.35*/36static irqreturn_t altera_ps2_rxint(int irq, void *dev_id)37{38struct ps2if *ps2if = dev_id;39unsigned int status;40int handled = IRQ_NONE;4142while ((status = readl(ps2if->base)) & 0xffff0000) {43serio_interrupt(ps2if->io, status & 0xff, 0);44handled = IRQ_HANDLED;45}4647return handled;48}4950/*51* Write a byte to the PS2 port.52*/53static int altera_ps2_write(struct serio *io, unsigned char val)54{55struct ps2if *ps2if = io->port_data;5657writel(val, ps2if->base);58return 0;59}6061static int altera_ps2_open(struct serio *io)62{63struct ps2if *ps2if = io->port_data;6465/* clear fifo */66while (readl(ps2if->base) & 0xffff0000)67/* empty */;6869writel(1, ps2if->base + 4); /* enable rx irq */70return 0;71}7273static void altera_ps2_close(struct serio *io)74{75struct ps2if *ps2if = io->port_data;7677writel(0, ps2if->base); /* disable rx irq */78}7980/*81* Add one device to this driver.82*/83static int __devinit altera_ps2_probe(struct platform_device *pdev)84{85struct ps2if *ps2if;86struct serio *serio;87int error, irq;8889ps2if = kzalloc(sizeof(struct ps2if), GFP_KERNEL);90serio = kzalloc(sizeof(struct serio), GFP_KERNEL);91if (!ps2if || !serio) {92error = -ENOMEM;93goto err_free_mem;94}9596serio->id.type = SERIO_8042;97serio->write = altera_ps2_write;98serio->open = altera_ps2_open;99serio->close = altera_ps2_close;100strlcpy(serio->name, dev_name(&pdev->dev), sizeof(serio->name));101strlcpy(serio->phys, dev_name(&pdev->dev), sizeof(serio->phys));102serio->port_data = ps2if;103serio->dev.parent = &pdev->dev;104ps2if->io = serio;105106ps2if->iomem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);107if (ps2if->iomem_res == NULL) {108error = -ENOENT;109goto err_free_mem;110}111112113irq = platform_get_irq(pdev, 0);114if (irq < 0) {115error = -ENXIO;116goto err_free_mem;117}118ps2if->irq = irq;119120if (!request_mem_region(ps2if->iomem_res->start,121resource_size(ps2if->iomem_res), pdev->name)) {122error = -EBUSY;123goto err_free_mem;124}125126ps2if->base = ioremap(ps2if->iomem_res->start,127resource_size(ps2if->iomem_res));128if (!ps2if->base) {129error = -ENOMEM;130goto err_free_res;131}132133error = request_irq(ps2if->irq, altera_ps2_rxint, 0, pdev->name, ps2if);134if (error) {135dev_err(&pdev->dev, "could not allocate IRQ %d: %d\n",136ps2if->irq, error);137goto err_unmap;138}139140dev_info(&pdev->dev, "base %p, irq %d\n", ps2if->base, ps2if->irq);141142serio_register_port(ps2if->io);143platform_set_drvdata(pdev, ps2if);144145return 0;146147err_unmap:148iounmap(ps2if->base);149err_free_res:150release_mem_region(ps2if->iomem_res->start,151resource_size(ps2if->iomem_res));152err_free_mem:153kfree(ps2if);154kfree(serio);155return error;156}157158/*159* Remove one device from this driver.160*/161static int __devexit altera_ps2_remove(struct platform_device *pdev)162{163struct ps2if *ps2if = platform_get_drvdata(pdev);164165platform_set_drvdata(pdev, NULL);166serio_unregister_port(ps2if->io);167free_irq(ps2if->irq, ps2if);168iounmap(ps2if->base);169release_mem_region(ps2if->iomem_res->start,170resource_size(ps2if->iomem_res));171kfree(ps2if);172173return 0;174}175176#ifdef CONFIG_OF177static const struct of_device_id altera_ps2_match[] = {178{ .compatible = "ALTR,ps2-1.0", },179{},180};181MODULE_DEVICE_TABLE(of, altera_ps2_match);182#else /* CONFIG_OF */183#define altera_ps2_match NULL184#endif /* CONFIG_OF */185186/*187* Our device driver structure188*/189static struct platform_driver altera_ps2_driver = {190.probe = altera_ps2_probe,191.remove = __devexit_p(altera_ps2_remove),192.driver = {193.name = DRV_NAME,194.owner = THIS_MODULE,195.of_match_table = altera_ps2_match,196},197};198199static int __init altera_ps2_init(void)200{201return platform_driver_register(&altera_ps2_driver);202}203module_init(altera_ps2_init);204205static void __exit altera_ps2_exit(void)206{207platform_driver_unregister(&altera_ps2_driver);208}209module_exit(altera_ps2_exit);210211MODULE_DESCRIPTION("Altera University Program PS2 controller driver");212MODULE_AUTHOR("Thomas Chou <[email protected]>");213MODULE_LICENSE("GPL");214MODULE_ALIAS("platform:" DRV_NAME);215216217