Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9f23b7ba authored by Arnd Bergmann's avatar Arnd Bergmann
Browse files

Merge tag 'zynq-soc-for-v5.0' of https://github.com/Xilinx/linux-xlnx into next/drivers

ARM: Xilinx Zynq SoC patches for v5.0

- Adding pl353 smc driver

* tag 'zynq-soc-for-v5.0' of https://github.com/Xilinx/linux-xlnx

:
  memory: pl353: Add driver for arm pl353 static memory controller
  dt-bindings: memory: Add pl353 smc controller devicetree binding information

Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents a6f119a0 fee10bd2
Loading
Loading
Loading
Loading
+47 −0
Original line number Diff line number Diff line
Device tree bindings for ARM PL353 static memory controller

PL353 static memory controller supports two kinds of memory
interfaces.i.e NAND and SRAM/NOR interfaces.
The actual devices are instantiated from the child nodes of pl353 smc node.

Required properties:
- compatible		: Should be "arm,pl353-smc-r2p1", "arm,primecell".
- reg			: Controller registers map and length.
- clock-names		: List of input clock names - "memclk", "apb_pclk"
			  (See clock bindings for details).
- clocks		: Clock phandles (see clock bindings for details).
- address-cells		: Must be 2.
- size-cells		: Must be 1.

Child nodes:
 For NAND the "arm,pl353-nand-r2p1" and for NOR the "cfi-flash" drivers are
supported as child nodes.

for NAND partition information please refer the below file
Documentation/devicetree/bindings/mtd/partition.txt

Example:
	smcc: memory-controller@e000e000
			compatible = "arm,pl353-smc-r2p1", "arm,primecell";
			clock-names = "memclk", "apb_pclk";
			clocks = <&clkc 11>, <&clkc 44>;
			reg = <0xe000e000 0x1000>;
			#address-cells = <2>;
			#size-cells = <1>;
			ranges = <0x0 0x0 0xe1000000 0x1000000 //Nand CS Region
				  0x1 0x0 0xe2000000 0x2000000 //SRAM/NOR CS Region
				  0x2 0x0 0xe4000000 0x2000000>; //SRAM/NOR CS Region
			nand_0: flash@e1000000 {
				compatible = "arm,pl353-nand-r2p1"
				reg = <0 0 0x1000000>;
				(...)
			};
			nor0: flash@e2000000 {
				compatible = "cfi-flash";
				reg = <1 0 0x2000000>;
			};
			nor1: flash@e4000000 {
				compatible = "cfi-flash";
				reg = <2 0 0x2000000>;
			};
	};
+9 −0
Original line number Diff line number Diff line
@@ -145,6 +145,15 @@ config DA8XX_DDRCTL
	  Texas Instruments da8xx SoCs. It's used to tweak various memory
	  controller configuration options.

config PL353_SMC
	tristate "ARM PL35X Static Memory Controller(SMC) driver"
	default y
	depends on ARM
	depends on ARM_AMBA
	help
	  This driver is for the ARM PL351/PL353 Static Memory
	  Controller(SMC) module.

source "drivers/memory/samsung/Kconfig"
source "drivers/memory/tegra/Kconfig"

+1 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ obj-$(CONFIG_MVEBU_DEVBUS) += mvebu-devbus.o
obj-$(CONFIG_JZ4780_NEMC)	+= jz4780-nemc.o
obj-$(CONFIG_MTK_SMI)		+= mtk-smi.o
obj-$(CONFIG_DA8XX_DDRCTL)	+= da8xx-ddrctl.o
obj-$(CONFIG_PL353_SMC)		+= pl353-smc.o

obj-$(CONFIG_SAMSUNG_MC)	+= samsung/
obj-$(CONFIG_TEGRA_MC)		+= tegra/
+463 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * ARM PL353 SMC driver
 *
 * Copyright (C) 2012 - 2018 Xilinx, Inc
 * Author: Punnaiah Choudary Kalluri <punnaiah@xilinx.com>
 * Author: Naga Sureshkumar Relli <nagasure@xilinx.com>
 */

