diff options
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.patch | 720 | ||||
-rw-r--r-- | target/linux/patches/6.15.6/orinoco.patch | 12933 |
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 |