Blame view

drivers/dio/dio.c 8.37 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
  /* Code to support devices on the DIO and DIO-II bus
   * Copyright (C) 05/1998 Peter Maydell <pmaydell@chiark.greenend.org.uk>
   * Copyright (C) 2004 Jochen Friedrich <jochen@scram.de>
   * 
   * This code has basically these routines at the moment:
   * int dio_find(u_int deviceid)
   *    Search the list of DIO devices and return the select code
   *    of the next unconfigured device found that matches the given device ID.
   *    Note that the deviceid parameter should be the encoded ID.
   *    This means that framebuffers should pass it as 
   *    DIO_ENCODE_ID(DIO_ID_FBUFFER,DIO_ID2_TOPCAT)
   *    (or whatever); everybody else just uses DIO_ID_FOOBAR.
   * unsigned long dio_scodetophysaddr(int scode)
   *    Return the physical address corresponding to the given select code.
   * int dio_scodetoipl(int scode)
   *    Every DIO card has a fixed interrupt priority level. This function 
   *    returns it, whatever it is.
   * const char *dio_scodetoname(int scode)
   *    Return a character string describing this board [might be "" if 
   *    not CONFIG_DIO_CONSTANTS]
   * void dio_config_board(int scode)     mark board as configured in the list
   * void dio_unconfig_board(int scode)   mark board as no longer configured
   *
   * This file is based on the way the Amiga port handles Zorro II cards, 
   * although we aren't so complicated...
   */
  #include <linux/module.h>
  #include <linux/string.h>
  #include <linux/types.h>
  #include <linux/kernel.h>
  #include <linux/init.h>
  #include <linux/dio.h>
  #include <linux/slab.h>                         /* kmalloc() */
  #include <asm/uaccess.h>
  #include <asm/io.h>                             /* readb() */
  
  struct dio_bus dio_bus = {
  	.resources = {
  		/* DIO range */
  		{ .name = "DIO mem", .start = 0x00600000, .end = 0x007fffff },
  		/* DIO-II range */
  		{ .name = "DIO-II mem", .start = 0x01000000, .end = 0x1fffffff }
  	},
  	.name = "DIO bus"
  };
  
  /* not a real config option yet! */
  #define CONFIG_DIO_CONSTANTS
  
  #ifdef CONFIG_DIO_CONSTANTS
  /* We associate each numeric ID with an appropriate descriptive string
   * using a constant array of these structs.
   * FIXME: we should be able to arrange to throw away most of the strings
   * using the initdata stuff. Then we wouldn't need to worry about 
   * carrying them around...
   * I think we do this by copying them into newly kmalloc()ed memory and 
   * marking the names[] array as .initdata ?
   */
  struct dioname
  {
          int id;
          const char *name;
  };
  
  /* useful macro */
  #define DIONAME(x) { DIO_ID_##x, DIO_DESC_##x }
  #define DIOFBNAME(x) { DIO_ENCODE_ID( DIO_ID_FBUFFER, DIO_ID2_##x), DIO_DESC2_##x }
  
  static struct dioname names[] = 
  {
          DIONAME(DCA0), DIONAME(DCA0REM), DIONAME(DCA1), DIONAME(DCA1REM),
          DIONAME(DCM), DIONAME(DCMREM),
          DIONAME(LAN),
          DIONAME(FHPIB), DIONAME(NHPIB),
          DIONAME(SCSI0), DIONAME(SCSI1), DIONAME(SCSI2), DIONAME(SCSI3),
          DIONAME(FBUFFER),
          DIONAME(PARALLEL), DIONAME(VME), DIONAME(DCL), DIONAME(DCLREM),
          DIONAME(MISC0), DIONAME(MISC1), DIONAME(MISC2), DIONAME(MISC3),
          DIONAME(MISC4), DIONAME(MISC5), DIONAME(MISC6), DIONAME(MISC7),
          DIONAME(MISC8), DIONAME(MISC9), DIONAME(MISC10), DIONAME(MISC11), 
          DIONAME(MISC12), DIONAME(MISC13),
          DIOFBNAME(GATORBOX), DIOFBNAME(TOPCAT), DIOFBNAME(RENAISSANCE),
          DIOFBNAME(LRCATSEYE), DIOFBNAME(HRCCATSEYE), DIOFBNAME(HRMCATSEYE),
          DIOFBNAME(DAVINCI), DIOFBNAME(XXXCATSEYE), DIOFBNAME(HYPERION),
          DIOFBNAME(XGENESIS), DIOFBNAME(TIGER), DIOFBNAME(YGENESIS)   
  };
  
  #undef DIONAME
  #undef DIOFBNAME
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
91
92
93
94
95
96
  static const char *unknowndioname 
          = "unknown DIO board -- please email <linux-m68k@lists.linux-m68k.org>!";
  
  static const char *dio_getname(int id)
  {
          /* return pointer to a constant string describing the board with given ID */
  	unsigned int i;
0a8320b04   Alejandro Martinez Ruiz   dio: ARRAY_SIZE()...
97
  	for (i = 0; i < ARRAY_SIZE(names); i++)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
                  if (names[i].id == id) 
                          return names[i].name;
  
          return unknowndioname;
  }
  
  #else
  
  static char dio_no_name[] = { 0 };
  #define dio_getname(_id)	(dio_no_name)
  
  #endif /* CONFIG_DIO_CONSTANTS */
  
  int __init dio_find(int deviceid)
  {
  	/* Called to find a DIO device before the full bus scan has run.
  	 * Only used by the console driver.
  	 */
  	int scode, id;
  	u_char prid, secid, i;
  	mm_segment_t fs;
  
  	for (scode = 0; scode < DIO_SCMAX; scode++) {
  		void *va;
  		unsigned long pa;
  
                  if (DIO_SCINHOLE(scode))
                          continue;
  
                  pa = dio_scodetophysaddr(scode);
  
  		if (!pa)
  			continue;
  
  		if (scode < DIOII_SCBASE)
  			va = (void *)(pa + DIO_VIRADDRBASE);
  		else
  			va = ioremap(pa, PAGE_SIZE);
  
  		fs = get_fs();
  		set_fs(KERNEL_DS);
  
                  if (get_user(i, (unsigned char *)va + DIO_IDOFF)) {
  			set_fs(fs);
  			if (scode >= DIOII_SCBASE)
  				iounmap(va);
                          continue;             /* no board present at that select code */
  		}
  
  		set_fs(fs);
  		prid = DIO_ID(va);
  
                  if (DIO_NEEDSSECID(prid)) {
                          secid = DIO_SECID(va);
                          id = DIO_ENCODE_ID(prid, secid);
                  } else
  			id = prid;
  
  		if (id == deviceid) {
  			if (scode >= DIOII_SCBASE)
  				iounmap(va);
  			return scode;
  		}
  	}
  
  	return -1;
  }
  
  /* This is the function that scans the DIO space and works out what
   * hardware is actually present.
   */
  static int __init dio_init(void)
  {
  	int scode;
  	mm_segment_t fs;
  	int i;
  	struct dio_dev *dev;
2e4c77bea   Geert Uytterhoeven   m68k: dio - Kill ...
175
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
176
177
178
179
180
181
182
183
184
  
  	if (!MACH_IS_HP300)
  		return 0;
  
          printk(KERN_INFO "Scanning for DIO devices...
  ");
  
  	/* Initialize the DIO bus */ 
  	INIT_LIST_HEAD(&dio_bus.devices);
9591463af   Kay Sievers   dio: struct devic...
185
  	dev_set_name(&dio_bus.dev, "dio");
2e4c77bea   Geert Uytterhoeven   m68k: dio - Kill ...
186
187
188
189
190
191
  	error = device_register(&dio_bus.dev);
  	if (error) {
  		pr_err("DIO: Error registering dio_bus
  ");
  		return error;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
220
221
222
223
224
225
226
227
228
229
230
  
  	/* Request all resources */
  	dio_bus.num_resources = (hp300_model == HP_320 ? 1 : 2);
  	for (i = 0; i < dio_bus.num_resources; i++)
  		request_resource(&iomem_resource, &dio_bus.resources[i]);
  
  	/* Register all devices */
          for (scode = 0; scode < DIO_SCMAX; ++scode)
          {
                  u_char prid, secid = 0;        /* primary, secondary ID bytes */
                  u_char *va;
  		unsigned long pa;
                  
                  if (DIO_SCINHOLE(scode))
                          continue;
  
  		pa = dio_scodetophysaddr(scode);
  
  		if (!pa)
  			continue;
  
  		if (scode < DIOII_SCBASE)
  			va = (void *)(pa + DIO_VIRADDRBASE);
  		else
  			va = ioremap(pa, PAGE_SIZE);
  
  		fs = get_fs();
  		set_fs(KERNEL_DS);
  
                  if (get_user(i, (unsigned char *)va + DIO_IDOFF)) {
  			set_fs(fs);
  			if (scode >= DIOII_SCBASE)
  				iounmap(va);
                          continue;              /* no board present at that select code */
  		}
  
  		set_fs(fs);
  
                  /* Found a board, allocate it an entry in the list */
e66860cbd   Deepak Saxena   [PATCH] drivers/d...
231
  		dev = kzalloc(sizeof(struct dio_dev), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
233
  		if (!dev)
  			return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
234
235
236
237
238
239
  		dev->bus = &dio_bus;
  		dev->dev.parent = &dio_bus.dev;
  		dev->dev.bus = &dio_bus_type;
  		dev->scode = scode;
  		dev->resource.start = pa;
  		dev->resource.end = pa + DIO_SIZE(scode, va);
9591463af   Kay Sievers   dio: struct devic...
240
  		dev_set_name(&dev->dev, "%02x", scode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
  
                  /* read the ID byte(s) and encode if necessary. */
  		prid = DIO_ID(va);
  
                  if (DIO_NEEDSSECID(prid)) {
                          secid = DIO_SECID(va);
                          dev->id = DIO_ENCODE_ID(prid, secid);
                  } else
                          dev->id = prid;
  
                  dev->ipl = DIO_IPL(va);
                  strcpy(dev->name,dio_getname(dev->id));
                  printk(KERN_INFO "select code %3d: ipl %d: ID %02X", dev->scode, dev->ipl, prid);
                  if (DIO_NEEDSSECID(prid))
                          printk(":%02X", secid);
                  printk(": %s
  ", dev->name);
  
  		if (scode >= DIOII_SCBASE)
  			iounmap(va);
2e4c77bea   Geert Uytterhoeven   m68k: dio - Kill ...
261
262
263
264
265
266
267
268
269
270
271
  		error = device_register(&dev->dev);
  		if (error) {
  			pr_err("DIO: Error registering device %s
  ",
  			       dev->name);
  			continue;
  		}
  		error = dio_create_sysfs_dev_files(dev);
  		if (error)
  			dev_err(&dev->dev, "Error creating sysfs files
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
          }
  	return 0;
  }
  
  subsys_initcall(dio_init);
  
  /* Bear in mind that this is called in the very early stages of initialisation
   * in order to get the address of the serial port for the console...
   */
  unsigned long dio_scodetophysaddr(int scode)
  {
          if (scode >= DIOII_SCBASE) {
                  return (DIOII_BASE + (scode - 132) * DIOII_DEVSIZE);
          } else if (scode > DIO_SCMAX || scode < 0)
                  return 0;
          else if (DIO_SCINHOLE(scode))
                  return 0;
  
          return (DIO_BASE + scode * DIO_DEVSIZE);
  }