<commit_before>//
//   Copyright (C) 2010 <NAME>
//
//   This program is free software; you can redistribute it and/or modify
//   it under the terms of the GNU General Public License as published by
//   the Free Software Foundation; either version 2 of the License, or
//   (at your option) any later version.
//
//   This program is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   GNU General Public License for more details.
//
//   You should have received a copy of the GNU General Public License
//   along with this program; if not, write to the Free Software
//   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
//

// FIXME: Since the change to auto-configuring cards, the separate drivers
// for 8i20 and 7i64 make no sense. They should simply spoof a GTOC and share a
// structure with the auto-conf type. It probably makes sense to consolidate
// into one file too, though it will be a biggie. 


#include <linux/slab.h>

#include "rtapi.h"
#include "rtapi_app.h"
#include "rtapi_string.h"
#include "rtapi_math.h"

#include "hal.h"

#include "hal/drivers/mesa-hostmot2/hostmot2.h"

//utility function delarations
int hm2_sserial_stopstart(hostmot2_t *hm2, hm2_module_descriptor_t *md, 
                          hm2_sserial_instance_t *inst, u32 start_mode);


int hm2_sserial_parse_md(hostmot2_t *hm2, int md_index) {
    hm2_module_descriptor_t *md = &hm2->md[md_index];
    int i, c;
    int pin = -1;
    int port_pin, port;
    u32 ddr_reg, src_reg, addr, buff;
    int r = 0;
    int count = 0;
    int chan_counts[] = {0,0,0,0,0,0,0,0};
    
    hm2->sserial.version = md->version;

    //
    // some standard sanity checks
    //

    if (hm2_md_is_consistent(hm2, md_index, 0, 5, 0x40, 0x001F)) {
        HM2_ERR("The bitfile contains Smart Serial modules for a firmware "
                "revision < rev22. This Driver now requires rev22 or newer "
                "firmwares\n");
        return -EINVAL;
    }
    
    if (!hm2_md_is_consistent_or_complain(hm2, md_index, 0, 6, 0x40, 0x003C)) {
        HM2_ERR("inconsistent Module Descriptor!\n");
        return -EINVAL;
    }

    if (hm2->sserial.num_instances != 0) {
        HM2_ERR(
                "found duplicate Module Descriptor for %s (inconsistent firmwar"
                "e), not loading driver\n",
                hm2_get_general_function_name(md->gtag)
                );
        return -EINVAL;
    }
    if (hm2->config.num_sserials > md->instances) {
        HM2_ERR(
                "num_sserials references %d instances, but only %d are available"
                ", not loading driver\n",
                hm2->config.num_sserials,
                md->instances
                );
        return -EINVAL;
    }

    if (hm2->config.num_sserials == 0) {
        return 0;
    }

    //
    // looks good, start initializing
    //

    if (hm2->config.num_sserials == -1) {
        hm2->sserial.num_instances = md->instances;
    } else {
        hm2->sserial.num_instances = hm2->config.num_sserials;
    }
    //num_instances may be revised down depending on detected hardware
    HM2_DBG("sserial_num_instances = %i\n", hm2->sserial.num_instances);

    // allocate the global interface pins into shared memory
    hm2->sserial.hal = (hm2_sserial_hal_t *)hal_malloc(sizeof(hm2_sserial_hal_t));
    if (hm2->sserial.hal == NULL) {
        HM2_ERR("hm2_sserial_parse_md: hm2_sserial_hal: out of memory!\n");
        r = -ENOMEM;
        goto fail0;
    }
    // allocate the per-instance HAL shared memory
    hm2->sserial.instance = (hm2_sserial_instance_t *)
    hal_malloc(hm2->sserial.num_instances * sizeof(hm2_sserial_instance_t));
    if (hm2->sserial.instance == NULL) {
        HM2_ERR("hm2_sserial_parse_md: hm2_sserial_instance: out of memory!\n");
        r = -ENOMEM;
        goto fail0;
    }
    // We can't create the pins until we know what is on each channel, and
    // can't communicate until the pin directions are set up.

    // Temporarily enable the pins that are not masked by sserial_mode

    for (port  = 0; port < hm2->ioport.num_instances; port ++) {
        ddr_reg = 0;
        src_reg = 0;
        for (port_pin = 0 ; port_pin < hm2->idrom.port_width; port_pin ++){
            pin++;
            if (hm2->pin[pin].sec_tag == HM2_GTAG_SMARTSERIAL) {
                // look for highest-indexed pin to determine number of channels
                if ((hm2->pin[pin].sec_pin & 0x0F) > chan_counts[hm2->pin[pin].sec_unit]) {
                    chan_counts[hm2->pin[pin].sec_unit] = (hm2->pin[pin].sec_pin & 0x0F);
                }
                // check if the channel is enabled
                HM2_DBG("sec unit = %i, sec pin = %i\n", hm2->pin[pin].sec_unit, hm2->pin[pin].sec_pin & 0x0F);
                if (hm2->config.sserial_modes[hm2->pin[pin].sec_unit]
                                        [(hm2->pin[pin].sec_pin & 0x0F) - 1] != 'x') {
                    src_reg |= (1 << port_pin);
                    if (hm2->pin[pin].sec_pin & 0x80){ ddr_reg |= (1 << port_pin); }
                }
            }
        }

        hm2->llio->write(hm2->llio, hm2->ioport.ddr_addr + 4 * port,
                         &ddr_reg, sizeof(u32));
        hm2->llio->write(hm2->llio, hm2->ioport.alt_source_addr + 4 * port,
                         &src_reg, sizeof(u32));

    }

    // Now iterate through the sserial instances, seeing what is on the enabled pins.
    for (i = 0 ; i < hm2->sserial.num_instances ; i++) {

        hm2_sserial_instance_t *inst = &hm2->sserial.instance[count];
        inst->module_index = i;
        inst->num_channels = chan_counts[i];

        HM2_DBG("num channels = %x\n", inst->num_channels);

        inst->command_reg_addr = md->base_address + i * md->instance_stride;
        inst->data_reg_addr
        = md->base_address + i * md->instance_stride + md->register_stride;
        
        buff=0x4000; //Reset
        hm2->llio->write(hm2->llio, inst->command_reg_addr, &buff, sizeof(u32));
        buff=0x0001; //Clear
        hm2->llio->write(hm2->llio, inst->command_reg_addr, &buff, sizeof(u32));
        if (hm2_sserial_waitfor(hm2, inst->command_reg_addr, 0xFFFFFFFF,1007) < 0){
            r = -EINVAL;
            goto fail0;
        }
        do {
            buff=0x2003; //Read firmware version
            hm2->llio->write(hm2->llio, inst->command_reg_addr, &buff, sizeof(u32));
            if (hm2_sserial_waitfor(hm2, inst->command_reg_addr, 0xFFFFFFFF,49) < 0){
                r = -EINVAL;
                goto fail0;
            }
            hm2->llio->read(hm2->llio, inst->data_reg_addr, &buff, sizeof(u32));
        } while (buff == 0xAA);
        HM2_PRINT("Smart Serial Firmware Version %i\n",buff);

        //start up in setup mode
        if ( hm2_sserial_stopstart(hm2, md, inst, 0xF00) < 0) {goto fail0;}
        
        inst->tag_8i20 = 0;
        inst->tag_7i64 = 0;
        inst->num_8i20 = 0;
        inst->num_7i64 = 0;
        inst->num_auto = 0;
        inst->tag_auto = 0;
        inst->tag_all = 0;
        inst->num_all = 0;
        for (c = 0 ; c < inst->num_channels ; c++) {
            u32 user1, user2;
            // user1 addr
            addr = md->base_address + 4 * md->register_stride
                                    + i * md->instance_stride + c * sizeof(u32);
            hm2->llio->read(hm2->llio, addr, &user1, sizeof(u32));
            // user2 addr
            addr = md->base_address + 5 * md->register_stride
                                    + i * md->instance_stride + c * sizeof(u32);
            hm2->llio->read(hm2->llio, addr, &user2, sizeof(u32));
            if (user2 & 0x0000ffff){// Parameter discovery
                inst->tag_auto |= (1 << c);
                inst->tag_all |= (1 << c);
                inst->num_auto += 1;
                inst->num_all += 1;
            } else {
                switch (user1) {
                    case 0x0: // nothing connected, or masked by config
                        for (pin = 0 ; pin < hm2->num_pins ; pin++){
                            if (hm2->pin[pin].sec_tag == HM2_GTAG_SMARTSERIAL
                                && (hm2->pin[pin].sec_pin & 0x0F) - 1  == c
                                && hm2->pin[pin].sec_unit == i){
                                hm2->pin[pin].sec_tag = 0;
                            }
                        }
                        break;
                    case HM2_SSERIAL_TYPE_8I20: // 8i20 found   
                        inst->tag_8i20 |= (1 << c);
                        inst->tag_all |= (1 << c);
                        inst->num_8i20 += 1;
                        inst->num_all += 1;
                        break;
                    case HM2_SSERIAL_TYPE_7I64: // 7i64 found
                        inst->tag_7i64 |= (1 << c);
                        inst->tag_all |= (1 << c);
                        inst->num_7i64 += 1;
                        inst->num_all += 1;
                        break;
                    default:
                        HM2_ERR("Unsupported Device %s4.4 found on sserial %d "
                                "channel %d\n", (char*)&user1, i, c);
                }
            }
        }
        if (inst->num_all > 0){
            r = hal_pin_bit_newf(HAL_IN, &(inst->run),
                                 hm2->llio->comp_id, 
                                 "%s.sserial.port-%1d.run",
                                 hm2->llio->name, i);
            if (r < 0) {
                HM2_ERR("error adding pin %s.sserial.%1d.run. aborting\n",
                        hm2->llio->name, i);
                goto fail0;
            }
            r = hal_pin_u32_newf(HAL_OUT, &(inst->state),
                                 hm2->llio->comp_id, 
                                 "%s.sserial.port-%1d.port_state",
                                 hm2->llio->name, i);
            if (r < 0) {
                HM2_ERR("error adding pin %s.sserial.%1d.port_state. aborting\n",
                        hm2->llio->name, i);
                goto fail0;
            }
            r = hal_pin_u32_newf(HAL_OUT, &(inst->fault_count),
                                 hm2->llio->comp_id, 
                                 "%s.sserial.port-%1d.fault-count",
                                 hm2->llio->name, i);
            if (r < 0) {
                HM2_ERR("error adding pin %s.sserial.%1d.fault-count. aborting\n",
                        hm2->llio->name, i);
                goto fail0;
            }
            r = hal_param_u32_newf(HAL_RW, &(inst->fault_inc),
                                   hm2->llio->comp_id, 
                                   "%s.sserial.port-%1d.fault-inc",
                                   hm2->llio->name, i);
            if (r < 0) {
                HM2_ERR("error adding parameter %s.sserial.port-%1d.fault-inc"
                        " aborting\n",hm2->llio->name, i);
                goto fail0;
            }            

            r = hal_param_u32_newf(HAL_RW, &(inst->fault_dec),
                                   hm2->llio->comp_id, 
                                   "%s.sserial.port-%1d.fault-dec",
                                   hm2->llio->name, i);
            if (r < 0) {
                HM2_ERR("error adding parameter %s.sserial.port-%1d.fault-dec"
                        " aborting\n",hm2->llio->name, i);
                goto fail0;
            }
        
            r = hal_param_u32_newf(HAL_RW, &(inst->fault_lim),
                                   hm2->llio->comp_id, 
                                   "%s.sserial.port-%1d.fault-lim",
                                   hm2->llio->name, i);
            if (r < 0) {
                HM2_ERR("error adding parameter %s.sserial.port-%1d.fault-lim"
                        " aborting\n",hm2->llio->name, i);
                goto fail0;
            }
            //parameter defaults;
            inst->fault_dec = 1;
            inst->fault_inc = 10;
            inst->fault_lim = 200;
            
            //only move to the next instance if this one contains things.
            //hm2->sserial.instance[0] will be the lowest numbered module
            //with attached hardware.
            
            count++ ;
        }
    }
    

    hm2->sserial.num_instances = count; // because of the extra increment

    if (hm2->sserial.num_instances > 0){

        // create the HAL pins
        if (hm2_8i20_create(hm2, md) < 0) goto fail0;
        if (hm2_7i64_create(hm2, md) < 0) goto fail0;
        if (hm2_auto_create(hm2, md) < 0) goto fail0;
        if (hm2_sserial_config_create(hm2) < 0) goto fail0;
        if (hm2_8i20_params(hm2) < 0) goto fail0;
        
        // Set up TRAM writes

        for (i = 0 ; i < hm2->sserial.num_instances ; i++){
            hm2_sserial_instance_t  *inst = &hm2->sserial.instance[i];
            int err_flag;
            if (inst->num_all > 0 ){
                // setup read-back in all modes

                r = hm2_register_tram_read_region(hm2, inst->command_reg_addr,
                                                  sizeof(u32),
                                                  &inst->command_reg_read);
                if (r < 0) {
                    HM2_ERR("error registering tram write region for sserial"
                            "command register (%d)\n", i);
                    goto fail0;
                }

                r = hm2_register_tram_read_region(hm2, inst->data_reg_addr,
                                                  sizeof(u32),
                                                  &inst->data_reg_read);
                if (r < 0) {
                    HM2_ERR("error registering tram write region for sserial "
                            "command register (%d)\n", i);
                    goto fail0;

                }
                // Nothing happens without a "Do It" command
                r = hm2_register_tram_write_region(hm2, inst->command_reg_addr,
                                                   sizeof(u32),
                                                   &inst->command_reg_write);
                if (r < 0) {
                    HM2_ERR("error registering tram write region for sserial "
                            "command register (%d)\n", i);
                    goto fail0;

                }
            }
            // Normal start to check for errors, as soon it will be too late to 
            // abort the load. 
            
            err_flag = 0;
            if (hm2_sserial_stopstart(hm2, md, inst, 0x900) < 0 ) {goto fail0;}
            if (hm2_sserial_auto_check(hm2, inst) < 0) {err_flag = -EINVAL;}
            if (hm2_sserial_8i20_check(hm2, inst) < 0) {/* do nothing */}
            if (hm2_sserial_7i64_check(hm2, inst) < 0) {err_flag = -EINVAL;}
            if (err_flag) goto fail0;
        }
    }
    
    
    // Stop the sserial ports.
    buff=0x800; //Stop All
    for (i = 0 ; i < hm2->sserial.num_instances ; i++) {
        hm2_sserial_instance_t *inst = &hm2->sserial.instance[i];
        hm2->llio->write(hm2->llio, inst->command_reg_addr, &buff, sizeof(u32));
    }
    // Return the physical ports to default
    ddr_reg = 0;
    src_reg = 0;
    for (port  = 0; port < hm2->ioport.num_instances; port ++) {
        hm2->llio->write(hm2->llio, hm2->ioport.ddr_addr + 4 * port,
                         &ddr_reg, sizeof(u32));
        hm2->llio->write(hm2->llio, hm2->ioport.alt_source_addr + 4 * port,
                         &src_reg, sizeof(u32));
    }
    return hm2->sserial.num_instances;

fail0:
    hm2_sserial_cleanup(hm2);
    hm2->sserial.num_instances = 0;
    return -EINVAL;
}


