/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. See the file COPYING * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * Copyright (C) 2010 Frank van Maarseveen */ #include #include #include #include #include "common.h" #include "bits.h" #include "types.h" #include "eeprom.h" #define PTR_BITS 10 struct write_request { uint16_t len_ptr; /* 16 - PTR_BITS used for storing len */ }; /* * There can be 2 pending requests: 'slot' and the len/src/dst tuple. When the * latter has completed (len==0) then the tuple is re-initialized from 'slot' and * the corresponding ee_write_request[] is cleared. 'slot' will be incremented * after len has become zero again to simplify code outside the ISR. */ static struct { uint8_t slot; /* code assumes atomic stores */ uint8_t len; uint8_t * src; uint16_t dst; } pending; static uint8_t ee_version; static uint8_t ee_newversion; static uint8_t ee_reset_slot_end; static struct write_request ee_write_request[EE_SLOT_END - EE_SLOT_BASE]; static uint8_t ee_suspend; static inline uint16_t ee_addr(uint8_t slot) { return (uint16_t)slot << 1; } static inline struct write_request *ee_wrq(uint8_t slot) { return ee_write_request + (uint8_t)(slot - EE_SLOT_BASE); } __attribute__((noinline)) static void _ee_read(uint8_t slot, void *buf, uint8_t len) { uint16_t eear; uint8_t *dst; eear = ee_addr(slot); dst = buf; while (len) { EEAR = eear; BIT_SET(EECR, EERE); *dst++ = EEDR; ++eear; --len; } } void ee_init(uint8_t version, uint8_t reset_slot_end) { ee_newversion = version; ee_reset_slot_end = reset_slot_end; EECR = 0; /* atomic byte programming */ pending.slot = EE_SLOT_END; _ee_read(EE_SLOT_INTERNAL, &ee_version, sizeof (ee_version)); } void ee_read(uint8_t slot, void *buf, uint8_t len) { uint8_t sreg; saveicli(sreg); ++ee_suspend; restorei(sreg); while (BIT_TST(EECR, EEPE)) ; _ee_read(slot, buf, len); saveicli(sreg); if (!--ee_suspend && pending.slot < EE_SLOT_END) BIT_SET(EECR, EERIE); restorei(sreg); } __attribute__((noinline)) void ee_write_async(uint8_t slot, void *buf, uint8_t len) { struct write_request *wrq; uint8_t sreg; wrq = ee_wrq(slot); saveicli(sreg); wrq->len_ptr = (uint16_t)buf | ((uint16_t)len << PTR_BITS); restorei(sreg); } void ee_write_generic(uint8_t slot, void *buf, uint8_t len) { uint8_t sreg; saveicli(sreg); while (pending.len) { restorei(sreg); saveicli(sreg); } pending.len = len; pending.src = buf; pending.dst = ee_addr(slot); ee_sync(); restorei(sreg); } void ee_init16(uint8_t slot, uint16_t *ptr, uint16_t value) { if (ee_version != ee_newversion || slot < ee_reset_slot_end) { *ptr = value; ee_write_async(slot, ptr, 2); } else { _ee_read(slot, ptr, 2); } } void ee_min16(uint8_t slot, uint16_t *ptr, uint16_t value) { if (value < *ptr) { *ptr = value; ee_write_async(slot, ptr, 2); } } void ee_max16(uint8_t slot, uint16_t *ptr, uint16_t value) { if (value > *ptr) { *ptr = value; ee_write_async(slot, ptr, 2); } } void ee_set16(uint8_t slot, uint16_t *ptr, uint16_t value) { *ptr = value; ee_write_async(slot, ptr, 2); } void ee_init32(uint8_t slot, uint32_t *ptr, uint32_t value) { if (ee_version != ee_newversion || slot < ee_reset_slot_end) { *ptr = value; ee_write_async(slot, ptr, 4); } else { _ee_read(slot, ptr, 4); } } void ee_min32(uint8_t slot, uint32_t *ptr, uint32_t value) { if (value < *ptr) { *ptr = value; ee_write_async(slot, ptr, 4); } } void ee_max32(uint8_t slot, uint32_t *ptr, uint32_t value) { if (value > *ptr) { *ptr = value; ee_write_async(slot, ptr, 4); } } void ee_set32(uint8_t slot, uint32_t *ptr, uint32_t value) { *ptr = value; ee_write_async(slot, ptr, 4); } __attribute__((noinline)) void ee_sync(void) { pending.slot = EE_SLOT_BASE; if (!ee_suspend) BIT_SET(EECR, EERIE); } bool_t ee_busy(void) { return pending.slot < EE_SLOT_END; } __attribute__((noinline)) void ee_wait(void) { while (ee_busy()) barrier(); } void ee_cleanup(void) { ee_wait(); } /* * 0x110 (code size) / 2 (bytes per instruction) / 4 (MHz) = 34 us. But of * course there are loops and conditionals involved so actual timing can be * different. */ ISR(EE_RDY_vect) { struct write_request *wrq; uint8_t data; if (ee_suspend) goto out_idle; while (pending.slot < EE_SLOT_END) { while (pending.len) { --pending.len; data = *pending.src++; EEAR = pending.dst++; BIT_SET(EECR, EERE); if (EEDR != data) { EEDR = data; goto out_write; } } wrq = ee_wrq(pending.slot); if (wrq->len_ptr) { pending.len = wrq->len_ptr >> PTR_BITS; pending.src = (uint8_t *)(wrq->len_ptr & ((1 << PTR_BITS) - 1)); pending.dst = ee_addr(pending.slot); wrq->len_ptr = 0; continue; } ++pending.slot; /* pending.len is zero */ } if (ee_version != ee_newversion) { pending.slot = EE_SLOT_END - 1; /* mark ourselves as busy */ ee_version = ee_newversion; EEDR = ee_version; EEAR = ee_addr(EE_SLOT_INTERNAL); goto out_write; } out_idle: #ifdef EEPROM_WRITE_LED CLR(EEPROM_WRITE_LED); #endif BIT_CLR(EECR, EERIE); return; out_write: #ifdef EEPROM_WRITE_LED OUTPUT(EEPROM_WRITE_LED); SET(EEPROM_WRITE_LED); #endif BIT_SET(EECR, EEMPE); BIT_SET(EECR, EEPE); }