* [PATCH wireless-next 0/4] wifi: cw1200: add XRadio XR819 support @ 2026-11-02 17:15 Pablo Mazzini 2026-11-02 17:16 ` [PATCH wireless-next 4/4] " Pablo Mazzini 0 siblings, 1 reply; 3+ messages in thread From: Pablo Mazzini @ 2026-11-02 17:15 UTC (permalink / raw) To: linux-wireless; +Cc: Pablo Mazzini, Ulf Hansson, linux-mmc, linux-kernel The XRadio XR819 SDIO Wi-Fi chip, used on the PocketBook Verse and on boards like the Orange Pi Zero, is a CW1x60 derivative. cw1200 drives it with a bootloader stage and two firmware differences: channel switch and join. Patch 2 moves the per-chip firmware into a table first. Tested on a PocketBook Verse: scan, WPA2-PSK, DHCP and ping. Pablo Mazzini (4): wifi: cw1200: don't update the beacon in station mode wifi: cw1200: describe the chips in a table wifi: cw1200: handle the XR819 channel switch and join wifi: cw1200: add XRadio XR819 support drivers/net/wireless/st/cw1200/Kconfig | 3 +- drivers/net/wireless/st/cw1200/cw1200.h | 3 +- drivers/net/wireless/st/cw1200/cw1200_sdio.c | 13 +- drivers/net/wireless/st/cw1200/fwio.c | 121 +++++++++++++------ drivers/net/wireless/st/cw1200/fwio.h | 15 ++- drivers/net/wireless/st/cw1200/main.c | 2 +- drivers/net/wireless/st/cw1200/sta.c | 12 +- drivers/net/wireless/st/cw1200/wsm.c | 3 +- include/linux/mmc/sdio_ids.h | 1 + 9 files changed, 126 insertions(+), 47 deletions(-) base-commit: 2c50cd5d10e8723797f28c7978f1f61688e0c0d2 -- 2.52.0 ^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH wireless-next 4/4] wifi: cw1200: add XRadio XR819 support 2026-11-02 17:15 [PATCH wireless-next 0/4] wifi: cw1200: add XRadio XR819 support Pablo Mazzini @ 2026-11-02 17:16 ` Pablo Mazzini 2026-11-11 12:19 ` Ulf Hansson 0 siblings, 1 reply; 3+ messages in thread From: Pablo Mazzini @ 2026-11-02 17:16 UTC (permalink / raw) To: linux-wireless; +Cc: Pablo Mazzini, Ulf Hansson, linux-mmc, linux-kernel The XR819 uses the ST-E SDIO vendor ID with device ID 0x2281 and reports itself as a CW1x60. It starts from a bootloader written into its memory, then takes the usual firmware download. Its modules run from a 24 MHz reference clock. Give the CW1x60 entry the XR819 bootloader and firmware, and load the bootloader instead of the NOP instruction. Signed-off-by: Pablo Mazzini <pmazzini@gmail.com> --- drivers/net/wireless/st/cw1200/Kconfig | 3 +- drivers/net/wireless/st/cw1200/cw1200_sdio.c | 13 ++++- drivers/net/wireless/st/cw1200/fwio.c | 58 ++++++++++++++++---- drivers/net/wireless/st/cw1200/fwio.h | 7 ++- include/linux/mmc/sdio_ids.h | 1 + 5 files changed, 66 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/st/cw1200/Kconfig b/drivers/net/wireless/st/cw1200/Kconfig index 03575e9894bb..9404e318137c 100644 --- a/drivers/net/wireless/st/cw1200/Kconfig +++ b/drivers/net/wireless/st/cw1200/Kconfig @@ -3,7 +3,8 @@ config CW1200 tristate "CW1200 WLAN support" depends on MAC80211 && CFG80211 help - This is a driver for the ST-E CW1100 & CW1200 WLAN chipsets. + This is a driver for the ST-E CW1100 & CW1200 and the XRadio + XR819 WLAN chipsets. This option just enables the driver core, see below for specific bus support. diff --git a/drivers/net/wireless/st/cw1200/cw1200_sdio.c b/drivers/net/wireless/st/cw1200/cw1200_sdio.c index 2b6afe221268..3db937785cab 100644 --- a/drivers/net/wireless/st/cw1200/cw1200_sdio.c +++ b/drivers/net/wireless/st/cw1200/cw1200_sdio.c @@ -35,6 +35,12 @@ static struct cw1200_platform_data_sdio sagrad_109x_evk_platform_data = { .sdd_file = "sdd_sagrad_1091_1098.bin", }; +/* XRadio XR819 modules, with a 24 MHz reference clock */ +static const struct cw1200_platform_data_sdio xr819_platform_data = { + .ref_clk = 24000, + .have_5ghz = false, +}; + /* Allow platform data to be overridden */ static struct cw1200_platform_data_sdio *global_plat_data = &sagrad_109x_evk_platform_data; @@ -51,6 +57,8 @@ struct hwbus_priv { static const struct sdio_device_id cw1200_sdio_ids[] = { { SDIO_DEVICE(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_XR819), + .driver_data = (kernel_ulong_t)&xr819_platform_data }, { /* end: all zeroes */ }, }; MODULE_DEVICE_TABLE(sdio, cw1200_sdio_ids); @@ -295,7 +303,10 @@ static int cw1200_sdio_probe(struct sdio_func *func, func->card->quirks |= MMC_QUIRK_LENIENT_FN0; - self->pdata = global_plat_data; /* FIXME */ + if (id->driver_data) + self->pdata = (const void *)id->driver_data; + else + self->pdata = global_plat_data; /* FIXME */ self->func = func; sdio_set_drvdata(func, self); sdio_claim_host(func); diff --git a/drivers/net/wireless/st/cw1200/fwio.c b/drivers/net/wireless/st/cw1200/fwio.c index 1a68ef6ed2fa..6b885fd717db 100644 --- a/drivers/net/wireless/st/cw1200/fwio.c +++ b/drivers/net/wireless/st/cw1200/fwio.c @@ -66,9 +66,10 @@ static const struct cw1200_chip cw1200_chips[] = { .firmware = FIRMWARE_CUT22, .sdd = SDD_FILE_22, }, { - .revision = CW1X60_HW_REV, - .firmware = FIRMWARE_CW1X60, - .sdd = SDD_FILE_CW1X60, + .revision = CW1X60_HW_REV, /* XRadio XR819 */ + .bootloader = BOOTLOADER_XR819, + .firmware = FIRMWARE_XR819, + .sdd = SDD_FILE_XR819, .sync_join = true, .no_switch_channel = true, }, @@ -85,6 +86,38 @@ static const struct cw1200_chip *cw1200_find_chip(int revision) return NULL; } +/* The bootloader is written word by word into the AHB memory */ +static int cw1200_load_bootloader(struct cw1200_common *priv) +{ + const struct firmware *bootloader; + const __le32 *data; + u32 addr = 0x08000000; + size_t i; + int ret; + + ret = request_firmware(&bootloader, priv->chip->bootloader, priv->pdev); + if (ret) { + pr_err("Can't load bootloader file %s.\n", priv->chip->bootloader); + return ret; + } + + data = (const __le32 *)bootloader->data; + for (i = 0; i < bootloader->size / 4; i++, addr += 4) { + ret = cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, + addr); + if (!ret) + ret = cw1200_reg_write_32(priv, ST90TDS_AHB_DPORT_REG_ID, + le32_to_cpu(data[i])); + if (ret) { + pr_err("Can't write bootloader.\n"); + break; + } + } + + release_firmware(bootloader); + return ret; +} + static int cw1200_load_firmware_cw1200(struct cw1200_common *priv) { int ret, block, num_blocks; @@ -136,6 +169,12 @@ static int cw1200_load_firmware_cw1200(struct cw1200_common *priv) if (!priv->sdd_path) priv->sdd_path = priv->chip->sdd; + if (priv->chip->bootloader) { + ret = cw1200_load_bootloader(priv); + if (ret) + return ret; + } + /* Initialize common registers */ APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, DOWNLOAD_ARE_YOU_HERE); APB_WRITE(DOWNLOAD_PUT_REG, 0); @@ -143,9 +182,11 @@ static int cw1200_load_firmware_cw1200(struct cw1200_common *priv) APB_WRITE(DOWNLOAD_STATUS_REG, DOWNLOAD_PENDING); APB_WRITE(DOWNLOAD_FLAGS_REG, 0); - /* Write the NOP Instruction */ - REG_WRITE(ST90TDS_SRAM_BASE_ADDR_REG_ID, 0xFFF20000); - REG_WRITE(ST90TDS_AHB_DPORT_REG_ID, 0xEAFFFFFE); + /* Write the NOP Instruction, unless the bootloader runs instead */ + if (!priv->chip->bootloader) { + REG_WRITE(ST90TDS_SRAM_BASE_ADDR_REG_ID, 0xFFF20000); + REG_WRITE(ST90TDS_AHB_DPORT_REG_ID, 0xEAFFFFFE); + } /* Release CPU from RESET */ REG_READ(ST90TDS_CONFIG_REG_ID, val32); @@ -483,11 +524,6 @@ int cw1200_load_firmware(struct cw1200_common *priv) switch (priv->hw_type) { case HIF_8601_SILICON: - if (priv->hw_revision == CW1X60_HW_REV) { - pr_err("Can't handle CW1160/1260 firmware load yet.\n"); - ret = -ENOTSUPP; - goto out; - } ret = cw1200_load_firmware_cw1200(priv); break; default: diff --git a/drivers/net/wireless/st/cw1200/fwio.h b/drivers/net/wireless/st/cw1200/fwio.h index 9925d4bcedda..fb66d564c304 100644 --- a/drivers/net/wireless/st/cw1200/fwio.h +++ b/drivers/net/wireless/st/cw1200/fwio.h @@ -14,13 +14,13 @@ #ifndef FWIO_H_INCLUDED #define FWIO_H_INCLUDED -#define BOOTLOADER_CW1X60 "boot_cw1x60.bin" -#define FIRMWARE_CW1X60 "wsm_cw1x60.bin" +#define BOOTLOADER_XR819 "xr819/boot_xr819.bin" +#define FIRMWARE_XR819 "xr819/fw_xr819.bin" #define FIRMWARE_CUT22 "wsm_22.bin" #define FIRMWARE_CUT20 "wsm_20.bin" #define FIRMWARE_CUT11 "wsm_11.bin" #define FIRMWARE_CUT10 "wsm_10.bin" -#define SDD_FILE_CW1X60 "sdd_cw1x60.bin" +#define SDD_FILE_XR819 "xr819/sdd_xr819.bin" #define SDD_FILE_22 "sdd_22.bin" #define SDD_FILE_20 "sdd_20.bin" #define SDD_FILE_11 "sdd_11.bin" @@ -28,6 +28,7 @@ struct cw1200_chip { int revision; + const char *bootloader; /* NULL: write the NOP instruction */ const char *firmware; const char *sdd; bool sync_join; /* no asynchronous join */ diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h index dfffda3d8002..35789be747a5 100644 --- a/include/linux/mmc/sdio_ids.h +++ b/include/linux/mmc/sdio_ids.h @@ -27,6 +27,7 @@ #define SDIO_VENDOR_ID_STE 0x0020 #define SDIO_DEVICE_ID_STE_CW1200 0x2280 +#define SDIO_DEVICE_ID_STE_XR819 0x2281 #define SDIO_VENDOR_ID_INTEL 0x0089 #define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX 0x1402 -- 2.52.0 ^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH wireless-next 4/4] wifi: cw1200: add XRadio XR819 support 2026-11-02 17:16 ` [PATCH wireless-next 4/4] " Pablo Mazzini @ 2026-11-11 12:19 ` Ulf Hansson 0 siblings, 0 replies; 3+ messages in thread From: Ulf Hansson @ 2026-11-11 12:19 UTC (permalink / raw) To: Pablo Mazzini; +Cc: linux-wireless, Ulf Hansson, linux-mmc, linux-kernel On Mon, Nov 2, 2026 at 6:16 PM Pablo Mazzini <pmazzini@gmail.com> wrote: > > The XR819 uses the ST-E SDIO vendor ID with device ID 0x2281 and > reports itself as a CW1x60. It starts from a bootloader written into > its memory, then takes the usual firmware download. Its modules run > from a 24 MHz reference clock. > > Give the CW1x60 entry the XR819 bootloader and firmware, and load the > bootloader instead of the NOP instruction. > > Signed-off-by: Pablo Mazzini <pmazzini@gmail.com> Please funnel this via the wireless tree. Acked-by: Ulf Hansson <ulfh@kernel.org> Kind regards Uffe > --- > drivers/net/wireless/st/cw1200/Kconfig | 3 +- > drivers/net/wireless/st/cw1200/cw1200_sdio.c | 13 ++++- > drivers/net/wireless/st/cw1200/fwio.c | 58 ++++++++++++++++---- > drivers/net/wireless/st/cw1200/fwio.h | 7 ++- > include/linux/mmc/sdio_ids.h | 1 + > 5 files changed, 66 insertions(+), 16 deletions(-) > > diff --git a/drivers/net/wireless/st/cw1200/Kconfig b/drivers/net/wireless/st/cw1200/Kconfig > index 03575e9894bb..9404e318137c 100644 > --- a/drivers/net/wireless/st/cw1200/Kconfig > +++ b/drivers/net/wireless/st/cw1200/Kconfig > @@ -3,7 +3,8 @@ config CW1200 > tristate "CW1200 WLAN support" > depends on MAC80211 && CFG80211 > help > - This is a driver for the ST-E CW1100 & CW1200 WLAN chipsets. > + This is a driver for the ST-E CW1100 & CW1200 and the XRadio > + XR819 WLAN chipsets. > This option just enables the driver core, see below for > specific bus support. > > diff --git a/drivers/net/wireless/st/cw1200/cw1200_sdio.c b/drivers/net/wireless/st/cw1200/cw1200_sdio.c > index 2b6afe221268..3db937785cab 100644 > --- a/drivers/net/wireless/st/cw1200/cw1200_sdio.c > +++ b/drivers/net/wireless/st/cw1200/cw1200_sdio.c > @@ -35,6 +35,12 @@ static struct cw1200_platform_data_sdio sagrad_109x_evk_platform_data = { > .sdd_file = "sdd_sagrad_1091_1098.bin", > }; > > +/* XRadio XR819 modules, with a 24 MHz reference clock */ > +static const struct cw1200_platform_data_sdio xr819_platform_data = { > + .ref_clk = 24000, > + .have_5ghz = false, > +}; > + > /* Allow platform data to be overridden */ > static struct cw1200_platform_data_sdio *global_plat_data = &sagrad_109x_evk_platform_data; > > @@ -51,6 +57,8 @@ struct hwbus_priv { > > static const struct sdio_device_id cw1200_sdio_ids[] = { > { SDIO_DEVICE(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200) }, > + { SDIO_DEVICE(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_XR819), > + .driver_data = (kernel_ulong_t)&xr819_platform_data }, > { /* end: all zeroes */ }, > }; > MODULE_DEVICE_TABLE(sdio, cw1200_sdio_ids); > @@ -295,7 +303,10 @@ static int cw1200_sdio_probe(struct sdio_func *func, > > func->card->quirks |= MMC_QUIRK_LENIENT_FN0; > > - self->pdata = global_plat_data; /* FIXME */ > + if (id->driver_data) > + self->pdata = (const void *)id->driver_data; > + else > + self->pdata = global_plat_data; /* FIXME */ > self->func = func; > sdio_set_drvdata(func, self); > sdio_claim_host(func); > diff --git a/drivers/net/wireless/st/cw1200/fwio.c b/drivers/net/wireless/st/cw1200/fwio.c > index 1a68ef6ed2fa..6b885fd717db 100644 > --- a/drivers/net/wireless/st/cw1200/fwio.c > +++ b/drivers/net/wireless/st/cw1200/fwio.c > @@ -66,9 +66,10 @@ static const struct cw1200_chip cw1200_chips[] = { > .firmware = FIRMWARE_CUT22, > .sdd = SDD_FILE_22, > }, { > - .revision = CW1X60_HW_REV, > - .firmware = FIRMWARE_CW1X60, > - .sdd = SDD_FILE_CW1X60, > + .revision = CW1X60_HW_REV, /* XRadio XR819 */ > + .bootloader = BOOTLOADER_XR819, > + .firmware = FIRMWARE_XR819, > + .sdd = SDD_FILE_XR819, > .sync_join = true, > .no_switch_channel = true, > }, > @@ -85,6 +86,38 @@ static const struct cw1200_chip *cw1200_find_chip(int revision) > return NULL; > } > > +/* The bootloader is written word by word into the AHB memory */ > +static int cw1200_load_bootloader(struct cw1200_common *priv) > +{ > + const struct firmware *bootloader; > + const __le32 *data; > + u32 addr = 0x08000000; > + size_t i; > + int ret; > + > + ret = request_firmware(&bootloader, priv->chip->bootloader, priv->pdev); > + if (ret) { > + pr_err("Can't load bootloader file %s.\n", priv->chip->bootloader); > + return ret; > + } > + > + data = (const __le32 *)bootloader->data; > + for (i = 0; i < bootloader->size / 4; i++, addr += 4) { > + ret = cw1200_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, > + addr); > + if (!ret) > + ret = cw1200_reg_write_32(priv, ST90TDS_AHB_DPORT_REG_ID, > + le32_to_cpu(data[i])); > + if (ret) { > + pr_err("Can't write bootloader.\n"); > + break; > + } > + } > + > + release_firmware(bootloader); > + return ret; > +} > + > static int cw1200_load_firmware_cw1200(struct cw1200_common *priv) > { > int ret, block, num_blocks; > @@ -136,6 +169,12 @@ static int cw1200_load_firmware_cw1200(struct cw1200_common *priv) > if (!priv->sdd_path) > priv->sdd_path = priv->chip->sdd; > > + if (priv->chip->bootloader) { > + ret = cw1200_load_bootloader(priv); > + if (ret) > + return ret; > + } > + > /* Initialize common registers */ > APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, DOWNLOAD_ARE_YOU_HERE); > APB_WRITE(DOWNLOAD_PUT_REG, 0); > @@ -143,9 +182,11 @@ static int cw1200_load_firmware_cw1200(struct cw1200_common *priv) > APB_WRITE(DOWNLOAD_STATUS_REG, DOWNLOAD_PENDING); > APB_WRITE(DOWNLOAD_FLAGS_REG, 0); > > - /* Write the NOP Instruction */ > - REG_WRITE(ST90TDS_SRAM_BASE_ADDR_REG_ID, 0xFFF20000); > - REG_WRITE(ST90TDS_AHB_DPORT_REG_ID, 0xEAFFFFFE); > + /* Write the NOP Instruction, unless the bootloader runs instead */ > + if (!priv->chip->bootloader) { > + REG_WRITE(ST90TDS_SRAM_BASE_ADDR_REG_ID, 0xFFF20000); > + REG_WRITE(ST90TDS_AHB_DPORT_REG_ID, 0xEAFFFFFE); > + } > > /* Release CPU from RESET */ > REG_READ(ST90TDS_CONFIG_REG_ID, val32); > @@ -483,11 +524,6 @@ int cw1200_load_firmware(struct cw1200_common *priv) > > switch (priv->hw_type) { > case HIF_8601_SILICON: > - if (priv->hw_revision == CW1X60_HW_REV) { > - pr_err("Can't handle CW1160/1260 firmware load yet.\n"); > - ret = -ENOTSUPP; > - goto out; > - } > ret = cw1200_load_firmware_cw1200(priv); > break; > default: > diff --git a/drivers/net/wireless/st/cw1200/fwio.h b/drivers/net/wireless/st/cw1200/fwio.h > index 9925d4bcedda..fb66d564c304 100644 > --- a/drivers/net/wireless/st/cw1200/fwio.h > +++ b/drivers/net/wireless/st/cw1200/fwio.h > @@ -14,13 +14,13 @@ > #ifndef FWIO_H_INCLUDED > #define FWIO_H_INCLUDED > > -#define BOOTLOADER_CW1X60 "boot_cw1x60.bin" > -#define FIRMWARE_CW1X60 "wsm_cw1x60.bin" > +#define BOOTLOADER_XR819 "xr819/boot_xr819.bin" > +#define FIRMWARE_XR819 "xr819/fw_xr819.bin" > #define FIRMWARE_CUT22 "wsm_22.bin" > #define FIRMWARE_CUT20 "wsm_20.bin" > #define FIRMWARE_CUT11 "wsm_11.bin" > #define FIRMWARE_CUT10 "wsm_10.bin" > -#define SDD_FILE_CW1X60 "sdd_cw1x60.bin" > +#define SDD_FILE_XR819 "xr819/sdd_xr819.bin" > #define SDD_FILE_22 "sdd_22.bin" > #define SDD_FILE_20 "sdd_20.bin" > #define SDD_FILE_11 "sdd_11.bin" > @@ -28,6 +28,7 @@ > > struct cw1200_chip { > int revision; > + const char *bootloader; /* NULL: write the NOP instruction */ > const char *firmware; > const char *sdd; > bool sync_join; /* no asynchronous join */ > diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h > index dfffda3d8002..35789be747a5 100644 > --- a/include/linux/mmc/sdio_ids.h > +++ b/include/linux/mmc/sdio_ids.h > @@ -27,6 +27,7 @@ > > #define SDIO_VENDOR_ID_STE 0x0020 > #define SDIO_DEVICE_ID_STE_CW1200 0x2280 > +#define SDIO_DEVICE_ID_STE_XR819 0x2281 > > #define SDIO_VENDOR_ID_INTEL 0x0089 > #define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX 0x1402 > -- > 2.52.0 > ^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-11-11 12:19 UTC | newest] Thread overview: 3+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-11-02 17:15 [PATCH wireless-next 0/4] wifi: cw1200: add XRadio XR819 support Pablo Mazzini 2026-11-02 17:16 ` [PATCH wireless-next 4/4] " Pablo Mazzini 2026-11-11 12:19 ` Ulf Hansson
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox