[PATCH linux dev-4.7 02/12] mtd: spi-nor: aspeed: Manage the segment address registers

Cédric Le Goater clg at kaod.org
Fri Oct 14 23:37:54 AEDT 2016


From: Milton Miller <miltonm at us.ibm.com>

This changes the aspeed mtd driver to use the segment address
registers from the driver, this eliminating a source of mismatch and
placing the kernel in control using its knowledge instead of relying
on hardware defaults or bootloaders to choose good values for the
windows based on the attached chips.

The initial commit factors out discovering the current window in which
the flash module is mapped by the controller. The intent is to adjust
the windows to the required size of the attached flash.  Also planned
is to enable direct memory mapped access and dma access, at least for
read accesses when the flash size is supported by the hardware.

The spi-nor layer already trusts its database of jedec ids over any
information in the device tree, this continues that philosophy
by using the knowledge to optimize the programming of the hardware.
The device tree may be used in the future to limit the choices,
for example specifying a max clock rate based on board wiring.

This does not have any locking around sanitizing the segments
or copying the new addresses back to the per-chip base address.
In the present code registers are updated before the devices
are probed.

We may want to cache the offsets in the per-chip structure to
when we support DMA.

Signed-off-by: Milton Miller <miltonm at us.ibm.com>
[clg : removed the calculation on the segment registers to check
       overlaps and kept only the reading of the segment base address.
       It needs more work ]
Signed-off-by: Cédric Le Goater <clg at kaod.org>
---
 drivers/mtd/spi-nor/aspeed-smc.c | 58 +++++++++++++++++++++++++++++++---------
 1 file changed, 46 insertions(+), 12 deletions(-)

