blob: a4147dd3b27035fa56c0000a7fd358ad1a95846c [file] [log] [blame]
From 21a99150a566b69edc7f3b9bc369bb4525e04acd Mon Sep 17 00:00:00 2001
From: Sam Shih <sam.shih@mediatek.com>
Date: Fri, 2 Jun 2023 13:06:17 +0800
Subject: [PATCH]
[spi-and-storage][999-2350-nvmem-core-Add-functions-to-make-number-reading-easy.patch]
---
drivers/nvmem/core.c | 161 +++++++++++++++++++++++++++------
include/linux/nvmem-consumer.h | 15 +++
2 files changed, 150 insertions(+), 26 deletions(-)
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 19ac0d055..f65305b6d 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -1102,16 +1102,8 @@ int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len)
}
EXPORT_SYMBOL_GPL(nvmem_cell_write);
-/**
- * nvmem_cell_read_u16() - Read a cell value as an u16
- *
- * @dev: Device that requests the nvmem cell.
- * @cell_id: Name of nvmem cell to read.
- * @val: pointer to output value.
- *
- * Return: 0 on success or negative errno.
- */
-int nvmem_cell_read_u16(struct device *dev, const char *cell_id, u16 *val)
+static int nvmem_cell_read_common(struct device *dev, const char *cell_id,
+ void *val, size_t count)
{
struct nvmem_cell *cell;
void *buf;
@@ -1126,21 +1118,50 @@ int nvmem_cell_read_u16(struct device *dev, const char *cell_id, u16 *val)
nvmem_cell_put(cell);
return PTR_ERR(buf);
}
- if (len != sizeof(*val)) {
+ if (len != count) {
kfree(buf);
nvmem_cell_put(cell);
return -EINVAL;
}
- memcpy(val, buf, sizeof(*val));
+ memcpy(val, buf, count);
kfree(buf);
nvmem_cell_put(cell);
return 0;
}
+
+/**
+ * nvmem_cell_read_u8() - Read a cell value as a u8
+ *
+ * @dev: Device that requests the nvmem cell.
+ * @cell_id: Name of nvmem cell to read.
+ * @val: pointer to output value.
+ *
+ * Return: 0 on success or negative errno.
+ */
+int nvmem_cell_read_u8(struct device *dev, const char *cell_id, u8 *val)
+{
+ return nvmem_cell_read_common(dev, cell_id, val, sizeof(*val));
+}
+EXPORT_SYMBOL_GPL(nvmem_cell_read_u8);
+
+/**
+ * nvmem_cell_read_u16() - Read a cell value as a u16
+ *
+ * @dev: Device that requests the nvmem cell.
+ * @cell_id: Name of nvmem cell to read.
+ * @val: pointer to output value.
+ *
+ * Return: 0 on success or negative errno.
+ */
+int nvmem_cell_read_u16(struct device *dev, const char *cell_id, u16 *val)
+{
+ return nvmem_cell_read_common(dev, cell_id, val, sizeof(*val));
+}
EXPORT_SYMBOL_GPL(nvmem_cell_read_u16);
/**
- * nvmem_cell_read_u32() - Read a cell value as an u32
+ * nvmem_cell_read_u32() - Read a cell value as a u32
*
* @dev: Device that requests the nvmem cell.
* @cell_id: Name of nvmem cell to read.
@@ -1149,32 +1170,120 @@ EXPORT_SYMBOL_GPL(nvmem_cell_read_u16);
* Return: 0 on success or negative errno.
*/
int nvmem_cell_read_u32(struct device *dev, const char *cell_id, u32 *val)
+{
+ return nvmem_cell_read_common(dev, cell_id, val, sizeof(*val));
+}
+EXPORT_SYMBOL_GPL(nvmem_cell_read_u32);
+
+/**
+ * nvmem_cell_read_u64() - Read a cell value as a u64
+ *
+ * @dev: Device that requests the nvmem cell.
+ * @cell_id: Name of nvmem cell to read.
+ * @val: pointer to output value.
+ *
+ * Return: 0 on success or negative errno.
+ */
+int nvmem_cell_read_u64(struct device *dev, const char *cell_id, u64 *val)
+{
+ return nvmem_cell_read_common(dev, cell_id, val, sizeof(*val));
+}
+EXPORT_SYMBOL_GPL(nvmem_cell_read_u64);
+
+static const void *nvmem_cell_read_variable_common(struct device *dev,
+ const char *cell_id,
+ size_t max_len, size_t *len)
{
struct nvmem_cell *cell;
+ int nbits;
void *buf;
- size_t len;
cell = nvmem_cell_get(dev, cell_id);
if (IS_ERR(cell))
- return PTR_ERR(cell);
+ return cell;
- buf = nvmem_cell_read(cell, &len);
- if (IS_ERR(buf)) {
- nvmem_cell_put(cell);
- return PTR_ERR(buf);
- }
- if (len != sizeof(*val)) {
+ nbits = cell->nbits;
+ buf = nvmem_cell_read(cell, len);
+ nvmem_cell_put(cell);
+ if (IS_ERR(buf))
+ return buf;
+
+ /*
+ * If nbits is set then nvmem_cell_read() can significantly exaggerate
+ * the length of the real data. Throw away the extra junk.
+ */
+ if (nbits)
+ *len = DIV_ROUND_UP(nbits, 8);
+
+ if (*len > max_len) {
kfree(buf);
- nvmem_cell_put(cell);
- return -EINVAL;
+ return ERR_PTR(-ERANGE);
}
- memcpy(val, buf, sizeof(*val));
+
+ return buf;
+}
+
+/**
+ * nvmem_cell_read_variable_le_u32() - Read up to 32-bits of data as a little endian number.
+ *
+ * @dev: Device that requests the nvmem cell.
+ * @cell_id: Name of nvmem cell to read.
+ * @val: pointer to output value.
+ *
+ * Return: 0 on success or negative errno.
+ */
+int nvmem_cell_read_variable_le_u32(struct device *dev, const char *cell_id,
+ u32 *val)
+{
+ size_t len;
+ const u8 *buf;
+ int i;
+
+ buf = nvmem_cell_read_variable_common(dev, cell_id, sizeof(*val), &len);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ /* Copy w/ implicit endian conversion */
+ *val = 0;
+ for (i = 0; i < len; i++)
+ *val |= buf[i] << (8 * i);
kfree(buf);
- nvmem_cell_put(cell);
+
return 0;
}
-EXPORT_SYMBOL_GPL(nvmem_cell_read_u32);
+EXPORT_SYMBOL_GPL(nvmem_cell_read_variable_le_u32);
+
+/**
+ * nvmem_cell_read_variable_le_u64() - Read up to 64-bits of data as a little endian number.
+ *
+ * @dev: Device that requests the nvmem cell.
+ * @cell_id: Name of nvmem cell to read.
+ * @val: pointer to output value.
+ *
+ * Return: 0 on success or negative errno.
+ */
+int nvmem_cell_read_variable_le_u64(struct device *dev, const char *cell_id,
+ u64 *val)
+{
+ size_t len;
+ const u8 *buf;
+ int i;
+
+ buf = nvmem_cell_read_variable_common(dev, cell_id, sizeof(*val), &len);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ /* Copy w/ implicit endian conversion */
+ *val = 0;
+ for (i = 0; i < len; i++)
+ *val |= (uint64_t)buf[i] << (8 * i);
+
+ kfree(buf);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nvmem_cell_read_variable_le_u64);
/**
* nvmem_device_cell_read() - Read a given nvmem device and cell
diff --git a/include/linux/nvmem-consumer.h b/include/linux/nvmem-consumer.h
index 0f490b288..227b93158 100644
--- a/include/linux/nvmem-consumer.h
+++ b/include/linux/nvmem-consumer.h
@@ -64,6 +64,10 @@ void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len);
int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len);
int nvmem_cell_read_u16(struct device *dev, const char *cell_id, u16 *val);
int nvmem_cell_read_u32(struct device *dev, const char *cell_id, u32 *val);
+int nvmem_cell_read_variable_le_u32(struct device *dev, const char *cell_id,
+ u32 *val);
+int nvmem_cell_read_variable_le_u64(struct device *dev, const char *cell_id,
+ u64 *val);
/* direct nvmem device read/write interface */
struct nvmem_device *nvmem_device_get(struct device *dev, const char *name);
@@ -135,6 +139,17 @@ static inline int nvmem_cell_read_u32(struct device *dev,
{
return -EOPNOTSUPP;
}
+static inline int nvmem_cell_read_variable_le_u32(struct device *dev,
+ const char *cell_id, u32 *val)
+{
+ return -ENOSYS;
+}
+
+static inline int nvmem_cell_read_variable_le_u64(struct device *dev,
+ const char *cell_id, u64 *val);
+{
+ return -ENOSYS;
+}
static inline struct nvmem_device *nvmem_device_get(struct device *dev,
const char *name)
--
2.34.1