summaryrefslogtreecommitdiffstats
path: root/drivers/mfd/pca9450.c
blob: 8fa5363f8a60e4208b07dfce4da3a76c42529f77 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2023 Holger Assmann, Pengutronix
 */

#include <common.h>
#include <driver.h>
#include <errno.h>
#include <i2c/i2c.h>
#include <init.h>
#include <mfd/pca9450.h>
#include <of.h>
#include <regmap.h>
#include <reset_source.h>

#define REASON_PMIC_RST		0x10
#define REASON_SW_RST		0x20
#define REASON_WDOG		0x40
#define REASON_PWON		0x80

static const struct regmap_config pca9450_regmap_i2c_config = {
	.reg_bits = 8,
	.val_bits = 8,
	.max_register = 0x2E,
};

static int pca9450_get_reset_source(struct device *dev, struct regmap *map)
{
	enum reset_src_type type;
	int reg;
	int ret;

	ret = regmap_read(map, PCA9450_PWRON_STAT, &reg);
	if (ret)
		return ret;

	switch (reg) {
	case REASON_PWON:
		dev_dbg(dev, "Power ON triggered by PMIC_ON_REQ.\n");
		type = RESET_POR;
		break;
	case REASON_WDOG:
		dev_dbg(dev, "Detected cold reset by WDOGB pin\n");
		type = RESET_WDG;
		break;
	case REASON_SW_RST:
		dev_dbg(dev, "Detected cold reset by SW_RST\n");
		type = RESET_RST;
		break;
	case REASON_PMIC_RST:
		dev_dbg(dev, "Detected cold reset by PMIC_RST_B\n");
		type = RESET_EXT;
		break;
	default:
		dev_warn(dev, "Could not determine reset reason.\n");
		type = RESET_UKWN;
	}

	reset_source_set_device(dev, type);

	return 0;
};

static struct regmap *pca9450_map;

static void (*pca9450_init_callback)(struct regmap *map);

int pca9450_register_init_callback(void(*callback)(struct regmap *map))
{
	if (pca9450_init_callback)
		return -EBUSY;

	pca9450_init_callback = callback;

	if (pca9450_map)
		pca9450_init_callback(pca9450_map);

	return 0;
}

static int __init pca9450_probe(struct device *dev)
{
	struct regmap *regmap;
	int reg;
	int ret;

	regmap = regmap_init_i2c(to_i2c_client(dev), &pca9450_regmap_i2c_config);
	if (IS_ERR(regmap))
		return PTR_ERR(regmap);

	ret = regmap_register_cdev(regmap, NULL);
	if (ret)
		return ret;

	ret = regmap_read(regmap, PCA9450_REG_DEV_ID, &reg);
	if (ret) {
		dev_err(dev, "Unable to read PMIC Chip ID\n");
		return ret;
	}

	/* Chip ID defined in bits [7:4] */
	dev_info(dev, "PMIC Chip ID: 0x%x\n", (reg >> 4));

	if (pca9450_init_callback)
		pca9450_init_callback(regmap);
	pca9450_map = regmap;

	pca9450_get_reset_source(dev,regmap);

	return of_platform_populate(dev->of_node, NULL, dev);
}

static __maybe_unused struct of_device_id pca9450_dt_ids[] = {
	{ .compatible = "nxp,pca9450a" },
	{ .compatible = "nxp,pca9450c" },
	{ .compatible = "nxp,pca9451a" },
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, pca9450_dt_ids);

static struct driver pca9450_i2c_driver = {
	.name		= "pca9450-i2c",
	.probe		= pca9450_probe,
	.of_compatible	= DRV_OF_COMPAT(pca9450_dt_ids),
};

coredevice_i2c_driver(pca9450_i2c_driver);