11da177e4SLinus Torvalds /* 2b454cc66SMaciej W. Rozycki * TURBOchannel bus services. 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (c) Harald Koerfgen, 1998 5b454cc66SMaciej W. Rozycki * Copyright (c) 2001, 2003, 2005, 2006 Maciej W. Rozycki 6b454cc66SMaciej W. Rozycki * Copyright (c) 2005 James Simmons 7b454cc66SMaciej W. Rozycki * 8b454cc66SMaciej W. Rozycki * This file is subject to the terms and conditions of the GNU 9b454cc66SMaciej W. Rozycki * General Public License. See the file "COPYING" in the main 10b454cc66SMaciej W. Rozycki * directory of this archive for more details. 111da177e4SLinus Torvalds */ 12b454cc66SMaciej W. Rozycki #include <linux/compiler.h> 13b454cc66SMaciej W. Rozycki #include <linux/errno.h> 141da177e4SLinus Torvalds #include <linux/init.h> 15b454cc66SMaciej W. Rozycki #include <linux/ioport.h> 161da177e4SLinus Torvalds #include <linux/kernel.h> 17b454cc66SMaciej W. Rozycki #include <linux/list.h> 181da177e4SLinus Torvalds #include <linux/module.h> 19a5fc9c0bSMaciej W. Rozycki #include <linux/string.h> 20b454cc66SMaciej W. Rozycki #include <linux/tc.h> 21a5fc9c0bSMaciej W. Rozycki #include <linux/types.h> 221da177e4SLinus Torvalds 23a5fc9c0bSMaciej W. Rozycki #include <asm/io.h> 24a5fc9c0bSMaciej W. Rozycki 25b454cc66SMaciej W. Rozycki static struct tc_bus tc_bus = { 26b454cc66SMaciej W. Rozycki .name = "TURBOchannel", 27b454cc66SMaciej W. Rozycki }; 281da177e4SLinus Torvalds 291da177e4SLinus Torvalds /* 30b454cc66SMaciej W. Rozycki * Probing for TURBOchannel modules. 311da177e4SLinus Torvalds */ 32b454cc66SMaciej W. Rozycki static void __init tc_bus_add_devices(struct tc_bus *tbus) 331da177e4SLinus Torvalds { 34b454cc66SMaciej W. Rozycki resource_size_t slotsize = tbus->info.slot_size << 20; 35b454cc66SMaciej W. Rozycki resource_size_t extslotsize = tbus->ext_slot_size; 36b454cc66SMaciej W. Rozycki resource_size_t slotaddr; 37b454cc66SMaciej W. Rozycki resource_size_t extslotaddr; 38b454cc66SMaciej W. Rozycki resource_size_t devsize; 39b454cc66SMaciej W. Rozycki void __iomem *module; 40b454cc66SMaciej W. Rozycki struct tc_dev *tdev; 411da177e4SLinus Torvalds int i, slot, err; 42a5fc9c0bSMaciej W. Rozycki u8 pattern[4]; 43b454cc66SMaciej W. Rozycki long offset; 441da177e4SLinus Torvalds 45b454cc66SMaciej W. Rozycki for (slot = 0; slot < tbus->num_tcslots; slot++) { 46b454cc66SMaciej W. Rozycki slotaddr = tbus->slot_base + slot * slotsize; 47b454cc66SMaciej W. Rozycki extslotaddr = tbus->ext_slot_base + slot * extslotsize; 48b454cc66SMaciej W. Rozycki module = ioremap_nocache(slotaddr, slotsize); 49a5fc9c0bSMaciej W. Rozycki BUG_ON(!module); 501da177e4SLinus Torvalds 51b454cc66SMaciej W. Rozycki offset = TC_OLDCARD; 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds err = 0; 54b454cc66SMaciej W. Rozycki err |= tc_preadb(pattern + 0, module + offset + TC_PATTERN0); 55b454cc66SMaciej W. Rozycki err |= tc_preadb(pattern + 1, module + offset + TC_PATTERN1); 56b454cc66SMaciej W. Rozycki err |= tc_preadb(pattern + 2, module + offset + TC_PATTERN2); 57b454cc66SMaciej W. Rozycki err |= tc_preadb(pattern + 3, module + offset + TC_PATTERN3); 58b454cc66SMaciej W. Rozycki if (err) 59b454cc66SMaciej W. Rozycki goto out_err; 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds if (pattern[0] != 0x55 || pattern[1] != 0x00 || 621da177e4SLinus Torvalds pattern[2] != 0xaa || pattern[3] != 0xff) { 63b454cc66SMaciej W. Rozycki offset = TC_NEWCARD; 641da177e4SLinus Torvalds 651da177e4SLinus Torvalds err = 0; 66b454cc66SMaciej W. Rozycki err |= tc_preadb(pattern + 0, 67b454cc66SMaciej W. Rozycki module + offset + TC_PATTERN0); 68b454cc66SMaciej W. Rozycki err |= tc_preadb(pattern + 1, 69b454cc66SMaciej W. Rozycki module + offset + TC_PATTERN1); 70b454cc66SMaciej W. Rozycki err |= tc_preadb(pattern + 2, 71b454cc66SMaciej W. Rozycki module + offset + TC_PATTERN2); 72b454cc66SMaciej W. Rozycki err |= tc_preadb(pattern + 3, 73b454cc66SMaciej W. Rozycki module + offset + TC_PATTERN3); 74b454cc66SMaciej W. Rozycki if (err) 75b454cc66SMaciej W. Rozycki goto out_err; 76a5fc9c0bSMaciej W. Rozycki } 771da177e4SLinus Torvalds 781da177e4SLinus Torvalds if (pattern[0] != 0x55 || pattern[1] != 0x00 || 79b454cc66SMaciej W. Rozycki pattern[2] != 0xaa || pattern[3] != 0xff) 80b454cc66SMaciej W. Rozycki goto out_err; 811da177e4SLinus Torvalds 82b454cc66SMaciej W. Rozycki /* Found a board, allocate it an entry in the list */ 83b454cc66SMaciej W. Rozycki tdev = kzalloc(sizeof(*tdev), GFP_KERNEL); 84b454cc66SMaciej W. Rozycki if (!tdev) { 85b454cc66SMaciej W. Rozycki printk(KERN_ERR "tc%x: unable to allocate tc_dev\n", 86b454cc66SMaciej W. Rozycki slot); 87b454cc66SMaciej W. Rozycki goto out_err; 88b454cc66SMaciej W. Rozycki } 89b454cc66SMaciej W. Rozycki sprintf(tdev->dev.bus_id, "tc%x", slot); 90b454cc66SMaciej W. Rozycki tdev->bus = tbus; 91b454cc66SMaciej W. Rozycki tdev->dev.parent = &tbus->dev; 92b454cc66SMaciej W. Rozycki tdev->dev.bus = &tc_bus_type; 93b454cc66SMaciej W. Rozycki tdev->slot = slot; 94b454cc66SMaciej W. Rozycki 951da177e4SLinus Torvalds for (i = 0; i < 8; i++) { 96b454cc66SMaciej W. Rozycki tdev->firmware[i] = 97b454cc66SMaciej W. Rozycki readb(module + offset + TC_FIRM_VER + 4 * i); 98b454cc66SMaciej W. Rozycki tdev->vendor[i] = 99b454cc66SMaciej W. Rozycki readb(module + offset + TC_VENDOR + 4 * i); 100b454cc66SMaciej W. Rozycki tdev->name[i] = 101b454cc66SMaciej W. Rozycki readb(module + offset + TC_MODULE + 4 * i); 1021da177e4SLinus Torvalds } 103b454cc66SMaciej W. Rozycki tdev->firmware[8] = 0; 104b454cc66SMaciej W. Rozycki tdev->vendor[8] = 0; 105b454cc66SMaciej W. Rozycki tdev->name[8] = 0; 106a5fc9c0bSMaciej W. Rozycki 107b454cc66SMaciej W. Rozycki pr_info("%s: %s %s %s\n", tdev->dev.bus_id, tdev->vendor, 108b454cc66SMaciej W. Rozycki tdev->name, tdev->firmware); 109b454cc66SMaciej W. Rozycki 110b454cc66SMaciej W. Rozycki devsize = readb(module + offset + TC_SLOT_SIZE); 111b454cc66SMaciej W. Rozycki devsize <<= 22; 112b454cc66SMaciej W. Rozycki if (devsize <= slotsize) { 113b454cc66SMaciej W. Rozycki tdev->resource.start = slotaddr; 114b454cc66SMaciej W. Rozycki tdev->resource.end = slotaddr + devsize - 1; 115b454cc66SMaciej W. Rozycki } else if (devsize <= extslotsize) { 116b454cc66SMaciej W. Rozycki tdev->resource.start = extslotaddr; 117b454cc66SMaciej W. Rozycki tdev->resource.end = extslotaddr + devsize - 1; 118b454cc66SMaciej W. Rozycki } else { 119b454cc66SMaciej W. Rozycki printk(KERN_ERR "%s: Cannot provide slot space " 120b454cc66SMaciej W. Rozycki "(%dMiB required, up to %dMiB supported)\n", 121b454cc66SMaciej W. Rozycki tdev->dev.bus_id, devsize >> 20, 122b454cc66SMaciej W. Rozycki max(slotsize, extslotsize) >> 20); 123b454cc66SMaciej W. Rozycki kfree(tdev); 124b454cc66SMaciej W. Rozycki goto out_err; 125b454cc66SMaciej W. Rozycki } 126b454cc66SMaciej W. Rozycki tdev->resource.name = tdev->name; 127b454cc66SMaciej W. Rozycki tdev->resource.flags = IORESOURCE_MEM; 128b454cc66SMaciej W. Rozycki 129b454cc66SMaciej W. Rozycki tc_device_get_irq(tdev); 130b454cc66SMaciej W. Rozycki 131b454cc66SMaciej W. Rozycki device_register(&tdev->dev); 132b454cc66SMaciej W. Rozycki list_add_tail(&tdev->node, &tbus->devices); 133b454cc66SMaciej W. Rozycki 134b454cc66SMaciej W. Rozycki out_err: 135a5fc9c0bSMaciej W. Rozycki iounmap(module); 1361da177e4SLinus Torvalds } 1371da177e4SLinus Torvalds } 1381da177e4SLinus Torvalds 1391da177e4SLinus Torvalds /* 140b454cc66SMaciej W. Rozycki * The main entry. 1411da177e4SLinus Torvalds */ 142778220f7SMaciej W. Rozycki static int __init tc_init(void) 1431da177e4SLinus Torvalds { 144b454cc66SMaciej W. Rozycki /* Initialize the TURBOchannel bus */ 145b454cc66SMaciej W. Rozycki if (tc_bus_get_info(&tc_bus)) 146778220f7SMaciej W. Rozycki return 0; 1471da177e4SLinus Torvalds 148b454cc66SMaciej W. Rozycki INIT_LIST_HEAD(&tc_bus.devices); 149b454cc66SMaciej W. Rozycki strcpy(tc_bus.dev.bus_id, "tc"); 150b454cc66SMaciej W. Rozycki device_register(&tc_bus.dev); 151b454cc66SMaciej W. Rozycki 152b454cc66SMaciej W. Rozycki if (tc_bus.info.slot_size) { 153b454cc66SMaciej W. Rozycki unsigned int tc_clock = tc_get_speed(&tc_bus) / 100000; 154b454cc66SMaciej W. Rozycki 155b454cc66SMaciej W. Rozycki pr_info("tc: TURBOchannel rev. %d at %d.%d MHz " 156b454cc66SMaciej W. Rozycki "(with%s parity)\n", tc_bus.info.revision, 157b454cc66SMaciej W. Rozycki tc_clock / 10, tc_clock % 10, 158b454cc66SMaciej W. Rozycki tc_bus.info.parity ? "" : "out"); 159b454cc66SMaciej W. Rozycki 160b454cc66SMaciej W. Rozycki tc_bus.resource[0].start = tc_bus.slot_base; 161b454cc66SMaciej W. Rozycki tc_bus.resource[0].end = tc_bus.slot_base + 162b454cc66SMaciej W. Rozycki (tc_bus.info.slot_size << 20) * 163*56a47da1SMaciej W. Rozycki tc_bus.num_tcslots - 1; 164b454cc66SMaciej W. Rozycki tc_bus.resource[0].name = tc_bus.name; 165b454cc66SMaciej W. Rozycki tc_bus.resource[0].flags = IORESOURCE_MEM; 166b454cc66SMaciej W. Rozycki if (request_resource(&iomem_resource, 167b454cc66SMaciej W. Rozycki &tc_bus.resource[0]) < 0) { 168b454cc66SMaciej W. Rozycki printk(KERN_ERR "tc: Cannot reserve resource\n"); 169b454cc66SMaciej W. Rozycki return 0; 170b454cc66SMaciej W. Rozycki } 171b454cc66SMaciej W. Rozycki if (tc_bus.ext_slot_size) { 172b454cc66SMaciej W. Rozycki tc_bus.resource[1].start = tc_bus.ext_slot_base; 173b454cc66SMaciej W. Rozycki tc_bus.resource[1].end = tc_bus.ext_slot_base + 174b454cc66SMaciej W. Rozycki tc_bus.ext_slot_size * 175*56a47da1SMaciej W. Rozycki tc_bus.num_tcslots - 1; 176b454cc66SMaciej W. Rozycki tc_bus.resource[1].name = tc_bus.name; 177b454cc66SMaciej W. Rozycki tc_bus.resource[1].flags = IORESOURCE_MEM; 178b454cc66SMaciej W. Rozycki if (request_resource(&iomem_resource, 179b454cc66SMaciej W. Rozycki &tc_bus.resource[1]) < 0) { 180b454cc66SMaciej W. Rozycki printk(KERN_ERR 181b454cc66SMaciej W. Rozycki "tc: Cannot reserve resource\n"); 182b454cc66SMaciej W. Rozycki release_resource(&tc_bus.resource[0]); 183b454cc66SMaciej W. Rozycki return 0; 184b454cc66SMaciej W. Rozycki } 1851da177e4SLinus Torvalds } 1861da177e4SLinus Torvalds 187b454cc66SMaciej W. Rozycki tc_bus_add_devices(&tc_bus); 1881da177e4SLinus Torvalds } 189778220f7SMaciej W. Rozycki 190778220f7SMaciej W. Rozycki return 0; 1911da177e4SLinus Torvalds } 1921da177e4SLinus Torvalds 1931da177e4SLinus Torvalds subsys_initcall(tc_init); 194