xref: /rk3399_ARM-atf/drivers/gpio/gpio_spi.c (revision f4c8834472dd55031e7b29a38a47ecb3ef53bb37)
1 /*
2  * Copyright (c) 2025-2026, 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 tpm_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 tpm_spi_priv * context)48 static void gpio_spi_start(struct tpm_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 tpm_spi_priv * context)55 static void gpio_spi_stop(struct tpm_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 tpm_spi_priv * context)61 static int gpio_spi_get_access(struct tpm_spi_priv *context)
62 {
63 	gpio_spi_sclk(0);
64 	return 0;
65 }
66 
gpio_spi_release_access(struct tpm_spi_priv * context)67 static void gpio_spi_release_access(struct tpm_spi_priv *context)
68 {
69 	/* No locking in this driver */
70 }
71 
xfer(unsigned int bytes,const void * out,void * in,int cpol,int cpha)72 static void xfer(unsigned int bytes, const void *out, void *in, int cpol, int cpha)
73 {
74 	uint32_t delay_us = gpio_spi_get_delay_us();
75 	for (unsigned int j = 0U; j < bytes; j++) {
76 		unsigned char in_byte  = 0U;
77 		unsigned char out_byte = (out != NULL) ? *(const uint8_t *)out++ : 0xFF;
78 
79 		for (int i = 7; i >= 0; i--) {
80 			if (cpha) {
81 				gpio_spi_sclk(!cpol);
82 			}
83 
84 			gpio_spi_mosi(!!(out_byte & (1 << i)));
85 
86 			udelay(delay_us);
87 			gpio_spi_sclk(cpha ? cpol : !cpol);
88 			udelay(delay_us);
89 
90 			in_byte |= gpio_spi_miso() << i;
91 
92 			if (!cpha) {
93 				gpio_spi_sclk(cpol);
94 			}
95 		}
96 
97 		if (in != NULL) {
98 			*(uint8_t *)in++ = in_byte;
99 		}
100 	}
101 }
102 
gpio_spi_xfer(struct tpm_spi_priv * context,unsigned int bytes,const uint8_t * out,uint8_t * in)103 static int gpio_spi_xfer(struct tpm_spi_priv *context, unsigned int bytes,
104 			 const uint8_t *out, uint8_t *in)
105 {
106 	if ((out == NULL) && (in == NULL)) {
107 		return -1;
108 	}
109 
110 	switch (config.spi_mode) {
111 	case 0:
112 		xfer(bytes, out, in, 0, 0);
113 		break;
114 	case 1:
115 		xfer(bytes, out, in, 0, 1);
116 		break;
117 	case 2:
118 		xfer(bytes, out, in, 1, 0);
119 		break;
120 	case 3:
121 		xfer(bytes, out, in, 1, 1);
122 		break;
123 	default:
124 		return -1;
125 	}
126 
127 	return 0;
128 }
129 
130 struct tpm_spi_ops gpio_spidev_ops = {
131 	.get_access = gpio_spi_get_access,
132 	.release_access = gpio_spi_release_access,
133 	.start = gpio_spi_start,
134 	.stop = gpio_spi_stop,
135 	.xfer = gpio_spi_xfer,
136 };
137 
gpio_spi_init(const struct gpio_spi_config * gpio_spi_data)138 struct tpm_spi_plat *gpio_spi_init(const struct gpio_spi_config *gpio_spi_data)
139 {
140 	gpio_spidev.priv = NULL; // No context, only one device supported
141 	config = *gpio_spi_data;
142 	gpio_spidev.ops = &gpio_spidev_ops;
143 
144 	gpio_set_value(gpio_spi_data->cs_gpio, 1);
145 	gpio_set_direction(gpio_spi_data->cs_gpio, GPIO_DIR_OUT);
146 
147 	gpio_set_value(gpio_spi_data->sclk_gpio, 0);
148 	gpio_set_direction(gpio_spi_data->sclk_gpio, GPIO_DIR_OUT);
149 
150 	gpio_set_value(gpio_spi_data->mosi_gpio, 1);
151 	gpio_set_direction(gpio_spi_data->mosi_gpio, GPIO_DIR_OUT);
152 
153 	gpio_set_direction(gpio_spi_data->miso_gpio, GPIO_DIR_IN);
154 
155 	return &gpio_spidev;
156 }
157