| /* | 
 |  * STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24 | 
 |  * Copyright (C) 2009, 2010  STMicroelectronics | 
 |  * | 
 |  * 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; either version 2 of the License, or | 
 |  * (at your option) any later version. | 
 |  * | 
 |  * This program is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |  * GNU General Public License for more details. | 
 |  * | 
 |  * You should have received a copy of the GNU General Public License along | 
 |  * with this program; if not, write to the Free Software Foundation, Inc., | 
 |  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 
 |  * | 
 |  * STMicroelectronics version 1.2.0, Copyright (C) 2010 | 
 |  * STMicroelectronics comes with ABSOLUTELY NO WARRANTY. | 
 |  * This is free software, and you are welcome to redistribute it | 
 |  * under certain conditions. | 
 |  * | 
 |  * @Author: Christophe RICARD tpmsupport@st.com | 
 |  * | 
 |  * @File: tpm_stm_st33_i2c.c | 
 |  * | 
 |  * @Synopsis: | 
 |  *	09/15/2010:	First shot driver tpm_tis driver for | 
 | 			 lpc is used as model. | 
 |  */ | 
 |  | 
 | #include <linux/pci.h> | 
 | #include <linux/module.h> | 
 | #include <linux/platform_device.h> | 
 | #include <linux/i2c.h> | 
 | #include <linux/fs.h> | 
 | #include <linux/miscdevice.h> | 
 | #include <linux/kernel.h> | 
 | #include <linux/delay.h> | 
 | #include <linux/init.h> | 
 | #include <linux/wait.h> | 
 | #include <linux/string.h> | 
 | #include <linux/interrupt.h> | 
 | #include <linux/spinlock.h> | 
 | #include <linux/sysfs.h> | 
 | #include <linux/gpio.h> | 
 | #include <linux/sched.h> | 
 | #include <linux/uaccess.h> | 
 | #include <linux/io.h> | 
 | #include <linux/slab.h> | 
 |  | 
 | #include "tpm.h" | 
 | #include "tpm_i2c_stm_st33.h" | 
 |  | 
 | enum stm33zp24_access { | 
 | 	TPM_ACCESS_VALID = 0x80, | 
 | 	TPM_ACCESS_ACTIVE_LOCALITY = 0x20, | 
 | 	TPM_ACCESS_REQUEST_PENDING = 0x04, | 
 | 	TPM_ACCESS_REQUEST_USE = 0x02, | 
 | }; | 
 |  | 
 | enum stm33zp24_status { | 
 | 	TPM_STS_VALID = 0x80, | 
 | 	TPM_STS_COMMAND_READY = 0x40, | 
 | 	TPM_STS_GO = 0x20, | 
 | 	TPM_STS_DATA_AVAIL = 0x10, | 
 | 	TPM_STS_DATA_EXPECT = 0x08, | 
 | }; | 
 |  | 
 | enum stm33zp24_int_flags { | 
 | 	TPM_GLOBAL_INT_ENABLE = 0x80, | 
 | 	TPM_INTF_CMD_READY_INT = 0x080, | 
 | 	TPM_INTF_FIFO_AVALAIBLE_INT = 0x040, | 
 | 	TPM_INTF_WAKE_UP_READY_INT = 0x020, | 
 | 	TPM_INTF_LOCALITY_CHANGE_INT = 0x004, | 
 | 	TPM_INTF_STS_VALID_INT = 0x002, | 
 | 	TPM_INTF_DATA_AVAIL_INT = 0x001, | 
 | }; | 
 |  | 
 | enum tis_defaults { | 
 | 	TIS_SHORT_TIMEOUT = 750, | 
 | 	TIS_LONG_TIMEOUT = 2000, | 
 | }; | 
 |  | 
 | /* | 
 |  * write8_reg | 
 |  * Send byte to the TIS register according to the ST33ZP24 I2C protocol. | 
 |  * @param: tpm_register, the tpm tis register where the data should be written | 
 |  * @param: tpm_data, the tpm_data to write inside the tpm_register | 
 |  * @param: tpm_size, The length of the data | 
 |  * @return: Returns negative errno, or else the number of bytes written. | 
 |  */ | 
 | static int write8_reg(struct i2c_client *client, u8 tpm_register, | 
 | 		      u8 *tpm_data, u16 tpm_size) | 
 | { | 
 | 	struct st33zp24_platform_data *pin_infos; | 
 |  | 
 | 	pin_infos = client->dev.platform_data; | 
 |  | 
 | 	pin_infos->tpm_i2c_buffer[0][0] = tpm_register; | 
 | 	memcpy(&pin_infos->tpm_i2c_buffer[0][1], tpm_data, tpm_size); | 
 | 	return i2c_master_send(client, pin_infos->tpm_i2c_buffer[0], | 
 | 				tpm_size + 1); | 
 | } /* write8_reg() */ | 
 |  | 
 | /* | 
 |  * read8_reg | 
 |  * Recv byte from the TIS register according to the ST33ZP24 I2C protocol. | 
 |  * @param: tpm_register, the tpm tis register where the data should be read | 
 |  * @param: tpm_data, the TPM response | 
 |  * @param: tpm_size, tpm TPM response size to read. | 
 |  * @return: number of byte read successfully: should be one if success. | 
 |  */ | 
 | static int read8_reg(struct i2c_client *client, u8 tpm_register, | 
 | 		    u8 *tpm_data, int tpm_size) | 
 | { | 
 | 	u8 status = 0; | 
 | 	u8 data; | 
 |  | 
 | 	data = TPM_DUMMY_BYTE; | 
 | 	status = write8_reg(client, tpm_register, &data, 1); | 
 | 	if (status == 2) | 
 | 		status = i2c_master_recv(client, tpm_data, tpm_size); | 
 | 	return status; | 
 | } /* read8_reg() */ | 
 |  | 
 | /* | 
 |  * I2C_WRITE_DATA | 
 |  * Send byte to the TIS register according to the ST33ZP24 I2C protocol. | 
 |  * @param: client, the chip description | 
 |  * @param: tpm_register, the tpm tis register where the data should be written | 
 |  * @param: tpm_data, the tpm_data to write inside the tpm_register | 
 |  * @param: tpm_size, The length of the data | 
 |  * @return: number of byte written successfully: should be one if success. | 
 |  */ | 
 | #define I2C_WRITE_DATA(client, tpm_register, tpm_data, tpm_size) \ | 
 | 	(write8_reg(client, tpm_register | \ | 
 | 	TPM_WRITE_DIRECTION, tpm_data, tpm_size)) | 
 |  | 
 | /* | 
 |  * I2C_READ_DATA | 
 |  * Recv byte from the TIS register according to the ST33ZP24 I2C protocol. | 
 |  * @param: tpm, the chip description | 
 |  * @param: tpm_register, the tpm tis register where the data should be read | 
 |  * @param: tpm_data, the TPM response | 
 |  * @param: tpm_size, tpm TPM response size to read. | 
 |  * @return: number of byte read successfully: should be one if success. | 
 |  */ | 
 | #define I2C_READ_DATA(client, tpm_register, tpm_data, tpm_size) \ | 
 | 	(read8_reg(client, tpm_register, tpm_data, tpm_size)) | 
 |  | 
 | /* | 
 |  * clear_interruption | 
 |  * clear the TPM interrupt register. | 
 |  * @param: tpm, the chip description | 
 |  */ | 
 | static void clear_interruption(struct i2c_client *client) | 
 | { | 
 | 	u8 interrupt; | 
 | 	I2C_READ_DATA(client, TPM_INT_STATUS, &interrupt, 1); | 
 | 	I2C_WRITE_DATA(client, TPM_INT_STATUS, &interrupt, 1); | 
 | 	I2C_READ_DATA(client, TPM_INT_STATUS, &interrupt, 1); | 
 | } /* clear_interruption() */ | 
 |  | 
 | /* | 
 |  * _wait_for_interrupt_serirq_timeout | 
 |  * @param: tpm, the chip description | 
 |  * @param: timeout, the timeout of the interrupt | 
 |  * @return: the status of the interruption. | 
 |  */ | 
 | static long _wait_for_interrupt_serirq_timeout(struct tpm_chip *chip, | 
 | 						unsigned long timeout) | 
 | { | 
 | 	long status; | 
 | 	struct i2c_client *client; | 
 | 	struct st33zp24_platform_data *pin_infos; | 
 |  | 
 | 	client = (struct i2c_client *)TPM_VPRIV(chip); | 
 | 	pin_infos = client->dev.platform_data; | 
 |  | 
 | 	status = wait_for_completion_interruptible_timeout( | 
 | 					&pin_infos->irq_detection, | 
 | 						timeout); | 
 | 	if (status > 0) | 
 | 		enable_irq(gpio_to_irq(pin_infos->io_serirq)); | 
 | 	gpio_direction_input(pin_infos->io_serirq); | 
 |  | 
 | 	return status; | 
 | } /* wait_for_interrupt_serirq_timeout() */ | 
 |  | 
 | static int wait_for_serirq_timeout(struct tpm_chip *chip, bool condition, | 
 | 				 unsigned long timeout) | 
 | { | 
 | 	int status = 2; | 
 | 	struct i2c_client *client; | 
 |  | 
 | 	client = (struct i2c_client *)TPM_VPRIV(chip); | 
 |  | 
 | 	status = _wait_for_interrupt_serirq_timeout(chip, timeout); | 
 | 	if (!status) { | 
 | 		status = -EBUSY; | 
 | 	} else { | 
 | 		clear_interruption(client); | 
 | 		if (condition) | 
 | 			status = 1; | 
 | 	} | 
 | 	return status; | 
 | } | 
 |  | 
 | /* | 
 |  * tpm_stm_i2c_cancel, cancel is not implemented. | 
 |  * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h | 
 |  */ | 
 | static void tpm_stm_i2c_cancel(struct tpm_chip *chip) | 
 | { | 
 | 	struct i2c_client *client; | 
 | 	u8 data; | 
 |  | 
 | 	client = (struct i2c_client *)TPM_VPRIV(chip); | 
 |  | 
 | 	data = TPM_STS_COMMAND_READY; | 
 | 	I2C_WRITE_DATA(client, TPM_STS, &data, 1); | 
 | 	if (chip->vendor.irq) | 
 | 		wait_for_serirq_timeout(chip, 1, chip->vendor.timeout_a); | 
 | }	/* tpm_stm_i2c_cancel() */ | 
 |  | 
 | /* | 
 |  * tpm_stm_spi_status return the TPM_STS register | 
 |  * @param: chip, the tpm chip description | 
 |  * @return: the TPM_STS register value. | 
 |  */ | 
 | static u8 tpm_stm_i2c_status(struct tpm_chip *chip) | 
 | { | 
 | 	struct i2c_client *client; | 
 | 	u8 data; | 
 | 	client = (struct i2c_client *)TPM_VPRIV(chip); | 
 |  | 
 | 	I2C_READ_DATA(client, TPM_STS, &data, 1); | 
 | 	return data; | 
 | }				/* tpm_stm_i2c_status() */ | 
 |  | 
 |  | 
 | /* | 
 |  * check_locality if the locality is active | 
 |  * @param: chip, the tpm chip description | 
 |  * @return: the active locality or -EACCESS. | 
 |  */ | 
 | static int check_locality(struct tpm_chip *chip) | 
 | { | 
 | 	struct i2c_client *client; | 
 | 	u8 data; | 
 | 	u8 status; | 
 |  | 
 | 	client = (struct i2c_client *)TPM_VPRIV(chip); | 
 |  | 
 | 	status = I2C_READ_DATA(client, TPM_ACCESS, &data, 1); | 
 | 	if (status && (data & | 
 | 		(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == | 
 | 		(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) | 
 | 		return chip->vendor.locality; | 
 |  | 
 | 	return -EACCES; | 
 |  | 
 | } /* check_locality() */ | 
 |  | 
 | /* | 
 |  * request_locality request the TPM locality | 
 |  * @param: chip, the chip description | 
 |  * @return: the active locality or EACCESS. | 
 |  */ | 
 | static int request_locality(struct tpm_chip *chip) | 
 | { | 
 | 	unsigned long stop; | 
 | 	long rc; | 
 | 	struct i2c_client *client; | 
 | 	u8 data; | 
 |  | 
 | 	client = (struct i2c_client *)TPM_VPRIV(chip); | 
 |  | 
 | 	if (check_locality(chip) == chip->vendor.locality) | 
 | 		return chip->vendor.locality; | 
 |  | 
 | 	data = TPM_ACCESS_REQUEST_USE; | 
 | 	rc = I2C_WRITE_DATA(client, TPM_ACCESS, &data, 1); | 
 | 	if (rc < 0) | 
 | 		goto end; | 
 |  | 
 | 	if (chip->vendor.irq) { | 
 | 		rc = wait_for_serirq_timeout(chip, (check_locality | 
 | 						       (chip) >= 0), | 
 | 						      chip->vendor.timeout_a); | 
 | 		if (rc > 0) | 
 | 			return chip->vendor.locality; | 
 | 	} else { | 
 | 		stop = jiffies + chip->vendor.timeout_a; | 
 | 		do { | 
 | 			if (check_locality(chip) >= 0) | 
 | 				return chip->vendor.locality; | 
 | 			msleep(TPM_TIMEOUT); | 
 | 		} while (time_before(jiffies, stop)); | 
 | 	} | 
 | 	rc = -EACCES; | 
 | end: | 
 | 	return rc; | 
 | } /* request_locality() */ | 
 |  | 
 | /* | 
 |  * release_locality release the active locality | 
 |  * @param: chip, the tpm chip description. | 
 |  */ | 
 | static void release_locality(struct tpm_chip *chip) | 
 | { | 
 | 	struct i2c_client *client; | 
 | 	u8 data; | 
 |  | 
 | 	client = (struct i2c_client *)TPM_VPRIV(chip); | 
 | 	data = TPM_ACCESS_ACTIVE_LOCALITY; | 
 |  | 
 | 	I2C_WRITE_DATA(client, TPM_ACCESS, &data, 1); | 
 | } | 
 |  | 
 | /* | 
 |  * get_burstcount return the burstcount address 0x19 0x1A | 
 |  * @param: chip, the chip description | 
 |  * return: the burstcount. | 
 |  */ | 
 | static int get_burstcount(struct tpm_chip *chip) | 
 | { | 
 | 	unsigned long stop; | 
 | 	int burstcnt, status; | 
 | 	u8 tpm_reg, temp; | 
 |  | 
 | 	struct i2c_client *client = (struct i2c_client *)TPM_VPRIV(chip); | 
 |  | 
 | 	stop = jiffies + chip->vendor.timeout_d; | 
 | 	do { | 
 | 		tpm_reg = TPM_STS + 1; | 
 | 		status = I2C_READ_DATA(client, tpm_reg, &temp, 1); | 
 | 		if (status < 0) | 
 | 			goto end; | 
 |  | 
 | 		tpm_reg = tpm_reg + 1; | 
 | 		burstcnt = temp; | 
 | 		status = I2C_READ_DATA(client, tpm_reg, &temp, 1); | 
 | 		if (status < 0) | 
 | 			goto end; | 
 |  | 
 | 		burstcnt |= temp << 8; | 
 | 		if (burstcnt) | 
 | 			return burstcnt; | 
 | 		msleep(TPM_TIMEOUT); | 
 | 	} while (time_before(jiffies, stop)); | 
 |  | 
 | end: | 
 | 	return -EBUSY; | 
 | } /* get_burstcount() */ | 
 |  | 
 | /* | 
 |  * wait_for_stat wait for a TPM_STS value | 
 |  * @param: chip, the tpm chip description | 
 |  * @param: mask, the value mask to wait | 
 |  * @param: timeout, the timeout | 
 |  * @param: queue, the wait queue. | 
 |  * @return: the tpm status, 0 if success, -ETIME if timeout is reached. | 
 |  */ | 
 | static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, | 
 | 			 wait_queue_head_t *queue) | 
 | { | 
 | 	unsigned long stop; | 
 | 	long rc; | 
 | 	u8 status; | 
 |  | 
 | 	 if (chip->vendor.irq) { | 
 | 		rc = wait_for_serirq_timeout(chip, ((tpm_stm_i2c_status | 
 | 							(chip) & mask) == | 
 | 						       mask), timeout); | 
 | 		if (rc > 0) | 
 | 			return 0; | 
 | 	} else { | 
 | 		stop = jiffies + timeout; | 
 | 		do { | 
 | 			msleep(TPM_TIMEOUT); | 
 | 			status = tpm_stm_i2c_status(chip); | 
 | 			if ((status & mask) == mask) | 
 | 				return 0; | 
 | 		} while (time_before(jiffies, stop)); | 
 | 	} | 
 | 	return -ETIME; | 
 | } /* wait_for_stat() */ | 
 |  | 
 | /* | 
 |  * recv_data receive data | 
 |  * @param: chip, the tpm chip description | 
 |  * @param: buf, the buffer where the data are received | 
 |  * @param: count, the number of data to receive | 
 |  * @return: the number of bytes read from TPM FIFO. | 
 |  */ | 
 | static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) | 
 | { | 
 | 	int size = 0, burstcnt, len; | 
 | 	struct i2c_client *client; | 
 |  | 
 | 	client = (struct i2c_client *)TPM_VPRIV(chip); | 
 |  | 
 | 	while (size < count && | 
 | 	       wait_for_stat(chip, | 
 | 			     TPM_STS_DATA_AVAIL | TPM_STS_VALID, | 
 | 			     chip->vendor.timeout_c, | 
 | 			     &chip->vendor.read_queue) | 
 | 	       == 0) { | 
 | 		burstcnt = get_burstcount(chip); | 
 | 		len = min_t(int, burstcnt, count - size); | 
 | 		I2C_READ_DATA(client, TPM_DATA_FIFO, buf + size, len); | 
 | 		size += len; | 
 | 	} | 
 | 	return size; | 
 | } | 
 |  | 
 | /* | 
 |  * tpm_ioserirq_handler the serirq irq handler | 
 |  * @param: irq, the tpm chip description | 
 |  * @param: dev_id, the description of the chip | 
 |  * @return: the status of the handler. | 
 |  */ | 
 | static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id) | 
 | { | 
 | 	struct tpm_chip *chip = dev_id; | 
 | 	struct i2c_client *client; | 
 | 	struct st33zp24_platform_data *pin_infos; | 
 |  | 
 | 	disable_irq_nosync(irq); | 
 |  | 
 | 	client = (struct i2c_client *)TPM_VPRIV(chip); | 
 | 	pin_infos = client->dev.platform_data; | 
 |  | 
 | 	complete(&pin_infos->irq_detection); | 
 | 	return IRQ_HANDLED; | 
 | } /* tpm_ioserirq_handler() */ | 
 |  | 
 |  | 
 | /* | 
 |  * tpm_stm_i2c_send send TPM commands through the I2C bus. | 
 |  * | 
 |  * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h | 
 |  * @param: buf,	the buffer to send. | 
 |  * @param: count, the number of bytes to send. | 
 |  * @return: In case of success the number of bytes sent. | 
 |  *			In other case, a < 0 value describing the issue. | 
 |  */ | 
 | static int tpm_stm_i2c_send(struct tpm_chip *chip, unsigned char *buf, | 
 | 			    size_t len) | 
 | { | 
 | 	u32 status, burstcnt = 0, i, size; | 
 | 	int ret; | 
 | 	u8 data; | 
 | 	struct i2c_client *client; | 
 |  | 
 | 	if (chip == NULL) | 
 | 		return -EBUSY; | 
 | 	if (len < TPM_HEADER_SIZE) | 
 | 		return -EBUSY; | 
 |  | 
 | 	client = (struct i2c_client *)TPM_VPRIV(chip); | 
 |  | 
 | 	client->flags = 0; | 
 |  | 
 | 	ret = request_locality(chip); | 
 | 	if (ret < 0) | 
 | 		return ret; | 
 |  | 
 | 	status = tpm_stm_i2c_status(chip); | 
 | 	if ((status & TPM_STS_COMMAND_READY) == 0) { | 
 | 		tpm_stm_i2c_cancel(chip); | 
 | 		if (wait_for_stat | 
 | 		    (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b, | 
 | 		     &chip->vendor.int_queue) < 0) { | 
 | 			ret = -ETIME; | 
 | 			goto out_err; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	for (i = 0; i < len - 1;) { | 
 | 		burstcnt = get_burstcount(chip); | 
 | 		size = min_t(int, len - i - 1, burstcnt); | 
 | 		ret = I2C_WRITE_DATA(client, TPM_DATA_FIFO, buf, size); | 
 | 		if (ret < 0) | 
 | 			goto out_err; | 
 |  | 
 | 		i += size; | 
 | 	} | 
 |  | 
 | 	status = tpm_stm_i2c_status(chip); | 
 | 	if ((status & TPM_STS_DATA_EXPECT) == 0) { | 
 | 		ret = -EIO; | 
 | 		goto out_err; | 
 | 	} | 
 |  | 
 | 	ret = I2C_WRITE_DATA(client, TPM_DATA_FIFO, buf + len - 1, 1); | 
 | 	if (ret < 0) | 
 | 		goto out_err; | 
 |  | 
 | 	status = tpm_stm_i2c_status(chip); | 
 | 	if ((status & TPM_STS_DATA_EXPECT) != 0) { | 
 | 		ret = -EIO; | 
 | 		goto out_err; | 
 | 	} | 
 |  | 
 | 	data = TPM_STS_GO; | 
 | 	I2C_WRITE_DATA(client, TPM_STS, &data, 1); | 
 |  | 
 | 	return len; | 
 | out_err: | 
 | 	tpm_stm_i2c_cancel(chip); | 
 | 	release_locality(chip); | 
 | 	return ret; | 
 | } | 
 |  | 
 | /* | 
 |  * tpm_stm_i2c_recv received TPM response through the I2C bus. | 
 |  * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h. | 
 |  * @param: buf,	the buffer to store datas. | 
 |  * @param: count, the number of bytes to send. | 
 |  * @return: In case of success the number of bytes received. | 
 |  *		In other case, a < 0 value describing the issue. | 
 |  */ | 
 | static int tpm_stm_i2c_recv(struct tpm_chip *chip, unsigned char *buf, | 
 | 			    size_t count) | 
 | { | 
 | 	int size = 0; | 
 | 	int expected; | 
 |  | 
 | 	if (chip == NULL) | 
 | 		return -EBUSY; | 
 |  | 
 | 	if (count < TPM_HEADER_SIZE) { | 
 | 		size = -EIO; | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	size = recv_data(chip, buf, TPM_HEADER_SIZE); | 
 | 	if (size < TPM_HEADER_SIZE) { | 
 | 		dev_err(chip->dev, "Unable to read header\n"); | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	expected = be32_to_cpu(*(__be32 *)(buf + 2)); | 
 | 	if (expected > count) { | 
 | 		size = -EIO; | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	size += recv_data(chip, &buf[TPM_HEADER_SIZE], | 
 | 					expected - TPM_HEADER_SIZE); | 
 | 	if (size < expected) { | 
 | 		dev_err(chip->dev, "Unable to read remainder of result\n"); | 
 | 		size = -ETIME; | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | out: | 
 | 	chip->vendor.cancel(chip); | 
 | 	release_locality(chip); | 
 | 	return size; | 
 | } | 
 |  | 
 | static bool tpm_st33_i2c_req_canceled(struct tpm_chip *chip, u8 status) | 
 | { | 
 | 	return (status == TPM_STS_COMMAND_READY); | 
 | } | 
 |  | 
 | static const struct file_operations tpm_st33_i2c_fops = { | 
 | 	.owner = THIS_MODULE, | 
 | 	.llseek = no_llseek, | 
 | 	.read = tpm_read, | 
 | 	.write = tpm_write, | 
 | 	.open = tpm_open, | 
 | 	.release = tpm_release, | 
 | }; | 
 |  | 
 | static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL); | 
 | static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL); | 
 | static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL); | 
 | static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL); | 
 | static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL); | 
 | static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, NULL); | 
 | static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL); | 
 | static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel); | 
 |  | 
 | static struct attribute *stm_tpm_attrs[] = { | 
 | 	&dev_attr_pubek.attr, | 
 | 	&dev_attr_pcrs.attr, | 
 | 	&dev_attr_enabled.attr, | 
 | 	&dev_attr_active.attr, | 
 | 	&dev_attr_owned.attr, | 
 | 	&dev_attr_temp_deactivated.attr, | 
 | 	&dev_attr_caps.attr, | 
 | 	&dev_attr_cancel.attr, NULL, | 
 | }; | 
 |  | 
 | static struct attribute_group stm_tpm_attr_grp = { | 
 | 	.attrs = stm_tpm_attrs | 
 | }; | 
 |  | 
 | static struct tpm_vendor_specific st_i2c_tpm = { | 
 | 	.send = tpm_stm_i2c_send, | 
 | 	.recv = tpm_stm_i2c_recv, | 
 | 	.cancel = tpm_stm_i2c_cancel, | 
 | 	.status = tpm_stm_i2c_status, | 
 | 	.req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, | 
 | 	.req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, | 
 | 	.req_canceled = tpm_st33_i2c_req_canceled, | 
 | 	.attr_group = &stm_tpm_attr_grp, | 
 | 	.miscdev = {.fops = &tpm_st33_i2c_fops,}, | 
 | }; | 
 |  | 
 | static int interrupts; | 
 | module_param(interrupts, int, 0444); | 
 | MODULE_PARM_DESC(interrupts, "Enable interrupts"); | 
 |  | 
 | static int power_mgt = 1; | 
 | module_param(power_mgt, int, 0444); | 
 | MODULE_PARM_DESC(power_mgt, "Power Management"); | 
 |  | 
 | /* | 
 |  * tpm_st33_i2c_probe initialize the TPM device | 
 |  * @param: client, the i2c_client drescription (TPM I2C description). | 
 |  * @param: id, the i2c_device_id struct. | 
 |  * @return: 0 in case of success. | 
 |  *	 -1 in other case. | 
 |  */ | 
 | static int | 
 | tpm_st33_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) | 
 | { | 
 | 	int err; | 
 | 	u8 intmask; | 
 | 	struct tpm_chip *chip; | 
 | 	struct st33zp24_platform_data *platform_data; | 
 |  | 
 | 	if (client == NULL) { | 
 | 		pr_info("%s: i2c client is NULL. Device not accessible.\n", | 
 | 			__func__); | 
 | 		err = -ENODEV; | 
 | 		goto end; | 
 | 	} | 
 |  | 
 | 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | 
 | 		dev_info(&client->dev, "client not i2c capable\n"); | 
 | 		err = -ENODEV; | 
 | 		goto end; | 
 | 	} | 
 |  | 
 | 	chip = tpm_register_hardware(&client->dev, &st_i2c_tpm); | 
 | 	if (!chip) { | 
 | 		dev_info(&client->dev, "fail chip\n"); | 
 | 		err = -ENODEV; | 
 | 		goto end; | 
 | 	} | 
 |  | 
 | 	platform_data = client->dev.platform_data; | 
 |  | 
 | 	if (!platform_data) { | 
 | 		dev_info(&client->dev, "chip not available\n"); | 
 | 		err = -ENODEV; | 
 | 		goto _tpm_clean_answer; | 
 | 	} | 
 |  | 
 | 	platform_data->tpm_i2c_buffer[0] = | 
 | 	    kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL); | 
 | 	if (platform_data->tpm_i2c_buffer[0] == NULL) { | 
 | 		err = -ENOMEM; | 
 | 		goto _tpm_clean_answer; | 
 | 	} | 
 | 	platform_data->tpm_i2c_buffer[1] = | 
 | 	    kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL); | 
 | 	if (platform_data->tpm_i2c_buffer[1] == NULL) { | 
 | 		err = -ENOMEM; | 
 | 		goto _tpm_clean_response1; | 
 | 	} | 
 |  | 
 | 	TPM_VPRIV(chip) = client; | 
 |  | 
 | 	chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT); | 
 | 	chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT); | 
 | 	chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT); | 
 | 	chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); | 
 |  | 
 | 	chip->vendor.locality = LOCALITY0; | 
 |  | 
 | 	if (power_mgt) { | 
 | 		err = gpio_request(platform_data->io_lpcpd, "TPM IO_LPCPD"); | 
 | 		if (err) | 
 | 			goto _gpio_init1; | 
 | 		gpio_set_value(platform_data->io_lpcpd, 1); | 
 | 	} | 
 |  | 
 | 	if (interrupts) { | 
 | 		init_completion(&platform_data->irq_detection); | 
 | 		if (request_locality(chip) != LOCALITY0) { | 
 | 			err = -ENODEV; | 
 | 			goto _tpm_clean_response2; | 
 | 		} | 
 | 		err = gpio_request(platform_data->io_serirq, "TPM IO_SERIRQ"); | 
 | 		if (err) | 
 | 			goto _gpio_init2; | 
 |  | 
 | 		clear_interruption(client); | 
 | 		err = request_irq(gpio_to_irq(platform_data->io_serirq), | 
 | 				&tpm_ioserirq_handler, | 
 | 				IRQF_TRIGGER_HIGH, | 
 | 				"TPM SERIRQ management", chip); | 
 | 		if (err < 0) { | 
 | 			dev_err(chip->dev , "TPM SERIRQ signals %d not available\n", | 
 | 				gpio_to_irq(platform_data->io_serirq)); | 
 | 			goto _irq_set; | 
 | 		} | 
 |  | 
 | 		err = I2C_READ_DATA(client, TPM_INT_ENABLE, &intmask, 1); | 
 | 		if (err < 0) | 
 | 			goto _irq_set; | 
 |  | 
 | 		intmask |= TPM_INTF_CMD_READY_INT | 
 | 			|  TPM_INTF_FIFO_AVALAIBLE_INT | 
 | 			|  TPM_INTF_WAKE_UP_READY_INT | 
 | 			|  TPM_INTF_LOCALITY_CHANGE_INT | 
 | 			|  TPM_INTF_STS_VALID_INT | 
 | 			|  TPM_INTF_DATA_AVAIL_INT; | 
 |  | 
 | 		err = I2C_WRITE_DATA(client, TPM_INT_ENABLE, &intmask, 1); | 
 | 		if (err < 0) | 
 | 			goto _irq_set; | 
 |  | 
 | 		intmask = TPM_GLOBAL_INT_ENABLE; | 
 | 		err = I2C_WRITE_DATA(client, (TPM_INT_ENABLE + 3), &intmask, 1); | 
 | 		if (err < 0) | 
 | 			goto _irq_set; | 
 |  | 
 | 		err = I2C_READ_DATA(client, TPM_INT_STATUS, &intmask, 1); | 
 | 		if (err < 0) | 
 | 			goto _irq_set; | 
 |  | 
 | 		chip->vendor.irq = interrupts; | 
 |  | 
 | 		tpm_gen_interrupt(chip); | 
 | 	} | 
 |  | 
 | 	tpm_get_timeouts(chip); | 
 |  | 
 | 	i2c_set_clientdata(client, chip); | 
 |  | 
 | 	dev_info(chip->dev, "TPM I2C Initialized\n"); | 
 | 	return 0; | 
 | _irq_set: | 
 | 	free_irq(gpio_to_irq(platform_data->io_serirq), (void *)chip); | 
 | _gpio_init2: | 
 | 	if (interrupts) | 
 | 		gpio_free(platform_data->io_serirq); | 
 | _gpio_init1: | 
 | 	if (power_mgt) | 
 | 		gpio_free(platform_data->io_lpcpd); | 
 | _tpm_clean_response2: | 
 | 	kzfree(platform_data->tpm_i2c_buffer[1]); | 
 | 	platform_data->tpm_i2c_buffer[1] = NULL; | 
 | _tpm_clean_response1: | 
 | 	kzfree(platform_data->tpm_i2c_buffer[0]); | 
 | 	platform_data->tpm_i2c_buffer[0] = NULL; | 
 | _tpm_clean_answer: | 
 | 	tpm_remove_hardware(chip->dev); | 
 | end: | 
 | 	pr_info("TPM I2C initialisation fail\n"); | 
 | 	return err; | 
 | } | 
 |  | 
 | /* | 
 |  * tpm_st33_i2c_remove remove the TPM device | 
 |  * @param: client, the i2c_client drescription (TPM I2C description). | 
 | 		clear_bit(0, &chip->is_open); | 
 |  * @return: 0 in case of success. | 
 |  */ | 
 | static int tpm_st33_i2c_remove(struct i2c_client *client) | 
 | { | 
 | 	struct tpm_chip *chip = (struct tpm_chip *)i2c_get_clientdata(client); | 
 | 	struct st33zp24_platform_data *pin_infos = | 
 | 		((struct i2c_client *)TPM_VPRIV(chip))->dev.platform_data; | 
 |  | 
 | 	if (pin_infos != NULL) { | 
 | 		free_irq(pin_infos->io_serirq, chip); | 
 |  | 
 | 		gpio_free(pin_infos->io_serirq); | 
 | 		gpio_free(pin_infos->io_lpcpd); | 
 |  | 
 | 		tpm_remove_hardware(chip->dev); | 
 |  | 
 | 		if (pin_infos->tpm_i2c_buffer[1] != NULL) { | 
 | 			kzfree(pin_infos->tpm_i2c_buffer[1]); | 
 | 			pin_infos->tpm_i2c_buffer[1] = NULL; | 
 | 		} | 
 | 		if (pin_infos->tpm_i2c_buffer[0] != NULL) { | 
 | 			kzfree(pin_infos->tpm_i2c_buffer[0]); | 
 | 			pin_infos->tpm_i2c_buffer[0] = NULL; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | #ifdef CONFIG_PM_SLEEP | 
 | /* | 
 |  * tpm_st33_i2c_pm_suspend suspend the TPM device | 
 |  * Added: Work around when suspend and no tpm application is running, suspend | 
 |  * may fail because chip->data_buffer is not set (only set in tpm_open in Linux | 
 |  * TPM core) | 
 |  * @param: client, the i2c_client drescription (TPM I2C description). | 
 |  * @param: mesg, the power management message. | 
 |  * @return: 0 in case of success. | 
 |  */ | 
 | static int tpm_st33_i2c_pm_suspend(struct device *dev) | 
 | { | 
 | 	struct tpm_chip *chip = dev_get_drvdata(dev); | 
 | 	struct st33zp24_platform_data *pin_infos = dev->platform_data; | 
 | 	int ret = 0; | 
 |  | 
 | 	if (power_mgt) { | 
 | 		gpio_set_value(pin_infos->io_lpcpd, 0); | 
 | 	} else { | 
 | 		if (chip->data_buffer == NULL) | 
 | 			chip->data_buffer = pin_infos->tpm_i2c_buffer[0]; | 
 | 		ret = tpm_pm_suspend(dev); | 
 | 	} | 
 | 	return ret; | 
 | }				/* tpm_st33_i2c_suspend() */ | 
 |  | 
 | /* | 
 |  * tpm_st33_i2c_pm_resume resume the TPM device | 
 |  * @param: client, the i2c_client drescription (TPM I2C description). | 
 |  * @return: 0 in case of success. | 
 |  */ | 
 | static int tpm_st33_i2c_pm_resume(struct device *dev) | 
 | { | 
 | 	struct tpm_chip *chip = dev_get_drvdata(dev); | 
 | 	struct st33zp24_platform_data *pin_infos = dev->platform_data; | 
 |  | 
 | 	int ret = 0; | 
 |  | 
 | 	if (power_mgt) { | 
 | 		gpio_set_value(pin_infos->io_lpcpd, 1); | 
 | 		ret = wait_for_serirq_timeout(chip, | 
 | 					  (chip->vendor.status(chip) & | 
 | 					  TPM_STS_VALID) == TPM_STS_VALID, | 
 | 					  chip->vendor.timeout_b); | 
 | 	} else { | 
 | 		if (chip->data_buffer == NULL) | 
 | 			chip->data_buffer = pin_infos->tpm_i2c_buffer[0]; | 
 | 		ret = tpm_pm_resume(dev); | 
 | 		if (!ret) | 
 | 			tpm_do_selftest(chip); | 
 | 	} | 
 | 	return ret; | 
 | }				/* tpm_st33_i2c_pm_resume() */ | 
 | #endif | 
 |  | 
 | static const struct i2c_device_id tpm_st33_i2c_id[] = { | 
 | 	{TPM_ST33_I2C, 0}, | 
 | 	{} | 
 | }; | 
 | MODULE_DEVICE_TABLE(i2c, tpm_st33_i2c_id); | 
 | static SIMPLE_DEV_PM_OPS(tpm_st33_i2c_ops, tpm_st33_i2c_pm_suspend, | 
 | 	tpm_st33_i2c_pm_resume); | 
 | static struct i2c_driver tpm_st33_i2c_driver = { | 
 | 	.driver = { | 
 | 		   .owner = THIS_MODULE, | 
 | 		   .name = TPM_ST33_I2C, | 
 | 		   .pm = &tpm_st33_i2c_ops, | 
 | 		   }, | 
 | 	.probe = tpm_st33_i2c_probe, | 
 | 	.remove = tpm_st33_i2c_remove, | 
 | 	.id_table = tpm_st33_i2c_id | 
 | }; | 
 |  | 
 | module_i2c_driver(tpm_st33_i2c_driver); | 
 |  | 
 | MODULE_AUTHOR("Christophe Ricard (tpmsupport@st.com)"); | 
 | MODULE_DESCRIPTION("STM TPM I2C ST33 Driver"); | 
 | MODULE_VERSION("1.2.0"); | 
 | MODULE_LICENSE("GPL"); |