diff --git a/0001-ACPI-temporary-dep-solution-for-battery-support.patch b/0001-ACPI-temporary-dep-solution-for-battery-support.patch new file mode 100644 index 000000000..737cd1055 --- /dev/null +++ b/0001-ACPI-temporary-dep-solution-for-battery-support.patch @@ -0,0 +1,148 @@ +From bbadbc67de278123e28dd6f9ee7e88b6ada56ce4 Mon Sep 17 00:00:00 2001 +From: Lan Tianyu +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 + diff --git a/0001-add-device-ID-for-Dell-Venue-8-Pro-s-wireless-adapte.patch b/0001-add-device-ID-for-Dell-Venue-8-Pro-s-wireless-adapte.patch new file mode 100644 index 000000000..dc1c997df --- /dev/null +++ b/0001-add-device-ID-for-Dell-Venue-8-Pro-s-wireless-adapte.patch @@ -0,0 +1,24 @@ +From 69968772071bb8632a57f42ec77a3acdddbe74d7 Mon Sep 17 00:00:00 2001 +From: Adam Williamson +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 + diff --git a/V3-4-5-I2C-ACPI-Add-i2c-ACPI-operation-region-support.patch b/V3-4-5-I2C-ACPI-Add-i2c-ACPI-operation-region-support.patch new file mode 100644 index 000000000..a8d0244e1 --- /dev/null +++ b/V3-4-5-I2C-ACPI-Add-i2c-ACPI-operation-region-support.patch @@ -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" +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 , 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 + +--- +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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++ ++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 */ diff --git a/V3-5-5-I2C-ACPI-Clean-up-I2C-ACPI-code-and-Add-CONFIG_I2C_ACPI-config.patch b/V3-5-5-I2C-ACPI-Clean-up-I2C-ACPI-code-and-Add-CONFIG_I2C_ACPI-config.patch new file mode 100644 index 000000000..40f673e01 --- /dev/null +++ b/V3-5-5-I2C-ACPI-Clean-up-I2C-ACPI-code-and-Add-CONFIG_I2C_ACPI-config.patch @@ -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" +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 , 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 +Signed-off-by: Lan Tianyu + +--- +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) diff --git a/baytrail_gpio_quirk_v3.patch b/baytrail_gpio_quirk_v3.patch new file mode 100644 index 000000000..b294ff51a --- /dev/null +++ b/baytrail_gpio_quirk_v3.patch @@ -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 */ diff --git a/x86-new-Intel-Atom-SoC-power-management-controller-driver.patch b/x86-new-Intel-Atom-SoC-power-management-controller-driver.patch new file mode 100644 index 000000000..b138fe26a --- /dev/null +++ b/x86-new-Intel-Atom-SoC-power-management-controller-driver.patch @@ -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" +X-Patchwork-Id: 3787341 +Message-Id: <5319374A.60308@linux.intel.com> +To: Joe Perches +Cc: "H. Peter Anvin" , + Matthew Garrett , mingo@redhat.com, + tglx@linutronix.de, linux-kernel@vger.kernel.org, + "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 +Reviewed-by: Joe Perches + +--- +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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#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 "); ++MODULE_DESCRIPTION("Intel Atom SOC Power Management Controller Interface"); ++MODULE_LICENSE("GPL v2");