162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci/* Disable MMIO tracing to prevent excessive logging of unwanted MMIO traces */ 562306a36Sopenharmony_ci#define __DISABLE_TRACE_MMIO__ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/acpi.h> 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci#include <linux/of_platform.h> 1562306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/soc/qcom/geni-se.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/** 2062306a36Sopenharmony_ci * DOC: Overview 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * Generic Interface (GENI) Serial Engine (SE) Wrapper driver is introduced 2362306a36Sopenharmony_ci * to manage GENI firmware based Qualcomm Universal Peripheral (QUP) Wrapper 2462306a36Sopenharmony_ci * controller. QUP Wrapper is designed to support various serial bus protocols 2562306a36Sopenharmony_ci * like UART, SPI, I2C, I3C, etc. 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/** 2962306a36Sopenharmony_ci * DOC: Hardware description 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * GENI based QUP is a highly-flexible and programmable module for supporting 3262306a36Sopenharmony_ci * a wide range of serial interfaces like UART, SPI, I2C, I3C, etc. A single 3362306a36Sopenharmony_ci * QUP module can provide upto 8 serial interfaces, using its internal 3462306a36Sopenharmony_ci * serial engines. The actual configuration is determined by the target 3562306a36Sopenharmony_ci * platform configuration. The protocol supported by each interface is 3662306a36Sopenharmony_ci * determined by the firmware loaded to the serial engine. Each SE consists 3762306a36Sopenharmony_ci * of a DMA Engine and GENI sub modules which enable serial engines to 3862306a36Sopenharmony_ci * support FIFO and DMA modes of operation. 3962306a36Sopenharmony_ci * 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * +-----------------------------------------+ 4262306a36Sopenharmony_ci * |QUP Wrapper | 4362306a36Sopenharmony_ci * | +----------------------------+ | 4462306a36Sopenharmony_ci * --QUP & SE Clocks--> | Serial Engine N | +-IO------> 4562306a36Sopenharmony_ci * | | ... | | Interface 4662306a36Sopenharmony_ci * <---Clock Perf.----+ +----+-----------------------+ | | 4762306a36Sopenharmony_ci * State Interface | | Serial Engine 1 | | | 4862306a36Sopenharmony_ci * | | | | | 4962306a36Sopenharmony_ci * | | | | | 5062306a36Sopenharmony_ci * <--------AHB-------> | | | | 5162306a36Sopenharmony_ci * | | +----+ | 5262306a36Sopenharmony_ci * | | | | 5362306a36Sopenharmony_ci * | | | | 5462306a36Sopenharmony_ci * <------SE IRQ------+ +----------------------------+ | 5562306a36Sopenharmony_ci * | | 5662306a36Sopenharmony_ci * +-----------------------------------------+ 5762306a36Sopenharmony_ci * 5862306a36Sopenharmony_ci * Figure 1: GENI based QUP Wrapper 5962306a36Sopenharmony_ci * 6062306a36Sopenharmony_ci * The GENI submodules include primary and secondary sequencers which are 6162306a36Sopenharmony_ci * used to drive TX & RX operations. On serial interfaces that operate using 6262306a36Sopenharmony_ci * master-slave model, primary sequencer drives both TX & RX operations. On 6362306a36Sopenharmony_ci * serial interfaces that operate using peer-to-peer model, primary sequencer 6462306a36Sopenharmony_ci * drives TX operation and secondary sequencer drives RX operation. 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/** 6862306a36Sopenharmony_ci * DOC: Software description 6962306a36Sopenharmony_ci * 7062306a36Sopenharmony_ci * GENI SE Wrapper driver is structured into 2 parts: 7162306a36Sopenharmony_ci * 7262306a36Sopenharmony_ci * geni_wrapper represents QUP Wrapper controller. This part of the driver 7362306a36Sopenharmony_ci * manages QUP Wrapper information such as hardware version, clock 7462306a36Sopenharmony_ci * performance table that is common to all the internal serial engines. 7562306a36Sopenharmony_ci * 7662306a36Sopenharmony_ci * geni_se represents serial engine. This part of the driver manages serial 7762306a36Sopenharmony_ci * engine information such as clocks, containing QUP Wrapper, etc. This part 7862306a36Sopenharmony_ci * of driver also supports operations (eg. initialize the concerned serial 7962306a36Sopenharmony_ci * engine, select between FIFO and DMA mode of operation etc.) that are 8062306a36Sopenharmony_ci * common to all the serial engines and are independent of serial interfaces. 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci#define MAX_CLK_PERF_LEVEL 32 8462306a36Sopenharmony_ci#define MAX_CLKS 2 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/** 8762306a36Sopenharmony_ci * struct geni_wrapper - Data structure to represent the QUP Wrapper Core 8862306a36Sopenharmony_ci * @dev: Device pointer of the QUP wrapper core 8962306a36Sopenharmony_ci * @base: Base address of this instance of QUP wrapper core 9062306a36Sopenharmony_ci * @clks: Handle to the primary & optional secondary AHB clocks 9162306a36Sopenharmony_ci * @num_clks: Count of clocks 9262306a36Sopenharmony_ci * @to_core: Core ICC path 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_cistruct geni_wrapper { 9562306a36Sopenharmony_ci struct device *dev; 9662306a36Sopenharmony_ci void __iomem *base; 9762306a36Sopenharmony_ci struct clk_bulk_data clks[MAX_CLKS]; 9862306a36Sopenharmony_ci unsigned int num_clks; 9962306a36Sopenharmony_ci}; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/** 10262306a36Sopenharmony_ci * struct geni_se_desc - Data structure to represent the QUP Wrapper resources 10362306a36Sopenharmony_ci * @clks: Name of the primary & optional secondary AHB clocks 10462306a36Sopenharmony_ci * @num_clks: Count of clock names 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_cistruct geni_se_desc { 10762306a36Sopenharmony_ci unsigned int num_clks; 10862306a36Sopenharmony_ci const char * const *clks; 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic const char * const icc_path_names[] = {"qup-core", "qup-config", 11262306a36Sopenharmony_ci "qup-memory"}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci#define QUP_HW_VER_REG 0x4 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/* Common SE registers */ 11762306a36Sopenharmony_ci#define GENI_INIT_CFG_REVISION 0x0 11862306a36Sopenharmony_ci#define GENI_S_INIT_CFG_REVISION 0x4 11962306a36Sopenharmony_ci#define GENI_OUTPUT_CTRL 0x24 12062306a36Sopenharmony_ci#define GENI_CGC_CTRL 0x28 12162306a36Sopenharmony_ci#define GENI_CLK_CTRL_RO 0x60 12262306a36Sopenharmony_ci#define GENI_FW_S_REVISION_RO 0x6c 12362306a36Sopenharmony_ci#define SE_GENI_BYTE_GRAN 0x254 12462306a36Sopenharmony_ci#define SE_GENI_TX_PACKING_CFG0 0x260 12562306a36Sopenharmony_ci#define SE_GENI_TX_PACKING_CFG1 0x264 12662306a36Sopenharmony_ci#define SE_GENI_RX_PACKING_CFG0 0x284 12762306a36Sopenharmony_ci#define SE_GENI_RX_PACKING_CFG1 0x288 12862306a36Sopenharmony_ci#define SE_GENI_M_GP_LENGTH 0x910 12962306a36Sopenharmony_ci#define SE_GENI_S_GP_LENGTH 0x914 13062306a36Sopenharmony_ci#define SE_DMA_TX_PTR_L 0xc30 13162306a36Sopenharmony_ci#define SE_DMA_TX_PTR_H 0xc34 13262306a36Sopenharmony_ci#define SE_DMA_TX_ATTR 0xc38 13362306a36Sopenharmony_ci#define SE_DMA_TX_LEN 0xc3c 13462306a36Sopenharmony_ci#define SE_DMA_TX_IRQ_EN 0xc48 13562306a36Sopenharmony_ci#define SE_DMA_TX_IRQ_EN_SET 0xc4c 13662306a36Sopenharmony_ci#define SE_DMA_TX_IRQ_EN_CLR 0xc50 13762306a36Sopenharmony_ci#define SE_DMA_TX_LEN_IN 0xc54 13862306a36Sopenharmony_ci#define SE_DMA_TX_MAX_BURST 0xc5c 13962306a36Sopenharmony_ci#define SE_DMA_RX_PTR_L 0xd30 14062306a36Sopenharmony_ci#define SE_DMA_RX_PTR_H 0xd34 14162306a36Sopenharmony_ci#define SE_DMA_RX_ATTR 0xd38 14262306a36Sopenharmony_ci#define SE_DMA_RX_LEN 0xd3c 14362306a36Sopenharmony_ci#define SE_DMA_RX_IRQ_EN 0xd48 14462306a36Sopenharmony_ci#define SE_DMA_RX_IRQ_EN_SET 0xd4c 14562306a36Sopenharmony_ci#define SE_DMA_RX_IRQ_EN_CLR 0xd50 14662306a36Sopenharmony_ci#define SE_DMA_RX_LEN_IN 0xd54 14762306a36Sopenharmony_ci#define SE_DMA_RX_MAX_BURST 0xd5c 14862306a36Sopenharmony_ci#define SE_DMA_RX_FLUSH 0xd60 14962306a36Sopenharmony_ci#define SE_GSI_EVENT_EN 0xe18 15062306a36Sopenharmony_ci#define SE_IRQ_EN 0xe1c 15162306a36Sopenharmony_ci#define SE_DMA_GENERAL_CFG 0xe30 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* GENI_OUTPUT_CTRL fields */ 15462306a36Sopenharmony_ci#define DEFAULT_IO_OUTPUT_CTRL_MSK GENMASK(6, 0) 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/* GENI_CGC_CTRL fields */ 15762306a36Sopenharmony_ci#define CFG_AHB_CLK_CGC_ON BIT(0) 15862306a36Sopenharmony_ci#define CFG_AHB_WR_ACLK_CGC_ON BIT(1) 15962306a36Sopenharmony_ci#define DATA_AHB_CLK_CGC_ON BIT(2) 16062306a36Sopenharmony_ci#define SCLK_CGC_ON BIT(3) 16162306a36Sopenharmony_ci#define TX_CLK_CGC_ON BIT(4) 16262306a36Sopenharmony_ci#define RX_CLK_CGC_ON BIT(5) 16362306a36Sopenharmony_ci#define EXT_CLK_CGC_ON BIT(6) 16462306a36Sopenharmony_ci#define PROG_RAM_HCLK_OFF BIT(8) 16562306a36Sopenharmony_ci#define PROG_RAM_SCLK_OFF BIT(9) 16662306a36Sopenharmony_ci#define DEFAULT_CGC_EN GENMASK(6, 0) 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/* SE_GSI_EVENT_EN fields */ 16962306a36Sopenharmony_ci#define DMA_RX_EVENT_EN BIT(0) 17062306a36Sopenharmony_ci#define DMA_TX_EVENT_EN BIT(1) 17162306a36Sopenharmony_ci#define GENI_M_EVENT_EN BIT(2) 17262306a36Sopenharmony_ci#define GENI_S_EVENT_EN BIT(3) 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/* SE_IRQ_EN fields */ 17562306a36Sopenharmony_ci#define DMA_RX_IRQ_EN BIT(0) 17662306a36Sopenharmony_ci#define DMA_TX_IRQ_EN BIT(1) 17762306a36Sopenharmony_ci#define GENI_M_IRQ_EN BIT(2) 17862306a36Sopenharmony_ci#define GENI_S_IRQ_EN BIT(3) 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci/* SE_DMA_GENERAL_CFG */ 18162306a36Sopenharmony_ci#define DMA_RX_CLK_CGC_ON BIT(0) 18262306a36Sopenharmony_ci#define DMA_TX_CLK_CGC_ON BIT(1) 18362306a36Sopenharmony_ci#define DMA_AHB_SLV_CFG_ON BIT(2) 18462306a36Sopenharmony_ci#define AHB_SEC_SLV_CLK_CGC_ON BIT(3) 18562306a36Sopenharmony_ci#define DUMMY_RX_NON_BUFFERABLE BIT(4) 18662306a36Sopenharmony_ci#define RX_DMA_ZERO_PADDING_EN BIT(5) 18762306a36Sopenharmony_ci#define RX_DMA_IRQ_DELAY_MSK GENMASK(8, 6) 18862306a36Sopenharmony_ci#define RX_DMA_IRQ_DELAY_SHFT 6 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/** 19162306a36Sopenharmony_ci * geni_se_get_qup_hw_version() - Read the QUP wrapper Hardware version 19262306a36Sopenharmony_ci * @se: Pointer to the corresponding serial engine. 19362306a36Sopenharmony_ci * 19462306a36Sopenharmony_ci * Return: Hardware Version of the wrapper. 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_ciu32 geni_se_get_qup_hw_version(struct geni_se *se) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct geni_wrapper *wrapper = se->wrapper; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci return readl_relaxed(wrapper->base + QUP_HW_VER_REG); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ciEXPORT_SYMBOL(geni_se_get_qup_hw_version); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic void geni_se_io_set_mode(void __iomem *base) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci u32 val; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci val = readl_relaxed(base + SE_IRQ_EN); 20962306a36Sopenharmony_ci val |= GENI_M_IRQ_EN | GENI_S_IRQ_EN; 21062306a36Sopenharmony_ci val |= DMA_TX_IRQ_EN | DMA_RX_IRQ_EN; 21162306a36Sopenharmony_ci writel_relaxed(val, base + SE_IRQ_EN); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci val = readl_relaxed(base + SE_GENI_DMA_MODE_EN); 21462306a36Sopenharmony_ci val &= ~GENI_DMA_MODE_EN; 21562306a36Sopenharmony_ci writel_relaxed(val, base + SE_GENI_DMA_MODE_EN); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci writel_relaxed(0, base + SE_GSI_EVENT_EN); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic void geni_se_io_init(void __iomem *base) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci u32 val; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci val = readl_relaxed(base + GENI_CGC_CTRL); 22562306a36Sopenharmony_ci val |= DEFAULT_CGC_EN; 22662306a36Sopenharmony_ci writel_relaxed(val, base + GENI_CGC_CTRL); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci val = readl_relaxed(base + SE_DMA_GENERAL_CFG); 22962306a36Sopenharmony_ci val |= AHB_SEC_SLV_CLK_CGC_ON | DMA_AHB_SLV_CFG_ON; 23062306a36Sopenharmony_ci val |= DMA_TX_CLK_CGC_ON | DMA_RX_CLK_CGC_ON; 23162306a36Sopenharmony_ci writel_relaxed(val, base + SE_DMA_GENERAL_CFG); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci writel_relaxed(DEFAULT_IO_OUTPUT_CTRL_MSK, base + GENI_OUTPUT_CTRL); 23462306a36Sopenharmony_ci writel_relaxed(FORCE_DEFAULT, base + GENI_FORCE_DEFAULT_REG); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic void geni_se_irq_clear(struct geni_se *se) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci writel_relaxed(0, se->base + SE_GSI_EVENT_EN); 24062306a36Sopenharmony_ci writel_relaxed(0xffffffff, se->base + SE_GENI_M_IRQ_CLEAR); 24162306a36Sopenharmony_ci writel_relaxed(0xffffffff, se->base + SE_GENI_S_IRQ_CLEAR); 24262306a36Sopenharmony_ci writel_relaxed(0xffffffff, se->base + SE_DMA_TX_IRQ_CLR); 24362306a36Sopenharmony_ci writel_relaxed(0xffffffff, se->base + SE_DMA_RX_IRQ_CLR); 24462306a36Sopenharmony_ci writel_relaxed(0xffffffff, se->base + SE_IRQ_EN); 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci/** 24862306a36Sopenharmony_ci * geni_se_init() - Initialize the GENI serial engine 24962306a36Sopenharmony_ci * @se: Pointer to the concerned serial engine. 25062306a36Sopenharmony_ci * @rx_wm: Receive watermark, in units of FIFO words. 25162306a36Sopenharmony_ci * @rx_rfr: Ready-for-receive watermark, in units of FIFO words. 25262306a36Sopenharmony_ci * 25362306a36Sopenharmony_ci * This function is used to initialize the GENI serial engine, configure 25462306a36Sopenharmony_ci * receive watermark and ready-for-receive watermarks. 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_civoid geni_se_init(struct geni_se *se, u32 rx_wm, u32 rx_rfr) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci u32 val; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci geni_se_irq_clear(se); 26162306a36Sopenharmony_ci geni_se_io_init(se->base); 26262306a36Sopenharmony_ci geni_se_io_set_mode(se->base); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci writel_relaxed(rx_wm, se->base + SE_GENI_RX_WATERMARK_REG); 26562306a36Sopenharmony_ci writel_relaxed(rx_rfr, se->base + SE_GENI_RX_RFR_WATERMARK_REG); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci val = readl_relaxed(se->base + SE_GENI_M_IRQ_EN); 26862306a36Sopenharmony_ci val |= M_COMMON_GENI_M_IRQ_EN; 26962306a36Sopenharmony_ci writel_relaxed(val, se->base + SE_GENI_M_IRQ_EN); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci val = readl_relaxed(se->base + SE_GENI_S_IRQ_EN); 27262306a36Sopenharmony_ci val |= S_COMMON_GENI_S_IRQ_EN; 27362306a36Sopenharmony_ci writel_relaxed(val, se->base + SE_GENI_S_IRQ_EN); 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ciEXPORT_SYMBOL(geni_se_init); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic void geni_se_select_fifo_mode(struct geni_se *se) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci u32 proto = geni_se_read_proto(se); 28062306a36Sopenharmony_ci u32 val, val_old; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci geni_se_irq_clear(se); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* UART driver manages enabling / disabling interrupts internally */ 28562306a36Sopenharmony_ci if (proto != GENI_SE_UART) { 28662306a36Sopenharmony_ci /* Non-UART use only primary sequencer so dont bother about S_IRQ */ 28762306a36Sopenharmony_ci val_old = val = readl_relaxed(se->base + SE_GENI_M_IRQ_EN); 28862306a36Sopenharmony_ci val |= M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN; 28962306a36Sopenharmony_ci val |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN; 29062306a36Sopenharmony_ci if (val != val_old) 29162306a36Sopenharmony_ci writel_relaxed(val, se->base + SE_GENI_M_IRQ_EN); 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci val_old = val = readl_relaxed(se->base + SE_GENI_DMA_MODE_EN); 29562306a36Sopenharmony_ci val &= ~GENI_DMA_MODE_EN; 29662306a36Sopenharmony_ci if (val != val_old) 29762306a36Sopenharmony_ci writel_relaxed(val, se->base + SE_GENI_DMA_MODE_EN); 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic void geni_se_select_dma_mode(struct geni_se *se) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci u32 proto = geni_se_read_proto(se); 30362306a36Sopenharmony_ci u32 val, val_old; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci geni_se_irq_clear(se); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* UART driver manages enabling / disabling interrupts internally */ 30862306a36Sopenharmony_ci if (proto != GENI_SE_UART) { 30962306a36Sopenharmony_ci /* Non-UART use only primary sequencer so dont bother about S_IRQ */ 31062306a36Sopenharmony_ci val_old = val = readl_relaxed(se->base + SE_GENI_M_IRQ_EN); 31162306a36Sopenharmony_ci val &= ~(M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN); 31262306a36Sopenharmony_ci val &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN); 31362306a36Sopenharmony_ci if (val != val_old) 31462306a36Sopenharmony_ci writel_relaxed(val, se->base + SE_GENI_M_IRQ_EN); 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci val_old = val = readl_relaxed(se->base + SE_GENI_DMA_MODE_EN); 31862306a36Sopenharmony_ci val |= GENI_DMA_MODE_EN; 31962306a36Sopenharmony_ci if (val != val_old) 32062306a36Sopenharmony_ci writel_relaxed(val, se->base + SE_GENI_DMA_MODE_EN); 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic void geni_se_select_gpi_mode(struct geni_se *se) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci u32 val; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci geni_se_irq_clear(se); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci writel(0, se->base + SE_IRQ_EN); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci val = readl(se->base + SE_GENI_M_IRQ_EN); 33262306a36Sopenharmony_ci val &= ~(M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN | 33362306a36Sopenharmony_ci M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN); 33462306a36Sopenharmony_ci writel(val, se->base + SE_GENI_M_IRQ_EN); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci writel(GENI_DMA_MODE_EN, se->base + SE_GENI_DMA_MODE_EN); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci val = readl(se->base + SE_GSI_EVENT_EN); 33962306a36Sopenharmony_ci val |= (DMA_RX_EVENT_EN | DMA_TX_EVENT_EN | GENI_M_EVENT_EN | GENI_S_EVENT_EN); 34062306a36Sopenharmony_ci writel(val, se->base + SE_GSI_EVENT_EN); 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci/** 34462306a36Sopenharmony_ci * geni_se_select_mode() - Select the serial engine transfer mode 34562306a36Sopenharmony_ci * @se: Pointer to the concerned serial engine. 34662306a36Sopenharmony_ci * @mode: Transfer mode to be selected. 34762306a36Sopenharmony_ci */ 34862306a36Sopenharmony_civoid geni_se_select_mode(struct geni_se *se, enum geni_se_xfer_mode mode) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci WARN_ON(mode != GENI_SE_FIFO && mode != GENI_SE_DMA && mode != GENI_GPI_DMA); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci switch (mode) { 35362306a36Sopenharmony_ci case GENI_SE_FIFO: 35462306a36Sopenharmony_ci geni_se_select_fifo_mode(se); 35562306a36Sopenharmony_ci break; 35662306a36Sopenharmony_ci case GENI_SE_DMA: 35762306a36Sopenharmony_ci geni_se_select_dma_mode(se); 35862306a36Sopenharmony_ci break; 35962306a36Sopenharmony_ci case GENI_GPI_DMA: 36062306a36Sopenharmony_ci geni_se_select_gpi_mode(se); 36162306a36Sopenharmony_ci break; 36262306a36Sopenharmony_ci case GENI_SE_INVALID: 36362306a36Sopenharmony_ci default: 36462306a36Sopenharmony_ci break; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ciEXPORT_SYMBOL(geni_se_select_mode); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci/** 37062306a36Sopenharmony_ci * DOC: Overview 37162306a36Sopenharmony_ci * 37262306a36Sopenharmony_ci * GENI FIFO packing is highly configurable. TX/RX packing/unpacking consist 37362306a36Sopenharmony_ci * of up to 4 operations, each operation represented by 4 configuration vectors 37462306a36Sopenharmony_ci * of 10 bits programmed in GENI_TX_PACKING_CFG0 and GENI_TX_PACKING_CFG1 for 37562306a36Sopenharmony_ci * TX FIFO and in GENI_RX_PACKING_CFG0 and GENI_RX_PACKING_CFG1 for RX FIFO. 37662306a36Sopenharmony_ci * Refer to below examples for detailed bit-field description. 37762306a36Sopenharmony_ci * 37862306a36Sopenharmony_ci * Example 1: word_size = 7, packing_mode = 4 x 8, msb_to_lsb = 1 37962306a36Sopenharmony_ci * 38062306a36Sopenharmony_ci * +-----------+-------+-------+-------+-------+ 38162306a36Sopenharmony_ci * | | vec_0 | vec_1 | vec_2 | vec_3 | 38262306a36Sopenharmony_ci * +-----------+-------+-------+-------+-------+ 38362306a36Sopenharmony_ci * | start | 0x6 | 0xe | 0x16 | 0x1e | 38462306a36Sopenharmony_ci * | direction | 1 | 1 | 1 | 1 | 38562306a36Sopenharmony_ci * | length | 6 | 6 | 6 | 6 | 38662306a36Sopenharmony_ci * | stop | 0 | 0 | 0 | 1 | 38762306a36Sopenharmony_ci * +-----------+-------+-------+-------+-------+ 38862306a36Sopenharmony_ci * 38962306a36Sopenharmony_ci * Example 2: word_size = 15, packing_mode = 2 x 16, msb_to_lsb = 0 39062306a36Sopenharmony_ci * 39162306a36Sopenharmony_ci * +-----------+-------+-------+-------+-------+ 39262306a36Sopenharmony_ci * | | vec_0 | vec_1 | vec_2 | vec_3 | 39362306a36Sopenharmony_ci * +-----------+-------+-------+-------+-------+ 39462306a36Sopenharmony_ci * | start | 0x0 | 0x8 | 0x10 | 0x18 | 39562306a36Sopenharmony_ci * | direction | 0 | 0 | 0 | 0 | 39662306a36Sopenharmony_ci * | length | 7 | 6 | 7 | 6 | 39762306a36Sopenharmony_ci * | stop | 0 | 0 | 0 | 1 | 39862306a36Sopenharmony_ci * +-----------+-------+-------+-------+-------+ 39962306a36Sopenharmony_ci * 40062306a36Sopenharmony_ci * Example 3: word_size = 23, packing_mode = 1 x 32, msb_to_lsb = 1 40162306a36Sopenharmony_ci * 40262306a36Sopenharmony_ci * +-----------+-------+-------+-------+-------+ 40362306a36Sopenharmony_ci * | | vec_0 | vec_1 | vec_2 | vec_3 | 40462306a36Sopenharmony_ci * +-----------+-------+-------+-------+-------+ 40562306a36Sopenharmony_ci * | start | 0x16 | 0xe | 0x6 | 0x0 | 40662306a36Sopenharmony_ci * | direction | 1 | 1 | 1 | 1 | 40762306a36Sopenharmony_ci * | length | 7 | 7 | 6 | 0 | 40862306a36Sopenharmony_ci * | stop | 0 | 0 | 1 | 0 | 40962306a36Sopenharmony_ci * +-----------+-------+-------+-------+-------+ 41062306a36Sopenharmony_ci * 41162306a36Sopenharmony_ci */ 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci#define NUM_PACKING_VECTORS 4 41462306a36Sopenharmony_ci#define PACKING_START_SHIFT 5 41562306a36Sopenharmony_ci#define PACKING_DIR_SHIFT 4 41662306a36Sopenharmony_ci#define PACKING_LEN_SHIFT 1 41762306a36Sopenharmony_ci#define PACKING_STOP_BIT BIT(0) 41862306a36Sopenharmony_ci#define PACKING_VECTOR_SHIFT 10 41962306a36Sopenharmony_ci/** 42062306a36Sopenharmony_ci * geni_se_config_packing() - Packing configuration of the serial engine 42162306a36Sopenharmony_ci * @se: Pointer to the concerned serial engine 42262306a36Sopenharmony_ci * @bpw: Bits of data per transfer word. 42362306a36Sopenharmony_ci * @pack_words: Number of words per fifo element. 42462306a36Sopenharmony_ci * @msb_to_lsb: Transfer from MSB to LSB or vice-versa. 42562306a36Sopenharmony_ci * @tx_cfg: Flag to configure the TX Packing. 42662306a36Sopenharmony_ci * @rx_cfg: Flag to configure the RX Packing. 42762306a36Sopenharmony_ci * 42862306a36Sopenharmony_ci * This function is used to configure the packing rules for the current 42962306a36Sopenharmony_ci * transfer. 43062306a36Sopenharmony_ci */ 43162306a36Sopenharmony_civoid geni_se_config_packing(struct geni_se *se, int bpw, int pack_words, 43262306a36Sopenharmony_ci bool msb_to_lsb, bool tx_cfg, bool rx_cfg) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci u32 cfg0, cfg1, cfg[NUM_PACKING_VECTORS] = {0}; 43562306a36Sopenharmony_ci int len; 43662306a36Sopenharmony_ci int temp_bpw = bpw; 43762306a36Sopenharmony_ci int idx_start = msb_to_lsb ? bpw - 1 : 0; 43862306a36Sopenharmony_ci int idx = idx_start; 43962306a36Sopenharmony_ci int idx_delta = msb_to_lsb ? -BITS_PER_BYTE : BITS_PER_BYTE; 44062306a36Sopenharmony_ci int ceil_bpw = ALIGN(bpw, BITS_PER_BYTE); 44162306a36Sopenharmony_ci int iter = (ceil_bpw * pack_words) / BITS_PER_BYTE; 44262306a36Sopenharmony_ci int i; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (iter <= 0 || iter > NUM_PACKING_VECTORS) 44562306a36Sopenharmony_ci return; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci for (i = 0; i < iter; i++) { 44862306a36Sopenharmony_ci len = min_t(int, temp_bpw, BITS_PER_BYTE) - 1; 44962306a36Sopenharmony_ci cfg[i] = idx << PACKING_START_SHIFT; 45062306a36Sopenharmony_ci cfg[i] |= msb_to_lsb << PACKING_DIR_SHIFT; 45162306a36Sopenharmony_ci cfg[i] |= len << PACKING_LEN_SHIFT; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (temp_bpw <= BITS_PER_BYTE) { 45462306a36Sopenharmony_ci idx = ((i + 1) * BITS_PER_BYTE) + idx_start; 45562306a36Sopenharmony_ci temp_bpw = bpw; 45662306a36Sopenharmony_ci } else { 45762306a36Sopenharmony_ci idx = idx + idx_delta; 45862306a36Sopenharmony_ci temp_bpw = temp_bpw - BITS_PER_BYTE; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci cfg[iter - 1] |= PACKING_STOP_BIT; 46262306a36Sopenharmony_ci cfg0 = cfg[0] | (cfg[1] << PACKING_VECTOR_SHIFT); 46362306a36Sopenharmony_ci cfg1 = cfg[2] | (cfg[3] << PACKING_VECTOR_SHIFT); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (tx_cfg) { 46662306a36Sopenharmony_ci writel_relaxed(cfg0, se->base + SE_GENI_TX_PACKING_CFG0); 46762306a36Sopenharmony_ci writel_relaxed(cfg1, se->base + SE_GENI_TX_PACKING_CFG1); 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci if (rx_cfg) { 47062306a36Sopenharmony_ci writel_relaxed(cfg0, se->base + SE_GENI_RX_PACKING_CFG0); 47162306a36Sopenharmony_ci writel_relaxed(cfg1, se->base + SE_GENI_RX_PACKING_CFG1); 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci /* 47562306a36Sopenharmony_ci * Number of protocol words in each FIFO entry 47662306a36Sopenharmony_ci * 0 - 4x8, four words in each entry, max word size of 8 bits 47762306a36Sopenharmony_ci * 1 - 2x16, two words in each entry, max word size of 16 bits 47862306a36Sopenharmony_ci * 2 - 1x32, one word in each entry, max word size of 32 bits 47962306a36Sopenharmony_ci * 3 - undefined 48062306a36Sopenharmony_ci */ 48162306a36Sopenharmony_ci if (pack_words || bpw == 32) 48262306a36Sopenharmony_ci writel_relaxed(bpw / 16, se->base + SE_GENI_BYTE_GRAN); 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ciEXPORT_SYMBOL(geni_se_config_packing); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cistatic void geni_se_clks_off(struct geni_se *se) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci struct geni_wrapper *wrapper = se->wrapper; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci clk_disable_unprepare(se->clk); 49162306a36Sopenharmony_ci clk_bulk_disable_unprepare(wrapper->num_clks, wrapper->clks); 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci/** 49562306a36Sopenharmony_ci * geni_se_resources_off() - Turn off resources associated with the serial 49662306a36Sopenharmony_ci * engine 49762306a36Sopenharmony_ci * @se: Pointer to the concerned serial engine. 49862306a36Sopenharmony_ci * 49962306a36Sopenharmony_ci * Return: 0 on success, standard Linux error codes on failure/error. 50062306a36Sopenharmony_ci */ 50162306a36Sopenharmony_ciint geni_se_resources_off(struct geni_se *se) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci int ret; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (has_acpi_companion(se->dev)) 50662306a36Sopenharmony_ci return 0; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci ret = pinctrl_pm_select_sleep_state(se->dev); 50962306a36Sopenharmony_ci if (ret) 51062306a36Sopenharmony_ci return ret; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci geni_se_clks_off(se); 51362306a36Sopenharmony_ci return 0; 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ciEXPORT_SYMBOL(geni_se_resources_off); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic int geni_se_clks_on(struct geni_se *se) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci int ret; 52062306a36Sopenharmony_ci struct geni_wrapper *wrapper = se->wrapper; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci ret = clk_bulk_prepare_enable(wrapper->num_clks, wrapper->clks); 52362306a36Sopenharmony_ci if (ret) 52462306a36Sopenharmony_ci return ret; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci ret = clk_prepare_enable(se->clk); 52762306a36Sopenharmony_ci if (ret) 52862306a36Sopenharmony_ci clk_bulk_disable_unprepare(wrapper->num_clks, wrapper->clks); 52962306a36Sopenharmony_ci return ret; 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci/** 53362306a36Sopenharmony_ci * geni_se_resources_on() - Turn on resources associated with the serial 53462306a36Sopenharmony_ci * engine 53562306a36Sopenharmony_ci * @se: Pointer to the concerned serial engine. 53662306a36Sopenharmony_ci * 53762306a36Sopenharmony_ci * Return: 0 on success, standard Linux error codes on failure/error. 53862306a36Sopenharmony_ci */ 53962306a36Sopenharmony_ciint geni_se_resources_on(struct geni_se *se) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci int ret; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (has_acpi_companion(se->dev)) 54462306a36Sopenharmony_ci return 0; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci ret = geni_se_clks_on(se); 54762306a36Sopenharmony_ci if (ret) 54862306a36Sopenharmony_ci return ret; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci ret = pinctrl_pm_select_default_state(se->dev); 55162306a36Sopenharmony_ci if (ret) 55262306a36Sopenharmony_ci geni_se_clks_off(se); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci return ret; 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ciEXPORT_SYMBOL(geni_se_resources_on); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci/** 55962306a36Sopenharmony_ci * geni_se_clk_tbl_get() - Get the clock table to program DFS 56062306a36Sopenharmony_ci * @se: Pointer to the concerned serial engine. 56162306a36Sopenharmony_ci * @tbl: Table in which the output is returned. 56262306a36Sopenharmony_ci * 56362306a36Sopenharmony_ci * This function is called by the protocol drivers to determine the different 56462306a36Sopenharmony_ci * clock frequencies supported by serial engine core clock. The protocol 56562306a36Sopenharmony_ci * drivers use the output to determine the clock frequency index to be 56662306a36Sopenharmony_ci * programmed into DFS. 56762306a36Sopenharmony_ci * 56862306a36Sopenharmony_ci * Return: number of valid performance levels in the table on success, 56962306a36Sopenharmony_ci * standard Linux error codes on failure. 57062306a36Sopenharmony_ci */ 57162306a36Sopenharmony_ciint geni_se_clk_tbl_get(struct geni_se *se, unsigned long **tbl) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci long freq = 0; 57462306a36Sopenharmony_ci int i; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if (se->clk_perf_tbl) { 57762306a36Sopenharmony_ci *tbl = se->clk_perf_tbl; 57862306a36Sopenharmony_ci return se->num_clk_levels; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci se->clk_perf_tbl = devm_kcalloc(se->dev, MAX_CLK_PERF_LEVEL, 58262306a36Sopenharmony_ci sizeof(*se->clk_perf_tbl), 58362306a36Sopenharmony_ci GFP_KERNEL); 58462306a36Sopenharmony_ci if (!se->clk_perf_tbl) 58562306a36Sopenharmony_ci return -ENOMEM; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci for (i = 0; i < MAX_CLK_PERF_LEVEL; i++) { 58862306a36Sopenharmony_ci freq = clk_round_rate(se->clk, freq + 1); 58962306a36Sopenharmony_ci if (freq <= 0 || freq == se->clk_perf_tbl[i - 1]) 59062306a36Sopenharmony_ci break; 59162306a36Sopenharmony_ci se->clk_perf_tbl[i] = freq; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci se->num_clk_levels = i; 59462306a36Sopenharmony_ci *tbl = se->clk_perf_tbl; 59562306a36Sopenharmony_ci return se->num_clk_levels; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ciEXPORT_SYMBOL(geni_se_clk_tbl_get); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci/** 60062306a36Sopenharmony_ci * geni_se_clk_freq_match() - Get the matching or closest SE clock frequency 60162306a36Sopenharmony_ci * @se: Pointer to the concerned serial engine. 60262306a36Sopenharmony_ci * @req_freq: Requested clock frequency. 60362306a36Sopenharmony_ci * @index: Index of the resultant frequency in the table. 60462306a36Sopenharmony_ci * @res_freq: Resultant frequency of the source clock. 60562306a36Sopenharmony_ci * @exact: Flag to indicate exact multiple requirement of the requested 60662306a36Sopenharmony_ci * frequency. 60762306a36Sopenharmony_ci * 60862306a36Sopenharmony_ci * This function is called by the protocol drivers to determine the best match 60962306a36Sopenharmony_ci * of the requested frequency as provided by the serial engine clock in order 61062306a36Sopenharmony_ci * to meet the performance requirements. 61162306a36Sopenharmony_ci * 61262306a36Sopenharmony_ci * If we return success: 61362306a36Sopenharmony_ci * - if @exact is true then @res_freq / <an_integer> == @req_freq 61462306a36Sopenharmony_ci * - if @exact is false then @res_freq / <an_integer> <= @req_freq 61562306a36Sopenharmony_ci * 61662306a36Sopenharmony_ci * Return: 0 on success, standard Linux error codes on failure. 61762306a36Sopenharmony_ci */ 61862306a36Sopenharmony_ciint geni_se_clk_freq_match(struct geni_se *se, unsigned long req_freq, 61962306a36Sopenharmony_ci unsigned int *index, unsigned long *res_freq, 62062306a36Sopenharmony_ci bool exact) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci unsigned long *tbl; 62362306a36Sopenharmony_ci int num_clk_levels; 62462306a36Sopenharmony_ci int i; 62562306a36Sopenharmony_ci unsigned long best_delta; 62662306a36Sopenharmony_ci unsigned long new_delta; 62762306a36Sopenharmony_ci unsigned int divider; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci num_clk_levels = geni_se_clk_tbl_get(se, &tbl); 63062306a36Sopenharmony_ci if (num_clk_levels < 0) 63162306a36Sopenharmony_ci return num_clk_levels; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci if (num_clk_levels == 0) 63462306a36Sopenharmony_ci return -EINVAL; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci best_delta = ULONG_MAX; 63762306a36Sopenharmony_ci for (i = 0; i < num_clk_levels; i++) { 63862306a36Sopenharmony_ci divider = DIV_ROUND_UP(tbl[i], req_freq); 63962306a36Sopenharmony_ci new_delta = req_freq - tbl[i] / divider; 64062306a36Sopenharmony_ci if (new_delta < best_delta) { 64162306a36Sopenharmony_ci /* We have a new best! */ 64262306a36Sopenharmony_ci *index = i; 64362306a36Sopenharmony_ci *res_freq = tbl[i]; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci /* If the new best is exact then we're done */ 64662306a36Sopenharmony_ci if (new_delta == 0) 64762306a36Sopenharmony_ci return 0; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci /* Record how close we got */ 65062306a36Sopenharmony_ci best_delta = new_delta; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (exact) 65562306a36Sopenharmony_ci return -EINVAL; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci return 0; 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ciEXPORT_SYMBOL(geni_se_clk_freq_match); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci#define GENI_SE_DMA_DONE_EN BIT(0) 66262306a36Sopenharmony_ci#define GENI_SE_DMA_EOT_EN BIT(1) 66362306a36Sopenharmony_ci#define GENI_SE_DMA_AHB_ERR_EN BIT(2) 66462306a36Sopenharmony_ci#define GENI_SE_DMA_EOT_BUF BIT(0) 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci/** 66762306a36Sopenharmony_ci * geni_se_tx_init_dma() - Initiate TX DMA transfer on the serial engine 66862306a36Sopenharmony_ci * @se: Pointer to the concerned serial engine. 66962306a36Sopenharmony_ci * @iova: Mapped DMA address. 67062306a36Sopenharmony_ci * @len: Length of the TX buffer. 67162306a36Sopenharmony_ci * 67262306a36Sopenharmony_ci * This function is used to initiate DMA TX transfer. 67362306a36Sopenharmony_ci */ 67462306a36Sopenharmony_civoid geni_se_tx_init_dma(struct geni_se *se, dma_addr_t iova, size_t len) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci u32 val; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci val = GENI_SE_DMA_DONE_EN; 67962306a36Sopenharmony_ci val |= GENI_SE_DMA_EOT_EN; 68062306a36Sopenharmony_ci val |= GENI_SE_DMA_AHB_ERR_EN; 68162306a36Sopenharmony_ci writel_relaxed(val, se->base + SE_DMA_TX_IRQ_EN_SET); 68262306a36Sopenharmony_ci writel_relaxed(lower_32_bits(iova), se->base + SE_DMA_TX_PTR_L); 68362306a36Sopenharmony_ci writel_relaxed(upper_32_bits(iova), se->base + SE_DMA_TX_PTR_H); 68462306a36Sopenharmony_ci writel_relaxed(GENI_SE_DMA_EOT_BUF, se->base + SE_DMA_TX_ATTR); 68562306a36Sopenharmony_ci writel(len, se->base + SE_DMA_TX_LEN); 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ciEXPORT_SYMBOL(geni_se_tx_init_dma); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci/** 69062306a36Sopenharmony_ci * geni_se_tx_dma_prep() - Prepare the serial engine for TX DMA transfer 69162306a36Sopenharmony_ci * @se: Pointer to the concerned serial engine. 69262306a36Sopenharmony_ci * @buf: Pointer to the TX buffer. 69362306a36Sopenharmony_ci * @len: Length of the TX buffer. 69462306a36Sopenharmony_ci * @iova: Pointer to store the mapped DMA address. 69562306a36Sopenharmony_ci * 69662306a36Sopenharmony_ci * This function is used to prepare the buffers for DMA TX. 69762306a36Sopenharmony_ci * 69862306a36Sopenharmony_ci * Return: 0 on success, standard Linux error codes on failure. 69962306a36Sopenharmony_ci */ 70062306a36Sopenharmony_ciint geni_se_tx_dma_prep(struct geni_se *se, void *buf, size_t len, 70162306a36Sopenharmony_ci dma_addr_t *iova) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci struct geni_wrapper *wrapper = se->wrapper; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci if (!wrapper) 70662306a36Sopenharmony_ci return -EINVAL; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci *iova = dma_map_single(wrapper->dev, buf, len, DMA_TO_DEVICE); 70962306a36Sopenharmony_ci if (dma_mapping_error(wrapper->dev, *iova)) 71062306a36Sopenharmony_ci return -EIO; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci geni_se_tx_init_dma(se, *iova, len); 71362306a36Sopenharmony_ci return 0; 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ciEXPORT_SYMBOL(geni_se_tx_dma_prep); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci/** 71862306a36Sopenharmony_ci * geni_se_rx_init_dma() - Initiate RX DMA transfer on the serial engine 71962306a36Sopenharmony_ci * @se: Pointer to the concerned serial engine. 72062306a36Sopenharmony_ci * @iova: Mapped DMA address. 72162306a36Sopenharmony_ci * @len: Length of the RX buffer. 72262306a36Sopenharmony_ci * 72362306a36Sopenharmony_ci * This function is used to initiate DMA RX transfer. 72462306a36Sopenharmony_ci */ 72562306a36Sopenharmony_civoid geni_se_rx_init_dma(struct geni_se *se, dma_addr_t iova, size_t len) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci u32 val; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci val = GENI_SE_DMA_DONE_EN; 73062306a36Sopenharmony_ci val |= GENI_SE_DMA_EOT_EN; 73162306a36Sopenharmony_ci val |= GENI_SE_DMA_AHB_ERR_EN; 73262306a36Sopenharmony_ci writel_relaxed(val, se->base + SE_DMA_RX_IRQ_EN_SET); 73362306a36Sopenharmony_ci writel_relaxed(lower_32_bits(iova), se->base + SE_DMA_RX_PTR_L); 73462306a36Sopenharmony_ci writel_relaxed(upper_32_bits(iova), se->base + SE_DMA_RX_PTR_H); 73562306a36Sopenharmony_ci /* RX does not have EOT buffer type bit. So just reset RX_ATTR */ 73662306a36Sopenharmony_ci writel_relaxed(0, se->base + SE_DMA_RX_ATTR); 73762306a36Sopenharmony_ci writel(len, se->base + SE_DMA_RX_LEN); 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ciEXPORT_SYMBOL(geni_se_rx_init_dma); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci/** 74262306a36Sopenharmony_ci * geni_se_rx_dma_prep() - Prepare the serial engine for RX DMA transfer 74362306a36Sopenharmony_ci * @se: Pointer to the concerned serial engine. 74462306a36Sopenharmony_ci * @buf: Pointer to the RX buffer. 74562306a36Sopenharmony_ci * @len: Length of the RX buffer. 74662306a36Sopenharmony_ci * @iova: Pointer to store the mapped DMA address. 74762306a36Sopenharmony_ci * 74862306a36Sopenharmony_ci * This function is used to prepare the buffers for DMA RX. 74962306a36Sopenharmony_ci * 75062306a36Sopenharmony_ci * Return: 0 on success, standard Linux error codes on failure. 75162306a36Sopenharmony_ci */ 75262306a36Sopenharmony_ciint geni_se_rx_dma_prep(struct geni_se *se, void *buf, size_t len, 75362306a36Sopenharmony_ci dma_addr_t *iova) 75462306a36Sopenharmony_ci{ 75562306a36Sopenharmony_ci struct geni_wrapper *wrapper = se->wrapper; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci if (!wrapper) 75862306a36Sopenharmony_ci return -EINVAL; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci *iova = dma_map_single(wrapper->dev, buf, len, DMA_FROM_DEVICE); 76162306a36Sopenharmony_ci if (dma_mapping_error(wrapper->dev, *iova)) 76262306a36Sopenharmony_ci return -EIO; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci geni_se_rx_init_dma(se, *iova, len); 76562306a36Sopenharmony_ci return 0; 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ciEXPORT_SYMBOL(geni_se_rx_dma_prep); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci/** 77062306a36Sopenharmony_ci * geni_se_tx_dma_unprep() - Unprepare the serial engine after TX DMA transfer 77162306a36Sopenharmony_ci * @se: Pointer to the concerned serial engine. 77262306a36Sopenharmony_ci * @iova: DMA address of the TX buffer. 77362306a36Sopenharmony_ci * @len: Length of the TX buffer. 77462306a36Sopenharmony_ci * 77562306a36Sopenharmony_ci * This function is used to unprepare the DMA buffers after DMA TX. 77662306a36Sopenharmony_ci */ 77762306a36Sopenharmony_civoid geni_se_tx_dma_unprep(struct geni_se *se, dma_addr_t iova, size_t len) 77862306a36Sopenharmony_ci{ 77962306a36Sopenharmony_ci struct geni_wrapper *wrapper = se->wrapper; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci if (!dma_mapping_error(wrapper->dev, iova)) 78262306a36Sopenharmony_ci dma_unmap_single(wrapper->dev, iova, len, DMA_TO_DEVICE); 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ciEXPORT_SYMBOL(geni_se_tx_dma_unprep); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci/** 78762306a36Sopenharmony_ci * geni_se_rx_dma_unprep() - Unprepare the serial engine after RX DMA transfer 78862306a36Sopenharmony_ci * @se: Pointer to the concerned serial engine. 78962306a36Sopenharmony_ci * @iova: DMA address of the RX buffer. 79062306a36Sopenharmony_ci * @len: Length of the RX buffer. 79162306a36Sopenharmony_ci * 79262306a36Sopenharmony_ci * This function is used to unprepare the DMA buffers after DMA RX. 79362306a36Sopenharmony_ci */ 79462306a36Sopenharmony_civoid geni_se_rx_dma_unprep(struct geni_se *se, dma_addr_t iova, size_t len) 79562306a36Sopenharmony_ci{ 79662306a36Sopenharmony_ci struct geni_wrapper *wrapper = se->wrapper; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci if (!dma_mapping_error(wrapper->dev, iova)) 79962306a36Sopenharmony_ci dma_unmap_single(wrapper->dev, iova, len, DMA_FROM_DEVICE); 80062306a36Sopenharmony_ci} 80162306a36Sopenharmony_ciEXPORT_SYMBOL(geni_se_rx_dma_unprep); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ciint geni_icc_get(struct geni_se *se, const char *icc_ddr) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci int i, err; 80662306a36Sopenharmony_ci const char *icc_names[] = {"qup-core", "qup-config", icc_ddr}; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci if (has_acpi_companion(se->dev)) 80962306a36Sopenharmony_ci return 0; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(se->icc_paths); i++) { 81262306a36Sopenharmony_ci if (!icc_names[i]) 81362306a36Sopenharmony_ci continue; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci se->icc_paths[i].path = devm_of_icc_get(se->dev, icc_names[i]); 81662306a36Sopenharmony_ci if (IS_ERR(se->icc_paths[i].path)) 81762306a36Sopenharmony_ci goto err; 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci return 0; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_cierr: 82362306a36Sopenharmony_ci err = PTR_ERR(se->icc_paths[i].path); 82462306a36Sopenharmony_ci if (err != -EPROBE_DEFER) 82562306a36Sopenharmony_ci dev_err_ratelimited(se->dev, "Failed to get ICC path '%s': %d\n", 82662306a36Sopenharmony_ci icc_names[i], err); 82762306a36Sopenharmony_ci return err; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci} 83062306a36Sopenharmony_ciEXPORT_SYMBOL(geni_icc_get); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ciint geni_icc_set_bw(struct geni_se *se) 83362306a36Sopenharmony_ci{ 83462306a36Sopenharmony_ci int i, ret; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(se->icc_paths); i++) { 83762306a36Sopenharmony_ci ret = icc_set_bw(se->icc_paths[i].path, 83862306a36Sopenharmony_ci se->icc_paths[i].avg_bw, se->icc_paths[i].avg_bw); 83962306a36Sopenharmony_ci if (ret) { 84062306a36Sopenharmony_ci dev_err_ratelimited(se->dev, "ICC BW voting failed on path '%s': %d\n", 84162306a36Sopenharmony_ci icc_path_names[i], ret); 84262306a36Sopenharmony_ci return ret; 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci return 0; 84762306a36Sopenharmony_ci} 84862306a36Sopenharmony_ciEXPORT_SYMBOL(geni_icc_set_bw); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_civoid geni_icc_set_tag(struct geni_se *se, u32 tag) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci int i; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(se->icc_paths); i++) 85562306a36Sopenharmony_ci icc_set_tag(se->icc_paths[i].path, tag); 85662306a36Sopenharmony_ci} 85762306a36Sopenharmony_ciEXPORT_SYMBOL(geni_icc_set_tag); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci/* To do: Replace this by icc_bulk_enable once it's implemented in ICC core */ 86062306a36Sopenharmony_ciint geni_icc_enable(struct geni_se *se) 86162306a36Sopenharmony_ci{ 86262306a36Sopenharmony_ci int i, ret; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(se->icc_paths); i++) { 86562306a36Sopenharmony_ci ret = icc_enable(se->icc_paths[i].path); 86662306a36Sopenharmony_ci if (ret) { 86762306a36Sopenharmony_ci dev_err_ratelimited(se->dev, "ICC enable failed on path '%s': %d\n", 86862306a36Sopenharmony_ci icc_path_names[i], ret); 86962306a36Sopenharmony_ci return ret; 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci return 0; 87462306a36Sopenharmony_ci} 87562306a36Sopenharmony_ciEXPORT_SYMBOL(geni_icc_enable); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ciint geni_icc_disable(struct geni_se *se) 87862306a36Sopenharmony_ci{ 87962306a36Sopenharmony_ci int i, ret; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(se->icc_paths); i++) { 88262306a36Sopenharmony_ci ret = icc_disable(se->icc_paths[i].path); 88362306a36Sopenharmony_ci if (ret) { 88462306a36Sopenharmony_ci dev_err_ratelimited(se->dev, "ICC disable failed on path '%s': %d\n", 88562306a36Sopenharmony_ci icc_path_names[i], ret); 88662306a36Sopenharmony_ci return ret; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci return 0; 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ciEXPORT_SYMBOL(geni_icc_disable); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_cistatic int geni_se_probe(struct platform_device *pdev) 89562306a36Sopenharmony_ci{ 89662306a36Sopenharmony_ci struct device *dev = &pdev->dev; 89762306a36Sopenharmony_ci struct geni_wrapper *wrapper; 89862306a36Sopenharmony_ci int ret; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci wrapper = devm_kzalloc(dev, sizeof(*wrapper), GFP_KERNEL); 90162306a36Sopenharmony_ci if (!wrapper) 90262306a36Sopenharmony_ci return -ENOMEM; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci wrapper->dev = dev; 90562306a36Sopenharmony_ci wrapper->base = devm_platform_ioremap_resource(pdev, 0); 90662306a36Sopenharmony_ci if (IS_ERR(wrapper->base)) 90762306a36Sopenharmony_ci return PTR_ERR(wrapper->base); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci if (!has_acpi_companion(&pdev->dev)) { 91062306a36Sopenharmony_ci const struct geni_se_desc *desc; 91162306a36Sopenharmony_ci int i; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci desc = device_get_match_data(&pdev->dev); 91462306a36Sopenharmony_ci if (!desc) 91562306a36Sopenharmony_ci return -EINVAL; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci wrapper->num_clks = min_t(unsigned int, desc->num_clks, MAX_CLKS); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci for (i = 0; i < wrapper->num_clks; ++i) 92062306a36Sopenharmony_ci wrapper->clks[i].id = desc->clks[i]; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci ret = of_count_phandle_with_args(dev->of_node, "clocks", "#clock-cells"); 92362306a36Sopenharmony_ci if (ret < 0) { 92462306a36Sopenharmony_ci dev_err(dev, "invalid clocks property at %pOF\n", dev->of_node); 92562306a36Sopenharmony_ci return ret; 92662306a36Sopenharmony_ci } 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci if (ret < wrapper->num_clks) { 92962306a36Sopenharmony_ci dev_err(dev, "invalid clocks count at %pOF, expected %d entries\n", 93062306a36Sopenharmony_ci dev->of_node, wrapper->num_clks); 93162306a36Sopenharmony_ci return -EINVAL; 93262306a36Sopenharmony_ci } 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci ret = devm_clk_bulk_get(dev, wrapper->num_clks, wrapper->clks); 93562306a36Sopenharmony_ci if (ret) { 93662306a36Sopenharmony_ci dev_err(dev, "Err getting clks %d\n", ret); 93762306a36Sopenharmony_ci return ret; 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci } 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci dev_set_drvdata(dev, wrapper); 94262306a36Sopenharmony_ci dev_dbg(dev, "GENI SE Driver probed\n"); 94362306a36Sopenharmony_ci return devm_of_platform_populate(dev); 94462306a36Sopenharmony_ci} 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_cistatic const char * const qup_clks[] = { 94762306a36Sopenharmony_ci "m-ahb", 94862306a36Sopenharmony_ci "s-ahb", 94962306a36Sopenharmony_ci}; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_cistatic const struct geni_se_desc qup_desc = { 95262306a36Sopenharmony_ci .clks = qup_clks, 95362306a36Sopenharmony_ci .num_clks = ARRAY_SIZE(qup_clks), 95462306a36Sopenharmony_ci}; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_cistatic const char * const i2c_master_hub_clks[] = { 95762306a36Sopenharmony_ci "s-ahb", 95862306a36Sopenharmony_ci}; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_cistatic const struct geni_se_desc i2c_master_hub_desc = { 96162306a36Sopenharmony_ci .clks = i2c_master_hub_clks, 96262306a36Sopenharmony_ci .num_clks = ARRAY_SIZE(i2c_master_hub_clks), 96362306a36Sopenharmony_ci}; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_cistatic const struct of_device_id geni_se_dt_match[] = { 96662306a36Sopenharmony_ci { .compatible = "qcom,geni-se-qup", .data = &qup_desc }, 96762306a36Sopenharmony_ci { .compatible = "qcom,geni-se-i2c-master-hub", .data = &i2c_master_hub_desc }, 96862306a36Sopenharmony_ci {} 96962306a36Sopenharmony_ci}; 97062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, geni_se_dt_match); 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_cistatic struct platform_driver geni_se_driver = { 97362306a36Sopenharmony_ci .driver = { 97462306a36Sopenharmony_ci .name = "geni_se_qup", 97562306a36Sopenharmony_ci .of_match_table = geni_se_dt_match, 97662306a36Sopenharmony_ci }, 97762306a36Sopenharmony_ci .probe = geni_se_probe, 97862306a36Sopenharmony_ci}; 97962306a36Sopenharmony_cimodule_platform_driver(geni_se_driver); 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ciMODULE_DESCRIPTION("GENI Serial Engine Driver"); 98262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 983