f67f1a1cbc37cb28c73ac16ef33ba23e577f2088
[bertos.git] / bertos / cpu / cortex-m3 / drv / ssi_lm3s.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2010 Develer S.r.l. (http://www.develer.com/)
30  *
31  * -->
32  *
33  * \brief LM3S1968 Synchronous Serial Interface (SSI) driver.
34  *
35  * \author Andrea Righi <arighi@develer.com>
36  */
37
38 #include <cfg/compiler.h>
39 #include <cfg/debug.h>
40 #include "io/lm3s.h"
41 #include "drv/ssi_lm3s.h"
42
43 /* SSI clocking informations (CPSDVSR + SCR) */
44 struct ssi_clock
45 {
46         unsigned int cpsdvsr;
47         unsigned int scr;
48 };
49
50 /*
51  * Evaluate the SSI clock prescale (SSICPSR) and SSI serial clock rate (SCR).
52  */
53 INLINE struct ssi_clock
54 lm3s_ssi_prescale(unsigned int bitrate)
55 {
56         struct ssi_clock ret;
57
58         for (ret.cpsdvsr = 2, ret.scr = CPU_FREQ / bitrate / ret.cpsdvsr - 1;
59                         ret.scr > 255; ret.cpsdvsr += 2);
60         ASSERT(ret.cpsdvsr < 255);
61
62         return ret;
63 }
64
65 /*
66  * Initialize the SSI interface.
67  *
68  * @base: the SSI port base address.
69  * @frame: the data transfer protocol (SSI_FRF_MOTO_MODE_0,
70  * SSI_FRF_MOTO_MODE_1, SSI_FRF_MOTO_MODE_2, SSI_FRF_MOTO_MODE_3, SSI_FRF_TI or
71  * SSI_FRF_NMW)
72  * @mode: the mode of operation (SSI_MODE_MASTER, SSI_MODE_SLAVE,
73  * SSI_MODE_SLAVE_OD)
74  * @bitrate: the SSI clock rate
75  * @data_width: number of bits per frame
76  *
77  * Return 0 in case of success, a negative value otherwise.
78  */
79 int lm3s_ssi_init(uint32_t base, uint32_t frame, int mode,
80                    unsigned int bitrate, unsigned int data_width)
81 {
82         struct ssi_clock ssi_clock;
83
84         ASSERT(base == SSI0_BASE || base == SSI1_BASE);
85         /* Configure the SSI operating mode */
86         switch (mode)
87         {
88                 /* SSI Slave Mode Output Disable */
89                 case SSI_MODE_SLAVE_OD:
90                         HWREG(base + SSI_O_CR1) = SSI_CR1_SOD;
91                         break;
92                 /* SSI Slave */
93                 case SSI_MODE_SLAVE:
94                         HWREG(base + SSI_O_CR1) = SSI_CR1_MS;
95                         break;
96                 /* SSI Master */
97                 case SSI_MODE_MASTER:
98                         HWREG(base + SSI_O_CR1) = 0;
99                         break;
100                 default:
101                         ASSERT(0);
102                         return -1;
103         }
104         /* Configure the peripheral clock and frame format */
105         ssi_clock = lm3s_ssi_prescale(bitrate);
106         HWREG(base + SSI_O_CPSR) = ssi_clock.cpsdvsr;
107         HWREG(base + SSI_O_CR0) =
108                         (ssi_clock.scr << 8)    |
109                         ((frame & 3) << 6)      |
110                         (frame & SSI_CR0_FRF_M) |
111                         (data_width - 1);
112         return 0;
113 }
114
115 /* Enable the SSI interface */
116 void lm3s_ssi_enable(uint32_t base)
117 {
118         HWREG(base + SSI_O_CR1) |= SSI_CR1_SSE;
119 }
120
121 /* Disable the SSI interface */
122 void lm3s_ssi_disable(uint32_t base)
123 {
124         HWREG(base + SSI_O_CR1) &= ~SSI_CR1_SSE;
125 }
126
127 /*
128  * Put a frame into the SSI transmit FIFO.
129  *
130  * NOTE: the upper bits of the frame will be automatically discarded by the
131  * hardware according to the frame data width, configured by lm3s_ssi_init().
132  */
133 void lm3s_ssi_write_frame(uint32_t base, uint32_t val)
134 {
135         /* Wait for available space in the TX FIFO */
136         while (!(HWREG(base + SSI_O_SR) & SSI_SR_TNF))
137                 cpu_relax();
138         /* Enqueue data to the TX FIFO */
139         HWREG(base + SSI_O_DR) = val;
140 }
141
142 /*
143  * Put a frame into the SSI transmit FIFO without blocking.
144  *
145  * NOTE: the upper bits of the frame will be automatically discarded by the
146  * hardware according to the frame data width, configured by lm3s_ssi_init().
147  *
148  * Return the number of frames written to the TX FIFO.
149  */
150 int lm3s_ssi_write_frame_nonblocking(uint32_t base, uint32_t val)
151 {
152         /* Check for available space in the TX FIFO */
153         if (!(HWREG(base + SSI_O_SR) & SSI_SR_TNF))
154                 return 0;
155         /* Enqueue data to the TX FIFO */
156         HWREG(base + SSI_O_DR) = val;
157         return 1;
158 }
159
160 /*
161  * Get a frame from the SSI receive FIFO.
162  */
163 void lm3s_ssi_read_frame(uint32_t base, uint32_t *val)
164 {
165         /* Wait for data available in the RX FIFO */
166         while (!(HWREG(base + SSI_O_SR) & SSI_SR_RNE))
167                 cpu_relax();
168         /* Read data from SSI RX FIFO */
169         *val = HWREG(base + SSI_O_DR);
170 }
171
172 /*
173  * Get a frame into the SSI receive FIFO without blocking.
174  *
175  * Return the number of frames read from the RX FIFO.
176  */
177 int lm3s_ssi_read_frame_nonblocking(uint32_t base, uint32_t *val)
178 {
179         /* Check for data available in the RX FIFO */
180         if (!(HWREG(base + SSI_O_SR) & SSI_SR_RNE))
181                 return 0;
182         /* Read data from SSI RX FIFO */
183         *val = HWREG(base + SSI_O_DR);
184         return 1;
185 }
186
187 /*
188  * Check if the SSI transmitter is busy or not
189  *
190  * This allows to determine whether the TX FIFO have been cleared by the
191  * hardware, so the transmission can be safely considered completed.
192  */
193 bool lm3s_ssi_txdone(uint32_t base)
194 {
195         /* Check if the SSI is busy */
196         return (HWREG(base + SSI_O_SR) & SSI_SR_BSY) ? true : false;
197 }