xref: /OK3568_Linux_fs/kernel/drivers/char/tpm/tpm_nsc.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright (C) 2004 IBM Corporation
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Authors:
6*4882a593Smuzhiyun  * Leendert van Doorn <leendert@watson.ibm.com>
7*4882a593Smuzhiyun  * Dave Safford <safford@watson.ibm.com>
8*4882a593Smuzhiyun  * Reiner Sailer <sailer@watson.ibm.com>
9*4882a593Smuzhiyun  * Kylene Hall <kjhall@us.ibm.com>
10*4882a593Smuzhiyun  *
11*4882a593Smuzhiyun  * Maintained by: <tpmdd-devel@lists.sourceforge.net>
12*4882a593Smuzhiyun  *
13*4882a593Smuzhiyun  * Device driver for TCG/TCPA TPM (trusted platform module).
14*4882a593Smuzhiyun  * Specifications at www.trustedcomputinggroup.org
15*4882a593Smuzhiyun  */
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun #include <linux/platform_device.h>
18*4882a593Smuzhiyun #include <linux/slab.h>
19*4882a593Smuzhiyun #include "tpm.h"
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun /* National definitions */
22*4882a593Smuzhiyun enum tpm_nsc_addr{
23*4882a593Smuzhiyun 	TPM_NSC_IRQ = 0x07,
24*4882a593Smuzhiyun 	TPM_NSC_BASE0_HI = 0x60,
25*4882a593Smuzhiyun 	TPM_NSC_BASE0_LO = 0x61,
26*4882a593Smuzhiyun 	TPM_NSC_BASE1_HI = 0x62,
27*4882a593Smuzhiyun 	TPM_NSC_BASE1_LO = 0x63
28*4882a593Smuzhiyun };
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun enum tpm_nsc_index {
31*4882a593Smuzhiyun 	NSC_LDN_INDEX = 0x07,
32*4882a593Smuzhiyun 	NSC_SID_INDEX = 0x20,
33*4882a593Smuzhiyun 	NSC_LDC_INDEX = 0x30,
34*4882a593Smuzhiyun 	NSC_DIO_INDEX = 0x60,
35*4882a593Smuzhiyun 	NSC_CIO_INDEX = 0x62,
36*4882a593Smuzhiyun 	NSC_IRQ_INDEX = 0x70,
37*4882a593Smuzhiyun 	NSC_ITS_INDEX = 0x71
38*4882a593Smuzhiyun };
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun enum tpm_nsc_status_loc {
41*4882a593Smuzhiyun 	NSC_STATUS = 0x01,
42*4882a593Smuzhiyun 	NSC_COMMAND = 0x01,
43*4882a593Smuzhiyun 	NSC_DATA = 0x00
44*4882a593Smuzhiyun };
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun /* status bits */
47*4882a593Smuzhiyun enum tpm_nsc_status {
48*4882a593Smuzhiyun 	NSC_STATUS_OBF = 0x01,	/* output buffer full */
49*4882a593Smuzhiyun 	NSC_STATUS_IBF = 0x02,	/* input buffer full */
50*4882a593Smuzhiyun 	NSC_STATUS_F0 = 0x04,	/* F0 */
51*4882a593Smuzhiyun 	NSC_STATUS_A2 = 0x08,	/* A2 */
52*4882a593Smuzhiyun 	NSC_STATUS_RDY = 0x10,	/* ready to receive command */
53*4882a593Smuzhiyun 	NSC_STATUS_IBR = 0x20	/* ready to receive data */
54*4882a593Smuzhiyun };
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun /* command bits */
57*4882a593Smuzhiyun enum tpm_nsc_cmd_mode {
58*4882a593Smuzhiyun 	NSC_COMMAND_NORMAL = 0x01,	/* normal mode */
59*4882a593Smuzhiyun 	NSC_COMMAND_EOC = 0x03,
60*4882a593Smuzhiyun 	NSC_COMMAND_CANCEL = 0x22
61*4882a593Smuzhiyun };
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun struct tpm_nsc_priv {
64*4882a593Smuzhiyun 	unsigned long base;
65*4882a593Smuzhiyun };
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun /*
68*4882a593Smuzhiyun  * Wait for a certain status to appear
69*4882a593Smuzhiyun  */
wait_for_stat(struct tpm_chip * chip,u8 mask,u8 val,u8 * data)70*4882a593Smuzhiyun static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data)
71*4882a593Smuzhiyun {
72*4882a593Smuzhiyun 	struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev);
73*4882a593Smuzhiyun 	unsigned long stop;
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun 	/* status immediately available check */
76*4882a593Smuzhiyun 	*data = inb(priv->base + NSC_STATUS);
77*4882a593Smuzhiyun 	if ((*data & mask) == val)
78*4882a593Smuzhiyun 		return 0;
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun 	/* wait for status */
81*4882a593Smuzhiyun 	stop = jiffies + 10 * HZ;
82*4882a593Smuzhiyun 	do {
83*4882a593Smuzhiyun 		msleep(TPM_TIMEOUT);
84*4882a593Smuzhiyun 		*data = inb(priv->base + 1);
85*4882a593Smuzhiyun 		if ((*data & mask) == val)
86*4882a593Smuzhiyun 			return 0;
87*4882a593Smuzhiyun 	}
88*4882a593Smuzhiyun 	while (time_before(jiffies, stop));
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun 	return -EBUSY;
91*4882a593Smuzhiyun }
92*4882a593Smuzhiyun 
nsc_wait_for_ready(struct tpm_chip * chip)93*4882a593Smuzhiyun static int nsc_wait_for_ready(struct tpm_chip *chip)
94*4882a593Smuzhiyun {
95*4882a593Smuzhiyun 	struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev);
96*4882a593Smuzhiyun 	int status;
97*4882a593Smuzhiyun 	unsigned long stop;
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 	/* status immediately available check */
100*4882a593Smuzhiyun 	status = inb(priv->base + NSC_STATUS);
101*4882a593Smuzhiyun 	if (status & NSC_STATUS_OBF)
102*4882a593Smuzhiyun 		status = inb(priv->base + NSC_DATA);
103*4882a593Smuzhiyun 	if (status & NSC_STATUS_RDY)
104*4882a593Smuzhiyun 		return 0;
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 	/* wait for status */
107*4882a593Smuzhiyun 	stop = jiffies + 100;
108*4882a593Smuzhiyun 	do {
109*4882a593Smuzhiyun 		msleep(TPM_TIMEOUT);
110*4882a593Smuzhiyun 		status = inb(priv->base + NSC_STATUS);
111*4882a593Smuzhiyun 		if (status & NSC_STATUS_OBF)
112*4882a593Smuzhiyun 			status = inb(priv->base + NSC_DATA);
113*4882a593Smuzhiyun 		if (status & NSC_STATUS_RDY)
114*4882a593Smuzhiyun 			return 0;
115*4882a593Smuzhiyun 	}
116*4882a593Smuzhiyun 	while (time_before(jiffies, stop));
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	dev_info(&chip->dev, "wait for ready failed\n");
119*4882a593Smuzhiyun 	return -EBUSY;
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 
tpm_nsc_recv(struct tpm_chip * chip,u8 * buf,size_t count)123*4882a593Smuzhiyun static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count)
124*4882a593Smuzhiyun {
125*4882a593Smuzhiyun 	struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev);
126*4882a593Smuzhiyun 	u8 *buffer = buf;
127*4882a593Smuzhiyun 	u8 data, *p;
128*4882a593Smuzhiyun 	u32 size;
129*4882a593Smuzhiyun 	__be32 *native_size;
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun 	if (count < 6)
132*4882a593Smuzhiyun 		return -EIO;
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	if (wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0) {
135*4882a593Smuzhiyun 		dev_err(&chip->dev, "F0 timeout\n");
136*4882a593Smuzhiyun 		return -EIO;
137*4882a593Smuzhiyun 	}
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	data = inb(priv->base + NSC_DATA);
140*4882a593Smuzhiyun 	if (data != NSC_COMMAND_NORMAL) {
141*4882a593Smuzhiyun 		dev_err(&chip->dev, "not in normal mode (0x%x)\n",
142*4882a593Smuzhiyun 			data);
143*4882a593Smuzhiyun 		return -EIO;
144*4882a593Smuzhiyun 	}
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun 	/* read the whole packet */
147*4882a593Smuzhiyun 	for (p = buffer; p < &buffer[count]; p++) {
148*4882a593Smuzhiyun 		if (wait_for_stat
149*4882a593Smuzhiyun 		    (chip, NSC_STATUS_OBF, NSC_STATUS_OBF, &data) < 0) {
150*4882a593Smuzhiyun 			dev_err(&chip->dev,
151*4882a593Smuzhiyun 				"OBF timeout (while reading data)\n");
152*4882a593Smuzhiyun 			return -EIO;
153*4882a593Smuzhiyun 		}
154*4882a593Smuzhiyun 		if (data & NSC_STATUS_F0)
155*4882a593Smuzhiyun 			break;
156*4882a593Smuzhiyun 		*p = inb(priv->base + NSC_DATA);
157*4882a593Smuzhiyun 	}
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 	if ((data & NSC_STATUS_F0) == 0 &&
160*4882a593Smuzhiyun 	(wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0)) {
161*4882a593Smuzhiyun 		dev_err(&chip->dev, "F0 not set\n");
162*4882a593Smuzhiyun 		return -EIO;
163*4882a593Smuzhiyun 	}
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 	data = inb(priv->base + NSC_DATA);
166*4882a593Smuzhiyun 	if (data != NSC_COMMAND_EOC) {
167*4882a593Smuzhiyun 		dev_err(&chip->dev,
168*4882a593Smuzhiyun 			"expected end of command(0x%x)\n", data);
169*4882a593Smuzhiyun 		return -EIO;
170*4882a593Smuzhiyun 	}
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun 	native_size = (__force __be32 *) (buf + 2);
173*4882a593Smuzhiyun 	size = be32_to_cpu(*native_size);
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 	if (count < size)
176*4882a593Smuzhiyun 		return -EIO;
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	return size;
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun 
tpm_nsc_send(struct tpm_chip * chip,u8 * buf,size_t count)181*4882a593Smuzhiyun static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count)
182*4882a593Smuzhiyun {
183*4882a593Smuzhiyun 	struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev);
184*4882a593Smuzhiyun 	u8 data;
185*4882a593Smuzhiyun 	int i;
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun 	/*
188*4882a593Smuzhiyun 	 * If we hit the chip with back to back commands it locks up
189*4882a593Smuzhiyun 	 * and never set IBF. Hitting it with this "hammer" seems to
190*4882a593Smuzhiyun 	 * fix it. Not sure why this is needed, we followed the flow
191*4882a593Smuzhiyun 	 * chart in the manual to the letter.
192*4882a593Smuzhiyun 	 */
193*4882a593Smuzhiyun 	outb(NSC_COMMAND_CANCEL, priv->base + NSC_COMMAND);
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun 	if (nsc_wait_for_ready(chip) != 0)
196*4882a593Smuzhiyun 		return -EIO;
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 	if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
199*4882a593Smuzhiyun 		dev_err(&chip->dev, "IBF timeout\n");
200*4882a593Smuzhiyun 		return -EIO;
201*4882a593Smuzhiyun 	}
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	outb(NSC_COMMAND_NORMAL, priv->base + NSC_COMMAND);
204*4882a593Smuzhiyun 	if (wait_for_stat(chip, NSC_STATUS_IBR, NSC_STATUS_IBR, &data) < 0) {
205*4882a593Smuzhiyun 		dev_err(&chip->dev, "IBR timeout\n");
206*4882a593Smuzhiyun 		return -EIO;
207*4882a593Smuzhiyun 	}
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	for (i = 0; i < count; i++) {
210*4882a593Smuzhiyun 		if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
211*4882a593Smuzhiyun 			dev_err(&chip->dev,
212*4882a593Smuzhiyun 				"IBF timeout (while writing data)\n");
213*4882a593Smuzhiyun 			return -EIO;
214*4882a593Smuzhiyun 		}
215*4882a593Smuzhiyun 		outb(buf[i], priv->base + NSC_DATA);
216*4882a593Smuzhiyun 	}
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun 	if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
219*4882a593Smuzhiyun 		dev_err(&chip->dev, "IBF timeout\n");
220*4882a593Smuzhiyun 		return -EIO;
221*4882a593Smuzhiyun 	}
222*4882a593Smuzhiyun 	outb(NSC_COMMAND_EOC, priv->base + NSC_COMMAND);
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun 	return 0;
225*4882a593Smuzhiyun }
226*4882a593Smuzhiyun 
tpm_nsc_cancel(struct tpm_chip * chip)227*4882a593Smuzhiyun static void tpm_nsc_cancel(struct tpm_chip *chip)
228*4882a593Smuzhiyun {
229*4882a593Smuzhiyun 	struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev);
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun 	outb(NSC_COMMAND_CANCEL, priv->base + NSC_COMMAND);
232*4882a593Smuzhiyun }
233*4882a593Smuzhiyun 
tpm_nsc_status(struct tpm_chip * chip)234*4882a593Smuzhiyun static u8 tpm_nsc_status(struct tpm_chip *chip)
235*4882a593Smuzhiyun {
236*4882a593Smuzhiyun 	struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev);
237*4882a593Smuzhiyun 
238*4882a593Smuzhiyun 	return inb(priv->base + NSC_STATUS);
239*4882a593Smuzhiyun }
240*4882a593Smuzhiyun 
tpm_nsc_req_canceled(struct tpm_chip * chip,u8 status)241*4882a593Smuzhiyun static bool tpm_nsc_req_canceled(struct tpm_chip *chip, u8 status)
242*4882a593Smuzhiyun {
243*4882a593Smuzhiyun 	return (status == NSC_STATUS_RDY);
244*4882a593Smuzhiyun }
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun static const struct tpm_class_ops tpm_nsc = {
247*4882a593Smuzhiyun 	.recv = tpm_nsc_recv,
248*4882a593Smuzhiyun 	.send = tpm_nsc_send,
249*4882a593Smuzhiyun 	.cancel = tpm_nsc_cancel,
250*4882a593Smuzhiyun 	.status = tpm_nsc_status,
251*4882a593Smuzhiyun 	.req_complete_mask = NSC_STATUS_OBF,
252*4882a593Smuzhiyun 	.req_complete_val = NSC_STATUS_OBF,
253*4882a593Smuzhiyun 	.req_canceled = tpm_nsc_req_canceled,
254*4882a593Smuzhiyun };
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun static struct platform_device *pdev = NULL;
257*4882a593Smuzhiyun 
tpm_nsc_remove(struct device * dev)258*4882a593Smuzhiyun static void tpm_nsc_remove(struct device *dev)
259*4882a593Smuzhiyun {
260*4882a593Smuzhiyun 	struct tpm_chip *chip = dev_get_drvdata(dev);
261*4882a593Smuzhiyun 	struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev);
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun 	tpm_chip_unregister(chip);
264*4882a593Smuzhiyun 	release_region(priv->base, 2);
265*4882a593Smuzhiyun }
266*4882a593Smuzhiyun 
267*4882a593Smuzhiyun static SIMPLE_DEV_PM_OPS(tpm_nsc_pm, tpm_pm_suspend, tpm_pm_resume);
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun static struct platform_driver nsc_drv = {
270*4882a593Smuzhiyun 	.driver          = {
271*4882a593Smuzhiyun 		.name    = "tpm_nsc",
272*4882a593Smuzhiyun 		.pm      = &tpm_nsc_pm,
273*4882a593Smuzhiyun 	},
274*4882a593Smuzhiyun };
275*4882a593Smuzhiyun 
tpm_read_index(int base,int index)276*4882a593Smuzhiyun static inline int tpm_read_index(int base, int index)
277*4882a593Smuzhiyun {
278*4882a593Smuzhiyun 	outb(index, base);
279*4882a593Smuzhiyun 	return inb(base+1) & 0xFF;
280*4882a593Smuzhiyun }
281*4882a593Smuzhiyun 
tpm_write_index(int base,int index,int value)282*4882a593Smuzhiyun static inline void tpm_write_index(int base, int index, int value)
283*4882a593Smuzhiyun {
284*4882a593Smuzhiyun 	outb(index, base);
285*4882a593Smuzhiyun 	outb(value & 0xFF, base+1);
286*4882a593Smuzhiyun }
287*4882a593Smuzhiyun 
init_nsc(void)288*4882a593Smuzhiyun static int __init init_nsc(void)
289*4882a593Smuzhiyun {
290*4882a593Smuzhiyun 	int rc = 0;
291*4882a593Smuzhiyun 	int lo, hi, err;
292*4882a593Smuzhiyun 	int nscAddrBase = TPM_ADDR;
293*4882a593Smuzhiyun 	struct tpm_chip *chip;
294*4882a593Smuzhiyun 	unsigned long base;
295*4882a593Smuzhiyun 	struct tpm_nsc_priv *priv;
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun 	/* verify that it is a National part (SID) */
298*4882a593Smuzhiyun 	if (tpm_read_index(TPM_ADDR, NSC_SID_INDEX) != 0xEF) {
299*4882a593Smuzhiyun 		nscAddrBase = (tpm_read_index(TPM_SUPERIO_ADDR, 0x2C)<<8)|
300*4882a593Smuzhiyun 			(tpm_read_index(TPM_SUPERIO_ADDR, 0x2B)&0xFE);
301*4882a593Smuzhiyun 		if (tpm_read_index(nscAddrBase, NSC_SID_INDEX) != 0xF6)
302*4882a593Smuzhiyun 			return -ENODEV;
303*4882a593Smuzhiyun 	}
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 	err = platform_driver_register(&nsc_drv);
306*4882a593Smuzhiyun 	if (err)
307*4882a593Smuzhiyun 		return err;
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun 	hi = tpm_read_index(nscAddrBase, TPM_NSC_BASE0_HI);
310*4882a593Smuzhiyun 	lo = tpm_read_index(nscAddrBase, TPM_NSC_BASE0_LO);
311*4882a593Smuzhiyun 	base = (hi<<8) | lo;
312*4882a593Smuzhiyun 
313*4882a593Smuzhiyun 	/* enable the DPM module */
314*4882a593Smuzhiyun 	tpm_write_index(nscAddrBase, NSC_LDC_INDEX, 0x01);
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun 	pdev = platform_device_alloc("tpm_nscl0", -1);
317*4882a593Smuzhiyun 	if (!pdev) {
318*4882a593Smuzhiyun 		rc = -ENOMEM;
319*4882a593Smuzhiyun 		goto err_unreg_drv;
320*4882a593Smuzhiyun 	}
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun 	pdev->num_resources = 0;
323*4882a593Smuzhiyun 	pdev->dev.driver = &nsc_drv.driver;
324*4882a593Smuzhiyun 	pdev->dev.release = tpm_nsc_remove;
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun 	if ((rc = platform_device_add(pdev)) < 0)
327*4882a593Smuzhiyun 		goto err_put_dev;
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
330*4882a593Smuzhiyun 	if (!priv) {
331*4882a593Smuzhiyun 		rc = -ENOMEM;
332*4882a593Smuzhiyun 		goto err_del_dev;
333*4882a593Smuzhiyun 	}
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun 	priv->base = base;
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun 	if (request_region(base, 2, "tpm_nsc0") == NULL ) {
338*4882a593Smuzhiyun 		rc = -EBUSY;
339*4882a593Smuzhiyun 		goto err_del_dev;
340*4882a593Smuzhiyun 	}
341*4882a593Smuzhiyun 
342*4882a593Smuzhiyun 	chip = tpmm_chip_alloc(&pdev->dev, &tpm_nsc);
343*4882a593Smuzhiyun 	if (IS_ERR(chip)) {
344*4882a593Smuzhiyun 		rc = -ENODEV;
345*4882a593Smuzhiyun 		goto err_rel_reg;
346*4882a593Smuzhiyun 	}
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun 	dev_set_drvdata(&chip->dev, priv);
349*4882a593Smuzhiyun 
350*4882a593Smuzhiyun 	rc = tpm_chip_register(chip);
351*4882a593Smuzhiyun 	if (rc)
352*4882a593Smuzhiyun 		goto err_rel_reg;
353*4882a593Smuzhiyun 
354*4882a593Smuzhiyun 	dev_dbg(&pdev->dev, "NSC TPM detected\n");
355*4882a593Smuzhiyun 	dev_dbg(&pdev->dev,
356*4882a593Smuzhiyun 		"NSC LDN 0x%x, SID 0x%x, SRID 0x%x\n",
357*4882a593Smuzhiyun 		tpm_read_index(nscAddrBase,0x07), tpm_read_index(nscAddrBase,0x20),
358*4882a593Smuzhiyun 		tpm_read_index(nscAddrBase,0x27));
359*4882a593Smuzhiyun 	dev_dbg(&pdev->dev,
360*4882a593Smuzhiyun 		"NSC SIOCF1 0x%x SIOCF5 0x%x SIOCF6 0x%x SIOCF8 0x%x\n",
361*4882a593Smuzhiyun 		tpm_read_index(nscAddrBase,0x21), tpm_read_index(nscAddrBase,0x25),
362*4882a593Smuzhiyun 		tpm_read_index(nscAddrBase,0x26), tpm_read_index(nscAddrBase,0x28));
363*4882a593Smuzhiyun 	dev_dbg(&pdev->dev, "NSC IO Base0 0x%x\n",
364*4882a593Smuzhiyun 		(tpm_read_index(nscAddrBase,0x60) << 8) | tpm_read_index(nscAddrBase,0x61));
365*4882a593Smuzhiyun 	dev_dbg(&pdev->dev, "NSC IO Base1 0x%x\n",
366*4882a593Smuzhiyun 		(tpm_read_index(nscAddrBase,0x62) << 8) | tpm_read_index(nscAddrBase,0x63));
367*4882a593Smuzhiyun 	dev_dbg(&pdev->dev, "NSC Interrupt number and wakeup 0x%x\n",
368*4882a593Smuzhiyun 		tpm_read_index(nscAddrBase,0x70));
369*4882a593Smuzhiyun 	dev_dbg(&pdev->dev, "NSC IRQ type select 0x%x\n",
370*4882a593Smuzhiyun 		tpm_read_index(nscAddrBase,0x71));
371*4882a593Smuzhiyun 	dev_dbg(&pdev->dev,
372*4882a593Smuzhiyun 		"NSC DMA channel select0 0x%x, select1 0x%x\n",
373*4882a593Smuzhiyun 		tpm_read_index(nscAddrBase,0x74), tpm_read_index(nscAddrBase,0x75));
374*4882a593Smuzhiyun 	dev_dbg(&pdev->dev,
375*4882a593Smuzhiyun 		"NSC Config "
376*4882a593Smuzhiyun 		"0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
377*4882a593Smuzhiyun 		tpm_read_index(nscAddrBase,0xF0), tpm_read_index(nscAddrBase,0xF1),
378*4882a593Smuzhiyun 		tpm_read_index(nscAddrBase,0xF2), tpm_read_index(nscAddrBase,0xF3),
379*4882a593Smuzhiyun 		tpm_read_index(nscAddrBase,0xF4), tpm_read_index(nscAddrBase,0xF5),
380*4882a593Smuzhiyun 		tpm_read_index(nscAddrBase,0xF6), tpm_read_index(nscAddrBase,0xF7),
381*4882a593Smuzhiyun 		tpm_read_index(nscAddrBase,0xF8), tpm_read_index(nscAddrBase,0xF9));
382*4882a593Smuzhiyun 
383*4882a593Smuzhiyun 	dev_info(&pdev->dev,
384*4882a593Smuzhiyun 		 "NSC TPM revision %d\n",
385*4882a593Smuzhiyun 		 tpm_read_index(nscAddrBase, 0x27) & 0x1F);
386*4882a593Smuzhiyun 
387*4882a593Smuzhiyun 	return 0;
388*4882a593Smuzhiyun 
389*4882a593Smuzhiyun err_rel_reg:
390*4882a593Smuzhiyun 	release_region(base, 2);
391*4882a593Smuzhiyun err_del_dev:
392*4882a593Smuzhiyun 	platform_device_del(pdev);
393*4882a593Smuzhiyun err_put_dev:
394*4882a593Smuzhiyun 	platform_device_put(pdev);
395*4882a593Smuzhiyun err_unreg_drv:
396*4882a593Smuzhiyun 	platform_driver_unregister(&nsc_drv);
397*4882a593Smuzhiyun 	return rc;
398*4882a593Smuzhiyun }
399*4882a593Smuzhiyun 
cleanup_nsc(void)400*4882a593Smuzhiyun static void __exit cleanup_nsc(void)
401*4882a593Smuzhiyun {
402*4882a593Smuzhiyun 	if (pdev) {
403*4882a593Smuzhiyun 		tpm_nsc_remove(&pdev->dev);
404*4882a593Smuzhiyun 		platform_device_unregister(pdev);
405*4882a593Smuzhiyun 	}
406*4882a593Smuzhiyun 
407*4882a593Smuzhiyun 	platform_driver_unregister(&nsc_drv);
408*4882a593Smuzhiyun }
409*4882a593Smuzhiyun 
410*4882a593Smuzhiyun module_init(init_nsc);
411*4882a593Smuzhiyun module_exit(cleanup_nsc);
412*4882a593Smuzhiyun 
413*4882a593Smuzhiyun MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
414*4882a593Smuzhiyun MODULE_DESCRIPTION("TPM Driver");
415*4882a593Smuzhiyun MODULE_VERSION("2.0");
416*4882a593Smuzhiyun MODULE_LICENSE("GPL");
417