From 077ad5b45fc9deaaf52a34516fae7ccd32b62623 Mon Sep 17 00:00:00 2001 From: ssssenai Date: Sat, 3 Dec 2022 02:09:58 +0800 Subject: [PATCH] arch: xtensa/esp32: Add esp32_himem_chardev.c Summary: - It is applicable to esp32 products and uses the himem part of 8M psram by creating character devices. Impact: - None Testing: - Use esp32-wrover series products for more than 1000 functional verifications. --- .../include/esp32/esp32_himem_chardev.h | 102 +++++ arch/xtensa/src/esp32/Make.defs | 1 + arch/xtensa/src/esp32/esp32_himem_chardev.c | 413 ++++++++++++++++++ 3 files changed, 516 insertions(+) create mode 100644 arch/xtensa/include/esp32/esp32_himem_chardev.h create mode 100644 arch/xtensa/src/esp32/esp32_himem_chardev.c diff --git a/arch/xtensa/include/esp32/esp32_himem_chardev.h b/arch/xtensa/include/esp32/esp32_himem_chardev.h new file mode 100644 index 0000000000..464ef0fa9f --- /dev/null +++ b/arch/xtensa/include/esp32/esp32_himem_chardev.h @@ -0,0 +1,102 @@ +/**************************************************************************** + * arch/xtensa/include/esp32/esp32_himem_chardev.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. + * + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifndef __ARCH_XTENSA_INCLUDE_ESP32_ESP32_HIMEM_CHARDEV_H +#define __ARCH_XTENSA_INCLUDE_ESP32_ESP32_HIMEM_CHARDEV_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/**************************************************************************** + * Name: himem_chardev_init + * + * Description: + * Himem_Cdev Initializes the operation. + * + * Input Parameters: + * None + * + * Returned Value: + * Returns 0 on success and non-zero on failure. + * + ****************************************************************************/ + +int himem_chardev_init(void); + +/**************************************************************************** + * Name: himem_chardev_exit + * + * Description: + * Himem_Cdev exits and releases the operation. + * + * Input Parameters: + * None + * + * Returned Value: + * Returns 0 on success and non-zero on failure. + * + ****************************************************************************/ + +int himem_chardev_exit(void); + +/**************************************************************************** + * Name: himem_chardev_register + * + * Description: + * Himem_Cdev indicates the registration operation. + * + * Input Parameters: + * name - Himem_Cdev indicates the registration name. + * size - Himem_Cdev registers the device size. + * + * Returned Value: + * Returns 0 on success and non-zero on failure. + * + ****************************************************************************/ + +int himem_chardev_register(char *name, size_t size); + +/**************************************************************************** + * Name: himem_chardev_unregister + * + * Description: + * Himem_Cdev cancels the registration + * + * Input Parameters: + * name - Himem_Cdev indicates the registration name. + * + * Returned Value: + * Returns 0 on success and non-zero on failure. + * + ****************************************************************************/ + +int himem_chardev_unregister(char *name); + +#ifdef __cplusplus +} +#endif + +#endif /* __ARCH_XTENSA_INCLUDE_ESP32_ESP32_HIMEM_CHARDEV_H */ diff --git a/arch/xtensa/src/esp32/Make.defs b/arch/xtensa/src/esp32/Make.defs index 4bed1702a1..f232f29c4d 100644 --- a/arch/xtensa/src/esp32/Make.defs +++ b/arch/xtensa/src/esp32/Make.defs @@ -105,6 +105,7 @@ ifeq ($(CONFIG_ESP32_SPIRAM),y) CHIP_CSRCS += esp32_spiram.c CHIP_CSRCS += esp32_psram.c CHIP_CSRCS += esp32_himem.c +CHIP_CSRCS += esp32_himem_chardev.c endif ifeq ($(CONFIG_ESP32_EFUSE),y) diff --git a/arch/xtensa/src/esp32/esp32_himem_chardev.c b/arch/xtensa/src/esp32/esp32_himem_chardev.c new file mode 100644 index 0000000000..4f81d2504e --- /dev/null +++ b/arch/xtensa/src/esp32/esp32_himem_chardev.c @@ -0,0 +1,413 @@ +/**************************************************************************** + * arch/xtensa/src/esp32/esp32_himem_chardev.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "esp32_himem.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define HIMEM_UNMAPPED (-1) + +struct himem_chardev_s +{ + struct list_node node; + size_t size; + esp_himem_handle_t mem_handle; + char name[32]; +}; + +struct himem_chardev_priv_s +{ + off_t offset; /* himem offset[byte]. update by seek(), read(), write(). */ +}; + +static struct list_node g_himem_chardev_list + = LIST_INITIAL_VALUE(g_himem_chardev_list); +static esp_himem_rangehandle_t g_range_handle; +static mutex_t lock; +static size_t g_ram_offset; /* used by himem map/unmap */ + +static void *g_himem_ptr; /* mapped himem pointer */ + +static struct file *g_mapped_filep; /* for multi device */ + +/**************************************************************************** + * Priavte Functions + ****************************************************************************/ + +static int himem_chardev_open(struct file *filep) +{ + struct himem_chardev_priv_s *priv; + + priv = kmm_malloc(sizeof(struct himem_chardev_priv_s)); + if (priv == NULL) + { + merr("Failed to malloc.\n"); + return -1; + } + priv->offset = 0; + filep->f_priv = priv; + + return 0; +} + +static int himem_chardev_close(struct file *filep) +{ + struct himem_chardev_priv_s *priv = filep->f_priv; + kmm_free(priv); + + return 0; +} + +static int himem_chardev_read_write(int is_write, struct file *filep, + char *buffer, size_t length) +{ + struct himem_chardev_s *dev = filep->f_inode->i_private; + struct himem_chardev_priv_s *priv = filep->f_priv; + size_t mmap_offset = 0; + int ret = 0; + size_t i = 0; + size_t copy_size = 0; + void *himem_ptr; + size_t copy_range = 0; + + ret = nxmutex_lock(&lock); + if (ret != 0) + { + merr("Failed to lock semaphore.\n"); + return -1; + } + + while (i < length) + { + mmap_offset = priv->offset / ESP_HIMEM_BLKSZ; + if (mmap_offset > (dev->size / ESP_HIMEM_BLKSZ)) + { + merr("copy size(%d) over himem phy mem size(%d).\n", + length, dev->size); + goto err; + } + if ((mmap_offset != g_ram_offset) || (g_mapped_filep != filep)) + { + if (g_ram_offset != HIMEM_UNMAPPED) + { + /* already mapped another page */ + + ret = esp_himem_unmap(g_range_handle, + g_himem_ptr, ESP_HIMEM_BLKSZ); + if (ret != 0) + { + merr("Failed to unmap himem.\n"); + goto err; + } + g_ram_offset = HIMEM_UNMAPPED; + g_mapped_filep = NULL; + } + ret = esp_himem_map(dev->mem_handle, g_range_handle, + mmap_offset * ESP_HIMEM_BLKSZ, + 0, ESP_HIMEM_BLKSZ, 0, &g_himem_ptr); + if (ret != 0) + { + merr("Failed to map himem.\n"); + goto err; + } + g_ram_offset = mmap_offset; + g_mapped_filep = filep; + } + himem_ptr = g_himem_ptr + priv->offset % ESP_HIMEM_BLKSZ; + copy_range = ESP_HIMEM_BLKSZ - priv->offset % ESP_HIMEM_BLKSZ; + + /* The case of crossing a phy mem page boundary */ + + if (copy_range < (length - i)) + { + if (is_write) + { + memcpy(himem_ptr, buffer + i, copy_range); + } + else + { + memcpy(buffer + i, himem_ptr, copy_range); + } + priv->offset += copy_range; + copy_size += copy_range; + i += copy_range; + } + else + { + if (is_write) + { + memcpy(himem_ptr, buffer + i, length - i); + } + else + { + memcpy(buffer + i, himem_ptr, length - i); + } + priv->offset += length - i; + copy_size += length - i; + i += length - i; + } + } + nxmutex_unlock(&lock); + return (int)(copy_size & INT_MAX); +err: + nxmutex_unlock(&lock); + return copy_size > 0 ? (int)(copy_size & INT_MAX) : -1; +} + +static int himem_chardev_read(struct file *filep, + char *dst, size_t length) +{ + return himem_chardev_read_write(0, filep, dst, length); +} + +static int himem_chardev_write(struct file *filep, + const char *src, size_t length) +{ + return himem_chardev_read_write(1, filep, (char *)src, length); +} + +static off_t himem_chardev_seek(struct file *filep, + off_t offset, int whence) +{ + struct himem_chardev_priv_s *priv = filep->f_priv; + struct himem_chardev_s *dev = filep->f_inode->i_private; + nxmutex_lock(&lock); + switch (whence) + { + case SEEK_SET: + priv->offset = offset; + break; + case SEEK_CUR: + priv->offset += offset; + break; + case SEEK_END: + priv->offset = dev->size + offset; + break; + default: + merr("invalid parameter: whence:%d\n", whence); + nxmutex_unlock(&lock); + return -1; + } + filep->f_pos = priv->offset; + nxmutex_unlock(&lock); + return priv->offset; +} + +static int himem_chardev_ioctl(struct file *filep, + int cmd, unsigned long arg) +{ + return 0; +} + +static const struct file_operations fops = +{ + .open = himem_chardev_open, + .close = himem_chardev_close, + .read = himem_chardev_read, + .write = himem_chardev_write, + .seek = himem_chardev_seek, + .ioctl = himem_chardev_ioctl, +}; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int himem_chardev_init(void) +{ + int ret = 0; + ret = esp_himem_init(); + if (ret != 0) + { + merr("Failed to himem init.\n"); + return ret; + } + + ret = esp_himem_alloc_map_range(ESP_HIMEM_BLKSZ, &g_range_handle); /* 32KB */ + if (ret != 0) + { + merr("Failed to alloc himem map.\n"); + return ret; + } + + ret = nxmutex_init(&lock); + if (ret != 0) + { + merr("Failed to init semaphore.\n"); + esp_himem_free_map_range(g_range_handle); + return ret; + } + + g_ram_offset = HIMEM_UNMAPPED; + g_mapped_filep = NULL; + return ret; +} + +int himem_chardev_exit(void) +{ + int ret = 0; + ret = nxmutex_destroy(&lock); + if (ret != 0) + { + merr("Failed to destroy semaphore.\n"); + esp_himem_free_map_range(g_range_handle); + return ret; + } + + ret = esp_himem_free_map_range(g_range_handle); + + return ret; +} + +int himem_chardev_register(char *name, size_t size) +{ + int ret = 0; + struct himem_chardev_s *dev; + dev = (struct himem_chardev_s *)kmm_malloc(sizeof(struct himem_chardev_s)); + if (dev == NULL) + { + merr("Failed to malloc.\n"); + return -1; + } + + /* 32KB Alignment */ + + size_t mod = size % ESP_HIMEM_BLKSZ; + if (mod != 0) + { + size += (ESP_HIMEM_BLKSZ - mod); + } + + dev->size = size; + + strncpy(dev->name, name, 32); + ret = esp_himem_alloc(dev->size, &dev->mem_handle); + if (ret != 0) + { + merr("Failed to alloc himem. size=%d\n", dev->size); + kmm_free(dev); + return ret; + } + + ret = register_driver(dev->name, &fops, 0666, dev); + if (ret != 0) + { + merr("Failed to register driver. dev=%s\n", dev->name); + esp_himem_free(dev->mem_handle); + kmm_free(dev); + return ret; + } + + ret = nxmutex_lock(&lock); + if (ret != 0) + { + merr("Failed to lock semaphore.\n"); + unregister_driver(dev->name); + esp_himem_free(dev->mem_handle); + kmm_free(dev); + return ret; + } + + list_add_tail(&g_himem_chardev_list, &dev->node); + ret = nxmutex_unlock(&lock); + if (ret != 0) + { + merr("Failed to unlock semaphore.\n"); + list_delete(&dev->node); + unregister_driver(dev->name); + esp_himem_free(dev->mem_handle); + kmm_free(dev); + return ret; + } + + return 0; +} + +int himem_chardev_unregister(char *name) +{ + int ret = 0; + int successed = 0; + struct himem_chardev_s *dev; + struct himem_chardev_s *tmp; + nxmutex_lock(&lock); + list_for_every_entry_safe(&g_himem_chardev_list, dev, + tmp, struct himem_chardev_s, node) + { + if (strcmp(name, dev->name) == 0) + { + if (g_ram_offset != HIMEM_UNMAPPED) + { + /* driver is mapping some page */ + + ret = esp_himem_unmap(g_range_handle, + g_himem_ptr, ESP_HIMEM_BLKSZ); + if (ret != 0) + { + merr("Failed to unmap himem.\n"); + successed = -1; + } + + g_ram_offset = HIMEM_UNMAPPED; + g_mapped_filep = NULL; + } + + ret = esp_himem_free(dev->mem_handle); + if (ret != 0) + { + merr("Failed to free himem.\n"); + successed = -1; + } + + ret = unregister_driver(dev->name); + if (ret != 0) + { + merr("Failed to unregister driver. dev=%s\n", dev->name); + successed = -1; + } + + list_delete(&dev->node); + kmm_free(dev); + nxmutex_unlock(&lock); + return successed; + } + } + + nxmutex_unlock(&lock); + merr("dev=%s is not registerd.\n", name); + + return -1; +}