diff --git a/drivers/mtd/spi-nor/aspeed-smc.c b/drivers/mtd/spi-nor/aspeed-smc.c
index e8744aef5fdc..a47eecfdfda2 100644
--- a/drivers/mtd/spi-nor/aspeed-smc.c
+++ b/drivers/mtd/spi-nor/aspeed-smc.c
@@ -136,6 +136,7 @@ enum smc_flash_type {
 };
 
 struct aspeed_smc_info {
+	u32 maxsize;		/* maximum size of 1 chip window */
 	u8 nce;			/* number of chip enables */
 	u8 maxwidth;		/* max width of spi bus */
 	bool hasdma;		/* has dma engine */
@@ -147,6 +148,7 @@ struct aspeed_smc_info {
 };
 
 static struct aspeed_smc_info fmc_2400_info = {
+	.maxsize = 64 * 1024 * 1024,
 	.nce = 5,
 	.maxwidth = 4,
 	.hasdma = true,
@@ -158,6 +160,7 @@ static struct aspeed_smc_info fmc_2400_info = {
 };
 
 static struct aspeed_smc_info smc_2400_info = {
+	.maxsize = 64 * 1024 * 1024,
 	.nce = 1,
 	.maxwidth = 2,
 	.hasdma = false,
@@ -169,6 +172,7 @@ static struct aspeed_smc_info smc_2400_info = {
 };
 
 static struct aspeed_smc_info fmc_2500_info = {
+	.maxsize = 256 * 1024 * 1024,
 	.nce = 3,
 	.maxwidth = 2,
 	.hasdma = true,
@@ -180,6 +184,7 @@ static struct aspeed_smc_info fmc_2500_info = {
 };
 
 static struct aspeed_smc_info smc_2500_info = {
+	.maxsize = 128 * 1024 * 1024,
 	.nce = 2,
 	.maxwidth = 2,
 	.hasdma = false,
@@ -212,11 +217,17 @@ struct aspeed_smc_controller {
 	struct mutex mutex;			/* controller access mutex */
 	const struct aspeed_smc_info *info;	/* type info of controller */
 	void __iomem *regs;			/* controller registers */
+	void __iomem *windows;			/* per-chip windows resource */
 	struct aspeed_smc_per_chip *chips[0];	/* pointers to attached chips */
 };
 
 #define TYPE_SETTING_REG 0x0
 
+#define LOG_8MB (23)	/* log(2)(8 * 1024 * 1024) */
+#define SEGMENT_ADDR_REG0 0x30
+#define         SEGMENT_ADDR_START(_r) ((((_r) >> 16) & 0xFF) << LOG_8MB)
+#define         SEGMENT_ADDR_END(_r) ((((_r) >> 24) & 0xFF) << LOG_8MB)
+
 #define CONTROL_SPI_AAF_MODE BIT(31)
 #define CONTROL_SPI_IO_MODE_MASK GENMASK(30, 28)
 #define CONTROL_SPI_IO_DUAL_DATA BIT(29)
@@ -403,13 +414,30 @@ of_platform_device_create_or_find(struct device_node *child,
 	return cdev;
 }
 
+static void __iomem *window_start(struct aspeed_smc_controller *controller,
+				  struct resource *r, unsigned int n)
+{
+	u32 offset = 0;
+	u32 reg;
+
+	if (controller->info->nce > 1) {
+		reg = readl(controller->regs + SEGMENT_ADDR_REG0 + n * 4);
+
+		if (SEGMENT_ADDR_START(reg) >= SEGMENT_ADDR_END(reg))
+			return NULL;
+
+		offset = SEGMENT_ADDR_START(reg) - r->start;
+	}
+
+	return controller->windows + offset;
+}
+
 static int aspeed_smc_probe(struct platform_device *dev)
 {
 	struct aspeed_smc_controller *controller;
 	const struct of_device_id *match;
 	const struct aspeed_smc_info *info;
 	struct resource *r;
-	void __iomem *regs;
 	struct device_node *child;
 	int err = 0;
 	unsigned int n;
@@ -418,19 +446,24 @@ static int aspeed_smc_probe(struct platform_device *dev)
 	if (!match || !match->data)
 		return -ENODEV;
 	info = match->data;
-	r = platform_get_resource(dev, IORESOURCE_MEM, 0);
-	regs = devm_ioremap_resource(&dev->dev, r);
-	if (IS_ERR(regs))
-		return PTR_ERR(regs);
 
 	controller = devm_kzalloc(&dev->dev, sizeof(*controller) +
 		info->nce * sizeof(controller->chips[0]), GFP_KERNEL);
 	if (!controller)
 		return -ENOMEM;
-	platform_set_drvdata(dev, controller);
-	controller->regs = regs;
 	controller->info = info;
 	mutex_init(&controller->mutex);
+	platform_set_drvdata(dev, controller);
+
+	r = platform_get_resource(dev, IORESOURCE_MEM, 0);
+	controller->regs = devm_ioremap_resource(&dev->dev, r);
+	if (IS_ERR(controller->regs))
+		return PTR_ERR(controller->regs);
+
+	r = platform_get_resource(dev, IORESOURCE_MEM, 1);
+	controller->windows = devm_ioremap_resource(&dev->dev, r);
+	if (IS_ERR(controller->windows))
+		return PTR_ERR(controller->windows);
 
 	/* The pinmux or bootloader will disable legacy mode. */
 
@@ -476,17 +509,18 @@ static int aspeed_smc_probe(struct platform_device *dev)
 		if (!chip)
 			continue;
 
-		r = platform_get_resource(dev, IORESOURCE_MEM, n + 1);
-		chip->base = devm_ioremap_resource(&dev->dev, r);
-		if (!chip->base)
-			continue;
-
 		chip->controller = controller;
 		chip->ctl = controller->regs + info->ctl0 + n * 4;
 
 		/* The device tree said the chip is spi. */
 		chip->type = smc_type_spi;
 
+		chip->base = window_start(controller, r, n);
+		if (!chip->base) {
+			dev_warn(&cdev->dev, "CE segment window closed.\n");
+			continue;
+		}
+
 		/*
 		 * Always turn on the write enable bit in the type and settings
 		 * or flash configuration register to allow opcodes to be sent
-- 
2.7.4



More information about the openbmc mailing list