xref: /kernel/linux/linux-5.10/drivers/nubus/proc.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* drivers/nubus/proc.c: Proc FS interface for NuBus.
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci   By David Huggins-Daines <dhd@debian.org>
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci   Much code and many ideas from drivers/pci/proc.c:
78c2ecf20Sopenharmony_ci   Copyright (c) 1997, 1998 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci   This is initially based on the Zorro and PCI interfaces.  However,
108c2ecf20Sopenharmony_ci   it works somewhat differently.  The intent is to provide a
118c2ecf20Sopenharmony_ci   structure in /proc analogous to the structure of the NuBus ROM
128c2ecf20Sopenharmony_ci   resources.
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci   Therefore each board function gets a directory, which may in turn
158c2ecf20Sopenharmony_ci   contain subdirectories.  Each slot resource is a file.  Unrecognized
168c2ecf20Sopenharmony_ci   resources are empty files, since every resource ID requires a special
178c2ecf20Sopenharmony_ci   case (e.g. if the resource ID implies a directory or block, then its
188c2ecf20Sopenharmony_ci   value has to be interpreted as a slot ROM pointer etc.).
198c2ecf20Sopenharmony_ci */
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <linux/types.h>
228c2ecf20Sopenharmony_ci#include <linux/kernel.h>
238c2ecf20Sopenharmony_ci#include <linux/nubus.h>
248c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
258c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
268c2ecf20Sopenharmony_ci#include <linux/slab.h>
278c2ecf20Sopenharmony_ci#include <linux/init.h>
288c2ecf20Sopenharmony_ci#include <linux/module.h>
298c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
308c2ecf20Sopenharmony_ci#include <asm/byteorder.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/*
338c2ecf20Sopenharmony_ci * /proc/bus/nubus/devices stuff
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic int
378c2ecf20Sopenharmony_cinubus_devices_proc_show(struct seq_file *m, void *v)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	struct nubus_rsrc *fres;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	for_each_func_rsrc(fres)
428c2ecf20Sopenharmony_ci		seq_printf(m, "%x\t%04x %04x %04x %04x\t%08lx\n",
438c2ecf20Sopenharmony_ci			   fres->board->slot, fres->category, fres->type,
448c2ecf20Sopenharmony_ci			   fres->dr_sw, fres->dr_hw, fres->board->slot_addr);
458c2ecf20Sopenharmony_ci	return 0;
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic struct proc_dir_entry *proc_bus_nubus_dir;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/*
518c2ecf20Sopenharmony_ci * /proc/bus/nubus/x/ stuff
528c2ecf20Sopenharmony_ci */
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistruct proc_dir_entry *nubus_proc_add_board(struct nubus_board *board)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	char name[2];
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	if (!proc_bus_nubus_dir)
598c2ecf20Sopenharmony_ci		return NULL;
608c2ecf20Sopenharmony_ci	snprintf(name, sizeof(name), "%x", board->slot);
618c2ecf20Sopenharmony_ci	return proc_mkdir(name, proc_bus_nubus_dir);
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/* The PDE private data for any directory under /proc/bus/nubus/x/
658c2ecf20Sopenharmony_ci * is the bytelanes value for the board in slot x.
668c2ecf20Sopenharmony_ci */
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistruct proc_dir_entry *nubus_proc_add_rsrc_dir(struct proc_dir_entry *procdir,
698c2ecf20Sopenharmony_ci					       const struct nubus_dirent *ent,
708c2ecf20Sopenharmony_ci					       struct nubus_board *board)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	char name[9];
738c2ecf20Sopenharmony_ci	int lanes = board->lanes;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (!procdir)
768c2ecf20Sopenharmony_ci		return NULL;
778c2ecf20Sopenharmony_ci	snprintf(name, sizeof(name), "%x", ent->type);
788c2ecf20Sopenharmony_ci	return proc_mkdir_data(name, 0555, procdir, (void *)lanes);
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/* The PDE private data for a file under /proc/bus/nubus/x/ is a pointer to
828c2ecf20Sopenharmony_ci * an instance of the following structure, which gives the location and size
838c2ecf20Sopenharmony_ci * of the resource data in the slot ROM. For slot resources which hold only a
848c2ecf20Sopenharmony_ci * small integer, this integer value is stored directly and size is set to 0.
858c2ecf20Sopenharmony_ci * A NULL private data pointer indicates an unrecognized resource.
868c2ecf20Sopenharmony_ci */
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistruct nubus_proc_pde_data {
898c2ecf20Sopenharmony_ci	unsigned char *res_ptr;
908c2ecf20Sopenharmony_ci	unsigned int res_size;
918c2ecf20Sopenharmony_ci};
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic struct nubus_proc_pde_data *
948c2ecf20Sopenharmony_cinubus_proc_alloc_pde_data(unsigned char *ptr, unsigned int size)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	struct nubus_proc_pde_data *pde_data;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	pde_data = kmalloc(sizeof(*pde_data), GFP_KERNEL);
998c2ecf20Sopenharmony_ci	if (!pde_data)
1008c2ecf20Sopenharmony_ci		return NULL;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	pde_data->res_ptr = ptr;
1038c2ecf20Sopenharmony_ci	pde_data->res_size = size;
1048c2ecf20Sopenharmony_ci	return pde_data;
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic int nubus_proc_rsrc_show(struct seq_file *m, void *v)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	struct inode *inode = m->private;
1108c2ecf20Sopenharmony_ci	struct nubus_proc_pde_data *pde_data;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	pde_data = PDE_DATA(inode);
1138c2ecf20Sopenharmony_ci	if (!pde_data)
1148c2ecf20Sopenharmony_ci		return 0;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	if (pde_data->res_size > m->size)
1178c2ecf20Sopenharmony_ci		return -EFBIG;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	if (pde_data->res_size) {
1208c2ecf20Sopenharmony_ci		int lanes = (int)proc_get_parent_data(inode);
1218c2ecf20Sopenharmony_ci		struct nubus_dirent ent;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci		if (!lanes)
1248c2ecf20Sopenharmony_ci			return 0;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci		ent.mask = lanes;
1278c2ecf20Sopenharmony_ci		ent.base = pde_data->res_ptr;
1288c2ecf20Sopenharmony_ci		ent.data = 0;
1298c2ecf20Sopenharmony_ci		nubus_seq_write_rsrc_mem(m, &ent, pde_data->res_size);
1308c2ecf20Sopenharmony_ci	} else {
1318c2ecf20Sopenharmony_ci		unsigned int data = (unsigned int)pde_data->res_ptr;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci		seq_putc(m, data >> 16);
1348c2ecf20Sopenharmony_ci		seq_putc(m, data >> 8);
1358c2ecf20Sopenharmony_ci		seq_putc(m, data >> 0);
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci	return 0;
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic int nubus_rsrc_proc_open(struct inode *inode, struct file *file)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	return single_open(file, nubus_proc_rsrc_show, inode);
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic const struct proc_ops nubus_rsrc_proc_ops = {
1468c2ecf20Sopenharmony_ci	.proc_open	= nubus_rsrc_proc_open,
1478c2ecf20Sopenharmony_ci	.proc_read	= seq_read,
1488c2ecf20Sopenharmony_ci	.proc_lseek	= seq_lseek,
1498c2ecf20Sopenharmony_ci	.proc_release	= single_release,
1508c2ecf20Sopenharmony_ci};
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_civoid nubus_proc_add_rsrc_mem(struct proc_dir_entry *procdir,
1538c2ecf20Sopenharmony_ci			     const struct nubus_dirent *ent,
1548c2ecf20Sopenharmony_ci			     unsigned int size)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	char name[9];
1578c2ecf20Sopenharmony_ci	struct nubus_proc_pde_data *pde_data;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (!procdir)
1608c2ecf20Sopenharmony_ci		return;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	snprintf(name, sizeof(name), "%x", ent->type);
1638c2ecf20Sopenharmony_ci	if (size)
1648c2ecf20Sopenharmony_ci		pde_data = nubus_proc_alloc_pde_data(nubus_dirptr(ent), size);
1658c2ecf20Sopenharmony_ci	else
1668c2ecf20Sopenharmony_ci		pde_data = NULL;
1678c2ecf20Sopenharmony_ci	proc_create_data(name, S_IFREG | 0444, procdir,
1688c2ecf20Sopenharmony_ci			 &nubus_rsrc_proc_ops, pde_data);
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_civoid nubus_proc_add_rsrc(struct proc_dir_entry *procdir,
1728c2ecf20Sopenharmony_ci			 const struct nubus_dirent *ent)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	char name[9];
1758c2ecf20Sopenharmony_ci	unsigned char *data = (unsigned char *)ent->data;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (!procdir)
1788c2ecf20Sopenharmony_ci		return;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	snprintf(name, sizeof(name), "%x", ent->type);
1818c2ecf20Sopenharmony_ci	proc_create_data(name, S_IFREG | 0444, procdir,
1828c2ecf20Sopenharmony_ci			 &nubus_rsrc_proc_ops,
1838c2ecf20Sopenharmony_ci			 nubus_proc_alloc_pde_data(data, 0));
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci/*
1878c2ecf20Sopenharmony_ci * /proc/nubus stuff
1888c2ecf20Sopenharmony_ci */
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_civoid __init nubus_proc_init(void)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	proc_create_single("nubus", 0, NULL, nubus_proc_show);
1938c2ecf20Sopenharmony_ci	proc_bus_nubus_dir = proc_mkdir("bus/nubus", NULL);
1948c2ecf20Sopenharmony_ci	if (!proc_bus_nubus_dir)
1958c2ecf20Sopenharmony_ci		return;
1968c2ecf20Sopenharmony_ci	proc_create_single("devices", 0, proc_bus_nubus_dir,
1978c2ecf20Sopenharmony_ci			nubus_devices_proc_show);
1988c2ecf20Sopenharmony_ci}
199