aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorThomas Bogendoerfer <tsbogend@alpha.franken.de>2007-08-25 05:01:50 -0400
committerRalf Baechle <ralf@linux-mips.org>2007-10-11 18:46:00 -0400
commitea202c632a52c4a83f1bd82d8d06bc8e04f2689a (patch)
tree1815b69d45b99d8f20d0fb33ba7b34b8319f2507 /arch
parent1f21d2bde0046e959b53756f74d96dfd040a803b (diff)
[MIPS] JAZZ fixes
- restructured irq handling - switched vdma to use memory allocated via get_free_pages - setup platform devices for serial, jazz_esp and jazzsonic - fixed cmos rtc access Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de> Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/mips/Kconfig1
-rw-r--r--arch/mips/jazz/Makefile2
-rw-r--r--arch/mips/jazz/irq.c94
-rw-r--r--arch/mips/jazz/jazz-platform.c60
-rw-r--r--arch/mips/jazz/jazzdma.c47
-rw-r--r--arch/mips/jazz/setup.c115
6 files changed, 161 insertions, 158 deletions
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index bb3fb4018602..a8bdc084e10c 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -93,6 +93,7 @@ config MACH_JAZZ
93 select ARC32 93 select ARC32
94 select ARCH_MAY_HAVE_PC_FDC 94 select ARCH_MAY_HAVE_PC_FDC
95 select GENERIC_ISA_DMA 95 select GENERIC_ISA_DMA
96 select IRQ_CPU
96 select I8259 97 select I8259
97 select ISA 98 select ISA
98 select PCSPEAKER 99 select PCSPEAKER
diff --git a/arch/mips/jazz/Makefile b/arch/mips/jazz/Makefile
index 575a9442bc82..5aee0c266d18 100644
--- a/arch/mips/jazz/Makefile
+++ b/arch/mips/jazz/Makefile
@@ -2,6 +2,6 @@
2# Makefile for the Jazz family specific parts of the kernel 2# Makefile for the Jazz family specific parts of the kernel
3# 3#
4 4
5obj-y := irq.o jazzdma.o jazz-platform.o reset.o setup.o 5obj-y := irq.o jazzdma.o reset.o setup.o
6 6
7EXTRA_CFLAGS += -Werror 7EXTRA_CFLAGS += -Werror
diff --git a/arch/mips/jazz/irq.c b/arch/mips/jazz/irq.c
index 015cf4bb51dd..56235412cbb7 100644
--- a/arch/mips/jazz/irq.c
+++ b/arch/mips/jazz/irq.c
@@ -11,15 +11,17 @@
11#include <linux/kernel.h> 11#include <linux/kernel.h>
12#include <linux/spinlock.h> 12#include <linux/spinlock.h>
13 13
14#include <asm/irq_cpu.h>
14#include <asm/i8259.h> 15#include <asm/i8259.h>
15#include <asm/io.h> 16#include <asm/io.h>
16#include <asm/jazz.h> 17#include <asm/jazz.h>
18#include <asm/pgtable.h>
17 19
18static DEFINE_SPINLOCK(r4030_lock); 20static DEFINE_SPINLOCK(r4030_lock);
19 21
20static void enable_r4030_irq(unsigned int irq) 22static void enable_r4030_irq(unsigned int irq)
21{ 23{
22 unsigned int mask = 1 << (irq - JAZZ_PARALLEL_IRQ); 24 unsigned int mask = 1 << (irq - JAZZ_IRQ_START);
23 unsigned long flags; 25 unsigned long flags;
24 26
25 spin_lock_irqsave(&r4030_lock, flags); 27 spin_lock_irqsave(&r4030_lock, flags);
@@ -30,7 +32,7 @@ static void enable_r4030_irq(unsigned int irq)
30 32
31void disable_r4030_irq(unsigned int irq) 33void disable_r4030_irq(unsigned int irq)
32{ 34{
33 unsigned int mask = ~(1 << (irq - JAZZ_PARALLEL_IRQ)); 35 unsigned int mask = ~(1 << (irq - JAZZ_IRQ_START));
34 unsigned long flags; 36 unsigned long flags;
35 37
36 spin_lock_irqsave(&r4030_lock, flags); 38 spin_lock_irqsave(&r4030_lock, flags);
@@ -51,7 +53,7 @@ void __init init_r4030_ints(void)
51{ 53{
52 int i; 54 int i;
53 55
54 for (i = JAZZ_PARALLEL_IRQ; i <= JAZZ_TIMER_IRQ; i++) 56 for (i = JAZZ_IRQ_START; i <= JAZZ_IRQ_END; i++)
55 set_irq_chip_and_handler(i, &r4030_irq_type, handle_level_irq); 57 set_irq_chip_and_handler(i, &r4030_irq_type, handle_level_irq);
56 58
57 r4030_write_reg16(JAZZ_IO_IRQ_ENABLE, 0); 59 r4030_write_reg16(JAZZ_IO_IRQ_ENABLE, 0);
@@ -66,82 +68,40 @@ void __init init_r4030_ints(void)
66 */ 68 */
67void __init arch_init_irq(void) 69void __init arch_init_irq(void)
68{ 70{
71 /*
72 * this is a hack to get back the still needed wired mapping
73 * killed by init_mm()
74 */
75
76 /* Map 0xe0000000 -> 0x0:800005C0, 0xe0010000 -> 0x1:30000580 */
77 add_wired_entry(0x02000017, 0x03c00017, 0xe0000000, PM_64K);
78 /* Map 0xe2000000 -> 0x0:900005C0, 0xe3010000 -> 0x0:910005C0 */
79 add_wired_entry(0x02400017, 0x02440017, 0xe2000000, PM_16M);
80 /* Map 0xe4000000 -> 0x0:600005C0, 0xe4100000 -> 400005C0 */
81 add_wired_entry(0x01800017, 0x01000017, 0xe4000000, PM_4M);
82
69 init_i8259_irqs(); /* Integrated i8259 */ 83 init_i8259_irqs(); /* Integrated i8259 */
84 mips_cpu_irq_init();
70 init_r4030_ints(); 85 init_r4030_ints();
71 86
72 change_c0_status(ST0_IM, IE_IRQ4 | IE_IRQ3 | IE_IRQ2 | IE_IRQ1); 87 change_c0_status(ST0_IM, IE_IRQ2 | IE_IRQ1);
73}
74
75static void loc_call(unsigned int irq, unsigned int mask)
76{
77 r4030_write_reg16(JAZZ_IO_IRQ_ENABLE,
78 r4030_read_reg16(JAZZ_IO_IRQ_ENABLE) & mask);
79 do_IRQ(irq);
80 r4030_write_reg16(JAZZ_IO_IRQ_ENABLE,
81 r4030_read_reg16(JAZZ_IO_IRQ_ENABLE) | mask);
82}
83
84static void ll_local_dev(void)
85{
86 switch (r4030_read_reg32(JAZZ_IO_IRQ_SOURCE)) {
87 case 0:
88 panic("Unimplemented loc_no_irq handler");
89 break;
90 case 4:
91 loc_call(JAZZ_PARALLEL_IRQ, JAZZ_IE_PARALLEL);
92 break;
93 case 8:
94 loc_call(JAZZ_PARALLEL_IRQ, JAZZ_IE_FLOPPY);
95 break;
96 case 12:
97 panic("Unimplemented loc_sound handler");
98 break;
99 case 16:
100 panic("Unimplemented loc_video handler");
101 break;
102 case 20:
103 loc_call(JAZZ_ETHERNET_IRQ, JAZZ_IE_ETHERNET);
104 break;
105 case 24:
106 loc_call(JAZZ_SCSI_IRQ, JAZZ_IE_SCSI);
107 break;
108 case 28:
109 loc_call(JAZZ_KEYBOARD_IRQ, JAZZ_IE_KEYBOARD);
110 break;
111 case 32:
112 loc_call(JAZZ_MOUSE_IRQ, JAZZ_IE_MOUSE);
113 break;
114 case 36:
115 loc_call(JAZZ_SERIAL1_IRQ, JAZZ_IE_SERIAL1);
116 break;
117 case 40:
118 loc_call(JAZZ_SERIAL2_IRQ, JAZZ_IE_SERIAL2);
119 break;
120 }
121} 88}
122 89
123asmlinkage void plat_irq_dispatch(void) 90asmlinkage void plat_irq_dispatch(void)
124{ 91{
125 unsigned int pending = read_c0_cause() & read_c0_status(); 92 unsigned int pending = read_c0_cause() & read_c0_status();
93 unsigned int irq;
126 94
127 if (pending & IE_IRQ5) 95 if (pending & IE_IRQ4) {
128 write_c0_compare(0);
129 else if (pending & IE_IRQ4) {
130 r4030_read_reg32(JAZZ_TIMER_REGISTER); 96 r4030_read_reg32(JAZZ_TIMER_REGISTER);
131 do_IRQ(JAZZ_TIMER_IRQ); 97 do_IRQ(JAZZ_TIMER_IRQ);
132 } else if (pending & IE_IRQ3) 98 } else if (pending & IE_IRQ2)
133 panic("Unimplemented ISA NMI handler");
134 else if (pending & IE_IRQ2)
135 do_IRQ(r4030_read_reg32(JAZZ_EISA_IRQ_ACK)); 99 do_IRQ(r4030_read_reg32(JAZZ_EISA_IRQ_ACK));
136 else if (pending & IE_IRQ1) { 100 else if (pending & IE_IRQ1) {
137 ll_local_dev(); 101 irq = *(volatile u8 *)JAZZ_IO_IRQ_SOURCE >> 2;
138 } else if (unlikely(pending & IE_IRQ0)) 102 if (likely(irq > 0))
139 panic("Unimplemented local_dma handler"); 103 do_IRQ(irq + JAZZ_IRQ_START - 1);
140 else if (pending & IE_SW1) { 104 else
141 clear_c0_cause(IE_SW1); 105 panic("Unimplemented loc_no_irq handler");
142 panic("Unimplemented sw1 handler");
143 } else if (pending & IE_SW0) {
144 clear_c0_cause(IE_SW0);
145 panic("Unimplemented sw0 handler");
146 } 106 }
147} 107}
diff --git a/arch/mips/jazz/jazz-platform.c b/arch/mips/jazz/jazz-platform.c
deleted file mode 100644
index fd736703eef2..000000000000
--- a/arch/mips/jazz/jazz-platform.c
+++ /dev/null
@@ -1,60 +0,0 @@
1/*
2 * This file is subject to the terms and conditions of the GNU General Public
3 * License. See the file "COPYING" in the main directory of this archive
4 * for more details.
5 *
6 * Copyright (C) 2007 Ralf Baechle (ralf@linux-mips.org)
7 */
8#include <linux/init.h>
9#include <linux/module.h>
10#include <linux/serial_8250.h>
11
12#include <asm/jazz.h>
13
14/*
15 * Confusion ... It seems the original Microsoft Jazz machine used to have a
16 * 4.096MHz clock for its UART while the MIPS Magnum and Millenium systems
17 * had 8MHz. The Olivetti M700-10 and the Acer PICA have 1.8432MHz like PCs.
18 */
19#ifdef CONFIG_OLIVETTI_M700
20#define JAZZ_BASE_BAUD 1843200
21#else
22#define JAZZ_BASE_BAUD 8000000 /* 3072000 */
23#endif
24
25#define JAZZ_UART_FLAGS (UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP)
26
27#define JAZZ_PORT(base, int) \
28{ \
29 .mapbase = base, \
30 .irq = int, \
31 .uartclk = JAZZ_BASE_BAUD, \
32 .iotype = UPIO_MEM, \
33 .flags = JAZZ_UART_FLAGS, \
34 .regshift = 0, \
35}
36
37static struct plat_serial8250_port uart8250_data[] = {
38 JAZZ_PORT(JAZZ_SERIAL1_BASE, JAZZ_SERIAL1_IRQ),
39 JAZZ_PORT(JAZZ_SERIAL2_BASE, JAZZ_SERIAL2_IRQ),
40 { },
41};
42
43static struct platform_device uart8250_device = {
44 .name = "serial8250",
45 .id = PLAT8250_DEV_PLATFORM,
46 .dev = {
47 .platform_data = uart8250_data,
48 },
49};
50
51static int __init uart8250_init(void)
52{
53 return platform_device_register(&uart8250_device);
54}
55
56module_init(uart8250_init);
57
58MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>");
59MODULE_LICENSE("GPL");
60MODULE_DESCRIPTION("8250 UART probe driver for the Jazz family");
diff --git a/arch/mips/jazz/jazzdma.c b/arch/mips/jazz/jazzdma.c
index e8e0ffb9354d..c672c08d49e5 100644
--- a/arch/mips/jazz/jazzdma.c
+++ b/arch/mips/jazz/jazzdma.c
@@ -27,7 +27,7 @@
27 */ 27 */
28#define CONF_DEBUG_VDMA 0 28#define CONF_DEBUG_VDMA 0
29 29
30static unsigned long vdma_pagetable_start; 30static VDMA_PGTBL_ENTRY *pgtbl;
31 31
32static DEFINE_SPINLOCK(vdma_lock); 32static DEFINE_SPINLOCK(vdma_lock);
33 33
@@ -46,7 +46,6 @@ static int debuglvl = 3;
46 */ 46 */
47static inline void vdma_pgtbl_init(void) 47static inline void vdma_pgtbl_init(void)
48{ 48{
49 VDMA_PGTBL_ENTRY *pgtbl = (VDMA_PGTBL_ENTRY *) vdma_pagetable_start;
50 unsigned long paddr = 0; 49 unsigned long paddr = 0;
51 int i; 50 int i;
52 51
@@ -60,31 +59,31 @@ static inline void vdma_pgtbl_init(void)
60/* 59/*
61 * Initialize the Jazz R4030 dma controller 60 * Initialize the Jazz R4030 dma controller
62 */ 61 */
63void __init vdma_init(void) 62static int __init vdma_init(void)
64{ 63{
65 /* 64 /*
66 * Allocate 32k of memory for DMA page tables. This needs to be page 65 * Allocate 32k of memory for DMA page tables. This needs to be page
67 * aligned and should be uncached to avoid cache flushing after every 66 * aligned and should be uncached to avoid cache flushing after every
68 * update. 67 * update.
69 */ 68 */
70 vdma_pagetable_start = 69 pgtbl = (VDMA_PGTBL_ENTRY *)__get_free_pages(GFP_KERNEL | GFP_DMA,
71 (unsigned long) alloc_bootmem_low_pages(VDMA_PGTBL_SIZE); 70 get_order(VDMA_PGTBL_SIZE));
72 if (!vdma_pagetable_start) 71 if (!pgtbl)
73 BUG(); 72 BUG();
74 dma_cache_wback_inv(vdma_pagetable_start, VDMA_PGTBL_SIZE); 73 dma_cache_wback_inv((unsigned long)pgtbl, VDMA_PGTBL_SIZE);
75 vdma_pagetable_start = KSEG1ADDR(vdma_pagetable_start); 74 pgtbl = (VDMA_PGTBL_ENTRY *)KSEG1ADDR(pgtbl);
76 75
77 /* 76 /*
78 * Clear the R4030 translation table 77 * Clear the R4030 translation table
79 */ 78 */
80 vdma_pgtbl_init(); 79 vdma_pgtbl_init();
81 80
82 r4030_write_reg32(JAZZ_R4030_TRSTBL_BASE, 81 r4030_write_reg32(JAZZ_R4030_TRSTBL_BASE, CPHYSADDR(pgtbl));
83 CPHYSADDR(vdma_pagetable_start));
84 r4030_write_reg32(JAZZ_R4030_TRSTBL_LIM, VDMA_PGTBL_SIZE); 82 r4030_write_reg32(JAZZ_R4030_TRSTBL_LIM, VDMA_PGTBL_SIZE);
85 r4030_write_reg32(JAZZ_R4030_TRSTBL_INV, 0); 83 r4030_write_reg32(JAZZ_R4030_TRSTBL_INV, 0);
86 84
87 printk("VDMA: R4030 DMA pagetables initialized.\n"); 85 printk(KERN_INFO "VDMA: R4030 DMA pagetables initialized.\n");
86 return 0;
88} 87}
89 88
90/* 89/*
@@ -92,7 +91,6 @@ void __init vdma_init(void)
92 */ 91 */
93unsigned long vdma_alloc(unsigned long paddr, unsigned long size) 92unsigned long vdma_alloc(unsigned long paddr, unsigned long size)
94{ 93{
95 VDMA_PGTBL_ENTRY *entry = (VDMA_PGTBL_ENTRY *) vdma_pagetable_start;
96 int first, last, pages, frame, i; 94 int first, last, pages, frame, i;
97 unsigned long laddr, flags; 95 unsigned long laddr, flags;
98 96
@@ -114,10 +112,10 @@ unsigned long vdma_alloc(unsigned long paddr, unsigned long size)
114 /* 112 /*
115 * Find free chunk 113 * Find free chunk
116 */ 114 */
117 pages = (size + 4095) >> 12; /* no. of pages to allocate */ 115 pages = VDMA_PAGE(paddr + size) - VDMA_PAGE(paddr) + 1;
118 first = 0; 116 first = 0;
119 while (1) { 117 while (1) {
120 while (entry[first].owner != VDMA_PAGE_EMPTY && 118 while (pgtbl[first].owner != VDMA_PAGE_EMPTY &&
121 first < VDMA_PGTBL_ENTRIES) first++; 119 first < VDMA_PGTBL_ENTRIES) first++;
122 if (first + pages > VDMA_PGTBL_ENTRIES) { /* nothing free */ 120 if (first + pages > VDMA_PGTBL_ENTRIES) { /* nothing free */
123 spin_unlock_irqrestore(&vdma_lock, flags); 121 spin_unlock_irqrestore(&vdma_lock, flags);
@@ -125,12 +123,13 @@ unsigned long vdma_alloc(unsigned long paddr, unsigned long size)
125 } 123 }
126 124
127 last = first + 1; 125 last = first + 1;
128 while (entry[last].owner == VDMA_PAGE_EMPTY 126 while (pgtbl[last].owner == VDMA_PAGE_EMPTY
129 && last - first < pages) 127 && last - first < pages)
130 last++; 128 last++;
131 129
132 if (last - first == pages) 130 if (last - first == pages)
133 break; /* found */ 131 break; /* found */
132 first = last + 1;
134 } 133 }
135 134
136 /* 135 /*
@@ -140,8 +139,8 @@ unsigned long vdma_alloc(unsigned long paddr, unsigned long size)
140 frame = paddr & ~(VDMA_PAGESIZE - 1); 139 frame = paddr & ~(VDMA_PAGESIZE - 1);
141 140
142 for (i = first; i < last; i++) { 141 for (i = first; i < last; i++) {
143 entry[i].frame = frame; 142 pgtbl[i].frame = frame;
144 entry[i].owner = laddr; 143 pgtbl[i].owner = laddr;
145 frame += VDMA_PAGESIZE; 144 frame += VDMA_PAGESIZE;
146 } 145 }
147 146
@@ -160,10 +159,10 @@ unsigned long vdma_alloc(unsigned long paddr, unsigned long size)
160 printk("%08x ", i << 12); 159 printk("%08x ", i << 12);
161 printk("\nPADDR: "); 160 printk("\nPADDR: ");
162 for (i = first; i < last; i++) 161 for (i = first; i < last; i++)
163 printk("%08x ", entry[i].frame); 162 printk("%08x ", pgtbl[i].frame);
164 printk("\nOWNER: "); 163 printk("\nOWNER: ");
165 for (i = first; i < last; i++) 164 for (i = first; i < last; i++)
166 printk("%08x ", entry[i].owner); 165 printk("%08x ", pgtbl[i].owner);
167 printk("\n"); 166 printk("\n");
168 } 167 }
169 168
@@ -181,7 +180,6 @@ EXPORT_SYMBOL(vdma_alloc);
181 */ 180 */
182int vdma_free(unsigned long laddr) 181int vdma_free(unsigned long laddr)
183{ 182{
184 VDMA_PGTBL_ENTRY *pgtbl = (VDMA_PGTBL_ENTRY *) vdma_pagetable_start;
185 int i; 183 int i;
186 184
187 i = laddr >> 12; 185 i = laddr >> 12;
@@ -213,8 +211,6 @@ EXPORT_SYMBOL(vdma_free);
213 */ 211 */
214int vdma_remap(unsigned long laddr, unsigned long paddr, unsigned long size) 212int vdma_remap(unsigned long laddr, unsigned long paddr, unsigned long size)
215{ 213{
216 VDMA_PGTBL_ENTRY *pgtbl =
217 (VDMA_PGTBL_ENTRY *) vdma_pagetable_start;
218 int first, pages, npages; 214 int first, pages, npages;
219 215
220 if (laddr > 0xffffff) { 216 if (laddr > 0xffffff) {
@@ -289,8 +285,6 @@ unsigned long vdma_phys2log(unsigned long paddr)
289{ 285{
290 int i; 286 int i;
291 int frame; 287 int frame;
292 VDMA_PGTBL_ENTRY *pgtbl =
293 (VDMA_PGTBL_ENTRY *) vdma_pagetable_start;
294 288
295 frame = paddr & ~(VDMA_PAGESIZE - 1); 289 frame = paddr & ~(VDMA_PAGESIZE - 1);
296 290
@@ -312,9 +306,6 @@ EXPORT_SYMBOL(vdma_phys2log);
312 */ 306 */
313unsigned long vdma_log2phys(unsigned long laddr) 307unsigned long vdma_log2phys(unsigned long laddr)
314{ 308{
315 VDMA_PGTBL_ENTRY *pgtbl =
316 (VDMA_PGTBL_ENTRY *) vdma_pagetable_start;
317
318 return pgtbl[laddr >> 12].frame + (laddr & (VDMA_PAGESIZE - 1)); 309 return pgtbl[laddr >> 12].frame + (laddr & (VDMA_PAGESIZE - 1));
319} 310}
320 311
@@ -564,3 +555,5 @@ int vdma_get_enable(int channel)
564 555
565 return enable; 556 return enable;
566} 557}
558
559arch_initcall(vdma_init);
diff --git a/arch/mips/jazz/setup.c b/arch/mips/jazz/setup.c
index 798279e06691..5c6271ab927f 100644
--- a/arch/mips/jazz/setup.c
+++ b/arch/mips/jazz/setup.c
@@ -7,6 +7,7 @@
7 * 7 *
8 * Copyright (C) 1996, 1997, 1998, 2001 by Ralf Baechle 8 * Copyright (C) 1996, 1997, 1998, 2001 by Ralf Baechle
9 * Copyright (C) 2001 MIPS Technologies, Inc. 9 * Copyright (C) 2001 MIPS Technologies, Inc.
10 * Copyright (C) 2007 by Thomas Bogendoerfer
10 */ 11 */
11#include <linux/eisa.h> 12#include <linux/eisa.h>
12#include <linux/hdreg.h> 13#include <linux/hdreg.h>
@@ -20,6 +21,8 @@
20#include <linux/ide.h> 21#include <linux/ide.h>
21#include <linux/pm.h> 22#include <linux/pm.h>
22#include <linux/screen_info.h> 23#include <linux/screen_info.h>
24#include <linux/platform_device.h>
25#include <linux/serial_8250.h>
23 26
24#include <asm/bootinfo.h> 27#include <asm/bootinfo.h>
25#include <asm/irq.h> 28#include <asm/irq.h>
@@ -30,6 +33,7 @@
30#include <asm/pgtable.h> 33#include <asm/pgtable.h>
31#include <asm/time.h> 34#include <asm/time.h>
32#include <asm/traps.h> 35#include <asm/traps.h>
36#include <asm/mc146818-time.h>
33 37
34extern asmlinkage void jazz_handle_int(void); 38extern asmlinkage void jazz_handle_int(void);
35 39
@@ -72,10 +76,8 @@ void __init plat_mem_setup(void)
72 76
73 /* Map 0xe0000000 -> 0x0:800005C0, 0xe0010000 -> 0x1:30000580 */ 77 /* Map 0xe0000000 -> 0x0:800005C0, 0xe0010000 -> 0x1:30000580 */
74 add_wired_entry (0x02000017, 0x03c00017, 0xe0000000, PM_64K); 78 add_wired_entry (0x02000017, 0x03c00017, 0xe0000000, PM_64K);
75
76 /* Map 0xe2000000 -> 0x0:900005C0, 0xe3010000 -> 0x0:910005C0 */ 79 /* Map 0xe2000000 -> 0x0:900005C0, 0xe3010000 -> 0x0:910005C0 */
77 add_wired_entry (0x02400017, 0x02440017, 0xe2000000, PM_16M); 80 add_wired_entry (0x02400017, 0x02440017, 0xe2000000, PM_16M);
78
79 /* Map 0xe4000000 -> 0x0:600005C0, 0xe4100000 -> 400005C0 */ 81 /* Map 0xe4000000 -> 0x0:600005C0, 0xe4100000 -> 400005C0 */
80 add_wired_entry (0x01800017, 0x01000017, 0xe4000000, PM_4M); 82 add_wired_entry (0x01800017, 0x01000017, 0xe4000000, PM_4M);
81 83
@@ -94,6 +96,7 @@ void __init plat_mem_setup(void)
94 96
95 _machine_restart = jazz_machine_restart; 97 _machine_restart = jazz_machine_restart;
96 98
99#ifdef CONFIG_VT
97 screen_info = (struct screen_info) { 100 screen_info = (struct screen_info) {
98 0, 0, /* orig-x, orig-y */ 101 0, 0, /* orig-x, orig-y */
99 0, /* unused */ 102 0, /* unused */
@@ -105,6 +108,112 @@ void __init plat_mem_setup(void)
105 0, /* orig_video_isVGA */ 108 0, /* orig_video_isVGA */
106 16 /* orig_video_points */ 109 16 /* orig_video_points */
107 }; 110 };
111#endif
108 112
109 vdma_init(); 113 add_preferred_console("ttyS", 0, "9600");
110} 114}
115
116#ifdef CONFIG_OLIVETTI_M700
117#define UART_CLK 1843200
118#else
119/* Some Jazz machines seem to have an 8MHz crystal clock but I don't know
120 exactly which ones ... XXX */
121#define UART_CLK (8000000 / 16) /* ( 3072000 / 16) */
122#endif
123
124#define MEMPORT(_base, _irq) \
125 { \
126 .mapbase = (_base), \
127 .membase = (void *)(_base), \
128 .irq = (_irq), \
129 .uartclk = UART_CLK, \
130 .iotype = UPIO_MEM, \
131 .flags = UPF_BOOT_AUTOCONF, \
132 }
133
134static struct plat_serial8250_port jazz_serial_data[] = {
135 MEMPORT(JAZZ_SERIAL1_BASE, JAZZ_SERIAL1_IRQ),
136 MEMPORT(JAZZ_SERIAL2_BASE, JAZZ_SERIAL2_IRQ),
137 { },
138};
139
140static struct platform_device jazz_serial8250_device = {
141 .name = "serial8250",
142 .id = PLAT8250_DEV_PLATFORM,
143 .dev = {
144 .platform_data = jazz_serial_data,
145 },
146};
147
148static struct resource jazz_esp_rsrc[] = {
149 {
150 .start = JAZZ_SCSI_BASE,
151 .end = JAZZ_SCSI_BASE + 31,
152 .flags = IORESOURCE_MEM
153 },
154 {
155 .start = JAZZ_SCSI_DMA,
156 .end = JAZZ_SCSI_DMA,
157 .flags = IORESOURCE_MEM
158 },
159 {
160 .start = JAZZ_SCSI_IRQ,
161 .end = JAZZ_SCSI_IRQ,
162 .flags = IORESOURCE_IRQ
163 }
164};
165
166static struct platform_device jazz_esp_pdev = {
167 .name = "jazz_esp",
168 .num_resources = ARRAY_SIZE(jazz_esp_rsrc),
169 .resource = jazz_esp_rsrc
170};
171
172static struct resource jazz_sonic_rsrc[] = {
173 {
174 .start = JAZZ_ETHERNET_BASE,
175 .end = JAZZ_ETHERNET_BASE + 0xff,
176 .flags = IORESOURCE_MEM
177 },
178 {
179 .start = JAZZ_ETHERNET_IRQ,
180 .end = JAZZ_ETHERNET_IRQ,
181 .flags = IORESOURCE_IRQ
182 }
183};
184
185static struct platform_device jazz_sonic_pdev = {
186 .name = "jazzsonic",
187 .num_resources = ARRAY_SIZE(jazz_sonic_rsrc),
188 .resource = jazz_sonic_rsrc
189};
190
191static struct resource jazz_cmos_rsrc[] = {
192 {
193 .start = 0x70,
194 .end = 0x71,
195 .flags = IORESOURCE_IO
196 },
197 {
198 .start = 8,
199 .end = 8,
200 .flags = IORESOURCE_IRQ
201 }
202};
203
204static struct platform_device jazz_cmos_pdev = {
205 .name = "rtc_cmos",
206 .num_resources = ARRAY_SIZE(jazz_cmos_rsrc),
207 .resource = jazz_cmos_rsrc
208};
209
210static int __init jazz_setup_devinit(void)
211{
212 platform_device_register(&jazz_serial8250_device);
213 platform_device_register(&jazz_esp_pdev);
214 platform_device_register(&jazz_sonic_pdev);
215 platform_device_register(&jazz_cmos_pdev);
216 return 0;
217}
218
219device_initcall(jazz_setup_devinit);
">long ret; u16 wait_state_count; issue_reset = 0; if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) { printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n", ioc->name, __func__); ret = -EAGAIN; goto out; } wait_state_count = 0; ioc_state = mpt2sas_base_get_iocstate(ioc, 1); while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { if (wait_state_count++ == 10) { printk(MPT2SAS_ERR_FMT "%s: failed due to ioc not operational\n", ioc->name, __func__); ret = -EFAULT; goto out; } ssleep(1); ioc_state = mpt2sas_base_get_iocstate(ioc, 1); printk(MPT2SAS_INFO_FMT "%s: waiting for " "operational state(count=%d)\n", ioc->name, __func__, wait_state_count); } if (wait_state_count) printk(MPT2SAS_INFO_FMT "%s: ioc is operational\n", ioc->name, __func__); mpi_request = kzalloc(ioc->request_sz, GFP_KERNEL); if (!mpi_request) { printk(MPT2SAS_ERR_FMT "%s: failed obtaining a memory for " "mpi_request\n", ioc->name, __func__); ret = -ENOMEM; goto out; } /* Check for overflow and wraparound */ if (karg.data_sge_offset * 4 > ioc->request_sz || karg.data_sge_offset > (UINT_MAX / 4)) { ret = -EINVAL; goto out; } /* copy in request message frame from user */ if (copy_from_user(mpi_request, mf, karg.data_sge_offset*4)) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); ret = -EFAULT; goto out; } if (mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT) { smid = mpt2sas_base_get_smid_hpr(ioc, ioc->ctl_cb_idx); if (!smid) { printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", ioc->name, __func__); ret = -EAGAIN; goto out; } } else { smid = mpt2sas_base_get_smid_scsiio(ioc, ioc->ctl_cb_idx, NULL); if (!smid) { printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", ioc->name, __func__); ret = -EAGAIN; goto out; } } ret = 0; ioc->ctl_cmds.status = MPT2_CMD_PENDING; memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); request = mpt2sas_base_get_msg_frame(ioc, smid); memcpy(request, mpi_request, karg.data_sge_offset*4); ioc->ctl_cmds.smid = smid; data_out_sz = karg.data_out_size; data_in_sz = karg.data_in_size; if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || mpi_request->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) { if (!le16_to_cpu(mpi_request->FunctionDependent1) || le16_to_cpu(mpi_request->FunctionDependent1) > ioc->facts.MaxDevHandle) { ret = -EINVAL; mpt2sas_base_free_smid(ioc, smid); goto out; } } /* obtain dma-able memory for data transfer */ if (data_out_sz) /* WRITE */ { data_out = pci_alloc_consistent(ioc->pdev, data_out_sz, &data_out_dma); if (!data_out) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); ret = -ENOMEM; mpt2sas_base_free_smid(ioc, smid); goto out; } if (copy_from_user(data_out, karg.data_out_buf_ptr, data_out_sz)) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); ret = -EFAULT; mpt2sas_base_free_smid(ioc, smid); goto out; } } if (data_in_sz) /* READ */ { data_in = pci_alloc_consistent(ioc->pdev, data_in_sz, &data_in_dma); if (!data_in) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); ret = -ENOMEM; mpt2sas_base_free_smid(ioc, smid); goto out; } } /* add scatter gather elements */ psge = (void *)request + (karg.data_sge_offset*4); if (!data_out_sz && !data_in_sz) { mpt2sas_base_build_zero_len_sge(ioc, psge); } else if (data_out_sz && data_in_sz) { /* WRITE sgel first */ sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC); sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; ioc->base_add_sg_single(psge, sgl_flags | data_out_sz, data_out_dma); /* incr sgel */ psge += ioc->sge_size; /* READ sgel last */ sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_END_OF_LIST); sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; ioc->base_add_sg_single(psge, sgl_flags | data_in_sz, data_in_dma); } else if (data_out_sz) /* WRITE */ { sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_END_OF_LIST | MPI2_SGE_FLAGS_HOST_TO_IOC); sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; ioc->base_add_sg_single(psge, sgl_flags | data_out_sz, data_out_dma); } else if (data_in_sz) /* READ */ { sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_END_OF_LIST); sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; ioc->base_add_sg_single(psge, sgl_flags | data_in_sz, data_in_dma); } /* send command to firmware */ #ifdef CONFIG_SCSI_MPT2SAS_LOGGING _ctl_display_some_debug(ioc, smid, "ctl_request", NULL); #endif init_completion(&ioc->ctl_cmds.done); switch (mpi_request->Function) { case MPI2_FUNCTION_SCSI_IO_REQUEST: case MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH: { Mpi2SCSIIORequest_t *scsiio_request = (Mpi2SCSIIORequest_t *)request; scsiio_request->SenseBufferLength = SCSI_SENSE_BUFFERSIZE; scsiio_request->SenseBufferLowAddress = mpt2sas_base_get_sense_buffer_dma(ioc, smid); memset(ioc->ctl_cmds.sense, 0, SCSI_SENSE_BUFFERSIZE); if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST) mpt2sas_base_put_smid_scsi_io(ioc, smid, le16_to_cpu(mpi_request->FunctionDependent1)); else mpt2sas_base_put_smid_default(ioc, smid); break; } case MPI2_FUNCTION_SCSI_TASK_MGMT: { Mpi2SCSITaskManagementRequest_t *tm_request = (Mpi2SCSITaskManagementRequest_t *)request; dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "TASK_MGMT: " "handle(0x%04x), task_type(0x%02x)\n", ioc->name, le16_to_cpu(tm_request->DevHandle), tm_request->TaskType)); if (tm_request->TaskType == MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK || tm_request->TaskType == MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK) { if (_ctl_set_task_mid(ioc, &karg, tm_request)) { mpt2sas_base_free_smid(ioc, smid); goto out; } } mpt2sas_scsih_set_tm_flag(ioc, le16_to_cpu( tm_request->DevHandle)); mpt2sas_base_put_smid_hi_priority(ioc, smid); break; } case MPI2_FUNCTION_SMP_PASSTHROUGH: { Mpi2SmpPassthroughRequest_t *smp_request = (Mpi2SmpPassthroughRequest_t *)mpi_request; u8 *data; /* ioc determines which port to use */ smp_request->PhysicalPort = 0xFF; if (smp_request->PassthroughFlags & MPI2_SMP_PT_REQ_PT_FLAGS_IMMEDIATE) data = (u8 *)&smp_request->SGL; else { if (unlikely(data_out == NULL)) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); mpt2sas_base_free_smid(ioc, smid); ret = -EINVAL; goto out; } data = data_out; } if (data[1] == 0x91 && (data[10] == 1 || data[10] == 2)) { ioc->ioc_link_reset_in_progress = 1; ioc->ignore_loginfos = 1; } mpt2sas_base_put_smid_default(ioc, smid); break; } case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL: { Mpi2SasIoUnitControlRequest_t *sasiounit_request = (Mpi2SasIoUnitControlRequest_t *)mpi_request; if (sasiounit_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET || sasiounit_request->Operation == MPI2_SAS_OP_PHY_LINK_RESET) { ioc->ioc_link_reset_in_progress = 1; ioc->ignore_loginfos = 1; } mpt2sas_base_put_smid_default(ioc, smid); break; } default: mpt2sas_base_put_smid_default(ioc, smid); break; } if (karg.timeout < MPT2_IOCTL_DEFAULT_TIMEOUT) timeout = MPT2_IOCTL_DEFAULT_TIMEOUT; else timeout = karg.timeout; timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, timeout*HZ); if (mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT) { Mpi2SCSITaskManagementRequest_t *tm_request = (Mpi2SCSITaskManagementRequest_t *)mpi_request; mpt2sas_scsih_clear_tm_flag(ioc, le16_to_cpu( tm_request->DevHandle)); } else if ((mpi_request->Function == MPI2_FUNCTION_SMP_PASSTHROUGH || mpi_request->Function == MPI2_FUNCTION_SAS_IO_UNIT_CONTROL) && ioc->ioc_link_reset_in_progress) { ioc->ioc_link_reset_in_progress = 0; ioc->ignore_loginfos = 0; } if (!(ioc->ctl_cmds.status & MPT2_CMD_COMPLETE)) { printk(MPT2SAS_ERR_FMT "%s: timeout\n", ioc->name, __func__); _debug_dump_mf(mpi_request, karg.data_sge_offset); if (!(ioc->ctl_cmds.status & MPT2_CMD_RESET)) issue_reset = 1; goto issue_host_reset; } mpi_reply = ioc->ctl_cmds.reply; ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; #ifdef CONFIG_SCSI_MPT2SAS_LOGGING if (mpi_reply->Function == MPI2_FUNCTION_SCSI_TASK_MGMT && (ioc->logging_level & MPT_DEBUG_TM)) { Mpi2SCSITaskManagementReply_t *tm_reply = (Mpi2SCSITaskManagementReply_t *)mpi_reply; printk(MPT2SAS_INFO_FMT "TASK_MGMT: " "IOCStatus(0x%04x), IOCLogInfo(0x%08x), " "TerminationCount(0x%08x)\n", ioc->name, le16_to_cpu(tm_reply->IOCStatus), le32_to_cpu(tm_reply->IOCLogInfo), le32_to_cpu(tm_reply->TerminationCount)); } #endif /* copy out xdata to user */ if (data_in_sz) { if (copy_to_user(karg.data_in_buf_ptr, data_in, data_in_sz)) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); ret = -ENODATA; goto out; } } /* copy out reply message frame to user */ if (karg.max_reply_bytes) { sz = min_t(u32, karg.max_reply_bytes, ioc->reply_sz); if (copy_to_user(karg.reply_frame_buf_ptr, ioc->ctl_cmds.reply, sz)) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); ret = -ENODATA; goto out; } } /* copy out sense to user */ if (karg.max_sense_bytes && (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || mpi_request->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) { sz = min_t(u32, karg.max_sense_bytes, SCSI_SENSE_BUFFERSIZE); if (copy_to_user(karg.sense_data_ptr, ioc->ctl_cmds.sense, sz)) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); ret = -ENODATA; goto out; } } issue_host_reset: if (issue_reset) { ret = -ENODATA; if ((mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || mpi_request->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH || mpi_request->Function == MPI2_FUNCTION_SATA_PASSTHROUGH)) { printk(MPT2SAS_INFO_FMT "issue target reset: handle " "= (0x%04x)\n", ioc->name, le16_to_cpu(mpi_request->FunctionDependent1)); mpt2sas_halt_firmware(ioc); mpt2sas_scsih_issue_tm(ioc, le16_to_cpu(mpi_request->FunctionDependent1), 0, 0, 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 10, 0, TM_MUTEX_ON); ioc->tm_cmds.status = MPT2_CMD_NOT_USED; } else mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, FORCE_BIG_HAMMER); } out: /* free memory associated with sg buffers */ if (data_in) pci_free_consistent(ioc->pdev, data_in_sz, data_in, data_in_dma); if (data_out) pci_free_consistent(ioc->pdev, data_out_sz, data_out, data_out_dma); kfree(mpi_request); ioc->ctl_cmds.status = MPT2_CMD_NOT_USED; return ret; } /** * _ctl_getiocinfo - main handler for MPT2IOCINFO opcode * @ioc: per adapter object * @arg - user space buffer containing ioctl content */ static long _ctl_getiocinfo(struct MPT2SAS_ADAPTER *ioc, void __user *arg) { struct mpt2_ioctl_iocinfo karg; if (copy_from_user(&karg, arg, sizeof(karg))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name, __func__)); memset(&karg, 0 , sizeof(karg)); if (ioc->is_warpdrive) karg.adapter_type = MPT2_IOCTL_INTERFACE_SAS2_SSS6200; else karg.adapter_type = MPT2_IOCTL_INTERFACE_SAS2; if (ioc->pfacts) karg.port_number = ioc->pfacts[0].PortNumber; karg.hw_rev = ioc->pdev->revision; karg.pci_id = ioc->pdev->device; karg.subsystem_device = ioc->pdev->subsystem_device; karg.subsystem_vendor = ioc->pdev->subsystem_vendor; karg.pci_information.u.bits.bus = ioc->pdev->bus->number; karg.pci_information.u.bits.device = PCI_SLOT(ioc->pdev->devfn); karg.pci_information.u.bits.function = PCI_FUNC(ioc->pdev->devfn); karg.pci_information.segment_id = pci_domain_nr(ioc->pdev->bus); karg.firmware_version = ioc->facts.FWVersion.Word; strcpy(karg.driver_version, MPT2SAS_DRIVER_NAME); strcat(karg.driver_version, "-"); strcat(karg.driver_version, MPT2SAS_DRIVER_VERSION); karg.bios_version = le32_to_cpu(ioc->bios_pg3.BiosVersion); if (copy_to_user(arg, &karg, sizeof(karg))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } return 0; } /** * _ctl_eventquery - main handler for MPT2EVENTQUERY opcode * @ioc: per adapter object * @arg - user space buffer containing ioctl content */ static long _ctl_eventquery(struct MPT2SAS_ADAPTER *ioc, void __user *arg) { struct mpt2_ioctl_eventquery karg; if (copy_from_user(&karg, arg, sizeof(karg))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name, __func__)); karg.event_entries = MPT2SAS_CTL_EVENT_LOG_SIZE; memcpy(karg.event_types, ioc->event_type, MPI2_EVENT_NOTIFY_EVENTMASK_WORDS * sizeof(u32)); if (copy_to_user(arg, &karg, sizeof(karg))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } return 0; } /** * _ctl_eventenable - main handler for MPT2EVENTENABLE opcode * @ioc: per adapter object * @arg - user space buffer containing ioctl content */ static long _ctl_eventenable(struct MPT2SAS_ADAPTER *ioc, void __user *arg) { struct mpt2_ioctl_eventenable karg; if (copy_from_user(&karg, arg, sizeof(karg))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name, __func__)); if (ioc->event_log) return 0; memcpy(ioc->event_type, karg.event_types, MPI2_EVENT_NOTIFY_EVENTMASK_WORDS * sizeof(u32)); mpt2sas_base_validate_event_type(ioc, ioc->event_type); /* initialize event_log */ ioc->event_context = 0; ioc->aen_event_read_flag = 0; ioc->event_log = kcalloc(MPT2SAS_CTL_EVENT_LOG_SIZE, sizeof(struct MPT2_IOCTL_EVENTS), GFP_KERNEL); if (!ioc->event_log) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -ENOMEM; } return 0; } /** * _ctl_eventreport - main handler for MPT2EVENTREPORT opcode * @ioc: per adapter object * @arg - user space buffer containing ioctl content */ static long _ctl_eventreport(struct MPT2SAS_ADAPTER *ioc, void __user *arg) { struct mpt2_ioctl_eventreport karg; u32 number_bytes, max_events, max; struct mpt2_ioctl_eventreport __user *uarg = arg; if (copy_from_user(&karg, arg, sizeof(karg))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name, __func__)); number_bytes = karg.hdr.max_data_size - sizeof(struct mpt2_ioctl_header); max_events = number_bytes/sizeof(struct MPT2_IOCTL_EVENTS); max = min_t(u32, MPT2SAS_CTL_EVENT_LOG_SIZE, max_events); /* If fewer than 1 event is requested, there must have * been some type of error. */ if (!max || !ioc->event_log) return -ENODATA; number_bytes = max * sizeof(struct MPT2_IOCTL_EVENTS); if (copy_to_user(uarg->event_data, ioc->event_log, number_bytes)) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } /* reset flag so SIGIO can restart */ ioc->aen_event_read_flag = 0; return 0; } /** * _ctl_do_reset - main handler for MPT2HARDRESET opcode * @ioc: per adapter object * @arg - user space buffer containing ioctl content */ static long _ctl_do_reset(struct MPT2SAS_ADAPTER *ioc, void __user *arg) { struct mpt2_ioctl_diag_reset karg; int retval; if (copy_from_user(&karg, arg, sizeof(karg))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } if (ioc->shost_recovery || ioc->pci_error_recovery || ioc->is_driver_loading) return -EAGAIN; dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter\n", ioc->name, __func__)); retval = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, FORCE_BIG_HAMMER); printk(MPT2SAS_INFO_FMT "host reset: %s\n", ioc->name, ((!retval) ? "SUCCESS" : "FAILED")); return 0; } /** * _ctl_btdh_search_sas_device - searching for sas device * @ioc: per adapter object * @btdh: btdh ioctl payload */ static int _ctl_btdh_search_sas_device(struct MPT2SAS_ADAPTER *ioc, struct mpt2_ioctl_btdh_mapping *btdh) { struct _sas_device *sas_device; unsigned long flags; int rc = 0; if (list_empty(&ioc->sas_device_list)) return rc; spin_lock_irqsave(&ioc->sas_device_lock, flags); list_for_each_entry(sas_device, &ioc->sas_device_list, list) { if (btdh->bus == 0xFFFFFFFF && btdh->id == 0xFFFFFFFF && btdh->handle == sas_device->handle) { btdh->bus = sas_device->channel; btdh->id = sas_device->id; rc = 1; goto out; } else if (btdh->bus == sas_device->channel && btdh->id == sas_device->id && btdh->handle == 0xFFFF) { btdh->handle = sas_device->handle; rc = 1; goto out; } } out: spin_unlock_irqrestore(&ioc->sas_device_lock, flags); return rc; } /** * _ctl_btdh_search_raid_device - searching for raid device * @ioc: per adapter object * @btdh: btdh ioctl payload */ static int _ctl_btdh_search_raid_device(struct MPT2SAS_ADAPTER *ioc, struct mpt2_ioctl_btdh_mapping *btdh) { struct _raid_device *raid_device; unsigned long flags; int rc = 0; if (list_empty(&ioc->raid_device_list)) return rc; spin_lock_irqsave(&ioc->raid_device_lock, flags); list_for_each_entry(raid_device, &ioc->raid_device_list, list) { if (btdh->bus == 0xFFFFFFFF && btdh->id == 0xFFFFFFFF && btdh->handle == raid_device->handle) { btdh->bus = raid_device->channel; btdh->id = raid_device->id; rc = 1; goto out; } else if (btdh->bus == raid_device->channel && btdh->id == raid_device->id && btdh->handle == 0xFFFF) { btdh->handle = raid_device->handle; rc = 1; goto out; } } out: spin_unlock_irqrestore(&ioc->raid_device_lock, flags); return rc; } /** * _ctl_btdh_mapping - main handler for MPT2BTDHMAPPING opcode * @ioc: per adapter object * @arg - user space buffer containing ioctl content */ static long _ctl_btdh_mapping(struct MPT2SAS_ADAPTER *ioc, void __user *arg) { struct mpt2_ioctl_btdh_mapping karg; int rc; if (copy_from_user(&karg, arg, sizeof(karg))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); rc = _ctl_btdh_search_sas_device(ioc, &karg); if (!rc) _ctl_btdh_search_raid_device(ioc, &karg); if (copy_to_user(arg, &karg, sizeof(karg))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } return 0; } /** * _ctl_diag_capability - return diag buffer capability * @ioc: per adapter object * @buffer_type: specifies either TRACE, SNAPSHOT, or EXTENDED * * returns 1 when diag buffer support is enabled in firmware */ static u8 _ctl_diag_capability(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type) { u8 rc = 0; switch (buffer_type) { case MPI2_DIAG_BUF_TYPE_TRACE: if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER) rc = 1; break; case MPI2_DIAG_BUF_TYPE_SNAPSHOT: if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER) rc = 1; break; case MPI2_DIAG_BUF_TYPE_EXTENDED: if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER) rc = 1; } return rc; } /** * _ctl_diag_register_2 - wrapper for registering diag buffer support * @ioc: per adapter object * @diag_register: the diag_register struct passed in from user space * */ static long _ctl_diag_register_2(struct MPT2SAS_ADAPTER *ioc, struct mpt2_diag_register *diag_register) { int rc, i; void *request_data = NULL; dma_addr_t request_data_dma; u32 request_data_sz = 0; Mpi2DiagBufferPostRequest_t *mpi_request; Mpi2DiagBufferPostReply_t *mpi_reply; u8 buffer_type; unsigned long timeleft; u16 smid; u16 ioc_status; u8 issue_reset = 0; dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) { printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n", ioc->name, __func__); rc = -EAGAIN; goto out; } buffer_type = diag_register->buffer_type; if (!_ctl_diag_capability(ioc, buffer_type)) { printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for " "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); return -EPERM; } if (ioc->diag_buffer_status[buffer_type] & MPT2_DIAG_BUFFER_IS_REGISTERED) { printk(MPT2SAS_ERR_FMT "%s: already has a registered " "buffer for buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); return -EINVAL; } if (diag_register->requested_buffer_size % 4) { printk(MPT2SAS_ERR_FMT "%s: the requested_buffer_size " "is not 4 byte aligned\n", ioc->name, __func__); return -EINVAL; } smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx); if (!smid) { printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", ioc->name, __func__); rc = -EAGAIN; goto out; } rc = 0; ioc->ctl_cmds.status = MPT2_CMD_PENDING; memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ioc->ctl_cmds.smid = smid; request_data = ioc->diag_buffer[buffer_type]; request_data_sz = diag_register->requested_buffer_size; ioc->unique_id[buffer_type] = diag_register->unique_id; ioc->diag_buffer_status[buffer_type] = 0; memcpy(ioc->product_specific[buffer_type], diag_register->product_specific, MPT2_PRODUCT_SPECIFIC_DWORDS); ioc->diagnostic_flags[buffer_type] = diag_register->diagnostic_flags; if (request_data) { request_data_dma = ioc->diag_buffer_dma[buffer_type]; if (request_data_sz != ioc->diag_buffer_sz[buffer_type]) { pci_free_consistent(ioc->pdev, ioc->diag_buffer_sz[buffer_type], request_data, request_data_dma); request_data = NULL; } } if (request_data == NULL) { ioc->diag_buffer_sz[buffer_type] = 0; ioc->diag_buffer_dma[buffer_type] = 0; request_data = pci_alloc_consistent( ioc->pdev, request_data_sz, &request_data_dma); if (request_data == NULL) { printk(MPT2SAS_ERR_FMT "%s: failed allocating memory" " for diag buffers, requested size(%d)\n", ioc->name, __func__, request_data_sz); mpt2sas_base_free_smid(ioc, smid); return -ENOMEM; } ioc->diag_buffer[buffer_type] = request_data; ioc->diag_buffer_sz[buffer_type] = request_data_sz; ioc->diag_buffer_dma[buffer_type] = request_data_dma; } mpi_request->Function = MPI2_FUNCTION_DIAG_BUFFER_POST; mpi_request->BufferType = diag_register->buffer_type; mpi_request->Flags = cpu_to_le32(diag_register->diagnostic_flags); mpi_request->BufferAddress = cpu_to_le64(request_data_dma); mpi_request->BufferLength = cpu_to_le32(request_data_sz); mpi_request->VF_ID = 0; /* TODO */ mpi_request->VP_ID = 0; dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: diag_buffer(0x%p), " "dma(0x%llx), sz(%d)\n", ioc->name, __func__, request_data, (unsigned long long)request_data_dma, le32_to_cpu(mpi_request->BufferLength))); for (i = 0; i < MPT2_PRODUCT_SPECIFIC_DWORDS; i++) mpi_request->ProductSpecific[i] = cpu_to_le32(ioc->product_specific[buffer_type][i]); init_completion(&ioc->ctl_cmds.done); mpt2sas_base_put_smid_default(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, MPT2_IOCTL_DEFAULT_TIMEOUT*HZ); if (!(ioc->ctl_cmds.status & MPT2_CMD_COMPLETE)) { printk(MPT2SAS_ERR_FMT "%s: timeout\n", ioc->name, __func__); _debug_dump_mf(mpi_request, sizeof(Mpi2DiagBufferPostRequest_t)/4); if (!(ioc->ctl_cmds.status & MPT2_CMD_RESET)) issue_reset = 1; goto issue_host_reset; } /* process the completed Reply Message Frame */ if ((ioc->ctl_cmds.status & MPT2_CMD_REPLY_VALID) == 0) { printk(MPT2SAS_ERR_FMT "%s: no reply message\n", ioc->name, __func__); rc = -EFAULT; goto out; } mpi_reply = ioc->ctl_cmds.reply; ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { ioc->diag_buffer_status[buffer_type] |= MPT2_DIAG_BUFFER_IS_REGISTERED; dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: success\n", ioc->name, __func__)); } else { printk(MPT2SAS_INFO_FMT "%s: ioc_status(0x%04x) " "log_info(0x%08x)\n", ioc->name, __func__, ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo)); rc = -EFAULT; } issue_host_reset: if (issue_reset) mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, FORCE_BIG_HAMMER); out: if (rc && request_data) pci_free_consistent(ioc->pdev, request_data_sz, request_data, request_data_dma); ioc->ctl_cmds.status = MPT2_CMD_NOT_USED; return rc; } /** * mpt2sas_enable_diag_buffer - enabling diag_buffers support driver load time * @ioc: per adapter object * @bits_to_register: bitwise field where trace is bit 0, and snapshot is bit 1 * * This is called when command line option diag_buffer_enable is enabled * at driver load time. */ void mpt2sas_enable_diag_buffer(struct MPT2SAS_ADAPTER *ioc, u8 bits_to_register) { struct mpt2_diag_register diag_register; memset(&diag_register, 0, sizeof(struct mpt2_diag_register)); if (bits_to_register & 1) { printk(MPT2SAS_INFO_FMT "registering trace buffer support\n", ioc->name); diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_TRACE; /* register for 1MB buffers */ diag_register.requested_buffer_size = (1024 * 1024); diag_register.unique_id = 0x7075900; _ctl_diag_register_2(ioc, &diag_register); } if (bits_to_register & 2) { printk(MPT2SAS_INFO_FMT "registering snapshot buffer support\n", ioc->name); diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_SNAPSHOT; /* register for 2MB buffers */ diag_register.requested_buffer_size = 2 * (1024 * 1024); diag_register.unique_id = 0x7075901; _ctl_diag_register_2(ioc, &diag_register); } if (bits_to_register & 4) { printk(MPT2SAS_INFO_FMT "registering extended buffer support\n", ioc->name); diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_EXTENDED; /* register for 2MB buffers */ diag_register.requested_buffer_size = 2 * (1024 * 1024); diag_register.unique_id = 0x7075901; _ctl_diag_register_2(ioc, &diag_register); } } /** * _ctl_diag_register - application register with driver * @ioc: per adapter object * @arg - user space buffer containing ioctl content * * This will allow the driver to setup any required buffers that will be * needed by firmware to communicate with the driver. */ static long _ctl_diag_register(struct MPT2SAS_ADAPTER *ioc, void __user *arg) { struct mpt2_diag_register karg; long rc; if (copy_from_user(&karg, arg, sizeof(karg))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } rc = _ctl_diag_register_2(ioc, &karg); return rc; } /** * _ctl_diag_unregister - application unregister with driver * @ioc: per adapter object * @arg - user space buffer containing ioctl content * * This will allow the driver to cleanup any memory allocated for diag * messages and to free up any resources. */ static long _ctl_diag_unregister(struct MPT2SAS_ADAPTER *ioc, void __user *arg) { struct mpt2_diag_unregister karg; void *request_data; dma_addr_t request_data_dma; u32 request_data_sz; u8 buffer_type; if (copy_from_user(&karg, arg, sizeof(karg))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); buffer_type = karg.unique_id & 0x000000ff; if (!_ctl_diag_capability(ioc, buffer_type)) { printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for " "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); return -EPERM; } if ((ioc->diag_buffer_status[buffer_type] & MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) { printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) is not " "registered\n", ioc->name, __func__, buffer_type); return -EINVAL; } if ((ioc->diag_buffer_status[buffer_type] & MPT2_DIAG_BUFFER_IS_RELEASED) == 0) { printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) has not been " "released\n", ioc->name, __func__, buffer_type); return -EINVAL; } if (karg.unique_id != ioc->unique_id[buffer_type]) { printk(MPT2SAS_ERR_FMT "%s: unique_id(0x%08x) is not " "registered\n", ioc->name, __func__, karg.unique_id); return -EINVAL; } request_data = ioc->diag_buffer[buffer_type]; if (!request_data) { printk(MPT2SAS_ERR_FMT "%s: doesn't have memory allocated for " "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); return -ENOMEM; } request_data_sz = ioc->diag_buffer_sz[buffer_type]; request_data_dma = ioc->diag_buffer_dma[buffer_type]; pci_free_consistent(ioc->pdev, request_data_sz, request_data, request_data_dma); ioc->diag_buffer[buffer_type] = NULL; ioc->diag_buffer_status[buffer_type] = 0; return 0; } /** * _ctl_diag_query - query relevant info associated with diag buffers * @ioc: per adapter object * @arg - user space buffer containing ioctl content * * The application will send only buffer_type and unique_id. Driver will * inspect unique_id first, if valid, fill in all the info. If unique_id is * 0x00, the driver will return info specified by Buffer Type. */ static long _ctl_diag_query(struct MPT2SAS_ADAPTER *ioc, void __user *arg) { struct mpt2_diag_query karg; void *request_data; int i; u8 buffer_type; if (copy_from_user(&karg, arg, sizeof(karg))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); karg.application_flags = 0; buffer_type = karg.buffer_type; if (!_ctl_diag_capability(ioc, buffer_type)) { printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for " "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); return -EPERM; } if ((ioc->diag_buffer_status[buffer_type] & MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) { printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) is not " "registered\n", ioc->name, __func__, buffer_type); return -EINVAL; } if (karg.unique_id & 0xffffff00) { if (karg.unique_id != ioc->unique_id[buffer_type]) { printk(MPT2SAS_ERR_FMT "%s: unique_id(0x%08x) is not " "registered\n", ioc->name, __func__, karg.unique_id); return -EINVAL; } } request_data = ioc->diag_buffer[buffer_type]; if (!request_data) { printk(MPT2SAS_ERR_FMT "%s: doesn't have buffer for " "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); return -ENOMEM; } if (ioc->diag_buffer_status[buffer_type] & MPT2_DIAG_BUFFER_IS_RELEASED) karg.application_flags = (MPT2_APP_FLAGS_APP_OWNED | MPT2_APP_FLAGS_BUFFER_VALID); else karg.application_flags = (MPT2_APP_FLAGS_APP_OWNED | MPT2_APP_FLAGS_BUFFER_VALID | MPT2_APP_FLAGS_FW_BUFFER_ACCESS); for (i = 0; i < MPT2_PRODUCT_SPECIFIC_DWORDS; i++) karg.product_specific[i] = ioc->product_specific[buffer_type][i]; karg.total_buffer_size = ioc->diag_buffer_sz[buffer_type]; karg.driver_added_buffer_size = 0; karg.unique_id = ioc->unique_id[buffer_type]; karg.diagnostic_flags = ioc->diagnostic_flags[buffer_type]; if (copy_to_user(arg, &karg, sizeof(struct mpt2_diag_query))) { printk(MPT2SAS_ERR_FMT "%s: unable to write mpt2_diag_query " "data @ %p\n", ioc->name, __func__, arg); return -EFAULT; } return 0; } /** * _ctl_send_release - Diag Release Message * @ioc: per adapter object * @buffer_type - specifies either TRACE, SNAPSHOT, or EXTENDED * @issue_reset - specifies whether host reset is required. * */ static int _ctl_send_release(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type, u8 *issue_reset) { Mpi2DiagReleaseRequest_t *mpi_request; Mpi2DiagReleaseReply_t *mpi_reply; u16 smid; u16 ioc_status; u32 ioc_state; int rc; unsigned long timeleft; dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); rc = 0; *issue_reset = 0; ioc_state = mpt2sas_base_get_iocstate(ioc, 1); if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " "skipping due to FAULT state\n", ioc->name, __func__)); rc = -EAGAIN; goto out; } if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) { printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n", ioc->name, __func__); rc = -EAGAIN; goto out; } smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx); if (!smid) { printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", ioc->name, __func__); rc = -EAGAIN; goto out; } ioc->ctl_cmds.status = MPT2_CMD_PENDING; memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ioc->ctl_cmds.smid = smid; mpi_request->Function = MPI2_FUNCTION_DIAG_RELEASE; mpi_request->BufferType = buffer_type; mpi_request->VF_ID = 0; /* TODO */ mpi_request->VP_ID = 0; init_completion(&ioc->ctl_cmds.done); mpt2sas_base_put_smid_default(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, MPT2_IOCTL_DEFAULT_TIMEOUT*HZ); if (!(ioc->ctl_cmds.status & MPT2_CMD_COMPLETE)) { printk(MPT2SAS_ERR_FMT "%s: timeout\n", ioc->name, __func__); _debug_dump_mf(mpi_request, sizeof(Mpi2DiagReleaseRequest_t)/4); if (!(ioc->ctl_cmds.status & MPT2_CMD_RESET)) *issue_reset = 1; rc = -EFAULT; goto out; } /* process the completed Reply Message Frame */ if ((ioc->ctl_cmds.status & MPT2_CMD_REPLY_VALID) == 0) { printk(MPT2SAS_ERR_FMT "%s: no reply message\n", ioc->name, __func__); rc = -EFAULT; goto out; } mpi_reply = ioc->ctl_cmds.reply; ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { ioc->diag_buffer_status[buffer_type] |= MPT2_DIAG_BUFFER_IS_RELEASED; dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: success\n", ioc->name, __func__)); } else { printk(MPT2SAS_INFO_FMT "%s: ioc_status(0x%04x) " "log_info(0x%08x)\n", ioc->name, __func__, ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo)); rc = -EFAULT; } out: ioc->ctl_cmds.status = MPT2_CMD_NOT_USED; return rc; } /** * _ctl_diag_release - request to send Diag Release Message to firmware * @arg - user space buffer containing ioctl content * * This allows ownership of the specified buffer to returned to the driver, * allowing an application to read the buffer without fear that firmware is * overwritting information in the buffer. */ static long _ctl_diag_release(struct MPT2SAS_ADAPTER *ioc, void __user *arg) { struct mpt2_diag_release karg; void *request_data; int rc; u8 buffer_type; u8 issue_reset = 0; if (copy_from_user(&karg, arg, sizeof(karg))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); buffer_type = karg.unique_id & 0x000000ff; if (!_ctl_diag_capability(ioc, buffer_type)) { printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for " "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); return -EPERM; } if ((ioc->diag_buffer_status[buffer_type] & MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) { printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) is not " "registered\n", ioc->name, __func__, buffer_type); return -EINVAL; } if (karg.unique_id != ioc->unique_id[buffer_type]) { printk(MPT2SAS_ERR_FMT "%s: unique_id(0x%08x) is not " "registered\n", ioc->name, __func__, karg.unique_id); return -EINVAL; } if (ioc->diag_buffer_status[buffer_type] & MPT2_DIAG_BUFFER_IS_RELEASED) { printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) " "is already released\n", ioc->name, __func__, buffer_type); return 0; } request_data = ioc->diag_buffer[buffer_type]; if (!request_data) { printk(MPT2SAS_ERR_FMT "%s: doesn't have memory allocated for " "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); return -ENOMEM; } /* buffers were released by due to host reset */ if ((ioc->diag_buffer_status[buffer_type] & MPT2_DIAG_BUFFER_IS_DIAG_RESET)) { ioc->diag_buffer_status[buffer_type] |= MPT2_DIAG_BUFFER_IS_RELEASED; ioc->diag_buffer_status[buffer_type] &= ~MPT2_DIAG_BUFFER_IS_DIAG_RESET; printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) " "was released due to host reset\n", ioc->name, __func__, buffer_type); return 0; } rc = _ctl_send_release(ioc, buffer_type, &issue_reset); if (issue_reset) mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, FORCE_BIG_HAMMER); return rc; } /** * _ctl_diag_read_buffer - request for copy of the diag buffer * @ioc: per adapter object * @arg - user space buffer containing ioctl content */ static long _ctl_diag_read_buffer(struct MPT2SAS_ADAPTER *ioc, void __user *arg) { struct mpt2_diag_read_buffer karg; struct mpt2_diag_read_buffer __user *uarg = arg; void *request_data, *diag_data; Mpi2DiagBufferPostRequest_t *mpi_request; Mpi2DiagBufferPostReply_t *mpi_reply; int rc, i; u8 buffer_type; unsigned long timeleft, request_size, copy_size; u16 smid; u16 ioc_status; u8 issue_reset = 0; if (copy_from_user(&karg, arg, sizeof(karg))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__)); buffer_type = karg.unique_id & 0x000000ff; if (!_ctl_diag_capability(ioc, buffer_type)) { printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for " "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); return -EPERM; } if (karg.unique_id != ioc->unique_id[buffer_type]) { printk(MPT2SAS_ERR_FMT "%s: unique_id(0x%08x) is not " "registered\n", ioc->name, __func__, karg.unique_id); return -EINVAL; } request_data = ioc->diag_buffer[buffer_type]; if (!request_data) { printk(MPT2SAS_ERR_FMT "%s: doesn't have buffer for " "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); return -ENOMEM; } request_size = ioc->diag_buffer_sz[buffer_type]; if ((karg.starting_offset % 4) || (karg.bytes_to_read % 4)) { printk(MPT2SAS_ERR_FMT "%s: either the starting_offset " "or bytes_to_read are not 4 byte aligned\n", ioc->name, __func__); return -EINVAL; } if (karg.starting_offset > request_size) return -EINVAL; diag_data = (void *)(request_data + karg.starting_offset); dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: diag_buffer(%p), " "offset(%d), sz(%d)\n", ioc->name, __func__, diag_data, karg.starting_offset, karg.bytes_to_read)); /* Truncate data on requests that are too large */ if ((diag_data + karg.bytes_to_read < diag_data) || (diag_data + karg.bytes_to_read > request_data + request_size)) copy_size = request_size - karg.starting_offset; else copy_size = karg.bytes_to_read; if (copy_to_user((void __user *)uarg->diagnostic_data, diag_data, copy_size)) { printk(MPT2SAS_ERR_FMT "%s: Unable to write " "mpt_diag_read_buffer_t data @ %p\n", ioc->name, __func__, diag_data); return -EFAULT; } if ((karg.flags & MPT2_FLAGS_REREGISTER) == 0) return 0; dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: Reregister " "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type)); if ((ioc->diag_buffer_status[buffer_type] & MPT2_DIAG_BUFFER_IS_RELEASED) == 0) { dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: " "buffer_type(0x%02x) is still registered\n", ioc->name, __func__, buffer_type)); return 0; } /* Get a free request frame and save the message context. */ if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) { printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n", ioc->name, __func__); rc = -EAGAIN; goto out; } smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx); if (!smid) { printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", ioc->name, __func__); rc = -EAGAIN; goto out; } rc = 0; ioc->ctl_cmds.status = MPT2_CMD_PENDING; memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); ioc->ctl_cmds.smid = smid; mpi_request->Function = MPI2_FUNCTION_DIAG_BUFFER_POST; mpi_request->BufferType = buffer_type; mpi_request->BufferLength = cpu_to_le32(ioc->diag_buffer_sz[buffer_type]); mpi_request->BufferAddress = cpu_to_le64(ioc->diag_buffer_dma[buffer_type]); for (i = 0; i < MPT2_PRODUCT_SPECIFIC_DWORDS; i++) mpi_request->ProductSpecific[i] = cpu_to_le32(ioc->product_specific[buffer_type][i]); mpi_request->VF_ID = 0; /* TODO */ mpi_request->VP_ID = 0; init_completion(&ioc->ctl_cmds.done); mpt2sas_base_put_smid_default(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, MPT2_IOCTL_DEFAULT_TIMEOUT*HZ); if (!(ioc->ctl_cmds.status & MPT2_CMD_COMPLETE)) { printk(MPT2SAS_ERR_FMT "%s: timeout\n", ioc->name, __func__); _debug_dump_mf(mpi_request, sizeof(Mpi2DiagBufferPostRequest_t)/4); if (!(ioc->ctl_cmds.status & MPT2_CMD_RESET)) issue_reset = 1; goto issue_host_reset; } /* process the completed Reply Message Frame */ if ((ioc->ctl_cmds.status & MPT2_CMD_REPLY_VALID) == 0) { printk(MPT2SAS_ERR_FMT "%s: no reply message\n", ioc->name, __func__); rc = -EFAULT; goto out; } mpi_reply = ioc->ctl_cmds.reply; ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { ioc->diag_buffer_status[buffer_type] |= MPT2_DIAG_BUFFER_IS_REGISTERED; dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: success\n", ioc->name, __func__)); } else { printk(MPT2SAS_INFO_FMT "%s: ioc_status(0x%04x) " "log_info(0x%08x)\n", ioc->name, __func__, ioc_status, le32_to_cpu(mpi_reply->IOCLogInfo)); rc = -EFAULT; } issue_host_reset: if (issue_reset) mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, FORCE_BIG_HAMMER); out: ioc->ctl_cmds.status = MPT2_CMD_NOT_USED; return rc; } #ifdef CONFIG_COMPAT /** * _ctl_compat_mpt_command - convert 32bit pointers to 64bit. * @ioc: per adapter object * @cmd - ioctl opcode * @arg - (struct mpt2_ioctl_command32) * * MPT2COMMAND32 - Handle 32bit applications running on 64bit os. */ static long _ctl_compat_mpt_command(struct MPT2SAS_ADAPTER *ioc, unsigned cmd, void __user *arg) { struct mpt2_ioctl_command32 karg32; struct mpt2_ioctl_command32 __user *uarg; struct mpt2_ioctl_command karg; if (_IOC_SIZE(cmd) != sizeof(struct mpt2_ioctl_command32)) return -EINVAL; uarg = (struct mpt2_ioctl_command32 __user *) arg; if (copy_from_user(&karg32, (char __user *)arg, sizeof(karg32))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } memset(&karg, 0, sizeof(struct mpt2_ioctl_command)); karg.hdr.ioc_number = karg32.hdr.ioc_number; karg.hdr.port_number = karg32.hdr.port_number; karg.hdr.max_data_size = karg32.hdr.max_data_size; karg.timeout = karg32.timeout; karg.max_reply_bytes = karg32.max_reply_bytes; karg.data_in_size = karg32.data_in_size; karg.data_out_size = karg32.data_out_size; karg.max_sense_bytes = karg32.max_sense_bytes; karg.data_sge_offset = karg32.data_sge_offset; karg.reply_frame_buf_ptr = compat_ptr(karg32.reply_frame_buf_ptr); karg.data_in_buf_ptr = compat_ptr(karg32.data_in_buf_ptr); karg.data_out_buf_ptr = compat_ptr(karg32.data_out_buf_ptr); karg.sense_data_ptr = compat_ptr(karg32.sense_data_ptr); return _ctl_do_mpt_command(ioc, karg, &uarg->mf); } #endif /** * _ctl_ioctl_main - main ioctl entry point * @file - (struct file) * @cmd - ioctl opcode * @arg - * compat - handles 32 bit applications in 64bit os */ static long _ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg, u8 compat) { struct MPT2SAS_ADAPTER *ioc; struct mpt2_ioctl_header ioctl_header; enum block_state state; long ret = -EINVAL; /* get IOCTL header */ if (copy_from_user(&ioctl_header, (char __user *)arg, sizeof(struct mpt2_ioctl_header))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); return -EFAULT; } if (_ctl_verify_adapter(ioctl_header.ioc_number, &ioc) == -1 || !ioc) return -ENODEV; if (ioc->shost_recovery || ioc->pci_error_recovery || ioc->is_driver_loading) return -EAGAIN; state = (file->f_flags & O_NONBLOCK) ? NON_BLOCKING : BLOCKING; if (state == NON_BLOCKING) { if (!mutex_trylock(&ioc->ctl_cmds.mutex)) return -EAGAIN; } else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex)) { return -ERESTARTSYS; } switch (cmd) { case MPT2IOCINFO: if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_iocinfo)) ret = _ctl_getiocinfo(ioc, arg); break; #ifdef CONFIG_COMPAT case MPT2COMMAND32: #endif case MPT2COMMAND: { struct mpt2_ioctl_command __user *uarg; struct mpt2_ioctl_command karg; #ifdef CONFIG_COMPAT if (compat) { ret = _ctl_compat_mpt_command(ioc, cmd, arg); break; } #endif if (copy_from_user(&karg, arg, sizeof(karg))) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); ret = -EFAULT; break; } if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_command)) { uarg = arg; ret = _ctl_do_mpt_command(ioc, karg, &uarg->mf); } break; } case MPT2EVENTQUERY: if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_eventquery)) ret = _ctl_eventquery(ioc, arg); break; case MPT2EVENTENABLE: if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_eventenable)) ret = _ctl_eventenable(ioc, arg); break; case MPT2EVENTREPORT: ret = _ctl_eventreport(ioc, arg); break; case MPT2HARDRESET: if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_diag_reset)) ret = _ctl_do_reset(ioc, arg); break; case MPT2BTDHMAPPING: if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_btdh_mapping)) ret = _ctl_btdh_mapping(ioc, arg); break; case MPT2DIAGREGISTER: if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_register)) ret = _ctl_diag_register(ioc, arg); break; case MPT2DIAGUNREGISTER: if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_unregister)) ret = _ctl_diag_unregister(ioc, arg); break; case MPT2DIAGQUERY: if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_query)) ret = _ctl_diag_query(ioc, arg); break; case MPT2DIAGRELEASE: if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_release)) ret = _ctl_diag_release(ioc, arg); break; case MPT2DIAGREADBUFFER: if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_read_buffer)) ret = _ctl_diag_read_buffer(ioc, arg); break; default: dctlprintk(ioc, printk(MPT2SAS_INFO_FMT "unsupported ioctl opcode(0x%08x)\n", ioc->name, cmd)); break; } mutex_unlock(&ioc->ctl_cmds.mutex); return ret; } /** * _ctl_ioctl - main ioctl entry point (unlocked) * @file - (struct file) * @cmd - ioctl opcode * @arg - */ static long _ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { long ret; ret = _ctl_ioctl_main(file, cmd, (void __user *)arg, 0); return ret; } #ifdef CONFIG_COMPAT /** * _ctl_ioctl_compat - main ioctl entry point (compat) * @file - * @cmd - * @arg - * * This routine handles 32 bit applications in 64bit os. */ static long _ctl_ioctl_compat(struct file *file, unsigned cmd, unsigned long arg) { long ret; ret = _ctl_ioctl_main(file, cmd, (void __user *)arg, 1); return ret; } #endif /* scsi host attributes */ /** * _ctl_version_fw_show - firmware version * @cdev - pointer to embedded class device * @buf - the buffer returned * * A sysfs 'read-only' shost attribute. */ static ssize_t _ctl_version_fw_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); return snprintf(buf, PAGE_SIZE, "%02d.%02d.%02d.%02d\n", (ioc->facts.FWVersion.Word & 0xFF000000) >> 24, (ioc->facts.FWVersion.Word & 0x00FF0000) >> 16, (ioc->facts.FWVersion.Word & 0x0000FF00) >> 8, ioc->facts.FWVersion.Word & 0x000000FF); } static DEVICE_ATTR(version_fw, S_IRUGO, _ctl_version_fw_show, NULL); /** * _ctl_version_bios_show - bios version * @cdev - pointer to embedded class device * @buf - the buffer returned * * A sysfs 'read-only' shost attribute. */ static ssize_t _ctl_version_bios_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); u32 version = le32_to_cpu(ioc->bios_pg3.BiosVersion); return snprintf(buf, PAGE_SIZE, "%02d.%02d.%02d.%02d\n", (version & 0xFF000000) >> 24, (version & 0x00FF0000) >> 16, (version & 0x0000FF00) >> 8, version & 0x000000FF); } static DEVICE_ATTR(version_bios, S_IRUGO, _ctl_version_bios_show, NULL); /** * _ctl_version_mpi_show - MPI (message passing interface) version * @cdev - pointer to embedded class device * @buf - the buffer returned * * A sysfs 'read-only' shost attribute. */ static ssize_t _ctl_version_mpi_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); return snprintf(buf, PAGE_SIZE, "%03x.%02x\n", ioc->facts.MsgVersion, ioc->facts.HeaderVersion >> 8); } static DEVICE_ATTR(version_mpi, S_IRUGO, _ctl_version_mpi_show, NULL); /** * _ctl_version_product_show - product name * @cdev - pointer to embedded class device * @buf - the buffer returned * * A sysfs 'read-only' shost attribute. */ static ssize_t _ctl_version_product_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); return snprintf(buf, 16, "%s\n", ioc->manu_pg0.ChipName); } static DEVICE_ATTR(version_product, S_IRUGO, _ctl_version_product_show, NULL); /** * _ctl_version_nvdata_persistent_show - ndvata persistent version * @cdev - pointer to embedded class device * @buf - the buffer returned * * A sysfs 'read-only' shost attribute. */ static ssize_t _ctl_version_nvdata_persistent_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); return snprintf(buf, PAGE_SIZE, "%08xh\n", le32_to_cpu(ioc->iounit_pg0.NvdataVersionPersistent.Word)); } static DEVICE_ATTR(version_nvdata_persistent, S_IRUGO, _ctl_version_nvdata_persistent_show, NULL); /** * _ctl_version_nvdata_default_show - nvdata default version * @cdev - pointer to embedded class device * @buf - the buffer returned * * A sysfs 'read-only' shost attribute. */ static ssize_t _ctl_version_nvdata_default_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); return snprintf(buf, PAGE_SIZE, "%08xh\n", le32_to_cpu(ioc->iounit_pg0.NvdataVersionDefault.Word)); } static DEVICE_ATTR(version_nvdata_default, S_IRUGO, _ctl_version_nvdata_default_show, NULL); /** * _ctl_board_name_show - board name * @cdev - pointer to embedded class device * @buf - the buffer returned * * A sysfs 'read-only' shost attribute. */ static ssize_t _ctl_board_name_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardName); } static DEVICE_ATTR(board_name, S_IRUGO, _ctl_board_name_show, NULL); /** * _ctl_board_assembly_show - board assembly name * @cdev - pointer to embedded class device * @buf - the buffer returned * * A sysfs 'read-only' shost attribute. */ static ssize_t _ctl_board_assembly_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardAssembly); } static DEVICE_ATTR(board_assembly, S_IRUGO, _ctl_board_assembly_show, NULL); /** * _ctl_board_tracer_show - board tracer number * @cdev - pointer to embedded class device * @buf - the buffer returned * * A sysfs 'read-only' shost attribute. */ static ssize_t _ctl_board_tracer_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardTracerNumber); } static DEVICE_ATTR(board_tracer, S_IRUGO, _ctl_board_tracer_show, NULL); /** * _ctl_io_delay_show - io missing delay * @cdev - pointer to embedded class device * @buf - the buffer returned * * This is for firmware implemention for deboucing device * removal events. * * A sysfs 'read-only' shost attribute. */ static ssize_t _ctl_io_delay_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->io_missing_delay); } static DEVICE_ATTR(io_delay, S_IRUGO, _ctl_io_delay_show, NULL); /** * _ctl_device_delay_show - device missing delay * @cdev - pointer to embedded class device * @buf - the buffer returned * * This is for firmware implemention for deboucing device * removal events. * * A sysfs 'read-only' shost attribute. */ static ssize_t _ctl_device_delay_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->device_missing_delay); } static DEVICE_ATTR(device_delay, S_IRUGO, _ctl_device_delay_show, NULL); /** * _ctl_fw_queue_depth_show - global credits * @cdev - pointer to embedded class device * @buf - the buffer returned * * This is firmware queue depth limit * * A sysfs 'read-only' shost attribute. */ static ssize_t _ctl_fw_queue_depth_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->facts.RequestCredit); } static DEVICE_ATTR(fw_queue_depth, S_IRUGO, _ctl_fw_queue_depth_show, NULL); /** * _ctl_sas_address_show - sas address * @cdev - pointer to embedded class device * @buf - the buffer returned * * This is the controller sas address * * A sysfs 'read-only' shost attribute. */ static ssize_t _ctl_host_sas_address_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); return snprintf(buf, PAGE_SIZE, "0x%016llx\n", (unsigned long long)ioc->sas_hba.sas_address); } static DEVICE_ATTR(host_sas_address, S_IRUGO, _ctl_host_sas_address_show, NULL); /** * _ctl_logging_level_show - logging level * @cdev - pointer to embedded class device * @buf - the buffer returned * * A sysfs 'read/write' shost attribute. */ static ssize_t _ctl_logging_level_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); return snprintf(buf, PAGE_SIZE, "%08xh\n", ioc->logging_level); } static ssize_t _ctl_logging_level_store(struct device *cdev, struct device_attribute *attr, const char *buf, size_t count) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); int val = 0; if (sscanf(buf, "%x", &val) != 1) return -EINVAL; ioc->logging_level = val; printk(MPT2SAS_INFO_FMT "logging_level=%08xh\n", ioc->name, ioc->logging_level); return strlen(buf); } static DEVICE_ATTR(logging_level, S_IRUGO | S_IWUSR, _ctl_logging_level_show, _ctl_logging_level_store); /* device attributes */ /* * _ctl_fwfault_debug_show - show/store fwfault_debug * @cdev - pointer to embedded class device * @buf - the buffer returned * * mpt2sas_fwfault_debug is command line option * A sysfs 'read/write' shost attribute. */ static ssize_t _ctl_fwfault_debug_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); return snprintf(buf, PAGE_SIZE, "%d\n", ioc->fwfault_debug); } static ssize_t _ctl_fwfault_debug_store(struct device *cdev, struct device_attribute *attr, const char *buf, size_t count) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); int val = 0; if (sscanf(buf, "%d", &val) != 1) return -EINVAL; ioc->fwfault_debug = val; printk(MPT2SAS_INFO_FMT "fwfault_debug=%d\n", ioc->name, ioc->fwfault_debug); return strlen(buf); } static DEVICE_ATTR(fwfault_debug, S_IRUGO | S_IWUSR, _ctl_fwfault_debug_show, _ctl_fwfault_debug_store); /** * _ctl_ioc_reset_count_show - ioc reset count * @cdev - pointer to embedded class device * @buf - the buffer returned * * This is firmware queue depth limit * * A sysfs 'read-only' shost attribute. */ static ssize_t _ctl_ioc_reset_count_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); return snprintf(buf, PAGE_SIZE, "%08d\n", ioc->ioc_reset_count); } static DEVICE_ATTR(ioc_reset_count, S_IRUGO, _ctl_ioc_reset_count_show, NULL); /** * _ctl_ioc_reply_queue_count_show - number of reply queues * @cdev - pointer to embedded class device * @buf - the buffer returned * * This is number of reply queues * * A sysfs 'read-only' shost attribute. */ static ssize_t _ctl_ioc_reply_queue_count_show(struct device *cdev, struct device_attribute *attr, char *buf) { u8 reply_queue_count; struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); if ((ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_MSI_X_INDEX) && ioc->msix_enable) reply_queue_count = ioc->reply_queue_count; else reply_queue_count = 1; return snprintf(buf, PAGE_SIZE, "%d\n", reply_queue_count); } static DEVICE_ATTR(reply_queue_count, S_IRUGO, _ctl_ioc_reply_queue_count_show, NULL); /** * _ctl_BRM_status_show - Backup Rail Monitor Status * @cdev - pointer to embedded class device * @buf - the buffer returned * * This is number of reply queues * * A sysfs 'read-only' shost attribute. */ static ssize_t _ctl_BRM_status_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); Mpi2IOUnitPage3_t *io_unit_pg3 = NULL; Mpi2ConfigReply_t mpi_reply; u16 backup_rail_monitor_status = 0; u16 ioc_status; int sz; ssize_t rc = 0; if (!ioc->is_warpdrive) { printk(MPT2SAS_ERR_FMT "%s: BRM attribute is only for"\ "warpdrive\n", ioc->name, __func__); goto out; } /* allocate upto GPIOVal 36 entries */ sz = offsetof(Mpi2IOUnitPage3_t, GPIOVal) + (sizeof(u16) * 36); io_unit_pg3 = kzalloc(sz, GFP_KERNEL); if (!io_unit_pg3) { printk(MPT2SAS_ERR_FMT "%s: failed allocating memory"\ "for iounit_pg3: (%d) bytes\n", ioc->name, __func__, sz); goto out; } if (mpt2sas_config_get_iounit_pg3(ioc, &mpi_reply, io_unit_pg3, sz) != 0) { printk(MPT2SAS_ERR_FMT "%s: failed reading iounit_pg3\n", ioc->name, __func__); goto out; } ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { printk(MPT2SAS_ERR_FMT "%s: iounit_pg3 failed with"\ "ioc_status(0x%04x)\n", ioc->name, __func__, ioc_status); goto out; } if (io_unit_pg3->GPIOCount < 25) { printk(MPT2SAS_ERR_FMT "%s: iounit_pg3->GPIOCount less than"\ "25 entries, detected (%d) entries\n", ioc->name, __func__, io_unit_pg3->GPIOCount); goto out; } /* BRM status is in bit zero of GPIOVal[24] */ backup_rail_monitor_status = le16_to_cpu(io_unit_pg3->GPIOVal[24]); rc = snprintf(buf, PAGE_SIZE, "%d\n", (backup_rail_monitor_status & 1)); out: kfree(io_unit_pg3); return rc; } static DEVICE_ATTR(BRM_status, S_IRUGO, _ctl_BRM_status_show, NULL); struct DIAG_BUFFER_START { __le32 Size; __le32 DiagVersion; u8 BufferType; u8 Reserved[3]; __le32 Reserved1; __le32 Reserved2; __le32 Reserved3; }; /** * _ctl_host_trace_buffer_size_show - host buffer size (trace only) * @cdev - pointer to embedded class device * @buf - the buffer returned * * A sysfs 'read-only' shost attribute. */ static ssize_t _ctl_host_trace_buffer_size_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); u32 size = 0; struct DIAG_BUFFER_START *request_data; if (!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) { printk(MPT2SAS_ERR_FMT "%s: host_trace_buffer is not " "registered\n", ioc->name, __func__); return 0; } if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) { printk(MPT2SAS_ERR_FMT "%s: host_trace_buffer is not " "registered\n", ioc->name, __func__); return 0; } request_data = (struct DIAG_BUFFER_START *) ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]; if ((le32_to_cpu(request_data->DiagVersion) == 0x00000000 || le32_to_cpu(request_data->DiagVersion) == 0x01000000) && le32_to_cpu(request_data->Reserved3) == 0x4742444c) size = le32_to_cpu(request_data->Size); ioc->ring_buffer_sz = size; return snprintf(buf, PAGE_SIZE, "%d\n", size); } static DEVICE_ATTR(host_trace_buffer_size, S_IRUGO, _ctl_host_trace_buffer_size_show, NULL); /** * _ctl_host_trace_buffer_show - firmware ring buffer (trace only) * @cdev - pointer to embedded class device * @buf - the buffer returned * * A sysfs 'read/write' shost attribute. * * You will only be able to read 4k bytes of ring buffer at a time. * In order to read beyond 4k bytes, you will have to write out the * offset to the same attribute, it will move the pointer. */ static ssize_t _ctl_host_trace_buffer_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); void *request_data; u32 size; if (!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) { printk(MPT2SAS_ERR_FMT "%s: host_trace_buffer is not " "registered\n", ioc->name, __func__); return 0; } if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) { printk(MPT2SAS_ERR_FMT "%s: host_trace_buffer is not " "registered\n", ioc->name, __func__); return 0; } if (ioc->ring_buffer_offset > ioc->ring_buffer_sz) return 0; size = ioc->ring_buffer_sz - ioc->ring_buffer_offset; size = (size > PAGE_SIZE) ? PAGE_SIZE : size; request_data = ioc->diag_buffer[0] + ioc->ring_buffer_offset; memcpy(buf, request_data, size); return size; } static ssize_t _ctl_host_trace_buffer_store(struct device *cdev, struct device_attribute *attr, const char *buf, size_t count) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); int val = 0; if (sscanf(buf, "%d", &val) != 1) return -EINVAL; ioc->ring_buffer_offset = val; return strlen(buf); } static DEVICE_ATTR(host_trace_buffer, S_IRUGO | S_IWUSR, _ctl_host_trace_buffer_show, _ctl_host_trace_buffer_store); /*****************************************/ /** * _ctl_host_trace_buffer_enable_show - firmware ring buffer (trace only) * @cdev - pointer to embedded class device * @buf - the buffer returned * * A sysfs 'read/write' shost attribute. * * This is a mechnism to post/release host_trace_buffers */ static ssize_t _ctl_host_trace_buffer_enable_show(struct device *cdev, struct device_attribute *attr, char *buf) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); if ((!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) || ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & MPT2_DIAG_BUFFER_IS_REGISTERED) == 0)) return snprintf(buf, PAGE_SIZE, "off\n"); else if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & MPT2_DIAG_BUFFER_IS_RELEASED)) return snprintf(buf, PAGE_SIZE, "release\n"); else return snprintf(buf, PAGE_SIZE, "post\n"); } static ssize_t _ctl_host_trace_buffer_enable_store(struct device *cdev, struct device_attribute *attr, const char *buf, size_t count) { struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); char str[10] = ""; struct mpt2_diag_register diag_register; u8 issue_reset = 0; if (sscanf(buf, "%9s", str) != 1) return -EINVAL; if (!strcmp(str, "post")) { /* exit out if host buffers are already posted */ if ((ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) && (ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & MPT2_DIAG_BUFFER_IS_REGISTERED) && ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & MPT2_DIAG_BUFFER_IS_RELEASED) == 0)) goto out; memset(&diag_register, 0, sizeof(struct mpt2_diag_register)); printk(MPT2SAS_INFO_FMT "posting host trace buffers\n", ioc->name); diag_register.buffer_type = MPI2_DIAG_BUF_TYPE_TRACE; diag_register.requested_buffer_size = (1024 * 1024); diag_register.unique_id = 0x7075900; ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] = 0; _ctl_diag_register_2(ioc, &diag_register); } else if (!strcmp(str, "release")) { /* exit out if host buffers are already released */ if (!ioc->diag_buffer[MPI2_DIAG_BUF_TYPE_TRACE]) goto out; if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) goto out; if ((ioc->diag_buffer_status[MPI2_DIAG_BUF_TYPE_TRACE] & MPT2_DIAG_BUFFER_IS_RELEASED)) goto out; printk(MPT2SAS_INFO_FMT "releasing host trace buffer\n", ioc->name); _ctl_send_release(ioc, MPI2_DIAG_BUF_TYPE_TRACE, &issue_reset); } out: return strlen(buf); } static DEVICE_ATTR(host_trace_buffer_enable, S_IRUGO | S_IWUSR, _ctl_host_trace_buffer_enable_show, _ctl_host_trace_buffer_enable_store); struct device_attribute *mpt2sas_host_attrs[] = { &dev_attr_version_fw, &dev_attr_version_bios, &dev_attr_version_mpi, &dev_attr_version_product, &dev_attr_version_nvdata_persistent, &dev_attr_version_nvdata_default, &dev_attr_board_name, &dev_attr_board_assembly, &dev_attr_board_tracer, &dev_attr_io_delay, &dev_attr_device_delay, &dev_attr_logging_level, &dev_attr_fwfault_debug, &dev_attr_fw_queue_depth, &dev_attr_host_sas_address, &dev_attr_ioc_reset_count, &dev_attr_host_trace_buffer_size, &dev_attr_host_trace_buffer, &dev_attr_host_trace_buffer_enable, &dev_attr_reply_queue_count, &dev_attr_BRM_status, NULL, }; /** * _ctl_device_sas_address_show - sas address * @cdev - pointer to embedded class device * @buf - the buffer returned * * This is the sas address for the target * * A sysfs 'read-only' shost attribute. */ static ssize_t _ctl_device_sas_address_show(struct device *dev, struct device_attribute *attr, char *buf) { struct scsi_device *sdev = to_scsi_device(dev); struct MPT2SAS_DEVICE *sas_device_priv_data = sdev->hostdata; return snprintf(buf, PAGE_SIZE, "0x%016llx\n", (unsigned long long)sas_device_priv_data->sas_target->sas_address); } static DEVICE_ATTR(sas_address, S_IRUGO, _ctl_device_sas_address_show, NULL); /** * _ctl_device_handle_show - device handle * @cdev - pointer to embedded class device * @buf - the buffer returned * * This is the firmware assigned device handle * * A sysfs 'read-only' shost attribute. */ static ssize_t _ctl_device_handle_show(struct device *dev, struct device_attribute *attr, char *buf) { struct scsi_device *sdev = to_scsi_device(dev); struct MPT2SAS_DEVICE *sas_device_priv_data = sdev->hostdata; return snprintf(buf, PAGE_SIZE, "0x%04x\n", sas_device_priv_data->sas_target->handle); } static DEVICE_ATTR(sas_device_handle, S_IRUGO, _ctl_device_handle_show, NULL); struct device_attribute *mpt2sas_dev_attrs[] = { &dev_attr_sas_address, &dev_attr_sas_device_handle, NULL, }; static const struct file_operations ctl_fops = { .owner = THIS_MODULE, .unlocked_ioctl = _ctl_ioctl, .release = _ctl_release, .poll = _ctl_poll, .fasync = _ctl_fasync, #ifdef CONFIG_COMPAT .compat_ioctl = _ctl_ioctl_compat, #endif .llseek = noop_llseek, }; static struct miscdevice ctl_dev = { .minor = MPT2SAS_MINOR, .name = MPT2SAS_DEV_NAME, .fops = &ctl_fops, }; /** * mpt2sas_ctl_init - main entry point for ctl. * */ void mpt2sas_ctl_init(void) { async_queue = NULL; if (misc_register(&ctl_dev) < 0) printk(KERN_ERR "%s can't register misc device [minor=%d]\n", MPT2SAS_DRIVER_NAME, MPT2SAS_MINOR); init_waitqueue_head(&ctl_poll_wait); } /** * mpt2sas_ctl_exit - exit point for ctl * */ void mpt2sas_ctl_exit(void) { struct MPT2SAS_ADAPTER *ioc; int i; list_for_each_entry(ioc, &mpt2sas_ioc_list, list) { /* free memory associated to diag buffers */ for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) { if (!ioc->diag_buffer[i]) continue; pci_free_consistent(ioc->pdev, ioc->diag_buffer_sz[i], ioc->diag_buffer[i], ioc->diag_buffer_dma[i]); ioc->diag_buffer[i] = NULL; ioc->diag_buffer_status[i] = 0; } kfree(ioc->event_log); } misc_deregister(&ctl_dev); }