// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2020 MediaTek Inc. * Author: Mac Lu */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include struct mtk_devinfo_priv { unsigned int *devinfo_data; }; struct devinfo_tag { unsigned int data_size; unsigned int data[0]; }; static int devinfo_parse_dt(struct mtk_devinfo_priv *priv, struct device *dev) { struct device_node *chosen_node; struct devinfo_tag *tags; unsigned int size = 0; chosen_node = of_find_node_by_path("/chosen"); if (!chosen_node) { chosen_node = of_find_node_by_path("/chosen@0"); if (!chosen_node) { pr_warn("chosen node is not found!!\n"); return -ENXIO; } } tags = (struct devinfo_tag *) of_get_property(chosen_node, "atag,devinfo", NULL); if (tags) { size = tags->data_size; priv->devinfo_data = devm_kzalloc(dev, sizeof(struct devinfo_tag) + (size * sizeof(unsigned int)), GFP_KERNEL); if (!priv->devinfo_data) return -ENOMEM; WARN_ON(size > 300); /* for size integer too big protection */ memcpy(priv->devinfo_data, tags->data, (size * sizeof(unsigned int))); } else { pr_warn("atag,devinfo is not found\n"); return -ENXIO; } return (size * sizeof(unsigned int)); } static int mtk_reg_read(void *context, unsigned int reg, void *_val, size_t bytes) { struct mtk_devinfo_priv *priv = context; unsigned int *val = _val; int i = 0, words = bytes / 4; while (words--) { *val++ = priv->devinfo_data[i + (reg / 4)]; i++; } return 0; } static int mtk_devinfo_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct nvmem_device *nvmem; struct nvmem_config econfig = {}; struct mtk_devinfo_priv *priv; int ret_size = 0; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; ret_size = devinfo_parse_dt(priv, dev); if (ret_size <= 0) { pr_warn("parse devinfo failed\n"); return ret_size; } econfig.size = ret_size; econfig.stride = 4; econfig.word_size = 4; econfig.name = "mtk-devinfo"; econfig.read_only = true; econfig.reg_read = mtk_reg_read; econfig.priv = priv; econfig.dev = dev; nvmem = devm_nvmem_register(dev, &econfig); return PTR_ERR_OR_ZERO(nvmem); } static const struct of_device_id mtk_devinfo_of_match[] = { { .compatible = "mediatek,devinfo",}, {/* sentinel */}, }; MODULE_DEVICE_TABLE(of, mtk_devinfo_of_match); static struct platform_driver mtk_devinfo_driver = { .probe = mtk_devinfo_probe, .driver = { .name = "mediatek,devinfo", .of_match_table = mtk_devinfo_of_match, }, }; static int __init mtk_devinfo_init(void) { int ret; ret = platform_driver_register(&mtk_devinfo_driver); if (ret) { pr_err("Failed to register devinfo driver\n"); return ret; } return 0; } static void __exit mtk_devinfo_exit(void) { return platform_driver_unregister(&mtk_devinfo_driver); } #ifdef MODULE module_init(mtk_devinfo_init); #else subsys_initcall(mtk_devinfo_init); #endif module_exit(mtk_devinfo_exit); MODULE_AUTHOR("Mac Lu "); MODULE_DESCRIPTION("Mediatek device information driver"); MODULE_LICENSE("GPL v2");