1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Device access for Dialog DA9063 modules
4 *
5 * Copyright 2012 Dialog Semiconductors Ltd.
6 * Copyright 2013 Philipp Zabel, Pengutronix
7 *
8 * Author: Krystian Garbaciak, Dialog Semiconductor
9 * Author: Michal Hajduk, Dialog Semiconductor
10 *
11 */
12
13#include <linux/kernel.h>
14#include <linux/module.h>
15#include <linux/init.h>
16#include <linux/slab.h>
17#include <linux/device.h>
18#include <linux/delay.h>
19#include <linux/interrupt.h>
20#include <linux/mutex.h>
21#include <linux/mfd/core.h>
22#include <linux/regmap.h>
23
24#include <linux/mfd/da9063/core.h>
25#include <linux/mfd/da9063/registers.h>
26
27#include <linux/proc_fs.h>
28#include <linux/kthread.h>
29#include <linux/uaccess.h>
30
31
32static const struct resource da9063_regulators_resources[] = {
33	{
34		.name	= "LDO_LIM",
35		.start	= DA9063_IRQ_LDO_LIM,
36		.end	= DA9063_IRQ_LDO_LIM,
37		.flags	= IORESOURCE_IRQ,
38	},
39};
40
41static const struct resource da9063_rtc_resources[] = {
42	{
43		.name	= "ALARM",
44		.start	= DA9063_IRQ_ALARM,
45		.end	= DA9063_IRQ_ALARM,
46		.flags	= IORESOURCE_IRQ,
47	},
48	{
49		.name	= "TICK",
50		.start	= DA9063_IRQ_TICK,
51		.end	= DA9063_IRQ_TICK,
52		.flags	= IORESOURCE_IRQ,
53	}
54};
55
56static const struct resource da9063_onkey_resources[] = {
57	{
58		.name	= "ONKEY",
59		.start	= DA9063_IRQ_ONKEY,
60		.end	= DA9063_IRQ_ONKEY,
61		.flags	= IORESOURCE_IRQ,
62	},
63};
64
65static const struct resource da9063_hwmon_resources[] = {
66	{
67		.start	= DA9063_IRQ_ADC_RDY,
68		.end	= DA9063_IRQ_ADC_RDY,
69		.flags	= IORESOURCE_IRQ,
70	},
71};
72
73
74static const struct mfd_cell da9063_common_devs[] = {
75	{
76		.name		= DA9063_DRVNAME_REGULATORS,
77		.num_resources	= ARRAY_SIZE(da9063_regulators_resources),
78		.resources	= da9063_regulators_resources,
79	},
80	{
81		.name		= DA9063_DRVNAME_LEDS,
82	},
83	{
84		.name		= DA9063_DRVNAME_WATCHDOG,
85		.of_compatible	= "dlg,da9063-watchdog",
86	},
87	{
88		.name		= DA9063_DRVNAME_HWMON,
89		.num_resources	= ARRAY_SIZE(da9063_hwmon_resources),
90		.resources	= da9063_hwmon_resources,
91	},
92	{
93		.name		= DA9063_DRVNAME_ONKEY,
94		.num_resources	= ARRAY_SIZE(da9063_onkey_resources),
95		.resources	= da9063_onkey_resources,
96		.of_compatible = "dlg,da9063-onkey",
97	},
98	{
99		.name		= DA9063_DRVNAME_VIBRATION,
100	},
101};
102
103/* Only present on DA9063 , not on DA9063L */
104static const struct mfd_cell da9063_devs[] = {
105	{
106		.name		= DA9063_DRVNAME_RTC,
107		.num_resources	= ARRAY_SIZE(da9063_rtc_resources),
108		.resources	= da9063_rtc_resources,
109		.of_compatible	= "dlg,da9063-rtc",
110	},
111};
112
113static int da9063_clear_fault_log(struct da9063 *da9063)
114{
115	int ret = 0;
116	int fault_log = 0;
117
118	ret = regmap_read(da9063->regmap, DA9063_REG_FAULT_LOG, &fault_log);
119	if (ret < 0) {
120		dev_err(da9063->dev, "Cannot read FAULT_LOG.\n");
121		return -EIO;
122	}
123
124	if (fault_log) {
125		if (fault_log & DA9063_TWD_ERROR)
126			dev_dbg(da9063->dev,
127				"Fault log entry detected: DA9063_TWD_ERROR\n");
128		if (fault_log & DA9063_POR)
129			dev_dbg(da9063->dev,
130				"Fault log entry detected: DA9063_POR\n");
131		if (fault_log & DA9063_VDD_FAULT)
132			dev_dbg(da9063->dev,
133				"Fault log entry detected: DA9063_VDD_FAULT\n");
134		if (fault_log & DA9063_VDD_START)
135			dev_dbg(da9063->dev,
136				"Fault log entry detected: DA9063_VDD_START\n");
137		if (fault_log & DA9063_TEMP_CRIT)
138			dev_dbg(da9063->dev,
139				"Fault log entry detected: DA9063_TEMP_CRIT\n");
140		if (fault_log & DA9063_KEY_RESET)
141			dev_dbg(da9063->dev,
142				"Fault log entry detected: DA9063_KEY_RESET\n");
143		if (fault_log & DA9063_NSHUTDOWN)
144			dev_dbg(da9063->dev,
145				"Fault log entry detected: DA9063_NSHUTDOWN\n");
146		if (fault_log & DA9063_WAIT_SHUT)
147			dev_dbg(da9063->dev,
148				"Fault log entry detected: DA9063_WAIT_SHUT\n");
149	}
150
151	ret = regmap_write(da9063->regmap,
152			   DA9063_REG_FAULT_LOG,
153			   fault_log);
154	if (ret < 0)
155		dev_err(da9063->dev,
156			"Cannot reset FAULT_LOG values %d\n", ret);
157
158	return ret;
159}
160
161int da9063_device_init(struct da9063 *da9063, unsigned int irq)
162{
163	int ret;
164
165	ret = da9063_clear_fault_log(da9063);
166	if (ret < 0)
167		dev_err(da9063->dev, "Cannot clear fault log\n");
168
169	da9063->flags = 0;
170	da9063->irq_base = -1;
171	da9063->chip_irq = irq;
172
173	ret = da9063_irq_init(da9063);
174	if (ret) {
175		dev_err(da9063->dev, "Cannot initialize interrupts.\n");
176		return ret;
177	}
178
179	da9063->irq_base = regmap_irq_chip_get_base(da9063->regmap_irq);
180
181	ret = devm_mfd_add_devices(da9063->dev, PLATFORM_DEVID_NONE,
182				   da9063_common_devs,
183				   ARRAY_SIZE(da9063_common_devs),
184				   NULL, da9063->irq_base, NULL);
185	if (ret) {
186		dev_err(da9063->dev, "Failed to add child devices\n");
187		return ret;
188	}
189
190	if (da9063->type == PMIC_TYPE_DA9063) {
191		ret = devm_mfd_add_devices(da9063->dev, PLATFORM_DEVID_NONE,
192					   da9063_devs, ARRAY_SIZE(da9063_devs),
193					   NULL, da9063->irq_base, NULL);
194		if (ret) {
195			dev_err(da9063->dev, "Failed to add child devices\n");
196			return ret;
197		}
198	}
199
200	return ret;
201}
202
203MODULE_DESCRIPTION("PMIC driver for Dialog DA9063");
204MODULE_AUTHOR("Krystian Garbaciak");
205MODULE_AUTHOR("Michal Hajduk");
206MODULE_LICENSE("GPL");
207