#include <linux/clk.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/pl353-smc.h>
#include <linux/amba/bus.h>

/* Register definitions */
#define PL353_SMC_MEMC_STATUS_OFFS	0	/* Controller status reg, RO */
#define PL353_SMC_CFG_CLR_OFFS		0xC	/* Clear config reg, WO */
#define PL353_SMC_DIRECT_CMD_OFFS	0x10	/* Direct command reg, WO */
#define PL353_SMC_SET_CYCLES_OFFS	0x14	/* Set cycles register, WO */
#define PL353_SMC_SET_OPMODE_OFFS	0x18	/* Set opmode register, WO */
#define PL353_SMC_ECC_STATUS_OFFS	0x400	/* ECC status register */
#define PL353_SMC_ECC_MEMCFG_OFFS	0x404	/* ECC mem config reg */
#define PL353_SMC_ECC_MEMCMD1_OFFS	0x408	/* ECC mem cmd1 reg */
#define PL353_SMC_ECC_MEMCMD2_OFFS	0x40C	/* ECC mem cmd2 reg */
#define PL353_SMC_ECC_VALUE0_OFFS	0x418	/* ECC value 0 reg */

/* Controller status register specific constants */
#define PL353_SMC_MEMC_STATUS_RAW_INT_1_SHIFT	6

/* Clear configuration register specific constants */
#define PL353_SMC_CFG_CLR_INT_CLR_1	0x10
#define PL353_SMC_CFG_CLR_ECC_INT_DIS_1	0x40
#define PL353_SMC_CFG_CLR_INT_DIS_1	0x2
#define PL353_SMC_CFG_CLR_DEFAULT_MASK	(PL353_SMC_CFG_CLR_INT_CLR_1 | \
					 PL353_SMC_CFG_CLR_ECC_INT_DIS_1 | \
					 PL353_SMC_CFG_CLR_INT_DIS_1)

/* Set cycles register specific constants */
#define PL353_SMC_SET_CYCLES_T0_MASK	0xF
#define PL353_SMC_SET_CYCLES_T0_SHIFT	0
#define PL353_SMC_SET_CYCLES_T1_MASK	0xF
#define PL353_SMC_SET_CYCLES_T1_SHIFT	4
#define PL353_SMC_SET_CYCLES_T2_MASK	0x7
#define PL353_SMC_SET_CYCLES_T2_SHIFT	8
#define PL353_SMC_SET_CYCLES_T3_MASK	0x7
#define PL353_SMC_SET_CYCLES_T3_SHIFT	11
#define PL353_SMC_SET_CYCLES_T4_MASK	0x7
#define PL353_SMC_SET_CYCLES_T4_SHIFT	14
#define PL353_SMC_SET_CYCLES_T5_MASK	0x7
#define PL353_SMC_SET_CYCLES_T5_SHIFT	17
#define PL353_SMC_SET_CYCLES_T6_MASK	0xF
#define PL353_SMC_SET_CYCLES_T6_SHIFT	20

/* ECC status register specific constants */
#define PL353_SMC_ECC_STATUS_BUSY	BIT(6)
#define PL353_SMC_ECC_REG_SIZE_OFFS	4

/* ECC memory config register specific constants */
#define PL353_SMC_ECC_MEMCFG_MODE_MASK	0xC
#define PL353_SMC_ECC_MEMCFG_MODE_SHIFT	2
#define PL353_SMC_ECC_MEMCFG_PGSIZE_MASK	0xC

#define PL353_SMC_DC_UPT_NAND_REGS	((4 << 23) |	/* CS: NAND chip */ \
				 (2 << 21))	/* UpdateRegs operation */

