217 lines · 4.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * linux/drivers/input/serio/pcips2.c * *  Copyright (C) 2003 Russell King, All Rights Reserved. * *  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_obj(*ps2if);	serio = kzalloc_obj(*serio);	if (!ps2if || !serio) {		ret = -ENOMEM;		goto release;	}  	serio->id.type		= SERIO_8042;	serio->write		= pcips2_write;	serio->open		= pcips2_open;	serio->close		= pcips2_close;	strscpy(serio->name, pci_name(dev), sizeof(serio->name));	strscpy(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");