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: ...@@ -161,6 +161,15 @@ Description:
power-on: power-on:
Representing a password required to use Representing a password required to use
the system 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: mechanism:
The means of authentication. This attribute is mandatory. The means of authentication. This attribute is mandatory.
...@@ -207,6 +216,13 @@ Description: ...@@ -207,6 +216,13 @@ Description:
On Lenovo systems the following additional settings are available: 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: lenovo_encoding:
The encoding method that is used. This can be either "ascii" The encoding method that is used. This can be either "ascii"
or "scancode". Default is set to "ascii" or "scancode". Default is set to "ascii"
...@@ -216,6 +232,22 @@ Description: ...@@ -216,6 +232,22 @@ Description:
two char code (e.g. "us", "fr", "gr") and may vary per platform. two char code (e.g. "us", "fr", "gr") and may vary per platform.
Default is set to "us" 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 What: /sys/class/firmware-attributes/*/attributes/pending_reboot
Date: February 2021 Date: February 2021
KernelVersion: 5.11 KernelVersion: 5.11
......
...@@ -455,6 +455,20 @@ Description: ...@@ -455,6 +455,20 @@ Description:
"Unknown", "Charging", "Discharging", "Unknown", "Charging", "Discharging",
"Not charging", "Full" "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 What: /sys/class/power_supply/<supply_name>/technology
Date: May 2007 Date: May 2007
Contact: linux-pm@vger.kernel.org Contact: linux-pm@vger.kernel.org
......
...@@ -3013,6 +3013,13 @@ W: http://acpi4asus.sf.net ...@@ -3013,6 +3013,13 @@ W: http://acpi4asus.sf.net
F: drivers/platform/x86/asus*.c F: drivers/platform/x86/asus*.c
F: drivers/platform/x86/eeepc*.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 ASUS WMI HARDWARE MONITOR DRIVER
M: Ed Brindley <kernel@maidavale.org> M: Ed Brindley <kernel@maidavale.org>
M: Denis Pauk <pauk.denis@gmail.com> M: Denis Pauk <pauk.denis@gmail.com>
...@@ -20836,6 +20843,13 @@ S: Maintained ...@@ -20836,6 +20843,13 @@ S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git x86/mm T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git x86/mm
F: arch/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 X86 PLATFORM DRIVERS
M: Hans de Goede <hdegoede@redhat.com> M: Hans de Goede <hdegoede@redhat.com>
M: Mark Gross <markgross@kernel.org> M: Mark Gross <markgross@kernel.org>
......
...@@ -879,4 +879,7 @@ source "drivers/leds/flash/Kconfig" ...@@ -879,4 +879,7 @@ source "drivers/leds/flash/Kconfig"
comment "LED Triggers" comment "LED Triggers"
source "drivers/leds/trigger/Kconfig" source "drivers/leds/trigger/Kconfig"
comment "Simple LED drivers"
source "drivers/leds/simple/Kconfig"
endif # NEW_LEDS endif # NEW_LEDS
...@@ -105,3 +105,6 @@ obj-$(CONFIG_LEDS_TRIGGERS) += trigger/ ...@@ -105,3 +105,6 @@ obj-$(CONFIG_LEDS_TRIGGERS) += trigger/
# LED Blink # LED Blink
obj-y += 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 @@ ...@@ -5,7 +5,6 @@
menuconfig SURFACE_PLATFORMS menuconfig SURFACE_PLATFORMS
bool "Microsoft Surface Platform-Specific Device Drivers" bool "Microsoft Surface Platform-Specific Device Drivers"
depends on ACPI
default y default y
help help
Say Y here to get to see options for platform-specific device drivers Say Y here to get to see options for platform-specific device drivers
...@@ -30,12 +29,14 @@ config SURFACE3_WMI ...@@ -30,12 +29,14 @@ config SURFACE3_WMI
config SURFACE_3_BUTTON config SURFACE_3_BUTTON
tristate "Power/home/volume buttons driver for Microsoft Surface 3 tablet" tristate "Power/home/volume buttons driver for Microsoft Surface 3 tablet"
depends on ACPI
depends on KEYBOARD_GPIO && I2C depends on KEYBOARD_GPIO && I2C
help help
This driver handles the power/home/volume buttons on the Microsoft Surface 3 tablet. This driver handles the power/home/volume buttons on the Microsoft Surface 3 tablet.
config SURFACE_3_POWER_OPREGION config SURFACE_3_POWER_OPREGION
tristate "Surface 3 battery platform operation region support" tristate "Surface 3 battery platform operation region support"
depends on ACPI
depends on I2C depends on I2C
help help
This driver provides support for ACPI operation This driver provides support for ACPI operation
...@@ -126,6 +127,7 @@ config SURFACE_DTX ...@@ -126,6 +127,7 @@ config SURFACE_DTX
config SURFACE_GPE config SURFACE_GPE
tristate "Surface GPE/Lid Support Driver" tristate "Surface GPE/Lid Support Driver"
depends on ACPI
depends on DMI depends on DMI
help help
This driver marks the GPEs related to the ACPI lid device found on This driver marks the GPEs related to the ACPI lid device found on
...@@ -135,6 +137,7 @@ config SURFACE_GPE ...@@ -135,6 +137,7 @@ config SURFACE_GPE
config SURFACE_HOTPLUG config SURFACE_HOTPLUG
tristate "Surface Hot-Plug Driver" tristate "Surface Hot-Plug Driver"
depends on ACPI
depends on GPIOLIB depends on GPIOLIB
help help
Driver for out-of-band hot-plug event signaling on Microsoft Surface Driver for out-of-band hot-plug event signaling on Microsoft Surface
...@@ -154,6 +157,7 @@ config SURFACE_HOTPLUG ...@@ -154,6 +157,7 @@ config SURFACE_HOTPLUG
config SURFACE_PLATFORM_PROFILE config SURFACE_PLATFORM_PROFILE
tristate "Surface Platform Profile Driver" tristate "Surface Platform Profile Driver"
depends on ACPI
depends on SURFACE_AGGREGATOR_REGISTRY depends on SURFACE_AGGREGATOR_REGISTRY
select ACPI_PLATFORM_PROFILE select ACPI_PLATFORM_PROFILE
help help
...@@ -176,6 +180,7 @@ config SURFACE_PLATFORM_PROFILE ...@@ -176,6 +180,7 @@ config SURFACE_PLATFORM_PROFILE
config SURFACE_PRO3_BUTTON config SURFACE_PRO3_BUTTON
tristate "Power/home/volume buttons driver for Microsoft Surface Pro 3/4 tablet" tristate "Power/home/volume buttons driver for Microsoft Surface Pro 3/4 tablet"
depends on ACPI
depends on INPUT depends on INPUT
help help
This driver handles the power/home/volume buttons on the Microsoft Surface Pro 3/4 tablet. This driver handles the power/home/volume buttons on the Microsoft Surface Pro 3/4 tablet.
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
menuconfig SURFACE_AGGREGATOR menuconfig SURFACE_AGGREGATOR
tristate "Microsoft Surface System Aggregator Module Subsystem and Drivers" tristate "Microsoft Surface System Aggregator Module Subsystem and Drivers"
depends on SERIAL_DEV_BUS depends on SERIAL_DEV_BUS
depends on ACPI
select CRC_CCITT select CRC_CCITT
help help
The Surface System Aggregator Module (Surface SAM or SSAM) is an 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) ...@@ -374,27 +374,19 @@ static int ssam_remove_device(struct device *dev, void *_data)
} }
/** /**
* ssam_controller_remove_clients() - Remove SSAM client devices registered as * ssam_remove_clients() - Remove SSAM client devices registered as direct
* direct children under the given controller. * children under the given parent device.
* @ctrl: The controller to remove all direct clients for. * @dev: The (parent) device to remove all direct clients for.
* *
* Remove all SSAM client devices registered as direct children under the * Remove all SSAM client devices registered as direct children under the given
* given controller. Note that this only accounts for direct children of the * device. Note that this only accounts for direct children of the device.
* controller device. This does not take care of any client devices where the * Refer to ssam_device_add()/ssam_device_remove() for more details.
* 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.
*/ */
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); 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. * ssam_bus_register() - Register and set-up the SSAM client device bus.
......
...@@ -12,14 +12,11 @@ ...@@ -12,14 +12,11 @@
#ifdef CONFIG_SURFACE_AGGREGATOR_BUS #ifdef CONFIG_SURFACE_AGGREGATOR_BUS
void ssam_controller_remove_clients(struct ssam_controller *ctrl);
int ssam_bus_register(void); int ssam_bus_register(void);
void ssam_bus_unregister(void); void ssam_bus_unregister(void);
#else /* CONFIG_SURFACE_AGGREGATOR_BUS */ #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 int ssam_bus_register(void) { return 0; }
static inline void ssam_bus_unregister(void) {} static inline void ssam_bus_unregister(void) {}
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/sysfs.h> #include <linux/sysfs.h>
#include <linux/surface_aggregator/controller.h> #include <linux/surface_aggregator/controller.h>
#include <linux/surface_aggregator/device.h>
#include "bus.h" #include "bus.h"
#include "controller.h" #include "controller.h"
...@@ -735,7 +736,7 @@ static void ssam_serial_hub_remove(struct serdev_device *serdev) ...@@ -735,7 +736,7 @@ static void ssam_serial_hub_remove(struct serdev_device *serdev)
ssam_controller_lock(ctrl); ssam_controller_lock(ctrl);
/* Remove all client devices. */ /* Remove all client devices. */
ssam_controller_remove_clients(ctrl); ssam_remove_clients(&serdev->dev);
/* Act as if suspending to silence events. */ /* Act as if suspending to silence events. */
status = ssam_ctrl_notif_display_off(ctrl); 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) ...@@ -258,20 +258,6 @@ static int ssam_uid_from_string(const char *str, struct ssam_device_uid *uid)
return 0; 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, static int ssam_hub_add_device(struct device *parent, struct ssam_controller *ctrl,
struct fwnode_handle *node) struct fwnode_handle *node)
{ {
...@@ -297,8 +283,8 @@ static int ssam_hub_add_device(struct device *parent, struct ssam_controller *ct ...@@ -297,8 +283,8 @@ static int ssam_hub_add_device(struct device *parent, struct ssam_controller *ct
return status; return status;
} }
static int ssam_hub_add_devices(struct device *parent, struct ssam_controller *ctrl, static int ssam_hub_register_clients(struct device *parent, struct ssam_controller *ctrl,
struct fwnode_handle *node) struct fwnode_handle *node)
{ {
struct fwnode_handle *child; struct fwnode_handle *child;
int status; int status;
...@@ -317,7 +303,7 @@ static int ssam_hub_add_devices(struct device *parent, struct ssam_controller *c ...@@ -317,7 +303,7 @@ static int ssam_hub_add_devices(struct device *parent, struct ssam_controller *c
return 0; return 0;
err: err:
ssam_hub_remove_devices(parent); ssam_remove_clients(parent);
return status; return status;
} }
...@@ -412,9 +398,9 @@ static void ssam_base_hub_update_workfn(struct work_struct *work) ...@@ -412,9 +398,9 @@ static void ssam_base_hub_update_workfn(struct work_struct *work)
hub->state = state; hub->state = state;
if (hub->state == SSAM_BASE_HUB_CONNECTED) 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 else
ssam_hub_remove_devices(&hub->sdev->dev); ssam_remove_clients(&hub->sdev->dev);
if (status) if (status)
dev_err(&hub->sdev->dev, "failed to update base-hub devices: %d\n", 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) ...@@ -496,7 +482,7 @@ static int ssam_base_hub_probe(struct ssam_device *sdev)
err: err:
ssam_notifier_unregister(sdev->ctrl, &hub->notif); ssam_notifier_unregister(sdev->ctrl, &hub->notif);
cancel_delayed_work_sync(&hub->update_work); cancel_delayed_work_sync(&hub->update_work);
ssam_hub_remove_devices(&sdev->dev); ssam_remove_clients(&sdev->dev);
return status; return status;
} }
...@@ -508,7 +494,7 @@ static void ssam_base_hub_remove(struct ssam_device *sdev) ...@@ -508,7 +494,7 @@ static void ssam_base_hub_remove(struct ssam_device *sdev)
ssam_notifier_unregister(sdev->ctrl, &hub->notif); ssam_notifier_unregister(sdev->ctrl, &hub->notif);
cancel_delayed_work_sync(&hub->update_work); 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[] = { static const struct ssam_device_id ssam_base_hub_match[] = {
...@@ -611,7 +597,7 @@ static int ssam_platform_hub_probe(struct platform_device *pdev) ...@@ -611,7 +597,7 @@ static int ssam_platform_hub_probe(struct platform_device *pdev)
set_secondary_fwnode(&pdev->dev, root); 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) { if (status) {
set_secondary_fwnode(&pdev->dev, NULL); set_secondary_fwnode(&pdev->dev, NULL);
software_node_unregister_node_group(nodes); software_node_unregister_node_group(nodes);
...@@ -625,7 +611,7 @@ static int ssam_platform_hub_remove(struct platform_device *pdev) ...@@ -625,7 +611,7 @@ static int ssam_platform_hub_remove(struct platform_device *pdev)
{ {
const struct software_node **nodes = platform_get_drvdata(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); set_secondary_fwnode(&pdev->dev, NULL);
software_node_unregister_node_group(nodes); software_node_unregister_node_group(nodes);
return 0; return 0;
......
...@@ -127,6 +127,19 @@ config GIGABYTE_WMI ...@@ -127,6 +127,19 @@ config GIGABYTE_WMI
To compile this driver as a module, choose M here: the module will To compile this driver as a module, choose M here: the module will
be called gigabyte-wmi. 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 config ACERHDF
tristate "Acer Aspire One temperature and fan driver" tristate "Acer Aspire One temperature and fan driver"
depends on ACPI && THERMAL depends on ACPI && THERMAL
...@@ -296,6 +309,25 @@ config ASUS_NB_WMI ...@@ -296,6 +309,25 @@ config ASUS_NB_WMI
If you have an ACPI-WMI compatible Asus Notebook, say Y or M If you have an ACPI-WMI compatible Asus Notebook, say Y or M
here. 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 config MERAKI_MX100
tristate "Cisco Meraki MX100 Platform Driver" tristate "Cisco Meraki MX100 Platform Driver"
depends on GPIOLIB depends on GPIOLIB
...@@ -993,6 +1025,23 @@ config TOUCHSCREEN_DMI ...@@ -993,6 +1025,23 @@ config TOUCHSCREEN_DMI
the OS-image for the device. This option supplies the missing info. the OS-image for the device. This option supplies the missing info.
Enable this for x86 tablets with Silead or Chipone touchscreens. 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 config FW_ATTR_CLASS
tristate tristate
...@@ -1077,6 +1126,18 @@ config INTEL_SCU_IPC_UTIL ...@@ -1077,6 +1126,18 @@ config INTEL_SCU_IPC_UTIL
low level access for debug work and updating the firmware. Say low level access for debug work and updating the firmware. Say
N unless you will be doing this on an Intel MID platform. 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 endif # X86_PLATFORM_DEVICES
config PMC_ATOM config PMC_ATOM
......
...@@ -15,6 +15,7 @@ obj-$(CONFIG_NVIDIA_WMI_EC_BACKLIGHT) += nvidia-wmi-ec-backlight.o ...@@ -15,6 +15,7 @@ obj-$(CONFIG_NVIDIA_WMI_EC_BACKLIGHT) += nvidia-wmi-ec-backlight.o
obj-$(CONFIG_PEAQ_WMI) += peaq-wmi.o obj-$(CONFIG_PEAQ_WMI) += peaq-wmi.o
obj-$(CONFIG_XIAOMI_WMI) += xiaomi-wmi.o obj-$(CONFIG_XIAOMI_WMI) += xiaomi-wmi.o
obj-$(CONFIG_GIGABYTE_WMI) += gigabyte-wmi.o obj-$(CONFIG_GIGABYTE_WMI) += gigabyte-wmi.o
obj-$(CONFIG_YOGABOOK_WMI) += lenovo-yogabook-wmi.o
# Acer # Acer
obj-$(CONFIG_ACERHDF) += acerhdf.o obj-$(CONFIG_ACERHDF) += acerhdf.o
...@@ -35,6 +36,7 @@ obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o ...@@ -35,6 +36,7 @@ obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o
obj-$(CONFIG_ASUS_WIRELESS) += asus-wireless.o obj-$(CONFIG_ASUS_WIRELESS) += asus-wireless.o
obj-$(CONFIG_ASUS_WMI) += asus-wmi.o obj-$(CONFIG_ASUS_WMI) += asus-wmi.o
obj-$(CONFIG_ASUS_NB_WMI) += asus-nb-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_LAPTOP) += eeepc-laptop.o
obj-$(CONFIG_EEEPC_WMI) += eeepc-wmi.o obj-$(CONFIG_EEEPC_WMI) += eeepc-wmi.o
...@@ -112,6 +114,7 @@ obj-$(CONFIG_I2C_MULTI_INSTANTIATE) += i2c-multi-instantiate.o ...@@ -112,6 +114,7 @@ obj-$(CONFIG_I2C_MULTI_INSTANTIATE) += i2c-multi-instantiate.o
obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o
obj-$(CONFIG_TOUCHSCREEN_DMI) += touchscreen_dmi.o obj-$(CONFIG_TOUCHSCREEN_DMI) += touchscreen_dmi.o
obj-$(CONFIG_WIRELESS_HOTKEY) += wireless-hotkey.o obj-$(CONFIG_WIRELESS_HOTKEY) += wireless-hotkey.o
obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets.o
# Intel uncore drivers # Intel uncore drivers
obj-$(CONFIG_INTEL_IPS) += intel_ips.o obj-$(CONFIG_INTEL_IPS) += intel_ips.o
...@@ -123,3 +126,6 @@ obj-$(CONFIG_INTEL_SCU_PLATFORM) += intel_scu_pltdrv.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_WDT) += intel_scu_wdt.o
obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o
obj-$(CONFIG_PMC_ATOM) += pmc_atom.o obj-$(CONFIG_PMC_ATOM) += pmc_atom.o
# Siemens Simatic Industrial PCs
obj-$(CONFIG_SIEMENS_SIMATIC_IPC) += simatic-ipc.o
...@@ -35,6 +35,12 @@ ...@@ -35,6 +35,12 @@
#define AMD_PMC_SCRATCH_REG_CZN 0x94 #define AMD_PMC_SCRATCH_REG_CZN 0x94
#define AMD_PMC_SCRATCH_REG_YC 0xD14 #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 */ /* Base address of SMU for mapping physical address to virtual address */
#define AMD_PMC_SMU_INDEX_ADDRESS 0xB8 #define AMD_PMC_SMU_INDEX_ADDRESS 0xB8
#define AMD_PMC_SMU_INDEX_DATA 0xBC #define AMD_PMC_SMU_INDEX_DATA 0xBC
...@@ -82,6 +88,7 @@ ...@@ -82,6 +88,7 @@
#define SOC_SUBSYSTEM_IP_MAX 12 #define SOC_SUBSYSTEM_IP_MAX 12
#define DELAY_MIN_US 2000 #define DELAY_MIN_US 2000
#define DELAY_MAX_US 3000 #define DELAY_MAX_US 3000
#define FIFO_SIZE 4096
enum amd_pmc_def { enum amd_pmc_def {
MSG_TEST = 0x01, MSG_TEST = 0x01,
MSG_OS_HINT_PCO, MSG_OS_HINT_PCO,
...@@ -121,14 +128,21 @@ struct amd_pmc_dev { ...@@ -121,14 +128,21 @@ struct amd_pmc_dev {
u16 minor; u16 minor;
u16 rev; u16 rev;
struct device *dev; struct device *dev;
struct pci_dev *rdev;
struct mutex lock; /* generic mutex lock */ struct mutex lock; /* generic mutex lock */
#if IS_ENABLED(CONFIG_DEBUG_FS) #if IS_ENABLED(CONFIG_DEBUG_FS)
struct dentry *dbgfs_dir; struct dentry *dbgfs_dir;
#endif /* CONFIG_DEBUG_FS */ #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 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_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) 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) ...@@ -175,6 +189,50 @@ static int amd_pmc_get_smu_version(struct amd_pmc_dev *dev)
return 0; 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, static int amd_pmc_idlemask_read(struct amd_pmc_dev *pdev, struct device *dev,
struct seq_file *s) struct seq_file *s)
{ {
...@@ -288,6 +346,10 @@ static void amd_pmc_dbgfs_register(struct amd_pmc_dev *dev) ...@@ -288,6 +346,10 @@ static void amd_pmc_dbgfs_register(struct amd_pmc_dev *dev)
&s0ix_stats_fops); &s0ix_stats_fops);
debugfs_create_file("amd_pmc_idlemask", 0644, dev->dbgfs_dir, dev, debugfs_create_file("amd_pmc_idlemask", 0644, dev->dbgfs_dir, dev,
&amd_pmc_idlemask_fops); &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 #else
static inline void amd_pmc_dbgfs_register(struct amd_pmc_dev *dev) 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) ...@@ -484,6 +546,13 @@ static int __maybe_unused amd_pmc_suspend(struct device *dev)
if (rc) if (rc)
dev_err(pdev->dev, "suspend failed\n"); 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; return rc;
} }
...@@ -504,6 +573,14 @@ static int __maybe_unused amd_pmc_resume(struct device *dev) ...@@ -504,6 +573,14 @@ static int __maybe_unused amd_pmc_resume(struct device *dev)
/* Dump the IdleMask to see the blockers */ /* Dump the IdleMask to see the blockers */
amd_pmc_idlemask_read(pdev, dev, NULL); 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; return 0;
} }
...@@ -521,6 +598,50 @@ static const struct pci_device_id pmc_pci_ids[] = { ...@@ -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) static int amd_pmc_probe(struct platform_device *pdev)
{ {
struct amd_pmc_dev *dev = &pmc; struct amd_pmc_dev *dev = &pmc;
...@@ -534,22 +655,23 @@ static int amd_pmc_probe(struct platform_device *pdev) ...@@ -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)); rdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0, 0));
if (!rdev || !pci_match_id(pmc_pci_ids, rdev)) { if (!rdev || !pci_match_id(pmc_pci_ids, rdev)) {
pci_dev_put(rdev); err = -ENODEV;
return -ENODEV; goto err_pci_dev_put;
} }
dev->cpu_id = rdev->device; dev->cpu_id = rdev->device;
dev->rdev = rdev;
err = pci_write_config_dword(rdev, AMD_PMC_SMU_INDEX_ADDRESS, AMD_PMC_BASE_ADDR_LO); err = pci_write_config_dword(rdev, AMD_PMC_SMU_INDEX_ADDRESS, AMD_PMC_BASE_ADDR_LO);
if (err) { if (err) {
dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMC_SMU_INDEX_ADDRESS); dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMC_SMU_INDEX_ADDRESS);
pci_dev_put(rdev); err = pcibios_err_to_errno(err);
return pcibios_err_to_errno(err); goto err_pci_dev_put;
} }
err = pci_read_config_dword(rdev, AMD_PMC_SMU_INDEX_DATA, &val); err = pci_read_config_dword(rdev, AMD_PMC_SMU_INDEX_DATA, &val);
if (err) { if (err) {
pci_dev_put(rdev); err = pcibios_err_to_errno(err);
return pcibios_err_to_errno(err); goto err_pci_dev_put;
} }
base_addr_lo = val & AMD_PMC_BASE_ADDR_HI_MASK; base_addr_lo = val & AMD_PMC_BASE_ADDR_HI_MASK;
...@@ -557,24 +679,25 @@ static int amd_pmc_probe(struct platform_device *pdev) ...@@ -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); err = pci_write_config_dword(rdev, AMD_PMC_SMU_INDEX_ADDRESS, AMD_PMC_BASE_ADDR_HI);
if (err) { if (err) {
dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMC_SMU_INDEX_ADDRESS); dev_err(dev->dev, "error writing to 0x%x\n", AMD_PMC_SMU_INDEX_ADDRESS);
pci_dev_put(rdev); err = pcibios_err_to_errno(err);
return pcibios_err_to_errno(err); goto err_pci_dev_put;
} }
err = pci_read_config_dword(rdev, AMD_PMC_SMU_INDEX_DATA, &val); err = pci_read_config_dword(rdev, AMD_PMC_SMU_INDEX_DATA, &val);
if (err) { if (err) {
pci_dev_put(rdev); err = pcibios_err_to_errno(err);
return pcibios_err_to_errno(err); goto err_pci_dev_put;
} }
base_addr_hi = val & AMD_PMC_BASE_ADDR_LO_MASK; base_addr_hi = val & AMD_PMC_BASE_ADDR_LO_MASK;
pci_dev_put(rdev);
base_addr = ((u64)base_addr_hi << 32 | base_addr_lo); base_addr = ((u64)base_addr_hi << 32 | base_addr_lo);
dev->regbase = devm_ioremap(dev->dev, base_addr + AMD_PMC_BASE_ADDR_OFFSET, dev->regbase = devm_ioremap(dev->dev, base_addr + AMD_PMC_BASE_ADDR_OFFSET,
AMD_PMC_MAPPING_SIZE); AMD_PMC_MAPPING_SIZE);
if (!dev->regbase) if (!dev->regbase) {
return -ENOMEM; err = -ENOMEM;
goto err_pci_dev_put;
}
mutex_init(&dev->lock); mutex_init(&dev->lock);
...@@ -583,8 +706,10 @@ static int amd_pmc_probe(struct platform_device *pdev) ...@@ -583,8 +706,10 @@ static int amd_pmc_probe(struct platform_device *pdev)
base_addr_hi = FCH_BASE_PHY_ADDR_HIGH; base_addr_hi = FCH_BASE_PHY_ADDR_HIGH;
fch_phys_addr = ((u64)base_addr_hi << 32 | base_addr_lo); 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); dev->fch_virt_addr = devm_ioremap(dev->dev, fch_phys_addr, FCH_SSC_MAPPING_SIZE);
if (!dev->fch_virt_addr) if (!dev->fch_virt_addr) {
return -ENOMEM; err = -ENOMEM;
goto err_pci_dev_put;
}
/* Use SMU to get the s0i3 debug stats */ /* Use SMU to get the s0i3 debug stats */
err = amd_pmc_setup_smu_logging(dev); err = amd_pmc_setup_smu_logging(dev);
...@@ -595,6 +720,10 @@ static int amd_pmc_probe(struct platform_device *pdev) ...@@ -595,6 +720,10 @@ static int amd_pmc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dev); platform_set_drvdata(pdev, dev);
amd_pmc_dbgfs_register(dev); amd_pmc_dbgfs_register(dev);
return 0; return 0;
err_pci_dev_put:
pci_dev_put(rdev);
return err;
} }
static int amd_pmc_remove(struct platform_device *pdev) static int amd_pmc_remove(struct platform_device *pdev)
...@@ -602,6 +731,7 @@ 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); struct amd_pmc_dev *dev = platform_get_drvdata(pdev);
amd_pmc_dbgfs_unregister(dev); amd_pmc_dbgfs_unregister(dev);
pci_dev_put(dev->rdev);
mutex_destroy(&dev->lock); mutex_destroy(&dev->lock);
return 0; return 0;
} }
......
This diff is collapsed.
This diff is collapsed.
...@@ -355,39 +355,20 @@ static int lis3lv02d_remove(struct platform_device *device) ...@@ -355,39 +355,20 @@ static int lis3lv02d_remove(struct platform_device *device)
return 0; return 0;
} }
#ifdef CONFIG_PM_SLEEP static int __maybe_unused lis3lv02d_suspend(struct device *dev)
static int lis3lv02d_suspend(struct device *dev)
{ {
/* make sure the device is off when we suspend */ /* make sure the device is off when we suspend */
lis3lv02d_poweroff(&lis3_dev); lis3lv02d_poweroff(&lis3_dev);
return 0; return 0;
} }
static int lis3lv02d_resume(struct device *dev) static int __maybe_unused lis3lv02d_resume(struct device *dev)
{ {
lis3lv02d_poweron(&lis3_dev); lis3lv02d_poweron(&lis3_dev);
return 0; return 0;
} }
static int lis3lv02d_restore(struct device *dev) static SIMPLE_DEV_PM_OPS(hp_accel_pm, lis3lv02d_suspend, lis3lv02d_resume);
{
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
/* For the HP MDPS aka 3D Driveguard */ /* For the HP MDPS aka 3D Driveguard */
static struct platform_driver lis3lv02d_driver = { static struct platform_driver lis3lv02d_driver = {
...@@ -395,7 +376,7 @@ static struct platform_driver lis3lv02d_driver = { ...@@ -395,7 +376,7 @@ static struct platform_driver lis3lv02d_driver = {
.remove = lis3lv02d_remove, .remove = lis3lv02d_remove,
.driver = { .driver = {
.name = "hp_accel", .name = "hp_accel",
.pm = HP_ACCEL_PM, .pm = &hp_accel_pm,
.acpi_match_table = lis3lv02d_device_ids, .acpi_match_table = lis3lv02d_device_ids,
}, },
}; };
......
...@@ -30,6 +30,8 @@ obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o ...@@ -30,6 +30,8 @@ obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o
# Intel PMIC / PMC / P-Unit drivers # Intel PMIC / PMC / P-Unit drivers
intel_bxtwc_tmu-y := bxtwc_tmu.o intel_bxtwc_tmu-y := bxtwc_tmu.o
obj-$(CONFIG_INTEL_BXTWC_PMIC_TMU) += intel_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 intel_chtdc_ti_pwrbtn-y := chtdc_ti_pwrbtn.o
obj-$(CONFIG_INTEL_CHTDC_TI_PWRBTN) += intel_chtdc_ti_pwrbtn.o obj-$(CONFIG_INTEL_CHTDC_TI_PWRBTN) += intel_chtdc_ti_pwrbtn.o
intel_mrfld_pwrbtn-y := mrfld_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 = ...@@ -110,6 +110,12 @@ static const struct int3472_tps68470_board_data surface_go_tps68470_board_data =
.tps68470_regulator_pdata = &surface_go_tps68470_pdata, .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[] = { static const struct dmi_system_id int3472_tps68470_board_data_table[] = {
{ {
.matches = { .matches = {
...@@ -125,6 +131,13 @@ static const struct dmi_system_id int3472_tps68470_board_data_table[] = { ...@@ -125,6 +131,13 @@ static const struct dmi_system_id int3472_tps68470_board_data_table[] = {
}, },
.driver_data = (void *)&surface_go_tps68470_board_data, .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[] = { ...@@ -225,6 +225,7 @@ static struct attribute *uncore_attrs[] = {
&min_freq_khz.attr, &min_freq_khz.attr,
NULL NULL
}; };
ATTRIBUTE_GROUPS(uncore);
static void uncore_sysfs_entry_release(struct kobject *kobj) static void uncore_sysfs_entry_release(struct kobject *kobj)
{ {
...@@ -236,7 +237,7 @@ 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 = { static struct kobj_type uncore_ktype = {
.release = uncore_sysfs_entry_release, .release = uncore_sysfs_entry_release,
.sysfs_ops = &kobj_sysfs_ops, .sysfs_ops = &kobj_sysfs_ops,
.default_attrs = uncore_attrs, .default_groups = uncore_groups,
}; };
/* Caller provides protection */ /* Caller provides protection */
......
This diff is collapsed.
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/platform_data/x86/clk-pmc-atom.h> #include <linux/platform_data/x86/clk-pmc-atom.h>
#include <linux/platform_data/x86/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/platform_device.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
...@@ -362,6 +363,30 @@ static void pmc_dbgfs_register(struct pmc_dev *pmc) ...@@ -362,6 +363,30 @@ static void pmc_dbgfs_register(struct pmc_dev *pmc)
} }
#endif /* CONFIG_DEBUG_FS */ #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 * Some systems need one or more of their pmc_plt_clks to be
* marked as critical. * marked as critical.
...@@ -370,6 +395,7 @@ static const struct dmi_system_id critclk_systems[] = { ...@@ -370,6 +395,7 @@ static const struct dmi_system_id critclk_systems[] = {
{ {
/* pmc_plt_clk0 is used for an external HSIC USB HUB */ /* pmc_plt_clk0 is used for an external HSIC USB HUB */
.ident = "MPL CEC1x", .ident = "MPL CEC1x",
.callback = dmi_callback,
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "MPL AG"), DMI_MATCH(DMI_SYS_VENDOR, "MPL AG"),
DMI_MATCH(DMI_PRODUCT_NAME, "CEC10 Family"), DMI_MATCH(DMI_PRODUCT_NAME, "CEC10 Family"),
...@@ -378,6 +404,7 @@ static const struct dmi_system_id critclk_systems[] = { ...@@ -378,6 +404,7 @@ static const struct dmi_system_id critclk_systems[] = {
{ {
/* pmc_plt_clk0 - 3 are used for the 4 ethernet controllers */ /* pmc_plt_clk0 - 3 are used for the 4 ethernet controllers */
.ident = "Lex 3I380D", .ident = "Lex 3I380D",
.callback = dmi_callback,
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Lex BayTrail"), DMI_MATCH(DMI_SYS_VENDOR, "Lex BayTrail"),
DMI_MATCH(DMI_PRODUCT_NAME, "3I380D"), DMI_MATCH(DMI_PRODUCT_NAME, "3I380D"),
...@@ -386,6 +413,7 @@ static const struct dmi_system_id critclk_systems[] = { ...@@ -386,6 +413,7 @@ static const struct dmi_system_id critclk_systems[] = {
{ {
/* pmc_plt_clk* - are used for ethernet controllers */ /* pmc_plt_clk* - are used for ethernet controllers */
.ident = "Lex 2I385SW", .ident = "Lex 2I385SW",
.callback = dmi_callback,
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Lex BayTrail"), DMI_MATCH(DMI_SYS_VENDOR, "Lex BayTrail"),
DMI_MATCH(DMI_PRODUCT_NAME, "2I385SW"), DMI_MATCH(DMI_PRODUCT_NAME, "2I385SW"),
...@@ -394,30 +422,17 @@ static const struct dmi_system_id critclk_systems[] = { ...@@ -394,30 +422,17 @@ static const struct dmi_system_id critclk_systems[] = {
{ {
/* pmc_plt_clk* - are used for ethernet controllers */ /* pmc_plt_clk* - are used for ethernet controllers */
.ident = "Beckhoff Baytrail", .ident = "Beckhoff Baytrail",
.callback = dmi_callback,
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Beckhoff Automation"), DMI_MATCH(DMI_SYS_VENDOR, "Beckhoff Automation"),
DMI_MATCH(DMI_PRODUCT_FAMILY, "CBxx63"), DMI_MATCH(DMI_PRODUCT_FAMILY, "CBxx63"),
}, },
}, },
{ {
.ident = "SIMATIC IPC227E", .ident = "SIEMENS AG",
.callback = dmi_callback_siemens,
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "SIEMENS AG"), 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, ...@@ -429,7 +444,6 @@ static int pmc_setup_clks(struct pci_dev *pdev, void __iomem *pmc_regmap,
{ {
struct platform_device *clkdev; struct platform_device *clkdev;
struct pmc_clk_data *clk_data; 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); clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
if (!clk_data) if (!clk_data)
...@@ -437,10 +451,8 @@ static int pmc_setup_clks(struct pci_dev *pdev, void __iomem *pmc_regmap, ...@@ -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->base = pmc_regmap; /* offset is added by client */
clk_data->clks = pmc_data->clks; clk_data->clks = pmc_data->clks;
if (d) { if (dmi_check_system(critclk_systems))
clk_data->critical = true; clk_data->critical = pmc_clk_is_critical;
pr_info("%s critclks quirk enabled\n", d->ident);
}
clkdev = platform_device_register_data(&pdev->dev, "clk-pmc-atom", clkdev = platform_device_register_data(&pdev->dev, "clk-pmc-atom",
PLATFORM_DEVID_NONE, 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 @@ ...@@ -9,6 +9,7 @@
#define TLMI_SETTINGS_MAXLEN 512 #define TLMI_SETTINGS_MAXLEN 512
#define TLMI_PWD_BUFSIZE 129 #define TLMI_PWD_BUFSIZE 129
#define TLMI_LANG_MAXLEN 4 #define TLMI_LANG_MAXLEN 4
#define TLMI_INDEX_MAX 32
/* Possible error values */ /* Possible error values */
struct tlmi_err_codes { struct tlmi_err_codes {
...@@ -21,8 +22,13 @@ enum encoding_option { ...@@ -21,8 +22,13 @@ enum encoding_option {
TLMI_ENCODING_SCANCODE, TLMI_ENCODING_SCANCODE,
}; };
enum level_option {
TLMI_LEVEL_USER,
TLMI_LEVEL_MASTER,
};
/* password configuration details */ /* password configuration details */
struct tlmi_pwdcfg { struct tlmi_pwdcfg_core {
uint32_t password_mode; uint32_t password_mode;
uint32_t password_state; uint32_t password_state;
uint32_t min_length; uint32_t min_length;
...@@ -31,6 +37,18 @@ struct tlmi_pwdcfg { ...@@ -31,6 +37,18 @@ struct tlmi_pwdcfg {
uint32_t supported_keyboard; 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 */ /* password setting details */
struct tlmi_pwd_setting { struct tlmi_pwd_setting {
struct kobject kobj; struct kobject kobj;
...@@ -42,6 +60,8 @@ struct tlmi_pwd_setting { ...@@ -42,6 +60,8 @@ struct tlmi_pwd_setting {
int maxlen; int maxlen;
enum encoding_option encoding; enum encoding_option encoding;
char kbdlang[TLMI_LANG_MAXLEN]; char kbdlang[TLMI_LANG_MAXLEN];
int index; /*Used for HDD and NVME auth */
enum level_option level;
}; };
/* Attribute setting details */ /* Attribute setting details */
...@@ -61,13 +81,19 @@ struct think_lmi { ...@@ -61,13 +81,19 @@ struct think_lmi {
bool can_get_password_settings; bool can_get_password_settings;
bool pending_changes; bool pending_changes;
bool can_debug_cmd; bool can_debug_cmd;
bool opcode_support;
struct tlmi_attr_setting *setting[TLMI_SETTINGS_COUNT]; struct tlmi_attr_setting *setting[TLMI_SETTINGS_COUNT];
struct device *class_dev; struct device *class_dev;
struct kset *attribute_kset; struct kset *attribute_kset;
struct kset *authentication_kset; struct kset *authentication_kset;
struct tlmi_pwdcfg pwdcfg;
struct tlmi_pwd_setting *pwd_admin; struct tlmi_pwd_setting *pwd_admin;
struct tlmi_pwd_setting *pwd_power; 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_ */ #endif /* !_THINK_LMI_H_ */
This diff is collapsed.
...@@ -107,6 +107,9 @@ static const struct property_entry chuwi_hi10_plus_props[] = { ...@@ -107,6 +107,9 @@ static const struct property_entry chuwi_hi10_plus_props[] = {
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi10plus.fw"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi10plus.fw"),
PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"), 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 = { ...@@ -124,15 +127,21 @@ static const struct ts_dmi_data chuwi_hi10_plus_data = {
.properties = chuwi_hi10_plus_props, .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[] = { static const struct property_entry chuwi_hi10_pro_props[] = {
PROPERTY_ENTRY_U32("touchscreen-min-x", 8), PROPERTY_ENTRY_U32("touchscreen-min-x", 80),
PROPERTY_ENTRY_U32("touchscreen-min-y", 8), PROPERTY_ENTRY_U32("touchscreen-min-y", 26),
PROPERTY_ENTRY_U32("touchscreen-size-x", 1912), PROPERTY_ENTRY_U32("touchscreen-size-x", 1962),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1272), PROPERTY_ENTRY_U32("touchscreen-size-y", 1254),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi10-pro.fw"), 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_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"), 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 = { ...@@ -352,18 +361,6 @@ static const struct ts_dmi_data gdix1001_01_upside_down_data = {
.properties = gdix1001_upside_down_props, .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[] = { static const struct property_entry gp_electronic_t701_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 960), PROPERTY_ENTRY_U32("touchscreen-size-x", 960),
PROPERTY_ENTRY_U32("touchscreen-size-y", 640), PROPERTY_ENTRY_U32("touchscreen-size-y", 640),
...@@ -1140,15 +1137,6 @@ const struct dmi_system_id touchscreen_dmi_table[] = { ...@@ -1140,15 +1137,6 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "eSTAR BEAUTY HD Intel Quad core"), 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 */ /* GP-electronic T701 */
.driver_data = (void *)&gp_electronic_t701_data, .driver_data = (void *)&gp_electronic_t701_data,
......
...@@ -175,6 +175,7 @@ static struct attribute *uv_hub_attrs[] = { ...@@ -175,6 +175,7 @@ static struct attribute *uv_hub_attrs[] = {
&cnode_attribute.attr, &cnode_attribute.attr,
NULL, NULL,
}; };
ATTRIBUTE_GROUPS(uv_hub);
static void hub_release(struct kobject *kobj) static void hub_release(struct kobject *kobj)
{ {
...@@ -205,7 +206,7 @@ static const struct sysfs_ops hub_sysfs_ops = { ...@@ -205,7 +206,7 @@ static const struct sysfs_ops hub_sysfs_ops = {
static struct kobj_type hub_attr_type = { static struct kobj_type hub_attr_type = {
.release = hub_release, .release = hub_release,
.sysfs_ops = &hub_sysfs_ops, .sysfs_ops = &hub_sysfs_ops,
.default_attrs = uv_hub_attrs, .default_groups = uv_hub_groups,
}; };
static int uv_hubs_init(void) static int uv_hubs_init(void)
...@@ -327,6 +328,7 @@ static struct attribute *uv_port_attrs[] = { ...@@ -327,6 +328,7 @@ static struct attribute *uv_port_attrs[] = {
&uv_port_conn_port_attribute.attr, &uv_port_conn_port_attribute.attr,
NULL, NULL,
}; };
ATTRIBUTE_GROUPS(uv_port);
static void uv_port_release(struct kobject *kobj) static void uv_port_release(struct kobject *kobj)
{ {
...@@ -357,7 +359,7 @@ static const struct sysfs_ops uv_port_sysfs_ops = { ...@@ -357,7 +359,7 @@ static const struct sysfs_ops uv_port_sysfs_ops = {
static struct kobj_type uv_port_attr_type = { static struct kobj_type uv_port_attr_type = {
.release = uv_port_release, .release = uv_port_release,
.sysfs_ops = &uv_port_sysfs_ops, .sysfs_ops = &uv_port_sysfs_ops,
.default_attrs = uv_port_attrs, .default_groups = uv_port_groups,
}; };
static int uv_ports_init(void) static int uv_ports_init(void)
......
...@@ -57,6 +57,11 @@ static_assert(sizeof(typeof_member(struct guid_block, guid)) == 16); ...@@ -57,6 +57,11 @@ static_assert(sizeof(typeof_member(struct guid_block, guid)) == 16);
static_assert(sizeof(struct guid_block) == 20); static_assert(sizeof(struct guid_block) == 20);
static_assert(__alignof__(struct guid_block) == 1); static_assert(__alignof__(struct guid_block) == 1);
enum { /* wmi_block flags */
WMI_READ_TAKES_NO_ARGS,
WMI_PROBED,
};
struct wmi_block { struct wmi_block {
struct wmi_device dev; struct wmi_device dev;
struct list_head list; struct list_head list;
...@@ -67,8 +72,7 @@ struct wmi_block { ...@@ -67,8 +72,7 @@ struct wmi_block {
wmi_notify_handler handler; wmi_notify_handler handler;
void *handler_data; void *handler_data;
u64 req_buf_size; u64 req_buf_size;
unsigned long flags;
bool read_takes_no_args;
}; };
...@@ -367,7 +371,7 @@ static acpi_status __query_block(struct wmi_block *wblock, u8 instance, ...@@ -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].type = ACPI_TYPE_INTEGER;
wq_params[0].integer.value = instance; 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; input.count = 0;
/* /*
...@@ -1005,6 +1009,7 @@ static int wmi_dev_probe(struct device *dev) ...@@ -1005,6 +1009,7 @@ static int wmi_dev_probe(struct device *dev)
} }
} }
set_bit(WMI_PROBED, &wblock->flags);
return 0; return 0;
probe_misc_failure: probe_misc_failure:
...@@ -1022,6 +1027,8 @@ static void wmi_dev_remove(struct device *dev) ...@@ -1022,6 +1027,8 @@ static void wmi_dev_remove(struct device *dev)
struct wmi_block *wblock = dev_to_wblock(dev); struct wmi_block *wblock = dev_to_wblock(dev);
struct wmi_driver *wdriver = drv_to_wdrv(dev->driver); struct wmi_driver *wdriver = drv_to_wdrv(dev->driver);
clear_bit(WMI_PROBED, &wblock->flags);
if (wdriver->filter_callback) { if (wdriver->filter_callback) {
misc_deregister(&wblock->char_dev); misc_deregister(&wblock->char_dev);
kfree(wblock->char_dev.name); kfree(wblock->char_dev.name);
...@@ -1113,7 +1120,7 @@ static int wmi_create_device(struct device *wmi_bus_dev, ...@@ -1113,7 +1120,7 @@ static int wmi_create_device(struct device *wmi_bus_dev,
* laptops, WQxx may not be a method at all.) * laptops, WQxx may not be a method at all.)
*/ */
if (info->type != ACPI_TYPE_METHOD || info->param_count == 0) 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); kfree(info);
...@@ -1319,15 +1326,17 @@ static void acpi_wmi_notify_handler(acpi_handle handle, u32 event, ...@@ -1319,15 +1326,17 @@ static void acpi_wmi_notify_handler(acpi_handle handle, u32 event,
return; return;
/* If a driver is bound, then notify the driver. */ /* 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 wmi_driver *driver = drv_to_wdrv(wblock->dev.dev.driver);
struct acpi_buffer evdata = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_buffer evdata = { ACPI_ALLOCATE_BUFFER, NULL };
acpi_status status; acpi_status status;
status = get_event_data(wblock, &evdata); if (!driver->no_notify_data) {
if (ACPI_FAILURE(status)) { status = get_event_data(wblock, &evdata);
dev_warn(&wblock->dev.dev, "failed to get event data\n"); if (ACPI_FAILURE(status)) {
return; dev_warn(&wblock->dev.dev, "failed to get event data\n");
return;
}
} }
if (driver->notify) if (driver->notify)
......
This diff is collapsed.
...@@ -134,6 +134,12 @@ static const char * const POWER_SUPPLY_SCOPE_TEXT[] = { ...@@ -134,6 +134,12 @@ static const char * const POWER_SUPPLY_SCOPE_TEXT[] = {
[POWER_SUPPLY_SCOPE_DEVICE] = "Device", [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[] = { static struct power_supply_attr power_supply_attrs[] = {
/* Properties of type `int' */ /* Properties of type `int' */
POWER_SUPPLY_ENUM_ATTR(STATUS), POWER_SUPPLY_ENUM_ATTR(STATUS),
...@@ -173,6 +179,7 @@ static struct power_supply_attr power_supply_attrs[] = { ...@@ -173,6 +179,7 @@ static struct power_supply_attr power_supply_attrs[] = {
POWER_SUPPLY_ATTR(CHARGE_CONTROL_LIMIT_MAX), POWER_SUPPLY_ATTR(CHARGE_CONTROL_LIMIT_MAX),
POWER_SUPPLY_ATTR(CHARGE_CONTROL_START_THRESHOLD), POWER_SUPPLY_ATTR(CHARGE_CONTROL_START_THRESHOLD),
POWER_SUPPLY_ATTR(CHARGE_CONTROL_END_THRESHOLD), POWER_SUPPLY_ATTR(CHARGE_CONTROL_END_THRESHOLD),
POWER_SUPPLY_ENUM_ATTR(CHARGE_BEHAVIOUR),
POWER_SUPPLY_ATTR(INPUT_CURRENT_LIMIT), POWER_SUPPLY_ATTR(INPUT_CURRENT_LIMIT),
POWER_SUPPLY_ATTR(INPUT_VOLTAGE_LIMIT), POWER_SUPPLY_ATTR(INPUT_VOLTAGE_LIMIT),
POWER_SUPPLY_ATTR(INPUT_POWER_LIMIT), POWER_SUPPLY_ATTR(INPUT_POWER_LIMIT),
...@@ -485,3 +492,52 @@ int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env) ...@@ -485,3 +492,52 @@ int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env)
return ret; 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 ...@@ -1589,6 +1589,17 @@ config NIC7018_WDT
To compile this driver as a module, choose M here: the module will be To compile this driver as a module, choose M here: the module will be
called nic7018_wdt. 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 # M68K Architecture
config M54xx_WATCHDOG config M54xx_WATCHDOG
......
...@@ -143,6 +143,7 @@ obj-$(CONFIG_NI903X_WDT) += ni903x_wdt.o ...@@ -143,6 +143,7 @@ obj-$(CONFIG_NI903X_WDT) += ni903x_wdt.o
obj-$(CONFIG_NIC7018_WDT) += nic7018_wdt.o obj-$(CONFIG_NIC7018_WDT) += nic7018_wdt.o
obj-$(CONFIG_MLX_WDT) += mlx_wdt.o obj-$(CONFIG_MLX_WDT) += mlx_wdt.o
obj-$(CONFIG_KEEMBAY_WATCHDOG) += keembay_wdt.o obj-$(CONFIG_KEEMBAY_WATCHDOG) += keembay_wdt.o
obj-$(CONFIG_SIEMENS_SIMATIC_IPC_WDT) += simatic-ipc-wdt.o
# M68K Architecture # M68K Architecture
obj-$(CONFIG_M54xx_WATCHDOG) += m54xx_wdt.o 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 @@ ...@@ -77,6 +77,8 @@
#define ASUS_WMI_DEVID_THERMAL_CTRL 0x00110011 #define ASUS_WMI_DEVID_THERMAL_CTRL 0x00110011
#define ASUS_WMI_DEVID_FAN_CTRL 0x00110012 /* deprecated */ #define ASUS_WMI_DEVID_FAN_CTRL 0x00110012 /* deprecated */
#define ASUS_WMI_DEVID_CPU_FAN_CTRL 0x00110013 #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 */ /* Power */
#define ASUS_WMI_DEVID_PROCESSOR_STATE 0x00120012 #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 { ...@@ -133,6 +133,7 @@ enum power_supply_property {
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD, /* in percents! */ POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD, /* in percents! */
POWER_SUPPLY_PROP_CHARGE_CONTROL_END_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_CURRENT_LIMIT,
POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT, POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT,
POWER_SUPPLY_PROP_INPUT_POWER_LIMIT, POWER_SUPPLY_PROP_INPUT_POWER_LIMIT,
...@@ -203,6 +204,12 @@ enum power_supply_usb_type { ...@@ -203,6 +204,12 @@ enum power_supply_usb_type {
POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID, /* Apple Charging Method */ 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 { enum power_supply_notifier_events {
PSY_EVENT_PROP_CHANGED, PSY_EVENT_PROP_CHANGED,
}; };
...@@ -709,4 +716,28 @@ static inline ...@@ -709,4 +716,28 @@ static inline
void power_supply_remove_hwmon_sysfs(struct power_supply *psy) {} void power_supply_remove_hwmon_sysfs(struct power_supply *psy) {}
#endif #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__ */ #endif /* __LINUX_POWER_SUPPLY_H__ */
...@@ -319,6 +319,15 @@ void ssam_device_driver_unregister(struct ssam_device_driver *d); ...@@ -319,6 +319,15 @@ void ssam_device_driver_unregister(struct ssam_device_driver *d);
ssam_device_driver_unregister) 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. ----------------------------------- */ /* -- Helpers for client-device requests. ----------------------------------- */
/** /**
......
...@@ -35,6 +35,7 @@ extern int set_required_buffer_size(struct wmi_device *wdev, u64 length); ...@@ -35,6 +35,7 @@ extern int set_required_buffer_size(struct wmi_device *wdev, u64 length);
struct wmi_driver { struct wmi_driver {
struct device_driver driver; struct device_driver driver;
const struct wmi_device_id *id_table; const struct wmi_device_id *id_table;
bool no_notify_data;
int (*probe)(struct wmi_device *wdev, const void *context); int (*probe)(struct wmi_device *wdev, const void *context);
void (*remove)(struct wmi_device *wdev); void (*remove)(struct wmi_device *wdev);
......
...@@ -15,7 +15,7 @@ struct process_cmd_struct { ...@@ -15,7 +15,7 @@ struct process_cmd_struct {
int arg; int arg;
}; };
static const char *version_str = "v1.10"; static const char *version_str = "v1.11";
static const int supported_api_ver = 1; static const int supported_api_ver = 1;
static struct isst_if_platform_info isst_platform_info; static struct isst_if_platform_info isst_platform_info;
static char *progname; static char *progname;
...@@ -1599,6 +1599,7 @@ static void set_scaling_min_to_cpuinfo_max(int cpu) ...@@ -1599,6 +1599,7 @@ static void set_scaling_min_to_cpuinfo_max(int cpu)
die_id != get_physical_die_id(i)) die_id != get_physical_die_id(i))
continue; continue;
adjust_scaling_max_from_base_freq(i);
set_cpufreq_scaling_min_max_from_cpuinfo(i, 1, 0); set_cpufreq_scaling_min_max_from_cpuinfo(i, 1, 0);
adjust_scaling_min_from_base_freq(i); adjust_scaling_min_from_base_freq(i);
} }
...@@ -1615,6 +1616,7 @@ static void set_scaling_min_to_cpuinfo_min(int cpu) ...@@ -1615,6 +1616,7 @@ static void set_scaling_min_to_cpuinfo_min(int cpu)
die_id != get_physical_die_id(i)) die_id != get_physical_die_id(i))
continue; continue;
adjust_scaling_max_from_base_freq(i);
set_cpufreq_scaling_min_max_from_cpuinfo(i, 0, 0); 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