blob: 01a89cbb5e6b37a81a898edd5cd794e630066ed9 [file] [log] [blame]
Anirudh Ghayal41ccba0f2017-11-13 20:31:51 +05301/* Copyright (c) 2017 The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/device.h>
14#include <linux/module.h>
15#include <linux/of.h>
16#include <linux/of_platform.h>
17#include <linux/nvmem-provider.h>
18#include <linux/regmap.h>
19
20#define SDAM_MEM_START 0x40
21#define REGISTER_MAP_ID 0x40
22#define REGISTER_MAP_VERSION 0x41
23#define SDAM_SIZE 0x44
24#define SDAM_PBS_TRIG_SET 0xE5
25#define SDAM_PBS_TRIG_CLR 0xE6
26
27struct sdam_chip {
28 struct platform_device *pdev;
29 struct regmap *regmap;
30 int base;
31 int size;
32};
33
34/* read only register offsets */
35static const u8 sdam_ro_map[] = {
36 REGISTER_MAP_ID,
37 REGISTER_MAP_VERSION,
38 SDAM_SIZE
39};
40
41static bool is_valid(struct sdam_chip *sdam, unsigned int offset, size_t len)
42{
43 int sdam_mem_end = SDAM_MEM_START + sdam->size - 1;
44
45 if (!len)
46 return false;
47
48 if (offset >= SDAM_MEM_START && offset <= sdam_mem_end
49 && (offset + len - 1) <= sdam_mem_end)
50 return true;
51 else if ((offset == SDAM_PBS_TRIG_SET || offset == SDAM_PBS_TRIG_CLR)
52 && (len == 1))
53 return true;
54
55 return false;
56}
57
58static bool is_ro(unsigned int offset, size_t len)
59{
60 int i;
61
62 for (i = 0; i < ARRAY_SIZE(sdam_ro_map); i++)
63 if (offset <= sdam_ro_map[i] && (offset + len) > sdam_ro_map[i])
64 return true;
65
66 return false;
67}
68
69static int sdam_read(void *priv, unsigned int offset, void *val, size_t bytes)
70{
71 struct sdam_chip *sdam = priv;
72 int rc;
73
74 if (!is_valid(sdam, offset, bytes)) {
75 pr_err("Invalid SDAM offset 0x%02x len=%zd\n", offset, bytes);
76 return -EINVAL;
77 }
78
79 rc = regmap_bulk_read(sdam->regmap, sdam->base + offset, val, bytes);
80 if (rc < 0)
81 pr_err("Failed to read SDAM offset 0x%02x len=%zd, rc=%d\n",
82 offset, bytes, rc);
83
84 return rc;
85}
86
87static int sdam_write(void *priv, unsigned int offset, void *val, size_t bytes)
88{
89 struct sdam_chip *sdam = priv;
90 int rc;
91
92 if (!is_valid(sdam, offset, bytes)) {
93 pr_err("Invalid SDAM offset 0x%02x len=%zd\n", offset, bytes);
94 return -EINVAL;
95 }
96
97 if (is_ro(offset, bytes)) {
98 pr_err("Invalid write offset 0x%02x len=%zd\n", offset, bytes);
99 return -EINVAL;
100 }
101
102 rc = regmap_bulk_write(sdam->regmap, sdam->base + offset, val, bytes);
103 if (rc < 0)
104 pr_err("Failed to write SDAM offset 0x%02x len=%zd, rc=%d\n",
105 offset, bytes, rc);
106
107 return rc;
108}
109
110static int sdam_probe(struct platform_device *pdev)
111{
112 struct sdam_chip *sdam;
113 struct nvmem_device *nvmem;
114 struct nvmem_config *sdam_config;
115 unsigned int val = 0;
116 int rc;
117
118 sdam = devm_kzalloc(&pdev->dev, sizeof(*sdam), GFP_KERNEL);
119 if (!sdam)
120 return -ENOMEM;
121
122 sdam_config = devm_kzalloc(&pdev->dev, sizeof(*sdam_config),
123 GFP_KERNEL);
124 if (!sdam_config)
125 return -ENOMEM;
126
127 sdam->regmap = dev_get_regmap(pdev->dev.parent, NULL);
128 if (!sdam->regmap) {
129 pr_err("Failed to get regmap handle\n");
130 return -ENXIO;
131 }
132
133 rc = of_property_read_u32(pdev->dev.of_node, "reg", &sdam->base);
134 if (rc < 0) {
135 pr_err("Failed to get SDAM base, rc=%d\n", rc);
136 return -EINVAL;
137 }
138
139 rc = regmap_read(sdam->regmap, sdam->base + SDAM_SIZE, &val);
140 if (rc < 0) {
141 pr_err("Failed to read SDAM_SIZE rc=%d\n", rc);
142 return -EINVAL;
143 }
144 sdam->size = val * 32;
145
146 sdam_config->dev = &pdev->dev;
147 sdam_config->name = "spmi_sdam";
148 sdam_config->id = pdev->id;
149 sdam_config->owner = THIS_MODULE,
150 sdam_config->stride = 1;
151 sdam_config->word_size = 1;
152 sdam_config->reg_read = sdam_read;
153 sdam_config->reg_write = sdam_write;
154 sdam_config->priv = sdam;
155
156 nvmem = nvmem_register(sdam_config);
157 if (IS_ERR(nvmem)) {
158 pr_err("Failed to register SDAM nvmem device rc=%ld\n",
159 PTR_ERR(nvmem));
160 return -ENXIO;
161 }
162 platform_set_drvdata(pdev, nvmem);
163
164 pr_info("SDAM base=0x%04x size=%d registered successfully\n",
165 sdam->base, sdam->size);
166
167 return 0;
168}
169
170static int sdam_remove(struct platform_device *pdev)
171{
172 struct nvmem_device *nvmem = platform_get_drvdata(pdev);
173
174 return nvmem_unregister(nvmem);
175}
176
177static const struct of_device_id sdam_match_table[] = {
178 {.compatible = "qcom,spmi-sdam"},
179 {},
180};
181
182static struct platform_driver sdam_driver = {
183 .driver = {
184 .name = "qcom,spmi-sdam",
185 .owner = THIS_MODULE,
186 .of_match_table = sdam_match_table,
187 },
188 .probe = sdam_probe,
189 .remove = sdam_remove,
190};
191
192static int __init sdam_init(void)
193{
194 return platform_driver_register(&sdam_driver);
195}
196subsys_initcall(sdam_init);
197
198static void __exit sdam_exit(void)
199{
200 return platform_driver_unregister(&sdam_driver);
201}
202module_exit(sdam_exit);
203
204MODULE_DESCRIPTION("QCOM SPMI SDAM driver");
205MODULE_LICENSE("GPL v2");