void hm2_sserial_prepare_tram_write(hostmot2_t *hm2, long period){
    // This function mainly serves to handle starting and stopping of the
    // smart-serial modules. Useful for restarting broken comms.
    // Actual tram setup is handled by the sub-drivers

    static int doit_err_count, comm_err_flag; // to avoid repeating error messages
    int i,f;

    if (hm2->sserial.num_instances <= 0) return;

    hm2_8i20_prepare_tram_write(hm2);
    hm2_7i64_prepare_tram_write(hm2);
    hm2_auto_prepare_tram_write(hm2);


    for (i = 0 ; i < hm2->sserial.num_instances ; i++ ) {
        // a state-machine to start and stop the ports, and to
        // supply Do-It commands when required.

        hm2_sserial_instance_t *inst = &hm2->sserial.instance[i];

        switch (*inst->state){
            case 0: // Idle
                if (! *inst->run){ return; }
                *inst->state = 0x11;
                inst->timer = 0;
                //set the modes for the cards
                hm2_8i20_setmode(hm2, inst);
                hm2_7i64_setmode(hm2, inst);
                hm2_sserial_auto_setmode(hm2, inst);

                *inst->command_reg_write = 0x900 | inst->tag_all;
                HM2_DBG("Tag-All = %x\n", inst->tag_all);
                *inst->fault_count = 0;
                doit_err_count = 0;
                comm_err_flag = 0;
                break;
            case 0x01: // normal running
                if (!*inst->run){
                     *inst->state = 0x02;
                    break;
                }
                if (*inst->fault_count > inst->fault_lim) {
                    // If there have been a large percentage of misses, for quite
                    // a long time, it's time to take it seriously. 
                    HM2_ERR("Smart Serial Comms Error: "
                            "There have been more than %i errors in %i "
                            "thread executions at least %i times. "
                            "See other error messages for details.\n",
                            inst->fault_dec, 
                            inst->fault_inc,
                            inst->fault_lim);
                    HM2_ERR("***Smart Serial Port %i will be stopped***\n",i); 
                    *inst->state = 0x20;
                    *inst->command_reg_write = 0x800; // stop command
                }
                if (*inst->command_reg_read) {
                    if (doit_err_count < 6){ doit_err_count++; }
                    if (doit_err_count == 4 ){ // ignore 4 errors at startup
                        HM2_ERR("Smart Serial port %i: DoIt not cleared from previous "
                                "servo thread. Servo thread rate probably too fast. "
                                "This message will not be repeated, but the " 
                                "%s.sserial.%1d.fault-count pin will indicate "
                                "if this is happening frequently.\n",
                                i, hm2->llio->name, i);
                    }
                    *inst->fault_count += inst->fault_inc;
                    *inst->command_reg_write = 0x80000000; // set bit31 for ignored cmd
                    break; // give the register chance to clear
                }
                if (*inst->data_reg_read & 0xff){ // indicates a failed transfer
                    *inst->fault_count += inst->fault_inc;
                    f = (*inst->data_reg_read & (comm_err_flag ^ 0xFF));
                    if (f != 0 && f != 0xFF){
                        comm_err_flag |= (f & -f); //mask LSb
                        HM2_ERR("Smart Serial Error: port %i channel %i. " 
                                "You may see this error if the FPGA card "
                                """read"" thread is not running. "
                                "This error message will not repeat.\n",
                                i, ffs(f) - 1);
                        hm2_sserial_auto_check(hm2, inst);
                        hm2_sserial_8i20_check(hm2, inst);
                        hm2_sserial_7i64_check(hm2, inst);
                    }
                }
                        
                if (*inst->fault_count > inst->fault_dec) {
                    *inst->fault_count -= inst->fault_dec;
                }
                else
                {
                    *inst->fault_count = 0;
                }
                
                *inst->command_reg_write = 0x1000 | inst->tag_all;
                break;

            case 0x02: // run to stop transition
                *inst->state = 0x10;
                inst->timer = 0;
                *inst->command_reg_write = 0x800;
                break;
            case 0x10:// wait for normal stop
            case 0x11:// wait for normal start
                inst->timer += period;
                if (*inst->command_reg_read != 0) {
                    if (inst->timer < 2100000000) {
                        break;
                    }
                    HM2_ERR("sserial_write:"
                            "Timeout waiting for CMD to clear\n");
                    *inst->fault_count += inst->fault_inc;
                    // carry on, nothing much we can do about it
                }
                *inst->state &= 0x0F;
                *inst->command_reg_write = 0x80000000; // mask pointless writes
                break;
            case 0x20:// Do-nothing state for serious errors. require run pin to cycle
                if ( ! *inst->run){*inst->state = 0x02;}
                break;
            default: // Should never happen
                HM2_ERR("Unhandled run/stop configuration in \n"
                        "hm2_sserial_write (%x)\n",
                        *inst->state);
                *inst->state = 0;
        }
    }
}

void hm2_sserial_process_tram_read(hostmot2_t *hm2, long period){

    hm2_8i20_process_tram_read(hm2);
    hm2_7i64_process_tram_read(hm2);
    hm2_auto_process_tram_read(hm2);
    hm2_sserial_process_config(hm2, period);

}

int hm2_sserial_config_create(hostmot2_t *hm2){
    int r;
    // create the global config pins

    r = hal_pin_bit_newf(HAL_IN, &(hm2->sserial.hal->read),
                         hm2->llio->comp_id, "%s.sserial.read",
                         hm2->llio->name);
    if (r < 0) { HM2_ERR("error adding pin %s.sserial.read. aborting\n",
                         hm2->llio->name);
        goto fail0;}

    r = hal_pin_bit_newf(HAL_IN, &(hm2->sserial.hal->write),
                         hm2->llio->comp_id, "%s.sserial.write",
                         hm2->llio->name);
    if (r < 0) {HM2_ERR("error adding pin %s.sserial.write. aborting\n",
                        hm2->llio->name);
        goto fail0;}
    r = hal_pin_u32_newf(HAL_IN, &(hm2->sserial.hal->parameter),
                         hm2->llio->comp_id, "%s.sserial.parameter",
                         hm2->llio->name);
    if (r < 0) {
        HM2_ERR("error adding pin %s.sserial.port. aborting\n",
                hm2->llio->name);
        goto fail0;}
    r = hal_pin_u32_newf(HAL_IN, &(hm2->sserial.hal->port),
                         hm2->llio->comp_id, "%s.sserial.port",
                         hm2->llio->name);
    if (r < 0) {
        HM2_ERR("error adding pin %s.sserial.parameter. aborting\n",
                hm2->llio->name);
        goto fail0;}
    r = hal_pin_u32_newf(HAL_IN, &(hm2->sserial.hal->channel),
                         hm2->llio->comp_id, "%s.sserial.channel",
                         hm2->llio->name);
    if (r < 0) {
        HM2_ERR("error adding pin %s.sserial.channel. aborting\n",
                hm2->llio->name);
        goto fail0;}
    r = hal_pin_u32_newf(HAL_IN, &(hm2->sserial.hal->value),
                         hm2->llio->comp_id, "%s.sserial.value",
                         hm2->llio->name);
    if (r < 0) { HM2_ERR("error adding pin %s.sserial.value. aborting\n",
                         hm2->llio->name);
        goto fail0;}
    r = hal_pin_u32_newf(HAL_OUT, &(hm2->sserial.hal->state),
                         hm2->llio->comp_id, "%s.sserial.state",
                         hm2->llio->name);
    if (r < 0) {HM2_ERR("error adding pin %s.sserial.state. aborting\n",
                        hm2->llio->name);
        goto fail0;}

    return 0;

fail0:
    return r;
}

void hm2_sserial_process_config(hostmot2_t *hm2, long period){

    static long timer;
    u32 buff;
    int chan, port, i;

    static hm2_sserial_instance_t *inst;
    static hm2_sserial_tram_t *tram;

    if (hm2->sserial.num_instances <= 0) return;

    chan = *hm2->sserial.hal->channel;
    port = *hm2->sserial.hal->port;

    // What follows is a state machine that handles the handshaking and communications
    // to allow reading and setting of the firmware parameters of arbitrary sserial
    // devices. It responds to tram-read data, so is in this function despite doing
    // some direct llio writes to non-TRAM registers.

    switch (*hm2->sserial.hal->state) {
        case 0: // idle
            if (*hm2->sserial.hal->read == 0 && *hm2->sserial.hal->write == 0) {
                return;
            }
            for (i = 0 ; i < hm2->sserial.num_instances &&
                 hm2->sserial.instance[i].module_index != port ; i++){
            }
            if (i >= hm2->sserial.num_instances) {
                HM2_ERR("sserial port %i not found\n", port);
                *hm2->sserial.hal->read = 0;
                *hm2->sserial.hal->write = 0;
                return;
            }
            inst = &hm2->sserial.instance[i];

            if (*inst->run) {
                HM2_ERR("It is not possible to set or read parameters on "
                        "active/running sserial ports\n");
                *hm2->sserial.hal->read = 0;
                *hm2->sserial.hal->write = 0;
                return;
            }

            if ((inst->tag_all & (1 << chan)) == 0) {
                HM2_ERR("No device to configure on sserial port %i channel %i\n",
                        port, chan);
                *hm2->sserial.hal->read = 0;
                *hm2->sserial.hal->write = 0;
                return;
            }

            tram = NULL ; // Make sure we don't accidentally configure the previous one

            if (inst->tag_8i20 & (1 << chan)) {
                for (i = 0
                     ; (i < inst->num_8i20) && (inst->tram_8i20[i].index != chan)
                     ; i++){
                }
                if (i >= inst->num_8i20) {
                    HM2_ERR("8i20 on sserial port %i, channel %i not found\n",
                            port, chan);
                    *hm2->sserial.hal->read = 0;
                    *hm2->sserial.hal->write = 0;
                    return;
                }
                tram = &inst->tram_8i20[i];
            }
            else if (inst->tag_7i64 & (1 << chan)) {
                for (i = 0
                     ; (i < inst->num_7i64) && (inst->tram_7i64[i].index != chan)
                     ; i++){}
                if (i >= inst->num_7i64) {
                    HM2_ERR("7i64 on sserial port %i, channel %i not found\n",
                            port, chan);
                    *hm2->sserial.hal->read = 0;
                    *hm2->sserial.hal->write = 0;
                    return;
                }
                tram = &inst->tram_7i64[i];
            }
            else if (inst->tag_auto & (1 << chan)) {
                for (i = 0
                     ; (i < inst->num_auto) && (inst->tram_auto[i].index != chan)
                     ; i++){}
                if (i >= inst->num_7i64) {
                    HM2_ERR("auto-config card on sserial port %i, channel %i "
                            "not found\n", port, chan);
                    *hm2->sserial.hal->read = 0;
                    *hm2->sserial.hal->write = 0;
                    return;
                }
                tram = &inst->tram_auto[i];
            }
            /***********************************************************
             // Add other sserial devices here...
             ************************************************************/

            if (tram == NULL) { // didn't find a tram...
                HM2_ERR("Unable to find a supported device for the specified "
                        "channel\n");
                *hm2->sserial.hal->read = 0;
                *hm2->sserial.hal->write = 0;
                return;
            }
            // valid port and channel, so act on the read/write requests
            if (*hm2->sserial.hal->parameter & 0xFF000000) {
                *hm2->sserial.hal->state = 1;}
            else if (*hm2->sserial.hal->write) {*hm2->sserial.hal->state = 9;}
            else if (*hm2->sserial.hal->read) { *hm2->sserial.hal->state = 5; }

            *inst->command_reg_write = 0xF00 | (1 << tram->index); // start in setup mode
            timer = 0;
            break;

        case 1: // eeprom or nvram access flag setup - wait for command clear
            *inst->command_reg_write = 0x80000000; // write mask
            if (*inst->run != 0) {
                *hm2->sserial.hal->state = 20;
                break;}
            timer += period;
            if (*inst->command_reg_read != 0) {
                if (timer > 50000000) {
                    *hm2->sserial.hal->state = 0;
                    *hm2->sserial.hal->read = 0;
                    HM2_ERR("SSerial setup mode start: Timeout in state 1\n");
                }
                break;
            }
            if (*inst->data_reg_read & (1 << tram->index)){
                *hm2->sserial.hal->state = 20;
                HM2_ERR("Channel-not-ready error in parameter write case 1\n"
                        "Data Reg = %08X\nCS Reg = %08X\n",
                        *inst->data_reg_read, *tram->reg_cs_read);
                //break;
            }
            buff = (*hm2->sserial.hal->parameter & 0xFF000000); //flag
            hm2->llio->write(hm2->llio, tram->reg_cs_addr, &buff, sizeof(u32));
            //reg_0 and CMD are always in the TRAM
            //..memory type
            *tram->reg_0_write = (*hm2->sserial.hal->parameter & 0x00FF0000)>>16;
            //..DoIt command
            *inst->command_reg_write = 0x1000 | (1 << tram->index);
            timer = 0;

            if (*hm2->sserial.hal->write) {*hm2->sserial.hal->state = 9;}
            else if (*hm2->sserial.hal->read) { *hm2->sserial.hal->state = 5; }
            else {
                HM2_ERR("Read or Write pins reset part-way through special "
                        "access mode setup, clearing access and aborting\n");
                *hm2->sserial.hal->state = 20;
            }
            break;

        case 5: // read request - wait for command clear
            *inst->command_reg_write = 0x80000000; // write mask
            if (*inst->run != 0) {
                *hm2->sserial.hal->state = 20;
                break;}
            timer += period;
            if (*inst->command_reg_read != 0) {
                if (timer > 50000000) {
                    *hm2->sserial.hal->state = 0;
                    *hm2->sserial.hal->read = 0;
                    HM2_ERR("SSerial setup param read: Timeout in state 5\n");
                }
                break;
            }
            if (*inst->data_reg_read & (1 << tram->index)){
                *hm2->sserial.hal->state = 20;
                HM2_ERR("Channel-not-ready error waiting to send param read"
                        "command (%x)\n", *inst->data_reg_read);
                break;
            }
            // read command setup
            buff = 0x45000000 | (*hm2->sserial.hal->parameter & 0x0000FFFF);
            hm2->llio->write(hm2->llio, tram->reg_cs_addr, &buff, sizeof(u32));
            // Do It command (written by tram)
            *inst->command_reg_write =  0x1000 | (1 << tram->index);
            timer = 0;
            *hm2->sserial.hal->state = 6;
            break;
        case 6: // waiting for read request ack.
            *inst->command_reg_write = 0x80000000; // write mask bit
            if (*inst->run != 0) { *hm2->sserial.hal->state = 0; return;}
            timer += period;
            if (*inst->command_reg_read != 0) {
                if (timer > 750000000) {
                    *hm2->sserial.hal->state = 20;
                    HM2_ERR("Channel-not-ready error in parameter write case 6\n"
                            "Data Reg = %08X\nCS Reg = %08X\n",
                            *inst->data_reg_read, *tram->reg_cs_read);
                    break;
                }
                break;
            }
            *hm2->sserial.hal->state = 20;
            if (*inst->data_reg_read & (1 << tram->index)){
                HM2_ERR("Channel-not-ready error in state 6: %x, %x\n"
                        "errror flags %d\n",
                        *inst->data_reg_read, (1 << tram->index),
                        (*tram->reg_cs_read & 0x0000FF00) >> 8);
                //              break;
            }
            // looks like success
            *hm2->sserial.hal->value = *tram->reg_0_read;
            break;

        case 9: // write command setup
            *inst->command_reg_write = 0x80000000; // write mask
            if (*inst->run != 0) {*hm2->sserial.hal->state = 0 ; return; }
            timer += period;
            if (*inst->command_reg_read != 0) {
                if (timer > 750000000) {
                    *hm2->sserial.hal->state = 20;
                    HM2_ERR("SSerial setup param write: Timeout in state 9\n");
                    break;
                }
            }
            if (*inst->data_reg_read & (1 << tram->index)){
                *hm2->sserial.hal->state = 20;
                HM2_ERR("Channel-not-ready error in parameter write case 9\n"
                        "Data Reg = %08X\nCS Reg = %08X\n",
                        *inst->data_reg_read, *tram->reg_cs_read);
                break;
            }
            // write command setup
            *tram->reg_0_write = *hm2->sserial.hal->value;
            buff = 0x65000000 | (*hm2->sserial.hal->parameter & 0x0000FFFF);
            hm2->llio->write(hm2->llio, tram->reg_cs_addr, &buff, sizeof(u32));
            // Do It command
            *inst->command_reg_write = 0x1000 | (1 << tram->index);
            timer = 0;
            *hm2->sserial.hal->state = 10;
            break;
        case 10: // waiting for write request ack.
            *inst->command_reg_write = 0x80000000; // write mask
            if (*inst->run != 0) {*hm2->sserial.hal->state = 0; return;}
            timer += period;
            if (*inst->command_reg_read != 0) {
                if (timer > 50000000) {
                    *hm2->sserial.hal->state = 20;
                    HM2_ERR("SSerial setup param write: Timeout in state 10\n");
                    break;
                }
                break;
            }
            *hm2->sserial.hal->state = 20;
            if (*inst->data_reg_read & (1 << tram->index)){
                HM2_ERR("Channel-not-ready error in parameter write case 10\n"
                        "Data Reg = %08X\nCS Reg = %08X\n",
                        *inst->data_reg_read, *tram->reg_cs_read);
            }
            break;
        case 20: // Clear flags and read/write pins
            *hm2->sserial.hal->write = 0;
            *hm2->sserial.hal->read = 0;

            buff = 0x0; // Clear MSB of CSR
            hm2->llio->write(hm2->llio, tram->reg_cs_addr, &buff, sizeof(u32));

            if (!(*hm2->sserial.hal->parameter & 0xFF000000)) {
                *inst->command_reg_write = 0x800;
                *hm2->sserial.hal->state = 22;
                break;
            }
            buff = (*hm2->sserial.hal->parameter & 0xFF000000); //flag
            hm2->llio->write(hm2->llio, tram->reg_cs_addr, &buff, sizeof(u32));
            *tram->reg_0_write = 0; //clear type flag
            *inst->command_reg_write = 0x1000 |  (1 << tram->index); // DoIt command
            timer = 0;
            *hm2->sserial.hal->state = 21;
            break;
        case 21:
            *inst->command_reg_write = 0x80000000; // write mask
            timer += period;
            if (*inst->command_reg_read != 0) {
                if (timer > 750000000) {
                    *inst->command_reg_write = 0x800; // stop request
                    *hm2->sserial.hal->state = 22;
                    HM2_ERR("Timeout in state 21, attempting to reset flags."
                            " suggest hard reset of cards (power off) \n");
                    break;
                }
                break;
            }
            if (*inst->data_reg_read & (1 << tram->index)){
                HM2_ERR("Channel-not-ready error in case 21, clearing flags\n"
                        "Data Reg = %08X\nCS Reg = %08X\n",
                        *inst->data_reg_read, *tram->reg_cs_read);
            }
            *inst->command_reg_write = 0x800; // stop request
            *hm2->sserial.hal->state = 22;
            break;
        case 22: // wait for port to stop
            *inst->command_reg_write = 0x80000000; // write mask
            timer += period;
            if (*inst->command_reg_read != 0) {
                if (timer > 220000000) {
                    *hm2->sserial.hal->state = 0;
                    HM2_ERR("Timeout waiting for port to stop \n");
                    break;
                }
                break;
            }
            *hm2->sserial.hal->state = 0;
            break;

        default:
            HM2_ERR("Unsupported state (%i) in sserial setup\n", *hm2->sserial.hal->state);
            *hm2->sserial.hal->state = 0;
            break;
    }
    if (*hm2->sserial.hal->state != 0){
        HM2_DBG("COM(r/w):%08x/%08x, DATA:%08x, CS:%08x, USER0:%08x\n", 
                *inst->command_reg_read, *inst->command_reg_write, 
                *inst->data_reg_read, buff, *tram->reg_0_write);
    }

}

void hm2_sserial_print_module(hostmot2_t *hm2) {
    int i;
    HM2_PRINT("SSerial: %d\n", hm2->sserial.num_instances);
    HM2_PRINT("  version %d\n", hm2->sserial.version);
    if (hm2->sserial.num_instances <= 0) return;
    for (i = 0; i < hm2->sserial.num_instances; i ++) {
        HM2_PRINT("    instance %d:\n", i);
        HM2_PRINT("        Command Addr 0x%04x\n", hm2->sserial.instance[i].command_reg_addr);
        HM2_PRINT("        Data Addr    0x%04x\n", hm2->sserial.instance[i].data_reg_addr);
        HM2_PRINT("        %d 8i20s\n", hm2->sserial.instance[i].num_8i20);
        HM2_PRINT("        %d 7i64s\n", hm2->sserial.instance[i].num_7i64);
    }
    HM2_PRINT("\n");
}

u32 hm2_sserial_get_param(hostmot2_t *hm2, hm2_sserial_tram_t *chan, int param){
    u32 data;

    if (0 > hm2_sserial_waitfor(hm2, chan->reg_cs_addr, 0x0000FF00, 20)){
        goto fail0;
    }

    data = 0x45000000 | param;
    hm2->llio->write(hm2->llio, chan->reg_cs_addr, &data, sizeof(u32));
    data = 0x1000 | (1 << chan->index);
    hm2->llio->write(hm2->llio, chan->reg_command_addr, &data, sizeof(u32));

    if (0 > hm2_sserial_waitfor(hm2, chan->reg_command_addr, 0xFFFFFFFF, 20)){
        goto fail0;
    }
    if (0 > hm2_sserial_waitfor(hm2, chan->reg_cs_addr, 0x00000FF00, 20)){
        goto fail0;
    }

    hm2->llio->read(hm2->llio, chan->reg_data_addr, &data, sizeof(u32));
    hm2->llio->read(hm2->llio, chan->reg_0_addr, &data, sizeof(u32));
    return data;
fail0:
    return -EINVAL;
}

int hm2_sserial_get_bytes(hostmot2_t *hm2, hm2_sserial_tram_t *chan, void *buffer, int addr, int size ){
    // Gets the bytes one at a time. This could be done more efficiently. 
    char *ptr;
    if (0 > hm2_sserial_waitfor(hm2, chan->reg_cs_addr, 0x0000FF00, 20)){
        goto fail0;
    }
    ptr = (char*)buffer;
    while(0 != size){ // -1 in size means "find null" for strings.
        
        u32 data = 0x4C000000 | addr++;
        hm2->llio->write(hm2->llio, chan->reg_cs_addr, &data, sizeof(u32));
        
        if (0 > hm2_sserial_waitfor(hm2, chan->reg_cs_addr, 0x0000FF00, 20)){
            goto fail0;
        }
        data = 0x1000 | (1 << chan->index);
        hm2->llio->write(hm2->llio, chan->reg_command_addr, &data, sizeof(u32));
        
        if (0 > hm2_sserial_waitfor(hm2, chan->reg_command_addr, 0xFFFFFFFF, 20)){
            goto fail0;
        }
        
        hm2->llio->read(hm2->llio, chan->reg_0_addr, &data, sizeof(u32));
        data &= 0x000000FF;
        size--;
        if (size < 0) { // string data
            if (data == 0 || size < (-HM2_SSERIAL_MAX_STRING_LENGTH)){
                size = 0; 
            } else {
                data |= 0x20; // lower case
            }
        }
        *(ptr++) = (unsigned char)data;
    }
    return addr;
        
fail0:
    HM2_ERR("Error trying to read config data in sserial_get_bytes\n");
    return -EINVAL;
}

int hm2_sserial_stopstart(hostmot2_t *hm2, hm2_module_descriptor_t *md, 
                          hm2_sserial_instance_t *inst, u32 start_mode){
    u32 buff, addr;
    int i = inst->module_index;
    int c;
    
    buff=0x800; //Stop All
    hm2->llio->write(hm2->llio, inst->command_reg_addr, &buff, sizeof(u32));
    if (hm2_sserial_waitfor(hm2, inst->command_reg_addr, 0xFFFFFFFF,51) < 0){
        return -EINVAL;
    }
    
    for (c = 0 ; c < inst->num_channels ; c++){
        if (hm2->config.sserial_modes[i][c] != 'x'){
            start_mode |= 1 << c;
            HM2_DBG("Start-mode = %x\n", start_mode);
            // CS addr - write card mode
            addr = md->base_address + 2 * md->register_stride
            + i * md->instance_stride + c * sizeof(u32);
            buff = (hm2->config.sserial_modes[i][c] - '0') << 24;
            hm2->llio->write(hm2->llio, addr, &buff, sizeof(u32));
        }
    }
    hm2->llio->write(hm2->llio, inst->command_reg_addr, &start_mode, sizeof(u32));
    if (hm2_sserial_waitfor(hm2, inst->command_reg_addr, 0xFFFFFFFF, 8000) < 0){
        return -EINVAL;
    }
    return 0;
}

int hm2_sserial_check_errors(hostmot2_t *hm2, hm2_sserial_tram_t *tram){
    u32 buff;
    int i;
    int err_flag = 0;
    u32 err_mask = 0xF300E1FF;
    const char *err_list[32] = {"CRC error", "Invalid cookie", "Overrun",
        "Timeout", "Extra character", "Serial Break Error", "Remote Fault", 
        "Too many errors", 
        
        "Remote fault", "unused", "unused", "unused", "unused", 
        "Communication error", "No Remote ID", "Communication Not Ready",
        
        "unused","unused","unused","unused","unused","unused","unused","unused",
        
        "Watchdog Fault", "No Enable", "Over Temperature", "Over Current", 
        "Over Voltage", "Under Voltage", "Illegal Remote Mode", "LBPCOM Fault"};
    
    hm2->llio->read(hm2->llio, tram->reg_data_addr, &buff, sizeof(u32));
    if (0 == (buff & (1 << tram->index))) {return 0;}
    
    hm2->llio->read(hm2->llio, tram->reg_cs_addr, &buff, sizeof(u32));
    buff &= err_mask;
    for (i = 31 ; i > 0 ; i--){
        if (buff & (1 << i)) {
            HM2_ERR("Smart serial card %s error = (%i) %s\n", 
                    tram->name, i, err_list[i]);
            err_flag = -EINVAL;
        }
    }
    return err_flag;
}

int hm2_sserial_waitfor(hostmot2_t *hm2, u32 addr, u32 mask, int ms){
    u64 t1, t2;
    u32 d;
    t1 = rtapi_get_time();
    do { // wait for addr to clear
        hm2->llio->read(hm2->llio, addr, &d, sizeof(u32));
        t2 = rtapi_get_time();
        if ((u32)(t2 - t1) > 1000000L * ms) {
            HM2_ERR("hm2_sserial_waitfor: Timeout (%dmS) waiting for addr %x &"
                    "mask %x val %x\n", ms, addr, mask, d & mask);
            addr += 0x100;
            hm2->llio->read(hm2->llio, addr, &d, sizeof(u32));
            HM2_ERR("DATA addr %x after timeout: %x\n", addr, d);
            return -1;
        }
    }while (d & mask);
    return 0;
}

void hm2_sserial_force_write(hostmot2_t *hm2){
    int i;
    u32 buff;
    for(i = 0; i < hm2->sserial.num_instances; i++){

        buff = 0x800;
        hm2->llio->write(hm2->llio, hm2->sserial.instance[i].command_reg_addr, &buff, sizeof(u32));
        *hm2->sserial.instance[i].run = 0;
        *hm2->sserial.instance[i].state = 0;
        hm2_sserial_waitfor(hm2, hm2->sserial.instance[i].command_reg_addr, 0xFFFFFFFF, 20);
        *hm2->sserial.instance[i].run = 1;
        *hm2->sserial.instance[i].command_reg_write = 0x80000000;
    }
}

void hm2_sserial_cleanup(hostmot2_t *hm2){
    int i;
    u32 buff;
    for (i = 1 ; i < hm2->sserial.num_instances; i++){
        //Shut down the sserial devices rather than leave that to the watchdog. 
        buff = 0x800;
        hm2->llio->write(hm2->llio,
                         hm2->sserial.instance[i].command_reg_addr,
                         &buff,
                         sizeof(u32));
        if (hm2->sserial.instance[i].tram_8i20 != NULL){
            hm2_sserial_8i20_cleanup(hm2);
        }
        if (hm2->sserial.instance[i].tram_7i64 != NULL){
            hm2_sserial_7i64_cleanup(hm2);
        }
        if (hm2->sserial.instance[i].tram_auto != NULL){
            hm2_sserial_auto_cleanup(hm2);
        }
    }
}

<commit_msg>src/hal/drivers/mesa-hostmot2/sserial.c One file at a time staging commits for integrating usermode-pci branch<commit_after>//
//   Copyright (C) 2010 <NAME>
//
//   This program is free software; you can redistribute it and/or modify
//   it under the terms of the GNU General Public License as published by
//   the Free Software Foundation; either version 2 of the License, or
//   (at your option) any later version.
//
//   This program is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   GNU General Public License for more details.
//
//   You should have received a copy of the GNU General Public License
//   along with this program; if not, write to the Free Software
//   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
//

// FIXME: Since the change to auto-configuring cards, the separate drivers
// for 8i20 and 7i64 make no sense. They should simply spoof a GTOC and share a
// structure with the auto-conf type. It probably makes sense to consolidate
// into one file too, though it will be a biggie. 


#include "config_module.h"
#include RTAPI_INC_SLAB_H

#include "rtapi.h"
#include "rtapi_app.h"
#include "rtapi_string.h"
#include "rtapi_math.h"

#include "hal.h"

#include "hal/drivers/mesa-hostmot2/hostmot2.h"

//utility function delarations
int hm2_sserial_stopstart(hostmot2_t *hm2, hm2_module_descriptor_t *md, 
                          hm2_sserial_instance_t *inst, u32 start_mode);


int hm2_sserial_parse_md(hostmot2_t *hm2, int md_index) {
    hm2_module_descriptor_t *md = &hm2->md[md_index];
    int i, c;
    int pin = -1;
    int port_pin, port;
    u32 ddr_reg, src_reg, addr, buff;
    int r = 0;
    int count = 0;
    int chan_counts[] = {0,0,0,0,0,0,0,0};
    
    hm2->sserial.version = md->version;

    //
    // some standard sanity checks
    //

    if (hm2_md_is_consistent(hm2, md_index, 0, 5, 0x40, 0x001F)) {
        HM2_ERR("The bitfile contains Smart Serial modules for a firmware "
                "revision < rev22. This Driver now requires rev22 or newer "
                "firmwares\n");
        return -EINVAL;
    }
    
    if (!hm2_md_is_consistent_or_complain(hm2, md_index, 0, 6, 0x40, 0x003C)) {
        HM2_ERR("inconsistent Module Descriptor!\n");
        return -EINVAL;
    }

    if (hm2->sserial.num_instances != 0) {
        HM2_ERR(
                "found duplicate Module Descriptor for %s (inconsistent firmwar"
                "e), not loading driver\n",
                hm2_get_general_function_name(md->gtag)
                );
        return -EINVAL;
    }
    if (hm2->config.num_sserials > md->instances) {
        HM2_ERR(
                "num_sserials references %d instances, but only %d are available"
                ", not loading driver\n",
                hm2->config.num_sserials,
                md->instances
                );
        return -EINVAL;
    }

    if (hm2->config.num_sserials == 0) {
        return 0;
    }

    //
    // looks good, start initializing
    //

    if (hm2->config.num_sserials == -1) {
        hm2->sserial.num_instances = md->instances;
    } else {
        hm2->sserial.num_instances = hm2->config.num_sserials;
    }
    //num_instances may be revised down depending on detected hardware
    HM2_DBG("sserial_num_instances = %i\n", hm2->sserial.num_instances);

    // allocate the global interface pins into shared memory
    hm2->sserial.hal = (hm2_sserial_hal_t *)hal_malloc(sizeof(hm2_sserial_hal_t));
    if (hm2->sserial.hal == NULL) {
        HM2_ERR("hm2_sserial_parse_md: hm2_sserial_hal: out of memory!\n");
        r = -ENOMEM;
        goto fail0;
    }
    // allocate the per-instance HAL shared memory
    hm2->sserial.instance = (hm2_sserial_instance_t *)
    hal_malloc(hm2->sserial.num_instances * sizeof(hm2_sserial_instance_t));
    if (hm2->sserial.instance == NULL) {
        HM2_ERR("hm2_sserial_parse_md: hm2_sserial_instance: out of memory!\n");
        r = -ENOMEM;
        goto fail0;
    }
    // We can't create the pins until we know what is on each channel, and
    // can't communicate until the pin directions are set up.

    // Temporarily enable the pins that are not masked by sserial_mode

    for (port  = 0; port < hm2->ioport.num_instances; port ++) {
        ddr_reg = 0;
        src_reg = 0;
        for (port_pin = 0 ; port_pin < hm2->idrom.port_width; port_pin ++){
            pin++;
            if (hm2->pin[pin].sec_tag == HM2_GTAG_SMARTSERIAL) {
                // look for highest-indexed pin to determine number of channels
                if ((hm2->pin[pin].sec_pin & 0x0F) > chan_counts[hm2->pin[pin].sec_unit]) {
                    chan_counts[hm2->pin[pin].sec_unit] = (hm2->pin[pin].sec_pin & 0x0F);
                }
                // check if the channel is enabled
                HM2_DBG("sec unit = %i, sec pin = %i\n", hm2->pin[pin].sec_unit, hm2->pin[pin].sec_pin & 0x0F);
                if (hm2->config.sserial_modes[hm2->pin[pin].sec_unit]
                                        [(hm2->pin[pin].sec_pin & 0x0F) - 1] != 'x') {
                    src_reg |= (1 << port_pin);
                    if (hm2->pin[pin].sec_pin & 0x80){ ddr_reg |= (1 << port_pin); }
                }
            }
        }

        hm2->llio->write(hm2->llio, hm2->ioport.ddr_addr + 4 * port,
                         &ddr_reg, sizeof(u32));
        hm2->llio->write(hm2->llio, hm2->ioport.alt_source_addr + 4 * port,
                         &src_reg, sizeof(u32));

    }

    // Now iterate through the sserial instances, seeing what is on the enabled pins.
    for (i = 0 ; i < hm2->sserial.num_instances ; i++) {

        hm2_sserial_instance_t *inst = &hm2->sserial.instance[count];
        inst->module_index = i;
        inst->num_channels = chan_counts[i];

        HM2_DBG("num channels = %x\n", inst->num_channels);

        inst->command_reg_addr = md->base_address + i * md->instance_stride;
        inst->data_reg_addr
        = md->base_address + i * md->instance_stride + md->register_stride;
        
        buff=0x4000; //Reset
        hm2->llio->write(hm2->llio, inst->command_reg_addr, &buff, sizeof(u32));
        buff=0x0001; //Clear
        hm2->llio->write(hm2->llio, inst->command_reg_addr, &buff, sizeof(u32));
        if (hm2_sserial_waitfor(hm2, inst->command_reg_addr, 0xFFFFFFFF,1007) < 0){
            r = -EINVAL;
            goto fail0;
        }
        do {
            buff=0x2003; //Read firmware version
            hm2->llio->write(hm2->llio, inst->command_reg_addr, &buff, sizeof(u32));
            if (hm2_sserial_waitfor(hm2, inst->command_reg_addr, 0xFFFFFFFF,49) < 0){
                r = -EINVAL;
                goto fail0;
            }
            hm2->llio->read(hm2->llio, inst->data_reg_addr, &buff, sizeof(u32));
        } while (buff == 0xAA);
        HM2_PRINT("Smart Serial Firmware Version %i\n",buff);

        //start up in setup mode
        if ( hm2_sserial_stopstart(hm2, md, inst, 0xF00) < 0) {goto fail0;}
        
        inst->tag_8i20 = 0;
        inst->tag_7i64 = 0;
        inst->num_8i20 = 0;
        inst->num_7i64 = 0;
        inst->num_auto = 0;
        inst->tag_auto = 0;
        inst->tag_all = 0;
        inst->num_all = 0;
        for (c = 0 ; c < inst->num_channels ; c++) {
            u32 user1, user2;
            // user1 addr
            addr = md->base_address + 4 * md->register_stride
                                    + i * md->instance_stride + c * sizeof(u32);
            hm2->llio->read(hm2->llio, addr, &user1, sizeof(u32));
            // user2 addr
            addr = md->base_address + 5 * md->register_stride
                                    + i * md->instance_stride + c * sizeof(u32);
            hm2->llio->read(hm2->llio, addr, &user2, sizeof(u32));
            if (user2 & 0x0000ffff){// Parameter discovery
                inst->tag_auto |= (1 << c);
                inst->tag_all |= (1 << c);
                inst->num_auto += 1;
                inst->num_all += 1;
            } else {
                switch (user1) {
                    case 0x0: // nothing connected, or masked by config
                        for (pin = 0 ; pin < hm2->num_pins ; pin++){
                            if (hm2->pin[pin].sec_tag == HM2_GTAG_SMARTSERIAL
                                && (hm2->pin[pin].sec_pin & 0x0F) - 1  == c
                                && hm2->pin[pin].sec_unit == i){
                                hm2->pin[pin].sec_tag = 0;
                            }
                        }
                        break;
                    case HM2_SSERIAL_TYPE_8I20: // 8i20 found   
                        inst->tag_8i20 |= (1 << c);
                        inst->tag_all |= (1 << c);
                        inst->num_8i20 += 1;
                        inst->num_all += 1;
                        break;
                    case HM2_SSERIAL_TYPE_7I64: // 7i64 found
                        inst->tag_7i64 |= (1 << c);
                        inst->tag_all |= (1 << c);
                        inst->num_7i64 += 1;
                        inst->num_all += 1;
                        break;
                    default:
                        HM2_ERR("Unsupported Device %s4.4 found on sserial %d "
                                "channel %d\n", (char*)&user1, i, c);
                }
            }
        }
        if (inst->num_all > 0){
            r = hal_pin_bit_newf(HAL_IN, &(inst->run),
                                 hm2->llio->comp_id, 
                                 "%s.sserial.port-%1d.run",
                                 hm2->llio->name, i);
            if (r < 0) {
                HM2_ERR("error adding pin %s.sserial.%1d.run. aborting\n",
                        hm2->llio->name, i);
                goto fail0;
            }
            r = hal_pin_u32_newf(HAL_OUT, &(inst->state),
                                 hm2->llio->comp_id, 
                                 "%s.sserial.port-%1d.port_state",
                                 hm2->llio->name, i);
            if (r < 0) {
                HM2_ERR("error adding pin %s.sserial.%1d.port_state. aborting\n",
                        hm2->llio->name, i);
                goto fail0;
            }
            r = hal_pin_u32_newf(HAL_OUT, &(inst->fault_count),
                                 hm2->llio->comp_id, 
                                 "%s.sserial.port-%1d.fault-count",
                                 hm2->llio->name, i);
            if (r < 0) {
                HM2_ERR("error adding pin %s.sserial.%1d.fault-count. aborting\n",
                        hm2->llio->name, i);
                goto fail0;
            }
            r = hal_param_u32_newf(HAL_RW, &(inst->fault_inc),
                                   hm2->llio->comp_id, 
                                   "%s.sserial.port-%1d.fault-inc",
                                   hm2->llio->name, i);
            if (r < 0) {
                HM2_ERR("error adding parameter %s.sserial.port-%1d.fault-inc"
                        " aborting\n",hm2->llio->name, i);
                goto fail0;
            }            

            r = hal_param_u32_newf(HAL_RW, &(inst->fault_dec),
                                   hm2->llio->comp_id, 
                                   "%s.sserial.port-%1d.fault-dec",
                                   hm2->llio->name, i);
            if (r < 0) {
                HM2_ERR("error adding parameter %s.sserial.port-%1d.fault-dec"
                        " aborting\n",hm2->llio->name, i);
                goto fail0;
            }
        
            r = hal_param_u32_newf(HAL_RW, &(inst->fault_lim),
                                   hm2->llio->comp_id, 
                                   "%s.sserial.port-%1d.fault-lim",
                                   hm2->llio->name, i);
            if (r < 0) {
                HM2_ERR("error adding parameter %s.sserial.port-%1d.fault-lim"
                        " aborting\n",hm2->llio->name, i);
                goto fail0;
            }
            //parameter defaults;
            inst->fault_dec = 1;
            inst->fault_inc = 10;
            inst->fault_lim = 200;
            
            //only move to the next instance if this one contains things.
            //hm2->sserial.instance[0] will be the lowest numbered module
            //with attached hardware.
            
            count++ ;
        }
    }
    

    hm2->sserial.num_instances = count; // because of the extra increment

    if (hm2->sserial.num_instances > 0){

        // create the HAL pins
        if (hm2_8i20_create(hm2, md) < 0) goto fail0;
        if (hm2_7i64_create(hm2, md) < 0) goto fail0;
        if (hm2_auto_create(hm2, md) < 0) goto fail0;
        if (hm2_sserial_config_create(hm2) < 0) goto fail0;
        if (hm2_8i20_params(hm2) < 0) goto fail0;
        
        // Set up TRAM writes

        for (i = 0 ; i < hm2->sserial.num_instances ; i++){
            hm2_sserial_instance_t  *inst = &hm2->sserial.instance[i];
            int err_flag;
            if (inst->num_all > 0 ){
                // setup read-back in all modes

                r = hm2_register_tram_read_region(hm2, inst->command_reg_addr,
                                                  sizeof(u32),
                                                  &inst->command_reg_read);
                if (r < 0) {
                    HM2_ERR("error registering tram write region for sserial"
                            "command register (%d)\n", i);
                    goto fail0;
                }

                r = hm2_register_tram_read_region(hm2, inst->data_reg_addr,
                                                  sizeof(u32),
                                                  &inst->data_reg_read);
                if (r < 0) {
                    HM2_ERR("error registering tram write region for sserial "
                            "command register (%d)\n", i);
                    goto fail0;

                }
                // Nothing happens without a "Do It" command
                r = hm2_register_tram_write_region(hm2, inst->command_reg_addr,
                                                   sizeof(u32),
                                                   &inst->command_reg_write);
                if (r < 0) {
                    HM2_ERR("error registering tram write region for sserial "
                            "command register (%d)\n", i);
                    goto fail0;

                }
            }
            // Normal start to check for errors, as soon it will be too late to 
            // abort the load. 
            
            err_flag = 0;
            if (hm2_sserial_stopstart(hm2, md, inst, 0x900) < 0 ) {goto fail0;}
            if (hm2_sserial_auto_check(hm2, inst) < 0) {err_flag = -EINVAL;}
            if (hm2_sserial_8i20_check(hm2, inst) < 0) {/* do nothing */}
            if (hm2_sserial_7i64_check(hm2, inst) < 0) {err_flag = -EINVAL;}
            if (err_flag) goto fail0;
        }
    }
    
    
    // Stop the sserial ports.
    buff=0x800; //Stop All
    for (i = 0 ; i < hm2->sserial.num_instances ; i++) {
        hm2_sserial_instance_t *inst = &hm2->sserial.instance[i];
        hm2->llio->write(hm2->llio, inst->command_reg_addr, &buff, sizeof(u32));
    }
    // Return the physical ports to default
    ddr_reg = 0;
    src_reg = 0;
    for (port  = 0; port < hm2->ioport.num_instances; port ++) {
        hm2->llio->write(hm2->llio, hm2->ioport.ddr_addr + 4 * port,
                         &ddr_reg, sizeof(u32));
        hm2->llio->write(hm2->llio, hm2->ioport.alt_source_addr + 4 * port,
                         &src_reg, sizeof(u32));
    }
    return hm2->sserial.num_instances;

fail0:
    hm2_sserial_cleanup(hm2);
    hm2->sserial.num_instances = 0;
    return -EINVAL;
}


void hm2_sserial_prepare_tram_write(hostmot2_t *hm2, long period){
    // This function mainly serves to handle starting and stopping of the
    // smart-serial modules. Useful for restarting broken comms.
    // Actual tram setup is handled by the sub-drivers

    static int doit_err_count, comm_err_flag; // to avoid repeating error messages
    int i,f;

    if (hm2->sserial.num_instances <= 0) return;

    hm2_8i20_prepare_tram_write(hm2);
    hm2_7i64_prepare_tram_write(hm2);
    hm2_auto_prepare_tram_write(hm2);


    for (i = 0 ; i < hm2->sserial.num_instances ; i++ ) {
        // a state-machine to start and stop the ports, and to
        // supply Do-It commands when required.

        hm2_sserial_instance_t *inst = &hm2->sserial.instance[i];

        switch (*inst->state){
            case 0: // Idle
                if (! *inst->run){ return; }
                *inst->state = 0x11;
                inst->timer = 0;
                //set the modes for the cards
                hm2_8i20_setmode(hm2, inst);
                hm2_7i64_setmode(hm2, inst);
                hm2_sserial_auto_setmode(hm2, inst);

                *inst->command_reg_write = 0x900 | inst->tag_all;
                HM2_DBG("Tag-All = %x\n", inst->tag_all);
                *inst->fault_count = 0;
                doit_err_count = 0;
                comm_err_flag = 0;
                break;
            case 0x01: // normal running
                if (!*inst->run){
                     *inst->state = 0x02;
                    break;
                }
                if (*inst->fault_count > inst->fault_lim) {
                    // If there have been a large percentage of misses, for quite
                    // a long time, it's time to take it seriously. 
                    HM2_ERR("Smart Serial Comms Error: "
                            "There have been more than %i errors in %i "
                            "thread executions at least %i times. "
                            "See other error messages for details.\n",
                            inst->fault_dec, 
                            inst->fault_inc,
                            inst->fault_lim);
                    HM2_ERR("***Smart Serial Port %i will be stopped***\n",i); 
                    *inst->state = 0x20;
                    *inst->command_reg_write = 0x800; // stop command
                }
                if (*inst->command_reg_read) {
                    if (doit_err_count < 6){ doit_err_count++; }
                    if (doit_err_count == 4 ){ // ignore 4 errors at startup
                        HM2_ERR("Smart Serial port %i: DoIt not cleared from previous "
                                "servo thread. Servo thread rate probably too fast. "
                                "This message will not be repeated, but the " 
                                "%s.sserial.%1d.fault-count pin will indicate "
                                "if this is happening frequently.\n",
                                i, hm2->llio->name, i);
                    }
                    *inst->fault_count += inst->fault_inc;
                    *inst->command_reg_write = 0x80000000; // set bit31 for ignored cmd
                    break; // give the register chance to clear
                }
                if (*inst->data_reg_read & 0xff){ // indicates a failed transfer
                    *inst->fault_count += inst->fault_inc;
                    f = (*inst->data_reg_read & (comm_err_flag ^ 0xFF));
                    if (f != 0 && f != 0xFF){
                        comm_err_flag |= (f & -f); //mask LSb
                        HM2_ERR("Smart Serial Error: port %i channel %i. " 
                                "You may see this error if the FPGA card "
                                """read"" thread is not running. "
                                "This error message will not repeat.\n",
                                i, ffs(f) - 1);
                        hm2_sserial_auto_check(hm2, inst);
                        hm2_sserial_8i20_check(hm2, inst);
                        hm2_sserial_7i64_check(hm2, inst);
                    }
                }
                        
                if (*inst->fault_count > inst->fault_dec) {
                    *inst->fault_count -= inst->fault_dec;
                }
                else
                {
                    *inst->fault_count = 0;
                }
                
                *inst->command_reg_write = 0x1000 | inst->tag_all;
                break;

            case 0x02: // run to stop transition
                *inst->state = 0x10;
                inst->timer = 0;
                *inst->command_reg_write = 0x800;
                break;
            case 0x10:// wait for normal stop
            case 0x11:// wait for normal start
                inst->timer += period;
                if (*inst->command_reg_read != 0) {
                    if (inst->timer < 2100000000) {
                        break;
                    }
                    HM2_ERR("sserial_write:"
                            "Timeout waiting for CMD to clear\n");
                    *inst->fault_count += inst->fault_inc;
                    // carry on, nothing much we can do about it
                }
                *inst->state &= 0x0F;
                *inst->command_reg_write = 0x80000000; // mask pointless writes
                break;
            case 0x20:// Do-nothing state for serious errors. require run pin to cycle
                if ( ! *inst->run){*inst->state = 0x02;}
                break;
            default: // Should never happen
                HM2_ERR("Unhandled run/stop configuration in \n"
                        "hm2_sserial_write (%x)\n",
                        *inst->state);
                *inst->state = 0;
        }
    }
}

