11bb92983SJerome Forissier // SPDX-License-Identifier: BSD-2-Clause
271c1078aSVictor Chong /*
371c1078aSVictor Chong * Copyright (c) 2016, Linaro Limited
471c1078aSVictor Chong */
571c1078aSVictor Chong
671c1078aSVictor Chong #include <drivers/pl022_spi.h>
771c1078aSVictor Chong #include <drivers/pl061_gpio.h>
871c1078aSVictor Chong #include <hikey_peripherals.h>
926128b8fSVictor Chong #include <io.h>
1026128b8fSVictor Chong #include <kernel/tee_time.h>
1178182cffSVictor Chong #include <mm/core_memprot.h>
1271c1078aSVictor Chong #include <stdint.h>
1371c1078aSVictor Chong #include <trace.h>
1426128b8fSVictor Chong #include <util.h>
1526128b8fSVictor Chong
1626128b8fSVictor Chong #define PL022_STAT 0x00C
1726128b8fSVictor Chong #define PL022_STAT_BSY SHIFT_U32(1, 4)
1826128b8fSVictor Chong
spi_cs_callback(enum gpio_level value)1926128b8fSVictor Chong static void spi_cs_callback(enum gpio_level value)
2026128b8fSVictor Chong {
2126128b8fSVictor Chong static bool inited;
2226128b8fSVictor Chong static struct pl061_data pd;
23c2e4eb43SAnton Rybakov vaddr_t gpio6_base = core_mmu_get_va(GPIO6_BASE, MEM_AREA_IO_NSEC,
24c2e4eb43SAnton Rybakov PL061_REG_SIZE);
25c2e4eb43SAnton Rybakov vaddr_t spi_base = core_mmu_get_va(SPI_BASE, MEM_AREA_IO_NSEC,
26c2e4eb43SAnton Rybakov PL022_REG_SIZE);
2726128b8fSVictor Chong
2826128b8fSVictor Chong if (!inited) {
2926128b8fSVictor Chong pl061_init(&pd);
3026128b8fSVictor Chong pl061_register(gpio6_base, 6);
3126128b8fSVictor Chong pl061_set_mode_control(GPIO6_2, PL061_MC_SW);
32fc5d98e8SManish Tomar pd.chip.ops->set_interrupt(NULL, GPIO6_2,
33fc5d98e8SManish Tomar GPIO_INTERRUPT_DISABLE);
34fc5d98e8SManish Tomar pd.chip.ops->set_direction(NULL, GPIO6_2, GPIO_DIR_OUT);
3526128b8fSVictor Chong inited = true;
3626128b8fSVictor Chong }
3726128b8fSVictor Chong
381c3ba0d4SEtienne Carriere if (io_read8(spi_base + PL022_STAT) & PL022_STAT_BSY)
3926128b8fSVictor Chong DMSG("pl022 busy - do NOT set CS!");
401c3ba0d4SEtienne Carriere while (io_read8(spi_base + PL022_STAT) & PL022_STAT_BSY)
4126128b8fSVictor Chong ;
4226128b8fSVictor Chong DMSG("pl022 done - set CS!");
4326128b8fSVictor Chong
44fc5d98e8SManish Tomar pd.chip.ops->set_value(NULL, GPIO6_2, value);
4526128b8fSVictor Chong }
4626128b8fSVictor Chong
spi_set_cs_mux(uint32_t val)4726128b8fSVictor Chong static void spi_set_cs_mux(uint32_t val)
4826128b8fSVictor Chong {
4926128b8fSVictor Chong uint32_t data;
50c2e4eb43SAnton Rybakov vaddr_t pmx0_base = core_mmu_get_va(PMX0_BASE, MEM_AREA_IO_NSEC,
51c2e4eb43SAnton Rybakov PMX0_REG_SIZE);
5226128b8fSVictor Chong
5326128b8fSVictor Chong if (val == PINMUX_SPI) {
5426128b8fSVictor Chong DMSG("Configure gpio6 pin2 as SPI");
551c3ba0d4SEtienne Carriere io_write32(pmx0_base + PMX0_IOMG106, PINMUX_SPI);
5626128b8fSVictor Chong } else {
5726128b8fSVictor Chong DMSG("Configure gpio6 pin2 as GPIO");
581c3ba0d4SEtienne Carriere io_write32(pmx0_base + PMX0_IOMG106, PINMUX_GPIO);
5926128b8fSVictor Chong }
6026128b8fSVictor Chong
611c3ba0d4SEtienne Carriere data = io_read32(pmx0_base + PMX0_IOMG106);
6226128b8fSVictor Chong if (data)
6326128b8fSVictor Chong DMSG("gpio6 pin2 is SPI");
6426128b8fSVictor Chong else
6526128b8fSVictor Chong DMSG("gpio6 pin2 is GPIO");
6626128b8fSVictor Chong }
6726128b8fSVictor Chong
spi_test_with_manual_cs_control(void)6826128b8fSVictor Chong static void spi_test_with_manual_cs_control(void)
6926128b8fSVictor Chong {
7026128b8fSVictor Chong struct pl022_data pd;
71c2e4eb43SAnton Rybakov vaddr_t spi_base = core_mmu_get_va(SPI_BASE, MEM_AREA_IO_NSEC,
72c2e4eb43SAnton Rybakov PL022_REG_SIZE);
7326128b8fSVictor Chong uint8_t tx[3] = {0x01, 0x80, 0x00};
7426128b8fSVictor Chong uint8_t rx[3] = {0};
7526128b8fSVictor Chong size_t i, j, len = 3;
7626128b8fSVictor Chong enum spi_result res;
7726128b8fSVictor Chong
7826128b8fSVictor Chong spi_set_cs_mux(PINMUX_GPIO);
7926128b8fSVictor Chong
8026128b8fSVictor Chong DMSG("Set CS callback");
8126128b8fSVictor Chong pd.cs_control = PL022_CS_CTRL_MANUAL;
8226128b8fSVictor Chong
83*bce2f88aSVincent Mailhol DMSG("spi_base: 0x%" PRIxVA, spi_base);
8426128b8fSVictor Chong DMSG("Configure SPI");
8526128b8fSVictor Chong pd.base = spi_base;
8626128b8fSVictor Chong pd.clk_hz = SPI_CLK_HZ;
8726128b8fSVictor Chong pd.speed_hz = SPI_10_KHZ;
8826128b8fSVictor Chong pd.mode = SPI_MODE0;
8926128b8fSVictor Chong pd.data_size_bits = 8;
9026128b8fSVictor Chong pd.loopback = true;
9126128b8fSVictor Chong
9226128b8fSVictor Chong pl022_init(&pd);
9326128b8fSVictor Chong pd.chip.ops->configure(&pd.chip);
9426128b8fSVictor Chong pd.chip.ops->start(&pd.chip);
9526128b8fSVictor Chong
9626128b8fSVictor Chong /*
9726128b8fSVictor Chong * Pulse CS only once for the whole transmission.
9826128b8fSVictor Chong * This is the scheme used by the pl022 driver.
9926128b8fSVictor Chong */
10026128b8fSVictor Chong spi_cs_callback(GPIO_LEVEL_HIGH);
10126128b8fSVictor Chong tee_time_busy_wait(2);
10226128b8fSVictor Chong spi_cs_callback(GPIO_LEVEL_LOW);
10326128b8fSVictor Chong for (j = 0; j < 10; j++) {
10426128b8fSVictor Chong DMSG("SPI test loop: %zu", j);
10526128b8fSVictor Chong res = pd.chip.ops->txrx8(&pd.chip, tx, rx, len);
10626128b8fSVictor Chong if (res) {
10726128b8fSVictor Chong EMSG("SPI transceive error %d", res);
10826128b8fSVictor Chong break;
10926128b8fSVictor Chong }
11026128b8fSVictor Chong
11126128b8fSVictor Chong for (i = 0; i < len; i++)
11226128b8fSVictor Chong DMSG("rx[%zu] = 0x%x", i, rx[i]);
11326128b8fSVictor Chong
11426128b8fSVictor Chong tee_time_busy_wait(20);
11526128b8fSVictor Chong }
11626128b8fSVictor Chong spi_cs_callback(GPIO_LEVEL_HIGH);
11726128b8fSVictor Chong
11826128b8fSVictor Chong /* Pulse CS once per transfer */
11926128b8fSVictor Chong spi_cs_callback(GPIO_LEVEL_HIGH);
12026128b8fSVictor Chong tee_time_busy_wait(2);
12126128b8fSVictor Chong for (j = 10; j < 20; j++) {
12226128b8fSVictor Chong DMSG("SPI test loop: %zu", j);
12326128b8fSVictor Chong spi_cs_callback(GPIO_LEVEL_LOW);
12426128b8fSVictor Chong res = pd.chip.ops->txrx8(&pd.chip, tx, rx, len);
12526128b8fSVictor Chong if (res) {
12626128b8fSVictor Chong EMSG("SPI transceive error %d", res);
12726128b8fSVictor Chong break;
12826128b8fSVictor Chong }
12926128b8fSVictor Chong
13026128b8fSVictor Chong for (i = 0; i < len; i++)
13126128b8fSVictor Chong DMSG("rx[%zu] = 0x%x", i, rx[i]);
13226128b8fSVictor Chong
13326128b8fSVictor Chong tee_time_busy_wait(20);
13426128b8fSVictor Chong spi_cs_callback(GPIO_LEVEL_HIGH);
13526128b8fSVictor Chong }
13626128b8fSVictor Chong
13726128b8fSVictor Chong /* Pulse CS once per word/byte */
13826128b8fSVictor Chong spi_set_cs_mux(PINMUX_SPI);
13926128b8fSVictor Chong tee_time_busy_wait(2);
14026128b8fSVictor Chong for (j = 20; j < 30; j++) {
14126128b8fSVictor Chong DMSG("SPI test loop: %zu", j);
14226128b8fSVictor Chong res = pd.chip.ops->txrx8(&pd.chip, tx, rx, len);
14326128b8fSVictor Chong if (res) {
14426128b8fSVictor Chong EMSG("SPI transceive error %d", res);
14526128b8fSVictor Chong break;
14626128b8fSVictor Chong }
14726128b8fSVictor Chong
14826128b8fSVictor Chong for (i = 0; i < len; i++)
14926128b8fSVictor Chong DMSG("rx[%zu] = 0x%x", i, rx[i]);
15026128b8fSVictor Chong
15126128b8fSVictor Chong tee_time_busy_wait(20);
15226128b8fSVictor Chong }
15326128b8fSVictor Chong
15426128b8fSVictor Chong pd.chip.ops->end(&pd.chip);
15526128b8fSVictor Chong }
15626128b8fSVictor Chong
spi_test_with_registered_cs_cb(void)15726128b8fSVictor Chong static void spi_test_with_registered_cs_cb(void)
15826128b8fSVictor Chong {
15926128b8fSVictor Chong struct pl022_data pd;
160c2e4eb43SAnton Rybakov vaddr_t spi_base = core_mmu_get_va(SPI_BASE, MEM_AREA_IO_NSEC,
161c2e4eb43SAnton Rybakov PL022_REG_SIZE);
16226128b8fSVictor Chong uint8_t tx[3] = {0x01, 0x80, 0x00};
16326128b8fSVictor Chong uint8_t rx[3] = {0};
16426128b8fSVictor Chong size_t i, j, len = 3;
16526128b8fSVictor Chong enum spi_result res;
16626128b8fSVictor Chong
16726128b8fSVictor Chong spi_set_cs_mux(PINMUX_GPIO);
16826128b8fSVictor Chong
16926128b8fSVictor Chong DMSG("Set CS callback");
17026128b8fSVictor Chong pd.cs_data.cs_cb = spi_cs_callback;
17126128b8fSVictor Chong pd.cs_control = PL022_CS_CTRL_CB;
17226128b8fSVictor Chong
173*bce2f88aSVincent Mailhol DMSG("spi_base: 0x%" PRIxVA, spi_base);
17426128b8fSVictor Chong DMSG("Configure SPI");
17526128b8fSVictor Chong pd.base = spi_base;
17626128b8fSVictor Chong pd.clk_hz = SPI_CLK_HZ;
17726128b8fSVictor Chong pd.speed_hz = SPI_10_KHZ;
17826128b8fSVictor Chong pd.mode = SPI_MODE0;
17926128b8fSVictor Chong pd.data_size_bits = 8;
18026128b8fSVictor Chong pd.loopback = true;
18126128b8fSVictor Chong
18226128b8fSVictor Chong pl022_init(&pd);
18326128b8fSVictor Chong pd.chip.ops->configure(&pd.chip);
18426128b8fSVictor Chong pd.chip.ops->start(&pd.chip);
18526128b8fSVictor Chong
18626128b8fSVictor Chong for (j = 0; j < 20; j++) {
18726128b8fSVictor Chong DMSG("SPI test loop: %zu", j);
18826128b8fSVictor Chong res = pd.chip.ops->txrx8(&pd.chip, tx, rx, len);
18926128b8fSVictor Chong if (res) {
19026128b8fSVictor Chong EMSG("SPI transceive error %d", res);
19126128b8fSVictor Chong break;
19226128b8fSVictor Chong }
19326128b8fSVictor Chong
19426128b8fSVictor Chong for (i = 0; i < len; i++)
19526128b8fSVictor Chong DMSG("rx[%zu] = 0x%x", i, rx[i]);
19626128b8fSVictor Chong
19726128b8fSVictor Chong tee_time_busy_wait(20);
19826128b8fSVictor Chong }
19926128b8fSVictor Chong
20026128b8fSVictor Chong pd.chip.ops->end(&pd.chip);
20126128b8fSVictor Chong }
20226128b8fSVictor Chong
spi_test_with_builtin_cs_control(void)20326128b8fSVictor Chong static void spi_test_with_builtin_cs_control(void)
20426128b8fSVictor Chong {
20526128b8fSVictor Chong struct pl061_data pd061;
20626128b8fSVictor Chong struct pl022_data pd022;
207c2e4eb43SAnton Rybakov vaddr_t gpio6_base = core_mmu_get_va(GPIO6_BASE, MEM_AREA_IO_NSEC,
208c2e4eb43SAnton Rybakov PL061_REG_SIZE);
209c2e4eb43SAnton Rybakov vaddr_t spi_base = core_mmu_get_va(SPI_BASE, MEM_AREA_IO_NSEC,
210c2e4eb43SAnton Rybakov PL022_REG_SIZE);
21126128b8fSVictor Chong uint8_t tx[3] = {0x01, 0x80, 0x00};
21226128b8fSVictor Chong uint8_t rx[3] = {0};
21326128b8fSVictor Chong size_t i, j, len = 3;
21426128b8fSVictor Chong enum spi_result res;
21526128b8fSVictor Chong
21626128b8fSVictor Chong spi_set_cs_mux(PINMUX_GPIO);
21726128b8fSVictor Chong
218*bce2f88aSVincent Mailhol DMSG("gpio6_base: 0x%" PRIxVA, gpio6_base);
21926128b8fSVictor Chong DMSG("Configure GPIO");
22026128b8fSVictor Chong pl061_init(&pd061);
22126128b8fSVictor Chong pl061_register(gpio6_base, 6);
22226128b8fSVictor Chong DMSG("Enable software mode control for chip select");
22326128b8fSVictor Chong pl061_set_mode_control(GPIO6_2, PL061_MC_SW);
22426128b8fSVictor Chong
22526128b8fSVictor Chong pd022.cs_data.gpio_data.chip = &pd061.chip;
22626128b8fSVictor Chong pd022.cs_data.gpio_data.pin_num = GPIO6_2;
22726128b8fSVictor Chong pd022.cs_control = PL022_CS_CTRL_AUTO_GPIO;
22826128b8fSVictor Chong
229*bce2f88aSVincent Mailhol DMSG("spi_base: 0x%" PRIxVA, spi_base);
23026128b8fSVictor Chong DMSG("Configure SPI");
23126128b8fSVictor Chong pd022.base = spi_base;
23226128b8fSVictor Chong pd022.clk_hz = SPI_CLK_HZ;
23326128b8fSVictor Chong pd022.speed_hz = SPI_10_KHZ;
23426128b8fSVictor Chong pd022.mode = SPI_MODE0;
23526128b8fSVictor Chong pd022.data_size_bits = 8;
23626128b8fSVictor Chong pd022.loopback = true;
23726128b8fSVictor Chong
23826128b8fSVictor Chong pl022_init(&pd022);
23926128b8fSVictor Chong pd022.chip.ops->configure(&pd022.chip);
24026128b8fSVictor Chong pd022.chip.ops->start(&pd022.chip);
24126128b8fSVictor Chong
24226128b8fSVictor Chong for (j = 0; j < 20; j++) {
24326128b8fSVictor Chong DMSG("SPI test loop: %zu", j);
24426128b8fSVictor Chong res = pd022.chip.ops->txrx8(&pd022.chip, tx, rx, len);
24526128b8fSVictor Chong if (res) {
24626128b8fSVictor Chong EMSG("SPI transceive error %d", res);
24726128b8fSVictor Chong break;
24826128b8fSVictor Chong }
24926128b8fSVictor Chong
25026128b8fSVictor Chong for (i = 0; i < len; i++)
25126128b8fSVictor Chong DMSG("rx[%zu] = 0x%x", i, rx[i]);
25226128b8fSVictor Chong
25326128b8fSVictor Chong tee_time_busy_wait(20);
25426128b8fSVictor Chong }
25526128b8fSVictor Chong
25626128b8fSVictor Chong pd022.chip.ops->end(&pd022.chip);
25726128b8fSVictor Chong }
25871c1078aSVictor Chong
25971c1078aSVictor Chong /*
2603765523aSVictor Chong * spi_init() MUST be run before calling this function!
26171c1078aSVictor Chong *
26226128b8fSVictor Chong * spi_test runs some loopback tests, so the SPI module will just receive
26326128b8fSVictor Chong * what is transmitted, i.e. 0x01, 0x80, 0x00.
26471c1078aSVictor Chong *
26571c1078aSVictor Chong * In non-loopback mode, the transmitted value will elicit a readback of
26671c1078aSVictor Chong * the measured value from the ADC chip on the Linksprite 96Boards
26771c1078aSVictor Chong * Mezzanine card [1], which can be connected to either a sliding
26871c1078aSVictor Chong * rheostat [2] or photoresistor [3].
26971c1078aSVictor Chong *
27071c1078aSVictor Chong * [1] http://linksprite.com/wiki/index.php5?title=Linker_Mezzanine_card_for_96board
27171c1078aSVictor Chong * [2] http://learn.linksprite.com/96-board/sliding-rheostat
27271c1078aSVictor Chong * [3] http://learn.linksprite.com/96-board/photoresistor
27371c1078aSVictor Chong */
spi_test(void)27471c1078aSVictor Chong void spi_test(void)
27571c1078aSVictor Chong {
27626128b8fSVictor Chong spi_test_with_builtin_cs_control();
27726128b8fSVictor Chong spi_test_with_registered_cs_cb();
27826128b8fSVictor Chong spi_test_with_manual_cs_control();
27971c1078aSVictor Chong }
280