A board support package for the LPC4088 Display Module.

Dependencies:   DM_HttpServer DM_USBHost

Dependents:   lpc4088_displaymodule_emwin lpc4088_displaymodule_demo_sphere sampleGUI sampleEmptyGUI ... more

Fork of DMSupport by EmbeddedArtists AB

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers BiosLoader.cpp Source File

BiosLoader.cpp

00001 /*
00002  *  Copyright 2014 Embedded Artists AB
00003  *
00004  *  Licensed under the Apache License, Version 2.0 (the "License");
00005  *  you may not use this file except in compliance with the License.
00006  *  You may obtain a copy of the License at
00007  *
00008  *    http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  *  Unless required by applicable law or agreed to in writing, software
00011  *  distributed under the License is distributed on an "AS IS" BASIS,
00012  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  *  See the License for the specific language governing permissions and
00014  *  limitations under the License.
00015  */
00016 
00017 #include "mbed.h"
00018 #include "us_ticker_api.h"
00019 #include "BiosLoader.h"
00020 #include "DMBoard.h"
00021 #include "BiosEEPROM.h"
00022 #include "crc.h"
00023 #include "bios.h"
00024 #include "meas.h"
00025 
00026 #if defined(DM_BOARD_BIOS_DEVELOPMENT)
00027   #ifdef __cplusplus
00028     extern "C" {
00029   #endif
00030       void bios_debug_aid(bios_header_t* header, const char** pMsg, uint32_t* paramSize);
00031   #ifdef __cplusplus
00032     }
00033   #endif
00034 #endif
00035 
00036 /******************************************************************************
00037  * Defines and typedefs
00038  *****************************************************************************/
00039  
00040 #define MOVE_POINTER(__x, __off) (   ( (uint32_t*)(__x) ) = (uint32_t*)( (uint32_t)(__x) + (__off) ) )
00041 
00042 /*
00043  * Make sure that we reserve at least this amount of RAM for future
00044  * expansion of the BIOS. This prevents the user from squeezing out
00045  * the last drop of available RAM in his application.
00046  */
00047 #define BIOS_RESERVED_CHUNK  0x1000
00048 #define BIOS_MAX_SIZE        0x100000
00049 #ifndef MAX
00050   #define MAX(__a, __b) (((__a)>(__b))?(__a):(__b))
00051 #endif
00052 
00053 /*
00054  * The BIOS is API compatible as long as the Major component of the
00055  * version is the same.
00056  */
00057 #define SUPPORTED_BIOS_VER   0x000000
00058 #define SUPPORTED_BIOS_MASK  0xff0000  // only look at the Major component
00059 #define SUPPORTED_VERSION(__ver) (((__ver)&SUPPORTED_BIOS_MASK) == SUPPORTED_BIOS_VER)
00060 
00061 //#define MAC_IN_SDK
00062 
00063 /******************************************************************************
00064  * Local variables
00065  *****************************************************************************/
00066 
00067 /******************************************************************************
00068  * Global functions
00069  *****************************************************************************/
00070 
00071 #if !defined(MAC_IN_SDK)
00072 /* The LPC4088QSB platform in the MBED SDK have defined the WEAK function
00073  * mbed_mac_address (ethernet_api.c) to read a unique MAC address from the
00074  * onboard EEPROM.
00075  * The LPC4088DM platform in the MBED SDK does not have the WEAK function
00076  * so it is implemented here instead. This version of the function will ask
00077  * the bios for a MAC address.
00078  */
00079 void mbed_mac_address(char *mac) {
00080   static char cache[6];
00081   static bool haveIt = false;
00082   if (!haveIt) {
00083     BiosLoader::instance().getMacAddress(cache);
00084     haveIt = true;
00085   }
00086   memcpy(mac, cache, 6);
00087 }
00088 #endif
00089 
00090 /******************************************************************************
00091  * Private Functions
00092  *****************************************************************************/
00093 
00094 // Called by the NVIC
00095 static void loader_i2c0_irq_handler()
00096 {
00097   BiosLoader::instance().handleI2CInterrupt();
00098 }
00099 
00100 
00101 // Function called from the BIOS
00102 static uint32_t readTimeMs()
00103 {
00104   return us_ticker_read()/1000;
00105 }
00106 
00107 
00108 BiosLoader::BiosLoader() : 
00109     _initialized(false),
00110     _biosData(NULL),
00111     _conf(NULL),
00112     _confSize(0),
00113     _stats(0)
00114 {
00115 }
00116 
00117 BiosLoader::~BiosLoader()
00118 {
00119   if (_biosData != NULL) {
00120     free(_biosData);
00121     _biosData = NULL;
00122   }
00123   if (_conf != NULL) {
00124     free(_conf);
00125     _conf = NULL;
00126     _confSize = 0;
00127   }
00128 }
00129 
00130 DMBoard::BoardError BiosLoader::readBIOS(uint8_t** data, uint32_t* size)
00131 {
00132   DMBoard::BoardError err = DMBoard::Ok;
00133   BiosEEPROM eeprom;
00134   file_header_t fh;
00135 
00136   if (_conf != NULL) {
00137     *data = _conf;
00138     *size = _confSize;
00139     return DMBoard::Ok;
00140   }
00141     
00142   do {
00143     if (!eeprom.read(0, (char*)&fh, sizeof(file_header_t))) {
00144       resetI2C();
00145       if (!eeprom.read(0, (char*)&fh, sizeof(file_header_t))) {
00146         err = DMBoard::BiosStorageError;
00147         break;
00148       }
00149     }
00150     
00151     if (fh.magic != BIOS_MAGIC) {
00152       err = DMBoard::BiosInvalidError;
00153       break;
00154     }
00155     
00156     if (!SUPPORTED_VERSION(fh.version)) {
00157       err = DMBoard::BiosVersionError;
00158       break;
00159     }
00160     
00161     if ((fh.headerSize + fh.size) > BIOS_MAX_SIZE) {
00162       err = DMBoard::BiosInvalidError;
00163       break;
00164     }
00165     
00166     _confSize = fh.headerSize + fh.size;
00167     _conf = (uint8_t*)malloc(MAX(_confSize,BIOS_RESERVED_CHUNK));
00168     if (_conf == NULL) {
00169       _confSize = 0;
00170       err = DMBoard::MemoryError;
00171       break;
00172     }
00173     
00174     if (!eeprom.read(0, (char*)_conf, _confSize)) {
00175       err = DMBoard::BiosStorageError;
00176       break;
00177     }
00178     
00179     uint32_t crc = crc_Buffer((uint32_t*)(&_conf[fh.headerSize]), fh.size/4);
00180     if (crc != fh.crc) {
00181       err = DMBoard::BiosInvalidError;
00182       break;
00183     }
00184     
00185     // Bios header has been verified and seems ok
00186     *data = _conf;
00187     *size = _confSize;
00188     _stats = fh.version;
00189     err = DMBoard::Ok;
00190   } while (false);
00191   
00192   if (err != DMBoard::Ok) {
00193     if (_conf != NULL) {
00194       free(_conf);
00195       _conf = NULL;
00196       _confSize = 0;
00197     }
00198   }
00199 
00200   return err;
00201 }
00202 
00203 DMBoard::BoardError BiosLoader::params(bios_header_t** header, void** instanceData)
00204 {
00205   if (!_initialized) {
00206     DMBoard::BoardError err = init();
00207     if (err != DMBoard::Ok) {
00208       return err;
00209     }    
00210   }
00211   if (_initialized) {
00212     *header = &_bios;
00213     *instanceData = _biosData;
00214     return DMBoard::Ok;
00215   } else {
00216     return DMBoard::BiosInvalidError;
00217   }
00218 }
00219 
00220 DMBoard::BoardError BiosLoader::init()
00221 {
00222   DMBoard::BoardError err = DMBoard::Ok;
00223   if (!_initialized) {
00224     do {
00225             
00226       // Get the display bios from the DMBoard. DMBoard will have verified it
00227       // and will keep it in RAM so there is no need to copy it.
00228       uint8_t* p = NULL;
00229       uint32_t size = 0;
00230       err = readBIOS(&p, &size);
00231       if (err != DMBoard::Ok) {
00232         break;
00233       }
00234       
00235 #if defined(MAC_IN_SDK)
00236       // The BIOS has been read so we know that the I2C bus is working. After the
00237       // BIOS is "started" it will take ownership of the bus and it can cause
00238       // problems for other peripherals on it. The only other peripheral today
00239       // is the EEPROM with the MAC address. It is read by mbed_mac_address() in
00240       // ethernet_api.c in the SDK and it will be cached. By reading it here now
00241       // we prevent future access to the I2C bus.
00242       char mac[6] = {0};
00243       mbed_mac_address(mac);
00244 #endif
00245       
00246       // Extract the function pointers so that they can be modified to match the
00247       // actual location of the code
00248       file_header_t* file_header = (file_header_t*)p;      
00249       memcpy(&_bios, &file_header->header, sizeof(bios_header_t));
00250       
00251       // Allocate memory for the BIOS instance data
00252       _biosData = malloc(file_header->paramSize);
00253       if (_biosData == NULL) {
00254         err = DMBoard::MemoryError;
00255         break;
00256       }
00257       
00258       // All offsets must be moved by two factors:
00259       // 1) The position of the code in RAM (location of "p")
00260       // 2) The header size (the code/data comes after it)
00261       uint32_t offset = ((uint32_t)p) + file_header->headerSize;
00262       uint32_t* functions = (uint32_t*)&_bios;
00263       for (uint32_t i = 0; i < (sizeof(bios_header_t)/sizeof(uint32_t)); i++) {
00264         functions[i] += offset;
00265       }
00266 
00267 #if defined(DM_BOARD_BIOS_DEVELOPMENT)
00268       // This requires that the project contains the source code for the BIOS
00269       const char* msg;
00270       uint32_t tmp = 0;
00271       bios_debug_aid(&_bios, &msg, &tmp);
00272       if (tmp > file_header->paramSize) {
00273         free(_biosData);
00274         _biosData = malloc(tmp);
00275         if (_biosData == NULL) {
00276           err = DMBoard::MemoryError;
00277           break;
00278         }
00279       }
00280       DMBoard::instance().logger()->printf("BIOS info: %s\n", msg);
00281 #endif
00282 
00283       // Prepare the BIOS instance data before calling the first function
00284       BiosError_t e = _bios.initParams(_biosData, SystemCoreClock, PeripheralClock, wait_us, readTimeMs);
00285       if (e != BiosError_Ok) {
00286         err = DMBoard::BiosInvalidError;
00287         break;
00288       }
00289 
00290       // Setup the mandatory I2C0 interrupt handler after initParams but before all other calls
00291       NVIC_DisableIRQ(I2C0_IRQn);
00292       NVIC_SetVector(I2C0_IRQn, (uint32_t)loader_i2c0_irq_handler);      
00293       NVIC_EnableIRQ(I2C0_IRQn);
00294 
00295       _initialized = true;
00296     } while(0);
00297   }
00298   return err;
00299 }
00300 
00301 void BiosLoader::resetI2C()
00302 {
00303     DMBoard::instance().logger()->printf("BiosLoader::resetI2C()\n");
00304     DigitalOut reset(P0_23);
00305     reset = 0;
00306     ThisThread::sleep_for(1);
00307     reset = 1;
00308     ThisThread::sleep_for(10);
00309 }
00310 
00311 
00312 /******************************************************************************
00313  * Public Functions
00314  *****************************************************************************/
00315 
00316 bool BiosLoader::isKnownSPIFIMemory(uint8_t mfgr, uint8_t devType, uint8_t devID, uint32_t memSize, uint32_t* eraseBlockSize)
00317 {
00318   if (!_initialized) {
00319     DMBoard::BoardError err = init();
00320     if (err != DMBoard::Ok) {
00321       return false;
00322     }    
00323   }
00324   if (_initialized) {
00325     bool known = false;
00326     BiosError_t err = _bios.spifiIsSupported(_biosData, mfgr,devType,devID,memSize,&known,eraseBlockSize);
00327     if (err == BiosError_Ok) {
00328       return known;
00329     }
00330   }
00331   return false;
00332 }
00333 
00334 void BiosLoader::getMacAddress(char mac[6])
00335 {
00336   if (!_initialized) {
00337     init();
00338   }
00339   if (_initialized) {
00340     BiosError_t err = _bios.ethernetMac(_biosData, mac);
00341     if (err == BiosError_Ok) {
00342       return;
00343     }
00344   }
00345   
00346   // We always consider the MAC address to be retrieved even though
00347   // reading is failed. If it wasn't possible to read then the default
00348   // address will be used.
00349   mac[0] = 0x00;
00350   mac[1] = 0x02;
00351   mac[2] = 0xF7;
00352   mac[3] = 0xF0;
00353   mac[4] = 0x00;
00354   mac[5] = 0x01;
00355 }
00356 
00357 void BiosLoader::handleI2CInterrupt()
00358 {
00359   _bios.i2cIRQHandler(_biosData);
00360 }
00361 
00362 void BiosLoader::getBiosStats(uint8_t& type, uint8_t& major, uint8_t& minor, uint8_t& rev)
00363 {
00364   type  = (_stats >> 24) & 0xff;
00365   major = (_stats >> 16) & 0xff;
00366   minor = (_stats >>  8) & 0xff;
00367   rev   = (_stats >>  0) & 0xff;
00368 }
00369