#define PL353_NAND_ECC_CMD1	((0x80)       |	/* Write command */ \
				 (0 << 8)     |	/* Read command */ \
				 (0x30 << 16) |	/* Read End command */ \
				 (1 << 24))	/* Read End command calid */

#define PL353_NAND_ECC_CMD2	((0x85)	      |	/* Write col change cmd */ \
				 (5 << 8)     |	/* Read col change cmd */ \
				 (0xE0 << 16) |	/* Read col change end cmd */ \
				 (1 << 24)) /* Read col change end cmd valid */
#define PL353_NAND_ECC_BUSY_TIMEOUT	(1 * HZ)
/**
 * struct pl353_smc_data - Private smc driver structure
 * @memclk:		Pointer to the peripheral clock
 * @aclk:		Pointer to the APER clock
 */
struct pl353_smc_data {
	struct clk		*memclk;
	struct clk		*aclk;
};

/* SMC virtual register base */
static void __iomem *pl353_smc_base;

/**
 * pl353_smc_set_buswidth - Set memory buswidth
 * @bw: Memory buswidth (8 | 16)
 * Return: 0 on success or negative errno.
 */
int pl353_smc_set_buswidth(unsigned int bw)
{
	if (bw != PL353_SMC_MEM_WIDTH_8  && bw != PL353_SMC_MEM_WIDTH_16)
		return -EINVAL;

	writel(bw, pl353_smc_base + PL353_SMC_SET_OPMODE_OFFS);
	writel(PL353_SMC_DC_UPT_NAND_REGS, pl353_smc_base +
	       PL353_SMC_DIRECT_CMD_OFFS);

	return 0;
}
EXPORT_SYMBOL_GPL(pl353_smc_set_buswidth);

/**
 * pl353_smc_set_cycles - Set memory timing parameters
 * @timings: NAND controller timing parameters
 *
 * Sets NAND chip specific timing parameters.
 */
void pl353_smc_set_cycles(u32 timings[])
{
	/*
	 * Set write pulse timing. This one is easy to extract:
	 *
	 * NWE_PULSE = tWP
	 */
	timings[0] &= PL353_SMC_SET_CYCLES_T0_MASK;
	timings[1] = (timings[1] & PL353_SMC_SET_CYCLES_T1_MASK) <<
			PL353_SMC_SET_CYCLES_T1_SHIFT;
	timings[2] = (timings[2]  & PL353_SMC_SET_CYCLES_T2_MASK) <<
			PL353_SMC_SET_CYCLES_T2_SHIFT;
	timings[3] = (timings[3]  & PL353_SMC_SET_CYCLES_T3_MASK) <<
			PL353_SMC_SET_CYCLES_T3_SHIFT;
	timings[4] = (timings[4] & PL353_SMC_SET_CYCLES_T4_MASK) <<
			PL353_SMC_SET_CYCLES_T4_SHIFT;
	timings[5]  = (timings[5]  & PL353_SMC_SET_CYCLES_T5_MASK) <<
			PL353_SMC_SET_CYCLES_T5_SHIFT;
	timings[6]  = (timings[6]  & PL353_SMC_SET_CYCLES_T6_MASK) <<
			PL353_SMC_SET_CYCLES_T6_SHIFT;
	timings[0] |= timings[1] | timings[2] | timings[3] |
			timings[4] | timings[5] | timings[6];

	writel(timings[0], pl353_smc_base + PL353_SMC_SET_CYCLES_OFFS);
	writel(PL353_SMC_DC_UPT_NAND_REGS, pl353_smc_base +
	       PL353_SMC_DIRECT_CMD_OFFS);
}
EXPORT_SYMBOL_GPL(pl353_smc_set_cycles);

/**
 * pl353_smc_ecc_is_busy - Read ecc busy flag
 * Return: the ecc_status bit from the ecc_status register. 1 = busy, 0 = idle
 */
bool pl353_smc_ecc_is_busy(void)
{
	return ((readl(pl353_smc_base + PL353_SMC_ECC_STATUS_OFFS) &
		  PL353_SMC_ECC_STATUS_BUSY) == PL353_SMC_ECC_STATUS_BUSY);
}
EXPORT_SYMBOL_GPL(pl353_smc_ecc_is_busy);

/**
 * pl353_smc_get_ecc_val - Read ecc_valueN registers
 * @ecc_reg: Index of the ecc_value reg (0..3)
 * Return: the content of the requested ecc_value register.
 *
 * There are four valid ecc_value registers. The argument is truncated to stay
 * within this valid boundary.
 */
u32 pl353_smc_get_ecc_val(int ecc_reg)
{
	u32 addr, reg;

	addr = PL353_SMC_ECC_VALUE0_OFFS +
		(ecc_reg * PL353_SMC_ECC_REG_SIZE_OFFS);
	reg = readl(pl353_smc_base + addr);

	return reg;
}
EXPORT_SYMBOL_GPL(pl353_smc_get_ecc_val);

/**
 * pl353_smc_get_nand_int_status_raw - Get NAND interrupt status bit
 * Return: the raw_int_status1 bit from the memc_status register
 */
int pl353_smc_get_nand_int_status_raw(void)
{
	u32 reg;

	reg = readl(pl353_smc_base + PL353_SMC_MEMC_STATUS_OFFS);
	reg >>= PL353_SMC_MEMC_STATUS_RAW_INT_1_SHIFT;
	reg &= 1;

	return reg;
}
EXPORT_SYMBOL_GPL(pl353_smc_get_nand_int_status_raw);

/**
 * pl353_smc_clr_nand_int - Clear NAND interrupt
 */
void pl353_smc_clr_nand_int(void)
{
	writel(PL353_SMC_CFG_CLR_INT_CLR_1,
	       pl353_smc_base + PL353_SMC_CFG_CLR_OFFS);
}
EXPORT_SYMBOL_GPL(pl353_smc_clr_nand_int);

/**
 * pl353_smc_set_ecc_mode - Set SMC ECC mode
 * @mode: ECC mode (BYPASS, APB, MEM)
 * Return: 0 on success or negative errno.
 */
int pl353_smc_set_ecc_mode(enum pl353_smc_ecc_mode mode)
{
	u32 reg;
	int ret = 0;

	switch (mode) {
	case PL353_SMC_ECCMODE_BYPASS:
	case PL353_SMC_ECCMODE_APB:
	case PL353_SMC_ECCMODE_MEM:

		reg = readl(pl353_smc_base + PL353_SMC_ECC_MEMCFG_OFFS);
		reg &= ~PL353_SMC_ECC_MEMCFG_MODE_MASK;
		reg |= mode << PL353_SMC_ECC_MEMCFG_MODE_SHIFT;
		writel(reg, pl353_smc_base + PL353_SMC_ECC_MEMCFG_OFFS);

		break;
	default:
		ret = -EINVAL;
	}

	return ret;
}
EXPORT_SYMBOL_GPL(pl353_smc_set_ecc_mode);

/**
 * pl353_smc_set_ecc_pg_size - Set SMC ECC page size
 * @pg_sz: ECC page size
 * Return: 0 on success or negative errno.
 */
int pl353_smc_set_ecc_pg_size(unsigned int pg_sz)
{
	u32 reg, sz;

	switch (pg_sz) {
	case 0:
		sz = 0;
		break;
	case SZ_512:
		sz = 1;
		break;
	case SZ_1K:
		sz = 2;
		break;
	case SZ_2K:
		sz = 3;
		break;
	default:
		return -EINVAL;
	}

	reg = readl(pl353_smc_base + PL353_SMC_ECC_MEMCFG_OFFS);
	reg &= ~PL353_SMC_ECC_MEMCFG_PGSIZE_MASK;
	reg |= sz;
	writel(reg, pl353_smc_base + PL353_SMC_ECC_MEMCFG_OFFS);

	return 0;
}
EXPORT_SYMBOL_GPL(pl353_smc_set_ecc_pg_size);

