// SPDX-License-Identifier: GPL-2.0 /* * Device driver for regulators in Hisi IC * * Copyright (c) 2013 Linaro Ltd. * Copyright (c) 2011 Hisilicon. * * Guodong Xu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct hisi_regulator_register_info { u32 ctrl_reg; u32 enable_mask; u32 eco_mode_mask; u32 vset_reg; u32 vset_mask; }; struct hisi_regulator { const char *name; struct hisi_regulator_register_info register_info; u32 off_on_delay; u32 eco_uA; struct regulator_desc rdesc; struct hisi_pmic *pmic; }; static DEFINE_MUTEX(enable_mutex); /* helper function to ensure when it returns it is at least 'delay_us' * microseconds after 'since'. */ static int hisi_regulator_is_enabled(struct regulator_dev *dev) { u32 reg_val; struct hisi_regulator *sreg = rdev_get_drvdata(dev); struct hisi_pmic *pmic = sreg->pmic; reg_val = hisi_pmic_read(pmic, sreg->register_info.ctrl_reg); pr_debug("<[%s]: ctrl_reg=0x%x,enable_state=%d>\n", __func__, sreg->register_info.ctrl_reg, (reg_val & sreg->register_info.enable_mask)); return ((reg_val & sreg->register_info.enable_mask) != 0); } static int hisi_regulator_enable(struct regulator_dev *dev) { struct hisi_regulator *sreg = rdev_get_drvdata(dev); struct hisi_pmic *pmic = sreg->pmic; /* keep a distance of off_on_delay from last time disabled */ usleep_range(sreg->off_on_delay, sreg->off_on_delay + 1000); pr_debug("<[%s]: off_on_delay=%dus>\n", __func__, sreg->off_on_delay); /* cannot enable more than one regulator at one time */ mutex_lock(&enable_mutex); usleep_range(HISI_REGS_ENA_PROTECT_TIME, HISI_REGS_ENA_PROTECT_TIME + 1000); /* set enable register */ hisi_pmic_rmw(pmic, sreg->register_info.ctrl_reg, sreg->register_info.enable_mask, sreg->register_info.enable_mask); pr_debug("<[%s]: ctrl_reg=0x%x,enable_mask=0x%x>\n", __func__, sreg->register_info.ctrl_reg, sreg->register_info.enable_mask); mutex_unlock(&enable_mutex); return 0; } static int hisi_regulator_disable(struct regulator_dev *dev) { struct hisi_regulator *sreg = rdev_get_drvdata(dev); struct hisi_pmic *pmic = sreg->pmic; /* set enable register to 0 */ hisi_pmic_rmw(pmic, sreg->register_info.ctrl_reg, sreg->register_info.enable_mask, 0); return 0; } static int hisi_regulator_get_voltage(struct regulator_dev *dev) { struct hisi_regulator *sreg = rdev_get_drvdata(dev); struct hisi_pmic *pmic = sreg->pmic; u32 reg_val, selector; /* get voltage selector */ reg_val = hisi_pmic_read(pmic, sreg->register_info.vset_reg); pr_debug("<[%s]: vset_reg=0x%x>\n", __func__, sreg->register_info.vset_reg); selector = (reg_val & sreg->register_info.vset_mask) >> (ffs(sreg->register_info.vset_mask) - 1); return sreg->rdesc.ops->list_voltage(dev, selector); } static int hisi_regulator_set_voltage(struct regulator_dev *dev, int min_uV, int max_uV, unsigned int *selector) { struct hisi_regulator *sreg = rdev_get_drvdata(dev); struct hisi_pmic *pmic = sreg->pmic; u32 vsel; int ret = 0; for (vsel = 0; vsel < sreg->rdesc.n_voltages; vsel++) { int uV = sreg->rdesc.volt_table[vsel]; /* Break at the first in-range value */ if (min_uV <= uV && uV <= max_uV) break; } /* unlikely to happen. sanity test done by regulator core */ if (unlikely(vsel == sreg->rdesc.n_voltages)) return -EINVAL; *selector = vsel; /* set voltage selector */ hisi_pmic_rmw(pmic, sreg->register_info.vset_reg, sreg->register_info.vset_mask, vsel << (ffs(sreg->register_info.vset_mask) - 1)); pr_debug("<[%s]: vset_reg=0x%x, vset_mask=0x%x, value=0x%x>\n", __func__, sreg->register_info.vset_reg, sreg->register_info.vset_mask, vsel << (ffs(sreg->register_info.vset_mask) - 1)); return ret; } static unsigned int hisi_regulator_get_mode(struct regulator_dev *dev) { struct hisi_regulator *sreg = rdev_get_drvdata(dev); struct hisi_pmic *pmic = sreg->pmic; u32 reg_val; reg_val = hisi_pmic_read(pmic, sreg->register_info.ctrl_reg); pr_debug("<[%s]: reg_val=%d, ctrl_reg=0x%x, eco_mode_mask=0x%x>\n", __func__, reg_val, sreg->register_info.ctrl_reg, sreg->register_info.eco_mode_mask); if (reg_val & sreg->register_info.eco_mode_mask) return REGULATOR_MODE_IDLE; else return REGULATOR_MODE_NORMAL; } static int hisi_regulator_set_mode(struct regulator_dev *dev, unsigned int mode) { struct hisi_regulator *sreg = rdev_get_drvdata(dev); struct hisi_pmic *pmic = sreg->pmic; u32 eco_mode; switch (mode) { case REGULATOR_MODE_NORMAL: eco_mode = HISI_ECO_MODE_DISABLE; break; case REGULATOR_MODE_IDLE: eco_mode = HISI_ECO_MODE_ENABLE; break; default: return -EINVAL; } /* set mode */ hisi_pmic_rmw(pmic, sreg->register_info.ctrl_reg, sreg->register_info.eco_mode_mask, eco_mode << (ffs(sreg->register_info.eco_mode_mask) - 1)); pr_debug("<[%s]: ctrl_reg=0x%x, eco_mode_mask=0x%x, value=0x%x>\n", __func__, sreg->register_info.ctrl_reg, sreg->register_info.eco_mode_mask, eco_mode << (ffs(sreg->register_info.eco_mode_mask) - 1)); return 0; } static unsigned int hisi_regulator_get_optimum_mode(struct regulator_dev *dev, int input_uV, int output_uV, int load_uA) { struct hisi_regulator *sreg = rdev_get_drvdata(dev); if (load_uA || ((unsigned int)load_uA > sreg->eco_uA)) return REGULATOR_MODE_NORMAL; else return REGULATOR_MODE_IDLE; } static int hisi_dt_parse_common(struct hisi_regulator *sreg, struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct regulator_desc *rdesc = &sreg->rdesc; unsigned int register_info[3] = {0}; int ret = 0; /* parse .register_info.ctrl_reg */ ret = of_property_read_u32_array(np, "hisilicon,hisi-ctrl", register_info, 3); if (ret) { dev_err(dev, "no hisilicon,hisi-ctrl property set\n"); goto dt_parse_common_end; } sreg->register_info.ctrl_reg = register_info[0]; sreg->register_info.enable_mask = register_info[1]; sreg->register_info.eco_mode_mask = register_info[2]; /* parse .register_info.vset_reg */ ret = of_property_read_u32_array(np, "hisilicon,hisi-vset", register_info, 2); if (ret) { dev_err(dev, "no hisilicon,hisi-vset property set\n"); goto dt_parse_common_end; } sreg->register_info.vset_reg = register_info[0]; sreg->register_info.vset_mask = register_info[1]; /* parse .off-on-delay */ ret = of_property_read_u32(np, "hisilicon,hisi-off-on-delay-us", &sreg->off_on_delay); if (ret) { dev_err(dev, "no hisilicon,hisi-off-on-delay-us property set\n"); goto dt_parse_common_end; } /* parse .enable_time */ ret = of_property_read_u32(np, "hisilicon,hisi-enable-time-us", &rdesc->enable_time); if (ret) { dev_err(dev, "no hisilicon,hisi-enable-time-us property set\n"); goto dt_parse_common_end; } /* parse .eco_uA */ ret = of_property_read_u32(np, "hisilicon,hisi-eco-microamp", &sreg->eco_uA); if (ret) { sreg->eco_uA = 0; ret = 0; } dt_parse_common_end: return ret; } static int hisi_dt_parse_ldo(struct hisi_regulator *sreg, struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct regulator_desc *rdesc = &sreg->rdesc; unsigned int *v_table; int ret = 0; /* parse .n_voltages, and .volt_table */ ret = of_property_read_u32(np, "hisilicon,hisi-n-voltages", &rdesc->n_voltages); if (ret) { dev_err(dev, "no hisilicon,hisi-n-voltages property set\n"); goto dt_parse_ldo_end; } /* alloc space for .volt_table */ v_table = devm_kzalloc(dev, sizeof(unsigned int) * rdesc->n_voltages, GFP_KERNEL); if (unlikely(!v_table)) { ret = -ENOMEM; dev_err(dev, "no memory for .volt_table\n"); goto dt_parse_ldo_end; } ret = of_property_read_u32_array(np, "hisilicon,hisi-vset-table", v_table, rdesc->n_voltages); if (ret) { dev_err(dev, "no hisilicon,hisi-vset-table property set\n"); goto dt_parse_ldo_end1; } rdesc->volt_table = v_table; /* parse hisi regulator's dt common part */ ret = hisi_dt_parse_common(sreg, pdev); if (ret) { dev_err(dev, "failure in hisi_dt_parse_common\n"); goto dt_parse_ldo_end1; } return ret; dt_parse_ldo_end1: dt_parse_ldo_end: return ret; } static struct regulator_ops hisi_ldo_rops = { .is_enabled = hisi_regulator_is_enabled, .enable = hisi_regulator_enable, .disable = hisi_regulator_disable, .list_voltage = regulator_list_voltage_table, .get_voltage = hisi_regulator_get_voltage, .set_voltage = hisi_regulator_set_voltage, .get_mode = hisi_regulator_get_mode, .set_mode = hisi_regulator_set_mode, .get_optimum_mode = hisi_regulator_get_optimum_mode, }; /* * Used only for parsing the DT properties */ static const struct of_device_id of_hisi_pmic_match_tbl[] = { { .compatible = "hisilicon,hi6421-spmi-pmic-ldo", }, { } }; static int hisi_regulator_probe_ldo(struct platform_device *pdev, struct device_node *np, struct hisi_pmic *pmic) { struct device *dev = &pdev->dev; struct regulator_desc *rdesc; struct regulator_dev *rdev; struct hisi_regulator *sreg = NULL; struct regulator_init_data *initdata; struct regulator_config config = { }; struct regulation_constraints *constraint; const char *supplyname = NULL; unsigned int temp_modes; int ret = 0; initdata = of_get_regulator_init_data(dev, np, NULL); if (!initdata) { pr_err("get regulator init data error !\n"); return -EINVAL; } /* hisi regulator supports two modes */ constraint = &initdata->constraints; ret = of_property_read_u32_array(np, "hisilicon,valid-modes-mask", &constraint->valid_modes_mask, 1); if (ret) { pr_err("no hisilicon,valid-modes-mask property set\n"); ret = -ENODEV; return ret; } ret = of_property_read_u32_array(np, "hisilicon,valid-idle-mask", &temp_modes, 1); if (ret) { pr_err("no hisilicon,valid-modes-mask property set\n"); ret = -ENODEV; return ret; } constraint->valid_ops_mask |= temp_modes; sreg = kzalloc(sizeof(*sreg), GFP_KERNEL); if (!sreg) return -ENOMEM; sreg->name = initdata->constraints.name; sreg->pmic = pmic; rdesc = &sreg->rdesc; rdesc->name = sreg->name; rdesc->ops = &hisi_ldo_rops; rdesc->type = REGULATOR_VOLTAGE; rdesc->min_uV = initdata->constraints.min_uV; supplyname = of_get_property(np, "hisilicon,supply_name", NULL); if (supplyname) initdata->supply_regulator = supplyname; /* parse device tree data for regulator specific */ ret = hisi_dt_parse_ldo(sreg, pdev); if (ret) { dev_err(dev, "device tree parameter parse error!\n"); goto hisi_probe_end; } config.dev = &pdev->dev; config.init_data = initdata; config.driver_data = sreg; config.of_node = pdev->dev.of_node; /* register regulator */ rdev = regulator_register(rdesc, &config); if (IS_ERR(rdev)) { dev_err(dev, "failed to register %s\n", rdesc->name); ret = PTR_ERR(rdev); goto hisi_probe_end; } pr_debug("[%s]:valid_modes_mask[0x%x], valid_ops_mask[0x%x]\n", rdesc->name, constraint->valid_modes_mask, constraint->valid_ops_mask); dev_set_drvdata(dev, rdev); hisi_probe_end: if (ret) kfree(sreg); return ret; } static int hisi_regulator_probe(struct platform_device *pdev) { struct device *pmic_dev = pdev->dev.parent; struct device_node *np = pmic_dev->of_node; struct device_node *regulators, *child; struct platform_device *new_pdev; struct hisi_pmic *pmic; int ret; dev_dbg(&pdev->dev, "probing hi6421v600 regulator\n"); /* * This driver is meant to be called by hi6421-spmi-core, * which should first set drvdata. If this doesn't happen, hit * a warn on and return. */ pmic = dev_get_drvdata(pmic_dev); if (WARN_ON(!pmic)) return -ENODEV; regulators = of_get_child_by_name(np, "regulators"); if (!regulators) { dev_err(&pdev->dev, "regulator node not found\n"); return -ENODEV; } /* * Parse all LDO regulator nodes */ for_each_child_of_node(regulators, child) { dev_dbg(&pdev->dev, "adding child %pOF\n", child); new_pdev = platform_device_alloc(child->name, -1); new_pdev->dev.parent = pmic_dev; new_pdev->dev.of_node = of_node_get(child); ret = platform_device_add(new_pdev); if (ret < 0) { platform_device_put(new_pdev); continue; } ret = hisi_regulator_probe_ldo(new_pdev, child, pmic); if (ret < 0) platform_device_put(new_pdev); } of_node_put(regulators); return 0; } static int hisi_regulator_remove(struct platform_device *pdev) { struct regulator_dev *rdev = dev_get_drvdata(&pdev->dev); struct hisi_regulator *sreg = rdev_get_drvdata(rdev); regulator_unregister(rdev); /* TODO: should i worry about that? devm_kzalloc */ if (sreg->rdesc.volt_table) devm_kfree(&pdev->dev, (unsigned int *)sreg->rdesc.volt_table); kfree(sreg); return 0; } static const struct platform_device_id hi6421v600_regulator_table[] = { { .name = "hi6421v600-regulator" }, {}, }; MODULE_DEVICE_TABLE(platform, hi6421v600_regulator_table); static struct platform_driver hi6421v600_regulator_driver = { .id_table = hi6421v600_regulator_table, .driver = { .name = "hi6421v600-regulator", }, .probe = hisi_regulator_probe, .remove = hisi_regulator_remove, }; module_platform_driver(hi6421v600_regulator_driver); MODULE_DESCRIPTION("Hi6421v600 regulator driver"); MODULE_LICENSE("GPL v2");