mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-16 17:23:55 +08:00
Immutable branch between MFD, X86, USB and Watchdog due for the v5.7 merge window
-----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEdrbJNaO+IJqU8IdIUa+KL4f8d2EFAl6iviIACgkQUa+KL4f8 d2EdiA/+OSpr4fiy9RTIxXnyROoFmI9ypKyDbXdkW0c2E2ZlH6d3LCFRCiF39DeQ 7U6uNXyNetUjzH6fXm1BM/nltNbAaAUmNlL1THGQ3wamMayL4S0hSHgy8ZUTcFj4 yC4TLwHAsXmNjHC0FbUul9LILooBs6MfEHeN+zK7tCI4ZLaygx5d/+ApFlbuU45c J4u7a32PEmM9i6BvSAQK2OGWChIRGvCnxekz9C8ebWULzzsOKPLFJccKEdORc8dK 5Mg3J8zC1IZ+ct6y+DvvbaYROP3vbNabJPHIjV/hLmP0g3qHghHUW265qLVrHu/z uCek8lgLTYetJ+Nn4/k7eoUDKkZgbgfukRLyHw/gu4YQaiwhjI2KVP+ytBWkCzA4 AOJVnMN756C1Rx3XrF/E63RdwuIfJA+VGGW7YzbPuXa0SvECPE//wVpZv6FSkJmA N4s1Z7yhuZEjGvuu4l5/ErZSbSN/2OMM4ahdQ4xoCAxYKkKMacS5Gds83VlnM6LQ Mwl77aTc84JqI1y4HROd2qj4J8YfE1F8lTRt8lepgeOL/kntM9U1lvBT3TCV3T0e xLC6GDCHo6N9vlyn1KU4PCyCHFNUJLD6Wq+H/jLZkcrx4k59caR0+I1+iyaQyo/i o+DgYCp6GnPFd3XuVLppg45n6qZ7wB67WOJ6iOpcO7+kMldg4Ck= =4M2c -----END PGP SIGNATURE----- Merge branch 'ib-mfd-x86-usb-watchdog-v5.7' Merge branch 'ib-mfd-x86-usb-watchdog-v5.7' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git to avoid conflicts in PDx86. Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
This commit is contained in:
commit
4a65ed6562
22
Documentation/ABI/obsolete/sysfs-driver-intel_pmc_bxt
Normal file
22
Documentation/ABI/obsolete/sysfs-driver-intel_pmc_bxt
Normal file
@ -0,0 +1,22 @@
|
||||
These files allow sending arbitrary IPC commands to the PMC/SCU which
|
||||
may be dangerous. These will be removed eventually and should not be
|
||||
used in any new applications.
|
||||
|
||||
What: /sys/bus/platform/devices/INT34D2:00/simplecmd
|
||||
Date: Jun 2015
|
||||
KernelVersion: 4.1
|
||||
Contact: Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||
Description: This interface allows userspace to send an arbitrary
|
||||
IPC command to the PMC/SCU.
|
||||
|
||||
Format: %d %d where first number is command and
|
||||
second number is subcommand.
|
||||
|
||||
What: /sys/bus/platform/devices/INT34D2:00/northpeak
|
||||
Date: Jun 2015
|
||||
KernelVersion: 4.1
|
||||
Contact: Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||
Description: This interface allows userspace to enable and disable
|
||||
Northpeak through the PMC/SCU.
|
||||
|
||||
Format: %u.
|
23
MAINTAINERS
23
MAINTAINERS
@ -8499,6 +8499,13 @@ L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/intel_atomisp2_pm.c
|
||||
|
||||
INTEL BROXTON PMC DRIVER
|
||||
M: Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||
M: Zha Qipeng <qipeng.zha@intel.com>
|
||||
S: Maintained
|
||||
F: drivers/mfd/intel_pmc_bxt.c
|
||||
F: include/linux/mfd/intel_pmc_bxt.h
|
||||
|
||||
INTEL C600 SERIES SAS CONTROLLER DRIVER
|
||||
M: Intel SCU Linux support <intel-linux-scu@intel.com>
|
||||
M: Artur Paszkiewicz <artur.paszkiewicz@intel.com>
|
||||
@ -8706,6 +8713,13 @@ F: include/uapi/linux/mic_common.h
|
||||
F: include/uapi/linux/mic_ioctl.h
|
||||
F: include/uapi/linux/scif_ioctl.h
|
||||
|
||||
INTEL P-Unit IPC DRIVER
|
||||
M: Zha Qipeng <qipeng.zha@intel.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: arch/x86/include/asm/intel_punit_ipc.h
|
||||
F: drivers/platform/x86/intel_punit_ipc.c
|
||||
|
||||
INTEL PMC CORE DRIVER
|
||||
M: Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com>
|
||||
M: Vishwanath Somayaji <vishwanath.somayaji@intel.com>
|
||||
@ -8713,15 +8727,6 @@ L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/intel_pmc_core*
|
||||
|
||||
INTEL PMC/P-Unit IPC DRIVER
|
||||
M: Zha Qipeng<qipeng.zha@intel.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: arch/x86/include/asm/intel_pmc_ipc.h
|
||||
F: arch/x86/include/asm/intel_punit_ipc.h
|
||||
F: drivers/platform/x86/intel_pmc_ipc.c
|
||||
F: drivers/platform/x86/intel_punit_ipc.c
|
||||
|
||||
INTEL PMIC GPIO DRIVERS
|
||||
M: Andy Shevchenko <andy@kernel.org>
|
||||
S: Maintained
|
||||
|
@ -595,7 +595,7 @@ config X86_INTEL_MID
|
||||
select I2C
|
||||
select DW_APB_TIMER
|
||||
select APB_TIMER
|
||||
select INTEL_SCU_IPC
|
||||
select INTEL_SCU_PCI
|
||||
select MFD_INTEL_MSIC
|
||||
---help---
|
||||
Select to build a kernel capable of supporting Intel MID (Mobile
|
||||
|
@ -88,11 +88,17 @@ static inline bool intel_mid_has_msic(void)
|
||||
return (intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_PENWELL);
|
||||
}
|
||||
|
||||
extern void intel_scu_devices_create(void);
|
||||
extern void intel_scu_devices_destroy(void);
|
||||
|
||||
#else /* !CONFIG_X86_INTEL_MID */
|
||||
|
||||
#define intel_mid_identify_cpu() 0
|
||||
#define intel_mid_has_msic() 0
|
||||
|
||||
static inline void intel_scu_devices_create(void) { }
|
||||
static inline void intel_scu_devices_destroy(void) { }
|
||||
|
||||
#endif /* !CONFIG_X86_INTEL_MID */
|
||||
|
||||
enum intel_mid_timer_options {
|
||||
@ -115,9 +121,6 @@ extern enum intel_mid_timer_options intel_mid_timer_options;
|
||||
#define SFI_MTMR_MAX_NUM 8
|
||||
#define SFI_MRTC_MAX 8
|
||||
|
||||
extern void intel_scu_devices_create(void);
|
||||
extern void intel_scu_devices_destroy(void);
|
||||
|
||||
/* VRTC timer */
|
||||
#define MRST_VRTC_MAP_SZ 1024
|
||||
/* #define MRST_VRTC_PGOFFSET 0xc00 */
|
||||
|
@ -1,59 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _ASM_X86_INTEL_PMC_IPC_H_
|
||||
#define _ASM_X86_INTEL_PMC_IPC_H_
|
||||
|
||||
/* Commands */
|
||||
#define PMC_IPC_PMIC_ACCESS 0xFF
|
||||
#define PMC_IPC_PMIC_ACCESS_READ 0x0
|
||||
#define PMC_IPC_PMIC_ACCESS_WRITE 0x1
|
||||
#define PMC_IPC_USB_PWR_CTRL 0xF0
|
||||
#define PMC_IPC_PMIC_BLACKLIST_SEL 0xEF
|
||||
#define PMC_IPC_PHY_CONFIG 0xEE
|
||||
#define PMC_IPC_NORTHPEAK_CTRL 0xED
|
||||
#define PMC_IPC_PM_DEBUG 0xEC
|
||||
#define PMC_IPC_PMC_TELEMTRY 0xEB
|
||||
#define PMC_IPC_PMC_FW_MSG_CTRL 0xEA
|
||||
|
||||
/* IPC return code */
|
||||
#define IPC_ERR_NONE 0
|
||||
#define IPC_ERR_CMD_NOT_SUPPORTED 1
|
||||
#define IPC_ERR_CMD_NOT_SERVICED 2
|
||||
#define IPC_ERR_UNABLE_TO_SERVICE 3
|
||||
#define IPC_ERR_CMD_INVALID 4
|
||||
#define IPC_ERR_CMD_FAILED 5
|
||||
#define IPC_ERR_EMSECURITY 6
|
||||
#define IPC_ERR_UNSIGNEDKERNEL 7
|
||||
|
||||
/* GCR reg offsets from gcr base*/
|
||||
#define PMC_GCR_PMC_CFG_REG 0x08
|
||||
#define PMC_GCR_TELEM_DEEP_S0IX_REG 0x78
|
||||
#define PMC_GCR_TELEM_SHLW_S0IX_REG 0x80
|
||||
|
||||
#if IS_ENABLED(CONFIG_INTEL_PMC_IPC)
|
||||
|
||||
int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
|
||||
u32 *out, u32 outlen);
|
||||
int intel_pmc_s0ix_counter_read(u64 *data);
|
||||
int intel_pmc_gcr_read64(u32 offset, u64 *data);
|
||||
|
||||
#else
|
||||
|
||||
static inline int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
|
||||
u32 *out, u32 outlen)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int intel_pmc_s0ix_counter_read(u64 *data)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int intel_pmc_gcr_read64(u32 offset, u64 *data)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#endif /*CONFIG_INTEL_PMC_IPC*/
|
||||
|
||||
#endif
|
@ -2,61 +2,69 @@
|
||||
#ifndef _ASM_X86_INTEL_SCU_IPC_H_
|
||||
#define _ASM_X86_INTEL_SCU_IPC_H_
|
||||
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/ioport.h>
|
||||
|
||||
#define IPCMSG_INDIRECT_READ 0x02
|
||||
#define IPCMSG_INDIRECT_WRITE 0x05
|
||||
struct device;
|
||||
struct intel_scu_ipc_dev;
|
||||
|
||||
#define IPCMSG_COLD_OFF 0x80 /* Only for Tangier */
|
||||
/**
|
||||
* struct intel_scu_ipc_data - Data used to configure SCU IPC
|
||||
* @mem: Base address of SCU IPC MMIO registers
|
||||
* @irq: The IRQ number used for SCU (optional)
|
||||
*/
|
||||
struct intel_scu_ipc_data {
|
||||
struct resource mem;
|
||||
int irq;
|
||||
};
|
||||
|
||||
#define IPCMSG_WARM_RESET 0xF0
|
||||
#define IPCMSG_COLD_RESET 0xF1
|
||||
#define IPCMSG_SOFT_RESET 0xF2
|
||||
#define IPCMSG_COLD_BOOT 0xF3
|
||||
struct intel_scu_ipc_dev *
|
||||
__intel_scu_ipc_register(struct device *parent,
|
||||
const struct intel_scu_ipc_data *scu_data,
|
||||
struct module *owner);
|
||||
|
||||
#define IPCMSG_VRTC 0xFA /* Set vRTC device */
|
||||
/* Command id associated with message IPCMSG_VRTC */
|
||||
#define IPC_CMD_VRTC_SETTIME 1 /* Set time */
|
||||
#define IPC_CMD_VRTC_SETALARM 2 /* Set alarm */
|
||||
#define intel_scu_ipc_register(parent, scu_data) \
|
||||
__intel_scu_ipc_register(parent, scu_data, THIS_MODULE)
|
||||
|
||||
/* Read single register */
|
||||
int intel_scu_ipc_ioread8(u16 addr, u8 *data);
|
||||
void intel_scu_ipc_unregister(struct intel_scu_ipc_dev *scu);
|
||||
|
||||
/* Read a vector */
|
||||
int intel_scu_ipc_readv(u16 *addr, u8 *data, int len);
|
||||
struct intel_scu_ipc_dev *
|
||||
__devm_intel_scu_ipc_register(struct device *parent,
|
||||
const struct intel_scu_ipc_data *scu_data,
|
||||
struct module *owner);
|
||||
|
||||
/* Write single register */
|
||||
int intel_scu_ipc_iowrite8(u16 addr, u8 data);
|
||||
#define devm_intel_scu_ipc_register(parent, scu_data) \
|
||||
__devm_intel_scu_ipc_register(parent, scu_data, THIS_MODULE)
|
||||
|
||||
/* Write a vector */
|
||||
int intel_scu_ipc_writev(u16 *addr, u8 *data, int len);
|
||||
struct intel_scu_ipc_dev *intel_scu_ipc_dev_get(void);
|
||||
void intel_scu_ipc_dev_put(struct intel_scu_ipc_dev *scu);
|
||||
struct intel_scu_ipc_dev *devm_intel_scu_ipc_dev_get(struct device *dev);
|
||||
|
||||
/* Update single register based on the mask */
|
||||
int intel_scu_ipc_update_register(u16 addr, u8 data, u8 mask);
|
||||
int intel_scu_ipc_dev_ioread8(struct intel_scu_ipc_dev *scu, u16 addr,
|
||||
u8 *data);
|
||||
int intel_scu_ipc_dev_iowrite8(struct intel_scu_ipc_dev *scu, u16 addr,
|
||||
u8 data);
|
||||
int intel_scu_ipc_dev_readv(struct intel_scu_ipc_dev *scu, u16 *addr,
|
||||
u8 *data, size_t len);
|
||||
int intel_scu_ipc_dev_writev(struct intel_scu_ipc_dev *scu, u16 *addr,
|
||||
u8 *data, size_t len);
|
||||
|
||||
/* Issue commands to the SCU with or without data */
|
||||
int intel_scu_ipc_simple_command(int cmd, int sub);
|
||||
int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
|
||||
u32 *out, int outlen);
|
||||
int intel_scu_ipc_dev_update(struct intel_scu_ipc_dev *scu, u16 addr,
|
||||
u8 data, u8 mask);
|
||||
|
||||
extern struct blocking_notifier_head intel_scu_notifier;
|
||||
int intel_scu_ipc_dev_simple_command(struct intel_scu_ipc_dev *scu, int cmd,
|
||||
int sub);
|
||||
int intel_scu_ipc_dev_command_with_size(struct intel_scu_ipc_dev *scu, int cmd,
|
||||
int sub, const void *in, size_t inlen,
|
||||
size_t size, void *out, size_t outlen);
|
||||
|
||||
static inline void intel_scu_notifier_add(struct notifier_block *nb)
|
||||
static inline int intel_scu_ipc_dev_command(struct intel_scu_ipc_dev *scu, int cmd,
|
||||
int sub, const void *in, size_t inlen,
|
||||
void *out, size_t outlen)
|
||||
{
|
||||
blocking_notifier_chain_register(&intel_scu_notifier, nb);
|
||||
return intel_scu_ipc_dev_command_with_size(scu, cmd, sub, in, inlen,
|
||||
inlen, out, outlen);
|
||||
}
|
||||
|
||||
static inline void intel_scu_notifier_remove(struct notifier_block *nb)
|
||||
{
|
||||
blocking_notifier_chain_unregister(&intel_scu_notifier, nb);
|
||||
}
|
||||
|
||||
static inline int intel_scu_notifier_post(unsigned long v, void *p)
|
||||
{
|
||||
return blocking_notifier_call_chain(&intel_scu_notifier, v, p);
|
||||
}
|
||||
|
||||
#define SCU_AVAILABLE 1
|
||||
#define SCU_DOWN 2
|
||||
#include <asm/intel_scu_ipc_legacy.h>
|
||||
|
||||
#endif
|
||||
|
91
arch/x86/include/asm/intel_scu_ipc_legacy.h
Normal file
91
arch/x86/include/asm/intel_scu_ipc_legacy.h
Normal file
@ -0,0 +1,91 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _ASM_X86_INTEL_SCU_IPC_LEGACY_H_
|
||||
#define _ASM_X86_INTEL_SCU_IPC_LEGACY_H_
|
||||
|
||||
#include <linux/notifier.h>
|
||||
|
||||
#define IPCMSG_INDIRECT_READ 0x02
|
||||
#define IPCMSG_INDIRECT_WRITE 0x05
|
||||
|
||||
#define IPCMSG_COLD_OFF 0x80 /* Only for Tangier */
|
||||
|
||||
#define IPCMSG_WARM_RESET 0xF0
|
||||
#define IPCMSG_COLD_RESET 0xF1
|
||||
#define IPCMSG_SOFT_RESET 0xF2
|
||||
#define IPCMSG_COLD_BOOT 0xF3
|
||||
|
||||
#define IPCMSG_VRTC 0xFA /* Set vRTC device */
|
||||
/* Command id associated with message IPCMSG_VRTC */
|
||||
#define IPC_CMD_VRTC_SETTIME 1 /* Set time */
|
||||
#define IPC_CMD_VRTC_SETALARM 2 /* Set alarm */
|
||||
|
||||
/* Don't call these in new code - they will be removed eventually */
|
||||
|
||||
/* Read single register */
|
||||
static inline int intel_scu_ipc_ioread8(u16 addr, u8 *data)
|
||||
{
|
||||
return intel_scu_ipc_dev_ioread8(NULL, addr, data);
|
||||
}
|
||||
|
||||
/* Read a vector */
|
||||
static inline int intel_scu_ipc_readv(u16 *addr, u8 *data, int len)
|
||||
{
|
||||
return intel_scu_ipc_dev_readv(NULL, addr, data, len);
|
||||
}
|
||||
|
||||
/* Write single register */
|
||||
static inline int intel_scu_ipc_iowrite8(u16 addr, u8 data)
|
||||
{
|
||||
return intel_scu_ipc_dev_iowrite8(NULL, addr, data);
|
||||
}
|
||||
|
||||
/* Write a vector */
|
||||
static inline int intel_scu_ipc_writev(u16 *addr, u8 *data, int len)
|
||||
{
|
||||
return intel_scu_ipc_dev_writev(NULL, addr, data, len);
|
||||
}
|
||||
|
||||
/* Update single register based on the mask */
|
||||
static inline int intel_scu_ipc_update_register(u16 addr, u8 data, u8 mask)
|
||||
{
|
||||
return intel_scu_ipc_dev_update(NULL, addr, data, mask);
|
||||
}
|
||||
|
||||
/* Issue commands to the SCU with or without data */
|
||||
static inline int intel_scu_ipc_simple_command(int cmd, int sub)
|
||||
{
|
||||
return intel_scu_ipc_dev_simple_command(NULL, cmd, sub);
|
||||
}
|
||||
|
||||
static inline int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
|
||||
u32 *out, int outlen)
|
||||
{
|
||||
/* New API takes both inlen and outlen as bytes so convert here */
|
||||
size_t inbytes = inlen * sizeof(u32);
|
||||
size_t outbytes = outlen * sizeof(u32);
|
||||
|
||||
return intel_scu_ipc_dev_command_with_size(NULL, cmd, sub, in, inbytes,
|
||||
inlen, out, outbytes);
|
||||
}
|
||||
|
||||
extern struct blocking_notifier_head intel_scu_notifier;
|
||||
|
||||
static inline void intel_scu_notifier_add(struct notifier_block *nb)
|
||||
{
|
||||
blocking_notifier_chain_register(&intel_scu_notifier, nb);
|
||||
}
|
||||
|
||||
static inline void intel_scu_notifier_remove(struct notifier_block *nb)
|
||||
{
|
||||
blocking_notifier_chain_unregister(&intel_scu_notifier, nb);
|
||||
}
|
||||
|
||||
static inline int intel_scu_notifier_post(unsigned long v, void *p)
|
||||
{
|
||||
return blocking_notifier_call_chain(&intel_scu_notifier, v, p);
|
||||
}
|
||||
|
||||
#define SCU_AVAILABLE 1
|
||||
#define SCU_DOWN 2
|
||||
|
||||
#endif
|
@ -10,6 +10,8 @@
|
||||
#define TELEM_MAX_EVENTS_SRAM 28
|
||||
#define TELEM_MAX_OS_ALLOCATED_EVENTS 20
|
||||
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
enum telemetry_unit {
|
||||
TELEM_PSS = 0,
|
||||
TELEM_IOSS,
|
||||
@ -51,6 +53,8 @@ struct telemetry_plt_config {
|
||||
struct telemetry_unit_config ioss_config;
|
||||
struct mutex telem_trace_lock;
|
||||
struct mutex telem_lock;
|
||||
struct intel_pmc_dev *pmc;
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
bool telem_in_use;
|
||||
};
|
||||
|
||||
@ -92,7 +96,7 @@ int telemetry_set_pltdata(const struct telemetry_core_ops *ops,
|
||||
|
||||
int telemetry_clear_pltdata(void);
|
||||
|
||||
int telemetry_pltconfig_valid(void);
|
||||
struct telemetry_plt_config *telemetry_get_pltdata(void);
|
||||
|
||||
int telemetry_get_evtname(enum telemetry_unit telem_unit,
|
||||
const char **name, int len);
|
||||
|
@ -551,7 +551,7 @@ config INTEL_SOC_PMIC
|
||||
|
||||
config INTEL_SOC_PMIC_BXTWC
|
||||
tristate "Support for Intel Broxton Whiskey Cove PMIC"
|
||||
depends on INTEL_PMC_IPC
|
||||
depends on MFD_INTEL_PMC_BXT
|
||||
select MFD_CORE
|
||||
select REGMAP_IRQ
|
||||
help
|
||||
@ -593,7 +593,7 @@ config INTEL_SOC_PMIC_MRFLD
|
||||
tristate "Support for Intel Merrifield Basin Cove PMIC"
|
||||
depends on GPIOLIB
|
||||
depends on ACPI
|
||||
depends on INTEL_SCU_IPC
|
||||
depends on INTEL_SCU
|
||||
select MFD_CORE
|
||||
select REGMAP_IRQ
|
||||
help
|
||||
@ -625,13 +625,27 @@ config MFD_INTEL_LPSS_PCI
|
||||
|
||||
config MFD_INTEL_MSIC
|
||||
bool "Intel MSIC"
|
||||
depends on INTEL_SCU_IPC
|
||||
depends on INTEL_SCU
|
||||
select MFD_CORE
|
||||
help
|
||||
Select this option to enable access to Intel MSIC (Avatele
|
||||
Passage) chip. This chip embeds audio, battery, GPIO, etc.
|
||||
devices used in Intel Medfield platforms.
|
||||
|
||||
config MFD_INTEL_PMC_BXT
|
||||
tristate "Intel PMC Driver for Broxton"
|
||||
depends on X86
|
||||
depends on X86_PLATFORM_DEVICES
|
||||
depends on ACPI
|
||||
select INTEL_SCU_IPC
|
||||
select MFD_CORE
|
||||
help
|
||||
This driver provides support for the PMC (Power Management
|
||||
Controller) on Intel Broxton and Apollo Lake. The PMC is a
|
||||
multi-function device that exposes IPC, General Control
|
||||
Register and P-unit access. In addition this creates devices
|
||||
for iTCO watchdog and telemetry that are part of the PMC.
|
||||
|
||||
config MFD_IPAQ_MICRO
|
||||
bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support"
|
||||
depends on SA1100_H3100 || SA1100_H3600
|
||||
|
@ -212,6 +212,7 @@ obj-$(CONFIG_MFD_INTEL_LPSS) += intel-lpss.o
|
||||
obj-$(CONFIG_MFD_INTEL_LPSS_PCI) += intel-lpss-pci.o
|
||||
obj-$(CONFIG_MFD_INTEL_LPSS_ACPI) += intel-lpss-acpi.o
|
||||
obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
|
||||
obj-$(CONFIG_MFD_INTEL_PMC_BXT) += intel_pmc_bxt.o
|
||||
obj-$(CONFIG_MFD_PALMAS) += palmas.o
|
||||
obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o
|
||||
obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
|
||||
|
468
drivers/mfd/intel_pmc_bxt.c
Normal file
468
drivers/mfd/intel_pmc_bxt.c
Normal file
@ -0,0 +1,468 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Driver for the Intel Broxton PMC
|
||||
*
|
||||
* (C) Copyright 2014 - 2020 Intel Corporation
|
||||
*
|
||||
* This driver is based on Intel SCU IPC driver (intel_scu_ipc.c) by
|
||||
* Sreedhara DS <sreedhara.ds@intel.com>
|
||||
*
|
||||
* The PMC (Power Management Controller) running on the ARC processor
|
||||
* communicates with another entity running in the IA (Intel Architecture)
|
||||
* core through an IPC (Intel Processor Communications) mechanism which in
|
||||
* turn sends messages between the IA and the PMC.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io-64-nonatomic-lo-hi.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/mfd/intel_pmc_bxt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/itco_wdt.h>
|
||||
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
/* Residency with clock rate at 19.2MHz to usecs */
|
||||
#define S0IX_RESIDENCY_IN_USECS(d, s) \
|
||||
({ \
|
||||
u64 result = 10ull * ((d) + (s)); \
|
||||
do_div(result, 192); \
|
||||
result; \
|
||||
})
|
||||
|
||||
/* Resources exported from IFWI */
|
||||
#define PLAT_RESOURCE_IPC_INDEX 0
|
||||
#define PLAT_RESOURCE_IPC_SIZE 0x1000
|
||||
#define PLAT_RESOURCE_GCR_OFFSET 0x1000
|
||||
#define PLAT_RESOURCE_GCR_SIZE 0x1000
|
||||
#define PLAT_RESOURCE_BIOS_DATA_INDEX 1
|
||||
#define PLAT_RESOURCE_BIOS_IFACE_INDEX 2
|
||||
#define PLAT_RESOURCE_TELEM_SSRAM_INDEX 3
|
||||
#define PLAT_RESOURCE_ISP_DATA_INDEX 4
|
||||
#define PLAT_RESOURCE_ISP_IFACE_INDEX 5
|
||||
#define PLAT_RESOURCE_GTD_DATA_INDEX 6
|
||||
#define PLAT_RESOURCE_GTD_IFACE_INDEX 7
|
||||
#define PLAT_RESOURCE_ACPI_IO_INDEX 0
|
||||
|
||||
/*
|
||||
* BIOS does not create an ACPI device for each PMC function, but
|
||||
* exports multiple resources from one ACPI device (IPC) for multiple
|
||||
* functions. This driver is responsible for creating a child device and
|
||||
* to export resources for those functions.
|
||||
*/
|
||||
#define SMI_EN_OFFSET 0x0040
|
||||
#define SMI_EN_SIZE 4
|
||||
#define TCO_BASE_OFFSET 0x0060
|
||||
#define TCO_REGS_SIZE 16
|
||||
#define TELEM_SSRAM_SIZE 240
|
||||
#define TELEM_PMC_SSRAM_OFFSET 0x1b00
|
||||
#define TELEM_PUNIT_SSRAM_OFFSET 0x1a00
|
||||
|
||||
/* Commands */
|
||||
#define PMC_NORTHPEAK_CTRL 0xed
|
||||
|
||||
static inline bool is_gcr_valid(u32 offset)
|
||||
{
|
||||
return offset < PLAT_RESOURCE_GCR_SIZE - 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_pmc_gcr_read64() - Read a 64-bit PMC GCR register
|
||||
* @pmc: PMC device pointer
|
||||
* @offset: offset of GCR register from GCR address base
|
||||
* @data: data pointer for storing the register output
|
||||
*
|
||||
* Reads the 64-bit PMC GCR register at given offset.
|
||||
*
|
||||
* Return: Negative value on error or 0 on success.
|
||||
*/
|
||||
int intel_pmc_gcr_read64(struct intel_pmc_dev *pmc, u32 offset, u64 *data)
|
||||
{
|
||||
if (!is_gcr_valid(offset))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&pmc->gcr_lock);
|
||||
*data = readq(pmc->gcr_mem_base + offset);
|
||||
spin_unlock(&pmc->gcr_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_pmc_gcr_read64);
|
||||
|
||||
/**
|
||||
* intel_pmc_gcr_update() - Update PMC GCR register bits
|
||||
* @pmc: PMC device pointer
|
||||
* @offset: offset of GCR register from GCR address base
|
||||
* @mask: bit mask for update operation
|
||||
* @val: update value
|
||||
*
|
||||
* Updates the bits of given GCR register as specified by
|
||||
* @mask and @val.
|
||||
*
|
||||
* Return: Negative value on error or 0 on success.
|
||||
*/
|
||||
int intel_pmc_gcr_update(struct intel_pmc_dev *pmc, u32 offset, u32 mask, u32 val)
|
||||
{
|
||||
u32 new_val;
|
||||
|
||||
if (!is_gcr_valid(offset))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&pmc->gcr_lock);
|
||||
new_val = readl(pmc->gcr_mem_base + offset);
|
||||
|
||||
new_val = (new_val & ~mask) | (val & mask);
|
||||
writel(new_val, pmc->gcr_mem_base + offset);
|
||||
|
||||
new_val = readl(pmc->gcr_mem_base + offset);
|
||||
spin_unlock(&pmc->gcr_lock);
|
||||
|
||||
/* Check whether the bit update is successful */
|
||||
return (new_val & mask) != (val & mask) ? -EIO : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_pmc_gcr_update);
|
||||
|
||||
/**
|
||||
* intel_pmc_s0ix_counter_read() - Read S0ix residency
|
||||
* @pmc: PMC device pointer
|
||||
* @data: Out param that contains current S0ix residency count.
|
||||
*
|
||||
* Writes to @data how many usecs the system has been in low-power S0ix
|
||||
* state.
|
||||
*
|
||||
* Return: An error code or 0 on success.
|
||||
*/
|
||||
int intel_pmc_s0ix_counter_read(struct intel_pmc_dev *pmc, u64 *data)
|
||||
{
|
||||
u64 deep, shlw;
|
||||
|
||||
spin_lock(&pmc->gcr_lock);
|
||||
deep = readq(pmc->gcr_mem_base + PMC_GCR_TELEM_DEEP_S0IX_REG);
|
||||
shlw = readq(pmc->gcr_mem_base + PMC_GCR_TELEM_SHLW_S0IX_REG);
|
||||
spin_unlock(&pmc->gcr_lock);
|
||||
|
||||
*data = S0IX_RESIDENCY_IN_USECS(deep, shlw);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_pmc_s0ix_counter_read);
|
||||
|
||||
/**
|
||||
* simplecmd_store() - Send a simple IPC command
|
||||
* @dev: Device under the attribute is
|
||||
* @attr: Attribute in question
|
||||
* @buf: Buffer holding data to be stored to the attribute
|
||||
* @count: Number of bytes in @buf
|
||||
*
|
||||
* Expects a string with two integers separated with space. These two
|
||||
* values hold command and subcommand that is send to PMC.
|
||||
*
|
||||
* Return: Number number of bytes written (@count) or negative errno in
|
||||
* case of error.
|
||||
*/
|
||||
static ssize_t simplecmd_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct intel_pmc_dev *pmc = dev_get_drvdata(dev);
|
||||
struct intel_scu_ipc_dev *scu = pmc->scu;
|
||||
int subcmd;
|
||||
int cmd;
|
||||
int ret;
|
||||
|
||||
ret = sscanf(buf, "%d %d", &cmd, &subcmd);
|
||||
if (ret != 2) {
|
||||
dev_err(dev, "Invalid values, expected: cmd subcmd\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = intel_scu_ipc_dev_simple_command(scu, cmd, subcmd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR_WO(simplecmd);
|
||||
|
||||
/**
|
||||
* northpeak_store() - Enable or disable Northpeak
|
||||
* @dev: Device under the attribute is
|
||||
* @attr: Attribute in question
|
||||
* @buf: Buffer holding data to be stored to the attribute
|
||||
* @count: Number of bytes in @buf
|
||||
*
|
||||
* Expects an unsigned integer. Non-zero enables Northpeak and zero
|
||||
* disables it.
|
||||
*
|
||||
* Return: Number number of bytes written (@count) or negative errno in
|
||||
* case of error.
|
||||
*/
|
||||
static ssize_t northpeak_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct intel_pmc_dev *pmc = dev_get_drvdata(dev);
|
||||
struct intel_scu_ipc_dev *scu = pmc->scu;
|
||||
unsigned long val;
|
||||
int subcmd;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul(buf, 0, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Northpeak is enabled if subcmd == 1 and disabled if it is 0 */
|
||||
if (val)
|
||||
subcmd = 1;
|
||||
else
|
||||
subcmd = 0;
|
||||
|
||||
ret = intel_scu_ipc_dev_simple_command(scu, PMC_NORTHPEAK_CTRL, subcmd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR_WO(northpeak);
|
||||
|
||||
static struct attribute *intel_pmc_attrs[] = {
|
||||
&dev_attr_northpeak.attr,
|
||||
&dev_attr_simplecmd.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group intel_pmc_group = {
|
||||
.attrs = intel_pmc_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *intel_pmc_groups[] = {
|
||||
&intel_pmc_group,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct resource punit_res[6];
|
||||
|
||||
static struct mfd_cell punit = {
|
||||
.name = "intel_punit_ipc",
|
||||
.resources = punit_res,
|
||||
};
|
||||
|
||||
static struct itco_wdt_platform_data tco_pdata = {
|
||||
.name = "Apollo Lake SoC",
|
||||
.version = 5,
|
||||
.no_reboot_use_pmc = true,
|
||||
};
|
||||
|
||||
static struct resource tco_res[2];
|
||||
|
||||
static const struct mfd_cell tco = {
|
||||
.name = "iTCO_wdt",
|
||||
.ignore_resource_conflicts = true,
|
||||
.resources = tco_res,
|
||||
.num_resources = ARRAY_SIZE(tco_res),
|
||||
.platform_data = &tco_pdata,
|
||||
.pdata_size = sizeof(tco_pdata),
|
||||
};
|
||||
|
||||
static const struct resource telem_res[] = {
|
||||
DEFINE_RES_MEM(TELEM_PUNIT_SSRAM_OFFSET, TELEM_SSRAM_SIZE),
|
||||
DEFINE_RES_MEM(TELEM_PMC_SSRAM_OFFSET, TELEM_SSRAM_SIZE),
|
||||
};
|
||||
|
||||
static const struct mfd_cell telem = {
|
||||
.name = "intel_telemetry",
|
||||
.resources = telem_res,
|
||||
.num_resources = ARRAY_SIZE(telem_res),
|
||||
};
|
||||
|
||||
static int intel_pmc_get_tco_resources(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
|
||||
if (acpi_has_watchdog())
|
||||
return 0;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO,
|
||||
PLAT_RESOURCE_ACPI_IO_INDEX);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get IO resource\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tco_res[0].flags = IORESOURCE_IO;
|
||||
tco_res[0].start = res->start + TCO_BASE_OFFSET;
|
||||
tco_res[0].end = tco_res[0].start + TCO_REGS_SIZE - 1;
|
||||
tco_res[1].flags = IORESOURCE_IO;
|
||||
tco_res[1].start = res->start + SMI_EN_OFFSET;
|
||||
tco_res[1].end = tco_res[1].start + SMI_EN_SIZE - 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_pmc_get_resources(struct platform_device *pdev,
|
||||
struct intel_pmc_dev *pmc,
|
||||
struct intel_scu_ipc_data *scu_data)
|
||||
{
|
||||
struct resource gcr_res;
|
||||
size_t npunit_res = 0;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
scu_data->irq = platform_get_irq_optional(pdev, 0);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_IPC_INDEX);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get IPC resource\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* IPC registers */
|
||||
scu_data->mem.flags = res->flags;
|
||||
scu_data->mem.start = res->start;
|
||||
scu_data->mem.end = res->start + PLAT_RESOURCE_IPC_SIZE - 1;
|
||||
|
||||
/* GCR registers */
|
||||
gcr_res.flags = res->flags;
|
||||
gcr_res.start = res->start + PLAT_RESOURCE_GCR_OFFSET;
|
||||
gcr_res.end = gcr_res.start + PLAT_RESOURCE_GCR_SIZE - 1;
|
||||
|
||||
pmc->gcr_mem_base = devm_ioremap_resource(&pdev->dev, &gcr_res);
|
||||
if (IS_ERR(pmc->gcr_mem_base))
|
||||
return PTR_ERR(pmc->gcr_mem_base);
|
||||
|
||||
/* Only register iTCO watchdog if there is no WDAT ACPI table */
|
||||
ret = intel_pmc_get_tco_resources(pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* BIOS data register */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_BIOS_DATA_INDEX);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get resource of P-unit BIOS data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
punit_res[npunit_res++] = *res;
|
||||
|
||||
/* BIOS interface register */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_BIOS_IFACE_INDEX);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get resource of P-unit BIOS interface\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
punit_res[npunit_res++] = *res;
|
||||
|
||||
/* ISP data register, optional */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_ISP_DATA_INDEX);
|
||||
if (res)
|
||||
punit_res[npunit_res++] = *res;
|
||||
|
||||
/* ISP interface register, optional */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_ISP_IFACE_INDEX);
|
||||
if (res)
|
||||
punit_res[npunit_res++] = *res;
|
||||
|
||||
/* GTD data register, optional */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_GTD_DATA_INDEX);
|
||||
if (res)
|
||||
punit_res[npunit_res++] = *res;
|
||||
|
||||
/* GTD interface register, optional */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_GTD_IFACE_INDEX);
|
||||
if (res)
|
||||
punit_res[npunit_res++] = *res;
|
||||
|
||||
punit.num_resources = npunit_res;
|
||||
|
||||
/* Telemetry SSRAM is optional */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_TELEM_SSRAM_INDEX);
|
||||
if (res)
|
||||
pmc->telem_base = res;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_pmc_create_devices(struct intel_pmc_dev *pmc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!acpi_has_watchdog()) {
|
||||
ret = devm_mfd_add_devices(pmc->dev, PLATFORM_DEVID_AUTO, &tco,
|
||||
1, NULL, 0, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_mfd_add_devices(pmc->dev, PLATFORM_DEVID_AUTO, &punit, 1,
|
||||
NULL, 0, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pmc->telem_base) {
|
||||
ret = devm_mfd_add_devices(pmc->dev, PLATFORM_DEVID_AUTO,
|
||||
&telem, 1, pmc->telem_base, 0, NULL);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id intel_pmc_acpi_ids[] = {
|
||||
{ "INT34D2" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, intel_pmc_acpi_ids);
|
||||
|
||||
static int intel_pmc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct intel_scu_ipc_data scu_data = {};
|
||||
struct intel_pmc_dev *pmc;
|
||||
int ret;
|
||||
|
||||
pmc = devm_kzalloc(&pdev->dev, sizeof(*pmc), GFP_KERNEL);
|
||||
if (!pmc)
|
||||
return -ENOMEM;
|
||||
|
||||
pmc->dev = &pdev->dev;
|
||||
spin_lock_init(&pmc->gcr_lock);
|
||||
|
||||
ret = intel_pmc_get_resources(pdev, pmc, &scu_data);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to request resources\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
pmc->scu = devm_intel_scu_ipc_register(&pdev->dev, &scu_data);
|
||||
if (IS_ERR(pmc->scu))
|
||||
return PTR_ERR(pmc->scu);
|
||||
|
||||
platform_set_drvdata(pdev, pmc);
|
||||
|
||||
ret = intel_pmc_create_devices(pmc);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "Failed to create PMC devices\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver intel_pmc_driver = {
|
||||
.probe = intel_pmc_probe,
|
||||
.driver = {
|
||||
.name = "intel_pmc_bxt",
|
||||
.acpi_match_table = intel_pmc_acpi_ids,
|
||||
.dev_groups = intel_pmc_groups,
|
||||
},
|
||||
};
|
||||
module_platform_driver(intel_pmc_driver);
|
||||
|
||||
MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
|
||||
MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>");
|
||||
MODULE_DESCRIPTION("Intel Broxton PMC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -15,7 +15,7 @@
|
||||
#include <linux/mfd/intel_soc_pmic_bxtwc.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <asm/intel_pmc_ipc.h>
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
/* PMIC device registers */
|
||||
#define REG_ADDR_MASK 0xFF00
|
||||
@ -58,6 +58,10 @@
|
||||
/* Whiskey Cove PMIC share same ACPI ID between different platforms */
|
||||
#define BROXTON_PMIC_WC_HRV 4
|
||||
|
||||
#define PMC_PMIC_ACCESS 0xFF
|
||||
#define PMC_PMIC_READ 0x0
|
||||
#define PMC_PMIC_WRITE 0x1
|
||||
|
||||
enum bxtwc_irqs {
|
||||
BXTWC_PWRBTN_LVL1_IRQ = 0,
|
||||
BXTWC_TMU_LVL1_IRQ,
|
||||
@ -288,13 +292,12 @@ static int regmap_ipc_byte_reg_read(void *context, unsigned int reg,
|
||||
|
||||
ipc_in[0] = reg;
|
||||
ipc_in[1] = i2c_addr;
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS,
|
||||
PMC_IPC_PMIC_ACCESS_READ,
|
||||
ipc_in, sizeof(ipc_in), (u32 *)ipc_out, 1);
|
||||
if (ret) {
|
||||
dev_err(pmic->dev, "Failed to read from PMIC\n");
|
||||
ret = intel_scu_ipc_dev_command(pmic->scu, PMC_PMIC_ACCESS,
|
||||
PMC_PMIC_READ, ipc_in, sizeof(ipc_in),
|
||||
ipc_out, sizeof(ipc_out));
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = ipc_out[0];
|
||||
|
||||
return 0;
|
||||
@ -303,7 +306,6 @@ static int regmap_ipc_byte_reg_read(void *context, unsigned int reg,
|
||||
static int regmap_ipc_byte_reg_write(void *context, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
int ret;
|
||||
int i2c_addr;
|
||||
u8 ipc_in[3];
|
||||
struct intel_soc_pmic *pmic = context;
|
||||
@ -321,15 +323,9 @@ static int regmap_ipc_byte_reg_write(void *context, unsigned int reg,
|
||||
ipc_in[0] = reg;
|
||||
ipc_in[1] = i2c_addr;
|
||||
ipc_in[2] = val;
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMIC_ACCESS,
|
||||
PMC_IPC_PMIC_ACCESS_WRITE,
|
||||
ipc_in, sizeof(ipc_in), NULL, 0);
|
||||
if (ret) {
|
||||
dev_err(pmic->dev, "Failed to write to PMIC\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return intel_scu_ipc_dev_command(pmic->scu, PMC_PMIC_ACCESS,
|
||||
PMC_PMIC_WRITE, ipc_in, sizeof(ipc_in),
|
||||
NULL, 0);
|
||||
}
|
||||
|
||||
/* sysfs interfaces to r/w PMIC registers, required by initial script */
|
||||
@ -457,6 +453,10 @@ static int bxtwc_probe(struct platform_device *pdev)
|
||||
dev_set_drvdata(&pdev->dev, pmic);
|
||||
pmic->dev = &pdev->dev;
|
||||
|
||||
pmic->scu = devm_intel_scu_ipc_dev_get(&pdev->dev);
|
||||
if (!pmic->scu)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
pmic->regmap = devm_regmap_init(&pdev->dev, NULL, pmic,
|
||||
&bxtwc_regmap_config);
|
||||
if (IS_ERR(pmic->regmap)) {
|
||||
|
@ -74,10 +74,11 @@ static const struct mfd_cell bcove_dev[] = {
|
||||
static int bcove_ipc_byte_reg_read(void *context, unsigned int reg,
|
||||
unsigned int *val)
|
||||
{
|
||||
struct intel_soc_pmic *pmic = context;
|
||||
u8 ipc_out;
|
||||
int ret;
|
||||
|
||||
ret = intel_scu_ipc_ioread8(reg, &ipc_out);
|
||||
ret = intel_scu_ipc_dev_ioread8(pmic->scu, reg, &ipc_out);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -88,10 +89,11 @@ static int bcove_ipc_byte_reg_read(void *context, unsigned int reg,
|
||||
static int bcove_ipc_byte_reg_write(void *context, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
struct intel_soc_pmic *pmic = context;
|
||||
u8 ipc_in = val;
|
||||
int ret;
|
||||
|
||||
ret = intel_scu_ipc_iowrite8(reg, ipc_in);
|
||||
ret = intel_scu_ipc_dev_iowrite8(pmic->scu, reg, ipc_in);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -117,6 +119,10 @@ static int bcove_probe(struct platform_device *pdev)
|
||||
if (!pmic)
|
||||
return -ENOMEM;
|
||||
|
||||
pmic->scu = devm_intel_scu_ipc_dev_get(dev);
|
||||
if (!pmic->scu)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, pmic);
|
||||
pmic->dev = &pdev->dev;
|
||||
|
||||
|
@ -1269,7 +1269,8 @@ config INTEL_UNCORE_FREQ_CONTROL
|
||||
config INTEL_BXTWC_PMIC_TMU
|
||||
tristate "Intel BXT Whiskey Cove TMU Driver"
|
||||
depends on REGMAP
|
||||
depends on INTEL_SOC_PMIC_BXTWC && INTEL_PMC_IPC
|
||||
depends on MFD_INTEL_PMC_BXT
|
||||
depends on INTEL_SOC_PMIC_BXTWC
|
||||
---help---
|
||||
Select this driver to use Intel BXT Whiskey Cove PMIC TMU feature.
|
||||
This driver enables the alarm wakeup functionality in the TMU unit
|
||||
@ -1295,7 +1296,7 @@ config INTEL_MFLD_THERMAL
|
||||
|
||||
config INTEL_MID_POWER_BUTTON
|
||||
tristate "power button driver for Intel MID platforms"
|
||||
depends on INTEL_SCU_IPC && INPUT
|
||||
depends on INTEL_SCU && INPUT
|
||||
help
|
||||
This driver handles the power button on the Intel MID platforms.
|
||||
|
||||
@ -1327,14 +1328,6 @@ config INTEL_PMC_CORE
|
||||
- LTR Ignore
|
||||
- MPHY/PLL gating status (Sunrisepoint PCH only)
|
||||
|
||||
config INTEL_PMC_IPC
|
||||
tristate "Intel PMC IPC Driver"
|
||||
depends on ACPI && PCI
|
||||
---help---
|
||||
This driver provides support for PMC control on some Intel platforms.
|
||||
The PMC is an ARC processor which defines IPC commands for communication
|
||||
with other entities in the CPU.
|
||||
|
||||
config INTEL_PUNIT_IPC
|
||||
tristate "Intel P-Unit IPC Driver"
|
||||
---help---
|
||||
@ -1342,17 +1335,30 @@ config INTEL_PUNIT_IPC
|
||||
which is used to bridge the communications between kernel and P-Unit.
|
||||
|
||||
config INTEL_SCU_IPC
|
||||
bool "Intel SCU IPC Support"
|
||||
depends on X86_INTEL_MID
|
||||
default y
|
||||
---help---
|
||||
IPC is used to bridge the communications between kernel and SCU on
|
||||
some embedded Intel x86 platforms. This is not needed for PC-type
|
||||
machines.
|
||||
bool
|
||||
|
||||
config INTEL_SCU
|
||||
bool
|
||||
select INTEL_SCU_IPC
|
||||
|
||||
config INTEL_SCU_PCI
|
||||
bool "Intel SCU PCI driver"
|
||||
depends on PCI
|
||||
select INTEL_SCU
|
||||
help
|
||||
This driver is used to bridge the communications between kernel
|
||||
and SCU on some embedded Intel x86 platforms. It also creates
|
||||
devices that are connected to the SoC through the SCU.
|
||||
Platforms supported:
|
||||
Medfield
|
||||
Clovertrail
|
||||
Merrifield
|
||||
Broxton
|
||||
Apollo Lake
|
||||
|
||||
config INTEL_SCU_IPC_UTIL
|
||||
tristate "Intel SCU IPC utility driver"
|
||||
depends on INTEL_SCU_IPC
|
||||
depends on INTEL_SCU
|
||||
---help---
|
||||
The IPC Util driver provides an interface with the SCU enabling
|
||||
low level access for debug work and updating the firmware. Say
|
||||
@ -1360,7 +1366,9 @@ config INTEL_SCU_IPC_UTIL
|
||||
|
||||
config INTEL_TELEMETRY
|
||||
tristate "Intel SoC Telemetry Driver"
|
||||
depends on INTEL_PMC_IPC && INTEL_PUNIT_IPC && X86_64
|
||||
depends on X86_64
|
||||
depends on MFD_INTEL_PMC_BXT
|
||||
depends on INTEL_PUNIT_IPC
|
||||
---help---
|
||||
This driver provides interfaces to configure and use
|
||||
telemetry for INTEL SoC from APL onwards. It is also
|
||||
|
@ -138,9 +138,9 @@ obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o
|
||||
obj-$(CONFIG_INTEL_MID_POWER_BUTTON) += intel_mid_powerbtn.o
|
||||
obj-$(CONFIG_INTEL_MRFLD_PWRBTN) += intel_mrfld_pwrbtn.o
|
||||
obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o intel_pmc_core_pltdrv.o
|
||||
obj-$(CONFIG_INTEL_PMC_IPC) += intel_pmc_ipc.o
|
||||
obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o
|
||||
obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o
|
||||
obj-$(CONFIG_INTEL_SCU_PCI) += intel_scu_pcidrv.o
|
||||
obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o
|
||||
obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.o \
|
||||
intel_telemetry_pltdrv.o \
|
||||
|
@ -46,6 +46,7 @@ struct mid_pb_ddata {
|
||||
unsigned short mirqlvl1_addr;
|
||||
unsigned short pbstat_addr;
|
||||
u8 pbstat_mask;
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
int (*setup)(struct mid_pb_ddata *ddata);
|
||||
};
|
||||
|
||||
@ -55,7 +56,8 @@ static int mid_pbstat(struct mid_pb_ddata *ddata, int *value)
|
||||
int ret;
|
||||
u8 pbstat;
|
||||
|
||||
ret = intel_scu_ipc_ioread8(ddata->pbstat_addr, &pbstat);
|
||||
ret = intel_scu_ipc_dev_ioread8(ddata->scu, ddata->pbstat_addr,
|
||||
&pbstat);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -67,14 +69,15 @@ static int mid_pbstat(struct mid_pb_ddata *ddata, int *value)
|
||||
|
||||
static int mid_irq_ack(struct mid_pb_ddata *ddata)
|
||||
{
|
||||
return intel_scu_ipc_update_register(ddata->mirqlvl1_addr, 0, MSIC_PWRBTNM);
|
||||
return intel_scu_ipc_dev_update(ddata->scu, ddata->mirqlvl1_addr, 0,
|
||||
MSIC_PWRBTNM);
|
||||
}
|
||||
|
||||
static int mrfld_setup(struct mid_pb_ddata *ddata)
|
||||
{
|
||||
/* Unmask the PBIRQ and MPBIRQ on Tangier */
|
||||
intel_scu_ipc_update_register(BCOVE_PBIRQ, 0, MSIC_PWRBTNM);
|
||||
intel_scu_ipc_update_register(BCOVE_PBIRQMASK, 0, MSIC_PWRBTNM);
|
||||
intel_scu_ipc_dev_update(ddata->scu, BCOVE_PBIRQ, 0, MSIC_PWRBTNM);
|
||||
intel_scu_ipc_dev_update(ddata->scu, BCOVE_PBIRQMASK, 0, MSIC_PWRBTNM);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -161,6 +164,10 @@ static int mid_pb_probe(struct platform_device *pdev)
|
||||
return error;
|
||||
}
|
||||
|
||||
ddata->scu = devm_intel_scu_ipc_dev_get(&pdev->dev);
|
||||
if (!ddata->scu)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
error = devm_request_threaded_irq(&pdev->dev, irq, NULL, mid_pb_isr,
|
||||
IRQF_ONESHOT, DRIVER_NAME, ddata);
|
||||
if (error) {
|
||||
|
@ -1,949 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Driver for the Intel PMC IPC mechanism
|
||||
*
|
||||
* (C) Copyright 2014-2015 Intel Corporation
|
||||
*
|
||||
* This driver is based on Intel SCU IPC driver(intel_scu_ipc.c) by
|
||||
* Sreedhara DS <sreedhara.ds@intel.com>
|
||||
*
|
||||
* PMC running in ARC processor communicates with other entity running in IA
|
||||
* core through IPC mechanism which in turn messaging between IA core ad PMC.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io-64-nonatomic-lo-hi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/intel_pmc_ipc.h>
|
||||
|
||||
#include <linux/platform_data/itco_wdt.h>
|
||||
|
||||
/*
|
||||
* IPC registers
|
||||
* The IA write to IPC_CMD command register triggers an interrupt to the ARC,
|
||||
* The ARC handles the interrupt and services it, writing optional data to
|
||||
* the IPC1 registers, updates the IPC_STS response register with the status.
|
||||
*/
|
||||
#define IPC_CMD 0x00
|
||||
#define IPC_CMD_MSI BIT(8)
|
||||
#define IPC_CMD_SIZE 16
|
||||
#define IPC_CMD_SUBCMD 12
|
||||
#define IPC_STATUS 0x04
|
||||
#define IPC_STATUS_IRQ BIT(2)
|
||||
#define IPC_STATUS_ERR BIT(1)
|
||||
#define IPC_STATUS_BUSY BIT(0)
|
||||
#define IPC_SPTR 0x08
|
||||
#define IPC_DPTR 0x0C
|
||||
#define IPC_WRITE_BUFFER 0x80
|
||||
#define IPC_READ_BUFFER 0x90
|
||||
|
||||
/* Residency with clock rate at 19.2MHz to usecs */
|
||||
#define S0IX_RESIDENCY_IN_USECS(d, s) \
|
||||
({ \
|
||||
u64 result = 10ull * ((d) + (s)); \
|
||||
do_div(result, 192); \
|
||||
result; \
|
||||
})
|
||||
|
||||
/*
|
||||
* 16-byte buffer for sending data associated with IPC command.
|
||||
*/
|
||||
#define IPC_DATA_BUFFER_SIZE 16
|
||||
|
||||
#define IPC_LOOP_CNT 3000000
|
||||
#define IPC_MAX_SEC 3
|
||||
|
||||
#define IPC_TRIGGER_MODE_IRQ true
|
||||
|
||||
/* exported resources from IFWI */
|
||||
#define PLAT_RESOURCE_IPC_INDEX 0
|
||||
#define PLAT_RESOURCE_IPC_SIZE 0x1000
|
||||
#define PLAT_RESOURCE_GCR_OFFSET 0x1000
|
||||
#define PLAT_RESOURCE_GCR_SIZE 0x1000
|
||||
#define PLAT_RESOURCE_BIOS_DATA_INDEX 1
|
||||
#define PLAT_RESOURCE_BIOS_IFACE_INDEX 2
|
||||
#define PLAT_RESOURCE_TELEM_SSRAM_INDEX 3
|
||||
#define PLAT_RESOURCE_ISP_DATA_INDEX 4
|
||||
#define PLAT_RESOURCE_ISP_IFACE_INDEX 5
|
||||
#define PLAT_RESOURCE_GTD_DATA_INDEX 6
|
||||
#define PLAT_RESOURCE_GTD_IFACE_INDEX 7
|
||||
#define PLAT_RESOURCE_ACPI_IO_INDEX 0
|
||||
|
||||
/*
|
||||
* BIOS does not create an ACPI device for each PMC function,
|
||||
* but exports multiple resources from one ACPI device(IPC) for
|
||||
* multiple functions. This driver is responsible to create a
|
||||
* platform device and to export resources for those functions.
|
||||
*/
|
||||
#define TCO_DEVICE_NAME "iTCO_wdt"
|
||||
#define SMI_EN_OFFSET 0x40
|
||||
#define SMI_EN_SIZE 4
|
||||
#define TCO_BASE_OFFSET 0x60
|
||||
#define TCO_REGS_SIZE 16
|
||||
#define PUNIT_DEVICE_NAME "intel_punit_ipc"
|
||||
#define TELEMETRY_DEVICE_NAME "intel_telemetry"
|
||||
#define TELEM_SSRAM_SIZE 240
|
||||
#define TELEM_PMC_SSRAM_OFFSET 0x1B00
|
||||
#define TELEM_PUNIT_SSRAM_OFFSET 0x1A00
|
||||
#define TCO_PMC_OFFSET 0x08
|
||||
#define TCO_PMC_SIZE 0x04
|
||||
|
||||
/* PMC register bit definitions */
|
||||
|
||||
/* PMC_CFG_REG bit masks */
|
||||
#define PMC_CFG_NO_REBOOT_MASK BIT_MASK(4)
|
||||
#define PMC_CFG_NO_REBOOT_EN (1 << 4)
|
||||
#define PMC_CFG_NO_REBOOT_DIS (0 << 4)
|
||||
|
||||
static struct intel_pmc_ipc_dev {
|
||||
struct device *dev;
|
||||
void __iomem *ipc_base;
|
||||
bool irq_mode;
|
||||
int irq;
|
||||
int cmd;
|
||||
struct completion cmd_complete;
|
||||
|
||||
/* The following PMC BARs share the same ACPI device with the IPC */
|
||||
resource_size_t acpi_io_base;
|
||||
int acpi_io_size;
|
||||
struct platform_device *tco_dev;
|
||||
|
||||
/* gcr */
|
||||
void __iomem *gcr_mem_base;
|
||||
bool has_gcr_regs;
|
||||
spinlock_t gcr_lock;
|
||||
|
||||
/* punit */
|
||||
struct platform_device *punit_dev;
|
||||
unsigned int punit_res_count;
|
||||
|
||||
/* Telemetry */
|
||||
resource_size_t telem_pmc_ssram_base;
|
||||
resource_size_t telem_punit_ssram_base;
|
||||
int telem_pmc_ssram_size;
|
||||
int telem_punit_ssram_size;
|
||||
u8 telem_res_inval;
|
||||
struct platform_device *telemetry_dev;
|
||||
} ipcdev;
|
||||
|
||||
static char *ipc_err_sources[] = {
|
||||
[IPC_ERR_NONE] =
|
||||
"no error",
|
||||
[IPC_ERR_CMD_NOT_SUPPORTED] =
|
||||
"command not supported",
|
||||
[IPC_ERR_CMD_NOT_SERVICED] =
|
||||
"command not serviced",
|
||||
[IPC_ERR_UNABLE_TO_SERVICE] =
|
||||
"unable to service",
|
||||
[IPC_ERR_CMD_INVALID] =
|
||||
"command invalid",
|
||||
[IPC_ERR_CMD_FAILED] =
|
||||
"command failed",
|
||||
[IPC_ERR_EMSECURITY] =
|
||||
"Invalid Battery",
|
||||
[IPC_ERR_UNSIGNEDKERNEL] =
|
||||
"Unsigned kernel",
|
||||
};
|
||||
|
||||
/* Prevent concurrent calls to the PMC */
|
||||
static DEFINE_MUTEX(ipclock);
|
||||
|
||||
static inline void ipc_send_command(u32 cmd)
|
||||
{
|
||||
ipcdev.cmd = cmd;
|
||||
if (ipcdev.irq_mode) {
|
||||
reinit_completion(&ipcdev.cmd_complete);
|
||||
cmd |= IPC_CMD_MSI;
|
||||
}
|
||||
writel(cmd, ipcdev.ipc_base + IPC_CMD);
|
||||
}
|
||||
|
||||
static inline u32 ipc_read_status(void)
|
||||
{
|
||||
return readl(ipcdev.ipc_base + IPC_STATUS);
|
||||
}
|
||||
|
||||
static inline void ipc_data_writel(u32 data, u32 offset)
|
||||
{
|
||||
writel(data, ipcdev.ipc_base + IPC_WRITE_BUFFER + offset);
|
||||
}
|
||||
|
||||
static inline u32 ipc_data_readl(u32 offset)
|
||||
{
|
||||
return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
|
||||
}
|
||||
|
||||
static inline u64 gcr_data_readq(u32 offset)
|
||||
{
|
||||
return readq(ipcdev.gcr_mem_base + offset);
|
||||
}
|
||||
|
||||
static inline int is_gcr_valid(u32 offset)
|
||||
{
|
||||
if (!ipcdev.has_gcr_regs)
|
||||
return -EACCES;
|
||||
|
||||
if (offset > PLAT_RESOURCE_GCR_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_pmc_gcr_read64() - Read a 64-bit PMC GCR register
|
||||
* @offset: offset of GCR register from GCR address base
|
||||
* @data: data pointer for storing the register output
|
||||
*
|
||||
* Reads the 64-bit PMC GCR register at given offset.
|
||||
*
|
||||
* Return: negative value on error or 0 on success.
|
||||
*/
|
||||
int intel_pmc_gcr_read64(u32 offset, u64 *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
spin_lock(&ipcdev.gcr_lock);
|
||||
|
||||
ret = is_gcr_valid(offset);
|
||||
if (ret < 0) {
|
||||
spin_unlock(&ipcdev.gcr_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*data = readq(ipcdev.gcr_mem_base + offset);
|
||||
|
||||
spin_unlock(&ipcdev.gcr_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_pmc_gcr_read64);
|
||||
|
||||
/**
|
||||
* intel_pmc_gcr_update() - Update PMC GCR register bits
|
||||
* @offset: offset of GCR register from GCR address base
|
||||
* @mask: bit mask for update operation
|
||||
* @val: update value
|
||||
*
|
||||
* Updates the bits of given GCR register as specified by
|
||||
* @mask and @val.
|
||||
*
|
||||
* Return: negative value on error or 0 on success.
|
||||
*/
|
||||
static int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val)
|
||||
{
|
||||
u32 new_val;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock(&ipcdev.gcr_lock);
|
||||
|
||||
ret = is_gcr_valid(offset);
|
||||
if (ret < 0)
|
||||
goto gcr_ipc_unlock;
|
||||
|
||||
new_val = readl(ipcdev.gcr_mem_base + offset);
|
||||
|
||||
new_val &= ~mask;
|
||||
new_val |= val & mask;
|
||||
|
||||
writel(new_val, ipcdev.gcr_mem_base + offset);
|
||||
|
||||
new_val = readl(ipcdev.gcr_mem_base + offset);
|
||||
|
||||
/* check whether the bit update is successful */
|
||||
if ((new_val & mask) != (val & mask)) {
|
||||
ret = -EIO;
|
||||
goto gcr_ipc_unlock;
|
||||
}
|
||||
|
||||
gcr_ipc_unlock:
|
||||
spin_unlock(&ipcdev.gcr_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int update_no_reboot_bit(void *priv, bool set)
|
||||
{
|
||||
u32 value = set ? PMC_CFG_NO_REBOOT_EN : PMC_CFG_NO_REBOOT_DIS;
|
||||
|
||||
return intel_pmc_gcr_update(PMC_GCR_PMC_CFG_REG,
|
||||
PMC_CFG_NO_REBOOT_MASK, value);
|
||||
}
|
||||
|
||||
static int intel_pmc_ipc_check_status(void)
|
||||
{
|
||||
int status;
|
||||
int ret = 0;
|
||||
|
||||
if (ipcdev.irq_mode) {
|
||||
if (0 == wait_for_completion_timeout(
|
||||
&ipcdev.cmd_complete, IPC_MAX_SEC * HZ))
|
||||
ret = -ETIMEDOUT;
|
||||
} else {
|
||||
int loop_count = IPC_LOOP_CNT;
|
||||
|
||||
while ((ipc_read_status() & IPC_STATUS_BUSY) && --loop_count)
|
||||
udelay(1);
|
||||
if (loop_count == 0)
|
||||
ret = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
status = ipc_read_status();
|
||||
if (ret == -ETIMEDOUT) {
|
||||
dev_err(ipcdev.dev,
|
||||
"IPC timed out, TS=0x%x, CMD=0x%x\n",
|
||||
status, ipcdev.cmd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (status & IPC_STATUS_ERR) {
|
||||
int i;
|
||||
|
||||
ret = -EIO;
|
||||
i = (status >> IPC_CMD_SIZE) & 0xFF;
|
||||
if (i < ARRAY_SIZE(ipc_err_sources))
|
||||
dev_err(ipcdev.dev,
|
||||
"IPC failed: %s, STS=0x%x, CMD=0x%x\n",
|
||||
ipc_err_sources[i], status, ipcdev.cmd);
|
||||
else
|
||||
dev_err(ipcdev.dev,
|
||||
"IPC failed: unknown, STS=0x%x, CMD=0x%x\n",
|
||||
status, ipcdev.cmd);
|
||||
if ((i == IPC_ERR_UNSIGNEDKERNEL) || (i == IPC_ERR_EMSECURITY))
|
||||
ret = -EACCES;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_pmc_ipc_simple_command() - Simple IPC command
|
||||
* @cmd: IPC command code.
|
||||
* @sub: IPC command sub type.
|
||||
*
|
||||
* Send a simple IPC command to PMC when don't need to specify
|
||||
* input/output data and source/dest pointers.
|
||||
*
|
||||
* Return: an IPC error code or 0 on success.
|
||||
*/
|
||||
static int intel_pmc_ipc_simple_command(int cmd, int sub)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ipclock);
|
||||
if (ipcdev.dev == NULL) {
|
||||
mutex_unlock(&ipclock);
|
||||
return -ENODEV;
|
||||
}
|
||||
ipc_send_command(sub << IPC_CMD_SUBCMD | cmd);
|
||||
ret = intel_pmc_ipc_check_status();
|
||||
mutex_unlock(&ipclock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_pmc_ipc_raw_cmd() - IPC command with data and pointers
|
||||
* @cmd: IPC command code.
|
||||
* @sub: IPC command sub type.
|
||||
* @in: input data of this IPC command.
|
||||
* @inlen: input data length in bytes.
|
||||
* @out: output data of this IPC command.
|
||||
* @outlen: output data length in dwords.
|
||||
* @sptr: data writing to SPTR register.
|
||||
* @dptr: data writing to DPTR register.
|
||||
*
|
||||
* Send an IPC command to PMC with input/output data and source/dest pointers.
|
||||
*
|
||||
* Return: an IPC error code or 0 on success.
|
||||
*/
|
||||
static int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen, u32 *out,
|
||||
u32 outlen, u32 dptr, u32 sptr)
|
||||
{
|
||||
u32 wbuf[4] = { 0 };
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if (inlen > IPC_DATA_BUFFER_SIZE || outlen > IPC_DATA_BUFFER_SIZE / 4)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&ipclock);
|
||||
if (ipcdev.dev == NULL) {
|
||||
mutex_unlock(&ipclock);
|
||||
return -ENODEV;
|
||||
}
|
||||
memcpy(wbuf, in, inlen);
|
||||
writel(dptr, ipcdev.ipc_base + IPC_DPTR);
|
||||
writel(sptr, ipcdev.ipc_base + IPC_SPTR);
|
||||
/* The input data register is 32bit register and inlen is in Byte */
|
||||
for (i = 0; i < ((inlen + 3) / 4); i++)
|
||||
ipc_data_writel(wbuf[i], 4 * i);
|
||||
ipc_send_command((inlen << IPC_CMD_SIZE) |
|
||||
(sub << IPC_CMD_SUBCMD) | cmd);
|
||||
ret = intel_pmc_ipc_check_status();
|
||||
if (!ret) {
|
||||
/* out is read from 32bit register and outlen is in 32bit */
|
||||
for (i = 0; i < outlen; i++)
|
||||
*out++ = ipc_data_readl(4 * i);
|
||||
}
|
||||
mutex_unlock(&ipclock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_pmc_ipc_command() - IPC command with input/output data
|
||||
* @cmd: IPC command code.
|
||||
* @sub: IPC command sub type.
|
||||
* @in: input data of this IPC command.
|
||||
* @inlen: input data length in bytes.
|
||||
* @out: output data of this IPC command.
|
||||
* @outlen: output data length in dwords.
|
||||
*
|
||||
* Send an IPC command to PMC with input/output data.
|
||||
*
|
||||
* Return: an IPC error code or 0 on success.
|
||||
*/
|
||||
int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
|
||||
u32 *out, u32 outlen)
|
||||
{
|
||||
return intel_pmc_ipc_raw_cmd(cmd, sub, in, inlen, out, outlen, 0, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_pmc_ipc_command);
|
||||
|
||||
static irqreturn_t ioc(int irq, void *dev_id)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (ipcdev.irq_mode) {
|
||||
status = ipc_read_status();
|
||||
writel(status | IPC_STATUS_IRQ, ipcdev.ipc_base + IPC_STATUS);
|
||||
}
|
||||
complete(&ipcdev.cmd_complete);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
struct intel_pmc_ipc_dev *pmc = &ipcdev;
|
||||
int ret;
|
||||
|
||||
/* Only one PMC is supported */
|
||||
if (pmc->dev)
|
||||
return -EBUSY;
|
||||
|
||||
pmc->irq_mode = IPC_TRIGGER_MODE_IRQ;
|
||||
|
||||
spin_lock_init(&ipcdev.gcr_lock);
|
||||
|
||||
ret = pcim_enable_device(pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
init_completion(&pmc->cmd_complete);
|
||||
|
||||
pmc->ipc_base = pcim_iomap_table(pdev)[0];
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, pdev->irq, ioc, 0, "intel_pmc_ipc",
|
||||
pmc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to request irq\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
pmc->dev = &pdev->dev;
|
||||
|
||||
pci_set_drvdata(pdev, pmc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pci_device_id ipc_pci_ids[] = {
|
||||
{PCI_VDEVICE(INTEL, 0x0a94), 0},
|
||||
{PCI_VDEVICE(INTEL, 0x1a94), 0},
|
||||
{PCI_VDEVICE(INTEL, 0x5a94), 0},
|
||||
{ 0,}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, ipc_pci_ids);
|
||||
|
||||
static struct pci_driver ipc_pci_driver = {
|
||||
.name = "intel_pmc_ipc",
|
||||
.id_table = ipc_pci_ids,
|
||||
.probe = ipc_pci_probe,
|
||||
};
|
||||
|
||||
static ssize_t intel_pmc_ipc_simple_cmd_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int subcmd;
|
||||
int cmd;
|
||||
int ret;
|
||||
|
||||
ret = sscanf(buf, "%d %d", &cmd, &subcmd);
|
||||
if (ret != 2) {
|
||||
dev_err(dev, "Error args\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = intel_pmc_ipc_simple_command(cmd, subcmd);
|
||||
if (ret) {
|
||||
dev_err(dev, "command %d error with %d\n", cmd, ret);
|
||||
return ret;
|
||||
}
|
||||
return (ssize_t)count;
|
||||
}
|
||||
static DEVICE_ATTR(simplecmd, 0200, NULL, intel_pmc_ipc_simple_cmd_store);
|
||||
|
||||
static ssize_t intel_pmc_ipc_northpeak_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
unsigned long val;
|
||||
int subcmd;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul(buf, 0, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val)
|
||||
subcmd = 1;
|
||||
else
|
||||
subcmd = 0;
|
||||
ret = intel_pmc_ipc_simple_command(PMC_IPC_NORTHPEAK_CTRL, subcmd);
|
||||
if (ret) {
|
||||
dev_err(dev, "command north %d error with %d\n", subcmd, ret);
|
||||
return ret;
|
||||
}
|
||||
return (ssize_t)count;
|
||||
}
|
||||
static DEVICE_ATTR(northpeak, 0200, NULL, intel_pmc_ipc_northpeak_store);
|
||||
|
||||
static struct attribute *intel_ipc_attrs[] = {
|
||||
&dev_attr_northpeak.attr,
|
||||
&dev_attr_simplecmd.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group intel_ipc_group = {
|
||||
.attrs = intel_ipc_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *intel_ipc_groups[] = {
|
||||
&intel_ipc_group,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct resource punit_res_array[] = {
|
||||
/* Punit BIOS */
|
||||
{
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
{
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
/* Punit ISP */
|
||||
{
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
{
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
/* Punit GTD */
|
||||
{
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
{
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
};
|
||||
|
||||
#define TCO_RESOURCE_ACPI_IO 0
|
||||
#define TCO_RESOURCE_SMI_EN_IO 1
|
||||
#define TCO_RESOURCE_GCR_MEM 2
|
||||
static struct resource tco_res[] = {
|
||||
/* ACPI - TCO */
|
||||
{
|
||||
.flags = IORESOURCE_IO,
|
||||
},
|
||||
/* ACPI - SMI */
|
||||
{
|
||||
.flags = IORESOURCE_IO,
|
||||
},
|
||||
};
|
||||
|
||||
static struct itco_wdt_platform_data tco_info = {
|
||||
.name = "Apollo Lake SoC",
|
||||
.version = 5,
|
||||
.no_reboot_priv = &ipcdev,
|
||||
.update_no_reboot_bit = update_no_reboot_bit,
|
||||
};
|
||||
|
||||
#define TELEMETRY_RESOURCE_PUNIT_SSRAM 0
|
||||
#define TELEMETRY_RESOURCE_PMC_SSRAM 1
|
||||
static struct resource telemetry_res[] = {
|
||||
/*Telemetry*/
|
||||
{
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
{
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
};
|
||||
|
||||
static int ipc_create_punit_device(void)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
const struct platform_device_info pdevinfo = {
|
||||
.parent = ipcdev.dev,
|
||||
.name = PUNIT_DEVICE_NAME,
|
||||
.id = -1,
|
||||
.res = punit_res_array,
|
||||
.num_res = ipcdev.punit_res_count,
|
||||
};
|
||||
|
||||
pdev = platform_device_register_full(&pdevinfo);
|
||||
if (IS_ERR(pdev))
|
||||
return PTR_ERR(pdev);
|
||||
|
||||
ipcdev.punit_dev = pdev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipc_create_tco_device(void)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct resource *res;
|
||||
const struct platform_device_info pdevinfo = {
|
||||
.parent = ipcdev.dev,
|
||||
.name = TCO_DEVICE_NAME,
|
||||
.id = -1,
|
||||
.res = tco_res,
|
||||
.num_res = ARRAY_SIZE(tco_res),
|
||||
.data = &tco_info,
|
||||
.size_data = sizeof(tco_info),
|
||||
};
|
||||
|
||||
res = tco_res + TCO_RESOURCE_ACPI_IO;
|
||||
res->start = ipcdev.acpi_io_base + TCO_BASE_OFFSET;
|
||||
res->end = res->start + TCO_REGS_SIZE - 1;
|
||||
|
||||
res = tco_res + TCO_RESOURCE_SMI_EN_IO;
|
||||
res->start = ipcdev.acpi_io_base + SMI_EN_OFFSET;
|
||||
res->end = res->start + SMI_EN_SIZE - 1;
|
||||
|
||||
pdev = platform_device_register_full(&pdevinfo);
|
||||
if (IS_ERR(pdev))
|
||||
return PTR_ERR(pdev);
|
||||
|
||||
ipcdev.tco_dev = pdev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipc_create_telemetry_device(void)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct resource *res;
|
||||
const struct platform_device_info pdevinfo = {
|
||||
.parent = ipcdev.dev,
|
||||
.name = TELEMETRY_DEVICE_NAME,
|
||||
.id = -1,
|
||||
.res = telemetry_res,
|
||||
.num_res = ARRAY_SIZE(telemetry_res),
|
||||
};
|
||||
|
||||
res = telemetry_res + TELEMETRY_RESOURCE_PUNIT_SSRAM;
|
||||
res->start = ipcdev.telem_punit_ssram_base;
|
||||
res->end = res->start + ipcdev.telem_punit_ssram_size - 1;
|
||||
|
||||
res = telemetry_res + TELEMETRY_RESOURCE_PMC_SSRAM;
|
||||
res->start = ipcdev.telem_pmc_ssram_base;
|
||||
res->end = res->start + ipcdev.telem_pmc_ssram_size - 1;
|
||||
|
||||
pdev = platform_device_register_full(&pdevinfo);
|
||||
if (IS_ERR(pdev))
|
||||
return PTR_ERR(pdev);
|
||||
|
||||
ipcdev.telemetry_dev = pdev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipc_create_pmc_devices(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* If we have ACPI based watchdog use that instead */
|
||||
if (!acpi_has_watchdog()) {
|
||||
ret = ipc_create_tco_device();
|
||||
if (ret) {
|
||||
dev_err(ipcdev.dev, "Failed to add tco platform device\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = ipc_create_punit_device();
|
||||
if (ret) {
|
||||
dev_err(ipcdev.dev, "Failed to add punit platform device\n");
|
||||
platform_device_unregister(ipcdev.tco_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!ipcdev.telem_res_inval) {
|
||||
ret = ipc_create_telemetry_device();
|
||||
if (ret) {
|
||||
dev_warn(ipcdev.dev,
|
||||
"Failed to add telemetry platform device\n");
|
||||
platform_device_unregister(ipcdev.punit_dev);
|
||||
platform_device_unregister(ipcdev.tco_dev);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ipc_plat_get_res(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res, *punit_res = punit_res_array;
|
||||
void __iomem *addr;
|
||||
int size;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO,
|
||||
PLAT_RESOURCE_ACPI_IO_INDEX);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get io resource\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
size = resource_size(res);
|
||||
ipcdev.acpi_io_base = res->start;
|
||||
ipcdev.acpi_io_size = size;
|
||||
dev_info(&pdev->dev, "io res: %pR\n", res);
|
||||
|
||||
ipcdev.punit_res_count = 0;
|
||||
|
||||
/* This is index 0 to cover BIOS data register */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_BIOS_DATA_INDEX);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get res of punit BIOS data\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
punit_res[ipcdev.punit_res_count++] = *res;
|
||||
dev_info(&pdev->dev, "punit BIOS data res: %pR\n", res);
|
||||
|
||||
/* This is index 1 to cover BIOS interface register */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_BIOS_IFACE_INDEX);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get res of punit BIOS iface\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
punit_res[ipcdev.punit_res_count++] = *res;
|
||||
dev_info(&pdev->dev, "punit BIOS interface res: %pR\n", res);
|
||||
|
||||
/* This is index 2 to cover ISP data register, optional */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_ISP_DATA_INDEX);
|
||||
if (res) {
|
||||
punit_res[ipcdev.punit_res_count++] = *res;
|
||||
dev_info(&pdev->dev, "punit ISP data res: %pR\n", res);
|
||||
}
|
||||
|
||||
/* This is index 3 to cover ISP interface register, optional */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_ISP_IFACE_INDEX);
|
||||
if (res) {
|
||||
punit_res[ipcdev.punit_res_count++] = *res;
|
||||
dev_info(&pdev->dev, "punit ISP interface res: %pR\n", res);
|
||||
}
|
||||
|
||||
/* This is index 4 to cover GTD data register, optional */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_GTD_DATA_INDEX);
|
||||
if (res) {
|
||||
punit_res[ipcdev.punit_res_count++] = *res;
|
||||
dev_info(&pdev->dev, "punit GTD data res: %pR\n", res);
|
||||
}
|
||||
|
||||
/* This is index 5 to cover GTD interface register, optional */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_GTD_IFACE_INDEX);
|
||||
if (res) {
|
||||
punit_res[ipcdev.punit_res_count++] = *res;
|
||||
dev_info(&pdev->dev, "punit GTD interface res: %pR\n", res);
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_IPC_INDEX);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get ipc resource\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
size = PLAT_RESOURCE_IPC_SIZE + PLAT_RESOURCE_GCR_SIZE;
|
||||
res->end = res->start + size - 1;
|
||||
|
||||
addr = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(addr))
|
||||
return PTR_ERR(addr);
|
||||
|
||||
ipcdev.ipc_base = addr;
|
||||
|
||||
ipcdev.gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
|
||||
dev_info(&pdev->dev, "ipc res: %pR\n", res);
|
||||
|
||||
ipcdev.telem_res_inval = 0;
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM,
|
||||
PLAT_RESOURCE_TELEM_SSRAM_INDEX);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get telemetry ssram resource\n");
|
||||
ipcdev.telem_res_inval = 1;
|
||||
} else {
|
||||
ipcdev.telem_punit_ssram_base = res->start +
|
||||
TELEM_PUNIT_SSRAM_OFFSET;
|
||||
ipcdev.telem_punit_ssram_size = TELEM_SSRAM_SIZE;
|
||||
ipcdev.telem_pmc_ssram_base = res->start +
|
||||
TELEM_PMC_SSRAM_OFFSET;
|
||||
ipcdev.telem_pmc_ssram_size = TELEM_SSRAM_SIZE;
|
||||
dev_info(&pdev->dev, "telemetry ssram res: %pR\n", res);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_pmc_s0ix_counter_read() - Read S0ix residency.
|
||||
* @data: Out param that contains current S0ix residency count.
|
||||
*
|
||||
* Return: an error code or 0 on success.
|
||||
*/
|
||||
int intel_pmc_s0ix_counter_read(u64 *data)
|
||||
{
|
||||
u64 deep, shlw;
|
||||
|
||||
if (!ipcdev.has_gcr_regs)
|
||||
return -EACCES;
|
||||
|
||||
deep = gcr_data_readq(PMC_GCR_TELEM_DEEP_S0IX_REG);
|
||||
shlw = gcr_data_readq(PMC_GCR_TELEM_SHLW_S0IX_REG);
|
||||
|
||||
*data = S0IX_RESIDENCY_IN_USECS(deep, shlw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_pmc_s0ix_counter_read);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id ipc_acpi_ids[] = {
|
||||
{ "INT34D2", 0},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, ipc_acpi_ids);
|
||||
#endif
|
||||
|
||||
static int ipc_plat_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ipcdev.dev = &pdev->dev;
|
||||
ipcdev.irq_mode = IPC_TRIGGER_MODE_IRQ;
|
||||
init_completion(&ipcdev.cmd_complete);
|
||||
spin_lock_init(&ipcdev.gcr_lock);
|
||||
|
||||
ipcdev.irq = platform_get_irq(pdev, 0);
|
||||
if (ipcdev.irq < 0)
|
||||
return -EINVAL;
|
||||
|
||||
ret = ipc_plat_get_res(pdev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to request resource\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ipc_create_pmc_devices();
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to create pmc devices\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (devm_request_irq(&pdev->dev, ipcdev.irq, ioc, IRQF_NO_SUSPEND,
|
||||
"intel_pmc_ipc", &ipcdev)) {
|
||||
dev_err(&pdev->dev, "Failed to request irq\n");
|
||||
ret = -EBUSY;
|
||||
goto err_irq;
|
||||
}
|
||||
|
||||
ipcdev.has_gcr_regs = true;
|
||||
|
||||
return 0;
|
||||
|
||||
err_irq:
|
||||
platform_device_unregister(ipcdev.tco_dev);
|
||||
platform_device_unregister(ipcdev.punit_dev);
|
||||
platform_device_unregister(ipcdev.telemetry_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ipc_plat_remove(struct platform_device *pdev)
|
||||
{
|
||||
devm_free_irq(&pdev->dev, ipcdev.irq, &ipcdev);
|
||||
platform_device_unregister(ipcdev.tco_dev);
|
||||
platform_device_unregister(ipcdev.punit_dev);
|
||||
platform_device_unregister(ipcdev.telemetry_dev);
|
||||
ipcdev.dev = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ipc_plat_driver = {
|
||||
.remove = ipc_plat_remove,
|
||||
.probe = ipc_plat_probe,
|
||||
.driver = {
|
||||
.name = "pmc-ipc-plat",
|
||||
.acpi_match_table = ACPI_PTR(ipc_acpi_ids),
|
||||
.dev_groups = intel_ipc_groups,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init intel_pmc_ipc_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = platform_driver_register(&ipc_plat_driver);
|
||||
if (ret) {
|
||||
pr_err("Failed to register PMC ipc platform driver\n");
|
||||
return ret;
|
||||
}
|
||||
ret = pci_register_driver(&ipc_pci_driver);
|
||||
if (ret) {
|
||||
pr_err("Failed to register PMC ipc pci driver\n");
|
||||
platform_driver_unregister(&ipc_plat_driver);
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit intel_pmc_ipc_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&ipc_pci_driver);
|
||||
platform_driver_unregister(&ipc_plat_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>");
|
||||
MODULE_DESCRIPTION("Intel PMC IPC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
/* Some modules are dependent on this, so init earlier */
|
||||
fs_initcall(intel_pmc_ipc_init);
|
||||
module_exit(intel_pmc_ipc_exit);
|
@ -18,11 +18,10 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/sfi.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <asm/intel-mid.h>
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
/* IPC defines the following message types */
|
||||
@ -55,14 +54,14 @@
|
||||
#define IPC_IOC 0x100 /* IPC command register IOC bit */
|
||||
|
||||
struct intel_scu_ipc_dev {
|
||||
struct device *dev;
|
||||
struct device dev;
|
||||
struct resource mem;
|
||||
struct module *owner;
|
||||
int irq;
|
||||
void __iomem *ipc_base;
|
||||
struct completion cmd_complete;
|
||||
u8 irq_mode;
|
||||
};
|
||||
|
||||
static struct intel_scu_ipc_dev ipcdev; /* Only one for now */
|
||||
|
||||
#define IPC_STATUS 0x04
|
||||
#define IPC_STATUS_IRQ BIT(2)
|
||||
#define IPC_STATUS_ERR BIT(1)
|
||||
@ -78,8 +77,110 @@ static struct intel_scu_ipc_dev ipcdev; /* Only one for now */
|
||||
/* Timeout in jiffies */
|
||||
#define IPC_TIMEOUT (3 * HZ)
|
||||
|
||||
static struct intel_scu_ipc_dev *ipcdev; /* Only one for now */
|
||||
static DEFINE_MUTEX(ipclock); /* lock used to prevent multiple call to SCU */
|
||||
|
||||
static struct class intel_scu_ipc_class = {
|
||||
.name = "intel_scu_ipc",
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
/**
|
||||
* intel_scu_ipc_dev_get() - Get SCU IPC instance
|
||||
*
|
||||
* The recommended new API takes SCU IPC instance as parameter and this
|
||||
* function can be called by driver to get the instance. This also makes
|
||||
* sure the driver providing the IPC functionality cannot be unloaded
|
||||
* while the caller has the instance.
|
||||
*
|
||||
* Call intel_scu_ipc_dev_put() to release the instance.
|
||||
*
|
||||
* Returns %NULL if SCU IPC is not currently available.
|
||||
*/
|
||||
struct intel_scu_ipc_dev *intel_scu_ipc_dev_get(void)
|
||||
{
|
||||
struct intel_scu_ipc_dev *scu = NULL;
|
||||
|
||||
mutex_lock(&ipclock);
|
||||
if (ipcdev) {
|
||||
get_device(&ipcdev->dev);
|
||||
/*
|
||||
* Prevent the IPC provider from being unloaded while it
|
||||
* is being used.
|
||||
*/
|
||||
if (!try_module_get(ipcdev->owner))
|
||||
put_device(&ipcdev->dev);
|
||||
else
|
||||
scu = ipcdev;
|
||||
}
|
||||
|
||||
mutex_unlock(&ipclock);
|
||||
return scu;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_scu_ipc_dev_get);
|
||||
|
||||
/**
|
||||
* intel_scu_ipc_dev_put() - Put SCU IPC instance
|
||||
* @scu: SCU IPC instance
|
||||
*
|
||||
* This function releases the SCU IPC instance retrieved from
|
||||
* intel_scu_ipc_dev_get() and allows the driver providing IPC to be
|
||||
* unloaded.
|
||||
*/
|
||||
void intel_scu_ipc_dev_put(struct intel_scu_ipc_dev *scu)
|
||||
{
|
||||
if (scu) {
|
||||
module_put(scu->owner);
|
||||
put_device(&scu->dev);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_scu_ipc_dev_put);
|
||||
|
||||
struct intel_scu_ipc_devres {
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
};
|
||||
|
||||
static void devm_intel_scu_ipc_dev_release(struct device *dev, void *res)
|
||||
{
|
||||
struct intel_scu_ipc_devres *dr = res;
|
||||
struct intel_scu_ipc_dev *scu = dr->scu;
|
||||
|
||||
intel_scu_ipc_dev_put(scu);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_intel_scu_ipc_dev_get() - Allocate managed SCU IPC device
|
||||
* @dev: Device requesting the SCU IPC device
|
||||
*
|
||||
* The recommended new API takes SCU IPC instance as parameter and this
|
||||
* function can be called by driver to get the instance. This also makes
|
||||
* sure the driver providing the IPC functionality cannot be unloaded
|
||||
* while the caller has the instance.
|
||||
*
|
||||
* Returns %NULL if SCU IPC is not currently available.
|
||||
*/
|
||||
struct intel_scu_ipc_dev *devm_intel_scu_ipc_dev_get(struct device *dev)
|
||||
{
|
||||
struct intel_scu_ipc_devres *dr;
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
|
||||
dr = devres_alloc(devm_intel_scu_ipc_dev_release, sizeof(*dr), GFP_KERNEL);
|
||||
if (!dr)
|
||||
return NULL;
|
||||
|
||||
scu = intel_scu_ipc_dev_get();
|
||||
if (!scu) {
|
||||
devres_free(dr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dr->scu = scu;
|
||||
devres_add(dev, dr);
|
||||
|
||||
return scu;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_intel_scu_ipc_dev_get);
|
||||
|
||||
/*
|
||||
* Send ipc command
|
||||
* Command Register (Write Only):
|
||||
@ -143,7 +244,6 @@ static inline int busy_loop(struct intel_scu_ipc_dev *scu)
|
||||
usleep_range(50, 100);
|
||||
} while (time_before(jiffies, end));
|
||||
|
||||
dev_err(scu->dev, "IPC timed out");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
@ -152,10 +252,8 @@ static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (!wait_for_completion_timeout(&scu->cmd_complete, IPC_TIMEOUT)) {
|
||||
dev_err(scu->dev, "IPC timed out\n");
|
||||
if (!wait_for_completion_timeout(&scu->cmd_complete, IPC_TIMEOUT))
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
status = ipc_read_status(scu);
|
||||
if (status & IPC_STATUS_ERR)
|
||||
@ -166,13 +264,13 @@ static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu)
|
||||
|
||||
static int intel_scu_ipc_check_status(struct intel_scu_ipc_dev *scu)
|
||||
{
|
||||
return scu->irq_mode ? ipc_wait_for_interrupt(scu) : busy_loop(scu);
|
||||
return scu->irq > 0 ? ipc_wait_for_interrupt(scu) : busy_loop(scu);
|
||||
}
|
||||
|
||||
/* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */
|
||||
static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
|
||||
static int pwr_reg_rdwr(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data,
|
||||
u32 count, u32 op, u32 id)
|
||||
{
|
||||
struct intel_scu_ipc_dev *scu = &ipcdev;
|
||||
int nc;
|
||||
u32 offset = 0;
|
||||
int err;
|
||||
@ -182,8 +280,9 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
|
||||
memset(cbuf, 0, sizeof(cbuf));
|
||||
|
||||
mutex_lock(&ipclock);
|
||||
|
||||
if (scu->dev == NULL) {
|
||||
if (!scu)
|
||||
scu = ipcdev;
|
||||
if (!scu) {
|
||||
mutex_unlock(&ipclock);
|
||||
return -ENODEV;
|
||||
}
|
||||
@ -222,7 +321,8 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_scu_ipc_ioread8 - read a word via the SCU
|
||||
* intel_scu_ipc_dev_ioread8() - Read a byte via the SCU
|
||||
* @scu: Optional SCU IPC instance
|
||||
* @addr: Register on SCU
|
||||
* @data: Return pointer for read byte
|
||||
*
|
||||
@ -231,14 +331,15 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
|
||||
*
|
||||
* This function may sleep.
|
||||
*/
|
||||
int intel_scu_ipc_ioread8(u16 addr, u8 *data)
|
||||
int intel_scu_ipc_dev_ioread8(struct intel_scu_ipc_dev *scu, u16 addr, u8 *data)
|
||||
{
|
||||
return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
|
||||
return pwr_reg_rdwr(scu, &addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
|
||||
}
|
||||
EXPORT_SYMBOL(intel_scu_ipc_ioread8);
|
||||
EXPORT_SYMBOL(intel_scu_ipc_dev_ioread8);
|
||||
|
||||
/**
|
||||
* intel_scu_ipc_iowrite8 - write a byte via the SCU
|
||||
* intel_scu_ipc_dev_iowrite8() - Write a byte via the SCU
|
||||
* @scu: Optional SCU IPC instance
|
||||
* @addr: Register on SCU
|
||||
* @data: Byte to write
|
||||
*
|
||||
@ -247,14 +348,15 @@ EXPORT_SYMBOL(intel_scu_ipc_ioread8);
|
||||
*
|
||||
* This function may sleep.
|
||||
*/
|
||||
int intel_scu_ipc_iowrite8(u16 addr, u8 data)
|
||||
int intel_scu_ipc_dev_iowrite8(struct intel_scu_ipc_dev *scu, u16 addr, u8 data)
|
||||
{
|
||||
return pwr_reg_rdwr(&addr, &data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
|
||||
return pwr_reg_rdwr(scu, &addr, &data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
|
||||
}
|
||||
EXPORT_SYMBOL(intel_scu_ipc_iowrite8);
|
||||
EXPORT_SYMBOL(intel_scu_ipc_dev_iowrite8);
|
||||
|
||||
/**
|
||||
* intel_scu_ipc_readvv - read a set of registers
|
||||
* intel_scu_ipc_dev_readv() - Read a set of registers
|
||||
* @scu: Optional SCU IPC instance
|
||||
* @addr: Register list
|
||||
* @data: Bytes to return
|
||||
* @len: Length of array
|
||||
@ -266,14 +368,16 @@ EXPORT_SYMBOL(intel_scu_ipc_iowrite8);
|
||||
*
|
||||
* This function may sleep.
|
||||
*/
|
||||
int intel_scu_ipc_readv(u16 *addr, u8 *data, int len)
|
||||
int intel_scu_ipc_dev_readv(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data,
|
||||
size_t len)
|
||||
{
|
||||
return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
|
||||
return pwr_reg_rdwr(scu, addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_R);
|
||||
}
|
||||
EXPORT_SYMBOL(intel_scu_ipc_readv);
|
||||
EXPORT_SYMBOL(intel_scu_ipc_dev_readv);
|
||||
|
||||
/**
|
||||
* intel_scu_ipc_writev - write a set of registers
|
||||
* intel_scu_ipc_dev_writev() - Write a set of registers
|
||||
* @scu: Optional SCU IPC instance
|
||||
* @addr: Register list
|
||||
* @data: Bytes to write
|
||||
* @len: Length of array
|
||||
@ -285,16 +389,18 @@ EXPORT_SYMBOL(intel_scu_ipc_readv);
|
||||
*
|
||||
* This function may sleep.
|
||||
*/
|
||||
int intel_scu_ipc_writev(u16 *addr, u8 *data, int len)
|
||||
int intel_scu_ipc_dev_writev(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data,
|
||||
size_t len)
|
||||
{
|
||||
return pwr_reg_rdwr(addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
|
||||
return pwr_reg_rdwr(scu, addr, data, len, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_W);
|
||||
}
|
||||
EXPORT_SYMBOL(intel_scu_ipc_writev);
|
||||
EXPORT_SYMBOL(intel_scu_ipc_dev_writev);
|
||||
|
||||
/**
|
||||
* intel_scu_ipc_update_register - r/m/w a register
|
||||
* intel_scu_ipc_dev_update() - Update a register
|
||||
* @scu: Optional SCU IPC instance
|
||||
* @addr: Register address
|
||||
* @bits: Bits to update
|
||||
* @data: Bits to update
|
||||
* @mask: Mask of bits to update
|
||||
*
|
||||
* Read-modify-write power control unit register. The first data argument
|
||||
@ -305,15 +411,17 @@ EXPORT_SYMBOL(intel_scu_ipc_writev);
|
||||
* This function may sleep. Locking between SCU accesses is handled
|
||||
* for the caller.
|
||||
*/
|
||||
int intel_scu_ipc_update_register(u16 addr, u8 bits, u8 mask)
|
||||
int intel_scu_ipc_dev_update(struct intel_scu_ipc_dev *scu, u16 addr, u8 data,
|
||||
u8 mask)
|
||||
{
|
||||
u8 data[2] = { bits, mask };
|
||||
return pwr_reg_rdwr(&addr, data, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_M);
|
||||
u8 tmp[2] = { data, mask };
|
||||
return pwr_reg_rdwr(scu, &addr, tmp, 1, IPCMSG_PCNTRL, IPC_CMD_PCNTRL_M);
|
||||
}
|
||||
EXPORT_SYMBOL(intel_scu_ipc_update_register);
|
||||
EXPORT_SYMBOL(intel_scu_ipc_dev_update);
|
||||
|
||||
/**
|
||||
* intel_scu_ipc_simple_command - send a simple command
|
||||
* intel_scu_ipc_dev_simple_command() - Send a simple command
|
||||
* @scu: Optional SCU IPC instance
|
||||
* @cmd: Command
|
||||
* @sub: Sub type
|
||||
*
|
||||
@ -324,62 +432,89 @@ EXPORT_SYMBOL(intel_scu_ipc_update_register);
|
||||
* This function may sleep. Locking for SCU accesses is handled for the
|
||||
* caller.
|
||||
*/
|
||||
int intel_scu_ipc_simple_command(int cmd, int sub)
|
||||
int intel_scu_ipc_dev_simple_command(struct intel_scu_ipc_dev *scu, int cmd,
|
||||
int sub)
|
||||
{
|
||||
struct intel_scu_ipc_dev *scu = &ipcdev;
|
||||
u32 cmdval;
|
||||
int err;
|
||||
|
||||
mutex_lock(&ipclock);
|
||||
if (scu->dev == NULL) {
|
||||
if (!scu)
|
||||
scu = ipcdev;
|
||||
if (!scu) {
|
||||
mutex_unlock(&ipclock);
|
||||
return -ENODEV;
|
||||
}
|
||||
ipc_command(scu, sub << 12 | cmd);
|
||||
scu = ipcdev;
|
||||
cmdval = sub << 12 | cmd;
|
||||
ipc_command(scu, cmdval);
|
||||
err = intel_scu_ipc_check_status(scu);
|
||||
mutex_unlock(&ipclock);
|
||||
if (err)
|
||||
dev_err(&scu->dev, "IPC command %#x failed with %d\n", cmdval, err);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(intel_scu_ipc_simple_command);
|
||||
EXPORT_SYMBOL(intel_scu_ipc_dev_simple_command);
|
||||
|
||||
/**
|
||||
* intel_scu_ipc_command - command with data
|
||||
* intel_scu_ipc_command_with_size() - Command with data
|
||||
* @scu: Optional SCU IPC instance
|
||||
* @cmd: Command
|
||||
* @sub: Sub type
|
||||
* @in: Input data
|
||||
* @inlen: Input length in dwords
|
||||
* @inlen: Input length in bytes
|
||||
* @size: Input size written to the IPC command register in whatever
|
||||
* units (dword, byte) the particular firmware requires. Normally
|
||||
* should be the same as @inlen.
|
||||
* @out: Output data
|
||||
* @outlen: Output length in dwords
|
||||
* @outlen: Output length in bytes
|
||||
*
|
||||
* Issue a command to the SCU which involves data transfers. Do the
|
||||
* data copies under the lock but leave it for the caller to interpret.
|
||||
*/
|
||||
int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
|
||||
u32 *out, int outlen)
|
||||
int intel_scu_ipc_dev_command_with_size(struct intel_scu_ipc_dev *scu, int cmd,
|
||||
int sub, const void *in, size_t inlen,
|
||||
size_t size, void *out, size_t outlen)
|
||||
{
|
||||
struct intel_scu_ipc_dev *scu = &ipcdev;
|
||||
size_t outbuflen = DIV_ROUND_UP(outlen, sizeof(u32));
|
||||
size_t inbuflen = DIV_ROUND_UP(inlen, sizeof(u32));
|
||||
u32 cmdval, inbuf[4] = {};
|
||||
int i, err;
|
||||
|
||||
if (inbuflen > 4 || outbuflen > 4)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&ipclock);
|
||||
if (scu->dev == NULL) {
|
||||
if (!scu)
|
||||
scu = ipcdev;
|
||||
if (!scu) {
|
||||
mutex_unlock(&ipclock);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
for (i = 0; i < inlen; i++)
|
||||
ipc_data_writel(scu, *in++, 4 * i);
|
||||
memcpy(inbuf, in, inlen);
|
||||
for (i = 0; i < inbuflen; i++)
|
||||
ipc_data_writel(scu, inbuf[i], 4 * i);
|
||||
|
||||
ipc_command(scu, (inlen << 16) | (sub << 12) | cmd);
|
||||
cmdval = (size << 16) | (sub << 12) | cmd;
|
||||
ipc_command(scu, cmdval);
|
||||
err = intel_scu_ipc_check_status(scu);
|
||||
|
||||
if (!err) {
|
||||
for (i = 0; i < outlen; i++)
|
||||
*out++ = ipc_data_readl(scu, 4 * i);
|
||||
u32 outbuf[4] = {};
|
||||
|
||||
for (i = 0; i < outbuflen; i++)
|
||||
outbuf[i] = ipc_data_readl(scu, 4 * i);
|
||||
|
||||
memcpy(out, outbuf, outlen);
|
||||
}
|
||||
|
||||
mutex_unlock(&ipclock);
|
||||
if (err)
|
||||
dev_err(&scu->dev, "IPC command %#x failed with %d\n", cmdval, err);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(intel_scu_ipc_command);
|
||||
EXPORT_SYMBOL(intel_scu_ipc_dev_command_with_size);
|
||||
|
||||
/*
|
||||
* Interrupt handler gets called when ioc bit of IPC_COMMAND_REG set to 1
|
||||
@ -399,61 +534,179 @@ static irqreturn_t ioc(int irq, void *dev_id)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipc_probe - probe an Intel SCU IPC
|
||||
* @pdev: the PCI device matching
|
||||
* @id: entry in the match table
|
||||
*
|
||||
* Enable and install an intel SCU IPC. This appears in the PCI space
|
||||
* but uses some hard coded addresses as well.
|
||||
*/
|
||||
static int ipc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
static void intel_scu_ipc_release(struct device *dev)
|
||||
{
|
||||
int err;
|
||||
struct intel_scu_ipc_dev *scu = &ipcdev;
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
|
||||
if (scu->dev) /* We support only one SCU */
|
||||
return -EBUSY;
|
||||
|
||||
err = pcim_enable_device(pdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
init_completion(&scu->cmd_complete);
|
||||
|
||||
scu->ipc_base = pcim_iomap_table(pdev)[0];
|
||||
|
||||
err = devm_request_irq(&pdev->dev, pdev->irq, ioc, 0, "intel_scu_ipc",
|
||||
scu);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Assign device at last */
|
||||
scu->dev = &pdev->dev;
|
||||
|
||||
intel_scu_devices_create();
|
||||
|
||||
pci_set_drvdata(pdev, scu);
|
||||
return 0;
|
||||
scu = container_of(dev, struct intel_scu_ipc_dev, dev);
|
||||
if (scu->irq > 0)
|
||||
free_irq(scu->irq, scu);
|
||||
iounmap(scu->ipc_base);
|
||||
release_mem_region(scu->mem.start, resource_size(&scu->mem));
|
||||
kfree(scu);
|
||||
}
|
||||
|
||||
static const struct pci_device_id pci_ids[] = {
|
||||
{ PCI_VDEVICE(INTEL, 0x080e) },
|
||||
{ PCI_VDEVICE(INTEL, 0x08ea) },
|
||||
{ PCI_VDEVICE(INTEL, 0x11a0) },
|
||||
{}
|
||||
};
|
||||
/**
|
||||
* __intel_scu_ipc_register() - Register SCU IPC device
|
||||
* @parent: Parent device
|
||||
* @scu_data: Data used to configure SCU IPC
|
||||
* @owner: Module registering the SCU IPC device
|
||||
*
|
||||
* Call this function to register SCU IPC mechanism under @parent.
|
||||
* Returns pointer to the new SCU IPC device or ERR_PTR() in case of
|
||||
* failure. The caller may use the returned instance if it needs to do
|
||||
* SCU IPC calls itself.
|
||||
*/
|
||||
struct intel_scu_ipc_dev *
|
||||
__intel_scu_ipc_register(struct device *parent,
|
||||
const struct intel_scu_ipc_data *scu_data,
|
||||
struct module *owner)
|
||||
{
|
||||
int err;
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
void __iomem *ipc_base;
|
||||
|
||||
static struct pci_driver ipc_driver = {
|
||||
.driver = {
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.name = "intel_scu_ipc",
|
||||
.id_table = pci_ids,
|
||||
.probe = ipc_probe,
|
||||
};
|
||||
builtin_pci_driver(ipc_driver);
|
||||
mutex_lock(&ipclock);
|
||||
/* We support only one IPC */
|
||||
if (ipcdev) {
|
||||
err = -EBUSY;
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
scu = kzalloc(sizeof(*scu), GFP_KERNEL);
|
||||
if (!scu) {
|
||||
err = -ENOMEM;
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
scu->owner = owner;
|
||||
scu->dev.parent = parent;
|
||||
scu->dev.class = &intel_scu_ipc_class;
|
||||
scu->dev.release = intel_scu_ipc_release;
|
||||
dev_set_name(&scu->dev, "intel_scu_ipc");
|
||||
|
||||
if (!request_mem_region(scu_data->mem.start, resource_size(&scu_data->mem),
|
||||
"intel_scu_ipc")) {
|
||||
err = -EBUSY;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
ipc_base = ioremap(scu_data->mem.start, resource_size(&scu_data->mem));
|
||||
if (!ipc_base) {
|
||||
err = -ENOMEM;
|
||||
goto err_release;
|
||||
}
|
||||
|
||||
scu->ipc_base = ipc_base;
|
||||
scu->mem = scu_data->mem;
|
||||
scu->irq = scu_data->irq;
|
||||
init_completion(&scu->cmd_complete);
|
||||
|
||||
if (scu->irq > 0) {
|
||||
err = request_irq(scu->irq, ioc, 0, "intel_scu_ipc", scu);
|
||||
if (err)
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
/*
|
||||
* After this point intel_scu_ipc_release() takes care of
|
||||
* releasing the SCU IPC resources once refcount drops to zero.
|
||||
*/
|
||||
err = device_register(&scu->dev);
|
||||
if (err) {
|
||||
put_device(&scu->dev);
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
/* Assign device at last */
|
||||
ipcdev = scu;
|
||||
mutex_unlock(&ipclock);
|
||||
|
||||
return scu;
|
||||
|
||||
err_unmap:
|
||||
iounmap(ipc_base);
|
||||
err_release:
|
||||
release_mem_region(scu_data->mem.start, resource_size(&scu_data->mem));
|
||||
err_free:
|
||||
kfree(scu);
|
||||
err_unlock:
|
||||
mutex_unlock(&ipclock);
|
||||
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__intel_scu_ipc_register);
|
||||
|
||||
/**
|
||||
* intel_scu_ipc_unregister() - Unregister SCU IPC
|
||||
* @scu: SCU IPC handle
|
||||
*
|
||||
* This unregisters the SCU IPC device and releases the acquired
|
||||
* resources once the refcount goes to zero.
|
||||
*/
|
||||
void intel_scu_ipc_unregister(struct intel_scu_ipc_dev *scu)
|
||||
{
|
||||
mutex_lock(&ipclock);
|
||||
if (!WARN_ON(!ipcdev)) {
|
||||
ipcdev = NULL;
|
||||
device_unregister(&scu->dev);
|
||||
}
|
||||
mutex_unlock(&ipclock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(intel_scu_ipc_unregister);
|
||||
|
||||
static void devm_intel_scu_ipc_unregister(struct device *dev, void *res)
|
||||
{
|
||||
struct intel_scu_ipc_devres *dr = res;
|
||||
struct intel_scu_ipc_dev *scu = dr->scu;
|
||||
|
||||
intel_scu_ipc_unregister(scu);
|
||||
}
|
||||
|
||||
/**
|
||||
* __devm_intel_scu_ipc_register() - Register managed SCU IPC device
|
||||
* @parent: Parent device
|
||||
* @scu_data: Data used to configure SCU IPC
|
||||
* @owner: Module registering the SCU IPC device
|
||||
*
|
||||
* Call this function to register managed SCU IPC mechanism under
|
||||
* @parent. Returns pointer to the new SCU IPC device or ERR_PTR() in
|
||||
* case of failure. The caller may use the returned instance if it needs
|
||||
* to do SCU IPC calls itself.
|
||||
*/
|
||||
struct intel_scu_ipc_dev *
|
||||
__devm_intel_scu_ipc_register(struct device *parent,
|
||||
const struct intel_scu_ipc_data *scu_data,
|
||||
struct module *owner)
|
||||
{
|
||||
struct intel_scu_ipc_devres *dr;
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
|
||||
dr = devres_alloc(devm_intel_scu_ipc_unregister, sizeof(*dr), GFP_KERNEL);
|
||||
if (!dr)
|
||||
return NULL;
|
||||
|
||||
scu = __intel_scu_ipc_register(parent, scu_data, owner);
|
||||
if (IS_ERR(scu)) {
|
||||
devres_free(dr);
|
||||
return scu;
|
||||
}
|
||||
|
||||
dr->scu = scu;
|
||||
devres_add(parent, dr);
|
||||
|
||||
return scu;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__devm_intel_scu_ipc_register);
|
||||
|
||||
static int __init intel_scu_ipc_init(void)
|
||||
{
|
||||
return class_register(&intel_scu_ipc_class);
|
||||
}
|
||||
subsys_initcall(intel_scu_ipc_init);
|
||||
|
||||
static void __exit intel_scu_ipc_exit(void)
|
||||
{
|
||||
class_unregister(&intel_scu_ipc_class);
|
||||
}
|
||||
module_exit(intel_scu_ipc_exit);
|
||||
|
@ -22,6 +22,9 @@
|
||||
|
||||
static int major;
|
||||
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
static DEFINE_MUTEX(scu_lock);
|
||||
|
||||
/* IOCTL commands */
|
||||
#define INTE_SCU_IPC_REGISTER_READ 0
|
||||
#define INTE_SCU_IPC_REGISTER_WRITE 1
|
||||
@ -52,12 +55,12 @@ static int scu_reg_access(u32 cmd, struct scu_ipc_data *data)
|
||||
|
||||
switch (cmd) {
|
||||
case INTE_SCU_IPC_REGISTER_READ:
|
||||
return intel_scu_ipc_readv(data->addr, data->data, count);
|
||||
return intel_scu_ipc_dev_readv(scu, data->addr, data->data, count);
|
||||
case INTE_SCU_IPC_REGISTER_WRITE:
|
||||
return intel_scu_ipc_writev(data->addr, data->data, count);
|
||||
return intel_scu_ipc_dev_writev(scu, data->addr, data->data, count);
|
||||
case INTE_SCU_IPC_REGISTER_UPDATE:
|
||||
return intel_scu_ipc_update_register(data->addr[0],
|
||||
data->data[0], data->mask);
|
||||
return intel_scu_ipc_dev_update(scu, data->addr[0], data->data[0],
|
||||
data->mask);
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
@ -91,8 +94,40 @@ static long scu_ipc_ioctl(struct file *fp, unsigned int cmd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scu_ipc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* Only single open at the time */
|
||||
mutex_lock(&scu_lock);
|
||||
if (scu) {
|
||||
ret = -EBUSY;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
scu = intel_scu_ipc_dev_get();
|
||||
if (!scu)
|
||||
ret = -ENODEV;
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&scu_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scu_ipc_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
mutex_lock(&scu_lock);
|
||||
intel_scu_ipc_dev_put(scu);
|
||||
scu = NULL;
|
||||
mutex_unlock(&scu_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations scu_ipc_fops = {
|
||||
.unlocked_ioctl = scu_ipc_ioctl,
|
||||
.open = scu_ipc_open,
|
||||
.release = scu_ipc_release,
|
||||
};
|
||||
|
||||
static int __init ipc_module_init(void)
|
||||
|
68
drivers/platform/x86/intel_scu_pcidrv.c
Normal file
68
drivers/platform/x86/intel_scu_pcidrv.c
Normal file
@ -0,0 +1,68 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* PCI driver for the Intel SCU.
|
||||
*
|
||||
* Copyright (C) 2008-2010, 2015, 2020 Intel Corporation
|
||||
* Authors: Sreedhara DS (sreedhara.ds@intel.com)
|
||||
* Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include <asm/intel-mid.h>
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
static int intel_scu_pci_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
void (*setup_fn)(void) = (void (*)(void))id->driver_data;
|
||||
struct intel_scu_ipc_data scu_data = {};
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
int ret;
|
||||
|
||||
ret = pcim_enable_device(pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
scu_data.mem = pdev->resource[0];
|
||||
scu_data.irq = pdev->irq;
|
||||
|
||||
scu = intel_scu_ipc_register(&pdev->dev, &scu_data);
|
||||
if (IS_ERR(scu))
|
||||
return PTR_ERR(scu);
|
||||
|
||||
if (setup_fn)
|
||||
setup_fn();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void intel_mid_scu_setup(void)
|
||||
{
|
||||
intel_scu_devices_create();
|
||||
}
|
||||
|
||||
static const struct pci_device_id pci_ids[] = {
|
||||
{ PCI_VDEVICE(INTEL, 0x080e),
|
||||
.driver_data = (kernel_ulong_t)intel_mid_scu_setup },
|
||||
{ PCI_VDEVICE(INTEL, 0x08ea),
|
||||
.driver_data = (kernel_ulong_t)intel_mid_scu_setup },
|
||||
{ PCI_VDEVICE(INTEL, 0x0a94) },
|
||||
{ PCI_VDEVICE(INTEL, 0x11a0),
|
||||
.driver_data = (kernel_ulong_t)intel_mid_scu_setup },
|
||||
{ PCI_VDEVICE(INTEL, 0x1a94) },
|
||||
{ PCI_VDEVICE(INTEL, 0x5a94) },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct pci_driver intel_scu_pci_driver = {
|
||||
.driver = {
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.name = "intel_scu",
|
||||
.id_table = pci_ids,
|
||||
.probe = intel_scu_pci_probe,
|
||||
};
|
||||
|
||||
builtin_pci_driver(intel_scu_pci_driver);
|
@ -353,21 +353,16 @@ int telemetry_clear_pltdata(void)
|
||||
EXPORT_SYMBOL_GPL(telemetry_clear_pltdata);
|
||||
|
||||
/**
|
||||
* telemetry_pltconfig_valid() - Checkif platform config is valid
|
||||
* telemetry_get_pltdata() - Return telemetry platform config
|
||||
*
|
||||
* Usage by other than telemetry module is invalid
|
||||
*
|
||||
* Return: 0 success, < 0 for failure
|
||||
* May be used by other telemetry modules to get platform specific
|
||||
* configuration.
|
||||
*/
|
||||
int telemetry_pltconfig_valid(void)
|
||||
struct telemetry_plt_config *telemetry_get_pltdata(void)
|
||||
{
|
||||
if (telm_core_conf.plt_config)
|
||||
return 0;
|
||||
|
||||
else
|
||||
return -EINVAL;
|
||||
return telm_core_conf.plt_config;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(telemetry_pltconfig_valid);
|
||||
EXPORT_SYMBOL_GPL(telemetry_get_pltdata);
|
||||
|
||||
static inline int telemetry_get_pssevtname(enum telemetry_unit telem_unit,
|
||||
const char **name, int len)
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mfd/intel_pmc_bxt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/seq_file.h>
|
||||
@ -22,7 +23,6 @@
|
||||
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/intel-family.h>
|
||||
#include <asm/intel_pmc_ipc.h>
|
||||
#include <asm/intel_telemetry.h>
|
||||
|
||||
#define DRIVER_NAME "telemetry_soc_debugfs"
|
||||
@ -647,10 +647,11 @@ DEFINE_SHOW_ATTRIBUTE(telem_soc_states);
|
||||
|
||||
static int telem_s0ix_res_get(void *data, u64 *val)
|
||||
{
|
||||
struct telemetry_plt_config *plt_config = telemetry_get_pltdata();
|
||||
u64 s0ix_total_res;
|
||||
int ret;
|
||||
|
||||
ret = intel_pmc_s0ix_counter_read(&s0ix_total_res);
|
||||
ret = intel_pmc_s0ix_counter_read(plt_config->pmc, &s0ix_total_res);
|
||||
if (ret) {
|
||||
pr_err("Failed to read S0ix residency");
|
||||
return ret;
|
||||
@ -837,12 +838,15 @@ static int pm_suspend_exit_cb(void)
|
||||
*/
|
||||
if (suspend_shlw_ctr_exit == suspend_shlw_ctr_temp &&
|
||||
suspend_deep_ctr_exit == suspend_deep_ctr_temp) {
|
||||
ret = intel_pmc_gcr_read64(PMC_GCR_TELEM_SHLW_S0IX_REG,
|
||||
struct telemetry_plt_config *plt_config = telemetry_get_pltdata();
|
||||
struct intel_pmc_dev *pmc = plt_config->pmc;
|
||||
|
||||
ret = intel_pmc_gcr_read64(pmc, PMC_GCR_TELEM_SHLW_S0IX_REG,
|
||||
&suspend_shlw_res_exit);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = intel_pmc_gcr_read64(PMC_GCR_TELEM_DEEP_S0IX_REG,
|
||||
ret = intel_pmc_gcr_read64(pmc, PMC_GCR_TELEM_DEEP_S0IX_REG,
|
||||
&suspend_deep_res_exit);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
@ -910,8 +914,7 @@ static int __init telemetry_debugfs_init(void)
|
||||
|
||||
debugfs_conf = (struct telemetry_debugfs_conf *)id->driver_data;
|
||||
|
||||
err = telemetry_pltconfig_valid();
|
||||
if (err < 0) {
|
||||
if (!telemetry_get_pltdata()) {
|
||||
pr_info("Invalid pltconfig, ensure IPC1 device is enabled in BIOS\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
@ -15,7 +15,6 @@
|
||||
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/intel-family.h>
|
||||
#include <asm/intel_pmc_ipc.h>
|
||||
#include <asm/intel_punit_ipc.h>
|
||||
#include <asm/intel_telemetry.h>
|
||||
|
||||
@ -35,6 +34,7 @@
|
||||
#define TELEM_SSRAM_STARTTIME_OFFSET 8
|
||||
#define TELEM_SSRAM_EVTLOG_OFFSET 16
|
||||
|
||||
#define IOSS_TELEM 0xeb
|
||||
#define IOSS_TELEM_EVENT_READ 0x0
|
||||
#define IOSS_TELEM_EVENT_WRITE 0x1
|
||||
#define IOSS_TELEM_INFO_READ 0x2
|
||||
@ -42,9 +42,6 @@
|
||||
#define IOSS_TELEM_TRACE_CTL_WRITE 0x6
|
||||
#define IOSS_TELEM_EVENT_CTL_READ 0x7
|
||||
#define IOSS_TELEM_EVENT_CTL_WRITE 0x8
|
||||
#define IOSS_TELEM_EVT_CTRL_WRITE_SIZE 0x4
|
||||
#define IOSS_TELEM_READ_WORD 0x1
|
||||
#define IOSS_TELEM_WRITE_FOURBYTES 0x4
|
||||
#define IOSS_TELEM_EVT_WRITE_SIZE 0x3
|
||||
|
||||
#define TELEM_INFO_SRAMEVTS_MASK 0xFF00
|
||||
@ -250,17 +247,14 @@ static int telemetry_check_evtid(enum telemetry_unit telem_unit,
|
||||
static inline int telemetry_plt_config_ioss_event(u32 evt_id, int index)
|
||||
{
|
||||
u32 write_buf;
|
||||
int ret;
|
||||
|
||||
write_buf = evt_id | TELEM_EVENT_ENABLE;
|
||||
write_buf <<= BITS_PER_BYTE;
|
||||
write_buf |= index;
|
||||
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
|
||||
IOSS_TELEM_EVENT_WRITE, (u8 *)&write_buf,
|
||||
IOSS_TELEM_EVT_WRITE_SIZE, NULL, 0);
|
||||
|
||||
return ret;
|
||||
return intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM,
|
||||
IOSS_TELEM_EVENT_WRITE, &write_buf,
|
||||
IOSS_TELEM_EVT_WRITE_SIZE, NULL, 0);
|
||||
}
|
||||
|
||||
static inline int telemetry_plt_config_pss_event(u32 evt_id, int index)
|
||||
@ -278,6 +272,7 @@ static inline int telemetry_plt_config_pss_event(u32 evt_id, int index)
|
||||
static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
|
||||
enum telemetry_action action)
|
||||
{
|
||||
struct intel_scu_ipc_dev *scu = telm_conf->scu;
|
||||
u8 num_ioss_evts, ioss_period;
|
||||
int ret, index, idx;
|
||||
u32 *ioss_evtmap;
|
||||
@ -288,9 +283,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
|
||||
ioss_evtmap = evtconfig.evtmap;
|
||||
|
||||
/* Get telemetry EVENT CTL */
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
|
||||
ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
|
||||
IOSS_TELEM_EVENT_CTL_READ, NULL, 0,
|
||||
&telem_ctrl, IOSS_TELEM_READ_WORD);
|
||||
&telem_ctrl, sizeof(telem_ctrl));
|
||||
if (ret) {
|
||||
pr_err("IOSS TELEM_CTRL Read Failed\n");
|
||||
return ret;
|
||||
@ -299,11 +294,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
|
||||
/* Disable Telemetry */
|
||||
TELEM_DISABLE(telem_ctrl);
|
||||
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
|
||||
IOSS_TELEM_EVENT_CTL_WRITE,
|
||||
(u8 *)&telem_ctrl,
|
||||
IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
|
||||
NULL, 0);
|
||||
ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
|
||||
IOSS_TELEM_EVENT_CTL_WRITE, &telem_ctrl,
|
||||
sizeof(telem_ctrl), NULL, 0);
|
||||
if (ret) {
|
||||
pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
|
||||
return ret;
|
||||
@ -315,10 +308,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
|
||||
/* Clear All Events */
|
||||
TELEM_CLEAR_EVENTS(telem_ctrl);
|
||||
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
|
||||
ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
|
||||
IOSS_TELEM_EVENT_CTL_WRITE,
|
||||
(u8 *)&telem_ctrl,
|
||||
IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
|
||||
&telem_ctrl, sizeof(telem_ctrl),
|
||||
NULL, 0);
|
||||
if (ret) {
|
||||
pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
|
||||
@ -344,10 +336,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
|
||||
/* Clear All Events */
|
||||
TELEM_CLEAR_EVENTS(telem_ctrl);
|
||||
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
|
||||
ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
|
||||
IOSS_TELEM_EVENT_CTL_WRITE,
|
||||
(u8 *)&telem_ctrl,
|
||||
IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
|
||||
&telem_ctrl, sizeof(telem_ctrl),
|
||||
NULL, 0);
|
||||
if (ret) {
|
||||
pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
|
||||
@ -396,10 +387,9 @@ static int telemetry_setup_iossevtconfig(struct telemetry_evtconfig evtconfig,
|
||||
TELEM_ENABLE_PERIODIC(telem_ctrl);
|
||||
telem_ctrl |= ioss_period;
|
||||
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
|
||||
ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
|
||||
IOSS_TELEM_EVENT_CTL_WRITE,
|
||||
(u8 *)&telem_ctrl,
|
||||
IOSS_TELEM_EVT_CTRL_WRITE_SIZE, NULL, 0);
|
||||
&telem_ctrl, sizeof(telem_ctrl), NULL, 0);
|
||||
if (ret) {
|
||||
pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n");
|
||||
return ret;
|
||||
@ -586,8 +576,9 @@ static int telemetry_setup(struct platform_device *pdev)
|
||||
u32 read_buf, events, event_regs;
|
||||
int ret;
|
||||
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY, IOSS_TELEM_INFO_READ,
|
||||
NULL, 0, &read_buf, IOSS_TELEM_READ_WORD);
|
||||
ret = intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM,
|
||||
IOSS_TELEM_INFO_READ, NULL, 0,
|
||||
&read_buf, sizeof(read_buf));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "IOSS TELEM_INFO Read Failed\n");
|
||||
return ret;
|
||||
@ -681,6 +672,8 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
|
||||
|
||||
mutex_lock(&(telm_conf->telem_lock));
|
||||
if (ioss_period) {
|
||||
struct intel_scu_ipc_dev *scu = telm_conf->scu;
|
||||
|
||||
if (TELEM_SAMPLE_PERIOD_INVALID(ioss_period)) {
|
||||
pr_err("IOSS Sampling Period Out of Range\n");
|
||||
ret = -EINVAL;
|
||||
@ -688,9 +681,9 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
|
||||
}
|
||||
|
||||
/* Get telemetry EVENT CTL */
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
|
||||
ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
|
||||
IOSS_TELEM_EVENT_CTL_READ, NULL, 0,
|
||||
&telem_ctrl, IOSS_TELEM_READ_WORD);
|
||||
&telem_ctrl, sizeof(telem_ctrl));
|
||||
if (ret) {
|
||||
pr_err("IOSS TELEM_CTRL Read Failed\n");
|
||||
goto out;
|
||||
@ -699,11 +692,10 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
|
||||
/* Disable Telemetry */
|
||||
TELEM_DISABLE(telem_ctrl);
|
||||
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
|
||||
IOSS_TELEM_EVENT_CTL_WRITE,
|
||||
(u8 *)&telem_ctrl,
|
||||
IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
|
||||
NULL, 0);
|
||||
ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
|
||||
IOSS_TELEM_EVENT_CTL_WRITE,
|
||||
&telem_ctrl, sizeof(telem_ctrl),
|
||||
NULL, 0);
|
||||
if (ret) {
|
||||
pr_err("IOSS TELEM_CTRL Event Disable Write Failed\n");
|
||||
goto out;
|
||||
@ -715,11 +707,10 @@ static int telemetry_plt_set_sampling_period(u8 pss_period, u8 ioss_period)
|
||||
TELEM_ENABLE_PERIODIC(telem_ctrl);
|
||||
telem_ctrl |= ioss_period;
|
||||
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
|
||||
IOSS_TELEM_EVENT_CTL_WRITE,
|
||||
(u8 *)&telem_ctrl,
|
||||
IOSS_TELEM_EVT_CTRL_WRITE_SIZE,
|
||||
NULL, 0);
|
||||
ret = intel_scu_ipc_dev_command(scu, IOSS_TELEM,
|
||||
IOSS_TELEM_EVENT_CTL_WRITE,
|
||||
&telem_ctrl, sizeof(telem_ctrl),
|
||||
NULL, 0);
|
||||
if (ret) {
|
||||
pr_err("IOSS TELEM_CTRL Event Enable Write Failed\n");
|
||||
goto out;
|
||||
@ -1014,9 +1005,9 @@ static int telemetry_plt_get_trace_verbosity(enum telemetry_unit telem_unit,
|
||||
break;
|
||||
|
||||
case TELEM_IOSS:
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
|
||||
IOSS_TELEM_TRACE_CTL_READ, NULL, 0, &temp,
|
||||
IOSS_TELEM_READ_WORD);
|
||||
ret = intel_scu_ipc_dev_command(telm_conf->scu,
|
||||
IOSS_TELEM, IOSS_TELEM_TRACE_CTL_READ,
|
||||
NULL, 0, &temp, sizeof(temp));
|
||||
if (ret) {
|
||||
pr_err("IOSS TRACE_CTL Read Failed\n");
|
||||
goto out;
|
||||
@ -1068,9 +1059,9 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
|
||||
break;
|
||||
|
||||
case TELEM_IOSS:
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
|
||||
IOSS_TELEM_TRACE_CTL_READ, NULL, 0, &temp,
|
||||
IOSS_TELEM_READ_WORD);
|
||||
ret = intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM,
|
||||
IOSS_TELEM_TRACE_CTL_READ,
|
||||
NULL, 0, &temp, sizeof(temp));
|
||||
if (ret) {
|
||||
pr_err("IOSS TRACE_CTL Read Failed\n");
|
||||
goto out;
|
||||
@ -1079,9 +1070,9 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
|
||||
TELEM_CLEAR_VERBOSITY_BITS(temp);
|
||||
TELEM_SET_VERBOSITY_BITS(temp, verbosity);
|
||||
|
||||
ret = intel_pmc_ipc_command(PMC_IPC_PMC_TELEMTRY,
|
||||
IOSS_TELEM_TRACE_CTL_WRITE, (u8 *)&temp,
|
||||
IOSS_TELEM_WRITE_FOURBYTES, NULL, 0);
|
||||
ret = intel_scu_ipc_dev_command(telm_conf->scu, IOSS_TELEM,
|
||||
IOSS_TELEM_TRACE_CTL_WRITE,
|
||||
&temp, sizeof(temp), NULL, 0);
|
||||
if (ret) {
|
||||
pr_err("IOSS TRACE_CTL Verbosity Set Failed\n");
|
||||
goto out;
|
||||
@ -1124,6 +1115,8 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev)
|
||||
|
||||
telm_conf = (struct telemetry_plt_config *)id->driver_data;
|
||||
|
||||
telm_conf->pmc = dev_get_drvdata(pdev->dev.parent);
|
||||
|
||||
mem = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(mem))
|
||||
return PTR_ERR(mem);
|
||||
@ -1136,6 +1129,12 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev)
|
||||
|
||||
telm_conf->ioss_config.regmap = mem;
|
||||
|
||||
telm_conf->scu = devm_intel_scu_ipc_dev_get(&pdev->dev);
|
||||
if (!telm_conf->scu) {
|
||||
ret = -EPROBE_DEFER;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mutex_init(&telm_conf->telem_lock);
|
||||
mutex_init(&telm_conf->telem_trace_lock);
|
||||
|
||||
|
@ -11,7 +11,7 @@ config TYPEC_MUX_PI3USB30532
|
||||
|
||||
config TYPEC_MUX_INTEL_PMC
|
||||
tristate "Intel PMC mux control"
|
||||
depends on INTEL_PMC_IPC
|
||||
depends on INTEL_SCU_IPC
|
||||
select USB_ROLE_SWITCH
|
||||
help
|
||||
Driver for USB muxes controlled by Intel PMC FW. Intel PMC FW can
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include <linux/usb/typec_dp.h>
|
||||
#include <linux/usb/typec_tbt.h>
|
||||
|
||||
#include <asm/intel_pmc_ipc.h>
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
#define PMC_USBC_CMD 0xa7
|
||||
|
||||
@ -96,6 +96,7 @@ struct pmc_usb_port {
|
||||
struct pmc_usb {
|
||||
u8 num_ports;
|
||||
struct device *dev;
|
||||
struct intel_scu_ipc_dev *ipc;
|
||||
struct pmc_usb_port *port;
|
||||
};
|
||||
|
||||
@ -107,9 +108,8 @@ static int pmc_usb_command(struct pmc_usb_port *port, u8 *msg, u32 len)
|
||||
* Error bit will always be 0 with the USBC command.
|
||||
* Status can be checked from the response message.
|
||||
*/
|
||||
intel_pmc_ipc_command(PMC_USBC_CMD, 0, msg, len,
|
||||
(void *)response, 1);
|
||||
|
||||
intel_scu_ipc_dev_command(port->pmc->ipc, PMC_USBC_CMD, 0, msg, len,
|
||||
response, sizeof(response));
|
||||
if (response[2]) {
|
||||
if (response[2] & BIT(1))
|
||||
return -EIO;
|
||||
@ -370,6 +370,10 @@ static int pmc_usb_probe(struct platform_device *pdev)
|
||||
if (!pmc->port)
|
||||
return -ENOMEM;
|
||||
|
||||
pmc->ipc = devm_intel_scu_ipc_dev_get(&pdev->dev);
|
||||
if (!pmc->ipc)
|
||||
return -ENODEV;
|
||||
|
||||
pmc->dev = &pdev->dev;
|
||||
|
||||
/*
|
||||
|
@ -41,8 +41,8 @@ config TYPEC_FUSB302
|
||||
config TYPEC_WCOVE
|
||||
tristate "Intel WhiskeyCove PMIC USB Type-C PHY driver"
|
||||
depends on ACPI
|
||||
depends on MFD_INTEL_PMC_BXT
|
||||
depends on INTEL_SOC_PMIC
|
||||
depends on INTEL_PMC_IPC
|
||||
depends on BXT_WC_PMIC_OPREGION
|
||||
help
|
||||
This driver adds support for USB Type-C on Intel Broxton platforms
|
||||
|
@ -64,6 +64,7 @@
|
||||
#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
|
||||
#include <linux/io.h> /* For inb/outb/... */
|
||||
#include <linux/platform_data/itco_wdt.h>
|
||||
#include <linux/mfd/intel_pmc_bxt.h>
|
||||
|
||||
#include "iTCO_vendor.h"
|
||||
|
||||
@ -233,12 +234,24 @@ static int update_no_reboot_bit_cnt(void *priv, bool set)
|
||||
return val != newval ? -EIO : 0;
|
||||
}
|
||||
|
||||
static void iTCO_wdt_no_reboot_bit_setup(struct iTCO_wdt_private *p,
|
||||
struct itco_wdt_platform_data *pdata)
|
||||
static int update_no_reboot_bit_pmc(void *priv, bool set)
|
||||
{
|
||||
if (pdata->update_no_reboot_bit) {
|
||||
p->update_no_reboot_bit = pdata->update_no_reboot_bit;
|
||||
p->no_reboot_priv = pdata->no_reboot_priv;
|
||||
struct intel_pmc_dev *pmc = priv;
|
||||
u32 bits = PMC_CFG_NO_REBOOT_EN;
|
||||
u32 value = set ? bits : 0;
|
||||
|
||||
return intel_pmc_gcr_update(pmc, PMC_GCR_PMC_CFG_REG, bits, value);
|
||||
}
|
||||
|
||||
static void iTCO_wdt_no_reboot_bit_setup(struct iTCO_wdt_private *p,
|
||||
struct platform_device *pdev,
|
||||
struct itco_wdt_platform_data *pdata)
|
||||
{
|
||||
if (pdata->no_reboot_use_pmc) {
|
||||
struct intel_pmc_dev *pmc = dev_get_drvdata(pdev->dev.parent);
|
||||
|
||||
p->update_no_reboot_bit = update_no_reboot_bit_pmc;
|
||||
p->no_reboot_priv = pmc;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -478,14 +491,14 @@ static int iTCO_wdt_probe(struct platform_device *pdev)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
iTCO_wdt_no_reboot_bit_setup(p, pdata);
|
||||
iTCO_wdt_no_reboot_bit_setup(p, pdev, pdata);
|
||||
|
||||
/*
|
||||
* Get the Memory-Mapped GCS or PMC register, we need it for the
|
||||
* NO_REBOOT flag (TCO v2 and v3).
|
||||
*/
|
||||
if (p->iTCO_version >= 2 && p->iTCO_version < 6 &&
|
||||
!pdata->update_no_reboot_bit) {
|
||||
!pdata->no_reboot_use_pmc) {
|
||||
p->gcs_pmc_res = platform_get_resource(pdev,
|
||||
IORESOURCE_MEM,
|
||||
ICH_RES_MEM_GCS_PMC);
|
||||
|
@ -33,14 +33,24 @@ enum {
|
||||
SCU_WATCHDOG_KEEPALIVE,
|
||||
};
|
||||
|
||||
static inline int wdt_command(int sub, u32 *in, int inlen)
|
||||
struct mid_wdt {
|
||||
struct watchdog_device wd;
|
||||
struct device *dev;
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
};
|
||||
|
||||
static inline int
|
||||
wdt_command(struct mid_wdt *mid, int sub, const void *in, size_t inlen, size_t size)
|
||||
{
|
||||
return intel_scu_ipc_command(IPC_WATCHDOG, sub, in, inlen, NULL, 0);
|
||||
struct intel_scu_ipc_dev *scu = mid->scu;
|
||||
|
||||
return intel_scu_ipc_dev_command_with_size(scu, IPC_WATCHDOG, sub, in,
|
||||
inlen, size, NULL, 0);
|
||||
}
|
||||
|
||||
static int wdt_start(struct watchdog_device *wd)
|
||||
{
|
||||
struct device *dev = watchdog_get_drvdata(wd);
|
||||
struct mid_wdt *mid = watchdog_get_drvdata(wd);
|
||||
int ret, in_size;
|
||||
int timeout = wd->timeout;
|
||||
struct ipc_wd_start {
|
||||
@ -49,38 +59,41 @@ static int wdt_start(struct watchdog_device *wd)
|
||||
} ipc_wd_start = { timeout - MID_WDT_PRETIMEOUT, timeout };
|
||||
|
||||
/*
|
||||
* SCU expects the input size for watchdog IPC to
|
||||
* be based on 4 bytes
|
||||
* SCU expects the input size for watchdog IPC to be 2 which is the
|
||||
* size of the structure in dwords. SCU IPC normally takes bytes
|
||||
* but this is a special case where we specify size to be different
|
||||
* than inlen.
|
||||
*/
|
||||
in_size = DIV_ROUND_UP(sizeof(ipc_wd_start), 4);
|
||||
|
||||
ret = wdt_command(SCU_WATCHDOG_START, (u32 *)&ipc_wd_start, in_size);
|
||||
ret = wdt_command(mid, SCU_WATCHDOG_START, &ipc_wd_start,
|
||||
sizeof(ipc_wd_start), in_size);
|
||||
if (ret)
|
||||
dev_crit(dev, "error starting watchdog: %d\n", ret);
|
||||
dev_crit(mid->dev, "error starting watchdog: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wdt_ping(struct watchdog_device *wd)
|
||||
{
|
||||
struct device *dev = watchdog_get_drvdata(wd);
|
||||
struct mid_wdt *mid = watchdog_get_drvdata(wd);
|
||||
int ret;
|
||||
|
||||
ret = wdt_command(SCU_WATCHDOG_KEEPALIVE, NULL, 0);
|
||||
ret = wdt_command(mid, SCU_WATCHDOG_KEEPALIVE, NULL, 0, 0);
|
||||
if (ret)
|
||||
dev_crit(dev, "Error executing keepalive: %d\n", ret);
|
||||
dev_crit(mid->dev, "Error executing keepalive: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wdt_stop(struct watchdog_device *wd)
|
||||
{
|
||||
struct device *dev = watchdog_get_drvdata(wd);
|
||||
struct mid_wdt *mid = watchdog_get_drvdata(wd);
|
||||
int ret;
|
||||
|
||||
ret = wdt_command(SCU_WATCHDOG_STOP, NULL, 0);
|
||||
ret = wdt_command(mid, SCU_WATCHDOG_STOP, NULL, 0, 0);
|
||||
if (ret)
|
||||
dev_crit(dev, "Error stopping watchdog: %d\n", ret);
|
||||
dev_crit(mid->dev, "Error stopping watchdog: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -110,6 +123,7 @@ static int mid_wdt_probe(struct platform_device *pdev)
|
||||
struct device *dev = &pdev->dev;
|
||||
struct watchdog_device *wdt_dev;
|
||||
struct intel_mid_wdt_pdata *pdata = dev->platform_data;
|
||||
struct mid_wdt *mid;
|
||||
int ret;
|
||||
|
||||
if (!pdata) {
|
||||
@ -123,10 +137,13 @@ static int mid_wdt_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
wdt_dev = devm_kzalloc(dev, sizeof(*wdt_dev), GFP_KERNEL);
|
||||
if (!wdt_dev)
|
||||
mid = devm_kzalloc(dev, sizeof(*mid), GFP_KERNEL);
|
||||
if (!mid)
|
||||
return -ENOMEM;
|
||||
|
||||
mid->dev = dev;
|
||||
wdt_dev = &mid->wd;
|
||||
|
||||
wdt_dev->info = &mid_wdt_info;
|
||||
wdt_dev->ops = &mid_wdt_ops;
|
||||
wdt_dev->min_timeout = MID_WDT_TIMEOUT_MIN;
|
||||
@ -135,7 +152,7 @@ static int mid_wdt_probe(struct platform_device *pdev)
|
||||
wdt_dev->parent = dev;
|
||||
|
||||
watchdog_set_nowayout(wdt_dev, WATCHDOG_NOWAYOUT);
|
||||
watchdog_set_drvdata(wdt_dev, dev);
|
||||
watchdog_set_drvdata(wdt_dev, mid);
|
||||
|
||||
ret = devm_request_irq(dev, pdata->irq, mid_wdt_irq,
|
||||
IRQF_SHARED | IRQF_NO_SUSPEND, "watchdog",
|
||||
@ -145,6 +162,10 @@ static int mid_wdt_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
mid->scu = devm_intel_scu_ipc_dev_get(dev);
|
||||
if (!mid->scu)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
/*
|
||||
* The firmware followed by U-Boot leaves the watchdog running
|
||||
* with the default threshold which may vary. When we get here
|
||||
|
53
include/linux/mfd/intel_pmc_bxt.h
Normal file
53
include/linux/mfd/intel_pmc_bxt.h
Normal file
@ -0,0 +1,53 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef MFD_INTEL_PMC_BXT_H
|
||||
#define MFD_INTEL_PMC_BXT_H
|
||||
|
||||
/* GCR reg offsets from GCR base */
|
||||
#define PMC_GCR_PMC_CFG_REG 0x08
|
||||
#define PMC_GCR_TELEM_DEEP_S0IX_REG 0x78
|
||||
#define PMC_GCR_TELEM_SHLW_S0IX_REG 0x80
|
||||
|
||||
/* PMC_CFG_REG bit masks */
|
||||
#define PMC_CFG_NO_REBOOT_EN BIT(4)
|
||||
|
||||
/**
|
||||
* struct intel_pmc_dev - Intel PMC device structure
|
||||
* @dev: Pointer to the parent PMC device
|
||||
* @scu: Pointer to the SCU IPC device data structure
|
||||
* @gcr_mem_base: Virtual base address of GCR (Global Configuration Registers)
|
||||
* @gcr_lock: Lock used to serialize access to GCR registers
|
||||
* @telem_base: Pointer to telemetry SSRAM base resource or %NULL if not
|
||||
* available
|
||||
*/
|
||||
struct intel_pmc_dev {
|
||||
struct device *dev;
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
void __iomem *gcr_mem_base;
|
||||
spinlock_t gcr_lock;
|
||||
struct resource *telem_base;
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_MFD_INTEL_PMC_BXT)
|
||||
int intel_pmc_gcr_read64(struct intel_pmc_dev *pmc, u32 offset, u64 *data);
|
||||
int intel_pmc_gcr_update(struct intel_pmc_dev *pmc, u32 offset, u32 mask, u32 val);
|
||||
int intel_pmc_s0ix_counter_read(struct intel_pmc_dev *pmc, u64 *data);
|
||||
#else
|
||||
static inline int intel_pmc_gcr_read64(struct intel_pmc_dev *pmc, u32 offset,
|
||||
u64 *data)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static inline int intel_pmc_gcr_update(struct intel_pmc_dev *pmc, u32 offset,
|
||||
u32 mask, u32 val)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static inline int intel_pmc_s0ix_counter_read(struct intel_pmc_dev *pmc, u64 *data)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* MFD_INTEL_PMC_BXT_H */
|
@ -13,6 +13,20 @@
|
||||
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/**
|
||||
* struct intel_soc_pmic - Intel SoC PMIC data
|
||||
* @irq: Master interrupt number of the parent PMIC device
|
||||
* @regmap: Pointer to the parent PMIC device regmap structure
|
||||
* @irq_chip_data: IRQ chip data for the PMIC itself
|
||||
* @irq_chip_data_pwrbtn: Chained IRQ chip data for the Power Button
|
||||
* @irq_chip_data_tmu: Chained IRQ chip data for the Time Management Unit
|
||||
* @irq_chip_data_bcu: Chained IRQ chip data for the Burst Control Unit
|
||||
* @irq_chip_data_adc: Chained IRQ chip data for the General Purpose ADC
|
||||
* @irq_chip_data_chgr: Chained IRQ chip data for the External Charger
|
||||
* @irq_chip_data_crit: Chained IRQ chip data for the Critical Event Handler
|
||||
* @dev: Pointer to the parent PMIC device
|
||||
* @scu: Pointer to the SCU IPC device data structure
|
||||
*/
|
||||
struct intel_soc_pmic {
|
||||
int irq;
|
||||
struct regmap *regmap;
|
||||
@ -24,6 +38,7 @@ struct intel_soc_pmic {
|
||||
struct regmap_irq_chip_data *irq_chip_data_chgr;
|
||||
struct regmap_irq_chip_data *irq_chip_data_crit;
|
||||
struct device *dev;
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
};
|
||||
|
||||
int intel_soc_pmic_exec_mipi_pmic_seq_element(u16 i2c_address, u32 reg_address,
|
||||
|
@ -12,13 +12,16 @@
|
||||
#define ICH_RES_MEM_OFF 2
|
||||
#define ICH_RES_MEM_GCS_PMC 0
|
||||
|
||||
/**
|
||||
* struct itco_wdt_platform_data - iTCO_wdt platform data
|
||||
* @name: Name of the platform
|
||||
* @version: iTCO version
|
||||
* @no_reboot_use_pmc: Use PMC BXT API to set and clear NO_REBOOT bit
|
||||
*/
|
||||
struct itco_wdt_platform_data {
|
||||
char name[32];
|
||||
unsigned int version;
|
||||
/* private data to be passed to update_no_reboot_bit API */
|
||||
void *no_reboot_priv;
|
||||
/* pointer for platform specific no reboot update function */
|
||||
int (*update_no_reboot_bit)(void *priv, bool set);
|
||||
bool no_reboot_use_pmc;
|
||||
};
|
||||
|
||||
#endif /* _ITCO_WDT_H_ */
|
||||
|
Loading…
Reference in New Issue
Block a user