// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ISA Plug & Play support
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
* Changelog:
* 2000-01-01 Added quirks handling for buggy hardware
* Peter Denison <peterd@pnd-pc.demon.co.uk>
* 2000-06-14 Added isapnp_probe_devs() and isapnp_activate_dev()
* Christoph Hellwig <hch@infradead.org>
* 2001-06-03 Added release_region calls to correspond with
* request_region calls when a failure occurs. Also
* added KERN_* constants to printk() calls.
* 2001-11-07 Added isapnp_{,un}register_driver calls along the lines
* of the pci driver interface
* Kai Germaschewski <kai.germaschewski@gmx.de>
* 2002-06-06 Made the use of dma channel 0 configurable
* Gerald Teschl <gerald.teschl@univie.ac.at>
* 2002-10-06 Ported to PnP Layer - Adam Belay <ambx1@neo.rr.com>
* 2003-08-11 Resource Management Updates - Adam Belay <ambx1@neo.rr.com>
*/
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/isapnp.h>
#include <linux/mutex.h>
#include <asm/io.h>
#include "../base.h"
#if 0
#define ISAPNP_REGION_OK
#endif
int isapnp_disable; /* Disable ISA PnP */
static int isapnp_rdp; /* Read Data Port */
static int isapnp_reset = 1; /* reset all PnP cards (deactivate) */
static int isapnp_verbose = 1; /* verbose mode */
module_param(isapnp_disable, int, 0);
MODULE_PARM_DESC(isapnp_disable, "ISA Plug & Play disable");
module_param(isapnp_rdp, int, 0);
MODULE_PARM_DESC(isapnp_rdp, "ISA Plug & Play read data port");
module_param(isapnp_reset, int, 0);
MODULE_PARM_DESC(isapnp_reset, "ISA Plug & Play reset all cards");
module_param(isapnp_verbose, int, 0);
MODULE_PARM_DESC(isapnp_verbose, "ISA Plug & Play verbose mode");
#define _PIDXR 0x279
#define _PNPWRP 0xa79
/* short tags */
#define _STAG_PNPVERNO 0x01
#define _STAG_LOGDEVID 0x02
#define _STAG_COMPATDEVID 0x03
#define _STAG_IRQ 0x04
#define _STAG_DMA 0x05
#define _STAG_STARTDEP 0x06
#define _STAG_ENDDEP 0x07
#define _STAG_IOPORT 0x08
#define _STAG_FIXEDIO 0x09
#define _STAG_VENDOR 0x0e
#define _STAG_END 0x0f
/* long tags */
#define _LTAG_MEMRANGE 0x81
#define _LTAG_ANSISTR 0x82
#define _LTAG_UNICODESTR 0x83
#define _LTAG_VENDOR 0x84
#define _LTAG_MEM32RANGE 0x85
#define _LTAG_FIXEDMEM32RANGE 0x86
/* Logical device control and configuration registers */
#define ISAPNP_CFG_ACTIVATE 0x30 /* byte */
#define ISAPNP_CFG_MEM 0x40 /* 4 * dword */
#define ISAPNP_CFG_PORT 0x60 /* 8 * word */
#define ISAPNP_CFG_IRQ 0x70 /* 2 * word */
#define ISAPNP_CFG_DMA 0x74 /* 2 * byte */
/*
* Sizes of ISAPNP logical device configuration register sets.
* See PNP-ISA-v1.0a.pdf, Appendix A.
*/
#define ISAPNP_MAX_MEM 4
#define ISAPNP_MAX_PORT 8
#define ISAPNP_MAX_IRQ 2
#define ISAPNP_MAX_DMA 2
static unsigned char isapnp_checksum_value;
static DEFINE_MUTEX(isapnp_cfg_mutex);
static int isapnp_csn_count;
/* some prototypes */
static inline void write_data(unsigned char x)
{
outb(x, _PNPWRP);
}
static inline void write_address(unsigned char x)
{
outb(x, _PIDXR);
udelay(20);
}
static inline unsigned char read_data(void)
{
unsigned char val = inb(isapnp_rdp);
return val;
}
unsigned char isapnp_read_byte(unsigned char idx)
{
write_address(idx);
return read_data();
}
static unsigned short isapnp_read_word(unsigned char idx)
{
unsigned short val;
val = isapnp_read_byte(idx);
val = (val << 8) + isapnp_read_byte(idx + 1);
return val;
}
void isapnp_write_byte(unsigned char idx, unsigned char val)
{
write_address(idx);
write_data(val);
}
static void isapnp_write_word(unsigned char idx, unsigned short val)
{
isapnp_write_byte(idx, val >> 8);
isapnp_write_byte(idx + 1, val);
}
static void isapnp_key(void)
{
unsigned char code = 0x6a, msb;
int i;
mdelay(1);
write_address(0x00);
write_address(0x00);
write_address(code);
for (i = 1; i < 32; i++) {
msb = ((code & 0x01) ^ ((code & 0x02) >> 1)) << 7;
code = (code >> 1) | msb;
write_address(code);
}
}
/* place all pnp cards in wait-for-key state */
static void isapnp_wait(void)
{
isapnp_write_byte(0x02, 0x02);
}
static void isapnp_wake(unsigned char csn)
{
isapnp_write_byte(0x03, csn);
}
static void isapnp_device(unsigned char logdev)
{
isapnp_write_byte(0x07, logdev);
}
static void isapnp_activate(unsigned char logdev)
{
isapnp_device(logdev);
isapnp_write_byte(ISAPNP_CFG_ACTIVATE, 1);
udelay(250);
}
static void isapnp_deactivate(unsigned char logdev)
{
isapnp_device(logdev);
isapnp_write_byte(ISAPNP_CFG_ACTIVATE, 0);
udelay(500);
}
static void __init isapnp_peek(unsigned char *data, int bytes)
{
int i, j;
unsigned char d = 0;
for (i = 1; i <= bytes; i++) {
for (j = 0; j < 20; j++) {
d = isapnp_read_byte(0x05);
if (d & 1)
break;
udelay(100);
}
if (!(d & 1)) {
if (data != NULL)
*data++ = 0xff;
continue;
}
d