18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * rsrc_iodyn.c -- Resource management routines for MEM-static 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/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <pcmcia/ss.h> 178c2ecf20Sopenharmony_ci#include <pcmcia/cistpl.h> 188c2ecf20Sopenharmony_ci#include "cs_internal.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistruct pcmcia_align_data { 228c2ecf20Sopenharmony_ci unsigned long mask; 238c2ecf20Sopenharmony_ci unsigned long offset; 248c2ecf20Sopenharmony_ci}; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic resource_size_t pcmcia_align(void *align_data, 278c2ecf20Sopenharmony_ci const struct resource *res, 288c2ecf20Sopenharmony_ci resource_size_t size, resource_size_t align) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci struct pcmcia_align_data *data = align_data; 318c2ecf20Sopenharmony_ci resource_size_t start; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci start = (res->start & ~data->mask) + data->offset; 348c2ecf20Sopenharmony_ci if (start < res->start) 358c2ecf20Sopenharmony_ci start += data->mask + 1; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#ifdef CONFIG_X86 388c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_IO) { 398c2ecf20Sopenharmony_ci if (start & 0x300) 408c2ecf20Sopenharmony_ci start = (start + 0x3ff) & ~0x3ff; 418c2ecf20Sopenharmony_ci } 428c2ecf20Sopenharmony_ci#endif 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#ifdef CONFIG_M68K 458c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_IO) { 468c2ecf20Sopenharmony_ci if ((res->start + size - 1) >= 1024) 478c2ecf20Sopenharmony_ci start = res->end; 488c2ecf20Sopenharmony_ci } 498c2ecf20Sopenharmony_ci#endif 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci return start; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic struct resource *__iodyn_find_io_region(struct pcmcia_socket *s, 568c2ecf20Sopenharmony_ci unsigned long base, int num, 578c2ecf20Sopenharmony_ci unsigned long align) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci struct resource *res = pcmcia_make_resource(0, num, IORESOURCE_IO, 608c2ecf20Sopenharmony_ci dev_name(&s->dev)); 618c2ecf20Sopenharmony_ci struct pcmcia_align_data data; 628c2ecf20Sopenharmony_ci unsigned long min = base; 638c2ecf20Sopenharmony_ci int ret; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci data.mask = align - 1; 668c2ecf20Sopenharmony_ci data.offset = base & data.mask; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI 698c2ecf20Sopenharmony_ci if (s->cb_dev) { 708c2ecf20Sopenharmony_ci ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1, 718c2ecf20Sopenharmony_ci min, 0, pcmcia_align, &data); 728c2ecf20Sopenharmony_ci } else 738c2ecf20Sopenharmony_ci#endif 748c2ecf20Sopenharmony_ci ret = allocate_resource(&ioport_resource, res, num, min, ~0UL, 758c2ecf20Sopenharmony_ci 1, pcmcia_align, &data); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (ret != 0) { 788c2ecf20Sopenharmony_ci kfree(res); 798c2ecf20Sopenharmony_ci res = NULL; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci return res; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int iodyn_find_io(struct pcmcia_socket *s, unsigned int attr, 858c2ecf20Sopenharmony_ci unsigned int *base, unsigned int num, 868c2ecf20Sopenharmony_ci unsigned int align, struct resource **parent) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci int i, ret = 0; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* Check for an already-allocated window that must conflict with 918c2ecf20Sopenharmony_ci * what was asked for. It is a hack because it does not catch all 928c2ecf20Sopenharmony_ci * potential conflicts, just the most obvious ones. 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_ci for (i = 0; i < MAX_IO_WIN; i++) { 958c2ecf20Sopenharmony_ci if (!s->io[i].res) 968c2ecf20Sopenharmony_ci continue; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (!*base) 998c2ecf20Sopenharmony_ci continue; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if ((s->io[i].res->start & (align-1)) == *base) 1028c2ecf20Sopenharmony_ci return -EBUSY; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci for (i = 0; i < MAX_IO_WIN; i++) { 1068c2ecf20Sopenharmony_ci struct resource *res = s->io[i].res; 1078c2ecf20Sopenharmony_ci unsigned int try; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (res && (res->flags & IORESOURCE_BITS) != 1108c2ecf20Sopenharmony_ci (attr & IORESOURCE_BITS)) 1118c2ecf20Sopenharmony_ci continue; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (!res) { 1148c2ecf20Sopenharmony_ci if (align == 0) 1158c2ecf20Sopenharmony_ci align = 0x10000; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci res = s->io[i].res = __iodyn_find_io_region(s, *base, 1188c2ecf20Sopenharmony_ci num, align); 1198c2ecf20Sopenharmony_ci if (!res) 1208c2ecf20Sopenharmony_ci return -EINVAL; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci *base = res->start; 1238c2ecf20Sopenharmony_ci s->io[i].res->flags = 1248c2ecf20Sopenharmony_ci ((res->flags & ~IORESOURCE_BITS) | 1258c2ecf20Sopenharmony_ci (attr & IORESOURCE_BITS)); 1268c2ecf20Sopenharmony_ci s->io[i].InUse = num; 1278c2ecf20Sopenharmony_ci *parent = res; 1288c2ecf20Sopenharmony_ci return 0; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* Try to extend top of window */ 1328c2ecf20Sopenharmony_ci try = res->end + 1; 1338c2ecf20Sopenharmony_ci if ((*base == 0) || (*base == try)) { 1348c2ecf20Sopenharmony_ci if (adjust_resource(s->io[i].res, res->start, 1358c2ecf20Sopenharmony_ci resource_size(res) + num)) 1368c2ecf20Sopenharmony_ci continue; 1378c2ecf20Sopenharmony_ci *base = try; 1388c2ecf20Sopenharmony_ci s->io[i].InUse += num; 1398c2ecf20Sopenharmony_ci *parent = res; 1408c2ecf20Sopenharmony_ci return 0; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* Try to extend bottom of window */ 1448c2ecf20Sopenharmony_ci try = res->start - num; 1458c2ecf20Sopenharmony_ci if ((*base == 0) || (*base == try)) { 1468c2ecf20Sopenharmony_ci if (adjust_resource(s->io[i].res, 1478c2ecf20Sopenharmony_ci res->start - num, 1488c2ecf20Sopenharmony_ci resource_size(res) + num)) 1498c2ecf20Sopenharmony_ci continue; 1508c2ecf20Sopenharmony_ci *base = try; 1518c2ecf20Sopenharmony_ci s->io[i].InUse += num; 1528c2ecf20Sopenharmony_ci *parent = res; 1538c2ecf20Sopenharmony_ci return 0; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return -EINVAL; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistruct pccard_resource_ops pccard_iodyn_ops = { 1628c2ecf20Sopenharmony_ci .validate_mem = NULL, 1638c2ecf20Sopenharmony_ci .find_io = iodyn_find_io, 1648c2ecf20Sopenharmony_ci .find_mem = NULL, 1658c2ecf20Sopenharmony_ci .init = static_init, 1668c2ecf20Sopenharmony_ci .exit = NULL, 1678c2ecf20Sopenharmony_ci}; 1688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pccard_iodyn_ops); 169