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