eepemu_24cxx.c 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. /*
  2. * PiLC HAT firmware
  3. * 24Cxx EEPROM emulation
  4. *
  5. * Copyright (c) 2016 Michael Buesch <m@bues.ch>
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along
  18. * with this program; if not, write to the Free Software Foundation, Inc.,
  19. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  20. */
  21. #include "eepemu_24cxx.h"
  22. #include "i2c_slave.h"
  23. #include <string.h>
  24. #include <avr/eeprom.h>
  25. /* Default to: emulated eeprom size = avr eeprom size. */
  26. #ifndef EE24CXX_ADDR_MASK
  27. # define EE24CXX_ADDR_MASK ((uint16_t)E2END)
  28. #endif
  29. /* Default to: emulated page size = 32 bytes. */
  30. #ifndef EE24CXX_PAGE_MASK
  31. # define EE24CXX_PAGE_MASK ((uint16_t)(32 - 1))
  32. #endif
  33. enum ee24cxx_state {
  34. EE24CXX_IDLE,
  35. EE24CXX_WRADDRLO,
  36. EE24CXX_ADDRCOMPLETE,
  37. };
  38. struct ee24cxx_context {
  39. enum ee24cxx_state state;
  40. uint16_t word_addr;
  41. bool write_en;
  42. };
  43. static struct ee24cxx_context ee24cxx;
  44. static uint8_t EEMEM ee24cxx_eeprom_mem[EE24CXX_ADDR_MASK + 1];
  45. void ee24cxx_set_we(bool write_enable)
  46. {
  47. struct ee24cxx_context *ee = &ee24cxx;
  48. ee->write_en = write_enable;
  49. }
  50. bool ee24cxx_get_we(void)
  51. {
  52. struct ee24cxx_context *ee = &ee24cxx;
  53. return ee->write_en;
  54. }
  55. static uint8_t ee24cxx_transmit(bool start)
  56. {
  57. struct ee24cxx_context *ee = &ee24cxx;
  58. uint8_t ret = 0;
  59. switch (ee->state) {
  60. case EE24CXX_IDLE:
  61. /* current-address-read. */
  62. ret = ee->word_addr & 0xFF;
  63. break;
  64. case EE24CXX_ADDRCOMPLETE:
  65. /* data read. */
  66. eeprom_busy_wait();
  67. ret = eeprom_read_byte(&ee24cxx_eeprom_mem[ee->word_addr]);
  68. ee->word_addr = (ee->word_addr + 1) & EE24CXX_ADDR_MASK;
  69. break;
  70. case EE24CXX_WRADDRLO:
  71. /* error: address not complete. */
  72. break;
  73. }
  74. return ret;
  75. }
  76. static void ee24cxx_receive(bool start, uint8_t data)
  77. {
  78. struct ee24cxx_context *ee = &ee24cxx;
  79. if (start)
  80. ee->state = EE24CXX_IDLE;
  81. switch (ee->state) {
  82. case EE24CXX_IDLE:
  83. /* address high byte write. */
  84. ee->word_addr = (ee->word_addr & 0x00FF) |
  85. ((uint16_t)data << 8);
  86. ee->word_addr &= EE24CXX_ADDR_MASK;
  87. ee->state = EE24CXX_WRADDRLO;
  88. break;
  89. case EE24CXX_WRADDRLO:
  90. /* address low byte write. */
  91. ee->word_addr = (ee->word_addr & 0xFF00) |
  92. ((uint16_t)data);
  93. ee->word_addr &= EE24CXX_ADDR_MASK;
  94. ee->state = EE24CXX_ADDRCOMPLETE;
  95. break;
  96. case EE24CXX_ADDRCOMPLETE:
  97. /* data write. */
  98. if (ee->write_en) {
  99. eeprom_busy_wait();
  100. eeprom_write_byte(&ee24cxx_eeprom_mem[ee->word_addr],
  101. data);
  102. }
  103. ee->word_addr = (ee->word_addr & ~EE24CXX_PAGE_MASK) |
  104. ((ee->word_addr + 1) & EE24CXX_PAGE_MASK);
  105. break;
  106. }
  107. }
  108. static const struct i2c_slave_ops __flash ee24cxx_i2c_slave_ops = {
  109. .transmit = ee24cxx_transmit,
  110. .receive = ee24cxx_receive,
  111. };
  112. void ee24cxx_init(void)
  113. {
  114. memset(&ee24cxx, 0, sizeof(ee24cxx));
  115. ee24cxx.state = EE24CXX_IDLE;
  116. i2cs_add_slave(EEPEMU_24CXX_ADDR, &ee24cxx_i2c_slave_ops);
  117. }