Linux Audio

Check our new training course

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