Blame view

RIOT/cpu/kinetis_common/periph/spi.c 4.93 KB
a752c7ab   elopes   add first test an...
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
  /*
   * Copyright (C) 2014 Hamburg University of Applied Sciences
   *               2014 PHYTEC Messtechnik GmbH
   *               2015 Eistec AB
   *               2016 Freie Universität Berlin
   *
   * This file is subject to the terms and conditions of the GNU Lesser
   * General Public License v2.1. See the file LICENSE in the top level
   * directory for more details.
   */
  
  /**
   * @ingroup     cpu_kinetis_common
   * @ingroup     drivers_periph_spi
   *
   * @{
   *
   * @file
   * @brief       Low-level SPI driver implementation
   *
   * @author      Peter Kietzmann <peter.kietzmann@haw-hamburg.de>
   * @author      Johann Fischer <j.fischer@phytec.de>
   * @author      Joakim Nohlgård <joakim.nohlgard@eistec.se>
   * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
   *
   * @}
   */
  
  #include "cpu.h"
  #include "mutex.h"
  #include "assert.h"
  #include "periph/spi.h"
  
  #define ENABLE_DEBUG        (0)
  #include "debug.h"
  
  /**
   * @brief   We use this mask to differentiate between SPI_HWCS() and GPIO_PIN()
   */
  #define SWCS_MASK           (0xfff0)
  
  /**
   * @brief   Allocation device locks
   */
  static mutex_t locks[SPI_NUMOF];
  
  static inline SPI_Type *dev(spi_t bus)
  {
      return spi_config[bus].dev;
  }
  
  static inline void poweron(spi_t bus)
  {
      switch((uint32_t)dev(bus)) {
          case (uint32_t)SPI0:
          case (uint32_t)SPI1:
              SIM->SCGC6 |= (spi_config[bus].simmask);
              break;
  #ifdef SPI2
          case (uint32_t)SPI2:
              SIM->SCGC3 |= (spi_config[bus].simmask);
              break;
  #endif
      }
  }
  
  static inline void poweroff(spi_t bus)
  {
      switch((uint32_t)dev(bus)) {
          case (uint32_t)SPI0:
          case (uint32_t)SPI1:
              SIM->SCGC6 &= ~(spi_config[bus].simmask);
              break;
  #ifdef SPI2
          case (uint32_t)SPI2:
              SIM->SCGC3 &= ~(spi_config[bus].simmask);
              break;
  #endif
      }
  }
  
  void spi_init(spi_t bus)
  {
      /* make sure given bus device is valid */
      assert(bus < SPI_NUMOF);
  
      /* initialize the buses lock */
      mutex_init(&locks[bus]);
      /* trigger pin initialization */
      spi_init_pins(bus);
      /* power on the bus temporarily */
      poweron(bus);
  
      /* make the base configuration: configure as SPI master, set CS inactive
       * state (for HWCS lines) and clear FIFO counters and disable FIFOs */
      dev(bus)->MCR = (SPI_MCR_MSTR_MASK | SPI_MCR_PCSIS_MASK |
                       SPI_MCR_CLR_RXF_MASK | SPI_MCR_CLR_TXF_MASK |
                       SPI_MCR_DIS_RXF_MASK | SPI_MCR_DIS_TXF_MASK |
                       SPI_MCR_DOZE_MASK | SPI_MCR_HALT_MASK | SPI_MCR_MDIS_MASK);
  
      /* disable all DMA and interrupt requests */
      dev(bus)->RSER = 0;
  
      /* and power off the bus until it is actually used */
      poweroff(bus);
  }
  
  void spi_init_pins(spi_t bus)
  {
      gpio_init_port(spi_config[bus].pin_miso, spi_config[bus].pcr);
      gpio_init_port(spi_config[bus].pin_mosi, spi_config[bus].pcr);
      gpio_init_port(spi_config[bus].pin_clk , spi_config[bus].pcr);
  }
  
  int spi_init_cs(spi_t bus, spi_cs_t cs)
  {
      if (bus >= SPI_NUMOF) {
          return SPI_NODEV;
      }
      if (cs == SPI_CS_UNDEF) {
          return SPI_NOCS;
      }
  
      if (cs & SWCS_MASK) {
          gpio_init((gpio_t)cs, GPIO_OUT);
      }
      else {
          if ((cs >= SPI_HWCS_NUMOF) ||
              (spi_config[bus].pin_cs[cs] == GPIO_UNDEF)) {
              return SPI_NOCS;
          }
          gpio_init_port(spi_config[bus].pin_cs[cs], spi_config[bus].pcr);
      }
  
      return SPI_OK;
  }
  
  int spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk)
  {
      /* lock and power on the bus */
      mutex_lock(&locks[bus]);
      poweron(bus);
  
      /* enable the device */
      dev(bus)->MCR &= ~(SPI_MCR_HALT_MASK | SPI_MCR_MDIS_MASK);
  
      /* configure clock and mode */
      dev(bus)->CTAR[0] = (mode | SPI_CTAR_FMSZ(7) | spi_clk_config[clk]);
  
      return SPI_OK;
  }
  
  void spi_release(spi_t bus)
  {
      /* disable, power off, and unlock the bus */
      dev(bus)->MCR |= (SPI_MCR_HALT_MASK | SPI_MCR_MDIS_MASK);
      poweroff(bus);
      mutex_unlock(&locks[bus]);
  }
  
  void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont,
                          const void *out, void *in, size_t len)
  {
      const uint8_t *out_buf = out;
      uint8_t *in_buf = in;
      uint32_t flags = SPI_PUSHR_CONT_MASK;
  
      assert(out_buf || in_buf);
  
      /* handle chip select */
      if (cs != SPI_CS_UNDEF) {
          if (cs & SWCS_MASK) {
              gpio_clear((gpio_t)cs);
          }
          else {
              flags |= (uint32_t)(1 << (cs + SPI_PUSHR_PCS_SHIFT));
          }
      }
  
      for (size_t i = 0; i < len; i++) {
          uint8_t tmp = (out_buf) ? out_buf[i] : 0;
          if ((i == (len - 1)) && (!cont)) {
              flags &= ~(SPI_PUSHR_CONT_MASK);
          }
  
          while (!(dev(bus)->SR & SPI_SR_TFFF_MASK)) {}
          dev(bus)->PUSHR = (tmp | flags);
  
          while (!(dev(bus)->SR & SPI_SR_RXCTR_MASK)) {}
          tmp = dev(bus)->POPR;
          if (in_buf) {
              in_buf[i] = tmp;
          }
      }
  
      if ((!cont) && (cs != SPI_CS_UNDEF) && (cs & SWCS_MASK)) {
          gpio_set((gpio_t)cs);
      }
  }