diff options
Diffstat (limited to 'drivers/platform')
47 files changed, 3314 insertions, 736 deletions
diff --git a/drivers/platform/chrome/chromeos_tbmc.c b/drivers/platform/chrome/chromeos_tbmc.c index 1e81f8144c0d..ce259ec9f990 100644 --- a/drivers/platform/chrome/chromeos_tbmc.c +++ b/drivers/platform/chrome/chromeos_tbmc.c @@ -99,7 +99,7 @@ static const struct acpi_device_id chromeos_tbmc_acpi_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, chromeos_tbmc_acpi_device_ids); -static const SIMPLE_DEV_PM_OPS(chromeos_tbmc_pm_ops, NULL, +static SIMPLE_DEV_PM_OPS(chromeos_tbmc_pm_ops, NULL, chromeos_tbmc_resume); static struct acpi_driver chromeos_tbmc_driver = { diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c index 31c8b8c49e45..e1b75775cd4a 100644 --- a/drivers/platform/chrome/cros_ec_lpc.c +++ b/drivers/platform/chrome/cros_ec_lpc.c @@ -25,14 +25,16 @@ #include <linux/dmi.h> #include <linux/delay.h> #include <linux/io.h> +#include <linux/interrupt.h> #include <linux/mfd/cros_ec.h> #include <linux/mfd/cros_ec_commands.h> -#include <linux/mfd/cros_ec_lpc_reg.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/printk.h> #include <linux/suspend.h> +#include "cros_ec_lpc_reg.h" + #define DRV_NAME "cros_ec_lpcs" #define ACPI_DRV_NAME "GOOG0004" @@ -248,7 +250,7 @@ static int cros_ec_lpc_probe(struct platform_device *pdev) acpi_status status; struct cros_ec_device *ec_dev; u8 buf[2]; - int ret; + int irq, ret; if (!devm_request_region(dev, EC_LPC_ADDR_MEMMAP, EC_MEMMAP_SIZE, dev_name(dev))) { @@ -287,6 +289,18 @@ static int cros_ec_lpc_probe(struct platform_device *pdev) sizeof(struct ec_response_get_protocol_info); ec_dev->dout_size = sizeof(struct ec_host_request); + /* + * Some boards do not have an IRQ allotted for cros_ec_lpc, + * which makes ENXIO an expected (and safe) scenario. + */ + irq = platform_get_irq(pdev, 0); + if (irq > 0) + ec_dev->irq = irq; + else if (irq != -ENXIO) { + dev_err(dev, "couldn't retrieve IRQ number (%d)\n", irq); + return irq; + } + ret = cros_ec_register(ec_dev); if (ret) { dev_err(dev, "couldn't register ec_dev (%d)\n", ret); diff --git a/drivers/platform/chrome/cros_ec_lpc_mec.c b/drivers/platform/chrome/cros_ec_lpc_mec.c index 2eda2c2fc210..c4edfa83e493 100644 --- a/drivers/platform/chrome/cros_ec_lpc_mec.c +++ b/drivers/platform/chrome/cros_ec_lpc_mec.c @@ -24,10 +24,11 @@ #include <linux/delay.h> #include <linux/io.h> #include <linux/mfd/cros_ec_commands.h> -#include <linux/mfd/cros_ec_lpc_mec.h> #include <linux/mutex.h> #include <linux/types.h> +#include "cros_ec_lpc_mec.h" + /* * This mutex must be held while accessing the EMI unit. We can't rely on the * EC mutex because memmap data may be accessed without it being held. diff --git a/drivers/platform/chrome/cros_ec_lpc_mec.h b/drivers/platform/chrome/cros_ec_lpc_mec.h new file mode 100644 index 000000000000..105068c0e919 --- /dev/null +++ b/drivers/platform/chrome/cros_ec_lpc_mec.h @@ -0,0 +1,90 @@ +/* + * cros_ec_lpc_mec - LPC variant I/O for Microchip EC + * + * Copyright (C) 2016 Google, Inc + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + * This driver uses the Chrome OS EC byte-level message-based protocol for + * communicating the keyboard state (which keys are pressed) from a keyboard EC + * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing, + * but everything else (including deghosting) is done here. The main + * motivation for this is to keep the EC firmware as simple as possible, since + * it cannot be easily upgraded and EC flash/IRAM space is relatively + * expensive. + */ + +#ifndef __CROS_EC_LPC_MEC_H +#define __CROS_EC_LPC_MEC_H + +#include <linux/mfd/cros_ec_commands.h> + +enum cros_ec_lpc_mec_emi_access_mode { + /* 8-bit access */ + ACCESS_TYPE_BYTE = 0x0, + /* 16-bit access */ + ACCESS_TYPE_WORD = 0x1, + /* 32-bit access */ + ACCESS_TYPE_LONG = 0x2, + /* + * 32-bit access, read or write of MEC_EMI_EC_DATA_B3 causes the + * EC data register to be incremented. + */ + ACCESS_TYPE_LONG_AUTO_INCREMENT = 0x3, +}; + +enum cros_ec_lpc_mec_io_type { + MEC_IO_READ, + MEC_IO_WRITE, +}; + +/* Access IO ranges 0x800 thru 0x9ff using EMI interface instead of LPC */ +#define MEC_EMI_RANGE_START EC_HOST_CMD_REGION0 +#define MEC_EMI_RANGE_END (EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SIZE) + +/* EMI registers are relative to base */ +#define MEC_EMI_BASE 0x800 +#define MEC_EMI_HOST_TO_EC (MEC_EMI_BASE + 0) +#define MEC_EMI_EC_TO_HOST (MEC_EMI_BASE + 1) +#define MEC_EMI_EC_ADDRESS_B0 (MEC_EMI_BASE + 2) +#define MEC_EMI_EC_ADDRESS_B1 (MEC_EMI_BASE + 3) +#define MEC_EMI_EC_DATA_B0 (MEC_EMI_BASE + 4) +#define MEC_EMI_EC_DATA_B1 (MEC_EMI_BASE + 5) +#define MEC_EMI_EC_DATA_B2 (MEC_EMI_BASE + 6) +#define MEC_EMI_EC_DATA_B3 (MEC_EMI_BASE + 7) + +/* + * cros_ec_lpc_mec_init + * + * Initialize MEC I/O. + */ +void cros_ec_lpc_mec_init(void); + +/* + * cros_ec_lpc_mec_destroy + * + * Cleanup MEC I/O. + */ +void cros_ec_lpc_mec_destroy(void); + +/** + * cros_ec_lpc_io_bytes_mec - Read / write bytes to MEC EMI port + * + * @io_type: MEC_IO_READ or MEC_IO_WRITE, depending on request + * @offset: Base read / write address + * @length: Number of bytes to read / write + * @buf: Destination / source buffer + * + * @return 8-bit checksum of all bytes read / written + */ +u8 cros_ec_lpc_io_bytes_mec(enum cros_ec_lpc_mec_io_type io_type, + unsigned int offset, unsigned int length, u8 *buf); + +#endif /* __CROS_EC_LPC_MEC_H */ diff --git a/drivers/platform/chrome/cros_ec_lpc_reg.c b/drivers/platform/chrome/cros_ec_lpc_reg.c index dcc7a3e30604..fc23d535c404 100644 --- a/drivers/platform/chrome/cros_ec_lpc_reg.c +++ b/drivers/platform/chrome/cros_ec_lpc_reg.c @@ -24,7 +24,8 @@ #include <linux/io.h> #include <linux/mfd/cros_ec.h> #include <linux/mfd/cros_ec_commands.h> -#include <linux/mfd/cros_ec_lpc_mec.h> + +#include "cros_ec_lpc_mec.h" static u8 lpc_read_bytes(unsigned int offset, unsigned int length, u8 *dest) { diff --git a/drivers/platform/chrome/cros_ec_lpc_reg.h b/drivers/platform/chrome/cros_ec_lpc_reg.h new file mode 100644 index 000000000000..1c12c38b306a --- /dev/null +++ b/drivers/platform/chrome/cros_ec_lpc_reg.h @@ -0,0 +1,61 @@ +/* + * cros_ec_lpc_reg - LPC access to the Chrome OS Embedded Controller + * + * Copyright (C) 2016 Google, Inc + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + * This driver uses the Chrome OS EC byte-level message-based protocol for + * communicating the keyboard state (which keys are pressed) from a keyboard EC + * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing, + * but everything else (including deghosting) is done here. The main + * motivation for this is to keep the EC firmware as simple as possible, since + * it cannot be easily upgraded and EC flash/IRAM space is relatively + * expensive. + */ + +#ifndef __CROS_EC_LPC_REG_H +#define __CROS_EC_LPC_REG_H + +/** + * cros_ec_lpc_read_bytes - Read bytes from a given LPC-mapped address. + * Returns 8-bit checksum of all bytes read. + * + * @offset: Base read address + * @length: Number of bytes to read + * @dest: Destination buffer + */ +u8 cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length, u8 *dest); + +/** + * cros_ec_lpc_write_bytes - Write bytes to a given LPC-mapped address. + * Returns 8-bit checksum of all bytes written. + * + * @offset: Base write address + * @length: Number of bytes to write + * @msg: Write data buffer + */ +u8 cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg); + +/** + * cros_ec_lpc_reg_init + * + * Initialize register I/O. + */ +void cros_ec_lpc_reg_init(void); + +/** + * cros_ec_lpc_reg_destroy + * + * Cleanup reg I/O. + */ +void cros_ec_lpc_reg_destroy(void); + +#endif /* __CROS_EC_LPC_REG_H */ diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index 2da567540c2d..7c639006252e 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2012 Intel, Inc. * Copyright (C) 2013 Intel, Inc. @@ -46,7 +47,6 @@ * exchange is properly mapped during a transfer. */ - #include <linux/module.h> #include <linux/mod_devicetable.h> #include <linux/interrupt.h> @@ -59,10 +59,11 @@ #include <linux/bitops.h> #include <linux/slab.h> #include <linux/io.h> -#include <linux/goldfish.h> #include <linux/dma-mapping.h> #include <linux/mm.h> #include <linux/acpi.h> +#include <linux/bug.h> +#include "goldfish_pipe_qemu.h" /* * Update this when something changes in the driver's behavior so the host @@ -73,71 +74,6 @@ enum { PIPE_CURRENT_DEVICE_VERSION = 2 }; -/* - * IMPORTANT: The following constants must match the ones used and defined - * in external/qemu/hw/goldfish_pipe.c in the Android source tree. - */ - -/* List of bitflags returned in status of CMD_POLL command */ -enum PipePollFlags { - PIPE_POLL_IN = 1 << 0, - PIPE_POLL_OUT = 1 << 1, - PIPE_POLL_HUP = 1 << 2 -}; - -/* Possible status values used to signal errors - see goldfish_pipe_error_convert */ -enum PipeErrors { - PIPE_ERROR_INVAL = -1, - PIPE_ERROR_AGAIN = -2, - PIPE_ERROR_NOMEM = -3, - PIPE_ERROR_IO = -4 -}; - -/* Bit-flags used to signal events from the emulator */ -enum PipeWakeFlags { - PIPE_WAKE_CLOSED = 1 << 0, /* emulator closed pipe */ - PIPE_WAKE_READ = 1 << 1, /* pipe can now be read from */ - PIPE_WAKE_WRITE = 1 << 2 /* pipe can now be written to */ -}; - -/* Bit flags for the 'flags' field */ -enum PipeFlagsBits { - BIT_CLOSED_ON_HOST = 0, /* pipe closed by host */ - BIT_WAKE_ON_WRITE = 1, /* want to be woken on writes */ - BIT_WAKE_ON_READ = 2, /* want to be woken on reads */ -}; - -enum PipeRegs { - PIPE_REG_CMD = 0, - - PIPE_REG_SIGNAL_BUFFER_HIGH = 4, - PIPE_REG_SIGNAL_BUFFER = 8, - PIPE_REG_SIGNAL_BUFFER_COUNT = 12, - - PIPE_REG_OPEN_BUFFER_HIGH = 20, - PIPE_REG_OPEN_BUFFER = 24, - - PIPE_REG_VERSION = 36, - - PIPE_REG_GET_SIGNALLED = 48, -}; - -enum PipeCmdCode { - PIPE_CMD_OPEN = 1, /* to be used by the pipe device itself */ - PIPE_CMD_CLOSE, - PIPE_CMD_POLL, - PIPE_CMD_WRITE, - PIPE_CMD_WAKE_ON_WRITE, - PIPE_CMD_READ, - PIPE_CMD_WAKE_ON_READ, - - /* - * TODO(zyy): implement a deferred read/write execution to allow - * parallel processing of pipe operations on the host. - */ - PIPE_CMD_WAKE_ON_DONE_IO, -}; - enum { MAX_BUFFERS_PER_COMMAND = 336, MAX_SIGNALLED_PIPES = 64, @@ -145,14 +81,12 @@ enum { }; struct goldfish_pipe_dev; -struct goldfish_pipe; -struct goldfish_pipe_command; /* A per-pipe command structure, shared with the host */ struct goldfish_pipe_command { - s32 cmd; /* PipeCmdCode, guest -> host */ - s32 id; /* pipe id, guest -> host */ - s32 status; /* command execution status, host -> guest */ + s32 cmd; /* PipeCmdCode, guest -> host */ + s32 id; /* pipe id, guest -> host */ + s32 status; /* command execution status, host -> guest */ s32 reserved; /* to pad to 64-bit boundary */ union { /* Parameters for PIPE_CMD_{READ,WRITE} */ @@ -184,19 +118,21 @@ struct open_command_param { /* Device-level set of buffers shared with the host */ struct goldfish_pipe_dev_buffers { struct open_command_param open_command_params; - struct signalled_pipe_buffer signalled_pipe_buffers[ - MAX_SIGNALLED_PIPES]; + struct signalled_pipe_buffer + signalled_pipe_buffers[MAX_SIGNALLED_PIPES]; }; /* This data type models a given pipe instance */ struct goldfish_pipe { /* pipe ID - index into goldfish_pipe_dev::pipes array */ u32 id; + /* The wake flags pipe is waiting for * Note: not protected with any lock, uses atomic operations * and barriers to make it thread-safe. */ unsigned long flags; + /* wake flags host have signalled, * - protected by goldfish_pipe_dev::lock */ @@ -220,8 +156,12 @@ struct goldfish_pipe { /* A wake queue for sleeping until host signals an event */ wait_queue_head_t wake_queue; + /* Pointer to the parent goldfish_pipe_dev instance */ struct goldfish_pipe_dev *dev; + + /* A buffer of pages, too large to fit into a stack frame */ + struct page *pages[MAX_BUFFERS_PER_COMMAND]; }; /* The global driver data. Holds a reference to the i/o page used to @@ -229,6 +169,9 @@ struct goldfish_pipe { * waiting to be awoken. */ struct goldfish_pipe_dev { + /* A magic number to check if this is an instance of this struct */ + void *magic; + /* * Global device spinlock. Protects the following members: * - pipes, pipes_capacity @@ -261,15 +204,22 @@ struct goldfish_pipe_dev { /* Head of a doubly linked list of signalled pipes */ struct goldfish_pipe *first_signalled_pipe; + /* ptr to platform device's device struct */ + struct device *pdev_dev; + /* Some device-specific data */ int irq; int version; unsigned char __iomem *base; -}; -static struct goldfish_pipe_dev pipe_dev[1] = {}; + /* an irq tasklet to run goldfish_interrupt_task */ + struct tasklet_struct irq_tasklet; -static int goldfish_cmd_locked(struct goldfish_pipe *pipe, enum PipeCmdCode cmd) + struct miscdevice miscdev; +}; + +static int goldfish_pipe_cmd_locked(struct goldfish_pipe *pipe, + enum PipeCmdCode cmd) { pipe->command_buffer->cmd = cmd; /* failure by default */ @@ -278,13 +228,13 @@ static int goldfish_cmd_locked(struct goldfish_pipe *pipe, enum PipeCmdCode cmd) return pipe->command_buffer->status; } -static int goldfish_cmd(struct goldfish_pipe *pipe, enum PipeCmdCode cmd) +static int goldfish_pipe_cmd(struct goldfish_pipe *pipe, enum PipeCmdCode cmd) { int status; if (mutex_lock_interruptible(&pipe->lock)) return PIPE_ERROR_IO; - status = goldfish_cmd_locked(pipe, cmd); + status = goldfish_pipe_cmd_locked(pipe, cmd); mutex_unlock(&pipe->lock); return status; } @@ -307,10 +257,12 @@ static int goldfish_pipe_error_convert(int status) } } -static int pin_user_pages(unsigned long first_page, unsigned long last_page, - unsigned int last_page_size, int is_write, - struct page *pages[MAX_BUFFERS_PER_COMMAND], - unsigned int *iter_last_page_size) +static int pin_user_pages(unsigned long first_page, + unsigned long last_page, + unsigned int last_page_size, + int is_write, + struct page *pages[MAX_BUFFERS_PER_COMMAND], + unsigned int *iter_last_page_size) { int ret; int requested_pages = ((last_page - first_page) >> PAGE_SHIFT) + 1; @@ -322,18 +274,18 @@ static int pin_user_pages(unsigned long first_page, unsigned long last_page, *iter_last_page_size = last_page_size; } - ret = get_user_pages_fast( - first_page, requested_pages, !is_write, pages); + ret = get_user_pages_fast(first_page, requested_pages, !is_write, + pages); if (ret <= 0) return -EFAULT; if (ret < requested_pages) *iter_last_page_size = PAGE_SIZE; - return ret; + return ret; } static void release_user_pages(struct page **pages, int pages_count, - int is_write, s32 consumed_size) + int is_write, s32 consumed_size) { int i; @@ -345,12 +297,15 @@ static void release_user_pages(struct page **pages, int pages_count, } /* Populate the call parameters, merging adjacent pages together */ -static void populate_rw_params( - struct page **pages, int pages_count, - unsigned long address, unsigned long address_end, - unsigned long first_page, unsigned long last_page, - unsigned int iter_last_page_size, int is_write, - struct goldfish_pipe_command *command) +static void populate_rw_params(struct page **pages, + int pages_count, + unsigned long address, + unsigned long address_end, + unsigned long first_page, + unsigned long last_page, + unsigned int iter_last_page_size, + int is_write, + struct goldfish_pipe_command *command) { /* * Process the first page separately - it's the only page that @@ -382,55 +337,59 @@ static void populate_rw_params( } static int transfer_max_buffers(struct goldfish_pipe *pipe, - unsigned long address, unsigned long address_end, int is_write, - unsigned long last_page, unsigned int last_page_size, - s32 *consumed_size, int *status) + unsigned long address, + unsigned long address_end, + int is_write, + unsigned long last_page, + unsigned int last_page_size, + s32 *consumed_size, + int *status) { - static struct page *pages[MAX_BUFFERS_PER_COMMAND]; unsigned long first_page = address & PAGE_MASK; unsigned int iter_last_page_size; - int pages_count = pin_user_pages(first_page, last_page, - last_page_size, is_write, - pages, &iter_last_page_size); - - if (pages_count < 0) - return pages_count; + int pages_count; /* Serialize access to the pipe command buffers */ if (mutex_lock_interruptible(&pipe->lock)) return -ERESTARTSYS; - populate_rw_params(pages, pages_count, address, address_end, - first_page, last_page, iter_last_page_size, is_write, - pipe->command_buffer); + pages_count = pin_user_pages(first_page, last_page, + last_page_size, is_write, + pipe->pages, &iter_last_page_size); + if (pages_count < 0) { + mutex_unlock(&pipe->lock); + return pages_count; + } + + populate_rw_params(pipe->pages, pages_count, address, address_end, + first_page, last_page, iter_last_page_size, is_write, + pipe->command_buffer); /* Transfer the data */ - *status = goldfish_cmd_locked(pipe, + *status = goldfish_pipe_cmd_locked(pipe, is_write ? PIPE_CMD_WRITE : PIPE_CMD_READ); *consumed_size = pipe->command_buffer->rw_params.consumed_size; - release_user_pages(pages, pages_count, is_write, *consumed_size); + release_user_pages(pipe->pages, pages_count, is_write, *consumed_size); mutex_unlock(&pipe->lock); - return 0; } static int wait_for_host_signal(struct goldfish_pipe *pipe, int is_write) { - u32 wakeBit = is_write ? BIT_WAKE_ON_WRITE : BIT_WAKE_ON_READ; + u32 wake_bit = is_write ? BIT_WAKE_ON_WRITE : BIT_WAKE_ON_READ; - set_bit(wakeBit, &pipe->flags); + set_bit(wake_bit, &pipe->flags); /* Tell the emulator we're going to wait for a wake event */ - (void)goldfish_cmd(pipe, + goldfish_pipe_cmd(pipe, is_write ? PIPE_CMD_WAKE_ON_WRITE : PIPE_CMD_WAKE_ON_READ); - while (test_bit(wakeBit, &pipe->flags)) { - if (wait_event_interruptible( - pipe->wake_queue, - !test_bit(wakeBit, &pipe->flags))) + while (test_bit(wake_bit, &pipe->flags)) { + if (wait_event_interruptible(pipe->wake_queue, + !test_bit(wake_bit, &pipe->flags))) return -ERESTARTSYS; if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags)) @@ -441,7 +400,9 @@ static int wait_for_host_signal(struct goldfish_pipe *pipe, int is_write) } static ssize_t goldfish_pipe_read_write(struct file *filp, - char __user *buffer, size_t bufflen, int is_write) + char __user *buffer, + size_t bufflen, + int is_write) { struct goldfish_pipe *pipe = filp->private_data; int count = 0, ret = -EINVAL; @@ -456,7 +417,7 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, return 0; /* Check the buffer range for access */ if (unlikely(!access_ok(is_write ? VERIFY_WRITE : VERIFY_READ, - buffer, bufflen))) + buffer, bufflen))) return -EFAULT; address = (unsigned long)buffer; @@ -469,8 +430,8 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, int status; ret = transfer_max_buffers(pipe, address, address_end, is_write, - last_page, last_page_size, &consumed_size, - &status); + last_page, last_page_size, + &consumed_size, &status); if (ret < 0) break; @@ -496,7 +457,8 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, * err. */ if (status != PIPE_ERROR_AGAIN) - pr_info_ratelimited("goldfish_pipe: backend error %d on %s\n", + dev_err_ratelimited(pipe->dev->pdev_dev, + "backend error %d on %s\n", status, is_write ? "write" : "read"); break; } @@ -522,19 +484,21 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, } static ssize_t goldfish_pipe_read(struct file *filp, char __user *buffer, - size_t bufflen, loff_t *ppos) + size_t bufflen, loff_t *ppos) { return goldfish_pipe_read_write(filp, buffer, bufflen, - /* is_write */ 0); + /* is_write */ 0); } static ssize_t goldfish_pipe_write(struct file *filp, - const char __user *buffer, size_t bufflen, - loff_t *ppos) + const char __user *buffer, size_t bufflen, + loff_t *ppos) { - return goldfish_pipe_read_write(filp, - /* cast away the const */(char __user *)buffer, bufflen, - /* is_write */ 1); + /* cast away the const */ + char __user *no_const_buffer = (char __user *)buffer; + + return goldfish_pipe_read_write(filp, no_const_buffer, bufflen, + /* is_write */ 1); } static __poll_t goldfish_pipe_poll(struct file *filp, poll_table *wait) @@ -545,7 +509,7 @@ static __poll_t goldfish_pipe_poll(struct file *filp, poll_table *wait) poll_wait(filp, &pipe->wake_queue, wait); - status = goldfish_cmd(pipe, PIPE_CMD_POLL); + status = goldfish_pipe_cmd(pipe, PIPE_CMD_POLL); if (status < 0) return -ERESTARTSYS; @@ -562,7 +526,7 @@ static __poll_t goldfish_pipe_poll(struct file *filp, poll_table *wait) } static void signalled_pipes_add_locked(struct goldfish_pipe_dev *dev, - u32 id, u32 flags) + u32 id, u32 flags) { struct goldfish_pipe *pipe; @@ -574,8 +538,8 @@ static void signalled_pipes_add_locked(struct goldfish_pipe_dev *dev, return; pipe->signalled_flags |= flags; - if (pipe->prev_signalled || pipe->next_signalled - || dev->first_signalled_pipe == pipe) + if (pipe->prev_signalled || pipe->next_signalled || + dev->first_signalled_pipe == pipe) return; /* already in the list */ pipe->next_signalled = dev->first_signalled_pipe; if (dev->first_signalled_pipe) @@ -584,7 +548,8 @@ static void signalled_pipes_add_locked(struct goldfish_pipe_dev *dev, } static void signalled_pipes_remove_locked(struct goldfish_pipe_dev *dev, - struct goldfish_pipe *pipe) { + struct goldfish_pipe *pipe) +{ if (pipe->prev_signalled) pipe->prev_signalled->next_signalled = pipe->next_signalled; if (pipe->next_signalled) @@ -623,10 +588,10 @@ static struct goldfish_pipe *signalled_pipes_pop_front( return pipe; } -static void goldfish_interrupt_task(unsigned long unused) +static void goldfish_interrupt_task(unsigned long dev_addr) { - struct goldfish_pipe_dev *dev = pipe_dev; /* Iterate over the signalled pipes and wake them one by one */ + struct goldfish_pipe_dev *dev = (struct goldfish_pipe_dev *)dev_addr; struct goldfish_pipe *pipe; int wakes; @@ -646,7 +611,9 @@ static void goldfish_interrupt_task(unsigned long unused) wake_up_interruptible(&pipe->wake_queue); } } -static DECLARE_TASKLET(goldfish_interrupt_tasklet, goldfish_interrupt_task, 0); + +static void goldfish_pipe_device_deinit(struct platform_device *pdev, + struct goldfish_pipe_dev *dev); /* * The general idea of the interrupt handling: @@ -668,7 +635,7 @@ static irqreturn_t goldfish_pipe_interrupt(int irq, void *dev_id) unsigned long flags; struct goldfish_pipe_dev *dev = dev_id; - if (dev != pipe_dev) + if (dev->magic != &goldfish_pipe_device_deinit) return IRQ_NONE; /* Request the signalled pipes from the device */ @@ -689,7 +656,7 @@ static irqreturn_t goldfish_pipe_interrupt(int irq, void *dev_id) spin_unlock_irqrestore(&dev->lock, flags); - tasklet_schedule(&goldfish_interrupt_tasklet); + tasklet_schedule(&dev->irq_tasklet); return IRQ_HANDLED; } @@ -702,7 +669,10 @@ static int get_free_pipe_id_locked(struct goldfish_pipe_dev *dev) return id; { - /* Reallocate the array */ + /* Reallocate the array. + * Since get_free_pipe_id_locked runs with interrupts disabled, + * we don't want to make calls that could lead to sleep. + */ u32 new_capacity = 2 * dev->pipes_capacity; struct goldfish_pipe **pipes = kcalloc(new_capacity, sizeof(*pipes), GFP_ATOMIC); @@ -717,6 +687,14 @@ static int get_free_pipe_id_locked(struct goldfish_pipe_dev *dev) return id; } +/* A helper function to get the instance of goldfish_pipe_dev from file */ +static struct goldfish_pipe_dev *to_goldfish_pipe_dev(struct file *file) +{ + struct miscdevice *miscdev = file->private_data; + + return container_of(miscdev, struct goldfish_pipe_dev, miscdev); +} + /** * goldfish_pipe_open - open a channel to the AVD * @inode: inode of device @@ -730,14 +708,15 @@ static int get_free_pipe_id_locked(struct goldfish_pipe_dev *dev) */ static int goldfish_pipe_open(struct inode *inode, struct file *file) { - struct goldfish_pipe_dev *dev = pipe_dev; + struct goldfish_pipe_dev *dev = to_goldfish_pipe_dev(file); unsigned long flags; int id; int status; /* Allocate new pipe kernel object */ struct goldfish_pipe *pipe = kzalloc(sizeof(*pipe), GFP_KERNEL); - if (pipe == NULL) + + if (!pipe) return -ENOMEM; pipe->dev = dev; @@ -748,6 +727,7 @@ static int goldfish_pipe_open(struct inode *inode, struct file *file) * Command buffer needs to be allocated on its own page to make sure * it is physically contiguous in host's address space. */ + BUILD_BUG_ON(sizeof(struct goldfish_pipe_command) > PAGE_SIZE); pipe->command_buffer = (struct goldfish_pipe_command *)__get_free_page(GFP_KERNEL); if (!pipe->command_buffer) { @@ -772,7 +752,7 @@ static int goldfish_pipe_open(struct inode *inode, struct file *file) MAX_BUFFERS_PER_COMMAND; dev->buffers->open_command_params.command_buffer_ptr = (u64)(unsigned long)__pa(pipe->command_buffer); - status = goldfish_cmd_locked(pipe, PIPE_CMD_OPEN); + status = goldfish_pipe_cmd_locked(pipe, PIPE_CMD_OPEN); spin_unlock_irqrestore(&dev->lock, flags); if (status < 0) goto err_cmd; @@ -798,7 +778,7 @@ static int goldfish_pipe_release(struct inode *inode, struct file *filp) struct goldfish_pipe_dev *dev = pipe->dev; /* The guest is closing the channel, so tell the emulator right now */ - (void)goldfish_cmd(pipe, PIPE_CMD_CLOSE); + goldfish_pipe_cmd(pipe, PIPE_CMD_CLOSE); spin_lock_irqsave(&dev->lock, flags); dev->pipes[pipe->id] = NULL; @@ -820,36 +800,55 @@ static const struct file_operations goldfish_pipe_fops = { .release = goldfish_pipe_release, }; -static struct miscdevice goldfish_pipe_dev = { - .minor = MISC_DYNAMIC_MINOR, - .name = "goldfish_pipe", - .fops = &goldfish_pipe_fops, -}; +static void init_miscdevice(struct miscdevice *miscdev) +{ + memset(miscdev, 0, sizeof(*miscdev)); + + miscdev->minor = MISC_DYNAMIC_MINOR; + miscdev->name = "goldfish_pipe"; + miscdev->fops = &goldfish_pipe_fops; +} + +static void write_pa_addr(void *addr, void __iomem *portl, void __iomem *porth) +{ + const unsigned long paddr = __pa(addr); + + writel(upper_32_bits(paddr), porth); + writel(lower_32_bits(paddr), portl); +} -static int goldfish_pipe_device_init(struct platform_device *pdev) +static int goldfish_pipe_device_init(struct platform_device *pdev, + struct goldfish_pipe_dev *dev) { - char *page; - struct goldfish_pipe_dev *dev = pipe_dev; - int err = devm_request_irq(&pdev->dev, dev->irq, - goldfish_pipe_interrupt, - IRQF_SHARED, "goldfish_pipe", dev); + int err; + + tasklet_init(&dev->irq_tasklet, &goldfish_interrupt_task, + (unsigned long)dev); + + err = devm_request_irq(&pdev->dev, dev->irq, + goldfish_pipe_interrupt, + IRQF_SHARED, "goldfish_pipe", dev); if (err) { dev_err(&pdev->dev, "unable to allocate IRQ for v2\n"); return err; } - err = misc_register(&goldfish_pipe_dev); + init_miscdevice(&dev->miscdev); + err = misc_register(&dev->miscdev); if (err) { dev_err(&pdev->dev, "unable to register v2 device\n"); return err; } + dev->pdev_dev = &pdev->dev; dev->first_signalled_pipe = NULL; dev->pipes_capacity = INITIAL_PIPES_CAPACITY; dev->pipes = kcalloc(dev->pipes_capacity, sizeof(*dev->pipes), - GFP_KERNEL); - if (!dev->pipes) + GFP_KERNEL); + if (!dev->pipes) { + misc_deregister(&dev->miscdev); return -ENOMEM; + } /* * We're going to pass two buffers, open_command_params and @@ -857,75 +856,67 @@ static int goldfish_pipe_device_init(struct platform_device *pdev) * needs to be contained in a single physical page. The easiest choice * is to just allocate a page and place the buffers in it. */ - if (WARN_ON(sizeof(*dev->buffers) > PAGE_SIZE)) - return -ENOMEM; - - page = (char *)__get_free_page(GFP_KERNEL); - if (!page) { + BUILD_BUG_ON(sizeof(struct goldfish_pipe_dev_buffers) > PAGE_SIZE); + dev->buffers = (struct goldfish_pipe_dev_buffers *) + __get_free_page(GFP_KERNEL); + if (!dev->buffers) { kfree(dev->pipes); + misc_deregister(&dev->miscdev); return -ENOMEM; } - dev->buffers = (struct goldfish_pipe_dev_buffers *)page; /* Send the buffer addresses to the host */ - { - u64 paddr = __pa(&dev->buffers->signalled_pipe_buffers); - - writel((u32)(unsigned long)(paddr >> 32), - dev->base + PIPE_REG_SIGNAL_BUFFER_HIGH); - writel((u32)(unsigned long)paddr, - dev->base + PIPE_REG_SIGNAL_BUFFER); - writel((u32)MAX_SIGNALLED_PIPES, - dev->base + PIPE_REG_SIGNAL_BUFFER_COUNT); - - paddr = __pa(&dev->buffers->open_command_params); - writel((u32)(unsigned long)(paddr >> 32), - dev->base + PIPE_REG_OPEN_BUFFER_HIGH); - writel((u32)(unsigned long)paddr, - dev->base + PIPE_REG_OPEN_BUFFER); - } + write_pa_addr(&dev->buffers->signalled_pipe_buffers, + dev->base + PIPE_REG_SIGNAL_BUFFER, + dev->base + PIPE_REG_SIGNAL_BUFFER_HIGH); + + writel(MAX_SIGNALLED_PIPES, + dev->base + PIPE_REG_SIGNAL_BUFFER_COUNT); + + write_pa_addr(&dev->buffers->open_command_params, + dev->base + PIPE_REG_OPEN_BUFFER, + dev->base + PIPE_REG_OPEN_BUFFER_HIGH); + + platform_set_drvdata(pdev, dev); return 0; } -static void goldfish_pipe_device_deinit(struct platform_device *pdev) +static void goldfish_pipe_device_deinit(struct platform_device *pdev, + struct goldfish_pipe_dev *dev) { - struct goldfish_pipe_dev *dev = pipe_dev; - - misc_deregister(&goldfish_pipe_dev); + misc_deregister(&dev->miscdev); + tasklet_kill(&dev->irq_tasklet); kfree(dev->pipes); free_page((unsigned long)dev->buffers); } static int goldfish_pipe_probe(struct platform_device *pdev) { - int err; struct resource *r; - struct goldfish_pipe_dev *dev = pipe_dev; + struct goldfish_pipe_dev *dev; - if (WARN_ON(sizeof(struct goldfish_pipe_command) > PAGE_SIZE)) + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) return -ENOMEM; - /* not thread safe, but this should not happen */ - WARN_ON(dev->base != NULL); - + dev->magic = &goldfish_pipe_device_deinit; spin_lock_init(&dev->lock); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (r == NULL || resource_size(r) < PAGE_SIZE) { + if (!r || resource_size(r) < PAGE_SIZE) { dev_err(&pdev->dev, "can't allocate i/o page\n"); return -EINVAL; } dev->base = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE); - if (dev->base == NULL) { + if (!dev->base) { dev_err(&pdev->dev, "ioremap failed\n"); return -EINVAL; } r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (r == NULL) { - err = -EINVAL; - goto error; - } + if (!r) + return -EINVAL; + dev->irq = r->start; /* @@ -935,25 +926,19 @@ static int goldfish_pipe_probe(struct platform_device *pdev) * reading device version back: this allows the host implementation to * detect the old driver (if there was no version write before read). */ - writel((u32)PIPE_DRIVER_VERSION, dev->base + PIPE_REG_VERSION); + writel(PIPE_DRIVER_VERSION, dev->base + PIPE_REG_VERSION); dev->version = readl(dev->base + PIPE_REG_VERSION); if (WARN_ON(dev->version < PIPE_CURRENT_DEVICE_VERSION)) return -EINVAL; - err = goldfish_pipe_device_init(pdev); - if (!err) - return 0; - -error: - dev->base = NULL; - return err; + return goldfish_pipe_device_init(pdev, dev); } static int goldfish_pipe_remove(struct platform_device *pdev) { - struct goldfish_pipe_dev *dev = pipe_dev; - goldfish_pipe_device_deinit(pdev); - dev->base = NULL; + struct goldfish_pipe_dev *dev = platform_get_drvdata(pdev); + + goldfish_pipe_device_deinit(pdev, dev); return 0; } @@ -981,4 +966,4 @@ static struct platform_driver goldfish_pipe_driver = { module_platform_driver(goldfish_pipe_driver); MODULE_AUTHOR("David Turner <digit@google.com>"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/goldfish/goldfish_pipe_qemu.h b/drivers/platform/goldfish/goldfish_pipe_qemu.h new file mode 100644 index 000000000000..b4d78c108afd --- /dev/null +++ b/drivers/platform/goldfish/goldfish_pipe_qemu.h @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * IMPORTANT: The following constants must match the ones used and defined in + * external/qemu/include/hw/misc/goldfish_pipe.h + */ + +#ifndef GOLDFISH_PIPE_QEMU_H +#define GOLDFISH_PIPE_QEMU_H + +/* List of bitflags returned in status of CMD_POLL command */ +enum PipePollFlags { + PIPE_POLL_IN = 1 << 0, + PIPE_POLL_OUT = 1 << 1, + PIPE_POLL_HUP = 1 << 2 +}; + +/* Possible status values used to signal errors */ +enum PipeErrors { + PIPE_ERROR_INVAL = -1, + PIPE_ERROR_AGAIN = -2, + PIPE_ERROR_NOMEM = -3, + PIPE_ERROR_IO = -4 +}; + +/* Bit-flags used to signal events from the emulator */ +enum PipeWakeFlags { + /* emulator closed pipe */ + PIPE_WAKE_CLOSED = 1 << 0, + + /* pipe can now be read from */ + PIPE_WAKE_READ = 1 << 1, + + /* pipe can now be written to */ + PIPE_WAKE_WRITE = 1 << 2, + + /* unlock this pipe's DMA buffer */ + PIPE_WAKE_UNLOCK_DMA = 1 << 3, + + /* unlock DMA buffer of the pipe shared to this pipe */ + PIPE_WAKE_UNLOCK_DMA_SHARED = 1 << 4, +}; + +/* Possible pipe closing reasons */ +enum PipeCloseReason { + /* guest sent a close command */ + PIPE_CLOSE_GRACEFUL = 0, + + /* guest rebooted, we're closing the pipes */ + PIPE_CLOSE_REBOOT = 1, + + /* close old pipes on snapshot load */ + PIPE_CLOSE_LOAD_SNAPSHOT = 2, + + /* some unrecoverable error on the pipe */ + PIPE_CLOSE_ERROR = 3, +}; + +/* Bit flags for the 'flags' field */ +enum PipeFlagsBits { + BIT_CLOSED_ON_HOST = 0, /* pipe closed by host */ + BIT_WAKE_ON_WRITE = 1, /* want to be woken on writes */ + BIT_WAKE_ON_READ = 2, /* want to be woken on reads */ +}; + +enum PipeRegs { + PIPE_REG_CMD = 0, + + PIPE_REG_SIGNAL_BUFFER_HIGH = 4, + PIPE_REG_SIGNAL_BUFFER = 8, + PIPE_REG_SIGNAL_BUFFER_COUNT = 12, + + PIPE_REG_OPEN_BUFFER_HIGH = 20, + PIPE_REG_OPEN_BUFFER = 24, + + PIPE_REG_VERSION = 36, + + PIPE_REG_GET_SIGNALLED = 48, +}; + +enum PipeCmdCode { + /* to be used by the pipe device itself */ + PIPE_CMD_OPEN = 1, + + PIPE_CMD_CLOSE, + PIPE_CMD_POLL, + PIPE_CMD_WRITE, + PIPE_CMD_WAKE_ON_WRITE, + PIPE_CMD_READ, + PIPE_CMD_WAKE_ON_READ, + + /* + * TODO(zyy): implement a deferred read/write execution to allow + * parallel processing of pipe operations on the host. + */ + PIPE_CMD_WAKE_ON_DONE_IO, +}; + +#endif /* GOLDFISH_PIPE_QEMU_H */ diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 0c1aa6c314f5..54f6a40c75c6 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -60,7 +60,10 @@ config ACERHDF After loading this driver the BIOS is still in control of the fan. To let the kernel handle the fan, do: - echo -n enabled > /sys/class/thermal/thermal_zone0/mode + echo -n enabled > /sys/class/thermal/thermal_zoneN/mode + where N=0,1,2... depending on the number of thermal nodes and the + detection order of your particular system. The "type" parameter + in the same node directory will tell you if it is "acerhdf". For more information about this driver see <http://piie.net/files/acerhdf_README.txt> @@ -105,6 +108,22 @@ config ASUS_LAPTOP If you have an ACPI-compatible ASUS laptop, say Y or M here. +config DCDBAS + tristate "Dell Systems Management Base Driver" + depends on X86 + help + The Dell Systems Management Base Driver provides a sysfs interface + for systems management software to perform System Management + Interrupts (SMIs) and Host Control Actions (system power cycle or + power off after OS shutdown) on certain Dell systems. + + See <file:Documentation/dcdbas.txt> for more details on the driver + and the Dell systems on which Dell systems management software makes + use of this driver. + + Say Y or M here to enable the driver for use by Dell systems + management software such as Dell OpenManage. + # # The DELL_SMBIOS driver depends on ACPI_WMI and/or DCDBAS if those # backends are selected. The "depends" line prevents a configuration @@ -227,6 +246,18 @@ config DELL_RBTN To compile this driver as a module, choose M here: the module will be called dell-rbtn. +config DELL_RBU + tristate "BIOS update support for DELL systems via sysfs" + depends on X86 + select FW_LOADER + select FW_LOADER_USER_HELPER + help + Say m if you want to have the option of updating the BIOS for your + DELL system. Note you need a Dell OpenManage or Dell Update package (DUP) + supporting application to communicate with the BIOS regarding the new + image for the image update to take effect. + See <file:Documentation/dell_rbu.txt> for more details on the driver. + config FUJITSU_LAPTOP tristate "Fujitsu Laptop Extras" @@ -336,6 +367,20 @@ config HP_WMI To compile this driver as a module, choose M here: the module will be called hp-wmi. +config LG_LAPTOP + tristate "LG Laptop Extras" + depends on ACPI + depends on ACPI_WMI + depends on INPUT + select INPUT_SPARSEKMAP + select LEDS_CLASS + help + This driver adds support for hotkeys as well as control of keyboard + backlight, battery maximum charge level and various other ACPI + features. + + If you have an LG Gram laptop, say Y or M here. + config MSI_LAPTOP tristate "MSI Laptop Extras" depends on ACPI @@ -867,6 +912,8 @@ config INTEL_CHT_INT33FE tristate "Intel Cherry Trail ACPI INT33FE Driver" depends on X86 && ACPI && I2C && REGULATOR depends on CHARGER_BQ24190=y || (CHARGER_BQ24190=m && m) + depends on USB_ROLES_INTEL_XHCI=y || (USB_ROLES_INTEL_XHCI=m && m) + depends on TYPEC_MUX_PI3USB30532=y || (TYPEC_MUX_PI3USB30532=m && m) ---help--- This driver add support for the INT33FE ACPI device found on some Intel Cherry Trail devices. @@ -1229,6 +1276,18 @@ config I2C_MULTI_INSTANTIATE To compile this driver as a module, choose M here: the module will be called i2c-multi-instantiate. +config INTEL_ATOMISP2_PM + tristate "Intel AtomISP2 dummy / power-management driver" + depends on PCI && IOSF_MBI && PM + help + Power-management driver for Intel's Image Signal Processor found on + Bay and Cherry Trail devices. This dummy driver's sole purpose is to + turn the ISP off (put it in D3) to save power and to allow entering + of S0ix modes. + + To compile this driver as a module, choose M here: the module + will be called intel_atomisp2_pm. + endif # X86_PLATFORM_DEVICES config PMC_ATOM diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index e6d1becf81ce..39ae94135406 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -9,9 +9,11 @@ obj-$(CONFIG_ASUS_NB_WMI) += asus-nb-wmi.o obj-$(CONFIG_ASUS_WIRELESS) += asus-wireless.o obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o obj-$(CONFIG_EEEPC_WMI) += eeepc-wmi.o +obj-$(CONFIG_LG_LAPTOP) += lg-laptop.o obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o obj-$(CONFIG_ACPI_CMPC) += classmate-laptop.o obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o +obj-$(CONFIG_DCDBAS) += dcdbas.o obj-$(CONFIG_DELL_SMBIOS) += dell-smbios.o dell-smbios-objs := dell-smbios-base.o dell-smbios-$(CONFIG_DELL_SMBIOS_WMI) += dell-smbios-wmi.o @@ -23,6 +25,7 @@ obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o obj-$(CONFIG_DELL_SMO8800) += dell-smo8800.o obj-$(CONFIG_DELL_RBTN) += dell-rbtn.o +obj-$(CONFIG_DELL_RBU) += dell_rbu.o obj-$(CONFIG_ACER_WMI) += acer-wmi.o obj-$(CONFIG_ACER_WIRELESS) += acer-wireless.o obj-$(CONFIG_ACERHDF) += acerhdf.o @@ -92,3 +95,4 @@ obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o obj-$(CONFIG_INTEL_CHTDC_TI_PWRBTN) += intel_chtdc_ti_pwrbtn.o obj-$(CONFIG_I2C_MULTI_INSTANTIATE) += i2c-multi-instantiate.o +obj-$(CONFIG_INTEL_ATOMISP2_PM) += intel_atomisp2_pm.o diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index ea22591ee66f..505224225378 100644 --- a/drivers/platform/x86/acerhdf.c +++ b/drivers/platform/x86/acerhdf.c @@ -86,6 +86,7 @@ static unsigned int interval = 10; static unsigned int fanon = 60000; static unsigned int fanoff = 53000; static unsigned int verbose; +static unsigned int list_supported; static unsigned int fanstate = ACERHDF_FAN_AUTO; static char force_bios[16]; static char force_product[16]; @@ -104,10 +105,12 @@ module_param(fanoff, uint, 0600); MODULE_PARM_DESC(fanoff, "Turn the fan off below this temperature"); module_param(verbose, uint, 0600); MODULE_PARM_DESC(verbose, "Enable verbose dmesg output"); +module_param(list_supported, uint, 0600); +MODULE_PARM_DESC(list_supported, "List supported models and BIOS versions"); module_param_string(force_bios, force_bios, 16, 0); -MODULE_PARM_DESC(force_bios, "Force BIOS version and omit BIOS check"); +MODULE_PARM_DESC(force_bios, "Pretend system has this known supported BIOS version"); module_param_string(force_product, force_product, 16, 0); -MODULE_PARM_DESC(force_product, "Force BIOS product and omit BIOS check"); +MODULE_PARM_DESC(force_product, "Pretend system is this known supported model"); /* * cmd_off: to switch the fan completely off and check if the fan is off @@ -130,7 +133,7 @@ static const struct manualcmd mcmd = { .moff = 0xff, }; -/* BIOS settings */ +/* BIOS settings - only used during probe */ struct bios_settings { const char *vendor; const char *product; @@ -141,8 +144,18 @@ struct bios_settings { int mcmd_enable; }; +/* This could be a daughter struct in the above, but not worth the redirect */ +struct ctrl_settings { + u8 fanreg; + u8 tempreg; + struct fancmd cmd; + int mcmd_enable; +}; + +static struct ctrl_settings ctrl_cfg __read_mostly; + /* Register addresses and values for different BIOS versions */ -static const struct bios_settings bios_tbl[] = { +static const struct bios_settings bios_tbl[] __initconst = { /* AOA110 */ {"Acer", "AOA110", "v0.3109", 0x55, 0x58, {0x1f, 0x00}, 0}, {"Acer", "AOA110", "v0.3114", 0x55, 0x58, {0x1f, 0x00}, 0}, @@ -233,6 +246,7 @@ static const struct bios_settings bios_tbl[] = { {"Gateway", "LT31", "v1.3201", 0x55, 0x58, {0x9e, 0x00}, 0}, {"Gateway", "LT31", "v1.3302", 0x55, 0x58, {0x9e, 0x00}, 0}, {"Gateway", "LT31", "v1.3303t", 0x55, 0x58, {0x9e, 0x00}, 0}, + {"Gateway", "LT31", "v1.3307", 0x55, 0x58, {0x9e, 0x00}, 0}, /* Packard Bell */ {"Packard Bell", "DOA150", "v0.3104", 0x55, 0x58, {0x21, 0x00}, 0}, {"Packard Bell", "DOA150", "v0.3105", 0x55, 0x58, {0x20, 0x00}, 0}, @@ -256,8 +270,6 @@ static const struct bios_settings bios_tbl[] = { {"", "", "", 0, 0, {0, 0}, 0} }; -static const struct bios_settings *bios_cfg __read_mostly; - /* * this struct is used to instruct thermal layer to use bang_bang instead of * default governor for acerhdf @@ -270,7 +282,7 @@ static int acerhdf_get_temp(int *temp) { u8 read_temp; - if (ec_read(bios_cfg->tempreg, &read_temp)) + if (ec_read(ctrl_cfg.tempreg, &read_temp)) return -EINVAL; *temp = read_temp * 1000; @@ -282,10 +294,10 @@ static int acerhdf_get_fanstate(int *state) { u8 fan; - if (ec_read(bios_cfg->fanreg, &fan)) + if (ec_read(ctrl_cfg.fanreg, &fan)) return -EINVAL; - if (fan != bios_cfg->cmd.cmd_off) + if (fan != ctrl_cfg.cmd.cmd_off) *state = ACERHDF_FAN_AUTO; else *state = ACERHDF_FAN_OFF; @@ -306,13 +318,13 @@ static void acerhdf_change_fanstate(int state) state = ACERHDF_FAN_AUTO; } - cmd = (state == ACERHDF_FAN_OFF) ? bios_cfg->cmd.cmd_off - : bios_cfg->cmd.cmd_auto; + cmd = (state == ACERHDF_FAN_OFF) ? ctrl_cfg.cmd.cmd_off + : ctrl_cfg.cmd.cmd_auto; fanstate = state; - ec_write(bios_cfg->fanreg, cmd); + ec_write(ctrl_cfg.fanreg, cmd); - if (bios_cfg->mcmd_enable && state == ACERHDF_FAN_OFF) { + if (ctrl_cfg.mcmd_enable && state == ACERHDF_FAN_OFF) { if (verbose) pr_notice("turning off fan manually\n"); ec_write(mcmd.mreg, mcmd.moff); @@ -615,10 +627,11 @@ static int str_starts_with(const char *str, const char *start) } /* check hardware */ -static int acerhdf_check_hardware(void) +static int __init acerhdf_check_hardware(void) { char const *vendor, *version, *product; const struct bios_settings *bt = NULL; + int found = 0; /* get BIOS data */ vendor = dmi_get_system_info(DMI_SYS_VENDOR); @@ -632,6 +645,17 @@ static int acerhdf_check_hardware(void) pr_info("Acer Aspire One Fan driver, v.%s\n", DRV_VER); + if (list_supported) { + pr_info("List of supported Manufacturer/Model/BIOS:\n"); + pr_info("---------------------------------------------------\n"); + for (bt = bios_tbl; bt->vendor[0]; bt++) { + pr_info("%-13s | %-17s | %-10s\n", bt->vendor, + bt->product, bt->version); + } + pr_info("---------------------------------------------------\n"); + return -ECANCELED; + } + if (force_bios[0]) { version = force_bios; pr_info("forcing BIOS version: %s\n", version); @@ -657,30 +681,36 @@ static int acerhdf_check_hardware(void) if (str_starts_with(vendor, bt->vendor) && str_starts_with(product, bt->product) && str_starts_with(version, bt->version)) { - bios_cfg = bt; + found = 1; break; } } - if (!bios_cfg) { + if (!found) { pr_err("unknown (unsupported) BIOS version %s/%s/%s, please report, aborting!\n", vendor, product, version); return -EINVAL; } + /* Copy control settings from BIOS table before we free it. */ + ctrl_cfg.fanreg = bt->fanreg; + ctrl_cfg.tempreg = bt->tempreg; + memcpy(&ctrl_cfg.cmd, &bt->cmd, sizeof(struct fancmd)); + ctrl_cfg.mcmd_enable = bt->mcmd_enable; + /* * if started with kernel mode off, prevent the kernel from switching * off the fan */ if (!kernelmode) { pr_notice("Fan control off, to enable do:\n"); - pr_notice("echo -n \"enabled\" > /sys/class/thermal/thermal_zone0/mode\n"); + pr_notice("echo -n \"enabled\" > /sys/class/thermal/thermal_zoneN/mode # N=0,1,2...\n"); } return 0; } -static int acerhdf_register_platform(void) +static int __init acerhdf_register_platform(void) { int err = 0; @@ -712,7 +742,7 @@ static void acerhdf_unregister_platform(void) platform_driver_unregister(&acerhdf_driver); } -static int acerhdf_register_thermal(void) +static int __init acerhdf_register_thermal(void) { cl_dev = thermal_cooling_device_register("acerhdf-fan", NULL, &acerhdf_cooling_ops); diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 2d6e272315a8..c285a16675ee 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -43,6 +43,7 @@ #include <linux/hwmon-sysfs.h> #include <linux/debugfs.h> #include <linux/seq_file.h> +#include <linux/platform_data/x86/asus-wmi.h> #include <linux/platform_device.h> #include <linux/thermal.h> #include <linux/acpi.h> @@ -69,89 +70,6 @@ MODULE_LICENSE("GPL"); #define NOTIFY_KBD_BRTDWN 0xc5 #define NOTIFY_KBD_BRTTOGGLE 0xc7 -/* WMI Methods */ -#define ASUS_WMI_METHODID_SPEC 0x43455053 /* BIOS SPECification */ -#define ASUS_WMI_METHODID_SFBD 0x44424653 /* Set First Boot Device */ -#define ASUS_WMI_METHODID_GLCD 0x44434C47 /* Get LCD status */ -#define ASUS_WMI_METHODID_GPID 0x44495047 /* Get Panel ID?? (Resol) */ -#define ASUS_WMI_METHODID_QMOD 0x444F4D51 /* Quiet MODe */ -#define ASUS_WMI_METHODID_SPLV 0x4C425053 /* Set Panel Light Value */ -#define ASUS_WMI_METHODID_AGFN 0x4E464741 /* FaN? */ -#define ASUS_WMI_METHODID_SFUN 0x4E554653 /* FUNCtionalities */ -#define ASUS_WMI_METHODID_SDSP 0x50534453 /* Set DiSPlay output */ -#define ASUS_WMI_METHODID_GDSP 0x50534447 /* Get DiSPlay output */ -#define ASUS_WMI_METHODID_DEVP 0x50564544 /* DEVice Policy */ -#define ASUS_WMI_METHODID_OSVR 0x5256534F /* OS VeRsion */ -#define ASUS_WMI_METHODID_DSTS 0x53544344 /* Device STatuS */ -#define ASUS_WMI_METHODID_DSTS2 0x53545344 /* Device STatuS #2*/ -#define ASUS_WMI_METHODID_BSTS 0x53545342 /* Bios STatuS ? */ -#define ASUS_WMI_METHODID_DEVS 0x53564544 /* DEVice Set */ -#define ASUS_WMI_METHODID_CFVS 0x53564643 /* CPU Frequency Volt Set */ -#define ASUS_WMI_METHODID_KBFT 0x5446424B /* KeyBoard FilTer */ -#define ASUS_WMI_METHODID_INIT 0x54494E49 /* INITialize */ -#define ASUS_WMI_METHODID_HKEY 0x59454B48 /* Hot KEY ?? */ - -#define ASUS_WMI_UNSUPPORTED_METHOD 0xFFFFFFFE - -/* Wireless */ -#define ASUS_WMI_DEVID_HW_SWITCH 0x00010001 -#define ASUS_WMI_DEVID_WIRELESS_LED 0x00010002 -#define ASUS_WMI_DEVID_CWAP 0x00010003 -#define ASUS_WMI_DEVID_WLAN 0x00010011 -#define ASUS_WMI_DEVID_WLAN_LED 0x00010012 -#define ASUS_WMI_DEVID_BLUETOOTH 0x00010013 -#define ASUS_WMI_DEVID_GPS 0x00010015 -#define ASUS_WMI_DEVID_WIMAX 0x00010017 -#define ASUS_WMI_DEVID_WWAN3G 0x00010019 -#define ASUS_WMI_DEVID_UWB 0x00010021 - -/* Leds */ -/* 0x000200XX and 0x000400XX */ -#define ASUS_WMI_DEVID_LED1 0x00020011 -#define ASUS_WMI_DEVID_LED2 0x00020012 -#define ASUS_WMI_DEVID_LED3 0x00020013 -#define ASUS_WMI_DEVID_LED4 0x00020014 -#define ASUS_WMI_DEVID_LED5 0x00020015 -#define ASUS_WMI_DEVID_LED6 0x00020016 - -/* Backlight and Brightness */ -#define ASUS_WMI_DEVID_ALS_ENABLE 0x00050001 /* Ambient Light Sensor */ -#define ASUS_WMI_DEVID_BACKLIGHT 0x00050011 -#define ASUS_WMI_DEVID_BRIGHTNESS 0x00050012 -#define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021 -#define ASUS_WMI_DEVID_LIGHT_SENSOR 0x00050022 /* ?? */ -#define ASUS_WMI_DEVID_LIGHTBAR 0x00050025 - -/* Misc */ -#define ASUS_WMI_DEVID_CAMERA 0x00060013 - -/* Storage */ -#define ASUS_WMI_DEVID_CARDREADER 0x00080013 - -/* Input */ -#define ASUS_WMI_DEVID_TOUCHPAD 0x00100011 -#define ASUS_WMI_DEVID_TOUCHPAD_LED 0x00100012 - -/* Fan, Thermal */ -#define ASUS_WMI_DEVID_THERMAL_CTRL 0x00110011 -#define ASUS_WMI_DEVID_FAN_CTRL 0x00110012 - -/* Power */ -#define ASUS_WMI_DEVID_PROCESSOR_STATE 0x00120012 - -/* Deep S3 / Resume on LID open */ -#define ASUS_WMI_DEVID_LID_RESUME 0x00120031 - -/* DSTS masks */ -#define ASUS_WMI_DSTS_STATUS_BIT 0x00000001 -#define ASUS_WMI_DSTS_UNKNOWN_BIT 0x00000002 -#define ASUS_WMI_DSTS_PRESENCE_BIT 0x00010000 -#define ASUS_WMI_DSTS_USER_BIT 0x00020000 -#define ASUS_WMI_DSTS_BIOS_BIT 0x00040000 -#define ASUS_WMI_DSTS_BRIGHTNESS_MASK 0x000000FF -#define ASUS_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00 -#define ASUS_WMI_DSTS_LIGHTBAR_MASK 0x0000000F - #define ASUS_FAN_DESC "cpu_fan" #define ASUS_FAN_MFUN 0x13 #define ASUS_FAN_SFUN_READ 0x06 @@ -239,7 +157,6 @@ struct asus_wmi { int lightbar_led_wk; struct workqueue_struct *led_workqueue; struct work_struct tpd_led_work; - struct work_struct kbd_led_work; struct work_struct wlan_led_work; struct work_struct lightbar_led_work; @@ -254,7 +171,7 @@ struct asus_wmi { int asus_hwmon_num_fans; int asus_hwmon_pwm; - struct hotplug_slot *hotplug_slot; + struct hotplug_slot hotplug_slot; struct mutex hotplug_lock; struct mutex wmi_lock; struct workqueue_struct *hotplug_workqueue; @@ -302,8 +219,7 @@ static void asus_wmi_input_exit(struct asus_wmi *asus) asus->inputdev = NULL; } -static int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, - u32 *retval) +int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval) { struct bios_args args = { .arg0 = arg0, @@ -339,6 +255,7 @@ exit: return 0; } +EXPORT_SYMBOL_GPL(asus_wmi_evaluate_method); static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args) { @@ -456,12 +373,9 @@ static enum led_brightness tpd_led_get(struct led_classdev *led_cdev) return read_tpd_led_state(asus); } -static void kbd_led_update(struct work_struct *work) +static void kbd_led_update(struct asus_wmi *asus) { int ctrl_param = 0; - struct asus_wmi *asus; - - asus = container_of(work, struct asus_wmi, kbd_led_work); /* * bits 0-2: level @@ -471,7 +385,6 @@ static void kbd_led_update(struct work_struct *work) ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F); asus_wmi_set_devstate(ASUS_WMI_DEVID_KBD_BACKLIGHT, ctrl_param, NULL); - led_classdev_notify_brightness_hw_changed(&asus->kbd_led, asus->kbd_led_wk); } static int kbd_led_read(struct asus_wmi *asus, int *level, int *env) @@ -516,7 +429,7 @@ static void do_kbd_led_set(struct led_classdev *led_cdev, int value) value = 0; asus->kbd_led_wk = value; - queue_work(asus->led_workqueue, &asus->kbd_led_work); + kbd_led_update(asus); } static void kbd_led_set(struct led_classdev *led_cdev, @@ -525,6 +438,14 @@ static void kbd_led_set(struct led_classdev *led_cdev, do_kbd_led_set(led_cdev, value); } +static void kbd_led_set_by_kbd(struct asus_wmi *asus, enum led_brightness value) +{ + struct led_classdev *led_cdev = &asus->kbd_led; + + do_kbd_led_set(led_cdev, value); + led_classdev_notify_brightness_hw_changed(led_cdev, asus->kbd_led_wk); +} + static enum led_brightness kbd_led_get(struct led_classdev *led_cdev) { struct asus_wmi *asus; @@ -671,8 +592,6 @@ static int asus_wmi_led_init(struct asus_wmi *asus) led_val = kbd_led_read(asus, NULL, NULL); if (led_val >= 0) { - INIT_WORK(&asus->kbd_led_work, kbd_led_update); - asus->kbd_led_wk = led_val; asus->kbd_led.name = "asus::kbd_backlight"; asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED; @@ -753,7 +672,7 @@ static void asus_rfkill_hotplug(struct asus_wmi *asus) if (asus->wlan.rfkill) rfkill_set_sw_state(asus->wlan.rfkill, blocked); - if (asus->hotplug_slot) { + if (asus->hotplug_slot.ops) { bus = pci_find_bus(0, 1); if (!bus) { pr_warn("Unable to find PCI bus 1?\n"); @@ -858,7 +777,8 @@ static void asus_unregister_rfkill_notifier(struct asus_wmi *asus, char *node) static int asus_get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) { - struct asus_wmi *asus = hotplug_slot->private; + struct asus_wmi *asus = container_of(hotplug_slot, + struct asus_wmi, hotplug_slot); int result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN); if (result < 0) @@ -868,8 +788,7 @@ static int asus_get_adapter_status(struct hotplug_slot *hotplug_slot, return 0; } -static struct hotplug_slot_ops asus_hotplug_slot_ops = { - .owner = THIS_MODULE, +static const struct hotplug_slot_ops asus_hotplug_slot_ops = { .get_adapter_status = asus_get_adapter_status, .get_power_status = asus_get_adapter_status, }; @@ -899,21 +818,9 @@ static int asus_setup_pci_hotplug(struct asus_wmi *asus) INIT_WORK(&asus->hotplug_work, asus_hotplug_work); - asus->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL); - if (!asus->hotplug_slot) - goto error_slot; + asus->hotplug_slot.ops = &asus_hotplug_slot_ops; - asus->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info), - GFP_KERNEL); - if (!asus->hotplug_slot->info) - goto error_info; - - asus->hotplug_slot->private = asus; - asus->hotplug_slot->ops = &asus_hotplug_slot_ops; - asus_get_adapter_status(asus->hotplug_slot, - &asus->hotplug_slot->info->adapter_status); - - ret = pci_hp_register(asus->hotplug_slot, bus, 0, "asus-wifi"); + ret = pci_hp_register(&asus->hotplug_slot, bus, 0, "asus-wifi"); if (ret) { pr_err("Unable to register hotplug slot - %d\n", ret); goto error_register; @@ -922,11 +829,7 @@ static int asus_setup_pci_hotplug(struct asus_wmi *asus) return 0; error_register: - kfree(asus->hotplug_slot->info); -error_info: - kfree(asus->hotplug_slot); - asus->hotplug_slot = NULL; -error_slot: + asus->hotplug_slot.ops = NULL; destroy_workqueue(asus->hotplug_workqueue); error_workqueue: return ret; @@ -1054,11 +957,8 @@ static void asus_wmi_rfkill_exit(struct asus_wmi *asus) * asus_unregister_rfkill_notifier() */ asus_rfkill_hotplug(asus); - if (asus->hotplug_slot) { - pci_hp_deregister(asus->hotplug_slot); - kfree(asus->hotplug_slot->info); - kfree(asus->hotplug_slot); - } + if (asus->hotplug_slot.ops) + pci_hp_deregister(&asus->hotplug_slot); if (asus->hotplug_workqueue) destroy_workqueue(asus->hotplug_workqueue); @@ -1765,18 +1665,18 @@ static void asus_wmi_notify(u32 value, void *context) } if (code == NOTIFY_KBD_BRTUP) { - do_kbd_led_set(&asus->kbd_led, asus->kbd_led_wk + 1); + kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1); goto exit; } if (code == NOTIFY_KBD_BRTDWN) { - do_kbd_led_set(&asus->kbd_led, asus->kbd_led_wk - 1); + kbd_led_set_by_kbd(asus, asus->kbd_led_wk - 1); goto exit; } if (code == NOTIFY_KBD_BRTTOGGLE) { if (asus->kbd_led_wk == asus->kbd_led.max_brightness) - do_kbd_led_set(&asus->kbd_led, 0); + kbd_led_set_by_kbd(asus, 0); else - do_kbd_led_set(&asus->kbd_led, asus->kbd_led_wk + 1); + kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1); goto exit; } @@ -2310,7 +2210,7 @@ static int asus_hotk_resume(struct device *device) struct asus_wmi *asus = dev_get_drvdata(device); if (!IS_ERR_OR_NULL(asus->kbd_led.dev)) - queue_work(asus->led_workqueue, &asus->kbd_led_work); + kbd_led_update(asus); return 0; } @@ -2346,7 +2246,7 @@ static int asus_hotk_restore(struct device *device) rfkill_set_sw_state(asus->uwb.rfkill, bl); } if (!IS_ERR_OR_NULL(asus->kbd_led.dev)) - queue_work(asus->led_workqueue, &asus->kbd_led_work); + kbd_led_update(asus); return 0; } diff --git a/drivers/platform/x86/dcdbas.c b/drivers/platform/x86/dcdbas.c new file mode 100644 index 000000000000..88bd7efafe14 --- /dev/null +++ b/drivers/platform/x86/dcdbas.c @@ -0,0 +1,761 @@ +/* + * dcdbas.c: Dell Systems Management Base Driver + * + * The Dell Systems Management Base Driver provides a sysfs interface for + * systems management software to perform System Management Interrupts (SMIs) + * and Host Control Actions (power cycle or power off after OS shutdown) on + * Dell systems. + * + * See Documentation/dcdbas.txt for more information. + * + * Copyright (C) 1995-2006 Dell Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2.0 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. + */ + +#include <linux/platform_device.h> +#include <linux/acpi.h> +#include <linux/dma-mapping.h> +#include <linux/errno.h> +#include <linux/cpu.h> +#include <linux/gfp.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mc146818rtc.h> +#include <linux/module.h> +#include <linux/reboot.h> +#include <linux/sched.h> +#include <linux/smp.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/mutex.h> + +#include "dcdbas.h" + +#define DRIVER_NAME "dcdbas" +#define DRIVER_VERSION "5.6.0-3.3" +#define DRIVER_DESCRIPTION "Dell Systems Management Base Driver" + +static struct platform_device *dcdbas_pdev; + +static u8 *smi_data_buf; +static dma_addr_t smi_data_buf_handle; +static unsigned long smi_data_buf_size; +static unsigned long max_smi_data_buf_size = MAX_SMI_DATA_BUF_SIZE; +static u32 smi_data_buf_phys_addr; +static DEFINE_MUTEX(smi_data_lock); +static u8 *eps_buffer; + +static unsigned int host_control_action; +static unsigned int host_control_smi_type; +static unsigned int host_control_on_shutdown; + +static bool wsmt_enabled; + +/** + * smi_data_buf_free: free SMI data buffer + */ +static void smi_data_buf_free(void) +{ + if (!smi_data_buf || wsmt_enabled) + return; + + dev_dbg(&dcdbas_pdev->dev, "%s: phys: %x size: %lu\n", + __func__, smi_data_buf_phys_addr, smi_data_buf_size); + + dma_free_coherent(&dcdbas_pdev->dev, smi_data_buf_size, smi_data_buf, + smi_data_buf_handle); + smi_data_buf = NULL; + smi_data_buf_handle = 0; + smi_data_buf_phys_addr = 0; + smi_data_buf_size = 0; +} + +/** + * smi_data_buf_realloc: grow SMI data buffer if needed + */ +static int smi_data_buf_realloc(unsigned long size) +{ + void *buf; + dma_addr_t handle; + + if (smi_data_buf_size >= size) + return 0; + + if (size > max_smi_data_buf_size) + return -EINVAL; + + /* new buffer is needed */ + buf = dma_alloc_coherent(&dcdbas_pdev->dev, size, &handle, GFP_KERNEL); + if (!buf) { + dev_dbg(&dcdbas_pdev->dev, + "%s: failed to allocate memory size %lu\n", + __func__, size); + return -ENOMEM; + } + /* memory zeroed by dma_alloc_coherent */ + + if (smi_data_buf) + memcpy(buf, smi_data_buf, smi_data_buf_size); + + /* free any existing buffer */ + smi_data_buf_free(); + + /* set up new buffer for use */ + smi_data_buf = buf; + smi_data_buf_handle = handle; + smi_data_buf_phys_addr = (u32) virt_to_phys(buf); + smi_data_buf_size = size; + + dev_dbg(&dcdbas_pdev->dev, "%s: phys: %x size: %lu\n", + __func__, smi_data_buf_phys_addr, smi_data_buf_size); + + return 0; +} + +static ssize_t smi_data_buf_phys_addr_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%x\n", smi_data_buf_phys_addr); +} + +static ssize_t smi_data_buf_size_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%lu\n", smi_data_buf_size); +} + +static ssize_t smi_data_buf_size_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long buf_size; + ssize_t ret; + + buf_size = simple_strtoul(buf, NULL, 10); + + /* make sure SMI data buffer is at least buf_size */ + mutex_lock(&smi_data_lock); + ret = smi_data_buf_realloc(buf_size); + mutex_unlock(&smi_data_lock); + if (ret) + return ret; + + return count; +} + +static ssize_t smi_data_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t pos, size_t count) +{ + ssize_t ret; + + mutex_lock(&smi_data_lock); + ret = memory_read_from_buffer(buf, count, &pos, smi_data_buf, + smi_data_buf_size); + mutex_unlock(&smi_data_lock); + return ret; +} + +static ssize_t smi_data_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t pos, size_t count) +{ + ssize_t ret; + + if ((pos + count) > max_smi_data_buf_size) + return -EINVAL; + + mutex_lock(&smi_data_lock); + + ret = smi_data_buf_realloc(pos + count); + if (ret) + goto out; + + memcpy(smi_data_buf + pos, buf, count); + ret = count; +out: + mutex_unlock(&smi_data_lock); + return ret; +} + +static ssize_t host_control_action_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%u\n", host_control_action); +} + +static ssize_t host_control_action_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t ret; + + /* make sure buffer is available for host control command */ + mutex_lock(&smi_data_lock); + ret = smi_data_buf_realloc(sizeof(struct apm_cmd)); + mutex_unlock(&smi_data_lock); + if (ret) + return ret; + + host_control_action = simple_strtoul(buf, NULL, 10); + return count; +} + +static ssize_t host_control_smi_type_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%u\n", host_control_smi_type); +} + +static ssize_t host_control_smi_type_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + host_control_smi_type = simple_strtoul(buf, NULL, 10); + return count; +} + +static ssize_t host_control_on_shutdown_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%u\n", host_control_on_shutdown); +} + +static ssize_t host_control_on_shutdown_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + host_control_on_shutdown = simple_strtoul(buf, NULL, 10); + return count; +} + +static int raise_smi(void *par) +{ + struct smi_cmd *smi_cmd = par; + + if (smp_processor_id() != 0) { + dev_dbg(&dcdbas_pdev->dev, "%s: failed to get CPU 0\n", + __func__); + return -EBUSY; + } + + /* generate SMI */ + /* inb to force posted write through and make SMI happen now */ + asm volatile ( + "outb %b0,%w1\n" + "inb %w1" + : /* no output args */ + : "a" (smi_cmd->command_code), + "d" (smi_cmd->command_address), + "b" (smi_cmd->ebx), + "c" (smi_cmd->ecx) + : "memory" + ); + + return 0; +} +/** + * dcdbas_smi_request: generate SMI request + * + * Called with smi_data_lock. + */ +int dcdbas_smi_request(struct smi_cmd *smi_cmd) +{ + int ret; + + if (smi_cmd->magic != SMI_CMD_MAGIC) { + dev_info(&dcdbas_pdev->dev, "%s: invalid magic value\n", + __func__); + return -EBADR; + } + + /* SMI requires CPU 0 */ + get_online_cpus(); + ret = smp_call_on_cpu(0, raise_smi, smi_cmd, true); + put_online_cpus(); + + return ret; +} + +/** + * smi_request_store: + * + * The valid values are: + * 0: zero SMI data buffer + * 1: generate calling interface SMI + * 2: generate raw SMI + * + * User application writes smi_cmd to smi_data before telling driver + * to generate SMI. + */ +static ssize_t smi_request_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct smi_cmd *smi_cmd; + unsigned long val = simple_strtoul(buf, NULL, 10); + ssize_t ret; + + mutex_lock(&smi_data_lock); + + if (smi_data_buf_size < sizeof(struct smi_cmd)) { + ret = -ENODEV; + goto out; + } + smi_cmd = (struct smi_cmd *)smi_data_buf; + + switch (val) { + case 2: + /* Raw SMI */ + ret = dcdbas_smi_request(smi_cmd); + if (!ret) + ret = count; + break; + case 1: + /* + * Calling Interface SMI + * + * Provide physical address of command buffer field within + * the struct smi_cmd to BIOS. + * + * Because the address that smi_cmd (smi_data_buf) points to + * will be from memremap() of a non-memory address if WSMT + * is present, we can't use virt_to_phys() on smi_cmd, so + * we have to use the physical address that was saved when + * the virtual address for smi_cmd was received. + */ + smi_cmd->ebx = smi_data_buf_phys_addr + + offsetof(struct smi_cmd, command_buffer); + ret = dcdbas_smi_request(smi_cmd); + if (!ret) + ret = count; + break; + case 0: + memset(smi_data_buf, 0, smi_data_buf_size); + ret = count; + break; + default: + ret = -EINVAL; + break; + } + +out: + mutex_unlock(&smi_data_lock); + return ret; +} +EXPORT_SYMBOL(dcdbas_smi_request); + +/** + * host_control_smi: generate host control SMI + * + * Caller must set up the host control command in smi_data_buf. + */ +static int host_control_smi(void) +{ + struct apm_cmd *apm_cmd; + u8 *data; + unsigned long flags; + u32 num_ticks; + s8 cmd_status; + u8 index; + + apm_cmd = (struct apm_cmd *)smi_data_buf; + apm_cmd->status = ESM_STATUS_CMD_UNSUCCESSFUL; + + switch (host_control_smi_type) { + case HC_SMITYPE_TYPE1: + spin_lock_irqsave(&rtc_lock, flags); + /* write SMI data buffer physical address */ + data = (u8 *)&smi_data_buf_phys_addr; + for (index = PE1300_CMOS_CMD_STRUCT_PTR; + index < (PE1300_CMOS_CMD_STRUCT_PTR + 4); + index++, data++) { + outb(index, + (CMOS_BASE_PORT + CMOS_PAGE2_INDEX_PORT_PIIX4)); + outb(*data, + (CMOS_BASE_PORT + CMOS_PAGE2_DATA_PORT_PIIX4)); + } + + /* first set status to -1 as called by spec */ + cmd_status = ESM_STATUS_CMD_UNSUCCESSFUL; + outb((u8) cmd_status, PCAT_APM_STATUS_PORT); + + /* generate SMM call */ + outb(ESM_APM_CMD, PCAT_APM_CONTROL_PORT); + spin_unlock_irqrestore(&rtc_lock, flags); + + /* wait a few to see if it executed */ + num_ticks = TIMEOUT_USEC_SHORT_SEMA_BLOCKING; + while ((cmd_status = inb(PCAT_APM_STATUS_PORT)) + == ESM_STATUS_CMD_UNSUCCESSFUL) { + num_ticks--; + if (num_ticks == EXPIRED_TIMER) + return -ETIME; + } + break; + + case HC_SMITYPE_TYPE2: + case HC_SMITYPE_TYPE3: + spin_lock_irqsave(&rtc_lock, flags); + /* write SMI data buffer physical address */ + data = (u8 *)&smi_data_buf_phys_addr; + for (index = PE1400_CMOS_CMD_STRUCT_PTR; + index < (PE1400_CMOS_CMD_STRUCT_PTR + 4); + index++, data++) { + outb(index, (CMOS_BASE_PORT + CMOS_PAGE1_INDEX_PORT)); + outb(*data, (CMOS_BASE_PORT + CMOS_PAGE1_DATA_PORT)); + } + + /* generate SMM call */ + if (host_control_smi_type == HC_SMITYPE_TYPE3) + outb(ESM_APM_CMD, PCAT_APM_CONTROL_PORT); + else + outb(ESM_APM_CMD, PE1400_APM_CONTROL_PORT); + + /* restore RTC index pointer since it was written to above */ + CMOS_READ(RTC_REG_C); + spin_unlock_irqrestore(&rtc_lock, flags); + + /* read control port back to serialize write */ + cmd_status = inb(PE1400_APM_CONTROL_PORT); + + /* wait a few to see if it executed */ + num_ticks = TIMEOUT_USEC_SHORT_SEMA_BLOCKING; + while (apm_cmd->status == ESM_STATUS_CMD_UNSUCCESSFUL) { + num_ticks--; + if (num_ticks == EXPIRED_TIMER) + return -ETIME; + } + break; + + default: + dev_dbg(&dcdbas_pdev->dev, "%s: invalid SMI type %u\n", + __func__, host_control_smi_type); + return -ENOSYS; + } + + return 0; +} + +/** + * dcdbas_host_control: initiate host control + * + * This function is called by the driver after the system has + * finished shutting down if the user application specified a + * host control action to perform on shutdown. It is safe to + * use smi_data_buf at this point because the system has finished + * shutting down and no userspace apps are running. + */ +static void dcdbas_host_control(void) +{ + struct apm_cmd *apm_cmd; + u8 action; + + if (host_control_action == HC_ACTION_NONE) + return; + + action = host_control_action; + host_control_action = HC_ACTION_NONE; + + if (!smi_data_buf) { + dev_dbg(&dcdbas_pdev->dev, "%s: no SMI buffer\n", __func__); + return; + } + + if (smi_data_buf_size < sizeof(struct apm_cmd)) { + dev_dbg(&dcdbas_pdev->dev, "%s: SMI buffer too small\n", + __func__); + return; + } + + apm_cmd = (struct apm_cmd *)smi_data_buf; + + /* power off takes precedence */ + if (action & HC_ACTION_HOST_CONTROL_POWEROFF) { + apm_cmd->command = ESM_APM_POWER_CYCLE; + apm_cmd->reserved = 0; + *((s16 *)&apm_cmd->parameters.shortreq.parm[0]) = (s16) 0; + host_control_smi(); + } else if (action & HC_ACTION_HOST_CONTROL_POWERCYCLE) { + apm_cmd->command = ESM_APM_POWER_CYCLE; + apm_cmd->reserved = 0; + *((s16 *)&apm_cmd->parameters.shortreq.parm[0]) = (s16) 20; + host_control_smi(); + } +} + +/* WSMT */ + +static u8 checksum(u8 *buffer, u8 length) +{ + u8 sum = 0; + u8 *end = buffer + length; + + while (buffer < end) + sum += *buffer++; + return sum; +} + +static inline struct smm_eps_table *check_eps_table(u8 *addr) +{ + struct smm_eps_table *eps = (struct smm_eps_table *)addr; + + if (strncmp(eps->smm_comm_buff_anchor, SMM_EPS_SIG, 4) != 0) + return NULL; + + if (checksum(addr, eps->length) != 0) + return NULL; + + return eps; +} + +static int dcdbas_check_wsmt(void) +{ + struct acpi_table_wsmt *wsmt = NULL; + struct smm_eps_table *eps = NULL; + u64 remap_size; + u8 *addr; + + acpi_get_table(ACPI_SIG_WSMT, 0, (struct acpi_table_header **)&wsmt); + if (!wsmt) + return 0; + + /* Check if WSMT ACPI table shows that protection is enabled */ + if (!(wsmt->protection_flags & ACPI_WSMT_FIXED_COMM_BUFFERS) || + !(wsmt->protection_flags & ACPI_WSMT_COMM_BUFFER_NESTED_PTR_PROTECTION)) + return 0; + + /* Scan for EPS (entry point structure) */ + for (addr = (u8 *)__va(0xf0000); + addr < (u8 *)__va(0x100000 - sizeof(struct smm_eps_table)); + addr += 16) { + eps = check_eps_table(addr); + if (eps) + break; + } + + if (!eps) { + dev_dbg(&dcdbas_pdev->dev, "found WSMT, but no EPS found\n"); + return -ENODEV; + } + + /* + * Get physical address of buffer and map to virtual address. + * Table gives size in 4K pages, regardless of actual system page size. + */ + if (upper_32_bits(eps->smm_comm_buff_addr + 8)) { + dev_warn(&dcdbas_pdev->dev, "found WSMT, but EPS buffer address is above 4GB\n"); + return -EINVAL; + } + /* + * Limit remap size to MAX_SMI_DATA_BUF_SIZE + 8 (since the first 8 + * bytes are used for a semaphore, not the data buffer itself). + */ + remap_size = eps->num_of_4k_pages * PAGE_SIZE; + if (remap_size > MAX_SMI_DATA_BUF_SIZE + 8) + remap_size = MAX_SMI_DATA_BUF_SIZE + 8; + eps_buffer = memremap(eps->smm_comm_buff_addr, remap_size, MEMREMAP_WB); + if (!eps_buffer) { + dev_warn(&dcdbas_pdev->dev, "found WSMT, but failed to map EPS buffer\n"); + return -ENOMEM; + } + + /* First 8 bytes is for a semaphore, not part of the smi_data_buf */ + smi_data_buf_phys_addr = eps->smm_comm_buff_addr + 8; + smi_data_buf = eps_buffer + 8; + smi_data_buf_size = remap_size - 8; + max_smi_data_buf_size = smi_data_buf_size; + wsmt_enabled = true; + dev_info(&dcdbas_pdev->dev, + "WSMT found, using firmware-provided SMI buffer.\n"); + return 1; +} + +/** + * dcdbas_reboot_notify: handle reboot notification for host control + */ +static int dcdbas_reboot_notify(struct notifier_block *nb, unsigned long code, + void *unused) +{ + switch (code) { + case SYS_DOWN: + case SYS_HALT: + case SYS_POWER_OFF: + if (host_control_on_shutdown) { + /* firmware is going to perform host control action */ + printk(KERN_WARNING "Please wait for shutdown " + "action to complete...\n"); + dcdbas_host_control(); + } + break; + } + + return NOTIFY_DONE; +} + +static struct notifier_block dcdbas_reboot_nb = { + .notifier_call = dcdbas_reboot_notify, + .next = NULL, + .priority = INT_MIN +}; + +static DCDBAS_BIN_ATTR_RW(smi_data); + +static struct bin_attribute *dcdbas_bin_attrs[] = { + &bin_attr_smi_data, + NULL +}; + +static DCDBAS_DEV_ATTR_RW(smi_data_buf_size); +static DCDBAS_DEV_ATTR_RO(smi_data_buf_phys_addr); +static DCDBAS_DEV_ATTR_WO(smi_request); +static DCDBAS_DEV_ATTR_RW(host_control_action); +static DCDBAS_DEV_ATTR_RW(host_control_smi_type); +static DCDBAS_DEV_ATTR_RW(host_control_on_shutdown); + +static struct attribute *dcdbas_dev_attrs[] = { + &dev_attr_smi_data_buf_size.attr, + &dev_attr_smi_data_buf_phys_addr.attr, + &dev_attr_smi_request.attr, + &dev_attr_host_control_action.attr, + &dev_attr_host_control_smi_type.attr, + &dev_attr_host_control_on_shutdown.attr, + NULL +}; + +static const struct attribute_group dcdbas_attr_group = { + .attrs = dcdbas_dev_attrs, + .bin_attrs = dcdbas_bin_attrs, +}; + +static int dcdbas_probe(struct platform_device *dev) +{ + int error; + + host_control_action = HC_ACTION_NONE; + host_control_smi_type = HC_SMITYPE_NONE; + + dcdbas_pdev = dev; + + /* Check if ACPI WSMT table specifies protected SMI buffer address */ + error = dcdbas_check_wsmt(); + if (error < 0) + return error; + + /* + * BIOS SMI calls require buffer addresses be in 32-bit address space. + * This is done by setting the DMA mask below. + */ + error = dma_set_coherent_mask(&dcdbas_pdev->dev, DMA_BIT_MASK(32)); + if (error) + return error; + + error = sysfs_create_group(&dev->dev.kobj, &dcdbas_attr_group); + if (error) + return error; + + register_reboot_notifier(&dcdbas_reboot_nb); + + dev_info(&dev->dev, "%s (version %s)\n", + DRIVER_DESCRIPTION, DRIVER_VERSION); + + return 0; +} + +static int dcdbas_remove(struct platform_device *dev) +{ + unregister_reboot_notifier(&dcdbas_reboot_nb); + sysfs_remove_group(&dev->dev.kobj, &dcdbas_attr_group); + + return 0; +} + +static struct platform_driver dcdbas_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .probe = dcdbas_probe, + .remove = dcdbas_remove, +}; + +static const struct platform_device_info dcdbas_dev_info __initconst = { + .name = DRIVER_NAME, + .id = -1, + .dma_mask = DMA_BIT_MASK(32), +}; + +static struct platform_device *dcdbas_pdev_reg; + +/** + * dcdbas_init: initialize driver + */ +static int __init dcdbas_init(void) +{ + int error; + + error = platform_driver_register(&dcdbas_driver); + if (error) + return error; + + dcdbas_pdev_reg = platform_device_register_full(&dcdbas_dev_info); + if (IS_ERR(dcdbas_pdev_reg)) { + error = PTR_ERR(dcdbas_pdev_reg); + goto err_unregister_driver; + } + + return 0; + + err_unregister_driver: + platform_driver_unregister(&dcdbas_driver); + return error; +} + +/** + * dcdbas_exit: perform driver cleanup + */ +static void __exit dcdbas_exit(void) +{ + /* + * make sure functions that use dcdbas_pdev are called + * before platform_device_unregister + */ + unregister_reboot_notifier(&dcdbas_reboot_nb); + + /* + * We have to free the buffer here instead of dcdbas_remove + * because only in module exit function we can be sure that + * all sysfs attributes belonging to this module have been + * released. + */ + if (dcdbas_pdev) + smi_data_buf_free(); + if (eps_buffer) + memunmap(eps_buffer); + platform_device_unregister(dcdbas_pdev_reg); + platform_driver_unregister(&dcdbas_driver); +} + +subsys_initcall_sync(dcdbas_init); +module_exit(dcdbas_exit); + +MODULE_DESCRIPTION(DRIVER_DESCRIPTION " (version " DRIVER_VERSION ")"); +MODULE_VERSION(DRIVER_VERSION); +MODULE_AUTHOR("Dell Inc."); +MODULE_LICENSE("GPL"); +/* Any System or BIOS claiming to be by Dell */ +MODULE_ALIAS("dmi:*:[bs]vnD[Ee][Ll][Ll]*:*"); diff --git a/drivers/platform/x86/dcdbas.h b/drivers/platform/x86/dcdbas.h new file mode 100644 index 000000000000..52729a494b00 --- /dev/null +++ b/drivers/platform/x86/dcdbas.h @@ -0,0 +1,117 @@ +/* + * dcdbas.h: Definitions for Dell Systems Management Base driver + * + * Copyright (C) 1995-2005 Dell Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2.0 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. + */ + +#ifndef _DCDBAS_H_ +#define _DCDBAS_H_ + +#include <linux/device.h> +#include <linux/sysfs.h> +#include <linux/types.h> + +#define MAX_SMI_DATA_BUF_SIZE (256 * 1024) + +#define HC_ACTION_NONE (0) +#define HC_ACTION_HOST_CONTROL_POWEROFF BIT(1) +#define HC_ACTION_HOST_CONTROL_POWERCYCLE BIT(2) + +#define HC_SMITYPE_NONE (0) +#define HC_SMITYPE_TYPE1 (1) +#define HC_SMITYPE_TYPE2 (2) +#define HC_SMITYPE_TYPE3 (3) + +#define ESM_APM_CMD (0x0A0) +#define ESM_APM_POWER_CYCLE (0x10) +#define ESM_STATUS_CMD_UNSUCCESSFUL (-1) + +#define CMOS_BASE_PORT (0x070) +#define CMOS_PAGE1_INDEX_PORT (0) +#define CMOS_PAGE1_DATA_PORT (1) +#define CMOS_PAGE2_INDEX_PORT_PIIX4 (2) +#define CMOS_PAGE2_DATA_PORT_PIIX4 (3) +#define PE1400_APM_CONTROL_PORT (0x0B0) +#define PCAT_APM_CONTROL_PORT (0x0B2) +#define PCAT_APM_STATUS_PORT (0x0B3) +#define PE1300_CMOS_CMD_STRUCT_PTR (0x38) +#define PE1400_CMOS_CMD_STRUCT_PTR (0x70) + +#define MAX_SYSMGMT_SHORTCMD_PARMBUF_LEN (14) +#define MAX_SYSMGMT_LONGCMD_SGENTRY_NUM (16) + +#define TIMEOUT_USEC_SHORT_SEMA_BLOCKING (10000) +#define EXPIRED_TIMER (0) + +#define SMI_CMD_MAGIC (0x534D4931) +#define SMM_EPS_SIG "$SCB" + +#define DCDBAS_DEV_ATTR_RW(_name) \ + DEVICE_ATTR(_name,0600,_name##_show,_name##_store); + +#define DCDBAS_DEV_ATTR_RO(_name) \ + DEVICE_ATTR(_name,0400,_name##_show,NULL); + +#define DCDBAS_DEV_ATTR_WO(_name) \ + DEVICE_ATTR(_name,0200,NULL,_name##_store); + +#define DCDBAS_BIN_ATTR_RW(_name) \ +struct bin_attribute bin_attr_##_name = { \ + .attr = { .name = __stringify(_name), \ + .mode = 0600 }, \ + .read = _name##_read, \ + .write = _name##_write, \ +} + +struct smi_cmd { + __u32 magic; + __u32 ebx; + __u32 ecx; + __u16 command_address; + __u8 command_code; + __u8 reserved; + __u8 command_buffer[1]; +} __attribute__ ((packed)); + +struct apm_cmd { + __u8 command; + __s8 status; + __u16 reserved; + union { + struct { + __u8 parm[MAX_SYSMGMT_SHORTCMD_PARMBUF_LEN]; + } __attribute__ ((packed)) shortreq; + + struct { + __u16 num_sg_entries; + struct { + __u32 size; + __u64 addr; + } __attribute__ ((packed)) + sglist[MAX_SYSMGMT_LONGCMD_SGENTRY_NUM]; + } __attribute__ ((packed)) longreq; + } __attribute__ ((packed)) parameters; +} __attribute__ ((packed)); + +int dcdbas_smi_request(struct smi_cmd *smi_cmd); + +struct smm_eps_table { + char smm_comm_buff_anchor[4]; + u8 length; + u8 checksum; + u8 version; + u64 smm_comm_buff_addr; + u64 num_of_4k_pages; +} __packed; + +#endif /* _DCDBAS_H_ */ + diff --git a/drivers/platform/x86/dell-smbios-smm.c b/drivers/platform/x86/dell-smbios-smm.c index 97a90bebc360..ab9b822a6dfe 100644 --- a/drivers/platform/x86/dell-smbios-smm.c +++ b/drivers/platform/x86/dell-smbios-smm.c @@ -18,7 +18,7 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/platform_device.h> -#include "../../firmware/dcdbas.h" +#include "dcdbas.h" #include "dell-smbios.h" static int da_command_address; diff --git a/drivers/platform/x86/dell_rbu.c b/drivers/platform/x86/dell_rbu.c new file mode 100644 index 000000000000..ccefa84f7305 --- /dev/null +++ b/drivers/platform/x86/dell_rbu.c @@ -0,0 +1,753 @@ +/* + * dell_rbu.c + * Bios Update driver for Dell systems + * Author: Dell Inc + * Abhay Salunke <abhay_salunke@dell.com> + * + * Copyright (C) 2005 Dell Inc. + * + * Remote BIOS Update (rbu) driver is used for updating DELL BIOS by + * creating entries in the /sys file systems on Linux 2.6 and higher + * kernels. The driver supports two mechanism to update the BIOS namely + * contiguous and packetized. Both these methods still require having some + * application to set the CMOS bit indicating the BIOS to update itself + * after a reboot. + * + * Contiguous method: + * This driver writes the incoming data in a monolithic image by allocating + * contiguous physical pages large enough to accommodate the incoming BIOS + * image size. + * + * Packetized method: + * The driver writes the incoming packet image by allocating a new packet + * on every time the packet data is written. This driver requires an + * application to break the BIOS image in to fixed sized packet chunks. + * + * See Documentation/dell_rbu.txt for more info. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2.0 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. + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/blkdev.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/moduleparam.h> +#include <linux/firmware.h> +#include <linux/dma-mapping.h> +#include <asm/set_memory.h> + +MODULE_AUTHOR("Abhay Salunke <abhay_salunke@dell.com>"); +MODULE_DESCRIPTION("Driver for updating BIOS image on DELL systems"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("3.2"); + +#define BIOS_SCAN_LIMIT 0xffffffff +#define MAX_IMAGE_LENGTH 16 +static struct _rbu_data { + void *image_update_buffer; + unsigned long image_update_buffer_size; + unsigned long bios_image_size; + int image_update_ordernum; + int dma_alloc; + spinlock_t lock; + unsigned long packet_read_count; + unsigned long num_packets; + unsigned long packetsize; + unsigned long imagesize; + int entry_created; +} rbu_data; + +static char image_type[MAX_IMAGE_LENGTH + 1] = "mono"; +module_param_string(image_type, image_type, sizeof (image_type), 0); +MODULE_PARM_DESC(image_type, + "BIOS image type. choose- mono or packet or init"); + +static unsigned long allocation_floor = 0x100000; +module_param(allocation_floor, ulong, 0644); +MODULE_PARM_DESC(allocation_floor, + "Minimum address for allocations when using Packet mode"); + +struct packet_data { + struct list_head list; + size_t length; + void *data; + int ordernum; +}; + +static struct packet_data packet_data_head; + +static struct platform_device *rbu_device; +static int context; +static dma_addr_t dell_rbu_dmaaddr; + +static void init_packet_head(void) +{ + INIT_LIST_HEAD(&packet_data_head.list); + rbu_data.packet_read_count = 0; + rbu_data.num_packets = 0; + rbu_data.packetsize = 0; + rbu_data.imagesize = 0; +} + +static int create_packet(void *data, size_t length) +{ + struct packet_data *newpacket; + int ordernum = 0; + int retval = 0; + unsigned int packet_array_size = 0; + void **invalid_addr_packet_array = NULL; + void *packet_data_temp_buf = NULL; + unsigned int idx = 0; + + pr_debug("create_packet: entry \n"); + + if (!rbu_data.packetsize) { + pr_debug("create_packet: packetsize not specified\n"); + retval = -EINVAL; + goto out_noalloc; + } + + spin_unlock(&rbu_data.lock); + + newpacket = kzalloc(sizeof (struct packet_data), GFP_KERNEL); + + if (!newpacket) { + printk(KERN_WARNING + "dell_rbu:%s: failed to allocate new " + "packet\n", __func__); + retval = -ENOMEM; + spin_lock(&rbu_data.lock); + goto out_noalloc; + } + + ordernum = get_order(length); + + /* + * BIOS errata mean we cannot allocate packets below 1MB or they will + * be overwritten by BIOS. + * + * array to temporarily hold packets + * that are below the allocation floor + * + * NOTE: very simplistic because we only need the floor to be at 1MB + * due to BIOS errata. This shouldn't be used for higher floors + * or you will run out of mem trying to allocate the array. + */ + packet_array_size = max( + (unsigned int)(allocation_floor / rbu_data.packetsize), + (unsigned int)1); + invalid_addr_packet_array = kcalloc(packet_array_size, sizeof(void *), + GFP_KERNEL); + + if (!invalid_addr_packet_array) { + printk(KERN_WARNING + "dell_rbu:%s: failed to allocate " + "invalid_addr_packet_array \n", + __func__); + retval = -ENOMEM; + spin_lock(&rbu_data.lock); + goto out_alloc_packet; + } + + while (!packet_data_temp_buf) { + packet_data_temp_buf = (unsigned char *) + __get_free_pages(GFP_KERNEL, ordernum); + if (!packet_data_temp_buf) { + printk(KERN_WARNING + "dell_rbu:%s: failed to allocate new " + "packet\n", __func__); + retval = -ENOMEM; + spin_lock(&rbu_data.lock); + goto out_alloc_packet_array; + } + + if ((unsigned long)virt_to_phys(packet_data_temp_buf) + < allocation_floor) { + pr_debug("packet 0x%lx below floor at 0x%lx.\n", + (unsigned long)virt_to_phys( + packet_data_temp_buf), + allocation_floor); + invalid_addr_packet_array[idx++] = packet_data_temp_buf; + packet_data_temp_buf = NULL; + } + } + /* + * set to uncachable or it may never get written back before reboot + */ + set_memory_uc((unsigned long)packet_data_temp_buf, 1 << ordernum); + + spin_lock(&rbu_data.lock); + + newpacket->data = packet_data_temp_buf; + + pr_debug("create_packet: newpacket at physical addr %lx\n", + (unsigned long)virt_to_phys(newpacket->data)); + + /* packets may not have fixed size */ + newpacket->length = length; + newpacket->ordernum = ordernum; + ++rbu_data.num_packets; + + /* initialize the newly created packet headers */ + INIT_LIST_HEAD(&newpacket->list); + list_add_tail(&newpacket->list, &packet_data_head.list); + + memcpy(newpacket->data, data, length); + + pr_debug("create_packet: exit \n"); + +out_alloc_packet_array: + /* always free packet array */ + for (;idx>0;idx--) { + pr_debug("freeing unused packet below floor 0x%lx.\n", + (unsigned long)virt_to_phys( + invalid_addr_packet_array[idx-1])); + free_pages((unsigned long)invalid_addr_packet_array[idx-1], + ordernum); + } + kfree(invalid_addr_packet_array); + +out_alloc_packet: + /* if error, free data */ + if (retval) + kfree(newpacket); + +out_noalloc: + return retval; +} + +static int packetize_data(const u8 *data, size_t length) +{ + int rc = 0; + int done = 0; + int packet_length; + u8 *temp; + u8 *end = (u8 *) data + length; + pr_debug("packetize_data: data length %zd\n", length); + if (!rbu_data.packetsize) { + printk(KERN_WARNING + "dell_rbu: packetsize not specified\n"); + return -EIO; + } + + temp = (u8 *) data; + + /* packetize the hunk */ + while (!done) { + if ((temp + rbu_data.packetsize) < end) + packet_length = rbu_data.packetsize; + else { + /* this is the last packet */ + packet_length = end - temp; + done = 1; + } + + if ((rc = create_packet(temp, packet_length))) + return rc; + + pr_debug("%p:%td\n", temp, (end - temp)); + temp += packet_length; + } + + rbu_data.imagesize = length; + + return rc; +} + +static int do_packet_read(char *data, struct list_head *ptemp_list, + int length, int bytes_read, int *list_read_count) +{ + void *ptemp_buf; + struct packet_data *newpacket = NULL; + int bytes_copied = 0; + int j = 0; + + newpacket = list_entry(ptemp_list, struct packet_data, list); + *list_read_count += newpacket->length; + + if (*list_read_count > bytes_read) { + /* point to the start of unread data */ + j = newpacket->length - (*list_read_count - bytes_read); + /* point to the offset in the packet buffer */ + ptemp_buf = (u8 *) newpacket->data + j; + /* + * check if there is enough room in + * * the incoming buffer + */ + if (length > (*list_read_count - bytes_read)) + /* + * copy what ever is there in this + * packet and move on + */ + bytes_copied = (*list_read_count - bytes_read); + else + /* copy the remaining */ + bytes_copied = length; + memcpy(data, ptemp_buf, bytes_copied); + } + return bytes_copied; +} + +static int packet_read_list(char *data, size_t * pread_length) +{ + struct list_head *ptemp_list; + int temp_count = 0; + int bytes_copied = 0; + int bytes_read = 0; + int remaining_bytes = 0; + char *pdest = data; + + /* check if we have any packets */ + if (0 == rbu_data.num_packets) + return -ENOMEM; + + remaining_bytes = *pread_length; + bytes_read = rbu_data.packet_read_count; + + ptemp_list = (&packet_data_head.list)->next; + while (!list_empty(ptemp_list)) { + bytes_copied = do_packet_read(pdest, ptemp_list, + remaining_bytes, bytes_read, &temp_count); + remaining_bytes -= bytes_copied; + bytes_read += bytes_copied; + pdest += bytes_copied; + /* + * check if we reached end of buffer before reaching the + * last packet + */ + if (remaining_bytes == 0) + break; + + ptemp_list = ptemp_list->next; + } + /*finally set the bytes read */ + *pread_length = bytes_read - rbu_data.packet_read_count; + rbu_data.packet_read_count = bytes_read; + return 0; +} + +static void packet_empty_list(void) +{ + struct list_head *ptemp_list; + struct list_head *pnext_list; + struct packet_data *newpacket; + + ptemp_list = (&packet_data_head.list)->next; + while (!list_empty(ptemp_list)) { + newpacket = + list_entry(ptemp_list, struct packet_data, list); + pnext_list = ptemp_list->next; + list_del(ptemp_list); + ptemp_list = pnext_list; + /* + * zero out the RBU packet memory before freeing + * to make sure there are no stale RBU packets left in memory + */ + memset(newpacket->data, 0, rbu_data.packetsize); + set_memory_wb((unsigned long)newpacket->data, + 1 << newpacket->ordernum); + free_pages((unsigned long) newpacket->data, + newpacket->ordernum); + kfree(newpacket); + } + rbu_data.packet_read_count = 0; + rbu_data.num_packets = 0; + rbu_data.imagesize = 0; +} + +/* + * img_update_free: Frees the buffer allocated for storing BIOS image + * Always called with lock held and returned with lock held + */ +static void img_update_free(void) +{ + if (!rbu_data.image_update_buffer) + return; + /* + * zero out this buffer before freeing it to get rid of any stale + * BIOS image copied in memory. + */ + memset(rbu_data.image_update_buffer, 0, + rbu_data.image_update_buffer_size); + if (rbu_data.dma_alloc == 1) + dma_free_coherent(NULL, rbu_data.bios_image_size, + rbu_data.image_update_buffer, dell_rbu_dmaaddr); + else + free_pages((unsigned long) rbu_data.image_update_buffer, + rbu_data.image_update_ordernum); + + /* + * Re-initialize the rbu_data variables after a free + */ + rbu_data.image_update_ordernum = -1; + rbu_data.image_update_buffer = NULL; + rbu_data.image_update_buffer_size = 0; + rbu_data.bios_image_size = 0; + rbu_data.dma_alloc = 0; +} + +/* + * img_update_realloc: This function allocates the contiguous pages to + * accommodate the requested size of data. The memory address and size + * values are stored globally and on every call to this function the new + * size is checked to see if more data is required than the existing size. + * If true the previous memory is freed and new allocation is done to + * accommodate the new size. If the incoming size is less then than the + * already allocated size, then that memory is reused. This function is + * called with lock held and returns with lock held. + */ +static int img_update_realloc(unsigned long size) +{ + unsigned char *image_update_buffer = NULL; + unsigned long rc; + unsigned long img_buf_phys_addr; + int ordernum; + int dma_alloc = 0; + + /* + * check if the buffer of sufficient size has been + * already allocated + */ + if (rbu_data.image_update_buffer_size >= size) { + /* + * check for corruption + */ + if ((size != 0) && (rbu_data.image_update_buffer == NULL)) { + printk(KERN_ERR "dell_rbu:%s: corruption " + "check failed\n", __func__); + return -EINVAL; + } + /* + * we have a valid pre-allocated buffer with + * sufficient size + */ + return 0; + } + + /* + * free any previously allocated buffer + */ + img_update_free(); + + spin_unlock(&rbu_data.lock); + + ordernum = get_order(size); + image_update_buffer = + (unsigned char *) __get_free_pages(GFP_KERNEL, ordernum); + + img_buf_phys_addr = + (unsigned long) virt_to_phys(image_update_buffer); + + if (img_buf_phys_addr > BIOS_SCAN_LIMIT) { + free_pages((unsigned long) image_update_buffer, ordernum); + ordernum = -1; + image_update_buffer = dma_alloc_coherent(NULL, size, + &dell_rbu_dmaaddr, GFP_KERNEL); + dma_alloc = 1; + } + + spin_lock(&rbu_data.lock); + + if (image_update_buffer != NULL) { + rbu_data.image_update_buffer = image_update_buffer; + rbu_data.image_update_buffer_size = size; + rbu_data.bios_image_size = + rbu_data.image_update_buffer_size; + rbu_data.image_update_ordernum = ordernum; + rbu_data.dma_alloc = dma_alloc; + rc = 0; + } else { + pr_debug("Not enough memory for image update:" + "size = %ld\n", size); + rc = -ENOMEM; + } + + return rc; +} + +static ssize_t read_packet_data(char *buffer, loff_t pos, size_t count) +{ + int retval; + size_t bytes_left; + size_t data_length; + char *ptempBuf = buffer; + + /* check to see if we have something to return */ + if (rbu_data.num_packets == 0) { + pr_debug("read_packet_data: no packets written\n"); + retval = -ENOMEM; + goto read_rbu_data_exit; + } + + if (pos > rbu_data.imagesize) { + retval = 0; + printk(KERN_WARNING "dell_rbu:read_packet_data: " + "data underrun\n"); + goto read_rbu_data_exit; + } + + bytes_left = rbu_data.imagesize - pos; + data_length = min(bytes_left, count); + + if ((retval = packet_read_list(ptempBuf, &data_length)) < 0) + goto read_rbu_data_exit; + + if ((pos + count) > rbu_data.imagesize) { + rbu_data.packet_read_count = 0; + /* this was the last copy */ + retval = bytes_left; + } else + retval = count; + + read_rbu_data_exit: + return retval; +} + +static ssize_t read_rbu_mono_data(char *buffer, loff_t pos, size_t count) +{ + /* check to see if we have something to return */ + if ((rbu_data.image_update_buffer == NULL) || + (rbu_data.bios_image_size == 0)) { + pr_debug("read_rbu_data_mono: image_update_buffer %p ," + "bios_image_size %lu\n", + rbu_data.image_update_buffer, + rbu_data.bios_image_size); + return -ENOMEM; + } + + return memory_read_from_buffer(buffer, count, &pos, + rbu_data.image_update_buffer, rbu_data.bios_image_size); +} + +static ssize_t read_rbu_data(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buffer, loff_t pos, size_t count) +{ + ssize_t ret_count = 0; + + spin_lock(&rbu_data.lock); + + if (!strcmp(image_type, "mono")) + ret_count = read_rbu_mono_data(buffer, pos, count); + else if (!strcmp(image_type, "packet")) + ret_count = read_packet_data(buffer, pos, count); + else + pr_debug("read_rbu_data: invalid image type specified\n"); + + spin_unlock(&rbu_data.lock); + return ret_count; +} + +static void callbackfn_rbu(const struct firmware *fw, void *context) +{ + rbu_data.entry_created = 0; + + if (!fw) + return; + + if (!fw->size) + goto out; + + spin_lock(&rbu_data.lock); + if (!strcmp(image_type, "mono")) { + if (!img_update_realloc(fw->size)) + memcpy(rbu_data.image_update_buffer, + fw->data, fw->size); + } else if (!strcmp(image_type, "packet")) { + /* + * we need to free previous packets if a + * new hunk of packets needs to be downloaded + */ + packet_empty_list(); + if (packetize_data(fw->data, fw->size)) + /* Incase something goes wrong when we are + * in middle of packetizing the data, we + * need to free up whatever packets might + * have been created before we quit. + */ + packet_empty_list(); + } else + pr_debug("invalid image type specified.\n"); + spin_unlock(&rbu_data.lock); + out: + release_firmware(fw); +} + +static ssize_t read_rbu_image_type(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buffer, loff_t pos, size_t count) +{ + int size = 0; + if (!pos) + size = scnprintf(buffer, count, "%s\n", image_type); + return size; +} + +static ssize_t write_rbu_image_type(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buffer, loff_t pos, size_t count) +{ + int rc = count; + int req_firm_rc = 0; + int i; + spin_lock(&rbu_data.lock); + /* + * Find the first newline or space + */ + for (i = 0; i < count; ++i) + if (buffer[i] == '\n' || buffer[i] == ' ') { + buffer[i] = '\0'; + break; + } + if (i == count) + buffer[count] = '\0'; + + if (strstr(buffer, "mono")) + strcpy(image_type, "mono"); + else if (strstr(buffer, "packet")) + strcpy(image_type, "packet"); + else if (strstr(buffer, "init")) { + /* + * If due to the user error the driver gets in a bad + * state where even though it is loaded , the + * /sys/class/firmware/dell_rbu entries are missing. + * to cover this situation the user can recreate entries + * by writing init to image_type. + */ + if (!rbu_data.entry_created) { + spin_unlock(&rbu_data.lock); + req_firm_rc = request_firmware_nowait(THIS_MODULE, + FW_ACTION_NOHOTPLUG, "dell_rbu", + &rbu_device->dev, GFP_KERNEL, &context, + callbackfn_rbu); + if (req_firm_rc) { + printk(KERN_ERR + "dell_rbu:%s request_firmware_nowait" + " failed %d\n", __func__, rc); + rc = -EIO; + } else + rbu_data.entry_created = 1; + + spin_lock(&rbu_data.lock); + } + } else { + printk(KERN_WARNING "dell_rbu: image_type is invalid\n"); + spin_unlock(&rbu_data.lock); + return -EINVAL; + } + + /* we must free all previous allocations */ + packet_empty_list(); + img_update_free(); + spin_unlock(&rbu_data.lock); + + return rc; +} + +static ssize_t read_rbu_packet_size(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buffer, loff_t pos, size_t count) +{ + int size = 0; + if (!pos) { + spin_lock(&rbu_data.lock); + size = scnprintf(buffer, count, "%lu\n", rbu_data.packetsize); + spin_unlock(&rbu_data.lock); + } + return size; +} + +static ssize_t write_rbu_packet_size(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buffer, loff_t pos, size_t count) +{ + unsigned long temp; + spin_lock(&rbu_data.lock); + packet_empty_list(); + sscanf(buffer, "%lu", &temp); + if (temp < 0xffffffff) + rbu_data.packetsize = temp; + + spin_unlock(&rbu_data.lock); + return count; +} + +static struct bin_attribute rbu_data_attr = { + .attr = {.name = "data", .mode = 0444}, + .read = read_rbu_data, +}; + +static struct bin_attribute rbu_image_type_attr = { + .attr = {.name = "image_type", .mode = 0644}, + .read = read_rbu_image_type, + .write = write_rbu_image_type, +}; + +static struct bin_attribute rbu_packet_size_attr = { + .attr = {.name = "packet_size", .mode = 0644}, + .read = read_rbu_packet_size, + .write = write_rbu_packet_size, +}; + +static int __init dcdrbu_init(void) +{ + int rc; + spin_lock_init(&rbu_data.lock); + + init_packet_head(); + rbu_device = platform_device_register_simple("dell_rbu", -1, NULL, 0); + if (IS_ERR(rbu_device)) { + printk(KERN_ERR + "dell_rbu:%s:platform_device_register_simple " + "failed\n", __func__); + return PTR_ERR(rbu_device); + } + + rc = sysfs_create_bin_file(&rbu_device->dev.kobj, &rbu_data_attr); + if (rc) + goto out_devreg; + rc = sysfs_create_bin_file(&rbu_device->dev.kobj, &rbu_image_type_attr); + if (rc) + goto out_data; + rc = sysfs_create_bin_file(&rbu_device->dev.kobj, + &rbu_packet_size_attr); + if (rc) + goto out_imtype; + + rbu_data.entry_created = 0; + return 0; + +out_imtype: + sysfs_remove_bin_file(&rbu_device->dev.kobj, &rbu_image_type_attr); +out_data: + sysfs_remove_bin_file(&rbu_device->dev.kobj, &rbu_data_attr); +out_devreg: + platform_device_unregister(rbu_device); + return rc; +} + +static __exit void dcdrbu_exit(void) +{ + spin_lock(&rbu_data.lock); + packet_empty_list(); + img_update_free(); + spin_unlock(&rbu_data.lock); + platform_device_unregister(rbu_device); +} + +module_exit(dcdrbu_exit); +module_init(dcdrbu_init); + +/* vim:noet:ts=8:sw=8 +*/ diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index a4bbf6ecd1f0..e6946a9beb5a 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -177,7 +177,7 @@ struct eeepc_laptop { struct rfkill *wwan3g_rfkill; struct rfkill *wimax_rfkill; - struct hotplug_slot *hotplug_slot; + struct hotplug_slot hotplug_slot; struct mutex hotplug_lock; struct led_classdev tpd_led; @@ -582,7 +582,7 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle) mutex_lock(&eeepc->hotplug_lock); pci_lock_rescan_remove(); - if (!eeepc->hotplug_slot) + if (!eeepc->hotplug_slot.ops) goto out_unlock; port = acpi_get_pci_dev(handle); @@ -715,8 +715,11 @@ static void eeepc_unregister_rfkill_notifier(struct eeepc_laptop *eeepc, static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) { - struct eeepc_laptop *eeepc = hotplug_slot->private; - int val = get_acpi(eeepc, CM_ASL_WLAN); + struct eeepc_laptop *eeepc; + int val; + + eeepc = container_of(hotplug_slot, struct eeepc_laptop, hotplug_slot); + val = get_acpi(eeepc, CM_ASL_WLAN); if (val == 1 || val == 0) *value = val; @@ -726,8 +729,7 @@ static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, return 0; } -static struct hotplug_slot_ops eeepc_hotplug_slot_ops = { - .owner = THIS_MODULE, +static const struct hotplug_slot_ops eeepc_hotplug_slot_ops = { .get_adapter_status = eeepc_get_adapter_status, .get_power_status = eeepc_get_adapter_status, }; @@ -742,21 +744,9 @@ static int eeepc_setup_pci_hotplug(struct eeepc_laptop *eeepc) return -ENODEV; } - eeepc->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL); - if (!eeepc->hotplug_slot) - goto error_slot; + eeepc->hotplug_slot.ops = &eeepc_hotplug_slot_ops; - eeepc->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info), - GFP_KERNEL); - if (!eeepc->hotplug_slot->info) - goto error_info; - - eeepc->hotplug_slot->private = eeepc; - eeepc->hotplug_slot->ops = &eeepc_hotplug_slot_ops; - eeepc_get_adapter_status(eeepc->hotplug_slot, - &eeepc->hotplug_slot->info->adapter_status); - - ret = pci_hp_register(eeepc->hotplug_slot, bus, 0, "eeepc-wifi"); + ret = pci_hp_register(&eeepc->hotplug_slot, bus, 0, "eeepc-wifi"); if (ret) { pr_err("Unable to register hotplug slot - %d\n", ret); goto error_register; @@ -765,11 +755,7 @@ static int eeepc_setup_pci_hotplug(struct eeepc_laptop *eeepc) return 0; error_register: - kfree(eeepc->hotplug_slot->info); -error_info: - kfree(eeepc->hotplug_slot); - eeepc->hotplug_slot = NULL; -error_slot: + eeepc->hotplug_slot.ops = NULL; return ret; } @@ -830,11 +816,8 @@ static void eeepc_rfkill_exit(struct eeepc_laptop *eeepc) eeepc->wlan_rfkill = NULL; } - if (eeepc->hotplug_slot) { - pci_hp_deregister(eeepc->hotplug_slot); - kfree(eeepc->hotplug_slot->info); - kfree(eeepc->hotplug_slot); - } + if (eeepc->hotplug_slot.ops) + pci_hp_deregister(&eeepc->hotplug_slot); if (eeepc->bluetooth_rfkill) { rfkill_unregister(eeepc->bluetooth_rfkill); diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index d4f1259ff5a2..b6489cba2985 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -212,7 +212,7 @@ static int read_ec_data(acpi_handle handle, int cmd, unsigned long *data) return 0; } } - pr_err("timeout in read_ec_cmd\n"); + pr_err("timeout in %s\n", __func__); return -1; } @@ -1147,6 +1147,13 @@ static const struct dmi_system_id no_hw_rfkill_list[] = { }, }, { + .ident = "Lenovo Legion Y530-15ICH", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Legion Y530-15ICH"), + }, + }, + { .ident = "Lenovo Legion Y720-15IKB", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c index 6cf9b7fa5bf0..e28bcf61b126 100644 --- a/drivers/platform/x86/intel-hid.c +++ b/drivers/platform/x86/intel-hid.c @@ -1,19 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Intel HID event & 5 button array driver * * Copyright (C) 2015 Alex Hung <alex.hung@canonical.com> * Copyright (C) 2015 Andrew Lutomirski <luto@kernel.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * */ #include <linux/acpi.h> diff --git a/drivers/platform/x86/intel-rst.c b/drivers/platform/x86/intel-rst.c index 7344d841f4d9..3b81cb896fed 100644 --- a/drivers/platform/x86/intel-rst.c +++ b/drivers/platform/x86/intel-rst.c @@ -1,26 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2013 Matthew Garrett <mjg59@srcf.ucam.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ - -#include <linux/init.h> +#include <linux/acpi.h> #include <linux/module.h> #include <linux/slab.h> -#include <linux/acpi.h> MODULE_LICENSE("GPL"); @@ -53,12 +38,10 @@ static ssize_t irst_store_wakeup_events(struct device *dev, acpi = to_acpi_device(dev); error = kstrtoul(buf, 0, &value); - if (error) return error; status = acpi_execute_simple_method(acpi->handle, "SFFS", value); - if (ACPI_FAILURE(status)) return -EINVAL; @@ -99,12 +82,10 @@ static ssize_t irst_store_wakeup_time(struct device *dev, acpi = to_acpi_device(dev); error = kstrtoul(buf, 0, &value); - if (error) return error; status = acpi_execute_simple_method(acpi->handle, "SFTV", value); - if (ACPI_FAILURE(status)) return -EINVAL; diff --git a/drivers/platform/x86/intel-smartconnect.c b/drivers/platform/x86/intel-smartconnect.c index bbe4c06c769f..64c2dc93472f 100644 --- a/drivers/platform/x86/intel-smartconnect.c +++ b/drivers/platform/x86/intel-smartconnect.c @@ -1,25 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2013 Matthew Garrett <mjg59@srcf.ucam.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ - -#include <linux/init.h> -#include <linux/module.h> #include <linux/acpi.h> +#include <linux/module.h> MODULE_LICENSE("GPL"); @@ -44,6 +29,7 @@ static const struct acpi_device_id smartconnect_ids[] = { {"INT33A0", 0}, {"", 0} }; +MODULE_DEVICE_TABLE(acpi, smartconnect_ids); static struct acpi_driver smartconnect_driver = { .owner = THIS_MODULE, @@ -56,5 +42,3 @@ static struct acpi_driver smartconnect_driver = { }; module_acpi_driver(smartconnect_driver); - -MODULE_DEVICE_TABLE(acpi, smartconnect_ids); diff --git a/drivers/platform/x86/intel-wmi-thunderbolt.c b/drivers/platform/x86/intel-wmi-thunderbolt.c index c2257bd06f18..9ded8e2af312 100644 --- a/drivers/platform/x86/intel-wmi-thunderbolt.c +++ b/drivers/platform/x86/intel-wmi-thunderbolt.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * WMI Thunderbolt driver * * Copyright (C) 2017 Dell Inc. All Rights Reserved. - * - * 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) KBUILD_MODNAME ": " fmt @@ -38,12 +30,16 @@ static ssize_t force_power_store(struct device *dev, input.length = sizeof(u8); input.pointer = &mode; mode = hex_to_bin(buf[0]); + dev_dbg(dev, "force_power: storing %#x\n", mode); if (mode == 0 || mode == 1) { status = wmi_evaluate_method(INTEL_WMI_THUNDERBOLT_GUID, 0, 1, &input, NULL); - if (ACPI_FAILURE(status)) + if (ACPI_FAILURE(status)) { + dev_dbg(dev, "force_power: failed to evaluate ACPI method\n"); return -ENODEV; + } } else { + dev_dbg(dev, "force_power: unsupported mode\n"); return -EINVAL; } return count; @@ -95,4 +91,4 @@ module_wmi_driver(intel_wmi_thunderbolt_driver); MODULE_ALIAS("wmi:" INTEL_WMI_THUNDERBOLT_GUID); MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>"); MODULE_DESCRIPTION("Intel WMI Thunderbolt force power driver"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel_atomisp2_pm.c b/drivers/platform/x86/intel_atomisp2_pm.c new file mode 100644 index 000000000000..9371603a0ac9 --- /dev/null +++ b/drivers/platform/x86/intel_atomisp2_pm.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Dummy driver for Intel's Image Signal Processor found on Bay and Cherry + * Trail devices. The sole purpose of this driver is to allow the ISP to + * be put in D3. + * + * Copyright (C) 2018 Hans de Goede <hdegoede@redhat.com> + * + * Based on various non upstream patches for ISP support: + * Copyright (C) 2010-2017 Intel Corporation. All rights reserved. + * Copyright (c) 2010 Silicon Hive www.siliconhive.com. + */ + +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/pci.h> +#include <linux/pm_runtime.h> +#include <asm/iosf_mbi.h> + +/* PCI configuration regs */ +#define PCI_INTERRUPT_CTRL 0x9c + +#define PCI_CSI_CONTROL 0xe8 +#define PCI_CSI_CONTROL_PORTS_OFF_MASK 0x7 + +/* IOSF BT_MBI_UNIT_PMC regs */ +#define ISPSSPM0 0x39 +#define ISPSSPM0_ISPSSC_OFFSET 0 +#define ISPSSPM0_ISPSSC_MASK 0x00000003 +#define ISPSSPM0_ISPSSS_OFFSET 24 +#define ISPSSPM0_ISPSSS_MASK 0x03000000 +#define ISPSSPM0_IUNIT_POWER_ON 0x0 +#define ISPSSPM0_IUNIT_POWER_OFF 0x3 + +static int isp_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + unsigned long timeout; + u32 val; + + pci_write_config_dword(dev, PCI_INTERRUPT_CTRL, 0); + + /* + * MRFLD IUNIT DPHY is located in an always-power-on island + * MRFLD HW design need all CSI ports are disabled before + * powering down the IUNIT. + */ + pci_read_config_dword(dev, PCI_CSI_CONTROL, &val); + val |= PCI_CSI_CONTROL_PORTS_OFF_MASK; + pci_write_config_dword(dev, PCI_CSI_CONTROL, val); + + /* Write 0x3 to ISPSSPM0 bit[1:0] to power off the IUNIT */ + iosf_mbi_modify(BT_MBI_UNIT_PMC, MBI_REG_READ, ISPSSPM0, + ISPSSPM0_IUNIT_POWER_OFF, ISPSSPM0_ISPSSC_MASK); + + /* + * There should be no IUNIT access while power-down is + * in progress HW sighting: 4567865 + * Wait up to 50 ms for the IUNIT to shut down. + */ + timeout = jiffies + msecs_to_jiffies(50); + while (1) { + /* Wait until ISPSSPM0 bit[25:24] shows 0x3 */ + iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ, ISPSSPM0, &val); + val = (val & ISPSSPM0_ISPSSS_MASK) >> ISPSSPM0_ISPSSS_OFFSET; + if (val == ISPSSPM0_IUNIT_POWER_OFF) + break; + + if (time_after(jiffies, timeout)) { + dev_err(&dev->dev, "IUNIT power-off timeout.\n"); + return -EBUSY; + } + usleep_range(1000, 2000); + } + + pm_runtime_allow(&dev->dev); + pm_runtime_put_sync_suspend(&dev->dev); + + return 0; +} + +static void isp_remove(struct pci_dev *dev) +{ + pm_runtime_get_sync(&dev->dev); + pm_runtime_forbid(&dev->dev); +} + +static int isp_pci_suspend(struct device *dev) +{ + return 0; +} + +static int isp_pci_resume(struct device *dev) +{ + return 0; +} + +static UNIVERSAL_DEV_PM_OPS(isp_pm_ops, isp_pci_suspend, + isp_pci_resume, NULL); + +static const struct pci_device_id isp_id_table[] = { + { PCI_VDEVICE(INTEL, 0x22b8), }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, isp_id_table); + +static struct pci_driver isp_pci_driver = { + .name = "intel_atomisp2_pm", + .id_table = isp_id_table, + .probe = isp_probe, + .remove = isp_remove, + .driver.pm = &isp_pm_ops, +}; + +module_pci_driver(isp_pci_driver); + +MODULE_DESCRIPTION("Intel AtomISP2 dummy / power-management drv (for suspend)"); +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel_bxtwc_tmu.c b/drivers/platform/x86/intel_bxtwc_tmu.c index 227943a20212..951c105bafc1 100644 --- a/drivers/platform/x86/intel_bxtwc_tmu.c +++ b/drivers/platform/x86/intel_bxtwc_tmu.c @@ -1,21 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * intel_bxtwc_tmu.c - Intel BXT Whiskey Cove PMIC TMU driver + * Intel BXT Whiskey Cove PMIC TMU driver * * Copyright (C) 2016 Intel Corporation. All rights reserved. * * This driver adds TMU (Time Management Unit) support for Intel BXT platform. * It enables the alarm wake-up functionality in the TMU unit of Whiskey Cove * PMIC. - * - * 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. - * */ #include <linux/module.h> diff --git a/drivers/platform/x86/intel_cht_int33fe.c b/drivers/platform/x86/intel_cht_int33fe.c index 7166f1cf8a1d..464fe93657b5 100644 --- a/drivers/platform/x86/intel_cht_int33fe.c +++ b/drivers/platform/x86/intel_cht_int33fe.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Intel Cherry Trail ACPI INT33FE pseudo device driver * * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.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. - * * Some Intel Cherry Trail based device which ship with Windows 10, have * this weird INT33FE ACPI device with a CRS table with 4 I2cSerialBusV2 * resources, for 4 different chips attached to various i2c busses: @@ -35,7 +32,7 @@ struct cht_int33fe_data { struct i2c_client *fusb302; struct i2c_client *pi3usb30532; /* Contain a list-head must be per device */ - struct device_connection connections[3]; + struct device_connection connections[5]; }; /* @@ -175,19 +172,20 @@ static int cht_int33fe_probe(struct platform_device *pdev) return -EPROBE_DEFER; /* Wait for i2c-adapter to load */ } - data->connections[0].endpoint[0] = "i2c-fusb302"; + data->connections[0].endpoint[0] = "port0"; data->connections[0].endpoint[1] = "i2c-pi3usb30532"; data->connections[0].id = "typec-switch"; - data->connections[1].endpoint[0] = "i2c-fusb302"; + data->connections[1].endpoint[0] = "port0"; data->connections[1].endpoint[1] = "i2c-pi3usb30532"; data->connections[1].id = "typec-mux"; - data->connections[2].endpoint[0] = "i2c-fusb302"; - data->connections[2].endpoint[1] = "intel_xhci_usb_sw-role-switch"; - data->connections[2].id = "usb-role-switch"; + data->connections[2].endpoint[0] = "port0"; + data->connections[2].endpoint[1] = "i2c-pi3usb30532"; + data->connections[2].id = "idff01m01"; + data->connections[3].endpoint[0] = "i2c-fusb302"; + data->connections[3].endpoint[1] = "intel_xhci_usb_sw-role-switch"; + data->connections[3].id = "usb-role-switch"; - device_connection_add(&data->connections[0]); - device_connection_add(&data->connections[1]); - device_connection_add(&data->connections[2]); + device_connections_add(data->connections); memset(&board_info, 0, sizeof(board_info)); strlcpy(board_info.type, "typec_fusb302", I2C_NAME_SIZE); @@ -218,9 +216,7 @@ out_unregister_max17047: if (data->max17047) i2c_unregister_device(data->max17047); - device_connection_remove(&data->connections[2]); - device_connection_remove(&data->connections[1]); - device_connection_remove(&data->connections[0]); + device_connections_remove(data->connections); return -EPROBE_DEFER; /* Wait for the i2c-adapter to load */ } @@ -234,9 +230,7 @@ static int cht_int33fe_remove(struct platform_device *pdev) if (data->max17047) i2c_unregister_device(data->max17047); - device_connection_remove(&data->connections[2]); - device_connection_remove(&data->connections[1]); - device_connection_remove(&data->connections[0]); + device_connections_remove(data->connections); return 0; } @@ -260,4 +254,4 @@ module_platform_driver(cht_int33fe_driver); MODULE_DESCRIPTION("Intel Cherry Trail ACPI INT33FE pseudo device driver"); MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel_chtdc_ti_pwrbtn.c b/drivers/platform/x86/intel_chtdc_ti_pwrbtn.c index 38b8e7cfe88c..0df2e82dd249 100644 --- a/drivers/platform/x86/intel_chtdc_ti_pwrbtn.c +++ b/drivers/platform/x86/intel_chtdc_ti_pwrbtn.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Power-button driver for Dollar Cove TI PMIC * Copyright (C) 2014 Intel Corp diff --git a/drivers/platform/x86/intel_int0002_vgpio.c b/drivers/platform/x86/intel_int0002_vgpio.c index e89ad4964dc1..4b8f7305fc8a 100644 --- a/drivers/platform/x86/intel_int0002_vgpio.c +++ b/drivers/platform/x86/intel_int0002_vgpio.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Intel INT0002 "Virtual GPIO" driver * @@ -9,10 +10,6 @@ * * Author: Dyut Kumar Sil <dyut.k.sil@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. - * * Some peripherals on Bay Trail and Cherry Trail platforms signal a Power * Management Event (PME) to the Power Management Controller (PMC) to wakeup * the system. When this happens software needs to clear the PME bus 0 status @@ -57,11 +54,7 @@ #define ICPU(model) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, } static const struct x86_cpu_id int0002_cpu_ids[] = { -/* - * Limit ourselves to Cherry Trail for now, until testing shows we - * need to handle the INT0002 device on Baytrail too. - * ICPU(INTEL_FAM6_ATOM_SILVERMONT), * Valleyview, Bay Trail * - */ + ICPU(INTEL_FAM6_ATOM_SILVERMONT), /* Valleyview, Bay Trail */ ICPU(INTEL_FAM6_ATOM_AIRMONT), /* Braswell, Cherry Trail */ {} }; @@ -110,6 +103,21 @@ static void int0002_irq_mask(struct irq_data *data) outl(gpe_en_reg, GPE0A_EN_PORT); } +static int int0002_irq_set_wake(struct irq_data *data, unsigned int on) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct platform_device *pdev = to_platform_device(chip->parent); + int irq = platform_get_irq(pdev, 0); + + /* Propagate to parent irq */ + if (on) + enable_irq_wake(irq); + else + disable_irq_wake(irq); + + return 0; +} + static irqreturn_t int0002_irq(int irq, void *data) { struct gpio_chip *chip = data; @@ -132,6 +140,7 @@ static struct irq_chip int0002_irqchip = { .irq_ack = int0002_irq_ack, .irq_mask = int0002_irq_mask, .irq_unmask = int0002_irq_unmask, + .irq_set_wake = int0002_irq_set_wake, }; static int int0002_probe(struct platform_device *pdev) @@ -216,4 +225,4 @@ module_platform_driver(int0002_driver); MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); MODULE_DESCRIPTION("Intel INT0002 Virtual GPIO driver"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c index c5ece7ef08c6..225638a1b09e 100644 --- a/drivers/platform/x86/intel_ips.c +++ b/drivers/platform/x86/intel_ips.c @@ -1,18 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2009-2010 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. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". - * * Authors: * Jesse Barnes <jbarnes@virtuousgeek.org> */ @@ -1697,6 +1686,6 @@ static struct pci_driver ips_pci_driver = { module_pci_driver(ips_pci_driver); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Jesse Barnes <jbarnes@virtuousgeek.org>"); MODULE_DESCRIPTION("Intelligent Power Sharing Driver"); diff --git a/drivers/platform/x86/intel_ips.h b/drivers/platform/x86/intel_ips.h index 60f4e3ddbe9f..512ad234ad0d 100644 --- a/drivers/platform/x86/intel_ips.h +++ b/drivers/platform/x86/intel_ips.h @@ -1,17 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2010 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. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". */ void ips_link_to_i915_driver(void); diff --git a/drivers/platform/x86/intel_menlow.c b/drivers/platform/x86/intel_menlow.c index ef9b0af8cdd3..77eb8709c931 100644 --- a/drivers/platform/x86/intel_menlow.c +++ b/drivers/platform/x86/intel_menlow.c @@ -1,25 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * intel_menlow.c - Intel menlow Driver for thermal management extension + * Intel menlow Driver for thermal management extension * * Copyright (C) 2008 Intel Corp * Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com> * Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com> - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * This driver creates the sys I/F for programming the sensors. * It also implements the driver for intel menlow memory controller (hardware @@ -29,20 +14,19 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/acpi.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/types.h> #include <linux/pci.h> #include <linux/pm.h> +#include <linux/slab.h> #include <linux/thermal.h> -#include <linux/acpi.h> +#include <linux/types.h> MODULE_AUTHOR("Thomas Sujith"); MODULE_AUTHOR("Zhang Rui"); MODULE_DESCRIPTION("Intel Menlow platform specific driver"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); /* * Memory controller device control diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c index 5ad44204a9c3..292bace83f1e 100644 --- a/drivers/platform/x86/intel_mid_powerbtn.c +++ b/drivers/platform/x86/intel_mid_powerbtn.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Power button driver for Intel MID platforms. * @@ -5,18 +6,8 @@ * * Author: Hong Liu <hong.liu@intel.com> * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * 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. */ -#include <linux/init.h> #include <linux/input.h> #include <linux/interrupt.h> #include <linux/mfd/intel_msic.h> @@ -121,12 +112,9 @@ static const struct mid_pb_ddata mrfld_ddata = { .setup = mrfld_setup, }; -#define ICPU(model, ddata) \ - { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, (kernel_ulong_t)&ddata } - static const struct x86_cpu_id mid_pb_cpu_ids[] = { - ICPU(INTEL_FAM6_ATOM_SALTWELL_MID, mfld_ddata), - ICPU(INTEL_FAM6_ATOM_SILVERMONT_MID, mrfld_ddata), + INTEL_CPU_FAM6(ATOM_SALTWELL_MID, mfld_ddata), + INTEL_CPU_FAM6(ATOM_SILVERMONT_MID, mrfld_ddata), {} }; diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c index 008a76903cbf..f402e2e74a38 100644 --- a/drivers/platform/x86/intel_mid_thermal.c +++ b/drivers/platform/x86/intel_mid_thermal.c @@ -1,39 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * intel_mid_thermal.c - Intel MID platform thermal driver + * Intel MID platform thermal driver * * Copyright (C) 2011 Intel Corporation * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Author: Durgadoss R <durgadoss.r@intel.com> */ #define pr_fmt(fmt) "intel_mid_thermal: " fmt -#include <linux/module.h> -#include <linux/init.h> +#include <linux/device.h> #include <linux/err.h> +#include <linux/mfd/intel_msic.h> +#include <linux/module.h> #include <linux/param.h> -#include <linux/device.h> #include <linux/platform_device.h> -#include <linux/slab.h> #include <linux/pm.h> +#include <linux/slab.h> #include <linux/thermal.h> -#include <linux/mfd/intel_msic.h> /* Number of thermal sensors */ #define MSIC_THERMAL_SENSORS 4 @@ -567,4 +551,4 @@ module_platform_driver(mid_thermal_driver); MODULE_AUTHOR("Durgadoss R <durgadoss.r@intel.com>"); MODULE_DESCRIPTION("Intel Medfield Platform Thermal Driver"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel_oaktrail.c b/drivers/platform/x86/intel_oaktrail.c index 5747f63c8d9f..3c0438ba385e 100644 --- a/drivers/platform/x86/intel_oaktrail.c +++ b/drivers/platform/x86/intel_oaktrail.c @@ -1,5 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0+ /* - * intel_oaktrail.c - Intel OakTrail Platform support. + * Intel OakTrail Platform support * * Copyright (C) 2010-2011 Intel Corporation * Author: Yin Kangkai (kangkai.yin@intel.com) @@ -8,21 +9,6 @@ * <cezary.jackiewicz (at) gmail.com>, based on MSI driver * Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * * This driver does below things: * 1. registers itself in the Linux backlight control in * /sys/class/backlight/intel_oaktrail/ @@ -38,18 +24,18 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/init.h> #include <linux/acpi.h> -#include <linux/fb.h> -#include <linux/mutex.h> +#include <linux/backlight.h> +#include <linux/dmi.h> #include <linux/err.h> +#include <linux/fb.h> #include <linux/i2c.h> -#include <linux/backlight.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> #include <linux/platform_device.h> -#include <linux/dmi.h> #include <linux/rfkill.h> + #include <acpi/video.h> #define DRIVER_NAME "intel_oaktrail" diff --git a/drivers/platform/x86/intel_pmc_core.c b/drivers/platform/x86/intel_pmc_core.c index 2d272a3e0176..6b31d410cb09 100644 --- a/drivers/platform/x86/intel_pmc_core.c +++ b/drivers/platform/x86/intel_pmc_core.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Intel Core SoC Power Management Controller Driver * @@ -6,16 +7,6 @@ * * Authors: Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com> * Vishwanath Somayaji <vishwanath.somayaji@intel.com> - * - * 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 diff --git a/drivers/platform/x86/intel_pmc_core.h b/drivers/platform/x86/intel_pmc_core.h index 93a7e99e1f8b..be045348ad86 100644 --- a/drivers/platform/x86/intel_pmc_core.h +++ b/drivers/platform/x86/intel_pmc_core.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Intel Core SoC Power Management Controller Header File * @@ -6,16 +7,6 @@ * * Authors: Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com> * Vishwanath Somayaji <vishwanath.somayaji@intel.com> - * - * 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_CORE_H diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c index e7edc8c63936..7964ba22ef8d 100644 --- a/drivers/platform/x86/intel_pmc_ipc.c +++ b/drivers/platform/x86/intel_pmc_ipc.c @@ -1,39 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * intel_pmc_ipc.c: Driver for the Intel PMC IPC mechanism + * Driver for the Intel PMC IPC mechanism * * (C) Copyright 2014-2015 Intel Corporation * - * This driver is based on Intel SCU IPC driver(intel_scu_opc.c) by + * This driver is based on Intel SCU IPC driver(intel_scu_ipc.c) by * Sreedhara DS <sreedhara.ds@intel.com> * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License. - * * PMC running in ARC processor communicates with other entity running in IA * core through IPC mechanism which in turn messaging between IA core ad PMC. */ -#include <linux/module.h> +#include <linux/acpi.h> +#include <linux/atomic.h> +#include <linux/bitops.h> #include <linux/delay.h> -#include <linux/errno.h> -#include <linux/init.h> #include <linux/device.h> -#include <linux/pm.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/io-64-nonatomic-lo-hi.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/notifier.h> #include <linux/pci.h> #include <linux/platform_device.h> -#include <linux/interrupt.h> +#include <linux/pm.h> #include <linux/pm_qos.h> -#include <linux/kernel.h> -#include <linux/bitops.h> #include <linux/sched.h> -#include <linux/atomic.h> -#include <linux/notifier.h> -#include <linux/suspend.h> -#include <linux/acpi.h> -#include <linux/io-64-nonatomic-lo-hi.h> #include <linux/spinlock.h> +#include <linux/suspend.h> #include <asm/intel_pmc_ipc.h> @@ -1029,7 +1024,7 @@ static void __exit intel_pmc_ipc_exit(void) MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>"); MODULE_DESCRIPTION("Intel PMC IPC driver"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); /* Some modules are dependent on this, so init earlier */ fs_initcall(intel_pmc_ipc_init); diff --git a/drivers/platform/x86/intel_punit_ipc.c b/drivers/platform/x86/intel_punit_ipc.c index 2efeab650345..79671927f4ef 100644 --- a/drivers/platform/x86/intel_punit_ipc.c +++ b/drivers/platform/x86/intel_punit_ipc.c @@ -1,25 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Driver for the Intel P-Unit Mailbox IPC mechanism * * (C) Copyright 2015 Intel Corporation * - * 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. - * * The heart of the P-Unit is the Foxton microcontroller and its firmware, * which provide mailbox interface for power management usage. */ -#include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/acpi.h> -#include <linux/delay.h> #include <linux/bitops.h> +#include <linux/delay.h> #include <linux/device.h> #include <linux/interrupt.h> #include <linux/io.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> #include <linux/platform_device.h> + #include <asm/intel_punit_ipc.h> /* IPC Mailbox registers */ diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index 75c8fef7a482..cdab916fbf92 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * intel_scu_ipc.c: Driver for the Intel SCU IPC mechanism + * Driver for the Intel SCU IPC mechanism * * (C) Copyright 2008-2010,2015 Intel Corporation * Author: Sreedhara DS (sreedhara.ds@intel.com) * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License. - * * SCU running in ARC processor communicates with other entity running in IA * core through IPC mechanism which in turn messaging between IA core ad SCU. * SCU has two IPC mechanism IPC-1 and IPC-2. IPC-1 is used between IA32 and @@ -16,14 +12,16 @@ * IPC-1 Driver provides an API for power control unit registers (e.g. MSIC) * along with other APIs. */ + #include <linux/delay.h> +#include <linux/device.h> #include <linux/errno.h> #include <linux/init.h> -#include <linux/device.h> -#include <linux/pm.h> -#include <linux/pci.h> #include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/pm.h> #include <linux/sfi.h> + #include <asm/intel-mid.h> #include <asm/intel_scu_ipc.h> diff --git a/drivers/platform/x86/intel_scu_ipcutil.c b/drivers/platform/x86/intel_scu_ipcutil.c index aa454241489c..8afe6fa06d7b 100644 --- a/drivers/platform/x86/intel_scu_ipcutil.c +++ b/drivers/platform/x86/intel_scu_ipcutil.c @@ -1,32 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * intel_scu_ipc.c: Driver for the Intel SCU IPC mechanism + * Driver for the Intel SCU IPC mechanism * * (C) Copyright 2008-2010 Intel Corporation * Author: Sreedhara DS (sreedhara.ds@intel.com) * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License. - * - * This driver provides ioctl interfaces to call intel scu ipc driver api + * This driver provides IOCTL interfaces to call Intel SCU IPC driver API. */ -#include <linux/module.h> -#include <linux/kernel.h> #include <linux/errno.h> -#include <linux/types.h> -#include <linux/fs.h> #include <linux/fcntl.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/module.h> #include <linux/sched.h> -#include <linux/uaccess.h> #include <linux/slab.h> -#include <linux/init.h> +#include <linux/types.h> +#include <linux/uaccess.h> + #include <asm/intel_scu_ipc.h> static int major; -/* ioctl commnds */ +/* IOCTL commands */ #define INTE_SCU_IPC_REGISTER_READ 0 #define INTE_SCU_IPC_REGISTER_WRITE 1 #define INTE_SCU_IPC_REGISTER_UPDATE 2 diff --git a/drivers/platform/x86/intel_telemetry_core.c b/drivers/platform/x86/intel_telemetry_core.c index f378621b5fe9..d4040bb222b4 100644 --- a/drivers/platform/x86/intel_telemetry_core.c +++ b/drivers/platform/x86/intel_telemetry_core.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Intel SoC Core Telemetry Driver * Copyright (C) 2015, Intel Corporation. * All Rights Reserved. * - * 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. - * * Telemetry Framework provides platform related PM and performance statistics. * This file provides the core telemetry API implementation. */ @@ -460,4 +452,4 @@ module_exit(telemetry_module_exit); MODULE_AUTHOR("Souvik Kumar Chakravarty <souvik.k.chakravarty@intel.com>"); MODULE_DESCRIPTION("Intel SoC Telemetry Interface"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel_telemetry_debugfs.c b/drivers/platform/x86/intel_telemetry_debugfs.c index cee08f236292..40bce560eb30 100644 --- a/drivers/platform/x86/intel_telemetry_debugfs.c +++ b/drivers/platform/x86/intel_telemetry_debugfs.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Intel SOC Telemetry debugfs Driver: Currently supports APL * Copyright (c) 2015, Intel Corporation. * All Rights Reserved. * - * 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. - * * This file provides the debugfs interfaces for telemetry. * /sys/kernel/debug/telemetry/pss_info: Shows Primary Control Sub-Sys Counters * /sys/kernel/debug/telemetry/ioss_info: Shows IO Sub-System Counters @@ -72,9 +64,6 @@ #define TELEM_IOSS_DX_D0IX_EVTS 25 #define TELEM_IOSS_PG_EVTS 30 -#define TELEM_DEBUGFS_CPU(model, data) \ - { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, (unsigned long)&data} - #define TELEM_CHECK_AND_PARSE_EVTS(EVTID, EVTNUM, BUF, EVTLOG, EVTDAT, MASK) { \ if (evtlog[index].telem_evtid == (EVTID)) { \ for (idx = 0; idx < (EVTNUM); idx++) \ @@ -319,8 +308,8 @@ static struct telemetry_debugfs_conf telem_apl_debugfs_conf = { }; static const struct x86_cpu_id telemetry_debugfs_cpu_ids[] = { - TELEM_DEBUGFS_CPU(INTEL_FAM6_ATOM_GOLDMONT, telem_apl_debugfs_conf), - TELEM_DEBUGFS_CPU(INTEL_FAM6_ATOM_GOLDMONT_PLUS, telem_apl_debugfs_conf), + INTEL_CPU_FAM6(ATOM_GOLDMONT, telem_apl_debugfs_conf), + INTEL_CPU_FAM6(ATOM_GOLDMONT_PLUS, telem_apl_debugfs_conf), {} }; @@ -951,12 +940,16 @@ static int __init telemetry_debugfs_init(void) debugfs_conf = (struct telemetry_debugfs_conf *)id->driver_data; err = telemetry_pltconfig_valid(); - if (err < 0) + if (err < 0) { + pr_info("Invalid pltconfig, ensure IPC1 device is enabled in BIOS\n"); return -ENODEV; + } err = telemetry_debugfs_check_evts(); - if (err < 0) + if (err < 0) { + pr_info("telemetry_debugfs_check_evts failed\n"); return -EINVAL; + } register_pm_notifier(&pm_notifier); @@ -1037,4 +1030,4 @@ module_exit(telemetry_debugfs_exit); MODULE_AUTHOR("Souvik Kumar Chakravarty <souvik.k.chakravarty@intel.com>"); MODULE_DESCRIPTION("Intel SoC Telemetry debugfs Interface"); MODULE_VERSION(DRIVER_VERSION); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel_telemetry_pltdrv.c b/drivers/platform/x86/intel_telemetry_pltdrv.c index fcc6bee51a42..df8565bad595 100644 --- a/drivers/platform/x86/intel_telemetry_pltdrv.c +++ b/drivers/platform/x86/intel_telemetry_pltdrv.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Intel SOC Telemetry Platform Driver: Currently supports APL * Copyright (c) 2015, Intel Corporation. * All Rights Reserved. * - * 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. - * * This file provides the platform specific telemetry implementation for APL. * It used the PUNIT and PMC IPC interfaces for configuring the counters. * The accumulated results are fetched from SRAM. @@ -1242,4 +1234,4 @@ module_exit(telemetry_module_exit); MODULE_AUTHOR("Souvik Kumar Chakravarty <souvik.k.chakravarty@intel.com>"); MODULE_DESCRIPTION("Intel SoC Telemetry Platform Driver"); MODULE_VERSION(DRIVER_VERSION); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel_turbo_max_3.c b/drivers/platform/x86/intel_turbo_max_3.c index a6d5aa0c3c47..7b9cc841ab65 100644 --- a/drivers/platform/x86/intel_turbo_max_3.c +++ b/drivers/platform/x86/intel_turbo_max_3.c @@ -1,28 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Intel Turbo Boost Max Technology 3.0 legacy (non HWP) enumeration driver * Copyright (c) 2017, Intel Corporation. * All rights reserved. * * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> - * - * 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/kernel.h> +#include <linux/cpufeature.h> +#include <linux/cpuhotplug.h> #include <linux/init.h> +#include <linux/kernel.h> #include <linux/topology.h> #include <linux/workqueue.h> -#include <linux/cpuhotplug.h> -#include <linux/cpufeature.h> + #include <asm/cpu_device_id.h> #include <asm/intel-family.h> diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c new file mode 100644 index 000000000000..c0bb1f864dfe --- /dev/null +++ b/drivers/platform/x86/lg-laptop.c @@ -0,0 +1,700 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * lg-laptop.c - LG Gram ACPI features and hotkeys Driver + * + * Copyright (C) 2018 Matan Ziv-Av <matan@svgalib.org> + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/acpi.h> +#include <linux/input.h> +#include <linux/input/sparse-keymap.h> +#include <linux/kernel.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/types.h> + +#define LED_DEVICE(_name, max) struct led_classdev _name = { \ + .name = __stringify(_name), \ + .max_brightness = max, \ + .brightness_set = _name##_set, \ + .brightness_get = _name##_get, \ +} + +MODULE_AUTHOR("Matan Ziv-Av"); +MODULE_DESCRIPTION("LG WMI Hotkey Driver"); +MODULE_LICENSE("GPL"); + +#define WMI_EVENT_GUID0 "E4FB94F9-7F2B-4173-AD1A-CD1D95086248" +#define WMI_EVENT_GUID1 "023B133E-49D1-4E10-B313-698220140DC2" +#define WMI_EVENT_GUID2 "37BE1AC0-C3F2-4B1F-BFBE-8FDEAF2814D6" +#define WMI_EVENT_GUID3 "911BAD44-7DF8-4FBB-9319-BABA1C4B293B" +#define WMI_METHOD_WMAB "C3A72B38-D3EF-42D3-8CBB-D5A57049F66D" +#define WMI_METHOD_WMBB "2B4F501A-BD3C-4394-8DCF-00A7D2BC8210" +#define WMI_EVENT_GUID WMI_EVENT_GUID0 + +#define WMAB_METHOD "\\XINI.WMAB" +#define WMBB_METHOD "\\XINI.WMBB" +#define SB_GGOV_METHOD "\\_SB.GGOV" +#define GOV_TLED 0x2020008 +#define WM_GET 1 +#define WM_SET 2 +#define WM_KEY_LIGHT 0x400 +#define WM_TLED 0x404 +#define WM_FN_LOCK 0x407 +#define WM_BATT_LIMIT 0x61 +#define WM_READER_MODE 0xBF +#define WM_FAN_MODE 0x33 +#define WMBB_USB_CHARGE 0x10B +#define WMBB_BATT_LIMIT 0x10C + +#define PLATFORM_NAME "lg-laptop" + +MODULE_ALIAS("wmi:" WMI_EVENT_GUID0); +MODULE_ALIAS("wmi:" WMI_EVENT_GUID1); +MODULE_ALIAS("wmi:" WMI_EVENT_GUID2); +MODULE_ALIAS("wmi:" WMI_EVENT_GUID3); +MODULE_ALIAS("wmi:" WMI_METHOD_WMAB); +MODULE_ALIAS("wmi:" WMI_METHOD_WMBB); +MODULE_ALIAS("acpi*:LGEX0815:*"); + +static struct platform_device *pf_device; +static struct input_dev *wmi_input_dev; + +static u32 inited; +#define INIT_INPUT_WMI_0 0x01 +#define INIT_INPUT_WMI_2 0x02 +#define INIT_INPUT_ACPI 0x04 +#define INIT_TPAD_LED 0x08 +#define INIT_KBD_LED 0x10 +#define INIT_SPARSE_KEYMAP 0x80 + +static const struct key_entry wmi_keymap[] = { + {KE_KEY, 0x70, {KEY_F15} }, /* LG control panel (F1) */ + {KE_KEY, 0x74, {KEY_F13} }, /* Touchpad toggle (F5) */ + {KE_KEY, 0xf020000, {KEY_F14} }, /* Read mode (F9) */ + {KE_KEY, 0x10000000, {KEY_F16} },/* Keyboard backlight (F8) - pressing + * this key both sends an event and + * changes backlight level. + */ + {KE_KEY, 0x80, {KEY_RFKILL} }, + {KE_END, 0} +}; + +static int ggov(u32 arg0) +{ + union acpi_object args[1]; + union acpi_object *r; + acpi_status status; + acpi_handle handle; + struct acpi_object_list arg; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + int res; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = arg0; + + status = acpi_get_handle(NULL, (acpi_string) SB_GGOV_METHOD, &handle); + if (ACPI_FAILURE(status)) { + pr_err("Cannot get handle"); + return -ENODEV; + } + + arg.count = 1; + arg.pointer = args; + + status = acpi_evaluate_object(handle, NULL, &arg, &buffer); + if (ACPI_FAILURE(status)) { + acpi_handle_err(handle, "GGOV: call failed.\n"); + return -EINVAL; + } + + r = buffer.pointer; + if (r->type != ACPI_TYPE_INTEGER) { + kfree(r); + return -EINVAL; + } + + res = r->integer.value; + kfree(r); + + return res; +} + +static union acpi_object *lg_wmab(u32 method, u32 arg1, u32 arg2) +{ + union acpi_object args[3]; + acpi_status status; + acpi_handle handle; + struct acpi_object_list arg; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = method; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = arg1; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = arg2; + + status = acpi_get_handle(NULL, (acpi_string) WMAB_METHOD, &handle); + if (ACPI_FAILURE(status)) { + pr_err("Cannot get handle"); + return NULL; + } + + arg.count = 3; + arg.pointer = args; + + status = acpi_evaluate_object(handle, NULL, &arg, &buffer); + if (ACPI_FAILURE(status)) { + acpi_handle_err(handle, "WMAB: call failed.\n"); + return NULL; + } + + return buffer.pointer; +} + +static union acpi_object *lg_wmbb(u32 method_id, u32 arg1, u32 arg2) +{ + union acpi_object args[3]; + acpi_status status; + acpi_handle handle; + struct acpi_object_list arg; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + u8 buf[32]; + + *(u32 *)buf = method_id; + *(u32 *)(buf + 4) = arg1; + *(u32 *)(buf + 16) = arg2; + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = 0; /* ignored */ + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = 1; /* Must be 1 or 2. Does not matter which */ + args[2].type = ACPI_TYPE_BUFFER; + args[2].buffer.length = 32; + args[2].buffer.pointer = buf; + + status = acpi_get_handle(NULL, (acpi_string)WMBB_METHOD, &handle); + if (ACPI_FAILURE(status)) { + pr_err("Cannot get handle"); + return NULL; + } + + arg.count = 3; + arg.pointer = args; + + status = acpi_evaluate_object(handle, NULL, &arg, &buffer); + if (ACPI_FAILURE(status)) { + acpi_handle_err(handle, "WMAB: call failed.\n"); + return NULL; + } + + return (union acpi_object *)buffer.pointer; +} + +static void wmi_notify(u32 value, void *context) +{ + struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_status status; + long data = (long)context; + + pr_debug("event guid %li\n", data); + status = wmi_get_event_data(value, &response); + if (ACPI_FAILURE(status)) { + pr_err("Bad event status 0x%x\n", status); + return; + } + + obj = (union acpi_object *)response.pointer; + if (!obj) + return; + + if (obj->type == ACPI_TYPE_INTEGER) { + int eventcode = obj->integer.value; + struct key_entry *key; + + key = + sparse_keymap_entry_from_scancode(wmi_input_dev, eventcode); + if (key && key->type == KE_KEY) + sparse_keymap_report_entry(wmi_input_dev, key, 1, true); + } + + pr_debug("Type: %i Eventcode: 0x%llx\n", obj->type, + obj->integer.value); + kfree(response.pointer); +} + +static void wmi_input_setup(void) +{ + acpi_status status; + + wmi_input_dev = input_allocate_device(); + if (wmi_input_dev) { + wmi_input_dev->name = "LG WMI hotkeys"; + wmi_input_dev->phys = "wmi/input0"; + wmi_input_dev->id.bustype = BUS_HOST; + + if (sparse_keymap_setup(wmi_input_dev, wmi_keymap, NULL) || + input_register_device(wmi_input_dev)) { + pr_info("Cannot initialize input device"); + input_free_device(wmi_input_dev); + return; + } + + inited |= INIT_SPARSE_KEYMAP; + status = wmi_install_notify_handler(WMI_EVENT_GUID0, wmi_notify, + (void *)0); + if (ACPI_SUCCESS(status)) + inited |= INIT_INPUT_WMI_0; + + status = wmi_install_notify_handler(WMI_EVENT_GUID2, wmi_notify, + (void *)2); + if (ACPI_SUCCESS(status)) + inited |= INIT_INPUT_WMI_2; + } else { + pr_info("Cannot allocate input device"); + } +} + +static void acpi_notify(struct acpi_device *device, u32 event) +{ + struct key_entry *key; + + acpi_handle_debug(device->handle, "notify: %d\n", event); + if (inited & INIT_SPARSE_KEYMAP) { + key = sparse_keymap_entry_from_scancode(wmi_input_dev, 0x80); + if (key && key->type == KE_KEY) + sparse_keymap_report_entry(wmi_input_dev, key, 1, true); + } +} + +static ssize_t fan_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + bool value; + union acpi_object *r; + u32 m; + int ret; + + ret = kstrtobool(buffer, &value); + if (ret) + return ret; + + r = lg_wmab(WM_FAN_MODE, WM_GET, 0); + if (!r) + return -EIO; + + if (r->type != ACPI_TYPE_INTEGER) { + kfree(r); + return -EIO; + } + + m = r->integer.value; + kfree(r); + r = lg_wmab(WM_FAN_MODE, WM_SET, (m & 0xffffff0f) | (value << 4)); + kfree(r); + r = lg_wmab(WM_FAN_MODE, WM_SET, (m & 0xfffffff0) | value); + kfree(r); + + return count; +} + +static ssize_t fan_mode_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + unsigned int status; + union acpi_object *r; + + r = lg_wmab(WM_FAN_MODE, WM_GET, 0); + if (!r) + return -EIO; + + if (r->type != ACPI_TYPE_INTEGER) { + kfree(r); + return -EIO; + } + + status = r->integer.value & 0x01; + kfree(r); + + return snprintf(buffer, PAGE_SIZE, "%d\n", status); +} + +static ssize_t usb_charge_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + bool value; + union acpi_object *r; + int ret; + + ret = kstrtobool(buffer, &value); + if (ret) + return ret; + + r = lg_wmbb(WMBB_USB_CHARGE, WM_SET, value); + if (!r) + return -EIO; + + kfree(r); + return count; +} + +static ssize_t usb_charge_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + unsigned int status; + union acpi_object *r; + + r = lg_wmbb(WMBB_USB_CHARGE, WM_GET, 0); + if (!r) + return -EIO; + + if (r->type != ACPI_TYPE_BUFFER) { + kfree(r); + return -EIO; + } + + status = !!r->buffer.pointer[0x10]; + + kfree(r); + + return snprintf(buffer, PAGE_SIZE, "%d\n", status); +} + +static ssize_t reader_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + bool value; + union acpi_object *r; + int ret; + + ret = kstrtobool(buffer, &value); + if (ret) + return ret; + + r = lg_wmab(WM_READER_MODE, WM_SET, value); + if (!r) + return -EIO; + + kfree(r); + return count; +} + +static ssize_t reader_mode_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + unsigned int status; + union acpi_object *r; + + r = lg_wmab(WM_READER_MODE, WM_GET, 0); + if (!r) + return -EIO; + + if (r->type != ACPI_TYPE_INTEGER) { + kfree(r); + return -EIO; + } + + status = !!r->integer.value; + + kfree(r); + + return snprintf(buffer, PAGE_SIZE, "%d\n", status); +} + +static ssize_t fn_lock_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + bool value; + union acpi_object *r; + int ret; + + ret = kstrtobool(buffer, &value); + if (ret) + return ret; + + r = lg_wmab(WM_FN_LOCK, WM_SET, value); + if (!r) + return -EIO; + + kfree(r); + return count; +} + +static ssize_t fn_lock_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + unsigned int status; + union acpi_object *r; + + r = lg_wmab(WM_FN_LOCK, WM_GET, 0); + if (!r) + return -EIO; + + if (r->type != ACPI_TYPE_BUFFER) { + kfree(r); + return -EIO; + } + + status = !!r->buffer.pointer[0]; + kfree(r); + + return snprintf(buffer, PAGE_SIZE, "%d\n", status); +} + +static ssize_t battery_care_limit_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + unsigned long value; + int ret; + + ret = kstrtoul(buffer, 10, &value); + if (ret) + return ret; + + if (value == 100 || value == 80) { + union acpi_object *r; + + r = lg_wmab(WM_BATT_LIMIT, WM_SET, value); + if (!r) + return -EIO; + + kfree(r); + return count; + } + + return -EINVAL; +} + +static ssize_t battery_care_limit_show(struct device *dev, + struct device_attribute *attr, + char *buffer) +{ + unsigned int status; + union acpi_object *r; + + r = lg_wmab(WM_BATT_LIMIT, WM_GET, 0); + if (!r) + return -EIO; + + if (r->type != ACPI_TYPE_INTEGER) { + kfree(r); + return -EIO; + } + + status = r->integer.value; + kfree(r); + if (status != 80 && status != 100) + status = 0; + + return snprintf(buffer, PAGE_SIZE, "%d\n", status); +} + +static DEVICE_ATTR_RW(fan_mode); +static DEVICE_ATTR_RW(usb_charge); +static DEVICE_ATTR_RW(reader_mode); +static DEVICE_ATTR_RW(fn_lock); +static DEVICE_ATTR_RW(battery_care_limit); + +static struct attribute *dev_attributes[] = { + &dev_attr_fan_mode.attr, + &dev_attr_usb_charge.attr, + &dev_attr_reader_mode.attr, + &dev_attr_fn_lock.attr, + &dev_attr_battery_care_limit.attr, + NULL +}; + +static const struct attribute_group dev_attribute_group = { + .attrs = dev_attributes, +}; + +static void tpad_led_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + union acpi_object *r; + + r = lg_wmab(WM_TLED, WM_SET, brightness > LED_OFF); + kfree(r); +} + +static enum led_brightness tpad_led_get(struct led_classdev *cdev) +{ + return ggov(GOV_TLED) > 0 ? LED_ON : LED_OFF; +} + +static LED_DEVICE(tpad_led, 1); + +static void kbd_backlight_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + u32 val; + union acpi_object *r; + + val = 0x22; + if (brightness <= LED_OFF) + val = 0; + if (brightness >= LED_FULL) + val = 0x24; + r = lg_wmab(WM_KEY_LIGHT, WM_SET, val); + kfree(r); +} + +static enum led_brightness kbd_backlight_get(struct led_classdev *cdev) +{ + union acpi_object *r; + int val; + + r = lg_wmab(WM_KEY_LIGHT, WM_GET, 0); + + if (!r) + return LED_OFF; + + if (r->type != ACPI_TYPE_BUFFER || r->buffer.pointer[1] != 0x05) { + kfree(r); + return LED_OFF; + } + + switch (r->buffer.pointer[0] & 0x27) { + case 0x24: + val = LED_FULL; + break; + case 0x22: + val = LED_HALF; + break; + default: + val = LED_OFF; + } + + kfree(r); + + return val; +} + +static LED_DEVICE(kbd_backlight, 255); + +static void wmi_input_destroy(void) +{ + if (inited & INIT_INPUT_WMI_2) + wmi_remove_notify_handler(WMI_EVENT_GUID2); + + if (inited & INIT_INPUT_WMI_0) + wmi_remove_notify_handler(WMI_EVENT_GUID0); + + if (inited & INIT_SPARSE_KEYMAP) + input_unregister_device(wmi_input_dev); + + inited &= ~(INIT_INPUT_WMI_0 | INIT_INPUT_WMI_2 | INIT_SPARSE_KEYMAP); +} + +static struct platform_driver pf_driver = { + .driver = { + .name = PLATFORM_NAME, + } +}; + +static int acpi_add(struct acpi_device *device) +{ + int ret; + + if (pf_device) + return 0; + + ret = platform_driver_register(&pf_driver); + if (ret) + return ret; + + pf_device = platform_device_register_simple(PLATFORM_NAME, + PLATFORM_DEVID_NONE, + NULL, 0); + if (IS_ERR(pf_device)) { + ret = PTR_ERR(pf_device); + pf_device = NULL; + pr_err("unable to register platform device\n"); + goto out_platform_registered; + } + + ret = sysfs_create_group(&pf_device->dev.kobj, &dev_attribute_group); + if (ret) + goto out_platform_device; + + if (!led_classdev_register(&pf_device->dev, &kbd_backlight)) + inited |= INIT_KBD_LED; + + if (!led_classdev_register(&pf_device->dev, &tpad_led)) + inited |= INIT_TPAD_LED; + + wmi_input_setup(); + + return 0; + +out_platform_device: + platform_device_unregister(pf_device); +out_platform_registered: + platform_driver_unregister(&pf_driver); + return ret; +} + +static int acpi_remove(struct acpi_device *device) +{ + sysfs_remove_group(&pf_device->dev.kobj, &dev_attribute_group); + if (inited & INIT_KBD_LED) + led_classdev_unregister(&kbd_backlight); + + if (inited & INIT_TPAD_LED) + led_classdev_unregister(&tpad_led); + + wmi_input_destroy(); + platform_device_unregister(pf_device); + pf_device = NULL; + platform_driver_unregister(&pf_driver); + + return 0; +} + +static const struct acpi_device_id device_ids[] = { + {"LGEX0815", 0}, + {"", 0} +}; +MODULE_DEVICE_TABLE(acpi, device_ids); + +static struct acpi_driver acpi_driver = { + .name = "LG Gram Laptop Support", + .class = "lg-laptop", + .ids = device_ids, + .ops = { + .add = acpi_add, + .remove = acpi_remove, + .notify = acpi_notify, + }, + .owner = THIS_MODULE, +}; + +static int __init acpi_init(void) +{ + int result; + + result = acpi_bus_register_driver(&acpi_driver); + if (result < 0) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error registering driver\n")); + return -ENODEV; + } + + return 0; +} + +static void __exit acpi_exit(void) +{ + acpi_bus_unregister_driver(&acpi_driver); +} + +module_init(acpi_init); +module_exit(acpi_exit); diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c index d89936c93ba0..c2c3a1a19879 100644 --- a/drivers/platform/x86/mlx-platform.c +++ b/drivers/platform/x86/mlx-platform.c @@ -575,7 +575,7 @@ static struct mlxreg_core_item mlxplat_mlxcpld_msn201x_items[] = { static struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_msn201x_data = { - .items = mlxplat_mlxcpld_msn21xx_items, + .items = mlxplat_mlxcpld_msn201x_items, .counter = ARRAY_SIZE(mlxplat_mlxcpld_msn201x_items), .cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET, .mask = MLXPLAT_CPLD_AGGR_MASK_DEF, diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c index cb204f973491..5f2d7ea912b5 100644 --- a/drivers/platform/x86/touchscreen_dmi.c +++ b/drivers/platform/x86/touchscreen_dmi.c @@ -42,10 +42,13 @@ static const struct ts_dmi_data chuwi_hi8_data = { }; static const struct property_entry chuwi_hi8_pro_props[] = { + PROPERTY_ENTRY_U32("touchscreen-min-x", 6), + PROPERTY_ENTRY_U32("touchscreen-min-y", 3), PROPERTY_ENTRY_U32("touchscreen-size-x", 1728), PROPERTY_ENTRY_U32("touchscreen-size-y", 1148), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-chuwi-hi8-pro.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -56,6 +59,8 @@ static const struct ts_dmi_data chuwi_hi8_pro_data = { }; static const struct property_entry chuwi_vi8_props[] = { + PROPERTY_ENTRY_U32("touchscreen-min-x", 4), + PROPERTY_ENTRY_U32("touchscreen-min-y", 6), PROPERTY_ENTRY_U32("touchscreen-size-x", 1724), PROPERTY_ENTRY_U32("touchscreen-size-y", 1140), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), @@ -88,9 +93,9 @@ static const struct ts_dmi_data chuwi_vi10_data = { static const struct property_entry connect_tablet9_props[] = { PROPERTY_ENTRY_U32("touchscreen-min-x", 9), - PROPERTY_ENTRY_U32("touchscreen-min-y", 8), + PROPERTY_ENTRY_U32("touchscreen-min-y", 10), PROPERTY_ENTRY_U32("touchscreen-size-x", 1664), - PROPERTY_ENTRY_U32("touchscreen-size-y", 878), + PROPERTY_ENTRY_U32("touchscreen-size-y", 880), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-connect-tablet9.fw"), @@ -104,8 +109,10 @@ static const struct ts_dmi_data connect_tablet9_data = { }; static const struct property_entry cube_iwork8_air_props[] = { - PROPERTY_ENTRY_U32("touchscreen-size-x", 1660), - PROPERTY_ENTRY_U32("touchscreen-size-y", 900), + PROPERTY_ENTRY_U32("touchscreen-min-x", 1), + PROPERTY_ENTRY_U32("touchscreen-min-y", 3), + PROPERTY_ENTRY_U32("touchscreen-size-x", 1664), + PROPERTY_ENTRY_U32("touchscreen-size-y", 896), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3670-cube-iwork8-air.fw"), PROPERTY_ENTRY_U32("silead,max-fingers", 10), @@ -179,11 +186,14 @@ static const struct ts_dmi_data gp_electronic_t701_data = { }; static const struct property_entry itworks_tw891_props[] = { + PROPERTY_ENTRY_U32("touchscreen-min-x", 1), + PROPERTY_ENTRY_U32("touchscreen-min-y", 5), PROPERTY_ENTRY_U32("touchscreen-size-x", 1600), - PROPERTY_ENTRY_U32("touchscreen-size-y", 890), + PROPERTY_ENTRY_U32("touchscreen-size-y", 896), PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3670-itworks-tw891.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), { } }; @@ -207,8 +217,10 @@ static const struct ts_dmi_data jumper_ezpad_6_pro_data = { }; static const struct property_entry jumper_ezpad_mini3_props[] = { + PROPERTY_ENTRY_U32("touchscreen-min-x", 23), + PROPERTY_ENTRY_U32("touchscreen-min-y", 16), PROPERTY_ENTRY_U32("touchscreen-size-x", 1700), - PROPERTY_ENTRY_U32("touchscreen-size-y", 1150), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1138), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3676-jumper-ezpad-mini3.fw"), PROPERTY_ENTRY_U32("silead,max-fingers", 10), @@ -237,6 +249,24 @@ static const struct ts_dmi_data onda_obook_20_plus_data = { .properties = onda_obook_20_plus_props, }; +static const struct property_entry onda_v80_plus_v3_props[] = { + PROPERTY_ENTRY_U32("touchscreen-min-x", 22), + PROPERTY_ENTRY_U32("touchscreen-min-y", 15), + PROPERTY_ENTRY_U32("touchscreen-size-x", 1698), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1140), + PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), + PROPERTY_ENTRY_STRING("firmware-name", + "gsl3676-onda-v80-plus-v3.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), + PROPERTY_ENTRY_BOOL("silead,home-button"), + { } +}; + +static const struct ts_dmi_data onda_v80_plus_v3_data = { + .acpi_name = "MSSL1680:00", + .properties = onda_v80_plus_v3_props, +}; + static const struct property_entry onda_v820w_32g_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1665), PROPERTY_ENTRY_U32("touchscreen-size-y", 1140), @@ -322,11 +352,14 @@ static const struct ts_dmi_data pov_mobii_wintab_p800w_v20_data = { }; static const struct property_entry pov_mobii_wintab_p800w_v21_props[] = { - PROPERTY_ENTRY_U32("touchscreen-size-x", 1800), - PROPERTY_ENTRY_U32("touchscreen-size-y", 1150), + PROPERTY_ENTRY_U32("touchscreen-min-x", 1), + PROPERTY_ENTRY_U32("touchscreen-min-y", 8), + PROPERTY_ENTRY_U32("touchscreen-size-x", 1794), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1148), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-pov-mobii-wintab-p800w.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -366,6 +399,22 @@ static const struct ts_dmi_data teclast_x98plus2_data = { .properties = teclast_x98plus2_props, }; +static const struct property_entry trekstor_primebook_c11_props[] = { + PROPERTY_ENTRY_U32("touchscreen-size-x", 1970), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1530), + PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), + PROPERTY_ENTRY_STRING("firmware-name", + "gsl1680-trekstor-primebook-c11.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), + PROPERTY_ENTRY_BOOL("silead,home-button"), + { } +}; + +static const struct ts_dmi_data trekstor_primebook_c11_data = { + .acpi_name = "MSSL1680:00", + .properties = trekstor_primebook_c11_props, +}; + static const struct property_entry trekstor_primebook_c13_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 2624), PROPERTY_ENTRY_U32("touchscreen-size-y", 1920), @@ -381,6 +430,22 @@ static const struct ts_dmi_data trekstor_primebook_c13_data = { .properties = trekstor_primebook_c13_props, }; +static const struct property_entry trekstor_primetab_t13b_props[] = { + PROPERTY_ENTRY_U32("touchscreen-size-x", 2500), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1900), + PROPERTY_ENTRY_STRING("firmware-name", + "gsl1680-trekstor-primetab-t13b.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), + PROPERTY_ENTRY_BOOL("silead,home-button"), + PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), + { } +}; + +static const struct ts_dmi_data trekstor_primetab_t13b_data = { + .acpi_name = "MSSL1680:00", + .properties = trekstor_primetab_t13b_props, +}; + static const struct property_entry trekstor_surftab_twin_10_1_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1900), PROPERTY_ENTRY_U32("touchscreen-size-y", 1280), @@ -397,6 +462,8 @@ static const struct ts_dmi_data trekstor_surftab_twin_10_1_data = { }; static const struct property_entry trekstor_surftab_wintron70_props[] = { + PROPERTY_ENTRY_U32("touchscreen-min-x", 12), + PROPERTY_ENTRY_U32("touchscreen-min-y", 8), PROPERTY_ENTRY_U32("touchscreen-size-x", 884), PROPERTY_ENTRY_U32("touchscreen-size-y", 632), PROPERTY_ENTRY_STRING("firmware-name", @@ -556,6 +623,14 @@ static const struct dmi_system_id touchscreen_dmi_table[] = { }, }, { + /* ONDA V80 plus v3 (P80PSBG9V3A01501) */ + .driver_data = (void *)&onda_v80_plus_v3_data, + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ONDA"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "V80 PLUS") + }, + }, + { /* ONDA V820w DualOS */ .driver_data = (void *)&onda_v820w_32g_data, .matches = { @@ -641,6 +716,14 @@ static const struct dmi_system_id touchscreen_dmi_table[] = { }, }, { + /* Trekstor Primebook C11 */ + .driver_data = (void *)&trekstor_primebook_c11_data, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TREKSTOR"), + DMI_MATCH(DMI_PRODUCT_NAME, "Primebook C11"), + }, + }, + { /* Trekstor Primebook C13 */ .driver_data = (void *)&trekstor_primebook_c13_data, .matches = { @@ -649,6 +732,14 @@ static const struct dmi_system_id touchscreen_dmi_table[] = { }, }, { + /* Trekstor Primetab T13B */ + .driver_data = (void *)&trekstor_primetab_t13b_data, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TREKSTOR"), + DMI_MATCH(DMI_PRODUCT_NAME, "Primetab T13B"), + }, + }, + { /* TrekStor SurfTab twin 10.1 ST10432-8 */ .driver_data = (void *)&trekstor_surftab_twin_10_1_data, .matches = { diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 04791ea5d97b..bea35be68706 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -987,19 +987,19 @@ static struct bus_type wmi_bus_type = { .remove = wmi_dev_remove, }; -static struct device_type wmi_type_event = { +static const struct device_type wmi_type_event = { .name = "event", .groups = wmi_event_groups, .release = wmi_dev_release, }; -static struct device_type wmi_type_method = { +static const struct device_type wmi_type_method = { .name = "method", .groups = wmi_method_groups, .release = wmi_dev_release, }; -static struct device_type wmi_type_data = { +static const struct device_type wmi_type_data = { .name = "data", .groups = wmi_data_groups, .release = wmi_dev_release, |