tpm_nsc.c 9.7 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
 * Copyright (C) 2004 IBM Corporation
 *
 * Authors:
 * Leendert van Doorn <leendert@watson.ibm.com>
 * Dave Safford <safford@watson.ibm.com>
 * Reiner Sailer <sailer@watson.ibm.com>
 * Kylene Hall <kjhall@us.ibm.com>
 *
 * Maintained by: <tpmdd_devel@lists.sourceforge.net>
 *
 * Device driver for TCG/TCPA TPM (trusted platform module).
 * Specifications at www.trustedcomputinggroup.org	 
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, version 2 of the
 * License.
 * 
 */

C
Chris Wright 已提交
22
#include <linux/platform_device.h>
L
Linus Torvalds 已提交
23 24 25
#include "tpm.h"

/* National definitions */
26 27 28 29 30 31
enum tpm_nsc_addr{
	TPM_NSC_IRQ = 0x07,
	TPM_NSC_BASE0_HI = 0x60,
	TPM_NSC_BASE0_LO = 0x61,
	TPM_NSC_BASE1_HI = 0x62,
	TPM_NSC_BASE1_LO = 0x63
32
};
L
Linus Torvalds 已提交
33

34 35 36 37 38 39 40 41 42
enum tpm_nsc_index {
	NSC_LDN_INDEX = 0x07,
	NSC_SID_INDEX = 0x20,
	NSC_LDC_INDEX = 0x30,
	NSC_DIO_INDEX = 0x60,
	NSC_CIO_INDEX = 0x62,
	NSC_IRQ_INDEX = 0x70,
	NSC_ITS_INDEX = 0x71
};
L
Linus Torvalds 已提交
43

44 45 46 47 48
enum tpm_nsc_status_loc {
	NSC_STATUS = 0x01,
	NSC_COMMAND = 0x01,
	NSC_DATA = 0x00
};
L
Linus Torvalds 已提交
49 50

/* status bits */
51
enum tpm_nsc_status {
52 53 54 55 56 57 58
	NSC_STATUS_OBF = 0x01,	/* output buffer full */
	NSC_STATUS_IBF = 0x02,	/* input buffer full */
	NSC_STATUS_F0 = 0x04,	/* F0 */
	NSC_STATUS_A2 = 0x08,	/* A2 */
	NSC_STATUS_RDY = 0x10,	/* ready to receive command */
	NSC_STATUS_IBR = 0x20	/* ready to receive data */
};
59

L
Linus Torvalds 已提交
60
/* command bits */
61 62 63 64 65
enum tpm_nsc_cmd_mode {
	NSC_COMMAND_NORMAL = 0x01,	/* normal mode */
	NSC_COMMAND_EOC = 0x03,
	NSC_COMMAND_CANCEL = 0x22
};
L
Linus Torvalds 已提交
66 67 68 69 70
/*
 * Wait for a certain status to appear
 */
static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data)
{
71
	unsigned long stop;
L
Linus Torvalds 已提交
72 73

	/* status immediately available check */
K
Kylene Jo Hall 已提交
74
	*data = inb(chip->vendor.base + NSC_STATUS);
L
Linus Torvalds 已提交
75 76 77 78
	if ((*data & mask) == val)
		return 0;

	/* wait for status */
79
	stop = jiffies + 10 * HZ;
L
Linus Torvalds 已提交
80
	do {
81
		msleep(TPM_TIMEOUT);
K
Kylene Jo Hall 已提交
82
		*data = inb(chip->vendor.base + 1);
83
		if ((*data & mask) == val)
L
Linus Torvalds 已提交
84 85
			return 0;
	}
86
	while (time_before(jiffies, stop));
L
Linus Torvalds 已提交
87 88 89 90 91 92 93

	return -EBUSY;
}

