James Bottomley | 2908d77 | 2006-08-29 09:22:51 -0500 | [diff] [blame] | 1 | /* |
| 2 | * Aic94xx SAS/SATA driver hardware interface header file. |
| 3 | * |
| 4 | * Copyright (C) 2005 Adaptec, Inc. All rights reserved. |
| 5 | * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> |
| 6 | * |
| 7 | * This file is licensed under GPLv2. |
| 8 | * |
| 9 | * This file is part of the aic94xx driver. |
| 10 | * |
| 11 | * The aic94xx driver is free software; you can redistribute it and/or |
| 12 | * modify it under the terms of the GNU General Public License as |
| 13 | * published by the Free Software Foundation; version 2 of the |
| 14 | * License. |
| 15 | * |
| 16 | * The aic94xx driver is distributed in the hope that it will be useful, |
| 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 19 | * General Public License for more details. |
| 20 | * |
| 21 | * You should have received a copy of the GNU General Public License |
| 22 | * along with the aic94xx driver; if not, write to the Free Software |
| 23 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| 24 | * |
| 25 | */ |
| 26 | |
| 27 | #ifndef _AIC94XX_HWI_H_ |
| 28 | #define _AIC94XX_HWI_H_ |
| 29 | |
| 30 | #include <linux/interrupt.h> |
| 31 | #include <linux/pci.h> |
| 32 | #include <linux/dma-mapping.h> |
| 33 | |
| 34 | #include <scsi/libsas.h> |
| 35 | |
| 36 | #include "aic94xx.h" |
| 37 | #include "aic94xx_sas.h" |
| 38 | |
| 39 | /* Define ASD_MAX_PHYS to the maximum phys ever. Currently 8. */ |
| 40 | #define ASD_MAX_PHYS 8 |
| 41 | #define ASD_PCBA_SN_SIZE 12 |
| 42 | |
James Bottomley | 2908d77 | 2006-08-29 09:22:51 -0500 | [diff] [blame] | 43 | struct asd_ha_addrspace { |
| 44 | void __iomem *addr; |
| 45 | unsigned long start; /* pci resource start */ |
| 46 | unsigned long len; /* pci resource len */ |
| 47 | unsigned long flags; /* pci resource flags */ |
| 48 | |
| 49 | /* addresses internal to the host adapter */ |
| 50 | u32 swa_base; /* mmspace 1 (MBAR1) uses this only */ |
| 51 | u32 swb_base; |
| 52 | u32 swc_base; |
| 53 | }; |
| 54 | |
| 55 | struct bios_struct { |
| 56 | int present; |
| 57 | u8 maj; |
| 58 | u8 min; |
| 59 | u32 bld; |
| 60 | }; |
| 61 | |
| 62 | struct unit_element_struct { |
| 63 | u16 num; |
| 64 | u16 size; |
| 65 | void *area; |
| 66 | }; |
| 67 | |
| 68 | struct flash_struct { |
| 69 | u32 bar; |
| 70 | int present; |
| 71 | int wide; |
| 72 | u8 manuf; |
| 73 | u8 dev_id; |
| 74 | u8 sec_prot; |
Gilbert Wu | 1237c98 | 2007-10-22 15:19:11 -0700 | [diff] [blame] | 75 | u8 method; |
James Bottomley | 2908d77 | 2006-08-29 09:22:51 -0500 | [diff] [blame] | 76 | |
| 77 | u32 dir_offs; |
| 78 | }; |
| 79 | |
| 80 | struct asd_phy_desc { |
| 81 | /* From CTRL-A settings, then set to what is appropriate */ |
| 82 | u8 sas_addr[SAS_ADDR_SIZE]; |
| 83 | u8 max_sas_lrate; |
| 84 | u8 min_sas_lrate; |
| 85 | u8 max_sata_lrate; |
| 86 | u8 min_sata_lrate; |
| 87 | u8 flags; |
| 88 | #define ASD_CRC_DIS 1 |
| 89 | #define ASD_SATA_SPINUP_HOLD 2 |
| 90 | |
| 91 | u8 phy_control_0; /* mode 5 reg 0x160 */ |
| 92 | u8 phy_control_1; /* mode 5 reg 0x161 */ |
| 93 | u8 phy_control_2; /* mode 5 reg 0x162 */ |
| 94 | u8 phy_control_3; /* mode 5 reg 0x163 */ |
| 95 | }; |
| 96 | |
| 97 | struct asd_dma_tok { |
| 98 | void *vaddr; |
| 99 | dma_addr_t dma_handle; |
| 100 | size_t size; |
| 101 | }; |
| 102 | |
| 103 | struct hw_profile { |
| 104 | struct bios_struct bios; |
| 105 | struct unit_element_struct ue; |
| 106 | struct flash_struct flash; |
| 107 | |
| 108 | u8 sas_addr[SAS_ADDR_SIZE]; |
| 109 | char pcba_sn[ASD_PCBA_SN_SIZE+1]; |
| 110 | |
| 111 | u8 enabled_phys; /* mask of enabled phys */ |
| 112 | struct asd_phy_desc phy_desc[ASD_MAX_PHYS]; |
| 113 | u32 max_scbs; /* absolute sequencer scb queue size */ |
| 114 | struct asd_dma_tok *scb_ext; |
| 115 | u32 max_ddbs; |
| 116 | struct asd_dma_tok *ddb_ext; |
| 117 | |
| 118 | spinlock_t ddb_lock; |
| 119 | void *ddb_bitmap; |
| 120 | |
| 121 | int num_phys; /* ENABLEABLE */ |
| 122 | int max_phys; /* REPORTED + ENABLEABLE */ |
| 123 | |
| 124 | unsigned addr_range; /* max # of addrs; max # of possible ports */ |
| 125 | unsigned port_name_base; |
| 126 | unsigned dev_name_base; |
| 127 | unsigned sata_name_base; |
| 128 | }; |
| 129 | |
| 130 | struct asd_ascb { |
| 131 | struct list_head list; |
| 132 | struct asd_ha_struct *ha; |
| 133 | |
| 134 | struct scb *scb; /* equals dma_scb->vaddr */ |
| 135 | struct asd_dma_tok dma_scb; |
| 136 | struct asd_dma_tok *sg_arr; |
| 137 | |
| 138 | void (*tasklet_complete)(struct asd_ascb *, struct done_list_struct *); |
| 139 | u8 uldd_timer:1; |
| 140 | |
| 141 | /* internally generated command */ |
| 142 | struct timer_list timer; |
James Bottomley | e2396f1 | 2008-02-23 23:44:19 -0600 | [diff] [blame] | 143 | struct completion *completion; |
James Bottomley | 2908d77 | 2006-08-29 09:22:51 -0500 | [diff] [blame] | 144 | u8 tag_valid:1; |
| 145 | __be16 tag; /* error recovery only */ |
| 146 | |
| 147 | /* If this is an Empty SCB, index of first edb in seq->edb_arr. */ |
| 148 | int edb_index; |
| 149 | |
| 150 | /* Used by the timer timeout function. */ |
| 151 | int tc_index; |
| 152 | |
| 153 | void *uldd_task; |
| 154 | }; |
| 155 | |
| 156 | #define ASD_DL_SIZE_BITS 0x8 |
| 157 | #define ASD_DL_SIZE (1<<(2+ASD_DL_SIZE_BITS)) |
| 158 | #define ASD_DEF_DL_TOGGLE 0x01 |
| 159 | |
| 160 | struct asd_seq_data { |
| 161 | spinlock_t pend_q_lock; |
| 162 | u16 scbpro; |
| 163 | int pending; |
| 164 | struct list_head pend_q; |
| 165 | int can_queue; /* per adapter */ |
| 166 | struct asd_dma_tok next_scb; /* next scb to be delivered to CSEQ */ |
| 167 | |
| 168 | spinlock_t tc_index_lock; |
| 169 | void **tc_index_array; |
| 170 | void *tc_index_bitmap; |
| 171 | int tc_index_bitmap_bits; |
| 172 | |
| 173 | struct tasklet_struct dl_tasklet; |
| 174 | struct done_list_struct *dl; /* array of done list entries, equals */ |
| 175 | struct asd_dma_tok *actual_dl; /* actual_dl->vaddr */ |
| 176 | int dl_toggle; |
| 177 | int dl_next; |
| 178 | |
| 179 | int num_edbs; |
| 180 | struct asd_dma_tok **edb_arr; |
| 181 | int num_escbs; |
| 182 | struct asd_ascb **escb_arr; /* array of pointers to escbs */ |
| 183 | }; |
| 184 | |
malahal@us.ibm.com | 3f04810 | 2006-10-04 17:28:37 -0700 | [diff] [blame] | 185 | /* This is an internal port structure. These are used to get accurate |
| 186 | * phy_mask for updating DDB 0. |
| 187 | */ |
| 188 | struct asd_port { |
| 189 | u8 sas_addr[SAS_ADDR_SIZE]; |
| 190 | u8 attached_sas_addr[SAS_ADDR_SIZE]; |
| 191 | u32 phy_mask; |
| 192 | int num_phys; |
| 193 | }; |
| 194 | |
James Bottomley | 2908d77 | 2006-08-29 09:22:51 -0500 | [diff] [blame] | 195 | /* This is the Host Adapter structure. It describes the hardware |
| 196 | * SAS adapter. |
| 197 | */ |
| 198 | struct asd_ha_struct { |
| 199 | struct pci_dev *pcidev; |
| 200 | const char *name; |
| 201 | |
| 202 | struct sas_ha_struct sas_ha; |
| 203 | |
| 204 | u8 revision_id; |
| 205 | |
| 206 | int iospace; |
| 207 | spinlock_t iolock; |
| 208 | struct asd_ha_addrspace io_handle[2]; |
| 209 | |
| 210 | struct hw_profile hw_prof; |
| 211 | |
| 212 | struct asd_phy phys[ASD_MAX_PHYS]; |
malahal@us.ibm.com | 3f04810 | 2006-10-04 17:28:37 -0700 | [diff] [blame] | 213 | spinlock_t asd_ports_lock; |
| 214 | struct asd_port asd_ports[ASD_MAX_PHYS]; |
James Bottomley | 2908d77 | 2006-08-29 09:22:51 -0500 | [diff] [blame] | 215 | struct asd_sas_port ports[ASD_MAX_PHYS]; |
| 216 | |
| 217 | struct dma_pool *scb_pool; |
| 218 | |
| 219 | struct asd_seq_data seq; /* sequencer related */ |
Gilbert Wu | 1237c98 | 2007-10-22 15:19:11 -0700 | [diff] [blame] | 220 | u32 bios_status; |
| 221 | const struct firmware *bios_image; |
James Bottomley | 2908d77 | 2006-08-29 09:22:51 -0500 | [diff] [blame] | 222 | }; |
| 223 | |
| 224 | /* ---------- Common macros ---------- */ |
| 225 | |
| 226 | #define ASD_BUSADDR_LO(__dma_handle) ((u32)(__dma_handle)) |
| 227 | #define ASD_BUSADDR_HI(__dma_handle) (((sizeof(dma_addr_t))==8) \ |
| 228 | ? ((u32)((__dma_handle) >> 32)) \ |
| 229 | : ((u32)0)) |
| 230 | |
| 231 | #define dev_to_asd_ha(__dev) pci_get_drvdata(to_pci_dev(__dev)) |
| 232 | #define SCB_SITE_VALID(__site_no) (((__site_no) & 0xF0FF) != 0x00FF \ |
| 233 | && ((__site_no) & 0xF0FF) > 0x001F) |
| 234 | /* For each bit set in __lseq_mask, set __lseq to equal the bit |
| 235 | * position of the set bit and execute the statement following. |
| 236 | * __mc is the temporary mask, used as a mask "counter". |
| 237 | */ |
| 238 | #define for_each_sequencer(__lseq_mask, __mc, __lseq) \ |
| 239 | for ((__mc)=(__lseq_mask),(__lseq)=0;(__mc)!=0;(__lseq++),(__mc)>>=1)\ |
| 240 | if (((__mc) & 1)) |
| 241 | #define for_each_phy(__lseq_mask, __mc, __lseq) \ |
| 242 | for ((__mc)=(__lseq_mask),(__lseq)=0;(__mc)!=0;(__lseq++),(__mc)>>=1)\ |
| 243 | if (((__mc) & 1)) |
| 244 | |
| 245 | #define PHY_ENABLED(_HA, _I) ((_HA)->hw_prof.enabled_phys & (1<<(_I))) |
| 246 | |
| 247 | /* ---------- DMA allocs ---------- */ |
| 248 | |
Al Viro | 3cc2754 | 2006-09-25 02:55:40 +0100 | [diff] [blame] | 249 | static inline struct asd_dma_tok *asd_dmatok_alloc(gfp_t flags) |
James Bottomley | 2908d77 | 2006-08-29 09:22:51 -0500 | [diff] [blame] | 250 | { |
| 251 | return kmem_cache_alloc(asd_dma_token_cache, flags); |
| 252 | } |
| 253 | |
| 254 | static inline void asd_dmatok_free(struct asd_dma_tok *token) |
| 255 | { |
| 256 | kmem_cache_free(asd_dma_token_cache, token); |
| 257 | } |
| 258 | |
| 259 | static inline struct asd_dma_tok *asd_alloc_coherent(struct asd_ha_struct * |
| 260 | asd_ha, size_t size, |
Al Viro | 3cc2754 | 2006-09-25 02:55:40 +0100 | [diff] [blame] | 261 | gfp_t flags) |
James Bottomley | 2908d77 | 2006-08-29 09:22:51 -0500 | [diff] [blame] | 262 | { |
| 263 | struct asd_dma_tok *token = asd_dmatok_alloc(flags); |
| 264 | if (token) { |
| 265 | token->size = size; |
| 266 | token->vaddr = dma_alloc_coherent(&asd_ha->pcidev->dev, |
| 267 | token->size, |
| 268 | &token->dma_handle, |
| 269 | flags); |
| 270 | if (!token->vaddr) { |
| 271 | asd_dmatok_free(token); |
| 272 | token = NULL; |
| 273 | } |
| 274 | } |
| 275 | return token; |
| 276 | } |
| 277 | |
| 278 | static inline void asd_free_coherent(struct asd_ha_struct *asd_ha, |
| 279 | struct asd_dma_tok *token) |
| 280 | { |
| 281 | if (token) { |
| 282 | dma_free_coherent(&asd_ha->pcidev->dev, token->size, |
| 283 | token->vaddr, token->dma_handle); |
| 284 | asd_dmatok_free(token); |
| 285 | } |
| 286 | } |
| 287 | |
| 288 | static inline void asd_init_ascb(struct asd_ha_struct *asd_ha, |
| 289 | struct asd_ascb *ascb) |
| 290 | { |
| 291 | INIT_LIST_HEAD(&ascb->list); |
| 292 | ascb->scb = ascb->dma_scb.vaddr; |
| 293 | ascb->ha = asd_ha; |
| 294 | ascb->timer.function = NULL; |
| 295 | init_timer(&ascb->timer); |
| 296 | ascb->tc_index = -1; |
James Bottomley | 2908d77 | 2006-08-29 09:22:51 -0500 | [diff] [blame] | 297 | } |
| 298 | |
| 299 | /* Must be called with the tc_index_lock held! |
| 300 | */ |
| 301 | static inline void asd_tc_index_release(struct asd_seq_data *seq, int index) |
| 302 | { |
| 303 | seq->tc_index_array[index] = NULL; |
| 304 | clear_bit(index, seq->tc_index_bitmap); |
| 305 | } |
| 306 | |
| 307 | /* Must be called with the tc_index_lock held! |
| 308 | */ |
| 309 | static inline int asd_tc_index_get(struct asd_seq_data *seq, void *ptr) |
| 310 | { |
| 311 | int index; |
| 312 | |
| 313 | index = find_first_zero_bit(seq->tc_index_bitmap, |
| 314 | seq->tc_index_bitmap_bits); |
| 315 | if (index == seq->tc_index_bitmap_bits) |
| 316 | return -1; |
| 317 | |
| 318 | seq->tc_index_array[index] = ptr; |
| 319 | set_bit(index, seq->tc_index_bitmap); |
| 320 | |
| 321 | return index; |
| 322 | } |
| 323 | |
| 324 | /* Must be called with the tc_index_lock held! |
| 325 | */ |
| 326 | static inline void *asd_tc_index_find(struct asd_seq_data *seq, int index) |
| 327 | { |
| 328 | return seq->tc_index_array[index]; |
| 329 | } |
| 330 | |
| 331 | /** |
| 332 | * asd_ascb_free -- free a single aSCB after is has completed |
| 333 | * @ascb: pointer to the aSCB of interest |
| 334 | * |
| 335 | * This frees an aSCB after it has been executed/completed by |
| 336 | * the sequencer. |
| 337 | */ |
| 338 | static inline void asd_ascb_free(struct asd_ascb *ascb) |
| 339 | { |
| 340 | if (ascb) { |
| 341 | struct asd_ha_struct *asd_ha = ascb->ha; |
| 342 | unsigned long flags; |
| 343 | |
| 344 | BUG_ON(!list_empty(&ascb->list)); |
| 345 | spin_lock_irqsave(&ascb->ha->seq.tc_index_lock, flags); |
| 346 | asd_tc_index_release(&ascb->ha->seq, ascb->tc_index); |
| 347 | spin_unlock_irqrestore(&ascb->ha->seq.tc_index_lock, flags); |
| 348 | dma_pool_free(asd_ha->scb_pool, ascb->dma_scb.vaddr, |
| 349 | ascb->dma_scb.dma_handle); |
| 350 | kmem_cache_free(asd_ascb_cache, ascb); |
| 351 | } |
| 352 | } |
| 353 | |
| 354 | /** |
| 355 | * asd_ascb_list_free -- free a list of ascbs |
| 356 | * @ascb_list: a list of ascbs |
| 357 | * |
| 358 | * This function will free a list of ascbs allocated by asd_ascb_alloc_list. |
| 359 | * It is used when say the scb queueing function returned QUEUE_FULL, |
| 360 | * and we do not need the ascbs any more. |
| 361 | */ |
| 362 | static inline void asd_ascb_free_list(struct asd_ascb *ascb_list) |
| 363 | { |
| 364 | LIST_HEAD(list); |
| 365 | struct list_head *n, *pos; |
| 366 | |
| 367 | __list_add(&list, ascb_list->list.prev, &ascb_list->list); |
| 368 | list_for_each_safe(pos, n, &list) { |
| 369 | list_del_init(pos); |
| 370 | asd_ascb_free(list_entry(pos, struct asd_ascb, list)); |
| 371 | } |
| 372 | } |
| 373 | |
| 374 | /* ---------- Function declarations ---------- */ |
| 375 | |
| 376 | int asd_init_hw(struct asd_ha_struct *asd_ha); |
David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 377 | irqreturn_t asd_hw_isr(int irq, void *dev_id); |
James Bottomley | 2908d77 | 2006-08-29 09:22:51 -0500 | [diff] [blame] | 378 | |
| 379 | |
| 380 | struct asd_ascb *asd_ascb_alloc_list(struct asd_ha_struct |
| 381 | *asd_ha, int *num, |
Al Viro | 3cc2754 | 2006-09-25 02:55:40 +0100 | [diff] [blame] | 382 | gfp_t gfp_mask); |
James Bottomley | 2908d77 | 2006-08-29 09:22:51 -0500 | [diff] [blame] | 383 | |
| 384 | int asd_post_ascb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb, |
| 385 | int num); |
| 386 | int asd_post_escb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb, |
| 387 | int num); |
| 388 | |
| 389 | int asd_init_post_escbs(struct asd_ha_struct *asd_ha); |
| 390 | void asd_build_control_phy(struct asd_ascb *ascb, int phy_id, u8 subfunc); |
| 391 | void asd_control_led(struct asd_ha_struct *asd_ha, int phy_id, int op); |
| 392 | void asd_turn_led(struct asd_ha_struct *asd_ha, int phy_id, int op); |
| 393 | int asd_enable_phys(struct asd_ha_struct *asd_ha, const u8 phy_mask); |
James Bottomley | 2908d77 | 2006-08-29 09:22:51 -0500 | [diff] [blame] | 394 | |
| 395 | void asd_ascb_timedout(unsigned long data); |
| 396 | int asd_chip_hardrst(struct asd_ha_struct *asd_ha); |
| 397 | |
| 398 | #endif |