add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

View File

@@ -0,0 +1,204 @@
#
# Input core configuration
#
config SERIO
tristate "Serial I/O support" if EMBEDDED || !X86
default y
help
Say Yes here if you have any input device that uses serial I/O to
communicate with the system. This includes the
* standard AT keyboard and PS/2 mouse *
as well as serial mice, Sun keyboards, some joysticks and 6dof
devices and more.
If unsure, say Y.
To compile this driver as a module, choose M here: the
module will be called serio.
if SERIO
config SERIO_I8042
tristate "i8042 PC Keyboard controller" if EMBEDDED || !X86
default y
depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && !M68K && !BLACKFIN
help
i8042 is the chip over which the standard AT keyboard and PS/2
mouse are connected to the computer. If you use these devices,
you'll need to say Y here.
If unsure, say Y.
To compile this driver as a module, choose M here: the
module will be called i8042.
config SERIO_SERPORT
tristate "Serial port line discipline"
default y
help
Say Y here if you plan to use an input device (mouse, joystick,
tablet, 6dof) that communicates over the RS232 serial (COM) port.
More information is available: <file:Documentation/input/input.txt>
If unsure, say Y.
To compile this driver as a module, choose M here: the
module will be called serport.
config SERIO_CT82C710
tristate "ct82c710 Aux port controller"
depends on X86
help
Say Y here if you have a Texas Instruments TravelMate notebook
equipped with the ct82c710 chip and want to use a mouse connected
to the "QuickPort".
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called ct82c710.
config SERIO_Q40KBD
tristate "Q40 keyboard controller"
depends on Q40
config SERIO_PARKBD
tristate "Parallel port keyboard adapter"
depends on PARPORT
help
Say Y here if you built a simple parallel port adapter to attach
an additional AT keyboard, XT keyboard or PS/2 mouse.
More information is available: <file:Documentation/input/input.txt>
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called parkbd.
config SERIO_RPCKBD
tristate "Acorn RiscPC keyboard controller"
depends on ARCH_ACORN
default y
help
Say Y here if you have the Acorn RiscPC and want to use an AT
keyboard connected to its keyboard controller.
To compile this driver as a module, choose M here: the
module will be called rpckbd.
config SERIO_AT32PSIF
tristate "AVR32 PSIF PS/2 keyboard and mouse controller"
depends on AVR32
help
Say Y here if you want to use the PSIF peripheral on AVR32 devices
and connect a PS/2 keyboard and/or mouse to it.
To compile this driver as a module, choose M here: the module will
be called at32psif.
config SERIO_AMBAKMI
tristate "AMBA KMI keyboard controller"
depends on ARM_AMBA
config SERIO_SA1111
tristate "Intel SA1111 keyboard controller"
depends on SA1111
config SERIO_GSCPS2
tristate "HP GSC PS/2 keyboard and PS/2 mouse controller"
depends on GSC
default y
help
This driver provides support for the PS/2 ports on PA-RISC machines
over which HP PS/2 keyboards and PS/2 mice may be connected.
If you use these devices, you'll need to say Y here.
It's safe to enable this driver, so if unsure, say Y.
To compile this driver as a module, choose M here: the
module will be called gscps2.
config HP_SDC
tristate "HP System Device Controller i8042 Support"
depends on (GSC || HP300) && SERIO
default y
help
This option enables support for the "System Device
Controller", an i8042 carrying microcode to manage a
few miscellaneous devices on some Hewlett Packard systems.
The SDC itself contains a 10ms resolution timer/clock capable
of delivering interrupts on a periodic and one-shot basis.
The SDC may also be connected to a battery-backed real-time
clock, a basic audio waveform generator, and an HP-HIL Master
Link Controller serving up to seven input devices.
By itself this option is rather useless, but enabling it will
enable selection of drivers for the abovementioned devices.
It is, however, incompatible with the old, reliable HIL keyboard
driver, and the new HIL driver is experimental, so if you plan
to use a HIL keyboard as your primary keyboard, you may wish
to keep using that driver until the new HIL drivers have had
more testing.
config HIL_MLC
tristate "HIL MLC Support (needed for HIL input devices)"
depends on HP_SDC
config SERIO_PCIPS2
tristate "PCI PS/2 keyboard and PS/2 mouse controller"
depends on PCI
help
Say Y here if you have a Mobility Docking station with PS/2
keyboard and mice ports.
To compile this driver as a module, choose M here: the
module will be called pcips2.
config SERIO_MACEPS2
tristate "SGI O2 MACE PS/2 controller"
depends on SGI_IP32
help
Say Y here if you have SGI O2 workstation and want to use its
PS/2 ports.
To compile this driver as a module, choose M here: the
module will be called maceps2.
config SERIO_LIBPS2
tristate "PS/2 driver library" if EMBEDDED
depends on SERIO_I8042 || SERIO_I8042=n
help
Say Y here if you are using a driver for device connected
to a PS/2 port, such as PS/2 mouse or standard AT keyboard.
To compile this driver as a module, choose M here: the
module will be called libps2.
config SERIO_RAW
tristate "Raw access to serio ports"
help
Say Y here if you want to have raw access to serio ports, such as
AUX ports on i8042 keyboard controller. Each serio port that is
bound to this driver will be accessible via a char device with
major 10 and dynamically allocated minor. The driver will try
allocating minor 1 (that historically corresponds to /dev/psaux)
first. To bind this driver to a serio port use sysfs interface:
echo -n "serio_raw" > /sys/bus/serio/devices/serioX/drvctl
To compile this driver as a module, choose M here: the
module will be called serio_raw.
config SERIO_XILINX_XPS_PS2
tristate "Xilinx XPS PS/2 Controller Support"
depends on PPC || MICROBLAZE
help
This driver supports XPS PS/2 IP from the Xilinx EDK on
PowerPC platform.
To compile this driver as a module, choose M here: the
module will be called xilinx_ps2.
endif

View File

@@ -0,0 +1,24 @@
#
# Makefile for the input core drivers.
#
# Each configuration option enables a list of files.
obj-$(CONFIG_SERIO) += serio.o
obj-$(CONFIG_SERIO_I8042) += i8042.o
obj-$(CONFIG_SERIO_PARKBD) += parkbd.o
obj-$(CONFIG_SERIO_SERPORT) += serport.o
obj-$(CONFIG_SERIO_CT82C710) += ct82c710.o
obj-$(CONFIG_SERIO_RPCKBD) += rpckbd.o
obj-$(CONFIG_SERIO_SA1111) += sa1111ps2.o
obj-$(CONFIG_SERIO_AMBAKMI) += ambakmi.o
obj-$(CONFIG_SERIO_AT32PSIF) += at32psif.o
obj-$(CONFIG_SERIO_Q40KBD) += q40kbd.o
obj-$(CONFIG_SERIO_GSCPS2) += gscps2.o
obj-$(CONFIG_HP_SDC) += hp_sdc.o
obj-$(CONFIG_HIL_MLC) += hp_sdc_mlc.o hil_mlc.o
obj-$(CONFIG_SERIO_PCIPS2) += pcips2.o
obj-$(CONFIG_SERIO_MACEPS2) += maceps2.o
obj-$(CONFIG_SERIO_LIBPS2) += libps2.o
obj-$(CONFIG_SERIO_RAW) += serio_raw.o
obj-$(CONFIG_SERIO_XILINX_XPS_PS2) += xilinx_ps2.o

View File

@@ -0,0 +1,222 @@
/*
* linux/drivers/input/serio/ambakmi.c
*
* Copyright (C) 2000-2003 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 <linux/err.h>
#include <linux/amba/bus.h>
#include <linux/amba/kmi.h>
#include <linux/clk.h>
#include <asm/io.h>
#include <asm/irq.h>
#define KMI_BASE (kmi->base)
struct amba_kmi_port {
struct serio *io;
struct clk *clk;
void __iomem *base;
unsigned int irq;
unsigned int divisor;
unsigned int open;
};
static irqreturn_t amba_kmi_int(int irq, void *dev_id)
{
struct amba_kmi_port *kmi = dev_id;
unsigned int status = readb(KMIIR);
int handled = IRQ_NONE;
while (status & KMIIR_RXINTR) {
serio_interrupt(kmi->io, readb(KMIDATA), 0);
status = readb(KMIIR);
handled = IRQ_HANDLED;
}
return handled;
}
static int amba_kmi_write(struct serio *io, unsigned char val)
{
struct amba_kmi_port *kmi = io->port_data;
unsigned int timeleft = 10000; /* timeout in 100ms */
while ((readb(KMISTAT) & KMISTAT_TXEMPTY) == 0 && --timeleft)
udelay(10);
if (timeleft)
writeb(val, KMIDATA);
return timeleft ? 0 : SERIO_TIMEOUT;
}
static int amba_kmi_open(struct serio *io)
{
struct amba_kmi_port *kmi = io->port_data;
unsigned int divisor;
int ret;
ret = clk_enable(kmi->clk);
if (ret)
goto out;
divisor = clk_get_rate(kmi->clk) / 8000000 - 1;
writeb(divisor, KMICLKDIV);
writeb(KMICR_EN, KMICR);
ret = request_irq(kmi->irq, amba_kmi_int, 0, "kmi-pl050", kmi);
if (ret) {
printk(KERN_ERR "kmi: failed to claim IRQ%d\n", kmi->irq);
writeb(0, KMICR);
goto clk_disable;
}
writeb(KMICR_EN | KMICR_RXINTREN, KMICR);
return 0;
clk_disable:
clk_disable(kmi->clk);
out:
return ret;
}
static void amba_kmi_close(struct serio *io)
{
struct amba_kmi_port *kmi = io->port_data;
writeb(0, KMICR);
free_irq(kmi->irq, kmi);
clk_disable(kmi->clk);
}
static int amba_kmi_probe(struct amba_device *dev, struct amba_id *id)
{
struct amba_kmi_port *kmi;
struct serio *io;
int ret;
ret = amba_request_regions(dev, NULL);
if (ret)
return ret;
kmi = kzalloc(sizeof(struct amba_kmi_port), GFP_KERNEL);
io = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (!kmi || !io) {
ret = -ENOMEM;
goto out;
}
io->id.type = SERIO_8042;
io->write = amba_kmi_write;
io->open = amba_kmi_open;
io->close = amba_kmi_close;
strlcpy(io->name, dev_name(&dev->dev), sizeof(io->name));
strlcpy(io->phys, dev_name(&dev->dev), sizeof(io->phys));
io->port_data = kmi;
io->dev.parent = &dev->dev;
kmi->io = io;
kmi->base = ioremap(dev->res.start, resource_size(&dev->res));
if (!kmi->base) {
ret = -ENOMEM;
goto out;
}
kmi->clk = clk_get(&dev->dev, "KMIREFCLK");
if (IS_ERR(kmi->clk)) {
ret = PTR_ERR(kmi->clk);
goto unmap;
}
kmi->irq = dev->irq[0];
amba_set_drvdata(dev, kmi);
serio_register_port(kmi->io);
return 0;
unmap:
iounmap(kmi->base);
out:
kfree(kmi);
kfree(io);
amba_release_regions(dev);
return ret;
}
static int amba_kmi_remove(struct amba_device *dev)
{
struct amba_kmi_port *kmi = amba_get_drvdata(dev);
amba_set_drvdata(dev, NULL);
serio_unregister_port(kmi->io);
clk_put(kmi->clk);
iounmap(kmi->base);
kfree(kmi);
amba_release_regions(dev);
return 0;
}
static int amba_kmi_resume(struct amba_device *dev)
{
struct amba_kmi_port *kmi = amba_get_drvdata(dev);
/* kick the serio layer to rescan this port */
serio_reconnect(kmi->io);
return 0;
}
static struct amba_id amba_kmi_idtable[] = {
{
.id = 0x00041050,
.mask = 0x000fffff,
},
{ 0, 0 }
};
static struct amba_driver ambakmi_driver = {
.drv = {
.name = "kmi-pl050",
},
.id_table = amba_kmi_idtable,
.probe = amba_kmi_probe,
.remove = amba_kmi_remove,
.resume = amba_kmi_resume,
};
static int __init amba_kmi_init(void)
{
return amba_driver_register(&ambakmi_driver);
}
static void __exit amba_kmi_exit(void)
{
amba_driver_unregister(&ambakmi_driver);
}
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");

View File

