LLVM/clang is not used for any compilation due to it not supporting the 8-bit architectures we use (MCS-51, AVR). This means we are effectively installing 250+ MiB of dependencies for a C formatting tool. Replace it with uncrustify, which uses only ~600 KiB of space and has more granular control of formatting (800+ options). Signed-off-by: Tim Crawford <tcrawford@system76.com>
172 lines
4.4 KiB
C
172 lines
4.4 KiB
C
/*
|
|
* Copyright (C) 2020 Evan Lojewski
|
|
*
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <board/flash.h>
|
|
#include <flash/entry.h>
|
|
|
|
// EC indirect flash access
|
|
volatile uint8_t __xdata __at(0x103B) ECINDAR0;
|
|
volatile uint8_t __xdata __at(0x103C) ECINDAR1;
|
|
volatile uint8_t __xdata __at(0x103D) ECINDAR2;
|
|
volatile uint8_t __xdata __at(0x103E) ECINDAR3;
|
|
volatile uint8_t __xdata __at(0x103F) ECINDDR;
|
|
|
|
#define SPI_DEVICE (0x70)
|
|
#define SPI_FOLLOW_MODE (0x0F)
|
|
#define SPI_CHIP_SELECT (0xFD)
|
|
#define SPI_CHIP_DESELECT (0xFE)
|
|
|
|
#define SPI_READ_STATUS_COMMAND (0x05)
|
|
#define SPI_READ_COMMAND (0x0B)
|
|
#define SPI_WRITE_COMMAND (0x02)
|
|
#define SPI_WRITE_ENABLE_COMMAND (0x06)
|
|
#define SPI_READ_STATUS_COMMAND (0x05)
|
|
|
|
#define SPI_ERASE_SECTOR_COMMAND (0xD7)
|
|
|
|
#define SPI_STATUS_WIP (0x01)
|
|
|
|
void flash_enter_follow_mode(void);
|
|
void flash_exit_follow_mode(void);
|
|
void flash_wait(void);
|
|
void flash_write_enable(void);
|
|
|
|
/**
|
|
* Main flash API entry point.
|
|
*
|
|
* NOTE: This *must* be the first function in this file to ensure that it is placed
|
|
* first in the resulting binary. This is required to ensure that address
|
|
* matches the address (FLASH_OFFSET) for flash_entry in wrapper.c.
|
|
* NOTE: __reentrant so that parameters and temperary vairables are placed on the
|
|
* stack, ensuring the main application __data variables are not stomped on.
|
|
* NOTE: __critical to ensure interrupts are disabled. This does mean that interrupt
|
|
* such as the timer will be block until flash acccess is complete
|
|
*/
|
|
void flash_entry(uint32_t addr, uint8_t *data, uint32_t length, uint8_t command) __reentrant __critical {
|
|
// Only allow access from 64KB to 128KB.
|
|
if ((addr < 0x10000) || (length > 0x10000) || ((addr + length) > 0x20000))
|
|
return;
|
|
|
|
if (command == FLASH_COMMAND_READ) {
|
|
while (length) {
|
|
// Fast read.
|
|
ECINDAR3 = SPI_DEVICE;
|
|
ECINDAR2 = addr >> 16;
|
|
ECINDAR1 = addr >> 8;
|
|
ECINDAR0 = addr;
|
|
|
|
*data = ECINDDR;
|
|
|
|
addr++;
|
|
data++;
|
|
length--;
|
|
}
|
|
} else if (command == FLASH_COMMAND_WRITE) {
|
|
flash_enter_follow_mode();
|
|
|
|
while (length) {
|
|
// Note, this is the slow way to do it, but it's simple and all
|
|
// bytes are written properly.
|
|
flash_write_enable();
|
|
|
|
// Select the device
|
|
ECINDAR1 = SPI_CHIP_SELECT;
|
|
|
|
// Send write command
|
|
ECINDDR = SPI_WRITE_COMMAND;
|
|
ECINDDR = addr >> 16;
|
|
ECINDDR = addr >> 8;
|
|
ECINDDR = addr;
|
|
|
|
ECINDDR = *data;
|
|
|
|
addr++;
|
|
data++;
|
|
length--;
|
|
|
|
// Deselect
|
|
ECINDAR1 = SPI_CHIP_DESELECT;
|
|
ECINDDR = 0x00;
|
|
|
|
// Wait WIP to be cleared
|
|
flash_wait();
|
|
}
|
|
|
|
flash_exit_follow_mode();
|
|
} else if (command == FLASH_COMMAND_ERASE_1K) {
|
|
flash_enter_follow_mode();
|
|
|
|
flash_write_enable();
|
|
|
|
// Select the device
|
|
ECINDAR1 = SPI_CHIP_SELECT;
|
|
|
|
// Send erase command
|
|
ECINDDR = SPI_ERASE_SECTOR_COMMAND;
|
|
ECINDDR = addr >> 16;
|
|
ECINDDR = addr >> 8;
|
|
ECINDDR = addr;
|
|
|
|
// Deselect
|
|
ECINDAR1 = SPI_CHIP_DESELECT;
|
|
ECINDDR = 0x00;
|
|
|
|
// Wait WIP to be cleared
|
|
flash_wait();
|
|
|
|
flash_exit_follow_mode();
|
|
}
|
|
}
|
|
|
|
void flash_enter_follow_mode(void) {
|
|
// Enter follow mode.
|
|
ECINDAR3 = SPI_FOLLOW_MODE | SPI_DEVICE;
|
|
ECINDAR2 = 0xFF;
|
|
ECINDAR1 = SPI_CHIP_DESELECT;
|
|
ECINDAR0 = 0x00;
|
|
ECINDDR = 0x00;
|
|
}
|
|
|
|
void flash_exit_follow_mode(void) {
|
|
// Exit follow mode
|
|
ECINDAR3 = SPI_DEVICE;
|
|
ECINDAR2 = 0;
|
|
ECINDAR1 = 0;
|
|
ECINDAR0 = 0;
|
|
}
|
|
|
|
void flash_wait(void) {
|
|
uint8_t status;
|
|
|
|
do {
|
|
// Select the device
|
|
ECINDAR1 = SPI_CHIP_SELECT;
|
|
|
|
// Send command
|
|
ECINDDR = SPI_READ_STATUS_COMMAND;
|
|
|
|
// read status
|
|
status = ECINDDR;
|
|
|
|
// Deselect
|
|
ECINDAR1 = SPI_CHIP_DESELECT;
|
|
ECINDDR = 0x00;
|
|
} while (status & SPI_STATUS_WIP);
|
|
}
|
|
|
|
void flash_write_enable(void) {
|
|
// Select the device
|
|
ECINDAR1 = SPI_CHIP_SELECT;
|
|
|
|
// Send device id command
|
|
ECINDDR = SPI_WRITE_ENABLE_COMMAND;
|
|
|
|
// Deselect
|
|
ECINDAR1 = SPI_CHIP_DESELECT;
|
|
ECINDDR = 0x00;
|
|
}
|