aboutsummaryrefslogtreecommitdiff
path: root/target/linux/bcm27xx/patches-6.1/950-0900-drivers-spi-Fix-spi-gpio-to-correctly-implement-sck-.patch
blob: ff8471411f5786f95ff8f19641d1b6ff139b8916 (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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
From 380c336af070edf85826abbb0057bf92a03ec466 Mon Sep 17 00:00:00 2001
From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
Date: Wed, 1 Mar 2023 17:57:11 +0000
Subject: [PATCH] drivers: spi: Fix spi-gpio to correctly implement
 sck-idle-input

Formerly, if configured using DT, CS GPIOs were driven from spi.c
and it was possible for CS to be asserted (low) *before* starting
to drive SCK. CS GPIOs have been brought under control of this
driver in both ACPI and DT cases, with a fixup for GPIO polarity.

Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
---
 drivers/spi/spi-gpio.c | 74 +++++++++++++++++++++++++++++-------------
 1 file changed, 51 insertions(+), 23 deletions(-)

--- a/drivers/spi/spi-gpio.c
+++ b/drivers/spi/spi-gpio.c
@@ -37,6 +37,7 @@ struct spi_gpio {
 	struct gpio_desc		*mosi;
 	bool				sck_idle_input;
 	struct gpio_desc		**cs_gpios;
+	bool                            cs_dont_invert;
 };
 
 /*----------------------------------------------------------------------*/
@@ -233,12 +234,18 @@ static void spi_gpio_chipselect(struct s
 			gpiod_set_value_cansleep(spi_gpio->sck, spi->mode & SPI_CPOL);
 	}
 
-	/* Drive chip select line, if we have one */
+	/*
+	 * Drive chip select line, if we have one.
+	 * SPI chip selects are normally active-low, but when
+	 * cs_dont_invert is set, we assume their polarity is
+	 * controlled by the GPIO, and write '1' to assert.
+	 */
 	if (spi_gpio->cs_gpios) {
 		struct gpio_desc *cs = spi_gpio->cs_gpios[spi->chip_select];
+		int val = ((spi->mode & SPI_CS_HIGH) || spi_gpio->cs_dont_invert) ?
+			is_active : !is_active;
 
-		/* SPI chip selects are normally active-low */
-		gpiod_set_value_cansleep(cs, (spi->mode & SPI_CS_HIGH) ? is_active : !is_active);
+		gpiod_set_value_cansleep(cs, val);
 	}
 
 	if (spi_gpio->sck_idle_input && !is_active)
@@ -254,12 +261,14 @@ static int spi_gpio_setup(struct spi_dev
 	/*
 	 * The CS GPIOs have already been
 	 * initialized from the descriptor lookup.
+	 * Here we set them to the non-asserted state.
 	 */
 	if (spi_gpio->cs_gpios) {
 		cs = spi_gpio->cs_gpios[spi->chip_select];
 		if (!spi->controller_state && cs)
 			status = gpiod_direction_output(cs,
-						  !(spi->mode & SPI_CS_HIGH));
+							!((spi->mode & SPI_CS_HIGH) ||
+							   spi_gpio->cs_dont_invert));
 	}
 
 	if (!status)
@@ -336,6 +345,38 @@ static int spi_gpio_request(struct devic
 	return PTR_ERR_OR_ZERO(spi_gpio->sck);
 }
 
+/*
+ * In order to implement "sck-idle-input" (which requires SCK
+ * direction and CS level to be switched in a particular order),
+ * we need to control GPIO chip selects from within this driver.
+ */
+
+static int spi_gpio_probe_get_cs_gpios(struct device *dev,
+				       struct spi_master *master,
+				       bool gpio_defines_polarity)
+{
+	int i;
+	struct spi_gpio *spi_gpio = spi_master_get_devdata(master);
+
+	spi_gpio->cs_dont_invert = gpio_defines_polarity;
+	spi_gpio->cs_gpios = devm_kcalloc(dev, master->num_chipselect,
+					  sizeof(*spi_gpio->cs_gpios),
+					  GFP_KERNEL);
+	if (!spi_gpio->cs_gpios)
+		return -ENOMEM;
+
+	for (i = 0; i < master->num_chipselect; i++) {
+		spi_gpio->cs_gpios[i] =
+			devm_gpiod_get_index(dev, "cs", i,
+					     gpio_defines_polarity ?
+						GPIOD_OUT_LOW : GPIOD_OUT_HIGH);
+		if (IS_ERR(spi_gpio->cs_gpios[i]))
+			return PTR_ERR(spi_gpio->cs_gpios[i]);
+	}
+
+	return 0;
+}
+
 #ifdef CONFIG_OF
 static const struct of_device_id spi_gpio_dt_ids[] = {
 	{ .compatible = "spi-gpio" },
@@ -346,10 +387,12 @@ MODULE_DEVICE_TABLE(of, spi_gpio_dt_ids)
 static int spi_gpio_probe_dt(struct platform_device *pdev,
 			     struct spi_master *master)
 {
-	master->dev.of_node = pdev->dev.of_node;
-	master->use_gpio_descriptors = true;
+	struct device *dev = &pdev->dev;
 
-	return 0;
+	master->dev.of_node = dev->of_node;
+	master->num_chipselect = gpiod_count(dev, "cs");
+
+	return spi_gpio_probe_get_cs_gpios(dev, master, true);
 }
 #else
 static inline int spi_gpio_probe_dt(struct platform_device *pdev,
@@ -364,8 +407,6 @@ static int spi_gpio_probe_pdata(struct p
 {
 	struct device *dev = &pdev->dev;
 	struct spi_gpio_platform_data *pdata = dev_get_platdata(dev);
-	struct spi_gpio *spi_gpio = spi_master_get_devdata(master);
-	int i;
 
 #ifdef GENERIC_BITBANG
 	if (!pdata || !pdata->num_chipselect)
@@ -377,20 +418,7 @@ static int spi_gpio_probe_pdata(struct p
 	 */
 	master->num_chipselect = pdata->num_chipselect ?: 1;
 
-	spi_gpio->cs_gpios = devm_kcalloc(dev, master->num_chipselect,
-					  sizeof(*spi_gpio->cs_gpios),
-					  GFP_KERNEL);
-	if (!spi_gpio->cs_gpios)
-		return -ENOMEM;
-
-	for (i = 0; i < master->num_chipselect; i++) {
-		spi_gpio->cs_gpios[i] = devm_gpiod_get_index(dev, "cs", i,
-							     GPIOD_OUT_HIGH);
-		if (IS_ERR(spi_gpio->cs_gpios[i]))
-			return PTR_ERR(spi_gpio->cs_gpios[i]);
-	}
-
-	return 0;
+	return spi_gpio_probe_get_cs_gpios(dev, master, false);
 }
 
 static int spi_gpio_probe(struct platform_device *pdev)