summaryrefslogtreecommitdiff
path: root/target/linux/patches/6.15.6
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/patches/6.15.6')
-rw-r--r--target/linux/patches/6.15.6/0001-pcmcia-Add-Hitachi-HD6446x-PCMCIA-socket-support.patch720
-rw-r--r--target/linux/patches/6.15.6/orinoco.patch12933
2 files changed, 13653 insertions, 0 deletions
diff --git a/target/linux/patches/6.15.6/0001-pcmcia-Add-Hitachi-HD6446x-PCMCIA-socket-support.patch b/target/linux/patches/6.15.6/0001-pcmcia-Add-Hitachi-HD6446x-PCMCIA-socket-support.patch
new file mode 100644
index 000000000..5c46fe5e4
--- /dev/null
+++ b/target/linux/patches/6.15.6/0001-pcmcia-Add-Hitachi-HD6446x-PCMCIA-socket-support.patch
@@ -0,0 +1,720 @@
+From b6ffbcb525539139a9b2255b992622f92757ea37 Mon Sep 17 00:00:00 2001
+From: Artur Rojek <contact@artur-rojek.eu>
+Date: Fri, 1 Aug 2025 22:52:22 +0200
+Subject: [PATCH] pcmcia: Add Hitachi HD6446x PCMCIA socket support
+
+Introduce support for the PC Card Controller part of the Hitachi HD6446x
+series of Intelligent Peripheral Controllers.
+
+WIP code. DO NOT UPSTREAM!
+---
+ arch/sh/boards/mach-hp6xx/setup.c | 45 ++-
+ arch/sh/cchips/hd6446x/hd64461.c | 56 +++-
+ arch/sh/include/asm/hd64461.h | 6 +-
+ drivers/pcmcia/Kconfig | 7 +
+ drivers/pcmcia/Makefile | 1 +
+ drivers/pcmcia/hd6446x_pcc.c | 453 ++++++++++++++++++++++++++++++
+ include/pcmcia/hd6446x_pcc.h | 9 +
+ 7 files changed, 569 insertions(+), 8 deletions(-)
+ create mode 100644 drivers/pcmcia/hd6446x_pcc.c
+ create mode 100644 include/pcmcia/hd6446x_pcc.h
+
+diff --git a/arch/sh/boards/mach-hp6xx/setup.c b/arch/sh/boards/mach-hp6xx/setup.c
+index 2ceead68d7bf..c697b8e1f5ac 100644
+--- a/arch/sh/boards/mach-hp6xx/setup.c
++++ b/arch/sh/boards/mach-hp6xx/setup.c
+@@ -18,19 +18,23 @@
+ #include <mach/hp6xx.h>
+ #include <cpu/dac.h>
+
++#include <pcmcia/hd6446x_pcc.h>
++
+ #define SCPCR 0xa4000116
+ #define SCPDR 0xa4000136
+
++#define CF_MEM_ATTR (0x15000000 - 0)
++
+ /* CF Slot */
+ static struct resource cf_ide_resources[] = {
+ [0] = {
+- .start = 0x15000000 + 0x1f0,
+- .end = 0x15000000 + 0x1f0 + 0x08 - 0x01,
++ .start = CF_MEM_ATTR + 0x1f0,
++ .end = CF_MEM_ATTR + 0x1f0 + 0x08 - 0x01,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+- .start = 0x15000000 + 0x1fe,
+- .end = 0x15000000 + 0x1fe + 0x01,
++ .start = CF_MEM_ATTR + 0x1fe,
++ .end = CF_MEM_ATTR + 0x1fe + 0x01,
+ .flags = IORESOURCE_MEM,
+ },
+ [2] = {
+@@ -51,6 +55,36 @@ static struct platform_device jornadakbd_device = {
+ .id = -1,
+ };
+
++static struct resource hd6446x_pcc_resources[] = {
++ [0] = {
++ .start = HD64461_PCC0ISR,
++ .end = HD64461_PCC0ISR + 0x10,
++ .flags = IORESOURCE_MEM,
++ },
++ [1] = {
++ .start = HD64461_PCC0_BASE,
++ .end = HD64461_PCC0_BASE + 0x4000000,
++ .flags = IORESOURCE_MEM,
++ },
++ [2] = {
++ .start = HD64461_IRQ_PCC0,
++ .flags = IORESOURCE_IRQ,
++ },
++};
++
++static struct hd6446x_pcc_plat_data hd6446x_pcc_platform_data = {
++ .slot_id = 1,
++ .io_support = true,
++};
++
++static struct platform_device hp6446x_pcc_device = {
++ .name = "hd6446x_pcc",
++ .id = -1,
++ .num_resources = ARRAY_SIZE(hd6446x_pcc_resources),
++ .resource = hd6446x_pcc_resources,
++ .dev.platform_data = &hd6446x_pcc_platform_data,
++};
++
+ static void dac_audio_start(struct dac_audio_pdata *pdata)
+ {
+ u16 v;
+@@ -108,6 +142,7 @@ static struct platform_device *hp6xx_devices[] __initdata = {
+ &cf_ide_device,
+ &jornadakbd_device,
+ &dac_audio_device,
++ &hp6446x_pcc_device,
+ };
+
+ static void __init hp6xx_init_irq(void)
+@@ -126,6 +161,8 @@ static void __init hp6xx_setup(char **cmdline_p)
+ u8 v8;
+ u16 v;
+
++ __set_io_port_base(0);
++
+ v = inw(HD64461_STBCR);
+ v |= HD64461_STBCR_SURTST | HD64461_STBCR_SIRST |
+ HD64461_STBCR_STM1ST | HD64461_STBCR_STM0ST |
+diff --git a/arch/sh/cchips/hd6446x/hd64461.c b/arch/sh/cchips/hd6446x/hd64461.c
+index 81764882d87d..965486584ee5 100644
+--- a/arch/sh/cchips/hd6446x/hd64461.c
++++ b/arch/sh/cchips/hd6446x/hd64461.c
+@@ -4,7 +4,9 @@
+ * Hitachi HD64461 companion chip support
+ */
+
++#include <linux/clkdev.h>
+ #include <linux/sched.h>
++#include <linux/sh_clk.h>
+ #include <linux/module.h>
+ #include <linux/kernel.h>
+ #include <linux/param.h>
+@@ -45,7 +47,7 @@ static void hd64461_mask_and_ack_irq(struct irq_data *data)
+ hd64461_mask_irq(data);
+
+ #ifdef CONFIG_HD64461_ENABLER
+- if (data->irq == HD64461_IRQBASE + 13)
++ if (data->irq == HD64461_IRQ_PCC1)
+ __raw_writeb(0x00, HD64461_PCC1CSCR);
+ #endif
+ }
+@@ -72,6 +74,51 @@ static void hd64461_irq_demux(struct irq_desc *desc)
+ }
+ }
+
++static int hd64461_clk_enable(struct clk *clk)
++{
++ u16 reg = __raw_readw(HD64461_STBCR);
++
++ printk("clk enable: %d\n", clk->enable_bit);
++
++ __raw_writew(reg & ~(1 << clk->enable_bit), HD64461_STBCR);
++
++ return 0;
++}
++
++static void hd64461_clk_disable(struct clk *clk)
++{
++ u16 reg = __raw_readw(HD64461_STBCR);
++
++ printk("clk disable: %d\n", clk->enable_bit);
++ //panic("clk disable: %d\n", clk->enable_bit);
++
++
++ __raw_writew(reg | (1 << clk->enable_bit), HD64461_STBCR);
++}
++
++static struct sh_clk_ops hd64461_clk_ops = {
++ .enable = hd64461_clk_enable,
++ .disable = hd64461_clk_disable,
++};
++
++static struct clk hd64461_clk[] = {
++ {
++ .enable_bit = 5,
++ .ops = &hd64461_clk_ops,
++ .flags = CLK_ENABLE_ON_INIT,
++ },
++ {
++ .enable_bit = 6,
++ .ops = &hd64461_clk_ops,
++ .flags = CLK_ENABLE_ON_INIT,
++ },
++};
++
++static struct clk_lookup hd64461_clk_lookup[] = {
++ CLKDEV_CON_ID("pcc1", &hd64461_clk[0]),
++ CLKDEV_CON_ID("pcc0", &hd64461_clk[1]),
++};
++
+ static int __init setup_hd64461(void)
+ {
+ int irq_base, i;
+@@ -106,6 +153,13 @@ static int __init setup_hd64461(void)
+ __raw_writeb(0x00, HD64461_PCC1CSCR);
+ #endif
+
++// for (i = 0; i < ARRAY_SIZE(hd64461_clk); i++)
++// clk_register(&hd64461_clk[i]);
++ clk_register(&hd64461_clk[1]);
++ clkdev_add_table(hd64461_clk_lookup, ARRAY_SIZE(hd64461_clk_lookup));
++
++ printk("done with clk setup\n");
++
+ return 0;
+ }
+
+diff --git a/arch/sh/include/asm/hd64461.h b/arch/sh/include/asm/hd64461.h
+index d2c485fa333b..91823ec07f79 100644
+--- a/arch/sh/include/asm/hd64461.h
++++ b/arch/sh/include/asm/hd64461.h
+@@ -17,9 +17,9 @@
+ #define HD64461_IOBASE 0xb0000000
+ #define HD64461_IO_OFFSET(x) (HD64461_IOBASE + (x))
+ #define HD64461_PCC0_BASE HD64461_IO_OFFSET(0x8000000)
+-#define HD64461_PCC0_ATTR (HD64461_PCC0_BASE) /* 0xb80000000 */
+-#define HD64461_PCC0_COMM (HD64461_PCC0_BASE+HD64461_PCC_WINDOW) /* 0xb90000000 */
+-#define HD64461_PCC0_IO (HD64461_PCC0_BASE+2*HD64461_PCC_WINDOW) /* 0xba0000000 */
++#define HD64461_PCC0_ATTR (HD64461_PCC0_BASE) /* 0xb8000000 */
++#define HD64461_PCC0_COMM (HD64461_PCC0_BASE+HD64461_PCC_WINDOW) /* 0xb9000000 */
++#define HD64461_PCC0_IO (HD64461_PCC0_BASE+2*HD64461_PCC_WINDOW) /* 0xba000000 */
+
+ /* Area 5 - Slot 1 - memory card only */
+ #define HD64461_PCC1_BASE HD64461_IO_OFFSET(0x4000000)
+diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig
+index dddb235dd020..f2434ca15c8e 100644
+--- a/drivers/pcmcia/Kconfig
++++ b/drivers/pcmcia/Kconfig
+@@ -159,6 +159,13 @@ config PCMCIA_ALCHEMY_DEVBOARD
+
+ This driver is also available as a module called db1xxx_ss.ko
+
++config PCMCIA_HD6446X_PCC
++ tristate "Hitachi HD6446x PCMCIA socket support"
++ depends on PCMCIA && HD6446X_SERIES
++ help
++ Say Y here to include support for the PC Card Controller part of
++ the Hitachi HD6446x series of Intelligent Peripheral Controllers.
++
+ config PCMCIA_XXS1500
+ tristate "MyCable XXS1500 PCMCIA socket support"
+ depends on PCMCIA && MIPS_XXS1500
+diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile
+index c9d51b150682..764df19be544 100644
+--- a/drivers/pcmcia/Makefile
++++ b/drivers/pcmcia/Makefile
+@@ -33,6 +33,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_HD6446X_PCC) += hd6446x_pcc.o
+
+ sa1111_cs-y += sa1111_generic.o
+ sa1111_cs-$(CONFIG_ASSABET_NEPONSET) += sa1111_neponset.o
+diff --git a/drivers/pcmcia/hd6446x_pcc.c b/drivers/pcmcia/hd6446x_pcc.c
+new file mode 100644
+index 000000000000..31074f93b55b
+--- /dev/null
++++ b/drivers/pcmcia/hd6446x_pcc.c
+@@ -0,0 +1,453 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * PC Card Controller driver for the Hitachi HD6446x series of Intelligent
++ * Peripheral Controllers.
++ *
++ * Copyright (c) 2023 - 2024 Artur Rojek <contact@artur-rojek.eu>
++ */
++
++#include <linux/clk.h>
++#include <linux/interrupt.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <pcmcia/hd6446x_pcc.h>
++#include <pcmcia/ss.h>
++#include <asm/hd64461.h>
++
++#include "mach-common/mach/hp6xx.h"
++
++#define HD6446X_PCC_ISR 0x00
++#define HD6446X_PCC_GCR 0x02
++#define HD6446X_PCC_CSCR 0x04
++#define HD6446X_PCC_CSCIER 0x06
++#define HD6446X_PCC_SCR 0x08
++
++#define HD6446X_PCC_ISR_CD (BIT(2) | BIT(3))
++#define HD6446X_PCC_ISR_VS1 BIT(4)
++#define HD6446X_PCC_ISR_VS2 BIT(5)
++#define HD6446X_PCC_ISR_MWP BIT(6)
++#define HD6446X_PCC_ISR_READY BIT(7)
++
++#define HD6446X_PCC_GCR_PMMOD BIT(3)
++#define HD6446X_PCC_GCR_VCC0 BIT(4)
++#define HD6446X_PCC_GCR_PCCT BIT(5)
++#define HD6446X_PCC_GCR_PCCR BIT(6)
++#define HD6446X_PCC_GCR_DRV BIT(7)
++
++#define HD6446X_PCC_CSCR_BD BIT(0)
++#define HD6446X_PCC_CSCR_BW BIT(1)
++#define HD6446X_PCC_CSCR_RC BIT(2)
++#define HD6446X_PCC_CSCR_CDC BIT(3)
++#define HD6446X_PCC_CSCR_SC BIT(4)
++#define HD6446X_PCC_CSCR_IREQ BIT(5)
++#define HD6446X_PCC_CSCR_SCDI BIT(7)
++
++#define HD6446X_PCC_CSCIER_BDE BIT(0)
++#define HD6446X_PCC_CSCIER_BWE BIT(1)
++#define HD6446X_PCC_CSCIER_RE BIT(2)
++#define HD6446X_PCC_CSCIER_CDE BIT(3)
++#define HD6446X_PCC_CSCIER_SCE BIT(4)
++#define HD6446X_PCC_CSCIER_IREQE_FALLING BIT(6)
++
++#define HD6446X_PCC_SCR_VCC1 BIT(1)
++
++#define HD6446X_PCC_WINDOW 0x1000000 /* 16 MiB */
++
++struct hd6446x_pcc {
++ const struct hd6446x_pcc_plat_data *pdata;
++ void __iomem *reg;
++ void __iomem *base;
++ struct clk *clk;
++ struct pcmcia_socket socket;
++ struct socket_state_t state;
++ bool memory_card;
++};
++
++static int hd64461_pcmcia_socket_set_voltage(struct hd6446x_pcc *pcc, int Vcc)
++{
++ int gcr, scr, stbcr;
++
++ gcr = readb(pcc->reg + HD6446X_PCC_GCR);
++ scr = readb(pcc->reg + HD6446X_PCC_SCR);
++
++ switch (Vcc) {
++ case 0:
++ gcr |= HD6446X_PCC_GCR_VCC0;
++ scr |= HD6446X_PCC_SCR_VCC1;
++ break;
++ case 33:
++ gcr |= HD6446X_PCC_GCR_VCC0;
++ scr &= ~HD6446X_PCC_SCR_VCC1;
++ break;
++ case 50:
++ gcr &= ~HD6446X_PCC_GCR_VCC0;
++ scr &= ~HD6446X_PCC_SCR_VCC1;
++ break;
++ default:
++ printk("Unsupported voltage: %d\n", Vcc);
++ return -EINVAL;
++ }
++
++ writeb(gcr, pcc->reg + HD6446X_PCC_GCR);
++ writeb(scr, pcc->reg + HD6446X_PCC_SCR);
++
++// stbcr = readw(HD64461_STBCR);
++
++ if (Vcc > 0)
++ clk_enable(pcc->clk);
++// stbcr &= ~HD64461_STBCR_SPC0ST;
++ else
++ clk_disable(pcc->clk);
++// stbcr |= HD64461_STBCR_SPC0ST;
++
++// writew(stbcr, HD64461_STBCR);
++
++ return 0;
++}
++
++static int hd64461_pcmcia_socket_init(struct pcmcia_socket *sock)
++{
++ struct hd6446x_pcc *pcc = sock->driver_data;
++ int reg;
++
++ printk("socket_init\n");
++
++// printk("init BCR1: %04x\n", readw(0xffffff60));
++
++ (void)hd64461_pcmcia_socket_set_voltage(pcc, 0);
++
++ reg = readb(HD64461_GPADR);
++ reg &= ~HD64461_GPADR_PCMCIA0;
++ writeb(reg, HD64461_GPADR);
++
++ return 0;
++}
++
++static int hd64461_pcmcia_socket_get_status(struct pcmcia_socket *sock,
++ unsigned int *value)
++{
++// struct hd64461_pcmcia_socket *socket = sock->driver_data;
++ struct hd6446x_pcc *pcc = sock->driver_data;
++ unsigned int status = 0;
++ int reg;
++
++ printk("get_status\n");
++
++ reg = readb(pcc->reg + HD6446X_PCC_ISR);
++
++// printk("PCC0ISR: %02x\n", reg);
++
++ if (reg & HD6446X_PCC_ISR_CD)
++ goto end; /* No card detected. */
++ status |= SS_DETECT;
++
++ if (pcc->memory_card) {
++ if (reg & HD6446X_PCC_ISR_READY)
++ status |= SS_READY;
++
++ if (reg & HD6446X_PCC_ISR_MWP)
++ status |= SS_WRPROT;
++ } else
++ status |= SS_STSCHG;
++
++ if (!(reg & HD6446X_PCC_ISR_VS1)) {
++ status |= SS_3VCARD;
++ printk("3v3 card\n");
++ }
++
++ if (!(reg & HD6446X_PCC_ISR_VS2)) {
++ status |= SS_XVCARD;
++ printk("X.Xv card\n");
++ }
++
++ if (pcc->state.Vcc || pcc->state.Vpp)
++ status |= SS_POWERON;
++
++end:
++ *value = status;
++// printk("status: %x, memory: %d\n", status, socket->memory_card);
++
++ return 0;
++}
++
++static int hd64461_pcmcia_socket_configure(struct pcmcia_socket *sock,
++ struct socket_state_t *state)
++{
++// struct hd64461_pcmcia_socket *socket = sock->driver_data;
++ struct hd6446x_pcc *pcc = sock->driver_data;
++ int reg = 0;
++// unsigned long flags;
++
++ writeb(0x0, pcc->reg + HD6446X_PCC_CSCIER);
++
++// local_irq_save(flags);
++
++// printk("socket_configure, flags: %x, csc_mask: %x, Vcc: %d, Vpp: %d, io_irq: %x\n",
++// state->flags, state->csc_mask, state->Vcc, state->Vpp, state->io_irq);
++
++ if (state->Vcc != pcc->state.Vcc || state->Vpp != pcc->state.Vpp)
++ if (hd64461_pcmcia_socket_set_voltage(pcc, state->Vcc)) {
++// local_irq_restore(flags);
++ return -EINVAL;
++ }
++
++ //reg = readb(HD64461_PCC0CSCIER) & HD64461_PCCCSCIER_IREQE_MASK;
++ reg = HD6446X_PCC_CSCIER_IREQE_FALLING;
++// reg = 0;
++
++ if (state->csc_mask & SS_DETECT)
++ reg |= HD6446X_PCC_CSCIER_CDE;
++ if (state->csc_mask & SS_READY)
++ reg |= HD6446X_PCC_CSCIER_RE;
++ if (state->csc_mask & SS_BATDEAD)
++ reg |= HD6446X_PCC_CSCIER_BDE;
++ if (state->csc_mask & SS_BATWARN)
++ reg |= HD6446X_PCC_CSCIER_BWE;
++ if (state->csc_mask & SS_STSCHG)
++ reg |= HD6446X_PCC_CSCIER_SCE;
++// if (state->flags & SS_IOCARD)
++// reg = HD64461_PCCCSCIER_IREQE_FALLING;
++
++
++ writeb(reg, pcc->reg + HD6446X_PCC_CSCIER);
++
++// reg = readb(HD64461_PCC0GCR);
++// reg = 0;
++// reg = HD6446X_PCC_GCR_PMMOD;
++ reg = readb(pcc->reg + HD6446X_PCC_GCR) & ~(HD6446X_PCC_GCR_PCCT |
++ HD6446X_PCC_GCR_PCCR |
++ HD6446X_PCC_GCR_DRV);
++
++ pcc->memory_card = !(state->flags & SS_IOCARD);
++ if (!pcc->memory_card)
++ reg |= HD6446X_PCC_GCR_PCCT;
++ if (state->flags & SS_RESET)
++ reg |= HD6446X_PCC_GCR_PCCR;
++ if (state->flags & SS_OUTPUT_ENA)
++ reg |= HD6446X_PCC_GCR_DRV;
++
++#if 0
++ if (socket->memory_card)
++ reg &= ~HD64461_PCCGCR_PCCT;
++ else
++ reg |= HD64461_PCCGCR_PCCT;
++ if (state->flags & SS_RESET)
++ reg |= HD64461_PCCGCR_PCCR;
++ else
++ reg &= ~HD64461_PCCGCR_PCCR;
++ if (state->flags & SS_OUTPUT_ENA)
++ reg |= HD64461_PCCGCR_DRVE;
++ else
++ reg &= ~HD64461_PCCGCR_DRVE;
++#endif
++
++ writeb(reg, pcc->reg + HD6446X_PCC_GCR);
++
++ pcc->state = *state;
++
++// local_irq_restore(flags);
++
++// printk("Configured: %x\n", state->flags);
++// printk("config BCR1: %04x\n", readw(0xffffff60));
++
++ return 0;
++}
++
++static int hd6446x_pcc_set_io_map(struct pcmcia_socket *socket,
++ struct pccard_io_map *io)
++{
++ /* We use a static map. */
++ printk("hd6446x_pcc_set_io_map\n");
++ return 0;
++}
++
++static int hd64461_pcmcia_socket_set_mem_map(struct pcmcia_socket *sock,
++ struct pccard_mem_map *map)
++{
++ struct hd6446x_pcc *pcc = sock->driver_data;
++// printk("set_mem_map\n");
++
++ if (map->map >= MAX_WIN)
++ return -EINVAL;
++
++ map->static_start = (uintptr_t)pcc->base + map->card_start;
++// map->static_start = HD64461_PCC0_BASE + map->card_start;
++// map->static_start = 0x8000000 + map->card_start;
++
++// printk("map->flags %d: %x\n", map->map, map->flags);
++
++ if (!(map->flags & MAP_ATTRIB))
++ map->static_start += HD6446X_PCC_WINDOW;
++
++ return 0;
++}
++
++static struct pccard_operations hd64461_pcmcia_socket_ops = {
++ .init = hd64461_pcmcia_socket_init,
++ .get_status = hd64461_pcmcia_socket_get_status,
++ .set_socket = hd64461_pcmcia_socket_configure,
++ .set_io_map = hd6446x_pcc_set_io_map,
++ .set_mem_map = hd64461_pcmcia_socket_set_mem_map,
++};
++
++static irqreturn_t hd64461_pcmcia_socket_irq(int irq, void *data)
++{
++ struct hd6446x_pcc *pcc = data;
++ int reg = readb(pcc->reg + HD6446X_PCC_CSCR) & ~HD6446X_PCC_CSCR_SCDI;
++ unsigned int events = 0;
++
++ if (reg & HD6446X_PCC_CSCR_IREQ) {
++ reg &= ~HD6446X_PCC_CSCR_IREQ;
++ writeb(reg, pcc->reg + HD6446X_PCC_CSCR);
++
++ return IRQ_NONE;
++ }
++
++ if (reg & HD6446X_PCC_CSCR_CDC) {
++ reg &= ~HD6446X_PCC_CSCR_CDC;
++ events |= SS_DETECT;
++
++ /* Card has been ejected. */
++ if (readb(pcc->reg + HD6446X_PCC_ISR) & HD6446X_PCC_ISR_CD)
++ reg &= ~(HD6446X_PCC_CSCR_RC | HD6446X_PCC_CSCR_BW |
++ HD6446X_PCC_CSCR_BD | HD6446X_PCC_CSCR_SC);
++ }
++
++ if (pcc->memory_card) {
++ if (reg & HD6446X_PCC_CSCR_RC) {
++ reg &= ~HD6446X_PCC_CSCR_RC;
++ events |= SS_READY;
++ }
++
++ if (reg & HD6446X_PCC_CSCR_BW) {
++ reg &= ~HD6446X_PCC_CSCR_BW;
++ events |= SS_BATWARN;
++ }
++
++ if (reg & HD6446X_PCC_CSCR_BD) {
++ reg &= ~HD6446X_PCC_CSCR_BD;
++ events |= SS_BATDEAD;
++ }
++ } else if (reg & HD6446X_PCC_CSCR_SC) {
++ reg &= ~HD6446X_PCC_CSCR_SC;
++ events |= SS_STSCHG;
++ }
++
++ writeb(reg, pcc->reg + HD6446X_PCC_CSCR);
++
++ if (events)
++ pcmcia_parse_events(&pcc->socket, events);
++
++// writeb(reg, HD64461_PCC0CSCR);
++
++ return IRQ_HANDLED;
++}
++
++static int hd64461_pcmcia_socket_probe(struct platform_device *pdev)
++{
++ struct hd6446x_pcc *pcc;
++ struct device *dev = &pdev->dev;
++ struct pcmcia_socket *sock;
++ int irq;
++ int reg, ret;
++
++ printk("pcc probe\n");
++
++// pcc = dev.platform_data;
++// socket = platform_get_drvdata(pdev);
++
++ pcc = devm_kzalloc(dev, sizeof(*pcc), GFP_KERNEL);
++ if (!pcc)
++ return -ENOMEM;
++
++ pcc->pdata = dev_get_platdata(dev);
++ if (!pcc->pdata) {
++ dev_err(dev, "Unable to get platform data\n");
++ return -EINVAL;
++ }
++
++ pcc->memory_card = true;
++ irq = platform_get_irq(pdev, 0);
++
++ pcc->reg = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(pcc->reg)) {
++ return PTR_ERR(pcc->reg);
++ }
++
++ pcc->base = devm_platform_ioremap_resource(pdev, 1);
++ if (IS_ERR(pcc->base)) {
++ return PTR_ERR(pcc->base);
++ }
++
++// pcc->reg = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++// pcc->base = platform_get_resource(pdev, IORESOURCE_MEM, 1);
++
++ sock = &pcc->socket;
++ sock->driver_data = pcc;
++ sock->resource_ops = &pccard_static_ops;
++ sock->ops = &hd64461_pcmcia_socket_ops;
++ sock->owner = THIS_MODULE;
++ sock->dev.parent = &pdev->dev;
++ sock->features = SS_CAP_STATIC_MAP | SS_CAP_PCCARD | SS_CAP_PAGE_REGS;
++ sock->pci_irq = irq;
++ sock->irq_mask = 0xffde;
++
++ sock->map_size = HD6446X_PCC_WINDOW;
++ sock->io_offset = (uintptr_t)pcc->base + sock->map_size * 2;
++// sock->io_offset = HD6446X_PCC_IO;
++// sock->io_offset = 0x8000000 + HD64461_PCC_WINDOW * 2;
++
++ printk("sh_io_port_base: %lx\n", sh_io_port_base);
++
++ ret = pcmcia_register_socket(sock);
++ if (ret) {
++ dev_err(dev, "Unable to register socket\n");
++ return ret;
++ }
++
++ /* Put the hardware in a known state. */
++// printk("CSCIER: %x\n", readb(pcc->reg + HD6446X_PCC_CSCIER));
++ writeb(HD6446X_PCC_CSCIER_IREQE_FALLING | HD6446X_PCC_CSCIER_CDE,
++ pcc->reg + HD6446X_PCC_CSCIER);
++ writeb(0x0, pcc->reg + HD6446X_PCC_CSCR);
++ writeb(HD6446X_PCC_GCR_PMMOD | HD6446X_PCC_GCR_DRV, pcc->reg + HD6446X_PCC_GCR);
++
++ ret = devm_request_irq(dev, irq, hd64461_pcmcia_socket_irq,
++ IRQF_SHARED, dev_name(dev), pcc);
++ if (ret < 0) {
++ dev_err(dev, "Failed to request irq: %d\n", ret);
++ return ret;
++ }
++
++ pcc->clk = devm_clk_get_prepared(dev,
++ pcc->pdata->slot_id ? "pcc0" : "pcc1");
++ if (IS_ERR(pcc->clk)) {
++ dev_err(dev, "Unable to get clock\n");
++ return PTR_ERR(pcc->clk);
++ }
++// clk_disable(pcc->clk);
++
++ printk("reg: %lx, base: %lx, basep: %lx\n",
++ (uintptr_t)pcc->reg,
++ (uintptr_t)pcc->base,
++ virt_to_phys(pcc->base));
++
++ return 0;
++}
++
++static void hd64461_pcmcia_socket_remove(struct platform_device *pdev)
++{
++ struct hd6446x_pcc *pcc = platform_get_drvdata(pdev);
++
++ pcmcia_unregister_socket(&pcc->socket);
++}
++
++static struct platform_driver hd64461_pcmcia_socket_driver = {
++ .driver = {
++ .name = "hd6446x_pcc",
++ },
++ .probe = hd64461_pcmcia_socket_probe,
++ .remove = hd64461_pcmcia_socket_remove,
++};
++
++module_platform_driver(hd64461_pcmcia_socket_driver);
+diff --git a/include/pcmcia/hd6446x_pcc.h b/include/pcmcia/hd6446x_pcc.h
+new file mode 100644
+index 000000000000..d1fe98f0a701
+--- /dev/null
++++ b/include/pcmcia/hd6446x_pcc.h
+@@ -0,0 +1,9 @@
++#ifndef _HD6446X_PCC_
++#define _HD6446X_PCC_
++
++struct hd6446x_pcc_plat_data {
++ int slot_id;
++ bool io_support;
++};
++
++#endif /* _HD6446X_PCC_ */
+--
+2.50.1
+
diff --git a/target/linux/patches/6.15.6/orinoco.patch b/target/linux/patches/6.15.6/orinoco.patch
new file mode 100644
index 000000000..6401013fd
--- /dev/null
+++ b/target/linux/patches/6.15.6/orinoco.patch
@@ -0,0 +1,12933 @@
+diff -Nur linux-6.15.6.orig/drivers/net/wireless/intersil/Kconfig linux-6.15.6/drivers/net/wireless/intersil/Kconfig
+--- linux-6.15.6.orig/drivers/net/wireless/intersil/Kconfig 2025-07-10 16:08:55.000000000 +0200
++++ linux-6.15.6/drivers/net/wireless/intersil/Kconfig 2025-08-03 00:20:27.566744589 +0200
+@@ -12,6 +12,7 @@
+
+ if WLAN_VENDOR_INTERSIL
+
++source "drivers/net/wireless/intersil/orinoco/Kconfig"
+ source "drivers/net/wireless/intersil/p54/Kconfig"
+
+ endif # WLAN_VENDOR_INTERSIL
+diff -Nur linux-6.15.6.orig/drivers/net/wireless/intersil/Makefile linux-6.15.6/drivers/net/wireless/intersil/Makefile
+--- linux-6.15.6.orig/drivers/net/wireless/intersil/Makefile 2025-07-10 16:08:55.000000000 +0200
++++ linux-6.15.6/drivers/net/wireless/intersil/Makefile 2025-08-03 00:20:54.161967083 +0200
+@@ -1,2 +1,3 @@
+ # SPDX-License-Identifier: GPL-2.0-only
++obj-$(CONFIG_HERMES) += orinoco/
+ obj-$(CONFIG_P54_COMMON) += p54/
+diff -Nur linux-6.15.6.orig/drivers/net/wireless/intersil/orinoco/airport.c linux-6.15.6/drivers/net/wireless/intersil/orinoco/airport.c
+--- linux-6.15.6.orig/drivers/net/wireless/intersil/orinoco/airport.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-6.15.6/drivers/net/wireless/intersil/orinoco/airport.c 2025-08-03 15:01:59.313131673 +0200
+@@ -0,0 +1,268 @@
++/* airport.c
++ *
++ * A driver for "Hermes" chipset based Apple Airport wireless
++ * card.
++ *
++ * Copyright notice & release notes in file main.c
++ *
++ * Note specific to airport stub:
++ *
++ * 0.05 : first version of the new split driver
++ * 0.06 : fix possible hang on powerup, add sleep support
++ */
++
++#define DRIVER_NAME "airport"
++#define PFX DRIVER_NAME ": "
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/mod_devicetable.h>
++#include <asm/pmac_feature.h>
++
++#include "orinoco.h"
++
++#define AIRPORT_IO_LEN (0x1000) /* one page */
++
++struct airport {
++ struct macio_dev *mdev;
++ void __iomem *vaddr;
++ unsigned int irq;
++ int irq_requested;
++ int ndev_registered;
++};
++
++static int
++airport_suspend(struct macio_dev *mdev, pm_message_t state)
++{
++ struct orinoco_private *priv = dev_get_drvdata(&mdev->ofdev.dev);
++ struct net_device *dev = priv->ndev;
++ struct airport *card = priv->card;
++ unsigned long flags;
++ int err;
++
++ printk(KERN_DEBUG "%s: Airport entering sleep mode\n", dev->name);
++
++ err = orinoco_lock(priv, &flags);
++ if (err) {
++ printk(KERN_ERR "%s: hw_unavailable on PBOOK_SLEEP_NOW\n",
++ dev->name);
++ return 0;
++ }
++
++ orinoco_down(priv);
++ orinoco_unlock(priv, &flags);
++
++ disable_irq(card->irq);
++ pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE,
++ macio_get_of_node(mdev), 0, 0);
++
++ return 0;
++}
++
++static int
++airport_resume(struct macio_dev *mdev)
++{
++ struct orinoco_private *priv = dev_get_drvdata(&mdev->ofdev.dev);
++ struct net_device *dev = priv->ndev;
++ struct airport *card = priv->card;
++ unsigned long flags;
++ int err;
++
++ printk(KERN_DEBUG "%s: Airport waking up\n", dev->name);
++
++ pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE,
++ macio_get_of_node(mdev), 0, 1);
++ msleep(200);
++
++ enable_irq(card->irq);
++
++ priv->hw.ops->lock_irqsave(&priv->lock, &flags);
++ err = orinoco_up(priv);
++ priv->hw.ops->unlock_irqrestore(&priv->lock, &flags);
++
++ return err;
++}
++
++static int
++airport_detach(struct macio_dev *mdev)
++{
++ struct orinoco_private *priv = dev_get_drvdata(&mdev->ofdev.dev);
++ struct airport *card = priv->card;
++
++ if (card->ndev_registered)
++ orinoco_if_del(priv);
++ card->ndev_registered = 0;
++
++ if (card->irq_requested)
++ free_irq(card->irq, priv);
++ card->irq_requested = 0;
++
++ if (card->vaddr)
++ iounmap(card->vaddr);
++ card->vaddr = NULL;
++
++ macio_release_resource(mdev, 0);
++
++ pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE,
++ macio_get_of_node(mdev), 0, 0);
++ ssleep(1);
++
++ macio_set_drvdata(mdev, NULL);
++ free_orinocodev(priv);
++
++ return 0;
++}
++
++static int airport_hard_reset(struct orinoco_private *priv)
++{
++ /* It would be nice to power cycle the Airport for a real hard
++ * reset, but for some reason although it appears to
++ * re-initialize properly, it falls in a screaming heap
++ * shortly afterwards. */
++#if 0
++ struct airport *card = priv->card;
++
++ /* Vitally important. If we don't do this it seems we get an
++ * interrupt somewhere during the power cycle, since
++ * hw_unavailable is already set it doesn't get ACKed, we get
++ * into an interrupt loop and the PMU decides to turn us
++ * off. */
++ disable_irq(card->irq);
++
++ pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE,
++ macio_get_of_node(card->mdev), 0, 0);
++ ssleep(1);
++ pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE,
++ macio_get_of_node(card->mdev), 0, 1);
++ ssleep(1);
++
++ enable_irq(card->irq);
++ ssleep(1);
++#endif
++
++ return 0;
++}
++
++static int
++airport_attach(struct macio_dev *mdev, const struct of_device_id *match)
++{
++ struct orinoco_private *priv;
++ struct airport *card;
++ unsigned long phys_addr;
++ struct hermes *hw;
++
++ if (macio_resource_count(mdev) < 1 || macio_irq_count(mdev) < 1) {
++ printk(KERN_ERR PFX "Wrong interrupt/addresses in OF tree\n");
++ return -ENODEV;
++ }
++
++ /* Allocate space for private device-specific data */
++ priv = alloc_orinocodev(sizeof(*card), &mdev->ofdev.dev,
++ airport_hard_reset, NULL);
++ if (!priv) {
++ printk(KERN_ERR PFX "Cannot allocate network device\n");
++ return -ENODEV;
++ }
++ card = priv->card;
++
++ hw = &priv->hw;
++ card->mdev = mdev;
++
++ if (macio_request_resource(mdev, 0, DRIVER_NAME)) {
++ printk(KERN_ERR PFX "can't request IO resource !\n");
++ free_orinocodev(priv);
++ return -EBUSY;
++ }
++
++ macio_set_drvdata(mdev, priv);
++
++ /* Setup interrupts & base address */
++ card->irq = macio_irq(mdev, 0);
++ phys_addr = macio_resource_start(mdev, 0); /* Physical address */
++ printk(KERN_DEBUG PFX "Physical address %lx\n", phys_addr);
++ card->vaddr = ioremap(phys_addr, AIRPORT_IO_LEN);
++ if (!card->vaddr) {
++ printk(KERN_ERR PFX "ioremap() failed\n");
++ goto failed;
++ }
++
++ hermes_struct_init(hw, card->vaddr, HERMES_16BIT_REGSPACING);
++
++ /* Power up card */
++ pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE,
++ macio_get_of_node(mdev), 0, 1);
++ ssleep(1);
++
++ /* Reset it before we get the interrupt */
++ hw->ops->init(hw);
++
++ if (request_irq(card->irq, orinoco_interrupt, 0, DRIVER_NAME, priv)) {
++ printk(KERN_ERR PFX "Couldn't get IRQ %d\n", card->irq);
++ goto failed;
++ }
++ card->irq_requested = 1;
++
++ /* Initialise the main driver */
++ if (orinoco_init(priv) != 0) {
++ printk(KERN_ERR PFX "orinoco_init() failed\n");
++ goto failed;
++ }
++
++ /* Register an interface with the stack */
++ if (orinoco_if_add(priv, phys_addr, card->irq, NULL) != 0) {
++ printk(KERN_ERR PFX "orinoco_if_add() failed\n");
++ goto failed;
++ }
++ card->ndev_registered = 1;
++ return 0;
++ failed:
++ airport_detach(mdev);
++ return -ENODEV;
++} /* airport_attach */
++
++
++static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
++ " (Benjamin Herrenschmidt <benh@kernel.crashing.org>)";
++MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
++MODULE_DESCRIPTION("Driver for the Apple Airport wireless card.");
++MODULE_LICENSE("Dual MPL/GPL");
++
++static const struct of_device_id airport_match[] = {
++ {
++ .name = "radio",
++ },
++ {},
++};
++
++MODULE_DEVICE_TABLE(of, airport_match);
++
++static struct macio_driver airport_driver = {
++ .driver = {
++ .name = DRIVER_NAME,
++ .owner = THIS_MODULE,
++ .of_match_table = airport_match,
++ },
++ .probe = airport_attach,
++ .remove = airport_detach,
++ .suspend = airport_suspend,
++ .resume = airport_resume,
++};
++
++static int __init
++init_airport(void)
++{
++ printk(KERN_DEBUG "%s\n", version);
++
++ return macio_register_driver(&airport_driver);
++}
++
++static void __exit
++exit_airport(void)
++{
++ macio_unregister_driver(&airport_driver);
++}
++
++module_init(init_airport);
++module_exit(exit_airport);
+diff -Nur linux-6.15.6.orig/drivers/net/wireless/intersil/orinoco/cfg.c linux-6.15.6/drivers/net/wireless/intersil/orinoco/cfg.c
+--- linux-6.15.6.orig/drivers/net/wireless/intersil/orinoco/cfg.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-6.15.6/drivers/net/wireless/intersil/orinoco/cfg.c 2025-08-03 16:35:08.118093622 +0200
+@@ -0,0 +1,292 @@
++/* cfg80211 support
++ *
++ * See copyright notice in main.c
++ */
++#include <linux/ieee80211.h>
++#include <net/cfg80211.h>
++#include "hw.h"
++#include "main.h"
++#include "orinoco.h"
++
++#include "cfg.h"
++
++/* Supported bitrates. Must agree with hw.c */
++static struct ieee80211_rate orinoco_rates[] = {
++ { .bitrate = 10 },
++ { .bitrate = 20 },
++ { .bitrate = 55 },
++ { .bitrate = 110 },
++};
++
++static const void * const orinoco_wiphy_privid = &orinoco_wiphy_privid;
++
++/* Called after orinoco_private is allocated. */
++void orinoco_wiphy_init(struct wiphy *wiphy)
++{
++ struct orinoco_private *priv = wiphy_priv(wiphy);
++
++ wiphy->privid = orinoco_wiphy_privid;
++
++ set_wiphy_dev(wiphy, priv->dev);
++}
++
++/* Called after firmware is initialised */
++int orinoco_wiphy_register(struct wiphy *wiphy)
++{
++ struct orinoco_private *priv = wiphy_priv(wiphy);
++ int i, channels = 0;
++
++ if (priv->firmware_type == FIRMWARE_TYPE_AGERE)
++ wiphy->max_scan_ssids = 1;
++ else
++ wiphy->max_scan_ssids = 0;
++
++ wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
++
++ /* TODO: should we set if we only have demo ad-hoc?
++ * (priv->has_port3)
++ */
++ if (priv->has_ibss)
++ wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC);
++
++ if (!priv->broken_monitor || force_monitor)
++ wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
++
++ priv->band.bitrates = orinoco_rates;
++ priv->band.n_bitrates = ARRAY_SIZE(orinoco_rates);
++
++ /* Only support channels allowed by the card EEPROM */
++ for (i = 0; i < NUM_CHANNELS; i++) {
++ if (priv->channel_mask & (1 << i)) {
++ priv->channels[i].center_freq =
++ ieee80211_channel_to_frequency(i + 1,
++ NL80211_BAND_2GHZ);
++ channels++;
++ }
++ }
++ priv->band.channels = priv->channels;
++ priv->band.n_channels = channels;
++
++ wiphy->bands[NL80211_BAND_2GHZ] = &priv->band;
++ wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
++
++ i = 0;
++ if (priv->has_wep) {
++ priv->cipher_suites[i] = WLAN_CIPHER_SUITE_WEP40;
++ i++;
++
++ if (priv->has_big_wep) {
++ priv->cipher_suites[i] = WLAN_CIPHER_SUITE_WEP104;
++ i++;
++ }
++ }
++ if (priv->has_wpa) {
++ priv->cipher_suites[i] = WLAN_CIPHER_SUITE_TKIP;
++ i++;
++ }
++ wiphy->cipher_suites = priv->cipher_suites;
++ wiphy->n_cipher_suites = i;
++
++ wiphy->rts_threshold = priv->rts_thresh;
++ if (!priv->has_mwo)
++ wiphy->frag_threshold = priv->frag_thresh + 1;
++ wiphy->retry_short = priv->short_retry_limit;
++ wiphy->retry_long = priv->long_retry_limit;
++
++ return wiphy_register(wiphy);
++}
++
++static int orinoco_change_vif(struct wiphy *wiphy, struct net_device *dev,
++ enum nl80211_iftype type,
++ struct vif_params *params)
++{
++ struct orinoco_private *priv = wiphy_priv(wiphy);
++ int err = 0;
++ unsigned long lock;
++
++ if (orinoco_lock(priv, &lock) != 0)
++ return -EBUSY;
++
++ switch (type) {
++ case NL80211_IFTYPE_ADHOC:
++ if (!priv->has_ibss && !priv->has_port3)
++ err = -EINVAL;
++ break;
++
++ case NL80211_IFTYPE_STATION:
++ break;
++
++ case NL80211_IFTYPE_MONITOR:
++ if (priv->broken_monitor && !force_monitor) {
++ wiphy_warn(wiphy,
++ "Monitor mode support is buggy in this firmware, not enabling\n");
++ err = -EINVAL;
++ }
++ break;
++
++ default:
++ err = -EINVAL;
++ }
++
++ if (!err) {
++ priv->iw_mode = type;
++ set_port_type(priv);
++ err = orinoco_commit(priv);
++ }
++
++ orinoco_unlock(priv, &lock);
++
++ retur