156893Sfenner// SPDX-License-Identifier: GPL-2.0-only
256893Sfenner/*
356893Sfenner * AMD Secure Processor device driver
456893Sfenner *
556893Sfenner * Copyright (C) 2014,2018 Advanced Micro Devices, Inc.
656893Sfenner *
756893Sfenner * Author: Tom Lendacky <thomas.lendacky@amd.com>
856893Sfenner */
956893Sfenner
1056893Sfenner#include <linux/module.h>
1156893Sfenner#include <linux/kernel.h>
1256893Sfenner#include <linux/device.h>
1356893Sfenner#include <linux/platform_device.h>
1456893Sfenner#include <linux/ioport.h>
1556893Sfenner#include <linux/dma-mapping.h>
1656893Sfenner#include <linux/kthread.h>
1756893Sfenner#include <linux/sched.h>
1856893Sfenner#include <linux/interrupt.h>
1956893Sfenner#include <linux/spinlock.h>
2056893Sfenner#include <linux/delay.h>
2156893Sfenner#include <linux/ccp.h>
2256893Sfenner#include <linux/of.h>
2356893Sfenner#include <linux/of_address.h>
2456893Sfenner#include <linux/acpi.h>
2556893Sfenner
2656893Sfenner#include "ccp-dev.h"
2756893Sfenner
2856893Sfennerstruct sp_platform {
2956893Sfenner	int coherent;
3056893Sfenner	unsigned int irq_count;
3156893Sfenner};
3256893Sfenner
3356893Sfennerstatic const struct sp_dev_vdata dev_vdata[] = {
3456893Sfenner	{
3556893Sfenner		.bar = 0,
3656893Sfenner#ifdef CONFIG_CRYPTO_DEV_SP_CCP
3756893Sfenner		.ccp_vdata = &ccpv3_platform,
3856893Sfenner#endif
3956893Sfenner	},
4056893Sfenner};
4156893Sfenner
4256893Sfenner#ifdef CONFIG_ACPI
4356893Sfennerstatic const struct acpi_device_id sp_acpi_match[] = {
4456893Sfenner	{ "AMDI0C00", (kernel_ulong_t)&dev_vdata[0] },
4556893Sfenner	{ },
4656893Sfenner};
4756893SfennerMODULE_DEVICE_TABLE(acpi, sp_acpi_match);
4856893Sfenner#endif
4956893Sfenner
5056893Sfenner#ifdef CONFIG_OF
5156893Sfennerstatic const struct of_device_id sp_of_match[] = {
5256893Sfenner	{ .compatible = "amd,ccp-seattle-v1a",
5356893Sfenner	  .data = (const void *)&dev_vdata[0] },
5456893Sfenner	{ },
5556893Sfenner};
5656893SfennerMODULE_DEVICE_TABLE(of, sp_of_match);
5756893Sfenner#endif
5856893Sfenner
5956893Sfennerstatic struct sp_dev_vdata *sp_get_of_version(struct platform_device *pdev)
6056893Sfenner{
6156893Sfenner#ifdef CONFIG_OF
6256893Sfenner	const struct of_device_id *match;
6356893Sfenner
6456893Sfenner	match = of_match_node(sp_of_match, pdev->dev.of_node);
6556893Sfenner	if (match && match->data)
6656893Sfenner		return (struct sp_dev_vdata *)match->data;
6756893Sfenner#endif
6856893Sfenner	return NULL;
6956893Sfenner}
7056893Sfenner
7156893Sfennerstatic struct sp_dev_vdata *sp_get_acpi_version(struct platform_device *pdev)
7256893Sfenner{
7356893Sfenner#ifdef CONFIG_ACPI
7456893Sfenner	const struct acpi_device_id *match;
7556893Sfenner
7656893Sfenner	match = acpi_match_device(sp_acpi_match, &pdev->dev);
7756893Sfenner	if (match && match->driver_data)
7856893Sfenner		return (struct sp_dev_vdata *)match->driver_data;
7956893Sfenner#endif
8056893Sfenner	return NULL;
8156893Sfenner}
8256893Sfenner
8356893Sfennerstatic int sp_get_irqs(struct sp_device *sp)
8456893Sfenner{
8556893Sfenner	struct sp_platform *sp_platform = sp->dev_specific;
8656893Sfenner	struct device *dev = sp->dev;
8756893Sfenner	struct platform_device *pdev = to_platform_device(dev);
8856893Sfenner	int ret;
8956893Sfenner
9056893Sfenner	sp_platform->irq_count = platform_irq_count(pdev);
9156893Sfenner
9256893Sfenner	ret = platform_get_irq(pdev, 0);
9356893Sfenner	if (ret < 0) {
9456893Sfenner		dev_notice(dev, "unable to get IRQ (%d)\n", ret);
9556893Sfenner		return ret;
9656893Sfenner	}
9756893Sfenner
9856893Sfenner	sp->psp_irq = ret;
9956893Sfenner	if (sp_platform->irq_count == 1) {
10056893Sfenner		sp->ccp_irq = ret;
10156893Sfenner	} else {
10256893Sfenner		ret = platform_get_irq(pdev, 1);
10356893Sfenner		if (ret < 0) {
10456893Sfenner			dev_notice(dev, "unable to get IRQ (%d)\n", ret);
10556893Sfenner			return ret;
10656893Sfenner		}
10756893Sfenner
10856893Sfenner		sp->ccp_irq = ret;
10956893Sfenner	}
11056893Sfenner
11156893Sfenner	return 0;
11256893Sfenner}
11356893Sfenner
11456893Sfennerstatic int sp_platform_probe(struct platform_device *pdev)
11556893Sfenner{
11656893Sfenner	struct sp_device *sp;
11756893Sfenner	struct sp_platform *sp_platform;
11856893Sfenner	struct device *dev = &pdev->dev;
11956893Sfenner	enum dev_dma_attr attr;
12056893Sfenner	int ret;
12156893Sfenner
12256893Sfenner	ret = -ENOMEM;
12356893Sfenner	sp = sp_alloc_struct(dev);
12456893Sfenner	if (!sp)
12556893Sfenner		goto e_err;
12656893Sfenner
12756893Sfenner	sp_platform = devm_kzalloc(dev, sizeof(*sp_platform), GFP_KERNEL);
12856893Sfenner	if (!sp_platform)
12956893Sfenner		goto e_err;
13056893Sfenner
13156893Sfenner	sp->dev_specific = sp_platform;
13256893Sfenner	sp->dev_vdata = pdev->dev.of_node ? sp_get_of_version(pdev)
13356893Sfenner					 : sp_get_acpi_version(pdev);
13456893Sfenner	if (!sp->dev_vdata) {
13556893Sfenner		ret = -ENODEV;
13656893Sfenner		dev_err(dev, "missing driver data\n");
13756893Sfenner		goto e_err;
13856893Sfenner	}
13956893Sfenner
14056893Sfenner	sp->io_map = devm_platform_ioremap_resource(pdev, 0);
14156893Sfenner	if (IS_ERR(sp->io_map)) {
14256893Sfenner		ret = PTR_ERR(sp->io_map);
14356893Sfenner		goto e_err;
14456893Sfenner	}
14556893Sfenner
14656893Sfenner	attr = device_get_dma_attr(dev);
14756893Sfenner	if (attr == DEV_DMA_NOT_SUPPORTED) {
14856893Sfenner		dev_err(dev, "DMA is not supported");
14956893Sfenner		goto e_err;
15056893Sfenner	}
15156893Sfenner
15256893Sfenner	sp_platform->coherent = (attr == DEV_DMA_COHERENT);
15356893Sfenner	if (sp_platform->coherent)
15456893Sfenner		sp->axcache = CACHE_WB_NO_ALLOC;
15556893Sfenner	else
15656893Sfenner		sp->axcache = CACHE_NONE;
15756893Sfenner
15856893Sfenner	ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48));
15956893Sfenner	if (ret) {
16056893Sfenner		dev_err(dev, "dma_set_mask_and_coherent failed (%d)\n", ret);
16156893Sfenner		goto e_err;
16256893Sfenner	}
16356893Sfenner
16456893Sfenner	ret = sp_get_irqs(sp);
16556893Sfenner	if (ret)
16656893Sfenner		goto e_err;
16756893Sfenner
16856893Sfenner	dev_set_drvdata(dev, sp);
16956893Sfenner
17056893Sfenner	ret = sp_init(sp);
17156893Sfenner	if (ret)
17256893Sfenner		goto e_err;
17356893Sfenner
17456893Sfenner	dev_notice(dev, "enabled\n");
17556893Sfenner
17656893Sfenner	return 0;
17756893Sfenner
17856893Sfennere_err:
17956893Sfenner	dev_notice(dev, "initialization failed\n");
18056893Sfenner	return ret;
18156893Sfenner}
18256893Sfenner
18356893Sfennerstatic void sp_platform_remove(struct platform_device *pdev)
18456893Sfenner{
18556893Sfenner	struct device *dev = &pdev->dev;
18656893Sfenner	struct sp_device *sp = dev_get_drvdata(dev);
18756893Sfenner
18856893Sfenner	sp_destroy(sp);
18956893Sfenner
19056893Sfenner	dev_notice(dev, "disabled\n");
19156893Sfenner}
19256893Sfenner
19356893Sfenner#ifdef CONFIG_PM
19456893Sfennerstatic int sp_platform_suspend(struct platform_device *pdev,
19556893Sfenner				pm_message_t state)
19656893Sfenner{
19756893Sfenner	struct device *dev = &pdev->dev;
19856893Sfenner	struct sp_device *sp = dev_get_drvdata(dev);
19956893Sfenner
20056893Sfenner	return sp_suspend(sp);
20156893Sfenner}
20256893Sfenner
20356893Sfennerstatic int sp_platform_resume(struct platform_device *pdev)
20456893Sfenner{
20556893Sfenner	struct device *dev = &pdev->dev;
20656893Sfenner	struct sp_device *sp = dev_get_drvdata(dev);
20756893Sfenner
20856893Sfenner	return sp_resume(sp);
20956893Sfenner}
21056893Sfenner#endif
21156893Sfenner
21256893Sfennerstatic struct platform_driver sp_platform_driver = {
21356893Sfenner	.driver = {
21456893Sfenner		.name = "ccp",
21556893Sfenner#ifdef CONFIG_ACPI
21656893Sfenner		.acpi_match_table = sp_acpi_match,
21756893Sfenner#endif
21856893Sfenner#ifdef CONFIG_OF
21956893Sfenner		.of_match_table = sp_of_match,
22056893Sfenner#endif
22156893Sfenner	},
22256893Sfenner	.probe = sp_platform_probe,
22356893Sfenner	.remove_new = sp_platform_remove,
22456893Sfenner#ifdef CONFIG_PM
22556893Sfenner	.suspend = sp_platform_suspend,
22656893Sfenner	.resume = sp_platform_resume,
22756893Sfenner#endif
22856893Sfenner};
229
230int sp_platform_init(void)
231{
232	return platform_driver_register(&sp_platform_driver);
233}
234
235void sp_platform_exit(void)
236{
237	platform_driver_unregister(&sp_platform_driver);
238}
239