xref: /openbmc/linux/drivers/nubus/proc.c (revision 7c0f6ba6)
11da177e4SLinus Torvalds /* drivers/nubus/proc.c: Proc FS interface for NuBus.
21da177e4SLinus Torvalds 
31da177e4SLinus Torvalds    By David Huggins-Daines <dhd@debian.org>
41da177e4SLinus Torvalds 
51da177e4SLinus Torvalds    Much code and many ideas from drivers/pci/proc.c:
61da177e4SLinus Torvalds    Copyright (c) 1997, 1998 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
71da177e4SLinus Torvalds 
81da177e4SLinus Torvalds    This is initially based on the Zorro and PCI interfaces.  However,
91da177e4SLinus Torvalds    it works somewhat differently.  The intent is to provide a
101da177e4SLinus Torvalds    structure in /proc analogous to the structure of the NuBus ROM
111da177e4SLinus Torvalds    resources.
121da177e4SLinus Torvalds 
131da177e4SLinus Torvalds    Therefore each NuBus device is in fact a directory, which may in
141da177e4SLinus Torvalds    turn contain subdirectories.  The "files" correspond to NuBus
151da177e4SLinus Torvalds    resource records.  For those types of records which we know how to
161da177e4SLinus Torvalds    convert to formats that are meaningful to userspace (mostly just
171da177e4SLinus Torvalds    icons) these files will provide "cooked" data.  Otherwise they will
181da177e4SLinus Torvalds    simply provide raw access (read-only of course) to the ROM.  */
191da177e4SLinus Torvalds 
201da177e4SLinus Torvalds #include <linux/types.h>
211da177e4SLinus Torvalds #include <linux/kernel.h>
221da177e4SLinus Torvalds #include <linux/nubus.h>
231da177e4SLinus Torvalds #include <linux/proc_fs.h>
24076ec04bSAlexey Dobriyan #include <linux/seq_file.h>
251da177e4SLinus Torvalds #include <linux/init.h>
2699ffab81SAdrian Bunk #include <linux/module.h>
2799ffab81SAdrian Bunk 
287c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
291da177e4SLinus Torvalds #include <asm/byteorder.h>
301da177e4SLinus Torvalds 
311da177e4SLinus Torvalds static int
32076ec04bSAlexey Dobriyan nubus_devices_proc_show(struct seq_file *m, void *v)
331da177e4SLinus Torvalds {
341da177e4SLinus Torvalds 	struct nubus_dev *dev = nubus_devices;
351da177e4SLinus Torvalds 
36076ec04bSAlexey Dobriyan 	while (dev) {
37076ec04bSAlexey Dobriyan 		seq_printf(m, "%x\t%04x %04x %04x %04x",
381da177e4SLinus Torvalds 			      dev->board->slot,
391da177e4SLinus Torvalds 			      dev->category,
401da177e4SLinus Torvalds 			      dev->type,
411da177e4SLinus Torvalds 			      dev->dr_sw,
421da177e4SLinus Torvalds 			      dev->dr_hw);
43076ec04bSAlexey Dobriyan 		seq_printf(m, "\t%08lx\n", dev->board->slot_addr);
441da177e4SLinus Torvalds 		dev = dev->next;
451da177e4SLinus Torvalds 	}
46076ec04bSAlexey Dobriyan 	return 0;
471da177e4SLinus Torvalds }
481da177e4SLinus Torvalds 
49076ec04bSAlexey Dobriyan static int nubus_devices_proc_open(struct inode *inode, struct file *file)
50076ec04bSAlexey Dobriyan {
51076ec04bSAlexey Dobriyan 	return single_open(file, nubus_devices_proc_show, NULL);
52076ec04bSAlexey Dobriyan }
53076ec04bSAlexey Dobriyan 
54076ec04bSAlexey Dobriyan static const struct file_operations nubus_devices_proc_fops = {
55076ec04bSAlexey Dobriyan 	.open		= nubus_devices_proc_open,
56076ec04bSAlexey Dobriyan 	.read		= seq_read,
57076ec04bSAlexey Dobriyan 	.llseek		= seq_lseek,
58076ec04bSAlexey Dobriyan 	.release	= single_release,
59076ec04bSAlexey Dobriyan };
60076ec04bSAlexey Dobriyan 
611da177e4SLinus Torvalds static struct proc_dir_entry *proc_bus_nubus_dir;
621da177e4SLinus Torvalds 
6311db656aSDavid Howells static const struct file_operations nubus_proc_subdir_fops = {
6411db656aSDavid Howells #warning Need to set some I/O handlers here
6511db656aSDavid Howells };
6611db656aSDavid Howells 
671da177e4SLinus Torvalds static void nubus_proc_subdir(struct nubus_dev* dev,
681da177e4SLinus Torvalds 			      struct proc_dir_entry* parent,
691da177e4SLinus Torvalds 			      struct nubus_dir* dir)
701da177e4SLinus Torvalds {
711da177e4SLinus Torvalds 	struct nubus_dirent ent;
721da177e4SLinus Torvalds 
731da177e4SLinus Torvalds 	/* Some of these are directories, others aren't */
741da177e4SLinus Torvalds 	while (nubus_readdir(dir, &ent) != -1) {
751da177e4SLinus Torvalds 		char name[8];
761da177e4SLinus Torvalds 		struct proc_dir_entry* e;
771da177e4SLinus Torvalds 
781da177e4SLinus Torvalds 		sprintf(name, "%x", ent.type);
7911db656aSDavid Howells 		e = proc_create(name, S_IFREG | S_IRUGO | S_IWUSR, parent,
8011db656aSDavid Howells 				&nubus_proc_subdir_fops);
8111db656aSDavid Howells 		if (!e)
8211db656aSDavid Howells 			return;
831da177e4SLinus Torvalds 	}
841da177e4SLinus Torvalds }
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds /* Can't do this recursively since the root directory is structured
871da177e4SLinus Torvalds    somewhat differently from the subdirectories */
881da177e4SLinus Torvalds static void nubus_proc_populate(struct nubus_dev* dev,
891da177e4SLinus Torvalds 				struct proc_dir_entry* parent,
901da177e4SLinus Torvalds 				struct nubus_dir* root)
911da177e4SLinus Torvalds {
921da177e4SLinus Torvalds 	struct nubus_dirent ent;
931da177e4SLinus Torvalds 
941da177e4SLinus Torvalds 	/* We know these are all directories (board resource + one or
951da177e4SLinus Torvalds 	   more functional resources) */
961da177e4SLinus Torvalds 	while (nubus_readdir(root, &ent) != -1) {
971da177e4SLinus Torvalds 		char name[8];
981da177e4SLinus Torvalds 		struct proc_dir_entry* e;
991da177e4SLinus Torvalds 		struct nubus_dir dir;
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds 		sprintf(name, "%x", ent.type);
1021da177e4SLinus Torvalds 		e = proc_mkdir(name, parent);
1031da177e4SLinus Torvalds 		if (!e) return;
1041da177e4SLinus Torvalds 
1051da177e4SLinus Torvalds 		/* And descend */
1061da177e4SLinus Torvalds 		if (nubus_get_subdir(&ent, &dir) == -1) {
1071da177e4SLinus Torvalds 			/* This shouldn't happen */
1081da177e4SLinus Torvalds 			printk(KERN_ERR "NuBus root directory node %x:%x has no subdir!\n",
1091da177e4SLinus Torvalds 			       dev->board->slot, ent.type);
1101da177e4SLinus Torvalds 			continue;
1111da177e4SLinus Torvalds 		} else {
1121da177e4SLinus Torvalds 			nubus_proc_subdir(dev, e, &dir);
1131da177e4SLinus Torvalds 		}
1141da177e4SLinus Torvalds 	}
1151da177e4SLinus Torvalds }
1161da177e4SLinus Torvalds 
1171da177e4SLinus Torvalds int nubus_proc_attach_device(struct nubus_dev *dev)
1181da177e4SLinus Torvalds {
1191da177e4SLinus Torvalds 	struct proc_dir_entry *e;
1201da177e4SLinus Torvalds 	struct nubus_dir root;
1211da177e4SLinus Torvalds 	char name[8];
1221da177e4SLinus Torvalds 
1231da177e4SLinus Torvalds 	if (dev == NULL) {
1241da177e4SLinus Torvalds 		printk(KERN_ERR
1251da177e4SLinus Torvalds 		       "NULL pointer in nubus_proc_attach_device, shoot the programmer!\n");
1261da177e4SLinus Torvalds 		return -1;
1271da177e4SLinus Torvalds 	}
1281da177e4SLinus Torvalds 
1291da177e4SLinus Torvalds 	if (dev->board == NULL) {
1301da177e4SLinus Torvalds 		printk(KERN_ERR
1311da177e4SLinus Torvalds 		       "NULL pointer in nubus_proc_attach_device, shoot the programmer!\n");
1321da177e4SLinus Torvalds 		printk("dev = %p, dev->board = %p\n", dev, dev->board);
1331da177e4SLinus Torvalds 		return -1;
1341da177e4SLinus Torvalds 	}
1351da177e4SLinus Torvalds 
1361da177e4SLinus Torvalds 	/* Create a directory */
1371da177e4SLinus Torvalds 	sprintf(name, "%x", dev->board->slot);
1381da177e4SLinus Torvalds 	e = dev->procdir = proc_mkdir(name, proc_bus_nubus_dir);
1391da177e4SLinus Torvalds 	if (!e)
1401da177e4SLinus Torvalds 		return -ENOMEM;
1411da177e4SLinus Torvalds 
1421da177e4SLinus Torvalds 	/* Now recursively populate it with files */
1431da177e4SLinus Torvalds 	nubus_get_root_dir(dev->board, &root);
1441da177e4SLinus Torvalds 	nubus_proc_populate(dev, e, &root);
1451da177e4SLinus Torvalds 
1461da177e4SLinus Torvalds 	return 0;
1471da177e4SLinus Torvalds }
14899ffab81SAdrian Bunk EXPORT_SYMBOL(nubus_proc_attach_device);
1491da177e4SLinus Torvalds 
15011db656aSDavid Howells /*
15111db656aSDavid Howells  * /proc/nubus stuff
15211db656aSDavid Howells  */
15311db656aSDavid Howells static int nubus_proc_show(struct seq_file *m, void *v)
15411db656aSDavid Howells {
15511db656aSDavid Howells 	const struct nubus_board *board = v;
15611db656aSDavid Howells 
15711db656aSDavid Howells 	/* Display header on line 1 */
15811db656aSDavid Howells 	if (v == SEQ_START_TOKEN)
15911db656aSDavid Howells 		seq_puts(m, "Nubus devices found:\n");
16011db656aSDavid Howells 	else
16111db656aSDavid Howells 		seq_printf(m, "Slot %X: %s\n", board->slot, board->name);
16211db656aSDavid Howells 	return 0;
16311db656aSDavid Howells }
16411db656aSDavid Howells 
16511db656aSDavid Howells static void *nubus_proc_start(struct seq_file *m, loff_t *_pos)
16611db656aSDavid Howells {
16711db656aSDavid Howells 	struct nubus_board *board;
16811db656aSDavid Howells 	unsigned pos;
16911db656aSDavid Howells 
17011db656aSDavid Howells 	if (*_pos > LONG_MAX)
17111db656aSDavid Howells 		return NULL;
17211db656aSDavid Howells 	pos = *_pos;
17311db656aSDavid Howells 	if (pos == 0)
17411db656aSDavid Howells 		return SEQ_START_TOKEN;
17511db656aSDavid Howells 	for (board = nubus_boards; board; board = board->next)
17611db656aSDavid Howells 		if (--pos == 0)
17711db656aSDavid Howells 			break;
17811db656aSDavid Howells 	return board;
17911db656aSDavid Howells }
18011db656aSDavid Howells 
18111db656aSDavid Howells static void *nubus_proc_next(struct seq_file *p, void *v, loff_t *_pos)
18211db656aSDavid Howells {
18311db656aSDavid Howells 	/* Walk the list of NuBus boards */
18411db656aSDavid Howells 	struct nubus_board *board = v;
18511db656aSDavid Howells 
18611db656aSDavid Howells 	++*_pos;
18711db656aSDavid Howells 	if (v == SEQ_START_TOKEN)
18811db656aSDavid Howells 		board = nubus_boards;
18911db656aSDavid Howells 	else if (board)
19011db656aSDavid Howells 		board = board->next;
19111db656aSDavid Howells 	return board;
19211db656aSDavid Howells }
19311db656aSDavid Howells 
19411db656aSDavid Howells static void nubus_proc_stop(struct seq_file *p, void *v)
19511db656aSDavid Howells {
19611db656aSDavid Howells }
19711db656aSDavid Howells 
19811db656aSDavid Howells static const struct seq_operations nubus_proc_seqops = {
19911db656aSDavid Howells 	.start	= nubus_proc_start,
20011db656aSDavid Howells 	.next	= nubus_proc_next,
20111db656aSDavid Howells 	.stop	= nubus_proc_stop,
20211db656aSDavid Howells 	.show	= nubus_proc_show,
20311db656aSDavid Howells };
20411db656aSDavid Howells 
20511db656aSDavid Howells static int nubus_proc_open(struct inode *inode, struct file *file)
20611db656aSDavid Howells {
20711db656aSDavid Howells 	return seq_open(file, &nubus_proc_seqops);
20811db656aSDavid Howells }
20911db656aSDavid Howells 
21011db656aSDavid Howells static const struct file_operations nubus_proc_fops = {
21111db656aSDavid Howells 	.open		= nubus_proc_open,
21211db656aSDavid Howells 	.read		= seq_read,
21311db656aSDavid Howells 	.llseek		= seq_lseek,
21411db656aSDavid Howells 	.release	= seq_release,
21511db656aSDavid Howells };
21611db656aSDavid Howells 
2171da177e4SLinus Torvalds void __init proc_bus_nubus_add_devices(void)
2181da177e4SLinus Torvalds {
2191da177e4SLinus Torvalds 	struct nubus_dev *dev;
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds 	for(dev = nubus_devices; dev; dev = dev->next)
2221da177e4SLinus Torvalds 		nubus_proc_attach_device(dev);
2231da177e4SLinus Torvalds }
2241da177e4SLinus Torvalds 
2251da177e4SLinus Torvalds void __init nubus_proc_init(void)
2261da177e4SLinus Torvalds {
22711db656aSDavid Howells 	proc_create("nubus", 0, NULL, &nubus_proc_fops);
2281da177e4SLinus Torvalds 	if (!MACH_IS_MAC)
2291da177e4SLinus Torvalds 		return;
2309c37066dSAlexey Dobriyan 	proc_bus_nubus_dir = proc_mkdir("bus/nubus", NULL);
231076ec04bSAlexey Dobriyan 	proc_create("devices", 0, proc_bus_nubus_dir, &nubus_devices_proc_fops);
2321da177e4SLinus Torvalds 	proc_bus_nubus_add_devices();
2331da177e4SLinus Torvalds }
234