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