* [PATCH v2] arm64: dts: renesas: condor/v3hsk: specify Ethernet PHY IRQs
From: Sergei Shtylyov @ 2018-06-13 16:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <4acc208e-c593-1e8a-00ca-fc9a5574074e@cogentembedded.com>
Specify Ethernet PHY IRQs in the Condor/V3HSK board device trees, now that
we have the GPIO support (previously phylib had to resort to polling).
Based on the original (and large) patch by Vladimir Barinov.
Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
---
Changes in version 2:
- merged in the analogous V3HSK patch, renamed the patch, and updated the
patch description accordingly.
arch/arm64/boot/dts/renesas/r8a77980-condor.dts | 2 ++
arch/arm64/boot/dts/renesas/r8a77980-v3hsk.dts | 2 ++
2 files changed, 4 insertions(+)
Index: renesas/arch/arm64/boot/dts/renesas/r8a77980-condor.dts
===================================================================
--- renesas.orig/arch/arm64/boot/dts/renesas/r8a77980-condor.dts
+++ renesas/arch/arm64/boot/dts/renesas/r8a77980-condor.dts
@@ -59,6 +59,8 @@
phy0: ethernet-phy at 0 {
rxc-skew-ps = <1500>;
reg = <0>;
+ interrupt-parent = <&gpio1>;
+ interrupts = <17 IRQ_TYPE_LEVEL_LOW>;
};
};
Index: renesas/arch/arm64/boot/dts/renesas/r8a77980-v3hsk.dts
===================================================================
--- renesas.orig/arch/arm64/boot/dts/renesas/r8a77980-v3hsk.dts
+++ renesas/arch/arm64/boot/dts/renesas/r8a77980-v3hsk.dts
@@ -48,6 +48,8 @@
phy0: ethernet-phy at 0 {
reg = <0>;
+ interrupt-parent = <&gpio4>;
+ interrupts = <23 IRQ_TYPE_LEVEL_LOW>;
};
};
^ permalink raw reply
* [PATCH v7 6/6] tty/serial: atmel: change the driver to work under at91-usart mfd
From: Radu Pirea @ 2018-06-13 16:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180613163621.23995-1-radu.pirea@microchip.com>
This patch modifies the place where resources and device tree properties
are searched.
Signed-off-by: Radu Pirea <radu.pirea@microchip.com>
Acked-by: Richard Genoud <richard.genoud@gmail.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
---
drivers/tty/serial/Kconfig | 1 +
drivers/tty/serial/atmel_serial.c | 42 ++++++++++++++++++++-----------
2 files changed, 28 insertions(+), 15 deletions(-)
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 3682fd3e960c..25e55332f8b1 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -119,6 +119,7 @@ config SERIAL_ATMEL
depends on ARCH_AT91 || COMPILE_TEST
select SERIAL_CORE
select SERIAL_MCTRL_GPIO if GPIOLIB
+ select MFD_AT91_USART
help
This enables the driver for the on-chip UARTs of the Atmel
AT91 processors.
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index df46a9e88c34..107114d73c5d 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -193,8 +193,7 @@ static struct console atmel_console;
#if defined(CONFIG_OF)
static const struct of_device_id atmel_serial_dt_ids[] = {
- { .compatible = "atmel,at91rm9200-usart" },
- { .compatible = "atmel,at91sam9260-usart" },
+ { .compatible = "atmel,at91rm9200-usart-serial" },
{ /* sentinel */ }
};
#endif
@@ -915,6 +914,7 @@ static void atmel_tx_dma(struct uart_port *port)
static int atmel_prepare_tx_dma(struct uart_port *port)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ struct device *mfd_dev = port->dev->parent;
dma_cap_mask_t mask;
struct dma_slave_config config;
int ret, nent;
@@ -922,7 +922,7 @@ static int atmel_prepare_tx_dma(struct uart_port *port)
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
- atmel_port->chan_tx = dma_request_slave_channel(port->dev, "tx");
+ atmel_port->chan_tx = dma_request_slave_channel(mfd_dev, "tx");
if (atmel_port->chan_tx == NULL)
goto chan_err;
dev_info(port->dev, "using %s for tx DMA transfers\n",
@@ -1093,6 +1093,7 @@ static void atmel_rx_from_dma(struct uart_port *port)
static int atmel_prepare_rx_dma(struct uart_port *port)
{
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ struct device *mfd_dev = port->dev->parent;
struct dma_async_tx_descriptor *desc;
dma_cap_mask_t mask;
struct dma_slave_config config;
@@ -1104,7 +1105,7 @@ static int atmel_prepare_rx_dma(struct uart_port *port)
dma_cap_zero(mask);
dma_cap_set(DMA_CYCLIC, mask);
- atmel_port->chan_rx = dma_request_slave_channel(port->dev, "rx");
+ atmel_port->chan_rx = dma_request_slave_channel(mfd_dev, "rx");
if (atmel_port->chan_rx == NULL)
goto chan_err;
dev_info(port->dev, "using %s for rx DMA transfers\n",
@@ -2222,8 +2223,8 @@ static const char *atmel_type(struct uart_port *port)
*/
static void atmel_release_port(struct uart_port *port)
{
- struct platform_device *pdev = to_platform_device(port->dev);
- int size = pdev->resource[0].end - pdev->resource[0].start + 1;
+ struct platform_device *mpdev = to_platform_device(port->dev->parent);
+ int size = resource_size(mpdev->resource);
release_mem_region(port->mapbase, size);
@@ -2238,8 +2239,8 @@ static void atmel_release_port(struct uart_port *port)
*/
static int atmel_request_port(struct uart_port *port)
{
- struct platform_device *pdev = to_platform_device(port->dev);
- int size = pdev->resource[0].end - pdev->resource[0].start + 1;
+ struct platform_device *mpdev = to_platform_device(port->dev->parent);
+ int size = resource_size(mpdev->resource);
if (!request_mem_region(port->mapbase, size, "atmel_serial"))
return -EBUSY;
@@ -2341,27 +2342,28 @@ static int atmel_init_port(struct atmel_uart_port *atmel_port,
{
int ret;
struct uart_port *port = &atmel_port->uart;
+ struct platform_device *mpdev = to_platform_device(pdev->dev.parent);
atmel_init_property(atmel_port, pdev);
atmel_set_ops(port);
- uart_get_rs485_mode(&pdev->dev, &port->rs485);
+ uart_get_rs485_mode(&mpdev->dev, &port->rs485);
port->iotype = UPIO_MEM;
port->flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP;
port->ops = &atmel_pops;
port->fifosize = 1;
port->dev = &pdev->dev;
- port->mapbase = pdev->resource[0].start;
- port->irq = pdev->resource[1].start;
+ port->mapbase = mpdev->resource[0].start;
+ port->irq = mpdev->resource[1].start;
port->rs485_config = atmel_config_rs485;
- port->membase = NULL;
+ port->membase = NULL;
memset(&atmel_port->rx_ring, 0, sizeof(atmel_port->rx_ring));
/* for console, the clock could already be configured */
if (!atmel_port->clk) {
- atmel_port->clk = clk_get(&pdev->dev, "usart");
+ atmel_port->clk = clk_get(&mpdev->dev, "usart");
if (IS_ERR(atmel_port->clk)) {
ret = PTR_ERR(atmel_port->clk);
atmel_port->clk = NULL;
@@ -2694,13 +2696,22 @@ static void atmel_serial_probe_fifos(struct atmel_uart_port *atmel_port,
static int atmel_serial_probe(struct platform_device *pdev)
{
struct atmel_uart_port *atmel_port;
- struct device_node *np = pdev->dev.of_node;
+ struct device_node *np = pdev->dev.parent->of_node;
void *data;
int ret = -ENODEV;
bool rs485_enabled;
BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1));
+ /*
+ * In device tree there is no node with "atmel,at91rm9200-usart-serial"
+ * as compatible string. This driver is probed by at91-usart mfd driver
+ * which is just a wrapper over the atmel_serial driver and
+ * spi-at91-usart driver. All attributes needed by this driver are
+ * found in of_node of parent.
+ */
+ pdev->dev.of_node = np;
+
ret = of_alias_get_id(np, "serial");
if (ret < 0)
/* port id not found in platform data nor device-tree aliases:
@@ -2835,6 +2846,7 @@ static int atmel_serial_remove(struct platform_device *pdev)
clk_put(atmel_port->clk);
atmel_port->clk = NULL;
+ pdev->dev.of_node = NULL;
return ret;
}
@@ -2845,7 +2857,7 @@ static struct platform_driver atmel_serial_driver = {
.suspend = atmel_serial_suspend,
.resume = atmel_serial_resume,
.driver = {
- .name = "atmel_usart",
+ .name = "atmel_usart_serial",
.of_match_table = of_match_ptr(atmel_serial_dt_ids),
},
};
--
2.17.1
^ permalink raw reply related
* [PATCH v7 5/6] spi: at91-usart: add driver for at91-usart as spi
From: Radu Pirea @ 2018-06-13 16:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180613163621.23995-1-radu.pirea@microchip.com>
This is the driver for at91-usart in spi mode. The USART IP can be configured
to work in many modes and one of them is SPI.
The driver was tested on sama5d3-xplained and sama5d4-xplained boards with
enc28j60 ethernet controller as slave.
Signed-off-by: Radu Pirea <radu.pirea@microchip.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
---
drivers/spi/Kconfig | 9 +
drivers/spi/Makefile | 1 +
drivers/spi/spi-at91-usart.c | 434 +++++++++++++++++++++++++++++++++++
3 files changed, 444 insertions(+)
create mode 100644 drivers/spi/spi-at91-usart.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 6fb0347a24f2..1a002a32d7aa 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -77,6 +77,15 @@ config SPI_ATMEL
This selects a driver for the Atmel SPI Controller, present on
many AT91 (ARM) chips.
+config SPI_AT91_USART
+ tristate "Atmel USART Controller SPI driver"
+ depends on HAS_DMA
+ depends on (ARCH_AT91 || COMPILE_TEST)
+ select MFD_AT91_USART
+ help
+ This selects a driver for the AT91 USART Controller as SPI Master,
+ present on AT91 and SAMA5 SoC series.
+
config SPI_AU1550
tristate "Au1550/Au1200/Au1300 SPI Controller"
depends on MIPS_ALCHEMY
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 34c5f2832ddf..fb6cb42f4eaa 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o
obj-$(CONFIG_SPI_ALTERA) += spi-altera.o
obj-$(CONFIG_SPI_ARMADA_3700) += spi-armada-3700.o
obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o
+obj-$(CONFIG_SPI_AT91_USART) += spi-at91-usart.o
obj-$(CONFIG_SPI_ATH79) += spi-ath79.o
obj-$(CONFIG_SPI_AU1550) += spi-au1550.o
obj-$(CONFIG_SPI_AXI_SPI_ENGINE) += spi-axi-spi-engine.o
diff --git a/drivers/spi/spi-at91-usart.c b/drivers/spi/spi-at91-usart.c
new file mode 100644
index 000000000000..ae889aa6bda3
--- /dev/null
+++ b/drivers/spi/spi-at91-usart.c
@@ -0,0 +1,434 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for AT91 USART Controllers as SPI
+ *
+ * Copyright (C) 2018 Microchip Technology Inc.
+ * Author: Radu Pirea <radu.pirea@microchip.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+
+#include <linux/spi/spi.h>
+
+#define US_CR 0x00
+#define US_MR 0x04
+#define US_IER 0x08
+#define US_IDR 0x0C
+#define US_CSR 0x14
+#define US_RHR 0x18
+#define US_THR 0x1C
+#define US_BRGR 0x20
+#define US_VERSION 0xFC
+
+#define US_CR_RSTRX BIT(2)
+#define US_CR_RSTTX BIT(3)
+#define US_CR_RXEN BIT(4)
+#define US_CR_RXDIS BIT(5)
+#define US_CR_TXEN BIT(6)
+#define US_CR_TXDIS BIT(7)
+
+#define US_MR_SPI_MASTER 0x0E
+#define US_MR_CHRL GENMASK(7, 6)
+#define US_MR_CPHA BIT(8)
+#define US_MR_CPOL BIT(16)
+#define US_MR_CLKO BIT(18)
+#define US_MR_WRDBT BIT(20)
+#define US_MR_LOOP BIT(15)
+
+#define US_IR_RXRDY BIT(0)
+#define US_IR_TXRDY BIT(1)
+#define US_IR_OVRE BIT(5)
+
+#define US_BRGR_SIZE BIT(16)
+
+#define US_MIN_CLK_DIV 0x06
+#define US_MAX_CLK_DIV BIT(16)
+
+#define US_RESET (US_CR_RSTRX | US_CR_RSTTX)
+#define US_DISABLE (US_CR_RXDIS | US_CR_TXDIS)
+#define US_ENABLE (US_CR_RXEN | US_CR_TXEN)
+#define US_OVRE_RXRDY_IRQS (US_IR_OVRE | US_IR_RXRDY)
+
+#define US_INIT \
+ (US_MR_SPI_MASTER | US_MR_CHRL | US_MR_CLKO | US_MR_WRDBT)
+
+/* Register access macros */
+#define at91_usart_spi_readl(port, reg) \
+ readl_relaxed((port)->regs + US_##reg)
+#define at91_usart_spi_writel(port, reg, value) \
+ writel_relaxed((value), (port)->regs + US_##reg)
+
+#define at91_usart_spi_readb(port, reg) \
+ readb_relaxed((port)->regs + US_##reg)
+#define at91_usart_spi_writeb(port, reg, value) \
+ writeb_relaxed((value), (port)->regs + US_##reg)
+
+struct at91_usart_spi {
+ struct spi_transfer *current_transfer;
+ void __iomem *regs;
+ struct device *dev;
+ struct clk *clk;
+
+ /*used in interrupt to protect data reading*/
+ spinlock_t lock;
+
+ int irq;
+ unsigned int current_tx_remaining_bytes;
+ unsigned int current_rx_remaining_bytes;
+ int done_status;
+
+ u32 spi_clk;
+ u32 status;
+
+ bool xfer_failed;
+};
+
+static inline u32 at91_usart_spi_tx_ready(struct at91_usart_spi *aus)
+{
+ return aus->status & US_IR_TXRDY;
+}
+
+static inline u32 at91_usart_spi_rx_ready(struct at91_usart_spi *aus)
+{
+ return aus->status & US_IR_RXRDY;
+}
+
+static inline u32 at91_usart_spi_check_overrun(struct at91_usart_spi *aus)
+{
+ return aus->status & US_IR_OVRE;
+}
+
+static inline u32 at91_usart_spi_read_status(struct at91_usart_spi *aus)
+{
+ aus->status = at91_usart_spi_readl(aus, CSR);
+ return aus->status;
+}
+
+static inline void at91_usart_spi_tx(struct at91_usart_spi *aus)
+{
+ unsigned int len = aus->current_transfer->len;
+ unsigned int remaining = aus->current_tx_remaining_bytes;
+ const u8 *tx_buf = aus->current_transfer->tx_buf;
+
+ if (!remaining)
+ return;
+
+ if (at91_usart_spi_tx_ready(aus)) {
+ at91_usart_spi_writeb(aus, THR, tx_buf[len - remaining]);
+ aus->current_tx_remaining_bytes--;
+ }
+}
+
+static inline void at91_usart_spi_rx(struct at91_usart_spi *aus)
+{
+ int len = aus->current_transfer->len;
+ int remaining = aus->current_rx_remaining_bytes;
+ u8 *rx_buf = aus->current_transfer->rx_buf;
+
+ if (!remaining)
+ return;
+
+ rx_buf[len - remaining] = at91_usart_spi_readb(aus, RHR);
+ aus->current_rx_remaining_bytes--;
+}
+
+static inline void
+at91_usart_spi_set_xfer_speed(struct at91_usart_spi *aus,
+ struct spi_transfer *xfer)
+{
+ at91_usart_spi_writel(aus, BRGR,
+ DIV_ROUND_UP(aus->spi_clk, xfer->speed_hz));
+}
+
+static irqreturn_t at91_usart_spi_interrupt(int irq, void *dev_id)
+{
+ struct spi_controller *controller = dev_id;
+ struct at91_usart_spi *aus = spi_master_get_devdata(controller);
+
+ spin_lock(&aus->lock);
+ at91_usart_spi_read_status(aus);
+
+ if (at91_usart_spi_check_overrun(aus)) {
+ aus->xfer_failed = true;
+ aus->done_status = -EIO;
+ at91_usart_spi_writel(aus, IDR, US_IR_OVRE | US_IR_RXRDY);
+ spin_unlock(&aus->lock);
+ return IRQ_HANDLED;
+ }
+
+ if (at91_usart_spi_rx_ready(aus)) {
+ at91_usart_spi_rx(aus);
+ spin_unlock(&aus->lock);
+ return IRQ_HANDLED;
+ }
+
+ spin_unlock(&aus->lock);
+
+ return IRQ_NONE;
+}
+
+static int at91_usart_spi_setup(struct spi_device *spi)
+{
+ struct at91_usart_spi *aus = spi_master_get_devdata(spi->controller);
+ u32 *ausd = spi->controller_state;
+ unsigned int mr = at91_usart_spi_readl(aus, MR);
+ u8 bits = spi->bits_per_word;
+
+ if (bits != 8) {
+ dev_dbg(&spi->dev, "Only 8 bits per word are supported\n");
+ return -EINVAL;
+ }
+
+ if (spi->mode & SPI_CPOL)
+ mr |= US_MR_CPOL;
+ else
+ mr &= ~US_MR_CPOL;
+
+ if (spi->mode & SPI_CPHA)
+ mr |= US_MR_CPHA;
+ else
+ mr &= ~US_MR_CPHA;
+
+ if (spi->mode & SPI_LOOP)
+ mr |= US_MR_LOOP;
+ else
+ mr &= ~US_MR_LOOP;
+
+ if (!ausd) {
+ ausd = kzalloc(sizeof(*ausd), GFP_KERNEL);
+ if (!ausd)
+ return -ENOMEM;
+
+ spi->controller_state = ausd;
+ }
+
+ *ausd = mr;
+
+ dev_dbg(&spi->dev,
+ "setup: bpw %u mode 0x%x -> mr %d %08x\n",
+ bits, spi->mode, spi->chip_select, mr);
+
+ return 0;
+}
+
+int at91_usart_spi_transfer_one(struct spi_controller *ctlr,
+ struct spi_device *spi,
+ struct spi_transfer *xfer)
+{
+ struct at91_usart_spi *aus = spi_master_get_devdata(ctlr);
+
+ at91_usart_spi_set_xfer_speed(aus, xfer);
+ aus->done_status = 0;
+ aus->xfer_failed = false;
+ aus->current_transfer = xfer;
+ aus->current_tx_remaining_bytes = xfer->len;
+ aus->current_rx_remaining_bytes = xfer->len;
+
+ while ((aus->current_tx_remaining_bytes ||
+ aus->current_rx_remaining_bytes) && !aus->xfer_failed) {
+ at91_usart_spi_read_status(aus);
+ at91_usart_spi_tx(aus);
+ cpu_relax();
+ }
+ if (aus->xfer_failed) {
+ dev_err(aus->dev, "Overrun!\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int at91_usart_spi_prepare_message(struct spi_controller *ctlr,
+ struct spi_message *message)
+{
+ struct at91_usart_spi *aus = spi_master_get_devdata(ctlr);
+ struct spi_device *spi = message->spi;
+ u32 *ausd = spi->controller_state;
+
+ at91_usart_spi_writel(aus, CR, US_ENABLE);
+ at91_usart_spi_writel(aus, IER, US_OVRE_RXRDY_IRQS);
+ at91_usart_spi_writel(aus, MR, *ausd);
+
+ return 0;
+}
+
+int at91_usart_spi_unprepare_message(struct spi_controller *ctlr,
+ struct spi_message *message)
+{
+ struct at91_usart_spi *aus = spi_master_get_devdata(ctlr);
+
+ at91_usart_spi_writel(aus, CR, US_RESET | US_DISABLE);
+ at91_usart_spi_writel(aus, IDR, US_OVRE_RXRDY_IRQS);
+
+ return 0;
+}
+
+static void at91_usart_spi_cleanup(struct spi_device *spi)
+{
+ struct at91_usart_spi_device *ausd = spi->controller_state;
+
+ spi->controller_state = NULL;
+ kfree(ausd);
+}
+
+static void at91_usart_spi_init(struct at91_usart_spi *aus)
+{
+ at91_usart_spi_writel(aus, MR, US_INIT);
+ at91_usart_spi_writel(aus, CR, US_RESET | US_DISABLE);
+}
+
+static int at91_usart_gpio_setup(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.parent->of_node;
+ int i;
+ int ret;
+ int nb;
+
+ if (!np)
+ return -EINVAL;
+
+ nb = of_gpio_named_count(np, "cs-gpios");
+ for (i = 0; i < nb; i++) {
+ int cs_gpio = of_get_named_gpio(np, "cs-gpios", i);
+
+ if (cs_gpio < 0)
+ return cs_gpio;
+
+ if (gpio_is_valid(cs_gpio)) {
+ ret = devm_gpio_request_one(&pdev->dev, cs_gpio,
+ GPIOF_DIR_OUT,
+ dev_name(&pdev->dev));
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int at91_usart_spi_probe(struct platform_device *pdev)
+{
+ struct resource *regs;
+ struct spi_controller *controller;
+ struct at91_usart_spi *aus;
+ struct clk *clk;
+ int irq;
+ int ret;
+
+ regs = platform_get_resource(to_platform_device(pdev->dev.parent),
+ IORESOURCE_MEM, 0);
+ if (!regs)
+ return -EINVAL;
+
+ irq = platform_get_irq(to_platform_device(pdev->dev.parent), 0);
+ if (irq < 0)
+ return irq;
+
+ clk = devm_clk_get(pdev->dev.parent, "usart");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ ret = -ENOMEM;
+ controller = spi_alloc_master(&pdev->dev, sizeof(*aus));
+ if (!controller)
+ goto at91_usart_spi_probe_fail;
+
+ ret = at91_usart_gpio_setup(pdev);
+ if (ret)
+ goto at91_usart_spi_probe_fail;
+
+ controller->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_CS_HIGH;
+ controller->dev.of_node = pdev->dev.parent->of_node;
+ controller->bits_per_word_mask = SPI_BPW_MASK(8);
+ controller->setup = at91_usart_spi_setup;
+ controller->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX;
+ controller->transfer_one = at91_usart_spi_transfer_one;
+ controller->prepare_message = at91_usart_spi_prepare_message;
+ controller->unprepare_message = at91_usart_spi_unprepare_message;
+ controller->cleanup = at91_usart_spi_cleanup;
+ controller->max_speed_hz = DIV_ROUND_UP(clk_get_rate(clk),
+ US_MIN_CLK_DIV);
+ controller->min_speed_hz = DIV_ROUND_UP(clk_get_rate(clk),
+ US_MAX_CLK_DIV);
+ platform_set_drvdata(pdev, controller);
+
+ aus = spi_master_get_devdata(controller);
+
+ aus->dev = &pdev->dev;
+ aus->regs = devm_ioremap_resource(&pdev->dev, regs);
+ if (IS_ERR(aus->regs)) {
+ ret = PTR_ERR(aus->regs);
+ goto at91_usart_spi_probe_fail;
+ }
+
+ aus->irq = irq;
+ aus->clk = clk;
+
+ ret = devm_request_irq(&pdev->dev, irq, at91_usart_spi_interrupt, 0,
+ dev_name(&pdev->dev), controller);
+ if (ret)
+ goto at91_usart_spi_probe_fail;
+
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ goto at91_usart_spi_probe_fail;
+
+ aus->spi_clk = clk_get_rate(clk);
+ at91_usart_spi_init(aus);
+
+ spin_lock_init(&aus->lock);
+ ret = devm_spi_register_master(&pdev->dev, controller);
+ if (ret)
+ goto fail_register_master;
+
+ dev_info(&pdev->dev,
+ "Atmel USART SPI Controller version 0x%x at 0x%08x (irq %d)\n",
+ at91_usart_spi_readl(aus, VERSION),
+ regs->start, irq);
+
+ return 0;
+
+fail_register_master:
+ clk_disable_unprepare(clk);
+at91_usart_spi_probe_fail:
+ spi_master_put(controller);
+ return ret;
+}
+
+static int at91_usart_spi_remove(struct platform_device *pdev)
+{
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct at91_usart_spi *aus = spi_master_get_devdata(master);
+
+ clk_disable_unprepare(aus->clk);
+
+ return 0;
+}
+
+static const struct of_device_id at91_usart_spi_dt_ids[] = {
+ { .compatible = "microchip,at91sam9g45-usart-spi"},
+ { /* sentinel */}
+};
+
+MODULE_DEVICE_TABLE(of, at91_usart_spi_dt_ids);
+
+static struct platform_driver at91_usart_spi_driver = {
+ .driver = {
+ .name = "at91_usart_spi",
+ },
+ .probe = at91_usart_spi_probe,
+ .remove = at91_usart_spi_remove,
+};
+
+module_platform_driver(at91_usart_spi_driver);
+
+MODULE_DESCRIPTION("Microchip AT91 USART SPI Controller driver");
+MODULE_AUTHOR("Radu Pirea <radu.pirea@microchip.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:at91_usart_spi");
--
2.17.1
^ permalink raw reply related
* [PATCH v7 4/6] MAINTAINERS: add at91 usart spi driver
From: Radu Pirea @ 2018-06-13 16:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180613163621.23995-1-radu.pirea@microchip.com>
Added entry for at91 usart mfd driver.
Signed-off-by: Radu Pirea <radu.pirea@microchip.com>
---
MAINTAINERS | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 12203d07c6af..dae31df711fb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9201,6 +9201,13 @@ F: drivers/mfd/at91-usart.c
F: include/dt-bindings/mfd/at91-usart.h
F: Documentation/devicetree/bindings/mfd/atmel-usart.txt
+MICROCHIP AT91 USART SPI DRIVER
+M: Radu Pirea <radu.pirea@microchip.com>
+L: linux-spi at vger.kernel.org
+S: Supported
+F: drivers/spi/spi-at91-usart.c
+F: Documentation/devicetree/bindings/mfd/atmel-usart.txt
+
MICROCHIP KSZ SERIES ETHERNET SWITCH DRIVER
M: Woojung Huh <Woojung.Huh@microchip.com>
M: Microchip Linux Driver Support <UNGLinuxDriver@microchip.com>
--
2.17.1
^ permalink raw reply related
* [PATCH v7 3/6] mfd: at91-usart: added mfd driver for usart
From: Radu Pirea @ 2018-06-13 16:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180613163621.23995-1-radu.pirea@microchip.com>
This mfd driver is just a wrapper over atmel_serial driver and
spi-at91-usart driver. Selection of one of the drivers is based on a
property from device tree. If the property is not specified, the default
driver is atmel_serial.
Signed-off-by: Radu Pirea <radu.pirea@microchip.com>
Acked-by: Rob Herring <robh@kernel.org>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>
---
drivers/mfd/Kconfig | 9 ++++++
drivers/mfd/Makefile | 1 +
drivers/mfd/at91-usart.c | 69 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 79 insertions(+)
create mode 100644 drivers/mfd/at91-usart.c
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index b860eb5aa194..a886672b960d 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -99,6 +99,15 @@ config MFD_AAT2870_CORE
additional drivers must be enabled in order to use the
functionality of the device.
+config MFD_AT91_USART
+ tristate "AT91 USART Driver"
+ select MFD_CORE
+ help
+ Select this to get support for AT91 USART IP. This is a wrapper
+ over at91-usart-serial driver and usart-spi-driver. Only one function
+ can be used at a time. The choice is done at boot time by the probe
+ function of this MFD driver according to a device tree property.
+
config MFD_ATMEL_FLEXCOM
tristate "Atmel Flexcom (Flexible Serial Communication Unit)"
select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index d9d2cf0d32ef..db1332aa96db 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -185,6 +185,7 @@ obj-$(CONFIG_MFD_SPMI_PMIC) += qcom-spmi-pmic.o
obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
obj-$(CONFIG_MFD_TPS65090) += tps65090.o
obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
+obj-$(CONFIG_MFD_AT91_USART) += at91-usart.o
obj-$(CONFIG_MFD_ATMEL_FLEXCOM) += atmel-flexcom.o
obj-$(CONFIG_MFD_ATMEL_HLCDC) += atmel-hlcdc.o
obj-$(CONFIG_MFD_ATMEL_SMC) += atmel-smc.o
diff --git a/drivers/mfd/at91-usart.c b/drivers/mfd/at91-usart.c
new file mode 100644
index 000000000000..3014ce532644
--- /dev/null
+++ b/drivers/mfd/at91-usart.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for AT91 USART
+ *
+ * Copyright (C) 2018 Microchip Technology
+ *
+ * Author: Radu Pirea <radu.pirea@microchip.com>
+ *
+ */
+
+#include <dt-bindings/mfd/at91-usart.h>
+
+#include <linux/module.h>
+#include <linux/mfd/core.h>
+#include <linux/property.h>
+
+static struct mfd_cell at91_usart_spi_subdev = {
+ .name = "at91_usart_spi",
+ .of_compatible = "microchip,at91sam9g45-usart-spi",
+ };
+
+static struct mfd_cell at91_usart_serial_subdev = {
+ .name = "atmel_usart_serial",
+ .of_compatible = "atmel,at91rm9200-usart-serial",
+ };
+
+static int at91_usart_mode_probe(struct platform_device *pdev)
+{
+ struct mfd_cell cell;
+ u32 opmode = AT91_USART_MODE_SERIAL;
+
+ device_property_read_u32(&pdev->dev, "atmel,usart-mode", &opmode);
+
+ switch (opmode) {
+ case AT91_USART_MODE_SPI:
+ cell = at91_usart_spi_subdev;
+ break;
+ case AT91_USART_MODE_SERIAL:
+ cell = at91_usart_serial_subdev;
+ break;
+ default:
+ break;
+ }
+
+ return devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO, &cell, 1,
+ NULL, 0, NULL);
+}
+
+static const struct of_device_id at91_usart_mode_of_match[] = {
+ { .compatible = "atmel,at91rm9200-usart" },
+ { .compatible = "atmel,at91sam9260-usart" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, at91_flexcom_of_match);
+
+static struct platform_driver at91_usart_mfd = {
+ .probe = at91_usart_mode_probe,
+ .driver = {
+ .name = "at91_usart_mode",
+ .of_match_table = at91_usart_mode_of_match,
+ },
+};
+
+module_platform_driver(at91_usart_mfd);
+
+MODULE_AUTHOR("Radu Pirea <radu.pirea@microchip.com>");
+MODULE_DESCRIPTION("AT91 USART MFD driver");
+MODULE_LICENSE("GPL v2");
--
2.17.1
^ permalink raw reply related
* [PATCH v7 2/6] dt-bindings: add binding for atmel-usart in SPI mode
From: Radu Pirea @ 2018-06-13 16:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180613163621.23995-1-radu.pirea@microchip.com>
This patch moves the bindings for serial from serial/atmel-usart.txt to
mfd/atmel-usart.txt and adds bindings for USART in SPI mode.
Signed-off-by: Radu Pirea <radu.pirea@microchip.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
.../bindings/{serial => mfd}/atmel-usart.txt | 25 +++++++++++++++++--
include/dt-bindings/mfd/at91-usart.h | 17 +++++++++++++
2 files changed, 40 insertions(+), 2 deletions(-)
rename Documentation/devicetree/bindings/{serial => mfd}/atmel-usart.txt (76%)
create mode 100644 include/dt-bindings/mfd/at91-usart.h
diff --git a/Documentation/devicetree/bindings/serial/atmel-usart.txt b/Documentation/devicetree/bindings/mfd/atmel-usart.txt
similarity index 76%
rename from Documentation/devicetree/bindings/serial/atmel-usart.txt
rename to Documentation/devicetree/bindings/mfd/atmel-usart.txt
index 7c0d6b2f53e4..3b9e18642c3b 100644
--- a/Documentation/devicetree/bindings/serial/atmel-usart.txt
+++ b/Documentation/devicetree/bindings/mfd/atmel-usart.txt
@@ -1,6 +1,6 @@
* Atmel Universal Synchronous Asynchronous Receiver/Transmitter (USART)
-Required properties:
+Required properties for USART:
- compatible: Should be "atmel,<chip>-usart" or "atmel,<chip>-dbgu"
The compatible <chip> indicated will be the first SoC to support an
additional mode or an USART new feature.
@@ -11,7 +11,13 @@ Required properties:
Required elements: "usart"
- clocks: phandles to input clocks.
-Optional properties:
+Required properties for USART in SPI mode:
+- #size-cells : Must be <0>
+- #address-cells : Must be <1>
+- cs-gpios: chipselects (internal cs not supported)
+- atmel,usart-mode : Must be <USART_MODE_SPI> (found in dt-bindings/mfd/at91-usart.h)
+
+Optional properties in serial mode:
- atmel,use-dma-rx: use of PDC or DMA for receiving data
- atmel,use-dma-tx: use of PDC or DMA for transmitting data
- {rts,cts,dtr,dsr,rng,dcd}-gpios: specify a GPIO for RTS/CTS/DTR/DSR/RI/DCD line respectively.
@@ -62,3 +68,18 @@ Example:
dma-names = "tx", "rx";
atmel,fifo-size = <32>;
};
+
+- SPI mode:
+ #include <dt-bindings/mfd/at91-usart.h>
+
+ spi0: spi at f001c000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "atmel,at91rm9200-usart", "atmel,at91sam9260-usart";
+ atmel,usart-mode = <USART_MODE_SPI>;
+ reg = <0xf001c000 0x100>;
+ interrupts = <12 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&usart0_clk>;
+ clock-names = "usart";
+ cs-gpios = <&pioB 3 0>;
+ };
diff --git a/include/dt-bindings/mfd/at91-usart.h b/include/dt-bindings/mfd/at91-usart.h
new file mode 100644
index 000000000000..2de5bc312e1e
--- /dev/null
+++ b/include/dt-bindings/mfd/at91-usart.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This header provides macros for AT91 USART DT bindings.
+ *
+ * Copyright (C) 2018 Microchip Technology
+ *
+ * Author: Radu Pirea <radu.pirea@microchip.com>
+ *
+ */
+
+#ifndef __DT_BINDINGS_AT91_USART_H__
+#define __DT_BINDINGS_AT91_USART_H__
+
+#define AT91_USART_MODE_SERIAL 0
+#define AT91_USART_MODE_SPI 1
+
+#endif /* __DT_BINDINGS_AT91_USART_H__ */
--
2.17.1
^ permalink raw reply related
* [PATCH v7 1/6] MAINTAINERS: add at91 usart mfd driver
From: Radu Pirea @ 2018-06-13 16:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180613163621.23995-1-radu.pirea@microchip.com>
Added entry for at91 usart mfd driver.
Signed-off-by: Radu Pirea <radu.pirea@microchip.com>
---
MAINTAINERS | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 8e2a2fddbd19..12203d07c6af 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9160,6 +9160,7 @@ M: Richard Genoud <richard.genoud@gmail.com>
S: Maintained
F: drivers/tty/serial/atmel_serial.c
F: drivers/tty/serial/atmel_serial.h
+F: Documentation/devicetree/bindings/mfd/atmel-usart.txt
MICROCHIP / ATMEL DMA DRIVER
M: Ludovic Desroches <ludovic.desroches@microchip.com>
@@ -9192,6 +9193,14 @@ S: Supported
F: drivers/mtd/nand/raw/atmel/*
F: Documentation/devicetree/bindings/mtd/atmel-nand.txt
+MICROCHIP AT91 USART MFD DRIVER
+M: Radu Pirea <radu.pirea@microchip.com>
+L: linux-kernel at vger.kernel.org
+S: Supported
+F: drivers/mfd/at91-usart.c
+F: include/dt-bindings/mfd/at91-usart.h
+F: Documentation/devicetree/bindings/mfd/atmel-usart.txt
+
MICROCHIP KSZ SERIES ETHERNET SWITCH DRIVER
M: Woojung Huh <Woojung.Huh@microchip.com>
M: Microchip Linux Driver Support <UNGLinuxDriver@microchip.com>
--
2.17.1
^ permalink raw reply related
* [PATCH v7 0/6] Driver for at91 usart in spi mode
From: Radu Pirea @ 2018-06-13 16:36 UTC (permalink / raw)
To: linux-arm-kernel
Hello,
This is the second version of driver. I added a mfd driver which by
default probes atmel_serial driver and if in dt is specified to probe
the spi driver, then the spi-at91-usart driver will be probed. The
compatible for atmel_serial is now the compatible for at91-usart mfd
driver and compatilbe for atmel_serial driver was changed in order to
keep the bindings for serial as they are.
Changes in v7:
- synced up SPDIX license with module license
- numbering of usart modes starts from 0 insteand of 1
Changes in v6:
- removed unused compatible strings from serial and spi drivers
Changes in v5:
- fixed usage of stdout-path property with atmel_serial driver
Changes in v4:
- modified the spi driver to use cs gpio support form spi subsystem
- fixed dma transfers for serial driver
- squashed binding for spi and serial and moved them to mfd/atmel-usart.txt
Changes in v3:
- fixed spi slaves probing
Changes in v2:
- added at91-usart mfd driver
- modified spi-at91-usart driver to work as mfd driver child
- modified atmel_serial driver to work as mfd driver child
Changes in v1:
- added spi-at91-usart driver
Radu Pirea (6):
MAINTAINERS: add at91 usart mfd driver
dt-bindings: add binding for atmel-usart in SPI mode
mfd: at91-usart: added mfd driver for usart
MAINTAINERS: add at91 usart spi driver
spi: at91-usart: add driver for at91-usart as spi
tty/serial: atmel: change the driver to work under at91-usart mfd
.../bindings/{serial => mfd}/atmel-usart.txt | 25 +-
MAINTAINERS | 16 +
drivers/mfd/Kconfig | 9 +
drivers/mfd/Makefile | 1 +
drivers/mfd/at91-usart.c | 69 +++
drivers/spi/Kconfig | 9 +
drivers/spi/Makefile | 1 +
drivers/spi/spi-at91-usart.c | 434 ++++++++++++++++++
drivers/tty/serial/Kconfig | 1 +
drivers/tty/serial/atmel_serial.c | 42 +-
include/dt-bindings/mfd/at91-usart.h | 17 +
11 files changed, 607 insertions(+), 17 deletions(-)
rename Documentation/devicetree/bindings/{serial => mfd}/atmel-usart.txt (76%)
create mode 100644 drivers/mfd/at91-usart.c
create mode 100644 drivers/spi/spi-at91-usart.c
create mode 100644 include/dt-bindings/mfd/at91-usart.h
--
2.17.1
^ permalink raw reply
* [linux-sunxi] Re: [PATCH v2 14/27] drm/sun4i: Don't check for panel or bridge on TV TCONs
From: Jernej Škrabec @ 2018-06-13 16:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAGb2v66QWr2CQhcfQ9BQds-0xeG=pS4MDzaNn7_HGz+q3gfkWA@mail.gmail.com>
Dne sreda, 13. junij 2018 ob 10:04:20 CEST je Chen-Yu Tsai napisal(a):
> On Wed, Jun 13, 2018 at 3:46 PM, Maxime Ripard
>
> <maxime.ripard@bootlin.com> wrote:
> > On Tue, Jun 12, 2018 at 10:00:23PM +0200, Jernej Skrabec wrote:
> >> TV TCONs are always connected to TV or HDMI encoder, so it doesn't make
> >> sense to check if panel or bridge is connected to them.
> >>
> >> Check if TCON has channel 0 and only then check for connected panel or
> >> bridges.
> >>
> >> Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
> >> ---
> >>
> >> drivers/gpu/drm/sun4i/sun4i_tcon.c | 12 +++++++++---
> >> 1 file changed, 9 insertions(+), 3 deletions(-)
> >>
> >> diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c
> >> b/drivers/gpu/drm/sun4i/sun4i_tcon.c index b1205a7bc20f..c9ffa5381185
> >> 100644
> >> --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
> >> +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
> >> @@ -1189,13 +1189,19 @@ static const struct component_ops sun4i_tcon_ops
> >> = {>>
> >> static int sun4i_tcon_probe(struct platform_device *pdev)
> >> {
> >>
> >> struct device_node *node = pdev->dev.of_node;
> >>
> >> + const struct sun4i_tcon_quirks *quirks;
> >>
> >> struct drm_bridge *bridge;
> >> struct drm_panel *panel;
> >> int ret;
> >>
> >> - ret = drm_of_find_panel_or_bridge(node, 1, 0, &panel, &bridge);
> >> - if (ret == -EPROBE_DEFER)
> >> - return ret;
> >> + quirks = of_device_get_match_data(&pdev->dev);
> >
> > We should probably check ofr the pointer value before dereferencing it.
>
> I think we've discussed this before. If the driver has data structures
> for all the supported compatible strings, and it is device tree only,
> then we should just let it blow up in the user's face, since they are
> obviously doing something they shouldn't be doing to get the driver
> to probe without a compatible string match.
TCON can't work with no quirks specified, since that would mean that neither
channels are present. Additionally, sun4i_tcon_bind() also doesn't check if
quirks are NULL or not. So I concur with Chen-Yu here.
Best regards,
Jernej
^ permalink raw reply
* [PATCH 2/2] mtd: rawnand: meson: add support for Amlogic NAND flash controller
From: Yixun Lan @ 2018-06-13 16:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180613161314.14894-1-yixun.lan@amlogic.com>
From: Liang Yang <liang.yang@amlogic.com>
Add initial support for the Amlogic NAND flash controller which found
in the Meson-GXBB/GXL/AXG SoCs.
Singed-off-by: Liang Yang <liang.yang@amlogic.com>
Signed-off-by: Yixun Lan <yixun.lan@amlogic.com>
---
drivers/mtd/nand/raw/Kconfig | 8 +
drivers/mtd/nand/raw/Makefile | 3 +
drivers/mtd/nand/raw/meson_nand.c | 1422 +++++++++++++++++++++++++++++
3 files changed, 1433 insertions(+)
create mode 100644 drivers/mtd/nand/raw/meson_nand.c
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index 19a2b283fbbe..b3c17a3ca8f4 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -534,4 +534,12 @@ config MTD_NAND_MTK
Enables support for NAND controller on MTK SoCs.
This controller is found on mt27xx, mt81xx, mt65xx SoCs.
+config MTD_NAND_MESON
+ tristate "Support for NAND flash controller on Amlogic's Meson SoCs"
+ depends on ARCH_MESON || COMPILE_TEST
+ select COMMON_CLK_REGMAP_MESON
+ select MFD_SYSCON
+ help
+ Enables support for NAND controller on Amlogic's Meson SoCs.
+
endif # MTD_NAND
diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
index 165b7ef9e9a1..cdf6162f38c3 100644
--- a/drivers/mtd/nand/raw/Makefile
+++ b/drivers/mtd/nand/raw/Makefile
@@ -1,5 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
+ccflags-$(CONFIG_MTD_NAND_MESON) += -I$(srctree)/drivers/clk/meson
+
obj-$(CONFIG_MTD_NAND) += nand.o
obj-$(CONFIG_MTD_NAND_ECC) += nand_ecc.o
obj-$(CONFIG_MTD_NAND_BCH) += nand_bch.o
@@ -56,6 +58,7 @@ obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o
obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/
obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o
obj-$(CONFIG_MTD_NAND_MTK) += mtk_ecc.o mtk_nand.o
+obj-$(CONFIG_MTD_NAND_MESON) += meson_nand.o
nand-objs := nand_base.o nand_bbt.o nand_timings.o nand_ids.o
nand-objs += nand_amd.o
diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c
new file mode 100644
index 000000000000..28abc3684772
--- /dev/null
+++ b/drivers/mtd/nand/raw/meson_nand.c
@@ -0,0 +1,1422 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Amlogic Meson Nand Flash Controller Driver
+ *
+ * Copyright (c) 2018 Amlogic, inc.
+ * Author: Liang Yang <liang.yang@amlogic.com>
+ */
+
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/module.h>
+#include <linux/iopoll.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include "clk-regmap.h"
+
+#define NFC_REG_CMD 0x00
+#define NFC_REG_CFG 0x04
+#define NFC_REG_DADR 0x08
+#define NFC_REG_IADR 0x0c
+#define NFC_REG_BUF 0x10
+#define NFC_REG_INFO 0x14
+#define NFC_REG_DC 0x18
+#define NFC_REG_ADR 0x1c
+#define NFC_REG_DL 0x20
+#define NFC_REG_DH 0x24
+#define NFC_REG_CADR 0x28
+#define NFC_REG_SADR 0x2c
+#define NFC_REG_PINS 0x30
+#define NFC_REG_VER 0x38
+
+
+#define NFC_CMD_DRD (0x8 << 14)
+#define NFC_CMD_IDLE (0xc << 14)
+#define NFC_CMD_DWR (0x4 << 14)
+#define NFC_CMD_CLE (0x5 << 14)
+#define NFC_CMD_ALE (0x6 << 14)
+#define NFC_CMD_ADL ((0 << 16) | (3 << 20))
+#define NFC_CMD_ADH ((1 << 16) | (3 << 20))
+#define NFC_CMD_AIL ((2 << 16) | (3 << 20))
+#define NFC_CMD_AIH ((3 << 16) | (3 << 20))
+#define NFC_CMD_SEED ((8 << 16) | (3 << 20))
+#define NFC_CMD_M2N ((0 << 17) | (2 << 20))
+#define NFC_CMD_N2M ((1 << 17) | (2 << 20))
+#define NFC_CMD_RB (1 << 20)
+#define NFC_CMD_IO6 ((0xb << 10) | (1 << 18))
+
+#define NFC_RB_USED (1 << 23)
+#define NFC_LARGE_PAGE (1 << 22)
+#define NFC_RW_OPS (2 << 20)
+
+#define NAND_TWB_TIME_CYCLE 10
+
+#define CMDRWGEN(cmd_dir, ran, bch, short_mode, page_size, pages) \
+ ( \
+ (cmd_dir) | \
+ ((ran) << 19) | \
+ ((bch) << 14) | \
+ ((short_mode) << 13) | \
+ (((page_size) & 0x7f) << 6) | \
+ ((pages) & 0x3f) \
+ )
+
+#define GENCMDDADDRL(adl, addr) ((adl) | ((addr) & 0xffff))
+#define GENCMDDADDRH(adh, addr) ((adh) | (((addr) >> 16) & 0xffff))
+#define GENCMDIADDRL(ail, addr) ((ail) | ((addr) & 0xffff))
+#define GENCMDIADDRH(aih, addr) ((aih) | (((addr) >> 16) & 0xffff))
+
+#define RB_STA(x) (1 << (26 + x))
+
+#define ECC_CHECK_RETURN_FF (-1)
+
+#define NAND_CE0 (0xe << 10)
+#define NAND_CE1 (0xd << 10)
+
+#define DMA_BUSY_TIMEOUT 0x100000
+
+#define MAX_CE_NUM 2
+#define RAN_ENABLE 1
+
+#define SD_EMMC_CLOCK 0x00
+#define CLK_ALWAYS_ON BIT(28)
+#define CLK_SELECT_NAND BIT(31)
+#define CLK_DIV_MASK GENMASK(5, 0)
+#define CLK_SRC_MASK GENMASK(7, 6)
+
+#define NFC_CLK_CYCLE 6
+
+/* nand flash controller delay 3 ns */
+#define NFC_DEFAULT_DELAY 3000
+
+#define MAX_ECC_INDEX 10
+
+#define MUX_CLK_NUM_PARENTS 2
+
+struct meson_nfc_info_format {
+ u16 info_bytes;
+ u8 zero_cnt; /* bit0~5 is valid */
+ struct ecc_sta {
+ u8 eccerr_cnt : 6;
+ u8 notused : 1;
+ u8 completed : 1;
+ } ecc;
+ u32 reserved;
+};
+
+#define PER_INFO_BYTE (sizeof(struct meson_nfc_info_format))
+
+struct meson_nfc_nand_chip {
+ struct list_head node;
+ struct nand_chip nand;
+ /*
+ * Then NAND controller support two oob modes:
+ * a) 2 user bytes with each ecc page;
+ * b) 16 user bytes with 1st ecc page and zero user byte
+ * with the other ecc pages.
+ * when using as mtd mode, the driver prefer to use 2 user bytes mode.
+ */
+ int user_mode;
+ int rand_mode; /* 0: disable scramble, 1: enable scramble */
+ int bch_mode;
+ int cs;
+
+ u8 *data_buf;
+ u8 *info_buf;
+};
+
+/*
+ * While booting from NAND, a page0 data is needed to tell ROM boot code
+ * to read SPL image, and the ROM boot code need to know which ecc mode
+ * is selected and whether scramble is enabled or not, and so on.
+ *
+ * So when updating SPL image, the driver need to store these informations
+ * into the page0, and SPL image will be loadded into next page - the page1.
+ */
+struct meson_nand_setup {
+ u32 d32;
+ u16 id;
+ u16 max;
+};
+
+struct meson_nand_page0 {
+ struct meson_nand_setup nand_setup;
+ unsigned char page_list[16];
+ unsigned short reserved[32];
+};
+
+struct meson_nand_ecc {
+ int bch;
+ int strength;
+ int parity;
+};
+
+struct meson_nfc_data {
+ struct meson_nand_ecc *ecc;
+ int ecc_num;
+ int bch_mode;
+ int short_bch;
+};
+
+struct meson_nfc_param {
+ int chip_select;
+ int rb_select;
+
+ int page_size;
+ int oob_size;
+ int ecc_size;
+ int ecc_bytes;
+
+ int rand_mode;
+ int oob_mode;
+ int bch_mode;
+ int ecc_step;
+
+ int ecc_max;
+};
+
+struct meson_nfc {
+ struct nand_hw_control controller;
+ struct clk *core_clk;
+ struct clk *device_clk;
+
+ struct device *dev;
+ void __iomem *reg_base;
+ struct regmap *reg_clk;
+
+ struct completion completion;
+ struct list_head chips;
+ struct meson_nfc_data *data;
+ struct meson_nfc_param param;
+ struct meson_nand_page0 *page0;
+
+ u8 *data_buf;
+ u8 *info_buf;
+};
+
+enum {
+ NFC_ECC_NONE = 0,
+ NFC_ECC_BCH8, /* bch8 with ecc page size of 512B */
+ NFC_ECC_BCH8_1K, /* bch8 with ecc page size of 1024B */
+ NFC_ECC_BCH24_1K,
+ NFC_ECC_BCH30_1K,
+ NFC_ECC_BCH40_1K,
+ NFC_ECC_BCH50_1K,
+ NFC_ECC_BCH60_1K,
+
+ /*
+ * Short mode is special only for page 0 when inplement booting
+ * from nand, which means a small size(384 bit / 8 = 48 Byte) of
+ * ecc page is used with a fixed ecc mode. rom code will use short mode
+ * to read page0 for getting nand parameters such as ecc, scramber, etc.
+ *
+ * Example, in GXL SoC, the first page adopt the short mode with
+ * 60bit ecc, while in AXG SoC, it adopt short mode with 8bit ecc.
+ */
+ NFC_ECC_BCH_SHORT,
+};
+
+enum {
+ NFC_USER2_OOB_BYTES = 2,
+ NFC_USER16_OOB_BYTES = 16,
+};
+
+#define MESON_ECC_DATA(b, s, p) \
+ { .bch = (b), .strength = (s), .parity = (p) }
+
+struct meson_nand_ecc meson_gxl_ecc[] = {
+ MESON_ECC_DATA(NFC_ECC_NONE, 0, 0),
+ MESON_ECC_DATA(NFC_ECC_BCH8, 8, 14),
+ MESON_ECC_DATA(NFC_ECC_BCH8_1K, 8, 14),
+ MESON_ECC_DATA(NFC_ECC_BCH24_1K, 24, 42),
+ MESON_ECC_DATA(NFC_ECC_BCH30_1K, 30, 54),
+ MESON_ECC_DATA(NFC_ECC_BCH40_1K, 40, 70),
+ MESON_ECC_DATA(NFC_ECC_BCH50_1K, 50, 88),
+ MESON_ECC_DATA(NFC_ECC_BCH60_1K, 60, 106),
+ MESON_ECC_DATA(NFC_ECC_BCH_SHORT, 0xff, 0xff),
+};
+
+struct meson_nand_ecc meson_axg_ecc[] = {
+ MESON_ECC_DATA(NFC_ECC_NONE, 0, 0),
+ MESON_ECC_DATA(NFC_ECC_BCH8, 8, 14),
+ MESON_ECC_DATA(NFC_ECC_BCH8_1K, 8, 14),
+ MESON_ECC_DATA(NFC_ECC_BCH_SHORT, 0xff, 0xff),
+};
+
+static inline struct meson_nfc_nand_chip *to_meson_nand(struct nand_chip *nand)
+{
+ return container_of(nand, struct meson_nfc_nand_chip, nand);
+}
+
+static int meson_nfc_page0_gen(struct meson_nfc *nfc)
+{
+ u32 cmd;
+
+ nfc->page0 = devm_kzalloc(nfc->dev,
+ sizeof(struct meson_nand_page0), GFP_KERNEL);
+ if(!nfc->page0)
+ return -ENOMEM;
+
+ cmd = CMDRWGEN(NFC_CMD_N2M, nfc->param.rand_mode,
+ nfc->param.bch_mode, 0,
+ nfc->param.ecc_size >> 3,
+ nfc->param.ecc_step);
+ cmd |= NFC_RB_USED | NFC_LARGE_PAGE | NFC_RW_OPS;
+ nfc->page0->nand_setup.d32 = cmd;
+
+ return 0;
+}
+
+static void meson_nfc_select_chip(struct mtd_info *mtd, int chip)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+ struct meson_nfc *nfc = nand_get_controller_data(nand);
+
+ if (chip != meson_chip->cs)
+ return;
+
+ nfc->param.chip_select = chip ? NAND_CE1 : NAND_CE0;
+ nfc->param.rb_select = chip ? NAND_CE1 : NAND_CE0;
+ nfc->param.oob_mode =
+ (meson_chip->user_mode == NFC_USER2_OOB_BYTES) ? 0 : 1;
+ nfc->param.rand_mode = meson_chip->rand_mode;
+ nfc->param.bch_mode = meson_chip->bch_mode;
+
+ nfc->param.ecc_step = mtd->writesize / nand->ecc.size;
+ nfc->param.ecc_size = nand->ecc.size;
+ nfc->param.ecc_bytes = nand->ecc.bytes;
+ nfc->param.page_size = mtd->writesize;
+ nfc->param.oob_size = mtd->oobsize;
+ nfc->param.ecc_max = nand->ecc.strength;
+
+ nfc->data_buf = meson_chip->data_buf;
+ nfc->info_buf = meson_chip->info_buf;
+}
+
+static inline void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
+{
+ writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff),
+ nfc->reg_base + NFC_REG_CMD);
+}
+
+static void meson_nfc_cmd_ctrl(struct mtd_info *mtd,
+ int cmd, unsigned int ctrl)
+{
+ struct meson_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
+
+ if (cmd == NAND_CMD_NONE)
+ return;
+
+ cmd = nfc->param.chip_select | (cmd & 0xff);
+ cmd |= (ctrl & NAND_CLE) ? NFC_CMD_CLE : NFC_CMD_ALE;
+
+ writel(cmd, nfc->reg_base + NFC_REG_CMD);
+}
+
+static inline void meson_nfc_cmd_seed(struct meson_nfc *nfc, u32 seed)
+{
+ writel(NFC_CMD_SEED | (0xc2 + (seed & 0x7fff)),
+ nfc->reg_base + NFC_REG_CMD);
+}
+
+static void meson_nfc_cmd_m2n(struct meson_nfc *nfc, int raw)
+{
+ u32 cmd, pagesize, pages, shortm = 0;
+ int bch = nfc->param.bch_mode;
+ int len = nfc->param.page_size;
+
+ pagesize = nfc->param.ecc_size;
+
+ if (unlikely(raw)) {
+ bch = NAND_ECC_NONE;
+ len = nfc->param.page_size + nfc->param.oob_size;
+ cmd = NFC_CMD_M2N |
+ (len & 0x3fff) | (nfc->param.rand_mode << 19);
+ writel(cmd, nfc->reg_base + NFC_REG_CMD);
+ return;
+ }
+
+ if (unlikely(bch == NFC_ECC_BCH_SHORT)) {
+ bch = nfc->data->short_bch;
+ pagesize = 384 >> 3;
+ pages = len / nfc->param.ecc_size;
+ memcpy(nfc->data_buf,
+ nfc->page0, sizeof(struct meson_nand_page0));
+ shortm = 1;
+ } else
+ pages = len / nfc->param.ecc_size;
+
+ cmd = CMDRWGEN(NFC_CMD_M2N,
+ nfc->param.rand_mode, bch, shortm, pagesize, pages);
+
+ writel(cmd, nfc->reg_base + NFC_REG_CMD);
+}
+
+static void meson_nfc_cmd_n2m(struct meson_nfc *nfc, int raw)
+{
+ u32 cmd, pagesize, pages, shortm = 0;
+ int bch = nfc->param.bch_mode;
+ int len = nfc->param.page_size;
+
+ pagesize = nfc->param.ecc_size;
+
+ if (unlikely(raw)) {
+ bch = NAND_ECC_NONE;
+ len = nfc->param.page_size + nfc->param.oob_size;
+ cmd = (len & 0x3fff) | (nfc->param.rand_mode << 19) |
+ NFC_CMD_N2M;
+ writel(cmd, nfc->reg_base + NFC_REG_CMD);
+ return;
+ }
+
+ if (unlikely(bch == NFC_ECC_BCH_SHORT)) {
+ bch = nfc->data->short_bch;
+ pagesize = 384 >> 3;
+ pages = len / nfc->param.ecc_size;
+ shortm = 1;
+ } else
+ pages = len / nfc->param.ecc_size;
+
+ cmd = CMDRWGEN(NFC_CMD_N2M,
+ nfc->param.rand_mode, bch, shortm, pagesize, pages);
+
+ writel(cmd, nfc->reg_base + NFC_REG_CMD);
+}
+
+static int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
+ unsigned int timeout_ms)
+{
+ u32 cmd_size = 0;
+ int ret;
+
+ /* wait cmd fifo is empty */
+ ret = readl_poll_timeout(nfc->reg_base + NFC_REG_CMD,
+ cmd_size,
+ !((cmd_size >> 22) & 0x1f),
+ 10, timeout_ms * 1000);
+ if (ret)
+ dev_err(nfc->dev, "wait for empty cmd FIFO time out\n");
+
+ return ret;
+}
+
+static int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
+{
+ meson_nfc_cmd_idle(nfc, 0);
+ meson_nfc_cmd_idle(nfc, 0);
+
+ return meson_nfc_wait_cmd_finish(nfc, DMA_BUSY_TIMEOUT);
+}
+
+static inline struct meson_nfc_info_format *nfc_info_ptr(struct meson_nfc *nfc,
+ int index)
+{
+ return (struct meson_nfc_info_format *) &nfc->info_buf[index * 8];
+}
+
+static u8 *meson_nfc_oob_ptr(struct meson_nfc *nfc, int i)
+{
+ int x, len;
+ int ecc_bytes = nfc->param.ecc_bytes, temp = nfc->param.ecc_size;
+
+ x = i ? 16 : 0;
+ len = (nfc->param.oob_mode) ? (temp * (i + 1) + ecc_bytes * i + x) :
+ (temp * (i + 1) + (ecc_bytes + 2) * i);
+
+ return nfc->data_buf + len;
+}
+
+static u8 *meson_nfc_data_ptr(struct meson_nfc *nfc, int i)
+{
+ int len, x;
+ int temp = nfc->param.ecc_size + nfc->param.ecc_bytes;
+
+ x = i ? 16 : 0;
+ len = nfc->param.oob_mode ? (temp * i + x) : (temp + 2) * i;
+
+ return nfc->data_buf + len;
+}
+
+static void meson_nfc_prase_data_oob(struct meson_nfc *nfc, u8 *buf, u8 *oob)
+{
+ int i, oob_len = 0;
+ u8 *dsrc, *osrc;
+
+ for (i = 0; i < nfc->param.ecc_step; i++) {
+ if (buf) {
+ dsrc = meson_nfc_data_ptr(nfc, i);
+ memcpy(buf, dsrc, nfc->param.ecc_size);
+ buf += nfc->param.ecc_size;
+ }
+
+ if (nfc->param.oob_mode)
+ oob_len = (i) ? nfc->param.ecc_bytes :
+ nfc->param.ecc_bytes + 16;
+ else
+ oob_len = nfc->param.ecc_bytes + 2;
+
+ osrc = meson_nfc_oob_ptr(nfc, i);
+ memcpy(oob, osrc, oob_len);
+ oob += oob_len;
+ }
+}
+
+static void meson_nfc_format_data_oob(struct meson_nfc *nfc,
+ const u8 *buf, u8 *oob)
+{
+ int i, oob_len = 0;
+ u8 *dsrc, *osrc;
+
+ for (i = 0; i < nfc->param.ecc_step; i++) {
+ if (buf) {
+ dsrc = meson_nfc_data_ptr(nfc, i);
+ memcpy(dsrc, buf, nfc->param.ecc_size);
+ buf += nfc->param.ecc_size;
+ }
+
+ if (nfc->param.oob_mode)
+ oob_len = i ? nfc->param.ecc_bytes :
+ nfc->param.ecc_bytes + 16;
+ else
+ oob_len = nfc->param.ecc_bytes + 2;
+
+ osrc = meson_nfc_oob_ptr(nfc, i);
+ memcpy(osrc, oob, oob_len);
+ oob += oob_len;
+ }
+}
+
+static int meson_nfc_queue_rb(struct meson_nfc *nfc)
+{
+ u32 cmd, cfg;
+ int ret = 0;
+
+ init_completion(&nfc->completion);
+
+ cfg = readl(nfc->reg_base + NFC_REG_CFG);
+ cfg |= (1 << 21);
+ writel(cfg, nfc->reg_base + NFC_REG_CFG);
+
+ meson_nfc_cmd_idle(nfc, NAND_TWB_TIME_CYCLE);
+ cmd = nfc->param.chip_select | NFC_CMD_CLE | (NAND_CMD_STATUS & 0xff);
+ writel(cmd, nfc->reg_base + NFC_REG_CMD);
+ meson_nfc_cmd_idle(nfc, NAND_TWB_TIME_CYCLE);
+
+ cmd = NFC_CMD_RB | NFC_CMD_IO6 | (1 << 16) | (0x18 & 0x1f);
+ writel(cmd, nfc->reg_base + NFC_REG_CMD);
+ meson_nfc_cmd_idle(nfc, 2);
+
+ ret = wait_for_completion_timeout(&nfc->completion,
+ msecs_to_jiffies(1000));
+ if (ret == 0) {
+ dev_err(nfc->dev, "wait nand irq timeout\n");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static void meson_nfc_set_user_byte(struct mtd_info *mtd,
+ struct nand_chip *chip, u8 *oob_buf)
+{
+ struct meson_nfc *nfc = nand_get_controller_data(chip);
+ struct meson_nfc_info_format *info;
+ int i, count;
+
+ if (nfc->param.oob_mode) {
+ memcpy(nfc->info_buf, oob_buf, 16);
+ return;
+ }
+
+ for (i = 0, count = 0; i < chip->ecc.steps; i++, count += 2) {
+ info = nfc_info_ptr(nfc, i);
+ info->info_bytes =
+ oob_buf[count] | (oob_buf[count + 1] << 8);
+ }
+}
+
+static void meson_nfc_get_user_byte(struct mtd_info *mtd,
+ struct nand_chip *chip, u8 *oob_buf)
+{
+ struct meson_nfc *nfc = nand_get_controller_data(chip);
+ struct meson_nfc_info_format *info;
+ int i, count;
+
+ if (nfc->param.oob_mode) {
+ memcpy(oob_buf, nfc->info_buf, 16);
+ return;
+ }
+
+ for (i = 0, count = 0; i < chip->ecc.steps; i++, count += 2) {
+ info = nfc_info_ptr(nfc, i);
+ oob_buf[count] = info->info_bytes & 0xff;
+ oob_buf[count + 1] = (info->info_bytes >> 8) & 0xff;
+ }
+}
+
+static int meson_nfc_ecc_correct(struct mtd_info *mtd,
+ struct nand_chip *chip)
+{
+ struct meson_nfc *nfc = nand_get_controller_data(chip);
+ struct meson_nfc_info_format *info;
+ u32 bitflips = 0, i;
+ u8 zero_cnt;
+
+ for (i = 0; i < nfc->param.ecc_step; i++) {
+ info = nfc_info_ptr(nfc, i);
+ if (info->ecc.eccerr_cnt == 0x3f) {
+ zero_cnt = info->zero_cnt & 0x3f;
+ if (nfc->param.rand_mode
+ && (zero_cnt < nfc->param.ecc_max))
+ return ECC_CHECK_RETURN_FF;
+ mtd->ecc_stats.failed++;
+ continue;
+ }
+ mtd->ecc_stats.corrected += info->ecc.eccerr_cnt;
+ bitflips = max_t(u32, bitflips, info->ecc.eccerr_cnt);
+ }
+
+ return bitflips;
+}
+
+static inline u8 meson_nfc_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct meson_nfc *nfc = nand_get_controller_data(chip);
+ u32 cmd;
+
+ cmd = nfc->param.chip_select | NFC_CMD_DRD | 0;
+ writel(cmd, nfc->reg_base + NFC_REG_CMD);
+
+ meson_nfc_cmd_idle(nfc, 0);
+ meson_nfc_cmd_idle(nfc, 0);
+
+ meson_nfc_wait_cmd_finish(nfc, 1000);
+
+ return readb(nfc->reg_base + NFC_REG_BUF);
+}
+
+static void meson_nfc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ buf[i] = meson_nfc_read_byte(mtd);
+}
+
+static void meson_nfc_write_byte(struct mtd_info *mtd, u8 byte)
+{
+ struct meson_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
+ u32 cmd;
+
+ meson_nfc_cmd_idle(nfc, NAND_TWB_TIME_CYCLE);
+
+ cmd = nfc->param.chip_select | NFC_CMD_DWR | (byte & 0xff);
+ writel(cmd, nfc->reg_base + NFC_REG_CMD);
+
+ meson_nfc_cmd_idle(nfc, NAND_TWB_TIME_CYCLE);
+ meson_nfc_cmd_idle(nfc, 0);
+
+ meson_nfc_wait_cmd_finish(nfc, 1000);
+}
+
+static void meson_nfc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ meson_nfc_write_byte(mtd, buf[i]);
+}
+
+static int meson_nfc_write_page_sub(struct mtd_info *mtd,
+ struct nand_chip *chip, const u8 *buf, int page, int raw)
+{
+ struct meson_nfc *nfc = nand_get_controller_data(chip);
+ dma_addr_t daddr, iaddr;
+ u32 cmd;
+ int ret;
+
+ nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+
+ daddr = dma_map_single(nfc->dev, (void *)nfc->data_buf,
+ mtd->writesize + mtd->oobsize, DMA_TO_DEVICE);
+ ret = dma_mapping_error(nfc->dev, daddr);
+ if (ret) {
+ dev_err(nfc->dev, "dma mapping error\n");
+ return -EINVAL;
+ }
+
+ iaddr = dma_map_single(nfc->dev, (void *)nfc->info_buf,
+ nfc->param.ecc_step * PER_INFO_BYTE, DMA_TO_DEVICE);
+ ret = dma_mapping_error(nfc->dev, iaddr);
+ if (ret) {
+ dev_err(nfc->dev, "dma mapping error\n");
+ return -EINVAL;
+ }
+
+ cmd = GENCMDDADDRL(NFC_CMD_ADL, daddr);
+ writel(cmd, nfc->reg_base + NFC_REG_CMD);
+ cmd = GENCMDDADDRH(NFC_CMD_ADH, daddr);
+ writel(cmd, nfc->reg_base + NFC_REG_CMD);
+
+ cmd = GENCMDIADDRL(NFC_CMD_AIL, iaddr);
+ writel(cmd, nfc->reg_base + NFC_REG_CMD);
+ cmd = GENCMDIADDRH(NFC_CMD_AIH, iaddr);
+ writel(cmd, nfc->reg_base + NFC_REG_CMD);
+
+ meson_nfc_cmd_seed(nfc, page);
+
+ meson_nfc_cmd_m2n(nfc, raw);
+
+ ret = meson_nfc_wait_dma_finish(nfc);
+
+ dma_unmap_single(nfc->dev, daddr,
+ mtd->writesize + mtd->oobsize, DMA_TO_DEVICE);
+ dma_unmap_single(nfc->dev, iaddr,
+ nfc->param.ecc_step * PER_INFO_BYTE, DMA_TO_DEVICE);
+
+ return nand_prog_page_end_op(chip);
+}
+
+static int meson_nfc_write_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, const u8 *buf, int oob_required, int page)
+{
+ struct meson_nfc *nfc = nand_get_controller_data(chip);
+ u8 *oob_buf = chip->oob_poi;
+
+ meson_nfc_format_data_oob(nfc, buf, oob_buf);
+
+ return meson_nfc_write_page_sub(mtd, chip, nfc->data_buf, page, 1);
+}
+
+static int meson_nfc_write_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, const u8 *buf,
+ int oob_required, int page)
+{
+ struct meson_nfc *nfc = nand_get_controller_data(chip);
+ u8 *oob_buf = chip->oob_poi;
+
+ memcpy(nfc->data_buf, buf, mtd->writesize);
+ meson_nfc_set_user_byte(mtd, chip, oob_buf);
+
+ return meson_nfc_write_page_sub(mtd, chip, nfc->data_buf, page, 0);
+}
+
+static void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc, int raw)
+{
+ struct meson_nfc_info_format *info;
+ int neccpages, i;
+
+ neccpages = raw ? 1 : nfc->param.ecc_step;
+
+ for (i = 0; i < neccpages; i++) {
+ info = nfc_info_ptr(nfc, neccpages - 1);
+ if (info->ecc.completed == 0)
+ dev_err(nfc->dev, "seems eccpage is invalid\n");
+ }
+}
+
+static int meson_nfc_read_page_sub(struct mtd_info *mtd,
+ struct nand_chip *chip, const u8 *buf, int page, int raw)
+{
+ struct meson_nfc *nfc = nand_get_controller_data(chip);
+ dma_addr_t daddr, iaddr;
+ u32 cmd;
+ int ret;
+
+ nand_read_page_op(chip, page, 0, NULL, 0);
+
+ daddr = dma_map_single(nfc->dev, nfc->data_buf,
+ mtd->writesize + mtd->oobsize, DMA_FROM_DEVICE);
+ ret = dma_mapping_error(nfc->dev, daddr);
+ if (ret) {
+ dev_err(nfc->dev, "dma mapping error\n");
+ return -EINVAL;
+ }
+
+ iaddr = dma_map_single(nfc->dev, nfc->info_buf,
+ nfc->param.ecc_step * PER_INFO_BYTE, DMA_FROM_DEVICE);
+ ret = dma_mapping_error(nfc->dev, iaddr);
+ if (ret) {
+ dev_err(nfc->dev, "dma mapping error\n");
+ return -EINVAL;
+ }
+
+ cmd = GENCMDDADDRL(NFC_CMD_ADL, daddr);
+ writel(cmd, nfc->reg_base + NFC_REG_CMD);
+ cmd = GENCMDDADDRH(NFC_CMD_ADH, daddr);
+ writel(cmd, nfc->reg_base + NFC_REG_CMD);
+
+ cmd = GENCMDIADDRL(NFC_CMD_AIL, iaddr);
+ writel(cmd, nfc->reg_base + NFC_REG_CMD);
+ cmd = GENCMDIADDRH(NFC_CMD_AIH, iaddr);
+ writel(cmd, nfc->reg_base + NFC_REG_CMD);
+
+ meson_nfc_cmd_seed(nfc, page);
+
+ meson_nfc_cmd_n2m(nfc, raw);
+
+ ret = meson_nfc_wait_dma_finish(nfc);
+
+ meson_nfc_queue_rb(nfc);
+
+ meson_nfc_check_ecc_pages_valid(nfc, raw);
+
+ dma_unmap_single(nfc->dev, daddr,
+ mtd->writesize + mtd->oobsize, DMA_FROM_DEVICE);
+ dma_unmap_single(nfc->dev, iaddr,
+ nfc->param.ecc_step * PER_INFO_BYTE, DMA_FROM_DEVICE);
+
+ return ret;
+}
+
+static int meson_nfc_read_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, u8 *buf, int oob_required, int page)
+{
+ struct meson_nfc *nfc = nand_get_controller_data(chip);
+ u8 *oob_buf = chip->oob_poi;
+ int ret;
+
+ ret = meson_nfc_read_page_sub(mtd, chip, nfc->data_buf, page, 1);
+ if (ret)
+ return ret;
+
+ meson_nfc_prase_data_oob(nfc, buf, oob_buf);
+
+ return 0;
+}
+
+static int meson_nfc_read_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, u8 *buf, int oob_required, int page)
+{
+ struct meson_nfc *nfc = nand_get_controller_data(chip);
+ u8 *oob_buf = chip->oob_poi;
+ int ret;
+
+ ret = meson_nfc_read_page_sub(mtd, chip, nfc->data_buf, page, 0);
+ if (ret)
+ return ret;
+
+ meson_nfc_get_user_byte(mtd, chip, oob_buf);
+
+ ret = meson_nfc_ecc_correct(mtd, chip);
+ if (ret == ECC_CHECK_RETURN_FF) {
+ if (buf)
+ memset(buf, 0xff, mtd->writesize);
+
+ memset(oob_buf, 0xff, mtd->oobsize);
+ return 0;
+ }
+ if (buf && (buf != nfc->data_buf))
+ memcpy(buf, nfc->data_buf, mtd->writesize);
+
+ return ret;
+}
+
+static int meson_nfc_read_oob_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, int page)
+{
+ return meson_nfc_read_page_raw(mtd, chip, NULL, 1, page);
+}
+
+static int meson_nfc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ return meson_nfc_read_page_hwecc(mtd, chip, NULL, 1, page);
+}
+
+static int meson_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct meson_nfc *nfc = nand_get_controller_data(chip);
+ int free_oob;
+
+ if (section > chip->ecc.steps)
+ return -ERANGE;
+
+ free_oob = nfc->param.oob_mode ? 16 : (chip->ecc.steps * 2);
+ oobregion->offset = section * chip->ecc.bytes + free_oob;
+ oobregion->length = chip->ecc.bytes;
+
+ return 0;
+}
+
+static int meson_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct meson_nfc *nfc = nand_get_controller_data(chip);
+
+ if (section > chip->ecc.steps)
+ return -ERANGE;
+
+ oobregion->offset = 0;
+ oobregion->length = nfc->param.oob_mode ? 16 : (chip->ecc.steps * 2);
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops meson_ooblayout_ops = {
+ .ecc = meson_ooblayout_ecc,
+ .free = meson_ooblayout_free,
+};
+
+static int meson_nfc_ecc_init(struct device *dev, struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+ struct meson_nfc *nfc = nand_get_controller_data(nand);
+ struct meson_nand_ecc *meson_ecc = nfc->data->ecc;
+ int num = nfc->data->ecc_num;
+ int nsectors, i, bytes;
+
+ /* support only ecc hw mode */
+ if (nand->ecc.mode != NAND_ECC_HW) {
+ dev_err(dev, "ecc.mode not supported\n");
+ return -EINVAL;
+ }
+
+ if (!nand->ecc.size || !nand->ecc.strength) {
+ /* use datasheet requirements */
+ nand->ecc.strength = nand->ecc_strength_ds;
+ nand->ecc.size = nand->ecc_step_ds;
+ }
+
+ if (nand->ecc.options & NAND_ECC_MAXIMIZE) {
+ nand->ecc.size = 1024;
+ nsectors = mtd->writesize / nand->ecc.size;
+
+ /* Reserve 2 bytes for each ecc page */
+ if (meson_chip->user_mode == NFC_USER2_OOB_BYTES)
+ bytes = mtd->oobsize - 2 * nsectors;
+ else
+ bytes = mtd->oobsize - 16;
+
+ bytes /= nsectors;
+
+ /* and bytes has to be even. */
+ if (bytes % 2)
+ bytes--;
+
+ nand->ecc.strength = bytes * 8 / fls(8 * nand->ecc.size);
+ } else {
+ if (nand->ecc.strength > meson_ecc[num - 1].strength) {
+ dev_err(dev, "not support ecc strength\n");
+ return -EINVAL;
+ }
+ }
+
+ for (i = 0; i < num; i++) {
+ if ((meson_ecc[i].strength == 0xff)
+ || (nand->ecc.strength < meson_ecc[i].strength))
+ break;
+ }
+
+ if (!i) {
+ nand->ecc.strength = 0;
+ } else {
+ nand->ecc.strength = meson_ecc[i - 1].strength;
+ nand->ecc.bytes = meson_ecc[i - 1].parity;
+ }
+
+ meson_chip->bch_mode = meson_ecc[i - 1].bch;
+
+ if (nand->ecc.size != 512 && nand->ecc.size != 1024)
+ return -EINVAL;
+
+ nsectors = mtd->writesize / nand->ecc.size;
+ bytes =(meson_chip->user_mode == NFC_USER2_OOB_BYTES) ? nsectors * 2 : 16;
+ if (mtd->oobsize < (nand->ecc.bytes * nsectors + bytes))
+ return -EINVAL;
+
+ return 0;
+}
+
+static const char * sd_emmc_ext_clk0_parent_names[MUX_CLK_NUM_PARENTS];
+
+static struct clk_regmap sd_emmc_c_ext_clk0_sel = {
+ .data = &(struct clk_regmap_mux_data){
+ .offset = SD_EMMC_CLOCK,
+ .mask = 0x3,
+ .shift = 6,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "sd_emmc_c_nand_clk_mux",
+ .ops = &clk_regmap_mux_ops,
+ .parent_names = sd_emmc_ext_clk0_parent_names,
+ .num_parents = ARRAY_SIZE(sd_emmc_ext_clk0_parent_names),
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static struct clk_regmap sd_emmc_c_ext_clk0_div = {
+ .data = &(struct clk_regmap_div_data){
+ .offset = SD_EMMC_CLOCK,
+ .shift = 0,
+ .width = 6,
+ .flags = CLK_DIVIDER_ROUND_CLOSEST | CLK_DIVIDER_ONE_BASED,
+ },
+ .hw.init = &(struct clk_init_data) {
+ .name = "sd_emmc_c_nand_clk_div",
+ .ops = &clk_regmap_divider_ops,
+ .parent_names = (const char *[]){ "sd_emmc_c_nand_clk_mux" },
+ .num_parents = 1,
+ .flags = CLK_SET_RATE_PARENT,
+ },
+};
+
+static int meson_nfc_clk_init(struct meson_nfc *nfc)
+{
+ struct clk_regmap *mux = &sd_emmc_c_ext_clk0_sel;
+ struct clk_regmap *div = &sd_emmc_c_ext_clk0_div;
+ struct clk *clk;
+ int i, ret;
+
+ /* request core clock */
+ nfc->core_clk = devm_clk_get(nfc->dev, "core");
+ if (IS_ERR(nfc->core_clk)) {
+ dev_err(nfc->dev, "failed to get core clk\n");
+ return PTR_ERR(nfc->core_clk);
+ }
+
+ /* init SD_EMMC_CLOCK to sane defaults w/min clock rate */
+ regmap_update_bits(nfc->reg_clk, 0,
+ CLK_SELECT_NAND | CLK_ALWAYS_ON | CLK_DIV_MASK,
+ CLK_SELECT_NAND | CLK_ALWAYS_ON | CLK_DIV_MASK);
+
+ /* get the mux parents */
+ for (i = 0; i < MUX_CLK_NUM_PARENTS; i++) {
+ char name[16];
+
+ snprintf(name, sizeof(name), "clkin%d", i);
+ clk = devm_clk_get(nfc->dev, name);
+ if (IS_ERR(clk)) {
+ if (clk != ERR_PTR(-EPROBE_DEFER))
+ dev_err(nfc->dev, "Missing clock %s\n", name);
+ return PTR_ERR(clk);
+ }
+
+ sd_emmc_ext_clk0_parent_names[i] = __clk_get_name(clk);
+ }
+
+ mux->map = nfc->reg_clk;
+ clk = devm_clk_register(nfc->dev, &mux->hw);
+ if (WARN_ON(IS_ERR(clk)))
+ return PTR_ERR(clk);
+
+ div->map = nfc->reg_clk;
+ nfc->device_clk = devm_clk_register(nfc->dev, &div->hw);
+ if (WARN_ON(IS_ERR(nfc->device_clk)))
+ return PTR_ERR(nfc->device_clk);
+
+ ret = clk_prepare_enable(nfc->core_clk);
+ if (ret) {
+ dev_err(nfc->dev, "failed to enable core clk\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(nfc->device_clk);
+ if (ret) {
+ dev_err(nfc->dev, "failed to enable device clk\n");
+ clk_disable_unprepare(nfc->core_clk);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void meson_nfc_disable_clk(struct meson_nfc *nfc)
+{
+ clk_disable_unprepare(nfc->device_clk);
+ clk_disable_unprepare(nfc->core_clk);
+}
+
+static int meson_nfc_buffer_init(struct mtd_info *mtd)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
+ struct meson_nfc *nfc = nand_get_controller_data(nand);
+ struct device *dev = nfc->dev;
+ int info_bytes, page_bytes;
+ int nsectors;
+
+ nsectors = mtd->writesize / nand->ecc.size;
+ info_bytes = nsectors * PER_INFO_BYTE;
+ page_bytes = mtd->writesize + mtd->oobsize;
+
+ if ((meson_chip->data_buf) && (meson_chip->info_buf))
+ return 0;
+
+ meson_chip->data_buf = devm_kzalloc(dev, page_bytes, GFP_KERNEL);
+ if (!meson_chip->data_buf)
+ return -ENOMEM;
+
+ meson_chip->info_buf = devm_kzalloc(dev, info_bytes, GFP_KERNEL);
+ if (!meson_chip->info_buf)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int meson_nfc_calc_set_timing(struct meson_nfc *nfc,
+ int rc_min, int rea_max, int rhoh_min)
+{
+ int div, bt_min, bt_max, bus_timing;
+ int ret;
+
+ div = DIV_ROUND_UP((rc_min / 1000), NFC_CLK_CYCLE);
+ ret = clk_set_rate(nfc->device_clk, 1000000000 / div);
+ if (ret) {
+ dev_err(nfc->dev, "failed to set nand clock rate\n");
+ return ret;
+ }
+
+ bt_min = (rea_max + NFC_DEFAULT_DELAY) / div;
+ bt_max = (NFC_DEFAULT_DELAY + rhoh_min + rc_min / 2) / div;
+
+ bt_min = DIV_ROUND_UP(bt_min, 1000);
+ bt_max = DIV_ROUND_UP(bt_max, 1000);
+
+ if (bt_max < bt_min)
+ return -EINVAL;
+
+ bus_timing = (bt_min + bt_max) / 2 + 1;
+
+ writel((1 << 21), nfc->reg_base + NFC_REG_CFG);
+ writel((NFC_CLK_CYCLE - 1) | (bus_timing << 5),
+ nfc->reg_base + NFC_REG_CFG);
+
+ writel((1 << 31), nfc->reg_base + NFC_REG_CMD);
+
+ return 0;
+}
+
+static int meson_nfc_setup_data_interface(struct mtd_info *mtd, int csline,
+ const struct nand_data_interface *conf)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct meson_nfc *nfc = nand_get_controller_data(nand);
+ const struct nand_sdr_timings *timings;
+
+ timings = nand_get_sdr_timings(conf);
+ if (IS_ERR(timings))
+ return -ENOTSUPP;
+
+ if (csline == NAND_DATA_IFACE_CHECK_ONLY)
+ return 0;
+
+ meson_nfc_calc_set_timing(nfc, timings->tRC_min,
+ timings->tREA_max, timings->tRHOH_min);
+
+ return 0;
+}
+
+static int meson_nfc_get_nand_chip_dts(struct meson_nfc *nfc,
+ struct meson_nfc_nand_chip *chip, struct device_node *np)
+{
+ struct device *dev = nfc->dev;
+
+ if (of_property_read_u32(np, "reg", &chip->cs)) {
+ dev_err(dev, "can not get ce number\n");
+ return -EINVAL;
+ }
+
+ if (chip->cs > MAX_CE_NUM) {
+ dev_err(dev, "ce number is beyond\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(np, "meson-nand-user-mode", &chip->user_mode)) {
+ dev_err(dev, "can not get user oob mode\n");
+ return -EINVAL;
+ }
+
+ if ((chip->user_mode != NFC_USER2_OOB_BYTES)
+ || (chip->user_mode != NFC_USER16_OOB_BYTES))
+ chip->user_mode = NFC_USER2_OOB_BYTES;
+
+ if (of_property_read_u32(np, "meson-nand-ran-mode", &chip->rand_mode)) {
+ dev_err(dev, "can not get scramble mode\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int meson_nfc_nand_chip_init(struct device *dev, struct meson_nfc *nfc,
+ struct device_node *np)
+{
+ struct meson_nfc_nand_chip *chip;
+ struct nand_chip *nand;
+ struct mtd_info *mtd;
+ int ret;
+
+ chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ ret = meson_nfc_get_nand_chip_dts(nfc, chip, np);
+ if (ret)
+ return ret;
+
+ nand = &chip->nand;
+ nand_set_flash_node(nand, np);
+ nand_set_controller_data(nand, nfc);
+
+ nand->options |= NAND_USE_BOUNCE_BUFFER;
+ nand->select_chip = meson_nfc_select_chip;
+ nand->write_byte = meson_nfc_write_byte;
+ nand->write_buf = meson_nfc_write_buf;
+ nand->read_byte = meson_nfc_read_byte;
+ nand->read_buf = meson_nfc_read_buf;
+ nand->cmd_ctrl = meson_nfc_cmd_ctrl;
+ nand->setup_data_interface = meson_nfc_setup_data_interface;
+
+ nand->chip_delay = 200;
+ nand->ecc.mode = NAND_ECC_HW;
+
+ nand->ecc.write_page_raw = meson_nfc_write_page_raw;
+ nand->ecc.write_page = meson_nfc_write_page_hwecc;
+ nand->ecc.write_oob_raw = nand_write_oob_std;
+ nand->ecc.write_oob = nand_write_oob_std;
+
+ nand->ecc.read_page_raw = meson_nfc_read_page_raw;
+ nand->ecc.read_page = meson_nfc_read_page_hwecc;
+ nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
+ nand->ecc.read_oob = meson_nfc_read_oob;
+
+ mtd = nand_to_mtd(nand);
+ mtd->owner = THIS_MODULE;
+ mtd->dev.parent = dev;
+ mtd->name = devm_kasprintf(nfc->dev, GFP_KERNEL,
+ "%s:nand", dev_name(dev));
+ if (!mtd->name) {
+ dev_err(nfc->dev, "Failed to allocate mtd->name\n");
+ return -ENOMEM;
+ }
+
+ mtd_set_ooblayout(mtd, &meson_ooblayout_ops);
+
+ ret = nand_scan_ident(mtd, 1, NULL);
+ if (ret) {
+ dev_err(dev, "failed to can ident\n");
+ return -ENODEV;
+ }
+
+ /* store bbt magic in page, cause OOB is not protected */
+ if (nand->bbt_options & NAND_BBT_USE_FLASH)
+ nand->bbt_options |= NAND_BBT_NO_OOB;
+
+ nand->options |= NAND_NO_SUBPAGE_WRITE;
+
+ ret = meson_nfc_ecc_init(dev, mtd);
+ if (ret) {
+ dev_err(dev, "failed to ecc init\n");
+ return -EINVAL;
+ }
+
+ if (nand->options & NAND_BUSWIDTH_16) {
+ dev_err(dev, "16bits buswidth not supported");
+ return -EINVAL;
+ }
+
+ ret = meson_nfc_buffer_init(mtd);
+ if (ret)
+ return -ENOMEM;
+
+ ret = nand_scan_tail(mtd);
+ if (ret)
+ return -ENODEV;
+
+ ret = mtd_device_register(mtd, NULL, 0);
+ if (ret) {
+ dev_err(dev, "failed to register mtd device: %d\n", ret);
+ nand_release(mtd);
+ return ret;
+ }
+
+ list_add_tail(&chip->node, &nfc->chips);
+
+ return 0;
+}
+
+static int meson_nfc_nand_chips_init(struct device *dev, struct meson_nfc *nfc)
+{
+ struct device_node *np = dev->of_node;
+ struct device_node *nand_np;
+ int ret;
+
+ for_each_child_of_node(np, nand_np) {
+ ret = meson_nfc_nand_chip_init(dev, nfc, nand_np);
+ if (ret) {
+ of_node_put(nand_np);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static irqreturn_t meson_nfc_irq(int irq, void *id)
+{
+ struct meson_nfc *nfc = id;
+ u32 cfg;
+
+ cfg = readl(nfc->reg_base + NFC_REG_CFG);
+ cfg |= (1 << 21);
+ writel(cfg, nfc->reg_base + NFC_REG_CFG);
+
+ complete(&nfc->completion);
+ return IRQ_HANDLED;
+}
+
+static const struct meson_nfc_data meson_gxl_data = {
+ .short_bch = NFC_ECC_BCH60_1K,
+ .ecc = meson_gxl_ecc,
+ .ecc_num = ARRAY_SIZE(meson_gxl_ecc),
+};
+
+static const struct meson_nfc_data meson_axg_data = {
+ .short_bch = NFC_ECC_BCH8_1K,
+ .ecc = meson_axg_ecc,
+ .ecc_num = ARRAY_SIZE(meson_axg_ecc),
+};
+
+static const struct of_device_id meson_nfc_id_table[] = {
+ {
+ .compatible = "amlogic,meson-gxl-nfc",
+ .data = &meson_gxl_data,
+ }, {
+ .compatible = "amlogic,meson-axg-nfc",
+ .data = &meson_axg_data,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, meson_nfc_id_table);
+
+static int meson_nfc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct meson_nfc *nfc;
+ struct resource *res;
+ const struct of_device_id *of_nfc_id;
+ int ret, irq;
+
+ nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
+ if (!nfc)
+ return -ENOMEM;
+
+ of_nfc_id = of_match_device(meson_nfc_id_table, &pdev->dev);
+ if (!of_nfc_id)
+ return -ENODEV;
+
+ nfc->data = (struct meson_nfc_data *)of_nfc_id->data;
+
+ spin_lock_init(&nfc->controller.lock);
+ init_waitqueue_head(&nfc->controller.wq);
+ INIT_LIST_HEAD(&nfc->chips);
+
+ nfc->dev = dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "Failed to nfc reg resource\n");
+ return -EINVAL;
+ }
+
+ nfc->reg_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(nfc->reg_base)) {
+ dev_err(dev, "Failed to lookup nfi reg base\n");
+ return PTR_ERR(nfc->reg_base);
+ }
+
+ nfc->reg_clk = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "amlogic,mmc-syscon");
+ if (IS_ERR(nfc->reg_clk)) {
+ dev_err(dev, "Failed to lookup clock base\n");
+ return PTR_ERR(nfc->reg_clk);
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "no nfi irq resource\n");
+ return -EINVAL;
+ }
+
+ ret = meson_nfc_clk_init(nfc);
+ if (ret) {
+ dev_err(dev, "failed to initialize nand clk\n");
+ goto err_clk;
+ }
+
+ ret = devm_request_irq(dev, irq, meson_nfc_irq, 0, dev_name(dev), nfc);
+ if (ret) {
+ dev_err(dev, "failed to request nfi irq\n");
+ ret = -EINVAL;
+ goto err_clk;
+ }
+
+ ret = dma_set_mask(dev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(dev, "failed to set dma mask\n");
+ goto err_clk;
+ }
+
+ platform_set_drvdata(pdev, nfc);
+
+ ret = meson_nfc_nand_chips_init(dev, nfc);
+ if (ret) {
+ dev_err(dev, "failed to init nand chips\n");
+ goto err_clk;
+ }
+
+ meson_nfc_page0_gen(nfc);
+ return 0;
+
+err_clk:
+ clk_disable_unprepare(nfc->device_clk);
+ clk_disable_unprepare(nfc->core_clk);
+
+ return ret;
+}
+
+static int meson_nfc_remove(struct platform_device *pdev)
+{
+ struct meson_nfc *nfc = platform_get_drvdata(pdev);
+ struct meson_nfc_nand_chip *chip;
+
+ while (!list_empty(&nfc->chips)) {
+ chip = list_first_entry(&nfc->chips, struct meson_nfc_nand_chip,
+ node);
+ nand_release(nand_to_mtd(&chip->nand));
+ list_del(&chip->node);
+ }
+
+ meson_nfc_disable_clk(nfc);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver meson_nfc_driver = {
+ .probe = meson_nfc_probe,
+ .remove = meson_nfc_remove,
+ .driver = {
+ .name = "meson_nand",
+ .of_match_table = meson_nfc_id_table,
+ },
+};
+
+module_platform_driver(meson_nfc_driver);
+
+MODULE_LICENSE("Dual MIT/GPL");
+MODULE_AUTHOR("Liang Yang <liang.yang@amlogic.com>");
+MODULE_DESCRIPTION("Amlogic's Meson NAND Flash Controller driver");
--
2.17.1
^ permalink raw reply related
* [PATCH 1/2] dt-bindings: nand: meson: add Amlogic NAND controller driver
From: Yixun Lan @ 2018-06-13 16:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180613161314.14894-1-yixun.lan@amlogic.com>
From: Liang Yang <liang.yang@amlogic.com>
Add Amlogic NAND controller dt-bindings for Meson SoC,
Current this driver support GXBB/GXL/AXG platform.
Signed-off-by: Liang Yang <liang.yang@amlogic.com>
Signed-off-by: Yixun Lan <yixun.lan@amlogic.com>
---
.../bindings/mtd/amlogic,meson-nand.txt | 118 ++++++++++++++++++
1 file changed, 118 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mtd/amlogic,meson-nand.txt
diff --git a/Documentation/devicetree/bindings/mtd/amlogic,meson-nand.txt b/Documentation/devicetree/bindings/mtd/amlogic,meson-nand.txt
new file mode 100644
index 000000000000..eac9f9433d5d
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/amlogic,meson-nand.txt
@@ -0,0 +1,118 @@
+Amlogic NAND Flash Controller (NFC) for GXBB/GXL/AXG family SoCs
+
+This file documents the properties in addition to those available in
+the MTD NAND bindings.
+
+Required properties:
+- compatible : contains one of:
+ - "amlogic,meson-gxl-nfc"
+ - "amlogic,meson-axg-nfc"
+- clocks :
+ A list of phandle + clock-specifier pairs for the clocks listed
+ in clock-names.
+
+- clock-names: Should contain the following:
+ "core" - NFC module gate clock
+ "clkin0" - Parent clock of internal mux
+ "clkin1" - Other parent clock of internal mux
+
+- pins : Select pins which NFC need.
+- nand_pins: Detail NAND pins information.
+ nand_pins: nand {
+ mux {
+ groups = "emmc_nand_d0",
+ "emmc_nand_d1",
+ "emmc_nand_d2",
+ "emmc_nand_d3",
+ "emmc_nand_d4",
+ "emmc_nand_d5",
+ "emmc_nand_d6",
+ "emmc_nand_d7",
+ "nand_ce0",
+ "nand_rb0",
+ "nand_ale",
+ "nand_cle",
+ "nand_wen_clk",
+ "nand_ren_wr";
+ function = "nand";
+ };
+ };
+
+- amlogic,mmc-syscon : Required for NAND clocks, it's shared with SD/eMMC
+ controller port C
+
+Optional children nodes:
+Children nodes represent the available nand chips.
+
+Optional properties:
+- meson-nand-user-mode :
+ only set 2 or 16 which mean the way of reading OOB bytes by NFC.
+- meson-nand-ran-mode :
+ setting 0 or 1, means disable/enable scrambler which keeps the balence
+ of 0 and 1
+
+Other properties:
+see Documentation/devicetree/bindings/mtd/nand.txt for generic bindings.
+
+Example demonstrate on AXG SoC:
+
+ sd_emmc_c: mmc at 7000 {
+ compatible = "simple-bus", "syscon";
+ reg = <0x0 0x7000 0x0 0x800>;
+ status = "okay";
+ };
+
+ nand: nfc at 7800 {
+ compatible = "amlogic,meson-axg-nfc";
+ reg = <0x0 0x7800 0x0 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <GIC_SPI 34 IRQ_TYPE_EDGE_RISING>;
+ status = "disabled";
+ clocks = <&clkc CLKID_SD_EMMC_C>,
+ <&clkc CLKID_SD_EMMC_C_CLK0>,
+ <&clkc CLKID_FCLK_DIV2>;
+ clock-names = "core", "clkin0", "clkin1";
+ amlogic,mmc-syscon = <&sd_mmc_c>;
+
+ status = "okay";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&nand_pins>;
+
+ nand at 0 {
+ reg = <0>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ nand-on-flash-bbt;
+ nand-ecc-mode = "hw";
+ nand-ecc-strength = <8>;
+ nand-ecc-step-size = <1024>;
+
+ meson-nand-user-mode = <2>;
+ meson-nand-ran-mode = <1>;
+
+ partition at 0 {
+ label = "boot";
+ reg = <0x00000000 0x00200000>;
+ read-only;
+ };
+ partition at 200000 {
+ label = "env";
+ reg = <0x00200000 0x00400000>;
+ };
+ partition at 600000 {
+ label = "system";
+ reg = <0x00600000 0x00a00000>;
+ };
+ partition at 1000000 {
+ label = "rootfs";
+ reg = <0x01000000 0x03000000>;
+ };
+ partition at 4000000 {
+ label = "media";
+ reg = <0x04000000 0x8000000>;
+ };
+ };
+ };
--
2.17.1
^ permalink raw reply related
* [PATCH 0/2] mtd: rawnand: meson: add Amlogic NAND driver support
From: Yixun Lan @ 2018-06-13 16:13 UTC (permalink / raw)
To: linux-arm-kernel
These two patches try to add initial NAND driver support for Amlogic Meson
SoCs, current it has been tested on GXL(p212) and AXG(s400) platform.
Liang Yang (2):
dt-bindings: nand: meson: add Amlogic NAND controller driver
mtd: rawnand: meson: add support for Amlogic NAND flash controller
.../bindings/mtd/amlogic,meson-nand.txt | 118 ++
drivers/mtd/nand/raw/Kconfig | 8 +
drivers/mtd/nand/raw/Makefile | 3 +
drivers/mtd/nand/raw/meson_nand.c | 1422 +++++++++++++++++
4 files changed, 1551 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mtd/amlogic,meson-nand.txt
create mode 100644 drivers/mtd/nand/raw/meson_nand.c
--
2.17.1
^ permalink raw reply
* [PATCH V2 2/2] efi/fb: Convert PCI bus address to resource if translated by the bridge
From: Bartlomiej Zolnierkiewicz @ 2018-06-13 16:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAKv+Gu9+M8zOtACPQ4_UMgp-M1LfZnciYe1VTQNqn_zNWJea4g@mail.gmail.com>
On Wednesday, June 13, 2018 05:45:48 PM Ard Biesheuvel wrote:
> On 18 May 2018 at 16:17, Sinan Kaya <okaya@codeaurora.org> wrote:
> > A host bridge is allowed to remap BAR addresses using _TRA attribute in
> > _CRS windows.
> >
> > pci_bus 0000:00: root bus resource [mem 0x80100100000-0x8011fffffff window] (bus address [0x00100000-0x1fffffff])
> > pci 0000:02:00.0: reg 0x10: [mem 0x8011e000000-0x8011effffff]
> >
> > When a VGA device is behind such a host bridge and the resource is
> > translated efifb driver is trying to do ioremap against bus address
> > rather than the resource address and is failing to probe.
> >
> > efifb: probing for efifb
> > efifb: cannot reserve video memory at 0x1e000000
> > efifb: framebuffer at 0x1e000000, using 1920k, total 1875k
> > efifb: mode is 800x600x32, linelength=3200, pages=1
> > efifb: scrolling: redraw
> > efifb: Truecolor: size=8:8:8:8, shift=24:16:8:0
> >
> > Use the host bridge offset information to convert bus address to
> > resource address in the fixup.
> >
> > Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
>
> Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
>
> Bartlomiej, could you please take these via the fbdev tree for v4.19?
Sure, I will queue it after the current merge window.
> Peter already gave his ack but Sinan dropped it (presumably because of
> the split in v2)
Peter, can I (re)add your ACK to V2 patches?
Best regards,
--
Bartlomiej Zolnierkiewicz
Samsung R&D Institute Poland
Samsung Electronics
^ permalink raw reply
* [PATCH v3 0/6] add virt-dma support for imx-sdma
From: Lucas Stach @ 2018-06-13 16:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1528822708.13468.9.camel@nxp.com>
Hi Robin,
Am Dienstag, den 12.06.2018, 08:58 +0000 schrieb Robin Gong:
> Hi Lucas,
> Is the below DEAD LOCK issue same as your side? If yes, then
> I'm afraid that we have to make another patch for uart to split dma
> functions in uart driver out of the code area which protected by
> port.lock. The warning make sense since allocate sdma bd memory
> dynamically in virt-dma instead of static allocated as before. I'll
> make another uart patch into my next version patchset.
Yes, I see the same lockdep splat on my system. Though I'm not sure if
this is really the root cause of the issue I see.
I'm getting timeouts for a serdev attached device and I don't think
this system ever enters memory reclaim, so the issue reported by
lockdep seems to be a mostly theoretical on this system. But lets see
how far I get when you fixed this issue, maybe the real bug is hiding
behind this one.
Regards,
Lucas
^ permalink raw reply
* [linux-sunxi] Re: [PATCH v2 24/27] drm: of: Export drm_crtc_port_mask()
From: Jernej Škrabec @ 2018-06-13 16:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180613073605.gnhcwrywooxhyryj@flea>
Dne sreda, 13. junij 2018 ob 09:36:05 CEST je Maxime Ripard napisal(a):
> On Tue, Jun 12, 2018 at 10:00:33PM +0200, Jernej Skrabec wrote:
> > Function is useful when drm_of_find_possible_crtcs() can't be used and
> > custom parsing is needed. This can happen for example when there is a
> > node with multiple muxes between crtc and encoder.
> >
> > Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
> > ---
> >
> > drivers/gpu/drm/drm_of.c | 4 ++--
> > include/drm/drm_of.h | 8 ++++++++
> > 2 files changed, 10 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c
> > index 1fe122461298..2e9cea3287b2 100644
> > --- a/drivers/gpu/drm/drm_of.c
> > +++ b/drivers/gpu/drm/drm_of.c
> > @@ -22,8 +22,8 @@ static void drm_release_of(struct device *dev, void
> > *data)>
> > * Given a port OF node, return the possible mask of the corresponding
> > * CRTC within a device's list of CRTCs. Returns zero if not found.
> > */
> >
> > -static uint32_t drm_crtc_port_mask(struct drm_device *dev,
> > - struct device_node *port)
> > +uint32_t drm_crtc_port_mask(struct drm_device *dev,
> > + struct device_node *port)
>
> It should probably be exported too?
Yes, of course. It will be in next version.
Best regards,
Jernej
^ permalink raw reply
* [linux-sunxi] Re: [PATCH v2 04/27] dt-bindings: display: sunxi-drm: Add TCON TOP description
From: Jernej Škrabec @ 2018-06-13 16:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180613073456.jk72zw4rt4ysjco7@flea>
Hi,
Dne sreda, 13. junij 2018 ob 09:34:56 CEST je Maxime Ripard napisal(a):
> Hi,
>
> Thanks for working on this!
>
> On Tue, Jun 12, 2018 at 10:00:13PM +0200, Jernej Skrabec wrote:
> > TCON TOP main purpose is to configure whole display pipeline. It
> > determines relationships between mixers and TCONs, selects source TCON
> > for HDMI, muxes LCD and TV encoder GPIO output, selects TV encoder
> > clock source and contains additional TV TCON and DSI gates.
> >
> > Signed-off-by: Jernej Skrabec <jernej.skrabec@siol.net>
> > ---
> >
> > .../bindings/display/sunxi/sun4i-drm.txt | 45 +++++++++++++++++++
> > include/dt-bindings/clock/sun8i-tcon-top.h | 11 +++++
> > 2 files changed, 56 insertions(+)
> > create mode 100644 include/dt-bindings/clock/sun8i-tcon-top.h
> >
> > diff --git a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
> > b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt index
> > 3346c1e2a7a0..ef64c589a4b3 100644
> > --- a/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
> > +++ b/Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
> >
> > @@ -187,6 +187,51 @@ And on the A23, A31, A31s and A33, you need one more
clock line:
> > - 'lvds-alt': An alternative clock source, separate from the TCON
> > channel 0
> >
> > clock, that can be used to drive the LVDS clock
> >
> > +TCON TOP
> > +--------
> > +
> > +TCON TOPs main purpose is to configure whole display pipeline. It
> > determines +relationships between mixers and TCONs, selects source TCON
> > for HDMI, muxes +LCD and TV encoder GPIO output, selects TV encoder clock
> > source and contains +additional TV TCON and DSI gates.
> > +
> > +It allows display pipeline to be configured in very different ways:
> > +
> > + / LCD0/LVDS0
> > + / TCON-LCD0
> > + | \ MIPI DSI
> > + mixer0 |
> > + \ / TCON-LCD1 - LCD1/LVDS1
> > + TCON-TOP
> > + / \ TCON-TV0 - TVE0/RGB
> > + mixer1 | \
> > + | TCON-TOP - HDMI
> > + | /
> > + \ TCON-TV1 - TVE1/RGB
> > +
> > +Note that both TCON TOP references same physical unit.
> > +
> > +Required properties:
> > + - compatible: value must be one of:
> > + * allwinner,sun8i-r40-tcon-top
> > + - reg: base address and size of the memory-mapped region.
> > + - clocks: phandle to the clocks feeding the TCON TOP
> > + * bus: TCON TOP interface clock
> > + - clock-names: clock name mentioned above
> > + - resets: phandle to the reset line driving the DRC
>
> s/DRC/TCON TOP/ ?
Yes, copy & paste issue
>
> > + * rst: TCON TOP reset line
>
> Remaining consistent with the clock name would be great
You mean "ahb"? I noticed that most other nodes with reset lines don't have a
name associated. Maybe I could just drop it and use first specified reset?
>
> > + - reset-names: reset name mentioned above
> > + - #clock-cells : must contain 1
>
> An example would be nice here
You mean node example? with ports? In the past, Rob was against examples
unless really necessary. Node from R40 DTSI can serve as an example.
>
> > +- ports: A ports node with endpoint definitions as defined in
> > + Documentation/devicetree/bindings/media/video-interfaces.txt. The
> > first port + should be the input for mixer0 mux. The second should be
> > the output for that + mux. Third port should be input for mixer1 mux.
> > Fourth port should be output + for mixer1 mux. Fifth port should be
> > input for HDMI mux. Sixth port should + be output for it. All output
> > endpoints should have reg property with the id + of the target TCON.
> > All ports should have only one enpoint connected to
> ^ endpoint
>
> I guess it would me more readable if you were to make it a bullet
> list, but this works for me otherwise.
Since I have to fix this patch anyway, I can refactor this text.
Best regards,
Jernej
^ permalink raw reply
* [PATCH 4/4] net: emaclite: Remove xemaclite_mdio_setup return check
From: Radhey Shyam Pandey @ 2018-06-13 16:02 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180613072905.GE24314@lunn.ch>
> -----Original Message-----
> From: Andrew Lunn [mailto:andrew at lunn.ch]
> Sent: Wednesday, June 13, 2018 12:59 PM
> To: Radhey Shyam Pandey <radheys@xilinx.com>
> Cc: davem at davemloft.net; michal.simek at xilinx.com;
> netdev at vger.kernel.org; linux-arm-kernel at lists.infradead.org; linux-
> kernel at vger.kernel.org
> Subject: Re: [PATCH 4/4] net: emaclite: Remove xemaclite_mdio_setup return
> check
>
> On Wed, Jun 13, 2018 at 12:05:19PM +0530, Radhey Shyam Pandey wrote:
> > Errors are already reported in xemaclite_mdio_setup so avoid
> > reporting it again.
> >
> > Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@xilinx.com>
> > Signed-off-by: Michal Simek <michal.simek@xilinx.com>
> > ---
> > drivers/net/ethernet/xilinx/xilinx_emaclite.c | 4 +---
> > 1 files changed, 1 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c
> b/drivers/net/ethernet/xilinx/xilinx_emaclite.c
> > index ec4608e..2a0c06e 100644
> > --- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c
> > +++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c
> > @@ -1143,9 +1143,7 @@ static int xemaclite_of_probe(struct
> platform_device *ofdev)
> > xemaclite_update_address(lp, ndev->dev_addr);
> >
> > lp->phy_node = of_parse_phandle(ofdev->dev.of_node, "phy-
> handle", 0);
> > - rc = xemaclite_mdio_setup(lp, &ofdev->dev);
> > - if (rc)
> > - dev_warn(&ofdev->dev, "error registering MDIO bus\n");
> > + xemaclite_mdio_setup(lp, &ofdev->dev);
> >
> > dev_info(dev, "MAC address is now %pM\n", ndev->dev_addr);
>
> The patch itself is O.K.
>
> Reviewed-by: Andrew Lunn <andrew@lunn.ch>
>
> However, do you want to keep going if the MDIO bus fails? Maybe you
> should failed the probe?
Thanks for the review. Yes, I will fix it in next series.
-Radhey
>
> Andrew
^ permalink raw reply
* [PATCH V2 2/2] efi/fb: Convert PCI bus address to resource if translated by the bridge
From: okaya at codeaurora.org @ 2018-06-13 15:50 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAKv+Gu9+M8zOtACPQ4_UMgp-M1LfZnciYe1VTQNqn_zNWJea4g@mail.gmail.com>
On 2018-06-13 11:45, Ard Biesheuvel wrote:
> On 18 May 2018 at 16:17, Sinan Kaya <okaya@codeaurora.org> wrote:
>> A host bridge is allowed to remap BAR addresses using _TRA attribute
>> in
>> _CRS windows.
>>
>> pci_bus 0000:00: root bus resource [mem 0x80100100000-0x8011fffffff
>> window] (bus address [0x00100000-0x1fffffff])
>> pci 0000:02:00.0: reg 0x10: [mem 0x8011e000000-0x8011effffff]
>>
>> When a VGA device is behind such a host bridge and the resource is
>> translated efifb driver is trying to do ioremap against bus address
>> rather than the resource address and is failing to probe.
>>
>> efifb: probing for efifb
>> efifb: cannot reserve video memory at 0x1e000000
>> efifb: framebuffer at 0x1e000000, using 1920k, total 1875k
>> efifb: mode is 800x600x32, linelength=3200, pages=1
>> efifb: scrolling: redraw
>> efifb: Truecolor: size=8:8:8:8, shift=24:16:8:0
>>
>> Use the host bridge offset information to convert bus address to
>> resource address in the fixup.
>>
>> Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
>
> Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
>
> Bartlomiej, could you please take these via the fbdev tree for v4.19?
> Peter already gave his ack but Sinan dropped it (presumably because of
> the split in v2)
>
> Sinan, does this need to go to -stable? I.e., has it ever worked
> before?
>
Yes, it needs to go to stable. It never worked before. Issue was found
during a graphics bring up with AST driver.
It needs a 32 bit graphics card and a 32 bit translating host bridge to
hit the issue.
>
>
>> ---
>> drivers/video/fbdev/efifb.c | 5 +++++
>> 1 file changed, 5 insertions(+)
>>
>> diff --git a/drivers/video/fbdev/efifb.c b/drivers/video/fbdev/efifb.c
>> index 6daac8d..429cc85 100644
>> --- a/drivers/video/fbdev/efifb.c
>> +++ b/drivers/video/fbdev/efifb.c
>> @@ -431,6 +431,7 @@ static void efifb_fixup_resources(struct pci_dev
>> *dev)
>> .end = screen_info.lfb_base + screen_info.lfb_size -
>> 1,
>> .flags = IORESOURCE_MEM,
>> };
>> + struct pci_bus_region region;
>> int i;
>>
>> if (efifb_pci_dev || screen_info.orig_video_isVGA !=
>> VIDEO_TYPE_EFI)
>> @@ -442,6 +443,10 @@ static void efifb_fixup_resources(struct pci_dev
>> *dev)
>> if (!screen_res.start)
>> return;
>>
>> + region.start = screen_res.start;
>> + region.end = screen_res.end;
>> + pcibios_bus_to_resource(dev->bus, &screen_res, ®ion);
>> +
>> for (i = 0; i <= PCI_STD_RESOURCE_END; i++) {
>> struct resource *res = &dev->resource[i];
>>
>> --
>> 2.7.4
>>
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> linux-arm-kernel at lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [RFC PATCH 6/8] dts: coresight: Clean up the device tree graph bindings
From: Matt Sealey @ 2018-06-13 15:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <c0d531ec-9201-bfd8-e6c3-9140698b0697@arm.com>
Hi Suzuki,
> > Why not use ?unit??
> >
> > I believe we had this discussion years ago about numbering serial ports
> > and sdhci (i.e. how do you know it?s UART0 or UART1 from just the address?
> > Some SoC?s don?t address sequentially *or* in a forward direction) - I
> > believe it?s not exactly codified in ePAPR, not am I sure where it may be
> > otherwise, but it exists.
>
> We have different situation here. We need to know *the port number* as
> understood by the hardware, so that we can enable *the specific* port for
> a given path.
For the purposes of abstraction, each port will have the property of having
a node which is pointed to by other nodes, and in the case of a true ATB
endpoint, no other nodes behind it.
It doesn't matter what the HW numbers it as as long as the driver can derive
it from whatever you put in the DT. So a funnel (which is ~8 ports muxed into
one output):
f1p0: port {
unit = <0>;
endpoint = <&f1out>;
};
f1p1: port {
unit = <4>;
endpoint = <&f1out>;
};
f1out: port {
endpoint = <&etf1>;
};
"unit" here is specific to the driver's understanding of ports within it's
own cycle of the graph. For a replicator you can invert the logic - input
ports don't need a unit, but the two outputs are filtered in CoreSight not
by leg but by transiting ATB ID in groups of 16 IDs. In that case maybe
you would want to describe all 8 possible units on each leg with the first
ID it would filter? Or just list tuples of filter IDs <id, first, last>
Who cares, really, as long as the driver knows what it means.
You don't need to namespace every property.
> As I mentioned above, we need the hardware numbers to enable the
> "specific" port.
Okay and how is this not able to be prescribed in a binding for "arm,coresight-funnel"
that:
"input ports are numbered from 0 to N where N is the maximum input port
number. This number is identified with the "unit" property, which directly
corresponds to the bit position in the funnel Ctrl_Reg register, and the
bit position multiplied by 3 for each 3-bit priority in the funnel
Priority_Ctrl_Reg, with N having a maximum of the defined register bitfield
DEVID[PORTCOUNT], minus one, for that component"
Or a replicator:
"output ports are numbered per the CoreSight ATB Replicator specification,
unit corresponding to the IDFILTERn register controlling ID filters for
that leg, with a maximum of the defined register bitfield DEVID[PORTNUM],
minus one"
One could clarify it, even, with labels for readability ("label" definitely
is a well defined if also completely arbitrary property).
..
> static void funnel_enable_hw(struct funnel_drvdata *drvdata, int port)
> {
> u32 functl;
>
> CS_UNLOCK(drvdata->base);
>
> functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL);
> functl &= ~FUNNEL_HOLDTIME_MASK;
> functl |= FUNNEL_HOLDTIME;
> functl |= (1 << port);
> writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL);
> writel_relaxed(drvdata->priority, drvdata->base + FUNNEL_PRICTL);
>
> CS_LOCK(drvdata->base);
> }
>
> No we don't need to parse it in both ways, up and down. Btw, the trace
> paths are not statically created. They are done at runtime, as configured
> by the user.
You do realize this isn't how the hardware works, correct?
Trace paths are fixed, they may diverge with different configurations, but
the full CoreSight topology (all funnels, replicators and intermediary
Components) is entirely unchangeable.
The DT should provide the information to provide a reference acyclic directed
graph of the entire topology (or entirely reasonably programmable topology where
at all possible) - if a user wants to trace from ETM_0 then they only
have particular paths to particular sinks, for instance ETM_0 and ETF_0
may be on their own path, so you cannot just "configure as a user"
a path from ETM_1 to ETF_0 since there isn't one.
Walking said graphs with the knowledge that CoreSight specifically disallows
loopbacks in ATB topology is basic computer science problem - literally a
matter of topological sorting. But let's build a graph once and traverse it -
don't build the graph partially each time or try and build it to cross-check
every time. The paths are wires in the design, lets not fake to the user
that there is any configurability in that or try and encode that in the
DT.
> Coming back to your suggestion of "unit", what does it imply ?
Whatever the driver likes. For uart and mmc, it was just a spurious number
but it could be applied as the end of, say, ttyS<N> or mmcblk<N>p3 or used
in any other driver-specific manner. The number you put in is up to you,
but the valid numbers would be in the binding for that particular device.
> Its too generic a term for something as concrete as a port number.
Is it?
Why would you need a whole other property type to encode a u32 that
describes an arbitrary number specific to that hardware device?
Ta,
Matt
IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.
^ permalink raw reply
* [PATCH 2/4] ARM: Introduce ability to enable invalidate of BTB with ICIALLU on Cortex-A15 for CVE-2017-5715
From: Tom Rini @ 2018-06-13 15:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180613133215.2cv7iyjb2laaha3j@kahuna>
On Wed, Jun 13, 2018 at 08:32:15AM -0500, Nishanth Menon wrote:
> On 23:05-20180612, Marek Vasut wrote:
> > On 06/12/2018 10:24 PM, Nishanth Menon wrote:
> [..]
> > > +#ifdef CONFIG_ARM_CORTEX_A15_CVE_2017_5715
> > > + mrc p15, 0, r0, c1, c0, 1 @ read auxilary control register
> > > + orr r0, r0, #1 << 0 @ Enable invalidates of BTB
> >
> > Can we use BIT() macro in the assembler code too ?
>
> Probably, but just following convention in the rest of the file. Do we
> want to change from existing code?
Agreed, we should follow the existing style (and I'm not 100% sure I
like using BIT() in asm files).
--
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20180613/cebb11ee/attachment-0001.sig>
^ permalink raw reply
* [PATCH V2 2/2] efi/fb: Convert PCI bus address to resource if translated by the bridge
From: Ard Biesheuvel @ 2018-06-13 15:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526653072-7153-2-git-send-email-okaya@codeaurora.org>
On 18 May 2018 at 16:17, Sinan Kaya <okaya@codeaurora.org> wrote:
> A host bridge is allowed to remap BAR addresses using _TRA attribute in
> _CRS windows.
>
> pci_bus 0000:00: root bus resource [mem 0x80100100000-0x8011fffffff window] (bus address [0x00100000-0x1fffffff])
> pci 0000:02:00.0: reg 0x10: [mem 0x8011e000000-0x8011effffff]
>
> When a VGA device is behind such a host bridge and the resource is
> translated efifb driver is trying to do ioremap against bus address
> rather than the resource address and is failing to probe.
>
> efifb: probing for efifb
> efifb: cannot reserve video memory at 0x1e000000
> efifb: framebuffer at 0x1e000000, using 1920k, total 1875k
> efifb: mode is 800x600x32, linelength=3200, pages=1
> efifb: scrolling: redraw
> efifb: Truecolor: size=8:8:8:8, shift=24:16:8:0
>
> Use the host bridge offset information to convert bus address to
> resource address in the fixup.
>
> Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Bartlomiej, could you please take these via the fbdev tree for v4.19?
Peter already gave his ack but Sinan dropped it (presumably because of
the split in v2)
Sinan, does this need to go to -stable? I.e., has it ever worked before?
> ---
> drivers/video/fbdev/efifb.c | 5 +++++
> 1 file changed, 5 insertions(+)
>
> diff --git a/drivers/video/fbdev/efifb.c b/drivers/video/fbdev/efifb.c
> index 6daac8d..429cc85 100644
> --- a/drivers/video/fbdev/efifb.c
> +++ b/drivers/video/fbdev/efifb.c
> @@ -431,6 +431,7 @@ static void efifb_fixup_resources(struct pci_dev *dev)
> .end = screen_info.lfb_base + screen_info.lfb_size - 1,
> .flags = IORESOURCE_MEM,
> };
> + struct pci_bus_region region;
> int i;
>
> if (efifb_pci_dev || screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
> @@ -442,6 +443,10 @@ static void efifb_fixup_resources(struct pci_dev *dev)
> if (!screen_res.start)
> return;
>
> + region.start = screen_res.start;
> + region.end = screen_res.end;
> + pcibios_bus_to_resource(dev->bus, &screen_res, ®ion);
> +
> for (i = 0; i <= PCI_STD_RESOURCE_END; i++) {
> struct resource *res = &dev->resource[i];
>
> --
> 2.7.4
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH V2 1/2] efi/fb: Simplify fixup code to prefer struct resource
From: Ard Biesheuvel @ 2018-06-13 15:42 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1526653072-7153-1-git-send-email-okaya@codeaurora.org>
On 18 May 2018 at 16:17, Sinan Kaya <okaya@codeaurora.org> wrote:
> Get rid of base and size variables in favor of a struct resource.
> The conditional for checking window can be replaced with
> resource_contains().
>
> Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> ---
> drivers/video/fbdev/efifb.c | 17 +++++++++++------
> 1 file changed, 11 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/video/fbdev/efifb.c b/drivers/video/fbdev/efifb.c
> index 46a4484..6daac8d 100644
> --- a/drivers/video/fbdev/efifb.c
> +++ b/drivers/video/fbdev/efifb.c
> @@ -426,17 +426,20 @@ static void record_efifb_bar_resource(struct pci_dev *dev, int idx, u64 offset)
>
> static void efifb_fixup_resources(struct pci_dev *dev)
> {
> - u64 base = screen_info.lfb_base;
> - u64 size = screen_info.lfb_size;
> + struct resource screen_res = {
> + .start = screen_info.lfb_base,
> + .end = screen_info.lfb_base + screen_info.lfb_size - 1,
> + .flags = IORESOURCE_MEM,
> + };
> int i;
>
> if (efifb_pci_dev || screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
> return;
>
> if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE)
> - base |= (u64)screen_info.ext_lfb_base << 32;
> + screen_res.start |= (u64)screen_info.ext_lfb_base << 32;
>
> - if (!base)
> + if (!screen_res.start)
> return;
>
> for (i = 0; i <= PCI_STD_RESOURCE_END; i++) {
> @@ -445,8 +448,10 @@ static void efifb_fixup_resources(struct pci_dev *dev)
> if (!(res->flags & IORESOURCE_MEM))
> continue;
>
> - if (res->start <= base && res->end >= base + size - 1) {
> - record_efifb_bar_resource(dev, i, base - res->start);
> + if (resource_contains(res, &screen_res)) {
> + u64 win_offset = screen_res.start - res->start;
> +
> + record_efifb_bar_resource(dev, i, win_offset);
> break;
> }
> }
> --
> 2.7.4
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH V2 2/2] efi/fb: Convert PCI bus address to resource if translated by the bridge
From: okaya at codeaurora.org @ 2018-06-13 15:29 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAKv+Gu_ZRNvW9CeqRyUpQGnrkkoOi1Hh08re=_Gz=EGVTAvbsA@mail.gmail.com>
On 2018-06-13 11:22, Ard Biesheuvel wrote:
> On 13 June 2018 at 17:17, <okaya@codeaurora.org> wrote:
>> On 2018-06-13 11:06, Ard Biesheuvel wrote:
>>>
>>> On 13 June 2018 at 16:22, Sinan Kaya <okaya@codeaurora.org> wrote:
>>>>
>>>> Hi Ard,
>>>>
>>>> On 5/18/2018 10:17 AM, Sinan Kaya wrote:
>>>>>
>>>>> A host bridge is allowed to remap BAR addresses using _TRA
>>>>> attribute in
>>>>> _CRS windows.
>>>>>
>>>>> pci_bus 0000:00: root bus resource [mem 0x80100100000-0x8011fffffff
>>>>> window] (bus address [0x00100000-0x1fffffff])
>>>>> pci 0000:02:00.0: reg 0x10: [mem 0x8011e000000-0x8011effffff]
>>>>>
>>>>> When a VGA device is behind such a host bridge and the resource is
>>>>> translated efifb driver is trying to do ioremap against bus address
>>>>> rather than the resource address and is failing to probe.
>>>>>
>>>>> efifb: probing for efifb
>>>>> efifb: cannot reserve video memory at 0x1e000000
>>>>> efifb: framebuffer at 0x1e000000, using 1920k, total 1875k
>>>>> efifb: mode is 800x600x32, linelength=3200, pages=1
>>>>> efifb: scrolling: redraw
>>>>> efifb: Truecolor: size=8:8:8:8, shift=24:16:8:0
>>>>>
>>>>> Use the host bridge offset information to convert bus address to
>>>>> resource address in the fixup.
>>>>>
>>>>> Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
>>>>> ---
>>>>
>>>>
>>>> I didn't see any messages about these getting picked up for 4.18.
>>>>
>>>> Are they queued on your own branch?
>>>>
>>>
>>> No, you never cc'ed me on them until now.
>>
>>
>> Ouch, I hoped that you would get it via get_maintainer script. Sorry
>> for
>> that.
>
> Actually, get_maintainer is right: this should go through the fbdev
> tree not the EFI tree
>
> Were you going to resend them? If not, I can find them in my archive
> and ack them, and we will ask Bartlomiej to take them for v4.19
I prefer ack rather than resend to be honest.
^ permalink raw reply
* [PATCH/RFT v2 1/2] arm64: dts: r8a77995: Add SCIF-{0,1,3,4,5} device nodes
From: Sergei Shtylyov @ 2018-06-13 15:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20180613151348.28697-1-horms+renesas@verge.net.au>
Hello!
On 06/13/2018 06:13 PM, Simon Horman wrote:
> From: Takeshi Kihara <takeshi.kihara.df@renesas.com>
>
> This patch adds the device nodes for SCIF-{0,1,3,4,5} serial ports,
Why hyphen between SCIF and # here in the subject?
> incl. clocks and power domain.
>
> Signed-off-by: Takeshi Kihara <takeshi.kihara.df@renesas.com>
> Signed-off-by: Ulrich Hecht <ulrich.hecht+renesas@gmail.com>
> [simon: updated internal clock from S3D1 to S3D1C to match documentation]
> Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
[...]
MBR, Sergei
^ permalink raw reply
* [PATCH V2 2/2] efi/fb: Convert PCI bus address to resource if translated by the bridge
From: Ard Biesheuvel @ 2018-06-13 15:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <442896963833043f84dd9d0c14d88351@codeaurora.org>
On 13 June 2018 at 17:17, <okaya@codeaurora.org> wrote:
> On 2018-06-13 11:06, Ard Biesheuvel wrote:
>>
>> On 13 June 2018 at 16:22, Sinan Kaya <okaya@codeaurora.org> wrote:
>>>
>>> Hi Ard,
>>>
>>> On 5/18/2018 10:17 AM, Sinan Kaya wrote:
>>>>
>>>> A host bridge is allowed to remap BAR addresses using _TRA attribute in
>>>> _CRS windows.
>>>>
>>>> pci_bus 0000:00: root bus resource [mem 0x80100100000-0x8011fffffff
>>>> window] (bus address [0x00100000-0x1fffffff])
>>>> pci 0000:02:00.0: reg 0x10: [mem 0x8011e000000-0x8011effffff]
>>>>
>>>> When a VGA device is behind such a host bridge and the resource is
>>>> translated efifb driver is trying to do ioremap against bus address
>>>> rather than the resource address and is failing to probe.
>>>>
>>>> efifb: probing for efifb
>>>> efifb: cannot reserve video memory at 0x1e000000
>>>> efifb: framebuffer at 0x1e000000, using 1920k, total 1875k
>>>> efifb: mode is 800x600x32, linelength=3200, pages=1
>>>> efifb: scrolling: redraw
>>>> efifb: Truecolor: size=8:8:8:8, shift=24:16:8:0
>>>>
>>>> Use the host bridge offset information to convert bus address to
>>>> resource address in the fixup.
>>>>
>>>> Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
>>>> ---
>>>
>>>
>>> I didn't see any messages about these getting picked up for 4.18.
>>>
>>> Are they queued on your own branch?
>>>
>>
>> No, you never cc'ed me on them until now.
>
>
> Ouch, I hoped that you would get it via get_maintainer script. Sorry for
> that.
Actually, get_maintainer is right: this should go through the fbdev
tree not the EFI tree
Were you going to resend them? If not, I can find them in my archive
and ack them, and we will ask Bartlomiej to take them for v4.19
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox