/* * Copyright (c) 2010, LSI Corp. * All rights reserved. * Author : Manjunath Ranganathaiah * Support: freebsdraid@lsi.com * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name of the <ORGANIZATION> nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include <dev/tws/tws.h> #include <dev/tws/tws_hdm.h> #include <dev/tws/tws_services.h> #include <sys/time.h> void tws_q_insert_tail(struct tws_softc *sc, struct tws_request *req, u_int8_t q_type ); struct tws_request * tws_q_remove_request(struct tws_softc *sc, struct tws_request *req, u_int8_t q_type ); struct tws_request *tws_q_remove_head(struct tws_softc *sc, u_int8_t q_type ); void tws_q_insert_head(struct tws_softc *sc, struct tws_request *req, u_int8_t q_type ); struct tws_request * tws_q_remove_tail(struct tws_softc *sc, u_int8_t q_type ); void tws_print_stats(void *arg); struct tws_sense *tws_find_sense_from_mfa(struct tws_softc *sc, u_int64_t mfa); static struct error_desc array[] = { { "Cannot add sysctl tree node", 0x2000, ERROR, "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" }, { "Register window not available", 0x2001, ERROR, "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" }, { "Can't allocate register window", 0x2002, ERROR, "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" }, { "Can't allocate interrupt", 0x2003, ERROR, "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" }, { "Can't set up interrupt", 0x2004, ERROR, "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" }, { "Couldn't intialize CAM", 0x2007, ERROR, "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" }, { "Couldn't create SIM device queue", 0x2100, ENOMEM, "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" }, { "Unable to create SIM entry", 0x2101, ENOMEM, "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" }, { "Unable to register the bus", 0x2102, ENXIO, "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" }, { "Unable to create the path", 0x2103, ENXIO, "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" }, { "Bus scan request to CAM failed", 0x2104, ENXIO, "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" }, { "Unable to intialize the driver", 0x2008, ENXIO, "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" }, { "Unable to intialize the controller", 0x2009, ENXIO, "%s: (0x%02X: 0x%04X): %s:\n", "ERROR" }, }; void tws_trace(const char *file, const char *fun, int linenum, struct tws_softc *sc, char *desc, u_int64_t val1, u_int64_t val2) { struct tws_trace_rec *rec = (struct tws_trace_rec *)sc->trace_q.q; volatile u_int16_t head, tail; char fmt[256]; head = sc->trace_q.head; tail = sc->trace_q.tail; /* getnanotime(&rec[tail].ts); */ strncpy(rec[tail].fname, file, TWS_TRACE_FNAME_LEN); strncpy(rec[tail].func, fun, TWS_TRACE_FUNC_LEN); rec[tail].linenum = linenum; strncpy(rec[tail].desc, desc, TWS_TRACE_DESC_LEN); rec[tail].val1 = val1; rec[tail].val2 = val2; tail = (tail+1) % sc->trace_q.depth; if ( head == tail ) { sc->trace_q.overflow = 1; sc->trace_q.head = (head+1) % sc->trace_q.depth; } sc->trace_q.tail = tail; /* tws_circular_q_insert(sc, &sc->trace_q, &rec, sizeof(struct tws_trace_rec)); */ if ( sc->is64bit ) strcpy(fmt, "%05d:%s::%s :%s: 0x%016lx : 0x%016lx \n"); else strcpy(fmt, "%05d:%s::%s :%s: 0x%016llx : 0x%016llx \n"); /* printf("%05d:%s::%s :%s: 0x%016llx : 0x%016llx \n", linenum, file, fun, desc, val1, val2); */ printf(fmt, linenum, file, fun, desc, val1, val2); } void tws_log(struct tws_softc *sc, int index) { device_printf((sc)->tws_dev, array[index].fmt, array[index].error_str, array[index].error_code, array[index].severity_level, array[index].desc ); } /* ----------- swap functions ----------- */ u_int16_t tws_swap16(u_int16_t val) { return((val << 8) | (val >> 8)); } u_int32_t tws_swap32(u_int32_t val) { return(((val << 24) | ((val << 8) & (0xFF0000)) | ((val >> 8) & (0xFF00)) | (val >> 24))); } u_int64_t tws_swap64(u_int64_t val) { return((((u_int64_t)(tws_swap32(((u_int32_t *)(&(val)))[1]))) << 32) | ((u_int32_t)(tws_swap32(((u_int32_t *)(&(val)))[0])))); } /* ----------- reg access ----------- */ void tws_write_reg(struct tws_softc *sc, int offset, u_int32_t value, int size) { bus_space_tag_t bus_tag = sc->bus_tag; bus_space_handle_t bus_handle = sc->bus_handle; if (size == 4) bus_space_write_4(bus_tag, bus_handle, offset, value); else if (size == 2) bus_space_write_2(bus_tag, bus_handle, offset, (u_int16_t)value); else bus_space_write_1(bus_tag, bus_handle, offset, (u_int8_t)value); } u_int32_t tws_read_reg(struct tws_softc *sc, int offset, int size) { bus_space_tag_t bus_tag = sc->bus_tag; bus_space_handle_t bus_handle = sc->bus_handle; if (size == 4) return((u_int32_t)bus_space_read_4(bus_tag, bus_handle, offset)); else if (size == 2) return((u_int32_t)bus_space_read_2(bus_tag, bus_handle, offset)); else return((u_int32_t)bus_space_read_1(bus_tag, bus_handle, offset)); } /* --------------------- Q service --------------------- */ /* * intialize q pointers with null. */ void tws_init_qs(struct tws_softc *sc) { mtx_lock(&sc->q_lock); for(int i=0;i<TWS_MAX_QS;i++) { sc->q_head[i] = NULL; sc->q_tail[i] = NULL; } mtx_unlock(&sc->q_lock); } /* called with lock held */ static void tws_insert2_empty_q(struct tws_softc *sc, struct tws_request *req, u_int8_t q_type ) { mtx_assert(&sc->q_lock, MA_OWNED); req->next = req->prev = NULL; sc->q_head[q_type] = sc->q_tail[q_type] = req; } /* called with lock held */ void tws_q_insert_head(struct tws_softc *sc, struct tws_request *req, u_int8_t q_type ) { mtx_assert(&sc->q_lock, MA_OWNED); if ( sc->q_head[q_type] == NULL ) { tws_insert2_empty_q(sc, req, q_type); } else { req->next = sc->q_head[q_type]; req->prev = NULL; sc->q_head[q_type]->prev = req; sc->q_head[q_type] = req; } } /* called with lock held */ void tws_q_insert_tail(struct tws_softc *sc, struct tws_request *req, u_int8_t q_type ) { mtx_assert(&sc->q_lock, MA_OWNED); if ( sc->q_tail[q_type] == NULL ) { tws_insert2_empty_q(sc, req, q_type); } else { req->prev = sc->q_tail[q_type]; req->next = NULL; sc->q_tail[q_type]->next = req; sc->q_tail[q_type] = req; } } /* called with lock held */ struct tws_request * tws_q_remove_head(struct tws_softc *sc, u_int8_t q_type ) { struct tws_request *r; mtx_assert(&sc->q_lock, MA_OWNED); r = sc->q_head[q_type]; if ( !r ) return(NULL); if ( r->next == NULL && r->prev == NULL ) { /* last element */ sc->q_head[q_type] = sc->q_tail[q_type] = NULL; } else { sc->q_head[q_type] = r->next; r->next->prev = NULL; r->next = NULL; r->prev = NULL; } return(r); } /* called with lock held */ struct tws_request * tws_q_remove_tail(struct tws_softc *sc, u_int8_t q_type ) { struct tws_request *r; mtx_assert(&sc->q_lock, MA_OWNED); r = sc->q_tail[q_type]; if ( !r ) return(NULL); if ( r->next == NULL && r->prev == NULL ) { /* last element */ sc->q_head[q_type] = sc->q_tail[q_type] = NULL; } else { sc->q_tail[q_type] = r->prev; r->prev->next = NULL; r->next = NULL; r->prev = NULL; } return(r); } /* returns removed request if successful. return NULL otherwise */ /* called with lock held */ struct tws_request * tws_q_remove_request(struct tws_softc *sc, struct tws_request *req, u_int8_t q_type ) { struct tws_request *r; mtx_assert(&sc->q_lock, MA_OWNED); if ( req == NULL ) { TWS_TRACE_DEBUG(sc, "null req", 0, q_type); return(NULL); } if ( req == sc->q_head[q_type] ) return(tws_q_remove_head(sc, q_type)); if ( req == sc->q_tail[q_type] ) return(tws_q_remove_tail(sc, q_type)); /* The given node is not at head or tail. * It's in the middle and there are more than * 2 elements on the q. */ if ( req->next == NULL || req->prev == NULL ) { TWS_TRACE_DEBUG(sc, "invalid req", 0, q_type); return(NULL); } /* debug only */ r = sc->q_head[q_type]; while ( r ) { if ( req == r ) break; r = r->next; } if ( !r ) { TWS_TRACE_DEBUG(sc, "req not in q", 0, req->request_id); return(NULL); } /* debug end */ req->prev->next = r->next; req->next->prev = r->prev; req->next = NULL; req->prev = NULL; return(req); } struct tws_sense * tws_find_sense_from_mfa(struct tws_softc *sc, u_int64_t mfa) { struct tws_sense *s; int i; TWS_TRACE_DEBUG(sc, "entry",sc,mfa); i = (mfa - sc->dma_mem_phys) / sizeof(struct tws_command_packet); if ( i>= 0 && i<tws_queue_depth) { s = &sc->sense_bufs[i]; if ( mfa == s->hdr_pkt_phy ) return(s); } TWS_TRACE_DEBUG(sc, "return null",0,mfa); return(NULL); } /* --------------------- Q service end --------------------- */ /* --------------------- misc service start --------------------- */ void tws_print_stats(void *arg) { struct tws_softc *sc = (struct tws_softc *)arg; TWS_TRACE(sc, "reqs(in, out)", sc->stats.reqs_in, sc->stats.reqs_out); TWS_TRACE(sc, "reqs(err, intrs)", sc->stats.reqs_errored , sc->stats.num_intrs); TWS_TRACE(sc, "reqs(ioctls, scsi)", sc->stats.ioctls , sc->stats.scsi_ios); callout_reset(&sc->stats_timer, 300 * hz, tws_print_stats, sc); } /* --------------------- misc service end --------------------- */