Commit 34770887 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'platform-drivers-x86-v5.17-1' of...

Merge tag 'platform-drivers-x86-v5.17-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86

Pull x86 platform driver updates from Hans de Goede:
 "Highlights:

  New drivers:
   - asus-tf103c-dock
   - intel_crystal_cove_charger
   - lenovo-yogabook-wmi
   - simatic-ipc platform-code + led driver + watchdog driver
   - x86-android-tablets (kernel module to workaround DSDT bugs on
     these)

  amd-pmc:
   - bug-fixes
   - smar trace buffer support

  asus-wmi:
   - support for custom fan curves

  int3472 (camera info ACPI object for Intel IPU3/SkyCam cameras):
   - ACPI core + int3472 changes to delay enumeration of camera sensor
     I2C clients until the PMIC for the sensor has been fully probed
   - Add support for board data (DSDT info is incomplete) for setting up
     the tps68470 PMIC used on some boards with these cameras
   - Add board data for the Microsoft Surface Go (original, v2 and v3)

  thinkpad_acpi:
   - various cleanups
   - support for forced battery discharging (for battery calibration)
   - support to inhibit battery charging
   - this includes power_supply core changes to add new APIs for this

  think_lmi:
   - enhanced BIOS password support

  various other small fixes and hardware-id additions"

* tag 'platform-drivers-x86-v5.17-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: (78 commits)
  power: supply: Provide stubs for charge_behaviour helpers
  platform/x86: x86-android-tablets: Fix GPIO lookup leak on error-exit
  platform/x86: int3472: Add board data for Surface Go 3
  platform/x86: Add Asus TF103C dock driver
  platform/x86: x86-android-tablets: Add TM800A550L data
  platform/x86: x86-android-tablets: Add Asus MeMO Pad 7 ME176C data
  platform/x86: x86-android-tablets: Add Asus TF103C data
  platform/x86: x86-android-tablets: Add support for preloading modules
  platform/x86: x86-android-tablets: Add support for registering GPIO lookup tables
  platform/x86: x86-android-tablets: Add support for instantiating serdevs
  platform/x86: x86-android-tablets: Add support for instantiating platform-devs
  platform/x86: x86-android-tablets: Add support for PMIC interrupts
  platform/x86: x86-android-tablets: Don't return -EPROBE_DEFER from a non probe() function
  platform/x86: touchscreen_dmi: Remove the Glavey TM800A550L entry
  platform/x86: touchscreen_dmi: Enable pen support on the Chuwi Hi10 Plus and Pro
  platform/x86: touchscreen_dmi: Correct min/max values for Chuwi Hi10 Pro (CWI529) tablet
  platform/x86: Add intel_crystal_cove_charger driver
  power: supply: fix charge_behaviour attribute initialization
  platform/x86: intel-uncore-frequency: use default_groups in kobj_type
  x86/platform/uv: use default_groups in kobj_type
  ...