void hm2_sserial_process_tram_read(hostmot2_t *hm2, long period){

    hm2_8i20_process_tram_read(hm2);
    hm2_7i64_process_tram_read(hm2);
    hm2_auto_process_tram_read(hm2);
    hm2_sserial_process_config(hm2, period);

}

int hm2_sserial_config_create(hostmot2_t *hm2){
    int r;
    // create the global config pins

    r = hal_pin_bit_newf(HAL_IN, &(hm2->sserial.hal->read),
                         hm2->llio->comp_id, "%s.sserial.read",
                         hm2->llio->name);
    if (r < 0) { HM2_ERR("error adding pin %s.sserial.read. aborting\n",
                         hm2->llio->name);
        goto fail0;}

    r = hal_pin_bit_newf(HAL_IN, &(hm2->sserial.hal->write),
                         hm2->llio->comp_id, "%s.sserial.write",
                         hm2->llio->name);
    if (r < 0) {HM2_ERR("error adding pin %s.sserial.write. aborting\n",
                        hm2->llio->name);
        goto fail0;}
    r = hal_pin_u32_newf(HAL_IN, &(hm2->sserial.hal->parameter),
                         hm2->llio->comp_id, "%s.sserial.parameter",
                         hm2->llio->name);
    if (r < 0) {
        HM2_ERR("error adding pin %s.sserial.port. aborting\n",
                hm2->llio->name);
        goto fail0;}
    r = hal_pin_u32_newf(HAL_IN, &(hm2->sserial.hal->port),
                         hm2->llio->comp_id, "%s.sserial.port",
                         hm2->llio->name);
    if (r < 0) {
        HM2_ERR("error adding pin %s.sserial.parameter. aborting\n",
                hm2->llio->name);
        goto fail0;}
    r = hal_pin_u32_newf(HAL_IN, &(hm2->sserial.hal->channel),
                         hm2->llio->comp_id, "%s.sserial.channel",
                         hm2->llio->name);
    if (r < 0) {
        HM2_ERR("error adding pin %s.sserial.channel. aborting\n",
                hm2->llio->name);
        goto fail0;}
    r = hal_pin_u32_newf(HAL_IN, &(hm2->sserial.hal->value),
                         hm2->llio->comp_id, "%s.sserial.value",
                         hm2->llio->name);
    if (r < 0) { HM2_ERR("error adding pin %s.sserial.value. aborting\n",
                         hm2->llio->name);
        goto fail0;}
    r = hal_pin_u32_newf(HAL_OUT, &(hm2->sserial.hal->state),
                         hm2->llio->comp_id, "%s.sserial.state",
                         hm2->llio->name);
    if (r < 0) {HM2_ERR("error adding pin %s.sserial.state. aborting\n",
                        hm2->llio->name);
        goto fail0;}

    return 0;

fail0:
    return r;
}

