blob: 99b93b89c15ae901081e7df3464d3aa493ce8640 [file] [log] [blame]
Harald Weltec3f170d2019-02-24 09:06:59 +01001/* Access functions for the per-SIM-slot NCN8025 chip card interface,
2 * which is controlled via (half) a SX1503 I2C GPIO expander.
3 *
4 * (C) 2019 by Harald Welte <laforge@gnumonks.org>
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9#include <stdint.h>
10#include <string.h>
11#include <utils_assert.h>
12#include <utils.h>
13#include "octsim_i2c.h"
14#include "ncn8025.h"
15
16#define SX1503_ADDR 0x20
17
18/*! translate from ncn8025_settings into SX1503 register value */
19static uint8_t ncn8025_encode(const struct ncn8025_settings *set)
20{
21 uint8_t reg = 0;
22 if (set->rstin)
23 reg |= 0x01;
24 if (!set->cmdvcc)
25 reg |= 0x02;
26 if (set->clkdiv & 1)
27 reg |= 0x04;
28 if (set->clkdiv & 2)
29 reg |= 0x08;
30 if (set->vsel & 1)
31 reg |= 0x10;
32 if (set->vsel & 2)
33 reg |= 0x20;
34 if (set->led)
35 reg |= 0x80;
36 return reg;
37}
38
39/*! translate from register value to ncn8025_settings */
40static int ncn8025_decode(uint8_t reg, struct ncn8025_settings *set)
41{
42 memset(set, 0, sizeof(*set));
43
44 if (reg & 0x01)
45 set->rstin = true;
46 if (!(reg & 0x02))
47 set->cmdvcc = true;
48 if (reg & 0x04)
49 set->clkdiv |= 0x01;
50 if (reg & 0x08)
51 set->clkdiv |= 0x02;
52 if (reg & 0x10)
53 set->vsel |= 0x01;
54 if (reg & 0x20)
55 set->vsel |= 0x02;
56 if (!(reg & 0x40))
57 set->simpres = true;
58 if ((reg & 0x80))
59 set->led = true;
60
61 return 0;
62}
63
64static const struct i2c_adapter *slot2adapter(unsigned int slot)
65{
66 unsigned int idx = slot / 2;
67 ASSERT(idx < ARRAY_SIZE(i2c));
68 return &i2c[idx];
69}
70
71
72static const uint8_t slot2data_reg(unsigned int slot)
73{
74 if (slot & 1)
75 return 0x00;
76 else
77 return 0x01;
78}
79
80static const uint8_t slot2dir_reg(unsigned int slot)
81{
82 if (slot & 1)
83 return 0x02;
84 else
85 return 0x03;
86}
87
88
89/*! Set a given NCN8025 as described in 'set'.
90 * \param[in] slot Slot number (0..7)
91 * \param[in] set Settings that shall be written
92 * \returns 0 on success; negative on error */
93int ncn8025_set(uint8_t slot, const struct ncn8025_settings *set)
94{
95 const struct i2c_adapter *adap = slot2adapter(slot);
96 uint8_t reg = slot2data_reg(slot);
97 uint8_t raw = ncn8025_encode(set);
98 return i2c_write_reg(adap, SX1503_ADDR, reg, raw);
99}
100
101/*! Get a given NCN8025 state from the chip.
102 * \param[in] slot Slot number (0..7)
103 * \param[out] set Settings that are retrieved
104 * \returns 0 on success; negative on error */
105int ncn8025_get(uint8_t slot, struct ncn8025_settings *set)
106{
107 const struct i2c_adapter *adap = slot2adapter(slot);
108 uint8_t reg = slot2data_reg(slot);
109 int rc;
110 rc = i2c_read_reg(adap, SX1503_ADDR, reg);
111 if (rc < 0)
112 return rc;
113 return ncn8025_decode(rc, set);
114}
115
116/*! default settings we use at start-up: powered off, in reset, slowest clock, 3V */
117static const struct ncn8025_settings def_settings = {
118 .rstin = true,
119 .cmdvcc = false,
120 .led = false,
121 .clkdiv = SIM_CLKDIV_8,
122 .vsel = SIM_VOLT_3V0,
123};
124
125/*! Initialize a given NCN8025/slot. */
126int ncn8025_init(unsigned int slot)
127{
128 const struct i2c_adapter *adap = slot2adapter(slot);
129 uint8_t reg = slot2dir_reg(slot);
130 int rc;
131 /* IO6 of each bank is input (!PRESENT), rest are outputs */
132 rc = i2c_write_reg(adap, SX1503_ADDR, reg, 0x40);
133 if (rc < 0)
134 return rc;
135 return ncn8025_set(slot, &def_settings);
136}