162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Code to support devices on the DIO and DIO-II bus 362306a36Sopenharmony_ci * Copyright (C) 05/1998 Peter Maydell <pmaydell@chiark.greenend.org.uk> 462306a36Sopenharmony_ci * Copyright (C) 2004 Jochen Friedrich <jochen@scram.de> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This code has basically these routines at the moment: 762306a36Sopenharmony_ci * int dio_find(u_int deviceid) 862306a36Sopenharmony_ci * Search the list of DIO devices and return the select code 962306a36Sopenharmony_ci * of the next unconfigured device found that matches the given device ID. 1062306a36Sopenharmony_ci * Note that the deviceid parameter should be the encoded ID. 1162306a36Sopenharmony_ci * This means that framebuffers should pass it as 1262306a36Sopenharmony_ci * DIO_ENCODE_ID(DIO_ID_FBUFFER,DIO_ID2_TOPCAT) 1362306a36Sopenharmony_ci * (or whatever); everybody else just uses DIO_ID_FOOBAR. 1462306a36Sopenharmony_ci * unsigned long dio_scodetophysaddr(int scode) 1562306a36Sopenharmony_ci * Return the physical address corresponding to the given select code. 1662306a36Sopenharmony_ci * int dio_scodetoipl(int scode) 1762306a36Sopenharmony_ci * Every DIO card has a fixed interrupt priority level. This function 1862306a36Sopenharmony_ci * returns it, whatever it is. 1962306a36Sopenharmony_ci * const char *dio_scodetoname(int scode) 2062306a36Sopenharmony_ci * Return a character string describing this board [might be "" if 2162306a36Sopenharmony_ci * not CONFIG_DIO_CONSTANTS] 2262306a36Sopenharmony_ci * void dio_config_board(int scode) mark board as configured in the list 2362306a36Sopenharmony_ci * void dio_unconfig_board(int scode) mark board as no longer configured 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * This file is based on the way the Amiga port handles Zorro II cards, 2662306a36Sopenharmony_ci * although we aren't so complicated... 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_ci#include <linux/module.h> 2962306a36Sopenharmony_ci#include <linux/string.h> 3062306a36Sopenharmony_ci#include <linux/types.h> 3162306a36Sopenharmony_ci#include <linux/kernel.h> 3262306a36Sopenharmony_ci#include <linux/init.h> 3362306a36Sopenharmony_ci#include <linux/dio.h> 3462306a36Sopenharmony_ci#include <linux/slab.h> /* kmalloc() */ 3562306a36Sopenharmony_ci#include <linux/uaccess.h> 3662306a36Sopenharmony_ci#include <linux/io.h> /* readb() */ 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistruct dio_bus dio_bus = { 3962306a36Sopenharmony_ci .resources = { 4062306a36Sopenharmony_ci /* DIO range */ 4162306a36Sopenharmony_ci { .name = "DIO mem", .start = 0x00600000, .end = 0x007fffff }, 4262306a36Sopenharmony_ci /* DIO-II range */ 4362306a36Sopenharmony_ci { .name = "DIO-II mem", .start = 0x01000000, .end = 0x1fffffff } 4462306a36Sopenharmony_ci }, 4562306a36Sopenharmony_ci .name = "DIO bus" 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* not a real config option yet! */ 4962306a36Sopenharmony_ci#define CONFIG_DIO_CONSTANTS 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#ifdef CONFIG_DIO_CONSTANTS 5262306a36Sopenharmony_ci/* We associate each numeric ID with an appropriate descriptive string 5362306a36Sopenharmony_ci * using a constant array of these structs. 5462306a36Sopenharmony_ci * FIXME: we should be able to arrange to throw away most of the strings 5562306a36Sopenharmony_ci * using the initdata stuff. Then we wouldn't need to worry about 5662306a36Sopenharmony_ci * carrying them around... 5762306a36Sopenharmony_ci * I think we do this by copying them into newly kmalloc()ed memory and 5862306a36Sopenharmony_ci * marking the names[] array as .initdata ? 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_cistruct dioname { 6162306a36Sopenharmony_ci int id; 6262306a36Sopenharmony_ci const char *name; 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* useful macro */ 6662306a36Sopenharmony_ci#define DIONAME(x) { DIO_ID_##x, DIO_DESC_##x } 6762306a36Sopenharmony_ci#define DIOFBNAME(x) { DIO_ENCODE_ID(DIO_ID_FBUFFER, DIO_ID2_##x), DIO_DESC2_##x } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic struct dioname names[] = { 7062306a36Sopenharmony_ci DIONAME(DCA0), DIONAME(DCA0REM), DIONAME(DCA1), DIONAME(DCA1REM), 7162306a36Sopenharmony_ci DIONAME(DCM), DIONAME(DCMREM), 7262306a36Sopenharmony_ci DIONAME(LAN), 7362306a36Sopenharmony_ci DIONAME(FHPIB), DIONAME(NHPIB), 7462306a36Sopenharmony_ci DIONAME(SCSI0), DIONAME(SCSI1), DIONAME(SCSI2), DIONAME(SCSI3), 7562306a36Sopenharmony_ci DIONAME(FBUFFER), 7662306a36Sopenharmony_ci DIONAME(PARALLEL), DIONAME(VME), DIONAME(DCL), DIONAME(DCLREM), 7762306a36Sopenharmony_ci DIONAME(MISC0), DIONAME(MISC1), DIONAME(MISC2), DIONAME(MISC3), 7862306a36Sopenharmony_ci DIONAME(MISC4), DIONAME(MISC5), DIONAME(MISC6), DIONAME(MISC7), 7962306a36Sopenharmony_ci DIONAME(MISC8), DIONAME(MISC9), DIONAME(MISC10), DIONAME(MISC11), 8062306a36Sopenharmony_ci DIONAME(MISC12), DIONAME(MISC13), 8162306a36Sopenharmony_ci DIOFBNAME(GATORBOX), DIOFBNAME(TOPCAT), DIOFBNAME(RENAISSANCE), 8262306a36Sopenharmony_ci DIOFBNAME(LRCATSEYE), DIOFBNAME(HRCCATSEYE), DIOFBNAME(HRMCATSEYE), 8362306a36Sopenharmony_ci DIOFBNAME(DAVINCI), DIOFBNAME(XXXCATSEYE), DIOFBNAME(HYPERION), 8462306a36Sopenharmony_ci DIOFBNAME(XGENESIS), DIOFBNAME(TIGER), DIOFBNAME(YGENESIS) 8562306a36Sopenharmony_ci}; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#undef DIONAME 8862306a36Sopenharmony_ci#undef DIOFBNAME 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic const char unknowndioname[] 9162306a36Sopenharmony_ci = "unknown DIO board, please email linux-m68k@lists.linux-m68k.org"; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic const char *dio_getname(int id) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci /* return pointer to a constant string describing the board with given ID */ 9662306a36Sopenharmony_ci unsigned int i; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(names); i++) 9962306a36Sopenharmony_ci if (names[i].id == id) 10062306a36Sopenharmony_ci return names[i].name; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return unknowndioname; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci#else 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic char dio_no_name[] = { 0 }; 10862306a36Sopenharmony_ci#define dio_getname(_id) (dio_no_name) 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci#endif /* CONFIG_DIO_CONSTANTS */ 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic void dio_dev_release(struct device *dev) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci struct dio_dev *ddev = container_of(dev, typeof(struct dio_dev), dev); 11562306a36Sopenharmony_ci kfree(ddev); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ciint __init dio_find(int deviceid) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci /* Called to find a DIO device before the full bus scan has run. 12162306a36Sopenharmony_ci * Only used by the console driver. 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_ci int scode, id; 12462306a36Sopenharmony_ci u_char prid, secid, i; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci for (scode = 0; scode < DIO_SCMAX; scode++) { 12762306a36Sopenharmony_ci void *va; 12862306a36Sopenharmony_ci unsigned long pa; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (DIO_SCINHOLE(scode)) 13162306a36Sopenharmony_ci continue; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci pa = dio_scodetophysaddr(scode); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (!pa) 13662306a36Sopenharmony_ci continue; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (scode < DIOII_SCBASE) 13962306a36Sopenharmony_ci va = (void *)(pa + DIO_VIRADDRBASE); 14062306a36Sopenharmony_ci else 14162306a36Sopenharmony_ci va = ioremap(pa, PAGE_SIZE); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (copy_from_kernel_nofault(&i, 14462306a36Sopenharmony_ci (unsigned char *)va + DIO_IDOFF, 1)) { 14562306a36Sopenharmony_ci if (scode >= DIOII_SCBASE) 14662306a36Sopenharmony_ci iounmap(va); 14762306a36Sopenharmony_ci continue; /* no board present at that select code */ 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci prid = DIO_ID(va); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (DIO_NEEDSSECID(prid)) { 15362306a36Sopenharmony_ci secid = DIO_SECID(va); 15462306a36Sopenharmony_ci id = DIO_ENCODE_ID(prid, secid); 15562306a36Sopenharmony_ci } else 15662306a36Sopenharmony_ci id = prid; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (id == deviceid) { 15962306a36Sopenharmony_ci if (scode >= DIOII_SCBASE) 16062306a36Sopenharmony_ci iounmap(va); 16162306a36Sopenharmony_ci return scode; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci return -1; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/* This is the function that scans the DIO space and works out what 16962306a36Sopenharmony_ci * hardware is actually present. 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_cistatic int __init dio_init(void) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci int scode; 17462306a36Sopenharmony_ci int i; 17562306a36Sopenharmony_ci struct dio_dev *dev; 17662306a36Sopenharmony_ci int error; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (!MACH_IS_HP300) 17962306a36Sopenharmony_ci return 0; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci printk(KERN_INFO "Scanning for DIO devices...\n"); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* Initialize the DIO bus */ 18462306a36Sopenharmony_ci INIT_LIST_HEAD(&dio_bus.devices); 18562306a36Sopenharmony_ci dev_set_name(&dio_bus.dev, "dio"); 18662306a36Sopenharmony_ci error = device_register(&dio_bus.dev); 18762306a36Sopenharmony_ci if (error) { 18862306a36Sopenharmony_ci pr_err("DIO: Error registering dio_bus\n"); 18962306a36Sopenharmony_ci return error; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* Request all resources */ 19362306a36Sopenharmony_ci dio_bus.num_resources = (hp300_model == HP_320 ? 1 : 2); 19462306a36Sopenharmony_ci for (i = 0; i < dio_bus.num_resources; i++) 19562306a36Sopenharmony_ci request_resource(&iomem_resource, &dio_bus.resources[i]); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* Register all devices */ 19862306a36Sopenharmony_ci for (scode = 0; scode < DIO_SCMAX; ++scode) { 19962306a36Sopenharmony_ci u_char prid, secid = 0; /* primary, secondary ID bytes */ 20062306a36Sopenharmony_ci u_char *va; 20162306a36Sopenharmony_ci unsigned long pa; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (DIO_SCINHOLE(scode)) 20462306a36Sopenharmony_ci continue; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci pa = dio_scodetophysaddr(scode); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (!pa) 20962306a36Sopenharmony_ci continue; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (scode < DIOII_SCBASE) 21262306a36Sopenharmony_ci va = (void *)(pa + DIO_VIRADDRBASE); 21362306a36Sopenharmony_ci else 21462306a36Sopenharmony_ci va = ioremap(pa, PAGE_SIZE); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (copy_from_kernel_nofault(&i, 21762306a36Sopenharmony_ci (unsigned char *)va + DIO_IDOFF, 1)) { 21862306a36Sopenharmony_ci if (scode >= DIOII_SCBASE) 21962306a36Sopenharmony_ci iounmap(va); 22062306a36Sopenharmony_ci continue; /* no board present at that select code */ 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* Found a board, allocate it an entry in the list */ 22462306a36Sopenharmony_ci dev = kzalloc(sizeof(struct dio_dev), GFP_KERNEL); 22562306a36Sopenharmony_ci if (!dev) { 22662306a36Sopenharmony_ci if (scode >= DIOII_SCBASE) 22762306a36Sopenharmony_ci iounmap(va); 22862306a36Sopenharmony_ci return -ENOMEM; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci dev->bus = &dio_bus; 23262306a36Sopenharmony_ci dev->dev.parent = &dio_bus.dev; 23362306a36Sopenharmony_ci dev->dev.bus = &dio_bus_type; 23462306a36Sopenharmony_ci dev->dev.release = dio_dev_release; 23562306a36Sopenharmony_ci dev->scode = scode; 23662306a36Sopenharmony_ci dev->resource.start = pa; 23762306a36Sopenharmony_ci dev->resource.end = pa + DIO_SIZE(scode, va); 23862306a36Sopenharmony_ci dev_set_name(&dev->dev, "%02x", scode); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* read the ID byte(s) and encode if necessary. */ 24162306a36Sopenharmony_ci prid = DIO_ID(va); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (DIO_NEEDSSECID(prid)) { 24462306a36Sopenharmony_ci secid = DIO_SECID(va); 24562306a36Sopenharmony_ci dev->id = DIO_ENCODE_ID(prid, secid); 24662306a36Sopenharmony_ci } else 24762306a36Sopenharmony_ci dev->id = prid; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci dev->ipl = DIO_IPL(va); 25062306a36Sopenharmony_ci strcpy(dev->name, dio_getname(dev->id)); 25162306a36Sopenharmony_ci printk(KERN_INFO "select code %3d: ipl %d: ID %02X", dev->scode, dev->ipl, prid); 25262306a36Sopenharmony_ci if (DIO_NEEDSSECID(prid)) 25362306a36Sopenharmony_ci printk(":%02X", secid); 25462306a36Sopenharmony_ci printk(": %s\n", dev->name); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (scode >= DIOII_SCBASE) 25762306a36Sopenharmony_ci iounmap(va); 25862306a36Sopenharmony_ci error = device_register(&dev->dev); 25962306a36Sopenharmony_ci if (error) { 26062306a36Sopenharmony_ci pr_err("DIO: Error registering device %s\n", 26162306a36Sopenharmony_ci dev->name); 26262306a36Sopenharmony_ci put_device(&dev->dev); 26362306a36Sopenharmony_ci continue; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci error = dio_create_sysfs_dev_files(dev); 26662306a36Sopenharmony_ci if (error) 26762306a36Sopenharmony_ci dev_err(&dev->dev, "Error creating sysfs files\n"); 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci return 0; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cisubsys_initcall(dio_init); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci/* Bear in mind that this is called in the very early stages of initialisation 27562306a36Sopenharmony_ci * in order to get the address of the serial port for the console... 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_ciunsigned long dio_scodetophysaddr(int scode) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci if (scode >= DIOII_SCBASE) 28062306a36Sopenharmony_ci return (DIOII_BASE + (scode - 132) * DIOII_DEVSIZE); 28162306a36Sopenharmony_ci else if (scode > DIO_SCMAX || scode < 0) 28262306a36Sopenharmony_ci return 0; 28362306a36Sopenharmony_ci else if (DIO_SCINHOLE(scode)) 28462306a36Sopenharmony_ci return 0; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci return (DIO_BASE + scode * DIO_DEVSIZE); 28762306a36Sopenharmony_ci} 288