18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci// Copyright (c) 2020, The Linux Foundation. All rights reserved. 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/module.h> 58c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 68c2ecf20Sopenharmony_ci#include <linux/of.h> 78c2ecf20Sopenharmony_ci#include <linux/of_device.h> 88c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 98c2ecf20Sopenharmony_ci#include <linux/regmap.h> 108c2ecf20Sopenharmony_ci#include <linux/regulator/driver.h> 118c2ecf20Sopenharmony_ci#include <linux/regulator/of_regulator.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define REG_PERPH_TYPE 0x04 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define QCOM_LAB_TYPE 0x24 168c2ecf20Sopenharmony_ci#define QCOM_IBB_TYPE 0x20 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define PMI8998_LAB_REG_BASE 0xde00 198c2ecf20Sopenharmony_ci#define PMI8998_IBB_REG_BASE 0xdc00 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define REG_LABIBB_STATUS1 0x08 228c2ecf20Sopenharmony_ci#define REG_LABIBB_ENABLE_CTL 0x46 238c2ecf20Sopenharmony_ci#define LABIBB_STATUS1_VREG_OK_BIT BIT(7) 248c2ecf20Sopenharmony_ci#define LABIBB_CONTROL_ENABLE BIT(7) 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define LAB_ENABLE_CTL_MASK BIT(7) 278c2ecf20Sopenharmony_ci#define IBB_ENABLE_CTL_MASK (BIT(7) | BIT(6)) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define LABIBB_OFF_ON_DELAY 1000 308c2ecf20Sopenharmony_ci#define LAB_ENABLE_TIME (LABIBB_OFF_ON_DELAY * 2) 318c2ecf20Sopenharmony_ci#define IBB_ENABLE_TIME (LABIBB_OFF_ON_DELAY * 10) 328c2ecf20Sopenharmony_ci#define LABIBB_POLL_ENABLED_TIME 1000 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistruct labibb_regulator { 358c2ecf20Sopenharmony_ci struct regulator_desc desc; 368c2ecf20Sopenharmony_ci struct device *dev; 378c2ecf20Sopenharmony_ci struct regmap *regmap; 388c2ecf20Sopenharmony_ci struct regulator_dev *rdev; 398c2ecf20Sopenharmony_ci u16 base; 408c2ecf20Sopenharmony_ci u8 type; 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistruct labibb_regulator_data { 448c2ecf20Sopenharmony_ci const char *name; 458c2ecf20Sopenharmony_ci u8 type; 468c2ecf20Sopenharmony_ci u16 base; 478c2ecf20Sopenharmony_ci const struct regulator_desc *desc; 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic const struct regulator_ops qcom_labibb_ops = { 518c2ecf20Sopenharmony_ci .enable = regulator_enable_regmap, 528c2ecf20Sopenharmony_ci .disable = regulator_disable_regmap, 538c2ecf20Sopenharmony_ci .is_enabled = regulator_is_enabled_regmap, 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic const struct regulator_desc pmi8998_lab_desc = { 578c2ecf20Sopenharmony_ci .enable_mask = LAB_ENABLE_CTL_MASK, 588c2ecf20Sopenharmony_ci .enable_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_ENABLE_CTL), 598c2ecf20Sopenharmony_ci .enable_val = LABIBB_CONTROL_ENABLE, 608c2ecf20Sopenharmony_ci .enable_time = LAB_ENABLE_TIME, 618c2ecf20Sopenharmony_ci .poll_enabled_time = LABIBB_POLL_ENABLED_TIME, 628c2ecf20Sopenharmony_ci .off_on_delay = LABIBB_OFF_ON_DELAY, 638c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 648c2ecf20Sopenharmony_ci .type = REGULATOR_VOLTAGE, 658c2ecf20Sopenharmony_ci .ops = &qcom_labibb_ops, 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic const struct regulator_desc pmi8998_ibb_desc = { 698c2ecf20Sopenharmony_ci .enable_mask = IBB_ENABLE_CTL_MASK, 708c2ecf20Sopenharmony_ci .enable_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_ENABLE_CTL), 718c2ecf20Sopenharmony_ci .enable_val = LABIBB_CONTROL_ENABLE, 728c2ecf20Sopenharmony_ci .enable_time = IBB_ENABLE_TIME, 738c2ecf20Sopenharmony_ci .poll_enabled_time = LABIBB_POLL_ENABLED_TIME, 748c2ecf20Sopenharmony_ci .off_on_delay = LABIBB_OFF_ON_DELAY, 758c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 768c2ecf20Sopenharmony_ci .type = REGULATOR_VOLTAGE, 778c2ecf20Sopenharmony_ci .ops = &qcom_labibb_ops, 788c2ecf20Sopenharmony_ci}; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic const struct labibb_regulator_data pmi8998_labibb_data[] = { 818c2ecf20Sopenharmony_ci {"lab", QCOM_LAB_TYPE, PMI8998_LAB_REG_BASE, &pmi8998_lab_desc}, 828c2ecf20Sopenharmony_ci {"ibb", QCOM_IBB_TYPE, PMI8998_IBB_REG_BASE, &pmi8998_ibb_desc}, 838c2ecf20Sopenharmony_ci { }, 848c2ecf20Sopenharmony_ci}; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic const struct of_device_id qcom_labibb_match[] = { 878c2ecf20Sopenharmony_ci { .compatible = "qcom,pmi8998-lab-ibb", .data = &pmi8998_labibb_data}, 888c2ecf20Sopenharmony_ci { }, 898c2ecf20Sopenharmony_ci}; 908c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, qcom_labibb_match); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic int qcom_labibb_regulator_probe(struct platform_device *pdev) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci struct labibb_regulator *vreg; 958c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 968c2ecf20Sopenharmony_ci struct regulator_config cfg = {}; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci const struct of_device_id *match; 998c2ecf20Sopenharmony_ci const struct labibb_regulator_data *reg_data; 1008c2ecf20Sopenharmony_ci struct regmap *reg_regmap; 1018c2ecf20Sopenharmony_ci unsigned int type; 1028c2ecf20Sopenharmony_ci int ret; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci reg_regmap = dev_get_regmap(pdev->dev.parent, NULL); 1058c2ecf20Sopenharmony_ci if (!reg_regmap) { 1068c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Couldn't get parent's regmap\n"); 1078c2ecf20Sopenharmony_ci return -ENODEV; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci match = of_match_device(qcom_labibb_match, &pdev->dev); 1118c2ecf20Sopenharmony_ci if (!match) 1128c2ecf20Sopenharmony_ci return -ENODEV; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci for (reg_data = match->data; reg_data->name; reg_data++) { 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* Validate if the type of regulator is indeed 1178c2ecf20Sopenharmony_ci * what's mentioned in DT. 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_ci ret = regmap_read(reg_regmap, reg_data->base + REG_PERPH_TYPE, 1208c2ecf20Sopenharmony_ci &type); 1218c2ecf20Sopenharmony_ci if (ret < 0) { 1228c2ecf20Sopenharmony_ci dev_err(dev, 1238c2ecf20Sopenharmony_ci "Peripheral type read failed ret=%d\n", 1248c2ecf20Sopenharmony_ci ret); 1258c2ecf20Sopenharmony_ci return -EINVAL; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (WARN_ON((type != QCOM_LAB_TYPE) && (type != QCOM_IBB_TYPE)) || 1298c2ecf20Sopenharmony_ci WARN_ON(type != reg_data->type)) 1308c2ecf20Sopenharmony_ci return -EINVAL; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci vreg = devm_kzalloc(&pdev->dev, sizeof(*vreg), 1338c2ecf20Sopenharmony_ci GFP_KERNEL); 1348c2ecf20Sopenharmony_ci if (!vreg) 1358c2ecf20Sopenharmony_ci return -ENOMEM; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci vreg->regmap = reg_regmap; 1388c2ecf20Sopenharmony_ci vreg->dev = dev; 1398c2ecf20Sopenharmony_ci vreg->base = reg_data->base; 1408c2ecf20Sopenharmony_ci vreg->type = reg_data->type; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci memcpy(&vreg->desc, reg_data->desc, sizeof(vreg->desc)); 1438c2ecf20Sopenharmony_ci vreg->desc.of_match = reg_data->name; 1448c2ecf20Sopenharmony_ci vreg->desc.name = reg_data->name; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci cfg.dev = vreg->dev; 1478c2ecf20Sopenharmony_ci cfg.driver_data = vreg; 1488c2ecf20Sopenharmony_ci cfg.regmap = vreg->regmap; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci vreg->rdev = devm_regulator_register(vreg->dev, &vreg->desc, 1518c2ecf20Sopenharmony_ci &cfg); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (IS_ERR(vreg->rdev)) { 1548c2ecf20Sopenharmony_ci dev_err(dev, "qcom_labibb: error registering %s : %d\n", 1558c2ecf20Sopenharmony_ci reg_data->name, ret); 1568c2ecf20Sopenharmony_ci return PTR_ERR(vreg->rdev); 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci return 0; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic struct platform_driver qcom_labibb_regulator_driver = { 1648c2ecf20Sopenharmony_ci .driver = { 1658c2ecf20Sopenharmony_ci .name = "qcom-lab-ibb-regulator", 1668c2ecf20Sopenharmony_ci .of_match_table = qcom_labibb_match, 1678c2ecf20Sopenharmony_ci }, 1688c2ecf20Sopenharmony_ci .probe = qcom_labibb_regulator_probe, 1698c2ecf20Sopenharmony_ci}; 1708c2ecf20Sopenharmony_cimodule_platform_driver(qcom_labibb_regulator_driver); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm labibb driver"); 1738c2ecf20Sopenharmony_ciMODULE_AUTHOR("Nisha Kumari <nishakumari@codeaurora.org>"); 1748c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sumit Semwal <sumit.semwal@linaro.org>"); 1758c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 176