/* Copyright (c) 2016, NORDUnet A/S. */ /* See LICENSE.txt for licensing information. */ #include #include /* -D__AVR_ATtiny828__ will include */ /* 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); mkm_chip_select(0); mkm_chip_select(1); 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); /* Flash blue LED three times to indicate wipe is done */ for (int x = 0; x < 6; x++) { AVR_LED_PORT ^= _BV(AVR_LED_BLUE_BIT); for (int i = 0; i < 3200; i++); } } 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; mkm_release(); while (1) { if (panic_p()) mkm_wipe(); /* Sleep is not working right at the moment (the AVR seems to be reset when brought out of sleep mode by a press on the panic button). if (!panic_p()) sleep(); */ } return 0; }