18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * linux/drivers/pcmcia/pxa2xx_balloon3.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Balloon3 PCMCIA specific routines.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  Author:	Nick Bane
88c2ecf20Sopenharmony_ci *  Created:	June, 2006
98c2ecf20Sopenharmony_ci *  Copyright:	Toby Churchill Ltd
108c2ecf20Sopenharmony_ci *  Derived from pxa2xx_mainstone.c, by Nico Pitre
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * Various modification by Marek Vasut <marek.vasut@gmail.com>
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/gpio.h>
178c2ecf20Sopenharmony_ci#include <linux/errno.h>
188c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
198c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
208c2ecf20Sopenharmony_ci#include <linux/irq.h>
218c2ecf20Sopenharmony_ci#include <linux/io.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <mach/balloon3.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <asm/mach-types.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include "soc_common.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic int balloon3_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	uint16_t ver;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	ver = __raw_readw(BALLOON3_FPGA_VER);
348c2ecf20Sopenharmony_ci	if (ver < 0x4f08)
358c2ecf20Sopenharmony_ci		pr_warn("The FPGA code, version 0x%04x, is too old. "
368c2ecf20Sopenharmony_ci			"PCMCIA/CF support might be broken in this version!",
378c2ecf20Sopenharmony_ci			ver);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	skt->socket.pci_irq = BALLOON3_BP_CF_NRDY_IRQ;
408c2ecf20Sopenharmony_ci	skt->stat[SOC_STAT_CD].gpio = BALLOON3_GPIO_S0_CD;
418c2ecf20Sopenharmony_ci	skt->stat[SOC_STAT_CD].name = "PCMCIA0 CD";
428c2ecf20Sopenharmony_ci	skt->stat[SOC_STAT_BVD1].irq = BALLOON3_BP_NSTSCHG_IRQ;
438c2ecf20Sopenharmony_ci	skt->stat[SOC_STAT_BVD1].name = "PCMCIA0 STSCHG";
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	return 0;
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic unsigned long balloon3_pcmcia_status[2] = {
498c2ecf20Sopenharmony_ci	BALLOON3_CF_nSTSCHG_BVD1,
508c2ecf20Sopenharmony_ci	BALLOON3_CF_nSTSCHG_BVD1
518c2ecf20Sopenharmony_ci};
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic void balloon3_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
548c2ecf20Sopenharmony_ci				    struct pcmcia_state *state)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	uint16_t status;
578c2ecf20Sopenharmony_ci	int flip;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	/* This actually reads the STATUS register */
608c2ecf20Sopenharmony_ci	status = __raw_readw(BALLOON3_CF_STATUS_REG);
618c2ecf20Sopenharmony_ci	flip = (status ^ balloon3_pcmcia_status[skt->nr])
628c2ecf20Sopenharmony_ci		& BALLOON3_CF_nSTSCHG_BVD1;
638c2ecf20Sopenharmony_ci	/*
648c2ecf20Sopenharmony_ci	 * Workaround for STSCHG which can't be deasserted:
658c2ecf20Sopenharmony_ci	 * We therefore disable/enable corresponding IRQs
668c2ecf20Sopenharmony_ci	 * as needed to avoid IRQ locks.
678c2ecf20Sopenharmony_ci	 */
688c2ecf20Sopenharmony_ci	if (flip) {
698c2ecf20Sopenharmony_ci		balloon3_pcmcia_status[skt->nr] = status;
708c2ecf20Sopenharmony_ci		if (status & BALLOON3_CF_nSTSCHG_BVD1)
718c2ecf20Sopenharmony_ci			enable_irq(BALLOON3_BP_NSTSCHG_IRQ);
728c2ecf20Sopenharmony_ci		else
738c2ecf20Sopenharmony_ci			disable_irq(BALLOON3_BP_NSTSCHG_IRQ);
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	state->ready	= !!(status & BALLOON3_CF_nIRQ);
778c2ecf20Sopenharmony_ci	state->bvd1	= !!(status & BALLOON3_CF_nSTSCHG_BVD1);
788c2ecf20Sopenharmony_ci	state->bvd2	= 0;	/* not available */
798c2ecf20Sopenharmony_ci	state->vs_3v	= 1;	/* Always true its a CF card */
808c2ecf20Sopenharmony_ci	state->vs_Xv	= 0;	/* not available */
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic int balloon3_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
848c2ecf20Sopenharmony_ci				       const socket_state_t *state)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	__raw_writew(BALLOON3_CF_RESET, BALLOON3_CF_CONTROL_REG +
878c2ecf20Sopenharmony_ci			((state->flags & SS_RESET) ?
888c2ecf20Sopenharmony_ci			BALLOON3_FPGA_SETnCLR : 0));
898c2ecf20Sopenharmony_ci	return 0;
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic struct pcmcia_low_level balloon3_pcmcia_ops = {
938c2ecf20Sopenharmony_ci	.owner			= THIS_MODULE,
948c2ecf20Sopenharmony_ci	.hw_init		= balloon3_pcmcia_hw_init,
958c2ecf20Sopenharmony_ci	.socket_state		= balloon3_pcmcia_socket_state,
968c2ecf20Sopenharmony_ci	.configure_socket	= balloon3_pcmcia_configure_socket,
978c2ecf20Sopenharmony_ci	.first			= 0,
988c2ecf20Sopenharmony_ci	.nr			= 1,
998c2ecf20Sopenharmony_ci};
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic struct platform_device *balloon3_pcmcia_device;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic int __init balloon3_pcmcia_init(void)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	int ret;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	if (!machine_is_balloon3())
1088c2ecf20Sopenharmony_ci		return -ENODEV;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	balloon3_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);
1118c2ecf20Sopenharmony_ci	if (!balloon3_pcmcia_device)
1128c2ecf20Sopenharmony_ci		return -ENOMEM;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	ret = platform_device_add_data(balloon3_pcmcia_device,
1158c2ecf20Sopenharmony_ci			&balloon3_pcmcia_ops, sizeof(balloon3_pcmcia_ops));
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	if (!ret)
1188c2ecf20Sopenharmony_ci		ret = platform_device_add(balloon3_pcmcia_device);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	if (ret)
1218c2ecf20Sopenharmony_ci		platform_device_put(balloon3_pcmcia_device);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	return ret;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic void __exit balloon3_pcmcia_exit(void)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	platform_device_unregister(balloon3_pcmcia_device);
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cimodule_init(balloon3_pcmcia_init);
1328c2ecf20Sopenharmony_cimodule_exit(balloon3_pcmcia_exit);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1358c2ecf20Sopenharmony_ciMODULE_AUTHOR("Nick Bane <nick@cecomputing.co.uk>");
1368c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:pxa2xx-pcmcia");
1378c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Balloon3 board CF/PCMCIA driver");
138