aboutsummaryrefslogtreecommitdiffstats
path: root/src/cc32/iso7816_slave.c
blob: 3b21eef9e7cc5a0b0301cd42559d09ffeb60217c (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
#include <stdint.h>
#include <memory.h>
#include <errno.h>

#include "cc32_irq.h"

enum iso_slave_reg {
	ISOCON		= 0x00,
	ISOCON1		= 0x04,
	ISOCON2		= 0x08,
	ISOSTS		= 0x0c,
	ISOBRC		= 0x10,
	ISOBUF		= 0x14,
	ISODIO		= 0x18,
	ISOMSK		= 0x1c,
	ISODMACON	= 0x30,
	ISODMASTS	= 0x34,
	ISODMABFAD	= 0x38,
	ISODMABFLEN	= 0x3c,
	ISODMABFPT	= 0x40,
	ISODMAMSK	= 0x44,
	ISOTCON		= 0x50,
	ISOTDAT		= 0x54,
	ISOTRLD		= 0x58,
	ISOTMSK		= 0x5c,
	ISONULL		= 0x60,
};

#define CC32_BASE_UART	0x0F8800
#define UART_REG(n)	(CC32_BASE_UART+(n))

#define ISOCON_TR	(1 << 5)
#define ISOCON_TACT	(1 << 4)

#define ISOSTS_TBE	(1 << 0)
#define ISOSTS_RBF	(1 << 1)
#define ISOSTS_PE	(1 << 2)
#define ISOSTS_OE	(1 << 3)

#define ISOCON2_SBIT	(1 << 7)

#define ISOBRC_DI(x)	((x) & 0xf)
#define ISOBRC_FI(x)	(((x) & 0xf) << 4)

struct iso_slave_state {
	uint8_t fi;
	uint8_t di;
};
static struct iso_slave_state iss;

/* will be called from FIQ mode */
static void slave_irq(uint8_t irq)
{
	//FIXME
}

int iso7816_slave_fi_di(uint8_t fi, uint8_t di)
{
	writel(ISOBRC_DI(di) | ISOBRC_FI(fi), UART_REG(ISOBRC));
	iss.fi = fi;
	iss.di = di;

	return 0;
}

int iso7816_slave_tx_ch(uint8_t ch)
{
	writel(ISOCON_TR, UART_REG(ISOCON));
	writel(ch, UART_REG(ISOBUF));

	if (readl(UART_REG(ISOSTS)) & ISOSTS_PE) {
		writel(ISOSTS_PE, UART_REG(ISOSTS));
		return -EIO;
	}

	while (readl(UART_REG(ISOCON)) & ISOCON_TACT) { }

	return 0;
}

int iso7816_slave_tx5(const uint8_t *data)
{
	iso7816_slave_tx_ch(data[0]);
	iso7816_slave_tx_ch(data[1]);
	iso7816_slave_tx_ch(data[2]);
	iso7816_slave_tx_ch(data[3]);
	iso7816_slave_tx_ch(data[4]);

	return 0;
}

int iso7816_slave_tx(const uint8_t *data, uint8_t len)
{
	int i;
	/* FIXME: fiq/dma ? */

	writel(ISOCON_TR, UART_REG(ISOCON));

	for (i = 0; i < len; i++) {
		if (iso7816_slave_tx_ch(data[i]) < 0)
			return -EIO;
	}

	return i;
}

int iso7816_slave_init(void)
{
	/* receive mode */
	writel(0, UART_REG(ISOCON));
	/* up to 6 times re-transmission */
	writel(6 | ISOCON2_SBIT, UART_REG(ISOCON1));
	/* T=0 mode */
	writel(0, UART_REG(ISOCON2));
	/* clear status bits, if any */
	writel(ISOSTS_PE|ISOSTS_OE, UART_REG(ISOSTS));
	/* disable SBIT interrupt */
	writel(ISOCON2_SBIT, UART_REG(ISOMSK));

	/* Initial Fi/Di setting */
	iso7816_slave_fi_di(1, 1);

	cc32_irq_register(IRQ_UART, &slave_irq);
	//cc32_irq_enable(IRQ_UART);

	return 0;
}