summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/fdomain_isa.c
blob: 2b4280a43a53e2d9233c8a1bf1f78f8c05deafbf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
// SPDX-License-Identifier: GPL-2.0

#include <linux/module.h>
#include <linux/io.h>
#include <linux/isa.h>
#include <scsi/scsi_host.h>
#include "fdomain.h"

#define MAXBOARDS_PARAM 4
static int io[MAXBOARDS_PARAM] = { 0, 0, 0, 0 };
module_param_hw_array(io, int, ioport, NULL, 0);
MODULE_PARM_DESC(io, "base I/O address of controller (0x140, 0x150, 0x160, 0x170)");

static int irq[MAXBOARDS_PARAM] = { 0, 0, 0, 0 };
module_param_hw_array(irq, int, irq, NULL, 0);
MODULE_PARM_DESC(irq, "IRQ of controller (0=auto [default])");

static int scsi_id[MAXBOARDS_PARAM] = { 0, 0, 0, 0 };
module_param_hw_array(scsi_id, int, other, NULL, 0);
MODULE_PARM_DESC(scsi_id, "SCSI ID of controller (default = 7)");

static unsigned long addresses[] = {
	0xc8000,
	0xca000,
	0xce000,
	0xde000,
};
#define ADDRESS_COUNT ARRAY_SIZE(addresses)

static unsigned short ports[] = { 0x140, 0x150, 0x160, 0x170 };
#define PORT_COUNT ARRAY_SIZE(ports)

static unsigned short irqs[] = { 3, 5, 10, 11, 12, 14, 15, 0 };

/* This driver works *ONLY* for Future Domain cards using the TMC-1800,
 * TMC-18C50, or TMC-18C30 chip.  This includes models TMC-1650, 1660, 1670,
 * and 1680. These are all 16-bit cards.
 * BIOS versions prior to 3.2 assigned SCSI ID 6 to SCSI adapter.
 *
 * The following BIOS signature signatures are for boards which do *NOT*
 * work with this driver (these TMC-8xx and TMC-9xx boards may work with the
 * Seagate driver):
 *
 * FUTURE DOMAIN CORP. (C) 1986-1988 V4.0I 03/16/88
 * FUTURE DOMAIN CORP. (C) 1986-1989 V5.0C2/14/89
 * FUTURE DOMAIN CORP. (C) 1986-1989 V6.0A7/28/89
 * FUTURE DOMAIN CORP. (C) 1986-1990 V6.0105/31/90
 * FUTURE DOMAIN CORP. (C) 1986-1990 V6.0209/18/90
 * FUTURE DOMAIN CORP. (C) 1986-1990 V7.009/18/90
 * FUTURE DOMAIN CORP. (C) 1992 V8.00.004/02/92
 *
 * (The cards which do *NOT* work are all 8-bit cards -- although some of
 * them have a 16-bit form-factor, the upper 8-bits are used only for IRQs
 * and are *NOT* used for data. You can tell the difference by following
 * the tracings on the circuit board -- if only the IRQ lines are involved,
 * you have a "8-bit" card, and should *NOT* use this driver.)
 */

static struct signature {
	const char *signature;
	int offset;
	int length;
	int this_id;
	int base_offset;
} signatures[] = {
/*          1         2         3         4         5         6 */
/* 123456789012345678901234567890123456789012345678901234567890 */
{ "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.07/28/89",	 5, 50,  6, 0x1fcc },
{ "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V1.07/28/89",	 5, 50,  6, 0x1fcc },
{ "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.07/28/89", 72, 50,  6, 0x1fa2 },
{ "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.0",	73, 43,  6, 0x1fa2 },
{ "FUTURE DOMAIN CORP. (C) 1991 1800-V2.0.",		72, 39,  6, 0x1fa3 },
{ "FUTURE DOMAIN CORP. (C) 1992 V3.00.004/02/92",	 5, 44,  6, 0 },
{ "FUTURE DOMAIN TMC-18XX (C) 1993 V3.203/12/93",	 5, 44,  7, 0 },
{ "IBM F1 P2 BIOS v1.0011/09/92",			 5, 28,  7, 0x1ff3 },
{ "IBM F1 P2 BIOS v1.0104/29/93",			 5, 28,  7, 0 },
{ "Future Domain Corp. V1.0008/18/93",			 5, 33,  7, 0 },
{ "Future Domain Corp. V2.0108/18/93",			 5, 33,  7, 0 },
{ "FUTURE DOMAIN CORP.  V3.5008/18/93",			 5, 34,  7, 0 },
{ "FUTURE DOMAIN 18c30/18c50/1800 (C) 1994 V3.5",	 5, 44,  7, 0 },
{ "FUTURE DOMAIN CORP.  V3.6008/18/93",			 5, 34,  7, 0 },
{ "FUTURE DOMAIN CORP.  V3.6108/18/93",			 5, 34,  7, 0 },
};
#define SIGNATURE_COUNT ARRAY_SIZE(signatures)

