1 /* 2 * tps65910.c -- TI TPS6591x 3 * 4 * Copyright 2010 Texas Instruments Inc. 5 * 6 * Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk> 7 * 8 * This program is free software; you can redistribute it and/or modify it 9 * under the terms of the GNU General Public License as published by the 10 * Free Software Foundation; either version 2 of the License, or (at your 11 * option) any later version. 12 * 13 */ 14 15 #include <linux/kernel.h> 16 #include <linux/module.h> 17 #include <linux/init.h> 18 #include <linux/slab.h> 19 #include <linux/err.h> 20 #include <linux/platform_device.h> 21 #include <linux/debugfs.h> 22 #include <linux/gpio.h> 23 #include <linux/mfd/tps65910.h> 24 25 #define COMP 0 26 #define COMP1 1 27 #define COMP2 2 28 29 /* Comparator 1 voltage selection table in millivolts */ 30 static const u16 COMP_VSEL_TABLE[] = { 31 0, 2500, 2500, 2500, 2500, 2550, 2600, 2650, 32 2700, 2750, 2800, 2850, 2900, 2950, 3000, 3050, 33 3100, 3150, 3200, 3250, 3300, 3350, 3400, 3450, 34 3500, 35 }; 36 37 struct comparator { 38 const char *name; 39 int reg; 40 int uV_max; 41 const u16 *vsel_table; 42 }; 43 44 static struct comparator tps_comparators[] = { 45 { 46 .name = "COMP1", 47 .reg = TPS65911_VMBCH, 48 .uV_max = 3500, 49 .vsel_table = COMP_VSEL_TABLE, 50 }, 51 { 52 .name = "COMP2", 53 .reg = TPS65911_VMBCH2, 54 .uV_max = 3500, 55 .vsel_table = COMP_VSEL_TABLE, 56 }, 57 }; 58 59 static int comp_threshold_set(struct tps65910 *tps65910, int id, int voltage) 60 { 61 struct comparator tps_comp = tps_comparators[id]; 62 int curr_voltage = 0; 63 int ret; 64 u8 index = 0, val; 65 66 if (id == COMP) 67 return 0; 68 69 while (curr_voltage < tps_comp.uV_max) { 70 curr_voltage = tps_comp.vsel_table[index]; 71 if (curr_voltage >= voltage) 72 break; 73 else if (curr_voltage < voltage) 74 index ++; 75 } 76 77 if (curr_voltage > tps_comp.uV_max) 78 return -EINVAL; 79 80 val = index << 1; 81 ret = tps65910->write(tps65910, tps_comp.reg, 1, &val); 82 83 return ret; 84 } 85 86 static int comp_threshold_get(struct tps65910 *tps65910, int id) 87 { 88 struct comparator tps_comp = tps_comparators[id]; 89 int ret; 90 u8 val; 91 92 if (id == COMP) 93 return 0; 94 95 ret = tps65910->read(tps65910, tps_comp.reg, 1, &val); 96 if (ret < 0) 97 return ret; 98 99 val >>= 1; 100 return tps_comp.vsel_table[val]; 101 } 102 103 static ssize_t comp_threshold_show(struct device *dev, 104 struct device_attribute *attr, char *buf) 105 { 106 struct tps65910 *tps65910 = dev_get_drvdata(dev->parent); 107 struct attribute comp_attr = attr->attr; 108 int id, uVolt; 109 110 if (!strcmp(comp_attr.name, "comp1_threshold")) 111 id = COMP1; 112 else if (!strcmp(comp_attr.name, "comp2_threshold")) 113 id = COMP2; 114 else 115 return -EINVAL; 116 117 uVolt = comp_threshold_get(tps65910, id); 118 119 return sprintf(buf, "%d\n", uVolt); 120 } 121 122 static DEVICE_ATTR(comp1_threshold, S_IRUGO, comp_threshold_show, NULL); 123 static DEVICE_ATTR(comp2_threshold, S_IRUGO, comp_threshold_show, NULL); 124 125 static int tps65911_comparator_probe(struct platform_device *pdev) 126 { 127 struct tps65910 *tps65910 = dev_get_drvdata(pdev->dev.parent); 128 struct tps65910_board *pdata = dev_get_platdata(tps65910->dev); 129 int ret; 130 131 ret = comp_threshold_set(tps65910, COMP1, pdata->vmbch_threshold); 132 if (ret < 0) { 133 dev_err(&pdev->dev, "cannot set COMP1 threshold\n"); 134 return ret; 135 } 136 137 ret = comp_threshold_set(tps65910, COMP2, pdata->vmbch2_threshold); 138 if (ret < 0) { 139 dev_err(&pdev->dev, "cannot set COMP2 threshold\n"); 140 return ret; 141 } 142 143 /* Create sysfs entry */ 144 ret = device_create_file(&pdev->dev, &dev_attr_comp1_threshold); 145 if (ret < 0) 146 dev_err(&pdev->dev, "failed to add COMP1 sysfs file\n"); 147 148 ret = device_create_file(&pdev->dev, &dev_attr_comp2_threshold); 149 if (ret < 0) 150 dev_err(&pdev->dev, "failed to add COMP2 sysfs file\n"); 151 152 return ret; 153 } 154 155 static int tps65911_comparator_remove(struct platform_device *pdev) 156 { 157 struct tps65910 *tps65910; 158 159 tps65910 = dev_get_drvdata(pdev->dev.parent); 160 device_remove_file(&pdev->dev, &dev_attr_comp2_threshold); 161 device_remove_file(&pdev->dev, &dev_attr_comp1_threshold); 162 163 return 0; 164 } 165 166 static struct platform_driver tps65911_comparator_driver = { 167 .driver = { 168 .name = "tps65911-comparator", 169 .owner = THIS_MODULE, 170 }, 171 .probe = tps65911_comparator_probe, 172 .remove = tps65911_comparator_remove, 173 }; 174 175 static int __init tps65911_comparator_init(void) 176 { 177 return platform_driver_register(&tps65911_comparator_driver); 178 } 179 subsys_initcall(tps65911_comparator_init); 180 181 static void __exit tps65911_comparator_exit(void) 182 { 183 platform_driver_unregister(&tps65911_comparator_driver); 184 } 185 module_exit(tps65911_comparator_exit); 186 187 MODULE_AUTHOR("Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>"); 188 MODULE_DESCRIPTION("TPS65911 comparator driver"); 189 MODULE_LICENSE("GPL v2"); 190 MODULE_ALIAS("platform:tps65911-comparator"); 191