| /** |
| * 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; |
| } |