blob: ffdd726c9edab6af963b96908c6e567cdbefb261 [file] [log] [blame]
Umang Agrawald13b5fe2017-12-19 12:09:31 +05301/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
2 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are
5 * met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above
9 * copyright notice, this list of conditions and the following
10 * disclaimer in the documentation and/or other materials provided
11 * with the distribution.
12 * * Neither the name of The Linux Foundation, Inc. nor the names of its
13 * contributors may be used to endorse or promote products derived
14 * from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <stdio.h>
30#include <string.h>
31#include <stdlib.h>
32#include <err.h>
33#include <debug.h>
34#include <spmi.h>
35#include <qpnp_wled.h>
36#include <qpnp_lcdb.h>
37#include <qtimer.h>
38#include <target.h>
39
40struct qpnp_lcdb *lcdb;
41
Umang Agrawal2e447c82018-09-11 15:00:47 +053042static int qpnp_lcdb_set_voltage(struct qpnp_lcdb *lcdb, uint32_t ldo_v,
43 uint32_t ncp_v)
Umang Agrawald13b5fe2017-12-19 12:09:31 +053044{
45 int rc = -1;
46 uint32_t new_uV, boost_ref_volt;
47 uint8_t val;
48
Umang Agrawal2e447c82018-09-11 15:00:47 +053049 /* Set LDO voltage */
50 if (ldo_v < lcdb->ldo_init_volt) {
Umang Agrawald13b5fe2017-12-19 12:09:31 +053051 dprintf(CRITICAL, "qpnp_ldo_set_voltage failed, min_uV %d is "
52 "less than the minimum supported voltage %d\n",
Umang Agrawal2e447c82018-09-11 15:00:47 +053053 ldo_v, lcdb->ldo_init_volt);
Umang Agrawald13b5fe2017-12-19 12:09:31 +053054 return rc;
55 }
56
Umang Agrawal2e447c82018-09-11 15:00:47 +053057 val = ((ldo_v - lcdb->ldo_init_volt) +
Umang Agrawald13b5fe2017-12-19 12:09:31 +053058 LDO_VREG_STEP_SIZE_UV - 1) / LDO_VREG_STEP_SIZE_UV;
59 new_uV = val * LDO_VREG_STEP_SIZE_UV + lcdb->ldo_init_volt;
60 if (new_uV > lcdb->ldo_max_volt) {
61 dprintf(CRITICAL, "qpnp_ldo_set_voltage unable to set voltage "
62 "(%d %d)\n", lcdb->ldo_min_volt, lcdb->ldo_max_volt);
63 return rc;
64 }
65
66 /* Value adjustment as initial 1V has 100mV step and rest 50mV step */
67 if ( val > 18 )
68 val = val - 9;
69 else
70 val = val / 2;
71
72 pmic_spmi_reg_mask_write(lcdb->lcdb_base +
73 QPNP_LCDB_LDO_OUTPUT_VOLTAGE_REG,
74 QPNP_LCDB_LDO_OUTPUT_VOLTAGE_MASK, val);
75
76 udelay(2);
77
78 /* Set NCP voltage */
Umang Agrawal2e447c82018-09-11 15:00:47 +053079 if (ncp_v < lcdb->ncp_init_volt) {
Umang Agrawald13b5fe2017-12-19 12:09:31 +053080 dprintf(CRITICAL, "qpnp_ncp_set_voltage failed, min_uV %d is "
81 "less than the minimum supported voltage %d\n",
Umang Agrawal2e447c82018-09-11 15:00:47 +053082 ncp_v, lcdb->ncp_init_volt);
Umang Agrawald13b5fe2017-12-19 12:09:31 +053083 return rc;
84 }
85
Umang Agrawal2e447c82018-09-11 15:00:47 +053086 val = ((ncp_v - lcdb->ncp_init_volt) +
Umang Agrawald13b5fe2017-12-19 12:09:31 +053087 NCP_VREG_STEP_SIZE_UV - 1) / NCP_VREG_STEP_SIZE_UV;
88 new_uV = val * NCP_VREG_STEP_SIZE_UV + lcdb->ncp_init_volt;
89 if (new_uV > lcdb->ncp_max_volt) {
90 dprintf(CRITICAL, "qpnp_ncp_set_voltage unable to set voltage "
91 "(%d %d)\n", lcdb->ncp_min_volt, lcdb->ncp_max_volt);
92 return rc;
93 }
94
95 /* Value adjustment as initial 1V has 100mV step and rest 50mV step */
96 if ( val > 18 )
97 val = val - 9;
98 else
99 val = val / 2;
100
101 pmic_spmi_reg_mask_write(lcdb->lcdb_base +
102 QPNP_LCDB_NCP_OUTPUT_VOLTAGE_REG,
103 QPNP_LCDB_NCP_OUTPUT_VOLTAGE_MASK, val);
104
105 udelay(2);
106
107 /* Set Boost voltage */
108 boost_ref_volt = ((lcdb->ldo_max_volt > lcdb->ncp_max_volt) ?
109 lcdb->ldo_max_volt : lcdb->ncp_max_volt) +
110 LCDB_BOOST_HEADROOM_VOLT_UV;
111 if (boost_ref_volt < lcdb->bst_init_volt)
112 boost_ref_volt = lcdb->bst_init_volt;
113
114 val = ((boost_ref_volt - lcdb->bst_init_volt) +
115 BST_VREG_STEP_SIZE_UV - 1) / BST_VREG_STEP_SIZE_UV;
116 new_uV = val * BST_VREG_STEP_SIZE_UV + lcdb->bst_init_volt;
117
118 pmic_spmi_reg_mask_write(lcdb->lcdb_base +
119 QPNP_LCDB_BST_OUTPUT_VOLTAGE_REG,
120 QPNP_LCDB_BST_OUTPUT_VOLTAGE_MASK, val);
121
122 return 0;
123}
124
Umang Agrawal2e447c82018-09-11 15:00:47 +0530125static int qpnp_lcdb_step_voltage(struct qpnp_lcdb *lcdb,
126 uint32_t step_start_uv)
Umang Agrawald13b5fe2017-12-19 12:09:31 +0530127{
128 int rc = 0;
Umang Agrawal2e447c82018-09-11 15:00:47 +0530129 uint32_t target_ldo_v, target_ncp_v, i;
130
131 for (i = step_start_uv; i <= LCDB_LDO_MAX_VOLTAGE_UV;
132 i += LCDB_STEP_UV) {
133 target_ldo_v = min(lcdb->ldo_min_volt, i);
134 target_ncp_v = min(lcdb->ncp_min_volt, i);
135 rc = qpnp_lcdb_set_voltage(lcdb, target_ldo_v, target_ncp_v);
136
137 if (lcdb->ldo_min_volt <= i && lcdb->ncp_min_volt <= i)
138 break;
139
140 /* 1ms wait */
141 mdelay(1);
142 }
143
144 return rc;
145}
146
147static int qpnp_lcdb_config(struct qpnp_lcdb *lcdb)
148{
Umang Agrawald13b5fe2017-12-19 12:09:31 +0530149 uint8_t reg = 0;
150
Umang Agrawald13b5fe2017-12-19 12:09:31 +0530151 if (lcdb->lcdb_pwrup_dly_ms > QPNP_LCDB_PWRUP_DLY_MAX_MS)
152 lcdb->lcdb_pwrup_dly_ms = QPNP_LCDB_PWRUP_DLY_MAX_MS;
153
154 if (lcdb->lcdb_pwrdn_dly_ms > QPNP_LCDB_PWRDN_DLY_MAX_MS)
155 lcdb->lcdb_pwrdn_dly_ms = QPNP_LCDB_PWRDN_DLY_MAX_MS;
156
157 reg = pmic_spmi_reg_read(lcdb->lcdb_base +
158 QPNP_LCDB_PWRUP_PWRDN_CTL_REG);
159
160 /* Set power up delay */
161 reg &= QPNP_LCDB_PWRUP_DLY_MASK;
162 reg |= (lcdb->lcdb_pwrup_dly_ms << LCDB_PWRUP_DLY_SHIFT);
163
164 /* Set power down delay */
Ashay Jaiswal496c2e42018-05-23 11:50:32 +0530165 reg &= ~(QPNP_LCDB_PWRDN_DLY_MASK);
Umang Agrawald13b5fe2017-12-19 12:09:31 +0530166 reg |= (lcdb->lcdb_pwrdn_dly_ms);
167
168 pmic_spmi_reg_write(lcdb->lcdb_base + QPNP_LCDB_PWRUP_PWRDN_CTL_REG,
169 reg);
170
171 /* Make LCDB module ready */
172 pmic_spmi_reg_mask_write(lcdb->lcdb_base + QPNP_LCDB_MODULE_RDY_REG,
173 QPNP_LCDB_MODULE_RDY_MASK, LCDB_MODULE_RDY);
174
Umang Agrawal2e447c82018-09-11 15:00:47 +0530175 return 0;
Umang Agrawald13b5fe2017-12-19 12:09:31 +0530176}
177
178static int qpnp_lcdb_setup(struct qpnp_lcdb *lcdb,
179 struct qpnp_wled_config_data *config)
180{
181 lcdb->lcdb_base = QPNP_LCDB_BASE;
182 lcdb->lcdb_pwrup_dly_ms = config->pwr_up_delay;
183 lcdb->lcdb_pwrdn_dly_ms = config->pwr_down_delay;
184 lcdb->ldo_min_volt = config->lab_min_volt;
185 lcdb->ldo_max_volt = config->lab_max_volt;
186 lcdb->ldo_init_volt = LCDB_LDO_INIT_VOLTAGE_UV;
187 lcdb->ncp_min_volt = config->ibb_min_volt;
188 lcdb->ncp_max_volt = config->ibb_max_volt;
189 lcdb->ncp_init_volt = LCDB_NCP_INIT_VOLTAGE_UV;
190 lcdb->bst_init_volt = LCDB_BOOST_INIT_VOLTAGE_UV;
191
192 return 0;
193}
194
Umang Agrawal2e447c82018-09-11 15:00:47 +0530195int qpnp_lcdb_enable(bool state)
196{
197 int rc = 0;
198 uint32_t target_ldo_v, target_ncp_v;
199
200 if (!lcdb) {
201 dprintf(CRITICAL, "%s: lcdb is not initialized yet\n", __func__);
202 return ERROR;
203 }
204
205 if (state) {
206 /*
207 * Set LDO/NCP voltage step wise (in steps of 500mV) as a part
208 * of SW WA to prevent loss in accuracy in ADC measurement of
209 * VBATT voltage.
210 * Set minimum of spec voltage or 4.5V.
211 */
212 target_ldo_v = min(lcdb->ldo_min_volt, (lcdb->ldo_init_volt +
213 LCDB_STEP_UV));
214 target_ncp_v = min(lcdb->ncp_min_volt, (lcdb->ncp_init_volt +
215 LCDB_STEP_UV));
216 rc = qpnp_lcdb_set_voltage(lcdb, target_ldo_v, target_ncp_v);
217
218 /* Enable LCDB */
219 pmic_spmi_reg_mask_write(lcdb->lcdb_base + QPNP_LCDB_ENABLE_CTL_REG,
220 QPNP_LCDB_ENABLE_CTL_MASK, (state << LCDB_ENABLE_SHIFT));
221
222 /* Initial delay of 10ms */
223 mdelay(10);
224
225 /* Start voltage stepping from 5V onwards */
226 if (lcdb->ldo_min_volt > (lcdb->ldo_init_volt + LCDB_STEP_UV) ||
227 lcdb->ncp_min_volt > (lcdb->ncp_init_volt + LCDB_STEP_UV))
228 rc = qpnp_lcdb_step_voltage(lcdb, target_ldo_v +
229 LCDB_STEP_UV);
230 } else {
231 pmic_spmi_reg_mask_write(lcdb->lcdb_base + QPNP_LCDB_ENABLE_CTL_REG,
232 QPNP_LCDB_ENABLE_CTL_MASK, (state << LCDB_ENABLE_SHIFT));
233 }
234
235 return rc;
236}
237
Umang Agrawald13b5fe2017-12-19 12:09:31 +0530238int qpnp_lcdb_init(struct qpnp_wled_config_data *config)
239{
240 int rc;
241
242 if (!target_is_pmi_enabled())
243 return ERR_NOT_FOUND;
244
245 lcdb = malloc(sizeof(struct qpnp_lcdb));
246 if (!lcdb)
247 return ERR_NO_MEMORY;
248
249 memset(lcdb, 0, sizeof(struct qpnp_lcdb));
250
251 rc = qpnp_lcdb_setup(lcdb, config);
252 if(rc) {
253 dprintf(CRITICAL, "Setting LCDB parameters failed\n");
254 return rc;
255 }
256
257 rc = qpnp_lcdb_config(lcdb);
258 if (rc) {
259 dprintf(CRITICAL, "lcdb config failed\n");
260 return rc;
261 }
262
263 return rc;
264}