1 /* 2 * Base driver for Marvell 88PM8607 3 * 4 * Copyright (C) 2009 Marvell International Ltd. 5 * Haojian Zhuang <haojian.zhuang@marvell.com> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 */ 11 12 #include <linux/kernel.h> 13 #include <linux/module.h> 14 #include <linux/i2c.h> 15 #include <linux/interrupt.h> 16 #include <linux/platform_device.h> 17 #include <linux/mfd/core.h> 18 #include <linux/mfd/88pm860x.h> 19 20 21 #define PM8607_REG_RESOURCE(_start, _end) \ 22 { \ 23 .start = PM8607_##_start, \ 24 .end = PM8607_##_end, \ 25 .flags = IORESOURCE_IO, \ 26 } 27 28 static struct resource pm8607_regulator_resources[] = { 29 PM8607_REG_RESOURCE(BUCK1, BUCK1), 30 PM8607_REG_RESOURCE(BUCK2, BUCK2), 31 PM8607_REG_RESOURCE(BUCK3, BUCK3), 32 PM8607_REG_RESOURCE(LDO1, LDO1), 33 PM8607_REG_RESOURCE(LDO2, LDO2), 34 PM8607_REG_RESOURCE(LDO3, LDO3), 35 PM8607_REG_RESOURCE(LDO4, LDO4), 36 PM8607_REG_RESOURCE(LDO5, LDO5), 37 PM8607_REG_RESOURCE(LDO6, LDO6), 38 PM8607_REG_RESOURCE(LDO7, LDO7), 39 PM8607_REG_RESOURCE(LDO8, LDO8), 40 PM8607_REG_RESOURCE(LDO9, LDO9), 41 PM8607_REG_RESOURCE(LDO10, LDO10), 42 PM8607_REG_RESOURCE(LDO12, LDO12), 43 PM8607_REG_RESOURCE(LDO14, LDO14), 44 }; 45 46 #define PM8607_REG_DEVS(_name, _id) \ 47 { \ 48 .name = "88pm8607-" #_name, \ 49 .num_resources = 1, \ 50 .resources = &pm8607_regulator_resources[PM8607_ID_##_id], \ 51 } 52 53 static struct mfd_cell pm8607_devs[] = { 54 PM8607_REG_DEVS(buck1, BUCK1), 55 PM8607_REG_DEVS(buck2, BUCK2), 56 PM8607_REG_DEVS(buck3, BUCK3), 57 PM8607_REG_DEVS(ldo1, LDO1), 58 PM8607_REG_DEVS(ldo2, LDO2), 59 PM8607_REG_DEVS(ldo3, LDO3), 60 PM8607_REG_DEVS(ldo4, LDO4), 61 PM8607_REG_DEVS(ldo5, LDO5), 62 PM8607_REG_DEVS(ldo6, LDO6), 63 PM8607_REG_DEVS(ldo7, LDO7), 64 PM8607_REG_DEVS(ldo8, LDO8), 65 PM8607_REG_DEVS(ldo9, LDO9), 66 PM8607_REG_DEVS(ldo10, LDO10), 67 PM8607_REG_DEVS(ldo12, LDO12), 68 PM8607_REG_DEVS(ldo14, LDO14), 69 }; 70 71 #define CHECK_IRQ(irq) \ 72 do { \ 73 if ((irq < 0) || (irq >= PM860X_NUM_IRQ)) \ 74 return -EINVAL; \ 75 } while (0) 76 77 /* IRQs only occur on 88PM8607 */ 78 int pm860x_mask_irq(struct pm860x_chip *chip, int irq) 79 { 80 struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \ 81 : chip->companion; 82 int offset, data, ret; 83 84 CHECK_IRQ(irq); 85 86 offset = (irq >> 3) + PM8607_INT_MASK_1; 87 data = 1 << (irq % 8); 88 ret = pm860x_set_bits(i2c, offset, data, 0); 89 90 return ret; 91 } 92 EXPORT_SYMBOL(pm860x_mask_irq); 93 94 int pm860x_unmask_irq(struct pm860x_chip *chip, int irq) 95 { 96 struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \ 97 : chip->companion; 98 int offset, data, ret; 99 100 CHECK_IRQ(irq); 101 102 offset = (irq >> 3) + PM8607_INT_MASK_1; 103 data = 1 << (irq % 8); 104 ret = pm860x_set_bits(i2c, offset, data, data); 105 106 return ret; 107 } 108 EXPORT_SYMBOL(pm860x_unmask_irq); 109 110 #define INT_STATUS_NUM (3) 111 112 static irqreturn_t pm8607_irq_thread(int irq, void *data) 113 { 114 DECLARE_BITMAP(irq_status, PM860X_NUM_IRQ); 115 struct pm860x_chip *chip = data; 116 struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \ 117 : chip->companion; 118 unsigned char status_buf[INT_STATUS_NUM << 1]; 119 unsigned long value; 120 int i, ret; 121 122 irq_status[0] = 0; 123 124 /* read out status register */ 125 ret = pm860x_bulk_read(i2c, PM8607_INT_STATUS1, 126 INT_STATUS_NUM << 1, status_buf); 127 if (ret < 0) 128 goto out; 129 if (chip->irq_mode) { 130 /* 0, clear by read. 1, clear by write */ 131 ret = pm860x_bulk_write(i2c, PM8607_INT_STATUS1, 132 INT_STATUS_NUM, status_buf); 133 if (ret < 0) 134 goto out; 135 } 136 137 /* clear masked interrupt status */ 138 for (i = 0, value = 0; i < INT_STATUS_NUM; i++) { 139 status_buf[i] &= status_buf[i + INT_STATUS_NUM]; 140 irq_status[0] |= status_buf[i] << (i * 8); 141 } 142 143 while (!bitmap_empty(irq_status, PM860X_NUM_IRQ)) { 144 irq = find_first_bit(irq_status, PM860X_NUM_IRQ); 145 clear_bit(irq, irq_status); 146 dev_dbg(chip->dev, "Servicing IRQ #%d\n", irq); 147 148 mutex_lock(&chip->irq_lock); 149 if (chip->irq[irq].handler) 150 chip->irq[irq].handler(irq, chip->irq[irq].data); 151 else { 152 pm860x_mask_irq(chip, irq); 153 dev_err(chip->dev, "Nobody cares IRQ %d. " 154 "Now mask it.\n", irq); 155 for (i = 0; i < (INT_STATUS_NUM << 1); i++) { 156 dev_err(chip->dev, "status[%d]:%x\n", i, 157 status_buf[i]); 158 } 159 } 160 mutex_unlock(&chip->irq_lock); 161 } 162 out: 163 return IRQ_HANDLED; 164 } 165 166 int pm860x_request_irq(struct pm860x_chip *chip, int irq, 167 irq_handler_t handler, void *data) 168 { 169 CHECK_IRQ(irq); 170 if (!handler) 171 return -EINVAL; 172 173 mutex_lock(&chip->irq_lock); 174 chip->irq[irq].handler = handler; 175 chip->irq[irq].data = data; 176 mutex_unlock(&chip->irq_lock); 177 178 return 0; 179 } 180 EXPORT_SYMBOL(pm860x_request_irq); 181 182 int pm860x_free_irq(struct pm860x_chip *chip, int irq) 183 { 184 CHECK_IRQ(irq); 185 186 mutex_lock(&chip->irq_lock); 187 chip->irq[irq].handler = NULL; 188 chip->irq[irq].data = NULL; 189 mutex_unlock(&chip->irq_lock); 190 191 return 0; 192 } 193 EXPORT_SYMBOL(pm860x_free_irq); 194 195 static int __devinit device_irq_init(struct pm860x_chip *chip, 196 struct pm860x_platform_data *pdata) 197 { 198 struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \ 199 : chip->companion; 200 unsigned char status_buf[INT_STATUS_NUM]; 201 int data, mask, ret = -EINVAL; 202 203 mutex_init(&chip->irq_lock); 204 205 mask = PM8607_B0_MISC1_INV_INT | PM8607_B0_MISC1_INT_CLEAR 206 | PM8607_B0_MISC1_INT_MASK; 207 data = 0; 208 chip->irq_mode = 0; 209 if (pdata && pdata->irq_mode) { 210 /* 211 * irq_mode defines the way of clearing interrupt. If it's 1, 212 * clear IRQ by write. Otherwise, clear it by read. 213 * This control bit is valid from 88PM8607 B0 steping. 214 */ 215 data |= PM8607_B0_MISC1_INT_CLEAR; 216 chip->irq_mode = 1; 217 } 218 ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, mask, data); 219 if (ret < 0) 220 goto out; 221 222 /* mask all IRQs */ 223 memset(status_buf, 0, INT_STATUS_NUM); 224 ret = pm860x_bulk_write(i2c, PM8607_INT_MASK_1, 225 INT_STATUS_NUM, status_buf); 226 if (ret < 0) 227 goto out; 228 229 if (chip->irq_mode) { 230 /* clear interrupt status by write */ 231 memset(status_buf, 0xFF, INT_STATUS_NUM); 232 ret = pm860x_bulk_write(i2c, PM8607_INT_STATUS1, 233 INT_STATUS_NUM, status_buf); 234 } else { 235 /* clear interrupt status by read */ 236 ret = pm860x_bulk_read(i2c, PM8607_INT_STATUS1, 237 INT_STATUS_NUM, status_buf); 238 } 239 if (ret < 0) 240 goto out; 241 242 memset(chip->irq, 0, sizeof(struct pm860x_irq) * PM860X_NUM_IRQ); 243 244 ret = request_threaded_irq(i2c->irq, NULL, pm8607_irq_thread, 245 IRQF_ONESHOT | IRQF_TRIGGER_LOW, 246 "88PM8607", chip); 247 if (ret < 0) { 248 dev_err(chip->dev, "Failed to request IRQ #%d.\n", i2c->irq); 249 goto out; 250 } 251 chip->chip_irq = i2c->irq; 252 return 0; 253 out: 254 return ret; 255 } 256 257 static void __devexit device_irq_exit(struct pm860x_chip *chip) 258 { 259 if (chip->chip_irq >= 0) 260 free_irq(chip->chip_irq, chip); 261 } 262 263 static void __devinit device_8606_init(struct pm860x_chip *chip, 264 struct i2c_client *i2c, 265 struct pm860x_platform_data *pdata) 266 { 267 } 268 269 static void __devinit device_8607_init(struct pm860x_chip *chip, 270 struct i2c_client *i2c, 271 struct pm860x_platform_data *pdata) 272 { 273 int i, count, data; 274 int ret; 275 276 ret = pm860x_reg_read(i2c, PM8607_CHIP_ID); 277 if (ret < 0) { 278 dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret); 279 goto out; 280 } 281 if ((ret & PM8607_VERSION_MASK) == PM8607_VERSION) 282 dev_info(chip->dev, "Marvell 88PM8607 (ID: %02x) detected\n", 283 ret); 284 else { 285 dev_err(chip->dev, "Failed to detect Marvell 88PM8607. " 286 "Chip ID: %02x\n", ret); 287 goto out; 288 } 289 290 ret = pm860x_reg_read(i2c, PM8607_BUCK3); 291 if (ret < 0) { 292 dev_err(chip->dev, "Failed to read BUCK3 register: %d\n", ret); 293 goto out; 294 } 295 if (ret & PM8607_BUCK3_DOUBLE) 296 chip->buck3_double = 1; 297 298 ret = pm860x_reg_read(i2c, PM8607_B0_MISC1); 299 if (ret < 0) { 300 dev_err(chip->dev, "Failed to read MISC1 register: %d\n", ret); 301 goto out; 302 } 303 304 if (pdata && (pdata->i2c_port == PI2C_PORT)) 305 data = PM8607_B0_MISC1_PI2C; 306 else 307 data = 0; 308 ret = pm860x_set_bits(i2c, PM8607_B0_MISC1, PM8607_B0_MISC1_PI2C, data); 309 if (ret < 0) { 310 dev_err(chip->dev, "Failed to access MISC1:%d\n", ret); 311 goto out; 312 } 313 314 ret = device_irq_init(chip, pdata); 315 if (ret < 0) 316 goto out; 317 318 count = ARRAY_SIZE(pm8607_devs); 319 for (i = 0; i < count; i++) { 320 ret = mfd_add_devices(chip->dev, i, &pm8607_devs[i], 321 1, NULL, 0); 322 if (ret != 0) { 323 dev_err(chip->dev, "Failed to add subdevs\n"); 324 goto out; 325 } 326 } 327 out: 328 return; 329 } 330 331 int pm860x_device_init(struct pm860x_chip *chip, 332 struct pm860x_platform_data *pdata) 333 { 334 chip->chip_irq = -EINVAL; 335 336 switch (chip->id) { 337 case CHIP_PM8606: 338 device_8606_init(chip, chip->client, pdata); 339 break; 340 case CHIP_PM8607: 341 device_8607_init(chip, chip->client, pdata); 342 break; 343 } 344 345 if (chip->companion) { 346 switch (chip->id) { 347 case CHIP_PM8607: 348 device_8606_init(chip, chip->companion, pdata); 349 break; 350 case CHIP_PM8606: 351 device_8607_init(chip, chip->companion, pdata); 352 break; 353 } 354 } 355 356 return 0; 357 } 358 359 void pm860x_device_exit(struct pm860x_chip *chip) 360 { 361 device_irq_exit(chip); 362 mfd_remove_devices(chip->dev); 363 } 364 365 MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM860x"); 366 MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); 367 MODULE_LICENSE("GPL"); 368