tps6105x.c (5796B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Core driver for TPS61050/61052 boost converters, used for while LED 4 * driving, audio power amplification, white LED flash, and generic 5 * boost conversion. Additionally it provides a 1-bit GPIO pin (out or in) 6 * and a flash synchronization pin to synchronize flash events when used as 7 * flashgun. 8 * 9 * Copyright (C) 2011 ST-Ericsson SA 10 * Written on behalf of Linaro for ST-Ericsson 11 * 12 * Author: Linus Walleij <linus.walleij@linaro.org> 13 */ 14 15#include <linux/module.h> 16#include <linux/init.h> 17#include <linux/i2c.h> 18#include <linux/regmap.h> 19#include <linux/gpio.h> 20#include <linux/spinlock.h> 21#include <linux/slab.h> 22#include <linux/err.h> 23#include <linux/mfd/core.h> 24#include <linux/mfd/tps6105x.h> 25 26static struct regmap_config tps6105x_regmap_config = { 27 .reg_bits = 8, 28 .val_bits = 8, 29 .max_register = TPS6105X_REG_3, 30}; 31 32static int tps6105x_startup(struct tps6105x *tps6105x) 33{ 34 int ret; 35 unsigned int regval; 36 37 ret = regmap_read(tps6105x->regmap, TPS6105X_REG_0, ®val); 38 if (ret) 39 return ret; 40 switch (regval >> TPS6105X_REG0_MODE_SHIFT) { 41 case TPS6105X_REG0_MODE_SHUTDOWN: 42 dev_info(&tps6105x->client->dev, 43 "TPS6105x found in SHUTDOWN mode\n"); 44 break; 45 case TPS6105X_REG0_MODE_TORCH: 46 dev_info(&tps6105x->client->dev, 47 "TPS6105x found in TORCH mode\n"); 48 break; 49 case TPS6105X_REG0_MODE_TORCH_FLASH: 50 dev_info(&tps6105x->client->dev, 51 "TPS6105x found in FLASH mode\n"); 52 break; 53 case TPS6105X_REG0_MODE_VOLTAGE: 54 dev_info(&tps6105x->client->dev, 55 "TPS6105x found in VOLTAGE mode\n"); 56 break; 57 default: 58 break; 59 } 60 61 return ret; 62} 63 64/* 65 * MFD cells - we always have a GPIO cell and we have one cell 66 * which is selected operation mode. 67 */ 68static struct mfd_cell tps6105x_gpio_cell = { 69 .name = "tps6105x-gpio", 70}; 71 72static struct mfd_cell tps6105x_leds_cell = { 73 .name = "tps6105x-leds", 74}; 75 76static struct mfd_cell tps6105x_flash_cell = { 77 .name = "tps6105x-flash", 78}; 79 80static struct mfd_cell tps6105x_regulator_cell = { 81 .name = "tps6105x-regulator", 82}; 83 84static int tps6105x_add_device(struct tps6105x *tps6105x, 85 struct mfd_cell *cell) 86{ 87 cell->platform_data = tps6105x; 88 cell->pdata_size = sizeof(*tps6105x); 89 90 return mfd_add_devices(&tps6105x->client->dev, 91 PLATFORM_DEVID_AUTO, cell, 1, NULL, 0, NULL); 92} 93 94static struct tps6105x_platform_data *tps6105x_parse_dt(struct device *dev) 95{ 96 struct device_node *np = dev->of_node; 97 struct tps6105x_platform_data *pdata; 98 struct device_node *child; 99 100 if (!np) 101 return ERR_PTR(-EINVAL); 102 if (of_get_available_child_count(np) > 1) { 103 dev_err(dev, "cannot support multiple operational modes"); 104 return ERR_PTR(-EINVAL); 105 } 106 pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); 107 if (!pdata) 108 return ERR_PTR(-ENOMEM); 109 pdata->mode = TPS6105X_MODE_SHUTDOWN; 110 for_each_available_child_of_node(np, child) { 111 if (child->name && !of_node_cmp(child->name, "regulator")) 112 pdata->mode = TPS6105X_MODE_VOLTAGE; 113 else if (child->name && !of_node_cmp(child->name, "led")) 114 pdata->mode = TPS6105X_MODE_TORCH; 115 } 116 117 return pdata; 118} 119 120static int tps6105x_probe(struct i2c_client *client, 121 const struct i2c_device_id *id) 122{ 123 struct tps6105x *tps6105x; 124 struct tps6105x_platform_data *pdata; 125 int ret; 126 127 pdata = dev_get_platdata(&client->dev); 128 if (!pdata) 129 pdata = tps6105x_parse_dt(&client->dev); 130 if (IS_ERR(pdata)) { 131 dev_err(&client->dev, "No platform data or DT found"); 132 return PTR_ERR(pdata); 133 } 134 135 tps6105x = devm_kmalloc(&client->dev, sizeof(*tps6105x), GFP_KERNEL); 136 if (!tps6105x) 137 return -ENOMEM; 138 139 tps6105x->regmap = devm_regmap_init_i2c(client, &tps6105x_regmap_config); 140 if (IS_ERR(tps6105x->regmap)) 141 return PTR_ERR(tps6105x->regmap); 142 143 i2c_set_clientdata(client, tps6105x); 144 tps6105x->client = client; 145 tps6105x->pdata = pdata; 146 147 ret = tps6105x_startup(tps6105x); 148 if (ret) { 149 dev_err(&client->dev, "chip initialization failed\n"); 150 return ret; 151 } 152 153 ret = tps6105x_add_device(tps6105x, &tps6105x_gpio_cell); 154 if (ret) 155 return ret; 156 157 switch (pdata->mode) { 158 case TPS6105X_MODE_SHUTDOWN: 159 dev_info(&client->dev, 160 "present, not used for anything, only GPIO\n"); 161 break; 162 case TPS6105X_MODE_TORCH: 163 ret = tps6105x_add_device(tps6105x, &tps6105x_leds_cell); 164 break; 165 case TPS6105X_MODE_TORCH_FLASH: 166 ret = tps6105x_add_device(tps6105x, &tps6105x_flash_cell); 167 break; 168 case TPS6105X_MODE_VOLTAGE: 169 ret = tps6105x_add_device(tps6105x, &tps6105x_regulator_cell); 170 break; 171 default: 172 dev_warn(&client->dev, "invalid mode: %d\n", pdata->mode); 173 break; 174 } 175 176 if (ret) 177 mfd_remove_devices(&client->dev); 178 179 return ret; 180} 181 182static int tps6105x_remove(struct i2c_client *client) 183{ 184 struct tps6105x *tps6105x = i2c_get_clientdata(client); 185 186 mfd_remove_devices(&client->dev); 187 188 /* Put chip in shutdown mode */ 189 regmap_update_bits(tps6105x->regmap, TPS6105X_REG_0, 190 TPS6105X_REG0_MODE_MASK, 191 TPS6105X_MODE_SHUTDOWN << TPS6105X_REG0_MODE_SHIFT); 192 193 return 0; 194} 195 196static const struct i2c_device_id tps6105x_id[] = { 197 { "tps61050", 0 }, 198 { "tps61052", 0 }, 199 { } 200}; 201MODULE_DEVICE_TABLE(i2c, tps6105x_id); 202 203static const struct of_device_id tps6105x_of_match[] = { 204 { .compatible = "ti,tps61050" }, 205 { .compatible = "ti,tps61052" }, 206 { }, 207}; 208MODULE_DEVICE_TABLE(of, tps6105x_of_match); 209 210static struct i2c_driver tps6105x_driver = { 211 .driver = { 212 .name = "tps6105x", 213 .of_match_table = tps6105x_of_match, 214 }, 215 .probe = tps6105x_probe, 216 .remove = tps6105x_remove, 217 .id_table = tps6105x_id, 218}; 219 220static int __init tps6105x_init(void) 221{ 222 return i2c_add_driver(&tps6105x_driver); 223} 224subsys_initcall(tps6105x_init); 225 226static void __exit tps6105x_exit(void) 227{ 228 i2c_del_driver(&tps6105x_driver); 229} 230module_exit(tps6105x_exit); 231 232MODULE_AUTHOR("Linus Walleij"); 233MODULE_DESCRIPTION("TPS6105x White LED Boost Converter Driver"); 234MODULE_LICENSE("GPL v2");