mirror of
https://github.com/armbian/build.git
synced 2025-09-19 04:31:38 +02:00
879 lines
21 KiB
Diff
879 lines
21 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Patrick Yavitz <pyavitz@armbian.com>
|
|
Date: Fri, 21 Jun 2024 11:54:06 -0400
|
|
Subject: add spacemit patch set
|
|
|
|
source: https://gitee.com/bianbu-linux/linux-6.1
|
|
|
|
Signed-off-by: Patrick Yavitz <pyavitz@armbian.com>
|
|
---
|
|
drivers/misc/Kconfig | 7 +++++++
|
|
drivers/misc/Makefile | 1 +
|
|
2 files changed, 8 insertions(+)
|
|
|
|
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
|
|
index 111111111111..222222222222 100644
|
|
--- a/drivers/misc/Kconfig
|
|
+++ b/drivers/misc/Kconfig
|
|
@@ -497,6 +497,13 @@ config VCPU_STALL_DETECTOR
|
|
|
|
If you do not intend to run this kernel as a guest, say N.
|
|
|
|
+config SPACEMIT_TCM
|
|
+ bool "Generic tcm alloc management driver"
|
|
+ depends on SOC_SPACEMIT
|
|
+ default y
|
|
+ help
|
|
+ This driver allows you to alloc tcm for userspace.
|
|
+
|
|
source "drivers/misc/c2port/Kconfig"
|
|
source "drivers/misc/eeprom/Kconfig"
|
|
source "drivers/misc/cb710/Kconfig"
|
|
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
|
|
index 111111111111..222222222222 100644
|
|
--- a/drivers/misc/Makefile
|
|
+++ b/drivers/misc/Makefile
|
|
@@ -62,3 +62,4 @@ obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o
|
|
obj-$(CONFIG_OPEN_DICE) += open-dice.o
|
|
obj-$(CONFIG_GP_PCI1XXXX) += mchp_pci1xxxx/
|
|
obj-$(CONFIG_VCPU_STALL_DETECTOR) += vcpu_stall_detector.o
|
|
+obj-$(CONFIG_SPACEMIT_TCM) += tcm.o
|
|
--
|
|
Armbian
|
|
|
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Patrick Yavitz <pyavitz@xxxxx.com>
|
|
Date: Sat, 22 Jun 2024 08:06:33 -0400
|
|
Subject: drivers: misc: eeprom: at24.c
|
|
|
|
Signed-off-by: Patrick Yavitz <pyavitz@xxxxx.com>
|
|
---
|
|
drivers/misc/eeprom/at24.c | 49 +-
|
|
drivers/misc/tcm.c | 674 ++++++++++
|
|
2 files changed, 693 insertions(+), 30 deletions(-)
|
|
|
|
diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c
|
|
index 111111111111..222222222222 100644
|
|
--- a/drivers/misc/eeprom/at24.c
|
|
+++ b/drivers/misc/eeprom/at24.c
|
|
@@ -433,7 +433,8 @@ static int at24_read(void *priv, unsigned int off, void *val, size_t count)
|
|
|
|
ret = pm_runtime_get_sync(dev);
|
|
if (ret < 0) {
|
|
- pm_runtime_put_noidle(dev);
|
|
+ pm_runtime_mark_last_busy(dev);
|
|
+ pm_runtime_put_autosuspend(dev);
|
|
return ret;
|
|
}
|
|
|
|
@@ -447,14 +448,16 @@ static int at24_read(void *priv, unsigned int off, void *val, size_t count)
|
|
ret = at24_regmap_read(at24, buf + i, off + i, count);
|
|
if (ret < 0) {
|
|
mutex_unlock(&at24->lock);
|
|
- pm_runtime_put(dev);
|
|
+ pm_runtime_mark_last_busy(dev);
|
|
+ pm_runtime_put_autosuspend(dev);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
mutex_unlock(&at24->lock);
|
|
|
|
- pm_runtime_put(dev);
|
|
+ pm_runtime_mark_last_busy(dev);
|
|
+ pm_runtime_put_autosuspend(dev);
|
|
|
|
if (unlikely(at24->read_post))
|
|
at24->read_post(off, buf, i);
|
|
@@ -480,7 +483,8 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count)
|
|
|
|
ret = pm_runtime_get_sync(dev);
|
|
if (ret < 0) {
|
|
- pm_runtime_put_noidle(dev);
|
|
+ pm_runtime_mark_last_busy(dev);
|
|
+ pm_runtime_put_autosuspend(dev);
|
|
return ret;
|
|
}
|
|
|
|
@@ -494,7 +498,8 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count)
|
|
ret = at24_regmap_write(at24, buf, off, count);
|
|
if (ret < 0) {
|
|
mutex_unlock(&at24->lock);
|
|
- pm_runtime_put(dev);
|
|
+ pm_runtime_mark_last_busy(dev);
|
|
+ pm_runtime_put_autosuspend(dev);
|
|
return ret;
|
|
}
|
|
buf += ret;
|
|
@@ -504,7 +509,8 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count)
|
|
|
|
mutex_unlock(&at24->lock);
|
|
|
|
- pm_runtime_put(dev);
|
|
+ pm_runtime_mark_last_busy(dev);
|
|
+ pm_runtime_put_autosuspend(dev);
|
|
|
|
return 0;
|
|
}
|
|
@@ -716,7 +722,6 @@ static int at24_probe(struct i2c_client *client)
|
|
at24->offset_adj = at24_get_offset_adj(flags, byte_len);
|
|
at24->client_regmaps[0] = regmap;
|
|
|
|
- at24->vcc_reg = devm_regulator_get(dev, "vcc");
|
|
if (IS_ERR(at24->vcc_reg))
|
|
return PTR_ERR(at24->vcc_reg);
|
|
|
|
@@ -769,17 +774,10 @@ static int at24_probe(struct i2c_client *client)
|
|
|
|
i2c_set_clientdata(client, at24);
|
|
|
|
- full_power = acpi_dev_state_d0(&client->dev);
|
|
- if (full_power) {
|
|
- err = regulator_enable(at24->vcc_reg);
|
|
- if (err) {
|
|
- dev_err(dev, "Failed to enable vcc regulator\n");
|
|
- return err;
|
|
- }
|
|
-
|
|
- pm_runtime_set_active(dev);
|
|
- }
|
|
+ pm_runtime_use_autosuspend(dev);
|
|
+ pm_runtime_set_autosuspend_delay(dev, 10);
|
|
pm_runtime_enable(dev);
|
|
+ pm_runtime_get_sync(dev);
|
|
|
|
/*
|
|
* Perform a one-byte test read to verify that the chip is functional,
|
|
@@ -790,8 +788,6 @@ static int at24_probe(struct i2c_client *client)
|
|
err = at24_read(at24, 0, &test_byte, 1);
|
|
if (err) {
|
|
pm_runtime_disable(dev);
|
|
- if (!pm_runtime_status_suspended(dev))
|
|
- regulator_disable(at24->vcc_reg);
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
@@ -799,8 +795,6 @@ static int at24_probe(struct i2c_client *client)
|
|
at24->nvmem = devm_nvmem_register(dev, &nvmem_config);
|
|
if (IS_ERR(at24->nvmem)) {
|
|
pm_runtime_disable(dev);
|
|
- if (!pm_runtime_status_suspended(dev))
|
|
- regulator_disable(at24->vcc_reg);
|
|
return dev_err_probe(dev, PTR_ERR(at24->nvmem),
|
|
"failed to register nvmem\n");
|
|
}
|
|
@@ -809,7 +803,8 @@ static int at24_probe(struct i2c_client *client)
|
|
if (cdata == &at24_data_spd)
|
|
at24_probe_temp_sensor(client);
|
|
|
|
- pm_runtime_idle(dev);
|
|
+ pm_runtime_mark_last_busy(dev);
|
|
+ pm_runtime_put_autosuspend(dev);
|
|
|
|
if (writable)
|
|
dev_info(dev, "%u byte %s EEPROM, writable, %u bytes/write\n",
|
|
@@ -835,18 +830,12 @@ static void at24_remove(struct i2c_client *client)
|
|
|
|
static int __maybe_unused at24_suspend(struct device *dev)
|
|
{
|
|
- struct i2c_client *client = to_i2c_client(dev);
|
|
- struct at24_data *at24 = i2c_get_clientdata(client);
|
|
-
|
|
- return regulator_disable(at24->vcc_reg);
|
|
+ return 0;
|
|
}
|
|
|
|
static int __maybe_unused at24_resume(struct device *dev)
|
|
{
|
|
- struct i2c_client *client = to_i2c_client(dev);
|
|
- struct at24_data *at24 = i2c_get_clientdata(client);
|
|
-
|
|
- return regulator_enable(at24->vcc_reg);
|
|
+ return 0;
|
|
}
|
|
|
|
static const struct dev_pm_ops at24_pm_ops = {
|
|
diff --git a/drivers/misc/tcm.c b/drivers/misc/tcm.c
|
|
new file mode 100644
|
|
index 000000000000..111111111111
|
|
--- /dev/null
|
|
+++ b/drivers/misc/tcm.c
|
|
@@ -0,0 +1,674 @@
|
|
+#include <linux/of_address.h>
|
|
+#include <linux/of_device.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/fs.h>
|
|
+#include <linux/mm.h>
|
|
+#include <linux/miscdevice.h>
|
|
+#include <linux/uaccess.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/mutex.h>
|
|
+#include <linux/list_sort.h>
|
|
+#include <linux/poll.h>
|
|
+#include <linux/compat.h>
|
|
+#include <linux/random.h>
|
|
+
|
|
+#define TCM_NAME "tcm"
|
|
+
|
|
+#define IOC_MAGIC 'c'
|
|
+#define TCM_MEM_SHOW _IOR(IOC_MAGIC, 2, int)
|
|
+#define TCM_VA_TO_PA _IOR(IOC_MAGIC, 4, int)
|
|
+#define TCM_REQUEST_MEM _IOR(IOC_MAGIC, 5, int)
|
|
+#define TCM_RELEASE_MEM _IOR(IOC_MAGIC, 6, int)
|
|
+
|
|
+#define MM_MIN_SHIFT (PAGE_SHIFT) /* 16 bytes */
|
|
+#define MM_MIN_CHUNK (1 << MM_MIN_SHIFT)
|
|
+#define MM_GRAN_MASK (MM_MIN_CHUNK-1)
|
|
+#define MM_ALIGN_UP(a) (((a) + MM_GRAN_MASK) & ~MM_GRAN_MASK)
|
|
+#define MM_ALIGN_DOWN(a) ((a) & ~MM_GRAN_MASK)
|
|
+#define MM_ALLOC_BIT (0x80000000)
|
|
+
|
|
+typedef struct {
|
|
+ size_t vaddr;
|
|
+ size_t size;
|
|
+} block_t;
|
|
+
|
|
+typedef struct {
|
|
+ size_t addr_base;
|
|
+ int block_num;
|
|
+ block_t *block;
|
|
+ struct device *dev;
|
|
+ struct mutex mutex;
|
|
+ wait_queue_head_t wait;
|
|
+ struct list_head req_head;
|
|
+} tcm_t;
|
|
+
|
|
+typedef struct {
|
|
+ struct list_head list;
|
|
+ uintptr_t paddr; /* Phy addr of memory */
|
|
+ size_t size; /* Size of this chunk */
|
|
+ uintptr_t next_paddr;
|
|
+ int block_id;
|
|
+ void *caller;
|
|
+} mm_node_t;
|
|
+
|
|
+typedef struct {
|
|
+ struct list_head head;
|
|
+ size_t max_size;
|
|
+} list_manager_t;
|
|
+
|
|
+typedef struct {
|
|
+ size_t mm_heapsize;
|
|
+ size_t free_size;
|
|
+ uintptr_t start;
|
|
+ uintptr_t end;
|
|
+ list_manager_t free;
|
|
+ list_manager_t alloc;
|
|
+} mm_heap_t;
|
|
+
|
|
+typedef struct {
|
|
+ struct list_head list;
|
|
+ phys_addr_t paddr;
|
|
+ size_t size;
|
|
+} mm_alloc_node_t;
|
|
+
|
|
+typedef struct {
|
|
+ struct list_head list;
|
|
+ int pid;
|
|
+ uint32_t rand_id;
|
|
+ size_t req_size;
|
|
+ int timeout;
|
|
+} request_mem_t;
|
|
+
|
|
+typedef struct {
|
|
+ void *vaddr;
|
|
+ void *paddr;
|
|
+} va_to_pa_msg_t;
|
|
+
|
|
+static tcm_t tcm;
|
|
+static mm_heap_t *g_mmheap;
|
|
+static int g_block_num;
|
|
+
|
|
+static void add_node(mm_heap_t *heap, list_manager_t *list, mm_node_t *node, char *tip)
|
|
+{
|
|
+ mm_node_t *cur;
|
|
+
|
|
+ node->next_paddr = node->paddr + node->size;
|
|
+ if (list_empty(&list->head)) {
|
|
+ list_add(&node->list, &list->head);
|
|
+ dev_dbg(tcm.dev, "[%s] add first node:%lx addr:%lx len:%lx\n", tip, (uintptr_t)node, (uintptr_t)node->paddr, node->size);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ list_for_each_entry(cur, &list->head, list) {
|
|
+ if ((size_t)cur->paddr > (size_t)node->paddr ) {
|
|
+ list_add_tail(&node->list, &cur->list);
|
|
+ dev_dbg(tcm.dev, "[%s] add node:%lx addr:%lx len:%lx\n", tip, (uintptr_t)node, (uintptr_t)node->paddr, node->size);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ dev_dbg(tcm.dev, "[%s] add tail node:%lx addr:%lx len:%lx\n", tip, (uintptr_t)node, (uintptr_t)node->paddr, node->size);
|
|
+
|
|
+ list_add_tail(&node->list, &list->head);
|
|
+}
|
|
+
|
|
+static void add_free_node(mm_heap_t *heap, mm_node_t *node)
|
|
+{
|
|
+ heap->free_size += node->size;
|
|
+ add_node(heap, &heap->free, node, "free");
|
|
+}
|
|
+
|
|
+static void del_free_node(mm_heap_t *heap, mm_node_t *node)
|
|
+{
|
|
+ heap->free_size -= node->size;
|
|
+ list_del(&node->list);
|
|
+}
|
|
+
|
|
+static void add_alloc_node(mm_heap_t *heap, mm_node_t *node)
|
|
+{
|
|
+ add_node(heap, &heap->alloc, node, "alloc");
|
|
+}
|
|
+
|
|
+static void del_alloc_node(mm_heap_t *heap, mm_node_t *node)
|
|
+{
|
|
+ list_del(&node->list);
|
|
+}
|
|
+
|
|
+static void mm_addregion(mm_heap_t *heap, void *heapstart, size_t heapsize)
|
|
+{
|
|
+ mm_node_t *node;
|
|
+ uintptr_t heapbase;
|
|
+ uintptr_t heapend;
|
|
+
|
|
+ heapbase = MM_ALIGN_UP((uintptr_t)heapstart);
|
|
+ heapend = MM_ALIGN_DOWN((uintptr_t)heapstart + (uintptr_t)heapsize);
|
|
+ heapsize = heapend - heapbase;
|
|
+
|
|
+ heap->mm_heapsize += heapsize;
|
|
+ heap->start = heapbase;
|
|
+ heap->end = heapend;
|
|
+
|
|
+ node = (mm_node_t *)(kmalloc(sizeof(mm_node_t), GFP_KERNEL));
|
|
+ node->paddr = heapbase;
|
|
+ node->size = heapsize;
|
|
+
|
|
+ add_free_node(heap, node);
|
|
+ dev_dbg(tcm.dev, "mm init(start:0x%lx)(len:0x%lx)\n", heapbase, heapsize);
|
|
+}
|
|
+
|
|
+static mm_node_t *get_free_max_node(mm_heap_t *heap, size_t size)
|
|
+{
|
|
+ mm_node_t *node, *max_node;
|
|
+
|
|
+ max_node = list_first_entry(&heap->free.head, mm_node_t, list);
|
|
+ list_for_each_entry(node, &heap->free.head, list) {
|
|
+ if (node->size >= size) {
|
|
+ max_node = node;
|
|
+ break;
|
|
+ }
|
|
+ if (node->size >= max_node->size) {
|
|
+ max_node = node;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return max_node;
|
|
+}
|
|
+
|
|
+static int node_fission(mm_heap_t *heap, mm_node_t *node, size_t size)
|
|
+{
|
|
+ size_t remaining = node->size - size;
|
|
+
|
|
+ dev_dbg(tcm.dev, "remaining size:%lx\n", remaining);
|
|
+ if (remaining > 0) {
|
|
+ mm_node_t *remainder = (mm_node_t *)(kmalloc(sizeof(mm_node_t), GFP_KERNEL));
|
|
+ if (!remainder) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ remainder->size = remaining;
|
|
+ remainder->paddr = node->paddr + size;
|
|
+ node->size = size;
|
|
+
|
|
+ add_free_node(heap, remainder);
|
|
+ }
|
|
+
|
|
+ del_free_node(heap, node);
|
|
+ add_alloc_node(heap, node);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void *mm_max_mallc(mm_heap_t *heap, size_t size, size_t *valid_size)
|
|
+{
|
|
+ mm_node_t *node;
|
|
+
|
|
+ size = MM_ALIGN_UP(size);
|
|
+
|
|
+ node = get_free_max_node(heap, size);
|
|
+
|
|
+ dev_dbg(tcm.dev, "\n%s node:(%lx)(%lx)(%lx)\n", __func__, (uintptr_t)node, (uintptr_t)node->paddr, node->size);
|
|
+
|
|
+ if (size <= node->size) {
|
|
+ node_fission(heap, node, size);
|
|
+ *valid_size = size;
|
|
+ } else {
|
|
+ node_fission(heap, node, node->size);
|
|
+ *valid_size = node->size;
|
|
+ }
|
|
+
|
|
+ return (void *)node->paddr;
|
|
+}
|
|
+
|
|
+static mm_node_t *get_node_by_ptr(mm_heap_t *heap, void *mem)
|
|
+{
|
|
+ mm_node_t *node;
|
|
+
|
|
+ list_for_each_entry(node, &heap->alloc.head, list) {
|
|
+ if ((size_t)node->paddr == (size_t)mem) {
|
|
+ return node;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static void mm_free(mm_heap_t *heap, void *mem)
|
|
+{
|
|
+ mm_node_t *cur, *next, *node;
|
|
+ int gc_flag = 0;
|
|
+
|
|
+ node = get_node_by_ptr(heap, mem);
|
|
+ if (!node) return;
|
|
+
|
|
+ dev_dbg(tcm.dev, "%s node:(%lx)(%lx)(%lx)\n", __func__, (uintptr_t)node, (uintptr_t)node->paddr, node->size);
|
|
+
|
|
+ del_alloc_node(heap, node);
|
|
+
|
|
+ list_for_each_entry_safe(cur, next, &heap->free.head, list) {
|
|
+ if (cur->next_paddr == (size_t)node->paddr) {
|
|
+ cur->size += node->size;
|
|
+ gc_flag |= 1;
|
|
+
|
|
+ dev_dbg(tcm.dev, "gc prev succful(%lx)(%lx)(%lx)\n", (uintptr_t)cur, (uintptr_t)cur->paddr, cur->size);
|
|
+ if (!list_is_last(&cur->list, &heap->free.head)) {
|
|
+ if (cur->next_paddr == (size_t)next->paddr) {
|
|
+ cur->size += next->size;
|
|
+ gc_flag |= 2;
|
|
+ dev_dbg(tcm.dev, "gc 2 next succful(%lx)(%lx)(%lx)\n", (uintptr_t)cur, (uintptr_t)cur->paddr, cur->size);
|
|
+ list_del(&next->list);
|
|
+ kfree(next);
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (node->next_paddr == (size_t)cur->paddr) {
|
|
+ cur->paddr = node->paddr;
|
|
+ cur->size += node->size;
|
|
+ gc_flag |= 2;
|
|
+ dev_dbg(tcm.dev, "gc next succful(%lx)(%lx)(%lx)\n", (uintptr_t)cur, (uintptr_t)cur->paddr, cur->size);
|
|
+ break;;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (gc_flag == 0) {
|
|
+ add_free_node(heap, node);
|
|
+ } else {
|
|
+ kfree(node);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void mm_show(mm_heap_t *heap)
|
|
+{
|
|
+ mm_node_t *node;
|
|
+ int i = 0;
|
|
+
|
|
+ printk("%s start\n", __func__);
|
|
+ list_for_each_entry(node, &heap->free.head, list) {
|
|
+ printk("mem free node[%d]: %lx paddr: %lx size:0x%lx\n",
|
|
+ i ++, (uintptr_t)node, (uintptr_t)node->paddr, node->size);
|
|
+ }
|
|
+
|
|
+ i = 0;
|
|
+ list_for_each_entry(node, &heap->alloc.head, list) {
|
|
+ printk("mem alloc node[%d]: %lx paddr: %lx size:0x%lx\n",
|
|
+ i ++, (uintptr_t)node, (uintptr_t)node->paddr, node->size);
|
|
+ }
|
|
+
|
|
+ printk("%s end\n", __func__);
|
|
+}
|
|
+
|
|
+static int get_id(uintptr_t ptr)
|
|
+{
|
|
+ int i;
|
|
+ for (i = 0; i < g_block_num; i++) {
|
|
+ if (ptr >= g_mmheap[i].start && ptr < g_mmheap[i].end){
|
|
+ return i;
|
|
+ }
|
|
+ }
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static void tcm_free(void *ptr)
|
|
+{
|
|
+ int id = get_id((uintptr_t)ptr);
|
|
+ if (id < 0) {
|
|
+ return;
|
|
+ }
|
|
+ mm_free(&g_mmheap[id], ptr);
|
|
+}
|
|
+
|
|
+static size_t total_free_size(void)
|
|
+{
|
|
+ int i;
|
|
+ size_t total = 0;
|
|
+
|
|
+ for (i = 0; i < g_block_num; i++) {
|
|
+ total += g_mmheap[i].free_size;
|
|
+ }
|
|
+
|
|
+ return total;
|
|
+}
|
|
+
|
|
+static struct list_head *tcm_discontinuous_malloc(size_t size)
|
|
+{
|
|
+ struct list_head *head;
|
|
+ int i, remain;
|
|
+ size_t total;
|
|
+
|
|
+ total = total_free_size();
|
|
+ if (total < size) return NULL;
|
|
+
|
|
+ head = kmalloc(sizeof(struct list_head), GFP_KERNEL);
|
|
+ if (!head) return NULL;
|
|
+
|
|
+ INIT_LIST_HEAD(head);
|
|
+ remain = size;
|
|
+
|
|
+ for (i = 0; i < g_block_num; i++) {
|
|
+ while (g_mmheap[i].free_size) {
|
|
+ mm_alloc_node_t *alloc = kmalloc(sizeof(mm_alloc_node_t), GFP_KERNEL);
|
|
+ alloc->paddr = (phys_addr_t)mm_max_mallc(&g_mmheap[i], remain, &alloc->size);
|
|
+ list_add(&alloc->list, head);
|
|
+ remain -= alloc->size;
|
|
+ if (remain <= 0) {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (remain <= 0) {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return head;
|
|
+}
|
|
+
|
|
+static int mm_init(mm_heap_t *heap, size_t start, size_t end)
|
|
+{
|
|
+ memset(heap, 0, sizeof(mm_heap_t));
|
|
+
|
|
+ INIT_LIST_HEAD(&heap->free.head);
|
|
+ INIT_LIST_HEAD(&heap->alloc.head);
|
|
+
|
|
+ mm_addregion(heap, (void *)start, end - start);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void *tcm_match_pa(unsigned long vaddr)
|
|
+{
|
|
+ // TODO
|
|
+ struct vm_area_struct *vma;
|
|
+ mm_alloc_node_t *node;
|
|
+ struct list_head *head;
|
|
+
|
|
+ vma = find_vma(current->mm, vaddr);
|
|
+ if (!vma) {
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ head = (struct list_head *)vma->vm_private_data;
|
|
+ list_for_each_entry(node, head, list) {
|
|
+ return (void *)node->paddr;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static request_mem_t *get_req_mem_node(int pid)
|
|
+{
|
|
+ request_mem_t *cur;
|
|
+
|
|
+ list_for_each_entry(cur, &tcm.req_head, list) {
|
|
+ if (pid == cur->pid) {
|
|
+ return cur;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static int del_req_mem_node(request_mem_t *node)
|
|
+{
|
|
+ mutex_lock(&tcm.mutex);
|
|
+ list_add_tail(&node->list, &tcm.req_head);
|
|
+ mutex_unlock(&tcm.mutex);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int add_req_mem_node(request_mem_t *node)
|
|
+{
|
|
+ mutex_lock(&tcm.mutex);
|
|
+ list_add_tail(&node->list, &tcm.req_head);
|
|
+ mutex_unlock(&tcm.mutex);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void tcm_vma_close(struct vm_area_struct *vma)
|
|
+{
|
|
+ mm_alloc_node_t *cur, *next;
|
|
+ struct list_head *head = (struct list_head *)vma->vm_private_data;
|
|
+
|
|
+ list_for_each_entry_safe(cur, next, head, list) {
|
|
+ tcm_free((void *)cur->paddr);
|
|
+ list_del(&cur->list);
|
|
+ kfree(cur);
|
|
+ }
|
|
+ kfree(head);
|
|
+ dev_dbg(tcm.dev, "wake up block thread");
|
|
+ wake_up_all(&tcm.wait);
|
|
+}
|
|
+
|
|
+static const struct vm_operations_struct tcm_vm_ops = {
|
|
+ .close = tcm_vma_close,
|
|
+};
|
|
+
|
|
+static int mmap_compare(void* priv, const struct list_head* a, const struct list_head* b)
|
|
+{
|
|
+ mm_alloc_node_t* da = list_entry(a, mm_alloc_node_t, list);
|
|
+ mm_alloc_node_t* db = list_entry(b, mm_alloc_node_t, list);
|
|
+
|
|
+ return ((size_t)da->paddr > (size_t)db->paddr) ? 1 : (((size_t)da->paddr < (size_t)db->paddr) ? -1 : 0);
|
|
+}
|
|
+
|
|
+static int tcm_mmap(struct file *file, struct vm_area_struct *vma)
|
|
+{
|
|
+ size_t size = vma->vm_end - vma->vm_start;
|
|
+ phys_addr_t offset = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT;
|
|
+ struct page* page = NULL;
|
|
+ unsigned long pfn;
|
|
+ unsigned long addr;
|
|
+ struct list_head *head;
|
|
+ mm_alloc_node_t *node;
|
|
+
|
|
+ /* Does it even fit in phys_addr_t? */
|
|
+ if (offset >> PAGE_SHIFT != vma->vm_pgoff)
|
|
+ return -EINVAL;
|
|
+
|
|
+ vma->vm_ops = &tcm_vm_ops;
|
|
+
|
|
+ mutex_lock(&tcm.mutex);
|
|
+ head = tcm_discontinuous_malloc(size);
|
|
+ mutex_unlock(&tcm.mutex);
|
|
+
|
|
+ if (!head) {
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ list_sort(NULL, head, mmap_compare);
|
|
+
|
|
+ vma->vm_private_data = head;
|
|
+ addr = vma->vm_start;
|
|
+
|
|
+ list_for_each_entry(node, head, list) {
|
|
+ pfn = node->paddr >> PAGE_SHIFT;
|
|
+ page = phys_to_page(node->paddr);
|
|
+ if (!page) {
|
|
+ return -ENXIO;
|
|
+ }
|
|
+ if (remap_pfn_range(vma,
|
|
+ addr,
|
|
+ pfn,
|
|
+ node->size,
|
|
+ vma->vm_page_prot)) {
|
|
+ return -EAGAIN;
|
|
+ }
|
|
+ addr += node->size;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static long tcm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
+{
|
|
+ if (cmd == TCM_MEM_SHOW) {
|
|
+ int i = 0;
|
|
+ for (; i < g_block_num; i++) {
|
|
+ printk("mem block id(%d):\n", i);
|
|
+ mm_show(&g_mmheap[i]);
|
|
+ }
|
|
+ }else if (cmd == TCM_VA_TO_PA) {
|
|
+ va_to_pa_msg_t msg;
|
|
+
|
|
+ if(copy_from_user(&msg, (void *)arg, sizeof(va_to_pa_msg_t))) {
|
|
+ return -EFAULT;
|
|
+ }
|
|
+
|
|
+ msg.paddr = tcm_match_pa((unsigned long)msg.vaddr);
|
|
+
|
|
+ if(copy_to_user((void *)arg, &msg, sizeof(va_to_pa_msg_t))) {
|
|
+ return -EFAULT;
|
|
+ }
|
|
+ } else if (cmd == TCM_REQUEST_MEM) {
|
|
+ size_t size;
|
|
+ request_mem_t *node;
|
|
+
|
|
+ if(copy_from_user(&size, (void *)arg, sizeof(size_t))) {
|
|
+ return -EFAULT;
|
|
+ }
|
|
+
|
|
+ node = kmalloc(sizeof(request_mem_t), GFP_KERNEL);
|
|
+ if (!node) return -ENOMEM;
|
|
+
|
|
+ node->pid = task_pid_nr(current);
|
|
+ node->req_size = size;
|
|
+ add_req_mem_node(node);
|
|
+ } else if (cmd == TCM_RELEASE_MEM) {
|
|
+ size_t size;
|
|
+ request_mem_t *node;
|
|
+ if(copy_from_user(&size, (void *)arg, sizeof(size_t))) {
|
|
+ return -EFAULT;
|
|
+ }
|
|
+ node = get_req_mem_node(task_pid_nr(current));
|
|
+ if (node) del_req_mem_node(node);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static unsigned int tcm_poll(struct file *file, poll_table *wait)
|
|
+{
|
|
+ __poll_t mask = 0;
|
|
+
|
|
+ request_mem_t *node = get_req_mem_node(task_pid_nr(current));
|
|
+ dev_dbg(tcm.dev, "poll get node(%lx)\n", (size_t)node);
|
|
+
|
|
+ if (node == NULL) {
|
|
+ mask = EPOLLERR;
|
|
+ } else {
|
|
+ poll_wait(file, &tcm.wait, wait);
|
|
+ if (total_free_size() >= node->req_size) {
|
|
+ mask = EPOLLIN;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return mask;
|
|
+}
|
|
+
|
|
+static const struct file_operations tcm_fops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .mmap = tcm_mmap,
|
|
+ .unlocked_ioctl = tcm_ioctl,
|
|
+ .poll = tcm_poll,
|
|
+};
|
|
+
|
|
+static struct miscdevice tcm_misc_device = {
|
|
+ .minor = MISC_DYNAMIC_MINOR,
|
|
+ .name = TCM_NAME,
|
|
+ .fops = &tcm_fops,
|
|
+};
|
|
+
|
|
+static const struct of_device_id tcm_dt_ids[] = {
|
|
+ { .compatible = "spacemit,k1-pro-tcm", .data = NULL },
|
|
+ { .compatible = "spacemit,k1-x-tcm", .data = NULL },
|
|
+ {}
|
|
+};
|
|
+
|
|
+static int tcm_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct resource *res;
|
|
+ int ret, num;
|
|
+ struct device_node *np, *child;
|
|
+
|
|
+ tcm.dev = &pdev->dev;
|
|
+
|
|
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
+ if (!res) {
|
|
+ dev_err(tcm.dev, "found no memory resource\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ np = tcm.dev->of_node;
|
|
+ num = (np) ? of_get_available_child_count(np) + 1 : 1;
|
|
+
|
|
+ dev_dbg(tcm.dev, "-----------child num:%d\n", num);
|
|
+ g_mmheap = kmalloc(sizeof(mm_heap_t)*num, GFP_KERNEL);
|
|
+ if (!g_mmheap)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ for_each_available_child_of_node(np, child) {
|
|
+ struct resource child_res;
|
|
+
|
|
+ ret = of_address_to_resource(child, 0, &child_res);
|
|
+ if (ret < 0) {
|
|
+ dev_err(tcm.dev,
|
|
+ "could not get address for node %pOF\n",
|
|
+ child);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (child_res.start < res->start || child_res.end > res->end) {
|
|
+ dev_err(tcm.dev,
|
|
+ "reserved block %pOF outside the tcm area\n",
|
|
+ child);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ mm_init(&g_mmheap[g_block_num], child_res.start , child_res.start + resource_size(&child_res));
|
|
+ g_block_num ++;
|
|
+
|
|
+ }
|
|
+
|
|
+ csr_write(0x5db, 1);
|
|
+ ret = misc_register(&tcm_misc_device);
|
|
+ if (ret) {
|
|
+ dev_err(tcm.dev, "failed to register misc device\n");
|
|
+ return ret;
|
|
+ }
|
|
+ mutex_init(&tcm.mutex);
|
|
+ init_waitqueue_head(&tcm.wait);
|
|
+ INIT_LIST_HEAD(&tcm.req_head);
|
|
+ dev_dbg(tcm.dev, "tcm register succfully\n");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int tcm_remove(struct platform_device *pdev)
|
|
+{
|
|
+ dev_dbg(tcm.dev, "tcm deregister succfully\n");
|
|
+ csr_write(0x5db, 0);
|
|
+ kfree(g_mmheap);
|
|
+ misc_deregister(&tcm_misc_device);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct platform_driver tcm_driver = {
|
|
+ .driver = {
|
|
+ .name = TCM_NAME,
|
|
+ .of_match_table = tcm_dt_ids,
|
|
+ },
|
|
+ .probe = tcm_probe,
|
|
+ .remove = tcm_remove,
|
|
+};
|
|
+
|
|
+static int __init tcm_init(void)
|
|
+{
|
|
+ return platform_driver_register(&tcm_driver);
|
|
+}
|
|
+
|
|
+module_init(tcm_init);
|
|
--
|
|
Armbian
|
|
|