diff options
author | Mark Allyn <mark.a.allyn@intel.com> | 2010-11-17 15:45:36 -0800 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-11-19 17:20:40 -0800 |
commit | 4856ab33eb5c33bdf17a5a1bd8d720bffe5f6110 (patch) | |
tree | d53549166190fb85bb173a4a468bd897037b8577 | |
parent | d948d5f96a4b5923599a7a657dbbd8660b0f9fa5 (diff) |
Staging: sep: Introduce sep driver
This driver is for the Security Processor, a dedicated encryption
and decryption driver that is used on the Intel mobile platform.
This has been checked with checkpatch and there are four
warnings for lines over 80 charactors.
There is one compile warning. This is for a function that is
only used if the rar register driver is needed. There is an
ifdef in a header file that stubs out the rar register driver
if the rar register is not configured.
This driver does add a configuration, which is CONFIG_DX_SEP.
Signed-off-by: Mark Allyn <mark.a.allyn@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/staging/Kconfig | 2 | ||||
-rw-r--r-- | drivers/staging/Makefile | 1 | ||||
-rw-r--r-- | drivers/staging/sep/Kconfig | 11 | ||||
-rw-r--r-- | drivers/staging/sep/Makefile | 2 | ||||
-rw-r--r-- | drivers/staging/sep/TODO | 12 | ||||
-rw-r--r-- | drivers/staging/sep/sep_dev.h | 156 | ||||
-rw-r--r-- | drivers/staging/sep/sep_driver.c | 4152 | ||||
-rw-r--r-- | drivers/staging/sep/sep_driver_api.h | 309 | ||||
-rw-r--r-- | drivers/staging/sep/sep_driver_config.h | 428 | ||||
-rw-r--r-- | drivers/staging/sep/sep_driver_hw_defs.h | 234 |
10 files changed, 5307 insertions, 0 deletions
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 49aee27b296..1f61914a3ab 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -119,6 +119,8 @@ source "drivers/staging/vme/Kconfig" source "drivers/staging/memrar/Kconfig" +source "drivers/staging/sep/Kconfig" + source "drivers/staging/iio/Kconfig" source "drivers/staging/zram/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 20c5641b6cd..75c0c1ff1cf 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_FB_UDL) += udlfb/ obj-$(CONFIG_HYPERV) += hv/ obj-$(CONFIG_VME_BUS) += vme/ obj-$(CONFIG_MRST_RAR_HANDLER) += memrar/ +obj-$(CONFIG_DX_SEP) += sep/ obj-$(CONFIG_IIO) += iio/ obj-$(CONFIG_ZRAM) += zram/ obj-$(CONFIG_WLAGS49_H2) += wlags49_h2/ diff --git a/drivers/staging/sep/Kconfig b/drivers/staging/sep/Kconfig new file mode 100644 index 00000000000..34f3ef5f9df --- /dev/null +++ b/drivers/staging/sep/Kconfig @@ -0,0 +1,11 @@ +config DX_SEP + tristate "Discretix SEP driver" + depends on PCI + default y + help + Discretix SEP driver; used for the security processor subsystem + on bard the Intel Mobile Internet Device. + + The driver's name is sep_driver. + + If unsure, select N. diff --git a/drivers/staging/sep/Makefile b/drivers/staging/sep/Makefile new file mode 100644 index 00000000000..628d5f91941 --- /dev/null +++ b/drivers/staging/sep/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_DX_SEP) := sep_driver.o + diff --git a/drivers/staging/sep/TODO b/drivers/staging/sep/TODO new file mode 100644 index 00000000000..371ee279f32 --- /dev/null +++ b/drivers/staging/sep/TODO @@ -0,0 +1,12 @@ +Todo's so far (from Alan Cox) +- Fix firmware loading - Done 09/10 M. Allyn +- Get firmware into firmware git tree - Firmware is non open source +- Review and tidy each algorithm function - Done 09/10 M. Allyn +- Check whether it can be plugged into any of the kernel crypto API + interfaces - Crypto API 'glue' is still not ready to submit +- Do something about the magic shared memory interface and replace it + with something saner (in Linux terms) - Done 09/10 M. Allyn +- Clean up unused ioctls - Needs vendor help +- Clean up unused fields in ioctl structures - Needs vendor help +- 64 bit size to be used for all user space addresses passed + to ioctl - Done 10/10 M. Allyn diff --git a/drivers/staging/sep/sep_dev.h b/drivers/staging/sep/sep_dev.h new file mode 100644 index 00000000000..7b4993b3650 --- /dev/null +++ b/drivers/staging/sep/sep_dev.h @@ -0,0 +1,156 @@ +#ifndef __SEP_DEV_H__ +#define __SEP_DEV_H__ + +/* + * + * sep_dev.h - Security Processor Device Structures + * + * Copyright(c) 2009,2010 Intel Corporation. All rights reserved. + * Contributions(c) 2009,2010 Discretix. All rights reserved. + * + * 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. + * + * 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., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * CONTACTS: + * + * Mark Allyn mark.a.allyn@intel.com + * Jayant Mangalampalli jayant.mangalampalli@intel.com + * + * CHANGES + * 2010.09.14 upgrade to Medfield + */ + +struct sep_device { + /* pointer to pci dev */ + struct pci_dev *pdev; + + /* character device file */ + struct cdev sep_cdev; + struct cdev sep_daemon_cdev; + struct cdev sep_singleton_cdev; + + /* devices (using misc dev) */ + struct miscdevice miscdev_sep; + struct miscdevice miscdev_singleton; + struct miscdevice miscdev_daemon; + + /* major / minor numbers of device */ + dev_t sep_devno; + dev_t sep_daemon_devno; + dev_t sep_singleton_devno; + + struct mutex sep_mutex; + struct mutex ioctl_mutex; + spinlock_t snd_rply_lck; + + /* flags to indicate use and lock status of sep */ + u32 pid_doing_transaction; + unsigned long in_use_flags; + + /* request daemon alread open */ + unsigned long request_daemon_open; + + /* 1 = Moorestown; 0 = Medfield */ + int mrst; + + /* address of the shared memory allocated during init for SEP driver + (coherent alloc) */ + dma_addr_t shared_bus; + size_t shared_size; + void *shared_addr; + + /* restricted access region (coherent alloc) */ + dma_addr_t rar_bus; + size_t rar_size; + void *rar_addr; + + /* Firmware regions; cache is at rar for Moorestown and + resident is at rar for Medfield */ + dma_addr_t cache_bus; + size_t cache_size; + void *cache_addr; + + dma_addr_t resident_bus; + size_t resident_size; + void *resident_addr; + + /* sep's scratchpad */ + dma_addr_t dcache_bus; + size_t dcache_size; + void *dcache_addr; + + /* Only used on Medfield */ + dma_addr_t extapp_bus; + size_t extapp_size; + void *extapp_addr; + + /* start address of the access to the SEP registers from driver */ + dma_addr_t reg_physical_addr; + dma_addr_t reg_physical_end; + void __iomem *reg_addr; + + /* wait queue head (event) of the driver */ + wait_queue_head_t event; + wait_queue_head_t event_request_daemon; + wait_queue_head_t event_mmap; + + struct sep_caller_id_entry + caller_id_table[SEP_CALLER_ID_TABLE_NUM_ENTRIES]; + + /* access flag for singleton device */ + unsigned long singleton_access_flag; + + /* transaction counter that coordinates the + transactions between SEP and HOST */ + unsigned long send_ct; + /* counter for the messages from sep */ + unsigned long reply_ct; + /* counter for the number of bytes allocated in the pool for the + current transaction */ + long data_pool_bytes_allocated; + + u32 num_of_data_allocations; + + /* number of the lli tables created in the current transaction */ + u32 num_lli_tables_created; + + /* number of data control blocks */ + u32 nr_dcb_creat; + + struct sep_dma_resource dma_res_arr[SEP_MAX_NUM_SYNC_DMA_OPS]; + +}; + +static inline void sep_write_reg(struct sep_device *dev, int reg, u32 value) +{ + void __iomem *addr = dev->reg_addr + reg; + writel(value, addr); +} + +static inline u32 sep_read_reg(struct sep_device *dev, int reg) +{ + void __iomem *addr = dev->reg_addr + reg; + return readl(addr); +} + +/* wait for SRAM write complete(indirect write */ +static inline void sep_wait_sram_write(struct sep_device *dev) +{ + u32 reg_val; + do + reg_val = sep_read_reg(dev, HW_SRAM_DATA_READY_REG_ADDR); + while (!(reg_val & 1)); +} + + +#endif diff --git a/drivers/staging/sep/sep_driver.c b/drivers/staging/sep/sep_driver.c new file mode 100644 index 00000000000..ef36239c7b2 --- /dev/null +++ b/drivers/staging/sep/sep_driver.c @@ -0,0 +1,4152 @@ +/* + * + * sep_driver.c - Security Processor Driver main group of functions + * + * Copyright(c) 2009,2010 Intel Corporation. All rights reserved. + * Contributions(c) 2009,2010 Discretix. All rights reserved. + * + * 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. + * + * 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., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * CONTACTS: + * + * Mark Allyn mark.a.allyn@intel.com + * Jayant Mangalampalli jayant.mangalampalli@intel.com + * + * CHANGES: + * + * 2009.06.26 Initial publish + * 2010.09.14 Upgrade to Medfield + * + */ +#define DEBUG +#include <linux/init.h> +#include <linux/module.h> +#include <linux/miscdevice.h> +#include <linux/fs.h> +#include <linux/cdev.h> +#include <linux/kdev_t.h> +#include <linux/mutex.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/poll.h> +#include <linux/wait.h> +#include <linux/pci.h> +#include <linux/firmware.h> +#include <linux/slab.h> +#include <linux/ioctl.h> +#include <asm/current.h> +#include <linux/ioport.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/pagemap.h> +#include <asm/cacheflush.h> +#include <linux/sched.h> +#include <linux/delay.h> + +#include <linux/netlink.h> +#include <linux/connector.h> +#include <linux/cn_proc.h> + +#include "sep_driver_hw_defs.h" +#include "sep_driver_config.h" +#include "sep_driver_api.h" +#include "sep_dev.h" + +/*---------------------------------------- + DEFINES +-----------------------------------------*/ + +#define SEP_RAR_IO_MEM_REGION_SIZE 0x40000 + +/*-------------------------------------------- + GLOBAL variables +--------------------------------------------*/ + +/* Keep this a single static object for now to keep the conversion easy */ + +static struct sep_device *sep_dev; + +/** + * sep_load_firmware - copy firmware cache/resident + * This functions copies the cache and resident from their source + * location into destination shared memory. + * @sep: pointer to struct sep_device + */ + +static int sep_load_firmware(struct sep_device *sep) +{ + const struct firmware *fw; + char *cache_name = "cache.image.bin"; + char *res_name = "resident.image.bin"; + char *extapp_name = "extapp.image.bin"; + + int error = 0; + unsigned int work1, work2, work3; + + /* set addresses and load resident */ + sep->resident_bus = sep->rar_bus; + sep->resident_addr = sep->rar_addr; + + error = request_firmware(&fw, res_name, &sep->pdev->dev); + if (error) { + dev_warn(&sep->pdev->dev, "cant request resident fw\n"); + return error; + } + + memcpy(sep->resident_addr, (void *)fw->data, fw->size); + sep->resident_size = fw->size; + release_firmware(fw); + + dev_dbg(&sep->pdev->dev, "resident virtual is %p\n", + sep->resident_addr); + dev_dbg(&sep->pdev->dev, "residnet bus is %lx\n", + (unsigned long)sep->resident_bus); + dev_dbg(&sep->pdev->dev, "resident size is %08x\n", + sep->resident_size); + + /* set addresses for dcache (no loading needed) */ + work1 = (unsigned int)sep->resident_bus; + work2 = (unsigned int)sep->resident_size; + work3 = (work1 + work2 + (1024 * 4)) & 0xfffff000; + sep->dcache_bus = (dma_addr_t)work3; + + work1 = (unsigned int)sep->resident_addr; + work2 = (unsigned int)sep->resident_size; + work3 = (work1 + work2 + (1024 * 4)) & 0xfffff000; + sep->dcache_addr = (void *)work3; + + sep->dcache_size = 1024 * 128; + + /* set addresses and load cache */ + sep->cache_bus = sep->dcache_bus + sep->dcache_size; + sep->cache_addr = sep->dcache_addr + sep->dcache_size; + + error = request_firmware(&fw, cache_name, &sep->pdev->dev); + if (error) { + dev_warn(&sep->pdev->dev, "cant request cache fw\n"); + return error; + } + + memcpy(sep->cache_addr, (void *)fw->data, fw->size); + sep->cache_size = fw->size; + release_firmware(fw); + + dev_dbg(&sep->pdev->dev, "cache virtual is %p\n", + sep->cache_addr); + dev_dbg(&sep->pdev->dev, "cache bus is %08lx\n", + (unsigned long)sep->cache_bus); + dev_dbg(&sep->pdev->dev, "cache size is %08x\n", + sep->cache_size); + + /* set addresses and load extapp */ + sep->extapp_bus = sep->cache_bus + (1024 * 370); + sep->extapp_addr = sep->cache_addr + (1024 * 370); + + error = request_firmware(&fw, extapp_name, &sep->pdev->dev); + if (error) { + dev_warn(&sep->pdev->dev, "cant request extapp fw\n"); + return error; + } + + memcpy(sep->extapp_addr, (void *)fw->data, fw->size); + sep->extapp_size = fw->size; + release_firmware(fw); + + dev_dbg(&sep->pdev->dev, "extapp virtual is %p\n", + sep->extapp_addr); + dev_dbg(&sep->pdev->dev, "extapp bus is %08llx\n", + (unsigned long long)sep->extapp_bus); + dev_dbg(&sep->pdev->dev, "extapp size is %08x\n", + sep->extapp_size); + + return error; +} + +MODULE_FIRMWARE("sep/cache.image.bin"); +MODULE_FIRMWARE("sep/resident.image.bin"); +MODULE_FIRMWARE("sep/extapp.image.bin"); + +/** + * sep_dump_message - dump the message that is pending + * @sep: sep device + */ + +static void sep_dump_message(struct sep_device *sep) +{ + int count; + u32 *p = sep->shared_addr; + for (count = 0; count < 12 * 4; count += 4) + dev_dbg(&sep->pdev->dev, + "Word %d of the message is %x\n", + count, *p++); +} + +/** + * sep_map_and_alloc_shared_area - allocate shared block + * @sep: security processor + * @size: size of shared area + */ + +static int sep_map_and_alloc_shared_area(struct sep_device *sep) +{ + sep->shared_addr = dma_alloc_coherent(&sep->pdev->dev, + sep->shared_size, + &sep->shared_bus, GFP_KERNEL); + + if (!sep->shared_addr) { + dev_warn(&sep->pdev->dev, + "shared memory dma_alloc_coherent failed\n"); + return -ENOMEM; + } + dev_dbg(&sep->pdev->dev, + "sep: shared_addr %x bytes @%p (bus %llx)\n", + sep->shared_size, sep->shared_addr, + (unsigned long long)sep->shared_bus); + return 0; +} + +/** + * sep_unmap_and_free_shared_area - free shared block + * @sep: security processor + */ +static void sep_unmap_and_free_shared_area(struct sep_device *sep) +{ + dev_dbg(&sep->pdev->dev, "shared area unmap and free\n"); + dma_free_coherent(&sep->pdev->dev, sep->shared_size, + sep->shared_addr, sep->shared_bus); +} + +/** + * sep_shared_bus_to_virt - convert bus/virt addresses + * Returns virtual address inside the shared area according + * to the bus address. + * @sep: pointer to struct sep_device + * @bus_address: address to convert + */ + +static void *sep_shared_bus_to_virt(struct sep_device *sep, + dma_addr_t bus_address) +{ + return sep->shared_addr + (bus_address - sep->shared_bus); +} + +/** + * open function for the singleton driver + * @inode_ptr struct inode * + * @file_ptr struct file * + */ + +static int sep_singleton_open(struct inode *inode_ptr, struct file *file_ptr) +{ + int error = 0; + + struct sep_device *sep; + + /** + * get the sep device structure and use it for the + * private_data field in filp for other methods + */ + sep = sep_dev; + + file_ptr->private_data = sep; + + dev_dbg(&sep->pdev->dev, "Singleton open for pid %d\n", + current->pid); + + dev_dbg(&sep->pdev->dev, "calling test and set for singleton 0\n"); + if (test_and_set_bit(0, &sep->singleton_access_flag)) { + error = -EBUSY; + goto end_function; + } + + dev_dbg(&sep->pdev->dev, + "sep_singleton_open end\n"); +end_function: + + return error; +} + +/** + * sep_open - device open method + * @inode: inode of sep device + * @filp: file handle to sep device + * + * Open method for the SEP device. Called when userspace opens + * the SEP device node. Must also release the memory data pool + * allocations. + * + * Returns zero on success otherwise an error code. + */ + +static int sep_open(struct inode *inode, struct file *filp) +{ + struct sep_device *sep; + + /** + * get the sep device structure and use it for the + * private_data field in filp for other methods + */ + sep = sep_dev; + filp->private_data = sep; + + dev_dbg(&sep->pdev->dev, "Open for pid %d\n", current->pid); + + /* anyone can open; locking takes place at transaction level */ + return 0; +} + +/** + * sep_singleton_release - close a SEP singleton device + * @inode: inode of SEP device + * @filp: file handle being closed + * + * Called on the final close of a SEP device. As the open protects against + * multiple simultaenous opens that means this method is called when the + * final reference to the open handle is dropped. + */ + +static int sep_singleton_release(struct inode *inode, struct file *filp) +{ + struct sep_device *sep = filp->private_data; + + dev_dbg(&sep->pdev->dev, "Singleton release for pid %d\n", + current->pid); + + clear_bit(0, &sep->singleton_access_flag); + + return 0; +} + +/** + * sep_request_daemonopen - request daemon open method + * @inode: inode of sep device + * @filp: file handle to sep device + * + * Open method for the SEP request daemon. Called when + * request daemon in userspace opens + * the SEP device node. + * + * Returns zero on success otherwise an error code. + */ + +static int sep_request_daemon_open(struct inode *inode, struct file *filp) +{ + struct sep_device *sep; + + int error = 0; + + sep = sep_dev; + filp->private_data = sep; + + dev_dbg(&sep->pdev->dev, "Request daemon open for pid %d\n", + current->pid); + + /* There is supposed to be only one request daemon */ + dev_dbg(&sep->pdev->dev, "calling test and set for req_dmon open 0\n"); + if (test_and_set_bit(0, &sep->request_daemon_open)) { + error = -EBUSY; + goto end_function; + } + +end_function: + + return error; +} + +/** + * sep_request_daemon_release - close a SEP daemon + * @inode: inode of SEP device + * @filp: file handle being closed + * + * Called on the final close of a SEP daemon. + */ + +static int sep_request_daemon_release(struct inode *inode, struct file *filp) +{ + struct sep_device *sep = filp->private_data; + + dev_dbg(&sep->pdev->dev, "Reques daemon release for pid %d\n", + current->pid); + + /* clear the request_daemon_open flag */ + clear_bit(0, &sep->request_daemon_open); + + return 0; +} + +/** + * This function raises interrupt to SEPm that signals that is has a + * new command from HOST + * @sep: struct sep_device * + */ +static int sep_req_daemon_send_reply_command_handler(struct sep_device *sep) +{ + int error = 0; + + unsigned long lck_flags; + + dev_dbg(&sep->pdev->dev, + "sep_req_daemon_send_reply_command_handler start\n"); + + error = 0; + + sep_dump_message(sep); + + /* counters are lockable region */ + spin_lock_irqsave(&sep->snd_rply_lck, lck_flags); + sep->send_ct++; + sep->reply_ct++; + + /* send the interrupt to SEP */ + sep_write_reg(sep, HW_HOST_HOST_SEP_GPR2_REG_ADDR, + sep->send_ct); + + sep->send_ct++; + + spin_unlock_irqrestore(&sep->snd_rply_lck, lck_flags); + + dev_dbg(&sep->pdev->dev, + "sep_req_daemon_send_reply send_ct %lx reply_ct %lx\n", + sep->send_ct, sep->reply_ct); + + dev_dbg(&sep->pdev->dev, + "sep_req_daemon_send_reply_command_handler end\n"); + + return error; +} + + +/** + * sep_free_dma_table_data_handler - handles the request + * for freeing dma table for synhronic actions + * @sep: pointere to struct sep_device + */ + +static int sep_free_dma_table_data_handler(struct sep_device *sep) +{ + /* counter */ + int count = 0; + + /* dcb counter */ + int dcb_counter = 0; + + /* pointer to the current dma_resource struct */ + struct sep_dma_resource *dma; + + dev_dbg(&sep->pdev->dev, + "sep_free_dma_table_data_handler start\n"); + + + for (dcb_counter = 0; dcb_counter < sep->nr_dcb_creat; + dcb_counter++) { + + dma = &sep->dma_res_arr[dcb_counter]; + + /* unmap and free input map array */ + if (dma->in_map_array) { + + for (count = 0; count < dma->in_num_pages; + count++) { + + dma_unmap_page(&sep->pdev->dev, + dma->in_map_array[count].dma_addr, + dma->in_map_array[count].size, + DMA_TO_DEVICE); + } + + kfree(dma->in_map_array); + } + + /* unmap output map array, DON'T free it yet */ + if (dma->out_map_array) { + for (count = 0; count < dma->out_num_pages; + count++) { + + dma_unmap_page(&sep->pdev->dev, + dma->out_map_array[count].dma_addr, + dma->out_map_array[count].size, + DMA_FROM_DEVICE); + } + + kfree(dma->out_map_array); + } + + /* free page cache for output */ + if (dma->in_page_array) { + for (count = 0; count < dma->in_num_pages; + count++) { + + flush_dcache_page(dma->in_page_array[count]); + page_cache_release(dma->in_page_array[count]); + } + + kfree(dma->in_page_array); + + } + + if (dma->out_page_array) { + for (count = 0; count < dma->out_num_pages; + count++) { + + if (!PageReserved(dma->out_page_array[count])) + SetPageDirty(dma->out_page_array[count]); + + flush_dcache_page(dma->out_page_array[count]); + page_cache_release(dma->out_page_array[count]); + } + + kfree(dma->out_page_array); + } + + /* reset all the values */ + dma->in_page_array = 0; + dma->out_page_array = 0; + dma->in_num_pages = 0; + dma->out_num_pages = 0; + dma->in_map_array = 0; + dma->out_map_array = 0; + dma->in_map_num_entries = 0; + dma->out_map_num_entries = 0; + + } + + sep->nr_dcb_creat = 0; + sep->num_lli_tables_created = 0; + + dev_dbg(&sep->pdev->dev, + "sep_free_dma_table_data_handler end\n"); + + return 0; +} + + +/** + * sep_request_daemon_mmap - maps the + * shared area to user space + * @filp: pointer to struct file + * @vma: pointer to vm_area_struct + */ +static int sep_request_daemon_mmap(struct file *filp, + struct vm_area_struct *vma) +{ + /* device */ + struct sep_device *sep = filp->private_data; + + dma_addr_t bus_address; + + int error = 0; + + dev_dbg(&sep->pdev->dev, "daemon mmap start\n"); + + if ((vma->vm_end - vma->vm_start) > SEP_DRIVER_MMMAP_AREA_SIZE) { + error = -EINVAL; + goto end_function; + } + + /* get physical address */ + bus_address = sep->shared_bus; + + dev_dbg(&sep->pdev->dev, + "bus_address is %08lx\n", + (unsigned long)bus_address); + + if (remap_pfn_range(vma, vma->vm_start, bus_address >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, vma->vm_page_prot)) { + + dev_warn(&sep->pdev->dev, + "remap_page_range failed\n"); + error = -EAGAIN; + goto end_function; + } + +end_function: + + dev_dbg(&sep->pdev->dev, "daemon mmap end\n"); + + return error; +} + +/** + * sep_request_daemon_poll - + * @sep: struct sep_device * for current sep device + * @filp: struct file * for open file + * @wait: poll_table * for poll + */ +static unsigned int sep_request_daemon_poll(struct file *filp, + poll_table *wait) +{ + u32 mask = 0; + + /* GPR2 register */ + u32 retval2; + + unsigned long lck_flags; + + struct sep_device *sep = filp->private_data; + + dev_dbg(&sep->pdev->dev, + "daemon poll: start\n"); + + dev_dbg(&sep->pdev->dev, "daemon poll: calling poll wait sep_event\n"); + + poll_wait(filp, &sep->event_request_daemon, wait); + + dev_dbg(&sep->pdev->dev, "daemon poll: exit poll wait sep_event\n"); + + dev_dbg(&sep->pdev->dev, + "daemon poll: send_ct is %lx reply ct is %lx\n", + sep->send_ct, sep->reply_ct); + + spin_lock_irqsave(&sep->snd_rply_lck, lck_flags); + /* check if the data is ready */ + if (sep->send_ct == sep->reply_ct) { + + spin_unlock_irqrestore(&sep->snd_rply_lck, lck_flags); + + retval2 = sep_read_reg(sep, HW_HOST_SEP_HOST_GPR2_REG_ADDR); + + dev_dbg(&sep->pdev->dev, + "daemon poll: data check (GPR2) is %x\n", retval2); + + /* check if PRINT request */ + if ((retval2 >> 30) & 0x1) { + + dev_dbg(&sep->pdev->dev, + "daemon poll: PRINTF request in\n"); + mask |= POLLIN; + goto end_function; + } + + /* check if NVS request */ + if (retval2 >> 31) { + + dev_dbg(&sep->pdev->dev, + "daemon poll: NVS request in\n"); + mask |= POLLPRI | POLLWRNORM; + } + } + + else { + + spin_unlock_irqrestore(&sep->snd_rply_lck, lck_flags); + + dev_dbg(&sep->pdev->dev, + "daemon poll: no reply received; returning 0\n"); + mask = 0; + } + +end_function: + + dev_dbg(&sep->pdev->dev, + "daemon poll: exit\n"); + return mask; +} + +/** + * sep_release - close a SEP device + * @inode: inode of SEP device + * @filp: file handle being closed + * + * Called on the final close of a SEP device. + */ + +static int sep_release(struct inode *inode, struct file *filp) +{ + struct sep_device *sep = filp->private_data; + + dev_dbg(&sep->pdev->dev, "Release for pid %d\n", current->pid); + + mutex_lock(&sep->sep_mutex); + /* is this the process that has a transaction open? + * If so, lets reset pid_doing_transaction to 0 and + * clear the in use flags, and then wake up sep_event + * so that other processes can do transactions + */ + dev_dbg(&sep->pdev->dev, "waking up event and mmap_event\n"); + if (sep->pid_doing_transaction == current->pid) { + clear_bit(SEP_MMAP_LOCK_BIT, &sep->in_use_flags); + clear_bit(SEP_SEND_MSG_LOCK_BIT, &sep->in_use_flags); + sep_free_dma_table_data_handler(sep); + wake_up(&sep->event); + sep->pid_doing_transaction = 0; + } + + mutex_unlock(&sep->sep_mutex); + return 0; +} + +/** + * sep_mmap - maps the shared area to user space + * @filp: pointer to struct file + * @vma: pointer to vm_area_struct + */ +static int sep_mmap(struct file *filp, struct vm_area_struct *vma) +{ + dma_addr_t bus_addr; + struct sep_device *sep = filp->private_data; + unsigned long error = 0; + + dev_dbg(&sep->pdev->dev, "mmap start\n"); + + + dev_dbg(&sep->pdev->dev, "calling wait on event_mmap, tsetbit" + " SEP_MMAP_LOCK_BIT in_use_flags 0\n"); + /* Set the transaction busy (own the device) */ + wait_event_interruptible(sep->event, + test_and_set_bit(SEP_MMAP_LOCK_BIT, + &sep->in_use_flags) == 0); + + if (signal_pending(current)) { + error = -EINTR; + goto end_function_with_error; + } + /* + * The pid_doing_transaction indicates that this process + * now owns the facilities to performa a transaction with + * the sep. While this process is performing a transaction, + * no other process who has the sep device open can perform + * any transactions. This method allows more than one process + * to have the device open at any given time, which provides + * finer granularity for device utilization by multiple + * processes. + */ + mutex_lock(&sep->sep_mutex); + sep->pid_doing_transaction = current->pid; + mutex_unlock(&sep->sep_mutex); + + /* zero the pools and the number of data pool alocation pointers */ + sep->data_pool_bytes_allocated = 0; + sep->num_of_data_allocations = 0; + + /* + * check that the size of the mapped range is as the size of the message + * shared area + */ + if ((vma->vm_end - vma->vm_start) > SEP_DRIVER_MMMAP_AREA_SIZE) { + error = -EINVAL; + goto end_function_with_error; + } + + dev_dbg(&sep->pdev->dev, + "shared_addr is %p\n", sep->shared_addr); + + /* get bus address */ + bus_addr = sep->shared_bus; + + dev_dbg(&sep->pdev->dev, + "bus_address is %lx\n", (unsigned long)bus_addr); + + if (remap_pfn_range(vma, vma->vm_start, bus_addr >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, vma->vm_page_prot)) { + dev_warn(&sep->pdev->dev, + "remap_page_range failed\n"); + error = -EAGAIN; + goto end_function_with_error; + } + + dev_dbg(&sep->pdev->dev, "mmap end\n"); + goto end_function; + +end_function_with_error: + + /* clear the bit */ + clear_bit(SEP_MMAP_LOCK_BIT, &sep->in_use_flags); + mutex_lock(&sep->sep_mutex); + sep->pid_doing_transaction = 0; + mutex_unlock(&sep->sep_mutex); + + /* raise event for stuck contextes */ + + dev_warn(&sep->pdev->dev, "mmap error - waking up event\n"); + wake_up(&sep->event); + +end_function: + + return error; +} + +/** + * sep_poll - + * @filp: pointer to struct file + * @wait: pointer to poll_table + */ +static unsigned int sep_poll(struct file *filp, poll_table *wait) +{ + u32 mask = 0; + u32 retval = 0; + u32 retval2 = 0; + + unsigned long lck_flags; + + struct sep_device *sep = filp->private_data; + + dev_dbg(&sep->pdev->dev, "poll: start\n"); + + /* am I the process that own the transaction? */ + mutex_lock(&sep->sep_mutex); + if (current->pid != sep->pid_doing_transaction) { + dev_warn(&sep->pdev->dev, "poll; wrong pid\n"); + mask = POLLERR; + mutex_unlock(&sep->sep_mutex); + goto end_function; + } + + mutex_unlock(&sep->sep_mutex); + + /* check if send command or send_reply were activated previously */ + if (!test_bit(SEP_SEND_MSG_LOCK_BIT, &sep->in_use_flags)) { + dev_warn(&sep->pdev->dev, "poll; lock bit set\n"); + mask = POLLERR; + goto end_function; + } + + /* add the event to the polling wait table */ + dev_dbg(&sep->pdev->dev, "poll: calling wait sep_event\n"); + + poll_wait(filp, &sep->event, wait); + + dev_dbg(&sep->pdev->dev, "poll: exit wait sep_event\n"); + + dev_dbg(&sep->pdev->dev, "poll: send_ct is %lx reply ct is %lx\n", + sep->send_ct, sep->reply_ct); + + /* check if error occured during poll */ + retval2 = sep_read_reg(sep, HW_HOST_SEP_HOST_GPR3_REG_ADDR); + + if (retval2 != 0x0) { + + dev_warn(&sep->pdev->dev, "poll; poll error %x\n", + retval2); + mask |= POLLERR; + goto end_function; + } + + spin_lock_irqsave(&sep->snd_rply_lck, lck_flags); + + if (sep->send_ct == sep->reply_ct) { + + spin_unlock_irqrestore(&sep->snd_rply_lck, lck_flags); + retval = sep_read_reg(sep, HW_HOST_SEP_HOST_GPR2_REG_ADDR); + dev_dbg(&sep->pdev->dev, "poll: data ready check (GPR2) %x\n", + retval); + + /* check if printf request */ + if ((retval >> 30) & 0x1) { + + dev_dbg(&sep->pdev->dev, "poll: sep printf request\n"); + wake_up(&sep->event_request_daemon); + goto end_function; + + } + + /* check if the this is sep reply or request */ + if (retval >> 31) { + + dev_dbg(&sep->pdev->dev, "poll: sep request\n"); + wake_up(&sep->event_request_daemon); + + } else { + + dev_dbg(&sep->pdev->dev, "poll: normal return\n"); + + /* in case it is again by send_reply_comand */ + + clear_bit(SEP_SEND_MSG_LOCK_BIT, &sep->in_use_flags); + + sep_dump_message(sep); + + dev_dbg(&sep->pdev->dev, |