tc.c 5.06 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
/*
2
 *	TURBOchannel bus services.
Linus Torvalds's avatar
Linus Torvalds committed
3
 *
4 5 6
 *	Copyright (c) Harald Koerfgen, 1998
 *	Copyright (c) 2001, 2003, 2005, 2006  Maciej W. Rozycki
 *	Copyright (c) 2005  James Simmons
Linus Torvalds's avatar
Linus Torvalds committed
7
 *
8 9 10
 *	This file is subject to the terms and conditions of the GNU
 *	General Public License.  See the file "COPYING" in the main
 *	directory of this archive for more details.
Linus Torvalds's avatar
Linus Torvalds committed
11
 */
12 13
#include <linux/compiler.h>
#include <linux/errno.h>
Linus Torvalds's avatar
Linus Torvalds committed
14
#include <linux/init.h>
15
#include <linux/ioport.h>
Linus Torvalds's avatar
Linus Torvalds committed
16
#include <linux/kernel.h>
17
#include <linux/list.h>
Linus Torvalds's avatar
Linus Torvalds committed
18
#include <linux/module.h>
19
#include <linux/string.h>
20
#include <linux/tc.h>
21
#include <linux/types.h>
Linus Torvalds's avatar
Linus Torvalds committed
22

23 24
#include <asm/io.h>

25 26 27
static struct tc_bus tc_bus = {
	.name = "TURBOchannel",
};
Linus Torvalds's avatar
Linus Torvalds committed
28 29

/*
30
 * Probing for TURBOchannel modules.
Linus Torvalds's avatar
Linus Torvalds committed
31
 */
32
static void __init tc_bus_add_devices(struct tc_bus *tbus)
Linus Torvalds's avatar
Linus Torvalds committed
33
{
34 35 36 37 38 39 40
	resource_size_t slotsize = tbus->info.slot_size << 20;
	resource_size_t extslotsize = tbus->ext_slot_size;
	resource_size_t slotaddr;
	resource_size_t extslotaddr;
	resource_size_t devsize;
	void __iomem *module;
	struct tc_dev *tdev;
Linus Torvalds's avatar
Linus Torvalds committed
41
	int i, slot, err;
42
	u8 pattern[4];
43
	long offset;
Linus Torvalds's avatar
Linus Torvalds committed
44

45 46 47 48
	for (slot = 0; slot < tbus->num_tcslots; slot++) {
		slotaddr = tbus->slot_base + slot * slotsize;
		extslotaddr = tbus->ext_slot_base + slot * extslotsize;
		module = ioremap_nocache(slotaddr, slotsize);
49
		BUG_ON(!module);
Linus Torvalds's avatar
Linus Torvalds committed
50

51
		offset = TC_OLDCARD;
Linus Torvalds's avatar
Linus Torvalds committed
52 53

		err = 0;
54 55 56 57 58 59
		err |= tc_preadb(pattern + 0, module + offset + TC_PATTERN0);
		err |= tc_preadb(pattern + 1, module + offset + TC_PATTERN1);
		err |= tc_preadb(pattern + 2, module + offset + TC_PATTERN2);
		err |= tc_preadb(pattern + 3, module + offset + TC_PATTERN3);
		if (err)
			goto out_err;
Linus Torvalds's avatar
Linus Torvalds committed
60 61 62

		if (pattern[0] != 0x55 || pattern[1] != 0x00 ||
		    pattern[2] != 0xaa || pattern[3] != 0xff) {
63
			offset = TC_NEWCARD;
Linus Torvalds's avatar
Linus Torvalds committed
64 65

			err = 0;
66 67 68 69 70 71 72 73 74 75
			err |= tc_preadb(pattern + 0,
					 module + offset + TC_PATTERN0);
			err |= tc_preadb(pattern + 1,
					 module + offset + TC_PATTERN1);
			err |= tc_preadb(pattern + 2,
					 module + offset + TC_PATTERN2);
			err |= tc_preadb(pattern + 3,
					 module + offset + TC_PATTERN3);
			if (err)
				goto out_err;
Linus Torvalds's avatar
Linus Torvalds committed
76 77 78
		}

		if (pattern[0] != 0x55 || pattern[1] != 0x00 ||
79 80 81 82 83 84 85 86 87
		    pattern[2] != 0xaa || pattern[3] != 0xff)
			goto out_err;

		/* Found a board, allocate it an entry in the list */
		tdev = kzalloc(sizeof(*tdev), GFP_KERNEL);
		if (!tdev) {
			printk(KERN_ERR "tc%x: unable to allocate tc_dev\n",
			       slot);
			goto out_err;
88
		}
89 90 91 92 93
		sprintf(tdev->dev.bus_id, "tc%x", slot);
		tdev->bus = tbus;
		tdev->dev.parent = &tbus->dev;
		tdev->dev.bus = &tc_bus_type;
		tdev->slot = slot;
Linus Torvalds's avatar
Linus Torvalds committed
94

95
		for (i = 0; i < 8; i++) {
96 97 98 99 100 101
			tdev->firmware[i] =
				readb(module + offset + TC_FIRM_VER + 4 * i);
			tdev->vendor[i] =
				readb(module + offset + TC_VENDOR + 4 * i);
			tdev->name[i] =
				readb(module + offset + TC_MODULE + 4 * i);
Linus Torvalds's avatar
Linus Torvalds committed
102
		}
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
		tdev->firmware[8] = 0;
		tdev->vendor[8] = 0;
		tdev->name[8] = 0;

		pr_info("%s: %s %s %s\n", tdev->dev.bus_id, tdev->vendor,
			tdev->name, tdev->firmware);

		devsize = readb(module + offset + TC_SLOT_SIZE);
		devsize <<= 22;
		if (devsize <= slotsize) {
			tdev->resource.start = slotaddr;
			tdev->resource.end = slotaddr + devsize - 1;
		} else if (devsize <= extslotsize) {
			tdev->resource.start = extslotaddr;
			tdev->resource.end = extslotaddr + devsize - 1;
		} else {
			printk(KERN_ERR "%s: Cannot provide slot space "
			       "(%dMiB required, up to %dMiB supported)\n",
			       tdev->dev.bus_id, devsize >> 20,
			       max(slotsize, extslotsize) >> 20);
			kfree(tdev);
			goto out_err;
Linus Torvalds's avatar
Linus Torvalds committed
125
		}
126 127 128 129
		tdev->resource.name = tdev->name;
		tdev->resource.flags = IORESOURCE_MEM;

		tc_device_get_irq(tdev);
130

131 132 133 134
		device_register(&tdev->dev);
		list_add_tail(&tdev->node, &tbus->devices);

out_err:
135
		iounmap(module);
Linus Torvalds's avatar
Linus Torvalds committed
136 137 138 139
	}
}

