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 48b9c93646SKishon Vijay Abraham I struct pbias_of_data { 49b9c93646SKishon Vijay Abraham I unsigned int offset; 50b9c93646SKishon Vijay Abraham I }; 51b9c93646SKishon Vijay Abraham I 5260e8c1e3SAxel Lin static const unsigned int pbias_volt_table[] = { 5360e8c1e3SAxel Lin 1800000, 5460e8c1e3SAxel Lin 3000000 5560e8c1e3SAxel Lin }; 5611469e0bSBalaji T K 57a180df71SBhumika Goyal static const struct regulator_ops pbias_regulator_voltage_ops = { 5860e8c1e3SAxel Lin .list_voltage = regulator_list_voltage_table, 5960e8c1e3SAxel Lin .get_voltage_sel = regulator_get_voltage_sel_regmap, 6060e8c1e3SAxel Lin .set_voltage_sel = regulator_set_voltage_sel_regmap, 6175dbf0a0SAxel Lin .enable = regulator_enable_regmap, 6260e8c1e3SAxel Lin .disable = regulator_disable_regmap, 6375dbf0a0SAxel Lin .is_enabled = regulator_is_enabled_regmap, 6411469e0bSBalaji T K }; 6511469e0bSBalaji T K 6611469e0bSBalaji T K static const struct pbias_reg_info pbias_mmc_omap2430 = { 6711469e0bSBalaji T K .enable = BIT(1), 6811469e0bSBalaji T K .enable_mask = BIT(1), 6911469e0bSBalaji T K .vmode = BIT(0), 70c329061bSKishon Vijay Abraham I .disable_val = 0, 7111469e0bSBalaji T K .enable_time = 100, 7211469e0bSBalaji T K .name = "pbias_mmc_omap2430" 7311469e0bSBalaji T K }; 7411469e0bSBalaji T K 7511469e0bSBalaji T K static const struct pbias_reg_info pbias_sim_omap3 = { 7611469e0bSBalaji T K .enable = BIT(9), 7711469e0bSBalaji T K .enable_mask = BIT(9), 7811469e0bSBalaji T K .vmode = BIT(8), 7911469e0bSBalaji T K .enable_time = 100, 8011469e0bSBalaji T K .name = "pbias_sim_omap3" 8111469e0bSBalaji T K }; 8211469e0bSBalaji T K 8311469e0bSBalaji T K static const struct pbias_reg_info pbias_mmc_omap4 = { 8411469e0bSBalaji T K .enable = BIT(26) | BIT(22), 8511469e0bSBalaji T K .enable_mask = BIT(26) | BIT(25) | BIT(22), 86c329061bSKishon Vijay Abraham I .disable_val = BIT(25), 8711469e0bSBalaji T K .vmode = BIT(21), 8811469e0bSBalaji T K .enable_time = 100, 8911469e0bSBalaji T K .name = "pbias_mmc_omap4" 9011469e0bSBalaji T K }; 9111469e0bSBalaji T K 9211469e0bSBalaji T K static const struct pbias_reg_info pbias_mmc_omap5 = { 9311469e0bSBalaji T K .enable = BIT(27) | BIT(26), 9411469e0bSBalaji T K .enable_mask = BIT(27) | BIT(25) | BIT(26), 95c329061bSKishon Vijay Abraham I .disable_val = BIT(25), 9611469e0bSBalaji T K .vmode = BIT(21), 9711469e0bSBalaji T K .enable_time = 100, 9811469e0bSBalaji T K .name = "pbias_mmc_omap5" 9911469e0bSBalaji T K }; 10011469e0bSBalaji T K 10111469e0bSBalaji T K static struct of_regulator_match pbias_matches[] = { 10211469e0bSBalaji T K { .name = "pbias_mmc_omap2430", .driver_data = (void *)&pbias_mmc_omap2430}, 10311469e0bSBalaji T K { .name = "pbias_sim_omap3", .driver_data = (void *)&pbias_sim_omap3}, 10411469e0bSBalaji T K { .name = "pbias_mmc_omap4", .driver_data = (void *)&pbias_mmc_omap4}, 10511469e0bSBalaji T K { .name = "pbias_mmc_omap5", .driver_data = (void *)&pbias_mmc_omap5}, 10611469e0bSBalaji T K }; 10711469e0bSBalaji T K #define PBIAS_NUM_REGS ARRAY_SIZE(pbias_matches) 10811469e0bSBalaji T K 109b9c93646SKishon Vijay Abraham I /* Offset from SCM general area (and syscon) base */ 110b9c93646SKishon Vijay Abraham I 111b9c93646SKishon Vijay Abraham I static const struct pbias_of_data pbias_of_data_omap2 = { 112b9c93646SKishon Vijay Abraham I .offset = 0x230, 113b9c93646SKishon Vijay Abraham I }; 114b9c93646SKishon Vijay Abraham I 115b9c93646SKishon Vijay Abraham I static const struct pbias_of_data pbias_of_data_omap3 = { 116b9c93646SKishon Vijay Abraham I .offset = 0x2b0, 117b9c93646SKishon Vijay Abraham I }; 118b9c93646SKishon Vijay Abraham I 119b9c93646SKishon Vijay Abraham I static const struct pbias_of_data pbias_of_data_omap4 = { 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_omap5 = { 124b9c93646SKishon Vijay Abraham I .offset = 0x60, 125b9c93646SKishon Vijay Abraham I }; 126b9c93646SKishon Vijay Abraham I 127b9c93646SKishon Vijay Abraham I static const struct pbias_of_data pbias_of_data_dra7 = { 128b9c93646SKishon Vijay Abraham I .offset = 0xe00, 129b9c93646SKishon Vijay Abraham I }; 130b9c93646SKishon Vijay Abraham I 13111469e0bSBalaji T K static const struct of_device_id pbias_of_match[] = { 13211469e0bSBalaji T K { .compatible = "ti,pbias-omap", }, 133b9c93646SKishon Vijay Abraham I { .compatible = "ti,pbias-omap2", .data = &pbias_of_data_omap2, }, 134b9c93646SKishon Vijay Abraham I { .compatible = "ti,pbias-omap3", .data = &pbias_of_data_omap3, }, 135b9c93646SKishon Vijay Abraham I { .compatible = "ti,pbias-omap4", .data = &pbias_of_data_omap4, }, 136b9c93646SKishon Vijay Abraham I { .compatible = "ti,pbias-omap5", .data = &pbias_of_data_omap5, }, 137b9c93646SKishon Vijay Abraham I { .compatible = "ti,pbias-dra7", .data = &pbias_of_data_dra7, }, 13811469e0bSBalaji T K {}, 13911469e0bSBalaji T K }; 14011469e0bSBalaji T K MODULE_DEVICE_TABLE(of, pbias_of_match); 14111469e0bSBalaji T K 14211469e0bSBalaji T K static int pbias_regulator_probe(struct platform_device *pdev) 14311469e0bSBalaji T K { 14411469e0bSBalaji T K struct device_node *np = pdev->dev.of_node; 14511469e0bSBalaji T K struct pbias_regulator_data *drvdata; 14611469e0bSBalaji T K struct resource *res; 14711469e0bSBalaji T K struct regulator_config cfg = { }; 14811469e0bSBalaji T K struct regmap *syscon; 14911469e0bSBalaji T K const struct pbias_reg_info *info; 15011469e0bSBalaji T K int ret = 0; 15111469e0bSBalaji T K int count, idx, data_idx = 0; 152b9c93646SKishon Vijay Abraham I const struct of_device_id *match; 153b9c93646SKishon Vijay Abraham I const struct pbias_of_data *data; 154b9c93646SKishon Vijay Abraham I unsigned int offset; 15511469e0bSBalaji T K 15611469e0bSBalaji T K count = of_regulator_match(&pdev->dev, np, pbias_matches, 15711469e0bSBalaji T K PBIAS_NUM_REGS); 15811469e0bSBalaji T K if (count < 0) 15911469e0bSBalaji T K return count; 16011469e0bSBalaji T K 16111469e0bSBalaji T K drvdata = devm_kzalloc(&pdev->dev, sizeof(struct pbias_regulator_data) 16211469e0bSBalaji T K * count, GFP_KERNEL); 1630ee42bb1SJingoo Han if (!drvdata) 16411469e0bSBalaji T K return -ENOMEM; 16511469e0bSBalaji T K 16611469e0bSBalaji T K syscon = syscon_regmap_lookup_by_phandle(np, "syscon"); 16711469e0bSBalaji T K if (IS_ERR(syscon)) 16811469e0bSBalaji T K return PTR_ERR(syscon); 16911469e0bSBalaji T K 170b9c93646SKishon Vijay Abraham I match = of_match_device(of_match_ptr(pbias_of_match), &pdev->dev); 171b9c93646SKishon Vijay Abraham I if (match && match->data) { 172b9c93646SKishon Vijay Abraham I data = match->data; 173b9c93646SKishon Vijay Abraham I offset = data->offset; 174b9c93646SKishon Vijay Abraham I } else { 175b9c93646SKishon Vijay Abraham I res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 176b9c93646SKishon Vijay Abraham I if (!res) 177b9c93646SKishon Vijay Abraham I return -EINVAL; 178b9c93646SKishon Vijay Abraham I 179b9c93646SKishon Vijay Abraham I offset = res->start; 180b9c93646SKishon Vijay Abraham I dev_WARN(&pdev->dev, 181b9c93646SKishon Vijay Abraham I "using legacy dt data for pbias offset\n"); 182b9c93646SKishon Vijay Abraham I } 183b9c93646SKishon Vijay Abraham I 18460e8c1e3SAxel Lin cfg.regmap = syscon; 18511469e0bSBalaji T K cfg.dev = &pdev->dev; 18611469e0bSBalaji T K 18711469e0bSBalaji T K for (idx = 0; idx < PBIAS_NUM_REGS && data_idx < count; idx++) { 18811469e0bSBalaji T K if (!pbias_matches[idx].init_data || 18911469e0bSBalaji T K !pbias_matches[idx].of_node) 19011469e0bSBalaji T K continue; 19111469e0bSBalaji T K 19211469e0bSBalaji T K info = pbias_matches[idx].driver_data; 19311469e0bSBalaji T K if (!info) 19411469e0bSBalaji T K return -ENODEV; 19511469e0bSBalaji T K 19611469e0bSBalaji T K drvdata[data_idx].syscon = syscon; 19711469e0bSBalaji T K drvdata[data_idx].info = info; 19811469e0bSBalaji T K drvdata[data_idx].desc.name = info->name; 19911469e0bSBalaji T K drvdata[data_idx].desc.owner = THIS_MODULE; 20011469e0bSBalaji T K drvdata[data_idx].desc.type = REGULATOR_VOLTAGE; 20111469e0bSBalaji T K drvdata[data_idx].desc.ops = &pbias_regulator_voltage_ops; 20260e8c1e3SAxel Lin drvdata[data_idx].desc.volt_table = pbias_volt_table; 20311469e0bSBalaji T K drvdata[data_idx].desc.n_voltages = 2; 20411469e0bSBalaji T K drvdata[data_idx].desc.enable_time = info->enable_time; 205b9c93646SKishon Vijay Abraham I drvdata[data_idx].desc.vsel_reg = offset; 20660e8c1e3SAxel Lin drvdata[data_idx].desc.vsel_mask = info->vmode; 207b9c93646SKishon Vijay Abraham I drvdata[data_idx].desc.enable_reg = offset; 20860e8c1e3SAxel Lin drvdata[data_idx].desc.enable_mask = info->enable_mask; 20975dbf0a0SAxel Lin drvdata[data_idx].desc.enable_val = info->enable; 210c329061bSKishon Vijay Abraham I drvdata[data_idx].desc.disable_val = info->disable_val; 21111469e0bSBalaji T K 21211469e0bSBalaji T K cfg.init_data = pbias_matches[idx].init_data; 21311469e0bSBalaji T K cfg.driver_data = &drvdata[data_idx]; 21411469e0bSBalaji T K cfg.of_node = pbias_matches[idx].of_node; 21511469e0bSBalaji T K 21611469e0bSBalaji T K drvdata[data_idx].dev = devm_regulator_register(&pdev->dev, 21711469e0bSBalaji T K &drvdata[data_idx].desc, &cfg); 21811469e0bSBalaji T K if (IS_ERR(drvdata[data_idx].dev)) { 21911469e0bSBalaji T K ret = PTR_ERR(drvdata[data_idx].dev); 22011469e0bSBalaji T K dev_err(&pdev->dev, 22111469e0bSBalaji T K "Failed to register regulator: %d\n", ret); 22211469e0bSBalaji T K goto err_regulator; 22311469e0bSBalaji T K } 22411469e0bSBalaji T K data_idx++; 22511469e0bSBalaji T K } 22611469e0bSBalaji T K 22711469e0bSBalaji T K platform_set_drvdata(pdev, drvdata); 22811469e0bSBalaji T K 22911469e0bSBalaji T K err_regulator: 23011469e0bSBalaji T K return ret; 23111469e0bSBalaji T K } 23211469e0bSBalaji T K 23311469e0bSBalaji T K static struct platform_driver pbias_regulator_driver = { 23411469e0bSBalaji T K .probe = pbias_regulator_probe, 23511469e0bSBalaji T K .driver = { 23611469e0bSBalaji T K .name = "pbias-regulator", 23711469e0bSBalaji T K .of_match_table = of_match_ptr(pbias_of_match), 23811469e0bSBalaji T K }, 23911469e0bSBalaji T K }; 24011469e0bSBalaji T K 24111469e0bSBalaji T K module_platform_driver(pbias_regulator_driver); 24211469e0bSBalaji T K 24311469e0bSBalaji T K MODULE_AUTHOR("Balaji T K <balajitk@ti.com>"); 24411469e0bSBalaji T K MODULE_DESCRIPTION("pbias voltage regulator"); 24511469e0bSBalaji T K MODULE_LICENSE("GPL"); 24611469e0bSBalaji T K MODULE_ALIAS("platform:pbias-regulator"); 247