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 COMP1 0 26 #define COMP2 1 27 28 /* Comparator 1 voltage selection table in millivolts */ 29 static const u16 COMP_VSEL_TABLE[] = { 30 0, 2500, 2500, 2500, 2500, 2550, 2600, 2650, 31 2700, 2750, 2800, 2850, 2900, 2950, 3000, 3050, 32 3100, 3150, 3200, 3250, 3300, 3350, 3400, 3450, 33 3500, 34 }; 35 36 struct comparator { 37 const char *name; 38 int reg; 39 int uV_max; 40 const u16 *vsel_table; 41 }; 42 43 static struct comparator tps_comparators[] = { 44 { 45 .name = "COMP1", 46 .reg = TPS65911_VMBCH, 47 .uV_max = 3500, 48 .vsel_table = COMP_VSEL_TABLE, 49 }, 50 { 51 .name = "COMP2", 52 .reg = TPS65911_VMBCH2, 53 .uV_max = 3500, 54 .vsel_table = COMP_VSEL_TABLE, 55 }, 56 }; 57 58 static int comp_threshold_set(struct tps65910 *tps65910, int id, int voltage) 59 { 60 struct comparator tps_comp = tps_comparators[id]; 61 int curr_voltage = 0; 62 int ret; 63 u8 index = 0, val; 64 65 while (curr_voltage < tps_comp.uV_max) { 66 curr_voltage = tps_comp.vsel_table[index]; 67 if (curr_voltage >= voltage) 68 break; 69 else if (curr_voltage < voltage) 70 index ++; 71 } 72 73 if (curr_voltage > tps_comp.uV_max) 74 return -EINVAL; 75 76 val = index << 1; 77 ret = tps65910_reg_write(tps65910, tps_comp.reg, val); 78 79 return ret; 80 } 81 82 static int comp_threshold_get(struct tps65910 *tps65910, int id) 83 { 84 struct comparator tps_comp = tps_comparators[id]; 85 unsigned int val; 86 int ret; 87 88 ret = tps65910_reg_read(tps65910, tps_comp.reg, &val); 89 if (ret < 0) 90 return ret; 91 92 val >>= 1; 93 return tps_comp.vsel_table[val]; 94 } 95 96 static ssize_t comp_threshold_show(struct device *dev, 97 struct device_attribute *attr, char *buf) 98 { 99 struct tps65910 *tps65910 = dev_get_drvdata(dev->parent); 100 struct attribute comp_attr = attr->attr; 101 int id, uVolt; 102 103 if (!strcmp(comp_attr.name, "comp1_threshold")) 104 id = COMP1; 105 else if (!strcmp(comp_attr.name, "comp2_threshold")) 106 id = COMP2; 107 else 108 return -EINVAL; 109 110 uVolt = comp_threshold_get(tps65910, id); 111 112 return sprintf(buf, "%d\n", uVolt); 113 } 114 115 static DEVICE_ATTR(comp1_threshold, S_IRUGO, comp_threshold_show, NULL); 116 static DEVICE_ATTR(comp2_threshold, S_IRUGO, comp_threshold_show, NULL); 117 118 static int tps65911_comparator_probe(struct platform_device *pdev) 119 { 120 struct tps65910 *tps65910 = dev_get_drvdata(pdev->dev.parent); 121 struct tps65910_board *pdata = dev_get_platdata(tps65910->dev); 122 int ret; 123 124 ret = comp_threshold_set(tps65910, COMP1, pdata->vmbch_threshold); 125 if (ret < 0) { 126 dev_err(&pdev->dev, "cannot set COMP1 threshold\n"); 127 return ret; 128 } 129 130 ret = comp_threshold_set(tps65910, COMP2, pdata->vmbch2_threshold); 131 if (ret < 0) { 132 dev_err(&pdev->dev, "cannot set COMP2 threshold\n"); 133 return ret; 134 } 135 136 /* Create sysfs entry */ 137 ret = device_create_file(&pdev->dev, &dev_attr_comp1_threshold); 138 if (ret < 0) 139 dev_err(&pdev->dev, "failed to add COMP1 sysfs file\n"); 140 141 ret = device_create_file(&pdev->dev, &dev_attr_comp2_threshold); 142 if (ret < 0) 143 dev_err(&pdev->dev, "failed to add COMP2 sysfs file\n"); 144 145 return ret; 146 } 147 148 static int tps65911_comparator_remove(struct platform_device *pdev) 149 { 150 struct tps65910 *tps65910; 151 152 tps65910 = dev_get_drvdata(pdev->dev.parent); 153 device_remove_file(&pdev->dev, &dev_attr_comp2_threshold); 154 device_remove_file(&pdev->dev, &dev_attr_comp1_threshold); 155 156 return 0; 157 } 158 159 static struct platform_driver tps65911_comparator_driver = { 160 .driver = { 161 .name = "tps65911-comparator", 162 }, 163 .probe = tps65911_comparator_probe, 164 .remove = tps65911_comparator_remove, 165 }; 166 167 static int __init tps65911_comparator_init(void) 168 { 169 return platform_driver_register(&tps65911_comparator_driver); 170 } 171 subsys_initcall(tps65911_comparator_init); 172 173 static void __exit tps65911_comparator_exit(void) 174 { 175 platform_driver_unregister(&tps65911_comparator_driver); 176 } 177 module_exit(tps65911_comparator_exit); 178 179 MODULE_AUTHOR("Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>"); 180 MODULE_DESCRIPTION("TPS65911 comparator driver"); 181 MODULE_LICENSE("GPL v2"); 182 MODULE_ALIAS("platform:tps65911-comparator"); 183