/* $NetBSD: emcfanctlutil.c,v 1.1 2025/03/11 13:56:48 brad Exp $ */
/*
* Copyright (c) 2025 Brad Spencer <
[email protected]>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifdef __RCSID
__RCSID("$NetBSD: emcfanctlutil.c,v 1.1 2025/03/11 13:56:48 brad Exp $");
#endif
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <err.h>
#include <fcntl.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <dev/i2c/emcfanreg.h>
#include <dev/i2c/emcfaninfo.h>
#define EXTERN
#include "emcfanctl.h"
#include "emcfanctlconst.h"
#include "emcfanctlutil.h"
int
emcfan_read_register(int fd, uint8_t reg, uint8_t *res, bool debug)
{
int err;
err = lseek(fd, reg, SEEK_SET);
if (err != -1) {
err = read(fd, res, 1);
if (err == -1)
err = errno;
else
err = 0;
} else {
err = errno;
}
if (debug)
fprintf(stderr,"emcfan_read_register: reg=0x%02X, res=0x%02x, return err: %d\n",reg, *res, err);
return err;
}
int
emcfan_write_register(int fd, uint8_t reg, uint8_t value, bool debug)
{
int err;
err = lseek(fd, reg, SEEK_SET);
if (err != -1) {
err = write(fd, &value, 1);
if (err == -1)
err = errno;
else
err = 0;
} else {
err = errno;
}
if (debug)
fprintf(stderr,"emcfan_write_register: reg=0x%02X, value=0x%02X, return err: %d\n",reg, value, err);
return err;
}
int
emcfan_rmw_register(int fd, uint8_t reg, uint8_t value,
const struct emcfan_bits_translate translation[],
long unsigned int translation_size,
int tindex,
bool debug)
{
int err = 0;
uint8_t current, oldcurrent;
err = emcfan_read_register(fd, reg, &oldcurrent, debug);
if (err != 0)
return(err);
current = oldcurrent & ~translation[tindex].clear_mask;
current = current | translation[tindex].bit_mask;
if (debug)
fprintf(stderr,"tindex=%d, clear_mask=0x%02X 0x%02X, value=%d (0x%02X), bit_mask=0x%02X 0x%02X, oldcurrent=%d (0x%02X), new current=%d (0x%02X)\n",tindex,
translation[tindex].clear_mask,
(uint8_t)~translation[tindex].clear_mask,
value,value,
translation[tindex].bit_mask,
(uint8_t)~translation[tindex].bit_mask,
oldcurrent,oldcurrent,current,current);
err = emcfan_write_register(fd, reg, current, debug);
return(err);
}
char *
emcfan_product_to_name(uint8_t product_id)
{
for(long unsigned int i = 0;i < __arraycount(emcfan_chip_infos); i++)
if (product_id == emcfan_chip_infos[i].product_id)
return(__UNCONST(emcfan_chip_infos[i].name));
return(NULL);
}
void
emcfan_family_to_name(int family, char *name, int len)
{
switch(family) {
case EMCFAN_FAMILY_210X:
snprintf(name, len, "%s", "EMC210x");
break;
case EMCFAN_FAMILY_230X:
snprintf(name, len, "%s", "EMC230x");
break;
case EMCFAN_FAMILY_UNKNOWN:
default:
snprintf(name, len, "%s", "UNKNOWN");
break;
}
return;
}
int
emcfan_find_info(uint8_t product)
{
for(long unsigned int i = 0;i < __arraycount(emcfan_chip_infos); i++)
if (product == emcfan_chip_infos[i].product_id)
return(i);
return(-1);
}
bool
emcfan_reg_is_real(int iindex, uint8_t reg)
{
int segment;
uint64_t index;
segment = reg / 64;
index = reg % 64;
return(emcfan_chip_infos[iindex].register_void[segment] & ((uint64_t)1 << index));
}
static int
emcfan_hunt_by_name(const struct emcfan_registers the_registers[], long unsigned int the_registers_size, char *the_name)
{
int r = -1;
for(long unsigned int i = 0;i < the_registers_size;i++) {
if (strcmp(the_name, the_registers[i].name) == 0) {
r = the_registers[i].reg;
break;
}
}
return(r);
}
int
emcfan_reg_by_name(uint8_t product_id, int product_family, char *name)
{
int r = -1;
switch(product_family) {
case EMCFAN_FAMILY_210X:
switch(product_id) {
case EMCFAN_PRODUCT_2101:
case EMCFAN_PRODUCT_2101R:
r = emcfan_hunt_by_name(emcfanctl_2101_registers,__arraycount(emcfanctl_2101_registers),name);
break;
case EMCFAN_PRODUCT_2103_1:
r = emcfan_hunt_by_name(emcfanctl_2103_1_registers,__arraycount(emcfanctl_2103_1_registers),name);
break;
case EMCFAN_PRODUCT_2103_24:
r = emcfan_hunt_by_name(emcfanctl_2103_24_registers,__arraycount(emcfanctl_2103_24_registers),name);
break;
case EMCFAN_PRODUCT_2104:
r = emcfan_hunt_by_name(emcfanctl_2104_registers,__arraycount(emcfanctl_2104_registers),name);
break;
case EMCFAN_PRODUCT_2106:
r = emcfan_hunt_by_name(emcfanctl_2106_registers,__arraycount(emcfanctl_2106_registers),name);
break;
default:
printf("UNSUPPORTED YET %d\n",product_id);
exit(99);
break;
};
break;
case EMCFAN_FAMILY_230X:
r = emcfan_hunt_by_name(emcfanctl_230x_registers,__arraycount(emcfanctl_230x_registers),name);
break;
};
return(r);
}
static const char *
emcfan_hunt_by_reg(const struct emcfan_registers the_registers[], long unsigned int the_registers_size, uint8_t the_reg)
{
const char *r = NULL;
for(long unsigned int i = 0;i < the_registers_size;i++) {
if (the_reg == the_registers[i].reg) {
r = the_registers[i].name;
break;
}
}
return(r);
}
const char *
emcfan_regname_by_reg(uint8_t product_id, int product_family, uint8_t reg)
{
const char *r = NULL;
switch(product_family) {
case EMCFAN_FAMILY_210X:
switch(product_id) {
case EMCFAN_PRODUCT_2101:
case EMCFAN_PRODUCT_2101R:
r = emcfan_hunt_by_reg(emcfanctl_2101_registers,__arraycount(emcfanctl_2101_registers),reg);
break;
case EMCFAN_PRODUCT_2103_1:
r = emcfan_hunt_by_reg(emcfanctl_2103_1_registers,__arraycount(emcfanctl_2103_1_registers),reg);
break;
case EMCFAN_PRODUCT_2103_24:
r = emcfan_hunt_by_reg(emcfanctl_2103_24_registers,__arraycount(emcfanctl_2103_24_registers),reg);
break;
case EMCFAN_PRODUCT_2104:
r = emcfan_hunt_by_reg(emcfanctl_2104_registers,__arraycount(emcfanctl_2104_registers),reg);
break;
case EMCFAN_PRODUCT_2106:
r = emcfan_hunt_by_reg(emcfanctl_2106_registers,__arraycount(emcfanctl_2106_registers),reg);
break;
default:
printf("UNSUPPORTED YET %d\n",product_id);
exit(99);
break;
};
break;
case EMCFAN_FAMILY_230X:
r = emcfan_hunt_by_reg(emcfanctl_230x_registers,__arraycount(emcfanctl_230x_registers),reg);
break;
};
return(r);
}
int
find_translated_blob_by_bits_instance(const struct emcfan_bits_translate translation[],
long unsigned int translation_size,
uint8_t bits, int instance)
{
int r = -10191;
uint8_t clear_mask;
uint8_t b;
for(long unsigned int i = 0;i < translation_size;i++) {
if (instance == translation[i].instance) {
clear_mask = translation[i].clear_mask;
b = bits & clear_mask;
if (b == translation[i].bit_mask) {
r = i;
break;
}
}
}
return(r);
}
int
find_translated_bits_by_hint_instance(const struct emcfan_bits_translate translation[],
long unsigned int translation_size,
int human_value, int instance)
{
int r = -10191;
for(long unsigned int i = 0;i < translation_size;i++) {
if (instance == translation[i].instance &&
human_value == translation[i].human_int) {
r = i;
break;
}
}
return(r);
}
int
find_translated_bits_by_hint(const struct emcfan_bits_translate translation[],
long unsigned int translation_size,
int human_value)
{
int r = -10191;
for(long unsigned int i = 0;i < translation_size;i++) {
if (human_value == translation[i].human_int) {
r = i;
break;
}
}
return(r);
}
int
find_translated_bits_by_str(const struct emcfan_bits_translate translation[],
long unsigned int translation_size,
char *human_str)
{
int r = -10191;
for(long unsigned int i = 0;i < translation_size;i++) {
if (strcmp(human_str,translation[i].human_str) == 0) {
r = i;
break;
}
}
return(r);
}
int
find_translated_bits_by_str_instance(const struct emcfan_bits_translate translation[],
long unsigned int translation_size,
char *human_str, int instance)
{
int r = -10191;
for(long unsigned int i = 0;i < translation_size;i++) {
if (instance == translation[i].instance &&
strcmp(human_str,translation[i].human_str) == 0) {
r = i;
break;
}
}
return(r);
}
int
find_human_int(const struct emcfan_bits_translate translation[],
long unsigned int translation_size,
uint8_t bits)
{
int r = -10191;
for(long unsigned int i = 0;i < translation_size;i++) {
if (bits == translation[i].bit_mask) {
r = translation[i].human_int;
break;
}
}
return(r);
}
char *
find_human_str(const struct emcfan_bits_translate translation[],
long unsigned int translation_size,
uint8_t bits)
{
char *r = NULL;
for(long unsigned int i = 0;i < translation_size;i++) {
if (bits == translation[i].bit_mask) {
r = __UNCONST(translation[i].human_str);
break;
}
}
return(r);
}