@@ -0,0 +1,375 @@
/*
* Copyright (C) 2007 Atmel Corporation
*
* Driver for the AT32AP700X PS/2 controller (PSIF).
*
* 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/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
/* PSIF register offsets */
#define PSIF_CR 0x00
#define PSIF_RHR 0x04
#define PSIF_THR 0x08
#define PSIF_SR 0x10
#define PSIF_IER 0x14
#define PSIF_IDR 0x18
#define PSIF_IMR 0x1c
#define PSIF_PSR 0x24
/* Bitfields in control register. */
#define PSIF_CR_RXDIS_OFFSET 1
#define PSIF_CR_RXDIS_SIZE 1
#define PSIF_CR_RXEN_OFFSET 0
#define PSIF_CR_RXEN_SIZE 1
#define PSIF_CR_SWRST_OFFSET 15
#define PSIF_CR_SWRST_SIZE 1
#define PSIF_CR_TXDIS_OFFSET 9
#define PSIF_CR_TXDIS_SIZE 1
#define PSIF_CR_TXEN_OFFSET 8
#define PSIF_CR_TXEN_SIZE 1
/* Bitfields in interrupt disable, enable, mask and status register. */
#define PSIF_NACK_OFFSET 8
#define PSIF_NACK_SIZE 1
#define PSIF_OVRUN_OFFSET 5
#define PSIF_OVRUN_SIZE 1
#define PSIF_PARITY_OFFSET 9
#define PSIF_PARITY_SIZE 1
#define PSIF_RXRDY_OFFSET 4
#define PSIF_RXRDY_SIZE 1
#define PSIF_TXEMPTY_OFFSET 1
#define PSIF_TXEMPTY_SIZE 1
#define PSIF_TXRDY_OFFSET 0
#define PSIF_TXRDY_SIZE 1
/* Bitfields in prescale register. */
#define PSIF_PSR_PRSCV_OFFSET 0
#define PSIF_PSR_PRSCV_SIZE 12
/* Bitfields in receive hold register. */
#define PSIF_RHR_RXDATA_OFFSET 0
#define PSIF_RHR_RXDATA_SIZE 8
/* Bitfields in transmit hold register. */
#define PSIF_THR_TXDATA_OFFSET 0
#define PSIF_THR_TXDATA_SIZE 8
/* Bit manipulation macros */
#define PSIF_BIT(name) \
(1 << PSIF_##name##_OFFSET)
#define PSIF_BF(name, value) \
(((value) & ((1 << PSIF_##name##_SIZE) - 1)) \
<< PSIF_##name##_OFFSET)
#define PSIF_BFEXT(name, value) \
(((value) >> PSIF_##name##_OFFSET) \
& ((1 << PSIF_##name##_SIZE) - 1))
#define PSIF_BFINS(name, value, old) \
(((old) & ~(((1 << PSIF_##name##_SIZE) - 1) \
<< PSIF_##name##_OFFSET)) \
| PSIF_BF(name, value))
/* Register access macros */
#define psif_readl(port, reg) \
__raw_readl((port)->regs + PSIF_##reg)
#define psif_writel(port, reg, value) \
__raw_writel((value), (port)->regs + PSIF_##reg)
struct psif {
struct platform_device *pdev;
struct clk *pclk;
struct serio *io;
void __iomem *regs;
unsigned int irq;
unsigned int open;
/* Prevent concurrent writes to PSIF THR. */
spinlock_t lock;
};
static irqreturn_t psif_interrupt(int irq, void *_ptr)
{
struct psif *psif = _ptr;
int retval = IRQ_NONE;
unsigned int io_flags = 0;
unsigned long status;
status = psif_readl(psif, SR);
if (status & PSIF_BIT(RXRDY)) {
unsigned char val = (unsigned char) psif_readl(psif, RHR);
if (status & PSIF_BIT(PARITY))
io_flags |= SERIO_PARITY;
if (status & PSIF_BIT(OVRUN))
dev_err(&psif->pdev->dev, "overrun read error\n");
serio_interrupt(psif->io, val, io_flags);
retval = IRQ_HANDLED;
}
return retval;
}
static int psif_write(struct serio *io, unsigned char val)
{
struct psif *psif = io->port_data;
unsigned long flags;
int timeout = 10;
int retval = 0;
spin_lock_irqsave(&psif->lock, flags);
while (!(psif_readl(psif, SR) & PSIF_BIT(TXEMPTY)) && timeout--)
msleep(10);
if (timeout >= 0) {
psif_writel(psif, THR, val);
} else {
dev_dbg(&psif->pdev->dev, "timeout writing to THR\n");
retval = -EBUSY;
}
spin_unlock_irqrestore(&psif->lock, flags);
return retval;
}
static int psif_open(struct serio *io)
{
struct psif *psif = io->port_data;
int retval;
retval = clk_enable(psif->pclk);
if (retval)
goto out;
psif_writel(psif, CR, PSIF_BIT(CR_TXEN) | PSIF_BIT(CR_RXEN));
psif_writel(psif, IER, PSIF_BIT(RXRDY));
psif->open = 1;
out:
return retval;
}
static void psif_close(struct serio *io)
{
struct psif *psif = io->port_data;
psif->open = 0;
psif_writel(psif, IDR, ~0UL);
psif_writel(psif, CR, PSIF_BIT(CR_TXDIS) | PSIF_BIT(CR_RXDIS));
clk_disable(psif->pclk);
}
static void psif_set_prescaler(struct psif *psif)
{
unsigned long prscv;
unsigned long rate = clk_get_rate(psif->pclk);
/* PRSCV = Pulse length (100 us) * PSIF module frequency. */
prscv = 100 * (rate / 1000000UL);
if (prscv > ((1<<PSIF_PSR_PRSCV_SIZE) - 1)) {
prscv = (1<<PSIF_PSR_PRSCV_SIZE) - 1;
dev_dbg(&psif->pdev->dev, "pclk too fast, "
"prescaler set to max\n");
}
clk_enable(psif->pclk);
psif_writel(psif, PSR, prscv);
clk_disable(psif->pclk);
}
static int __init psif_probe(struct platform_device *pdev)
{
struct resource *regs;
struct psif *psif;
struct serio *io;
struct clk *pclk;
int irq;
int ret;
psif = kzalloc(sizeof(struct psif), GFP_KERNEL);
if (!psif) {
dev_dbg(&pdev->dev, "out of memory\n");
ret = -ENOMEM;
goto out;
}
psif->pdev = pdev;
io = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (!io) {
dev_dbg(&pdev->dev, "out of memory\n");
ret = -ENOMEM;
goto out_free_psif;
}
psif->io = io;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!regs) {
dev_dbg(&pdev->dev, "no mmio resources defined\n");
ret = -ENOMEM;
goto out_free_io;
}
psif->regs = ioremap(regs->start, resource_size(regs));
if (!psif->regs) {
ret = -ENOMEM;
dev_dbg(&pdev->dev, "could not map I/O memory\n");
goto out_free_io;
}
pclk = clk_get(&pdev->dev, "pclk");
if (IS_ERR(pclk)) {
dev_dbg(&pdev->dev, "could not get peripheral clock\n");
ret = PTR_ERR(pclk);
goto out_iounmap;
}
psif->pclk = pclk;
/* Reset the PSIF to enter at a known state. */
ret = clk_enable(pclk);
if (ret) {
dev_dbg(&pdev->dev, "could not enable pclk\n");
goto out_put_clk;
}
psif_writel(psif, CR, PSIF_BIT(CR_SWRST));
clk_disable(pclk);
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_dbg(&pdev->dev, "could not get irq\n");
ret = -ENXIO;
goto out_put_clk;
}
ret = request_irq(irq, psif_interrupt, IRQF_SHARED, "at32psif", psif);
if (ret) {
dev_dbg(&pdev->dev, "could not request irq %d\n", irq);
goto out_put_clk;
}
psif->irq = irq;
io->id.type = SERIO_8042;
io->write = psif_write;
io->open = psif_open;
io->close = psif_close;
snprintf(io->name, sizeof(io->name), "AVR32 PS/2 port%d", pdev->id);
snprintf(io->phys, sizeof(io->phys), "at32psif/serio%d", pdev->id);
io->port_data = psif;
io->dev.parent = &pdev->dev;
psif_set_prescaler(psif);
spin_lock_init(&psif->lock);
serio_register_port(psif->io);
platform_set_drvdata(pdev, psif);
dev_info(&pdev->dev, "Atmel AVR32 PSIF PS/2 driver on 0x%08x irq %d\n",
(int)psif->regs, psif->irq);
return 0;
out_put_clk:
clk_put(psif->pclk);
out_iounmap:
iounmap(psif->regs);
out_free_io:
kfree(io);
out_free_psif:
kfree(psif);
out:
return ret;
}
static int __exit psif_remove(struct platform_device *pdev)
{
struct psif *psif = platform_get_drvdata(pdev);
psif_writel(psif, IDR, ~0UL);
psif_writel(psif, CR, PSIF_BIT(CR_TXDIS) | PSIF_BIT(CR_RXDIS));
serio_unregister_port(psif->io);
iounmap(psif->regs);
free_irq(psif->irq, psif);
clk_put(psif->pclk);
kfree(psif);
platform_set_drvdata(pdev, NULL);
return 0;
}
#ifdef CONFIG_PM
static int psif_suspend(struct platform_device *pdev, pm_message_t state)
{
struct psif *psif = platform_get_drvdata(pdev);
if (psif->open) {
psif_writel(psif, CR, PSIF_BIT(CR_RXDIS) | PSIF_BIT(CR_TXDIS));
clk_disable(psif->pclk);
}
return 0;
}
static int psif_resume(struct platform_device *pdev)
{
struct psif *psif = platform_get_drvdata(pdev);
if (psif->open) {
clk_enable(psif->pclk);
psif_set_prescaler(psif);
psif_writel(psif, CR, PSIF_BIT(CR_RXEN) | PSIF_BIT(CR_TXEN));
}
return 0;
}
#else
#define psif_suspend NULL
#define psif_resume NULL
#endif
static struct platform_driver psif_driver = {
.remove = __exit_p(psif_remove),
.driver = {
.name = "atmel_psif",
},
.suspend = psif_suspend,
.resume = psif_resume,
};
static int __init psif_init(void)
{
return platform_driver_probe(&psif_driver, psif_probe);
}
static void __exit psif_exit(void)
{
platform_driver_unregister(&psif_driver);
}
module_init(psif_init);
module_exit(psif_exit);
MODULE_AUTHOR("Hans-Christian Egtvedt <hans-christian.egtvedt@atmel.com>");
MODULE_DESCRIPTION("Atmel AVR32 PSIF PS/2 driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,260 @@
/*
* Copyright (c) 1999-2001 Vojtech Pavlik
*/
/*
* 82C710 C&T mouse port chip driver for Linux
*/
/*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/serio.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <asm/io.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("82C710 C&T mouse port chip driver");
MODULE_LICENSE("GPL");
/*
* ct82c710 interface
*/
#define CT82C710_DEV_IDLE 0x01 /* Device Idle */
#define CT82C710_RX_FULL 0x02 /* Device Char received */
#define CT82C710_TX_IDLE 0x04 /* Device XMIT Idle */
#define CT82C710_RESET 0x08 /* Device Reset */
#define CT82C710_INTS_ON 0x10 /* Device Interrupt On */
#define CT82C710_ERROR_FLAG 0x20 /* Device Error */
#define CT82C710_CLEAR 0x40 /* Device Clear */
#define CT82C710_ENABLE 0x80 /* Device Enable */
#define CT82C710_IRQ 12
#define CT82C710_DATA ct82c710_iores.start
#define CT82C710_STATUS (ct82c710_iores.start + 1)
static struct serio *ct82c710_port;
static struct platform_device *ct82c710_device;
static struct resource ct82c710_iores;
/*
* Interrupt handler for the 82C710 mouse port. A character
* is waiting in the 82C710.
*/
static irqreturn_t ct82c710_interrupt(int cpl, void *dev_id)
{
return serio_interrupt(ct82c710_port, inb(CT82C710_DATA), 0);
}
/*
* Wait for device to send output char and flush any input char.
*/
static int ct82c170_wait(void)
{
int timeout = 60000;
while ((inb(CT82C710_STATUS) & (CT82C710_RX_FULL | CT82C710_TX_IDLE | CT82C710_DEV_IDLE))
!= (CT82C710_DEV_IDLE | CT82C710_TX_IDLE) && timeout) {
if (inb_p(CT82C710_STATUS) & CT82C710_RX_FULL) inb_p(CT82C710_DATA);
udelay(1);
timeout--;
}
return !timeout;
}
static void ct82c710_close(struct serio *serio)
{
if (ct82c170_wait())
printk(KERN_WARNING "ct82c710.c: Device busy in close()\n");
outb_p(inb_p(CT82C710_STATUS) & ~(CT82C710_ENABLE | CT82C710_INTS_ON), CT82C710_STATUS);
if (ct82c170_wait())
printk(KERN_WARNING "ct82c710.c: Device busy in close()\n");
free_irq(CT82C710_IRQ, NULL);
}
static int ct82c710_open(struct serio *serio)
{
unsigned char status;
if (request_irq(CT82C710_IRQ, ct82c710_interrupt, 0, "ct82c710", NULL))
return -1;
status = inb_p(CT82C710_STATUS);
status |= (CT82C710_ENABLE | CT82C710_RESET);
outb_p(status, CT82C710_STATUS);
status &= ~(CT82C710_RESET);
outb_p(status, CT82C710_STATUS);
status |= CT82C710_INTS_ON;
outb_p(status, CT82C710_STATUS); /* Enable interrupts */
while (ct82c170_wait()) {
printk(KERN_ERR "ct82c710: Device busy in open()\n");
status &= ~(CT82C710_ENABLE | CT82C710_INTS_ON);
outb_p(status, CT82C710_STATUS);
free_irq(CT82C710_IRQ, NULL);
return -1;
}
return 0;
}
/*
* Write to the 82C710 mouse device.
*/
static int ct82c710_write(struct serio *port, unsigned char c)
{
if (ct82c170_wait()) return -1;
outb_p(c, CT82C710_DATA);
return 0;
}
/*
* See if we can find a 82C710 device. Read mouse address.
*/
static int __init ct82c710_detect(void)
{
outb_p(0x55, 0x2fa); /* Any value except 9, ff or 36 */
outb_p(0xaa, 0x3fa); /* Inverse of 55 */
outb_p(0x36, 0x3fa); /* Address the chip */
outb_p(0xe4, 0x3fa); /* 390/4; 390 = config address */
outb_p(0x1b, 0x2fa); /* Inverse of e4 */
outb_p(0x0f, 0x390); /* Write index */
if (inb_p(0x391) != 0xe4) /* Config address found? */
return -ENODEV; /* No: no 82C710 here */
outb_p(0x0d, 0x390); /* Write index */
ct82c710_iores.start = inb_p(0x391) << 2; /* Get mouse I/O address */
ct82c710_iores.end = ct82c710_iores.start + 1;
ct82c710_iores.flags = IORESOURCE_IO;
outb_p(0x0f, 0x390);
outb_p(0x0f, 0x391); /* Close config mode */
return 0;
}
static int __devinit ct82c710_probe(struct platform_device *dev)
{
ct82c710_port = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (!ct82c710_port)
return -ENOMEM;
ct82c710_port->id.type = SERIO_8042;
ct82c710_port->dev.parent = &dev->dev;
ct82c710_port->open = ct82c710_open;
ct82c710_port->close = ct82c710_close;
ct82c710_port->write = ct82c710_write;
strlcpy(ct82c710_port->name, "C&T 82c710 mouse port",
sizeof(ct82c710_port->name));
snprintf(ct82c710_port->phys, sizeof(ct82c710_port->phys),
"isa%16llx/serio0", (unsigned long long)CT82C710_DATA);
serio_register_port(ct82c710_port);
return 0;
}
static int __devexit ct82c710_remove(struct platform_device *dev)
{
serio_unregister_port(ct82c710_port);
return 0;
}
static struct platform_driver ct82c710_driver = {
.driver = {
.name = "ct82c710",
.owner = THIS_MODULE,
},
.probe = ct82c710_probe,
.remove = __devexit_p(ct82c710_remove),
};
static int __init ct82c710_init(void)
{
int error;
error = ct82c710_detect();
if (error)
return error;
error = platform_driver_register(&ct82c710_driver);
if (error)
return error;
ct82c710_device = platform_device_alloc("ct82c710", -1);
if (!ct82c710_device) {
error = -ENOMEM;
goto err_unregister_driver;
}
error = platform_device_add_resources(ct82c710_device, &ct82c710_iores, 1);
if (error)
goto err_free_device;
error = platform_device_add(ct82c710_device);
if (error)
goto err_free_device;
serio_register_port(ct82c710_port);
printk(KERN_INFO "serio: C&T 82c710 mouse port at %#llx irq %d\n",
(unsigned long long)CT82C710_DATA, CT82C710_IRQ);
return 0;
err_free_device:
platform_device_put(ct82c710_device);
err_unregister_driver:
platform_driver_unregister(&ct82c710_driver);
return error;
}
static void __exit ct82c710_exit(void)
{
platform_device_unregister(ct82c710_device);
platform_driver_unregister(&ct82c710_driver);
}
module_init(ct82c710_init);
module_exit(ct82c710_exit);

View File

@@ -0,0 +1,463 @@
/*
* drivers/input/serio/gscps2.c
*
* Copyright (c) 2004-2006 Helge Deller <deller@gmx.de>
* Copyright (c) 2002 Laurent Canet <canetl@esiee.fr>
* Copyright (c) 2002 Thibaut Varene <varenet@parisc-linux.org>
*
* Pieces of code based on linux-2.4's hp_mouse.c & hp_keyb.c
* Copyright (c) 1999 Alex deVries <alex@onefishtwo.ca>
* Copyright (c) 1999-2000 Philipp Rumpf <prumpf@tux.org>
* Copyright (c) 2000 Xavier Debacker <debackex@esiee.fr>
* Copyright (c) 2000-2001 Thomas Marteau <marteaut@esiee.fr>
*
* HP GSC PS/2 port driver, found in PA/RISC Workstations
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* TODO:
* - Dino testing (did HP ever shipped a machine on which this port
* was usable/enabled ?)
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/serio.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/pci_ids.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/parisc-device.h>
MODULE_AUTHOR("Laurent Canet <canetl@esiee.fr>, Thibaut Varene <varenet@parisc-linux.org>, Helge Deller <deller@gmx.de>");
MODULE_DESCRIPTION("HP GSC PS2 port driver");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(parisc, gscps2_device_tbl);
#define PFX "gscps2.c: "
/*
* Driver constants
*/
/* various constants */
#define ENABLE 1
#define DISABLE 0
#define GSC_DINO_OFFSET 0x0800 /* offset for DINO controller versus LASI one */
/* PS/2 IO port offsets */
#define GSC_ID 0x00 /* device ID offset (see: GSC_ID_XXX) */
#define GSC_RESET 0x00 /* reset port offset */
#define GSC_RCVDATA 0x04 /* receive port offset */
#define GSC_XMTDATA 0x04 /* transmit port offset */
#define GSC_CONTROL 0x08 /* see: Control register bits */
#define GSC_STATUS 0x0C /* see: Status register bits */
/* Control register bits */
#define GSC_CTRL_ENBL 0x01 /* enable interface */
#define GSC_CTRL_LPBXR 0x02 /* loopback operation */
#define GSC_CTRL_DIAG 0x20 /* directly control clock/data line */
#define GSC_CTRL_DATDIR 0x40 /* data line direct control */
#define GSC_CTRL_CLKDIR 0x80 /* clock line direct control */
/* Status register bits */
#define GSC_STAT_RBNE 0x01 /* Receive Buffer Not Empty */
#define GSC_STAT_TBNE 0x02 /* Transmit Buffer Not Empty */
#define GSC_STAT_TERR 0x04 /* Timeout Error */
#define GSC_STAT_PERR 0x08 /* Parity Error */
#define GSC_STAT_CMPINTR 0x10 /* Composite Interrupt = irq on any port */
#define GSC_STAT_DATSHD 0x40 /* Data Line Shadow */
#define GSC_STAT_CLKSHD 0x80 /* Clock Line Shadow */
/* IDs returned by GSC_ID port register */
#define GSC_ID_KEYBOARD 0 /* device ID values */
#define GSC_ID_MOUSE 1
static irqreturn_t gscps2_interrupt(int irq, void *dev);
#define BUFFER_SIZE 0x0f
/* GSC PS/2 port device struct */
struct gscps2port {
struct list_head node;
struct parisc_device *padev;
struct serio *port;
spinlock_t lock;
char *addr;
u8 act, append; /* position in buffer[] */
struct {
u8 data;
u8 str;
} buffer[BUFFER_SIZE+1];
int id;
};
/*
* Various HW level routines
*/
#define gscps2_readb_input(x) readb((x)+GSC_RCVDATA)
#define gscps2_readb_control(x) readb((x)+GSC_CONTROL)
#define gscps2_readb_status(x) readb((x)+GSC_STATUS)
#define gscps2_writeb_control(x, y) writeb((x), (y)+GSC_CONTROL)
/*
* wait_TBE() - wait for Transmit Buffer Empty
*/
static int wait_TBE(char *addr)
{
int timeout = 25000; /* device is expected to react within 250 msec */
while (gscps2_readb_status(addr) & GSC_STAT_TBNE) {
if (!--timeout)
return 0; /* This should not happen */
udelay(10);
}
return 1;
}
/*
* gscps2_flush() - flush the receive buffer
*/
static void gscps2_flush(struct gscps2port *ps2port)
{
while (gscps2_readb_status(ps2port->addr) & GSC_STAT_RBNE)
gscps2_readb_input(ps2port->addr);
ps2port->act = ps2port->append = 0;
}
/*
* gscps2_writeb_output() - write a byte to the port
*
* returns 1 on success, 0 on error
*/
static inline int gscps2_writeb_output(struct gscps2port *ps2port, u8 data)
{
unsigned long flags;
char *addr = ps2port->addr;
if (!wait_TBE(addr)) {
printk(KERN_DEBUG PFX "timeout - could not write byte %#x\n", data);
return 0;
}
while (gscps2_readb_status(ps2port->addr) & GSC_STAT_RBNE)
/* wait */;
spin_lock_irqsave(&ps2port->lock, flags);
writeb(data, addr+GSC_XMTDATA);
spin_unlock_irqrestore(&ps2port->lock, flags);
/* this is ugly, but due to timing of the port it seems to be necessary. */
mdelay(6);
/* make sure any received data is returned as fast as possible */
/* this is important e.g. when we set the LEDs on the keyboard */
gscps2_interrupt(0, NULL);
return 1;
}
/*
* gscps2_enable() - enables or disables the port
*/
static void gscps2_enable(struct gscps2port *ps2port, int enable)
{
unsigned long flags;
u8 data;
/* now enable/disable the port */
spin_lock_irqsave(&ps2port->lock, flags);
gscps2_flush(ps2port);
data = gscps2_readb_control(ps2port->addr);
if (enable)
data |= GSC_CTRL_ENBL;
else
data &= ~GSC_CTRL_ENBL;
gscps2_writeb_control(data, ps2port->addr);
spin_unlock_irqrestore(&ps2port->lock, flags);
wait_TBE(ps2port->addr);
gscps2_flush(ps2port);
}
/*
* gscps2_reset() - resets the PS/2 port
*/
static void gscps2_reset(struct gscps2port *ps2port)
{
char *addr = ps2port->addr;
unsigned long flags;
/* reset the interface */
spin_lock_irqsave(&ps2port->lock, flags);
gscps2_flush(ps2port);
writeb(0xff, addr+GSC_RESET);
gscps2_flush(ps2port);
spin_unlock_irqrestore(&ps2port->lock, flags);
}
static LIST_HEAD(ps2port_list);
/**
* gscps2_interrupt() - Interruption service routine
*
* This function reads received PS/2 bytes and processes them on
* all interfaces.
* The problematic part here is, that the keyboard and mouse PS/2 port
* share the same interrupt and it's not possible to send data if any
* one of them holds input data. To solve this problem we try to receive
* the data as fast as possible and handle the reporting to the upper layer
* later.
*/
static irqreturn_t gscps2_interrupt(int irq, void *dev)
{
struct gscps2port *ps2port;
list_for_each_entry(ps2port, &ps2port_list, node) {
unsigned long flags;
spin_lock_irqsave(&ps2port->lock, flags);
while ( (ps2port->buffer[ps2port->append].str =
gscps2_readb_status(ps2port->addr)) & GSC_STAT_RBNE ) {
ps2port->buffer[ps2port->append].data =
gscps2_readb_input(ps2port->addr);
ps2port->append = ((ps2port->append+1) & BUFFER_SIZE);
}
spin_unlock_irqrestore(&ps2port->lock, flags);
} /* list_for_each_entry */
/* all data was read from the ports - now report the data to upper layer */
list_for_each_entry(ps2port, &ps2port_list, node) {
while (ps2port->act != ps2port->append) {
unsigned int rxflags;
u8 data, status;
/* Did new data arrived while we read existing data ?
If yes, exit now and let the new irq handler start over again */
if (gscps2_readb_status(ps2port->addr) & GSC_STAT_CMPINTR)
return IRQ_HANDLED;
status = ps2port->buffer[ps2port->act].str;
data = ps2port->buffer[ps2port->act].data;
ps2port->act = ((ps2port->act+1) & BUFFER_SIZE);
rxflags = ((status & GSC_STAT_TERR) ? SERIO_TIMEOUT : 0 ) |
((status & GSC_STAT_PERR) ? SERIO_PARITY : 0 );
serio_interrupt(ps2port->port, data, rxflags);
} /* while() */
} /* list_for_each_entry */
return IRQ_HANDLED;
}
/*
* gscps2_write() - send a byte out through the aux interface.
*/
static int gscps2_write(struct serio *port, unsigned char data)
{
struct gscps2port *ps2port = port->port_data;
if (!gscps2_writeb_output(ps2port, data)) {
printk(KERN_DEBUG PFX "sending byte %#x failed.\n", data);
return -1;
}
return 0;
}
/*
* gscps2_open() is called when a port is opened by the higher layer.
* It resets and enables the port.
*/
static int gscps2_open(struct serio *port)
{
struct gscps2port *ps2port = port->port_data;
gscps2_reset(ps2port);
/* enable it */
gscps2_enable(ps2port, ENABLE);
gscps2_interrupt(0, NULL);
return 0;
}
/*
* gscps2_close() disables the port
*/
static void gscps2_close(struct serio *port)
{
struct gscps2port *ps2port = port->port_data;
gscps2_enable(ps2port, DISABLE);
}
/**
* gscps2_probe() - Probes PS2 devices
* @return: success/error report
*/
static int __init gscps2_probe(struct parisc_device *dev)
{
struct gscps2port *ps2port;
struct serio *serio;
unsigned long hpa = dev->hpa.start;
int ret;
if (!dev->irq)
return -ENODEV;
/* Offset for DINO PS/2. Works with LASI even */
if (dev->id.sversion == 0x96)
hpa += GSC_DINO_OFFSET;
ps2port = kzalloc(sizeof(struct gscps2port), GFP_KERNEL);
serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (!ps2port || !serio) {
ret = -ENOMEM;
goto fail_nomem;
}
dev_set_drvdata(&dev->dev, ps2port);
ps2port->port = serio;
ps2port->padev = dev;
ps2port->addr = ioremap_nocache(hpa, GSC_STATUS + 4);
spin_lock_init(&ps2port->lock);
gscps2_reset(ps2port);
ps2port->id = readb(ps2port->addr + GSC_ID) & 0x0f;
snprintf(serio->name, sizeof(serio->name), "GSC PS/2 %s",
(ps2port->id == GSC_ID_KEYBOARD) ? "keyboard" : "mouse");
strlcpy(serio->phys, dev_name(&dev->dev), sizeof(serio->phys));
serio->id.type = SERIO_8042;
serio->write = gscps2_write;
serio->open = gscps2_open;
serio->close = gscps2_close;
serio->port_data = ps2port;
serio->dev.parent = &dev->dev;
ret = -EBUSY;
if (request_irq(dev->irq, gscps2_interrupt, IRQF_SHARED, ps2port->port->name, ps2port))
goto fail_miserably;
if (ps2port->id != GSC_ID_KEYBOARD && ps2port->id != GSC_ID_MOUSE) {
printk(KERN_WARNING PFX "Unsupported PS/2 port at 0x%08lx (id=%d) ignored\n",
hpa, ps2port->id);
ret = -ENODEV;
goto fail;
}
#if 0
if (!request_mem_region(hpa, GSC_STATUS + 4, ps2port->port.name))
goto fail;
#endif
printk(KERN_INFO "serio: %s port at 0x%p irq %d @ %s\n",
ps2port->port->name,
ps2port->addr,
ps2port->padev->irq,
ps2port->port->phys);
serio_register_port(ps2port->port);
list_add_tail(&ps2port->node, &ps2port_list);
return 0;
fail:
free_irq(dev->irq, ps2port);
fail_miserably:
iounmap(ps2port->addr);
release_mem_region(dev->hpa.start, GSC_STATUS + 4);
fail_nomem:
kfree(ps2port);
kfree(serio);
return ret;
}
/**
* gscps2_remove() - Removes PS2 devices
* @return: success/error report
*/
static int __devexit gscps2_remove(struct parisc_device *dev)
{
struct gscps2port *ps2port = dev_get_drvdata(&dev->dev);
serio_unregister_port(ps2port->port);
free_irq(dev->irq, ps2port);
gscps2_flush(ps2port);
list_del(&ps2port->node);
iounmap(ps2port->addr);
#if 0
release_mem_region(dev->hpa, GSC_STATUS + 4);
#endif
dev_set_drvdata(&dev->dev, NULL);
kfree(ps2port);
return 0;
}
static struct parisc_device_id gscps2_device_tbl[] = {
{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00084 }, /* LASI PS/2 */
#ifdef DINO_TESTED
{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00096 }, /* DINO PS/2 */
#endif
{ 0, } /* 0 terminated list */
};
static struct parisc_driver parisc_ps2_driver = {
.name = "gsc_ps2",
.id_table = gscps2_device_tbl,
.probe = gscps2_probe,
.remove = gscps2_remove,
};
static int __init gscps2_init(void)
{
register_parisc_driver(&parisc_ps2_driver);
return 0;
}
static void __exit gscps2_exit(void)
{
unregister_parisc_driver(&parisc_ps2_driver);
}
module_init(gscps2_init);
module_exit(gscps2_exit);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,358 @@
/*
* Access to HP-HIL MLC through HP System Device Controller.
*
* Copyright (c) 2001 Brian S. Julin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL").
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
*
* References:
* HP-HIL Technical Reference Manual. Hewlett Packard Product No. 45918A
* System Device Controller Microprocessor Firmware Theory of Operation
* for Part Number 1820-4784 Revision B. Dwg No. A-1820-4784-2
*
*/
#include <linux/hil_mlc.h>
#include <linux/hp_sdc.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/semaphore.h>
#define PREFIX "HP SDC MLC: "
static hil_mlc hp_sdc_mlc;
MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
MODULE_DESCRIPTION("Glue for onboard HIL MLC in HP-PARISC machines");
MODULE_LICENSE("Dual BSD/GPL");
static struct hp_sdc_mlc_priv_s {
int emtestmode;
hp_sdc_transaction trans;
u8 tseq[16];
int got5x;
} hp_sdc_mlc_priv;
/************************* Interrupt context ******************************/
static void hp_sdc_mlc_isr (int irq, void *dev_id,
uint8_t status, uint8_t data)
{
int idx;
hil_mlc *mlc = &hp_sdc_mlc;
write_lock(&mlc->lock);
if (mlc->icount < 0) {
printk(KERN_WARNING PREFIX "HIL Overflow!\n");
up(&mlc->isem);
goto out;
}
idx = 15 - mlc->icount;
if ((status & HP_SDC_STATUS_IRQMASK) == HP_SDC_STATUS_HILDATA) {
mlc->ipacket[idx] |= data | HIL_ERR_INT;
mlc->icount--;
if (hp_sdc_mlc_priv.got5x || !idx)
goto check;
if ((mlc->ipacket[idx - 1] & HIL_PKT_ADDR_MASK) !=
(mlc->ipacket[idx] & HIL_PKT_ADDR_MASK)) {
mlc->ipacket[idx] &= ~HIL_PKT_ADDR_MASK;
mlc->ipacket[idx] |= (mlc->ipacket[idx - 1]
& HIL_PKT_ADDR_MASK);
}
goto check;
}
/* We know status is 5X */
if (data & HP_SDC_HIL_ISERR)
goto err;
mlc->ipacket[idx] =
(data & HP_SDC_HIL_R1MASK) << HIL_PKT_ADDR_SHIFT;
hp_sdc_mlc_priv.got5x = 1;
goto out;
check:
hp_sdc_mlc_priv.got5x = 0;
if (mlc->imatch == 0)
goto done;
if ((mlc->imatch == (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL))
&& (mlc->ipacket[idx] == (mlc->imatch | idx)))
goto done;
if (mlc->ipacket[idx] == mlc->imatch)
goto done;
goto out;
err:
printk(KERN_DEBUG PREFIX "err code %x\n", data);
switch (data) {
case HP_SDC_HIL_RC_DONE:
printk(KERN_WARNING PREFIX "Bastard SDC reconfigured loop!\n");
break;
case HP_SDC_HIL_ERR:
mlc->ipacket[idx] |= HIL_ERR_INT | HIL_ERR_PERR |
HIL_ERR_FERR | HIL_ERR_FOF;
break;
case HP_SDC_HIL_TO:
mlc->ipacket[idx] |= HIL_ERR_INT | HIL_ERR_LERR;
break;
case HP_SDC_HIL_RC:
printk(KERN_WARNING PREFIX "Bastard SDC decided to reconfigure loop!\n");
break;
default:
printk(KERN_WARNING PREFIX "Unkown HIL Error status (%x)!\n", data);
break;
}
/* No more data will be coming due to an error. */
done:
tasklet_schedule(mlc->tasklet);
up(&mlc->isem);
out:
write_unlock(&mlc->lock);
}
/******************** Tasklet or userspace context functions ****************/
static int hp_sdc_mlc_in(hil_mlc *mlc, suseconds_t timeout)
{
struct hp_sdc_mlc_priv_s *priv;
int rc = 2;
priv = mlc->priv;
/* Try to down the semaphore */
if (down_trylock(&mlc->isem)) {
struct timeval tv;
if (priv->emtestmode) {
mlc->ipacket[0] =
HIL_ERR_INT | (mlc->opacket &
(HIL_PKT_CMD |
HIL_PKT_ADDR_MASK |
HIL_PKT_DATA_MASK));
mlc->icount = 14;
/* printk(KERN_DEBUG PREFIX ">[%x]\n", mlc->ipacket[0]); */
goto wasup;
}
do_gettimeofday(&tv);
tv.tv_usec += USEC_PER_SEC * (tv.tv_sec - mlc->instart.tv_sec);
if (tv.tv_usec - mlc->instart.tv_usec > mlc->intimeout) {
/* printk("!%i %i",
tv.tv_usec - mlc->instart.tv_usec,
mlc->intimeout);
*/
rc = 1;
up(&mlc->isem);
}
goto done;
}
wasup:
up(&mlc->isem);
rc = 0;
done:
return rc;
}
static int hp_sdc_mlc_cts(hil_mlc *mlc)
{
struct hp_sdc_mlc_priv_s *priv;
priv = mlc->priv;
/* Try to down the semaphores -- they should be up. */
BUG_ON(down_trylock(&mlc->isem));
BUG_ON(down_trylock(&mlc->osem));
up(&mlc->isem);
up(&mlc->osem);
if (down_trylock(&mlc->csem)) {
if (priv->trans.act.semaphore != &mlc->csem)
goto poll;
else
goto busy;
}
if (!(priv->tseq[4] & HP_SDC_USE_LOOP))
goto done;
poll:
priv->trans.act.semaphore = &mlc->csem;
priv->trans.actidx = 0;
priv->trans.idx = 1;
priv->trans.endidx = 5;
priv->tseq[0] =
HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN | HP_SDC_ACT_SEMAPHORE;
priv->tseq[1] = HP_SDC_CMD_READ_USE;
priv->tseq[2] = 1;
priv->tseq[3] = 0;
priv->tseq[4] = 0;
__hp_sdc_enqueue_transaction(&priv->trans);
busy:
return 1;
done:
priv->trans.act.semaphore = &mlc->osem;
up(&mlc->csem);
return 0;
}
static void hp_sdc_mlc_out(hil_mlc *mlc)
{
struct hp_sdc_mlc_priv_s *priv;
priv = mlc->priv;
/* Try to down the semaphore -- it should be up. */
BUG_ON(down_trylock(&mlc->osem));
if (mlc->opacket & HIL_DO_ALTER_CTRL)
goto do_control;
do_data:
if (priv->emtestmode) {
up(&mlc->osem);
return;
}
/* Shouldn't be sending commands when loop may be busy */
BUG_ON(down_trylock(&mlc->csem));
up(&mlc->csem);
priv->trans.actidx = 0;
priv->trans.idx = 1;
priv->trans.act.semaphore = &mlc->osem;
priv->trans.endidx = 6;
priv->tseq[0] =
HP_SDC_ACT_DATAREG | HP_SDC_ACT_POSTCMD | HP_SDC_ACT_SEMAPHORE;
priv->tseq[1] = 0x7;
priv->tseq[2] =
(mlc->opacket &
(HIL_PKT_ADDR_MASK | HIL_PKT_CMD))
>> HIL_PKT_ADDR_SHIFT;
priv->tseq[3] =
(mlc->opacket & HIL_PKT_DATA_MASK)
>> HIL_PKT_DATA_SHIFT;
priv->tseq[4] = 0; /* No timeout */
if (priv->tseq[3] == HIL_CMD_DHR)
priv->tseq[4] = 1;
priv->tseq[5] = HP_SDC_CMD_DO_HIL;
goto enqueue;
do_control:
priv->emtestmode = mlc->opacket & HIL_CTRL_TEST;
/* we cannot emulate this, it should not be used. */
BUG_ON((mlc->opacket & (HIL_CTRL_APE | HIL_CTRL_IPF)) == HIL_CTRL_APE);
if ((mlc->opacket & HIL_CTRL_ONLY) == HIL_CTRL_ONLY)
goto control_only;
/* Should not send command/data after engaging APE */
BUG_ON(mlc->opacket & HIL_CTRL_APE);
/* Disengaging APE this way would not be valid either since
* the loop must be allowed to idle.
*
* So, it works out that we really never actually send control
* and data when using SDC, we just send the data.
*/
goto do_data;
control_only:
priv->trans.actidx = 0;
priv->trans.idx = 1;
priv->trans.act.semaphore = &mlc->osem;
priv->trans.endidx = 4;
priv->tseq[0] =
HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT | HP_SDC_ACT_SEMAPHORE;
priv->tseq[1] = HP_SDC_CMD_SET_LPC;
priv->tseq[2] = 1;
/* priv->tseq[3] = (mlc->ddc + 1) | HP_SDC_LPS_ACSUCC; */
priv->tseq[3] = 0;
if (mlc->opacket & HIL_CTRL_APE) {
priv->tseq[3] |= HP_SDC_LPC_APE_IPF;
BUG_ON(down_trylock(&mlc->csem));
}
enqueue:
hp_sdc_enqueue_transaction(&priv->trans);
}
static int __init hp_sdc_mlc_init(void)
{
hil_mlc *mlc = &hp_sdc_mlc;
#ifdef __mc68000__
if (!MACH_IS_HP300)
return -ENODEV;
#endif
printk(KERN_INFO PREFIX "Registering the System Domain Controller's HIL MLC.\n");
hp_sdc_mlc_priv.emtestmode = 0;
hp_sdc_mlc_priv.trans.seq = hp_sdc_mlc_priv.tseq;
hp_sdc_mlc_priv.trans.act.semaphore = &mlc->osem;
hp_sdc_mlc_priv.got5x = 0;
mlc->cts = &hp_sdc_mlc_cts;
mlc->in = &hp_sdc_mlc_in;
mlc->out = &hp_sdc_mlc_out;
mlc->priv = &hp_sdc_mlc_priv;
if (hil_mlc_register(mlc)) {
printk(KERN_WARNING PREFIX "Failed to register MLC structure with hil_mlc\n");
goto err0;
}
if (hp_sdc_request_hil_irq(&hp_sdc_mlc_isr)) {
printk(KERN_WARNING PREFIX "Request for raw HIL ISR hook denied\n");
goto err1;
}
return 0;
err1:
if (hil_mlc_unregister(mlc))
printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n"
"This is bad. Could cause an oops.\n");
err0:
return -EBUSY;
}
static void __exit hp_sdc_mlc_exit(void)
{
hil_mlc *mlc = &hp_sdc_mlc;
if (hp_sdc_release_hil_irq(&hp_sdc_mlc_isr))
printk(KERN_ERR PREFIX "Failed to release the raw HIL ISR hook.\n"
"This is bad. Could cause an oops.\n");
if (hil_mlc_unregister(mlc))
printk(KERN_ERR PREFIX "Failed to unregister MLC structure with hil_mlc.\n"
"This is bad. Could cause an oops.\n");
}
module_init(hp_sdc_mlc_init);
module_exit(hp_sdc_mlc_exit);

View File

@@ -0,0 +1,90 @@
#ifndef _I8042_IO_H
#define _I8042_IO_H
/*
* 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.
*/
/*
* Names.
*/
#define I8042_KBD_PHYS_DESC "isa0060/serio0"
#define I8042_AUX_PHYS_DESC "isa0060/serio1"
#define I8042_MUX_PHYS_DESC "isa0060/serio%d"
/*
* IRQs.
*/
#ifdef __alpha__
# define I8042_KBD_IRQ 1
# define I8042_AUX_IRQ (RTC_PORT(0) == 0x170 ? 9 : 12) /* Jensen is special */
#elif defined(__arm__)
/* defined in include/asm-arm/arch-xxx/irqs.h */
#include <asm/irq.h>
#elif defined(CONFIG_SH_CAYMAN)
#include <asm/irq.h>
#else
# define I8042_KBD_IRQ 1
# define I8042_AUX_IRQ 12
#endif
/*
* Register numbers.
*/
#define I8042_COMMAND_REG 0x64
#define I8042_STATUS_REG 0x64
#define I8042_DATA_REG 0x60
static inline int i8042_read_data(void)
{
return inb(I8042_DATA_REG);
}
static inline int i8042_read_status(void)
{
return inb(I8042_STATUS_REG);
}
static inline void i8042_write_data(int val)
{
outb(val, I8042_DATA_REG);
}
static inline void i8042_write_command(int val)
{
outb(val, I8042_COMMAND_REG);
}
static inline int i8042_platform_init(void)
{
/*
* On some platforms touching the i8042 data register region can do really
* bad things. Because of this the region is always reserved on such boxes.
*/
#if defined(CONFIG_PPC)
if (check_legacy_ioport(I8042_DATA_REG))
return -ENODEV;
#endif
#if !defined(__sh__) && !defined(__alpha__) && !defined(__mips__)
if (!request_region(I8042_DATA_REG, 16, "i8042"))
return -EBUSY;
#endif
i8042_reset = 1;
return 0;
}
static inline void i8042_platform_exit(void)
{
#if !defined(__sh__) && !defined(__alpha__)
release_region(I8042_DATA_REG, 16);
#endif
}
#endif /* _I8042_IO_H */

View File

@@ -0,0 +1,76 @@
#ifndef _I8042_IP22_H
#define _I8042_IP22_H
#include <asm/sgi/ioc.h>
#include <asm/sgi/ip22.h>
/*
* 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.
*/
/*
* Names.
*/
#define I8042_KBD_PHYS_DESC "hpc3ps2/serio0"
#define I8042_AUX_PHYS_DESC "hpc3ps2/serio1"
#define I8042_MUX_PHYS_DESC "hpc3ps2/serio%d"
/*
* IRQs.
*/
#define I8042_KBD_IRQ SGI_KEYBD_IRQ
#define I8042_AUX_IRQ SGI_KEYBD_IRQ
/*
* Register numbers.
*/
#define I8042_COMMAND_REG ((unsigned long)&sgioc->kbdmouse.command)
#define I8042_STATUS_REG ((unsigned long)&sgioc->kbdmouse.command)
#define I8042_DATA_REG ((unsigned long)&sgioc->kbdmouse.data)
static inline int i8042_read_data(void)
{
return sgioc->kbdmouse.data;
}
static inline int i8042_read_status(void)
{
return sgioc->kbdmouse.command;
}
static inline void i8042_write_data(int val)
{
sgioc->kbdmouse.data = val;
}
static inline void i8042_write_command(int val)
{
sgioc->kbdmouse.command = val;
}
static inline int i8042_platform_init(void)
{
#if 0
/* XXX sgi_kh is a virtual address */
if (!request_mem_region(sgi_kh, sizeof(struct hpc_keyb), "i8042"))
return -EBUSY;
#endif
i8042_reset = 1;
return 0;
}
static inline void i8042_platform_exit(void)
{
#if 0
release_mem_region(JAZZ_KEYBOARD_ADDRESS, sizeof(struct hpc_keyb));
#endif
}
#endif /* _I8042_IP22_H */

View File

@@ -0,0 +1,69 @@
#ifndef _I8042_JAZZ_H
#define _I8042_JAZZ_H
#include <asm/jazz.h>
/*
* 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.
*/
/*
* Names.
*/
#define I8042_KBD_PHYS_DESC "R4030/serio0"
#define I8042_AUX_PHYS_DESC "R4030/serio1"
#define I8042_MUX_PHYS_DESC "R4030/serio%d"
/*
* IRQs.
*/
#define I8042_KBD_IRQ JAZZ_KEYBOARD_IRQ
#define I8042_AUX_IRQ JAZZ_MOUSE_IRQ
#define I8042_COMMAND_REG ((unsigned long)&jazz_kh->command)
#define I8042_STATUS_REG ((unsigned long)&jazz_kh->command)
#define I8042_DATA_REG ((unsigned long)&jazz_kh->data)
static inline int i8042_read_data(void)
{
return jazz_kh->data;
}
static inline int i8042_read_status(void)
{
return jazz_kh->command;
}
static inline void i8042_write_data(int val)
{
jazz_kh->data = val;
}
static inline void i8042_write_command(int val)
{
jazz_kh->command = val;
}
static inline int i8042_platform_init(void)
{
#if 0
/* XXX JAZZ_KEYBOARD_ADDRESS is a virtual address */
if (!request_mem_region(JAZZ_KEYBOARD_ADDRESS, 2, "i8042"))
return -EBUSY;
#endif
return 0;
}
static inline void i8042_platform_exit(void)
{
#if 0
release_mem_region(JAZZ_KEYBOARD_ADDRESS, 2);
#endif
}
#endif /* _I8042_JAZZ_H */

View File

@@ -0,0 +1,136 @@
#ifndef _I8042_PPCIO_H
#define _I8042_PPCIO_H
/*
* 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.
*/
#if defined(CONFIG_WALNUT)
#define I8042_KBD_IRQ 25
#define I8042_AUX_IRQ 26
#define I8042_KBD_PHYS_DESC "walnutps2/serio0"
#define I8042_AUX_PHYS_DESC "walnutps2/serio1"
#define I8042_MUX_PHYS_DESC "walnutps2/serio%d"
extern void *kb_cs;
extern void *kb_data;
#define I8042_COMMAND_REG (*(int *)kb_cs)
#define I8042_DATA_REG (*(int *)kb_data)
static inline int i8042_read_data(void)
{
return readb(kb_data);
}
static inline int i8042_read_status(void)
{
return readb(kb_cs);
}
static inline void i8042_write_data(int val)
{
writeb(val, kb_data);
}
static inline void i8042_write_command(int val)
{
writeb(val, kb_cs);
}
static inline int i8042_platform_init(void)
{
i8042_reset = 1;
return 0;
}
static inline void i8042_platform_exit(void)
{
}
#elif defined(CONFIG_SPRUCE)
#define I8042_KBD_IRQ 22
#define I8042_AUX_IRQ 21
#define I8042_KBD_PHYS_DESC "spruceps2/serio0"
#define I8042_AUX_PHYS_DESC "spruceps2/serio1"
#define I8042_MUX_PHYS_DESC "spruceps2/serio%d"
#define I8042_COMMAND_REG 0xff810000
#define I8042_DATA_REG 0xff810001
static inline int i8042_read_data(void)
{
unsigned long kbd_data;
__raw_writel(0x00000088, 0xff500008);
eieio();
__raw_writel(0x03000000, 0xff50000c);
eieio();
asm volatile("lis 7,0xff88 \n\
lswi 6,7,0x8 \n\
mr %0,6"
: "=r" (kbd_data) :: "6", "7");
__raw_writel(0x00000000, 0xff50000c);
eieio();
return (unsigned char)(kbd_data >> 24);
}
static inline int i8042_read_status(void)
{
unsigned long kbd_status;
__raw_writel(0x00000088, 0xff500008);
eieio();
__raw_writel(0x03000000, 0xff50000c);
eieio();
asm volatile("lis 7,0xff88 \n\
ori 7,7,0x8 \n\
lswi 6,7,0x8 \n\
mr %0,6"
: "=r" (kbd_status) :: "6", "7");
__raw_writel(0x00000000, 0xff50000c);
eieio();
return (unsigned char)(kbd_status >> 24);
}
static inline void i8042_write_data(int val)
{
*((unsigned char *)0xff810000) = (char)val;
}
static inline void i8042_write_command(int val)
{
*((unsigned char *)0xff810001) = (char)val;
}
static inline int i8042_platform_init(void)
{
i8042_reset = 1;
return 0;
}
static inline void i8042_platform_exit(void)
{
}
#else
#include "i8042-io.h"
#endif
#endif /* _I8042_PPCIO_H */

View File

@@ -0,0 +1,75 @@
#ifndef _I8042_SNIRM_H
#define _I8042_SNIRM_H
#include <asm/sni.h>
/*
* 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.
*/
/*
* Names.
*/
#define I8042_KBD_PHYS_DESC "onboard/serio0"
#define I8042_AUX_PHYS_DESC "onboard/serio1"
#define I8042_MUX_PHYS_DESC "onboard/serio%d"
/*
* IRQs.
*/
static int i8042_kbd_irq;
static int i8042_aux_irq;
#define I8042_KBD_IRQ i8042_kbd_irq
#define I8042_AUX_IRQ i8042_aux_irq
static void __iomem *kbd_iobase;
#define I8042_COMMAND_REG (kbd_iobase + 0x64UL)
#define I8042_DATA_REG (kbd_iobase + 0x60UL)
static inline int i8042_read_data(void)
{
return readb(kbd_iobase + 0x60UL);
}
static inline int i8042_read_status(void)
{
return readb(kbd_iobase + 0x64UL);
}
static inline void i8042_write_data(int val)
{
writeb(val, kbd_iobase + 0x60UL);
}
static inline void i8042_write_command(int val)
{
writeb(val, kbd_iobase + 0x64UL);
}
static inline int i8042_platform_init(void)
{
/* RM200 is strange ... */
if (sni_brd_type == SNI_BRD_RM200) {
kbd_iobase = ioremap(0x16000000, 4);
i8042_kbd_irq = 33;
i8042_aux_irq = 44;
} else {
kbd_iobase = ioremap(0x14000000, 4);
i8042_kbd_irq = 1;
i8042_aux_irq = 12;
}
if (!kbd_iobase)
return -ENOMEM;
return 0;
}
static inline void i8042_platform_exit(void)
{
}
#endif /* _I8042_SNIRM_H */

View File

@@ -0,0 +1,155 @@
#ifndef _I8042_SPARCIO_H
#define _I8042_SPARCIO_H
#include <linux/of_device.h>
#include <asm/io.h>
#include <asm/oplib.h>
#include <asm/prom.h>
static int i8042_kbd_irq = -1;
static int i8042_aux_irq = -1;
#define I8042_KBD_IRQ i8042_kbd_irq
#define I8042_AUX_IRQ i8042_aux_irq
#define I8042_KBD_PHYS_DESC "sparcps2/serio0"
#define I8042_AUX_PHYS_DESC "sparcps2/serio1"
#define I8042_MUX_PHYS_DESC "sparcps2/serio%d"
static void __iomem *kbd_iobase;
static struct resource *kbd_res;
#define I8042_COMMAND_REG (kbd_iobase + 0x64UL)
#define I8042_DATA_REG (kbd_iobase + 0x60UL)
static inline int i8042_read_data(void)
{
return readb(kbd_iobase + 0x60UL);
}
static inline int i8042_read_status(void)
{
return readb(kbd_iobase + 0x64UL);
}
static inline void i8042_write_data(int val)
{
writeb(val, kbd_iobase + 0x60UL);
}
static inline void i8042_write_command(int val)
{
writeb(val, kbd_iobase + 0x64UL);
}
#ifdef CONFIG_PCI
#define OBP_PS2KBD_NAME1 "kb_ps2"
#define OBP_PS2KBD_NAME2 "keyboard"
#define OBP_PS2MS_NAME1 "kdmouse"
#define OBP_PS2MS_NAME2 "mouse"
static int __devinit sparc_i8042_probe(struct of_device *op, const struct of_device_id *match)
{
struct device_node *dp = op->node;
dp = dp->child;
while (dp) {
if (!strcmp(dp->name, OBP_PS2KBD_NAME1) ||
!strcmp(dp->name, OBP_PS2KBD_NAME2)) {
struct of_device *kbd = of_find_device_by_node(dp);
unsigned int irq = kbd->irqs[0];
if (irq == 0xffffffff)
irq = op->irqs[0];
i8042_kbd_irq = irq;
kbd_iobase = of_ioremap(&kbd->resource[0],
0, 8, "kbd");
kbd_res = &kbd->resource[0];
} else if (!strcmp(dp->name, OBP_PS2MS_NAME1) ||
!strcmp(dp->name, OBP_PS2MS_NAME2)) {
struct of_device *ms = of_find_device_by_node(dp);
unsigned int irq = ms->irqs[0];
if (irq == 0xffffffff)
irq = op->irqs[0];
i8042_aux_irq = irq;
}
dp = dp->sibling;
}
return 0;
}
static int __devexit sparc_i8042_remove(struct of_device *op)
{
of_iounmap(kbd_res, kbd_iobase, 8);
return 0;
}
static const struct of_device_id sparc_i8042_match[] = {
{
.name = "8042",
},
{},
};
MODULE_DEVICE_TABLE(of, sparc_i8042_match);
static struct of_platform_driver sparc_i8042_driver = {
.name = "i8042",
.match_table = sparc_i8042_match,
.probe = sparc_i8042_probe,
.remove = __devexit_p(sparc_i8042_remove),
};
static int __init i8042_platform_init(void)
{
struct device_node *root = of_find_node_by_path("/");
if (!strcmp(root->name, "SUNW,JavaStation-1")) {
/* Hardcoded values for MrCoffee. */
i8042_kbd_irq = i8042_aux_irq = 13 | 0x20;
kbd_iobase = ioremap(0x71300060, 8);
if (!kbd_iobase)
return -ENODEV;
} else {
int err = of_register_driver(&sparc_i8042_driver,
&of_bus_type);
if (err)
return err;
if (i8042_kbd_irq == -1 ||
i8042_aux_irq == -1) {
if (kbd_iobase) {
of_iounmap(kbd_res, kbd_iobase, 8);
kbd_iobase = (void __iomem *) NULL;
}
return -ENODEV;
}
}
i8042_reset = 1;
return 0;
}
static inline void i8042_platform_exit(void)
{
struct device_node *root = of_find_node_by_path("/");
if (strcmp(root->name, "SUNW,JavaStation-1"))
of_unregister_driver(&sparc_i8042_driver);
}
#else /* !CONFIG_PCI */
static int __init i8042_platform_init(void)
{
return -ENODEV;
}
static inline void i8042_platform_exit(void)
{
}
#endif /* !CONFIG_PCI */
#endif /* _I8042_SPARCIO_H */

View File

@@ -0,0 +1,902 @@
#ifndef _I8042_X86IA64IO_H
#define _I8042_X86IA64IO_H
/*
* 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.
*/
/*
* Names.
*/
#define I8042_KBD_PHYS_DESC "isa0060/serio0"
#define I8042_AUX_PHYS_DESC "isa0060/serio1"
#define I8042_MUX_PHYS_DESC "isa0060/serio%d"
/*
* IRQs.
*/
#if defined(__ia64__)
# define I8042_MAP_IRQ(x) isa_irq_to_vector((x))
#else
# define I8042_MAP_IRQ(x) (x)
#endif
#define I8042_KBD_IRQ i8042_kbd_irq
#define I8042_AUX_IRQ i8042_aux_irq
static int i8042_kbd_irq;
static int i8042_aux_irq;
/*
* Register numbers.
*/
#define I8042_COMMAND_REG i8042_command_reg
#define I8042_STATUS_REG i8042_command_reg
#define I8042_DATA_REG i8042_data_reg
static int i8042_command_reg = 0x64;
static int i8042_data_reg = 0x60;
static inline int i8042_read_data(void)
{
return inb(I8042_DATA_REG);
}
static inline int i8042_read_status(void)
{
return inb(I8042_STATUS_REG);
}
static inline void i8042_write_data(int val)
{
outb(val, I8042_DATA_REG);
}
static inline void i8042_write_command(int val)
{
outb(val, I8042_COMMAND_REG);
}
#ifdef CONFIG_X86
#include <linux/dmi.h>
static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = {
{
/*
* Arima-Rioworks HDAMB -
* AUX LOOP command does not raise AUX IRQ
*/
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "RIOWORKS"),
DMI_MATCH(DMI_BOARD_NAME, "HDAMB"),
DMI_MATCH(DMI_BOARD_VERSION, "Rev E"),
},
},
{
/* ASUS G1S */
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."),
DMI_MATCH(DMI_BOARD_NAME, "G1S"),
DMI_MATCH(DMI_BOARD_VERSION, "1.0"),
},
},
{
/* ASUS P65UP5 - AUX LOOP command does not raise AUX IRQ */
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
DMI_MATCH(DMI_BOARD_NAME, "P/I-P65UP5"),
DMI_MATCH(DMI_BOARD_VERSION, "REV 2.X"),
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Compaq"),
DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"),
DMI_MATCH(DMI_PRODUCT_VERSION, "8500"),
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Compaq"),
DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"),
DMI_MATCH(DMI_PRODUCT_VERSION, "DL760"),
},
},
{
/* OQO Model 01 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "OQO"),
DMI_MATCH(DMI_PRODUCT_NAME, "ZEPTO"),
DMI_MATCH(DMI_PRODUCT_VERSION, "00"),
},
},
{
/* ULI EV4873 - AUX LOOP does not work properly */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ULI"),
DMI_MATCH(DMI_PRODUCT_NAME, "EV4873"),
DMI_MATCH(DMI_PRODUCT_VERSION, "5a"),
},
},
{
/* Microsoft Virtual Machine */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"),
DMI_MATCH(DMI_PRODUCT_VERSION, "VS2005R2"),
},
},
{
/* Medion MAM 2070 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Notebook"),
DMI_MATCH(DMI_PRODUCT_NAME, "MAM 2070"),
DMI_MATCH(DMI_PRODUCT_VERSION, "5a"),
},
},
{
/* Blue FB5601 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "blue"),
DMI_MATCH(DMI_PRODUCT_NAME, "FB5601"),
DMI_MATCH(DMI_PRODUCT_VERSION, "M606"),
},
},
{
/* Gigabyte M912 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
DMI_MATCH(DMI_PRODUCT_NAME, "M912"),
DMI_MATCH(DMI_PRODUCT_VERSION, "01"),
},
},
{
/* Gigabyte M1022M netbook */
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co.,Ltd."),
DMI_MATCH(DMI_BOARD_NAME, "M1022E"),
DMI_MATCH(DMI_BOARD_VERSION, "1.02"),
},
},
{
/* Gigabyte Spring Peak - defines wrong chassis type */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
DMI_MATCH(DMI_PRODUCT_NAME, "Spring Peak"),
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv9700"),
DMI_MATCH(DMI_PRODUCT_VERSION, "Rev 1"),
},
},
{ }
};
/*
* Some Fujitsu notebooks are having trouble with touchpads if
* active multiplexing mode is activated. Luckily they don't have
* external PS/2 ports so we can safely disable it.
* ... apparently some Toshibas don't like MUX mode either and
* die horrible death on reboot.
*/
static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {
{
/* Fujitsu Lifebook P7010/P7010D */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "P7010"),
},
},
{
/* Fujitsu Lifebook P7010 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
DMI_MATCH(DMI_PRODUCT_NAME, "0000000000"),
},
},
{
/* Fujitsu Lifebook P5020D */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P Series"),
},
},
{
/* Fujitsu Lifebook S2000 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S Series"),
},
},
{
/* Fujitsu Lifebook S6230 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S6230"),
},
},
{
/* Fujitsu T70H */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "FMVLT70H"),
},
},
{
/* Fujitsu-Siemens Lifebook T3010 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T3010"),
},
},
{
/* Fujitsu-Siemens Lifebook E4010 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E4010"),
},
},
{
/* Fujitsu-Siemens Amilo Pro 2010 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2010"),
},
},
{
/* Fujitsu-Siemens Amilo Pro 2030 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
DMI_MATCH(DMI_PRODUCT_NAME, "AMILO PRO V2030"),
},
},
{
/*
* No data is coming from the touchscreen unless KBC
* is in legacy mode.
*/
/* Panasonic CF-29 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"),
DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"),
},
},
{
/*
* HP Pavilion DV4017EA -
* errors on MUX ports are reported without raising AUXDATA
* causing "spurious NAK" messages.
*/
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EA032EA#ABF)"),
},
},
{
/*
* HP Pavilion ZT1000 -
* like DV4017EA does not raise AUXERR for errors on MUX ports.
*/
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion Notebook PC"),
DMI_MATCH(DMI_PRODUCT_VERSION, "HP Pavilion Notebook ZT1000"),
},
},
{
/*
* HP Pavilion DV4270ca -
* like DV4017EA does not raise AUXERR for errors on MUX ports.
*/
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EH476UA#ABL)"),
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P10"),
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
DMI_MATCH(DMI_PRODUCT_NAME, "EQUIUM A110"),
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ALIENWARE"),
DMI_MATCH(DMI_PRODUCT_NAME, "Sentia"),
},
},
{
/* Sharp Actius MM20 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "SHARP"),
DMI_MATCH(DMI_PRODUCT_NAME, "PC-MM20 Series"),
},
},
{
/* Sony Vaio VPCZ122GX */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "VPCZ122GX"),
},
},
{
/* Sony Vaio FS-115b */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FS115B"),
},
},
{
/*
* Sony Vaio FZ-240E -
* reset and GET ID commands issued via KBD port are
* sometimes being delivered to AUX3.
*/
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ240E"),
},
},
{
/* Amoi M636/A737 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Amoi Electronics CO.,LTD."),
DMI_MATCH(DMI_PRODUCT_NAME, "M636/A737 platform"),
},
},
{
/* Lenovo 3000 n100 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "076804U"),
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"),
},
},
{
/* Gericom Bellagio */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Gericom"),
DMI_MATCH(DMI_PRODUCT_NAME, "N34AS6"),
},
},
{
/* IBM 2656 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "IBM"),
DMI_MATCH(DMI_PRODUCT_NAME, "2656"),
},
},
{
/* Dell XPS M1530 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "XPS M1530"),
},
},
{
/* Compal HEL80I */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "COMPAL"),
DMI_MATCH(DMI_PRODUCT_NAME, "HEL80I"),
},
},
{
/* Dell Vostro 1510 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro1510"),
},
},
{
/* Acer Aspire 5536 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5536"),
DMI_MATCH(DMI_PRODUCT_VERSION, "0100"),
},
},
{
/* Dell Vostro V13 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"),
},
},
{ }
};
static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = {
{
/* MSI Wind U-100 */
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "U-100"),
DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"),
},
},
{
/* LG Electronics X110 */
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "X110"),
DMI_MATCH(DMI_BOARD_VENDOR, "LG Electronics Inc."),
},
},
{
/* Acer Aspire One 150 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "AOA150"),
},
},
{
/* Advent 4211 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "DIXONSXP"),
DMI_MATCH(DMI_PRODUCT_NAME, "Advent 4211"),
},
},
{
/* Medion Akoya Mini E1210 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
DMI_MATCH(DMI_PRODUCT_NAME, "E1210"),
},
},
{
/* Medion Akoya E1222 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
DMI_MATCH(DMI_PRODUCT_NAME, "E122X"),
},
},
{
/* Mivvy M310 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "VIOOO"),
DMI_MATCH(DMI_PRODUCT_NAME, "N10"),
},
},
{
/* Dell Vostro 1320 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1320"),
},
},
{
/* Dell Vostro 1520 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1520"),
},
},
{
/* Dell Vostro 1720 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1720"),
},
},
{ }
};
#ifdef CONFIG_PNP
static const struct dmi_system_id __initconst i8042_dmi_nopnp_table[] = {
{
/* Intel MBO Desktop D845PESV */
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "D845PESV"),
DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"),
},
},
{
/* MSI Wind U-100 */
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "U-100"),
DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"),
},
},
{ }
};
static const struct dmi_system_id __initconst i8042_dmi_laptop_table[] = {
{
.matches = {
DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */
},
},
{
.matches = {
DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */
},
},
{
.matches = {
DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */
},
},
{
.matches = {
DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */
},
},
{ }
};
#endif
static const struct dmi_system_id __initconst i8042_dmi_notimeout_table[] = {
{
/* Dell Vostro V13 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"),
},
},
{ }
};
/*
* Some Wistron based laptops need us to explicitly enable the 'Dritek
* keyboard extension' to make their extra keys start generating scancodes.
* Originally, this was just confined to older laptops, but a few Acer laptops
* have turned up in 2007 that also need this again.
*/
static const struct dmi_system_id __initconst i8042_dmi_dritek_table[] = {
{
/* Acer Aspire 5610 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"),
},
},
{
/* Acer Aspire 5630 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"),
},
},
{
/* Acer Aspire 5650 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"),
},
},
{
/* Acer Aspire 5680 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"),
},
},
{
/* Acer Aspire 5720 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5720"),
},
},
{
/* Acer Aspire 9110 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"),
},
},
{
/* Acer TravelMate 660 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 660"),
},
},
{
/* Acer TravelMate 2490 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"),
},
},
{
/* Acer TravelMate 4280 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4280"),
},
},
{ }
};
#endif /* CONFIG_X86 */
#ifdef CONFIG_PNP
#include <linux/pnp.h>
static bool i8042_pnp_kbd_registered;
static unsigned int i8042_pnp_kbd_devices;
static bool i8042_pnp_aux_registered;
static unsigned int i8042_pnp_aux_devices;
static int i8042_pnp_command_reg;
static int i8042_pnp_data_reg;
static int i8042_pnp_kbd_irq;
static int i8042_pnp_aux_irq;
static char i8042_pnp_kbd_name[32];
static char i8042_pnp_aux_name[32];
static int i8042_pnp_kbd_probe(struct pnp_dev *dev, const struct pnp_device_id *did)
{
if (pnp_port_valid(dev, 0) && pnp_port_len(dev, 0) == 1)
i8042_pnp_data_reg = pnp_port_start(dev,0);
if (pnp_port_valid(dev, 1) && pnp_port_len(dev, 1) == 1)
i8042_pnp_command_reg = pnp_port_start(dev, 1);
if (pnp_irq_valid(dev,0))
i8042_pnp_kbd_irq = pnp_irq(dev, 0);
strlcpy(i8042_pnp_kbd_name, did->id, sizeof(i8042_pnp_kbd_name));
if (strlen(pnp_dev_name(dev))) {
strlcat(i8042_pnp_kbd_name, ":", sizeof(i8042_pnp_kbd_name));
strlcat(i8042_pnp_kbd_name, pnp_dev_name(dev), sizeof(i8042_pnp_kbd_name));
}
i8042_pnp_kbd_devices++;
return 0;
}
static int i8042_pnp_aux_probe(struct pnp_dev *dev, const struct pnp_device_id *did)
{
if (pnp_port_valid(dev, 0) && pnp_port_len(dev, 0) == 1)
i8042_pnp_data_reg = pnp_port_start(dev,0);
if (pnp_port_valid(dev, 1) && pnp_port_len(dev, 1) == 1)
i8042_pnp_command_reg = pnp_port_start(dev, 1);
if (pnp_irq_valid(dev, 0))
i8042_pnp_aux_irq = pnp_irq(dev, 0);
strlcpy(i8042_pnp_aux_name, did->id, sizeof(i8042_pnp_aux_name));
if (strlen(pnp_dev_name(dev))) {
strlcat(i8042_pnp_aux_name, ":", sizeof(i8042_pnp_aux_name));
strlcat(i8042_pnp_aux_name, pnp_dev_name(dev), sizeof(i8042_pnp_aux_name));
}
i8042_pnp_aux_devices++;
return 0;
}
static struct pnp_device_id pnp_kbd_devids[] = {
{ .id = "PNP0303", .driver_data = 0 },
{ .id = "PNP030b", .driver_data = 0 },
{ .id = "", },
};
static struct pnp_driver i8042_pnp_kbd_driver = {
.name = "i8042 kbd",
.id_table = pnp_kbd_devids,
.probe = i8042_pnp_kbd_probe,
};
static struct pnp_device_id pnp_aux_devids[] = {
{ .id = "FJC6000", .driver_data = 0 },
{ .id = "FJC6001", .driver_data = 0 },
{ .id = "PNP0f03", .driver_data = 0 },
{ .id = "PNP0f0b", .driver_data = 0 },
{ .id = "PNP0f0e", .driver_data = 0 },
{ .id = "PNP0f12", .driver_data = 0 },
{ .id = "PNP0f13", .driver_data = 0 },
{ .id = "PNP0f19", .driver_data = 0 },
{ .id = "PNP0f1c", .driver_data = 0 },
{ .id = "SYN0801", .driver_data = 0 },
{ .id = "", },
};
static struct pnp_driver i8042_pnp_aux_driver = {
.name = "i8042 aux",
.id_table = pnp_aux_devids,
.probe = i8042_pnp_aux_probe,
};
static void i8042_pnp_exit(void)
{
if (i8042_pnp_kbd_registered) {
i8042_pnp_kbd_registered = false;
pnp_unregister_driver(&i8042_pnp_kbd_driver);
}
if (i8042_pnp_aux_registered) {
i8042_pnp_aux_registered = false;
pnp_unregister_driver(&i8042_pnp_aux_driver);
}
}
static int __init i8042_pnp_init(void)
{
char kbd_irq_str[4] = { 0 }, aux_irq_str[4] = { 0 };
bool pnp_data_busted = false;
int err;
#ifdef CONFIG_X86
if (dmi_check_system(i8042_dmi_nopnp_table))
i8042_nopnp = true;
#endif
if (i8042_nopnp) {
printk(KERN_INFO "i8042: PNP detection disabled\n");
return 0;
}
err = pnp_register_driver(&i8042_pnp_kbd_driver);
if (!err)
i8042_pnp_kbd_registered = true;
err = pnp_register_driver(&i8042_pnp_aux_driver);
if (!err)
i8042_pnp_aux_registered = true;
if (!i8042_pnp_kbd_devices && !i8042_pnp_aux_devices) {
i8042_pnp_exit();
#if defined(__ia64__)
return -ENODEV;
#else
printk(KERN_INFO "PNP: No PS/2 controller found. Probing ports directly.\n");
return 0;
#endif
}
if (i8042_pnp_kbd_devices)
snprintf(kbd_irq_str, sizeof(kbd_irq_str),
"%d", i8042_pnp_kbd_irq);
if (i8042_pnp_aux_devices)
snprintf(aux_irq_str, sizeof(aux_irq_str),
"%d", i8042_pnp_aux_irq);
printk(KERN_INFO "PNP: PS/2 Controller [%s%s%s] at %#x,%#x irq %s%s%s\n",
i8042_pnp_kbd_name, (i8042_pnp_kbd_devices && i8042_pnp_aux_devices) ? "," : "",
i8042_pnp_aux_name,
i8042_pnp_data_reg, i8042_pnp_command_reg,
kbd_irq_str, (i8042_pnp_kbd_devices && i8042_pnp_aux_devices) ? "," : "",
aux_irq_str);
#if defined(__ia64__)
if (!i8042_pnp_kbd_devices)
i8042_nokbd = true;
if (!i8042_pnp_aux_devices)
i8042_noaux = true;
#endif
if (((i8042_pnp_data_reg & ~0xf) == (i8042_data_reg & ~0xf) &&
i8042_pnp_data_reg != i8042_data_reg) ||
!i8042_pnp_data_reg) {
printk(KERN_WARNING
"PNP: PS/2 controller has invalid data port %#x; "
"using default %#x\n",
i8042_pnp_data_reg, i8042_data_reg);
i8042_pnp_data_reg = i8042_data_reg;
pnp_data_busted = true;
}
if (((i8042_pnp_command_reg & ~0xf) == (i8042_command_reg & ~0xf) &&
i8042_pnp_command_reg != i8042_command_reg) ||
!i8042_pnp_command_reg) {
printk(KERN_WARNING
"PNP: PS/2 controller has invalid command port %#x; "
"using default %#x\n",
i8042_pnp_command_reg, i8042_command_reg);
i8042_pnp_command_reg = i8042_command_reg;
pnp_data_busted = true;
}
if (!i8042_nokbd && !i8042_pnp_kbd_irq) {
printk(KERN_WARNING
"PNP: PS/2 controller doesn't have KBD irq; "
"using default %d\n", i8042_kbd_irq);
i8042_pnp_kbd_irq = i8042_kbd_irq;
pnp_data_busted = true;
}
if (!i8042_noaux && !i8042_pnp_aux_irq) {
if (!pnp_data_busted && i8042_pnp_kbd_irq) {
printk(KERN_WARNING
"PNP: PS/2 appears to have AUX port disabled, "
"if this is incorrect please boot with "
"i8042.nopnp\n");
i8042_noaux = true;
} else {
printk(KERN_WARNING
"PNP: PS/2 controller doesn't have AUX irq; "
"using default %d\n", i8042_aux_irq);
i8042_pnp_aux_irq = i8042_aux_irq;
}
}
i8042_data_reg = i8042_pnp_data_reg;
i8042_command_reg = i8042_pnp_command_reg;
i8042_kbd_irq = i8042_pnp_kbd_irq;
i8042_aux_irq = i8042_pnp_aux_irq;
#ifdef CONFIG_X86
i8042_bypass_aux_irq_test = !pnp_data_busted &&
dmi_check_system(i8042_dmi_laptop_table);
#endif
return 0;
}
#else
static inline int i8042_pnp_init(void) { return 0; }
static inline void i8042_pnp_exit(void) { }
#endif
static int __init i8042_platform_init(void)
{
int retval;
/*
* On ix86 platforms touching the i8042 data register region can do really
* bad things. Because of this the region is always reserved on ix86 boxes.
*
* if (!request_region(I8042_DATA_REG, 16, "i8042"))
* return -EBUSY;
*/
i8042_kbd_irq = I8042_MAP_IRQ(1);
i8042_aux_irq = I8042_MAP_IRQ(12);
retval = i8042_pnp_init();
if (retval)
return retval;
#if defined(__ia64__)
i8042_reset = true;
#endif
#ifdef CONFIG_X86
if (dmi_check_system(i8042_dmi_reset_table))
i8042_reset = true;
if (dmi_check_system(i8042_dmi_noloop_table))
i8042_noloop = true;
if (dmi_check_system(i8042_dmi_nomux_table))
i8042_nomux = true;
if (dmi_check_system(i8042_dmi_notimeout_table))
i8042_notimeout = true;
if (dmi_check_system(i8042_dmi_dritek_table))
i8042_dritek = true;
#endif /* CONFIG_X86 */
return retval;
}
static inline void i8042_platform_exit(void)
{
i8042_pnp_exit();
}
#endif /* _I8042_X86IA64IO_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,103 @@
#ifndef _I8042_H
#define _I8042_H
/*
* Copyright (c) 1999-2002 Vojtech Pavlik
*
* 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.
*/
/*
* Arch-dependent inline functions and defines.
*/
#if defined(CONFIG_MACH_JAZZ)
#include "i8042-jazzio.h"
#elif defined(CONFIG_SGI_HAS_I8042)
#include "i8042-ip22io.h"
#elif defined(CONFIG_SNI_RM)
#include "i8042-snirm.h"
#elif defined(CONFIG_PPC)
#include "i8042-ppcio.h"
#elif defined(CONFIG_SPARC)
#include "i8042-sparcio.h"
#elif defined(CONFIG_X86) || defined(CONFIG_IA64)
#include "i8042-x86ia64io.h"
#else
#include "i8042-io.h"
#endif
/*
* This is in 50us units, the time we wait for the i8042 to react. This
* has to be long enough for the i8042 itself to timeout on sending a byte
* to a non-existent mouse.
*/
#define I8042_CTL_TIMEOUT 10000
/*
* Status register bits.
*/
#define I8042_STR_PARITY 0x80
#define I8042_STR_TIMEOUT 0x40
#define I8042_STR_AUXDATA 0x20
#define I8042_STR_KEYLOCK 0x10
#define I8042_STR_CMDDAT 0x08
#define I8042_STR_MUXERR 0x04
#define I8042_STR_IBF 0x02
#define I8042_STR_OBF 0x01
/*
* Control register bits.
*/
#define I8042_CTR_KBDINT 0x01
#define I8042_CTR_AUXINT 0x02
#define I8042_CTR_IGNKEYLOCK 0x08
#define I8042_CTR_KBDDIS 0x10
#define I8042_CTR_AUXDIS 0x20
#define I8042_CTR_XLATE 0x40
/*
* Return codes.
*/
#define I8042_RET_CTL_TEST 0x55
/*
* Expected maximum internal i8042 buffer size. This is used for flushing
* the i8042 buffers.
*/
#define I8042_BUFFER_SIZE 16
/*
* Number of AUX ports on controllers supporting active multiplexing
* specification
*/
#define I8042_NUM_MUX_PORTS 4
/*
* Debug.
*/
#ifdef DEBUG
static unsigned long i8042_start_time;
#define dbg_init() do { i8042_start_time = jiffies; } while (0)
#define dbg(format, arg...) \
do { \
if (i8042_debug) \
printk(KERN_DEBUG __FILE__ ": " format " [%d]\n" , \
## arg, (int) (jiffies - i8042_start_time)); \
} while (0)
#else
#define dbg_init() do { } while (0)
#define dbg(format, arg...) do {} while (0)
#endif
#endif /* _I8042_H */

View File

@@ -0,0 +1,376 @@
/*
* PS/2 driver library
*
* Copyright (c) 1999-2002 Vojtech Pavlik
* Copyright (c) 2004 Dmitry Torokhov
*/
/*
* 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/delay.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/i8042.h>
#include <linux/init.h>
#include <linux/libps2.h>
#define DRIVER_DESC "PS/2 driver library"
MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
MODULE_DESCRIPTION("PS/2 driver library");
MODULE_LICENSE("GPL");
/*
* ps2_sendbyte() sends a byte to the device and waits for acknowledge.
* It doesn't handle retransmission, though it could - because if there
* is a need for retransmissions device has to be replaced anyway.
*
* ps2_sendbyte() can only be called from a process context.
*/
int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout)
{
serio_pause_rx(ps2dev->serio);
ps2dev->nak = 1;
ps2dev->flags |= PS2_FLAG_ACK;
serio_continue_rx(ps2dev->serio);
if (serio_write(ps2dev->serio, byte) == 0)
wait_event_timeout(ps2dev->wait,
!(ps2dev->flags & PS2_FLAG_ACK),
msecs_to_jiffies(timeout));
serio_pause_rx(ps2dev->serio);
ps2dev->flags &= ~PS2_FLAG_ACK;
serio_continue_rx(ps2dev->serio);
return -ps2dev->nak;
}
EXPORT_SYMBOL(ps2_sendbyte);
void ps2_begin_command(struct ps2dev *ps2dev)
{
mutex_lock(&ps2dev->cmd_mutex);
if (i8042_check_port_owner(ps2dev->serio))
i8042_lock_chip();
}
EXPORT_SYMBOL(ps2_begin_command);
void ps2_end_command(struct ps2dev *ps2dev)
{
if (i8042_check_port_owner(ps2dev->serio))
i8042_unlock_chip();
mutex_unlock(&ps2dev->cmd_mutex);
}
EXPORT_SYMBOL(ps2_end_command);
/*
* ps2_drain() waits for device to transmit requested number of bytes
* and discards them.
*/
void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout)
{
if (maxbytes > sizeof(ps2dev->cmdbuf)) {
WARN_ON(1);
maxbytes = sizeof(ps2dev->cmdbuf);
}
ps2_begin_command(ps2dev);
serio_pause_rx(ps2dev->serio);
ps2dev->flags = PS2_FLAG_CMD;
ps2dev->cmdcnt = maxbytes;
serio_continue_rx(ps2dev->serio);
wait_event_timeout(ps2dev->wait,
!(ps2dev->flags & PS2_FLAG_CMD),
msecs_to_jiffies(timeout));
ps2_end_command(ps2dev);
}
EXPORT_SYMBOL(ps2_drain);
/*
* ps2_is_keyboard_id() checks received ID byte against the list of
* known keyboard IDs.
*/
int ps2_is_keyboard_id(char id_byte)
{
static const char keyboard_ids[] = {
0xab, /* Regular keyboards */
0xac, /* NCD Sun keyboard */
0x2b, /* Trust keyboard, translated */
0x5d, /* Trust keyboard */
0x60, /* NMB SGI keyboard, translated */
0x47, /* NMB SGI keyboard */
};
return memchr(keyboard_ids, id_byte, sizeof(keyboard_ids)) != NULL;
}
EXPORT_SYMBOL(ps2_is_keyboard_id);
/*
* ps2_adjust_timeout() is called after receiving 1st byte of command
* response and tries to reduce remaining timeout to speed up command
* completion.
*/
static int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout)
{
switch (command) {
case PS2_CMD_RESET_BAT:
/*
* Device has sent the first response byte after
* reset command, reset is thus done, so we can
* shorten the timeout.
* The next byte will come soon (keyboard) or not
* at all (mouse).
*/
if (timeout > msecs_to_jiffies(100))
timeout = msecs_to_jiffies(100);
break;
case PS2_CMD_GETID:
/*
* Microsoft Natural Elite keyboard responds to
* the GET ID command as it were a mouse, with
* a single byte. Fail the command so atkbd will
* use alternative probe to detect it.
*/
if (ps2dev->cmdbuf[1] == 0xaa) {
serio_pause_rx(ps2dev->serio);
ps2dev->flags = 0;
serio_continue_rx(ps2dev->serio);
timeout = 0;
}
/*
* If device behind the port is not a keyboard there
* won't be 2nd byte of ID response.
*/
if (!ps2_is_keyboard_id(ps2dev->cmdbuf[1])) {
serio_pause_rx(ps2dev->serio);
ps2dev->flags = ps2dev->cmdcnt = 0;
serio_continue_rx(ps2dev->serio);
timeout = 0;
}
break;
default:
break;
}
return timeout;
}
/*
* ps2_command() sends a command and its parameters to the mouse,
* then waits for the response and puts it in the param array.
*
* ps2_command() can only be called from a process context
*/
int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
{
int timeout;
int send = (command >> 12) & 0xf;
int receive = (command >> 8) & 0xf;
int rc = -1;
int i;
if (receive > sizeof(ps2dev->cmdbuf)) {
WARN_ON(1);
return -1;
}
if (send && !param) {
WARN_ON(1);
return -1;
}
serio_pause_rx(ps2dev->serio);
ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0;
ps2dev->cmdcnt = receive;
if (receive && param)
for (i = 0; i < receive; i++)
ps2dev->cmdbuf[(receive - 1) - i] = param[i];
serio_continue_rx(ps2dev->serio);
/*
* Some devices (Synaptics) peform the reset before
* ACKing the reset command, and so it can take a long
* time before the ACK arrrives.
*/
if (ps2_sendbyte(ps2dev, command & 0xff,
command == PS2_CMD_RESET_BAT ? 1000 : 200))
goto out;
for (i = 0; i < send; i++)
if (ps2_sendbyte(ps2dev, param[i], 200))
goto out;
/*
* The reset command takes a long time to execute.
*/
timeout = msecs_to_jiffies(command == PS2_CMD_RESET_BAT ? 4000 : 500);
timeout = wait_event_timeout(ps2dev->wait,
!(ps2dev->flags & PS2_FLAG_CMD1), timeout);
if (ps2dev->cmdcnt && !(ps2dev->flags & PS2_FLAG_CMD1)) {
timeout = ps2_adjust_timeout(ps2dev, command, timeout);
wait_event_timeout(ps2dev->wait,
!(ps2dev->flags & PS2_FLAG_CMD), timeout);
}
if (param)
for (i = 0; i < receive; i++)
param[i] = ps2dev->cmdbuf[(receive - 1) - i];
if (ps2dev->cmdcnt && (command != PS2_CMD_RESET_BAT || ps2dev->cmdcnt != 1))
goto out;
rc = 0;
out:
serio_pause_rx(ps2dev->serio);
ps2dev->flags = 0;
serio_continue_rx(ps2dev->serio);
return rc;
}
EXPORT_SYMBOL(__ps2_command);
int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
{
int rc;
ps2_begin_command(ps2dev);
rc = __ps2_command(ps2dev, param, command);
ps2_end_command(ps2dev);
return rc;
}
EXPORT_SYMBOL(ps2_command);
/*
* ps2_init() initializes ps2dev structure
*/
void ps2_init(struct ps2dev *ps2dev, struct serio *serio)
{
mutex_init(&ps2dev->cmd_mutex);
lockdep_set_subclass(&ps2dev->cmd_mutex, serio->depth);
init_waitqueue_head(&ps2dev->wait);
ps2dev->serio = serio;
}
EXPORT_SYMBOL(ps2_init);
/*
* ps2_handle_ack() is supposed to be used in interrupt handler
* to properly process ACK/NAK of a command from a PS/2 device.
*/
int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data)
{
switch (data) {
case PS2_RET_ACK:
ps2dev->nak = 0;
break;
case PS2_RET_NAK:
ps2dev->flags |= PS2_FLAG_NAK;
ps2dev->nak = PS2_RET_NAK;
break;
case PS2_RET_ERR:
if (ps2dev->flags & PS2_FLAG_NAK) {
ps2dev->flags &= ~PS2_FLAG_NAK;
ps2dev->nak = PS2_RET_ERR;
break;
}
/*
* Workaround for mice which don't ACK the Get ID command.
* These are valid mouse IDs that we recognize.
*/
case 0x00:
case 0x03:
case 0x04:
if (ps2dev->flags & PS2_FLAG_WAITID) {
ps2dev->nak = 0;
break;
}
/* Fall through */
default:
return 0;
}
if (!ps2dev->nak) {
ps2dev->flags &= ~PS2_FLAG_NAK;
if (ps2dev->cmdcnt)
ps2dev->flags |= PS2_FLAG_CMD | PS2_FLAG_CMD1;
}
ps2dev->flags &= ~PS2_FLAG_ACK;
wake_up(&ps2dev->wait);
if (data != PS2_RET_ACK)
ps2_handle_response(ps2dev, data);
return 1;
}
EXPORT_SYMBOL(ps2_handle_ack);
/*
* ps2_handle_response() is supposed to be used in interrupt handler
* to properly store device's response to a command and notify process
* waiting for completion of the command.
*/
int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data)
{
if (ps2dev->cmdcnt)
ps2dev->cmdbuf[--ps2dev->cmdcnt] = data;
if (ps2dev->flags & PS2_FLAG_CMD1) {
ps2dev->flags &= ~PS2_FLAG_CMD1;
if (ps2dev->cmdcnt)
wake_up(&ps2dev->wait);
}
if (!ps2dev->cmdcnt) {
ps2dev->flags &= ~PS2_FLAG_CMD;
wake_up(&ps2dev->wait);
}
return 1;
}
EXPORT_SYMBOL(ps2_handle_response);
void ps2_cmd_aborted(struct ps2dev *ps2dev)
{
if (ps2dev->flags & PS2_FLAG_ACK)
ps2dev->nak = 1;
if (ps2dev->flags & (PS2_FLAG_ACK | PS2_FLAG_CMD))
wake_up(&ps2dev->wait);
/* reset all flags except last nack */
ps2dev->flags &= PS2_FLAG_NAK;
}
EXPORT_SYMBOL(ps2_cmd_aborted);

