162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* drivers/nubus/proc.c: Proc FS interface for NuBus. 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci By David Huggins-Daines <dhd@debian.org> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci Much code and many ideas from drivers/pci/proc.c: 762306a36Sopenharmony_ci Copyright (c) 1997, 1998 Martin Mares <mj@atrey.karlin.mff.cuni.cz> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci This is initially based on the Zorro and PCI interfaces. However, 1062306a36Sopenharmony_ci it works somewhat differently. The intent is to provide a 1162306a36Sopenharmony_ci structure in /proc analogous to the structure of the NuBus ROM 1262306a36Sopenharmony_ci resources. 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci Therefore each board function gets a directory, which may in turn 1562306a36Sopenharmony_ci contain subdirectories. Each slot resource is a file. Unrecognized 1662306a36Sopenharmony_ci resources are empty files, since every resource ID requires a special 1762306a36Sopenharmony_ci case (e.g. if the resource ID implies a directory or block, then its 1862306a36Sopenharmony_ci value has to be interpreted as a slot ROM pointer etc.). 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/types.h> 2262306a36Sopenharmony_ci#include <linux/kernel.h> 2362306a36Sopenharmony_ci#include <linux/nubus.h> 2462306a36Sopenharmony_ci#include <linux/proc_fs.h> 2562306a36Sopenharmony_ci#include <linux/seq_file.h> 2662306a36Sopenharmony_ci#include <linux/slab.h> 2762306a36Sopenharmony_ci#include <linux/init.h> 2862306a36Sopenharmony_ci#include <linux/module.h> 2962306a36Sopenharmony_ci#include <linux/uaccess.h> 3062306a36Sopenharmony_ci#include <asm/byteorder.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* 3362306a36Sopenharmony_ci * /proc/bus/nubus/devices stuff 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic int 3762306a36Sopenharmony_cinubus_devices_proc_show(struct seq_file *m, void *v) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci struct nubus_rsrc *fres; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci for_each_func_rsrc(fres) 4262306a36Sopenharmony_ci seq_printf(m, "%x\t%04x %04x %04x %04x\t%08lx\n", 4362306a36Sopenharmony_ci fres->board->slot, fres->category, fres->type, 4462306a36Sopenharmony_ci fres->dr_sw, fres->dr_hw, fres->board->slot_addr); 4562306a36Sopenharmony_ci return 0; 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic struct proc_dir_entry *proc_bus_nubus_dir; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* 5162306a36Sopenharmony_ci * /proc/bus/nubus/x/ stuff 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistruct proc_dir_entry *nubus_proc_add_board(struct nubus_board *board) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci char name[2]; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci if (!proc_bus_nubus_dir || !nubus_populate_procfs) 5962306a36Sopenharmony_ci return NULL; 6062306a36Sopenharmony_ci snprintf(name, sizeof(name), "%x", board->slot); 6162306a36Sopenharmony_ci return proc_mkdir(name, proc_bus_nubus_dir); 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* The PDE private data for any directory under /proc/bus/nubus/x/ 6562306a36Sopenharmony_ci * is the bytelanes value for the board in slot x. 6662306a36Sopenharmony_ci */ 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistruct proc_dir_entry *nubus_proc_add_rsrc_dir(struct proc_dir_entry *procdir, 6962306a36Sopenharmony_ci const struct nubus_dirent *ent, 7062306a36Sopenharmony_ci struct nubus_board *board) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci char name[9]; 7362306a36Sopenharmony_ci int lanes = board->lanes; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (!procdir || !nubus_populate_procfs) 7662306a36Sopenharmony_ci return NULL; 7762306a36Sopenharmony_ci snprintf(name, sizeof(name), "%x", ent->type); 7862306a36Sopenharmony_ci remove_proc_subtree(name, procdir); 7962306a36Sopenharmony_ci return proc_mkdir_data(name, 0555, procdir, (void *)lanes); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* The PDE private data for a file under /proc/bus/nubus/x/ is a pointer to 8362306a36Sopenharmony_ci * an instance of the following structure, which gives the location and size 8462306a36Sopenharmony_ci * of the resource data in the slot ROM. For slot resources which hold only a 8562306a36Sopenharmony_ci * small integer, this integer value is stored directly and size is set to 0. 8662306a36Sopenharmony_ci * A NULL private data pointer indicates an unrecognized resource. 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistruct nubus_proc_pde_data { 9062306a36Sopenharmony_ci unsigned char *res_ptr; 9162306a36Sopenharmony_ci unsigned int res_size; 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic struct nubus_proc_pde_data * 9562306a36Sopenharmony_cinubus_proc_alloc_pde_data(unsigned char *ptr, unsigned int size) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci struct nubus_proc_pde_data *pded; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci pded = kmalloc(sizeof(*pded), GFP_KERNEL); 10062306a36Sopenharmony_ci if (!pded) 10162306a36Sopenharmony_ci return NULL; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci pded->res_ptr = ptr; 10462306a36Sopenharmony_ci pded->res_size = size; 10562306a36Sopenharmony_ci return pded; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic int nubus_proc_rsrc_show(struct seq_file *m, void *v) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct inode *inode = m->private; 11162306a36Sopenharmony_ci struct nubus_proc_pde_data *pded; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci pded = pde_data(inode); 11462306a36Sopenharmony_ci if (!pded) 11562306a36Sopenharmony_ci return 0; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (pded->res_size > m->size) 11862306a36Sopenharmony_ci return -EFBIG; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (pded->res_size) { 12162306a36Sopenharmony_ci int lanes = (int)proc_get_parent_data(inode); 12262306a36Sopenharmony_ci struct nubus_dirent ent; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (!lanes) 12562306a36Sopenharmony_ci return 0; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci ent.mask = lanes; 12862306a36Sopenharmony_ci ent.base = pded->res_ptr; 12962306a36Sopenharmony_ci ent.data = 0; 13062306a36Sopenharmony_ci nubus_seq_write_rsrc_mem(m, &ent, pded->res_size); 13162306a36Sopenharmony_ci } else { 13262306a36Sopenharmony_ci unsigned int data = (unsigned int)pded->res_ptr; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci seq_putc(m, data >> 16); 13562306a36Sopenharmony_ci seq_putc(m, data >> 8); 13662306a36Sopenharmony_ci seq_putc(m, data >> 0); 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic int nubus_rsrc_proc_open(struct inode *inode, struct file *file) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci return single_open(file, nubus_proc_rsrc_show, inode); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic const struct proc_ops nubus_rsrc_proc_ops = { 14762306a36Sopenharmony_ci .proc_open = nubus_rsrc_proc_open, 14862306a36Sopenharmony_ci .proc_read = seq_read, 14962306a36Sopenharmony_ci .proc_lseek = seq_lseek, 15062306a36Sopenharmony_ci .proc_release = single_release, 15162306a36Sopenharmony_ci}; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_civoid nubus_proc_add_rsrc_mem(struct proc_dir_entry *procdir, 15462306a36Sopenharmony_ci const struct nubus_dirent *ent, 15562306a36Sopenharmony_ci unsigned int size) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci char name[9]; 15862306a36Sopenharmony_ci struct nubus_proc_pde_data *pded; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (!procdir || !nubus_populate_procfs) 16162306a36Sopenharmony_ci return; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci snprintf(name, sizeof(name), "%x", ent->type); 16462306a36Sopenharmony_ci if (size) 16562306a36Sopenharmony_ci pded = nubus_proc_alloc_pde_data(nubus_dirptr(ent), size); 16662306a36Sopenharmony_ci else 16762306a36Sopenharmony_ci pded = NULL; 16862306a36Sopenharmony_ci remove_proc_subtree(name, procdir); 16962306a36Sopenharmony_ci proc_create_data(name, S_IFREG | 0444, procdir, 17062306a36Sopenharmony_ci &nubus_rsrc_proc_ops, pded); 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_civoid nubus_proc_add_rsrc(struct proc_dir_entry *procdir, 17462306a36Sopenharmony_ci const struct nubus_dirent *ent) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci char name[9]; 17762306a36Sopenharmony_ci unsigned char *data = (unsigned char *)ent->data; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (!procdir || !nubus_populate_procfs) 18062306a36Sopenharmony_ci return; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci snprintf(name, sizeof(name), "%x", ent->type); 18362306a36Sopenharmony_ci remove_proc_subtree(name, procdir); 18462306a36Sopenharmony_ci proc_create_data(name, S_IFREG | 0444, procdir, 18562306a36Sopenharmony_ci &nubus_rsrc_proc_ops, 18662306a36Sopenharmony_ci nubus_proc_alloc_pde_data(data, 0)); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci/* 19062306a36Sopenharmony_ci * /proc/nubus stuff 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_civoid __init nubus_proc_init(void) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci proc_create_single("nubus", 0, NULL, nubus_proc_show); 19662306a36Sopenharmony_ci proc_bus_nubus_dir = proc_mkdir("bus/nubus", NULL); 19762306a36Sopenharmony_ci if (!proc_bus_nubus_dir) 19862306a36Sopenharmony_ci return; 19962306a36Sopenharmony_ci proc_create_single("devices", 0, proc_bus_nubus_dir, 20062306a36Sopenharmony_ci nubus_devices_proc_show); 20162306a36Sopenharmony_ci} 202