aboutsummaryrefslogtreecommitdiffstats
path: root/src/lpc176x/spi.c
blob: c297fb011fac78c15d9e931c62501de2c430b810 (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
// SPI support on lpc176x
//
// Copyright (C) 2018  Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.

#include "command.h" // shutdown
#include "gpio.h" // spi_setup
#include "internal.h" // gpio_peripheral
#include "sched.h" // sched_shutdown

DECL_ENUMERATION("spi_bus", "ssp0", 0);
DECL_CONSTANT_STR("BUS_PINS_ssp0", "P0.17,P0.18,P0.15");

static void
spi_init(void)
{
    static int have_run_init;
    if (have_run_init)
        return;
    have_run_init = 1;

    // Configure MISO0, MOSI0, SCK0 pins
    gpio_peripheral(GPIO(0, 17), 2, 0);
    gpio_peripheral(GPIO(0, 18), 2, 0);
    gpio_peripheral(GPIO(0, 15), 2, 0);

    // Setup clock
    enable_pclock(PCLK_SSP0);

    // Set initial registers
    LPC_SSP0->CR0 = 0x07;
    LPC_SSP0->CPSR = 254;
    LPC_SSP0->CR1 = 1<<1;
}

struct spi_config
spi_setup(uint32_t bus, uint8_t mode, uint32_t rate)
{
    if (bus)
        shutdown("Invalid spi_setup parameters");

    // Make sure bus is enabled
    spi_init();

    // Setup clock rate and mode
    struct spi_config res = {0, 0};
    uint32_t pclk = SystemCoreClock;
    uint32_t div = DIV_ROUND_UP(pclk/2, rate) << 1;
    res.cpsr = div < 2 ? 2 : (div > 254 ? 254 : div);
    res.cr0 = 0x07 | (mode << 6);

    return res;
}

void
spi_prepare(struct spi_config config)
{
    LPC_SSP0->CR0 = config.cr0;
    LPC_SSP0->CPSR = config.cpsr;
}

void
spi_transfer(struct spi_config config, uint8_t receive_data
             , uint8_t len, uint8_t *data)
{
    if (receive_data) {
        while (len--) {
            LPC_SSP0->DR = *data;
            // wait for read data to be ready
            while (!(LPC_SSP0->SR & (1<<2)))
                ;
            // get data
            *data++ = LPC_SSP0->DR;
        }
    } else {
        while (len--) {
            LPC_SSP0->DR = *data++;
            // wait for read data to be ready
            while (!(LPC_SSP0->SR & (1<<2)))
                ;
            // read data (to clear receive fifo)
            LPC_SSP0->DR;
        }
    }
}