18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * support.c - standard functions for the use of pnp protocol drivers 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2003 Adam Belay <ambx1@neo.rr.com> 68c2ecf20Sopenharmony_ci * Copyright (C) 2008 Hewlett-Packard Development Company, L.P. 78c2ecf20Sopenharmony_ci * Bjorn Helgaas <bjorn.helgaas@hp.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/ctype.h> 128c2ecf20Sopenharmony_ci#include <linux/pnp.h> 138c2ecf20Sopenharmony_ci#include "base.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci/** 168c2ecf20Sopenharmony_ci * pnp_is_active - Determines if a device is active based on its current 178c2ecf20Sopenharmony_ci * resources 188c2ecf20Sopenharmony_ci * @dev: pointer to the desired PnP device 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_ciint pnp_is_active(struct pnp_dev *dev) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci /* 238c2ecf20Sopenharmony_ci * I don't think this is very reliable because pnp_disable_dev() 248c2ecf20Sopenharmony_ci * only clears out auto-assigned resources. 258c2ecf20Sopenharmony_ci */ 268c2ecf20Sopenharmony_ci if (!pnp_port_start(dev, 0) && pnp_port_len(dev, 0) <= 1 && 278c2ecf20Sopenharmony_ci !pnp_mem_start(dev, 0) && pnp_mem_len(dev, 0) <= 1 && 288c2ecf20Sopenharmony_ci pnp_irq(dev, 0) == -1 && pnp_dma(dev, 0) == -1) 298c2ecf20Sopenharmony_ci return 0; 308c2ecf20Sopenharmony_ci else 318c2ecf20Sopenharmony_ci return 1; 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pnp_is_active); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* 378c2ecf20Sopenharmony_ci * Functionally similar to acpi_ex_eisa_id_to_string(), but that's 388c2ecf20Sopenharmony_ci * buried in the ACPI CA, and we can't depend on it being present. 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_civoid pnp_eisa_id_to_string(u32 id, char *str) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci id = be32_to_cpu(id); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci /* 458c2ecf20Sopenharmony_ci * According to the specs, the first three characters are five-bit 468c2ecf20Sopenharmony_ci * compressed ASCII, and the left-over high order bit should be zero. 478c2ecf20Sopenharmony_ci * However, the Linux ISAPNP code historically used six bits for the 488c2ecf20Sopenharmony_ci * first character, and there seem to be IDs that depend on that, 498c2ecf20Sopenharmony_ci * e.g., "nEC8241" in the Linux 8250_pnp serial driver and the 508c2ecf20Sopenharmony_ci * FreeBSD sys/pc98/cbus/sio_cbus.c driver. 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_ci str[0] = 'A' + ((id >> 26) & 0x3f) - 1; 538c2ecf20Sopenharmony_ci str[1] = 'A' + ((id >> 21) & 0x1f) - 1; 548c2ecf20Sopenharmony_ci str[2] = 'A' + ((id >> 16) & 0x1f) - 1; 558c2ecf20Sopenharmony_ci str[3] = hex_asc_hi(id >> 8); 568c2ecf20Sopenharmony_ci str[4] = hex_asc_lo(id >> 8); 578c2ecf20Sopenharmony_ci str[5] = hex_asc_hi(id); 588c2ecf20Sopenharmony_ci str[6] = hex_asc_lo(id); 598c2ecf20Sopenharmony_ci str[7] = '\0'; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cichar *pnp_resource_type_name(struct resource *res) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci switch (pnp_resource_type(res)) { 658c2ecf20Sopenharmony_ci case IORESOURCE_IO: 668c2ecf20Sopenharmony_ci return "io"; 678c2ecf20Sopenharmony_ci case IORESOURCE_MEM: 688c2ecf20Sopenharmony_ci return "mem"; 698c2ecf20Sopenharmony_ci case IORESOURCE_IRQ: 708c2ecf20Sopenharmony_ci return "irq"; 718c2ecf20Sopenharmony_ci case IORESOURCE_DMA: 728c2ecf20Sopenharmony_ci return "dma"; 738c2ecf20Sopenharmony_ci case IORESOURCE_BUS: 748c2ecf20Sopenharmony_ci return "bus"; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci return "unknown"; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_civoid dbg_pnp_show_resources(struct pnp_dev *dev, char *desc) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct pnp_resource *pnp_res; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (list_empty(&dev->resources)) 848c2ecf20Sopenharmony_ci pnp_dbg(&dev->dev, "%s: no current resources\n", desc); 858c2ecf20Sopenharmony_ci else { 868c2ecf20Sopenharmony_ci pnp_dbg(&dev->dev, "%s: current resources:\n", desc); 878c2ecf20Sopenharmony_ci list_for_each_entry(pnp_res, &dev->resources, list) 888c2ecf20Sopenharmony_ci pnp_dbg(&dev->dev, "%pr\n", &pnp_res->res); 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cichar *pnp_option_priority_name(struct pnp_option *option) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci switch (pnp_option_priority(option)) { 958c2ecf20Sopenharmony_ci case PNP_RES_PRIORITY_PREFERRED: 968c2ecf20Sopenharmony_ci return "preferred"; 978c2ecf20Sopenharmony_ci case PNP_RES_PRIORITY_ACCEPTABLE: 988c2ecf20Sopenharmony_ci return "acceptable"; 998c2ecf20Sopenharmony_ci case PNP_RES_PRIORITY_FUNCTIONAL: 1008c2ecf20Sopenharmony_ci return "functional"; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci return "invalid"; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_civoid dbg_pnp_show_option(struct pnp_dev *dev, struct pnp_option *option) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci char buf[128]; 1088c2ecf20Sopenharmony_ci int len = 0, i; 1098c2ecf20Sopenharmony_ci struct pnp_port *port; 1108c2ecf20Sopenharmony_ci struct pnp_mem *mem; 1118c2ecf20Sopenharmony_ci struct pnp_irq *irq; 1128c2ecf20Sopenharmony_ci struct pnp_dma *dma; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (pnp_option_is_dependent(option)) 1158c2ecf20Sopenharmony_ci len += scnprintf(buf + len, sizeof(buf) - len, 1168c2ecf20Sopenharmony_ci " dependent set %d (%s) ", 1178c2ecf20Sopenharmony_ci pnp_option_set(option), 1188c2ecf20Sopenharmony_ci pnp_option_priority_name(option)); 1198c2ecf20Sopenharmony_ci else 1208c2ecf20Sopenharmony_ci len += scnprintf(buf + len, sizeof(buf) - len, 1218c2ecf20Sopenharmony_ci " independent "); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci switch (option->type) { 1248c2ecf20Sopenharmony_ci case IORESOURCE_IO: 1258c2ecf20Sopenharmony_ci port = &option->u.port; 1268c2ecf20Sopenharmony_ci len += scnprintf(buf + len, sizeof(buf) - len, "io min %#llx " 1278c2ecf20Sopenharmony_ci "max %#llx align %lld size %lld flags %#x", 1288c2ecf20Sopenharmony_ci (unsigned long long) port->min, 1298c2ecf20Sopenharmony_ci (unsigned long long) port->max, 1308c2ecf20Sopenharmony_ci (unsigned long long) port->align, 1318c2ecf20Sopenharmony_ci (unsigned long long) port->size, port->flags); 1328c2ecf20Sopenharmony_ci break; 1338c2ecf20Sopenharmony_ci case IORESOURCE_MEM: 1348c2ecf20Sopenharmony_ci mem = &option->u.mem; 1358c2ecf20Sopenharmony_ci len += scnprintf(buf + len, sizeof(buf) - len, "mem min %#llx " 1368c2ecf20Sopenharmony_ci "max %#llx align %lld size %lld flags %#x", 1378c2ecf20Sopenharmony_ci (unsigned long long) mem->min, 1388c2ecf20Sopenharmony_ci (unsigned long long) mem->max, 1398c2ecf20Sopenharmony_ci (unsigned long long) mem->align, 1408c2ecf20Sopenharmony_ci (unsigned long long) mem->size, mem->flags); 1418c2ecf20Sopenharmony_ci break; 1428c2ecf20Sopenharmony_ci case IORESOURCE_IRQ: 1438c2ecf20Sopenharmony_ci irq = &option->u.irq; 1448c2ecf20Sopenharmony_ci len += scnprintf(buf + len, sizeof(buf) - len, "irq"); 1458c2ecf20Sopenharmony_ci if (bitmap_empty(irq->map.bits, PNP_IRQ_NR)) 1468c2ecf20Sopenharmony_ci len += scnprintf(buf + len, sizeof(buf) - len, 1478c2ecf20Sopenharmony_ci " <none>"); 1488c2ecf20Sopenharmony_ci else { 1498c2ecf20Sopenharmony_ci for (i = 0; i < PNP_IRQ_NR; i++) 1508c2ecf20Sopenharmony_ci if (test_bit(i, irq->map.bits)) 1518c2ecf20Sopenharmony_ci len += scnprintf(buf + len, 1528c2ecf20Sopenharmony_ci sizeof(buf) - len, 1538c2ecf20Sopenharmony_ci " %d", i); 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci len += scnprintf(buf + len, sizeof(buf) - len, " flags %#x", 1568c2ecf20Sopenharmony_ci irq->flags); 1578c2ecf20Sopenharmony_ci if (irq->flags & IORESOURCE_IRQ_OPTIONAL) 1588c2ecf20Sopenharmony_ci len += scnprintf(buf + len, sizeof(buf) - len, 1598c2ecf20Sopenharmony_ci " (optional)"); 1608c2ecf20Sopenharmony_ci break; 1618c2ecf20Sopenharmony_ci case IORESOURCE_DMA: 1628c2ecf20Sopenharmony_ci dma = &option->u.dma; 1638c2ecf20Sopenharmony_ci len += scnprintf(buf + len, sizeof(buf) - len, "dma"); 1648c2ecf20Sopenharmony_ci if (!dma->map) 1658c2ecf20Sopenharmony_ci len += scnprintf(buf + len, sizeof(buf) - len, 1668c2ecf20Sopenharmony_ci " <none>"); 1678c2ecf20Sopenharmony_ci else { 1688c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) 1698c2ecf20Sopenharmony_ci if (dma->map & (1 << i)) 1708c2ecf20Sopenharmony_ci len += scnprintf(buf + len, 1718c2ecf20Sopenharmony_ci sizeof(buf) - len, 1728c2ecf20Sopenharmony_ci " %d", i); 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci len += scnprintf(buf + len, sizeof(buf) - len, " (bitmask %#x) " 1758c2ecf20Sopenharmony_ci "flags %#x", dma->map, dma->flags); 1768c2ecf20Sopenharmony_ci break; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci pnp_dbg(&dev->dev, "%s\n", buf); 1798c2ecf20Sopenharmony_ci} 180