History

misc: tps6594-pfsm: Add driver for TI TPS6594 PFSM

This PFSM controls the operational modes of the PMIC: - STANDBY and LP_STANDBY, - ACTIVE state, - MCU_ONLY state, - RETENTION state, with or without DDR and/or GPIO retention. Depending on the current operational mode, some voltage domains remain energized while others can be off. This PFSM is also used to trigger a firmware update, and provides R/W access to device registers. See Documentation/misc-devices/tps6594-pfsm.rst for more information.

Julien Panis committed 3 years ago

Commit a0df3ef1 parent 875fdd04 files · +356 −0Browse files

Unified · Split

4 files
  1. drivers/misc/Kconfig +12 −0
  2. drivers/misc/Makefile +1 −0
  3. drivers/misc/tps6594-pfsm.c +306 −0
  4. include/uapi/linux/tps6594_pfsm.h +37 −0
drivers/misc/Kconfig+12−0
@@ -549,6 +549,18 @@ config TPS6594_ESM · 548 unmodified lines
549549 This driver can also be built as a module. If so, the module
550550 will be called tps6594-esm.
551551
552+config TPS6594_PFSM
553+ tristate "TI TPS6594 Pre-configurable Finite State Machine support"
554+ depends on MFD_TPS6594
555+ default MFD_TPS6594
556+ help
557+ Support PFSM (Pre-configurable Finite State Machine) on TPS6594 PMIC devices.
558+ These devices integrate a finite state machine engine, which manages the state
559+ of the device during operating state transition.
560+
561+ This driver can also be built as a module. If so, the module
562+ will be called tps6594-pfsm.
563+
552564 source "drivers/misc/c2port/Kconfig"
553565 source "drivers/misc/eeprom/Kconfig"
554566 source "drivers/misc/cb710/Kconfig"
drivers/misc/Makefile+1−0
@@ -66,3 +66,4 @@ obj-$(CONFIG_VCPU_STALL_DETECTOR) += vcpu_stall_detector.o · 65 unmodified lines
6666 obj-$(CONFIG_TMR_MANAGER) += xilinx_tmr_manager.o
6767 obj-$(CONFIG_TMR_INJECT) += xilinx_tmr_inject.o
6868 obj-$(CONFIG_TPS6594_ESM) += tps6594-esm.o
69+obj-$(CONFIG_TPS6594_PFSM) += tps6594-pfsm.o
drivers/misc/tps6594-pfsm.c+306−0
@@ -0,0 +1,306 @@
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+
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+
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+
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+
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+
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+
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 dev_err_probe(dev, irq, "Failed to get %s irq\n",
270+ pdev->resource[i].name);
271+
272+ ret = devm_request_threaded_irq(dev, irq, NULL,
273+ tps6594_pfsm_isr, IRQF_ONESHOT,
274+ pdev->resource[i].name, pdev);
275+ if (ret)
276+ return dev_err_probe(dev, ret, "Failed to request irq\n");
277+ }
278+
279+ platform_set_drvdata(pdev, pfsm);
280+
281+ return misc_register(&pfsm->miscdev);
282+}
283+
284+static int tps6594_pfsm_remove(struct platform_device *pdev)
285+{
286+ struct tps6594_pfsm *pfsm = platform_get_drvdata(pdev);
287+
288+ misc_deregister(&pfsm->miscdev);
289+
290+ return 0;
291+}
292+
293+static struct platform_driver tps6594_pfsm_driver = {
294+ .driver = {
295+ .name = "tps6594-pfsm",
296+ },
297+ .probe = tps6594_pfsm_probe,
298+ .remove = tps6594_pfsm_remove,
299+};
300+
301+module_platform_driver(tps6594_pfsm_driver);
302+
303+MODULE_ALIAS("platform:tps6594-pfsm");
304+MODULE_AUTHOR("Julien Panis <jpanis@baylibre.com>");
305+MODULE_DESCRIPTION("TPS6594 Pre-configurable Finite State Machine Driver");
306+MODULE_LICENSE("GPL");
include/uapi/linux/tps6594_pfsm.h+37−0
@@ -0,0 +1,37 @@
1+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2+/*
3+ * Userspace ABI for TPS6594 PMIC Pre-configurable Finite State Machine
4+ *
5+ * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/
6+ */
7+
8+#ifndef __TPS6594_PFSM_H
9+#define __TPS6594_PFSM_H
10+
11+#include <linux/const.h>
12+#include <linux/ioctl.h>
13+#include <linux/types.h>
14+
15+/**
16+ * struct pmic_state_opt - PMIC state options
17+ * @gpio_retention: if enabled, power rails associated with GPIO retention remain active
18+ * @ddr_retention: if enabled, power rails associated with DDR retention remain active
19+ * @mcu_only_startup_dest: if enabled, startup destination state is MCU_ONLY
20+ */
21+struct pmic_state_opt {
22+ __u8 gpio_retention;
23+ __u8 ddr_retention;
24+ __u8 mcu_only_startup_dest;
25+};
26+
27+/* Commands */
28+#define PMIC_BASE 'P'
29+
30+#define PMIC_GOTO_STANDBY _IO(PMIC_BASE, 0)
31+#define PMIC_GOTO_LP_STANDBY _IO(PMIC_BASE, 1)
32+#define PMIC_UPDATE_PGM _IO(PMIC_BASE, 2)
33+#define PMIC_SET_ACTIVE_STATE _IO(PMIC_BASE, 3)
34+#define PMIC_SET_MCU_ONLY_STATE _IOW(PMIC_BASE, 4, struct pmic_state_opt)
35+#define PMIC_SET_RETENTION_STATE _IOW(PMIC_BASE, 5, struct pmic_state_opt)
36+
37+#endif /* __TPS6594_PFSM_H */

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t