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; 3311469e0bSBalaji T K u32 vmode; 3411469e0bSBalaji T K unsigned int enable_time; 3511469e0bSBalaji T K char *name; 3611469e0bSBalaji T K }; 3711469e0bSBalaji T K 3811469e0bSBalaji T K struct pbias_regulator_data { 3911469e0bSBalaji T K struct regulator_desc desc; 4011469e0bSBalaji T K void __iomem *pbias_addr; 4111469e0bSBalaji T K struct regulator_dev *dev; 4211469e0bSBalaji T K struct regmap *syscon; 4311469e0bSBalaji T K const struct pbias_reg_info *info; 4411469e0bSBalaji T K int voltage; 4511469e0bSBalaji T K }; 4611469e0bSBalaji T K 47b9c93646SKishon Vijay Abraham I struct pbias_of_data { 48b9c93646SKishon Vijay Abraham I unsigned int offset; 49b9c93646SKishon Vijay Abraham I }; 50b9c93646SKishon Vijay Abraham I 5160e8c1e3SAxel Lin static const unsigned int pbias_volt_table[] = { 5260e8c1e3SAxel Lin 1800000, 5360e8c1e3SAxel Lin 3000000 5460e8c1e3SAxel Lin }; 5511469e0bSBalaji T K 5611469e0bSBalaji T K static struct regulator_ops pbias_regulator_voltage_ops = { 5760e8c1e3SAxel Lin .list_voltage = regulator_list_voltage_table, 5860e8c1e3SAxel Lin .get_voltage_sel = regulator_get_voltage_sel_regmap, 5960e8c1e3SAxel Lin .set_voltage_sel = regulator_set_voltage_sel_regmap, 6075dbf0a0SAxel Lin .enable = regulator_enable_regmap, 6160e8c1e3SAxel Lin .disable = regulator_disable_regmap, 6275dbf0a0SAxel Lin .is_enabled = regulator_is_enabled_regmap, 6311469e0bSBalaji T K }; 6411469e0bSBalaji T K 6511469e0bSBalaji T K static const struct pbias_reg_info pbias_mmc_omap2430 = { 6611469e0bSBalaji T K .enable = BIT(1), 6711469e0bSBalaji T K .enable_mask = BIT(1), 6811469e0bSBalaji T K .vmode = BIT(0), 6911469e0bSBalaji T K .enable_time = 100, 7011469e0bSBalaji T K .name = "pbias_mmc_omap2430" 7111469e0bSBalaji T K }; 7211469e0bSBalaji T K 7311469e0bSBalaji T K static const struct pbias_reg_info pbias_sim_omap3 = { 7411469e0bSBalaji T K .enable = BIT(9), 7511469e0bSBalaji T K .enable_mask = BIT(9), 7611469e0bSBalaji T K .vmode = BIT(8), 7711469e0bSBalaji T K .enable_time = 100, 7811469e0bSBalaji T K .name = "pbias_sim_omap3" 7911469e0bSBalaji T K }; 8011469e0bSBalaji T K 8111469e0bSBalaji T K static const struct pbias_reg_info pbias_mmc_omap4 = { 8211469e0bSBalaji T K .enable = BIT(26) | BIT(22), 8311469e0bSBalaji T K .enable_mask = BIT(26) | BIT(25) | BIT(22), 8411469e0bSBalaji T K .vmode = BIT(21), 8511469e0bSBalaji T K .enable_time = 100, 8611469e0bSBalaji T K .name = "pbias_mmc_omap4" 8711469e0bSBalaji T K }; 8811469e0bSBalaji T K 8911469e0bSBalaji T K static const struct pbias_reg_info pbias_mmc_omap5 = { 9011469e0bSBalaji T K .enable = BIT(27) | BIT(26), 9111469e0bSBalaji T K .enable_mask = BIT(27) | BIT(25) | BIT(26), 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 105b9c93646SKishon Vijay Abraham I /* Offset from SCM general area (and syscon) base */ 106b9c93646SKishon Vijay Abraham I 107b9c93646SKishon Vijay Abraham I static const struct pbias_of_data pbias_of_data_omap2 = { 108b9c93646SKishon Vijay Abraham I .offset = 0x230, 109b9c93646SKishon Vijay Abraham I }; 110b9c93646SKishon Vijay Abraham I 111b9c93646SKishon Vijay Abraham I static const struct pbias_of_data pbias_of_data_omap3 = { 112b9c93646SKishon Vijay Abraham I .offset = 0x2b0, 113b9c93646SKishon Vijay Abraham I }; 114b9c93646SKishon Vijay Abraham I 115b9c93646SKishon Vijay Abraham I static const struct pbias_of_data pbias_of_data_omap4 = { 116b9c93646SKishon Vijay Abraham I .offset = 0x60, 117b9c93646SKishon Vijay Abraham I }; 118b9c93646SKishon Vijay Abraham I 119b9c93646SKishon Vijay Abraham I static const struct pbias_of_data pbias_of_data_omap5 = { 120b9c93646SKishon Vijay Abraham I .offset = 0x60, 121b9c93646SKishon Vijay Abraham I }; 122b9c93646SKishon Vijay Abraham I 123b9c93646SKishon Vijay Abraham I static const struct pbias_of_data pbias_of_data_dra7 = { 124b9c93646SKishon Vijay Abraham I .offset = 0xe00, 125b9c93646SKishon Vijay Abraham I }; 126b9c93646SKishon Vijay Abraham I 12711469e0bSBalaji T K static const struct of_device_id pbias_of_match[] = { 12811469e0bSBalaji T K { .compatible = "ti,pbias-omap", }, 129b9c93646SKishon Vijay Abraham I { .compatible = "ti,pbias-omap2", .data = &pbias_of_data_omap2, }, 130b9c93646SKishon Vijay Abraham I { .compatible = "ti,pbias-omap3", .data = &pbias_of_data_omap3, }, 131b9c93646SKishon Vijay Abraham I { .compatible = "ti,pbias-omap4", .data = &pbias_of_data_omap4, }, 132b9c93646SKishon Vijay Abraham I { .compatible = "ti,pbias-omap5", .data = &pbias_of_data_omap5, }, 133b9c93646SKishon Vijay Abraham I { .compatible = "ti,pbias-dra7", .data = &pbias_of_data_dra7, }, 13411469e0bSBalaji T K {}, 13511469e0bSBalaji T K }; 13611469e0bSBalaji T K MODULE_DEVICE_TABLE(of, pbias_of_match); 13711469e0bSBalaji T K 13811469e0bSBalaji T K static int pbias_regulator_probe(struct platform_device *pdev) 13911469e0bSBalaji T K { 14011469e0bSBalaji T K struct device_node *np = pdev->dev.of_node; 14111469e0bSBalaji T K struct pbias_regulator_data *drvdata; 14211469e0bSBalaji T K struct resource *res; 14311469e0bSBalaji T K struct regulator_config cfg = { }; 14411469e0bSBalaji T K struct regmap *syscon; 14511469e0bSBalaji T K const struct pbias_reg_info *info; 14611469e0bSBalaji T K int ret = 0; 14711469e0bSBalaji T K int count, idx, data_idx = 0; 148b9c93646SKishon Vijay Abraham I const struct of_device_id *match; 149b9c93646SKishon Vijay Abraham I const struct pbias_of_data *data; 150b9c93646SKishon Vijay Abraham I unsigned int offset; 15111469e0bSBalaji T K 15211469e0bSBalaji T K count = of_regulator_match(&pdev->dev, np, pbias_matches, 15311469e0bSBalaji T K PBIAS_NUM_REGS); 15411469e0bSBalaji T K if (count < 0) 15511469e0bSBalaji T K return count; 15611469e0bSBalaji T K 15711469e0bSBalaji T K drvdata = devm_kzalloc(&pdev->dev, sizeof(struct pbias_regulator_data) 15811469e0bSBalaji T K * count, GFP_KERNEL); 1590ee42bb1SJingoo Han if (!drvdata) 16011469e0bSBalaji T K return -ENOMEM; 16111469e0bSBalaji T K 16211469e0bSBalaji T K syscon = syscon_regmap_lookup_by_phandle(np, "syscon"); 16311469e0bSBalaji T K if (IS_ERR(syscon)) 16411469e0bSBalaji T K return PTR_ERR(syscon); 16511469e0bSBalaji T K 166b9c93646SKishon Vijay Abraham I match = of_match_device(of_match_ptr(pbias_of_match), &pdev->dev); 167b9c93646SKishon Vijay Abraham I if (match && match->data) { 168b9c93646SKishon Vijay Abraham I data = match->data; 169b9c93646SKishon Vijay Abraham I offset = data->offset; 170b9c93646SKishon Vijay Abraham I } else { 171b9c93646SKishon Vijay Abraham I res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 172b9c93646SKishon Vijay Abraham I if (!res) 173b9c93646SKishon Vijay Abraham I return -EINVAL; 174b9c93646SKishon Vijay Abraham I 175b9c93646SKishon Vijay Abraham I offset = res->start; 176b9c93646SKishon Vijay Abraham I dev_WARN(&pdev->dev, 177b9c93646SKishon Vijay Abraham I "using legacy dt data for pbias offset\n"); 178b9c93646SKishon Vijay Abraham I } 179b9c93646SKishon Vijay Abraham I 18060e8c1e3SAxel Lin cfg.regmap = syscon; 18111469e0bSBalaji T K cfg.dev = &pdev->dev; 18211469e0bSBalaji T K 18311469e0bSBalaji T K for (idx = 0; idx < PBIAS_NUM_REGS && data_idx < count; idx++) { 18411469e0bSBalaji T K if (!pbias_matches[idx].init_data || 18511469e0bSBalaji T K !pbias_matches[idx].of_node) 18611469e0bSBalaji T K continue; 18711469e0bSBalaji T K 18811469e0bSBalaji T K info = pbias_matches[idx].driver_data; 18911469e0bSBalaji T K if (!info) 19011469e0bSBalaji T K return -ENODEV; 19111469e0bSBalaji T K 19211469e0bSBalaji T K drvdata[data_idx].syscon = syscon; 19311469e0bSBalaji T K drvdata[data_idx].info = info; 19411469e0bSBalaji T K drvdata[data_idx].desc.name = info->name; 19511469e0bSBalaji T K drvdata[data_idx].desc.owner = THIS_MODULE; 19611469e0bSBalaji T K drvdata[data_idx].desc.type = REGULATOR_VOLTAGE; 19711469e0bSBalaji T K drvdata[data_idx].desc.ops = &pbias_regulator_voltage_ops; 19860e8c1e3SAxel Lin drvdata[data_idx].desc.volt_table = pbias_volt_table; 19911469e0bSBalaji T K drvdata[data_idx].desc.n_voltages = 2; 20011469e0bSBalaji T K drvdata[data_idx].desc.enable_time = info->enable_time; 201b9c93646SKishon Vijay Abraham I drvdata[data_idx].desc.vsel_reg = offset; 20260e8c1e3SAxel Lin drvdata[data_idx].desc.vsel_mask = info->vmode; 203b9c93646SKishon Vijay Abraham I drvdata[data_idx].desc.enable_reg = offset; 20460e8c1e3SAxel Lin drvdata[data_idx].desc.enable_mask = info->enable_mask; 20575dbf0a0SAxel Lin drvdata[data_idx].desc.enable_val = info->enable; 20611469e0bSBalaji T K 20711469e0bSBalaji T K cfg.init_data = pbias_matches[idx].init_data; 20811469e0bSBalaji T K cfg.driver_data = &drvdata[data_idx]; 20911469e0bSBalaji T K cfg.of_node = pbias_matches[idx].of_node; 21011469e0bSBalaji T K 21111469e0bSBalaji T K drvdata[data_idx].dev = devm_regulator_register(&pdev->dev, 21211469e0bSBalaji T K &drvdata[data_idx].desc, &cfg); 21311469e0bSBalaji T K if (IS_ERR(drvdata[data_idx].dev)) { 21411469e0bSBalaji T K ret = PTR_ERR(drvdata[data_idx].dev); 21511469e0bSBalaji T K dev_err(&pdev->dev, 21611469e0bSBalaji T K "Failed to register regulator: %d\n", ret); 21711469e0bSBalaji T K goto err_regulator; 21811469e0bSBalaji T K } 21911469e0bSBalaji T K data_idx++; 22011469e0bSBalaji T K } 22111469e0bSBalaji T K 22211469e0bSBalaji T K platform_set_drvdata(pdev, drvdata); 22311469e0bSBalaji T K 22411469e0bSBalaji T K err_regulator: 22511469e0bSBalaji T K return ret; 22611469e0bSBalaji T K } 22711469e0bSBalaji T K 22811469e0bSBalaji T K static struct platform_driver pbias_regulator_driver = { 22911469e0bSBalaji T K .probe = pbias_regulator_probe, 23011469e0bSBalaji T K .driver = { 23111469e0bSBalaji T K .name = "pbias-regulator", 23211469e0bSBalaji T K .of_match_table = of_match_ptr(pbias_of_match), 23311469e0bSBalaji T K }, 23411469e0bSBalaji T K }; 23511469e0bSBalaji T K 23611469e0bSBalaji T K module_platform_driver(pbias_regulator_driver); 23711469e0bSBalaji T K 23811469e0bSBalaji T K MODULE_AUTHOR("Balaji T K <balajitk@ti.com>"); 23911469e0bSBalaji T K MODULE_DESCRIPTION("pbias voltage regulator"); 24011469e0bSBalaji T K MODULE_LICENSE("GPL"); 24111469e0bSBalaji T K MODULE_ALIAS("platform:pbias-regulator"); 242