aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/i2c
diff options
context:
space:
mode:
authorDavid Box <david.e.box@linux.intel.com>2015-01-15 04:12:17 -0500
committerWolfram Sang <wsa@the-dreams.de>2015-01-26 06:26:25 -0500
commit894acb2f823b13afacfe40b02efbd9146af58586 (patch)
tree0f4b01bb796bfbceaf1764111a356581502bedcd /drivers/i2c
parentc0601d285efe063def984f91b04de2d227f89526 (diff)
i2c: designware: Add Intel Baytrail PMIC I2C bus support
This patch implements an I2C bus sharing mechanism between the host and platform hardware on select Intel BayTrail SoC platforms using the X-Powers AXP288 PMIC. On these platforms access to the PMIC must be shared with platform hardware. The hardware unit assumes full control of the I2C bus and the host must request access through a special semaphore. Hardware control of the bus also makes it necessary to disable runtime pm to avoid interfering with hardware transactions. Signed-off-by: David E. Box <david.e.box@linux.intel.com> Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/busses/Kconfig11
-rw-r--r--drivers/i2c/busses/Makefile1
-rw-r--r--drivers/i2c/busses/i2c-designware-baytrail.c160
-rw-r--r--drivers/i2c/busses/i2c-designware-core.h6
-rw-r--r--drivers/i2c/busses/i2c-designware-platdrv.c20
5 files changed, 193 insertions, 5 deletions
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 31e8308ba899..83062b2c0e6b 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -465,6 +465,17 @@ config I2C_DESIGNWARE_PCI
465 This driver can also be built as a module. If so, the module 465 This driver can also be built as a module. If so, the module
466 will be called i2c-designware-pci. 466 will be called i2c-designware-pci.
467 467
468config I2C_DESIGNWARE_BAYTRAIL
469 bool "Intel Baytrail I2C semaphore support"
470 depends on I2C_DESIGNWARE_PLATFORM
471 select IOSF_MBI
472 help
473 This driver enables managed host access to the PMIC I2C bus on select
474 Intel BayTrail platforms using the X-Powers AXP288 PMIC. It allows
475 the host to request uninterrupted access to the PMIC's I2C bus from
476 the platform firmware controlling it. You should say Y if running on
477 a BayTrail system using the AXP288.
478
468config I2C_EFM32 479config I2C_EFM32
469 tristate "EFM32 I2C controller" 480 tristate "EFM32 I2C controller"
470 depends on ARCH_EFM32 || COMPILE_TEST 481 depends on ARCH_EFM32 || COMPILE_TEST
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 56388f658d2f..a04f9726596d 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o
41obj-$(CONFIG_I2C_DESIGNWARE_CORE) += i2c-designware-core.o 41obj-$(CONFIG_I2C_DESIGNWARE_CORE) += i2c-designware-core.o
42obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o 42obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o
43i2c-designware-platform-objs := i2c-designware-platdrv.o 43i2c-designware-platform-objs := i2c-designware-platdrv.o
44i2c-designware-platform-$(CONFIG_I2C_DESIGNWARE_BAYTRAIL) += i2c-designware-baytrail.o
44obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o 45obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o
45i2c-designware-pci-objs := i2c-designware-pcidrv.o 46i2c-designware-pci-objs := i2c-designware-pcidrv.o
46obj-$(CONFIG_I2C_EFM32) += i2c-efm32.o 47obj-$(CONFIG_I2C_EFM32) += i2c-efm32.o
diff --git a/drivers/i2c/busses/i2c-designware-baytrail.c b/drivers/i2c/busses/i2c-designware-baytrail.c
new file mode 100644
index 000000000000..5f1ff4cc5c34
--- /dev/null
+++ b/drivers/i2c/busses/i2c-designware-baytrail.c
@@ -0,0 +1,160 @@
1/*
2 * Intel BayTrail PMIC I2C bus semaphore implementaion
3 * Copyright (c) 2014, Intel Corporation.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 */
14#include <linux/module.h>
15#include <linux/delay.h>
16#include <linux/device.h>
17#include <linux/acpi.h>
18#include <linux/i2c.h>
19#include <linux/interrupt.h>
20#include <asm/iosf_mbi.h>
21#include "i2c-designware-core.h"
22
23#define SEMAPHORE_TIMEOUT 100
24#define PUNIT_SEMAPHORE 0x7
25
26static unsigned long acquired;
27
28static int get_sem(struct device *dev, u32 *sem)
29{
30 u32 reg_val;
31 int ret;
32
33 ret = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ, PUNIT_SEMAPHORE,
34 &reg_val);
35 if (ret) {
36 dev_err(dev, "iosf failed to read punit semaphore\n");
37 return ret;
38 }
39
40 *sem = reg_val & 0x1;
41
42 return 0;
43}
44
45static void reset_semaphore(struct device *dev)
46{
47 u32 data;
48
49 if (iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
50 PUNIT_SEMAPHORE, &data)) {
51 dev_err(dev, "iosf failed to reset punit semaphore during read\n");
52 return;
53 }
54
55 data = data & 0xfffffffe;
56 if (iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
57 PUNIT_SEMAPHORE, data))
58 dev_err(dev, "iosf failed to reset punit semaphore during write\n");
59}
60
61int baytrail_i2c_acquire(struct dw_i2c_dev *dev)
62{
63 u32 sem = 0;
64 int ret;
65 unsigned long start, end;
66
67 if (!dev || !dev->dev)
68 return -ENODEV;
69
70 if (!dev->acquire_lock)
71 return 0;
72
73 /* host driver writes 0x2 to side band semaphore register */
74 ret = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
75 PUNIT_SEMAPHORE, 0x2);
76 if (ret) {
77 dev_err(dev->dev, "iosf punit semaphore request failed\n");
78 return ret;
79 }
80
81 /* host driver waits for bit 0 to be set in semaphore register */
82 start = jiffies;
83 end = start + msecs_to_jiffies(SEMAPHORE_TIMEOUT);
84 while (!time_after(jiffies, end)) {
85 ret = get_sem(dev->dev, &sem);
86 if (!ret && sem) {
87 acquired = jiffies;
88 dev_dbg(dev->dev, "punit semaphore acquired after %ums\n",
89 jiffies_to_msecs(jiffies - start));
90 return 0;
91 }
92
93 usleep_range(1000, 2000);
94 }
95
96 dev_err(dev->dev, "punit semaphore timed out, resetting\n");
97 reset_semaphore(dev->dev);
98
99 ret = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
100 PUNIT_SEMAPHORE, &sem);
101 if (!ret)
102 dev_err(dev->dev, "iosf failed to read punit semaphore\n");
103 else
104 dev_err(dev->dev, "PUNIT SEM: %d\n", sem);
105
106 WARN_ON(1);
107
108 return -ETIMEDOUT;
109}
110EXPORT_SYMBOL(baytrail_i2c_acquire);
111
112void baytrail_i2c_release(struct dw_i2c_dev *dev)
113{
114 if (!dev || !dev->dev)
115 return;
116
117 if (!dev->acquire_lock)
118 return;
119
120 reset_semaphore(dev->dev);
121 dev_dbg(dev->dev, "punit semaphore held for %ums\n",
122 jiffies_to_msecs(jiffies - acquired));
123}
124EXPORT_SYMBOL(baytrail_i2c_release);
125
126int i2c_dw_eval_lock_support(struct dw_i2c_dev *dev)
127{
128 acpi_status status;
129 unsigned long long shared_host = 0;
130 acpi_handle handle;
131
132 if (!dev || !dev->dev)
133 return 0;
134
135 handle = ACPI_HANDLE(dev->dev);
136 if (!handle)
137 return 0;
138
139 status = acpi_evaluate_integer(handle, "_SEM", NULL, &shared_host);
140
141 if (ACPI_FAILURE(status))
142 return 0;
143
144 if (shared_host) {
145 dev_info(dev->dev, "I2C bus managed by PUNIT\n");
146 dev->acquire_lock = baytrail_i2c_acquire;
147 dev->release_lock = baytrail_i2c_release;
148 dev->pm_runtime_disabled = true;
149 }
150
151 if (!iosf_mbi_available())
152 return -EPROBE_DEFER;
153
154 return 0;
155}
156EXPORT_SYMBOL(i2c_dw_eval_lock_support);
157
158MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
159MODULE_DESCRIPTION("Baytrail I2C Semaphore driver");
160MODULE_LICENSE("GPL v2");
diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h
index ef8ba839e2d3..9630222abf32 100644
--- a/drivers/i2c/busses/i2c-designware-core.h
+++ b/drivers/i2c/busses/i2c-designware-core.h
@@ -125,3 +125,9 @@ extern void i2c_dw_disable(struct dw_i2c_dev *dev);
125extern void i2c_dw_clear_int(struct dw_i2c_dev *dev); 125extern void i2c_dw_clear_int(struct dw_i2c_dev *dev);
126extern void i2c_dw_disable_int(struct dw_i2c_dev *dev); 126extern void i2c_dw_disable_int(struct dw_i2c_dev *dev);
127extern u32 i2c_dw_read_comp_param(struct dw_i2c_dev *dev); 127extern u32 i2c_dw_read_comp_param(struct dw_i2c_dev *dev);
128
129#if IS_ENABLED(CONFIG_I2C_DESIGNWARE_BAYTRAIL)
130extern int i2c_dw_eval_lock_support(struct dw_i2c_dev *dev);
131#else
132static inline int i2c_dw_eval_lock_support(struct dw_i2c_dev *dev) { return 0; }
133#endif
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index 2b463c313e4e..c270f5f9a8f9 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -195,6 +195,10 @@ static int dw_i2c_probe(struct platform_device *pdev)
195 clk_freq = pdata->i2c_scl_freq; 195 clk_freq = pdata->i2c_scl_freq;
196 } 196 }
197 197
198 r = i2c_dw_eval_lock_support(dev);
199 if (r)
200 return r;
201
198 dev->functionality = 202 dev->functionality =
199 I2C_FUNC_I2C | 203 I2C_FUNC_I2C |
200 I2C_FUNC_10BIT_ADDR | 204 I2C_FUNC_10BIT_ADDR |
@@ -257,10 +261,14 @@ static int dw_i2c_probe(struct platform_device *pdev)
257 return r; 261 return r;
258 } 262 }
259 263
260 pm_runtime_set_autosuspend_delay(&pdev->dev, 1000); 264 if (dev->pm_runtime_disabled) {
261 pm_runtime_use_autosuspend(&pdev->dev); 265 pm_runtime_forbid(&pdev->dev);
262 pm_runtime_set_active(&pdev->dev); 266 } else {
263 pm_runtime_enable(&pdev->dev); 267 pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);
268 pm_runtime_use_autosuspend(&pdev->dev);
269 pm_runtime_set_active(&pdev->dev);
270 pm_runtime_enable(&pdev->dev);
271 }
264 272
265 return 0; 273 return 0;
266} 274}
@@ -310,7 +318,9 @@ static int dw_i2c_resume(struct device *dev)
310 struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev); 318 struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev);
311 319
312 clk_prepare_enable(i_dev->clk); 320 clk_prepare_enable(i_dev->clk);
313 i2c_dw_init(i_dev); 321
322 if (!i_dev->pm_runtime_disabled)
323 i2c_dw_init(i_dev);
314 324
315 return 0; 325 return 0;
316} 326}