void hm2_sserial_process_config(hostmot2_t *hm2, long period){

    static long timer;
    u32 buff;
    int chan, port, i;

    static hm2_sserial_instance_t *inst;
    static hm2_sserial_tram_t *tram;

    if (hm2->sserial.num_instances <= 0) return;

    chan = *hm2->sserial.hal->channel;
    port = *hm2->sserial.hal->port;

    // What follows is a state machine that handles the handshaking and communications
    // to allow reading and setting of the firmware parameters of arbitrary sserial
    // devices. It responds to tram-read data, so is in this function despite doing
    // some direct llio writes to non-TRAM registers.

    switch (*hm2->sserial.hal->state) {
        case 0: // idle
            if (*hm2->sserial.hal->read == 0 && *hm2->sserial.hal->write == 0) {
                return;
            }
            for (i = 0 ; i < hm2->sserial.num_instances &&
                 hm2->sserial.instance[i].module_index != port ; i++){
            }
            if (i >= hm2->sserial.num_instances) {
                HM2_ERR("sserial port %i not found\n", port);
                *hm2->sserial.hal->read = 0;
                *hm2->sserial.hal->write = 0;
                return;
            }
            inst = &hm2->sserial.instance[i];

            if (*inst->run) {
                HM2_ERR("It is not possible to set or read parameters on "
                        "active/running sserial ports\n");
                *hm2->sserial.hal->read = 0;
                *hm2->sserial.hal->write = 0;
                return;
            }

            if ((inst->tag_all & (1 << chan)) == 0) {
                HM2_ERR("No device to configure on sserial port %i channel %i\n",
                        port, chan);
                *hm2->sserial.hal->read = 0;
                *hm2->sserial.hal->write = 0;
                return;
            }

            tram = NULL ; // Make sure we don't accidentally configure the previous one

            if (inst->tag_8i20 & (1 << chan)) {
                for (i = 0
                     ; (i < inst->num_8i20) && (inst->tram_8i20[i].index != chan)
                     ; i++){
                }
                if (i >= inst->num_8i20) {
                    HM2_ERR("8i20 on sserial port %i, channel %i not found\n",
                            port, chan);
                    *hm2->sserial.hal->read = 0;
                    *hm2->sserial.hal->write = 0;
                    return;
                }
                tram = &inst->tram_8i20[i];
            }
            else if (inst->tag_7i64 & (1 << chan)) {
                for (i = 0
                     ; (i < inst->num_7i64) && (inst->tram_7i64[i].index != chan)
                     ; i++){}
                if (i >= inst->num_7i64) {
                    HM2_ERR("7i64 on sserial port %i, channel %i not found\n",
                            port, chan);
                    *hm2->sserial.hal->read = 0;
                    *hm2->sserial.hal->write = 0;
                    return;
                }
                tram = &inst->tram_7i64[i];
            }
            else if (inst->tag_auto & (1 << chan)) {
                for (i = 0
                     ; (i < inst->num_auto) && (inst->tram_auto[i].index != chan)
                     ; i++){}
                if (i >= inst->num_7i64) {
                    HM2_ERR("auto-config card on sserial port %i, channel %i "
                            "not found\n", port, chan);
                    *hm2->sserial.hal->read = 0;
                    *hm2->sserial.hal->write = 0;
                    return;
                }
                tram = &inst->tram_auto[i];
            }
            /***********************************************************
             // Add other sserial devices here...
             ************************************************************/

            if (tram == NULL) { // didn't find a tram...
                HM2_ERR("Unable to find a supported device for the specified "
                        "channel\n");
                *hm2->sserial.hal->read = 0;
                *hm2->sserial.hal->write = 0;
                return;
            }
            // valid port and channel, so act on the read/write requests
            if (*hm2->sserial.hal->parameter & 0xFF000000) {
                *hm2->sserial.hal->state = 1;}
            else if (*hm2->sserial.hal->write) {*hm2->sserial.hal->state = 9;}
            else if (*hm2->sserial.hal->read) { *hm2->sserial.hal->state = 5; }

            *inst->command_reg_write = 0xF00 | (1 << tram->index); // start in setup mode
            timer = 0;
            break;

        case 1: // eeprom or nvram access flag setup - wait for command clear
            *inst->command_reg_write = 0x80000000; // write mask
            if (*inst->run != 0) {
                *hm2->sserial.hal->state = 20;
                break;}
            timer += period;
            if (*inst->command_reg_read != 0) {
                if (timer > 50000000) {
                    *hm2->sserial.hal->state = 0;
                    *hm2->sserial.hal->read = 0;
                    HM2_ERR("SSerial setup mode start: Timeout in state 1\n");
                }
                break;
            }
            if (*inst->data_reg_read & (1 << tram->index)){
                *hm2->sserial.hal->state = 20;
                HM2_ERR("Channel-not-ready error in parameter write case 1\n"
                        "Data Reg = %08X\nCS Reg = %08X\n",
                        *inst->data_reg_read, *tram->reg_cs_read);
                //break;
            }
            buff = (*hm2->sserial.hal->parameter & 0xFF000000); //flag
            hm2->llio->write(hm2->llio, tram->reg_cs_addr, &buff, sizeof(u32));
            //reg_0 and CMD are always in the TRAM
            //..memory type
            *tram->reg_0_write = (*hm2->sserial.hal->parameter & 0x00FF0000)>>16;
            //..DoIt command
            *inst->command_reg_write = 0x1000 | (1 << tram->index);
            timer = 0;

            if (*hm2->sserial.hal->write) {*hm2->sserial.hal->state = 9;}
            else if (*hm2->sserial.hal->read) { *hm2->sserial.hal->state = 5; }
            else {
                HM2_ERR("Read or Write pins reset part-way through special "
                        "access mode setup, clearing access and aborting\n");
                *hm2->sserial.hal->state = 20;
            }
            break;

        case 5: // read request - wait for command clear
            *inst->command_reg_write = 0x80000000; // write mask
            if (*inst->run != 0) {
                *hm2->sserial.hal->state = 20;
                break;}
            timer += period;
            if (*inst->command_reg_read != 0) {
                if (timer > 50000000) {
                    *hm2->sserial.hal->state = 0;
                    *hm2->sserial.hal->read = 0;
                    HM2_ERR("SSerial setup param read: Timeout in state 5\n");
                }
                break;
            }
            if (*inst->data_reg_read & (1 << tram->index)){
                *hm2->sserial.hal->state = 20;
                HM2_ERR("Channel-not-ready error waiting to send param read"
                        "command (%x)\n", *inst->data_reg_read);
                break;
            }
            // read command setup
            buff = 0x45000000 | (*hm2->sserial.hal->parameter & 0x0000FFFF);
            hm2->llio->write(hm2->llio, tram->reg_cs_addr, &buff, sizeof(u32));
            // Do It command (written by tram)
            *inst->command_reg_write =  0x1000 | (1 << tram->index);
            timer = 0;
            *hm2->sserial.hal->state = 6;
            break;
        case 6: // waiting for read request ack.
            *inst->command_reg_write = 0x80000000; // write mask bit
            if (*inst->run != 0) { *hm2->sserial.hal->state = 0; return;}
            timer += period;
            if (*inst->command_reg_read != 0) {
                if (timer > 750000000) {
                    *hm2->sserial.hal->state = 20;
                    HM2_ERR("Channel-not-ready error in parameter write case 6\n"
                            "Data Reg = %08X\nCS Reg = %08X\n",
                            *inst->data_reg_read, *tram->reg_cs_read);
                    break;
                }
                break;
            }
            *hm2->sserial.hal->state = 20;
            if (*inst->data_reg_read & (1 << tram->index)){
                HM2_ERR("Channel-not-ready error in state 6: %x, %x\n"
                        "errror flags %d\n",
                        *inst->data_reg_read, (1 << tram->index),
                        (*tram->reg_cs_read & 0x0000FF00) >> 8);
                //              break;
            }
            // looks like success
            *hm2->sserial.hal->value = *tram->reg_0_read;
            break;

        case 9: // write command setup
            *inst->command_reg_write = 0x80000000; // write mask
            if (*inst->run != 0) {*hm2->sserial.hal->state = 0 ; return; }
            timer += period;
            if (*inst->command_reg_read != 0) {
                if (timer > 750000000) {
                    *hm2->sserial.hal->state = 20;
                    HM2_ERR("SSerial setup param write: Timeout in state 9\n");
                    break;
                }
            }
            if (*inst->data_reg_read & (1 << tram->index)){
                *hm2->sserial.hal->state = 20;
                HM2_ERR("Channel-not-ready error in parameter write case 9\n"
                        "Data Reg = %08X\nCS Reg = %08X\n",
                        *inst->data_reg_read, *tram->reg_cs_read);
                break;
            }
            // write command setup
            *tram->reg_0_write = *hm2->sserial.hal->value;
            buff = 0x65000000 | (*hm2->sserial.hal->parameter & 0x0000FFFF);
            hm2->llio->write(hm2->llio, tram->reg_cs_addr, &buff, sizeof(u32));
            // Do It command
            *inst->command_reg_write = 0x1000 | (1 << tram->index);
            timer = 0;
            *hm2->sserial.hal->state = 10;
            break;
        case 10: // waiting for write request ack.
            *inst->command_reg_write = 0x80000000; // write mask
            if (*inst->run != 0) {*hm2->sserial.hal->state = 0; return;}
            timer += period;
            if (*inst->command_reg_read != 0) {
                if (timer > 50000000) {
                    *hm2->sserial.hal->state = 20;
                    HM2_ERR("SSerial setup param write: Timeout in state 10\n");
                    break;
                }
                break;
            }
            *hm2->sserial.hal->state = 20;
            if (*inst->data_reg_read & (1 << tram->index)){
                HM2_ERR("Channel-not-ready error in parameter write case 10\n"
                        "Data Reg = %08X\nCS Reg = %08X\n",
                        *inst->data_reg_read, *tram->reg_cs_read);
            }
            break;
        case 20: // Clear flags and read/write pins
            *hm2->sserial.hal->write = 0;
            *hm2->sserial.hal->read = 0;

            buff = 0x0; // Clear MSB of CSR
            hm2->llio->write(hm2->llio, tram->reg_cs_addr, &buff, sizeof(u32));

            if (!(*hm2->sserial.hal->parameter & 0xFF000000)) {
                *inst->command_reg_write = 0x800;
                *hm2->sserial.hal->state = 22;
                break;
            }
            buff = (*hm2->sserial.hal->parameter & 0xFF000000); //flag
            hm2->llio->write(hm2->llio, tram->reg_cs_addr, &buff, sizeof(u32));
            *tram->reg_0_write = 0; //clear type flag
            *inst->command_reg_write = 0x1000 |  (1 << tram->index); // DoIt command
            timer = 0;
            *hm2->sserial.hal->state = 21;
            break;
        case 21:
            *inst->command_reg_write = 0x80000000; // write mask
            timer += period;
            if (*inst->command_reg_read != 0) {
                if (timer > 750000000) {
                    *inst->command_reg_write = 0x800; // stop request
                    *hm2->sserial.hal->state = 22;
                    HM2_ERR("Timeout in state 21, attempting to reset flags."
                            " suggest hard reset of cards (power off) \n");
                    break;
                }
                break;
            }
            if (*inst->data_reg_read & (1 << tram->index)){
                HM2_ERR("Channel-not-ready error in case 21, clearing flags\n"
                        "Data Reg = %08X\nCS Reg = %08X\n",
                        *inst->data_reg_read, *tram->reg_cs_read);
            }
            *inst->command_reg_write = 0x800; // stop request
            *hm2->sserial.hal->state = 22;
            break;
        case 22: // wait for port to stop
            *inst->command_reg_write = 0x80000000; // write mask
            timer += period;
            if (*inst->command_reg_read != 0) {
                if (timer > 220000000) {
                    *hm2->sserial.hal->state = 0;
                    HM2_ERR("Timeout waiting for port to stop \n");
                    break;
                }
                break;
            }
            *hm2->sserial.hal->state = 0;
            break;

        default:
            HM2_ERR("Unsupported state (%i) in sserial setup\n", *hm2->sserial.hal->state);
            *hm2->sserial.hal->state = 0;
            break;
    }
    if (*hm2->sserial.hal->state != 0){
        HM2_DBG("COM(r/w):%08x/%08x, DATA:%08x, CS:%08x, USER0:%08x\n", 
                *inst->command_reg_read, *inst->command_reg_write, 
                *inst->data_reg_read, buff, *tram->reg_0_write);
    }

}

