ccp-platform.c 5.3 KB
Newer Older
1 2 3
/*
 * AMD Cryptographic Coprocessor (CCP) driver
 *
4
 * Copyright (C) 2014,2016 Advanced Micro Devices, Inc.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 *
 * Author: Tom Lendacky <thomas.lendacky@amd.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
#include <linux/dma-mapping.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/ccp.h>
25
#include <linux/of.h>
T
Tom Lendacky 已提交
26 27
#include <linux/of_address.h>
#include <linux/acpi.h>
28 29 30

#include "ccp-dev.h"

T
Tom Lendacky 已提交
31 32 33 34
struct ccp_platform {
	int coherent;
};

35 36 37 38 39 40 41 42 43 44 45 46
static const struct acpi_device_id ccp_acpi_match[];
static const struct of_device_id ccp_of_match[];

static struct ccp_vdata *ccp_get_of_version(struct platform_device *pdev)
{
#ifdef CONFIG_OF
	const struct of_device_id *match;

	match = of_match_node(ccp_of_match, pdev->dev.of_node);
	if (match && match->data)
		return (struct ccp_vdata *)match->data;
#endif
47
	return NULL;
48 49 50 51 52 53 54 55 56 57 58
}

static struct ccp_vdata *ccp_get_acpi_version(struct platform_device *pdev)
{
#ifdef CONFIG_ACPI
	const struct acpi_device_id *match;

	match = acpi_match_device(ccp_acpi_match, &pdev->dev);
	if (match && match->driver_data)
		return (struct ccp_vdata *)match->driver_data;
#endif
59
	return NULL;
60 61
}

62 63 64
static int ccp_get_irq(struct ccp_device *ccp)
{
	struct device *dev = ccp->dev;
65
	struct platform_device *pdev = to_platform_device(dev);
66 67 68
	int ret;

	ret = platform_get_irq(pdev, 0);
69 70
	if (ret < 0) {
		dev_notice(dev, "unable to get IRQ (%d)\n", ret);
71
		return ret;
72
	}
73 74

	ccp->irq = ret;
75 76
	ret = request_irq(ccp->irq, ccp->vdata->perform->irqhandler, 0,
			  ccp->name, dev);
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
	if (ret) {
		dev_notice(dev, "unable to allocate IRQ (%d)\n", ret);
		return ret;
	}

	return 0;
}

static int ccp_get_irqs(struct ccp_device *ccp)
{
	struct device *dev = ccp->dev;
	int ret;

	ret = ccp_get_irq(ccp);
	if (!ret)
		return 0;

	/* Couldn't get an interrupt */
	dev_notice(dev, "could not enable interrupts (%d)\n", ret);

	return ret;
}

static void ccp_free_irqs(struct ccp_device *ccp)
{
	struct device *dev = ccp->dev;

	free_irq(ccp->irq, dev);
}

