Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

284
drivers/input/serio/Kconfig Normal file
View file

@ -0,0 +1,284 @@
#
# Input core configuration
#
config SERIO
tristate "Serial I/O support"
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.
config ARCH_MIGHT_HAVE_PC_SERIO
bool
help
Select this config option from the architecture Kconfig if
the architecture might use a PC serio device (i8042) to
communicate with keyboard, mouse, etc.
if SERIO
config SERIO_I8042
tristate "i8042 PC Keyboard controller"
default y
depends on ARCH_MIGHT_HAVE_PC_SERIO
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
depends on TTY
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"
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.
config SERIO_ALTERA_PS2
tristate "Altera UP PS/2 controller"
depends on HAS_IOMEM
help
Say Y here if you have Altera University Program PS/2 ports.
To compile this driver as a module, choose M here: the
module will be called altera_ps2.
config SERIO_AMS_DELTA
tristate "Amstrad Delta (E3) mailboard support"
depends on MACH_AMS_DELTA
default y
---help---
Say Y here if you have an E3 and want to use its mailboard,
or any standard AT keyboard connected to the mailboard port.
When used for the E3 mailboard, a non-standard key table
must be loaded from userspace, possibly using udev extras
provided keymap helper utility.
To compile this driver as a module, choose M here;
the module will be called ams_delta_serio.
config SERIO_PS2MULT
tristate "TQC PS/2 multiplexer"
help
Say Y here if you have the PS/2 line multiplexer like the one
present on TQC boards.
To compile this driver as a module, choose M here: the
module will be called ps2mult.
config SERIO_ARC_PS2
tristate "ARC PS/2 support"
help
Say Y here if you have an ARC FPGA platform with a PS/2
controller in it.
To compile this driver as a module, choose M here; the module
will be called arc_ps2.
config SERIO_APBPS2
tristate "GRLIB APBPS2 PS/2 keyboard/mouse controller"
depends on OF
help
Say Y here if you want support for GRLIB APBPS2 peripherals used
to connect to PS/2 keyboard and/or mouse.
To compile this driver as a module, choose M here: the module will
be called apbps2.
config SERIO_OLPC_APSP
tristate "OLPC AP-SP input support"
depends on OLPC || COMPILE_TEST
help
Say Y here if you want support for the keyboard and touchpad included
in the OLPC XO-1.75 and XO-4 laptops.
To compile this driver as a module, choose M here: the module will
be called olpc_apsp.
config HYPERV_KEYBOARD
tristate "Microsoft Synthetic Keyboard driver"
depends on HYPERV
default HYPERV
help
Select this option to enable the Hyper-V Keyboard driver.
To compile this driver as a module, choose M here: the module will
be called hyperv_keyboard.
endif

View file

@ -0,0 +1,31 @@
#
# 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_PS2MULT) += ps2mult.o
obj-$(CONFIG_SERIO_MACEPS2) += maceps2.o
obj-$(CONFIG_SERIO_LIBPS2) += libps2.o
obj-$(CONFIG_SERIO_RAW) += serio_raw.o
obj-$(CONFIG_SERIO_AMS_DELTA) += ams_delta_serio.o
obj-$(CONFIG_SERIO_XILINX_XPS_PS2) += xilinx_ps2.o
obj-$(CONFIG_SERIO_ALTERA_PS2) += altera_ps2.o
obj-$(CONFIG_SERIO_ARC_PS2) += arc_ps2.o
obj-$(CONFIG_SERIO_APBPS2) += apbps2.o
obj-$(CONFIG_SERIO_OLPC_APSP) += olpc_apsp.o
obj-$(CONFIG_HYPERV_KEYBOARD) += hyperv-keyboard.o

View file

@ -0,0 +1,201 @@
/*
* Altera University Program PS2 controller driver
*
* Copyright (C) 2008 Thomas Chou <thomas@wytron.com.tw>
*
* Based on sa1111ps2.c, which is:
* Copyright (C) 2002 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/of.h>
#define DRV_NAME "altera_ps2"
struct ps2if {
struct serio *io;
struct resource *iomem_res;
void __iomem *base;
unsigned irq;
};
/*
* Read all bytes waiting in the PS2 port. There should be
* at the most one, but we loop for safety.
*/
static irqreturn_t altera_ps2_rxint(int irq, void *dev_id)
{
struct ps2if *ps2if = dev_id;
unsigned int status;
irqreturn_t handled = IRQ_NONE;
while ((status = readl(ps2if->base)) & 0xffff0000) {
serio_interrupt(ps2if->io, status & 0xff, 0);
handled = IRQ_HANDLED;
}
return handled;
}
/*
* Write a byte to the PS2 port.
*/
static int altera_ps2_write(struct serio *io, unsigned char val)
{
struct ps2if *ps2if = io->port_data;
writel(val, ps2if->base);
return 0;
}
static int altera_ps2_open(struct serio *io)
{
struct ps2if *ps2if = io->port_data;
/* clear fifo */
while (readl(ps2if->base) & 0xffff0000)
/* empty */;
writel(1, ps2if->base + 4); /* enable rx irq */
return 0;
}
static void altera_ps2_close(struct serio *io)
{
struct ps2if *ps2if = io->port_data;
writel(0, ps2if->base + 4); /* disable rx irq */
}
/*
* Add one device to this driver.
*/
static int altera_ps2_probe(struct platform_device *pdev)
{
struct ps2if *ps2if;
struct serio *serio;
int error, irq;
ps2if = kzalloc(sizeof(struct ps2if), GFP_KERNEL);
serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (!ps2if || !serio) {
error = -ENOMEM;
goto err_free_mem;
}
serio->id.type = SERIO_8042;
serio->write = altera_ps2_write;
serio->open = altera_ps2_open;
serio->close = altera_ps2_close;
strlcpy(serio->name, dev_name(&pdev->dev), sizeof(serio->name));
strlcpy(serio->phys, dev_name(&pdev->dev), sizeof(serio->phys));
serio->port_data = ps2if;
serio->dev.parent = &pdev->dev;
ps2if->io = serio;
ps2if->iomem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (ps2if->iomem_res == NULL) {
error = -ENOENT;
goto err_free_mem;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
error = -ENXIO;
goto err_free_mem;
}
ps2if->irq = irq;
if (!request_mem_region(ps2if->iomem_res->start,
resource_size(ps2if->iomem_res), pdev->name)) {
error = -EBUSY;
goto err_free_mem;
}
ps2if->base = ioremap(ps2if->iomem_res->start,
resource_size(ps2if->iomem_res));
if (!ps2if->base) {
error = -ENOMEM;
goto err_free_res;
}
error = request_irq(ps2if->irq, altera_ps2_rxint, 0, pdev->name, ps2if);
if (error) {
dev_err(&pdev->dev, "could not allocate IRQ %d: %d\n",
ps2if->irq, error);
goto err_unmap;
}
dev_info(&pdev->dev, "base %p, irq %d\n", ps2if->base, ps2if->irq);
serio_register_port(ps2if->io);
platform_set_drvdata(pdev, ps2if);
return 0;
err_unmap:
iounmap(ps2if->base);
err_free_res:
release_mem_region(ps2if->iomem_res->start,
resource_size(ps2if->iomem_res));
err_free_mem:
kfree(ps2if);
kfree(serio);
return error;
}
/*
* Remove one device from this driver.
*/
static int altera_ps2_remove(struct platform_device *pdev)
{
struct ps2if *ps2if = platform_get_drvdata(pdev);
serio_unregister_port(ps2if->io);
free_irq(ps2if->irq, ps2if);
iounmap(ps2if->base);
release_mem_region(ps2if->iomem_res->start,
resource_size(ps2if->iomem_res));
kfree(ps2if);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id altera_ps2_match[] = {
{ .compatible = "ALTR,ps2-1.0", },
{ .compatible = "altr,ps2-1.0", },
{},
};
MODULE_DEVICE_TABLE(of, altera_ps2_match);
#endif /* CONFIG_OF */
/*
* Our device driver structure
*/
static struct platform_driver altera_ps2_driver = {
.probe = altera_ps2_probe,
.remove = altera_ps2_remove,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(altera_ps2_match),
},
};
module_platform_driver(altera_ps2_driver);
MODULE_DESCRIPTION("Altera University Program PS2 controller driver");
MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);

View file

