blob: 5197405eb453e29d9f0e140424799ed7d8b6550f [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <assert.h>
#include <hal/hal_flash_int.h>
#include "mcu/mips_hal.h"
#include <mcu/p32mx470f512h.h>
#include <string.h>
#include <xc.h>
#define VIRT_TO_PHY(ADDRESS) (unsigned int)((int)(ADDRESS) & 0x1FFFFFFF)
#define PHY_TO_VIRT(ADDRESS) (unsigned int)((int)(ADDRESS) | 0x80000000)
#define PIC32MX_FLASH_SECTOR_SZ (4 * 1024)
#define WORD_SIZE (4)
#define ROW_SIZE (512 * WORD_SIZE)
#define ROW_PROGRAM (0b0011)
#define WORD_PROGRAM (0b0001)
#define ERASE_PAGE (0b0100)
static int pic32mx_flash_read(const struct hal_flash *dev, uint32_t address,
void *dst, uint32_t num_bytes);
static int pic32mx_flash_write(const struct hal_flash *dev, uint32_t address,
const void *src, uint32_t num_bytes);
static int pic32mx_flash_erase_sector(const struct hal_flash *dev,
uint32_t sector_address);
static int pic32mx_flash_sector_info(const struct hal_flash *dev, int idx,
uint32_t *address, uint32_t *sz);
static int pic32mx_flash_init(const struct hal_flash *dev);
static const struct hal_flash_funcs pic32mx_flash_funcs = {
.hff_read = pic32mx_flash_read,
.hff_write = pic32mx_flash_write,
.hff_erase_sector = pic32mx_flash_erase_sector,
.hff_sector_info = pic32mx_flash_sector_info,
.hff_init = pic32mx_flash_init
};
const struct hal_flash pic32mx_flash_dev = {
.hf_itf = &pic32mx_flash_funcs,
.hf_base_addr = 0x1D000000,
.hf_size = 512 * 1024,
.hf_sector_cnt = 128,
.hf_align = 4, /* num bytes must be a multiple of 4 as writes can only
* be done on word boundary.
*/
.hf_erased_val = 0xff,
};
static int
flash_do_op(uint32_t op)
{
uint32_t ctx;
__HAL_DISABLE_INTERRUPTS(ctx);
NVMCON = _NVMCON_WREN_MASK | (op & _NVMCON_NVMOP_MASK);
/*
* Disable core timer by setting the DC flag in CP0 Cause register.
* If the core timer is not disabled, the kernel would miss the core timer
* interrupt while the CPU is stalling.
*/
_CP0_SET_CAUSE(_CP0_GET_CAUSE() | _CP0_CAUSE_DC_MASK);
NVMKEY = 0x0;
NVMKEY = 0xAA996655;
NVMKEY = 0x556699AA;
NVMCONSET = _NVMCON_WR_MASK;
while(NVMCON & _NVMCON_WR_MASK) {}
/* Re-enable core timer */
_CP0_SET_CAUSE(_CP0_GET_CAUSE() & ~_CP0_CAUSE_DC_MASK);
__HAL_ENABLE_INTERRUPTS(ctx);
NVMCONCLR = _NVMCON_WREN_MASK;
return (NVMCON & (_NVMCON_WRERR_MASK | _NVMCON_LVDERR_MASK)) ? -1 : 0;
}
static int
pic32mx_flash_read(const struct hal_flash *dev, uint32_t address,
void *dst, uint32_t num_bytes)
{
(void)dev;
memcpy(dst, (void *)PHY_TO_VIRT(address), num_bytes);
return 0;
}
static int
pic32mx_flash_write(const struct hal_flash *dev, uint32_t address,
const void *src, uint32_t num_bytes)
{
(void)dev;
const uint32_t *data = (const uint32_t*)src;
if (num_bytes & (WORD_SIZE -1)) {
return -1;
}
while (address & (ROW_SIZE - 1) && num_bytes >= WORD_SIZE) {
NVMADDR = address;
address += WORD_SIZE;
NVMDATA = *data++;
if (flash_do_op(WORD_PROGRAM)) {
return -1;
}
num_bytes -= WORD_SIZE;
}
while (num_bytes >= ROW_SIZE) {
NVMADDR = address;
address += ROW_SIZE;
NVMSRCADDR = VIRT_TO_PHY(data);
data += 512;
if (flash_do_op(ROW_PROGRAM)) {
return -1;
}
num_bytes -= ROW_SIZE;
}
while (num_bytes >= WORD_SIZE) {
NVMADDR = address;
address += WORD_SIZE;
NVMDATA = *data++;
if (flash_do_op(WORD_PROGRAM)) {
return -1;
}
num_bytes -= WORD_SIZE;
}
return 0;
}
static int
pic32mx_flash_erase_sector(const struct hal_flash *dev,
uint32_t sector_address)
{
(void)dev;
NVMADDR = sector_address;
return flash_do_op(ERASE_PAGE);
}
static int
pic32mx_flash_sector_info(const struct hal_flash *dev, int idx,
uint32_t *address, uint32_t *sz)
{
assert(idx < pic32mx_flash_dev.hf_sector_cnt);
*address = pic32mx_flash_dev.hf_base_addr + idx * PIC32MX_FLASH_SECTOR_SZ;
*sz = PIC32MX_FLASH_SECTOR_SZ;
return 0;
}
static int
pic32mx_flash_init(const struct hal_flash *dev)
{
(void)dev;
return 0;
}