653823259c07f57a88c349d1fb6c967da67dd9d2
[linux-3.10.git] / drivers / power / reset / palmas-poweroff.c
1 /*
2  * palmas-poweroff.c : Power off and reset for Palma device.
3  *
4  * Copyright (c) 2013, NVIDIA Corporation.
5  *
6  * Author: Laxman Dewangan <ldewangan@nvidia.com>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation version 2.
11  *
12  * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
13  * whether express or implied; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20  * 02111-1307, USA
21  */
22
23 #include <linux/kernel.h>
24 #include <linux/errno.h>
25 #include <linux/init.h>
26 #include <linux/module.h>
27 #include <linux/platform_device.h>
28 #include <linux/mfd/palmas.h>
29 #include <linux/power/reset/system-pmic.h>
30
31 struct palmas_pm {
32         struct device *dev;
33         struct palmas *palmas;
34         struct system_pmic_dev *system_pmic_dev;
35         int num_int_mask_regs;
36         int int_mask_reg_add[PALMAS_MAX_INTERRUPT_MASK_REG];
37         int int_status_reg_add[PALMAS_MAX_INTERRUPT_MASK_REG];
38         int int_mask_val[PALMAS_MAX_INTERRUPT_MASK_REG];
39 };
40
41 static void palmas_power_off(void *drv_data)
42 {
43         struct palmas_pm *palmas_pm = drv_data;
44         struct palmas *palmas = palmas_pm->palmas;
45         unsigned int val;
46         int i;
47         int ret;
48
49         for (i = 0; i < palmas_pm->num_int_mask_regs; ++i) {
50                 ret = palmas_write(palmas, PALMAS_INTERRUPT_BASE,
51                                 palmas_pm->int_mask_reg_add[i],
52                                 palmas_pm->int_mask_val[i]);
53                 if (ret < 0)
54                         dev_err(palmas_pm->dev,
55                                 "register 0x%02x write failed: %d\n",
56                                 palmas_pm->int_mask_reg_add[i], ret);
57
58                 ret = palmas_read(palmas, PALMAS_INTERRUPT_BASE,
59                                         palmas_pm->int_status_reg_add[i], &val);
60                 if (ret < 0)
61                         dev_err(palmas_pm->dev,
62                                 "register 0x%02x read failed: %d\n",
63                                 palmas_pm->int_status_reg_add[i], ret);
64         }
65
66         /* Mask all COLD RST condition */
67         palmas_write(palmas, PALMAS_PMU_CONTROL_BASE,
68                                 PALMAS_SWOFF_COLDRST, 0x0);
69
70         dev_info(palmas_pm->dev, "Powering off the device\n");
71
72         /* Power off the device */
73         palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE,
74                                 PALMAS_DEV_CTRL, 1, 0);
75 }
76
77 static void palmas_power_reset(void *drv_data)
78 {
79         struct palmas_pm *palmas_pm = drv_data;
80         struct palmas *palmas = palmas_pm->palmas;
81         unsigned int val;
82         int i;
83         int ret;
84
85         for (i = 0; i < palmas_pm->num_int_mask_regs; ++i) {
86                 ret = palmas_write(palmas, PALMAS_INTERRUPT_BASE,
87                                 palmas_pm->int_mask_reg_add[i],
88                                 palmas_pm->int_mask_val[i]);
89                 if (ret < 0)
90                         dev_err(palmas_pm->dev,
91                                 "register 0x%02x write failed: %d\n",
92                                 palmas_pm->int_mask_reg_add[i], ret);
93
94                 ret = palmas_read(palmas, PALMAS_INTERRUPT_BASE,
95                                         palmas_pm->int_status_reg_add[i], &val);
96                 if (ret < 0)
97                         dev_err(palmas_pm->dev,
98                                 "register 0x%02x read failed: %d\n",
99                                 palmas_pm->int_status_reg_add[i], ret);
100         }
101
102         /* SW-WAR for ES Version 2.1, 2.0 and 1.0 */
103         if (palmas_is_es_version_or_less(palmas, 2, 1)) {
104                 dev_info(palmas_pm->dev, "Resetting Palmas through RTC\n");
105                 ret = palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE,
106                                 PALMAS_DEV_CTRL, PALMAS_DEV_CTRL_SW_RST, 0);
107                 if (ret < 0) {
108                         dev_err(palmas_pm->dev,
109                                 "DEV_CTRL update failed: %d\n", ret);
110                         goto reset_direct;
111                 }
112
113                 ret = palmas_update_bits(palmas, PALMAS_RTC_BASE,
114                                 PALMAS_RTC_INTERRUPTS_REG,
115                                 PALMAS_RTC_INTERRUPTS_REG_IT_TIMER,
116                                 PALMAS_RTC_INTERRUPTS_REG_IT_TIMER);
117                 if (ret < 0) {
118                         dev_err(palmas_pm->dev,
119                                 "RTC_INTERRUPTS update failed: %d\n", ret);
120                         goto reset_direct;
121                 }
122
123                 ret = palmas_update_bits(palmas, PALMAS_RTC_BASE,
124                         PALMAS_RTC_CTRL_REG, PALMAS_RTC_CTRL_REG_STOP_RTC,
125                         PALMAS_RTC_CTRL_REG_STOP_RTC);
126                 if (ret < 0) {
127                         dev_err(palmas_pm->dev,
128                                 "RTC_CTRL_REG update failed: %d\n", ret);
129                         goto reset_direct;
130                 }
131
132                 ret = palmas_update_bits(palmas, PALMAS_INTERRUPT_BASE,
133                                 PALMAS_INT2_MASK, PALMAS_INT2_MASK_RTC_TIMER, 0);
134                 if (ret < 0) {
135                         dev_err(palmas_pm->dev,
136                                 "INT2_MASK update failed: %d\n", ret);
137                         goto reset_direct;
138                 }
139
140                 ret = palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE,
141                                 PALMAS_DEV_CTRL, PALMAS_DEV_CTRL_SW_RST,
142                                 PALMAS_DEV_CTRL_SW_RST);
143                 if (ret < 0) {
144                         dev_err(palmas_pm->dev,
145                                 "DEV_CTRL update failed: %d\n", ret);
146                         goto reset_direct;
147                 }
148                 return;
149         }
150
151 reset_direct:
152         dev_info(palmas_pm->dev, "Power reset the device\n");
153         palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE,
154                                 PALMAS_DEV_CTRL, 0x2, 0x2);
155 }
156
157 static struct system_pmic_ops palmas_pm_ops = {
158         .power_off = palmas_power_off,
159         .power_reset = palmas_power_reset,
160 };
161
162 static int palmas_pm_probe(struct platform_device *pdev)
163 {
164         struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
165         struct palmas_pm *palmas_pm = NULL;
166         struct palmas_platform_data *palmas_pdata;
167         struct palmas_pm_platform_data *pm_pdata = NULL;
168         struct system_pmic_config config;
169         int i;
170
171         palmas_pm = devm_kzalloc(&pdev->dev, sizeof(*palmas_pm),
172                         GFP_KERNEL);
173         if (!palmas_pm) {
174                 dev_err(&pdev->dev, "Memory allocation failed.\n");
175                 return -ENOMEM;
176         }
177
178         palmas_pm->dev = &pdev->dev;
179         palmas_pm->palmas = palmas;
180         platform_set_drvdata(pdev, palmas_pm);
181
182         palmas_pm->num_int_mask_regs = PALMAS_MAX_INTERRUPT_MASK_REG;
183         if (palmas->id != TPS80036)
184                 palmas_pm->num_int_mask_regs = 4;
185
186         palmas_pm->int_mask_reg_add[0] = PALMAS_INT1_MASK;
187         palmas_pm->int_mask_reg_add[1] = PALMAS_INT2_MASK;
188         palmas_pm->int_mask_reg_add[2] = PALMAS_INT3_MASK;
189         palmas_pm->int_mask_reg_add[3] = PALMAS_INT4_MASK;
190         palmas_pm->int_mask_reg_add[4] = PALMAS_INT5_MASK;
191         palmas_pm->int_mask_reg_add[5] = PALMAS_INT6_MASK;
192         palmas_pm->int_status_reg_add[0] = PALMAS_INT1_STATUS;
193         palmas_pm->int_status_reg_add[1] = PALMAS_INT2_STATUS;
194         palmas_pm->int_status_reg_add[2] = PALMAS_INT3_STATUS;
195         palmas_pm->int_status_reg_add[3] = PALMAS_INT4_STATUS;
196         palmas_pm->int_status_reg_add[4] = PALMAS_INT5_STATUS;
197         palmas_pm->int_status_reg_add[5] = PALMAS_INT6_STATUS;
198         for (i = 0; i < palmas_pm->num_int_mask_regs; ++i)
199                 palmas_pm->int_mask_val[i] = 0xFF;
200
201         palmas_pdata = dev_get_platdata(pdev->dev.parent);
202         if (palmas_pdata)
203                 pm_pdata = palmas_pdata->pm_pdata;
204
205         if (pm_pdata) {
206                 config.allow_power_off = pm_pdata->use_power_off;
207                 config.allow_power_reset = pm_pdata->use_power_reset;
208         } else {
209                 config.allow_power_off = true;
210                 config.allow_power_reset = false;
211         }
212
213         palmas_pm->system_pmic_dev = system_pmic_register(&pdev->dev,
214                                 &palmas_pm_ops, &config, palmas_pm);
215         if (IS_ERR(palmas_pm->system_pmic_dev)) {
216                 int ret = PTR_ERR(palmas_pm->system_pmic_dev);
217                 dev_err(&pdev->dev, "System PMIC registartion failed: %d\n",
218                         ret);
219                 return ret;
220         }
221         return 0;
222 }
223
224 static int palmas_pm_remove(struct platform_device *pdev)
225 {
226         struct palmas_pm *palmas_pm = platform_get_drvdata(pdev);
227
228         system_pmic_unregister(palmas_pm->system_pmic_dev);
229         return 0;
230 }
231
232 static struct platform_driver palmas_pm_driver = {
233         .probe          = palmas_pm_probe,
234         .remove         = palmas_pm_remove,
235         .driver         = {
236                 .owner  = THIS_MODULE,
237                 .name   = "palmas-pm",
238         },
239 };
240
241 module_platform_driver(palmas_pm_driver);
242 MODULE_ALIAS("platform:palmas-pm");
243 MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
244 MODULE_LICENSE("GPL v2");