18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * rsrc_nonstatic.c -- Resource management routines for !SS_CAP_STATIC_MAP sockets 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * The initial developer of the original code is David A. Hinds 68c2ecf20Sopenharmony_ci * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds 78c2ecf20Sopenharmony_ci * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * (C) 1999 David A. Hinds 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/errno.h> 188c2ecf20Sopenharmony_ci#include <linux/types.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/ioport.h> 218c2ecf20Sopenharmony_ci#include <linux/timer.h> 228c2ecf20Sopenharmony_ci#include <linux/pci.h> 238c2ecf20Sopenharmony_ci#include <linux/device.h> 248c2ecf20Sopenharmony_ci#include <linux/io.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <asm/irq.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <pcmcia/ss.h> 298c2ecf20Sopenharmony_ci#include <pcmcia/cistpl.h> 308c2ecf20Sopenharmony_ci#include "cs_internal.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* moved to rsrc_mgr.c 338c2ecf20Sopenharmony_ciMODULE_AUTHOR("David A. Hinds, Dominik Brodowski"); 348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 358c2ecf20Sopenharmony_ci*/ 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* Parameters that can be set with 'insmod' */ 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0444) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ciINT_MODULE_PARM(probe_mem, 1); /* memory probe? */ 428c2ecf20Sopenharmony_ci#ifdef CONFIG_PCMCIA_PROBE 438c2ecf20Sopenharmony_ciINT_MODULE_PARM(probe_io, 1); /* IO port probe? */ 448c2ecf20Sopenharmony_ciINT_MODULE_PARM(mem_limit, 0x10000); 458c2ecf20Sopenharmony_ci#endif 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* for io_db and mem_db */ 488c2ecf20Sopenharmony_cistruct resource_map { 498c2ecf20Sopenharmony_ci u_long base, num; 508c2ecf20Sopenharmony_ci struct resource_map *next; 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistruct socket_data { 548c2ecf20Sopenharmony_ci struct resource_map mem_db; 558c2ecf20Sopenharmony_ci struct resource_map mem_db_valid; 568c2ecf20Sopenharmony_ci struct resource_map io_db; 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#define MEM_PROBE_LOW (1 << 0) 608c2ecf20Sopenharmony_ci#define MEM_PROBE_HIGH (1 << 1) 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* Action field */ 638c2ecf20Sopenharmony_ci#define REMOVE_MANAGED_RESOURCE 1 648c2ecf20Sopenharmony_ci#define ADD_MANAGED_RESOURCE 2 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/*====================================================================== 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci Linux resource management extensions 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci======================================================================*/ 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic struct resource * 738c2ecf20Sopenharmony_ciclaim_region(struct pcmcia_socket *s, resource_size_t base, 748c2ecf20Sopenharmony_ci resource_size_t size, int type, char *name) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci struct resource *res, *parent; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci parent = type & IORESOURCE_MEM ? &iomem_resource : &ioport_resource; 798c2ecf20Sopenharmony_ci res = pcmcia_make_resource(base, size, type | IORESOURCE_BUSY, name); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (res) { 828c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI 838c2ecf20Sopenharmony_ci if (s && s->cb_dev) 848c2ecf20Sopenharmony_ci parent = pci_find_parent_resource(s->cb_dev, res); 858c2ecf20Sopenharmony_ci#endif 868c2ecf20Sopenharmony_ci if (!parent || request_resource(parent, res)) { 878c2ecf20Sopenharmony_ci kfree(res); 888c2ecf20Sopenharmony_ci res = NULL; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci return res; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic void free_region(struct resource *res) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci if (res) { 978c2ecf20Sopenharmony_ci release_resource(res); 988c2ecf20Sopenharmony_ci kfree(res); 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/*====================================================================== 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci These manage the internal databases of available resources. 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci======================================================================*/ 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic int add_interval(struct resource_map *map, u_long base, u_long num) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct resource_map *p, *q; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci for (p = map; ; p = p->next) { 1138c2ecf20Sopenharmony_ci if ((p != map) && (p->base+p->num >= base)) { 1148c2ecf20Sopenharmony_ci p->num = max(num + base - p->base, p->num); 1158c2ecf20Sopenharmony_ci return 0; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci if ((p->next == map) || (p->next->base > base+num-1)) 1188c2ecf20Sopenharmony_ci break; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci q = kmalloc(sizeof(struct resource_map), GFP_KERNEL); 1218c2ecf20Sopenharmony_ci if (!q) { 1228c2ecf20Sopenharmony_ci printk(KERN_WARNING "out of memory to update resources\n"); 1238c2ecf20Sopenharmony_ci return -ENOMEM; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci q->base = base; q->num = num; 1268c2ecf20Sopenharmony_ci q->next = p->next; p->next = q; 1278c2ecf20Sopenharmony_ci return 0; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/*====================================================================*/ 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic int sub_interval(struct resource_map *map, u_long base, u_long num) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct resource_map *p, *q; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci for (p = map; ; p = q) { 1378c2ecf20Sopenharmony_ci q = p->next; 1388c2ecf20Sopenharmony_ci if (q == map) 1398c2ecf20Sopenharmony_ci break; 1408c2ecf20Sopenharmony_ci if ((q->base+q->num > base) && (base+num > q->base)) { 1418c2ecf20Sopenharmony_ci if (q->base >= base) { 1428c2ecf20Sopenharmony_ci if (q->base+q->num <= base+num) { 1438c2ecf20Sopenharmony_ci /* Delete whole block */ 1448c2ecf20Sopenharmony_ci p->next = q->next; 1458c2ecf20Sopenharmony_ci kfree(q); 1468c2ecf20Sopenharmony_ci /* don't advance the pointer yet */ 1478c2ecf20Sopenharmony_ci q = p; 1488c2ecf20Sopenharmony_ci } else { 1498c2ecf20Sopenharmony_ci /* Cut off bit from the front */ 1508c2ecf20Sopenharmony_ci q->num = q->base + q->num - base - num; 1518c2ecf20Sopenharmony_ci q->base = base + num; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci } else if (q->base+q->num <= base+num) { 1548c2ecf20Sopenharmony_ci /* Cut off bit from the end */ 1558c2ecf20Sopenharmony_ci q->num = base - q->base; 1568c2ecf20Sopenharmony_ci } else { 1578c2ecf20Sopenharmony_ci /* Split the block into two pieces */ 1588c2ecf20Sopenharmony_ci p = kmalloc(sizeof(struct resource_map), 1598c2ecf20Sopenharmony_ci GFP_KERNEL); 1608c2ecf20Sopenharmony_ci if (!p) { 1618c2ecf20Sopenharmony_ci printk(KERN_WARNING "out of memory to update resources\n"); 1628c2ecf20Sopenharmony_ci return -ENOMEM; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci p->base = base+num; 1658c2ecf20Sopenharmony_ci p->num = q->base+q->num - p->base; 1668c2ecf20Sopenharmony_ci q->num = base - q->base; 1678c2ecf20Sopenharmony_ci p->next = q->next ; q->next = p; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci return 0; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/*====================================================================== 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci These routines examine a region of IO or memory addresses to 1778c2ecf20Sopenharmony_ci determine what ranges might be genuinely available. 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci======================================================================*/ 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci#ifdef CONFIG_PCMCIA_PROBE 1828c2ecf20Sopenharmony_cistatic void do_io_probe(struct pcmcia_socket *s, unsigned int base, 1838c2ecf20Sopenharmony_ci unsigned int num) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct resource *res; 1868c2ecf20Sopenharmony_ci struct socket_data *s_data = s->resource_data; 1878c2ecf20Sopenharmony_ci unsigned int i, j, bad; 1888c2ecf20Sopenharmony_ci int any; 1898c2ecf20Sopenharmony_ci u_char *b, hole, most; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci dev_info(&s->dev, "cs: IO port probe %#x-%#x:", base, base+num-1); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* First, what does a floating port look like? */ 1948c2ecf20Sopenharmony_ci b = kzalloc(256, GFP_KERNEL); 1958c2ecf20Sopenharmony_ci if (!b) { 1968c2ecf20Sopenharmony_ci pr_cont("\n"); 1978c2ecf20Sopenharmony_ci dev_err(&s->dev, "do_io_probe: unable to kmalloc 256 bytes\n"); 1988c2ecf20Sopenharmony_ci return; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci for (i = base, most = 0; i < base+num; i += 8) { 2018c2ecf20Sopenharmony_ci res = claim_region(s, i, 8, IORESOURCE_IO, "PCMCIA ioprobe"); 2028c2ecf20Sopenharmony_ci if (!res) 2038c2ecf20Sopenharmony_ci continue; 2048c2ecf20Sopenharmony_ci hole = inb(i); 2058c2ecf20Sopenharmony_ci for (j = 1; j < 8; j++) 2068c2ecf20Sopenharmony_ci if (inb(i+j) != hole) 2078c2ecf20Sopenharmony_ci break; 2088c2ecf20Sopenharmony_ci free_region(res); 2098c2ecf20Sopenharmony_ci if ((j == 8) && (++b[hole] > b[most])) 2108c2ecf20Sopenharmony_ci most = hole; 2118c2ecf20Sopenharmony_ci if (b[most] == 127) 2128c2ecf20Sopenharmony_ci break; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci kfree(b); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci bad = any = 0; 2178c2ecf20Sopenharmony_ci for (i = base; i < base+num; i += 8) { 2188c2ecf20Sopenharmony_ci res = claim_region(s, i, 8, IORESOURCE_IO, "PCMCIA ioprobe"); 2198c2ecf20Sopenharmony_ci if (!res) { 2208c2ecf20Sopenharmony_ci if (!any) 2218c2ecf20Sopenharmony_ci pr_cont(" excluding"); 2228c2ecf20Sopenharmony_ci if (!bad) 2238c2ecf20Sopenharmony_ci bad = any = i; 2248c2ecf20Sopenharmony_ci continue; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci for (j = 0; j < 8; j++) 2278c2ecf20Sopenharmony_ci if (inb(i+j) != most) 2288c2ecf20Sopenharmony_ci break; 2298c2ecf20Sopenharmony_ci free_region(res); 2308c2ecf20Sopenharmony_ci if (j < 8) { 2318c2ecf20Sopenharmony_ci if (!any) 2328c2ecf20Sopenharmony_ci pr_cont(" excluding"); 2338c2ecf20Sopenharmony_ci if (!bad) 2348c2ecf20Sopenharmony_ci bad = any = i; 2358c2ecf20Sopenharmony_ci } else { 2368c2ecf20Sopenharmony_ci if (bad) { 2378c2ecf20Sopenharmony_ci sub_interval(&s_data->io_db, bad, i-bad); 2388c2ecf20Sopenharmony_ci pr_cont(" %#x-%#x", bad, i-1); 2398c2ecf20Sopenharmony_ci bad = 0; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci if (bad) { 2448c2ecf20Sopenharmony_ci if ((num > 16) && (bad == base) && (i == base+num)) { 2458c2ecf20Sopenharmony_ci sub_interval(&s_data->io_db, bad, i-bad); 2468c2ecf20Sopenharmony_ci pr_cont(" nothing: probe failed.\n"); 2478c2ecf20Sopenharmony_ci return; 2488c2ecf20Sopenharmony_ci } else { 2498c2ecf20Sopenharmony_ci sub_interval(&s_data->io_db, bad, i-bad); 2508c2ecf20Sopenharmony_ci pr_cont(" %#x-%#x", bad, i-1); 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci pr_cont("%s\n", !any ? " clean" : ""); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci#endif 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci/*======================================================================*/ 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci/** 2618c2ecf20Sopenharmony_ci * readable() - iomem validation function for cards with a valid CIS 2628c2ecf20Sopenharmony_ci */ 2638c2ecf20Sopenharmony_cistatic int readable(struct pcmcia_socket *s, struct resource *res, 2648c2ecf20Sopenharmony_ci unsigned int *count) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci int ret = -EINVAL; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (s->fake_cis) { 2698c2ecf20Sopenharmony_ci dev_dbg(&s->dev, "fake CIS is being used: can't validate mem\n"); 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci s->cis_mem.res = res; 2748c2ecf20Sopenharmony_ci s->cis_virt = ioremap(res->start, s->map_size); 2758c2ecf20Sopenharmony_ci if (s->cis_virt) { 2768c2ecf20Sopenharmony_ci mutex_unlock(&s->ops_mutex); 2778c2ecf20Sopenharmony_ci /* as we're only called from pcmcia.c, we're safe */ 2788c2ecf20Sopenharmony_ci if (s->callback->validate) 2798c2ecf20Sopenharmony_ci ret = s->callback->validate(s, count); 2808c2ecf20Sopenharmony_ci /* invalidate mapping */ 2818c2ecf20Sopenharmony_ci mutex_lock(&s->ops_mutex); 2828c2ecf20Sopenharmony_ci iounmap(s->cis_virt); 2838c2ecf20Sopenharmony_ci s->cis_virt = NULL; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci s->cis_mem.res = NULL; 2868c2ecf20Sopenharmony_ci if ((ret) || (*count == 0)) 2878c2ecf20Sopenharmony_ci return -EINVAL; 2888c2ecf20Sopenharmony_ci return 0; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci/** 2928c2ecf20Sopenharmony_ci * checksum() - iomem validation function for simple memory cards 2938c2ecf20Sopenharmony_ci */ 2948c2ecf20Sopenharmony_cistatic int checksum(struct pcmcia_socket *s, struct resource *res, 2958c2ecf20Sopenharmony_ci unsigned int *value) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci pccard_mem_map map; 2988c2ecf20Sopenharmony_ci int i, a = 0, b = -1, d; 2998c2ecf20Sopenharmony_ci void __iomem *virt; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci virt = ioremap(res->start, s->map_size); 3028c2ecf20Sopenharmony_ci if (virt) { 3038c2ecf20Sopenharmony_ci map.map = 0; 3048c2ecf20Sopenharmony_ci map.flags = MAP_ACTIVE; 3058c2ecf20Sopenharmony_ci map.speed = 0; 3068c2ecf20Sopenharmony_ci map.res = res; 3078c2ecf20Sopenharmony_ci map.card_start = 0; 3088c2ecf20Sopenharmony_ci s->ops->set_mem_map(s, &map); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* Don't bother checking every word... */ 3118c2ecf20Sopenharmony_ci for (i = 0; i < s->map_size; i += 44) { 3128c2ecf20Sopenharmony_ci d = readl(virt+i); 3138c2ecf20Sopenharmony_ci a += d; 3148c2ecf20Sopenharmony_ci b &= d; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci map.flags = 0; 3188c2ecf20Sopenharmony_ci s->ops->set_mem_map(s, &map); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci iounmap(virt); 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if (b == -1) 3248c2ecf20Sopenharmony_ci return -EINVAL; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci *value = a; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci return 0; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci/** 3328c2ecf20Sopenharmony_ci * do_validate_mem() - low level validate a memory region for PCMCIA use 3338c2ecf20Sopenharmony_ci * @s: PCMCIA socket to validate 3348c2ecf20Sopenharmony_ci * @base: start address of resource to check 3358c2ecf20Sopenharmony_ci * @size: size of resource to check 3368c2ecf20Sopenharmony_ci * @validate: validation function to use 3378c2ecf20Sopenharmony_ci * 3388c2ecf20Sopenharmony_ci * do_validate_mem() splits up the memory region which is to be checked 3398c2ecf20Sopenharmony_ci * into two parts. Both are passed to the @validate() function. If 3408c2ecf20Sopenharmony_ci * @validate() returns non-zero, or the value parameter to @validate() 3418c2ecf20Sopenharmony_ci * is zero, or the value parameter is different between both calls, 3428c2ecf20Sopenharmony_ci * the check fails, and -EINVAL is returned. Else, 0 is returned. 3438c2ecf20Sopenharmony_ci */ 3448c2ecf20Sopenharmony_cistatic int do_validate_mem(struct pcmcia_socket *s, 3458c2ecf20Sopenharmony_ci unsigned long base, unsigned long size, 3468c2ecf20Sopenharmony_ci int validate (struct pcmcia_socket *s, 3478c2ecf20Sopenharmony_ci struct resource *res, 3488c2ecf20Sopenharmony_ci unsigned int *value)) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci struct socket_data *s_data = s->resource_data; 3518c2ecf20Sopenharmony_ci struct resource *res1, *res2; 3528c2ecf20Sopenharmony_ci unsigned int info1 = 1, info2 = 1; 3538c2ecf20Sopenharmony_ci int ret = -EINVAL; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "PCMCIA memprobe"); 3568c2ecf20Sopenharmony_ci res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM, 3578c2ecf20Sopenharmony_ci "PCMCIA memprobe"); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (res1 && res2) { 3608c2ecf20Sopenharmony_ci ret = 0; 3618c2ecf20Sopenharmony_ci if (validate) { 3628c2ecf20Sopenharmony_ci ret = validate(s, res1, &info1); 3638c2ecf20Sopenharmony_ci ret += validate(s, res2, &info2); 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci dev_dbg(&s->dev, "cs: memory probe 0x%06lx-0x%06lx: %pr %pr %u %u %u", 3688c2ecf20Sopenharmony_ci base, base+size-1, res1, res2, ret, info1, info2); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci free_region(res2); 3718c2ecf20Sopenharmony_ci free_region(res1); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if ((ret) || (info1 != info2) || (info1 == 0)) 3748c2ecf20Sopenharmony_ci return -EINVAL; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (validate && !s->fake_cis) { 3778c2ecf20Sopenharmony_ci /* move it to the validated data set */ 3788c2ecf20Sopenharmony_ci add_interval(&s_data->mem_db_valid, base, size); 3798c2ecf20Sopenharmony_ci sub_interval(&s_data->mem_db, base, size); 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci return 0; 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci/** 3878c2ecf20Sopenharmony_ci * do_mem_probe() - validate a memory region for PCMCIA use 3888c2ecf20Sopenharmony_ci * @s: PCMCIA socket to validate 3898c2ecf20Sopenharmony_ci * @base: start address of resource to check 3908c2ecf20Sopenharmony_ci * @num: size of resource to check 3918c2ecf20Sopenharmony_ci * @validate: validation function to use 3928c2ecf20Sopenharmony_ci * @fallback: validation function to use if validate fails 3938c2ecf20Sopenharmony_ci * 3948c2ecf20Sopenharmony_ci * do_mem_probe() checks a memory region for use by the PCMCIA subsystem. 3958c2ecf20Sopenharmony_ci * To do so, the area is split up into sensible parts, and then passed 3968c2ecf20Sopenharmony_ci * into the @validate() function. Only if @validate() and @fallback() fail, 3978c2ecf20Sopenharmony_ci * the area is marked as unavaibale for use by the PCMCIA subsystem. The 3988c2ecf20Sopenharmony_ci * function returns the size of the usable memory area. 3998c2ecf20Sopenharmony_ci */ 4008c2ecf20Sopenharmony_cistatic int do_mem_probe(struct pcmcia_socket *s, u_long base, u_long num, 4018c2ecf20Sopenharmony_ci int validate (struct pcmcia_socket *s, 4028c2ecf20Sopenharmony_ci struct resource *res, 4038c2ecf20Sopenharmony_ci unsigned int *value), 4048c2ecf20Sopenharmony_ci int fallback (struct pcmcia_socket *s, 4058c2ecf20Sopenharmony_ci struct resource *res, 4068c2ecf20Sopenharmony_ci unsigned int *value)) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci struct socket_data *s_data = s->resource_data; 4098c2ecf20Sopenharmony_ci u_long i, j, bad, fail, step; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci dev_info(&s->dev, "cs: memory probe 0x%06lx-0x%06lx:", 4128c2ecf20Sopenharmony_ci base, base+num-1); 4138c2ecf20Sopenharmony_ci bad = fail = 0; 4148c2ecf20Sopenharmony_ci step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff); 4158c2ecf20Sopenharmony_ci /* don't allow too large steps */ 4168c2ecf20Sopenharmony_ci if (step > 0x800000) 4178c2ecf20Sopenharmony_ci step = 0x800000; 4188c2ecf20Sopenharmony_ci /* cis_readable wants to map 2x map_size */ 4198c2ecf20Sopenharmony_ci if (step < 2 * s->map_size) 4208c2ecf20Sopenharmony_ci step = 2 * s->map_size; 4218c2ecf20Sopenharmony_ci for (i = j = base; i < base+num; i = j + step) { 4228c2ecf20Sopenharmony_ci if (!fail) { 4238c2ecf20Sopenharmony_ci for (j = i; j < base+num; j += step) { 4248c2ecf20Sopenharmony_ci if (!do_validate_mem(s, j, step, validate)) 4258c2ecf20Sopenharmony_ci break; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci fail = ((i == base) && (j == base+num)); 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci if ((fail) && (fallback)) { 4308c2ecf20Sopenharmony_ci for (j = i; j < base+num; j += step) 4318c2ecf20Sopenharmony_ci if (!do_validate_mem(s, j, step, fallback)) 4328c2ecf20Sopenharmony_ci break; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci if (i != j) { 4358c2ecf20Sopenharmony_ci if (!bad) 4368c2ecf20Sopenharmony_ci pr_cont(" excluding"); 4378c2ecf20Sopenharmony_ci pr_cont(" %#05lx-%#05lx", i, j-1); 4388c2ecf20Sopenharmony_ci sub_interval(&s_data->mem_db, i, j-i); 4398c2ecf20Sopenharmony_ci bad += j-i; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci pr_cont("%s\n", !bad ? " clean" : ""); 4438c2ecf20Sopenharmony_ci return num - bad; 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci#ifdef CONFIG_PCMCIA_PROBE 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci/** 4508c2ecf20Sopenharmony_ci * inv_probe() - top-to-bottom search for one usuable high memory area 4518c2ecf20Sopenharmony_ci * @s: PCMCIA socket to validate 4528c2ecf20Sopenharmony_ci * @m: resource_map to check 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_cistatic u_long inv_probe(struct resource_map *m, struct pcmcia_socket *s) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci struct socket_data *s_data = s->resource_data; 4578c2ecf20Sopenharmony_ci u_long ok; 4588c2ecf20Sopenharmony_ci if (m == &s_data->mem_db) 4598c2ecf20Sopenharmony_ci return 0; 4608c2ecf20Sopenharmony_ci ok = inv_probe(m->next, s); 4618c2ecf20Sopenharmony_ci if (ok) { 4628c2ecf20Sopenharmony_ci if (m->base >= 0x100000) 4638c2ecf20Sopenharmony_ci sub_interval(&s_data->mem_db, m->base, m->num); 4648c2ecf20Sopenharmony_ci return ok; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci if (m->base < 0x100000) 4678c2ecf20Sopenharmony_ci return 0; 4688c2ecf20Sopenharmony_ci return do_mem_probe(s, m->base, m->num, readable, checksum); 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci/** 4728c2ecf20Sopenharmony_ci * validate_mem() - memory probe function 4738c2ecf20Sopenharmony_ci * @s: PCMCIA socket to validate 4748c2ecf20Sopenharmony_ci * @probe_mask: MEM_PROBE_LOW | MEM_PROBE_HIGH 4758c2ecf20Sopenharmony_ci * 4768c2ecf20Sopenharmony_ci * The memory probe. If the memory list includes a 64K-aligned block 4778c2ecf20Sopenharmony_ci * below 1MB, we probe in 64K chunks, and as soon as we accumulate at 4788c2ecf20Sopenharmony_ci * least mem_limit free space, we quit. Returns 0 on usuable ports. 4798c2ecf20Sopenharmony_ci */ 4808c2ecf20Sopenharmony_cistatic int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct resource_map *m, mm; 4838c2ecf20Sopenharmony_ci static unsigned char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 }; 4848c2ecf20Sopenharmony_ci unsigned long b, i, ok = 0; 4858c2ecf20Sopenharmony_ci struct socket_data *s_data = s->resource_data; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* We do up to four passes through the list */ 4888c2ecf20Sopenharmony_ci if (probe_mask & MEM_PROBE_HIGH) { 4898c2ecf20Sopenharmony_ci if (inv_probe(s_data->mem_db.next, s) > 0) 4908c2ecf20Sopenharmony_ci return 0; 4918c2ecf20Sopenharmony_ci if (s_data->mem_db_valid.next != &s_data->mem_db_valid) 4928c2ecf20Sopenharmony_ci return 0; 4938c2ecf20Sopenharmony_ci dev_notice(&s->dev, 4948c2ecf20Sopenharmony_ci "cs: warning: no high memory space available!\n"); 4958c2ecf20Sopenharmony_ci return -ENODEV; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) { 4998c2ecf20Sopenharmony_ci mm = *m; 5008c2ecf20Sopenharmony_ci /* Only probe < 1 MB */ 5018c2ecf20Sopenharmony_ci if (mm.base >= 0x100000) 5028c2ecf20Sopenharmony_ci continue; 5038c2ecf20Sopenharmony_ci if ((mm.base | mm.num) & 0xffff) { 5048c2ecf20Sopenharmony_ci ok += do_mem_probe(s, mm.base, mm.num, readable, 5058c2ecf20Sopenharmony_ci checksum); 5068c2ecf20Sopenharmony_ci continue; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci /* Special probe for 64K-aligned block */ 5098c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 5108c2ecf20Sopenharmony_ci b = order[i] << 12; 5118c2ecf20Sopenharmony_ci if ((b >= mm.base) && (b+0x10000 <= mm.base+mm.num)) { 5128c2ecf20Sopenharmony_ci if (ok >= mem_limit) 5138c2ecf20Sopenharmony_ci sub_interval(&s_data->mem_db, b, 0x10000); 5148c2ecf20Sopenharmony_ci else 5158c2ecf20Sopenharmony_ci ok += do_mem_probe(s, b, 0x10000, 5168c2ecf20Sopenharmony_ci readable, checksum); 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci if (ok > 0) 5228c2ecf20Sopenharmony_ci return 0; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci return -ENODEV; 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci#else /* CONFIG_PCMCIA_PROBE */ 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci/** 5308c2ecf20Sopenharmony_ci * validate_mem() - memory probe function 5318c2ecf20Sopenharmony_ci * @s: PCMCIA socket to validate 5328c2ecf20Sopenharmony_ci * @probe_mask: ignored 5338c2ecf20Sopenharmony_ci * 5348c2ecf20Sopenharmony_ci * Returns 0 on usuable ports. 5358c2ecf20Sopenharmony_ci */ 5368c2ecf20Sopenharmony_cistatic int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci struct resource_map *m, mm; 5398c2ecf20Sopenharmony_ci struct socket_data *s_data = s->resource_data; 5408c2ecf20Sopenharmony_ci unsigned long ok = 0; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) { 5438c2ecf20Sopenharmony_ci mm = *m; 5448c2ecf20Sopenharmony_ci ok += do_mem_probe(s, mm.base, mm.num, readable, checksum); 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci if (ok > 0) 5478c2ecf20Sopenharmony_ci return 0; 5488c2ecf20Sopenharmony_ci return -ENODEV; 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci#endif /* CONFIG_PCMCIA_PROBE */ 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci/** 5558c2ecf20Sopenharmony_ci * pcmcia_nonstatic_validate_mem() - try to validate iomem for PCMCIA use 5568c2ecf20Sopenharmony_ci * @s: PCMCIA socket to validate 5578c2ecf20Sopenharmony_ci * 5588c2ecf20Sopenharmony_ci * This is tricky... when we set up CIS memory, we try to validate 5598c2ecf20Sopenharmony_ci * the memory window space allocations. 5608c2ecf20Sopenharmony_ci * 5618c2ecf20Sopenharmony_ci * Locking note: Must be called with skt_mutex held! 5628c2ecf20Sopenharmony_ci */ 5638c2ecf20Sopenharmony_cistatic int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci struct socket_data *s_data = s->resource_data; 5668c2ecf20Sopenharmony_ci unsigned int probe_mask = MEM_PROBE_LOW; 5678c2ecf20Sopenharmony_ci int ret; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (!probe_mem || !(s->state & SOCKET_PRESENT)) 5708c2ecf20Sopenharmony_ci return 0; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci if (s->features & SS_CAP_PAGE_REGS) 5738c2ecf20Sopenharmony_ci probe_mask = MEM_PROBE_HIGH; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci ret = validate_mem(s, probe_mask); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci if (s_data->mem_db_valid.next != &s_data->mem_db_valid) 5788c2ecf20Sopenharmony_ci return 0; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci return ret; 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistruct pcmcia_align_data { 5848c2ecf20Sopenharmony_ci unsigned long mask; 5858c2ecf20Sopenharmony_ci unsigned long offset; 5868c2ecf20Sopenharmony_ci struct resource_map *map; 5878c2ecf20Sopenharmony_ci}; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cistatic resource_size_t pcmcia_common_align(struct pcmcia_align_data *align_data, 5908c2ecf20Sopenharmony_ci resource_size_t start) 5918c2ecf20Sopenharmony_ci{ 5928c2ecf20Sopenharmony_ci resource_size_t ret; 5938c2ecf20Sopenharmony_ci /* 5948c2ecf20Sopenharmony_ci * Ensure that we have the correct start address 5958c2ecf20Sopenharmony_ci */ 5968c2ecf20Sopenharmony_ci ret = (start & ~align_data->mask) + align_data->offset; 5978c2ecf20Sopenharmony_ci if (ret < start) 5988c2ecf20Sopenharmony_ci ret += align_data->mask + 1; 5998c2ecf20Sopenharmony_ci return ret; 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistatic resource_size_t 6038c2ecf20Sopenharmony_cipcmcia_align(void *align_data, const struct resource *res, 6048c2ecf20Sopenharmony_ci resource_size_t size, resource_size_t align) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci struct pcmcia_align_data *data = align_data; 6078c2ecf20Sopenharmony_ci struct resource_map *m; 6088c2ecf20Sopenharmony_ci resource_size_t start; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci start = pcmcia_common_align(data, res->start); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci for (m = data->map->next; m != data->map; m = m->next) { 6138c2ecf20Sopenharmony_ci unsigned long map_start = m->base; 6148c2ecf20Sopenharmony_ci unsigned long map_end = m->base + m->num - 1; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci /* 6178c2ecf20Sopenharmony_ci * If the lower resources are not available, try aligning 6188c2ecf20Sopenharmony_ci * to this entry of the resource database to see if it'll 6198c2ecf20Sopenharmony_ci * fit here. 6208c2ecf20Sopenharmony_ci */ 6218c2ecf20Sopenharmony_ci if (start < map_start) 6228c2ecf20Sopenharmony_ci start = pcmcia_common_align(data, map_start); 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci /* 6258c2ecf20Sopenharmony_ci * If we're above the area which was passed in, there's 6268c2ecf20Sopenharmony_ci * no point proceeding. 6278c2ecf20Sopenharmony_ci */ 6288c2ecf20Sopenharmony_ci if (start >= res->end) 6298c2ecf20Sopenharmony_ci break; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci if ((start + size - 1) <= map_end) 6328c2ecf20Sopenharmony_ci break; 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci /* 6368c2ecf20Sopenharmony_ci * If we failed to find something suitable, ensure we fail. 6378c2ecf20Sopenharmony_ci */ 6388c2ecf20Sopenharmony_ci if (m == data->map) 6398c2ecf20Sopenharmony_ci start = res->end; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci return start; 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci/* 6458c2ecf20Sopenharmony_ci * Adjust an existing IO region allocation, but making sure that we don't 6468c2ecf20Sopenharmony_ci * encroach outside the resources which the user supplied. 6478c2ecf20Sopenharmony_ci */ 6488c2ecf20Sopenharmony_cistatic int __nonstatic_adjust_io_region(struct pcmcia_socket *s, 6498c2ecf20Sopenharmony_ci unsigned long r_start, 6508c2ecf20Sopenharmony_ci unsigned long r_end) 6518c2ecf20Sopenharmony_ci{ 6528c2ecf20Sopenharmony_ci struct resource_map *m; 6538c2ecf20Sopenharmony_ci struct socket_data *s_data = s->resource_data; 6548c2ecf20Sopenharmony_ci int ret = -ENOMEM; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci for (m = s_data->io_db.next; m != &s_data->io_db; m = m->next) { 6578c2ecf20Sopenharmony_ci unsigned long start = m->base; 6588c2ecf20Sopenharmony_ci unsigned long end = m->base + m->num - 1; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci if (start > r_start || r_end > end) 6618c2ecf20Sopenharmony_ci continue; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci ret = 0; 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci return ret; 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci/*====================================================================== 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci These find ranges of I/O ports or memory addresses that are not 6728c2ecf20Sopenharmony_ci currently allocated by other devices. 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci The 'align' field should reflect the number of bits of address 6758c2ecf20Sopenharmony_ci that need to be preserved from the initial value of *base. It 6768c2ecf20Sopenharmony_ci should be a power of two, greater than or equal to 'num'. A value 6778c2ecf20Sopenharmony_ci of 0 means that all bits of *base are significant. *base should 6788c2ecf20Sopenharmony_ci also be strictly less than 'align'. 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci======================================================================*/ 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_cistatic struct resource *__nonstatic_find_io_region(struct pcmcia_socket *s, 6838c2ecf20Sopenharmony_ci unsigned long base, int num, 6848c2ecf20Sopenharmony_ci unsigned long align) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_IO, 6878c2ecf20Sopenharmony_ci dev_name(&s->dev)); 6888c2ecf20Sopenharmony_ci struct socket_data *s_data = s->resource_data; 6898c2ecf20Sopenharmony_ci struct pcmcia_align_data data; 6908c2ecf20Sopenharmony_ci unsigned long min = base; 6918c2ecf20Sopenharmony_ci int ret; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci if (!res) 6948c2ecf20Sopenharmony_ci return NULL; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci data.mask = align - 1; 6978c2ecf20Sopenharmony_ci data.offset = base & data.mask; 6988c2ecf20Sopenharmony_ci data.map = &s_data->io_db; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI 7018c2ecf20Sopenharmony_ci if (s->cb_dev) { 7028c2ecf20Sopenharmony_ci ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1, 7038c2ecf20Sopenharmony_ci min, 0, pcmcia_align, &data); 7048c2ecf20Sopenharmony_ci } else 7058c2ecf20Sopenharmony_ci#endif 7068c2ecf20Sopenharmony_ci ret = allocate_resource(&ioport_resource, res, num, min, ~0UL, 7078c2ecf20Sopenharmony_ci 1, pcmcia_align, &data); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci if (ret != 0) { 7108c2ecf20Sopenharmony_ci kfree(res); 7118c2ecf20Sopenharmony_ci res = NULL; 7128c2ecf20Sopenharmony_ci } 7138c2ecf20Sopenharmony_ci return res; 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic int nonstatic_find_io(struct pcmcia_socket *s, unsigned int attr, 7178c2ecf20Sopenharmony_ci unsigned int *base, unsigned int num, 7188c2ecf20Sopenharmony_ci unsigned int align, struct resource **parent) 7198c2ecf20Sopenharmony_ci{ 7208c2ecf20Sopenharmony_ci int i, ret = 0; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci /* Check for an already-allocated window that must conflict with 7238c2ecf20Sopenharmony_ci * what was asked for. It is a hack because it does not catch all 7248c2ecf20Sopenharmony_ci * potential conflicts, just the most obvious ones. 7258c2ecf20Sopenharmony_ci */ 7268c2ecf20Sopenharmony_ci for (i = 0; i < MAX_IO_WIN; i++) { 7278c2ecf20Sopenharmony_ci if (!s->io[i].res) 7288c2ecf20Sopenharmony_ci continue; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci if (!*base) 7318c2ecf20Sopenharmony_ci continue; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci if ((s->io[i].res->start & (align-1)) == *base) 7348c2ecf20Sopenharmony_ci return -EBUSY; 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci for (i = 0; i < MAX_IO_WIN; i++) { 7388c2ecf20Sopenharmony_ci struct resource *res = s->io[i].res; 7398c2ecf20Sopenharmony_ci unsigned int try; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci if (res && (res->flags & IORESOURCE_BITS) != 7428c2ecf20Sopenharmony_ci (attr & IORESOURCE_BITS)) 7438c2ecf20Sopenharmony_ci continue; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci if (!res) { 7468c2ecf20Sopenharmony_ci if (align == 0) 7478c2ecf20Sopenharmony_ci align = 0x10000; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci res = s->io[i].res = __nonstatic_find_io_region(s, 7508c2ecf20Sopenharmony_ci *base, num, 7518c2ecf20Sopenharmony_ci align); 7528c2ecf20Sopenharmony_ci if (!res) 7538c2ecf20Sopenharmony_ci return -EINVAL; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci *base = res->start; 7568c2ecf20Sopenharmony_ci s->io[i].res->flags = 7578c2ecf20Sopenharmony_ci ((res->flags & ~IORESOURCE_BITS) | 7588c2ecf20Sopenharmony_ci (attr & IORESOURCE_BITS)); 7598c2ecf20Sopenharmony_ci s->io[i].InUse = num; 7608c2ecf20Sopenharmony_ci *parent = res; 7618c2ecf20Sopenharmony_ci return 0; 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci /* Try to extend top of window */ 7658c2ecf20Sopenharmony_ci try = res->end + 1; 7668c2ecf20Sopenharmony_ci if ((*base == 0) || (*base == try)) { 7678c2ecf20Sopenharmony_ci ret = __nonstatic_adjust_io_region(s, res->start, 7688c2ecf20Sopenharmony_ci res->end + num); 7698c2ecf20Sopenharmony_ci if (!ret) { 7708c2ecf20Sopenharmony_ci ret = adjust_resource(s->io[i].res, res->start, 7718c2ecf20Sopenharmony_ci resource_size(res) + num); 7728c2ecf20Sopenharmony_ci if (ret) 7738c2ecf20Sopenharmony_ci continue; 7748c2ecf20Sopenharmony_ci *base = try; 7758c2ecf20Sopenharmony_ci s->io[i].InUse += num; 7768c2ecf20Sopenharmony_ci *parent = res; 7778c2ecf20Sopenharmony_ci return 0; 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci } 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci /* Try to extend bottom of window */ 7828c2ecf20Sopenharmony_ci try = res->start - num; 7838c2ecf20Sopenharmony_ci if ((*base == 0) || (*base == try)) { 7848c2ecf20Sopenharmony_ci ret = __nonstatic_adjust_io_region(s, 7858c2ecf20Sopenharmony_ci res->start - num, 7868c2ecf20Sopenharmony_ci res->end); 7878c2ecf20Sopenharmony_ci if (!ret) { 7888c2ecf20Sopenharmony_ci ret = adjust_resource(s->io[i].res, 7898c2ecf20Sopenharmony_ci res->start - num, 7908c2ecf20Sopenharmony_ci resource_size(res) + num); 7918c2ecf20Sopenharmony_ci if (ret) 7928c2ecf20Sopenharmony_ci continue; 7938c2ecf20Sopenharmony_ci *base = try; 7948c2ecf20Sopenharmony_ci s->io[i].InUse += num; 7958c2ecf20Sopenharmony_ci *parent = res; 7968c2ecf20Sopenharmony_ci return 0; 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci } 7998c2ecf20Sopenharmony_ci } 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci return -EINVAL; 8028c2ecf20Sopenharmony_ci} 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_cistatic struct resource *nonstatic_find_mem_region(u_long base, u_long num, 8068c2ecf20Sopenharmony_ci u_long align, int low, struct pcmcia_socket *s) 8078c2ecf20Sopenharmony_ci{ 8088c2ecf20Sopenharmony_ci struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_MEM, 8098c2ecf20Sopenharmony_ci dev_name(&s->dev)); 8108c2ecf20Sopenharmony_ci struct socket_data *s_data = s->resource_data; 8118c2ecf20Sopenharmony_ci struct pcmcia_align_data data; 8128c2ecf20Sopenharmony_ci unsigned long min, max; 8138c2ecf20Sopenharmony_ci int ret, i, j; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci if (!res) 8168c2ecf20Sopenharmony_ci return NULL; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci low = low || !(s->features & SS_CAP_PAGE_REGS); 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci data.mask = align - 1; 8218c2ecf20Sopenharmony_ci data.offset = base & data.mask; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 8248c2ecf20Sopenharmony_ci data.map = &s_data->mem_db_valid; 8258c2ecf20Sopenharmony_ci if (low) { 8268c2ecf20Sopenharmony_ci max = 0x100000UL; 8278c2ecf20Sopenharmony_ci min = base < max ? base : 0; 8288c2ecf20Sopenharmony_ci } else { 8298c2ecf20Sopenharmony_ci max = ~0UL; 8308c2ecf20Sopenharmony_ci min = 0x100000UL + base; 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci for (j = 0; j < 2; j++) { 8348c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI 8358c2ecf20Sopenharmony_ci if (s->cb_dev) { 8368c2ecf20Sopenharmony_ci ret = pci_bus_alloc_resource(s->cb_dev->bus, 8378c2ecf20Sopenharmony_ci res, num, 1, min, 0, 8388c2ecf20Sopenharmony_ci pcmcia_align, &data); 8398c2ecf20Sopenharmony_ci } else 8408c2ecf20Sopenharmony_ci#endif 8418c2ecf20Sopenharmony_ci { 8428c2ecf20Sopenharmony_ci ret = allocate_resource(&iomem_resource, 8438c2ecf20Sopenharmony_ci res, num, min, max, 1, 8448c2ecf20Sopenharmony_ci pcmcia_align, &data); 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci if (ret == 0) 8478c2ecf20Sopenharmony_ci break; 8488c2ecf20Sopenharmony_ci data.map = &s_data->mem_db; 8498c2ecf20Sopenharmony_ci } 8508c2ecf20Sopenharmony_ci if (ret == 0 || low) 8518c2ecf20Sopenharmony_ci break; 8528c2ecf20Sopenharmony_ci low = 1; 8538c2ecf20Sopenharmony_ci } 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci if (ret != 0) { 8568c2ecf20Sopenharmony_ci kfree(res); 8578c2ecf20Sopenharmony_ci res = NULL; 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci return res; 8608c2ecf20Sopenharmony_ci} 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_cistatic int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end) 8648c2ecf20Sopenharmony_ci{ 8658c2ecf20Sopenharmony_ci struct socket_data *data = s->resource_data; 8668c2ecf20Sopenharmony_ci unsigned long size = end - start + 1; 8678c2ecf20Sopenharmony_ci int ret = 0; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if (end < start) 8708c2ecf20Sopenharmony_ci return -EINVAL; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci switch (action) { 8738c2ecf20Sopenharmony_ci case ADD_MANAGED_RESOURCE: 8748c2ecf20Sopenharmony_ci ret = add_interval(&data->mem_db, start, size); 8758c2ecf20Sopenharmony_ci if (!ret) 8768c2ecf20Sopenharmony_ci do_mem_probe(s, start, size, NULL, NULL); 8778c2ecf20Sopenharmony_ci break; 8788c2ecf20Sopenharmony_ci case REMOVE_MANAGED_RESOURCE: 8798c2ecf20Sopenharmony_ci ret = sub_interval(&data->mem_db, start, size); 8808c2ecf20Sopenharmony_ci break; 8818c2ecf20Sopenharmony_ci default: 8828c2ecf20Sopenharmony_ci ret = -EINVAL; 8838c2ecf20Sopenharmony_ci } 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci return ret; 8868c2ecf20Sopenharmony_ci} 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_cistatic int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end) 8908c2ecf20Sopenharmony_ci{ 8918c2ecf20Sopenharmony_ci struct socket_data *data = s->resource_data; 8928c2ecf20Sopenharmony_ci unsigned long size; 8938c2ecf20Sopenharmony_ci int ret = 0; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci#if defined(CONFIG_X86) 8968c2ecf20Sopenharmony_ci /* on x86, avoid anything < 0x100 for it is often used for 8978c2ecf20Sopenharmony_ci * legacy platform devices */ 8988c2ecf20Sopenharmony_ci if (start < 0x100) 8998c2ecf20Sopenharmony_ci start = 0x100; 9008c2ecf20Sopenharmony_ci#endif 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci size = end - start + 1; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci if (end < start) 9058c2ecf20Sopenharmony_ci return -EINVAL; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci if (end > IO_SPACE_LIMIT) 9088c2ecf20Sopenharmony_ci return -EINVAL; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci switch (action) { 9118c2ecf20Sopenharmony_ci case ADD_MANAGED_RESOURCE: 9128c2ecf20Sopenharmony_ci if (add_interval(&data->io_db, start, size) != 0) { 9138c2ecf20Sopenharmony_ci ret = -EBUSY; 9148c2ecf20Sopenharmony_ci break; 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci#ifdef CONFIG_PCMCIA_PROBE 9178c2ecf20Sopenharmony_ci if (probe_io) 9188c2ecf20Sopenharmony_ci do_io_probe(s, start, size); 9198c2ecf20Sopenharmony_ci#endif 9208c2ecf20Sopenharmony_ci break; 9218c2ecf20Sopenharmony_ci case REMOVE_MANAGED_RESOURCE: 9228c2ecf20Sopenharmony_ci sub_interval(&data->io_db, start, size); 9238c2ecf20Sopenharmony_ci break; 9248c2ecf20Sopenharmony_ci default: 9258c2ecf20Sopenharmony_ci ret = -EINVAL; 9268c2ecf20Sopenharmony_ci break; 9278c2ecf20Sopenharmony_ci } 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci return ret; 9308c2ecf20Sopenharmony_ci} 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI 9348c2ecf20Sopenharmony_cistatic int nonstatic_autoadd_resources(struct pcmcia_socket *s) 9358c2ecf20Sopenharmony_ci{ 9368c2ecf20Sopenharmony_ci struct resource *res; 9378c2ecf20Sopenharmony_ci int i, done = 0; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci if (!s->cb_dev || !s->cb_dev->bus) 9408c2ecf20Sopenharmony_ci return -ENODEV; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci#if defined(CONFIG_X86) 9438c2ecf20Sopenharmony_ci /* If this is the root bus, the risk of hitting some strange 9448c2ecf20Sopenharmony_ci * system devices is too high: If a driver isn't loaded, the 9458c2ecf20Sopenharmony_ci * resources are not claimed; even if a driver is loaded, it 9468c2ecf20Sopenharmony_ci * may not request all resources or even the wrong one. We 9478c2ecf20Sopenharmony_ci * can neither trust the rest of the kernel nor ACPI/PNP and 9488c2ecf20Sopenharmony_ci * CRS parsing to get it right. Therefore, use several 9498c2ecf20Sopenharmony_ci * safeguards: 9508c2ecf20Sopenharmony_ci * 9518c2ecf20Sopenharmony_ci * - Do not auto-add resources if the CardBus bridge is on 9528c2ecf20Sopenharmony_ci * the PCI root bus 9538c2ecf20Sopenharmony_ci * 9548c2ecf20Sopenharmony_ci * - Avoid any I/O ports < 0x100. 9558c2ecf20Sopenharmony_ci * 9568c2ecf20Sopenharmony_ci * - On PCI-PCI bridges, only use resources which are set up 9578c2ecf20Sopenharmony_ci * exclusively for the secondary PCI bus: the risk of hitting 9588c2ecf20Sopenharmony_ci * system devices is quite low, as they usually aren't 9598c2ecf20Sopenharmony_ci * connected to the secondary PCI bus. 9608c2ecf20Sopenharmony_ci */ 9618c2ecf20Sopenharmony_ci if (s->cb_dev->bus->number == 0) 9628c2ecf20Sopenharmony_ci return -EINVAL; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) { 9658c2ecf20Sopenharmony_ci res = s->cb_dev->bus->resource[i]; 9668c2ecf20Sopenharmony_ci#else 9678c2ecf20Sopenharmony_ci pci_bus_for_each_resource(s->cb_dev->bus, res, i) { 9688c2ecf20Sopenharmony_ci#endif 9698c2ecf20Sopenharmony_ci if (!res) 9708c2ecf20Sopenharmony_ci continue; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_IO) { 9738c2ecf20Sopenharmony_ci /* safeguard against the root resource, where the 9748c2ecf20Sopenharmony_ci * risk of hitting any other device would be too 9758c2ecf20Sopenharmony_ci * high */ 9768c2ecf20Sopenharmony_ci if (res == &ioport_resource) 9778c2ecf20Sopenharmony_ci continue; 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci dev_info(&s->cb_dev->dev, 9808c2ecf20Sopenharmony_ci "pcmcia: parent PCI bridge window: %pR\n", 9818c2ecf20Sopenharmony_ci res); 9828c2ecf20Sopenharmony_ci if (!adjust_io(s, ADD_MANAGED_RESOURCE, res->start, res->end)) 9838c2ecf20Sopenharmony_ci done |= IORESOURCE_IO; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci } 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_MEM) { 9888c2ecf20Sopenharmony_ci /* safeguard against the root resource, where the 9898c2ecf20Sopenharmony_ci * risk of hitting any other device would be too 9908c2ecf20Sopenharmony_ci * high */ 9918c2ecf20Sopenharmony_ci if (res == &iomem_resource) 9928c2ecf20Sopenharmony_ci continue; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci dev_info(&s->cb_dev->dev, 9958c2ecf20Sopenharmony_ci "pcmcia: parent PCI bridge window: %pR\n", 9968c2ecf20Sopenharmony_ci res); 9978c2ecf20Sopenharmony_ci if (!adjust_memory(s, ADD_MANAGED_RESOURCE, res->start, res->end)) 9988c2ecf20Sopenharmony_ci done |= IORESOURCE_MEM; 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci } 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci /* if we got at least one of IO, and one of MEM, we can be glad and 10038c2ecf20Sopenharmony_ci * activate the PCMCIA subsystem */ 10048c2ecf20Sopenharmony_ci if (done == (IORESOURCE_MEM | IORESOURCE_IO)) 10058c2ecf20Sopenharmony_ci s->resource_setup_done = 1; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci return 0; 10088c2ecf20Sopenharmony_ci} 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci#else 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_cistatic inline int nonstatic_autoadd_resources(struct pcmcia_socket *s) 10138c2ecf20Sopenharmony_ci{ 10148c2ecf20Sopenharmony_ci return -ENODEV; 10158c2ecf20Sopenharmony_ci} 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci#endif 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_cistatic int nonstatic_init(struct pcmcia_socket *s) 10218c2ecf20Sopenharmony_ci{ 10228c2ecf20Sopenharmony_ci struct socket_data *data; 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci data = kzalloc(sizeof(struct socket_data), GFP_KERNEL); 10258c2ecf20Sopenharmony_ci if (!data) 10268c2ecf20Sopenharmony_ci return -ENOMEM; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci data->mem_db.next = &data->mem_db; 10298c2ecf20Sopenharmony_ci data->mem_db_valid.next = &data->mem_db_valid; 10308c2ecf20Sopenharmony_ci data->io_db.next = &data->io_db; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci s->resource_data = (void *) data; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci nonstatic_autoadd_resources(s); 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci return 0; 10378c2ecf20Sopenharmony_ci} 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_cistatic void nonstatic_release_resource_db(struct pcmcia_socket *s) 10408c2ecf20Sopenharmony_ci{ 10418c2ecf20Sopenharmony_ci struct socket_data *data = s->resource_data; 10428c2ecf20Sopenharmony_ci struct resource_map *p, *q; 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci for (p = data->mem_db_valid.next; p != &data->mem_db_valid; p = q) { 10458c2ecf20Sopenharmony_ci q = p->next; 10468c2ecf20Sopenharmony_ci kfree(p); 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci for (p = data->mem_db.next; p != &data->mem_db; p = q) { 10498c2ecf20Sopenharmony_ci q = p->next; 10508c2ecf20Sopenharmony_ci kfree(p); 10518c2ecf20Sopenharmony_ci } 10528c2ecf20Sopenharmony_ci for (p = data->io_db.next; p != &data->io_db; p = q) { 10538c2ecf20Sopenharmony_ci q = p->next; 10548c2ecf20Sopenharmony_ci kfree(p); 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci kfree(data); 10588c2ecf20Sopenharmony_ci} 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_cistruct pccard_resource_ops pccard_nonstatic_ops = { 10628c2ecf20Sopenharmony_ci .validate_mem = pcmcia_nonstatic_validate_mem, 10638c2ecf20Sopenharmony_ci .find_io = nonstatic_find_io, 10648c2ecf20Sopenharmony_ci .find_mem = nonstatic_find_mem_region, 10658c2ecf20Sopenharmony_ci .init = nonstatic_init, 10668c2ecf20Sopenharmony_ci .exit = nonstatic_release_resource_db, 10678c2ecf20Sopenharmony_ci}; 10688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pccard_nonstatic_ops); 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci/* sysfs interface to the resource database */ 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_cistatic ssize_t show_io_db(struct device *dev, 10748c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 10758c2ecf20Sopenharmony_ci{ 10768c2ecf20Sopenharmony_ci struct pcmcia_socket *s = dev_get_drvdata(dev); 10778c2ecf20Sopenharmony_ci struct socket_data *data; 10788c2ecf20Sopenharmony_ci struct resource_map *p; 10798c2ecf20Sopenharmony_ci ssize_t ret = 0; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci mutex_lock(&s->ops_mutex); 10828c2ecf20Sopenharmony_ci data = s->resource_data; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci for (p = data->io_db.next; p != &data->io_db; p = p->next) { 10858c2ecf20Sopenharmony_ci if (ret > (PAGE_SIZE - 10)) 10868c2ecf20Sopenharmony_ci continue; 10878c2ecf20Sopenharmony_ci ret += scnprintf(&buf[ret], (PAGE_SIZE - ret - 1), 10888c2ecf20Sopenharmony_ci "0x%08lx - 0x%08lx\n", 10898c2ecf20Sopenharmony_ci ((unsigned long) p->base), 10908c2ecf20Sopenharmony_ci ((unsigned long) p->base + p->num - 1)); 10918c2ecf20Sopenharmony_ci } 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci mutex_unlock(&s->ops_mutex); 10948c2ecf20Sopenharmony_ci return ret; 10958c2ecf20Sopenharmony_ci} 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_cistatic ssize_t store_io_db(struct device *dev, 10988c2ecf20Sopenharmony_ci struct device_attribute *attr, 10998c2ecf20Sopenharmony_ci const char *buf, size_t count) 11008c2ecf20Sopenharmony_ci{ 11018c2ecf20Sopenharmony_ci struct pcmcia_socket *s = dev_get_drvdata(dev); 11028c2ecf20Sopenharmony_ci unsigned long start_addr, end_addr; 11038c2ecf20Sopenharmony_ci unsigned int add = ADD_MANAGED_RESOURCE; 11048c2ecf20Sopenharmony_ci ssize_t ret = 0; 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci ret = sscanf(buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr); 11078c2ecf20Sopenharmony_ci if (ret != 2) { 11088c2ecf20Sopenharmony_ci ret = sscanf(buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr); 11098c2ecf20Sopenharmony_ci add = REMOVE_MANAGED_RESOURCE; 11108c2ecf20Sopenharmony_ci if (ret != 2) { 11118c2ecf20Sopenharmony_ci ret = sscanf(buf, "0x%lx - 0x%lx", &start_addr, 11128c2ecf20Sopenharmony_ci &end_addr); 11138c2ecf20Sopenharmony_ci add = ADD_MANAGED_RESOURCE; 11148c2ecf20Sopenharmony_ci if (ret != 2) 11158c2ecf20Sopenharmony_ci return -EINVAL; 11168c2ecf20Sopenharmony_ci } 11178c2ecf20Sopenharmony_ci } 11188c2ecf20Sopenharmony_ci if (end_addr < start_addr) 11198c2ecf20Sopenharmony_ci return -EINVAL; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci mutex_lock(&s->ops_mutex); 11228c2ecf20Sopenharmony_ci ret = adjust_io(s, add, start_addr, end_addr); 11238c2ecf20Sopenharmony_ci mutex_unlock(&s->ops_mutex); 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci return ret ? ret : count; 11268c2ecf20Sopenharmony_ci} 11278c2ecf20Sopenharmony_cistatic DEVICE_ATTR(available_resources_io, 0600, show_io_db, store_io_db); 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_cistatic ssize_t show_mem_db(struct device *dev, 11308c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 11318c2ecf20Sopenharmony_ci{ 11328c2ecf20Sopenharmony_ci struct pcmcia_socket *s = dev_get_drvdata(dev); 11338c2ecf20Sopenharmony_ci struct socket_data *data; 11348c2ecf20Sopenharmony_ci struct resource_map *p; 11358c2ecf20Sopenharmony_ci ssize_t ret = 0; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci mutex_lock(&s->ops_mutex); 11388c2ecf20Sopenharmony_ci data = s->resource_data; 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci for (p = data->mem_db_valid.next; p != &data->mem_db_valid; 11418c2ecf20Sopenharmony_ci p = p->next) { 11428c2ecf20Sopenharmony_ci if (ret > (PAGE_SIZE - 10)) 11438c2ecf20Sopenharmony_ci continue; 11448c2ecf20Sopenharmony_ci ret += scnprintf(&buf[ret], (PAGE_SIZE - ret - 1), 11458c2ecf20Sopenharmony_ci "0x%08lx - 0x%08lx\n", 11468c2ecf20Sopenharmony_ci ((unsigned long) p->base), 11478c2ecf20Sopenharmony_ci ((unsigned long) p->base + p->num - 1)); 11488c2ecf20Sopenharmony_ci } 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci for (p = data->mem_db.next; p != &data->mem_db; p = p->next) { 11518c2ecf20Sopenharmony_ci if (ret > (PAGE_SIZE - 10)) 11528c2ecf20Sopenharmony_ci continue; 11538c2ecf20Sopenharmony_ci ret += scnprintf(&buf[ret], (PAGE_SIZE - ret - 1), 11548c2ecf20Sopenharmony_ci "0x%08lx - 0x%08lx\n", 11558c2ecf20Sopenharmony_ci ((unsigned long) p->base), 11568c2ecf20Sopenharmony_ci ((unsigned long) p->base + p->num - 1)); 11578c2ecf20Sopenharmony_ci } 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci mutex_unlock(&s->ops_mutex); 11608c2ecf20Sopenharmony_ci return ret; 11618c2ecf20Sopenharmony_ci} 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_cistatic ssize_t store_mem_db(struct device *dev, 11648c2ecf20Sopenharmony_ci struct device_attribute *attr, 11658c2ecf20Sopenharmony_ci const char *buf, size_t count) 11668c2ecf20Sopenharmony_ci{ 11678c2ecf20Sopenharmony_ci struct pcmcia_socket *s = dev_get_drvdata(dev); 11688c2ecf20Sopenharmony_ci unsigned long start_addr, end_addr; 11698c2ecf20Sopenharmony_ci unsigned int add = ADD_MANAGED_RESOURCE; 11708c2ecf20Sopenharmony_ci ssize_t ret = 0; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci ret = sscanf(buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr); 11738c2ecf20Sopenharmony_ci if (ret != 2) { 11748c2ecf20Sopenharmony_ci ret = sscanf(buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr); 11758c2ecf20Sopenharmony_ci add = REMOVE_MANAGED_RESOURCE; 11768c2ecf20Sopenharmony_ci if (ret != 2) { 11778c2ecf20Sopenharmony_ci ret = sscanf(buf, "0x%lx - 0x%lx", &start_addr, 11788c2ecf20Sopenharmony_ci &end_addr); 11798c2ecf20Sopenharmony_ci add = ADD_MANAGED_RESOURCE; 11808c2ecf20Sopenharmony_ci if (ret != 2) 11818c2ecf20Sopenharmony_ci return -EINVAL; 11828c2ecf20Sopenharmony_ci } 11838c2ecf20Sopenharmony_ci } 11848c2ecf20Sopenharmony_ci if (end_addr < start_addr) 11858c2ecf20Sopenharmony_ci return -EINVAL; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci mutex_lock(&s->ops_mutex); 11888c2ecf20Sopenharmony_ci ret = adjust_memory(s, add, start_addr, end_addr); 11898c2ecf20Sopenharmony_ci mutex_unlock(&s->ops_mutex); 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci return ret ? ret : count; 11928c2ecf20Sopenharmony_ci} 11938c2ecf20Sopenharmony_cistatic DEVICE_ATTR(available_resources_mem, 0600, show_mem_db, store_mem_db); 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_cistatic struct attribute *pccard_rsrc_attributes[] = { 11968c2ecf20Sopenharmony_ci &dev_attr_available_resources_io.attr, 11978c2ecf20Sopenharmony_ci &dev_attr_available_resources_mem.attr, 11988c2ecf20Sopenharmony_ci NULL, 11998c2ecf20Sopenharmony_ci}; 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_cistatic const struct attribute_group rsrc_attributes = { 12028c2ecf20Sopenharmony_ci .attrs = pccard_rsrc_attributes, 12038c2ecf20Sopenharmony_ci}; 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_cistatic int pccard_sysfs_add_rsrc(struct device *dev, 12068c2ecf20Sopenharmony_ci struct class_interface *class_intf) 12078c2ecf20Sopenharmony_ci{ 12088c2ecf20Sopenharmony_ci struct pcmcia_socket *s = dev_get_drvdata(dev); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci if (s->resource_ops != &pccard_nonstatic_ops) 12118c2ecf20Sopenharmony_ci return 0; 12128c2ecf20Sopenharmony_ci return sysfs_create_group(&dev->kobj, &rsrc_attributes); 12138c2ecf20Sopenharmony_ci} 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_cistatic void pccard_sysfs_remove_rsrc(struct device *dev, 12168c2ecf20Sopenharmony_ci struct class_interface *class_intf) 12178c2ecf20Sopenharmony_ci{ 12188c2ecf20Sopenharmony_ci struct pcmcia_socket *s = dev_get_drvdata(dev); 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci if (s->resource_ops != &pccard_nonstatic_ops) 12218c2ecf20Sopenharmony_ci return; 12228c2ecf20Sopenharmony_ci sysfs_remove_group(&dev->kobj, &rsrc_attributes); 12238c2ecf20Sopenharmony_ci} 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_cistatic struct class_interface pccard_rsrc_interface __refdata = { 12268c2ecf20Sopenharmony_ci .class = &pcmcia_socket_class, 12278c2ecf20Sopenharmony_ci .add_dev = &pccard_sysfs_add_rsrc, 12288c2ecf20Sopenharmony_ci .remove_dev = &pccard_sysfs_remove_rsrc, 12298c2ecf20Sopenharmony_ci}; 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_cistatic int __init nonstatic_sysfs_init(void) 12328c2ecf20Sopenharmony_ci{ 12338c2ecf20Sopenharmony_ci return class_interface_register(&pccard_rsrc_interface); 12348c2ecf20Sopenharmony_ci} 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_cistatic void __exit nonstatic_sysfs_exit(void) 12378c2ecf20Sopenharmony_ci{ 12388c2ecf20Sopenharmony_ci class_interface_unregister(&pccard_rsrc_interface); 12398c2ecf20Sopenharmony_ci} 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_cimodule_init(nonstatic_sysfs_init); 12428c2ecf20Sopenharmony_cimodule_exit(nonstatic_sysfs_exit); 1243