111469e0bSBalaji T K /* 211469e0bSBalaji T K * pbias-regulator.c 311469e0bSBalaji T K * 411469e0bSBalaji T K * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ 511469e0bSBalaji T K * Author: Balaji T K <balajitk@ti.com> 611469e0bSBalaji T K * 711469e0bSBalaji T K * This program is free software; you can redistribute it and/or 811469e0bSBalaji T K * modify it under the terms of the GNU General Public License as 911469e0bSBalaji T K * published by the Free Software Foundation version 2. 1011469e0bSBalaji T K * 1111469e0bSBalaji T K * This program is distributed "as is" WITHOUT ANY WARRANTY of any 1211469e0bSBalaji T K * kind, whether express or implied; without even the implied warranty 1311469e0bSBalaji T K * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1411469e0bSBalaji T K * GNU General Public License for more details. 1511469e0bSBalaji T K */ 1611469e0bSBalaji T K 1711469e0bSBalaji T K #include <linux/err.h> 1811469e0bSBalaji T K #include <linux/io.h> 1911469e0bSBalaji T K #include <linux/module.h> 2011469e0bSBalaji T K #include <linux/mfd/syscon.h> 2111469e0bSBalaji T K #include <linux/platform_device.h> 2211469e0bSBalaji T K #include <linux/regulator/driver.h> 2311469e0bSBalaji T K #include <linux/regulator/machine.h> 2411469e0bSBalaji T K #include <linux/regulator/of_regulator.h> 2511469e0bSBalaji T K #include <linux/regmap.h> 2611469e0bSBalaji T K #include <linux/slab.h> 2711469e0bSBalaji T K #include <linux/of.h> 2811469e0bSBalaji T K #include <linux/of_device.h> 2911469e0bSBalaji T K 3011469e0bSBalaji T K struct pbias_reg_info { 3111469e0bSBalaji T K u32 enable; 3211469e0bSBalaji T K u32 enable_mask; 33c329061bSKishon Vijay Abraham I u32 disable_val; 3411469e0bSBalaji T K u32 vmode; 3511469e0bSBalaji T K unsigned int enable_time; 3611469e0bSBalaji T K char *name; 3711469e0bSBalaji T K }; 3811469e0bSBalaji T K 3911469e0bSBalaji T K struct pbias_regulator_data { 4011469e0bSBalaji T K struct regulator_desc desc; 4111469e0bSBalaji T K void __iomem *pbias_addr; 4211469e0bSBalaji T K struct regulator_dev *dev; 4311469e0bSBalaji T K struct regmap *syscon; 4411469e0bSBalaji T K const struct pbias_reg_info *info; 4511469e0bSBalaji T K int voltage; 4611469e0bSBalaji T K }; 4711469e0bSBalaji T K 4860e8c1e3SAxel Lin static const unsigned int pbias_volt_table[] = { 4960e8c1e3SAxel Lin 1800000, 5060e8c1e3SAxel Lin 3000000 5160e8c1e3SAxel Lin }; 5211469e0bSBalaji T K 5311469e0bSBalaji T K static struct regulator_ops pbias_regulator_voltage_ops = { 5460e8c1e3SAxel Lin .list_voltage = regulator_list_voltage_table, 5560e8c1e3SAxel Lin .get_voltage_sel = regulator_get_voltage_sel_regmap, 5660e8c1e3SAxel Lin .set_voltage_sel = regulator_set_voltage_sel_regmap, 5775dbf0a0SAxel Lin .enable = regulator_enable_regmap, 5860e8c1e3SAxel Lin .disable = regulator_disable_regmap, 5975dbf0a0SAxel Lin .is_enabled = regulator_is_enabled_regmap, 6011469e0bSBalaji T K }; 6111469e0bSBalaji T K 6211469e0bSBalaji T K static const struct pbias_reg_info pbias_mmc_omap2430 = { 6311469e0bSBalaji T K .enable = BIT(1), 6411469e0bSBalaji T K .enable_mask = BIT(1), 6511469e0bSBalaji T K .vmode = BIT(0), 66c329061bSKishon Vijay Abraham I .disable_val = 0, 6711469e0bSBalaji T K .enable_time = 100, 6811469e0bSBalaji T K .name = "pbias_mmc_omap2430" 6911469e0bSBalaji T K }; 7011469e0bSBalaji T K 7111469e0bSBalaji T K static const struct pbias_reg_info pbias_sim_omap3 = { 7211469e0bSBalaji T K .enable = BIT(9), 7311469e0bSBalaji T K .enable_mask = BIT(9), 7411469e0bSBalaji T K .vmode = BIT(8), 7511469e0bSBalaji T K .enable_time = 100, 7611469e0bSBalaji T K .name = "pbias_sim_omap3" 7711469e0bSBalaji T K }; 7811469e0bSBalaji T K 7911469e0bSBalaji T K static const struct pbias_reg_info pbias_mmc_omap4 = { 8011469e0bSBalaji T K .enable = BIT(26) | BIT(22), 8111469e0bSBalaji T K .enable_mask = BIT(26) | BIT(25) | BIT(22), 82c329061bSKishon Vijay Abraham I .disable_val = BIT(25), 8311469e0bSBalaji T K .vmode = BIT(21), 8411469e0bSBalaji T K .enable_time = 100, 8511469e0bSBalaji T K .name = "pbias_mmc_omap4" 8611469e0bSBalaji T K }; 8711469e0bSBalaji T K 8811469e0bSBalaji T K static const struct pbias_reg_info pbias_mmc_omap5 = { 8911469e0bSBalaji T K .enable = BIT(27) | BIT(26), 9011469e0bSBalaji T K .enable_mask = BIT(27) | BIT(25) | BIT(26), 91c329061bSKishon Vijay Abraham I .disable_val = BIT(25), 9211469e0bSBalaji T K .vmode = BIT(21), 9311469e0bSBalaji T K .enable_time = 100, 9411469e0bSBalaji T K .name = "pbias_mmc_omap5" 9511469e0bSBalaji T K }; 9611469e0bSBalaji T K 9711469e0bSBalaji T K static struct of_regulator_match pbias_matches[] = { 9811469e0bSBalaji T K { .name = "pbias_mmc_omap2430", .driver_data = (void *)&pbias_mmc_omap2430}, 9911469e0bSBalaji T K { .name = "pbias_sim_omap3", .driver_data = (void *)&pbias_sim_omap3}, 10011469e0bSBalaji T K { .name = "pbias_mmc_omap4", .driver_data = (void *)&pbias_mmc_omap4}, 10111469e0bSBalaji T K { .name = "pbias_mmc_omap5", .driver_data = (void *)&pbias_mmc_omap5}, 10211469e0bSBalaji T K }; 10311469e0bSBalaji T K #define PBIAS_NUM_REGS ARRAY_SIZE(pbias_matches) 10411469e0bSBalaji T K 10511469e0bSBalaji T K static const struct of_device_id pbias_of_match[] = { 10611469e0bSBalaji T K { .compatible = "ti,pbias-omap", }, 10711469e0bSBalaji T K {}, 10811469e0bSBalaji T K }; 10911469e0bSBalaji T K MODULE_DEVICE_TABLE(of, pbias_of_match); 11011469e0bSBalaji T K 11111469e0bSBalaji T K static int pbias_regulator_probe(struct platform_device *pdev) 11211469e0bSBalaji T K { 11311469e0bSBalaji T K struct device_node *np = pdev->dev.of_node; 11411469e0bSBalaji T K struct pbias_regulator_data *drvdata; 11511469e0bSBalaji T K struct resource *res; 11611469e0bSBalaji T K struct regulator_config cfg = { }; 11711469e0bSBalaji T K struct regmap *syscon; 11811469e0bSBalaji T K const struct pbias_reg_info *info; 11911469e0bSBalaji T K int ret = 0; 12011469e0bSBalaji T K int count, idx, data_idx = 0; 12111469e0bSBalaji T K 12211469e0bSBalaji T K count = of_regulator_match(&pdev->dev, np, pbias_matches, 12311469e0bSBalaji T K PBIAS_NUM_REGS); 12411469e0bSBalaji T K if (count < 0) 12511469e0bSBalaji T K return count; 12611469e0bSBalaji T K 12711469e0bSBalaji T K drvdata = devm_kzalloc(&pdev->dev, sizeof(struct pbias_regulator_data) 12811469e0bSBalaji T K * count, GFP_KERNEL); 1290ee42bb1SJingoo Han if (!drvdata) 13011469e0bSBalaji T K return -ENOMEM; 13111469e0bSBalaji T K 13211469e0bSBalaji T K syscon = syscon_regmap_lookup_by_phandle(np, "syscon"); 13311469e0bSBalaji T K if (IS_ERR(syscon)) 13411469e0bSBalaji T K return PTR_ERR(syscon); 13511469e0bSBalaji T K 13660e8c1e3SAxel Lin cfg.regmap = syscon; 13711469e0bSBalaji T K cfg.dev = &pdev->dev; 13811469e0bSBalaji T K 13911469e0bSBalaji T K for (idx = 0; idx < PBIAS_NUM_REGS && data_idx < count; idx++) { 14011469e0bSBalaji T K if (!pbias_matches[idx].init_data || 14111469e0bSBalaji T K !pbias_matches[idx].of_node) 14211469e0bSBalaji T K continue; 14311469e0bSBalaji T K 14411469e0bSBalaji T K info = pbias_matches[idx].driver_data; 14511469e0bSBalaji T K if (!info) 14611469e0bSBalaji T K return -ENODEV; 14711469e0bSBalaji T K 14811469e0bSBalaji T K res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 14911469e0bSBalaji T K if (!res) 15011469e0bSBalaji T K return -EINVAL; 15111469e0bSBalaji T K 15211469e0bSBalaji T K drvdata[data_idx].syscon = syscon; 15311469e0bSBalaji T K drvdata[data_idx].info = info; 15411469e0bSBalaji T K drvdata[data_idx].desc.name = info->name; 15511469e0bSBalaji T K drvdata[data_idx].desc.owner = THIS_MODULE; 15611469e0bSBalaji T K drvdata[data_idx].desc.type = REGULATOR_VOLTAGE; 15711469e0bSBalaji T K drvdata[data_idx].desc.ops = &pbias_regulator_voltage_ops; 15860e8c1e3SAxel Lin drvdata[data_idx].desc.volt_table = pbias_volt_table; 15911469e0bSBalaji T K drvdata[data_idx].desc.n_voltages = 2; 16011469e0bSBalaji T K drvdata[data_idx].desc.enable_time = info->enable_time; 16160e8c1e3SAxel Lin drvdata[data_idx].desc.vsel_reg = res->start; 16260e8c1e3SAxel Lin drvdata[data_idx].desc.vsel_mask = info->vmode; 16360e8c1e3SAxel Lin drvdata[data_idx].desc.enable_reg = res->start; 16460e8c1e3SAxel Lin drvdata[data_idx].desc.enable_mask = info->enable_mask; 16575dbf0a0SAxel Lin drvdata[data_idx].desc.enable_val = info->enable; 166c329061bSKishon Vijay Abraham I drvdata[data_idx].desc.disable_val = info->disable_val; 16711469e0bSBalaji T K 16811469e0bSBalaji T K cfg.init_data = pbias_matches[idx].init_data; 16911469e0bSBalaji T K cfg.driver_data = &drvdata[data_idx]; 17011469e0bSBalaji T K cfg.of_node = pbias_matches[idx].of_node; 17111469e0bSBalaji T K 17211469e0bSBalaji T K drvdata[data_idx].dev = devm_regulator_register(&pdev->dev, 17311469e0bSBalaji T K &drvdata[data_idx].desc, &cfg); 17411469e0bSBalaji T K if (IS_ERR(drvdata[data_idx].dev)) { 17511469e0bSBalaji T K ret = PTR_ERR(drvdata[data_idx].dev); 17611469e0bSBalaji T K dev_err(&pdev->dev, 17711469e0bSBalaji T K "Failed to register regulator: %d\n", ret); 17811469e0bSBalaji T K goto err_regulator; 17911469e0bSBalaji T K } 18011469e0bSBalaji T K data_idx++; 18111469e0bSBalaji T K } 18211469e0bSBalaji T K 18311469e0bSBalaji T K platform_set_drvdata(pdev, drvdata); 18411469e0bSBalaji T K 18511469e0bSBalaji T K err_regulator: 18611469e0bSBalaji T K return ret; 18711469e0bSBalaji T K } 18811469e0bSBalaji T K 18911469e0bSBalaji T K static struct platform_driver pbias_regulator_driver = { 19011469e0bSBalaji T K .probe = pbias_regulator_probe, 19111469e0bSBalaji T K .driver = { 19211469e0bSBalaji T K .name = "pbias-regulator", 19311469e0bSBalaji T K .of_match_table = of_match_ptr(pbias_of_match), 19411469e0bSBalaji T K }, 19511469e0bSBalaji T K }; 19611469e0bSBalaji T K 19711469e0bSBalaji T K module_platform_driver(pbias_regulator_driver); 19811469e0bSBalaji T K 19911469e0bSBalaji T K MODULE_AUTHOR("Balaji T K <balajitk@ti.com>"); 20011469e0bSBalaji T K MODULE_DESCRIPTION("pbias voltage regulator"); 20111469e0bSBalaji T K MODULE_LICENSE("GPL"); 20211469e0bSBalaji T K MODULE_ALIAS("platform:pbias-regulator"); 203