18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Viper/Zeus PCMCIA support 38c2ecf20Sopenharmony_ci * Copyright 2004 Arcom Control Systems 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Maintained by Marc Zyngier <maz@misterjones.org> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based on: 88c2ecf20Sopenharmony_ci * iPAQ h2200 PCMCIA support 98c2ecf20Sopenharmony_ci * Copyright 2004 Koen Kooi <koen@vestingbar.nl> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 128c2ecf20Sopenharmony_ci * License. See the file COPYING in the main directory of this archive for 138c2ecf20Sopenharmony_ci * more details. 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/init.h> 188c2ecf20Sopenharmony_ci#include <linux/kernel.h> 198c2ecf20Sopenharmony_ci#include <linux/errno.h> 208c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 228c2ecf20Sopenharmony_ci#include <linux/gpio.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <pcmcia/ss.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <asm/irq.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <linux/platform_data/pcmcia-pxa2xx_viper.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include "soc_common.h" 318c2ecf20Sopenharmony_ci#include "pxa2xx_base.h" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic struct platform_device *arcom_pcmcia_dev; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic inline struct arcom_pcmcia_pdata *viper_get_pdata(void) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci return arcom_pcmcia_dev->dev.platform_data; 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int viper_pcmcia_hw_init(struct soc_pcmcia_socket *skt) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct arcom_pcmcia_pdata *pdata = viper_get_pdata(); 438c2ecf20Sopenharmony_ci unsigned long flags; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci skt->stat[SOC_STAT_CD].gpio = pdata->cd_gpio; 468c2ecf20Sopenharmony_ci skt->stat[SOC_STAT_CD].name = "PCMCIA_CD"; 478c2ecf20Sopenharmony_ci skt->stat[SOC_STAT_RDY].gpio = pdata->rdy_gpio; 488c2ecf20Sopenharmony_ci skt->stat[SOC_STAT_RDY].name = "CF ready"; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (gpio_request(pdata->pwr_gpio, "CF power")) 518c2ecf20Sopenharmony_ci goto err_request_pwr; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci local_irq_save(flags); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (gpio_direction_output(pdata->pwr_gpio, 0)) { 568c2ecf20Sopenharmony_ci local_irq_restore(flags); 578c2ecf20Sopenharmony_ci goto err_dir; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci local_irq_restore(flags); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return 0; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cierr_dir: 658c2ecf20Sopenharmony_ci gpio_free(pdata->pwr_gpio); 668c2ecf20Sopenharmony_cierr_request_pwr: 678c2ecf20Sopenharmony_ci dev_err(&arcom_pcmcia_dev->dev, "Failed to setup PCMCIA GPIOs\n"); 688c2ecf20Sopenharmony_ci return -1; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* 728c2ecf20Sopenharmony_ci * Release all resources. 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_cistatic void viper_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci struct arcom_pcmcia_pdata *pdata = viper_get_pdata(); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci gpio_free(pdata->pwr_gpio); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic void viper_pcmcia_socket_state(struct soc_pcmcia_socket *skt, 828c2ecf20Sopenharmony_ci struct pcmcia_state *state) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci state->vs_3v = 1; /* Can only apply 3.3V */ 858c2ecf20Sopenharmony_ci state->vs_Xv = 0; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic int viper_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, 898c2ecf20Sopenharmony_ci const socket_state_t *state) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct arcom_pcmcia_pdata *pdata = viper_get_pdata(); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* Silently ignore Vpp, output enable, speaker enable. */ 948c2ecf20Sopenharmony_ci pdata->reset(state->flags & SS_RESET); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* Apply socket voltage */ 978c2ecf20Sopenharmony_ci switch (state->Vcc) { 988c2ecf20Sopenharmony_ci case 0: 998c2ecf20Sopenharmony_ci gpio_set_value(pdata->pwr_gpio, 0); 1008c2ecf20Sopenharmony_ci break; 1018c2ecf20Sopenharmony_ci case 33: 1028c2ecf20Sopenharmony_ci gpio_set_value(pdata->pwr_gpio, 1); 1038c2ecf20Sopenharmony_ci break; 1048c2ecf20Sopenharmony_ci default: 1058c2ecf20Sopenharmony_ci dev_err(&arcom_pcmcia_dev->dev, "Unsupported Vcc:%d\n", state->Vcc); 1068c2ecf20Sopenharmony_ci return -1; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci return 0; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic struct pcmcia_low_level viper_pcmcia_ops = { 1138c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1148c2ecf20Sopenharmony_ci .hw_init = viper_pcmcia_hw_init, 1158c2ecf20Sopenharmony_ci .hw_shutdown = viper_pcmcia_hw_shutdown, 1168c2ecf20Sopenharmony_ci .socket_state = viper_pcmcia_socket_state, 1178c2ecf20Sopenharmony_ci .configure_socket = viper_pcmcia_configure_socket, 1188c2ecf20Sopenharmony_ci .nr = 1, 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic struct platform_device *viper_pcmcia_device; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int viper_pcmcia_probe(struct platform_device *pdev) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci int ret; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* I can't imagine more than one device, but you never know... */ 1288c2ecf20Sopenharmony_ci if (arcom_pcmcia_dev) 1298c2ecf20Sopenharmony_ci return -EEXIST; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (!pdev->dev.platform_data) 1328c2ecf20Sopenharmony_ci return -EINVAL; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci viper_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1); 1358c2ecf20Sopenharmony_ci if (!viper_pcmcia_device) 1368c2ecf20Sopenharmony_ci return -ENOMEM; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci arcom_pcmcia_dev = pdev; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci viper_pcmcia_device->dev.parent = &pdev->dev; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci ret = platform_device_add_data(viper_pcmcia_device, 1438c2ecf20Sopenharmony_ci &viper_pcmcia_ops, 1448c2ecf20Sopenharmony_ci sizeof(viper_pcmcia_ops)); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (!ret) 1478c2ecf20Sopenharmony_ci ret = platform_device_add(viper_pcmcia_device); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (ret) { 1508c2ecf20Sopenharmony_ci platform_device_put(viper_pcmcia_device); 1518c2ecf20Sopenharmony_ci arcom_pcmcia_dev = NULL; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci return ret; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic int viper_pcmcia_remove(struct platform_device *pdev) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci platform_device_unregister(viper_pcmcia_device); 1608c2ecf20Sopenharmony_ci arcom_pcmcia_dev = NULL; 1618c2ecf20Sopenharmony_ci return 0; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic struct platform_device_id viper_pcmcia_id_table[] = { 1658c2ecf20Sopenharmony_ci { .name = "viper-pcmcia", }, 1668c2ecf20Sopenharmony_ci { .name = "zeus-pcmcia", }, 1678c2ecf20Sopenharmony_ci { }, 1688c2ecf20Sopenharmony_ci}; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic struct platform_driver viper_pcmcia_driver = { 1718c2ecf20Sopenharmony_ci .probe = viper_pcmcia_probe, 1728c2ecf20Sopenharmony_ci .remove = viper_pcmcia_remove, 1738c2ecf20Sopenharmony_ci .driver = { 1748c2ecf20Sopenharmony_ci .name = "arcom-pcmcia", 1758c2ecf20Sopenharmony_ci }, 1768c2ecf20Sopenharmony_ci .id_table = viper_pcmcia_id_table, 1778c2ecf20Sopenharmony_ci}; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cimodule_platform_driver(viper_pcmcia_driver); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(platform, viper_pcmcia_id_table); 1828c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 183