brintos

brintos / linux-shallow public Read only

0
0
Text · 8.4 KiB · 9bcca18 Raw
326 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * PFSM (Pre-configurable Finite State Machine) driver for TI TPS65224/TPS6594/TPS6593/LP8764 PMICs4 *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 222#define TPS6594_STARTUP_DEST_ACTIVE_VAL   323#define TPS6594_STARTUP_DEST_SHIFT	  524#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 access31 * page 0 (user registers) and page 1 (NVM control and configuration).32 */33#define TPS6594_PMIC_MAX_POS 0x20034 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 structure39 *40 * @miscdev: misc device infos41 * @regmap:  regmap for accessing the device registers42 * @chip_id: chip identifier of the device43 */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	else118		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	else127		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		else221			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_new = 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");326