mmci_qcom_dml.c (5799B)
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 48static 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 98static 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, "dma-names", name); 104 105 if (index < 0) 106 return -ENODEV; 107 108 if (of_parse_phandle_with_args(np, "dmas", "#dma-cells", index, 109 &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 */ 119static 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, "tx"); 130 producer_id = of_get_dml_pipe_index(np, "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 182static u32 qcom_get_dctrl_cfg(struct mmci_host *host) 183{ 184 return MCI_DPSM_ENABLE | (host->data->blksz << 4); 185} 186 187static 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 199void qcom_variant_init(struct mmci_host *host) 200{ 201 host->ops = &qcom_variant_ops; 202}