diff -uNr linux-2.6.35.4/Documentation/tpm/tpm_stm_st19_i2c.txt linux-st19i2c/Documentation/tpm/tpm_stm_st19_i2c.txt --- linux-2.6.35.4/Documentation/tpm/tpm_stm_st19_i2c.txt 1969-12-31 18:00:00.000000000 -0600 +++ linux-st19i2c/Documentation/tpm/tpm_stm_st19_i2c.txt 2010-09-17 09:42:46.296572075 -0500 @@ -0,0 +1,170 @@ +/* + * STMicroelectronics TPM I2C Linux driver for TPM ST19NP18 + * 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 + */ + +PURPOSE OF THE DOCUMENT +------------------------ +This document is intend to describe how to install the TPM driver for +TPM ST19NP18 using I2C protocols. + + +PLATFORM USED FOR TESTING +-------------------------- +During the development, several embedded platforms running ARM CPU have been +used. +Validated platforms listing: +- TI Beagleboard +- STMicroelectronics Spear 300 +- STMicroelectronics Spear 600 + +REQUIREMENTS +------------- +Software +========= +This TPM driver could be install under a kernel which implement at least the +following features: +- Linux GENERIC_GPIO programming interfaces. +- I2C new style programming interface base (with probe & remove functions) +- 1 I2C adapter (I2C Linux controller driver) or use of I2C_GPIO driver for I2C +bitbanging. + + +Hardware +========= +To run a TPM the platform needs at least: +- 2 Power supply (3.3V). +- 1 I2C controller or 2 GPIOs used for SDA & SCL (I2C bit bang method). +- 2 GPIOs for signals accept_command & data_available. + +TPM I2C speed is 100Khz (Maximum) + +All TPM signals work at 3.3V + +HOW TO INSTALL +--------------- +Platform installation file +=========================== +(N.B: platform file in arch//mach-/ + + +1 - Software integration +========================= + could be: alpha, arm, avr32, blackfin, cris, frv, h8300, ia64, +m32r, m68k, m68knommu, parisc, powerpc, s390, sh, sparc, sparc64, um, x86, +xtensa... + corresponds to your platform + +In the file where the machine_init() function exists, the developer must +declare: +- 1 struct st19np18_platform_data to provide which gpio the driver will use. + * The accept_pin and data_avail_pin gpio are configured as input only. + * This gpio management is under the platform developer responsability. + +Finally in the machine_init() function provided in the same file, the developer +should use the well known function i2c_register_board_info() from the I2C Linux +API Core. + +2- Hardware integration +======================== +- ST recommends connecting VPS1 and VPS2 to board power supply and at least two +GNDs (on each side of TSSOP28 package). (See datasheet for further informations) + +- As the ST19NP18 has no internal pull up, ST recommands to had: + * 2 external pull up on SDA & SCL signals (RpSDA/RpSCL) with value according +to the abacus on page 40 or the "I2C Bus specification", version 2.1 January +2000. + + +Platform integration advises +============================= + +For power management purposes, the kernel will send a TPM_SaveState command in the +suspend tpm driver function. +If the platform generate a TPM Init event on wakeup, the first TPM command that should +be executed before the Linux kernel is back (resume function execution) is +TPM_Startup(ST_STATE). + +Here is an example with beagleboard: +==================================== +Depending on the platform, the developper should specify in +the platform init file the following informations: +- The platform gpio's used to managed the tpm's accept_pin/data_avail_pin +(in a struct st19np18_platform_data declaration). +- The TPM I2C 7 bits address (TPM_I2C_ST19_ADDR_WR) (in a struct i2c_board_info). + +Then the developper should add the TPM slave device to the good i2c adapter with the +i2c_register_board_info function (Assuming that the gpio and the i2c bus are well configured). + +Note: For the beagleboard configure your kernel with the following option: CONFIG_OMAP_MUX=y + +file arch\arm\mach-omap2\board-omap3beagle.c +add the following: +----------------------------------------------------------------------- + +static struct st19np18_platform_data tpm_data = { + .accept_pin = 135, + .data_avail_pin = 143, +}; + +static struct i2c_board_info __initdata tpm_st19_i2c_board_info[] = { + { + I2C_BOARD_INFO(TPM_DRIVER_NAME, TPM_I2C_ST19_ADDR_WR), + .platform_data = &tpm_data, + }, +}; + +------------------------------------------------------------------------ +Then complete the beagleboard init to be like that: +------------------------------------------------------------------------ +static void __init omap3_beagle_init(void) +{ + omap3_mux_init(board_mux, OMAP_PACKAGE_CBB); + omap3_beagle_i2c_init(); + platform_add_devices(omap3_beagle_devices, + ARRAY_SIZE(omap3_beagle_devices)); + omap_serial_init(); + + omap_mux_init_gpio(170, OMAP_PIN_INPUT); + gpio_request(170, "DVI_nPD"); + /* REVISIT leave DVI powered down until it's needed ... */ + gpio_direction_output(170, true); + + usb_musb_init(&musb_board_data); + usb_ehci_init(&ehci_pdata); + omap3beagle_flash_init(); + + beagle_display_init(); + + /* Ensure SDRC pins are mux'd for self-refresh */ + omap_mux_init_signal("sdrc_cke0", OMAP_PIN_OUTPUT); + omap_mux_init_signal("sdrc_cke1", OMAP_PIN_OUTPUT); + omap_mux_init_gpio(((struct st19np18_platform_data *) + tpm_st19_i2c_board_info[0].platform_data)->data_avail_pin, + OMAP_PIN_INPUT); + omap_mux_init_gpio(((struct st19np18_platform_data *) + tpm_st19_i2c_board_info[0].platform_data)->accept_pin, + OMAP_PIN_INPUT); + + i2c_register_board_info(3, tpm_st19_i2c_board_info, ARRAY_SIZE(tpm_st19_i2c_board_info)); +} diff -uNr linux-2.6.35.4/drivers/char/tpm/Kconfig linux-st19i2c/drivers/char/tpm/Kconfig --- linux-2.6.35.4/drivers/char/tpm/Kconfig 2010-08-26 18:47:12.000000000 -0500 +++ linux-st19i2c/drivers/char/tpm/Kconfig 2010-09-16 14:15:16.685780128 -0500 @@ -60,4 +60,13 @@ Further information on this driver and the supported hardware can be found at http://www.prosec.rub.de/tpm +config TCG_ST19_I2C + tristate "STMicroelectronics ST19 I2C TPM" + depends on I2C + depends on GPIOLIB + ---help--- + If you have a TPM security chip from STMicroelectronics working with + an I2C bus say Yes and it will be accessible from within Linux. + To compile this driver as a module, choose M here; the module will be + called tpm_stm_st19_i2c. endif # TCG_TPM diff -uNr linux-2.6.35.4/drivers/char/tpm/Makefile linux-st19i2c/drivers/char/tpm/Makefile --- linux-2.6.35.4/drivers/char/tpm/Makefile 2010-08-26 18:47:12.000000000 -0500 +++ linux-st19i2c/drivers/char/tpm/Makefile 2010-09-17 10:17:24.076572385 -0500 @@ -9,3 +9,4 @@ obj-$(CONFIG_TCG_NSC) += tpm_nsc.o obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o +obj-$(CONFIG_TCG_ST19_I2C) += tpm_stm_st19_i2c.o diff -uNr linux-2.6.35.4/drivers/char/tpm/tpm_stm_st19_i2c.c linux-st19i2c/drivers/char/tpm/tpm_stm_st19_i2c.c --- linux-2.6.35.4/drivers/char/tpm/tpm_stm_st19_i2c.c 1969-12-31 18:00:00.000000000 -0600 +++ linux-st19i2c/drivers/char/tpm/tpm_stm_st19_i2c.c 2010-09-17 09:49:44.739668841 -0500 @@ -0,0 +1,800 @@ +/* + * STMicroelectronics TPM I2C Linux driver for TPM ST19NP18 + * 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_st19_i2c.c + * + * @Synopsis: + * ---------------------------------------------------------------------- + * 02/12/2008 + * - Stand alone implementation (without any TPM api) + * ---------------------------------------------------------------------- + * 03/02/2010 + * - Power management (suspend and resume functions) + * implementation + * ---------------------------------------------------------------------- + * 03/19/2010 + * - Use of the linux kernel TPM api --> driver/char/tpm + * ---------------------------------------------------------------------- + * 05/26/2010 + * - Update code for code submission and bug fixes: + * - Comments spelling fixes + * - Lindent script execution + * - checkpatch.pl script execution + * - fix syslog error when loaded as a module: + * "release() function missing and must be fixed" + * - name files change from + * stm_st19_tpm_i2c to tpm_stm_st19_i2c + * ---------------------------------------------------------------------- + * 06/15/2010 + * - Update for new tpm core device. + * num_opens --> is_open + * ---------------------------------------------------------------------- + * 07/08/2010 + * - Update probe, resume suspend functions + * - Fix issue suspend buffer and work around related to the + * chip->data_buffer not allocated. + * ---------------------------------------------------------------------- + * 09/03/2010 + * - Review under LKLM + * - Patches from Joe Perches after review which fix some break and + * some neatings. + * ---------------------------------------------------------------------- + * 09/16/2010 + * - Remove unaccurate comment. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "tpm.h" + +#include "tpm_stm_st19_i2c.h" + +#ifdef DEBUG +#define FUNC_ENTER() pr_info("%s\n", __func__) +#else +#define FUNC_ENTER() do {} while (0) +#endif + +static struct st19np18_platform_data *pin_infos; + +/* + * gpio_readpin is a wrapper to read a gpio value. + * Use generic gpio APIs + * @param: pin_id, the pin identifier where the value will be read. + * @return: the gpio value (should be 0 or 1) or negative errno + */ +static int gpio_readpin(int pin_id) +{ + int ret; + ret = gpio_direction_input(pin_id); + if (ret == 0) + return gpio_get_value(pin_id); + return ret; +} + +/* + * gpio_writepin is a wrapper to write a gpio value. + * Use generic gpio APIs. + * @param: pin_id, the pin identifier where the value will be wrote. + * @param: value, the value that will be written. + * @return: 0 in case of success + */ +#ifdef DEBUG +static int gpio_writepin(int pin_id, int value) +{ + int ret; + ret = gpio_direction_output(pin_id, value); + + if (ret == 0) + gpio_set_value(pin_id, value); + return ret; +} +#endif + +static int wait_until_good_shape(void) +{ + int state_data = 0; + int state_command = 0; + int timeout = msecs_to_jiffies(STARTUP_WAIT_INTERVAL); + int time = msecs_to_jiffies(TICK_GPIO_SPOOLING); + int ret = 0; + wait_queue_head_t queue; + + int wait_time = 0; + DEFINE_WAIT(__wait); + init_waitqueue_head(&queue); + + do { + prepare_to_wait(&queue, &__wait, TASK_INTERRUPTIBLE); + state_data = gpio_readpin(pin_infos->data_avail_pin); + state_command = gpio_readpin(pin_infos->accept_pin); + + if (state_data == 0 && state_command > 0) + return 0; + else if (wait_time >= timeout) + return -EIO; + else if (!signal_pending(current)) { + ret = schedule_timeout(time); + wait_time += time; + } else { + ret = -ERESTARTSYS; + break; + } + } while (1); + finish_wait(&queue, &__wait); + + return ret; +} + +/* + * wait_event_interruptible_on_gpio is a function that poll on + * GPIO dataavailable and GPIO acceptcommand + * @param: queue, the queue where the work will be stored + * @param: timeout, maximal pooling time. + * @return: DATA_ON in case of data_available pin goes high (logical value 1). + * COMMAND_ON in case of accept_command pin goes high (logical value 1). + * -EIO in case of data_available & accept_command pin goes high + * (logical value 1). + * -EPERM in case of data_available & accept_command pin still low + * (logical value 0). + */ +static int wait_event_interruptible_on_gpio(wait_queue_head_t queue, + int timeout) +{ + int state_data = 0; + int state_command = 0; + int ret = msecs_to_jiffies(TICK_GPIO_SPOOLING); + struct tpm_chip *chip = + (struct tpm_chip *)i2c_get_clientdata(pin_infos->client); + int long_timeout = + tpm_calc_ordinal_duration(chip, TPM_I2C_ORDINAL_LONG); + int wait_time = 0; + DEFINE_WAIT(__wait); + + if (timeout > long_timeout) + timeout = long_timeout; + + do { + prepare_to_wait(&queue, &__wait, TASK_INTERRUPTIBLE); + state_data = gpio_readpin(pin_infos->data_avail_pin); + state_command = gpio_readpin(pin_infos->accept_pin); + + if (state_data > 0 || state_command > 0) + break; + else if (wait_time >= timeout) + break; + else if (!signal_pending(current)) { + ret = + schedule_timeout(msecs_to_jiffies + (TICK_GPIO_SPOOLING)); + wait_time += msecs_to_jiffies(TICK_GPIO_SPOOLING); + } else { + ret = -ERESTARTSYS; + break; + } + } while (1); + finish_wait(&queue, &__wait); + + return (state_data && state_command) ? -EIO : state_data ? DATA_ON : + state_command ? COMMAND_ON : -EPERM; +} + +/* + * responseSize return the command size + * @param: buffer, command buffer. + * @param: size, the buffer size. + * @return: the command size. + */ +static int responseSize(const char *buffer, size_t size) +{ + size_t val = 0; + if (size >= TPM_HEADER_SIZE) { + val = (size_t) (((unsigned)buffer[2]) << 24 + | ((unsigned)buffer[3]) << 16 + | ((unsigned)buffer[4]) << 8 | (unsigned) + buffer[5]); + } + + if (val < TPM_BUFSIZE) + return val; + else + return TPM_BUFSIZE; +} + +/* + * tpm_stm_i2c_send send TPM commands through the I2C bus. + * Before sending any TPM commands, tpm_stm_i2c_send poll data_available and + * accept_command TPM GPIOs. + * + * In case the data_available is high (logical value 1), tpm_stm_i2c_send will + * empty the TPM FIFO by reading all the datas stored inside the TPM. + * + * Then, if the accept_command TPM GPIO is high(logical value 1) + * tpm_stm_i2c_send will first send the 10 bytes header of the TCG commands and + * then send the others bytes by 40 bytes blocks. + * + * data_available and accept_command TPM GPIOs will goes low when the TPM + * compute the command. + * + * @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 count) +{ + u32 ret = 0, i, size, ordinal, pin = 0; + struct i2c_client *client; + + FUNC_ENTER(); + + if (chip == NULL) + return -EBUSY; + if (count < TPM_HEADER_SIZE) + return -EBUSY; + client = (struct i2c_client *)pin_infos->client; + + ordinal = be32_to_cpu(*((__be32 *) (buf + 6))); + + /* i2c_client initialization */ + client->flags = 0; + + /* Wait for AcceptCmd signal high */ + /* Check if data are available before */ + /* sending data (data_avail_pin hight) */ + /* If data are available, we read the data */ + init_waitqueue_head(&pin_infos->write_queue); + pin = wait_event_interruptible_on_gpio( + pin_infos->write_queue, + tpm_calc_ordinal_duration(chip, ordinal)); + if (pin < 0) { + ret = pin; + goto end; + } + + client->flags = I2C_M_RD; + + size = TPM_HEADER_SIZE; + i = 0; + while (pin == DATA_ON && i < size) { + int bytes; + if (i == 0) + bytes = TPM_HEADER_SIZE; + else + bytes = min_t(int, count - i, TPM_I2C_BLOCK_SIZE); + + ret = i2c_master_recv(client, pin_infos->tpm_i2c_buffer[1], + bytes); + if (ret < 0) + goto end; + if (i == 0) { + size = responseSize(pin_infos->tpm_i2c_buffer[1], + count); + i += TPM_HEADER_SIZE; + } else + i += TPM_I2C_BLOCK_SIZE; + + if (i < size) + pin = wait_event_interruptible_on_gpio( + pin_infos->write_queue, + msecs_to_jiffies(TPM_I2C_SHORT)); + } + + pin = wait_event_interruptible_on_gpio(pin_infos->write_queue, + msecs_to_jiffies(TPM_I2C_SHORT)); + + /* i2c_client initialization */ + client->flags = 0; + + size = TPM_HEADER_SIZE; + i = 0; + while (i < size && pin == COMMAND_ON) { + int bytes; + + if (i == 0) + bytes = TPM_HEADER_SIZE; + else + bytes = min_t(int, count - i, TPM_I2C_BLOCK_SIZE); + + memcpy(pin_infos->tpm_i2c_buffer[0], buf + i, bytes); + + if (i == 0) { + size = responseSize(buf, count); + size = size < count ? size : count; + } + if (count < TPM_HEADER_SIZE) + bytes = count; + ret = i2c_master_send(client, pin_infos->tpm_i2c_buffer[0], + bytes); + if (ret < 0) { + pr_info("Failed to send data\n"); + goto end; + } + + if (i == 0) + i += TPM_HEADER_SIZE; + else + i += TPM_I2C_BLOCK_SIZE; + /* Wait for AcceptCmd signal hight */ + if (i < size) + pin = wait_event_interruptible_on_gpio( + pin_infos->write_queue, + msecs_to_jiffies(TPM_I2C_SHORT)); + + if (pin != COMMAND_ON) { + pr_info("Failed to read gpio pin (AcceptCmd)\n"); + ret = -EIO; + goto end; + } + } + + if (i == 0) { + pr_info("Failed to read gpio pin (AcceptCmd)\n"); + ret = -EIO; + } +end: + return ret ? ret : count; +} + +/* + * tpm_stm_i2c_recv received TPM response through the I2C bus. + * Before receiving any TPM response, tpm_stm_i2c_recv poll data_available and + * accept_command TPM GPIOs. + * + * In case the accept_command is high (logical value 1), tpm_stm_i2c_recv will + * do nothing. + * + * Then, if the data_available TPM GPIO is high(logical value 1) + * tpm_stm_i2c_recv will first receive the 10 bytes header of the TCG TPM + * response and then receive the others bytes by 40 bytes blocks. + * + * accept_command TPM GPIOs will goes high when the TPM Fofo is empty. + * + * @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 ret = 0; + int i, size; + int pin = 0; + struct i2c_client *client; + + FUNC_ENTER(); + + if (chip == NULL) + return -EBUSY; + if (count < TPM_HEADER_SIZE) + return -EBUSY; + + client = (struct i2c_client *)pin_infos->client; + + /* Configure TPM I2C */ + client->flags = I2C_M_RD; + + /* Spool on the good gpio as long as pin GPIO 3 not HIGHT */ + init_waitqueue_head(&chip->vendor.read_queue); + pin = wait_event_interruptible_on_gpio(chip->vendor.read_queue, + tpm_calc_ordinal_duration(chip, TPM_I2C_ORDINAL_LONG)); + + size = TPM_HEADER_SIZE; + i = 0; + while (i < size && pin == DATA_ON) { + int bytes; + + if (count < TPM_HEADER_SIZE) + bytes = count; + else if (i == 0) + bytes = TPM_HEADER_SIZE; + else + bytes = min_t(int, size - i, TPM_I2C_BLOCK_SIZE); + + ret = i2c_master_recv(client, pin_infos->tpm_i2c_buffer[1], + bytes); + if (ret < 0) { + pr_info(" Failed to read gpio pin (DataAvailable)\n"); + goto end; + } + + if (!buf) { + pr_info("read buffer is NULL\n"); + goto end; + } + + memcpy(buf + i, pin_infos->tpm_i2c_buffer[1], size); + + if (i == 0) { + size = responseSize(buf, size); + if (size > count) + size = count; + i += TPM_HEADER_SIZE; + } else + i += TPM_I2C_BLOCK_SIZE; + + if (i < size) + pin = wait_event_interruptible_on_gpio( + chip->vendor.read_queue, + msecs_to_jiffies(TPM_I2C_SHORT)); + } + + if (i == 0) { + pr_info("Failed to read gpio pin (DataAvailable)\n"); + ret = -EIO; + goto end; + } + return size; +end: + return ret; +} + +/* + * 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) +{ +} /* tpm_stm_i2c_cancel() */ + +/* + * tpm_stm_i2c_status is not implemented because TIS registers are not + * implemented. + */ +static u8 tpm_stm_i2c_status(struct tpm_chip *chip) +{ + return -ENOSYS; +} /* tpm_stm_i2c_status() */ + +/* + * tpm_st19_i2c_ioctl provides 2 handles: + * - TPMIOC_CANCEL: allow to CANCEL a TPM commands execution. + * See tpm_stm_i2c_cancel description above + * - TPMIOC_TRANSMIT: allow to transmit a TPM commands. + * + * @return: In case of success, return TPM response size. + * In other case return < 0 value describing the issue. + */ +static ssize_t tpm_st19_i2c_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int in_size = 0, out_size = 0; + struct tpm_chip *chip = file->private_data; + + switch (cmd) { + case TPMIOC_CANCEL: + tpm_stm_i2c_cancel(chip); + return -ENOSYS; + case TPMIOC_TRANSMIT: + if (copy_from_user(pin_infos->tpm_i2c_buffer[0], + (const char *)arg, TPM_HEADER_SIZE)) + return -EFAULT; + in_size = responseSize(pin_infos->tpm_i2c_buffer[0], + TPM_HEADER_SIZE); + if (in_size > sizeof(pin_infos->tpm_i2c_buffer[0])) + in_size = sizeof(pin_infos->tpm_i2c_buffer[0]); + if (copy_from_user(pin_infos->tpm_i2c_buffer[0], + (const char *)arg, in_size)) + return -EFAULT; + tpm_stm_i2c_send(chip, pin_infos->tpm_i2c_buffer[0], in_size); + + out_size = tpm_stm_i2c_recv(chip, pin_infos->tpm_i2c_buffer[1], + TPM_BUFSIZE); + if (copy_to_user((char *)arg, pin_infos->tpm_i2c_buffer[1], + out_size)) + return -EFAULT; + return out_size; + default: + return -ENOTTY; + } + return -ENOTTY; +} + +static const struct file_operations tpm_st19_i2c_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = tpm_read, + .ioctl = tpm_st19_i2c_ioctl, + .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, + .attr_group = &stm_tpm_attr_grp, + .miscdev = {.fops = &tpm_st19_i2c_fops,}, +}; + +/* + * tpm_st19_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_st19_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int err; + struct tpm_chip *chip; + struct st19np18_platform_data *platform_data; + + FUNC_ENTER(); + + err = 0; + + /* Check I2C platform functionnalities */ + if (client == NULL) { + pr_info("client is NULL. exiting.\n"); + err = -ENODEV; + goto end; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_info("client not i2c capable\n"); + err = -ENODEV; + goto end; + } + + chip = tpm_register_hardware(&client->dev, &st_i2c_tpm); + if (!chip) { + err = -ENODEV; + goto end; + } + + /* + * ST19 TPM does not support interrupt. chip->vendor.irq is only + * set to a value greater that 0 because status function have no + * sense with this device (TIS register not available) + */ + chip->vendor.irq = 1; + + platform_data = client->dev.platform_data; + pin_infos = platform_data; + 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_response; + } + + platform_data->client = client; + + /* Register GPIO pin through generic Linux GPIO API */ + err = gpio_request(platform_data->accept_pin, "accept command"); + if (err) + goto _gpio_init; + + err = gpio_request(platform_data->data_avail_pin, "data available"); + if (err) + goto _gpio_init; + + err = wait_until_good_shape(); + if (err) + goto _gpio_set; + + tpm_get_timeouts(chip); + + /* attach chip datas to client */ + i2c_set_clientdata(client, chip); + pin_infos->bChipF = false; + + pr_info("TPM I2C Initialized\n"); + return 0; +_gpio_set: +_gpio_init: + if (platform_data) { + gpio_free(platform_data->accept_pin); + gpio_free(platform_data->data_avail_pin); + } +_tpm_clean_response: + tpm_remove_hardware(chip->dev); + if (platform_data->tpm_i2c_buffer[1] != NULL) { + kfree(platform_data->tpm_i2c_buffer[1]); + platform_data->tpm_i2c_buffer[1] = NULL; + } +_tpm_clean_answer: + if (platform_data->tpm_i2c_buffer[0] != NULL) { + kfree(platform_data->tpm_i2c_buffer[0]); + platform_data->tpm_i2c_buffer[0] = NULL; + } + pin_infos->bChipF = true; +end: + pr_info("TPM I2C initialisation fail\n"); + return err; +} + +/* + * tpm_st19_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 __devexit int tpm_st19_i2c_remove(struct i2c_client *client) +{ + struct tpm_chip *chip = (struct tpm_chip *)i2c_get_clientdata(client); + FUNC_ENTER(); + + if (pin_infos != NULL) { + gpio_free(pin_infos->accept_pin); + gpio_free(pin_infos->data_avail_pin); + + /* Check if chip has been previously clean */ + if (pin_infos->bChipF != true) + tpm_remove_hardware(chip->dev); + if (pin_infos->tpm_i2c_buffer[1] != NULL) { + kfree(pin_infos->tpm_i2c_buffer[1]); + pin_infos->tpm_i2c_buffer[1] = NULL; + } + if (pin_infos->tpm_i2c_buffer[0] != NULL) { + kfree(pin_infos->tpm_i2c_buffer[0]); + pin_infos->tpm_i2c_buffer[0] = NULL; + } + } + + return 0; +} + +/* + * tpm_st19_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_st19_i2c_pm_suspend(struct i2c_client *client, pm_message_t mesg) +{ + struct tpm_chip *chip = + (struct tpm_chip *)i2c_get_clientdata(pin_infos->client); + int ret = 0; + if (chip->data_buffer == NULL) + chip->data_buffer = pin_infos->tpm_i2c_buffer[0]; + ret = tpm_pm_suspend(&client->dev, mesg); + return ret; +} /* tpm_st19_i2c_suspend() */ + +/* + * tpm_st19_i2c_pm_resume resume the TPM device + * This part of the Linux driver should be move in an other part or + * environment (bootloader ?) + * @param: client, the i2c_client drescription (TPM I2C description). + * @return: 0 in case of success. + */ +static int tpm_st19_i2c_pm_resume(struct i2c_client *client) +{ + struct tpm_chip *chip = + (struct tpm_chip *)i2c_get_clientdata(pin_infos->client); + int ret = 0; + if (chip->data_buffer == NULL) + chip->data_buffer = pin_infos->tpm_i2c_buffer[0]; + ret = tpm_pm_resume(&client->dev); + return ret; +} /* tpm_st19_i2c_pm_resume() */ + +static const struct i2c_device_id tpm_st19_i2c_id[] = { + {TPM_DRIVER_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, tpm_st19_i2c_id); + +static struct i2c_driver tpm_st19_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = TPM_DRIVER_NAME, + }, + .probe = tpm_st19_i2c_probe, + .remove = tpm_st19_i2c_remove, + .resume = tpm_st19_i2c_pm_resume, + .suspend = tpm_st19_i2c_pm_suspend, + .id_table = tpm_st19_i2c_id +}; + +/* + * tpm_st19_i2c_init initialize driver + * @return: 0 if successful, else non zero value. + */ +static int __init tpm_st19_i2c_init(void) +{ + FUNC_ENTER(); + return i2c_add_driver(&tpm_st19_i2c_driver); +} + +/* + * tpm_st19_i2c_exit The kernel calls this function during unloading the + * module or during shut down process + */ +static void __exit tpm_st19_i2c_exit(void) +{ + FUNC_ENTER(); + i2c_del_driver(&tpm_st19_i2c_driver); +} + +module_init(tpm_st19_i2c_init); +module_exit(tpm_st19_i2c_exit); + +MODULE_AUTHOR("Christophe Ricard (tpmsupport@st.com)"); +MODULE_DESCRIPTION("STM TPM I2C ST19 Driver"); +MODULE_VERSION("1.2.0"); +MODULE_LICENSE("GPL"); Binary files linux-2.6.35.4/drivers/char/tpm/.tpm_stm_st19_i2c.c.swp and linux-st19i2c/drivers/char/tpm/.tpm_stm_st19_i2c.c.swp differ diff -uNr linux-2.6.35.4/drivers/char/tpm/tpm_stm_st19_i2c.h linux-st19i2c/drivers/char/tpm/tpm_stm_st19_i2c.h --- linux-2.6.35.4/drivers/char/tpm/tpm_stm_st19_i2c.h 1969-12-31 18:00:00.000000000 -0600 +++ linux-st19i2c/drivers/char/tpm/tpm_stm_st19_i2c.h 2010-09-16 14:15:16.685780128 -0500 @@ -0,0 +1,63 @@ +/* + * STMicroelectronics TPM I2C Linux driver for TPM ST19NP18 + * 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: stm_st19_tpm_i2c.h + * + * @Date: 02/12/2008 + */ +#ifndef __STM_ST19_TPM_I2C_MAIN_H__ +#define __STM_ST19_TPM_I2C_MAIN_H__ + +#include +#include +#include +#include +#include +#include +#include + +#define MINOR_NUM_I2C 224 + +#define TPM_DRIVER_NAME "st19np18" + +#define TPM_BUFSIZE 2048 + +#define TPM_HEADER_SIZE 10 +#define TPM_I2C_BLOCK_SIZE 0x28 + +#define TPM_I2C_ORDINAL_LONG 0x0D /* TPM_ORD_TakeOwnership */ + +#define TPM_I2C_SHORT 2000 /* 2s */ +#define TICK_GPIO_SPOOLING 2 +#define STARTUP_WAIT_INTERVAL 8 /* 8ms */ + +/* ioctl commands */ +#define TPMIOC_CANCEL _IO('T', 0x00) /* Not supported */ +#define TPMIOC_TRANSMIT _IO('T', 0x01) + +#define DATA_ON 1 /* data available */ +#define COMMAND_ON 2 /* accept command */ + +#endif /* __STM_ST19_TPM_I2C_MAIN_H__ */ diff -uNr linux-2.6.35.4/include/linux/i2c/tpm_stm_st19_i2c.h linux-st19i2c/include/linux/i2c/tpm_stm_st19_i2c.h --- linux-2.6.35.4/include/linux/i2c/tpm_stm_st19_i2c.h 1969-12-31 18:00:00.000000000 -0600 +++ linux-st19i2c/include/linux/i2c/tpm_stm_st19_i2c.h 2010-09-16 14:15:16.685780128 -0500 @@ -0,0 +1,45 @@ +/* + * STMicroelectronics TPM I2C Linux driver for TPM ST19NP18 + * Copyright (C) 2009, 2010 STMicroelectronics + * Christophe RICARD tpmsupport@st.com + * 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. + * + * @File: stm_st19_tpm_i2c.h + * + * @Date: 06/15/2008 + */ +#ifndef __STM_ST19_TPM_I2C_H__ +#define __STM_ST19_TPM_I2C_H__ + +#include + +#define TPM_DRIVER_NAME "st19np18" +#define TPM_I2C_ST19_ADDR_WR (0x26 >> 1) + +struct st19np18_platform_data { + int accept_pin; /* accept command pin */ + int data_avail_pin;/* data available pin */ + struct i2c_client *client; + bool bChipF; + u8 *tpm_i2c_buffer[2]; /* 0 Request 1 Response */ + wait_queue_head_t write_queue; +}; + +#endif /* __STM_ST19_TPM_I2C_H__ */