View File

@@ -0,0 +1,211 @@
/*
* SGI O2 MACE PS2 controller driver for linux
*
* Copyright (C) 2002 Vivien Chappelier
*
* 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/serio.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/err.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/ip32/mace.h>
#include <asm/ip32/ip32_ints.h>
MODULE_AUTHOR("Vivien Chappelier <vivien.chappelier@linux-mips.org");
MODULE_DESCRIPTION("SGI O2 MACE PS2 controller driver");
MODULE_LICENSE("GPL");
#define MACE_PS2_TIMEOUT 10000 /* in 50us unit */
#define PS2_STATUS_CLOCK_SIGNAL BIT(0) /* external clock signal */
#define PS2_STATUS_CLOCK_INHIBIT BIT(1) /* clken output signal */
#define PS2_STATUS_TX_INPROGRESS BIT(2) /* transmission in progress */
#define PS2_STATUS_TX_EMPTY BIT(3) /* empty transmit buffer */
#define PS2_STATUS_RX_FULL BIT(4) /* full receive buffer */
#define PS2_STATUS_RX_INPROGRESS BIT(5) /* reception in progress */
#define PS2_STATUS_ERROR_PARITY BIT(6) /* parity error */
#define PS2_STATUS_ERROR_FRAMING BIT(7) /* framing error */
#define PS2_CONTROL_TX_CLOCK_DISABLE BIT(0) /* inhibit clock signal after TX */
#define PS2_CONTROL_TX_ENABLE BIT(1) /* transmit enable */
#define PS2_CONTROL_TX_INT_ENABLE BIT(2) /* enable transmit interrupt */
#define PS2_CONTROL_RX_INT_ENABLE BIT(3) /* enable receive interrupt */
#define PS2_CONTROL_RX_CLOCK_ENABLE BIT(4) /* pause reception if set to 0 */
#define PS2_CONTROL_RESET BIT(5) /* reset */
struct maceps2_data {
struct mace_ps2port *port;
int irq;
};
static struct maceps2_data port_data[2];
static struct serio *maceps2_port[2];
static struct platform_device *maceps2_device;
static int maceps2_write(struct serio *dev, unsigned char val)
{
struct mace_ps2port *port = ((struct maceps2_data *)dev->port_data)->port;
unsigned int timeout = MACE_PS2_TIMEOUT;
do {
if (port->status & PS2_STATUS_TX_EMPTY) {
port->tx = val;
return 0;
}
udelay(50);
} while (timeout--);
return -1;
}
static irqreturn_t maceps2_interrupt(int irq, void *dev_id)
{
struct serio *dev = dev_id;
struct mace_ps2port *port = ((struct maceps2_data *)dev->port_data)->port;
unsigned long byte;
if (port->status & PS2_STATUS_RX_FULL) {
byte = port->rx;
serio_interrupt(dev, byte & 0xff, 0);
}
return IRQ_HANDLED;
}
static int maceps2_open(struct serio *dev)
{
struct maceps2_data *data = (struct maceps2_data *)dev->port_data;
if (request_irq(data->irq, maceps2_interrupt, 0, "PS2 port", dev)) {
printk(KERN_ERR "Could not allocate PS/2 IRQ\n");
return -EBUSY;
}
/* Reset port */
data->port->control = PS2_CONTROL_TX_CLOCK_DISABLE | PS2_CONTROL_RESET;
udelay(100);
/* Enable interrupts */
data->port->control = PS2_CONTROL_RX_CLOCK_ENABLE |
PS2_CONTROL_TX_ENABLE |
PS2_CONTROL_RX_INT_ENABLE;
return 0;
}
static void maceps2_close(struct serio *dev)
{
struct maceps2_data *data = (struct maceps2_data *)dev->port_data;
data->port->control = PS2_CONTROL_TX_CLOCK_DISABLE | PS2_CONTROL_RESET;
udelay(100);
free_irq(data->irq, dev);
}
static struct serio * __devinit maceps2_allocate_port(int idx)
{
struct serio *serio;
serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (serio) {
serio->id.type = SERIO_8042;
serio->write = maceps2_write;
serio->open = maceps2_open;
serio->close = maceps2_close;
snprintf(serio->name, sizeof(serio->name), "MACE PS/2 port%d", idx);
snprintf(serio->phys, sizeof(serio->phys), "mace/serio%d", idx);
serio->port_data = &port_data[idx];
serio->dev.parent = &maceps2_device->dev;
}
return serio;
}
static int __devinit maceps2_probe(struct platform_device *dev)
{
maceps2_port[0] = maceps2_allocate_port(0);
maceps2_port[1] = maceps2_allocate_port(1);
if (!maceps2_port[0] || !maceps2_port[1]) {
kfree(maceps2_port[0]);
kfree(maceps2_port[1]);
return -ENOMEM;
}
serio_register_port(maceps2_port[0]);
serio_register_port(maceps2_port[1]);
return 0;
}
static int __devexit maceps2_remove(struct platform_device *dev)
{
serio_unregister_port(maceps2_port[0]);
serio_unregister_port(maceps2_port[1]);
return 0;
}
static struct platform_driver maceps2_driver = {
.driver = {
.name = "maceps2",
.owner = THIS_MODULE,
},
.probe = maceps2_probe,
.remove = __devexit_p(maceps2_remove),
};
static int __init maceps2_init(void)
{
int error;
error = platform_driver_register(&maceps2_driver);
if (error)
return error;
maceps2_device = platform_device_alloc("maceps2", -1);
if (!maceps2_device) {
error = -ENOMEM;
goto err_unregister_driver;
}
port_data[0].port = &mace->perif.ps2.keyb;
port_data[0].irq = MACEISA_KEYB_IRQ;
port_data[1].port = &mace->perif.ps2.mouse;
port_data[1].irq = MACEISA_MOUSE_IRQ;
error = platform_device_add(maceps2_device);
if (error)
goto err_free_device;
return 0;
err_free_device:
platform_device_put(maceps2_device);
err_unregister_driver:
platform_driver_unregister(&maceps2_driver);
return error;
}
static void __exit maceps2_exit(void)
{
platform_device_unregister(maceps2_device);
platform_driver_unregister(&maceps2_driver);
}
module_init(maceps2_init);
module_exit(maceps2_exit);

