1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. |
4 | */ |
5 | |
6 | #include <linux/firmware/qcom/qcom_scm.h> |
7 | #include <linux/mod_devicetable.h> |
8 | #include <linux/nvmem-provider.h> |
9 | #include <linux/platform_device.h> |
10 | #include <linux/pm_runtime.h> |
11 | |
12 | /** |
13 | * struct sec_qfprom - structure holding secure qfprom attributes |
14 | * |
15 | * @base: starting physical address for secure qfprom corrected address space. |
16 | * @dev: qfprom device structure. |
17 | */ |
18 | struct sec_qfprom { |
19 | phys_addr_t base; |
20 | struct device *dev; |
21 | }; |
22 | |
23 | static int sec_qfprom_reg_read(void *context, unsigned int reg, void *_val, size_t bytes) |
24 | { |
25 | struct sec_qfprom *priv = context; |
26 | unsigned int i; |
27 | u8 *val = _val; |
28 | u32 read_val; |
29 | u8 *tmp; |
30 | |
31 | for (i = 0; i < bytes; i++, reg++) { |
32 | if (i == 0 || reg % 4 == 0) { |
33 | if (qcom_scm_io_readl(addr: priv->base + (reg & ~3), val: &read_val)) { |
34 | dev_err(priv->dev, "Couldn't access fuse register\n" ); |
35 | return -EINVAL; |
36 | } |
37 | tmp = (u8 *)&read_val; |
38 | } |
39 | |
40 | val[i] = tmp[reg & 3]; |
41 | } |
42 | |
43 | return 0; |
44 | } |
45 | |
46 | static int sec_qfprom_probe(struct platform_device *pdev) |
47 | { |
48 | struct nvmem_config econfig = { |
49 | .name = "sec-qfprom" , |
50 | .add_legacy_fixed_of_cells = true, |
51 | .stride = 1, |
52 | .word_size = 1, |
53 | .id = NVMEM_DEVID_AUTO, |
54 | .reg_read = sec_qfprom_reg_read, |
55 | }; |
56 | struct device *dev = &pdev->dev; |
57 | struct nvmem_device *nvmem; |
58 | struct sec_qfprom *priv; |
59 | struct resource *res; |
60 | |
61 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
62 | if (!priv) |
63 | return -ENOMEM; |
64 | |
65 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
66 | if (!res) |
67 | return -EINVAL; |
68 | |
69 | priv->base = res->start; |
70 | |
71 | econfig.size = resource_size(res); |
72 | econfig.dev = dev; |
73 | econfig.priv = priv; |
74 | |
75 | priv->dev = dev; |
76 | |
77 | nvmem = devm_nvmem_register(dev, cfg: &econfig); |
78 | |
79 | return PTR_ERR_OR_ZERO(ptr: nvmem); |
80 | } |
81 | |
82 | static const struct of_device_id sec_qfprom_of_match[] = { |
83 | { .compatible = "qcom,sec-qfprom" }, |
84 | {/* sentinel */}, |
85 | }; |
86 | MODULE_DEVICE_TABLE(of, sec_qfprom_of_match); |
87 | |
88 | static struct platform_driver qfprom_driver = { |
89 | .probe = sec_qfprom_probe, |
90 | .driver = { |
91 | .name = "qcom_sec_qfprom" , |
92 | .of_match_table = sec_qfprom_of_match, |
93 | }, |
94 | }; |
95 | module_platform_driver(qfprom_driver); |
96 | MODULE_DESCRIPTION("Qualcomm Secure QFPROM driver" ); |
97 | MODULE_LICENSE("GPL" ); |
98 | |