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
This commit is contained in:
Tomasz Moń 2021-07-02 12:02:26 +02:00
parent c9f2308a1d
commit e11fa5f74e
15 changed files with 1142 additions and 2 deletions

View File

@ -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("<audio_init");
talk_announce_voice_invalid(); /* notify user w/ voice prompt if voice file invalid */
#ifdef HAVE_WIFI
wifi_init();
#endif
/* runtime database has to be initialized after audio_init() */
cpu_boost(false);

View File

@ -536,6 +536,15 @@ target/hosted/sdl/pcm-sdl.c
#endif /* !defined(BOOTLOADER) */
/* WiFi */
#if !defined(BOOTLOADER)
#if (CONFIG_PLATFORM & PLATFORM_NATIVE)
#if defined(HAVE_W8686_SPI)
drivers/libertas/if_spi.c
#endif
#endif /* (CONFIG_PLATFORM & PLATFORM_NATIVE) */
#endif /* !defined(BOOTLOADER) */
/* CPU Specific - By class then particular chip if applicable */
#if defined(CPU_COLDFIRE)
@ -1266,6 +1275,7 @@ target/arm/tms320dm320/sansa-connect/usb-sansaconnect.c
target/arm/tms320dm320/sansa-connect/avr-sansaconnect.c
target/arm/tms320dm320/sansa-connect/backlight-sansaconnect.c
target/arm/tms320dm320/sansa-connect/pcm-sansaconnect.c
target/arm/tms320dm320/sansa-connect/wifi-sansaconnect.c
target/arm/tms320dm320/dma-dm320.c
#endif /* SANSA_CONNECT */

View File

@ -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.

Binary file not shown.

View File

@ -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);
}
}

View File

@ -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

View File

@ -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

View File

@ -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

29
firmware/export/wifi.h Normal file
View File

@ -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

View File

@ -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, &parameter, sizeof(parameter));

View File

@ -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);

View File

@ -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);
}

View File

@ -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();

View File

@ -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");