static int fdomain_isa_match(struct device *dev, unsigned int ndev)
{
	struct Scsi_Host *sh;
	int i, base = 0, irq = 0;
	unsigned long bios_base = 0;
	struct signature *sig = NULL;
	void __iomem *p;
	static struct signature *saved_sig;
	int this_id = 7;

	if (ndev < ADDRESS_COUNT) {	/* scan supported ISA BIOS addresses */
		p = ioremap(addresses[ndev], FDOMAIN_BIOS_SIZE);
		if (!p)
			return 0;
		for (i = 0; i < SIGNATURE_COUNT; i++)
			if (check_signature(p + signatures[i].offset,
					    signatures[i].signature,
					    signatures[i].length))
				break;
		if (i == SIGNATURE_COUNT)	/* no signature found */
			goto fail_unmap;
		sig = &signatures[i];
		bios_base = addresses[ndev];
		/* read I/O base from BIOS area */
		if (sig->base_offset)
			base = readb(p + sig->base_offset) +
			      (readb(p + sig->base_offset + 1) << 8);
		iounmap(p);
		if (base) {
			dev_info(dev, "BIOS at 0x%lx specifies I/O base 0x%x\n",
				 bios_base, base);
		} else { /* no I/O base in BIOS area */
			dev_info(dev, "BIOS at 0x%lx\n", bios_base);
			/* save BIOS signature for later use in port probing */
			saved_sig = sig;
			return 0;
		}
	} else	/* scan supported I/O ports */
		base = ports[ndev - ADDRESS_COUNT];

	/* use saved BIOS signature if present */
	if (!sig && saved_sig)
		sig = saved_sig;

	if (!request_region(base, FDOMAIN_REGION_SIZE, "fdomain_isa"))
		return 0;

	irq = irqs[(inb(base + REG_CFG1) & CFG1_IRQ_MASK) >> 1];

	if (sig)
		this_id = sig->this_id;

	sh = fdomain_create(base, irq, this_id, dev);
	if (!sh) {
		release_region(base, FDOMAIN_REGION_SIZE);
		return 0;
	}

	dev_set_drvdata(dev, sh);
	return 1;
fail_unmap:
	iounmap(p);
	return 0;
}

static int fdomain_isa_param_match(struct device *dev, unsigned int ndev)
{
	struct Scsi_Host *sh;
	int irq_ = irq[ndev];

	if (!io[ndev])
		return 0;

	if (!request_region(io[ndev], FDOMAIN_REGION_SIZE, "fdomain_isa")) {
		dev_err(dev, "base 0x%x already in use", io[ndev]);
		return 0;
	}

	if (irq_ <= 0)
		irq_ = irqs[(inb(io[ndev] + REG_CFG1) & CFG1_IRQ_MASK) >> 1];

	sh = fdomain_create(io[ndev], irq_, scsi_id[ndev], dev);
	if (!sh) {
		dev_err(dev, "controller not found at base 0x%x", io[ndev]);
		release_region(io[ndev], FDOMAIN_REGION_SIZE);
		return 0;
	}

	dev_set_drvdata(dev, sh);
	return 1;
}

static void fdomain_isa_remove(struct device *dev, unsigned int ndev)
{
	struct Scsi_Host *sh = dev_get_drvdata(dev);
	int base = sh->io_port;

	fdomain_destroy(sh);
	release_region(base, FDOMAIN_REGION_SIZE);
	dev_set_drvdata(dev, NULL);
}

static struct isa_driver fdomain_isa_driver = {
	.match		= fdomain_isa_match,
	.remove		= fdomain_isa_remove,
	.driver = {
		.name	= "fdomain_isa",
		.pm	= FDOMAIN_PM_OPS,
	},
};

static int __init fdomain_isa_init(void)
{
	int isa_probe_count = ADDRESS_COUNT + PORT_COUNT;

	if (io[0]) {	/* use module parameters if present */
		fdomain_isa_driver.match = fdomain_isa_param_match;
		isa_probe_count = MAXBOARDS_PARAM;
	}

	return isa_register_driver(&fdomain_isa_driver, isa_probe_count);
}

static void __exit fdomain_isa_exit(void)
{
	isa_unregister_driver(&fdomain_isa_driver);
}

module_init(fdomain_isa_init);
module_exit(fdomain_isa_exit);

MODULE_AUTHOR("Ondrej Zary, Rickard E. Faith");
MODULE_DESCRIPTION("Future Domain TMC-16x0 ISA SCSI driver");
MODULE_LICENSE("GPL");