static int ccp_platform_probe(struct platform_device *pdev)
{
	struct ccp_device *ccp;
T
Tom Lendacky 已提交
110
	struct ccp_platform *ccp_platform;
111
	struct device *dev = &pdev->dev;
112
	enum dev_dma_attr attr;
113 114 115 116 117 118 119 120
	struct resource *ior;
	int ret;

	ret = -ENOMEM;
	ccp = ccp_alloc_struct(dev);
	if (!ccp)
		goto e_err;

T
Tom Lendacky 已提交
121 122 123 124 125
	ccp_platform = devm_kzalloc(dev, sizeof(*ccp_platform), GFP_KERNEL);
	if (!ccp_platform)
		goto e_err;

	ccp->dev_specific = ccp_platform;
126 127 128 129 130 131 132
	ccp->vdata = pdev->dev.of_node ? ccp_get_of_version(pdev)
					 : ccp_get_acpi_version(pdev);
	if (!ccp->vdata || !ccp->vdata->version) {
		ret = -ENODEV;
		dev_err(dev, "missing driver data\n");
		goto e_err;
	}
133 134 135
	ccp->get_irq = ccp_get_irqs;
	ccp->free_irq = ccp_free_irqs;

136
	ior = platform_get_resource(pdev, IORESOURCE_MEM, 0);
137 138 139
	ccp->io_map = devm_ioremap_resource(dev, ior);
	if (IS_ERR(ccp->io_map)) {
		ret = PTR_ERR(ccp->io_map);
140
		goto e_err;
141 142 143
	}
	ccp->io_regs = ccp->io_map;

144 145 146
	attr = device_get_dma_attr(dev);
	if (attr == DEV_DMA_NOT_SUPPORTED) {
		dev_err(dev, "DMA is not supported");
147
		goto e_err;
148
	}
149

150
	ccp_platform->coherent = (attr == DEV_DMA_COHERENT);
T
Tom Lendacky 已提交
151
	if (ccp_platform->coherent)
152 153 154 155
		ccp->axcache = CACHE_WB_NO_ALLOC;
	else
		ccp->axcache = CACHE_NONE;

156 157 158 159 160 161
	ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48));
	if (ret) {
		dev_err(dev, "dma_set_mask_and_coherent failed (%d)\n", ret);
		goto e_err;
	}

162 163
	dev_set_drvdata(dev, ccp);

164
	ret = ccp_dev_init(ccp);
165
	if (ret)
166
		goto e_err;
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181

	dev_notice(dev, "enabled\n");

	return 0;

e_err:
	dev_notice(dev, "initialization failed\n");
	return ret;
}

static int ccp_platform_remove(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct ccp_device *ccp = dev_get_drvdata(dev);

182
	ccp_dev_destroy(ccp);
183 184 185 186 187 188 189 190 191 192 193 194 195

	dev_notice(dev, "disabled\n");

	return 0;
}

#ifdef CONFIG_PM
static int ccp_platform_suspend(struct platform_device *pdev,
				pm_message_t state)
{
	struct device *dev = &pdev->dev;
	struct ccp_device *ccp = dev_get_drvdata(dev);

196
	return ccp_dev_suspend(ccp, state);
197 198 199 200 201 202 203
}

static int ccp_platform_resume(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct ccp_device *ccp = dev_get_drvdata(dev);

204
	return ccp_dev_resume(ccp);
205 206 207
}
#endif

T
Tom Lendacky 已提交
208 209
#ifdef CONFIG_ACPI
static const struct acpi_device_id ccp_acpi_match[] = {
210
	{ "AMDI0C00", (kernel_ulong_t)&ccpv3 },
T
Tom Lendacky 已提交
211 212
	{ },
};
213
MODULE_DEVICE_TABLE(acpi, ccp_acpi_match);
T
Tom Lendacky 已提交
214 215 216 217
#endif

#ifdef CONFIG_OF
static const struct of_device_id ccp_of_match[] = {
218
	{ .compatible = "amd,ccp-seattle-v1a",
219
	  .data = (const void *)&ccpv3_platform },
220 221
	{ },
};
222
MODULE_DEVICE_TABLE(of, ccp_of_match);
T
Tom Lendacky 已提交
223
#endif
224 225 226

static struct platform_driver ccp_platform_driver = {
	.driver = {
227
		.name = "ccp",
T
Tom Lendacky 已提交
228 229 230 231 232 233
#ifdef CONFIG_ACPI
		.acpi_match_table = ccp_acpi_match,
#endif
#ifdef CONFIG_OF
		.of_match_table = ccp_of_match,
#endif
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
	},
	.probe = ccp_platform_probe,
	.remove = ccp_platform_remove,
#ifdef CONFIG_PM
	.suspend = ccp_platform_suspend,
	.resume = ccp_platform_resume,
#endif
};

int ccp_platform_init(void)
{
	return platform_driver_register(&ccp_platform_driver);
}

void ccp_platform_exit(void)
{
	platform_driver_unregister(&ccp_platform_driver);
}