diff options
author | Akhilesh Reddy Khumbum <akhumbum@nvidia.com> | 2016-05-02 13:15:30 -0400 |
---|---|---|
committer | Laxman Dewangan <ldewangan@nvidia.com> | 2016-05-11 12:10:44 -0400 |
commit | 292a80589a2377f1f8d41968e1b472ed929c11cf (patch) | |
tree | e2973baebcf38b94f6e05eb1ddd2b979df4bdd5d | |
parent | efb3dc52908669c654cc64afef823be72867444b (diff) |
spi: tegra: Add SPI proxy driver for AON SPI
SPI2 is accessible by both AON/SPE and CCPLEX. If both CPU's access the SPI
bus at the same time, there will be a conflict. In order to ensure there are
no conflicts, we select SPE as the owner of the SPI2 controller and all the
other CPU's that need to touch the SPI2 controller will send an IPC message
to the SPE and SPE will perform the SPI transaction on the requesting CPU's
behalf. This driver registers as the standard kernel SPI controller driver
for AON SPI controller i.e. SPI2. This driver sends the IPC messages comprising
of the SPI transfers to SPE instead of directly programming the SPI registers.
Bug 1701661
Change-Id: I149d2bdd938c3f3aa03a754c2cc0678758fbba9e
Signed-off-by: Akhilesh Reddy Khumbum <akhumbum@nvidia.com>
Reviewed-on: http://git-master/r/1117289
Reviewed-by: Automatic_Commit_Validation_User
GVS: Gerrit_Virtual_Submit
Reviewed-by: Krishna Yarlagadda <kyarlagadda@nvidia.com>
Tested-by: Krishna Yarlagadda <kyarlagadda@nvidia.com>
Reviewed-by: Mustafa Bilgen <mbilgen@nvidia.com>
Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
-rw-r--r-- | drivers/spi/Kconfig | 6 | ||||
-rw-r--r-- | drivers/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spi/aon-spi-messages.h | 175 | ||||
-rw-r--r-- | drivers/spi/spi-tegra-aon.c | 490 |
4 files changed, 672 insertions, 0 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index e967f041c..5dc8ca807 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig | |||
@@ -6,3 +6,9 @@ config QSPI_TEGRA186 | |||
6 | QSPI driver for Nvidia Tegra210 QSPI Controller interface. This controller | 6 | QSPI driver for Nvidia Tegra210 QSPI Controller interface. This controller |
7 | is different from the spi controller and first time introduces on Tegra210 soc. | 7 | is different from the spi controller and first time introduces on Tegra210 soc. |
8 | 8 | ||
9 | config SPI_TEGRA186_AON | ||
10 | bool "Tegra18x AON SPI proxy driver" | ||
11 | depends on ARCH_TEGRA_18x_SOC | ||
12 | default y | ||
13 | select TEGRA_IVC | ||
14 | select TEGRA_HSP | ||
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 99aa54025..35efac7f4 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile | |||
@@ -4,4 +4,5 @@ ccflags-y += -Werror | |||
4 | ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG | 4 | ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG |
5 | 5 | ||
6 | obj-$(CONFIG_QSPI_TEGRA186) += spi-tegra186-qspi.o | 6 | obj-$(CONFIG_QSPI_TEGRA186) += spi-tegra186-qspi.o |
7 | obj-$(CONFIG_SPI_TEGRA186_AON) += spi-tegra-aon.o | ||
7 | 8 | ||
diff --git a/drivers/spi/aon-spi-messages.h b/drivers/spi/aon-spi-messages.h new file mode 100644 index 000000000..c7e77db7d --- /dev/null +++ b/drivers/spi/aon-spi-messages.h | |||
@@ -0,0 +1,175 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. | ||
3 | * | ||
4 | * NVIDIA CORPORATION and its licensors retain all intellectual property | ||
5 | * and proprietary rights in and to this software, related documentation | ||
6 | * and any modifications thereto. Any use, reproduction, disclosure or | ||
7 | * distribution of this software and related documentation without an express | ||
8 | * license agreement from NVIDIA CORPORATION is strictly prohibited. | ||
9 | */ | ||
10 | |||
11 | #ifndef _AON_SPI_MESSAGES_H_ | ||
12 | #define _AON_SPI_MESSAGES_H_ | ||
13 | |||
14 | #include <linux/types.h> | ||
15 | #define TEGRA_IVC_ALIGN 64 | ||
16 | /* 24640 is emperically derived by observing the max len transactions | ||
17 | * touch. | ||
18 | */ | ||
19 | #define AON_SPI_MAX_DATA_SIZE 24640 | ||
20 | |||
21 | /* All the enums and the fields inside the structs described in this header | ||
22 | * file supports only uX type, where X can be 8,16,32. For inter CPU commun- | ||
23 | * ication, it is more stable to use this type. | ||
24 | */ | ||
25 | |||
26 | /* This enum represents the types of spi requests assocaited | ||
27 | * with AON. | ||
28 | */ | ||
29 | enum aon_spi_request_type { | ||
30 | AON_SPI_REQUEST_TYPE_INIT = 1, | ||
31 | AON_SPI_REQUEST_TYPE_SETUP = 2, | ||
32 | AON_SPI_REQUEST_TYPE_XFER = 3, | ||
33 | AON_SPI_REQUEST_TYPE_SUSPEND = 4, | ||
34 | AON_SPI_REQUEST_TYPE_RESUME = 5, | ||
35 | }; | ||
36 | |||
37 | /* | ||
38 | * This enum indicates the status of the request from CCPLEX. | ||
39 | */ | ||
40 | enum aon_spi_status { | ||
41 | AON_SPI_STATUS_OK = 0, | ||
42 | AON_SPI_STATUS_ERROR = 1, | ||
43 | }; | ||
44 | |||
45 | /* This enum represents whether the current SPI transaction | ||
46 | * is a read or write. Also indicates whether the current | ||
47 | * message is the first or last in the context of a big xfer | ||
48 | * split into multiple xfers. | ||
49 | */ | ||
50 | enum aon_spi_xfer_flag { | ||
51 | AON_SPI_XFER_FLAG_WRITE = BIT(1), | ||
52 | AON_SPI_XFER_FLAG_READ = BIT(2), | ||
53 | AON_SPI_XFER_FIRST_MSG = BIT(3), | ||
54 | AON_SPI_XFER_LAST_MSG = BIT(4), | ||
55 | AON_SPI_XFER_HANDLE_CACHE = BIT(5), | ||
56 | }; | ||
57 | |||
58 | /* This struct is used to setup the SPI client setup for AON | ||
59 | * SPI controller. | ||
60 | * Fields: | ||
61 | * cs_setup_clk_count: CS pin setup clock count | ||
62 | * cs_hold_clk_count: CS pin hold clock count | ||
63 | * cs_inactive_cycles: CS pin inactive clock count | ||
64 | * set_rx_tap_delay: Specify if the SPI device need to set | ||
65 | RX tap delay | ||
66 | * spi_max_clk_rate: Specify the default clock rate of SPI client | ||
67 | * spi_no_dma: Flag to indicate pio or dma mode | ||
68 | */ | ||
69 | struct aon_spi_setup_request { | ||
70 | u32 cs_setup_clk_count; | ||
71 | u32 cs_hold_clk_count; | ||
72 | u32 cs_inactive_cycles; | ||
73 | u32 spi_max_clk_rate; | ||
74 | u8 chip_select; | ||
75 | u8 set_rx_tap_delay; | ||
76 | bool spi_no_dma; | ||
77 | }; | ||
78 | |||
79 | /* This struct indicates the parameters for SPI xfer from CCPLEX to SPE. | ||
80 | * Fields: | ||
81 | * spi_clk_rate: Specify clock rate for current transfer | ||
82 | * flags: Indicate first/last message | ||
83 | * length: Current transfer length | ||
84 | * tx_buf_offset: Offset in the data field of the aon_spi_xfer_request | ||
85 | * struct for tx_buf contents. The buffer memory need to | ||
86 | * be aligned for DMA transfer | ||
87 | * rx_buf_offset: Offset in the data field of the aon_spi_xfer_request | ||
88 | * struct for rx_buf. The buffer memory need to be | ||
89 | * aligned for DMA transfer | ||
90 | * chip_select: Chip select of the slave device | ||
91 | * bits_per_word: Select bits_per_word | ||
92 | * tx_nbits: Number of bits used for writing | ||
93 | * rx_nbits: Number of bits used for reading | ||
94 | * | ||
95 | * When SPI can transfer in 1x,2x or 4x. It can get this tranfer information | ||
96 | * from device through tx_nbits and rx_nbits. In Bi-direction, these | ||
97 | * two should both be set. User can set transfer mode with SPI_NBITS_SINGLE(1x) | ||
98 | * SPI_NBITS_DUAL(2x) and SPI_NBITS_QUAD(4x) to support these three transfer. | ||
99 | */ | ||
100 | struct aon_spi_xfer_params { | ||
101 | u32 spi_clk_rate; | ||
102 | /* enum aon_spi_xfer_flag */ | ||
103 | u16 flags; | ||
104 | u16 length; | ||
105 | u16 tx_buf_offset; | ||
106 | u16 rx_buf_offset; | ||
107 | u16 mode; | ||
108 | u8 chip_select; | ||
109 | u8 bits_per_word; | ||
110 | u8 tx_nbits; | ||
111 | u8 rx_nbits; | ||
112 | }; | ||
113 | |||
114 | /* This struct indicates the contents of the xfer request from CCPLEX to SPE | ||
115 | * for the AON SPI controller. | ||
116 | * | ||
117 | * Fields: | ||
118 | * xfers; Paramters for the current transfers. | ||
119 | * data: Buffer that holds the data for the current SPI ransaction. | ||
120 | * The size is aligned to the size of the cache line. | ||
121 | */ | ||
122 | struct aon_spi_xfer_request { | ||
123 | union { | ||
124 | struct aon_spi_xfer_params xfers; | ||
125 | u8 align_t[TEGRA_IVC_ALIGN - sizeof(u32)]; | ||
126 | }; | ||
127 | u8 data[AON_SPI_MAX_DATA_SIZE]; | ||
128 | }; | ||
129 | |||
130 | /* This structure indicates the contents of the response from the remote CPU | ||
131 | * i.e SPE for the previously requested transaction via CCPLEX proxy driver. | ||
132 | * | ||
133 | * Fields: | ||
134 | * data: This just matches the data field in the xfer request struct. | ||
135 | * All the response is stored in this buf and can be accessed by | ||
136 | * the offset fields known in the xfer params. | ||
137 | */ | ||
138 | struct aon_spi_xfer_response { | ||
139 | union { | ||
140 | u8 align_t[TEGRA_IVC_ALIGN - sizeof(u32)]; | ||
141 | }; | ||
142 | u8 data[AON_SPI_MAX_DATA_SIZE]; | ||
143 | }; | ||
144 | |||
145 | /* This structure indicates the current SPI request from CCPLEX to SPE for the | ||
146 | * AON SPI controller. | ||
147 | * | ||
148 | * Fields: | ||
149 | * req_type: Indicates the type of request. Supports init, setup and xfer. | ||
150 | * data: Union of structs of all the request types. | ||
151 | */ | ||
152 | struct aon_spi_request { | ||
153 | /* enum aon_spi_request_type */ | ||
154 | u32 req_type; | ||
155 | union { | ||
156 | struct aon_spi_setup_request setup; | ||
157 | struct aon_spi_xfer_request xfer; | ||
158 | } data; | ||
159 | }; | ||
160 | |||
161 | /* This structure indicates the response for the SPI request from SPE to CCPLEX | ||
162 | * for the AON SPI controller. | ||
163 | * | ||
164 | * Fields: | ||
165 | * status: Response in regard to the request i.e success/failure. | ||
166 | * data: Union of structs of all the response types. | ||
167 | */ | ||
168 | struct aon_spi_response { | ||
169 | u32 status; | ||
170 | union { | ||
171 | struct aon_spi_xfer_response xfer; | ||
172 | } data; | ||
173 | }; | ||
174 | |||
175 | #endif | ||
diff --git a/drivers/spi/spi-tegra-aon.c b/drivers/spi/spi-tegra-aon.c new file mode 100644 index 000000000..5a7c253df --- /dev/null +++ b/drivers/spi/spi-tegra-aon.c | |||
@@ -0,0 +1,490 @@ | |||
1 | /* | ||
2 | * SPI proxy driver for NVIDIA's Tegra186 AON SPI Controller. | ||
3 | * | ||
4 | * Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | */ | ||
18 | |||
19 | #include <linux/module.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/of_device.h> | ||
22 | #include <linux/spi/spi.h> | ||
23 | #include <linux/spi/spi-tegra.h> | ||
24 | #include <linux/tegra-aon.h> | ||
25 | #include <linux/mailbox_client.h> | ||
26 | |||
27 | #include "aon-spi-messages.h" | ||
28 | |||
29 | #define SPI_DMA_TIMEOUT (msecs_to_jiffies(5000)) | ||
30 | #define MAX_HOLD_CYCLES 16 | ||
31 | #define SPI_DEFAULT_SPEED 25000000 | ||
32 | |||
33 | #define MAX_CHIP_SELECT 4 | ||
34 | #define SPI_DEF_CHIPSELECT 0 | ||
35 | |||
36 | #define SPI_DEFAULT_RX_TAP_DELAY 10 | ||
37 | |||
38 | /* block period in ms */ | ||
39 | #define TX_BLOCK_PERIOD 200 | ||
40 | |||
41 | struct tegra_aon_spi_chipdata { | ||
42 | bool set_rx_tap_delay; | ||
43 | }; | ||
44 | |||
45 | struct tegra_spi_data { | ||
46 | struct device *dev; | ||
47 | struct spi_master *master; | ||
48 | u32 spi_max_frequency; | ||
49 | u8 def_chip_select; | ||
50 | const struct tegra_aon_spi_chipdata *chip_data; | ||
51 | struct completion *xfer_completion; | ||
52 | struct mbox_client cl; | ||
53 | struct mbox_chan *mbox; | ||
54 | struct tegra_spi_device_controller_data cdata[MAX_CHIP_SELECT]; | ||
55 | struct aon_spi_response *spi_resp; | ||
56 | struct aon_spi_request *spi_req; | ||
57 | }; | ||
58 | |||
59 | |||
60 | static struct tegra_spi_device_controller_data | ||
61 | *tegra_spi_get_cdata_dt(struct spi_device *spi, | ||
62 | struct tegra_spi_data *tspi) | ||
63 | { | ||
64 | struct tegra_spi_device_controller_data *cdata; | ||
65 | struct device_node *slave_np, *data_np; | ||
66 | |||
67 | slave_np = spi->dev.of_node; | ||
68 | if (!slave_np) { | ||
69 | dev_dbg(&spi->dev, "device node not found\n"); | ||
70 | return NULL; | ||
71 | } | ||
72 | |||
73 | data_np = of_get_child_by_name(slave_np, "controller-data"); | ||
74 | if (!data_np) { | ||
75 | dev_dbg(&spi->dev, "child node 'controller-data' not found\n"); | ||
76 | return NULL; | ||
77 | } | ||
78 | |||
79 | cdata = &tspi->cdata[spi->chip_select]; | ||
80 | memset(cdata, 0, sizeof(*cdata)); | ||
81 | |||
82 | of_property_read_u32(data_np, "nvidia,cs-setup-clk-count", | ||
83 | &cdata->cs_setup_clk_count); | ||
84 | of_property_read_u32(data_np, "nvidia,cs-hold-clk-count", | ||
85 | &cdata->cs_hold_clk_count); | ||
86 | of_property_read_u32(data_np, "nvidia,cs-inactive-cycles", | ||
87 | &cdata->cs_inactive_cycles); | ||
88 | of_node_put(data_np); | ||
89 | |||
90 | return cdata; | ||
91 | } | ||
92 | |||
93 | static void tegra_aon_spi_mbox_rcv_msg(struct mbox_client *cl, void *mssg) | ||
94 | { | ||
95 | struct tegra_aon_mbox_msg *msg = mssg; | ||
96 | struct spi_master *master = dev_get_drvdata(cl->dev); | ||
97 | struct tegra_spi_data *tspi = spi_master_get_devdata(master); | ||
98 | |||
99 | memcpy(tspi->spi_resp, msg->data, sizeof(*tspi->spi_resp)); | ||
100 | complete(tspi->xfer_completion); | ||
101 | } | ||
102 | |||
103 | static int ivc_aon_spi_send_req(struct tegra_spi_data *tspi, int len) | ||
104 | { | ||
105 | int status; | ||
106 | struct tegra_aon_mbox_msg msg; | ||
107 | |||
108 | msg.length = len; | ||
109 | msg.data = (void *)tspi->spi_req; | ||
110 | status = mbox_send_message(tspi->mbox, (void *)&msg); | ||
111 | if (status < 0) { | ||
112 | dev_err(tspi->dev, "mbox_send_message() failed with %d\n", | ||
113 | status); | ||
114 | } else { | ||
115 | status = wait_for_completion_timeout(tspi->xfer_completion, | ||
116 | SPI_DMA_TIMEOUT); | ||
117 | if (status == 0) { | ||
118 | dev_err(tspi->dev, "Timeout waiting for ipc response\n"); | ||
119 | return -ETIMEDOUT; | ||
120 | } else { | ||
121 | status = tspi->spi_resp->status; | ||
122 | } | ||
123 | } | ||
124 | |||
125 | return status; | ||
126 | } | ||
127 | |||
128 | static int do_ivc_aon_spi_init(struct tegra_spi_data *tspi) | ||
129 | { | ||
130 | int len, status; | ||
131 | |||
132 | tspi->spi_req->req_type = AON_SPI_REQUEST_TYPE_INIT; | ||
133 | len = sizeof(tspi->spi_req->req_type); | ||
134 | status = ivc_aon_spi_send_req(tspi, len); | ||
135 | if (status) | ||
136 | return -EIO; | ||
137 | |||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | static int do_ivc_aon_spi_setup(struct spi_device *spi, | ||
142 | struct tegra_spi_data *tspi) | ||
143 | { | ||
144 | int status; | ||
145 | struct tegra_spi_device_controller_data *cdata = spi->controller_data; | ||
146 | struct aon_spi_request *req = tspi->spi_req; | ||
147 | int len; | ||
148 | |||
149 | dev_dbg(tspi->dev, "%s -> invocation\n", __func__); | ||
150 | req->req_type = AON_SPI_REQUEST_TYPE_SETUP; | ||
151 | if (cdata) { | ||
152 | req->data.setup.cs_setup_clk_count = cdata->cs_setup_clk_count; | ||
153 | req->data.setup.cs_hold_clk_count = cdata->cs_hold_clk_count; | ||
154 | req->data.setup.cs_inactive_cycles = cdata->cs_inactive_cycles; | ||
155 | } | ||
156 | req->data.setup.set_rx_tap_delay = tspi->chip_data->set_rx_tap_delay; | ||
157 | req->data.setup.chip_select = spi->chip_select; | ||
158 | spi->max_speed_hz = spi->max_speed_hz ? : tspi->spi_max_frequency; | ||
159 | req->data.setup.spi_max_clk_rate = spi->max_speed_hz; | ||
160 | /* TODO: No-Dma support to be added for core driver */ | ||
161 | req->data.setup.spi_no_dma = false; | ||
162 | len = (sizeof(req->req_type) + sizeof(struct aon_spi_setup_request)); | ||
163 | status = ivc_aon_spi_send_req(tspi, len); | ||
164 | if (status) | ||
165 | return -EIO; | ||
166 | |||
167 | return 0; | ||
168 | } | ||
169 | |||
170 | #if defined(ENABLE_DUMPS) | ||
171 | static void tegra_spi_dump_buf(u8 *buffer, int len, const char *name) | ||
172 | { | ||
173 | int j; | ||
174 | |||
175 | pr_dbg("%s:::", name); | ||
176 | for (j = 0; j < len; j++) | ||
177 | pr_dbg("%x ", *(buffer + j)); | ||
178 | pr_dbg("\n"); | ||
179 | } | ||
180 | #else | ||
181 | static void tegra_spi_dump_buf(u8 *buffer, int len, const char *name) | ||
182 | { | ||
183 | } | ||
184 | #endif | ||
185 | |||
186 | static int do_aon_ivc_spi_xfer(struct spi_device *spi, | ||
187 | struct spi_transfer *spi_xfer, | ||
188 | struct tegra_spi_data *tspi, | ||
189 | enum aon_spi_xfer_flag xfer_type) | ||
190 | { | ||
191 | int length = 0, status = 0; | ||
192 | struct aon_spi_request *req = tspi->spi_req; | ||
193 | struct aon_spi_response *resp = tspi->spi_resp; | ||
194 | |||
195 | req->req_type = AON_SPI_REQUEST_TYPE_XFER; | ||
196 | |||
197 | req->data.xfer.xfers.flags = xfer_type; | ||
198 | if (spi_xfer->tx_buf) | ||
199 | req->data.xfer.xfers.flags |= AON_SPI_XFER_FLAG_WRITE; | ||
200 | if (spi_xfer->rx_buf) | ||
201 | req->data.xfer.xfers.flags |= AON_SPI_XFER_FLAG_READ; | ||
202 | |||
203 | req->data.xfer.xfers.length = spi_xfer->len; | ||
204 | req->data.xfer.xfers.rx_buf_offset = 0; | ||
205 | req->data.xfer.xfers.tx_buf_offset = 0; | ||
206 | req->data.xfer.xfers.chip_select = spi->chip_select; | ||
207 | req->data.xfer.xfers.tx_nbits = spi_xfer->tx_nbits; | ||
208 | req->data.xfer.xfers.rx_nbits = spi_xfer->rx_nbits; | ||
209 | req->data.xfer.xfers.bits_per_word = spi_xfer->bits_per_word; | ||
210 | req->data.xfer.xfers.mode = spi->mode; | ||
211 | req->data.xfer.xfers.spi_clk_rate = spi_xfer->speed_hz; | ||
212 | /* per-word bits-on-wire */ | ||
213 | |||
214 | if (spi_xfer->tx_buf) { | ||
215 | memcpy(req->data.xfer.data, spi_xfer->tx_buf, spi_xfer->len); | ||
216 | tegra_spi_dump_buf(req->data.xfer.data, spi_xfer->len, | ||
217 | "reqdata"); | ||
218 | } | ||
219 | /* alignmemt + data */ | ||
220 | length = (TEGRA_IVC_ALIGN + spi_xfer->len + 1); | ||
221 | if (spi_xfer->len > AON_SPI_MAX_DATA_SIZE) { | ||
222 | dev_err(tspi->dev, "length %d greater than max length\n", | ||
223 | spi_xfer->len); | ||
224 | return -E2BIG; | ||
225 | } | ||
226 | status = ivc_aon_spi_send_req(tspi, length); | ||
227 | if (status) { | ||
228 | dev_err(tspi->dev, "Error in transfer\n"); | ||
229 | return -EIO; | ||
230 | } | ||
231 | if (spi_xfer->rx_buf) { | ||
232 | memcpy(spi_xfer->rx_buf, resp->data.xfer.data, spi_xfer->len); | ||
233 | tegra_spi_dump_buf(spi_xfer->rx_buf, spi_xfer->len, "rxdata"); | ||
234 | } | ||
235 | |||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | static int tegra_spi_transfer_one_message(struct spi_master *master, | ||
240 | struct spi_message *msg) | ||
241 | { | ||
242 | struct tegra_spi_data *tspi = spi_master_get_devdata(master); | ||
243 | struct spi_transfer *xfer; | ||
244 | int ret = -1; | ||
245 | int count = 0; | ||
246 | u16 flags = 0; | ||
247 | |||
248 | msg->status = 0; | ||
249 | msg->actual_length = 0; | ||
250 | |||
251 | list_for_each_entry(xfer, &msg->transfers, transfer_list) { | ||
252 | flags |= AON_SPI_XFER_HANDLE_CACHE; | ||
253 | if (count == 0) | ||
254 | flags |= AON_SPI_XFER_FIRST_MSG; | ||
255 | if (list_is_last(&xfer->transfer_list, &msg->transfers)) | ||
256 | flags |= AON_SPI_XFER_LAST_MSG; | ||
257 | |||
258 | ret = do_aon_ivc_spi_xfer(msg->spi, xfer, tspi, flags); | ||
259 | if (!ret) | ||
260 | msg->actual_length += xfer->len; | ||
261 | count++; | ||
262 | } | ||
263 | |||
264 | msg->status = ret; | ||
265 | spi_finalize_current_message(master); | ||
266 | |||
267 | return ret; | ||
268 | } | ||
269 | |||
270 | static int tegra_aon_spi_setup(struct spi_device *spi) | ||
271 | { | ||
272 | int ret; | ||
273 | struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master); | ||
274 | struct tegra_spi_device_controller_data *cdata = spi->controller_data; | ||
275 | |||
276 | dev_dbg(tspi->dev, "setup %d bpw, %scpol, %scpha, %dHz\n", | ||
277 | spi->bits_per_word, | ||
278 | spi->mode & SPI_CPOL ? "" : "~", | ||
279 | spi->mode & SPI_CPHA ? "" : "~", | ||
280 | spi->max_speed_hz); | ||
281 | |||
282 | if (!cdata) { | ||
283 | cdata = tegra_spi_get_cdata_dt(spi, tspi); | ||
284 | if (!cdata) | ||
285 | dev_err(&spi->dev, "Controller data not found\n"); | ||
286 | else | ||
287 | spi->controller_data = cdata; | ||
288 | } | ||
289 | |||
290 | /* Set speed to the spi max fequency if spi device has not set */ | ||
291 | spi->max_speed_hz = spi->max_speed_hz ? : tspi->spi_max_frequency; | ||
292 | |||
293 | ret = do_ivc_aon_spi_setup(spi, tspi); | ||
294 | if (ret) { | ||
295 | dev_err(&spi->dev, "SPI aon client setup failed %d\n", ret); | ||
296 | ret = -EIO; | ||
297 | } | ||
298 | |||
299 | return ret; | ||
300 | } | ||
301 | |||
302 | static const struct tegra_aon_spi_chipdata tegra186_aon_spi_chipdata = { | ||
303 | .set_rx_tap_delay = false, | ||
304 | }; | ||
305 | |||
306 | static const struct of_device_id tegra_aon_spi_of_match[] = { | ||
307 | { | ||
308 | .compatible = "nvidia,tegra186-aon-spi", | ||
309 | .data = &tegra186_aon_spi_chipdata, | ||
310 | }, | ||
311 | {} | ||
312 | }; | ||
313 | MODULE_DEVICE_TABLE(of, tegra_aon_spi_of_match); | ||
314 | |||
315 | static int tegra_aon_spi_probe(struct platform_device *pdev) | ||
316 | { | ||
317 | struct spi_master *master; | ||
318 | struct tegra_spi_data *tspi; | ||
319 | struct device_node *np = pdev->dev.of_node; | ||
320 | const unsigned int *prop; | ||
321 | const struct tegra_aon_spi_chipdata *chip_data = NULL; | ||
322 | int ret = 0; | ||
323 | int bus_num; | ||
324 | |||
325 | chip_data = of_device_get_match_data(&pdev->dev); | ||
326 | if (!chip_data) { | ||
327 | dev_err(&pdev->dev, "No platform/chip data, exiting\n"); | ||
328 | return -ENODEV; | ||
329 | } | ||
330 | bus_num = of_alias_get_id(pdev->dev.of_node, "spi"); | ||
331 | if (bus_num < 0) { | ||
332 | dev_warn(&pdev->dev, | ||
333 | "Dynamic bus number will be registerd\n"); | ||
334 | bus_num = -1; | ||
335 | } | ||
336 | |||
337 | master = spi_alloc_master(&pdev->dev, sizeof(*tspi)); | ||
338 | if (!master) | ||
339 | return -ENOMEM; | ||
340 | |||
341 | /* the spi->mode bits understood by this driver: */ | ||
342 | master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST | | ||
343 | SPI_TX_DUAL | SPI_RX_DUAL; | ||
344 | /* supported bpw 4-32 */ | ||
345 | master->bits_per_word_mask = (u32) ~(BIT(0)|BIT(1)|BIT(2)); | ||
346 | master->setup = tegra_aon_spi_setup; | ||
347 | master->transfer_one_message = tegra_spi_transfer_one_message; | ||
348 | master->num_chipselect = MAX_CHIP_SELECT; | ||
349 | master->bus_num = bus_num; | ||
350 | |||
351 | dev_set_drvdata(&pdev->dev, master); | ||
352 | tspi = spi_master_get_devdata(master); | ||
353 | tspi->master = master; | ||
354 | tspi->dev = &pdev->dev; | ||
355 | tspi->cl.dev = &pdev->dev; | ||
356 | tspi->cl.tx_block = true; | ||
357 | tspi->cl.tx_tout = TX_BLOCK_PERIOD; | ||
358 | tspi->cl.knows_txdone = false; | ||
359 | tspi->cl.rx_callback = tegra_aon_spi_mbox_rcv_msg; | ||
360 | tspi->mbox = mbox_request_channel(&tspi->cl, 0); | ||
361 | if (IS_ERR(tspi->mbox)) { | ||
362 | dev_warn(&pdev->dev, | ||
363 | "can't get mailbox channel (%d)\n", | ||
364 | (int)PTR_ERR(tspi->mbox)); | ||
365 | ret = PTR_ERR(tspi->mbox); | ||
366 | goto exit_free_master; | ||
367 | } | ||
368 | dev_dbg(&pdev->dev, "tspi->mbox = %p\n", tspi->mbox); | ||
369 | tspi->def_chip_select = SPI_DEF_CHIPSELECT; | ||
370 | tspi->dev = &pdev->dev; | ||
371 | tspi->chip_data = chip_data; | ||
372 | tspi->xfer_completion = devm_kzalloc(&pdev->dev, | ||
373 | sizeof(struct completion), GFP_KERNEL); | ||
374 | if (!tspi->xfer_completion) { | ||
375 | ret = -ENOMEM; | ||
376 | goto exit_free_mbox; | ||
377 | } | ||
378 | init_completion(tspi->xfer_completion); | ||
379 | |||
380 | prop = of_get_property(np, "spi-max-frequency", NULL); | ||
381 | if (prop) | ||
382 | tspi->spi_max_frequency = be32_to_cpup(prop); | ||
383 | if (!tspi->spi_max_frequency) { | ||
384 | tspi->spi_max_frequency = SPI_DEFAULT_SPEED; | ||
385 | dev_dbg(&pdev->dev, "Max frequency set to %d\n", | ||
386 | SPI_DEFAULT_SPEED); | ||
387 | } | ||
388 | |||
389 | tspi->spi_req = devm_kzalloc(tspi->dev, sizeof(*tspi->spi_req), | ||
390 | GFP_KERNEL); | ||
391 | if (!tspi->spi_req) { | ||
392 | ret = -ENOMEM; | ||
393 | goto exit_free_mbox; | ||
394 | } | ||
395 | tspi->spi_resp = devm_kzalloc(tspi->dev, sizeof(*tspi->spi_resp), | ||
396 | GFP_KERNEL); | ||
397 | if (!tspi->spi_resp) { | ||
398 | ret = -ENOMEM; | ||
399 | goto exit_free_mbox; | ||
400 | } | ||
401 | |||
402 | ret = do_ivc_aon_spi_init(tspi); | ||
403 | if (ret) { | ||
404 | dev_err(&pdev->dev, "SPI aon init failed %d\n", ret); | ||
405 | ret = -EIO; | ||
406 | goto exit_free_mbox; | ||
407 | } | ||
408 | master->dev.of_node = pdev->dev.of_node; | ||
409 | ret = devm_spi_register_master(&pdev->dev, master); | ||
410 | if (ret < 0) { | ||
411 | dev_err(&pdev->dev, "can not register to master err %d\n", ret); | ||
412 | goto exit_free_mbox; | ||
413 | } | ||
414 | dev_dbg(&pdev->dev, "tegra_aon_spi_driver_probe() OK\n"); | ||
415 | |||
416 | return ret; | ||
417 | |||
418 | exit_free_mbox: | ||
419 | mbox_free_channel(tspi->mbox); | ||
420 | exit_free_master: | ||
421 | spi_master_put(master); | ||
422 | dev_err(&pdev->dev, "tegra_aon_spi_driver_probe() FAILED\n"); | ||
423 | |||
424 | return ret; | ||
425 | } | ||
426 | |||
427 | static int tegra_aon_spi_remove(struct platform_device *pdev) | ||
428 | { | ||
429 | struct spi_master *master = dev_get_drvdata(&pdev->dev); | ||
430 | struct tegra_spi_data *tspi = spi_master_get_devdata(master); | ||
431 | |||
432 | mbox_free_channel(tspi->mbox); | ||
433 | |||
434 | return 0; | ||
435 | } | ||
436 | |||
437 | #ifdef CONFIG_PM_SLEEP | ||
438 | static int tegra_aon_spi_suspend(struct device *dev) | ||
439 | { | ||
440 | struct spi_master *master = dev_get_drvdata(dev); | ||
441 | struct tegra_spi_data *tspi = spi_master_get_devdata(master); | ||
442 | int len, ret, status; | ||
443 | |||
444 | ret = spi_master_suspend(master); | ||
445 | if (ret) | ||
446 | return ret; | ||
447 | |||
448 | tspi->spi_req->req_type = AON_SPI_REQUEST_TYPE_SUSPEND; | ||
449 | len = sizeof(tspi->spi_req->req_type); | ||
450 | status = ivc_aon_spi_send_req(tspi, len); | ||
451 | if (status) | ||
452 | return -EBUSY; | ||
453 | |||
454 | return 0; | ||
455 | } | ||
456 | |||
457 | static int tegra_aon_spi_resume(struct device *dev) | ||
458 | { | ||
459 | struct spi_master *master = dev_get_drvdata(dev); | ||
460 | struct tegra_spi_data *tspi = spi_master_get_devdata(master); | ||
461 | int len, ret; | ||
462 | |||
463 | tspi->spi_req->req_type = AON_SPI_REQUEST_TYPE_RESUME; | ||
464 | len = sizeof(tspi->spi_req->req_type); | ||
465 | ret = ivc_aon_spi_send_req(tspi, len); | ||
466 | if (ret) | ||
467 | return -EIO; | ||
468 | |||
469 | return spi_master_resume(master); | ||
470 | } | ||
471 | #endif | ||
472 | |||
473 | static const struct dev_pm_ops tegra_aon_spi_pm_ops = { | ||
474 | SET_SYSTEM_SLEEP_PM_OPS(tegra_aon_spi_suspend, tegra_aon_spi_resume) | ||
475 | }; | ||
476 | |||
477 | static struct platform_driver tegra_aon_spi_driver = { | ||
478 | .driver = { | ||
479 | .name = "tegra-aon-spi", | ||
480 | .owner = THIS_MODULE, | ||
481 | .of_match_table = of_match_ptr(tegra_aon_spi_of_match), | ||
482 | .pm = &tegra_aon_spi_pm_ops, | ||
483 | }, | ||
484 | .remove = tegra_aon_spi_remove, | ||
485 | .probe = tegra_aon_spi_probe, | ||
486 | }; | ||
487 | module_platform_driver(tegra_aon_spi_driver); | ||
488 | |||
489 | MODULE_DESCRIPTION("NVIDIA Tegra186 AON SPI Controller Driver"); | ||
490 | MODULE_LICENSE("GPL v2"); | ||