parents 46a67e76 3367d1bd
......@@ -161,6 +161,15 @@ Description:
power-on:
Representing a password required to use
the system
system-mgmt:
Representing System Management password.
See Lenovo extensions section for details
HDD:
Representing HDD password
See Lenovo extensions section for details
NVMe:
Representing NVMe password
See Lenovo extensions section for details
mechanism:
The means of authentication. This attribute is mandatory.
......@@ -207,6 +216,13 @@ Description:
On Lenovo systems the following additional settings are available:
role: system-mgmt This gives the same authority as the bios-admin password to control
security related features. The authorities allocated can be set via
the BIOS menu SMP Access Control Policy
role: HDD & NVMe This password is used to unlock access to the drive at boot. Note see
'level' and 'index' extensions below.
lenovo_encoding:
The encoding method that is used. This can be either "ascii"
or "scancode". Default is set to "ascii"
......@@ -216,6 +232,22 @@ Description:
two char code (e.g. "us", "fr", "gr") and may vary per platform.
Default is set to "us"
level:
Available for HDD and NVMe authentication to set 'user' or 'master'
privilege level.
If only the user password is configured then this should be used to
unlock the drive at boot. If both master and user passwords are set
then either can be used. If a master password is set a user password
is required.
This attribute defaults to 'user' level
index:
Used with HDD and NVME authentication to set the drive index
that is being referenced (e.g hdd0, hdd1 etc)
This attribute defaults to device 0.
What: /sys/class/firmware-attributes/*/attributes/pending_reboot
Date: February 2021
KernelVersion: 5.11
......
......@@ -455,6 +455,20 @@ Description:
"Unknown", "Charging", "Discharging",
"Not charging", "Full"
What: /sys/class/power_supply/<supply_name>/charge_behaviour
Date: November 2021
Contact: linux-pm@vger.kernel.org
Description:
Represents the charging behaviour.
Access: Read, Write
Valid values:
================ ====================================
auto: Charge normally, respect thresholds
inhibit-charge: Do not charge while AC is attached
force-discharge: Force discharge while AC is attached
What: /sys/class/power_supply/<supply_name>/technology
Date: May 2007
Contact: linux-pm@vger.kernel.org
......
......@@ -3013,6 +3013,13 @@ W: http://acpi4asus.sf.net
F: drivers/platform/x86/asus*.c
F: drivers/platform/x86/eeepc*.c
ASUS TF103C DOCK DRIVER
M: Hans de Goede <hdegoede@redhat.com>
L: platform-driver-x86@vger.kernel.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git
F: drivers/platform/x86/asus-tf103c-dock.c
ASUS WMI HARDWARE MONITOR DRIVER
M: Ed Brindley <kernel@maidavale.org>
M: Denis Pauk <pauk.denis@gmail.com>
......@@ -20836,6 +20843,13 @@ S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git x86/mm
F: arch/x86/mm/
X86 PLATFORM ANDROID TABLETS DSDT FIXUP DRIVER
M: Hans de Goede <hdegoede@redhat.com>
L: platform-driver-x86@vger.kernel.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git
F: drivers/platform/x86/x86-android-tablets.c
X86 PLATFORM DRIVERS
M: Hans de Goede <hdegoede@redhat.com>
M: Mark Gross <markgross@kernel.org>
......
......@@ -879,4 +879,7 @@ source "drivers/leds/flash/Kconfig"
comment "LED Triggers"
source "drivers/leds/trigger/Kconfig"
comment "Simple LED drivers"
source "drivers/leds/simple/Kconfig"
endif # NEW_LEDS
......@@ -105,3 +105,6 @@ obj-$(CONFIG_LEDS_TRIGGERS) += trigger/
# LED Blink
obj-y += blink/
# Simple LED drivers
obj-y += simple/
# SPDX-License-Identifier: GPL-2.0-only
config LEDS_SIEMENS_SIMATIC_IPC
tristate "LED driver for Siemens Simatic IPCs"
depends on LEDS_CLASS
depends on SIEMENS_SIMATIC_IPC
help
This option enables support for the LEDs of several Industrial PCs
from Siemens.
To compile this driver as a module, choose M here: the module
will be called simatic-ipc-leds.
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC) += simatic-ipc-leds.o
// SPDX-License-Identifier: GPL-2.0
/*
* Siemens SIMATIC IPC driver for LEDs
*
* Copyright (c) Siemens AG, 2018-2021
*
* Authors:
* Henning Schild <henning.schild@siemens.com>
* Jan Kiszka <jan.kiszka@siemens.com>
* Gerd Haeussler <gerd.haeussler.ext@siemens.com>
*/
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_data/x86/simatic-ipc-base.h>
#include <linux/platform_device.h>
#include <linux/sizes.h>
#include <linux/spinlock.h>
#define SIMATIC_IPC_LED_PORT_BASE 0x404E
struct simatic_ipc_led {
unsigned int value; /* mask for io and offset for mem */
char *name;
struct led_classdev cdev;
};
static struct simatic_ipc_led simatic_ipc_leds_io[] = {
{1 << 15, "green:" LED_FUNCTION_STATUS "-1" },
{1 << 7, "yellow:" LED_FUNCTION_STATUS "-1" },
{1 << 14, "red:" LED_FUNCTION_STATUS "-2" },
{1 << 6, "yellow:" LED_FUNCTION_STATUS "-2" },
{1 << 13, "red:" LED_FUNCTION_STATUS "-3" },
{1 << 5, "yellow:" LED_FUNCTION_STATUS "-3" },
{ }
};
/* the actual start will be discovered with PCI, 0 is a placeholder */
struct resource simatic_ipc_led_mem_res = DEFINE_RES_MEM_NAMED(0, SZ_4K, KBUILD_MODNAME);
static void *simatic_ipc_led_memory;
static struct simatic_ipc_led simatic_ipc_leds_mem[] = {
{0x500 + 0x1A0, "red:" LED_FUNCTION_STATUS "-1"},
{0x500 + 0x1A8, "green:" LED_FUNCTION_STATUS "-1"},
{0x500 + 0x1C8, "red:" LED_FUNCTION_STATUS "-2"},
{0x500 + 0x1D0, "green:" LED_FUNCTION_STATUS "-2"},
{0x500 + 0x1E0, "red:" LED_FUNCTION_STATUS "-3"},
{0x500 + 0x198, "green:" LED_FUNCTION_STATUS "-3"},
{ }
};
static struct resource simatic_ipc_led_io_res =
DEFINE_RES_IO_NAMED(SIMATIC_IPC_LED_PORT_BASE, SZ_2, KBUILD_MODNAME);
static DEFINE_SPINLOCK(reg_lock);
static inline struct simatic_ipc_led *cdev_to_led(struct led_classdev *led_cd)
{
return container_of(led_cd, struct simatic_ipc_led, cdev);
}
static void simatic_ipc_led_set_io(struct led_classdev *led_cd,
enum led_brightness brightness)
{
struct simatic_ipc_led *led = cdev_to_led(led_cd);
unsigned long flags;
unsigned int val;
spin_lock_irqsave(&reg_lock, flags);
val = inw(SIMATIC_IPC_LED_PORT_BASE);
if (brightness == LED_OFF)
outw(val | led->value, SIMATIC_IPC_LED_PORT_BASE);
else
outw(val & ~led->value, SIMATIC_IPC_LED_PORT_BASE);
spin_unlock_irqrestore(&reg_lock, flags);
}
static enum led_brightness simatic_ipc_led_get_io(struct led_classdev *led_cd)
{
struct simatic_ipc_led *led = cdev_to_led(led_cd);
return inw(SIMATIC_IPC_LED_PORT_BASE) & led->value ? LED_OFF : led_cd->max_brightness;
}
static void simatic_ipc_led_set_mem(struct led_classdev *led_cd,
enum led_brightness brightness)
{
struct simatic_ipc_led *led = cdev_to_led(led_cd);
u32 *p;
p = simatic_ipc_led_memory + led->value;
*p = (*p & ~1) | (brightness == LED_OFF);
}
static enum led_brightness simatic_ipc_led_get_mem(struct led_classdev *led_cd)
{
struct simatic_ipc_led *led = cdev_to_led(led_cd);
u32 *p;
p = simatic_ipc_led_memory + led->value;
return (*p & 1) ? LED_OFF : led_cd->max_brightness;
}
static int simatic_ipc_leds_probe(struct platform_device *pdev)
{
const struct simatic_ipc_platform *plat = pdev->dev.platform_data;
struct device *dev = &pdev->dev;
struct simatic_ipc_led *ipcled;
struct led_classdev *cdev;
struct resource *res;
int err, type;
u32 *p;
switch (plat->devmode) {
case SIMATIC_IPC_DEVICE_227D:
case SIMATIC_IPC_DEVICE_427E:
res = &simatic_ipc_led_io_res;
ipcled = simatic_ipc_leds_io;
/* on 227D the two bytes work the other way araound */
if (plat->devmode == SIMATIC_IPC_DEVICE_227D) {
while (ipcled->value) {
ipcled->value = swab16(ipcled->value);
ipcled++;
}
ipcled = simatic_ipc_leds_io;
}
type = IORESOURCE_IO;
if (!devm_request_region(dev, res->start, resource_size(res), KBUILD_MODNAME)) {
dev_err(dev, "Unable to register IO resource at %pR\n", res);
return -EBUSY;
}
break;
case SIMATIC_IPC_DEVICE_127E:
res = &simatic_ipc_led_mem_res;
ipcled = simatic_ipc_leds_mem;
type = IORESOURCE_MEM;
/* get GPIO base from PCI */
res->start = simatic_ipc_get_membase0(PCI_DEVFN(13, 0));
if (res->start == 0)
return -ENODEV;
/* do the final address calculation */
res->start = res->start + (0xC5 << 16);
res->end += res->start;
simatic_ipc_led_memory = devm_ioremap_resource(dev, res);
if (IS_ERR(simatic_ipc_led_memory))
return PTR_ERR(simatic_ipc_led_memory);
/* initialize power/watchdog LED */
p = simatic_ipc_led_memory + 0x500 + 0x1D8; /* PM_WDT_OUT */
*p = (*p & ~1);
p = simatic_ipc_led_memory + 0x500 + 0x1C0; /* PM_BIOS_BOOT_N */
*p = (*p | 1);
break;
default:
return -ENODEV;
}
while (ipcled->value) {
cdev = &ipcled->cdev;
if (type == IORESOURCE_MEM) {
cdev->brightness_set = simatic_ipc_led_set_mem;
cdev->brightness_get = simatic_ipc_led_get_mem;
} else {
cdev->brightness_set = simatic_ipc_led_set_io;
cdev->brightness_get = simatic_ipc_led_get_io;
}
cdev->max_brightness = LED_ON;
cdev->name = ipcled->name;
err = devm_led_classdev_register(dev, cdev);
if (err < 0)
return err;
ipcled++;
}
return 0;
}
static struct platform_driver simatic_ipc_led_driver = {
.probe = simatic_ipc_leds_probe,
.driver = {
.name = KBUILD_MODNAME,
}
};
module_platform_driver(simatic_ipc_led_driver);
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" KBUILD_MODNAME);
MODULE_AUTHOR("Henning Schild <henning.schild@siemens.com>");
......@@ -5,7 +5,6 @@
menuconfig SURFACE_PLATFORMS
bool "Microsoft Surface Platform-Specific Device Drivers"
depends on ACPI
default y
help
Say Y here to get to see options for platform-specific device drivers
......@@ -30,12 +29,14 @@ config SURFACE3_WMI
config SURFACE_3_BUTTON
tristate "Power/home/volume buttons driver for Microsoft Surface 3 tablet"
depends on ACPI
depends on KEYBOARD_GPIO && I2C
help
This driver handles the power/home/volume buttons on the Microsoft Surface 3 tablet.
config SURFACE_3_POWER_OPREGION
tristate "Surface 3 battery platform operation region support"
depends on ACPI
depends on I2C
help
This driver provides support for ACPI operation
......@@ -126,6 +127,7 @@ config SURFACE_DTX
config SURFACE_GPE
tristate "Surface GPE/Lid Support Driver"
depends on ACPI
depends on DMI
help
This driver marks the GPEs related to the ACPI lid device found on
......@@ -135,6 +137,7 @@ config SURFACE_GPE
config SURFACE_HOTPLUG
tristate "Surface Hot-Plug Driver"
depends on ACPI
depends on GPIOLIB
help
Driver for out-of-band hot-plug event signaling on Microsoft Surface
......@@ -154,6 +157,7 @@ config SURFACE_HOTPLUG
config SURFACE_PLATFORM_PROFILE
tristate "Surface Platform Profile Driver"
depends on ACPI
depends on SURFACE_AGGREGATOR_REGISTRY
select ACPI_PLATFORM_PROFILE
help
......@@ -176,6 +180,7 @@ config SURFACE_PLATFORM_PROFILE
config SURFACE_PRO3_BUTTON
tristate "Power/home/volume buttons driver for Microsoft Surface Pro 3/4 tablet"
depends on ACPI
depends on INPUT
help
This driver handles the power/home/volume buttons on the Microsoft Surface Pro 3/4 tablet.
......
......@@ -4,6 +4,7 @@
menuconfig SURFACE_AGGREGATOR
tristate "Microsoft Surface System Aggregator Module Subsystem and Drivers"
depends on SERIAL_DEV_BUS
depends on ACPI
select CRC_CCITT
help
The Surface System Aggregator Module (Surface SAM or SSAM) is an
......
......@@ -374,27 +374,19 @@ static int ssam_remove_device(struct device *dev, void *_data)
}
/**
* ssam_controller_remove_clients() - Remove SSAM client devices registered as
* direct children under the given controller.
* @ctrl: The controller to remove all direct clients for.
* ssam_remove_clients() - Remove SSAM client devices registered as direct
* children under the given parent device.
* @dev: The (parent) device to remove all direct clients for.
*
* Remove all SSAM client devices registered as direct children under the
* given controller. Note that this only accounts for direct children of the
* controller device. This does not take care of any client devices where the
* parent device has been manually set before calling ssam_device_add. Refer
* to ssam_device_add()/ssam_device_remove() for more details on those cases.
*
* To avoid new devices being added in parallel to this call, the main
* controller lock (not statelock) must be held during this (and if
* necessary, any subsequent deinitialization) call.
* Remove all SSAM client devices registered as direct children under the given
* device. Note that this only accounts for direct children of the device.
* Refer to ssam_device_add()/ssam_device_remove() for more details.
*/
void ssam_controller_remove_clients(struct ssam_controller *ctrl)
void ssam_remove_clients(struct device *dev)
{
struct device *dev;
dev = ssam_controller_device(ctrl);
device_for_each_child_reverse(dev, NULL, ssam_remove_device);
}
EXPORT_SYMBOL_GPL(ssam_remove_clients);
/**
* ssam_bus_register() - Register and set-up the SSAM client device bus.
......
......@@ -12,14 +12,11 @@
#ifdef CONFIG_SURFACE_AGGREGATOR_BUS
void ssam_controller_remove_clients(struct ssam_controller *ctrl);
int ssam_bus_register(void);
void ssam_bus_unregister(void);
#else /* CONFIG_SURFACE_AGGREGATOR_BUS */
static inline void ssam_controller_remove_clients(struct ssam_controller *ctrl) {}
static inline int ssam_bus_register(void) { return 0; }
static inline void ssam_bus_unregister(void) {}
......
......@@ -22,6 +22,7 @@
#include <linux/sysfs.h>
#include <linux/surface_aggregator/controller.h>
#include <linux/surface_aggregator/device.h>
#include "bus.h"
#include "controller.h"
......@@ -735,7 +736,7 @@ static void ssam_serial_hub_remove(struct serdev_device *serdev)
ssam_controller_lock(ctrl);
/* Remove all client devices. */
ssam_controller_remove_clients(ctrl);
ssam_remove_clients(&serdev->dev);
/* Act as if suspending to silence events. */
status = ssam_ctrl_notif_display_off(ctrl);
......
......@@ -258,20 +258,6 @@ static int ssam_uid_from_string(const char *str, struct ssam_device_uid *uid)
return 0;
}
static int ssam_hub_remove_devices_fn(struct device *dev, void *data)
{
if (!is_ssam_device(dev))
return 0;
ssam_device_remove(to_ssam_device(dev));
return 0;
}
static void ssam_hub_remove_devices(struct device *parent)
{
device_for_each_child_reverse(parent, NULL, ssam_hub_remove_devices_fn);
}
static int ssam_hub_add_device(struct device *parent, struct ssam_controller *ctrl,
struct fwnode_handle *node)
{
......@@ -297,8 +283,8 @@ static int ssam_hub_add_device(struct device *parent, struct ssam_controller *ct
return status;
}
static int ssam_hub_add_devices(struct device *parent, struct ssam_controller *ctrl,
struct fwnode_handle *node)
static int ssam_hub_register_clients(struct device *parent, struct ssam_controller *ctrl,
struct fwnode_handle *node)
{
struct fwnode_handle *child;
int status;
......@@ -317,7 +303,7 @@ static int ssam_hub_add_devices(struct device *parent, struct ssam_controller *c
return 0;
err:
ssam_hub_remove_devices(parent);
ssam_remove_clients(parent);
return status;
}
......@@ -412,9 +398,9 @@ static void ssam_base_hub_update_workfn(struct work_struct *work)
hub->state = state;
if (hub->state == SSAM_BASE_HUB_CONNECTED)
status = ssam_hub_add_devices(&hub->sdev->dev, hub->sdev->ctrl, node);
status = ssam_hub_register_clients(&hub->sdev->dev, hub->sdev->ctrl, node);
else
ssam_hub_remove_devices(&hub->sdev->dev);
ssam_remove_clients(&hub->sdev->dev);
if (status)
dev_err(&hub->sdev->dev, "failed to update base-hub devices: %d\n", status);
......@@ -496,7 +482,7 @@ static int ssam_base_hub_probe(struct ssam_device *sdev)
err:
ssam_notifier_unregister(sdev->ctrl, &hub->notif);
cancel_delayed_work_sync(&hub->update_work);
ssam_hub_remove_devices(&sdev->dev);
ssam_remove_clients(&sdev->dev);
return status;
}
......@@ -508,7 +494,7 @@ static void ssam_base_hub_remove(struct ssam_device *sdev)
ssam_notifier_unregister(sdev->ctrl, &hub->notif);
cancel_delayed_work_sync(&hub->update_work);
ssam_hub_remove_devices(&sdev->dev);
ssam_remove_clients(&sdev->dev);
}
static const struct ssam_device_id ssam_base_hub_match[] = {
......@@ -611,7 +597,7 @@ static int ssam_platform_hub_probe(struct platform_device *pdev)
set_secondary_fwnode(&pdev->dev, root);
status = ssam_hub_add_devices(&pdev->dev, ctrl, root);
status = ssam_hub_register_clients(&pdev->dev, ctrl, root);
if (status) {
set_secondary_fwnode(&pdev->dev, NULL);
software_node_unregister_node_group(nodes);
......@@ -625,7 +611,7 @@ static int ssam_platform_hub_remove(struct platform_device *pdev)
{
const struct software_node **nodes = platform_get_drvdata(pdev);
ssam_hub_remove_devices(&pdev->dev);
ssam_remove_clients(&pdev->dev);
set_secondary_fwnode(&pdev->dev, NULL);
software_node_unregister_node_group(nodes);
return 0;
......
......@@ -127,6 +127,19 @@ config GIGABYTE_WMI
To compile this driver as a module, choose M here: the module will
be called gigabyte-wmi.
config YOGABOOK_WMI
tristate "Lenovo Yoga Book tablet WMI key driver"
depends on ACPI_WMI
depends on INPUT
select LEDS_CLASS
select NEW_LEDS
help
Say Y here if you want to support the 'Pen' key and keyboard backlight
control on the Lenovo Yoga Book tablets.
To compile this driver as a module, choose M here: the module will
be called lenovo-yogabook-wmi.
config ACERHDF
tristate "Acer Aspire One temperature and fan driver"
depends on ACPI && THERMAL
......@@ -296,6 +309,25 @@ config ASUS_NB_WMI
If you have an ACPI-WMI compatible Asus Notebook, say Y or M
here.
config ASUS_TF103C_DOCK
tristate "Asus TF103C 2-in-1 keyboard dock"
depends on ACPI
depends on I2C
depends on INPUT
depends on HID
depends on GPIOLIB
help
This is a driver for the keyboard, touchpad and USB port of the
keyboard dock for the Asus TF103C 2-in-1 tablet.
This keyboard dock has its own I2C attached embedded controller
and the keyboard and touchpad are also connected over I2C,
instead of using the usual USB connection. This means that the
keyboard dock requires this special driver to function.
If you have an Asus TF103C tablet say Y or M here, for a generic x86
distro config say M here.
config MERAKI_MX100
tristate "Cisco Meraki MX100 Platform Driver"
depends on GPIOLIB
......@@ -993,6 +1025,23 @@ config TOUCHSCREEN_DMI
the OS-image for the device. This option supplies the missing info.
Enable this for x86 tablets with Silead or Chipone touchscreens.
config X86_ANDROID_TABLETS
tristate "X86 Android tablet support"
depends on I2C && SERIAL_DEV_BUS && ACPI && GPIOLIB
help
X86 tablets which ship with Android as (part of) the factory image
typically have various problems with their DSDTs. The factory kernels
shipped on these devices typically have device addresses and GPIOs
hardcoded in the kernel, rather than specified in their DSDT.
With the DSDT containing a random collection of devices which may or
may not actually be present. This driver contains various fixes for
such tablets, including instantiating kernel devices for devices which
are missing from the DSDT.
If you have a x86 Android tablet say Y or M here, for a generic x86
distro config say M here.
config FW_ATTR_CLASS
tristate
......@@ -1077,6 +1126,18 @@ config INTEL_SCU_IPC_UTIL
low level access for debug work and updating the firmware. Say
N unless you will be doing this on an Intel MID platform.
config SIEMENS_SIMATIC_IPC
tristate "Siemens Simatic IPC Class driver"
depends on PCI
help
This Simatic IPC class driver is the central of several drivers. It
is mainly used for system identification, after which drivers in other
classes will take care of driving specifics of those machines.
i.e. LEDs and watchdog.
To compile this driver as a module, choose M here: the module
will be called simatic-ipc.
endif # X86_PLATFORM_DEVICES
config PMC_ATOM
......
......@@ -15,6 +15,7 @@ obj-$(CONFIG_NVIDIA_WMI_EC_BACKLIGHT) += nvidia-wmi-ec-backlight.o
obj-$(CONFIG_PEAQ_WMI) += peaq-wmi.o
obj-$(CONFIG_XIAOMI_WMI) += xiaomi-wmi.o
obj-$(CONFIG_GIGABYTE_WMI) += gigabyte-wmi.o
obj-$(CONFIG_YOGABOOK_WMI) += lenovo-yogabook-wmi.o
# Acer
obj-$(CONFIG_ACERHDF) += acerhdf.o
......@@ -35,6 +36,7 @@ obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o
obj-$(CONFIG_ASUS_WIRELESS) += asus-wireless.o
obj-$(CONFIG_ASUS_WMI) += asus-wmi.o
obj-$(CONFIG_ASUS_NB_WMI) += asus-nb-wmi.o
obj-$(CONFIG_ASUS_TF103C_DOCK) += asus-tf103c-dock.o
obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o
obj-$(CONFIG_EEEPC_WMI) += eeepc-wmi.o
......@@ -112,6 +114,7 @@ obj-$(CONFIG_I2C_MULTI_INSTANTIATE) += i2c-multi-instantiate.o
obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o
obj-$(CONFIG_TOUCHSCREEN_DMI) += touchscreen_dmi.o
obj-$(CONFIG_WIRELESS_HOTKEY) += wireless-hotkey.o
obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets.o
# Intel uncore drivers
obj-$(CONFIG_INTEL_IPS) += intel_ips.o
......@@ -123,3 +126,6 @@ obj-$(CONFIG_INTEL_SCU_PLATFORM) += intel_scu_pltdrv.o
obj-$(CONFIG_INTEL_SCU_WDT) += intel_scu_wdt.o
obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o
obj-$(CONFIG_PMC_ATOM) += pmc_atom.o
# Siemens Simatic Industrial PCs
obj-$(CONFIG_SIEMENS_SIMATIC_IPC) += simatic-ipc.o
......@@ -35,6 +35,12 @@
#define AMD_PMC_SCRATCH_REG_CZN 0x94
#define AMD_PMC_SCRATCH_REG_YC 0xD14
/* STB Registers */
#define AMD_PMC_STB_INDEX_ADDRESS 0xF8
#define AMD_PMC_STB_INDEX_DATA 0xFC
#define AMD_PMC_STB_PMI_0 0x03E30600
#define AMD_PMC_STB_PREDEF 0xC6000001
/* Base address of SMU for mapping physical address to virtual address */
#define AMD_PMC_SMU_INDEX_ADDRESS 0xB8
#define AMD_PMC_SMU_INDEX_DATA 0xBC
......@@ -82,6 +88,7 @@
#define SOC_SUBSYSTEM_IP_MAX 12
#define DELAY_MIN_US 2000
#define DELAY_MAX_US 3000
#define FIFO_SIZE 4096
enum amd_pmc_def {
MSG_TEST = 0x01,
MSG_OS_HINT_PCO,
......@@ -121,14 +128,21 @@ struct amd_pmc_dev {
u16 minor;
u16 rev;
struct device *dev;
struct pci_dev *rdev;
struct mutex lock; /* generic mutex lock */
#if IS_ENABLED(CONFIG_DEBUG_FS)
struct dentry *dbgfs_dir;
#endif /* CONFIG_DEBUG_FS */
};
static bool enable_stb;
module_param(enable_stb, bool, 0644);
MODULE_PARM_DESC(enable_stb, "Enable the STB debug mechanism");
static struct amd_pmc_dev pmc;
static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret);
static int amd_pmc_write_stb(struct amd_pmc_dev *dev, u32 data);
static int amd_pmc_read_stb(struct amd_pmc_dev *dev, u32 *buf);
static inline u32 amd_pmc_reg_read(struct amd_pmc_dev *dev, int reg_offset)
{
......@@ -175,6 +189,50 @@ static int amd_pmc_get_smu_version(struct amd_pmc_dev *dev)
return 0;
}
static int amd_pmc_stb_debugfs_open(struct inode *inode, struct file *filp)
{
struct amd_pmc_dev *dev = filp->f_inode->i_private;
u32 size = FIFO_SIZE * sizeof(u32);
u32 *buf;
int rc;
buf = kzalloc(size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
rc = amd_pmc_read_stb(dev, buf);
if (rc) {
kfree(buf);
return rc;
}
filp->private_data = buf;
return rc;
}
static ssize_t amd_pmc_stb_debugfs_read(struct file *filp, char __user *buf, size_t size,
loff_t *pos)
{
if (!filp->private_data)
return -EINVAL;
return simple_read_from_buffer(buf, size, pos, filp->private_data,
FIFO_SIZE * sizeof(u32));
}
static int amd_pmc_stb_debugfs_release(struct inode *inode, struct file *filp)
{
kfree(filp->private_data);
return 0;
}
const struct file_operations amd_pmc_stb_debugfs_fops = {
.owner = THIS_MODULE,
.open = amd_pmc_stb_debugfs_open,
.read = amd_pmc_stb_debugfs_read,
.release = amd_pmc_stb_debugfs_release,
};
static int amd_pmc_idlemask_read(struct amd_pmc_dev *pdev, struct device *dev,
struct seq_file *s)
{
......@@ -288,6 +346,10 @@ static void amd_pmc_dbgfs_register(struct amd_pmc_dev *dev)
&s0ix_stats_fops);
debugfs_create_file("amd_pmc_idlemask", 0644, dev->dbgfs_dir, dev,
&amd_pmc_idlemask_fops);
/* Enable STB only when the module_param is set */
if (enable_stb)
debugfs_create_file("stb_read", 0644, dev->dbgfs_dir, dev,
&amd_pmc_stb_debugfs_fops);
}
#else
static inline void amd_pmc_dbgfs_register(struct amd_pmc_dev *dev)
......@@ -484,6 +546,13 @@ static int __maybe_unused amd_pmc_suspend(struct device *dev)
if (rc)
dev_err(pdev->dev, "suspend failed\n");
if (enable_stb)
rc = amd_pmc_write_stb(pdev, AMD_PMC_STB_PREDEF);
if (rc) {
dev_err(pdev->dev, "error writing to STB\n");
return rc;
}
return rc;
}
......@@ -504,6 +573,14 @@ static int __maybe_unused amd_pmc_resume(struct device *dev)
/* Dump the IdleMask to see the blockers */
amd_pmc_idlemask_read(pdev, dev, NULL);
/* Write data incremented by 1 to distinguish in stb_read */
if (enable_stb)
rc = amd_pmc_write_stb(pdev, AMD_PMC_STB_PREDEF + 1);
if (rc) {
dev_err(pdev->dev, "error writing to STB\n");
return rc;
}
return 0;
}
......@@ -521,6 +598,50 @@ static const struct pci_device_id pmc_pci_ids[] = {
{ }
};
static int amd_pmc_write_stb(struct amd_pmc_dev *dev, u32 data)
{
int err;
err = pci_write_config_dword(dev->rdev, AMD_PMC_STB_INDEX_ADDRESS, AMD_PMC_STB_PMI_0);
if (err) {
dev_err(dev->dev, "failed to write addr in stb: 0x%X\n",
AMD_PMC_STB_INDEX_ADDRESS);
return pcibios_err_to_errno(err);
}
err = pci_write_config_dword(dev->rdev, AMD_PMC_STB_INDEX_DATA, data);
if (err) {
dev_err(dev->dev, "failed to write data in stb: 0x%X\n",
AMD_PMC_STB_INDEX_DATA);
return pcibios_err_to_errno(err);
}
return 0;
}
static int amd_pmc_read_stb(struct amd_pmc_dev *dev, u32 *buf)
{
int i, err;
err = pci_write_config_dword(dev->rdev, AMD_PMC_STB_INDEX_ADDRESS, AMD_PMC_STB_PMI_0);
if (err) {
dev_err(dev->dev, "error writing addr to stb: 0x%X\n",
AMD_PMC_STB_INDEX_ADDRESS);
return pcibios_err_to_errno(err);
}
for (i = 0; i < FIFO_SIZE; i++) {
err = pci_read_config_dword(dev->rdev, AMD_PMC_STB_INDEX_DATA, buf++);
if (err) {
dev_err(dev->dev, "error reading data from stb: 0x%X\n",
AMD_PMC_STB_INDEX_DATA);
return pcibios_err_to_errno(err);
}
}
return 0;
}
static int amd_pmc_probe(struct platform_device *pdev)
{
struct amd_pmc_dev *dev = &pmc;
......@@ -534,22 +655,23 @@ static int amd_pmc_probe(struct platform_device *pdev)
rdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0, 0));
if (!rdev || !pci_match_id(pmc_pci_ids, rdev)) {
pci_dev_put(rdev);
return -ENODEV;
err = -ENODEV;
goto err_pci_dev_put;
}
dev->cpu_id = rdev->device;
dev->rdev = rdev;
err = pci_write_config_dword(rdev, AMD_PMC_SMU_INDEX_ADDRESS, AMD_PMC_BASE_ADDR_LO);
if (err) {
dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMC_SMU_INDEX_ADDRESS);
pci_dev_put(rdev);
return pcibios_err_to_errno(err);
err = pcibios_err_to_errno(err);
goto err_pci_dev_put;
}
err = pci_read_config_dword(rdev, AMD_PMC_SMU_INDEX_DATA, &val);
if (err) {
pci_dev_put(rdev);
return pcibios_err_to_errno(err);
err = pcibios_err_to_errno(err);
goto err_pci_dev_put;
}
base_addr_lo = val & AMD_PMC_BASE_ADDR_HI_MASK;
......@@ -557,24 +679,25 @@ static int amd_pmc_probe(struct platform_device *pdev)
err = pci_write_config_dword(rdev, AMD_PMC_SMU_INDEX_ADDRESS, AMD_PMC_BASE_ADDR_HI);
if (err) {
dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMC_SMU_INDEX_ADDRESS);
pci_dev_put(rdev);
return pcibios_err_to_errno(err);
err = pcibios_err_to_errno(err);
goto err_pci_dev_put;
}
err = pci_read_config_dword(rdev, AMD_PMC_SMU_INDEX_DATA, &val);
if (err) {
pci_dev_put(rdev);
return pcibios_err_to_errno(err);
err = pcibios_err_to_errno(err);
goto err_pci_dev_put;
}
base_addr_hi = val & AMD_PMC_BASE_ADDR_LO_MASK;
pci_dev_put(rdev);
base_addr = ((u64)base_addr_hi << 32 | base_addr_lo);
dev->regbase = devm_ioremap(dev->dev, base_addr + AMD_PMC_BASE_ADDR_OFFSET,
AMD_PMC_MAPPING_SIZE);
if (!dev->regbase)
return -ENOMEM;
if (!dev->regbase) {
err = -ENOMEM;
goto err_pci_dev_put;
}
mutex_init(&dev->lock);
......@@ -583,8 +706,10 @@ static int amd_pmc_probe(struct platform_device *pdev)
base_addr_hi = FCH_BASE_PHY_ADDR_HIGH;
fch_phys_addr = ((u64)base_addr_hi << 32 | base_addr_lo);
dev->fch_virt_addr = devm_ioremap(dev->dev, fch_phys_addr, FCH_SSC_MAPPING_SIZE);
if (!dev->fch_virt_addr)
return -ENOMEM;
if (!dev->fch_virt_addr) {
err = -ENOMEM;
goto err_pci_dev_put;
}
/* Use SMU to get the s0i3 debug stats */
err = amd_pmc_setup_smu_logging(dev);
......@@ -595,6 +720,10 @@ static int amd_pmc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dev);
amd_pmc_dbgfs_register(dev);
return 0;
err_pci_dev_put:
pci_dev_put(rdev);
return err;
}
static int amd_pmc_remove(struct platform_device *pdev)
......@@ -602,6 +731,7 @@ static int amd_pmc_remove(struct platform_device *pdev)
struct amd_pmc_dev *dev = platform_get_drvdata(pdev);
amd_pmc_dbgfs_unregister(dev);
pci_dev_put(dev->rdev);
mutex_destroy(&dev->lock);
return 0;
}
......
This diff is collapsed.
This diff is collapsed.
......@@ -355,39 +355,20 @@ static int lis3lv02d_remove(struct platform_device *device)
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int lis3lv02d_suspend(struct device *dev)
static int __maybe_unused lis3lv02d_suspend(struct device *dev)
{
/* make sure the device is off when we suspend */
lis3lv02d_poweroff(&lis3_dev);
return 0;
}
static int lis3lv02d_resume(struct device *dev)
static int __maybe_unused lis3lv02d_resume(struct device *dev)
{
lis3lv02d_poweron(&lis3_dev);
return 0;
}
static int lis3lv02d_restore(struct device *dev)
{
lis3lv02d_poweron(&lis3_dev);
return 0;
}
static const struct dev_pm_ops hp_accel_pm = {
.suspend = lis3lv02d_suspend,
.resume = lis3lv02d_resume,
.freeze = lis3lv02d_suspend,
.thaw = lis3lv02d_resume,
.poweroff = lis3lv02d_suspend,
.restore = lis3lv02d_restore,
};
#define HP_ACCEL_PM (&hp_accel_pm)
#else
#define HP_ACCEL_PM NULL
#endif
static SIMPLE_DEV_PM_OPS(hp_accel_pm, lis3lv02d_suspend, lis3lv02d_resume);
/* For the HP MDPS aka 3D Driveguard */
static struct platform_driver lis3lv02d_driver = {
......@@ -395,7 +376,7 @@ static struct platform_driver lis3lv02d_driver = {
.remove = lis3lv02d_remove,
.driver = {
.name = "hp_accel",
.pm = HP_ACCEL_PM,
.pm = &hp_accel_pm,
.acpi_match_table = lis3lv02d_device_ids,
},
};
......
......@@ -30,6 +30,8 @@ obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o
# Intel PMIC / PMC / P-Unit drivers
intel_bxtwc_tmu-y := bxtwc_tmu.o
obj-$(CONFIG_INTEL_BXTWC_PMIC_TMU) += intel_bxtwc_tmu.o
intel_crystal_cove_charger-y := crystal_cove_charger.o
obj-$(CONFIG_X86_ANDROID_TABLETS) += intel_crystal_cove_charger.o
intel_chtdc_ti_pwrbtn-y := chtdc_ti_pwrbtn.o
obj-$(CONFIG_INTEL_CHTDC_TI_PWRBTN) += intel_chtdc_ti_pwrbtn.o
intel_mrfld_pwrbtn-y := mrfld_pwrbtn.o
......
// SPDX-License-Identifier: GPL-2.0+
/*
* Driver for the external-charger IRQ pass-through function of the
* Intel Bay Trail Crystal Cove PMIC.
*
* Note this is NOT a power_supply class driver, it just deals with IRQ
* pass-through, this requires a separate driver because the PMIC's
* level 2 interrupt for this must be explicitly acked.
*/
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/mfd/intel_soc_pmic.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#define CHGRIRQ_REG 0x0a
struct crystal_cove_charger_data {
struct mutex buslock; /* irq_bus_lock */
struct irq_chip irqchip;
struct regmap *regmap;
struct irq_domain *irq_domain;
int irq;
int charger_irq;
bool irq_enabled;
bool irq_is_enabled;
};
static irqreturn_t crystal_cove_charger_irq(int irq, void *data)
{
struct crystal_cove_charger_data *charger = data;
/* No need to read CHGRIRQ_REG as there is only 1 IRQ */
handle_nested_irq(charger->charger_irq);
/* Ack CHGRIRQ 0 */
regmap_write(charger->regmap, CHGRIRQ_REG, BIT(0));
return IRQ_HANDLED;
}
static void crystal_cove_charger_irq_bus_lock(struct irq_data *data)
{
struct crystal_cove_charger_data *charger = irq_data_get_irq_chip_data(data);
mutex_lock(&charger->buslock);
}
static void crystal_cove_charger_irq_bus_sync_unlock(struct irq_data *data)
{
struct crystal_cove_charger_data *charger = irq_data_get_irq_chip_data(data);
if (charger->irq_is_enabled != charger->irq_enabled) {
if (charger->irq_enabled)
enable_irq(charger->irq);
else
disable_irq(charger->irq);
charger->irq_is_enabled = charger->irq_enabled;
}
mutex_unlock(&charger->buslock);
}
static void crystal_cove_charger_irq_unmask(struct irq_data *data)
{
struct crystal_cove_charger_data *charger = irq_data_get_irq_chip_data(data);
charger->irq_enabled = true;
}
static void crystal_cove_charger_irq_mask(struct irq_data *data)
{
struct crystal_cove_charger_data *charger = irq_data_get_irq_chip_data(data);
charger->irq_enabled = false;
}
static void crystal_cove_charger_rm_irq_domain(void *data)
{
struct crystal_cove_charger_data *charger = data;
irq_domain_remove(charger->irq_domain);
}
static int crystal_cove_charger_probe(struct platform_device *pdev)
{
struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
struct crystal_cove_charger_data *charger;
int ret;
charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);
if (!charger)
return -ENOMEM;
charger->regmap = pmic->regmap;
mutex_init(&charger->buslock);
charger->irq = platform_get_irq(pdev, 0);
if (charger->irq < 0)
return charger->irq;
charger->irq_domain = irq_domain_create_linear(dev_fwnode(pdev->dev.parent), 1,
&irq_domain_simple_ops, NULL);
if (!charger->irq_domain)
return -ENOMEM;
/* Distuingish IRQ domain from others sharing (MFD) the same fwnode */
irq_domain_update_bus_token(charger->irq_domain, DOMAIN_BUS_WAKEUP);
ret = devm_add_action_or_reset(&pdev->dev, crystal_cove_charger_rm_irq_domain, charger);
if (ret)
return ret;
charger->charger_irq = irq_create_mapping(charger->irq_domain, 0);
if (!charger->charger_irq)
return -ENOMEM;
charger->irqchip.name = KBUILD_MODNAME;
charger->irqchip.irq_unmask = crystal_cove_charger_irq_unmask;
charger->irqchip.irq_mask = crystal_cove_charger_irq_mask;
charger->irqchip.irq_bus_lock = crystal_cove_charger_irq_bus_lock;
charger->irqchip.irq_bus_sync_unlock = crystal_cove_charger_irq_bus_sync_unlock;
irq_set_chip_data(charger->charger_irq, charger);
irq_set_chip_and_handler(charger->charger_irq, &charger->irqchip, handle_simple_irq);
irq_set_nested_thread(charger->charger_irq, true);
irq_set_noprobe(charger->charger_irq);
ret = devm_request_threaded_irq(&pdev->dev, charger->irq, NULL,
crystal_cove_charger_irq,
IRQF_ONESHOT | IRQF_NO_AUTOEN,
KBUILD_MODNAME, charger);
if (ret)
return dev_err_probe(&pdev->dev, ret, "requesting irq\n");
return 0;
}
static struct platform_driver crystal_cove_charger_driver = {
.probe = crystal_cove_charger_probe,
.driver = {
.name = "crystal_cove_charger",
},
};
module_platform_driver(crystal_cove_charger_driver);
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com");
MODULE_DESCRIPTION("Intel Bay Trail Crystal Cove external charger IRQ pass-through");
MODULE_LICENSE("GPL");
......@@ -110,6 +110,12 @@ static const struct int3472_tps68470_board_data surface_go_tps68470_board_data =
.tps68470_regulator_pdata = &surface_go_tps68470_pdata,
};
static const struct int3472_tps68470_board_data surface_go3_tps68470_board_data = {
.dev_name = "i2c-INT3472:01",
.tps68470_gpio_lookup_table = &surface_go_tps68470_gpios,
.tps68470_regulator_pdata = &surface_go_tps68470_pdata,
};
static const struct dmi_system_id int3472_tps68470_board_data_table[] = {
{
.matches = {
......@@ -125,6 +131,13 @@ static const struct dmi_system_id int3472_tps68470_board_data_table[] = {
},
.driver_data = (void *)&surface_go_tps68470_board_data,
},
{
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Go 3"),
},
.driver_data = (void *)&surface_go3_tps68470_board_data,
},
{ }
};
......
......@@ -225,6 +225,7 @@ static struct attribute *uncore_attrs[] = {
&min_freq_khz.attr,
NULL
};
ATTRIBUTE_GROUPS(uncore);
static void uncore_sysfs_entry_release(struct kobject *kobj)
{
......@@ -236,7 +237,7 @@ static void uncore_sysfs_entry_release(struct kobject *kobj)
static struct kobj_type uncore_ktype = {
.release = uncore_sysfs_entry_release,
.sysfs_ops = &kobj_sysfs_ops,
.default_attrs = uncore_attrs,
.default_groups = uncore_groups,
};
/* Caller provides protection */
......
This diff is collapsed.
......@@ -13,6 +13,7 @@
#include <linux/io.h>
#include <linux/platform_data/x86/clk-pmc-atom.h>
#include <linux/platform_data/x86/pmc_atom.h>
#include <linux/platform_data/x86/simatic-ipc.h>
#include <linux/platform_device.h>
#include <linux/pci.h>
#include <linux/seq_file.h>
......@@ -362,6 +363,30 @@ static void pmc_dbgfs_register(struct pmc_dev *pmc)
}
#endif /* CONFIG_DEBUG_FS */
static bool pmc_clk_is_critical = true;
static int dmi_callback(const struct dmi_system_id *d)
{
pr_info("%s critclks quirk enabled\n", d->ident);
return 1;
}
static int dmi_callback_siemens(const struct dmi_system_id *d)
{
u32 st_id;
if (dmi_walk(simatic_ipc_find_dmi_entry_helper, &st_id))
goto out;
if (st_id == SIMATIC_IPC_IPC227E || st_id == SIMATIC_IPC_IPC277E)
return dmi_callback(d);
out:
pmc_clk_is_critical = false;
return 1;
}
/*
* Some systems need one or more of their pmc_plt_clks to be
* marked as critical.
......@@ -370,6 +395,7 @@ static const struct dmi_system_id critclk_systems[] = {
{
/* pmc_plt_clk0 is used for an external HSIC USB HUB */
.ident = "MPL CEC1x",
.callback = dmi_callback,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "MPL AG"),
DMI_MATCH(DMI_PRODUCT_NAME, "CEC10 Family"),
......@@ -378,6 +404,7 @@ static const struct dmi_system_id critclk_systems[] = {
{
/* pmc_plt_clk0 - 3 are used for the 4 ethernet controllers */
.ident = "Lex 3I380D",
.callback = dmi_callback,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Lex BayTrail"),
DMI_MATCH(DMI_PRODUCT_NAME, "3I380D"),
......@@ -386,6 +413,7 @@ static const struct dmi_system_id critclk_systems[] = {
{
/* pmc_plt_clk* - are used for ethernet controllers */
.ident = "Lex 2I385SW",
.callback = dmi_callback,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Lex BayTrail"),
DMI_MATCH(DMI_PRODUCT_NAME, "2I385SW"),
......@@ -394,30 +422,17 @@ static const struct dmi_system_id critclk_systems[] = {
{
/* pmc_plt_clk* - are used for ethernet controllers */
.ident = "Beckhoff Baytrail",
.callback = dmi_callback,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Beckhoff Automation"),
DMI_MATCH(DMI_PRODUCT_FAMILY, "CBxx63"),
},
},
{
.ident = "SIMATIC IPC227E",
.ident = "SIEMENS AG",
.callback = dmi_callback_siemens,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "SIEMENS AG"),
DMI_MATCH(DMI_PRODUCT_VERSION, "6ES7647-8B"),
},
},
{
.ident = "SIMATIC IPC277E",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "SIEMENS AG"),
DMI_MATCH(DMI_PRODUCT_VERSION, "6AV7882-0"),
},
},
{
.ident = "CONNECT X300",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "SIEMENS AG"),
DMI_MATCH(DMI_PRODUCT_VERSION, "A5E45074588"),
},
},
......@@ -429,7 +444,6 @@ static int pmc_setup_clks(struct pci_dev *pdev, void __iomem *pmc_regmap,
{
struct platform_device *clkdev;
struct pmc_clk_data *clk_data;
const struct dmi_system_id *d = dmi_first_match(critclk_systems);
clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
if (!clk_data)
......@@ -437,10 +451,8 @@ static int pmc_setup_clks(struct pci_dev *pdev, void __iomem *pmc_regmap,
clk_data->base = pmc_regmap; /* offset is added by client */
clk_data->clks = pmc_data->clks;
if (d) {
clk_data->critical = true;
pr_info("%s critclks quirk enabled\n", d->ident);
}
if (dmi_check_system(critclk_systems))
clk_data->critical = pmc_clk_is_critical;
clkdev = platform_device_register_data(&pdev->dev, "clk-pmc-atom",
PLATFORM_DEVID_NONE,
......
// SPDX-License-Identifier: GPL-2.0
/*
* Siemens SIMATIC IPC platform driver
*
* Copyright (c) Siemens AG, 2018-2021
*
* Authors:
* Henning Schild <henning.schild@siemens.com>
* Jan Kiszka <jan.kiszka@siemens.com>
* Gerd Haeussler <gerd.haeussler.ext@siemens.com>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/dmi.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_data/x86/simatic-ipc.h>
#include <linux/platform_device.h>
static struct platform_device *ipc_led_platform_device;
static struct platform_device *ipc_wdt_platform_device;
static const struct dmi_system_id simatic_ipc_whitelist[] = {
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "SIEMENS AG"),
},
},
{}
};
static struct simatic_ipc_platform platform_data;
static struct {
u32 station_id;
u8 led_mode;
u8 wdt_mode;
} device_modes[] = {
{SIMATIC_IPC_IPC127E, SIMATIC_IPC_DEVICE_127E, SIMATIC_IPC_DEVICE_NONE},
{SIMATIC_IPC_IPC227D, SIMATIC_IPC_DEVICE_227D, SIMATIC_IPC_DEVICE_NONE},
{SIMATIC_IPC_IPC227E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_227E},
{SIMATIC_IPC_IPC277E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227E},
{SIMATIC_IPC_IPC427D, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE},
{SIMATIC_IPC_IPC427E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_427E},
{SIMATIC_IPC_IPC477E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_427E},
};
static int register_platform_devices(u32 station_id)
{
u8 ledmode = SIMATIC_IPC_DEVICE_NONE;
u8 wdtmode = SIMATIC_IPC_DEVICE_NONE;
int i;
platform_data.devmode = SIMATIC_IPC_DEVICE_NONE;
for (i = 0; i < ARRAY_SIZE(device_modes); i++) {
if (device_modes[i].station_id == station_id) {
ledmode = device_modes[i].led_mode;
wdtmode = device_modes[i].wdt_mode;
break;
}
}
if (ledmode != SIMATIC_IPC_DEVICE_NONE) {
platform_data.devmode = ledmode;
ipc_led_platform_device =
platform_device_register_data(NULL,
KBUILD_MODNAME "_leds", PLATFORM_DEVID_NONE,
&platform_data,
sizeof(struct simatic_ipc_platform));
if (IS_ERR(ipc_led_platform_device))
return PTR_ERR(ipc_led_platform_device);
pr_debug("device=%s created\n",
ipc_led_platform_device->name);
}
if (wdtmode != SIMATIC_IPC_DEVICE_NONE) {
platform_data.devmode = wdtmode;
ipc_wdt_platform_device =
platform_device_register_data(NULL,
KBUILD_MODNAME "_wdt", PLATFORM_DEVID_NONE,
&platform_data,
sizeof(struct simatic_ipc_platform));
if (IS_ERR(ipc_wdt_platform_device))
return PTR_ERR(ipc_wdt_platform_device);
pr_debug("device=%s created\n",
ipc_wdt_platform_device->name);
}
if (ledmode == SIMATIC_IPC_DEVICE_NONE &&
wdtmode == SIMATIC_IPC_DEVICE_NONE) {
pr_warn("unsupported IPC detected, station id=%08x\n",
station_id);
return -EINVAL;
}
return 0;
}
/* FIXME: this should eventually be done with generic P2SB discovery code
* the individual drivers for watchdogs and LEDs access memory that implements
* GPIO, but pinctrl will not come up because of missing ACPI entries
*
* While there is no conflict a cleaner solution would be to somehow bring up
* pinctrl even with these ACPI entries missing, and base the drivers on pinctrl.
* After which the following function could be dropped, together with the code
* poking the memory.
*/
/*
* Get membase address from PCI, used in leds and wdt module. Here we read
* the bar0. The final address calculation is done in the appropriate modules
*/
u32 simatic_ipc_get_membase0(unsigned int p2sb)
{
struct pci_bus *bus;
u32 bar0 = 0;
/*
* The GPIO memory is in bar0 of the hidden P2SB device.
* Unhide the device to have a quick look at it, before we hide it
* again.
* Also grab the pci rescan lock so that device does not get discovered
* and remapped while it is visible.
* This code is inspired by drivers/mfd/lpc_ich.c
*/
bus = pci_find_bus(0, 0);
pci_lock_rescan_remove();
pci_bus_write_config_byte(bus, p2sb, 0xE1, 0x0);
pci_bus_read_config_dword(bus, p2sb, PCI_BASE_ADDRESS_0, &bar0);
bar0 &= ~0xf;
pci_bus_write_config_byte(bus, p2sb, 0xE1, 0x1);
pci_unlock_rescan_remove();
return bar0;
}
EXPORT_SYMBOL(simatic_ipc_get_membase0);
static int __init simatic_ipc_init_module(void)
{
const struct dmi_system_id *match;
u32 station_id;
int err;
match = dmi_first_match(simatic_ipc_whitelist);
if (!match)
return 0;
err = dmi_walk(simatic_ipc_find_dmi_entry_helper, &station_id);
if (err || station_id == SIMATIC_IPC_INVALID_STATION_ID) {
pr_warn("DMI entry %d not found\n", SIMATIC_IPC_DMI_ENTRY_OEM);
return 0;
}
return register_platform_devices(station_id);
}
static void __exit simatic_ipc_exit_module(void)
{
platform_device_unregister(ipc_led_platform_device);
ipc_led_platform_device = NULL;
platform_device_unregister(ipc_wdt_platform_device);
ipc_wdt_platform_device = NULL;
}
module_init(simatic_ipc_init_module);
module_exit(simatic_ipc_exit_module);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Gerd Haeussler <gerd.haeussler.ext@siemens.com>");
MODULE_ALIAS("dmi:*:svnSIEMENSAG:*");
This diff is collapsed.
......@@ -9,6 +9,7 @@
#define TLMI_SETTINGS_MAXLEN 512
#define TLMI_PWD_BUFSIZE 129
#define TLMI_LANG_MAXLEN 4
#define TLMI_INDEX_MAX 32
/* Possible error values */
struct tlmi_err_codes {
......@@ -21,8 +22,13 @@ enum encoding_option {
TLMI_ENCODING_SCANCODE,
};
enum level_option {
TLMI_LEVEL_USER,
TLMI_LEVEL_MASTER,
};
/* password configuration details */
struct tlmi_pwdcfg {
struct tlmi_pwdcfg_core {
uint32_t password_mode;
uint32_t password_state;
uint32_t min_length;
......@@ -31,6 +37,18 @@ struct tlmi_pwdcfg {
uint32_t supported_keyboard;
};
struct tlmi_pwdcfg_ext {
uint32_t hdd_user_password;
uint32_t hdd_master_password;
uint32_t nvme_user_password;
uint32_t nvme_master_password;
};
struct tlmi_pwdcfg {
struct tlmi_pwdcfg_core core;
struct tlmi_pwdcfg_ext ext;
};
/* password setting details */
struct tlmi_pwd_setting {
struct kobject kobj;
......@@ -42,6 +60,8 @@ struct tlmi_pwd_setting {
int maxlen;
enum encoding_option encoding;
char kbdlang[TLMI_LANG_MAXLEN];
int index; /*Used for HDD and NVME auth */
enum level_option level;
};
/* Attribute setting details */
......@@ -61,13 +81,19 @@ struct think_lmi {
bool can_get_password_settings;
bool pending_changes;
bool can_debug_cmd;
bool opcode_support;
struct tlmi_attr_setting *setting[TLMI_SETTINGS_COUNT];
struct device *class_dev;
struct kset *attribute_kset;
struct kset *authentication_kset;
struct tlmi_pwdcfg pwdcfg;
struct tlmi_pwd_setting *pwd_admin;
struct tlmi_pwd_setting *pwd_power;
struct tlmi_pwd_setting *pwd_system;
struct tlmi_pwd_setting *pwd_hdd;
struct tlmi_pwd_setting *pwd_nvme;
};
#endif /* !_THINK_LMI_H_ */
This diff is collapsed.
......@@ -107,6 +107,9 @@ static const struct property_entry chuwi_hi10_plus_props[] = {
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi10plus.fw"),
PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
PROPERTY_ENTRY_BOOL("silead,pen-supported"),
PROPERTY_ENTRY_U32("silead,pen-resolution-x", 8),
PROPERTY_ENTRY_U32("silead,pen-resolution-y", 8),
{ }
};
......@@ -124,15 +127,21 @@ static const struct ts_dmi_data chuwi_hi10_plus_data = {
.properties = chuwi_hi10_plus_props,
};
static const u32 chuwi_hi10_pro_efi_min_max[] = { 8, 1911, 8, 1271 };
static const struct property_entry chuwi_hi10_pro_props[] = {
PROPERTY_ENTRY_U32("touchscreen-min-x", 8),
PROPERTY_ENTRY_U32("touchscreen-min-y", 8),
PROPERTY_ENTRY_U32("touchscreen-size-x", 1912),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1272),
PROPERTY_ENTRY_U32("touchscreen-min-x", 80),
PROPERTY_ENTRY_U32("touchscreen-min-y", 26),
PROPERTY_ENTRY_U32("touchscreen-size-x", 1962),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1254),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi10-pro.fw"),
PROPERTY_ENTRY_U32_ARRAY("silead,efi-fw-min-max", chuwi_hi10_pro_efi_min_max),
PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
PROPERTY_ENTRY_BOOL("silead,pen-supported"),
PROPERTY_ENTRY_U32("silead,pen-resolution-x", 8),
PROPERTY_ENTRY_U32("silead,pen-resolution-y", 8),
{ }
};
......@@ -352,18 +361,6 @@ static const struct ts_dmi_data gdix1001_01_upside_down_data = {
.properties = gdix1001_upside_down_props,
};
static const struct property_entry glavey_tm800a550l_props[] = {
PROPERTY_ENTRY_STRING("firmware-name", "gt912-glavey-tm800a550l.fw"),
PROPERTY_ENTRY_STRING("goodix,config-name", "gt912-glavey-tm800a550l.cfg"),
PROPERTY_ENTRY_U32("goodix,main-clk", 54),
{ }
};
static const struct ts_dmi_data glavey_tm800a550l_data = {
.acpi_name = "GDIX1001:00",
.properties = glavey_tm800a550l_props,
};
static const struct property_entry gp_electronic_t701_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 960),
PROPERTY_ENTRY_U32("touchscreen-size-y", 640),
......@@ -1140,15 +1137,6 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "eSTAR BEAUTY HD Intel Quad core"),
},
},
{ /* Glavey TM800A550L */
.driver_data = (void *)&glavey_tm800a550l_data,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
/* Above strings are too generic, also match on BIOS version */
DMI_MATCH(DMI_BIOS_VERSION, "ZY-8-BI-PX4S70VTR400-X423B-005-D"),
},
},
{
/* GP-electronic T701 */
.driver_data = (void *)&gp_electronic_t701_data,
......
......@@ -175,6 +175,7 @@ static struct attribute *uv_hub_attrs[] = {
&cnode_attribute.attr,
NULL,
};
ATTRIBUTE_GROUPS(uv_hub);
static void hub_release(struct kobject *kobj)
{
......@@ -205,7 +206,7 @@ static const struct sysfs_ops hub_sysfs_ops = {
static struct kobj_type hub_attr_type = {
.release = hub_release,
.sysfs_ops = &hub_sysfs_ops,
.default_attrs = uv_hub_attrs,
.default_groups = uv_hub_groups,
};
static int uv_hubs_init(void)
......@@ -327,6 +328,7 @@ static struct attribute *uv_port_attrs[] = {
&uv_port_conn_port_attribute.attr,
NULL,
};
ATTRIBUTE_GROUPS(uv_port);
static void uv_port_release(struct kobject *kobj)
{
......@@ -357,7 +359,7 @@ static const struct sysfs_ops uv_port_sysfs_ops = {
static struct kobj_type uv_port_attr_type = {
.release = uv_port_release,
.sysfs_ops = &uv_port_sysfs_ops,
.default_attrs = uv_port_attrs,
.default_groups = uv_port_groups,
};
static int uv_ports_init(void)
......
......@@ -57,6 +57,11 @@ static_assert(sizeof(typeof_member(struct guid_block, guid)) == 16);
static_assert(sizeof(struct guid_block) == 20);
static_assert(__alignof__(struct guid_block) == 1);
enum { /* wmi_block flags */
WMI_READ_TAKES_NO_ARGS,
WMI_PROBED,
};
struct wmi_block {
struct wmi_device dev;
struct list_head list;
......@@ -67,8 +72,7 @@ struct wmi_block {
wmi_notify_handler handler;
void *handler_data;
u64 req_buf_size;
bool read_takes_no_args;
unsigned long flags;
};
......@@ -367,7 +371,7 @@ static acpi_status __query_block(struct wmi_block *wblock, u8 instance,
wq_params[0].type = ACPI_TYPE_INTEGER;
wq_params[0].integer.value = instance;
if (instance == 0 && wblock->read_takes_no_args)
if (instance == 0 && test_bit(WMI_READ_TAKES_NO_ARGS, &wblock->flags))
input.count = 0;
/*
......@@ -1005,6 +1009,7 @@ static int wmi_dev_probe(struct device *dev)
}
}
set_bit(WMI_PROBED, &wblock->flags);
return 0;
probe_misc_failure:
......@@ -1022,6 +1027,8 @@ 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);
if (wdriver->filter_callback) {
misc_deregister(&wblock->char_dev);
kfree(wblock->char_dev.name);
......@@ -1113,7 +1120,7 @@ static int wmi_create_device(struct device *wmi_bus_dev,
* laptops, WQxx may not be a method at all.)
*/
if (info->type != ACPI_TYPE_METHOD || info->param_count == 0)
wblock->read_takes_no_args = true;
set_bit(WMI_READ_TAKES_NO_ARGS, &wblock->flags);
kfree(info);
......@@ -1319,15 +1326,17 @@ static void acpi_wmi_notify_handler(acpi_handle handle, u32 event,
return;
/* If a driver is bound, then notify the driver. */
if (wblock->dev.dev.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;
status = get_event_data(wblock, &evdata);
if (ACPI_FAILURE(status)) {
dev_warn(&wblock->dev.dev, "failed to get event data\n");
return;
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;
}
}
if (driver->notify)
......
This diff is collapsed.
......@@ -134,6 +134,12 @@ static const char * const POWER_SUPPLY_SCOPE_TEXT[] = {
[POWER_SUPPLY_SCOPE_DEVICE] = "Device",
};
static const char * const POWER_SUPPLY_CHARGE_BEHAVIOUR_TEXT[] = {
[POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO] = "auto",
[POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE] = "inhibit-charge",
[POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE] = "force-discharge",
};
static struct power_supply_attr power_supply_attrs[] = {
/* Properties of type `int' */
POWER_SUPPLY_ENUM_ATTR(STATUS),
......@@ -173,6 +179,7 @@ static struct power_supply_attr power_supply_attrs[] = {
POWER_SUPPLY_ATTR(CHARGE_CONTROL_LIMIT_MAX),
POWER_SUPPLY_ATTR(CHARGE_CONTROL_START_THRESHOLD),
POWER_SUPPLY_ATTR(CHARGE_CONTROL_END_THRESHOLD),
POWER_SUPPLY_ENUM_ATTR(CHARGE_BEHAVIOUR),
POWER_SUPPLY_ATTR(INPUT_CURRENT_LIMIT),
POWER_SUPPLY_ATTR(INPUT_VOLTAGE_LIMIT),
POWER_SUPPLY_ATTR(INPUT_POWER_LIMIT),
......@@ -485,3 +492,52 @@ int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env)
return ret;
}
ssize_t power_supply_charge_behaviour_show(struct device *dev,
unsigned int available_behaviours,
enum power_supply_charge_behaviour current_behaviour,
char *buf)
{
bool match = false, available, active;
ssize_t count = 0;
int i;
for (i = 0; i < ARRAY_SIZE(POWER_SUPPLY_CHARGE_BEHAVIOUR_TEXT); i++) {
available = available_behaviours & BIT(i);
active = i == current_behaviour;
if (available && active) {
count += sysfs_emit_at(buf, count, "[%s] ",
POWER_SUPPLY_CHARGE_BEHAVIOUR_TEXT[i]);
match = true;
} else if (available) {
count += sysfs_emit_at(buf, count, "%s ",
POWER_SUPPLY_CHARGE_BEHAVIOUR_TEXT[i]);
}
}
if (!match) {
dev_warn(dev, "driver reporting unsupported charge behaviour\n");
return -EINVAL;
}
if (count)
buf[count - 1] = '\n';
return count;
}
EXPORT_SYMBOL_GPL(power_supply_charge_behaviour_show);
int power_supply_charge_behaviour_parse(unsigned int available_behaviours, const char *buf)
{
int i = sysfs_match_string(POWER_SUPPLY_CHARGE_BEHAVIOUR_TEXT, buf);
if (i < 0)
return i;
if (available_behaviours & BIT(i))
return i;
return -EINVAL;
}
EXPORT_SYMBOL_GPL(power_supply_charge_behaviour_parse);
......@@ -1589,6 +1589,17 @@ config NIC7018_WDT
To compile this driver as a module, choose M here: the module will be
called nic7018_wdt.
config SIEMENS_SIMATIC_IPC_WDT
tristate "Siemens Simatic IPC Watchdog"
depends on SIEMENS_SIMATIC_IPC
select WATCHDOG_CORE
help
This driver adds support for several watchdogs found in Industrial
PCs from Siemens.
To compile this driver as a module, choose M here: the module will be
called simatic-ipc-wdt.
# M68K Architecture
config M54xx_WATCHDOG
......
......@@ -143,6 +143,7 @@ obj-$(CONFIG_NI903X_WDT) += ni903x_wdt.o
obj-$(CONFIG_NIC7018_WDT) += nic7018_wdt.o
obj-$(CONFIG_MLX_WDT) += mlx_wdt.o
obj-$(CONFIG_KEEMBAY_WATCHDOG) += keembay_wdt.o
obj-$(CONFIG_SIEMENS_SIMATIC_IPC_WDT) += simatic-ipc-wdt.o
# M68K Architecture
obj-$(CONFIG_M54xx_WATCHDOG) += m54xx_wdt.o
......
// SPDX-License-Identifier: GPL-2.0
/*
* Siemens SIMATIC IPC driver for Watchdogs
*
* Copyright (c) Siemens AG, 2020-2021
*
* Authors:
* Gerd Haeussler <gerd.haeussler.ext@siemens.com>
*/
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_data/x86/simatic-ipc-base.h>
#include <linux/platform_device.h>
#include <linux/sizes.h>
#include <linux/util_macros.h>
#include <linux/watchdog.h>
#define WD_ENABLE_IOADR 0x62
#define WD_TRIGGER_IOADR 0x66
#define GPIO_COMMUNITY0_PORT_ID 0xaf
#define PAD_CFG_DW0_GPP_A_23 0x4b8
#define SAFE_EN_N_427E 0x01
#define SAFE_EN_N_227E 0x04
#define WD_ENABLED 0x01
#define WD_TRIGGERED 0x80
#define WD_MACROMODE 0x02
#define TIMEOUT_MIN 2
#define TIMEOUT_DEF 64
#define TIMEOUT_MAX 64
#define GP_STATUS_REG_227E 0x404D /* IO PORT for SAFE_EN_N on 227E */
static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0000);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
static struct resource gp_status_reg_227e_res =
DEFINE_RES_IO_NAMED(GP_STATUS_REG_227E, SZ_1, KBUILD_MODNAME);
static struct resource io_resource_enable =
DEFINE_RES_IO_NAMED(WD_ENABLE_IOADR, SZ_1,
KBUILD_MODNAME " WD_ENABLE_IOADR");
static struct resource io_resource_trigger =
DEFINE_RES_IO_NAMED(WD_TRIGGER_IOADR, SZ_1,
KBUILD_MODNAME " WD_TRIGGER_IOADR");
/* the actual start will be discovered with pci, 0 is a placeholder */
static struct resource mem_resource =
DEFINE_RES_MEM_NAMED(0, SZ_4, "WD_RESET_BASE_ADR");
static u32 wd_timeout_table[] = {2, 4, 6, 8, 16, 32, 48, 64 };
static void __iomem *wd_reset_base_addr;
static int wd_start(struct watchdog_device *wdd)
{
outb(inb(WD_ENABLE_IOADR) | WD_ENABLED, WD_ENABLE_IOADR);
return 0;
}
static int wd_stop(struct watchdog_device *wdd)
{
outb(inb(WD_ENABLE_IOADR) & ~WD_ENABLED, WD_ENABLE_IOADR);
return 0;
}
static int wd_ping(struct watchdog_device *wdd)
{
inb(WD_TRIGGER_IOADR);
return 0;
}
static int wd_set_timeout(struct watchdog_device *wdd, unsigned int t)
{
int timeout_idx = find_closest(t, wd_timeout_table,
ARRAY_SIZE(wd_timeout_table));
outb((inb(WD_ENABLE_IOADR) & 0xc7) | timeout_idx << 3, WD_ENABLE_IOADR);
wdd->timeout = wd_timeout_table[timeout_idx];
return 0;
}
static const struct watchdog_info wdt_ident = {
.options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING |
WDIOF_SETTIMEOUT,
.identity = KBUILD_MODNAME,
};
static const struct watchdog_ops wdt_ops = {
.owner = THIS_MODULE,
.start = wd_start,
.stop = wd_stop,
.ping = wd_ping,
.set_timeout = wd_set_timeout,
};
static void wd_secondary_enable(u32 wdtmode)
{
u16 resetbit;
/* set safe_en_n so we are not just WDIOF_ALARMONLY */
if (wdtmode == SIMATIC_IPC_DEVICE_227E) {
/* enable SAFE_EN_N on GP_STATUS_REG_227E */
resetbit = inb(GP_STATUS_REG_227E);
outb(resetbit & ~SAFE_EN_N_227E, GP_STATUS_REG_227E);
} else {
/* enable SAFE_EN_N on PCH D1600 */
resetbit = ioread16(wd_reset_base_addr);
iowrite16(resetbit & ~SAFE_EN_N_427E, wd_reset_base_addr);
}
}
static int wd_setup(u32 wdtmode)
{
unsigned int bootstatus = 0;
int timeout_idx;
timeout_idx = find_closest(TIMEOUT_DEF, wd_timeout_table,
ARRAY_SIZE(wd_timeout_table));
if (inb(WD_ENABLE_IOADR) & WD_TRIGGERED)
bootstatus |= WDIOF_CARDRESET;
/* reset alarm bit, set macro mode, and set timeout */
outb(WD_TRIGGERED | WD_MACROMODE | timeout_idx << 3, WD_ENABLE_IOADR);
wd_secondary_enable(wdtmode);
return bootstatus;
}
static struct watchdog_device wdd_data = {
.info = &wdt_ident,
.ops = &wdt_ops,
.min_timeout = TIMEOUT_MIN,
.max_timeout = TIMEOUT_MAX
};
static int simatic_ipc_wdt_probe(struct platform_device *pdev)
{
struct simatic_ipc_platform *plat = pdev->dev.platform_data;
struct device *dev = &pdev->dev;
struct resource *res;
switch (plat->devmode) {
case SIMATIC_IPC_DEVICE_227E:
if (!devm_request_region(dev, gp_status_reg_227e_res.start,
resource_size(&gp_status_reg_227e_res),
KBUILD_MODNAME)) {
dev_err(dev,
"Unable to register IO resource at %pR\n",
&gp_status_reg_227e_res);
return -EBUSY;
}
fallthrough;
case SIMATIC_IPC_DEVICE_427E:
wdd_data.parent = dev;
break;
default:
return -EINVAL;
}
if (!devm_request_region(dev, io_resource_enable.start,
resource_size(&io_resource_enable),
io_resource_enable.name)) {
dev_err(dev,
"Unable to register IO resource at %#x\n",
WD_ENABLE_IOADR);
return -EBUSY;
}
if (!devm_request_region(dev, io_resource_trigger.start,
resource_size(&io_resource_trigger),
io_resource_trigger.name)) {
dev_err(dev,
"Unable to register IO resource at %#x\n",
WD_TRIGGER_IOADR);
return -EBUSY;
}
if (plat->devmode == SIMATIC_IPC_DEVICE_427E) {
res = &mem_resource;
/* get GPIO base from PCI */
res->start = simatic_ipc_get_membase0(PCI_DEVFN(0x1f, 1));
if (res->start == 0)
return -ENODEV;
/* do the final address calculation */
res->start = res->start + (GPIO_COMMUNITY0_PORT_ID << 16) +
PAD_CFG_DW0_GPP_A_23;
res->end += res->start;
wd_reset_base_addr = devm_ioremap_resource(dev, res);
if (IS_ERR(wd_reset_base_addr))
return PTR_ERR(wd_reset_base_addr);
}
wdd_data.bootstatus = wd_setup(plat->devmode);
if (wdd_data.bootstatus)
dev_warn(dev, "last reboot caused by watchdog reset\n");
watchdog_set_nowayout(&wdd_data, nowayout);
watchdog_stop_on_reboot(&wdd_data);
return devm_watchdog_register_device(dev, &wdd_data);
}
static struct platform_driver simatic_ipc_wdt_driver = {
.probe = simatic_ipc_wdt_probe,
.driver = {
.name = KBUILD_MODNAME,
},
};
module_platform_driver(simatic_ipc_wdt_driver);
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" KBUILD_MODNAME);
MODULE_AUTHOR("Gerd Haeussler <gerd.haeussler.ext@siemens.com>");
......@@ -77,6 +77,8 @@
#define ASUS_WMI_DEVID_THERMAL_CTRL 0x00110011
#define ASUS_WMI_DEVID_FAN_CTRL 0x00110012 /* deprecated */
#define ASUS_WMI_DEVID_CPU_FAN_CTRL 0x00110013
#define ASUS_WMI_DEVID_CPU_FAN_CURVE 0x00110024
#define ASUS_WMI_DEVID_GPU_FAN_CURVE 0x00110025
/* Power */
#define ASUS_WMI_DEVID_PROCESSOR_STATE 0x00120012
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Siemens SIMATIC IPC drivers
*
* Copyright (c) Siemens AG, 2018-2021
*
* Authors:
* Henning Schild <henning.schild@siemens.com>
* Gerd Haeussler <gerd.haeussler.ext@siemens.com>
*/
#ifndef __PLATFORM_DATA_X86_SIMATIC_IPC_BASE_H
#define __PLATFORM_DATA_X86_SIMATIC_IPC_BASE_H
#include <linux/types.h>
#define SIMATIC_IPC_DEVICE_NONE 0
#define SIMATIC_IPC_DEVICE_227D 1
#define SIMATIC_IPC_DEVICE_427E 2
#define SIMATIC_IPC_DEVICE_127E 3
#define SIMATIC_IPC_DEVICE_227E 4
struct simatic_ipc_platform {
u8 devmode;
};
u32 simatic_ipc_get_membase0(unsigned int p2sb);
#endif /* __PLATFORM_DATA_X86_SIMATIC_IPC_BASE_H */
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Siemens SIMATIC IPC drivers
*
* Copyright (c) Siemens AG, 2018-2021
*
* Authors:
* Henning Schild <henning.schild@siemens.com>
* Gerd Haeussler <gerd.haeussler.ext@siemens.com>
*/
#ifndef __PLATFORM_DATA_X86_SIMATIC_IPC_H
#define __PLATFORM_DATA_X86_SIMATIC_IPC_H
#include <linux/dmi.h>
#include <linux/platform_data/x86/simatic-ipc-base.h>
#define SIMATIC_IPC_DMI_ENTRY_OEM 129
/* binary type */
#define SIMATIC_IPC_DMI_TYPE 0xff
#define SIMATIC_IPC_DMI_GROUP 0x05
#define SIMATIC_IPC_DMI_ENTRY 0x02
#define SIMATIC_IPC_DMI_TID 0x02
enum simatic_ipc_station_ids {
SIMATIC_IPC_INVALID_STATION_ID = 0,
SIMATIC_IPC_IPC227D = 0x00000501,
SIMATIC_IPC_IPC427D = 0x00000701,
SIMATIC_IPC_IPC227E = 0x00000901,
SIMATIC_IPC_IPC277E = 0x00000902,
SIMATIC_IPC_IPC427E = 0x00000A01,
SIMATIC_IPC_IPC477E = 0x00000A02,
SIMATIC_IPC_IPC127E = 0x00000D01,
};
static inline u32 simatic_ipc_get_station_id(u8 *data, int max_len)
{
struct {
u8 type; /* type (0xff = binary) */
u8 len; /* len of data entry */
u8 group;
u8 entry;
u8 tid;
__le32 station_id; /* station id (LE) */
} __packed * data_entry = (void *)data + sizeof(struct dmi_header);
while ((u8 *)data_entry < data + max_len) {
if (data_entry->type == SIMATIC_IPC_DMI_TYPE &&
data_entry->len == sizeof(*data_entry) &&
data_entry->group == SIMATIC_IPC_DMI_GROUP &&
data_entry->entry == SIMATIC_IPC_DMI_ENTRY &&
data_entry->tid == SIMATIC_IPC_DMI_TID) {
return le32_to_cpu(data_entry->station_id);
}
data_entry = (void *)((u8 *)(data_entry) + data_entry->len);
}
return SIMATIC_IPC_INVALID_STATION_ID;
}
static inline void
simatic_ipc_find_dmi_entry_helper(const struct dmi_header *dh, void *_data)
{
u32 *id = _data;
if (dh->type != SIMATIC_IPC_DMI_ENTRY_OEM)
return;
*id = simatic_ipc_get_station_id((u8 *)dh, dh->length);
}
#endif /* __PLATFORM_DATA_X86_SIMATIC_IPC_H */
......@@ -133,6 +133,7 @@ enum power_supply_property {
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD, /* in percents! */
POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD, /* in percents! */
POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT,
POWER_SUPPLY_PROP_INPUT_POWER_LIMIT,
......@@ -203,6 +204,12 @@ enum power_supply_usb_type {
POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID, /* Apple Charging Method */
};
enum power_supply_charge_behaviour {
POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO = 0,
POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE,
POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE,
};
enum power_supply_notifier_events {
PSY_EVENT_PROP_CHANGED,
};
......@@ -709,4 +716,28 @@ static inline
void power_supply_remove_hwmon_sysfs(struct power_supply *psy) {}
#endif
#ifdef CONFIG_SYSFS
ssize_t power_supply_charge_behaviour_show(struct device *dev,
unsigned int available_behaviours,
enum power_supply_charge_behaviour behaviour,
char *buf);
int power_supply_charge_behaviour_parse(unsigned int available_behaviours, const char *buf);
#else
static inline
ssize_t power_supply_charge_behaviour_show(struct device *dev,
unsigned int available_behaviours,
enum power_supply_charge_behaviour behaviour,
char *buf)
{
return -EOPNOTSUPP;
}
static inline int power_supply_charge_behaviour_parse(unsigned int available_behaviours,
const char *buf)
{
return -EOPNOTSUPP;
}
#endif
#endif /* __LINUX_POWER_SUPPLY_H__ */
......@@ -319,6 +319,15 @@ void ssam_device_driver_unregister(struct ssam_device_driver *d);
ssam_device_driver_unregister)
/* -- Helpers for controller and hub devices. ------------------------------- */
#ifdef CONFIG_SURFACE_AGGREGATOR_BUS
void ssam_remove_clients(struct device *dev);
#else /* CONFIG_SURFACE_AGGREGATOR_BUS */
static inline void ssam_remove_clients(struct device *dev) {}
#endif /* CONFIG_SURFACE_AGGREGATOR_BUS */
/* -- Helpers for client-device requests. ----------------------------------- */
/**
......
......@@ -35,6 +35,7 @@ extern int set_required_buffer_size(struct wmi_device *wdev, u64 length);
struct wmi_driver {
struct device_driver driver;
const struct wmi_device_id *id_table;
bool no_notify_data;
int (*probe)(struct wmi_device *wdev, const void *context);
void (*remove)(struct wmi_device *wdev);
......
......@@ -15,7 +15,7 @@ struct process_cmd_struct {
int arg;
};
static const char *version_str = "v1.10";
static const char *version_str = "v1.11";
static const int supported_api_ver = 1;
static struct isst_if_platform_info isst_platform_info;
static char *progname;
......@@ -1599,6 +1599,7 @@ static void set_scaling_min_to_cpuinfo_max(int cpu)
die_id != get_physical_die_id(i))
continue;
adjust_scaling_max_from_base_freq(i);
set_cpufreq_scaling_min_max_from_cpuinfo(i, 1, 0);
adjust_scaling_min_from_base_freq(i);
}
......@@ -1615,6 +1616,7 @@ static void set_scaling_min_to_cpuinfo_min(int cpu)
die_id != get_physical_die_id(i))
continue;
adjust_scaling_max_from_base_freq(i);
set_cpufreq_scaling_min_max_from_cpuinfo(i, 0, 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