xtensa/esp32: Add high memory support to work with PSRAM
This commit is contained in:
parent
cbd4e90781
commit
f09d103528
@ -591,6 +591,37 @@ config ESP32_SPIRAM_2T_MODE
|
||||
Applications will not be affected unless the use the esp_himem
|
||||
APIs, which are not supported in 2T mode.
|
||||
|
||||
config SPIRAM_BANKSWITCH_ENABLE
|
||||
bool "Enable bank switching for >4MiB external RAM"
|
||||
default y
|
||||
help
|
||||
The ESP32 only supports 4MiB of external RAM in its address
|
||||
space. The hardware does support larger memories, but these
|
||||
have to be bank-switched in and out of this address space.
|
||||
Enabling this allows you to reserve some MMU pages for this,
|
||||
which allows the use of the esp_himem api to manage these
|
||||
banks.
|
||||
#Note that this is limited to 62 banks, as
|
||||
#esp_spiram_writeback_cache needs some kind of mapping of
|
||||
#some banks below that mark to work. We cannot at this
|
||||
#moment guarantee this to exist when himem is enabled.
|
||||
If spiram 2T mode is enabled, the size of 64Mbit psram will
|
||||
be changed as 32Mbit, so himem will be unusable.
|
||||
|
||||
config SPIRAM_BANKSWITCH_RESERVE
|
||||
int "Amount of 32K pages to reserve for bank switching"
|
||||
depends on SPIRAM_BANKSWITCH_ENABLE
|
||||
default 8
|
||||
range 1 62
|
||||
help
|
||||
Select the amount of banks reserved for bank switching. Note
|
||||
that the amount of RAM allocatable with malloc will decrease
|
||||
by 32K for each page reserved here.
|
||||
Note that this reservation is only actually done if your
|
||||
program actually uses the himem API. Without any himem
|
||||
calls, the reservation is not done and the original amount
|
||||
of memory will be available.
|
||||
|
||||
endmenu #SPI RAM Config
|
||||
|
||||
menu "Ethernet configuration"
|
||||
|
@ -121,6 +121,7 @@ endif
|
||||
ifeq ($(CONFIG_ESP32_SPIRAM),y)
|
||||
CHIP_CSRCS += esp32_spiram.c
|
||||
CHIP_CSRCS += esp32_psram.c
|
||||
CHIP_CSRCS += esp32_himem.c
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_ESP32_EMAC),y)
|
||||
|
855
arch/xtensa/src/esp32/esp32_himem.c
Normal file
855
arch/xtensa/src/esp32/esp32_himem.c
Normal file
@ -0,0 +1,855 @@
|
||||
/****************************************************************************
|
||||
* arch/xtensa/src/esp32/esp32_himem.c
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <debug.h>
|
||||
#include <nuttx/kmalloc.h>
|
||||
#include <nuttx/himem/himem.h>
|
||||
|
||||
#include "esp32_spiram.h"
|
||||
#include "esp32_himem.h"
|
||||
#include "hardware/esp32_soc.h"
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/* So, why does the API look this way and is so inflexible to not allow any
|
||||
* maps beyond the full 32K chunks? Most of it has to do with the fact that
|
||||
* the cache works on the *virtual* addresses What this comes down to is that
|
||||
* while it's allowed to map a range of physical memory into the address
|
||||
* space two times, there's no cache consistency between the two regions.
|
||||
*
|
||||
* This means that a write to region A may or may not show up, perhaps
|
||||
* delayed, in region B, as it depends on the time that the writeback to SPI
|
||||
* RAM is done on A and the time before the corresponding cache line is
|
||||
* invalidated on B. Note that this goes for every 32-byte cache line: this
|
||||
* implies that if a program writes to address X and Y within A, the write to
|
||||
* Y may show up before the write to X does.
|
||||
*
|
||||
* It gets even worse when both A and B are written: theoretically, a write
|
||||
* to a 32-byte cache line in A can be entirely undone because of a write to
|
||||
* a different addres in B that happens to be in the same 32-byte cache line.
|
||||
*
|
||||
* Because of these reasons, we do not allow double mappings at all. This,
|
||||
* however, has other implications that make supporting ranges not really
|
||||
* useful. Because the lack of double mappings, applications will need to do
|
||||
* their own management of mapped regions, meaning they will normally map in
|
||||
* and out blocks at a time anyway, as mapping more fluent regions would
|
||||
* result in the chance of accidentally mapping two overlapping regions. As
|
||||
* this is the case, to keep the code simple, at the moment we just force
|
||||
* these blocks to be equal to the 32K MMU page size. The API itself does
|
||||
* allow for more granular allocations, so if there's a pressing need for a
|
||||
* more complex solution in the future, we can do this.
|
||||
*
|
||||
* Note: In the future, we can expand on this api to do a memcpy() between
|
||||
* SPI RAM and (internal) memory using the SPI1 peripheral. This needs
|
||||
* support for SPI1 to be in the SPI driver, however.
|
||||
*/
|
||||
|
||||
/* How many 32KB pages will be reserved for bank switch */
|
||||
|
||||
#if CONFIG_SPIRAM_BANKSWITCH_ENABLE
|
||||
# define SPIRAM_BANKSWITCH_RESERVE CONFIG_SPIRAM_BANKSWITCH_RESERVE
|
||||
#else
|
||||
# define SPIRAM_BANKSWITCH_RESERVE 0
|
||||
#endif
|
||||
|
||||
#define CACHE_BLOCKSIZE (32*1024)
|
||||
|
||||
/* Start of the virtual address range reserved for himem use */
|
||||
|
||||
#define VIRT_HIMEM_RANGE_START (SOC_EXTRAM_DATA_LOW + \
|
||||
(128 - SPIRAM_BANKSWITCH_RESERVE) * \
|
||||
CACHE_BLOCKSIZE)
|
||||
|
||||
/* Start MMU block reserved for himem use */
|
||||
|
||||
#define VIRT_HIMEM_RANGE_BLOCKSTART (128-SPIRAM_BANKSWITCH_RESERVE)
|
||||
|
||||
/* Start physical block */
|
||||
|
||||
#define PHYS_HIMEM_BLOCKSTART (128 - SPIRAM_BANKSWITCH_RESERVE)
|
||||
|
||||
#define HIMEM_CHECK(cond, str, err) if (cond) \
|
||||
do \
|
||||
{ merr("%s: %s", __FUNCTION__, str); \
|
||||
return err; \
|
||||
} while(0)
|
||||
|
||||
/* Character driver methods */
|
||||
|
||||
static int himem_open(FAR struct file *filep);
|
||||
static int himem_close(FAR struct file *filep);
|
||||
static ssize_t himem_read(FAR struct file *filep, FAR char *buffer,
|
||||
size_t buflen);
|
||||
static ssize_t himem_write(FAR struct file *filep, FAR const char *buffer,
|
||||
size_t buflen);
|
||||
static int himem_ioctl(FAR struct file *filep, int cmd,
|
||||
unsigned long arg);
|
||||
|
||||
/* This structure is used only for access control */
|
||||
|
||||
struct himem_access_s
|
||||
{
|
||||
sem_t exclsem; /* Supports mutual exclusion */
|
||||
};
|
||||
|
||||
/* Metadata for a block of physical RAM */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned int is_alloced: 1;
|
||||
unsigned int is_mapped: 1;
|
||||
} ramblock_t;
|
||||
|
||||
/* Metadata for a 32-K memory address range */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned int is_alloced: 1;
|
||||
unsigned int is_mapped: 1;
|
||||
unsigned int ram_block: 16;
|
||||
} rangeblock_t;
|
||||
|
||||
static ramblock_t *g_ram_descriptor = NULL;
|
||||
static rangeblock_t *g_range_descriptor = NULL;
|
||||
static int g_ramblockcnt = 0;
|
||||
static const int g_rangeblockcnt = SPIRAM_BANKSWITCH_RESERVE;
|
||||
|
||||
/* Used by the spinlock */
|
||||
|
||||
irqstate_t spinlock_flags;
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
||||
static const struct file_operations g_himemfops =
|
||||
{
|
||||
himem_open,
|
||||
himem_close,
|
||||
himem_read,
|
||||
himem_write,
|
||||
NULL,
|
||||
himem_ioctl,
|
||||
NULL
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Private functions
|
||||
****************************************************************************/
|
||||
|
||||
static inline int ramblock_idx_valid(int ramblock_idx)
|
||||
{
|
||||
return (ramblock_idx >= 0 && ramblock_idx < g_ramblockcnt);
|
||||
}
|
||||
|
||||
static inline int rangeblock_idx_valid(int rangeblock_idx)
|
||||
{
|
||||
return (rangeblock_idx >= 0 && rangeblock_idx < g_rangeblockcnt);
|
||||
}
|
||||
|
||||
static void set_bank(int virt_bank, int phys_bank, int ct)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = cache_sram_mmu_set(0, 0, SOC_EXTRAM_DATA_LOW + CACHE_BLOCKSIZE *
|
||||
virt_bank, phys_bank * CACHE_BLOCKSIZE, 32, ct);
|
||||
DEBUGASSERT(r == 0);
|
||||
r = cache_sram_mmu_set(1, 0, SOC_EXTRAM_DATA_LOW + CACHE_BLOCKSIZE *
|
||||
virt_bank, phys_bank * CACHE_BLOCKSIZE, 32, ct);
|
||||
DEBUGASSERT(r == 0);
|
||||
|
||||
UNUSED(r);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
size_t esp_himem_get_phys_size(void)
|
||||
{
|
||||
int paddr_start = (4096 * 1024) - (CACHE_BLOCKSIZE *
|
||||
SPIRAM_BANKSWITCH_RESERVE);
|
||||
return esp_spiram_get_size() - paddr_start;
|
||||
}
|
||||
|
||||
size_t esp_himem_get_free_size(void)
|
||||
{
|
||||
size_t ret = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < g_ramblockcnt; i++)
|
||||
{
|
||||
if (!g_ram_descriptor[i].is_alloced)
|
||||
{
|
||||
ret += CACHE_BLOCKSIZE;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t esp_himem_reserved_area_size(void)
|
||||
{
|
||||
return CACHE_BLOCKSIZE * SPIRAM_BANKSWITCH_RESERVE;
|
||||
}
|
||||
|
||||
int esp_himem_init(void)
|
||||
{
|
||||
FAR struct himem_access_s *priv;
|
||||
int paddr_start = (4096 * 1024) - (CACHE_BLOCKSIZE *
|
||||
SPIRAM_BANKSWITCH_RESERVE);
|
||||
int paddr_end;
|
||||
int maxram;
|
||||
int ret;
|
||||
|
||||
if (SPIRAM_BANKSWITCH_RESERVE == 0)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Allocate a new himem access instance */
|
||||
|
||||
priv = (FAR struct himem_access_s *)
|
||||
kmm_zalloc(sizeof(struct himem_access_s));
|
||||
|
||||
if (!priv)
|
||||
{
|
||||
merr("ERROR: Failed to allocate device structure\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
maxram = esp_spiram_get_size();
|
||||
|
||||
/* Catch double init */
|
||||
|
||||
/* Looks weird; last arg is empty so it expands to 'return ;' */
|
||||
|
||||
HIMEM_CHECK(g_ram_descriptor != NULL, "already initialized", (int) NULL);
|
||||
|
||||
HIMEM_CHECK(g_range_descriptor != NULL, "already initialized", (int) NULL);
|
||||
|
||||
/* need to have some reserved banks */
|
||||
|
||||
HIMEM_CHECK(SPIRAM_BANKSWITCH_RESERVE == 0, "No banks reserved for \
|
||||
himem", (int) NULL);
|
||||
|
||||
/* Start and end of physical reserved memory. Note it starts slightly under
|
||||
* the 4MiB mark as the reserved banks can't have an unity mapping to be
|
||||
* used by malloc anymore; we treat them as himem instead.
|
||||
*/
|
||||
|
||||
paddr_end = maxram;
|
||||
g_ramblockcnt = ((paddr_end - paddr_start) / CACHE_BLOCKSIZE);
|
||||
|
||||
/* Allocate data structures */
|
||||
|
||||
g_ram_descriptor = kmm_malloc(sizeof(ramblock_t) * g_ramblockcnt);
|
||||
g_range_descriptor = kmm_malloc(sizeof(rangeblock_t) * \
|
||||
SPIRAM_BANKSWITCH_RESERVE);
|
||||
|
||||
if (g_ram_descriptor == NULL || g_range_descriptor == NULL)
|
||||
{
|
||||
merr("Cannot allocate memory for meta info. Not initializing!");
|
||||
free(g_ram_descriptor);
|
||||
free(g_range_descriptor);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Register the character driver */
|
||||
|
||||
ret = register_driver("/dev/himem", &g_himemfops, 0666, priv);
|
||||
if (ret < 0)
|
||||
{
|
||||
merr("ERROR: Failed to register driver: %d\n", ret);
|
||||
kmm_free(priv);
|
||||
}
|
||||
|
||||
minfo("Initialized. Using last %d 32KB address blocks for bank \
|
||||
switching on %d KB of physical memory.\n",
|
||||
SPIRAM_BANKSWITCH_RESERVE, (paddr_end - paddr_start) / 1024);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* Allocate count not-necessarily consecutive physical RAM blocks, return
|
||||
* numbers in blocks[]. Return true if blocks can be allocated, false if not.
|
||||
*/
|
||||
|
||||
static bool allocate_blocks(int count, uint16_t *blocks_out)
|
||||
{
|
||||
int n = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < g_ramblockcnt && n != count; i++)
|
||||
{
|
||||
if (!g_ram_descriptor[i].is_alloced)
|
||||
{
|
||||
blocks_out[n] = i;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
if (n == count)
|
||||
{
|
||||
/* All blocks could be allocated. Mark as in use. */
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
g_ram_descriptor[blocks_out[i]].is_alloced = true;
|
||||
g_ram_descriptor[blocks_out[i]].is_mapped = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Error allocating blocks */
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int esp_himem_alloc(size_t size, esp_himem_handle_t *handle_out)
|
||||
{
|
||||
esp_himem_ramdata_t *r;
|
||||
int blocks;
|
||||
int ok;
|
||||
|
||||
/* If the size is not multiple of BLOCKSIZE, there is an issue */
|
||||
|
||||
if (size % CACHE_BLOCKSIZE != 0)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
blocks = size / CACHE_BLOCKSIZE;
|
||||
|
||||
r = kmm_malloc(sizeof(esp_himem_ramdata_t));
|
||||
if (!r)
|
||||
{
|
||||
goto nomem;
|
||||
}
|
||||
|
||||
r->block = kmm_malloc(sizeof(uint16_t) * blocks);
|
||||
if (!r->block)
|
||||
{
|
||||
goto nomem;
|
||||
}
|
||||
|
||||
spinlock_flags = spin_lock_irqsave();
|
||||
|
||||
ok = allocate_blocks(blocks, r->block);
|
||||
|
||||
spin_unlock_irqrestore(spinlock_flags);
|
||||
if (!ok)
|
||||
{
|
||||
goto nomem;
|
||||
}
|
||||
|
||||
r->block_ct = blocks;
|
||||
*handle_out = r;
|
||||
|
||||
return OK;
|
||||
|
||||
nomem:
|
||||
if (r)
|
||||
{
|
||||
free(r->block);
|
||||
free(r);
|
||||
}
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int esp_himem_free(esp_himem_handle_t handle)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Check if any of the blocks is still mapped; fail if this is the case. */
|
||||
|
||||
for (i = 0; i < handle->block_ct; i++)
|
||||
{
|
||||
DEBUGASSERT(ramblock_idx_valid(handle->block[i]));
|
||||
HIMEM_CHECK(g_ram_descriptor[handle->block[i]].is_mapped,
|
||||
"block in range still mapped", -EINVAL);
|
||||
}
|
||||
|
||||
/* Mark blocks as free */
|
||||
|
||||
spinlock_flags = spin_lock_irqsave();
|
||||
for (i = 0; i < handle->block_ct; i++)
|
||||
{
|
||||
g_ram_descriptor[handle->block[i]].is_alloced = false;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(spinlock_flags);
|
||||
|
||||
/* Free handle */
|
||||
|
||||
free(handle->block);
|
||||
free(handle);
|
||||
return OK;
|
||||
}
|
||||
|
||||
int esp_himem_alloc_map_range(size_t size,
|
||||
esp_himem_rangehandle_t *handle_out)
|
||||
{
|
||||
esp_himem_rangedata_t *r;
|
||||
int i;
|
||||
int blocks;
|
||||
int start_free;
|
||||
|
||||
HIMEM_CHECK(g_ram_descriptor == NULL, "Himem not available!",
|
||||
-EINVAL);
|
||||
|
||||
HIMEM_CHECK(size % CACHE_BLOCKSIZE != 0,
|
||||
"requested size not aligned to blocksize",
|
||||
-EINVAL);
|
||||
|
||||
blocks = size / CACHE_BLOCKSIZE;
|
||||
|
||||
r = kmm_malloc(sizeof(esp_himem_rangedata_t) * 1);
|
||||
if (!r)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
r->block_ct = blocks;
|
||||
r->block_start = -1;
|
||||
|
||||
start_free = 0;
|
||||
spinlock_flags = spin_lock_irqsave();
|
||||
|
||||
for (i = 0; i < g_rangeblockcnt; i++)
|
||||
{
|
||||
if (g_range_descriptor[i].is_alloced)
|
||||
{
|
||||
start_free = i + 1; /* optimistically assume next block is free... */
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i - start_free == blocks - 1)
|
||||
{
|
||||
/* We found a span of blocks that's big enough to allocate
|
||||
* the requested range in.
|
||||
*/
|
||||
|
||||
r->block_start = start_free;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (r->block_start == -1)
|
||||
{
|
||||
/* Couldn't find enough free blocks */
|
||||
|
||||
free(r);
|
||||
spin_unlock_irqrestore(spinlock_flags);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Range is found. Mark the blocks as in use. */
|
||||
|
||||
for (i = 0; i < blocks; i++)
|
||||
{
|
||||
g_range_descriptor[r->block_start + i].is_alloced = 1;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(spinlock_flags);
|
||||
|
||||
/* All done. */
|
||||
|
||||
*handle_out = r;
|
||||
return OK;
|
||||
}
|
||||
|
||||
int esp_himem_free_map_range(esp_himem_rangehandle_t handle)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Check if any of the blocks in the range have a mapping */
|
||||
|
||||
for (i = 0; i < handle->block_ct; i++)
|
||||
{
|
||||
assert(rangeblock_idx_valid(handle->block_start + i));
|
||||
|
||||
/* should be allocated, if handle is valid */
|
||||
|
||||
assert(g_range_descriptor[i + handle->block_start].is_alloced == 1);
|
||||
|
||||
HIMEM_CHECK(g_range_descriptor[i + handle->block_start].is_mapped,
|
||||
"memory still mapped to range", -EINVAL);
|
||||
}
|
||||
|
||||
/* We should be good to free this. Mark blocks as free. */
|
||||
|
||||
spinlock_flags = spin_lock_irqsave();
|
||||
|
||||
for (i = 0; i < handle->block_ct; i++)
|
||||
{
|
||||
g_range_descriptor[i + handle->block_start].is_alloced = 0;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(spinlock_flags);
|
||||
free(handle);
|
||||
return OK;
|
||||
}
|
||||
|
||||
int esp_himem_map(esp_himem_handle_t handle,
|
||||
esp_himem_rangehandle_t range,
|
||||
size_t ram_offset,
|
||||
size_t range_offset,
|
||||
size_t len,
|
||||
int flags,
|
||||
void **out_ptr)
|
||||
{
|
||||
int i;
|
||||
int ram_block = ram_offset / CACHE_BLOCKSIZE;
|
||||
int range_block = range_offset / CACHE_BLOCKSIZE;
|
||||
int blockcount = len / CACHE_BLOCKSIZE;
|
||||
|
||||
HIMEM_CHECK(g_ram_descriptor == NULL, "Himem not available!",
|
||||
-EINVAL);
|
||||
|
||||
/* Offsets and length must be block-aligned */
|
||||
|
||||
HIMEM_CHECK(ram_offset % CACHE_BLOCKSIZE != 0,
|
||||
"ram offset not aligned to blocksize", -EINVAL);
|
||||
|
||||
HIMEM_CHECK(range_offset % CACHE_BLOCKSIZE != 0,
|
||||
"range not aligned to blocksize", -EINVAL);
|
||||
|
||||
HIMEM_CHECK(len % CACHE_BLOCKSIZE != 0,
|
||||
"length not aligned to blocksize", -EINVAL);
|
||||
|
||||
/* ram and range should be within allocated range */
|
||||
|
||||
HIMEM_CHECK(ram_block + blockcount > handle->block_ct,
|
||||
"args not in range of phys ram handle", -EINVAL);
|
||||
|
||||
HIMEM_CHECK(range_block + blockcount > range->block_ct,
|
||||
"args not in range of range handle", -EINVAL);
|
||||
|
||||
/* Check if ram blocks aren't already mapped, and if memory range is
|
||||
* unmapped.
|
||||
*/
|
||||
|
||||
for (i = 0; i < blockcount; i++)
|
||||
{
|
||||
HIMEM_CHECK(g_ram_descriptor[handle->block[i + ram_block]].is_mapped,
|
||||
"ram already mapped", -EINVAL);
|
||||
|
||||
HIMEM_CHECK(g_range_descriptor[range->block_start + i +
|
||||
range_block].is_mapped, "range already mapped",
|
||||
-EINVAL);
|
||||
}
|
||||
|
||||
/* Map and mark as mapped */
|
||||
|
||||
spinlock_flags = spin_lock_irqsave();
|
||||
|
||||
for (i = 0; i < blockcount; i++)
|
||||
{
|
||||
assert(ramblock_idx_valid(handle->block[i + ram_block]));
|
||||
g_ram_descriptor[handle->block[i + ram_block]].is_mapped = 1;
|
||||
g_range_descriptor[range->block_start + i + range_block].is_mapped = 1;
|
||||
g_range_descriptor[range->block_start + i + range_block].ram_block =
|
||||
handle->block[i + ram_block];
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(spinlock_flags);
|
||||
|
||||
for (i = 0; i < blockcount; i++)
|
||||
{
|
||||
set_bank(VIRT_HIMEM_RANGE_BLOCKSTART + range->block_start + i +
|
||||
range_block, handle->block[i + ram_block] +
|
||||
PHYS_HIMEM_BLOCKSTART, 1);
|
||||
}
|
||||
|
||||
/* Set out pointer */
|
||||
|
||||
*out_ptr = (void *)(VIRT_HIMEM_RANGE_START +
|
||||
(range->block_start + range_offset) * CACHE_BLOCKSIZE);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int esp_himem_unmap(esp_himem_rangehandle_t range, void *ptr,
|
||||
size_t len)
|
||||
{
|
||||
/* Note: doesn't actually unmap, just clears cache and marks blocks as
|
||||
* unmapped.
|
||||
* Future optimization: could actually lazy-unmap here: essentially, do
|
||||
* nothing and only clear the cache when we re-use the block for a
|
||||
* different physical address.
|
||||
*/
|
||||
|
||||
int range_offset = (uint32_t)ptr - VIRT_HIMEM_RANGE_START;
|
||||
int range_block = (range_offset / CACHE_BLOCKSIZE) - range->block_start;
|
||||
int blockcount = len / CACHE_BLOCKSIZE;
|
||||
int i;
|
||||
|
||||
HIMEM_CHECK(range_offset % CACHE_BLOCKSIZE != 0,
|
||||
"range offset not block-aligned", -EINVAL);
|
||||
|
||||
HIMEM_CHECK(len % CACHE_BLOCKSIZE != 0,
|
||||
"map length not block-aligned", -EINVAL);
|
||||
|
||||
HIMEM_CHECK(range_block + blockcount > range->block_ct,
|
||||
"range out of bounds for handle", -EINVAL);
|
||||
|
||||
spinlock_flags = spin_lock_irqsave();
|
||||
|
||||
for (i = 0; i < blockcount; i++)
|
||||
{
|
||||
int ramblock = g_range_descriptor[range->block_start + i +
|
||||
range_block].ram_block;
|
||||
|
||||
assert(ramblock_idx_valid(ramblock));
|
||||
g_ram_descriptor[ramblock].is_mapped = 0;
|
||||
g_range_descriptor[range->block_start + i + range_block].is_mapped = 0;
|
||||
}
|
||||
|
||||
esp_spiram_writeback_cache();
|
||||
spin_unlock_irqrestore(spinlock_flags);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: himem_open
|
||||
*
|
||||
* Description:
|
||||
* This function is called whenever the LM-75 device is opened.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int himem_open(FAR struct file *filep)
|
||||
{
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: himem_close
|
||||
*
|
||||
* Description:
|
||||
* This routine is called when the LM-75 device is closed.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int himem_close(FAR struct file *filep)
|
||||
{
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: himem_read
|
||||
****************************************************************************/
|
||||
|
||||
static ssize_t himem_read(FAR struct file *filep, FAR char *buffer,
|
||||
size_t buflen)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: himem_write
|
||||
****************************************************************************/
|
||||
|
||||
static ssize_t himem_write(FAR struct file *filep, FAR const char *buffer,
|
||||
size_t buflen)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: himem_ioctl
|
||||
****************************************************************************/
|
||||
|
||||
static int himem_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
|
||||
{
|
||||
int ret = OK;
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
/* Allocate the physical RAM blocks */
|
||||
|
||||
case HIMEMIOC_ALLOC_BLOCKS:
|
||||
{
|
||||
FAR struct esp_himem_par *param =
|
||||
(FAR struct esp_himem_par *)((uintptr_t)arg);
|
||||
|
||||
DEBUGASSERT(param != NULL);
|
||||
|
||||
/* Allocate the memory we're going to check. */
|
||||
|
||||
ret = esp_himem_alloc(param->memfree, &(param->handle));
|
||||
if (ret < 0)
|
||||
{
|
||||
minfo("Error: esp_himem_alloc() failed!\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
/* Free the physical RAM blocks */
|
||||
|
||||
case HIMEMIOC_FREE_BLOCKS:
|
||||
{
|
||||
FAR struct esp_himem_par *param =
|
||||
(FAR struct esp_himem_par *)((uintptr_t)arg);
|
||||
|
||||
DEBUGASSERT(param != NULL);
|
||||
|
||||
ret = esp_himem_free(param->handle);
|
||||
if (ret < 0)
|
||||
{
|
||||
minfo("Error: esp_himem_free() failed!\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
/* Allocate the maping range */
|
||||
|
||||
case HIMEMIOC_ALLOC_MAP_RANGE:
|
||||
{
|
||||
FAR struct esp_himem_par *param =
|
||||
(FAR struct esp_himem_par *)((uintptr_t)arg);
|
||||
|
||||
DEBUGASSERT(param != NULL);
|
||||
|
||||
/* Allocate a block of address range */
|
||||
|
||||
ret = esp_himem_alloc_map_range(ESP_HIMEM_BLKSZ, &(param->range));
|
||||
if (ret < 0)
|
||||
{
|
||||
minfo("Error: esp_himem_alloc_map_range() failed!\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
/* Free the maping range */
|
||||
|
||||
case HIMEMIOC_FREE_MAP_RANGE:
|
||||
{
|
||||
FAR struct esp_himem_par *param =
|
||||
(FAR struct esp_himem_par *)((uintptr_t)arg);
|
||||
|
||||
DEBUGASSERT(param != NULL);
|
||||
|
||||
ret = esp_himem_free_map_range(param->range);
|
||||
if (ret < 0)
|
||||
{
|
||||
minfo("Error: esp_himem_free_map_range() failed!\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
/* Map the himem blocks */
|
||||
|
||||
case HIMEMIOC_MAP:
|
||||
{
|
||||
FAR struct esp_himem_par *param =
|
||||
(FAR struct esp_himem_par *)((uintptr_t)arg);
|
||||
|
||||
DEBUGASSERT(param != NULL);
|
||||
|
||||
ret = esp_himem_map(param->handle,
|
||||
param->range,
|
||||
param->ram_offset,
|
||||
param->range_offset,
|
||||
param->len,
|
||||
param->flags,
|
||||
(void **) &(param->ptr));
|
||||
if (ret < 0)
|
||||
{
|
||||
minfo("error: esp_himem_map() failed!\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
/* Unmap the himem blocks */
|
||||
|
||||
case HIMEMIOC_UNMAP:
|
||||
{
|
||||
FAR struct esp_himem_par *param =
|
||||
(FAR struct esp_himem_par *)((uintptr_t)arg);
|
||||
|
||||
DEBUGASSERT(param != NULL);
|
||||
|
||||
ret = esp_himem_unmap(param->range,
|
||||
(void *) param->ptr,
|
||||
param->len);
|
||||
if (ret < 0)
|
||||
{
|
||||
minfo("error: esp_himem_unmap() failed!\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
/* Get the physical external memory size */
|
||||
|
||||
case HIMEMIOC_GET_PHYS_SIZE:
|
||||
{
|
||||
FAR struct esp_himem_par *param =
|
||||
(FAR struct esp_himem_par *)((uintptr_t)arg);
|
||||
|
||||
DEBUGASSERT(param != NULL);
|
||||
|
||||
param->memcnt = esp_himem_get_phys_size();
|
||||
}
|
||||
break;
|
||||
|
||||
/* Get the free memory size */
|
||||
|
||||
case HIMEMIOC_GET_FREE_SIZE:
|
||||
{
|
||||
FAR struct esp_himem_par *param =
|
||||
(FAR struct esp_himem_par *)((uintptr_t)arg);
|
||||
|
||||
DEBUGASSERT(param != NULL);
|
||||
|
||||
param->memfree = esp_himem_get_free_size();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
sninfo("Unrecognized cmd: %d\n", cmd);
|
||||
ret = -ENOTTY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
193
arch/xtensa/src/esp32/esp32_himem.h
Normal file
193
arch/xtensa/src/esp32/esp32_himem.h
Normal file
@ -0,0 +1,193 @@
|
||||
/****************************************************************************
|
||||
* arch/xtensa/src/esp32/esp32_himem.h
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __ARCH_XTENSA_SRC_ESP32_ESP32_HIMEM_H
|
||||
#define __ARCH_XTENSA_SRC_ESP32_ESP32_HIMEM_H
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* Indicates that a mapping will only be read from. Note that this is unused
|
||||
* for now.
|
||||
*/
|
||||
|
||||
#define ESP_HIMEM_MAPFLAG_RO 1
|
||||
|
||||
/* Allocate a block in high memory
|
||||
*
|
||||
* params:
|
||||
* size Size of the to-be-allocated block, in bytes. Note that this
|
||||
* needs to be a multiple of the external RAM mmu block size
|
||||
* (32K).
|
||||
* [out] handle_out Handle to be returned
|
||||
*
|
||||
* returns:
|
||||
* - ESP_OK if succesful
|
||||
* - ESP_ERR_NO_MEM if out of memory
|
||||
* - ESP_ERR_INVALID_SIZE if size is not a multiple of 32K
|
||||
*/
|
||||
|
||||
int esp_himem_alloc(size_t size, esp_himem_handle_t *handle_out);
|
||||
|
||||
/* Description: Allocate a memory region to map blocks into
|
||||
*
|
||||
* This allocates a contiguous CPU memory region that can be used to map
|
||||
* blocks of physical memory into.
|
||||
*
|
||||
* params:
|
||||
* size Size of the range to be allocated. Note this needs to be a multiple
|
||||
* of the external RAM mmu block size (32K).
|
||||
* [out] handle_out Handle to be returned
|
||||
*
|
||||
* returns:
|
||||
* - ESP_OK if succesful
|
||||
* - ESP_ERR_NO_MEM if out of memory or address space
|
||||
* - ESP_ERR_INVALID_SIZE if size is not a multiple of 32K
|
||||
*/
|
||||
|
||||
int esp_himem_alloc_map_range(size_t size, esp_himem_rangehandle_t
|
||||
*handle_out);
|
||||
|
||||
/* Description: Map a block of high memory into the CPUs address space
|
||||
*
|
||||
* This effectively makes the block available for read/write operations.
|
||||
*
|
||||
* Note: The region to be mapped needs to have offsets and sizes that are
|
||||
* aligned to the SPI RAM MMU block size (32K)
|
||||
*
|
||||
* params:
|
||||
* handle Handle to the block of memory, as given by esp_himem_alloc
|
||||
* range Range handle to map the memory in
|
||||
* ram_offset Offset into the block of physical memory of the block to
|
||||
* map
|
||||
* range_offset Offset into the address range where the block will be
|
||||
* mapped,
|
||||
* len Length of region to map
|
||||
* flags One of ESP_HIMEM_MAPFLAG_*
|
||||
* [out] out_ptr Pointer to variable to store resulting memory pointer in
|
||||
*
|
||||
* returns:
|
||||
* - ESP_OK if the memory could be mapped
|
||||
* - ESP_ERR_INVALID_ARG if offset, range or len aren't MMU-block-aligned
|
||||
* (32K)
|
||||
* - ESP_ERR_INVALID_SIZE if the offsets/lengths don't fit in the allocated
|
||||
* memory or range
|
||||
* - ESP_ERR_INVALID_STATE if a block in the selected ram offset/length is
|
||||
* already mapped, or if a block in the selected range offset/length
|
||||
* already has a mapping.
|
||||
*/
|
||||
|
||||
int esp_himem_map(esp_himem_handle_t handle,
|
||||
esp_himem_rangehandle_t range,
|
||||
size_t ram_offset,
|
||||
size_t range_offset,
|
||||
size_t len,
|
||||
int flags,
|
||||
void **out_ptr);
|
||||
|
||||
/* Description: Free a block of physical memory
|
||||
*
|
||||
* This clears out the associated handle making the memory available for
|
||||
* re-allocation again.
|
||||
* This will only succeed if none of the memory blocks currently have a
|
||||
* mapping.
|
||||
*
|
||||
* params:
|
||||
* handle Handle to the block of memory, as given by esp_himem_alloc
|
||||
*
|
||||
* returns:
|
||||
* - ESP_OK if the memory is succesfully freed
|
||||
* - ESP_ERR_INVALID_ARG if the handle still is (partially) mapped
|
||||
*/
|
||||
|
||||
int esp_himem_free(esp_himem_handle_t handle);
|
||||
|
||||
/* Description: Free a mapping range
|
||||
*
|
||||
* This clears out the associated handle making the range available for
|
||||
* re-allocation again.
|
||||
* This will only succeed if none of the range blocks currently are used for
|
||||
* a mapping.
|
||||
*
|
||||
* params:
|
||||
* handle Handle to the range block, as given by esp_himem_alloc_map_range
|
||||
*
|
||||
* returns:
|
||||
* - ESP_OK if the memory is succesfully freed
|
||||
* - ESP_ERR_INVALID_ARG if the handle still is (partially) mapped to
|
||||
*/
|
||||
|
||||
int esp_himem_free_map_range(esp_himem_rangehandle_t handle);
|
||||
|
||||
/* Description: Unmap a region
|
||||
*
|
||||
* params:
|
||||
* range Range handle
|
||||
* ptr Pointer returned by esp_himem_map
|
||||
* len Length of the block to be unmapped. Must be aligned to the SPI RAM
|
||||
* MMU blocksize (32K)
|
||||
* returns:
|
||||
* - ESP_OK if the memory is succesfully unmapped,
|
||||
* - ESP_ERR_INVALID_ARG if ptr or len are invalid.
|
||||
*/
|
||||
|
||||
int esp_himem_unmap(esp_himem_rangehandle_t range, void *ptr,
|
||||
size_t len);
|
||||
|
||||
/* Description: Get total amount of memory under control of himem API
|
||||
*
|
||||
* returns:
|
||||
* Amount of memory, in bytes
|
||||
*/
|
||||
|
||||
size_t esp_himem_get_phys_size(void);
|
||||
|
||||
/* Description: Get free amount of memory under control of himem API
|
||||
*
|
||||
* returns:
|
||||
* Amount of free memory, in bytes
|
||||
*/
|
||||
|
||||
size_t esp_himem_get_free_size(void);
|
||||
|
||||
/* Description: Get amount of SPI memory address space needed for
|
||||
* bankswitching
|
||||
*
|
||||
* Note: This is also weakly defined in esp32/spiram.c and returns 0 there,
|
||||
* so if no other function in this file is used, no memory is reserved.
|
||||
*
|
||||
* return:
|
||||
* Amount of reserved area, in bytes
|
||||
*/
|
||||
|
||||
size_t esp_himem_reserved_area_size(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* __ARCH_XTENSA_SRC_ESP32_ESP32_HIMEM_H */
|
@ -61,6 +61,7 @@
|
||||
|
||||
#include <syslog.h>
|
||||
#include <sys/errno.h>
|
||||
#include <nuttx/himem/himem.h>
|
||||
|
||||
#include "esp32_procfs_imm.h"
|
||||
#include "esp32-core.h"
|
||||
@ -142,6 +143,14 @@ int esp32_bringup(void)
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ESP32_SPIRAM)
|
||||
ret = esp_himem_init();
|
||||
if (ret < 0)
|
||||
{
|
||||
syslog(LOG_ERR, "ERROR: Failed to init HIMEM: %d\n", ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FS_PROCFS
|
||||
/* Mount the procfs file system */
|
||||
|
||||
|
@ -100,6 +100,7 @@
|
||||
#define _NOTECTLBASE (0x2c00) /* Note filter control ioctl commands*/
|
||||
#define _NOTERAMBASE (0x2d00) /* Noteram device ioctl commands*/
|
||||
#define _RCIOCBASE (0x2e00) /* Remote Control device ioctl commands */
|
||||
#define _HIMEMBASE (0x2f00) /* Himem device ioctl commands*/
|
||||
#define _WLIOCBASE (0x8b00) /* Wireless modules ioctl network commands */
|
||||
|
||||
/* boardctl() commands share the same number space */
|
||||
@ -545,6 +546,11 @@
|
||||
#define _RCIOCVALID(c) (_IOC_TYPE(c)==_RCIOCBASE)
|
||||
#define _RCIOC(nr) _IOC(_RCIOCBASE,nr)
|
||||
|
||||
/* Hime drivers **********************************************************/
|
||||
|
||||
#define _HIMEMIOCVALID(c) (_IOC_TYPE(c) == _HIMEMBASE)
|
||||
#define _HIMEMIOC(nr) _IOC(_HIMEMBASE, nr)
|
||||
|
||||
/* Wireless driver network ioctl definitions ********************************/
|
||||
|
||||
/* (see nuttx/include/wireless/wireless.h */
|
||||
|
173
include/nuttx/himem/himem.h
Normal file
173
include/nuttx/himem/himem.h
Normal file
@ -0,0 +1,173 @@
|
||||
/****************************************************************************
|
||||
* include/nuttx/himem/himem.h
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef __INCLUDE_NUTTX_HIMEM_HIMEM_H
|
||||
#define __INCLUDE_NUTTX_HIMEM_HIMEM_H
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
#include <nuttx/fs/ioctl.h>
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#ifdef CONFIG_ESP32_SPIRAM
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/* ESP32 MMU block size */
|
||||
|
||||
#define ESP_HIMEM_BLKSZ (0x8000)
|
||||
|
||||
/* Command: HIMEMIOC_ALLOC_BLOCKS
|
||||
* Description: Allocate a certain number of physical RAM blocks.
|
||||
* Arguments: A structure containing the size of physical block to allocate
|
||||
* and its esp_himem_handle_t.
|
||||
* Return: Zero (OK) on success. Minus one will be returned on failure
|
||||
* with the errno value set appropriately.
|
||||
*/
|
||||
|
||||
#define HIMEMIOC_ALLOC_BLOCKS _HIMEMIOC(0x0001)
|
||||
|
||||
/* Command: HIMEMIOC_FREE_BLOCKS
|
||||
* Description: Free a certain number of physical RAM blocks.
|
||||
* Arguments: A structure containing the size of physical block to allocate
|
||||
* and its esp_himem_handle_t.
|
||||
* Return: Zero (OK) on success. Minus one will be returned on failure
|
||||
* with the errno value set appropriately.
|
||||
*/
|
||||
|
||||
#define HIMEMIOC_FREE_BLOCKS _HIMEMIOC(0x0002)
|
||||
|
||||
/* Command: HIMEMIOC_ALLOC_MAP_RANGE
|
||||
* Description: Free the physical RAM blocks
|
||||
* Arguments: A structure containing the block size and its
|
||||
* esp_himem_rangehandle_t.
|
||||
* Return: Zero (OK) on success. Minus one will be returned on failure
|
||||
* with the errno value set appropriately.
|
||||
*/
|
||||
|
||||
#define HIMEMIOC_ALLOC_MAP_RANGE _HIMEMIOC(0x0003)
|
||||
|
||||
/* Command: HIMEMIOC_FREE_MAP_RANGE
|
||||
* Description: Maps the memory addresses to the physical psram range.
|
||||
* Arguments: A structure containing the esp_himem_handle_t handle, the
|
||||
* esp_himem_rangehandle_t, the ram offset, the range offset,
|
||||
* the length, the memory flags and the output pointer.
|
||||
* Return: Zero (OK) on success. Minus one will be returned on failure
|
||||
* with the errno value set appropriately.
|
||||
*/
|
||||
|
||||
#define HIMEMIOC_FREE_MAP_RANGE _HIMEMIOC(0x0004)
|
||||
|
||||
/* Command: HIMEMIOC_MAP
|
||||
* Description: Maps the memory addresses to the physical psram range.
|
||||
* Arguments: A structure containing the esp_himem_handle_t handle, the
|
||||
* esp_himem_rangehandle_t, the ram offset, the range offset,
|
||||
* the length, the memory flags and the output pointer.
|
||||
* Return: Zero (OK) on success. Minus one will be returned on failure
|
||||
* with the errno value set appropriately.
|
||||
*/
|
||||
|
||||
#define HIMEMIOC_MAP _HIMEMIOC(0x0005)
|
||||
|
||||
/* Command: HIMEMIOC_UNMAP
|
||||
* Description: Unmaps the memory addresses to the physical psram range.
|
||||
* Arguments: A structure containing the esp_himem_rangehandle_t, the
|
||||
* memory pointer and the memory length.
|
||||
* the length, the memory flags and the output pointer.
|
||||
* Return: Zero (OK) on success. Minus one will be returned on failure
|
||||
* with the errno value set appropriately.
|
||||
*/
|
||||
|
||||
#define HIMEMIOC_UNMAP _HIMEMIOC(0x0006)
|
||||
|
||||
/* Command: HIMEMIOC_GET_PHYS_SIZE
|
||||
* Description: Get the size of physical external memory
|
||||
* Arguments: None
|
||||
* Return: Zero (OK) on success. Minus one will be returned on failure
|
||||
* with the errno value set appropriately.
|
||||
*/
|
||||
|
||||
#define HIMEMIOC_GET_PHYS_SIZE _HIMEMIOC(0x0007)
|
||||
|
||||
/* Command: HIMEMIOC_GET_FREE_SIZE
|
||||
* Description: Get the amount of free memory
|
||||
* Arguments: None
|
||||
* Return: Zero (OK) on success. Minus one will be returned on failure
|
||||
* with the errno value set appropriately.
|
||||
*/
|
||||
|
||||
#define HIMEMIOC_GET_FREE_SIZE _HIMEMIOC(0x0008)
|
||||
|
||||
/****************************************************************************
|
||||
* Public Types
|
||||
****************************************************************************/
|
||||
|
||||
/* Handle for a window of address space */
|
||||
|
||||
typedef struct esp_himem_rangedata_t
|
||||
{
|
||||
int block_ct;
|
||||
int block_start;
|
||||
} esp_himem_rangedata_t;
|
||||
|
||||
/* Handle for a range of physical memory */
|
||||
|
||||
typedef struct esp_himem_ramdata_t
|
||||
{
|
||||
int block_ct;
|
||||
uint16_t *block;
|
||||
} esp_himem_ramdata_t;
|
||||
|
||||
/* Opaque pointers as handles for ram/range data */
|
||||
|
||||
typedef struct esp_himem_ramdata_t *esp_himem_handle_t;
|
||||
typedef struct esp_himem_rangedata_t *esp_himem_rangehandle_t;
|
||||
|
||||
/* Structs with the parameters passed to the IOCTLs */
|
||||
|
||||
struct esp_himem_par
|
||||
{
|
||||
esp_himem_handle_t handle;
|
||||
esp_himem_rangehandle_t range;
|
||||
size_t ram_offset;
|
||||
size_t range_offset;
|
||||
size_t memfree;
|
||||
size_t memcnt;
|
||||
size_t len;
|
||||
int flags;
|
||||
uint32_t *ptr;
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Public Data
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define EXTERN extern "C"
|
||||
extern "C"
|
||||
{
|
||||
#else
|
||||
#define EXTERN extern
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Public Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
int esp_himem_init(void);
|
||||
|
||||
#undef EXTERN
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_ESP32_SPIRAM */
|
||||
#endif /* __INCLUDE_NUTTX_HIMEM_HIMEM_H */
|
Loading…
Reference in New Issue
Block a user