aboutsummaryrefslogtreecommitdiffstats
path: root/sysmoOCTSIM/ncn8025.c
blob: 61509bc4c2042d211180f16b197402c40878e629 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
/* 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_active(uint8_t slot)
{
	uint8_t pin = slot2int_pin(slot);
	return !gpio_get_pin_level(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_active(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",
};

static const unsigned int 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], 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");
}