1 // SPDX-License-Identifier: GPL-2.0
3 * Driver for the Intel Broxton PMC
5 * (C) Copyright 2014 - 2020 Intel Corporation
7 * This driver is based on Intel SCU IPC driver (intel_scu_ipc.c) by
8 * Sreedhara DS <sreedhara.ds@intel.com>
10 * The PMC (Power Management Controller) running on the ARC processor
11 * communicates with another entity running in the IA (Intel Architecture)
12 * core through an IPC (Intel Processor Communications) mechanism which in
13 * turn sends messages between the IA and the PMC.
16 #include <linux/acpi.h>
17 #include <linux/delay.h>
18 #include <linux/errno.h>
19 #include <linux/interrupt.h>
20 #include <linux/io-64-nonatomic-lo-hi.h>
21 #include <linux/mfd/core.h>
22 #include <linux/mfd/intel_pmc_bxt.h>
23 #include <linux/module.h>
24 #include <linux/platform_device.h>
25 #include <linux/platform_data/itco_wdt.h>
26 #include <linux/platform_data/x86/intel_scu_ipc.h>
28 /* Residency with clock rate at 19.2MHz to usecs */
29 #define S0IX_RESIDENCY_IN_USECS(d, s) \
31 u64 result = 10ull * ((d) + (s)); \
32 do_div(result, 192); \
36 /* Resources exported from IFWI */
37 #define PLAT_RESOURCE_IPC_INDEX 0
38 #define PLAT_RESOURCE_IPC_SIZE 0x1000
39 #define PLAT_RESOURCE_GCR_OFFSET 0x1000
40 #define PLAT_RESOURCE_GCR_SIZE 0x1000
41 #define PLAT_RESOURCE_BIOS_DATA_INDEX 1
42 #define PLAT_RESOURCE_BIOS_IFACE_INDEX 2
43 #define PLAT_RESOURCE_TELEM_SSRAM_INDEX 3
44 #define PLAT_RESOURCE_ISP_DATA_INDEX 4
45 #define PLAT_RESOURCE_ISP_IFACE_INDEX 5
46 #define PLAT_RESOURCE_GTD_DATA_INDEX 6
47 #define PLAT_RESOURCE_GTD_IFACE_INDEX 7
48 #define PLAT_RESOURCE_ACPI_IO_INDEX 0
51 * BIOS does not create an ACPI device for each PMC function, but
52 * exports multiple resources from one ACPI device (IPC) for multiple
53 * functions. This driver is responsible for creating a child device and
54 * to export resources for those functions.
56 #define SMI_EN_OFFSET 0x0040
58 #define TCO_BASE_OFFSET 0x0060
59 #define TCO_REGS_SIZE 16
60 #define TELEM_SSRAM_SIZE 240
61 #define TELEM_PMC_SSRAM_OFFSET 0x1b00
62 #define TELEM_PUNIT_SSRAM_OFFSET 0x1a00
65 #define PMC_NORTHPEAK_CTRL 0xed
67 static inline bool is_gcr_valid(u32 offset
)
69 return offset
< PLAT_RESOURCE_GCR_SIZE
- 8;
73 * intel_pmc_gcr_read64() - Read a 64-bit PMC GCR register
74 * @pmc: PMC device pointer
75 * @offset: offset of GCR register from GCR address base
76 * @data: data pointer for storing the register output
78 * Reads the 64-bit PMC GCR register at given offset.
80 * Return: Negative value on error or 0 on success.
82 int intel_pmc_gcr_read64(struct intel_pmc_dev
*pmc
, u32 offset
, u64
*data
)
84 if (!is_gcr_valid(offset
))
87 spin_lock(&pmc
->gcr_lock
);
88 *data
= readq(pmc
->gcr_mem_base
+ offset
);
89 spin_unlock(&pmc
->gcr_lock
);
93 EXPORT_SYMBOL_GPL(intel_pmc_gcr_read64
);
96 * intel_pmc_gcr_update() - Update PMC GCR register bits
97 * @pmc: PMC device pointer
98 * @offset: offset of GCR register from GCR address base
99 * @mask: bit mask for update operation
102 * Updates the bits of given GCR register as specified by
105 * Return: Negative value on error or 0 on success.
107 int intel_pmc_gcr_update(struct intel_pmc_dev
*pmc
, u32 offset
, u32 mask
, u32 val
)
111 if (!is_gcr_valid(offset
))
114 spin_lock(&pmc
->gcr_lock
);
115 new_val
= readl(pmc
->gcr_mem_base
+ offset
);
117 new_val
= (new_val
& ~mask
) | (val
& mask
);
118 writel(new_val
, pmc
->gcr_mem_base
+ offset
);
120 new_val
= readl(pmc
->gcr_mem_base
+ offset
);
121 spin_unlock(&pmc
->gcr_lock
);
123 /* Check whether the bit update is successful */
124 return (new_val
& mask
) != (val
& mask
) ? -EIO
: 0;
126 EXPORT_SYMBOL_GPL(intel_pmc_gcr_update
);
129 * intel_pmc_s0ix_counter_read() - Read S0ix residency
130 * @pmc: PMC device pointer
131 * @data: Out param that contains current S0ix residency count.
133 * Writes to @data how many usecs the system has been in low-power S0ix
136 * Return: An error code or 0 on success.
138 int intel_pmc_s0ix_counter_read(struct intel_pmc_dev
*pmc
, u64
*data
)
142 spin_lock(&pmc
->gcr_lock
);
143 deep
= readq(pmc
->gcr_mem_base
+ PMC_GCR_TELEM_DEEP_S0IX_REG
);
144 shlw
= readq(pmc
->gcr_mem_base
+ PMC_GCR_TELEM_SHLW_S0IX_REG
);
145 spin_unlock(&pmc
->gcr_lock
);
147 *data
= S0IX_RESIDENCY_IN_USECS(deep
, shlw
);
150 EXPORT_SYMBOL_GPL(intel_pmc_s0ix_counter_read
);
153 * simplecmd_store() - Send a simple IPC command
154 * @dev: Device under the attribute is
155 * @attr: Attribute in question
156 * @buf: Buffer holding data to be stored to the attribute
157 * @count: Number of bytes in @buf
159 * Expects a string with two integers separated with space. These two
160 * values hold command and subcommand that is send to PMC.
162 * Return: Number number of bytes written (@count) or negative errno in
165 static ssize_t
simplecmd_store(struct device
*dev
, struct device_attribute
*attr
,
166 const char *buf
, size_t count
)
168 struct intel_pmc_dev
*pmc
= dev_get_drvdata(dev
);
169 struct intel_scu_ipc_dev
*scu
= pmc
->scu
;
174 ret
= sscanf(buf
, "%d %d", &cmd
, &subcmd
);
176 dev_err(dev
, "Invalid values, expected: cmd subcmd\n");
180 ret
= intel_scu_ipc_dev_simple_command(scu
, cmd
, subcmd
);
186 static DEVICE_ATTR_WO(simplecmd
);
189 * northpeak_store() - Enable or disable Northpeak
190 * @dev: Device under the attribute is
191 * @attr: Attribute in question
192 * @buf: Buffer holding data to be stored to the attribute
193 * @count: Number of bytes in @buf
195 * Expects an unsigned integer. Non-zero enables Northpeak and zero
198 * Return: Number number of bytes written (@count) or negative errno in
201 static ssize_t
northpeak_store(struct device
*dev
, struct device_attribute
*attr
,
202 const char *buf
, size_t count
)
204 struct intel_pmc_dev
*pmc
= dev_get_drvdata(dev
);
205 struct intel_scu_ipc_dev
*scu
= pmc
->scu
;
210 ret
= kstrtoul(buf
, 0, &val
);
214 /* Northpeak is enabled if subcmd == 1 and disabled if it is 0 */
220 ret
= intel_scu_ipc_dev_simple_command(scu
, PMC_NORTHPEAK_CTRL
, subcmd
);
226 static DEVICE_ATTR_WO(northpeak
);
228 static struct attribute
*intel_pmc_attrs
[] = {
229 &dev_attr_northpeak
.attr
,
230 &dev_attr_simplecmd
.attr
,
234 static const struct attribute_group intel_pmc_group
= {
235 .attrs
= intel_pmc_attrs
,
238 static const struct attribute_group
*intel_pmc_groups
[] = {
243 static struct resource punit_res
[6];
245 static struct mfd_cell punit
= {
246 .name
= "intel_punit_ipc",
247 .resources
= punit_res
,
250 static struct itco_wdt_platform_data tco_pdata
= {
251 .name
= "Apollo Lake SoC",
253 .no_reboot_use_pmc
= true,
256 static struct resource tco_res
[2];
258 static const struct mfd_cell tco
= {
260 .ignore_resource_conflicts
= true,
261 .resources
= tco_res
,
262 .num_resources
= ARRAY_SIZE(tco_res
),
263 .platform_data
= &tco_pdata
,
264 .pdata_size
= sizeof(tco_pdata
),
267 static const struct resource telem_res
[] = {
268 DEFINE_RES_MEM(TELEM_PUNIT_SSRAM_OFFSET
, TELEM_SSRAM_SIZE
),
269 DEFINE_RES_MEM(TELEM_PMC_SSRAM_OFFSET
, TELEM_SSRAM_SIZE
),
272 static const struct mfd_cell telem
= {
273 .name
= "intel_telemetry",
274 .resources
= telem_res
,
275 .num_resources
= ARRAY_SIZE(telem_res
),
278 static int intel_pmc_get_tco_resources(struct platform_device
*pdev
)
280 struct resource
*res
;
282 if (acpi_has_watchdog())
285 res
= platform_get_resource(pdev
, IORESOURCE_IO
,
286 PLAT_RESOURCE_ACPI_IO_INDEX
);
288 dev_err(&pdev
->dev
, "Failed to get IO resource\n");
292 tco_res
[0].flags
= IORESOURCE_IO
;
293 tco_res
[0].start
= res
->start
+ TCO_BASE_OFFSET
;
294 tco_res
[0].end
= tco_res
[0].start
+ TCO_REGS_SIZE
- 1;
295 tco_res
[1].flags
= IORESOURCE_IO
;
296 tco_res
[1].start
= res
->start
+ SMI_EN_OFFSET
;
297 tco_res
[1].end
= tco_res
[1].start
+ SMI_EN_SIZE
- 1;
302 static int intel_pmc_get_resources(struct platform_device
*pdev
,
303 struct intel_pmc_dev
*pmc
,
304 struct intel_scu_ipc_data
*scu_data
)
306 struct resource gcr_res
;
307 size_t npunit_res
= 0;
308 struct resource
*res
;
311 scu_data
->irq
= platform_get_irq_optional(pdev
, 0);
313 res
= platform_get_resource(pdev
, IORESOURCE_MEM
,
314 PLAT_RESOURCE_IPC_INDEX
);
316 dev_err(&pdev
->dev
, "Failed to get IPC resource\n");
321 scu_data
->mem
.flags
= res
->flags
;
322 scu_data
->mem
.start
= res
->start
;
323 scu_data
->mem
.end
= res
->start
+ PLAT_RESOURCE_IPC_SIZE
- 1;
326 gcr_res
.flags
= res
->flags
;
327 gcr_res
.start
= res
->start
+ PLAT_RESOURCE_GCR_OFFSET
;
328 gcr_res
.end
= gcr_res
.start
+ PLAT_RESOURCE_GCR_SIZE
- 1;
330 pmc
->gcr_mem_base
= devm_ioremap_resource(&pdev
->dev
, &gcr_res
);
331 if (IS_ERR(pmc
->gcr_mem_base
))
332 return PTR_ERR(pmc
->gcr_mem_base
);
334 /* Only register iTCO watchdog if there is no WDAT ACPI table */
335 ret
= intel_pmc_get_tco_resources(pdev
);
339 /* BIOS data register */
340 res
= platform_get_resource(pdev
, IORESOURCE_MEM
,
341 PLAT_RESOURCE_BIOS_DATA_INDEX
);
343 dev_err(&pdev
->dev
, "Failed to get resource of P-unit BIOS data\n");
346 punit_res
[npunit_res
++] = *res
;
348 /* BIOS interface register */
349 res
= platform_get_resource(pdev
, IORESOURCE_MEM
,
350 PLAT_RESOURCE_BIOS_IFACE_INDEX
);
352 dev_err(&pdev
->dev
, "Failed to get resource of P-unit BIOS interface\n");
355 punit_res
[npunit_res
++] = *res
;
357 /* ISP data register, optional */
358 res
= platform_get_resource(pdev
, IORESOURCE_MEM
,
359 PLAT_RESOURCE_ISP_DATA_INDEX
);
361 punit_res
[npunit_res
++] = *res
;
363 /* ISP interface register, optional */
364 res
= platform_get_resource(pdev
, IORESOURCE_MEM
,
365 PLAT_RESOURCE_ISP_IFACE_INDEX
);
367 punit_res
[npunit_res
++] = *res
;
369 /* GTD data register, optional */
370 res
= platform_get_resource(pdev
, IORESOURCE_MEM
,
371 PLAT_RESOURCE_GTD_DATA_INDEX
);
373 punit_res
[npunit_res
++] = *res
;
375 /* GTD interface register, optional */
376 res
= platform_get_resource(pdev
, IORESOURCE_MEM
,
377 PLAT_RESOURCE_GTD_IFACE_INDEX
);
379 punit_res
[npunit_res
++] = *res
;
381 punit
.num_resources
= npunit_res
;
383 /* Telemetry SSRAM is optional */
384 res
= platform_get_resource(pdev
, IORESOURCE_MEM
,
385 PLAT_RESOURCE_TELEM_SSRAM_INDEX
);
387 pmc
->telem_base
= res
;
392 static int intel_pmc_create_devices(struct intel_pmc_dev
*pmc
)
396 if (!acpi_has_watchdog()) {
397 ret
= devm_mfd_add_devices(pmc
->dev
, PLATFORM_DEVID_AUTO
, &tco
,
403 ret
= devm_mfd_add_devices(pmc
->dev
, PLATFORM_DEVID_AUTO
, &punit
, 1,
408 if (pmc
->telem_base
) {
409 ret
= devm_mfd_add_devices(pmc
->dev
, PLATFORM_DEVID_AUTO
,
410 &telem
, 1, pmc
->telem_base
, 0, NULL
);
416 static const struct acpi_device_id intel_pmc_acpi_ids
[] = {
420 MODULE_DEVICE_TABLE(acpi
, intel_pmc_acpi_ids
);
422 static int intel_pmc_probe(struct platform_device
*pdev
)
424 struct intel_scu_ipc_data scu_data
= {};
425 struct intel_pmc_dev
*pmc
;
428 pmc
= devm_kzalloc(&pdev
->dev
, sizeof(*pmc
), GFP_KERNEL
);
432 pmc
->dev
= &pdev
->dev
;
433 spin_lock_init(&pmc
->gcr_lock
);
435 ret
= intel_pmc_get_resources(pdev
, pmc
, &scu_data
);
437 dev_err(&pdev
->dev
, "Failed to request resources\n");
441 pmc
->scu
= devm_intel_scu_ipc_register(&pdev
->dev
, &scu_data
);
442 if (IS_ERR(pmc
->scu
))
443 return PTR_ERR(pmc
->scu
);
445 platform_set_drvdata(pdev
, pmc
);
447 ret
= intel_pmc_create_devices(pmc
);
449 dev_err(&pdev
->dev
, "Failed to create PMC devices\n");
454 static struct platform_driver intel_pmc_driver
= {
455 .probe
= intel_pmc_probe
,
457 .name
= "intel_pmc_bxt",
458 .acpi_match_table
= intel_pmc_acpi_ids
,
459 .dev_groups
= intel_pmc_groups
,
462 module_platform_driver(intel_pmc_driver
);
464 MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
465 MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>");
466 MODULE_DESCRIPTION("Intel Broxton PMC driver");
467 MODULE_LICENSE("GPL v2");