void hm2_sserial_print_module(hostmot2_t *hm2) {
    int i;
    HM2_PRINT("SSerial: %d\n", hm2->sserial.num_instances);
    HM2_PRINT("  version %d\n", hm2->sserial.version);
    if (hm2->sserial.num_instances <= 0) return;
    for (i = 0; i < hm2->sserial.num_instances; i ++) {
        HM2_PRINT("    instance %d:\n", i);
        HM2_PRINT("        Command Addr 0x%04x\n", hm2->sserial.instance[i].command_reg_addr);
        HM2_PRINT("        Data Addr    0x%04x\n", hm2->sserial.instance[i].data_reg_addr);
        HM2_PRINT("        %d 8i20s\n", hm2->sserial.instance[i].num_8i20);
        HM2_PRINT("        %d 7i64s\n", hm2->sserial.instance[i].num_7i64);
    }
    HM2_PRINT("\n");
}

u32 hm2_sserial_get_param(hostmot2_t *hm2, hm2_sserial_tram_t *chan, int param){
    u32 data;

    if (0 > hm2_sserial_waitfor(hm2, chan->reg_cs_addr, 0x0000FF00, 20)){
        goto fail0;
    }

    data = 0x45000000 | param;
    hm2->llio->write(hm2->llio, chan->reg_cs_addr, &data, sizeof(u32));
    data = 0x1000 | (1 << chan->index);
    hm2->llio->write(hm2->llio, chan->reg_command_addr, &data, sizeof(u32));

    if (0 > hm2_sserial_waitfor(hm2, chan->reg_command_addr, 0xFFFFFFFF, 20)){
        goto fail0;
    }
    if (0 > hm2_sserial_waitfor(hm2, chan->reg_cs_addr, 0x00000FF00, 20)){
        goto fail0;
    }

    hm2->llio->read(hm2->llio, chan->reg_data_addr, &data, sizeof(u32));
    hm2->llio->read(hm2->llio, chan->reg_0_addr, &data, sizeof(u32));
    return data;
fail0:
    return -EINVAL;
}

