File Index
Files and directories
Sign in
FILE

tps6594-pfsm.c

File preview with syntax highlighting.

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