]>
Commit | Line | Data |
---|---|---|
1802d0be | 1 | // SPDX-License-Identifier: GPL-2.0-only |
4c7e4fe3 ACC |
2 | /* |
3 | * Copyright (c) 2015 MediaTek Inc. | |
4 | * Author: Andrew-CT Chen <[email protected]> | |
4c7e4fe3 ACC |
5 | */ |
6 | ||
7 | #include <linux/device.h> | |
8 | #include <linux/module.h> | |
ac316725 | 9 | #include <linux/mod_devicetable.h> |
ba360fd0 | 10 | #include <linux/io.h> |
4c7e4fe3 ACC |
11 | #include <linux/nvmem-provider.h> |
12 | #include <linux/platform_device.h> | |
de6e0509 ADR |
13 | #include <linux/property.h> |
14 | ||
15 | struct mtk_efuse_pdata { | |
16 | bool uses_post_processing; | |
17 | }; | |
4c7e4fe3 | 18 | |
a48f1fff MY |
19 | struct mtk_efuse_priv { |
20 | void __iomem *base; | |
21 | }; | |
22 | ||
ba360fd0 SK |
23 | static int mtk_reg_read(void *context, |
24 | unsigned int reg, void *_val, size_t bytes) | |
25 | { | |
a48f1fff | 26 | struct mtk_efuse_priv *priv = context; |
98e2c4ef CY |
27 | void __iomem *addr = priv->base + reg; |
28 | u8 *val = _val; | |
29 | int i; | |
ba360fd0 | 30 | |
98e2c4ef CY |
31 | for (i = 0; i < bytes; i++, val++) |
32 | *val = readb(addr + i); | |
ba360fd0 SK |
33 | |
34 | return 0; | |
35 | } | |
36 | ||
de6e0509 ADR |
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 | ||
4c7e4fe3 ACC |
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; | |
4dd5f60e | 73 | struct nvmem_config econfig = {}; |
a48f1fff | 74 | struct mtk_efuse_priv *priv; |
de6e0509 | 75 | const struct mtk_efuse_pdata *pdata; |
a48f1fff MY |
76 | |
77 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | |
78 | if (!priv) | |
79 | return -ENOMEM; | |
4c7e4fe3 | 80 | |
f5c97da8 | 81 | priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); |
a48f1fff MY |
82 | if (IS_ERR(priv->base)) |
83 | return PTR_ERR(priv->base); | |
4c7e4fe3 | 84 | |
de6e0509 | 85 | pdata = device_get_match_data(dev); |
2cc3b37f | 86 | econfig.add_legacy_fixed_of_cells = true; |
98e2c4ef CY |
87 | econfig.stride = 1; |
88 | econfig.word_size = 1; | |
4dd5f60e | 89 | econfig.reg_read = mtk_reg_read; |
4dd5f60e | 90 | econfig.size = resource_size(res); |
a48f1fff | 91 | econfig.priv = priv; |
4dd5f60e | 92 | econfig.dev = dev; |
de6e0509 ADR |
93 | if (pdata->uses_post_processing) |
94 | econfig.layout = &mtk_efuse_layout; | |
7e68a645 | 95 | nvmem = devm_nvmem_register(dev, &econfig); |
4c7e4fe3 | 96 | |
7e68a645 | 97 | return PTR_ERR_OR_ZERO(nvmem); |
4c7e4fe3 ACC |
98 | } |
99 | ||
de6e0509 ADR |
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 | ||
4c7e4fe3 | 108 | static const struct of_device_id mtk_efuse_of_match[] = { |
de6e0509 ADR |
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 }, | |
4c7e4fe3 ACC |
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, | |
4c7e4fe3 ACC |
118 | .driver = { |
119 | .name = "mediatek,efuse", | |
120 | .of_match_table = mtk_efuse_of_match, | |
121 | }, | |
122 | }; | |
564e7f87 ACC |
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 | ||
4c7e4fe3 ACC |
145 | MODULE_AUTHOR("Andrew-CT Chen <[email protected]>"); |
146 | MODULE_DESCRIPTION("Mediatek EFUSE driver"); | |
147 | MODULE_LICENSE("GPL v2"); |