aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/generic/backport-5.10/810-v6.0-0001-nvmem-microchip-otpc-add-support.patch
diff options
context:
space:
mode:
authorRafał Miłecki <rafal@milecki.pl>2023-01-06 19:37:37 +0100
committerRafał Miłecki <rafal@milecki.pl>2023-01-06 22:08:03 +0100
commit8dfe69cdfc5c943f946faa873ff330b47125011d (patch)
treea68e869e00bcadec6c32252916e1a0fc5e18c51d /target/linux/generic/backport-5.10/810-v6.0-0001-nvmem-microchip-otpc-add-support.patch
parentd3e89e69c52115d1c02f5c16a4aa6b721e003578 (diff)
downloadupstream-8dfe69cdfc5c943f946faa873ff330b47125011d.tar.gz
upstream-8dfe69cdfc5c943f946faa873ff330b47125011d.tar.bz2
upstream-8dfe69cdfc5c943f946faa873ff330b47125011d.zip
kernel: update nvmem subsystem to the latest upstream
Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
Diffstat (limited to 'target/linux/generic/backport-5.10/810-v6.0-0001-nvmem-microchip-otpc-add-support.patch')
-rw-r--r--target/linux/generic/backport-5.10/810-v6.0-0001-nvmem-microchip-otpc-add-support.patch389
1 files changed, 389 insertions, 0 deletions
diff --git a/target/linux/generic/backport-5.10/810-v6.0-0001-nvmem-microchip-otpc-add-support.patch b/target/linux/generic/backport-5.10/810-v6.0-0001-nvmem-microchip-otpc-add-support.patch
new file mode 100644
index 0000000000..eb99ec190c
--- /dev/null
+++ b/target/linux/generic/backport-5.10/810-v6.0-0001-nvmem-microchip-otpc-add-support.patch
@@ -0,0 +1,389 @@
+From 98830350d3fc824c1ff5c338140fe20f041a5916 Mon Sep 17 00:00:00 2001
+From: Claudiu Beznea <claudiu.beznea@microchip.com>
+Date: Wed, 6 Jul 2022 11:06:22 +0100
+Subject: [PATCH] nvmem: microchip-otpc: add support
+
+Add support for Microchip OTP controller available on SAMA7G5. The OTPC
+controls the access to a non-volatile memory. The memory behind OTPC is
+organized into packets, packets are composed by a fixed length header
+(4 bytes long) and a variable length payload (payload length is available
+in the header). When software request the data at an offset in memory
+the OTPC will return (via header + data registers) the whole packet that
+has a word at that offset. For the OTP memory layout like below:
+
+offset OTP Memory layout
+
+ . .
+ . ... .
+ . .
+0x0E +-----------+ <--- packet X
+ | header X |
+0x12 +-----------+
+ | payload X |
+0x16 | |
+ | |
+0x1A | |
+ +-----------+
+ . .
+ . ... .
+ . .
+
+if user requests data at address 0x16 the data started at 0x0E will be
+returned by controller. User will be able to fetch the whole packet
+starting at 0x0E (or parts of the packet) via proper registers. The same
+packet will be returned if software request the data at offset 0x0E or
+0x12 or 0x1A.
+
+The OTP will be populated by Microchip with at least 2 packets first one
+being boot configuration packet and the 2nd one being temperature
+calibration packet. The packet order will be preserved b/w different chip
+revisions but the packet sizes may change.
+
+For the above reasons and to keep the same software able to work on all
+chip variants the read function of the driver is working with a packet
+id instead of an offset in OTP memory.
+
+Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
+Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+Link: https://lore.kernel.org/r/20220706100627.6534-3-srinivas.kandagatla@linaro.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ MAINTAINERS | 8 +
+ drivers/nvmem/Kconfig | 7 +
+ drivers/nvmem/Makefile | 2 +
+ drivers/nvmem/microchip-otpc.c | 288 +++++++++++++++++++++++++++++++++
+ 4 files changed, 305 insertions(+)
+ create mode 100644 drivers/nvmem/microchip-otpc.c
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -11565,6 +11565,14 @@ S: Supported
+ F: Documentation/devicetree/bindings/mtd/atmel-nand.txt
+ F: drivers/mtd/nand/raw/atmel/*
+
++MICROCHIP OTPC DRIVER
++M: Claudiu Beznea <claudiu.beznea@microchip.com>
++L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
++S: Supported
++F: Documentation/devicetree/bindings/nvmem/microchip,sama7g5-otpc.yaml
++F: drivers/nvmem/microchip-otpc.c
++F: dt-bindings/nvmem/microchip,sama7g5-otpc.h
++
+ MICROCHIP PWM DRIVER
+ M: Claudiu Beznea <claudiu.beznea@microchip.com>
+ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -107,6 +107,13 @@ config MTK_EFUSE
+ This driver can also be built as a module. If so, the module
+ will be called efuse-mtk.
+
++config MICROCHIP_OTPC
++ tristate "Microchip OTPC support"
++ depends on ARCH_AT91 || COMPILE_TEST
++ help
++ This driver enable the OTP controller available on Microchip SAMA7G5
++ SoCs. It controlls the access to the OTP memory connected to it.
++
+ config NVMEM_NINTENDO_OTP
+ tristate "Nintendo Wii and Wii U OTP Support"
+ depends on WII || COMPILE_TEST
+--- a/drivers/nvmem/Makefile
++++ b/drivers/nvmem/Makefile
+@@ -67,3 +67,5 @@ obj-$(CONFIG_NVMEM_SUNPLUS_OCOTP) += nvm
+ nvmem_sunplus_ocotp-y := sunplus-ocotp.o
+ obj-$(CONFIG_NVMEM_APPLE_EFUSES) += nvmem-apple-efuses.o
+ nvmem-apple-efuses-y := apple-efuses.o
++obj-$(CONFIG_MICROCHIP_OTPC) += nvmem-microchip-otpc.o
++nvmem-microchip-otpc-y := microchip-otpc.o
+--- /dev/null
++++ b/drivers/nvmem/microchip-otpc.c
+@@ -0,0 +1,288 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * OTP Memory controller
++ *
++ * Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries
++ *
++ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
++ */
++
++#include <linux/bitfield.h>
++#include <linux/iopoll.h>
++#include <linux/module.h>
++#include <linux/nvmem-provider.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++
++#define MCHP_OTPC_CR (0x0)
++#define MCHP_OTPC_CR_READ BIT(6)
++#define MCHP_OTPC_MR (0x4)
++#define MCHP_OTPC_MR_ADDR GENMASK(31, 16)
++#define MCHP_OTPC_AR (0x8)
++#define MCHP_OTPC_SR (0xc)
++#define MCHP_OTPC_SR_READ BIT(6)
++#define MCHP_OTPC_HR (0x20)
++#define MCHP_OTPC_HR_SIZE GENMASK(15, 8)
++#define MCHP_OTPC_DR (0x24)
++
++#define MCHP_OTPC_NAME "mchp-otpc"
++#define MCHP_OTPC_SIZE (11 * 1024)
++
++/**
++ * struct mchp_otpc - OTPC private data structure
++ * @base: base address
++ * @dev: struct device pointer
++ * @packets: list of packets in OTP memory
++ * @npackets: number of packets in OTP memory
++ */
++struct mchp_otpc {
++ void __iomem *base;
++ struct device *dev;
++ struct list_head packets;
++ u32 npackets;
++};
++
++/**
++ * struct mchp_otpc_packet - OTPC packet data structure
++ * @list: list head
++ * @id: packet ID
++ * @offset: packet offset (in words) in OTP memory
++ */
++struct mchp_otpc_packet {
++ struct list_head list;
++ u32 id;
++ u32 offset;
++};
++
++static struct mchp_otpc_packet *mchp_otpc_id_to_packet(struct mchp_otpc *otpc,
++ u32 id)
++{
++ struct mchp_otpc_packet *packet;
++
++ if (id >= otpc->npackets)
++ return NULL;
++
++ list_for_each_entry(packet, &otpc->packets, list) {
++ if (packet->id == id)
++ return packet;
++ }
++
++ return NULL;
++}
++
++static int mchp_otpc_prepare_read(struct mchp_otpc *otpc,
++ unsigned int offset)
++{
++ u32 tmp;
++
++ /* Set address. */
++ tmp = readl_relaxed(otpc->base + MCHP_OTPC_MR);
++ tmp &= ~MCHP_OTPC_MR_ADDR;
++ tmp |= FIELD_PREP(MCHP_OTPC_MR_ADDR, offset);
++ writel_relaxed(tmp, otpc->base + MCHP_OTPC_MR);
++
++ /* Set read. */
++ tmp = readl_relaxed(otpc->base + MCHP_OTPC_CR);
++ tmp |= MCHP_OTPC_CR_READ;
++ writel_relaxed(tmp, otpc->base + MCHP_OTPC_CR);
++
++ /* Wait for packet to be transferred into temporary buffers. */
++ return read_poll_timeout(readl_relaxed, tmp, !(tmp & MCHP_OTPC_SR_READ),
++ 10000, 2000, false, otpc->base + MCHP_OTPC_SR);
++}
++
++/*
++ * OTPC memory is organized into packets. Each packets contains a header and
++ * a payload. Header is 4 bytes long and contains the size of the payload.
++ * Payload size varies. The memory footprint is something as follows:
++ *
++ * Memory offset Memory footprint Packet ID
++ * ------------- ---------------- ---------
++ *
++ * 0x0 +------------+ <-- packet 0
++ * | header 0 |
++ * 0x4 +------------+
++ * | payload 0 |
++ * . .
++ * . ... .
++ * . .
++ * offset1 +------------+ <-- packet 1
++ * | header 1 |
++ * offset1 + 0x4 +------------+
++ * | payload 1 |
++ * . .
++ * . ... .
++ * . .
++ * offset2 +------------+ <-- packet 2
++ * . .
++ * . ... .
++ * . .
++ * offsetN +------------+ <-- packet N
++ * | header N |
++ * offsetN + 0x4 +------------+
++ * | payload N |
++ * . .
++ * . ... .
++ * . .
++ * +------------+
++ *
++ * where offset1, offset2, offsetN depends on the size of payload 0, payload 1,
++ * payload N-1.
++ *
++ * The access to memory is done on a per packet basis: the control registers
++ * need to be updated with an offset address (within a packet range) and the
++ * data registers will be update by controller with information contained by
++ * that packet. E.g. if control registers are updated with any address within
++ * the range [offset1, offset2) the data registers are updated by controller
++ * with packet 1. Header data is accessible though MCHP_OTPC_HR register.
++ * Payload data is accessible though MCHP_OTPC_DR and MCHP_OTPC_AR registers.
++ * There is no direct mapping b/w the offset requested by software and the
++ * offset returned by hardware.
++ *
++ * For this, the read function will return the first requested bytes in the
++ * packet. The user will have to be aware of the memory footprint before doing
++ * the read request.
++ */
++static int mchp_otpc_read(void *priv, unsigned int off, void *val,
++ size_t bytes)
++{
++ struct mchp_otpc *otpc = priv;
++ struct mchp_otpc_packet *packet;
++ u32 *buf = val;
++ u32 offset;
++ size_t len = 0;
++ int ret, payload_size;
++
++ /*
++ * We reach this point with off being multiple of stride = 4 to
++ * be able to cross the subsystem. Inside the driver we use continuous
++ * unsigned integer numbers for packet id, thus devide off by 4
++ * before passing it to mchp_otpc_id_to_packet().
++ */
++ packet = mchp_otpc_id_to_packet(otpc, off / 4);
++ if (!packet)
++ return -EINVAL;
++ offset = packet->offset;
++
++ while (len < bytes) {
++ ret = mchp_otpc_prepare_read(otpc, offset);
++ if (ret)
++ return ret;
++
++ /* Read and save header content. */
++ *buf++ = readl_relaxed(otpc->base + MCHP_OTPC_HR);
++ len += sizeof(*buf);
++ offset++;
++ if (len >= bytes)
++ break;
++
++ /* Read and save payload content. */
++ payload_size = FIELD_GET(MCHP_OTPC_HR_SIZE, *(buf - 1));
++ writel_relaxed(0UL, otpc->base + MCHP_OTPC_AR);
++ do {
++ *buf++ = readl_relaxed(otpc->base + MCHP_OTPC_DR);
++ len += sizeof(*buf);
++ offset++;
++ payload_size--;
++ } while (payload_size >= 0 && len < bytes);
++ }
++
++ return 0;
++}
++
++static int mchp_otpc_init_packets_list(struct mchp_otpc *otpc, u32 *size)
++{
++ struct mchp_otpc_packet *packet;
++ u32 word, word_pos = 0, id = 0, npackets = 0, payload_size;
++ int ret;
++
++ INIT_LIST_HEAD(&otpc->packets);
++ *size = 0;
++
++ while (*size < MCHP_OTPC_SIZE) {
++ ret = mchp_otpc_prepare_read(otpc, word_pos);
++ if (ret)
++ return ret;
++
++ word = readl_relaxed(otpc->base + MCHP_OTPC_HR);
++ payload_size = FIELD_GET(MCHP_OTPC_HR_SIZE, word);
++ if (!payload_size)
++ break;
++
++ packet = devm_kzalloc(otpc->dev, sizeof(*packet), GFP_KERNEL);
++ if (!packet)
++ return -ENOMEM;
++
++ packet->id = id++;
++ packet->offset = word_pos;
++ INIT_LIST_HEAD(&packet->list);
++ list_add_tail(&packet->list, &otpc->packets);
++
++ /* Count size by adding header and paload sizes. */
++ *size += 4 * (payload_size + 1);
++ /* Next word: this packet (header, payload) position + 1. */
++ word_pos += payload_size + 2;
++
++ npackets++;
++ }
++
++ otpc->npackets = npackets;
++
++ return 0;
++}
++
++static struct nvmem_config mchp_nvmem_config = {
++ .name = MCHP_OTPC_NAME,
++ .type = NVMEM_TYPE_OTP,
++ .read_only = true,
++ .word_size = 4,
++ .stride = 4,
++ .reg_read = mchp_otpc_read,
++};
++
++static int mchp_otpc_probe(struct platform_device *pdev)
++{
++ struct nvmem_device *nvmem;
++ struct mchp_otpc *otpc;
++ u32 size;
++ int ret;
++
++ otpc = devm_kzalloc(&pdev->dev, sizeof(*otpc), GFP_KERNEL);
++ if (!otpc)
++ return -ENOMEM;
++
++ otpc->base = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(otpc->base))
++ return PTR_ERR(otpc->base);
++
++ otpc->dev = &pdev->dev;
++ ret = mchp_otpc_init_packets_list(otpc, &size);
++ if (ret)
++ return ret;
++
++ mchp_nvmem_config.dev = otpc->dev;
++ mchp_nvmem_config.size = size;
++ mchp_nvmem_config.priv = otpc;
++ nvmem = devm_nvmem_register(&pdev->dev, &mchp_nvmem_config);
++
++ return PTR_ERR_OR_ZERO(nvmem);
++}
++
++static const struct of_device_id __maybe_unused mchp_otpc_ids[] = {
++ { .compatible = "microchip,sama7g5-otpc", },
++ { },
++};
++MODULE_DEVICE_TABLE(of, mchp_otpc_ids);
++
++static struct platform_driver mchp_otpc_driver = {
++ .probe = mchp_otpc_probe,
++ .driver = {
++ .name = MCHP_OTPC_NAME,
++ .of_match_table = of_match_ptr(mchp_otpc_ids),
++ },
++};
++module_platform_driver(mchp_otpc_driver);
++
++MODULE_AUTHOR("Claudiu Beznea <claudiu.beznea@microchip.com>");
++MODULE_DESCRIPTION("Microchip SAMA7G5 OTPC driver");
++MODULE_LICENSE("GPL");