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 37static 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 46static 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 61static struct resource da9063_onkey_resources[] = { 62 { 63 .start = DA9063_IRQ_ONKEY, 64 .end = DA9063_IRQ_ONKEY, 65 .flags = IORESOURCE_IRQ, 66 }, 67}; 68 69static 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 78static const 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 .of_compatible = "dlg,da9063-watchdog", 90 }, 91 { 92 .name = DA9063_DRVNAME_HWMON, 93 .num_resources = ARRAY_SIZE(da9063_hwmon_resources), 94 .resources = da9063_hwmon_resources, 95 }, 96 { 97 .name = DA9063_DRVNAME_ONKEY, 98 .num_resources = ARRAY_SIZE(da9063_onkey_resources), 99 .resources = da9063_onkey_resources, 100 }, 101 { 102 .name = DA9063_DRVNAME_RTC, 103 .num_resources = ARRAY_SIZE(da9063_rtc_resources), 104 .resources = da9063_rtc_resources, 105 .of_compatible = "dlg,da9063-rtc", 106 }, 107 { 108 .name = DA9063_DRVNAME_VIBRATION, 109 }, 110}; 111 112int da9063_device_init(struct da9063 *da9063, unsigned int irq) 113{ 114 struct da9063_pdata *pdata = da9063->dev->platform_data; 115 int model, variant_id, variant_code; 116 int ret; 117 118 if (pdata) { 119 da9063->flags = pdata->flags; 120 da9063->irq_base = pdata->irq_base; 121 } else { 122 da9063->flags = 0; 123 da9063->irq_base = -1; 124 } 125 da9063->chip_irq = irq; 126 127 if (pdata && pdata->init != NULL) { 128 ret = pdata->init(da9063); 129 if (ret != 0) { 130 dev_err(da9063->dev, 131 "Platform initialization failed.\n"); 132 return ret; 133 } 134 } 135 136 ret = regmap_read(da9063->regmap, DA9063_REG_CHIP_ID, &model); 137 if (ret < 0) { 138 dev_err(da9063->dev, "Cannot read chip model id.\n"); 139 return -EIO; 140 } 141 if (model != PMIC_DA9063) { 142 dev_err(da9063->dev, "Invalid chip model id: 0x%02x\n", model); 143 return -ENODEV; 144 } 145 146 ret = regmap_read(da9063->regmap, DA9063_REG_CHIP_VARIANT, &variant_id); 147 if (ret < 0) { 148 dev_err(da9063->dev, "Cannot read chip variant id.\n"); 149 return -EIO; 150 } 151 152 variant_code = variant_id >> DA9063_CHIP_VARIANT_SHIFT; 153 154 dev_info(da9063->dev, 155 "Device detected (chip-ID: 0x%02X, var-ID: 0x%02X)\n", 156 model, variant_id); 157 158 if (variant_code < PMIC_DA9063_BB && variant_code != PMIC_DA9063_AD) { 159 dev_err(da9063->dev, 160 "Cannot support variant code: 0x%02X\n", variant_code); 161 return -ENODEV; 162 } 163 164 da9063->model = model; 165 da9063->variant_code = variant_code; 166 167 ret = da9063_irq_init(da9063); 168 if (ret) { 169 dev_err(da9063->dev, "Cannot initialize interrupts.\n"); 170 return ret; 171 } 172 173 da9063->irq_base = regmap_irq_chip_get_base(da9063->regmap_irq); 174 175 ret = mfd_add_devices(da9063->dev, -1, da9063_devs, 176 ARRAY_SIZE(da9063_devs), NULL, da9063->irq_base, 177 NULL); 178 if (ret) 179 dev_err(da9063->dev, "Cannot add MFD cells\n"); 180 181 return ret; 182} 183 184void da9063_device_exit(struct da9063 *da9063) 185{ 186 mfd_remove_devices(da9063->dev); 187 da9063_irq_exit(da9063); 188} 189 190MODULE_DESCRIPTION("PMIC driver for Dialog DA9063"); 191MODULE_AUTHOR("Krystian Garbaciak <krystian.garbaciak@diasemi.com>, Michal Hajduk <michal.hajduk@diasemi.com>"); 192MODULE_LICENSE("GPL"); 193