View File

@@ -0,0 +1,217 @@
/*
* Parallel port to Keyboard port adapter driver for Linux
*
* Copyright (c) 1999-2004 Vojtech Pavlik
*/
/*
* 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.
*/
/*
* To connect an AT or XT keyboard to the parallel port, a fairly simple adapter
* can be made:
*
* Parallel port Keyboard port
*
* +5V --------------------- +5V (4)
*
* ______
* +5V -------|______|--.
* |
* ACK (10) ------------|
* |--- KBD CLOCK (5)
* STROBE (1) ---|<|----'
*
* ______
* +5V -------|______|--.
* |
* BUSY (11) -----------|
* |--- KBD DATA (1)
* AUTOFD (14) --|<|----'
*
* GND (18-25) ------------- GND (3)
*
* The diodes can be fairly any type, and the resistors should be somewhere
* around 5 kOhm, but the adapter will likely work without the resistors,
* too.
*
* The +5V source can be taken either from USB, from mouse or keyboard ports,
* or from a joystick port. Unfortunately, the parallel port of a PC doesn't
* have a +5V pin, and feeding the keyboard from signal pins is out of question
* with 300 mA power reqirement of a typical AT keyboard.
*/
#include <linux/module.h>
#include <linux/parport.h>
#include <linux/init.h>
#include <linux/serio.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Parallel port to Keyboard port adapter driver");
MODULE_LICENSE("GPL");
static unsigned int parkbd_pp_no;
module_param_named(port, parkbd_pp_no, int, 0);
MODULE_PARM_DESC(port, "Parallel port the adapter is connected to (default is 0)");
static unsigned int parkbd_mode = SERIO_8042;
module_param_named(mode, parkbd_mode, uint, 0);
MODULE_PARM_DESC(mode, "Mode of operation: XT = 0/AT = 1 (default)");
#define PARKBD_CLOCK 0x01 /* Strobe & Ack */
#define PARKBD_DATA 0x02 /* AutoFd & Busy */
static int parkbd_buffer;
static int parkbd_counter;
static unsigned long parkbd_last;
static int parkbd_writing;
static unsigned long parkbd_start;
static struct pardevice *parkbd_dev;
static struct serio *parkbd_port;
static int parkbd_readlines(void)
{
return (parport_read_status(parkbd_dev->port) >> 6) ^ 2;
}
static void parkbd_writelines(int data)
{
parport_write_control(parkbd_dev->port, (~data & 3) | 0x10);
}
static int parkbd_write(struct serio *port, unsigned char c)
{
unsigned char p;
if (!parkbd_mode) return -1;
p = c ^ (c >> 4);
p = p ^ (p >> 2);
p = p ^ (p >> 1);
parkbd_counter = 0;
parkbd_writing = 1;
parkbd_buffer = c | (((int) (~p & 1)) << 8) | 0x600;
parkbd_writelines(2);
return 0;
}
static void parkbd_interrupt(void *dev_id)
{
if (parkbd_writing) {
if (parkbd_counter && ((parkbd_counter == 11) || time_after(jiffies, parkbd_last + HZ/100))) {
parkbd_counter = 0;
parkbd_buffer = 0;
parkbd_writing = 0;
parkbd_writelines(3);
return;
}
parkbd_writelines(((parkbd_buffer >> parkbd_counter++) & 1) | 2);
if (parkbd_counter == 11) {
parkbd_counter = 0;
parkbd_buffer = 0;
parkbd_writing = 0;
parkbd_writelines(3);
}
} else {
if ((parkbd_counter == parkbd_mode + 10) || time_after(jiffies, parkbd_last + HZ/100)) {
parkbd_counter = 0;
parkbd_buffer = 0;
}
parkbd_buffer |= (parkbd_readlines() >> 1) << parkbd_counter++;
if (parkbd_counter == parkbd_mode + 10)
serio_interrupt(parkbd_port, (parkbd_buffer >> (2 - parkbd_mode)) & 0xff, 0);
}
parkbd_last = jiffies;
}
static int parkbd_getport(void)
{
struct parport *pp;
pp = parport_find_number(parkbd_pp_no);
if (pp == NULL) {
printk(KERN_ERR "parkbd: no such parport\n");
return -ENODEV;
}
parkbd_dev = parport_register_device(pp, "parkbd", NULL, NULL, parkbd_interrupt, PARPORT_DEV_EXCL, NULL);
parport_put_port(pp);
if (!parkbd_dev)
return -ENODEV;
if (parport_claim(parkbd_dev)) {
parport_unregister_device(parkbd_dev);
return -EBUSY;
}
parkbd_start = jiffies;
return 0;
}
static struct serio * __init parkbd_allocate_serio(void)
{
struct serio *serio;
serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (serio) {
serio->id.type = parkbd_mode;
serio->write = parkbd_write,
strlcpy(serio->name, "PARKBD AT/XT keyboard adapter", sizeof(serio->name));
snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", parkbd_dev->port->name);
}
return serio;
}
static int __init parkbd_init(void)
{
int err;
err = parkbd_getport();
if (err)
return err;
parkbd_port = parkbd_allocate_serio();
if (!parkbd_port) {
parport_release(parkbd_dev);
return -ENOMEM;
}
parkbd_writelines(3);
serio_register_port(parkbd_port);
printk(KERN_INFO "serio: PARKBD %s adapter on %s\n",
parkbd_mode ? "AT" : "XT", parkbd_dev->port->name);
return 0;
}
static void __exit parkbd_exit(void)
{
parport_release(parkbd_dev);
serio_unregister_port(parkbd_port);
parport_unregister_device(parkbd_dev);
}
module_init(parkbd_init);
module_exit(parkbd_exit);

