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 */
43struct tps6594_pfsm {
44 struct miscdevice miscdev;
45 struct regmap *regmap;
46};
47
48static 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(map: pfsm->regmap, reg: pos + i, val: &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
78static 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(map: pfsm->regmap, reg: pos + i, val);
99 if (ret)
100 return ret;
101 }
102
103 *ppos = pos + count;
104
105 return count;
106}
107
108static 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(map: regmap, TPS6594_REG_FSM_I2C_TRIGGERS,
114 TPS6594_BIT_TRIGGER_I2C(5) | TPS6594_BIT_TRIGGER_I2C(6));
115 else
116 ret = regmap_clear_bits(map: 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(map: regmap, TPS6594_REG_FSM_I2C_TRIGGERS,
123 TPS6594_BIT_TRIGGER_I2C(7));
124 else
125 ret = regmap_clear_bits(map: regmap, TPS6594_REG_FSM_I2C_TRIGGERS,
126 TPS6594_BIT_TRIGGER_I2C(7));
127
128 return ret;
129}
130
131static 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(map: 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(map: 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(map: 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(map: 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(map: 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(map: 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(to: &state_opt, from: argp, n: sizeof(state_opt)))
173 return -EFAULT;
174
175 /* Configure retention triggers */
176 ret = tps6594_pfsm_configure_ret_trig(regmap: pfsm->regmap, gpio_ret: state_opt.gpio_retention,
177 ddr_ret: state_opt.ddr_retention);
178 if (ret)
179 return ret;
180
181 /* Modify NSLEEP1-2 bits */
182 ret = regmap_clear_bits(map: pfsm->regmap, TPS6594_REG_FSM_NSLEEP_TRIGGERS,
183 TPS6594_BIT_NSLEEP1B);
184 if (ret)
185 return ret;
186
187 ret = regmap_set_bits(map: pfsm->regmap, TPS6594_REG_FSM_NSLEEP_TRIGGERS,
188 TPS6594_BIT_NSLEEP2B);
189 break;
190 case PMIC_SET_RETENTION_STATE:
191 if (copy_from_user(to: &state_opt, from: argp, n: 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(map: 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(map: 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(regmap: pfsm->regmap, gpio_ret: state_opt.gpio_retention,
208 ddr_ret: state_opt.ddr_retention);
209 if (ret)
210 return ret;
211
212 /* Modify NSLEEP1-2 bits */
213 ret = regmap_clear_bits(map: pfsm->regmap, TPS6594_REG_FSM_NSLEEP_TRIGGERS,
214 TPS6594_BIT_NSLEEP2B);
215 break;
216 }
217
218 return ret;
219}
220
221static 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
230static 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
245static int tps6594_pfsm_probe(struct platform_device *pdev)
246{
247 struct tps6594_pfsm *pfsm;
248 struct tps6594 *tps = dev_get_drvdata(dev: pdev->dev.parent);
249 struct device *dev = &pdev->dev;
250 int irq;
251 int ret;
252 int i;
253
254 pfsm = devm_kzalloc(dev, size: 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, fmt: "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 thread_fn: tps6594_pfsm_isr, IRQF_ONESHOT,
273 devname: pdev->resource[i].name, dev_id: pdev);
274 if (ret)
275 return dev_err_probe(dev, err: ret, fmt: "Failed to request irq\n");
276 }
277
278 platform_set_drvdata(pdev, data: pfsm);
279
280 return misc_register(misc: &pfsm->miscdev);
281}
282
283static void tps6594_pfsm_remove(struct platform_device *pdev)
284{
285 struct tps6594_pfsm *pfsm = platform_get_drvdata(pdev);
286
287 misc_deregister(misc: &pfsm->miscdev);
288}
289
290static 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
298module_platform_driver(tps6594_pfsm_driver);
299
300MODULE_ALIAS("platform:tps6594-pfsm");
301MODULE_AUTHOR("Julien Panis <jpanis@baylibre.com>");
302MODULE_DESCRIPTION("TPS6594 Pre-configurable Finite State Machine Driver");
303MODULE_LICENSE("GPL");
304

source code of linux/drivers/misc/tps6594-pfsm.c