18e685483SKrystian Garbaciak /* 28e685483SKrystian Garbaciak * da9063-core.c: Device access for Dialog DA9063 modules 38e685483SKrystian Garbaciak * 48e685483SKrystian Garbaciak * Copyright 2012 Dialog Semiconductors Ltd. 58e685483SKrystian Garbaciak * Copyright 2013 Philipp Zabel, Pengutronix 68e685483SKrystian Garbaciak * 737778d83SSteve Twiss * Author: Krystian Garbaciak, Dialog Semiconductor 837778d83SSteve Twiss * Author: Michal Hajduk, Dialog Semiconductor 98e685483SKrystian Garbaciak * 108e685483SKrystian Garbaciak * This program is free software; you can redistribute it and/or modify it 118e685483SKrystian Garbaciak * under the terms of the GNU General Public License as published by the 128e685483SKrystian Garbaciak * Free Software Foundation; either version 2 of the License, or (at your 138e685483SKrystian Garbaciak * option) any later version. 148e685483SKrystian Garbaciak * 158e685483SKrystian Garbaciak */ 168e685483SKrystian Garbaciak 178e685483SKrystian Garbaciak #include <linux/kernel.h> 188e685483SKrystian Garbaciak #include <linux/module.h> 198e685483SKrystian Garbaciak #include <linux/init.h> 208e685483SKrystian Garbaciak #include <linux/slab.h> 218e685483SKrystian Garbaciak #include <linux/device.h> 228e685483SKrystian Garbaciak #include <linux/delay.h> 238e685483SKrystian Garbaciak #include <linux/interrupt.h> 248e685483SKrystian Garbaciak #include <linux/mutex.h> 258e685483SKrystian Garbaciak #include <linux/mfd/core.h> 268e685483SKrystian Garbaciak #include <linux/regmap.h> 278e685483SKrystian Garbaciak 288e685483SKrystian Garbaciak #include <linux/mfd/da9063/core.h> 298e685483SKrystian Garbaciak #include <linux/mfd/da9063/pdata.h> 308e685483SKrystian Garbaciak #include <linux/mfd/da9063/registers.h> 318e685483SKrystian Garbaciak 328e685483SKrystian Garbaciak #include <linux/proc_fs.h> 338e685483SKrystian Garbaciak #include <linux/kthread.h> 348e685483SKrystian Garbaciak #include <linux/uaccess.h> 358e685483SKrystian Garbaciak 368e685483SKrystian Garbaciak 37a0e08b86SKrystian Garbaciak static struct resource da9063_regulators_resources[] = { 38a0e08b86SKrystian Garbaciak { 39a0e08b86SKrystian Garbaciak .name = "LDO_LIM", 40a0e08b86SKrystian Garbaciak .start = DA9063_IRQ_LDO_LIM, 41a0e08b86SKrystian Garbaciak .end = DA9063_IRQ_LDO_LIM, 42a0e08b86SKrystian Garbaciak .flags = IORESOURCE_IRQ, 43a0e08b86SKrystian Garbaciak }, 44a0e08b86SKrystian Garbaciak }; 45a0e08b86SKrystian Garbaciak 46a0e08b86SKrystian Garbaciak static struct resource da9063_rtc_resources[] = { 47a0e08b86SKrystian Garbaciak { 48a0e08b86SKrystian Garbaciak .name = "ALARM", 49a0e08b86SKrystian Garbaciak .start = DA9063_IRQ_ALARM, 50a0e08b86SKrystian Garbaciak .end = DA9063_IRQ_ALARM, 51a0e08b86SKrystian Garbaciak .flags = IORESOURCE_IRQ, 52a0e08b86SKrystian Garbaciak }, 53a0e08b86SKrystian Garbaciak { 54a0e08b86SKrystian Garbaciak .name = "TICK", 55a0e08b86SKrystian Garbaciak .start = DA9063_IRQ_TICK, 56a0e08b86SKrystian Garbaciak .end = DA9063_IRQ_TICK, 57a0e08b86SKrystian Garbaciak .flags = IORESOURCE_IRQ, 58a0e08b86SKrystian Garbaciak } 59a0e08b86SKrystian Garbaciak }; 60a0e08b86SKrystian Garbaciak 61a0e08b86SKrystian Garbaciak static struct resource da9063_onkey_resources[] = { 62a0e08b86SKrystian Garbaciak { 639011e4a8SSteve Twiss .name = "ONKEY", 64a0e08b86SKrystian Garbaciak .start = DA9063_IRQ_ONKEY, 65a0e08b86SKrystian Garbaciak .end = DA9063_IRQ_ONKEY, 66a0e08b86SKrystian Garbaciak .flags = IORESOURCE_IRQ, 67a0e08b86SKrystian Garbaciak }, 68a0e08b86SKrystian Garbaciak }; 69a0e08b86SKrystian Garbaciak 70a0e08b86SKrystian Garbaciak static struct resource da9063_hwmon_resources[] = { 71a0e08b86SKrystian Garbaciak { 72a0e08b86SKrystian Garbaciak .start = DA9063_IRQ_ADC_RDY, 73a0e08b86SKrystian Garbaciak .end = DA9063_IRQ_ADC_RDY, 74a0e08b86SKrystian Garbaciak .flags = IORESOURCE_IRQ, 75a0e08b86SKrystian Garbaciak }, 76a0e08b86SKrystian Garbaciak }; 77a0e08b86SKrystian Garbaciak 78a0e08b86SKrystian Garbaciak 79c2ffec5eSMarek Vasut static const struct mfd_cell da9063_common_devs[] = { 808e685483SKrystian Garbaciak { 818e685483SKrystian Garbaciak .name = DA9063_DRVNAME_REGULATORS, 82a0e08b86SKrystian Garbaciak .num_resources = ARRAY_SIZE(da9063_regulators_resources), 83a0e08b86SKrystian Garbaciak .resources = da9063_regulators_resources, 848e685483SKrystian Garbaciak }, 858e685483SKrystian Garbaciak { 868e685483SKrystian Garbaciak .name = DA9063_DRVNAME_LEDS, 878e685483SKrystian Garbaciak }, 888e685483SKrystian Garbaciak { 898e685483SKrystian Garbaciak .name = DA9063_DRVNAME_WATCHDOG, 9071e03de4SSteve Twiss .of_compatible = "dlg,da9063-watchdog", 918e685483SKrystian Garbaciak }, 928e685483SKrystian Garbaciak { 938e685483SKrystian Garbaciak .name = DA9063_DRVNAME_HWMON, 94a0e08b86SKrystian Garbaciak .num_resources = ARRAY_SIZE(da9063_hwmon_resources), 95a0e08b86SKrystian Garbaciak .resources = da9063_hwmon_resources, 968e685483SKrystian Garbaciak }, 978e685483SKrystian Garbaciak { 988e685483SKrystian Garbaciak .name = DA9063_DRVNAME_ONKEY, 99a0e08b86SKrystian Garbaciak .num_resources = ARRAY_SIZE(da9063_onkey_resources), 100a0e08b86SKrystian Garbaciak .resources = da9063_onkey_resources, 1019011e4a8SSteve Twiss .of_compatible = "dlg,da9063-onkey", 1028e685483SKrystian Garbaciak }, 1038e685483SKrystian Garbaciak { 104c2ffec5eSMarek Vasut .name = DA9063_DRVNAME_VIBRATION, 105c2ffec5eSMarek Vasut }, 106c2ffec5eSMarek Vasut }; 107c2ffec5eSMarek Vasut 108c2ffec5eSMarek Vasut /* Only present on DA9063 , not on DA9063L */ 109c2ffec5eSMarek Vasut static const struct mfd_cell da9063_devs[] = { 110c2ffec5eSMarek Vasut { 1118e685483SKrystian Garbaciak .name = DA9063_DRVNAME_RTC, 112a0e08b86SKrystian Garbaciak .num_resources = ARRAY_SIZE(da9063_rtc_resources), 113a0e08b86SKrystian Garbaciak .resources = da9063_rtc_resources, 11471e03de4SSteve Twiss .of_compatible = "dlg,da9063-rtc", 1158e685483SKrystian Garbaciak }, 1168e685483SKrystian Garbaciak }; 1178e685483SKrystian Garbaciak 1189011e4a8SSteve Twiss static int da9063_clear_fault_log(struct da9063 *da9063) 1199011e4a8SSteve Twiss { 1209011e4a8SSteve Twiss int ret = 0; 1219011e4a8SSteve Twiss int fault_log = 0; 1229011e4a8SSteve Twiss 1239011e4a8SSteve Twiss ret = regmap_read(da9063->regmap, DA9063_REG_FAULT_LOG, &fault_log); 1249011e4a8SSteve Twiss if (ret < 0) { 1259011e4a8SSteve Twiss dev_err(da9063->dev, "Cannot read FAULT_LOG.\n"); 1269011e4a8SSteve Twiss return -EIO; 1279011e4a8SSteve Twiss } 1289011e4a8SSteve Twiss 1299011e4a8SSteve Twiss if (fault_log) { 1309011e4a8SSteve Twiss if (fault_log & DA9063_TWD_ERROR) 1319011e4a8SSteve Twiss dev_dbg(da9063->dev, 1329011e4a8SSteve Twiss "Fault log entry detected: DA9063_TWD_ERROR\n"); 1339011e4a8SSteve Twiss if (fault_log & DA9063_POR) 1349011e4a8SSteve Twiss dev_dbg(da9063->dev, 1359011e4a8SSteve Twiss "Fault log entry detected: DA9063_POR\n"); 1369011e4a8SSteve Twiss if (fault_log & DA9063_VDD_FAULT) 1379011e4a8SSteve Twiss dev_dbg(da9063->dev, 1389011e4a8SSteve Twiss "Fault log entry detected: DA9063_VDD_FAULT\n"); 1399011e4a8SSteve Twiss if (fault_log & DA9063_VDD_START) 1409011e4a8SSteve Twiss dev_dbg(da9063->dev, 1419011e4a8SSteve Twiss "Fault log entry detected: DA9063_VDD_START\n"); 1429011e4a8SSteve Twiss if (fault_log & DA9063_TEMP_CRIT) 1439011e4a8SSteve Twiss dev_dbg(da9063->dev, 1449011e4a8SSteve Twiss "Fault log entry detected: DA9063_TEMP_CRIT\n"); 1459011e4a8SSteve Twiss if (fault_log & DA9063_KEY_RESET) 1469011e4a8SSteve Twiss dev_dbg(da9063->dev, 1479011e4a8SSteve Twiss "Fault log entry detected: DA9063_KEY_RESET\n"); 1489011e4a8SSteve Twiss if (fault_log & DA9063_NSHUTDOWN) 1499011e4a8SSteve Twiss dev_dbg(da9063->dev, 1509011e4a8SSteve Twiss "Fault log entry detected: DA9063_NSHUTDOWN\n"); 1519011e4a8SSteve Twiss if (fault_log & DA9063_WAIT_SHUT) 1529011e4a8SSteve Twiss dev_dbg(da9063->dev, 1539011e4a8SSteve Twiss "Fault log entry detected: DA9063_WAIT_SHUT\n"); 1549011e4a8SSteve Twiss } 1559011e4a8SSteve Twiss 1569011e4a8SSteve Twiss ret = regmap_write(da9063->regmap, 1579011e4a8SSteve Twiss DA9063_REG_FAULT_LOG, 1589011e4a8SSteve Twiss fault_log); 1599011e4a8SSteve Twiss if (ret < 0) 1609011e4a8SSteve Twiss dev_err(da9063->dev, 1619011e4a8SSteve Twiss "Cannot reset FAULT_LOG values %d\n", ret); 1629011e4a8SSteve Twiss 1639011e4a8SSteve Twiss return ret; 1649011e4a8SSteve Twiss } 1659011e4a8SSteve Twiss 1668e685483SKrystian Garbaciak int da9063_device_init(struct da9063 *da9063, unsigned int irq) 1678e685483SKrystian Garbaciak { 1688e685483SKrystian Garbaciak struct da9063_pdata *pdata = da9063->dev->platform_data; 169c1d12c78SOpensource [Steve Twiss] int model, variant_id, variant_code; 1708e685483SKrystian Garbaciak int ret; 1718e685483SKrystian Garbaciak 1729011e4a8SSteve Twiss ret = da9063_clear_fault_log(da9063); 1739011e4a8SSteve Twiss if (ret < 0) 1749011e4a8SSteve Twiss dev_err(da9063->dev, "Cannot clear fault log\n"); 1759011e4a8SSteve Twiss 1768e685483SKrystian Garbaciak if (pdata) { 1778e685483SKrystian Garbaciak da9063->flags = pdata->flags; 1788e685483SKrystian Garbaciak da9063->irq_base = pdata->irq_base; 1798e685483SKrystian Garbaciak } else { 1808e685483SKrystian Garbaciak da9063->flags = 0; 181439b8bddSDmitry Lavnikevich da9063->irq_base = -1; 1828e685483SKrystian Garbaciak } 1838e685483SKrystian Garbaciak da9063->chip_irq = irq; 1848e685483SKrystian Garbaciak 1858e685483SKrystian Garbaciak if (pdata && pdata->init != NULL) { 1868e685483SKrystian Garbaciak ret = pdata->init(da9063); 1878e685483SKrystian Garbaciak if (ret != 0) { 1888e685483SKrystian Garbaciak dev_err(da9063->dev, 1898e685483SKrystian Garbaciak "Platform initialization failed.\n"); 1908e685483SKrystian Garbaciak return ret; 1918e685483SKrystian Garbaciak } 1928e685483SKrystian Garbaciak } 1938e685483SKrystian Garbaciak 1948e685483SKrystian Garbaciak ret = regmap_read(da9063->regmap, DA9063_REG_CHIP_ID, &model); 1958e685483SKrystian Garbaciak if (ret < 0) { 1968e685483SKrystian Garbaciak dev_err(da9063->dev, "Cannot read chip model id.\n"); 1978e685483SKrystian Garbaciak return -EIO; 1988e685483SKrystian Garbaciak } 199df7878f9SMarek Vasut if (model != PMIC_CHIP_ID_DA9063) { 2008e685483SKrystian Garbaciak dev_err(da9063->dev, "Invalid chip model id: 0x%02x\n", model); 2018e685483SKrystian Garbaciak return -ENODEV; 2028e685483SKrystian Garbaciak } 2038e685483SKrystian Garbaciak 204c1d12c78SOpensource [Steve Twiss] ret = regmap_read(da9063->regmap, DA9063_REG_CHIP_VARIANT, &variant_id); 2058e685483SKrystian Garbaciak if (ret < 0) { 206c1d12c78SOpensource [Steve Twiss] dev_err(da9063->dev, "Cannot read chip variant id.\n"); 2078e685483SKrystian Garbaciak return -EIO; 2088e685483SKrystian Garbaciak } 209c1d12c78SOpensource [Steve Twiss] 210c1d12c78SOpensource [Steve Twiss] variant_code = variant_id >> DA9063_CHIP_VARIANT_SHIFT; 211c1d12c78SOpensource [Steve Twiss] 212c1d12c78SOpensource [Steve Twiss] dev_info(da9063->dev, 213c1d12c78SOpensource [Steve Twiss] "Device detected (chip-ID: 0x%02X, var-ID: 0x%02X)\n", 214c1d12c78SOpensource [Steve Twiss] model, variant_id); 215c1d12c78SOpensource [Steve Twiss] 2169cb42e2aSOpensource [Steve Twiss] if (variant_code < PMIC_DA9063_BB && variant_code != PMIC_DA9063_AD) { 2179cb42e2aSOpensource [Steve Twiss] dev_err(da9063->dev, 2189cb42e2aSOpensource [Steve Twiss] "Cannot support variant code: 0x%02X\n", variant_code); 2198e685483SKrystian Garbaciak return -ENODEV; 2208e685483SKrystian Garbaciak } 2218e685483SKrystian Garbaciak 222c1d12c78SOpensource [Steve Twiss] da9063->variant_code = variant_code; 2238e685483SKrystian Garbaciak 224a0e08b86SKrystian Garbaciak ret = da9063_irq_init(da9063); 225a0e08b86SKrystian Garbaciak if (ret) { 226a0e08b86SKrystian Garbaciak dev_err(da9063->dev, "Cannot initialize interrupts.\n"); 227a0e08b86SKrystian Garbaciak return ret; 228a0e08b86SKrystian Garbaciak } 229a0e08b86SKrystian Garbaciak 230439b8bddSDmitry Lavnikevich da9063->irq_base = regmap_irq_chip_get_base(da9063->regmap_irq); 231439b8bddSDmitry Lavnikevich 232152bed76SMarek Vasut ret = devm_mfd_add_devices(da9063->dev, PLATFORM_DEVID_NONE, 233c2ffec5eSMarek Vasut da9063_common_devs, 234c2ffec5eSMarek Vasut ARRAY_SIZE(da9063_common_devs), 235c2ffec5eSMarek Vasut NULL, da9063->irq_base, NULL); 236c2ffec5eSMarek Vasut if (ret) { 237c2ffec5eSMarek Vasut dev_err(da9063->dev, "Failed to add child devices\n"); 238c2ffec5eSMarek Vasut return ret; 239c2ffec5eSMarek Vasut } 240c2ffec5eSMarek Vasut 241c2ffec5eSMarek Vasut if (da9063->type == PMIC_TYPE_DA9063) { 242c2ffec5eSMarek Vasut ret = devm_mfd_add_devices(da9063->dev, PLATFORM_DEVID_NONE, 243152bed76SMarek Vasut da9063_devs, ARRAY_SIZE(da9063_devs), 244152bed76SMarek Vasut NULL, da9063->irq_base, NULL); 245c2ffec5eSMarek Vasut if (ret) { 246c2ffec5eSMarek Vasut dev_err(da9063->dev, "Failed to add child devices\n"); 247c2ffec5eSMarek Vasut return ret; 248c2ffec5eSMarek Vasut } 249c2ffec5eSMarek Vasut } 2508e685483SKrystian Garbaciak 2518e685483SKrystian Garbaciak return ret; 2528e685483SKrystian Garbaciak } 2538e685483SKrystian Garbaciak 2548e685483SKrystian Garbaciak MODULE_DESCRIPTION("PMIC driver for Dialog DA9063"); 25537778d83SSteve Twiss MODULE_AUTHOR("Krystian Garbaciak"); 25637778d83SSteve Twiss MODULE_AUTHOR("Michal Hajduk"); 2578e685483SKrystian Garbaciak MODULE_LICENSE("GPL"); 258