1 /* 2 * This program is free software; you can redistribute it and/or modify 3 * it under the terms of the GNU General Public License version 2 as 4 * published by the Free Software Foundation. 5 * 6 * This program is distributed in the hope that it will be useful, 7 * but WITHOUT ANY WARRANTY; without even the implied warranty of 8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 * GNU General Public License for more details. 10 * 11 * Copyright (C) 2012 ARM Limited 12 */ 13 14 #define DRVNAME "vexpress-regulator" 15 #define pr_fmt(fmt) DRVNAME ": " fmt 16 17 #include <linux/device.h> 18 #include <linux/err.h> 19 #include <linux/module.h> 20 #include <linux/of_device.h> 21 #include <linux/regulator/driver.h> 22 #include <linux/regulator/machine.h> 23 #include <linux/regulator/of_regulator.h> 24 #include <linux/vexpress.h> 25 26 struct vexpress_regulator { 27 struct regulator_desc desc; 28 struct regulator_dev *regdev; 29 struct regmap *regmap; 30 }; 31 32 static int vexpress_regulator_get_voltage(struct regulator_dev *regdev) 33 { 34 struct vexpress_regulator *reg = rdev_get_drvdata(regdev); 35 u32 uV; 36 int err = regmap_read(reg->regmap, 0, &uV); 37 38 return err ? err : uV; 39 } 40 41 static int vexpress_regulator_set_voltage(struct regulator_dev *regdev, 42 int min_uV, int max_uV, unsigned *selector) 43 { 44 struct vexpress_regulator *reg = rdev_get_drvdata(regdev); 45 46 return regmap_write(reg->regmap, 0, min_uV); 47 } 48 49 static struct regulator_ops vexpress_regulator_ops_ro = { 50 .get_voltage = vexpress_regulator_get_voltage, 51 }; 52 53 static struct regulator_ops vexpress_regulator_ops = { 54 .get_voltage = vexpress_regulator_get_voltage, 55 .set_voltage = vexpress_regulator_set_voltage, 56 }; 57 58 static int vexpress_regulator_probe(struct platform_device *pdev) 59 { 60 struct vexpress_regulator *reg; 61 struct regulator_init_data *init_data; 62 struct regulator_config config = { }; 63 64 reg = devm_kzalloc(&pdev->dev, sizeof(*reg), GFP_KERNEL); 65 if (!reg) 66 return -ENOMEM; 67 68 reg->regmap = devm_regmap_init_vexpress_config(&pdev->dev); 69 if (IS_ERR(reg->regmap)) 70 return PTR_ERR(reg->regmap); 71 72 reg->desc.name = dev_name(&pdev->dev); 73 reg->desc.type = REGULATOR_VOLTAGE; 74 reg->desc.owner = THIS_MODULE; 75 reg->desc.continuous_voltage_range = true; 76 77 init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node, 78 ®->desc); 79 if (!init_data) 80 return -EINVAL; 81 82 init_data->constraints.apply_uV = 0; 83 if (init_data->constraints.min_uV && init_data->constraints.max_uV) 84 reg->desc.ops = &vexpress_regulator_ops; 85 else 86 reg->desc.ops = &vexpress_regulator_ops_ro; 87 88 config.dev = &pdev->dev; 89 config.init_data = init_data; 90 config.driver_data = reg; 91 config.of_node = pdev->dev.of_node; 92 93 reg->regdev = devm_regulator_register(&pdev->dev, ®->desc, &config); 94 if (IS_ERR(reg->regdev)) 95 return PTR_ERR(reg->regdev); 96 97 platform_set_drvdata(pdev, reg); 98 99 return 0; 100 } 101 102 static const struct of_device_id vexpress_regulator_of_match[] = { 103 { .compatible = "arm,vexpress-volt", }, 104 { } 105 }; 106 MODULE_DEVICE_TABLE(of, vexpress_regulator_of_match); 107 108 static struct platform_driver vexpress_regulator_driver = { 109 .probe = vexpress_regulator_probe, 110 .driver = { 111 .name = DRVNAME, 112 .of_match_table = vexpress_regulator_of_match, 113 }, 114 }; 115 116 module_platform_driver(vexpress_regulator_driver); 117 118 MODULE_AUTHOR("Pawel Moll <pawel.moll@arm.com>"); 119 MODULE_DESCRIPTION("Versatile Express regulator"); 120 MODULE_LICENSE("GPL"); 121 MODULE_ALIAS("platform:vexpress-regulator"); 122