View File

@@ -0,0 +1,232 @@
/*
* linux/drivers/input/serio/pcips2.c
*
* Copyright (C) 2003 Russell King, All Rights Reserved.
*
* 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.
*
* I'm not sure if this is a generic PS/2 PCI interface or specific to
* the Mobility Electronics docking station.
*/
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/input.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/delay.h>
#include <asm/io.h>
#define PS2_CTRL (0)
#define PS2_STATUS (1)
#define PS2_DATA (2)
#define PS2_CTRL_CLK (1<<0)
#define PS2_CTRL_DAT (1<<1)
#define PS2_CTRL_TXIRQ (1<<2)
#define PS2_CTRL_ENABLE (1<<3)
#define PS2_CTRL_RXIRQ (1<<4)
#define PS2_STAT_CLK (1<<0)
#define PS2_STAT_DAT (1<<1)
#define PS2_STAT_PARITY (1<<2)
#define PS2_STAT_RXFULL (1<<5)
#define PS2_STAT_TXBUSY (1<<6)
#define PS2_STAT_TXEMPTY (1<<7)
struct pcips2_data {
struct serio *io;
unsigned int base;
struct pci_dev *dev;
};
static int pcips2_write(struct serio *io, unsigned char val)
{
struct pcips2_data *ps2if = io->port_data;
unsigned int stat;
do {
stat = inb(ps2if->base + PS2_STATUS);
cpu_relax();
} while (!(stat & PS2_STAT_TXEMPTY));
outb(val, ps2if->base + PS2_DATA);
return 0;
}
static irqreturn_t pcips2_interrupt(int irq, void *devid)
{
struct pcips2_data *ps2if = devid;
unsigned char status, scancode;
int handled = 0;
do {
unsigned int flag;
status = inb(ps2if->base + PS2_STATUS);
if (!(status & PS2_STAT_RXFULL))
break;
handled = 1;
scancode = inb(ps2if->base + PS2_DATA);
if (status == 0xff && scancode == 0xff)
break;
flag = (status & PS2_STAT_PARITY) ? 0 : SERIO_PARITY;
if (hweight8(scancode) & 1)
flag ^= SERIO_PARITY;
serio_interrupt(ps2if->io, scancode, flag);
} while (1);
return IRQ_RETVAL(handled);
}
static void pcips2_flush_input(struct pcips2_data *ps2if)
{
unsigned char status, scancode;
do {
status = inb(ps2if->base + PS2_STATUS);
if (!(status & PS2_STAT_RXFULL))
break;
scancode = inb(ps2if->base + PS2_DATA);
if (status == 0xff && scancode == 0xff)
break;
} while (1);
}
static int pcips2_open(struct serio *io)
{
struct pcips2_data *ps2if = io->port_data;
int ret, val = 0;
outb(PS2_CTRL_ENABLE, ps2if->base);
pcips2_flush_input(ps2if);
ret = request_irq(ps2if->dev->irq, pcips2_interrupt, IRQF_SHARED,
"pcips2", ps2if);
if (ret == 0)
val = PS2_CTRL_ENABLE | PS2_CTRL_RXIRQ;
outb(val, ps2if->base);
return ret;
}
static void pcips2_close(struct serio *io)
{
struct pcips2_data *ps2if = io->port_data;
outb(0, ps2if->base);
free_irq(ps2if->dev->irq, ps2if);
}
static int __devinit pcips2_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
struct pcips2_data *ps2if;
struct serio *serio;
int ret;
ret = pci_enable_device(dev);
if (ret)
goto out;
ret = pci_request_regions(dev, "pcips2");
if (ret)
goto disable;
ps2if = kzalloc(sizeof(struct pcips2_data), GFP_KERNEL);
serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (!ps2if || !serio) {
ret = -ENOMEM;
goto release;
}
serio->id.type = SERIO_8042;
serio->write = pcips2_write;
serio->open = pcips2_open;
serio->close = pcips2_close;
strlcpy(serio->name, pci_name(dev), sizeof(serio->name));
strlcpy(serio->phys, dev_name(&dev->dev), sizeof(serio->phys));
serio->port_data = ps2if;
serio->dev.parent = &dev->dev;
ps2if->io = serio;
ps2if->dev = dev;
ps2if->base = pci_resource_start(dev, 0);
pci_set_drvdata(dev, ps2if);
serio_register_port(ps2if->io);
return 0;
release:
kfree(ps2if);
kfree(serio);
pci_release_regions(dev);
disable:
pci_disable_device(dev);
out:
return ret;
}
static void __devexit pcips2_remove(struct pci_dev *dev)
{
struct pcips2_data *ps2if = pci_get_drvdata(dev);
serio_unregister_port(ps2if->io);
pci_set_drvdata(dev, NULL);
kfree(ps2if);
pci_release_regions(dev);
pci_disable_device(dev);
}
static struct pci_device_id pcips2_ids[] = {
{
.vendor = 0x14f2, /* MOBILITY */
.device = 0x0123, /* Keyboard */
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.class = PCI_CLASS_INPUT_KEYBOARD << 8,
.class_mask = 0xffff00,
},
{
.vendor = 0x14f2, /* MOBILITY */
.device = 0x0124, /* Mouse */
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.class = PCI_CLASS_INPUT_MOUSE << 8,
.class_mask = 0xffff00,
},
{ 0, }
};
static struct pci_driver pcips2_driver = {
.name = "pcips2",
.id_table = pcips2_ids,
.probe = pcips2_probe,
.remove = __devexit_p(pcips2_remove),
};
static int __init pcips2_init(void)
{
return pci_register_driver(&pcips2_driver);
}
static void __exit pcips2_exit(void)
{
pci_unregister_driver(&pcips2_driver);
}
module_init(pcips2_init);
module_exit(pcips2_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
MODULE_DESCRIPTION("PCI PS/2 keyboard/mouse driver");
MODULE_DEVICE_TABLE(pci, pcips2_ids);

View File

@@ -0,0 +1,187 @@
/*
* Copyright (c) 2000-2001 Vojtech Pavlik
*
* Based on the work of:
* Richard Zidlicky <Richard.Zidlicky@stud.informatik.uni-erlangen.de>
*/
/*
* Q40 PS/2 keyboard controller driver for Linux/m68k
*/
/*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/interrupt.h>
#include <linux/err.h>
#include <linux/bitops.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/q40_master.h>
#include <asm/irq.h>
#include <asm/q40ints.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Q40 PS/2 keyboard controller driver");
MODULE_LICENSE("GPL");
static DEFINE_SPINLOCK(q40kbd_lock);
static struct serio *q40kbd_port;
static struct platform_device *q40kbd_device;
static irqreturn_t q40kbd_interrupt(int irq, void *dev_id)
{
unsigned long flags;
spin_lock_irqsave(&q40kbd_lock, flags);
if (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG))
serio_interrupt(q40kbd_port, master_inb(KEYCODE_REG), 0);
master_outb(-1, KEYBOARD_UNLOCK_REG);
spin_unlock_irqrestore(&q40kbd_lock, flags);
return IRQ_HANDLED;
}
/*
* q40kbd_flush() flushes all data that may be in the keyboard buffers
*/
static void q40kbd_flush(void)
{
int maxread = 100;
unsigned long flags;
spin_lock_irqsave(&q40kbd_lock, flags);
while (maxread-- && (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG)))
master_inb(KEYCODE_REG);
spin_unlock_irqrestore(&q40kbd_lock, flags);
}
/*
* q40kbd_open() is called when a port is open by the higher layer.
* It allocates the interrupt and enables in in the chip.
*/
static int q40kbd_open(struct serio *port)
{
q40kbd_flush();
if (request_irq(Q40_IRQ_KEYBOARD, q40kbd_interrupt, 0, "q40kbd", NULL)) {
printk(KERN_ERR "q40kbd.c: Can't get irq %d.\n", Q40_IRQ_KEYBOARD);
return -EBUSY;
}
/* off we go */
master_outb(-1, KEYBOARD_UNLOCK_REG);
master_outb(1, KEY_IRQ_ENABLE_REG);
return 0;
}
static void q40kbd_close(struct serio *port)
{
master_outb(0, KEY_IRQ_ENABLE_REG);
master_outb(-1, KEYBOARD_UNLOCK_REG);
free_irq(Q40_IRQ_KEYBOARD, NULL);
q40kbd_flush();
}
static int __devinit q40kbd_probe(struct platform_device *dev)
{
q40kbd_port = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (!q40kbd_port)
return -ENOMEM;
q40kbd_port->id.type = SERIO_8042;
q40kbd_port->open = q40kbd_open;
q40kbd_port->close = q40kbd_close;
q40kbd_port->dev.parent = &dev->dev;
strlcpy(q40kbd_port->name, "Q40 Kbd Port", sizeof(q40kbd_port->name));
strlcpy(q40kbd_port->phys, "Q40", sizeof(q40kbd_port->phys));
serio_register_port(q40kbd_port);
printk(KERN_INFO "serio: Q40 kbd registered\n");
return 0;
}
static int __devexit q40kbd_remove(struct platform_device *dev)
{
serio_unregister_port(q40kbd_port);
return 0;
}
static struct platform_driver q40kbd_driver = {
.driver = {
.name = "q40kbd",
.owner = THIS_MODULE,
},
.probe = q40kbd_probe,
.remove = __devexit_p(q40kbd_remove),
};
static int __init q40kbd_init(void)
{
int error;
if (!MACH_IS_Q40)
return -ENODEV;
error = platform_driver_register(&q40kbd_driver);
if (error)
return error;
q40kbd_device = platform_device_alloc("q40kbd", -1);
if (!q40kbd_device)
goto err_unregister_driver;
error = platform_device_add(q40kbd_device);
if (error)
goto err_free_device;
return 0;
err_free_device:
platform_device_put(q40kbd_device);
err_unregister_driver:
platform_driver_unregister(&q40kbd_driver);
return error;
}
static void __exit q40kbd_exit(void)
{
platform_device_unregister(q40kbd_device);
platform_driver_unregister(&q40kbd_driver);
}
module_init(q40kbd_init);
module_exit(q40kbd_exit);