static int nsc_wait_for_ready(struct tpm_chip *chip)
{
	int status;
94
	unsigned long stop;
L
Linus Torvalds 已提交
95 96

	/* status immediately available check */
K
Kylene Jo Hall 已提交
97
	status = inb(chip->vendor.base + NSC_STATUS);
L
Linus Torvalds 已提交
98
	if (status & NSC_STATUS_OBF)
K
Kylene Jo Hall 已提交
99
		status = inb(chip->vendor.base + NSC_DATA);
L
Linus Torvalds 已提交
100 101 102 103
	if (status & NSC_STATUS_RDY)
		return 0;

	/* wait for status */
104
	stop = jiffies + 100;
L
Linus Torvalds 已提交
105
	do {
106
		msleep(TPM_TIMEOUT);
K
Kylene Jo Hall 已提交
107
		status = inb(chip->vendor.base + NSC_STATUS);
L
Linus Torvalds 已提交
108
		if (status & NSC_STATUS_OBF)
K
Kylene Jo Hall 已提交
109
			status = inb(chip->vendor.base + NSC_DATA);
110
		if (status & NSC_STATUS_RDY)
L
Linus Torvalds 已提交
111 112
			return 0;
	}
113
	while (time_before(jiffies, stop));
L
Linus Torvalds 已提交
114

115
	dev_info(chip->dev, "wait for ready failed\n");
L
Linus Torvalds 已提交
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
	return -EBUSY;
}


static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count)
{
	u8 *buffer = buf;
	u8 data, *p;
	u32 size;
	__be32 *native_size;

	if (count < 6)
		return -EIO;

	if (wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0) {
131
		dev_err(chip->dev, "F0 timeout\n");
L
Linus Torvalds 已提交
132 133 134
		return -EIO;
	}
	if ((data =
K
Kylene Jo Hall 已提交
135
	     inb(chip->vendor.base + NSC_DATA)) != NSC_COMMAND_NORMAL) {
136
		dev_err(chip->dev, "not in normal mode (0x%x)\n",
L
Linus Torvalds 已提交
137 138 139 140 141 142 143 144
			data);
		return -EIO;
	}

	/* read the whole packet */
	for (p = buffer; p < &buffer[count]; p++) {
		if (wait_for_stat
		    (chip, NSC_STATUS_OBF, NSC_STATUS_OBF, &data) < 0) {
145
			dev_err(chip->dev,
L
Linus Torvalds 已提交
146 147 148 149 150
				"OBF timeout (while reading data)\n");
			return -EIO;
		}
		if (data & NSC_STATUS_F0)
			break;
K
Kylene Jo Hall 已提交
151
		*p = inb(chip->vendor.base + NSC_DATA);
L
Linus Torvalds 已提交
152 153
	}

154 155
	if ((data & NSC_STATUS_F0) == 0 &&
	(wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0)) {
156
		dev_err(chip->dev, "F0 not set\n");
L
Linus Torvalds 已提交
157 158
		return -EIO;
	}
K
Kylene Jo Hall 已提交
159
	if ((data = inb(chip->vendor.base + NSC_DATA)) != NSC_COMMAND_EOC) {
160
		dev_err(chip->dev,
L
Linus Torvalds 已提交
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
			"expected end of command(0x%x)\n", data);
		return -EIO;
	}

	native_size = (__force __be32 *) (buf + 2);
	size = be32_to_cpu(*native_size);

	if (count < size)
		return -EIO;

	return size;
}

static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count)
{
	u8 data;
	int i;

	/*
	 * If we hit the chip with back to back commands it locks up
	 * and never set IBF. Hitting it with this "hammer" seems to
	 * fix it. Not sure why this is needed, we followed the flow
	 * chart in the manual to the letter.
	 */
K
Kylene Jo Hall 已提交
185
	outb(NSC_COMMAND_CANCEL, chip->vendor.base + NSC_COMMAND);
L
Linus Torvalds 已提交
186 187 188 189 190

	if (nsc_wait_for_ready(chip) != 0)
		return -EIO;

	if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
191
		dev_err(chip->dev, "IBF timeout\n");
L
Linus Torvalds 已提交
192 193 194
		return -EIO;
	}

K
Kylene Jo Hall 已提交
195
	outb(NSC_COMMAND_NORMAL, chip->vendor.base + NSC_COMMAND);
L
Linus Torvalds 已提交
196
	if (wait_for_stat(chip, NSC_STATUS_IBR, NSC_STATUS_IBR, &data) < 0) {
197
		dev_err(chip->dev, "IBR timeout\n");
L
Linus Torvalds 已提交
198 199 200 201 202
		return -EIO;
	}

	for (i = 0; i < count; i++) {
		if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
203
			dev_err(chip->dev,
L
Linus Torvalds 已提交
204 205 206
				"IBF timeout (while writing data)\n");
			return -EIO;
		}
K
Kylene Jo Hall 已提交
207
		outb(buf[i], chip->vendor.base + NSC_DATA);
L
Linus Torvalds 已提交
208 209 210
	}

	if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
211
		dev_err(chip->dev, "IBF timeout\n");
L
Linus Torvalds 已提交
212 213
		return -EIO;
	}