@ -0,0 +1,213 @@
/*
* 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/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_prepare_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, IRQF_SHARED, "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_unprepare(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_unprepare(kmi->clk);
}
static int amba_kmi_probe(struct amba_device *dev,
const 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);
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 }
};
MODULE_DEVICE_TABLE(amba, amba_kmi_idtable);
static struct amba_driver ambakmi_driver = {
.drv = {
.name = "kmi-pl050",
.owner = THIS_MODULE,
},
.id_table = amba_kmi_idtable,
.probe = amba_kmi_probe,
.remove = amba_kmi_remove,
.resume = amba_kmi_resume,
};
module_amba_driver(ambakmi_driver);
MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
MODULE_DESCRIPTION("AMBA KMI controller driver");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,191 @@
/*
* Amstrad E3 (Delta) keyboard port driver
*
* Copyright (c) 2006 Matt Callow
* Copyright (c) 2010 Janusz Krzysztofik
*
* 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.
*
* Thanks to Cliff Lawson for his help
*
* The Amstrad Delta keyboard (aka mailboard) uses normal PC-AT style serial
* transmission. The keyboard port is formed of two GPIO lines, for clock
* and data. Due to strict timing requirements of the interface,
* the serial data stream is read and processed by a FIQ handler.
* The resulting words are fetched by this driver from a circular buffer.
*
* Standard AT keyboard driver (atkbd) is used for handling the keyboard data.
* However, when used with the E3 mailboard that producecs non-standard
* scancodes, a custom key table must be prepared and loaded from userspace.
*/
#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/serio.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <asm/mach-types.h>
#include <mach/board-ams-delta.h>
#include <mach/ams-delta-fiq.h>
MODULE_AUTHOR("Matt Callow");
MODULE_DESCRIPTION("AMS Delta (E3) keyboard port driver");
MODULE_LICENSE("GPL");
static struct serio *ams_delta_serio;
static int check_data(int data)
{
int i, parity = 0;
/* check valid stop bit */
if (!(data & 0x400)) {
dev_warn(&ams_delta_serio->dev,
"invalid stop bit, data=0x%X\n",
data);
return SERIO_FRAME;
}
/* calculate the parity */
for (i = 1; i < 10; i++) {
if (data & (1 << i))
parity++;
}
/* it should be odd */
if (!(parity & 0x01)) {
dev_warn(&ams_delta_serio->dev,
"paritiy check failed, data=0x%X parity=0x%X\n",
data, parity);
return SERIO_PARITY;
}
return 0;
}
static irqreturn_t ams_delta_serio_interrupt(int irq, void *dev_id)
{
int *circ_buff = &fiq_buffer[FIQ_CIRC_BUFF];
int data, dfl;
u8 scancode;
fiq_buffer[FIQ_IRQ_PEND] = 0;
/*
* Read data from the circular buffer, check it
* and then pass it on the serio
*/
while (fiq_buffer[FIQ_KEYS_CNT] > 0) {
data = circ_buff[fiq_buffer[FIQ_HEAD_OFFSET]++];
fiq_buffer[FIQ_KEYS_CNT]--;
if (fiq_buffer[FIQ_HEAD_OFFSET] == fiq_buffer[FIQ_BUF_LEN])
fiq_buffer[FIQ_HEAD_OFFSET] = 0;
dfl = check_data(data);
scancode = (u8) (data >> 1) & 0xFF;
serio_interrupt(ams_delta_serio, scancode, dfl);
}
return IRQ_HANDLED;
}
static int ams_delta_serio_open(struct serio *serio)
{
/* enable keyboard */
gpio_set_value(AMS_DELTA_GPIO_PIN_KEYBRD_PWR, 1);
return 0;
}
static void ams_delta_serio_close(struct serio *serio)
{
/* disable keyboard */
gpio_set_value(AMS_DELTA_GPIO_PIN_KEYBRD_PWR, 0);
}
static const struct gpio ams_delta_gpios[] __initconst_or_module = {
{
.gpio = AMS_DELTA_GPIO_PIN_KEYBRD_DATA,
.flags = GPIOF_DIR_IN,
.label = "serio-data",
},
{
.gpio = AMS_DELTA_GPIO_PIN_KEYBRD_CLK,
.flags = GPIOF_DIR_IN,
.label = "serio-clock",
},
{
.gpio = AMS_DELTA_GPIO_PIN_KEYBRD_PWR,
.flags = GPIOF_OUT_INIT_LOW,
.label = "serio-power",
},
{
.gpio = AMS_DELTA_GPIO_PIN_KEYBRD_DATAOUT,
.flags = GPIOF_OUT_INIT_LOW,
.label = "serio-dataout",
},
};
static int __init ams_delta_serio_init(void)
{
int err;
if (!machine_is_ams_delta())
return -ENODEV;
ams_delta_serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (!ams_delta_serio)
return -ENOMEM;
ams_delta_serio->id.type = SERIO_8042;
ams_delta_serio->open = ams_delta_serio_open;
ams_delta_serio->close = ams_delta_serio_close;
strlcpy(ams_delta_serio->name, "AMS DELTA keyboard adapter",
sizeof(ams_delta_serio->name));
strlcpy(ams_delta_serio->phys, "GPIO/serio0",
sizeof(ams_delta_serio->phys));
err = gpio_request_array(ams_delta_gpios,
ARRAY_SIZE(ams_delta_gpios));
if (err) {
pr_err("ams_delta_serio: Couldn't request gpio pins\n");
goto serio;
}
err = request_irq(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK),
ams_delta_serio_interrupt, IRQ_TYPE_EDGE_RISING,
"ams-delta-serio", 0);
if (err < 0) {
pr_err("ams_delta_serio: couldn't request gpio interrupt %d\n",
gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK));
goto gpio;
}
/*
* Since GPIO register handling for keyboard clock pin is performed
* at FIQ level, switch back from edge to simple interrupt handler
* to avoid bad interaction.
*/
irq_set_handler(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK),
handle_simple_irq);
serio_register_port(ams_delta_serio);
dev_info(&ams_delta_serio->dev, "%s\n", ams_delta_serio->name);
return 0;
gpio:
gpio_free_array(ams_delta_gpios,
ARRAY_SIZE(ams_delta_gpios));
serio:
kfree(ams_delta_serio);
return err;
}
module_init(ams_delta_serio_init);
static void __exit ams_delta_serio_exit(void)
{
serio_unregister_port(ams_delta_serio);
free_irq(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), 0);
gpio_free_array(ams_delta_gpios,
ARRAY_SIZE(ams_delta_gpios));
}
module_exit(ams_delta_serio_exit);

View file

