blob: 99c5f27abef93efca8bc65c1f274563bf7284924 [file] [log] [blame]
Subbaraman Narayanamurthyc3d22c42018-01-22 18:39:02 -08001/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
David Collins8885f792017-01-26 14:36:34 -08002 *
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/module.h>
14#include <linux/slab.h>
15#include <linux/spmi.h>
16#include <linux/platform_device.h>
17#include <linux/regmap.h>
18#include <linux/err.h>
19#include <linux/qpnp/qpnp-revid.h>
20#include <linux/of.h>
21
22#define REVID_REVISION1 0x0
23#define REVID_REVISION2 0x1
24#define REVID_REVISION3 0x2
25#define REVID_REVISION4 0x3
26#define REVID_TYPE 0x4
27#define REVID_SUBTYPE 0x5
28#define REVID_STATUS1 0x8
29#define REVID_SPARE_0 0x60
Rama Krishna Phani A379c70e2017-07-30 19:04:31 +053030#define REVID_TP_REV 0xf1
David Collins8885f792017-01-26 14:36:34 -080031#define REVID_FAB_ID 0xf2
32
33#define QPNP_REVID_DEV_NAME "qcom,qpnp-revid"
34
35static const char *const pmic_names[] = {
36 [0] = "Unknown PMIC",
37 [PM8941_SUBTYPE] = "PM8941",
38 [PM8841_SUBTYPE] = "PM8841",
39 [PM8019_SUBTYPE] = "PM8019",
40 [PM8226_SUBTYPE] = "PM8226",
41 [PM8110_SUBTYPE] = "PM8110",
42 [PMA8084_SUBTYPE] = "PMA8084",
43 [PMI8962_SUBTYPE] = "PMI8962",
44 [PMD9635_SUBTYPE] = "PMD9635",
45 [PM8994_SUBTYPE] = "PM8994",
46 [PMI8994_SUBTYPE] = "PMI8994",
47 [PM8916_SUBTYPE] = "PM8916",
48 [PM8004_SUBTYPE] = "PM8004",
49 [PM8909_SUBTYPE] = "PM8909",
50 [PM2433_SUBTYPE] = "PM2433",
51 [PMD9655_SUBTYPE] = "PMD9655",
52 [PM8950_SUBTYPE] = "PM8950",
Anirudh Ghayalce54ec32018-03-04 19:33:38 +053053 [PM8953_SUBTYPE] = "PM8953",
David Collins8885f792017-01-26 14:36:34 -080054 [PMI8950_SUBTYPE] = "PMI8950",
55 [PMK8001_SUBTYPE] = "PMK8001",
56 [PMI8996_SUBTYPE] = "PMI8996",
57 [PM8998_SUBTYPE] = "PM8998",
58 [PMI8998_SUBTYPE] = "PMI8998",
59 [PM8005_SUBTYPE] = "PM8005",
60 [PM8937_SUBTYPE] = "PM8937",
61 [PM660L_SUBTYPE] = "PM660L",
62 [PM660_SUBTYPE] = "PM660",
Kiran Gunda8c383ac2017-12-07 11:16:12 +053063 [PMI632_SUBTYPE] = "PMI632",
David Collins8885f792017-01-26 14:36:34 -080064 [PMI8937_SUBTYPE] = "PMI8937",
Subbaraman Narayanamurthyc3d22c42018-01-22 18:39:02 -080065 [PM855_SUBTYPE] = "PM855",
66 [PM855B_SUBTYPE] = "PM855B",
67 [PM855L_SUBTYPE] = "PM855L",
David Collins8885f792017-01-26 14:36:34 -080068};
69
70struct revid_chip {
71 struct list_head link;
72 struct device_node *dev_node;
73 struct pmic_revid_data data;
74};
75
76static LIST_HEAD(revid_chips);
77static DEFINE_MUTEX(revid_chips_lock);
78
79static const struct of_device_id qpnp_revid_match_table[] = {
80 { .compatible = QPNP_REVID_DEV_NAME },
81 {}
82};
83
84static u8 qpnp_read_byte(struct regmap *regmap, u16 addr)
85{
86 int rc;
87 int val;
88
89 rc = regmap_read(regmap, addr, &val);
90 if (rc) {
91 pr_err("read failed rc=%d\n", rc);
92 return 0;
93 }
94 return (u8)val;
95}
96
97/**
98 * get_revid_data - Return the revision information of PMIC
99 * @dev_node: Pointer to the revid peripheral of the PMIC for which
100 * revision information is seeked
101 *
102 * CONTEXT: Should be called in non atomic context
103 *
104 * RETURNS: pointer to struct pmic_revid_data filled with the information
105 * about the PMIC revision
106 */
107struct pmic_revid_data *get_revid_data(struct device_node *dev_node)
108{
109 struct revid_chip *revid_chip;
110
111 if (!dev_node)
112 return ERR_PTR(-EINVAL);
113
114 mutex_lock(&revid_chips_lock);
115 list_for_each_entry(revid_chip, &revid_chips, link) {
116 if (dev_node == revid_chip->dev_node) {
117 mutex_unlock(&revid_chips_lock);
118 return &revid_chip->data;
119 }
120 }
121 mutex_unlock(&revid_chips_lock);
122 return ERR_PTR(-EINVAL);
123}
124EXPORT_SYMBOL(get_revid_data);
125
126#define PM8941_PERIPHERAL_SUBTYPE 0x01
127#define PM8226_PERIPHERAL_SUBTYPE 0x04
128#define PMD9655_PERIPHERAL_SUBTYPE 0x0F
129#define PMI8950_PERIPHERAL_SUBTYPE 0x11
130#define PMI8937_PERIPHERAL_SUBTYPE 0x37
131static size_t build_pmic_string(char *buf, size_t n, int sid,
132 u8 subtype, u8 rev1, u8 rev2, u8 rev3, u8 rev4)
133{
134 size_t pos = 0;
135 /*
136 * In early versions of PM8941 and PM8226, the major revision number
137 * started incrementing from 0 (eg 0 = v1.0, 1 = v2.0).
138 * Increment the major revision number here if the chip is an early
139 * version of PM8941 or PM8226.
140 */
141 if (((int)subtype == PM8941_PERIPHERAL_SUBTYPE
142 || (int)subtype == PM8226_PERIPHERAL_SUBTYPE)
143 && rev4 < 0x02)
144 rev4++;
145
146 pos += snprintf(buf + pos, n - pos, "PMIC@SID%d", sid);
147 if (subtype >= ARRAY_SIZE(pmic_names) || subtype == 0)
148 pos += snprintf(buf + pos, n - pos, ": %s (subtype: 0x%02X)",
149 pmic_names[0], subtype);
150 else
151 pos += snprintf(buf + pos, n - pos, ": %s",
152 pmic_names[subtype]);
153 pos += snprintf(buf + pos, n - pos, " v%d.%d", rev4, rev3);
154 if (rev2 || rev1)
155 pos += snprintf(buf + pos, n - pos, ".%d", rev2);
156 if (rev1)
157 pos += snprintf(buf + pos, n - pos, ".%d", rev1);
158 return pos;
159}
160
161#define PMIC_PERIPHERAL_TYPE 0x51
162#define PMIC_STRING_MAXLENGTH 80
163static int qpnp_revid_probe(struct platform_device *pdev)
164{
165 u8 rev1, rev2, rev3, rev4, pmic_type, pmic_subtype, pmic_status;
Rama Krishna Phani A379c70e2017-07-30 19:04:31 +0530166 u8 option1, option2, option3, option4, spare0;
David Collins8885f792017-01-26 14:36:34 -0800167 unsigned int base;
Rama Krishna Phani A379c70e2017-07-30 19:04:31 +0530168 int rc, fab_id, tp_rev;
David Collins8885f792017-01-26 14:36:34 -0800169 char pmic_string[PMIC_STRING_MAXLENGTH] = {'\0'};
170 struct revid_chip *revid_chip;
171 struct regmap *regmap;
172
173 regmap = dev_get_regmap(pdev->dev.parent, NULL);
174 if (!regmap) {
175 dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
176 return -EINVAL;
177 }
178
179 rc = of_property_read_u32(pdev->dev.of_node, "reg", &base);
180 if (rc < 0) {
181 dev_err(&pdev->dev,
182 "Couldn't find reg in node = %s rc = %d\n",
183 pdev->dev.of_node->full_name, rc);
184 return rc;
185 }
186 pmic_type = qpnp_read_byte(regmap, base + REVID_TYPE);
187 if (pmic_type != PMIC_PERIPHERAL_TYPE) {
188 pr_err("Invalid REVID peripheral type: %02X\n", pmic_type);
189 return -EINVAL;
190 }
191
192 rev1 = qpnp_read_byte(regmap, base + REVID_REVISION1);
193 rev2 = qpnp_read_byte(regmap, base + REVID_REVISION2);
194 rev3 = qpnp_read_byte(regmap, base + REVID_REVISION3);
195 rev4 = qpnp_read_byte(regmap, base + REVID_REVISION4);
196
197 pmic_subtype = qpnp_read_byte(regmap, base + REVID_SUBTYPE);
198 if (pmic_subtype != PMD9655_PERIPHERAL_SUBTYPE)
199 pmic_status = qpnp_read_byte(regmap, base + REVID_STATUS1);
200 else
201 pmic_status = 0;
202
203 /* special case for PMI8937 */
204 if (pmic_subtype == PMI8950_PERIPHERAL_SUBTYPE) {
205 /* read spare register */
206 spare0 = qpnp_read_byte(regmap, base + REVID_SPARE_0);
207 if (spare0)
208 pmic_subtype = PMI8937_PERIPHERAL_SUBTYPE;
209 }
210
211 if (of_property_read_bool(pdev->dev.of_node, "qcom,fab-id-valid"))
212 fab_id = qpnp_read_byte(regmap, base + REVID_FAB_ID);
213 else
214 fab_id = -EINVAL;
215
Rama Krishna Phani A379c70e2017-07-30 19:04:31 +0530216 if (of_property_read_bool(pdev->dev.of_node, "qcom,tp-rev-valid"))
217 tp_rev = qpnp_read_byte(regmap, base + REVID_TP_REV);
218 else
219 tp_rev = -EINVAL;
220
David Collins8885f792017-01-26 14:36:34 -0800221 revid_chip = devm_kzalloc(&pdev->dev, sizeof(struct revid_chip),
222 GFP_KERNEL);
223 if (!revid_chip)
224 return -ENOMEM;
225
226 revid_chip->dev_node = pdev->dev.of_node;
227 revid_chip->data.rev1 = rev1;
228 revid_chip->data.rev2 = rev2;
229 revid_chip->data.rev3 = rev3;
230 revid_chip->data.rev4 = rev4;
231 revid_chip->data.pmic_subtype = pmic_subtype;
232 revid_chip->data.pmic_type = pmic_type;
233 revid_chip->data.fab_id = fab_id;
Rama Krishna Phani A379c70e2017-07-30 19:04:31 +0530234 revid_chip->data.tp_rev = tp_rev;
David Collins8885f792017-01-26 14:36:34 -0800235
236 if (pmic_subtype < ARRAY_SIZE(pmic_names))
237 revid_chip->data.pmic_name = pmic_names[pmic_subtype];
238 else
239 revid_chip->data.pmic_name = pmic_names[0];
240
241 mutex_lock(&revid_chips_lock);
242 list_add(&revid_chip->link, &revid_chips);
243 mutex_unlock(&revid_chips_lock);
244
245 option1 = pmic_status & 0x3;
246 option2 = (pmic_status >> 2) & 0x3;
247 option3 = (pmic_status >> 4) & 0x3;
248 option4 = (pmic_status >> 6) & 0x3;
249
250 build_pmic_string(pmic_string, PMIC_STRING_MAXLENGTH,
251 to_spmi_device(pdev->dev.parent)->usid,
252 pmic_subtype, rev1, rev2, rev3, rev4);
253 pr_info("%s options: %d, %d, %d, %d\n",
254 pmic_string, option1, option2, option3, option4);
255 return 0;
256}
257
258static struct platform_driver qpnp_revid_driver = {
259 .probe = qpnp_revid_probe,
260 .driver = {
261 .name = QPNP_REVID_DEV_NAME,
262 .owner = THIS_MODULE,
263 .of_match_table = qpnp_revid_match_table,
264 },
265};
266
267static int __init qpnp_revid_init(void)
268{
269 return platform_driver_register(&qpnp_revid_driver);
270}
271
272static void __exit qpnp_revid_exit(void)
273{
274 return platform_driver_unregister(&qpnp_revid_driver);
275}
276
277subsys_initcall(qpnp_revid_init);
278module_exit(qpnp_revid_exit);
279
280MODULE_DESCRIPTION("QPNP REVID DRIVER");
281MODULE_LICENSE("GPL v2");
282MODULE_ALIAS("platform:" QPNP_REVID_DEV_NAME);