Mercurial > ecos-v3_0-branch
annotate packages/devs/spi/arm/at91/current/src/spi_at91.c @ 2729:74dbf4c3f2e1 after-copyright-change-20090129
Update all copyright banners to reflect FSF ownership; fix and improve licence text.
| author | jlarmour |
|---|---|
| date | Thu, 29 Jan 2009 17:47:46 +0000 |
| parents | e40a637e7b2e |
| children | fe2b9e9dcbe2 |
| rev | line source |
|---|---|
| 1794 | 1 //========================================================================== |
| 2 // | |
| 3 // spi_at91.c | |
| 4 // | |
| 5 // Atmel AT91 (ARM) SPI driver | |
| 6 // | |
| 7 //========================================================================== | |
|
2729
74dbf4c3f2e1
Update all copyright banners to reflect FSF ownership; fix and improve licence text.
jlarmour
parents:
2569
diff
changeset
|
8 // ####ECOSGPLCOPYRIGHTBEGIN#### |
|
74dbf4c3f2e1
Update all copyright banners to reflect FSF ownership; fix and improve licence text.
jlarmour
parents:
2569
diff
changeset
|
9 // ------------------------------------------- |
|
74dbf4c3f2e1
Update all copyright banners to reflect FSF ownership; fix and improve licence text.
jlarmour
parents:
2569
diff
changeset
|
10 // This file is part of eCos, the Embedded Configurable Operating System. |
|
74dbf4c3f2e1
Update all copyright banners to reflect FSF ownership; fix and improve licence text.
jlarmour
parents:
2569
diff
changeset
|
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. |
| 1794 | 12 // |
|
2729
74dbf4c3f2e1
Update all copyright banners to reflect FSF ownership; fix and improve licence text.
jlarmour
parents:
2569
diff
changeset
|
13 // eCos is free software; you can redistribute it and/or modify it under |
|
74dbf4c3f2e1
Update all copyright banners to reflect FSF ownership; fix and improve licence text.
jlarmour
parents:
2569
diff
changeset
|
14 // the terms of the GNU General Public License as published by the Free |
|
74dbf4c3f2e1
Update all copyright banners to reflect FSF ownership; fix and improve licence text.
jlarmour
parents:
2569
diff
changeset
|
15 // Software Foundation; either version 2 or (at your option) any later |
|
74dbf4c3f2e1
Update all copyright banners to reflect FSF ownership; fix and improve licence text.
jlarmour
parents:
2569
diff
changeset
|
16 // version. |
| 1794 | 17 // |
|
2729
74dbf4c3f2e1
Update all copyright banners to reflect FSF ownership; fix and improve licence text.
jlarmour
parents:
2569
diff
changeset
|
18 // eCos is distributed in the hope that it will be useful, but WITHOUT |
|
74dbf4c3f2e1
Update all copyright banners to reflect FSF ownership; fix and improve licence text.
jlarmour
parents:
2569
diff
changeset
|
19 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
74dbf4c3f2e1
Update all copyright banners to reflect FSF ownership; fix and improve licence text.
jlarmour
parents:
2569
diff
changeset
|
20 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
74dbf4c3f2e1
Update all copyright banners to reflect FSF ownership; fix and improve licence text.
jlarmour
parents:
2569
diff
changeset
|
21 // for more details. |
| 1794 | 22 // |
|
2729
74dbf4c3f2e1
Update all copyright banners to reflect FSF ownership; fix and improve licence text.
jlarmour
parents:
2569
diff
changeset
|
23 // You should have received a copy of the GNU General Public License |
|
74dbf4c3f2e1
Update all copyright banners to reflect FSF ownership; fix and improve licence text.
jlarmour
parents:
2569
diff
changeset
|
24 // along with eCos; if not, write to the Free Software Foundation, Inc., |
|
74dbf4c3f2e1
Update all copyright banners to reflect FSF ownership; fix and improve licence text.
jlarmour
parents:
2569
diff
changeset
|
25 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 1794 | 26 // |
|
2729
74dbf4c3f2e1
Update all copyright banners to reflect FSF ownership; fix and improve licence text.
jlarmour
parents:
2569
diff
changeset
|
27 // As a special exception, if other files instantiate templates or use |
|
74dbf4c3f2e1
Update all copyright banners to reflect FSF ownership; fix and improve licence text.
jlarmour
parents:
2569
diff
changeset
|
28 // macros or inline functions from this file, or you compile this file |
|
74dbf4c3f2e1
Update all copyright banners to reflect FSF ownership; fix and improve licence text.
jlarmour
parents:
2569
diff
changeset
|
29 // and link it with other works to produce a work based on this file, |
|
74dbf4c3f2e1
Update all copyright banners to reflect FSF ownership; fix and improve licence text.
jlarmour
parents:
2569
diff
changeset
|
30 // this file does not by itself cause the resulting work to be covered by |
|
74dbf4c3f2e1
Update all copyright banners to reflect FSF ownership; fix and improve licence text.
jlarmour
parents:
2569
diff
changeset
|
31 // the GNU General Public License. However the source code for this file |
|
74dbf4c3f2e1
Update all copyright banners to reflect FSF ownership; fix and improve licence text.
jlarmour
parents:
2569
diff
changeset
|
32 // must still be made available in accordance with section (3) of the GNU |
|
74dbf4c3f2e1
Update all copyright banners to reflect FSF ownership; fix and improve licence text.
jlarmour
parents:
2569
diff
changeset
|
33 // General Public License v2. |
| 1794 | 34 // |
|
2729
74dbf4c3f2e1
Update all copyright banners to reflect FSF ownership; fix and improve licence text.
jlarmour
parents:
2569
diff
changeset
|
35 // This exception does not invalidate any other reasons why a work based |
|
74dbf4c3f2e1
Update all copyright banners to reflect FSF ownership; fix and improve licence text.
jlarmour
parents:
2569
diff
changeset
|
36 // on this file might be covered by the GNU General Public License. |
|
74dbf4c3f2e1
Update all copyright banners to reflect FSF ownership; fix and improve licence text.
jlarmour
parents:
2569
diff
changeset
|
37 // ------------------------------------------- |
|
74dbf4c3f2e1
Update all copyright banners to reflect FSF ownership; fix and improve licence text.
jlarmour
parents:
2569
diff
changeset
|
38 // ####ECOSGPLCOPYRIGHTEND#### |
| 1794 | 39 //========================================================================== |
| 40 //#####DESCRIPTIONBEGIN#### | |
| 41 // | |
| 42 // Author(s): Savin Zlobec <savin@elatec.si> | |
| 43 // Date: 2004-08-25 | |
| 44 // | |
| 45 //####DESCRIPTIONEND#### | |
| 46 // | |
| 47 //========================================================================== | |
| 48 | |
| 49 #include <pkgconf/hal.h> | |
| 50 #include <pkgconf/io_spi.h> | |
| 51 #include <pkgconf/devs_spi_arm_at91.h> | |
| 52 | |
| 53 #include <cyg/infra/cyg_type.h> | |
| 54 #include <cyg/infra/cyg_ass.h> | |
| 55 #include <cyg/hal/hal_io.h> | |
| 56 #include <cyg/hal/hal_if.h> | |
| 57 #include <cyg/hal/hal_intr.h> | |
| 58 #include <cyg/hal/drv_api.h> | |
| 59 #include <cyg/io/spi.h> | |
| 60 #include <cyg/io/spi_at91.h> | |
| 61 #include <cyg/error/codes.h> | |
| 62 | |
| 63 // ------------------------------------------------------------------------- | |
| 2220 | 64 static void spi_at91_init_bus(cyg_spi_at91_bus_t * bus); |
| 1794 | 65 |
| 66 static cyg_uint32 spi_at91_ISR(cyg_vector_t vector, cyg_addrword_t data); | |
| 67 | |
| 68 static void spi_at91_DSR(cyg_vector_t vector, | |
| 69 cyg_ucount32 count, | |
| 70 cyg_addrword_t data); | |
| 71 | |
| 72 static void spi_at91_transaction_begin(cyg_spi_device *dev); | |
| 73 | |
| 74 static void spi_at91_transaction_transfer(cyg_spi_device *dev, | |
| 75 cyg_bool polled, | |
| 76 cyg_uint32 count, | |
| 77 const cyg_uint8 *tx_data, | |
| 78 cyg_uint8 *rx_data, | |
| 79 cyg_bool drop_cs); | |
| 80 | |
| 81 static void spi_at91_transaction_tick(cyg_spi_device *dev, | |
| 82 cyg_bool polled, | |
| 83 cyg_uint32 count); | |
| 84 | |
| 85 static void spi_at91_transaction_end(cyg_spi_device* dev); | |
| 86 | |
| 87 static int spi_at91_get_config(cyg_spi_device *dev, | |
| 88 cyg_uint32 key, | |
| 89 void *buf, | |
| 90 cyg_uint32 *len); | |
| 91 | |
| 92 static int spi_at91_set_config(cyg_spi_device *dev, | |
| 93 cyg_uint32 key, | |
| 94 const void *buf, | |
| 95 cyg_uint32 *len); | |
| 96 | |
| 97 // ------------------------------------------------------------------------- | |
| 98 // AT91 SPI BUS | |
| 99 | |
| 2220 | 100 #ifdef CYGHWR_DEVS_SPI_ARM_AT91_BUS0 |
| 101 cyg_spi_at91_bus_t cyg_spi_at91_bus0 = { | |
| 1794 | 102 .spi_bus.spi_transaction_begin = spi_at91_transaction_begin, |
| 103 .spi_bus.spi_transaction_transfer = spi_at91_transaction_transfer, | |
| 104 .spi_bus.spi_transaction_tick = spi_at91_transaction_tick, | |
| 105 .spi_bus.spi_transaction_end = spi_at91_transaction_end, | |
| 106 .spi_bus.spi_get_config = spi_at91_get_config, | |
| 2220 | 107 .spi_bus.spi_set_config = spi_at91_set_config, |
| 108 .interrupt_number = CYGNUM_HAL_INTERRUPT_SPI, | |
| 109 .base = AT91_SPI, | |
| 110 #ifndef CYGDAT_DEVS_SPI_ARM_AT91_BUS0_NPCS0_NONE | |
| 111 .cs_en[0] = true, | |
| 112 .cs_gpio[0] = CYGDAT_DEVS_SPI_ARM_AT91_BUS0_NPCS0, | |
| 113 #else | |
| 114 .cs_en[0] = false, | |
| 115 #endif | |
| 116 #ifndef CYGDAT_DEVS_SPI_ARM_AT91_BUS0_NPCS1_NONE | |
| 117 .cs_en[1] = true, | |
| 118 .cs_gpio[1] = CYGDAT_DEVS_SPI_ARM_AT91_BUS0_NPCS1, | |
| 119 #else | |
| 120 .cs_en[1] = false, | |
| 121 #endif | |
| 122 #ifndef CYGDAT_DEVS_SPI_ARM_AT91_BUS0_NPCS2_NONE | |
| 123 .cs_en[2] = true, | |
| 124 .cs_gpio[2] = CYGDAT_DEVS_SPI_ARM_AT91_BUS0_NPCS2, | |
| 125 #else | |
| 126 .cs_en[2] = false, | |
| 127 #endif | |
| 128 #ifndef CYGDAT_DEVS_SPI_ARM_AT91_BUS0_NPCS3_NONE | |
| 129 .cs_en[3] = true, | |
| 130 .cs_gpio[3] = CYGDAT_DEVS_SPI_ARM_AT91_BUS0_NPCS3, | |
| 131 #else | |
| 132 .cs_en[3] = false, | |
| 133 #endif | |
| 1794 | 134 }; |
| 135 | |
| 136 CYG_SPI_DEFINE_BUS_TABLE(cyg_spi_at91_device_t, 0); | |
| 2220 | 137 #endif |
| 138 #ifdef CYGHWR_DEVS_SPI_ARM_AT91_BUS1 | |
| 139 cyg_spi_at91_bus_t cyg_spi_at91_bus1 = { | |
| 140 .spi_bus.spi_transaction_begin = spi_at91_transaction_begin, | |
| 141 .spi_bus.spi_transaction_transfer = spi_at91_transaction_transfer, | |
| 142 .spi_bus.spi_transaction_tick = spi_at91_transaction_tick, | |
| 143 .spi_bus.spi_transaction_end = spi_at91_transaction_end, | |
| 144 .spi_bus.spi_get_config = spi_at91_get_config, | |
| 145 .spi_bus.spi_set_config = spi_at91_set_config, | |
| 146 .interrupt_number = CYGNUM_HAL_INTERRUPT_SPI1, | |
| 147 .base = AT91_SPI1, | |
| 148 #ifndef CYGDAT_DEVS_SPI_ARM_AT91_BUS1_NPCS0_NONE | |
| 149 .cs_en[0] = true, | |
| 150 .cs_gpio[0] = CYGDAT_DEVS_SPI_ARM_AT91_BUS1_NPCS0, | |
| 151 #else | |
| 152 .cs_en[0] = false, | |
| 153 #endif | |
| 154 #ifndef CYGDAT_DEVS_SPI_ARM_AT91_BUS1_NPCS1_NONE | |
| 155 .cs_en[1] = true, | |
| 156 .cs_gpio[1] = CYGDAT_DEVS_SPI_ARM_AT91_BUS1_NPCS1, | |
| 157 #else | |
| 158 .cs_en[1] = false, | |
| 159 #endif | |
| 160 #ifndef CYGDAT_DEVS_SPI_ARM_AT91_BUS1_NPCS2_NONE | |
| 161 .cs_en[2] = true, | |
| 162 .cs_gpio[2] = CYGDAT_DEVS_SPI_ARM_AT91_BUS1_NPCS2, | |
| 163 #else | |
| 164 .cs_en[2] = false, | |
| 165 #endif | |
| 166 #ifndef CYGDAT_DEVS_SPI_ARM_AT91_BUS1_NPCS3_NONE | |
| 167 .cs_en[3] = true, | |
| 168 .cs_gpio[3] = CYGDAT_DEVS_SPI_ARM_AT91_BUS1_NPCS3, | |
| 169 #else | |
| 170 .cs_en[3] = false, | |
| 171 #endif | |
| 172 }; | |
| 1794 | 173 |
| 2220 | 174 CYG_SPI_DEFINE_BUS_TABLE(cyg_spi_at91_device_t, 1); |
| 175 #endif | |
| 1794 | 176 // ------------------------------------------------------------------------- |
| 177 | |
| 178 void | |
| 179 cyg_spi_at91_bus_init(void) | |
| 180 { | |
| 2220 | 181 |
| 182 #ifdef CYGHWR_DEVS_SPI_ARM_AT91_BUS0 | |
| 183 // NOTE: here we let the SPI controller control | |
| 184 // the data in, out and clock signals, but | |
| 185 // we need to handle the chip selects manually | |
| 186 // in order to achieve better chip select control | |
| 187 // in between transactions. | |
| 188 | |
|
2569
e40a637e7b2e
* src/spi_at91.c: Typo fixes from var_io.h. Correctly calculate
asl
parents:
2369
diff
changeset
|
189 // Put SPI MISO, MOSI and SPCK pins into peripheral mode |
| 2220 | 190 HAL_ARM_AT91_PIO_CFG(AT91_SPI_SPCK); |
| 191 HAL_ARM_AT91_PIO_CFG(AT91_SPI_MISO); | |
|
2569
e40a637e7b2e
* src/spi_at91.c: Typo fixes from var_io.h. Correctly calculate
asl
parents:
2369
diff
changeset
|
192 HAL_ARM_AT91_PIO_CFG(AT91_SPI_MOSI); |
| 2220 | 193 spi_at91_init_bus(&cyg_spi_at91_bus0); |
| 194 #endif | |
| 195 #ifdef CYGHWR_DEVS_SPI_ARM_AT91_BUS1 | |
| 196 // NOTE: here we let the SPI controller control | |
| 197 // the data in, out and clock signals, but | |
| 198 // we need to handle the chip selects manually | |
| 199 // in order to achieve better chip select control | |
| 200 // in between transactions. | |
| 201 | |
|
2569
e40a637e7b2e
* src/spi_at91.c: Typo fixes from var_io.h. Correctly calculate
asl
parents:
2369
diff
changeset
|
202 // Put SPI MISO, MOSI and SPCK pins into peripheral mode |
| 2220 | 203 HAL_ARM_AT91_PIO_CFG(AT91_SPI1_SPCK); |
| 204 HAL_ARM_AT91_PIO_CFG(AT91_SPI1_MISO); | |
| 205 HAL_ARM_AT91_PIO_CFG(AT91_SPI1_MOSI); | |
| 206 spi_at91_init_bus(&cyg_spi_at91_bus1); | |
| 207 #endif | |
| 208 } | |
| 209 | |
| 210 // ------------------------------------------------------------------------- | |
| 211 | |
| 212 static void spi_at91_init_bus(cyg_spi_at91_bus_t * spi_bus) | |
| 213 { | |
| 214 cyg_uint32 ctr; | |
| 1794 | 215 // Create and attach SPI interrupt object |
| 2220 | 216 cyg_drv_interrupt_create(spi_bus->interrupt_number, |
| 1794 | 217 4, |
| 2220 | 218 (cyg_addrword_t)spi_bus, |
| 1794 | 219 spi_at91_ISR, |
| 220 spi_at91_DSR, | |
| 2220 | 221 &spi_bus->spi_interrupt_handle, |
| 222 &spi_bus->spi_interrupt); | |
| 1794 | 223 |
| 2220 | 224 cyg_drv_interrupt_attach(spi_bus->spi_interrupt_handle); |
| 1794 | 225 |
| 226 // Init transfer mutex and condition | |
| 2220 | 227 cyg_drv_mutex_init(&spi_bus->transfer_mx); |
| 228 cyg_drv_cond_init(&spi_bus->transfer_cond, | |
| 229 &spi_bus->transfer_mx); | |
| 1794 | 230 |
| 231 // Init flags | |
| 2220 | 232 spi_bus->transfer_end = true; |
| 233 spi_bus->cs_up = false; | |
| 1794 | 234 |
| 235 // Soft reset the SPI controller | |
| 2220 | 236 HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_CR, AT91_SPI_CR_SWRST); |
| 1794 | 237 |
| 238 // Configure SPI pins | |
| 239 | |
| 2220 | 240 |
| 1794 | 241 // Put SPI chip select pins in IO output mode |
| 2220 | 242 for(ctr = 0;ctr<4;ctr++) |
| 243 { | |
| 244 if(spi_bus->cs_en[ctr]) | |
| 245 { | |
| 246 HAL_ARM_AT91_GPIO_CFG_DIRECTION(spi_bus->cs_gpio[ctr],AT91_PIN_OUT); | |
|
2569
e40a637e7b2e
* src/spi_at91.c: Typo fixes from var_io.h. Correctly calculate
asl
parents:
2369
diff
changeset
|
247 HAL_ARM_AT91_GPIO_SET(spi_bus->cs_gpio[ctr]); |
| 2220 | 248 } |
| 249 } | |
| 1794 | 250 // Call upper layer bus init |
| 2220 | 251 CYG_SPI_BUS_COMMON_INIT(&spi_bus->spi_bus); |
| 1794 | 252 } |
| 253 | |
| 254 static cyg_uint32 | |
| 255 spi_at91_ISR(cyg_vector_t vector, cyg_addrword_t data) | |
| 256 { | |
| 257 cyg_uint32 stat; | |
| 2220 | 258 cyg_spi_at91_bus_t * spi_bus = (cyg_spi_at91_bus_t *)data; |
| 1794 | 259 // Read the status register and disable |
|
2569
e40a637e7b2e
* src/spi_at91.c: Typo fixes from var_io.h. Correctly calculate
asl
parents:
2369
diff
changeset
|
260 // the SPI int events that have occurred |
| 1794 | 261 |
| 2220 | 262 HAL_READ_UINT32(spi_bus->base+AT91_SPI_SR, stat); |
| 263 HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_IDR, stat); | |
| 1794 | 264 |
| 265 cyg_drv_interrupt_mask(vector); | |
| 266 cyg_drv_interrupt_acknowledge(vector); | |
| 267 | |
| 268 return CYG_ISR_CALL_DSR; | |
| 269 } | |
| 270 | |
| 271 static void | |
| 272 spi_at91_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data) | |
| 273 { | |
| 274 cyg_spi_at91_bus_t *spi_bus = (cyg_spi_at91_bus_t *) data; | |
| 275 cyg_uint32 stat; | |
| 276 | |
| 277 // Read the status register and | |
|
2569
e40a637e7b2e
* src/spi_at91.c: Typo fixes from var_io.h. Correctly calculate
asl
parents:
2369
diff
changeset
|
278 // check for transfer completion |
| 1794 | 279 |
| 2220 | 280 HAL_READ_UINT32(spi_bus->base+AT91_SPI_SR, stat); |
| 1794 | 281 |
| 282 if((stat & AT91_SPI_SR_ENDRX) && (stat & AT91_SPI_SR_ENDTX)) | |
| 283 { | |
| 284 // Transfer ended | |
| 285 spi_bus->transfer_end = true; | |
| 286 cyg_drv_cond_signal(&spi_bus->transfer_cond); | |
| 287 } | |
| 288 else | |
| 289 { | |
| 290 // Transfer still in progress - unmask the SPI | |
| 291 // int so we can get more SPI int events | |
| 292 cyg_drv_interrupt_unmask(vector); | |
| 293 } | |
| 294 } | |
| 295 | |
| 296 static cyg_bool | |
| 297 spi_at91_calc_scbr(cyg_spi_at91_device_t *dev) | |
| 298 { | |
| 299 cyg_uint32 scbr; | |
| 300 cyg_bool res = true; | |
| 301 | |
| 302 // Calculate SCBR from baud rate | |
| 303 | |
|
2569
e40a637e7b2e
* src/spi_at91.c: Typo fixes from var_io.h. Correctly calculate
asl
parents:
2369
diff
changeset
|
304 scbr = CYGNUM_HAL_ARM_AT91_CLOCK_SPEED / dev->cl_brate; |
|
e40a637e7b2e
* src/spi_at91.c: Typo fixes from var_io.h. Correctly calculate
asl
parents:
2369
diff
changeset
|
305 if ((2*(CYGNUM_HAL_ARM_AT91_CLOCK_SPEED % dev->cl_brate)) >= dev->cl_brate) |
|
e40a637e7b2e
* src/spi_at91.c: Typo fixes from var_io.h. Correctly calculate
asl
parents:
2369
diff
changeset
|
306 scbr++; |
| 1794 | 307 |
| 308 if (scbr < 2) | |
| 309 { | |
| 310 dev->cl_scbr = 2; | |
| 311 dev->cl_div32 = 0; | |
| 312 res = false; | |
| 313 } | |
| 314 else if (scbr > 255) | |
| 315 { | |
| 316 dev->cl_div32 = 1; | |
| 317 | |
|
2569
e40a637e7b2e
* src/spi_at91.c: Typo fixes from var_io.h. Correctly calculate
asl
parents:
2369
diff
changeset
|
318 scbr = CYGNUM_HAL_ARM_AT91_CLOCK_SPEED / (32*dev->cl_brate); |
| 1794 | 319 |
| 320 if (scbr < 2) | |
| 321 { | |
| 322 dev->cl_scbr = 2; | |
| 323 res = false; | |
| 324 } | |
| 325 else if (scbr > 255) | |
| 326 { | |
| 327 dev->cl_scbr = 255; | |
| 328 res = false; | |
| 329 } | |
| 330 else | |
| 331 dev->cl_scbr = (cyg_uint8)scbr; | |
| 332 } | |
| 333 else | |
| 334 { | |
| 335 dev->cl_scbr = (cyg_uint8)scbr; | |
| 336 dev->cl_div32 = 0; | |
| 337 } | |
| 338 | |
| 339 return res; | |
| 340 } | |
| 341 | |
| 342 static void | |
| 2220 | 343 spi_at91_set_npcs(cyg_spi_at91_bus_t *spi_bus,int val) |
| 344 { | |
| 345 cyg_uint32 ctr; | |
| 346 for(ctr=0;ctr<4;ctr++) | |
| 347 { | |
| 348 if(spi_bus->cs_en[ctr]) | |
| 349 { | |
|
2369
ad59d69732ff
* src/spi_at91.c (spi_at91_transaction_begin): Don't use #ifdef
asl
parents:
2290
diff
changeset
|
350 HAL_ARM_AT91_GPIO_PUT(spi_bus->cs_gpio[ctr], (val & (1<<ctr))); |
| 2220 | 351 } |
| 352 } | |
| 353 } | |
| 354 | |
| 355 static void | |
| 1794 | 356 spi_at91_start_transfer(cyg_spi_at91_device_t *dev) |
| 357 { | |
| 358 cyg_spi_at91_bus_t *spi_bus = (cyg_spi_at91_bus_t *)dev->spi_device.spi_bus; | |
| 359 | |
| 360 if (spi_bus->cs_up) | |
| 361 return; | |
| 362 | |
| 363 // Force minimal delay between two transfers - in case two transfers | |
| 364 // follow each other w/o delay, then we have to wait here in order for | |
| 365 // the peripheral device to detect cs transition from inactive to active. | |
| 366 CYGACC_CALL_IF_DELAY_US(dev->tr_bt_udly); | |
| 367 | |
| 368 // Raise CS | |
| 369 | |
| 370 #ifdef CYGHWR_DEVS_SPI_ARM_AT91_PCSDEC | |
| 2220 | 371 spi_at91_set_npcs(spi_bus,~dev->dev_num); |
| 1794 | 372 #else |
|
2290
d2e08e32ef04
* src/spi_at91.c: Fixed the chip select functions.
jlarmour
parents:
2220
diff
changeset
|
373 spi_at91_set_npcs(spi_bus,~(1<<dev->dev_num)); |
| 1794 | 374 #endif |
| 375 CYGACC_CALL_IF_DELAY_US(dev->cs_up_udly); | |
| 376 | |
| 377 spi_bus->cs_up = true; | |
| 378 } | |
| 379 | |
| 380 static void | |
| 381 spi_at91_drop_cs(cyg_spi_at91_device_t *dev) | |
| 382 { | |
| 383 cyg_spi_at91_bus_t *spi_bus = (cyg_spi_at91_bus_t *)dev->spi_device.spi_bus; | |
| 384 | |
| 385 if (!spi_bus->cs_up) | |
| 386 return; | |
| 387 | |
| 388 // Drop CS | |
| 389 | |
| 390 CYGACC_CALL_IF_DELAY_US(dev->cs_dw_udly); | |
| 2220 | 391 spi_at91_set_npcs(spi_bus,0x0F); |
| 1794 | 392 spi_bus->cs_up = false; |
| 393 } | |
| 394 | |
| 395 static void | |
| 396 spi_at91_transfer(cyg_spi_at91_device_t *dev, | |
| 397 cyg_uint32 count, | |
| 398 const cyg_uint8 *tx_data, | |
| 399 cyg_uint8 *rx_data) | |
| 400 { | |
| 401 cyg_spi_at91_bus_t *spi_bus = (cyg_spi_at91_bus_t *)dev->spi_device.spi_bus; | |
| 402 | |
| 403 // Since PDC transfer buffer counters are 16 bit long, | |
| 404 // we have to split longer transfers into chunks. | |
| 405 while (count > 0) | |
| 406 { | |
| 407 cyg_uint16 tr_count = count > 0xFFFF ? 0xFFFF : count; | |
| 408 | |
| 409 // Set rx buf pointer and counter | |
| 410 if (NULL != rx_data) | |
| 411 { | |
| 2220 | 412 HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_RPR, (cyg_uint32)rx_data); |
| 413 HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_RCR, (cyg_uint32)tr_count); | |
| 1794 | 414 } |
| 415 | |
| 416 // Set tx buf pointer and counter | |
| 2220 | 417 HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_TPR, (cyg_uint32)tx_data); |
| 418 HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_TCR, (cyg_uint32)tr_count); | |
| 1794 | 419 |
| 2220 | 420 #ifdef AT91_SPI_PTCR |
| 421 HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_PTCR, | |
| 422 AT91_SPI_PTCR_RXTEN | AT91_SPI_PTCR_TXTEN); | |
| 423 #endif | |
| 1794 | 424 // Enable the SPI int events we are interested in |
| 2220 | 425 HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_IER, |
| 426 AT91_SPI_SR_ENDRX | AT91_SPI_SR_ENDTX); | |
| 1794 | 427 |
| 428 cyg_drv_mutex_lock(&spi_bus->transfer_mx); | |
| 429 { | |
| 430 spi_bus->transfer_end = false; | |
| 431 | |
| 432 // Unmask the SPI int | |
| 2220 | 433 cyg_drv_interrupt_unmask(spi_bus->interrupt_number); |
| 1794 | 434 |
|
2569
e40a637e7b2e
* src/spi_at91.c: Typo fixes from var_io.h. Correctly calculate
asl
parents:
2369
diff
changeset
|
435 // Wait for its completion |
| 1794 | 436 cyg_drv_dsr_lock(); |
| 437 { | |
| 438 while (!spi_bus->transfer_end) | |
| 439 cyg_drv_cond_wait(&spi_bus->transfer_cond); | |
| 440 } | |
| 441 cyg_drv_dsr_unlock(); | |
| 442 } | |
| 443 cyg_drv_mutex_unlock(&spi_bus->transfer_mx); | |
| 444 | |
| 445 if (NULL == rx_data) | |
| 446 { | |
| 447 cyg_uint32 val; | |
| 448 | |
| 449 // If rx buffer was NULL, then the PDC receiver data transfer | |
| 450 // was not started and we didn't wait for ENDRX, but only for | |
| 451 // ENDTX. Meaning that right now the last byte is being serialized | |
| 452 // over the line and when finished input data will appear in | |
| 453 // rx data reg. We have to wait for this to happen here, if we | |
| 454 // don't we'll get the last received byte as the first one in the | |
| 455 // next transfer! | |
| 456 | |
| 457 // FIXME: is there any better way to do this? | |
| 458 // If not, then precalculate this value. | |
| 459 val = 8000000/dev->cl_brate; | |
| 460 CYGACC_CALL_IF_DELAY_US(val > 1 ? val : 1); | |
| 461 | |
| 462 // Clear the rx data reg | |
| 2220 | 463 HAL_READ_UINT32(spi_bus->base+AT91_SPI_RDR, val); |
| 1794 | 464 } |
| 465 | |
| 466 // Adjust running variables | |
| 467 | |
|
1807
950c4e9bf178
* src/spi_at91.c: Fixed a typo in spi_at91_transfer reported by
asl
parents:
1794
diff
changeset
|
468 if (NULL != rx_data) |
| 1794 | 469 rx_data += tr_count; |
| 470 tx_data += tr_count; | |
| 471 count -= tr_count; | |
| 472 } | |
| 473 } | |
| 474 | |
| 475 static void | |
| 476 spi_at91_transfer_polled(cyg_spi_at91_device_t *dev, | |
| 477 cyg_uint32 count, | |
| 478 const cyg_uint8 *tx_data, | |
| 479 cyg_uint8 *rx_data) | |
| 480 { | |
| 481 cyg_uint32 val; | |
| 2220 | 482 cyg_spi_at91_bus_t *spi_bus = (cyg_spi_at91_bus_t *)dev->spi_device.spi_bus; |
| 1794 | 483 |
| 484 // Transmit and receive byte by byte | |
| 485 while (count-- > 0) | |
| 486 { | |
| 487 // Wait for transmit data register empty | |
| 488 do | |
| 489 { | |
| 2220 | 490 HAL_READ_UINT32(spi_bus->base+AT91_SPI_SR, val); |
| 1794 | 491 } while ( !(val & AT91_SPI_SR_TDRE) ); |
| 492 | |
| 493 // Send next byte over the wire | |
| 494 val = *tx_data++; | |
| 2220 | 495 HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_TDR, val); |
| 1794 | 496 |
| 497 // Wait for reveive data register full | |
| 498 do | |
| 499 { | |
| 2220 | 500 HAL_READ_UINT32(spi_bus->base+AT91_SPI_SR, val); |
| 1794 | 501 } while ( !(val & AT91_SPI_SR_RDRF) ); |
| 502 | |
| 503 // Store received byte | |
| 2220 | 504 HAL_READ_UINT32(spi_bus->base+AT91_SPI_RDR, val); |
| 1794 | 505 if (NULL != rx_data) |
| 506 *rx_data++ = val; | |
| 507 } | |
| 508 } | |
| 509 | |
| 510 // ------------------------------------------------------------------------- | |
| 511 | |
| 512 static void | |
| 513 spi_at91_transaction_begin(cyg_spi_device *dev) | |
| 514 { | |
| 515 cyg_spi_at91_device_t *at91_spi_dev = (cyg_spi_at91_device_t *) dev; | |
| 2220 | 516 cyg_spi_at91_bus_t *spi_bus = |
| 517 (cyg_spi_at91_bus_t *)at91_spi_dev->spi_device.spi_bus; | |
| 1794 | 518 cyg_uint32 val; |
| 519 | |
| 520 if (!at91_spi_dev->init) | |
| 521 { | |
| 522 at91_spi_dev->init = true; | |
| 523 spi_at91_calc_scbr(at91_spi_dev); | |
| 524 } | |
| 525 | |
| 526 // Configure SPI channel 0 - this is the only channel we | |
| 527 // use for all devices since we drive chip selects manually | |
| 528 | |
| 529 val = AT91_SPI_CSR_BITS8; | |
| 530 | |
| 531 if (1 == at91_spi_dev->cl_pol) | |
| 532 val |= AT91_SPI_CSR_CPOL; | |
| 533 | |
| 534 if (1 == at91_spi_dev->cl_pha) | |
| 535 val |= AT91_SPI_CSR_NCPHA; | |
| 536 | |
| 537 val |= AT91_SPI_CSR_SCBR(at91_spi_dev->cl_scbr); | |
| 538 | |
| 2220 | 539 HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_CSR0, val); |
| 1794 | 540 |
| 541 // Enable SPI clock | |
| 2220 | 542 HAL_WRITE_UINT32(AT91_PMC+AT91_PMC_PCER, 1<<spi_bus->interrupt_number); |
| 543 | |
| 1794 | 544 // Enable the SPI controller |
| 2220 | 545 HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_CR, AT91_SPI_CR_SPIEN); |
| 1794 | 546 |
|
2290
d2e08e32ef04
* src/spi_at91.c: Fixed the chip select functions.
jlarmour
parents:
2220
diff
changeset
|
547 /* As we are using this driver only in master mode with NPCS0 |
|
2569
e40a637e7b2e
* src/spi_at91.c: Typo fixes from var_io.h. Correctly calculate
asl
parents:
2369
diff
changeset
|
548 configured as GPIO instead of a peripheral pin, it is necessary |
|
2290
d2e08e32ef04
* src/spi_at91.c: Fixed the chip select functions.
jlarmour
parents:
2220
diff
changeset
|
549 for the Mode Failure detection to be switched off as this will |
|
d2e08e32ef04
* src/spi_at91.c: Fixed the chip select functions.
jlarmour
parents:
2220
diff
changeset
|
550 cause havoc with the driver */ |
|
d2e08e32ef04
* src/spi_at91.c: Fixed the chip select functions.
jlarmour
parents:
2220
diff
changeset
|
551 |
| 1794 | 552 // Put SPI bus into master mode |
|
2369
ad59d69732ff
* src/spi_at91.c (spi_at91_transaction_begin): Don't use #ifdef
asl
parents:
2290
diff
changeset
|
553 if (1 == at91_spi_dev->cl_div32) { |
|
ad59d69732ff
* src/spi_at91.c (spi_at91_transaction_begin): Don't use #ifdef
asl
parents:
2290
diff
changeset
|
554 val = AT91_SPI_MR_MSTR | AT91_SPI_MR_DIV32; |
|
2290
d2e08e32ef04
* src/spi_at91.c: Fixed the chip select functions.
jlarmour
parents:
2220
diff
changeset
|
555 #ifdef AT91_SPI_MR_MODFDIS |
|
2369
ad59d69732ff
* src/spi_at91.c (spi_at91_transaction_begin): Don't use #ifdef
asl
parents:
2290
diff
changeset
|
556 val |= AT91_SPI_MR_MODFDIS; |
|
2290
d2e08e32ef04
* src/spi_at91.c: Fixed the chip select functions.
jlarmour
parents:
2220
diff
changeset
|
557 #endif |
|
2369
ad59d69732ff
* src/spi_at91.c (spi_at91_transaction_begin): Don't use #ifdef
asl
parents:
2290
diff
changeset
|
558 HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_MR, val); |
|
ad59d69732ff
* src/spi_at91.c (spi_at91_transaction_begin): Don't use #ifdef
asl
parents:
2290
diff
changeset
|
559 } else { |
|
ad59d69732ff
* src/spi_at91.c (spi_at91_transaction_begin): Don't use #ifdef
asl
parents:
2290
diff
changeset
|
560 val = AT91_SPI_MR_MSTR; |
|
2290
d2e08e32ef04
* src/spi_at91.c: Fixed the chip select functions.
jlarmour
parents:
2220
diff
changeset
|
561 #ifdef AT91_SPI_MR_MODFDIS |
|
2369
ad59d69732ff
* src/spi_at91.c (spi_at91_transaction_begin): Don't use #ifdef
asl
parents:
2290
diff
changeset
|
562 val |= AT91_SPI_MR_MODFDIS; |
|
2290
d2e08e32ef04
* src/spi_at91.c: Fixed the chip select functions.
jlarmour
parents:
2220
diff
changeset
|
563 #endif |
|
2369
ad59d69732ff
* src/spi_at91.c (spi_at91_transaction_begin): Don't use #ifdef
asl
parents:
2290
diff
changeset
|
564 HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_MR, val); |
|
ad59d69732ff
* src/spi_at91.c (spi_at91_transaction_begin): Don't use #ifdef
asl
parents:
2290
diff
changeset
|
565 } |
| 1794 | 566 } |
| 567 | |
| 568 static void | |
| 569 spi_at91_transaction_transfer(cyg_spi_device *dev, | |
| 570 cyg_bool polled, | |
| 571 cyg_uint32 count, | |
| 572 const cyg_uint8 *tx_data, | |
| 573 cyg_uint8 *rx_data, | |
| 574 cyg_bool drop_cs) | |
| 575 { | |
| 576 cyg_spi_at91_device_t *at91_spi_dev = (cyg_spi_at91_device_t *) dev; | |
| 577 | |
| 578 // Select the device if not already selected | |
| 579 spi_at91_start_transfer(at91_spi_dev); | |
| 580 | |
| 581 // Perform the transfer | |
| 582 if (polled) | |
| 583 spi_at91_transfer_polled(at91_spi_dev, count, tx_data, rx_data); | |
| 584 else | |
| 585 spi_at91_transfer(at91_spi_dev, count, tx_data, rx_data); | |
| 586 | |
| 587 // Deselect the device if requested | |
| 588 if (drop_cs) | |
| 589 spi_at91_drop_cs(at91_spi_dev); | |
| 590 } | |
| 591 | |
| 592 static void | |
| 593 spi_at91_transaction_tick(cyg_spi_device *dev, | |
| 594 cyg_bool polled, | |
| 595 cyg_uint32 count) | |
| 596 { | |
| 597 const cyg_uint32 zeros[10] = { 0,0,0,0,0,0,0,0,0,0 }; | |
| 598 | |
| 599 cyg_spi_at91_device_t *at91_spi_dev = (cyg_spi_at91_device_t *) dev; | |
| 600 | |
| 601 // Transfer count zeros to the device - we don't touch the | |
| 602 // chip select, the device could be selected or deselected. | |
| 603 // It is up to the device driver to decide in wich state the | |
| 604 // device will be ticked. | |
| 605 | |
| 606 while (count > 0) | |
| 607 { | |
| 608 int tcnt = count > 40 ? 40 : count; | |
| 609 | |
| 610 if (polled) | |
| 611 spi_at91_transfer_polled(at91_spi_dev, tcnt, | |
| 612 (const cyg_uint8 *) zeros, NULL); | |
| 613 else | |
| 614 spi_at91_transfer(at91_spi_dev, tcnt, | |
| 615 (const cyg_uint8 *) zeros, NULL); | |
| 616 | |
| 617 count -= tcnt; | |
| 618 } | |
| 619 } | |
| 620 | |
| 621 static void | |
| 622 spi_at91_transaction_end(cyg_spi_device* dev) | |
| 623 { | |
| 2220 | 624 cyg_spi_at91_device_t * at91_spi_dev = (cyg_spi_at91_device_t *)dev; |
| 625 cyg_spi_at91_bus_t *spi_bus = | |
| 626 (cyg_spi_at91_bus_t *)at91_spi_dev->spi_device.spi_bus; | |
| 1794 | 627 |
| 2220 | 628 // Disable the SPI controller |
| 629 HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_CR, AT91_SPI_CR_SPIDIS); | |
| 630 | |
| 1794 | 631 // Disable SPI clock |
| 2220 | 632 HAL_WRITE_UINT32(AT91_PMC+AT91_PMC_PCDR,1<<spi_bus->interrupt_number); |
| 633 | |
| 1794 | 634 spi_at91_drop_cs((cyg_spi_at91_device_t *) dev); |
| 635 } | |
| 636 | |
| 637 static int | |
| 638 spi_at91_get_config(cyg_spi_device *dev, | |
| 639 cyg_uint32 key, | |
| 640 void *buf, | |
| 641 cyg_uint32 *len) | |
| 642 { | |
| 643 cyg_spi_at91_device_t *at91_spi_dev = (cyg_spi_at91_device_t *) dev; | |
| 644 | |
| 645 switch (key) | |
| 646 { | |
| 647 case CYG_IO_GET_CONFIG_SPI_CLOCKRATE: | |
| 648 { | |
| 649 if (*len != sizeof(cyg_uint32)) | |
| 650 return -EINVAL; | |
| 651 else | |
| 652 { | |
| 653 cyg_uint32 *cl_brate = (cyg_uint32 *)buf; | |
| 654 *cl_brate = at91_spi_dev->cl_brate; | |
| 655 } | |
| 656 } | |
| 657 break; | |
| 658 default: | |
| 659 return -EINVAL; | |
| 660 } | |
| 661 return ENOERR; | |
| 662 } | |
| 663 | |
| 664 static int | |
| 665 spi_at91_set_config(cyg_spi_device *dev, | |
| 666 cyg_uint32 key, | |
| 667 const void *buf, | |
| 668 cyg_uint32 *len) | |
| 669 { | |
| 670 cyg_spi_at91_device_t *at91_spi_dev = (cyg_spi_at91_device_t *) dev; | |
| 671 | |
| 672 switch (key) | |
| 673 { | |
| 674 case CYG_IO_SET_CONFIG_SPI_CLOCKRATE: | |
| 675 { | |
| 676 if (*len != sizeof(cyg_uint32)) | |
| 677 return -EINVAL; | |
| 678 else | |
| 679 { | |
| 680 cyg_uint32 cl_brate = *((cyg_uint32 *)buf); | |
| 681 cyg_uint32 old_cl_brate = at91_spi_dev->cl_brate; | |
| 682 | |
| 683 at91_spi_dev->cl_brate = cl_brate; | |
| 684 | |
| 685 if (!spi_at91_calc_scbr(at91_spi_dev)) | |
| 686 { | |
| 687 at91_spi_dev->cl_brate = old_cl_brate; | |
| 688 spi_at91_calc_scbr(at91_spi_dev); | |
| 689 return -EINVAL; | |
| 690 } | |
| 691 } | |
| 692 } | |
| 693 break; | |
| 694 default: | |
| 695 return -EINVAL; | |
| 696 } | |
| 697 return ENOERR; | |
| 698 } | |
| 699 | |
| 700 // ------------------------------------------------------------------------- | |
| 701 // EOF spi_at91.c |
