xref: /linux/drivers/nvmem/meson-efuse.c (revision c95baf12f5077419db01313ab61c2aac007d40cd)
15b497af4SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2ad855eaeSCarlo Caione /*
39593ad32SMartin Blumenstingl  * Amlogic Meson GX eFuse Driver
4ad855eaeSCarlo Caione  *
5ad855eaeSCarlo Caione  * Copyright (c) 2016 Endless Computers, Inc.
6ad855eaeSCarlo Caione  * Author: Carlo Caione <carlo@endlessm.com>
7ad855eaeSCarlo Caione  */
8ad855eaeSCarlo Caione 
9611fbca1SJerome Brunet #include <linux/clk.h>
10ad855eaeSCarlo Caione #include <linux/module.h>
11ad855eaeSCarlo Caione #include <linux/nvmem-provider.h>
12ad855eaeSCarlo Caione #include <linux/of.h>
13ad855eaeSCarlo Caione #include <linux/platform_device.h>
14ad855eaeSCarlo Caione 
15ad855eaeSCarlo Caione #include <linux/firmware/meson/meson_sm.h>
16ad855eaeSCarlo Caione 
17ad855eaeSCarlo Caione static int meson_efuse_read(void *context, unsigned int offset,
18ad855eaeSCarlo Caione 			    void *val, size_t bytes)
19ad855eaeSCarlo Caione {
20*8cde3c21SCarlo Caione 	struct meson_sm_firmware *fw = context;
21*8cde3c21SCarlo Caione 
22*8cde3c21SCarlo Caione 	return meson_sm_call_read(fw, (u8 *)val, bytes, SM_EFUSE_READ, offset,
23ad855eaeSCarlo Caione 				  bytes, 0, 0, 0);
24ad855eaeSCarlo Caione }
25ad855eaeSCarlo Caione 
26e5507730SJerome Brunet static int meson_efuse_write(void *context, unsigned int offset,
27e5507730SJerome Brunet 			     void *val, size_t bytes)
28e5507730SJerome Brunet {
29*8cde3c21SCarlo Caione 	struct meson_sm_firmware *fw = context;
30*8cde3c21SCarlo Caione 
31*8cde3c21SCarlo Caione 	return meson_sm_call_write(fw, (u8 *)val, bytes, SM_EFUSE_WRITE, offset,
32e5507730SJerome Brunet 				   bytes, 0, 0, 0);
33e5507730SJerome Brunet }
34e5507730SJerome Brunet 
35ad855eaeSCarlo Caione static const struct of_device_id meson_efuse_match[] = {
36ad855eaeSCarlo Caione 	{ .compatible = "amlogic,meson-gxbb-efuse", },
37ad855eaeSCarlo Caione 	{ /* sentinel */ },
38ad855eaeSCarlo Caione };
39ad855eaeSCarlo Caione MODULE_DEVICE_TABLE(of, meson_efuse_match);
40ad855eaeSCarlo Caione 
41ad855eaeSCarlo Caione static int meson_efuse_probe(struct platform_device *pdev)
42ad855eaeSCarlo Caione {
43401488d1SJerome Brunet 	struct device *dev = &pdev->dev;
44*8cde3c21SCarlo Caione 	struct meson_sm_firmware *fw;
45*8cde3c21SCarlo Caione 	struct device_node *sm_np;
46ad855eaeSCarlo Caione 	struct nvmem_device *nvmem;
47401488d1SJerome Brunet 	struct nvmem_config *econfig;
48611fbca1SJerome Brunet 	struct clk *clk;
49ad855eaeSCarlo Caione 	unsigned int size;
50611fbca1SJerome Brunet 	int ret;
51611fbca1SJerome Brunet 
52*8cde3c21SCarlo Caione 	sm_np = of_parse_phandle(pdev->dev.of_node, "secure-monitor", 0);
53*8cde3c21SCarlo Caione 	if (!sm_np) {
54*8cde3c21SCarlo Caione 		dev_err(&pdev->dev, "no secure-monitor node\n");
55*8cde3c21SCarlo Caione 		return -ENODEV;
56*8cde3c21SCarlo Caione 	}
57*8cde3c21SCarlo Caione 
58*8cde3c21SCarlo Caione 	fw = meson_sm_get(sm_np);
59*8cde3c21SCarlo Caione 	of_node_put(sm_np);
60*8cde3c21SCarlo Caione 	if (!fw)
61*8cde3c21SCarlo Caione 		return -EPROBE_DEFER;
62*8cde3c21SCarlo Caione 
63611fbca1SJerome Brunet 	clk = devm_clk_get(dev, NULL);
64611fbca1SJerome Brunet 	if (IS_ERR(clk)) {
65611fbca1SJerome Brunet 		ret = PTR_ERR(clk);
66611fbca1SJerome Brunet 		if (ret != -EPROBE_DEFER)
67611fbca1SJerome Brunet 			dev_err(dev, "failed to get efuse gate");
68611fbca1SJerome Brunet 		return ret;
69611fbca1SJerome Brunet 	}
70611fbca1SJerome Brunet 
71611fbca1SJerome Brunet 	ret = clk_prepare_enable(clk);
72611fbca1SJerome Brunet 	if (ret) {
73611fbca1SJerome Brunet 		dev_err(dev, "failed to enable gate");
74611fbca1SJerome Brunet 		return ret;
75611fbca1SJerome Brunet 	}
76611fbca1SJerome Brunet 
77611fbca1SJerome Brunet 	ret = devm_add_action_or_reset(dev,
78611fbca1SJerome Brunet 				       (void(*)(void *))clk_disable_unprepare,
79611fbca1SJerome Brunet 				       clk);
80611fbca1SJerome Brunet 	if (ret) {
81611fbca1SJerome Brunet 		dev_err(dev, "failed to add disable callback");
82611fbca1SJerome Brunet 		return ret;
83611fbca1SJerome Brunet 	}
84ad855eaeSCarlo Caione 
85*8cde3c21SCarlo Caione 	if (meson_sm_call(fw, SM_EFUSE_USER_MAX, &size, 0, 0, 0, 0, 0) < 0) {
868649dbe5SJerome Brunet 		dev_err(dev, "failed to get max user");
87ad855eaeSCarlo Caione 		return -EINVAL;
888649dbe5SJerome Brunet 	}
89ad855eaeSCarlo Caione 
90401488d1SJerome Brunet 	econfig = devm_kzalloc(dev, sizeof(*econfig), GFP_KERNEL);
91401488d1SJerome Brunet 	if (!econfig)
92401488d1SJerome Brunet 		return -ENOMEM;
93ad855eaeSCarlo Caione 
94401488d1SJerome Brunet 	econfig->dev = dev;
95401488d1SJerome Brunet 	econfig->name = dev_name(dev);
96401488d1SJerome Brunet 	econfig->stride = 1;
97401488d1SJerome Brunet 	econfig->word_size = 1;
98401488d1SJerome Brunet 	econfig->reg_read = meson_efuse_read;
99e5507730SJerome Brunet 	econfig->reg_write = meson_efuse_write;
100401488d1SJerome Brunet 	econfig->size = size;
101*8cde3c21SCarlo Caione 	econfig->priv = fw;
102401488d1SJerome Brunet 
103401488d1SJerome Brunet 	nvmem = devm_nvmem_register(&pdev->dev, econfig);
104ad855eaeSCarlo Caione 
10590696a40SAndrey Smirnov 	return PTR_ERR_OR_ZERO(nvmem);
106ad855eaeSCarlo Caione }
107ad855eaeSCarlo Caione 
108ad855eaeSCarlo Caione static struct platform_driver meson_efuse_driver = {
109ad855eaeSCarlo Caione 	.probe = meson_efuse_probe,
110ad855eaeSCarlo Caione 	.driver = {
111ad855eaeSCarlo Caione 		.name = "meson-efuse",
112ad855eaeSCarlo Caione 		.of_match_table = meson_efuse_match,
113ad855eaeSCarlo Caione 	},
114ad855eaeSCarlo Caione };
115ad855eaeSCarlo Caione 
116ad855eaeSCarlo Caione module_platform_driver(meson_efuse_driver);
117ad855eaeSCarlo Caione 
118ad855eaeSCarlo Caione MODULE_AUTHOR("Carlo Caione <carlo@endlessm.com>");
1199593ad32SMartin Blumenstingl MODULE_DESCRIPTION("Amlogic Meson GX NVMEM driver");
120ad855eaeSCarlo Caione MODULE_LICENSE("GPL v2");
121