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 const 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 { 122 struct tps6105x *tps6105x; 123 struct tps6105x_platform_data *pdata; 124 int ret; 125 126 pdata = dev_get_platdata(&client->dev); 127 if (!pdata) 128 pdata = tps6105x_parse_dt(&client->dev); 129 if (IS_ERR(pdata)) { 130 dev_err(&client->dev, "No platform data or DT found"); 131 return PTR_ERR(pdata); 132 } 133 134 tps6105x = devm_kmalloc(&client->dev, sizeof(*tps6105x), GFP_KERNEL); 135 if (!tps6105x) 136 return -ENOMEM; 137 138 tps6105x->regmap = devm_regmap_init_i2c(client, &tps6105x_regmap_config); 139 if (IS_ERR(tps6105x->regmap)) 140 return PTR_ERR(tps6105x->regmap); 141 142 i2c_set_clientdata(client, tps6105x); 143 tps6105x->client = client; 144 tps6105x->pdata = pdata; 145 146 ret = tps6105x_startup(tps6105x); 147 if (ret) { 148 dev_err(&client->dev, "chip initialization failed\n"); 149 return ret; 150 } 151 152 ret = tps6105x_add_device(tps6105x, &tps6105x_gpio_cell); 153 if (ret) 154 return ret; 155 156 switch (pdata->mode) { 157 case TPS6105X_MODE_SHUTDOWN: 158 dev_info(&client->dev, 159 "present, not used for anything, only GPIO\n"); 160 break; 161 case TPS6105X_MODE_TORCH: 162 ret = tps6105x_add_device(tps6105x, &tps6105x_leds_cell); 163 break; 164 case TPS6105X_MODE_TORCH_FLASH: 165 ret = tps6105x_add_device(tps6105x, &tps6105x_flash_cell); 166 break; 167 case TPS6105X_MODE_VOLTAGE: 168 ret = tps6105x_add_device(tps6105x, &tps6105x_regulator_cell); 169 break; 170 default: 171 dev_warn(&client->dev, "invalid mode: %d\n", pdata->mode); 172 break; 173 } 174 175 if (ret) 176 mfd_remove_devices(&client->dev); 177 178 return ret; 179 } 180 181 static void tps6105x_remove(struct i2c_client *client) 182 { 183 struct tps6105x *tps6105x = i2c_get_clientdata(client); 184 185 mfd_remove_devices(&client->dev); 186 187 /* Put chip in shutdown mode */ 188 regmap_update_bits(tps6105x->regmap, TPS6105X_REG_0, 189 TPS6105X_REG0_MODE_MASK, 190 TPS6105X_MODE_SHUTDOWN << TPS6105X_REG0_MODE_SHIFT); 191 } 192 193 static const struct i2c_device_id tps6105x_id[] = { 194 { "tps61050" }, 195 { "tps61052" }, 196 { } 197 }; 198 MODULE_DEVICE_TABLE(i2c, tps6105x_id); 199 200 static const struct of_device_id tps6105x_of_match[] = { 201 { .compatible = "ti,tps61050" }, 202 { .compatible = "ti,tps61052" }, 203 { }, 204 }; 205 MODULE_DEVICE_TABLE(of, tps6105x_of_match); 206 207 static struct i2c_driver tps6105x_driver = { 208 .driver = { 209 .name = "tps6105x", 210 .of_match_table = tps6105x_of_match, 211 }, 212 .probe = tps6105x_probe, 213 .remove = tps6105x_remove, 214 .id_table = tps6105x_id, 215 }; 216 217 static int __init tps6105x_init(void) 218 { 219 return i2c_add_driver(&tps6105x_driver); 220 } 221 subsys_initcall(tps6105x_init); 222 223 static void __exit tps6105x_exit(void) 224 { 225 i2c_del_driver(&tps6105x_driver); 226 } 227 module_exit(tps6105x_exit); 228 229 MODULE_AUTHOR("Linus Walleij"); 230 MODULE_DESCRIPTION("TPS6105x White LED Boost Converter Driver"); 231 MODULE_LICENSE("GPL v2"); 232