1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * |
4 | * Copyright (c) 2011, The Linux Foundation. All rights reserved. |
5 | */ |
6 | #include <linux/of.h> |
7 | #include <linux/of_dma.h> |
8 | #include <linux/bitops.h> |
9 | #include <linux/mmc/host.h> |
10 | #include <linux/mmc/card.h> |
11 | #include "mmci.h" |
12 | |
13 | /* Registers */ |
14 | #define DML_CONFIG 0x00 |
15 | #define PRODUCER_CRCI_MSK GENMASK(1, 0) |
16 | #define PRODUCER_CRCI_DISABLE 0 |
17 | #define PRODUCER_CRCI_X_SEL BIT(0) |
18 | #define PRODUCER_CRCI_Y_SEL BIT(1) |
19 | #define CONSUMER_CRCI_MSK GENMASK(3, 2) |
20 | #define CONSUMER_CRCI_DISABLE 0 |
21 | #define CONSUMER_CRCI_X_SEL BIT(2) |
22 | #define CONSUMER_CRCI_Y_SEL BIT(3) |
23 | #define PRODUCER_TRANS_END_EN BIT(4) |
24 | #define BYPASS BIT(16) |
25 | #define DIRECT_MODE BIT(17) |
26 | #define INFINITE_CONS_TRANS BIT(18) |
27 | |
28 | #define DML_SW_RESET 0x08 |
29 | #define DML_PRODUCER_START 0x0c |
30 | #define DML_CONSUMER_START 0x10 |
31 | #define DML_PRODUCER_PIPE_LOGICAL_SIZE 0x14 |
32 | #define DML_CONSUMER_PIPE_LOGICAL_SIZE 0x18 |
33 | #define DML_PIPE_ID 0x1c |
34 | #define PRODUCER_PIPE_ID_SHFT 0 |
35 | #define PRODUCER_PIPE_ID_MSK GENMASK(4, 0) |
36 | #define CONSUMER_PIPE_ID_SHFT 16 |
37 | #define CONSUMER_PIPE_ID_MSK GENMASK(20, 16) |
38 | |
39 | #define DML_PRODUCER_BAM_BLOCK_SIZE 0x24 |
40 | #define DML_PRODUCER_BAM_TRANS_SIZE 0x28 |
41 | |
42 | /* other definitions */ |
43 | #define PRODUCER_PIPE_LOGICAL_SIZE 4096 |
44 | #define CONSUMER_PIPE_LOGICAL_SIZE 4096 |
45 | |
46 | #define DML_OFFSET 0x800 |
47 | |
48 | static int qcom_dma_start(struct mmci_host *host, unsigned int *datactrl) |
49 | { |
50 | u32 config; |
51 | void __iomem *base = host->base + DML_OFFSET; |
52 | struct mmc_data *data = host->data; |
53 | int ret = mmci_dmae_start(host, datactrl); |
54 | |
55 | if (ret) |
56 | return ret; |
57 | |
58 | if (data->flags & MMC_DATA_READ) { |
59 | /* Read operation: configure DML for producer operation */ |
60 | /* Set producer CRCI-x and disable consumer CRCI */ |
61 | config = readl_relaxed(base + DML_CONFIG); |
62 | config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_X_SEL; |
63 | config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_DISABLE; |
64 | writel_relaxed(config, base + DML_CONFIG); |
65 | |
66 | /* Set the Producer BAM block size */ |
67 | writel_relaxed(data->blksz, base + DML_PRODUCER_BAM_BLOCK_SIZE); |
68 | |
69 | /* Set Producer BAM Transaction size */ |
70 | writel_relaxed(data->blocks * data->blksz, |
71 | base + DML_PRODUCER_BAM_TRANS_SIZE); |
72 | /* Set Producer Transaction End bit */ |
73 | config = readl_relaxed(base + DML_CONFIG); |
74 | config |= PRODUCER_TRANS_END_EN; |
75 | writel_relaxed(config, base + DML_CONFIG); |
76 | /* Trigger producer */ |
77 | writel_relaxed(1, base + DML_PRODUCER_START); |
78 | } else { |
79 | /* Write operation: configure DML for consumer operation */ |
80 | /* Set consumer CRCI-x and disable producer CRCI*/ |
81 | config = readl_relaxed(base + DML_CONFIG); |
82 | config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_X_SEL; |
83 | config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_DISABLE; |
84 | writel_relaxed(config, base + DML_CONFIG); |
85 | /* Clear Producer Transaction End bit */ |
86 | config = readl_relaxed(base + DML_CONFIG); |
87 | config &= ~PRODUCER_TRANS_END_EN; |
88 | writel_relaxed(config, base + DML_CONFIG); |
89 | /* Trigger consumer */ |
90 | writel_relaxed(1, base + DML_CONSUMER_START); |
91 | } |
92 | |
93 | /* make sure the dml is configured before dma is triggered */ |
94 | wmb(); |
95 | return 0; |
96 | } |
97 | |
98 | static int of_get_dml_pipe_index(struct device_node *np, const char *name) |
99 | { |
100 | int index; |
101 | struct of_phandle_args dma_spec; |
102 | |
103 | index = of_property_match_string(np, propname: "dma-names" , string: name); |
104 | |
105 | if (index < 0) |
106 | return -ENODEV; |
107 | |
108 | if (of_parse_phandle_with_args(np, list_name: "dmas" , cells_name: "#dma-cells" , index, |
109 | out_args: &dma_spec)) |
110 | return -ENODEV; |
111 | |
112 | if (dma_spec.args_count) |
113 | return dma_spec.args[0]; |
114 | |
115 | return -ENODEV; |
116 | } |
117 | |
118 | /* Initialize the dml hardware connected to SD Card controller */ |
119 | static int qcom_dma_setup(struct mmci_host *host) |
120 | { |
121 | u32 config; |
122 | void __iomem *base; |
123 | int consumer_id, producer_id; |
124 | struct device_node *np = host->mmc->parent->of_node; |
125 | |
126 | if (mmci_dmae_setup(host)) |
127 | return -EINVAL; |
128 | |
129 | consumer_id = of_get_dml_pipe_index(np, name: "tx" ); |
130 | producer_id = of_get_dml_pipe_index(np, name: "rx" ); |
131 | |
132 | if (producer_id < 0 || consumer_id < 0) { |
133 | mmci_dmae_release(host); |
134 | return -EINVAL; |
135 | } |
136 | |
137 | base = host->base + DML_OFFSET; |
138 | |
139 | /* Reset the DML block */ |
140 | writel_relaxed(1, base + DML_SW_RESET); |
141 | |
142 | /* Disable the producer and consumer CRCI */ |
143 | config = (PRODUCER_CRCI_DISABLE | CONSUMER_CRCI_DISABLE); |
144 | /* |
145 | * Disable the bypass mode. Bypass mode will only be used |
146 | * if data transfer is to happen in PIO mode and don't |
147 | * want the BAM interface to connect with SDCC-DML. |
148 | */ |
149 | config &= ~BYPASS; |
150 | /* |
151 | * Disable direct mode as we don't DML to MASTER the AHB bus. |
152 | * BAM connected with DML should MASTER the AHB bus. |
153 | */ |
154 | config &= ~DIRECT_MODE; |
155 | /* |
156 | * Disable infinite mode transfer as we won't be doing any |
157 | * infinite size data transfers. All data transfer will be |
158 | * of finite data size. |
159 | */ |
160 | config &= ~INFINITE_CONS_TRANS; |
161 | writel_relaxed(config, base + DML_CONFIG); |
162 | |
163 | /* |
164 | * Initialize the logical BAM pipe size for producer |
165 | * and consumer. |
166 | */ |
167 | writel_relaxed(PRODUCER_PIPE_LOGICAL_SIZE, |
168 | base + DML_PRODUCER_PIPE_LOGICAL_SIZE); |
169 | writel_relaxed(CONSUMER_PIPE_LOGICAL_SIZE, |
170 | base + DML_CONSUMER_PIPE_LOGICAL_SIZE); |
171 | |
172 | /* Initialize Producer/consumer pipe id */ |
173 | writel_relaxed(producer_id | (consumer_id << CONSUMER_PIPE_ID_SHFT), |
174 | base + DML_PIPE_ID); |
175 | |
176 | /* Make sure dml initialization is finished */ |
177 | mb(); |
178 | |
179 | return 0; |
180 | } |
181 | |
182 | static u32 qcom_get_dctrl_cfg(struct mmci_host *host) |
183 | { |
184 | return MCI_DPSM_ENABLE | (host->data->blksz << 4); |
185 | } |
186 | |
187 | static struct mmci_host_ops qcom_variant_ops = { |
188 | .prep_data = mmci_dmae_prep_data, |
189 | .unprep_data = mmci_dmae_unprep_data, |
190 | .get_datactrl_cfg = qcom_get_dctrl_cfg, |
191 | .get_next_data = mmci_dmae_get_next_data, |
192 | .dma_setup = qcom_dma_setup, |
193 | .dma_release = mmci_dmae_release, |
194 | .dma_start = qcom_dma_start, |
195 | .dma_finalize = mmci_dmae_finalize, |
196 | .dma_error = mmci_dmae_error, |
197 | }; |
198 | |
199 | void qcom_variant_init(struct mmci_host *host) |
200 | { |
201 | host->ops = &qcom_variant_ops; |
202 | } |
203 | |