1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2015 MediaTek Inc. |
4 | * Author: Andrew-CT Chen <andrew-ct.chen@mediatek.com> |
5 | */ |
6 | |
7 | #include <linux/device.h> |
8 | #include <linux/module.h> |
9 | #include <linux/mod_devicetable.h> |
10 | #include <linux/io.h> |
11 | #include <linux/nvmem-provider.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/property.h> |
14 | |
15 | struct mtk_efuse_pdata { |
16 | bool uses_post_processing; |
17 | }; |
18 | |
19 | struct mtk_efuse_priv { |
20 | void __iomem *base; |
21 | }; |
22 | |
23 | static int mtk_reg_read(void *context, |
24 | unsigned int reg, void *_val, size_t bytes) |
25 | { |
26 | struct mtk_efuse_priv *priv = context; |
27 | void __iomem *addr = priv->base + reg; |
28 | u8 *val = _val; |
29 | int i; |
30 | |
31 | for (i = 0; i < bytes; i++, val++) |
32 | *val = readb(addr: addr + i); |
33 | |
34 | return 0; |
35 | } |
36 | |
37 | static int mtk_efuse_gpu_speedbin_pp(void *context, const char *id, int index, |
38 | unsigned int offset, void *data, size_t bytes) |
39 | { |
40 | u8 *val = data; |
41 | |
42 | if (val[0] < 8) |
43 | val[0] = BIT(val[0]); |
44 | |
45 | return 0; |
46 | } |
47 | |
48 | static void mtk_efuse_fixup_cell_info(struct nvmem_device *nvmem, |
49 | struct nvmem_layout *layout, |
50 | struct nvmem_cell_info *cell) |
51 | { |
52 | size_t sz = strlen(cell->name); |
53 | |
54 | /* |
55 | * On some SoCs, the GPU speedbin is not read as bitmask but as |
56 | * a number with range [0-7] (max 3 bits): post process to use |
57 | * it in OPP tables to describe supported-hw. |
58 | */ |
59 | if (cell->nbits <= 3 && |
60 | strncmp(cell->name, "gpu-speedbin" , min(sz, strlen("gpu-speedbin" ))) == 0) |
61 | cell->read_post_process = mtk_efuse_gpu_speedbin_pp; |
62 | } |
63 | |
64 | static struct nvmem_layout mtk_efuse_layout = { |
65 | .fixup_cell_info = mtk_efuse_fixup_cell_info, |
66 | }; |
67 | |
68 | static int mtk_efuse_probe(struct platform_device *pdev) |
69 | { |
70 | struct device *dev = &pdev->dev; |
71 | struct resource *res; |
72 | struct nvmem_device *nvmem; |
73 | struct nvmem_config econfig = {}; |
74 | struct mtk_efuse_priv *priv; |
75 | const struct mtk_efuse_pdata *pdata; |
76 | |
77 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
78 | if (!priv) |
79 | return -ENOMEM; |
80 | |
81 | priv->base = devm_platform_get_and_ioremap_resource(pdev, index: 0, res: &res); |
82 | if (IS_ERR(ptr: priv->base)) |
83 | return PTR_ERR(ptr: priv->base); |
84 | |
85 | pdata = device_get_match_data(dev); |
86 | econfig.add_legacy_fixed_of_cells = true; |
87 | econfig.stride = 1; |
88 | econfig.word_size = 1; |
89 | econfig.reg_read = mtk_reg_read; |
90 | econfig.size = resource_size(res); |
91 | econfig.priv = priv; |
92 | econfig.dev = dev; |
93 | if (pdata->uses_post_processing) |
94 | econfig.layout = &mtk_efuse_layout; |
95 | nvmem = devm_nvmem_register(dev, cfg: &econfig); |
96 | |
97 | return PTR_ERR_OR_ZERO(ptr: nvmem); |
98 | } |
99 | |
100 | static const struct mtk_efuse_pdata mtk_mt8186_efuse_pdata = { |
101 | .uses_post_processing = true, |
102 | }; |
103 | |
104 | static const struct mtk_efuse_pdata mtk_efuse_pdata = { |
105 | .uses_post_processing = false, |
106 | }; |
107 | |
108 | static const struct of_device_id mtk_efuse_of_match[] = { |
109 | { .compatible = "mediatek,mt8173-efuse" , .data = &mtk_efuse_pdata }, |
110 | { .compatible = "mediatek,mt8186-efuse" , .data = &mtk_mt8186_efuse_pdata }, |
111 | { .compatible = "mediatek,efuse" , .data = &mtk_efuse_pdata }, |
112 | {/* sentinel */}, |
113 | }; |
114 | MODULE_DEVICE_TABLE(of, mtk_efuse_of_match); |
115 | |
116 | static struct platform_driver mtk_efuse_driver = { |
117 | .probe = mtk_efuse_probe, |
118 | .driver = { |
119 | .name = "mediatek,efuse" , |
120 | .of_match_table = mtk_efuse_of_match, |
121 | }, |
122 | }; |
123 | |
124 | static int __init mtk_efuse_init(void) |
125 | { |
126 | int ret; |
127 | |
128 | ret = platform_driver_register(&mtk_efuse_driver); |
129 | if (ret) { |
130 | pr_err("Failed to register efuse driver\n" ); |
131 | return ret; |
132 | } |
133 | |
134 | return 0; |
135 | } |
136 | |
137 | static void __exit mtk_efuse_exit(void) |
138 | { |
139 | return platform_driver_unregister(&mtk_efuse_driver); |
140 | } |
141 | |
142 | subsys_initcall(mtk_efuse_init); |
143 | module_exit(mtk_efuse_exit); |
144 | |
145 | MODULE_AUTHOR("Andrew-CT Chen <andrew-ct.chen@mediatek.com>" ); |
146 | MODULE_DESCRIPTION("Mediatek EFUSE driver" ); |
147 | MODULE_LICENSE("GPL v2" ); |
148 | |