tpm_nsc.c 10.0 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9
/*
 * 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>
 *
K
Kent Yoder 已提交
10
 * Maintained by: <tpmdd-devel@lists.sourceforge.net>
L
Linus Torvalds 已提交
11 12 13 14 15 16 17 18 19 20 21
 *
 * 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>
23
#include <linux/slab.h>
L
Linus Torvalds 已提交
24 25 26
#include "tpm.h"

/* National definitions */
27 28 29 30 31 32
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
33
};
L
Linus Torvalds 已提交
34

35 36 37 38 39 40 41 42 43
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 已提交
44

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

/* status bits */
52
enum tpm_nsc_status {
53 54 55 56 57 58 59
	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 */
};
60

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

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

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

	return -EBUSY;
}

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

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

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

116
	dev_info(chip->dev, "wait for ready failed\n");
L
Linus Torvalds 已提交
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
	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) {
132
		dev_err(chip->dev, "F0 timeout\n");
L
Linus Torvalds 已提交
133 134 135
		return -EIO;
	}
	if ((data =
K
Kylene Jo Hall 已提交
136
	     inb(chip->vendor.base + NSC_DATA)) != NSC_COMMAND_NORMAL) {
137
		dev_err(chip->dev, "not in normal mode (0x%x)\n",
L
Linus Torvalds 已提交
138 139 140 141 142 143 144 145
			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) {
146
			dev_err(chip->dev,
L
Linus Torvalds 已提交
147 148 149 150 151
				"OBF timeout (while reading data)\n");
			return -EIO;
		}
		if (data & NSC_STATUS_F0)
			break;
K
Kylene Jo Hall 已提交
152
		*p = inb(chip->vendor.base + NSC_DATA);
L
Linus Torvalds 已提交
153 154
	}

155 156
	if ((data & NSC_STATUS_F0) == 0 &&
	(wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0)) {
157
		dev_err(chip->dev, "F0 not set\n");
L
Linus Torvalds 已提交
158 159
		return -EIO;
	}
K
Kylene Jo Hall 已提交
160
	if ((data = inb(chip->vendor.base + NSC_DATA)) != NSC_COMMAND_EOC) {
161
		dev_err(chip->dev,
L
Linus Torvalds 已提交
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
			"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 已提交
186
	outb(NSC_COMMAND_CANCEL, chip->vendor.base + NSC_COMMAND);
L
Linus Torvalds 已提交
187 188 189 190 191

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

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

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

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

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

	return count;
}

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

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

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

239 240 241 242 243 244 245 246 247 248
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,
249
	NULL,
250 251 252 253
};

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

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

266 267
static struct platform_device *pdev = NULL;

S
Sam Ravnborg 已提交
268
static void tpm_nsc_remove(struct device *dev)
269 270 271
{
	struct tpm_chip *chip = dev_get_drvdata(dev);
	if ( chip ) {
K
Kylene Jo Hall 已提交
272
		release_region(chip->vendor.base, 2);
273 274 275 276
		tpm_remove_hardware(chip->dev);
	}
}

277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
static int tpm_nsc_suspend(struct platform_device *dev, pm_message_t msg)
{
	return tpm_pm_suspend(&dev->dev, msg);
}

static int tpm_nsc_resume(struct platform_device *dev)
{
	return tpm_pm_resume(&dev->dev);
}

static struct platform_driver nsc_drv = {
	.suspend         = tpm_nsc_suspend,
	.resume          = tpm_nsc_resume,
	.driver          = {
		.name    = "tpm_nsc",
		.owner   = THIS_MODULE,
	},
294 295 296
};

static int __init init_nsc(void)
L
Linus Torvalds 已提交
297 298
{
	int rc = 0;
J
Jeff Garzik 已提交
299
	int lo, hi, err;
300
	int nscAddrBase = TPM_ADDR;
301 302
	struct tpm_chip *chip;
	unsigned long base;
303

L
Linus Torvalds 已提交
304
	/* verify that it is a National part (SID) */
305 306 307
	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);
308 309
		if (tpm_read_index(nscAddrBase, NSC_SID_INDEX) != 0xF6)
			return -ENODEV;
L
Linus Torvalds 已提交
310 311
	}

312
	err = platform_driver_register(&nsc_drv);
J
Jeff Garzik 已提交
313 314
	if (err)
		return err;
