18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2019 Xilinx, Inc.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/module.h>
78c2ecf20Sopenharmony_ci#include <linux/nvmem-provider.h>
88c2ecf20Sopenharmony_ci#include <linux/of.h>
98c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
108c2ecf20Sopenharmony_ci#include <linux/firmware/xlnx-zynqmp.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#define SILICON_REVISION_MASK 0xF
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistruct zynqmp_nvmem_data {
158c2ecf20Sopenharmony_ci	struct device *dev;
168c2ecf20Sopenharmony_ci	struct nvmem_device *nvmem;
178c2ecf20Sopenharmony_ci};
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic int zynqmp_nvmem_read(void *context, unsigned int offset,
208c2ecf20Sopenharmony_ci			     void *val, size_t bytes)
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	int ret;
238c2ecf20Sopenharmony_ci	int idcode, version;
248c2ecf20Sopenharmony_ci	struct zynqmp_nvmem_data *priv = context;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	ret = zynqmp_pm_get_chipid(&idcode, &version);
278c2ecf20Sopenharmony_ci	if (ret < 0)
288c2ecf20Sopenharmony_ci		return ret;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	dev_dbg(priv->dev, "Read chipid val %x %x\n", idcode, version);
318c2ecf20Sopenharmony_ci	*(int *)val = version & SILICON_REVISION_MASK;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	return 0;
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic struct nvmem_config econfig = {
378c2ecf20Sopenharmony_ci	.name = "zynqmp-nvmem",
388c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
398c2ecf20Sopenharmony_ci	.word_size = 1,
408c2ecf20Sopenharmony_ci	.size = 1,
418c2ecf20Sopenharmony_ci	.read_only = true,
428c2ecf20Sopenharmony_ci};
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic const struct of_device_id zynqmp_nvmem_match[] = {
458c2ecf20Sopenharmony_ci	{ .compatible = "xlnx,zynqmp-nvmem-fw", },
468c2ecf20Sopenharmony_ci	{ /* sentinel */ },
478c2ecf20Sopenharmony_ci};
488c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, zynqmp_nvmem_match);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic int zynqmp_nvmem_probe(struct platform_device *pdev)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
538c2ecf20Sopenharmony_ci	struct zynqmp_nvmem_data *priv;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	priv = devm_kzalloc(dev, sizeof(struct zynqmp_nvmem_data), GFP_KERNEL);
568c2ecf20Sopenharmony_ci	if (!priv)
578c2ecf20Sopenharmony_ci		return -ENOMEM;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	priv->dev = dev;
608c2ecf20Sopenharmony_ci	econfig.dev = dev;
618c2ecf20Sopenharmony_ci	econfig.reg_read = zynqmp_nvmem_read;
628c2ecf20Sopenharmony_ci	econfig.priv = priv;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	priv->nvmem = devm_nvmem_register(dev, &econfig);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	return PTR_ERR_OR_ZERO(priv->nvmem);
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic struct platform_driver zynqmp_nvmem_driver = {
708c2ecf20Sopenharmony_ci	.probe = zynqmp_nvmem_probe,
718c2ecf20Sopenharmony_ci	.driver = {
728c2ecf20Sopenharmony_ci		.name = "zynqmp-nvmem",
738c2ecf20Sopenharmony_ci		.of_match_table = zynqmp_nvmem_match,
748c2ecf20Sopenharmony_ci	},
758c2ecf20Sopenharmony_ci};
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cimodule_platform_driver(zynqmp_nvmem_driver);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michal Simek <michal.simek@xilinx.com>, Nava kishore Manne <navam@xilinx.com>");
808c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ZynqMP NVMEM driver");
818c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
82