From e11fa5f74eb49b10ebefbe8882bb50186dffb2a4 Mon Sep 17 00:00:00 2001 From: Tomasz Moń Date: Fri, 2 Jul 2021 12:02:26 +0200 Subject: 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 --- apps/main.c | 5 + firmware/SOURCES | 10 + firmware/drivers/libertas/firmware/LICENCE.Marvell | 22 + firmware/drivers/libertas/firmware/gspi8686_v9.bin | Bin 0 -> 126652 bytes .../libertas/firmware/gspi8686_v9_helper.bin | Bin 0 -> 2140 bytes firmware/drivers/libertas/if_spi.c | 674 +++++++++++++++++++++ firmware/drivers/libertas/if_spi.h | 215 +++++++ firmware/drivers/libertas/if_spi_drv.h | 34 ++ firmware/export/config/sansaconnect.h | 6 + firmware/export/wifi.h | 29 + .../tms320dm320/sansa-connect/avr-sansaconnect.c | 10 +- .../tms320dm320/sansa-connect/avr-sansaconnect.h | 2 + .../tms320dm320/sansa-connect/wifi-sansaconnect.c | 130 ++++ firmware/target/arm/tms320dm320/system-dm320.c | 2 + tools/buildzip.pl | 5 + 15 files changed, 1142 insertions(+), 2 deletions(-) create mode 100644 firmware/drivers/libertas/firmware/LICENCE.Marvell create mode 100644 firmware/drivers/libertas/firmware/gspi8686_v9.bin create mode 100644 firmware/drivers/libertas/firmware/gspi8686_v9_helper.bin create mode 100644 firmware/drivers/libertas/if_spi.c create mode 100644 firmware/drivers/libertas/if_spi.h create mode 100644 firmware/drivers/libertas/if_spi_drv.h create mode 100644 firmware/export/wifi.h create mode 100644 firmware/target/arm/tms320dm320/sansa-connect/wifi-sansaconnect.c diff --git a/apps/main.c b/apps/main.c index c7b223faea..483d280204 100644 --- a/apps/main.c +++ b/apps/main.c @@ -36,6 +36,7 @@ #include "panic.h" #include "menu.h" #include "usb.h" +#include "wifi.h" #include "powermgmt.h" #if !defined(DX50) && !defined(DX90) #include "adc.h" @@ -636,6 +637,10 @@ static void init(void) CHART(" ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 by Tomasz Moń + * Ported from Linux libertas driver + * Copyright 2008 Analog Devices Inc. + * Authors: + * Andrey Yurovsky + * Colin McCabe + * 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 +#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 + * Colin McCabe + * + * 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 + +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 diff --git a/firmware/export/config/sansaconnect.h b/firmware/export/config/sansaconnect.h index 5fc91a2794..874b198316 100644 --- a/firmware/export/config/sansaconnect.h +++ b/firmware/export/config/sansaconnect.h @@ -135,6 +135,12 @@ /* Define this if you have a software controlled poweroff */ #define HAVE_SW_POWEROFF +#ifndef BOOTLOADER +#define HAVE_WIFI +/* define this if the target has Marvell 88W8686 interfaced over SPI */ +#define HAVE_W8686_SPI +#endif + /* The number of bytes reserved for loadable codecs */ #define CODEC_SIZE 0x100000 diff --git a/firmware/export/wifi.h b/firmware/export/wifi.h new file mode 100644 index 0000000000..a70e062cb0 --- /dev/null +++ b/firmware/export/wifi.h @@ -0,0 +1,29 @@ +/*************************************************************************** + * __________ __ ___. + * 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 WIFI_H +#define WIFI_H + +#include "config.h" + +void wifi_init(void) INIT_ATTR; + +#endif diff --git a/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.c b/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.c index 12058eba03..a4efe0962a 100644 --- a/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.c +++ b/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.c @@ -75,7 +75,7 @@ #define CMD_WHEEL_EN 0xD0 #define CMD_SET_INTCHRG 0xD1 #define CMD_GET_INTCHRG 0xD2 -#define CMD_UNKNOWN_D3 0xD3 +#define CMD_WIFI_PD 0xD3 #define CMD_UNKNOWN_D4 0xD4 #define CMD_UNKNOWN_D5 0xD5 #define CMD_UNKNOWN_D6 0xD6 @@ -315,7 +315,7 @@ static size_t avr_command_data_size(uint8_t opcode) case CMD_WHEEL_EN: return 1; case CMD_SET_INTCHRG: return 1; case CMD_GET_INTCHRG: return 1; - case CMD_UNKNOWN_D3: return 1; + case CMD_WIFI_PD: return 1; case CMD_UNKNOWN_D4: return 1; case CMD_UNKNOWN_D5: return 2; case CMD_UNKNOWN_D6: return 2; @@ -536,6 +536,12 @@ void avr_hid_enable_charger(void) avr_execute_command(CMD_SET_INTCHRG, &enable, sizeof(enable)); } +void avr_hid_wifi_pd(int high) +{ + uint8_t state = high ? 0x01 : 0x00; + avr_execute_command(CMD_WIFI_PD, &state, sizeof(state)); +} + static void avr_hid_lcm_power(uint8_t parameter) { avr_execute_command(CMD_LCM_POWER, ¶meter, sizeof(parameter)); diff --git a/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.h b/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.h index 64b44675f7..baa7083760 100644 --- a/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.h +++ b/firmware/target/arm/tms320dm320/sansa-connect/avr-sansaconnect.h @@ -28,6 +28,8 @@ void avr_hid_init(void); void avr_hid_enable_charger(void); +void avr_hid_wifi_pd(int high); + void avr_hid_lcm_sleep(void); void avr_hid_lcm_wake(void); void avr_hid_lcm_power_on(void); diff --git a/firmware/target/arm/tms320dm320/sansa-connect/wifi-sansaconnect.c b/firmware/target/arm/tms320dm320/sansa-connect/wifi-sansaconnect.c new file mode 100644 index 0000000000..867b1da477 --- /dev/null +++ b/firmware/target/arm/tms320dm320/sansa-connect/wifi-sansaconnect.c @@ -0,0 +1,130 @@ +/*************************************************************************** +* __________ __ ___. +* 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. +* +****************************************************************************/ + +#include "kernel.h" +#include "system.h" +#include "spi.h" +#include "avr-sansaconnect.h" +#include "libertas/if_spi_drv.h" + +#define IO_SERIAL0_XMIT (0x100) + +void libertas_spi_init(void) +{ + IO_GIO_DIR0 &= ~((1 << 4) /* CS */ | (1 << 3) /* reset */); + libertas_spi_reset(1); + libertas_spi_cs(1); + + /* Enable the clock */ + bitset16(&IO_CLK_MOD2, CLK_MOD2_SIF0); + + /* Disable transmitter */ + IO_SERIAL0_TX_ENABLE = 0x0001; + + /* SELSDEN = 0, SLVEN = 0, SIOCLR = 0, SCLKM = 1, MSB = 1, MSSEL = 0, + * RATE = 2 -> 15MHz + */ + IO_SERIAL0_MODE = 0x0601; + + /* Disable the clock */ + bitclr16(&IO_CLK_MOD2, CLK_MOD2_SIF0); + + /* Make sure the SPI clock is not inverted */ + bitclr16(&IO_CLK_INV, CLK_INV_SIF0); +} + +void libertas_spi_reset(int high) +{ + if (high) + { + IO_GIO_BITSET0 = (1 << 3); + } + else + { + IO_GIO_BITCLR0 = (1 << 3); + } +} + +void libertas_spi_pd(int high) +{ + avr_hid_wifi_pd(high); +} + +void libertas_spi_cs(int high) +{ + if (high) + { + IO_GIO_BITSET0 = (1 << 4); + } + else + { + IO_GIO_BITCLR0 = (1 << 4); + } +} + +void libertas_spi_tx(const uint8_t *buf, int len) +{ + /* Enable the clock */ + bitset16(&IO_CLK_MOD2, CLK_MOD2_SIF0); + IO_SERIAL0_TX_ENABLE = 0x0001; + + while (len > 0) + { + IO_SERIAL0_TX_DATA = *(buf + 1); + while (IO_SERIAL0_RX_DATA & IO_SERIAL0_XMIT) {}; + IO_SERIAL0_TX_DATA = *buf; + while (IO_SERIAL0_RX_DATA & IO_SERIAL0_XMIT) {}; + + buf += 2; + len -= 2; + } + + IO_SERIAL0_TX_ENABLE = 0x0000; + + /* Disable the clock */ + bitclr16(&IO_CLK_MOD2, CLK_MOD2_SIF0); +} + +void libertas_spi_rx(uint8_t *buf, int len) +{ + /* Enable the clock */ + bitset16(&IO_CLK_MOD2, CLK_MOD2_SIF0); + IO_SERIAL0_TX_ENABLE = 0x0001; + + while (len > 0) + { + uint16_t data; + IO_SERIAL0_TX_DATA = 0; + while ((data = IO_SERIAL0_RX_DATA) & IO_SERIAL0_XMIT) {}; + *(buf + 1) = data & 0xFF; + IO_SERIAL0_TX_DATA = 0; + while ((data = IO_SERIAL0_RX_DATA) & IO_SERIAL0_XMIT) {}; + *buf = data & 0xFF; + + buf += 2; + len -= 2; + } + + IO_SERIAL0_TX_ENABLE = 0x0000; + + /* Disable the clock */ + bitclr16(&IO_CLK_MOD2, CLK_MOD2_SIF0); +} diff --git a/firmware/target/arm/tms320dm320/system-dm320.c b/firmware/target/arm/tms320dm320/system-dm320.c index 935f3609a6..c80082afd8 100644 --- a/firmware/target/arm/tms320dm320/system-dm320.c +++ b/firmware/target/arm/tms320dm320/system-dm320.c @@ -369,9 +369,11 @@ void system_init(void) #endif #ifdef SANSA_CONNECT +#ifndef HAVE_WIFI /* keep WIFI CS and reset high to save power */ IO_GIO_DIR0 &= ~((1 << 4) /* CS */ | (1 << 3) /* reset */); IO_GIO_BITSET0 = (1 << 4) | (1 << 3); +#endif i2c_init(); avr_hid_init(); diff --git a/tools/buildzip.pl b/tools/buildzip.pl index 4e9ba79eeb..43c304ff12 100755 --- a/tools/buildzip.pl +++ b/tools/buildzip.pl @@ -387,6 +387,11 @@ sub buildzip { open(NOMEDIA, ">$temp_dir/.nomedia") || die "can't open .nomedia"; close(NOMEDIA); } + # copy wifi firmware + if ($modelname =~ /sansaconnect/) { + glob_mkdir("$temp_dir/libertas"); + glob_copy("$ROOT/firmware/drivers/libertas/firmware/*", "$temp_dir/libertas/"); + } glob_mkdir("$temp_dir/langs"); glob_mkdir("$temp_dir/rocks"); -- cgit v1.2.3