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