18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drivers/pcmcia/sa1100_h3600.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * PCMCIA implementation routines for H3600 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/device.h> 118c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/gpio.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <mach/hardware.h> 178c2ecf20Sopenharmony_ci#include <asm/irq.h> 188c2ecf20Sopenharmony_ci#include <asm/mach-types.h> 198c2ecf20Sopenharmony_ci#include <mach/h3xxx.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "sa1100_generic.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic int h3600_pcmcia_hw_init(struct soc_pcmcia_socket *skt) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci int err; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci skt->stat[SOC_STAT_CD].name = skt->nr ? "pcmcia1-detect" : "pcmcia0-detect"; 288c2ecf20Sopenharmony_ci skt->stat[SOC_STAT_RDY].name = skt->nr ? "pcmcia1-ready" : "pcmcia0-ready"; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci err = soc_pcmcia_request_gpiods(skt); 318c2ecf20Sopenharmony_ci if (err) 328c2ecf20Sopenharmony_ci return err; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci switch (skt->nr) { 358c2ecf20Sopenharmony_ci case 0: 368c2ecf20Sopenharmony_ci err = gpio_request(H3XXX_EGPIO_OPT_NVRAM_ON, "OPT NVRAM ON"); 378c2ecf20Sopenharmony_ci if (err) 388c2ecf20Sopenharmony_ci goto err01; 398c2ecf20Sopenharmony_ci err = gpio_direction_output(H3XXX_EGPIO_OPT_NVRAM_ON, 0); 408c2ecf20Sopenharmony_ci if (err) 418c2ecf20Sopenharmony_ci goto err03; 428c2ecf20Sopenharmony_ci err = gpio_request(H3XXX_EGPIO_OPT_ON, "OPT ON"); 438c2ecf20Sopenharmony_ci if (err) 448c2ecf20Sopenharmony_ci goto err03; 458c2ecf20Sopenharmony_ci err = gpio_direction_output(H3XXX_EGPIO_OPT_ON, 0); 468c2ecf20Sopenharmony_ci if (err) 478c2ecf20Sopenharmony_ci goto err04; 488c2ecf20Sopenharmony_ci err = gpio_request(H3XXX_EGPIO_OPT_RESET, "OPT RESET"); 498c2ecf20Sopenharmony_ci if (err) 508c2ecf20Sopenharmony_ci goto err04; 518c2ecf20Sopenharmony_ci err = gpio_direction_output(H3XXX_EGPIO_OPT_RESET, 0); 528c2ecf20Sopenharmony_ci if (err) 538c2ecf20Sopenharmony_ci goto err05; 548c2ecf20Sopenharmony_ci err = gpio_request(H3XXX_EGPIO_CARD_RESET, "PCMCIA CARD RESET"); 558c2ecf20Sopenharmony_ci if (err) 568c2ecf20Sopenharmony_ci goto err05; 578c2ecf20Sopenharmony_ci err = gpio_direction_output(H3XXX_EGPIO_CARD_RESET, 0); 588c2ecf20Sopenharmony_ci if (err) 598c2ecf20Sopenharmony_ci goto err06; 608c2ecf20Sopenharmony_ci break; 618c2ecf20Sopenharmony_ci case 1: 628c2ecf20Sopenharmony_ci break; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci return 0; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cierr06: gpio_free(H3XXX_EGPIO_CARD_RESET); 678c2ecf20Sopenharmony_cierr05: gpio_free(H3XXX_EGPIO_OPT_RESET); 688c2ecf20Sopenharmony_cierr04: gpio_free(H3XXX_EGPIO_OPT_ON); 698c2ecf20Sopenharmony_cierr03: gpio_free(H3XXX_EGPIO_OPT_NVRAM_ON); 708c2ecf20Sopenharmony_cierr01: gpio_free(H3XXX_GPIO_PCMCIA_IRQ0); 718c2ecf20Sopenharmony_ci return err; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic void h3600_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci switch (skt->nr) { 778c2ecf20Sopenharmony_ci case 0: 788c2ecf20Sopenharmony_ci /* Disable CF bus: */ 798c2ecf20Sopenharmony_ci gpio_set_value(H3XXX_EGPIO_OPT_NVRAM_ON, 0); 808c2ecf20Sopenharmony_ci gpio_set_value(H3XXX_EGPIO_OPT_ON, 0); 818c2ecf20Sopenharmony_ci gpio_set_value(H3XXX_EGPIO_OPT_RESET, 1); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci gpio_free(H3XXX_EGPIO_CARD_RESET); 848c2ecf20Sopenharmony_ci gpio_free(H3XXX_EGPIO_OPT_RESET); 858c2ecf20Sopenharmony_ci gpio_free(H3XXX_EGPIO_OPT_ON); 868c2ecf20Sopenharmony_ci gpio_free(H3XXX_EGPIO_OPT_NVRAM_ON); 878c2ecf20Sopenharmony_ci break; 888c2ecf20Sopenharmony_ci case 1: 898c2ecf20Sopenharmony_ci break; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic void 948c2ecf20Sopenharmony_cih3600_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci state->bvd1 = 0; 978c2ecf20Sopenharmony_ci state->bvd2 = 0; 988c2ecf20Sopenharmony_ci state->vs_3v = 0; 998c2ecf20Sopenharmony_ci state->vs_Xv = 0; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic int 1038c2ecf20Sopenharmony_cih3600_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci if (state->Vcc != 0 && state->Vcc != 33 && state->Vcc != 50) { 1068c2ecf20Sopenharmony_ci printk(KERN_ERR "h3600_pcmcia: unrecognized Vcc %u.%uV\n", 1078c2ecf20Sopenharmony_ci state->Vcc / 10, state->Vcc % 10); 1088c2ecf20Sopenharmony_ci return -1; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci gpio_set_value(H3XXX_EGPIO_CARD_RESET, !!(state->flags & SS_RESET)); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* Silently ignore Vpp, output enable, speaker enable. */ 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return 0; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic void h3600_pcmcia_socket_init(struct soc_pcmcia_socket *skt) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci /* Enable CF bus: */ 1218c2ecf20Sopenharmony_ci gpio_set_value(H3XXX_EGPIO_OPT_NVRAM_ON, 1); 1228c2ecf20Sopenharmony_ci gpio_set_value(H3XXX_EGPIO_OPT_ON, 1); 1238c2ecf20Sopenharmony_ci gpio_set_value(H3XXX_EGPIO_OPT_RESET, 0); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci msleep(10); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic void h3600_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci /* 1318c2ecf20Sopenharmony_ci * FIXME: This doesn't fit well. We don't have the mechanism in 1328c2ecf20Sopenharmony_ci * the generic PCMCIA layer to deal with the idea of two sockets 1338c2ecf20Sopenharmony_ci * on one bus. We rely on the cs.c behaviour shutting down 1348c2ecf20Sopenharmony_ci * socket 0 then socket 1. 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_ci if (skt->nr == 1) { 1378c2ecf20Sopenharmony_ci gpio_set_value(H3XXX_EGPIO_OPT_ON, 0); 1388c2ecf20Sopenharmony_ci gpio_set_value(H3XXX_EGPIO_OPT_NVRAM_ON, 0); 1398c2ecf20Sopenharmony_ci /* hmm, does this suck power? */ 1408c2ecf20Sopenharmony_ci gpio_set_value(H3XXX_EGPIO_OPT_RESET, 1); 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistruct pcmcia_low_level h3600_pcmcia_ops = { 1458c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1468c2ecf20Sopenharmony_ci .hw_init = h3600_pcmcia_hw_init, 1478c2ecf20Sopenharmony_ci .hw_shutdown = h3600_pcmcia_hw_shutdown, 1488c2ecf20Sopenharmony_ci .socket_state = h3600_pcmcia_socket_state, 1498c2ecf20Sopenharmony_ci .configure_socket = h3600_pcmcia_configure_socket, 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci .socket_init = h3600_pcmcia_socket_init, 1528c2ecf20Sopenharmony_ci .socket_suspend = h3600_pcmcia_socket_suspend, 1538c2ecf20Sopenharmony_ci}; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ciint pcmcia_h3600_init(struct device *dev) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci int ret = -ENODEV; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (machine_is_h3600() || machine_is_h3100()) 1608c2ecf20Sopenharmony_ci ret = sa11xx_drv_pcmcia_probe(dev, &h3600_pcmcia_ops, 0, 2); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return ret; 1638c2ecf20Sopenharmony_ci} 164