Subject: [patch 06/16] axon driver registration and general char device interface Description: This driver provides interfaces for DaCS using the the Axon Cell Companion Chip. A pair of axon drives ( one on the host pci system and one using the Axon as a southbridge) work together to provide DMA between Host and Cell/Axon system, provde Shared memory support, DMA, and notifcation between systems. This patch provides the driver main code ( including the init and exit routines and driver entry points). Signed-off-by: H Brett Bolen (hbbolen@us.ibm.com) Signed-off-by: Murali Iyer (mniyer@us.ibm.com) Signed-off-by: Tim Schimke (tschimke@us.ibm.com) Signed-off-by: Jesse Arroyo (arroyoj@us.ibm.com) Index: linux-2.6.23.1/drivers/misc/triblade/axon.h =================================================================== --- /dev/null +++ linux-2.6.23.1/drivers/misc/triblade/axon.h @@ -0,0 +1,301 @@ +/** + * axon_h - axon driver data structs. + * + * 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright (C) IBM Corporation, 2008 + * + * Authors: H Brett Bolen , + * Tim Schimke , + * Jesse Arroyo , + * Murali N Iyer + * + */ + + +#ifndef __AXON_H__ +#define __AXON_H__ + +#include +#include +#include + +#ifndef CONFIG_TB_AXON_PCI +#include +#endif + + +#include "scratchpad.h" +#include "axon_callback.h" + +#ifdef CONFIG_TB_AXON_PCI +#include "axon_pci.h" +#else +#include "axon_sb.h" +#endif + +#include "axon_dmax.h" +#include "axon_util.h" + +/* device capabilities */ + +#define AXON_MATCH_XDR 0x00008001 +#define AXON_MATCH_DDR 0x00004001 +#define AXON_MATCH_MBX 0x00008004 + +#define AXON_BAR_NR 6 +#define MAX_RES_NR 8 + +#define MEM_DEV_NR 2 +#define DEV_DEV_NR 1 + +#define MODULE_NAME "axon" + +/* + * Shared memory areas must have a size which is at least 1MB and not + * greater than 128MB. Also the size must be a power of 2. + */ +#define LOCAL_SMA_SIZE_DEFAULT 1*1024*1024 /* default 1MB of local SMA */ + +/* d2d size + apnet size must be a power of 2 */ +#define LOCAL_D2D_SIZE ((4*1024) * 1024) /* for internal */ +#define LOCAL_APNET_SIZE (128*1024) /* for apnet */ +#define D2D_MSG_BUF_SIZE (16*1024) +#define D2D_MR_BUF_SIZE (LOCAL_D2D_SIZE - 256 \ + - LOCAL_APNET_SIZE\ + - D2D_MSG_BUF_SIZE) + +struct axon_d2d { + u32 msg_head; + u32 msg_tail; + u64 pcie_plb5_addr; + u64 pcie_mem1base_addr; + u32 dma_interrupt_values[4]; + u64 dma_channel_seq_issued[2]; + u64 dma_channel_seq_complete[2]; + u32 reserved1[46]; /* Pad to 256 byte boundary */ + struct d2d_message msg_buffer[D2D_MSG_BUF_SIZE/D2D_MESSAGE_SIZE]; + u32 apnet_buffer[LOCAL_APNET_SIZE/sizeof(u32)]; + u32 mr_buffer[D2D_MR_BUF_SIZE/sizeof(u32)]; +}; + +/* mmapped region shared by user and MR code */ +#define DMA_CMD_BUF_SIZE_DEFAULT (64*1024) + +/* id and version */ +extern const char *axon_name; +extern const char *axon_version; +extern const int axon_id; +extern const int axon_rev; + +/* + * @struct axon_dataq - holds data for usermod reads + */ +#define AXON_NUM_CALLBACKS 16 +#define AXON_DATAQ_BUFSZ 1024 +struct axon_dataq { + char buf[ AXON_DATAQ_BUFSZ]; + int buf_sz; + int read_index; + int write_index; + spinlock_t irq_lock; /* lock for isr */ + wait_queue_head_t irq_queue; /* read blocking */ + +}; + +/** + * struct axon - axon instance private data + */ +struct axon +{ + + /* identification - used to verify that both host and cell sides + * have matching versions. + */ + int id; + int ver; + int index; + + /* pci config */ + int irq; + char irq_name[32]; + + /* axon registers */ + void __iomem *mmio_base; + struct scratchpad_struct __iomem *scratchpad; + + u64 axon_base_paddr; + void __iomem *c3poreg_base_addr; + + struct dmax *dmax; + + + u64 pciereg_base_paddr; /* phys addr of PCI regs */ + u64 pciereg_size; + void __iomem *pciereg_base_addr; /* vaddr of PCI UTL regs */ + void __iomem *pciecnfg_base_addr; /* vaddr of PCI CNFG regs */ + +#ifdef CONFIG_TB_AXON_PCI + struct pci_dev *axon_device; + unsigned long pcie_mem0base; /* d2d */ + unsigned long pcie_mem0len; /* d2d */ + unsigned long pcie_mem1base; /* mmio */ + unsigned long pcie_mem1len; /* mmio */ + unsigned long pcie_mem2base; /* sma */ + unsigned long pcie_mem2len; /* sma */ +#else + + struct of_device *axon_device; + + dcr_host_t dcrreg_host; /* vaddr of PCI DCR regs */ + u64 dcrreg_base; /* base addr of PCI DCR regs */ + u64 dcrreg_size; + + u64 pcie_mem0base; + u64 pcie_mem0base_len; + u64 pcie_mem1base; + u64 pcie_mem1base_len; + u64 pcie_mem2base; + u64 pcie_mem2base_len; +#endif + + void __iomem *mbxreg_base_addr; /* addr to cause cbe mbx irq */ + u64 msi_addr; /* addr to cause hst msi irq */ + + u64 dma_mask; /* DMA mask */ + + /* axon chip internal bus addresses */ + u64 local_phys_plb5_addr; + u64 pcie_plb5_addr; + + /* AXONIO_SMA_INFO sma; */ + void *raw_sma_ptr; + int raw_sma_order; + void *raw_d2d_ptr; + int raw_d2d_order; + + + void *local_sma_address; /* user accessable */ + unsigned long local_sma_size; + dma_addr_t local_sma_dma_handle; + u64 local_sma_plb5_addr; /* chip internal bus address */ + int remote_sma_size; + void __iomem *remote_sma_address; /* user accessable */ + u64 remote_sma_paddr; /* phys addr of remote sma */ + u64 remote_sma_plb5_addr; /* chip internal bus address */ + + struct axon_d2d *local_d2d; + unsigned long local_d2d_size; + dma_addr_t local_d2d_dma_handle; + u64 local_apnet_plb5_addr; /*chip internal bus address */ + int remote_d2d_size; + struct axon_d2d __iomem *remote_d2d; + u64 remote_d2d_paddr; /* phys addr of remote d2d */ + u64 remote_apnet_plb5_addr; /* chip internal bus address */ + u64 remote_plb5_base_addr; /* chip internal bus address */ + + + struct cdev cdev; + + /* callbacks */ + struct axon_callback_item callbacks[ AXON_NUM_CALLBACKS]; + + /* Misc MR stuff */ + struct axon_mr_control *mr_control; + void *local_d2d_mr_address; + unsigned long local_d2d_mr_length; + + void __iomem *remote_d2d_mr_address; /* processor addr */ + int remote_d2d_mr_length; + void *raw_dma_cmd_buf; + void *dma_cmd_buf; + int dma_cmd_buf_length; + int num_memory_regions; + u64 maximum_mr_size_reg_pages; + u64 maximum_mr_request_size; + u64 maximum_dma_size_reg_pages; + u64 num_dma_descriptors; + + /* Additions for timing DMA setup time */ + u64 dma_ioctl_requests; + u64 total_dma_setup_time; + + struct axon_dataq readq; + u32 d2d_msg_rhead; /* head offset in remote d2d msgb */ + spinlock_t d2d_msg_lock; + + /* sysfs support */ + dev_t dev_id; + struct class_device *class_dev; + struct class *class; + + int piggyback_irqs; + + struct mutex open_lock; /* lock for opens */ + atomic_t open_count; /* number of opens on this FD */ + +#ifdef CONFIG_TB_AXON_MTRR_SMA + int mtrr_reg; +#endif + +}; + +/** + * axon_device_table - table of axon instances + */ +extern struct axon *axon_device_table; + +/** + * remote_sma_offset_to_plb5 - Provides the mapping of the remote SMA offset + * to the internal bus address of the Axon chip. + * @axon_priv: + * @offset: + */ +static inline u64 remote_sma_offset_to_plb5(struct axon *axon_priv, + unsigned long offset) +{ + return axon_priv->remote_sma_plb5_addr + offset; +} + +/** + * local_phys_to_plb5 - Provides the mapping of the local physical or DMA + * address to the internal bus address of the Axon chip. + * the internal + * @axon_priv: + * @addr: + * + */ +static inline u64 local_phys_to_plb5(struct axon *axon_priv, + unsigned long addr) +{ + return (addr & 0x000FFFFFFFFFFFFFul) | axon_priv->local_phys_plb5_addr; + +} + +/* + * prototypes + */ + +/** + * add_to_readqueue + * @axon_priv - dev instance structure + * @type - * @data - + */ +void add_to_readqueue( struct axon *axon_priv, char *type, int data); + + + + +#endif /* __AXON_H__ */ Index: linux-2.6.23.1/drivers/misc/triblade/axon_hw.h =================================================================== --- /dev/null +++ linux-2.6.23.1/drivers/misc/triblade/axon_hw.h @@ -0,0 +1,262 @@ +/** + * axon_hw - axon chip defines. + * + * 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright (C) IBM Corporation, 2008 + * + * Authors: H Brett Bolen , + * Tim Schimke , + * Jesse Arroyo , + * Murali N Iyer + * + */ + + +#ifndef __LINUX_AXON_HW_H__ +#define __LINUX_AXON_HW_H__ + +/* + * Note that many of these defines are in place for cards + * that do not have the sufficient device tree entries. + * Once all of the hardware has the correct entries + * this shall be cleaned up + * + */ + +/* XDR base on Axon PLB5 */ +#define PLB5_XDR_BASE_HI 0x60000060u +#define PLB5_XDR_BASE_LO 0x00000000u +#define PLB5_XDR_BASE 0x6000006000000000ul + +/* Device base on Axon PLB5 */ +#define PLB5_DEV_BASE_HI 0x40000044u +#define PLB5_DEV_BASE_LO 0x00000000u +#define BE_DEV_BASE_OFFSET 0x0000004400000000ul + +#define PLB5_EBC_BASE_HI 0x40000045u /* points to SPI */ +#define PLB5_EBC_BASE_LO 0x40000000u /* interface */ +#define GPIO_OFFSET 0x700u /* to GPIO */ +#define GPIO_SOFT_RESET 0x01000000u /* Bit 7 in big endian */ + +/* PCIe base on Axon PLB5 */ +#define PLB5_PCIE0_BASE_HI 0xA00000A0u +#define PLB5_PCIE0_BASE_LO 0x00000000u +#define PLB5_PCIE0_BASE 0xA00000A000000000ul +#define BE_PCIE0_BASE_OFFSET 0x000000A000000000ul + +/* PCIe memory base on Axon PLB5 */ +#define PLB5_PCIE0_MEM0BASE_HI 0xC00000C0u /* port0 */ +#define PLB5_PCIE0_MEM0BASE_LO 0x00000000u +#define PLB5_PCIE0_MEM0BASE 0xC00000C000000000ul +#define BE_PCIE0_MEM0BASE_OFFSET 0x000000C000000000ul + +#define PLB5_PCIE0_MEM1BASE_HI 0xE0000000u +#define PLB5_PCIE0_MEM1BASE_LO 0x00000000u +#define PLB5_PCIE0_MEM1BASE 0xE000000000000000ul + +#define PLB5_PCIE0_MEM2BASE_HI 0xA00000A4u /* end point node only */ +#define PLB5_PCIE0_MEM2BASE_LO 0x00000000u +#define PLB5_PCIE0_MEM2BASE 0xA00000A400000000ul +#define BE_PCIE0_MEM2BASE_OFFSET 0x000000A400000000ul + +#define PLB5_PCIE1_MEM0BASE_HI 0xD00000D0u /* port1 */ +#define PLB5_PCIE1_MEM0BASE_LO 0x00000000u +#define PLB5_PCIE1_MEM1BASE_HI 0xF0000000u +#define PLB5_PCIE1_MEM1BASE_LO 0x00000000u + + +#define PLB5_PCIE1_BASE_HI 0xA00000A2u +#define PLB5_PCIE1_BASE_LO 0x00000000u +#define PLB5_PCIE1_BASE 0xA00000A200000000ul +#define BE_PCIE1_BASE_OFFSET 0x000000A200000000ul + +#define PLB5_PCIE1_MEM0BASE_HI 0xD00000D0u +#define PLB5_PCIE1_MEM0BASE_LO 0x00000000u +#define PLB5_PCIE1_MEM0BASE 0xD00000D000000000ul +#define BE_PCIE1_MEM0BASE_OFFSET 0x000000D000000000ul + +#define PLB5_PCIE1_MEM1BASE_HI 0xF0000000u +#define PLB5_PCIE1_MEM1BASE_LO 0x00000000u +#define PLB5_PCIE1_MEM1BASE 0xF000000000000000ul + +#define PLB5_PCIE1_MEM2BASE_HI 0xA00000A5u /* end point node only */ +#define PLB5_PCIE1_MEM2BASE_LO 0x00000000u +#define PLB5_PCIE1_MEM2BASE 0xA00000A500000000ul +#define BE_PCIE1_MEM2BASE_OFFSET 0x000000A500000000ul + + +/***************************************/ +/* BAR26 -- MMIO device (non-prefetch) */ +/***************************************/ +#define BAR26_PCIE_OFFSET 0x0000000000004000ul +#define BAR26_PCIE_SIZE 0x0000000000004000ul + +/* PCIe0 UTL+CFG */ +#define PCIE_CFG_OFFSET 0x0000000000005000ul +#define PCIE_CFG_SIZE 0x0000000000001000ul + +#define PCIE_CFG_BAR0 0x0010u +#define PCIE_CFG_BAR1 0x0014u +#define PCIE_CFG_BAR2 0x0018u +#define PCIE_CFG_BAR3 0x001Cu +#define PCIE_CFG_BAR4 0x0020u +#define PCIE_CFG_BAR5 0x0024u +#define PCIE_CFG_BAR6 0x0028u + +#define PCIE_CFG_MSICAP 0x0048u +#define PCIE_CFG_MSIADDRL 0x004Cu +#define PCIE_CFG_MSIADDRH 0x0050u +#define PCIE_CFG_MSIDATA 0x0054u + +#define PCIE_CFG_LINK_CAP_PORT 0x0067u +/* Firmware updates this register to indicate ready / boot complete */ + #define PCIE_CFG_LINK_CAP_PORT_OK 0x6Du + +#define PCIE_CFG_SP0 0x00F0u +#define PCIE_CFG_SP1 0x00F4u +#define PCIE_CFG_SP2 0x00F8u +#define PCIE_CFG_SP3 0x00FCu + +#define PCIE_CFG_PIM0L 0x0340u +#define PCIE_CFG_PIM0H 0x0344u +#define PCIE_CFG_PIM1L 0x0348u +#define PCIE_CFG_PIM1H 0x034Cu +#define PCIE_CFG_PIM2L 0x0358u +#define PCIE_CFG_PIM2H 0x035Cu +#define PCIE_CFG_PIM3L 0x0360u +#define PCIE_CFG_PIM3H 0x0364u +#define PCIE_CFG_PIM4L 0x0368u +#define PCIE_CFG_PIM4H 0x036Cu +#define PCIE_CFG_PIM6L 0x03C0u +#define PCIE_CFG_PIM6H 0x03C4u + +#define PCIE_CFG_PIM01SL 0x0350u +#define PCIE_CFG_PIM01SH 0x0354u +#define PCIE_CFG_PIM34SL 0x0370u +#define PCIE_CFG_PIM34SH 0x0374u +#define PCIE_CFG_PIM26S 0x03C8u + +#define PCIE_CFG_POM0L 0x0380u +#define PCIE_CFG_POM0H 0x0384u +#define PCIE_CFG_POM1L 0x0388u +#define PCIE_CFG_POM1H 0x038Cu +#define PCIE_CFG_POM2L 0x0390u +#define PCIE_CFG_POM2H 0x0394u + +#define PCIE_CFG_POM0L_MASK 0xf8000000u + +#define PCIE_CFG_REG(b, r) (((void *)(b))+PCIE_CFG_OFFSET+(r)) + +/* PCIe1 UTL+CFG */ +#define PCIE_UTL_OFFSET 0x0000000000004000ul +#define PCIE_UTL_SIZE 0x0000000000001000ul + +#define PCIE_UTL_ISR 0x14u +#define PCIE_UTL_ISR_CLR 0x18u +#define PCIE_UTL_ISR_MSK 0x1Cu + +#define PCIE_UTL_ISR_APP0 0x80000000u +#define PCIE_UTL_ISR_APP1 0x40000000u +#define PCIE_UTL_ISR_APP2 0x20000000u +#define PCIE_UTL_ISR_APP3 0x10000000u +#define PCIE_UTL_ISR_APP4 0x08000000u +#define PCIE_UTL_ISR_APP5 0x04000000u +#define PCIE_UTL_ISR_APP6 0x02000000u +#define PCIE_UTL_ISR_APP7 0x01000000u + +#define PCIE_UTL_ISR_CLEARALL 0xFF000000u +#define PCIE_UTL_ISR_ENABLEALL 0xFF000000u + +#define PCIE_UTL_REG(b, r) (((void *)(b))+PCIE_UTL_OFFSET+(r)) + +#define BAR26_DEV_OFFSET 0x0000000000000000ul +#define BAR26_DEV_SIZE 0x0000000000004000ul + +#define DEV_C3PO_OFFSET 0x0000000000000000ul +#define DEV_C3PO_SIZE 0x0000000000001000ul + +#define C3PO_SP_OFFSET 0xC00u +#define C3PO_SP_SIZE 0x80u +/* Last 4 SP regs reserved for Mailbox payload inbound to the host -- saw */ + +#define C3PO_SP00 0x00u +#define C3PO_SP01 0x04u +#define C3PO_SP02 0x08u +#define C3PO_SP03 0x0Cu +#define C3PO_SP04 0x10u +#define C3PO_SP05 0x14u +#define C3PO_SP06 0x18u +#define C3PO_SP07 0x1Cu +#define C3PO_SP08 0x20u +#define C3PO_SP09 0x24u +#define C3PO_SP10 0x28u +#define C3PO_SP11 0x2Cu +#define C3PO_SP12 0x30u +#define C3PO_SP13 0x34u +#define C3PO_SP14 0x38u +#define C3PO_SP15 0x3Cu +#define C3PO_SP16 0x40u +#define C3PO_SP17 0x44u +#define C3PO_SP18 0x48u +#define C3PO_SP19 0x4Cu +#define C3PO_SP20 0x50u +#define C3PO_SP21 0x54u +#define C3PO_SP22 0x58u +#define C3PO_SP23 0x5Cu +#define C3PO_SP24 0x60u +#define C3PO_SP25 0x64u +#define C3PO_SP26 0x68u +#define C3PO_SP27 0x6Cu +#define C3PO_SP28 0x70u +#define C3PO_SP29 0x74u +#define C3PO_SP30 0x78u +#define C3PO_SP31 0x7Cu + +#define DEV_MBX_MSG0_OFFSET 0x0000000000002000ul +#define DEV_MBX_MSG0_SIZE 0x0000000000000010ul + +#define DEV_MSI0_OFFSET 0x0000000000003000ul +#define DEV_MSI0_SIZE 0x0000000000001000ul + +/* PCIE regs accessable via DCR */ +#define DCR_PCIE0_GPL5_OFFSET 0x0000000000002400ul +#define DCR_PCIE0_GPL5_SIZE 0x0000000000000400ul + +#define DCR_MEM0_BASE_UPPER 0x06 +#define DCR_MEM0_BASE_LOWER 0x07 +#define DCR_MEM0_MASK_UPPER 0x08 +#define DCR_MEM0_MASK_LOWER 0x09 +#define DCR_MEM1_BASE_UPPER 0x0A +#define DCR_MEM1_BASE_LOWER 0x0B +#define DCR_MEM1_MASK_UPPER 0x0C +#define DCR_MEM1_MASK_LOWER 0x0D +#define DCR_MEM2_BASE_UPPER 0x0E +#define DCR_MEM2_BASE_LOWER 0x0F +#define DCR_MEM2_MASK_UPPER 0x10 +#define DCR_MEM2_MASK_LOWER 0x11 + +#define DCR_MEM0_MASK_RSTFLGS 0xF8000000u +#define DCR_MEM0_MASK_1CHNL 0x0002 +#define DCR_MEM0_MASK_2CHNL 0x0001 +#define DCR_MEM0_MASK_VALID 0x0400 + +#define DCR_MEM2_MASK_RSTFLGS 0xFFFFFF80u +#define DCR_MEM2_MASK_1CHNL 0x0008 +#define DCR_MEM2_MASK_2CHNL 0x0004 +#define DCR_MEM2_MASK_VALID 0x0001 + +#endif /* __LINUX_AXON_HW_H__ */ + Index: linux-2.6.23.1/drivers/misc/triblade/scratchpad.h =================================================================== --- /dev/null +++ linux-2.6.23.1/drivers/misc/triblade/scratchpad.h @@ -0,0 +1,69 @@ +/** + * axon_scratchpad - provides access to a small memory space on the axon + * chip that is used for communication between the + * host and the cell systems. + * + * 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright (C) IBM Corporation, 2008 + * + * Authors: H Brett Bolen , + * Tim Schimke , + * Jesse Arroyo , + * Murali N Iyer + * + */ + +#ifndef __SCRATCHPAD_H__ +#define __SCRATCHPAD_H__ + +/** + * scratchpad_comm + * general purpose memory area accessable from both the host + * and the cell processors. + * 32 word split between two sides + * + * Each side writes to local_sp registers + * and reads from remote_sp registers + * + * todo: lowercase members ( these were defines ). + * + */ + +union scratchpad_comm { + struct{ + u32 cmd_seqno; + u32 cmd_ack; + u32 cmd; + u32 subcmd; + u32 params[4]; + u32 rfu0; + u32 int_disable; + u32 d2d_size; + u32 d2d_phys_hi; + u32 d2d_phys_lo; + u32 sma_size; + u32 sma_phys_hi; + u32 sma_phys_lo; +/* u32 data[8]; */ + }; + u32 sp_array[16]; +}; + + +#define SP_NULL_VAL 0xA55AA55A +#define SP_SMA_READY 0x41584F4E /* AXON */ + +#endif /* __SCRATCHPAD_H__ */ Index: linux-2.6.23.1/drivers/misc/triblade/axon_main.c =================================================================== --- /dev/null +++ linux-2.6.23.1/drivers/misc/triblade/axon_main.c @@ -0,0 +1,1250 @@ +/** + * axon_main - linux driver entry points. setup and teardown. + * + * This Axon Device Driver uses the IBM Axon Companion Chip as an + * interface between computer systems. The chip use PCI-Express + * interface to link two computer systems, such as the IBM Hybrid + * 'Tri-Blade' which consists of a 'host processor' blade and two CBE + * blades. The host blade and the CBE systems communicate using this + * driver via the PCIe interface. The 'host' computer system sees the + * CBE blades as PCI-Express devices. This driver does NOT link the + * two cell blades. + * + * This driver provides these application services: + * 1. Shared Memory Access (SMA). A block of memory on each blade + * can be 'mapped' into both user memory spaces. + * 2. Dynamic Memory Access (DMA). Blocks of memory may be moved from + * one system to the linked system ( *host->cbe or cbe->host). + * 3. Notification. An application send a notification to the other + * system. + * + * This driver also provides a driver interface for a companion + * APNET driver to use SMA and DMA to emulate a point-to-point + * network interface. + * + * 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright (C) IBM Corporation, 2008 + * + * Authors: H Brett Bolen , + * Tim Schimke , + * Jesse Arroyo , + * Murali N Iyer + */ + +/* #define DEBUG */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "axon.h" +#include "axon_hw.h" +#include "axon_sma.h" +#include "axon_sysfs.h" +#include "axon_debugfs.h" +#include "axon_diags.h" +#include "axon_util.h" +#include "axon_mr.h" +#include "axon_task.h" + +#define UNGUARDED_LOCAL_SMA + +/* + * ID + */ +#define MODULE_NAME "axon" +#define AXON_VERSIONDC "1.0.00" /* major.minor.subminor, keep only numbers */ + +const char *axon_name = MODULE_NAME; +const char *axon_version = AXON_VERSIONDC; +const int axon_id = 0x101; +const int axon_rev = 1; + + +/* + * module parameters, + */ +int axon_major = 0; /* dynamic allocation of major number */ +int axon_local_d2d_pages = LOCAL_D2D_SIZE/PAGE_SIZE; +int axon_local_sma_pages = LOCAL_SMA_SIZE_DEFAULT/PAGE_SIZE; + +MODULE_AUTHOR("H Brett Bolen "); +MODULE_AUTHOR("Murali Iyer "); +MODULE_AUTHOR("Tim Schimke "); +MODULE_AUTHOR("Jesse Arroyo "); + +MODULE_VERSION(AXON_VERSIONDC); +MODULE_DESCRIPTION("Provides PCIe interconnect between Host and " \ + "CBE/Axon based systems."); +MODULE_LICENSE("GPL"); + + +/** + * Data + */ + +/* axon task queue */ +static struct AXON_TASKQUEUE_TYPE axon_request_tq = {0}; +static void axon_process_workqueue( struct work_struct *ignored); +static DECLARE_WORK( axon_workqueue, axon_process_workqueue); + +/* wait queue for notifications */ +static DECLARE_WAIT_QUEUE_HEAD(axon_notification_tqh); + +/* spinlock */ +DEFINE_SPINLOCK( axon_taskqueue_spinlock); + +/* axon instance table */ +int num_axons_found = 0; +int axon_max_devices = MAX_NUM_AXONS; +struct axon *axon_device_table; + +/** + * axon_get_tq - read item from work queue + * @item: work queue item + */ +static int axon_get_tq( struct AXON_TQ_TASK *item) +{ + int ri = axon_request_tq.read; + int wi = axon_request_tq.write; + int sz = axon_request_tq.size; + if ( ri != wi) { + *item = axon_request_tq.items[ri]; + axon_request_tq.read = (ri+1) % sz; + return 1; + } + + return 0; +} + +/** + * axon_put_tq - add a work queue task + * @item - work item + */ +static void axon_put_tq( struct AXON_TQ_TASK item) +{ + int ri, wi, sz; + + ri = axon_request_tq.read; + wi = axon_request_tq.write; + sz = axon_request_tq.size; + if ((wi+1)%sz != ri) { + axon_request_tq.items[wi] = item; + axon_request_tq.write = (wi+1) %sz; + } else { + axon_request_tq.num_errors++; + } + axon_request_tq.num_requests++; + +} + +/** + * add to read queue + * @axon_priv: device instance + * @type: string to add to read queue + * @data: aux data + */ +void add_to_readqueue( struct axon *axon_priv, char *type, int data) +{ + struct axon_dataq *rq = &(axon_priv->readq); + int ri = rq->read_index; + int wi = rq->write_index; + + if ( rq->buf_sz != AXON_DATAQ_BUFSZ) + return; + + spin_lock_irq( &rq->irq_lock); + + /* if full, make room by removing oldest entry */ + if ((wi != ri) && + (((wi + 16 ) % rq->buf_sz) == ri)) { + rq->read_index = ri = (ri+16) % rq->buf_sz; + } + + /* add data */ + sprintf(&rq->buf[ wi], "%-4s %d\n", type, data); + + /* update queue pointer */ + rq->write_index += 16; + rq->write_index %= rq->buf_sz; + + spin_unlock_irq( &rq->irq_lock); + + wake_up( &rq->irq_queue); +} + +/** + * axon_add_task - add item to work queue and userlib read queue, + * and schedule it + */ +void axon_add_task( int cmd, + void *priv, + int data0, + int data1, + char *aux) +{ + long flag; + struct AXON_TQ_TASK task; + + pr_debug("put_tq: cmd=%d, data=0x%x, 0x%x, aux='%s'\n", + cmd, data0, data1, aux); + + /* apnet short circuit callback -- call in interrupt context */ + if ( data1 == APNET_MSG_TYPE ) { + axon_do_callback(cmd, priv, data0, data1, aux); + return; + } + + /* apnet short circuit callback -- call in interrupt context */ + if ( data1 == DACS_MSG_TYPE) { + axon_do_callback(cmd, priv, data0, data1, aux); + add_to_readqueue( (struct axon *) priv, aux, data1); + return; + } + + /* add to task queue */ + task.cmd = cmd; + task.axon_priv = priv; + task.data[0] = data0; + task.data[1] = data1; + task.aux[15] = 0; + strncpy(task.aux, aux, 15); + spin_lock_irqsave( &axon_taskqueue_spinlock, flag); + axon_put_tq( task); + spin_unlock_irqrestore( &axon_taskqueue_spinlock, flag); + + /* schedule task queue */ + schedule_work( &axon_workqueue); +} + +/* + * axon_process_workqueue + */ +static void axon_process_workqueue( struct work_struct *ignored) +{ + int count; + + /* read up to 16 items from queue */ + count = 16; + while (count-- > 0) { + /* get an item, and process it */ + struct AXON_TQ_TASK task = { 0 }; + if (axon_get_tq( &task)) { + switch ( task.cmd) { + case TQ_INIT: + case TQ_DIAGS: + break; + + case TQ_IRQ: + if ( task.axon_priv) { + axon_do_callback( task.cmd, + task.axon_priv, + task.data[0], + task.data[1], + task.aux); + } + break; + default: + pr_debug("TQ.%p: BAD cmd=%d, data=0x%xs '%s'\n", + task.axon_priv, + task.cmd, + task.data[0], + task.aux); + break; + } + } + } +} + +/** + * axon_open - open the axon device instance + * @inode: inode pointer + * @filp: file pointer + */ +static int axon_open( struct inode *inode, struct file *filp) +{ + struct axon *axon_priv; + int rc = 0; + + if ( !inode ) { + pr_debug( "ERROR -NOINODE\n"); + pr_debug( "ERROR -NOINODE\n"); + return -EAGAIN; + } + + axon_priv = container_of( inode->i_cdev, struct axon, cdev); + filp->private_data = axon_priv; + + if(mutex_lock_interruptible(&axon_priv->open_lock)) { + return -ERESTARTSYS; + } + + /* enforce exclusive access */ + if(atomic_read(&axon_priv->open_count)){ + rc = -EBUSY; + goto out; + } + atomic_inc(&axon_priv->open_count); + + /* map remote buffers if needed */ + if (!axon_priv->remote_sma_address || + !axon_priv->remote_d2d) { + axon_arch_map_remote_mem( axon_priv); + } + + /* if any shared area is not available, then the open must fail */ + if (!axon_priv->local_sma_address || + !axon_priv->local_d2d ) { + pr_info("open failed: " + "local shared memory not allocated " + "sma=%p d2d=%p]\n", + axon_priv->local_sma_address, + axon_priv->local_d2d); + pr_info("open failed: " + "local shared memory not allocated sma=%p d2d=%p]\n", + axon_priv->local_sma_address, + axon_priv->local_d2d); + pr_info( "ERROR -ENOMEM\n"); + rc = -ENOMEM; + goto out; + } + + if (!axon_priv->remote_sma_address || + !axon_priv->remote_d2d ) { + pr_info("open failed: remote shared memory not allocated ]\n"); + /* + * it is probable that the driver on the other side + * isn't initialized yet, so set rtn code = "try again later" + */ + pr_info( "ERROR -EAGAIN rsma=%p rd2d=%p\n", + axon_priv->remote_sma_address, + axon_priv->remote_d2d); + pr_info( "ERROR -EAGAIN rsma=%p rd2d=%p\n", + axon_priv->remote_sma_address, + axon_priv->remote_d2d); + + if ( 1) + axon_diags_dump_scratchpad( axon_priv, + "open fail -EAGAIN"); + rc = -EAGAIN; + goto out; + } + + if (!axon_priv->mr_control) { + axon_priv->remote_d2d_mr_address = + &axon_priv->remote_d2d->mr_buffer; + axon_priv->remote_d2d_mr_length = D2D_MR_BUF_SIZE; + axon_priv->local_d2d_mr_length = D2D_MR_BUF_SIZE; + axon_priv->local_d2d_mr_address = + &axon_priv->local_d2d->mr_buffer; + rc = axon_initialize_mr(axon_priv); + if (rc != 0) { + pr_info("%s:initialize MR failed with rc %x\n", + __FUNCTION__, rc); + rc = -ENOMEM; + goto out; + } + } else { + + rc = axon_reinitialize_mr(axon_priv); + if (rc != 0) { + pr_info("%s: reinitialize MR failed with rc %x\n", + __FUNCTION__, rc); + rc = -ENOMEM; + goto out; + } + } + + out: + mutex_unlock(&(axon_priv->open_lock)); + return rc; +} + +/** + * axon_release - close axon device + * @inode: inode pointer + * @filp: file pointer + */ +static int axon_release(struct inode *inode, struct file *filp) +{ + struct axon *axon_priv; + int rc; + + axon_priv = container_of( inode->i_cdev, struct axon, cdev); + filp->private_data = axon_priv; + + if(mutex_lock_interruptible(&axon_priv->open_lock)) { + return -ERESTARTSYS; + } + + atomic_dec(&axon_priv->open_count); + + /* cleanup memory regions */ + rc = axon_reinitialize_mr(axon_priv); + if (rc != 0) { + pr_debug(" axon_close: " + "MR cleanup failed with rc %x\n", rc); + } + + mutex_unlock(&(axon_priv->open_lock)); + return 0; +} + +/** + * axon_read - read from axon device ( status/completion information) + * @filp: file pointer + * @buf: user buffer + * @count: read request size + * @loff_t: file offset + */ +static ssize_t axon_read( struct file *filp, + char __user *buf, + size_t count, + loff_t *ppos) +{ + int retval = 0; + struct axon *axon_priv = filp->private_data; + + struct axon_dataq *rq; + + /* initial checking */ + if (!axon_priv ) { + pr_debug(": private data is uninitialied\n"); + return -EIO; + } + if ( count < 16) { + return -EINVAL; + } + + /* wait for data in read queue */ + rq = &axon_priv->readq; + wait_event_interruptible(rq->irq_queue, + (rq->read_index != rq->write_index)); + + + /* read the queue */ + if (rq->read_index != rq->write_index) { + int data_bytes = 0; + char data[16] = {0}; + int ri = 0; + int wi = 0; + + spin_lock_irq( &rq->irq_lock); + ri = rq->read_index; + wi = rq->write_index; + data_bytes = ((wi + rq->buf_sz - ri) % rq->buf_sz); + if ( data_bytes > 0) { + memcpy(data, &rq->buf[ rq->read_index], 16); + rq->read_index += 16; + rq->read_index %= rq->buf_sz; + } + spin_unlock_irq( &rq->irq_lock); + + data[14] = '\n'; + data[15] = 0; + + /* got data? */ + if ( data_bytes != 0) { + int err = 0; + pr_debug("got data\n"); + retval = 16; + err = copy_to_user(buf, data, retval); + if ( err < 0) + retval = err; + } else { + pr_debug("no data\n"); + } + } + + + return retval; +} + +/** + * axon_write - write to axon device + * @filp: file pointer + * @buf: user buffer + * @count: read request size + * @loff_t: file offset + */ +static ssize_t axon_write(struct file *filp, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct axon *axon_priv = filp->private_data; + struct d2d_message msg; + static int msg_count = 0; + msg.cmd = D2D_MSG_DIAGS; + msg.data[0] = msg_count++; + msg.data[1] = 0x1234; + msg.data[2] = 0x5678; + + /* diags */ + axon_add_task( TQ_DIAGS, axon_priv, 0x10, 0x11, + "axon_write"); + axon_send_d2d_msg(axon_priv, &msg); + + + return count; +} + +/** + * register_mr - register a memory region + * @axon_priv: device instance + * @uaddr: user memory address + */ +int register_mr( struct axon *axon_priv, void __user *uaddr) +{ + int ret; + struct AXON_MR_registration memreg_registration; + + ret = copy_from_user(&memreg_registration, + uaddr, + sizeof(memreg_registration)); + if ( ret != 0) + return -EFAULT; + + ret = axon_register_mr(axon_priv, + (void __user *) + memreg_registration.local_dma_memory, + memreg_registration.local_dma_memory_size, + &memreg_registration.memory_region_handle, + memreg_registration.permissions); + if (ret != 0) + return ret; + + ret = copy_to_user(uaddr, + &memreg_registration, + sizeof(memreg_registration)); + + return ret; +} + + +/** + * register_mr_32 - register a memory region + * @axon_priv: device instance + * @uaddr: user memory address + */ +int register_mr_32( struct axon *axon_priv, void __user *uaddr) +{ + int ret; + struct AXON_MR_registration memreg_registration; + + + ret = copy_from_user(&memreg_registration, + uaddr, + sizeof(memreg_registration)); + if ( ret != 0) + return -EFAULT; + + ret = axon_register_mr(axon_priv, + compat_ptr((unsigned long) + memreg_registration.local_dma_memory), + memreg_registration.local_dma_memory_size, + &memreg_registration.memory_region_handle, + memreg_registration.permissions); + if (ret != 0) + return ret; + + ret = copy_to_user(uaddr, + &memreg_registration, + sizeof(memreg_registration)); + + return ret; +} + + +/** + * deregister_mr - register a memory region + * @axon_priv: device instance + * @uaddr: user memory address + */ +int deregister_mr( struct axon *axon_priv, void __user *uaddr) +{ + int ret; + struct AXON_MR_deregistration mr_dereg; + + ret = copy_from_user(&mr_dereg, + uaddr, + sizeof(mr_dereg)); + if (ret != 0) + return -EFAULT; + + ret = axon_deregister_mr(axon_priv, + mr_dereg.memory_region_handle); + if (ret != 0) + return ret; + + ret = copy_to_user(uaddr, + &mr_dereg, + sizeof(mr_dereg)); + if ( ret != 0) + return -EFAULT; + + return ret; +} + +/** + * ext_register_mr - extend a memory region registration + * @axon_priv: device instance + * @uaddr: user memory address + */ +int extend_register_mr( struct axon *axon_priv, void __user *uaddr) +{ + int ret; + struct AXON_MR_ext_registration mr_ext_reg; + + ret = copy_from_user(&mr_ext_reg, + uaddr, + sizeof(mr_ext_reg)); + if (ret != 0) + return -EFAULT; + + ret = axon_extend_register_mr(axon_priv, + mr_ext_reg.memory_region_handle, + mr_ext_reg.permissions); + if (ret != 0) + return ret; + + ret = copy_to_user(uaddr, + &mr_ext_reg, + sizeof(mr_ext_reg)); + if ( ret != 0) + return -EFAULT; + + return ret; +} + +/** + * issue_dma_fast - issue a dma using the mmapped command area + * @axon_priv: device instance + * @offset: offset into user memory area + */ +static long issue_dma_fast(struct axon *axon_priv, __u32 offset) +{ + struct AXON_dma_command_list_fast *dma_cmd_list; + struct AXON_dma_request *dma_request; + void *cmd_buffer_end; + union tbtime_t start_time; + union tbtime_t end_time; + int ret; + + tbtime_get(&start_time); + + dma_cmd_list = axon_priv->dma_cmd_buf + offset; + cmd_buffer_end = axon_priv->dma_cmd_buf + axon_priv->dma_cmd_buf_length; + + if (unlikely( + ((void *)dma_cmd_list + + sizeof(struct AXON_dma_command_list_fast)) > + cmd_buffer_end)) { + pr_debug("Command list is not within buffer\n"); + ret = -EINVAL; + goto out; + } + + dma_request = axon_priv->dma_cmd_buf + dma_cmd_list->dma_req_offset; + if (unlikely( + ((void *)dma_request + + (dma_cmd_list->dma_requests_available * + sizeof(struct AXON_dma_request))) > + cmd_buffer_end)) { + pr_debug("DMA Request is not within buffer\n"); + ret = -EINVAL; + goto out; + } + ret = axon_perform_dma(axon_priv, + dma_cmd_list->dma_requests_available, + &dma_cmd_list->dma_requests_started, + dma_request); + if ( unlikely(ret < 0)) + return ret; + + tbtime_get(&end_time); + axon_priv->dma_ioctl_requests += 1; + axon_priv->total_dma_setup_time += end_time.tb - start_time.tb; + +out: + return ret; +} + +/** + * send_notify - send a notification + * @axon_priv: device instance + * @uaddr: user memory address + */ +int send_notify( struct axon *axon_priv, void __user *uaddr) +{ + int ret; + struct AXON_WAKEUP wakeup; + + ret = copy_from_user(&wakeup, uaddr, sizeof(wakeup)); + if ( ret != 0) { + return -EFAULT; + } + + /* send notification */ + axon_arch_notify(axon_priv, wakeup.type); + + return 0; +} + + + +/** + * axon_ioctl - I/O control for axon device + * @filp: file pointer + * @cmd: command type + * @arg: command argument + */ +static long axon_ioctl(struct file *filp, + unsigned int cmd, + unsigned long arg) +{ + int ret; + struct axon *axon_priv; + + ret = -EFAULT; /* general failure */ + + /* retrieve private struct */ + axon_priv = (struct axon *) filp->private_data; + if (axon_priv == 0) { + pr_debug( "axon_ioctl: bad private struct = %p\n", axon_priv); + return -EFAULT; + } + + /* decode ioctl command */ + switch (cmd) { + case AXONIO_ISSUE_DMA_FAST: + ret = issue_dma_fast( axon_priv, (__u32)arg); + break; + case AXONIO_DMA_REGISTER: + ret = register_mr( axon_priv, (void __user *)arg); + break; + case AXONIO_DMA_DEREGISTER: + ret = deregister_mr( axon_priv, (void __user *)arg); + break; + case AXONIO_DMA_EXTREGISTER: + ret = extend_register_mr( axon_priv, (void __user *)arg); + break; + case AXONIO_NOTIFY: + ret = send_notify( axon_priv, (void __user *)arg); + break; + + } + + return ret; +} + + +static long axon_compat_ioctl(struct file *filp, + unsigned int cmd, + unsigned long arg) +{ + int ret; + struct axon *axon_priv; + + ret = -ENOIOCTLCMD; + + /* retrieve private struct */ + axon_priv = (struct axon *) filp->private_data; + if (axon_priv == 0) { + pr_debug( "axon_ioctl: bad private struct = %p\n", axon_priv); + return -EFAULT; + } + + /* decode ioctl command */ + switch (cmd) { + + case AXONIO_DMA_REGISTER: + ret = register_mr_32( axon_priv, compat_ptr(arg)); + break; + + case AXONIO_DMA_DEREGISTER: + ret = deregister_mr( axon_priv, compat_ptr(arg)); + break; + + case AXONIO_DMA_EXTREGISTER: + ret = extend_register_mr( axon_priv, compat_ptr(arg)); + break; + case AXONIO_NOTIFY: + ret = send_notify( axon_priv, compat_ptr(arg)); + break; + + default: + pr_debug("No matching IOCTL %08x\n", cmd); + } + return ret; +} + +/** + * axon_mmap -- map axon Shared Memory Area + * @filp: file pointer + * @vma: virtual memory descriptor + * + */ +int axon_mmap( struct file *filp, struct vm_area_struct *vma) +{ + long length = vma->vm_end - vma->vm_start; + int ret = -EIO; + struct axon *axon_priv; + unsigned int offset; + + /* retrieve private struct */ + axon_priv = (struct axon *) filp->private_data; + if (axon_priv == 0) { + return -EIO; + } + + /* map which section */ + offset = (vma->vm_pgoff << PAGE_SHIFT); + + + if ( offset == LOCAL_SMA_OFFSET) { + if (axon_priv->local_sma_address == 0) { + printk(KERN_ERR "ERROR -- no local sma\n"); + return -EIO; + } + + /* check length - do not allow larger mappings than the + number of pages allocated */ + if (length > axon_priv->local_sma_size) { + printk(KERN_ERR "ERROR -- bad length\n"); + return -EIO; + } + + /* map the whole physically contiguous area in one piece */ + ret = remap_pfn_range( + vma, + vma->vm_start, + (virt_to_phys((void *)axon_priv->local_sma_address) + >> PAGE_SHIFT), + length, + vma->vm_page_prot); + + + if ( ret < 0) { + printk(KERN_ERR "ERROR -- invalid remap\n"); + return ret; + } + + + + } + + else if (offset == REMOTE_SMA_OFFSET) { + + unsigned long int rsma_pg_no; + + if (axon_priv->remote_sma_address == 0) { + printk(KERN_ERR "ERROR -- no remote sma\n"); + return -EIO; + } + + /* + * check length - do not allow larger mappings than the + * number of pages allocated + */ + if (length > axon_priv->remote_sma_size) { + printk(KERN_ERR "ERROR -- bad length\n"); + return -EIO; + } + + rsma_pg_no = axon_arch_get_rsma_page(axon_priv); + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + +#ifdef CONFIG_TB_AXON_PCI +#ifdef CONFIG_TB_AXON_MTRR_SMA + vma->vm_page_prot = + __pgprot(pgprot_val(vma->vm_page_prot) & ~(_PAGE_PWT)); +#endif +#endif + +#ifdef UNGUARDED_LOCAL_SMA +#ifdef CONFIG_PPC64 + vma->vm_page_prot &= ~(_PAGE_GUARDED); +#endif +#endif + + + /* map the whole physically contiguous area in one piece */ + ret = remap_pfn_range( + vma, + vma->vm_start, + rsma_pg_no, + length, + vma->vm_page_prot); + + pr_debug("mmap remote sma: rsma_pg_no= 0x%lx, rc= %i\n", + rsma_pg_no, ret); + + + if ( ret < 0) { + printk(KERN_ERR "ERROR -- invalid remap\n"); + return -EIO; + } + + } else if (offset == DMA_COMMAND_BUFFER_OFFSET) { + if (!axon_priv->dma_cmd_buf) { + printk(KERN_ERR "ERROR - - no DMA Command Buffer\n"); + return -EIO; + } + + if (length > axon_priv->dma_cmd_buf_length) { + printk(KERN_ERR "ERROR -- bad length\n"); + return -EIO; + } + + ret = remap_pfn_range( + vma, + vma->vm_start, + (virt_to_phys((void *)axon_priv->dma_cmd_buf) + >> PAGE_SHIFT), + axon_priv->dma_cmd_buf_length, + PAGE_SHARED); + + + if ( ret < 0) { + printk(KERN_ERR "ERROR -- invalid remap\n"); + return ret; + } + + + } else { + printk(KERN_ERR "ERROR -- invalid offset\n"); + return -EIO; + } + + + return ret; +} + +/** + * axon_poll - support select/poll method to check for data + * @filp: file pointer + * @wait: poll_table ( select/pool) + */ +unsigned int axon_poll( struct file *filp, poll_table *wait) +{ + unsigned mask = 0; + struct axon *axon_priv = filp->private_data; + + if ( axon_priv != NULL) { + struct axon_dataq *rq; + + /* call poll */ + rq = &axon_priv->readq; + poll_wait( filp, &rq->irq_queue, wait); + + /* check for data in queue */ + if (rq->read_index != rq->write_index) { + /* pr_debug("axon.%d: got data!\n", + axon_priv->instance); */ + mask = POLLIN | POLLRDNORM; + + } + } + + return mask; +} + +/* + * following functions implement the interface between the base axon driver + * and the apnet virtual ethernet driver + */ + +/* + * axon_get_num_devices - get number of axon devices + * Returns number of axon devices which are active + */ +int axon_get_num_devices( void ) +{ + if (!axon_device_table) { + pr_debug("axon_device_table is NULL\n"); + return 0; + } else { + BUG_ON(num_axons_found > axon_max_devices); + return num_axons_found; + } +} +EXPORT_SYMBOL_GPL(axon_get_num_devices); + +/** + * axon_get_remote_memory - get driver-to-driver memory area + * @dev_num - devicenumber + * @returns pointer to portion of remote D2D area, which belongs to apnet + */ +void __iomem *axon_get_remote_memory(int dev_num) +{ + + if (!axon_device_table) { + pr_debug("axon_device_table is NULL\n"); + return NULL; + } + if (( dev_num >= num_axons_found) || ( dev_num < 0)) { + pr_debug("dev_num parm invalid, = %i\n", dev_num); + return NULL; + } + + if (!axon_device_table[dev_num].remote_d2d ) { + axon_arch_map_remote_mem( &axon_device_table[dev_num]); + if (!axon_device_table[dev_num].remote_d2d) { + pr_debug("remote d2d area not allocated yet\n"); + return NULL; + } + } + + return (void *)axon_device_table[dev_num].remote_d2d + + offsetof(struct axon_d2d, apnet_buffer); +} +EXPORT_SYMBOL_GPL(axon_get_remote_memory); + +/** + * axon_get_local_memory - get d2d memory area + * @dev_num - device number + * @size - size of requested memory + * @returns pointer to portion of local D2D area, which belongs to apnet + */ +void *axon_get_local_memory(int dev_num, size_t size) +{ + if (!axon_device_table ) { + pr_debug("axon_device_table is NULL\n"); + return NULL; + } + if (( dev_num >= num_axons_found) || ( dev_num < 0)) { + pr_debug("dev_num parm invalid, = %i\n", dev_num); + return NULL; + } + + if (!axon_device_table[dev_num].local_d2d) { + pr_debug("no local d2d area\n"); + return NULL; + } + + if (size > LOCAL_APNET_SIZE) { + pr_debug("requested size is too large, size= 0x%x, " + "offset= 0x%lx, d2dsize= 0x%x\n", + (int) size, + (long) axon_local_d2d_pages * PAGE_SIZE, + (int) axon_device_table[dev_num].local_d2d_size); + return NULL; + } + return &axon_device_table[dev_num].local_d2d->apnet_buffer; +} +EXPORT_SYMBOL_GPL(axon_get_local_memory); + +int axon_dma_map_single(int dev_num, void *addr, size_t size, \ + int dir, dma_addr_t *handle) +{ + if (!axon_device_table ) { + pr_debug("axon_device_table is NULL\n"); + return 0; + } + if (( dev_num >= num_axons_found) || ( dev_num < 0)) { + pr_debug("dev_num parm invalid, = %i\n", dev_num); + return 0; + } + + return axon_arch_map_single(&axon_device_table[dev_num], + addr, + size, + dir, + handle); +} +EXPORT_SYMBOL_GPL(axon_dma_map_single); + +void axon_dma_unmap_single(int dev_num, dma_addr_t handle, size_t size, int dir) +{ + if (!axon_device_table ) { + pr_debug("axon_device_table is NULL\n"); + return; + } + if (( dev_num >= num_axons_found) || ( dev_num < 0)) { + pr_debug("dev_num parm invalid, = %i\n", dev_num); + return; + } + axon_arch_unmap_single(&axon_device_table[dev_num], + handle, + size, + dir); +} +EXPORT_SYMBOL_GPL(axon_dma_unmap_single); + +/** + * axon char driver file operations + */ +const struct file_operations axon_fops = +{ + .owner = THIS_MODULE, + .read = axon_read, + .write = axon_write, + .poll = axon_poll, + .unlocked_ioctl = axon_ioctl, + .compat_ioctl = axon_compat_ioctl, + .open = axon_open, + .release = axon_release, + .mmap = axon_mmap, +}; + + + +/** + * axon_module_init - initialize module + */ +static int __init axon_module_init(void) +{ + int rc = 0; + + /* allocate table for device private structs */ + axon_device_table = kmalloc( axon_max_devices * sizeof(struct axon), + GFP_KERNEL); + if ( !axon_device_table) { + printk(KERN_ERR"Axon Driver could not allocate " + "driver table for %d drivers \n", + axon_max_devices); + rc = -ENOMEM; + goto fail_malloc; + } + memset( axon_device_table, 0, axon_max_devices*sizeof(struct axon)); + + + /* initialize work queue */ + axon_request_tq.read = 0; + axon_request_tq.write = 0; + axon_request_tq.size = TQ_TASK_QUEUE_SZ; + axon_request_tq.status = 0; + axon_request_tq.num_requests = 0; + axon_request_tq.num_responses = 0; + axon_request_tq.num_errors = 0; + spin_lock_init( &axon_taskqueue_spinlock); + + + /* + * register the device major number + */ + if ( axon_major ) { + dev_t dev_id; + + dev_id = MKDEV( axon_major, 0); + rc = register_chrdev_region( dev_id, axon_max_devices, "axon"); + if (rc) { + printk(KERN_ERR"axon: register_chrdev_region() failed " + "errno = %d\n", rc); + return -1; + } + + + } else { + dev_t dev_id; + rc = alloc_chrdev_region( &dev_id, + 0, axon_max_devices, "axon"); + if (rc) { + printk(KERN_ERR"axon: alloc_chrdev_region() failed " + "errno = %d\n", rc); + return -1; + } + axon_major = MAJOR(dev_id); + pr_debug("major number assigned = 0x%x\n", axon_major); + } + + rc = axon_arch_mbx_init(); /* init mailbox driver if cell */ + if (rc) { + printk(KERN_ERR "unable to initialize mailbox driver " + "rc[%d]\n", rc); + goto error; + } + + rc = axon_arch_dmax_init(); + + + if (rc) { + printk(KERN_ERR "unable to initialize dmax driver " + "rc[%d]\n", rc); + goto error; + } + + /* sysfs entries */ + axon_sysfs_init(); + axon_debugfs_init(); + + rc = axon_arch_register_driver(); + if (rc) { + printk(KERN_ERR "unable to register PCI driver rc[%d]\n", rc); + goto error; + } + + /* syslog banner */ + pr_info( "%s driver Version %s (%d,%d) loaded.\n", + axon_name, axon_version, axon_id, axon_rev); + + +fail_malloc: +error: + return rc; +} +module_init(axon_module_init) + + +/** + * axon_module_exit + */ +static void __exit axon_module_exit(void) +{ + dev_t dev_id = MKDEV( axon_major, 0); + + /* banner */ + pr_debug("axon module unloading\n"); + + /* /sys entries */ + axon_sysfs_exit(); + axon_debugfs_exit(); + + /* tell dmax driver to exit */ + axon_arch_dmax_exit(); + + /* tell mailbox driver to exit */ + axon_arch_mbx_exit(); + + /* unregister device major/minor numbers */ + unregister_chrdev_region( dev_id, axon_max_devices); + + /* unregister pci device */ + axon_arch_unregister_driver(); + + /* free device table */ + if (axon_device_table) + kfree(axon_device_table); + + return; +} +module_exit(axon_module_exit) + --