/*
140
 * The main entry.
Linus Torvalds's avatar
Linus Torvalds committed
141
 */
142
static int __init tc_init(void)
Linus Torvalds's avatar
Linus Torvalds committed
143
{
144 145
	/* Initialize the TURBOchannel bus */
	if (tc_bus_get_info(&tc_bus))
146
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
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
	INIT_LIST_HEAD(&tc_bus.devices);
	strcpy(tc_bus.dev.bus_id, "tc");
	device_register(&tc_bus.dev);

	if (tc_bus.info.slot_size) {
		unsigned int tc_clock = tc_get_speed(&tc_bus) / 100000;

		pr_info("tc: TURBOchannel rev. %d at %d.%d MHz "
			"(with%s parity)\n", tc_bus.info.revision,
			tc_clock / 10, tc_clock % 10,
			tc_bus.info.parity ? "" : "out");

		tc_bus.resource[0].start = tc_bus.slot_base;
		tc_bus.resource[0].end = tc_bus.slot_base +
					 (tc_bus.info.slot_size << 20) *
					 tc_bus.num_tcslots;
		tc_bus.resource[0].name = tc_bus.name;
		tc_bus.resource[0].flags = IORESOURCE_MEM;
		if (request_resource(&iomem_resource,
				     &tc_bus.resource[0]) < 0) {
			printk(KERN_ERR "tc: Cannot reserve resource\n");
			return 0;
		}
		if (tc_bus.ext_slot_size) {
			tc_bus.resource[1].start = tc_bus.ext_slot_base;
			tc_bus.resource[1].end = tc_bus.ext_slot_base +
						 tc_bus.ext_slot_size *
						 tc_bus.num_tcslots;
			tc_bus.resource[1].name = tc_bus.name;
			tc_bus.resource[1].flags = IORESOURCE_MEM;
			if (request_resource(&iomem_resource,
					     &tc_bus.resource[1]) < 0) {
				printk(KERN_ERR
				       "tc: Cannot reserve resource\n");
				release_resource(&tc_bus.resource[0]);
				return 0;
			}
185
		}
186 187

		tc_bus_add_devices(&tc_bus);
Linus Torvalds's avatar
Linus Torvalds committed
188
	}
189 190

	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
191 192 193
}

subsys_initcall(tc_init);