diff options
Diffstat (limited to 'meta-v1000/recipes-kernel/amd-spi')
-rw-r--r-- | meta-v1000/recipes-kernel/amd-spi/amd-spi_1.0.bb | 16 | ||||
-rw-r--r-- | meta-v1000/recipes-kernel/amd-spi/files/Makefile | 14 | ||||
-rw-r--r-- | meta-v1000/recipes-kernel/amd-spi/files/spi_amd.c | 479 | ||||
-rw-r--r-- | meta-v1000/recipes-kernel/amd-spi/files/spi_amd.h | 28 | ||||
-rw-r--r-- | meta-v1000/recipes-kernel/amd-spi/files/spirom.c | 519 | ||||
-rw-r--r-- | meta-v1000/recipes-kernel/amd-spi/files/spirom.h | 53 |
6 files changed, 0 insertions, 1109 deletions
diff --git a/meta-v1000/recipes-kernel/amd-spi/amd-spi_1.0.bb b/meta-v1000/recipes-kernel/amd-spi/amd-spi_1.0.bb deleted file mode 100644 index b23e5ce6..00000000 --- a/meta-v1000/recipes-kernel/amd-spi/amd-spi_1.0.bb +++ /dev/null @@ -1,16 +0,0 @@ -DESCRIPTION = "This kernel module provides support for AMD SPI controller driver" -LICENSE = "BSD | GPLv2" -LIC_FILES_CHKSUM = "file://spi_amd.c;endline=29;md5=e9fdf6da58412e619d89ec9e135a1be3" - -inherit module - -SRC_URI = "file://Makefile \ - file://spi_amd.c \ - file://spi_amd.h \ - file://spirom.c \ - file://spirom.h \ - " - -S = "${WORKDIR}" - -# The inherit of module.bbclass will take care of the rest diff --git a/meta-v1000/recipes-kernel/amd-spi/files/Makefile b/meta-v1000/recipes-kernel/amd-spi/files/Makefile deleted file mode 100644 index f778a69a..00000000 --- a/meta-v1000/recipes-kernel/amd-spi/files/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -obj-m := spi_amd.o spirom.o - -SRC := $(shell pwd) - -all: - $(MAKE) -C $(KERNEL_SRC) M=$(SRC) - -modules_install: - $(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install - -clean: - rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c - rm -f Module.markers Module.symvers modules.order - rm -rf .tmp_versions Modules.symvers diff --git a/meta-v1000/recipes-kernel/amd-spi/files/spi_amd.c b/meta-v1000/recipes-kernel/amd-spi/files/spi_amd.c deleted file mode 100644 index 998d9ea6..00000000 --- a/meta-v1000/recipes-kernel/amd-spi/files/spi_amd.c +++ /dev/null @@ -1,479 +0,0 @@ -/***************************************************************************** -* -* Copyright (c) 2013, Advanced Micro Devices, Inc. -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Advanced Micro Devices, Inc. nor the names of -* its contributors may be used to endorse or promote products derived -* from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -* -* -***************************************************************************/ -#include <linux/init.h> -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/spi/spi.h> -#include <linux/kthread.h> - -#include "spi_amd.h" - -struct amd_platform_data { - u8 chip_select; -}; - -struct amd_spi { - void __iomem *io_remap_addr; - unsigned long io_base_addr; - u32 rom_addr; - struct spi_master *master; - struct amd_platform_data controller_data; - struct task_struct *kthread_spi; - struct list_head msg_queue; - wait_queue_head_t wq; -}; - -static struct pci_device_id amd_spi_pci_device_id[] = { - { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LPC_BRIDGE) }, - {} -}; -MODULE_DEVICE_TABLE(pci, amd_spi_pci_device_id); - -static inline u8 amd_spi_readreg8(struct spi_master *master, int idx) -{ - struct amd_spi *amd_spi = spi_master_get_devdata(master); - - return ioread8((u8 *)amd_spi->io_remap_addr + idx); -} - -static inline void amd_spi_writereg8(struct spi_master *master, int idx, - u8 val) -{ - struct amd_spi *amd_spi = spi_master_get_devdata(master); - - iowrite8(val, ((u8 *)amd_spi->io_remap_addr + idx)); -} - -static inline void amd_spi_setclear_reg8(struct spi_master *master, int idx, - u8 set, u8 clear) -{ - u8 tmp = amd_spi_readreg8(master, idx); - tmp = (tmp & ~clear) | set; - amd_spi_writereg8(master, idx, tmp); -} - -static inline u32 amd_spi_readreg32(struct spi_master *master, int idx) -{ - struct amd_spi *amd_spi = spi_master_get_devdata(master); - - return ioread32((u8 *)amd_spi->io_remap_addr + idx); -} - -static inline void amd_spi_writereg32(struct spi_master *master, int idx, - u32 val) -{ - struct amd_spi *amd_spi = spi_master_get_devdata(master); - - iowrite32(val, ((u8 *)amd_spi->io_remap_addr + idx)); -} - -static inline void amd_spi_setclear_reg32(struct spi_master *master, int idx, - u32 set, u32 clear) -{ - u32 tmp = amd_spi_readreg32(master, idx); - tmp = (tmp & ~clear) | set; - amd_spi_writereg32(master, idx, tmp); -} - -static void amd_spi_select_chip(struct spi_master *master) -{ - struct amd_spi *amd_spi = spi_master_get_devdata(master); - u8 chip_select = amd_spi->controller_data.chip_select; - - amd_spi_setclear_reg8(master, AMD_SPI_ALT_CS_REG, chip_select, - AMD_SPI_ALT_CS_MASK); -} - - -static void amd_spi_clear_fifo_ptr(struct spi_master *master) -{ - amd_spi_setclear_reg32(master, AMD_SPI_CTRL0_REG, AMD_SPI_FIFO_CLEAR, - AMD_SPI_FIFO_CLEAR); -} - -static void amd_spi_set_opcode(struct spi_master *master, u8 cmd_opcode) -{ - amd_spi_setclear_reg32(master, AMD_SPI_CTRL0_REG, cmd_opcode, - AMD_SPI_OPCODE_MASK); -} - -static inline void amd_spi_set_rx_count(struct spi_master *master, - u8 rx_count) -{ - amd_spi_setclear_reg8(master, AMD_SPI_RX_COUNT_REG, rx_count, 0xff); -} - -static inline void amd_spi_set_tx_count(struct spi_master *master, - u8 tx_count) -{ - amd_spi_setclear_reg8(master, AMD_SPI_TX_COUNT_REG, tx_count, 0xff); -} - -static void amd_spi_execute_opcode(struct spi_master *master) -{ - struct amd_spi *amd_spi = spi_master_get_devdata(master); - bool spi_busy; - - /* Set ExecuteOpCode bit in the CTRL0 register */ - amd_spi_setclear_reg32(master, AMD_SPI_CTRL0_REG, AMD_SPI_EXEC_CMD, - AMD_SPI_EXEC_CMD); - - /* poll for SPI bus to become idle */ - spi_busy = (ioread32((u8 *)amd_spi->io_remap_addr + - AMD_SPI_CTRL0_REG) & AMD_SPI_BUSY) == AMD_SPI_BUSY; - while (spi_busy) { - set_current_state(TASK_INTERRUPTIBLE); - schedule(); - set_current_state(TASK_RUNNING); - spi_busy = (ioread32((u8 *)amd_spi->io_remap_addr + - AMD_SPI_CTRL0_REG) & AMD_SPI_BUSY) == AMD_SPI_BUSY; - } -} - -/* Helper function */ -#ifdef CONFIG_SPI_DEBUG -static void amd_spi_dump_reg(struct spi_master *master) -{ - struct amd_spi *amd_spi = spi_master_get_devdata(master); - - printk(KERN_DEBUG DRIVER_NAME ": SPI CTRL 0 registers: 0x%.8x\n", - ioread32((u8 *)amd_spi->io_remap_addr + AMD_SPI_CTRL0_REG)); - /* - * We cannot read CTRL1 register, because reading it would - * inadvertently increment the FIFO pointer. - */ - printk(KERN_DEBUG DRIVER_NAME ": SPI ALT CS registers: 0x%.2x\n", - ioread8((u8 *)amd_spi->io_remap_addr + AMD_SPI_ALT_CS_REG)); - printk(KERN_DEBUG DRIVER_NAME ": SPI Tx Byte Count: 0x%.2x\n", - ioread8((u8 *)amd_spi->io_remap_addr + AMD_SPI_TX_COUNT_REG)); - printk(KERN_DEBUG DRIVER_NAME ": SPI Rx Byte Count: 0x%.2x\n", - ioread8((u8 *)amd_spi->io_remap_addr + AMD_SPI_RX_COUNT_REG)); - printk(KERN_DEBUG DRIVER_NAME ": SPI Status registers: 0x%.8x\n", - ioread32((u8 *)amd_spi->io_remap_addr + AMD_SPI_STATUS_REG)); -} -#else -static void amd_spi_dump_reg(struct spi_master *master) {} -#endif - - -static int amd_spi_master_setup(struct spi_device *spi) -{ - struct spi_master *master = spi->master; - struct amd_spi *amd_spi = spi_master_get_devdata(master); - - amd_spi->controller_data.chip_select = spi->chip_select; - - amd_spi_select_chip(master); - - return 0; -} - -static int amd_spi_master_transfer(struct spi_master *master, - struct spi_message *msg) -{ - struct amd_spi *amd_spi = spi_master_get_devdata(master); - - /* - * Add new message to the queue and let the kernel thread know - * about it. - */ - list_add_tail(&msg->queue, &amd_spi->msg_queue); - wake_up_interruptible(&amd_spi->wq); - - return 0; -} -static int amd_spi_thread(void *t) -{ - struct amd_spi *amd_spi = t; - struct spi_master *master = amd_spi->master; - struct spi_transfer *transfer = NULL; - struct spi_message *message = NULL; - int direction = 0,i = 0,saved_index = 0; - int opcode_found = 0,recv_flag = 0,tx_len = 0,rx_len = 0; - u8 cmd_opcode = 0; - long timeout = 0; - u8 *buffer = NULL; - - /* - * What we do here is actually pretty simple. We pick one message - * at a time from the message queue set up by the controller, and - * then process all the spi_transfers of that spi_message in one go. - * We then remove the message from the queue, and complete the - * transaction. This might not be the best approach, but this is how - * we chose to implement this. Note that out SPI controller has FIFO - * size of 70 bytes, but we consider it to contain a maximum of - * 64-bytes of data and 3-bytes of address. - */ - while (1) { - /* - * Let us wait on a wait queue till the message queue is empty. - */ - do { - timeout = wait_event_interruptible_timeout(amd_spi->wq, - !list_empty(&amd_spi->msg_queue),1000); - - /* check stop condition */ - if (kthread_should_stop()) { - set_current_state(TASK_RUNNING); - return 0; - } - } while(timeout == 0); - - /* - * Else, pull the very first message from the queue and process - * all transfers within that message. And process the messages - * in a pure linear fashion. We also remove the spi_message - * from the queue. - */ - message = list_entry(amd_spi->msg_queue.next, - struct spi_message, queue); - list_del_init(&message->queue); - - /* We store the CS# line to be used for this spi_message */ - amd_spi->controller_data.chip_select = - message->spi->chip_select; - - /* Setting all variables to default value. */ - direction = i = 0; - opcode_found = 0; - recv_flag = tx_len = rx_len = 0; - cmd_opcode = 0; - buffer = NULL; - saved_index = 0; - - amd_spi_select_chip(master); - - /* - * This loop extracts spi_transfers from the spi message, - * programs the command into command register. Pointer variable - * *buffer* points to either tx_buf or rx_buf of spi_transfer - * depending on direction of transfer. Also programs FIFO of - * controller if data has to be transmitted. - */ - list_for_each_entry(transfer, &message->transfers, - transfer_list) - { - if(transfer->rx_buf != NULL) - direction = RECEIVE; - else if(transfer->tx_buf != NULL) - direction = TRANSMIT; - - switch (direction) { - case TRANSMIT: - buffer = (u8 *)transfer->tx_buf; - - if(opcode_found != 1) { - /* Store no. of bytes to be sent into - * FIFO */ - tx_len = transfer->len - 1; - /* Store opcode */ - cmd_opcode = *(u8 *)transfer->tx_buf; - /* Pointing to start of TX data */ - buffer++; - /* Program the command register*/ - amd_spi_set_opcode(master, cmd_opcode); - opcode_found = 1; - } else { - /* Store no. of bytes to be sent into - * FIFO */ - tx_len = transfer->len; - } - - /* Write data into the FIFO. */ - for (i = 0; i < tx_len; i++) { - iowrite8(buffer[i], - ((u8 *)amd_spi->io_remap_addr + - AMD_SPI_FIFO_BASE + - i + saved_index)); - } - - /* Set no. of bytes to be transmitted */ - amd_spi_set_tx_count(master, - tx_len + saved_index); - - /* - * Saving the index, from where next - * spi_transfer's data will be stored in FIFO. - */ - saved_index = i; - break; - case RECEIVE: - /* Store no. of bytes to be received from - * FIFO */ - rx_len = transfer->len; - buffer = (u8 *)transfer->rx_buf; - recv_flag=1; - break; - } - } - - /* Set the RX count to the number of bytes to expect in - * response */ - amd_spi_set_rx_count(master, rx_len ); - amd_spi_clear_fifo_ptr(master); - amd_spi_dump_reg(master); - /* Executing command */ - amd_spi_execute_opcode(master); - amd_spi_dump_reg(master); - - if(recv_flag == 1) { - /* Read data from FIFO to receive buffer */ - for (i = 0; i < rx_len; i++) { - buffer[i] = ioread8((u8 *)amd_spi->io_remap_addr - + AMD_SPI_FIFO_BASE - + tx_len + i); - } - - recv_flag = 0; - } - - /* Update statistics */ - message->actual_length = tx_len + rx_len + 1 ; - /* complete the transaction */ - message->status = 0; - spi_finalize_current_message(master); - } - - return 0; -} - -static int amd_spi_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - struct device *dev = &pdev->dev; - struct spi_master *master; - struct amd_spi *amd_spi; - u32 io_base_addr; - int err = 0; - - /* Allocate storage for spi_master and driver private data */ - master = spi_alloc_master(dev, sizeof(struct amd_spi)); - if (master == NULL) { - dev_err(dev, "Error allocating SPI master\n"); - return -ENOMEM; - } - - amd_spi = spi_master_get_devdata(master); - amd_spi->master = master; - - /* - * Lets first get the base address of SPI registers. The SPI Base - * Address is stored at offset 0xA0 into the LPC PCI configuration - * space. As per the specification, it is stored at bits 6:31 of the - * register. The address is aligned at 64-byte boundary, - * so we should just mask the lower 6 bits and get the address. - */ - pci_read_config_dword(pdev, AMD_PCI_LPC_SPI_BASE_ADDR_REG, - &io_base_addr); - amd_spi->io_base_addr = io_base_addr & AMD_SPI_BASE_ADDR_MASK; - amd_spi->io_remap_addr = ioremap_nocache(amd_spi->io_base_addr, - AMD_SPI_MEM_SIZE); - if (amd_spi->io_remap_addr == NULL) { - dev_err(dev, "ioremap of SPI registers failed\n"); - err = -ENOMEM; - goto err_free_master; - } - dev_dbg(dev, "io_base_addr: 0x%.8lx, io_remap_address: %p\n", - amd_spi->io_base_addr, amd_spi->io_remap_addr); - INIT_LIST_HEAD(&amd_spi->msg_queue); - init_waitqueue_head(&amd_spi->wq); - amd_spi->kthread_spi = kthread_run(amd_spi_thread, amd_spi, - "amd_spi_thread"); - - /* Now lets initialize the fields of spi_master */ - master->bus_num = 0; /* - * This should be the same as passed in - * spi_board_info structure - */ - master->num_chipselect = 4; /* Can be overwritten later during setup */ - master->mode_bits = 0; - master->flags = 0; - master->setup = amd_spi_master_setup; - master->transfer_one_message = amd_spi_master_transfer; - /* Register the controller with SPI framework */ - err = spi_register_master(master); - if (err) { - dev_err(dev, "error registering SPI controller\n"); - goto err_iounmap; - } - pci_set_drvdata(pdev, amd_spi); - - return 0; - -err_iounmap: - iounmap(amd_spi->io_remap_addr); -err_free_master: - spi_master_put(master); - - return 0; -} - -static void amd_spi_pci_remove(struct pci_dev *pdev) -{ - struct amd_spi *amd_spi = pci_get_drvdata(pdev); - - kthread_stop(amd_spi->kthread_spi); - iounmap(amd_spi->io_remap_addr); - spi_unregister_master(amd_spi->master); - spi_master_put(amd_spi->master); - pci_set_drvdata(pdev, NULL); -} - -static struct pci_driver amd_spi_pci_driver = { - .name = "amd_spi", - .id_table = amd_spi_pci_device_id, - .probe = amd_spi_pci_probe, - .remove = amd_spi_pci_remove, -}; - -static int __init amd_spi_init(void) -{ - int ret; - - pr_info("AMD SPI Driver v%s\n", SPI_VERSION); - - ret = pci_register_driver(&amd_spi_pci_driver); - if (ret) - return ret; - - return 0; -} -module_init(amd_spi_init); - -static void __exit amd_spi_exit(void) -{ - pci_unregister_driver(&amd_spi_pci_driver); -} -module_exit(amd_spi_exit); - -MODULE_LICENSE("Dual BSD/GPL"); -MODULE_AUTHOR("Arindam Nath <arindam.nath@amd.com>"); -MODULE_AUTHOR("Sanjay Mehta <sanju.mehta@amd.com>"); -MODULE_DESCRIPTION("AMD SPI Master Controller Driver"); diff --git a/meta-v1000/recipes-kernel/amd-spi/files/spi_amd.h b/meta-v1000/recipes-kernel/amd-spi/files/spi_amd.h deleted file mode 100644 index ec58b9a8..00000000 --- a/meta-v1000/recipes-kernel/amd-spi/files/spi_amd.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef SPI_AMD_H -#define SPI_AMD_H - -#define DRIVER_NAME "spi_amd" -#define SPI_VERSION "1.0" - -#define AMD_SPI_CTRL0_REG 0x00 - #define AMD_SPI_EXEC_CMD (0x1 << 16) - #define AMD_SPI_OPCODE_MASK 0xFF - #define AMD_SPI_FIFO_CLEAR (0x1 << 20) - #define AMD_SPI_BUSY (0x1 << 31) -#define AMD_SPI_ALT_CS_REG 0x1D - #define AMD_SPI_ALT_CS_MASK 0x3 -#define AMD_SPI_FIFO_BASE 0x80 -#define AMD_SPI_TX_COUNT_REG 0x48 -#define AMD_SPI_RX_COUNT_REG 0x4B -#define AMD_SPI_STATUS_REG 0x4C - -#define AMD_PCI_LPC_SPI_BASE_ADDR_REG 0xA0 -#define AMD_SPI_BASE_ADDR_MASK ~0x3F -#define AMD_SPI_MEM_SIZE 200 - -#define PCI_DEVICE_ID_AMD_LPC_BRIDGE 0x790E - -#define TRANSMIT 1 -#define RECEIVE 2 - -#endif /* SPI_AMD_H */ diff --git a/meta-v1000/recipes-kernel/amd-spi/files/spirom.c b/meta-v1000/recipes-kernel/amd-spi/files/spirom.c deleted file mode 100644 index cb5970ba..00000000 --- a/meta-v1000/recipes-kernel/amd-spi/files/spirom.c +++ /dev/null @@ -1,519 +0,0 @@ -/***************************************************************************** -* -* spirom.c - SPI ROM client driver -* -* Copyright (c) 2014, Advanced Micro Devices, Inc. -* -* 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 St, Fifth Floor, Boston, MA 02110-1301 USA -* -***************************************************************************/ -#include <linux/init.h> -#include <linux/module.h> -#include <linux/ioctl.h> -#include <linux/fs.h> -#include <linux/device.h> -#include <linux/err.h> -#include <linux/list.h> -#include <linux/errno.h> -#include <linux/mutex.h> -#include <linux/slab.h> -#include <linux/spi/spi.h> -#include <linux/pci.h> -#include <linux/delay.h> - -#include <linux/uaccess.h> - -#include "spirom.h" - -#define SPIROM_VERSION "0.2" - -/* - * SPI has a character major number assigned. We allocate minor numbers - * dynamically using a bitmask. You must use hotplug tools, such as udev - * (or mdev with busybox) to create and destroy the /dev/spiromB.C device - * nodes, since there is no fixed association of minor numbers with any - * particular SPI bus or device. - */ -#define SPIROM_MAJOR 153 /* assigned */ -#define N_SPI_MINORS 32 /* ... up to 256 */ - -#define SPI_BUS 0 -#define SPI_BUS_CS1 0 - -static unsigned long minors[N_SPI_MINORS / BITS_PER_LONG]; - - -struct spirom_data { - dev_t devt; - spinlock_t spi_lock; - struct spi_device *spi; - struct list_head device_entry; - struct completion done; - - struct mutex buf_lock; - unsigned users; -}; - -static LIST_HEAD(device_list); -static DEFINE_MUTEX(device_list_lock); - -/*-------------------------------------------------------------------------*/ - -/* - * We can't use the standard synchronous wrappers for file I/O; we - * need to protect against async removal of the underlying spi_device. - */ -static void spirom_complete(void *arg) -{ - complete(arg); -} - -static ssize_t -spirom_sync(struct spirom_data *spirom, struct spi_message *message) -{ - int status; - - message->complete = spirom_complete; - message->context = &spirom->done; - - spin_lock_irq(&spirom->spi_lock); - if (spirom->spi == NULL) - status = -ESHUTDOWN; - else - status = spi_async(spirom->spi, message); - spin_unlock_irq(&spirom->spi_lock); - - if (status == 0) { - /* - * There might be cases where the controller driver has been - * unloaded in the middle of a transaction. So we might end up - * in a situation where we will be waiting for an event which - * will never happen. So we provide a timeout of 1 second for - * situations like this. - */ - wait_for_completion_timeout(&spirom->done, HZ); - status = message->status; - if (status == 0) - status = message->actual_length; - } - return status; -} - -static int spirom_message(struct spirom_data *spirom, - struct spi_ioc_transfer *u_trans, unsigned long arg) -{ - struct spi_message msg; - struct spi_transfer *transfer; - u8 *buffer; - int status = u_trans->len; - - buffer = u_trans->buf; - spi_message_init(&msg); - - /* The very first spi_transfer will contain the command only */ - transfer = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL); - if (!transfer) - return -ENOMEM; - - transfer->tx_buf = buffer; - transfer->len = 1; - buffer += transfer->len; - spi_message_add_tail(transfer, &msg); - - /* - * If the command expects an address as its argument, we populate - * it in the very next spi_transfer. - */ - if (u_trans->addr_present) { - transfer = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL); - if (!transfer) - return -ENOMEM; - - transfer->tx_buf = buffer; - transfer->len = 3; // 3-byte address - buffer += transfer->len; - spi_message_add_tail(transfer, &msg); - } - - /* - * Next is data, which can have a maximum of 64-bytes, the size limited - * by the number of bytes that can stored in the controller FIFO. - */ - if (u_trans->len) { - transfer = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL); - if (!transfer) - return -ENOMEM; - - if (u_trans->direction == TRANSMIT) - transfer->tx_buf = buffer; - else if (u_trans->direction == RECEIVE) - transfer->rx_buf = buffer; - - transfer->len = u_trans->len; - /* No need to increment buffer pointer */ - spi_message_add_tail(transfer, &msg); - } - - status = spirom_sync(spirom, &msg); - - if (u_trans->direction == RECEIVE) { - /* - * The received data should have been populated in - * u_trans->buf, so we just need to copy it into the - * user-space buffer. - */ - buffer = u_trans->buf; - if (u_trans->addr_present) { - buffer += 4; // 1-byte command and 3-byte address - if(__copy_to_user((u8 __user *) - (((struct spi_ioc_transfer *)arg)->buf) + 4, - buffer, u_trans->len)) { - status = -EFAULT; - } - } else { - buffer += 1; // 1-byte command only - if(__copy_to_user((u8 __user *) - (((struct spi_ioc_transfer *)arg)->buf) + 1, - buffer, u_trans->len)) { - status = -EFAULT; - } - } - } - - /* Done with everything, free the memory taken by spi_transfer */ - while (msg.transfers.next != &msg.transfers) { - transfer = list_entry(msg.transfers.next, struct spi_transfer, - transfer_list); - msg.transfers.next = transfer->transfer_list.next; - transfer->transfer_list.next->prev = &msg.transfers; - kfree(transfer); - } - - return status; -} - -static long -spirom_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - int err = 0; - int retval = 0; - struct spirom_data *spirom; - struct spi_device *spi; - u32 tmp; - struct spi_ioc_transfer *ioc; - - /* Check type and command number */ - if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC) - return -ENOTTY; - - /* Check access direction once here; don't repeat below. - * IOC_DIR is from the user perspective, while access_ok is - * from the kernel perspective; so they look reversed. - */ - if (_IOC_DIR(cmd) & _IOC_READ) - err = !access_ok(VERIFY_WRITE, - (void __user *)arg, _IOC_SIZE(cmd)); - if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE) - err = !access_ok(VERIFY_READ, - (void __user *)arg, _IOC_SIZE(cmd)); - if (err) - return -EFAULT; - - /* guard against device removal before, or while, - * we issue this ioctl. - */ - spirom = filp->private_data; - spin_lock_irq(&spirom->spi_lock); - spi = spi_dev_get(spirom->spi); - spin_unlock_irq(&spirom->spi_lock); - - if (spi == NULL) - return -ESHUTDOWN; - - /* use the buffer lock here for triple duty: - * - prevent I/O (from us) so calling spi_setup() is safe; - * - prevent concurrent SPI_IOC_WR_* from morphing - * data fields while SPI_IOC_RD_* reads them; - * - SPI_IOC_MESSAGE needs the buffer locked "normally". - */ - mutex_lock(&spirom->buf_lock); - - /* segmented and/or full-duplex I/O request */ - if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0)) || - _IOC_DIR(cmd) !=_IOC_WRITE) { - retval = -ENOTTY; - goto out; - } - - tmp = sizeof(struct spi_ioc_transfer); - - /* copy into scratch area */ - ioc = kzalloc(tmp, GFP_KERNEL); - if (!ioc) { - retval = -ENOMEM; - goto out; - } - if (__copy_from_user(ioc, (struct spi_ioc_transfer __user *)arg, - tmp)) { - kfree(ioc); - retval = -EFAULT; - goto out; - } - - /* translate to spi_message, execute */ - retval = spirom_message(spirom, ioc, arg); - kfree(ioc); - -out: - mutex_unlock(&spirom->buf_lock); - spi_dev_put(spi); - return retval; -} - -static int spirom_open(struct inode *inode, struct file *filp) -{ - struct spirom_data *spirom; - int status = -ENXIO; - - mutex_lock(&device_list_lock); - - list_for_each_entry(spirom, &device_list, device_entry) { - if (spirom->devt == inode->i_rdev) { - status = 0; - break; - } - } - if (status == 0) { - if (status == 0) { - spirom->users++; - filp->private_data = spirom; - nonseekable_open(inode, filp); - } - } else - pr_debug("spirom: nothing for minor %d\n", iminor(inode)); - - mutex_unlock(&device_list_lock); - return status; -} - -static int spirom_release(struct inode *inode, struct file *filp) -{ - struct spirom_data *spirom; - int status = 0; - - mutex_lock(&device_list_lock); - spirom = filp->private_data; - filp->private_data = NULL; - - /* last close? */ - spirom->users--; - if (!spirom->users) { - int dofree; - - /* ... after we unbound from the underlying device? */ - spin_lock_irq(&spirom->spi_lock); - dofree = (spirom->spi == NULL); - spin_unlock_irq(&spirom->spi_lock); - - if (dofree) - kfree(spirom); - } - mutex_unlock(&device_list_lock); - - return status; -} - -static const struct file_operations spirom_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = spirom_ioctl, - .open = spirom_open, - .release = spirom_release, -}; - -static int __init add_spi_device_to_bus(void) -{ - struct spi_master *spi_master; - struct spi_device *spi_device; - struct spi_board_info spi_info; - - spi_master = spi_busnum_to_master(SPI_BUS); - if (!spi_master) { - printk(KERN_ALERT "Please make sure to \'modprobe " - "spi_amd\' driver first\n"); - return -1; - } - memset(&spi_info, 0, sizeof(struct spi_board_info)); - - strlcpy(spi_info.modalias, "spirom", SPI_NAME_SIZE); - spi_info.bus_num = SPI_BUS; //Bus number of SPI master - spi_info.chip_select = SPI_BUS_CS1; //CS on which SPI device is connected - - spi_device = spi_new_device(spi_master, &spi_info); - if (!spi_device) - return -ENODEV; - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* The main reason to have this class is to make mdev/udev create the - * /dev/spiromB.C character device nodes exposing our userspace API. - * It also simplifies memory management. - */ - -static struct class *spirom_class; - -/*-------------------------------------------------------------------------*/ - -static int spirom_probe(struct spi_device *spi) -{ - struct spirom_data *spirom; - int status; - unsigned long minor; - - /* Allocate driver data */ - spirom = kzalloc(sizeof(*spirom), GFP_KERNEL); - if (!spirom) - return -ENOMEM; - - /* Initialize the driver data */ - spirom->spi = spi; - spin_lock_init(&spirom->spi_lock); - mutex_init(&spirom->buf_lock); - - INIT_LIST_HEAD(&spirom->device_entry); - init_completion(&spirom->done); - - /* If we can allocate a minor number, hook up this device. - * Reusing minors is fine so long as udev or mdev is working. - */ - mutex_lock(&device_list_lock); - minor = find_first_zero_bit(minors, N_SPI_MINORS); - if (minor < N_SPI_MINORS) { - struct device *dev; - - spirom->devt = MKDEV(SPIROM_MAJOR, minor); - dev = device_create(spirom_class, &spi->dev, spirom->devt, - spirom, "spirom%d.%d", - spi->master->bus_num, spi->chip_select); - status = IS_ERR(dev) ? PTR_ERR(dev) : 0; - } else { - dev_dbg(&spi->dev, "no minor number available!\n"); - status = -ENODEV; - } - if (status == 0) { - set_bit(minor, minors); - list_add(&spirom->device_entry, &device_list); - } - mutex_unlock(&device_list_lock); - - if (status == 0) - spi_set_drvdata(spi, spirom); - else - kfree(spirom); - - return status; -} - -static int spirom_remove(struct spi_device *spi) -{ - struct spirom_data *spirom = spi_get_drvdata(spi); - - /* make sure ops on existing fds can abort cleanly */ - spin_lock_irq(&spirom->spi_lock); - spirom->spi = NULL; - spi_set_drvdata(spi, NULL); - spin_unlock_irq(&spirom->spi_lock); - - /* prevent new opens */ - mutex_lock(&device_list_lock); - list_del(&spirom->device_entry); - clear_bit(MINOR(spirom->devt), minors); - device_destroy(spirom_class, spirom->devt); - if (spirom->users == 0) - kfree(spirom); - mutex_unlock(&device_list_lock); - - return 0; -} - -static struct spi_driver spirom_spi = { - .driver = { - .name = "spirom", - .owner = THIS_MODULE, - }, - .probe = spirom_probe, - .remove = spirom_remove, - - /* NOTE: suspend/resume methods are not necessary here. - * We don't do anything except pass the requests to/from - * the underlying controller. The refrigerator handles - * most issues; the controller driver handles the rest. - */ -}; - -/*-------------------------------------------------------------------------*/ - -static int __init spirom_init(void) -{ - int status; - - pr_info("AMD SPIROM Driver v%s\n", SPIROM_VERSION); - - /* Claim our 256 reserved device numbers. Then register a class - * that will key udev/mdev to add/remove /dev nodes. Last, register - * the driver which manages those device numbers. - */ - BUILD_BUG_ON(N_SPI_MINORS > 256); - status = register_chrdev(SPIROM_MAJOR, "spi", &spirom_fops); - if (status < 0) - return status; - - spirom_class = class_create(THIS_MODULE, "spirom"); - if (IS_ERR(spirom_class)) { - unregister_chrdev(SPIROM_MAJOR, spirom_spi.driver.name); - return PTR_ERR(spirom_class); - } - - status = spi_register_driver(&spirom_spi); - if (status < 0) { - class_destroy(spirom_class); - unregister_chrdev(SPIROM_MAJOR, spirom_spi.driver.name); - } - - status = add_spi_device_to_bus(); - if (status < 0) { - spi_unregister_driver(&spirom_spi); - class_destroy(spirom_class); - unregister_chrdev(SPIROM_MAJOR, spirom_spi.driver.name); - } - - return status; -} -module_init(spirom_init); - -static void __exit spirom_exit(void) -{ - spi_unregister_driver(&spirom_spi); - class_destroy(spirom_class); - unregister_chrdev(SPIROM_MAJOR, spirom_spi.driver.name); -} -module_exit(spirom_exit); - -MODULE_AUTHOR("Arindam Nath <arindam.nath@amd.com>"); -MODULE_DESCRIPTION("User mode SPI ROM interface"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("spi:spirom"); diff --git a/meta-v1000/recipes-kernel/amd-spi/files/spirom.h b/meta-v1000/recipes-kernel/amd-spi/files/spirom.h deleted file mode 100644 index 941b357a..00000000 --- a/meta-v1000/recipes-kernel/amd-spi/files/spirom.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef SPIROM_H -#define SPIROM_H - -#include <linux/types.h> - -/*---------------------------------------------------------------------------*/ - -/* IOCTL commands */ - -#define SPI_IOC_MAGIC 'k' - -#define TRANSMIT 1 -#define RECEIVE 2 - -/* - * struct spi_ioc_transfer - interface structure between application and ioctl - * - * @buf: Buffer to hold 1-byte command, 3-bytes address, and 64-byte data for - * transmit or receive. The internal FIFO of our controller can hold a - * maximum of 70 bytes, including the address. But here we assume the - * maximum data excluding address to be 64-bytes long. - * - * @direction: Direction of data transfer, either TRANSMIT or RECEIVE. - * - * @len: Length of data excluding command and address. - * - * @addr_present: Flag to indicate whether 'buf' above contains an address. - */ -struct spi_ioc_transfer { - __u8 buf[64 + 1 + 3]; - __u8 direction; - __u8 len; - __u8 addr_present; -}; - -/* not all platforms use <asm-generic/ioctl.h> or _IOC_TYPECHECK() ... */ -#define SPI_MSGSIZE(N) \ - ((((N)*(sizeof (struct spi_ioc_transfer))) < (1 << _IOC_SIZEBITS)) \ - ? ((N)*(sizeof (struct spi_ioc_transfer))) : 0) -#define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)]) - -/* SPI ROM command codes */ -#define ROM_WREN 0x06 -#define ROM_WRDI 0x04 -#define ROM_RDSR 0x05 -#define ROM_RDID 0x9F -#define ROM_CHIP_ERASE 0x60 -#define ROM_SECTOR_ERASE 0x20 -#define ROM_BLOCK_ERASE 0xD8 -#define ROM_READ 0x03 -#define ROM_WRITE 0x02 - -#endif /* SPIROM_H */ |