K
Kylene Jo Hall 已提交
214
	outb(NSC_COMMAND_EOC, chip->vendor.base + NSC_COMMAND);
L
Linus Torvalds 已提交
215 216 217 218 219 220

	return count;
}

static void tpm_nsc_cancel(struct tpm_chip *chip)
{
K
Kylene Jo Hall 已提交
221
	outb(NSC_COMMAND_CANCEL, chip->vendor.base + NSC_COMMAND);
L
Linus Torvalds 已提交
222 223
}

224 225
static u8 tpm_nsc_status(struct tpm_chip *chip)
{
K
Kylene Jo Hall 已提交
226
	return inb(chip->vendor.base + NSC_STATUS);
227 228
}

L
Linus Torvalds 已提交
229 230 231 232 233 234 235 236 237
static struct file_operations nsc_ops = {
	.owner = THIS_MODULE,
	.llseek = no_llseek,
	.open = tpm_open,
	.read = tpm_read,
	.write = tpm_write,
	.release = tpm_release,
};

238 239 240 241 242 243 244 245 246 247
static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL);
static DEVICE_ATTR(cancel, S_IWUSR|S_IWGRP, NULL, tpm_store_cancel);

static struct attribute * nsc_attrs[] = {
	&dev_attr_pubek.attr,
	&dev_attr_pcrs.attr,
	&dev_attr_caps.attr,
	&dev_attr_cancel.attr,
248
	NULL,
249 250 251 252
};

static struct attribute_group nsc_attr_grp = { .attrs = nsc_attrs };

L
Linus Torvalds 已提交
253 254 255 256
static struct tpm_vendor_specific tpm_nsc = {
	.recv = tpm_nsc_recv,
	.send = tpm_nsc_send,
	.cancel = tpm_nsc_cancel,
257
	.status = tpm_nsc_status,
L
Linus Torvalds 已提交
258 259
	.req_complete_mask = NSC_STATUS_OBF,
	.req_complete_val = NSC_STATUS_OBF,
K
Kylene Hall 已提交
260
	.req_canceled = NSC_STATUS_RDY,
261
	.attr_group = &nsc_attr_grp,
L
Linus Torvalds 已提交
262 263 264
	.miscdev = { .fops = &nsc_ops, },
};

265 266 267 268 269 270
static struct platform_device *pdev = NULL;

static void __devexit tpm_nsc_remove(struct device *dev)
{
	struct tpm_chip *chip = dev_get_drvdata(dev);
	if ( chip ) {
K
Kylene Jo Hall 已提交
271
		release_region(chip->vendor.base, 2);
272 273 274 275 276 277 278 279 280 281 282 283 284
		tpm_remove_hardware(chip->dev);
	}
}

static struct device_driver nsc_drv = {
	.name = "tpm_nsc",
	.bus = &platform_bus_type,
	.owner = THIS_MODULE,
	.suspend = tpm_pm_suspend,
	.resume = tpm_pm_resume,
};

static int __init init_nsc(void)
L
Linus Torvalds 已提交
285 286
{
	int rc = 0;
287
	int lo, hi;
288
	int nscAddrBase = TPM_ADDR;
289

290

L
Linus Torvalds 已提交
291
	/* verify that it is a National part (SID) */
292 293 294
	if (tpm_read_index(TPM_ADDR, NSC_SID_INDEX) != 0xEF) {
		nscAddrBase = (tpm_read_index(TPM_SUPERIO_ADDR, 0x2C)<<8)|
			(tpm_read_index(TPM_SUPERIO_ADDR, 0x2B)&0xFE);
295 296
		if (tpm_read_index(nscAddrBase, NSC_SID_INDEX) != 0xF6)
			return -ENODEV;
L
Linus Torvalds 已提交
297 298
	}

299 300
	driver_register(&nsc_drv);

301 302 303 304
	hi = tpm_read_index(nscAddrBase, TPM_NSC_BASE0_HI);
	lo = tpm_read_index(nscAddrBase, TPM_NSC_BASE0_LO);
	tpm_nsc.base = (hi<<8) | lo;

305 306 307
	/* enable the DPM module */
	tpm_write_index(nscAddrBase, NSC_LDC_INDEX, 0x01);

308 309 310 311 312
	pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL);
	if (!pdev) {
		rc = -ENOMEM;
		goto err_unreg_drv;
	}
