Commit 268c9783 authored by Vojtech Pavlik's avatar Vojtech Pavlik

This patch adds two new serio input drivers. Both are "UART" type

drivers for PS/2 ports on both StrongARM and ARM Integrator hardware.
  -- Russell King
parent db82e821
...@@ -20,3 +20,9 @@ dep_tristate ' Parallel port keyboard adapter' CONFIG_SERIO_PARKBD $CONFIG_SERI ...@@ -20,3 +20,9 @@ dep_tristate ' Parallel port keyboard adapter' CONFIG_SERIO_PARKBD $CONFIG_SERI
if [ "$CONFIG_ARCH_ACORN" = "y" ]; then if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
dep_tristate ' Acorn RiscPC keyboard controller' CONFIG_SERIO_ACORN $CONFIG_SERIO dep_tristate ' Acorn RiscPC keyboard controller' CONFIG_SERIO_ACORN $CONFIG_SERIO
fi fi
if [ "$CONFIG_ARCH_INTEGRATOR" = "y" ]; then
dep_tristate ' AMBA KMI keyboard controller' CONFIG_SERIO_AMBAKMI $CONFIG_SERIO
fi
if [ "$CONFIG_SA1111" = "y" ]; then
dep_tristate ' Intel SA1111 keyboard controller' CONFIG_SERIO_SA1111 $CONFIG_SERIO
fi
...@@ -14,6 +14,8 @@ obj-$(CONFIG_SERIO_PARKBD) += parkbd.o ...@@ -14,6 +14,8 @@ obj-$(CONFIG_SERIO_PARKBD) += parkbd.o
obj-$(CONFIG_SERIO_SERPORT) += serport.o obj-$(CONFIG_SERIO_SERPORT) += serport.o
obj-$(CONFIG_SERIO_CT82C710) += ct82c710.o obj-$(CONFIG_SERIO_CT82C710) += ct82c710.o
obj-$(CONFIG_SERIO_RPCKBD) += rpckbd.o obj-$(CONFIG_SERIO_RPCKBD) += rpckbd.o
obj-$(CONFIG_SERIO_SA1111) += sa1111ps2.o
obj-$(CONFIG_SERIO_AMBAKMI) += ambakmi.o
obj-$(CONFIG_SERIO_Q40KBD) += q40kbd.o obj-$(CONFIG_SERIO_Q40KBD) += q40kbd.o
# The global Rules.make. # The global Rules.make.
......
/*
* linux/drivers/input/serio/amba_kmi.c
*
* Copyright (C) 2000 Deep Blue Solutions Ltd.
* 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 as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/hardware/amba_kmi.h>
#include <asm/mach/amba_kmi.h>
extern struct pt_regs *kbd_pt_regs;
#define KMI_BASE (kmi->base)
struct amba_kmi_port {
struct serio io;
struct amba_kmi_port *next;
unsigned long base;
unsigned int irq;
unsigned int divisor;
char name[32];
char phys[16];
};
static void amba_kmi_int(int irq, void *dev_id, struct pt_regs *regs)
{
struct amba_kmi_port *kmi = dev_id;
unsigned int status = __raw_readb(KMIIR);
kbd_pt_regs = regs;
while (status & KMIIR_RXINTR) {
serio_interrupt(&kmi->io, __raw_readb(KMIDATA), 0);
status = __raw_readb(KMIIR);
}
}
static int amba_kmi_write(struct serio *io, unsigned char val)
{
struct amba_kmi_port *kmi = io->driver;
unsigned int timeleft = 10000; /* timeout in 100ms */
while ((__raw_readb(KMISTAT) & KMISTAT_TXEMPTY) == 0 && timeleft--)
udelay(10);
if (timeleft)
__raw_writeb(val, KMIDATA);
return timeleft ? 0 : SERIO_TIMEOUT;
}
static int amba_kmi_open(struct serio *io)
{
struct amba_kmi_port *kmi = io->driver;
int ret;
__raw_writeb(kmi->divisor, KMICLKDIV);
__raw_writeb(KMICR_EN, KMICR);
ret = request_irq(kmi->irq, amba_kmi_int, 0, kmi->phys, kmi);
if (ret) {
printk(KERN_ERR "kmi: failed to claim IRQ%d\n", kmi->irq);
__raw_writeb(0, KMICR);
return ret;
}
__raw_writeb(KMICR_EN | KMICR_RXINTREN, KMICR);
return 0;
}
static void amba_kmi_close(struct serio *io)
{
struct amba_kmi_port *kmi = io->driver;
free_irq(kmi->irq, kmi);
__raw_writeb(0, KMICR);
}
static struct amba_kmi_port *list;
static int __init amba_kmi_init_one(char *type, unsigned long base, int irq, int nr)
{
struct amba_kmi_port *kmi;
kmi = kmalloc(sizeof(struct amba_kmi_port), GFP_KERNEL);
if (!kmi)
return -ENOMEM;
memset(kmi, 0, sizeof(struct amba_kmi_port));
kmi->io.type = SERIO_8042;
kmi->io.write = amba_kmi_write;
kmi->io.open = amba_kmi_open;
kmi->io.close = amba_kmi_close;
kmi->io.name = kmi->name;
kmi->io.phys = kmi->phys;
kmi->io.driver = kmi;
kmi->base = base;
kmi->irq = irq;
kmi->divisor = 24 / 8 - 1;
kmi->next = list;
list = kmi;
snprintf(kmi->name, sizeof(kmi->name), "AMBA KMI PS/2 %s port", type);
snprintf(kmi->phys, sizeof(kmi->phys), "amba/serio%d", nr);
serio_register_port(&kmi->io);
return 0;
}
static int __init amba_kmi_init(void)
{
amba_kmi_init_one("keyboard", IO_ADDRESS(KMI0_BASE), IRQ_KMIINT0, 0);
amba_kmi_init_one("mouse", IO_ADDRESS(KMI1_BASE), IRQ_KMIINT1, 1);
return 0;
}
static void __exit amba_kmi_exit(void)
{
struct amba_kmi_port *kmi, *next;
kmi = list;
while (kmi) {
next = kmi->next;
serio_unregister_port(&kmi->io);
kfree(kmi);
kmi = next;
}
}
module_init(amba_kmi_init);
module_exit(amba_kmi_exit);
MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
MODULE_DESCRIPTION("AMBA KMI controller driver");
MODULE_LICENSE("GPL");
/*
* linux/drivers/input/serio/sa1111ps2.c
*
* 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 as published by
* the Free Software Foundation; either version 2 of the License.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <asm/hardware/sa1111.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
extern struct pt_regs *kbd_pt_regs;
struct ps2if {
struct serio io;
struct resource *res;
unsigned long base;
unsigned int irq;
unsigned int skpcr_mask;
};
/*
* Read all bytes waiting in the PS2 port. There should be
* at the most one, but we loop for safety. If there was a
* framing error, we have to manually clear the status.
*/
static void ps2_int(int irq, void *dev_id, struct pt_regs *regs)
{
struct ps2if *sa = dev_id;
unsigned int scancode, flag, status;
kbd_pt_regs = regs;
status = sa1111_readl(sa->base + SA1111_PS2STAT);
while (status & PS2STAT_RXF) {
if (status & PS2STAT_STP)
sa1111_writel(PS2STAT_STP, sa->base + SA1111_PS2STAT);
flag = (status & PS2STAT_STP ? SERIO_FRAME : 0) |
(status & PS2STAT_RXP ? 0 : SERIO_PARITY);
scancode = sa1111_readl(sa->base + SA1111_PS2DATA) & 0xff;
if (hweight8(scancode) & 1)
flag ^= SERIO_PARITY;
serio_interrupt(&sa->io, scancode, flag);
status = sa1111_readl(sa->base + SA1111_PS2STAT);
}
}
/*
* Write a byte to the PS2 port. We have to wait for the
* port to indicate that the transmitter is empty.
*/
static int ps2_write(struct serio *io, unsigned char val)
{
struct ps2if *sa = io->driver;
unsigned int timeleft = 10000; /* timeout in 100ms */
while ((sa1111_readl(sa->base + SA1111_PS2STAT) & PS2STAT_TXE) == 0 &&
timeleft--)
udelay(10);
if (timeleft)
sa1111_writel(val, sa->base + SA1111_PS2DATA);
return timeleft ? 0 : SERIO_TIMEOUT;
}
static int ps2_open(struct serio *io)
{
struct ps2if *sa = io->driver;
int ret;
sa1111_enable_device(sa->skpcr_mask);
ret = request_irq(sa->irq, ps2_int, 0, "ps2", sa);
if (ret) {
printk(KERN_ERR "sa1111ps2: could not allocate IRQ%d: %d\n",
sa->irq, ret);
return ret;
}
sa1111_writel(PS2CR_ENA, sa->base + SA1111_PS2CR);
return 0;
}
static void ps2_close(struct serio *io)
{
struct ps2if *sa = io->driver;
sa1111_writel(0, sa->base + SA1111_PS2CR);
free_irq(sa->irq, sa);
sa1111_disable_device(sa->skpcr_mask);
}
/*
* Clear the input buffer.
*/
static void __init ps2_clear_input(struct ps2if *sa)
{
int maxread = 100;
while (maxread--) {
if ((sa1111_readl(sa->base + SA1111_PS2DATA) & 0xff) == 0xff)
break;
}
}
static inline unsigned int
ps2_test_one(struct ps2if *sa, unsigned int mask)
{
unsigned int val;
sa1111_writel(PS2CR_ENA | mask, sa->base + SA1111_PS2CR);
udelay(2);
val = sa1111_readl(sa->base + SA1111_PS2STAT);
return val & (PS2STAT_KBC | PS2STAT_KBD);
}
/*
* Test the keyboard interface. We basically check to make sure that
* we can drive each line to the keyboard independently of each other.
*/
static int __init ps2_test(struct ps2if *sa)
{
unsigned int stat;
int ret = 0;
stat = ps2_test_one(sa, PS2CR_FKC);
if (stat != PS2STAT_KBD) {
printk("Keyboard interface test failed[1]: %02x\n", stat);
ret = -ENODEV;
}
stat = ps2_test_one(sa, 0);
if (stat != (PS2STAT_KBC | PS2STAT_KBD)) {
printk("Keyboard interface test failed[2]: %02x\n", stat);
ret = -ENODEV;
}
stat = ps2_test_one(sa, PS2CR_FKD);
if (stat != PS2STAT_KBC) {
printk("Keyboard interface test failed[3]: %02x\n", stat);
ret = -ENODEV;
}
sa1111_writel(0, sa->base + SA1111_PS2CR);
return ret;
}
/*
* Initialise one PS/2 port.
*/
static int __init ps2_init_one(struct sa1111_device *dev, struct ps2if *sa)
{
int ret;
/*
* Request the physical region for this PS2 port.
*/
sa->res = request_mem_region(_SA1111(sa->base), 512, "ps2");
if (!sa->res)
return -EBUSY;
/*
* Convert the chip offset to virtual address.
*/
sa->base += (unsigned long)dev->base;
sa1111_enable_device(sa->skpcr_mask);
/* Incoming clock is 8MHz */
sa1111_writel(0, sa->base + SA1111_PS2CLKDIV);
sa1111_writel(127, sa->base + SA1111_PS2PRECNT);
/*
* Flush any pending input.
*/
ps2_clear_input(sa);
/*
* Test the keyboard interface.
*/
ret = ps2_test(sa);
if (ret)
goto out;
/*
* Flush any pending input.
*/
ps2_clear_input(sa);
sa1111_disable_device(sa->skpcr_mask);
serio_register_port(&sa->io);
return 0;
out:
sa1111_disable_device(sa->skpcr_mask);
release_resource(sa->res);
return ret;
}
/*
* Remove one PS/2 port.
*/
static void __exit ps2_remove_one(struct ps2if *sa)
{
serio_unregister_port(&sa->io);
release_resource(sa->res);
}
static struct ps2if ps2_kbd_port =
{
io: {
type: SERIO_8042,
write: ps2_write,
open: ps2_open,
close: ps2_close,
name: "SA1111 PS/2 kbd port",
phys: "sa1111/serio0",
driver: &ps2_kbd_port,
},
base: SA1111_KBD,
irq: IRQ_TPRXINT,
skpcr_mask: SKPCR_PTCLKEN,
};
static struct ps2if ps2_mse_port =
{
io: {
type: SERIO_8042,
write: ps2_write,
open: ps2_open,
close: ps2_close,
name: "SA1111 PS/2 mouse port",
phys: "sa1111/serio1",
driver: &ps2_mse_port,
},
base: SA1111_MSE,
irq: IRQ_MSRXINT,
skpcr_mask: SKPCR_PMCLKEN,
};
static int __init ps2_init(void)
{
int ret = -ENODEV;
if (sa1111) {
ret = ps2_init_one(sa1111, &ps2_kbd_port);
}
return ret;
}
static void __exit ps2_exit(void)
{
ps2_remove_one(&ps2_kbd_port);
}
module_init(ps2_init);
module_exit(ps2_exit);
MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
MODULE_DESCRIPTION("SA1111 PS2 controller driver");
MODULE_LICENSE("GPL");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment