platform-drivers-x86 for v5.3-1

ASUS WMI driver got a big refactoring in order to support the TUF Gaming
 laptops. Besides that, the regression with backlight being permanently off
 on various EeePC laptops has been fixed.
 
 Accelerometer on HP ProBook 450 G0 shows wrong measurements due to
 X axis being inverted. This has been fixed.
 
 Intel PMC core driver has been extended to be ACPI enumerated
 if the DSDT provides device with _HID "INT33A1". This allows
 to convert the driver to be pure platform and support new hardware
 purely based on ACPI DSDT.
 
 From now on the Intel Speed Select Technology is supported thru
 a corresponding driver. This driver provides an access to the features
 of the ISST, such as Performance Profile, Core Power, Base frequency and
 Turbo Frequency.
 
 Mellanox platform drivers has been refactored and now extended
 to support more systems, including new coming ones.
 
 The OLPC XO-1.75 platform is now supported.
 
 CB4063 Beckhoff Automation board is using PMC clocks,
 provided via pmc_atom driver, for ethernet controllers in a way
 that they can't be managed by the clock driver. The quirk
 has been extended to cover this case.
 
 Touchscreen on Chuwi Hi10 Plus tablet has been enabled. Meanwhile
 the information of Chuwi Hi10 Air has been fixed to cover more models
 based on the same platform.
 
 Xiaomi notebooks have WMI interface enabled. Thus, the driver to support it
 has been provided. It required some extension of the generic WMI library,
 which allows to propagate opaque context to the ->probe() of the
 individual drivers.
 
 This release includes debugfs clean up from Greg KH for several drivers
 that drop return code check and make debugfs absence or failure non-fatal.
 
 Miscellaneous fixes here and there, mostly for Acer WMI and
 various Intel drivers.
 
 The listed below commits are duplicated due to previously pushed fixes in v5.2 cycle:
 - 1dd93f873d platform/x86: asus-wmi: Only Tell EC the OS will handle display hotkeys from asus_nb_wmi
 - 89ae3a0736 platform/x86: intel-vbtn: Report switch events when event wakes device
 - fa882fc80d platform/x86: mlx-platform: Fix parent device in i2c-mux-reg device registration
 - 0bfcd24b39 platform/mellanox: mlxreg-hotplug: Add devm_free_irq call to remove flow
 
 The following is an automated git shortlog grouped by driver:
 
 acer-wmi:
  -  Mark expected switch fall-throughs
  -  no need to check return value of debugfs_create functions
 
 asus-nb-wmi:
  -  Add microphone mute key code
 
 asus-wmi:
  -  Use dev_get_drvdata()
  -  Do not disable keyboard backlight on unloading
  -  Switch fan boost mode
  -  Enhance detection of thermal data
  -  Organize code into sections
  -  Refactor error handling
  -  Support WMI event queue
  -  Refactor WMI event handling
  -  Improve DSTS WMI method ID detection
  -  Increase input buffer size of WMI methods
  -  Fix preserving keyboard backlight intensity on load
  -  Fix hwmon device cleanup
  -  no need to check return value of debugfs_create functions
  -  Only Tell EC the OS will handle display hotkeys from asus_nb_wmi
 
 dell-laptop:
  -  no need to check return value of debugfs_create functions
 
 hp_accel:
  -  Add support for HP ProBook 450 G0
 
 ideapad-laptop:
  -  no need to check return value of debugfs_create functions
 
 intel_int0002_vgpio:
  -  Get rid of custom ICPU() macro
 
 intel_menlow:
  -  avoid null pointer deference error
 
 intel_pmc:
  -  no need to check return value of debugfs_create functions
 
 intel_pmc_core:
  -  Attach using APCI HID "INT33A1"
  -  transform Pkg C-state residency from TSC ticks into microseconds
 
 intel_telemetry:
  -  no need to check return value of debugfs_create functions
 
 intel-vbtn:
  -  Report switch events when event wakes device
 
 ISST:
  -  Restore state on resume
  -  Add Intel Speed Select PUNIT MSR interface
  -  Add Intel Speed Select mailbox interface via MSRs
  -  Add Intel Speed Select mailbox interface via PCI
  -  Add Intel Speed Select mmio interface
  -  Add IOCTL to Translate Linux logical CPU to PUNIT CPU number
  -  Store per CPU information
  -  Add common API to register and handle ioctls
  -  Update ioctl-number.txt for Intel Speed Select interface
  -  A tool to validate Intel Speed Select commands
  -  Add .gitignore file
 
 MAINTAINERS:
  -  Update for Intel Speed Select Technology
 
 mlx-platform:
  -  Fix error handling in mlxplat_init()
  -  Add more reset cause attributes
  -  Modify DMI matching order
  -  Add regmap structure for the next generation systems
  -  Change API for i2c-mlxcpld driver activation
  -  Move regmap initialization before all drivers activation
  -  Fix parent device in i2c-mux-reg device registration
  -  Add new attribute for mlxreg-io sysfs interfaces
 
 pcengines-apuv2:
  -  Make two symbols static
  -  Fix PCENGINES_APU2 Kconfig warning
 
 OLPC:
  -  Add a config menu category for XO 1.75
  -  Require CONFIG_POWER_SUPPLY for XO-1.75 EC
  -  Fix olpc_xo175_ec_cmd() return value
  -  Make olpc_dt_compatible_match() static __init
  -  Add INPUT dependencies
  -  Fix build error without CONFIG_SPI
  -  Add a regulator for the DCON
  -  Add XO-1.75 EC driver
  -  Use BIT() and GENMASK() for event masks
  -  Avoid a warning if the EC didn't register yet
  -  Move EC-specific functionality out from x86
  -  Remove an unused include
  -  Add OLPC XO-1.75 EC bindings
 
 platform/mellanox:
  -  mlxreg-hotplug: Add devm_free_irq call to remove flow
 
 pmc_atom:
  -  Add CB4063 Beckhoff Automation board to critclk_systems DMI table
  -  no need to check return value of debugfs_create functions
 
 Kconfig:
  - Remove left-over BACKLIGHT_LCD_SUPPORT
 
 samsung-laptop:
  -  no need to check return value of debugfs_create functions
 
 touchscreen_dmi:
  -  Update Hi10 Air filter
  -  Add info for the CHUWI Hi10 Plus tablet.
 
 wmi:
  -  add Xiaomi WMI key driver
  -  add context argument to the probe function
  -  add context pointer field to struct wmi_device_id
  -  Add function to get _UID of WMI device
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEqaflIX74DDDzMJJtb7wzTHR8rCgFAl0rP88ACgkQb7wzTHR8
 rCis8BAAnRgRgi8x1C7xn66gAUHsDXpY0tF9cp/Fw3HyTmFCQkRSmnLkMM2DqGi+
 dvB9U1zPcGWwdwryKFsJXioEK3erYpiYyT2VwLtW4S7P5jQ+N9biT4TZ8yFp0MEr
 MZC50LZDV1JTp1a0GQyrMpfoMBnE7UhR2GL8UbGli/WwXFE5BLkrJ1pdrjhYZOHl
 rJcgq3HPAhV5qkUkIU7gTC2GGSPydjBqk0OhVIU4dPsYwXIb2gXc0yR0QVwKm5x3
 I/NQwBOBMKmdI6uJ8BJyg/p888Strw65YJaTe5wtvG8ljuIbcN/aQ3ZmClNrUnc0
 58byqJCpRhg9HN39VpF9rsApEGxKTlitAUAUKy7lgue7/mycHbA1Syzz29AIM+2v
 ey2/zgFeeWtgh1cuh2cUWlCE6woW7ED4VpDxhkXlX4xGUp+CILEiFqcsULlcc4j5
 sgojCLRPs78roYj9Y84CwYbsd7J/Ce4r2evBpKYPqYxDbUiuH2aVQtEdPTKv9/xC
 yHtBuJJSxY7a+sf4OZONRo13dfvRoZIPjcccR8yTOakS2/1Fqph7MpHyDkwFAfeS
 M2f+OcJn9IECol1391PTLj9Dx3jApyVk21HJdiIj7sKZgJOSS54AFm0/Ywk0MFpY
 XScXKulV48SdL4ZKup5aIpDzyP5zuvXszKQboRitep1dHiR9bl0=
 =DC5j
 -----END PGP SIGNATURE-----

Merge tag 'platform-drivers-x86-v5.3-1' of git://git.infradead.org/linux-platform-drivers-x86

Pull x86 platform driver updates from Andy Shevchenko:
 "Gathered a bunch of x86 platform driver changes. It's rather big,
  since includes two big refactors and completely new driver:

   - ASUS WMI driver got a big refactoring in order to support the TUF
     Gaming laptops. Besides that, the regression with backlight being
     permanently off on various EeePC laptops has been fixed.

   - Accelerometer on HP ProBook 450 G0 shows wrong measurements due to
     X axis being inverted. This has been fixed.

   - Intel PMC core driver has been extended to be ACPI enumerated if
     the DSDT provides device with _HID "INT33A1". This allows to
     convert the driver to be pure platform and support new hardware
     purely based on ACPI DSDT.

   - From now on the Intel Speed Select Technology is supported thru a
     corresponding driver. This driver provides an access to the
     features of the ISST, such as Performance Profile, Core Power, Base
     frequency and Turbo Frequency.

   - Mellanox platform drivers has been refactored and now extended to
     support more systems, including new coming ones.

   - The OLPC XO-1.75 platform is now supported.

   - CB4063 Beckhoff Automation board is using PMC clocks, provided via
     pmc_atom driver, for ethernet controllers in a way that they can't
     be managed by the clock driver. The quirk has been extended to
     cover this case.

   - Touchscreen on Chuwi Hi10 Plus tablet has been enabled. Meanwhile
     the information of Chuwi Hi10 Air has been fixed to cover more
     models based on the same platform.

   - Xiaomi notebooks have WMI interface enabled. Thus, the driver to
     support it has been provided. It required some extension of the
     generic WMI library, which allows to propagate opaque context to
     the ->probe() of the individual drivers.

  This release includes debugfs clean up from Greg KH for several
  drivers that drop return code check and make debugfs absence or
  failure non-fatal.

  Also miscellaneous fixes here and there, mostly for Acer WMI and
  various Intel drivers"

* tag 'platform-drivers-x86-v5.3-1' of git://git.infradead.org/linux-platform-drivers-x86: (74 commits)
  platform/x86: Fix PCENGINES_APU2 Kconfig warning
  tools/power/x86/intel-speed-select: Add .gitignore file
  platform/x86: mlx-platform: Fix error handling in mlxplat_init()
  platform/x86: intel_pmc_core: Attach using APCI HID "INT33A1"
  platform/x86: intel_pmc_core: transform Pkg C-state residency from TSC ticks into microseconds
  platform/x86: asus-wmi: Use dev_get_drvdata()
  Documentation/ABI: Add new attribute for mlxreg-io sysfs interfaces
  platform/x86: mlx-platform: Add more reset cause attributes
  platform/x86: mlx-platform: Modify DMI matching order
  platform/x86: mlx-platform: Add regmap structure for the next generation systems
  platform/x86: mlx-platform: Change API for i2c-mlxcpld driver activation
  platform/x86: mlx-platform: Move regmap initialization before all drivers activation
  MAINTAINERS: Update for Intel Speed Select Technology
  tools/power/x86: A tool to validate Intel Speed Select commands
  platform/x86: ISST: Restore state on resume
  platform/x86: ISST: Add Intel Speed Select PUNIT MSR interface
  platform/x86: ISST: Add Intel Speed Select mailbox interface via MSRs
  platform/x86: ISST: Add Intel Speed Select mailbox interface via PCI
  platform/x86: ISST: Add Intel Speed Select mmio interface
  platform/x86: ISST: Add IOCTL to Translate Linux logical CPU to PUNIT CPU number
  ...
This commit is contained in:
Linus Torvalds 2019-07-14 16:51:47 -07:00
commit 5516745311
65 changed files with 6591 additions and 643 deletions

View File

@ -120,3 +120,23 @@ Description: These files show the system reset cause, as following: ComEx
the last reset cause. the last reset cause.
The files are read only. The files are read only.
Date: June 2019
KernelVersion: 5.3
Contact: Vadim Pasternak <vadimpmellanox.com>
Description: These files show the system reset cause, as following:
COMEX thermal shutdown; wathchdog power off or reset was derived
by one of the next components: COMEX, switch board or by Small Form
Factor mezzanine, reset requested from ASIC, reset cuased by BIOS
reload. Value 1 in file means this is reset cause, 0 - otherwise.
Only one of the above causes could be 1 at the same time, representing
only last reset cause.
The files are read only.
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_comex_thermal
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_comex_wd
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_from_asic
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_reload_bios
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_sff_wd
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_swb_wd

View File

@ -36,3 +36,13 @@ KernelVersion: 3.5
Contact: "AceLan Kao" <acelan.kao@canonical.com> Contact: "AceLan Kao" <acelan.kao@canonical.com>
Description: Description:
Resume on lid open. 1 means on, 0 means off. Resume on lid open. 1 means on, 0 means off.
What: /sys/devices/platform/<platform>/fan_mode
Date: Apr 2019
KernelVersion: 5.2
Contact: "Yurii Pavlovskyi" <yurii.pavlovskyi@gmail.com>
Description:
Fan boost mode:
* 0 - normal,
* 1 - overboost,
* 2 - silent

View File

@ -0,0 +1,23 @@
OLPC XO-1.75 Embedded Controller
Required properties:
- compatible: Should be "olpc,xo1.75-ec".
- cmd-gpios: gpio specifier of the CMD pin
The embedded controller requires the SPI controller driver to signal readiness
to receive a transfer (that is, when TX FIFO contains the response data) by
strobing the ACK pin with the ready signal. See the "ready-gpios" property of the
SSP binding as documented in:
<Documentation/devicetree/bindings/spi/spi-pxa2xx.txt>.
Example:
&ssp3 {
spi-slave;
ready-gpios = <&gpio 125 GPIO_ACTIVE_HIGH>;
slave {
compatible = "olpc,xo1.75-ec";
spi-cpha;
cmd-gpios = <&gpio 155 GPIO_ACTIVE_HIGH>;
};
};

View File

@ -348,3 +348,4 @@ Code Seq#(hex) Include File Comments
0xF6 all LTTng Linux Trace Toolkit Next Generation 0xF6 all LTTng Linux Trace Toolkit Next Generation
<mailto:mathieu.desnoyers@efficios.com> <mailto:mathieu.desnoyers@efficios.com>
0xFD all linux/dm-ioctl.h 0xFD all linux/dm-ioctl.h
0xFE all linux/isst_if.h

View File

@ -8246,6 +8246,14 @@ S: Supported
F: drivers/infiniband/hw/i40iw/ F: drivers/infiniband/hw/i40iw/
F: include/uapi/rdma/i40iw-abi.h F: include/uapi/rdma/i40iw-abi.h
INTEL SPEED SELECT TECHNOLOGY
M: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/x86/intel_speed_select_if/
F: tools/power/x86/intel-speed-select/
F: include/uapi/linux/isst_if.h
INTEL TELEMETRY DRIVER INTEL TELEMETRY DRIVER
M: Rajneesh Bhardwaj <rajneesh.bhardwaj@linux.intel.com> M: Rajneesh Bhardwaj <rajneesh.bhardwaj@linux.intel.com>
M: "David E. Box" <david.e.box@linux.intel.com> M: "David E. Box" <david.e.box@linux.intel.com>

View File

@ -2733,6 +2733,7 @@ config OLPC
select OF select OF
select OF_PROMTREE select OF_PROMTREE
select IRQ_DOMAIN select IRQ_DOMAIN
select OLPC_EC
---help--- ---help---
Add support for detecting the unique features of the OLPC Add support for detecting the unique features of the OLPC
XO hardware. XO hardware.

View File

@ -9,12 +9,10 @@
struct olpc_platform_t { struct olpc_platform_t {
int flags; int flags;
uint32_t boardrev; uint32_t boardrev;
int ecver;
}; };
#define OLPC_F_PRESENT 0x01 #define OLPC_F_PRESENT 0x01
#define OLPC_F_DCON 0x02 #define OLPC_F_DCON 0x02
#define OLPC_F_EC_WIDE_SCI 0x04
#ifdef CONFIG_OLPC #ifdef CONFIG_OLPC
@ -64,13 +62,6 @@ static inline int olpc_board_at_least(uint32_t rev)
return olpc_platform_info.boardrev >= rev; return olpc_platform_info.boardrev >= rev;
} }
extern void olpc_ec_wakeup_set(u16 value);
extern void olpc_ec_wakeup_clear(u16 value);
extern bool olpc_ec_wakeup_available(void);
extern int olpc_ec_mask_write(u16 bits);
extern int olpc_ec_sci_query(u16 *sci_value);
#else #else
static inline int machine_is_olpc(void) static inline int machine_is_olpc(void)
@ -83,14 +74,6 @@ static inline int olpc_has_dcon(void)
return 0; return 0;
} }
static inline void olpc_ec_wakeup_set(u16 value) { }
static inline void olpc_ec_wakeup_clear(u16 value) { }
static inline bool olpc_ec_wakeup_available(void)
{
return false;
}
#endif #endif
#ifdef CONFIG_OLPC_XO1_PM #ifdef CONFIG_OLPC_XO1_PM
@ -101,20 +84,6 @@ extern void olpc_xo1_pm_wakeup_clear(u16 value);
extern int pci_olpc_init(void); extern int pci_olpc_init(void);
/* SCI source values */
#define EC_SCI_SRC_EMPTY 0x00
#define EC_SCI_SRC_GAME 0x01
#define EC_SCI_SRC_BATTERY 0x02
#define EC_SCI_SRC_BATSOC 0x04
#define EC_SCI_SRC_BATERR 0x08
#define EC_SCI_SRC_EBOOK 0x10 /* XO-1 only */
#define EC_SCI_SRC_WLAN 0x20 /* XO-1 only */
#define EC_SCI_SRC_ACPWR 0x40
#define EC_SCI_SRC_BATCRIT 0x80
#define EC_SCI_SRC_GPWAKE 0x100 /* XO-1.5 only */
#define EC_SCI_SRC_ALL 0x1FF
/* GPIO assignments */ /* GPIO assignments */
#define OLPC_GPIO_MIC_AC 1 #define OLPC_GPIO_MIC_AC 1

View File

@ -26,9 +26,6 @@
struct olpc_platform_t olpc_platform_info; struct olpc_platform_t olpc_platform_info;
EXPORT_SYMBOL_GPL(olpc_platform_info); EXPORT_SYMBOL_GPL(olpc_platform_info);
/* EC event mask to be applied during suspend (defining wakeup sources). */
static u16 ec_wakeup_mask;
/* what the timeout *should* be (in ms) */ /* what the timeout *should* be (in ms) */
#define EC_BASE_TIMEOUT 20 #define EC_BASE_TIMEOUT 20
@ -182,83 +179,6 @@ err:
return ret; return ret;
} }
void olpc_ec_wakeup_set(u16 value)
{
ec_wakeup_mask |= value;
}
EXPORT_SYMBOL_GPL(olpc_ec_wakeup_set);
void olpc_ec_wakeup_clear(u16 value)
{
ec_wakeup_mask &= ~value;
}
EXPORT_SYMBOL_GPL(olpc_ec_wakeup_clear);
/*
* Returns true if the compile and runtime configurations allow for EC events
* to wake the system.
*/
bool olpc_ec_wakeup_available(void)
{
if (!machine_is_olpc())
return false;
/*
* XO-1 EC wakeups are available when olpc-xo1-sci driver is
* compiled in
*/
#ifdef CONFIG_OLPC_XO1_SCI
if (olpc_platform_info.boardrev < olpc_board_pre(0xd0)) /* XO-1 */
return true;
#endif
/*
* XO-1.5 EC wakeups are available when olpc-xo15-sci driver is
* compiled in
*/
#ifdef CONFIG_OLPC_XO15_SCI
if (olpc_platform_info.boardrev >= olpc_board_pre(0xd0)) /* XO-1.5 */
return true;
#endif
return false;
}
EXPORT_SYMBOL_GPL(olpc_ec_wakeup_available);
int olpc_ec_mask_write(u16 bits)
{
if (olpc_platform_info.flags & OLPC_F_EC_WIDE_SCI) {
__be16 ec_word = cpu_to_be16(bits);
return olpc_ec_cmd(EC_WRITE_EXT_SCI_MASK, (void *) &ec_word, 2,
NULL, 0);
} else {
unsigned char ec_byte = bits & 0xff;
return olpc_ec_cmd(EC_WRITE_SCI_MASK, &ec_byte, 1, NULL, 0);
}
}
EXPORT_SYMBOL_GPL(olpc_ec_mask_write);
int olpc_ec_sci_query(u16 *sci_value)
{
int ret;
if (olpc_platform_info.flags & OLPC_F_EC_WIDE_SCI) {
__be16 ec_word;
ret = olpc_ec_cmd(EC_EXT_SCI_QUERY,
NULL, 0, (void *) &ec_word, 2);
if (ret == 0)
*sci_value = be16_to_cpu(ec_word);
} else {
unsigned char ec_byte;
ret = olpc_ec_cmd(EC_SCI_QUERY, NULL, 0, &ec_byte, 1);
if (ret == 0)
*sci_value = ec_byte;
}
return ret;
}
EXPORT_SYMBOL_GPL(olpc_ec_sci_query);
static bool __init check_ofw_architecture(struct device_node *root) static bool __init check_ofw_architecture(struct device_node *root)
{ {
const char *olpc_arch; const char *olpc_arch;
@ -292,6 +212,10 @@ static bool __init platform_detect(void)
if (success) { if (success) {
olpc_platform_info.boardrev = get_board_revision(root); olpc_platform_info.boardrev = get_board_revision(root);
olpc_platform_info.flags |= OLPC_F_PRESENT; olpc_platform_info.flags |= OLPC_F_PRESENT;
pr_info("OLPC board revision %s%X\n",
((olpc_platform_info.boardrev & 0xf) < 8) ? "pre" : "",
olpc_platform_info.boardrev >> 4);
} }
of_node_put(root); of_node_put(root);
@ -311,27 +235,8 @@ static int __init add_xo1_platform_devices(void)
return PTR_ERR_OR_ZERO(pdev); return PTR_ERR_OR_ZERO(pdev);
} }
static int olpc_xo1_ec_probe(struct platform_device *pdev)
{
/* get the EC revision */
olpc_ec_cmd(EC_FIRMWARE_REV, NULL, 0,
(unsigned char *) &olpc_platform_info.ecver, 1);
/* EC version 0x5f adds support for wide SCI mask */
if (olpc_platform_info.ecver >= 0x5f)
olpc_platform_info.flags |= OLPC_F_EC_WIDE_SCI;
pr_info("OLPC board revision %s%X (EC=%x)\n",
((olpc_platform_info.boardrev & 0xf) < 8) ? "pre" : "",
olpc_platform_info.boardrev >> 4,
olpc_platform_info.ecver);
return 0;
}
static int olpc_xo1_ec_suspend(struct platform_device *pdev) static int olpc_xo1_ec_suspend(struct platform_device *pdev)
{ {
olpc_ec_mask_write(ec_wakeup_mask);
/* /*
* Squelch SCIs while suspended. This is a fix for * Squelch SCIs while suspended. This is a fix for
* <http://dev.laptop.org/ticket/1835>. * <http://dev.laptop.org/ticket/1835>.
@ -355,15 +260,27 @@ static int olpc_xo1_ec_resume(struct platform_device *pdev)
} }
static struct olpc_ec_driver ec_xo1_driver = { static struct olpc_ec_driver ec_xo1_driver = {
.probe = olpc_xo1_ec_probe,
.suspend = olpc_xo1_ec_suspend, .suspend = olpc_xo1_ec_suspend,
.resume = olpc_xo1_ec_resume, .resume = olpc_xo1_ec_resume,
.ec_cmd = olpc_xo1_ec_cmd, .ec_cmd = olpc_xo1_ec_cmd,
#ifdef CONFIG_OLPC_XO1_SCI
/*
* XO-1 EC wakeups are available when olpc-xo1-sci driver is
* compiled in
*/
.wakeup_available = true,
#endif
}; };
static struct olpc_ec_driver ec_xo1_5_driver = { static struct olpc_ec_driver ec_xo1_5_driver = {
.probe = olpc_xo1_ec_probe,
.ec_cmd = olpc_xo1_ec_cmd, .ec_cmd = olpc_xo1_ec_cmd,
#ifdef CONFIG_OLPC_XO1_5_SCI
/*
* XO-1.5 EC wakeups are available when olpc-xo15-sci driver is
* compiled in
*/
.wakeup_available = true,
#endif
}; };
static int __init olpc_init(void) static int __init olpc_init(void)

View File

@ -216,7 +216,7 @@ static u32 __init olpc_dt_get_board_revision(void)
return be32_to_cpu(rev); return be32_to_cpu(rev);
} }
int olpc_dt_compatible_match(phandle node, const char *compat) static int __init olpc_dt_compatible_match(phandle node, const char *compat)
{ {
char buf[64], *p; char buf[64], *p;
int plen, len; int plen, len;

View File

@ -393,7 +393,7 @@ static bool asus_kbd_wmi_led_control_present(struct hid_device *hdev)
if (!IS_ENABLED(CONFIG_ASUS_WMI)) if (!IS_ENABLED(CONFIG_ASUS_WMI))
return false; return false;
ret = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS2, ret = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS,
ASUS_WMI_DEVID_KBD_BACKLIGHT, 0, &value); ASUS_WMI_DEVID_KBD_BACKLIGHT, 0, &value);
hid_dbg(hdev, "WMI backlight check: rc %d value %x", ret, value); hid_dbg(hdev, "WMI backlight check: rc %d value %x", ret, value);
if (ret) if (ret)

View File

@ -11,3 +11,5 @@ source "drivers/platform/goldfish/Kconfig"
source "drivers/platform/chrome/Kconfig" source "drivers/platform/chrome/Kconfig"
source "drivers/platform/mellanox/Kconfig" source "drivers/platform/mellanox/Kconfig"
source "drivers/platform/olpc/Kconfig"

View File

@ -6,6 +6,6 @@
obj-$(CONFIG_X86) += x86/ obj-$(CONFIG_X86) += x86/
obj-$(CONFIG_MELLANOX_PLATFORM) += mellanox/ obj-$(CONFIG_MELLANOX_PLATFORM) += mellanox/
obj-$(CONFIG_MIPS) += mips/ obj-$(CONFIG_MIPS) += mips/
obj-$(CONFIG_OLPC) += olpc/ obj-$(CONFIG_OLPC_EC) += olpc/
obj-$(CONFIG_GOLDFISH) += goldfish/ obj-$(CONFIG_GOLDFISH) += goldfish/
obj-$(CONFIG_CHROME_PLATFORMS) += chrome/ obj-$(CONFIG_CHROME_PLATFORMS) += chrome/

View File

@ -0,0 +1,29 @@
config OLPC_EC
select REGULATOR
bool
menuconfig OLPC_XO175
bool "Platform support for OLPC XO 1.75 hardware"
depends on ARCH_MMP || COMPILE_TEST
help
Say Y here to get to see options for the ARM-based OLPC platform.
This option alone does not add any kernel code.
Unless you have an OLPC XO laptop, you will want to say N.
if OLPC_XO175
config OLPC_XO175_EC
tristate "OLPC XO 1.75 Embedded Controller"
depends on SPI_SLAVE
depends on INPUT
depends on POWER_SUPPLY
select OLPC_EC
help
Include support for the OLPC XO Embedded Controller (EC). The EC
provides various platform services, including support for the power,
button, restart, shutdown and battery charging status.
Unless you have an OLPC XO laptop, you will want to say N.
endif # OLPC_XO175

View File

@ -2,4 +2,5 @@
# #
# OLPC XO platform-specific drivers # OLPC XO platform-specific drivers
# #
obj-$(CONFIG_OLPC) += olpc-ec.o obj-$(CONFIG_OLPC_EC) += olpc-ec.o
obj-$(CONFIG_OLPC_XO175_EC) += olpc-xo175-ec.o

View File

@ -15,8 +15,8 @@
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/regulator/driver.h>
#include <linux/olpc-ec.h> #include <linux/olpc-ec.h>
#include <asm/olpc.h>
struct ec_cmd_desc { struct ec_cmd_desc {
u8 cmd; u8 cmd;
@ -32,15 +32,26 @@ struct ec_cmd_desc {
struct olpc_ec_priv { struct olpc_ec_priv {
struct olpc_ec_driver *drv; struct olpc_ec_driver *drv;
u8 version;
struct work_struct worker; struct work_struct worker;
struct mutex cmd_lock; struct mutex cmd_lock;
/* DCON regulator */
struct regulator_dev *dcon_rdev;
bool dcon_enabled;
/* Pending EC commands */ /* Pending EC commands */
struct list_head cmd_q; struct list_head cmd_q;
spinlock_t cmd_q_lock; spinlock_t cmd_q_lock;
struct dentry *dbgfs_dir; struct dentry *dbgfs_dir;
/*
* EC event mask to be applied during suspend (defining wakeup
* sources).
*/
u16 ec_wakeup_mask;
/* /*
* Running an EC command while suspending means we don't always finish * Running an EC command while suspending means we don't always finish
* the command before the machine suspends. This means that the EC * the command before the machine suspends. This means that the EC
@ -118,8 +129,11 @@ int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen)
struct olpc_ec_priv *ec = ec_priv; struct olpc_ec_priv *ec = ec_priv;
struct ec_cmd_desc desc; struct ec_cmd_desc desc;
/* Ensure a driver and ec hook have been registered */ /* Driver not yet registered. */
if (WARN_ON(!ec_driver || !ec_driver->ec_cmd)) if (!ec_driver)
return -EPROBE_DEFER;
if (WARN_ON(!ec_driver->ec_cmd))
return -ENODEV; return -ENODEV;
if (!ec) if (!ec)
@ -149,6 +163,88 @@ int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen)
} }
EXPORT_SYMBOL_GPL(olpc_ec_cmd); EXPORT_SYMBOL_GPL(olpc_ec_cmd);
void olpc_ec_wakeup_set(u16 value)
{
struct olpc_ec_priv *ec = ec_priv;
if (WARN_ON(!ec))
return;
ec->ec_wakeup_mask |= value;
}
EXPORT_SYMBOL_GPL(olpc_ec_wakeup_set);
void olpc_ec_wakeup_clear(u16 value)
{
struct olpc_ec_priv *ec = ec_priv;
if (WARN_ON(!ec))
return;
ec->ec_wakeup_mask &= ~value;
}
EXPORT_SYMBOL_GPL(olpc_ec_wakeup_clear);
int olpc_ec_mask_write(u16 bits)
{
struct olpc_ec_priv *ec = ec_priv;
if (WARN_ON(!ec))
return -ENODEV;
/* EC version 0x5f adds support for wide SCI mask */
if (ec->version >= 0x5f) {
__be16 ec_word = cpu_to_be16(bits);
return olpc_ec_cmd(EC_WRITE_EXT_SCI_MASK, (void *)&ec_word, 2, NULL, 0);
} else {
u8 ec_byte = bits & 0xff;
return olpc_ec_cmd(EC_WRITE_SCI_MASK, &ec_byte, 1, NULL, 0);
}
}
EXPORT_SYMBOL_GPL(olpc_ec_mask_write);
/*
* Returns true if the compile and runtime configurations allow for EC events
* to wake the system.
*/
bool olpc_ec_wakeup_available(void)
{
if (WARN_ON(!ec_driver))
return false;
return ec_driver->wakeup_available;
}
EXPORT_SYMBOL_GPL(olpc_ec_wakeup_available);
int olpc_ec_sci_query(u16 *sci_value)
{
struct olpc_ec_priv *ec = ec_priv;
int ret;
if (WARN_ON(!ec))
return -ENODEV;
/* EC version 0x5f adds support for wide SCI mask */
if (ec->version >= 0x5f) {
__be16 ec_word;
ret = olpc_ec_cmd(EC_EXT_SCI_QUERY, NULL, 0, (void *)&ec_word, 2);
if (ret == 0)
*sci_value = be16_to_cpu(ec_word);
} else {
u8 ec_byte;
ret = olpc_ec_cmd(EC_SCI_QUERY, NULL, 0, &ec_byte, 1);
if (ret == 0)
*sci_value = ec_byte;
}
return ret;
}
EXPORT_SYMBOL_GPL(olpc_ec_sci_query);
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
/* /*
@ -254,9 +350,61 @@ static struct dentry *olpc_ec_setup_debugfs(void)
#endif /* CONFIG_DEBUG_FS */ #endif /* CONFIG_DEBUG_FS */
static int olpc_ec_set_dcon_power(struct olpc_ec_priv *ec, bool state)
{
unsigned char ec_byte = state;
int ret;
if (ec->dcon_enabled == state)
return 0;
ret = olpc_ec_cmd(EC_DCON_POWER_MODE, &ec_byte, 1, NULL, 0);
if (ret)
return ret;
ec->dcon_enabled = state;
return 0;
}
static int dcon_regulator_enable(struct regulator_dev *rdev)
{
struct olpc_ec_priv *ec = rdev_get_drvdata(rdev);
return olpc_ec_set_dcon_power(ec, true);
}
static int dcon_regulator_disable(struct regulator_dev *rdev)
{
struct olpc_ec_priv *ec = rdev_get_drvdata(rdev);
return olpc_ec_set_dcon_power(ec, false);
}
static int dcon_regulator_is_enabled(struct regulator_dev *rdev)
{
struct olpc_ec_priv *ec = rdev_get_drvdata(rdev);
return ec->dcon_enabled ? 1 : 0;
}
static struct regulator_ops dcon_regulator_ops = {
.enable = dcon_regulator_enable,
.disable = dcon_regulator_disable,
.is_enabled = dcon_regulator_is_enabled,
};
static const struct regulator_desc dcon_desc = {
.name = "dcon",
.id = 0,
.ops = &dcon_regulator_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
};
static int olpc_ec_probe(struct platform_device *pdev) static int olpc_ec_probe(struct platform_device *pdev)
{ {
struct olpc_ec_priv *ec; struct olpc_ec_priv *ec;
struct regulator_config config = { };
int err; int err;
if (!ec_driver) if (!ec_driver)
@ -276,14 +424,26 @@ static int olpc_ec_probe(struct platform_device *pdev)
ec_priv = ec; ec_priv = ec;
platform_set_drvdata(pdev, ec); platform_set_drvdata(pdev, ec);
err = ec_driver->probe ? ec_driver->probe(pdev) : 0; /* get the EC revision */
err = olpc_ec_cmd(EC_FIRMWARE_REV, NULL, 0, &ec->version, 1);
if (err) { if (err) {
ec_priv = NULL; ec_priv = NULL;
kfree(ec); kfree(ec);
} else { return err;
ec->dbgfs_dir = olpc_ec_setup_debugfs();
} }
config.dev = pdev->dev.parent;
config.driver_data = ec;
ec->dcon_enabled = true;
ec->dcon_rdev = devm_regulator_register(&pdev->dev, &dcon_desc,
&config);
if (IS_ERR(ec->dcon_rdev)) {
dev_err(&pdev->dev, "failed to register DCON regulator\n");
return PTR_ERR(ec->dcon_rdev);
}
ec->dbgfs_dir = olpc_ec_setup_debugfs();
return err; return err;
} }
@ -293,6 +453,8 @@ static int olpc_ec_suspend(struct device *dev)
struct olpc_ec_priv *ec = platform_get_drvdata(pdev); struct olpc_ec_priv *ec = platform_get_drvdata(pdev);
int err = 0; int err = 0;
olpc_ec_mask_write(ec->ec_wakeup_mask);
if (ec_driver->suspend) if (ec_driver->suspend)
err = ec_driver->suspend(pdev); err = ec_driver->suspend(pdev);
if (!err) if (!err)

View File

@ -0,0 +1,753 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Driver for the OLPC XO-1.75 Embedded Controller.
*
* The EC protocol is documented at:
* http://wiki.laptop.org/go/XO_1.75_HOST_to_EC_Protocol
*
* Copyright (C) 2010 One Laptop per Child Foundation.
* Copyright (C) 2018 Lubomir Rintel <lkundrak@v3.sk>
*/
#include <linux/completion.h>
#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/input.h>
#include <linux/kfifo.h>
#include <linux/module.h>
#include <linux/olpc-ec.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/reboot.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/spi/spi.h>
struct ec_cmd_t {
u8 cmd;
u8 bytes_returned;
};
enum ec_chan_t {
CHAN_NONE = 0,
CHAN_SWITCH,
CHAN_CMD_RESP,
CHAN_KEYBOARD,
CHAN_TOUCHPAD,
CHAN_EVENT,
CHAN_DEBUG,
CHAN_CMD_ERROR,
};
/*
* EC events
*/
#define EVENT_AC_CHANGE 1 /* AC plugged/unplugged */
#define EVENT_BATTERY_STATUS 2 /* Battery low/full/error/gone */
#define EVENT_BATTERY_CRITICAL 3 /* Battery critical voltage */
#define EVENT_BATTERY_SOC_CHANGE 4 /* 1% SOC Change */
#define EVENT_BATTERY_ERROR 5 /* Abnormal error, query for cause */
#define EVENT_POWER_PRESSED 6 /* Power button was pressed */
#define EVENT_POWER_PRESS_WAKE 7 /* Woken up with a power button */
#define EVENT_TIMED_HOST_WAKE 8 /* Host wake timer */
#define EVENT_OLS_HIGH_LIMIT 9 /* OLS crossed dark threshold */
#define EVENT_OLS_LOW_LIMIT 10 /* OLS crossed light threshold */
/*
* EC commands
* (from http://dev.laptop.org/git/users/rsmith/ec-1.75/tree/ec_cmd.h)
*/
#define CMD_GET_API_VERSION 0x08 /* out: u8 */
#define CMD_READ_VOLTAGE 0x10 /* out: u16, *9.76/32, mV */
#define CMD_READ_CURRENT 0x11 /* out: s16, *15.625/120, mA */
#define CMD_READ_ACR 0x12 /* out: s16, *6250/15, uAh */
#define CMD_READ_BATT_TEMPERATURE 0x13 /* out: u16, *100/256, deg C */
#define CMD_READ_AMBIENT_TEMPERATURE 0x14 /* unimplemented, no hardware */
#define CMD_READ_BATTERY_STATUS 0x15 /* out: u8, bitmask */
#define CMD_READ_SOC 0x16 /* out: u8, percentage */
#define CMD_READ_GAUGE_ID 0x17 /* out: u8 * 8 */
#define CMD_READ_GAUGE_DATA 0x18 /* in: u8 addr, out: u8 data */
#define CMD_READ_BOARD_ID 0x19 /* out: u16 (platform id) */
#define CMD_READ_BATT_ERR_CODE 0x1f /* out: u8, error bitmask */
#define CMD_SET_DCON_POWER 0x26 /* in: u8 */
#define CMD_RESET_EC 0x28 /* none */
#define CMD_READ_BATTERY_TYPE 0x2c /* out: u8 */
#define CMD_SET_AUTOWAK 0x33 /* out: u8 */
#define CMD_SET_EC_WAKEUP_TIMER 0x36 /* in: u32, out: ? */
#define CMD_READ_EXT_SCI_MASK 0x37 /* ? */
#define CMD_WRITE_EXT_SCI_MASK 0x38 /* ? */
#define CMD_CLEAR_EC_WAKEUP_TIMER 0x39 /* none */
#define CMD_ENABLE_RUNIN_DISCHARGE 0x3B /* none */
#define CMD_DISABLE_RUNIN_DISCHARGE 0x3C /* none */
#define CMD_READ_MPPT_ACTIVE 0x3d /* out: u8 */
#define CMD_READ_MPPT_LIMIT 0x3e /* out: u8 */
#define CMD_SET_MPPT_LIMIT 0x3f /* in: u8 */
#define CMD_DISABLE_MPPT 0x40 /* none */
#define CMD_ENABLE_MPPT 0x41 /* none */
#define CMD_READ_VIN 0x42 /* out: u16 */
#define CMD_EXT_SCI_QUERY 0x43 /* ? */
#define RSP_KEYBOARD_DATA 0x48 /* ? */
#define RSP_TOUCHPAD_DATA 0x49 /* ? */
#define CMD_GET_FW_VERSION 0x4a /* out: u8 * 16 */
#define CMD_POWER_CYCLE 0x4b /* none */
#define CMD_POWER_OFF 0x4c /* none */
#define CMD_RESET_EC_SOFT 0x4d /* none */
#define CMD_READ_GAUGE_U16 0x4e /* ? */
#define CMD_ENABLE_MOUSE 0x4f /* ? */
#define CMD_ECHO 0x52 /* in: u8 * 5, out: u8 * 5 */
#define CMD_GET_FW_DATE 0x53 /* out: u8 * 16 */
#define CMD_GET_FW_USER 0x54 /* out: u8 * 16 */
#define CMD_TURN_OFF_POWER 0x55 /* none (same as 0x4c) */
#define CMD_READ_OLS 0x56 /* out: u16 */
#define CMD_OLS_SMT_LEDON 0x57 /* none */
#define CMD_OLS_SMT_LEDOFF 0x58 /* none */
#define CMD_START_OLS_ASSY 0x59 /* none */
#define CMD_STOP_OLS_ASSY 0x5a /* none */
#define CMD_OLS_SMTTEST_STOP 0x5b /* none */
#define CMD_READ_VIN_SCALED 0x5c /* out: u16 */
#define CMD_READ_BAT_MIN_W 0x5d /* out: u16 */
#define CMD_READ_BAR_MAX_W 0x5e /* out: u16 */
#define CMD_RESET_BAT_MINMAX_W 0x5f /* none */
#define CMD_READ_LOCATION 0x60 /* in: u16 addr, out: u8 data */
#define CMD_WRITE_LOCATION 0x61 /* in: u16 addr, u8 data */
#define CMD_KEYBOARD_CMD 0x62 /* in: u8, out: ? */
#define CMD_TOUCHPAD_CMD 0x63 /* in: u8, out: ? */
#define CMD_GET_FW_HASH 0x64 /* out: u8 * 16 */
#define CMD_SUSPEND_HINT 0x65 /* in: u8 */
#define CMD_ENABLE_WAKE_TIMER 0x66 /* in: u8 */
#define CMD_SET_WAKE_TIMER 0x67 /* in: 32 */
#define CMD_ENABLE_WAKE_AUTORESET 0x68 /* in: u8 */
#define CMD_OLS_SET_LIMITS 0x69 /* in: u16, u16 */
#define CMD_OLS_GET_LIMITS 0x6a /* out: u16, u16 */
#define CMD_OLS_SET_CEILING 0x6b /* in: u16 */
#define CMD_OLS_GET_CEILING 0x6c /* out: u16 */
/*
* Accepted EC commands, and how many bytes they return. There are plenty
* of EC commands that are no longer implemented, or are implemented only on
* certain older boards.
*/
static const struct ec_cmd_t olpc_xo175_ec_cmds[] = {
{ CMD_GET_API_VERSION, 1 },
{ CMD_READ_VOLTAGE, 2 },
{ CMD_READ_CURRENT, 2 },
{ CMD_READ_ACR, 2 },
{ CMD_READ_BATT_TEMPERATURE, 2 },
{ CMD_READ_BATTERY_STATUS, 1 },
{ CMD_READ_SOC, 1 },
{ CMD_READ_GAUGE_ID, 8 },
{ CMD_READ_GAUGE_DATA, 1 },
{ CMD_READ_BOARD_ID, 2 },
{ CMD_READ_BATT_ERR_CODE, 1 },
{ CMD_SET_DCON_POWER, 0 },
{ CMD_RESET_EC, 0 },
{ CMD_READ_BATTERY_TYPE, 1 },
{ CMD_ENABLE_RUNIN_DISCHARGE, 0 },
{ CMD_DISABLE_RUNIN_DISCHARGE, 0 },
{ CMD_READ_MPPT_ACTIVE, 1 },
{ CMD_READ_MPPT_LIMIT, 1 },
{ CMD_SET_MPPT_LIMIT, 0 },
{ CMD_DISABLE_MPPT, 0 },
{ CMD_ENABLE_MPPT, 0 },
{ CMD_READ_VIN, 2 },
{ CMD_GET_FW_VERSION, 16 },
{ CMD_POWER_CYCLE, 0 },
{ CMD_POWER_OFF, 0 },
{ CMD_RESET_EC_SOFT, 0 },
{ CMD_ECHO, 5 },
{ CMD_GET_FW_DATE, 16 },
{ CMD_GET_FW_USER, 16 },
{ CMD_TURN_OFF_POWER, 0 },
{ CMD_READ_OLS, 2 },
{ CMD_OLS_SMT_LEDON, 0 },
{ CMD_OLS_SMT_LEDOFF, 0 },
{ CMD_START_OLS_ASSY, 0 },
{ CMD_STOP_OLS_ASSY, 0 },
{ CMD_OLS_SMTTEST_STOP, 0 },
{ CMD_READ_VIN_SCALED, 2 },
{ CMD_READ_BAT_MIN_W, 2 },
{ CMD_READ_BAR_MAX_W, 2 },
{ CMD_RESET_BAT_MINMAX_W, 0 },
{ CMD_READ_LOCATION, 1 },
{ CMD_WRITE_LOCATION, 0 },
{ CMD_GET_FW_HASH, 16 },
{ CMD_SUSPEND_HINT, 0 },
{ CMD_ENABLE_WAKE_TIMER, 0 },
{ CMD_SET_WAKE_TIMER, 0 },
{ CMD_ENABLE_WAKE_AUTORESET, 0 },
{ CMD_OLS_SET_LIMITS, 0 },
{ CMD_OLS_GET_LIMITS, 4 },
{ CMD_OLS_SET_CEILING, 0 },
{ CMD_OLS_GET_CEILING, 2 },
{ CMD_READ_EXT_SCI_MASK, 2 },
{ CMD_WRITE_EXT_SCI_MASK, 0 },
{ }
};
#define EC_MAX_CMD_DATA_LEN 5
#define EC_MAX_RESP_LEN 16
#define LOG_BUF_SIZE 128
#define PM_WAKEUP_TIME 1000
#define EC_ALL_EVENTS GENMASK(15, 0)
enum ec_state_t {
CMD_STATE_IDLE = 0,
CMD_STATE_WAITING_FOR_SWITCH,
CMD_STATE_CMD_IN_TX_FIFO,
CMD_STATE_CMD_SENT,
CMD_STATE_RESP_RECEIVED,
CMD_STATE_ERROR_RECEIVED,
};
struct olpc_xo175_ec_cmd {
u8 command;
u8 nr_args;
u8 data_len;
u8 args[EC_MAX_CMD_DATA_LEN];
};
struct olpc_xo175_ec_resp {
u8 channel;
u8 byte;
};
struct olpc_xo175_ec {
bool suspended;
/* SPI related stuff. */
struct spi_device *spi;
struct spi_transfer xfer;
struct spi_message msg;
union {
struct olpc_xo175_ec_cmd cmd;
struct olpc_xo175_ec_resp resp;
} tx_buf, rx_buf;
/* GPIO for the CMD signals. */
struct gpio_desc *gpio_cmd;
/* Command handling related state. */
spinlock_t cmd_state_lock;
int cmd_state;
bool cmd_running;
struct completion cmd_done;
struct olpc_xo175_ec_cmd cmd;
u8 resp_data[EC_MAX_RESP_LEN];
int expected_resp_len;
int resp_len;
/* Power button. */
struct input_dev *pwrbtn;
/* Debug handling. */
char logbuf[LOG_BUF_SIZE];
int logbuf_len;
};
static struct platform_device *olpc_ec;
static int olpc_xo175_ec_resp_len(u8 cmd)
{
const struct ec_cmd_t *p;
for (p = olpc_xo175_ec_cmds; p->cmd; p++) {
if (p->cmd == cmd)
return p->bytes_returned;
}
return -EINVAL;
}
static void olpc_xo175_ec_flush_logbuf(struct olpc_xo175_ec *priv)
{
dev_dbg(&priv->spi->dev, "got debug string [%*pE]\n",
priv->logbuf_len, priv->logbuf);
priv->logbuf_len = 0;
}
static void olpc_xo175_ec_complete(void *arg);
static void olpc_xo175_ec_send_command(struct olpc_xo175_ec *priv, void *cmd,
size_t cmdlen)
{
int ret;
memcpy(&priv->tx_buf, cmd, cmdlen);
priv->xfer.len = cmdlen;
spi_message_init_with_transfers(&priv->msg, &priv->xfer, 1);
priv->msg.complete = olpc_xo175_ec_complete;
priv->msg.context = priv;
ret = spi_async(priv->spi, &priv->msg);
if (ret)
dev_err(&priv->spi->dev, "spi_async() failed %d\n", ret);
}
static void olpc_xo175_ec_read_packet(struct olpc_xo175_ec *priv)
{
u8 nonce[] = {0xA5, 0x5A};
olpc_xo175_ec_send_command(priv, nonce, sizeof(nonce));
}
static void olpc_xo175_ec_complete(void *arg)
{
struct olpc_xo175_ec *priv = arg;
struct device *dev = &priv->spi->dev;
struct power_supply *psy;
unsigned long flags;
u8 channel;
u8 byte;
int ret;
ret = priv->msg.status;
if (ret) {
dev_err(dev, "SPI transfer failed: %d\n", ret);
spin_lock_irqsave(&priv->cmd_state_lock, flags);
if (priv->cmd_running) {
priv->resp_len = 0;
priv->cmd_state = CMD_STATE_ERROR_RECEIVED;
complete(&priv->cmd_done);
}
spin_unlock_irqrestore(&priv->cmd_state_lock, flags);
if (ret != -EINTR)
olpc_xo175_ec_read_packet(priv);
return;
}
channel = priv->rx_buf.resp.channel;
byte = priv->rx_buf.resp.byte;
switch (channel) {
case CHAN_NONE:
spin_lock_irqsave(&priv->cmd_state_lock, flags);
if (!priv->cmd_running) {
/* We can safely ignore these */
dev_err(dev, "spurious FIFO read packet\n");
spin_unlock_irqrestore(&priv->cmd_state_lock, flags);
return;
}
priv->cmd_state = CMD_STATE_CMD_SENT;
if (!priv->expected_resp_len)
complete(&priv->cmd_done);
olpc_xo175_ec_read_packet(priv);
spin_unlock_irqrestore(&priv->cmd_state_lock, flags);
return;
case CHAN_SWITCH:
spin_lock_irqsave(&priv->cmd_state_lock, flags);
if (!priv->cmd_running) {
/* Just go with the flow */
dev_err(dev, "spurious SWITCH packet\n");
memset(&priv->cmd, 0, sizeof(priv->cmd));
priv->cmd.command = CMD_ECHO;
}
priv->cmd_state = CMD_STATE_CMD_IN_TX_FIFO;
/* Throw command into TxFIFO */
gpiod_set_value_cansleep(priv->gpio_cmd, 0);
olpc_xo175_ec_send_command(priv, &priv->cmd, sizeof(priv->cmd));
spin_unlock_irqrestore(&priv->cmd_state_lock, flags);
return;
case CHAN_CMD_RESP:
spin_lock_irqsave(&priv->cmd_state_lock, flags);
if (!priv->cmd_running) {
dev_err(dev, "spurious response packet\n");
} else if (priv->resp_len >= priv->expected_resp_len) {
dev_err(dev, "too many response packets\n");
} else {
priv->resp_data[priv->resp_len++] = byte;
if (priv->resp_len == priv->expected_resp_len) {
priv->cmd_state = CMD_STATE_RESP_RECEIVED;
complete(&priv->cmd_done);
}
}
spin_unlock_irqrestore(&priv->cmd_state_lock, flags);
break;
case CHAN_CMD_ERROR:
spin_lock_irqsave(&priv->cmd_state_lock, flags);
if (!priv->cmd_running) {
dev_err(dev, "spurious cmd error packet\n");
} else {
priv->resp_data[0] = byte;
priv->resp_len = 1;
priv->cmd_state = CMD_STATE_ERROR_RECEIVED;
complete(&priv->cmd_done);
}
spin_unlock_irqrestore(&priv->cmd_state_lock, flags);
break;
case CHAN_KEYBOARD:
dev_warn(dev, "keyboard is not supported\n");
break;
case CHAN_TOUCHPAD:
dev_warn(dev, "touchpad is not supported\n");
break;
case CHAN_EVENT:
dev_dbg(dev, "got event %.2x\n", byte);
switch (byte) {
case EVENT_AC_CHANGE:
psy = power_supply_get_by_name("olpc-ac");
if (psy) {
power_supply_changed(psy);
power_supply_put(psy);
}
break;
case EVENT_BATTERY_STATUS:
case EVENT_BATTERY_CRITICAL:
case EVENT_BATTERY_SOC_CHANGE:
case EVENT_BATTERY_ERROR:
psy = power_supply_get_by_name("olpc-battery");
if (psy) {
power_supply_changed(psy);
power_supply_put(psy);
}
break;
case EVENT_POWER_PRESSED:
input_report_key(priv->pwrbtn, KEY_POWER, 1);
input_sync(priv->pwrbtn);
input_report_key(priv->pwrbtn, KEY_POWER, 0);
input_sync(priv->pwrbtn);
/* fall through */
case EVENT_POWER_PRESS_WAKE:
case EVENT_TIMED_HOST_WAKE:
pm_wakeup_event(priv->pwrbtn->dev.parent,
PM_WAKEUP_TIME);
break;
default:
dev_dbg(dev, "ignored unknown event %.2x\n", byte);
break;
}
break;
case CHAN_DEBUG:
if (byte == '\n') {
olpc_xo175_ec_flush_logbuf(priv);
} else if (isprint(byte)) {
priv->logbuf[priv->logbuf_len++] = byte;
if (priv->logbuf_len == LOG_BUF_SIZE)
olpc_xo175_ec_flush_logbuf(priv);
}
break;
default:
dev_warn(dev, "unknown channel: %d, %.2x\n", channel, byte);
break;
}
/* Most non-command packets get the TxFIFO refilled and an ACK. */
olpc_xo175_ec_read_packet(priv);
}
/*
* This function is protected with a mutex. We can safely assume that
* there will be only one instance of this function running at a time.
* One of the ways in which we enforce this is by waiting until we get
* all response bytes back from the EC, rather than just the number that
* the caller requests (otherwise, we might start a new command while an
* old command's response bytes are still incoming).
*/
static int olpc_xo175_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *resp,
size_t resp_len, void *ec_cb_arg)
{
struct olpc_xo175_ec *priv = ec_cb_arg;
struct device *dev = &priv->spi->dev;
unsigned long flags;
size_t nr_bytes;
int ret = 0;
dev_dbg(dev, "CMD %x, %zd bytes expected\n", cmd, resp_len);
if (inlen > 5) {
dev_err(dev, "command len %zd too big!\n", resp_len);
return -EOVERFLOW;
}
/* Suspending in the middle of an EC command hoses things badly! */
if (WARN_ON(priv->suspended))
return -EBUSY;
/* Ensure a valid command and return bytes */
ret = olpc_xo175_ec_resp_len(cmd);
if (ret < 0) {
dev_err_ratelimited(dev, "unknown command 0x%x\n", cmd);
/*
* Assume the best in our callers, and allow unknown commands
* through. I'm not the charitable type, but it was beaten
* into me. Just maintain a minimum standard of sanity.
*/
if (resp_len > sizeof(priv->resp_data)) {
dev_err(dev, "response too big: %zd!\n", resp_len);
return -EOVERFLOW;
}
nr_bytes = resp_len;
} else {
nr_bytes = (size_t)ret;
ret = 0;
}
resp_len = min(resp_len, nr_bytes);
spin_lock_irqsave(&priv->cmd_state_lock, flags);
/* Initialize the state machine */
init_completion(&priv->cmd_done);
priv->cmd_running = true;
priv->cmd_state = CMD_STATE_WAITING_FOR_SWITCH;
memset(&priv->cmd, 0, sizeof(priv->cmd));
priv->cmd.command = cmd;
priv->cmd.nr_args = inlen;
priv->cmd.data_len = 0;
memcpy(priv->cmd.args, inbuf, inlen);
priv->expected_resp_len = nr_bytes;
priv->resp_len = 0;
/* Tickle the cmd gpio to get things started */
gpiod_set_value_cansleep(priv->gpio_cmd, 1);
spin_unlock_irqrestore(&priv->cmd_state_lock, flags);
/* The irq handler should do the rest */
if (!wait_for_completion_timeout(&priv->cmd_done,
msecs_to_jiffies(4000))) {
dev_err(dev, "EC cmd error: timeout in STATE %d\n",
priv->cmd_state);
gpiod_set_value_cansleep(priv->gpio_cmd, 0);
spi_slave_abort(priv->spi);
olpc_xo175_ec_read_packet(priv);
return -ETIMEDOUT;
}
spin_lock_irqsave(&priv->cmd_state_lock, flags);
/* Deal with the results. */
if (priv->cmd_state == CMD_STATE_ERROR_RECEIVED) {
/* EC-provided error is in the single response byte */
dev_err(dev, "command 0x%x returned error 0x%x\n",
cmd, priv->resp_data[0]);
ret = -EREMOTEIO;
} else if (priv->resp_len != nr_bytes) {
dev_err(dev, "command 0x%x returned %d bytes, expected %zd bytes\n",
cmd, priv->resp_len, nr_bytes);
ret = -EREMOTEIO;
} else {
/*
* We may have 8 bytes in priv->resp, but we only care about
* what we've been asked for. If the caller asked for only 2
* bytes, give them that. We've guaranteed that
* resp_len <= priv->resp_len and priv->resp_len == nr_bytes.
*/
memcpy(resp, priv->resp_data, resp_len);
}
/* This should already be low, but just in case. */
gpiod_set_value_cansleep(priv->gpio_cmd, 0);
priv->cmd_running = false;
spin_unlock_irqrestore(&priv->cmd_state_lock, flags);
return ret;
}
static int olpc_xo175_ec_set_event_mask(unsigned int mask)
{
u8 args[2];
args[0] = mask >> 0;
args[1] = mask >> 8;
return olpc_ec_cmd(CMD_WRITE_EXT_SCI_MASK, args, 2, NULL, 0);
}
static void olpc_xo175_ec_power_off(void)
{
while (1) {
olpc_ec_cmd(CMD_POWER_OFF, NULL, 0, NULL, 0);
mdelay(1000);
}
}
static int __maybe_unused olpc_xo175_ec_suspend(struct device *dev)
{
struct olpc_xo175_ec *priv = dev_get_drvdata(dev);
static struct {
u8 suspend;
u32 suspend_count;
} __packed hintargs;
static unsigned int suspend_count;
/*
* SOC_SLEEP is not wired to the EC on B3 and earlier boards.
* This command lets the EC know instead. The suspend count doesn't seem
* to be used anywhere but in the EC debug output.
*/
hintargs.suspend = 1;
hintargs.suspend_count = suspend_count++;
olpc_ec_cmd(CMD_SUSPEND_HINT, (void *)&hintargs, sizeof(hintargs),
NULL, 0);
/*
* After we've sent the suspend hint, don't allow further EC commands
* to be run until we've resumed. Userspace tasks should be frozen,
* but kernel threads and interrupts could still schedule EC commands.
*/
priv->suspended = true;
return 0;
}
static int __maybe_unused olpc_xo175_ec_resume_noirq(struct device *dev)
{
struct olpc_xo175_ec *priv = dev_get_drvdata(dev);
priv->suspended = false;
return 0;
}
static int __maybe_unused olpc_xo175_ec_resume(struct device *dev)
{
u8 x = 0;
/*
* The resume hint is only needed if no other commands are
* being sent during resume. all it does is tell the EC
* the SoC is definitely awake.
*/
olpc_ec_cmd(CMD_SUSPEND_HINT, &x, 1, NULL, 0);
/* Enable all EC events while we're awake */
olpc_xo175_ec_set_event_mask(EC_ALL_EVENTS);
return 0;
}
static struct olpc_ec_driver olpc_xo175_ec_driver = {
.ec_cmd = olpc_xo175_ec_cmd,
};
static int olpc_xo175_ec_remove(struct spi_device *spi)
{
if (pm_power_off == olpc_xo175_ec_power_off)
pm_power_off = NULL;
spi_slave_abort(spi);
platform_device_unregister(olpc_ec);
olpc_ec = NULL;
return 0;
}
static int olpc_xo175_ec_probe(struct spi_device *spi)
{
struct olpc_xo175_ec *priv;
int ret;
if (olpc_ec) {
dev_err(&spi->dev, "OLPC EC already registered.\n");
return -EBUSY;
}
priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->gpio_cmd = devm_gpiod_get(&spi->dev, "cmd", GPIOD_OUT_LOW);
if (IS_ERR(priv->gpio_cmd)) {
dev_err(&spi->dev, "failed to get cmd gpio: %ld\n",
PTR_ERR(priv->gpio_cmd));
return PTR_ERR(priv->gpio_cmd);
}
priv->spi = spi;
spin_lock_init(&priv->cmd_state_lock);
priv->cmd_state = CMD_STATE_IDLE;
init_completion(&priv->cmd_done);
priv->logbuf_len = 0;
/* Set up power button input device */
priv->pwrbtn = devm_input_allocate_device(&spi->dev);
if (!priv->pwrbtn)
return -ENOMEM;
priv->pwrbtn->name = "Power Button";
priv->pwrbtn->dev.parent = &spi->dev;
input_set_capability(priv->pwrbtn, EV_KEY, KEY_POWER);
ret = input_register_device(priv->pwrbtn);
if (ret) {
dev_err(&spi->dev, "error registering input device: %d\n", ret);
return ret;
}
spi_set_drvdata(spi, priv);
priv->xfer.rx_buf = &priv->rx_buf;
priv->xfer.tx_buf = &priv->tx_buf;
olpc_xo175_ec_read_packet(priv);
olpc_ec_driver_register(&olpc_xo175_ec_driver, priv);
olpc_ec = platform_device_register_resndata(&spi->dev, "olpc-ec", -1,
NULL, 0, NULL, 0);
/* Enable all EC events while we're awake */
olpc_xo175_ec_set_event_mask(EC_ALL_EVENTS);
if (pm_power_off == NULL)
pm_power_off = olpc_xo175_ec_power_off;
dev_info(&spi->dev, "OLPC XO-1.75 Embedded Controller driver\n");
return 0;
}
static const struct dev_pm_ops olpc_xo175_ec_pm_ops = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL, olpc_xo175_ec_resume_noirq)
SET_RUNTIME_PM_OPS(olpc_xo175_ec_suspend, olpc_xo175_ec_resume, NULL)
};
static const struct of_device_id olpc_xo175_ec_of_match[] = {
{ .compatible = "olpc,xo1.75-ec" },
{ }
};
MODULE_DEVICE_TABLE(of, olpc_xo175_ec_of_match);
static struct spi_driver olpc_xo175_ec_spi_driver = {
.driver = {
.name = "olpc-xo175-ec",
.of_match_table = olpc_xo175_ec_of_match,
.pm = &olpc_xo175_ec_pm_ops,
},
.probe = olpc_xo175_ec_probe,
.remove = olpc_xo175_ec_remove,
};
module_spi_driver(olpc_xo175_ec_spi_driver);
MODULE_DESCRIPTION("OLPC XO-1.75 Embedded Controller driver");
MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>"); /* Functionality */
MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>"); /* Bugs */
MODULE_LICENSE("GPL");

View File

@ -778,6 +778,16 @@ config INTEL_WMI_THUNDERBOLT
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 intel-wmi-thunderbolt. be called intel-wmi-thunderbolt.
config XIAOMI_WMI
tristate "Xiaomi WMI key driver"
depends on ACPI_WMI
depends on INPUT
help
Say Y here if you want to support WMI-based keys on Xiaomi notebooks.
To compile this driver as a module, choose M here: the module will
be called xiaomi-wmi.
config MSI_WMI config MSI_WMI
tristate "MSI WMI extras" tristate "MSI WMI extras"
depends on ACPI_WMI depends on ACPI_WMI
@ -903,7 +913,6 @@ config TOSHIBA_WMI
config ACPI_CMPC config ACPI_CMPC
tristate "CMPC Laptop Extras" tristate "CMPC Laptop Extras"
depends on ACPI && INPUT depends on ACPI && INPUT
depends on BACKLIGHT_LCD_SUPPORT
depends on RFKILL || RFKILL=n depends on RFKILL || RFKILL=n
select BACKLIGHT_CLASS_DEVICE select BACKLIGHT_CLASS_DEVICE
help help
@ -1127,7 +1136,6 @@ config INTEL_OAKTRAIL
config SAMSUNG_Q10 config SAMSUNG_Q10
tristate "Samsung Q10 Extras" tristate "Samsung Q10 Extras"
depends on ACPI depends on ACPI
depends on BACKLIGHT_LCD_SUPPORT
select BACKLIGHT_CLASS_DEVICE select BACKLIGHT_CLASS_DEVICE
---help--- ---help---
This driver provides support for backlight control on Samsung Q10 This driver provides support for backlight control on Samsung Q10
@ -1314,7 +1322,7 @@ config HUAWEI_WMI
config PCENGINES_APU2 config PCENGINES_APU2
tristate "PC Engines APUv2/3 front button and LEDs driver" tristate "PC Engines APUv2/3 front button and LEDs driver"
depends on INPUT && INPUT_KEYBOARD depends on INPUT && INPUT_KEYBOARD && GPIOLIB
depends on LEDS_CLASS depends on LEDS_CLASS
select GPIO_AMD_FCH select GPIO_AMD_FCH
select KEYBOARD_GPIO_POLLED select KEYBOARD_GPIO_POLLED
@ -1326,6 +1334,8 @@ config PCENGINES_APU2
To compile this driver as a module, choose M here: the module To compile this driver as a module, choose M here: the module
will be called pcengines-apuv2. will be called pcengines-apuv2.
source "drivers/platform/x86/intel_speed_select_if/Kconfig"
endif # X86_PLATFORM_DEVICES endif # X86_PLATFORM_DEVICES
config PMC_ATOM config PMC_ATOM

View File

@ -51,6 +51,7 @@ obj-$(CONFIG_SURFACE3_WMI) += surface3-wmi.o
obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o
obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o
obj-$(CONFIG_INTEL_WMI_THUNDERBOLT) += intel-wmi-thunderbolt.o obj-$(CONFIG_INTEL_WMI_THUNDERBOLT) += intel-wmi-thunderbolt.o
obj-$(CONFIG_XIAOMI_WMI) += xiaomi-wmi.o
# toshiba_acpi must link after wmi to ensure that wmi devices are found # toshiba_acpi must link after wmi to ensure that wmi devices are found
# before toshiba_acpi initializes # before toshiba_acpi initializes
@ -89,7 +90,7 @@ obj-$(CONFIG_INTEL_BXTWC_PMIC_TMU) += intel_bxtwc_tmu.o
obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.o \ obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.o \
intel_telemetry_pltdrv.o \ intel_telemetry_pltdrv.o \
intel_telemetry_debugfs.o intel_telemetry_debugfs.o
obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o intel_pmc_core_pltdrv.o
obj-$(CONFIG_PMC_ATOM) += pmc_atom.o obj-$(CONFIG_PMC_ATOM) += pmc_atom.o
obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o obj-$(CONFIG_MLX_PLATFORM) += mlx-platform.o
obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o
@ -98,3 +99,4 @@ obj-$(CONFIG_INTEL_MRFLD_PWRBTN) += intel_mrfld_pwrbtn.o
obj-$(CONFIG_I2C_MULTI_INSTANTIATE) += i2c-multi-instantiate.o obj-$(CONFIG_I2C_MULTI_INSTANTIATE) += i2c-multi-instantiate.o
obj-$(CONFIG_INTEL_ATOMISP2_PM) += intel_atomisp2_pm.o obj-$(CONFIG_INTEL_ATOMISP2_PM) += intel_atomisp2_pm.o
obj-$(CONFIG_PCENGINES_APU2) += pcengines-apuv2.o obj-$(CONFIG_PCENGINES_APU2) += pcengines-apuv2.o
obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += intel_speed_select_if/

View File

@ -259,7 +259,6 @@ struct acer_data {
struct acer_debug { struct acer_debug {
struct dentry *root; struct dentry *root;
struct dentry *devices;
u32 wmid_devices; u32 wmid_devices;
}; };
@ -1002,6 +1001,7 @@ static acpi_status WMID_get_u32(u32 *value, u32 cap)
*value = tmp & 0x1; *value = tmp & 0x1;
return 0; return 0;
} }
/* fall through */
default: default:
return AE_ERROR; return AE_ERROR;
} }
@ -1328,6 +1328,7 @@ static acpi_status get_u32(u32 *value, u32 cap)
status = AMW0_get_u32(value, cap); status = AMW0_get_u32(value, cap);
break; break;
} }
/* fall through */
case ACER_WMID: case ACER_WMID:
status = WMID_get_u32(value, cap); status = WMID_get_u32(value, cap);
break; break;
@ -1370,6 +1371,7 @@ static acpi_status set_u32(u32 value, u32 cap)
return AMW0_set_u32(value, cap); return AMW0_set_u32(value, cap);
} }
/* fall through */
case ACER_WMID: case ACER_WMID:
return WMID_set_u32(value, cap); return WMID_set_u32(value, cap);
case ACER_WMID_v2: case ACER_WMID_v2:
@ -1379,6 +1381,7 @@ static acpi_status set_u32(u32 value, u32 cap)
return wmid_v2_set_u32(value, cap); return wmid_v2_set_u32(value, cap);
else if (wmi_has_guid(WMID_GUID2)) else if (wmi_has_guid(WMID_GUID2))
return WMID_set_u32(value, cap); return WMID_set_u32(value, cap);
/* fall through */
default: default:
return AE_BAD_PARAMETER; return AE_BAD_PARAMETER;
} }
@ -2148,29 +2151,15 @@ static struct platform_device *acer_platform_device;
static void remove_debugfs(void) static void remove_debugfs(void)
{ {
debugfs_remove(interface->debug.devices); debugfs_remove_recursive(interface->debug.root);
debugfs_remove(interface->debug.root);
} }
static int __init create_debugfs(void) static void __init create_debugfs(void)
{ {
interface->debug.root = debugfs_create_dir("acer-wmi", NULL); interface->debug.root = debugfs_create_dir("acer-wmi", NULL);
if (!interface->debug.root) {
pr_err("Failed to create debugfs directory");
return -ENOMEM;
}
interface->debug.devices = debugfs_create_u32("devices", S_IRUGO, debugfs_create_u32("devices", S_IRUGO, interface->debug.root,
interface->debug.root, &interface->debug.wmid_devices);
&interface->debug.wmid_devices);
if (!interface->debug.devices)
goto error_debugfs;
return 0;
error_debugfs:
remove_debugfs();
return -ENOMEM;
} }
static int __init acer_wmi_init(void) static int __init acer_wmi_init(void)
@ -2300,9 +2289,7 @@ static int __init acer_wmi_init(void)
if (wmi_has_guid(WMID_GUID2)) { if (wmi_has_guid(WMID_GUID2)) {
interface->debug.wmid_devices = get_wmid_devices(); interface->debug.wmid_devices = get_wmid_devices();
err = create_debugfs(); create_debugfs();
if (err)
goto error_create_debugfs;
} }
/* Override any initial settings with values from the commandline */ /* Override any initial settings with values from the commandline */
@ -2310,8 +2297,6 @@ static int __init acer_wmi_init(void)
return 0; return 0;
error_create_debugfs:
platform_device_del(acer_platform_device);
error_device_add: error_device_add:
platform_device_put(acer_platform_device); platform_device_put(acer_platform_device);
error_device_alloc: error_device_alloc:

View File

@ -463,6 +463,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
{ KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } }, { KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } },
{ KE_IGNORE, 0x6E, }, /* Low Battery notification */ { KE_IGNORE, 0x6E, }, /* Low Battery notification */
{ KE_KEY, 0x7a, { KEY_ALS_TOGGLE } }, /* Ambient Light Sensor Toggle */ { KE_KEY, 0x7a, { KEY_ALS_TOGGLE } }, /* Ambient Light Sensor Toggle */
{ KE_KEY, 0x7c, { KEY_MICMUTE } },
{ KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */ { KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */
{ KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */ { KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */
{ KE_KEY, 0x82, { KEY_CAMERA } }, { KE_KEY, 0x82, { KEY_CAMERA } },
@ -477,7 +478,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
{ KE_KEY, 0x92, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + DVI */ { KE_KEY, 0x92, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + DVI */
{ KE_KEY, 0x93, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + DVI */ { KE_KEY, 0x93, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + DVI */
{ KE_KEY, 0x95, { KEY_MEDIA } }, { KE_KEY, 0x95, { KEY_MEDIA } },
{ KE_KEY, 0x99, { KEY_PHONE } }, { KE_KEY, 0x99, { KEY_PHONE } }, /* Conflicts with fan mode switch */
{ KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */ { KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */
{ KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */ { KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */
{ KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */ { KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */

View File

@ -57,6 +57,7 @@ MODULE_LICENSE("GPL");
#define NOTIFY_KBD_BRTUP 0xc4 #define NOTIFY_KBD_BRTUP 0xc4
#define NOTIFY_KBD_BRTDWN 0xc5 #define NOTIFY_KBD_BRTDWN 0xc5
#define NOTIFY_KBD_BRTTOGGLE 0xc7 #define NOTIFY_KBD_BRTTOGGLE 0xc7
#define NOTIFY_KBD_FBM 0x99
#define ASUS_WMI_FNLOCK_BIOS_DISABLED BIT(0) #define ASUS_WMI_FNLOCK_BIOS_DISABLED BIT(0)
@ -67,9 +68,27 @@ MODULE_LICENSE("GPL");
#define ASUS_FAN_CTRL_MANUAL 1 #define ASUS_FAN_CTRL_MANUAL 1
#define ASUS_FAN_CTRL_AUTO 2 #define ASUS_FAN_CTRL_AUTO 2
#define ASUS_FAN_MODE_NORMAL 0
#define ASUS_FAN_MODE_OVERBOOST 1
#define ASUS_FAN_MODE_OVERBOOST_MASK 0x01
#define ASUS_FAN_MODE_SILENT 2
#define ASUS_FAN_MODE_SILENT_MASK 0x02
#define ASUS_FAN_MODES_MASK 0x03
#define USB_INTEL_XUSB2PR 0xD0 #define USB_INTEL_XUSB2PR 0xD0
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
#define ASUS_ACPI_UID_ASUSWMI "ASUSWMI"
#define ASUS_ACPI_UID_ATK "ATK"
#define WMI_EVENT_QUEUE_SIZE 0x10
#define WMI_EVENT_QUEUE_END 0x1
#define WMI_EVENT_MASK 0xFFFF
/* The WMI hotkey event value is always the same. */
#define WMI_EVENT_VALUE_ATK 0xFF
#define WMI_EVENT_MASK 0xFFFF
static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL }; static const char * const ashs_ids[] = { "ATK4001", "ATK4002", NULL };
static bool ashs_present(void) static bool ashs_present(void)
@ -85,6 +104,7 @@ static bool ashs_present(void)
struct bios_args { struct bios_args {
u32 arg0; u32 arg0;
u32 arg1; u32 arg1;
u32 arg2; /* At least TUF Gaming series uses 3 dword input buffer. */
} __packed; } __packed;
/* /*
@ -132,6 +152,7 @@ struct asus_wmi {
int dsts_id; int dsts_id;
int spec; int spec;
int sfun; int sfun;
bool wmi_event_queue;
struct input_dev *inputdev; struct input_dev *inputdev;
struct backlight_device *backlight_device; struct backlight_device *backlight_device;
@ -161,6 +182,10 @@ struct asus_wmi {
int asus_hwmon_num_fans; int asus_hwmon_num_fans;
int asus_hwmon_pwm; int asus_hwmon_pwm;
bool fan_mode_available;
u8 fan_mode_mask;
u8 fan_mode;
struct hotplug_slot hotplug_slot; struct hotplug_slot hotplug_slot;
struct mutex hotplug_lock; struct mutex hotplug_lock;
struct mutex wmi_lock; struct mutex wmi_lock;
@ -174,6 +199,8 @@ struct asus_wmi {
struct asus_wmi_driver *driver; struct asus_wmi_driver *driver;
}; };
/* Input **********************************************************************/
static int asus_wmi_input_init(struct asus_wmi *asus) static int asus_wmi_input_init(struct asus_wmi *asus)
{ {
int err; int err;
@ -211,11 +238,15 @@ static void asus_wmi_input_exit(struct asus_wmi *asus)
asus->inputdev = NULL; asus->inputdev = NULL;
} }
int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval) /* WMI ************************************************************************/
static int asus_wmi_evaluate_method3(u32 method_id,
u32 arg0, u32 arg1, u32 arg2, u32 *retval)
{ {
struct bios_args args = { struct bios_args args = {
.arg0 = arg0, .arg0 = arg0,
.arg1 = arg1, .arg1 = arg1,
.arg2 = arg2,
}; };
struct acpi_buffer input = { (acpi_size) sizeof(args), &args }; struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
@ -227,7 +258,7 @@ int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval)
&input, &output); &input, &output);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
goto exit; return -EIO;
obj = (union acpi_object *)output.pointer; obj = (union acpi_object *)output.pointer;
if (obj && obj->type == ACPI_TYPE_INTEGER) if (obj && obj->type == ACPI_TYPE_INTEGER)
@ -238,15 +269,16 @@ int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval)
kfree(obj); kfree(obj);
exit:
if (ACPI_FAILURE(status))
return -EIO;
if (tmp == ASUS_WMI_UNSUPPORTED_METHOD) if (tmp == ASUS_WMI_UNSUPPORTED_METHOD)
return -ENODEV; return -ENODEV;
return 0; return 0;
} }
int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval)
{
return asus_wmi_evaluate_method3(method_id, arg0, arg1, 0, retval);
}
EXPORT_SYMBOL_GPL(asus_wmi_evaluate_method); EXPORT_SYMBOL_GPL(asus_wmi_evaluate_method);
static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args) static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args)
@ -320,9 +352,8 @@ static int asus_wmi_get_devstate_simple(struct asus_wmi *asus, u32 dev_id)
ASUS_WMI_DSTS_STATUS_BIT); ASUS_WMI_DSTS_STATUS_BIT);
} }
/* /* LEDs ***********************************************************************/
* LEDs
*/
/* /*
* These functions actually update the LED's, and are called from a * These functions actually update the LED's, and are called from a
* workqueue. By doing this as separate work rather than when the LED * workqueue. By doing this as separate work rather than when the LED
@ -427,6 +458,10 @@ static void do_kbd_led_set(struct led_classdev *led_cdev, int value)
static void kbd_led_set(struct led_classdev *led_cdev, static void kbd_led_set(struct led_classdev *led_cdev,
enum led_brightness value) enum led_brightness value)
{ {
/* Prevent disabling keyboard backlight on module unregister */
if (led_cdev->flags & LED_UNREGISTERING)
return;
do_kbd_led_set(led_cdev, value); do_kbd_led_set(led_cdev, value);
} }
@ -582,8 +617,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
goto error; goto error;
} }
led_val = kbd_led_read(asus, NULL, NULL); if (!kbd_led_read(asus, &led_val, NULL)) {
if (led_val >= 0) {
asus->kbd_led_wk = led_val; asus->kbd_led_wk = led_val;
asus->kbd_led.name = "asus::kbd_backlight"; asus->kbd_led.name = "asus::kbd_backlight";
asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED; asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
@ -633,6 +667,7 @@ error:
return rv; return rv;
} }
/* RF *************************************************************************/
/* /*
* PCI hotplug (for wlan rfkill) * PCI hotplug (for wlan rfkill)
@ -1055,6 +1090,8 @@ exit:
return result; return result;
} }
/* Quirks *********************************************************************/
static void asus_wmi_set_xusb2pr(struct asus_wmi *asus) static void asus_wmi_set_xusb2pr(struct asus_wmi *asus)
{ {
struct pci_dev *xhci_pdev; struct pci_dev *xhci_pdev;
@ -1087,9 +1124,8 @@ static void asus_wmi_set_als(void)
asus_wmi_set_devstate(ASUS_WMI_DEVID_ALS_ENABLE, 1, NULL); asus_wmi_set_devstate(ASUS_WMI_DEVID_ALS_ENABLE, 1, NULL);
} }
/* /* Hwmon device ***************************************************************/
* Hwmon device
*/
static int asus_hwmon_agfn_fan_speed_read(struct asus_wmi *asus, int fan, static int asus_hwmon_agfn_fan_speed_read(struct asus_wmi *asus, int fan,
int *speed) int *speed)
{ {
@ -1353,8 +1389,7 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,
struct attribute *attr, int idx) struct attribute *attr, int idx)
{ {
struct device *dev = container_of(kobj, struct device, kobj); struct device *dev = container_of(kobj, struct device, kobj);
struct platform_device *pdev = to_platform_device(dev->parent); struct asus_wmi *asus = dev_get_drvdata(dev->parent);
struct asus_wmi *asus = platform_get_drvdata(pdev);
int dev_id = -1; int dev_id = -1;
int fan_attr = -1; int fan_attr = -1;
u32 value = ASUS_WMI_UNSUPPORTED_METHOD; u32 value = ASUS_WMI_UNSUPPORTED_METHOD;
@ -1395,8 +1430,11 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,
else else
ok = fan_attr <= asus->asus_hwmon_num_fans; ok = fan_attr <= asus->asus_hwmon_num_fans;
} else if (dev_id == ASUS_WMI_DEVID_THERMAL_CTRL) { } else if (dev_id == ASUS_WMI_DEVID_THERMAL_CTRL) {
/* If value is zero, something is clearly wrong */ /*
if (!value) * If the temperature value in deci-Kelvin is near the absolute
* zero temperature, something is clearly wrong
*/
if (value == 0 || value == 1)
ok = false; ok = false;
} else if (fan_attr <= asus->asus_hwmon_num_fans && fan_attr != -1) { } else if (fan_attr <= asus->asus_hwmon_num_fans && fan_attr != -1) {
ok = true; ok = true;
@ -1415,11 +1453,12 @@ __ATTRIBUTE_GROUPS(hwmon_attribute);
static int asus_wmi_hwmon_init(struct asus_wmi *asus) static int asus_wmi_hwmon_init(struct asus_wmi *asus)
{ {
struct device *dev = &asus->platform_device->dev;
struct device *hwmon; struct device *hwmon;
hwmon = hwmon_device_register_with_groups(&asus->platform_device->dev, hwmon = devm_hwmon_device_register_with_groups(dev, "asus", asus,
"asus", asus, hwmon_attribute_groups);
hwmon_attribute_groups);
if (IS_ERR(hwmon)) { if (IS_ERR(hwmon)) {
pr_err("Could not register asus hwmon device\n"); pr_err("Could not register asus hwmon device\n");
return PTR_ERR(hwmon); return PTR_ERR(hwmon);
@ -1427,9 +1466,137 @@ static int asus_wmi_hwmon_init(struct asus_wmi *asus)
return 0; return 0;
} }
/* static int asus_wmi_fan_init(struct asus_wmi *asus)
* Backlight {
*/ int status;
asus->asus_hwmon_pwm = -1;
asus->asus_hwmon_num_fans = -1;
asus->asus_hwmon_fan_manual_mode = false;
status = asus_hwmon_get_fan_number(asus, &asus->asus_hwmon_num_fans);
if (status) {
asus->asus_hwmon_num_fans = 0;
pr_warn("Could not determine number of fans: %d\n", status);
return -ENXIO;
}
pr_info("Number of fans: %d\n", asus->asus_hwmon_num_fans);
return 0;
}
/* Fan mode *******************************************************************/
static int fan_mode_check_present(struct asus_wmi *asus)
{
u32 result;
int err;
asus->fan_mode_available = false;
err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_MODE, &result);
if (err) {
if (err == -ENODEV)
return 0;
else
return err;
}
if ((result & ASUS_WMI_DSTS_PRESENCE_BIT) &&
(result & ASUS_FAN_MODES_MASK)) {
asus->fan_mode_available = true;
asus->fan_mode_mask = result & ASUS_FAN_MODES_MASK;
}
return 0;
}
static int fan_mode_write(struct asus_wmi *asus)
{
int err;
u8 value;
u32 retval;
value = asus->fan_mode;
pr_info("Set fan mode: %u\n", value);
err = asus_wmi_set_devstate(ASUS_WMI_DEVID_FAN_MODE, value, &retval);
if (err) {
pr_warn("Failed to set fan mode: %d\n", err);
return err;
}
if (retval != 1) {
pr_warn("Failed to set fan mode (retval): 0x%x\n", retval);
return -EIO;
}
return 0;
}
static int fan_mode_switch_next(struct asus_wmi *asus)
{
if (asus->fan_mode == ASUS_FAN_MODE_NORMAL) {
if (asus->fan_mode_mask & ASUS_FAN_MODE_OVERBOOST_MASK)
asus->fan_mode = ASUS_FAN_MODE_OVERBOOST;
else if (asus->fan_mode_mask & ASUS_FAN_MODE_SILENT_MASK)
asus->fan_mode = ASUS_FAN_MODE_SILENT;
} else if (asus->fan_mode == ASUS_FAN_MODE_OVERBOOST) {
if (asus->fan_mode_mask & ASUS_FAN_MODE_SILENT_MASK)
asus->fan_mode = ASUS_FAN_MODE_SILENT;
else
asus->fan_mode = ASUS_FAN_MODE_NORMAL;
} else {
asus->fan_mode = ASUS_FAN_MODE_NORMAL;
}
return fan_mode_write(asus);
}
static ssize_t fan_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct asus_wmi *asus = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%d\n", asus->fan_mode);
}
static ssize_t fan_mode_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int result;
u8 new_mode;
struct asus_wmi *asus = dev_get_drvdata(dev);
result = kstrtou8(buf, 10, &new_mode);
if (result < 0) {
pr_warn("Trying to store invalid value\n");
return result;
}
if (new_mode == ASUS_FAN_MODE_OVERBOOST) {
if (!(asus->fan_mode_mask & ASUS_FAN_MODE_OVERBOOST_MASK))
return -EINVAL;
} else if (new_mode == ASUS_FAN_MODE_SILENT) {
if (!(asus->fan_mode_mask & ASUS_FAN_MODE_SILENT_MASK))
return -EINVAL;
} else if (new_mode != ASUS_FAN_MODE_NORMAL) {
return -EINVAL;
}
asus->fan_mode = new_mode;
fan_mode_write(asus);
return result;
}
// Fan mode: 0 - normal, 1 - overboost, 2 - silent
static DEVICE_ATTR_RW(fan_mode);
/* Backlight ******************************************************************/
static int read_backlight_power(struct asus_wmi *asus) static int read_backlight_power(struct asus_wmi *asus)
{ {
int ret; int ret;
@ -1611,6 +1778,8 @@ static int is_display_toggle(int code)
return 0; return 0;
} }
/* Fn-lock ********************************************************************/
static bool asus_wmi_has_fnlock_key(struct asus_wmi *asus) static bool asus_wmi_has_fnlock_key(struct asus_wmi *asus)
{ {
u32 result; u32 result;
@ -1628,88 +1797,148 @@ static void asus_wmi_fnlock_update(struct asus_wmi *asus)
asus_wmi_set_devstate(ASUS_WMI_DEVID_FNLOCK, mode, NULL); asus_wmi_set_devstate(ASUS_WMI_DEVID_FNLOCK, mode, NULL);
} }
static void asus_wmi_notify(u32 value, void *context) /* WMI events *****************************************************************/
static int asus_wmi_get_event_code(u32 value)
{ {
struct asus_wmi *asus = context;
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj; union acpi_object *obj;
acpi_status status; acpi_status status;
int code; int code;
int orig_code;
unsigned int key_value = 1;
bool autorelease = 1;
status = wmi_get_event_data(value, &response); status = wmi_get_event_data(value, &response);
if (status != AE_OK) { if (ACPI_FAILURE(status)) {
pr_err("bad event status 0x%x\n", status); pr_warn("Failed to get WMI notify code: %s\n",
return; acpi_format_exception(status));
return -EIO;
} }
obj = (union acpi_object *)response.pointer; obj = (union acpi_object *)response.pointer;
if (!obj || obj->type != ACPI_TYPE_INTEGER) if (obj && obj->type == ACPI_TYPE_INTEGER)
goto exit; code = (int)(obj->integer.value & WMI_EVENT_MASK);
else
code = -EIO;
kfree(obj);
return code;
}
static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus)
{
int orig_code;
unsigned int key_value = 1;
bool autorelease = 1;
code = obj->integer.value;
orig_code = code; orig_code = code;
if (asus->driver->key_filter) { if (asus->driver->key_filter) {
asus->driver->key_filter(asus->driver, &code, &key_value, asus->driver->key_filter(asus->driver, &code, &key_value,
&autorelease); &autorelease);
if (code == ASUS_WMI_KEY_IGNORE) if (code == ASUS_WMI_KEY_IGNORE)
goto exit; return;
} }
if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX) if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
code = ASUS_WMI_BRN_UP; code = ASUS_WMI_BRN_UP;
else if (code >= NOTIFY_BRNDOWN_MIN && else if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX)
code <= NOTIFY_BRNDOWN_MAX)
code = ASUS_WMI_BRN_DOWN; code = ASUS_WMI_BRN_DOWN;
if (code == ASUS_WMI_BRN_DOWN || code == ASUS_WMI_BRN_UP) { if (code == ASUS_WMI_BRN_DOWN || code == ASUS_WMI_BRN_UP) {
if (acpi_video_get_backlight_type() == acpi_backlight_vendor) { if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
asus_wmi_backlight_notify(asus, orig_code); asus_wmi_backlight_notify(asus, orig_code);
goto exit; return;
} }
} }
if (code == NOTIFY_KBD_BRTUP) { if (code == NOTIFY_KBD_BRTUP) {
kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1); kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1);
goto exit; return;
} }
if (code == NOTIFY_KBD_BRTDWN) { if (code == NOTIFY_KBD_BRTDWN) {
kbd_led_set_by_kbd(asus, asus->kbd_led_wk - 1); kbd_led_set_by_kbd(asus, asus->kbd_led_wk - 1);
goto exit; return;
} }
if (code == NOTIFY_KBD_BRTTOGGLE) { if (code == NOTIFY_KBD_BRTTOGGLE) {
if (asus->kbd_led_wk == asus->kbd_led.max_brightness) if (asus->kbd_led_wk == asus->kbd_led.max_brightness)
kbd_led_set_by_kbd(asus, 0); kbd_led_set_by_kbd(asus, 0);
else else
kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1); kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1);
goto exit; return;
} }
if (code == NOTIFY_FNLOCK_TOGGLE) { if (code == NOTIFY_FNLOCK_TOGGLE) {
asus->fnlock_locked = !asus->fnlock_locked; asus->fnlock_locked = !asus->fnlock_locked;
asus_wmi_fnlock_update(asus); asus_wmi_fnlock_update(asus);
goto exit; return;
} }
if (is_display_toggle(code) && if (asus->fan_mode_available && code == NOTIFY_KBD_FBM) {
asus->driver->quirks->no_display_toggle) fan_mode_switch_next(asus);
goto exit; return;
}
if (is_display_toggle(code) && asus->driver->quirks->no_display_toggle)
return;
if (!sparse_keymap_report_event(asus->inputdev, code, if (!sparse_keymap_report_event(asus->inputdev, code,
key_value, autorelease)) key_value, autorelease))
pr_info("Unknown key %x pressed\n", code); pr_info("Unknown key %x pressed\n", code);
exit:
kfree(obj);
} }
/* static void asus_wmi_notify(u32 value, void *context)
* Sys helpers {
*/ struct asus_wmi *asus = context;
int code;
int i;
for (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) {
code = asus_wmi_get_event_code(value);
if (code < 0) {
pr_warn("Failed to get notify code: %d\n", code);
return;
}
if (code == WMI_EVENT_QUEUE_END || code == WMI_EVENT_MASK)
return;
asus_wmi_handle_event_code(code, asus);
/*
* Double check that queue is present:
* ATK (with queue) uses 0xff, ASUSWMI (without) 0xd2.
*/
if (!asus->wmi_event_queue || value != WMI_EVENT_VALUE_ATK)
return;
}
pr_warn("Failed to process event queue, last code: 0x%x\n", code);
}
static int asus_wmi_notify_queue_flush(struct asus_wmi *asus)
{
int code;
int i;
for (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) {
code = asus_wmi_get_event_code(WMI_EVENT_VALUE_ATK);
if (code < 0) {
pr_warn("Failed to get event during flush: %d\n", code);
return code;
}
if (code == WMI_EVENT_QUEUE_END || code == WMI_EVENT_MASK)
return 0;
}
pr_warn("Failed to flush event queue\n");
return -EIO;
}
/* Sysfs **********************************************************************/
static int parse_arg(const char *buf, unsigned long count, int *val) static int parse_arg(const char *buf, unsigned long count, int *val)
{ {
if (!count) if (!count)
@ -1805,6 +2034,7 @@ static struct attribute *platform_attributes[] = {
&dev_attr_touchpad.attr, &dev_attr_touchpad.attr,
&dev_attr_lid_resume.attr, &dev_attr_lid_resume.attr,
&dev_attr_als_enable.attr, &dev_attr_als_enable.attr,
&dev_attr_fan_mode.attr,
NULL NULL
}; };
@ -1826,6 +2056,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
devid = ASUS_WMI_DEVID_LID_RESUME; devid = ASUS_WMI_DEVID_LID_RESUME;
else if (attr == &dev_attr_als_enable.attr) else if (attr == &dev_attr_als_enable.attr)
devid = ASUS_WMI_DEVID_ALS_ENABLE; devid = ASUS_WMI_DEVID_ALS_ENABLE;
else if (attr == &dev_attr_fan_mode.attr)
ok = asus->fan_mode_available;
if (devid != -1) if (devid != -1)
ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0); ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0);
@ -1848,11 +2080,12 @@ static int asus_wmi_sysfs_init(struct platform_device *device)
return sysfs_create_group(&device->dev.kobj, &platform_attribute_group); return sysfs_create_group(&device->dev.kobj, &platform_attribute_group);
} }
/* /* Platform device ************************************************************/
* Platform device
*/
static int asus_wmi_platform_init(struct asus_wmi *asus) static int asus_wmi_platform_init(struct asus_wmi *asus)
{ {
struct device *dev = &asus->platform_device->dev;
char *wmi_uid;
int rv; int rv;
/* INIT enable hotkeys on some models */ /* INIT enable hotkeys on some models */
@ -1882,11 +2115,41 @@ static int asus_wmi_platform_init(struct asus_wmi *asus)
* Note, on most Eeepc, there is no way to check if a method exist * Note, on most Eeepc, there is no way to check if a method exist
* or note, while on notebooks, they returns 0xFFFFFFFE on failure, * or note, while on notebooks, they returns 0xFFFFFFFE on failure,
* but once again, SPEC may probably be used for that kind of things. * but once again, SPEC may probably be used for that kind of things.
*
* Additionally at least TUF Gaming series laptops return nothing for
* unknown methods, so the detection in this way is not possible.
*
* There is strong indication that only ACPI WMI devices that have _UID
* equal to "ASUSWMI" use DCTS whereas those with "ATK" use DSTS.
*/ */
if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, 0, 0, NULL)) wmi_uid = wmi_get_acpi_device_uid(ASUS_WMI_MGMT_GUID);
if (!wmi_uid)
return -ENODEV;
if (!strcmp(wmi_uid, ASUS_ACPI_UID_ASUSWMI)) {
dev_info(dev, "Detected ASUSWMI, use DCTS\n");
asus->dsts_id = ASUS_WMI_METHODID_DCTS;
} else {
dev_info(dev, "Detected %s, not ASUSWMI, use DSTS\n", wmi_uid);
asus->dsts_id = ASUS_WMI_METHODID_DSTS; asus->dsts_id = ASUS_WMI_METHODID_DSTS;
else }
asus->dsts_id = ASUS_WMI_METHODID_DSTS2;
/*
* Some devices can have multiple event codes stored in a queue before
* the module load if it was unloaded intermittently after calling
* the INIT method (enables event handling). The WMI notify handler is
* expected to retrieve all event codes until a retrieved code equals
* queue end marker (One or Ones). Old codes are flushed from the queue
* upon module load. Not enabling this when it should be has minimal
* visible impact so fall back if anything goes wrong.
*/
wmi_uid = wmi_get_acpi_device_uid(asus->driver->event_guid);
if (wmi_uid && !strcmp(wmi_uid, ASUS_ACPI_UID_ATK)) {
dev_info(dev, "Detected ATK, enable event queue\n");
if (!asus_wmi_notify_queue_flush(asus))
asus->wmi_event_queue = true;
}
/* CWAP allow to define the behavior of the Fn+F2 key, /* CWAP allow to define the behavior of the Fn+F2 key,
* this method doesn't seems to be present on Eee PCs */ * this method doesn't seems to be present on Eee PCs */
@ -1894,17 +2157,11 @@ static int asus_wmi_platform_init(struct asus_wmi *asus)
asus_wmi_set_devstate(ASUS_WMI_DEVID_CWAP, asus_wmi_set_devstate(ASUS_WMI_DEVID_CWAP,
asus->driver->quirks->wapf, NULL); asus->driver->quirks->wapf, NULL);
return asus_wmi_sysfs_init(asus->platform_device); return 0;
} }
static void asus_wmi_platform_exit(struct asus_wmi *asus) /* debugfs ********************************************************************/
{
asus_wmi_sysfs_exit(asus->platform_device);
}
/*
* debugfs
*/
struct asus_wmi_debugfs_node { struct asus_wmi_debugfs_node {
struct asus_wmi *asus; struct asus_wmi *asus;
char *name; char *name;
@ -2005,74 +2262,33 @@ static void asus_wmi_debugfs_exit(struct asus_wmi *asus)
debugfs_remove_recursive(asus->debug.root); debugfs_remove_recursive(asus->debug.root);
} }
static int asus_wmi_debugfs_init(struct asus_wmi *asus) static void asus_wmi_debugfs_init(struct asus_wmi *asus)
{ {
struct dentry *dent;
int i; int i;
asus->debug.root = debugfs_create_dir(asus->driver->name, NULL); asus->debug.root = debugfs_create_dir(asus->driver->name, NULL);
if (!asus->debug.root) {
pr_err("failed to create debugfs directory\n");
goto error_debugfs;
}
dent = debugfs_create_x32("method_id", S_IRUGO | S_IWUSR, debugfs_create_x32("method_id", S_IRUGO | S_IWUSR, asus->debug.root,
asus->debug.root, &asus->debug.method_id); &asus->debug.method_id);
if (!dent)
goto error_debugfs;
dent = debugfs_create_x32("dev_id", S_IRUGO | S_IWUSR, debugfs_create_x32("dev_id", S_IRUGO | S_IWUSR, asus->debug.root,
asus->debug.root, &asus->debug.dev_id); &asus->debug.dev_id);
if (!dent)
goto error_debugfs;
dent = debugfs_create_x32("ctrl_param", S_IRUGO | S_IWUSR, debugfs_create_x32("ctrl_param", S_IRUGO | S_IWUSR, asus->debug.root,
asus->debug.root, &asus->debug.ctrl_param); &asus->debug.ctrl_param);
if (!dent)
goto error_debugfs;
for (i = 0; i < ARRAY_SIZE(asus_wmi_debug_files); i++) { for (i = 0; i < ARRAY_SIZE(asus_wmi_debug_files); i++) {
struct asus_wmi_debugfs_node *node = &asus_wmi_debug_files[i]; struct asus_wmi_debugfs_node *node = &asus_wmi_debug_files[i];
node->asus = asus; node->asus = asus;
dent = debugfs_create_file(node->name, S_IFREG | S_IRUGO, debugfs_create_file(node->name, S_IFREG | S_IRUGO,
asus->debug.root, node, asus->debug.root, node,
&asus_wmi_debugfs_io_ops); &asus_wmi_debugfs_io_ops);
if (!dent) {
pr_err("failed to create debug file: %s\n", node->name);
goto error_debugfs;
}
} }
return 0;
error_debugfs:
asus_wmi_debugfs_exit(asus);
return -ENOMEM;
} }
static int asus_wmi_fan_init(struct asus_wmi *asus) /* Init / exit ****************************************************************/
{
int status;
asus->asus_hwmon_pwm = -1;
asus->asus_hwmon_num_fans = -1;
asus->asus_hwmon_fan_manual_mode = false;
status = asus_hwmon_get_fan_number(asus, &asus->asus_hwmon_num_fans);
if (status) {
asus->asus_hwmon_num_fans = 0;
pr_warn("Could not determine number of fans: %d\n", status);
return -ENXIO;
}
pr_info("Number of fans: %d\n", asus->asus_hwmon_num_fans);
return 0;
}
/*
* WMI Driver
*/
static int asus_wmi_add(struct platform_device *pdev) static int asus_wmi_add(struct platform_device *pdev)
{ {
struct platform_driver *pdrv = to_platform_driver(pdev->dev.driver); struct platform_driver *pdrv = to_platform_driver(pdev->dev.driver);
@ -2099,6 +2315,14 @@ static int asus_wmi_add(struct platform_device *pdev)
if (err) if (err)
goto fail_platform; goto fail_platform;
err = fan_mode_check_present(asus);
if (err)
goto fail_fan_mode;
err = asus_wmi_sysfs_init(asus->platform_device);
if (err)
goto fail_sysfs;
err = asus_wmi_input_init(asus); err = asus_wmi_input_init(asus);
if (err) if (err)
goto fail_input; goto fail_input;
@ -2162,14 +2386,10 @@ static int asus_wmi_add(struct platform_device *pdev)
goto fail_wmi_handler; goto fail_wmi_handler;
} }
err = asus_wmi_debugfs_init(asus); asus_wmi_debugfs_init(asus);
if (err)
goto fail_debugfs;
return 0; return 0;
fail_debugfs:
wmi_remove_notify_handler(asus->driver->event_guid);
fail_wmi_handler: fail_wmi_handler:
asus_wmi_backlight_exit(asus); asus_wmi_backlight_exit(asus);
fail_backlight: fail_backlight:
@ -2180,7 +2400,9 @@ fail_leds:
fail_hwmon: fail_hwmon:
asus_wmi_input_exit(asus); asus_wmi_input_exit(asus);
fail_input: fail_input:
asus_wmi_platform_exit(asus); asus_wmi_sysfs_exit(asus->platform_device);
fail_sysfs:
fail_fan_mode:
fail_platform: fail_platform:
kfree(asus); kfree(asus);
return err; return err;
@ -2197,16 +2419,15 @@ static int asus_wmi_remove(struct platform_device *device)
asus_wmi_led_exit(asus); asus_wmi_led_exit(asus);
asus_wmi_rfkill_exit(asus); asus_wmi_rfkill_exit(asus);
asus_wmi_debugfs_exit(asus); asus_wmi_debugfs_exit(asus);
asus_wmi_platform_exit(asus); asus_wmi_sysfs_exit(asus->platform_device);
asus_hwmon_fan_set_auto(asus); asus_hwmon_fan_set_auto(asus);
kfree(asus); kfree(asus);
return 0; return 0;
} }
/* /* Platform driver - hibernate/resume callbacks *******************************/
* Platform driver - hibernate/resume callbacks
*/
static int asus_hotk_thaw(struct device *device) static int asus_hotk_thaw(struct device *device)
{ {
struct asus_wmi *asus = dev_get_drvdata(device); struct asus_wmi *asus = dev_get_drvdata(device);
@ -2282,6 +2503,8 @@ static const struct dev_pm_ops asus_pm_ops = {
.resume = asus_hotk_resume, .resume = asus_hotk_resume,
}; };
/* Registration ***************************************************************/
static int asus_wmi_probe(struct platform_device *pdev) static int asus_wmi_probe(struct platform_device *pdev)
{ {
struct platform_driver *pdrv = to_platform_driver(pdev->dev.driver); struct platform_driver *pdrv = to_platform_driver(pdev->dev.driver);

View File

@ -2173,9 +2173,8 @@ static int __init dell_init(void)
kbd_led_init(&platform_device->dev); kbd_led_init(&platform_device->dev);
dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL); dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL);
if (dell_laptop_dir != NULL) debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL,
debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL, &dell_debugfs_fops);
&dell_debugfs_fops);
dell_laptop_register_notifier(&dell_laptop_notifier); dell_laptop_register_notifier(&dell_laptop_notifier);

