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_dt_cell_info(struct nvmem_device *nvmem, |
49 | struct nvmem_cell_info *cell) |
50 | { |
51 | size_t sz = strlen(cell->name); |
52 | |
53 | /* |
54 | * On some SoCs, the GPU speedbin is not read as bitmask but as |
55 | * a number with range [0-7] (max 3 bits): post process to use |
56 | * it in OPP tables to describe supported-hw. |
57 | */ |
58 | if (cell->nbits <= 3 && |
59 | strncmp(cell->name, "gpu-speedbin" , min(sz, strlen("gpu-speedbin" ))) == 0) |
60 | cell->read_post_process = mtk_efuse_gpu_speedbin_pp; |
61 | } |
62 | |
63 | static int mtk_efuse_probe(struct platform_device *pdev) |
64 | { |
65 | struct device *dev = &pdev->dev; |
66 | struct resource *res; |
67 | struct nvmem_device *nvmem; |
68 | struct nvmem_config econfig = {}; |
69 | struct mtk_efuse_priv *priv; |
70 | const struct mtk_efuse_pdata *pdata; |
71 | struct platform_device *socinfo; |
72 | |
73 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
74 | if (!priv) |
75 | return -ENOMEM; |
76 | |
77 | priv->base = devm_platform_get_and_ioremap_resource(pdev, index: 0, res: &res); |
78 | if (IS_ERR(ptr: priv->base)) |
79 | return PTR_ERR(ptr: priv->base); |
80 | |
81 | pdata = device_get_match_data(dev); |
82 | econfig.add_legacy_fixed_of_cells = true; |
83 | econfig.stride = 1; |
84 | econfig.word_size = 1; |
85 | econfig.reg_read = mtk_reg_read; |
86 | econfig.size = resource_size(res); |
87 | econfig.priv = priv; |
88 | econfig.dev = dev; |
89 | if (pdata->uses_post_processing) |
90 | econfig.fixup_dt_cell_info = &mtk_efuse_fixup_dt_cell_info; |
91 | nvmem = devm_nvmem_register(dev, cfg: &econfig); |
92 | if (IS_ERR(ptr: nvmem)) |
93 | return PTR_ERR(ptr: nvmem); |
94 | |
95 | socinfo = platform_device_register_data(parent: &pdev->dev, name: "mtk-socinfo" , |
96 | PLATFORM_DEVID_AUTO, NULL, size: 0); |
97 | if (IS_ERR(ptr: socinfo)) |
98 | dev_info(dev, "MediaTek SoC Information will be unavailable\n" ); |
99 | |
100 | platform_set_drvdata(pdev, data: socinfo); |
101 | return 0; |
102 | } |
103 | |
104 | static const struct mtk_efuse_pdata mtk_mt8186_efuse_pdata = { |
105 | .uses_post_processing = true, |
106 | }; |
107 | |
108 | static const struct mtk_efuse_pdata mtk_efuse_pdata = { |
109 | .uses_post_processing = false, |
110 | }; |
111 | |
112 | static const struct of_device_id mtk_efuse_of_match[] = { |
113 | { .compatible = "mediatek,mt8173-efuse" , .data = &mtk_efuse_pdata }, |
114 | { .compatible = "mediatek,mt8186-efuse" , .data = &mtk_mt8186_efuse_pdata }, |
115 | { .compatible = "mediatek,efuse" , .data = &mtk_efuse_pdata }, |
116 | {/* sentinel */}, |
117 | }; |
118 | MODULE_DEVICE_TABLE(of, mtk_efuse_of_match); |
119 | |
120 | static void mtk_efuse_remove(struct platform_device *pdev) |
121 | { |
122 | struct platform_device *socinfo = platform_get_drvdata(pdev); |
123 | |
124 | if (!IS_ERR_OR_NULL(ptr: socinfo)) |
125 | platform_device_unregister(socinfo); |
126 | } |
127 | |
128 | static struct platform_driver mtk_efuse_driver = { |
129 | .probe = mtk_efuse_probe, |
130 | .remove_new = mtk_efuse_remove, |
131 | .driver = { |
132 | .name = "mediatek,efuse" , |
133 | .of_match_table = mtk_efuse_of_match, |
134 | }, |
135 | }; |
136 | |
137 | static int __init mtk_efuse_init(void) |
138 | { |
139 | int ret; |
140 | |
141 | ret = platform_driver_register(&mtk_efuse_driver); |
142 | if (ret) { |
143 | pr_err("Failed to register efuse driver\n" ); |
144 | return ret; |
145 | } |
146 | |
147 | return 0; |
148 | } |
149 | |
150 | static void __exit mtk_efuse_exit(void) |
151 | { |
152 | return platform_driver_unregister(&mtk_efuse_driver); |
153 | } |
154 | |
155 | subsys_initcall(mtk_efuse_init); |
156 | module_exit(mtk_efuse_exit); |
157 | |
158 | MODULE_AUTHOR("Andrew-CT Chen <andrew-ct.chen@mediatek.com>" ); |
159 | MODULE_DESCRIPTION("Mediatek EFUSE driver" ); |
160 | MODULE_LICENSE("GPL v2" ); |
161 | |