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 <andrew-ct.chen@mediatek.com> | |
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 | ||
1172460e MR |
48 | static void mtk_efuse_fixup_dt_cell_info(struct nvmem_device *nvmem, |
49 | struct nvmem_cell_info *cell) | |
de6e0509 ADR |
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 | ||
4c7e4fe3 ACC |
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; | |
4dd5f60e | 68 | struct nvmem_config econfig = {}; |
a48f1fff | 69 | struct mtk_efuse_priv *priv; |
de6e0509 | 70 | const struct mtk_efuse_pdata *pdata; |
a48f1fff MY |
71 | |
72 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | |
73 | if (!priv) | |
74 | return -ENOMEM; | |
4c7e4fe3 | 75 | |
f5c97da8 | 76 | priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); |
a48f1fff MY |
77 | if (IS_ERR(priv->base)) |
78 | return PTR_ERR(priv->base); | |
4c7e4fe3 | 79 | |
de6e0509 | 80 | pdata = device_get_match_data(dev); |
2cc3b37f | 81 | econfig.add_legacy_fixed_of_cells = true; |
98e2c4ef CY |
82 | econfig.stride = 1; |
83 | econfig.word_size = 1; | |
4dd5f60e | 84 | econfig.reg_read = mtk_reg_read; |
4dd5f60e | 85 | econfig.size = resource_size(res); |
a48f1fff | 86 | econfig.priv = priv; |
4dd5f60e | 87 | econfig.dev = dev; |
de6e0509 | 88 | if (pdata->uses_post_processing) |
1172460e | 89 | econfig.fixup_dt_cell_info = &mtk_efuse_fixup_dt_cell_info; |
7e68a645 | 90 | nvmem = devm_nvmem_register(dev, &econfig); |
4c7e4fe3 | 91 | |
7e68a645 | 92 | return PTR_ERR_OR_ZERO(nvmem); |
4c7e4fe3 ACC |
93 | } |
94 | ||
de6e0509 ADR |
95 | static const struct mtk_efuse_pdata mtk_mt8186_efuse_pdata = { |
96 | .uses_post_processing = true, | |
97 | }; | |
98 | ||
99 | static const struct mtk_efuse_pdata mtk_efuse_pdata = { | |
100 | .uses_post_processing = false, | |
101 | }; | |
102 | ||
4c7e4fe3 | 103 | static const struct of_device_id mtk_efuse_of_match[] = { |
de6e0509 ADR |
104 | { .compatible = "mediatek,mt8173-efuse", .data = &mtk_efuse_pdata }, |
105 | { .compatible = "mediatek,mt8186-efuse", .data = &mtk_mt8186_efuse_pdata }, | |
106 | { .compatible = "mediatek,efuse", .data = &mtk_efuse_pdata }, | |
4c7e4fe3 ACC |
107 | {/* sentinel */}, |
108 | }; | |
109 | MODULE_DEVICE_TABLE(of, mtk_efuse_of_match); | |
110 | ||
111 | static struct platform_driver mtk_efuse_driver = { | |
112 | .probe = mtk_efuse_probe, | |
4c7e4fe3 ACC |
113 | .driver = { |
114 | .name = "mediatek,efuse", | |
115 | .of_match_table = mtk_efuse_of_match, | |
116 | }, | |
117 | }; | |
564e7f87 ACC |
118 | |
119 | static int __init mtk_efuse_init(void) | |
120 | { | |
121 | int ret; | |
122 | ||
123 | ret = platform_driver_register(&mtk_efuse_driver); | |
124 | if (ret) { | |
125 | pr_err("Failed to register efuse driver\n"); | |
126 | return ret; | |
127 | } | |
128 | ||
129 | return 0; | |
130 | } | |
131 | ||
132 | static void __exit mtk_efuse_exit(void) | |
133 | { | |
134 | return platform_driver_unregister(&mtk_efuse_driver); | |
135 | } | |
136 | ||
137 | subsys_initcall(mtk_efuse_init); | |
138 | module_exit(mtk_efuse_exit); | |
139 | ||
4c7e4fe3 ACC |
140 | MODULE_AUTHOR("Andrew-CT Chen <andrew-ct.chen@mediatek.com>"); |
141 | MODULE_DESCRIPTION("Mediatek EFUSE driver"); | |
142 | MODULE_LICENSE("GPL v2"); |