18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2012-2020 IBM Corporation
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author: Ashley Lai <ashleydlai@gmail.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Maintained by: <tpmdd-devel@lists.sourceforge.net>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Device driver for TCG/TCPA TPM (trusted platform module).
108c2ecf20Sopenharmony_ci * Specifications at www.trustedcomputinggroup.org
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
148c2ecf20Sopenharmony_ci#include <linux/dmapool.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <asm/vio.h>
178c2ecf20Sopenharmony_ci#include <asm/irq.h>
188c2ecf20Sopenharmony_ci#include <linux/types.h>
198c2ecf20Sopenharmony_ci#include <linux/list.h>
208c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
218c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
228c2ecf20Sopenharmony_ci#include <linux/wait.h>
238c2ecf20Sopenharmony_ci#include <asm/prom.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "tpm.h"
268c2ecf20Sopenharmony_ci#include "tpm_ibmvtpm.h"
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic const char tpm_ibmvtpm_driver_name[] = "tpm_ibmvtpm";
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic const struct vio_device_id tpm_ibmvtpm_device_table[] = {
318c2ecf20Sopenharmony_ci	{ "IBM,vtpm", "IBM,vtpm"},
328c2ecf20Sopenharmony_ci	{ "IBM,vtpm", "IBM,vtpm20"},
338c2ecf20Sopenharmony_ci	{ "", "" }
348c2ecf20Sopenharmony_ci};
358c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(vio, tpm_ibmvtpm_device_table);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/**
388c2ecf20Sopenharmony_ci * ibmvtpm_send_crq_word() - Send a CRQ request
398c2ecf20Sopenharmony_ci * @vdev:	vio device struct
408c2ecf20Sopenharmony_ci * @w1:		pre-constructed first word of tpm crq (second word is reserved)
418c2ecf20Sopenharmony_ci *
428c2ecf20Sopenharmony_ci * Return:
438c2ecf20Sopenharmony_ci *	0 - Success
448c2ecf20Sopenharmony_ci *	Non-zero - Failure
458c2ecf20Sopenharmony_ci */
468c2ecf20Sopenharmony_cistatic int ibmvtpm_send_crq_word(struct vio_dev *vdev, u64 w1)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	return plpar_hcall_norets(H_SEND_CRQ, vdev->unit_address, w1, 0);
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/**
528c2ecf20Sopenharmony_ci * ibmvtpm_send_crq() - Send a CRQ request
538c2ecf20Sopenharmony_ci *
548c2ecf20Sopenharmony_ci * @vdev:	vio device struct
558c2ecf20Sopenharmony_ci * @valid:	Valid field
568c2ecf20Sopenharmony_ci * @msg:	Type field
578c2ecf20Sopenharmony_ci * @len:	Length field
588c2ecf20Sopenharmony_ci * @data:	Data field
598c2ecf20Sopenharmony_ci *
608c2ecf20Sopenharmony_ci * The ibmvtpm crq is defined as follows:
618c2ecf20Sopenharmony_ci *
628c2ecf20Sopenharmony_ci * Byte  |   0   |   1   |   2   |   3   |   4   |   5   |   6   |   7
638c2ecf20Sopenharmony_ci * -----------------------------------------------------------------------
648c2ecf20Sopenharmony_ci * Word0 | Valid | Type  |     Length    |              Data
658c2ecf20Sopenharmony_ci * -----------------------------------------------------------------------
668c2ecf20Sopenharmony_ci * Word1 |                Reserved
678c2ecf20Sopenharmony_ci * -----------------------------------------------------------------------
688c2ecf20Sopenharmony_ci *
698c2ecf20Sopenharmony_ci * Which matches the following structure (on bigendian host):
708c2ecf20Sopenharmony_ci *
718c2ecf20Sopenharmony_ci * struct ibmvtpm_crq {
728c2ecf20Sopenharmony_ci *         u8 valid;
738c2ecf20Sopenharmony_ci *         u8 msg;
748c2ecf20Sopenharmony_ci *         __be16 len;
758c2ecf20Sopenharmony_ci *         __be32 data;
768c2ecf20Sopenharmony_ci *         __be64 reserved;
778c2ecf20Sopenharmony_ci * } __attribute__((packed, aligned(8)));
788c2ecf20Sopenharmony_ci *
798c2ecf20Sopenharmony_ci * However, the value is passed in a register so just compute the numeric value
808c2ecf20Sopenharmony_ci * to load into the register avoiding byteswap altogether. Endian only affects
818c2ecf20Sopenharmony_ci * memory loads and stores - registers are internally represented the same.
828c2ecf20Sopenharmony_ci *
838c2ecf20Sopenharmony_ci * Return:
848c2ecf20Sopenharmony_ci *	0 (H_SUCCESS) - Success
858c2ecf20Sopenharmony_ci *	Non-zero - Failure
868c2ecf20Sopenharmony_ci */
878c2ecf20Sopenharmony_cistatic int ibmvtpm_send_crq(struct vio_dev *vdev,
888c2ecf20Sopenharmony_ci		u8 valid, u8 msg, u16 len, u32 data)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	u64 w1 = ((u64)valid << 56) | ((u64)msg << 48) | ((u64)len << 32) |
918c2ecf20Sopenharmony_ci		(u64)data;
928c2ecf20Sopenharmony_ci	return ibmvtpm_send_crq_word(vdev, w1);
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci/**
968c2ecf20Sopenharmony_ci * tpm_ibmvtpm_recv - Receive data after send
978c2ecf20Sopenharmony_ci *
988c2ecf20Sopenharmony_ci * @chip:	tpm chip struct
998c2ecf20Sopenharmony_ci * @buf:	buffer to read
1008c2ecf20Sopenharmony_ci * @count:	size of buffer
1018c2ecf20Sopenharmony_ci *
1028c2ecf20Sopenharmony_ci * Return:
1038c2ecf20Sopenharmony_ci *	Number of bytes read
1048c2ecf20Sopenharmony_ci */
1058c2ecf20Sopenharmony_cistatic int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
1088c2ecf20Sopenharmony_ci	u16 len;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	if (!ibmvtpm->rtce_buf) {
1118c2ecf20Sopenharmony_ci		dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n");
1128c2ecf20Sopenharmony_ci		return 0;
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	len = ibmvtpm->res_len;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	if (count < len) {
1188c2ecf20Sopenharmony_ci		dev_err(ibmvtpm->dev,
1198c2ecf20Sopenharmony_ci			"Invalid size in recv: count=%zd, crq_size=%d\n",
1208c2ecf20Sopenharmony_ci			count, len);
1218c2ecf20Sopenharmony_ci		return -EIO;
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	spin_lock(&ibmvtpm->rtce_lock);
1258c2ecf20Sopenharmony_ci	memcpy((void *)buf, (void *)ibmvtpm->rtce_buf, len);
1268c2ecf20Sopenharmony_ci	memset(ibmvtpm->rtce_buf, 0, len);
1278c2ecf20Sopenharmony_ci	ibmvtpm->res_len = 0;
1288c2ecf20Sopenharmony_ci	spin_unlock(&ibmvtpm->rtce_lock);
1298c2ecf20Sopenharmony_ci	return len;
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci/**
1338c2ecf20Sopenharmony_ci * ibmvtpm_crq_send_init - Send a CRQ initialize message
1348c2ecf20Sopenharmony_ci * @ibmvtpm:	vtpm device struct
1358c2ecf20Sopenharmony_ci *
1368c2ecf20Sopenharmony_ci * Return:
1378c2ecf20Sopenharmony_ci *	0 on success.
1388c2ecf20Sopenharmony_ci *	Non-zero on failure.
1398c2ecf20Sopenharmony_ci */
1408c2ecf20Sopenharmony_cistatic int ibmvtpm_crq_send_init(struct ibmvtpm_dev *ibmvtpm)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	int rc;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	rc = ibmvtpm_send_crq_word(ibmvtpm->vdev, INIT_CRQ_CMD);
1458c2ecf20Sopenharmony_ci	if (rc != H_SUCCESS)
1468c2ecf20Sopenharmony_ci		dev_err(ibmvtpm->dev,
1478c2ecf20Sopenharmony_ci			"%s failed rc=%d\n", __func__, rc);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	return rc;
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci/**
1538c2ecf20Sopenharmony_ci * tpm_ibmvtpm_resume - Resume from suspend
1548c2ecf20Sopenharmony_ci *
1558c2ecf20Sopenharmony_ci * @dev:	device struct
1568c2ecf20Sopenharmony_ci *
1578c2ecf20Sopenharmony_ci * Return: Always 0.
1588c2ecf20Sopenharmony_ci */
1598c2ecf20Sopenharmony_cistatic int tpm_ibmvtpm_resume(struct device *dev)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	struct tpm_chip *chip = dev_get_drvdata(dev);
1628c2ecf20Sopenharmony_ci	struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
1638c2ecf20Sopenharmony_ci	int rc = 0;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	do {
1668c2ecf20Sopenharmony_ci		if (rc)
1678c2ecf20Sopenharmony_ci			msleep(100);
1688c2ecf20Sopenharmony_ci		rc = plpar_hcall_norets(H_ENABLE_CRQ,
1698c2ecf20Sopenharmony_ci					ibmvtpm->vdev->unit_address);
1708c2ecf20Sopenharmony_ci	} while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc));
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	if (rc) {
1738c2ecf20Sopenharmony_ci		dev_err(dev, "Error enabling ibmvtpm rc=%d\n", rc);
1748c2ecf20Sopenharmony_ci		return rc;
1758c2ecf20Sopenharmony_ci	}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	rc = vio_enable_interrupts(ibmvtpm->vdev);
1788c2ecf20Sopenharmony_ci	if (rc) {
1798c2ecf20Sopenharmony_ci		dev_err(dev, "Error vio_enable_interrupts rc=%d\n", rc);
1808c2ecf20Sopenharmony_ci		return rc;
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	rc = ibmvtpm_crq_send_init(ibmvtpm);
1848c2ecf20Sopenharmony_ci	if (rc)
1858c2ecf20Sopenharmony_ci		dev_err(dev, "Error send_init rc=%d\n", rc);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	return rc;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci/**
1918c2ecf20Sopenharmony_ci * tpm_ibmvtpm_send() - Send a TPM command
1928c2ecf20Sopenharmony_ci * @chip:	tpm chip struct
1938c2ecf20Sopenharmony_ci * @buf:	buffer contains data to send
1948c2ecf20Sopenharmony_ci * @count:	size of buffer
1958c2ecf20Sopenharmony_ci *
1968c2ecf20Sopenharmony_ci * Return:
1978c2ecf20Sopenharmony_ci *   0 on success,
1988c2ecf20Sopenharmony_ci *   -errno on error
1998c2ecf20Sopenharmony_ci */
2008c2ecf20Sopenharmony_cistatic int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
2038c2ecf20Sopenharmony_ci	bool retry = true;
2048c2ecf20Sopenharmony_ci	int rc, sig;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	if (!ibmvtpm->rtce_buf) {
2078c2ecf20Sopenharmony_ci		dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n");
2088c2ecf20Sopenharmony_ci		return 0;
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	if (count > ibmvtpm->rtce_size) {
2128c2ecf20Sopenharmony_ci		dev_err(ibmvtpm->dev,
2138c2ecf20Sopenharmony_ci			"Invalid size in send: count=%zd, rtce_size=%d\n",
2148c2ecf20Sopenharmony_ci			count, ibmvtpm->rtce_size);
2158c2ecf20Sopenharmony_ci		return -EIO;
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	if (ibmvtpm->tpm_processing_cmd) {
2198c2ecf20Sopenharmony_ci		dev_info(ibmvtpm->dev,
2208c2ecf20Sopenharmony_ci		         "Need to wait for TPM to finish\n");
2218c2ecf20Sopenharmony_ci		/* wait for previous command to finish */
2228c2ecf20Sopenharmony_ci		sig = wait_event_interruptible(ibmvtpm->wq, !ibmvtpm->tpm_processing_cmd);
2238c2ecf20Sopenharmony_ci		if (sig)
2248c2ecf20Sopenharmony_ci			return -EINTR;
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	spin_lock(&ibmvtpm->rtce_lock);
2288c2ecf20Sopenharmony_ci	ibmvtpm->res_len = 0;
2298c2ecf20Sopenharmony_ci	memcpy((void *)ibmvtpm->rtce_buf, (void *)buf, count);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	/*
2328c2ecf20Sopenharmony_ci	 * set the processing flag before the Hcall, since we may get the
2338c2ecf20Sopenharmony_ci	 * result (interrupt) before even being able to check rc.
2348c2ecf20Sopenharmony_ci	 */
2358c2ecf20Sopenharmony_ci	ibmvtpm->tpm_processing_cmd = 1;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ciagain:
2388c2ecf20Sopenharmony_ci	rc = ibmvtpm_send_crq(ibmvtpm->vdev,
2398c2ecf20Sopenharmony_ci			IBMVTPM_VALID_CMD, VTPM_TPM_COMMAND,
2408c2ecf20Sopenharmony_ci			count, ibmvtpm->rtce_dma_handle);
2418c2ecf20Sopenharmony_ci	if (rc != H_SUCCESS) {
2428c2ecf20Sopenharmony_ci		/*
2438c2ecf20Sopenharmony_ci		 * H_CLOSED can be returned after LPM resume.  Call
2448c2ecf20Sopenharmony_ci		 * tpm_ibmvtpm_resume() to re-enable the CRQ then retry
2458c2ecf20Sopenharmony_ci		 * ibmvtpm_send_crq() once before failing.
2468c2ecf20Sopenharmony_ci		 */
2478c2ecf20Sopenharmony_ci		if (rc == H_CLOSED && retry) {
2488c2ecf20Sopenharmony_ci			tpm_ibmvtpm_resume(ibmvtpm->dev);
2498c2ecf20Sopenharmony_ci			retry = false;
2508c2ecf20Sopenharmony_ci			goto again;
2518c2ecf20Sopenharmony_ci		}
2528c2ecf20Sopenharmony_ci		dev_err(ibmvtpm->dev, "tpm_ibmvtpm_send failed rc=%d\n", rc);
2538c2ecf20Sopenharmony_ci		ibmvtpm->tpm_processing_cmd = 0;
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	spin_unlock(&ibmvtpm->rtce_lock);
2578c2ecf20Sopenharmony_ci	return 0;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic void tpm_ibmvtpm_cancel(struct tpm_chip *chip)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	return;
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic u8 tpm_ibmvtpm_status(struct tpm_chip *chip)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	return ibmvtpm->tpm_processing_cmd;
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci/**
2738c2ecf20Sopenharmony_ci * ibmvtpm_crq_get_rtce_size - Send a CRQ request to get rtce size
2748c2ecf20Sopenharmony_ci *
2758c2ecf20Sopenharmony_ci * @ibmvtpm:	vtpm device struct
2768c2ecf20Sopenharmony_ci *
2778c2ecf20Sopenharmony_ci * Return:
2788c2ecf20Sopenharmony_ci *	0 on success.
2798c2ecf20Sopenharmony_ci *	Non-zero on failure.
2808c2ecf20Sopenharmony_ci */
2818c2ecf20Sopenharmony_cistatic int ibmvtpm_crq_get_rtce_size(struct ibmvtpm_dev *ibmvtpm)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	int rc;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	rc = ibmvtpm_send_crq(ibmvtpm->vdev,
2868c2ecf20Sopenharmony_ci			IBMVTPM_VALID_CMD, VTPM_GET_RTCE_BUFFER_SIZE, 0, 0);
2878c2ecf20Sopenharmony_ci	if (rc != H_SUCCESS)
2888c2ecf20Sopenharmony_ci		dev_err(ibmvtpm->dev,
2898c2ecf20Sopenharmony_ci			"ibmvtpm_crq_get_rtce_size failed rc=%d\n", rc);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	return rc;
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci/**
2958c2ecf20Sopenharmony_ci * ibmvtpm_crq_get_version - Send a CRQ request to get vtpm version
2968c2ecf20Sopenharmony_ci *			   - Note that this is vtpm version and not tpm version
2978c2ecf20Sopenharmony_ci *
2988c2ecf20Sopenharmony_ci * @ibmvtpm:	vtpm device struct
2998c2ecf20Sopenharmony_ci *
3008c2ecf20Sopenharmony_ci * Return:
3018c2ecf20Sopenharmony_ci *	0 on success.
3028c2ecf20Sopenharmony_ci *	Non-zero on failure.
3038c2ecf20Sopenharmony_ci */
3048c2ecf20Sopenharmony_cistatic int ibmvtpm_crq_get_version(struct ibmvtpm_dev *ibmvtpm)
3058c2ecf20Sopenharmony_ci{
3068c2ecf20Sopenharmony_ci	int rc;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	rc = ibmvtpm_send_crq(ibmvtpm->vdev,
3098c2ecf20Sopenharmony_ci			IBMVTPM_VALID_CMD, VTPM_GET_VERSION, 0, 0);
3108c2ecf20Sopenharmony_ci	if (rc != H_SUCCESS)
3118c2ecf20Sopenharmony_ci		dev_err(ibmvtpm->dev,
3128c2ecf20Sopenharmony_ci			"ibmvtpm_crq_get_version failed rc=%d\n", rc);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	return rc;
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci/**
3188c2ecf20Sopenharmony_ci * ibmvtpm_crq_send_init_complete - Send a CRQ initialize complete message
3198c2ecf20Sopenharmony_ci * @ibmvtpm:	vtpm device struct
3208c2ecf20Sopenharmony_ci *
3218c2ecf20Sopenharmony_ci * Return:
3228c2ecf20Sopenharmony_ci *	0 on success.
3238c2ecf20Sopenharmony_ci *	Non-zero on failure.
3248c2ecf20Sopenharmony_ci */
3258c2ecf20Sopenharmony_cistatic int ibmvtpm_crq_send_init_complete(struct ibmvtpm_dev *ibmvtpm)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	int rc;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	rc = ibmvtpm_send_crq_word(ibmvtpm->vdev, INIT_CRQ_COMP_CMD);
3308c2ecf20Sopenharmony_ci	if (rc != H_SUCCESS)
3318c2ecf20Sopenharmony_ci		dev_err(ibmvtpm->dev,
3328c2ecf20Sopenharmony_ci			"ibmvtpm_crq_send_init_complete failed rc=%d\n", rc);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	return rc;
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci/**
3388c2ecf20Sopenharmony_ci * tpm_ibmvtpm_remove - ibm vtpm remove entry point
3398c2ecf20Sopenharmony_ci * @vdev:	vio device struct
3408c2ecf20Sopenharmony_ci *
3418c2ecf20Sopenharmony_ci * Return: Always 0.
3428c2ecf20Sopenharmony_ci */
3438c2ecf20Sopenharmony_cistatic int tpm_ibmvtpm_remove(struct vio_dev *vdev)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	struct tpm_chip *chip = dev_get_drvdata(&vdev->dev);
3468c2ecf20Sopenharmony_ci	struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
3478c2ecf20Sopenharmony_ci	int rc = 0;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	tpm_chip_unregister(chip);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	free_irq(vdev->irq, ibmvtpm);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	do {
3548c2ecf20Sopenharmony_ci		if (rc)
3558c2ecf20Sopenharmony_ci			msleep(100);
3568c2ecf20Sopenharmony_ci		rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
3578c2ecf20Sopenharmony_ci	} while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	dma_unmap_single(ibmvtpm->dev, ibmvtpm->crq_dma_handle,
3608c2ecf20Sopenharmony_ci			 CRQ_RES_BUF_SIZE, DMA_BIDIRECTIONAL);
3618c2ecf20Sopenharmony_ci	free_page((unsigned long)ibmvtpm->crq_queue.crq_addr);
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	if (ibmvtpm->rtce_buf) {
3648c2ecf20Sopenharmony_ci		dma_unmap_single(ibmvtpm->dev, ibmvtpm->rtce_dma_handle,
3658c2ecf20Sopenharmony_ci				 ibmvtpm->rtce_size, DMA_BIDIRECTIONAL);
3668c2ecf20Sopenharmony_ci		kfree(ibmvtpm->rtce_buf);
3678c2ecf20Sopenharmony_ci	}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	kfree(ibmvtpm);
3708c2ecf20Sopenharmony_ci	/* For tpm_ibmvtpm_get_desired_dma */
3718c2ecf20Sopenharmony_ci	dev_set_drvdata(&vdev->dev, NULL);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	return 0;
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci/**
3778c2ecf20Sopenharmony_ci * tpm_ibmvtpm_get_desired_dma - Get DMA size needed by this driver
3788c2ecf20Sopenharmony_ci * @vdev:	vio device struct
3798c2ecf20Sopenharmony_ci *
3808c2ecf20Sopenharmony_ci * Return:
3818c2ecf20Sopenharmony_ci *	Number of bytes the driver needs to DMA map.
3828c2ecf20Sopenharmony_ci */
3838c2ecf20Sopenharmony_cistatic unsigned long tpm_ibmvtpm_get_desired_dma(struct vio_dev *vdev)
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	struct tpm_chip *chip = dev_get_drvdata(&vdev->dev);
3868c2ecf20Sopenharmony_ci	struct ibmvtpm_dev *ibmvtpm;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	/*
3898c2ecf20Sopenharmony_ci	 * ibmvtpm initializes at probe time, so the data we are
3908c2ecf20Sopenharmony_ci	 * asking for may not be set yet. Estimate that 4K required
3918c2ecf20Sopenharmony_ci	 * for TCE-mapped buffer in addition to CRQ.
3928c2ecf20Sopenharmony_ci	 */
3938c2ecf20Sopenharmony_ci	if (chip)
3948c2ecf20Sopenharmony_ci		ibmvtpm = dev_get_drvdata(&chip->dev);
3958c2ecf20Sopenharmony_ci	else
3968c2ecf20Sopenharmony_ci		return CRQ_RES_BUF_SIZE + PAGE_SIZE;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	return CRQ_RES_BUF_SIZE + ibmvtpm->rtce_size;
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci/**
4028c2ecf20Sopenharmony_ci * tpm_ibmvtpm_suspend - Suspend
4038c2ecf20Sopenharmony_ci * @dev:	device struct
4048c2ecf20Sopenharmony_ci *
4058c2ecf20Sopenharmony_ci * Return: Always 0.
4068c2ecf20Sopenharmony_ci */
4078c2ecf20Sopenharmony_cistatic int tpm_ibmvtpm_suspend(struct device *dev)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	struct tpm_chip *chip = dev_get_drvdata(dev);
4108c2ecf20Sopenharmony_ci	struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
4118c2ecf20Sopenharmony_ci	int rc = 0;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	rc = ibmvtpm_send_crq(ibmvtpm->vdev,
4148c2ecf20Sopenharmony_ci			IBMVTPM_VALID_CMD, VTPM_PREPARE_TO_SUSPEND, 0, 0);
4158c2ecf20Sopenharmony_ci	if (rc != H_SUCCESS)
4168c2ecf20Sopenharmony_ci		dev_err(ibmvtpm->dev,
4178c2ecf20Sopenharmony_ci			"tpm_ibmvtpm_suspend failed rc=%d\n", rc);
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	return rc;
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci/**
4238c2ecf20Sopenharmony_ci * ibmvtpm_reset_crq - Reset CRQ
4248c2ecf20Sopenharmony_ci *
4258c2ecf20Sopenharmony_ci * @ibmvtpm:	ibm vtpm struct
4268c2ecf20Sopenharmony_ci *
4278c2ecf20Sopenharmony_ci * Return:
4288c2ecf20Sopenharmony_ci *	0 on success.
4298c2ecf20Sopenharmony_ci *	Non-zero on failure.
4308c2ecf20Sopenharmony_ci */
4318c2ecf20Sopenharmony_cistatic int ibmvtpm_reset_crq(struct ibmvtpm_dev *ibmvtpm)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	int rc = 0;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	do {
4368c2ecf20Sopenharmony_ci		if (rc)
4378c2ecf20Sopenharmony_ci			msleep(100);
4388c2ecf20Sopenharmony_ci		rc = plpar_hcall_norets(H_FREE_CRQ,
4398c2ecf20Sopenharmony_ci					ibmvtpm->vdev->unit_address);
4408c2ecf20Sopenharmony_ci	} while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	memset(ibmvtpm->crq_queue.crq_addr, 0, CRQ_RES_BUF_SIZE);
4438c2ecf20Sopenharmony_ci	ibmvtpm->crq_queue.index = 0;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	return plpar_hcall_norets(H_REG_CRQ, ibmvtpm->vdev->unit_address,
4468c2ecf20Sopenharmony_ci				  ibmvtpm->crq_dma_handle, CRQ_RES_BUF_SIZE);
4478c2ecf20Sopenharmony_ci}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_cistatic bool tpm_ibmvtpm_req_canceled(struct tpm_chip *chip, u8 status)
4508c2ecf20Sopenharmony_ci{
4518c2ecf20Sopenharmony_ci	return (status == 0);
4528c2ecf20Sopenharmony_ci}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_cistatic const struct tpm_class_ops tpm_ibmvtpm = {
4558c2ecf20Sopenharmony_ci	.recv = tpm_ibmvtpm_recv,
4568c2ecf20Sopenharmony_ci	.send = tpm_ibmvtpm_send,
4578c2ecf20Sopenharmony_ci	.cancel = tpm_ibmvtpm_cancel,
4588c2ecf20Sopenharmony_ci	.status = tpm_ibmvtpm_status,
4598c2ecf20Sopenharmony_ci	.req_complete_mask = 1,
4608c2ecf20Sopenharmony_ci	.req_complete_val = 0,
4618c2ecf20Sopenharmony_ci	.req_canceled = tpm_ibmvtpm_req_canceled,
4628c2ecf20Sopenharmony_ci};
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_cistatic const struct dev_pm_ops tpm_ibmvtpm_pm_ops = {
4658c2ecf20Sopenharmony_ci	.suspend = tpm_ibmvtpm_suspend,
4668c2ecf20Sopenharmony_ci	.resume = tpm_ibmvtpm_resume,
4678c2ecf20Sopenharmony_ci};
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci/**
4708c2ecf20Sopenharmony_ci * ibmvtpm_crq_get_next - Get next responded crq
4718c2ecf20Sopenharmony_ci *
4728c2ecf20Sopenharmony_ci * @ibmvtpm:	vtpm device struct
4738c2ecf20Sopenharmony_ci *
4748c2ecf20Sopenharmony_ci * Return: vtpm crq pointer or NULL.
4758c2ecf20Sopenharmony_ci */
4768c2ecf20Sopenharmony_cistatic struct ibmvtpm_crq *ibmvtpm_crq_get_next(struct ibmvtpm_dev *ibmvtpm)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	struct ibmvtpm_crq_queue *crq_q = &ibmvtpm->crq_queue;
4798c2ecf20Sopenharmony_ci	struct ibmvtpm_crq *crq = &crq_q->crq_addr[crq_q->index];
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	if (crq->valid & VTPM_MSG_RES) {
4828c2ecf20Sopenharmony_ci		if (++crq_q->index == crq_q->num_entry)
4838c2ecf20Sopenharmony_ci			crq_q->index = 0;
4848c2ecf20Sopenharmony_ci		smp_rmb();
4858c2ecf20Sopenharmony_ci	} else
4868c2ecf20Sopenharmony_ci		crq = NULL;
4878c2ecf20Sopenharmony_ci	return crq;
4888c2ecf20Sopenharmony_ci}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci/**
4918c2ecf20Sopenharmony_ci * ibmvtpm_crq_process - Process responded crq
4928c2ecf20Sopenharmony_ci *
4938c2ecf20Sopenharmony_ci * @crq:	crq to be processed
4948c2ecf20Sopenharmony_ci * @ibmvtpm:	vtpm device struct
4958c2ecf20Sopenharmony_ci *
4968c2ecf20Sopenharmony_ci */
4978c2ecf20Sopenharmony_cistatic void ibmvtpm_crq_process(struct ibmvtpm_crq *crq,
4988c2ecf20Sopenharmony_ci				struct ibmvtpm_dev *ibmvtpm)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci	int rc = 0;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	switch (crq->valid) {
5038c2ecf20Sopenharmony_ci	case VALID_INIT_CRQ:
5048c2ecf20Sopenharmony_ci		switch (crq->msg) {
5058c2ecf20Sopenharmony_ci		case INIT_CRQ_RES:
5068c2ecf20Sopenharmony_ci			dev_info(ibmvtpm->dev, "CRQ initialized\n");
5078c2ecf20Sopenharmony_ci			rc = ibmvtpm_crq_send_init_complete(ibmvtpm);
5088c2ecf20Sopenharmony_ci			if (rc)
5098c2ecf20Sopenharmony_ci				dev_err(ibmvtpm->dev, "Unable to send CRQ init complete rc=%d\n", rc);
5108c2ecf20Sopenharmony_ci			return;
5118c2ecf20Sopenharmony_ci		case INIT_CRQ_COMP_RES:
5128c2ecf20Sopenharmony_ci			dev_info(ibmvtpm->dev,
5138c2ecf20Sopenharmony_ci				 "CRQ initialization completed\n");
5148c2ecf20Sopenharmony_ci			return;
5158c2ecf20Sopenharmony_ci		default:
5168c2ecf20Sopenharmony_ci			dev_err(ibmvtpm->dev, "Unknown crq message type: %d\n", crq->msg);
5178c2ecf20Sopenharmony_ci			return;
5188c2ecf20Sopenharmony_ci		}
5198c2ecf20Sopenharmony_ci	case IBMVTPM_VALID_CMD:
5208c2ecf20Sopenharmony_ci		switch (crq->msg) {
5218c2ecf20Sopenharmony_ci		case VTPM_GET_RTCE_BUFFER_SIZE_RES:
5228c2ecf20Sopenharmony_ci			if (be16_to_cpu(crq->len) <= 0) {
5238c2ecf20Sopenharmony_ci				dev_err(ibmvtpm->dev, "Invalid rtce size\n");
5248c2ecf20Sopenharmony_ci				return;
5258c2ecf20Sopenharmony_ci			}
5268c2ecf20Sopenharmony_ci			ibmvtpm->rtce_size = be16_to_cpu(crq->len);
5278c2ecf20Sopenharmony_ci			ibmvtpm->rtce_buf = kmalloc(ibmvtpm->rtce_size,
5288c2ecf20Sopenharmony_ci						    GFP_ATOMIC);
5298c2ecf20Sopenharmony_ci			if (!ibmvtpm->rtce_buf) {
5308c2ecf20Sopenharmony_ci				dev_err(ibmvtpm->dev, "Failed to allocate memory for rtce buffer\n");
5318c2ecf20Sopenharmony_ci				return;
5328c2ecf20Sopenharmony_ci			}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci			ibmvtpm->rtce_dma_handle = dma_map_single(ibmvtpm->dev,
5358c2ecf20Sopenharmony_ci				ibmvtpm->rtce_buf, ibmvtpm->rtce_size,
5368c2ecf20Sopenharmony_ci				DMA_BIDIRECTIONAL);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci			if (dma_mapping_error(ibmvtpm->dev,
5398c2ecf20Sopenharmony_ci					      ibmvtpm->rtce_dma_handle)) {
5408c2ecf20Sopenharmony_ci				kfree(ibmvtpm->rtce_buf);
5418c2ecf20Sopenharmony_ci				ibmvtpm->rtce_buf = NULL;
5428c2ecf20Sopenharmony_ci				dev_err(ibmvtpm->dev, "Failed to dma map rtce buffer\n");
5438c2ecf20Sopenharmony_ci			}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci			return;
5468c2ecf20Sopenharmony_ci		case VTPM_GET_VERSION_RES:
5478c2ecf20Sopenharmony_ci			ibmvtpm->vtpm_version = be32_to_cpu(crq->data);
5488c2ecf20Sopenharmony_ci			return;
5498c2ecf20Sopenharmony_ci		case VTPM_TPM_COMMAND_RES:
5508c2ecf20Sopenharmony_ci			/* len of the data in rtce buffer */
5518c2ecf20Sopenharmony_ci			ibmvtpm->res_len = be16_to_cpu(crq->len);
5528c2ecf20Sopenharmony_ci			ibmvtpm->tpm_processing_cmd = 0;
5538c2ecf20Sopenharmony_ci			wake_up_interruptible(&ibmvtpm->wq);
5548c2ecf20Sopenharmony_ci			return;
5558c2ecf20Sopenharmony_ci		default:
5568c2ecf20Sopenharmony_ci			return;
5578c2ecf20Sopenharmony_ci		}
5588c2ecf20Sopenharmony_ci	}
5598c2ecf20Sopenharmony_ci	return;
5608c2ecf20Sopenharmony_ci}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci/**
5638c2ecf20Sopenharmony_ci * ibmvtpm_interrupt -	Interrupt handler
5648c2ecf20Sopenharmony_ci *
5658c2ecf20Sopenharmony_ci * @irq:		irq number to handle
5668c2ecf20Sopenharmony_ci * @vtpm_instance:	vtpm that received interrupt
5678c2ecf20Sopenharmony_ci *
5688c2ecf20Sopenharmony_ci * Returns:
5698c2ecf20Sopenharmony_ci *	IRQ_HANDLED
5708c2ecf20Sopenharmony_ci **/
5718c2ecf20Sopenharmony_cistatic irqreturn_t ibmvtpm_interrupt(int irq, void *vtpm_instance)
5728c2ecf20Sopenharmony_ci{
5738c2ecf20Sopenharmony_ci	struct ibmvtpm_dev *ibmvtpm = (struct ibmvtpm_dev *) vtpm_instance;
5748c2ecf20Sopenharmony_ci	struct ibmvtpm_crq *crq;
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	/* while loop is needed for initial setup (get version and
5778c2ecf20Sopenharmony_ci	 * get rtce_size). There should be only one tpm request at any
5788c2ecf20Sopenharmony_ci	 * given time.
5798c2ecf20Sopenharmony_ci	 */
5808c2ecf20Sopenharmony_ci	while ((crq = ibmvtpm_crq_get_next(ibmvtpm)) != NULL) {
5818c2ecf20Sopenharmony_ci		ibmvtpm_crq_process(crq, ibmvtpm);
5828c2ecf20Sopenharmony_ci		wake_up_interruptible(&ibmvtpm->crq_queue.wq);
5838c2ecf20Sopenharmony_ci		crq->valid = 0;
5848c2ecf20Sopenharmony_ci		smp_wmb();
5858c2ecf20Sopenharmony_ci	}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
5888c2ecf20Sopenharmony_ci}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci/**
5918c2ecf20Sopenharmony_ci * tpm_ibmvtpm_probe - ibm vtpm initialize entry point
5928c2ecf20Sopenharmony_ci *
5938c2ecf20Sopenharmony_ci * @vio_dev:	vio device struct
5948c2ecf20Sopenharmony_ci * @id:		vio device id struct
5958c2ecf20Sopenharmony_ci *
5968c2ecf20Sopenharmony_ci * Return:
5978c2ecf20Sopenharmony_ci *	0 on success.
5988c2ecf20Sopenharmony_ci *	Non-zero on failure.
5998c2ecf20Sopenharmony_ci */
6008c2ecf20Sopenharmony_cistatic int tpm_ibmvtpm_probe(struct vio_dev *vio_dev,
6018c2ecf20Sopenharmony_ci				   const struct vio_device_id *id)
6028c2ecf20Sopenharmony_ci{
6038c2ecf20Sopenharmony_ci	struct ibmvtpm_dev *ibmvtpm;
6048c2ecf20Sopenharmony_ci	struct device *dev = &vio_dev->dev;
6058c2ecf20Sopenharmony_ci	struct ibmvtpm_crq_queue *crq_q;
6068c2ecf20Sopenharmony_ci	struct tpm_chip *chip;
6078c2ecf20Sopenharmony_ci	int rc = -ENOMEM, rc1;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	chip = tpmm_chip_alloc(dev, &tpm_ibmvtpm);
6108c2ecf20Sopenharmony_ci	if (IS_ERR(chip))
6118c2ecf20Sopenharmony_ci		return PTR_ERR(chip);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	ibmvtpm = kzalloc(sizeof(struct ibmvtpm_dev), GFP_KERNEL);
6148c2ecf20Sopenharmony_ci	if (!ibmvtpm) {
6158c2ecf20Sopenharmony_ci		dev_err(dev, "kzalloc for ibmvtpm failed\n");
6168c2ecf20Sopenharmony_ci		goto cleanup;
6178c2ecf20Sopenharmony_ci	}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	ibmvtpm->dev = dev;
6208c2ecf20Sopenharmony_ci	ibmvtpm->vdev = vio_dev;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	crq_q = &ibmvtpm->crq_queue;
6238c2ecf20Sopenharmony_ci	crq_q->crq_addr = (struct ibmvtpm_crq *)get_zeroed_page(GFP_KERNEL);
6248c2ecf20Sopenharmony_ci	if (!crq_q->crq_addr) {
6258c2ecf20Sopenharmony_ci		dev_err(dev, "Unable to allocate memory for crq_addr\n");
6268c2ecf20Sopenharmony_ci		goto cleanup;
6278c2ecf20Sopenharmony_ci	}
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	crq_q->num_entry = CRQ_RES_BUF_SIZE / sizeof(*crq_q->crq_addr);
6308c2ecf20Sopenharmony_ci	init_waitqueue_head(&crq_q->wq);
6318c2ecf20Sopenharmony_ci	ibmvtpm->crq_dma_handle = dma_map_single(dev, crq_q->crq_addr,
6328c2ecf20Sopenharmony_ci						 CRQ_RES_BUF_SIZE,
6338c2ecf20Sopenharmony_ci						 DMA_BIDIRECTIONAL);
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	if (dma_mapping_error(dev, ibmvtpm->crq_dma_handle)) {
6368c2ecf20Sopenharmony_ci		dev_err(dev, "dma mapping failed\n");
6378c2ecf20Sopenharmony_ci		goto cleanup;
6388c2ecf20Sopenharmony_ci	}
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	rc = plpar_hcall_norets(H_REG_CRQ, vio_dev->unit_address,
6418c2ecf20Sopenharmony_ci				ibmvtpm->crq_dma_handle, CRQ_RES_BUF_SIZE);
6428c2ecf20Sopenharmony_ci	if (rc == H_RESOURCE)
6438c2ecf20Sopenharmony_ci		rc = ibmvtpm_reset_crq(ibmvtpm);
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	if (rc) {
6468c2ecf20Sopenharmony_ci		dev_err(dev, "Unable to register CRQ rc=%d\n", rc);
6478c2ecf20Sopenharmony_ci		goto reg_crq_cleanup;
6488c2ecf20Sopenharmony_ci	}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	rc = request_irq(vio_dev->irq, ibmvtpm_interrupt, 0,
6518c2ecf20Sopenharmony_ci			 tpm_ibmvtpm_driver_name, ibmvtpm);
6528c2ecf20Sopenharmony_ci	if (rc) {
6538c2ecf20Sopenharmony_ci		dev_err(dev, "Error %d register irq 0x%x\n", rc, vio_dev->irq);
6548c2ecf20Sopenharmony_ci		goto init_irq_cleanup;
6558c2ecf20Sopenharmony_ci	}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	rc = vio_enable_interrupts(vio_dev);
6588c2ecf20Sopenharmony_ci	if (rc) {
6598c2ecf20Sopenharmony_ci		dev_err(dev, "Error %d enabling interrupts\n", rc);
6608c2ecf20Sopenharmony_ci		goto init_irq_cleanup;
6618c2ecf20Sopenharmony_ci	}
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	init_waitqueue_head(&ibmvtpm->wq);
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	crq_q->index = 0;
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	dev_set_drvdata(&chip->dev, ibmvtpm);
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	spin_lock_init(&ibmvtpm->rtce_lock);
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	rc = ibmvtpm_crq_send_init(ibmvtpm);
6728c2ecf20Sopenharmony_ci	if (rc)
6738c2ecf20Sopenharmony_ci		goto init_irq_cleanup;
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	rc = ibmvtpm_crq_get_version(ibmvtpm);
6768c2ecf20Sopenharmony_ci	if (rc)
6778c2ecf20Sopenharmony_ci		goto init_irq_cleanup;
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	rc = ibmvtpm_crq_get_rtce_size(ibmvtpm);
6808c2ecf20Sopenharmony_ci	if (rc)
6818c2ecf20Sopenharmony_ci		goto init_irq_cleanup;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	if (!wait_event_timeout(ibmvtpm->crq_queue.wq,
6848c2ecf20Sopenharmony_ci				ibmvtpm->rtce_buf != NULL,
6858c2ecf20Sopenharmony_ci				HZ)) {
6868c2ecf20Sopenharmony_ci		rc = -ENODEV;
6878c2ecf20Sopenharmony_ci		dev_err(dev, "CRQ response timed out\n");
6888c2ecf20Sopenharmony_ci		goto init_irq_cleanup;
6898c2ecf20Sopenharmony_ci	}
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	if (!strcmp(id->compat, "IBM,vtpm20"))
6938c2ecf20Sopenharmony_ci		chip->flags |= TPM_CHIP_FLAG_TPM2;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	rc = tpm_get_timeouts(chip);
6968c2ecf20Sopenharmony_ci	if (rc)
6978c2ecf20Sopenharmony_ci		goto init_irq_cleanup;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	if (chip->flags & TPM_CHIP_FLAG_TPM2) {
7008c2ecf20Sopenharmony_ci		rc = tpm2_get_cc_attrs_tbl(chip);
7018c2ecf20Sopenharmony_ci		if (rc)
7028c2ecf20Sopenharmony_ci			goto init_irq_cleanup;
7038c2ecf20Sopenharmony_ci	}
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	return tpm_chip_register(chip);
7068c2ecf20Sopenharmony_ciinit_irq_cleanup:
7078c2ecf20Sopenharmony_ci	do {
7088c2ecf20Sopenharmony_ci		rc1 = plpar_hcall_norets(H_FREE_CRQ, vio_dev->unit_address);
7098c2ecf20Sopenharmony_ci	} while (rc1 == H_BUSY || H_IS_LONG_BUSY(rc1));
7108c2ecf20Sopenharmony_cireg_crq_cleanup:
7118c2ecf20Sopenharmony_ci	dma_unmap_single(dev, ibmvtpm->crq_dma_handle, CRQ_RES_BUF_SIZE,
7128c2ecf20Sopenharmony_ci			 DMA_BIDIRECTIONAL);
7138c2ecf20Sopenharmony_cicleanup:
7148c2ecf20Sopenharmony_ci	if (ibmvtpm) {
7158c2ecf20Sopenharmony_ci		if (crq_q->crq_addr)
7168c2ecf20Sopenharmony_ci			free_page((unsigned long)crq_q->crq_addr);
7178c2ecf20Sopenharmony_ci		kfree(ibmvtpm);
7188c2ecf20Sopenharmony_ci	}
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	return rc;
7218c2ecf20Sopenharmony_ci}
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_cistatic struct vio_driver ibmvtpm_driver = {
7248c2ecf20Sopenharmony_ci	.id_table	 = tpm_ibmvtpm_device_table,
7258c2ecf20Sopenharmony_ci	.probe		 = tpm_ibmvtpm_probe,
7268c2ecf20Sopenharmony_ci	.remove		 = tpm_ibmvtpm_remove,
7278c2ecf20Sopenharmony_ci	.get_desired_dma = tpm_ibmvtpm_get_desired_dma,
7288c2ecf20Sopenharmony_ci	.name		 = tpm_ibmvtpm_driver_name,
7298c2ecf20Sopenharmony_ci	.pm		 = &tpm_ibmvtpm_pm_ops,
7308c2ecf20Sopenharmony_ci};
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci/**
7338c2ecf20Sopenharmony_ci * ibmvtpm_module_init - Initialize ibm vtpm module.
7348c2ecf20Sopenharmony_ci *
7358c2ecf20Sopenharmony_ci *
7368c2ecf20Sopenharmony_ci * Return:
7378c2ecf20Sopenharmony_ci *	0 on success.
7388c2ecf20Sopenharmony_ci *	Non-zero on failure.
7398c2ecf20Sopenharmony_ci */
7408c2ecf20Sopenharmony_cistatic int __init ibmvtpm_module_init(void)
7418c2ecf20Sopenharmony_ci{
7428c2ecf20Sopenharmony_ci	return vio_register_driver(&ibmvtpm_driver);
7438c2ecf20Sopenharmony_ci}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci/**
7468c2ecf20Sopenharmony_ci * ibmvtpm_module_exit - Tear down ibm vtpm module.
7478c2ecf20Sopenharmony_ci */
7488c2ecf20Sopenharmony_cistatic void __exit ibmvtpm_module_exit(void)
7498c2ecf20Sopenharmony_ci{
7508c2ecf20Sopenharmony_ci	vio_unregister_driver(&ibmvtpm_driver);
7518c2ecf20Sopenharmony_ci}
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_cimodule_init(ibmvtpm_module_init);
7548c2ecf20Sopenharmony_cimodule_exit(ibmvtpm_module_exit);
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ciMODULE_AUTHOR("adlai@us.ibm.com");
7578c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IBM vTPM Driver");
7588c2ecf20Sopenharmony_ciMODULE_VERSION("1.0");
7598c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
760