summaryrefslogtreecommitdiffstats
path: root/drivers/platform/x86/intel_pmc_ipc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform/x86/intel_pmc_ipc.c')
-rw-r--r--drivers/platform/x86/intel_pmc_ipc.c156
1 files changed, 136 insertions, 20 deletions
diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c
index 0651d47b8eeb..e4d4dfe3e1d1 100644
--- a/drivers/platform/x86/intel_pmc_ipc.c
+++ b/drivers/platform/x86/intel_pmc_ipc.c
@@ -57,10 +57,6 @@
#define IPC_WRITE_BUFFER 0x80
#define IPC_READ_BUFFER 0x90
-/* PMC Global Control Registers */
-#define GCR_TELEM_DEEP_S0IX_OFFSET 0x1078
-#define GCR_TELEM_SHLW_S0IX_OFFSET 0x1080
-
/* Residency with clock rate at 19.2MHz to usecs */
#define S0IX_RESIDENCY_IN_USECS(d, s) \
({ \
@@ -82,7 +78,7 @@
/* exported resources from IFWI */
#define PLAT_RESOURCE_IPC_INDEX 0
#define PLAT_RESOURCE_IPC_SIZE 0x1000
-#define PLAT_RESOURCE_GCR_OFFSET 0x1008
+#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
@@ -112,6 +108,13 @@
#define TCO_PMC_OFFSET 0x8
#define TCO_PMC_SIZE 0x4
+/* PMC register bit definitions */
+
+/* PMC_CFG_REG bit masks */
+#define PMC_CFG_NO_REBOOT_MASK (1 << 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;
@@ -126,8 +129,7 @@ static struct intel_pmc_ipc_dev {
struct platform_device *tco_dev;
/* gcr */
- resource_size_t gcr_base;
- int gcr_size;
+ void __iomem *gcr_mem_base;
bool has_gcr_regs;
/* punit */
@@ -196,7 +198,128 @@ static inline u32 ipc_data_readl(u32 offset)
static inline u64 gcr_data_readq(u32 offset)
{
- return readq(ipcdev.ipc_base + 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_read() - Read PMC GCR register
+ * @offset: offset of GCR register from GCR address base
+ * @data: data pointer for storing the register output
+ *
+ * Reads the PMC GCR register of given offset.
+ *
+ * Return: negative value on error or 0 on success.
+ */
+int intel_pmc_gcr_read(u32 offset, u32 *data)
+{
+ int ret;
+
+ mutex_lock(&ipclock);
+
+ ret = is_gcr_valid(offset);
+ if (ret < 0) {
+ mutex_unlock(&ipclock);
+ return ret;
+ }
+
+ *data = readl(ipcdev.gcr_mem_base + offset);
+
+ mutex_unlock(&ipclock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(intel_pmc_gcr_read);
+
+/**
+ * intel_pmc_gcr_write() - Write PMC GCR register
+ * @offset: offset of GCR register from GCR address base
+ * @data: register update value
+ *
+ * Writes the PMC GCR register of given offset with given
+ * value.
+ *
+ * Return: negative value on error or 0 on success.
+ */
+int intel_pmc_gcr_write(u32 offset, u32 data)
+{
+ int ret;
+
+ mutex_lock(&ipclock);
+
+ ret = is_gcr_valid(offset);
+ if (ret < 0) {
+ mutex_unlock(&ipclock);
+ return ret;
+ }
+
+ writel(data, ipcdev.gcr_mem_base + offset);
+
+ mutex_unlock(&ipclock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(intel_pmc_gcr_write);
+
+/**
+ * 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.
+ */
+int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val)
+{
+ u32 new_val;
+ int ret = 0;
+
+ mutex_lock(&ipclock);
+
+ 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:
+ mutex_unlock(&ipclock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(intel_pmc_gcr_update);
+
+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)
@@ -516,15 +639,13 @@ static struct resource tco_res[] = {
{
.flags = IORESOURCE_IO,
},
- /* GCS */
- {
- .flags = IORESOURCE_MEM,
- },
};
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
@@ -581,10 +702,6 @@ static int ipc_create_tco_device(void)
res->start = ipcdev.acpi_io_base + SMI_EN_OFFSET;
res->end = res->start + SMI_EN_SIZE - 1;
- res = tco_res + TCO_RESOURCE_GCR_MEM;
- res->start = ipcdev.gcr_base + TCO_PMC_OFFSET;
- res->end = res->start + TCO_PMC_SIZE - 1;
-
pdev = platform_device_register_full(&pdevinfo);
if (IS_ERR(pdev))
return PTR_ERR(pdev);
@@ -746,8 +863,7 @@ static int ipc_plat_get_res(struct platform_device *pdev)
}
ipcdev.ipc_base = addr;
- ipcdev.gcr_base = res->start + PLAT_RESOURCE_GCR_OFFSET;
- ipcdev.gcr_size = PLAT_RESOURCE_GCR_SIZE;
+ ipcdev.gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
dev_info(&pdev->dev, "ipc res: %pR\n", res);
ipcdev.telem_res_inval = 0;
@@ -782,8 +898,8 @@ int intel_pmc_s0ix_counter_read(u64 *data)
if (!ipcdev.has_gcr_regs)
return -EACCES;
- deep = gcr_data_readq(GCR_TELEM_DEEP_S0IX_OFFSET);
- shlw = gcr_data_readq(GCR_TELEM_SHLW_S0IX_OFFSET);
+ 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);