1/* 2 * linux/drivers/pcmcia/pxa2xx_trizeps4.c 3 * 4 * TRIZEPS PCMCIA specific routines. 5 * 6 * Author: J��rgen Schindele 7 * Created: 20 02, 2006 8 * Copyright: J��rgen Schindele 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13 */ 14 15#include <linux/module.h> 16#include <linux/init.h> 17#include <linux/kernel.h> 18#include <linux/gpio.h> 19#include <linux/interrupt.h> 20#include <linux/platform_device.h> 21 22#include <asm/mach-types.h> 23#include <asm/irq.h> 24 25#include <mach/pxa2xx-regs.h> 26#include <mach/trizeps4.h> 27 28#include "soc_common.h" 29 30extern void board_pcmcia_power(int power); 31 32static struct pcmcia_irqs irqs[] = { 33 { 0, IRQ_GPIO(GPIO_PCD), "cs0_cd" } 34 /* on other baseboards we can have more inputs */ 35}; 36 37static int trizeps_pcmcia_hw_init(struct soc_pcmcia_socket *skt) 38{ 39 int ret, i; 40 /* we dont have voltage/card/ready detection 41 * so we dont need interrupts for it 42 */ 43 switch (skt->nr) { 44 case 0: 45 if (gpio_request(GPIO_PRDY, "cf_irq") < 0) { 46 pr_err("%s: sock %d unable to request gpio %d\n", __func__, 47 skt->nr, GPIO_PRDY); 48 return -EBUSY; 49 } 50 if (gpio_direction_input(GPIO_PRDY) < 0) { 51 pr_err("%s: sock %d unable to set input gpio %d\n", __func__, 52 skt->nr, GPIO_PRDY); 53 gpio_free(GPIO_PRDY); 54 return -EINVAL; 55 } 56 skt->socket.pci_irq = IRQ_GPIO(GPIO_PRDY); 57 break; 58 59#ifndef CONFIG_MACH_TRIZEPS_CONXS 60 case 1: 61#endif 62 default: 63 break; 64 } 65 /* release the reset of this card */ 66 pr_debug("%s: sock %d irq %d\n", __func__, skt->nr, skt->socket.pci_irq); 67 68 /* supplementory irqs for the socket */ 69 for (i = 0; i < ARRAY_SIZE(irqs); i++) { 70 if (irqs[i].sock != skt->nr) 71 continue; 72 if (gpio_request(IRQ_TO_GPIO(irqs[i].irq), irqs[i].str) < 0) { 73 pr_err("%s: sock %d unable to request gpio %d\n", 74 __func__, skt->nr, IRQ_TO_GPIO(irqs[i].irq)); 75 ret = -EBUSY; 76 goto error; 77 } 78 if (gpio_direction_input(IRQ_TO_GPIO(irqs[i].irq)) < 0) { 79 pr_err("%s: sock %d unable to set input gpio %d\n", 80 __func__, skt->nr, IRQ_TO_GPIO(irqs[i].irq)); 81 ret = -EINVAL; 82 goto error; 83 } 84 } 85 return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); 86 87error: 88 for (; i >= 0; i--) { 89 gpio_free(IRQ_TO_GPIO(irqs[i].irq)); 90 } 91 return (ret); 92} 93 94static void trizeps_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) 95{ 96 int i; 97 /* free allocated gpio's */ 98 gpio_free(GPIO_PRDY); 99 for (i = 0; i < ARRAY_SIZE(irqs); i++) 100 gpio_free(IRQ_TO_GPIO(irqs[i].irq)); 101} 102 103static unsigned long trizeps_pcmcia_status[2]; 104 105static void trizeps_pcmcia_socket_state(struct soc_pcmcia_socket *skt, 106 struct pcmcia_state *state) 107{ 108 unsigned short status = 0, change; 109 status = CFSR_readw(); 110 change = (status ^ trizeps_pcmcia_status[skt->nr]) & 111 ConXS_CFSR_BVD_MASK; 112 if (change) { 113 trizeps_pcmcia_status[skt->nr] = status; 114 if (status & ConXS_CFSR_BVD1) { 115 /* enable_irq empty */ 116 } else { 117 /* disable_irq empty */ 118 } 119 } 120 121 switch (skt->nr) { 122 case 0: 123 /* just fill in fix states */ 124 state->detect = gpio_get_value(GPIO_PCD) ? 0 : 1; 125 state->ready = gpio_get_value(GPIO_PRDY) ? 1 : 0; 126 state->bvd1 = (status & ConXS_CFSR_BVD1) ? 1 : 0; 127 state->bvd2 = (status & ConXS_CFSR_BVD2) ? 1 : 0; 128 state->vs_3v = (status & ConXS_CFSR_VS1) ? 0 : 1; 129 state->vs_Xv = (status & ConXS_CFSR_VS2) ? 0 : 1; 130 state->wrprot = 0; /* not available */ 131 break; 132 133#ifndef CONFIG_MACH_TRIZEPS_CONXS 134 /* on ConXS we only have one slot. Second is inactive */ 135 case 1: 136 state->detect = 0; 137 state->ready = 0; 138 state->bvd1 = 0; 139 state->bvd2 = 0; 140 state->vs_3v = 0; 141 state->vs_Xv = 0; 142 state->wrprot = 0; 143 break; 144 145#endif 146 } 147} 148 149static int trizeps_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, 150 const socket_state_t *state) 151{ 152 int ret = 0; 153 unsigned short power = 0; 154 155 /* we do nothing here just check a bit */ 156 switch (state->Vcc) { 157 case 0: power &= 0xfc; break; 158 case 33: power |= ConXS_BCR_S0_VCC_3V3; break; 159 case 50: 160 pr_err("%s(): Vcc 5V not supported in socket\n", __func__); 161 break; 162 default: 163 pr_err("%s(): bad Vcc %u\n", __func__, state->Vcc); 164 ret = -1; 165 } 166 167 switch (state->Vpp) { 168 case 0: power &= 0xf3; break; 169 case 33: power |= ConXS_BCR_S0_VPP_3V3; break; 170 case 120: 171 pr_err("%s(): Vpp 12V not supported in socket\n", __func__); 172 break; 173 default: 174 if (state->Vpp != state->Vcc) { 175 pr_err("%s(): bad Vpp %u\n", __func__, state->Vpp); 176 ret = -1; 177 } 178 } 179 180 switch (skt->nr) { 181 case 0: /* we only have 3.3V */ 182 board_pcmcia_power(power); 183 break; 184 185#ifndef CONFIG_MACH_TRIZEPS_CONXS 186 /* on ConXS we only have one slot. Second is inactive */ 187 case 1: 188#endif 189 default: 190 break; 191 } 192 193 return ret; 194} 195 196static void trizeps_pcmcia_socket_init(struct soc_pcmcia_socket *skt) 197{ 198 /* default is on */ 199 board_pcmcia_power(0x9); 200} 201 202static void trizeps_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) 203{ 204 board_pcmcia_power(0x0); 205} 206 207static struct pcmcia_low_level trizeps_pcmcia_ops = { 208 .owner = THIS_MODULE, 209 .hw_init = trizeps_pcmcia_hw_init, 210 .hw_shutdown = trizeps_pcmcia_hw_shutdown, 211 .socket_state = trizeps_pcmcia_socket_state, 212 .configure_socket = trizeps_pcmcia_configure_socket, 213 .socket_init = trizeps_pcmcia_socket_init, 214 .socket_suspend = trizeps_pcmcia_socket_suspend, 215#ifdef CONFIG_MACH_TRIZEPS_CONXS 216 .nr = 1, 217#else 218 .nr = 2, 219#endif 220 .first = 0, 221}; 222 223static struct platform_device *trizeps_pcmcia_device; 224 225static int __init trizeps_pcmcia_init(void) 226{ 227 int ret; 228 229 trizeps_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1); 230 if (!trizeps_pcmcia_device) 231 return -ENOMEM; 232 233 ret = platform_device_add_data(trizeps_pcmcia_device, 234 &trizeps_pcmcia_ops, sizeof(trizeps_pcmcia_ops)); 235 236 if (ret == 0) 237 ret = platform_device_add(trizeps_pcmcia_device); 238 239 if (ret) 240 platform_device_put(trizeps_pcmcia_device); 241 242 return ret; 243} 244 245static void __exit trizeps_pcmcia_exit(void) 246{ 247 platform_device_unregister(trizeps_pcmcia_device); 248} 249 250fs_initcall(trizeps_pcmcia_init); 251module_exit(trizeps_pcmcia_exit); 252 253MODULE_LICENSE("GPL"); 254MODULE_AUTHOR("Juergen Schindele"); 255MODULE_ALIAS("platform:pxa2xx-pcmcia"); 256