View File

@@ -0,0 +1,157 @@
/*
* Copyright (c) 2000-2001 Vojtech Pavlik
* Copyright (c) 2002 Russell King
*/
/*
* Acorn RiscPC PS/2 keyboard controller driver for Linux/ARM
*/
/*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <asm/irq.h>
#include <mach/hardware.h>
#include <asm/hardware/iomd.h>
#include <asm/system.h>
MODULE_AUTHOR("Vojtech Pavlik, Russell King");
MODULE_DESCRIPTION("Acorn RiscPC PS/2 keyboard controller driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:kart");
static int rpckbd_write(struct serio *port, unsigned char val)
{
while (!(iomd_readb(IOMD_KCTRL) & (1 << 7)))
cpu_relax();
iomd_writeb(val, IOMD_KARTTX);
return 0;
}
static irqreturn_t rpckbd_rx(int irq, void *dev_id)
{
struct serio *port = dev_id;
unsigned int byte;
int handled = IRQ_NONE;
while (iomd_readb(IOMD_KCTRL) & (1 << 5)) {
byte = iomd_readb(IOMD_KARTRX);
serio_interrupt(port, byte, 0);
handled = IRQ_HANDLED;
}
return handled;
}
static irqreturn_t rpckbd_tx(int irq, void *dev_id)
{
return IRQ_HANDLED;
}
static int rpckbd_open(struct serio *port)
{
/* Reset the keyboard state machine. */
iomd_writeb(0, IOMD_KCTRL);
iomd_writeb(8, IOMD_KCTRL);
iomd_readb(IOMD_KARTRX);
if (request_irq(IRQ_KEYBOARDRX, rpckbd_rx, 0, "rpckbd", port) != 0) {
printk(KERN_ERR "rpckbd.c: Could not allocate keyboard receive IRQ\n");
return -EBUSY;
}
if (request_irq(IRQ_KEYBOARDTX, rpckbd_tx, 0, "rpckbd", port) != 0) {
printk(KERN_ERR "rpckbd.c: Could not allocate keyboard transmit IRQ\n");
free_irq(IRQ_KEYBOARDRX, NULL);
return -EBUSY;
}
return 0;
}
static void rpckbd_close(struct serio *port)
{
free_irq(IRQ_KEYBOARDRX, port);
free_irq(IRQ_KEYBOARDTX, port);
}
/*
* Allocate and initialize serio structure for subsequent registration
* with serio core.
*/
static int __devinit rpckbd_probe(struct platform_device *dev)
{
struct serio *serio;
serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (!serio)
return -ENOMEM;
serio->id.type = SERIO_8042;
serio->write = rpckbd_write;
serio->open = rpckbd_open;
serio->close = rpckbd_close;
serio->dev.parent = &dev->dev;
strlcpy(serio->name, "RiscPC PS/2 kbd port", sizeof(serio->name));
strlcpy(serio->phys, "rpckbd/serio0", sizeof(serio->phys));
platform_set_drvdata(dev, serio);
serio_register_port(serio);
return 0;
}
static int __devexit rpckbd_remove(struct platform_device *dev)
{
struct serio *serio = platform_get_drvdata(dev);
serio_unregister_port(serio);
return 0;
}
static struct platform_driver rpckbd_driver = {
.probe = rpckbd_probe,
.remove = __devexit_p(rpckbd_remove),
.driver = {
.name = "kart",
.owner = THIS_MODULE,
},
};
static int __init rpckbd_init(void)
{
return platform_driver_register(&rpckbd_driver);
}
static void __exit rpckbd_exit(void)
{
platform_driver_unregister(&rpckbd_driver);
}
module_init(rpckbd_init);
module_exit(rpckbd_exit);

