diff options
Diffstat (limited to 'drivers/dma/ste_dma40.c')
-rw-r--r-- | drivers/dma/ste_dma40.c | 141 |
1 files changed, 137 insertions, 4 deletions
diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 21a759731ef1..17e2600a00cf 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c | |||
@@ -208,6 +208,9 @@ struct d40_chan { | |||
208 | struct d40_def_lcsp log_def; | 208 | struct d40_def_lcsp log_def; |
209 | struct d40_lcla_elem lcla; | 209 | struct d40_lcla_elem lcla; |
210 | struct d40_log_lli_full *lcpa; | 210 | struct d40_log_lli_full *lcpa; |
211 | /* Runtime reconfiguration */ | ||
212 | dma_addr_t runtime_addr; | ||
213 | enum dma_data_direction runtime_direction; | ||
211 | }; | 214 | }; |
212 | 215 | ||
213 | /** | 216 | /** |
@@ -1886,9 +1889,16 @@ static int d40_prep_slave_sg_log(struct d40_desc *d40d, | |||
1886 | d40d->lli_tx_len = 1; | 1889 | d40d->lli_tx_len = 1; |
1887 | 1890 | ||
1888 | if (direction == DMA_FROM_DEVICE) | 1891 | if (direction == DMA_FROM_DEVICE) |
1889 | dev_addr = d40c->base->plat_data->dev_rx[d40c->dma_cfg.src_dev_type]; | 1892 | if (d40c->runtime_addr) |
1893 | dev_addr = d40c->runtime_addr; | ||
1894 | else | ||
1895 | dev_addr = d40c->base->plat_data->dev_rx[d40c->dma_cfg.src_dev_type]; | ||
1890 | else if (direction == DMA_TO_DEVICE) | 1896 | else if (direction == DMA_TO_DEVICE) |
1891 | dev_addr = d40c->base->plat_data->dev_tx[d40c->dma_cfg.dst_dev_type]; | 1897 | if (d40c->runtime_addr) |
1898 | dev_addr = d40c->runtime_addr; | ||
1899 | else | ||
1900 | dev_addr = d40c->base->plat_data->dev_tx[d40c->dma_cfg.dst_dev_type]; | ||
1901 | |||
1892 | else | 1902 | else |
1893 | return -EINVAL; | 1903 | return -EINVAL; |
1894 | 1904 | ||
@@ -1931,9 +1941,15 @@ static int d40_prep_slave_sg_phy(struct d40_desc *d40d, | |||
1931 | 1941 | ||
1932 | if (direction == DMA_FROM_DEVICE) { | 1942 | if (direction == DMA_FROM_DEVICE) { |
1933 | dst_dev_addr = 0; | 1943 | dst_dev_addr = 0; |
1934 | src_dev_addr = d40c->base->plat_data->dev_rx[d40c->dma_cfg.src_dev_type]; | 1944 | if (d40c->runtime_addr) |
1945 | src_dev_addr = d40c->runtime_addr; | ||
1946 | else | ||
1947 | src_dev_addr = d40c->base->plat_data->dev_rx[d40c->dma_cfg.src_dev_type]; | ||
1935 | } else if (direction == DMA_TO_DEVICE) { | 1948 | } else if (direction == DMA_TO_DEVICE) { |
1936 | dst_dev_addr = d40c->base->plat_data->dev_tx[d40c->dma_cfg.dst_dev_type]; | 1949 | if (d40c->runtime_addr) |
1950 | dst_dev_addr = d40c->runtime_addr; | ||
1951 | else | ||
1952 | dst_dev_addr = d40c->base->plat_data->dev_tx[d40c->dma_cfg.dst_dev_type]; | ||
1937 | src_dev_addr = 0; | 1953 | src_dev_addr = 0; |
1938 | } else | 1954 | } else |
1939 | return -EINVAL; | 1955 | return -EINVAL; |
@@ -2070,6 +2086,117 @@ static void d40_issue_pending(struct dma_chan *chan) | |||
2070 | spin_unlock_irqrestore(&d40c->lock, flags); | 2086 | spin_unlock_irqrestore(&d40c->lock, flags); |
2071 | } | 2087 | } |
2072 | 2088 | ||
2089 | /* Runtime reconfiguration extension */ | ||
2090 | static void d40_set_runtime_config(struct dma_chan *chan, | ||
2091 | struct dma_slave_config *config) | ||
2092 | { | ||
2093 | struct d40_chan *d40c = container_of(chan, struct d40_chan, chan); | ||
2094 | struct stedma40_chan_cfg *cfg = &d40c->dma_cfg; | ||
2095 | enum dma_slave_buswidth config_addr_width; | ||
2096 | dma_addr_t config_addr; | ||
2097 | u32 config_maxburst; | ||
2098 | enum stedma40_periph_data_width addr_width; | ||
2099 | int psize; | ||
2100 | |||
2101 | if (config->direction == DMA_FROM_DEVICE) { | ||
2102 | dma_addr_t dev_addr_rx = | ||
2103 | d40c->base->plat_data->dev_rx[cfg->src_dev_type]; | ||
2104 | |||
2105 | config_addr = config->src_addr; | ||
2106 | if (dev_addr_rx) | ||
2107 | dev_dbg(d40c->base->dev, | ||
2108 | "channel has a pre-wired RX address %08x " | ||
2109 | "overriding with %08x\n", | ||
2110 | dev_addr_rx, config_addr); | ||
2111 | if (cfg->dir != STEDMA40_PERIPH_TO_MEM) | ||
2112 | dev_dbg(d40c->base->dev, | ||
2113 | "channel was not configured for peripheral " | ||
2114 | "to memory transfer (%d) overriding\n", | ||
2115 | cfg->dir); | ||
2116 | cfg->dir = STEDMA40_PERIPH_TO_MEM; | ||
2117 | |||
2118 | config_addr_width = config->src_addr_width; | ||
2119 | config_maxburst = config->src_maxburst; | ||
2120 | |||
2121 | } else if (config->direction == DMA_TO_DEVICE) { | ||
2122 | dma_addr_t dev_addr_tx = | ||
2123 | d40c->base->plat_data->dev_tx[cfg->dst_dev_type]; | ||
2124 | |||
2125 | config_addr = config->dst_addr; | ||
2126 | if (dev_addr_tx) | ||
2127 | dev_dbg(d40c->base->dev, | ||
2128 | "channel has a pre-wired TX address %08x " | ||
2129 | "overriding with %08x\n", | ||
2130 | dev_addr_tx, config_addr); | ||
2131 | if (cfg->dir != STEDMA40_MEM_TO_PERIPH) | ||
2132 | dev_dbg(d40c->base->dev, | ||
2133 | "channel was not configured for memory " | ||
2134 | "to peripheral transfer (%d) overriding\n", | ||
2135 | cfg->dir); | ||
2136 | cfg->dir = STEDMA40_MEM_TO_PERIPH; | ||
2137 | |||
2138 | config_addr_width = config->dst_addr_width; | ||
2139 | config_maxburst = config->dst_maxburst; | ||
2140 | |||
2141 | } else { | ||
2142 | dev_err(d40c->base->dev, | ||
2143 | "unrecognized channel direction %d\n", | ||
2144 | config->direction); | ||
2145 | return; | ||
2146 | } | ||
2147 | |||
2148 | switch (config_addr_width) { | ||
2149 | case DMA_SLAVE_BUSWIDTH_1_BYTE: | ||
2150 | addr_width = STEDMA40_BYTE_WIDTH; | ||
2151 | break; | ||
2152 | case DMA_SLAVE_BUSWIDTH_2_BYTES: | ||
2153 | addr_width = STEDMA40_HALFWORD_WIDTH; | ||
2154 | break; | ||
2155 | case DMA_SLAVE_BUSWIDTH_4_BYTES: | ||
2156 | addr_width = STEDMA40_WORD_WIDTH; | ||
2157 | break; | ||
2158 | case DMA_SLAVE_BUSWIDTH_8_BYTES: | ||
2159 | addr_width = STEDMA40_DOUBLEWORD_WIDTH; | ||
2160 | break; | ||
2161 | default: | ||
2162 | dev_err(d40c->base->dev, | ||
2163 | "illegal peripheral address width " | ||
2164 | "requested (%d)\n", | ||
2165 | config->src_addr_width); | ||
2166 | return; | ||
2167 | } | ||
2168 | |||
2169 | if (config_maxburst >= 16) | ||
2170 | psize = STEDMA40_PSIZE_LOG_16; | ||
2171 | else if (config_maxburst >= 8) | ||
2172 | psize = STEDMA40_PSIZE_LOG_8; | ||
2173 | else if (config_maxburst >= 4) | ||
2174 | psize = STEDMA40_PSIZE_LOG_4; | ||
2175 | else | ||
2176 | psize = STEDMA40_PSIZE_LOG_1; | ||
2177 | |||
2178 | /* Set up all the endpoint configs */ | ||
2179 | cfg->src_info.data_width = addr_width; | ||
2180 | cfg->src_info.psize = psize; | ||
2181 | cfg->src_info.endianess = STEDMA40_LITTLE_ENDIAN; | ||
2182 | cfg->src_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL; | ||
2183 | cfg->dst_info.data_width = addr_width; | ||
2184 | cfg->dst_info.psize = psize; | ||
2185 | cfg->dst_info.endianess = STEDMA40_LITTLE_ENDIAN; | ||
2186 | cfg->dst_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL; | ||
2187 | |||
2188 | /* These settings will take precedence later */ | ||
2189 | d40c->runtime_addr = config_addr; | ||
2190 | d40c->runtime_direction = config->direction; | ||
2191 | dev_dbg(d40c->base->dev, | ||
2192 | "configured channel %s for %s, data width %d, " | ||
2193 | "maxburst %d bytes, LE, no flow control\n", | ||
2194 | dma_chan_name(chan), | ||
2195 | (config->direction == DMA_FROM_DEVICE) ? "RX" : "TX", | ||
2196 | config_addr_width, | ||
2197 | config_maxburst); | ||
2198 | } | ||
2199 | |||
2073 | static int d40_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, | 2200 | static int d40_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, |
2074 | unsigned long arg) | 2201 | unsigned long arg) |
2075 | { | 2202 | { |
@@ -2092,6 +2219,12 @@ static int d40_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, | |||
2092 | return d40_pause(chan); | 2219 | return d40_pause(chan); |
2093 | case DMA_RESUME: | 2220 | case DMA_RESUME: |
2094 | return d40_resume(chan); | 2221 | return d40_resume(chan); |
2222 | case DMA_SLAVE_CONFIG: | ||
2223 | d40_set_runtime_config(chan, | ||
2224 | (struct dma_slave_config *) arg); | ||
2225 | return 0; | ||
2226 | default: | ||
2227 | break; | ||
2095 | } | 2228 | } |
2096 | 2229 | ||
2097 | /* Other commands are unimplemented */ | 2230 | /* Other commands are unimplemented */ |