blob: 540b166e5212a097e858c61b9b498a2784d94e73 [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>
Harald Welte1b9a5b82019-02-24 23:04:45 +010011#include <stdio.h>
Harald Weltec3f170d2019-02-24 09:06:59 +010012#include <utils_assert.h>
13#include <utils.h>
Harald Welte5af7c8c2019-02-24 23:13:44 +010014#include "atmel_start_pins.h"
Harald Weltec3f170d2019-02-24 09:06:59 +010015#include "octsim_i2c.h"
16#include "ncn8025.h"
17
18#define SX1503_ADDR 0x20
19
20/*! translate from ncn8025_settings into SX1503 register value */
21static uint8_t ncn8025_encode(const struct ncn8025_settings *set)
22{
23 uint8_t reg = 0;
Harald Welte17625062019-04-16 20:47:49 +020024 if (!set->rstin)
Harald Weltec3f170d2019-02-24 09:06:59 +010025 reg |= 0x01;
26 if (!set->cmdvcc)
27 reg |= 0x02;
28 if (set->clkdiv & 1)
29 reg |= 0x04;
30 if (set->clkdiv & 2)
31 reg |= 0x08;
32 if (set->vsel & 1)
33 reg |= 0x10;
34 if (set->vsel & 2)
35 reg |= 0x20;
36 if (set->led)
37 reg |= 0x80;
38 return reg;
39}
40
41/*! translate from register value to ncn8025_settings */
42static int ncn8025_decode(uint8_t reg, struct ncn8025_settings *set)
43{
44 memset(set, 0, sizeof(*set));
45
Harald Welte17625062019-04-16 20:47:49 +020046 if (!(reg & 0x01))
Harald Weltec3f170d2019-02-24 09:06:59 +010047 set->rstin = true;
48 if (!(reg & 0x02))
49 set->cmdvcc = true;
50 if (reg & 0x04)
51 set->clkdiv |= 0x01;
52 if (reg & 0x08)
53 set->clkdiv |= 0x02;
54 if (reg & 0x10)
55 set->vsel |= 0x01;
56 if (reg & 0x20)
57 set->vsel |= 0x02;
58 if (!(reg & 0x40))
59 set->simpres = true;
60 if ((reg & 0x80))
61 set->led = true;
62
63 return 0;
64}
65
66static const struct i2c_adapter *slot2adapter(unsigned int slot)
67{
68 unsigned int idx = slot / 2;
69 ASSERT(idx < ARRAY_SIZE(i2c));
70 return &i2c[idx];
71}
72
73
74static const uint8_t slot2data_reg(unsigned int slot)
75{
76 if (slot & 1)
77 return 0x00;
78 else
79 return 0x01;
80}
81
82static const uint8_t slot2dir_reg(unsigned int slot)
83{
84 if (slot & 1)
85 return 0x02;
86 else
87 return 0x03;
88}
89
Harald Welte5af7c8c2019-02-24 23:13:44 +010090static const uint8_t slot2int_pin(unsigned int slot)
91{
92 static const uint8_t slot2pin[8] = { SIM0_INT, SIM1_INT, SIM2_INT, SIM3_INT,
93 SIM4_INT, SIM5_INT, SIM6_INT, SIM7_INT };
94 ASSERT(slot < ARRAY_SIZE(slot2pin));
95 return slot2pin[slot];
96}
97
Harald Welte08ea0622019-05-11 21:14:55 +020098bool ncn8025_interrupt_level(uint8_t slot)
Harald Welte5af7c8c2019-02-24 23:13:44 +010099{
100 uint8_t pin = slot2int_pin(slot);
Harald Welte08ea0622019-05-11 21:14:55 +0200101 return gpio_get_pin_level(pin);
Harald Welte5af7c8c2019-02-24 23:13:44 +0100102}
103
Harald Weltec3f170d2019-02-24 09:06:59 +0100104
105/*! Set a given NCN8025 as described in 'set'.
106 * \param[in] slot Slot number (0..7)
107 * \param[in] set Settings that shall be written
108 * \returns 0 on success; negative on error */
109int ncn8025_set(uint8_t slot, const struct ncn8025_settings *set)
110{
111 const struct i2c_adapter *adap = slot2adapter(slot);
112 uint8_t reg = slot2data_reg(slot);
113 uint8_t raw = ncn8025_encode(set);
114 return i2c_write_reg(adap, SX1503_ADDR, reg, raw);
115}
116
117/*! Get a given NCN8025 state from the chip.
118 * \param[in] slot Slot number (0..7)
119 * \param[out] set Settings that are retrieved
120 * \returns 0 on success; negative on error */
121int ncn8025_get(uint8_t slot, struct ncn8025_settings *set)
122{
123 const struct i2c_adapter *adap = slot2adapter(slot);
124 uint8_t reg = slot2data_reg(slot);
125 int rc;
126 rc = i2c_read_reg(adap, SX1503_ADDR, reg);
127 if (rc < 0)
128 return rc;
Harald Welte5af7c8c2019-02-24 23:13:44 +0100129 rc = ncn8025_decode(rc, set);
Harald Welte08ea0622019-05-11 21:14:55 +0200130 set->interrupt = ncn8025_interrupt_level(slot);
Harald Welte5af7c8c2019-02-24 23:13:44 +0100131 return rc;
Harald Weltec3f170d2019-02-24 09:06:59 +0100132}
133
134/*! default settings we use at start-up: powered off, in reset, slowest clock, 3V */
135static const struct ncn8025_settings def_settings = {
136 .rstin = true,
137 .cmdvcc = false,
138 .led = false,
139 .clkdiv = SIM_CLKDIV_8,
140 .vsel = SIM_VOLT_3V0,
141};
142
143/*! Initialize a given NCN8025/slot. */
144int ncn8025_init(unsigned int slot)
145{
146 const struct i2c_adapter *adap = slot2adapter(slot);
147 uint8_t reg = slot2dir_reg(slot);
148 int rc;
149 /* IO6 of each bank is input (!PRESENT), rest are outputs */
150 rc = i2c_write_reg(adap, SX1503_ADDR, reg, 0x40);
151 if (rc < 0)
152 return rc;
153 return ncn8025_set(slot, &def_settings);
154}
Harald Welte1b9a5b82019-02-24 23:04:45 +0100155
156static const char *volt_str[] = {
157 [SIM_VOLT_3V0] = "3.0",
158 [SIM_VOLT_5V0] = "5.0",
159 [SIM_VOLT_1V8] = "1.8",
160};
161
162static const unsigned int div_val[] = {
163 [SIM_CLKDIV_1] = 1,
164 [SIM_CLKDIV_2] = 2,
165 [SIM_CLKDIV_4] = 4,
166 [SIM_CLKDIV_8] = 8,
167};
168
169void ncn8025_dump(const struct ncn8025_settings *set)
170{
171 printf("VOLT=%s, CLKDIV=%u", volt_str[set->vsel], div_val[set->clkdiv]);
172 if (set->rstin)
173 printf(", RST");
174 if (set->cmdvcc)
175 printf(", VCC");
Harald Welte5af7c8c2019-02-24 23:13:44 +0100176 if (set->interrupt)
177 printf(", INT");
Harald Welte1b9a5b82019-02-24 23:04:45 +0100178 if (set->simpres)
179 printf(", SIMPRES");
180 if (set->led)
181 printf(", LED");
182}