int hm2_sserial_get_bytes(hostmot2_t *hm2, hm2_sserial_tram_t *chan, void *buffer, int addr, int size ){
    // Gets the bytes one at a time. This could be done more efficiently. 
    char *ptr;
    if (0 > hm2_sserial_waitfor(hm2, chan->reg_cs_addr, 0x0000FF00, 20)){
        goto fail0;
    }
    ptr = (char*)buffer;
    while(0 != size){ // -1 in size means "find null" for strings.
        
        u32 data = 0x4C000000 | addr++;
        hm2->llio->write(hm2->llio, chan->reg_cs_addr, &data, sizeof(u32));
        
        if (0 > hm2_sserial_waitfor(hm2, chan->reg_cs_addr, 0x0000FF00, 20)){
            goto fail0;
        }
        data = 0x1000 | (1 << chan->index);
        hm2->llio->write(hm2->llio, chan->reg_command_addr, &data, sizeof(u32));
        
        if (0 > hm2_sserial_waitfor(hm2, chan->reg_command_addr, 0xFFFFFFFF, 20)){
            goto fail0;
        }
        
        hm2->llio->read(hm2->llio, chan->reg_0_addr, &data, sizeof(u32));
        data &= 0x000000FF;
        size--;
        if (size < 0) { // string data
            if (data == 0 || size < (-HM2_SSERIAL_MAX_STRING_LENGTH)){
                size = 0; 
            } else {
                data |= 0x20; // lower case
            }
        }
        *(ptr++) = (unsigned char)data;
    }
    return addr;
        
fail0:
    HM2_ERR("Error trying to read config data in sserial_get_bytes\n");
    return -EINVAL;
}

