2019-05-29 17:17:58 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2014-07-29 06:50:30 +04:00
/*
*
* Copyright ( c ) 2011 , The Linux Foundation . All rights reserved .
*/
# include <linux/of.h>
# include <linux/of_dma.h>
# include <linux/bitops.h>
# include <linux/mmc/host.h>
# include <linux/mmc/card.h>
# include "mmci.h"
/* Registers */
# define DML_CONFIG 0x00
# define PRODUCER_CRCI_MSK GENMASK(1, 0)
# define PRODUCER_CRCI_DISABLE 0
# define PRODUCER_CRCI_X_SEL BIT(0)
# define PRODUCER_CRCI_Y_SEL BIT(1)
# define CONSUMER_CRCI_MSK GENMASK(3, 2)
# define CONSUMER_CRCI_DISABLE 0
# define CONSUMER_CRCI_X_SEL BIT(2)
# define CONSUMER_CRCI_Y_SEL BIT(3)
# define PRODUCER_TRANS_END_EN BIT(4)
# define BYPASS BIT(16)
# define DIRECT_MODE BIT(17)
# define INFINITE_CONS_TRANS BIT(18)
# define DML_SW_RESET 0x08
# define DML_PRODUCER_START 0x0c
# define DML_CONSUMER_START 0x10
# define DML_PRODUCER_PIPE_LOGICAL_SIZE 0x14
# define DML_CONSUMER_PIPE_LOGICAL_SIZE 0x18
# define DML_PIPE_ID 0x1c
# define PRODUCER_PIPE_ID_SHFT 0
# define PRODUCER_PIPE_ID_MSK GENMASK(4, 0)
# define CONSUMER_PIPE_ID_SHFT 16
# define CONSUMER_PIPE_ID_MSK GENMASK(20, 16)
# define DML_PRODUCER_BAM_BLOCK_SIZE 0x24
# define DML_PRODUCER_BAM_TRANS_SIZE 0x28
/* other definitions */
# define PRODUCER_PIPE_LOGICAL_SIZE 4096
# define CONSUMER_PIPE_LOGICAL_SIZE 4096
# define DML_OFFSET 0x800
2019-03-06 17:04:53 +03:00
static int qcom_dma_start ( struct mmci_host * host , unsigned int * datactrl )
2014-07-29 06:50:30 +04:00
{
u32 config ;
void __iomem * base = host - > base + DML_OFFSET ;
2019-03-06 17:04:53 +03:00
struct mmc_data * data = host - > data ;
int ret = mmci_dmae_start ( host , datactrl ) ;
if ( ret )
return ret ;
2014-07-29 06:50:30 +04:00
if ( data - > flags & MMC_DATA_READ ) {
/* Read operation: configure DML for producer operation */
/* Set producer CRCI-x and disable consumer CRCI */
config = readl_relaxed ( base + DML_CONFIG ) ;
config = ( config & ~ PRODUCER_CRCI_MSK ) | PRODUCER_CRCI_X_SEL ;
config = ( config & ~ CONSUMER_CRCI_MSK ) | CONSUMER_CRCI_DISABLE ;
writel_relaxed ( config , base + DML_CONFIG ) ;
/* Set the Producer BAM block size */
writel_relaxed ( data - > blksz , base + DML_PRODUCER_BAM_BLOCK_SIZE ) ;
/* Set Producer BAM Transaction size */
writel_relaxed ( data - > blocks * data - > blksz ,
base + DML_PRODUCER_BAM_TRANS_SIZE ) ;
/* Set Producer Transaction End bit */
config = readl_relaxed ( base + DML_CONFIG ) ;
config | = PRODUCER_TRANS_END_EN ;
writel_relaxed ( config , base + DML_CONFIG ) ;
/* Trigger producer */
writel_relaxed ( 1 , base + DML_PRODUCER_START ) ;
} else {
/* Write operation: configure DML for consumer operation */
/* Set consumer CRCI-x and disable producer CRCI*/
config = readl_relaxed ( base + DML_CONFIG ) ;
config = ( config & ~ CONSUMER_CRCI_MSK ) | CONSUMER_CRCI_X_SEL ;
config = ( config & ~ PRODUCER_CRCI_MSK ) | PRODUCER_CRCI_DISABLE ;
writel_relaxed ( config , base + DML_CONFIG ) ;
/* Clear Producer Transaction End bit */
config = readl_relaxed ( base + DML_CONFIG ) ;
config & = ~ PRODUCER_TRANS_END_EN ;
writel_relaxed ( config , base + DML_CONFIG ) ;
/* Trigger consumer */
writel_relaxed ( 1 , base + DML_CONSUMER_START ) ;
}
/* make sure the dml is configured before dma is triggered */
wmb ( ) ;
2019-03-06 17:04:53 +03:00
return 0 ;
2014-07-29 06:50:30 +04:00
}
static int of_get_dml_pipe_index ( struct device_node * np , const char * name )
{
int index ;
struct of_phandle_args dma_spec ;
index = of_property_match_string ( np , " dma-names " , name ) ;
if ( index < 0 )
return - ENODEV ;
if ( of_parse_phandle_with_args ( np , " dmas " , " #dma-cells " , index ,
& dma_spec ) )
return - ENODEV ;
if ( dma_spec . args_count )
return dma_spec . args [ 0 ] ;
return - ENODEV ;
}
/* Initialize the dml hardware connected to SD Card controller */
2018-10-08 15:08:33 +03:00
static int qcom_dma_setup ( struct mmci_host * host )
2014-07-29 06:50:30 +04:00
{
u32 config ;
void __iomem * base ;
int consumer_id , producer_id ;
2018-07-16 14:08:18 +03:00
struct device_node * np = host - > mmc - > parent - > of_node ;
2014-07-29 06:50:30 +04:00
2018-10-08 15:08:33 +03:00
if ( mmci_dmae_setup ( host ) )
return - EINVAL ;
2014-07-29 06:50:30 +04:00
consumer_id = of_get_dml_pipe_index ( np , " tx " ) ;
producer_id = of_get_dml_pipe_index ( np , " rx " ) ;
2018-07-16 14:08:18 +03:00
if ( producer_id < 0 | | consumer_id < 0 ) {
2018-10-08 15:08:33 +03:00
mmci_dmae_release ( host ) ;
return - EINVAL ;
2018-07-16 14:08:18 +03:00
}
2014-07-29 06:50:30 +04:00
base = host - > base + DML_OFFSET ;
/* Reset the DML block */
writel_relaxed ( 1 , base + DML_SW_RESET ) ;
/* Disable the producer and consumer CRCI */
config = ( PRODUCER_CRCI_DISABLE | CONSUMER_CRCI_DISABLE ) ;
/*
* Disable the bypass mode . Bypass mode will only be used
* if data transfer is to happen in PIO mode and don ' t
* want the BAM interface to connect with SDCC - DML .
*/
config & = ~ BYPASS ;
/*
* Disable direct mode as we don ' t DML to MASTER the AHB bus .
* BAM connected with DML should MASTER the AHB bus .
*/
config & = ~ DIRECT_MODE ;
/*
* Disable infinite mode transfer as we won ' t be doing any
* infinite size data transfers . All data transfer will be
* of finite data size .
*/
config & = ~ INFINITE_CONS_TRANS ;
writel_relaxed ( config , base + DML_CONFIG ) ;
/*
* Initialize the logical BAM pipe size for producer
* and consumer .
*/
writel_relaxed ( PRODUCER_PIPE_LOGICAL_SIZE ,
base + DML_PRODUCER_PIPE_LOGICAL_SIZE ) ;
writel_relaxed ( CONSUMER_PIPE_LOGICAL_SIZE ,
base + DML_CONSUMER_PIPE_LOGICAL_SIZE ) ;
/* Initialize Producer/consumer pipe id */
writel_relaxed ( producer_id | ( consumer_id < < CONSUMER_PIPE_ID_SHFT ) ,
base + DML_PIPE_ID ) ;
2017-02-28 01:29:20 +03:00
/* Make sure dml initialization is finished */
2014-07-29 06:50:30 +04:00
mb ( ) ;
2018-10-08 15:08:33 +03:00
return 0 ;
2018-07-16 14:08:18 +03:00
}
2014-07-29 06:50:30 +04:00
2019-03-27 12:05:30 +03:00
static u32 qcom_get_dctrl_cfg ( struct mmci_host * host )
{
return MCI_DPSM_ENABLE | ( host - > data - > blksz < < 4 ) ;
}
2018-07-16 14:08:18 +03:00
static struct mmci_host_ops qcom_variant_ops = {
2018-10-08 15:08:36 +03:00
. prep_data = mmci_dmae_prep_data ,
. unprep_data = mmci_dmae_unprep_data ,
2019-03-27 12:05:30 +03:00
. get_datactrl_cfg = qcom_get_dctrl_cfg ,
2018-10-08 15:08:37 +03:00
. get_next_data = mmci_dmae_get_next_data ,
2018-07-16 14:08:18 +03:00
. dma_setup = qcom_dma_setup ,
2018-10-08 15:08:33 +03:00
. dma_release = mmci_dmae_release ,
2019-03-06 17:04:53 +03:00
. dma_start = qcom_dma_start ,
2018-10-08 15:08:39 +03:00
. dma_finalize = mmci_dmae_finalize ,
2018-10-08 15:08:40 +03:00
. dma_error = mmci_dmae_error ,
2018-07-16 14:08:18 +03:00
} ;
void qcom_variant_init ( struct mmci_host * host )
{
host - > ops = & qcom_variant_ops ;
2014-07-29 06:50:30 +04:00
}