add untracked patches
This commit is contained in:
parent
0e84f13bb3
commit
0557a6fc2d
148
0001-ACPI-temporary-dep-solution-for-battery-support.patch
Normal file
148
0001-ACPI-temporary-dep-solution-for-battery-support.patch
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
From bbadbc67de278123e28dd6f9ee7e88b6ada56ce4 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Lan Tianyu <tianyu.lan@intel.com>
|
||||||
|
Date: Fri, 21 Mar 2014 16:42:12 +0800
|
||||||
|
Subject: [PATCH] ACPI: temporary dep solution for battery support
|
||||||
|
|
||||||
|
This is a dep workaround for battery support on Asus T100TA and the formal
|
||||||
|
dep solution is under developing. This patch is just for test and will
|
||||||
|
not be upstreamed.
|
||||||
|
---
|
||||||
|
drivers/acpi/scan.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++---
|
||||||
|
drivers/i2c/i2c-acpi.c | 1 +
|
||||||
|
include/linux/acpi.h | 1 +
|
||||||
|
3 files changed, 63 insertions(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
|
||||||
|
index 7efe546..254afb7 100644
|
||||||
|
--- a/drivers/acpi/scan.c
|
||||||
|
+++ b/drivers/acpi/scan.c
|
||||||
|
@@ -36,6 +36,7 @@ bool acpi_force_hot_remove;
|
||||||
|
|
||||||
|
static const char *dummy_hid = "device";
|
||||||
|
|
||||||
|
+static LIST_HEAD(acpi_bus_dep_device_list);
|
||||||
|
static LIST_HEAD(acpi_bus_id_list);
|
||||||
|
static DEFINE_MUTEX(acpi_scan_lock);
|
||||||
|
static LIST_HEAD(acpi_scan_handlers_list);
|
||||||
|
@@ -43,6 +44,12 @@ DEFINE_MUTEX(acpi_device_lock);
|
||||||
|
LIST_HEAD(acpi_wakeup_device_list);
|
||||||
|
static DEFINE_MUTEX(acpi_hp_context_lock);
|
||||||
|
|
||||||
|
+
|
||||||
|
+struct acpi_dep_handle {
|
||||||
|
+ struct list_head node;
|
||||||
|
+ acpi_handle handle;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
struct acpi_device_bus_id{
|
||||||
|
char bus_id[15];
|
||||||
|
unsigned int instance_no;
|
||||||
|
@@ -2027,10 +2034,22 @@ static void acpi_scan_init_hotplug(struct acpi_device *adev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+
|
||||||
|
+static int acpi_dep_device_check(acpi_handle handle)
|
||||||
|
+{
|
||||||
|
+ struct acpi_dep_handle *dep;
|
||||||
|
+
|
||||||
|
+ list_for_each_entry(dep, &acpi_bus_dep_device_list, node)
|
||||||
|
+ if (dep->handle == handle)
|
||||||
|
+ return -EEXIST;
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
|
||||||
|
void *not_used, void **return_value)
|
||||||
|
{
|
||||||
|
struct acpi_device *device = NULL;
|
||||||
|
+ struct acpi_dep_handle *dep = NULL;
|
||||||
|
int type;
|
||||||
|
unsigned long long sta;
|
||||||
|
int result;
|
||||||
|
@@ -2048,9 +2067,24 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
|
||||||
|
return AE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
- acpi_add_single_object(&device, handle, type, sta);
|
||||||
|
- if (!device)
|
||||||
|
- return AE_CTRL_DEPTH;
|
||||||
|
+ if (!acpi_dep_device_check(handle)
|
||||||
|
+ && acpi_has_method(handle, "_BIX")
|
||||||
|
+ && acpi_has_method(handle, "_DEP")) {
|
||||||
|
+ dep = kmalloc(sizeof(struct acpi_dep_handle), GFP_KERNEL);
|
||||||
|
+ if (!dep)
|
||||||
|
+ return AE_CTRL_DEPTH;
|
||||||
|
+ dep->handle = handle;
|
||||||
|
+ list_add_tail(&dep->node , &acpi_bus_dep_device_list);
|
||||||
|
+
|
||||||
|
+ acpi_handle_info(dep->handle,
|
||||||
|
+ "is added to dep device list.\n");
|
||||||
|
+
|
||||||
|
+ return AE_OK;
|
||||||
|
+ } else {
|
||||||
|
+ acpi_add_single_object(&device, handle, type, sta);
|
||||||
|
+ if (!device)
|
||||||
|
+ return AE_CTRL_DEPTH;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
acpi_scan_init_hotplug(device);
|
||||||
|
|
||||||
|
@@ -2061,6 +2095,30 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
|
||||||
|
return AE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
+int acpi_walk_dep_device_list(void)
|
||||||
|
+{
|
||||||
|
+ struct acpi_dep_handle *dep, *tmp;
|
||||||
|
+ acpi_status status;
|
||||||
|
+ unsigned long long sta;
|
||||||
|
+
|
||||||
|
+ list_for_each_entry_safe(dep, tmp, &acpi_bus_dep_device_list, node) {
|
||||||
|
+ status = acpi_evaluate_integer(dep->handle, "_STA", NULL, &sta);
|
||||||
|
+
|
||||||
|
+ if (ACPI_FAILURE(status)) {
|
||||||
|
+ acpi_handle_warn(dep->handle,
|
||||||
|
+ "Status check failed (0x%x)\n", status);
|
||||||
|
+ } else if (sta & ACPI_STA_DEVICE_ENABLED) {
|
||||||
|
+ acpi_bus_scan(dep->handle);
|
||||||
|
+ acpi_handle_info(dep->handle,
|
||||||
|
+ "Device is readly\n");
|
||||||
|
+ list_del(&dep->node);
|
||||||
|
+ kfree(dep);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+EXPORT_SYMBOL_GPL(acpi_walk_dep_device_list);
|
||||||
|
+
|
||||||
|
static int acpi_scan_attach_handler(struct acpi_device *device)
|
||||||
|
{
|
||||||
|
struct acpi_hardware_id *hwid;
|
||||||
|
diff --git a/drivers/i2c/i2c-acpi.c b/drivers/i2c/i2c-acpi.c
|
||||||
|
index a0ae867..471490a 100644
|
||||||
|
--- a/drivers/i2c/i2c-acpi.c
|
||||||
|
+++ b/drivers/i2c/i2c-acpi.c
|
||||||
|
@@ -349,6 +349,7 @@ int acpi_i2c_install_space_handler(struct i2c_adapter *adapter)
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ acpi_walk_dep_device_list();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
|
||||||
|
index 667204c..66ad0dd 100644
|
||||||
|
--- a/include/linux/acpi.h
|
||||||
|
+++ b/include/linux/acpi.h
|
||||||
|
@@ -115,6 +115,7 @@ int acpi_boot_init (void);
|
||||||
|
void acpi_boot_table_init (void);
|
||||||
|
int acpi_mps_check (void);
|
||||||
|
int acpi_numa_init (void);
|
||||||
|
+int acpi_walk_dep_device_list(void);
|
||||||
|
|
||||||
|
int acpi_table_init (void);
|
||||||
|
int acpi_table_parse(char *id, acpi_tbl_table_handler handler);
|
||||||
|
--
|
||||||
|
1.8.3.1
|
||||||
|
|
@ -0,0 +1,24 @@
|
|||||||
|
From 69968772071bb8632a57f42ec77a3acdddbe74d7 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Adam Williamson <awilliam@redhat.com>
|
||||||
|
Date: Mon, 14 Apr 2014 16:05:32 -0700
|
||||||
|
Subject: [PATCH] add device ID for Dell Venue 8 Pro's wireless adapter
|
||||||
|
|
||||||
|
---
|
||||||
|
drivers/net/wireless/ath/ath6kl/sdio.c | 1 +
|
||||||
|
1 file changed, 1 insertion(+)
|
||||||
|
|
||||||
|
diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c
|
||||||
|
index 7126bdd..f403ebb 100644
|
||||||
|
--- a/drivers/net/wireless/ath/ath6kl/sdio.c
|
||||||
|
+++ b/drivers/net/wireless/ath/ath6kl/sdio.c
|
||||||
|
@@ -1403,6 +1403,7 @@ static const struct sdio_device_id ath6kl_sdio_devices[] = {
|
||||||
|
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x1))},
|
||||||
|
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x0))},
|
||||||
|
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x1))},
|
||||||
|
+ {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x18))},
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
|
||||||
|
--
|
||||||
|
1.9.0
|
||||||
|
|
461
V3-4-5-I2C-ACPI-Add-i2c-ACPI-operation-region-support.patch
Normal file
461
V3-4-5-I2C-ACPI-Add-i2c-ACPI-operation-region-support.patch
Normal file
@ -0,0 +1,461 @@
|
|||||||
|
From patchwork Tue May 20 12:59:23 2014
|
||||||
|
Content-Type: text/plain; charset="utf-8"
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Transfer-Encoding: 7bit
|
||||||
|
Subject: [V3,4/5] I2C/ACPI: Add i2c ACPI operation region support
|
||||||
|
From: "lan,Tianyu" <tianyu.lan@intel.com>
|
||||||
|
X-Patchwork-Id: 4209471
|
||||||
|
Message-Id: <1400590764-11108-5-git-send-email-tianyu.lan@intel.com>
|
||||||
|
To: wsa@the-dreams.de, rjw@rjwysocki.net,
|
||||||
|
mika.westerberg@linux.intel.com, awilliam@redhat.com, lenb@kernel.org
|
||||||
|
Cc: Lan Tianyu <tianyu.lan@intel.com>, linux-i2c@vger.kernel.org,
|
||||||
|
linux-kernel@vger.kernel.org, linux-acpi@vger.kernel.org
|
||||||
|
Date: Tue, 20 May 2014 20:59:23 +0800
|
||||||
|
|
||||||
|
ACPI 5.0 spec(5.5.2.4.5) defines GenericSerialBus(i2c, spi, uart) operation region.
|
||||||
|
It allows ACPI aml code able to access such kind of devices to implement
|
||||||
|
some ACPI standard method.
|
||||||
|
|
||||||
|
ACPI Spec defines some access attribute to associate with i2c protocol.
|
||||||
|
AttribQuick Read/Write Quick Protocol
|
||||||
|
AttribSendReceive Send/Receive Byte Protocol
|
||||||
|
AttribByte Read/Write Byte Protocol
|
||||||
|
AttribWord Read/Write Word Protocol
|
||||||
|
AttribBlock Read/Write Block Protocol
|
||||||
|
AttribBytes Read/Write N-Bytes Protocol
|
||||||
|
AttribProcessCall Process Call Protocol
|
||||||
|
AttribBlockProcessCall Write Block-Read Block Process Call Protocol
|
||||||
|
AttribRawBytes Raw Read/Write N-BytesProtocol
|
||||||
|
AttribRawProcessBytes Raw Process Call Protocol
|
||||||
|
|
||||||
|
On the Asus T100TA, Bios use GenericSerialBus operation region to access
|
||||||
|
i2c device to get battery info.
|
||||||
|
|
||||||
|
Sample code From Asus T100TA
|
||||||
|
|
||||||
|
Scope (_SB.I2C1)
|
||||||
|
{
|
||||||
|
Name (UMPC, ResourceTemplate ()
|
||||||
|
{
|
||||||
|
I2cSerialBus (0x0066, ControllerInitiated, 0x00061A80,
|
||||||
|
AddressingMode7Bit, "\\_SB.I2C1",
|
||||||
|
0x00, ResourceConsumer, ,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
OperationRegion (DVUM, GenericSerialBus, Zero, 0x0100)
|
||||||
|
Field (DVUM, BufferAcc, NoLock, Preserve)
|
||||||
|
{
|
||||||
|
Connection (UMPC),
|
||||||
|
Offset (0x81),
|
||||||
|
AccessAs (BufferAcc, AttribBytes (0x3E)),
|
||||||
|
FGC0, 8
|
||||||
|
}
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
Device (BATC)
|
||||||
|
{
|
||||||
|
Name (_HID, EisaId ("PNP0C0A")) // _HID: Hardware ID
|
||||||
|
Name (_UID, One) // _UID: Unique ID
|
||||||
|
...
|
||||||
|
|
||||||
|
Method (_BST, 0, NotSerialized) // _BST: Battery Status
|
||||||
|
{
|
||||||
|
If (LEqual (AVBL, One))
|
||||||
|
{
|
||||||
|
Store (FGC0, BFFG)
|
||||||
|
If (LNotEqual (STAT, One))
|
||||||
|
{
|
||||||
|
ShiftRight (CHST, 0x04, Local0)
|
||||||
|
And (Local0, 0x03, Local0)
|
||||||
|
If (LOr (LEqual (Local0, One), LEqual (Local0, 0x02)))
|
||||||
|
{
|
||||||
|
Store (0x02, Local1)
|
||||||
|
}
|
||||||
|
...
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
The i2c operation region is defined under I2C1 scope. _BST method under
|
||||||
|
battery device BATC read battery status from the field "FCG0". The request
|
||||||
|
would be sent to i2c operation region handler.
|
||||||
|
|
||||||
|
This patch is to add i2c ACPI operation region support. Due to there are
|
||||||
|
only "Byte" and "Bytes" protocol access on the Asus T100TA, other protocols
|
||||||
|
have not been tested.
|
||||||
|
|
||||||
|
About RawBytes and RawProcessBytes protocol, they needs specific drivers to interpret
|
||||||
|
reference data from AML code according ACPI 5.0 SPEC(5.5.2.4.5.3.9 and 5.5.2.4.5.3.10).
|
||||||
|
So far, not found such case and will add when find real case.
|
||||||
|
|
||||||
|
Signed-off-by: Lan Tianyu <tianyu.lan@intel.com>
|
||||||
|
|
||||||
|
---
|
||||||
|
drivers/i2c/Makefile | 5 +-
|
||||||
|
drivers/i2c/i2c-acpi.c | 273 +++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
drivers/i2c/i2c-core.c | 2 +
|
||||||
|
include/linux/acpi.h | 11 ++
|
||||||
|
include/linux/i2c.h | 10 ++
|
||||||
|
5 files changed, 300 insertions(+), 1 deletion(-)
|
||||||
|
create mode 100644 drivers/i2c/i2c-acpi.c
|
||||||
|
|
||||||
|
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
|
||||||
|
index 1722f50..80db307 100644
|
||||||
|
--- a/drivers/i2c/Makefile
|
||||||
|
+++ b/drivers/i2c/Makefile
|
||||||
|
@@ -2,8 +2,11 @@
|
||||||
|
# Makefile for the i2c core.
|
||||||
|
#
|
||||||
|
|
||||||
|
+i2ccore-y := i2c-core.o
|
||||||
|
+i2ccore-$(CONFIG_ACPI) += i2c-acpi.o
|
||||||
|
+
|
||||||
|
obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o
|
||||||
|
-obj-$(CONFIG_I2C) += i2c-core.o
|
||||||
|
+obj-$(CONFIG_I2C) += i2ccore.o
|
||||||
|
obj-$(CONFIG_I2C_SMBUS) += i2c-smbus.o
|
||||||
|
obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
|
||||||
|
obj-$(CONFIG_I2C_MUX) += i2c-mux.o
|
||||||
|
diff --git a/drivers/i2c/i2c-acpi.c b/drivers/i2c/i2c-acpi.c
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..f7f4c89
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/drivers/i2c/i2c-acpi.c
|
||||||
|
@@ -0,0 +1,273 @@
|
||||||
|
+/*
|
||||||
|
+ * I2C ACPI code
|
||||||
|
+ *
|
||||||
|
+ * Copyright (C) 2014 Intel Corp
|
||||||
|
+ *
|
||||||
|
+ * Author: Lan Tianyu <tianyu.lan@intel.com>
|
||||||
|
+ *
|
||||||
|
+ * This program is free software; you can redistribute it and/or modify
|
||||||
|
+ * it under the terms of the GNU General Public License version 2 as
|
||||||
|
+ * published by the Free Software Foundation.
|
||||||
|
+ *
|
||||||
|
+ * This program is distributed in the hope that it will be useful, but
|
||||||
|
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
+ * for more details.
|
||||||
|
+ */
|
||||||
|
+#define pr_fmt(fmt) "I2C/ACPI : " fmt
|
||||||
|
+
|
||||||
|
+#include <linux/kernel.h>
|
||||||
|
+#include <linux/errno.h>
|
||||||
|
+#include <linux/err.h>
|
||||||
|
+#include <linux/i2c.h>
|
||||||
|
+#include <linux/acpi.h>
|
||||||
|
+
|
||||||
|
+struct acpi_i2c_handler_data {
|
||||||
|
+ struct acpi_connection_info info;
|
||||||
|
+ struct i2c_adapter *adapter;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+struct gsb_buffer {
|
||||||
|
+ u8 status;
|
||||||
|
+ u8 len;
|
||||||
|
+ union {
|
||||||
|
+ u16 wdata;
|
||||||
|
+ u8 bdata;
|
||||||
|
+ u8 data[0];
|
||||||
|
+ };
|
||||||
|
+} __packed;
|
||||||
|
+
|
||||||
|
+static int acpi_gsb_i2c_read_bytes(struct i2c_client *client,
|
||||||
|
+ u8 cmd, u8 *data, u8 data_len)
|
||||||
|
+{
|
||||||
|
+
|
||||||
|
+ struct i2c_msg msgs[2];
|
||||||
|
+ int ret;
|
||||||
|
+ u8 *buffer;
|
||||||
|
+
|
||||||
|
+ buffer = kzalloc(data_len, GFP_KERNEL);
|
||||||
|
+ if (!buffer)
|
||||||
|
+ return AE_NO_MEMORY;
|
||||||
|
+
|
||||||
|
+ msgs[0].addr = client->addr;
|
||||||
|
+ msgs[0].flags = client->flags;
|
||||||
|
+ msgs[0].len = 1;
|
||||||
|
+ msgs[0].buf = &cmd;
|
||||||
|
+
|
||||||
|
+ msgs[1].addr = client->addr;
|
||||||
|
+ msgs[1].flags = client->flags | I2C_M_RD;
|
||||||
|
+ msgs[1].len = data_len;
|
||||||
|
+ msgs[1].buf = buffer;
|
||||||
|
+
|
||||||
|
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
|
||||||
|
+ if (ret < 0)
|
||||||
|
+ dev_err(&client->adapter->dev, "i2c read failed\n");
|
||||||
|
+ else
|
||||||
|
+ memcpy(data, buffer, data_len);
|
||||||
|
+
|
||||||
|
+ kfree(buffer);
|
||||||
|
+ return ret;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int acpi_gsb_i2c_write_bytes(struct i2c_client *client,
|
||||||
|
+ u8 cmd, u8 *data, u8 data_len)
|
||||||
|
+{
|
||||||
|
+
|
||||||
|
+ struct i2c_msg msgs[1];
|
||||||
|
+ u8 *buffer;
|
||||||
|
+ int ret = AE_OK;
|
||||||
|
+
|
||||||
|
+ buffer = kzalloc(data_len + 1, GFP_KERNEL);
|
||||||
|
+ if (!buffer)
|
||||||
|
+ return AE_NO_MEMORY;
|
||||||
|
+
|
||||||
|
+ buffer[0] = cmd;
|
||||||
|
+ memcpy(buffer + 1, data, data_len);
|
||||||
|
+
|
||||||
|
+ msgs[0].addr = client->addr;
|
||||||
|
+ msgs[0].flags = client->flags;
|
||||||
|
+ msgs[0].len = data_len + 1;
|
||||||
|
+ msgs[0].buf = buffer;
|
||||||
|
+
|
||||||
|
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
|
||||||
|
+ if (ret < 0)
|
||||||
|
+ dev_err(&client->adapter->dev, "i2c write failed\n");
|
||||||
|
+
|
||||||
|
+ kfree(buffer);
|
||||||
|
+ return ret;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static acpi_status
|
||||||
|
+acpi_i2c_space_handler(u32 function, acpi_physical_address command,
|
||||||
|
+ u32 bits, u64 *value64,
|
||||||
|
+ void *handler_context, void *region_context)
|
||||||
|
+{
|
||||||
|
+ struct gsb_buffer *gsb = (struct gsb_buffer *)value64;
|
||||||
|
+ struct acpi_i2c_handler_data *data = handler_context;
|
||||||
|
+ struct acpi_connection_info *info = &data->info;
|
||||||
|
+ struct acpi_resource_i2c_serialbus *sb;
|
||||||
|
+ struct i2c_adapter *adapter = data->adapter;
|
||||||
|
+ struct i2c_client client;
|
||||||
|
+ struct acpi_resource *ares;
|
||||||
|
+ u32 accessor_type = function >> 16;
|
||||||
|
+ u8 action = function & ACPI_IO_MASK;
|
||||||
|
+ acpi_status ret = AE_OK;
|
||||||
|
+ int status;
|
||||||
|
+
|
||||||
|
+ ret = acpi_buffer_to_resource(info->connection, info->length, &ares);
|
||||||
|
+ if (ACPI_FAILURE(ret))
|
||||||
|
+ return ret;
|
||||||
|
+
|
||||||
|
+ if (!value64 || ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) {
|
||||||
|
+ ret = AE_BAD_PARAMETER;
|
||||||
|
+ goto err;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ sb = &ares->data.i2c_serial_bus;
|
||||||
|
+ if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_I2C) {
|
||||||
|
+ ret = AE_BAD_PARAMETER;
|
||||||
|
+ goto err;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ memset(&client, 0, sizeof(client));
|
||||||
|
+ client.adapter = adapter;
|
||||||
|
+ client.addr = sb->slave_address;
|
||||||
|
+ client.flags = 0;
|
||||||
|
+
|
||||||
|
+ if (sb->access_mode == ACPI_I2C_10BIT_MODE)
|
||||||
|
+ client.flags |= I2C_CLIENT_TEN;
|
||||||
|
+
|
||||||
|
+ switch (accessor_type) {
|
||||||
|
+ case ACPI_GSB_ACCESS_ATTRIB_SEND_RCV:
|
||||||
|
+ if (action == ACPI_READ) {
|
||||||
|
+ status = i2c_smbus_read_byte(&client);
|
||||||
|
+ if (status >= 0) {
|
||||||
|
+ gsb->bdata = status;
|
||||||
|
+ status = 0;
|
||||||
|
+ }
|
||||||
|
+ } else {
|
||||||
|
+ status = i2c_smbus_write_byte(&client, gsb->bdata);
|
||||||
|
+ }
|
||||||
|
+ break;
|
||||||
|
+
|
||||||
|
+ case ACPI_GSB_ACCESS_ATTRIB_BYTE:
|
||||||
|
+ if (action == ACPI_READ) {
|
||||||
|
+ status = i2c_smbus_read_byte_data(&client, command);
|
||||||
|
+ if (status >= 0) {
|
||||||
|
+ gsb->bdata = status;
|
||||||
|
+ status = 0;
|
||||||
|
+ }
|
||||||
|
+ } else {
|
||||||
|
+ status = i2c_smbus_write_byte_data(&client, command,
|
||||||
|
+ gsb->bdata);
|
||||||
|
+ }
|
||||||
|
+ break;
|
||||||
|
+
|
||||||
|
+ case ACPI_GSB_ACCESS_ATTRIB_WORD:
|
||||||
|
+ if (action == ACPI_READ) {
|
||||||
|
+ status = i2c_smbus_read_word_data(&client, command);
|
||||||
|
+ if (status >= 0) {
|
||||||
|
+ gsb->wdata = status;
|
||||||
|
+ status = 0;
|
||||||
|
+ }
|
||||||
|
+ } else {
|
||||||
|
+ status = i2c_smbus_write_word_data(&client, command,
|
||||||
|
+ gsb->wdata);
|
||||||
|
+ }
|
||||||
|
+ break;
|
||||||
|
+
|
||||||
|
+ case ACPI_GSB_ACCESS_ATTRIB_BLOCK:
|
||||||
|
+ if (action == ACPI_READ) {
|
||||||
|
+ status = i2c_smbus_read_block_data(&client, command,
|
||||||
|
+ gsb->data);
|
||||||
|
+ if (status >= 0) {
|
||||||
|
+ gsb->len = status;
|
||||||
|
+ status = 0;
|
||||||
|
+ }
|
||||||
|
+ } else {
|
||||||
|
+ status = i2c_smbus_write_block_data(&client, command,
|
||||||
|
+ gsb->len, gsb->data);
|
||||||
|
+ }
|
||||||
|
+ break;
|
||||||
|
+
|
||||||
|
+ case ACPI_GSB_ACCESS_ATTRIB_MULTIBYTE:
|
||||||
|
+ if (action == ACPI_READ) {
|
||||||
|
+ status = acpi_gsb_i2c_read_bytes(&client, command,
|
||||||
|
+ gsb->data, info->access_length);
|
||||||
|
+ if (status > 0)
|
||||||
|
+ status = 0;
|
||||||
|
+ } else {
|
||||||
|
+ status = acpi_gsb_i2c_write_bytes(&client, command,
|
||||||
|
+ gsb->data, info->access_length);
|
||||||
|
+ }
|
||||||
|
+ break;
|
||||||
|
+
|
||||||
|
+ default:
|
||||||
|
+ pr_info("protocol(0x%02x) is not supported.\n", accessor_type);
|
||||||
|
+ ret = AE_BAD_PARAMETER;
|
||||||
|
+ goto err;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ gsb->status = status;
|
||||||
|
+
|
||||||
|
+ err:
|
||||||
|
+ ACPI_FREE(ares);
|
||||||
|
+ return ret;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+int acpi_i2c_install_space_handler(struct i2c_adapter *adapter)
|
||||||
|
+{
|
||||||
|
+ acpi_handle handle = ACPI_HANDLE(adapter->dev.parent);
|
||||||
|
+ struct acpi_i2c_handler_data *data;
|
||||||
|
+ acpi_status status;
|
||||||
|
+
|
||||||
|
+ if (!handle)
|
||||||
|
+ return -ENODEV;
|
||||||
|
+
|
||||||
|
+ data = kzalloc(sizeof(struct acpi_i2c_handler_data),
|
||||||
|
+ GFP_KERNEL);
|
||||||
|
+ if (!data)
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+
|
||||||
|
+ data->adapter = adapter;
|
||||||
|
+ status = acpi_bus_attach_private_data(handle, (void *)data);
|
||||||
|
+ if (ACPI_FAILURE(status)) {
|
||||||
|
+ kfree(data);
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ status = acpi_install_address_space_handler(handle,
|
||||||
|
+ ACPI_ADR_SPACE_GSBUS,
|
||||||
|
+ &acpi_i2c_space_handler,
|
||||||
|
+ NULL,
|
||||||
|
+ data);
|
||||||
|
+ if (ACPI_FAILURE(status)) {
|
||||||
|
+ dev_err(&adapter->dev, "Error installing i2c space handler\n");
|
||||||
|
+ acpi_bus_detach_private_data(handle);
|
||||||
|
+ kfree(data);
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+void acpi_i2c_remove_space_handler(struct i2c_adapter *adapter)
|
||||||
|
+{
|
||||||
|
+ acpi_handle handle = ACPI_HANDLE(adapter->dev.parent);
|
||||||
|
+ struct acpi_i2c_handler_data *data;
|
||||||
|
+ acpi_status status;
|
||||||
|
+
|
||||||
|
+ if (!handle)
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
+ acpi_remove_address_space_handler(handle,
|
||||||
|
+ ACPI_ADR_SPACE_GSBUS,
|
||||||
|
+ &acpi_i2c_space_handler);
|
||||||
|
+
|
||||||
|
+ status = acpi_bus_get_private_data(handle, (void **)&data);
|
||||||
|
+ if (ACPI_SUCCESS(status))
|
||||||
|
+ kfree(data);
|
||||||
|
+
|
||||||
|
+ acpi_bus_detach_private_data(handle);
|
||||||
|
+}
|
||||||
|
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
|
||||||
|
index 7c7f4b8..e25cb84 100644
|
||||||
|
--- a/drivers/i2c/i2c-core.c
|
||||||
|
+++ b/drivers/i2c/i2c-core.c
|
||||||
|
@@ -1293,6 +1293,7 @@ exit_recovery:
|
||||||
|
/* create pre-declared device nodes */
|
||||||
|
of_i2c_register_devices(adap);
|
||||||
|
acpi_i2c_register_devices(adap);
|
||||||
|
+ acpi_i2c_install_space_handler(adap);
|
||||||
|
|
||||||
|
if (adap->nr < __i2c_first_dynamic_bus_num)
|
||||||
|
i2c_scan_static_board_info(adap);
|
||||||
|
@@ -1466,6 +1467,7 @@ void i2c_del_adapter(struct i2c_adapter *adap)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ acpi_i2c_remove_space_handler(adap);
|
||||||
|
/* Tell drivers about this removal */
|
||||||
|
mutex_lock(&core_lock);
|
||||||
|
bus_for_each_drv(&i2c_bus_type, NULL, adap,
|
||||||
|
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
|
||||||
|
index 7a8f2cd..ea53b9b 100644
|
||||||
|
--- a/include/linux/acpi.h
|
||||||
|
+++ b/include/linux/acpi.h
|
||||||
|
@@ -361,6 +361,17 @@ extern bool osc_sb_apei_support_acked;
|
||||||
|
#define OSC_PCI_EXPRESS_CAPABILITY_CONTROL 0x00000010
|
||||||
|
#define OSC_PCI_CONTROL_MASKS 0x0000001f
|
||||||
|
|
||||||
|
+#define ACPI_GSB_ACCESS_ATTRIB_QUICK 0x00000002
|
||||||
|
+#define ACPI_GSB_ACCESS_ATTRIB_SEND_RCV 0x00000004
|
||||||
|
+#define ACPI_GSB_ACCESS_ATTRIB_BYTE 0x00000006
|
||||||
|
+#define ACPI_GSB_ACCESS_ATTRIB_WORD 0x00000008
|
||||||
|
+#define ACPI_GSB_ACCESS_ATTRIB_BLOCK 0x0000000A
|
||||||
|
+#define ACPI_GSB_ACCESS_ATTRIB_MULTIBYTE 0x0000000B
|
||||||
|
+#define ACPI_GSB_ACCESS_ATTRIB_WORD_CALL 0x0000000C
|
||||||
|
+#define ACPI_GSB_ACCESS_ATTRIB_BLOCK_CALL 0x0000000D
|
||||||
|
+#define ACPI_GSB_ACCESS_ATTRIB_RAW_BYTES 0x0000000E
|
||||||
|
+#define ACPI_GSB_ACCESS_ATTRIB_RAW_PROCESS 0x0000000F
|
||||||
|
+
|
||||||
|
extern acpi_status acpi_pci_osc_control_set(acpi_handle handle,
|
||||||
|
u32 *mask, u32 req);
|
||||||
|
|
||||||
|
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
|
||||||
|
index b556e0a..f7a939a 100644
|
||||||
|
--- a/include/linux/i2c.h
|
||||||
|
+++ b/include/linux/i2c.h
|
||||||
|
@@ -577,4 +577,14 @@ static inline struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_OF */
|
||||||
|
|
||||||
|
+#ifdef CONFIG_ACPI
|
||||||
|
+int acpi_i2c_install_space_handler(struct i2c_adapter *adapter);
|
||||||
|
+void acpi_i2c_remove_space_handler(struct i2c_adapter *adapter);
|
||||||
|
+#else
|
||||||
|
+static inline void acpi_i2c_remove_space_handler(struct i2c_adapter *adapter)
|
||||||
|
+{ }
|
||||||
|
+static inline int acpi_i2c_install_space_handler(struct i2c_adapter *adapter)
|
||||||
|
+{ return 0; }
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
#endif /* _LINUX_I2C_H */
|
@ -0,0 +1,310 @@
|
|||||||
|
From patchwork Tue May 20 12:59:24 2014
|
||||||
|
Content-Type: text/plain; charset="utf-8"
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Transfer-Encoding: 7bit
|
||||||
|
Subject: [V3,
|
||||||
|
5/5] I2C/ACPI: Clean up I2C ACPI code and Add CONFIG_I2C_ACPI config
|
||||||
|
From: "lan,Tianyu" <tianyu.lan@intel.com>
|
||||||
|
X-Patchwork-Id: 4209461
|
||||||
|
Message-Id: <1400590764-11108-6-git-send-email-tianyu.lan@intel.com>
|
||||||
|
To: wsa@the-dreams.de, rjw@rjwysocki.net,
|
||||||
|
mika.westerberg@linux.intel.com, awilliam@redhat.com, lenb@kernel.org
|
||||||
|
Cc: Lan Tianyu <tianyu.lan@intel.com>, linux-i2c@vger.kernel.org,
|
||||||
|
linux-kernel@vger.kernel.org, linux-acpi@vger.kernel.org
|
||||||
|
Date: Tue, 20 May 2014 20:59:24 +0800
|
||||||
|
|
||||||
|
Clean up ACPI related code in the i2c core and add CONFIG_I2C_ACPI
|
||||||
|
to enable I2C ACPI code.
|
||||||
|
|
||||||
|
Current there is a race between removing I2C ACPI operation region
|
||||||
|
and ACPI AML code accessing. So make i2c core built-in if CONFIG_I2C_ACPI
|
||||||
|
is set.
|
||||||
|
|
||||||
|
Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||||
|
Signed-off-by: Lan Tianyu <tianyu.lan@intel.com>
|
||||||
|
|
||||||
|
---
|
||||||
|
drivers/i2c/Kconfig | 18 +++++++++-
|
||||||
|
drivers/i2c/Makefile | 2 +-
|
||||||
|
drivers/i2c/i2c-acpi.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
drivers/i2c/i2c-core.c | 95 --------------------------------------------------
|
||||||
|
include/linux/i2c.h | 4 ++-
|
||||||
|
5 files changed, 110 insertions(+), 98 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
|
||||||
|
index 7b7ea32..3e3b680 100644
|
||||||
|
--- a/drivers/i2c/Kconfig
|
||||||
|
+++ b/drivers/i2c/Kconfig
|
||||||
|
@@ -2,7 +2,9 @@
|
||||||
|
# I2C subsystem configuration
|
||||||
|
#
|
||||||
|
|
||||||
|
-menuconfig I2C
|
||||||
|
+menu "I2C support"
|
||||||
|
+
|
||||||
|
+config I2C
|
||||||
|
tristate "I2C support"
|
||||||
|
select RT_MUTEXES
|
||||||
|
---help---
|
||||||
|
@@ -21,6 +23,18 @@ menuconfig I2C
|
||||||
|
This I2C support can also be built as a module. If so, the module
|
||||||
|
will be called i2c-core.
|
||||||
|
|
||||||
|
+config I2C_ACPI
|
||||||
|
+ bool "I2C ACPI support"
|
||||||
|
+ select I2C
|
||||||
|
+ depends on ACPI
|
||||||
|
+ default y
|
||||||
|
+ help
|
||||||
|
+ Say Y here if you want to enable ACPI I2C support. This includes support
|
||||||
|
+ for automatic enumeration of I2C slave devices and support for ACPI I2C
|
||||||
|
+ Operation Regions. Operation Regions allow firmware (BIOS) code to
|
||||||
|
+ access I2C slave devices, such as smart batteries through an I2C host
|
||||||
|
+ controller driver.
|
||||||
|
+
|
||||||
|
if I2C
|
||||||
|
|
||||||
|
config I2C_BOARDINFO
|
||||||
|
@@ -124,3 +138,5 @@ config I2C_DEBUG_BUS
|
||||||
|
on.
|
||||||
|
|
||||||
|
endif # I2C
|
||||||
|
+
|
||||||
|
+endmenu
|
||||||
|
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
|
||||||
|
index 80db307..a1f590c 100644
|
||||||
|
--- a/drivers/i2c/Makefile
|
||||||
|
+++ b/drivers/i2c/Makefile
|
||||||
|
@@ -3,7 +3,7 @@
|
||||||
|
#
|
||||||
|
|
||||||
|
i2ccore-y := i2c-core.o
|
||||||
|
-i2ccore-$(CONFIG_ACPI) += i2c-acpi.o
|
||||||
|
+i2ccore-$(CONFIG_I2C_ACPI) += i2c-acpi.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o
|
||||||
|
obj-$(CONFIG_I2C) += i2ccore.o
|
||||||
|
diff --git a/drivers/i2c/i2c-acpi.c b/drivers/i2c/i2c-acpi.c
|
||||||
|
index f7f4c89..e8b6196 100644
|
||||||
|
--- a/drivers/i2c/i2c-acpi.c
|
||||||
|
+++ b/drivers/i2c/i2c-acpi.c
|
||||||
|
@@ -37,6 +37,95 @@ struct gsb_buffer {
|
||||||
|
};
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
+static int acpi_i2c_add_resource(struct acpi_resource *ares, void *data)
|
||||||
|
+{
|
||||||
|
+ struct i2c_board_info *info = data;
|
||||||
|
+
|
||||||
|
+ if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
|
||||||
|
+ struct acpi_resource_i2c_serialbus *sb;
|
||||||
|
+
|
||||||
|
+ sb = &ares->data.i2c_serial_bus;
|
||||||
|
+ if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_I2C) {
|
||||||
|
+ info->addr = sb->slave_address;
|
||||||
|
+ if (sb->access_mode == ACPI_I2C_10BIT_MODE)
|
||||||
|
+ info->flags |= I2C_CLIENT_TEN;
|
||||||
|
+ }
|
||||||
|
+ } else if (info->irq < 0) {
|
||||||
|
+ struct resource r;
|
||||||
|
+
|
||||||
|
+ if (acpi_dev_resource_interrupt(ares, 0, &r))
|
||||||
|
+ info->irq = r.start;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* Tell the ACPI core to skip this resource */
|
||||||
|
+ return 1;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level,
|
||||||
|
+ void *data, void **return_value)
|
||||||
|
+{
|
||||||
|
+ struct i2c_adapter *adapter = data;
|
||||||
|
+ struct list_head resource_list;
|
||||||
|
+ struct i2c_board_info info;
|
||||||
|
+ struct acpi_device *adev;
|
||||||
|
+ int ret;
|
||||||
|
+
|
||||||
|
+ if (acpi_bus_get_device(handle, &adev))
|
||||||
|
+ return AE_OK;
|
||||||
|
+ if (acpi_bus_get_status(adev) || !adev->status.present)
|
||||||
|
+ return AE_OK;
|
||||||
|
+
|
||||||
|
+ memset(&info, 0, sizeof(info));
|
||||||
|
+ info.acpi_node.companion = adev;
|
||||||
|
+ info.irq = -1;
|
||||||
|
+
|
||||||
|
+ INIT_LIST_HEAD(&resource_list);
|
||||||
|
+ ret = acpi_dev_get_resources(adev, &resource_list,
|
||||||
|
+ acpi_i2c_add_resource, &info);
|
||||||
|
+ acpi_dev_free_resource_list(&resource_list);
|
||||||
|
+
|
||||||
|
+ if (ret < 0 || !info.addr)
|
||||||
|
+ return AE_OK;
|
||||||
|
+
|
||||||
|
+ adev->power.flags.ignore_parent = true;
|
||||||
|
+ strlcpy(info.type, dev_name(&adev->dev), sizeof(info.type));
|
||||||
|
+ if (!i2c_new_device(adapter, &info)) {
|
||||||
|
+ adev->power.flags.ignore_parent = false;
|
||||||
|
+ dev_err(&adapter->dev,
|
||||||
|
+ "failed to add I2C device %s from ACPI\n",
|
||||||
|
+ dev_name(&adev->dev));
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return AE_OK;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * acpi_i2c_register_devices - enumerate I2C slave devices behind adapter
|
||||||
|
+ * @adap: pointer to adapter
|
||||||
|
+ *
|
||||||
|
+ * Enumerate all I2C slave devices behind this adapter by walking the ACPI
|
||||||
|
+ * namespace. When a device is found it will be added to the Linux device
|
||||||
|
+ * model and bound to the corresponding ACPI handle.
|
||||||
|
+ */
|
||||||
|
+void acpi_i2c_register_devices(struct i2c_adapter *adap)
|
||||||
|
+{
|
||||||
|
+ acpi_handle handle;
|
||||||
|
+ acpi_status status;
|
||||||
|
+
|
||||||
|
+ if (!adap->dev.parent)
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
+ handle = ACPI_HANDLE(adap->dev.parent);
|
||||||
|
+ if (!handle)
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
|
||||||
|
+ acpi_i2c_add_device, NULL,
|
||||||
|
+ adap, NULL);
|
||||||
|
+ if (ACPI_FAILURE(status))
|
||||||
|
+ dev_warn(&adap->dev, "failed to enumerate I2C slaves\n");
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static int acpi_gsb_i2c_read_bytes(struct i2c_client *client,
|
||||||
|
u8 cmd, u8 *data, u8 data_len)
|
||||||
|
{
|
||||||
|
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
|
||||||
|
index e25cb84..4ccff11 100644
|
||||||
|
--- a/drivers/i2c/i2c-core.c
|
||||||
|
+++ b/drivers/i2c/i2c-core.c
|
||||||
|
@@ -1092,101 +1092,6 @@ EXPORT_SYMBOL(of_find_i2c_adapter_by_node);
|
||||||
|
static void of_i2c_register_devices(struct i2c_adapter *adap) { }
|
||||||
|
#endif /* CONFIG_OF */
|
||||||
|
|
||||||
|
-/* ACPI support code */
|
||||||
|
-
|
||||||
|
-#if IS_ENABLED(CONFIG_ACPI)
|
||||||
|
-static int acpi_i2c_add_resource(struct acpi_resource *ares, void *data)
|
||||||
|
-{
|
||||||
|
- struct i2c_board_info *info = data;
|
||||||
|
-
|
||||||
|
- if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
|
||||||
|
- struct acpi_resource_i2c_serialbus *sb;
|
||||||
|
-
|
||||||
|
- sb = &ares->data.i2c_serial_bus;
|
||||||
|
- if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_I2C) {
|
||||||
|
- info->addr = sb->slave_address;
|
||||||
|
- if (sb->access_mode == ACPI_I2C_10BIT_MODE)
|
||||||
|
- info->flags |= I2C_CLIENT_TEN;
|
||||||
|
- }
|
||||||
|
- } else if (info->irq < 0) {
|
||||||
|
- struct resource r;
|
||||||
|
-
|
||||||
|
- if (acpi_dev_resource_interrupt(ares, 0, &r))
|
||||||
|
- info->irq = r.start;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* Tell the ACPI core to skip this resource */
|
||||||
|
- return 1;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level,
|
||||||
|
- void *data, void **return_value)
|
||||||
|
-{
|
||||||
|
- struct i2c_adapter *adapter = data;
|
||||||
|
- struct list_head resource_list;
|
||||||
|
- struct i2c_board_info info;
|
||||||
|
- struct acpi_device *adev;
|
||||||
|
- int ret;
|
||||||
|
-
|
||||||
|
- if (acpi_bus_get_device(handle, &adev))
|
||||||
|
- return AE_OK;
|
||||||
|
- if (acpi_bus_get_status(adev) || !adev->status.present)
|
||||||
|
- return AE_OK;
|
||||||
|
-
|
||||||
|
- memset(&info, 0, sizeof(info));
|
||||||
|
- info.acpi_node.companion = adev;
|
||||||
|
- info.irq = -1;
|
||||||
|
-
|
||||||
|
- INIT_LIST_HEAD(&resource_list);
|
||||||
|
- ret = acpi_dev_get_resources(adev, &resource_list,
|
||||||
|
- acpi_i2c_add_resource, &info);
|
||||||
|
- acpi_dev_free_resource_list(&resource_list);
|
||||||
|
-
|
||||||
|
- if (ret < 0 || !info.addr)
|
||||||
|
- return AE_OK;
|
||||||
|
-
|
||||||
|
- adev->power.flags.ignore_parent = true;
|
||||||
|
- strlcpy(info.type, dev_name(&adev->dev), sizeof(info.type));
|
||||||
|
- if (!i2c_new_device(adapter, &info)) {
|
||||||
|
- adev->power.flags.ignore_parent = false;
|
||||||
|
- dev_err(&adapter->dev,
|
||||||
|
- "failed to add I2C device %s from ACPI\n",
|
||||||
|
- dev_name(&adev->dev));
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- return AE_OK;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-/**
|
||||||
|
- * acpi_i2c_register_devices - enumerate I2C slave devices behind adapter
|
||||||
|
- * @adap: pointer to adapter
|
||||||
|
- *
|
||||||
|
- * Enumerate all I2C slave devices behind this adapter by walking the ACPI
|
||||||
|
- * namespace. When a device is found it will be added to the Linux device
|
||||||
|
- * model and bound to the corresponding ACPI handle.
|
||||||
|
- */
|
||||||
|
-static void acpi_i2c_register_devices(struct i2c_adapter *adap)
|
||||||
|
-{
|
||||||
|
- acpi_handle handle;
|
||||||
|
- acpi_status status;
|
||||||
|
-
|
||||||
|
- if (!adap->dev.parent)
|
||||||
|
- return;
|
||||||
|
-
|
||||||
|
- handle = ACPI_HANDLE(adap->dev.parent);
|
||||||
|
- if (!handle)
|
||||||
|
- return;
|
||||||
|
-
|
||||||
|
- status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
|
||||||
|
- acpi_i2c_add_device, NULL,
|
||||||
|
- adap, NULL);
|
||||||
|
- if (ACPI_FAILURE(status))
|
||||||
|
- dev_warn(&adap->dev, "failed to enumerate I2C slaves\n");
|
||||||
|
-}
|
||||||
|
-#else
|
||||||
|
-static inline void acpi_i2c_register_devices(struct i2c_adapter *adap) {}
|
||||||
|
-#endif /* CONFIG_ACPI */
|
||||||
|
-
|
||||||
|
static int i2c_do_add_adapter(struct i2c_driver *driver,
|
||||||
|
struct i2c_adapter *adap)
|
||||||
|
{
|
||||||
|
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
|
||||||
|
index f7a939a..ea50766 100644
|
||||||
|
--- a/include/linux/i2c.h
|
||||||
|
+++ b/include/linux/i2c.h
|
||||||
|
@@ -577,10 +577,12 @@ static inline struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_OF */
|
||||||
|
|
||||||
|
-#ifdef CONFIG_ACPI
|
||||||
|
+#ifdef CONFIG_I2C_ACPI
|
||||||
|
int acpi_i2c_install_space_handler(struct i2c_adapter *adapter);
|
||||||
|
void acpi_i2c_remove_space_handler(struct i2c_adapter *adapter);
|
||||||
|
+void acpi_i2c_register_devices(struct i2c_adapter *adap);
|
||||||
|
#else
|
||||||
|
+static inline void acpi_i2c_register_devices(struct i2c_adapter *adap) { }
|
||||||
|
static inline void acpi_i2c_remove_space_handler(struct i2c_adapter *adapter)
|
||||||
|
{ }
|
||||||
|
static inline int acpi_i2c_install_space_handler(struct i2c_adapter *adapter)
|
87
baytrail_gpio_quirk_v3.patch
Normal file
87
baytrail_gpio_quirk_v3.patch
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c
|
||||||
|
index 69e29f4..d79c6d7 100644
|
||||||
|
--- a/drivers/acpi/acpi_lpss.c
|
||||||
|
+++ b/drivers/acpi/acpi_lpss.c
|
||||||
|
@@ -180,6 +180,7 @@ static const struct acpi_device_id acpi_lpss_device_ids[] = {
|
||||||
|
{ "80860F14", (unsigned long)&byt_sdio_dev_desc },
|
||||||
|
{ "80860F41", (unsigned long)&byt_i2c_dev_desc },
|
||||||
|
{ "INT33B2", },
|
||||||
|
+ { "INT33FC", },
|
||||||
|
|
||||||
|
{ "INT3430", (unsigned long)&lpt_dev_desc },
|
||||||
|
{ "INT3431", (unsigned long)&lpt_dev_desc },
|
||||||
|
diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c
|
||||||
|
index ebb3f39..ebed2a0 100644
|
||||||
|
--- a/drivers/mmc/host/sdhci-acpi.c
|
||||||
|
+++ b/drivers/mmc/host/sdhci-acpi.c
|
||||||
|
@@ -123,7 +123,7 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = {
|
||||||
|
|
||||||
|
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
|
||||||
|
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION,
|
||||||
|
- .quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON,
|
||||||
|
+ .quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON | SDHCI_QUIRK2_BROKEN_POWER_ENABLE,
|
||||||
|
.caps = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD,
|
||||||
|
.flags = SDHCI_ACPI_RUNTIME_PM,
|
||||||
|
.pm_caps = MMC_PM_KEEP_POWER,
|
||||||
|
diff --git a/drivers/pinctrl/pinctrl-baytrail.c b/drivers/pinctrl/pinctrl-baytrail.c
|
||||||
|
index 6e8301f..6c8eda2 100644
|
||||||
|
--- a/drivers/pinctrl/pinctrl-baytrail.c
|
||||||
|
+++ b/drivers/pinctrl/pinctrl-baytrail.c
|
||||||
|
@@ -151,9 +151,9 @@ static void __iomem *byt_gpio_reg(struct gpio_chip *chip, unsigned offset,
|
||||||
|
|
||||||
|
static bool is_special_pin(struct byt_gpio *vg, unsigned offset)
|
||||||
|
{
|
||||||
|
- /* SCORE pin 92-93 */
|
||||||
|
+ /* SCORE pin 92-93; 41 for SDIO pwr_en bug */
|
||||||
|
if (!strcmp(vg->range->name, BYT_SCORE_ACPI_UID) &&
|
||||||
|
- offset >= 92 && offset <= 93)
|
||||||
|
+ ((offset >= 92 && offset <= 93) || (offset == 41)))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
/* SUS pin 11-21 */
|
||||||
|
@@ -176,6 +176,10 @@ static int byt_gpio_request(struct gpio_chip *chip, unsigned offset)
|
||||||
|
* But, some pins may have func pin mux 001 represents
|
||||||
|
* GPIO function. Only allow user to export pin with
|
||||||
|
* func pin mux preset as GPIO function by BIOS/FW.
|
||||||
|
+ *
|
||||||
|
+ * We do make an exception, however, for pin 41 which
|
||||||
|
+ * is needed in order to power up the SDIO bus (as per
|
||||||
|
+ * the intel erratum)
|
||||||
|
*/
|
||||||
|
value = readl(reg) & BYT_PIN_MUX;
|
||||||
|
special = is_special_pin(vg, offset);
|
||||||
|
@@ -185,6 +189,13 @@ static int byt_gpio_request(struct gpio_chip *chip, unsigned offset)
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ /* This is an attempt to stop the SDHCI drivers from requesting IRQ lines
|
||||||
|
+ * through the pinctrl driver. This may be a quirk in the hardware or it
|
||||||
|
+ * may be a bug here in the IRQ handling
|
||||||
|
+ */
|
||||||
|
+ if (offset == 38)
|
||||||
|
+ return -EINVAL;
|
||||||
|
+
|
||||||
|
pm_runtime_get(&vg->pdev->dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
@@ -572,6 +583,7 @@ static const struct dev_pm_ops byt_gpio_pm_ops = {
|
||||||
|
|
||||||
|
static const struct acpi_device_id byt_gpio_acpi_match[] = {
|
||||||
|
{ "INT33B2", 0 },
|
||||||
|
+ { "INT33FC", 0 },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(acpi, byt_gpio_acpi_match);
|
||||||
|
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
|
||||||
|
index 7be12b8..3a7fd87 100644
|
||||||
|
--- a/include/linux/mmc/sdhci.h
|
||||||
|
+++ b/include/linux/mmc/sdhci.h
|
||||||
|
@@ -102,6 +102,8 @@ struct sdhci_host {
|
||||||
|
#define SDHCI_QUIRK2_BROKEN_HS200 (1<<6)
|
||||||
|
/* Controller does not support DDR50 */
|
||||||
|
#define SDHCI_QUIRK2_BROKEN_DDR50 (1<<7)
|
||||||
|
+/* Controller cannot initialize power (must use GPIO instead) */
|
||||||
|
+#define SDHCI_QUIRK2_BROKEN_POWER_ENABLE (1<<8)
|
||||||
|
|
||||||
|
int irq; /* Device IRQ */
|
||||||
|
void __iomem *ioaddr; /* Mapped address */
|
484
x86-new-Intel-Atom-SoC-power-management-controller-driver.patch
Normal file
484
x86-new-Intel-Atom-SoC-power-management-controller-driver.patch
Normal file
@ -0,0 +1,484 @@
|
|||||||
|
From patchwork Fri Mar 7 03:04:42 2014
|
||||||
|
Content-Type: text/plain; charset="utf-8"
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Transfer-Encoding: 7bit
|
||||||
|
Subject: x86: new Intel Atom SoC power management controller driver
|
||||||
|
From: "Li, Aubrey" <aubrey.li@linux.intel.com>
|
||||||
|
X-Patchwork-Id: 3787341
|
||||||
|
Message-Id: <5319374A.60308@linux.intel.com>
|
||||||
|
To: Joe Perches <joe@perches.com>
|
||||||
|
Cc: "H. Peter Anvin" <hpa@linux.intel.com>,
|
||||||
|
Matthew Garrett <mjg59@srcf.ucam.org>, mingo@redhat.com,
|
||||||
|
tglx@linutronix.de, linux-kernel@vger.kernel.org,
|
||||||
|
"alan@linux.intel.com" <alan@linux.intel.com>
|
||||||
|
Date: Fri, 07 Mar 2014 11:04:42 +0800
|
||||||
|
|
||||||
|
On 2014/3/7 10:21, Joe Perches wrote:
|
||||||
|
> On Fri, 2014-03-07 at 10:08 +0800, Li, Aubrey wrote:
|
||||||
|
>
|
||||||
|
>> The Power Management Controller (PMC) controls many of the power
|
||||||
|
>> management features present in the SoC. This driver provides
|
||||||
|
>> interface to configure the Power Management Controller (PMC).
|
||||||
|
>
|
||||||
|
> More trivial notes.
|
||||||
|
>
|
||||||
|
> Nothing really that should stop this from being applied.
|
||||||
|
>
|
||||||
|
All make sense to me. Welcome your more comments, Joe!
|
||||||
|
|
||||||
|
Thanks,
|
||||||
|
-Aubrey
|
||||||
|
|
||||||
|
[PATCH] X86 platform: New Intel Atom SOC power management controller driver
|
||||||
|
|
||||||
|
The Power Management Controller (PMC) controls many of the power
|
||||||
|
management features present in the SoC. This driver provides
|
||||||
|
interface to configure the Power Management Controller (PMC).
|
||||||
|
|
||||||
|
This driver exposes PMC device state and sleep state residency
|
||||||
|
via debugfs:
|
||||||
|
/sys/kernel/debugfs/pmc_atom/dev_state
|
||||||
|
/sys/kernel/debugfs/pmc_atom/sleep_state
|
||||||
|
|
||||||
|
This driver also provides a native power off function via PMC PCI
|
||||||
|
IO port.
|
||||||
|
|
||||||
|
Signed-off-by: Aubrey Li <aubrey.li@intel.com>
|
||||||
|
Reviewed-by: Joe Perches <joe@perches.com>
|
||||||
|
|
||||||
|
---
|
||||||
|
arch/x86/Kconfig | 4 +
|
||||||
|
arch/x86/include/asm/pmc_atom.h | 91 ++++++++++++
|
||||||
|
arch/x86/kernel/Makefile | 1 +
|
||||||
|
arch/x86/kernel/pmc_atom.c | 297 +++++++++++++++++++++++++++++++++++++++
|
||||||
|
4 files changed, 393 insertions(+)
|
||||||
|
create mode 100644 arch/x86/include/asm/pmc_atom.h
|
||||||
|
create mode 100644 arch/x86/kernel/pmc_atom.c
|
||||||
|
|
||||||
|
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
|
||||||
|
index 0af5250..18f7d38 100644
|
||||||
|
--- a/arch/x86/Kconfig
|
||||||
|
+++ b/arch/x86/Kconfig
|
||||||
|
@@ -2413,6 +2413,10 @@
|
||||||
|
tristate
|
||||||
|
default m
|
||||||
|
depends on PCI
|
||||||
|
+
|
||||||
|
+config PMC_ATOM
|
||||||
|
+ def_bool y
|
||||||
|
+ depends on PCI
|
||||||
|
|
||||||
|
source "net/Kconfig"
|
||||||
|
|
||||||
|
diff --git a/arch/x86/include/asm/pmc_atom.h b/arch/x86/include/asm/pmc_atom.h
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..43b68fc
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/arch/x86/include/asm/pmc_atom.h
|
||||||
|
@@ -0,0 +1,91 @@
|
||||||
|
+/*
|
||||||
|
+ * Intel Atom SOC Power Management Controller Header File
|
||||||
|
+ * Copyright (c) 2014, Intel Corporation.
|
||||||
|
+ *
|
||||||
|
+ * This program is free software; you can redistribute it and/or modify it
|
||||||
|
+ * under the terms and conditions of the GNU General Public License,
|
||||||
|
+ * version 2, as published by the Free Software Foundation.
|
||||||
|
+ *
|
||||||
|
+ * This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
+ * more details.
|
||||||
|
+ *
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+#ifndef PMC_ATOM_H
|
||||||
|
+#define PMC_ATOM_H
|
||||||
|
+
|
||||||
|
+/* ValleyView Power Control Unit PCI Device ID */
|
||||||
|
+#define PCI_DEVICE_ID_VLV_PMC 0x0F1C
|
||||||
|
+
|
||||||
|
+/* PMC Memory mapped IO registers */
|
||||||
|
+#define PMC_BASE_ADDR_OFFSET 0x44
|
||||||
|
+#define PMC_BASE_ADDR_MASK 0xFFFFFE00
|
||||||
|
+#define PMC_MMIO_REG_LEN 0x100
|
||||||
|
+#define PMC_REG_BIT_WIDTH 32
|
||||||
|
+
|
||||||
|
+/* BIOS uses FUNC_DIS to disable specific function */
|
||||||
|
+#define PMC_FUNC_DIS 0x34
|
||||||
|
+#define PMC_FUNC_DIS_2 0x38
|
||||||
|
+/* The timers acumulate time spent in sleep state */
|
||||||
|
+#define PMC_S0IR_TMR 0x80
|
||||||
|
+#define PMC_S0I1_TMR 0x84
|
||||||
|
+#define PMC_S0I2_TMR 0x88
|
||||||
|
+#define PMC_S0I3_TMR 0x8C
|
||||||
|
+#define PMC_S0_TMR 0x90
|
||||||
|
+/* Sleep state counter is in units of of 32us */
|
||||||
|
+#define PMC_TMR_SHIFT 5
|
||||||
|
+
|
||||||
|
+/* These registers reflect D3 status of functions */
|
||||||
|
+#define PMC_D3_STS_0 0xA0
|
||||||
|
+
|
||||||
|
+#define BIT_LPSS1_F0_DMA BIT(0)
|
||||||
|
+#define BIT_LPSS1_F1_PWM1 BIT(1)
|
||||||
|
+#define BIT_LPSS1_F2_PWM2 BIT(2)
|
||||||
|
+#define BIT_LPSS1_F3_HSUART1 BIT(3)
|
||||||
|
+#define BIT_LPSS1_F4_HSUART2 BIT(4)
|
||||||
|
+#define BIT_LPSS1_F5_SPI BIT(5)
|
||||||
|
+#define BIT_LPSS1_F6_XXX BIT(6)
|
||||||
|
+#define BIT_LPSS1_F7_XXX BIT(7)
|
||||||
|
+#define BIT_SCC_EMMC BIT(8)
|
||||||
|
+#define BIT_SCC_SDIO BIT(9)
|
||||||
|
+#define BIT_SCC_SDCARD BIT(10)
|
||||||
|
+#define BIT_SCC_MIPI BIT(11)
|
||||||
|
+#define BIT_HDA BIT(12)
|
||||||
|
+#define BIT_LPE BIT(13)
|
||||||
|
+#define BIT_OTG BIT(14)
|
||||||
|
+#define BIT_USH BIT(15)
|
||||||
|
+#define BIT_GBE BIT(16)
|
||||||
|
+#define BIT_SATA BIT(17)
|
||||||
|
+#define BIT_USB_EHCI BIT(18)
|
||||||
|
+#define BIT_SEC BIT(19)
|
||||||
|
+#define BIT_PCIE_PORT0 BIT(20)
|
||||||
|
+#define BIT_PCIE_PORT1 BIT(21)
|
||||||
|
+#define BIT_PCIE_PORT2 BIT(22)
|
||||||
|
+#define BIT_PCIE_PORT3 BIT(23)
|
||||||
|
+#define BIT_LPSS2_F0_DMA BIT(24)
|
||||||
|
+#define BIT_LPSS2_F1_I2C1 BIT(25)
|
||||||
|
+#define BIT_LPSS2_F2_I2C2 BIT(26)
|
||||||
|
+#define BIT_LPSS2_F3_I2C3 BIT(27)
|
||||||
|
+#define BIT_LPSS2_F4_I2C4 BIT(28)
|
||||||
|
+#define BIT_LPSS2_F5_I2C5 BIT(29)
|
||||||
|
+#define BIT_LPSS2_F6_I2C6 BIT(30)
|
||||||
|
+#define BIT_LPSS2_F7_I2C7 BIT(31)
|
||||||
|
+
|
||||||
|
+#define PMC_D3_STS_1 0xA4
|
||||||
|
+#define BIT_SMB BIT(0)
|
||||||
|
+#define BIT_USH_SS_PHY BIT(1)
|
||||||
|
+#define BIT_OTG_SS_PHY BIT(2)
|
||||||
|
+#define BIT_DFX BIT(3)
|
||||||
|
+
|
||||||
|
+/* PMC I/O Registers */
|
||||||
|
+#define ACPI_BASE_ADDR_OFFSET 0x40
|
||||||
|
+#define ACPI_BASE_ADDR_MASK 0xFFFFFE00
|
||||||
|
+#define ACPI_MMIO_REG_LEN 0x100
|
||||||
|
+
|
||||||
|
+#define PM1_CNT 0x4
|
||||||
|
+#define SLEEP_TYPE_MASK 0xFFFFECFF
|
||||||
|
+#define SLEEP_TYPE_S5 0x1C00
|
||||||
|
+#define SLEEP_ENABLE 0x2000
|
||||||
|
+#endif /* PMC_ATOM_H */
|
||||||
|
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
|
||||||
|
index cb648c8..b71a61a 100644
|
||||||
|
--- a/arch/x86/kernel/Makefile
|
||||||
|
+++ b/arch/x86/kernel/Makefile
|
||||||
|
@@ -104,6 +104,7 @@ obj-$(CONFIG_EFI) += sysfb_efi.o
|
||||||
|
obj-$(CONFIG_PERF_EVENTS) += perf_regs.o
|
||||||
|
obj-$(CONFIG_TRACING) += tracepoint.o
|
||||||
|
obj-$(CONFIG_IOSF_MBI) += iosf_mbi.o
|
||||||
|
+obj-$(CONFIG_PMC_ATOM) += pmc_atom.o
|
||||||
|
|
||||||
|
###
|
||||||
|
# 64 bit specific files
|
||||||
|
diff --git a/arch/x86/kernel/pmc_atom.c b/arch/x86/kernel/pmc_atom.c
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..5991030
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/arch/x86/kernel/pmc_atom.c
|
||||||
|
@@ -0,0 +1,297 @@
|
||||||
|
+/*
|
||||||
|
+ * Intel Atom SOC Power Management Controller Driver
|
||||||
|
+ * Copyright (c) 2014, Intel Corporation.
|
||||||
|
+ *
|
||||||
|
+ * This program is free software; you can redistribute it and/or modify it
|
||||||
|
+ * under the terms and conditions of the GNU General Public License,
|
||||||
|
+ * version 2, as published by the Free Software Foundation.
|
||||||
|
+ *
|
||||||
|
+ * This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
+ * more details.
|
||||||
|
+ *
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
+
|
||||||
|
+#include <linux/module.h>
|
||||||
|
+#include <linux/init.h>
|
||||||
|
+#include <linux/pci.h>
|
||||||
|
+#include <linux/device.h>
|
||||||
|
+#include <linux/debugfs.h>
|
||||||
|
+#include <linux/seq_file.h>
|
||||||
|
+#include <linux/io.h>
|
||||||
|
+
|
||||||
|
+#include <asm/pmc_atom.h>
|
||||||
|
+
|
||||||
|
+#define DRIVER_NAME KBUILD_MODNAME
|
||||||
|
+
|
||||||
|
+struct pmc_dev {
|
||||||
|
+ struct pci_dev *pdev;
|
||||||
|
+ u32 base_addr;
|
||||||
|
+ void __iomem *regmap;
|
||||||
|
+#ifdef CONFIG_DEBUG_FS
|
||||||
|
+ struct dentry *dbgfs_dir;
|
||||||
|
+#endif /* CONFIG_DEBUG_FS */
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static u32 acpi_base_addr;
|
||||||
|
+
|
||||||
|
+struct pmc_dev_map {
|
||||||
|
+ const char *name;
|
||||||
|
+ u32 bit_mask;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static const struct pmc_dev_map dev_map[] = {
|
||||||
|
+ {"0 - LPSS1_F0_DMA", BIT_LPSS1_F0_DMA},
|
||||||
|
+ {"1 - LPSS1_F1_PWM1", BIT_LPSS1_F1_PWM1},
|
||||||
|
+ {"2 - LPSS1_F2_PWM2", BIT_LPSS1_F2_PWM2},
|
||||||
|
+ {"3 - LPSS1_F3_HSUART1", BIT_LPSS1_F3_HSUART1},
|
||||||
|
+ {"4 - LPSS1_F4_HSUART2", BIT_LPSS1_F4_HSUART2},
|
||||||
|
+ {"5 - LPSS1_F5_SPI", BIT_LPSS1_F5_SPI},
|
||||||
|
+ {"6 - LPSS1_F6_Reserved", BIT_LPSS1_F6_XXX},
|
||||||
|
+ {"7 - LPSS1_F7_Reserved", BIT_LPSS1_F7_XXX},
|
||||||
|
+ {"8 - SCC_EMMC", BIT_SCC_EMMC},
|
||||||
|
+ {"9 - SCC_SDIO", BIT_SCC_SDIO},
|
||||||
|
+ {"10 - SCC_SDCARD", BIT_SCC_SDCARD},
|
||||||
|
+ {"11 - SCC_MIPI", BIT_SCC_MIPI},
|
||||||
|
+ {"12 - HDA", BIT_HDA},
|
||||||
|
+ {"13 - LPE", BIT_LPE},
|
||||||
|
+ {"14 - OTG", BIT_OTG},
|
||||||
|
+ {"15 - USH", BIT_USH},
|
||||||
|
+ {"16 - GBE", BIT_GBE},
|
||||||
|
+ {"17 - SATA", BIT_SATA},
|
||||||
|
+ {"18 - USB_EHCI", BIT_USB_EHCI},
|
||||||
|
+ {"19 - SEC", BIT_SEC},
|
||||||
|
+ {"20 - PCIE_PORT0", BIT_PCIE_PORT0},
|
||||||
|
+ {"21 - PCIE_PORT1", BIT_PCIE_PORT1},
|
||||||
|
+ {"22 - PCIE_PORT2", BIT_PCIE_PORT2},
|
||||||
|
+ {"23 - PCIE_PORT3", BIT_PCIE_PORT3},
|
||||||
|
+ {"24 - LPSS2_F0_DMA", BIT_LPSS2_F0_DMA},
|
||||||
|
+ {"25 - LPSS2_F1_I2C1", BIT_LPSS2_F1_I2C1},
|
||||||
|
+ {"26 - LPSS2_F2_I2C2", BIT_LPSS2_F2_I2C2},
|
||||||
|
+ {"27 - LPSS2_F3_I2C3", BIT_LPSS2_F3_I2C3},
|
||||||
|
+ {"28 - LPSS2_F3_I2C4", BIT_LPSS2_F4_I2C4},
|
||||||
|
+ {"29 - LPSS2_F5_I2C5", BIT_LPSS2_F5_I2C5},
|
||||||
|
+ {"30 - LPSS2_F6_I2C6", BIT_LPSS2_F6_I2C6},
|
||||||
|
+ {"31 - LPSS2_F7_I2C7", BIT_LPSS2_F7_I2C7},
|
||||||
|
+ {"32 - SMB", BIT_SMB},
|
||||||
|
+ {"33 - USH_SS_PHY", BIT_OTG_SS_PHY},
|
||||||
|
+ {"34 - OTG_SS_PHY", BIT_USH_SS_PHY},
|
||||||
|
+ {"35 - DFX", BIT_DFX},
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static inline u32 pmc_reg_read(struct pmc_dev *pmc, int reg_offset)
|
||||||
|
+{
|
||||||
|
+ return readl(pmc->regmap + reg_offset);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void pmc_power_off(void)
|
||||||
|
+{
|
||||||
|
+ u16 pm1_cnt_port;
|
||||||
|
+ u32 pm1_cnt_value;
|
||||||
|
+
|
||||||
|
+ pr_info("Preparing to enter system sleep state S5\n");
|
||||||
|
+
|
||||||
|
+ pm1_cnt_port = acpi_base_addr + PM1_CNT;
|
||||||
|
+
|
||||||
|
+ pm1_cnt_value = inl(pm1_cnt_port);
|
||||||
|
+ pm1_cnt_value &= SLEEP_TYPE_MASK;
|
||||||
|
+ pm1_cnt_value |= SLEEP_TYPE_S5;
|
||||||
|
+ pm1_cnt_value |= SLEEP_ENABLE;
|
||||||
|
+
|
||||||
|
+ outl(pm1_cnt_value, pm1_cnt_port);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+#ifdef CONFIG_DEBUG_FS
|
||||||
|
+static int pmc_dev_state_show(struct seq_file *s, void *unused)
|
||||||
|
+{
|
||||||
|
+ struct pmc_dev *pmc = (struct pmc_dev *)s->private;
|
||||||
|
+ u32 func_dis, func_dis_2, func_dis_index;
|
||||||
|
+ u32 d3_sts_0, d3_sts_1, d3_sts_index;
|
||||||
|
+ int dev_num, dev_index, reg_index;
|
||||||
|
+
|
||||||
|
+ func_dis = pmc_reg_read(pmc, PMC_FUNC_DIS);
|
||||||
|
+ func_dis_2 = pmc_reg_read(pmc, PMC_FUNC_DIS_2);
|
||||||
|
+ d3_sts_0 = pmc_reg_read(pmc, PMC_D3_STS_0);
|
||||||
|
+ d3_sts_1 = pmc_reg_read(pmc, PMC_D3_STS_1);
|
||||||
|
+
|
||||||
|
+ dev_num = sizeof(dev_map) / sizeof(struct pmc_dev_map);
|
||||||
|
+
|
||||||
|
+ for (dev_index = 0; dev_index < dev_num; dev_index++) {
|
||||||
|
+ reg_index = dev_index / PMC_REG_BIT_WIDTH;
|
||||||
|
+ if (reg_index) {
|
||||||
|
+ func_dis_index = func_dis_2;
|
||||||
|
+ d3_sts_index = d3_sts_1;
|
||||||
|
+ } else {
|
||||||
|
+ func_dis_index = func_dis;
|
||||||
|
+ d3_sts_index = d3_sts_0;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ seq_printf(s, "Dev: %-32s\tState: %s [%s]\n",
|
||||||
|
+ dev_map[dev_index].name,
|
||||||
|
+ dev_map[dev_index].bit_mask & func_dis_index ?
|
||||||
|
+ "Disabled" : "Enabled ",
|
||||||
|
+ dev_map[dev_index].bit_mask & d3_sts_index ?
|
||||||
|
+ "D3" : "D0");
|
||||||
|
+ }
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int pmc_dev_state_open(struct inode *inode, struct file *file)
|
||||||
|
+{
|
||||||
|
+ return single_open(file, pmc_dev_state_show, inode->i_private);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static const struct file_operations pmc_dev_state_ops = {
|
||||||
|
+ .open = pmc_dev_state_open,
|
||||||
|
+ .read = seq_read,
|
||||||
|
+ .llseek = seq_lseek,
|
||||||
|
+ .release = single_release,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static int pmc_sleep_tmr_show(struct seq_file *s, void *unused)
|
||||||
|
+{
|
||||||
|
+ struct pmc_dev *pmc = (struct pmc_dev *)s->private;
|
||||||
|
+ u64 s0ir_tmr, s0i1_tmr, s0i2_tmr, s0i3_tmr, s0_tmr;
|
||||||
|
+
|
||||||
|
+ s0ir_tmr = pmc_reg_read(pmc, PMC_S0IR_TMR) << PMC_TMR_SHIFT;
|
||||||
|
+ s0i1_tmr = pmc_reg_read(pmc, PMC_S0I1_TMR) << PMC_TMR_SHIFT;
|
||||||
|
+ s0i2_tmr = pmc_reg_read(pmc, PMC_S0I2_TMR) << PMC_TMR_SHIFT;
|
||||||
|
+ s0i3_tmr = pmc_reg_read(pmc, PMC_S0I3_TMR) << PMC_TMR_SHIFT;
|
||||||
|
+ s0_tmr = pmc_reg_read(pmc, PMC_S0_TMR) << PMC_TMR_SHIFT;
|
||||||
|
+
|
||||||
|
+ seq_printf(s, "S0IR Residency:\t%lldus\n", s0ir_tmr);
|
||||||
|
+ seq_printf(s, "S0I1 Residency:\t%lldus\n", s0i1_tmr);
|
||||||
|
+ seq_printf(s, "S0I2 Residency:\t%lldus\n", s0i2_tmr);
|
||||||
|
+ seq_printf(s, "S0I3 Residency:\t%lldus\n", s0i3_tmr);
|
||||||
|
+ seq_printf(s, "S0 Residency:\t%lldus\n", s0_tmr);
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int pmc_sleep_tmr_open(struct inode *inode, struct file *file)
|
||||||
|
+{
|
||||||
|
+ return single_open(file, pmc_sleep_tmr_show, inode->i_private);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static const struct file_operations pmc_sleep_tmr_ops = {
|
||||||
|
+ .open = pmc_sleep_tmr_open,
|
||||||
|
+ .read = seq_read,
|
||||||
|
+ .llseek = seq_lseek,
|
||||||
|
+ .release = single_release,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static void pmc_dbgfs_unregister(struct pmc_dev *pmc)
|
||||||
|
+{
|
||||||
|
+ if (!pmc->dbgfs_dir)
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
+ debugfs_remove_recursive(pmc->dbgfs_dir);
|
||||||
|
+ pmc->dbgfs_dir = NULL;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int pmc_dbgfs_register(struct pmc_dev *pmc)
|
||||||
|
+{
|
||||||
|
+ struct dentry *dir, *f;
|
||||||
|
+
|
||||||
|
+ dir = debugfs_create_dir("pmc_atom", NULL);
|
||||||
|
+ if (!dir)
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+
|
||||||
|
+ f = debugfs_create_file("dev_state", S_IFREG | S_IRUGO,
|
||||||
|
+ dir, pmc, &pmc_dev_state_ops);
|
||||||
|
+ if (!f) {
|
||||||
|
+ dev_err(&pmc->pdev->dev, "dev_states register failed\n");
|
||||||
|
+ goto err;
|
||||||
|
+ }
|
||||||
|
+ f = debugfs_create_file("sleep_state", S_IFREG | S_IRUGO,
|
||||||
|
+ dir, pmc, &pmc_sleep_tmr_ops);
|
||||||
|
+ if (!f) {
|
||||||
|
+ dev_err(&pmc->pdev->dev, "sleep_state register failed\n");
|
||||||
|
+ goto err;
|
||||||
|
+ }
|
||||||
|
+ pmc->dbgfs_dir = dir;
|
||||||
|
+ return 0;
|
||||||
|
+err:
|
||||||
|
+ pmc_dbgfs_unregister(pmc);
|
||||||
|
+ return -ENODEV;
|
||||||
|
+}
|
||||||
|
+#endif /* CONFIG_DEBUG_FS */
|
||||||
|
+
|
||||||
|
+static int pmc_probe(struct pci_dev *pdev,
|
||||||
|
+ const struct pci_device_id *unused)
|
||||||
|
+{
|
||||||
|
+ struct pmc_dev *pmc;
|
||||||
|
+ int ret;
|
||||||
|
+
|
||||||
|
+ ret = pci_enable_device(pdev);
|
||||||
|
+ if (ret < 0) {
|
||||||
|
+ dev_err(&pdev->dev, "error: could not enable device\n");
|
||||||
|
+ goto err_enable_device;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ ret = pci_request_regions(pdev, DRIVER_NAME);
|
||||||
|
+ if (ret) {
|
||||||
|
+ dev_err(&pdev->dev, "error: could not request PCI region\n");
|
||||||
|
+ goto err_request_regions;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ pmc = devm_kzalloc(&pdev->dev, sizeof(struct pmc_dev), GFP_KERNEL);
|
||||||
|
+ if (!pmc) {
|
||||||
|
+ ret = -ENOMEM;
|
||||||
|
+ goto err_devm_kzalloc;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ pmc->pdev = pci_dev_get(pdev);
|
||||||
|
+
|
||||||
|
+ pci_read_config_dword(pdev, PMC_BASE_ADDR_OFFSET, &pmc->base_addr);
|
||||||
|
+ pmc->base_addr &= PMC_BASE_ADDR_MASK;
|
||||||
|
+
|
||||||
|
+ pmc->regmap = devm_ioremap_nocache(&pdev->dev,
|
||||||
|
+ pmc->base_addr, PMC_MMIO_REG_LEN);
|
||||||
|
+ if (!pmc->regmap) {
|
||||||
|
+ dev_err(&pdev->dev, "error: ioremap failed\n");
|
||||||
|
+ ret = -ENOMEM;
|
||||||
|
+ goto err_devm_ioremap;
|
||||||
|
+ }
|
||||||
|
+ pci_set_drvdata(pdev, pmc);
|
||||||
|
+#ifdef CONFIG_DEBUG_FS
|
||||||
|
+ pmc_dbgfs_register(pmc);
|
||||||
|
+#endif /* CONFIG_DEBUG_FS */
|
||||||
|
+
|
||||||
|
+ /* Install power off function */
|
||||||
|
+ pci_read_config_dword(pdev, ACPI_BASE_ADDR_OFFSET, &acpi_base_addr);
|
||||||
|
+ acpi_base_addr &= ACPI_BASE_ADDR_MASK;
|
||||||
|
+ if (acpi_base_addr != 0 && pm_power_off == NULL)
|
||||||
|
+ pm_power_off = pmc_power_off;
|
||||||
|
+ return 0;
|
||||||
|
+err_devm_ioremap:
|
||||||
|
+ pci_dev_put(pdev);
|
||||||
|
+err_devm_kzalloc:
|
||||||
|
+ pci_release_regions(pdev);
|
||||||
|
+err_request_regions:
|
||||||
|
+ pci_disable_device(pdev);
|
||||||
|
+err_enable_device:
|
||||||
|
+ dev_err(&pdev->dev, "error: probe failed\n");
|
||||||
|
+ return ret;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static const struct pci_device_id pmc_pci_ids[] = {
|
||||||
|
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_VLV_PMC) },
|
||||||
|
+ { 0, },
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+MODULE_DEVICE_TABLE(pci, pmc_pci_ids);
|
||||||
|
+
|
||||||
|
+static struct pci_driver pmc_pci_driver = {
|
||||||
|
+ .name = DRIVER_NAME,
|
||||||
|
+ .probe = pmc_probe,
|
||||||
|
+ .id_table = pmc_pci_ids,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+module_pci_driver(pmc_pci_driver);
|
||||||
|
+
|
||||||
|
+MODULE_AUTHOR("Aubrey Li <aubrey.li@linux.intel.com>");
|
||||||
|
+MODULE_DESCRIPTION("Intel Atom SOC Power Management Controller Interface");
|
||||||
|
+MODULE_LICENSE("GPL v2");
|
Loading…
Reference in New Issue
Block a user