static int __maybe_unused pl353_smc_suspend(struct device *dev)
{
	struct pl353_smc_data *pl353_smc = dev_get_drvdata(dev);

	clk_disable(pl353_smc->memclk);
	clk_disable(pl353_smc->aclk);

	return 0;
}

static int __maybe_unused pl353_smc_resume(struct device *dev)
{
	int ret;
	struct pl353_smc_data *pl353_smc = dev_get_drvdata(dev);

	ret = clk_enable(pl353_smc->aclk);
	if (ret) {
		dev_err(dev, "Cannot enable axi domain clock.\n");
		return ret;
	}

	ret = clk_enable(pl353_smc->memclk);
	if (ret) {
		dev_err(dev, "Cannot enable memory clock.\n");
		clk_disable(pl353_smc->aclk);
		return ret;
	}

	return ret;
}

static struct amba_driver pl353_smc_driver;

static SIMPLE_DEV_PM_OPS(pl353_smc_dev_pm_ops, pl353_smc_suspend,
			 pl353_smc_resume);

/**
 * pl353_smc_init_nand_interface - Initialize the NAND interface
 * @adev: Pointer to the amba_device struct
 * @nand_node: Pointer to the pl353_nand device_node struct
 */
static void pl353_smc_init_nand_interface(struct amba_device *adev,
					  struct device_node *nand_node)
{
	unsigned long timeout;

	pl353_smc_set_buswidth(PL353_SMC_MEM_WIDTH_8);
	writel(PL353_SMC_CFG_CLR_INT_CLR_1,
	       pl353_smc_base + PL353_SMC_CFG_CLR_OFFS);
	writel(PL353_SMC_DC_UPT_NAND_REGS, pl353_smc_base +
	       PL353_SMC_DIRECT_CMD_OFFS);

	timeout = jiffies + PL353_NAND_ECC_BUSY_TIMEOUT;
	/* Wait till the ECC operation is complete */
	do {
		if (pl353_smc_ecc_is_busy())
			cpu_relax();
		else
			break;
	} while (!time_after_eq(jiffies, timeout));

	if (time_after_eq(jiffies, timeout))
		return;

	writel(PL353_NAND_ECC_CMD1,
	       pl353_smc_base + PL353_SMC_ECC_MEMCMD1_OFFS);
	writel(PL353_NAND_ECC_CMD2,
	       pl353_smc_base + PL353_SMC_ECC_MEMCMD2_OFFS);
}

static const struct of_device_id pl353_smc_supported_children[] = {
	{
		.compatible = "cfi-flash"
	},
	{
		.compatible = "arm,pl353-nand-r2p1",
		.data = pl353_smc_init_nand_interface
	},
	{}
};