int hm2_sserial_stopstart(hostmot2_t *hm2, hm2_module_descriptor_t *md, 
                          hm2_sserial_instance_t *inst, u32 start_mode){
    u32 buff, addr;
    int i = inst->module_index;
    int c;
    
    buff=0x800; //Stop All
    hm2->llio->write(hm2->llio, inst->command_reg_addr, &buff, sizeof(u32));
    if (hm2_sserial_waitfor(hm2, inst->command_reg_addr, 0xFFFFFFFF,51) < 0){
        return -EINVAL;
    }
    
    for (c = 0 ; c < inst->num_channels ; c++){
        if (hm2->config.sserial_modes[i][c] != 'x'){
            start_mode |= 1 << c;
            HM2_DBG("Start-mode = %x\n", start_mode);
            // CS addr - write card mode
            addr = md->base_address + 2 * md->register_stride
            + i * md->instance_stride + c * sizeof(u32);
            buff = (hm2->config.sserial_modes[i][c] - '0') << 24;
            hm2->llio->write(hm2->llio, addr, &buff, sizeof(u32));
        }
    }
    hm2->llio->write(hm2->llio, inst->command_reg_addr, &start_mode, sizeof(u32));
    if (hm2_sserial_waitfor(hm2, inst->command_reg_addr, 0xFFFFFFFF, 8000) < 0){
        return -EINVAL;
    }
    return 0;
}

