/* * TI LM3632 LDO Regulator Driver * * Copyright 2015 Texas Instruments * * Author: Milo Kim * * 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. * */ #include #include #include #include #include #include #include #include #include enum lm3632_regulator_id { LM3632_REGULATOR_BOOST, LM3632_LDO_POS, LM3632_LDO_NEG, }; struct lm3632_regulator { int ena_gpio; struct lm3632 *lm3632; struct regulator_desc *desc; struct regulator_dev *regulator; struct regulator_init_data *init_data; }; static const int lm3632_boost_vtbl[] = { 4500000, 4550000, 4600000, 4650000, 4700000, 4750000, 4800000, 4850000, 4900000, 4950000, 5000000, 5050000, 5100000, 5150000, 5200000, 5250000, 5300000, 5350000, 5400000, 5450000, 5500000, 5550000, 5600000, 5650000, 5700000, 5750000, 5800000, 5850000, 5900000, 5950000, 6000000, 6050000, 6100000, 6150000, 6200000, 6250000, 6300000, 6350000, 6400000, }; static const int lm3632_ldo_vtbl[] = { 4000000, 4050000, 4100000, 4150000, 4200000, 4250000, 4300000, 4350000, 4400000, 4450000, 4500000, 4550000, 4600000, 4650000, 4700000, 4750000, 4800000, 4850000, 4900000, 4950000, 5000000, 5050000, 5100000, 5150000, 5200000, 5250000, 5300000, 5350000, 5400000, 5450000, 5500000, 5550000, 5600000, 5650000, 5700000, 5750000, 5800000, 5850000, 5900000, 5950000, 6000000, }; static struct regulator_ops lm3632_boost_voltage_table_ops = { .list_voltage = regulator_list_voltage_table, .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, }; static struct regulator_ops lm3632_regulator_voltage_table_ops = { .list_voltage = regulator_list_voltage_table, .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, }; static struct regulator_desc lm3632_regulator_desc[] = { { .name = "vboost", .id = LM3632_REGULATOR_BOOST, .ops = &lm3632_boost_voltage_table_ops, .n_voltages = ARRAY_SIZE(lm3632_boost_vtbl), .volt_table = lm3632_boost_vtbl, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, .vsel_reg = LM3632_REG_VOUT_BOOST, .vsel_mask = LM3632_VOUT_MASK, }, { .name = "ldo_vpos", .id = LM3632_LDO_POS, .ops = &lm3632_regulator_voltage_table_ops, .n_voltages = ARRAY_SIZE(lm3632_ldo_vtbl), .volt_table = lm3632_ldo_vtbl, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, .vsel_reg = LM3632_REG_VOUT_POS, .vsel_mask = LM3632_VOUT_MASK, .enable_reg = LM3632_REG_BIAS_CONFIG, .enable_mask = LM3632_EN_VPOS_MASK, }, { .name = "ldo_vneg", .id = LM3632_LDO_NEG, .ops = &lm3632_regulator_voltage_table_ops, .n_voltages = ARRAY_SIZE(lm3632_ldo_vtbl), .volt_table = lm3632_ldo_vtbl, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, .vsel_reg = LM3632_REG_VOUT_NEG, .vsel_mask = LM3632_VOUT_MASK, .enable_reg = LM3632_REG_BIAS_CONFIG, .enable_mask = LM3632_EN_VNEG_MASK, }, }; static struct of_regulator_match lm3632_regulator_matches[] = { { .name = "vboost", .driver_data = (void *)LM3632_REGULATOR_BOOST, }, { .name = "vpos", .driver_data = (void *)LM3632_LDO_POS, }, { .name = "vneg", .driver_data = (void *)LM3632_LDO_NEG, }, }; static int lm3632_regulator_parse_dt(struct device *dev, struct lm3632_regulator *lm3632_regulator, int id) { struct device_node *node = dev->of_node; int count; int gpio; count = of_regulator_match(dev, node, &lm3632_regulator_matches[id], 1); if (count <= 0) return -ENODEV; lm3632_regulator->init_data = lm3632_regulator_matches[id].init_data; /* * Check LCM_EN1/2_GPIO is configured. * Those pins are used for enabling VPOS/VNEG LDOs. */ if (id == LM3632_LDO_POS) { gpio = of_get_named_gpio(node, "ti,lcm-en1-gpio", 0); if (gpio > 0) lm3632_regulator->ena_gpio = gpio; } else if (id == LM3632_LDO_NEG) { gpio = of_get_named_gpio(node, "ti,lcm-en2-gpio", 0); if (gpio > 0) lm3632_regulator->ena_gpio = gpio; } return 0; } static int lm3632_regulator_probe(struct platform_device *pdev) { struct lm3632 *lm3632 = dev_get_drvdata(pdev->dev.parent); struct lm3632_regulator *lm3632_regulator; struct regulator_config cfg = { }; struct regulator_dev *rdev; int id = pdev->id; int ret; lm3632_regulator = devm_kzalloc(&pdev->dev, sizeof(*lm3632_regulator), GFP_KERNEL); if (!lm3632_regulator) return -ENOMEM; lm3632_regulator->ena_gpio = 0; lm3632_regulator->lm3632 = lm3632; lm3632_regulator->init_data = lm3632->pdata->regulator_data[id]; if (!lm3632_regulator->init_data) { if (IS_ENABLED(CONFIG_OF)) ret = lm3632_regulator_parse_dt(&pdev->dev, lm3632_regulator, id); else ret = -ENODEV; if (ret) return ret; } cfg.dev = pdev->dev.parent; cfg.init_data = lm3632_regulator->init_data; cfg.driver_data = lm3632_regulator; cfg.regmap = lm3632->regmap; /* Update register value if external enable pin is used */ if (lm3632_regulator->ena_gpio > 0) { cfg.ena_gpio = lm3632_regulator->ena_gpio; cfg.ena_gpio_flags = GPIOF_OUT_INIT_LOW; ret = lm3632_update_bits(lm3632_regulator->lm3632, LM3632_REG_BIAS_CONFIG, LM3632_EXT_EN_MASK, LM3632_EXT_EN_MASK); if (ret) { dev_err(&pdev->dev, "external pin err: %d\n", ret); return ret; } } rdev = regulator_register(&lm3632_regulator_desc[id], &cfg); if (IS_ERR(rdev)) { ret = PTR_ERR(rdev); dev_err(&pdev->dev, "[%d] regulator register err: %d\n", id, ret); return ret; } lm3632_regulator->regulator = rdev; platform_set_drvdata(pdev, lm3632_regulator); return 0; } static int lm3632_regulator_remove(struct platform_device *pdev) { struct lm3632_regulator *lm3632_regulator = platform_get_drvdata(pdev); regulator_unregister(lm3632_regulator->regulator); return 0; } #ifdef CONFIG_OF static const struct of_device_id lm3632_regulator_of_match[] = { { .compatible = "ti,lm3632-regulator", }, { } }; MODULE_DEVICE_TABLE(of, lm3632_regulator_of_match); #endif static struct platform_driver lm3632_regulator_driver = { .probe = lm3632_regulator_probe, .remove = lm3632_regulator_remove, .driver = { .name = "lm3632-regulator", .owner = THIS_MODULE, .of_match_table = of_match_ptr(lm3632_regulator_of_match), }, }; module_platform_driver(lm3632_regulator_driver); MODULE_DESCRIPTION("TI LM3632 Regulator Driver"); MODULE_AUTHOR("Milo Kim"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:lm3632-regulator");