summaryrefslogtreecommitdiff
path: root/snd-alpx-dkms/snd-alpx/alpx_mtd.c
diff options
context:
space:
mode:
Diffstat (limited to 'snd-alpx-dkms/snd-alpx/alpx_mtd.c')
-rw-r--r--snd-alpx-dkms/snd-alpx/alpx_mtd.c318
1 files changed, 318 insertions, 0 deletions
diff --git a/snd-alpx-dkms/snd-alpx/alpx_mtd.c b/snd-alpx-dkms/snd-alpx/alpx_mtd.c
new file mode 100644
index 0000000..d0ddd52
--- /dev/null
+++ b/snd-alpx-dkms/snd-alpx/alpx_mtd.c
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+* Support for Digigram AlpX PCI-e boards
+*
+* Copyright (c) 2024 Digigram Digital (info@digigram.com)
+*/
+
+#include "alpx_mtd.h"
+#include "alpx.h"
+#include "alpx_reg.h"
+
+static const u32 ALPX_FLASH_ERASED_AREA_VALUE = 0xFFFFFFFF;
+
+
+int alpx_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ struct alpx_device *alpx_dev = mtd->priv;
+ size_t size;
+ u32 page_index;
+ u32 offset;
+ u32 cmd;
+ int ret;
+
+ page_index = from >> ALPX_FLASH_SECTOR_SHIFT;
+ offset = from & (ALPX_FLASH_SECTOR_SIZE-1);
+
+ while (len) {
+ if ((offset + len) > ALPX_FLASH_SECTOR_SIZE)
+ size = ALPX_FLASH_SECTOR_SIZE - offset;
+ else
+ size = len;
+
+ cmd = ALP_PROC_COMMAND_MAKE_P16(
+ ALP_PROC_CMD_READ_FLASH_SECTOR, page_index);
+
+ dev_dbg(alpx_dev->dev,"Reading %zu bytes Flash sector 0x%x.\n", size, page_index);
+
+ mutex_lock(&alpx_dev->proc_mutex);
+ ret = alpx_proc_cmd(alpx_dev, cmd);
+ if (ret) {
+ mutex_unlock(&alpx_dev->proc_mutex);
+ dev_err(alpx_dev->dev,
+ "cmd(CMD_READ_FLASH_SECTOR, 0x%x) failed (%d)\n",
+ page_index, ret);
+
+ return ret;
+ }
+
+ memcpy_fromio(buf, ALPX_AREA(alpx_dev, ALP, SHARED) + offset,
+ size);
+ mutex_unlock(&alpx_dev->proc_mutex);
+
+ if (retlen)
+ *retlen += size;
+ buf += size;
+ len -= size;
+ offset = 0;
+ page_index++;
+ }
+ return 0;
+}
+
+int alpx_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ struct alpx_device *alpx_dev = mtd->priv;
+ size_t size;
+ u32 page_index;
+ u32 offset;
+ u32 cmd;
+ int ret;
+
+ page_index = to >> ALPX_FLASH_SECTOR_SHIFT;
+ offset = to & (ALPX_FLASH_SECTOR_SIZE-1);
+
+ while (len) {
+ if ((offset + len) > ALPX_FLASH_SECTOR_SIZE)
+ size = ALPX_FLASH_SECTOR_SIZE - offset;
+ else
+ size = len;
+
+ dev_dbg(alpx_dev->dev,"Writing %zu bytes to sector 0x%x.\n", size, page_index);
+
+ mutex_lock(&alpx_dev->proc_mutex);
+ if (size != ALPX_FLASH_SECTOR_SIZE) {
+ /* Partial page write -> read modify write*/
+ cmd = ALP_PROC_COMMAND_MAKE_P16(
+ ALP_PROC_CMD_READ_FLASH_SECTOR, page_index);
+
+ ret = alpx_proc_cmd(alpx_dev, cmd);
+ if (ret) {
+ mutex_unlock(&alpx_dev->proc_mutex);
+ dev_err(alpx_dev->dev,
+ "cmd(CMD_READ_FLASH_SECTOR, 0x%x) failed (%d)\n",
+ page_index, ret);
+ return ret;
+ }
+ }
+
+ memcpy_toio(ALPX_AREA(alpx_dev, ALP, SHARED) + offset, buf,
+ size);
+
+ cmd = ALP_PROC_COMMAND_MAKE_P16(
+ ALP_PROC_CMD_WRITE_FLASH_SECTOR, page_index);
+
+ ret = alpx_proc_cmd(alpx_dev, cmd);
+ if (ret) {
+ mutex_unlock(&alpx_dev->proc_mutex);
+ dev_err(alpx_dev->dev,
+ "cmd(CMD_WRITE_FLASH_SECTOR, 0x%x) failed (%d)\n",
+ page_index, ret);
+ return ret;
+ }
+ mutex_unlock(&alpx_dev->proc_mutex);
+
+ if (retlen)
+ *retlen += size;
+ buf += size;
+ len -= size;
+ offset = 0;
+ page_index++;
+ }
+ return 0;
+}
+
+int alpx_mtd_probe(struct alpx_device *alpx_dev, const struct mtd_partition* partitions, u32 partitions_size)
+{
+ struct mtd_info *mtd = &alpx_dev->mtd_info;
+
+ /* Setup the MTD structure */
+ mtd->type = MTD_RAM;
+ mtd->flags = MTD_WRITEABLE | MTD_NO_ERASE;
+ mtd->size = ALPX_FLASH_CHIP_SIZE;
+ mtd->writesize = ALPX_FLASH_SECTOR_SIZE;
+ mtd->writebufsize = mtd->writesize;
+ mtd->priv = alpx_dev;
+
+ mtd->owner = THIS_MODULE;
+ mtd->dev.parent = alpx_dev->dev;
+ mtd->_read = alpx_mtd_read;
+ mtd->_write = alpx_mtd_write;
+
+ return mtd_device_register(mtd, partitions,
+ partitions_size);
+}
+
+int alpx_mtd_remove(struct alpx_device *alpx_dev)
+{
+ return alpx_dev->variant->flash_partitions.qty ? mtd_device_unregister(&alpx_dev->mtd_info) : 0;
+}
+
+/* Read one Flash's page into the shared area NO LOCK DONE !!*/
+static int alpx_mtd_load_one_page_into_shared_area(struct alpx_device* alpx_dev, u32 from)
+{
+ u32 page_index = from >> ALPX_FLASH_SECTOR_SHIFT;
+ u32 cmd = ALP_PROC_COMMAND_MAKE_P16(
+ ALP_PROC_CMD_READ_FLASH_SECTOR, page_index);
+ size_t size = ALPX_FLASH_SECTOR_SIZE;
+
+ int ret;
+
+ /* Check requested length below FLASH sector's size, TODO check page_index too TODO check form is aligned on page */
+ if (from % ALPX_FLASH_SECTOR_SIZE != 0)
+ return -EINVAL;
+
+ dev_dbg(alpx_dev->dev,"Reading %zu bytes Flash sector 0x%x (0x%x).\n", size, page_index, from);
+
+
+ ret = alpx_proc_cmd(alpx_dev, cmd);
+ if (ret) {
+ dev_err(alpx_dev->dev,
+ "cmd(CMD_READ_FLASH_SECTOR, 0x%x) failed (%d)\n",
+ page_index, ret);
+ return ret;
+ }
+ print_hex_dump_bytes("LOADED Shared area :", DUMP_PREFIX_NONE, ALPX_AREA(alpx_dev, ALP, SHARED), 32);
+ return ret;
+}
+
+/* Store the shared area into the Flash at the to address. NO LOCK DONE */
+static int alpx_mtd_store_one_page_into_shared_area(struct alpx_device* alpx_dev, u32 to)
+{
+ u32 page_index = to >> ALPX_FLASH_SECTOR_SHIFT;
+ u32 cmd = ALP_PROC_COMMAND_MAKE_P16(
+ ALP_PROC_CMD_WRITE_FLASH_SECTOR, page_index);
+ size_t size = ALPX_FLASH_SECTOR_SIZE;
+
+ int ret;
+
+ print_hex_dump_bytes("STORED Shared area :", DUMP_PREFIX_NONE, ALPX_AREA(alpx_dev, ALP, SHARED), 32);
+
+ /* Check requested length below FLASH sector's size, TODO check page_index too TODO check form is aligned on page */
+ if (to % ALPX_FLASH_SECTOR_SIZE != 0)
+ return -EINVAL;
+
+ dev_dbg(alpx_dev->dev,"Storing %zu bytes to Flash sector 0x%x.\n", size, page_index);
+
+ ret = alpx_proc_cmd(alpx_dev, cmd);
+ if (ret) {
+ dev_err(alpx_dev->dev,
+ "cmd(ALP_PROC_CMD_WRITE_FLASH_SECTOR, 0x%x) failed (%d)\n",
+ page_index, ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+int alpx_mtd_is_golden_prod_area_valid(struct alpx_device* alpx_dev)
+{
+ int ret = 0;
+
+ mutex_lock(&alpx_dev->proc_mutex);
+ ret = alpx_mtd_load_one_page_into_shared_area(alpx_dev, alpx_dev->variant->flash_golden_production_base);
+
+ if (!ret) {
+ //Now check Production area validity per se : something set in and not in erased state
+ ret = (*(u32*)ALPX_AREA(alpx_dev, ALP, SHARED)) != ALPX_FLASH_ERASED_AREA_VALUE;
+ }
+ else {
+ dev_err(alpx_dev->dev, "Error %d when loading shared area from flash.\n", ret);
+ }
+ mutex_unlock(&alpx_dev->proc_mutex);
+ return ret;
+}
+
+int alpx_mtd_replicate_prod_area(struct alpx_device* alpx_dev)
+{
+ /* Read the USER's Production area and then write it to the GOLDEN'sProduction area */
+ //Read User
+
+ int ret = 0;
+ mutex_lock(&alpx_dev->proc_mutex);
+
+ ret= alpx_mtd_load_one_page_into_shared_area(alpx_dev,
+ alpx_dev->variant->flash_partitions.partitions[ALPX_FLASH_PARTITION_PRODUCTION_ID].offset);
+ if (!ret) {
+ //Write it to the GOLDEN's area
+ ret = alpx_mtd_store_one_page_into_shared_area(alpx_dev, alpx_dev->variant->flash_golden_production_base);
+ if (ret) {
+ dev_err(alpx_dev->dev,"Error %d when storing the USER's Production area into GOLDEN.\n", ret);
+ }
+ }
+ else {
+ dev_err(alpx_dev->dev,"Error %d when reading the USER's Production area.\n", ret);
+ }
+
+ mutex_unlock(&alpx_dev->proc_mutex);
+ return ret;
+}
+
+int alpx_mtd_read_from(struct alpx_device* alpx_dev,
+ uint32_t from,
+ unsigned char* to,
+ unsigned int length)
+{
+ int ret = 0;
+
+ //Check : never read more than SHARE area length
+ if (length > ALP_SHARED_SIZE)
+ return -EINVAL;
+
+ mutex_lock(&alpx_dev->proc_mutex);
+ //Load the PRODUCTION into the shared AREA, then extract the Serial Number
+ ret= alpx_mtd_load_one_page_into_shared_area(alpx_dev, from);
+
+ if (!ret) {
+ unsigned int idx = 0;
+ //NO memcpy() when dealing with SHARED Area
+ for (idx = 0 ; idx < length; ++idx) {
+ to[idx] = *(((unsigned char*)ALPX_AREA(alpx_dev, ALP, SHARED))+idx);
+ }
+ print_hex_dump_bytes("READ:", DUMP_PREFIX_NONE, to, length);
+ } else {
+ dev_err(alpx_dev->dev, " Error 0x%x when reading Flash memory at %d.\n", ret, from);
+ }
+
+ mutex_unlock(&alpx_dev->proc_mutex);
+
+ return ret;
+}
+
+int alpx_mtd_load_shared_from(struct alpx_device* alpx_dev, uint32_t from)
+{
+ //Only one page at the moment, must be ALP_SHARED_SIZE bytes after.
+ return alpx_mtd_load_one_page_into_shared_area(alpx_dev, from);
+}
+
+int alpx_mtd_read_shared(struct alpx_device* alpx_dev,
+ uint32_t at,
+ unsigned char* to,
+ unsigned int length)
+{
+ unsigned int idx;
+ //NO memcpy() when dealing with SHARED Area
+ for (idx = 0 ; idx < length; ++idx) {
+ to[idx] = *(((unsigned char*)ALPX_AREA(alpx_dev, ALP, SHARED))+idx + at);
+ }
+ print_hex_dump_bytes("READ:", DUMP_PREFIX_NONE, to, length);
+ return 0;
+}
+
+int alpx_mtd_clean_shared(struct alpx_device* alpx_dev)
+{
+ unsigned int idx;
+ //NO memcpy() when dealing with SHARED Area
+ for (idx = 0 ; idx < (ALPX_FLASH_SECTOR_SIZE / sizeof(unsigned int)); ++idx) {
+ *(unsigned int*)(ALPX_AREA(alpx_dev, ALP, SHARED)+idx) = 0;
+ }
+ return 0;
+}
+
+int alpx_mtd_is_available(struct alpx_device* alpx_dev)
+{
+ //DANTE : no MTD available yet
+ return alpx_dev->variant->model != ALPX_VARIANT_MODEL_ALPDANTE;
+}