1/* 2 * TURBOchannel bus services. 3 * 4 * Copyright (c) Harald Koerfgen, 1998 5 * Copyright (c) 2001, 2003, 2005, 2006 Maciej W. Rozycki 6 * Copyright (c) 2005 James Simmons 7 * 8 * This file is subject to the terms and conditions of the GNU 9 * General Public License. See the file "COPYING" in the main 10 * directory of this archive for more details. 11 */ 12#include <linux/compiler.h> 13#include <linux/errno.h> 14#include <linux/init.h> 15#include <linux/ioport.h> 16#include <linux/kernel.h> 17#include <linux/list.h> 18#include <linux/module.h> 19#include <linux/slab.h> 20#include <linux/string.h> 21#include <linux/tc.h> 22#include <linux/types.h> 23 24#include <asm/io.h> 25 26static struct tc_bus tc_bus = { 27 .name = "TURBOchannel", 28}; 29 30/* 31 * Probing for TURBOchannel modules. 32 */ 33static void __init tc_bus_add_devices(struct tc_bus *tbus) 34{ 35 resource_size_t slotsize = tbus->info.slot_size << 20; 36 resource_size_t extslotsize = tbus->ext_slot_size; 37 resource_size_t slotaddr; 38 resource_size_t extslotaddr; 39 resource_size_t devsize; 40 void __iomem *module; 41 struct tc_dev *tdev; 42 int i, slot, err; 43 u8 pattern[4]; 44 long offset; 45 46 for (slot = 0; slot < tbus->num_tcslots; slot++) { 47 slotaddr = tbus->slot_base + slot * slotsize; 48 extslotaddr = tbus->ext_slot_base + slot * extslotsize; 49 module = ioremap_nocache(slotaddr, slotsize); 50 BUG_ON(!module); 51 52 offset = TC_OLDCARD; 53 54 err = 0; 55 err |= tc_preadb(pattern + 0, module + offset + TC_PATTERN0); 56 err |= tc_preadb(pattern + 1, module + offset + TC_PATTERN1); 57 err |= tc_preadb(pattern + 2, module + offset + TC_PATTERN2); 58 err |= tc_preadb(pattern + 3, module + offset + TC_PATTERN3); 59 if (err) 60 goto out_err; 61 62 if (pattern[0] != 0x55 || pattern[1] != 0x00 || 63 pattern[2] != 0xaa || pattern[3] != 0xff) { 64 offset = TC_NEWCARD; 65 66 err = 0; 67 err |= tc_preadb(pattern + 0, 68 module + offset + TC_PATTERN0); 69 err |= tc_preadb(pattern + 1, 70 module + offset + TC_PATTERN1); 71 err |= tc_preadb(pattern + 2, 72 module + offset + TC_PATTERN2); 73 err |= tc_preadb(pattern + 3, 74 module + offset + TC_PATTERN3); 75 if (err) 76 goto out_err; 77 } 78 79 if (pattern[0] != 0x55 || pattern[1] != 0x00 || 80 pattern[2] != 0xaa || pattern[3] != 0xff) 81 goto out_err; 82 83 /* Found a board, allocate it an entry in the list */ 84 tdev = kzalloc(sizeof(*tdev), GFP_KERNEL); 85 if (!tdev) { 86 pr_err("tc%x: unable to allocate tc_dev\n", slot); 87 goto out_err; 88 } 89 dev_set_name(&tdev->dev, "tc%x", slot); 90 tdev->bus = tbus; 91 tdev->dev.parent = &tbus->dev; 92 tdev->dev.bus = &tc_bus_type; 93 tdev->slot = slot; 94 95 for (i = 0; i < 8; i++) { 96 tdev->firmware[i] = 97 readb(module + offset + TC_FIRM_VER + 4 * i); 98 tdev->vendor[i] = 99 readb(module + offset + TC_VENDOR + 4 * i); 100 tdev->name[i] = 101 readb(module + offset + TC_MODULE + 4 * i); 102 } 103 tdev->firmware[8] = 0; 104 tdev->vendor[8] = 0; 105 tdev->name[8] = 0; 106 107 pr_info("%s: %s %s %s\n", dev_name(&tdev->dev), tdev->vendor, 108 tdev->name, tdev->firmware); 109 110 devsize = readb(module + offset + TC_SLOT_SIZE); 111 devsize <<= 22; 112 if (devsize <= slotsize) { 113 tdev->resource.start = slotaddr; 114 tdev->resource.end = slotaddr + devsize - 1; 115 } else if (devsize <= extslotsize) { 116 tdev->resource.start = extslotaddr; 117 tdev->resource.end = extslotaddr + devsize - 1; 118 } else { 119 pr_err("%s: Cannot provide slot space " 120 "(%ldMiB required, up to %ldMiB supported)\n", 121 dev_name(&tdev->dev), (long)(devsize >> 20), 122 (long)(max(slotsize, extslotsize) >> 20)); 123 kfree(tdev); 124 goto out_err; 125 } 126 tdev->resource.name = tdev->name; 127 tdev->resource.flags = IORESOURCE_MEM; 128 129 tc_device_get_irq(tdev); 130 131 if (device_register(&tdev->dev)) { 132 put_device(&tdev->dev); 133 goto out_err; 134 } 135 list_add_tail(&tdev->node, &tbus->devices); 136 137out_err: 138 iounmap(module); 139 } 140} 141 142/* 143 * The main entry. 144 */ 145static int __init tc_init(void) 146{ 147 /* Initialize the TURBOchannel bus */ 148 if (tc_bus_get_info(&tc_bus)) 149 goto out_err; 150 151 INIT_LIST_HEAD(&tc_bus.devices); 152 dev_set_name(&tc_bus.dev, "tc"); 153 if (device_register(&tc_bus.dev)) 154 goto out_err_device; 155 156 if (tc_bus.info.slot_size) { 157 unsigned int tc_clock = tc_get_speed(&tc_bus) / 100000; 158 159 pr_info("tc: TURBOchannel rev. %d at %d.%d MHz " 160 "(with%s parity)\n", tc_bus.info.revision, 161 tc_clock / 10, tc_clock % 10, 162 tc_bus.info.parity ? "" : "out"); 163 164 tc_bus.resource[0].start = tc_bus.slot_base; 165 tc_bus.resource[0].end = tc_bus.slot_base + 166 (tc_bus.info.slot_size << 20) * 167 tc_bus.num_tcslots - 1; 168 tc_bus.resource[0].name = tc_bus.name; 169 tc_bus.resource[0].flags = IORESOURCE_MEM; 170 if (request_resource(&iomem_resource, 171 &tc_bus.resource[0]) < 0) { 172 pr_err("tc: Cannot reserve resource\n"); 173 goto out_err_device; 174 } 175 if (tc_bus.ext_slot_size) { 176 tc_bus.resource[1].start = tc_bus.ext_slot_base; 177 tc_bus.resource[1].end = tc_bus.ext_slot_base + 178 tc_bus.ext_slot_size * 179 tc_bus.num_tcslots - 1; 180 tc_bus.resource[1].name = tc_bus.name; 181 tc_bus.resource[1].flags = IORESOURCE_MEM; 182 if (request_resource(&iomem_resource, 183 &tc_bus.resource[1]) < 0) { 184 pr_err("tc: Cannot reserve resource\n"); 185 goto out_err_resource; 186 } 187 } 188 189 tc_bus_add_devices(&tc_bus); 190 } 191 192 return 0; 193 194out_err_resource: 195 release_resource(&tc_bus.resource[0]); 196out_err_device: 197 put_device(&tc_bus.dev); 198out_err: 199 return 0; 200} 201 202subsys_initcall(tc_init); 203