162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/module.h>
462306a36Sopenharmony_ci#include <linux/io.h>
562306a36Sopenharmony_ci#include <linux/isa.h>
662306a36Sopenharmony_ci#include <scsi/scsi_host.h>
762306a36Sopenharmony_ci#include "fdomain.h"
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#define MAXBOARDS_PARAM 4
1062306a36Sopenharmony_cistatic int io[MAXBOARDS_PARAM] = { 0, 0, 0, 0 };
1162306a36Sopenharmony_cimodule_param_hw_array(io, int, ioport, NULL, 0);
1262306a36Sopenharmony_ciMODULE_PARM_DESC(io, "base I/O address of controller (0x140, 0x150, 0x160, 0x170)");
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic int irq[MAXBOARDS_PARAM] = { 0, 0, 0, 0 };
1562306a36Sopenharmony_cimodule_param_hw_array(irq, int, irq, NULL, 0);
1662306a36Sopenharmony_ciMODULE_PARM_DESC(irq, "IRQ of controller (0=auto [default])");
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic int scsi_id[MAXBOARDS_PARAM] = { 0, 0, 0, 0 };
1962306a36Sopenharmony_cimodule_param_hw_array(scsi_id, int, other, NULL, 0);
2062306a36Sopenharmony_ciMODULE_PARM_DESC(scsi_id, "SCSI ID of controller (default = 7)");
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic unsigned long addresses[] = {
2362306a36Sopenharmony_ci	0xc8000,
2462306a36Sopenharmony_ci	0xca000,
2562306a36Sopenharmony_ci	0xce000,
2662306a36Sopenharmony_ci	0xde000,
2762306a36Sopenharmony_ci};
2862306a36Sopenharmony_ci#define ADDRESS_COUNT ARRAY_SIZE(addresses)
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic unsigned short ports[] = { 0x140, 0x150, 0x160, 0x170 };
3162306a36Sopenharmony_ci#define PORT_COUNT ARRAY_SIZE(ports)
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic unsigned short irqs[] = { 3, 5, 10, 11, 12, 14, 15, 0 };
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/* This driver works *ONLY* for Future Domain cards using the TMC-1800,
3662306a36Sopenharmony_ci * TMC-18C50, or TMC-18C30 chip.  This includes models TMC-1650, 1660, 1670,
3762306a36Sopenharmony_ci * and 1680. These are all 16-bit cards.
3862306a36Sopenharmony_ci * BIOS versions prior to 3.2 assigned SCSI ID 6 to SCSI adapter.
3962306a36Sopenharmony_ci *
4062306a36Sopenharmony_ci * The following BIOS signature signatures are for boards which do *NOT*
4162306a36Sopenharmony_ci * work with this driver (these TMC-8xx and TMC-9xx boards may work with the
4262306a36Sopenharmony_ci * Seagate driver):
4362306a36Sopenharmony_ci *
4462306a36Sopenharmony_ci * FUTURE DOMAIN CORP. (C) 1986-1988 V4.0I 03/16/88
4562306a36Sopenharmony_ci * FUTURE DOMAIN CORP. (C) 1986-1989 V5.0C2/14/89
4662306a36Sopenharmony_ci * FUTURE DOMAIN CORP. (C) 1986-1989 V6.0A7/28/89
4762306a36Sopenharmony_ci * FUTURE DOMAIN CORP. (C) 1986-1990 V6.0105/31/90
4862306a36Sopenharmony_ci * FUTURE DOMAIN CORP. (C) 1986-1990 V6.0209/18/90
4962306a36Sopenharmony_ci * FUTURE DOMAIN CORP. (C) 1986-1990 V7.009/18/90
5062306a36Sopenharmony_ci * FUTURE DOMAIN CORP. (C) 1992 V8.00.004/02/92
5162306a36Sopenharmony_ci *
5262306a36Sopenharmony_ci * (The cards which do *NOT* work are all 8-bit cards -- although some of
5362306a36Sopenharmony_ci * them have a 16-bit form-factor, the upper 8-bits are used only for IRQs
5462306a36Sopenharmony_ci * and are *NOT* used for data. You can tell the difference by following
5562306a36Sopenharmony_ci * the tracings on the circuit board -- if only the IRQ lines are involved,
5662306a36Sopenharmony_ci * you have a "8-bit" card, and should *NOT* use this driver.)
5762306a36Sopenharmony_ci */
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic struct signature {
6062306a36Sopenharmony_ci	const char *signature;
6162306a36Sopenharmony_ci	int offset;
6262306a36Sopenharmony_ci	int length;
6362306a36Sopenharmony_ci	int this_id;
6462306a36Sopenharmony_ci	int base_offset;
6562306a36Sopenharmony_ci} signatures[] = {
6662306a36Sopenharmony_ci/*          1         2         3         4         5         6 */
6762306a36Sopenharmony_ci/* 123456789012345678901234567890123456789012345678901234567890 */
6862306a36Sopenharmony_ci{ "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.07/28/89",	 5, 50,  6, 0x1fcc },
6962306a36Sopenharmony_ci{ "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V1.07/28/89",	 5, 50,  6, 0x1fcc },
7062306a36Sopenharmony_ci{ "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.07/28/89", 72, 50,  6, 0x1fa2 },
7162306a36Sopenharmony_ci{ "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.0",	73, 43,  6, 0x1fa2 },
7262306a36Sopenharmony_ci{ "FUTURE DOMAIN CORP. (C) 1991 1800-V2.0.",		72, 39,  6, 0x1fa3 },
7362306a36Sopenharmony_ci{ "FUTURE DOMAIN CORP. (C) 1992 V3.00.004/02/92",	 5, 44,  6, 0 },
7462306a36Sopenharmony_ci{ "FUTURE DOMAIN TMC-18XX (C) 1993 V3.203/12/93",	 5, 44,  7, 0 },
7562306a36Sopenharmony_ci{ "IBM F1 P2 BIOS v1.0011/09/92",			 5, 28,  7, 0x1ff3 },
7662306a36Sopenharmony_ci{ "IBM F1 P2 BIOS v1.0104/29/93",			 5, 28,  7, 0 },
7762306a36Sopenharmony_ci{ "Future Domain Corp. V1.0008/18/93",			 5, 33,  7, 0 },
7862306a36Sopenharmony_ci{ "Future Domain Corp. V2.0108/18/93",			 5, 33,  7, 0 },
7962306a36Sopenharmony_ci{ "FUTURE DOMAIN CORP.  V3.5008/18/93",			 5, 34,  7, 0 },
8062306a36Sopenharmony_ci{ "FUTURE DOMAIN 18c30/18c50/1800 (C) 1994 V3.5",	 5, 44,  7, 0 },
8162306a36Sopenharmony_ci{ "FUTURE DOMAIN CORP.  V3.6008/18/93",			 5, 34,  7, 0 },
8262306a36Sopenharmony_ci{ "FUTURE DOMAIN CORP.  V3.6108/18/93",			 5, 34,  7, 0 },
8362306a36Sopenharmony_ci};
8462306a36Sopenharmony_ci#define SIGNATURE_COUNT ARRAY_SIZE(signatures)
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic int fdomain_isa_match(struct device *dev, unsigned int ndev)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	struct Scsi_Host *sh;
8962306a36Sopenharmony_ci	int i, base = 0, irq = 0;
9062306a36Sopenharmony_ci	unsigned long bios_base = 0;
9162306a36Sopenharmony_ci	struct signature *sig = NULL;
9262306a36Sopenharmony_ci	void __iomem *p;
9362306a36Sopenharmony_ci	static struct signature *saved_sig;
9462306a36Sopenharmony_ci	int this_id = 7;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	if (ndev < ADDRESS_COUNT) {	/* scan supported ISA BIOS addresses */
9762306a36Sopenharmony_ci		p = ioremap(addresses[ndev], FDOMAIN_BIOS_SIZE);
9862306a36Sopenharmony_ci		if (!p)
9962306a36Sopenharmony_ci			return 0;
10062306a36Sopenharmony_ci		for (i = 0; i < SIGNATURE_COUNT; i++)
10162306a36Sopenharmony_ci			if (check_signature(p + signatures[i].offset,
10262306a36Sopenharmony_ci					    signatures[i].signature,
10362306a36Sopenharmony_ci					    signatures[i].length))
10462306a36Sopenharmony_ci				break;
10562306a36Sopenharmony_ci		if (i == SIGNATURE_COUNT)	/* no signature found */
10662306a36Sopenharmony_ci			goto fail_unmap;
10762306a36Sopenharmony_ci		sig = &signatures[i];
10862306a36Sopenharmony_ci		bios_base = addresses[ndev];
10962306a36Sopenharmony_ci		/* read I/O base from BIOS area */
11062306a36Sopenharmony_ci		if (sig->base_offset)
11162306a36Sopenharmony_ci			base = readb(p + sig->base_offset) +
11262306a36Sopenharmony_ci			      (readb(p + sig->base_offset + 1) << 8);
11362306a36Sopenharmony_ci		iounmap(p);
11462306a36Sopenharmony_ci		if (base) {
11562306a36Sopenharmony_ci			dev_info(dev, "BIOS at 0x%lx specifies I/O base 0x%x\n",
11662306a36Sopenharmony_ci				 bios_base, base);
11762306a36Sopenharmony_ci		} else { /* no I/O base in BIOS area */
11862306a36Sopenharmony_ci			dev_info(dev, "BIOS at 0x%lx\n", bios_base);
11962306a36Sopenharmony_ci			/* save BIOS signature for later use in port probing */
12062306a36Sopenharmony_ci			saved_sig = sig;
12162306a36Sopenharmony_ci			return 0;
12262306a36Sopenharmony_ci		}
12362306a36Sopenharmony_ci	} else	/* scan supported I/O ports */
12462306a36Sopenharmony_ci		base = ports[ndev - ADDRESS_COUNT];
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	/* use saved BIOS signature if present */
12762306a36Sopenharmony_ci	if (!sig && saved_sig)
12862306a36Sopenharmony_ci		sig = saved_sig;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (!request_region(base, FDOMAIN_REGION_SIZE, "fdomain_isa"))
13162306a36Sopenharmony_ci		return 0;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	irq = irqs[(inb(base + REG_CFG1) & CFG1_IRQ_MASK) >> 1];
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	if (sig)
13662306a36Sopenharmony_ci		this_id = sig->this_id;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	sh = fdomain_create(base, irq, this_id, dev);
13962306a36Sopenharmony_ci	if (!sh) {
14062306a36Sopenharmony_ci		release_region(base, FDOMAIN_REGION_SIZE);
14162306a36Sopenharmony_ci		return 0;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	dev_set_drvdata(dev, sh);
14562306a36Sopenharmony_ci	return 1;
14662306a36Sopenharmony_cifail_unmap:
14762306a36Sopenharmony_ci	iounmap(p);
14862306a36Sopenharmony_ci	return 0;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic int fdomain_isa_param_match(struct device *dev, unsigned int ndev)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct Scsi_Host *sh;
15462306a36Sopenharmony_ci	int irq_ = irq[ndev];
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (!io[ndev])
15762306a36Sopenharmony_ci		return 0;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	if (!request_region(io[ndev], FDOMAIN_REGION_SIZE, "fdomain_isa")) {
16062306a36Sopenharmony_ci		dev_err(dev, "base 0x%x already in use", io[ndev]);
16162306a36Sopenharmony_ci		return 0;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	if (irq_ <= 0)
16562306a36Sopenharmony_ci		irq_ = irqs[(inb(io[ndev] + REG_CFG1) & CFG1_IRQ_MASK) >> 1];
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	sh = fdomain_create(io[ndev], irq_, scsi_id[ndev], dev);
16862306a36Sopenharmony_ci	if (!sh) {
16962306a36Sopenharmony_ci		dev_err(dev, "controller not found at base 0x%x", io[ndev]);
17062306a36Sopenharmony_ci		release_region(io[ndev], FDOMAIN_REGION_SIZE);
17162306a36Sopenharmony_ci		return 0;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	dev_set_drvdata(dev, sh);
17562306a36Sopenharmony_ci	return 1;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic void fdomain_isa_remove(struct device *dev, unsigned int ndev)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	struct Scsi_Host *sh = dev_get_drvdata(dev);
18162306a36Sopenharmony_ci	int base = sh->io_port;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	fdomain_destroy(sh);
18462306a36Sopenharmony_ci	release_region(base, FDOMAIN_REGION_SIZE);
18562306a36Sopenharmony_ci	dev_set_drvdata(dev, NULL);
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic struct isa_driver fdomain_isa_driver = {
18962306a36Sopenharmony_ci	.match		= fdomain_isa_match,
19062306a36Sopenharmony_ci	.remove		= fdomain_isa_remove,
19162306a36Sopenharmony_ci	.driver = {
19262306a36Sopenharmony_ci		.name	= "fdomain_isa",
19362306a36Sopenharmony_ci		.pm	= FDOMAIN_PM_OPS,
19462306a36Sopenharmony_ci	},
19562306a36Sopenharmony_ci};
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic int __init fdomain_isa_init(void)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	int isa_probe_count = ADDRESS_COUNT + PORT_COUNT;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if (io[0]) {	/* use module parameters if present */
20262306a36Sopenharmony_ci		fdomain_isa_driver.match = fdomain_isa_param_match;
20362306a36Sopenharmony_ci		isa_probe_count = MAXBOARDS_PARAM;
20462306a36Sopenharmony_ci	}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	return isa_register_driver(&fdomain_isa_driver, isa_probe_count);
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic void __exit fdomain_isa_exit(void)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	isa_unregister_driver(&fdomain_isa_driver);
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cimodule_init(fdomain_isa_init);
21562306a36Sopenharmony_cimodule_exit(fdomain_isa_exit);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ciMODULE_AUTHOR("Ondrej Zary, Rickard E. Faith");
21862306a36Sopenharmony_ciMODULE_DESCRIPTION("Future Domain TMC-16x0 ISA SCSI driver");
21962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
220