@ -0,0 +1,228 @@
/*
* Copyright (C) 2013 Aeroflex Gaisler
*
* This driver supports the APBPS2 PS/2 core available in the GRLIB
* VHDL IP core library.
*
* Full documentation of the APBPS2 core can be found here:
* http://www.gaisler.com/products/grlib/grip.pdf
*
* See "Documentation/devicetree/bindings/input/ps2keyb-mouse-apbps2.txt" for
* information on open firmware properties.
*
* 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.
*
* Contributors: Daniel Hellstrom <daniel@gaisler.com>
*/
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/module.h>
#include <linux/serio.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/io.h>
struct apbps2_regs {
u32 __iomem data; /* 0x00 */
u32 __iomem status; /* 0x04 */
u32 __iomem ctrl; /* 0x08 */
u32 __iomem reload; /* 0x0c */
};
#define APBPS2_STATUS_DR (1<<0)
#define APBPS2_STATUS_PE (1<<1)
#define APBPS2_STATUS_FE (1<<2)
#define APBPS2_STATUS_KI (1<<3)
#define APBPS2_STATUS_RF (1<<4)
#define APBPS2_STATUS_TF (1<<5)
#define APBPS2_STATUS_TCNT (0x1f<<22)
#define APBPS2_STATUS_RCNT (0x1f<<27)
#define APBPS2_CTRL_RE (1<<0)
#define APBPS2_CTRL_TE (1<<1)
#define APBPS2_CTRL_RI (1<<2)
#define APBPS2_CTRL_TI (1<<3)
struct apbps2_priv {
struct serio *io;
struct apbps2_regs *regs;
};
static int apbps2_idx;
static irqreturn_t apbps2_isr(int irq, void *dev_id)
{
struct apbps2_priv *priv = dev_id;
unsigned long status, data, rxflags;
irqreturn_t ret = IRQ_NONE;
while ((status = ioread32be(&priv->regs->status)) & APBPS2_STATUS_DR) {
data = ioread32be(&priv->regs->data);
rxflags = (status & APBPS2_STATUS_PE) ? SERIO_PARITY : 0;
rxflags |= (status & APBPS2_STATUS_FE) ? SERIO_FRAME : 0;
/* clear error bits? */
if (rxflags)
iowrite32be(0, &priv->regs->status);
serio_interrupt(priv->io, data, rxflags);
ret = IRQ_HANDLED;
}
return ret;
}
static int apbps2_write(struct serio *io, unsigned char val)
{
struct apbps2_priv *priv = io->port_data;
unsigned int tleft = 10000; /* timeout in 100ms */
/* delay until PS/2 controller has room for more chars */
while ((ioread32be(&priv->regs->status) & APBPS2_STATUS_TF) && tleft--)
udelay(10);
if ((ioread32be(&priv->regs->status) & APBPS2_STATUS_TF) == 0) {
iowrite32be(val, &priv->regs->data);
iowrite32be(APBPS2_CTRL_RE | APBPS2_CTRL_RI | APBPS2_CTRL_TE,
&priv->regs->ctrl);
return 0;
}
return -ETIMEDOUT;
}
static int apbps2_open(struct serio *io)
{
struct apbps2_priv *priv = io->port_data;
int limit;
unsigned long tmp;
/* clear error flags */
iowrite32be(0, &priv->regs->status);
/* Clear old data if available (unlikely) */
limit = 1024;
while ((ioread32be(&priv->regs->status) & APBPS2_STATUS_DR) && --limit)
tmp = ioread32be(&priv->regs->data);
/* Enable reciever and it's interrupt */
iowrite32be(APBPS2_CTRL_RE | APBPS2_CTRL_RI, &priv->regs->ctrl);
return 0;
}
static void apbps2_close(struct serio *io)
{
struct apbps2_priv *priv = io->port_data;
/* stop interrupts at PS/2 HW level */
iowrite32be(0, &priv->regs->ctrl);
}
/* Initialize one APBPS2 PS/2 core */
static int apbps2_of_probe(struct platform_device *ofdev)
{
struct apbps2_priv *priv;
int irq, err;
u32 freq_hz;
struct resource *res;
priv = devm_kzalloc(&ofdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
dev_err(&ofdev->dev, "memory allocation failed\n");
return -ENOMEM;
}
/* Find Device Address */
res = platform_get_resource(ofdev, IORESOURCE_MEM, 0);
priv->regs = devm_ioremap_resource(&ofdev->dev, res);
if (IS_ERR(priv->regs))
return PTR_ERR(priv->regs);
/* Reset hardware, disable interrupt */
iowrite32be(0, &priv->regs->ctrl);
/* IRQ */
irq = irq_of_parse_and_map(ofdev->dev.of_node, 0);
err = devm_request_irq(&ofdev->dev, irq, apbps2_isr,
IRQF_SHARED, "apbps2", priv);
if (err) {
dev_err(&ofdev->dev, "request IRQ%d failed\n", irq);
return err;
}
/* Get core frequency */
if (of_property_read_u32(ofdev->dev.of_node, "freq", &freq_hz)) {
dev_err(&ofdev->dev, "unable to get core frequency\n");
return -EINVAL;
}
/* Set reload register to core freq in kHz/10 */
iowrite32be(freq_hz / 10000, &priv->regs->reload);
priv->io = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (!priv->io)
return -ENOMEM;
priv->io->id.type = SERIO_8042;
priv->io->open = apbps2_open;
priv->io->close = apbps2_close;
priv->io->write = apbps2_write;
priv->io->port_data = priv;
strlcpy(priv->io->name, "APBPS2 PS/2", sizeof(priv->io->name));
snprintf(priv->io->phys, sizeof(priv->io->phys),
"apbps2_%d", apbps2_idx++);
dev_info(&ofdev->dev, "irq = %d, base = 0x%p\n", irq, priv->regs);
serio_register_port(priv->io);
platform_set_drvdata(ofdev, priv);
return 0;
}
static int apbps2_of_remove(struct platform_device *of_dev)
{
struct apbps2_priv *priv = platform_get_drvdata(of_dev);
serio_unregister_port(priv->io);
return 0;
}
static const struct of_device_id apbps2_of_match[] = {
{ .name = "GAISLER_APBPS2", },
{ .name = "01_060", },
{}
};
MODULE_DEVICE_TABLE(of, apbps2_of_match);
static struct platform_driver apbps2_of_driver = {
.driver = {
.name = "grlib-apbps2",
.owner = THIS_MODULE,
.of_match_table = apbps2_of_match,
},
.probe = apbps2_of_probe,
.remove = apbps2_of_remove,
};
module_platform_driver(apbps2_of_driver);
MODULE_AUTHOR("Aeroflex Gaisler AB.");
MODULE_DESCRIPTION("GRLIB APBPS2 PS/2 serial I/O");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,280 @@
/*
* Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
*
* 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.
*
* Driver is originally developed by Pavel Sokolov <psokolov@synopsys.com>
*/
#include <linux/err.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#define ARC_PS2_PORTS 2
#define ARC_ARC_PS2_ID 0x0001f609
#define STAT_TIMEOUT 128
#define PS2_STAT_RX_FRM_ERR (1)
#define PS2_STAT_RX_BUF_OVER (1 << 1)
#define PS2_STAT_RX_INT_EN (1 << 2)
#define PS2_STAT_RX_VAL (1 << 3)
#define PS2_STAT_TX_ISNOT_FUL (1 << 4)
#define PS2_STAT_TX_INT_EN (1 << 5)
struct arc_ps2_port {
void __iomem *data_addr;
void __iomem *status_addr;
struct serio *io;
};
struct arc_ps2_data {
struct arc_ps2_port port[ARC_PS2_PORTS];
void __iomem *addr;
unsigned int frame_error;
unsigned int buf_overflow;
unsigned int total_int;
};
static void arc_ps2_check_rx(struct arc_ps2_data *arc_ps2,
struct arc_ps2_port *port)
{
unsigned int timeout = 1000;
unsigned int flag, status;
unsigned char data;
do {
status = ioread32(port->status_addr);
if (!(status & PS2_STAT_RX_VAL))
return;
data = ioread32(port->data_addr) & 0xff;
flag = 0;
arc_ps2->total_int++;
if (status & PS2_STAT_RX_FRM_ERR) {
arc_ps2->frame_error++;
flag |= SERIO_PARITY;
} else if (status & PS2_STAT_RX_BUF_OVER) {
arc_ps2->buf_overflow++;
flag |= SERIO_FRAME;
}
serio_interrupt(port->io, data, flag);
} while (--timeout);
dev_err(&port->io->dev, "PS/2 hardware stuck\n");
}
static irqreturn_t arc_ps2_interrupt(int irq, void *dev)
{
struct arc_ps2_data *arc_ps2 = dev;
int i;
for (i = 0; i < ARC_PS2_PORTS; i++)
arc_ps2_check_rx(arc_ps2, &arc_ps2->port[i]);
return IRQ_HANDLED;
}
static int arc_ps2_write(struct serio *io, unsigned char val)
{
unsigned status;
struct arc_ps2_port *port = io->port_data;
int timeout = STAT_TIMEOUT;
do {
status = ioread32(port->status_addr);
cpu_relax();
if (status & PS2_STAT_TX_ISNOT_FUL) {
iowrite32(val & 0xff, port->data_addr);
return 0;
}
} while (--timeout);
dev_err(&io->dev, "write timeout\n");
return -ETIMEDOUT;
}
static int arc_ps2_open(struct serio *io)
{
struct arc_ps2_port *port = io->port_data;
iowrite32(PS2_STAT_RX_INT_EN, port->status_addr);
return 0;
}
static void arc_ps2_close(struct serio *io)
{
struct arc_ps2_port *port = io->port_data;
iowrite32(ioread32(port->status_addr) & ~PS2_STAT_RX_INT_EN,
port->status_addr);
}
static void __iomem *arc_ps2_calc_addr(struct arc_ps2_data *arc_ps2,
int index, bool status)
{
void __iomem *addr;
addr = arc_ps2->addr + 4 + 4 * index;
if (status)
addr += ARC_PS2_PORTS * 4;
return addr;
}
static void arc_ps2_inhibit_ports(struct arc_ps2_data *arc_ps2)
{
void __iomem *addr;
u32 val;
int i;
for (i = 0; i < ARC_PS2_PORTS; i++) {
addr = arc_ps2_calc_addr(arc_ps2, i, true);
val = ioread32(addr);
val &= ~(PS2_STAT_RX_INT_EN | PS2_STAT_TX_INT_EN);
iowrite32(val, addr);
}
}
static int arc_ps2_create_port(struct platform_device *pdev,
struct arc_ps2_data *arc_ps2,
int index)
{
struct arc_ps2_port *port = &arc_ps2->port[index];
struct serio *io;
io = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (!io)
return -ENOMEM;
io->id.type = SERIO_8042;
io->write = arc_ps2_write;
io->open = arc_ps2_open;
io->close = arc_ps2_close;
snprintf(io->name, sizeof(io->name), "ARC PS/2 port%d", index);
snprintf(io->phys, sizeof(io->phys), "arc/serio%d", index);
io->port_data = port;
port->io = io;
port->data_addr = arc_ps2_calc_addr(arc_ps2, index, false);
port->status_addr = arc_ps2_calc_addr(arc_ps2, index, true);
dev_dbg(&pdev->dev, "port%d is allocated (data = 0x%p, status = 0x%p)\n",
index, port->data_addr, port->status_addr);
serio_register_port(port->io);
return 0;
}
static int arc_ps2_probe(struct platform_device *pdev)
{
struct arc_ps2_data *arc_ps2;
struct resource *res;
int irq;
int error, id, i;
irq = platform_get_irq_byname(pdev, "arc_ps2_irq");
if (irq < 0) {
dev_err(&pdev->dev, "no IRQ defined\n");
return -EINVAL;
}
arc_ps2 = devm_kzalloc(&pdev->dev, sizeof(struct arc_ps2_data),
GFP_KERNEL);
if (!arc_ps2) {
dev_err(&pdev->dev, "out of memory\n");
return -ENOMEM;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
arc_ps2->addr = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(arc_ps2->addr))
return PTR_ERR(arc_ps2->addr);
dev_info(&pdev->dev, "irq = %d, address = 0x%p, ports = %i\n",
irq, arc_ps2->addr, ARC_PS2_PORTS);
id = ioread32(arc_ps2->addr);
if (id != ARC_ARC_PS2_ID) {
dev_err(&pdev->dev, "device id does not match\n");
return -ENXIO;
}
arc_ps2_inhibit_ports(arc_ps2);
error = devm_request_irq(&pdev->dev, irq, arc_ps2_interrupt,
0, "arc_ps2", arc_ps2);
if (error) {
dev_err(&pdev->dev, "Could not allocate IRQ\n");
return error;
}
for (i = 0; i < ARC_PS2_PORTS; i++) {
error = arc_ps2_create_port(pdev, arc_ps2, i);
if (error) {
while (--i >= 0)
serio_unregister_port(arc_ps2->port[i].io);
return error;
}
}
platform_set_drvdata(pdev, arc_ps2);
return 0;
}
static int arc_ps2_remove(struct platform_device *pdev)
{
struct arc_ps2_data *arc_ps2 = platform_get_drvdata(pdev);
int i;
for (i = 0; i < ARC_PS2_PORTS; i++)
serio_unregister_port(arc_ps2->port[i].io);
dev_dbg(&pdev->dev, "interrupt count = %i\n", arc_ps2->total_int);
dev_dbg(&pdev->dev, "frame error count = %i\n", arc_ps2->frame_error);
dev_dbg(&pdev->dev, "buffer overflow count = %i\n",
arc_ps2->buf_overflow);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id arc_ps2_match[] = {
{ .compatible = "snps,arc_ps2" },
{ },
};
MODULE_DEVICE_TABLE(of, arc_ps2_match);
#endif
static struct platform_driver arc_ps2_driver = {
.driver = {
.name = "arc_ps2",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(arc_ps2_match),
},
.probe = arc_ps2_probe,
.remove = arc_ps2_remove,
};
module_platform_driver(arc_ps2_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Pavel Sokolov <psokolov@synopsys.com>");
MODULE_DESCRIPTION("ARC PS/2 Driver");

View file

@ -0,0 +1,364 @@
/*
* 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>
#include <linux/slab.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;
/* Prevent concurrent writes to PSIF THR. */
spinlock_t lock;
bool open;
};
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--)
udelay(50);
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 = true;
out:
return retval;
}
static void psif_close(struct serio *io)
{
struct psif *psif = io->port_data;
psif->open = false;
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);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int psif_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
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 device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
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;
}
#endif
static SIMPLE_DEV_PM_OPS(psif_pm_ops, psif_suspend, psif_resume);
static struct platform_driver psif_driver = {
.remove = __exit_p(psif_remove),
.driver = {
.name = "atmel_psif",
.owner = THIS_MODULE,
.pm = &psif_pm_ops,
},
};
module_platform_driver_probe(psif_driver, psif_probe);
MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");
MODULE_DESCRIPTION("Atmel AVR32 PSIF PS/2 driver");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,261 @@
/*
* 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 <linux/slab.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;
int err;
err = request_irq(CT82C710_IRQ, ct82c710_interrupt, 0, "ct82c710", NULL);
if (err)
return err;
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 -EBUSY;
}
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 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);
printk(KERN_INFO "serio: C&T 82c710 mouse port at %#llx irq %d\n",
(unsigned long long)CT82C710_DATA, CT82C710_IRQ);
return 0;
}
static int 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 = 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;
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,464 @@
/*
* 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/slab.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 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-ps2-%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 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

1135
drivers/input/serio/hp_sdc.c Normal file

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 "Unknown 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;
int err;
#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;
err = hil_mlc_register(mlc);
if (err) {
printk(KERN_WARNING PREFIX "Failed to register MLC structure with hil_mlc\n");
return err;
}
if (hp_sdc_request_hil_irq(&hp_sdc_mlc_isr)) {
printk(KERN_WARNING PREFIX "Request for raw HIL ISR hook denied\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");
return -EBUSY;
}
return 0;
}
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,452 @@
/*
* Copyright (c) 2013, Microsoft Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/completion.h>
#include <linux/hyperv.h>
#include <linux/serio.h>
#include <linux/slab.h>
/*
* Current version 1.0
*
*/
#define SYNTH_KBD_VERSION_MAJOR 1
#define SYNTH_KBD_VERSION_MINOR 0
#define SYNTH_KBD_VERSION (SYNTH_KBD_VERSION_MINOR | \
(SYNTH_KBD_VERSION_MAJOR << 16))
/*
* Message types in the synthetic input protocol
*/
enum synth_kbd_msg_type {
SYNTH_KBD_PROTOCOL_REQUEST = 1,
SYNTH_KBD_PROTOCOL_RESPONSE = 2,
SYNTH_KBD_EVENT = 3,
SYNTH_KBD_LED_INDICATORS = 4,
};
/*
* Basic message structures.
*/
struct synth_kbd_msg_hdr {
__le32 type;
};
struct synth_kbd_msg {
struct synth_kbd_msg_hdr header;
char data[]; /* Enclosed message */
};
union synth_kbd_version {
__le32 version;
};
/*
* Protocol messages
*/
struct synth_kbd_protocol_request {
struct synth_kbd_msg_hdr header;
union synth_kbd_version version_requested;
};
#define PROTOCOL_ACCEPTED BIT(0)
struct synth_kbd_protocol_response {
struct synth_kbd_msg_hdr header;
__le32 proto_status;
};
#define IS_UNICODE BIT(0)
#define IS_BREAK BIT(1)
#define IS_E0 BIT(2)
#define IS_E1 BIT(3)
struct synth_kbd_keystroke {
struct synth_kbd_msg_hdr header;
__le16 make_code;
__le16 reserved0;
__le32 info; /* Additional information */
};
#define HK_MAXIMUM_MESSAGE_SIZE 256
#define KBD_VSC_SEND_RING_BUFFER_SIZE (10 * PAGE_SIZE)
#define KBD_VSC_RECV_RING_BUFFER_SIZE (10 * PAGE_SIZE)
#define XTKBD_EMUL0 0xe0
#define XTKBD_EMUL1 0xe1
#define XTKBD_RELEASE 0x80
/*
* Represents a keyboard device
*/
struct hv_kbd_dev {
struct hv_device *hv_dev;
struct serio *hv_serio;
struct synth_kbd_protocol_request protocol_req;
struct synth_kbd_protocol_response protocol_resp;
/* Synchronize the request/response if needed */
struct completion wait_event;
spinlock_t lock; /* protects 'started' field */
bool started;
};
static void hv_kbd_on_receive(struct hv_device *hv_dev,
struct synth_kbd_msg *msg, u32 msg_length)
{
struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev);
struct synth_kbd_keystroke *ks_msg;
unsigned long flags;
u32 msg_type = __le32_to_cpu(msg->header.type);
u32 info;
u16 scan_code;
switch (msg_type) {
case SYNTH_KBD_PROTOCOL_RESPONSE:
/*
* Validate the information provided by the host.
* If the host is giving us a bogus packet,
* drop the packet (hoping the problem
* goes away).
*/
if (msg_length < sizeof(struct synth_kbd_protocol_response)) {
dev_err(&hv_dev->device,
"Illegal protocol response packet (len: %d)\n",
msg_length);
break;
}
memcpy(&kbd_dev->protocol_resp, msg,
sizeof(struct synth_kbd_protocol_response));
complete(&kbd_dev->wait_event);
break;
case SYNTH_KBD_EVENT:
/*
* Validate the information provided by the host.
* If the host is giving us a bogus packet,
* drop the packet (hoping the problem
* goes away).
*/
if (msg_length < sizeof(struct synth_kbd_keystroke)) {
dev_err(&hv_dev->device,
"Illegal keyboard event packet (len: %d)\n",
msg_length);
break;
}
ks_msg = (struct synth_kbd_keystroke *)msg;
info = __le32_to_cpu(ks_msg->info);
/*
* Inject the information through the serio interrupt.
*/
spin_lock_irqsave(&kbd_dev->lock, flags);
if (kbd_dev->started) {
if (info & IS_E0)
serio_interrupt(kbd_dev->hv_serio,
XTKBD_EMUL0, 0);
if (info & IS_E1)
serio_interrupt(kbd_dev->hv_serio,
XTKBD_EMUL1, 0);
scan_code = __le16_to_cpu(ks_msg->make_code);
if (info & IS_BREAK)
scan_code |= XTKBD_RELEASE;
serio_interrupt(kbd_dev->hv_serio, scan_code, 0);
}
spin_unlock_irqrestore(&kbd_dev->lock, flags);
/*
* Only trigger a wakeup on key down, otherwise
* "echo freeze > /sys/power/state" can't really enter the
* state because the Enter-UP can trigger a wakeup at once.
*/
if (!(info & IS_BREAK))
pm_wakeup_event(&hv_dev->device, 0);
break;
default:
dev_err(&hv_dev->device,
"unhandled message type %d\n", msg_type);
}
}
static void hv_kbd_handle_received_packet(struct hv_device *hv_dev,
struct vmpacket_descriptor *desc,
u32 bytes_recvd,
u64 req_id)
{
struct synth_kbd_msg *msg;
u32 msg_sz;
switch (desc->type) {
case VM_PKT_COMP:
break;
case VM_PKT_DATA_INBAND:
/*
* We have a packet that has "inband" data. The API used
* for retrieving the packet guarantees that the complete
* packet is read. So, minimally, we should be able to
* parse the payload header safely (assuming that the host
* can be trusted. Trusting the host seems to be a
* reasonable assumption because in a virtualized
* environment there is not whole lot you can do if you
* don't trust the host.
*
* Nonetheless, let us validate if the host can be trusted
* (in a trivial way). The interesting aspect of this
* validation is how do you recover if we discover that the
* host is not to be trusted? Simply dropping the packet, I
* don't think is an appropriate recovery. In the interest
* of failing fast, it may be better to crash the guest.
* For now, I will just drop the packet!
*/
msg_sz = bytes_recvd - (desc->offset8 << 3);
if (msg_sz <= sizeof(struct synth_kbd_msg_hdr)) {
/*
* Drop the packet and hope
* the problem magically goes away.
*/
dev_err(&hv_dev->device,
"Illegal packet (type: %d, tid: %llx, size: %d)\n",
desc->type, req_id, msg_sz);
break;
}
msg = (void *)desc + (desc->offset8 << 3);
hv_kbd_on_receive(hv_dev, msg, msg_sz);
break;
default:
dev_err(&hv_dev->device,
"unhandled packet type %d, tid %llx len %d\n",
desc->type, req_id, bytes_recvd);
break;
}
}
static void hv_kbd_on_channel_callback(void *context)
{
struct hv_device *hv_dev = context;
void *buffer;
int bufferlen = 0x100; /* Start with sensible size */
u32 bytes_recvd;
u64 req_id;
int error;
buffer = kmalloc(bufferlen, GFP_ATOMIC);
if (!buffer)
return;
while (1) {
error = vmbus_recvpacket_raw(hv_dev->channel, buffer, bufferlen,
&bytes_recvd, &req_id);
switch (error) {
case 0:
if (bytes_recvd == 0) {
kfree(buffer);
return;
}
hv_kbd_handle_received_packet(hv_dev, buffer,
bytes_recvd, req_id);
break;
case -ENOBUFS:
kfree(buffer);
/* Handle large packet */
bufferlen = bytes_recvd;
buffer = kmalloc(bytes_recvd, GFP_ATOMIC);
if (!buffer)
return;
break;
}
}
}
static int hv_kbd_connect_to_vsp(struct hv_device *hv_dev)
{
struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev);
struct synth_kbd_protocol_request *request;
struct synth_kbd_protocol_response *response;
u32 proto_status;
int error;
request = &kbd_dev->protocol_req;
memset(request, 0, sizeof(struct synth_kbd_protocol_request));
request->header.type = __cpu_to_le32(SYNTH_KBD_PROTOCOL_REQUEST);
request->version_requested.version = __cpu_to_le32(SYNTH_KBD_VERSION);
error = vmbus_sendpacket(hv_dev->channel, request,
sizeof(struct synth_kbd_protocol_request),
(unsigned long)request,
VM_PKT_DATA_INBAND,
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
if (error)
return error;
if (!wait_for_completion_timeout(&kbd_dev->wait_event, 10 * HZ))
return -ETIMEDOUT;
response = &kbd_dev->protocol_resp;
proto_status = __le32_to_cpu(response->proto_status);
if (!(proto_status & PROTOCOL_ACCEPTED)) {
dev_err(&hv_dev->device,
"synth_kbd protocol request failed (version %d)\n",
SYNTH_KBD_VERSION);
return -ENODEV;
}
return 0;
}
static int hv_kbd_start(struct serio *serio)
{
struct hv_kbd_dev *kbd_dev = serio->port_data;
unsigned long flags;
spin_lock_irqsave(&kbd_dev->lock, flags);
kbd_dev->started = true;
spin_unlock_irqrestore(&kbd_dev->lock, flags);
return 0;
}
static void hv_kbd_stop(struct serio *serio)
{
struct hv_kbd_dev *kbd_dev = serio->port_data;
unsigned long flags;
spin_lock_irqsave(&kbd_dev->lock, flags);
kbd_dev->started = false;
spin_unlock_irqrestore(&kbd_dev->lock, flags);
}
static int hv_kbd_probe(struct hv_device *hv_dev,
const struct hv_vmbus_device_id *dev_id)
{
struct hv_kbd_dev *kbd_dev;
struct serio *hv_serio;
int error;
kbd_dev = kzalloc(sizeof(struct hv_kbd_dev), GFP_KERNEL);
hv_serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (!kbd_dev || !hv_serio) {
error = -ENOMEM;
goto err_free_mem;
}
kbd_dev->hv_dev = hv_dev;
kbd_dev->hv_serio = hv_serio;
spin_lock_init(&kbd_dev->lock);
init_completion(&kbd_dev->wait_event);
hv_set_drvdata(hv_dev, kbd_dev);
hv_serio->dev.parent = &hv_dev->device;
hv_serio->id.type = SERIO_8042_XL;
hv_serio->port_data = kbd_dev;
strlcpy(hv_serio->name, dev_name(&hv_dev->device),
sizeof(hv_serio->name));
strlcpy(hv_serio->phys, dev_name(&hv_dev->device),
sizeof(hv_serio->phys));
hv_serio->start = hv_kbd_start;
hv_serio->stop = hv_kbd_stop;
error = vmbus_open(hv_dev->channel,
KBD_VSC_SEND_RING_BUFFER_SIZE,
KBD_VSC_RECV_RING_BUFFER_SIZE,
NULL, 0,
hv_kbd_on_channel_callback,
hv_dev);
if (error)
goto err_free_mem;
error = hv_kbd_connect_to_vsp(hv_dev);
if (error)
goto err_close_vmbus;
serio_register_port(kbd_dev->hv_serio);
device_init_wakeup(&hv_dev->device, true);
return 0;
err_close_vmbus:
vmbus_close(hv_dev->channel);
err_free_mem:
kfree(hv_serio);
kfree(kbd_dev);
return error;
}
static int hv_kbd_remove(struct hv_device *hv_dev)
{
struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev);
device_init_wakeup(&hv_dev->device, false);
serio_unregister_port(kbd_dev->hv_serio);
vmbus_close(hv_dev->channel);
kfree(kbd_dev);
hv_set_drvdata(hv_dev, NULL);
return 0;
}
/*
* Keyboard GUID
* {f912ad6d-2b17-48ea-bd65-f927a61c7684}
*/
#define HV_KBD_GUID \
.guid = { \
0x6d, 0xad, 0x12, 0xf9, 0x17, 0x2b, 0xea, 0x48, \
0xbd, 0x65, 0xf9, 0x27, 0xa6, 0x1c, 0x76, 0x84 \
}
static const struct hv_vmbus_device_id id_table[] = {
/* Keyboard guid */
{ HV_KBD_GUID, },
{ },
};
MODULE_DEVICE_TABLE(vmbus, id_table);
static struct hv_driver hv_kbd_drv = {
.name = KBUILD_MODNAME,
.id_table = id_table,
.probe = hv_kbd_probe,
.remove = hv_kbd_remove,
};
static int __init hv_kbd_init(void)
{
return vmbus_driver_register(&hv_kbd_drv);
}
static void __exit hv_kbd_exit(void)
{
vmbus_driver_unregister(&hv_kbd_drv);
}
MODULE_LICENSE("GPL");
module_init(hv_kbd_init);
module_exit(hv_kbd_exit);