static int pl353_smc_probe(struct amba_device *adev, const struct amba_id *id)
{
	struct pl353_smc_data *pl353_smc;
	struct device_node *child;
	struct resource *res;
	int err;
	struct device_node *of_node = adev->dev.of_node;
	static void (*init)(struct amba_device *adev,
			    struct device_node *nand_node);
	const struct of_device_id *match = NULL;

	pl353_smc = devm_kzalloc(&adev->dev, sizeof(*pl353_smc), GFP_KERNEL);
	if (!pl353_smc)
		return -ENOMEM;

	/* Get the NAND controller virtual address */
	res = &adev->res;
	pl353_smc_base = devm_ioremap_resource(&adev->dev, res);
	if (IS_ERR(pl353_smc_base))
		return PTR_ERR(pl353_smc_base);

	pl353_smc->aclk = devm_clk_get(&adev->dev, "apb_pclk");
	if (IS_ERR(pl353_smc->aclk)) {
		dev_err(&adev->dev, "aclk clock not found.\n");
		return PTR_ERR(pl353_smc->aclk);
	}

	pl353_smc->memclk = devm_clk_get(&adev->dev, "memclk");
	if (IS_ERR(pl353_smc->memclk)) {
		dev_err(&adev->dev, "memclk clock not found.\n");
		return PTR_ERR(pl353_smc->memclk);
	}

	err = clk_prepare_enable(pl353_smc->aclk);
	if (err) {
		dev_err(&adev->dev, "Unable to enable AXI clock.\n");
		return err;
	}

	err = clk_prepare_enable(pl353_smc->memclk);
	if (err) {
		dev_err(&adev->dev, "Unable to enable memory clock.\n");
		goto out_clk_dis_aper;
	}

	amba_set_drvdata(adev, pl353_smc);

	/* clear interrupts */
	writel(PL353_SMC_CFG_CLR_DEFAULT_MASK,
	       pl353_smc_base + PL353_SMC_CFG_CLR_OFFS);

	/* Find compatible children. Only a single child is supported */
	for_each_available_child_of_node(of_node, child) {
		match = of_match_node(pl353_smc_supported_children, child);
		if (!match) {
			dev_warn(&adev->dev, "unsupported child node\n");
			continue;
		}
		break;
	}
	if (!match) {
		dev_err(&adev->dev, "no matching children\n");
		goto out_clk_disable;
	}

	init = match->data;
	if (init)
		init(adev, child);
	of_platform_device_create(child, NULL, &adev->dev);

	return 0;

out_clk_disable:
	clk_disable_unprepare(pl353_smc->memclk);
out_clk_dis_aper:
	clk_disable_unprepare(pl353_smc->aclk);

	return err;
}

static int pl353_smc_remove(struct amba_device *adev)
{
	struct pl353_smc_data *pl353_smc = amba_get_drvdata(adev);

	clk_disable_unprepare(pl353_smc->memclk);
	clk_disable_unprepare(pl353_smc->aclk);

	return 0;
}

static const struct amba_id pl353_ids[] = {
	{
	.id = 0x00041353,
	.mask = 0x000fffff,
	},
	{ 0, 0 },
};
MODULE_DEVICE_TABLE(amba, pl353_ids);

static struct amba_driver pl353_smc_driver = {
	.drv = {
		.owner = THIS_MODULE,
		.name = "pl353-smc",
		.pm = &pl353_smc_dev_pm_ops,
	},
	.id_table = pl353_ids,
	.probe = pl353_smc_probe,
	.remove = pl353_smc_remove,
};

module_amba_driver(pl353_smc_driver);

MODULE_AUTHOR("Xilinx, Inc.");
MODULE_DESCRIPTION("ARM PL353 SMC Driver");
MODULE_LICENSE("GPL");
+30 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * ARM PL353 SMC Driver Header
 *
 * Copyright (C) 2012 - 2018 Xilinx, Inc
 */

#ifndef __LINUX_PL353_SMC_H
#define __LINUX_PL353_SMC_H

enum pl353_smc_ecc_mode {
	PL353_SMC_ECCMODE_BYPASS = 0,
	PL353_SMC_ECCMODE_APB = 1,
	PL353_SMC_ECCMODE_MEM = 2
};

enum pl353_smc_mem_width {
	PL353_SMC_MEM_WIDTH_8 = 0,
	PL353_SMC_MEM_WIDTH_16 = 1
};

u32 pl353_smc_get_ecc_val(int ecc_reg);
bool pl353_smc_ecc_is_busy(void);
int pl353_smc_get_nand_int_status_raw(void);
void pl353_smc_clr_nand_int(void);
int pl353_smc_set_ecc_mode(enum pl353_smc_ecc_mode mode);
int pl353_smc_set_ecc_pg_size(unsigned int pg_sz);
int pl353_smc_set_buswidth(unsigned int bw);
void pl353_smc_set_cycles(u32 timings[]);
#endif