|  | /* | 
|  | * pbias-regulator.c | 
|  | * | 
|  | * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ | 
|  | * Author: Balaji T K <balajitk@ti.com> | 
|  | * | 
|  | * This program is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU General Public License as | 
|  | * published by the Free Software Foundation version 2. | 
|  | * | 
|  | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | 
|  | * kind, whether express or implied; without even the implied warranty | 
|  | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | * GNU General Public License for more details. | 
|  | */ | 
|  |  | 
|  | #include <linux/err.h> | 
|  | #include <linux/io.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/mfd/syscon.h> | 
|  | #include <linux/platform_device.h> | 
|  | #include <linux/regulator/driver.h> | 
|  | #include <linux/regulator/machine.h> | 
|  | #include <linux/regulator/of_regulator.h> | 
|  | #include <linux/regmap.h> | 
|  | #include <linux/slab.h> | 
|  | #include <linux/of.h> | 
|  | #include <linux/of_device.h> | 
|  |  | 
|  | struct pbias_reg_info { | 
|  | u32 enable; | 
|  | u32 enable_mask; | 
|  | u32 disable_val; | 
|  | u32 vmode; | 
|  | unsigned int enable_time; | 
|  | char *name; | 
|  | }; | 
|  |  | 
|  | struct pbias_regulator_data { | 
|  | struct regulator_desc desc; | 
|  | void __iomem *pbias_addr; | 
|  | struct regulator_dev *dev; | 
|  | struct regmap *syscon; | 
|  | const struct pbias_reg_info *info; | 
|  | int voltage; | 
|  | }; | 
|  |  | 
|  | struct pbias_of_data { | 
|  | unsigned int offset; | 
|  | }; | 
|  |  | 
|  | static const unsigned int pbias_volt_table[] = { | 
|  | 1800000, | 
|  | 3000000 | 
|  | }; | 
|  |  | 
|  | static struct regulator_ops pbias_regulator_voltage_ops = { | 
|  | .list_voltage = regulator_list_voltage_table, | 
|  | .get_voltage_sel = regulator_get_voltage_sel_regmap, | 
|  | .set_voltage_sel = regulator_set_voltage_sel_regmap, | 
|  | .enable = regulator_enable_regmap, | 
|  | .disable = regulator_disable_regmap, | 
|  | .is_enabled = regulator_is_enabled_regmap, | 
|  | }; | 
|  |  | 
|  | static const struct pbias_reg_info pbias_mmc_omap2430 = { | 
|  | .enable = BIT(1), | 
|  | .enable_mask = BIT(1), | 
|  | .vmode = BIT(0), | 
|  | .disable_val = 0, | 
|  | .enable_time = 100, | 
|  | .name = "pbias_mmc_omap2430" | 
|  | }; | 
|  |  | 
|  | static const struct pbias_reg_info pbias_sim_omap3 = { | 
|  | .enable = BIT(9), | 
|  | .enable_mask = BIT(9), | 
|  | .vmode = BIT(8), | 
|  | .enable_time = 100, | 
|  | .name = "pbias_sim_omap3" | 
|  | }; | 
|  |  | 
|  | static const struct pbias_reg_info pbias_mmc_omap4 = { | 
|  | .enable = BIT(26) | BIT(22), | 
|  | .enable_mask = BIT(26) | BIT(25) | BIT(22), | 
|  | .disable_val = BIT(25), | 
|  | .vmode = BIT(21), | 
|  | .enable_time = 100, | 
|  | .name = "pbias_mmc_omap4" | 
|  | }; | 
|  |  | 
|  | static const struct pbias_reg_info pbias_mmc_omap5 = { | 
|  | .enable = BIT(27) | BIT(26), | 
|  | .enable_mask = BIT(27) | BIT(25) | BIT(26), | 
|  | .disable_val = BIT(25), | 
|  | .vmode = BIT(21), | 
|  | .enable_time = 100, | 
|  | .name = "pbias_mmc_omap5" | 
|  | }; | 
|  |  | 
|  | static struct of_regulator_match pbias_matches[] = { | 
|  | { .name = "pbias_mmc_omap2430", .driver_data = (void *)&pbias_mmc_omap2430}, | 
|  | { .name = "pbias_sim_omap3", .driver_data = (void *)&pbias_sim_omap3}, | 
|  | { .name = "pbias_mmc_omap4", .driver_data = (void *)&pbias_mmc_omap4}, | 
|  | { .name = "pbias_mmc_omap5", .driver_data = (void *)&pbias_mmc_omap5}, | 
|  | }; | 
|  | #define PBIAS_NUM_REGS	ARRAY_SIZE(pbias_matches) | 
|  |  | 
|  | /* Offset from SCM general area (and syscon) base */ | 
|  |  | 
|  | static const struct pbias_of_data pbias_of_data_omap2 = { | 
|  | .offset = 0x230, | 
|  | }; | 
|  |  | 
|  | static const struct pbias_of_data pbias_of_data_omap3 = { | 
|  | .offset = 0x2b0, | 
|  | }; | 
|  |  | 
|  | static const struct pbias_of_data pbias_of_data_omap4 = { | 
|  | .offset = 0x60, | 
|  | }; | 
|  |  | 
|  | static const struct pbias_of_data pbias_of_data_omap5 = { | 
|  | .offset = 0x60, | 
|  | }; | 
|  |  | 
|  | static const struct pbias_of_data pbias_of_data_dra7 = { | 
|  | .offset = 0xe00, | 
|  | }; | 
|  |  | 
|  | static const struct of_device_id pbias_of_match[] = { | 
|  | { .compatible = "ti,pbias-omap", }, | 
|  | { .compatible = "ti,pbias-omap2", .data = &pbias_of_data_omap2, }, | 
|  | { .compatible = "ti,pbias-omap3", .data = &pbias_of_data_omap3, }, | 
|  | { .compatible = "ti,pbias-omap4", .data = &pbias_of_data_omap4, }, | 
|  | { .compatible = "ti,pbias-omap5", .data = &pbias_of_data_omap5, }, | 
|  | { .compatible = "ti,pbias-dra7", .data = &pbias_of_data_dra7, }, | 
|  | {}, | 
|  | }; | 
|  | MODULE_DEVICE_TABLE(of, pbias_of_match); | 
|  |  | 
|  | static int pbias_regulator_probe(struct platform_device *pdev) | 
|  | { | 
|  | struct device_node *np = pdev->dev.of_node; | 
|  | struct pbias_regulator_data *drvdata; | 
|  | struct resource *res; | 
|  | struct regulator_config cfg = { }; | 
|  | struct regmap *syscon; | 
|  | const struct pbias_reg_info *info; | 
|  | int ret = 0; | 
|  | int count, idx, data_idx = 0; | 
|  | const struct of_device_id *match; | 
|  | const struct pbias_of_data *data; | 
|  | unsigned int offset; | 
|  |  | 
|  | count = of_regulator_match(&pdev->dev, np, pbias_matches, | 
|  | PBIAS_NUM_REGS); | 
|  | if (count < 0) | 
|  | return count; | 
|  |  | 
|  | drvdata = devm_kzalloc(&pdev->dev, sizeof(struct pbias_regulator_data) | 
|  | * count, GFP_KERNEL); | 
|  | if (!drvdata) | 
|  | return -ENOMEM; | 
|  |  | 
|  | syscon = syscon_regmap_lookup_by_phandle(np, "syscon"); | 
|  | if (IS_ERR(syscon)) | 
|  | return PTR_ERR(syscon); | 
|  |  | 
|  | match = of_match_device(of_match_ptr(pbias_of_match), &pdev->dev); | 
|  | if (match && match->data) { | 
|  | data = match->data; | 
|  | offset = data->offset; | 
|  | } else { | 
|  | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
|  | if (!res) | 
|  | return -EINVAL; | 
|  |  | 
|  | offset = res->start; | 
|  | dev_WARN(&pdev->dev, | 
|  | "using legacy dt data for pbias offset\n"); | 
|  | } | 
|  |  | 
|  | cfg.regmap = syscon; | 
|  | cfg.dev = &pdev->dev; | 
|  |  | 
|  | for (idx = 0; idx < PBIAS_NUM_REGS && data_idx < count; idx++) { | 
|  | if (!pbias_matches[idx].init_data || | 
|  | !pbias_matches[idx].of_node) | 
|  | continue; | 
|  |  | 
|  | info = pbias_matches[idx].driver_data; | 
|  | if (!info) | 
|  | return -ENODEV; | 
|  |  | 
|  | drvdata[data_idx].syscon = syscon; | 
|  | drvdata[data_idx].info = info; | 
|  | drvdata[data_idx].desc.name = info->name; | 
|  | drvdata[data_idx].desc.owner = THIS_MODULE; | 
|  | drvdata[data_idx].desc.type = REGULATOR_VOLTAGE; | 
|  | drvdata[data_idx].desc.ops = &pbias_regulator_voltage_ops; | 
|  | drvdata[data_idx].desc.volt_table = pbias_volt_table; | 
|  | drvdata[data_idx].desc.n_voltages = 2; | 
|  | drvdata[data_idx].desc.enable_time = info->enable_time; | 
|  | drvdata[data_idx].desc.vsel_reg = offset; | 
|  | drvdata[data_idx].desc.vsel_mask = info->vmode; | 
|  | drvdata[data_idx].desc.enable_reg = offset; | 
|  | drvdata[data_idx].desc.enable_mask = info->enable_mask; | 
|  | drvdata[data_idx].desc.enable_val = info->enable; | 
|  | drvdata[data_idx].desc.disable_val = info->disable_val; | 
|  |  | 
|  | cfg.init_data = pbias_matches[idx].init_data; | 
|  | cfg.driver_data = &drvdata[data_idx]; | 
|  | cfg.of_node = pbias_matches[idx].of_node; | 
|  |  | 
|  | drvdata[data_idx].dev = devm_regulator_register(&pdev->dev, | 
|  | &drvdata[data_idx].desc, &cfg); | 
|  | if (IS_ERR(drvdata[data_idx].dev)) { | 
|  | ret = PTR_ERR(drvdata[data_idx].dev); | 
|  | dev_err(&pdev->dev, | 
|  | "Failed to register regulator: %d\n", ret); | 
|  | goto err_regulator; | 
|  | } | 
|  | data_idx++; | 
|  | } | 
|  |  | 
|  | platform_set_drvdata(pdev, drvdata); | 
|  |  | 
|  | err_regulator: | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static struct platform_driver pbias_regulator_driver = { | 
|  | .probe		= pbias_regulator_probe, | 
|  | .driver		= { | 
|  | .name		= "pbias-regulator", | 
|  | .of_match_table = of_match_ptr(pbias_of_match), | 
|  | }, | 
|  | }; | 
|  |  | 
|  | module_platform_driver(pbias_regulator_driver); | 
|  |  | 
|  | MODULE_AUTHOR("Balaji T K <balajitk@ti.com>"); | 
|  | MODULE_DESCRIPTION("pbias voltage regulator"); | 
|  | MODULE_LICENSE("GPL"); | 
|  | MODULE_ALIAS("platform:pbias-regulator"); |