459 lines
11 KiB
C
459 lines
11 KiB
C
/*
|
|
* uflash driver for PIC32.
|
|
*
|
|
* Based on GPIO driver:
|
|
* Copyright (C) 2012 Serge Vakulenko, <serge@vak.ru>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software
|
|
* and its documentation for any purpose and without fee is hereby
|
|
* granted, provided that the above copyright notice appear in all
|
|
* copies and that both that the copyright notice and this
|
|
* permission notice and warranty disclaimer appear in supporting
|
|
* documentation, and that the name of the author not be used in
|
|
* advertising or publicity pertaining to distribution of the
|
|
* software without specific, written prior permission.
|
|
*
|
|
* The author disclaim all warranties with regard to this
|
|
* software, including all implied warranties of merchantability
|
|
* and fitness. In no event shall the author be liable for any
|
|
* special, indirect or consequential damages or any damages
|
|
* whatsoever resulting from loss of use, data or profits, whether
|
|
* in an action of contract, negligence or other tortious action,
|
|
* arising out of or in connection with the use or performance of
|
|
* this software.
|
|
*/
|
|
#include "param.h"
|
|
#include "conf.h"
|
|
#include "user.h"
|
|
#include "ioctl.h"
|
|
#include "uflash.h"
|
|
#include "systm.h"
|
|
#include "uio.h"
|
|
#include "stat.h"
|
|
|
|
#include "map.h"
|
|
#include "proc.h"
|
|
#include "buf.h"
|
|
#include "inode.h"
|
|
#include "namei.h"
|
|
#include "fs.h"
|
|
#include "mount.h"
|
|
#include "file.h"
|
|
#include "signalvar.h"
|
|
|
|
/* TODO: some sort of interlock is needed so that the contents
|
|
* of flash are not replaced while a processing that depends
|
|
* on the contents is executing.
|
|
*/
|
|
|
|
#define MINOR_UNIT 0x07 /* Minor mask: unit number */
|
|
|
|
#define FLASH_PAGE_SIZE 4096
|
|
#define FLASH_BUFFER_INT_COUNT 4
|
|
|
|
static void flashErasePage(unsigned addrPage);
|
|
static void flashWriteUint32(unsigned addrUint32, unsigned *rgu32Data, unsigned cu32Data);
|
|
|
|
// Flash operations
|
|
#define NVMOP_WORD_PGM 0x4001 // Word program operation
|
|
#define NVMOP_ROW_PGM 0x4003 // Row write
|
|
#define NVMOP_PAGE_ERASE 0x4004 // Page erase operation
|
|
|
|
#define USE_USER_FLASH_START (USER_FLASH_START-0x20000000UL)
|
|
|
|
/*
|
|
* To enable debug output, comment out the first line,
|
|
* and uncomment the second line.
|
|
*/
|
|
#define PRINTDBG(...) /*empty*/
|
|
//#define PRINTDBG printf
|
|
|
|
char targetToFlash[31] = "";
|
|
|
|
int
|
|
uflash_open (dev, flag, mode)
|
|
dev_t dev;
|
|
{
|
|
if (u.u_uid != 0)
|
|
return EPERM;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
uflash_close (dev, flag, mode)
|
|
dev_t dev;
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
uflash_read (dev, uio, flag)
|
|
dev_t dev;
|
|
struct uio *uio;
|
|
int flag;
|
|
{
|
|
register struct iovec *iov;
|
|
register u_int cnt;
|
|
char *buf = targetToFlash;
|
|
|
|
cnt = strlen(targetToFlash);
|
|
|
|
/* I/o size should be large enough. */
|
|
iov = uio->uio_iov;
|
|
if (iov->iov_len < cnt)
|
|
return EIO;
|
|
|
|
/* Read only cnt bytes. */
|
|
if (uio->uio_offset >= cnt)
|
|
return 0;
|
|
|
|
cnt -= uio->uio_offset;
|
|
|
|
|
|
/* Print port status to buffer. */
|
|
//PRINTDBG ("uflash_read -> %s", buf);
|
|
|
|
bcopy (buf + uio->uio_offset, iov->iov_base, cnt);
|
|
iov->iov_base += cnt;
|
|
iov->iov_len -= cnt;
|
|
uio->uio_resid -= cnt;
|
|
uio->uio_offset += cnt;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
uflash_write (dev, uio, flag)
|
|
dev_t dev;
|
|
struct uio *uio;
|
|
int flag;
|
|
{
|
|
register struct iovec *iov = uio->uio_iov;
|
|
register u_int cnt = 30;
|
|
char * buf = targetToFlash;
|
|
|
|
/* I/o size should be large enough. */
|
|
if (iov->iov_len > cnt)
|
|
return EIO;
|
|
|
|
if( iov->iov_len < cnt ) cnt = iov->iov_len;
|
|
|
|
cnt -= uio->uio_offset;
|
|
bcopy (iov->iov_base, buf+uio->uio_offset, cnt);
|
|
buf[uio->uio_offset+cnt]=0;
|
|
|
|
iov->iov_base += cnt;
|
|
iov->iov_len -= cnt;
|
|
uio->uio_resid -= cnt;
|
|
uio->uio_offset += cnt;
|
|
|
|
//PRINTDBG ("uflash_read ('%s')\n", buf);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Commands:
|
|
* UFLASH_LOAD - load the program specified into user flash
|
|
*/
|
|
|
|
int
|
|
uflash_ioctl (dev, cmd, addr, flag)
|
|
dev_t dev;
|
|
register u_int cmd;
|
|
caddr_t addr;
|
|
{
|
|
int result = 0;
|
|
struct inode *ip;
|
|
|
|
//unsigned unit = cmd & 0xff;
|
|
cmd &= ~0xff;
|
|
|
|
if( cmd != UFLASH_LOAD )
|
|
return EINVAL;
|
|
|
|
struct nameidata nd;
|
|
register struct nameidata *ndp = &nd;
|
|
|
|
//printf ("uflash_ioctl ('%s', ['%s', '%s', ...])\n", uap->fname, uap->argp[0], uap->argp[1]);
|
|
|
|
NDINIT (ndp, LOOKUP, FOLLOW, targetToFlash);
|
|
ip = namei (ndp);
|
|
if (ip == NULL) {
|
|
result = EACCES;
|
|
goto done;
|
|
}
|
|
|
|
struct stat sb;
|
|
int err = ino_stat (ip, &sb);
|
|
if( err ) {
|
|
result = err;
|
|
goto done;
|
|
}
|
|
|
|
/* Erase Pages */
|
|
|
|
unsigned address = USE_USER_FLASH_START;
|
|
int remaining = (int)sb.st_size;
|
|
|
|
while( remaining > 0 ) {
|
|
flashErasePage( address );
|
|
address += 4096;
|
|
remaining -= 4096;
|
|
}
|
|
|
|
/* TODO: reorgainize writing so that everything except the first
|
|
* couple of bytes is written to flash and verified before
|
|
* writing and verifying the first couple of bytes. The purpose
|
|
* would be to leave the uflash area in an invalid state so that
|
|
* the stub code would not attempt to execute it.
|
|
*/
|
|
|
|
/* Write bin */
|
|
|
|
address = USE_USER_FLASH_START;
|
|
remaining = (int)sb.st_size;
|
|
|
|
unsigned buffer[FLASH_BUFFER_INT_COUNT];
|
|
|
|
while( remaining ) {
|
|
unsigned readsize = FLASH_BUFFER_INT_COUNT * sizeof(unsigned);
|
|
if( readsize > remaining ) readsize = remaining;
|
|
|
|
int resid;
|
|
err = rdwri (UIO_READ, ip, (caddr_t)buffer, readsize, (off_t) address - USE_USER_FLASH_START, IO_UNIT, &resid);
|
|
|
|
if( err ) {
|
|
result = err;
|
|
goto done;
|
|
}
|
|
|
|
if( resid ) {
|
|
result = EIO;
|
|
goto done;
|
|
}
|
|
|
|
flashWriteUint32(address, buffer, FLASH_BUFFER_INT_COUNT);
|
|
|
|
address += readsize;
|
|
remaining -= readsize;
|
|
}
|
|
|
|
/* Verify bin */
|
|
|
|
address = USE_USER_FLASH_START;
|
|
remaining = (int)sb.st_size;
|
|
|
|
while( remaining ) {
|
|
unsigned readsize = FLASH_BUFFER_INT_COUNT * sizeof(unsigned);
|
|
if( readsize > remaining ) readsize = remaining;
|
|
|
|
int resid;
|
|
err = rdwri (UIO_READ, ip, (caddr_t)buffer, readsize, (off_t) address - (unsigned)USE_USER_FLASH_START, IO_UNIT, &resid);
|
|
|
|
if( err ) {
|
|
result = err;
|
|
goto done;
|
|
}
|
|
|
|
if( resid ) {
|
|
result = EIO;
|
|
goto done;
|
|
}
|
|
|
|
int i;
|
|
for( i = 0; i < readsize; ++i ) {
|
|
if( ((char*)address)[i] != ((char*)buffer)[i] ) {
|
|
result = EIO;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
address += readsize;
|
|
remaining -= readsize;
|
|
}
|
|
|
|
done:
|
|
if (ip)
|
|
iput(ip);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
/*** void flashOperation(unsigned nvmop, unsigned addr, unsigned data)
|
|
**
|
|
** Synopsis:
|
|
** Performs either a page erase, word write, or row write
|
|
**
|
|
** Parameters:
|
|
** nvmop either NVMOP_PAGE_ERASE, NVMOP_WORD_PGM, or NVMOP_ROW_PGM
|
|
** addr the unsigned_t flash address of: the page to erase, word location to write, or row location to write
|
|
** data a unsigned_t of data to write, or the unsigned_t of the SRAM address containing the array of data to write to the row
|
|
**
|
|
** Return Values:
|
|
** True if successful, false if failed
|
|
**
|
|
** Errors:
|
|
** None
|
|
**
|
|
** Notes:
|
|
** data has no meaning when page erase is specified and should be set to 0ul
|
|
**
|
|
*/
|
|
|
|
/*
|
|
* nvm_operation is borrowed from usb_boot.c and is controlled
|
|
* by the following copyright:
|
|
*
|
|
* USB HID bootloader for PIC32 microcontroller.
|
|
*
|
|
* Based on Microchip sources.
|
|
* Heavily rewritten by Serge Vakulenko, <serge@vak.ru>.
|
|
*
|
|
* The software supplied herewith by Microchip Technology Incorporated
|
|
* (the 'Company') for its PIC(R) Microcontroller is intended and
|
|
* supplied to you, the Company's customer, for use solely and
|
|
* exclusively on Microchip PIC Microcontroller products. The
|
|
* software is owned by the Company and/or its supplier, and is
|
|
* protected under applicable copyright laws. All rights are reserved.
|
|
* Any use in violation of the foregoing restrictions may subject the
|
|
* user to criminal sanctions under applicable laws, as well as to
|
|
* civil liability for the breach of the terms and conditions of this license.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED IN AN 'AS IS' CONDITION. NO WARRANTIES,
|
|
* WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED
|
|
* TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
|
* PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. THE COMPANY SHALL NOT,
|
|
* IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL OR
|
|
* CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
|
|
*/
|
|
static void nvm_operation (unsigned op, unsigned address, unsigned data)
|
|
{
|
|
int x;
|
|
|
|
// Convert virtual address to physical
|
|
NVMADDR = address & 0x1fffffff;
|
|
NVMDATA = data;
|
|
|
|
// Disable interrupts
|
|
x = mips_intr_disable();
|
|
|
|
// Enable Flash Write/Erase Operations
|
|
NVMCON = PIC32_NVMCON_WREN | op;
|
|
|
|
// Data sheet prescribes 6us delay for LVD to become stable.
|
|
// To be on the safer side, we shall set 7us delay.
|
|
udelay (7);
|
|
|
|
NVMKEY = 0xAA996655;
|
|
NVMKEY = 0x556699AA;
|
|
NVMCONSET = PIC32_NVMCON_WR;
|
|
|
|
// Wait for WR bit to clear
|
|
while (NVMCON & PIC32_NVMCON_WR)
|
|
continue;
|
|
|
|
// Disable Flash Write/Erase operations
|
|
NVMCONCLR = PIC32_NVMCON_WREN;
|
|
|
|
// Enable interrupts
|
|
mips_intr_restore (x);
|
|
#if 0
|
|
// Check error status
|
|
if (NVMCON & (PIC32_NVMCON_WRERR | PIC32_NVMCON_LVDERR)) {
|
|
TODO;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// flashErasePage and flashWriteUint32 where originally from the file flash.c
|
|
// implements the low level flash control and access.
|
|
//
|
|
// flash.c originated from the cpustick.com skeleton project from
|
|
// http://www.cpustick.com/downloads.htm and was originally written
|
|
// by Rich Testardi; please preserve this reference and share bug
|
|
// fixes with rich@testardi.com.
|
|
/*** void flashErasePage(unsigned addrPage)
|
|
**
|
|
** Synopsis:
|
|
** Erases the page starting at the page address.
|
|
*
|
|
** Parameters:
|
|
** addrPage The virtual address of the page to erase
|
|
**
|
|
** Return Values:
|
|
** None
|
|
**
|
|
** Errors:
|
|
** None
|
|
**
|
|
** Notes:
|
|
**
|
|
** addrPage must be page aligned.
|
|
**
|
|
*/
|
|
static void flashErasePage(unsigned addrPage)
|
|
{
|
|
int i;
|
|
int j;
|
|
unsigned x = ~0;
|
|
unsigned *rgUint32 = (unsigned *) addrPage;
|
|
|
|
// we learned that just because the flash does not successfully
|
|
// erase on the first attempt, it might on another. We found that
|
|
// we can double the life of flash by attempting to
|
|
// erase the flash up to 5 times before quitting.
|
|
for(j=0; j<5; j++) {
|
|
// first check to see if the page needs to be erased
|
|
for (i = 0; i < FLASH_PAGE_SIZE/sizeof(unsigned); i++) {
|
|
x &= rgUint32[i];
|
|
}
|
|
|
|
// flash erased, we are done.
|
|
if(x == ~0) {
|
|
break;
|
|
}
|
|
|
|
// Unlock and Erase Page
|
|
nvm_operation(NVMOP_PAGE_ERASE, addrPage, 0);
|
|
}
|
|
|
|
// at this point, we don't care if the flash ever actually erased as
|
|
// as we will catch the failure when we verify the programming.
|
|
}
|
|
|
|
|
|
/*** void flashWriteUint32(unsigned addrUint32, unsigned *rgu32Data, unsigned cu32Data)
|
|
**
|
|
** Synopsis:
|
|
** Writes an array to unsigned to flash
|
|
*
|
|
** Parameters:
|
|
** rgu32Data Pointer to an array of unsigned
|
|
** cu32Data The number of unsigned to write
|
|
**
|
|
** Return Values:
|
|
** None
|
|
**
|
|
** Errors:
|
|
** None
|
|
**
|
|
** Notes:
|
|
**
|
|
** Assumes the pages have already been erased.
|
|
**
|
|
*/
|
|
static void flashWriteUint32(unsigned addrUint32, unsigned *rgu32Data, unsigned cu32Data)
|
|
{
|
|
int i = 0;
|
|
|
|
for(i=0; i < cu32Data; i++) {
|
|
// only do this if the data is not what is already in flash
|
|
if(rgu32Data[i] != ~0) {
|
|
// Write the data
|
|
nvm_operation(NVMOP_WORD_PGM, addrUint32, rgu32Data[i]);
|
|
}
|
|
addrUint32 += sizeof(unsigned);
|
|
}
|
|
}
|