From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 38DE5C761A6 for ; Thu, 30 Mar 2023 09:23:23 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:Content-Type: Content-Transfer-Encoding:Reply-To:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:In-Reply-To:References:Cc:To:Subject: From:MIME-Version:Date:Message-ID:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=ex/EguDT1cUinN8tGcOA9Gz86VrUldi9NiHab/PJSn4=; b=w4b0QbE+5rPx+dC4KdsSEyd+2t L+n1EPYBZFUYsyWsmDUaHzap5DbqAGlyI4JaBU8sX7VF7vSYV4j7ZsJMbXNkNqvltfJIf7yrexxh8 3nYaBHq/vpMZ8OCCU4sXPRVJj7ACHcNOqEqN/7vbgEQZkcLLPNRSCLbP4mHCzlAsijIVtM15qK278 0D3+vC00gVvjXeJRKFWaiO0ZBnafrYvAndTD2BmfZIZf6nGl0GUNOI1foGB6Q2jUZJ5PfjyxbDbGg GWuH6OL1fL5959C+1+MKKKC/SEdrzpLc3cA/cxK6CbAqgc7s8PW96s7vQKldessASDSOE2mJo//Vm hTL39vPQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.96 #2 (Red Hat Linux)) id 1phoV1-003GBM-2e; Thu, 30 Mar 2023 09:23:15 +0000 Received: from mail-wr1-x42e.google.com ([2a00:1450:4864:20::42e]) by bombadil.infradead.org with esmtps (Exim 4.96 #2 (Red Hat Linux)) id 1phoUx-003G8E-0s for linux-amlogic@lists.infradead.org; Thu, 30 Mar 2023 09:23:13 +0000 Received: by mail-wr1-x42e.google.com with SMTP id r11so18333058wrr.12 for ; Thu, 30 Mar 2023 02:23:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1680168187; h=content-transfer-encoding:in-reply-to:organization:references:cc:to :content-language:subject:reply-to:from:user-agent:mime-version:date :message-id:from:to:cc:subject:date:message-id:reply-to; bh=zVbJBPOpKkbhGyx27wqJIbibuzsS8krruWYUofYY0Zk=; b=qztYgqg4lKi9lQ28iN6ULtq2H3AiSFfJb1ZhsLl7nWEvOS+r1AEnlgqWlVPIixJ6C6 4bHL8n3HeOSa6kgMzJpUp3JOb7gpKWY+H0GO+izzq/3GeyYXLSOcMNDS7jmqWoWI4imT M3Yfo8GdFbgGq4K8qikR8x80iUjMVn0Wxs70HpAJKUPB7hbrdlhj4tV1mO0vi9gCwNWF SQAqXXpRclSb/69DVKgIwwCcyN+zPC6SgRIvwkdZwr2AzgVyOZfJzizit/8oO5pmcpCa +WwvvIf+8+1/uYW0Zm14QMEykFYirAoy3SAF4pCH7gkMecH3wg4B/4Vcjr6qucKrlWGy Md5A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1680168187; h=content-transfer-encoding:in-reply-to:organization:references:cc:to :content-language:subject:reply-to:from:user-agent:mime-version:date :message-id:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=zVbJBPOpKkbhGyx27wqJIbibuzsS8krruWYUofYY0Zk=; b=F/Cgia63yOhX5nkgVMd5F6DYETTdHExlj8yhpsvnNfXBbA+3gZ4jrdZCN5TAZR2iGM 7TYjpgG30wbmLbx41bHI3hfsHolmn80mmMB6d8WnqupJ6zs9MiyicT1JZ8Oksfs91/CF 2b3b+DPM5YEPiqt+FGPAKF9UQyZUQi7B2HcesDOaasLRJ/qE3HzJhIf+5R1VynoYz4x9 vzaUzU46KNj4wzKwKCctYrbNJmbn8ltchZS7Lr9by4EQQX798S1yTXq0n/icPSx0RK9i Nw0FMsX/m+MWQROAeKezFM7apnV5NGNK+EQwRSJYrD+RnmcEbkJfu8cI72JTm9bcWca4 vk8w== X-Gm-Message-State: AAQBX9cQdjM71dsLSO/R19Q4mASa2apTqHZ54LwqJPi5ZXHIqHO/OoEW fxEtnqd2kr0YGayqgBE8uI0a4Q== X-Google-Smtp-Source: AKy350apytzG5kQ6EWhbRum3TcFZIe9Nl+6/MHdLr/z9vYr/N8u1MF3zm0kHDNFanxdCdDzj/ErWOg== X-Received: by 2002:adf:e44d:0:b0:2d0:d739:f901 with SMTP id t13-20020adfe44d000000b002d0d739f901mr18959334wrm.20.1680168187293; Thu, 30 Mar 2023 02:23:07 -0700 (PDT) Received: from ?IPV6:2a01:e0a:982:cbb0:fe8e:8291:fc25:6c8f? ([2a01:e0a:982:cbb0:fe8e:8291:fc25:6c8f]) by smtp.gmail.com with ESMTPSA id m14-20020a056000008e00b002e52dfb9256sm49668wrx.41.2023.03.30.02.23.06 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 30 Mar 2023 02:23:06 -0700 (PDT) Message-ID: Date: Thu, 30 Mar 2023 11:23:05 +0200 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.8.0 From: Neil Armstrong Subject: Re: [PATCH v2 2/2] spi: add support for Meson A1 SPI Flash Controller Content-Language: en-US To: Martin Kurbanov , Mark Brown , Rob Herring , Krzysztof Kozlowski , Kevin Hilman , Jerome Brunet , Martin Blumenstingl Cc: linux-spi@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-amlogic@lists.infradead.org, linux-kernel@vger.kernel.org, kernel@sberdevices.ru References: <20230327211351.686831-1-mmkurbanov@sberdevices.ru> <20230327211351.686831-3-mmkurbanov@sberdevices.ru> Organization: Linaro Developer Services In-Reply-To: <20230327211351.686831-3-mmkurbanov@sberdevices.ru> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20230330_022311_316043_4A2A6C84 X-CRM114-Status: GOOD ( 26.07 ) X-BeenThere: linux-amlogic@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: neil.armstrong@linaro.org Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="us-ascii"; Format="flowed" Sender: "linux-amlogic" Errors-To: linux-amlogic-bounces+linux-amlogic=archiver.kernel.org@lists.infradead.org On 27/03/2023 23:13, Martin Kurbanov wrote: > This is a driver for the Amlogic Meson SPI flash controller support > on A113L SoC. > > Signed-off-by: Martin Kurbanov > --- > drivers/spi/Kconfig | 7 + > drivers/spi/Makefile | 1 + > drivers/spi/spi-meson-spifc-a1.c | 453 +++++++++++++++++++++++++++++++ > 3 files changed, 461 insertions(+) > create mode 100644 drivers/spi/spi-meson-spifc-a1.c > > diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig > index 3b1c0878bb85..80ec01dbe7c1 100644 > --- a/drivers/spi/Kconfig > +++ b/drivers/spi/Kconfig > @@ -582,6 +582,13 @@ config SPI_MESON_SPIFC > This enables master mode support for the SPIFC (SPI flash > controller) available in Amlogic Meson SoCs. > > +config SPI_MESON_SPIFC_A1 > + tristate "Amlogic Meson A1 SPIFC controller" > + depends on ARCH_MESON || COMPILE_TEST > + help > + This enables master mode support for the SPIFC (SPI flash > + controller) available in Amlogic Meson A1 (A113L SoC). > + > config SPI_MICROCHIP_CORE > tristate "Microchip FPGA SPI controllers" > depends on SPI_MASTER > diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile > index be9ba40ef8d0..702053970967 100644 > --- a/drivers/spi/Makefile > +++ b/drivers/spi/Makefile > @@ -72,6 +72,7 @@ obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o > obj-$(CONFIG_SPI_LP8841_RTC) += spi-lp8841-rtc.o > obj-$(CONFIG_SPI_MESON_SPICC) += spi-meson-spicc.o > obj-$(CONFIG_SPI_MESON_SPIFC) += spi-meson-spifc.o > +obj-$(CONFIG_SPI_MESON_SPIFC_A1) += spi-meson-spifc-a1.o > obj-$(CONFIG_SPI_MICROCHIP_CORE) += spi-microchip-core.o > obj-$(CONFIG_SPI_MICROCHIP_CORE_QSPI) += spi-microchip-core-qspi.o > obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mpc512x-psc.o > diff --git a/drivers/spi/spi-meson-spifc-a1.c b/drivers/spi/spi-meson-spifc-a1.c > new file mode 100644 > index 000000000000..9fecf0a71bef > --- /dev/null > +++ b/drivers/spi/spi-meson-spifc-a1.c > @@ -0,0 +1,453 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Driver for Amlogic Meson A1 SPI flash controller (SPIFC) > + * > + * Copyright (c) 2023, SberDevices. All Rights Reserved. > + * > + * Author: Martin Kurbanov > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#define SPIFC_A1_AHB_CTRL_REG 0x0 > +#define SPIFC_A1_AHB_BUS_EN BIT(31) > + > +#define SPIFC_A1_USER_CTRL0_REG 0x200 > +#define SPIFC_A1_USER_REQUEST_ENABLE BIT(31) > +#define SPIFC_A1_USER_REQUEST_FINISH BIT(30) > +#define SPIFC_A1_USER_DATA_UPDATED BIT(0) > + > +#define SPIFC_A1_USER_CTRL1_REG 0x204 > +#define SPIFC_A1_USER_CMD_ENABLE BIT(30) > +#define SPIFC_A1_USER_CMD_MODE GENMASK(29, 28) > +#define SPIFC_A1_USER_CMD_CODE GENMASK(27, 20) > +#define SPIFC_A1_USER_ADDR_ENABLE BIT(19) > +#define SPIFC_A1_USER_ADDR_MODE GENMASK(18, 17) > +#define SPIFC_A1_USER_ADDR_BYTES GENMASK(16, 15) > +#define SPIFC_A1_USER_DOUT_ENABLE BIT(14) > +#define SPIFC_A1_USER_DOUT_MODE GENMASK(11, 10) > +#define SPIFC_A1_USER_DOUT_BYTES GENMASK(9, 0) > + > +#define SPIFC_A1_USER_CTRL2_REG 0x208 > +#define SPIFC_A1_USER_DUMMY_ENABLE BIT(31) > +#define SPIFC_A1_USER_DUMMY_MODE GENMASK(30, 29) > +#define SPIFC_A1_USER_DUMMY_CLK_SYCLES GENMASK(28, 23) > + > +#define SPIFC_A1_USER_CTRL3_REG 0x20c > +#define SPIFC_A1_USER_DIN_ENABLE BIT(31) > +#define SPIFC_A1_USER_DIN_MODE GENMASK(28, 27) > +#define SPIFC_A1_USER_DIN_BYTES GENMASK(25, 16) > + > +#define SPIFC_A1_USER_ADDR_REG 0x210 > + > +#define SPIFC_A1_AHB_REQ_CTRL_REG 0x214 > +#define SPIFC_A1_AHB_REQ_ENABLE BIT(31) > + > +#define SPIFC_A1_ACTIMING0_REG (0x0088 << 2) > +#define SPIFC_A1_TSLCH GENMASK(31, 30) > +#define SPIFC_A1_TCLSH GENMASK(29, 28) > +#define SPIFC_A1_TSHWL GENMASK(20, 16) > +#define SPIFC_A1_TSHSL2 GENMASK(15, 12) > +#define SPIFC_A1_TSHSL1 GENMASK(11, 8) > +#define SPIFC_A1_TWHSL GENMASK(7, 0) > + > +#define SPIFC_A1_DBUF_CTRL_REG 0x240 > +#define SPIFC_A1_DBUF_DIR BIT(31) > +#define SPIFC_A1_DBUF_AUTO_UPDATE_ADDR BIT(30) > +#define SPIFC_A1_DBUF_ADDR GENMASK(7, 0) > + > +#define SPIFC_A1_DBUF_DATA_REG 0x244 > + > +#define SPIFC_A1_USER_DBUF_ADDR_REG 0x248 > + > +#define SPIFC_A1_BUFFER_SIZE 512 > + > +#define SPIFC_A1_MAX_HZ 200000000 > +#define SPIFC_A1_MIN_HZ 1000000 > + > +#define SPIFC_A1_USER_CMD(op) ( \ > + SPIFC_A1_USER_CMD_ENABLE | \ > + FIELD_PREP(SPIFC_A1_USER_CMD_CODE, (op)->cmd.opcode) | \ > + FIELD_PREP(SPIFC_A1_USER_CMD_MODE, ilog2((op)->cmd.buswidth))) > + > +#define SPIFC_A1_USER_ADDR(op) ( \ > + SPIFC_A1_USER_ADDR_ENABLE | \ > + FIELD_PREP(SPIFC_A1_USER_ADDR_MODE, ilog2((op)->addr.buswidth)) | \ > + FIELD_PREP(SPIFC_A1_USER_ADDR_BYTES, (op)->addr.nbytes - 1)) > + > +#define SPIFC_A1_USER_DUMMY(op) ( \ > + SPIFC_A1_USER_DUMMY_ENABLE | \ > + FIELD_PREP(SPIFC_A1_USER_DUMMY_MODE, ilog2((op)->dummy.buswidth)) | \ > + FIELD_PREP(SPIFC_A1_USER_DUMMY_CLK_SYCLES, (op)->dummy.nbytes << 3)) > + > +#define SPIFC_A1_TSLCH_VAL FIELD_PREP(SPIFC_A1_TSLCH, 1) > +#define SPIFC_A1_TCLSH_VAL FIELD_PREP(SPIFC_A1_TCLSH, 1) > +#define SPIFC_A1_TSHWL_VAL FIELD_PREP(SPIFC_A1_TSHWL, 7) > +#define SPIFC_A1_TSHSL2_VAL FIELD_PREP(SPIFC_A1_TSHSL2, 7) > +#define SPIFC_A1_TSHSL1_VAL FIELD_PREP(SPIFC_A1_TSHSL1, 7) > +#define SPIFC_A1_TWHSL_VAL FIELD_PREP(SPIFC_A1_TWHSL, 2) > +#define SPIFC_A1_ACTIMING0_VAL (SPIFC_A1_TSLCH_VAL | SPIFC_A1_TCLSH_VAL | \ > + SPIFC_A1_TSHWL_VAL | SPIFC_A1_TSHSL2_VAL | \ > + SPIFC_A1_TSHSL1_VAL | SPIFC_A1_TWHSL_VAL) > + > +struct meson_spifc_a1 { > + struct spi_controller *ctrl; > + struct clk *clk; > + struct device *dev; > + void __iomem *base; > +}; > + > +static int meson_spifc_a1_request(struct meson_spifc_a1 *spifc, bool read) > +{ > + u32 mask = SPIFC_A1_USER_REQUEST_FINISH | > + (read ? SPIFC_A1_USER_DATA_UPDATED : 0); > + u32 val; > + > + writel(SPIFC_A1_USER_REQUEST_ENABLE, > + spifc->base + SPIFC_A1_USER_CTRL0_REG); > + > + return readl_poll_timeout(spifc->base + SPIFC_A1_USER_CTRL0_REG, > + val, (val & mask) == mask, 0, > + 200 * USEC_PER_MSEC); > +} > + > +static void meson_spifc_a1_drain_buffer(struct meson_spifc_a1 *spifc, > + char *buf, u32 len) > +{ > + u32 data; > + const u32 count = len / sizeof(data); > + const u32 pad = len % sizeof(data); > + > + writel(SPIFC_A1_DBUF_AUTO_UPDATE_ADDR, > + spifc->base + SPIFC_A1_DBUF_CTRL_REG); > + ioread32_rep(spifc->base + SPIFC_A1_DBUF_DATA_REG, buf, count); > + > + if (pad) { > + data = readl(spifc->base + SPIFC_A1_DBUF_DATA_REG); > + memcpy(buf + len - pad, &data, pad); > + } > +} > + > +static void meson_spifc_a1_fill_buffer(struct meson_spifc_a1 *spifc, > + const char *buf, u32 len) > +{ > + u32 data; > + const u32 count = len / sizeof(data); > + const u32 pad = len % sizeof(data); > + > + writel(SPIFC_A1_DBUF_DIR | SPIFC_A1_DBUF_AUTO_UPDATE_ADDR, > + spifc->base + SPIFC_A1_DBUF_CTRL_REG); > + iowrite32_rep(spifc->base + SPIFC_A1_DBUF_DATA_REG, buf, count); > + > + if (pad) { > + memcpy(&data, buf + len - pad, pad); > + writel(data, spifc->base + SPIFC_A1_DBUF_DATA_REG); > + } > +} > + > +static void meson_spifc_a1_user_init(struct meson_spifc_a1 *spifc) > +{ > + writel(0, spifc->base + SPIFC_A1_USER_CTRL0_REG); > + writel(0, spifc->base + SPIFC_A1_USER_CTRL1_REG); > + writel(0, spifc->base + SPIFC_A1_USER_CTRL2_REG); > + writel(0, spifc->base + SPIFC_A1_USER_CTRL3_REG); > +} > + > +static void meson_spifc_a1_set_cmd(struct meson_spifc_a1 *spifc, u32 cmd_cfg) > +{ > + u32 val; > + > + val = readl(spifc->base + SPIFC_A1_USER_CTRL1_REG); > + val &= ~(SPIFC_A1_USER_CMD_MODE | SPIFC_A1_USER_CMD_CODE); > + val |= cmd_cfg; > + writel(val, spifc->base + SPIFC_A1_USER_CTRL1_REG); > +} > + > +static void meson_spifc_a1_set_addr(struct meson_spifc_a1 *spifc, u32 addr, > + u32 addr_cfg) > +{ > + u32 val; > + > + writel(addr, spifc->base + SPIFC_A1_USER_ADDR_REG); > + > + val = readl(spifc->base + SPIFC_A1_USER_CTRL1_REG); > + val &= ~(SPIFC_A1_USER_ADDR_MODE | SPIFC_A1_USER_ADDR_BYTES); > + val |= addr_cfg; > + writel(val, spifc->base + SPIFC_A1_USER_CTRL1_REG); > +} > + > +static void meson_spifc_a1_set_dummy(struct meson_spifc_a1 *spifc, > + u32 dummy_cfg) > +{ > + u32 val = readl(spifc->base + SPIFC_A1_USER_CTRL2_REG); > + > + val &= ~(SPIFC_A1_USER_DUMMY_MODE | SPIFC_A1_USER_DUMMY_CLK_SYCLES); > + val |= dummy_cfg; > + writel(val, spifc->base + SPIFC_A1_USER_CTRL2_REG); > +} > + > +static int meson_spifc_a1_read(struct meson_spifc_a1 *spifc, void *buf, > + u32 size, u32 mode) > +{ > + u32 val = readl(spifc->base + SPIFC_A1_USER_CTRL3_REG); > + int ret; > + > + val &= ~(SPIFC_A1_USER_DIN_MODE | SPIFC_A1_USER_DIN_BYTES); > + val |= SPIFC_A1_USER_DIN_ENABLE; > + val |= FIELD_PREP(SPIFC_A1_USER_DIN_MODE, mode); > + val |= FIELD_PREP(SPIFC_A1_USER_DIN_BYTES, size); > + writel(val, spifc->base + SPIFC_A1_USER_CTRL3_REG); > + > + ret = meson_spifc_a1_request(spifc, true); > + if (!ret) > + meson_spifc_a1_drain_buffer(spifc, buf, size); > + > + return ret; > +} > + > +static int meson_spifc_a1_write(struct meson_spifc_a1 *spifc, const void *buf, > + u32 size, u32 mode) > +{ > + u32 val; > + > + meson_spifc_a1_fill_buffer(spifc, buf, size); > + > + val = readl(spifc->base + SPIFC_A1_USER_CTRL1_REG); > + val &= ~(SPIFC_A1_USER_DOUT_MODE | SPIFC_A1_USER_DOUT_BYTES); > + val |= FIELD_PREP(SPIFC_A1_USER_DOUT_MODE, mode); > + val |= FIELD_PREP(SPIFC_A1_USER_DOUT_BYTES, size); > + val |= SPIFC_A1_USER_DOUT_ENABLE; > + writel(val, spifc->base + SPIFC_A1_USER_CTRL1_REG); > + > + return meson_spifc_a1_request(spifc, false); > +} > + > +static int meson_spifc_a1_exec_op(struct spi_mem *mem, > + const struct spi_mem_op *op) > +{ > + struct meson_spifc_a1 *spifc = > + spi_controller_get_devdata(mem->spi->controller); > + size_t off, nbytes = op->data.nbytes; > + u32 cmd_cfg, addr_cfg, dummy_cfg, dmode; > + int ret; > + > + meson_spifc_a1_user_init(spifc); > + > + cmd_cfg = SPIFC_A1_USER_CMD(op); > + meson_spifc_a1_set_cmd(spifc, cmd_cfg); > + > + if (op->addr.nbytes) { > + addr_cfg = SPIFC_A1_USER_ADDR(op); > + meson_spifc_a1_set_addr(spifc, op->addr.val, addr_cfg); > + } > + > + if (op->dummy.nbytes) { > + dummy_cfg = SPIFC_A1_USER_DUMMY(op); > + meson_spifc_a1_set_dummy(spifc, dummy_cfg); > + } > + > + if (!op->data.nbytes) > + return meson_spifc_a1_request(spifc, false); > + > + dmode = ilog2(op->data.buswidth); > + off = 0; > + > + do { > + size_t block_size = min_t(size_t, nbytes, SPIFC_A1_BUFFER_SIZE); > + > + meson_spifc_a1_set_cmd(spifc, cmd_cfg); > + > + if (op->addr.nbytes) > + meson_spifc_a1_set_addr(spifc, op->addr.val + off, > + addr_cfg); > + > + if (op->dummy.nbytes) > + meson_spifc_a1_set_dummy(spifc, dummy_cfg); > + > + writel(0, spifc->base + SPIFC_A1_USER_DBUF_ADDR_REG); > + > + if (op->data.dir == SPI_MEM_DATA_IN) > + ret = meson_spifc_a1_read(spifc, op->data.buf.in + off, > + block_size, dmode); > + else > + ret = meson_spifc_a1_write(spifc, > + op->data.buf.out + off, > + block_size, dmode); > + > + nbytes -= block_size; > + off += block_size; > + } while (nbytes != 0 && !ret); > + > + return ret; > +} > + > +static void meson_spifc_a1_hw_init(struct meson_spifc_a1 *spifc) > +{ > + u32 regv; > + > + regv = readl(spifc->base + SPIFC_A1_AHB_REQ_CTRL_REG); > + regv &= ~(SPIFC_A1_AHB_REQ_ENABLE); > + writel(regv, spifc->base + SPIFC_A1_AHB_REQ_CTRL_REG); > + > + regv = readl(spifc->base + SPIFC_A1_AHB_CTRL_REG); > + regv &= ~(SPIFC_A1_AHB_BUS_EN); > + writel(regv, spifc->base + SPIFC_A1_AHB_CTRL_REG); > + > + writel(SPIFC_A1_ACTIMING0_VAL, spifc->base + SPIFC_A1_ACTIMING0_REG); > + > + writel(0, spifc->base + SPIFC_A1_USER_DBUF_ADDR_REG); > +} > + > +static const struct spi_controller_mem_ops meson_spifc_a1_mem_ops = { > + .exec_op = meson_spifc_a1_exec_op, > +}; > + > +static int meson_spifc_a1_probe(struct platform_device *pdev) > +{ > + struct spi_controller *ctrl; > + struct meson_spifc_a1 *spifc; > + int ret; > + > + ctrl = devm_spi_alloc_master(&pdev->dev, sizeof(*spifc)); > + if (!ctrl) > + return -ENOMEM; > + > + spifc = spi_controller_get_devdata(ctrl); > + platform_set_drvdata(pdev, spifc); > + > + spifc->dev = &pdev->dev; > + spifc->ctrl = ctrl; > + > + spifc->base = devm_platform_ioremap_resource(pdev, 0); > + if (IS_ERR(spifc->base)) > + return PTR_ERR(spifc->base); > + > + spifc->clk = devm_clk_get_enabled(spifc->dev, NULL); > + if (IS_ERR(spifc->clk)) > + return dev_err_probe(spifc->dev, PTR_ERR(spifc->clk), > + "unable to get clock\n"); > + > + meson_spifc_a1_hw_init(spifc); > + > + pm_runtime_set_autosuspend_delay(spifc->dev, 500); > + pm_runtime_use_autosuspend(spifc->dev); > + devm_pm_runtime_enable(spifc->dev); > + > + ctrl->num_chipselect = 1; > + ctrl->dev.of_node = pdev->dev.of_node; > + ctrl->bits_per_word_mask = SPI_BPW_MASK(8); > + ctrl->auto_runtime_pm = true; > + ctrl->mem_ops = &meson_spifc_a1_mem_ops; > + ctrl->min_speed_hz = SPIFC_A1_MIN_HZ; > + ctrl->max_speed_hz = SPIFC_A1_MAX_HZ; > + ctrl->mode_bits = (SPI_RX_DUAL | SPI_TX_DUAL | > + SPI_RX_QUAD | SPI_TX_QUAD); > + > + ret = devm_spi_register_controller(spifc->dev, ctrl); > + if (ret) > + return dev_err_probe(spifc->dev, ret, > + "failed to register spi controller\n"); > + > + return 0; > +} > + > +#ifdef CONFIG_PM_SLEEP > +static int meson_spifc_a1_suspend(struct device *dev) > +{ > + struct meson_spifc_a1 *spifc = dev_get_drvdata(dev); > + int ret; > + > + ret = spi_controller_suspend(spifc->ctrl); > + if (ret) > + return ret; > + > + if (!pm_runtime_suspended(dev)) > + clk_disable_unprepare(spifc->clk); > + > + return 0; > +} > + > +static int meson_spifc_a1_resume(struct device *dev) > +{ > + struct meson_spifc_a1 *spifc = dev_get_drvdata(dev); > + int ret = 0; > + > + if (!pm_runtime_suspended(dev)) { > + ret = clk_prepare_enable(spifc->clk); > + if (ret) > + return ret; > + } > + > + meson_spifc_a1_hw_init(spifc); > + > + ret = spi_controller_resume(spifc->ctrl); > + if (ret) > + clk_disable_unprepare(spifc->clk); > + > + return ret; > +} > +#endif /* CONFIG_PM_SLEEP */ > + > +#ifdef CONFIG_PM > +static int meson_spifc_a1_runtime_suspend(struct device *dev) > +{ > + struct meson_spifc_a1 *spifc = dev_get_drvdata(dev); > + > + clk_disable_unprepare(spifc->clk); > + > + return 0; > +} > + > +static int meson_spifc_a1_runtime_resume(struct device *dev) > +{ > + struct meson_spifc_a1 *spifc = dev_get_drvdata(dev); > + int ret; > + > + ret = clk_prepare_enable(spifc->clk); > + if (!ret) > + meson_spifc_a1_hw_init(spifc); > + > + return ret; > +} > +#endif /* CONFIG_PM */ > + > +static const struct dev_pm_ops meson_spifc_a1_pm_ops = { > + SET_SYSTEM_SLEEP_PM_OPS(meson_spifc_a1_suspend, meson_spifc_a1_resume) > + SET_RUNTIME_PM_OPS(meson_spifc_a1_runtime_suspend, > + meson_spifc_a1_runtime_resume, > + NULL) > +}; > + > +#ifdef CONFIG_OF > +static const struct of_device_id meson_spifc_a1_dt_match[] = { > + { .compatible = "amlogic,meson-a1-spifc", }, > + { }, > +}; > +MODULE_DEVICE_TABLE(of, meson_spifc_a1_dt_match); > +#endif /* CONFIG_OF */ > + > +static struct platform_driver meson_spifc_a1_driver = { > + .probe = meson_spifc_a1_probe, > + .driver = { > + .name = "meson-spifc-a1", > + .of_match_table = of_match_ptr(meson_spifc_a1_dt_match), > + .pm = &meson_spifc_a1_pm_ops, > + }, > +}; > +module_platform_driver(meson_spifc_a1_driver); > + > +MODULE_AUTHOR("Martin Kurbanov "); > +MODULE_DESCRIPTION("Amlogic Meson A1 SPIFC driver"); > +MODULE_LICENSE("GPL"); Looks really clean, drop meson everywhere in favor to amlogic and rename to spi-amlogic-a1. It looks like a bold move, but we started dropping the meson name for a while now in bindings, but I think it's time to drop meson in driver name for new SoCs which aren't related to the old Set-Top-Box SoC seris which were named "Meson". Since it's a new driver, it's the right moment :-) Thanks, Neil _______________________________________________ linux-amlogic mailing list linux-amlogic@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-amlogic