313 314 315 316 317 318 319

	pdev->name = "tpm_nscl0";
	pdev->id = -1;
	pdev->num_resources = 0;
	pdev->dev.release = tpm_nsc_remove;
	pdev->dev.driver = &nsc_drv;

320 321
	if ((rc = platform_device_register(pdev)) < 0)
		goto err_free_dev;
322 323

	if (request_region(tpm_nsc.base, 2, "tpm_nsc0") == NULL ) {
324 325
		rc = -EBUSY;
		goto err_unreg_dev;
326 327
	}

328 329
	if ((rc = tpm_register_hardware(&pdev->dev, &tpm_nsc)) < 0)
		goto err_rel_reg;
330 331 332

	dev_dbg(&pdev->dev, "NSC TPM detected\n");
	dev_dbg(&pdev->dev,
L
Linus Torvalds 已提交
333
		"NSC LDN 0x%x, SID 0x%x, SRID 0x%x\n",
334 335
		tpm_read_index(nscAddrBase,0x07), tpm_read_index(nscAddrBase,0x20),
		tpm_read_index(nscAddrBase,0x27));
336
	dev_dbg(&pdev->dev,
L
Linus Torvalds 已提交
337
		"NSC SIOCF1 0x%x SIOCF5 0x%x SIOCF6 0x%x SIOCF8 0x%x\n",
338 339
		tpm_read_index(nscAddrBase,0x21), tpm_read_index(nscAddrBase,0x25),
		tpm_read_index(nscAddrBase,0x26), tpm_read_index(nscAddrBase,0x28));
340
	dev_dbg(&pdev->dev, "NSC IO Base0 0x%x\n",
341
		(tpm_read_index(nscAddrBase,0x60) << 8) | tpm_read_index(nscAddrBase,0x61));
342
	dev_dbg(&pdev->dev, "NSC IO Base1 0x%x\n",
343
		(tpm_read_index(nscAddrBase,0x62) << 8) | tpm_read_index(nscAddrBase,0x63));
344
	dev_dbg(&pdev->dev, "NSC Interrupt number and wakeup 0x%x\n",
345
		tpm_read_index(nscAddrBase,0x70));
346
	dev_dbg(&pdev->dev, "NSC IRQ type select 0x%x\n",
347
		tpm_read_index(nscAddrBase,0x71));
348
	dev_dbg(&pdev->dev,
L
Linus Torvalds 已提交
349
		"NSC DMA channel select0 0x%x, select1 0x%x\n",
350
		tpm_read_index(nscAddrBase,0x74), tpm_read_index(nscAddrBase,0x75));
351
	dev_dbg(&pdev->dev,
L
Linus Torvalds 已提交
352 353
		"NSC Config "
		"0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
354 355 356 357 358
		tpm_read_index(nscAddrBase,0xF0), tpm_read_index(nscAddrBase,0xF1),
		tpm_read_index(nscAddrBase,0xF2), tpm_read_index(nscAddrBase,0xF3),
		tpm_read_index(nscAddrBase,0xF4), tpm_read_index(nscAddrBase,0xF5),
		tpm_read_index(nscAddrBase,0xF6), tpm_read_index(nscAddrBase,0xF7),
		tpm_read_index(nscAddrBase,0xF8), tpm_read_index(nscAddrBase,0xF9));
L
Linus Torvalds 已提交
359

360
	dev_info(&pdev->dev,
361 362
		 "NSC TPM revision %d\n",
		 tpm_read_index(nscAddrBase, 0x27) & 0x1F);
L
Linus Torvalds 已提交
363 364

	return 0;
365 366 367 368 369 370 371 372 373 374

err_rel_reg:
	release_region(tpm_nsc.base, 2);
err_unreg_dev:
	platform_device_unregister(pdev);
err_free_dev:
	kfree(pdev);
err_unreg_drv:
	driver_unregister(&nsc_drv);
	return rc;
L
Linus Torvalds 已提交
375 376 377 378
}

static void __exit cleanup_nsc(void)
{
379 380 381 382 383 384 385 386
	if (pdev) {
		tpm_nsc_remove(&pdev->dev);
		platform_device_unregister(pdev);
		kfree(pdev);
		pdev = NULL;
	}

	driver_unregister(&nsc_drv);
L
Linus Torvalds 已提交
387 388 389 390 391 392 393 394 395
}

module_init(init_nsc);
module_exit(cleanup_nsc);

MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
MODULE_DESCRIPTION("TPM Driver");
MODULE_VERSION("2.0");
MODULE_LICENSE("GPL");