xref: /openbmc/linux/drivers/misc/tps6594-pfsm.c (revision 1314e122)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * PFSM (Pre-configurable Finite State Machine) driver for TI TPS6594/TPS6593/LP8764 PMICs
4  *
5  * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/
6  */
7 
8 #include <linux/errno.h>
9 #include <linux/fs.h>
10 #include <linux/interrupt.h>
11 #include <linux/ioctl.h>
12 #include <linux/miscdevice.h>
13 #include <linux/module.h>
14 #include <linux/platform_device.h>
15 #include <linux/regmap.h>
16 
17 #include <linux/mfd/tps6594.h>
18 
19 #include <linux/tps6594_pfsm.h>
20 
21 #define TPS6594_STARTUP_DEST_MCU_ONLY_VAL 2
22 #define TPS6594_STARTUP_DEST_ACTIVE_VAL   3
23 #define TPS6594_STARTUP_DEST_SHIFT	  5
24 #define TPS6594_STARTUP_DEST_MCU_ONLY	  (TPS6594_STARTUP_DEST_MCU_ONLY_VAL \
25 					   << TPS6594_STARTUP_DEST_SHIFT)
26 #define TPS6594_STARTUP_DEST_ACTIVE	  (TPS6594_STARTUP_DEST_ACTIVE_VAL \
27 					   << TPS6594_STARTUP_DEST_SHIFT)
28 
29 /*
30  * To update the PMIC firmware, the user must be able to access
31  * page 0 (user registers) and page 1 (NVM control and configuration).
32  */
33 #define TPS6594_PMIC_MAX_POS 0x200
34 
35 #define TPS6594_FILE_TO_PFSM(f) container_of((f)->private_data, struct tps6594_pfsm, miscdev)
36 
37 /**
38  * struct tps6594_pfsm - device private data structure
39  *
40  * @miscdev: misc device infos
41  * @regmap:  regmap for accessing the device registers
42  */
43 struct tps6594_pfsm {
44 	struct miscdevice miscdev;
45 	struct regmap *regmap;
46 };
47 
tps6594_pfsm_read(struct file * f,char __user * buf,size_t count,loff_t * ppos)48 static ssize_t tps6594_pfsm_read(struct file *f, char __user *buf,
49 				 size_t count, loff_t *ppos)
50 {
51 	struct tps6594_pfsm *pfsm = TPS6594_FILE_TO_PFSM(f);
52 	loff_t pos = *ppos;
53 	unsigned int val;
54 	int ret;
55 	int i;
56 
57 	if (pos < 0)
58 		return -EINVAL;
59 	if (pos >= TPS6594_PMIC_MAX_POS)
60 		return 0;
61 	if (count > TPS6594_PMIC_MAX_POS - pos)
62 		count = TPS6594_PMIC_MAX_POS - pos;
63 
64 	for (i = 0 ; i < count ; i++) {
65 		ret = regmap_read(pfsm->regmap, pos + i, &val);
66 		if (ret)
67 			return ret;
68 
69 		if (put_user(val, buf + i))
70 			return -EFAULT;
71 	}
72 
73 	*ppos = pos + count;
74 
75 	return count;
76 }
77 
tps6594_pfsm_write(struct file * f,const char __user * buf,size_t count,loff_t * ppos)78 static ssize_t tps6594_pfsm_write(struct file *f, const char __user *buf,
79 				  size_t count, loff_t *ppos)
80 {
81 	struct tps6594_pfsm *pfsm = TPS6594_FILE_TO_PFSM(f);
82 	loff_t pos = *ppos;
83 	char val;
84 	int ret;
85 	int i;
86 
87 	if (pos < 0)
88 		return -EINVAL;
89 	if (pos >= TPS6594_PMIC_MAX_POS || !count)
90 		return 0;
91 	if (count > TPS6594_PMIC_MAX_POS - pos)
92 		count = TPS6594_PMIC_MAX_POS - pos;
93 
94 	for (i = 0 ; i < count ; i++) {
95 		if (get_user(val, buf + i))
96 			return -EFAULT;
97 
98 		ret = regmap_write(pfsm->regmap, pos + i, val);
99 		if (ret)
100 			return ret;
101 	}
102 
103 	*ppos = pos + count;
104 
105 	return count;
106 }
107 
tps6594_pfsm_configure_ret_trig(struct regmap * regmap,u8 gpio_ret,u8 ddr_ret)108 static int tps6594_pfsm_configure_ret_trig(struct regmap *regmap, u8 gpio_ret, u8 ddr_ret)
109 {
110 	int ret;
111 
112 	if (gpio_ret)
113 		ret = regmap_set_bits(regmap, TPS6594_REG_FSM_I2C_TRIGGERS,
114 				      TPS6594_BIT_TRIGGER_I2C(5) | TPS6594_BIT_TRIGGER_I2C(6));
115 	else
116 		ret = regmap_clear_bits(regmap, TPS6594_REG_FSM_I2C_TRIGGERS,
117 					TPS6594_BIT_TRIGGER_I2C(5) | TPS6594_BIT_TRIGGER_I2C(6));
118 	if (ret)
119 		return ret;
120 
121 	if (ddr_ret)
122 		ret = regmap_set_bits(regmap, TPS6594_REG_FSM_I2C_TRIGGERS,
123 				      TPS6594_BIT_TRIGGER_I2C(7));
124 	else
125 		ret = regmap_clear_bits(regmap, TPS6594_REG_FSM_I2C_TRIGGERS,
126 					TPS6594_BIT_TRIGGER_I2C(7));
127 
128 	return ret;
129 }
130 
tps6594_pfsm_ioctl(struct file * f,unsigned int cmd,unsigned long arg)131 static long tps6594_pfsm_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
132 {
133 	struct tps6594_pfsm *pfsm = TPS6594_FILE_TO_PFSM(f);
134 	struct pmic_state_opt state_opt;
135 	void __user *argp = (void __user *)arg;
136 	int ret = -ENOIOCTLCMD;
137 
138 	switch (cmd) {
139 	case PMIC_GOTO_STANDBY:
140 		/* Disable LP mode */
141 		ret = regmap_clear_bits(pfsm->regmap, TPS6594_REG_RTC_CTRL_2,
142 					TPS6594_BIT_LP_STANDBY_SEL);
143 		if (ret)
144 			return ret;
145 
146 		/* Force trigger */
147 		ret = regmap_write_bits(pfsm->regmap, TPS6594_REG_FSM_I2C_TRIGGERS,
148 					TPS6594_BIT_TRIGGER_I2C(0), TPS6594_BIT_TRIGGER_I2C(0));
149 		break;
150 	case PMIC_GOTO_LP_STANDBY:
151 		/* Enable LP mode */
152 		ret = regmap_set_bits(pfsm->regmap, TPS6594_REG_RTC_CTRL_2,
153 				      TPS6594_BIT_LP_STANDBY_SEL);
154 		if (ret)
155 			return ret;
156 
157 		/* Force trigger */
158 		ret = regmap_write_bits(pfsm->regmap, TPS6594_REG_FSM_I2C_TRIGGERS,
159 					TPS6594_BIT_TRIGGER_I2C(0), TPS6594_BIT_TRIGGER_I2C(0));
160 		break;
161 	case PMIC_UPDATE_PGM:
162 		/* Force trigger */
163 		ret = regmap_write_bits(pfsm->regmap, TPS6594_REG_FSM_I2C_TRIGGERS,
164 					TPS6594_BIT_TRIGGER_I2C(3), TPS6594_BIT_TRIGGER_I2C(3));
165 		break;
166 	case PMIC_SET_ACTIVE_STATE:
167 		/* Modify NSLEEP1-2 bits */
168 		ret = regmap_set_bits(pfsm->regmap, TPS6594_REG_FSM_NSLEEP_TRIGGERS,
169 				      TPS6594_BIT_NSLEEP1B | TPS6594_BIT_NSLEEP2B);
170 		break;
171 	case PMIC_SET_MCU_ONLY_STATE:
172 		if (copy_from_user(&state_opt, argp, sizeof(state_opt)))
173 			return -EFAULT;
174 
175 		/* Configure retention triggers */
176 		ret = tps6594_pfsm_configure_ret_trig(pfsm->regmap, state_opt.gpio_retention,
177 						      state_opt.ddr_retention);
178 		if (ret)
179 			return ret;
180 
181 		/* Modify NSLEEP1-2 bits */
182 		ret = regmap_clear_bits(pfsm->regmap, TPS6594_REG_FSM_NSLEEP_TRIGGERS,
183 					TPS6594_BIT_NSLEEP1B);
184 		if (ret)
185 			return ret;
186 
187 		ret = regmap_set_bits(pfsm->regmap, TPS6594_REG_FSM_NSLEEP_TRIGGERS,
188 				      TPS6594_BIT_NSLEEP2B);
189 		break;
190 	case PMIC_SET_RETENTION_STATE:
191 		if (copy_from_user(&state_opt, argp, sizeof(state_opt)))
192 			return -EFAULT;
193 
194 		/* Configure wake-up destination */
195 		if (state_opt.mcu_only_startup_dest)
196 			ret = regmap_write_bits(pfsm->regmap, TPS6594_REG_RTC_CTRL_2,
197 						TPS6594_MASK_STARTUP_DEST,
198 						TPS6594_STARTUP_DEST_MCU_ONLY);
199 		else
200 			ret = regmap_write_bits(pfsm->regmap, TPS6594_REG_RTC_CTRL_2,
201 						TPS6594_MASK_STARTUP_DEST,
202 						TPS6594_STARTUP_DEST_ACTIVE);
203 		if (ret)
204 			return ret;
205 
206 		/* Configure retention triggers */
207 		ret = tps6594_pfsm_configure_ret_trig(pfsm->regmap, state_opt.gpio_retention,
208 						      state_opt.ddr_retention);
209 		if (ret)
210 			return ret;
211 
212 		/* Modify NSLEEP1-2 bits */
213 		ret = regmap_clear_bits(pfsm->regmap, TPS6594_REG_FSM_NSLEEP_TRIGGERS,
214 					TPS6594_BIT_NSLEEP2B);
215 		break;
216 	}
217 
218 	return ret;
219 }
220 
221 static const struct file_operations tps6594_pfsm_fops = {
222 	.owner		= THIS_MODULE,
223 	.llseek		= generic_file_llseek,
224 	.read		= tps6594_pfsm_read,
225 	.write		= tps6594_pfsm_write,
226 	.unlocked_ioctl	= tps6594_pfsm_ioctl,
227 	.compat_ioctl   = compat_ptr_ioctl,
228 };
229 
tps6594_pfsm_isr(int irq,void * dev_id)230 static irqreturn_t tps6594_pfsm_isr(int irq, void *dev_id)
231 {
232 	struct platform_device *pdev = dev_id;
233 	int i;
234 
235 	for (i = 0 ; i < pdev->num_resources ; i++) {
236 		if (irq == platform_get_irq_byname(pdev, pdev->resource[i].name)) {
237 			dev_err(pdev->dev.parent, "%s event detected\n", pdev->resource[i].name);
238 			return IRQ_HANDLED;
239 		}
240 	}
241 
242 	return IRQ_NONE;
243 }
244 
tps6594_pfsm_probe(struct platform_device * pdev)245 static int tps6594_pfsm_probe(struct platform_device *pdev)
246 {
247 	struct tps6594_pfsm *pfsm;
248 	struct tps6594 *tps = dev_get_drvdata(pdev->dev.parent);
249 	struct device *dev = &pdev->dev;
250 	int irq;
251 	int ret;
252 	int i;
253 
254 	pfsm = devm_kzalloc(dev, sizeof(struct tps6594_pfsm), GFP_KERNEL);
255 	if (!pfsm)
256 		return -ENOMEM;
257 
258 	pfsm->regmap = tps->regmap;
259 
260 	pfsm->miscdev.minor = MISC_DYNAMIC_MINOR;
261 	pfsm->miscdev.name = devm_kasprintf(dev, GFP_KERNEL, "pfsm-%ld-0x%02x",
262 					    tps->chip_id, tps->reg);
263 	pfsm->miscdev.fops = &tps6594_pfsm_fops;
264 	pfsm->miscdev.parent = dev->parent;
265 
266 	for (i = 0 ; i < pdev->num_resources ; i++) {
267 		irq = platform_get_irq_byname(pdev, pdev->resource[i].name);
268 		if (irq < 0)
269 			return irq;
270 
271 		ret = devm_request_threaded_irq(dev, irq, NULL,
272 						tps6594_pfsm_isr, IRQF_ONESHOT,
273 						pdev->resource[i].name, pdev);
274 		if (ret)
275 			return dev_err_probe(dev, ret, "Failed to request irq\n");
276 	}
277 
278 	platform_set_drvdata(pdev, pfsm);
279 
280 	return misc_register(&pfsm->miscdev);
281 }
282 
tps6594_pfsm_remove(struct platform_device * pdev)283 static void tps6594_pfsm_remove(struct platform_device *pdev)
284 {
285 	struct tps6594_pfsm *pfsm = platform_get_drvdata(pdev);
286 
287 	misc_deregister(&pfsm->miscdev);
288 }
289 
290 static struct platform_driver tps6594_pfsm_driver = {
291 	.driver	= {
292 		.name = "tps6594-pfsm",
293 	},
294 	.probe = tps6594_pfsm_probe,
295 	.remove_new = tps6594_pfsm_remove,
296 };
297 
298 module_platform_driver(tps6594_pfsm_driver);
299 
300 MODULE_ALIAS("platform:tps6594-pfsm");
301 MODULE_AUTHOR("Julien Panis <jpanis@baylibre.com>");
302 MODULE_DESCRIPTION("TPS6594 Pre-configurable Finite State Machine Driver");
303 MODULE_LICENSE("GPL");
304