diff options
author | Tomasz Moń <desowin@gmail.com> | 2021-07-02 12:02:26 +0200 |
---|---|---|
committer | Tomasz Moń <desowin@gmail.com> | 2021-07-04 07:41:44 +0000 |
commit | e11fa5f74eb49b10ebefbe8882bb50186dffb2a4 (patch) | |
tree | 295a28ca58bc4d7b5153629046c1e2894db02f5e /firmware/drivers | |
parent | c9f2308a1d4401ceefaac47b1ea851530d07e47d (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.Marvell | 22 | ||||
-rw-r--r-- | firmware/drivers/libertas/firmware/gspi8686_v9.bin | bin | 0 -> 126652 bytes | |||
-rw-r--r-- | firmware/drivers/libertas/firmware/gspi8686_v9_helper.bin | bin | 0 -> 2140 bytes | |||
-rw-r--r-- | firmware/drivers/libertas/if_spi.c | 674 | ||||
-rw-r--r-- | firmware/drivers/libertas/if_spi.h | 215 | ||||
-rw-r--r-- | firmware/drivers/libertas/if_spi_drv.h | 34 |
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 Binary files differnew file mode 100644 index 0000000000..58dc03d30e --- /dev/null +++ b/firmware/drivers/libertas/firmware/gspi8686_v9.bin diff --git a/firmware/drivers/libertas/firmware/gspi8686_v9_helper.bin b/firmware/drivers/libertas/firmware/gspi8686_v9_helper.bin Binary files differnew file mode 100644 index 0000000000..424cfb386c --- /dev/null +++ b/firmware/drivers/libertas/firmware/gspi8686_v9_helper.bin 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 |