blob: d1d5c0427135334db8cfcceb77edd572167c8da0 (
plain) (
tree)
|
|
/* Copyright (c) 2016, NORDUnet A/S. */
/* See LICENSE.txt for licensing information. */
#include <inttypes.h>
#include <avr/io.h> /* -D__AVR_ATtiny828__ will include <avr/iotn828.h> */
/* Mapping of pins to names. */
#define MKM_AVR_CS_N PORTA5
#define MKM_CONTROL_AVR_ENA PORTA6
#define MKM_CONTROL_FPGA_DIS PORTA7
#define MKM_AVR_MOSI PORTD0
#define MKM_AVR_MISO PORTD1
#define MKM_AVR_SCK PORTD3
/* Input pins. */
#define AVR_PANIC_PIN PINA /* Panic button. */
#define AVR_PANIC_BIT PINA4 /* 0 = panic. */
/* Output ports. */
#define AVR_LED_PORT PORTA
#define AVR_LED_BLUE_BIT PORTA0
#define AVR_LED_RED_BIT PORTA3
#define AVR_LED_YELLOW_BIT PORTA2
#define AVR_LED_GREEN_BIT PORTA1
#define MKM_CS_PORT PORTA /* MKM chip select. */
#define MKM_CS_BIT MKM_AVR_CS_N /* 0 => selected. */
#define MKM_CONTROL_AVR_PORT PORTA /* AVR in control. */
#define MKM_CONTROL_AVR_BIT MKM_CONTROL_AVR_ENA /* 0 => in control. */
#define MKM_CONTROL_FPGA_PORT PORTA /* FPGA in control. */
#define MKM_CONTROL_FPGA_BIT MKM_CONTROL_FPGA_DIS /* 0 => in control. */
/* SPI. */
#define MKM_AVR_MOSI_PORT PORTD
#define MKM_AVR_MOSI_BIT MKM_AVR_MOSI /* PD0, Master Out Slave In. */
#define MKM_AVR_MISO_PORT PORTD
#define MKM_AVR_MISO_BIT PORTD1 /* PD1, Master In Slave Out. */
#define MKM_AVR_SCK_PORT PORTD
#define MKM_AVR_SCK_BIT PORTD3 /* PD3, SPI clock. */
/*******/
/* MKM */
static inline void
mkm_chip_select(int select_flag)
{
if (select_flag)
MKM_CS_PORT &= ~_BV(MKM_CS_BIT); /* CS low. */
else
MKM_CS_PORT |= _BV(MKM_CS_BIT); /* CS high. */
}
static inline void
mkm_grab()
{
MKM_CONTROL_FPGA_PORT |= _BV(MKM_CONTROL_FPGA_BIT);
MKM_CONTROL_AVR_PORT &= ~_BV(MKM_CONTROL_AVR_BIT);
}
static inline void
mkm_release()
{
MKM_CONTROL_AVR_PORT |= _BV(MKM_CONTROL_AVR_BIT);
MKM_CONTROL_FPGA_PORT &= ~_BV(MKM_CONTROL_FPGA_BIT);
}
/*******/
/* SPI */
#define SPI_SS DDRC0 /* SPI slave select. */
static inline void
spi_setup(int on_flag)
{
if (on_flag)
{
/* Disable SPI power reduction. */
PRR &= ~_BV(PRSPI);
/* Configure MOSI and SCK pins as output. */
DDRD = _BV(MKM_AVR_MOSI_BIT) | _BV(MKM_AVR_SCK_BIT);
/* Make sure SPI slave select (SS) is configured as output
before enabling SPI master mode! */
DDRC |= _BV(SPI_SS);
/* Enable SPI in master mode, clock rate f/16. */
SPCR = _BV(SPE) | _BV(MSTR) | _BV(SPR0);
}
else
{
SPCR &= ~_BV(SPE); /* Disable SPI. */
PRR |= _BV(PRSPI); /* Enable SPI power reduction. */
}
}
/* SPI commands for MKM (23K640). */
#define SPI_READ 0x03
#define SPI_WRITE 0x02
#define SPI_RDSR 0x05 /* Read status register. */
#define SPI_WRSR 0x01 /* Write status register. */
static inline void
spi_write(uint8_t val)
{
/* Move the value to be sent to the SPI slave into the SPI
register. This starts the SPI clock. */
SPDR = val;
/* Wait for the byte to be shifted into the slave. */
loop_until_bit_is_set(SPSR, SPIF);
}
static inline uint8_t
spi_read()
{
/* Start clocking the SPI slave by moving a dummy byte (0) into the
SPI register and wait for the byte from the slave to be shifted
in. */
spi_write(0);
/* Read the SPI register and return the value. */
return SPDR;
}
static inline uint8_t
spi_read_status()
{
spi_write(SPI_RDSR);
return SPDR;
}
#define SPI_OPERATION_BYTE 0x00
#define SPI_OPERATION_SEQUENCE 0x40
#define SPI_OPERATION_PAGE 0x80
static inline void
spi_set_operation(uint8_t mode)
{
spi_write(SPI_WRSR);
spi_write(mode);
}
#if 0
static void
spi_write_byte(uint16_t addr, uint8_t data)
{
mkm_chip_select(1);
spi_write(SPI_WRITE);
spi_write(addr & 0xff00);
spi_write(addr & 0x00ff);
spi_write(data);
mkm_chip_select(0);
}
static uint8_t
spi_read_byte(uint16_t addr)
{
mkm_chip_select(1);
spi_write(SPI_READ);
spi_write(addr & 0xff00);
spi_write(addr & 0x00ff);
uint8_t data = spi_read();
mkm_chip_select(0);
return data;
}
#endif
/**********************/
/* Tamper protection. */
#if 0
/* Interrupt handler for tamper pin. */
void
tamper_int(void) __attribute__ ((interrupt))
{
}
#endif
static inline int
panic_p()
{
return !bit_is_set(AVR_PANIC_PIN, AVR_PANIC_BIT);
}
static inline void
init_ports()
{
/* Configure all PORTA pins except the tamper detection pin as
outputs. */
DDRA = 0xff & ~_BV(AVR_PANIC_BIT);
}
static void
init_interrupts()
{
PCMSK0 |= _BV(PCINT4); /* Set mask bit for PCINT4 */
PCICR |= _BV(PCIE0); /* Enable pin change interrupt 0. */
}
static void
init_power_reduction()
{
/* TBD: Disable everything that we don't need? Note that the effect
of this should marginal since it's only saving energy when we're
awaken and actualy wiping memory. */
}
static inline void
mkm_wipe()
{
AVR_LED_PORT |= _BV(AVR_LED_RED_BIT);
spi_setup(1);
mkm_grab();
mkm_chip_select(1);
spi_set_operation(SPI_OPERATION_SEQUENCE);
spi_write(SPI_WRITE);
spi_write(0); /* Address, high byte. */
spi_write(0); /* Address, low byte. */
for (int i = 0; i < 0x1fff; i++) /* 8192 bytes (64Kbit). */
spi_write(0);
mkm_chip_select(0);
mkm_release();
spi_setup(0);
AVR_LED_PORT &= ~_BV(AVR_LED_RED_BIT);
}
static inline void
sleep()
{
SMCR &= ~0x2; /* Set sleep mode to "power down"... */
SMCR |= 0x5; /* ... and enable sleep. */
asm("sei"); /* Enable interrupts. */
asm("sleep"); /* Go to sleep. */
SMCR &= ~0x1; /* Disable sleep. */
}
int
main()
{
init_ports();
init_power_reduction();
init_interrupts();
/* Flash LED's at startup. */
AVR_LED_PORT |= 0x0f;
for (int i = 0; i < 16000; i++);
AVR_LED_PORT &= ~0x0f;
while (1)
{
if (panic_p())
mkm_wipe();
if (!panic_p())
sleep();
}
return 0;
}
|