View File

@@ -0,0 +1,356 @@
/*
* 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/input.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 <linux/slab.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/hardware/sa1111.h>
struct ps2if {
struct serio *io;
struct sa1111_dev *dev;
void __iomem *base;
unsigned int open;
spinlock_t lock;
unsigned int head;
unsigned int tail;
unsigned char buf[4];
};
/*
* 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 irqreturn_t ps2_rxint(int irq, void *dev_id)
{
struct ps2if *ps2if = dev_id;
unsigned int scancode, flag, status;
status = sa1111_readl(ps2if->base + SA1111_PS2STAT);
while (status & PS2STAT_RXF) {
if (status & PS2STAT_STP)
sa1111_writel(PS2STAT_STP, ps2if->base + SA1111_PS2STAT);
flag = (status & PS2STAT_STP ? SERIO_FRAME : 0) |
(status & PS2STAT_RXP ? 0 : SERIO_PARITY);
scancode = sa1111_readl(ps2if->base + SA1111_PS2DATA) & 0xff;
if (hweight8(scancode) & 1)
flag ^= SERIO_PARITY;
serio_interrupt(ps2if->io, scancode, flag);
status = sa1111_readl(ps2if->base + SA1111_PS2STAT);
}
return IRQ_HANDLED;
}
/*
* Completion of ps2 write
*/
static irqreturn_t ps2_txint(int irq, void *dev_id)
{
struct ps2if *ps2if = dev_id;
unsigned int status;
spin_lock(&ps2if->lock);
status = sa1111_readl(ps2if->base + SA1111_PS2STAT);
if (ps2if->head == ps2if->tail) {
disable_irq_nosync(irq);
/* done */
} else if (status & PS2STAT_TXE) {
sa1111_writel(ps2if->buf[ps2if->tail], ps2if->base + SA1111_PS2DATA);
ps2if->tail = (ps2if->tail + 1) & (sizeof(ps2if->buf) - 1);
}
spin_unlock(&ps2if->lock);
return IRQ_HANDLED;
}
/*
* 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 *ps2if = io->port_data;
unsigned long flags;
unsigned int head;
spin_lock_irqsave(&ps2if->lock, flags);
/*
* If the TX register is empty, we can go straight out.
*/
if (sa1111_readl(ps2if->base + SA1111_PS2STAT) & PS2STAT_TXE) {
sa1111_writel(val, ps2if->base + SA1111_PS2DATA);
} else {
if (ps2if->head == ps2if->tail)
enable_irq(ps2if->dev->irq[1]);
head = (ps2if->head + 1) & (sizeof(ps2if->buf) - 1);
if (head != ps2if->tail) {
ps2if->buf[ps2if->head] = val;
ps2if->head = head;
}
}
spin_unlock_irqrestore(&ps2if->lock, flags);
return 0;
}
static int ps2_open(struct serio *io)
{
struct ps2if *ps2if = io->port_data;
int ret;
sa1111_enable_device(ps2if->dev);
ret = request_irq(ps2if->dev->irq[0], ps2_rxint, 0,
SA1111_DRIVER_NAME(ps2if->dev), ps2if);
if (ret) {
printk(KERN_ERR "sa1111ps2: could not allocate IRQ%d: %d\n",
ps2if->dev->irq[0], ret);
return ret;
}
ret = request_irq(ps2if->dev->irq[1], ps2_txint, 0,
SA1111_DRIVER_NAME(ps2if->dev), ps2if);
if (ret) {
printk(KERN_ERR "sa1111ps2: could not allocate IRQ%d: %d\n",
ps2if->dev->irq[1], ret);
free_irq(ps2if->dev->irq[0], ps2if);
return ret;
}
ps2if->open = 1;
enable_irq_wake(ps2if->dev->irq[0]);
sa1111_writel(PS2CR_ENA, ps2if->base + SA1111_PS2CR);
return 0;
}
static void ps2_close(struct serio *io)
{
struct ps2if *ps2if = io->port_data;
sa1111_writel(0, ps2if->base + SA1111_PS2CR);
disable_irq_wake(ps2if->dev->irq[0]);
ps2if->open = 0;
free_irq(ps2if->dev->irq[1], ps2if);
free_irq(ps2if->dev->irq[0], ps2if);
sa1111_disable_device(ps2if->dev);
}
/*
* Clear the input buffer.
*/
static void __devinit ps2_clear_input(struct ps2if *ps2if)
{
int maxread = 100;
while (maxread--) {
if ((sa1111_readl(ps2if->base + SA1111_PS2DATA) & 0xff) == 0xff)
break;
}
}
static inline unsigned int
ps2_test_one(struct ps2if *ps2if, unsigned int mask)
{
unsigned int val;
sa1111_writel(PS2CR_ENA | mask, ps2if->base + SA1111_PS2CR);
udelay(2);
val = sa1111_readl(ps2if->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 *ps2if)
{
unsigned int stat;
int ret = 0;
stat = ps2_test_one(ps2if, PS2CR_FKC);
if (stat != PS2STAT_KBD) {
printk("PS/2 interface test failed[1]: %02x\n", stat);
ret = -ENODEV;
}
stat = ps2_test_one(ps2if, 0);
if (stat != (PS2STAT_KBC | PS2STAT_KBD)) {
printk("PS/2 interface test failed[2]: %02x\n", stat);
ret = -ENODEV;
}
stat = ps2_test_one(ps2if, PS2CR_FKD);
if (stat != PS2STAT_KBC) {
printk("PS/2 interface test failed[3]: %02x\n", stat);
ret = -ENODEV;
}
sa1111_writel(0, ps2if->base + SA1111_PS2CR);
return ret;
}
/*
* Add one device to this driver.
*/
static int __devinit ps2_probe(struct sa1111_dev *dev)
{
struct ps2if *ps2if;
struct serio *serio;
int ret;
ps2if = kzalloc(sizeof(struct ps2if), GFP_KERNEL);
serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (!ps2if || !serio) {
ret = -ENOMEM;
goto free;
}
serio->id.type = SERIO_8042;
serio->write = ps2_write;
serio->open = ps2_open;
serio->close = ps2_close;
strlcpy(serio->name, dev_name(&dev->dev), sizeof(serio->name));
strlcpy(serio->phys, dev_name(&dev->dev), sizeof(serio->phys));
serio->port_data = ps2if;
serio->dev.parent = &dev->dev;
ps2if->io = serio;
ps2if->dev = dev;
sa1111_set_drvdata(dev, ps2if);
spin_lock_init(&ps2if->lock);
/*
* Request the physical region for this PS2 port.
*/
if (!request_mem_region(dev->res.start,
dev->res.end - dev->res.start + 1,
SA1111_DRIVER_NAME(dev))) {
ret = -EBUSY;
goto free;
}
/*
* Our parent device has already mapped the region.
*/
ps2if->base = dev->mapbase;
sa1111_enable_device(ps2if->dev);
/* Incoming clock is 8MHz */
sa1111_writel(0, ps2if->base + SA1111_PS2CLKDIV);
sa1111_writel(127, ps2if->base + SA1111_PS2PRECNT);
/*
* Flush any pending input.
*/
ps2_clear_input(ps2if);
/*
* Test the keyboard interface.
*/
ret = ps2_test(ps2if);
if (ret)
goto out;
/*
* Flush any pending input.
*/
ps2_clear_input(ps2if);
sa1111_disable_device(ps2if->dev);
serio_register_port(ps2if->io);
return 0;
out:
sa1111_disable_device(ps2if->dev);
release_mem_region(dev->res.start,
dev->res.end - dev->res.start + 1);
free:
sa1111_set_drvdata(dev, NULL);
kfree(ps2if);
kfree(serio);
return ret;
}
/*
* Remove one device from this driver.
*/
static int ps2_remove(struct sa1111_dev *dev)
{
struct ps2if *ps2if = sa1111_get_drvdata(dev);
serio_unregister_port(ps2if->io);
release_mem_region(dev->res.start,
dev->res.end - dev->res.start + 1);
sa1111_set_drvdata(dev, NULL);
kfree(ps2if);
return 0;
}
/*
* Our device driver structure
*/
static struct sa1111_driver ps2_driver = {
.drv = {
.name = "sa1111-ps2",
},
.devid = SA1111_DEVID_PS2,
.probe = ps2_probe,
.remove = ps2_remove,
};
static int __init ps2_init(void)
{
return sa1111_driver_register(&ps2_driver);
}
static void __exit ps2_exit(void)
{
sa1111_driver_unregister(&ps2_driver);
}
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");

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,409 @@
/*
* Raw serio device providing access to a raw byte stream from underlying
* serio port. Closely emulates behavior of pre-2.6 /dev/psaux device
*
* Copyright (c) 2004 Dmitry Torokhov
*
* 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/sched.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/poll.h>
#include <linux/module.h>
#include <linux/serio.h>
#include <linux/init.h>
#include <linux/major.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/wait.h>
#include <linux/mutex.h>
#define DRIVER_DESC "Raw serio driver"
MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
#define SERIO_RAW_QUEUE_LEN 64
struct serio_raw {
unsigned char queue[SERIO_RAW_QUEUE_LEN];
unsigned int tail, head;
char name[16];
unsigned int refcnt;
struct serio *serio;
struct miscdevice dev;
wait_queue_head_t wait;
struct list_head list;
struct list_head node;
};
struct serio_raw_list {
struct fasync_struct *fasync;
struct serio_raw *serio_raw;
struct list_head node;
};
static DEFINE_MUTEX(serio_raw_mutex);
static LIST_HEAD(serio_raw_list);
static unsigned int serio_raw_no;
/*********************************************************************
* Interface with userspace (file operations) *
*********************************************************************/
static int serio_raw_fasync(int fd, struct file *file, int on)
{
struct serio_raw_list *list = file->private_data;
return fasync_helper(fd, file, on, &list->fasync);
}
static struct serio_raw *serio_raw_locate(int minor)
{
struct serio_raw *serio_raw;
list_for_each_entry(serio_raw, &serio_raw_list, node) {
if (serio_raw->dev.minor == minor)
return serio_raw;
}
return NULL;
}
static int serio_raw_open(struct inode *inode, struct file *file)
{
struct serio_raw *serio_raw;
struct serio_raw_list *list;
int retval = 0;
lock_kernel();
retval = mutex_lock_interruptible(&serio_raw_mutex);
if (retval)
goto out_bkl;
if (!(serio_raw = serio_raw_locate(iminor(inode)))) {
retval = -ENODEV;
goto out;
}
if (!serio_raw->serio) {
retval = -ENODEV;
goto out;
}
if (!(list = kzalloc(sizeof(struct serio_raw_list), GFP_KERNEL))) {
retval = -ENOMEM;
goto out;
}
list->serio_raw = serio_raw;
file->private_data = list;
serio_raw->refcnt++;
list_add_tail(&list->node, &serio_raw->list);
out:
mutex_unlock(&serio_raw_mutex);
out_bkl:
unlock_kernel();
return retval;
}
static int serio_raw_cleanup(struct serio_raw *serio_raw)
{
if (--serio_raw->refcnt == 0) {
misc_deregister(&serio_raw->dev);
list_del_init(&serio_raw->node);
kfree(serio_raw);
return 1;
}
return 0;
}
static int serio_raw_release(struct inode *inode, struct file *file)
{
struct serio_raw_list *list = file->private_data;
struct serio_raw *serio_raw = list->serio_raw;
mutex_lock(&serio_raw_mutex);
serio_raw_cleanup(serio_raw);
mutex_unlock(&serio_raw_mutex);
return 0;
}
static int serio_raw_fetch_byte(struct serio_raw *serio_raw, char *c)
{
unsigned long flags;
int empty;
spin_lock_irqsave(&serio_raw->serio->lock, flags);
empty = serio_raw->head == serio_raw->tail;
if (!empty) {
*c = serio_raw->queue[serio_raw->tail];
serio_raw->tail = (serio_raw->tail + 1) % SERIO_RAW_QUEUE_LEN;
}
spin_unlock_irqrestore(&serio_raw->serio->lock, flags);
return !empty;
}
static ssize_t serio_raw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
struct serio_raw_list *list = file->private_data;
struct serio_raw *serio_raw = list->serio_raw;
char uninitialized_var(c);
ssize_t retval = 0;
if (!serio_raw->serio)
return -ENODEV;
if (serio_raw->head == serio_raw->tail && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
retval = wait_event_interruptible(list->serio_raw->wait,
serio_raw->head != serio_raw->tail || !serio_raw->serio);
if (retval)
return retval;
if (!serio_raw->serio)
return -ENODEV;
while (retval < count && serio_raw_fetch_byte(serio_raw, &c)) {
if (put_user(c, buffer++))
return -EFAULT;
retval++;
}
return retval;
}
static ssize_t serio_raw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
struct serio_raw_list *list = file->private_data;
ssize_t written = 0;
int retval;
unsigned char c;
retval = mutex_lock_interruptible(&serio_raw_mutex);
if (retval)
return retval;
if (!list->serio_raw->serio) {
retval = -ENODEV;
goto out;
}
if (count > 32)
count = 32;
while (count--) {
if (get_user(c, buffer++)) {
retval = -EFAULT;
goto out;
}
if (serio_write(list->serio_raw->serio, c)) {
retval = -EIO;
goto out;
}
written++;
};
out:
mutex_unlock(&serio_raw_mutex);
return written;
}
static unsigned int serio_raw_poll(struct file *file, poll_table *wait)
{
struct serio_raw_list *list = file->private_data;
poll_wait(file, &list->serio_raw->wait, wait);
if (list->serio_raw->head != list->serio_raw->tail)
return POLLIN | POLLRDNORM;
return 0;
}
static const struct file_operations serio_raw_fops = {
.owner = THIS_MODULE,
.open = serio_raw_open,
.release = serio_raw_release,
.read = serio_raw_read,
.write = serio_raw_write,
.poll = serio_raw_poll,
.fasync = serio_raw_fasync,
};
/*********************************************************************
* Interface with serio port *
*********************************************************************/
static irqreturn_t serio_raw_interrupt(struct serio *serio, unsigned char data,
unsigned int dfl)
{
struct serio_raw *serio_raw = serio_get_drvdata(serio);
struct serio_raw_list *list;
unsigned int head = serio_raw->head;
/* we are holding serio->lock here so we are prootected */
serio_raw->queue[head] = data;
head = (head + 1) % SERIO_RAW_QUEUE_LEN;
if (likely(head != serio_raw->tail)) {
serio_raw->head = head;
list_for_each_entry(list, &serio_raw->list, node)
kill_fasync(&list->fasync, SIGIO, POLL_IN);
wake_up_interruptible(&serio_raw->wait);
}
return IRQ_HANDLED;
}
static int serio_raw_connect(struct serio *serio, struct serio_driver *drv)
{
struct serio_raw *serio_raw;
int err;
if (!(serio_raw = kzalloc(sizeof(struct serio_raw), GFP_KERNEL))) {
printk(KERN_ERR "serio_raw.c: can't allocate memory for a device\n");
return -ENOMEM;
}
mutex_lock(&serio_raw_mutex);
snprintf(serio_raw->name, sizeof(serio_raw->name), "serio_raw%d", serio_raw_no++);
serio_raw->refcnt = 1;
serio_raw->serio = serio;
INIT_LIST_HEAD(&serio_raw->list);
init_waitqueue_head(&serio_raw->wait);
serio_set_drvdata(serio, serio_raw);
err = serio_open(serio, drv);
if (err)
goto out_free;
list_add_tail(&serio_raw->node, &serio_raw_list);
serio_raw->dev.minor = PSMOUSE_MINOR;
serio_raw->dev.name = serio_raw->name;
serio_raw->dev.parent = &serio->dev;
serio_raw->dev.fops = &serio_raw_fops;
err = misc_register(&serio_raw->dev);
if (err) {
serio_raw->dev.minor = MISC_DYNAMIC_MINOR;
err = misc_register(&serio_raw->dev);
}
if (err) {
printk(KERN_INFO "serio_raw: failed to register raw access device for %s\n",
serio->phys);
goto out_close;
}
printk(KERN_INFO "serio_raw: raw access enabled on %s (%s, minor %d)\n",
serio->phys, serio_raw->name, serio_raw->dev.minor);
goto out;
out_close:
serio_close(serio);
list_del_init(&serio_raw->node);
out_free:
serio_set_drvdata(serio, NULL);
kfree(serio_raw);
out:
mutex_unlock(&serio_raw_mutex);
return err;
}
static int serio_raw_reconnect(struct serio *serio)
{
struct serio_raw *serio_raw = serio_get_drvdata(serio);
struct serio_driver *drv = serio->drv;
if (!drv || !serio_raw) {
printk(KERN_DEBUG "serio_raw: reconnect request, but serio is disconnected, ignoring...\n");
return -1;
}
/*
* Nothing needs to be done here, we just need this method to
* keep the same device.
*/
return 0;
}
static void serio_raw_disconnect(struct serio *serio)
{
struct serio_raw *serio_raw;
mutex_lock(&serio_raw_mutex);
serio_raw = serio_get_drvdata(serio);
serio_close(serio);
serio_set_drvdata(serio, NULL);
serio_raw->serio = NULL;
if (!serio_raw_cleanup(serio_raw))
wake_up_interruptible(&serio_raw->wait);
mutex_unlock(&serio_raw_mutex);
}
static struct serio_device_id serio_raw_serio_ids[] = {
{
.type = SERIO_8042,
.proto = SERIO_ANY,
.id = SERIO_ANY,
.extra = SERIO_ANY,
},
{
.type = SERIO_8042_XL,
.proto = SERIO_ANY,
.id = SERIO_ANY,
.extra = SERIO_ANY,
},
{ 0 }
};
MODULE_DEVICE_TABLE(serio, serio_raw_serio_ids);
static struct serio_driver serio_raw_drv = {
.driver = {
.name = "serio_raw",
},
.description = DRIVER_DESC,
.id_table = serio_raw_serio_ids,
.interrupt = serio_raw_interrupt,
.connect = serio_raw_connect,
.reconnect = serio_raw_reconnect,
.disconnect = serio_raw_disconnect,
.manual_bind = 1,
};
static int __init serio_raw_init(void)
{
return serio_register_driver(&serio_raw_drv);
}
static void __exit serio_raw_exit(void)
{
serio_unregister_driver(&serio_raw_drv);
}
module_init(serio_raw_init);
module_exit(serio_raw_exit);