View file

@ -0,0 +1,95 @@
#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>
#elif defined(CONFIG_PPC)
extern int of_i8042_kbd_irq;
extern int of_i8042_aux_irq;
# define I8042_KBD_IRQ of_i8042_kbd_irq
# define I8042_AUX_IRQ of_i8042_aux_irq
#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__)
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,61 @@
#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)
{
}
#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,158 @@
#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;
#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
static struct resource *kbd_res;
#define OBP_PS2KBD_NAME1 "kb_ps2"
#define OBP_PS2KBD_NAME2 "keyboard"
#define OBP_PS2MS_NAME1 "kdmouse"
#define OBP_PS2MS_NAME2 "mouse"
static int sparc_i8042_probe(struct platform_device *op)
{
struct device_node *dp = op->dev.of_node;
dp = dp->child;
while (dp) {
if (!strcmp(dp->name, OBP_PS2KBD_NAME1) ||
!strcmp(dp->name, OBP_PS2KBD_NAME2)) {
struct platform_device *kbd = of_find_device_by_node(dp);
unsigned int irq = kbd->archdata.irqs[0];
if (irq == 0xffffffff)
irq = op->archdata.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 platform_device *ms = of_find_device_by_node(dp);
unsigned int irq = ms->archdata.irqs[0];
if (irq == 0xffffffff)
irq = op->archdata.irqs[0];
i8042_aux_irq = irq;
}
dp = dp->sibling;
}
return 0;
}
static int sparc_i8042_remove(struct platform_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 platform_driver sparc_i8042_driver = {
.driver = {
.name = "i8042",
.owner = THIS_MODULE,
.of_match_table = sparc_i8042_match,
},
.probe = sparc_i8042_probe,
.remove = 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 = platform_driver_register(&sparc_i8042_driver);
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"))
platform_driver_unregister(&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,73 @@
/*
* Code specific to PKUnity SoC and UniCore ISA
*
* Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
* Copyright (C) 2001-2011 Guan Xuetao
*
* 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.
*/
#ifndef _I8042_UNICORE32_H
#define _I8042_UNICORE32_H
#include <mach/hardware.h>
/*
* Names.
*/
#define I8042_KBD_PHYS_DESC "isa0060/serio0"
#define I8042_AUX_PHYS_DESC "isa0060/serio1"
#define I8042_MUX_PHYS_DESC "isa0060/serio%d"
/*
* IRQs.
*/
#define I8042_KBD_IRQ IRQ_PS2_KBD
#define I8042_AUX_IRQ IRQ_PS2_AUX
/*
* Register numbers.
*/
#define I8042_COMMAND_REG PS2_COMMAND
#define I8042_STATUS_REG PS2_STATUS
#define I8042_DATA_REG PS2_DATA
#define I8042_REGION_START (resource_size_t)(PS2_DATA)
#define I8042_REGION_SIZE (resource_size_t)(16)
static inline int i8042_read_data(void)
{
return readb(I8042_DATA_REG);
}
static inline int i8042_read_status(void)
{
return readb(I8042_STATUS_REG);
}
static inline void i8042_write_data(int val)
{
writeb(val, I8042_DATA_REG);
}
static inline void i8042_write_command(int val)
{
writeb(val, I8042_COMMAND_REG);
}
static inline int i8042_platform_init(void)
{
if (!request_mem_region(I8042_REGION_START, I8042_REGION_SIZE, "i8042"))
return -EBUSY;
i8042_reset = 1;
return 0;
}
static inline void i8042_platform_exit(void)
{
release_mem_region(I8042_REGION_START, I8042_REGION_SIZE);
}
#endif /* _I8042_UNICORE32_H */

File diff suppressed because it is too large Load diff

1529
drivers/input/serio/i8042.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,85 @@
#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"
#elif defined(CONFIG_UNICORE32)
#include "i8042-unicore32io.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
/*
* 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 KBUILD_MODNAME ": [%d] " format, \
(int) (jiffies - i8042_start_time), ##arg); \
} while (0)
#else
#define dbg_init() do { } while (0)
#define dbg(format, arg...) \
do { \
if (0) \
printk(KERN_DEBUG pr_fmt(format), ##arg); \
} while (0)
#endif
#endif /* _I8042_H */

View file

@ -0,0 +1,374 @@
/*
* 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/interrupt.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/i8042.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 arrives.
*/
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,210 @@
/*
* 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/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 *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 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 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 = 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,283 @@
/*
* OLPC serio driver for multiplexed input from Marvell MMP security processor
*
* Copyright (C) 2011-2013 One Laptop Per Child
*
* 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.
*/
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/serio.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/delay.h>
/*
* The OLPC XO-1.75 and XO-4 laptops do not have a hardware PS/2 controller.
* Instead, the OLPC firmware runs a bit-banging PS/2 implementation on an
* otherwise-unused slow processor which is included in the Marvell MMP2/MMP3
* SoC, known as the "Security Processor" (SP) or "Wireless Trusted Module"
* (WTM). This firmware then reports its results via the WTM registers,
* which we read from the Application Processor (AP, i.e. main CPU) in this
* driver.
*
* On the hardware side we have a PS/2 mouse and an AT keyboard, the data
* is multiplexed through this system. We create a serio port for each one,
* and demultiplex the data accordingly.
*/
/* WTM register offsets */
#define SECURE_PROCESSOR_COMMAND 0x40
#define COMMAND_RETURN_STATUS 0x80
#define COMMAND_FIFO_STATUS 0xc4
#define PJ_RST_INTERRUPT 0xc8
#define PJ_INTERRUPT_MASK 0xcc
/*
* The upper byte of SECURE_PROCESSOR_COMMAND and COMMAND_RETURN_STATUS is
* used to identify which port (device) is being talked to. The lower byte
* is the data being sent/received.
*/
#define PORT_MASK 0xff00
#define DATA_MASK 0x00ff
#define PORT_SHIFT 8
#define KEYBOARD_PORT 0
#define TOUCHPAD_PORT 1
/* COMMAND_FIFO_STATUS */
#define CMD_CNTR_MASK 0x7 /* Number of pending/unprocessed commands */
#define MAX_PENDING_CMDS 4 /* from device specs */
/* PJ_RST_INTERRUPT */
#define SP_COMMAND_COMPLETE_RESET 0x1
/* PJ_INTERRUPT_MASK */
#define INT_0 (1 << 0)
/* COMMAND_FIFO_STATUS */
#define CMD_STS_MASK 0x100
struct olpc_apsp {
struct device *dev;
struct serio *kbio;
struct serio *padio;
void __iomem *base;
int open_count;
int irq;
};
static int olpc_apsp_write(struct serio *port, unsigned char val)
{
struct olpc_apsp *priv = port->port_data;
unsigned int i;
u32 which = 0;
if (port == priv->padio)
which = TOUCHPAD_PORT << PORT_SHIFT;
else
which = KEYBOARD_PORT << PORT_SHIFT;
dev_dbg(priv->dev, "olpc_apsp_write which=%x val=%x\n", which, val);
for (i = 0; i < 50; i++) {
u32 sts = readl(priv->base + COMMAND_FIFO_STATUS);
if ((sts & CMD_CNTR_MASK) < MAX_PENDING_CMDS) {
writel(which | val,
priv->base + SECURE_PROCESSOR_COMMAND);
return 0;
}
/* SP busy. This has not been seen in practice. */
mdelay(1);
}
dev_dbg(priv->dev, "olpc_apsp_write timeout, status=%x\n",
readl(priv->base + COMMAND_FIFO_STATUS));
return -ETIMEDOUT;
}
static irqreturn_t olpc_apsp_rx(int irq, void *dev_id)
{
struct olpc_apsp *priv = dev_id;
unsigned int w, tmp;
struct serio *serio;
/*
* Write 1 to PJ_RST_INTERRUPT to acknowledge and clear the interrupt
* Write 0xff00 to SECURE_PROCESSOR_COMMAND.
*/
tmp = readl(priv->base + PJ_RST_INTERRUPT);
if (!(tmp & SP_COMMAND_COMPLETE_RESET)) {
dev_warn(priv->dev, "spurious interrupt?\n");
return IRQ_NONE;
}
w = readl(priv->base + COMMAND_RETURN_STATUS);
dev_dbg(priv->dev, "olpc_apsp_rx %x\n", w);
if (w >> PORT_SHIFT == KEYBOARD_PORT)
serio = priv->kbio;
else
serio = priv->padio;
serio_interrupt(serio, w & DATA_MASK, 0);
/* Ack and clear interrupt */
writel(tmp | SP_COMMAND_COMPLETE_RESET, priv->base + PJ_RST_INTERRUPT);
writel(PORT_MASK, priv->base + SECURE_PROCESSOR_COMMAND);
pm_wakeup_event(priv->dev, 1000);
return IRQ_HANDLED;
}
static int olpc_apsp_open(struct serio *port)
{
struct olpc_apsp *priv = port->port_data;
unsigned int tmp;
if (priv->open_count++ == 0) {
/* Enable interrupt 0 by clearing its bit */
tmp = readl(priv->base + PJ_INTERRUPT_MASK);
writel(tmp & ~INT_0, priv->base + PJ_INTERRUPT_MASK);
}
return 0;
}
static void olpc_apsp_close(struct serio *port)
{
struct olpc_apsp *priv = port->port_data;
unsigned int tmp;
if (--priv->open_count == 0) {
/* Disable interrupt 0 */
tmp = readl(priv->base + PJ_INTERRUPT_MASK);
writel(tmp | INT_0, priv->base + PJ_INTERRUPT_MASK);
}
}
static int olpc_apsp_probe(struct platform_device *pdev)
{
struct serio *kb_serio, *pad_serio;
struct olpc_apsp *priv;
struct resource *res;
struct device_node *np;
unsigned long l;
int error;
priv = devm_kzalloc(&pdev->dev, sizeof(struct olpc_apsp), GFP_KERNEL);
if (!priv)
return -ENOMEM;
np = pdev->dev.of_node;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(priv->base)) {
dev_err(&pdev->dev, "Failed to map WTM registers\n");
return PTR_ERR(priv->base);
}
priv->irq = platform_get_irq(pdev, 0);
if (priv->irq < 0)
return priv->irq;
l = readl(priv->base + COMMAND_FIFO_STATUS);
if (!(l & CMD_STS_MASK)) {
dev_err(&pdev->dev, "SP cannot accept commands.\n");
return -EIO;
}
/* KEYBOARD */
kb_serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (!kb_serio)
return -ENOMEM;
kb_serio->id.type = SERIO_8042_XL;
kb_serio->write = olpc_apsp_write;
kb_serio->open = olpc_apsp_open;
kb_serio->close = olpc_apsp_close;
kb_serio->port_data = priv;
kb_serio->dev.parent = &pdev->dev;
strlcpy(kb_serio->name, "sp keyboard", sizeof(kb_serio->name));
strlcpy(kb_serio->phys, "sp/serio0", sizeof(kb_serio->phys));
priv->kbio = kb_serio;
serio_register_port(kb_serio);
/* TOUCHPAD */
pad_serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (!pad_serio) {
error = -ENOMEM;
goto err_pad;
}
pad_serio->id.type = SERIO_8042;
pad_serio->write = olpc_apsp_write;
pad_serio->open = olpc_apsp_open;
pad_serio->close = olpc_apsp_close;
pad_serio->port_data = priv;
pad_serio->dev.parent = &pdev->dev;
strlcpy(pad_serio->name, "sp touchpad", sizeof(pad_serio->name));
strlcpy(pad_serio->phys, "sp/serio1", sizeof(pad_serio->phys));
priv->padio = pad_serio;
serio_register_port(pad_serio);
error = request_irq(priv->irq, olpc_apsp_rx, 0, "olpc-apsp", priv);
if (error) {
dev_err(&pdev->dev, "Failed to request IRQ\n");
goto err_irq;
}
priv->dev = &pdev->dev;
device_init_wakeup(priv->dev, 1);
platform_set_drvdata(pdev, priv);
dev_dbg(&pdev->dev, "probed successfully.\n");
return 0;
err_irq:
serio_unregister_port(pad_serio);
err_pad:
serio_unregister_port(kb_serio);
return error;
}
static int olpc_apsp_remove(struct platform_device *pdev)
{
struct olpc_apsp *priv = platform_get_drvdata(pdev);
free_irq(priv->irq, priv);
serio_unregister_port(priv->kbio);
serio_unregister_port(priv->padio);
return 0;
}
static const struct of_device_id olpc_apsp_dt_ids[] = {
{ .compatible = "olpc,ap-sp", },
{}
};
MODULE_DEVICE_TABLE(of, olpc_apsp_dt_ids);
static struct platform_driver olpc_apsp_driver = {
.probe = olpc_apsp_probe,
.remove = olpc_apsp_remove,
.driver = {
.name = "olpc-apsp",
.owner = THIS_MODULE,
.of_match_table = olpc_apsp_dt_ids,
},
};
MODULE_DESCRIPTION("OLPC AP-SP serio driver");
MODULE_LICENSE("GPL");
module_platform_driver(olpc_apsp_driver);

View file

@ -0,0 +1,218 @@
/*
* 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/slab.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,220 @@
/*
* 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/slab.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 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 pcips2_remove(struct pci_dev *dev)
{
struct pcips2_data *ps2if = pci_get_drvdata(dev);
serio_unregister_port(ps2if->io);
kfree(ps2if);
pci_release_regions(dev);
pci_disable_device(dev);
}
static const 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, }
};
MODULE_DEVICE_TABLE(pci, pcips2_ids);
static struct pci_driver pcips2_driver = {
.name = "pcips2",
.id_table = pcips2_ids,
.probe = pcips2_probe,
.remove = pcips2_remove,
};
module_pci_driver(pcips2_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
MODULE_DESCRIPTION("PCI PS/2 keyboard/mouse driver");

View file

@ -0,0 +1,307 @@
/*
* TQC PS/2 Multiplexer driver
*
* Copyright (C) 2010 Dmitry Eremin-Solenikov
*
* 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/slab.h>
#include <linux/module.h>
#include <linux/serio.h>
MODULE_AUTHOR("Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>");
MODULE_DESCRIPTION("TQC PS/2 Multiplexer driver");
MODULE_LICENSE("GPL");
#define PS2MULT_KB_SELECTOR 0xA0
#define PS2MULT_MS_SELECTOR 0xA1
#define PS2MULT_ESCAPE 0x7D
#define PS2MULT_BSYNC 0x7E
#define PS2MULT_SESSION_START 0x55
#define PS2MULT_SESSION_END 0x56
struct ps2mult_port {
struct serio *serio;
unsigned char sel;
bool registered;
};
#define PS2MULT_NUM_PORTS 2
#define PS2MULT_KBD_PORT 0
#define PS2MULT_MOUSE_PORT 1
struct ps2mult {
struct serio *mx_serio;
struct ps2mult_port ports[PS2MULT_NUM_PORTS];
spinlock_t lock;
struct ps2mult_port *in_port;
struct ps2mult_port *out_port;
bool escape;
};
/* First MUST come PS2MULT_NUM_PORTS selectors */
static const unsigned char ps2mult_controls[] = {
PS2MULT_KB_SELECTOR, PS2MULT_MS_SELECTOR,
PS2MULT_ESCAPE, PS2MULT_BSYNC,
PS2MULT_SESSION_START, PS2MULT_SESSION_END,
};
static const struct serio_device_id ps2mult_serio_ids[] = {
{
.type = SERIO_RS232,
.proto = SERIO_PS2MULT,
.id = SERIO_ANY,
.extra = SERIO_ANY,
},
{ 0 }
};
MODULE_DEVICE_TABLE(serio, ps2mult_serio_ids);
static void ps2mult_select_port(struct ps2mult *psm, struct ps2mult_port *port)
{
struct serio *mx_serio = psm->mx_serio;
serio_write(mx_serio, port->sel);
psm->out_port = port;
dev_dbg(&mx_serio->dev, "switched to sel %02x\n", port->sel);
}
static int ps2mult_serio_write(struct serio *serio, unsigned char data)
{
struct serio *mx_port = serio->parent;
struct ps2mult *psm = serio_get_drvdata(mx_port);
struct ps2mult_port *port = serio->port_data;
bool need_escape;
unsigned long flags;
spin_lock_irqsave(&psm->lock, flags);
if (psm->out_port != port)
ps2mult_select_port(psm, port);
need_escape = memchr(ps2mult_controls, data, sizeof(ps2mult_controls));
dev_dbg(&serio->dev,
"write: %s%02x\n", need_escape ? "ESC " : "", data);
if (need_escape)
serio_write(mx_port, PS2MULT_ESCAPE);
serio_write(mx_port, data);
spin_unlock_irqrestore(&psm->lock, flags);
return 0;
}
static int ps2mult_serio_start(struct serio *serio)
{
struct ps2mult *psm = serio_get_drvdata(serio->parent);
struct ps2mult_port *port = serio->port_data;
unsigned long flags;
spin_lock_irqsave(&psm->lock, flags);
port->registered = true;
spin_unlock_irqrestore(&psm->lock, flags);
return 0;
}
static void ps2mult_serio_stop(struct serio *serio)
{
struct ps2mult *psm = serio_get_drvdata(serio->parent);
struct ps2mult_port *port = serio->port_data;
unsigned long flags;
spin_lock_irqsave(&psm->lock, flags);
port->registered = false;
spin_unlock_irqrestore(&psm->lock, flags);
}
static int ps2mult_create_port(struct ps2mult *psm, int i)
{
struct serio *mx_serio = psm->mx_serio;
struct serio *serio;
serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (!serio)
return -ENOMEM;
strlcpy(serio->name, "TQC PS/2 Multiplexer", sizeof(serio->name));
snprintf(serio->phys, sizeof(serio->phys),
"%s/port%d", mx_serio->phys, i);
serio->id.type = SERIO_8042;
serio->write = ps2mult_serio_write;
serio->start = ps2mult_serio_start;
serio->stop = ps2mult_serio_stop;
serio->parent = psm->mx_serio;
serio->port_data = &psm->ports[i];
psm->ports[i].serio = serio;
return 0;
}
static void ps2mult_reset(struct ps2mult *psm)
{
unsigned long flags;
spin_lock_irqsave(&psm->lock, flags);
serio_write(psm->mx_serio, PS2MULT_SESSION_END);
serio_write(psm->mx_serio, PS2MULT_SESSION_START);
ps2mult_select_port(psm, &psm->ports[PS2MULT_KBD_PORT]);
spin_unlock_irqrestore(&psm->lock, flags);
}
static int ps2mult_connect(struct serio *serio, struct serio_driver *drv)
{
struct ps2mult *psm;
int i;
int error;
if (!serio->write)
return -EINVAL;
psm = kzalloc(sizeof(*psm), GFP_KERNEL);
if (!psm)
return -ENOMEM;
spin_lock_init(&psm->lock);
psm->mx_serio = serio;
for (i = 0; i < PS2MULT_NUM_PORTS; i++) {
psm->ports[i].sel = ps2mult_controls[i];
error = ps2mult_create_port(psm, i);
if (error)
goto err_out;
}
psm->in_port = psm->out_port = &psm->ports[PS2MULT_KBD_PORT];
serio_set_drvdata(serio, psm);
error = serio_open(serio, drv);
if (error)
goto err_out;
ps2mult_reset(psm);
for (i = 0; i < PS2MULT_NUM_PORTS; i++) {
struct serio *s = psm->ports[i].serio;
dev_info(&serio->dev, "%s port at %s\n", s->name, serio->phys);
serio_register_port(s);
}
return 0;
err_out:
while (--i >= 0)
kfree(psm->ports[i].serio);
kfree(psm);
return error;
}
static void ps2mult_disconnect(struct serio *serio)
{
struct ps2mult *psm = serio_get_drvdata(serio);
/* Note that serio core already take care of children ports */
serio_write(serio, PS2MULT_SESSION_END);
serio_close(serio);
kfree(psm);
serio_set_drvdata(serio, NULL);
}
static int ps2mult_reconnect(struct serio *serio)
{
struct ps2mult *psm = serio_get_drvdata(serio);
ps2mult_reset(psm);
return 0;
}
static irqreturn_t ps2mult_interrupt(struct serio *serio,
unsigned char data, unsigned int dfl)
{
struct ps2mult *psm = serio_get_drvdata(serio);
struct ps2mult_port *in_port;
unsigned long flags;
dev_dbg(&serio->dev, "Received %02x flags %02x\n", data, dfl);
spin_lock_irqsave(&psm->lock, flags);
if (psm->escape) {
psm->escape = false;
in_port = psm->in_port;
if (in_port->registered)
serio_interrupt(in_port->serio, data, dfl);
goto out;
}
switch (data) {
case PS2MULT_ESCAPE:
dev_dbg(&serio->dev, "ESCAPE\n");
psm->escape = true;
break;
case PS2MULT_BSYNC:
dev_dbg(&serio->dev, "BSYNC\n");
psm->in_port = psm->out_port;
break;
case PS2MULT_SESSION_START:
dev_dbg(&serio->dev, "SS\n");
break;
case PS2MULT_SESSION_END:
dev_dbg(&serio->dev, "SE\n");
break;
case PS2MULT_KB_SELECTOR:
dev_dbg(&serio->dev, "KB\n");
psm->in_port = &psm->ports[PS2MULT_KBD_PORT];
break;
case PS2MULT_MS_SELECTOR:
dev_dbg(&serio->dev, "MS\n");
psm->in_port = &psm->ports[PS2MULT_MOUSE_PORT];
break;
default:
in_port = psm->in_port;
if (in_port->registered)
serio_interrupt(in_port->serio, data, dfl);
break;
}
out:
spin_unlock_irqrestore(&psm->lock, flags);
return IRQ_HANDLED;
}
static struct serio_driver ps2mult_drv = {
.driver = {
.name = "ps2mult",
},
.description = "TQC PS/2 Multiplexer driver",
.id_table = ps2mult_serio_ids,
.interrupt = ps2mult_interrupt,
.connect = ps2mult_connect,
.disconnect = ps2mult_disconnect,
.reconnect = ps2mult_reconnect,
};
module_serio_driver(ps2mult_drv);

View file

@ -0,0 +1,194 @@
/*
* 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/serio.h>
#include <linux/interrupt.h>
#include <linux/err.h>
#include <linux/bitops.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/q40_master.h>
#include <asm/irq.h>
#include <asm/q40ints.h>
#define DRV_NAME "q40kbd"
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Q40 PS/2 keyboard controller driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);
struct q40kbd {
struct serio *port;
spinlock_t lock;
};
static irqreturn_t q40kbd_interrupt(int irq, void *dev_id)
{
struct q40kbd *q40kbd = 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(struct q40kbd *q40kbd)
{
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);
}
static void q40kbd_stop(void)
{
master_outb(0, KEY_IRQ_ENABLE_REG);
master_outb(-1, KEYBOARD_UNLOCK_REG);
}
/*
* 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)
{
struct q40kbd *q40kbd = port->port_data;
q40kbd_flush(q40kbd);
/* 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)
{
struct q40kbd *q40kbd = port->port_data;
q40kbd_stop();
q40kbd_flush(q40kbd);
}
static int q40kbd_probe(struct platform_device *pdev)
{
struct q40kbd *q40kbd;
struct serio *port;
int error;
q40kbd = kzalloc(sizeof(struct q40kbd), GFP_KERNEL);
port = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (!q40kbd || !port) {
error = -ENOMEM;
goto err_free_mem;
}
q40kbd->port = port;
spin_lock_init(&q40kbd->lock);
port->id.type = SERIO_8042;
port->open = q40kbd_open;
port->close = q40kbd_close;
port->port_data = q40kbd;
port->dev.parent = &pdev->dev;
strlcpy(port->name, "Q40 Kbd Port", sizeof(port->name));
strlcpy(port->phys, "Q40", sizeof(port->phys));
q40kbd_stop();
error = request_irq(Q40_IRQ_KEYBOARD, q40kbd_interrupt, 0,
DRV_NAME, q40kbd);
if (error) {
dev_err(&pdev->dev, "Can't get irq %d.\n", Q40_IRQ_KEYBOARD);
goto err_free_mem;
}
serio_register_port(q40kbd->port);
platform_set_drvdata(pdev, q40kbd);
printk(KERN_INFO "serio: Q40 kbd registered\n");
return 0;
err_free_mem:
kfree(port);
kfree(q40kbd);
return error;
}
static int q40kbd_remove(struct platform_device *pdev)
{
struct q40kbd *q40kbd = platform_get_drvdata(pdev);
/*
* q40kbd_close() will be called as part of unregistering
* and will ensure that IRQ is turned off, so it is safe
* to unregister port first and free IRQ later.
*/
serio_unregister_port(q40kbd->port);
free_irq(Q40_IRQ_KEYBOARD, q40kbd);
kfree(q40kbd);
return 0;
}
static struct platform_driver q40kbd_driver = {
.driver = {
.name = "q40kbd",
.owner = THIS_MODULE,
},
.remove = q40kbd_remove,
};
module_platform_driver_probe(q40kbd_driver, q40kbd_probe);

View file

@ -0,0 +1,174 @@
/*
* 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/serio.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <mach/hardware.h>
#include <asm/hardware/iomd.h>
MODULE_AUTHOR("Vojtech Pavlik, Russell King");
MODULE_DESCRIPTION("Acorn RiscPC PS/2 keyboard controller driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:kart");
struct rpckbd_data {
int tx_irq;
int rx_irq;
};
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)
{
struct rpckbd_data *rpckbd = port->port_data;
/* Reset the keyboard state machine. */
iomd_writeb(0, IOMD_KCTRL);
iomd_writeb(8, IOMD_KCTRL);
iomd_readb(IOMD_KARTRX);
if (request_irq(rpckbd->rx_irq, rpckbd_rx, 0, "rpckbd", port) != 0) {
printk(KERN_ERR "rpckbd.c: Could not allocate keyboard receive IRQ\n");
return -EBUSY;
}
if (request_irq(rpckbd->tx_irq, rpckbd_tx, 0, "rpckbd", port) != 0) {
printk(KERN_ERR "rpckbd.c: Could not allocate keyboard transmit IRQ\n");
free_irq(rpckbd->rx_irq, port);
return -EBUSY;
}
return 0;
}
static void rpckbd_close(struct serio *port)
{
struct rpckbd_data *rpckbd = port->port_data;
free_irq(rpckbd->rx_irq, port);
free_irq(rpckbd->tx_irq, port);
}
/*
* Allocate and initialize serio structure for subsequent registration
* with serio core.
*/
static int rpckbd_probe(struct platform_device *dev)
{
struct rpckbd_data *rpckbd;
struct serio *serio;
int tx_irq, rx_irq;
rx_irq = platform_get_irq(dev, 0);
if (rx_irq <= 0)
return rx_irq < 0 ? rx_irq : -ENXIO;
tx_irq = platform_get_irq(dev, 1);
if (tx_irq <= 0)
return tx_irq < 0 ? tx_irq : -ENXIO;
serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
rpckbd = kzalloc(sizeof(*rpckbd), GFP_KERNEL);
if (!serio || !rpckbd) {
kfree(rpckbd);
kfree(serio);
return -ENOMEM;
}
rpckbd->rx_irq = rx_irq;
rpckbd->tx_irq = tx_irq;
serio->id.type = SERIO_8042;
serio->write = rpckbd_write;
serio->open = rpckbd_open;
serio->close = rpckbd_close;
serio->dev.parent = &dev->dev;
serio->port_data = rpckbd;
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 rpckbd_remove(struct platform_device *dev)
{
struct serio *serio = platform_get_drvdata(dev);
struct rpckbd_data *rpckbd = serio->port_data;
serio_unregister_port(serio);
kfree(rpckbd);
return 0;
}
static struct platform_driver rpckbd_driver = {
.probe = rpckbd_probe,
.remove = rpckbd_remove,
.driver = {
.name = "kart",
.owner = THIS_MODULE,
},
};
module_platform_driver(rpckbd_driver);

View file

@ -0,0 +1,378 @@
/*
* 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/hardware/sa1111.h>
#define PS2CR 0x0000
#define PS2STAT 0x0004
#define PS2DATA 0x0008
#define PS2CLKDIV 0x000c
#define PS2PRECNT 0x0010
#define PS2CR_ENA 0x08
#define PS2CR_FKD 0x02
#define PS2CR_FKC 0x01
#define PS2STAT_STP 0x0100
#define PS2STAT_TXE 0x0080
#define PS2STAT_TXB 0x0040
#define PS2STAT_RXF 0x0020
#define PS2STAT_RXB 0x0010
#define PS2STAT_ENA 0x0008
#define PS2STAT_RXP 0x0004
#define PS2STAT_KBD 0x0002
#define PS2STAT_KBC 0x0001
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 + PS2STAT);
while (status & PS2STAT_RXF) {
if (status & PS2STAT_STP)
sa1111_writel(PS2STAT_STP, ps2if->base + PS2STAT);
flag = (status & PS2STAT_STP ? SERIO_FRAME : 0) |
(status & PS2STAT_RXP ? 0 : SERIO_PARITY);
scancode = sa1111_readl(ps2if->base + PS2DATA) & 0xff;
if (hweight8(scancode) & 1)
flag ^= SERIO_PARITY;
serio_interrupt(ps2if->io, scancode, flag);
status = sa1111_readl(ps2if->base + 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 + PS2STAT);
if (ps2if->head == ps2if->tail) {
disable_irq_nosync(irq);
/* done */
} else if (status & PS2STAT_TXE) {
sa1111_writel(ps2if->buf[ps2if->tail], ps2if->base + 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 + PS2STAT) & PS2STAT_TXE) {
sa1111_writel(val, ps2if->base + 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;
ret = sa1111_enable_device(ps2if->dev);
if (ret)
return ret;
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);
sa1111_disable_device(ps2if->dev);
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);
sa1111_disable_device(ps2if->dev);
return ret;
}
ps2if->open = 1;
enable_irq_wake(ps2if->dev->irq[0]);
sa1111_writel(PS2CR_ENA, ps2if->base + PS2CR);
return 0;
}
static void ps2_close(struct serio *io)
{
struct ps2if *ps2if = io->port_data;
sa1111_writel(0, ps2if->base + 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 ps2_clear_input(struct ps2if *ps2if)
{
int maxread = 100;
while (maxread--) {
if ((sa1111_readl(ps2if->base + PS2DATA) & 0xff) == 0xff)
break;
}
}
static unsigned int ps2_test_one(struct ps2if *ps2if,
unsigned int mask)
{
unsigned int val;
sa1111_writel(PS2CR_ENA | mask, ps2if->base + PS2CR);
udelay(2);
val = sa1111_readl(ps2if->base + 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 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 + PS2CR);
return ret;
}
/*
* Add one device to this driver.
*/
static int 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 + PS2CLKDIV);
sa1111_writel(127, ps2if->base + 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, resource_size(&dev->res));
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, resource_size(&dev->res));
sa1111_set_drvdata(dev, NULL);
kfree(ps2if);
return 0;
}
/*
* Our device driver structure
*/
static struct sa1111_driver ps2_driver = {
.drv = {
.name = "sa1111-ps2",
.owner = THIS_MODULE,
},
.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");

1058
drivers/input/serio/serio.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,444 @@
/*
* 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/kref.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/module.h>
#include <linux/serio.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];
struct kref kref;
struct serio *serio;
struct miscdevice dev;
wait_queue_head_t wait;
struct list_head client_list;
struct list_head node;
bool dead;
};
struct serio_raw_client {
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);
/*********************************************************************
* Interface with userspace (file operations) *
*********************************************************************/
static int serio_raw_fasync(int fd, struct file *file, int on)
{
struct serio_raw_client *client = file->private_data;
return fasync_helper(fd, file, on, &client->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_client *client;
int retval;
retval = mutex_lock_interruptible(&serio_raw_mutex);
if (retval)
return retval;
serio_raw = serio_raw_locate(iminor(inode));
if (!serio_raw) {
retval = -ENODEV;
goto out;
}
if (serio_raw->dead) {
retval = -ENODEV;
goto out;
}
client = kzalloc(sizeof(struct serio_raw_client), GFP_KERNEL);
if (!client) {
retval = -ENOMEM;
goto out;
}
client->serio_raw = serio_raw;
file->private_data = client;
kref_get(&serio_raw->kref);
serio_pause_rx(serio_raw->serio);
list_add_tail(&client->node, &serio_raw->client_list);
serio_continue_rx(serio_raw->serio);
out:
mutex_unlock(&serio_raw_mutex);
return retval;
}
static void serio_raw_free(struct kref *kref)
{
struct serio_raw *serio_raw =
container_of(kref, struct serio_raw, kref);
put_device(&serio_raw->serio->dev);
kfree(serio_raw);
}
static int serio_raw_release(struct inode *inode, struct file *file)
{
struct serio_raw_client *client = file->private_data;
struct serio_raw *serio_raw = client->serio_raw;
serio_pause_rx(serio_raw->serio);
list_del(&client->node);
serio_continue_rx(serio_raw->serio);
kfree(client);
kref_put(&serio_raw->kref, serio_raw_free);
return 0;
}
static bool serio_raw_fetch_byte(struct serio_raw *serio_raw, char *c)
{
bool empty;
serio_pause_rx(serio_raw->serio);
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;
}
serio_continue_rx(serio_raw->serio);
return !empty;
}
static ssize_t serio_raw_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
struct serio_raw_client *client = file->private_data;
struct serio_raw *serio_raw = client->serio_raw;
char uninitialized_var(c);
ssize_t read = 0;
int error;
for (;;) {
if (serio_raw->dead)
return -ENODEV;
if (serio_raw->head == serio_raw->tail &&
(file->f_flags & O_NONBLOCK))
return -EAGAIN;
if (count == 0)
break;
while (read < count && serio_raw_fetch_byte(serio_raw, &c)) {
if (put_user(c, buffer++))
return -EFAULT;
read++;
}
if (read)
break;
if (!(file->f_flags & O_NONBLOCK)) {
error = wait_event_interruptible(serio_raw->wait,
serio_raw->head != serio_raw->tail ||
serio_raw->dead);
if (error)
return error;
}
}
return read;
}
static ssize_t serio_raw_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{
struct serio_raw_client *client = file->private_data;
struct serio_raw *serio_raw = client->serio_raw;
int retval = 0;
unsigned char c;
retval = mutex_lock_interruptible(&serio_raw_mutex);
if (retval)
return retval;
if (serio_raw->dead) {
retval = -ENODEV;
goto out;
}
if (count > 32)
count = 32;
while (count--) {
if (get_user(c, buffer++)) {
retval = -EFAULT;
goto out;
}
if (serio_write(serio_raw->serio, c)) {
/* Either signal error or partial write */
if (retval == 0)
retval = -EIO;
goto out;
}
retval++;
}
out:
mutex_unlock(&serio_raw_mutex);
return retval;
}
static unsigned int serio_raw_poll(struct file *file, poll_table *wait)
{
struct serio_raw_client *client = file->private_data;
struct serio_raw *serio_raw = client->serio_raw;
unsigned int mask;
poll_wait(file, &serio_raw->wait, wait);
mask = serio_raw->dead ? POLLHUP | POLLERR : POLLOUT | POLLWRNORM;
if (serio_raw->head != serio_raw->tail)
mask |= POLLIN | POLLRDNORM;
return mask;
}
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,
.llseek = noop_llseek,
};
/*********************************************************************
* 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_client *client;
unsigned int head = serio_raw->head;
/* we are holding serio->lock here so we are protected */
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(client, &serio_raw->client_list, node)
kill_fasync(&client->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)
{
static atomic_t serio_raw_no = ATOMIC_INIT(0);
struct serio_raw *serio_raw;
int err;
serio_raw = kzalloc(sizeof(struct serio_raw), GFP_KERNEL);
if (!serio_raw) {
dev_dbg(&serio->dev, "can't allocate memory for a device\n");
return -ENOMEM;
}
snprintf(serio_raw->name, sizeof(serio_raw->name),
"serio_raw%ld", (long)atomic_inc_return(&serio_raw_no) - 1);
kref_init(&serio_raw->kref);
INIT_LIST_HEAD(&serio_raw->client_list);
init_waitqueue_head(&serio_raw->wait);
serio_raw->serio = serio;
get_device(&serio->dev);
serio_set_drvdata(serio, serio_raw);
err = serio_open(serio, drv);
if (err)
goto err_free;
err = mutex_lock_killable(&serio_raw_mutex);
if (err)
goto err_close;
list_add_tail(&serio_raw->node, &serio_raw_list);
mutex_unlock(&serio_raw_mutex);
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) {
dev_err(&serio->dev,
"failed to register raw access device for %s\n",
serio->phys);
goto err_unlink;
}
dev_info(&serio->dev, "raw access enabled on %s (%s, minor %d)\n",
serio->phys, serio_raw->name, serio_raw->dev.minor);
return 0;
err_unlink:
list_del_init(&serio_raw->node);
err_close:
serio_close(serio);
err_free:
serio_set_drvdata(serio, NULL);
kref_put(&serio_raw->kref, serio_raw_free);
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) {
dev_dbg(&serio->dev,
"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;
}
/*
* Wake up users waiting for IO so they can disconnect from
* dead device.
*/
static void serio_raw_hangup(struct serio_raw *serio_raw)
{
struct serio_raw_client *client;
serio_pause_rx(serio_raw->serio);
list_for_each_entry(client, &serio_raw->client_list, node)
kill_fasync(&client->fasync, SIGIO, POLL_HUP);
serio_continue_rx(serio_raw->serio);
wake_up_interruptible(&serio_raw->wait);
}
static void serio_raw_disconnect(struct serio *serio)
{
struct serio_raw *serio_raw = serio_get_drvdata(serio);
misc_deregister(&serio_raw->dev);
mutex_lock(&serio_raw_mutex);
serio_raw->dead = true;
list_del_init(&serio_raw->node);
mutex_unlock(&serio_raw_mutex);
serio_raw_hangup(serio_raw);
serio_close(serio);
kref_put(&serio_raw->kref, serio_raw_free);
serio_set_drvdata(serio, NULL);
}
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 = true,
};
module_serio_driver(serio_raw_drv);

