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 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 283 static 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 290 static 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 298 module_platform_driver(tps6594_pfsm_driver); 299 300 MODULE_ALIAS("platform:tps6594-pfsm"); 301 MODULE_AUTHOR("Julien Panis <jpanis@baylibre.com>"); 302 MODULE_DESCRIPTION("TPS6594 Pre-configurable Finite State Machine Driver"); 303 MODULE_LICENSE("GPL"); 304