Linux Audio

Check our new training course

Yocto / OpenEmbedded training

Mar 24-27, 2025, special US time zones
Register
Loading...
  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(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
 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(pfsm->regmap, 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(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
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(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
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(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
283static 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
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");
1