View File

@@ -0,0 +1,251 @@
/*
* Input device TTY line discipline
*
* Copyright (c) 1999-2002 Vojtech Pavlik
*
* This is a module that converts a tty line into a much simpler
* 'serial io port' abstraction that the input device drivers use.
*/
/*
* 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 <asm/uaccess.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/tty.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Input device TTY line discipline");
MODULE_LICENSE("GPL");
MODULE_ALIAS_LDISC(N_MOUSE);
#define SERPORT_BUSY 1
#define SERPORT_ACTIVE 2
#define SERPORT_DEAD 3
struct serport {
struct tty_struct *tty;
wait_queue_head_t wait;
struct serio *serio;
struct serio_device_id id;
spinlock_t lock;
unsigned long flags;
};
/*
* Callback functions from the serio code.
*/
static int serport_serio_write(struct serio *serio, unsigned char data)
{
struct serport *serport = serio->port_data;
return -(serport->tty->ops->write(serport->tty, &data, 1) != 1);
}
static int serport_serio_open(struct serio *serio)
{
struct serport *serport = serio->port_data;
unsigned long flags;
spin_lock_irqsave(&serport->lock, flags);
set_bit(SERPORT_ACTIVE, &serport->flags);
spin_unlock_irqrestore(&serport->lock, flags);
return 0;
}
static void serport_serio_close(struct serio *serio)
{
struct serport *serport = serio->port_data;
unsigned long flags;
spin_lock_irqsave(&serport->lock, flags);
clear_bit(SERPORT_ACTIVE, &serport->flags);
set_bit(SERPORT_DEAD, &serport->flags);
spin_unlock_irqrestore(&serport->lock, flags);
wake_up_interruptible(&serport->wait);
}
/*
* serport_ldisc_open() is the routine that is called upon setting our line
* discipline on a tty. It prepares the serio struct.
*/
static int serport_ldisc_open(struct tty_struct *tty)
{
struct serport *serport;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
serport = kzalloc(sizeof(struct serport), GFP_KERNEL);
if (!serport)
return -ENOMEM;
serport->tty = tty;
spin_lock_init(&serport->lock);
init_waitqueue_head(&serport->wait);
tty->disc_data = serport;
tty->receive_room = 256;
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
return 0;
}
/*
* serport_ldisc_close() is the opposite of serport_ldisc_open()
*/
static void serport_ldisc_close(struct tty_struct *tty)
{
struct serport *serport = (struct serport *) tty->disc_data;
kfree(serport);
}
/*
* serport_ldisc_receive() is called by the low level tty driver when characters
* are ready for us. We forward the characters, one by one to the 'interrupt'
* routine.
*/
static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count)
{
struct serport *serport = (struct serport*) tty->disc_data;
unsigned long flags;
int i;
spin_lock_irqsave(&serport->lock, flags);
if (!test_bit(SERPORT_ACTIVE, &serport->flags))
goto out;
for (i = 0; i < count; i++)
serio_interrupt(serport->serio, cp[i], 0);
out:
spin_unlock_irqrestore(&serport->lock, flags);
}
/*
* serport_ldisc_read() just waits indefinitely if everything goes well.
* However, when the serio driver closes the serio port, it finishes,
* returning 0 characters.
*/
static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, unsigned char __user * buf, size_t nr)
{
struct serport *serport = (struct serport*) tty->disc_data;
struct serio *serio;
char name[64];
if (test_and_set_bit(SERPORT_BUSY, &serport->flags))
return -EBUSY;
serport->serio = serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (!serio)
return -ENOMEM;
strlcpy(serio->name, "Serial port", sizeof(serio->name));
snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", tty_name(tty, name));
serio->id = serport->id;
serio->id.type = SERIO_RS232;
serio->write = serport_serio_write;
serio->open = serport_serio_open;
serio->close = serport_serio_close;
serio->port_data = serport;
serio_register_port(serport->serio);
printk(KERN_INFO "serio: Serial port %s\n", tty_name(tty, name));
wait_event_interruptible(serport->wait, test_bit(SERPORT_DEAD, &serport->flags));
serio_unregister_port(serport->serio);
serport->serio = NULL;
clear_bit(SERPORT_DEAD, &serport->flags);
clear_bit(SERPORT_BUSY, &serport->flags);
return 0;
}
/*
* serport_ldisc_ioctl() allows to set the port protocol, and device ID
*/
static int serport_ldisc_ioctl(struct tty_struct * tty, struct file * file, unsigned int cmd, unsigned long arg)
{
struct serport *serport = (struct serport*) tty->disc_data;
unsigned long type;
if (cmd == SPIOCSTYPE) {
if (get_user(type, (unsigned long __user *) arg))
return -EFAULT;
serport->id.proto = type & 0x000000ff;
serport->id.id = (type & 0x0000ff00) >> 8;
serport->id.extra = (type & 0x00ff0000) >> 16;
return 0;
}
return -EINVAL;
}
static void serport_ldisc_write_wakeup(struct tty_struct * tty)
{
struct serport *serport = (struct serport *) tty->disc_data;
unsigned long flags;
spin_lock_irqsave(&serport->lock, flags);
if (test_bit(SERPORT_ACTIVE, &serport->flags))
serio_drv_write_wakeup(serport->serio);
spin_unlock_irqrestore(&serport->lock, flags);
}
/*
* The line discipline structure.
*/
static struct tty_ldisc_ops serport_ldisc = {
.owner = THIS_MODULE,
.name = "input",
.open = serport_ldisc_open,
.close = serport_ldisc_close,
.read = serport_ldisc_read,
.ioctl = serport_ldisc_ioctl,
.receive_buf = serport_ldisc_receive,
.write_wakeup = serport_ldisc_write_wakeup
};
/*
* The functions for insering/removing us as a module.
*/
static int __init serport_init(void)
{
int retval;
retval = tty_register_ldisc(N_MOUSE, &serport_ldisc);
if (retval)
printk(KERN_ERR "serport.c: Error registering line discipline.\n");
return retval;
}
static void __exit serport_exit(void)
{
tty_unregister_ldisc(N_MOUSE);
}
module_init(serport_init);
module_exit(serport_exit);

View File

@@ -0,0 +1,386 @@
/*
* Xilinx XPS PS/2 device driver
*
* (c) 2005 MontaVista Software, Inc.
* (c) 2008 Xilinx, Inc.
*
* 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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/serio.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/io.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#define DRIVER_NAME "xilinx_ps2"
/* Register offsets for the xps2 device */
#define XPS2_SRST_OFFSET 0x00000000 /* Software Reset register */
#define XPS2_STATUS_OFFSET 0x00000004 /* Status register */
#define XPS2_RX_DATA_OFFSET 0x00000008 /* Receive Data register */
#define XPS2_TX_DATA_OFFSET 0x0000000C /* Transmit Data register */
#define XPS2_GIER_OFFSET 0x0000002C /* Global Interrupt Enable reg */
#define XPS2_IPISR_OFFSET 0x00000030 /* Interrupt Status register */
#define XPS2_IPIER_OFFSET 0x00000038 /* Interrupt Enable register */
/* Reset Register Bit Definitions */
#define XPS2_SRST_RESET 0x0000000A /* Software Reset */
/* Status Register Bit Positions */
#define XPS2_STATUS_RX_FULL 0x00000001 /* Receive Full */
#define XPS2_STATUS_TX_FULL 0x00000002 /* Transmit Full */
/* Bit definitions for ISR/IER registers. Both the registers have the same bit
* definitions and are only defined once. */
#define XPS2_IPIXR_WDT_TOUT 0x00000001 /* Watchdog Timeout Interrupt */
#define XPS2_IPIXR_TX_NOACK 0x00000002 /* Transmit No ACK Interrupt */
#define XPS2_IPIXR_TX_ACK 0x00000004 /* Transmit ACK (Data) Interrupt */
#define XPS2_IPIXR_RX_OVF 0x00000008 /* Receive Overflow Interrupt */
#define XPS2_IPIXR_RX_ERR 0x00000010 /* Receive Error Interrupt */
#define XPS2_IPIXR_RX_FULL 0x00000020 /* Receive Data Interrupt */
/* Mask for all the Transmit Interrupts */
#define XPS2_IPIXR_TX_ALL (XPS2_IPIXR_TX_NOACK | XPS2_IPIXR_TX_ACK)
/* Mask for all the Receive Interrupts */
#define XPS2_IPIXR_RX_ALL (XPS2_IPIXR_RX_OVF | XPS2_IPIXR_RX_ERR | \
XPS2_IPIXR_RX_FULL)
/* Mask for all the Interrupts */
#define XPS2_IPIXR_ALL (XPS2_IPIXR_TX_ALL | XPS2_IPIXR_RX_ALL | \
XPS2_IPIXR_WDT_TOUT)
/* Global Interrupt Enable mask */
#define XPS2_GIER_GIE_MASK 0x80000000
struct xps2data {
int irq;
spinlock_t lock;
void __iomem *base_address; /* virt. address of control registers */
unsigned int flags;
struct serio serio; /* serio */
};
/************************************/
/* XPS PS/2 data transmission calls */
/************************************/
/**
* xps2_recv() - attempts to receive a byte from the PS/2 port.
* @drvdata: pointer to ps2 device private data structure
* @byte: address where the read data will be copied
*
* If there is any data available in the PS/2 receiver, this functions reads
* the data, otherwise it returns error.
*/
static int xps2_recv(struct xps2data *drvdata, u8 *byte)
{
u32 sr;
int status = -1;
/* If there is data available in the PS/2 receiver, read it */
sr = in_be32(drvdata->base_address + XPS2_STATUS_OFFSET);
if (sr & XPS2_STATUS_RX_FULL) {
*byte = in_be32(drvdata->base_address + XPS2_RX_DATA_OFFSET);
status = 0;
}
return status;
}
/*********************/
/* Interrupt handler */
/*********************/
static irqreturn_t xps2_interrupt(int irq, void *dev_id)
{
struct xps2data *drvdata = dev_id;
u32 intr_sr;
u8 c;
int status;
/* Get the PS/2 interrupts and clear them */
intr_sr = in_be32(drvdata->base_address + XPS2_IPISR_OFFSET);
out_be32(drvdata->base_address + XPS2_IPISR_OFFSET, intr_sr);
/* Check which interrupt is active */
if (intr_sr & XPS2_IPIXR_RX_OVF)
dev_warn(drvdata->serio.dev.parent, "receive overrun error\n");
if (intr_sr & XPS2_IPIXR_RX_ERR)
drvdata->flags |= SERIO_PARITY;
if (intr_sr & (XPS2_IPIXR_TX_NOACK | XPS2_IPIXR_WDT_TOUT))
drvdata->flags |= SERIO_TIMEOUT;
if (intr_sr & XPS2_IPIXR_RX_FULL) {
status = xps2_recv(drvdata, &c);
/* Error, if a byte is not received */
if (status) {
dev_err(drvdata->serio.dev.parent,
"wrong rcvd byte count (%d)\n", status);
} else {
serio_interrupt(&drvdata->serio, c, drvdata->flags);
drvdata->flags = 0;
}
}
return IRQ_HANDLED;
}
/*******************/
/* serio callbacks */
/*******************/
/**
* sxps2_write() - sends a byte out through the PS/2 port.
* @pserio: pointer to the serio structure of the PS/2 port
* @c: data that needs to be written to the PS/2 port
*
* This function checks if the PS/2 transmitter is empty and sends a byte.
* Otherwise it returns error. Transmission fails only when nothing is connected
* to the PS/2 port. Thats why, we do not try to resend the data in case of a
* failure.
*/
static int sxps2_write(struct serio *pserio, unsigned char c)
{
struct xps2data *drvdata = pserio->port_data;
unsigned long flags;
u32 sr;
int status = -1;
spin_lock_irqsave(&drvdata->lock, flags);
/* If the PS/2 transmitter is empty send a byte of data */
sr = in_be32(drvdata->base_address + XPS2_STATUS_OFFSET);
if (!(sr & XPS2_STATUS_TX_FULL)) {
out_be32(drvdata->base_address + XPS2_TX_DATA_OFFSET, c);
status = 0;
}
spin_unlock_irqrestore(&drvdata->lock, flags);
return status;
}
/**
* sxps2_open() - called when a port is opened by the higher layer.
* @pserio: pointer to the serio structure of the PS/2 device
*
* This function requests irq and enables interrupts for the PS/2 device.
*/
static int sxps2_open(struct serio *pserio)
{
struct xps2data *drvdata = pserio->port_data;
int error;
u8 c;
error = request_irq(drvdata->irq, &xps2_interrupt, 0,
DRIVER_NAME, drvdata);
if (error) {
dev_err(drvdata->serio.dev.parent,
"Couldn't allocate interrupt %d\n", drvdata->irq);
return error;
}
/* start reception by enabling the interrupts */
out_be32(drvdata->base_address + XPS2_GIER_OFFSET, XPS2_GIER_GIE_MASK);
out_be32(drvdata->base_address + XPS2_IPIER_OFFSET, XPS2_IPIXR_RX_ALL);
(void)xps2_recv(drvdata, &c);
return 0; /* success */
}
/**
* sxps2_close() - frees the interrupt.
* @pserio: pointer to the serio structure of the PS/2 device
*
* This function frees the irq and disables interrupts for the PS/2 device.
*/
static void sxps2_close(struct serio *pserio)
{
struct xps2data *drvdata = pserio->port_data;
/* Disable the PS2 interrupts */
out_be32(drvdata->base_address + XPS2_GIER_OFFSET, 0x00);
out_be32(drvdata->base_address + XPS2_IPIER_OFFSET, 0x00);
free_irq(drvdata->irq, drvdata);
}
/**
* xps2_of_probe - probe method for the PS/2 device.
* @of_dev: pointer to OF device structure
* @match: pointer to the stucture used for matching a device
*
* This function probes the PS/2 device in the device tree.
* It initializes the driver data structure and the hardware.
* It returns 0, if the driver is bound to the PS/2 device, or a negative
* value if there is an error.
*/
static int __devinit xps2_of_probe(struct of_device *ofdev,
const struct of_device_id *match)
{
struct resource r_irq; /* Interrupt resources */
struct resource r_mem; /* IO mem resources */
struct xps2data *drvdata;
struct serio *serio;
struct device *dev = &ofdev->dev;
resource_size_t remap_size, phys_addr;
int error;
dev_info(dev, "Device Tree Probing \'%s\'\n",
ofdev->node->name);
/* Get iospace for the device */
error = of_address_to_resource(ofdev->node, 0, &r_mem);
if (error) {
dev_err(dev, "invalid address\n");
return error;
}
/* Get IRQ for the device */
if (of_irq_to_resource(ofdev->node, 0, &r_irq) == NO_IRQ) {
dev_err(dev, "no IRQ found\n");
return -ENODEV;
}
drvdata = kzalloc(sizeof(struct xps2data), GFP_KERNEL);
if (!drvdata) {
dev_err(dev, "Couldn't allocate device private record\n");
return -ENOMEM;
}
dev_set_drvdata(dev, drvdata);
spin_lock_init(&drvdata->lock);
drvdata->irq = r_irq.start;
phys_addr = r_mem.start;
remap_size = r_mem.end - r_mem.start + 1;
if (!request_mem_region(phys_addr, remap_size, DRIVER_NAME)) {
dev_err(dev, "Couldn't lock memory region at 0x%08llX\n",
(unsigned long long)phys_addr);
error = -EBUSY;
goto failed1;
}
/* Fill in configuration data and add them to the list */
drvdata->base_address = ioremap(phys_addr, remap_size);
if (drvdata->base_address == NULL) {
dev_err(dev, "Couldn't ioremap memory at 0x%08llX\n",
(unsigned long long)phys_addr);
error = -EFAULT;
goto failed2;
}
/* Disable all the interrupts, just in case */
out_be32(drvdata->base_address + XPS2_IPIER_OFFSET, 0);
/* Reset the PS2 device and abort any current transaction, to make sure
* we have the PS2 in a good state */
out_be32(drvdata->base_address + XPS2_SRST_OFFSET, XPS2_SRST_RESET);
dev_info(dev, "Xilinx PS2 at 0x%08llX mapped to 0x%p, irq=%d\n",
(unsigned long long)phys_addr, drvdata->base_address,
drvdata->irq);
serio = &drvdata->serio;
serio->id.type = SERIO_8042;
serio->write = sxps2_write;
serio->open = sxps2_open;
serio->close = sxps2_close;
serio->port_data = drvdata;
serio->dev.parent = dev;
snprintf(serio->name, sizeof(serio->name),
"Xilinx XPS PS/2 at %08llX", (unsigned long long)phys_addr);
snprintf(serio->phys, sizeof(serio->phys),
"xilinxps2/serio at %08llX", (unsigned long long)phys_addr);
serio_register_port(serio);
return 0; /* success */
failed2:
release_mem_region(phys_addr, remap_size);
failed1:
kfree(drvdata);
dev_set_drvdata(dev, NULL);
return error;
}
/**
* xps2_of_remove - unbinds the driver from the PS/2 device.
* @of_dev: pointer to OF device structure
*
* This function is called if a device is physically removed from the system or
* if the driver module is being unloaded. It frees any resources allocated to
* the device.
*/
static int __devexit xps2_of_remove(struct of_device *of_dev)
{
struct device *dev = &of_dev->dev;
struct xps2data *drvdata = dev_get_drvdata(dev);
struct resource r_mem; /* IO mem resources */
serio_unregister_port(&drvdata->serio);
iounmap(drvdata->base_address);
/* Get iospace of the device */
if (of_address_to_resource(of_dev->node, 0, &r_mem))
dev_err(dev, "invalid address\n");
else
release_mem_region(r_mem.start, r_mem.end - r_mem.start + 1);
kfree(drvdata);
dev_set_drvdata(dev, NULL);
return 0;
}
/* Match table for of_platform binding */
static struct of_device_id xps2_of_match[] __devinitdata = {
{ .compatible = "xlnx,xps-ps2-1.00.a", },
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(of, xps2_of_match);
static struct of_platform_driver xps2_of_driver = {
.name = DRIVER_NAME,
.match_table = xps2_of_match,
.probe = xps2_of_probe,
.remove = __devexit_p(xps2_of_remove),
};
static int __init xps2_init(void)
{
return of_register_platform_driver(&xps2_of_driver);
}
static void __exit xps2_cleanup(void)
{
of_unregister_platform_driver(&xps2_of_driver);
}
module_init(xps2_init);
module_exit(xps2_cleanup);
MODULE_AUTHOR("Xilinx, Inc.");
MODULE_DESCRIPTION("Xilinx XPS PS/2 driver");
MODULE_LICENSE("GPL");