/* 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 * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include #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"); }