18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Code to support devices on the DIO and DIO-II bus 38c2ecf20Sopenharmony_ci * Copyright (C) 05/1998 Peter Maydell <pmaydell@chiark.greenend.org.uk> 48c2ecf20Sopenharmony_ci * Copyright (C) 2004 Jochen Friedrich <jochen@scram.de> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This code has basically these routines at the moment: 78c2ecf20Sopenharmony_ci * int dio_find(u_int deviceid) 88c2ecf20Sopenharmony_ci * Search the list of DIO devices and return the select code 98c2ecf20Sopenharmony_ci * of the next unconfigured device found that matches the given device ID. 108c2ecf20Sopenharmony_ci * Note that the deviceid parameter should be the encoded ID. 118c2ecf20Sopenharmony_ci * This means that framebuffers should pass it as 128c2ecf20Sopenharmony_ci * DIO_ENCODE_ID(DIO_ID_FBUFFER,DIO_ID2_TOPCAT) 138c2ecf20Sopenharmony_ci * (or whatever); everybody else just uses DIO_ID_FOOBAR. 148c2ecf20Sopenharmony_ci * unsigned long dio_scodetophysaddr(int scode) 158c2ecf20Sopenharmony_ci * Return the physical address corresponding to the given select code. 168c2ecf20Sopenharmony_ci * int dio_scodetoipl(int scode) 178c2ecf20Sopenharmony_ci * Every DIO card has a fixed interrupt priority level. This function 188c2ecf20Sopenharmony_ci * returns it, whatever it is. 198c2ecf20Sopenharmony_ci * const char *dio_scodetoname(int scode) 208c2ecf20Sopenharmony_ci * Return a character string describing this board [might be "" if 218c2ecf20Sopenharmony_ci * not CONFIG_DIO_CONSTANTS] 228c2ecf20Sopenharmony_ci * void dio_config_board(int scode) mark board as configured in the list 238c2ecf20Sopenharmony_ci * void dio_unconfig_board(int scode) mark board as no longer configured 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * This file is based on the way the Amiga port handles Zorro II cards, 268c2ecf20Sopenharmony_ci * although we aren't so complicated... 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ci#include <linux/module.h> 298c2ecf20Sopenharmony_ci#include <linux/string.h> 308c2ecf20Sopenharmony_ci#include <linux/types.h> 318c2ecf20Sopenharmony_ci#include <linux/kernel.h> 328c2ecf20Sopenharmony_ci#include <linux/init.h> 338c2ecf20Sopenharmony_ci#include <linux/dio.h> 348c2ecf20Sopenharmony_ci#include <linux/slab.h> /* kmalloc() */ 358c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 368c2ecf20Sopenharmony_ci#include <asm/io.h> /* readb() */ 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistruct dio_bus dio_bus = { 398c2ecf20Sopenharmony_ci .resources = { 408c2ecf20Sopenharmony_ci /* DIO range */ 418c2ecf20Sopenharmony_ci { .name = "DIO mem", .start = 0x00600000, .end = 0x007fffff }, 428c2ecf20Sopenharmony_ci /* DIO-II range */ 438c2ecf20Sopenharmony_ci { .name = "DIO-II mem", .start = 0x01000000, .end = 0x1fffffff } 448c2ecf20Sopenharmony_ci }, 458c2ecf20Sopenharmony_ci .name = "DIO bus" 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* not a real config option yet! */ 498c2ecf20Sopenharmony_ci#define CONFIG_DIO_CONSTANTS 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#ifdef CONFIG_DIO_CONSTANTS 528c2ecf20Sopenharmony_ci/* We associate each numeric ID with an appropriate descriptive string 538c2ecf20Sopenharmony_ci * using a constant array of these structs. 548c2ecf20Sopenharmony_ci * FIXME: we should be able to arrange to throw away most of the strings 558c2ecf20Sopenharmony_ci * using the initdata stuff. Then we wouldn't need to worry about 568c2ecf20Sopenharmony_ci * carrying them around... 578c2ecf20Sopenharmony_ci * I think we do this by copying them into newly kmalloc()ed memory and 588c2ecf20Sopenharmony_ci * marking the names[] array as .initdata ? 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_cistruct dioname 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci int id; 638c2ecf20Sopenharmony_ci const char *name; 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* useful macro */ 678c2ecf20Sopenharmony_ci#define DIONAME(x) { DIO_ID_##x, DIO_DESC_##x } 688c2ecf20Sopenharmony_ci#define DIOFBNAME(x) { DIO_ENCODE_ID( DIO_ID_FBUFFER, DIO_ID2_##x), DIO_DESC2_##x } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic struct dioname names[] = 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci DIONAME(DCA0), DIONAME(DCA0REM), DIONAME(DCA1), DIONAME(DCA1REM), 738c2ecf20Sopenharmony_ci DIONAME(DCM), DIONAME(DCMREM), 748c2ecf20Sopenharmony_ci DIONAME(LAN), 758c2ecf20Sopenharmony_ci DIONAME(FHPIB), DIONAME(NHPIB), 768c2ecf20Sopenharmony_ci DIONAME(SCSI0), DIONAME(SCSI1), DIONAME(SCSI2), DIONAME(SCSI3), 778c2ecf20Sopenharmony_ci DIONAME(FBUFFER), 788c2ecf20Sopenharmony_ci DIONAME(PARALLEL), DIONAME(VME), DIONAME(DCL), DIONAME(DCLREM), 798c2ecf20Sopenharmony_ci DIONAME(MISC0), DIONAME(MISC1), DIONAME(MISC2), DIONAME(MISC3), 808c2ecf20Sopenharmony_ci DIONAME(MISC4), DIONAME(MISC5), DIONAME(MISC6), DIONAME(MISC7), 818c2ecf20Sopenharmony_ci DIONAME(MISC8), DIONAME(MISC9), DIONAME(MISC10), DIONAME(MISC11), 828c2ecf20Sopenharmony_ci DIONAME(MISC12), DIONAME(MISC13), 838c2ecf20Sopenharmony_ci DIOFBNAME(GATORBOX), DIOFBNAME(TOPCAT), DIOFBNAME(RENAISSANCE), 848c2ecf20Sopenharmony_ci DIOFBNAME(LRCATSEYE), DIOFBNAME(HRCCATSEYE), DIOFBNAME(HRMCATSEYE), 858c2ecf20Sopenharmony_ci DIOFBNAME(DAVINCI), DIOFBNAME(XXXCATSEYE), DIOFBNAME(HYPERION), 868c2ecf20Sopenharmony_ci DIOFBNAME(XGENESIS), DIOFBNAME(TIGER), DIOFBNAME(YGENESIS) 878c2ecf20Sopenharmony_ci}; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci#undef DIONAME 908c2ecf20Sopenharmony_ci#undef DIOFBNAME 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic const char unknowndioname[] 938c2ecf20Sopenharmony_ci = "unknown DIO board, please email linux-m68k@lists.linux-m68k.org"; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic const char *dio_getname(int id) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci /* return pointer to a constant string describing the board with given ID */ 988c2ecf20Sopenharmony_ci unsigned int i; 998c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(names); i++) 1008c2ecf20Sopenharmony_ci if (names[i].id == id) 1018c2ecf20Sopenharmony_ci return names[i].name; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return unknowndioname; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci#else 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic char dio_no_name[] = { 0 }; 1098c2ecf20Sopenharmony_ci#define dio_getname(_id) (dio_no_name) 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci#endif /* CONFIG_DIO_CONSTANTS */ 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic void dio_dev_release(struct device *dev) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct dio_dev *ddev = container_of(dev, typeof(struct dio_dev), dev); 1168c2ecf20Sopenharmony_ci kfree(ddev); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ciint __init dio_find(int deviceid) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci /* Called to find a DIO device before the full bus scan has run. 1228c2ecf20Sopenharmony_ci * Only used by the console driver. 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_ci int scode, id; 1258c2ecf20Sopenharmony_ci u_char prid, secid, i; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci for (scode = 0; scode < DIO_SCMAX; scode++) { 1288c2ecf20Sopenharmony_ci void *va; 1298c2ecf20Sopenharmony_ci unsigned long pa; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (DIO_SCINHOLE(scode)) 1328c2ecf20Sopenharmony_ci continue; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci pa = dio_scodetophysaddr(scode); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (!pa) 1378c2ecf20Sopenharmony_ci continue; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (scode < DIOII_SCBASE) 1408c2ecf20Sopenharmony_ci va = (void *)(pa + DIO_VIRADDRBASE); 1418c2ecf20Sopenharmony_ci else 1428c2ecf20Sopenharmony_ci va = ioremap(pa, PAGE_SIZE); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (copy_from_kernel_nofault(&i, 1458c2ecf20Sopenharmony_ci (unsigned char *)va + DIO_IDOFF, 1)) { 1468c2ecf20Sopenharmony_ci if (scode >= DIOII_SCBASE) 1478c2ecf20Sopenharmony_ci iounmap(va); 1488c2ecf20Sopenharmony_ci continue; /* no board present at that select code */ 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci prid = DIO_ID(va); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (DIO_NEEDSSECID(prid)) { 1548c2ecf20Sopenharmony_ci secid = DIO_SECID(va); 1558c2ecf20Sopenharmony_ci id = DIO_ENCODE_ID(prid, secid); 1568c2ecf20Sopenharmony_ci } else 1578c2ecf20Sopenharmony_ci id = prid; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (id == deviceid) { 1608c2ecf20Sopenharmony_ci if (scode >= DIOII_SCBASE) 1618c2ecf20Sopenharmony_ci iounmap(va); 1628c2ecf20Sopenharmony_ci return scode; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return -1; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci/* This is the function that scans the DIO space and works out what 1708c2ecf20Sopenharmony_ci * hardware is actually present. 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_cistatic int __init dio_init(void) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci int scode; 1758c2ecf20Sopenharmony_ci int i; 1768c2ecf20Sopenharmony_ci struct dio_dev *dev; 1778c2ecf20Sopenharmony_ci int error; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (!MACH_IS_HP300) 1808c2ecf20Sopenharmony_ci return 0; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci printk(KERN_INFO "Scanning for DIO devices...\n"); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* Initialize the DIO bus */ 1858c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dio_bus.devices); 1868c2ecf20Sopenharmony_ci dev_set_name(&dio_bus.dev, "dio"); 1878c2ecf20Sopenharmony_ci error = device_register(&dio_bus.dev); 1888c2ecf20Sopenharmony_ci if (error) { 1898c2ecf20Sopenharmony_ci pr_err("DIO: Error registering dio_bus\n"); 1908c2ecf20Sopenharmony_ci return error; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* Request all resources */ 1948c2ecf20Sopenharmony_ci dio_bus.num_resources = (hp300_model == HP_320 ? 1 : 2); 1958c2ecf20Sopenharmony_ci for (i = 0; i < dio_bus.num_resources; i++) 1968c2ecf20Sopenharmony_ci request_resource(&iomem_resource, &dio_bus.resources[i]); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* Register all devices */ 1998c2ecf20Sopenharmony_ci for (scode = 0; scode < DIO_SCMAX; ++scode) 2008c2ecf20Sopenharmony_ci { 2018c2ecf20Sopenharmony_ci u_char prid, secid = 0; /* primary, secondary ID bytes */ 2028c2ecf20Sopenharmony_ci u_char *va; 2038c2ecf20Sopenharmony_ci unsigned long pa; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (DIO_SCINHOLE(scode)) 2068c2ecf20Sopenharmony_ci continue; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci pa = dio_scodetophysaddr(scode); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (!pa) 2118c2ecf20Sopenharmony_ci continue; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (scode < DIOII_SCBASE) 2148c2ecf20Sopenharmony_ci va = (void *)(pa + DIO_VIRADDRBASE); 2158c2ecf20Sopenharmony_ci else 2168c2ecf20Sopenharmony_ci va = ioremap(pa, PAGE_SIZE); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (copy_from_kernel_nofault(&i, 2198c2ecf20Sopenharmony_ci (unsigned char *)va + DIO_IDOFF, 1)) { 2208c2ecf20Sopenharmony_ci if (scode >= DIOII_SCBASE) 2218c2ecf20Sopenharmony_ci iounmap(va); 2228c2ecf20Sopenharmony_ci continue; /* no board present at that select code */ 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* Found a board, allocate it an entry in the list */ 2268c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(struct dio_dev), GFP_KERNEL); 2278c2ecf20Sopenharmony_ci if (!dev) 2288c2ecf20Sopenharmony_ci return 0; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci dev->bus = &dio_bus; 2318c2ecf20Sopenharmony_ci dev->dev.parent = &dio_bus.dev; 2328c2ecf20Sopenharmony_ci dev->dev.bus = &dio_bus_type; 2338c2ecf20Sopenharmony_ci dev->dev.release = dio_dev_release; 2348c2ecf20Sopenharmony_ci dev->scode = scode; 2358c2ecf20Sopenharmony_ci dev->resource.start = pa; 2368c2ecf20Sopenharmony_ci dev->resource.end = pa + DIO_SIZE(scode, va); 2378c2ecf20Sopenharmony_ci dev_set_name(&dev->dev, "%02x", scode); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* read the ID byte(s) and encode if necessary. */ 2408c2ecf20Sopenharmony_ci prid = DIO_ID(va); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (DIO_NEEDSSECID(prid)) { 2438c2ecf20Sopenharmony_ci secid = DIO_SECID(va); 2448c2ecf20Sopenharmony_ci dev->id = DIO_ENCODE_ID(prid, secid); 2458c2ecf20Sopenharmony_ci } else 2468c2ecf20Sopenharmony_ci dev->id = prid; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci dev->ipl = DIO_IPL(va); 2498c2ecf20Sopenharmony_ci strcpy(dev->name,dio_getname(dev->id)); 2508c2ecf20Sopenharmony_ci printk(KERN_INFO "select code %3d: ipl %d: ID %02X", dev->scode, dev->ipl, prid); 2518c2ecf20Sopenharmony_ci if (DIO_NEEDSSECID(prid)) 2528c2ecf20Sopenharmony_ci printk(":%02X", secid); 2538c2ecf20Sopenharmony_ci printk(": %s\n", dev->name); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (scode >= DIOII_SCBASE) 2568c2ecf20Sopenharmony_ci iounmap(va); 2578c2ecf20Sopenharmony_ci error = device_register(&dev->dev); 2588c2ecf20Sopenharmony_ci if (error) { 2598c2ecf20Sopenharmony_ci pr_err("DIO: Error registering device %s\n", 2608c2ecf20Sopenharmony_ci dev->name); 2618c2ecf20Sopenharmony_ci put_device(&dev->dev); 2628c2ecf20Sopenharmony_ci continue; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci error = dio_create_sysfs_dev_files(dev); 2658c2ecf20Sopenharmony_ci if (error) 2668c2ecf20Sopenharmony_ci dev_err(&dev->dev, "Error creating sysfs files\n"); 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci return 0; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cisubsys_initcall(dio_init); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci/* Bear in mind that this is called in the very early stages of initialisation 2748c2ecf20Sopenharmony_ci * in order to get the address of the serial port for the console... 2758c2ecf20Sopenharmony_ci */ 2768c2ecf20Sopenharmony_ciunsigned long dio_scodetophysaddr(int scode) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci if (scode >= DIOII_SCBASE) { 2798c2ecf20Sopenharmony_ci return (DIOII_BASE + (scode - 132) * DIOII_DEVSIZE); 2808c2ecf20Sopenharmony_ci } else if (scode > DIO_SCMAX || scode < 0) 2818c2ecf20Sopenharmony_ci return 0; 2828c2ecf20Sopenharmony_ci else if (DIO_SCINHOLE(scode)) 2838c2ecf20Sopenharmony_ci return 0; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci return (DIO_BASE + scode * DIO_DEVSIZE); 2868c2ecf20Sopenharmony_ci} 287