1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * TI LP8788 MFD - core interface 4 * 5 * Copyright 2012 Texas Instruments 6 * 7 * Author: Milo(Woogyom) Kim <milo.kim@ti.com> 8 */ 9 10 #include <linux/err.h> 11 #include <linux/i2c.h> 12 #include <linux/mfd/core.h> 13 #include <linux/mfd/lp8788.h> 14 #include <linux/module.h> 15 #include <linux/slab.h> 16 17 #define MAX_LP8788_REGISTERS 0xA2 18 19 #define MFD_DEV_SIMPLE(_name) \ 20 { \ 21 .name = LP8788_DEV_##_name, \ 22 } 23 24 #define MFD_DEV_WITH_ID(_name, _id) \ 25 { \ 26 .name = LP8788_DEV_##_name, \ 27 .id = _id, \ 28 } 29 30 #define MFD_DEV_WITH_RESOURCE(_name, _resource, num_resource) \ 31 { \ 32 .name = LP8788_DEV_##_name, \ 33 .resources = _resource, \ 34 .num_resources = num_resource, \ 35 } 36 37 static const struct resource chg_irqs[] = { 38 /* Charger Interrupts */ 39 { 40 .start = LP8788_INT_CHG_INPUT_STATE, 41 .end = LP8788_INT_PRECHG_TIMEOUT, 42 .name = LP8788_CHG_IRQ, 43 .flags = IORESOURCE_IRQ, 44 }, 45 /* Power Routing Switch Interrupts */ 46 { 47 .start = LP8788_INT_ENTER_SYS_SUPPORT, 48 .end = LP8788_INT_EXIT_SYS_SUPPORT, 49 .name = LP8788_PRSW_IRQ, 50 .flags = IORESOURCE_IRQ, 51 }, 52 /* Battery Interrupts */ 53 { 54 .start = LP8788_INT_BATT_LOW, 55 .end = LP8788_INT_NO_BATT, 56 .name = LP8788_BATT_IRQ, 57 .flags = IORESOURCE_IRQ, 58 }, 59 }; 60 61 static const struct resource rtc_irqs[] = { 62 { 63 .start = LP8788_INT_RTC_ALARM1, 64 .end = LP8788_INT_RTC_ALARM2, 65 .name = LP8788_ALM_IRQ, 66 .flags = IORESOURCE_IRQ, 67 }, 68 }; 69 70 static const struct mfd_cell lp8788_devs[] = { 71 /* 4 bucks */ 72 MFD_DEV_WITH_ID(BUCK, 1), 73 MFD_DEV_WITH_ID(BUCK, 2), 74 MFD_DEV_WITH_ID(BUCK, 3), 75 MFD_DEV_WITH_ID(BUCK, 4), 76 77 /* 12 digital ldos */ 78 MFD_DEV_WITH_ID(DLDO, 1), 79 MFD_DEV_WITH_ID(DLDO, 2), 80 MFD_DEV_WITH_ID(DLDO, 3), 81 MFD_DEV_WITH_ID(DLDO, 4), 82 MFD_DEV_WITH_ID(DLDO, 5), 83 MFD_DEV_WITH_ID(DLDO, 6), 84 MFD_DEV_WITH_ID(DLDO, 7), 85 MFD_DEV_WITH_ID(DLDO, 8), 86 MFD_DEV_WITH_ID(DLDO, 9), 87 MFD_DEV_WITH_ID(DLDO, 10), 88 MFD_DEV_WITH_ID(DLDO, 11), 89 MFD_DEV_WITH_ID(DLDO, 12), 90 91 /* 10 analog ldos */ 92 MFD_DEV_WITH_ID(ALDO, 1), 93 MFD_DEV_WITH_ID(ALDO, 2), 94 MFD_DEV_WITH_ID(ALDO, 3), 95 MFD_DEV_WITH_ID(ALDO, 4), 96 MFD_DEV_WITH_ID(ALDO, 5), 97 MFD_DEV_WITH_ID(ALDO, 6), 98 MFD_DEV_WITH_ID(ALDO, 7), 99 MFD_DEV_WITH_ID(ALDO, 8), 100 MFD_DEV_WITH_ID(ALDO, 9), 101 MFD_DEV_WITH_ID(ALDO, 10), 102 103 /* ADC */ 104 MFD_DEV_SIMPLE(ADC), 105 106 /* battery charger */ 107 MFD_DEV_WITH_RESOURCE(CHARGER, chg_irqs, ARRAY_SIZE(chg_irqs)), 108 109 /* rtc */ 110 MFD_DEV_WITH_RESOURCE(RTC, rtc_irqs, ARRAY_SIZE(rtc_irqs)), 111 112 /* backlight */ 113 MFD_DEV_SIMPLE(BACKLIGHT), 114 115 /* current sink for vibrator */ 116 MFD_DEV_SIMPLE(VIBRATOR), 117 118 /* current sink for keypad LED */ 119 MFD_DEV_SIMPLE(KEYLED), 120 }; 121 122 int lp8788_read_byte(struct lp8788 *lp, u8 reg, u8 *data) 123 { 124 int ret; 125 unsigned int val; 126 127 ret = regmap_read(lp->regmap, reg, &val); 128 if (ret < 0) { 129 dev_err(lp->dev, "failed to read 0x%.2x\n", reg); 130 return ret; 131 } 132 133 *data = (u8)val; 134 return 0; 135 } 136 EXPORT_SYMBOL_GPL(lp8788_read_byte); 137 138 int lp8788_read_multi_bytes(struct lp8788 *lp, u8 reg, u8 *data, size_t count) 139 { 140 return regmap_bulk_read(lp->regmap, reg, data, count); 141 } 142 EXPORT_SYMBOL_GPL(lp8788_read_multi_bytes); 143 144 int lp8788_write_byte(struct lp8788 *lp, u8 reg, u8 data) 145 { 146 return regmap_write(lp->regmap, reg, data); 147 } 148 EXPORT_SYMBOL_GPL(lp8788_write_byte); 149 150 int lp8788_update_bits(struct lp8788 *lp, u8 reg, u8 mask, u8 data) 151 { 152 return regmap_update_bits(lp->regmap, reg, mask, data); 153 } 154 EXPORT_SYMBOL_GPL(lp8788_update_bits); 155 156 static int lp8788_platform_init(struct lp8788 *lp) 157 { 158 struct lp8788_platform_data *pdata = lp->pdata; 159 160 return (pdata && pdata->init_func) ? pdata->init_func(lp) : 0; 161 } 162 163 static const struct regmap_config lp8788_regmap_config = { 164 .reg_bits = 8, 165 .val_bits = 8, 166 .max_register = MAX_LP8788_REGISTERS, 167 }; 168 169 static int lp8788_probe(struct i2c_client *cl) 170 { 171 struct lp8788 *lp; 172 struct lp8788_platform_data *pdata = dev_get_platdata(&cl->dev); 173 int ret; 174 175 lp = devm_kzalloc(&cl->dev, sizeof(struct lp8788), GFP_KERNEL); 176 if (!lp) 177 return -ENOMEM; 178 179 lp->regmap = devm_regmap_init_i2c(cl, &lp8788_regmap_config); 180 if (IS_ERR(lp->regmap)) { 181 ret = PTR_ERR(lp->regmap); 182 dev_err(&cl->dev, "regmap init i2c err: %d\n", ret); 183 return ret; 184 } 185 186 lp->pdata = pdata; 187 lp->dev = &cl->dev; 188 i2c_set_clientdata(cl, lp); 189 190 ret = lp8788_platform_init(lp); 191 if (ret) 192 return ret; 193 194 ret = lp8788_irq_init(lp, cl->irq); 195 if (ret) 196 return ret; 197 198 ret = mfd_add_devices(lp->dev, -1, lp8788_devs, 199 ARRAY_SIZE(lp8788_devs), NULL, 0, NULL); 200 if (ret) 201 goto err_exit_irq; 202 203 return 0; 204 205 err_exit_irq: 206 lp8788_irq_exit(lp); 207 return ret; 208 } 209 210 static void lp8788_remove(struct i2c_client *cl) 211 { 212 struct lp8788 *lp = i2c_get_clientdata(cl); 213 214 mfd_remove_devices(lp->dev); 215 lp8788_irq_exit(lp); 216 } 217 218 static const struct i2c_device_id lp8788_ids[] = { 219 {"lp8788", 0}, 220 { } 221 }; 222 MODULE_DEVICE_TABLE(i2c, lp8788_ids); 223 224 static struct i2c_driver lp8788_driver = { 225 .driver = { 226 .name = "lp8788", 227 }, 228 .probe_new = lp8788_probe, 229 .remove = lp8788_remove, 230 .id_table = lp8788_ids, 231 }; 232 233 static int __init lp8788_init(void) 234 { 235 return i2c_add_driver(&lp8788_driver); 236 } 237 subsys_initcall(lp8788_init); 238 239 static void __exit lp8788_exit(void) 240 { 241 i2c_del_driver(&lp8788_driver); 242 } 243 module_exit(lp8788_exit); 244 245 MODULE_DESCRIPTION("TI LP8788 MFD Driver"); 246 MODULE_AUTHOR("Milo Kim"); 247