1 /*
2 * Copyright (c) 2025, ARM Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <stdint.h>
8 #include <stdlib.h>
9
10 #include <common/debug.h>
11 #include <drivers/delay_timer.h>
12 #include <drivers/gpio.h>
13 #include <drivers/gpio_spi.h>
14 #include <platform_def.h>
15
16 static struct gpio_spi_config config;
17
18 static struct spi_plat gpio_spidev;
19
gpio_spi_get_delay_us(void)20 static uint32_t gpio_spi_get_delay_us(void)
21 {
22 /* Delay used twice per clock cycle,
23 * high and low.
24 */
25 return 500000 / config.spi_max_clock;
26 }
27
gpio_spi_miso(void)28 static int gpio_spi_miso(void)
29 {
30 return gpio_get_value(config.miso_gpio);
31 }
32
gpio_spi_sclk(int bit)33 static void gpio_spi_sclk(int bit)
34 {
35 gpio_set_value(config.sclk_gpio, bit);
36 }
37
gpio_spi_mosi(int bit)38 static void gpio_spi_mosi(int bit)
39 {
40 gpio_set_value(config.mosi_gpio, bit);
41 }
42
gpio_spi_cs(int bit)43 static void gpio_spi_cs(int bit)
44 {
45 gpio_set_value(config.cs_gpio, bit);
46 }
47
gpio_spi_start(struct spi_priv * context)48 static void gpio_spi_start(struct spi_priv *context)
49 {
50 gpio_spi_cs(1);
51 gpio_spi_sclk(0);
52 gpio_spi_cs(0);
53 }
54
gpio_spi_stop(struct spi_priv * context)55 static void gpio_spi_stop(struct spi_priv *context)
56 {
57 gpio_spi_cs(1);
58 }
59
60 /* set sclk to a known state (0) before performing any further action */
gpio_spi_get_access(struct spi_priv * context)61 static int gpio_spi_get_access(struct spi_priv *context)
62 {
63 gpio_spi_sclk(0);
64 return 0;
65 }
66
gpio_spi_release_access(struct spi_priv * context)67 static void gpio_spi_release_access(struct spi_priv *context)
68 {
69 /* No locking in this driver */
70 }
xfer(unsigned int bytes,const void * out,void * in,int cpol,int cpha)71 static void xfer(unsigned int bytes, const void *out, void *in, int cpol, int cpha)
72 {
73 uint32_t delay_us = gpio_spi_get_delay_us();
74 for (unsigned int j = 0U; j < bytes; j++) {
75 unsigned char in_byte = 0U;
76 unsigned char out_byte = (out != NULL) ? *(const uint8_t *)out++ : 0xFF;
77
78 for (int i = 7; i >= 0; i--) {
79 if (cpha) {
80 gpio_spi_sclk(!cpol);
81 }
82
83 gpio_spi_mosi(!!(out_byte & (1 << i)));
84
85 udelay(delay_us);
86 gpio_spi_sclk(cpha ? cpol : !cpol);
87 udelay(delay_us);
88
89 in_byte |= gpio_spi_miso() << i;
90
91 if (!cpha) {
92 gpio_spi_sclk(cpol);
93 }
94 }
95
96 if (in != NULL) {
97 *(uint8_t *)in++ = in_byte;
98 }
99 }
100 }
101
gpio_spi_xfer(struct spi_priv * context,unsigned int bytes,const void * out,void * in)102 static int gpio_spi_xfer(struct spi_priv *context, unsigned int bytes,
103 const void *out, void *in)
104 {
105 if ((out == NULL) && (in == NULL)) {
106 return -1;
107 }
108
109 switch (config.spi_mode) {
110 case 0:
111 xfer(bytes, out, in, 0, 0);
112 break;
113 case 1:
114 xfer(bytes, out, in, 0, 1);
115 break;
116 case 2:
117 xfer(bytes, out, in, 1, 0);
118 break;
119 case 3:
120 xfer(bytes, out, in, 1, 1);
121 break;
122 default:
123 return -1;
124 }
125
126 return 0;
127 }
128
129 struct spi_ops gpio_spidev_ops = {
130 .get_access = gpio_spi_get_access,
131 .release_access = gpio_spi_release_access,
132 .start = gpio_spi_start,
133 .stop = gpio_spi_stop,
134 .xfer = gpio_spi_xfer,
135 };
136
gpio_spi_init(const struct gpio_spi_config * gpio_spi_data)137 struct spi_plat *gpio_spi_init(const struct gpio_spi_config *gpio_spi_data)
138 {
139 gpio_spidev.priv = NULL; // No context, only one device supported
140 config = *gpio_spi_data;
141 gpio_spidev.ops = &gpio_spidev_ops;
142
143 gpio_set_value(gpio_spi_data->cs_gpio, 1);
144 gpio_set_direction(gpio_spi_data->cs_gpio, GPIO_DIR_OUT);
145
146 gpio_set_value(gpio_spi_data->sclk_gpio, 0);
147 gpio_set_direction(gpio_spi_data->sclk_gpio, GPIO_DIR_OUT);
148
149 gpio_set_value(gpio_spi_data->mosi_gpio, 1);
150 gpio_set_direction(gpio_spi_data->mosi_gpio, GPIO_DIR_OUT);
151
152 gpio_set_direction(gpio_spi_data->miso_gpio, GPIO_DIR_IN);
153
154 return &gpio_spidev;
155 }
156