blob: 04d0ebe69e7c17542111fe1240c0aa52c7860e17 [file] [log] [blame]
/* Access functions for the per-SIM-slot NCN8025 chip card interface,
* which is controlled via (half) a SX1503 I2C GPIO expander.
*
* (C) 2019 by Harald Welte <laforge@gnumonks.org>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <utils_assert.h>
#include <utils.h>
#include "atmel_start_pins.h"
#include "octsim_i2c.h"
#include "ncn8025.h"
#define SX1503_ADDR 0x20
/*! translate from ncn8025_settings into SX1503 register value */
static uint8_t ncn8025_encode(const struct ncn8025_settings *set)
{
uint8_t reg = 0;
if (!set->rstin)
reg |= 0x01;
if (!set->cmdvcc)
reg |= 0x02;
if (set->clkdiv & 1)
reg |= 0x04;
if (set->clkdiv & 2)
reg |= 0x08;
if (set->vsel & 1)
reg |= 0x10;
if (set->vsel & 2)
reg |= 0x20;
if (set->led)
reg |= 0x80;
return reg;
}
/*! translate from register value to ncn8025_settings */
static int ncn8025_decode(uint8_t reg, struct ncn8025_settings *set)
{
memset(set, 0, sizeof(*set));
if (!(reg & 0x01))
set->rstin = true;
if (!(reg & 0x02))
set->cmdvcc = true;
if (reg & 0x04)
set->clkdiv |= 0x01;
if (reg & 0x08)
set->clkdiv |= 0x02;
if (reg & 0x10)
set->vsel |= 0x01;
if (reg & 0x20)
set->vsel |= 0x02;
if (!(reg & 0x40))
set->simpres = true;
if ((reg & 0x80))
set->led = true;
return 0;
}
static const struct i2c_adapter *slot2adapter(unsigned int slot)
{
unsigned int idx = slot / 2;
ASSERT(idx < ARRAY_SIZE(i2c));
return &i2c[idx];
}
static const uint8_t slot2data_reg(unsigned int slot)
{
if (slot & 1)
return 0x00;
else
return 0x01;
}
static const uint8_t slot2dir_reg(unsigned int slot)
{
if (slot & 1)
return 0x02;
else
return 0x03;
}
static const uint8_t slot2int_pin(unsigned int slot)
{
static const uint8_t slot2pin[8] = { SIM0_INT, SIM1_INT, SIM2_INT, SIM3_INT,
SIM4_INT, SIM5_INT, SIM6_INT, SIM7_INT };
ASSERT(slot < ARRAY_SIZE(slot2pin));
return slot2pin[slot];
}
bool ncn8025_interrupt_level(uint8_t slot)
{
uint8_t pin = slot2int_pin(slot);
uint32_t pin_values = hri_port_read_IN_reg(PORT, GPIO_PORT(pin));
return (bool)(pin_values & (0x01U << GPIO_PIN(pin)));
}
/*! Set a given NCN8025 as described in 'set'.
* \param[in] slot Slot number (0..7)
* \param[in] set Settings that shall be written
* \returns 0 on success; negative on error */
int ncn8025_set(uint8_t slot, const struct ncn8025_settings *set)
{
const struct i2c_adapter *adap = slot2adapter(slot);
uint8_t reg = slot2data_reg(slot);
uint8_t raw = ncn8025_encode(set);
return i2c_write_reg(adap, SX1503_ADDR, reg, raw);
}
/*! Get a given NCN8025 state from the chip.
* \param[in] slot Slot number (0..7)
* \param[out] set Settings that are retrieved
* \returns 0 on success; negative on error */
int ncn8025_get(uint8_t slot, struct ncn8025_settings *set)
{
const struct i2c_adapter *adap = slot2adapter(slot);
uint8_t reg = slot2data_reg(slot);
int rc;
rc = i2c_read_reg(adap, SX1503_ADDR, reg);
if (rc < 0)
return rc;
rc = ncn8025_decode(rc, set);
set->interrupt = ncn8025_interrupt_level(slot);
return rc;
}
/*! default settings we use at start-up: powered off, in reset, slowest clock, 3V */
static const struct ncn8025_settings def_settings = {
.rstin = true,
.cmdvcc = false,
.led = false,
.clkdiv = SIM_CLKDIV_8,
.vsel = SIM_VOLT_3V0,
};
/*! Initialize a given NCN8025/slot. */
int ncn8025_init(unsigned int slot)
{
const struct i2c_adapter *adap = slot2adapter(slot);
uint8_t reg = slot2dir_reg(slot);
int rc;
/* IO6 of each bank is input (!PRESENT), rest are outputs */
rc = i2c_write_reg(adap, SX1503_ADDR, reg, 0x40);
if (rc < 0)
return rc;
return ncn8025_set(slot, &def_settings);
}
static const char *volt_str[] = {
[SIM_VOLT_3V0] = "3.0",
[SIM_VOLT_5V0] = "5.0",
[SIM_VOLT_1V8] = "1.8",
};
const unsigned int ncn8025_div_val[] = {
[SIM_CLKDIV_1] = 1,
[SIM_CLKDIV_2] = 2,
[SIM_CLKDIV_4] = 4,
[SIM_CLKDIV_8] = 8,
};
void ncn8025_dump(const struct ncn8025_settings *set)
{
printf("VOLT=%s, CLKDIV=%u", volt_str[set->vsel], ncn8025_div_val[set->clkdiv]);
if (set->rstin)
printf(", RST");
if (set->cmdvcc)
printf(", VCC");
if (set->interrupt)
printf(", INT");
if (set->simpres)
printf(", SIMPRES");
if (set->led)
printf(", LED");
}