summaryrefslogtreecommitdiff
path: root/firmware/drivers
diff options
context:
space:
mode:
authorTomasz Moń <desowin@gmail.com>2021-07-02 12:02:26 +0200
committerTomasz Moń <desowin@gmail.com>2021-07-04 07:41:44 +0000
commite11fa5f74eb49b10ebefbe8882bb50186dffb2a4 (patch)
tree295a28ca58bc4d7b5153629046c1e2894db02f5e /firmware/drivers
parentc9f2308a1d4401ceefaac47b1ea851530d07e47d (diff)
Sansa Connect: Initial libertas WiFi driver port
Import non-free firmware image from linux-firmware package. Firmware loading works but is disabled at compile time because just loading firmware without configuring device results in higher power consumption without any benefit to end user. Change-Id: I8fd252c49385ede1ea4e0f9b1e29adeb331ab8ae
Diffstat (limited to 'firmware/drivers')
-rw-r--r--firmware/drivers/libertas/firmware/LICENCE.Marvell22
-rw-r--r--firmware/drivers/libertas/firmware/gspi8686_v9.binbin0 -> 126652 bytes
-rw-r--r--firmware/drivers/libertas/firmware/gspi8686_v9_helper.binbin0 -> 2140 bytes
-rw-r--r--firmware/drivers/libertas/if_spi.c674
-rw-r--r--firmware/drivers/libertas/if_spi.h215
-rw-r--r--firmware/drivers/libertas/if_spi_drv.h34
6 files changed, 945 insertions, 0 deletions
diff --git a/firmware/drivers/libertas/firmware/LICENCE.Marvell b/firmware/drivers/libertas/firmware/LICENCE.Marvell
new file mode 100644
index 0000000000..fdf4cda9c0
--- /dev/null
+++ b/firmware/drivers/libertas/firmware/LICENCE.Marvell
@@ -0,0 +1,22 @@
+Copyright © 2019. Marvell International Ltd. All rights reserved.
+
+Redistribution and use in binary form is permitted provided that the following
+conditions are met:
+
+1. Redistributions must reproduce the above copyright notice, this list of
+conditions and the following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+2. Redistribution and use shall be used only with Marvell silicon products.
+Any other use, reproduction, modification, translation, or compilation of the
+Software is prohibited.
+
+3. No reverse engineering, decompilation, or disassembly is permitted.
+
+TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THIS SOFTWARE IS PROVIDED
+“AS IS” WITHOUT WARRANTY OF ANY KIND, INCLUDING, WITHOUT LIMITATION, ANY EXPRESS
+OR IMPLIED WARRANTIES OF MERCHANTABILITY, ACCURACY, FITNESS OR SUFFICIENCY FOR A
+PARTICULAR PURPOSE, SATISFACTORY QUALITY, CORRESPONDENCE WITH DESCRIPTION, QUIET
+ENJOYMENT OR NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY RIGHTS.
+MARVELL, ITS AFFILIATES AND THEIR SUPPLIERS DISCLAIM ANY WARRANTY THAT THE
+DELIVERABLES WILL OPERATE WITHOUT INTERRUPTION OR BE ERROR-FREE.
diff --git a/firmware/drivers/libertas/firmware/gspi8686_v9.bin b/firmware/drivers/libertas/firmware/gspi8686_v9.bin
new file mode 100644
index 0000000000..58dc03d30e
--- /dev/null
+++ b/firmware/drivers/libertas/firmware/gspi8686_v9.bin
Binary files differ
diff --git a/firmware/drivers/libertas/firmware/gspi8686_v9_helper.bin b/firmware/drivers/libertas/firmware/gspi8686_v9_helper.bin
new file mode 100644
index 0000000000..424cfb386c
--- /dev/null
+++ b/firmware/drivers/libertas/firmware/gspi8686_v9_helper.bin
Binary files differ
diff --git a/firmware/drivers/libertas/if_spi.c b/firmware/drivers/libertas/if_spi.c
new file mode 100644
index 0000000000..be564cc64f
--- /dev/null
+++ b/firmware/drivers/libertas/if_spi.c
@@ -0,0 +1,674 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 by Tomasz Moń
+ * Ported from Linux libertas driver
+ * Copyright 2008 Analog Devices Inc.
+ * Authors:
+ * Andrey Yurovsky <andrey@cozybit.com>
+ * Colin McCabe <colin@cozybit.com>
+ * Inspired by if_sdio.c, Copyright 2007-2008 Pierre Ossman
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "config.h"
+/*#define LOGF_ENABLE*/
+#include "logf.h"
+#include "errno.h"
+#include "file.h"
+#include "panic.h"
+#include "system.h"
+#include "tick.h"
+#include <stddef.h>
+#include "if_spi.h"
+#include "if_spi_drv.h"
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#define DIV_ROUND_UP(n,d) (((n) + (d) - 1)/(d))
+
+struct if_spi_card
+{
+ /* The card ID and card revision, as reported by the hardware. */
+ uint16_t card_id;
+ uint8_t card_rev;
+
+ unsigned long spu_port_delay;
+ unsigned long spu_reg_delay;
+
+ uint8_t cmd_buffer[IF_SPI_CMD_BUF_SIZE];
+};
+
+#define MODEL_8686 0x0b
+
+static const struct
+{
+ uint16_t model;
+ const char *helper;
+ const char *main;
+}
+fw_table[] =
+{
+ { MODEL_8686, ROCKBOX_DIR"/libertas/gspi8686_v9_helper.bin", ROCKBOX_DIR"/libertas/gspi8686_v9.bin" },
+ { 0, NULL, NULL }
+};
+
+
+/*
+ * SPI Interface Unit Routines
+ *
+ * The SPU sits between the host and the WLAN module.
+ * All communication with the firmware is through SPU transactions.
+ *
+ * First we have to put a SPU register name on the bus. Then we can
+ * either read from or write to that register.
+ *
+ */
+
+static void spu_transaction_init(struct if_spi_card *card)
+{
+ (void)card;
+ /* Linux delays 400 ns if spu_transaction_finish() was called
+ * within the same jiffy. As we don't have jiffy counter nor
+ * nanosecond delays, simply delay for 1 us. This currently
+ * does not really matter as this driver simply loads firmware.
+ */
+ udelay(1);
+ libertas_spi_cs(0); /* assert CS */
+}
+
+static void spu_transaction_finish(struct if_spi_card *card)
+{
+ (void)card;
+ libertas_spi_cs(1); /* drop CS */
+}
+
+/*
+ * Write out a byte buffer to an SPI register,
+ * using a series of 16-bit transfers.
+ */
+static int spu_write(struct if_spi_card *card, uint16_t reg, const uint8_t *buf, int len)
+{
+ int err = 0;
+ uint8_t reg_out[2];
+
+ /* You must give an even number of bytes to the SPU, even if it
+ * doesn't care about the last one. */
+ if (len & 0x1)
+ panicf("Odd length in spu_write()");
+
+ reg |= IF_SPI_WRITE_OPERATION_MASK;
+ reg_out[0] = (reg & 0x00FF);
+ reg_out[1] = (reg & 0xFF00) >> 8;
+
+ spu_transaction_init(card);
+ libertas_spi_tx(reg_out, sizeof(reg_out));
+ libertas_spi_tx(buf, len);
+ spu_transaction_finish(card);
+ return err;
+}
+
+static inline int spu_write_u16(struct if_spi_card *card, uint16_t reg, uint16_t val)
+{
+ uint8_t buf[2];
+ buf[0] = (val & 0x00FF);
+ buf[1] = (val & 0xFF00) >> 8;
+ return spu_write(card, reg, buf, sizeof(buf));
+}
+
+static inline int spu_reg_is_port_reg(uint16_t reg)
+{
+ switch (reg)
+ {
+ case IF_SPI_IO_RDWRPORT_REG:
+ case IF_SPI_CMD_RDWRPORT_REG:
+ case IF_SPI_DATA_RDWRPORT_REG:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int spu_read(struct if_spi_card *card, uint16_t reg, uint8_t *buf, int len)
+{
+ unsigned int delay;
+ int err = 0;
+ uint8_t reg_out[2];
+
+ /*
+ * You must take an even number of bytes from the SPU, even if you
+ * don't care about the last one.
+ */
+ if (len & 0x1)
+ panicf("Odd length in spu_read()");
+
+ reg |= IF_SPI_READ_OPERATION_MASK;
+ reg_out[0] = (reg & 0x00FF);
+ reg_out[1] = (reg & 0xFF00) >> 8;
+
+ spu_transaction_init(card);
+ libertas_spi_tx(reg_out, sizeof(reg_out));
+
+ delay = spu_reg_is_port_reg(reg) ? card->spu_port_delay : card->spu_reg_delay;
+ /* Busy-wait while the SPU fills the FIFO */
+ delay = DIV_ROUND_UP((100 + (delay * 10)), 1000);
+ if (delay < 1000)
+ udelay(delay);
+ else
+ mdelay(DIV_ROUND_UP(delay, 1000));
+
+ libertas_spi_rx(buf, len);
+ spu_transaction_finish(card);
+ return err;
+}
+
+/* Read 16 bits from an SPI register */
+static inline int spu_read_u16(struct if_spi_card *card, uint16_t reg, uint16_t *val)
+{
+ uint8_t buf[2];
+ int ret;
+
+ ret = spu_read(card, reg, buf, sizeof(buf));
+ if (ret == 0)
+ *val = buf[0] | (buf[1] << 8);
+
+ return ret;
+}
+
+/*
+ * Read 32 bits from an SPI register.
+ * The low 16 bits are read first.
+ */
+static int spu_read_u32(struct if_spi_card *card, uint16_t reg, uint32_t *val)
+{
+ uint8_t buf[4];
+ int err;
+
+ err = spu_read(card, reg, buf, sizeof(buf));
+ if (!err)
+ *val = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+ return err;
+}
+
+/*
+ * Keep reading 16 bits from an SPI register until you get the correct result.
+ *
+ * If mask = 0, the correct result is any non-zero number.
+ * If mask != 0, the correct result is any number where
+ * number & target_mask == target
+ *
+ * Returns -ETIMEDOUT if a five seconds passes without the correct result.
+ */
+static int spu_wait_for_u16(struct if_spi_card *card, uint16_t reg,
+ uint16_t target_mask, uint16_t target)
+{
+ int err;
+ unsigned long timeout = current_tick + 5*HZ;
+ while (1)
+ {
+ uint16_t val;
+ err = spu_read_u16(card, reg, &val);
+ if (err)
+ return err;
+ if (target_mask)
+ {
+ if ((val & target_mask) == target)
+ return 0;
+ }
+ else
+ {
+ if (val)
+ return 0;
+ }
+ udelay(100);
+ if (TIME_AFTER(current_tick, timeout))
+ {
+ logf("%s: timeout with val=%02x, target_mask=%02x, target=%02x",
+ __func__, val, target_mask, target);
+ return -ETIMEDOUT;
+ }
+ }
+}
+
+/*
+ * Read 16 bits from an SPI register until you receive a specific value.
+ * Returns -ETIMEDOUT if a 4 tries pass without success.
+ */
+static int spu_wait_for_u32(struct if_spi_card *card, uint32_t reg, uint32_t target)
+{
+ int err, try;
+ for (try = 0; try < 4; ++try)
+ {
+ uint32_t val = 0;
+ err = spu_read_u32(card, reg, &val);
+ if (err)
+ return err;
+ if (val == target)
+ return 0;
+ mdelay(100);
+ }
+ return -ETIMEDOUT;
+}
+
+static int spu_set_interrupt_mode(struct if_spi_card *card,
+ int suppress_host_int,
+ int auto_int)
+{
+ int err = 0;
+
+ /*
+ * We can suppress a host interrupt by clearing the appropriate
+ * bit in the "host interrupt status mask" register
+ */
+ if (suppress_host_int) {
+ err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0);
+ if (err)
+ return err;
+ } else {
+ err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG,
+ IF_SPI_HISM_TX_DOWNLOAD_RDY |
+ IF_SPI_HISM_RX_UPLOAD_RDY |
+ IF_SPI_HISM_CMD_DOWNLOAD_RDY |
+ IF_SPI_HISM_CARDEVENT |
+ IF_SPI_HISM_CMD_UPLOAD_RDY);
+ if (err)
+ return err;
+ }
+
+ /*
+ * If auto-interrupts are on, the completion of certain transactions
+ * will trigger an interrupt automatically. If auto-interrupts
+ * are off, we need to set the "Card Interrupt Cause" register to
+ * trigger a card interrupt.
+ */
+ if (auto_int) {
+ err = spu_write_u16(card, IF_SPI_HOST_INT_CTRL_REG,
+ IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO |
+ IF_SPI_HICT_RX_UPLOAD_OVER_AUTO |
+ IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO |
+ IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO);
+ if (err)
+ return err;
+ } else {
+ err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0);
+ if (err)
+ return err;
+ }
+ return err;
+}
+
+static int spu_get_chip_revision(struct if_spi_card *card,
+ uint16_t *card_id, uint8_t *card_rev)
+{
+ int err = 0;
+ uint32_t dev_ctrl;
+ err = spu_read_u32(card, IF_SPI_DEVICEID_CTRL_REG, &dev_ctrl);
+ if (err)
+ return err;
+ *card_id = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_ID(dev_ctrl);
+ *card_rev = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dev_ctrl);
+ return err;
+}
+
+static int spu_set_bus_mode(struct if_spi_card *card, uint16_t mode)
+{
+ int err = 0;
+ uint16_t rval;
+ /* set bus mode */
+ err = spu_write_u16(card, IF_SPI_SPU_BUS_MODE_REG, mode);
+ if (err)
+ return err;
+ /* Check that we were able to read back what we just wrote. */
+ err = spu_read_u16(card, IF_SPI_SPU_BUS_MODE_REG, &rval);
+ if (err)
+ return err;
+ if ((rval & 0xF) != mode)
+ {
+ logf("Can't read bus mode register");
+ return -EIO;
+ }
+ return 0;
+}
+
+static int spu_init(struct if_spi_card *card)
+{
+ int err = 0;
+ uint32_t delay;
+
+ err = spu_set_bus_mode(card,
+ IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING |
+ IF_SPI_BUS_MODE_DELAY_METHOD_TIMED |
+ IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA);
+ if (err)
+ return err;
+ card->spu_port_delay = 1000;
+ card->spu_reg_delay = 1000;
+ err = spu_read_u32(card, IF_SPI_DELAY_READ_REG, &delay);
+ if (err)
+ return err;
+ card->spu_port_delay = delay & 0x0000ffff;
+ card->spu_reg_delay = (delay & 0xffff0000) >> 16;
+
+ logf("Initialized SPU unit. "
+ "spu_port_delay=0x%04lx, spu_reg_delay=0x%04lx",
+ card->spu_port_delay, card->spu_reg_delay);
+ return err;
+}
+
+
+/*
+ * Firmware Loading
+ */
+
+static int if_spi_prog_helper_firmware(struct if_spi_card *card, int fd)
+{
+ int err = 0;
+ int bytes_read;
+ uint8_t *temp = card->cmd_buffer;
+
+ err = spu_set_interrupt_mode(card, 1, 0);
+ if (err)
+ goto out;
+
+ /* Load helper firmware image */
+ while ((bytes_read = read(fd, temp, HELPER_FW_LOAD_CHUNK_SZ)) > 0)
+ {
+ /*
+ * Scratch pad 1 should contain the number of bytes we
+ * want to download to the firmware
+ */
+ err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG,
+ HELPER_FW_LOAD_CHUNK_SZ);
+ if (err)
+ goto out;
+
+ err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG,
+ IF_SPI_HIST_CMD_DOWNLOAD_RDY,
+ IF_SPI_HIST_CMD_DOWNLOAD_RDY);
+ if (err)
+ goto out;
+
+ /*
+ * Feed the data into the command read/write port reg
+ * in chunks of 64 bytes
+ */
+ memset(temp + bytes_read, 0, HELPER_FW_LOAD_CHUNK_SZ - bytes_read);
+ mdelay(10);
+ err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG,
+ temp, HELPER_FW_LOAD_CHUNK_SZ);
+ if (err)
+ goto out;
+
+ /* Interrupt the boot code */
+ err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
+ if (err)
+ goto out;
+ err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
+ IF_SPI_CIC_CMD_DOWNLOAD_OVER);
+ if (err)
+ goto out;
+ }
+
+ /*
+ * Once the helper / single stage firmware download is complete,
+ * write 0 to scratch pad 1 and interrupt the
+ * bootloader. This completes the helper download.
+ */
+ err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, FIRMWARE_DNLD_OK);
+ if (err)
+ goto out;
+ err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
+ if (err)
+ goto out;
+ err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
+ IF_SPI_CIC_CMD_DOWNLOAD_OVER);
+out:
+ if (err)
+ logf("failed to load helper firmware (err=%d)", err);
+
+ return err;
+}
+
+/*
+ * Returns the length of the next packet the firmware expects us to send.
+ * Sets crc_err if the previous transfer had a CRC error.
+ */
+static int if_spi_prog_main_firmware_check_len(struct if_spi_card *card,
+ int *crc_err)
+{
+ uint16_t len;
+ int err = 0;
+
+ /*
+ * wait until the host interrupt status register indicates
+ * that we are ready to download
+ */
+ err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG,
+ IF_SPI_HIST_CMD_DOWNLOAD_RDY,
+ IF_SPI_HIST_CMD_DOWNLOAD_RDY);
+ if (err)
+ {
+ logf("timed out waiting for host_int_status");
+ return err;
+ }
+
+ /* Ask the device how many bytes of firmware it wants. */
+ err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len);
+ if (err)
+ return err;
+
+ if (len > IF_SPI_CMD_BUF_SIZE)
+ {
+ logf("firmware load device requested a larger transfer than we are prepared to handle (len = %d)",
+ len);
+ return -EIO;
+ }
+ if (len & 0x1) {
+ logf("%s: crc error", __func__);
+ len &= ~0x1;
+ *crc_err = 1;
+ } else
+ *crc_err = 0;
+
+ return len;
+}
+
+static int if_spi_prog_main_firmware(struct if_spi_card *card, int fd)
+{
+ int len;
+ int bytes_read = 0, crc_err = 0, err = 0;
+ uint16_t num_crc_errs;
+
+ err = spu_set_interrupt_mode(card, 1, 0);
+ if (err)
+ goto out;
+
+ err = spu_wait_for_u16(card, IF_SPI_SCRATCH_1_REG, 0, 0);
+ if (err)
+ {
+ logf("%s: timed out waiting for initial scratch reg = 0", __func__);
+ goto out;
+ }
+
+ num_crc_errs = 0;
+ while ((len = if_spi_prog_main_firmware_check_len(card, &crc_err)))
+ {
+ if (len < 0)
+ {
+ err = len;
+ goto out;
+ }
+ if (crc_err)
+ {
+ /* Previous transfer failed. */
+ if (++num_crc_errs > MAX_MAIN_FW_LOAD_CRC_ERR)
+ {
+ logf("Too many CRC errors encountered in firmware load.");
+ err = -EIO;
+ goto out;
+ }
+
+ /* Rewind so we read back the data from previous transfer */
+ lseek(fd, -bytes_read, SEEK_CUR);
+ }
+
+ bytes_read = read(fd, card->cmd_buffer, len);
+ if (bytes_read < 0)
+ {
+ /*
+ * If there are no more bytes left, we would normally
+ * expect to have terminated with len = 0
+ */
+ logf("Firmware load wants more bytes than we have to offer.");
+ break;
+ }
+ else if (bytes_read < len)
+ {
+ memset(card->cmd_buffer + bytes_read, 0, len - bytes_read);
+ }
+
+ err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
+ if (err)
+ goto out;
+ err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG, card->cmd_buffer, len);
+ if (err)
+ goto out;
+ err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
+ IF_SPI_CIC_CMD_DOWNLOAD_OVER);
+ if (err)
+ goto out;
+ }
+ if (read(fd, card->cmd_buffer, IF_SPI_CMD_BUF_SIZE) > 0)
+ {
+ logf("firmware load wants fewer bytes than we have to offer");
+ }
+
+ /* Confirm firmware download */
+ err = spu_wait_for_u32(card, IF_SPI_SCRATCH_4_REG,
+ SUCCESSFUL_FW_DOWNLOAD_MAGIC);
+ if (err)
+ {
+ logf("failed to confirm the firmware download");
+ goto out;
+ }
+
+out:
+ if (err)
+ logf("failed to load firmware (err=%d)", err);
+
+ return err;
+}
+
+static int if_spi_init_card(struct if_spi_card *card)
+{
+ int err;
+ size_t i;
+ uint32_t scratch;
+ int fd;
+
+ err = spu_init(card);
+ if (err)
+ goto out;
+ err = spu_get_chip_revision(card, &card->card_id, &card->card_rev);
+ if (err)
+ goto out;
+
+ err = spu_read_u32(card, IF_SPI_SCRATCH_4_REG, &scratch);
+ if (err)
+ goto out;
+ if (scratch == SUCCESSFUL_FW_DOWNLOAD_MAGIC)
+ logf("Firmware is already loaded for Marvell WLAN 802.11 adapter");
+ else {
+ /* Check if we support this card */
+ for (i = 0; i < ARRAY_SIZE(fw_table); i++) {
+ if (card->card_id == fw_table[i].model)
+ break;
+ }
+ if (i == ARRAY_SIZE(fw_table)) {
+ logf("Unsupported chip_id: 0x%02x", card->card_id);
+ err = -ENODEV;
+ goto out;
+ }
+
+ logf("Initializing FW for Marvell WLAN 802.11 adapter "
+ "(chip_id = 0x%04x, chip_rev = 0x%02x)",
+ card->card_id, card->card_rev);
+
+ fd = open(fw_table[i].helper, O_RDONLY);
+ if (fd >= 0)
+ {
+ err = if_spi_prog_helper_firmware(card, fd);
+ close(fd);
+ if (err)
+ goto out;
+ }
+ else
+ {
+ logf("failed to find firmware helper (%s)", fw_table[i].helper);
+ err = -ENOENT;
+ goto out;
+ }
+
+ fd = open(fw_table[i].main, O_RDONLY);
+ if (fd >= 0)
+ {
+ err = if_spi_prog_main_firmware(card, fd);
+ close(fd);
+ if (err)
+ goto out;
+ }
+ else
+ {
+ logf("failed to find firmware (%s)", fw_table[i].main);
+ err = -ENOENT;
+ goto out;
+ }
+
+ logf("loaded FW for Marvell WLAN 802.11 adapter");
+ }
+
+ err = spu_set_interrupt_mode(card, 0, 1);
+ if (err)
+ goto out;
+
+out:
+ return err;
+}
+
+void wifi_init(void) INIT_ATTR
+{
+#if 0
+ static struct if_spi_card card;
+ libertas_spi_init();
+ libertas_spi_pd(1);
+ libertas_spi_reset(1);
+ mdelay(100);
+ if (!if_spi_init_card(&card))
+ {
+ /* TODO: Configure card and enter deep sleep */
+ }
+ else
+#else
+ libertas_spi_init();
+ (void)if_spi_init_card;
+#endif
+ {
+ /* Keep the lines in lowest power configuration */
+ libertas_spi_pd(0);
+ libertas_spi_reset(1);
+ libertas_spi_cs(1);
+ }
+}
diff --git a/firmware/drivers/libertas/if_spi.h b/firmware/drivers/libertas/if_spi.h
new file mode 100644
index 0000000000..bfca12981a
--- /dev/null
+++ b/firmware/drivers/libertas/if_spi.h
@@ -0,0 +1,215 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 by Tomasz Moń
+ * Ported from Linux libertas driver
+ * Copyright 2008 Analog Devices Inc.
+ * Authors:
+ * Andrey Yurovsky <andrey@cozybit.com>
+ * Colin McCabe <colin@cozybit.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef _LBS_IF_SPI_H_
+#define _LBS_IF_SPI_H_
+
+#define IPFIELD_ALIGN_OFFSET 2
+#define IF_SPI_CMD_BUF_SIZE 2400
+
+/***************** Firmware *****************/
+
+#define IF_SPI_FW_NAME_MAX 30
+
+#define MAX_MAIN_FW_LOAD_CRC_ERR 10
+
+/* Chunk size when loading the helper firmware */
+#define HELPER_FW_LOAD_CHUNK_SZ 64
+
+/* Value to write to indicate end of helper firmware dnld */
+#define FIRMWARE_DNLD_OK 0x0000
+
+/* Value to check once the main firmware is downloaded */
+#define SUCCESSFUL_FW_DOWNLOAD_MAGIC 0x88888888
+
+/***************** SPI Interface Unit *****************/
+/* Masks used in SPI register read/write operations */
+#define IF_SPI_READ_OPERATION_MASK 0x0
+#define IF_SPI_WRITE_OPERATION_MASK 0x8000
+
+/* SPI register offsets. 4-byte aligned. */
+#define IF_SPI_DEVICEID_CTRL_REG 0x00 /* DeviceID controller reg */
+#define IF_SPI_IO_READBASE_REG 0x04 /* Read I/O base reg */
+#define IF_SPI_IO_WRITEBASE_REG 0x08 /* Write I/O base reg */
+#define IF_SPI_IO_RDWRPORT_REG 0x0C /* Read/Write I/O port reg */
+
+#define IF_SPI_CMD_READBASE_REG 0x10 /* Read command base reg */
+#define IF_SPI_CMD_WRITEBASE_REG 0x14 /* Write command base reg */
+#define IF_SPI_CMD_RDWRPORT_REG 0x18 /* Read/Write command port reg */
+
+#define IF_SPI_DATA_READBASE_REG 0x1C /* Read data base reg */
+#define IF_SPI_DATA_WRITEBASE_REG 0x20 /* Write data base reg */
+#define IF_SPI_DATA_RDWRPORT_REG 0x24 /* Read/Write data port reg */
+
+#define IF_SPI_SCRATCH_1_REG 0x28 /* Scratch reg 1 */
+#define IF_SPI_SCRATCH_2_REG 0x2C /* Scratch reg 2 */
+#define IF_SPI_SCRATCH_3_REG 0x30 /* Scratch reg 3 */
+#define IF_SPI_SCRATCH_4_REG 0x34 /* Scratch reg 4 */
+
+#define IF_SPI_TX_FRAME_SEQ_NUM_REG 0x38 /* Tx frame sequence number reg */
+#define IF_SPI_TX_FRAME_STATUS_REG 0x3C /* Tx frame status reg */
+
+#define IF_SPI_HOST_INT_CTRL_REG 0x40 /* Host interrupt controller reg */
+
+#define IF_SPI_CARD_INT_CAUSE_REG 0x44 /* Card interrupt cause reg */
+#define IF_SPI_CARD_INT_STATUS_REG 0x48 /* Card interrupt status reg */
+#define IF_SPI_CARD_INT_EVENT_MASK_REG 0x4C /* Card interrupt event mask */
+#define IF_SPI_CARD_INT_STATUS_MASK_REG 0x50 /* Card interrupt status mask */
+
+#define IF_SPI_CARD_INT_RESET_SELECT_REG 0x54 /* Card interrupt reset select */
+
+#define IF_SPI_HOST_INT_CAUSE_REG 0x58 /* Host interrupt cause reg */
+#define IF_SPI_HOST_INT_STATUS_REG 0x5C /* Host interrupt status reg */
+#define IF_SPI_HOST_INT_EVENT_MASK_REG 0x60 /* Host interrupt event mask */
+#define IF_SPI_HOST_INT_STATUS_MASK_REG 0x64 /* Host interrupt status mask */
+#define IF_SPI_HOST_INT_RESET_SELECT_REG 0x68 /* Host interrupt reset select */
+
+#define IF_SPI_DELAY_READ_REG 0x6C /* Delay read reg */
+#define IF_SPI_SPU_BUS_MODE_REG 0x70 /* SPU BUS mode reg */
+
+/***************** IF_SPI_DEVICEID_CTRL_REG *****************/
+#define IF_SPI_DEVICEID_CTRL_REG_TO_CARD_ID(dc) ((dc & 0xffff0000)>>16)
+#define IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dc) (dc & 0x000000ff)
+
+/***************** IF_SPI_HOST_INT_CTRL_REG *****************/
+/* Host Interrupt Control bit : Wake up */
+#define IF_SPI_HICT_WAKE_UP (1<<0)
+/* Host Interrupt Control bit : WLAN ready */
+#define IF_SPI_HICT_WLAN_READY (1<<1)
+/*#define IF_SPI_HICT_FIFO_FIRST_HALF_EMPTY (1<<2) */
+/*#define IF_SPI_HICT_FIFO_SECOND_HALF_EMPTY (1<<3) */
+/*#define IF_SPI_HICT_IRQSRC_WLAN (1<<4) */
+/* Host Interrupt Control bit : Tx auto download */
+#define IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO (1<<5)
+/* Host Interrupt Control bit : Rx auto upload */
+#define IF_SPI_HICT_RX_UPLOAD_OVER_AUTO (1<<6)
+/* Host Interrupt Control bit : Command auto download */
+#define IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO (1<<7)
+/* Host Interrupt Control bit : Command auto upload */
+#define IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO (1<<8)
+
+/***************** IF_SPI_CARD_INT_CAUSE_REG *****************/
+/* Card Interrupt Case bit : Tx download over */
+#define IF_SPI_CIC_TX_DOWNLOAD_OVER (1<<0)
+/* Card Interrupt Case bit : Rx upload over */
+#define IF_SPI_CIC_RX_UPLOAD_OVER (1<<1)
+/* Card Interrupt Case bit : Command download over */
+#define IF_SPI_CIC_CMD_DOWNLOAD_OVER (1<<2)
+/* Card Interrupt Case bit : Host event */
+#define IF_SPI_CIC_HOST_EVENT (1<<3)
+/* Card Interrupt Case bit : Command upload over */
+#define IF_SPI_CIC_CMD_UPLOAD_OVER (1<<4)
+/* Card Interrupt Case bit : Power down */
+#define IF_SPI_CIC_POWER_DOWN (1<<5)
+
+/***************** IF_SPI_CARD_INT_STATUS_REG *****************/
+#define IF_SPI_CIS_TX_DOWNLOAD_OVER (1<<0)
+#define IF_SPI_CIS_RX_UPLOAD_OVER (1<<1)
+#define IF_SPI_CIS_CMD_DOWNLOAD_OVER (1<<2)
+#define IF_SPI_CIS_HOST_EVENT (1<<3)
+#define IF_SPI_CIS_CMD_UPLOAD_OVER (1<<4)
+#define IF_SPI_CIS_POWER_DOWN (1<<5)
+
+/***************** IF_SPI_HOST_INT_CAUSE_REG *****************/
+#define IF_SPI_HICU_TX_DOWNLOAD_RDY (1<<0)
+#define IF_SPI_HICU_RX_UPLOAD_RDY (1<<1)
+#define IF_SPI_HICU_CMD_DOWNLOAD_RDY (1<<2)
+#define IF_SPI_HICU_CARD_EVENT (1<<3)
+#define IF_SPI_HICU_CMD_UPLOAD_RDY (1<<4)
+#define IF_SPI_HICU_IO_WR_FIFO_OVERFLOW (1<<5)
+#define IF_SPI_HICU_IO_RD_FIFO_UNDERFLOW (1<<6)
+#define IF_SPI_HICU_DATA_WR_FIFO_OVERFLOW (1<<7)
+#define IF_SPI_HICU_DATA_RD_FIFO_UNDERFLOW (1<<8)
+#define IF_SPI_HICU_CMD_WR_FIFO_OVERFLOW (1<<9)
+#define IF_SPI_HICU_CMD_RD_FIFO_UNDERFLOW (1<<10)
+
+/***************** IF_SPI_HOST_INT_STATUS_REG *****************/
+/* Host Interrupt Status bit : Tx download ready */
+#define IF_SPI_HIST_TX_DOWNLOAD_RDY (1<<0)
+/* Host Interrupt Status bit : Rx upload ready */
+#define IF_SPI_HIST_RX_UPLOAD_RDY (1<<1)
+/* Host Interrupt Status bit : Command download ready */
+#define IF_SPI_HIST_CMD_DOWNLOAD_RDY (1<<2)
+/* Host Interrupt Status bit : Card event */
+#define IF_SPI_HIST_CARD_EVENT (1<<3)
+/* Host Interrupt Status bit : Command upload ready */
+#define IF_SPI_HIST_CMD_UPLOAD_RDY (1<<4)
+/* Host Interrupt Status bit : I/O write FIFO overflow */
+#define IF_SPI_HIST_IO_WR_FIFO_OVERFLOW (1<<5)
+/* Host Interrupt Status bit : I/O read FIFO underflow */
+#define IF_SPI_HIST_IO_RD_FIFO_UNDRFLOW (1<<6)
+/* Host Interrupt Status bit : Data write FIFO overflow */
+#define IF_SPI_HIST_DATA_WR_FIFO_OVERFLOW (1<<7)
+/* Host Interrupt Status bit : Data read FIFO underflow */
+#define IF_SPI_HIST_DATA_RD_FIFO_UNDERFLOW (1<<8)
+/* Host Interrupt Status bit : Command write FIFO overflow */
+#define IF_SPI_HIST_CMD_WR_FIFO_OVERFLOW (1<<9)
+/* Host Interrupt Status bit : Command read FIFO underflow */
+#define IF_SPI_HIST_CMD_RD_FIFO_UNDERFLOW (1<<10)
+
+/***************** IF_SPI_HOST_INT_STATUS_MASK_REG *****************/
+/* Host Interrupt Status Mask bit : Tx download ready */
+#define IF_SPI_HISM_TX_DOWNLOAD_RDY (1<<0)
+/* Host Interrupt Status Mask bit : Rx upload ready */
+#define IF_SPI_HISM_RX_UPLOAD_RDY (1<<1)
+/* Host Interrupt Status Mask bit : Command download ready */
+#define IF_SPI_HISM_CMD_DOWNLOAD_RDY (1<<2)
+/* Host Interrupt Status Mask bit : Card event */
+#define IF_SPI_HISM_CARDEVENT (1<<3)
+/* Host Interrupt Status Mask bit : Command upload ready */
+#define IF_SPI_HISM_CMD_UPLOAD_RDY (1<<4)
+/* Host Interrupt Status Mask bit : I/O write FIFO overflow */
+#define IF_SPI_HISM_IO_WR_FIFO_OVERFLOW (1<<5)
+/* Host Interrupt Status Mask bit : I/O read FIFO underflow */
+#define IF_SPI_HISM_IO_RD_FIFO_UNDERFLOW (1<<6)
+/* Host Interrupt Status Mask bit : Data write FIFO overflow */
+#define IF_SPI_HISM_DATA_WR_FIFO_OVERFLOW (1<<7)
+/* Host Interrupt Status Mask bit : Data write FIFO underflow */
+#define IF_SPI_HISM_DATA_RD_FIFO_UNDERFLOW (1<<8)
+/* Host Interrupt Status Mask bit : Command write FIFO overflow */
+#define IF_SPI_HISM_CMD_WR_FIFO_OVERFLOW (1<<9)
+/* Host Interrupt Status Mask bit : Command write FIFO underflow */
+#define IF_SPI_HISM_CMD_RD_FIFO_UNDERFLOW (1<<10)
+
+/***************** IF_SPI_SPU_BUS_MODE_REG *****************/
+/* SCK edge on which the WLAN module outputs data on MISO */
+#define IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_FALLING 0x8
+#define IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING 0x0
+
+/* In a SPU read operation, there is a delay between writing the SPU
+ * register name and getting back data from the WLAN module.
+ * This can be specified in terms of nanoseconds or in terms of dummy
+ * clock cycles which the master must output before receiving a response. */
+#define IF_SPI_BUS_MODE_DELAY_METHOD_DUMMY_CLOCK 0x4
+#define IF_SPI_BUS_MODE_DELAY_METHOD_TIMED 0x0
+
+/* Some different modes of SPI operation */
+#define IF_SPI_BUS_MODE_8_BIT_ADDRESS_16_BIT_DATA 0x00
+#define IF_SPI_BUS_MODE_8_BIT_ADDRESS_32_BIT_DATA 0x01
+#define IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA 0x02
+#define IF_SPI_BUS_MODE_16_BIT_ADDRESS_32_BIT_DATA 0x03
+
+#endif
diff --git a/firmware/drivers/libertas/if_spi_drv.h b/firmware/drivers/libertas/if_spi_drv.h
new file mode 100644
index 0000000000..6c8b5c3d92
--- /dev/null
+++ b/firmware/drivers/libertas/if_spi_drv.h
@@ -0,0 +1,34 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 by Tomasz Moń
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef LIBERTAS_IF_SPI_DRV
+#define LIBERTAS_IF_SPI_DRV
+
+#include <stdint.h>
+
+void libertas_spi_init(void);
+void libertas_spi_reset(int high);
+void libertas_spi_pd(int high);
+void libertas_spi_cs(int high);
+void libertas_spi_tx(const uint8_t *buf, int len);
+void libertas_spi_rx(uint8_t *buf, int len);
+
+#endif