1798a8eeeSLinus Walleij /* 2798a8eeeSLinus Walleij * Core driver for TPS61050/61052 boost converters, used for while LED 3798a8eeeSLinus Walleij * driving, audio power amplification, white LED flash, and generic 4798a8eeeSLinus Walleij * boost conversion. Additionally it provides a 1-bit GPIO pin (out or in) 5798a8eeeSLinus Walleij * and a flash synchronization pin to synchronize flash events when used as 6798a8eeeSLinus Walleij * flashgun. 7798a8eeeSLinus Walleij * 8798a8eeeSLinus Walleij * Copyright (C) 2011 ST-Ericsson SA 9798a8eeeSLinus Walleij * Written on behalf of Linaro for ST-Ericsson 10798a8eeeSLinus Walleij * 11798a8eeeSLinus Walleij * Author: Linus Walleij <linus.walleij@linaro.org> 12798a8eeeSLinus Walleij * 13798a8eeeSLinus Walleij * License terms: GNU General Public License (GPL) version 2 14798a8eeeSLinus Walleij */ 15798a8eeeSLinus Walleij 16798a8eeeSLinus Walleij #include <linux/module.h> 17798a8eeeSLinus Walleij #include <linux/init.h> 18798a8eeeSLinus Walleij #include <linux/i2c.h> 19798a8eeeSLinus Walleij #include <linux/mutex.h> 20798a8eeeSLinus Walleij #include <linux/gpio.h> 21798a8eeeSLinus Walleij #include <linux/spinlock.h> 22798a8eeeSLinus Walleij #include <linux/slab.h> 23798a8eeeSLinus Walleij #include <linux/err.h> 24798a8eeeSLinus Walleij #include <linux/regulator/driver.h> 25798a8eeeSLinus Walleij #include <linux/mfd/core.h> 26798a8eeeSLinus Walleij #include <linux/mfd/tps6105x.h> 27798a8eeeSLinus Walleij 28798a8eeeSLinus Walleij int tps6105x_set(struct tps6105x *tps6105x, u8 reg, u8 value) 29798a8eeeSLinus Walleij { 30798a8eeeSLinus Walleij int ret; 31798a8eeeSLinus Walleij 32798a8eeeSLinus Walleij ret = mutex_lock_interruptible(&tps6105x->lock); 33798a8eeeSLinus Walleij if (ret) 34798a8eeeSLinus Walleij return ret; 35798a8eeeSLinus Walleij ret = i2c_smbus_write_byte_data(tps6105x->client, reg, value); 36798a8eeeSLinus Walleij mutex_unlock(&tps6105x->lock); 37798a8eeeSLinus Walleij if (ret < 0) 38798a8eeeSLinus Walleij return ret; 39798a8eeeSLinus Walleij 40798a8eeeSLinus Walleij return 0; 41798a8eeeSLinus Walleij } 42798a8eeeSLinus Walleij EXPORT_SYMBOL(tps6105x_set); 43798a8eeeSLinus Walleij 44798a8eeeSLinus Walleij int tps6105x_get(struct tps6105x *tps6105x, u8 reg, u8 *buf) 45798a8eeeSLinus Walleij { 46798a8eeeSLinus Walleij int ret; 47798a8eeeSLinus Walleij 48798a8eeeSLinus Walleij ret = mutex_lock_interruptible(&tps6105x->lock); 49798a8eeeSLinus Walleij if (ret) 50798a8eeeSLinus Walleij return ret; 51798a8eeeSLinus Walleij ret = i2c_smbus_read_byte_data(tps6105x->client, reg); 52798a8eeeSLinus Walleij mutex_unlock(&tps6105x->lock); 53798a8eeeSLinus Walleij if (ret < 0) 54798a8eeeSLinus Walleij return ret; 55798a8eeeSLinus Walleij 56798a8eeeSLinus Walleij *buf = ret; 57798a8eeeSLinus Walleij return 0; 58798a8eeeSLinus Walleij } 59798a8eeeSLinus Walleij EXPORT_SYMBOL(tps6105x_get); 60798a8eeeSLinus Walleij 61798a8eeeSLinus Walleij /* 62798a8eeeSLinus Walleij * Masks off the bits in the mask and sets the bits in the bitvalues 63798a8eeeSLinus Walleij * parameter in one atomic operation 64798a8eeeSLinus Walleij */ 65798a8eeeSLinus Walleij int tps6105x_mask_and_set(struct tps6105x *tps6105x, u8 reg, 66798a8eeeSLinus Walleij u8 bitmask, u8 bitvalues) 67798a8eeeSLinus Walleij { 68798a8eeeSLinus Walleij int ret; 69798a8eeeSLinus Walleij u8 regval; 70798a8eeeSLinus Walleij 71798a8eeeSLinus Walleij ret = mutex_lock_interruptible(&tps6105x->lock); 72798a8eeeSLinus Walleij if (ret) 73798a8eeeSLinus Walleij return ret; 74798a8eeeSLinus Walleij ret = i2c_smbus_read_byte_data(tps6105x->client, reg); 75798a8eeeSLinus Walleij if (ret < 0) 76798a8eeeSLinus Walleij goto fail; 77798a8eeeSLinus Walleij regval = ret; 78798a8eeeSLinus Walleij regval = (~bitmask & regval) | (bitmask & bitvalues); 79798a8eeeSLinus Walleij ret = i2c_smbus_write_byte_data(tps6105x->client, reg, regval); 80798a8eeeSLinus Walleij fail: 81798a8eeeSLinus Walleij mutex_unlock(&tps6105x->lock); 82798a8eeeSLinus Walleij if (ret < 0) 83798a8eeeSLinus Walleij return ret; 84798a8eeeSLinus Walleij 85798a8eeeSLinus Walleij return 0; 86798a8eeeSLinus Walleij } 87798a8eeeSLinus Walleij EXPORT_SYMBOL(tps6105x_mask_and_set); 88798a8eeeSLinus Walleij 89f791be49SBill Pemberton static int tps6105x_startup(struct tps6105x *tps6105x) 90798a8eeeSLinus Walleij { 91798a8eeeSLinus Walleij int ret; 92798a8eeeSLinus Walleij u8 regval; 93798a8eeeSLinus Walleij 94798a8eeeSLinus Walleij ret = tps6105x_get(tps6105x, TPS6105X_REG_0, ®val); 95798a8eeeSLinus Walleij if (ret) 96798a8eeeSLinus Walleij return ret; 97798a8eeeSLinus Walleij switch (regval >> TPS6105X_REG0_MODE_SHIFT) { 98798a8eeeSLinus Walleij case TPS6105X_REG0_MODE_SHUTDOWN: 99798a8eeeSLinus Walleij dev_info(&tps6105x->client->dev, 100798a8eeeSLinus Walleij "TPS6105x found in SHUTDOWN mode\n"); 101798a8eeeSLinus Walleij break; 102798a8eeeSLinus Walleij case TPS6105X_REG0_MODE_TORCH: 103798a8eeeSLinus Walleij dev_info(&tps6105x->client->dev, 104798a8eeeSLinus Walleij "TPS6105x found in TORCH mode\n"); 105798a8eeeSLinus Walleij break; 106798a8eeeSLinus Walleij case TPS6105X_REG0_MODE_TORCH_FLASH: 107798a8eeeSLinus Walleij dev_info(&tps6105x->client->dev, 108798a8eeeSLinus Walleij "TPS6105x found in FLASH mode\n"); 109798a8eeeSLinus Walleij break; 110798a8eeeSLinus Walleij case TPS6105X_REG0_MODE_VOLTAGE: 111798a8eeeSLinus Walleij dev_info(&tps6105x->client->dev, 112798a8eeeSLinus Walleij "TPS6105x found in VOLTAGE mode\n"); 113798a8eeeSLinus Walleij break; 114798a8eeeSLinus Walleij default: 115798a8eeeSLinus Walleij break; 116798a8eeeSLinus Walleij } 117798a8eeeSLinus Walleij 118798a8eeeSLinus Walleij return ret; 119798a8eeeSLinus Walleij } 120798a8eeeSLinus Walleij 121798a8eeeSLinus Walleij /* 122798a8eeeSLinus Walleij * MFD cells - we have one cell which is selected operation 123798a8eeeSLinus Walleij * mode, and we always have a GPIO cell. 124798a8eeeSLinus Walleij */ 125798a8eeeSLinus Walleij static struct mfd_cell tps6105x_cells[] = { 126798a8eeeSLinus Walleij { 127798a8eeeSLinus Walleij /* name will be runtime assigned */ 128798a8eeeSLinus Walleij .id = -1, 129798a8eeeSLinus Walleij }, 130798a8eeeSLinus Walleij { 131798a8eeeSLinus Walleij .name = "tps6105x-gpio", 132798a8eeeSLinus Walleij .id = -1, 133798a8eeeSLinus Walleij }, 134798a8eeeSLinus Walleij }; 135798a8eeeSLinus Walleij 136f791be49SBill Pemberton static int tps6105x_probe(struct i2c_client *client, 137798a8eeeSLinus Walleij const struct i2c_device_id *id) 138798a8eeeSLinus Walleij { 139798a8eeeSLinus Walleij struct tps6105x *tps6105x; 140798a8eeeSLinus Walleij struct tps6105x_platform_data *pdata; 141798a8eeeSLinus Walleij int ret; 142798a8eeeSLinus Walleij int i; 143798a8eeeSLinus Walleij 144798a8eeeSLinus Walleij tps6105x = kmalloc(sizeof(*tps6105x), GFP_KERNEL); 145798a8eeeSLinus Walleij if (!tps6105x) 146798a8eeeSLinus Walleij return -ENOMEM; 147798a8eeeSLinus Walleij 148798a8eeeSLinus Walleij i2c_set_clientdata(client, tps6105x); 149798a8eeeSLinus Walleij tps6105x->client = client; 150798a8eeeSLinus Walleij pdata = client->dev.platform_data; 151798a8eeeSLinus Walleij tps6105x->pdata = pdata; 152798a8eeeSLinus Walleij mutex_init(&tps6105x->lock); 153798a8eeeSLinus Walleij 154798a8eeeSLinus Walleij ret = tps6105x_startup(tps6105x); 155798a8eeeSLinus Walleij if (ret) { 156798a8eeeSLinus Walleij dev_err(&client->dev, "chip initialization failed\n"); 157798a8eeeSLinus Walleij goto fail; 158798a8eeeSLinus Walleij } 159798a8eeeSLinus Walleij 160798a8eeeSLinus Walleij /* Remove warning texts when you implement new cell drivers */ 161798a8eeeSLinus Walleij switch (pdata->mode) { 162798a8eeeSLinus Walleij case TPS6105X_MODE_SHUTDOWN: 163798a8eeeSLinus Walleij dev_info(&client->dev, 164798a8eeeSLinus Walleij "present, not used for anything, only GPIO\n"); 165798a8eeeSLinus Walleij break; 166798a8eeeSLinus Walleij case TPS6105X_MODE_TORCH: 167798a8eeeSLinus Walleij tps6105x_cells[0].name = "tps6105x-leds"; 168798a8eeeSLinus Walleij dev_warn(&client->dev, 169798a8eeeSLinus Walleij "torch mode is unsupported\n"); 170798a8eeeSLinus Walleij break; 171798a8eeeSLinus Walleij case TPS6105X_MODE_TORCH_FLASH: 172798a8eeeSLinus Walleij tps6105x_cells[0].name = "tps6105x-flash"; 173798a8eeeSLinus Walleij dev_warn(&client->dev, 174798a8eeeSLinus Walleij "flash mode is unsupported\n"); 175798a8eeeSLinus Walleij break; 176798a8eeeSLinus Walleij case TPS6105X_MODE_VOLTAGE: 177798a8eeeSLinus Walleij tps6105x_cells[0].name ="tps6105x-regulator"; 178798a8eeeSLinus Walleij break; 179798a8eeeSLinus Walleij default: 180798a8eeeSLinus Walleij break; 181798a8eeeSLinus Walleij } 182798a8eeeSLinus Walleij 183798a8eeeSLinus Walleij /* Set up and register the platform devices. */ 184798a8eeeSLinus Walleij for (i = 0; i < ARRAY_SIZE(tps6105x_cells); i++) { 185798a8eeeSLinus Walleij /* One state holder for all drivers, this is simple */ 186a7c98ce2SSamuel Ortiz tps6105x_cells[i].platform_data = tps6105x; 187a7c98ce2SSamuel Ortiz tps6105x_cells[i].pdata_size = sizeof(*tps6105x); 188798a8eeeSLinus Walleij } 189798a8eeeSLinus Walleij 190798a8eeeSLinus Walleij ret = mfd_add_devices(&client->dev, 0, tps6105x_cells, 1910848c94fSMark Brown ARRAY_SIZE(tps6105x_cells), NULL, 0, NULL); 192798a8eeeSLinus Walleij if (ret) 193798a8eeeSLinus Walleij goto fail; 194798a8eeeSLinus Walleij 195798a8eeeSLinus Walleij return 0; 196798a8eeeSLinus Walleij 197798a8eeeSLinus Walleij fail: 198798a8eeeSLinus Walleij kfree(tps6105x); 199798a8eeeSLinus Walleij return ret; 200798a8eeeSLinus Walleij } 201798a8eeeSLinus Walleij 202*4740f73fSBill Pemberton static int tps6105x_remove(struct i2c_client *client) 203798a8eeeSLinus Walleij { 204798a8eeeSLinus Walleij struct tps6105x *tps6105x = i2c_get_clientdata(client); 205798a8eeeSLinus Walleij 206798a8eeeSLinus Walleij mfd_remove_devices(&client->dev); 207798a8eeeSLinus Walleij 208798a8eeeSLinus Walleij /* Put chip in shutdown mode */ 209798a8eeeSLinus Walleij tps6105x_mask_and_set(tps6105x, TPS6105X_REG_0, 210798a8eeeSLinus Walleij TPS6105X_REG0_MODE_MASK, 211798a8eeeSLinus Walleij TPS6105X_MODE_SHUTDOWN << TPS6105X_REG0_MODE_SHIFT); 212798a8eeeSLinus Walleij 213798a8eeeSLinus Walleij kfree(tps6105x); 214798a8eeeSLinus Walleij return 0; 215798a8eeeSLinus Walleij } 216798a8eeeSLinus Walleij 217798a8eeeSLinus Walleij static const struct i2c_device_id tps6105x_id[] = { 218798a8eeeSLinus Walleij { "tps61050", 0 }, 219798a8eeeSLinus Walleij { "tps61052", 0 }, 220798a8eeeSLinus Walleij { } 221798a8eeeSLinus Walleij }; 222798a8eeeSLinus Walleij MODULE_DEVICE_TABLE(i2c, tps6105x_id); 223798a8eeeSLinus Walleij 224798a8eeeSLinus Walleij static struct i2c_driver tps6105x_driver = { 225798a8eeeSLinus Walleij .driver = { 226798a8eeeSLinus Walleij .name = "tps6105x", 227798a8eeeSLinus Walleij }, 228798a8eeeSLinus Walleij .probe = tps6105x_probe, 22984449216SBill Pemberton .remove = tps6105x_remove, 230798a8eeeSLinus Walleij .id_table = tps6105x_id, 231798a8eeeSLinus Walleij }; 232798a8eeeSLinus Walleij 233798a8eeeSLinus Walleij static int __init tps6105x_init(void) 234798a8eeeSLinus Walleij { 235798a8eeeSLinus Walleij return i2c_add_driver(&tps6105x_driver); 236798a8eeeSLinus Walleij } 237798a8eeeSLinus Walleij subsys_initcall(tps6105x_init); 238798a8eeeSLinus Walleij 239798a8eeeSLinus Walleij static void __exit tps6105x_exit(void) 240798a8eeeSLinus Walleij { 241798a8eeeSLinus Walleij i2c_del_driver(&tps6105x_driver); 242798a8eeeSLinus Walleij } 243798a8eeeSLinus Walleij module_exit(tps6105x_exit); 244798a8eeeSLinus Walleij 245798a8eeeSLinus Walleij MODULE_AUTHOR("Linus Walleij"); 246798a8eeeSLinus Walleij MODULE_DESCRIPTION("TPS6105x White LED Boost Converter Driver"); 247798a8eeeSLinus Walleij MODULE_LICENSE("GPL v2"); 248