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 */ 30static 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 37struct comparator { 38 const char *name; 39 int reg; 40 int uV_max; 41 const u16 *vsel_table; 42}; 43 44static 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 59static 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 86static 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 103static 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 122static DEVICE_ATTR(comp1_threshold, S_IRUGO, comp_threshold_show, NULL); 123static DEVICE_ATTR(comp2_threshold, S_IRUGO, comp_threshold_show, NULL); 124 125static 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 155static 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 166static struct platform_driver tps65911_comparator_driver = { 167 .driver = { 168 .name = "tps65911-comparator", 169 }, 170 .probe = tps65911_comparator_probe, 171 .remove = tps65911_comparator_remove, 172}; 173 174static int __init tps65911_comparator_init(void) 175{ 176 return platform_driver_register(&tps65911_comparator_driver); 177} 178subsys_initcall(tps65911_comparator_init); 179 180static void __exit tps65911_comparator_exit(void) 181{ 182 platform_driver_unregister(&tps65911_comparator_driver); 183} 184module_exit(tps65911_comparator_exit); 185 186MODULE_AUTHOR("Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>"); 187MODULE_DESCRIPTION("TPS65911 comparator driver"); 188MODULE_LICENSE("GPL v2"); 189MODULE_ALIAS("platform:tps65911-comparator"); 190