int hm2_sserial_check_errors(hostmot2_t *hm2, hm2_sserial_tram_t *tram){
    u32 buff;
    int i;
    int err_flag = 0;
    u32 err_mask = 0xF300E1FF;
    const char *err_list[32] = {"CRC error", "Invalid cookie", "Overrun",
        "Timeout", "Extra character", "Serial Break Error", "Remote Fault", 
        "Too many errors", 
        
        "Remote fault", "unused", "unused", "unused", "unused", 
        "Communication error", "No Remote ID", "Communication Not Ready",
        
        "unused","unused","unused","unused","unused","unused","unused","unused",
        
        "Watchdog Fault", "No Enable", "Over Temperature", "Over Current", 
        "Over Voltage", "Under Voltage", "Illegal Remote Mode", "LBPCOM Fault"};
    
    hm2->llio->read(hm2->llio, tram->reg_data_addr, &buff, sizeof(u32));
    if (0 == (buff & (1 << tram->index))) {return 0;}
    
    hm2->llio->read(hm2->llio, tram->reg_cs_addr, &buff, sizeof(u32));
    buff &= err_mask;
    for (i = 31 ; i > 0 ; i--){
        if (buff & (1 << i)) {
            HM2_ERR("Smart serial card %s error = (%i) %s\n", 
                    tram->name, i, err_list[i]);
            err_flag = -EINVAL;
        }
    }
    return err_flag;
}

int hm2_sserial_waitfor(hostmot2_t *hm2, u32 addr, u32 mask, int ms){
    u64 t1, t2;
    u32 d;
    t1 = rtapi_get_time();
    do { // wait for addr to clear
        hm2->llio->read(hm2->llio, addr, &d, sizeof(u32));
        t2 = rtapi_get_time();
        if ((u32)(t2 - t1) > 1000000L * ms) {
            HM2_ERR("hm2_sserial_waitfor: Timeout (%dmS) waiting for addr %x &"
                    "mask %x val %x\n", ms, addr, mask, d & mask);
            addr += 0x100;
            hm2->llio->read(hm2->llio, addr, &d, sizeof(u32));
            HM2_ERR("DATA addr %x after timeout: %x\n", addr, d);
            return -1;
        }
    }while (d & mask);
    return 0;
}

void hm2_sserial_force_write(hostmot2_t *hm2){
    int i;
    u32 buff;
    for(i = 0; i < hm2->sserial.num_instances; i++){

        buff = 0x800;
        hm2->llio->write(hm2->llio, hm2->sserial.instance[i].command_reg_addr, &buff, sizeof(u32));
        *hm2->sserial.instance[i].run = 0;
        *hm2->sserial.instance[i].state = 0;
        hm2_sserial_waitfor(hm2, hm2->sserial.instance[i].command_reg_addr, 0xFFFFFFFF, 20);
        *hm2->sserial.instance[i].run = 1;
        *hm2->sserial.instance[i].command_reg_write = 0x80000000;
    }
}

void hm2_sserial_cleanup(hostmot2_t *hm2){
    int i;
    u32 buff;
    for (i = 1 ; i < hm2->sserial.num_instances; i++){
        //Shut down the sserial devices rather than leave that to the watchdog. 
        buff = 0x800;
        hm2->llio->write(hm2->llio,
                         hm2->sserial.instance[i].command_reg_addr,
                         &buff,
                         sizeof(u32));
        if (hm2->sserial.instance[i].tram_8i20 != NULL){
            hm2_sserial_8i20_cleanup(hm2);
        }
        if (hm2->sserial.instance[i].tram_7i64 != NULL){
            hm2_sserial_7i64_cleanup(hm2);
        }
        if (hm2->sserial.instance[i].tram_auto != NULL){
            hm2_sserial_auto_cleanup(hm2);
        }
    }
}


{'fraction_non_alphanumeric': 0.12975686517874954, 'fraction_numerical': 0.03780003989450121, 'mean_word_length': 1.4402109248242294, 'pattern_counts': {'":': 0, '<': 236, '<?xml version=': 0, '>': 1334, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 50, 'word_list_counts': {'cursed_substrings.json': 78, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7235436', 'n_tokens_mistral': 31034, 'n_tokens_neox': 28044, 'n_words': 6293}