* [PATCH 1/1] pcmcia: gayle: initial support
2024-10-26 15:42 [PATCH 0/1] [RFC][NOT WORKING] amiga: pcmcia: inb()/inw() on GAYLE_IO space Paolo Pisati
@ 2024-10-26 15:42 ` Paolo Pisati
2024-11-02 2:02 ` [PATCH 0/1] [RFC][NOT WORKING] amiga: pcmcia: inb()/inw() on GAYLE_IO space Michael Schmitz
1 sibling, 0 replies; 4+ messages in thread
From: Paolo Pisati @ 2024-10-26 15:42 UTC (permalink / raw)
To: linux-m68k
Signed-off-by: Paolo Pisati <p.pisati@gmail.com>
---
arch/m68k/amiga/pcmcia.c | 5 +
arch/m68k/amiga/platform.c | 29 +++
arch/m68k/include/asm/amipcmcia.h | 11 ++
drivers/pcmcia/Kconfig | 8 +
drivers/pcmcia/Makefile | 1 +
drivers/pcmcia/gayle.c | 307 ++++++++++++++++++++++++++++++
6 files changed, 361 insertions(+)
create mode 100644 drivers/pcmcia/gayle.c
diff --git a/arch/m68k/amiga/pcmcia.c b/arch/m68k/amiga/pcmcia.c
index 7106f0c3639b..4bdd8ca1a481 100644
--- a/arch/m68k/amiga/pcmcia.c
+++ b/arch/m68k/amiga/pcmcia.c
@@ -66,6 +66,11 @@ int pcmcia_copy_tuple(unsigned char tuple_id, void *tuple, int max_len)
}
EXPORT_SYMBOL(pcmcia_copy_tuple);
+unsigned char pcmcia_get_voltage(void) {
+ return gayle.config & 0x03;
+}
+EXPORT_SYMBOL(pcmcia_get_voltage);
+
void pcmcia_program_voltage(int voltage)
{
unsigned char v;
diff --git a/arch/m68k/amiga/platform.c b/arch/m68k/amiga/platform.c
index d34029d7b058..2dd6c788e5a0 100644
--- a/arch/m68k/amiga/platform.c
+++ b/arch/m68k/amiga/platform.c
@@ -110,6 +110,26 @@ static const struct gayle_ide_platform_data a1200_ide_pdata __initconst = {
.explicit_ack = 1,
};
+static const struct resource a1200_pcmcia_resource[] __initconst = {
+ [0] = {
+ .name = "Gayle memory",
+ .start = GAYLE_RAM,
+ .end = GAYLE_RAM+GAYLE_RAMSIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .name = "Gayle attribute",
+ .start = GAYLE_ATTRIBUTE,
+ .end = GAYLE_ATTRIBUTE+GAYLE_ATTRIBUTESIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+ [2] = {
+ .name = "Gayle I/O",
+ .start = GAYLE_IO,
+ .end = GAYLE_IO+(2*GAYLE_IOSIZE)-1,
+ .flags = IORESOURCE_MEM,
+ }
+};
static const struct resource a4000_ide_resource __initconst = {
.start = 0xdd2000,
@@ -190,6 +210,15 @@ static int __init amiga_init_devices(void)
sizeof(a1200_ide_pdata));
if (error)
return error;
+
+ }
+
+ if (AMIGAHW_PRESENT(PCMCIA)) {
+ pdev = platform_device_register_simple("amiga-gayle-pcmcia", -1,
+ a1200_pcmcia_resource,
+ ARRAY_SIZE(a1200_pcmcia_resource));
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
}
if (AMIGAHW_PRESENT(A4000_IDE)) {
diff --git a/arch/m68k/include/asm/amipcmcia.h b/arch/m68k/include/asm/amipcmcia.h
index 6f1ec1887d82..281345bea41e 100644
--- a/arch/m68k/include/asm/amipcmcia.h
+++ b/arch/m68k/include/asm/amipcmcia.h
@@ -19,6 +19,7 @@
void pcmcia_reset(void);
int pcmcia_copy_tuple(unsigned char tuple_id, void *tuple, int max_len);
+unsigned char pcmcia_get_voltage(void);
void pcmcia_program_voltage(int voltage);
void pcmcia_access_speed(int speed);
void pcmcia_write_enable(void);
@@ -51,6 +52,16 @@ static inline void pcmcia_disable_irq(void)
#define PCMCIA_INSERTED (gayle.cardstatus & GAYLE_CS_CCDET)
+static inline void pcmcia_disable_ccdet_irq(void)
+{
+ gayle.inten &= ~GAYLE_IRQ_CCDET;
+}
+
+static inline u_char pcmcia_wena(void)
+{
+ return (gayle.cardstatus & GAYLE_CS_WR);
+}
+
/* valid voltages for pcmcia_ProgramVoltage */
#define PCMCIA_0V 0
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig
index dddb235dd020..98883bafc95e 100644
--- a/drivers/pcmcia/Kconfig
+++ b/drivers/pcmcia/Kconfig
@@ -253,4 +253,12 @@ config PCCARD_NONSTATIC
config PCCARD_IODYN
bool
+config PCMCIA_GAYLE
+ tristate "Amiga Gayle bridge support"
+ depends on PCMCIA && AMIGA && AMIGA_PCMCIA && !APNE
+ help
+ Say Y here to include support for PCMCIA host bridges that
+ are register compatible with Commodore's Gayle. This is found on
+ Amiga systems. If unsure, say N.
+
endif # PCCARD
diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile
index 31e0e34011bb..b89bcde972f9 100644
--- a/drivers/pcmcia/Makefile
+++ b/drivers/pcmcia/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_OMAP_CF) += omap_cf.o
obj-$(CONFIG_ELECTRA_CF) += electra_cf.o
obj-$(CONFIG_PCMCIA_ALCHEMY_DEVBOARD) += db1xxx_ss.o
obj-$(CONFIG_PCMCIA_MAX1600) += max1600.o
+obj-$(CONFIG_PCMCIA_GAYLE) += gayle.o
sa1111_cs-y += sa1111_generic.o
sa1111_cs-$(CONFIG_ASSABET_NEPONSET) += sa1111_neponset.o
diff --git a/drivers/pcmcia/gayle.c b/drivers/pcmcia/gayle.c
new file mode 100644
index 000000000000..c951fcc4f9f7
--- /dev/null
+++ b/drivers/pcmcia/gayle.c
@@ -0,0 +1,307 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Commodore's Gayle PC Card controller driver
+ *
+ * Copyright (c) 2024 Paolo Pisati <p.pisati@gmail.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <asm/amigaints.h>
+#include <asm/amigayle.h>
+#include <asm/amipcmcia.h>
+
+#include <pcmcia/ss.h>
+
+/* in case of emergency, break the glass */
+/* #define DEBUG */
+
+struct gayle_pcmcia_sock {
+ struct pcmcia_socket socket;
+
+ phys_addr_t phys_io;
+ void *virt_io;
+ phys_addr_t phys_attr;
+ phys_addr_t phys_mem;
+
+ int card_irq;
+};
+
+#define to_gayle_socket(x) container_of(x, struct gayle_pcmcia_sock, socket)
+
+static int gayle_sock_set_socket(struct pcmcia_socket *skt,
+ struct socket_state_t *state)
+{
+ struct gayle_pcmcia_sock *sock = to_gayle_socket(skt);
+
+ dev_dbg(&skt->dev, "%s flags 0x%x csc_mask 0x%x io_irq 0x%x "
+ "Vcc %d Vpp %d\n",
+ __func__, state->flags, state->csc_mask, state->io_irq,
+ state->Vcc, state->Vpp);
+
+ if (state->Vpp && (state->Vcc != state->Vpp)) {
+ pr_err("pcmcia: unsupported Vpp %d\n", state->Vpp);
+ return -EINVAL;
+ }
+
+ if (state->flags & SS_RESET) {
+ pcmcia_reset();
+ msleep(500); /* give card time to inizialize */
+ pr_err("%s cardstatus: 0x%x\n", __func__, gayle.cardstatus);
+ pr_err("%s inten: 0x%x\n", __func__, gayle.inten);
+ }
+
+ /*
+ * XXX shouldn't we apply power only when state->flags & SS_OUTPUT_ENA?
+ * but if so, what's the point of setting a Vcc without powering the
+ * socket on?
+ */
+ switch (state->Vcc) {
+ case 0:
+ pcmcia_program_voltage(PCMCIA_0V);
+ break;
+ case 50:
+ pcmcia_program_voltage(PCMCIA_5V);
+ break;
+ default:
+ pr_err("pcmcia: unsupported Vcc %d\n", state->Vcc);
+ }
+
+ return 0;
+}
+
+static int gayle_sock_get_status(struct pcmcia_socket *skt,
+ unsigned int *value)
+{
+ unsigned int status;
+
+ status = PCMCIA_INSERTED ? SS_DETECT : 0;
+
+ switch(pcmcia_get_voltage()) {
+ case GAYLE_CFG_5V:
+ case GAYLE_CFG_12V:
+ status |= SS_POWERON; /* power is applied to the card */
+ break;
+ case GAYLE_CFG_0V:
+ break;
+ default:
+ status |= SS_XVCARD; /* treated as unsupported in core */
+ }
+
+ status |= pcmcia_wena() ? 0 : SS_WRPROT; /* card is write protected */
+
+ if (status & (SS_DETECT | SS_POWERON))
+ status |= SS_READY;
+
+ dev_dbg(&skt->dev,"%s status: 0x%x\n", __func__, status);
+ *value = status;
+
+ return 0;
+}
+
+static int gayle_sock_init(struct pcmcia_socket *skt)
+{
+ dev_dbg(&skt->dev, "%s::%d\n", __func__, __LINE__);
+ return 0;
+}
+
+static int gayle_sock_suspend(struct pcmcia_socket *skt)
+{
+ dev_dbg(&skt->dev, "%s::%d\n", __func__, __LINE__);
+ return 0;
+}
+
+static int gayle_sock_set_io_map(struct pcmcia_socket *skt,
+ struct pccard_io_map *map)
+{
+ struct gayle_pcmcia_sock *sock = to_gayle_socket(skt);
+ dev_dbg(&skt->dev, "%s::%d\n", __func__, __LINE__);
+
+ map->start = sock->phys_io;
+ map->stop = map->start + GAYLE_IOSIZE;
+
+ return 0;
+}
+
+static int gayle_sock_set_mem_map(struct pcmcia_socket *skt,
+ struct pccard_mem_map *map)
+{
+ struct gayle_pcmcia_sock *sock = to_gayle_socket(skt);
+ dev_dbg(&skt->dev, "%s::%d %s card_start: 0x%x\n", __func__, __LINE__,
+ (map->flags & MAP_ATTRIB) ? "ATTR" : "MEM", map->card_start);
+
+ if (map->flags & MAP_ATTRIB)
+ map->static_start = sock->phys_attr + map->card_start;
+ else
+ map->static_start = sock->phys_mem + map->card_start;
+
+ return 0;
+}
+
+static struct pccard_operations gayle_pcmcia_operations = {
+ .init = gayle_sock_init,
+ .suspend = gayle_sock_suspend,
+ .get_status = gayle_sock_get_status,
+ .set_socket = gayle_sock_set_socket,
+ .set_io_map = gayle_sock_set_io_map,
+ .set_mem_map = gayle_sock_set_mem_map,
+};
+
+struct gayle_pcmcia_sock *sock;
+
+static irqreturn_t gayle_stschg_irq(int irq, void *data)
+{
+ unsigned char pcmcia_intreq;
+ struct gayle_pcmcia_sock *sock = data;
+
+ pcmcia_intreq = pcmcia_get_intreq();
+ if (!(pcmcia_intreq & (GAYLE_IRQ_SC | GAYLE_IRQ_DA | GAYLE_IRQ_WR |
+ GAYLE_IRQ_IRQ)))
+ return IRQ_NONE;
+
+ pr_debug("%s::%d intreq: 0x%x\n", __func__, __LINE__, pcmcia_intreq);
+ pcmcia_ack_int(pcmcia_get_intreq());
+ pr_debug("%s::%d intreq: 0x%x\n", __func__, __LINE__, pcmcia_get_intreq());
+ pcmcia_parse_events(&sock->socket, SS_STSCHG);
+ pr_debug("%s::%d intreq: 0x%x\n", __func__, __LINE__, pcmcia_get_intreq());
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t gayle_ccdet_irq(int irq, void *data)
+{
+ unsigned char pcmcia_intreq;
+ struct gayle_pcmcia_sock *sock = data;
+
+ pcmcia_intreq = pcmcia_get_intreq();
+ if (!(pcmcia_intreq & GAYLE_IRQ_CCDET))
+ return IRQ_NONE;
+
+ pr_debug("%s::%d intreq: 0x%x\n", __func__, __LINE__, pcmcia_intreq);
+ gayle.intreq = GAYLE_IRQ_IDE | GAYLE_IRQ_SC | GAYLE_IRQ_DA |
+ GAYLE_IRQ_WR | GAYLE_IRQ_IRQ;
+ pr_debug("%s::%d intreq: 0x%x\n", __func__, __LINE__, pcmcia_get_intreq());
+ pcmcia_parse_events(&sock->socket, SS_DETECT);
+ pr_debug("%s::%d intreq: 0x%x\n", __func__, __LINE__, pcmcia_get_intreq());
+
+ return IRQ_HANDLED;
+}
+
+
+static int gayle_pcmcia_init(struct platform_device *pdev) {
+ struct resource *r;
+ int ret;
+
+ dev_dbg(&pdev->dev, "%s::%d\n", __func__, __LINE__);
+
+ sock = kzalloc(sizeof(struct gayle_pcmcia_sock), GFP_KERNEL);
+ if (!sock)
+ return -ENOMEM;
+
+ /* card: irq assigned to the card itself. */
+ sock->card_irq = IRQ_AMIGA_PORTS;
+
+ /* PCMCIA Attribute area address */
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "Gayle attribute");
+ sock->phys_attr = r->start;
+
+ /* PCMCIA Memory area address */
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "Gayle memory");
+ sock->phys_mem = r->start;
+
+ /* PCMCIA IO area address */
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "Gayle I/O");
+ sock->phys_io = r->start;
+
+ sock->virt_io = (void *)(ioremap(sock->phys_io, 2*GAYLE_IOSIZE));
+ dev_dbg(&pdev->dev, "%s::%d ioremap: 0x%px\n", __func__, __LINE__,
+ sock->virt_io);
+ sock->virt_io -= GAYLE_IO;
+ dev_dbg(&pdev->dev, "%s::%d ioremap: 0x%px\n", __func__, __LINE__,
+ sock->virt_io);
+ if (!sock->virt_io) {
+ pr_err("pcmcia: cannot remap IO area\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ sock->socket.io_offset = (unsigned long)sock->virt_io;
+
+ sock->socket.ops = &gayle_pcmcia_operations;
+ sock->socket.owner = THIS_MODULE;
+ sock->socket.pci_irq = sock->card_irq;
+ sock->socket.features = SS_CAP_STATIC_MAP | SS_CAP_PCCARD;
+ sock->socket.map_size = GAYLE_RAMSIZE;
+ sock->socket.dev.parent = &pdev->dev;
+ sock->socket.resource_ops = &pccard_static_ops;
+
+ platform_set_drvdata(pdev, sock);
+
+ if (request_irq(IRQ_AMIGA_PORTS, gayle_stschg_irq, IRQF_SHARED, "pcmcia_stschg",
+ sock)) {
+ dev_dbg(&pdev->dev, "unable to setup stschg interrupt\n");
+ goto out;
+ }
+
+ if (request_irq(IRQ_AMIGA_EXTER, gayle_ccdet_irq, IRQF_SHARED, "pcmcia_ccdet",
+ sock)) {
+ dev_dbg(&pdev->dev, "unable to setup ccdet interrupt\n");
+ goto out1;
+ }
+
+ dev_dbg(&pdev->dev, "%s::%d\n", __func__, __LINE__);
+ ret = pcmcia_register_socket(&sock->socket);
+ if (ret) {
+ pr_err("pcmcia failed to register\n");
+ goto out2;
+ }
+
+ gayle.inten |= GAYLE_IRQ_CCDET | GAYLE_IRQ_SC | GAYLE_IRQ_DA |
+ GAYLE_IRQ_WR | GAYLE_IRQ_IRQ;
+ pr_err("%s inten: 0x%x\n", __func__, gayle.inten);
+
+ pr_info("Gayle pcmcia @ io/attr/mem %09x %09x %09x card irqs @ %d\n",
+ sock->phys_io, sock->phys_attr, sock->phys_mem,
+ sock->card_irq);
+
+ return 0;
+out2:
+ free_irq(IRQ_AMIGA_EXTER, &pdev->dev);
+out1:
+ free_irq(IRQ_AMIGA_PORTS, &pdev->dev);
+out:
+ iounmap(sock->virt_io);
+ kfree(sock);
+ return ret;
+}
+
+static int gayle_pcmcia_exit(struct platform_device *pdev) {
+ dev_dbg(&pdev->dev, "%s::%d\n", __func__, __LINE__);
+
+ pcmcia_disable_irq();
+ pcmcia_disable_ccdet_irq();
+ free_irq(IRQ_AMIGA_PORTS, sock);
+ free_irq(IRQ_AMIGA_EXTER, sock);
+ iounmap(sock->virt_io);
+ pcmcia_reset();
+ pcmcia_unregister_socket(&sock->socket);
+ kfree(sock);
+
+ return 0;
+}
+
+static struct platform_driver gayle_pcmcia_driver = {
+ .driver = {
+ .name = "amiga-gayle-pcmcia",
+ },
+ .probe = gayle_pcmcia_init,
+ .remove = gayle_pcmcia_exit,
+};
+
+module_platform_driver(gayle_pcmcia_driver);
+
+MODULE_AUTHOR("Paolo Pisati");
+MODULE_DESCRIPTION("Commodore's Gayle PC Card controller driver");
+MODULE_LICENSE("GPL v2");
--
2.34.1
^ permalink raw reply related [flat|nested] 4+ messages in thread