summaryrefslogtreecommitdiff
path: root/drivers/mfd/lp87565.c
blob: 88ce4d7c50a71c4007a407c340bb0b50aa6a2485 (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
128
129
130
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2017 Texas Instruments Incorporated - https://www.ti.com/
 *
 * Author: Keerthy <j-keerthy@ti.com>
 */

#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regmap.h>

#include <linux/mfd/lp87565.h>

static const struct regmap_config lp87565_regmap_config = {
	.reg_bits = 8,
	.val_bits = 8,
	.max_register = LP87565_REG_MAX,
};

static const struct mfd_cell lp87565_cells[] = {
	{ .name = "lp87565-q1-regulator", },
	{ .name = "lp87565-q1-gpio", },
};

static const struct of_device_id of_lp87565_match_table[] = {
	{ .compatible = "ti,lp87565", },
	{
		.compatible = "ti,lp87524-q1",
		.data = (void *)LP87565_DEVICE_TYPE_LP87524_Q1,
	},
	{
		.compatible = "ti,lp87565-q1",
		.data = (void *)LP87565_DEVICE_TYPE_LP87565_Q1,
	},
	{
		.compatible = "ti,lp87561-q1",
		.data = (void *)LP87565_DEVICE_TYPE_LP87561_Q1,
	},
	{}
};
MODULE_DEVICE_TABLE(of, of_lp87565_match_table);

static int lp87565_probe(struct i2c_client *client)
{
	struct lp87565 *lp87565;
	const struct of_device_id *of_id;
	int ret;
	unsigned int otpid;

	lp87565 = devm_kzalloc(&client->dev, sizeof(*lp87565), GFP_KERNEL);
	if (!lp87565)
		return -ENOMEM;

	lp87565->dev = &client->dev;

	lp87565->regmap = devm_regmap_init_i2c(client, &lp87565_regmap_config);
	if (IS_ERR(lp87565->regmap)) {
		ret = PTR_ERR(lp87565->regmap);
		dev_err(lp87565->dev,
			"Failed to initialize register map: %d\n", ret);
		return ret;
	}

	lp87565->reset_gpio = devm_gpiod_get_optional(lp87565->dev, "reset",
						      GPIOD_OUT_LOW);
	if (IS_ERR(lp87565->reset_gpio)) {
		ret = PTR_ERR(lp87565->reset_gpio);
		if (ret == -EPROBE_DEFER)
			return ret;
	}

	if (lp87565->reset_gpio) {
		gpiod_set_value_cansleep(lp87565->reset_gpio, 1);
		/* The minimum assertion time is undocumented, just guess */
		usleep_range(2000, 4000);

		gpiod_set_value_cansleep(lp87565->reset_gpio, 0);
		/* Min 1.2 ms before first I2C transaction */
		usleep_range(1500, 3000);
	}

	ret = regmap_read(lp87565->regmap, LP87565_REG_OTP_REV, &otpid);
	if (ret) {
		dev_err(lp87565->dev, "Failed to read OTP ID\n");
		return ret;
	}

	lp87565->rev = otpid & LP87565_OTP_REV_OTP_ID;

	of_id = of_match_device(of_lp87565_match_table, &client->dev);
	if (of_id)
		lp87565->dev_type = (enum lp87565_device_type)of_id->data;

	i2c_set_clientdata(client, lp87565);

	return devm_mfd_add_devices(lp87565->dev, PLATFORM_DEVID_AUTO,
				    lp87565_cells, ARRAY_SIZE(lp87565_cells),
				    NULL, 0, NULL);
}

static void lp87565_shutdown(struct i2c_client *client)
{
	struct lp87565 *lp87565 = i2c_get_clientdata(client);

	gpiod_set_value_cansleep(lp87565->reset_gpio, 1);
}

static const struct i2c_device_id lp87565_id_table[] = {
	{ "lp87565-q1", 0 },
	{ },
};
MODULE_DEVICE_TABLE(i2c, lp87565_id_table);

static struct i2c_driver lp87565_driver = {
	.driver	= {
		.name	= "lp87565",
		.of_match_table = of_lp87565_match_table,
	},
	.probe = lp87565_probe,
	.shutdown = lp87565_shutdown,
	.id_table = lp87565_id_table,
};
module_i2c_driver(lp87565_driver);

MODULE_AUTHOR("J Keerthy <j-keerthy@ti.com>");
MODULE_DESCRIPTION("lp87565 chip family Multi-Function Device driver");
MODULE_LICENSE("GPL v2");