18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * FILE NAME 38c2ecf20Sopenharmony_ci * drivers/pcmcia/vrc4173_cardu.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * BRIEF MODULE DESCRIPTION 68c2ecf20Sopenharmony_ci * NEC VRC4173 CARDU driver for Socket Services 78c2ecf20Sopenharmony_ci * (This device doesn't support CardBus. it is supporting only 16bit PC Card.) 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Copyright 2002,2003 Yoichi Yuasa <yuasa@linux-mips.org> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 128c2ecf20Sopenharmony_ci * under the terms of the GNU General Public License as published by the 138c2ecf20Sopenharmony_ci * Free Software Foundation; either version 2 of the License, or (at your 148c2ecf20Sopenharmony_ci * option) any later version. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 178c2ecf20Sopenharmony_ci * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 188c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 198c2ecf20Sopenharmony_ci * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 208c2ecf20Sopenharmony_ci * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 218c2ecf20Sopenharmony_ci * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 228c2ecf20Sopenharmony_ci * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 238c2ecf20Sopenharmony_ci * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 248c2ecf20Sopenharmony_ci * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 258c2ecf20Sopenharmony_ci * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * You should have received a copy of the GNU General Public License along 288c2ecf20Sopenharmony_ci * with this program; if not, write to the Free Software Foundation, Inc., 298c2ecf20Sopenharmony_ci * 675 Mass Ave, Cambridge, MA 02139, USA. 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_ci#include <linux/init.h> 328c2ecf20Sopenharmony_ci#include <linux/module.h> 338c2ecf20Sopenharmony_ci#include <linux/pci.h> 348c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 358c2ecf20Sopenharmony_ci#include <linux/types.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include <asm/io.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#include <pcmcia/ss.h> 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#include "vrc4173_cardu.h" 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NEC VRC4173 CARDU driver for Socket Services"); 448c2ecf20Sopenharmony_ciMODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>"); 458c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic int vrc4173_cardu_slots; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic vrc4173_socket_t cardu_sockets[CARDU_MAX_SOCKETS]; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ciextern struct socket_info_t *pcmcia_register_socket (int slot, 528c2ecf20Sopenharmony_ci struct pccard_operations *vtable, 538c2ecf20Sopenharmony_ci int use_bus_pm); 548c2ecf20Sopenharmony_ciextern void pcmcia_unregister_socket(struct socket_info_t *s); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic inline uint8_t exca_readb(vrc4173_socket_t *socket, uint16_t offset) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci return readb(socket->base + EXCA_REGS_BASE + offset); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic inline uint16_t exca_readw(vrc4173_socket_t *socket, uint16_t offset) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci uint16_t val; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci val = readb(socket->base + EXCA_REGS_BASE + offset); 668c2ecf20Sopenharmony_ci val |= (u16)readb(socket->base + EXCA_REGS_BASE + offset + 1) << 8; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci return val; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic inline void exca_writeb(vrc4173_socket_t *socket, uint16_t offset, uint8_t val) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci writeb(val, socket->base + EXCA_REGS_BASE + offset); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic inline void exca_writew(vrc4173_socket_t *socket, uint8_t offset, uint16_t val) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci writeb((u8)val, socket->base + EXCA_REGS_BASE + offset); 798c2ecf20Sopenharmony_ci writeb((u8)(val >> 8), socket->base + EXCA_REGS_BASE + offset + 1); 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic inline uint32_t cardbus_socket_readl(vrc4173_socket_t *socket, u16 offset) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci return readl(socket->base + CARDBUS_SOCKET_REGS_BASE + offset); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic inline void cardbus_socket_writel(vrc4173_socket_t *socket, u16 offset, uint32_t val) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci writel(val, socket->base + CARDBUS_SOCKET_REGS_BASE + offset); 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic void cardu_pciregs_init(struct pci_dev *dev) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci u32 syscnt; 958c2ecf20Sopenharmony_ci u16 brgcnt; 968c2ecf20Sopenharmony_ci u8 devcnt; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci pci_write_config_dword(dev, 0x1c, 0x10000000); 998c2ecf20Sopenharmony_ci pci_write_config_dword(dev, 0x20, 0x17fff000); 1008c2ecf20Sopenharmony_ci pci_write_config_dword(dev, 0x2c, 0); 1018c2ecf20Sopenharmony_ci pci_write_config_dword(dev, 0x30, 0xfffc); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci pci_read_config_word(dev, BRGCNT, &brgcnt); 1048c2ecf20Sopenharmony_ci brgcnt &= ~IREQ_INT; 1058c2ecf20Sopenharmony_ci pci_write_config_word(dev, BRGCNT, brgcnt); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci pci_read_config_dword(dev, SYSCNT, &syscnt); 1088c2ecf20Sopenharmony_ci syscnt &= ~(BAD_VCC_REQ_DISB|PCPCI_EN|CH_ASSIGN_MASK|SUB_ID_WR_EN|PCI_CLK_RIN); 1098c2ecf20Sopenharmony_ci syscnt |= (CH_ASSIGN_NODMA|ASYN_INT_MODE); 1108c2ecf20Sopenharmony_ci pci_write_config_dword(dev, SYSCNT, syscnt); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci pci_read_config_byte(dev, DEVCNT, &devcnt); 1138c2ecf20Sopenharmony_ci devcnt &= ~(ZOOM_VIDEO_EN|SR_PCI_INT_SEL_MASK|PCI_INT_MODE|IRQ_MODE); 1148c2ecf20Sopenharmony_ci devcnt |= (SR_PCI_INT_SEL_NONE|IFG); 1158c2ecf20Sopenharmony_ci pci_write_config_byte(dev, DEVCNT, devcnt); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci pci_write_config_byte(dev, CHIPCNT, S_PREF_DISB); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci pci_write_config_byte(dev, SERRDIS, 0); 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic int cardu_init(unsigned int slot) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci vrc4173_socket_t *socket = &cardu_sockets[slot]; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci cardu_pciregs_init(socket->dev); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* CARD_SC bits are cleared by reading CARD_SC. */ 1298c2ecf20Sopenharmony_ci exca_writeb(socket, GLO_CNT, 0); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci socket->cap.features |= SS_CAP_PCCARD | SS_CAP_PAGE_REGS; 1328c2ecf20Sopenharmony_ci socket->cap.irq_mask = 0; 1338c2ecf20Sopenharmony_ci socket->cap.map_size = 0x1000; 1348c2ecf20Sopenharmony_ci socket->cap.pci_irq = socket->dev->irq; 1358c2ecf20Sopenharmony_ci socket->events = 0; 1368c2ecf20Sopenharmony_ci spin_lock_init(socket->event_lock); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* Enable PC Card status interrupts */ 1398c2ecf20Sopenharmony_ci exca_writeb(socket, CARD_SCI, CARD_DT_EN|RDY_EN|BAT_WAR_EN|BAT_DEAD_EN); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci return 0; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic int cardu_register_callback(unsigned int sock, 1458c2ecf20Sopenharmony_ci void (*handler)(void *, unsigned int), 1468c2ecf20Sopenharmony_ci void * info) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci vrc4173_socket_t *socket = &cardu_sockets[sock]; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci socket->handler = handler; 1518c2ecf20Sopenharmony_ci socket->info = info; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return 0; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic int cardu_inquire_socket(unsigned int sock, socket_cap_t *cap) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci vrc4173_socket_t *socket = &cardu_sockets[sock]; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci *cap = socket->cap; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return 0; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic int cardu_get_status(unsigned int sock, u_int *value) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci vrc4173_socket_t *socket = &cardu_sockets[sock]; 1688c2ecf20Sopenharmony_ci uint32_t state; 1698c2ecf20Sopenharmony_ci uint8_t status; 1708c2ecf20Sopenharmony_ci u_int val = 0; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci status = exca_readb(socket, IF_STATUS); 1738c2ecf20Sopenharmony_ci if (status & CARD_PWR) val |= SS_POWERON; 1748c2ecf20Sopenharmony_ci if (status & READY) val |= SS_READY; 1758c2ecf20Sopenharmony_ci if (status & CARD_WP) val |= SS_WRPROT; 1768c2ecf20Sopenharmony_ci if ((status & (CARD_DETECT1|CARD_DETECT2)) == (CARD_DETECT1|CARD_DETECT2)) 1778c2ecf20Sopenharmony_ci val |= SS_DETECT; 1788c2ecf20Sopenharmony_ci if (exca_readb(socket, INT_GEN_CNT) & CARD_TYPE_IO) { 1798c2ecf20Sopenharmony_ci if (status & STSCHG) val |= SS_STSCHG; 1808c2ecf20Sopenharmony_ci } else { 1818c2ecf20Sopenharmony_ci status &= BV_DETECT_MASK; 1828c2ecf20Sopenharmony_ci if (status != BV_DETECT_GOOD) { 1838c2ecf20Sopenharmony_ci if (status == BV_DETECT_WARN) val |= SS_BATWARN; 1848c2ecf20Sopenharmony_ci else val |= SS_BATDEAD; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci state = cardbus_socket_readl(socket, SKT_PRE_STATE); 1898c2ecf20Sopenharmony_ci if (state & VOL_3V_CARD_DT) val |= SS_3VCARD; 1908c2ecf20Sopenharmony_ci if (state & VOL_XV_CARD_DT) val |= SS_XVCARD; 1918c2ecf20Sopenharmony_ci if (state & CB_CARD_DT) val |= SS_CARDBUS; 1928c2ecf20Sopenharmony_ci if (!(state & 1938c2ecf20Sopenharmony_ci (VOL_YV_CARD_DT|VOL_XV_CARD_DT|VOL_3V_CARD_DT|VOL_5V_CARD_DT|CCD20|CCD10))) 1948c2ecf20Sopenharmony_ci val |= SS_PENDING; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci *value = val; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci return 0; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic inline uint8_t set_Vcc_value(u_char Vcc) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci switch (Vcc) { 2048c2ecf20Sopenharmony_ci case 33: 2058c2ecf20Sopenharmony_ci return VCC_3V; 2068c2ecf20Sopenharmony_ci case 50: 2078c2ecf20Sopenharmony_ci return VCC_5V; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return VCC_0V; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic inline uint8_t set_Vpp_value(u_char Vpp) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci switch (Vpp) { 2168c2ecf20Sopenharmony_ci case 33: 2178c2ecf20Sopenharmony_ci case 50: 2188c2ecf20Sopenharmony_ci return VPP_VCC; 2198c2ecf20Sopenharmony_ci case 120: 2208c2ecf20Sopenharmony_ci return VPP_12V; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return VPP_0V; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic int cardu_set_socket(unsigned int sock, socket_state_t *state) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci vrc4173_socket_t *socket = &cardu_sockets[sock]; 2298c2ecf20Sopenharmony_ci uint8_t val; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (((state->Vpp == 33) || (state->Vpp == 50)) && (state->Vpp != state->Vcc)) 2328c2ecf20Sopenharmony_ci return -EINVAL; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci val = set_Vcc_value(state->Vcc); 2358c2ecf20Sopenharmony_ci val |= set_Vpp_value(state->Vpp); 2368c2ecf20Sopenharmony_ci if (state->flags & SS_OUTPUT_ENA) val |= CARD_OUT_EN; 2378c2ecf20Sopenharmony_ci exca_writeb(socket, PWR_CNT, val); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci val = exca_readb(socket, INT_GEN_CNT) & CARD_REST0; 2408c2ecf20Sopenharmony_ci if (state->flags & SS_RESET) val &= ~CARD_REST0; 2418c2ecf20Sopenharmony_ci else val |= CARD_REST0; 2428c2ecf20Sopenharmony_ci if (state->flags & SS_IOCARD) val |= CARD_TYPE_IO; 2438c2ecf20Sopenharmony_ci exca_writeb(socket, INT_GEN_CNT, val); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci return 0; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic int cardu_get_io_map(unsigned int sock, struct pccard_io_map *io) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci vrc4173_socket_t *socket = &cardu_sockets[sock]; 2518c2ecf20Sopenharmony_ci uint8_t ioctl, window; 2528c2ecf20Sopenharmony_ci u_char map; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci map = io->map; 2558c2ecf20Sopenharmony_ci if (map > 1) 2568c2ecf20Sopenharmony_ci return -EINVAL; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci io->start = exca_readw(socket, IO_WIN_SA(map)); 2598c2ecf20Sopenharmony_ci io->stop = exca_readw(socket, IO_WIN_EA(map)); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci ioctl = exca_readb(socket, IO_WIN_CNT); 2628c2ecf20Sopenharmony_ci window = exca_readb(socket, ADR_WIN_EN); 2638c2ecf20Sopenharmony_ci io->flags = (window & IO_WIN_EN(map)) ? MAP_ACTIVE : 0; 2648c2ecf20Sopenharmony_ci if (ioctl & IO_WIN_DATA_AUTOSZ(map)) 2658c2ecf20Sopenharmony_ci io->flags |= MAP_AUTOSZ; 2668c2ecf20Sopenharmony_ci else if (ioctl & IO_WIN_DATA_16BIT(map)) 2678c2ecf20Sopenharmony_ci io->flags |= MAP_16BIT; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci return 0; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic int cardu_set_io_map(unsigned int sock, struct pccard_io_map *io) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci vrc4173_socket_t *socket = &cardu_sockets[sock]; 2758c2ecf20Sopenharmony_ci uint16_t ioctl; 2768c2ecf20Sopenharmony_ci uint8_t window, enable; 2778c2ecf20Sopenharmony_ci u_char map; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci map = io->map; 2808c2ecf20Sopenharmony_ci if (map > 1) 2818c2ecf20Sopenharmony_ci return -EINVAL; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci window = exca_readb(socket, ADR_WIN_EN); 2848c2ecf20Sopenharmony_ci enable = IO_WIN_EN(map); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (window & enable) { 2878c2ecf20Sopenharmony_ci window &= ~enable; 2888c2ecf20Sopenharmony_ci exca_writeb(socket, ADR_WIN_EN, window); 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci exca_writew(socket, IO_WIN_SA(map), io->start); 2928c2ecf20Sopenharmony_ci exca_writew(socket, IO_WIN_EA(map), io->stop); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci ioctl = exca_readb(socket, IO_WIN_CNT) & ~IO_WIN_CNT_MASK(map); 2958c2ecf20Sopenharmony_ci if (io->flags & MAP_AUTOSZ) ioctl |= IO_WIN_DATA_AUTOSZ(map); 2968c2ecf20Sopenharmony_ci else if (io->flags & MAP_16BIT) ioctl |= IO_WIN_DATA_16BIT(map); 2978c2ecf20Sopenharmony_ci exca_writeb(socket, IO_WIN_CNT, ioctl); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (io->flags & MAP_ACTIVE) 3008c2ecf20Sopenharmony_ci exca_writeb(socket, ADR_WIN_EN, window | enable); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci return 0; 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic int cardu_get_mem_map(unsigned int sock, struct pccard_mem_map *mem) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci vrc4173_socket_t *socket = &cardu_sockets[sock]; 3088c2ecf20Sopenharmony_ci uint32_t start, stop, offset, page; 3098c2ecf20Sopenharmony_ci uint8_t window; 3108c2ecf20Sopenharmony_ci u_char map; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci map = mem->map; 3138c2ecf20Sopenharmony_ci if (map > 4) 3148c2ecf20Sopenharmony_ci return -EINVAL; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci window = exca_readb(socket, ADR_WIN_EN); 3178c2ecf20Sopenharmony_ci mem->flags = (window & MEM_WIN_EN(map)) ? MAP_ACTIVE : 0; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci start = exca_readw(socket, MEM_WIN_SA(map)); 3208c2ecf20Sopenharmony_ci mem->flags |= (start & MEM_WIN_DSIZE) ? MAP_16BIT : 0; 3218c2ecf20Sopenharmony_ci start = (start & 0x0fff) << 12; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci stop = exca_readw(socket, MEM_WIN_EA(map)); 3248c2ecf20Sopenharmony_ci stop = ((stop & 0x0fff) << 12) + 0x0fff; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci offset = exca_readw(socket, MEM_WIN_OA(map)); 3278c2ecf20Sopenharmony_ci mem->flags |= (offset & MEM_WIN_WP) ? MAP_WRPROT : 0; 3288c2ecf20Sopenharmony_ci mem->flags |= (offset & MEM_WIN_REGSET) ? MAP_ATTRIB : 0; 3298c2ecf20Sopenharmony_ci offset = ((offset & 0x3fff) << 12) + start; 3308c2ecf20Sopenharmony_ci mem->card_start = offset & 0x03ffffff; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci page = exca_readb(socket, MEM_WIN_SAU(map)) << 24; 3338c2ecf20Sopenharmony_ci mem->sys_start = start + page; 3348c2ecf20Sopenharmony_ci mem->sys_stop = start + page; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci return 0; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic int cardu_set_mem_map(unsigned int sock, struct pccard_mem_map *mem) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci vrc4173_socket_t *socket = &cardu_sockets[sock]; 3428c2ecf20Sopenharmony_ci uint16_t value; 3438c2ecf20Sopenharmony_ci uint8_t window, enable; 3448c2ecf20Sopenharmony_ci u_long sys_start, sys_stop, card_start; 3458c2ecf20Sopenharmony_ci u_char map; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci map = mem->map; 3488c2ecf20Sopenharmony_ci sys_start = mem->sys_start; 3498c2ecf20Sopenharmony_ci sys_stop = mem->sys_stop; 3508c2ecf20Sopenharmony_ci card_start = mem->card_start; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (map > 4 || sys_start > sys_stop || ((sys_start ^ sys_stop) >> 24) || 3538c2ecf20Sopenharmony_ci (card_start >> 26)) 3548c2ecf20Sopenharmony_ci return -EINVAL; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci window = exca_readb(socket, ADR_WIN_EN); 3578c2ecf20Sopenharmony_ci enable = MEM_WIN_EN(map); 3588c2ecf20Sopenharmony_ci if (window & enable) { 3598c2ecf20Sopenharmony_ci window &= ~enable; 3608c2ecf20Sopenharmony_ci exca_writeb(socket, ADR_WIN_EN, window); 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci exca_writeb(socket, MEM_WIN_SAU(map), sys_start >> 24); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci value = (sys_start >> 12) & 0x0fff; 3668c2ecf20Sopenharmony_ci if (mem->flags & MAP_16BIT) value |= MEM_WIN_DSIZE; 3678c2ecf20Sopenharmony_ci exca_writew(socket, MEM_WIN_SA(map), value); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci value = (sys_stop >> 12) & 0x0fff; 3708c2ecf20Sopenharmony_ci exca_writew(socket, MEM_WIN_EA(map), value); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci value = ((card_start - sys_start) >> 12) & 0x3fff; 3738c2ecf20Sopenharmony_ci if (mem->flags & MAP_WRPROT) value |= MEM_WIN_WP; 3748c2ecf20Sopenharmony_ci if (mem->flags & MAP_ATTRIB) value |= MEM_WIN_REGSET; 3758c2ecf20Sopenharmony_ci exca_writew(socket, MEM_WIN_OA(map), value); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci if (mem->flags & MAP_ACTIVE) 3788c2ecf20Sopenharmony_ci exca_writeb(socket, ADR_WIN_EN, window | enable); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci return 0; 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic void cardu_proc_setup(unsigned int sock, struct proc_dir_entry *base) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic struct pccard_operations cardu_operations = { 3888c2ecf20Sopenharmony_ci .init = cardu_init, 3898c2ecf20Sopenharmony_ci .register_callback = cardu_register_callback, 3908c2ecf20Sopenharmony_ci .inquire_socket = cardu_inquire_socket, 3918c2ecf20Sopenharmony_ci .get_status = cardu_get_status, 3928c2ecf20Sopenharmony_ci .set_socket = cardu_set_socket, 3938c2ecf20Sopenharmony_ci .get_io_map = cardu_get_io_map, 3948c2ecf20Sopenharmony_ci .set_io_map = cardu_set_io_map, 3958c2ecf20Sopenharmony_ci .get_mem_map = cardu_get_mem_map, 3968c2ecf20Sopenharmony_ci .set_mem_map = cardu_set_mem_map, 3978c2ecf20Sopenharmony_ci .proc_setup = cardu_proc_setup, 3988c2ecf20Sopenharmony_ci}; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic void cardu_bh(void *data) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci vrc4173_socket_t *socket = (vrc4173_socket_t *)data; 4038c2ecf20Sopenharmony_ci uint16_t events; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci spin_lock_irq(&socket->event_lock); 4068c2ecf20Sopenharmony_ci events = socket->events; 4078c2ecf20Sopenharmony_ci socket->events = 0; 4088c2ecf20Sopenharmony_ci spin_unlock_irq(&socket->event_lock); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (socket->handler) 4118c2ecf20Sopenharmony_ci socket->handler(socket->info, events); 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic uint16_t get_events(vrc4173_socket_t *socket) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci uint16_t events = 0; 4178c2ecf20Sopenharmony_ci uint8_t csc, status; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci status = exca_readb(socket, IF_STATUS); 4208c2ecf20Sopenharmony_ci csc = exca_readb(socket, CARD_SC); 4218c2ecf20Sopenharmony_ci if ((csc & CARD_DT_CHG) && 4228c2ecf20Sopenharmony_ci ((status & (CARD_DETECT1|CARD_DETECT2)) == (CARD_DETECT1|CARD_DETECT2))) 4238c2ecf20Sopenharmony_ci events |= SS_DETECT; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if ((csc & RDY_CHG) && (status & READY)) 4268c2ecf20Sopenharmony_ci events |= SS_READY; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (exca_readb(socket, INT_GEN_CNT) & CARD_TYPE_IO) { 4298c2ecf20Sopenharmony_ci if ((csc & BAT_DEAD_ST_CHG) && (status & STSCHG)) 4308c2ecf20Sopenharmony_ci events |= SS_STSCHG; 4318c2ecf20Sopenharmony_ci } else { 4328c2ecf20Sopenharmony_ci if (csc & (BAT_WAR_CHG|BAT_DEAD_ST_CHG)) { 4338c2ecf20Sopenharmony_ci if ((status & BV_DETECT_MASK) != BV_DETECT_GOOD) { 4348c2ecf20Sopenharmony_ci if (status == BV_DETECT_WARN) events |= SS_BATWARN; 4358c2ecf20Sopenharmony_ci else events |= SS_BATDEAD; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci return events; 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic void cardu_interrupt(int irq, void *dev_id) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci vrc4173_socket_t *socket = (vrc4173_socket_t *)dev_id; 4468c2ecf20Sopenharmony_ci uint16_t events; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci INIT_WORK(&socket->tq_work, cardu_bh, socket); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci events = get_events(socket); 4518c2ecf20Sopenharmony_ci if (events) { 4528c2ecf20Sopenharmony_ci spin_lock(&socket->event_lock); 4538c2ecf20Sopenharmony_ci socket->events |= events; 4548c2ecf20Sopenharmony_ci spin_unlock(&socket->event_lock); 4558c2ecf20Sopenharmony_ci schedule_work(&socket->tq_work); 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cistatic int vrc4173_cardu_probe(struct pci_dev *dev, 4608c2ecf20Sopenharmony_ci const struct pci_device_id *ent) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci vrc4173_socket_t *socket; 4638c2ecf20Sopenharmony_ci unsigned long start, len, flags; 4648c2ecf20Sopenharmony_ci int slot, err, ret; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci slot = vrc4173_cardu_slots++; 4678c2ecf20Sopenharmony_ci socket = &cardu_sockets[slot]; 4688c2ecf20Sopenharmony_ci if (socket->noprobe != 0) 4698c2ecf20Sopenharmony_ci return -EBUSY; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci sprintf(socket->name, "NEC VRC4173 CARDU%1d", slot+1); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci if ((err = pci_enable_device(dev)) < 0) 4748c2ecf20Sopenharmony_ci return err; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci start = pci_resource_start(dev, 0); 4778c2ecf20Sopenharmony_ci if (start == 0) { 4788c2ecf20Sopenharmony_ci ret = -ENODEV; 4798c2ecf20Sopenharmony_ci goto disable; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci len = pci_resource_len(dev, 0); 4838c2ecf20Sopenharmony_ci if (len == 0) { 4848c2ecf20Sopenharmony_ci ret = -ENODEV; 4858c2ecf20Sopenharmony_ci goto disable; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci flags = pci_resource_flags(dev, 0); 4898c2ecf20Sopenharmony_ci if ((flags & IORESOURCE_MEM) == 0) { 4908c2ecf20Sopenharmony_ci ret = -EBUSY; 4918c2ecf20Sopenharmony_ci goto disable; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci err = pci_request_regions(dev, socket->name); 4958c2ecf20Sopenharmony_ci if (err < 0) { 4968c2ecf20Sopenharmony_ci ret = err; 4978c2ecf20Sopenharmony_ci goto disable; 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci socket->base = ioremap(start, len); 5018c2ecf20Sopenharmony_ci if (socket->base == NULL) { 5028c2ecf20Sopenharmony_ci ret = -ENODEV; 5038c2ecf20Sopenharmony_ci goto release; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci socket->dev = dev; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci socket->pcmcia_socket = pcmcia_register_socket(slot, &cardu_operations, 1); 5098c2ecf20Sopenharmony_ci if (socket->pcmcia_socket == NULL) { 5108c2ecf20Sopenharmony_ci ret = -ENOMEM; 5118c2ecf20Sopenharmony_ci goto unmap; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci if (request_irq(dev->irq, cardu_interrupt, IRQF_SHARED, socket->name, socket) < 0) { 5158c2ecf20Sopenharmony_ci ret = -EBUSY; 5168c2ecf20Sopenharmony_ci goto unregister; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci printk(KERN_INFO "%s at %#08lx, IRQ %d\n", socket->name, start, dev->irq); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci return 0; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ciunregister: 5248c2ecf20Sopenharmony_ci pcmcia_unregister_socket(socket->pcmcia_socket); 5258c2ecf20Sopenharmony_ci socket->pcmcia_socket = NULL; 5268c2ecf20Sopenharmony_ciunmap: 5278c2ecf20Sopenharmony_ci iounmap(socket->base); 5288c2ecf20Sopenharmony_ci socket->base = NULL; 5298c2ecf20Sopenharmony_cirelease: 5308c2ecf20Sopenharmony_ci pci_release_regions(dev); 5318c2ecf20Sopenharmony_cidisable: 5328c2ecf20Sopenharmony_ci pci_disable_device(dev); 5338c2ecf20Sopenharmony_ci return ret; 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_cistatic int vrc4173_cardu_setup(char *options) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci if (options == NULL || *options == '\0') 5398c2ecf20Sopenharmony_ci return 1; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci if (strncmp(options, "cardu1:", 7) == 0) { 5428c2ecf20Sopenharmony_ci options += 7; 5438c2ecf20Sopenharmony_ci if (*options != '\0') { 5448c2ecf20Sopenharmony_ci if (strncmp(options, "noprobe", 7) == 0) { 5458c2ecf20Sopenharmony_ci cardu_sockets[CARDU1].noprobe = 1; 5468c2ecf20Sopenharmony_ci options += 7; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (*options != ',') 5508c2ecf20Sopenharmony_ci return 1; 5518c2ecf20Sopenharmony_ci } else 5528c2ecf20Sopenharmony_ci return 1; 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci if (strncmp(options, "cardu2:", 7) == 0) { 5568c2ecf20Sopenharmony_ci options += 7; 5578c2ecf20Sopenharmony_ci if ((*options != '\0') && (strncmp(options, "noprobe", 7) == 0)) 5588c2ecf20Sopenharmony_ci cardu_sockets[CARDU2].noprobe = 1; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci return 1; 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci__setup("vrc4173_cardu=", vrc4173_cardu_setup); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic const struct pci_device_id vrc4173_cardu_id_table[] = { 5678c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_NAPCCARD) }, 5688c2ecf20Sopenharmony_ci {0, } 5698c2ecf20Sopenharmony_ci}; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_cistatic struct pci_driver vrc4173_cardu_driver = { 5728c2ecf20Sopenharmony_ci .name = "NEC VRC4173 CARDU", 5738c2ecf20Sopenharmony_ci .probe = vrc4173_cardu_probe, 5748c2ecf20Sopenharmony_ci .id_table = vrc4173_cardu_id_table, 5758c2ecf20Sopenharmony_ci}; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_cistatic int vrc4173_cardu_init(void) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci vrc4173_cardu_slots = 0; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci return pci_register_driver(&vrc4173_cardu_driver); 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_cistatic void vrc4173_cardu_exit(void) 5858c2ecf20Sopenharmony_ci{ 5868c2ecf20Sopenharmony_ci pci_unregister_driver(&vrc4173_cardu_driver); 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cimodule_init(vrc4173_cardu_init); 5908c2ecf20Sopenharmony_cimodule_exit(vrc4173_cardu_exit); 5918c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, vrc4173_cardu_id_table); 592