View File

@ -143,7 +143,7 @@ fail_smbios_cmd:
return ret; return ret;
} }
static int dell_smbios_wmi_probe(struct wmi_device *wdev) static int dell_smbios_wmi_probe(struct wmi_device *wdev, const void *context)
{ {
struct wmi_driver *wdriver = struct wmi_driver *wdriver =
container_of(wdev->dev.driver, struct wmi_driver, driver); container_of(wdev->dev.driver, struct wmi_driver, driver);

View File

@ -98,7 +98,8 @@ EXPORT_SYMBOL_GPL(dell_wmi_get_hotfix);
* WMI buffer length 12 4 <length> * WMI buffer length 12 4 <length>
* WMI hotfix number 16 4 <hotfix> * WMI hotfix number 16 4 <hotfix>
*/ */
static int dell_wmi_descriptor_probe(struct wmi_device *wdev) static int dell_wmi_descriptor_probe(struct wmi_device *wdev,
const void *context)
{ {
union acpi_object *obj = NULL; union acpi_object *obj = NULL;
struct descriptor_priv *priv; struct descriptor_priv *priv;

View File

@ -659,7 +659,7 @@ static int dell_wmi_events_set_enabled(bool enable)
return dell_smbios_error(ret); return dell_smbios_error(ret);
} }
static int dell_wmi_probe(struct wmi_device *wdev) static int dell_wmi_probe(struct wmi_device *wdev, const void *context)
{ {
struct dell_wmi_priv *priv; struct dell_wmi_priv *priv;
int ret; int ret;

View File

@ -229,6 +229,7 @@ static const struct dmi_system_id lis3lv02d_dmi_ids[] = {
AXIS_DMI_MATCH("HPB440G3", "HP ProBook 440 G3", x_inverted_usd), AXIS_DMI_MATCH("HPB440G3", "HP ProBook 440 G3", x_inverted_usd),
AXIS_DMI_MATCH("HPB440G4", "HP ProBook 440 G4", x_inverted), AXIS_DMI_MATCH("HPB440G4", "HP ProBook 440 G4", x_inverted),
AXIS_DMI_MATCH("HPB442x", "HP ProBook 442", xy_rotated_left), AXIS_DMI_MATCH("HPB442x", "HP ProBook 442", xy_rotated_left),
AXIS_DMI_MATCH("HPB450G0", "HP ProBook 450 G0", x_inverted),
AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted), AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted),
AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap), AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap),
AXIS_DMI_MATCH("HPB532x", "HP ProBook 532", y_inverted), AXIS_DMI_MATCH("HPB532x", "HP ProBook 532", y_inverted),

View File

@ -166,7 +166,7 @@ static int huawei_wmi_input_setup(struct wmi_device *wdev)
return input_register_device(priv->idev); return input_register_device(priv->idev);
} }
static int huawei_wmi_probe(struct wmi_device *wdev) static int huawei_wmi_probe(struct wmi_device *wdev, const void *context)
{ {
struct huawei_wmi_priv *priv; struct huawei_wmi_priv *priv;
int err; int err;

View File

@ -316,34 +316,15 @@ static int debugfs_cfg_show(struct seq_file *s, void *data)
} }
DEFINE_SHOW_ATTRIBUTE(debugfs_cfg); DEFINE_SHOW_ATTRIBUTE(debugfs_cfg);
static int ideapad_debugfs_init(struct ideapad_private *priv) static void ideapad_debugfs_init(struct ideapad_private *priv)
{ {
struct dentry *node; struct dentry *dir;
priv->debug = debugfs_create_dir("ideapad", NULL); dir = debugfs_create_dir("ideapad", NULL);
if (priv->debug == NULL) { priv->debug = dir;
pr_err("failed to create debugfs directory");
goto errout;
}
node = debugfs_create_file("cfg", S_IRUGO, priv->debug, priv, debugfs_create_file("cfg", S_IRUGO, dir, priv, &debugfs_cfg_fops);
&debugfs_cfg_fops); debugfs_create_file("status", S_IRUGO, dir, priv, &debugfs_status_fops);
if (!node) {
pr_err("failed to create cfg in debugfs");
goto errout;
}
node = debugfs_create_file("status", S_IRUGO, priv->debug, priv,
&debugfs_status_fops);
if (!node) {
pr_err("failed to create status in debugfs");
goto errout;
}
return 0;
errout:
return -ENOMEM;
} }
static void ideapad_debugfs_exit(struct ideapad_private *priv) static void ideapad_debugfs_exit(struct ideapad_private *priv)
@ -1012,9 +993,7 @@ static int ideapad_acpi_add(struct platform_device *pdev)
if (ret) if (ret)
return ret; return ret;
ret = ideapad_debugfs_init(priv); ideapad_debugfs_init(priv);
if (ret)
goto debugfs_failed;
ret = ideapad_input_init(priv); ret = ideapad_input_init(priv);
if (ret) if (ret)
@ -1071,7 +1050,6 @@ backlight_failed:
ideapad_input_exit(priv); ideapad_input_exit(priv);
input_failed: input_failed:
ideapad_debugfs_exit(priv); ideapad_debugfs_exit(priv);
debugfs_failed:
ideapad_sysfs_exit(priv); ideapad_sysfs_exit(priv);
return ret; return ret;
} }

View File

@ -56,7 +56,8 @@ static const struct attribute_group tbt_attribute_group = {
.attrs = tbt_attrs, .attrs = tbt_attrs,
}; };
static int intel_wmi_thunderbolt_probe(struct wmi_device *wdev) static int intel_wmi_thunderbolt_probe(struct wmi_device *wdev,
const void *context)
{ {
int ret; int ret;

View File

@ -51,17 +51,6 @@
#define GPE0A_STS_PORT 0x420 #define GPE0A_STS_PORT 0x420
#define GPE0A_EN_PORT 0x428 #define GPE0A_EN_PORT 0x428
#define BAYTRAIL 0x01
#define CHERRYTRAIL 0x02
#define ICPU(model, data) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, data }
static const struct x86_cpu_id int0002_cpu_ids[] = {
ICPU(INTEL_FAM6_ATOM_SILVERMONT, BAYTRAIL), /* Valleyview, Bay Trail */
ICPU(INTEL_FAM6_ATOM_AIRMONT, CHERRYTRAIL), /* Braswell, Cherry Trail */
{}
};
/* /*
* As this is not a real GPIO at all, but just a hack to model an event in * As this is not a real GPIO at all, but just a hack to model an event in
* ACPI the get / set functions are dummy functions. * ACPI the get / set functions are dummy functions.
@ -157,6 +146,12 @@ static struct irq_chip int0002_cht_irqchip = {
*/ */
}; };
static const struct x86_cpu_id int0002_cpu_ids[] = {
INTEL_CPU_FAM6(ATOM_SILVERMONT, int0002_byt_irqchip), /* Valleyview, Bay Trail */
INTEL_CPU_FAM6(ATOM_AIRMONT, int0002_cht_irqchip), /* Braswell, Cherry Trail */
{}
};
static int int0002_probe(struct platform_device *pdev) static int int0002_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
@ -210,10 +205,7 @@ static int int0002_probe(struct platform_device *pdev)
return ret; return ret;
} }
if (cpu_id->driver_data == BAYTRAIL) irq_chip = (struct irq_chip *)cpu_id->driver_data;
irq_chip = &int0002_byt_irqchip;
else
irq_chip = &int0002_cht_irqchip;
ret = gpiochip_irqchip_add(chip, irq_chip, 0, handle_edge_irq, ret = gpiochip_irqchip_add(chip, irq_chip, 0, handle_edge_irq,
IRQ_TYPE_NONE); IRQ_TYPE_NONE);

View File

@ -180,9 +180,13 @@ static int intel_menlow_memory_add(struct acpi_device *device)
static int intel_menlow_memory_remove(struct acpi_device *device) static int intel_menlow_memory_remove(struct acpi_device *device)
{ {
struct thermal_cooling_device *cdev = acpi_driver_data(device); struct thermal_cooling_device *cdev;
if (!device || !cdev) if (!device)
return -EINVAL;
cdev = acpi_driver_data(device);
if (!cdev)
return -EINVAL; return -EINVAL;
sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); sysfs_remove_link(&device->dev.kobj, "thermal_cooling");

View File

@ -26,6 +26,7 @@
#include <asm/cpu_device_id.h> #include <asm/cpu_device_id.h>
#include <asm/intel-family.h> #include <asm/intel-family.h>
#include <asm/msr.h> #include <asm/msr.h>
#include <asm/tsc.h>
#include "intel_pmc_core.h" #include "intel_pmc_core.h"
@ -740,7 +741,9 @@ static int pmc_core_pkgc_show(struct seq_file *s, void *unused)
if (rdmsrl_safe(map[index].bit_mask, &pcstate_count)) if (rdmsrl_safe(map[index].bit_mask, &pcstate_count))
continue; continue;
seq_printf(s, "%-8s : 0x%llx\n", map[index].name, pcstate_count *= 1000;
do_div(pcstate_count, tsc_khz);
seq_printf(s, "%-8s : %llu\n", map[index].name,
pcstate_count); pcstate_count);
} }
@ -753,14 +756,11 @@ static void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev)
debugfs_remove_recursive(pmcdev->dbgfs_dir); debugfs_remove_recursive(pmcdev->dbgfs_dir);
} }
static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev) static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
{ {
struct dentry *dir; struct dentry *dir;
dir = debugfs_create_dir("pmc_core", NULL); dir = debugfs_create_dir("pmc_core", NULL);
if (!dir)
return -ENOMEM;
pmcdev->dbgfs_dir = dir; pmcdev->dbgfs_dir = dir;
debugfs_create_file("slp_s0_residency_usec", 0444, dir, pmcdev, debugfs_create_file("slp_s0_residency_usec", 0444, dir, pmcdev,
@ -794,13 +794,10 @@ static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
debugfs_create_bool("slp_s0_dbg_latch", 0644, debugfs_create_bool("slp_s0_dbg_latch", 0644,
dir, &slps0_dbg_latch); dir, &slps0_dbg_latch);
} }
return 0;
} }
#else #else
static inline int pmc_core_dbgfs_register(struct pmc_dev *pmcdev) static inline void pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
{ {
return 0;
} }
static inline void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev) static inline void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev)
@ -862,7 +859,6 @@ static int pmc_core_probe(struct platform_device *pdev)
struct pmc_dev *pmcdev = &pmc; struct pmc_dev *pmcdev = &pmc;
const struct x86_cpu_id *cpu_id; const struct x86_cpu_id *cpu_id;
u64 slp_s0_addr; u64 slp_s0_addr;
int err;
if (device_initialized) if (device_initialized)
return -ENODEV; return -ENODEV;
@ -896,12 +892,7 @@ static int pmc_core_probe(struct platform_device *pdev)
pmcdev->pmc_xram_read_bit = pmc_core_check_read_lock_bit(); pmcdev->pmc_xram_read_bit = pmc_core_check_read_lock_bit();
dmi_check_system(pmc_core_dmi_table); dmi_check_system(pmc_core_dmi_table);
err = pmc_core_dbgfs_register(pmcdev); pmc_core_dbgfs_register(pmcdev);
if (err < 0) {
dev_warn(&pdev->dev, "debugfs register failed.\n");
iounmap(pmcdev->regbase);
return err;
}
device_initialized = true; device_initialized = true;
dev_info(&pdev->dev, " initialized\n"); dev_info(&pdev->dev, " initialized\n");
@ -1023,47 +1014,23 @@ static const struct dev_pm_ops pmc_core_pm_ops = {
SET_LATE_SYSTEM_SLEEP_PM_OPS(pmc_core_suspend, pmc_core_resume) SET_LATE_SYSTEM_SLEEP_PM_OPS(pmc_core_suspend, pmc_core_resume)
}; };
static const struct acpi_device_id pmc_core_acpi_ids[] = {
{"INT33A1", 0}, /* _HID for Intel Power Engine, _CID PNP0D80*/
{ }
};
MODULE_DEVICE_TABLE(acpi, pmc_core_acpi_ids);
static struct platform_driver pmc_core_driver = { static struct platform_driver pmc_core_driver = {
.driver = { .driver = {
.name = "intel_pmc_core", .name = "intel_pmc_core",
.acpi_match_table = ACPI_PTR(pmc_core_acpi_ids),
.pm = &pmc_core_pm_ops, .pm = &pmc_core_pm_ops,
}, },
.probe = pmc_core_probe, .probe = pmc_core_probe,
.remove = pmc_core_remove, .remove = pmc_core_remove,
}; };
static struct platform_device pmc_core_device = { module_platform_driver(pmc_core_driver);
.name = "intel_pmc_core",
};
static int __init pmc_core_init(void)
{
int ret;
if (!x86_match_cpu(intel_pmc_core_ids))
return -ENODEV;
ret = platform_driver_register(&pmc_core_driver);
if (ret)
return ret;
ret = platform_device_register(&pmc_core_device);
if (ret) {
platform_driver_unregister(&pmc_core_driver);
return ret;
}
return 0;
}
static void __exit pmc_core_exit(void)
{
platform_device_unregister(&pmc_core_device);
platform_driver_unregister(&pmc_core_driver);
}
module_init(pmc_core_init)
module_exit(pmc_core_exit)
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Intel PMC Core Driver"); MODULE_DESCRIPTION("Intel PMC Core Driver");

View File

@ -0,0 +1,62 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Intel PMC Core platform init
* Copyright (c) 2019, Google Inc.
* Author - Rajat Jain
*
* This code instantiates platform devices for intel_pmc_core driver, only
* on supported platforms that may not have the ACPI devices in the ACPI tables.
* No new platforms should be added here, because we expect that new platforms
* should all have the ACPI device, which is the preferred way of enumeration.
*/
#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
static struct platform_device pmc_core_device = {
.name = "intel_pmc_core",
};
/*
* intel_pmc_core_platform_ids is the list of platforms where we want to
* instantiate the platform_device if not already instantiated. This is
* different than intel_pmc_core_ids in intel_pmc_core.c which is the
* list of platforms that the driver supports for pmc_core device. The
* other list may grow, but this list should not.
*/
static const struct x86_cpu_id intel_pmc_core_platform_ids[] = {
INTEL_CPU_FAM6(SKYLAKE_MOBILE, pmc_core_device),
INTEL_CPU_FAM6(SKYLAKE_DESKTOP, pmc_core_device),
INTEL_CPU_FAM6(KABYLAKE_MOBILE, pmc_core_device),
INTEL_CPU_FAM6(KABYLAKE_DESKTOP, pmc_core_device),
INTEL_CPU_FAM6(CANNONLAKE_MOBILE, pmc_core_device),
INTEL_CPU_FAM6(ICELAKE_MOBILE, pmc_core_device),
{}
};
MODULE_DEVICE_TABLE(x86cpu, intel_pmc_core_platform_ids);
static int __init pmc_core_platform_init(void)
{
/* Skip creating the platform device if ACPI already has a device */
if (acpi_dev_present("INT33A1", NULL, -1))
return -ENODEV;
if (!x86_match_cpu(intel_pmc_core_platform_ids))
return -ENODEV;
return platform_device_register(&pmc_core_device);
}
static void __exit pmc_core_platform_exit(void)
{
platform_device_unregister(&pmc_core_device);
}
module_init(pmc_core_platform_init);
module_exit(pmc_core_platform_exit);
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,17 @@
menu "Intel Speed Select Technology interface support"
depends on PCI
depends on X86_64 || COMPILE_TEST
config INTEL_SPEED_SELECT_INTERFACE
tristate "Intel(R) Speed Select Technology interface drivers"
help
This config enables the Intel(R) Speed Select Technology interface
drivers. The Intel(R) speed select technology features are non
architectural and only supported on specific Xeon(R) servers.
These drivers provide interface to directly communicate with hardware
via MMIO and Mail boxes to enumerate and control all the speed select
features.
Enable this config, if there is a need to enable and control the
Intel(R) Speed Select Technology features from the user space.
endmenu

View File

@ -0,0 +1,10 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile - Intel Speed Select Interface drivers
# Copyright (c) 2019, Intel Corporation.
#
obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_common.o
obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mmio.o
obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mbox_pci.o
obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mbox_msr.o

View File

@ -0,0 +1,672 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Intel Speed Select Interface: Common functions
* Copyright (c) 2019, Intel Corporation.
* All rights reserved.
*
* Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
*/
#include <linux/cpufeature.h>
#include <linux/cpuhotplug.h>
#include <linux/fs.h>
#include <linux/hashtable.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <uapi/linux/isst_if.h>
#include "isst_if_common.h"
#define MSR_THREAD_ID_INFO 0x53
#define MSR_CPU_BUS_NUMBER 0x128
static struct isst_if_cmd_cb punit_callbacks[ISST_IF_DEV_MAX];
static int punit_msr_white_list[] = {
MSR_TURBO_RATIO_LIMIT,
MSR_CONFIG_TDP_CONTROL,
};
struct isst_valid_cmd_ranges {
u16 cmd;
u16 sub_cmd_beg;
u16 sub_cmd_end;
};
struct isst_cmd_set_req_type {
u16 cmd;
u16 sub_cmd;
u16 param;
};
static const struct isst_valid_cmd_ranges isst_valid_cmds[] = {
{0xD0, 0x00, 0x03},
{0x7F, 0x00, 0x0B},
{0x7F, 0x10, 0x12},
{0x7F, 0x20, 0x23},
};
static const struct isst_cmd_set_req_type isst_cmd_set_reqs[] = {
{0xD0, 0x00, 0x08},
{0xD0, 0x01, 0x08},
{0xD0, 0x02, 0x08},
{0xD0, 0x03, 0x08},
{0x7F, 0x02, 0x00},
{0x7F, 0x08, 0x00},
};
struct isst_cmd {
struct hlist_node hnode;
u64 data;
u32 cmd;
int cpu;
int mbox_cmd_type;
u32 param;
};
static DECLARE_HASHTABLE(isst_hash, 8);
static DEFINE_MUTEX(isst_hash_lock);
static int isst_store_new_cmd(int cmd, u32 cpu, int mbox_cmd_type, u32 param,
u32 data)
{
struct isst_cmd *sst_cmd;
sst_cmd = kmalloc(sizeof(*sst_cmd), GFP_KERNEL);
if (!sst_cmd)
return -ENOMEM;
sst_cmd->cpu = cpu;
sst_cmd->cmd = cmd;
sst_cmd->mbox_cmd_type = mbox_cmd_type;
sst_cmd->param = param;
sst_cmd->data = data;
hash_add(isst_hash, &sst_cmd->hnode, sst_cmd->cmd);
return 0;
}
static void isst_delete_hash(void)
{
struct isst_cmd *sst_cmd;
struct hlist_node *tmp;
int i;
hash_for_each_safe(isst_hash, i, tmp, sst_cmd, hnode) {
hash_del(&sst_cmd->hnode);
kfree(sst_cmd);
}
}
/**
* isst_store_cmd() - Store command to a hash table
* @cmd: Mailbox command.
* @sub_cmd: Mailbox sub-command or MSR id.
* @mbox_cmd_type: Mailbox or MSR command.
* @param: Mailbox parameter.
* @data: Mailbox request data or MSR data.
*
* Stores the command to a hash table if there is no such command already
* stored. If already stored update the latest parameter and data for the
* command.
*
* Return: Return result of store to hash table, 0 for success, others for
* failure.
*/
int isst_store_cmd(int cmd, int sub_cmd, u32 cpu, int mbox_cmd_type,
u32 param, u64 data)
{
struct isst_cmd *sst_cmd;
int full_cmd, ret;
full_cmd = (cmd & GENMASK_ULL(15, 0)) << 16;
full_cmd |= (sub_cmd & GENMASK_ULL(15, 0));
mutex_lock(&isst_hash_lock);
hash_for_each_possible(isst_hash, sst_cmd, hnode, full_cmd) {
if (sst_cmd->cmd == full_cmd && sst_cmd->cpu == cpu &&
sst_cmd->mbox_cmd_type == mbox_cmd_type) {
sst_cmd->param = param;
sst_cmd->data = data;
mutex_unlock(&isst_hash_lock);
return 0;
}
}
ret = isst_store_new_cmd(full_cmd, cpu, mbox_cmd_type, param, data);
mutex_unlock(&isst_hash_lock);
return ret;
}
EXPORT_SYMBOL_GPL(isst_store_cmd);
static void isst_mbox_resume_command(struct isst_if_cmd_cb *cb,
struct isst_cmd *sst_cmd)
{
struct isst_if_mbox_cmd mbox_cmd;
int wr_only;
mbox_cmd.command = (sst_cmd->cmd & GENMASK_ULL(31, 16)) >> 16;
mbox_cmd.sub_command = sst_cmd->cmd & GENMASK_ULL(15, 0);
mbox_cmd.parameter = sst_cmd->param;
mbox_cmd.req_data = sst_cmd->data;
mbox_cmd.logical_cpu = sst_cmd->cpu;
(cb->cmd_callback)((u8 *)&mbox_cmd, &wr_only, 1);
}
/**
* isst_resume_common() - Process Resume request
*
* On resume replay all mailbox commands and MSRs.
*
* Return: None.
*/
void isst_resume_common(void)
{
struct isst_cmd *sst_cmd;
int i;
hash_for_each(isst_hash, i, sst_cmd, hnode) {
struct isst_if_cmd_cb *cb;
if (sst_cmd->mbox_cmd_type) {
cb = &punit_callbacks[ISST_IF_DEV_MBOX];
if (cb->registered)
isst_mbox_resume_command(cb, sst_cmd);
} else {
wrmsrl_safe_on_cpu(sst_cmd->cpu, sst_cmd->cmd,
sst_cmd->data);
}
}
}
EXPORT_SYMBOL_GPL(isst_resume_common);
static void isst_restore_msr_local(int cpu)
{
struct isst_cmd *sst_cmd;
int i;
mutex_lock(&isst_hash_lock);
for (i = 0; i < ARRAY_SIZE(punit_msr_white_list); ++i) {
if (!punit_msr_white_list[i])
break;
hash_for_each_possible(isst_hash, sst_cmd, hnode,
punit_msr_white_list[i]) {
if (!sst_cmd->mbox_cmd_type && sst_cmd->cpu == cpu)
wrmsrl_safe(sst_cmd->cmd, sst_cmd->data);
}
}
mutex_unlock(&isst_hash_lock);
}
/**
* isst_if_mbox_cmd_invalid() - Check invalid mailbox commands
* @cmd: Pointer to the command structure to verify.
*
* Invalid command to PUNIT to may result in instability of the platform.
* This function has a whitelist of commands, which are allowed.
*
* Return: Return true if the command is invalid, else false.
*/
bool isst_if_mbox_cmd_invalid(struct isst_if_mbox_cmd *cmd)
{
int i;
if (cmd->logical_cpu >= nr_cpu_ids)
return true;
for (i = 0; i < ARRAY_SIZE(isst_valid_cmds); ++i) {
if (cmd->command == isst_valid_cmds[i].cmd &&
(cmd->sub_command >= isst_valid_cmds[i].sub_cmd_beg &&
cmd->sub_command <= isst_valid_cmds[i].sub_cmd_end)) {
return false;
}
}
return true;
}
EXPORT_SYMBOL_GPL(isst_if_mbox_cmd_invalid);
/**
* isst_if_mbox_cmd_set_req() - Check mailbox command is a set request
* @cmd: Pointer to the command structure to verify.
*
* Check if the given mail box level is set request and not a get request.
*
* Return: Return true if the command is set_req, else false.
*/
bool isst_if_mbox_cmd_set_req(struct isst_if_mbox_cmd *cmd)
{
int i;
for (i = 0; i < ARRAY_SIZE(isst_cmd_set_reqs); ++i) {
if (cmd->command == isst_cmd_set_reqs[i].cmd &&
cmd->sub_command == isst_cmd_set_reqs[i].sub_cmd &&
cmd->parameter == isst_cmd_set_reqs[i].param) {
return true;
}
}
return false;
}
EXPORT_SYMBOL_GPL(isst_if_mbox_cmd_set_req);
static int isst_if_get_platform_info(void __user *argp)
{
struct isst_if_platform_info info;
info.api_version = ISST_IF_API_VERSION,
info.driver_version = ISST_IF_DRIVER_VERSION,
info.max_cmds_per_ioctl = ISST_IF_CMD_LIMIT,
info.mbox_supported = punit_callbacks[ISST_IF_DEV_MBOX].registered;
info.mmio_supported = punit_callbacks[ISST_IF_DEV_MMIO].registered;
if (copy_to_user(argp, &info, sizeof(info)))
return -EFAULT;
return 0;
}
struct isst_if_cpu_info {
/* For BUS 0 and BUS 1 only, which we need for PUNIT interface */
int bus_info[2];
int punit_cpu_id;
};
static struct isst_if_cpu_info *isst_cpu_info;
/**
* isst_if_get_pci_dev() - Get the PCI device instance for a CPU
* @cpu: Logical CPU number.
* @bus_number: The bus number assigned by the hardware.
* @dev: The device number assigned by the hardware.
* @fn: The function number assigned by the hardware.
*
* Using cached bus information, find out the PCI device for a bus number,
* device and function.
*
* Return: Return pci_dev pointer or NULL.
*/
struct pci_dev *isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn)
{
int bus_number;
if (bus_no < 0 || bus_no > 1 || cpu < 0 || cpu >= nr_cpu_ids ||
cpu >= num_possible_cpus())
return NULL;
bus_number = isst_cpu_info[cpu].bus_info[bus_no];
if (bus_number < 0)
return NULL;
return pci_get_domain_bus_and_slot(0, bus_number, PCI_DEVFN(dev, fn));
}
EXPORT_SYMBOL_GPL(isst_if_get_pci_dev);
static int isst_if_cpu_online(unsigned int cpu)
{
u64 data;
int ret;
ret = rdmsrl_safe(MSR_CPU_BUS_NUMBER, &data);
if (ret) {
/* This is not a fatal error on MSR mailbox only I/F */
isst_cpu_info[cpu].bus_info[0] = -1;
isst_cpu_info[cpu].bus_info[1] = -1;
} else {
isst_cpu_info[cpu].bus_info[0] = data & 0xff;
isst_cpu_info[cpu].bus_info[1] = (data >> 8) & 0xff;
}
ret = rdmsrl_safe(MSR_THREAD_ID_INFO, &data);
if (ret) {
isst_cpu_info[cpu].punit_cpu_id = -1;
return ret;
}
isst_cpu_info[cpu].punit_cpu_id = data;
isst_restore_msr_local(cpu);
return 0;
}
static int isst_if_online_id;
static int isst_if_cpu_info_init(void)
{
int ret;
isst_cpu_info = kcalloc(num_possible_cpus(),
sizeof(*isst_cpu_info),
GFP_KERNEL);
if (!isst_cpu_info)
return -ENOMEM;
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
"platform/x86/isst-if:online",
isst_if_cpu_online, NULL);
if (ret < 0) {
kfree(isst_cpu_info);
return ret;
}
isst_if_online_id = ret;
return 0;
}
static void isst_if_cpu_info_exit(void)
{
cpuhp_remove_state(isst_if_online_id);
kfree(isst_cpu_info);
};
static long isst_if_proc_phyid_req(u8 *cmd_ptr, int *write_only, int resume)
{
struct isst_if_cpu_map *cpu_map;
cpu_map = (struct isst_if_cpu_map *)cmd_ptr;
if (cpu_map->logical_cpu >= nr_cpu_ids ||
cpu_map->logical_cpu >= num_possible_cpus())
return -EINVAL;
*write_only = 0;
cpu_map->physical_cpu = isst_cpu_info[cpu_map->logical_cpu].punit_cpu_id;
return 0;
}
static bool match_punit_msr_white_list(int msr)
{
int i;
for (i = 0; i < ARRAY_SIZE(punit_msr_white_list); ++i) {
if (punit_msr_white_list[i] == msr)
return true;
}
return false;
}
static long isst_if_msr_cmd_req(u8 *cmd_ptr, int *write_only, int resume)
{
struct isst_if_msr_cmd *msr_cmd;
int ret;
msr_cmd = (struct isst_if_msr_cmd *)cmd_ptr;
if (!match_punit_msr_white_list(msr_cmd->msr))
return -EINVAL;
if (msr_cmd->logical_cpu >= nr_cpu_ids)
return -EINVAL;
if (msr_cmd->read_write) {
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
ret = wrmsrl_safe_on_cpu(msr_cmd->logical_cpu,
msr_cmd->msr,
msr_cmd->data);
*write_only = 1;
if (!ret && !resume)
ret = isst_store_cmd(0, msr_cmd->msr,
msr_cmd->logical_cpu,
0, 0, msr_cmd->data);
} else {
u64 data;
ret = rdmsrl_safe_on_cpu(msr_cmd->logical_cpu,
msr_cmd->msr, &data);
if (!ret) {
msr_cmd->data = data;
*write_only = 0;
}
}
return ret;
}
static long isst_if_exec_multi_cmd(void __user *argp, struct isst_if_cmd_cb *cb)
{
unsigned char __user *ptr;
u32 cmd_count;
u8 *cmd_ptr;
long ret;
int i;
/* Each multi command has u32 command count as the first field */
if (copy_from_user(&cmd_count, argp, sizeof(cmd_count)))
return -EFAULT;
if (!cmd_count || cmd_count > ISST_IF_CMD_LIMIT)
return -EINVAL;
cmd_ptr = kmalloc(cb->cmd_size, GFP_KERNEL);
if (!cmd_ptr)
return -ENOMEM;
/* cb->offset points to start of the command after the command count */
ptr = argp + cb->offset;
for (i = 0; i < cmd_count; ++i) {
int wr_only;
if (signal_pending(current)) {
ret = -EINTR;
break;
}
if (copy_from_user(cmd_ptr, ptr, cb->cmd_size)) {
ret = -EFAULT;
break;
}
ret = cb->cmd_callback(cmd_ptr, &wr_only, 0);
if (ret)
break;
if (!wr_only && copy_to_user(ptr, cmd_ptr, cb->cmd_size)) {
ret = -EFAULT;
break;
}
ptr += cb->cmd_size;
}
kfree(cmd_ptr);
return i ? i : ret;
}
static long isst_if_def_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
void __user *argp = (void __user *)arg;
struct isst_if_cmd_cb cmd_cb;
struct isst_if_cmd_cb *cb;
long ret = -ENOTTY;
switch (cmd) {
case ISST_IF_GET_PLATFORM_INFO:
ret = isst_if_get_platform_info(argp);
break;
case ISST_IF_GET_PHY_ID:
cmd_cb.cmd_size = sizeof(struct isst_if_cpu_map);
cmd_cb.offset = offsetof(struct isst_if_cpu_maps, cpu_map);
cmd_cb.cmd_callback = isst_if_proc_phyid_req;
ret = isst_if_exec_multi_cmd(argp, &cmd_cb);
break;
case ISST_IF_IO_CMD:
cb = &punit_callbacks[ISST_IF_DEV_MMIO];
if (cb->registered)
ret = isst_if_exec_multi_cmd(argp, cb);
break;
case ISST_IF_MBOX_COMMAND:
cb = &punit_callbacks[ISST_IF_DEV_MBOX];
if (cb->registered)
ret = isst_if_exec_multi_cmd(argp, cb);
break;
case ISST_IF_MSR_COMMAND:
cmd_cb.cmd_size = sizeof(struct isst_if_msr_cmd);
cmd_cb.offset = offsetof(struct isst_if_msr_cmds, msr_cmd);
cmd_cb.cmd_callback = isst_if_msr_cmd_req;
ret = isst_if_exec_multi_cmd(argp, &cmd_cb);
break;
default:
break;
}
return ret;
}
static DEFINE_MUTEX(punit_misc_dev_lock);
static int misc_usage_count;
static int misc_device_ret;
static int misc_device_open;
static int isst_if_open(struct inode *inode, struct file *file)
{
int i, ret = 0;
/* Fail open, if a module is going away */
mutex_lock(&punit_misc_dev_lock);
for (i = 0; i < ISST_IF_DEV_MAX; ++i) {
struct isst_if_cmd_cb *cb = &punit_callbacks[i];
if (cb->registered && !try_module_get(cb->owner)) {
ret = -ENODEV;
break;
}
}
if (ret) {
int j;
for (j = 0; j < i; ++j) {
struct isst_if_cmd_cb *cb;
cb = &punit_callbacks[j];
if (cb->registered)
module_put(cb->owner);
}
} else {
misc_device_open++;
}
mutex_unlock(&punit_misc_dev_lock);
return ret;
}
static int isst_if_relase(struct inode *inode, struct file *f)
{
int i;
mutex_lock(&punit_misc_dev_lock);
misc_device_open--;
for (i = 0; i < ISST_IF_DEV_MAX; ++i) {
struct isst_if_cmd_cb *cb = &punit_callbacks[i];
if (cb->registered)
module_put(cb->owner);
}
mutex_unlock(&punit_misc_dev_lock);
return 0;
}
static const struct file_operations isst_if_char_driver_ops = {
.open = isst_if_open,
.unlocked_ioctl = isst_if_def_ioctl,
.release = isst_if_relase,
};
static struct miscdevice isst_if_char_driver = {
.minor = MISC_DYNAMIC_MINOR,
.name = "isst_interface",
.fops = &isst_if_char_driver_ops,
};
/**
* isst_if_cdev_register() - Register callback for IOCTL
* @device_type: The device type this callback handling.
* @cb: Callback structure.
*
* This function registers a callback to device type. On very first call
* it will register a misc device, which is used for user kernel interface.
* Other calls simply increment ref count. Registry will fail, if the user
* already opened misc device for operation. Also if the misc device
* creation failed, then it will not try again and all callers will get
* failure code.
*
* Return: Return the return value from the misc creation device or -EINVAL
* for unsupported device type.
*/
int isst_if_cdev_register(int device_type, struct isst_if_cmd_cb *cb)
{
if (misc_device_ret)
return misc_device_ret;
if (device_type >= ISST_IF_DEV_MAX)
return -EINVAL;
mutex_lock(&punit_misc_dev_lock);
if (misc_device_open) {
mutex_unlock(&punit_misc_dev_lock);
return -EAGAIN;
}
if (!misc_usage_count) {
int ret;
misc_device_ret = misc_register(&isst_if_char_driver);
if (misc_device_ret)
goto unlock_exit;
ret = isst_if_cpu_info_init();
if (ret) {
misc_deregister(&isst_if_char_driver);
misc_device_ret = ret;
goto unlock_exit;
}
}
memcpy(&punit_callbacks[device_type], cb, sizeof(*cb));
punit_callbacks[device_type].registered = 1;
misc_usage_count++;
unlock_exit:
mutex_unlock(&punit_misc_dev_lock);
return misc_device_ret;
}
EXPORT_SYMBOL_GPL(isst_if_cdev_register);
/**
* isst_if_cdev_unregister() - Unregister callback for IOCTL
* @device_type: The device type to unregister.
*
* This function unregisters the previously registered callback. If this
* is the last callback unregistering, then misc device is removed.
*
* Return: None.
*/
void isst_if_cdev_unregister(int device_type)
{
mutex_lock(&punit_misc_dev_lock);
misc_usage_count--;
punit_callbacks[device_type].registered = 0;
if (device_type == ISST_IF_DEV_MBOX)
isst_delete_hash();
if (!misc_usage_count && !misc_device_ret) {
misc_deregister(&isst_if_char_driver);
isst_if_cpu_info_exit();
}
mutex_unlock(&punit_misc_dev_lock);
}
EXPORT_SYMBOL_GPL(isst_if_cdev_unregister);
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,69 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Intel Speed Select Interface: Drivers Internal defines
* Copyright (c) 2019, Intel Corporation.
* All rights reserved.
*
* Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
*/
#ifndef __ISST_IF_COMMON_H
#define __ISST_IF_COMMON_H
#define INTEL_RAPL_PRIO_DEVID_0 0x3451
#define INTEL_CFG_MBOX_DEVID_0 0x3459
/*
* Validate maximum commands in a single request.
* This is enough to handle command to every core in one ioctl, or all
* possible message id to one CPU. Limit is also helpful for resonse time
* per IOCTL request, as PUNIT may take different times to process each
* request and may hold for long for too many commands.
*/
#define ISST_IF_CMD_LIMIT 64
#define ISST_IF_API_VERSION 0x01
#define ISST_IF_DRIVER_VERSION 0x01
#define ISST_IF_DEV_MBOX 0
#define ISST_IF_DEV_MMIO 1
#define ISST_IF_DEV_MAX 2
/**
* struct isst_if_cmd_cb - Used to register a IOCTL handler
* @registered: Used by the common code to store registry. Caller don't
* to touch this field
* @cmd_size: The command size of the individual command in IOCTL
* @offset: Offset to the first valid member in command structure.
* This will be the offset of the start of the command
* after command count field
* @cmd_callback: Callback function to handle IOCTL. The callback has the
* command pointer with data for command. There is a pointer
* called write_only, which when set, will not copy the
* response to user ioctl buffer. The "resume" argument
* can be used to avoid storing the command for replay
* during system resume
*
* This structure is used to register an handler for IOCTL. To avoid
* code duplication common code handles all the IOCTL command read/write
* including handling multiple command in single IOCTL. The caller just
* need to execute a command via the registered callback.
*/
struct isst_if_cmd_cb {
int registered;
int cmd_size;
int offset;
struct module *owner;
long (*cmd_callback)(u8 *ptr, int *write_only, int resume);
};
/* Internal interface functions */
int isst_if_cdev_register(int type, struct isst_if_cmd_cb *cb);
void isst_if_cdev_unregister(int type);
struct pci_dev *isst_if_get_pci_dev(int cpu, int bus, int dev, int fn);
bool isst_if_mbox_cmd_set_req(struct isst_if_mbox_cmd *mbox_cmd);
bool isst_if_mbox_cmd_invalid(struct isst_if_mbox_cmd *cmd);
int isst_store_cmd(int cmd, int sub_command, u32 cpu, int mbox_cmd,
u32 param, u64 data);
void isst_resume_common(void);
#endif

