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