188 lines
5.4 KiB
Diff
188 lines
5.4 KiB
Diff
From 8e5e628a9de439d02914b85a48e1ac3e04ea486a Mon Sep 17 00:00:00 2001
|
|
From: Linus Walleij <linus.walleij@linaro.org>
|
|
Date: Thu, 11 Oct 2018 20:03:49 +0200
|
|
Subject: [PATCH 06/18] mtd: physmap_of_gemini: Handle pin control
|
|
|
|
This enables the complex mapping for the Gemini and kicks in
|
|
custom read/write functions that will wrap the existing
|
|
simple functions in calls to enable/disable the parallel
|
|
flash pins using pin controls.
|
|
|
|
This is necessary on some hardware such as the D-Link
|
|
DIR-685 where all flash pins are patched in/out at the same
|
|
time, but some of the flash pins are in practice unused by
|
|
the flash and have anyway been reused as GPIO.
|
|
|
|
This concerns specifically CE1 on the Gemini. There is only
|
|
one flash chip, so only CE0 is used, and the line for CE1
|
|
has been reused as chip select for the emulated SPI port
|
|
connected to the display. If we try to use the same lines
|
|
for flash and GPIO at the same time, one of them will loose:
|
|
the GPIO line will disappear because it gets disconnected
|
|
from the pin when the flash group is muxed out.
|
|
|
|
Fix this by introducing two pin control states named simply
|
|
"enabled" and "disabled" and only enable the flash lines
|
|
when absolutely necessary (during read/write/copy). This
|
|
way, they are available for GPIO at all other times and
|
|
the display works.
|
|
|
|
Collect all the state variables in a struct named
|
|
struct gemini_flash and allocate this struct at probe
|
|
time.
|
|
|
|
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
|
|
---
|
|
drivers/mtd/maps/Kconfig | 1 +
|
|
drivers/mtd/maps/physmap_of_gemini.c | 105 ++++++++++++++++++++++++++-
|
|
2 files changed, 105 insertions(+), 1 deletion(-)
|
|
|
|
--- a/drivers/mtd/maps/Kconfig
|
|
+++ b/drivers/mtd/maps/Kconfig
|
|
@@ -89,6 +89,7 @@ config MTD_PHYSMAP_OF_GEMINI
|
|
depends on MTD_PHYSMAP_OF
|
|
depends on MFD_SYSCON
|
|
default ARCH_GEMINI
|
|
+ select MTD_COMPLEX_MAPPINGS
|
|
help
|
|
This provides some extra DT physmap parsing for the Gemini
|
|
platforms, some detection and setting up parallel mode on the
|
|
--- a/drivers/mtd/maps/physmap_of_gemini.c
|
|
+++ b/drivers/mtd/maps/physmap_of_gemini.c
|
|
@@ -10,9 +10,11 @@
|
|
#include <linux/of.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/mtd/map.h>
|
|
+#include <linux/mtd/xip.h>
|
|
#include <linux/mfd/syscon.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/bitops.h>
|
|
+#include <linux/pinctrl/consumer.h>
|
|
#include "physmap_of_gemini.h"
|
|
|
|
/*
|
|
@@ -49,6 +51,77 @@ static const struct of_device_id syscon_
|
|
{ },
|
|
};
|
|
|
|
+struct gemini_flash {
|
|
+ struct device *dev;
|
|
+ struct pinctrl *p;
|
|
+ struct pinctrl_state *enabled_state;
|
|
+ struct pinctrl_state *disabled_state;
|
|
+};
|
|
+
|
|
+/* Static local state */
|
|
+static struct gemini_flash *gf;
|
|
+
|
|
+static void gemini_flash_enable_pins(void)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ if (IS_ERR(gf->enabled_state))
|
|
+ return;
|
|
+ ret = pinctrl_select_state(gf->p, gf->enabled_state);
|
|
+ if (ret)
|
|
+ dev_err(gf->dev, "failed to enable pins\n");
|
|
+}
|
|
+
|
|
+static void gemini_flash_disable_pins(void)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ if (IS_ERR(gf->disabled_state))
|
|
+ return;
|
|
+ ret = pinctrl_select_state(gf->p, gf->disabled_state);
|
|
+ if (ret)
|
|
+ dev_err(gf->dev, "failed to disable pins\n");
|
|
+}
|
|
+
|
|
+static map_word __xipram gemini_flash_map_read(struct map_info *map,
|
|
+ unsigned long ofs)
|
|
+{
|
|
+ map_word __xipram ret;
|
|
+
|
|
+ gemini_flash_enable_pins();
|
|
+ ret = inline_map_read(map, ofs);
|
|
+ gemini_flash_disable_pins();
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void __xipram gemini_flash_map_write(struct map_info *map,
|
|
+ const map_word datum,
|
|
+ unsigned long ofs)
|
|
+{
|
|
+ gemini_flash_enable_pins();
|
|
+ inline_map_write(map, datum, ofs);
|
|
+ gemini_flash_disable_pins();
|
|
+}
|
|
+
|
|
+static void __xipram gemini_flash_map_copy_from(struct map_info *map,
|
|
+ void *to, unsigned long from,
|
|
+ ssize_t len)
|
|
+{
|
|
+ gemini_flash_enable_pins();
|
|
+ inline_map_copy_from(map, to, from, len);
|
|
+ gemini_flash_disable_pins();
|
|
+}
|
|
+
|
|
+static void __xipram gemini_flash_map_copy_to(struct map_info *map,
|
|
+ unsigned long to,
|
|
+ const void *from, ssize_t len)
|
|
+{
|
|
+ gemini_flash_enable_pins();
|
|
+ inline_map_copy_to(map, to, from, len);
|
|
+ gemini_flash_disable_pins();
|
|
+}
|
|
+
|
|
int of_flash_probe_gemini(struct platform_device *pdev,
|
|
struct device_node *np,
|
|
struct map_info *map)
|
|
@@ -62,6 +135,11 @@ int of_flash_probe_gemini(struct platfor
|
|
if (!of_device_is_compatible(np, "cortina,gemini-flash"))
|
|
return 0;
|
|
|
|
+ gf = devm_kzalloc(dev, sizeof(*gf), GFP_KERNEL);
|
|
+ if (!gf)
|
|
+ return -ENOMEM;
|
|
+ gf->dev = dev;
|
|
+
|
|
rmap = syscon_regmap_lookup_by_phandle(np, "syscon");
|
|
if (IS_ERR(rmap)) {
|
|
dev_err(dev, "no syscon\n");
|
|
@@ -96,7 +174,32 @@ int of_flash_probe_gemini(struct platfor
|
|
map->bankwidth * 8);
|
|
}
|
|
|
|
- dev_info(&pdev->dev, "initialized Gemini-specific physmap control\n");
|
|
+ gf->p = devm_pinctrl_get(dev);
|
|
+ if (IS_ERR(gf->p)) {
|
|
+ dev_err(dev, "no pinctrl handle\n");
|
|
+ ret = PTR_ERR(gf->p);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ gf->enabled_state = pinctrl_lookup_state(gf->p, "enabled");
|
|
+ if (IS_ERR(gf->enabled_state))
|
|
+ dev_err(dev, "no enabled pin control state\n");
|
|
+
|
|
+ gf->disabled_state = pinctrl_lookup_state(gf->p, "disabled");
|
|
+ if (IS_ERR(gf->enabled_state)) {
|
|
+ dev_err(dev, "no disabled pin control state\n");
|
|
+ } else {
|
|
+ ret = pinctrl_select_state(gf->p, gf->disabled_state);
|
|
+ if (ret)
|
|
+ dev_err(gf->dev, "failed to disable pins\n");
|
|
+ }
|
|
+
|
|
+ map->read = gemini_flash_map_read;
|
|
+ map->write = gemini_flash_map_write;
|
|
+ map->copy_from = gemini_flash_map_copy_from;
|
|
+ map->copy_to = gemini_flash_map_copy_to;
|
|
+
|
|
+ dev_info(dev, "initialized Gemini-specific physmap control\n");
|
|
|
|
return 0;
|
|
}
|