View File

@ -0,0 +1,216 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Intel Speed Select Interface: Mbox via MSR Interface
* Copyright (c) 2019, Intel Corporation.
* All rights reserved.
*
* Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
*/
#include <linux/module.h>
#include <linux/cpuhotplug.h>
#include <linux/pci.h>
#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <linux/suspend.h>
#include <linux/topology.h>
#include <linux/uaccess.h>
#include <uapi/linux/isst_if.h>
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
#include "isst_if_common.h"
#define MSR_OS_MAILBOX_INTERFACE 0xB0
#define MSR_OS_MAILBOX_DATA 0xB1
#define MSR_OS_MAILBOX_BUSY_BIT 31
/*
* Based on experiments count is never more than 1, as the MSR overhead
* is enough to finish the command. So here this is the worst case number.
*/
#define OS_MAILBOX_RETRY_COUNT 3
static int isst_if_send_mbox_cmd(u8 command, u8 sub_command, u32 parameter,
u32 command_data, u32 *response_data)
{
u32 retries;
u64 data;
int ret;
/* Poll for rb bit == 0 */
retries = OS_MAILBOX_RETRY_COUNT;
do {
rdmsrl(MSR_OS_MAILBOX_INTERFACE, data);
if (data & BIT_ULL(MSR_OS_MAILBOX_BUSY_BIT)) {
ret = -EBUSY;
continue;
}
ret = 0;
break;
} while (--retries);
if (ret)
return ret;
/* Write DATA register */
wrmsrl(MSR_OS_MAILBOX_DATA, command_data);
/* Write command register */
data = BIT_ULL(MSR_OS_MAILBOX_BUSY_BIT) |
(parameter & GENMASK_ULL(13, 0)) << 16 |
(sub_command << 8) |
command;
wrmsrl(MSR_OS_MAILBOX_INTERFACE, data);
/* Poll for rb bit == 0 */
retries = OS_MAILBOX_RETRY_COUNT;
do {
rdmsrl(MSR_OS_MAILBOX_INTERFACE, data);
if (data & BIT_ULL(MSR_OS_MAILBOX_BUSY_BIT)) {
ret = -EBUSY;
continue;
}
if (data & 0xff)
return -ENXIO;
if (response_data) {
rdmsrl(MSR_OS_MAILBOX_DATA, data);
*response_data = data;
}
ret = 0;
break;
} while (--retries);
return ret;
}
struct msrl_action {
int err;
struct isst_if_mbox_cmd *mbox_cmd;
};
/* revisit, smp_call_function_single should be enough for atomic mailbox! */
static void msrl_update_func(void *info)
{
struct msrl_action *act = info;
act->err = isst_if_send_mbox_cmd(act->mbox_cmd->command,
act->mbox_cmd->sub_command,
act->mbox_cmd->parameter,
act->mbox_cmd->req_data,
&act->mbox_cmd->resp_data);
}
static long isst_if_mbox_proc_cmd(u8 *cmd_ptr, int *write_only, int resume)
{
struct msrl_action action;
int ret;
action.mbox_cmd = (struct isst_if_mbox_cmd *)cmd_ptr;
if (isst_if_mbox_cmd_invalid(action.mbox_cmd))
return -EINVAL;
if (isst_if_mbox_cmd_set_req(action.mbox_cmd) &&
!capable(CAP_SYS_ADMIN))
return -EPERM;
/*
* To complete mailbox command, we need to access two MSRs.
* So we don't want race to complete a mailbox transcation.
* Here smp_call ensures that msrl_update_func() has no race
* and also with wait flag, wait for completion.
* smp_call_function_single is using get_cpu() and put_cpu().
*/
ret = smp_call_function_single(action.mbox_cmd->logical_cpu,
msrl_update_func, &action, 1);
if (ret)
return ret;
if (!action.err && !resume && isst_if_mbox_cmd_set_req(action.mbox_cmd))
action.err = isst_store_cmd(action.mbox_cmd->command,
action.mbox_cmd->sub_command,
action.mbox_cmd->logical_cpu, 1,
action.mbox_cmd->parameter,
action.mbox_cmd->req_data);
*write_only = 0;
return action.err;
}
static int isst_pm_notify(struct notifier_block *nb,
unsigned long mode, void *_unused)
{
switch (mode) {
case PM_POST_HIBERNATION:
case PM_POST_RESTORE:
case PM_POST_SUSPEND:
isst_resume_common();
break;
default:
break;
}
return 0;
}
static struct notifier_block isst_pm_nb = {
.notifier_call = isst_pm_notify,
};
#define ICPU(model) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, }
static const struct x86_cpu_id isst_if_cpu_ids[] = {
ICPU(INTEL_FAM6_SKYLAKE_X),
{}
};
MODULE_DEVICE_TABLE(x86cpu, isst_if_cpu_ids);
static int __init isst_if_mbox_init(void)
{
struct isst_if_cmd_cb cb;
const struct x86_cpu_id *id;
u64 data;
int ret;
id = x86_match_cpu(isst_if_cpu_ids);
if (!id)
return -ENODEV;
/* Check presence of mailbox MSRs */
ret = rdmsrl_safe(MSR_OS_MAILBOX_INTERFACE, &data);
if (ret)
return ret;
ret = rdmsrl_safe(MSR_OS_MAILBOX_DATA, &data);
if (ret)
return ret;
memset(&cb, 0, sizeof(cb));
cb.cmd_size = sizeof(struct isst_if_mbox_cmd);
cb.offset = offsetof(struct isst_if_mbox_cmds, mbox_cmd);
cb.cmd_callback = isst_if_mbox_proc_cmd;
cb.owner = THIS_MODULE;
ret = isst_if_cdev_register(ISST_IF_DEV_MBOX, &cb);
if (ret)
return ret;
ret = register_pm_notifier(&isst_pm_nb);
if (ret)
isst_if_cdev_unregister(ISST_IF_DEV_MBOX);
return ret;
}
module_init(isst_if_mbox_init)
static void __exit isst_if_mbox_exit(void)
{
unregister_pm_notifier(&isst_pm_nb);
isst_if_cdev_unregister(ISST_IF_DEV_MBOX);
}
module_exit(isst_if_mbox_exit)
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Intel speed select interface mailbox driver");

View File

