📄 hostmot2.c
字号:
//// Copyright (C) 2007-2008 Sebastian Kuzminsky//// 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//#include <linux/slab.h>#include <linux/ctype.h>#include "rtapi.h"#include "rtapi_app.h"#include "rtapi_string.h"#include "rtapi_math.h"#include "hal.h"#include "hostmot2.h"#include "bitfile.h"MODULE_INFO(emc2, "component:hostmot2:RTAI driver for the HostMot2 firmware from Mesa Electronics.");MODULE_INFO(emc2, "funct:read:1:Read all registers.");MODULE_INFO(emc2, "funct:write:1:Write all registers.");MODULE_INFO(emc2, "funct:pet_watchdog:0:Pet the watchdog to keep it from biting us for a while.");MODULE_INFO(emc2, "license:GPL");MODULE_LICENSE("GPL");int debug_idrom = 0;RTAPI_MP_INT(debug_idrom, "Developer/debug use only! Enable debug logging of the HostMot2\nIDROM header.");int debug_module_descriptors = 0;RTAPI_MP_INT(debug_module_descriptors, "Developer/debug use only! Enable debug logging of the HostMot2\nModule Descriptors.");int debug_modules = 0;RTAPI_MP_INT(debug_modules, "Developer/debug use only! Enable debug logging of the HostMot2\nModules used.");// this keeps track of all the hm2 instances that have been registered by// the low-level driversstruct list_head hm2_list;static int comp_id;// // functions exported to EMC// static void hm2_read(void *void_hm2, long period) { hostmot2_t *hm2 = void_hm2; // if there are comm problems, wait for the user to fix it if ((*hm2->llio->io_error) != 0) return; // if the watchdog has bit, wait for the user to reset it if ((hm2->watchdog.num_instances == 1) && (*hm2->watchdog.instance[0].hal.pin.has_bit != 0)) return; hm2_tram_read(hm2); if ((*hm2->llio->io_error) != 0) return; hm2_ioport_gpio_process_tram_read(hm2); hm2_encoder_process_tram_read(hm2, period); hm2_stepgen_process_tram_read(hm2, period); hm2_raw_read(hm2);}static void hm2_write(void *void_hm2, long period) { hostmot2_t *hm2 = void_hm2; // if there are comm problems, wait for the user to fix it if ((*hm2->llio->io_error) != 0) return; // if the watchdog has bit, wait for the user to reset it if ((hm2->watchdog.num_instances == 1) && (*hm2->watchdog.instance[0].hal.pin.has_bit != 0)) return; hm2_ioport_gpio_prepare_tram_write(hm2); hm2_pwmgen_prepare_tram_write(hm2); hm2_stepgen_prepare_tram_write(hm2, period); hm2_tram_write(hm2); // these usually do nothing // they only write to the FPGA if certain pins & params have changed hm2_ioport_write(hm2); // handles gpio.is_output but not gpio.out (that's done in tram_write() above) hm2_watchdog_write(hm2); // in case the user has written to the watchdog.timeout_ns param hm2_pwmgen_write(hm2); // update pwmgen registers if needed hm2_stepgen_write(hm2); // update stepgen registers if needed hm2_encoder_write(hm2); // update ctrl register if needed hm2_raw_write(hm2);}static void hm2_read_gpio(void *void_hm2, long period) { hostmot2_t *hm2 = void_hm2; // if there are comm problems, wait for the user to fix it if ((*hm2->llio->io_error) != 0) return; // if the watchdog has bit, wait for the user to reset it if ((hm2->watchdog.num_instances == 1) && (*hm2->watchdog.instance[0].hal.pin.has_bit != 0)) return; hm2_ioport_gpio_read(hm2);}static void hm2_write_gpio(void *void_hm2, long period) { hostmot2_t *hm2 = void_hm2; // if there are comm problems, wait for the user to fix it if ((*hm2->llio->io_error) != 0) return; // if the watchdog has bit, wait for the user to reset it if ((hm2->watchdog.num_instances == 1) && (*hm2->watchdog.instance[0].hal.pin.has_bit != 0)) return; hm2_ioport_gpio_write(hm2);}// // misc little helper functions//// FIXME: the static automatic string makes this function non-reentrantconst char *hm2_hz_to_mhz(u32 freq_hz) { static char mhz_str[20]; int freq_mhz, freq_mhz_fractional; freq_mhz = freq_hz / (1000*1000); freq_mhz_fractional = (freq_hz / 1000) % 1000; sprintf(mhz_str, "%d.%03d", freq_mhz, freq_mhz_fractional); return mhz_str;}// FIXME: the static automatic string makes this function non-reentrantconst char *hm2_get_general_function_name(int gtag) { switch (gtag) { case HM2_GTAG_WATCHDOG: return "Watchdog"; case HM2_GTAG_IOPORT: return "IOPort"; case HM2_GTAG_ENCODER: return "Encoder"; case HM2_GTAG_STEPGEN: return "StepGen"; case HM2_GTAG_PWMGEN: return "PWMGen"; case HM2_GTAG_TRANSLATIONRAM: return "TranslationRAM"; default: { static char unknown[100]; rtapi_snprintf(unknown, 100, "(unknown-gtag-%d)", gtag); return unknown; } }}static int hm2_parse_config_string(hostmot2_t *hm2, char *config_string) { // default is to enable everything in the firmware hm2->config.num_encoders = -1; hm2->config.num_pwmgens = -1; hm2->config.num_stepgens = -1; hm2->config.enable_raw = 0; hm2->config.firmware = NULL; if (config_string == NULL) return 0; DBG("parsing config string \"%s\"\n", config_string); do { char *token; char *endp; token = strsep(&config_string, " "); if (token == NULL) break; if (token[0] == '\0') { if ((config_string == NULL) || (config_string[0] == '\0')) break; continue; } if (strncmp(token, "num_encoders=", 13) == 0) { token += 13; hm2->config.num_encoders = simple_strtol(token, &endp, 0); } else if (strncmp(token, "num_pwmgens=", 12) == 0) { token += 12; hm2->config.num_pwmgens = simple_strtol(token, &endp, 0); } else if (strncmp(token, "num_stepgens=", 13) == 0) { token += 13; hm2->config.num_stepgens = simple_strtol(token, &endp, 0); } else if (strncmp(token, "enable_raw", 10) == 0) { token += 10; hm2->config.enable_raw = 1; } else if (strncmp(token, "firmware=", 9) == 0) { hm2->config.firmware = token + 9; } else { ERR("invalid token in config string: \"%s\"\n", token); return -EINVAL; } } while (1); DBG("final config:\n"); DBG(" num_encoders=%d\n", hm2->config.num_encoders); DBG(" num_pwmgens=%d\n", hm2->config.num_pwmgens); DBG(" num_stepgens=%d\n", hm2->config.num_stepgens); DBG(" enable_raw=%d\n", hm2->config.enable_raw); DBG(" firmware=%s\n", hm2->config.firmware ? hm2->config.firmware : "(NULL)"); return 0;}// // functions for dealing with the idrom// static void hm2_print_idrom(hostmot2_t *hm2) { PRINT("IDRom:\n"); if (hm2->idrom.idrom_type == 2) { PRINT(" IDRom Type: 0x%08X\n", hm2->idrom.idrom_type); } else { PRINT(" IDRom Type: 0x%08X ***** Expected 2! Continuing anyway! *****\n", hm2->idrom.idrom_type); } if (hm2->idrom.offset_to_modules == 0x40) { PRINT(" Offset to Modules: 0x%08X\n", hm2->idrom.offset_to_modules); } else { PRINT(" Offset to Modules: 0x%08X ***** Expected 0x40! Continuing anyway *****\n", hm2->idrom.offset_to_modules); } if (hm2->idrom.offset_to_pin_desc == 0x200) { PRINT(" Offset to Pin Description: 0x%08X\n", hm2->idrom.offset_to_pin_desc); } else { PRINT(" Offset to Pin Description: 0x%08X ***** Expected 0x200! Continuing anyway! *****\n", hm2->idrom.offset_to_pin_desc); } PRINT( " Board Name: %c%c%c%c%c%c%c%c\n", hm2->idrom.board_name[0], hm2->idrom.board_name[1], hm2->idrom.board_name[2], hm2->idrom.board_name[3], hm2->idrom.board_name[4], hm2->idrom.board_name[5], hm2->idrom.board_name[6], hm2->idrom.board_name[7] ); PRINT(" FPGA Size: %u\n", hm2->idrom.fpga_size); PRINT(" FPGA Pins: %u\n", hm2->idrom.fpga_pins); PRINT(" IO Ports: %u\n", hm2->idrom.io_ports); PRINT(" IO Width: %u\n", hm2->idrom.io_width); if (hm2->idrom.port_width == 24) { PRINT(" Port Width: %u\n", hm2->idrom.port_width); } else { PRINT(" Port Width: %u ***** Expected 24! Continuing anyway! *****\n", hm2->idrom.port_width); } PRINT( " Clock Low: %d Hz (%d KHz, %d MHz)\n", hm2->idrom.clock_low, (hm2->idrom.clock_low / 1000), (hm2->idrom.clock_low / (1000 * 1000)) ); PRINT( " Clock High: %d Hz (%d KHz, %d MHz)\n", hm2->idrom.clock_high, (hm2->idrom.clock_high / 1000), (hm2->idrom.clock_high / (1000 * 1000)) ); PRINT(" Instance Stride 0: 0x%08X\n", hm2->idrom.instance_stride_0); PRINT(" Instance Stride 1: 0x%08X\n", hm2->idrom.instance_stride_1); PRINT(" Register Stride 0: 0x%08X\n", hm2->idrom.register_stride_0); PRINT(" Register Stride 1: 0x%08X\n", hm2->idrom.register_stride_1); }static int hm2_read_idrom(hostmot2_t *hm2) { // // find the idrom offset // if (!hm2->llio->read(hm2->llio, HM2_ADDR_IDROM_OFFSET, &hm2->idrom_offset, 2)) { ERR("error reading IDROM Offset\n"); return -EIO; } // // first read in the idrom type to make sure we know how to deal with it // if (!hm2->llio->read(hm2->llio, hm2->idrom_offset, &hm2->idrom.idrom_type, sizeof(hm2->idrom.idrom_type))) { ERR("error reading IDROM type\n"); return -EIO; } if (hm2->idrom.idrom_type != 2) { ERR("invalid IDROM type %d, expected 2, aborting load\n", hm2->idrom.idrom_type); return -EINVAL; } // // ok, read in the whole thing // if (!hm2->llio->read(hm2->llio, hm2->idrom_offset, &hm2->idrom, sizeof(hm2->idrom))) { ERR("error reading IDROM\n"); return -EIO; } // // verify the idrom we read // if (hm2->idrom.port_width != 24) { ERR("invalid IDROM PortWidth %d, expected 24, aborting load\n", hm2->idrom.port_width); hm2_print_idrom(hm2); return -EINVAL; } if (hm2->idrom.io_width != (hm2->idrom.io_ports * hm2->idrom.port_width)) { ERR( "IDROM IOWidth is %d, but IDROM IOPorts is %d and IDROM PortWidth is %d (inconsistent firmware), aborting driver load\n", hm2->idrom.io_width, hm2->idrom.io_ports, hm2->idrom.port_width ); return -EINVAL; } if (hm2->idrom.io_ports != hm2->llio->num_ioport_connectors) { ERR( "IDROM IOPorts is %d but llio num_ioport_connectors is %d, driver and firmware are inconsistent, aborting driver load\n", hm2->idrom.io_ports, hm2->llio->num_ioport_connectors ); return -EINVAL; } if (hm2->idrom.clock_low < 1e6) { ERR( "IDROM ClockLow is %d, that's too low, aborting driver load\n", hm2->idrom.clock_low ); return -EINVAL; } if (hm2->idrom.clock_high < 1e6) { ERR( "IDROM ClockHigh is %d, that's too low, aborting driver load\n", hm2->idrom.clock_high ); return -EINVAL; } if (debug_idrom) { hm2_print_idrom(hm2); } return 0;}// reads the Module Descriptors// doesnt do any validation or parsing or anything, that's in hm2_parse_module_descriptors(), which comes nextstatic int hm2_read_module_descriptors(hostmot2_t *hm2) { int addr = hm2->idrom_offset + hm2->idrom.offset_to_modules; for ( hm2->num_mds = 0; hm2->num_mds < HM2_MAX_MODULE_DESCRIPTORS; hm2->num_mds ++, addr += 12 ) { u32 d[3]; hm2_module_descriptor_t *md = &hm2->md[hm2->num_mds]; if (!hm2->llio->read(hm2->llio, addr, d, 12)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -