1 /* 2 * da9063-core.c: Device access for Dialog DA9063 modules 3 * 4 * Copyright 2012 Dialog Semiconductors Ltd. 5 * Copyright 2013 Philipp Zabel, Pengutronix 6 * 7 * Author: Krystian Garbaciak <krystian.garbaciak@diasemi.com>, 8 * Michal Hajduk <michal.hajduk@diasemi.com> 9 * 10 * This program is free software; you can redistribute it and/or modify it 11 * under the terms of the GNU General Public License as published by the 12 * Free Software Foundation; either version 2 of the License, or (at your 13 * option) any later version. 14 * 15 */ 16 17 #include <linux/kernel.h> 18 #include <linux/module.h> 19 #include <linux/init.h> 20 #include <linux/slab.h> 21 #include <linux/device.h> 22 #include <linux/delay.h> 23 #include <linux/interrupt.h> 24 #include <linux/mutex.h> 25 #include <linux/mfd/core.h> 26 #include <linux/regmap.h> 27 28 #include <linux/mfd/da9063/core.h> 29 #include <linux/mfd/da9063/pdata.h> 30 #include <linux/mfd/da9063/registers.h> 31 32 #include <linux/proc_fs.h> 33 #include <linux/kthread.h> 34 #include <linux/uaccess.h> 35 36 37 static struct resource da9063_regulators_resources[] = { 38 { 39 .name = "LDO_LIM", 40 .start = DA9063_IRQ_LDO_LIM, 41 .end = DA9063_IRQ_LDO_LIM, 42 .flags = IORESOURCE_IRQ, 43 }, 44 }; 45 46 static struct resource da9063_rtc_resources[] = { 47 { 48 .name = "ALARM", 49 .start = DA9063_IRQ_ALARM, 50 .end = DA9063_IRQ_ALARM, 51 .flags = IORESOURCE_IRQ, 52 }, 53 { 54 .name = "TICK", 55 .start = DA9063_IRQ_TICK, 56 .end = DA9063_IRQ_TICK, 57 .flags = IORESOURCE_IRQ, 58 } 59 }; 60 61 static struct resource da9063_onkey_resources[] = { 62 { 63 .start = DA9063_IRQ_ONKEY, 64 .end = DA9063_IRQ_ONKEY, 65 .flags = IORESOURCE_IRQ, 66 }, 67 }; 68 69 static struct resource da9063_hwmon_resources[] = { 70 { 71 .start = DA9063_IRQ_ADC_RDY, 72 .end = DA9063_IRQ_ADC_RDY, 73 .flags = IORESOURCE_IRQ, 74 }, 75 }; 76 77 78 static struct mfd_cell da9063_devs[] = { 79 { 80 .name = DA9063_DRVNAME_REGULATORS, 81 .num_resources = ARRAY_SIZE(da9063_regulators_resources), 82 .resources = da9063_regulators_resources, 83 }, 84 { 85 .name = DA9063_DRVNAME_LEDS, 86 }, 87 { 88 .name = DA9063_DRVNAME_WATCHDOG, 89 }, 90 { 91 .name = DA9063_DRVNAME_HWMON, 92 .num_resources = ARRAY_SIZE(da9063_hwmon_resources), 93 .resources = da9063_hwmon_resources, 94 }, 95 { 96 .name = DA9063_DRVNAME_ONKEY, 97 .num_resources = ARRAY_SIZE(da9063_onkey_resources), 98 .resources = da9063_onkey_resources, 99 }, 100 { 101 .name = DA9063_DRVNAME_RTC, 102 .num_resources = ARRAY_SIZE(da9063_rtc_resources), 103 .resources = da9063_rtc_resources, 104 }, 105 { 106 .name = DA9063_DRVNAME_VIBRATION, 107 }, 108 }; 109 110 int da9063_device_init(struct da9063 *da9063, unsigned int irq) 111 { 112 struct da9063_pdata *pdata = da9063->dev->platform_data; 113 int model, revision; 114 int ret; 115 116 if (pdata) { 117 da9063->flags = pdata->flags; 118 da9063->irq_base = pdata->irq_base; 119 } else { 120 da9063->flags = 0; 121 da9063->irq_base = 0; 122 } 123 da9063->chip_irq = irq; 124 125 if (pdata && pdata->init != NULL) { 126 ret = pdata->init(da9063); 127 if (ret != 0) { 128 dev_err(da9063->dev, 129 "Platform initialization failed.\n"); 130 return ret; 131 } 132 } 133 134 ret = regmap_read(da9063->regmap, DA9063_REG_CHIP_ID, &model); 135 if (ret < 0) { 136 dev_err(da9063->dev, "Cannot read chip model id.\n"); 137 return -EIO; 138 } 139 if (model != PMIC_DA9063) { 140 dev_err(da9063->dev, "Invalid chip model id: 0x%02x\n", model); 141 return -ENODEV; 142 } 143 144 ret = regmap_read(da9063->regmap, DA9063_REG_CHIP_VARIANT, &revision); 145 if (ret < 0) { 146 dev_err(da9063->dev, "Cannot read chip revision id.\n"); 147 return -EIO; 148 } 149 revision >>= DA9063_CHIP_VARIANT_SHIFT; 150 if (revision != 3) { 151 dev_err(da9063->dev, "Unknown chip revision: %d\n", revision); 152 return -ENODEV; 153 } 154 155 da9063->model = model; 156 da9063->revision = revision; 157 158 dev_info(da9063->dev, 159 "Device detected (model-ID: 0x%02X rev-ID: 0x%02X)\n", 160 model, revision); 161 162 ret = da9063_irq_init(da9063); 163 if (ret) { 164 dev_err(da9063->dev, "Cannot initialize interrupts.\n"); 165 return ret; 166 } 167 168 ret = mfd_add_devices(da9063->dev, -1, da9063_devs, 169 ARRAY_SIZE(da9063_devs), NULL, da9063->irq_base, 170 NULL); 171 if (ret) 172 dev_err(da9063->dev, "Cannot add MFD cells\n"); 173 174 return ret; 175 } 176 177 void da9063_device_exit(struct da9063 *da9063) 178 { 179 mfd_remove_devices(da9063->dev); 180 da9063_irq_exit(da9063); 181 } 182 183 MODULE_DESCRIPTION("PMIC driver for Dialog DA9063"); 184 MODULE_AUTHOR("Krystian Garbaciak <krystian.garbaciak@diasemi.com>, Michal Hajduk <michal.hajduk@diasemi.com>"); 185 MODULE_LICENSE("GPL"); 186