From patchwork Wed May 6 17:59:50 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Brian Norris X-Patchwork-Id: 469096 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2001:1868:205::9]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id A526C1402B5 for ; Thu, 7 May 2015 04:03:32 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b=gmcH1trJ; dkim-atps=neutral Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1Yq3e9-0004d1-HL; Wed, 06 May 2015 18:02:13 +0000 Received: from mail-pa0-x236.google.com ([2607:f8b0:400e:c03::236]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1Yq3dQ-0004Ah-Bc for linux-mtd@lists.infradead.org; Wed, 06 May 2015 18:01:32 +0000 Received: by pabsx10 with SMTP id sx10so15941643pab.3 for ; Wed, 06 May 2015 11:01:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-type:content-transfer-encoding; bh=WOLGnNeRFLQw6DYpTzccUJ0e9/H1IS47+DhdKSKTBD0=; b=gmcH1trJHuRxgoUuOnh7NB3dcdjgTW+3HIf6F6JwV6M7P+oOAbk4y8hze9ICZNUg5X s8YN7qQvUf/BQTEH7G41Kiyx5lLkyj3KNTn7cxZEFxzptcUZ+OChmIHzX8CGU6GsPEIf OBDZ1ZoS6CrjovpFQ4vC8dwNHpAU7S6EhuPTyN8zY/LDiY5uF4dY72PCdMVLnEkYgGdV Cz5jR9cR+3hRy/ghqt/W21+ZeOFAT/mxApd3PGBFQ8t++yN67S4EWgqQ+6sD+bl8r/4k GRG4sqUZk114hZ1cE9HOxytsu01CDjnecba9ee6UHaER1adpCuStxTb5mJ8IGkL9hz8b TDnQ== X-Received: by 10.68.132.194 with SMTP id ow2mr63144158pbb.51.1430935267484; Wed, 06 May 2015 11:01:07 -0700 (PDT) Received: from ld-irv-0074.broadcom.com (5520-maca-inet1-outside.broadcom.com. [216.31.211.11]) by mx.google.com with ESMTPSA id xz3sm2484294pbc.13.2015.05.06.11.01.05 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 06 May 2015 11:01:06 -0700 (PDT) From: Brian Norris To: Subject: [PATCH v3 06/10] mtd: brcmstb_nand: add SoC-specific support Date: Wed, 6 May 2015 10:59:50 -0700 Message-Id: <1430935194-7579-7-git-send-email-computersforpeace@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1430935194-7579-1-git-send-email-computersforpeace@gmail.com> References: <1430935194-7579-1-git-send-email-computersforpeace@gmail.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20150506_110128_493183_9804EDB2 X-CRM114-Status: GOOD ( 22.63 ) X-Spam-Score: -0.8 (/) X-Spam-Report: SpamAssassin version 3.4.0 on bombadil.infradead.org summary: Content analysis details: (-0.8 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [2607:f8b0:400e:c03:0:0:0:236 listed in] [list.dnswl.org] 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider (computersforpeace[at]gmail.com) -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid Cc: devicetree@vger.kernel.org, Florian Fainelli , Scott Branden , Kevin Cernekee , Corneliu Doban , Ray Jui , =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= , linux-kernel@vger.kernel.org, Dan Ehrenberg , Jonathan Richardson , Anatol Pomazao , Gregory Fong , bcm-kernel-feedback-list@broadcom.com, Brian Norris , Dmitry Torokhov X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-mtd" Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org The STB NAND controller is integrated into various non-STB SoCs, and those SoCs integrate things like interrupts and busing differently. Add support for masking/clearing interrupts via a custom hook, as well as preparing/unpreparing the data bus for data transfers. Does not support any new SoCs yet; support will be added incrementally. Signed-off-by: Brian Norris --- drivers/mtd/nand/Makefile | 3 +- drivers/mtd/nand/brcmnand.h | 56 +++++++++++++++++++++++++++ drivers/mtd/nand/brcmstb_nand.c | 75 ++++++++++++++++++++++++++++++++++--- drivers/mtd/nand/brcmstb_nand_soc.c | 65 ++++++++++++++++++++++++++++++++ 4 files changed, 193 insertions(+), 6 deletions(-) create mode 100644 drivers/mtd/nand/brcmnand.h create mode 100644 drivers/mtd/nand/brcmstb_nand_soc.c diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 3b1adddc83dd..806727d5a84b 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/ obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o -obj-$(CONFIG_MTD_NAND_BRCMSTB) += brcmstb_nand.o +obj-$(CONFIG_MTD_NAND_BRCMSTB) += brcmnand.o +brcmnand-objs := brcmstb_nand.o brcmstb_nand_soc.o nand-objs := nand_base.o nand_bbt.o nand_timings.o diff --git a/drivers/mtd/nand/brcmnand.h b/drivers/mtd/nand/brcmnand.h new file mode 100644 index 000000000000..59e0bfef2331 --- /dev/null +++ b/drivers/mtd/nand/brcmnand.h @@ -0,0 +1,56 @@ +/* + * Copyright © 2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __BRCMNAND_H__ +#define __BRCMNAND_H__ + +#include + +struct device; +struct device_node; + +struct brcmnand_soc { + struct device *dev; /* parent device */ + struct device_node *dn; + bool (*ctlrdy_ack)(struct brcmnand_soc *soc); + void (*ctlrdy_set_enabled)(struct brcmnand_soc *soc, bool en); + void (*prepare_data_bus)(struct brcmnand_soc *soc, bool prepare); + void *priv; +}; + +/** + * Probe for a custom Broadcom SoC + * + * @dev: device to bind devres objects to + * @dn: DT node for the custom SoC + * + * Return a new soc struct if successful. Should be freed with + * brcmnand_remove_soc(). + * Return NULL for all other errors + */ +struct brcmnand_soc *devm_brcmnand_probe_soc(struct device *dev, + struct device_node *dn); + +static inline void brcmnand_soc_data_bus_prepare(struct brcmnand_soc *soc) +{ + if (soc && soc->prepare_data_bus) + soc->prepare_data_bus(soc, true); +} + +static inline void brcmnand_soc_data_bus_unprepare(struct brcmnand_soc *soc) +{ + if (soc && soc->prepare_data_bus) + soc->prepare_data_bus(soc, false); +} + +#endif /* __BRCMNAND_H__ */ diff --git a/drivers/mtd/nand/brcmstb_nand.c b/drivers/mtd/nand/brcmstb_nand.c index ec65a48d2487..5abc88cfe702 100644 --- a/drivers/mtd/nand/brcmstb_nand.c +++ b/drivers/mtd/nand/brcmstb_nand.c @@ -37,6 +37,8 @@ #include #include +#include "brcmnand.h" + /* * This flag controls if WP stays on between erase/write commands to mitigate * flash corruption due to power glitches. Values: @@ -117,6 +119,9 @@ struct brcmnand_controller { unsigned int dma_irq; int nand_version; + /* Some SoCs provide custom interrupt status register(s) */ + struct brcmnand_soc *soc; + int cmd_pending; bool dma_pending; struct completion done; @@ -963,6 +968,17 @@ static irqreturn_t brcmnand_ctlrdy_irq(int irq, void *data) return IRQ_HANDLED; } +/* Handle SoC-specific interrupt hardware */ +static irqreturn_t brcmnand_irq(int irq, void *data) +{ + struct brcmnand_controller *ctrl = data; + + if (ctrl->soc->ctlrdy_ack(ctrl->soc)) + return brcmnand_ctlrdy_irq(irq, data); + + return IRQ_NONE; +} + static irqreturn_t brcmnand_dma_irq(int irq, void *data) { struct brcmnand_controller *ctrl = data; @@ -1151,12 +1167,18 @@ static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command, if (native_cmd == CMD_PARAMETER_READ || native_cmd == CMD_PARAMETER_CHANGE_COL) { int i; + + brcmnand_soc_data_bus_prepare(ctrl->soc); + /* * Must cache the FLASH_CACHE now, since changes in * SECTOR_SIZE_1K may invalidate it */ for (i = 0; i < FC_WORDS; i++) ctrl->flash_cache[i] = brcmnand_read_fc(ctrl, i); + + brcmnand_soc_data_bus_unprepare(ctrl->soc); + /* Cleanup from HW quirk: restore SECTOR_SIZE_1K */ if (host->hwcfg.sector_size_1k) brcmnand_set_sector_size_1k(host, @@ -1369,10 +1391,15 @@ static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip, brcmnand_send_cmd(host, CMD_PAGE_READ); brcmnand_waitfunc(mtd, chip); - if (likely(buf)) + if (likely(buf)) { + brcmnand_soc_data_bus_prepare(ctrl->soc); + for (j = 0; j < FC_WORDS; j++, buf++) *buf = brcmnand_read_fc(ctrl, j); + brcmnand_soc_data_bus_unprepare(ctrl->soc); + } + if (oob) oob += read_oob_from_regs(ctrl, i, oob, mtd->oobsize / trans, @@ -1544,12 +1571,17 @@ static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip, lower_32_bits(addr)); (void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS); - if (buf) + if (buf) { + brcmnand_soc_data_bus_prepare(ctrl->soc); + for (j = 0; j < FC_WORDS; j++, buf++) brcmnand_write_fc(ctrl, j, *buf); - else if (oob) + + brcmnand_soc_data_bus_unprepare(ctrl->soc); + } else if (oob) { for (j = 0; j < FC_WORDS; j++) brcmnand_write_fc(ctrl, j, 0xffffffff); + } if (oob) { oob += write_oob_to_regs(ctrl, i, oob, @@ -1993,6 +2025,11 @@ static int brcmnand_resume(struct device *dev) brcmnand_write_reg(ctrl, BRCMNAND_CS_XOR, ctrl->nand_cs_nand_xor); brcmnand_write_reg(ctrl, BRCMNAND_CORR_THRESHOLD, ctrl->corr_stat_threshold); + if (ctrl->soc) { + /* Clear/re-enable interrupt */ + ctrl->soc->ctlrdy_ack(ctrl->soc); + ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true); + } list_for_each_entry(host, &ctrl->host_list, node) { struct mtd_info *mtd = &host->mtd; @@ -2122,8 +2159,36 @@ static int brcmnand_probe(struct platform_device *pdev) return -ENODEV; } - ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0, - DRV_NAME, ctrl); + /* + * Some SoCs integrate this controller (e.g., its interrupt bits) in + * interesting ways + */ + if (of_property_read_bool(dn, "brcm,nand-soc")) { + struct device_node *soc_dn; + + soc_dn = of_parse_phandle(dn, "brcm,nand-soc", 0); + if (!soc_dn) + return -ENODEV; + + ctrl->soc = devm_brcmnand_probe_soc(dev, soc_dn); + if (!ctrl->soc) { + dev_err(dev, "could not probe SoC data\n"); + of_node_put(soc_dn); + return -ENODEV; + } + + ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0, + DRV_NAME, ctrl); + + /* Enable interrupt */ + ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true); + + of_node_put(soc_dn); + } else { + /* Use standard interrupt infrastructure */ + ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0, + DRV_NAME, ctrl); + } if (ret < 0) { dev_err(dev, "can't allocate IRQ %d: error %d\n", ctrl->irq, ret); diff --git a/drivers/mtd/nand/brcmstb_nand_soc.c b/drivers/mtd/nand/brcmstb_nand_soc.c new file mode 100644 index 000000000000..970912c690a7 --- /dev/null +++ b/drivers/mtd/nand/brcmstb_nand_soc.c @@ -0,0 +1,65 @@ +/* + * Copyright © 2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#include "brcmnand.h" + +#define DRV_NAME "brcmnand-soc" + +struct brcmnand_soc_ofdata { + int (*init)(struct brcmnand_soc *soc); + bool (*ctlrdy_ack)(struct brcmnand_soc *soc); + void (*ctlrdy_set_enabled)(struct brcmnand_soc *soc, bool en); + void (*prepare_data_bus)(struct brcmnand_soc *soc, bool prepare); +}; + +static const struct of_device_id brcmnand_soc_ofmatch[] = { + {}, +}; + +struct brcmnand_soc *devm_brcmnand_probe_soc(struct device *dev, + struct device_node *dn) +{ + const struct brcmnand_soc_ofdata *soc_data; + const struct of_device_id *match; + struct brcmnand_soc *soc; + int ret; + + match = of_match_node(brcmnand_soc_ofmatch, dn); + if (!match) + return NULL; + + soc_data = match->data; + + soc = devm_kzalloc(dev, sizeof(*soc), GFP_KERNEL); + if (!soc) + return NULL; + + soc->dev = dev; + soc->dn = dn; + soc->ctlrdy_ack = soc_data->ctlrdy_ack; + soc->ctlrdy_set_enabled = soc_data->ctlrdy_set_enabled; + soc->prepare_data_bus = soc_data->prepare_data_bus; + if (soc_data->init) { + ret = soc_data->init(soc); + if (ret) + return NULL; + } + + return soc; +}