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 26 static struct regmap_config tps6105x_regmap_config = { 27 .reg_bits = 8, 28 .val_bits = 8, 29 .max_register = TPS6105X_REG_3, 30 }; 31 32 static 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 */ 68 static struct mfd_cell tps6105x_gpio_cell = { 69 .name = "tps6105x-gpio", 70 }; 71 72 static struct mfd_cell tps6105x_leds_cell = { 73 .name = "tps6105x-leds", 74 }; 75 76 static struct mfd_cell tps6105x_flash_cell = { 77 .name = "tps6105x-flash", 78 }; 79 80 static struct mfd_cell tps6105x_regulator_cell = { 81 .name = "tps6105x-regulator", 82 }; 83 84 static 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 94 static 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 120 static 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 182 static void 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 194 static const struct i2c_device_id tps6105x_id[] = { 195 { "tps61050", 0 }, 196 { "tps61052", 0 }, 197 { } 198 }; 199 MODULE_DEVICE_TABLE(i2c, tps6105x_id); 200 201 static const struct of_device_id tps6105x_of_match[] = { 202 { .compatible = "ti,tps61050" }, 203 { .compatible = "ti,tps61052" }, 204 { }, 205 }; 206 MODULE_DEVICE_TABLE(of, tps6105x_of_match); 207 208 static struct i2c_driver tps6105x_driver = { 209 .driver = { 210 .name = "tps6105x", 211 .of_match_table = tps6105x_of_match, 212 }, 213 .probe = tps6105x_probe, 214 .remove = tps6105x_remove, 215 .id_table = tps6105x_id, 216 }; 217 218 static int __init tps6105x_init(void) 219 { 220 return i2c_add_driver(&tps6105x_driver); 221 } 222 subsys_initcall(tps6105x_init); 223 224 static void __exit tps6105x_exit(void) 225 { 226 i2c_del_driver(&tps6105x_driver); 227 } 228 module_exit(tps6105x_exit); 229 230 MODULE_AUTHOR("Linus Walleij"); 231 MODULE_DESCRIPTION("TPS6105x White LED Boost Converter Driver"); 232 MODULE_LICENSE("GPL v2"); 233