18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * linux/drivers/input/serio/pcips2.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2003 Russell King, All Rights Reserved.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  I'm not sure if this is a generic PS/2 PCI interface or specific to
88c2ecf20Sopenharmony_ci *  the Mobility Electronics docking station.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
128c2ecf20Sopenharmony_ci#include <linux/ioport.h>
138c2ecf20Sopenharmony_ci#include <linux/input.h>
148c2ecf20Sopenharmony_ci#include <linux/pci.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/serio.h>
178c2ecf20Sopenharmony_ci#include <linux/delay.h>
188c2ecf20Sopenharmony_ci#include <asm/io.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define PS2_CTRL		(0)
218c2ecf20Sopenharmony_ci#define PS2_STATUS		(1)
228c2ecf20Sopenharmony_ci#define PS2_DATA		(2)
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define PS2_CTRL_CLK		(1<<0)
258c2ecf20Sopenharmony_ci#define PS2_CTRL_DAT		(1<<1)
268c2ecf20Sopenharmony_ci#define PS2_CTRL_TXIRQ		(1<<2)
278c2ecf20Sopenharmony_ci#define PS2_CTRL_ENABLE		(1<<3)
288c2ecf20Sopenharmony_ci#define PS2_CTRL_RXIRQ		(1<<4)
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define PS2_STAT_CLK		(1<<0)
318c2ecf20Sopenharmony_ci#define PS2_STAT_DAT		(1<<1)
328c2ecf20Sopenharmony_ci#define PS2_STAT_PARITY		(1<<2)
338c2ecf20Sopenharmony_ci#define PS2_STAT_RXFULL		(1<<5)
348c2ecf20Sopenharmony_ci#define PS2_STAT_TXBUSY		(1<<6)
358c2ecf20Sopenharmony_ci#define PS2_STAT_TXEMPTY	(1<<7)
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistruct pcips2_data {
388c2ecf20Sopenharmony_ci	struct serio	*io;
398c2ecf20Sopenharmony_ci	unsigned int	base;
408c2ecf20Sopenharmony_ci	struct pci_dev	*dev;
418c2ecf20Sopenharmony_ci};
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic int pcips2_write(struct serio *io, unsigned char val)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct pcips2_data *ps2if = io->port_data;
468c2ecf20Sopenharmony_ci	unsigned int stat;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	do {
498c2ecf20Sopenharmony_ci		stat = inb(ps2if->base + PS2_STATUS);
508c2ecf20Sopenharmony_ci		cpu_relax();
518c2ecf20Sopenharmony_ci	} while (!(stat & PS2_STAT_TXEMPTY));
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	outb(val, ps2if->base + PS2_DATA);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	return 0;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic irqreturn_t pcips2_interrupt(int irq, void *devid)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	struct pcips2_data *ps2if = devid;
618c2ecf20Sopenharmony_ci	unsigned char status, scancode;
628c2ecf20Sopenharmony_ci	int handled = 0;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	do {
658c2ecf20Sopenharmony_ci		unsigned int flag;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci		status = inb(ps2if->base + PS2_STATUS);
688c2ecf20Sopenharmony_ci		if (!(status & PS2_STAT_RXFULL))
698c2ecf20Sopenharmony_ci			break;
708c2ecf20Sopenharmony_ci		handled = 1;
718c2ecf20Sopenharmony_ci		scancode = inb(ps2if->base + PS2_DATA);
728c2ecf20Sopenharmony_ci		if (status == 0xff && scancode == 0xff)
738c2ecf20Sopenharmony_ci			break;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci		flag = (status & PS2_STAT_PARITY) ? 0 : SERIO_PARITY;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci		if (hweight8(scancode) & 1)
788c2ecf20Sopenharmony_ci			flag ^= SERIO_PARITY;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci		serio_interrupt(ps2if->io, scancode, flag);
818c2ecf20Sopenharmony_ci	} while (1);
828c2ecf20Sopenharmony_ci	return IRQ_RETVAL(handled);
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic void pcips2_flush_input(struct pcips2_data *ps2if)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	unsigned char status, scancode;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	do {
908c2ecf20Sopenharmony_ci		status = inb(ps2if->base + PS2_STATUS);
918c2ecf20Sopenharmony_ci		if (!(status & PS2_STAT_RXFULL))
928c2ecf20Sopenharmony_ci			break;
938c2ecf20Sopenharmony_ci		scancode = inb(ps2if->base + PS2_DATA);
948c2ecf20Sopenharmony_ci		if (status == 0xff && scancode == 0xff)
958c2ecf20Sopenharmony_ci			break;
968c2ecf20Sopenharmony_ci	} while (1);
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic int pcips2_open(struct serio *io)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	struct pcips2_data *ps2if = io->port_data;
1028c2ecf20Sopenharmony_ci	int ret, val = 0;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	outb(PS2_CTRL_ENABLE, ps2if->base);
1058c2ecf20Sopenharmony_ci	pcips2_flush_input(ps2if);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	ret = request_irq(ps2if->dev->irq, pcips2_interrupt, IRQF_SHARED,
1088c2ecf20Sopenharmony_ci			  "pcips2", ps2if);
1098c2ecf20Sopenharmony_ci	if (ret == 0)
1108c2ecf20Sopenharmony_ci		val = PS2_CTRL_ENABLE | PS2_CTRL_RXIRQ;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	outb(val, ps2if->base);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	return ret;
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic void pcips2_close(struct serio *io)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	struct pcips2_data *ps2if = io->port_data;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	outb(0, ps2if->base);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	free_irq(ps2if->dev->irq, ps2if);
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic int pcips2_probe(struct pci_dev *dev, const struct pci_device_id *id)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	struct pcips2_data *ps2if;
1298c2ecf20Sopenharmony_ci	struct serio *serio;
1308c2ecf20Sopenharmony_ci	int ret;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	ret = pci_enable_device(dev);
1338c2ecf20Sopenharmony_ci	if (ret)
1348c2ecf20Sopenharmony_ci		goto out;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	ret = pci_request_regions(dev, "pcips2");
1378c2ecf20Sopenharmony_ci	if (ret)
1388c2ecf20Sopenharmony_ci		goto disable;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	ps2if = kzalloc(sizeof(struct pcips2_data), GFP_KERNEL);
1418c2ecf20Sopenharmony_ci	serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
1428c2ecf20Sopenharmony_ci	if (!ps2if || !serio) {
1438c2ecf20Sopenharmony_ci		ret = -ENOMEM;
1448c2ecf20Sopenharmony_ci		goto release;
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	serio->id.type		= SERIO_8042;
1498c2ecf20Sopenharmony_ci	serio->write		= pcips2_write;
1508c2ecf20Sopenharmony_ci	serio->open		= pcips2_open;
1518c2ecf20Sopenharmony_ci	serio->close		= pcips2_close;
1528c2ecf20Sopenharmony_ci	strlcpy(serio->name, pci_name(dev), sizeof(serio->name));
1538c2ecf20Sopenharmony_ci	strlcpy(serio->phys, dev_name(&dev->dev), sizeof(serio->phys));
1548c2ecf20Sopenharmony_ci	serio->port_data	= ps2if;
1558c2ecf20Sopenharmony_ci	serio->dev.parent	= &dev->dev;
1568c2ecf20Sopenharmony_ci	ps2if->io		= serio;
1578c2ecf20Sopenharmony_ci	ps2if->dev		= dev;
1588c2ecf20Sopenharmony_ci	ps2if->base		= pci_resource_start(dev, 0);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	pci_set_drvdata(dev, ps2if);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	serio_register_port(ps2if->io);
1638c2ecf20Sopenharmony_ci	return 0;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci release:
1668c2ecf20Sopenharmony_ci	kfree(ps2if);
1678c2ecf20Sopenharmony_ci	kfree(serio);
1688c2ecf20Sopenharmony_ci	pci_release_regions(dev);
1698c2ecf20Sopenharmony_ci disable:
1708c2ecf20Sopenharmony_ci	pci_disable_device(dev);
1718c2ecf20Sopenharmony_ci out:
1728c2ecf20Sopenharmony_ci	return ret;
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic void pcips2_remove(struct pci_dev *dev)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	struct pcips2_data *ps2if = pci_get_drvdata(dev);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	serio_unregister_port(ps2if->io);
1808c2ecf20Sopenharmony_ci	kfree(ps2if);
1818c2ecf20Sopenharmony_ci	pci_release_regions(dev);
1828c2ecf20Sopenharmony_ci	pci_disable_device(dev);
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic const struct pci_device_id pcips2_ids[] = {
1868c2ecf20Sopenharmony_ci	{
1878c2ecf20Sopenharmony_ci		.vendor		= 0x14f2,	/* MOBILITY */
1888c2ecf20Sopenharmony_ci		.device		= 0x0123,	/* Keyboard */
1898c2ecf20Sopenharmony_ci		.subvendor	= PCI_ANY_ID,
1908c2ecf20Sopenharmony_ci		.subdevice	= PCI_ANY_ID,
1918c2ecf20Sopenharmony_ci		.class		= PCI_CLASS_INPUT_KEYBOARD << 8,
1928c2ecf20Sopenharmony_ci		.class_mask	= 0xffff00,
1938c2ecf20Sopenharmony_ci	},
1948c2ecf20Sopenharmony_ci	{
1958c2ecf20Sopenharmony_ci		.vendor		= 0x14f2,	/* MOBILITY */
1968c2ecf20Sopenharmony_ci		.device		= 0x0124,	/* Mouse */
1978c2ecf20Sopenharmony_ci		.subvendor	= PCI_ANY_ID,
1988c2ecf20Sopenharmony_ci		.subdevice	= PCI_ANY_ID,
1998c2ecf20Sopenharmony_ci		.class		= PCI_CLASS_INPUT_MOUSE << 8,
2008c2ecf20Sopenharmony_ci		.class_mask	= 0xffff00,
2018c2ecf20Sopenharmony_ci	},
2028c2ecf20Sopenharmony_ci	{ 0, }
2038c2ecf20Sopenharmony_ci};
2048c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pcips2_ids);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic struct pci_driver pcips2_driver = {
2078c2ecf20Sopenharmony_ci	.name			= "pcips2",
2088c2ecf20Sopenharmony_ci	.id_table		= pcips2_ids,
2098c2ecf20Sopenharmony_ci	.probe			= pcips2_probe,
2108c2ecf20Sopenharmony_ci	.remove			= pcips2_remove,
2118c2ecf20Sopenharmony_ci};
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cimodule_pci_driver(pcips2_driver);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
2168c2ecf20Sopenharmony_ciMODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
2178c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PCI PS/2 keyboard/mouse driver");
218