aboutsummaryrefslogtreecommitdiff
path: root/src/kern/spi/spi_manager.c
blob: 83b6e1fb7f936f9d91a7bf79922c22cd8b680ff4 (plain) (blame)
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
#include "kern/spi/spi_manager.h"

#include "arch/stm32l4xxx/peripherals/rcc.h"
#include "arch/stm32l4xxx/peripherals/spi.h"
#include "kern/log.h"

struct spi {
  __IO spi_regs_t* regs;
  spi_select_t selection;
  bool reserved;
};

static spi_t spi_buses[N_SPI_SELECT];

static void enable_spi_clock(spi_t* spi, int enable)
{
  switch (spi->selection) {
    case SPI_SELECT_SPI1:
      regset(RCC.apb2en_r, rcc_spi1en, !!enable);
      break;

    case SPI_SELECT_SPI2:
      regset(RCC.apb1en1_r, rcc_spi3en, !!enable);
      break;

    default:
      break;
  }
}

static spi_t* configure_spi(spi_t* spi, spi_opts_t* opts, int* ec)
{
  enable_spi_clock(spi, 1);

  uint32_t reg = 0;
  regset(reg, spi_ldma_tx, opts->number_of_data_parity_tx);
  regset(reg, spi_ldma_rx, opts->number_of_data_parity_rx);
  regset(reg, spi_frxth, opts->rx_interrupt_fifo_threshold);
  regset(reg, spi_ds, opts->data_size);
  regset(reg, spi_txeie, opts->enable_tx_buffer_empty_interrupt);
  regset(reg, spi_rxneie, opts->enable_rx_buffer_not_empty_interrupt);
  regset(reg, spi_errie, opts->enable_error_interrupt);
  regset(reg, spi_frf, opts->frame_format);
  regset(reg, spi_nssp, opts->enable_nss_pulse);
  regset(reg, spi_ssoe, opts->enable_ss_output);
  regset(reg, spi_txdmaen, opts->enable_tx_dma);
  regset(reg, spi_rxdmaen, opts->enable_rx_dma);

  spi->regs->c_r2 = reg;

  reg = 0;
  regset(reg, spi_bidimode, opts->enable_bidirectional_data_mode);
  regset(reg, spi_crcen, opts->enable_crc);
  regset(reg, spi_crcnext, 0);
  regset(reg, spi_crcl, opts->crc_length);
  regset(reg, spi_rxonly, opts->receieve_only);
  regset(reg, spi_ssm, opts->enable_software_slave_management);
  regset(reg, spi_ssi, opts->internal_slave_select);
  regset(reg, spi_lsbfirst, !opts->endianness);
  regset(reg, spi_spe, 1);
  regset(reg, spi_br, opts->baud);
  regset(reg, spi_mstr, !opts->spi_mode);
  regset(reg, spi_cpol, opts->polarity);
  regset(reg, spi_cpha, opts->clock_phase);

  spi->regs->c_r1 = reg;

  return spi;
}

static int try_reserve_spi(spi_t* spi)
{
  return !__sync_fetch_and_or(&spi->reserved, 1);
}

void spi_start(spi_t* spi)
{
  regset(spi->regs->c_r1, spi_spe, 1);
}

void release_spi(spi_t* spi)
{
  enable_spi_clock(spi, 0);
  regset(spi->regs->c_r1, spi_spe, 0);
  spi->reserved = 0;
}

void spi_set_crc_next(spi_t* spi)
{
  regset(spi->regs->c_r1, spi_crcnext, 1);
}

spi_t* reserve_spi(spi_select_t spi_select, spi_opts_t* opts, int* ec)
{
  *ec = 0;

  if (spi_select < 0 || spi_select >= N_SPI_SELECT) {
    *ec = SPI_ERROR_SELECTION_NOT_VALID;
    return NULL;
  }

  spi_t* ret = &spi_buses[spi_select];

  if (!try_reserve_spi(ret)) {
    *ec = SPI_ERROR_ALREADY_IN_USE;
    return NULL;
  }

  ret->selection = spi_select;
  switch (spi_select) {
#define SPI(sp, ptr)    \
  case SPI_SELECT_##sp: \
    ret->regs = &ptr;   \
    break;
#include "arch/stm32l4xxx/peripherals/tables/stm32l432xx/spi/buses.inc"
#undef SPI
    default:
      *ec = SPI_ERROR_SELECTION_NOT_VALID;
      return NULL;
  }

  return configure_spi(ret, opts, ec);
}

void spi_write_8_sync(spi_t* spi, uint8_t data)
{
  while (!regget(spi->regs->s_r, spi_txe))
    ;
  spi->regs->dl_r = data;
}

void spi_write_16_sync(spi_t* spi, uint16_t data)
{
  while (!regget(spi->regs->s_r, spi_txe))
    ;
  spi->regs->d_r = data;
}

uint8_t spi_read_8_sync(spi_t* spi)
{
  while (!regget(spi->regs->s_r, spi_rxne))
    ;
  return spi->regs->dl_r;
}

uint16_t spi_read_16_sync(spi_t* spi)
{
  while (!regget(spi->regs->s_r, spi_rxne))
    ;
  return spi->regs->d_r;
}