Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Disk Array driver for Compaq SMART2 Controllers |
| 3 | * Copyright 1998 Compaq Computer Corporation |
| 4 | * |
| 5 | * This program is free software; you can redistribute it and/or modify |
| 6 | * it under the terms of the GNU General Public License as published by |
| 7 | * the Free Software Foundation; either version 2 of the License, or |
| 8 | * (at your option) any later version. |
| 9 | * |
| 10 | * This program is distributed in the hope that it will be useful, |
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or |
| 13 | * NON INFRINGEMENT. See the GNU General Public License for more details. |
| 14 | * |
| 15 | * You should have received a copy of the GNU General Public License |
| 16 | * along with this program; if not, write to the Free Software |
| 17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| 18 | * |
| 19 | * Questions/Comments/Bugfixes to iss_storagedev@hp.com |
| 20 | * |
| 21 | * If you want to make changes, improve or add functionality to this |
| 22 | * driver, you'll probably need the Compaq Array Controller Interface |
| 23 | * Specificiation (Document number ECG086/1198) |
| 24 | */ |
| 25 | |
| 26 | /* |
| 27 | * This file contains the controller communication implementation for |
| 28 | * Compaq SMART-1 and SMART-2 controllers. To the best of my knowledge, |
| 29 | * this should support: |
| 30 | * |
| 31 | * PCI: |
| 32 | * SMART-2/P, SMART-2DH, SMART-2SL, SMART-221, SMART-3100ES, SMART-3200 |
| 33 | * Integerated SMART Array Controller, SMART-4200, SMART-4250ES |
| 34 | * |
| 35 | * EISA: |
| 36 | * SMART-2/E, SMART, IAES, IDA-2, IDA |
| 37 | */ |
| 38 | |
| 39 | /* |
| 40 | * Memory mapped FIFO interface (SMART 42xx cards) |
| 41 | */ |
| 42 | static void smart4_submit_command(ctlr_info_t *h, cmdlist_t *c) |
| 43 | { |
| 44 | writel(c->busaddr, h->vaddr + S42XX_REQUEST_PORT_OFFSET); |
| 45 | } |
| 46 | |
| 47 | /* |
| 48 | * This card is the opposite of the other cards. |
| 49 | * 0 turns interrupts on... |
| 50 | * 0x08 turns them off... |
| 51 | */ |
| 52 | static void smart4_intr_mask(ctlr_info_t *h, unsigned long val) |
| 53 | { |
| 54 | if (val) |
| 55 | { /* Turn interrupts on */ |
| 56 | writel(0, h->vaddr + S42XX_REPLY_INTR_MASK_OFFSET); |
| 57 | } else /* Turn them off */ |
| 58 | { |
| 59 | writel( S42XX_INTR_OFF, |
| 60 | h->vaddr + S42XX_REPLY_INTR_MASK_OFFSET); |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | /* |
| 65 | * For older cards FIFO Full = 0. |
| 66 | * On this card 0 means there is room, anything else FIFO Full. |
| 67 | * |
| 68 | */ |
| 69 | static unsigned long smart4_fifo_full(ctlr_info_t *h) |
| 70 | { |
| 71 | |
| 72 | return (!readl(h->vaddr + S42XX_REQUEST_PORT_OFFSET)); |
| 73 | } |
| 74 | |
| 75 | /* This type of controller returns -1 if the fifo is empty, |
| 76 | * Not 0 like the others. |
| 77 | * And we need to let it know we read a value out |
| 78 | */ |
| 79 | static unsigned long smart4_completed(ctlr_info_t *h) |
| 80 | { |
| 81 | long register_value |
| 82 | = readl(h->vaddr + S42XX_REPLY_PORT_OFFSET); |
| 83 | |
| 84 | /* Fifo is empty */ |
| 85 | if( register_value == 0xffffffff) |
| 86 | return 0; |
| 87 | |
| 88 | /* Need to let it know we got the reply */ |
| 89 | /* We do this by writing a 0 to the port we just read from */ |
| 90 | writel(0, h->vaddr + S42XX_REPLY_PORT_OFFSET); |
| 91 | |
| 92 | return ((unsigned long) register_value); |
| 93 | } |
| 94 | |
| 95 | /* |
| 96 | * This hardware returns interrupt pending at a different place and |
| 97 | * it does not tell us if the fifo is empty, we will have check |
| 98 | * that by getting a 0 back from the comamnd_completed call. |
| 99 | */ |
| 100 | static unsigned long smart4_intr_pending(ctlr_info_t *h) |
| 101 | { |
| 102 | unsigned long register_value = |
| 103 | readl(h->vaddr + S42XX_INTR_STATUS); |
| 104 | |
| 105 | if( register_value & S42XX_INTR_PENDING) |
| 106 | return FIFO_NOT_EMPTY; |
| 107 | return 0 ; |
| 108 | } |
| 109 | |
| 110 | static struct access_method smart4_access = { |
| 111 | smart4_submit_command, |
| 112 | smart4_intr_mask, |
| 113 | smart4_fifo_full, |
| 114 | smart4_intr_pending, |
| 115 | smart4_completed, |
| 116 | }; |
| 117 | |
| 118 | /* |
| 119 | * Memory mapped FIFO interface (PCI SMART2 and SMART 3xxx cards) |
| 120 | */ |
| 121 | static void smart2_submit_command(ctlr_info_t *h, cmdlist_t *c) |
| 122 | { |
| 123 | writel(c->busaddr, h->vaddr + COMMAND_FIFO); |
| 124 | } |
| 125 | |
| 126 | static void smart2_intr_mask(ctlr_info_t *h, unsigned long val) |
| 127 | { |
| 128 | writel(val, h->vaddr + INTR_MASK); |
| 129 | } |
| 130 | |
| 131 | static unsigned long smart2_fifo_full(ctlr_info_t *h) |
| 132 | { |
| 133 | return readl(h->vaddr + COMMAND_FIFO); |
| 134 | } |
| 135 | |
| 136 | static unsigned long smart2_completed(ctlr_info_t *h) |
| 137 | { |
| 138 | return readl(h->vaddr + COMMAND_COMPLETE_FIFO); |
| 139 | } |
| 140 | |
| 141 | static unsigned long smart2_intr_pending(ctlr_info_t *h) |
| 142 | { |
| 143 | return readl(h->vaddr + INTR_PENDING); |
| 144 | } |
| 145 | |
| 146 | static struct access_method smart2_access = { |
| 147 | smart2_submit_command, |
| 148 | smart2_intr_mask, |
| 149 | smart2_fifo_full, |
| 150 | smart2_intr_pending, |
| 151 | smart2_completed, |
| 152 | }; |
| 153 | |
| 154 | /* |
| 155 | * IO access for SMART-2/E cards |
| 156 | */ |
| 157 | static void smart2e_submit_command(ctlr_info_t *h, cmdlist_t *c) |
| 158 | { |
| 159 | outl(c->busaddr, h->io_mem_addr + COMMAND_FIFO); |
| 160 | } |
| 161 | |
| 162 | static void smart2e_intr_mask(ctlr_info_t *h, unsigned long val) |
| 163 | { |
| 164 | outl(val, h->io_mem_addr + INTR_MASK); |
| 165 | } |
| 166 | |
| 167 | static unsigned long smart2e_fifo_full(ctlr_info_t *h) |
| 168 | { |
| 169 | return inl(h->io_mem_addr + COMMAND_FIFO); |
| 170 | } |
| 171 | |
| 172 | static unsigned long smart2e_completed(ctlr_info_t *h) |
| 173 | { |
| 174 | return inl(h->io_mem_addr + COMMAND_COMPLETE_FIFO); |
| 175 | } |
| 176 | |
| 177 | static unsigned long smart2e_intr_pending(ctlr_info_t *h) |
| 178 | { |
| 179 | return inl(h->io_mem_addr + INTR_PENDING); |
| 180 | } |
| 181 | |
| 182 | static struct access_method smart2e_access = { |
| 183 | smart2e_submit_command, |
| 184 | smart2e_intr_mask, |
| 185 | smart2e_fifo_full, |
| 186 | smart2e_intr_pending, |
| 187 | smart2e_completed, |
| 188 | }; |
| 189 | |
| 190 | /* |
| 191 | * IO access for older SMART-1 type cards |
| 192 | */ |
| 193 | #define SMART1_SYSTEM_MASK 0xC8E |
| 194 | #define SMART1_SYSTEM_DOORBELL 0xC8F |
| 195 | #define SMART1_LOCAL_MASK 0xC8C |
| 196 | #define SMART1_LOCAL_DOORBELL 0xC8D |
| 197 | #define SMART1_INTR_MASK 0xC89 |
| 198 | #define SMART1_LISTADDR 0xC90 |
| 199 | #define SMART1_LISTLEN 0xC94 |
| 200 | #define SMART1_TAG 0xC97 |
| 201 | #define SMART1_COMPLETE_ADDR 0xC98 |
| 202 | #define SMART1_LISTSTATUS 0xC9E |
| 203 | |
| 204 | #define CHANNEL_BUSY 0x01 |
| 205 | #define CHANNEL_CLEAR 0x02 |
| 206 | |
| 207 | static void smart1_submit_command(ctlr_info_t *h, cmdlist_t *c) |
| 208 | { |
| 209 | /* |
| 210 | * This __u16 is actually a bunch of control flags on SMART |
| 211 | * and below. We want them all to be zero. |
| 212 | */ |
| 213 | c->hdr.size = 0; |
| 214 | |
| 215 | outb(CHANNEL_CLEAR, h->io_mem_addr + SMART1_SYSTEM_DOORBELL); |
| 216 | |
| 217 | outl(c->busaddr, h->io_mem_addr + SMART1_LISTADDR); |
| 218 | outw(c->size, h->io_mem_addr + SMART1_LISTLEN); |
| 219 | |
| 220 | outb(CHANNEL_BUSY, h->io_mem_addr + SMART1_LOCAL_DOORBELL); |
| 221 | } |
| 222 | |
| 223 | static void smart1_intr_mask(ctlr_info_t *h, unsigned long val) |
| 224 | { |
| 225 | if (val == 1) { |
| 226 | outb(0xFD, h->io_mem_addr + SMART1_SYSTEM_DOORBELL); |
| 227 | outb(CHANNEL_BUSY, h->io_mem_addr + SMART1_LOCAL_DOORBELL); |
| 228 | outb(0x01, h->io_mem_addr + SMART1_INTR_MASK); |
| 229 | outb(0x01, h->io_mem_addr + SMART1_SYSTEM_MASK); |
| 230 | } else { |
| 231 | outb(0, h->io_mem_addr + 0xC8E); |
| 232 | } |
| 233 | } |
| 234 | |
| 235 | static unsigned long smart1_fifo_full(ctlr_info_t *h) |
| 236 | { |
| 237 | unsigned char chan; |
| 238 | chan = inb(h->io_mem_addr + SMART1_SYSTEM_DOORBELL) & CHANNEL_CLEAR; |
| 239 | return chan; |
| 240 | } |
| 241 | |
| 242 | static unsigned long smart1_completed(ctlr_info_t *h) |
| 243 | { |
| 244 | unsigned char status; |
| 245 | unsigned long cmd; |
| 246 | |
| 247 | if (inb(h->io_mem_addr + SMART1_SYSTEM_DOORBELL) & CHANNEL_BUSY) { |
| 248 | outb(CHANNEL_BUSY, h->io_mem_addr + SMART1_SYSTEM_DOORBELL); |
| 249 | |
| 250 | cmd = inl(h->io_mem_addr + SMART1_COMPLETE_ADDR); |
| 251 | status = inb(h->io_mem_addr + SMART1_LISTSTATUS); |
| 252 | |
| 253 | outb(CHANNEL_CLEAR, h->io_mem_addr + SMART1_LOCAL_DOORBELL); |
| 254 | |
| 255 | /* |
| 256 | * this is x86 (actually compaq x86) only, so it's ok |
| 257 | */ |
| 258 | if (cmd) ((cmdlist_t*)bus_to_virt(cmd))->req.hdr.rcode = status; |
| 259 | } else { |
| 260 | cmd = 0; |
| 261 | } |
| 262 | return cmd; |
| 263 | } |
| 264 | |
| 265 | static unsigned long smart1_intr_pending(ctlr_info_t *h) |
| 266 | { |
| 267 | unsigned char chan; |
| 268 | chan = inb(h->io_mem_addr + SMART1_SYSTEM_DOORBELL) & CHANNEL_BUSY; |
| 269 | return chan; |
| 270 | } |
| 271 | |
| 272 | static struct access_method smart1_access = { |
| 273 | smart1_submit_command, |
| 274 | smart1_intr_mask, |
| 275 | smart1_fifo_full, |
| 276 | smart1_intr_pending, |
| 277 | smart1_completed, |
| 278 | }; |