315

316 317
	hi = tpm_read_index(nscAddrBase, TPM_NSC_BASE0_HI);
	lo = tpm_read_index(nscAddrBase, TPM_NSC_BASE0_LO);
318
	base = (hi<<8) | lo;
319

320 321 322
	/* enable the DPM module */
	tpm_write_index(nscAddrBase, NSC_LDC_INDEX, 0x01);

323
	pdev = platform_device_alloc("tpm_nscl0", -1);
324 325 326 327
	if (!pdev) {
		rc = -ENOMEM;
		goto err_unreg_drv;
	}
328 329

	pdev->num_resources = 0;
330
	pdev->dev.driver = &nsc_drv.driver;
331 332
	pdev->dev.release = tpm_nsc_remove;

333 334
	if ((rc = platform_device_add(pdev)) < 0)
		goto err_put_dev;
335

336
	if (request_region(base, 2, "tpm_nsc0") == NULL ) {
337
		rc = -EBUSY;
338
		goto err_del_dev;
339 340
	}

341 342
	if (!(chip = tpm_register_hardware(&pdev->dev, &tpm_nsc))) {
		rc = -ENODEV;
343
		goto err_rel_reg;
344
	}
345 346 347

	dev_dbg(&pdev->dev, "NSC TPM detected\n");
	dev_dbg(&pdev->dev,
L
Linus Torvalds 已提交
348
		"NSC LDN 0x%x, SID 0x%x, SRID 0x%x\n",
349 350
		tpm_read_index(nscAddrBase,0x07), tpm_read_index(nscAddrBase,0x20),
		tpm_read_index(nscAddrBase,0x27));
351
	dev_dbg(&pdev->dev,
L
Linus Torvalds 已提交
352
		"NSC SIOCF1 0x%x SIOCF5 0x%x SIOCF6 0x%x SIOCF8 0x%x\n",
353 354
		tpm_read_index(nscAddrBase,0x21), tpm_read_index(nscAddrBase,0x25),
		tpm_read_index(nscAddrBase,0x26), tpm_read_index(nscAddrBase,0x28));
355
	dev_dbg(&pdev->dev, "NSC IO Base0 0x%x\n",
356
		(tpm_read_index(nscAddrBase,0x60) << 8) | tpm_read_index(nscAddrBase,0x61));
357
	dev_dbg(&pdev->dev, "NSC IO Base1 0x%x\n",
358
		(tpm_read_index(nscAddrBase,0x62) << 8) | tpm_read_index(nscAddrBase,0x63));
359
	dev_dbg(&pdev->dev, "NSC Interrupt number and wakeup 0x%x\n",
360
		tpm_read_index(nscAddrBase,0x70));
361
	dev_dbg(&pdev->dev, "NSC IRQ type select 0x%x\n",
362
		tpm_read_index(nscAddrBase,0x71));
363
	dev_dbg(&pdev->dev,
L
Linus Torvalds 已提交
364
		"NSC DMA channel select0 0x%x, select1 0x%x\n",
365
		tpm_read_index(nscAddrBase,0x74), tpm_read_index(nscAddrBase,0x75));
366
	dev_dbg(&pdev->dev,
L
Linus Torvalds 已提交
367 368
		"NSC Config "
		"0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
369 370 371 372 373
		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 已提交
374

375
	dev_info(&pdev->dev,
376 377
		 "NSC TPM revision %d\n",
		 tpm_read_index(nscAddrBase, 0x27) & 0x1F);
L
Linus Torvalds 已提交
378

379 380
	chip->vendor.base = base;

L
Linus Torvalds 已提交
381
	return 0;
382 383

err_rel_reg:
384
	release_region(base, 2);
385 386 387 388
err_del_dev:
	platform_device_del(pdev);
err_put_dev:
	platform_device_put(pdev);
389
err_unreg_drv:
390
	platform_driver_unregister(&nsc_drv);
391
	return rc;
L
Linus Torvalds 已提交
392 393 394 395
}

static void __exit cleanup_nsc(void)
{
396 397 398 399 400
	if (pdev) {
		tpm_nsc_remove(&pdev->dev);
		platform_device_unregister(pdev);
	}

401
	platform_driver_unregister(&nsc_drv);
L
Linus Torvalds 已提交
402 403 404 405 406 407 408 409 410
}

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");