aboutsummaryrefslogtreecommitdiff
path: root/drivers/platform/x86/intel_bytcr_bcntl.c
blob: 62be304fc000adb94c32cd6f5fabca58e8daefc1 (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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
/*
 * Baytrail CR reverse boost(5V) control
 * -- Device access for reverse boost control
 *
 * Copyright (c) 2013, Intel Corporation.
 *
 * Author: Ramakrishna Pallala <ramakrishna.pallala@intel.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; version 2
 * of the License.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/acpi.h>

#define BYT_CR_BCNTL_REG	0x5
#define BYT_CR_BCFG_REG		0xd
#define BOOST_EN_CNTL_BIT	(1 << 4)

/* No of times we should retry on -EAGAIN error */
#define NR_RETRY_CNT		3

static struct i2c_client *i2c_bcntl;

static int bcntl_write_reg8(struct i2c_client *client, u8 reg, u8 value)
{
	int ret, i;

	for (i = 0; i < NR_RETRY_CNT; i++) {
		ret = i2c_smbus_write_byte_data(client, reg, value);
		if (ret < 0)
			continue;
		else
			break;
	}
	if (ret < 0)
		dev_err(&client->dev, "I2C SMbus Write error:%d\n", ret);

	return ret;
}

static int bcntl_read_reg8(struct i2c_client *client, u8 reg)
{
	int ret, i;

	for (i = 0; i < NR_RETRY_CNT; i++) {
		ret = i2c_smbus_read_byte_data(client, reg);
		if (ret < 0)
			continue;
		else
			break;
	}
	if (ret < 0)
		dev_err(&client->dev, "I2C SMbus Read error:%d\n", ret);

	return ret;
}

int intel_bytcr_boost_enable(void)
{
	int ret, val;

	if (!i2c_bcntl)
		return -EAGAIN;

	ret = bcntl_read_reg8(i2c_bcntl, BYT_CR_BCNTL_REG);
	if (ret < 0)
		goto i2c_err;

	val = ret | BOOST_EN_CNTL_BIT;
	ret = bcntl_write_reg8(i2c_bcntl, BYT_CR_BCNTL_REG, val);

i2c_err:
	return ret;
}
EXPORT_SYMBOL(intel_bytcr_boost_enable);

int intel_bytcr_boost_disable(void)
{
	int ret, val;

	if (!i2c_bcntl)
		return -EAGAIN;

	ret = bcntl_read_reg8(i2c_bcntl, BYT_CR_BCNTL_REG);
	if (ret < 0)
		goto i2c_err;

	val = ret & ~BOOST_EN_CNTL_BIT;
	ret = bcntl_write_reg8(i2c_bcntl, BYT_CR_BCNTL_REG, val);

i2c_err:
	return ret;
}
EXPORT_SYMBOL(intel_bytcr_boost_disable);

static void init_boost_cntl_regs(void)
{
	int ret, val;

	/* configure the reverse boost control register */
	ret = bcntl_read_reg8(i2c_bcntl, BYT_CR_BCFG_REG);
	if (ret < 0)
		goto i2c_err;

	val = ret & ~BOOST_EN_CNTL_BIT;
	ret = bcntl_write_reg8(i2c_bcntl, BYT_CR_BCFG_REG, val);
	if (ret < 0)
		goto i2c_err;

	/* disable reverse boost during init */
	ret = intel_bytcr_boost_disable();
	if (ret < 0)
		goto i2c_err;

	return;
i2c_err:
	dev_err(&i2c_bcntl->dev, "bcntl init failed\n");
}

static int bcntl_i2c_probe(struct i2c_client *client,
			    const struct i2c_device_id *id)
{
	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);

	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) {
		dev_err(&client->dev,
				"SM bus doesn't support BYTE transactions\n");
		return -EIO;
	}

	i2c_bcntl = client;
	init_boost_cntl_regs();

	return 0;
}

static int bcntl_i2c_remove(struct i2c_client *i2c)
{
	return 0;
}

static void bcntl_shutdown(struct i2c_client *client)
{
	return;
}

static int bcntl_suspend(struct device *dev)
{
	return 0;
}

static int bcntl_resume(struct device *dev)
{
	return 0;
}

static int bcntl_runtime_suspend(struct device *dev)
{
	return 0;
}

static int bcntl_runtime_resume(struct device *dev)
{
	return 0;
}

static int bcntl_runtime_idle(struct device *dev)
{
	return 0;
}

static const struct dev_pm_ops bcntl_pm_ops = {
		SET_SYSTEM_SLEEP_PM_OPS(bcntl_suspend,
				bcntl_resume)
		SET_RUNTIME_PM_OPS(bcntl_runtime_suspend,
				bcntl_runtime_resume,
				bcntl_runtime_idle)
};

static const struct i2c_device_id bcntl_i2c_id[] = {
	{ "bytcr_bcntl", },
	{"INBC0000", },
	{ }
};
MODULE_DEVICE_TABLE(i2c, bcntl_i2c_id);

static const struct acpi_device_id bcntl_acpi_id[] = {
	{"INBC0000", },
	{}
};
MODULE_DEVICE_TABLE(acpi, bcntl_acpi_id);

static struct i2c_driver bcntl_i2c_driver = {
	.driver = {
		.name = "intel_bytcr_bcntl",
		.owner = THIS_MODULE,
		.pm = &bcntl_pm_ops,
		.acpi_match_table = ACPI_PTR(bcntl_acpi_id),
	},
	.probe = bcntl_i2c_probe,
	.remove = bcntl_i2c_remove,
	.shutdown = bcntl_shutdown,
	.id_table = bcntl_i2c_id,
};

static int __init bcntl_i2c_init(void)
{
	int ret;

	ret = i2c_add_driver(&bcntl_i2c_driver);
	if (ret != 0)
		pr_err("Failed to register pmic I2C driver: %d\n", ret);

	return ret;
}
subsys_initcall(bcntl_i2c_init);

static void __exit bcntl_i2c_exit(void)
{
	i2c_del_driver(&bcntl_i2c_driver);
}
module_exit(bcntl_i2c_exit);

MODULE_DESCRIPTION("Baytrail CR Reverse Boost Control");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com");