@ -0,0 +1,214 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Intel Speed Select Interface: Mbox via PCI Interface
* Copyright (c) 2019, Intel Corporation.
* All rights reserved.
*
* Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
*/
#include <linux/cpufeature.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/sched/signal.h>
#include <linux/uaccess.h>
#include <uapi/linux/isst_if.h>
#include "isst_if_common.h"
#define PUNIT_MAILBOX_DATA 0xA0
#define PUNIT_MAILBOX_INTERFACE 0xA4
#define PUNIT_MAILBOX_BUSY_BIT 31
/*
* Commands has variable amount of processing time. Most of the commands will
* be done in 0-3 tries, but some takes up to 50.
* The real processing time was observed as 25us for the most of the commands
* at 2GHz. It is possible to optimize this count taking samples on customer
* systems.
*/
#define OS_MAILBOX_RETRY_COUNT 50
struct isst_if_device {
struct mutex mutex;
};
static int isst_if_mbox_cmd(struct pci_dev *pdev,
struct isst_if_mbox_cmd *mbox_cmd)
{
u32 retries, data;
int ret;
/* Poll for rb bit == 0 */
retries = OS_MAILBOX_RETRY_COUNT;
do {
ret = pci_read_config_dword(pdev, PUNIT_MAILBOX_INTERFACE,
&data);
if (ret)
return ret;
if (data & BIT_ULL(PUNIT_MAILBOX_BUSY_BIT)) {
ret = -EBUSY;
continue;
}
ret = 0;
break;
} while (--retries);
if (ret)
return ret;
/* Write DATA register */
ret = pci_write_config_dword(pdev, PUNIT_MAILBOX_DATA,
mbox_cmd->req_data);
if (ret)
return ret;
/* Write command register */
data = BIT_ULL(PUNIT_MAILBOX_BUSY_BIT) |
(mbox_cmd->parameter & GENMASK_ULL(13, 0)) << 16 |
(mbox_cmd->sub_command << 8) |
mbox_cmd->command;
ret = pci_write_config_dword(pdev, PUNIT_MAILBOX_INTERFACE, data);
if (ret)
return ret;
/* Poll for rb bit == 0 */
retries = OS_MAILBOX_RETRY_COUNT;
do {
ret = pci_read_config_dword(pdev, PUNIT_MAILBOX_INTERFACE,
&data);
if (ret)
return ret;
if (data & BIT_ULL(PUNIT_MAILBOX_BUSY_BIT)) {
ret = -EBUSY;
continue;
}
if (data & 0xff)
return -ENXIO;
ret = pci_read_config_dword(pdev, PUNIT_MAILBOX_DATA, &data);
if (ret)
return ret;
mbox_cmd->resp_data = data;
ret = 0;
break;
} while (--retries);
return ret;
}
static long isst_if_mbox_proc_cmd(u8 *cmd_ptr, int *write_only, int resume)
{
struct isst_if_mbox_cmd *mbox_cmd;
struct isst_if_device *punit_dev;
struct pci_dev *pdev;
int ret;
mbox_cmd = (struct isst_if_mbox_cmd *)cmd_ptr;
if (isst_if_mbox_cmd_invalid(mbox_cmd))
return -EINVAL;
if (isst_if_mbox_cmd_set_req(mbox_cmd) && !capable(CAP_SYS_ADMIN))
return -EPERM;
pdev = isst_if_get_pci_dev(mbox_cmd->logical_cpu, 1, 30, 1);
if (!pdev)
return -EINVAL;
punit_dev = pci_get_drvdata(pdev);
if (!punit_dev)
return -EINVAL;
/*
* Basically we are allowing one complete mailbox transaction on
* a mapped PCI device at a time.
*/
mutex_lock(&punit_dev->mutex);
ret = isst_if_mbox_cmd(pdev, mbox_cmd);
if (!ret && !resume && isst_if_mbox_cmd_set_req(mbox_cmd))
ret = isst_store_cmd(mbox_cmd->command,
mbox_cmd->sub_command,
mbox_cmd->logical_cpu, 1,
mbox_cmd->parameter,
mbox_cmd->req_data);
mutex_unlock(&punit_dev->mutex);
if (ret)
return ret;
*write_only = 0;
return 0;
}
static const struct pci_device_id isst_if_mbox_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, INTEL_CFG_MBOX_DEVID_0)},
{ 0 },
};
MODULE_DEVICE_TABLE(pci, isst_if_mbox_ids);
static int isst_if_mbox_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct isst_if_device *punit_dev;
struct isst_if_cmd_cb cb;
int ret;
punit_dev = devm_kzalloc(&pdev->dev, sizeof(*punit_dev), GFP_KERNEL);
if (!punit_dev)
return -ENOMEM;
ret = pcim_enable_device(pdev);
if (ret)
return ret;
mutex_init(&punit_dev->mutex);
pci_set_drvdata(pdev, punit_dev);
memset(&cb, 0, sizeof(cb));
cb.cmd_size = sizeof(struct isst_if_mbox_cmd);
cb.offset = offsetof(struct isst_if_mbox_cmds, mbox_cmd);
cb.cmd_callback = isst_if_mbox_proc_cmd;
cb.owner = THIS_MODULE;
ret = isst_if_cdev_register(ISST_IF_DEV_MBOX, &cb);
if (ret)
mutex_destroy(&punit_dev->mutex);
return ret;
}
static void isst_if_mbox_remove(struct pci_dev *pdev)
{
struct isst_if_device *punit_dev;
punit_dev = pci_get_drvdata(pdev);
isst_if_cdev_unregister(ISST_IF_DEV_MBOX);
mutex_destroy(&punit_dev->mutex);
}
static int __maybe_unused isst_if_resume(struct device *device)
{
isst_resume_common();
return 0;
}
static SIMPLE_DEV_PM_OPS(isst_if_pm_ops, NULL, isst_if_resume);
static struct pci_driver isst_if_pci_driver = {
.name = "isst_if_mbox_pci",
.id_table = isst_if_mbox_ids,
.probe = isst_if_mbox_probe,
.remove = isst_if_mbox_remove,
.driver.pm = &isst_if_pm_ops,
};
module_pci_driver(isst_if_pci_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Intel speed select interface pci mailbox driver");

View File

@ -0,0 +1,180 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Intel Speed Select Interface: MMIO Interface
* Copyright (c) 2019, Intel Corporation.
* All rights reserved.
*
* Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
*/
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/sched/signal.h>
#include <linux/uaccess.h>
#include <uapi/linux/isst_if.h>
#include "isst_if_common.h"
struct isst_mmio_range {
int beg;
int end;
};
struct isst_mmio_range mmio_range[] = {
{0x04, 0x14},
{0x20, 0xD0},
};
struct isst_if_device {
void __iomem *punit_mmio;
u32 range_0[5];
u32 range_1[45];
struct mutex mutex;
};
static long isst_if_mmio_rd_wr(u8 *cmd_ptr, int *write_only, int resume)
{
struct isst_if_device *punit_dev;
struct isst_if_io_reg *io_reg;
struct pci_dev *pdev;
io_reg = (struct isst_if_io_reg *)cmd_ptr;
if (io_reg->reg < 0x04 || io_reg->reg > 0xD0)
return -EINVAL;
if (io_reg->read_write && !capable(CAP_SYS_ADMIN))
return -EPERM;
pdev = isst_if_get_pci_dev(io_reg->logical_cpu, 0, 0, 1);
if (!pdev)
return -EINVAL;
punit_dev = pci_get_drvdata(pdev);
if (!punit_dev)
return -EINVAL;
/*
* Ensure that operation is complete on a PCI device to avoid read
* write race by using per PCI device mutex.
*/
mutex_lock(&punit_dev->mutex);
if (io_reg->read_write) {
writel(io_reg->value, punit_dev->punit_mmio+io_reg->reg);
*write_only = 1;
} else {
io_reg->value = readl(punit_dev->punit_mmio+io_reg->reg);
*write_only = 0;
}
mutex_unlock(&punit_dev->mutex);
return 0;
}
static const struct pci_device_id isst_if_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, INTEL_RAPL_PRIO_DEVID_0)},
{ 0 },
};
MODULE_DEVICE_TABLE(pci, isst_if_ids);
static int isst_if_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct isst_if_device *punit_dev;
struct isst_if_cmd_cb cb;
u32 mmio_base, pcu_base;
u64 base_addr;
int ret;
punit_dev = devm_kzalloc(&pdev->dev, sizeof(*punit_dev), GFP_KERNEL);
if (!punit_dev)
return -ENOMEM;
ret = pcim_enable_device(pdev);
if (ret)
return ret;
ret = pci_read_config_dword(pdev, 0xD0, &mmio_base);
if (ret)
return ret;
ret = pci_read_config_dword(pdev, 0xFC, &pcu_base);
if (ret)
return ret;
pcu_base &= GENMASK(10, 0);
base_addr = (u64)mmio_base << 23 | (u64) pcu_base << 12;
punit_dev->punit_mmio = devm_ioremap(&pdev->dev, base_addr, 256);
if (!punit_dev->punit_mmio)
return -ENOMEM;
mutex_init(&punit_dev->mutex);
pci_set_drvdata(pdev, punit_dev);
memset(&cb, 0, sizeof(cb));
cb.cmd_size = sizeof(struct isst_if_io_reg);
cb.offset = offsetof(struct isst_if_io_regs, io_reg);
cb.cmd_callback = isst_if_mmio_rd_wr;
cb.owner = THIS_MODULE;
ret = isst_if_cdev_register(ISST_IF_DEV_MMIO, &cb);
if (ret)
mutex_destroy(&punit_dev->mutex);
return ret;
}
static void isst_if_remove(struct pci_dev *pdev)
{
struct isst_if_device *punit_dev;
punit_dev = pci_get_drvdata(pdev);
isst_if_cdev_unregister(ISST_IF_DEV_MBOX);
mutex_destroy(&punit_dev->mutex);
}
static int __maybe_unused isst_if_suspend(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
struct isst_if_device *punit_dev;
int i;
punit_dev = pci_get_drvdata(pdev);
for (i = 0; i < ARRAY_SIZE(punit_dev->range_0); ++i)
punit_dev->range_0[i] = readl(punit_dev->punit_mmio +
mmio_range[0].beg + 4 * i);
for (i = 0; i < ARRAY_SIZE(punit_dev->range_1); ++i)
punit_dev->range_1[i] = readl(punit_dev->punit_mmio +
mmio_range[1].beg + 4 * i);
return 0;
}
static int __maybe_unused isst_if_resume(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
struct isst_if_device *punit_dev;
int i;
punit_dev = pci_get_drvdata(pdev);
for (i = 0; i < ARRAY_SIZE(punit_dev->range_0); ++i)
writel(punit_dev->range_0[i], punit_dev->punit_mmio +
mmio_range[0].beg + 4 * i);
for (i = 0; i < ARRAY_SIZE(punit_dev->range_1); ++i)
writel(punit_dev->range_1[i], punit_dev->punit_mmio +
mmio_range[1].beg + 4 * i);
return 0;
}
static SIMPLE_DEV_PM_OPS(isst_if_pm_ops, isst_if_suspend, isst_if_resume);
static struct pci_driver isst_if_pci_driver = {
.name = "isst_if_pci",
.id_table = isst_if_ids,
.probe = isst_if_probe,
.remove = isst_if_remove,
.driver.pm = &isst_if_pm_ops,
};
module_pci_driver(isst_if_pci_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Intel speed select interface mmio driver");

View File

@ -900,7 +900,7 @@ static int __init telemetry_debugfs_init(void)
{ {
const struct x86_cpu_id *id; const struct x86_cpu_id *id;
int err; int err;
struct dentry *f; struct dentry *dir;
/* Only APL supported for now */ /* Only APL supported for now */
id = x86_match_cpu(telemetry_debugfs_cpu_ids); id = x86_match_cpu(telemetry_debugfs_cpu_ids);
@ -923,68 +923,22 @@ static int __init telemetry_debugfs_init(void)
register_pm_notifier(&pm_notifier); register_pm_notifier(&pm_notifier);
err = -ENOMEM; dir = debugfs_create_dir("telemetry", NULL);
debugfs_conf->telemetry_dbg_dir = debugfs_create_dir("telemetry", NULL); debugfs_conf->telemetry_dbg_dir = dir;
if (!debugfs_conf->telemetry_dbg_dir)
goto out_pm;
f = debugfs_create_file("pss_info", S_IFREG | S_IRUGO,
debugfs_conf->telemetry_dbg_dir, NULL,
&telem_pss_states_fops);
if (!f) {
pr_err("pss_sample_info debugfs register failed\n");
goto out;
}
f = debugfs_create_file("ioss_info", S_IFREG | S_IRUGO,
debugfs_conf->telemetry_dbg_dir, NULL,
&telem_ioss_states_fops);
if (!f) {
pr_err("ioss_sample_info debugfs register failed\n");
goto out;
}
f = debugfs_create_file("soc_states", S_IFREG | S_IRUGO,
debugfs_conf->telemetry_dbg_dir,
NULL, &telem_soc_states_fops);
if (!f) {
pr_err("ioss_sample_info debugfs register failed\n");
goto out;
}
f = debugfs_create_file("s0ix_residency_usec", S_IFREG | S_IRUGO,
debugfs_conf->telemetry_dbg_dir,
NULL, &telem_s0ix_fops);
if (!f) {
pr_err("s0ix_residency_usec debugfs register failed\n");
goto out;
}
f = debugfs_create_file("pss_trace_verbosity", S_IFREG | S_IRUGO,
debugfs_conf->telemetry_dbg_dir, NULL,
&telem_pss_trc_verb_ops);
if (!f) {
pr_err("pss_trace_verbosity debugfs register failed\n");
goto out;
}
f = debugfs_create_file("ioss_trace_verbosity", S_IFREG | S_IRUGO,
debugfs_conf->telemetry_dbg_dir, NULL,
&telem_ioss_trc_verb_ops);
if (!f) {
pr_err("ioss_trace_verbosity debugfs register failed\n");
goto out;
}
debugfs_create_file("pss_info", S_IFREG | S_IRUGO, dir, NULL,
&telem_pss_states_fops);
debugfs_create_file("ioss_info", S_IFREG | S_IRUGO, dir, NULL,
&telem_ioss_states_fops);
debugfs_create_file("soc_states", S_IFREG | S_IRUGO, dir, NULL,
&telem_soc_states_fops);
debugfs_create_file("s0ix_residency_usec", S_IFREG | S_IRUGO, dir, NULL,
&telem_s0ix_fops);
debugfs_create_file("pss_trace_verbosity", S_IFREG | S_IRUGO, dir, NULL,
&telem_pss_trc_verb_ops);
debugfs_create_file("ioss_trace_verbosity", S_IFREG | S_IRUGO, dir,
NULL, &telem_ioss_trc_verb_ops);
return 0; return 0;
out:
debugfs_remove_recursive(debugfs_conf->telemetry_dbg_dir);
debugfs_conf->telemetry_dbg_dir = NULL;
out_pm:
unregister_pm_notifier(&pm_notifier);
return err;
} }
static void __exit telemetry_debugfs_exit(void) static void __exit telemetry_debugfs_exit(void)

View File

@ -44,6 +44,8 @@
#define MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET 0x3b #define MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET 0x3b
#define MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET 0x40 #define MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET 0x40
#define MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET 0x41 #define MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET 0x41
#define MLXPLAT_CPLD_LPC_REG_AGGRCO_OFFSET 0x42
#define MLXPLAT_CPLD_LPC_REG_AGGRCO_MASK_OFFSET 0x43
#define MLXPLAT_CPLD_LPC_REG_ASIC_HEALTH_OFFSET 0x50 #define MLXPLAT_CPLD_LPC_REG_ASIC_HEALTH_OFFSET 0x50
#define MLXPLAT_CPLD_LPC_REG_ASIC_EVENT_OFFSET 0x51 #define MLXPLAT_CPLD_LPC_REG_ASIC_EVENT_OFFSET 0x51
#define MLXPLAT_CPLD_LPC_REG_ASIC_MASK_OFFSET 0x52 #define MLXPLAT_CPLD_LPC_REG_ASIC_MASK_OFFSET 0x52
@ -105,7 +107,9 @@
MLXPLAT_CPLD_AGGR_FAN_MASK_DEF) MLXPLAT_CPLD_AGGR_FAN_MASK_DEF)
#define MLXPLAT_CPLD_AGGR_ASIC_MASK_NG 0x01 #define MLXPLAT_CPLD_AGGR_ASIC_MASK_NG 0x01
#define MLXPLAT_CPLD_AGGR_MASK_NG_DEF 0x04 #define MLXPLAT_CPLD_AGGR_MASK_NG_DEF 0x04
#define MLXPLAT_CPLD_AGGR_MASK_COMEX BIT(0)
#define MLXPLAT_CPLD_LOW_AGGR_MASK_LOW 0xc1 #define MLXPLAT_CPLD_LOW_AGGR_MASK_LOW 0xc1
#define MLXPLAT_CPLD_LOW_AGGR_MASK_I2C BIT(6)
#define MLXPLAT_CPLD_PSU_MASK GENMASK(1, 0) #define MLXPLAT_CPLD_PSU_MASK GENMASK(1, 0)
#define MLXPLAT_CPLD_PWR_MASK GENMASK(1, 0) #define MLXPLAT_CPLD_PWR_MASK GENMASK(1, 0)
#define MLXPLAT_CPLD_FAN_MASK GENMASK(3, 0) #define MLXPLAT_CPLD_FAN_MASK GENMASK(3, 0)
@ -159,6 +163,7 @@
* @pdev_io_regs - register access platform devices * @pdev_io_regs - register access platform devices
* @pdev_fan - FAN platform devices * @pdev_fan - FAN platform devices
* @pdev_wd - array of watchdog platform devices * @pdev_wd - array of watchdog platform devices
* @regmap: device register map
*/ */
struct mlxplat_priv { struct mlxplat_priv {
struct platform_device *pdev_i2c; struct platform_device *pdev_i2c;
@ -168,6 +173,7 @@ struct mlxplat_priv {
struct platform_device *pdev_io_regs; struct platform_device *pdev_io_regs;
struct platform_device *pdev_fan; struct platform_device *pdev_fan;
struct platform_device *pdev_wd[MLXPLAT_CPLD_WD_MAX_DEVS]; struct platform_device *pdev_wd[MLXPLAT_CPLD_WD_MAX_DEVS];
void *regmap;
}; };
/* Regions for LPC I2C controller and LPC base register space */ /* Regions for LPC I2C controller and LPC base register space */
@ -181,6 +187,14 @@ static const struct resource mlxplat_lpc_resources[] = {
IORESOURCE_IO), IORESOURCE_IO),
}; };
/* Platform next generation systems i2c data */
static struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_i2c_ng_data = {
.cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
.mask = MLXPLAT_CPLD_AGGR_MASK_COMEX,
.cell_low = MLXPLAT_CPLD_LPC_REG_AGGRCO_OFFSET,
.mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_I2C,
};
/* Platform default channels */ /* Platform default channels */
static const int mlxplat_default_channels[][MLXPLAT_CPLD_GRP_CHNL_NUM] = { static const int mlxplat_default_channels[][MLXPLAT_CPLD_GRP_CHNL_NUM] = {
{ {
@ -704,7 +718,7 @@ struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_default_ng_data = {
.items = mlxplat_mlxcpld_default_ng_items, .items = mlxplat_mlxcpld_default_ng_items,
.counter = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_items), .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_items),
.cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET, .cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET,
.mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF, .mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF | MLXPLAT_CPLD_AGGR_MASK_COMEX,
.cell_low = MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET, .cell_low = MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET,
.mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW, .mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW,
}; };
@ -1112,6 +1126,12 @@ static struct mlxreg_core_data mlxplat_mlxcpld_msn21xx_regs_io_data[] = {
.mask = GENMASK(7, 0) & ~BIT(6), .mask = GENMASK(7, 0) & ~BIT(6),
.mode = 0444, .mode = 0444,
}, },
{
.label = "reset_sff_wd",
.reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(6),
.mode = 0444,
},
{ {
.label = "psu1_on", .label = "psu1_on",
.reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET, .reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET,
@ -1200,6 +1220,18 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_regs_io_data[] = {
.mask = GENMASK(7, 0) & ~BIT(4), .mask = GENMASK(7, 0) & ~BIT(4),
.mode = 0444, .mode = 0444,
}, },
{
.label = "reset_from_asic",
.reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(5),
.mode = 0444,
},
{
.label = "reset_swb_wd",
.reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(6),
.mode = 0444,
},
{ {
.label = "reset_asic_thermal", .label = "reset_asic_thermal",
.reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET, .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET,
@ -1212,6 +1244,12 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_regs_io_data[] = {
.mask = GENMASK(7, 0) & ~BIT(3), .mask = GENMASK(7, 0) & ~BIT(3),
.mode = 0444, .mode = 0444,
}, },
{
.label = "reset_comex_wd",
.reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(6),
.mode = 0444,
},
{ {
.label = "reset_voltmon_upgrade_fail", .label = "reset_voltmon_upgrade_fail",
.reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET, .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET,
@ -1224,6 +1262,18 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_regs_io_data[] = {
.mask = GENMASK(7, 0) & ~BIT(1), .mask = GENMASK(7, 0) & ~BIT(1),
.mode = 0444, .mode = 0444,
}, },
{
.label = "reset_comex_thermal",
.reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(3),
.mode = 0444,
},
{
.label = "reset_reload_bios",
.reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET,
.mask = GENMASK(7, 0) & ~BIT(5),
.mode = 0444,
},
{ {
.label = "psu1_on", .label = "psu1_on",
.reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET, .reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET,
@ -1531,6 +1581,7 @@ static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg)
case MLXPLAT_CPLD_LPC_REG_WP2_OFFSET: case MLXPLAT_CPLD_LPC_REG_WP2_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRCO_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC_EVENT_OFFSET: case MLXPLAT_CPLD_LPC_REG_ASIC_EVENT_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_ASIC_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET: case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET:
@ -1578,6 +1629,8 @@ static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg)
case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRCO_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRCO_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC_HEALTH_OFFSET: case MLXPLAT_CPLD_LPC_REG_ASIC_HEALTH_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC_EVENT_OFFSET: case MLXPLAT_CPLD_LPC_REG_ASIC_EVENT_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_ASIC_MASK_OFFSET:
@ -1645,6 +1698,8 @@ static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg)
case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRCO_OFFSET:
case MLXPLAT_CPLD_LPC_REG_AGGRCO_MASK_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC_HEALTH_OFFSET: case MLXPLAT_CPLD_LPC_REG_ASIC_HEALTH_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC_EVENT_OFFSET: case MLXPLAT_CPLD_LPC_REG_ASIC_EVENT_OFFSET:
case MLXPLAT_CPLD_LPC_REG_ASIC_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_ASIC_MASK_OFFSET:
@ -1691,6 +1746,11 @@ static const struct reg_default mlxplat_mlxcpld_regmap_default[] = {
{ MLXPLAT_CPLD_LPC_REG_WD_CLEAR_WP_OFFSET, 0x00 }, { MLXPLAT_CPLD_LPC_REG_WD_CLEAR_WP_OFFSET, 0x00 },
}; };
static const struct reg_default mlxplat_mlxcpld_regmap_ng[] = {
{ MLXPLAT_CPLD_LPC_REG_PWM_CONTROL_OFFSET, 0x00 },
{ MLXPLAT_CPLD_LPC_REG_WD_CLEAR_WP_OFFSET, 0x00 },
};
struct mlxplat_mlxcpld_regmap_context { struct mlxplat_mlxcpld_regmap_context {
void __iomem *base; void __iomem *base;
}; };
@ -1729,17 +1789,33 @@ static const struct regmap_config mlxplat_mlxcpld_regmap_config = {
.reg_write = mlxplat_mlxcpld_reg_write, .reg_write = mlxplat_mlxcpld_reg_write,
}; };
static const struct regmap_config mlxplat_mlxcpld_regmap_config_ng = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 255,
.cache_type = REGCACHE_FLAT,
.writeable_reg = mlxplat_mlxcpld_writeable_reg,
.readable_reg = mlxplat_mlxcpld_readable_reg,
.volatile_reg = mlxplat_mlxcpld_volatile_reg,
.reg_defaults = mlxplat_mlxcpld_regmap_ng,
.num_reg_defaults = ARRAY_SIZE(mlxplat_mlxcpld_regmap_ng),
.reg_read = mlxplat_mlxcpld_reg_read,
.reg_write = mlxplat_mlxcpld_reg_write,
};
static struct resource mlxplat_mlxcpld_resources[] = { static struct resource mlxplat_mlxcpld_resources[] = {
[0] = DEFINE_RES_IRQ_NAMED(17, "mlxreg-hotplug"), [0] = DEFINE_RES_IRQ_NAMED(17, "mlxreg-hotplug"),
}; };
static struct platform_device *mlxplat_dev; static struct platform_device *mlxplat_dev;
static struct mlxreg_core_hotplug_platform_data *mlxplat_i2c;
static struct mlxreg_core_hotplug_platform_data *mlxplat_hotplug; static struct mlxreg_core_hotplug_platform_data *mlxplat_hotplug;
static struct mlxreg_core_platform_data *mlxplat_led; static struct mlxreg_core_platform_data *mlxplat_led;
static struct mlxreg_core_platform_data *mlxplat_regs_io; static struct mlxreg_core_platform_data *mlxplat_regs_io;
static struct mlxreg_core_platform_data *mlxplat_fan; static struct mlxreg_core_platform_data *mlxplat_fan;
static struct mlxreg_core_platform_data static struct mlxreg_core_platform_data
*mlxplat_wd_data[MLXPLAT_CPLD_WD_MAX_DEVS]; *mlxplat_wd_data[MLXPLAT_CPLD_WD_MAX_DEVS];
static const struct regmap_config *mlxplat_regmap_config;
static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi) static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi)
{ {
@ -1834,11 +1910,49 @@ static int __init mlxplat_dmi_qmb7xx_matched(const struct dmi_system_id *dmi)
mlxplat_fan = &mlxplat_default_fan_data; mlxplat_fan = &mlxplat_default_fan_data;
for (i = 0; i < ARRAY_SIZE(mlxplat_mlxcpld_wd_set_type2); i++) for (i = 0; i < ARRAY_SIZE(mlxplat_mlxcpld_wd_set_type2); i++)
mlxplat_wd_data[i] = &mlxplat_mlxcpld_wd_set_type2[i]; mlxplat_wd_data[i] = &mlxplat_mlxcpld_wd_set_type2[i];
mlxplat_i2c = &mlxplat_mlxcpld_i2c_ng_data;
mlxplat_regmap_config = &mlxplat_mlxcpld_regmap_config_ng;
return 1; return 1;
}; };
static const struct dmi_system_id mlxplat_dmi_table[] __initconst = { static const struct dmi_system_id mlxplat_dmi_table[] __initconst = {
{
.callback = mlxplat_dmi_default_matched,
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "VMOD0001"),
},
},
{
.callback = mlxplat_dmi_msn21xx_matched,
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "VMOD0002"),
},
},
{
.callback = mlxplat_dmi_msn274x_matched,
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "VMOD0003"),
},
},
{
.callback = mlxplat_dmi_msn201x_matched,
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "VMOD0004"),
},
},
{
.callback = mlxplat_dmi_qmb7xx_matched,
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "VMOD0005"),
},
},
{
.callback = mlxplat_dmi_qmb7xx_matched,
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "VMOD0007"),
},
},
{ {
.callback = mlxplat_dmi_msn274x_matched, .callback = mlxplat_dmi_msn274x_matched,
.matches = { .matches = {
@ -1916,42 +2030,6 @@ static const struct dmi_system_id mlxplat_dmi_table[] __initconst = {
DMI_MATCH(DMI_PRODUCT_NAME, "MSN38"), DMI_MATCH(DMI_PRODUCT_NAME, "MSN38"),
}, },
}, },
{
.callback = mlxplat_dmi_default_matched,
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "VMOD0001"),
},
},
{
.callback = mlxplat_dmi_msn21xx_matched,
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "VMOD0002"),
},
},
{
.callback = mlxplat_dmi_msn274x_matched,
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "VMOD0003"),
},
},
{
.callback = mlxplat_dmi_msn201x_matched,
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "VMOD0004"),
},
},
{
.callback = mlxplat_dmi_qmb7xx_matched,
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "VMOD0005"),
},
},
{
.callback = mlxplat_dmi_qmb7xx_matched,
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "VMOD0007"),
},
},
{ } { }
}; };
@ -2018,13 +2096,36 @@ static int __init mlxplat_init(void)
} }
platform_set_drvdata(mlxplat_dev, priv); platform_set_drvdata(mlxplat_dev, priv);
mlxplat_mlxcpld_regmap_ctx.base = devm_ioport_map(&mlxplat_dev->dev,
mlxplat_lpc_resources[1].start, 1);
if (!mlxplat_mlxcpld_regmap_ctx.base) {
err = -ENOMEM;
goto fail_alloc;
}
if (!mlxplat_regmap_config)
mlxplat_regmap_config = &mlxplat_mlxcpld_regmap_config;
priv->regmap = devm_regmap_init(&mlxplat_dev->dev, NULL,
&mlxplat_mlxcpld_regmap_ctx,
mlxplat_regmap_config);
if (IS_ERR(priv->regmap)) {
err = PTR_ERR(priv->regmap);
goto fail_alloc;
}
err = mlxplat_mlxcpld_verify_bus_topology(&nr); err = mlxplat_mlxcpld_verify_bus_topology(&nr);
if (nr < 0) if (nr < 0)
goto fail_alloc; goto fail_alloc;
nr = (nr == MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM) ? -1 : nr; nr = (nr == MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM) ? -1 : nr;
priv->pdev_i2c = platform_device_register_simple("i2c_mlxcpld", nr, if (mlxplat_i2c)
NULL, 0); mlxplat_i2c->regmap = priv->regmap;
priv->pdev_i2c = platform_device_register_resndata(
&mlxplat_dev->dev, "i2c_mlxcpld",
nr, mlxplat_mlxcpld_resources,
ARRAY_SIZE(mlxplat_mlxcpld_resources),
mlxplat_i2c, sizeof(*mlxplat_i2c));
if (IS_ERR(priv->pdev_i2c)) { if (IS_ERR(priv->pdev_i2c)) {
err = PTR_ERR(priv->pdev_i2c); err = PTR_ERR(priv->pdev_i2c);
goto fail_alloc; goto fail_alloc;
@ -2042,21 +2143,8 @@ static int __init mlxplat_init(void)
} }
} }
mlxplat_mlxcpld_regmap_ctx.base = devm_ioport_map(&mlxplat_dev->dev, /* Add hotplug driver */
mlxplat_lpc_resources[1].start, 1); mlxplat_hotplug->regmap = priv->regmap;
if (!mlxplat_mlxcpld_regmap_ctx.base) {
err = -ENOMEM;
goto fail_platform_mux_register;
}
mlxplat_hotplug->regmap = devm_regmap_init(&mlxplat_dev->dev, NULL,
&mlxplat_mlxcpld_regmap_ctx,
&mlxplat_mlxcpld_regmap_config);
if (IS_ERR(mlxplat_hotplug->regmap)) {
err = PTR_ERR(mlxplat_hotplug->regmap);
goto fail_platform_mux_register;
}
priv->pdev_hotplug = platform_device_register_resndata( priv->pdev_hotplug = platform_device_register_resndata(
&mlxplat_dev->dev, "mlxreg-hotplug", &mlxplat_dev->dev, "mlxreg-hotplug",
PLATFORM_DEVID_NONE, PLATFORM_DEVID_NONE,
@ -2069,16 +2157,16 @@ static int __init mlxplat_init(void)
} }
/* Set default registers. */ /* Set default registers. */
for (j = 0; j < mlxplat_mlxcpld_regmap_config.num_reg_defaults; j++) { for (j = 0; j < mlxplat_regmap_config->num_reg_defaults; j++) {
err = regmap_write(mlxplat_hotplug->regmap, err = regmap_write(priv->regmap,
mlxplat_mlxcpld_regmap_default[j].reg, mlxplat_regmap_config->reg_defaults[j].reg,
mlxplat_mlxcpld_regmap_default[j].def); mlxplat_regmap_config->reg_defaults[j].def);
if (err) if (err)
goto fail_platform_mux_register; goto fail_platform_mux_register;
} }
/* Add LED driver. */ /* Add LED driver. */
mlxplat_led->regmap = mlxplat_hotplug->regmap; mlxplat_led->regmap = priv->regmap;
priv->pdev_led = platform_device_register_resndata( priv->pdev_led = platform_device_register_resndata(
&mlxplat_dev->dev, "leds-mlxreg", &mlxplat_dev->dev, "leds-mlxreg",
PLATFORM_DEVID_NONE, NULL, 0, PLATFORM_DEVID_NONE, NULL, 0,
@ -2090,7 +2178,7 @@ static int __init mlxplat_init(void)
/* Add registers io access driver. */ /* Add registers io access driver. */
if (mlxplat_regs_io) { if (mlxplat_regs_io) {
mlxplat_regs_io->regmap = mlxplat_hotplug->regmap; mlxplat_regs_io->regmap = priv->regmap;
priv->pdev_io_regs = platform_device_register_resndata( priv->pdev_io_regs = platform_device_register_resndata(
&mlxplat_dev->dev, "mlxreg-io", &mlxplat_dev->dev, "mlxreg-io",
PLATFORM_DEVID_NONE, NULL, 0, PLATFORM_DEVID_NONE, NULL, 0,
@ -2104,7 +2192,7 @@ static int __init mlxplat_init(void)
/* Add FAN driver. */ /* Add FAN driver. */
if (mlxplat_fan) { if (mlxplat_fan) {
mlxplat_fan->regmap = mlxplat_hotplug->regmap; mlxplat_fan->regmap = priv->regmap;
priv->pdev_fan = platform_device_register_resndata( priv->pdev_fan = platform_device_register_resndata(
&mlxplat_dev->dev, "mlxreg-fan", &mlxplat_dev->dev, "mlxreg-fan",
PLATFORM_DEVID_NONE, NULL, 0, PLATFORM_DEVID_NONE, NULL, 0,
@ -2119,7 +2207,7 @@ static int __init mlxplat_init(void)
/* Add WD drivers. */ /* Add WD drivers. */
for (j = 0; j < MLXPLAT_CPLD_WD_MAX_DEVS; j++) { for (j = 0; j < MLXPLAT_CPLD_WD_MAX_DEVS; j++) {
if (mlxplat_wd_data[j]) { if (mlxplat_wd_data[j]) {
mlxplat_wd_data[j]->regmap = mlxplat_hotplug->regmap; mlxplat_wd_data[j]->regmap = priv->regmap;
priv->pdev_wd[j] = platform_device_register_resndata( priv->pdev_wd[j] = platform_device_register_resndata(
&mlxplat_dev->dev, "mlx-wdt", &mlxplat_dev->dev, "mlx-wdt",
j, NULL, 0, j, NULL, 0,
@ -2133,8 +2221,8 @@ static int __init mlxplat_init(void)
} }
/* Sync registers with hardware. */ /* Sync registers with hardware. */
regcache_mark_dirty(mlxplat_hotplug->regmap); regcache_mark_dirty(priv->regmap);
err = regcache_sync(mlxplat_hotplug->regmap); err = regcache_sync(priv->regmap);
if (err) if (err)
goto fail_platform_wd_register; goto fail_platform_wd_register;

View File

@ -77,7 +77,7 @@ static const struct gpio_led_platform_data apu2_leds_pdata = {
.leds = apu2_leds, .leds = apu2_leds,
}; };
struct gpiod_lookup_table gpios_led_table = { static struct gpiod_lookup_table gpios_led_table = {
.dev_id = "leds-gpio", .dev_id = "leds-gpio",
.table = { .table = {
GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED1, GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED1,
@ -110,7 +110,7 @@ static const struct gpio_keys_platform_data apu2_keys_pdata = {
.name = "apu2-keys", .name = "apu2-keys",
}; };
struct gpiod_lookup_table gpios_key_table = { static struct gpiod_lookup_table gpios_key_table = {
.dev_id = "gpio-keys-polled", .dev_id = "gpio-keys-polled",
.table = { .table = {
GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_MODESW, GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_MODESW,

View File

@ -341,45 +341,24 @@ static int pmc_sleep_tmr_show(struct seq_file *s, void *unused)
DEFINE_SHOW_ATTRIBUTE(pmc_sleep_tmr); DEFINE_SHOW_ATTRIBUTE(pmc_sleep_tmr);
static void pmc_dbgfs_unregister(struct pmc_dev *pmc) static void pmc_dbgfs_register(struct pmc_dev *pmc)
{ {
debugfs_remove_recursive(pmc->dbgfs_dir); struct dentry *dir;
}
static int pmc_dbgfs_register(struct pmc_dev *pmc)
{
struct dentry *dir, *f;
dir = debugfs_create_dir("pmc_atom", NULL); dir = debugfs_create_dir("pmc_atom", NULL);
if (!dir)
return -ENOMEM;
pmc->dbgfs_dir = dir; pmc->dbgfs_dir = dir;
f = debugfs_create_file("dev_state", S_IFREG | S_IRUGO, debugfs_create_file("dev_state", S_IFREG | S_IRUGO, dir, pmc,
dir, pmc, &pmc_dev_state_fops); &pmc_dev_state_fops);
if (!f) debugfs_create_file("pss_state", S_IFREG | S_IRUGO, dir, pmc,
goto err; &pmc_pss_state_fops);
debugfs_create_file("sleep_state", S_IFREG | S_IRUGO, dir, pmc,
f = debugfs_create_file("pss_state", S_IFREG | S_IRUGO, &pmc_sleep_tmr_fops);
dir, pmc, &pmc_pss_state_fops);
if (!f)
goto err;
f = debugfs_create_file("sleep_state", S_IFREG | S_IRUGO,
dir, pmc, &pmc_sleep_tmr_fops);
if (!f)
goto err;
return 0;
err:
pmc_dbgfs_unregister(pmc);
return -ENODEV;
} }
#else #else
static int pmc_dbgfs_register(struct pmc_dev *pmc) static void pmc_dbgfs_register(struct pmc_dev *pmc)
{ {
return 0;
} }
#endif /* CONFIG_DEBUG_FS */ #endif /* CONFIG_DEBUG_FS */
@ -412,6 +391,14 @@ static const struct dmi_system_id critclk_systems[] = {
DMI_MATCH(DMI_BOARD_NAME, "CB3163"), DMI_MATCH(DMI_BOARD_NAME, "CB3163"),
}, },
}, },
{
/* pmc_plt_clk* - are used for ethernet controllers */
.ident = "Beckhoff CB4063",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Beckhoff Automation"),
DMI_MATCH(DMI_BOARD_NAME, "CB4063"),
},
},
{ {
/* pmc_plt_clk* - are used for ethernet controllers */ /* pmc_plt_clk* - are used for ethernet controllers */
.ident = "Beckhoff CB6263", .ident = "Beckhoff CB6263",
@ -491,9 +478,7 @@ static int pmc_setup_dev(struct pci_dev *pdev, const struct pci_device_id *ent)
/* PMC hardware registers setup */ /* PMC hardware registers setup */
pmc_hw_reg_setup(pmc); pmc_hw_reg_setup(pmc);
ret = pmc_dbgfs_register(pmc); pmc_dbgfs_register(pmc);
if (ret)
dev_warn(&pdev->dev, "debugfs register failed\n");
/* Register platform clocks - PMC_PLT_CLK [0..5] */ /* Register platform clocks - PMC_PLT_CLK [0..5] */
ret = pmc_setup_clks(pdev, pmc->regmap, data); ret = pmc_setup_clks(pdev, pmc->regmap, data);

View File

@ -1276,15 +1276,12 @@ static void samsung_debugfs_exit(struct samsung_laptop *samsung)
debugfs_remove_recursive(samsung->debug.root); debugfs_remove_recursive(samsung->debug.root);
} }
static int samsung_debugfs_init(struct samsung_laptop *samsung) static void samsung_debugfs_init(struct samsung_laptop *samsung)
{ {
struct dentry *dent; struct dentry *root;
samsung->debug.root = debugfs_create_dir("samsung-laptop", NULL); root = debugfs_create_dir("samsung-laptop", NULL);
if (!samsung->debug.root) { samsung->debug.root = root;
pr_err("failed to create debugfs directory");
goto error_debugfs;
}
samsung->debug.f0000_wrapper.data = samsung->f0000_segment; samsung->debug.f0000_wrapper.data = samsung->f0000_segment;
samsung->debug.f0000_wrapper.size = 0xffff; samsung->debug.f0000_wrapper.size = 0xffff;
@ -1295,60 +1292,24 @@ static int samsung_debugfs_init(struct samsung_laptop *samsung)
samsung->debug.sdiag_wrapper.data = samsung->sdiag; samsung->debug.sdiag_wrapper.data = samsung->sdiag;
samsung->debug.sdiag_wrapper.size = strlen(samsung->sdiag); samsung->debug.sdiag_wrapper.size = strlen(samsung->sdiag);
dent = debugfs_create_u16("command", S_IRUGO | S_IWUSR, debugfs_create_u16("command", S_IRUGO | S_IWUSR, root,
samsung->debug.root, &samsung->debug.command); &samsung->debug.command);
if (!dent) debugfs_create_u32("d0", S_IRUGO | S_IWUSR, root,
goto error_debugfs; &samsung->debug.data.d0);
debugfs_create_u32("d1", S_IRUGO | S_IWUSR, root,
dent = debugfs_create_u32("d0", S_IRUGO | S_IWUSR, samsung->debug.root, &samsung->debug.data.d1);
&samsung->debug.data.d0); debugfs_create_u16("d2", S_IRUGO | S_IWUSR, root,
if (!dent) &samsung->debug.data.d2);
goto error_debugfs; debugfs_create_u8("d3", S_IRUGO | S_IWUSR, root,
&samsung->debug.data.d3);
dent = debugfs_create_u32("d1", S_IRUGO | S_IWUSR, samsung->debug.root, debugfs_create_blob("data", S_IRUGO | S_IWUSR, root,
&samsung->debug.data.d1); &samsung->debug.data_wrapper);
if (!dent) debugfs_create_blob("f0000_segment", S_IRUSR | S_IWUSR, root,
goto error_debugfs; &samsung->debug.f0000_wrapper);
debugfs_create_file("call", S_IFREG | S_IRUGO, root, samsung,
dent = debugfs_create_u16("d2", S_IRUGO | S_IWUSR, samsung->debug.root, &samsung_laptop_call_fops);
&samsung->debug.data.d2); debugfs_create_blob("sdiag", S_IRUGO | S_IWUSR, root,
if (!dent) &samsung->debug.sdiag_wrapper);
goto error_debugfs;
dent = debugfs_create_u8("d3", S_IRUGO | S_IWUSR, samsung->debug.root,
&samsung->debug.data.d3);
if (!dent)
goto error_debugfs;
dent = debugfs_create_blob("data", S_IRUGO | S_IWUSR,
samsung->debug.root,
&samsung->debug.data_wrapper);
if (!dent)
goto error_debugfs;
dent = debugfs_create_blob("f0000_segment", S_IRUSR | S_IWUSR,
samsung->debug.root,
&samsung->debug.f0000_wrapper);
if (!dent)
goto error_debugfs;
dent = debugfs_create_file("call", S_IFREG | S_IRUGO,
samsung->debug.root, samsung,
&samsung_laptop_call_fops);
if (!dent)
goto error_debugfs;
dent = debugfs_create_blob("sdiag", S_IRUGO | S_IWUSR,
samsung->debug.root,
&samsung->debug.sdiag_wrapper);
if (!dent)
goto error_debugfs;
return 0;
error_debugfs:
samsung_debugfs_exit(samsung);
return -ENOMEM;
} }
static void samsung_sabi_exit(struct samsung_laptop *samsung) static void samsung_sabi_exit(struct samsung_laptop *samsung)
@ -1741,9 +1702,7 @@ static int __init samsung_init(void)
if (ret) if (ret)
goto error_lid_handling; goto error_lid_handling;
ret = samsung_debugfs_init(samsung); samsung_debugfs_init(samsung);
if (ret)
goto error_debugfs;
samsung->pm_nb.notifier_call = samsung_pm_notification; samsung->pm_nb.notifier_call = samsung_pm_notification;
register_pm_notifier(&samsung->pm_nb); register_pm_notifier(&samsung->pm_nb);
@ -1751,8 +1710,6 @@ static int __init samsung_init(void)
samsung_platform_device = samsung->platform_device; samsung_platform_device = samsung->platform_device;
return ret; return ret;
error_debugfs:
samsung_lid_handling_exit(samsung);
error_lid_handling: error_lid_handling:
samsung_leds_exit(samsung); samsung_leds_exit(samsung);
error_leds: error_leds:

View File

@ -87,6 +87,22 @@ static const struct ts_dmi_data chuwi_hi10_air_data = {
.properties = chuwi_hi10_air_props, .properties = chuwi_hi10_air_props,
}; };
static const struct property_entry chuwi_hi10_plus_props[] = {
PROPERTY_ENTRY_U32("touchscreen-min-x", 0),
PROPERTY_ENTRY_U32("touchscreen-min-y", 5),
PROPERTY_ENTRY_U32("touchscreen-size-x", 1914),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1283),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi10plus.fw"),
PROPERTY_ENTRY_U32("silead,max-fingers", 10),
PROPERTY_ENTRY_BOOL("silead,home-button"),
{ }
};
static const struct ts_dmi_data chuwi_hi10_plus_data = {
.acpi_name = "MSSL0017:00",
.properties = chuwi_hi10_plus_props,
};
static const struct property_entry chuwi_vi8_props[] = { static const struct property_entry chuwi_vi8_props[] = {
PROPERTY_ENTRY_U32("touchscreen-min-x", 4), PROPERTY_ENTRY_U32("touchscreen-min-x", 4),
PROPERTY_ENTRY_U32("touchscreen-min-y", 6), PROPERTY_ENTRY_U32("touchscreen-min-y", 6),
@ -597,10 +613,20 @@ static const struct dmi_system_id touchscreen_dmi_table[] = {
/* Chuwi Hi10 Air */ /* Chuwi Hi10 Air */
.driver_data = (void *)&chuwi_hi10_air_data, .driver_data = (void *)&chuwi_hi10_air_data,
.matches = { .matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"), DMI_MATCH(DMI_SYS_VENDOR, "CHUWI INNOVATION AND TECHNOLOGY(SHENZHEN)CO.LTD"),
DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"),
DMI_MATCH(DMI_PRODUCT_SKU, "P1W6_C109D_B"), DMI_MATCH(DMI_PRODUCT_SKU, "P1W6_C109D_B"),
}, },
}, },
{
/* Chuwi Hi10 Plus (CWI527) */
.driver_data = (void *)&chuwi_hi10_plus_data,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"),
DMI_MATCH(DMI_PRODUCT_NAME, "Hi10 plus tablet"),
DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"),
},
},
{ {
/* Chuwi Vi8 (CWI506) */ /* Chuwi Vi8 (CWI506) */
.driver_data = (void *)&chuwi_vi8_data, .driver_data = (void *)&chuwi_vi8_data,

View File

@ -46,7 +46,7 @@ read_bmof(struct file *filp, struct kobject *kobj,
return count; return count;
} }
static int wmi_bmof_probe(struct wmi_device *wdev) static int wmi_bmof_probe(struct wmi_device *wdev, const void *context)
{ {
struct bmof_priv *priv; struct bmof_priv *priv;
int ret; int ret;

View File

@ -129,6 +129,28 @@ static bool find_guid(const char *guid_string, struct wmi_block **out)
return false; return false;
} }
static const void *find_guid_context(struct wmi_block *wblock,
struct wmi_driver *wdriver)
{
const struct wmi_device_id *id;
uuid_le guid_input;
if (wblock == NULL || wdriver == NULL)
return NULL;
if (wdriver->id_table == NULL)
return NULL;
id = wdriver->id_table;
while (*id->guid_string) {
if (uuid_le_to_bin(id->guid_string, &guid_input))
continue;
if (!memcmp(wblock->gblock.guid, &guid_input, 16))
return id->context;
id++;
}
return NULL;
}
static int get_subobj_info(acpi_handle handle, const char *pathname, static int get_subobj_info(acpi_handle handle, const char *pathname,
struct acpi_device_info **info) struct acpi_device_info **info)
{ {
@ -618,6 +640,25 @@ bool wmi_has_guid(const char *guid_string)
} }
EXPORT_SYMBOL_GPL(wmi_has_guid); EXPORT_SYMBOL_GPL(wmi_has_guid);
/**
* wmi_get_acpi_device_uid() - Get _UID name of ACPI device that defines GUID
* @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
*
* Find the _UID of ACPI device associated with this WMI GUID.
*
* Return: The ACPI _UID field value or NULL if the WMI GUID was not found
*/
char *wmi_get_acpi_device_uid(const char *guid_string)
{
struct wmi_block *wblock = NULL;
if (!find_guid(guid_string, &wblock))
return NULL;
return acpi_device_uid(wblock->acpi_device);
}
EXPORT_SYMBOL_GPL(wmi_get_acpi_device_uid);
static struct wmi_block *dev_to_wblock(struct device *dev) static struct wmi_block *dev_to_wblock(struct device *dev)
{ {
return container_of(dev, struct wmi_block, dev.dev); return container_of(dev, struct wmi_block, dev.dev);
@ -887,7 +928,8 @@ static int wmi_dev_probe(struct device *dev)
dev_warn(dev, "failed to enable device -- probing anyway\n"); dev_warn(dev, "failed to enable device -- probing anyway\n");
if (wdriver->probe) { if (wdriver->probe) {
ret = wdriver->probe(dev_to_wdev(dev)); ret = wdriver->probe(dev_to_wdev(dev),
find_guid_context(wblock, wdriver));
if (ret != 0) if (ret != 0)
goto probe_failure; goto probe_failure;
} }

View File

@ -0,0 +1,92 @@
// SPDX-License-Identifier: GPL-2.0
/* WMI driver for Xiaomi Laptops */
#include <linux/acpi.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/wmi.h>
#include <uapi/linux/input-event-codes.h>
#define XIAOMI_KEY_FN_ESC_0 "A2095CCE-0491-44E7-BA27-F8ED8F88AA86"
#define XIAOMI_KEY_FN_ESC_1 "7BBE8E39-B486-473D-BA13-66F75C5805CD"
#define XIAOMI_KEY_FN_FN "409B028D-F06B-4C7C-8BBB-EE133A6BD87E"
#define XIAOMI_KEY_CAPSLOCK "83FE7607-053A-4644-822A-21532C621FC7"
#define XIAOMI_KEY_FN_F7 "76E9027C-95D0-4180-8692-DA6747DD1C2D"
#define XIAOMI_DEVICE(guid, key) \
.guid_string = (guid), \
.context = &(const unsigned int){key}
struct xiaomi_wmi {
struct input_dev *input_dev;
unsigned int key_code;
};
int xiaomi_wmi_probe(struct wmi_device *wdev, const void *context)
{
struct xiaomi_wmi *data;
if (wdev == NULL || context == NULL)
return -EINVAL;
data = devm_kzalloc(&wdev->dev, sizeof(struct xiaomi_wmi), GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
dev_set_drvdata(&wdev->dev, data);
data->input_dev = devm_input_allocate_device(&wdev->dev);
if (data->input_dev == NULL)
return -ENOMEM;
data->input_dev->name = "Xiaomi WMI keys";
data->input_dev->phys = "wmi/input0";
data->key_code = *((const unsigned int *)context);
set_bit(EV_KEY, data->input_dev->evbit);
set_bit(data->key_code, data->input_dev->keybit);
return input_register_device(data->input_dev);
}
void xiaomi_wmi_notify(struct wmi_device *wdev, union acpi_object *dummy)
{
struct xiaomi_wmi *data;
if (wdev == NULL)
return;
data = dev_get_drvdata(&wdev->dev);
if (data == NULL)
return;
input_report_key(data->input_dev, data->key_code, 1);
input_sync(data->input_dev);
input_report_key(data->input_dev, data->key_code, 0);
input_sync(data->input_dev);
}
static const struct wmi_device_id xiaomi_wmi_id_table[] = {
// { XIAOMI_DEVICE(XIAOMI_KEY_FN_ESC_0, KEY_FN_ESC) },
// { XIAOMI_DEVICE(XIAOMI_KEY_FN_ESC_1, KEY_FN_ESC) },
{ XIAOMI_DEVICE(XIAOMI_KEY_FN_FN, KEY_PROG1) },
// { XIAOMI_DEVICE(XIAOMI_KEY_CAPSLOCK, KEY_CAPSLOCK) },
{ XIAOMI_DEVICE(XIAOMI_KEY_FN_F7, KEY_CUT) },
/* Terminating entry */
{ }
};
static struct wmi_driver xiaomi_wmi_driver = {
.driver = {
.name = "xiaomi-wmi",
},
.id_table = xiaomi_wmi_id_table,
.probe = xiaomi_wmi_probe,
.notify = xiaomi_wmi_notify,
};
module_wmi_driver(xiaomi_wmi_driver);
MODULE_DEVICE_TABLE(wmi, xiaomi_wmi_id_table);
MODULE_AUTHOR("Mattias Jacobsson");
MODULE_DESCRIPTION("Xiaomi WMI driver");
MODULE_LICENSE("GPL v2");

View File

@ -152,7 +152,7 @@ config BATTERY_PMU
config BATTERY_OLPC config BATTERY_OLPC
tristate "One Laptop Per Child battery" tristate "One Laptop Per Child battery"
depends on X86_32 && OLPC depends on OLPC_EC
help help
Say Y to enable support for the battery on the OLPC laptop. Say Y to enable support for the battery on the OLPC laptop.

View File

@ -17,7 +17,6 @@
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/olpc-ec.h> #include <linux/olpc-ec.h>
#include <asm/olpc.h>
#define EC_BAT_VOLTAGE 0x10 /* uint16_t, *9.76/32, mV */ #define EC_BAT_VOLTAGE 0x10 /* uint16_t, *9.76/32, mV */

View File

@ -374,6 +374,7 @@ extern acpi_status wmi_install_notify_handler(const char *guid,
extern acpi_status wmi_remove_notify_handler(const char *guid); extern acpi_status wmi_remove_notify_handler(const char *guid);
extern acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out); extern acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out);
extern bool wmi_has_guid(const char *guid); extern bool wmi_has_guid(const char *guid);
extern char *wmi_get_acpi_device_uid(const char *guid);
#endif /* CONFIG_ACPI_WMI */ #endif /* CONFIG_ACPI_WMI */

View File

@ -798,6 +798,7 @@ struct tee_client_device_id {
*/ */
struct wmi_device_id { struct wmi_device_id {
const char guid_string[UUID_STRING_LEN+1]; const char guid_string[UUID_STRING_LEN+1];
const void *context;
}; };
#endif /* LINUX_MOD_DEVICETABLE_H */ #endif /* LINUX_MOD_DEVICETABLE_H */

View File

@ -2,6 +2,8 @@
#ifndef _LINUX_OLPC_EC_H #ifndef _LINUX_OLPC_EC_H
#define _LINUX_OLPC_EC_H #define _LINUX_OLPC_EC_H
#include <linux/bits.h>
/* XO-1 EC commands */ /* XO-1 EC commands */
#define EC_FIRMWARE_REV 0x08 #define EC_FIRMWARE_REV 0x08
#define EC_WRITE_SCI_MASK 0x1b #define EC_WRITE_SCI_MASK 0x1b
@ -16,28 +18,57 @@
#define EC_SCI_QUERY 0x84 #define EC_SCI_QUERY 0x84
#define EC_EXT_SCI_QUERY 0x85 #define EC_EXT_SCI_QUERY 0x85
/* SCI source values */
#define EC_SCI_SRC_GAME BIT(0)
#define EC_SCI_SRC_BATTERY BIT(1)
#define EC_SCI_SRC_BATSOC BIT(2)
#define EC_SCI_SRC_BATERR BIT(3)
#define EC_SCI_SRC_EBOOK BIT(4) /* XO-1 only */
#define EC_SCI_SRC_WLAN BIT(5) /* XO-1 only */
#define EC_SCI_SRC_ACPWR BIT(6)
#define EC_SCI_SRC_BATCRIT BIT(7)
#define EC_SCI_SRC_GPWAKE BIT(8) /* XO-1.5 only */
#define EC_SCI_SRC_ALL GENMASK(8, 0)
struct platform_device; struct platform_device;
struct olpc_ec_driver { struct olpc_ec_driver {
int (*probe)(struct platform_device *);
int (*suspend)(struct platform_device *); int (*suspend)(struct platform_device *);
int (*resume)(struct platform_device *); int (*resume)(struct platform_device *);
int (*ec_cmd)(u8, u8 *, size_t, u8 *, size_t, void *); int (*ec_cmd)(u8, u8 *, size_t, u8 *, size_t, void *);
bool wakeup_available;
}; };
#ifdef CONFIG_OLPC #ifdef CONFIG_OLPC_EC
extern void olpc_ec_driver_register(struct olpc_ec_driver *drv, void *arg); extern void olpc_ec_driver_register(struct olpc_ec_driver *drv, void *arg);
extern int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, extern int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf,
size_t outlen); size_t outlen);
extern void olpc_ec_wakeup_set(u16 value);
extern void olpc_ec_wakeup_clear(u16 value);
extern int olpc_ec_mask_write(u16 bits);
extern int olpc_ec_sci_query(u16 *sci_value);
extern bool olpc_ec_wakeup_available(void);
#else #else
static inline int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, static inline int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf,
size_t outlen) { return -ENODEV; } size_t outlen) { return -ENODEV; }
#endif /* CONFIG_OLPC */ static inline void olpc_ec_wakeup_set(u16 value) { }
static inline void olpc_ec_wakeup_clear(u16 value) { }
static inline bool olpc_ec_wakeup_available(void)
{
return false;
}
#endif /* CONFIG_OLPC_EC */
#endif /* _LINUX_OLPC_EC_H */ #endif /* _LINUX_OLPC_EC_H */

View File

@ -18,8 +18,8 @@
#define ASUS_WMI_METHODID_GDSP 0x50534447 /* Get DiSPlay output */ #define ASUS_WMI_METHODID_GDSP 0x50534447 /* Get DiSPlay output */
#define ASUS_WMI_METHODID_DEVP 0x50564544 /* DEVice Policy */ #define ASUS_WMI_METHODID_DEVP 0x50564544 /* DEVice Policy */
#define ASUS_WMI_METHODID_OSVR 0x5256534F /* OS VeRsion */ #define ASUS_WMI_METHODID_OSVR 0x5256534F /* OS VeRsion */
#define ASUS_WMI_METHODID_DSTS 0x53544344 /* Device STatuS */ #define ASUS_WMI_METHODID_DCTS 0x53544344 /* Device status (DCTS) */
#define ASUS_WMI_METHODID_DSTS2 0x53545344 /* Device STatuS #2*/ #define ASUS_WMI_METHODID_DSTS 0x53545344 /* Device status (DSTS) */
#define ASUS_WMI_METHODID_BSTS 0x53545342 /* Bios STatuS ? */ #define ASUS_WMI_METHODID_BSTS 0x53545342 /* Bios STatuS ? */
#define ASUS_WMI_METHODID_DEVS 0x53564544 /* DEVice Set */ #define ASUS_WMI_METHODID_DEVS 0x53564544 /* DEVice Set */
#define ASUS_WMI_METHODID_CFVS 0x53564643 /* CPU Frequency Volt Set */ #define ASUS_WMI_METHODID_CFVS 0x53564643 /* CPU Frequency Volt Set */
@ -57,6 +57,7 @@
#define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021 #define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021
#define ASUS_WMI_DEVID_LIGHT_SENSOR 0x00050022 /* ?? */ #define ASUS_WMI_DEVID_LIGHT_SENSOR 0x00050022 /* ?? */
#define ASUS_WMI_DEVID_LIGHTBAR 0x00050025 #define ASUS_WMI_DEVID_LIGHTBAR 0x00050025
#define ASUS_WMI_DEVID_FAN_MODE 0x00110018
/* Misc */ /* Misc */
#define ASUS_WMI_DEVID_CAMERA 0x00060013 #define ASUS_WMI_DEVID_CAMERA 0x00060013

View File

@ -36,7 +36,7 @@ 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;
int (*probe)(struct wmi_device *wdev); int (*probe)(struct wmi_device *wdev, const void *context);
int (*remove)(struct wmi_device *wdev); int (*remove)(struct wmi_device *wdev);
void (*notify)(struct wmi_device *device, union acpi_object *data); void (*notify)(struct wmi_device *device, union acpi_object *data);
long (*filter_callback)(struct wmi_device *wdev, unsigned int cmd, long (*filter_callback)(struct wmi_device *wdev, unsigned int cmd,

View File

@ -0,0 +1,172 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Intel Speed Select Interface: OS to hardware Interface
* Copyright (c) 2019, Intel Corporation.
* All rights reserved.
*
* Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
*/
#ifndef __ISST_IF_H
#define __ISST_IF_H
#include <linux/types.h>
/**
* struct isst_if_platform_info - Define platform information
* @api_version: Version of the firmware document, which this driver
* can communicate
* @driver_version: Driver version, which will help user to send right
* commands. Even if the firmware is capable, driver may
* not be ready
* @max_cmds_per_ioctl: Returns the maximum number of commands driver will
* accept in a single ioctl
* @mbox_supported: Support of mail box interface
* @mmio_supported: Support of mmio interface for core-power feature
*
* Used to return output of IOCTL ISST_IF_GET_PLATFORM_INFO. This
* information can be used by the user space, to get the driver, firmware
* support and also number of commands to send in a single IOCTL request.
*/
struct isst_if_platform_info {
__u16 api_version;
__u16 driver_version;
__u16 max_cmds_per_ioctl;
__u8 mbox_supported;
__u8 mmio_supported;
};
/**
* struct isst_if_cpu_map - CPU mapping between logical and physical CPU
* @logical_cpu: Linux logical CPU number
* @physical_cpu: PUNIT CPU number
*
* Used to convert from Linux logical CPU to PUNIT CPU numbering scheme.
* The PUNIT CPU number is different than APIC ID based CPU numbering.
*/
struct isst_if_cpu_map {
__u32 logical_cpu;
__u32 physical_cpu;
};
/**
* struct isst_if_cpu_maps - structure for CPU map IOCTL
* @cmd_count: Number of CPU mapping command in cpu_map[]
* @cpu_map[]: Holds one or more CPU map data structure
*
* This structure used with ioctl ISST_IF_GET_PHY_ID to send
* one or more CPU mapping commands. Here IOCTL return value indicates
* number of commands sent or error number if no commands have been sent.
*/
struct isst_if_cpu_maps {
__u32 cmd_count;
struct isst_if_cpu_map cpu_map[1];
};
/**
* struct isst_if_io_reg - Read write PUNIT IO register
* @read_write: Value 0: Read, 1: Write
* @logical_cpu: Logical CPU number to get target PCI device.
* @reg: PUNIT register offset
* @value: For write operation value to write and for
* for read placeholder read value
*
* Structure to specify read/write data to PUNIT registers.
*/
struct isst_if_io_reg {
__u32 read_write; /* Read:0, Write:1 */
__u32 logical_cpu;
__u32 reg;
__u32 value;
};
/**
* struct isst_if_io_regs - structure for IO register commands
* @cmd_count: Number of io reg commands in io_reg[]
* @io_reg[]: Holds one or more io_reg command structure
*
* This structure used with ioctl ISST_IF_IO_CMD to send
* one or more read/write commands to PUNIT. Here IOCTL return value
* indicates number of requests sent or error number if no requests have
* been sent.
*/
struct isst_if_io_regs {
__u32 req_count;
struct isst_if_io_reg io_reg[1];
};
/**
* struct isst_if_mbox_cmd - Structure to define mail box command
* @logical_cpu: Logical CPU number to get target PCI device
* @parameter: Mailbox parameter value
* @req_data: Request data for the mailbox
* @resp_data: Response data for mailbox command response
* @command: Mailbox command value
* @sub_command: Mailbox sub command value
* @reserved: Unused, set to 0
*
* Structure to specify mailbox command to be sent to PUNIT.
*/
struct isst_if_mbox_cmd {
__u32 logical_cpu;
__u32 parameter;
__u32 req_data;
__u32 resp_data;
__u16 command;
__u16 sub_command;
__u32 reserved;
};
/**
* struct isst_if_mbox_cmds - structure for mailbox commands
* @cmd_count: Number of mailbox commands in mbox_cmd[]
* @mbox_cmd[]: Holds one or more mbox commands
*
* This structure used with ioctl ISST_IF_MBOX_COMMAND to send
* one or more mailbox commands to PUNIT. Here IOCTL return value
* indicates number of commands sent or error number if no commands have
* been sent.
*/
struct isst_if_mbox_cmds {
__u32 cmd_count;
struct isst_if_mbox_cmd mbox_cmd[1];
};
/**
* struct isst_if_msr_cmd - Structure to define msr command
* @read_write: Value 0: Read, 1: Write
* @logical_cpu: Logical CPU number
* @msr: MSR number
* @data: For write operation, data to write, for read
* place holder
*
* Structure to specify MSR command related to PUNIT.
*/
struct isst_if_msr_cmd {
__u32 read_write; /* Read:0, Write:1 */
__u32 logical_cpu;
__u64 msr;
__u64 data;
};
/**
* struct isst_if_msr_cmds - structure for msr commands
* @cmd_count: Number of mailbox commands in msr_cmd[]
* @msr_cmd[]: Holds one or more msr commands
*
* This structure used with ioctl ISST_IF_MSR_COMMAND to send
* one or more MSR commands. IOCTL return value indicates number of
* commands sent or error number if no commands have been sent.
*/
struct isst_if_msr_cmds {
__u32 cmd_count;
struct isst_if_msr_cmd msr_cmd[1];
};
#define ISST_IF_MAGIC 0xFE
#define ISST_IF_GET_PLATFORM_INFO _IOR(ISST_IF_MAGIC, 0, struct isst_if_platform_info *)
#define ISST_IF_GET_PHY_ID _IOWR(ISST_IF_MAGIC, 1, struct isst_if_cpu_map *)
#define ISST_IF_IO_CMD _IOW(ISST_IF_MAGIC, 2, struct isst_if_io_regs *)
#define ISST_IF_MBOX_COMMAND _IOWR(ISST_IF_MAGIC, 3, struct isst_if_mbox_cmds *)
#define ISST_IF_MSR_COMMAND _IOWR(ISST_IF_MAGIC, 4, struct isst_if_msr_cmds *)
#endif

View File

@ -19,6 +19,7 @@ help:
@echo ' gpio - GPIO tools' @echo ' gpio - GPIO tools'
@echo ' hv - tools used when in Hyper-V clients' @echo ' hv - tools used when in Hyper-V clients'
@echo ' iio - IIO tools' @echo ' iio - IIO tools'
@echo ' intel-speed-select - Intel Speed Select tool'
@echo ' kvm_stat - top-like utility for displaying kvm statistics' @echo ' kvm_stat - top-like utility for displaying kvm statistics'
@echo ' leds - LEDs tools' @echo ' leds - LEDs tools'
@echo ' liblockdep - user-space wrapper for kernel locking-validator' @echo ' liblockdep - user-space wrapper for kernel locking-validator'
@ -82,7 +83,7 @@ perf: FORCE
selftests: FORCE selftests: FORCE
$(call descend,testing/$@) $(call descend,testing/$@)
turbostat x86_energy_perf_policy: FORCE turbostat x86_energy_perf_policy intel-speed-select: FORCE
$(call descend,power/x86/$@) $(call descend,power/x86/$@)
tmon: FORCE tmon: FORCE
@ -115,7 +116,7 @@ liblockdep_install:
selftests_install: selftests_install:
$(call descend,testing/$(@:_install=),install) $(call descend,testing/$(@:_install=),install)
turbostat_install x86_energy_perf_policy_install: turbostat_install x86_energy_perf_policy_install intel-speed-select_install:
$(call descend,power/x86/$(@:_install=),install) $(call descend,power/x86/$(@:_install=),install)
tmon_install: tmon_install:
@ -132,7 +133,7 @@ install: acpi_install cgroup_install cpupower_install gpio_install \
perf_install selftests_install turbostat_install usb_install \ perf_install selftests_install turbostat_install usb_install \
virtio_install vm_install bpf_install x86_energy_perf_policy_install \ virtio_install vm_install bpf_install x86_energy_perf_policy_install \
tmon_install freefall_install objtool_install kvm_stat_install \ tmon_install freefall_install objtool_install kvm_stat_install \
wmi_install pci_install debugging_install wmi_install pci_install debugging_install intel-speed-select_install
acpi_clean: acpi_clean:
$(call descend,power/acpi,clean) $(call descend,power/acpi,clean)
@ -162,7 +163,7 @@ perf_clean:
selftests_clean: selftests_clean:
$(call descend,testing/$(@:_clean=),clean) $(call descend,testing/$(@:_clean=),clean)
turbostat_clean x86_energy_perf_policy_clean: turbostat_clean x86_energy_perf_policy_clean intel-speed-select_clean:
$(call descend,power/x86/$(@:_clean=),clean) $(call descend,power/x86/$(@:_clean=),clean)
tmon_clean: tmon_clean:
@ -178,6 +179,7 @@ clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean \
perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \ perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \
vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \ vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \ freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \
gpio_clean objtool_clean leds_clean wmi_clean pci_clean firmware_clean debugging_clean gpio_clean objtool_clean leds_clean wmi_clean pci_clean firmware_clean debugging_clean \
intel-speed-select_clean
.PHONY: FORCE .PHONY: FORCE

View File

@ -0,0 +1,2 @@
include/
intel-speed-select

View File

@ -0,0 +1 @@
intel-speed-select-y += isst-config.o isst-core.o isst-display.o

View File

@ -0,0 +1,56 @@
# SPDX-License-Identifier: GPL-2.0
include ../../../scripts/Makefile.include
bindir ?= /usr/bin
ifeq ($(srctree),)
srctree := $(patsubst %/,%,$(dir $(CURDIR)))
srctree := $(patsubst %/,%,$(dir $(srctree)))
srctree := $(patsubst %/,%,$(dir $(srctree)))
srctree := $(patsubst %/,%,$(dir $(srctree)))
endif
# Do not use make's built-in rules
# (this improves performance and avoids hard-to-debug behaviour);
MAKEFLAGS += -r
override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include
ALL_TARGETS := intel-speed-select
ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))
all: $(ALL_PROGRAMS)
export srctree OUTPUT CC LD CFLAGS
include $(srctree)/tools/build/Makefile.include
#
# We need the following to be outside of kernel tree
#
$(OUTPUT)include/linux/isst_if.h: ../../../../include/uapi/linux/isst_if.h
mkdir -p $(OUTPUT)include/linux 2>&1 || true
ln -sf $(CURDIR)/../../../../include/uapi/linux/isst_if.h $@
prepare: $(OUTPUT)include/linux/isst_if.h
ISST_IN := $(OUTPUT)intel-speed-select-in.o
$(ISST_IN): prepare FORCE
$(Q)$(MAKE) $(build)=intel-speed-select
$(OUTPUT)intel-speed-select: $(ISST_IN)
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
clean:
rm -f $(ALL_PROGRAMS)
rm -rf $(OUTPUT)include/linux/isst_if.h
find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete
install: $(ALL_PROGRAMS)
install -d -m 755 $(DESTDIR)$(bindir); \
for program in $(ALL_PROGRAMS); do \
install $$program $(DESTDIR)$(bindir); \
done
FORCE:
.PHONY: all install clean FORCE prepare

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,721 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Intel Speed Select -- Enumerate and control features
* Copyright (c) 2019 Intel Corporation.
*/
#include "isst.h"
int isst_get_ctdp_levels(int cpu, struct isst_pkg_ctdp *pkg_dev)
{
unsigned int resp;
int ret;
ret = isst_send_mbox_command(cpu, CONFIG_TDP,
CONFIG_TDP_GET_LEVELS_INFO, 0, 0, &resp);
if (ret)
return ret;
debug_printf("cpu:%d CONFIG_TDP_GET_LEVELS_INFO resp:%x\n", cpu, resp);
pkg_dev->version = resp & 0xff;
pkg_dev->levels = (resp >> 8) & 0xff;
pkg_dev->current_level = (resp >> 16) & 0xff;
pkg_dev->locked = !!(resp & BIT(24));
pkg_dev->enabled = !!(resp & BIT(31));
return 0;
}
int isst_get_ctdp_control(int cpu, int config_index,
struct isst_pkg_ctdp_level_info *ctdp_level)
{
unsigned int resp;
int ret;
ret = isst_send_mbox_command(cpu, CONFIG_TDP,
CONFIG_TDP_GET_TDP_CONTROL, 0,
config_index, &resp);
if (ret)
return ret;
ctdp_level->fact_support = resp & BIT(0);
ctdp_level->pbf_support = !!(resp & BIT(1));
ctdp_level->fact_enabled = !!(resp & BIT(16));
ctdp_level->pbf_enabled = !!(resp & BIT(17));
debug_printf(
"cpu:%d CONFIG_TDP_GET_TDP_CONTROL resp:%x fact_support:%d pbf_support: %d fact_enabled:%d pbf_enabled:%d\n",
cpu, resp, ctdp_level->fact_support, ctdp_level->pbf_support,
ctdp_level->fact_enabled, ctdp_level->pbf_enabled);
return 0;
}
int isst_get_tdp_info(int cpu, int config_index,
struct isst_pkg_ctdp_level_info *ctdp_level)
{
unsigned int resp;
int ret;
ret = isst_send_mbox_command(cpu, CONFIG_TDP, CONFIG_TDP_GET_TDP_INFO,
0, config_index, &resp);
if (ret)
return ret;
ctdp_level->pkg_tdp = resp & GENMASK(14, 0);
ctdp_level->tdp_ratio = (resp & GENMASK(23, 16)) >> 16;
debug_printf(
"cpu:%d ctdp:%d CONFIG_TDP_GET_TDP_INFO resp:%x tdp_ratio:%d pkg_tdp:%d\n",
cpu, config_index, resp, ctdp_level->tdp_ratio,
ctdp_level->pkg_tdp);
return 0;
}
int isst_get_pwr_info(int cpu, int config_index,
struct isst_pkg_ctdp_level_info *ctdp_level)
{
unsigned int resp;
int ret;
ret = isst_send_mbox_command(cpu, CONFIG_TDP, CONFIG_TDP_GET_PWR_INFO,
0, config_index, &resp);
if (ret)
return ret;
ctdp_level->pkg_max_power = resp & GENMASK(14, 0);
ctdp_level->pkg_min_power = (resp & GENMASK(30, 16)) >> 16;
debug_printf(
"cpu:%d ctdp:%d CONFIG_TDP_GET_PWR_INFO resp:%x pkg_max_power:%d pkg_min_power:%d\n",
cpu, config_index, resp, ctdp_level->pkg_max_power,
ctdp_level->pkg_min_power);
return 0;
}
int isst_get_tjmax_info(int cpu, int config_index,
struct isst_pkg_ctdp_level_info *ctdp_level)
{
unsigned int resp;
int ret;
ret = isst_send_mbox_command(cpu, CONFIG_TDP, CONFIG_TDP_GET_TJMAX_INFO,
0, config_index, &resp);
if (ret)
return ret;
ctdp_level->t_proc_hot = resp & GENMASK(7, 0);
debug_printf(
"cpu:%d ctdp:%d CONFIG_TDP_GET_TJMAX_INFO resp:%x t_proc_hot:%d\n",
cpu, config_index, resp, ctdp_level->t_proc_hot);
return 0;
}
int isst_get_coremask_info(int cpu, int config_index,
struct isst_pkg_ctdp_level_info *ctdp_level)
{
unsigned int resp;
int i, ret;
ctdp_level->cpu_count = 0;
for (i = 0; i < 2; ++i) {
unsigned long long mask;
int cpu_count = 0;
ret = isst_send_mbox_command(cpu, CONFIG_TDP,
CONFIG_TDP_GET_CORE_MASK, 0,
(i << 8) | config_index, &resp);
if (ret)
return ret;
debug_printf(
"cpu:%d ctdp:%d mask:%d CONFIG_TDP_GET_CORE_MASK resp:%x\n",
cpu, config_index, i, resp);
mask = (unsigned long long)resp << (32 * i);
set_cpu_mask_from_punit_coremask(cpu, mask,
ctdp_level->core_cpumask_size,
ctdp_level->core_cpumask,
&cpu_count);
ctdp_level->cpu_count += cpu_count;
debug_printf("cpu:%d ctdp:%d mask:%d cpu count:%d\n", cpu,
config_index, i, ctdp_level->cpu_count);
}
return 0;
}
int isst_get_get_trl(int cpu, int level, int avx_level, int *trl)
{
unsigned int req, resp;
int ret;
req = level | (avx_level << 16);
ret = isst_send_mbox_command(cpu, CONFIG_TDP,
CONFIG_TDP_GET_TURBO_LIMIT_RATIOS, 0, req,
&resp);
if (ret)
return ret;
debug_printf(
"cpu:%d CONFIG_TDP_GET_TURBO_LIMIT_RATIOS req:%x resp:%x\n",
cpu, req, resp);
trl[0] = resp & GENMASK(7, 0);
trl[1] = (resp & GENMASK(15, 8)) >> 8;
trl[2] = (resp & GENMASK(23, 16)) >> 16;
trl[3] = (resp & GENMASK(31, 24)) >> 24;
req = level | BIT(8) | (avx_level << 16);
ret = isst_send_mbox_command(cpu, CONFIG_TDP,
CONFIG_TDP_GET_TURBO_LIMIT_RATIOS, 0, req,
&resp);
if (ret)
return ret;
debug_printf("cpu:%d CONFIG_TDP_GET_TURBO_LIMIT req:%x resp:%x\n", cpu,
req, resp);
trl[4] = resp & GENMASK(7, 0);
trl[5] = (resp & GENMASK(15, 8)) >> 8;
trl[6] = (resp & GENMASK(23, 16)) >> 16;
trl[7] = (resp & GENMASK(31, 24)) >> 24;
return 0;
}
int isst_set_tdp_level_msr(int cpu, int tdp_level)
{
int ret;
debug_printf("cpu: tdp_level via MSR %d\n", cpu, tdp_level);
if (isst_get_config_tdp_lock_status(cpu)) {
debug_printf("cpu: tdp_locked %d\n", cpu);
return -1;
}
if (tdp_level > 2)
return -1; /* invalid value */
ret = isst_send_msr_command(cpu, 0x64b, 1,
(unsigned long long *)&tdp_level);
if (ret)
return ret;
debug_printf("cpu: tdp_level via MSR successful %d\n", cpu, tdp_level);
return 0;
}
int isst_set_tdp_level(int cpu, int tdp_level)
{
unsigned int resp;
int ret;
ret = isst_send_mbox_command(cpu, CONFIG_TDP, CONFIG_TDP_SET_LEVEL, 0,
tdp_level, &resp);
if (ret)
return isst_set_tdp_level_msr(cpu, tdp_level);
return 0;
}
int isst_get_pbf_info(int cpu, int level, struct isst_pbf_info *pbf_info)
{
unsigned int req, resp;
int i, ret;
pbf_info->core_cpumask_size = alloc_cpu_set(&pbf_info->core_cpumask);
for (i = 0; i < 2; ++i) {
unsigned long long mask;
int count;
ret = isst_send_mbox_command(cpu, CONFIG_TDP,
CONFIG_TDP_PBF_GET_CORE_MASK_INFO,
0, (i << 8) | level, &resp);
if (ret)
return ret;
debug_printf(
"cpu:%d CONFIG_TDP_PBF_GET_CORE_MASK_INFO resp:%x\n",
cpu, resp);
mask = (unsigned long long)resp << (32 * i);
set_cpu_mask_from_punit_coremask(cpu, mask,
pbf_info->core_cpumask_size,
pbf_info->core_cpumask,
&count);
}
req = level;
ret = isst_send_mbox_command(cpu, CONFIG_TDP,
CONFIG_TDP_PBF_GET_P1HI_P1LO_INFO, 0, req,
&resp);
if (ret)
return ret;
debug_printf("cpu:%d CONFIG_TDP_PBF_GET_P1HI_P1LO_INFO resp:%x\n", cpu,
resp);
pbf_info->p1_low = resp & 0xff;
pbf_info->p1_high = (resp & GENMASK(15, 8)) >> 8;
req = level;
ret = isst_send_mbox_command(
cpu, CONFIG_TDP, CONFIG_TDP_PBF_GET_TDP_INFO, 0, req, &resp);
if (ret)
return ret;
debug_printf("cpu:%d CONFIG_TDP_PBF_GET_TDP_INFO resp:%x\n", cpu, resp);
pbf_info->tdp = resp & 0xffff;
req = level;
ret = isst_send_mbox_command(
cpu, CONFIG_TDP, CONFIG_TDP_PBF_GET_TJ_MAX_INFO, 0, req, &resp);
if (ret)
return ret;
debug_printf("cpu:%d CONFIG_TDP_PBF_GET_TJ_MAX_INFO resp:%x\n", cpu,
resp);
pbf_info->t_control = (resp >> 8) & 0xff;
pbf_info->t_prochot = resp & 0xff;
return 0;
}
void isst_get_pbf_info_complete(struct isst_pbf_info *pbf_info)
{
free_cpu_set(pbf_info->core_cpumask);
}
int isst_set_pbf_fact_status(int cpu, int pbf, int enable)
{
struct isst_pkg_ctdp pkg_dev;
struct isst_pkg_ctdp_level_info ctdp_level;
int current_level;
unsigned int req = 0, resp;
int ret;
ret = isst_get_ctdp_levels(cpu, &pkg_dev);
if (ret)
return ret;
current_level = pkg_dev.current_level;
ret = isst_get_ctdp_control(cpu, current_level, &ctdp_level);
if (ret)
return ret;
if (pbf) {
if (ctdp_level.fact_enabled)
req = BIT(16);
if (enable)
req |= BIT(17);
else
req &= ~BIT(17);
} else {
if (ctdp_level.pbf_enabled)
req = BIT(17);
if (enable)
req |= BIT(16);
else
req &= ~BIT(16);
}
ret = isst_send_mbox_command(cpu, CONFIG_TDP,
CONFIG_TDP_SET_TDP_CONTROL, 0, req, &resp);
if (ret)
return ret;
debug_printf("cpu:%d CONFIG_TDP_SET_TDP_CONTROL pbf/fact:%d req:%x\n",
cpu, pbf, req);
return 0;
}
int isst_get_fact_bucket_info(int cpu, int level,
struct isst_fact_bucket_info *bucket_info)
{
unsigned int resp;
int i, k, ret;
for (i = 0; i < 2; ++i) {
int j;
ret = isst_send_mbox_command(
cpu, CONFIG_TDP,
CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_NUMCORES, 0,
(i << 8) | level, &resp);
if (ret)
return ret;
debug_printf(
"cpu:%d CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_NUMCORES index:%d level:%d resp:%x\n",
cpu, i, level, resp);
for (j = 0; j < 4; ++j) {
bucket_info[j + (i * 4)].high_priority_cores_count =
(resp >> (j * 8)) & 0xff;
}
}
for (k = 0; k < 3; ++k) {
for (i = 0; i < 2; ++i) {
int j;
ret = isst_send_mbox_command(
cpu, CONFIG_TDP,
CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_RATIOS, 0,
(k << 16) | (i << 8) | level, &resp);
if (ret)
return ret;
debug_printf(
"cpu:%d CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_RATIOS index:%d level:%d avx:%d resp:%x\n",
cpu, i, level, k, resp);
for (j = 0; j < 4; ++j) {
switch (k) {
case 0:
bucket_info[j + (i * 4)].sse_trl =
(resp >> (j * 8)) & 0xff;
break;
case 1:
bucket_info[j + (i * 4)].avx_trl =
(resp >> (j * 8)) & 0xff;
break;
case 2:
bucket_info[j + (i * 4)].avx512_trl =
(resp >> (j * 8)) & 0xff;
break;
default:
break;
}
}
}
}
return 0;
}
int isst_get_fact_info(int cpu, int level, struct isst_fact_info *fact_info)
{
unsigned int resp;
int ret;
ret = isst_send_mbox_command(cpu, CONFIG_TDP,
CONFIG_TDP_GET_FACT_LP_CLIPPING_RATIO, 0,
level, &resp);
if (ret)
return ret;
debug_printf("cpu:%d CONFIG_TDP_GET_FACT_LP_CLIPPING_RATIO resp:%x\n",
cpu, resp);
fact_info->lp_clipping_ratio_license_sse = resp & 0xff;
fact_info->lp_clipping_ratio_license_avx2 = (resp >> 8) & 0xff;
fact_info->lp_clipping_ratio_license_avx512 = (resp >> 16) & 0xff;
ret = isst_get_fact_bucket_info(cpu, level, fact_info->bucket_info);
return ret;
}
int isst_set_trl(int cpu, unsigned long long trl)
{
int ret;
if (!trl)
trl = 0xFFFFFFFFFFFFFFFFULL;
ret = isst_send_msr_command(cpu, 0x1AD, 1, &trl);
if (ret)
return ret;
return 0;
}
int isst_set_trl_from_current_tdp(int cpu, unsigned long long trl)
{
unsigned long long msr_trl;
int ret;
if (trl) {
msr_trl = trl;
} else {
struct isst_pkg_ctdp pkg_dev;
int trl[8];
int i;
ret = isst_get_ctdp_levels(cpu, &pkg_dev);
if (ret)
return ret;
ret = isst_get_get_trl(cpu, pkg_dev.current_level, 0, trl);
if (ret)
return ret;
msr_trl = 0;
for (i = 0; i < 8; ++i) {
unsigned long long _trl = trl[i];
msr_trl |= (_trl << (i * 8));
}
}
ret = isst_send_msr_command(cpu, 0x1AD, 1, &msr_trl);
if (ret)
return ret;
return 0;
}
/* Return 1 if locked */
int isst_get_config_tdp_lock_status(int cpu)
{
unsigned long long tdp_control = 0;
int ret;
ret = isst_send_msr_command(cpu, 0x64b, 0, &tdp_control);
if (ret)
return ret;
ret = !!(tdp_control & BIT(31));
return ret;
}
void isst_get_process_ctdp_complete(int cpu, struct isst_pkg_ctdp *pkg_dev)
{
int i;
if (!pkg_dev->processed)
return;
for (i = 0; i < pkg_dev->levels; ++i) {
struct isst_pkg_ctdp_level_info *ctdp_level;
ctdp_level = &pkg_dev->ctdp_level[i];
if (ctdp_level->pbf_support)
free_cpu_set(ctdp_level->pbf_info.core_cpumask);
free_cpu_set(ctdp_level->core_cpumask);
}
}
int isst_get_process_ctdp(int cpu, int tdp_level, struct isst_pkg_ctdp *pkg_dev)
{
int i, ret;
if (pkg_dev->processed)
return 0;
ret = isst_get_ctdp_levels(cpu, pkg_dev);
if (ret)
return ret;
debug_printf("cpu: %d ctdp enable:%d current level: %d levels:%d\n",
cpu, pkg_dev->enabled, pkg_dev->current_level,
pkg_dev->levels);
for (i = 0; i <= pkg_dev->levels; ++i) {
struct isst_pkg_ctdp_level_info *ctdp_level;
if (tdp_level != 0xff && i != tdp_level)
continue;
debug_printf("cpu:%d Get Information for TDP level:%d\n", cpu,
i);
ctdp_level = &pkg_dev->ctdp_level[i];
ctdp_level->processed = 1;
ctdp_level->level = i;
ctdp_level->control_cpu = cpu;
ctdp_level->pkg_id = get_physical_package_id(cpu);
ctdp_level->die_id = get_physical_die_id(cpu);
ret = isst_get_ctdp_control(cpu, i, ctdp_level);
if (ret)
return ret;
ret = isst_get_tdp_info(cpu, i, ctdp_level);
if (ret)
return ret;
ret = isst_get_pwr_info(cpu, i, ctdp_level);
if (ret)
return ret;
ret = isst_get_tjmax_info(cpu, i, ctdp_level);
if (ret)
return ret;
ctdp_level->core_cpumask_size =
alloc_cpu_set(&ctdp_level->core_cpumask);
ret = isst_get_coremask_info(cpu, i, ctdp_level);
if (ret)
return ret;
ret = isst_get_get_trl(cpu, i, 0,
ctdp_level->trl_sse_active_cores);
if (ret)
return ret;
ret = isst_get_get_trl(cpu, i, 1,
ctdp_level->trl_avx_active_cores);
if (ret)
return ret;
ret = isst_get_get_trl(cpu, i, 2,
ctdp_level->trl_avx_512_active_cores);
if (ret)
return ret;
if (ctdp_level->pbf_support) {
ret = isst_get_pbf_info(cpu, i, &ctdp_level->pbf_info);
if (!ret)
ctdp_level->pbf_found = 1;
}
if (ctdp_level->fact_support) {
ret = isst_get_fact_info(cpu, i,
&ctdp_level->fact_info);
if (ret)
return ret;
}
}
pkg_dev->processed = 1;
return 0;
}
int isst_pm_qos_config(int cpu, int enable_clos, int priority_type)
{
unsigned int req, resp;
int ret;
ret = isst_send_mbox_command(cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, 0, 0,
&resp);
if (ret)
return ret;
debug_printf("cpu:%d CLOS_PM_QOS_CONFIG resp:%x\n", cpu, resp);
req = resp;
if (enable_clos)
req = req | BIT(1);
else
req = req & ~BIT(1);
if (priority_type)
req = req | BIT(2);
else
req = req & ~BIT(2);
ret = isst_send_mbox_command(cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG,
BIT(MBOX_CMD_WRITE_BIT), req, &resp);
if (ret)
return ret;
debug_printf("cpu:%d CLOS_PM_QOS_CONFIG priority type:%d req:%x\n", cpu,
priority_type, req);
return 0;
}
int isst_pm_get_clos(int cpu, int clos, struct isst_clos_config *clos_config)
{
unsigned int resp;
int ret;
ret = isst_send_mbox_command(cpu, CONFIG_CLOS, CLOS_PM_CLOS, clos, 0,
&resp);
if (ret)
return ret;
clos_config->pkg_id = get_physical_package_id(cpu);
clos_config->die_id = get_physical_die_id(cpu);
clos_config->epp = resp & 0x0f;
clos_config->clos_prop_prio = (resp >> 4) & 0x0f;
clos_config->clos_min = (resp >> 8) & 0xff;
clos_config->clos_max = (resp >> 16) & 0xff;
clos_config->clos_desired = (resp >> 24) & 0xff;
return 0;
}
int isst_set_clos(int cpu, int clos, struct isst_clos_config *clos_config)
{
unsigned int req, resp;
unsigned int param;
int ret;
req = clos_config->epp & 0x0f;
req |= (clos_config->clos_prop_prio & 0x0f) << 4;
req |= (clos_config->clos_min & 0xff) << 8;
req |= (clos_config->clos_max & 0xff) << 16;
req |= (clos_config->clos_desired & 0xff) << 24;
param = BIT(MBOX_CMD_WRITE_BIT) | clos;
ret = isst_send_mbox_command(cpu, CONFIG_CLOS, CLOS_PM_CLOS, param, req,
&resp);
if (ret)
return ret;
debug_printf("cpu:%d CLOS_PM_CLOS param:%x req:%x\n", cpu, param, req);
return 0;
}
int isst_clos_get_assoc_status(int cpu, int *clos_id)
{
unsigned int resp;
unsigned int param;
int core_id, ret;
core_id = find_phy_core_num(cpu);
param = core_id;
ret = isst_send_mbox_command(cpu, CONFIG_CLOS, CLOS_PQR_ASSOC, param, 0,
&resp);
if (ret)
return ret;
debug_printf("cpu:%d CLOS_PQR_ASSOC param:%x resp:%x\n", cpu, param,
resp);
*clos_id = (resp >> 16) & 0x03;
return 0;
}
int isst_clos_associate(int cpu, int clos_id)
{
unsigned int req, resp;
unsigned int param;
int core_id, ret;
req = (clos_id & 0x03) << 16;
core_id = find_phy_core_num(cpu);
param = BIT(MBOX_CMD_WRITE_BIT) | core_id;
ret = isst_send_mbox_command(cpu, CONFIG_CLOS, CLOS_PQR_ASSOC, param,
req, &resp);
if (ret)
return ret;
debug_printf("cpu:%d CLOS_PQR_ASSOC param:%x req:%x\n", cpu, param,
req);
return 0;
}

View File

@ -0,0 +1,479 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Intel dynamic_speed_select -- Enumerate and control features
* Copyright (c) 2019 Intel Corporation.
*/
#include "isst.h"
#define DISP_FREQ_MULTIPLIER 100000
static void printcpumask(int str_len, char *str, int mask_size,
cpu_set_t *cpu_mask)
{
int i, max_cpus = get_topo_max_cpus();
unsigned int *mask;
int size, index, curr_index;
size = max_cpus / (sizeof(unsigned int) * 8);
if (max_cpus % (sizeof(unsigned int) * 8))
size++;
mask = calloc(size, sizeof(unsigned int));
if (!mask)
return;
for (i = 0; i < max_cpus; ++i) {
int mask_index, bit_index;
if (!CPU_ISSET_S(i, mask_size, cpu_mask))
continue;
mask_index = i / (sizeof(unsigned int) * 8);
bit_index = i % (sizeof(unsigned int) * 8);
mask[mask_index] |= BIT(bit_index);
}
curr_index = 0;
for (i = size - 1; i >= 0; --i) {
index = snprintf(&str[curr_index], str_len - curr_index, "%08x",
mask[i]);
curr_index += index;
if (i) {
strncat(&str[curr_index], ",", str_len - curr_index);
curr_index++;
}
}
free(mask);
}
static void format_and_print_txt(FILE *outf, int level, char *header,
char *value)
{
char *spaces = " ";
static char delimiters[256];
int i, j = 0;
if (!level)
return;
if (level == 1) {
strcpy(delimiters, " ");
} else {
for (i = 0; i < level - 1; ++i)
j += snprintf(&delimiters[j], sizeof(delimiters) - j,
"%s", spaces);
}
if (header && value) {
fprintf(outf, "%s", delimiters);
fprintf(outf, "%s:%s\n", header, value);
} else if (header) {
fprintf(outf, "%s", delimiters);
fprintf(outf, "%s\n", header);
}
}
static int last_level;
static void format_and_print(FILE *outf, int level, char *header, char *value)
{
char *spaces = " ";
static char delimiters[256];
int i;
if (!out_format_is_json()) {
format_and_print_txt(outf, level, header, value);
return;
}
if (level == 0) {
if (header)
fprintf(outf, "{");
else
fprintf(outf, "\n}\n");
} else {
int j = 0;
for (i = 0; i < level; ++i)
j += snprintf(&delimiters[j], sizeof(delimiters) - j,
"%s", spaces);
if (last_level == level)
fprintf(outf, ",\n");
if (value) {
if (last_level != level)
fprintf(outf, "\n");
fprintf(outf, "%s\"%s\": ", delimiters, header);
fprintf(outf, "\"%s\"", value);
} else {
for (i = last_level - 1; i >= level; --i) {
int k = 0;
for (j = i; j > 0; --j)
k += snprintf(&delimiters[k],
sizeof(delimiters) - k,
"%s", spaces);
if (i == level && header)
fprintf(outf, "\n%s},", delimiters);
else
fprintf(outf, "\n%s}", delimiters);
}
if (abs(last_level - level) < 3)
fprintf(outf, "\n");
if (header)
fprintf(outf, "%s\"%s\": {", delimiters,
header);
}
}
last_level = level;
}
static void print_packag_info(int cpu, FILE *outf)
{
char header[256];
snprintf(header, sizeof(header), "package-%d",
get_physical_package_id(cpu));
format_and_print(outf, 1, header, NULL);
snprintf(header, sizeof(header), "die-%d", get_physical_die_id(cpu));
format_and_print(outf, 2, header, NULL);
snprintf(header, sizeof(header), "cpu-%d", cpu);
format_and_print(outf, 3, header, NULL);
}
static void _isst_pbf_display_information(int cpu, FILE *outf, int level,
struct isst_pbf_info *pbf_info,
int disp_level)
{
char header[256];
char value[256];
snprintf(header, sizeof(header), "speed-select-base-freq");
format_and_print(outf, disp_level, header, NULL);
snprintf(header, sizeof(header), "high-priority-base-frequency(KHz)");
snprintf(value, sizeof(value), "%d",
pbf_info->p1_high * DISP_FREQ_MULTIPLIER);
format_and_print(outf, disp_level + 1, header, value);
snprintf(header, sizeof(header), "high-priority-cpu-mask");
printcpumask(sizeof(value), value, pbf_info->core_cpumask_size,
pbf_info->core_cpumask);
format_and_print(outf, disp_level + 1, header, value);
snprintf(header, sizeof(header), "low-priority-base-frequency(KHz)");
snprintf(value, sizeof(value), "%d",
pbf_info->p1_low * DISP_FREQ_MULTIPLIER);
format_and_print(outf, disp_level + 1, header, value);
snprintf(header, sizeof(header), "tjunction-temperature(C)");
snprintf(value, sizeof(value), "%d", pbf_info->t_prochot);
format_and_print(outf, disp_level + 1, header, value);
snprintf(header, sizeof(header), "thermal-design-power(W)");
snprintf(value, sizeof(value), "%d", pbf_info->tdp);
format_and_print(outf, disp_level + 1, header, value);
}
static void _isst_fact_display_information(int cpu, FILE *outf, int level,
int fact_bucket, int fact_avx,
struct isst_fact_info *fact_info,
int base_level)
{
struct isst_fact_bucket_info *bucket_info = fact_info->bucket_info;
char header[256];
char value[256];
int j;
snprintf(header, sizeof(header), "speed-select-turbo-freq");
format_and_print(outf, base_level, header, NULL);
for (j = 0; j < ISST_FACT_MAX_BUCKETS; ++j) {
if (fact_bucket != 0xff && fact_bucket != j)
continue;
if (!bucket_info[j].high_priority_cores_count)
break;
snprintf(header, sizeof(header), "bucket-%d", j);
format_and_print(outf, base_level + 1, header, NULL);
snprintf(header, sizeof(header), "high-priority-cores-count");
snprintf(value, sizeof(value), "%d",
bucket_info[j].high_priority_cores_count);
format_and_print(outf, base_level + 2, header, value);
if (fact_avx & 0x01) {
snprintf(header, sizeof(header),
"high-priority-max-frequency(KHz)");
snprintf(value, sizeof(value), "%d",
bucket_info[j].sse_trl * DISP_FREQ_MULTIPLIER);
format_and_print(outf, base_level + 2, header, value);
}
if (fact_avx & 0x02) {
snprintf(header, sizeof(header),
"high-priority-max-avx2-frequency(KHz)");
snprintf(value, sizeof(value), "%d",
bucket_info[j].avx_trl * DISP_FREQ_MULTIPLIER);
format_and_print(outf, base_level + 2, header, value);
}
if (fact_avx & 0x04) {
snprintf(header, sizeof(header),
"high-priority-max-avx512-frequency(KHz)");
snprintf(value, sizeof(value), "%d",
bucket_info[j].avx512_trl *
DISP_FREQ_MULTIPLIER);
format_and_print(outf, base_level + 2, header, value);
}
}
snprintf(header, sizeof(header),
"speed-select-turbo-freq-clip-frequencies");
format_and_print(outf, base_level + 1, header, NULL);
snprintf(header, sizeof(header), "low-priority-max-frequency(KHz)");
snprintf(value, sizeof(value), "%d",
fact_info->lp_clipping_ratio_license_sse *
DISP_FREQ_MULTIPLIER);
format_and_print(outf, base_level + 2, header, value);
snprintf(header, sizeof(header),
"low-priority-max-avx2-frequency(KHz)");
snprintf(value, sizeof(value), "%d",
fact_info->lp_clipping_ratio_license_avx2 *
DISP_FREQ_MULTIPLIER);
format_and_print(outf, base_level + 2, header, value);
snprintf(header, sizeof(header),
"low-priority-max-avx512-frequency(KHz)");
snprintf(value, sizeof(value), "%d",
fact_info->lp_clipping_ratio_license_avx512 *
DISP_FREQ_MULTIPLIER);
format_and_print(outf, base_level + 2, header, value);
}
void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level,
struct isst_pkg_ctdp *pkg_dev)
{
char header[256];
char value[256];
int i, base_level = 1;
print_packag_info(cpu, outf);
for (i = 0; i <= pkg_dev->levels; ++i) {
struct isst_pkg_ctdp_level_info *ctdp_level;
int j;
ctdp_level = &pkg_dev->ctdp_level[i];
if (!ctdp_level->processed)
continue;
snprintf(header, sizeof(header), "perf-profile-level-%d",
ctdp_level->level);
format_and_print(outf, base_level + 3, header, NULL);
snprintf(header, sizeof(header), "cpu-count");
j = get_cpu_count(get_physical_die_id(cpu),
get_physical_die_id(cpu));
snprintf(value, sizeof(value), "%d", j);
format_and_print(outf, base_level + 4, header, value);
snprintf(header, sizeof(header), "enable-cpu-mask");
printcpumask(sizeof(value), value,
ctdp_level->core_cpumask_size,
ctdp_level->core_cpumask);
format_and_print(outf, base_level + 4, header, value);
snprintf(header, sizeof(header), "thermal-design-power-ratio");
snprintf(value, sizeof(value), "%d", ctdp_level->tdp_ratio);
format_and_print(outf, base_level + 4, header, value);
snprintf(header, sizeof(header), "base-frequency(KHz)");
snprintf(value, sizeof(value), "%d",
ctdp_level->tdp_ratio * DISP_FREQ_MULTIPLIER);
format_and_print(outf, base_level + 4, header, value);
snprintf(header, sizeof(header),
"speed-select-turbo-freq-support");
snprintf(value, sizeof(value), "%d", ctdp_level->fact_support);
format_and_print(outf, base_level + 4, header, value);
snprintf(header, sizeof(header),
"speed-select-base-freq-support");
snprintf(value, sizeof(value), "%d", ctdp_level->pbf_support);
format_and_print(outf, base_level + 4, header, value);
snprintf(header, sizeof(header),
"speed-select-base-freq-enabled");
snprintf(value, sizeof(value), "%d", ctdp_level->pbf_enabled);
format_and_print(outf, base_level + 4, header, value);
snprintf(header, sizeof(header),
"speed-select-turbo-freq-enabled");
snprintf(value, sizeof(value), "%d", ctdp_level->fact_enabled);
format_and_print(outf, base_level + 4, header, value);
snprintf(header, sizeof(header), "thermal-design-power(W)");
snprintf(value, sizeof(value), "%d", ctdp_level->pkg_tdp);
format_and_print(outf, base_level + 4, header, value);
snprintf(header, sizeof(header), "tjunction-max(C)");
snprintf(value, sizeof(value), "%d", ctdp_level->t_proc_hot);
format_and_print(outf, base_level + 4, header, value);
snprintf(header, sizeof(header), "turbo-ratio-limits-sse");
format_and_print(outf, base_level + 4, header, NULL);
for (j = 0; j < 8; ++j) {
snprintf(header, sizeof(header), "bucket-%d", j);
format_and_print(outf, base_level + 5, header, NULL);
snprintf(header, sizeof(header), "core-count");
snprintf(value, sizeof(value), "%d", j);
format_and_print(outf, base_level + 6, header, value);
snprintf(header, sizeof(header), "turbo-ratio");
snprintf(value, sizeof(value), "%d",
ctdp_level->trl_sse_active_cores[j]);
format_and_print(outf, base_level + 6, header, value);
}
snprintf(header, sizeof(header), "turbo-ratio-limits-avx");
format_and_print(outf, base_level + 4, header, NULL);
for (j = 0; j < 8; ++j) {
snprintf(header, sizeof(header), "bucket-%d", j);
format_and_print(outf, base_level + 5, header, NULL);
snprintf(header, sizeof(header), "core-count");
snprintf(value, sizeof(value), "%d", j);
format_and_print(outf, base_level + 6, header, value);
snprintf(header, sizeof(header), "turbo-ratio");
snprintf(value, sizeof(value), "%d",
ctdp_level->trl_avx_active_cores[j]);
format_and_print(outf, base_level + 6, header, value);
}
snprintf(header, sizeof(header), "turbo-ratio-limits-avx512");
format_and_print(outf, base_level + 4, header, NULL);
for (j = 0; j < 8; ++j) {
snprintf(header, sizeof(header), "bucket-%d", j);
format_and_print(outf, base_level + 5, header, NULL);
snprintf(header, sizeof(header), "core-count");
snprintf(value, sizeof(value), "%d", j);
format_and_print(outf, base_level + 6, header, value);
snprintf(header, sizeof(header), "turbo-ratio");
snprintf(value, sizeof(value), "%d",
ctdp_level->trl_avx_512_active_cores[j]);
format_and_print(outf, base_level + 6, header, value);
}
if (ctdp_level->pbf_support)
_isst_pbf_display_information(cpu, outf, i,
&ctdp_level->pbf_info,
base_level + 4);
if (ctdp_level->fact_support)
_isst_fact_display_information(cpu, outf, i, 0xff, 0xff,
&ctdp_level->fact_info,
base_level + 4);
}
format_and_print(outf, 1, NULL, NULL);
}
void isst_ctdp_display_information_start(FILE *outf)
{
last_level = 0;
format_and_print(outf, 0, "start", NULL);
}
void isst_ctdp_display_information_end(FILE *outf)
{
format_and_print(outf, 0, NULL, NULL);
}
void isst_pbf_display_information(int cpu, FILE *outf, int level,
struct isst_pbf_info *pbf_info)
{
print_packag_info(cpu, outf);
_isst_pbf_display_information(cpu, outf, level, pbf_info, 4);
format_and_print(outf, 1, NULL, NULL);
}
void isst_fact_display_information(int cpu, FILE *outf, int level,
int fact_bucket, int fact_avx,
struct isst_fact_info *fact_info)
{
print_packag_info(cpu, outf);
_isst_fact_display_information(cpu, outf, level, fact_bucket, fact_avx,
fact_info, 4);
format_and_print(outf, 1, NULL, NULL);
}
void isst_clos_display_information(int cpu, FILE *outf, int clos,
struct isst_clos_config *clos_config)
{
char header[256];
char value[256];
snprintf(header, sizeof(header), "package-%d",
get_physical_package_id(cpu));
format_and_print(outf, 1, header, NULL);
snprintf(header, sizeof(header), "die-%d", get_physical_die_id(cpu));
format_and_print(outf, 2, header, NULL);
snprintf(header, sizeof(header), "cpu-%d", cpu);
format_and_print(outf, 3, header, NULL);
snprintf(header, sizeof(header), "core-power");
format_and_print(outf, 4, header, NULL);
snprintf(header, sizeof(header), "clos");
snprintf(value, sizeof(value), "%d", clos);
format_and_print(outf, 5, header, value);
snprintf(header, sizeof(header), "epp");
snprintf(value, sizeof(value), "%d", clos_config->epp);
format_and_print(outf, 5, header, value);
snprintf(header, sizeof(header), "clos-proportional-priority");
snprintf(value, sizeof(value), "%d", clos_config->clos_prop_prio);
format_and_print(outf, 5, header, value);
snprintf(header, sizeof(header), "clos-min");
snprintf(value, sizeof(value), "%d", clos_config->clos_min);
format_and_print(outf, 5, header, value);
snprintf(header, sizeof(header), "clos-max");
snprintf(value, sizeof(value), "%d", clos_config->clos_max);
format_and_print(outf, 5, header, value);
snprintf(header, sizeof(header), "clos-desired");
snprintf(value, sizeof(value), "%d", clos_config->clos_desired);
format_and_print(outf, 5, header, value);
format_and_print(outf, 1, NULL, NULL);
}
void isst_display_result(int cpu, FILE *outf, char *feature, char *cmd,
int result)
{
char header[256];
char value[256];
snprintf(header, sizeof(header), "package-%d",
get_physical_package_id(cpu));
format_and_print(outf, 1, header, NULL);
snprintf(header, sizeof(header), "die-%d", get_physical_die_id(cpu));
format_and_print(outf, 2, header, NULL);
snprintf(header, sizeof(header), "cpu-%d", cpu);
format_and_print(outf, 3, header, NULL);
snprintf(header, sizeof(header), "%s", feature);
format_and_print(outf, 4, header, NULL);
snprintf(header, sizeof(header), "%s", cmd);
snprintf(value, sizeof(value), "%d", result);
format_and_print(outf, 5, header, value);
format_and_print(outf, 1, NULL, NULL);
}

View File

@ -0,0 +1,231 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Intel Speed Select -- Enumerate and control features
* Copyright (c) 2019 Intel Corporation.
*/
#ifndef _ISST_H_
#define _ISST_H_
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sched.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <getopt.h>
#include <err.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <cpuid.h>
#include <dirent.h>
#include <errno.h>
#include <stdarg.h>
#include <sys/ioctl.h>
#define BIT(x) (1 << (x))
#define GENMASK(h, l) (((~0UL) << (l)) & (~0UL >> (sizeof(long) * 8 - 1 - (h))))
#define GENMASK_ULL(h, l) \
(((~0ULL) << (l)) & (~0ULL >> (sizeof(long long) * 8 - 1 - (h))))
#define CONFIG_TDP 0x7f
#define CONFIG_TDP_GET_LEVELS_INFO 0x00
#define CONFIG_TDP_GET_TDP_CONTROL 0x01
#define CONFIG_TDP_SET_TDP_CONTROL 0x02
#define CONFIG_TDP_GET_TDP_INFO 0x03
#define CONFIG_TDP_GET_PWR_INFO 0x04
#define CONFIG_TDP_GET_TJMAX_INFO 0x05
#define CONFIG_TDP_GET_CORE_MASK 0x06
#define CONFIG_TDP_GET_TURBO_LIMIT_RATIOS 0x07
#define CONFIG_TDP_SET_LEVEL 0x08
#define CONFIG_TDP_GET_UNCORE_P0_P1_INFO 0X09
#define CONFIG_TDP_GET_P1_INFO 0x0a
#define CONFIG_TDP_GET_MEM_FREQ 0x0b
#define CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_NUMCORES 0x10
#define CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_RATIOS 0x11
#define CONFIG_TDP_GET_FACT_LP_CLIPPING_RATIO 0x12
#define CONFIG_TDP_PBF_GET_CORE_MASK_INFO 0x20
#define CONFIG_TDP_PBF_GET_P1HI_P1LO_INFO 0x21
#define CONFIG_TDP_PBF_GET_TJ_MAX_INFO 0x22
#define CONFIG_TDP_PBF_GET_TDP_INFO 0X23
#define CONFIG_CLOS 0xd0
#define CLOS_PQR_ASSOC 0x00
#define CLOS_PM_CLOS 0x01
#define CLOS_PM_QOS_CONFIG 0x02
#define CLOS_STATUS 0x03
#define MBOX_CMD_WRITE_BIT 0x08
#define PM_QOS_INFO_OFFSET 0x00
#define PM_QOS_CONFIG_OFFSET 0x04
#define PM_CLOS_OFFSET 0x08
#define PQR_ASSOC_OFFSET 0x20
struct isst_clos_config {
int pkg_id;
int die_id;
unsigned char epp;
unsigned char clos_prop_prio;
unsigned char clos_min;
unsigned char clos_max;
unsigned char clos_desired;
};
struct isst_fact_bucket_info {
int high_priority_cores_count;
int sse_trl;
int avx_trl;
int avx512_trl;
};
struct isst_pbf_info {
int pbf_acticated;
int pbf_available;
size_t core_cpumask_size;
cpu_set_t *core_cpumask;
int p1_high;
int p1_low;
int t_control;
int t_prochot;
int tdp;
};
#define ISST_TRL_MAX_ACTIVE_CORES 8
#define ISST_FACT_MAX_BUCKETS 8
struct isst_fact_info {
int lp_clipping_ratio_license_sse;
int lp_clipping_ratio_license_avx2;
int lp_clipping_ratio_license_avx512;
struct isst_fact_bucket_info bucket_info[ISST_FACT_MAX_BUCKETS];
};
struct isst_pkg_ctdp_level_info {
int processed;
int control_cpu;
int pkg_id;
int die_id;
int level;
int fact_support;
int pbf_support;
int fact_enabled;
int pbf_enabled;
int tdp_ratio;
int active;
int tdp_control;
int pkg_tdp;
int pkg_min_power;
int pkg_max_power;
int fact;
int t_proc_hot;
int uncore_p0;
int uncore_p1;
int sse_p1;
int avx2_p1;
int avx512_p1;
int mem_freq;
size_t core_cpumask_size;
cpu_set_t *core_cpumask;
int cpu_count;
int trl_sse_active_cores[ISST_TRL_MAX_ACTIVE_CORES];
int trl_avx_active_cores[ISST_TRL_MAX_ACTIVE_CORES];
int trl_avx_512_active_cores[ISST_TRL_MAX_ACTIVE_CORES];
int kobj_bucket_index;
int active_bucket;
int fact_max_index;
int fact_max_config;
int pbf_found;
int pbf_active;
struct isst_pbf_info pbf_info;
struct isst_fact_info fact_info;
};
#define ISST_MAX_TDP_LEVELS (4 + 1) /* +1 for base config */
struct isst_pkg_ctdp {
int locked;
int version;
int processed;
int levels;
int current_level;
int enabled;
struct isst_pkg_ctdp_level_info ctdp_level[ISST_MAX_TDP_LEVELS];
};
extern int get_topo_max_cpus(void);
extern int get_cpu_count(int pkg_id, int die_id);
/* Common interfaces */
extern void debug_printf(const char *format, ...);
extern int out_format_is_json(void);
extern int get_physical_package_id(int cpu);
extern int get_physical_die_id(int cpu);
extern size_t alloc_cpu_set(cpu_set_t **cpu_set);
extern void free_cpu_set(cpu_set_t *cpu_set);
extern int find_logical_cpu(int pkg_id, int die_id, int phy_cpu);
extern int find_phy_cpu_num(int logical_cpu);
extern int find_phy_core_num(int logical_cpu);
extern void set_cpu_mask_from_punit_coremask(int cpu,
unsigned long long core_mask,
size_t core_cpumask_size,
cpu_set_t *core_cpumask,
int *cpu_cnt);
extern int isst_send_mbox_command(unsigned int cpu, unsigned char command,
unsigned char sub_command,
unsigned int write,
unsigned int req_data, unsigned int *resp);
extern int isst_send_msr_command(unsigned int cpu, unsigned int command,
int write, unsigned long long *req_resp);
extern int isst_get_ctdp_levels(int cpu, struct isst_pkg_ctdp *pkg_dev);
extern int isst_get_process_ctdp(int cpu, int tdp_level,
struct isst_pkg_ctdp *pkg_dev);
extern void isst_get_process_ctdp_complete(int cpu,
struct isst_pkg_ctdp *pkg_dev);
extern void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level,
struct isst_pkg_ctdp *pkg_dev);
extern void isst_ctdp_display_information_start(FILE *outf);
extern void isst_ctdp_display_information_end(FILE *outf);
extern void isst_pbf_display_information(int cpu, FILE *outf, int level,
struct isst_pbf_info *info);
extern int isst_set_tdp_level(int cpu, int tdp_level);
extern int isst_set_tdp_level_msr(int cpu, int tdp_level);
extern int isst_set_pbf_fact_status(int cpu, int pbf, int enable);
extern int isst_get_pbf_info(int cpu, int level,
struct isst_pbf_info *pbf_info);
extern void isst_get_pbf_info_complete(struct isst_pbf_info *pbf_info);
extern int isst_get_fact_info(int cpu, int level,
struct isst_fact_info *fact_info);
extern int isst_get_fact_bucket_info(int cpu, int level,
struct isst_fact_bucket_info *bucket_info);
extern void isst_fact_display_information(int cpu, FILE *outf, int level,
int fact_bucket, int fact_avx,
struct isst_fact_info *fact_info);
extern int isst_set_trl(int cpu, unsigned long long trl);
extern int isst_set_trl_from_current_tdp(int cpu, unsigned long long trl);
extern int isst_get_config_tdp_lock_status(int cpu);
extern int isst_pm_qos_config(int cpu, int enable_clos, int priority_type);
extern int isst_pm_get_clos(int cpu, int clos,
struct isst_clos_config *clos_config);
extern int isst_set_clos(int cpu, int clos,
struct isst_clos_config *clos_config);
extern int isst_clos_associate(int cpu, int clos);
extern int isst_clos_get_assoc_status(int cpu, int *clos_id);
extern void isst_clos_display_information(int cpu, FILE *outf, int clos,
struct isst_clos_config *clos_config);
extern int isst_read_reg(unsigned short reg, unsigned int *val);
extern int isst_write_reg(int reg, unsigned int val);
extern void isst_display_result(int cpu, FILE *outf, char *feature, char *cmd,
int result);
#endif