#include <rtapi_slab.h>
#include "rtapi.h"
#include "rtapi_string.h"
#include "rtapi_math.h"
#include "hal.h"
#include "hal/drivers/mesa-hostmot2/hostmot2.h"
void hm2_watchdog_process_tram_read(hostmot2_t *hm2) {
if (hm2->watchdog.num_instances == 0) return;
if ((*hm2->llio->io_error) != 0) return;
if (hm2->llio->needs_reset != 0) return;
if (hm2->watchdog.status_reg[0] & 0x1) {
HM2_ERR("Watchdog has bit! (set the .has-bit pin to False to resume)\n");
*hm2->watchdog.instance[0].hal.pin.has_bit = 1;
hm2->llio->needs_reset = 1;
}
}
int hm2_watchdog_parse_md(hostmot2_t *hm2, int md_index) {
hm2_module_descriptor_t *md = &hm2->md[md_index];
int r;
if (!hm2_md_is_consistent_or_complain(hm2, md_index, 0, 3, 4, 0)) {
HM2_ERR("inconsistent Module Descriptor!\n");
return -EINVAL;
}
if (hm2->watchdog.num_instances != 0) {
HM2_ERR(
"found duplicate Module Descriptor for %s (inconsistent firmware), not loading driver\n",
hm2_get_general_function_name(md->gtag)
);
return -EINVAL;
}
if (md->instances != 1) {
HM2_PRINT("MD declares %d watchdogs! only using the first one...\n", md->instances);
}
hm2->watchdog.num_instances = 1;
hm2->watchdog.instance = (hm2_watchdog_instance_t *)hal_malloc(hm2->watchdog.num_instances * sizeof(hm2_watchdog_instance_t));
if (hm2->watchdog.instance == NULL) {
HM2_ERR("out of memory!\n");
r = -ENOMEM;
goto fail0;
}
hm2->watchdog.clock_frequency = md->clock_freq;
hm2->watchdog.version = md->version;
hm2->watchdog.timer_addr = md->base_address + (0 * md->register_stride);
hm2->watchdog.status_addr = md->base_address + (1 * md->register_stride);
hm2->watchdog.reset_addr = md->base_address + (2 * md->register_stride);
r = hm2_register_tram_read_region(hm2, hm2->watchdog.status_addr, (hm2->watchdog.num_instances * sizeof(rtapi_u32)), &hm2->watchdog.status_reg);
if (r < 0) {
HM2_ERR("error registering tram read region for watchdog (%d)\n", r);
goto fail0;
}
r = hm2_register_tram_write_region(hm2, hm2->watchdog.reset_addr, sizeof(rtapi_u32), &hm2->watchdog.reset_reg);
if (r < 0) {
HM2_ERR("error registering tram write region for watchdog (%d)!\n", r);
goto fail0;
}
hm2->watchdog.timer_reg = (rtapi_u32 *)rtapi_kmalloc(hm2->watchdog.num_instances * sizeof(rtapi_u32), RTAPI_GFP_KERNEL);
if (hm2->watchdog.timer_reg == NULL) {
HM2_ERR("out of memory!\n");
r = -ENOMEM;
goto fail0;
}
r = hal_pin_bit_newf(
HAL_IO,
&(hm2->watchdog.instance[0].hal.pin.has_bit),
hm2->llio->comp_id,
"%s.watchdog.has_bit",
hm2->llio->name
);
if (r < 0) {
HM2_ERR("error adding pin, aborting\n");
r = -EINVAL;
goto fail1;
}
r = hal_param_u32_newf(
HAL_RW,
&(hm2->watchdog.instance[0].hal.param.timeout_ns),
hm2->llio->comp_id,
"%s.watchdog.timeout_ns",
hm2->llio->name
);
if (r < 0) {
HM2_ERR("error adding param, aborting\n");
r = -EINVAL;
goto fail1;
}
*hm2->watchdog.instance[0].hal.pin.has_bit = 0;
hm2->watchdog.instance[0].hal.param.timeout_ns = 5 * 1000 * 1000; hm2->watchdog.instance[0].enable = 0;
return hm2->watchdog.num_instances;
fail1:
rtapi_kfree(hm2->watchdog.timer_reg);
fail0:
hm2->watchdog.num_instances = 0;
return r;
}
void hm2_watchdog_print_module(hostmot2_t *hm2) {
int i;
HM2_PRINT("Watchdog: %d\n", hm2->watchdog.num_instances);
if (hm2->watchdog.num_instances <= 0) return;
HM2_PRINT(" clock_frequency: %d Hz (%s MHz)\n", hm2->watchdog.clock_frequency, hm2_hz_to_mhz(hm2->watchdog.clock_frequency));
HM2_PRINT(" version: %d\n", hm2->watchdog.version);
HM2_PRINT(" timer_addr: 0x%04X\n", hm2->watchdog.timer_addr);
HM2_PRINT(" status_addr: 0x%04X\n", hm2->watchdog.status_addr);
HM2_PRINT(" reset_addr: 0x%04X\n", hm2->watchdog.reset_addr);
for (i = 0; i < hm2->watchdog.num_instances; i ++) {
HM2_PRINT(" instance %d:\n", i);
HM2_PRINT(" param timeout_ns = %u\n", hm2->watchdog.instance[i].hal.param.timeout_ns);
HM2_PRINT(" pin has_bit = %d\n", (*hm2->watchdog.instance[i].hal.pin.has_bit));
HM2_PRINT(" reg timer = 0x%08X\n", hm2->watchdog.timer_reg[i]);
}
}
void hm2_watchdog_cleanup(hostmot2_t *hm2) {
if (hm2->watchdog.num_instances <= 0) return;
if (hm2->watchdog.timer_reg != NULL) rtapi_kfree(hm2->watchdog.timer_reg);
}
void hm2_watchdog_prepare_tram_write(hostmot2_t *hm2) {
if (hm2->watchdog.num_instances <= 0) return;
hm2->watchdog.reset_reg[0] = 0x5a000000;
}
void hm2_watchdog_force_write(hostmot2_t *hm2) {
rtapi_u64 tmp;
if (hm2->watchdog.num_instances != 1) return;
if (hm2->watchdog.instance[0].enable == 0) {
hm2->watchdog.timer_reg[0] = 0x80000000;
} else {
tmp = (hm2->watchdog.instance[0].hal.param.timeout_ns * ((double)hm2->watchdog.clock_frequency / (double)(1000 * 1000 * 1000))) - 1;
if (tmp < 0x80000000) {
hm2->watchdog.timer_reg[0] = tmp;
} else {
tmp = 0x7FFFFFFF;
hm2->watchdog.timer_reg[0] = tmp;
hm2->watchdog.instance[0].hal.param.timeout_ns = (tmp + 1) / ((double)hm2->watchdog.clock_frequency / (double)(1000 * 1000 * 1000));
HM2_ERR("requested watchdog timeout is out of range, setting it to max: %u ns\n", hm2->watchdog.instance[0].hal.param.timeout_ns);
}
}
hm2->llio->write(hm2->llio, hm2->watchdog.timer_addr, hm2->watchdog.timer_reg, (hm2->watchdog.num_instances * sizeof(rtapi_u32)));
hm2->watchdog.instance[0].written_timeout_ns = hm2->watchdog.instance[0].hal.param.timeout_ns;
hm2->watchdog.instance[0].written_enable = hm2->watchdog.instance[0].enable;
hm2->llio->write(hm2->llio, hm2->watchdog.status_addr, hm2->watchdog.status_reg, sizeof(rtapi_u32));
}
void hm2_watchdog_write(hostmot2_t *hm2, long period_ns) {
if (hm2->watchdog.num_instances != 1) return;
if ((*hm2->llio->io_error) != 0) return;
if (*hm2->watchdog.instance[0].hal.pin.has_bit) return;
hm2->watchdog.instance[0].enable = 1;
if (hm2->llio->needs_reset || hm2->llio->needs_soft_reset) {
if(hm2->llio->needs_reset)
HM2_PRINT("trying to recover from IO error or Watchdog bite\n");
hm2->watchdog.status_reg[0] = 0;
hm2_force_write(hm2);
if ((*hm2->llio->io_error) != 0) {
HM2_PRINT("error recovery failed\n");
return;
}
if(hm2->llio->needs_reset)
HM2_PRINT("error recover successful!\n");
hm2->llio->needs_reset = hm2->llio->needs_soft_reset = 0;
}
if (
(hm2->watchdog.instance[0].hal.param.timeout_ns == hm2->watchdog.instance[0].written_timeout_ns)
&&
(hm2->watchdog.instance[0].enable == hm2->watchdog.instance[0].written_enable)
) {
return;
}
if (hm2->watchdog.instance[0].hal.param.timeout_ns < (1.5 * period_ns)) {
HM2_PRINT(
"Watchdog timeout (%u ns) is dangerously short compared to hm2_write() period (%ld ns)\n",
hm2->watchdog.instance[0].hal.param.timeout_ns,
period_ns
);
}
hm2_watchdog_force_write(hm2);
}