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