Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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.

Signed-off-by: Julien Panis <jpanis@baylibre.com>
Message-ID: <20230511095126.105104-5-jpanis@baylibre.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Julien Panis and committed by
Greg Kroah-Hartman
a0df3ef0 875fdd07

+356
+12
drivers/misc/Kconfig
··· 549 549 This driver can also be built as a module. If so, the module 550 550 will be called tps6594-esm. 551 551 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 + 552 564 source "drivers/misc/c2port/Kconfig" 553 565 source "drivers/misc/eeprom/Kconfig" 554 566 source "drivers/misc/cb710/Kconfig"
+1
drivers/misc/Makefile
··· 66 66 obj-$(CONFIG_TMR_MANAGER) += xilinx_tmr_manager.o 67 67 obj-$(CONFIG_TMR_INJECT) += xilinx_tmr_inject.o 68 68 obj-$(CONFIG_TPS6594_ESM) += tps6594-esm.o 69 + obj-$(CONFIG_TPS6594_PFSM) += tps6594-pfsm.o
+306
drivers/misc/tps6594-pfsm.c
··· 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");
+37
include/uapi/linux/tps6594_pfsm.h
··· 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 */