mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 17:18:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
284
drivers/input/serio/Kconfig
Normal file
284
drivers/input/serio/Kconfig
Normal 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
|
31
drivers/input/serio/Makefile
Normal file
31
drivers/input/serio/Makefile
Normal 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
|
201
drivers/input/serio/altera_ps2.c
Normal file
201
drivers/input/serio/altera_ps2.c
Normal 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);
|
213
drivers/input/serio/ambakmi.c
Normal file
213
drivers/input/serio/ambakmi.c
Normal 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");
|
191
drivers/input/serio/ams_delta_serio.c
Normal file
191
drivers/input/serio/ams_delta_serio.c
Normal 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);
|
228
drivers/input/serio/apbps2.c
Normal file
228
drivers/input/serio/apbps2.c
Normal 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");
|
280
drivers/input/serio/arc_ps2.c
Normal file
280
drivers/input/serio/arc_ps2.c
Normal 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");
|
364
drivers/input/serio/at32psif.c
Normal file
364
drivers/input/serio/at32psif.c
Normal 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");
|
261
drivers/input/serio/ct82c710.c
Normal file
261
drivers/input/serio/ct82c710.c
Normal 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);
|
464
drivers/input/serio/gscps2.c
Normal file
464
drivers/input/serio/gscps2.c
Normal 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);
|
||||
|
1016
drivers/input/serio/hil_mlc.c
Normal file
1016
drivers/input/serio/hil_mlc.c
Normal file
File diff suppressed because it is too large
Load diff
1135
drivers/input/serio/hp_sdc.c
Normal file
1135
drivers/input/serio/hp_sdc.c
Normal file
File diff suppressed because it is too large
Load diff
358
drivers/input/serio/hp_sdc_mlc.c
Normal file
358
drivers/input/serio/hp_sdc_mlc.c
Normal 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);
|
452
drivers/input/serio/hyperv-keyboard.c
Normal file
452
drivers/input/serio/hyperv-keyboard.c
Normal 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);
|
95
drivers/input/serio/i8042-io.h
Normal file
95
drivers/input/serio/i8042-io.h
Normal 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 */
|
76
drivers/input/serio/i8042-ip22io.h
Normal file
76
drivers/input/serio/i8042-ip22io.h
Normal 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 */
|
69
drivers/input/serio/i8042-jazzio.h
Normal file
69
drivers/input/serio/i8042-jazzio.h
Normal 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 */
|
61
drivers/input/serio/i8042-ppcio.h
Normal file
61
drivers/input/serio/i8042-ppcio.h
Normal 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 */
|
75
drivers/input/serio/i8042-snirm.h
Normal file
75
drivers/input/serio/i8042-snirm.h
Normal 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 */
|
158
drivers/input/serio/i8042-sparcio.h
Normal file
158
drivers/input/serio/i8042-sparcio.h
Normal 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 */
|
73
drivers/input/serio/i8042-unicore32io.h
Normal file
73
drivers/input/serio/i8042-unicore32io.h
Normal 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 */
|
1097
drivers/input/serio/i8042-x86ia64io.h
Normal file
1097
drivers/input/serio/i8042-x86ia64io.h
Normal file
File diff suppressed because it is too large
Load diff
1529
drivers/input/serio/i8042.c
Normal file
1529
drivers/input/serio/i8042.c
Normal file
File diff suppressed because it is too large
Load diff
85
drivers/input/serio/i8042.h
Normal file
85
drivers/input/serio/i8042.h
Normal 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 */
|
374
drivers/input/serio/libps2.c
Normal file
374
drivers/input/serio/libps2.c
Normal 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);
|
210
drivers/input/serio/maceps2.c
Normal file
210
drivers/input/serio/maceps2.c
Normal 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);
|
283
drivers/input/serio/olpc_apsp.c
Normal file
283
drivers/input/serio/olpc_apsp.c
Normal 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);
|
218
drivers/input/serio/parkbd.c
Normal file
218
drivers/input/serio/parkbd.c
Normal 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);
|
220
drivers/input/serio/pcips2.c
Normal file
220
drivers/input/serio/pcips2.c
Normal 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");
|
307
drivers/input/serio/ps2mult.c
Normal file
307
drivers/input/serio/ps2mult.c
Normal 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);
|
194
drivers/input/serio/q40kbd.c
Normal file
194
drivers/input/serio/q40kbd.c
Normal 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);
|
174
drivers/input/serio/rpckbd.c
Normal file
174
drivers/input/serio/rpckbd.c
Normal 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);
|
378
drivers/input/serio/sa1111ps2.c
Normal file
378
drivers/input/serio/sa1111ps2.c
Normal 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
1058
drivers/input/serio/serio.c
Normal file
File diff suppressed because it is too large
Load diff
444
drivers/input/serio/serio_raw.c
Normal file
444
drivers/input/serio/serio_raw.c
Normal 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);
|
301
drivers/input/serio/serport.c
Normal file
301
drivers/input/serio/serport.c
Normal 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);
|
377
drivers/input/serio/xilinx_ps2.c
Normal file
377
drivers/input/serio/xilinx_ps2.c
Normal 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");
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue