Commit 8a696a29 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'platform-drivers-x86-v6.8-2' of...

Merge tag 'platform-drivers-x86-v6.8-2' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86

Pull x86 platform driver fixes from Hans de Goede:

 - WMI bus driver fixes

 - Second attempt (previously reverted) at P2SB PCI rescan deadlock fix

 - AMD PMF driver improvements

 - MAINTAINERS updates

 - Misc other small fixes and hw-id additions

* tag 'platform-drivers-x86-v6.8-2' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86:
  platform/x86: touchscreen_dmi: Add info for the TECLAST X16 Plus tablet
  platform/x86/intel/ifs: Call release_firmware() when handling errors.
  platform/x86/amd/pmf: Fix memory leak in amd_pmf_get_pb_data()
  platform/x86/amd/pmf: Get ambient light information from AMD SFH driver
  platform/x86/amd/pmf: Get Human presence information from AMD SFH driver
  platform/mellanox: mlxbf-pmc: Fix offset calculation for crspace events
  platform/mellanox: mlxbf-tmfifo: Drop Tx network packet when Tx TmFIFO is full
  MAINTAINERS: remove defunct acpi4asus project info from asus notebooks section
  MAINTAINERS: add Luke Jones as maintainer for asus notebooks
  MAINTAINERS: Remove Perry Yuan as DELL WMI HARDWARE PRIVACY SUPPORT maintainer
  platform/x86: silicom-platform: Add missing "Description:" for power_cycle sysfs attr
  platform/x86: intel-wmi-sbl-fw-update: Fix function name in error message
  platform/x86: p2sb: Use pci_resource_n() in p2sb_read_bar0()
  platform/x86: p2sb: Allow p2sb_bar() calls during PCI device probe
  platform/x86: intel-uncore-freq: Fix types in sysfs callbacks
  platform/x86: wmi: Fix wmi_dev_probe()
  platform/x86: wmi: Fix notify callback locking
  platform/x86: wmi: Decouple legacy WMI notify handlers from wmi_block_list
  platform/x86: wmi: Return immediately if an suitable WMI event is found
  platform/x86: wmi: Fix error handling in legacy WMI notify handler functions
parents 95534043 1abdf288
......@@ -10,6 +10,7 @@ What: /sys/devices/platform/silicom-platform/power_cycle
Date: November 2023
KernelVersion: 6.7
Contact: Henry Shi <henrys@silicom-usa.com>
Description:
This file allow user to power cycle the platform.
Default value is 0; when set to 1, it powers down
the platform, waits 5 seconds, then powers on the
......
......@@ -3168,10 +3168,10 @@ F: drivers/hwmon/asus-ec-sensors.c
ASUS NOTEBOOKS AND EEEPC ACPI/WMI EXTRAS DRIVERS
M: Corentin Chary <corentin.chary@gmail.com>
L: acpi4asus-user@lists.sourceforge.net
M: Luke D. Jones <luke@ljones.dev>
L: platform-driver-x86@vger.kernel.org
S: Maintained
W: http://acpi4asus.sf.net
W: https://asus-linux.org/
F: drivers/platform/x86/asus*.c
F: drivers/platform/x86/eeepc*.c
......@@ -5958,7 +5958,6 @@ S: Maintained
F: drivers/platform/x86/dell/dell-wmi-descriptor.c
DELL WMI HARDWARE PRIVACY SUPPORT
M: Perry Yuan <Perry.Yuan@dell.com>
L: Dell.Client.Kernel@dell.com
L: platform-driver-x86@vger.kernel.org
S: Maintained
......
......@@ -1170,7 +1170,7 @@ static int mlxbf_pmc_program_crspace_counter(int blk_num, uint32_t cnt_num,
int ret;
addr = pmc->block[blk_num].mmio_base +
(rounddown(cnt_num, 2) * MLXBF_PMC_CRSPACE_PERFSEL_SZ);
((cnt_num / 2) * MLXBF_PMC_CRSPACE_PERFSEL_SZ);
ret = mlxbf_pmc_readl(addr, &word);
if (ret)
return ret;
......@@ -1413,7 +1413,7 @@ static int mlxbf_pmc_read_crspace_event(int blk_num, uint32_t cnt_num,
int ret;
addr = pmc->block[blk_num].mmio_base +
(rounddown(cnt_num, 2) * MLXBF_PMC_CRSPACE_PERFSEL_SZ);
((cnt_num / 2) * MLXBF_PMC_CRSPACE_PERFSEL_SZ);
ret = mlxbf_pmc_readl(addr, &word);
if (ret)
return ret;
......
......@@ -47,6 +47,9 @@
/* Message with data needs at least two words (for header & data). */
#define MLXBF_TMFIFO_DATA_MIN_WORDS 2
/* Tx timeout in milliseconds. */
#define TMFIFO_TX_TIMEOUT 2000
/* ACPI UID for BlueField-3. */
#define TMFIFO_BF3_UID 1
......@@ -62,12 +65,14 @@ struct mlxbf_tmfifo;
* @drop_desc: dummy desc for packet dropping
* @cur_len: processed length of the current descriptor
* @rem_len: remaining length of the pending packet
* @rem_padding: remaining bytes to send as paddings
* @pkt_len: total length of the pending packet
* @next_avail: next avail descriptor id
* @num: vring size (number of descriptors)
* @align: vring alignment size
* @index: vring index
* @vdev_id: vring virtio id (VIRTIO_ID_xxx)
* @tx_timeout: expire time of last tx packet
* @fifo: pointer to the tmfifo structure
*/
struct mlxbf_tmfifo_vring {
......@@ -79,12 +84,14 @@ struct mlxbf_tmfifo_vring {
struct vring_desc drop_desc;
int cur_len;
int rem_len;
int rem_padding;
u32 pkt_len;
u16 next_avail;
int num;
int align;
int index;
int vdev_id;
unsigned long tx_timeout;
struct mlxbf_tmfifo *fifo;
};
......@@ -819,6 +826,50 @@ static bool mlxbf_tmfifo_rxtx_one_desc(struct mlxbf_tmfifo_vring *vring,
return true;
}
static void mlxbf_tmfifo_check_tx_timeout(struct mlxbf_tmfifo_vring *vring)
{
unsigned long flags;
/* Only handle Tx timeout for network vdev. */
if (vring->vdev_id != VIRTIO_ID_NET)
return;
/* Initialize the timeout or return if not expired. */
if (!vring->tx_timeout) {
/* Initialize the timeout. */
vring->tx_timeout = jiffies +
msecs_to_jiffies(TMFIFO_TX_TIMEOUT);
return;
} else if (time_before(jiffies, vring->tx_timeout)) {
/* Return if not timeout yet. */
return;
}
/*
* Drop the packet after timeout. The outstanding packet is
* released and the remaining bytes will be sent with padding byte 0x00
* as a recovery. On the peer(host) side, the padding bytes 0x00 will be
* either dropped directly, or appended into existing outstanding packet
* thus dropped as corrupted network packet.
*/
vring->rem_padding = round_up(vring->rem_len, sizeof(u64));
mlxbf_tmfifo_release_pkt(vring);
vring->cur_len = 0;
vring->rem_len = 0;
vring->fifo->vring[0] = NULL;
/*
* Make sure the load/store are in order before
* returning back to virtio.
*/
virtio_mb(false);
/* Notify upper layer. */
spin_lock_irqsave(&vring->fifo->spin_lock[0], flags);
vring_interrupt(0, vring->vq);
spin_unlock_irqrestore(&vring->fifo->spin_lock[0], flags);
}
/* Rx & Tx processing of a queue. */
static void mlxbf_tmfifo_rxtx(struct mlxbf_tmfifo_vring *vring, bool is_rx)
{
......@@ -841,6 +892,7 @@ static void mlxbf_tmfifo_rxtx(struct mlxbf_tmfifo_vring *vring, bool is_rx)
return;
do {
retry:
/* Get available FIFO space. */
if (avail == 0) {
if (is_rx)
......@@ -851,6 +903,17 @@ static void mlxbf_tmfifo_rxtx(struct mlxbf_tmfifo_vring *vring, bool is_rx)
break;
}
/* Insert paddings for discarded Tx packet. */
if (!is_rx) {
vring->tx_timeout = 0;
while (vring->rem_padding >= sizeof(u64)) {
writeq(0, vring->fifo->tx.data);
vring->rem_padding -= sizeof(u64);
if (--avail == 0)
goto retry;
}
}
/* Console output always comes from the Tx buffer. */
if (!is_rx && devid == VIRTIO_ID_CONSOLE) {
mlxbf_tmfifo_console_tx(fifo, avail);
......@@ -860,6 +923,10 @@ static void mlxbf_tmfifo_rxtx(struct mlxbf_tmfifo_vring *vring, bool is_rx)
/* Handle one descriptor. */
more = mlxbf_tmfifo_rxtx_one_desc(vring, is_rx, &avail);
} while (more);
/* Check Tx timeout. */
if (avail <= 0 && !is_rx)
mlxbf_tmfifo_check_tx_timeout(vring);
}
/* Handle Rx or Tx queues. */
......
......@@ -10,6 +10,7 @@ config AMD_PMF
depends on AMD_NB
select ACPI_PLATFORM_PROFILE
depends on TEE && AMDTEE
depends on AMD_SFH_HID
help
This driver provides support for the AMD Platform Management Framework.
The goal is to enhance end user experience by making AMD PCs smarter,
......
......@@ -10,6 +10,7 @@
*/
#include <acpi/button.h>
#include <linux/amd-pmf-io.h>
#include <linux/power_supply.h>
#include <linux/units.h>
#include "pmf.h"
......@@ -44,6 +45,8 @@ void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *
dev_dbg(dev->dev, "Max C0 Residency: %u\n", in->ev_info.max_c0residency);
dev_dbg(dev->dev, "GFX Busy: %u\n", in->ev_info.gfx_busy);
dev_dbg(dev->dev, "LID State: %s\n", in->ev_info.lid_state ? "close" : "open");
dev_dbg(dev->dev, "User Presence: %s\n", in->ev_info.user_present ? "Present" : "Away");
dev_dbg(dev->dev, "Ambient Light: %d\n", in->ev_info.ambient_light);
dev_dbg(dev->dev, "==== TA inputs END ====\n");
}
#else
......@@ -147,6 +150,38 @@ static int amd_pmf_get_slider_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_
return 0;
}
static int amd_pmf_get_sensor_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
{
struct amd_sfh_info sfh_info;
int ret;
/* Get ALS data */
ret = amd_get_sfh_info(&sfh_info, MT_ALS);
if (!ret)
in->ev_info.ambient_light = sfh_info.ambient_light;
else
return ret;
/* get HPD data */
ret = amd_get_sfh_info(&sfh_info, MT_HPD);
if (ret)
return ret;
switch (sfh_info.user_present) {
case SFH_NOT_DETECTED:
in->ev_info.user_present = 0xff; /* assume no sensors connected */
break;
case SFH_USER_PRESENT:
in->ev_info.user_present = 1;
break;
case SFH_USER_AWAY:
in->ev_info.user_present = 0;
break;
}
return 0;
}
void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in)
{
/* TA side lid open is 1 and close is 0, hence the ! here */
......@@ -155,4 +190,5 @@ void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_tab
amd_pmf_get_smu_info(dev, in);
amd_pmf_get_battery_info(dev, in);
amd_pmf_get_slider_info(dev, in);
amd_pmf_get_sensor_info(dev, in);
}
......@@ -298,8 +298,10 @@ static ssize_t amd_pmf_get_pb_data(struct file *filp, const char __user *buf,
if (!new_policy_buf)
return -ENOMEM;
if (copy_from_user(new_policy_buf, buf, length))
if (copy_from_user(new_policy_buf, buf, length)) {
kfree(new_policy_buf);
return -EFAULT;
}
kfree(dev->policy_buf);
dev->policy_buf = new_policy_buf;
......
......@@ -399,7 +399,8 @@ int ifs_load_firmware(struct device *dev)
if (fw->size != expected_size) {
dev_err(dev, "File size mismatch (expected %u, actual %zu). Corrupted IFS image.\n",
expected_size, fw->size);
return -EINVAL;
ret = -EINVAL;
goto release;
}
ret = image_sanity_check(dev, (struct microcode_header_intel *)fw->data);
......
......@@ -23,23 +23,23 @@ static int (*uncore_read)(struct uncore_data *data, unsigned int *min, unsigned
static int (*uncore_write)(struct uncore_data *data, unsigned int input, unsigned int min_max);
static int (*uncore_read_freq)(struct uncore_data *data, unsigned int *freq);
static ssize_t show_domain_id(struct device *dev, struct device_attribute *attr, char *buf)
static ssize_t show_domain_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
struct uncore_data *data = container_of(attr, struct uncore_data, domain_id_dev_attr);
struct uncore_data *data = container_of(attr, struct uncore_data, domain_id_kobj_attr);
return sprintf(buf, "%u\n", data->domain_id);
}
static ssize_t show_fabric_cluster_id(struct device *dev, struct device_attribute *attr, char *buf)
static ssize_t show_fabric_cluster_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
struct uncore_data *data = container_of(attr, struct uncore_data, fabric_cluster_id_dev_attr);
struct uncore_data *data = container_of(attr, struct uncore_data, fabric_cluster_id_kobj_attr);
return sprintf(buf, "%u\n", data->cluster_id);
}
static ssize_t show_package_id(struct device *dev, struct device_attribute *attr, char *buf)
static ssize_t show_package_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
struct uncore_data *data = container_of(attr, struct uncore_data, package_id_dev_attr);
struct uncore_data *data = container_of(attr, struct uncore_data, package_id_kobj_attr);
return sprintf(buf, "%u\n", data->package_id);
}
......@@ -97,30 +97,30 @@ static ssize_t show_perf_status_freq_khz(struct uncore_data *data, char *buf)
}
#define store_uncore_min_max(name, min_max) \
static ssize_t store_##name(struct device *dev, \
struct device_attribute *attr, \
static ssize_t store_##name(struct kobject *kobj, \
struct kobj_attribute *attr, \
const char *buf, size_t count) \
{ \
struct uncore_data *data = container_of(attr, struct uncore_data, name##_dev_attr);\
struct uncore_data *data = container_of(attr, struct uncore_data, name##_kobj_attr);\
\
return store_min_max_freq_khz(data, buf, count, \
min_max); \
}
#define show_uncore_min_max(name, min_max) \
static ssize_t show_##name(struct device *dev, \
struct device_attribute *attr, char *buf)\
static ssize_t show_##name(struct kobject *kobj, \
struct kobj_attribute *attr, char *buf)\
{ \
struct uncore_data *data = container_of(attr, struct uncore_data, name##_dev_attr);\
struct uncore_data *data = container_of(attr, struct uncore_data, name##_kobj_attr);\
\
return show_min_max_freq_khz(data, buf, min_max); \
}
#define show_uncore_perf_status(name) \
static ssize_t show_##name(struct device *dev, \
struct device_attribute *attr, char *buf)\
static ssize_t show_##name(struct kobject *kobj, \
struct kobj_attribute *attr, char *buf)\
{ \
struct uncore_data *data = container_of(attr, struct uncore_data, name##_dev_attr);\
struct uncore_data *data = container_of(attr, struct uncore_data, name##_kobj_attr);\
\
return show_perf_status_freq_khz(data, buf); \
}
......@@ -134,11 +134,11 @@ show_uncore_min_max(max_freq_khz, 1);
show_uncore_perf_status(current_freq_khz);
#define show_uncore_data(member_name) \
static ssize_t show_##member_name(struct device *dev, \
struct device_attribute *attr, char *buf)\
static ssize_t show_##member_name(struct kobject *kobj, \
struct kobj_attribute *attr, char *buf)\
{ \
struct uncore_data *data = container_of(attr, struct uncore_data,\
member_name##_dev_attr);\
member_name##_kobj_attr);\
\
return sysfs_emit(buf, "%u\n", \
data->member_name); \
......@@ -149,29 +149,29 @@ show_uncore_data(initial_max_freq_khz);
#define init_attribute_rw(_name) \
do { \
sysfs_attr_init(&data->_name##_dev_attr.attr); \
data->_name##_dev_attr.show = show_##_name; \
data->_name##_dev_attr.store = store_##_name; \
data->_name##_dev_attr.attr.name = #_name; \
data->_name##_dev_attr.attr.mode = 0644; \
sysfs_attr_init(&data->_name##_kobj_attr.attr); \
data->_name##_kobj_attr.show = show_##_name; \
data->_name##_kobj_attr.store = store_##_name; \
data->_name##_kobj_attr.attr.name = #_name; \
data->_name##_kobj_attr.attr.mode = 0644; \
} while (0)
#define init_attribute_ro(_name) \
do { \
sysfs_attr_init(&data->_name##_dev_attr.attr); \
data->_name##_dev_attr.show = show_##_name; \
data->_name##_dev_attr.store = NULL; \
data->_name##_dev_attr.attr.name = #_name; \
data->_name##_dev_attr.attr.mode = 0444; \
sysfs_attr_init(&data->_name##_kobj_attr.attr); \
data->_name##_kobj_attr.show = show_##_name; \
data->_name##_kobj_attr.store = NULL; \
data->_name##_kobj_attr.attr.name = #_name; \
data->_name##_kobj_attr.attr.mode = 0444; \
} while (0)
#define init_attribute_root_ro(_name) \
do { \
sysfs_attr_init(&data->_name##_dev_attr.attr); \
data->_name##_dev_attr.show = show_##_name; \
data->_name##_dev_attr.store = NULL; \
data->_name##_dev_attr.attr.name = #_name; \
data->_name##_dev_attr.attr.mode = 0400; \
sysfs_attr_init(&data->_name##_kobj_attr.attr); \
data->_name##_kobj_attr.show = show_##_name; \
data->_name##_kobj_attr.store = NULL; \
data->_name##_kobj_attr.attr.name = #_name; \
data->_name##_kobj_attr.attr.mode = 0400; \
} while (0)
static int create_attr_group(struct uncore_data *data, char *name)
......@@ -186,21 +186,21 @@ static int create_attr_group(struct uncore_data *data, char *name)
if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) {
init_attribute_root_ro(domain_id);
data->uncore_attrs[index++] = &data->domain_id_dev_attr.attr;
data->uncore_attrs[index++] = &data->domain_id_kobj_attr.attr;
init_attribute_root_ro(fabric_cluster_id);
data->uncore_attrs[index++] = &data->fabric_cluster_id_dev_attr.attr;
data->uncore_attrs[index++] = &data->fabric_cluster_id_kobj_attr.attr;
init_attribute_root_ro(package_id);
data->uncore_attrs[index++] = &data->package_id_dev_attr.attr;
data->uncore_attrs[index++] = &data->package_id_kobj_attr.attr;
}
data->uncore_attrs[index++] = &data->max_freq_khz_dev_attr.attr;
data->uncore_attrs[index++] = &data->min_freq_khz_dev_attr.attr;
data->uncore_attrs[index++] = &data->initial_min_freq_khz_dev_attr.attr;
data->uncore_attrs[index++] = &data->initial_max_freq_khz_dev_attr.attr;
data->uncore_attrs[index++] = &data->max_freq_khz_kobj_attr.attr;
data->uncore_attrs[index++] = &data->min_freq_khz_kobj_attr.attr;
data->uncore_attrs[index++] = &data->initial_min_freq_khz_kobj_attr.attr;
data->uncore_attrs[index++] = &data->initial_max_freq_khz_kobj_attr.attr;
ret = uncore_read_freq(data, &freq);
if (!ret)
data->uncore_attrs[index++] = &data->current_freq_khz_dev_attr.attr;
data->uncore_attrs[index++] = &data->current_freq_khz_kobj_attr.attr;
data->uncore_attrs[index] = NULL;
......
......@@ -26,14 +26,14 @@
* @instance_id: Unique instance id to append to directory name
* @name: Sysfs entry name for this instance
* @uncore_attr_group: Attribute group storage
* @max_freq_khz_dev_attr: Storage for device attribute max_freq_khz
* @mix_freq_khz_dev_attr: Storage for device attribute min_freq_khz
* @initial_max_freq_khz_dev_attr: Storage for device attribute initial_max_freq_khz
* @initial_min_freq_khz_dev_attr: Storage for device attribute initial_min_freq_khz
* @current_freq_khz_dev_attr: Storage for device attribute current_freq_khz
* @domain_id_dev_attr: Storage for device attribute domain_id
* @fabric_cluster_id_dev_attr: Storage for device attribute fabric_cluster_id
* @package_id_dev_attr: Storage for device attribute package_id
* @max_freq_khz_kobj_attr: Storage for kobject attribute max_freq_khz
* @mix_freq_khz_kobj_attr: Storage for kobject attribute min_freq_khz
* @initial_max_freq_khz_kobj_attr: Storage for kobject attribute initial_max_freq_khz
* @initial_min_freq_khz_kobj_attr: Storage for kobject attribute initial_min_freq_khz
* @current_freq_khz_kobj_attr: Storage for kobject attribute current_freq_khz
* @domain_id_kobj_attr: Storage for kobject attribute domain_id
* @fabric_cluster_id_kobj_attr: Storage for kobject attribute fabric_cluster_id
* @package_id_kobj_attr: Storage for kobject attribute package_id
* @uncore_attrs: Attribute storage for group creation
*
* This structure is used to encapsulate all data related to uncore sysfs
......@@ -53,14 +53,14 @@ struct uncore_data {
char name[32];
struct attribute_group uncore_attr_group;
struct device_attribute max_freq_khz_dev_attr;
struct device_attribute min_freq_khz_dev_attr;
struct device_attribute initial_max_freq_khz_dev_attr;
struct device_attribute initial_min_freq_khz_dev_attr;
struct device_attribute current_freq_khz_dev_attr;
struct device_attribute domain_id_dev_attr;
struct device_attribute fabric_cluster_id_dev_attr;
struct device_attribute package_id_dev_attr;
struct kobj_attribute max_freq_khz_kobj_attr;
struct kobj_attribute min_freq_khz_kobj_attr;
struct kobj_attribute initial_max_freq_khz_kobj_attr;
struct kobj_attribute initial_min_freq_khz_kobj_attr;
struct kobj_attribute current_freq_khz_kobj_attr;
struct kobj_attribute domain_id_kobj_attr;
struct kobj_attribute fabric_cluster_id_kobj_attr;
struct kobj_attribute package_id_kobj_attr;
struct attribute *uncore_attrs[9];
};
......
......@@ -32,7 +32,7 @@ static int get_fwu_request(struct device *dev, u32 *out)
return -ENODEV;
if (obj->type != ACPI_TYPE_INTEGER) {
dev_warn(dev, "wmi_query_block returned invalid value\n");
dev_warn(dev, "wmidev_block_query returned invalid value\n");
kfree(obj);
return -EINVAL;
}
......@@ -55,7 +55,7 @@ static int set_fwu_request(struct device *dev, u32 in)
status = wmidev_block_set(to_wmi_device(dev), 0, &input);
if (ACPI_FAILURE(status)) {
dev_err(dev, "wmi_set_block failed\n");
dev_err(dev, "wmidev_block_set failed\n");
return -ENODEV;
}
......
......@@ -26,6 +26,21 @@ static const struct x86_cpu_id p2sb_cpu_ids[] = {
{}
};
/*
* Cache BAR0 of P2SB device functions 0 to 7.
* TODO: The constant 8 is the number of functions that PCI specification
* defines. Same definitions exist tree-wide. Unify this definition and
* the other definitions then move to include/uapi/linux/pci.h.
*/
#define NR_P2SB_RES_CACHE 8
struct p2sb_res_cache {
u32 bus_dev_id;
struct resource res;
};
static struct p2sb_res_cache p2sb_resources[NR_P2SB_RES_CACHE];
static int p2sb_get_devfn(unsigned int *devfn)
{
unsigned int fn = P2SB_DEVFN_DEFAULT;
......@@ -39,10 +54,18 @@ static int p2sb_get_devfn(unsigned int *devfn)
return 0;
}
static bool p2sb_valid_resource(struct resource *res)
{
if (res->flags)
return true;
return false;
}
/* Copy resource from the first BAR of the device in question */
static int p2sb_read_bar0(struct pci_dev *pdev, struct resource *mem)
static void p2sb_read_bar0(struct pci_dev *pdev, struct resource *mem)
{
struct resource *bar0 = &pdev->resource[0];
struct resource *bar0 = pci_resource_n(pdev, 0);
/* Make sure we have no dangling pointers in the output */
memset(mem, 0, sizeof(*mem));
......@@ -56,49 +79,66 @@ static int p2sb_read_bar0(struct pci_dev *pdev, struct resource *mem)
mem->end = bar0->end;
mem->flags = bar0->flags;
mem->desc = bar0->desc;
return 0;
}
static int p2sb_scan_and_read(struct pci_bus *bus, unsigned int devfn, struct resource *mem)
static void p2sb_scan_and_cache_devfn(struct pci_bus *bus, unsigned int devfn)
{
struct p2sb_res_cache *cache = &p2sb_resources[PCI_FUNC(devfn)];
struct pci_dev *pdev;
int ret;
pdev = pci_scan_single_device(bus, devfn);
if (!pdev)
return -ENODEV;
return;
ret = p2sb_read_bar0(pdev, mem);
p2sb_read_bar0(pdev, &cache->res);
cache->bus_dev_id = bus->dev.id;
pci_stop_and_remove_bus_device(pdev);
return ret;
}
/**
* p2sb_bar - Get Primary to Sideband (P2SB) bridge device BAR
* @bus: PCI bus to communicate with
* @devfn: PCI slot and function to communicate with
* @mem: memory resource to be filled in
*
* The BIOS prevents the P2SB device from being enumerated by the PCI
* subsystem, so we need to unhide and hide it back to lookup the BAR.
*
* if @bus is NULL, the bus 0 in domain 0 will be used.
* If @devfn is 0, it will be replaced by devfn of the P2SB device.
*
* Caller must provide a valid pointer to @mem.
*
* Locking is handled by pci_rescan_remove_lock mutex.
*
* Return:
* 0 on success or appropriate errno value on error.
*/
int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem)
static int p2sb_scan_and_cache(struct pci_bus *bus, unsigned int devfn)
{
unsigned int slot, fn;
if (PCI_FUNC(devfn) == 0) {
/*
* When function number of the P2SB device is zero, scan it and
* other function numbers, and if devices are available, cache
* their BAR0s.
*/
slot = PCI_SLOT(devfn);
for (fn = 0; fn < NR_P2SB_RES_CACHE; fn++)
p2sb_scan_and_cache_devfn(bus, PCI_DEVFN(slot, fn));
} else {
/* Scan the P2SB device and cache its BAR0 */
p2sb_scan_and_cache_devfn(bus, devfn);
}
if (!p2sb_valid_resource(&p2sb_resources[PCI_FUNC(devfn)].res))
return -ENOENT;
return 0;
}
static struct pci_bus *p2sb_get_bus(struct pci_bus *bus)
{
static struct pci_bus *p2sb_bus;
bus = bus ?: p2sb_bus;
if (bus)
return bus;
/* Assume P2SB is on the bus 0 in domain 0 */
p2sb_bus = pci_find_bus(0, 0);
return p2sb_bus;
}
static int p2sb_cache_resources(void)
{
struct pci_dev *pdev_p2sb;
unsigned int devfn_p2sb;
u32 value = P2SBC_HIDE;
struct pci_bus *bus;
u16 class;
int ret;
/* Get devfn for P2SB device itself */
......@@ -106,8 +146,17 @@ int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem)
if (ret)
return ret;
/* if @bus is NULL, use bus 0 in domain 0 */
bus = bus ?: pci_find_bus(0, 0);
bus = p2sb_get_bus(NULL);
if (!bus)
return -ENODEV;
/*
* When a device with same devfn exists and its device class is not
* PCI_CLASS_MEMORY_OTHER for P2SB, do not touch it.
*/
pci_bus_read_config_word(bus, devfn_p2sb, PCI_CLASS_DEVICE, &class);
if (!PCI_POSSIBLE_ERROR(class) && class != PCI_CLASS_MEMORY_OTHER)
return -ENODEV;
/*
* Prevent concurrent PCI bus scan from seeing the P2SB device and
......@@ -115,17 +164,16 @@ int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem)
*/
pci_lock_rescan_remove();
/* Unhide the P2SB device, if needed */
/*
* The BIOS prevents the P2SB device from being enumerated by the PCI
* subsystem, so we need to unhide and hide it back to lookup the BAR.
* Unhide the P2SB device here, if needed.
*/
pci_bus_read_config_dword(bus, devfn_p2sb, P2SBC, &value);
if (value & P2SBC_HIDE)
pci_bus_write_config_dword(bus, devfn_p2sb, P2SBC, 0);
pdev_p2sb = pci_scan_single_device(bus, devfn_p2sb);
if (devfn)
ret = p2sb_scan_and_read(bus, devfn, mem);
else
ret = p2sb_read_bar0(pdev_p2sb, mem);
pci_stop_and_remove_bus_device(pdev_p2sb);
ret = p2sb_scan_and_cache(bus, devfn_p2sb);
/* Hide the P2SB device, if it was hidden */
if (value & P2SBC_HIDE)
......@@ -133,12 +181,62 @@ int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem)
pci_unlock_rescan_remove();
if (ret)
return ret;
return ret;
}
/**
* p2sb_bar - Get Primary to Sideband (P2SB) bridge device BAR
* @bus: PCI bus to communicate with
* @devfn: PCI slot and function to communicate with
* @mem: memory resource to be filled in
*
* If @bus is NULL, the bus 0 in domain 0 will be used.
* If @devfn is 0, it will be replaced by devfn of the P2SB device.
*
* Caller must provide a valid pointer to @mem.
*
* Return:
* 0 on success or appropriate errno value on error.
*/
int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem)
{
struct p2sb_res_cache *cache;
int ret;
bus = p2sb_get_bus(bus);
if (!bus)
return -ENODEV;
if (!devfn) {
ret = p2sb_get_devfn(&devfn);
if (ret)
return ret;
}
if (mem->flags == 0)
cache = &p2sb_resources[PCI_FUNC(devfn)];
if (cache->bus_dev_id != bus->dev.id)
return -ENODEV;
if (!p2sb_valid_resource(&cache->res))
return -ENOENT;
memcpy(mem, &cache->res, sizeof(*mem));
return 0;
}
EXPORT_SYMBOL_GPL(p2sb_bar);
static int __init p2sb_fs_init(void)
{
p2sb_cache_resources();
return 0;
}
/*
* pci_rescan_remove_lock to avoid access to unhidden P2SB devices can
* not be locked in sysfs pci bus rescan path because of deadlock. To
* avoid the deadlock, access to P2SB devices with the lock at an early
* step in kernel initialization and cache required resources. This
* should happen after subsys_initcall which initializes PCI subsystem
* and before device_initcall which requires P2SB resources.
*/
fs_initcall(p2sb_fs_init);
......@@ -944,6 +944,32 @@ static const struct ts_dmi_data teclast_tbook11_data = {
.properties = teclast_tbook11_props,
};
static const struct property_entry teclast_x16_plus_props[] = {
PROPERTY_ENTRY_U32("touchscreen-min-x", 8),
PROPERTY_ENTRY_U32("touchscreen-min-y", 14),
PROPERTY_ENTRY_U32("touchscreen-size-x", 1916),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1264),
PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-teclast-x16-plus.fw"),
PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
static const struct ts_dmi_data teclast_x16_plus_data = {
.embedded_fw = {
.name = "silead/gsl3692-teclast-x16-plus.fw",
.prefix = { 0xf0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 },
.length = 43560,
.sha256 = { 0x9d, 0xb0, 0x3d, 0xf1, 0x00, 0x3c, 0xb5, 0x25,
0x62, 0x8a, 0xa0, 0x93, 0x4b, 0xe0, 0x4e, 0x75,
0xd1, 0x27, 0xb1, 0x65, 0x3c, 0xba, 0xa5, 0x0f,
0xcd, 0xb4, 0xbe, 0x00, 0xbb, 0xf6, 0x43, 0x29 },
},
.acpi_name = "MSSL1680:00",
.properties = teclast_x16_plus_props,
};
static const struct property_entry teclast_x3_plus_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1980),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1500),
......@@ -1612,6 +1638,15 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_SKU, "E5A6_A1"),
},
},
{
/* Teclast X16 Plus */
.driver_data = (void *)&teclast_x16_plus_data,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "TECLAST"),
DMI_MATCH(DMI_PRODUCT_NAME, "Default string"),
DMI_MATCH(DMI_PRODUCT_SKU, "D3A5_A1"),
},
},
{
/* Teclast X3 Plus */
.driver_data = (void *)&teclast_x3_plus_data,
......
......@@ -25,6 +25,7 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/rwsem.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/types.h>
......@@ -56,7 +57,6 @@ static_assert(__alignof__(struct guid_block) == 1);
enum { /* wmi_block flags */
WMI_READ_TAKES_NO_ARGS,
WMI_PROBED,
};
struct wmi_block {
......@@ -64,8 +64,10 @@ struct wmi_block {
struct list_head list;
struct guid_block gblock;
struct acpi_device *acpi_device;
struct rw_semaphore notify_lock; /* Protects notify callback add/remove */
wmi_notify_handler handler;
void *handler_data;
bool driver_ready;
unsigned long flags;
};
......@@ -219,6 +221,17 @@ static int wmidev_match_guid(struct device *dev, const void *data)
return 0;
}
static int wmidev_match_notify_id(struct device *dev, const void *data)
{
struct wmi_block *wblock = dev_to_wblock(dev);
const u32 *notify_id = data;
if (wblock->gblock.flags & ACPI_WMI_EVENT && wblock->gblock.notify_id == *notify_id)
return 1;
return 0;
}
static struct bus_type wmi_bus_type;
static struct wmi_device *wmi_find_device_by_guid(const char *guid_string)
......@@ -238,6 +251,17 @@ static struct wmi_device *wmi_find_device_by_guid(const char *guid_string)
return dev_to_wdev(dev);
}
static struct wmi_device *wmi_find_event_by_notify_id(const u32 notify_id)
{
struct device *dev;
dev = bus_find_device(&wmi_bus_type, NULL, &notify_id, wmidev_match_notify_id);
if (!dev)
return ERR_PTR(-ENODEV);
return to_wmi_device(dev);
}
static void wmi_device_put(struct wmi_device *wdev)
{
put_device(&wdev->dev);
......@@ -572,32 +596,31 @@ acpi_status wmi_install_notify_handler(const char *guid,
wmi_notify_handler handler,
void *data)
{
struct wmi_block *block;
acpi_status status = AE_NOT_EXIST;
guid_t guid_input;
if (!guid || !handler)
return AE_BAD_PARAMETER;
struct wmi_block *wblock;
struct wmi_device *wdev;
acpi_status status;
if (guid_parse(guid, &guid_input))
return AE_BAD_PARAMETER;
wdev = wmi_find_device_by_guid(guid);
if (IS_ERR(wdev))
return AE_ERROR;
list_for_each_entry(block, &wmi_block_list, list) {
acpi_status wmi_status;
wblock = container_of(wdev, struct wmi_block, dev);
if (guid_equal(&block->gblock.guid, &guid_input)) {
if (block->handler)
return AE_ALREADY_ACQUIRED;
down_write(&wblock->notify_lock);
if (wblock->handler) {
status = AE_ALREADY_ACQUIRED;
} else {
wblock->handler = handler;
wblock->handler_data = data;
block->handler = handler;
block->handler_data = data;
if (ACPI_FAILURE(wmi_method_enable(wblock, true)))
dev_warn(&wblock->dev.dev, "Failed to enable device\n");
wmi_status = wmi_method_enable(block, true);
if ((wmi_status != AE_OK) ||
((wmi_status == AE_OK) && (status == AE_NOT_EXIST)))
status = wmi_status;
}
status = AE_OK;
}
up_write(&wblock->notify_lock);
wmi_device_put(wdev);
return status;
}
......@@ -613,30 +636,31 @@ EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
*/
acpi_status wmi_remove_notify_handler(const char *guid)
{
struct wmi_block *block;
acpi_status status = AE_NOT_EXIST;
guid_t guid_input;
struct wmi_block *wblock;
struct wmi_device *wdev;
acpi_status status;
if (!guid)
return AE_BAD_PARAMETER;
wdev = wmi_find_device_by_guid(guid);
if (IS_ERR(wdev))
return AE_ERROR;
if (guid_parse(guid, &guid_input))
return AE_BAD_PARAMETER;
wblock = container_of(wdev, struct wmi_block, dev);
list_for_each_entry(block, &wmi_block_list, list) {
acpi_status wmi_status;
down_write(&wblock->notify_lock);
if (!wblock->handler) {
status = AE_NULL_ENTRY;
} else {
if (ACPI_FAILURE(wmi_method_enable(wblock, false)))
dev_warn(&wblock->dev.dev, "Failed to disable device\n");
if (guid_equal(&block->gblock.guid, &guid_input)) {
if (!block->handler)
return AE_NULL_ENTRY;
wblock->handler = NULL;
wblock->handler_data = NULL;
wmi_status = wmi_method_enable(block, false);
block->handler = NULL;
block->handler_data = NULL;
if (wmi_status != AE_OK || (wmi_status == AE_OK && status == AE_NOT_EXIST))
status = wmi_status;
}
status = AE_OK;
}
up_write(&wblock->notify_lock);
wmi_device_put(wdev);
return status;
}
......@@ -655,15 +679,19 @@ EXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
{
struct wmi_block *wblock;
struct wmi_device *wdev;
acpi_status status;
list_for_each_entry(wblock, &wmi_block_list, list) {
struct guid_block *gblock = &wblock->gblock;
wdev = wmi_find_event_by_notify_id(event);
if (IS_ERR(wdev))
return AE_NOT_FOUND;
if ((gblock->flags & ACPI_WMI_EVENT) && gblock->notify_id == event)
return get_event_data(wblock, out);
}
wblock = container_of(wdev, struct wmi_block, dev);
status = get_event_data(wblock, out);
return AE_NOT_FOUND;
wmi_device_put(wdev);
return status;
}
EXPORT_SYMBOL_GPL(wmi_get_event_data);
......@@ -868,7 +896,7 @@ static int wmi_dev_probe(struct device *dev)
if (wdriver->probe) {
ret = wdriver->probe(dev_to_wdev(dev),
find_guid_context(wblock, wdriver));
if (!ret) {
if (ret) {
if (ACPI_FAILURE(wmi_method_enable(wblock, false)))
dev_warn(dev, "Failed to disable device\n");
......@@ -876,7 +904,9 @@ static int wmi_dev_probe(struct device *dev)
}
}
set_bit(WMI_PROBED, &wblock->flags);
down_write(&wblock->notify_lock);
wblock->driver_ready = true;
up_write(&wblock->notify_lock);
return 0;
}
......@@ -886,7 +916,9 @@ static void wmi_dev_remove(struct device *dev)
struct wmi_block *wblock = dev_to_wblock(dev);
struct wmi_driver *wdriver = drv_to_wdrv(dev->driver);
clear_bit(WMI_PROBED, &wblock->flags);
down_write(&wblock->notify_lock);
wblock->driver_ready = false;
up_write(&wblock->notify_lock);
if (wdriver->remove)
wdriver->remove(dev_to_wdev(dev));
......@@ -999,6 +1031,8 @@ static int wmi_create_device(struct device *wmi_bus_dev,
wblock->dev.setable = true;
out_init:
init_rwsem(&wblock->notify_lock);
wblock->driver_ready = false;
wblock->dev.dev.bus = &wmi_bus_type;
wblock->dev.dev.parent = wmi_bus_dev;
......@@ -1171,6 +1205,26 @@ acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
}
}
static void wmi_notify_driver(struct wmi_block *wblock)
{
struct wmi_driver *driver = drv_to_wdrv(wblock->dev.dev.driver);
struct acpi_buffer data = { ACPI_ALLOCATE_BUFFER, NULL };
acpi_status status;
if (!driver->no_notify_data) {
status = get_event_data(wblock, &data);
if (ACPI_FAILURE(status)) {
dev_warn(&wblock->dev.dev, "Failed to get event data\n");
return;
}
}
if (driver->notify)
driver->notify(&wblock->dev, data.pointer);
kfree(data.pointer);
}
static int wmi_notify_device(struct device *dev, void *data)
{
struct wmi_block *wblock = dev_to_wblock(dev);
......@@ -1179,28 +1233,17 @@ static int wmi_notify_device(struct device *dev, void *data)
if (!(wblock->gblock.flags & ACPI_WMI_EVENT && wblock->gblock.notify_id == *event))
return 0;
/* If a driver is bound, then notify the driver. */
if (test_bit(WMI_PROBED, &wblock->flags) && wblock->dev.dev.driver) {
struct wmi_driver *driver = drv_to_wdrv(wblock->dev.dev.driver);
struct acpi_buffer evdata = { ACPI_ALLOCATE_BUFFER, NULL };
acpi_status status;
if (!driver->no_notify_data) {
status = get_event_data(wblock, &evdata);
if (ACPI_FAILURE(status)) {
dev_warn(&wblock->dev.dev, "failed to get event data\n");
return -EIO;
}
}
if (driver->notify)
driver->notify(&wblock->dev, evdata.pointer);
kfree(evdata.pointer);
} else if (wblock->handler) {
/* Legacy handler */
wblock->handler(*event, wblock->handler_data);
down_read(&wblock->notify_lock);
/* The WMI driver notify handler conflicts with the legacy WMI handler.
* Because of this the WMI driver notify handler takes precedence.
*/
if (wblock->dev.dev.driver && wblock->driver_ready) {
wmi_notify_driver(wblock);
} else {
if (wblock->handler)
wblock->handler(*event, wblock->handler_data);
}
up_read(&wblock->notify_lock);
acpi_bus_generate_netlink_event(wblock->acpi_device->pnp.device_class,
dev_name(&wblock->dev.dev), *event, 0);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment