162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * drivers/pcmcia/sa1100_h3600.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * PCMCIA implementation routines for H3600 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/device.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/gpio.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <mach/hardware.h> 1762306a36Sopenharmony_ci#include <asm/irq.h> 1862306a36Sopenharmony_ci#include <asm/mach-types.h> 1962306a36Sopenharmony_ci#include <mach/h3xxx.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "sa1100_generic.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic int h3600_pcmcia_hw_init(struct soc_pcmcia_socket *skt) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci int err; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci skt->stat[SOC_STAT_CD].name = skt->nr ? "pcmcia1-detect" : "pcmcia0-detect"; 2862306a36Sopenharmony_ci skt->stat[SOC_STAT_RDY].name = skt->nr ? "pcmcia1-ready" : "pcmcia0-ready"; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci err = soc_pcmcia_request_gpiods(skt); 3162306a36Sopenharmony_ci if (err) 3262306a36Sopenharmony_ci return err; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci switch (skt->nr) { 3562306a36Sopenharmony_ci case 0: 3662306a36Sopenharmony_ci err = gpio_request(H3XXX_EGPIO_OPT_NVRAM_ON, "OPT NVRAM ON"); 3762306a36Sopenharmony_ci if (err) 3862306a36Sopenharmony_ci goto err01; 3962306a36Sopenharmony_ci err = gpio_direction_output(H3XXX_EGPIO_OPT_NVRAM_ON, 0); 4062306a36Sopenharmony_ci if (err) 4162306a36Sopenharmony_ci goto err03; 4262306a36Sopenharmony_ci err = gpio_request(H3XXX_EGPIO_OPT_ON, "OPT ON"); 4362306a36Sopenharmony_ci if (err) 4462306a36Sopenharmony_ci goto err03; 4562306a36Sopenharmony_ci err = gpio_direction_output(H3XXX_EGPIO_OPT_ON, 0); 4662306a36Sopenharmony_ci if (err) 4762306a36Sopenharmony_ci goto err04; 4862306a36Sopenharmony_ci err = gpio_request(H3XXX_EGPIO_OPT_RESET, "OPT RESET"); 4962306a36Sopenharmony_ci if (err) 5062306a36Sopenharmony_ci goto err04; 5162306a36Sopenharmony_ci err = gpio_direction_output(H3XXX_EGPIO_OPT_RESET, 0); 5262306a36Sopenharmony_ci if (err) 5362306a36Sopenharmony_ci goto err05; 5462306a36Sopenharmony_ci err = gpio_request(H3XXX_EGPIO_CARD_RESET, "PCMCIA CARD RESET"); 5562306a36Sopenharmony_ci if (err) 5662306a36Sopenharmony_ci goto err05; 5762306a36Sopenharmony_ci err = gpio_direction_output(H3XXX_EGPIO_CARD_RESET, 0); 5862306a36Sopenharmony_ci if (err) 5962306a36Sopenharmony_ci goto err06; 6062306a36Sopenharmony_ci break; 6162306a36Sopenharmony_ci case 1: 6262306a36Sopenharmony_ci break; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci return 0; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cierr06: gpio_free(H3XXX_EGPIO_CARD_RESET); 6762306a36Sopenharmony_cierr05: gpio_free(H3XXX_EGPIO_OPT_RESET); 6862306a36Sopenharmony_cierr04: gpio_free(H3XXX_EGPIO_OPT_ON); 6962306a36Sopenharmony_cierr03: gpio_free(H3XXX_EGPIO_OPT_NVRAM_ON); 7062306a36Sopenharmony_cierr01: gpio_free(H3XXX_GPIO_PCMCIA_IRQ0); 7162306a36Sopenharmony_ci return err; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic void h3600_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci switch (skt->nr) { 7762306a36Sopenharmony_ci case 0: 7862306a36Sopenharmony_ci /* Disable CF bus: */ 7962306a36Sopenharmony_ci gpio_set_value(H3XXX_EGPIO_OPT_NVRAM_ON, 0); 8062306a36Sopenharmony_ci gpio_set_value(H3XXX_EGPIO_OPT_ON, 0); 8162306a36Sopenharmony_ci gpio_set_value(H3XXX_EGPIO_OPT_RESET, 1); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci gpio_free(H3XXX_EGPIO_CARD_RESET); 8462306a36Sopenharmony_ci gpio_free(H3XXX_EGPIO_OPT_RESET); 8562306a36Sopenharmony_ci gpio_free(H3XXX_EGPIO_OPT_ON); 8662306a36Sopenharmony_ci gpio_free(H3XXX_EGPIO_OPT_NVRAM_ON); 8762306a36Sopenharmony_ci break; 8862306a36Sopenharmony_ci case 1: 8962306a36Sopenharmony_ci break; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic void 9462306a36Sopenharmony_cih3600_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci state->bvd1 = 0; 9762306a36Sopenharmony_ci state->bvd2 = 0; 9862306a36Sopenharmony_ci state->vs_3v = 0; 9962306a36Sopenharmony_ci state->vs_Xv = 0; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic int 10362306a36Sopenharmony_cih3600_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state_t *state) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci if (state->Vcc != 0 && state->Vcc != 33 && state->Vcc != 50) { 10662306a36Sopenharmony_ci printk(KERN_ERR "h3600_pcmcia: unrecognized Vcc %u.%uV\n", 10762306a36Sopenharmony_ci state->Vcc / 10, state->Vcc % 10); 10862306a36Sopenharmony_ci return -1; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci gpio_set_value(H3XXX_EGPIO_CARD_RESET, !!(state->flags & SS_RESET)); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* Silently ignore Vpp, output enable, speaker enable. */ 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return 0; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic void h3600_pcmcia_socket_init(struct soc_pcmcia_socket *skt) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci /* Enable CF bus: */ 12162306a36Sopenharmony_ci gpio_set_value(H3XXX_EGPIO_OPT_NVRAM_ON, 1); 12262306a36Sopenharmony_ci gpio_set_value(H3XXX_EGPIO_OPT_ON, 1); 12362306a36Sopenharmony_ci gpio_set_value(H3XXX_EGPIO_OPT_RESET, 0); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci msleep(10); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic void h3600_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci /* 13162306a36Sopenharmony_ci * FIXME: This doesn't fit well. We don't have the mechanism in 13262306a36Sopenharmony_ci * the generic PCMCIA layer to deal with the idea of two sockets 13362306a36Sopenharmony_ci * on one bus. We rely on the cs.c behaviour shutting down 13462306a36Sopenharmony_ci * socket 0 then socket 1. 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_ci if (skt->nr == 1) { 13762306a36Sopenharmony_ci gpio_set_value(H3XXX_EGPIO_OPT_ON, 0); 13862306a36Sopenharmony_ci gpio_set_value(H3XXX_EGPIO_OPT_NVRAM_ON, 0); 13962306a36Sopenharmony_ci /* hmm, does this suck power? */ 14062306a36Sopenharmony_ci gpio_set_value(H3XXX_EGPIO_OPT_RESET, 1); 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistruct pcmcia_low_level h3600_pcmcia_ops = { 14562306a36Sopenharmony_ci .owner = THIS_MODULE, 14662306a36Sopenharmony_ci .hw_init = h3600_pcmcia_hw_init, 14762306a36Sopenharmony_ci .hw_shutdown = h3600_pcmcia_hw_shutdown, 14862306a36Sopenharmony_ci .socket_state = h3600_pcmcia_socket_state, 14962306a36Sopenharmony_ci .configure_socket = h3600_pcmcia_configure_socket, 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci .socket_init = h3600_pcmcia_socket_init, 15262306a36Sopenharmony_ci .socket_suspend = h3600_pcmcia_socket_suspend, 15362306a36Sopenharmony_ci}; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ciint pcmcia_h3600_init(struct device *dev) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci int ret = -ENODEV; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (machine_is_h3600()) 16062306a36Sopenharmony_ci ret = sa11xx_drv_pcmcia_probe(dev, &h3600_pcmcia_ops, 0, 2); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci return ret; 16362306a36Sopenharmony_ci} 164