Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mfd: Add twl4030 madc driver

Introducing a driver for MADC on TWL4030 powerIC. MADC stands for monitoring
ADC. This driver monitors the real time conversion of analog signals like
battery temperature, battery cuurent etc.

Signed-off-by: Keerthy <j-keerthy@ti.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>

authored by

Keerthy and committed by
Samuel Ortiz
f99c1d4f bcd2f639

+954
+10
drivers/mfd/Kconfig
··· 178 178 high speed USB OTG transceiver, an audio codec (on most 179 179 versions) and many other features. 180 180 181 + config TWL4030_MADC 182 + tristate "Texas Instruments TWL4030 MADC" 183 + depends on TWL4030_CORE 184 + help 185 + This driver provides support for triton TWL4030-MADC. The 186 + driver supports both RT and SW conversion methods. 187 + 188 + This driver can be built as a module. If so it will be 189 + named twl4030-madc 190 + 181 191 config TWL4030_POWER 182 192 bool "Support power resources on TWL4030 family chips" 183 193 depends on TWL4030_CORE && ARM
+1
drivers/mfd/Makefile
··· 38 38 obj-$(CONFIG_MENELAUS) += menelaus.o 39 39 40 40 obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o 41 + obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o 41 42 obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o 42 43 obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o 43 44 obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o
+802
drivers/mfd/twl4030-madc.c
··· 1 + /* 2 + * 3 + * TWL4030 MADC module driver-This driver monitors the real time 4 + * conversion of analog signals like battery temperature, 5 + * battery type, battery level etc. 6 + * 7 + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ 8 + * J Keerthy <j-keerthy@ti.com> 9 + * 10 + * Based on twl4030-madc.c 11 + * Copyright (C) 2008 Nokia Corporation 12 + * Mikko Ylinen <mikko.k.ylinen@nokia.com> 13 + * 14 + * Amit Kucheria <amit.kucheria@canonical.com> 15 + * 16 + * This program is free software; you can redistribute it and/or 17 + * modify it under the terms of the GNU General Public License 18 + * version 2 as published by the Free Software Foundation. 19 + * 20 + * This program is distributed in the hope that it will be useful, but 21 + * WITHOUT ANY WARRANTY; without even the implied warranty of 22 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 23 + * General Public License for more details. 24 + * 25 + * You should have received a copy of the GNU General Public License 26 + * along with this program; if not, write to the Free Software 27 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 28 + * 02110-1301 USA 29 + * 30 + */ 31 + 32 + #include <linux/init.h> 33 + #include <linux/device.h> 34 + #include <linux/interrupt.h> 35 + #include <linux/kernel.h> 36 + #include <linux/delay.h> 37 + #include <linux/platform_device.h> 38 + #include <linux/slab.h> 39 + #include <linux/i2c/twl.h> 40 + #include <linux/i2c/twl4030-madc.h> 41 + #include <linux/module.h> 42 + #include <linux/stddef.h> 43 + #include <linux/mutex.h> 44 + #include <linux/bitops.h> 45 + #include <linux/jiffies.h> 46 + #include <linux/types.h> 47 + #include <linux/gfp.h> 48 + #include <linux/err.h> 49 + 50 + /* 51 + * struct twl4030_madc_data - a container for madc info 52 + * @dev - pointer to device structure for madc 53 + * @lock - mutex protecting this data structure 54 + * @requests - Array of request struct corresponding to SW1, SW2 and RT 55 + * @imr - Interrupt mask register of MADC 56 + * @isr - Interrupt status register of MADC 57 + */ 58 + struct twl4030_madc_data { 59 + struct device *dev; 60 + struct mutex lock; /* mutex protecting this data structure */ 61 + struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS]; 62 + int imr; 63 + int isr; 64 + }; 65 + 66 + static struct twl4030_madc_data *twl4030_madc; 67 + 68 + struct twl4030_prescale_divider_ratios { 69 + s16 numerator; 70 + s16 denominator; 71 + }; 72 + 73 + static const struct twl4030_prescale_divider_ratios 74 + twl4030_divider_ratios[16] = { 75 + {1, 1}, /* CHANNEL 0 No Prescaler */ 76 + {1, 1}, /* CHANNEL 1 No Prescaler */ 77 + {6, 10}, /* CHANNEL 2 */ 78 + {6, 10}, /* CHANNEL 3 */ 79 + {6, 10}, /* CHANNEL 4 */ 80 + {6, 10}, /* CHANNEL 5 */ 81 + {6, 10}, /* CHANNEL 6 */ 82 + {6, 10}, /* CHANNEL 7 */ 83 + {3, 14}, /* CHANNEL 8 */ 84 + {1, 3}, /* CHANNEL 9 */ 85 + {1, 1}, /* CHANNEL 10 No Prescaler */ 86 + {15, 100}, /* CHANNEL 11 */ 87 + {1, 4}, /* CHANNEL 12 */ 88 + {1, 1}, /* CHANNEL 13 Reserved channels */ 89 + {1, 1}, /* CHANNEL 14 Reseved channels */ 90 + {5, 11}, /* CHANNEL 15 */ 91 + }; 92 + 93 + 94 + /* 95 + * Conversion table from -3 to 55 degree Celcius 96 + */ 97 + static int therm_tbl[] = { 98 + 30800, 29500, 28300, 27100, 99 + 26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700, 17900, 100 + 17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100, 12600, 12100, 101 + 11600, 11200, 10800, 10400, 10000, 9630, 9280, 8950, 8620, 8310, 102 + 8020, 7730, 7460, 7200, 6950, 6710, 6470, 6250, 6040, 5830, 103 + 5640, 5450, 5260, 5090, 4920, 4760, 4600, 4450, 4310, 4170, 104 + 4040, 3910, 3790, 3670, 3550 105 + }; 106 + 107 + /* 108 + * Structure containing the registers 109 + * of different conversion methods supported by MADC. 110 + * Hardware or RT real time conversion request initiated by external host 111 + * processor for RT Signal conversions. 112 + * External host processors can also request for non RT conversions 113 + * SW1 and SW2 software conversions also called asynchronous or GPC request. 114 + */ 115 + static 116 + const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = { 117 + [TWL4030_MADC_RT] = { 118 + .sel = TWL4030_MADC_RTSELECT_LSB, 119 + .avg = TWL4030_MADC_RTAVERAGE_LSB, 120 + .rbase = TWL4030_MADC_RTCH0_LSB, 121 + }, 122 + [TWL4030_MADC_SW1] = { 123 + .sel = TWL4030_MADC_SW1SELECT_LSB, 124 + .avg = TWL4030_MADC_SW1AVERAGE_LSB, 125 + .rbase = TWL4030_MADC_GPCH0_LSB, 126 + .ctrl = TWL4030_MADC_CTRL_SW1, 127 + }, 128 + [TWL4030_MADC_SW2] = { 129 + .sel = TWL4030_MADC_SW2SELECT_LSB, 130 + .avg = TWL4030_MADC_SW2AVERAGE_LSB, 131 + .rbase = TWL4030_MADC_GPCH0_LSB, 132 + .ctrl = TWL4030_MADC_CTRL_SW2, 133 + }, 134 + }; 135 + 136 + /* 137 + * Function to read a particular channel value. 138 + * @madc - pointer to struct twl4030_madc_data 139 + * @reg - lsb of ADC Channel 140 + * If the i2c read fails it returns an error else returns 0. 141 + */ 142 + static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg) 143 + { 144 + u8 msb, lsb; 145 + int ret; 146 + /* 147 + * For each ADC channel, we have MSB and LSB register pair. MSB address 148 + * is always LSB address+1. reg parameter is the address of LSB register 149 + */ 150 + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &msb, reg + 1); 151 + if (ret) { 152 + dev_err(madc->dev, "unable to read MSB register 0x%X\n", 153 + reg + 1); 154 + return ret; 155 + } 156 + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &lsb, reg); 157 + if (ret) { 158 + dev_err(madc->dev, "unable to read LSB register 0x%X\n", reg); 159 + return ret; 160 + } 161 + 162 + return (int)(((msb << 8) | lsb) >> 6); 163 + } 164 + 165 + /* 166 + * Return battery temperature 167 + * Or < 0 on failure. 168 + */ 169 + static int twl4030battery_temperature(int raw_volt) 170 + { 171 + u8 val; 172 + int temp, curr, volt, res, ret; 173 + 174 + volt = (raw_volt * TEMP_STEP_SIZE) / TEMP_PSR_R; 175 + /* Getting and calculating the supply current in micro ampers */ 176 + ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val, 177 + REG_BCICTL2); 178 + if (ret < 0) 179 + return ret; 180 + curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10; 181 + /* Getting and calculating the thermistor resistance in ohms */ 182 + res = volt * 1000 / curr; 183 + /* calculating temperature */ 184 + for (temp = 58; temp >= 0; temp--) { 185 + int actual = therm_tbl[temp]; 186 + 187 + if ((actual - res) >= 0) 188 + break; 189 + } 190 + 191 + return temp + 1; 192 + } 193 + 194 + static int twl4030battery_current(int raw_volt) 195 + { 196 + int ret; 197 + u8 val; 198 + 199 + ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val, 200 + TWL4030_BCI_BCICTL1); 201 + if (ret) 202 + return ret; 203 + if (val & TWL4030_BCI_CGAIN) /* slope of 0.44 mV/mA */ 204 + return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R1; 205 + else /* slope of 0.88 mV/mA */ 206 + return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2; 207 + } 208 + /* 209 + * Function to read channel values 210 + * @madc - pointer to twl4030_madc_data struct 211 + * @reg_base - Base address of the first channel 212 + * @Channels - 16 bit bitmap. If the bit is set, channel value is read 213 + * @buf - The channel values are stored here. if read fails error 214 + * value is stored 215 + * Returns the number of successfully read channels. 216 + */ 217 + static int twl4030_madc_read_channels(struct twl4030_madc_data *madc, 218 + u8 reg_base, unsigned 219 + long channels, int *buf) 220 + { 221 + int count = 0, count_req = 0, i; 222 + u8 reg; 223 + 224 + for_each_set_bit(i, &channels, TWL4030_MADC_MAX_CHANNELS) { 225 + reg = reg_base + 2 * i; 226 + buf[i] = twl4030_madc_channel_raw_read(madc, reg); 227 + if (buf[i] < 0) { 228 + dev_err(madc->dev, 229 + "Unable to read register 0x%X\n", reg); 230 + count_req++; 231 + continue; 232 + } 233 + switch (i) { 234 + case 10: 235 + buf[i] = twl4030battery_current(buf[i]); 236 + if (buf[i] < 0) { 237 + dev_err(madc->dev, "err reading current\n"); 238 + count_req++; 239 + } else { 240 + count++; 241 + buf[i] = buf[i] - 750; 242 + } 243 + break; 244 + case 1: 245 + buf[i] = twl4030battery_temperature(buf[i]); 246 + if (buf[i] < 0) { 247 + dev_err(madc->dev, "err reading temperature\n"); 248 + count_req++; 249 + } else { 250 + buf[i] -= 3; 251 + count++; 252 + } 253 + break; 254 + default: 255 + count++; 256 + /* Analog Input (V) = conv_result * step_size / R 257 + * conv_result = decimal value of 10-bit conversion 258 + * result 259 + * step size = 1.5 / (2 ^ 10 -1) 260 + * R = Prescaler ratio for input channels. 261 + * Result given in mV hence multiplied by 1000. 262 + */ 263 + buf[i] = (buf[i] * 3 * 1000 * 264 + twl4030_divider_ratios[i].denominator) 265 + / (2 * 1023 * 266 + twl4030_divider_ratios[i].numerator); 267 + } 268 + } 269 + if (count_req) 270 + dev_err(madc->dev, "%d channel conversion failed\n", count_req); 271 + 272 + return count; 273 + } 274 + 275 + /* 276 + * Enables irq. 277 + * @madc - pointer to twl4030_madc_data struct 278 + * @id - irq number to be enabled 279 + * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2 280 + * corresponding to RT, SW1, SW2 conversion requests. 281 + * If the i2c read fails it returns an error else returns 0. 282 + */ 283 + static int twl4030_madc_enable_irq(struct twl4030_madc_data *madc, u8 id) 284 + { 285 + u8 val; 286 + int ret; 287 + 288 + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr); 289 + if (ret) { 290 + dev_err(madc->dev, "unable to read imr register 0x%X\n", 291 + madc->imr); 292 + return ret; 293 + } 294 + val &= ~(1 << id); 295 + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr); 296 + if (ret) { 297 + dev_err(madc->dev, 298 + "unable to write imr register 0x%X\n", madc->imr); 299 + return ret; 300 + 301 + } 302 + 303 + return 0; 304 + } 305 + 306 + /* 307 + * Disables irq. 308 + * @madc - pointer to twl4030_madc_data struct 309 + * @id - irq number to be disabled 310 + * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2 311 + * corresponding to RT, SW1, SW2 conversion requests. 312 + * Returns error if i2c read/write fails. 313 + */ 314 + static int twl4030_madc_disable_irq(struct twl4030_madc_data *madc, u8 id) 315 + { 316 + u8 val; 317 + int ret; 318 + 319 + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr); 320 + if (ret) { 321 + dev_err(madc->dev, "unable to read imr register 0x%X\n", 322 + madc->imr); 323 + return ret; 324 + } 325 + val |= (1 << id); 326 + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr); 327 + if (ret) { 328 + dev_err(madc->dev, 329 + "unable to write imr register 0x%X\n", madc->imr); 330 + return ret; 331 + } 332 + 333 + return 0; 334 + } 335 + 336 + static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc) 337 + { 338 + struct twl4030_madc_data *madc = _madc; 339 + const struct twl4030_madc_conversion_method *method; 340 + u8 isr_val, imr_val; 341 + int i, len, ret; 342 + struct twl4030_madc_request *r; 343 + 344 + mutex_lock(&madc->lock); 345 + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &isr_val, madc->isr); 346 + if (ret) { 347 + dev_err(madc->dev, "unable to read isr register 0x%X\n", 348 + madc->isr); 349 + goto err_i2c; 350 + } 351 + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &imr_val, madc->imr); 352 + if (ret) { 353 + dev_err(madc->dev, "unable to read imr register 0x%X\n", 354 + madc->imr); 355 + goto err_i2c; 356 + } 357 + isr_val &= ~imr_val; 358 + for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { 359 + if (!(isr_val & (1 << i))) 360 + continue; 361 + ret = twl4030_madc_disable_irq(madc, i); 362 + if (ret < 0) 363 + dev_dbg(madc->dev, "Disable interrupt failed%d\n", i); 364 + madc->requests[i].result_pending = 1; 365 + } 366 + for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { 367 + r = &madc->requests[i]; 368 + /* No pending results for this method, move to next one */ 369 + if (!r->result_pending) 370 + continue; 371 + method = &twl4030_conversion_methods[r->method]; 372 + /* Read results */ 373 + len = twl4030_madc_read_channels(madc, method->rbase, 374 + r->channels, r->rbuf); 375 + /* Return results to caller */ 376 + if (r->func_cb != NULL) { 377 + r->func_cb(len, r->channels, r->rbuf); 378 + r->func_cb = NULL; 379 + } 380 + /* Free request */ 381 + r->result_pending = 0; 382 + r->active = 0; 383 + } 384 + mutex_unlock(&madc->lock); 385 + 386 + return IRQ_HANDLED; 387 + 388 + err_i2c: 389 + /* 390 + * In case of error check whichever request is active 391 + * and service the same. 392 + */ 393 + for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { 394 + r = &madc->requests[i]; 395 + if (r->active == 0) 396 + continue; 397 + method = &twl4030_conversion_methods[r->method]; 398 + /* Read results */ 399 + len = twl4030_madc_read_channels(madc, method->rbase, 400 + r->channels, r->rbuf); 401 + /* Return results to caller */ 402 + if (r->func_cb != NULL) { 403 + r->func_cb(len, r->channels, r->rbuf); 404 + r->func_cb = NULL; 405 + } 406 + /* Free request */ 407 + r->result_pending = 0; 408 + r->active = 0; 409 + } 410 + mutex_unlock(&madc->lock); 411 + 412 + return IRQ_HANDLED; 413 + } 414 + 415 + static int twl4030_madc_set_irq(struct twl4030_madc_data *madc, 416 + struct twl4030_madc_request *req) 417 + { 418 + struct twl4030_madc_request *p; 419 + int ret; 420 + 421 + p = &madc->requests[req->method]; 422 + memcpy(p, req, sizeof(*req)); 423 + ret = twl4030_madc_enable_irq(madc, req->method); 424 + if (ret < 0) { 425 + dev_err(madc->dev, "enable irq failed!!\n"); 426 + return ret; 427 + } 428 + 429 + return 0; 430 + } 431 + 432 + /* 433 + * Function which enables the madc conversion 434 + * by writing to the control register. 435 + * @madc - pointer to twl4030_madc_data struct 436 + * @conv_method - can be TWL4030_MADC_RT, TWL4030_MADC_SW2, TWL4030_MADC_SW1 437 + * corresponding to RT SW1 or SW2 conversion methods. 438 + * Returns 0 if succeeds else a negative error value 439 + */ 440 + static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc, 441 + int conv_method) 442 + { 443 + const struct twl4030_madc_conversion_method *method; 444 + int ret = 0; 445 + method = &twl4030_conversion_methods[conv_method]; 446 + switch (conv_method) { 447 + case TWL4030_MADC_SW1: 448 + case TWL4030_MADC_SW2: 449 + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, 450 + TWL4030_MADC_SW_START, method->ctrl); 451 + if (ret) { 452 + dev_err(madc->dev, 453 + "unable to write ctrl register 0x%X\n", 454 + method->ctrl); 455 + return ret; 456 + } 457 + break; 458 + default: 459 + break; 460 + } 461 + 462 + return 0; 463 + } 464 + 465 + /* 466 + * Function that waits for conversion to be ready 467 + * @madc - pointer to twl4030_madc_data struct 468 + * @timeout_ms - timeout value in milliseconds 469 + * @status_reg - ctrl register 470 + * returns 0 if succeeds else a negative error value 471 + */ 472 + static int twl4030_madc_wait_conversion_ready(struct twl4030_madc_data *madc, 473 + unsigned int timeout_ms, 474 + u8 status_reg) 475 + { 476 + unsigned long timeout; 477 + int ret; 478 + 479 + timeout = jiffies + msecs_to_jiffies(timeout_ms); 480 + do { 481 + u8 reg; 482 + 483 + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &reg, status_reg); 484 + if (ret) { 485 + dev_err(madc->dev, 486 + "unable to read status register 0x%X\n", 487 + status_reg); 488 + return ret; 489 + } 490 + if (!(reg & TWL4030_MADC_BUSY) && (reg & TWL4030_MADC_EOC_SW)) 491 + return 0; 492 + usleep_range(500, 2000); 493 + } while (!time_after(jiffies, timeout)); 494 + dev_err(madc->dev, "conversion timeout!\n"); 495 + 496 + return -EAGAIN; 497 + } 498 + 499 + /* 500 + * An exported function which can be called from other kernel drivers. 501 + * @req twl4030_madc_request structure 502 + * req->rbuf will be filled with read values of channels based on the 503 + * channel index. If a particular channel reading fails there will 504 + * be a negative error value in the corresponding array element. 505 + * returns 0 if succeeds else error value 506 + */ 507 + int twl4030_madc_conversion(struct twl4030_madc_request *req) 508 + { 509 + const struct twl4030_madc_conversion_method *method; 510 + u8 ch_msb, ch_lsb; 511 + int ret; 512 + 513 + if (!req) 514 + return -EINVAL; 515 + mutex_lock(&twl4030_madc->lock); 516 + if (req->method < TWL4030_MADC_RT || req->method > TWL4030_MADC_SW2) { 517 + ret = -EINVAL; 518 + goto out; 519 + } 520 + /* Do we have a conversion request ongoing */ 521 + if (twl4030_madc->requests[req->method].active) { 522 + ret = -EBUSY; 523 + goto out; 524 + } 525 + ch_msb = (req->channels >> 8) & 0xff; 526 + ch_lsb = req->channels & 0xff; 527 + method = &twl4030_conversion_methods[req->method]; 528 + /* Select channels to be converted */ 529 + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_msb, method->sel + 1); 530 + if (ret) { 531 + dev_err(twl4030_madc->dev, 532 + "unable to write sel register 0x%X\n", method->sel + 1); 533 + return ret; 534 + } 535 + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_lsb, method->sel); 536 + if (ret) { 537 + dev_err(twl4030_madc->dev, 538 + "unable to write sel register 0x%X\n", method->sel + 1); 539 + return ret; 540 + } 541 + /* Select averaging for all channels if do_avg is set */ 542 + if (req->do_avg) { 543 + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, 544 + ch_msb, method->avg + 1); 545 + if (ret) { 546 + dev_err(twl4030_madc->dev, 547 + "unable to write avg register 0x%X\n", 548 + method->avg + 1); 549 + return ret; 550 + } 551 + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, 552 + ch_lsb, method->avg); 553 + if (ret) { 554 + dev_err(twl4030_madc->dev, 555 + "unable to write sel reg 0x%X\n", 556 + method->sel + 1); 557 + return ret; 558 + } 559 + } 560 + if (req->type == TWL4030_MADC_IRQ_ONESHOT && req->func_cb != NULL) { 561 + ret = twl4030_madc_set_irq(twl4030_madc, req); 562 + if (ret < 0) 563 + goto out; 564 + ret = twl4030_madc_start_conversion(twl4030_madc, req->method); 565 + if (ret < 0) 566 + goto out; 567 + twl4030_madc->requests[req->method].active = 1; 568 + ret = 0; 569 + goto out; 570 + } 571 + /* With RT method we should not be here anymore */ 572 + if (req->method == TWL4030_MADC_RT) { 573 + ret = -EINVAL; 574 + goto out; 575 + } 576 + ret = twl4030_madc_start_conversion(twl4030_madc, req->method); 577 + if (ret < 0) 578 + goto out; 579 + twl4030_madc->requests[req->method].active = 1; 580 + /* Wait until conversion is ready (ctrl register returns EOC) */ 581 + ret = twl4030_madc_wait_conversion_ready(twl4030_madc, 5, method->ctrl); 582 + if (ret) { 583 + twl4030_madc->requests[req->method].active = 0; 584 + goto out; 585 + } 586 + ret = twl4030_madc_read_channels(twl4030_madc, method->rbase, 587 + req->channels, req->rbuf); 588 + twl4030_madc->requests[req->method].active = 0; 589 + 590 + out: 591 + mutex_unlock(&twl4030_madc->lock); 592 + 593 + return ret; 594 + } 595 + EXPORT_SYMBOL_GPL(twl4030_madc_conversion); 596 + 597 + /* 598 + * Return channel value 599 + * Or < 0 on failure. 600 + */ 601 + int twl4030_get_madc_conversion(int channel_no) 602 + { 603 + struct twl4030_madc_request req; 604 + int temp = 0; 605 + int ret; 606 + 607 + req.channels = (1 << channel_no); 608 + req.method = TWL4030_MADC_SW2; 609 + req.active = 0; 610 + req.func_cb = NULL; 611 + ret = twl4030_madc_conversion(&req); 612 + if (ret < 0) 613 + return ret; 614 + if (req.rbuf[channel_no] > 0) 615 + temp = req.rbuf[channel_no]; 616 + 617 + return temp; 618 + } 619 + EXPORT_SYMBOL_GPL(twl4030_get_madc_conversion); 620 + 621 + /* 622 + * Function to enable or disable bias current for 623 + * main battery type reading or temperature sensing 624 + * @madc - pointer to twl4030_madc_data struct 625 + * @chan - can be one of the two values 626 + * TWL4030_BCI_ITHEN - Enables bias current for main battery type reading 627 + * TWL4030_BCI_TYPEN - Enables bias current for main battery temperature 628 + * sensing 629 + * @on - enable or disable chan. 630 + */ 631 + static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc, 632 + int chan, int on) 633 + { 634 + int ret; 635 + u8 regval; 636 + 637 + ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, 638 + &regval, TWL4030_BCI_BCICTL1); 639 + if (ret) { 640 + dev_err(madc->dev, "unable to read BCICTL1 reg 0x%X", 641 + TWL4030_BCI_BCICTL1); 642 + return ret; 643 + } 644 + if (on) 645 + regval |= chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN; 646 + else 647 + regval &= chan ? ~TWL4030_BCI_ITHEN : ~TWL4030_BCI_TYPEN; 648 + ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, 649 + regval, TWL4030_BCI_BCICTL1); 650 + if (ret) { 651 + dev_err(madc->dev, "unable to write BCICTL1 reg 0x%X\n", 652 + TWL4030_BCI_BCICTL1); 653 + return ret; 654 + } 655 + 656 + return 0; 657 + } 658 + 659 + /* 660 + * Function that sets MADC software power on bit to enable MADC 661 + * @madc - pointer to twl4030_madc_data struct 662 + * @on - Enable or disable MADC software powen on bit. 663 + * returns error if i2c read/write fails else 0 664 + */ 665 + static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on) 666 + { 667 + u8 regval; 668 + int ret; 669 + 670 + ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, 671 + &regval, TWL4030_MADC_CTRL1); 672 + if (ret) { 673 + dev_err(madc->dev, "unable to read madc ctrl1 reg 0x%X\n", 674 + TWL4030_MADC_CTRL1); 675 + return ret; 676 + } 677 + if (on) 678 + regval |= TWL4030_MADC_MADCON; 679 + else 680 + regval &= ~TWL4030_MADC_MADCON; 681 + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, regval, TWL4030_MADC_CTRL1); 682 + if (ret) { 683 + dev_err(madc->dev, "unable to write madc ctrl1 reg 0x%X\n", 684 + TWL4030_MADC_CTRL1); 685 + return ret; 686 + } 687 + 688 + return 0; 689 + } 690 + 691 + /* 692 + * Initialize MADC and request for threaded irq 693 + */ 694 + static int __devinit twl4030_madc_probe(struct platform_device *pdev) 695 + { 696 + struct twl4030_madc_data *madc; 697 + struct twl4030_madc_platform_data *pdata = pdev->dev.platform_data; 698 + int ret; 699 + u8 regval; 700 + 701 + if (!pdata) { 702 + dev_err(&pdev->dev, "platform_data not available\n"); 703 + return -EINVAL; 704 + } 705 + madc = kzalloc(sizeof(*madc), GFP_KERNEL); 706 + if (!madc) 707 + return -ENOMEM; 708 + 709 + /* 710 + * Phoenix provides 2 interrupt lines. The first one is connected to 711 + * the OMAP. The other one can be connected to the other processor such 712 + * as modem. Hence two separate ISR and IMR registers. 713 + */ 714 + madc->imr = (pdata->irq_line == 1) ? 715 + TWL4030_MADC_IMR1 : TWL4030_MADC_IMR2; 716 + madc->isr = (pdata->irq_line == 1) ? 717 + TWL4030_MADC_ISR1 : TWL4030_MADC_ISR2; 718 + ret = twl4030_madc_set_power(madc, 1); 719 + if (ret < 0) 720 + goto err_power; 721 + ret = twl4030_madc_set_current_generator(madc, 0, 1); 722 + if (ret < 0) 723 + goto err_current_generator; 724 + 725 + ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, 726 + &regval, TWL4030_BCI_BCICTL1); 727 + if (ret) { 728 + dev_err(&pdev->dev, "unable to read reg BCI CTL1 0x%X\n", 729 + TWL4030_BCI_BCICTL1); 730 + goto err_i2c; 731 + } 732 + regval |= TWL4030_BCI_MESBAT; 733 + ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, 734 + regval, TWL4030_BCI_BCICTL1); 735 + if (ret) { 736 + dev_err(&pdev->dev, "unable to write reg BCI Ctl1 0x%X\n", 737 + TWL4030_BCI_BCICTL1); 738 + goto err_i2c; 739 + } 740 + platform_set_drvdata(pdev, madc); 741 + mutex_init(&madc->lock); 742 + ret = request_threaded_irq(platform_get_irq(pdev, 0), NULL, 743 + twl4030_madc_threaded_irq_handler, 744 + IRQF_TRIGGER_RISING, "twl4030_madc", madc); 745 + if (ret) { 746 + dev_dbg(&pdev->dev, "could not request irq\n"); 747 + goto err_irq; 748 + } 749 + twl4030_madc = madc; 750 + return 0; 751 + err_irq: 752 + platform_set_drvdata(pdev, NULL); 753 + err_i2c: 754 + twl4030_madc_set_current_generator(madc, 0, 0); 755 + err_current_generator: 756 + twl4030_madc_set_power(madc, 0); 757 + err_power: 758 + kfree(madc); 759 + 760 + return ret; 761 + } 762 + 763 + static int __devexit twl4030_madc_remove(struct platform_device *pdev) 764 + { 765 + struct twl4030_madc_data *madc = platform_get_drvdata(pdev); 766 + 767 + free_irq(platform_get_irq(pdev, 0), madc); 768 + platform_set_drvdata(pdev, NULL); 769 + twl4030_madc_set_current_generator(madc, 0, 0); 770 + twl4030_madc_set_power(madc, 0); 771 + kfree(madc); 772 + 773 + return 0; 774 + } 775 + 776 + static struct platform_driver twl4030_madc_driver = { 777 + .probe = twl4030_madc_probe, 778 + .remove = __exit_p(twl4030_madc_remove), 779 + .driver = { 780 + .name = "twl4030_madc", 781 + .owner = THIS_MODULE, 782 + }, 783 + }; 784 + 785 + static int __init twl4030_madc_init(void) 786 + { 787 + return platform_driver_register(&twl4030_madc_driver); 788 + } 789 + 790 + module_init(twl4030_madc_init); 791 + 792 + static void __exit twl4030_madc_exit(void) 793 + { 794 + platform_driver_unregister(&twl4030_madc_driver); 795 + } 796 + 797 + module_exit(twl4030_madc_exit); 798 + 799 + MODULE_DESCRIPTION("TWL4030 ADC driver"); 800 + MODULE_LICENSE("GPL"); 801 + MODULE_AUTHOR("J Keerthy"); 802 + MODULE_ALIAS("twl4030_madc");
+141
include/linux/i2c/twl4030-madc.h
··· 1 + /* 2 + * twl4030_madc.h - Header for TWL4030 MADC 3 + * 4 + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ 5 + * J Keerthy <j-keerthy@ti.com> 6 + * 7 + * This program is free software; you can redistribute it and/or 8 + * modify it under the terms of the GNU General Public License 9 + * version 2 as published by the Free Software Foundation. 10 + * 11 + * This program is distributed in the hope that it will be useful, but 12 + * WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 + * General Public License for more details. 15 + * 16 + * You should have received a copy of the GNU General Public License 17 + * along with this program; if not, write to the Free Software 18 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 19 + * 02110-1301 USA 20 + * 21 + */ 22 + 23 + #ifndef _TWL4030_MADC_H 24 + #define _TWL4030_MADC_H 25 + 26 + struct twl4030_madc_conversion_method { 27 + u8 sel; 28 + u8 avg; 29 + u8 rbase; 30 + u8 ctrl; 31 + }; 32 + 33 + #define TWL4030_MADC_MAX_CHANNELS 16 34 + 35 + 36 + /* 37 + * twl4030_madc_request- madc request packet for channel conversion 38 + * @channels: 16 bit bitmap for individual channels 39 + * @do_avgP: sample the input channel for 4 consecutive cycles 40 + * @method: RT, SW1, SW2 41 + * @type: Polling or interrupt based method 42 + */ 43 + 44 + struct twl4030_madc_request { 45 + unsigned long channels; 46 + u16 do_avg; 47 + u16 method; 48 + u16 type; 49 + bool active; 50 + bool result_pending; 51 + int rbuf[TWL4030_MADC_MAX_CHANNELS]; 52 + void (*func_cb)(int len, int channels, int *buf); 53 + }; 54 + 55 + enum conversion_methods { 56 + TWL4030_MADC_RT, 57 + TWL4030_MADC_SW1, 58 + TWL4030_MADC_SW2, 59 + TWL4030_MADC_NUM_METHODS 60 + }; 61 + 62 + enum sample_type { 63 + TWL4030_MADC_WAIT, 64 + TWL4030_MADC_IRQ_ONESHOT, 65 + TWL4030_MADC_IRQ_REARM 66 + }; 67 + 68 + #define TWL4030_MADC_CTRL1 0x00 69 + #define TWL4030_MADC_CTRL2 0x01 70 + 71 + #define TWL4030_MADC_RTSELECT_LSB 0x02 72 + #define TWL4030_MADC_SW1SELECT_LSB 0x06 73 + #define TWL4030_MADC_SW2SELECT_LSB 0x0A 74 + 75 + #define TWL4030_MADC_RTAVERAGE_LSB 0x04 76 + #define TWL4030_MADC_SW1AVERAGE_LSB 0x08 77 + #define TWL4030_MADC_SW2AVERAGE_LSB 0x0C 78 + 79 + #define TWL4030_MADC_CTRL_SW1 0x12 80 + #define TWL4030_MADC_CTRL_SW2 0x13 81 + 82 + #define TWL4030_MADC_RTCH0_LSB 0x17 83 + #define TWL4030_MADC_GPCH0_LSB 0x37 84 + 85 + #define TWL4030_MADC_MADCON (1 << 0) /* MADC power on */ 86 + #define TWL4030_MADC_BUSY (1 << 0) /* MADC busy */ 87 + /* MADC conversion completion */ 88 + #define TWL4030_MADC_EOC_SW (1 << 1) 89 + /* MADC SWx start conversion */ 90 + #define TWL4030_MADC_SW_START (1 << 5) 91 + #define TWL4030_MADC_ADCIN0 (1 << 0) 92 + #define TWL4030_MADC_ADCIN1 (1 << 1) 93 + #define TWL4030_MADC_ADCIN2 (1 << 2) 94 + #define TWL4030_MADC_ADCIN3 (1 << 3) 95 + #define TWL4030_MADC_ADCIN4 (1 << 4) 96 + #define TWL4030_MADC_ADCIN5 (1 << 5) 97 + #define TWL4030_MADC_ADCIN6 (1 << 6) 98 + #define TWL4030_MADC_ADCIN7 (1 << 7) 99 + #define TWL4030_MADC_ADCIN8 (1 << 8) 100 + #define TWL4030_MADC_ADCIN9 (1 << 9) 101 + #define TWL4030_MADC_ADCIN10 (1 << 10) 102 + #define TWL4030_MADC_ADCIN11 (1 << 11) 103 + #define TWL4030_MADC_ADCIN12 (1 << 12) 104 + #define TWL4030_MADC_ADCIN13 (1 << 13) 105 + #define TWL4030_MADC_ADCIN14 (1 << 14) 106 + #define TWL4030_MADC_ADCIN15 (1 << 15) 107 + 108 + /* Fixed channels */ 109 + #define TWL4030_MADC_BTEMP TWL4030_MADC_ADCIN1 110 + #define TWL4030_MADC_VBUS TWL4030_MADC_ADCIN8 111 + #define TWL4030_MADC_VBKB TWL4030_MADC_ADCIN9 112 + #define TWL4030_MADC_ICHG TWL4030_MADC_ADCIN10 113 + #define TWL4030_MADC_VCHG TWL4030_MADC_ADCIN11 114 + #define TWL4030_MADC_VBAT TWL4030_MADC_ADCIN12 115 + 116 + /* Step size and prescaler ratio */ 117 + #define TEMP_STEP_SIZE 147 118 + #define TEMP_PSR_R 100 119 + #define CURR_STEP_SIZE 147 120 + #define CURR_PSR_R1 44 121 + #define CURR_PSR_R2 88 122 + 123 + #define TWL4030_BCI_BCICTL1 0x23 124 + #define TWL4030_BCI_CGAIN 0x020 125 + #define TWL4030_BCI_MESBAT (1 << 1) 126 + #define TWL4030_BCI_TYPEN (1 << 4) 127 + #define TWL4030_BCI_ITHEN (1 << 3) 128 + 129 + #define REG_BCICTL2 0x024 130 + #define TWL4030_BCI_ITHSENS 0x007 131 + 132 + struct twl4030_madc_user_parms { 133 + int channel; 134 + int average; 135 + int status; 136 + u16 result; 137 + }; 138 + 139 + int twl4030_madc_conversion(struct twl4030_madc_request *conv); 140 + int twl4030_get_madc_conversion(int channel_no); 141 + #endif