cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

rn-pci-acp3x.c (10072B)


      1// SPDX-License-Identifier: GPL-2.0+
      2//
      3// AMD Renoir ACP PCI Driver
      4//
      5//Copyright 2020 Advanced Micro Devices, Inc.
      6
      7#include <linux/pci.h>
      8#include <linux/acpi.h>
      9#include <linux/dmi.h>
     10#include <linux/module.h>
     11#include <linux/io.h>
     12#include <linux/delay.h>
     13#include <linux/platform_device.h>
     14#include <linux/interrupt.h>
     15#include <linux/pm_runtime.h>
     16
     17#include "rn_acp3x.h"
     18
     19static int acp_power_gating;
     20module_param(acp_power_gating, int, 0644);
     21MODULE_PARM_DESC(acp_power_gating, "Enable acp power gating");
     22
     23/*
     24 * dmic_acpi_check = -1 - Use ACPI/DMI method to detect the DMIC hardware presence at runtime
     25 *                 =  0 - Skip the DMIC device creation and return probe failure
     26 *                 =  1 - Force DMIC support
     27 */
     28static int dmic_acpi_check = ACP_DMIC_AUTO;
     29module_param(dmic_acpi_check, bint, 0644);
     30MODULE_PARM_DESC(dmic_acpi_check, "Digital microphone presence (-1=auto, 0=none, 1=force)");
     31
     32struct acp_dev_data {
     33	void __iomem *acp_base;
     34	struct resource *res;
     35	struct platform_device *pdev[ACP_DEVS];
     36};
     37
     38static int rn_acp_power_on(void __iomem *acp_base)
     39{
     40	u32 val;
     41	int timeout;
     42
     43	val = rn_readl(acp_base + ACP_PGFSM_STATUS);
     44
     45	if (val == 0)
     46		return val;
     47
     48	if ((val & ACP_PGFSM_STATUS_MASK) !=
     49				ACP_POWER_ON_IN_PROGRESS)
     50		rn_writel(ACP_PGFSM_CNTL_POWER_ON_MASK,
     51			  acp_base + ACP_PGFSM_CONTROL);
     52	timeout = 0;
     53	while (++timeout < 500) {
     54		val = rn_readl(acp_base + ACP_PGFSM_STATUS);
     55		if (!val)
     56			return 0;
     57		udelay(1);
     58	}
     59	return -ETIMEDOUT;
     60}
     61
     62static int rn_acp_power_off(void __iomem *acp_base)
     63{
     64	u32 val;
     65	int timeout;
     66
     67	rn_writel(ACP_PGFSM_CNTL_POWER_OFF_MASK,
     68		  acp_base + ACP_PGFSM_CONTROL);
     69	timeout = 0;
     70	while (++timeout < 500) {
     71		val = rn_readl(acp_base + ACP_PGFSM_STATUS);
     72		if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_OFF)
     73			return 0;
     74		udelay(1);
     75	}
     76	return -ETIMEDOUT;
     77}
     78
     79static int rn_acp_reset(void __iomem *acp_base)
     80{
     81	u32 val;
     82	int timeout;
     83
     84	rn_writel(1, acp_base + ACP_SOFT_RESET);
     85	timeout = 0;
     86	while (++timeout < 500) {
     87		val = rn_readl(acp_base + ACP_SOFT_RESET);
     88		if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
     89			break;
     90		cpu_relax();
     91	}
     92	rn_writel(0, acp_base + ACP_SOFT_RESET);
     93	timeout = 0;
     94	while (++timeout < 500) {
     95		val = rn_readl(acp_base + ACP_SOFT_RESET);
     96		if (!val)
     97			return 0;
     98		cpu_relax();
     99	}
    100	return -ETIMEDOUT;
    101}
    102
    103static void rn_acp_enable_interrupts(void __iomem *acp_base)
    104{
    105	u32 ext_intr_ctrl;
    106
    107	rn_writel(0x01, acp_base + ACP_EXTERNAL_INTR_ENB);
    108	ext_intr_ctrl = rn_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
    109	ext_intr_ctrl |= ACP_ERROR_MASK;
    110	rn_writel(ext_intr_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
    111}
    112
    113static void rn_acp_disable_interrupts(void __iomem *acp_base)
    114{
    115	rn_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base +
    116		  ACP_EXTERNAL_INTR_STAT);
    117	rn_writel(0x00, acp_base + ACP_EXTERNAL_INTR_ENB);
    118}
    119
    120static int rn_acp_init(void __iomem *acp_base)
    121{
    122	int ret;
    123
    124	/* power on */
    125	ret = rn_acp_power_on(acp_base);
    126	if (ret) {
    127		pr_err("ACP power on failed\n");
    128		return ret;
    129	}
    130	rn_writel(0x01, acp_base + ACP_CONTROL);
    131	/* Reset */
    132	ret = rn_acp_reset(acp_base);
    133	if (ret) {
    134		pr_err("ACP reset failed\n");
    135		return ret;
    136	}
    137	rn_writel(0x03, acp_base + ACP_CLKMUX_SEL);
    138	rn_acp_enable_interrupts(acp_base);
    139	return 0;
    140}
    141
    142static int rn_acp_deinit(void __iomem *acp_base)
    143{
    144	int ret;
    145
    146	rn_acp_disable_interrupts(acp_base);
    147	/* Reset */
    148	ret = rn_acp_reset(acp_base);
    149	if (ret) {
    150		pr_err("ACP reset failed\n");
    151		return ret;
    152	}
    153	rn_writel(0x00, acp_base + ACP_CLKMUX_SEL);
    154	rn_writel(0x00, acp_base + ACP_CONTROL);
    155	/* power off */
    156	if (acp_power_gating) {
    157		ret = rn_acp_power_off(acp_base);
    158		if (ret) {
    159			pr_err("ACP power off failed\n");
    160			return ret;
    161		}
    162	}
    163	return 0;
    164}
    165
    166static const struct dmi_system_id rn_acp_quirk_table[] = {
    167	{
    168		/* Lenovo IdeaPad S340-14API */
    169		.matches = {
    170			DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
    171			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81NB"),
    172		}
    173	},
    174	{
    175		/* Lenovo IdeaPad Flex 5 14ARE05 */
    176		.matches = {
    177			DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
    178			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81X2"),
    179		}
    180	},
    181	{
    182		/* Lenovo IdeaPad 5 15ARE05 */
    183		.matches = {
    184			DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
    185			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81YQ"),
    186		}
    187	},
    188	{
    189		/* Lenovo ThinkPad E14 Gen 2 */
    190		.matches = {
    191			DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
    192			DMI_EXACT_MATCH(DMI_BOARD_NAME, "20T6CTO1WW"),
    193		}
    194	},
    195	{
    196		/* Lenovo ThinkPad X395 */
    197		.matches = {
    198			DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
    199			DMI_EXACT_MATCH(DMI_BOARD_NAME, "20NLCTO1WW"),
    200		}
    201	},
    202	{}
    203};
    204
    205static int snd_rn_acp_probe(struct pci_dev *pci,
    206			    const struct pci_device_id *pci_id)
    207{
    208	struct acp_dev_data *adata;
    209	struct platform_device_info pdevinfo[ACP_DEVS];
    210#if defined(CONFIG_ACPI)
    211	acpi_handle handle;
    212	acpi_integer dmic_status;
    213#endif
    214	const struct dmi_system_id *dmi_id;
    215	unsigned int irqflags, flag;
    216	int ret, index;
    217	u32 addr;
    218
    219	/* Return if acp config flag is defined */
    220	flag = snd_amd_acp_find_config(pci);
    221	if (flag)
    222		return -ENODEV;
    223
    224	/* Renoir device check */
    225	if (pci->revision != 0x01)
    226		return -ENODEV;
    227
    228	if (pci_enable_device(pci)) {
    229		dev_err(&pci->dev, "pci_enable_device failed\n");
    230		return -ENODEV;
    231	}
    232
    233	ret = pci_request_regions(pci, "AMD ACP3x audio");
    234	if (ret < 0) {
    235		dev_err(&pci->dev, "pci_request_regions failed\n");
    236		goto disable_pci;
    237	}
    238
    239	adata = devm_kzalloc(&pci->dev, sizeof(struct acp_dev_data),
    240			     GFP_KERNEL);
    241	if (!adata) {
    242		ret = -ENOMEM;
    243		goto release_regions;
    244	}
    245
    246	/* check for msi interrupt support */
    247	ret = pci_enable_msi(pci);
    248	if (ret)
    249		/* msi is not enabled */
    250		irqflags = IRQF_SHARED;
    251	else
    252		/* msi is enabled */
    253		irqflags = 0;
    254
    255	addr = pci_resource_start(pci, 0);
    256	adata->acp_base = devm_ioremap(&pci->dev, addr,
    257				       pci_resource_len(pci, 0));
    258	if (!adata->acp_base) {
    259		ret = -ENOMEM;
    260		goto disable_msi;
    261	}
    262	pci_set_master(pci);
    263	pci_set_drvdata(pci, adata);
    264	ret = rn_acp_init(adata->acp_base);
    265	if (ret)
    266		goto disable_msi;
    267
    268	if (!dmic_acpi_check) {
    269		ret = -ENODEV;
    270		goto de_init;
    271	} else if (dmic_acpi_check == ACP_DMIC_AUTO) {
    272#if defined(CONFIG_ACPI)
    273		handle = ACPI_HANDLE(&pci->dev);
    274		ret = acpi_evaluate_integer(handle, "_WOV", NULL, &dmic_status);
    275		if (ACPI_FAILURE(ret)) {
    276			ret = -ENODEV;
    277			goto de_init;
    278		}
    279		if (!dmic_status) {
    280			ret = -ENODEV;
    281			goto de_init;
    282		}
    283#endif
    284		dmi_id = dmi_first_match(rn_acp_quirk_table);
    285		if (dmi_id && !dmi_id->driver_data) {
    286			dev_info(&pci->dev, "ACPI settings override using DMI (ACP mic is not present)");
    287			ret = -ENODEV;
    288			goto de_init;
    289		}
    290	}
    291
    292	adata->res = devm_kzalloc(&pci->dev,
    293				  sizeof(struct resource) * 2,
    294				  GFP_KERNEL);
    295	if (!adata->res) {
    296		ret = -ENOMEM;
    297		goto de_init;
    298	}
    299
    300	adata->res[0].name = "acp_pdm_iomem";
    301	adata->res[0].flags = IORESOURCE_MEM;
    302	adata->res[0].start = addr;
    303	adata->res[0].end = addr + (ACP_REG_END - ACP_REG_START);
    304	adata->res[1].name = "acp_pdm_irq";
    305	adata->res[1].flags = IORESOURCE_IRQ;
    306	adata->res[1].start = pci->irq;
    307	adata->res[1].end = pci->irq;
    308
    309	memset(&pdevinfo, 0, sizeof(pdevinfo));
    310	pdevinfo[0].name = "acp_rn_pdm_dma";
    311	pdevinfo[0].id = 0;
    312	pdevinfo[0].parent = &pci->dev;
    313	pdevinfo[0].num_res = 2;
    314	pdevinfo[0].res = adata->res;
    315	pdevinfo[0].data = &irqflags;
    316	pdevinfo[0].size_data = sizeof(irqflags);
    317
    318	pdevinfo[1].name = "dmic-codec";
    319	pdevinfo[1].id = 0;
    320	pdevinfo[1].parent = &pci->dev;
    321	pdevinfo[2].name = "acp_pdm_mach";
    322	pdevinfo[2].id = 0;
    323	pdevinfo[2].parent = &pci->dev;
    324	for (index = 0; index < ACP_DEVS; index++) {
    325		adata->pdev[index] =
    326				platform_device_register_full(&pdevinfo[index]);
    327		if (IS_ERR(adata->pdev[index])) {
    328			dev_err(&pci->dev, "cannot register %s device\n",
    329				pdevinfo[index].name);
    330			ret = PTR_ERR(adata->pdev[index]);
    331			goto unregister_devs;
    332		}
    333	}
    334	pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);
    335	pm_runtime_use_autosuspend(&pci->dev);
    336	pm_runtime_put_noidle(&pci->dev);
    337	pm_runtime_allow(&pci->dev);
    338	return 0;
    339
    340unregister_devs:
    341	for (index = 0; index < ACP_DEVS; index++)
    342		platform_device_unregister(adata->pdev[index]);
    343de_init:
    344	if (rn_acp_deinit(adata->acp_base))
    345		dev_err(&pci->dev, "ACP de-init failed\n");
    346disable_msi:
    347	pci_disable_msi(pci);
    348release_regions:
    349	pci_release_regions(pci);
    350disable_pci:
    351	pci_disable_device(pci);
    352
    353	return ret;
    354}
    355
    356static int snd_rn_acp_suspend(struct device *dev)
    357{
    358	int ret;
    359	struct acp_dev_data *adata;
    360
    361	adata = dev_get_drvdata(dev);
    362	ret = rn_acp_deinit(adata->acp_base);
    363	if (ret)
    364		dev_err(dev, "ACP de-init failed\n");
    365	else
    366		dev_dbg(dev, "ACP de-initialized\n");
    367
    368	return ret;
    369}
    370
    371static int snd_rn_acp_resume(struct device *dev)
    372{
    373	int ret;
    374	struct acp_dev_data *adata;
    375
    376	adata = dev_get_drvdata(dev);
    377	ret = rn_acp_init(adata->acp_base);
    378	if (ret) {
    379		dev_err(dev, "ACP init failed\n");
    380		return ret;
    381	}
    382	return 0;
    383}
    384
    385static const struct dev_pm_ops rn_acp_pm = {
    386	.runtime_suspend = snd_rn_acp_suspend,
    387	.runtime_resume =  snd_rn_acp_resume,
    388	.suspend = snd_rn_acp_suspend,
    389	.resume =	snd_rn_acp_resume,
    390	.restore =	snd_rn_acp_resume,
    391	.poweroff =	snd_rn_acp_suspend,
    392};
    393
    394static void snd_rn_acp_remove(struct pci_dev *pci)
    395{
    396	struct acp_dev_data *adata;
    397	int ret, index;
    398
    399	adata = pci_get_drvdata(pci);
    400	for (index = 0; index < ACP_DEVS; index++)
    401		platform_device_unregister(adata->pdev[index]);
    402	ret = rn_acp_deinit(adata->acp_base);
    403	if (ret)
    404		dev_err(&pci->dev, "ACP de-init failed\n");
    405	pm_runtime_forbid(&pci->dev);
    406	pm_runtime_get_noresume(&pci->dev);
    407	pci_disable_msi(pci);
    408	pci_release_regions(pci);
    409	pci_disable_device(pci);
    410}
    411
    412static const struct pci_device_id snd_rn_acp_ids[] = {
    413	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
    414	.class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
    415	.class_mask = 0xffffff },
    416	{ 0, },
    417};
    418MODULE_DEVICE_TABLE(pci, snd_rn_acp_ids);
    419
    420static struct pci_driver rn_acp_driver  = {
    421	.name = KBUILD_MODNAME,
    422	.id_table = snd_rn_acp_ids,
    423	.probe = snd_rn_acp_probe,
    424	.remove = snd_rn_acp_remove,
    425	.driver = {
    426		.pm = &rn_acp_pm,
    427	}
    428};
    429
    430module_pci_driver(rn_acp_driver);
    431
    432MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
    433MODULE_DESCRIPTION("AMD ACP Renoir PCI driver");
    434MODULE_LICENSE("GPL v2");