View file

@ -0,0 +1,301 @@
/*
* 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>
#include <linux/compat.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 and flags, 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;
unsigned int ch_flags = 0;
int i;
spin_lock_irqsave(&serport->lock, flags);
if (!test_bit(SERPORT_ACTIVE, &serport->flags))
goto out;
for (i = 0; i < count; i++) {
if (fp) {
switch (fp[i]) {
case TTY_FRAME:
ch_flags = SERIO_FRAME;
break;
case TTY_PARITY:
ch_flags = SERIO_PARITY;
break;
default:
ch_flags = 0;
break;
}
}
serio_interrupt(serport->serio, cp[i], ch_flags);
}
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->dev.parent = tty->dev;
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;
}
static void serport_set_type(struct tty_struct *tty, unsigned long type)
{
struct serport *serport = tty->disc_data;
serport->id.proto = type & 0x000000ff;
serport->id.id = (type & 0x0000ff00) >> 8;
serport->id.extra = (type & 0x00ff0000) >> 16;
}
/*
* 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)
{
if (cmd == SPIOCSTYPE) {
unsigned long type;
if (get_user(type, (unsigned long __user *) arg))
return -EFAULT;
serport_set_type(tty, type);
return 0;
}
return -EINVAL;
}
#ifdef CONFIG_COMPAT
#define COMPAT_SPIOCSTYPE _IOW('q', 0x01, compat_ulong_t)
static long serport_ldisc_compat_ioctl(struct tty_struct *tty,
struct file *file,
unsigned int cmd, unsigned long arg)
{
if (cmd == COMPAT_SPIOCSTYPE) {
void __user *uarg = compat_ptr(arg);
compat_ulong_t compat_type;
if (get_user(compat_type, (compat_ulong_t __user *)uarg))
return -EFAULT;
serport_set_type(tty, compat_type);
return 0;
}
return -EINVAL;
}
#endif
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,
#ifdef CONFIG_COMPAT
.compat_ioctl = serport_ldisc_compat_ioctl,
#endif
.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,377 @@
/*
* 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/slab.h>
#include <linux/list.h>
#include <linux/io.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_irq.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 */
struct device *dev;
};
/************************************/
/* 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->dev, "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->dev,
"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->dev,
"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 structure 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 xps2_of_probe(struct platform_device *ofdev)
{
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;
unsigned int irq;
int error;
dev_info(dev, "Device Tree Probing \'%s\'\n",
ofdev->dev.of_node->name);
/* Get iospace for the device */
error = of_address_to_resource(ofdev->dev.of_node, 0, &r_mem);
if (error) {
dev_err(dev, "invalid address\n");
return error;
}
/* Get IRQ for the device */
irq = irq_of_parse_and_map(ofdev->dev.of_node, 0);
if (!irq) {
dev_err(dev, "no IRQ found\n");
return -ENODEV;
}
drvdata = kzalloc(sizeof(struct xps2data), GFP_KERNEL);
serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (!drvdata || !serio) {
error = -ENOMEM;
goto failed1;
}
spin_lock_init(&drvdata->lock);
drvdata->irq = irq;
drvdata->serio = serio;
drvdata->dev = dev;
phys_addr = r_mem.start;
remap_size = resource_size(&r_mem);
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->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);
platform_set_drvdata(ofdev, drvdata);
return 0; /* success */
failed2:
release_mem_region(phys_addr, remap_size);
failed1:
kfree(serio);
kfree(drvdata);
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 xps2_of_remove(struct platform_device *of_dev)
{
struct xps2data *drvdata = platform_get_drvdata(of_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->dev.of_node, 0, &r_mem))
dev_err(drvdata->dev, "invalid address\n");
else
release_mem_region(r_mem.start, resource_size(&r_mem));
kfree(drvdata);
return 0;
}
/* Match table for of_platform binding */
static const struct of_device_id xps2_of_match[] = {
{ .compatible = "xlnx,xps-ps2-1.00.a", },
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(of, xps2_of_match);
static struct platform_driver xps2_of_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = xps2_of_match,
},
.probe = xps2_of_probe,
.remove = xps2_of_remove,
};
module_platform_driver(xps2_of_driver);
MODULE_AUTHOR("Xilinx, Inc.");
MODULE_DESCRIPTION("Xilinx XPS PS/2 driver");
MODULE_LICENSE("GPL");