18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Copyright (C) 2012 Paul Parsons <lost.distance@yahoo.com>
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/module.h>
78c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
88c2ecf20Sopenharmony_ci#include <linux/err.h>
98c2ecf20Sopenharmony_ci#include <linux/gpio.h>
108c2ecf20Sopenharmony_ci#include <linux/irq.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <asm/mach-types.h>
138c2ecf20Sopenharmony_ci#include <mach/hx4700.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include "soc_common.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic struct gpio gpios[] = {
188c2ecf20Sopenharmony_ci	{ GPIO114_HX4700_CF_RESET,    GPIOF_OUT_INIT_LOW,   "CF reset"        },
198c2ecf20Sopenharmony_ci	{ EGPIO4_CF_3V3_ON,           GPIOF_OUT_INIT_LOW,   "CF 3.3V enable"  },
208c2ecf20Sopenharmony_ci};
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic int hx4700_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	int ret;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	ret = gpio_request_array(gpios, ARRAY_SIZE(gpios));
278c2ecf20Sopenharmony_ci	if (ret)
288c2ecf20Sopenharmony_ci		goto out;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	/*
318c2ecf20Sopenharmony_ci	 * IRQ type must be set before soc_pcmcia_hw_init() calls request_irq().
328c2ecf20Sopenharmony_ci	 * The asic3 default IRQ type is level trigger low level detect, exactly
338c2ecf20Sopenharmony_ci	 * the the signal present on GPIOD4_CF_nCD when a CF card is inserted.
348c2ecf20Sopenharmony_ci	 * If the IRQ type is not changed, the asic3 interrupt handler will loop
358c2ecf20Sopenharmony_ci	 * repeatedly because it is unable to clear the level trigger interrupt.
368c2ecf20Sopenharmony_ci	 */
378c2ecf20Sopenharmony_ci	irq_set_irq_type(gpio_to_irq(GPIOD4_CF_nCD), IRQ_TYPE_EDGE_BOTH);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	skt->stat[SOC_STAT_CD].gpio = GPIOD4_CF_nCD;
408c2ecf20Sopenharmony_ci	skt->stat[SOC_STAT_CD].name = "PCMCIA CD";
418c2ecf20Sopenharmony_ci	skt->stat[SOC_STAT_RDY].gpio = GPIO60_HX4700_CF_RNB;
428c2ecf20Sopenharmony_ci	skt->stat[SOC_STAT_RDY].name = "PCMCIA Ready";
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ciout:
458c2ecf20Sopenharmony_ci	return ret;
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic void hx4700_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	gpio_free_array(gpios, ARRAY_SIZE(gpios));
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic void hx4700_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
548c2ecf20Sopenharmony_ci	struct pcmcia_state *state)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	state->vs_3v = 1;
578c2ecf20Sopenharmony_ci	state->vs_Xv = 0;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic int hx4700_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
618c2ecf20Sopenharmony_ci	const socket_state_t *state)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	switch (state->Vcc) {
648c2ecf20Sopenharmony_ci	case 0:
658c2ecf20Sopenharmony_ci		gpio_set_value(EGPIO4_CF_3V3_ON, 0);
668c2ecf20Sopenharmony_ci		break;
678c2ecf20Sopenharmony_ci	case 33:
688c2ecf20Sopenharmony_ci		gpio_set_value(EGPIO4_CF_3V3_ON, 1);
698c2ecf20Sopenharmony_ci		break;
708c2ecf20Sopenharmony_ci	default:
718c2ecf20Sopenharmony_ci		printk(KERN_ERR "pcmcia: Unsupported Vcc: %d\n", state->Vcc);
728c2ecf20Sopenharmony_ci		return -EINVAL;
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	gpio_set_value(GPIO114_HX4700_CF_RESET, (state->flags & SS_RESET) != 0);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	return 0;
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic struct pcmcia_low_level hx4700_pcmcia_ops = {
818c2ecf20Sopenharmony_ci	.owner          = THIS_MODULE,
828c2ecf20Sopenharmony_ci	.nr             = 1,
838c2ecf20Sopenharmony_ci	.hw_init        = hx4700_pcmcia_hw_init,
848c2ecf20Sopenharmony_ci	.hw_shutdown    = hx4700_pcmcia_hw_shutdown,
858c2ecf20Sopenharmony_ci	.socket_state   = hx4700_pcmcia_socket_state,
868c2ecf20Sopenharmony_ci	.configure_socket = hx4700_pcmcia_configure_socket,
878c2ecf20Sopenharmony_ci};
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic struct platform_device *hx4700_pcmcia_device;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic int __init hx4700_pcmcia_init(void)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	struct platform_device *pdev;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (!machine_is_h4700())
968c2ecf20Sopenharmony_ci		return -ENODEV;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	pdev = platform_device_register_data(NULL, "pxa2xx-pcmcia", -1,
998c2ecf20Sopenharmony_ci		&hx4700_pcmcia_ops, sizeof(hx4700_pcmcia_ops));
1008c2ecf20Sopenharmony_ci	if (IS_ERR(pdev))
1018c2ecf20Sopenharmony_ci		return PTR_ERR(pdev);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	hx4700_pcmcia_device = pdev;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	return 0;
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic void __exit hx4700_pcmcia_exit(void)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	platform_device_unregister(hx4700_pcmcia_device);
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cimodule_init(hx4700_pcmcia_init);
1148c2ecf20Sopenharmony_cimodule_exit(hx4700_pcmcia_exit);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ciMODULE_AUTHOR("Paul Parsons <lost.distance@yahoo.com>");
1178c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("HP iPAQ hx4700 PCMCIA driver");
1188c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
119