* [PATCH 0/7] MIPS: Loongson1B: add NAND, DMA and GPIO support
@ 2016-04-06 12:09 Keguang Zhang
2016-04-06 12:09 ` [PATCH 1/7] clk: Loongson1: Update clocks of Loongson1B Keguang Zhang
` (6 more replies)
0 siblings, 7 replies; 8+ messages in thread
From: Keguang Zhang @ 2016-04-06 12:09 UTC (permalink / raw)
To: linux-kernel, linux-mips, linux-clk, linux-pm, dmaengine,
linux-gpio, linux-mtd
Cc: Ralf Baechle, Michael Turquette, Stephen Boyd, Rafael J. Wysocki,
Viresh Kumar, Vinod Koul, Dan Williams, Linus Walleij,
Alexandre Courbot, Boris Brezillon, Richard Weinberger,
David Woodhouse, Brian Norris, Kelvin Cheung
From: Kelvin Cheung <keguang.zhang@gmail.com>
This patchset is to add NAND, DMA and GPIO support for Loongson1B,
and moreover, include some updates/fixes.
This applies on top of mips-for-linux-next.
Thanks!
Kelvin Cheung (7):
clk: Loongson1: Update clocks of Loongson1B
cpufreq: Loongson1: Update cpufreq of Loongson1B
dmaengine: Loongson1: add Loongson1 dmaengine driver
mtd: nand: add Loongson1 NAND driver
gpio: Loongson1: add Loongson1 GPIO driver
MIPS: Loongson1B: Some updates/fixes for LS1B
MAINTAINERS: add Loongson1 architecture entry
MAINTAINERS | 9 +
arch/mips/Kconfig | 2 +
arch/mips/configs/loongson1b_defconfig | 125 +++++
arch/mips/configs/ls1b_defconfig | 110 -----
arch/mips/include/asm/mach-loongson32/cpufreq.h | 1 -
arch/mips/include/asm/mach-loongson32/dma.h | 25 +
arch/mips/include/asm/mach-loongson32/irq.h | 1 -
arch/mips/include/asm/mach-loongson32/loongson1.h | 4 +-
arch/mips/include/asm/mach-loongson32/nand.h | 30 ++
arch/mips/include/asm/mach-loongson32/platform.h | 14 +-
arch/mips/include/asm/mach-loongson32/regs-clk.h | 24 +-
arch/mips/include/asm/mach-loongson32/regs-mux.h | 84 ++--
arch/mips/include/asm/mach-loongson32/regs-pwm.h | 12 +-
arch/mips/loongson32/common/platform.c | 105 ++++-
arch/mips/loongson32/common/reset.c | 13 +-
arch/mips/loongson32/common/time.c | 27 +-
arch/mips/loongson32/ls1b/board.c | 67 ++-
drivers/clk/Makefile | 2 +-
drivers/clk/clk-loongson1.c | 163 +++++++
drivers/clk/clk-ls1x.c | 162 -------
drivers/cpufreq/Makefile | 2 +-
drivers/cpufreq/loongson1-cpufreq.c | 230 +++++++++
drivers/cpufreq/ls1x-cpufreq.c | 222 ---------
drivers/dma/Kconfig | 9 +
drivers/dma/Makefile | 1 +
drivers/dma/loongson1-dma.c | 546 ++++++++++++++++++++++
drivers/gpio/Kconfig | 7 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-loongson1.c | 102 ++++
drivers/mtd/nand/Kconfig | 8 +
drivers/mtd/nand/Makefile | 1 +
drivers/mtd/nand/loongson1_nand.c | 522 +++++++++++++++++++++
32 files changed, 2041 insertions(+), 590 deletions(-)
create mode 100644 arch/mips/configs/loongson1b_defconfig
delete mode 100644 arch/mips/configs/ls1b_defconfig
create mode 100644 arch/mips/include/asm/mach-loongson32/dma.h
create mode 100644 arch/mips/include/asm/mach-loongson32/nand.h
create mode 100644 drivers/clk/clk-loongson1.c
delete mode 100644 drivers/clk/clk-ls1x.c
create mode 100644 drivers/cpufreq/loongson1-cpufreq.c
delete mode 100644 drivers/cpufreq/ls1x-cpufreq.c
create mode 100644 drivers/dma/loongson1-dma.c
create mode 100644 drivers/gpio/gpio-loongson1.c
create mode 100644 drivers/mtd/nand/loongson1_nand.c
--
1.9.1
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/7] clk: Loongson1: Update clocks of Loongson1B
2016-04-06 12:09 [PATCH 0/7] MIPS: Loongson1B: add NAND, DMA and GPIO support Keguang Zhang
@ 2016-04-06 12:09 ` Keguang Zhang
2016-04-06 12:09 ` [PATCH 2/7] cpufreq: Loongson1: Update cpufreq " Keguang Zhang
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Keguang Zhang @ 2016-04-06 12:09 UTC (permalink / raw)
To: linux-kernel, linux-mips, linux-clk, linux-pm, dmaengine,
linux-gpio, linux-mtd
Cc: Ralf Baechle, Michael Turquette, Stephen Boyd, Rafael J. Wysocki,
Viresh Kumar, Vinod Koul, Dan Williams, Linus Walleij,
Alexandre Courbot, Boris Brezillon, Richard Weinberger,
David Woodhouse, Brian Norris, Kelvin Cheung
From: Kelvin Cheung <keguang.zhang@gmail.com>
- Rename the file to clk-loongson1.c
- Add AC97, DMA and NAND clock
- Update clock names
- Remove superfluous error messages
Signed-off-by: Kelvin Cheung <keguang.zhang@gmail.com>
---
drivers/clk/Makefile | 2 +-
drivers/clk/clk-loongson1.c | 163 ++++++++++++++++++++++++++++++++++++++++++++
drivers/clk/clk-ls1x.c | 162 -------------------------------------------
3 files changed, 164 insertions(+), 163 deletions(-)
create mode 100644 drivers/clk/clk-loongson1.c
delete mode 100644 drivers/clk/clk-ls1x.c
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 46869d6..5845b2c 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -25,7 +25,7 @@ obj-$(CONFIG_COMMON_CLK_CS2000_CP) += clk-cs2000-cp.o
obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o
obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o
obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o
-obj-$(CONFIG_MACH_LOONGSON32) += clk-ls1x.o
+obj-$(CONFIG_MACH_LOONGSON32) += clk-loongson1.o
obj-$(CONFIG_COMMON_CLK_MAX_GEN) += clk-max-gen.o
obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
obj-$(CONFIG_COMMON_CLK_MAX77802) += clk-max77802.o
diff --git a/drivers/clk/clk-loongson1.c b/drivers/clk/clk-loongson1.c
new file mode 100644
index 0000000..ce2135c
--- /dev/null
+++ b/drivers/clk/clk-loongson1.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2012-2016 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#include <loongson1.h>
+
+#define OSC (33 * 1000000)
+#define DIV_APB 2
+
+static DEFINE_SPINLOCK(_lock);
+
+static int ls1x_pll_clk_enable(struct clk_hw *hw)
+{
+ return 0;
+}
+
+static void ls1x_pll_clk_disable(struct clk_hw *hw)
+{
+}
+
+static unsigned long ls1x_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ u32 pll, rate;
+
+ pll = __raw_readl(LS1X_CLK_PLL_FREQ);
+ rate = 12 + (pll & 0x3f) + (((pll >> 8) & 0x3ff) >> 10);
+ rate *= OSC;
+ rate >>= 1;
+
+ return rate;
+}
+
+static const struct clk_ops ls1x_pll_clk_ops = {
+ .enable = ls1x_pll_clk_enable,
+ .disable = ls1x_pll_clk_disable,
+ .recalc_rate = ls1x_pll_recalc_rate,
+};
+
+static struct clk *__init clk_register_pll(struct device *dev,
+ const char *name,
+ const char *parent_name,
+ unsigned long flags)
+{
+ struct clk_hw *hw;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ /* allocate the divider */
+ hw = kzalloc(sizeof(*hw), GFP_KERNEL);
+ if (!hw)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &ls1x_pll_clk_ops;
+ init.flags = flags | CLK_IS_BASIC;
+ init.parent_names = (parent_name ? &parent_name : NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+ hw->init = &init;
+
+ /* register the clock */
+ clk = clk_register(dev, hw);
+
+ if (IS_ERR(clk))
+ kfree(hw);
+
+ return clk;
+}
+
+static const char *const cpu_parents[] = { "cpu_clk_div", "osc_33m_clk", };
+static const char *const ahb_parents[] = { "ahb_clk_div", "osc_33m_clk", };
+static const char *const dc_parents[] = { "dc_clk_div", "osc_33m_clk", };
+
+void __init ls1x_clk_init(void)
+{
+ struct clk *clk;
+
+ clk = clk_register_fixed_rate(NULL, "osc_33m_clk", NULL, CLK_IS_ROOT,
+ OSC);
+ clk_register_clkdev(clk, "osc_33m_clk", NULL);
+
+ /* clock derived from 33 MHz OSC clk */
+ clk = clk_register_pll(NULL, "pll_clk", "osc_33m_clk", 0);
+ clk_register_clkdev(clk, "pll_clk", NULL);
+
+ /* clock derived from PLL clk */
+ /* _____
+ * _______________________| |
+ * OSC ___/ | MUX |___ CPU CLK
+ * \___ PLL ___ CPU DIV ___| |
+ * |_____|
+ */
+ clk = clk_register_divider(NULL, "cpu_clk_div", "pll_clk",
+ CLK_GET_RATE_NOCACHE, LS1X_CLK_PLL_DIV,
+ DIV_CPU_SHIFT, DIV_CPU_WIDTH,
+ CLK_DIVIDER_ONE_BASED |
+ CLK_DIVIDER_ROUND_CLOSEST, &_lock);
+ clk_register_clkdev(clk, "cpu_clk_div", NULL);
+ clk = clk_register_mux(NULL, "cpu_clk", cpu_parents,
+ ARRAY_SIZE(cpu_parents),
+ CLK_SET_RATE_NO_REPARENT, LS1X_CLK_PLL_DIV,
+ BYPASS_CPU_SHIFT, BYPASS_CPU_WIDTH, 0, &_lock);
+ clk_register_clkdev(clk, "cpu_clk", NULL);
+
+ /* _____
+ * _______________________| |
+ * OSC ___/ | MUX |___ DC CLK
+ * \___ PLL ___ DC DIV ___| |
+ * |_____|
+ */
+ clk = clk_register_divider(NULL, "dc_clk_div", "pll_clk",
+ 0, LS1X_CLK_PLL_DIV, DIV_DC_SHIFT,
+ DIV_DC_WIDTH, CLK_DIVIDER_ONE_BASED, &_lock);
+ clk_register_clkdev(clk, "dc_clk_div", NULL);
+ clk = clk_register_mux(NULL, "dc_clk", dc_parents,
+ ARRAY_SIZE(dc_parents),
+ CLK_SET_RATE_NO_REPARENT, LS1X_CLK_PLL_DIV,
+ BYPASS_DC_SHIFT, BYPASS_DC_WIDTH, 0, &_lock);
+ clk_register_clkdev(clk, "dc_clk", NULL);
+
+ /* _____
+ * _______________________| |
+ * OSC ___/ | MUX |___ DDR CLK
+ * \___ PLL ___ DDR DIV ___| |
+ * |_____|
+ */
+ clk = clk_register_divider(NULL, "ahb_clk_div", "pll_clk",
+ 0, LS1X_CLK_PLL_DIV, DIV_DDR_SHIFT,
+ DIV_DDR_WIDTH, CLK_DIVIDER_ONE_BASED,
+ &_lock);
+ clk_register_clkdev(clk, "ahb_clk_div", NULL);
+ clk = clk_register_mux(NULL, "ahb_clk", ahb_parents,
+ ARRAY_SIZE(ahb_parents),
+ CLK_SET_RATE_NO_REPARENT, LS1X_CLK_PLL_DIV,
+ BYPASS_DDR_SHIFT, BYPASS_DDR_WIDTH, 0, &_lock);
+ clk_register_clkdev(clk, "ahb_clk", NULL);
+ clk_register_clkdev(clk, "ls1x-dma", NULL);
+ clk_register_clkdev(clk, "stmmaceth", NULL);
+
+ /* clock derived from AHB clk */
+ /* APB clk is always half of the AHB clk */
+ clk = clk_register_fixed_factor(NULL, "apb_clk", "ahb_clk", 0, 1,
+ DIV_APB);
+ clk_register_clkdev(clk, "apb_clk", NULL);
+ clk_register_clkdev(clk, "ls1x-ac97", NULL);
+ clk_register_clkdev(clk, "ls1x-i2c", NULL);
+ clk_register_clkdev(clk, "ls1x-nand", NULL);
+ clk_register_clkdev(clk, "ls1x-pwmtimer", NULL);
+ clk_register_clkdev(clk, "ls1x-spi", NULL);
+ clk_register_clkdev(clk, "ls1x-wdt", NULL);
+ clk_register_clkdev(clk, "serial8250", NULL);
+}
diff --git a/drivers/clk/clk-ls1x.c b/drivers/clk/clk-ls1x.c
deleted file mode 100644
index d4c6198..0000000
--- a/drivers/clk/clk-ls1x.c
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright (c) 2012 Zhang, Keguang <keguang.zhang@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#include <linux/clkdev.h>
-#include <linux/clk-provider.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/err.h>
-
-#include <loongson1.h>
-
-#define OSC (33 * 1000000)
-#define DIV_APB 2
-
-static DEFINE_SPINLOCK(_lock);
-
-static int ls1x_pll_clk_enable(struct clk_hw *hw)
-{
- return 0;
-}
-
-static void ls1x_pll_clk_disable(struct clk_hw *hw)
-{
-}
-
-static unsigned long ls1x_pll_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
-{
- u32 pll, rate;
-
- pll = __raw_readl(LS1X_CLK_PLL_FREQ);
- rate = 12 + (pll & 0x3f) + (((pll >> 8) & 0x3ff) >> 10);
- rate *= OSC;
- rate >>= 1;
-
- return rate;
-}
-
-static const struct clk_ops ls1x_pll_clk_ops = {
- .enable = ls1x_pll_clk_enable,
- .disable = ls1x_pll_clk_disable,
- .recalc_rate = ls1x_pll_recalc_rate,
-};
-
-static struct clk *__init clk_register_pll(struct device *dev,
- const char *name,
- const char *parent_name,
- unsigned long flags)
-{
- struct clk_hw *hw;
- struct clk *clk;
- struct clk_init_data init;
-
- /* allocate the divider */
- hw = kzalloc(sizeof(struct clk_hw), GFP_KERNEL);
- if (!hw) {
- pr_err("%s: could not allocate clk_hw\n", __func__);
- return ERR_PTR(-ENOMEM);
- }
-
- init.name = name;
- init.ops = &ls1x_pll_clk_ops;
- init.flags = flags | CLK_IS_BASIC;
- init.parent_names = (parent_name ? &parent_name : NULL);
- init.num_parents = (parent_name ? 1 : 0);
- hw->init = &init;
-
- /* register the clock */
- clk = clk_register(dev, hw);
-
- if (IS_ERR(clk))
- kfree(hw);
-
- return clk;
-}
-
-static const char * const cpu_parents[] = { "cpu_clk_div", "osc_33m_clk", };
-static const char * const ahb_parents[] = { "ahb_clk_div", "osc_33m_clk", };
-static const char * const dc_parents[] = { "dc_clk_div", "osc_33m_clk", };
-
-void __init ls1x_clk_init(void)
-{
- struct clk *clk;
-
- clk = clk_register_fixed_rate(NULL, "osc_33m_clk", NULL, CLK_IS_ROOT,
- OSC);
- clk_register_clkdev(clk, "osc_33m_clk", NULL);
-
- /* clock derived from 33 MHz OSC clk */
- clk = clk_register_pll(NULL, "pll_clk", "osc_33m_clk", 0);
- clk_register_clkdev(clk, "pll_clk", NULL);
-
- /* clock derived from PLL clk */
- /* _____
- * _______________________| |
- * OSC ___/ | MUX |___ CPU CLK
- * \___ PLL ___ CPU DIV ___| |
- * |_____|
- */
- clk = clk_register_divider(NULL, "cpu_clk_div", "pll_clk",
- CLK_GET_RATE_NOCACHE, LS1X_CLK_PLL_DIV,
- DIV_CPU_SHIFT, DIV_CPU_WIDTH,
- CLK_DIVIDER_ONE_BASED |
- CLK_DIVIDER_ROUND_CLOSEST, &_lock);
- clk_register_clkdev(clk, "cpu_clk_div", NULL);
- clk = clk_register_mux(NULL, "cpu_clk", cpu_parents,
- ARRAY_SIZE(cpu_parents),
- CLK_SET_RATE_NO_REPARENT, LS1X_CLK_PLL_DIV,
- BYPASS_CPU_SHIFT, BYPASS_CPU_WIDTH, 0, &_lock);
- clk_register_clkdev(clk, "cpu_clk", NULL);
-
- /* _____
- * _______________________| |
- * OSC ___/ | MUX |___ DC CLK
- * \___ PLL ___ DC DIV ___| |
- * |_____|
- */
- clk = clk_register_divider(NULL, "dc_clk_div", "pll_clk",
- 0, LS1X_CLK_PLL_DIV, DIV_DC_SHIFT,
- DIV_DC_WIDTH, CLK_DIVIDER_ONE_BASED, &_lock);
- clk_register_clkdev(clk, "dc_clk_div", NULL);
- clk = clk_register_mux(NULL, "dc_clk", dc_parents,
- ARRAY_SIZE(dc_parents),
- CLK_SET_RATE_NO_REPARENT, LS1X_CLK_PLL_DIV,
- BYPASS_DC_SHIFT, BYPASS_DC_WIDTH, 0, &_lock);
- clk_register_clkdev(clk, "dc_clk", NULL);
-
- /* _____
- * _______________________| |
- * OSC ___/ | MUX |___ DDR CLK
- * \___ PLL ___ DDR DIV ___| |
- * |_____|
- */
- clk = clk_register_divider(NULL, "ahb_clk_div", "pll_clk",
- 0, LS1X_CLK_PLL_DIV, DIV_DDR_SHIFT,
- DIV_DDR_WIDTH, CLK_DIVIDER_ONE_BASED,
- &_lock);
- clk_register_clkdev(clk, "ahb_clk_div", NULL);
- clk = clk_register_mux(NULL, "ahb_clk", ahb_parents,
- ARRAY_SIZE(ahb_parents),
- CLK_SET_RATE_NO_REPARENT, LS1X_CLK_PLL_DIV,
- BYPASS_DDR_SHIFT, BYPASS_DDR_WIDTH, 0, &_lock);
- clk_register_clkdev(clk, "ahb_clk", NULL);
- clk_register_clkdev(clk, "stmmaceth", NULL);
-
- /* clock derived from AHB clk */
- /* APB clk is always half of the AHB clk */
- clk = clk_register_fixed_factor(NULL, "apb_clk", "ahb_clk", 0, 1,
- DIV_APB);
- clk_register_clkdev(clk, "apb_clk", NULL);
- clk_register_clkdev(clk, "ls1x_i2c", NULL);
- clk_register_clkdev(clk, "ls1x_pwmtimer", NULL);
- clk_register_clkdev(clk, "ls1x_spi", NULL);
- clk_register_clkdev(clk, "ls1x_wdt", NULL);
- clk_register_clkdev(clk, "serial8250", NULL);
-}
--
1.9.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 2/7] cpufreq: Loongson1: Update cpufreq of Loongson1B
2016-04-06 12:09 [PATCH 0/7] MIPS: Loongson1B: add NAND, DMA and GPIO support Keguang Zhang
2016-04-06 12:09 ` [PATCH 1/7] clk: Loongson1: Update clocks of Loongson1B Keguang Zhang
@ 2016-04-06 12:09 ` Keguang Zhang
2016-04-06 12:09 ` [PATCH 3/7] dmaengine: Loongson1: add Loongson1 dmaengine driver Keguang Zhang
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Keguang Zhang @ 2016-04-06 12:09 UTC (permalink / raw)
To: linux-kernel, linux-mips, linux-clk, linux-pm, dmaengine,
linux-gpio, linux-mtd
Cc: Ralf Baechle, Michael Turquette, Stephen Boyd, Rafael J. Wysocki,
Viresh Kumar, Vinod Koul, Dan Williams, Linus Walleij,
Alexandre Courbot, Boris Brezillon, Richard Weinberger,
David Woodhouse, Brian Norris, Kelvin Cheung
From: Kelvin Cheung <keguang.zhang@gmail.com>
- Rename the file to loongson1-cpufreq.c
- Use kcalloc() instead of kzalloc()
- Use devm_kzalloc() instead of global structure
- Use dev_get_platdata() to access the platform_data field
instead of referencing it directly
- Remove superfluous error messages
Signed-off-by: Kelvin Cheung <keguang.zhang@gmail.com>
---
drivers/cpufreq/Makefile | 2 +-
drivers/cpufreq/loongson1-cpufreq.c | 230 ++++++++++++++++++++++++++++++++++++
drivers/cpufreq/ls1x-cpufreq.c | 222 ----------------------------------
3 files changed, 231 insertions(+), 223 deletions(-)
create mode 100644 drivers/cpufreq/loongson1-cpufreq.c
delete mode 100644 drivers/cpufreq/ls1x-cpufreq.c
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index 9e63fb1..bebe9c8 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -100,7 +100,7 @@ obj-$(CONFIG_CRIS_MACH_ARTPEC3) += cris-artpec3-cpufreq.o
obj-$(CONFIG_ETRAXFS) += cris-etraxfs-cpufreq.o
obj-$(CONFIG_IA64_ACPI_CPUFREQ) += ia64-acpi-cpufreq.o
obj-$(CONFIG_LOONGSON2_CPUFREQ) += loongson2_cpufreq.o
-obj-$(CONFIG_LOONGSON1_CPUFREQ) += ls1x-cpufreq.o
+obj-$(CONFIG_LOONGSON1_CPUFREQ) += loongson1-cpufreq.o
obj-$(CONFIG_SH_CPU_FREQ) += sh-cpufreq.o
obj-$(CONFIG_SPARC_US2E_CPUFREQ) += sparc-us2e-cpufreq.o
obj-$(CONFIG_SPARC_US3_CPUFREQ) += sparc-us3-cpufreq.o
diff --git a/drivers/cpufreq/loongson1-cpufreq.c b/drivers/cpufreq/loongson1-cpufreq.c
new file mode 100644
index 0000000..d029211
--- /dev/null
+++ b/drivers/cpufreq/loongson1-cpufreq.c
@@ -0,0 +1,230 @@
+/*
+ * CPU Frequency Scaling for Loongson 1 SoC
+ *
+ * Copyright (C) 2014-2016 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/cpu.h>
+#include <linux/cpufreq.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <cpufreq.h>
+#include <loongson1.h>
+
+struct ls1x_cpufreq {
+ struct device *dev;
+ struct clk *clk; /* CPU clk */
+ struct clk *mux_clk; /* MUX of CPU clk */
+ struct clk *pll_clk; /* PLL clk */
+ struct clk *osc_clk; /* OSC clk */
+ unsigned int max_freq;
+ unsigned int min_freq;
+};
+
+static struct ls1x_cpufreq *cpufreq;
+
+static int ls1x_cpufreq_notifier(struct notifier_block *nb,
+ unsigned long val, void *data)
+{
+ if (val == CPUFREQ_POSTCHANGE)
+ current_cpu_data.udelay_val = loops_per_jiffy;
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block ls1x_cpufreq_notifier_block = {
+ .notifier_call = ls1x_cpufreq_notifier
+};
+
+static int ls1x_cpufreq_target(struct cpufreq_policy *policy,
+ unsigned int index)
+{
+ struct device *cpu_dev = get_cpu_device(policy->cpu);
+ unsigned int old_freq, new_freq;
+
+ old_freq = policy->cur;
+ new_freq = policy->freq_table[index].frequency;
+
+ /*
+ * The procedure of reconfiguring CPU clk is as below.
+ *
+ * - Reparent CPU clk to OSC clk
+ * - Reset CPU clock (very important)
+ * - Reconfigure CPU DIV
+ * - Reparent CPU clk back to CPU DIV clk
+ */
+
+ clk_set_parent(policy->clk, cpufreq->osc_clk);
+ __raw_writel(__raw_readl(LS1X_CLK_PLL_DIV) | RST_CPU_EN | RST_CPU,
+ LS1X_CLK_PLL_DIV);
+ __raw_writel(__raw_readl(LS1X_CLK_PLL_DIV) & ~(RST_CPU_EN | RST_CPU),
+ LS1X_CLK_PLL_DIV);
+ clk_set_rate(cpufreq->mux_clk, new_freq * 1000);
+ clk_set_parent(policy->clk, cpufreq->mux_clk);
+ dev_dbg(cpu_dev, "%u KHz --> %u KHz\n", old_freq, new_freq);
+
+ return 0;
+}
+
+static int ls1x_cpufreq_init(struct cpufreq_policy *policy)
+{
+ struct device *cpu_dev = get_cpu_device(policy->cpu);
+ struct cpufreq_frequency_table *freq_tbl;
+ unsigned int pll_freq, freq;
+ int steps, i, ret;
+
+ pll_freq = clk_get_rate(cpufreq->pll_clk) / 1000;
+
+ steps = 1 << DIV_CPU_WIDTH;
+ freq_tbl = kcalloc(steps, sizeof(*freq_tbl), GFP_KERNEL);
+ if (!freq_tbl)
+ return -ENOMEM;
+
+ for (i = 0; i < (steps - 1); i++) {
+ freq = pll_freq / (i + 1);
+ if ((freq < cpufreq->min_freq) || (freq > cpufreq->max_freq))
+ freq_tbl[i].frequency = CPUFREQ_ENTRY_INVALID;
+ else
+ freq_tbl[i].frequency = freq;
+ dev_dbg(cpu_dev,
+ "cpufreq table: index %d: frequency %d\n", i,
+ freq_tbl[i].frequency);
+ }
+ freq_tbl[i].frequency = CPUFREQ_TABLE_END;
+
+ policy->clk = cpufreq->clk;
+ ret = cpufreq_generic_init(policy, freq_tbl, 0);
+ if (ret)
+ kfree(freq_tbl);
+
+ return ret;
+}
+
+static int ls1x_cpufreq_exit(struct cpufreq_policy *policy)
+{
+ kfree(policy->freq_table);
+ return 0;
+}
+
+static struct cpufreq_driver ls1x_cpufreq_driver = {
+ .name = "cpufreq-ls1x",
+ .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
+ .verify = cpufreq_generic_frequency_table_verify,
+ .target_index = ls1x_cpufreq_target,
+ .get = cpufreq_generic_get,
+ .init = ls1x_cpufreq_init,
+ .exit = ls1x_cpufreq_exit,
+ .attr = cpufreq_generic_attr,
+};
+
+static int ls1x_cpufreq_remove(struct platform_device *pdev)
+{
+ cpufreq_unregister_notifier(&ls1x_cpufreq_notifier_block,
+ CPUFREQ_TRANSITION_NOTIFIER);
+ cpufreq_unregister_driver(&ls1x_cpufreq_driver);
+
+ return 0;
+}
+
+static int ls1x_cpufreq_probe(struct platform_device *pdev)
+{
+ struct plat_ls1x_cpufreq *pdata = dev_get_platdata(&pdev->dev);
+ struct clk *clk;
+ int ret;
+
+ if (!pdata || !pdata->clk_name || !pdata->osc_clk_name) {
+ dev_err(&pdev->dev, "platform data missing\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ cpufreq =
+ devm_kzalloc(&pdev->dev, sizeof(struct ls1x_cpufreq), GFP_KERNEL);
+ if (!cpufreq) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cpufreq->dev = &pdev->dev;
+
+ clk = devm_clk_get(&pdev->dev, pdata->clk_name);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "unable to get %s clock\n",
+ pdata->clk_name);
+ ret = PTR_ERR(clk);
+ goto out;
+ }
+ cpufreq->clk = clk;
+
+ clk = clk_get_parent(clk);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "unable to get parent of %s clock\n",
+ __clk_get_name(cpufreq->clk));
+ ret = PTR_ERR(clk);
+ goto out;
+ }
+ cpufreq->mux_clk = clk;
+
+ clk = clk_get_parent(clk);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "unable to get parent of %s clock\n",
+ __clk_get_name(cpufreq->mux_clk));
+ ret = PTR_ERR(clk);
+ goto out;
+ }
+ cpufreq->pll_clk = clk;
+
+ clk = devm_clk_get(&pdev->dev, pdata->osc_clk_name);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "unable to get %s clock\n",
+ pdata->osc_clk_name);
+ ret = PTR_ERR(clk);
+ goto out;
+ }
+ cpufreq->osc_clk = clk;
+
+ cpufreq->max_freq = pdata->max_freq;
+ cpufreq->min_freq = pdata->min_freq;
+
+ ret = cpufreq_register_driver(&ls1x_cpufreq_driver);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "failed to register CPUFreq driver: %d\n", ret);
+ goto out;
+ }
+
+ ret = cpufreq_register_notifier(&ls1x_cpufreq_notifier_block,
+ CPUFREQ_TRANSITION_NOTIFIER);
+
+ if (!ret)
+ goto out;
+
+ dev_err(&pdev->dev, "failed to register CPUFreq notifier: %d\n", ret);
+
+ cpufreq_unregister_driver(&ls1x_cpufreq_driver);
+out:
+ return ret;
+}
+
+static struct platform_driver ls1x_cpufreq_platdrv = {
+ .probe = ls1x_cpufreq_probe,
+ .remove = ls1x_cpufreq_remove,
+ .driver = {
+ .name = "ls1x-cpufreq",
+ },
+};
+
+module_platform_driver(ls1x_cpufreq_platdrv);
+
+MODULE_AUTHOR("Kelvin Cheung <keguang.zhang@gmail.com>");
+MODULE_DESCRIPTION("Loongson1 CPUFreq driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/cpufreq/ls1x-cpufreq.c b/drivers/cpufreq/ls1x-cpufreq.c
deleted file mode 100644
index 262581b..0000000
--- a/drivers/cpufreq/ls1x-cpufreq.c
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * CPU Frequency Scaling for Loongson 1 SoC
- *
- * Copyright (C) 2014 Zhang, Keguang <keguang.zhang@gmail.com>
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#include <linux/clk.h>
-#include <linux/clk-provider.h>
-#include <linux/cpu.h>
-#include <linux/cpufreq.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-
-#include <cpufreq.h>
-#include <loongson1.h>
-
-static struct {
- struct device *dev;
- struct clk *clk; /* CPU clk */
- struct clk *mux_clk; /* MUX of CPU clk */
- struct clk *pll_clk; /* PLL clk */
- struct clk *osc_clk; /* OSC clk */
- unsigned int max_freq;
- unsigned int min_freq;
-} ls1x_cpufreq;
-
-static int ls1x_cpufreq_notifier(struct notifier_block *nb,
- unsigned long val, void *data)
-{
- if (val == CPUFREQ_POSTCHANGE)
- current_cpu_data.udelay_val = loops_per_jiffy;
-
- return NOTIFY_OK;
-}
-
-static struct notifier_block ls1x_cpufreq_notifier_block = {
- .notifier_call = ls1x_cpufreq_notifier
-};
-
-static int ls1x_cpufreq_target(struct cpufreq_policy *policy,
- unsigned int index)
-{
- unsigned int old_freq, new_freq;
-
- old_freq = policy->cur;
- new_freq = policy->freq_table[index].frequency;
-
- /*
- * The procedure of reconfiguring CPU clk is as below.
- *
- * - Reparent CPU clk to OSC clk
- * - Reset CPU clock (very important)
- * - Reconfigure CPU DIV
- * - Reparent CPU clk back to CPU DIV clk
- */
-
- dev_dbg(ls1x_cpufreq.dev, "%u KHz --> %u KHz\n", old_freq, new_freq);
- clk_set_parent(policy->clk, ls1x_cpufreq.osc_clk);
- __raw_writel(__raw_readl(LS1X_CLK_PLL_DIV) | RST_CPU_EN | RST_CPU,
- LS1X_CLK_PLL_DIV);
- __raw_writel(__raw_readl(LS1X_CLK_PLL_DIV) & ~(RST_CPU_EN | RST_CPU),
- LS1X_CLK_PLL_DIV);
- clk_set_rate(ls1x_cpufreq.mux_clk, new_freq * 1000);
- clk_set_parent(policy->clk, ls1x_cpufreq.mux_clk);
-
- return 0;
-}
-
-static int ls1x_cpufreq_init(struct cpufreq_policy *policy)
-{
- struct cpufreq_frequency_table *freq_tbl;
- unsigned int pll_freq, freq;
- int steps, i, ret;
-
- pll_freq = clk_get_rate(ls1x_cpufreq.pll_clk) / 1000;
-
- steps = 1 << DIV_CPU_WIDTH;
- freq_tbl = kzalloc(sizeof(*freq_tbl) * steps, GFP_KERNEL);
- if (!freq_tbl) {
- dev_err(ls1x_cpufreq.dev,
- "failed to alloc cpufreq_frequency_table\n");
- ret = -ENOMEM;
- goto out;
- }
-
- for (i = 0; i < (steps - 1); i++) {
- freq = pll_freq / (i + 1);
- if ((freq < ls1x_cpufreq.min_freq) ||
- (freq > ls1x_cpufreq.max_freq))
- freq_tbl[i].frequency = CPUFREQ_ENTRY_INVALID;
- else
- freq_tbl[i].frequency = freq;
- dev_dbg(ls1x_cpufreq.dev,
- "cpufreq table: index %d: frequency %d\n", i,
- freq_tbl[i].frequency);
- }
- freq_tbl[i].frequency = CPUFREQ_TABLE_END;
-
- policy->clk = ls1x_cpufreq.clk;
- ret = cpufreq_generic_init(policy, freq_tbl, 0);
- if (ret)
- kfree(freq_tbl);
-out:
- return ret;
-}
-
-static int ls1x_cpufreq_exit(struct cpufreq_policy *policy)
-{
- kfree(policy->freq_table);
- return 0;
-}
-
-static struct cpufreq_driver ls1x_cpufreq_driver = {
- .name = "cpufreq-ls1x",
- .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
- .verify = cpufreq_generic_frequency_table_verify,
- .target_index = ls1x_cpufreq_target,
- .get = cpufreq_generic_get,
- .init = ls1x_cpufreq_init,
- .exit = ls1x_cpufreq_exit,
- .attr = cpufreq_generic_attr,
-};
-
-static int ls1x_cpufreq_remove(struct platform_device *pdev)
-{
- cpufreq_unregister_notifier(&ls1x_cpufreq_notifier_block,
- CPUFREQ_TRANSITION_NOTIFIER);
- cpufreq_unregister_driver(&ls1x_cpufreq_driver);
-
- return 0;
-}
-
-static int ls1x_cpufreq_probe(struct platform_device *pdev)
-{
- struct plat_ls1x_cpufreq *pdata = pdev->dev.platform_data;
- struct clk *clk;
- int ret;
-
- if (!pdata || !pdata->clk_name || !pdata->osc_clk_name)
- return -EINVAL;
-
- ls1x_cpufreq.dev = &pdev->dev;
-
- clk = devm_clk_get(&pdev->dev, pdata->clk_name);
- if (IS_ERR(clk)) {
- dev_err(ls1x_cpufreq.dev, "unable to get %s clock\n",
- pdata->clk_name);
- ret = PTR_ERR(clk);
- goto out;
- }
- ls1x_cpufreq.clk = clk;
-
- clk = clk_get_parent(clk);
- if (IS_ERR(clk)) {
- dev_err(ls1x_cpufreq.dev, "unable to get parent of %s clock\n",
- __clk_get_name(ls1x_cpufreq.clk));
- ret = PTR_ERR(clk);
- goto out;
- }
- ls1x_cpufreq.mux_clk = clk;
-
- clk = clk_get_parent(clk);
- if (IS_ERR(clk)) {
- dev_err(ls1x_cpufreq.dev, "unable to get parent of %s clock\n",
- __clk_get_name(ls1x_cpufreq.mux_clk));
- ret = PTR_ERR(clk);
- goto out;
- }
- ls1x_cpufreq.pll_clk = clk;
-
- clk = devm_clk_get(&pdev->dev, pdata->osc_clk_name);
- if (IS_ERR(clk)) {
- dev_err(ls1x_cpufreq.dev, "unable to get %s clock\n",
- pdata->osc_clk_name);
- ret = PTR_ERR(clk);
- goto out;
- }
- ls1x_cpufreq.osc_clk = clk;
-
- ls1x_cpufreq.max_freq = pdata->max_freq;
- ls1x_cpufreq.min_freq = pdata->min_freq;
-
- ret = cpufreq_register_driver(&ls1x_cpufreq_driver);
- if (ret) {
- dev_err(ls1x_cpufreq.dev,
- "failed to register cpufreq driver: %d\n", ret);
- goto out;
- }
-
- ret = cpufreq_register_notifier(&ls1x_cpufreq_notifier_block,
- CPUFREQ_TRANSITION_NOTIFIER);
-
- if (!ret)
- goto out;
-
- dev_err(ls1x_cpufreq.dev, "failed to register cpufreq notifier: %d\n",
- ret);
-
- cpufreq_unregister_driver(&ls1x_cpufreq_driver);
-out:
- return ret;
-}
-
-static struct platform_driver ls1x_cpufreq_platdrv = {
- .driver = {
- .name = "ls1x-cpufreq",
- },
- .probe = ls1x_cpufreq_probe,
- .remove = ls1x_cpufreq_remove,
-};
-
-module_platform_driver(ls1x_cpufreq_platdrv);
-
-MODULE_AUTHOR("Kelvin Cheung <keguang.zhang@gmail.com>");
-MODULE_DESCRIPTION("Loongson 1 CPUFreq driver");
-MODULE_LICENSE("GPL");
--
1.9.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 3/7] dmaengine: Loongson1: add Loongson1 dmaengine driver
2016-04-06 12:09 [PATCH 0/7] MIPS: Loongson1B: add NAND, DMA and GPIO support Keguang Zhang
2016-04-06 12:09 ` [PATCH 1/7] clk: Loongson1: Update clocks of Loongson1B Keguang Zhang
2016-04-06 12:09 ` [PATCH 2/7] cpufreq: Loongson1: Update cpufreq " Keguang Zhang
@ 2016-04-06 12:09 ` Keguang Zhang
2016-04-06 12:09 ` [PATCH 4/7] mtd: nand: add Loongson1 NAND driver Keguang Zhang
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Keguang Zhang @ 2016-04-06 12:09 UTC (permalink / raw)
To: linux-kernel, linux-mips, linux-clk, linux-pm, dmaengine,
linux-gpio, linux-mtd
Cc: Ralf Baechle, Michael Turquette, Stephen Boyd, Rafael J. Wysocki,
Viresh Kumar, Vinod Koul, Dan Williams, Linus Walleij,
Alexandre Courbot, Boris Brezillon, Richard Weinberger,
David Woodhouse, Brian Norris, Kelvin Cheung
From: Kelvin Cheung <keguang.zhang@gmail.com>
This patch adds DMA Engine driver for Loongson1B.
Signed-off-by: Kelvin Cheung <keguang.zhang@gmail.com>
---
drivers/dma/Kconfig | 9 +
drivers/dma/Makefile | 1 +
drivers/dma/loongson1-dma.c | 546 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 556 insertions(+)
create mode 100644 drivers/dma/loongson1-dma.c
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index d96d87c..835d212 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -527,6 +527,15 @@ config ZX_DMA
help
Support the DMA engine for ZTE ZX296702 platform devices.
+config DMA_LOONGSON1
+ tristate "Loongson1 DMA support"
+ depends on MACH_LOONGSON32
+ select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
+ help
+ This selects support for the DMA controller in Loongson1 SoCs,
+ and is required by Loongson1 NAND Flash and AC97 support.
+
# driver files
source "drivers/dma/bestcomm/Kconfig"
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 6084127..b0eceb0 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -65,6 +65,7 @@ obj-$(CONFIG_TI_DMA_CROSSBAR) += ti-dma-crossbar.o
obj-$(CONFIG_TI_EDMA) += edma.o
obj-$(CONFIG_XGENE_DMA) += xgene-dma.o
obj-$(CONFIG_ZX_DMA) += zx296702_dma.o
+obj-$(CONFIG_DMA_LOONGSON1) += loongson1-dma.o
obj-y += qcom/
obj-y += xilinx/
diff --git a/drivers/dma/loongson1-dma.c b/drivers/dma/loongson1-dma.c
new file mode 100644
index 0000000..687eed7
--- /dev/null
+++ b/drivers/dma/loongson1-dma.c
@@ -0,0 +1,546 @@
+/*
+ * DMA Driver for Loongson 1 SoC
+ *
+ * Copyright (C) 2015-2016 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/dmapool.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_dma.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <dma.h>
+
+#include "dmaengine.h"
+#include "virt-dma.h"
+
+/* Loongson 1 DMA Register Definitions */
+#define DMA_CTRL 0x0
+
+/* DMA Control Register Bits */
+#define DMA_STOP BIT(4)
+#define DMA_START BIT(3)
+#define ASK_VALID BIT(2)
+
+#define DMA_ADDR_MASK (0xffffffc0)
+
+/* DMA H/W Descriptor Bits */
+#define NEXT_EN BIT(0)
+
+/* DMA Command Register Bits */
+#define DMA_RAM2DEV BIT(12)
+#define DMA_TRANS_OVER BIT(3)
+#define DMA_SINGLE_TRANS_OVER BIT(2)
+#define DMA_INT BIT(1)
+#define DMA_INT_MASK BIT(0)
+
+struct ls1x_dma_hwdesc {
+ u32 next; /* next descriptor address */
+ u32 saddr; /* memory DMA address */
+ u32 daddr; /* device DMA address */
+ u32 length;
+ u32 stride;
+ u32 cycles;
+ u32 cmd;
+ u32 phys; /* used by driver */
+} __aligned(64);
+
+struct ls1x_dma_desc {
+ struct virt_dma_desc vdesc;
+ struct ls1x_dma_chan *chan;
+
+ enum dma_transfer_direction dir;
+ enum dma_transaction_type type;
+
+ unsigned int nr_descs; /* number of descriptors */
+ unsigned int nr_done; /* number of completed descriptors */
+ struct ls1x_dma_hwdesc *desc[0]; /* DMA coherent descriptors */
+};
+
+struct ls1x_dma_chan {
+ struct virt_dma_chan vchan;
+ unsigned int id;
+ void __iomem *reg_base;
+ unsigned int irq;
+ struct dma_pool *desc_pool;
+
+ struct dma_slave_config config;
+
+ struct ls1x_dma_desc *dma_desc;
+ unsigned int curr_hwdesc;
+};
+
+struct ls1x_dma {
+ struct dma_device dma_dev;
+ struct clk *clk;
+ void __iomem *reg_base;
+
+ unsigned int nr_dma_chans;
+ struct ls1x_dma_chan dma_chan[0];
+};
+
+#define to_ls1x_dma_chan(chan) \
+ container_of(chan, struct ls1x_dma_chan, vchan.chan)
+
+#define to_ls1x_dma_desc(vdesc) \
+ container_of(vdesc, struct ls1x_dma_desc, vdesc)
+
+/* macros for registers read/write */
+#define chan_writel(chan, off, val) \
+ __raw_writel((val), (chan)->reg_base + (off))
+
+#define chan_readl(chan, off) \
+ __raw_readl((chan)->reg_base + (off))
+
+bool ls1x_dma_filter_fn(struct dma_chan *chan, void *param)
+{
+ struct ls1x_dma_chan *dma_chan = to_ls1x_dma_chan(chan);
+ unsigned int chan_id = *(unsigned int *)param;
+
+ if (chan_id == dma_chan->id)
+ return true;
+ else
+ return false;
+}
+
+static void ls1x_dma_free_chan_resources(struct dma_chan *chan)
+{
+ struct ls1x_dma_chan *dma_chan = to_ls1x_dma_chan(chan);
+
+ vchan_free_chan_resources(&dma_chan->vchan);
+ dma_pool_destroy(dma_chan->desc_pool);
+ dma_chan->desc_pool = NULL;
+}
+
+static int ls1x_dma_alloc_chan_resources(struct dma_chan *chan)
+{
+ struct ls1x_dma_chan *dma_chan = to_ls1x_dma_chan(chan);
+
+ if (dma_chan->desc_pool)
+ return 0;
+
+ dma_chan->desc_pool = dma_pool_create(dma_chan_name(chan),
+ chan->device->dev,
+ sizeof(struct ls1x_dma_hwdesc),
+ __alignof__(struct
+ ls1x_dma_hwdesc), 0);
+ if (!dma_chan->desc_pool) {
+ dev_err(&chan->dev->device,
+ "failed to allocate descriptor pool\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void ls1x_dma_free_desc(struct virt_dma_desc *vdesc)
+{
+ struct ls1x_dma_desc *dma_desc = to_ls1x_dma_desc(vdesc);
+ int i;
+
+ for (i = 0; i < dma_desc->nr_descs; i++)
+ dma_pool_free(dma_desc->chan->desc_pool, dma_desc->desc[i],
+ dma_desc->desc[i]->phys);
+ kfree(dma_desc);
+}
+
+static struct ls1x_dma_desc *ls1x_dma_alloc_desc(struct ls1x_dma_chan *dma_chan,
+ int sg_len)
+{
+ struct ls1x_dma_desc *dma_desc;
+ struct dma_chan *chan = &dma_chan->vchan.chan;
+ dma_addr_t desc_phys;
+ int i;
+
+ dma_desc =
+ kzalloc(sizeof(struct ls1x_dma_desc) +
+ sg_len * sizeof(struct ls1x_dma_hwdesc *), GFP_NOWAIT);
+ if (!dma_desc) {
+ dev_err(&chan->dev->device,
+ "failed to allocate DMA descriptor\n");
+ return NULL;
+ }
+
+ for (i = 0; i < sg_len; i++) {
+ dma_desc->desc[i] = dma_pool_alloc(dma_chan->desc_pool,
+ GFP_NOWAIT, &desc_phys);
+ if (!dma_desc->desc[i])
+ goto err;
+
+ /*memorize the physical address of descriptor */
+ dma_desc->desc[i]->phys = desc_phys;
+ }
+ dma_desc->chan = dma_chan;
+ dma_desc->nr_descs = sg_len;
+ dma_desc->nr_done = 0;
+
+ return dma_desc;
+err:
+ dev_err(&chan->dev->device, "failed to allocate H/W DMA descriptor\n");
+
+ while (--i >= 0)
+ dma_pool_free(dma_chan->desc_pool, dma_desc->desc[i],
+ dma_desc->desc[i]->phys);
+ kfree(dma_desc);
+
+ return NULL;
+}
+
+static struct dma_async_tx_descriptor *
+ls1x_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
+ unsigned int sg_len,
+ enum dma_transfer_direction direction,
+ unsigned long flags, void *context)
+{
+ struct ls1x_dma_chan *dma_chan = to_ls1x_dma_chan(chan);
+ struct dma_slave_config *config = &dma_chan->config;
+ struct ls1x_dma_desc *dma_desc;
+ struct scatterlist *sg;
+ unsigned int dev_addr, bus_width, cmd, i;
+
+ if (!is_slave_direction(direction)) {
+ dev_err(&chan->dev->device, "invalid DMA direction\n");
+ return NULL;
+ }
+
+ dev_dbg(&chan->dev->device, "sg_len=%d, dir=%s, flags=0x%lx\n", sg_len,
+ direction == DMA_MEM_TO_DEV ? "to device" : "from device",
+ flags);
+
+ switch (direction) {
+ case DMA_MEM_TO_DEV:
+ dev_addr = config->dst_addr;
+ bus_width = config->dst_addr_width;
+ cmd = DMA_RAM2DEV | DMA_INT;
+ break;
+ case DMA_DEV_TO_MEM:
+ dev_addr = config->src_addr;
+ bus_width = config->src_addr_width;
+ cmd = DMA_INT;
+ break;
+ default:
+ dev_err(&chan->dev->device,
+ "unsupported DMA transfer mode: %d\n", direction);
+ return NULL;
+ }
+
+ /*allocate DMA descriptors */
+ dma_desc = ls1x_dma_alloc_desc(dma_chan, sg_len);
+ if (!dma_desc)
+ return NULL;
+ dma_desc->dir = direction;
+ dma_desc->type = DMA_SLAVE;
+
+ /*config DMA descriptors */
+ for_each_sg(sgl, sg, sg_len, i) {
+ dma_addr_t buf_addr = sg_dma_address(sg);
+ size_t buf_len = sg_dma_len(sg);
+
+ if (!IS_ALIGNED(buf_addr, 4 * bus_width)) {
+ dev_err(&chan->dev->device,
+ "buf_addr is not aligned on %d-byte boundary\n",
+ 4 * bus_width);
+ ls1x_dma_free_desc(&dma_desc->vdesc);
+ return NULL;
+ }
+
+ if (!IS_ALIGNED(buf_len, bus_width))
+ dev_warn(&chan->dev->device,
+ "buf_len is not aligned on %d-byte boundary\n",
+ bus_width);
+
+ dma_desc->desc[i]->saddr = buf_addr;
+ dma_desc->desc[i]->daddr = dev_addr;
+ dma_desc->desc[i]->length = buf_len / bus_width;
+ dma_desc->desc[i]->stride = 0;
+ dma_desc->desc[i]->cycles = 1;
+ dma_desc->desc[i]->cmd = cmd;
+ dma_desc->desc[i]->next =
+ sg_is_last(sg) ? 0 : dma_desc->desc[i + 1]->phys;
+
+ dev_dbg(&chan->dev->device,
+ "desc=%p, saddr=%08x, daddr=%08x, length=%u\n",
+ &dma_desc->desc[i], buf_addr, dev_addr, buf_len);
+ }
+
+ return vchan_tx_prep(&dma_chan->vchan, &dma_desc->vdesc, flags);
+}
+
+static int ls1x_dma_slave_config(struct dma_chan *chan,
+ struct dma_slave_config *config)
+{
+ struct ls1x_dma_chan *dma_chan = to_ls1x_dma_chan(chan);
+
+ memcpy(&dma_chan->config, config, sizeof(struct dma_slave_config));
+
+ return 0;
+}
+
+static int ls1x_dma_terminate_all(struct dma_chan *chan)
+{
+ struct ls1x_dma_chan *dma_chan = to_ls1x_dma_chan(chan);
+ unsigned long flags;
+ LIST_HEAD(head);
+
+ spin_lock_irqsave(&dma_chan->vchan.lock, flags);
+
+ chan_writel(dma_chan, DMA_CTRL,
+ chan_readl(dma_chan, DMA_CTRL) | DMA_STOP);
+ dma_chan->dma_desc = NULL;
+ vchan_get_all_descriptors(&dma_chan->vchan, &head);
+
+ spin_unlock_irqrestore(&dma_chan->vchan.lock, flags);
+
+ vchan_dma_desc_free_list(&dma_chan->vchan, &head);
+
+ return 0;
+}
+
+static size_t ls1x_dma_desc_residue(struct ls1x_dma_desc *dma_desc,
+ unsigned int next_sg)
+{
+ struct ls1x_dma_chan *dma_chan = dma_desc->chan;
+ struct dma_slave_config *config = &dma_chan->config;
+ unsigned int i, bus_width, bytes = 0;
+
+ if (dma_desc->dir == DMA_MEM_TO_DEV)
+ bus_width = config->dst_addr_width;
+ else
+ bus_width = config->src_addr_width;
+
+ for (i = next_sg; i < dma_desc->nr_descs; i++)
+ bytes += dma_desc->desc[i]->length * bus_width;
+
+ return bytes;
+}
+
+static enum dma_status ls1x_dma_tx_status(struct dma_chan *chan,
+ dma_cookie_t cookie,
+ struct dma_tx_state *txstate)
+{
+ struct ls1x_dma_chan *dma_chan = to_ls1x_dma_chan(chan);
+ struct ls1x_dma_desc *dma_desc = dma_chan->dma_desc;
+ struct virt_dma_desc *vdesc;
+ enum dma_status status;
+ unsigned int residue = 0;
+ unsigned long flags;
+
+ status = dma_cookie_status(chan, cookie, txstate);
+ if ((status == DMA_COMPLETE) || !txstate)
+ return status;
+
+ spin_lock_irqsave(&dma_chan->vchan.lock, flags);
+
+ vdesc = vchan_find_desc(&dma_chan->vchan, cookie);
+ if (vdesc)
+ /* not yet processed */
+ residue = ls1x_dma_desc_residue(to_ls1x_dma_desc(vdesc), 0);
+ else if (cookie == dma_chan->dma_desc->vdesc.tx.cookie)
+ /* in progress */
+ residue = ls1x_dma_desc_residue(dma_desc, dma_desc->nr_done);
+ else
+ residue = 0;
+
+ spin_unlock_irqrestore(&dma_chan->vchan.lock, flags);
+
+ dma_set_residue(txstate, residue);
+
+ return status;
+}
+
+static void ls1x_trigger_dma(struct ls1x_dma_chan *dma_chan)
+{
+ struct dma_chan *chan = &dma_chan->vchan.chan;
+ struct ls1x_dma_desc *dma_desc;
+ struct virt_dma_desc *vdesc;
+ unsigned int val;
+
+ vdesc = vchan_next_desc(&dma_chan->vchan);
+ if (!vdesc) {
+ dev_warn(&chan->dev->device, "No pending descriptor\n");
+ return;
+ }
+ dma_chan->dma_desc = dma_desc = to_ls1x_dma_desc(vdesc);
+
+ dev_dbg(&chan->dev->device, "cookie=%d, %u descs, starting desc=%p\n",
+ chan->cookie, dma_desc->nr_descs, &dma_desc->desc[0]);
+
+ val = dma_desc->desc[0]->phys & DMA_ADDR_MASK;
+ val |= dma_chan->id;
+ val |= DMA_START;
+ chan_writel(dma_chan, DMA_CTRL, val);
+}
+
+static void ls1x_dma_issue_pending(struct dma_chan *chan)
+{
+ struct ls1x_dma_chan *dma_chan = to_ls1x_dma_chan(chan);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dma_chan->vchan.lock, flags);
+
+ if (vchan_issue_pending(&dma_chan->vchan) && !dma_chan->dma_desc)
+ ls1x_trigger_dma(dma_chan);
+
+ spin_unlock_irqrestore(&dma_chan->vchan.lock, flags);
+}
+
+static irqreturn_t ls1x_dma_irq_handler(int irq, void *data)
+{
+ struct ls1x_dma_chan *dma_chan = data;
+ struct dma_chan *chan = &dma_chan->vchan.chan;
+
+ dev_dbg(&chan->dev->device, "DMA IRQ %d on channel %d\n", irq,
+ dma_chan->id);
+ if (!dma_chan->dma_desc) {
+ dev_warn(&chan->dev->device,
+ "DMA IRQ with no active descriptor on channel %d\n",
+ dma_chan->id);
+ return IRQ_NONE;
+ }
+
+ spin_lock(&dma_chan->vchan.lock);
+
+ if (dma_chan->dma_desc->type == DMA_CYCLIC) {
+ vchan_cyclic_callback(&dma_chan->dma_desc->vdesc);
+ } else {
+ list_del(&dma_chan->dma_desc->vdesc.node);
+ vchan_cookie_complete(&dma_chan->dma_desc->vdesc);
+ dma_chan->dma_desc = NULL;
+ }
+
+ spin_unlock(&dma_chan->vchan.lock);
+ return IRQ_HANDLED;
+}
+
+static int ls1x_dma_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct plat_ls1x_dma *pdata = dev_get_platdata(dev);
+ struct dma_device *dma_dev;
+ struct ls1x_dma *dma;
+ struct ls1x_dma_chan *dma_chan;
+ struct resource *res;
+ int i, ret;
+
+ /*initialize DMA device */
+ dma =
+ devm_kzalloc(dev,
+ sizeof(struct ls1x_dma) +
+ pdata->nr_channels * sizeof(struct ls1x_dma_chan),
+ GFP_KERNEL);
+ if (!dma)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "failed to get I/O memory\n");
+ return -EINVAL;
+ }
+
+ dma->reg_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(dma->reg_base))
+ return PTR_ERR(dma->reg_base);
+
+ dma_dev = &dma->dma_dev;
+
+ dma_cap_set(DMA_SLAVE, dma_dev->cap_mask);
+ dma_cap_set(DMA_PRIVATE, dma_dev->cap_mask);
+
+ dma_dev->dev = dev;
+ dma_dev->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
+ dma_dev->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
+ dma_dev->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
+ dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT;
+ dma_dev->device_alloc_chan_resources = ls1x_dma_alloc_chan_resources;
+ dma_dev->device_free_chan_resources = ls1x_dma_free_chan_resources;
+ dma_dev->device_prep_slave_sg = ls1x_dma_prep_slave_sg;
+ dma_dev->device_config = ls1x_dma_slave_config;
+ dma_dev->device_terminate_all = ls1x_dma_terminate_all;
+ dma_dev->device_tx_status = ls1x_dma_tx_status;
+ dma_dev->device_issue_pending = ls1x_dma_issue_pending;
+
+ INIT_LIST_HEAD(&dma_dev->channels);
+
+ /*initialize DMA channels */
+ for (i = 0; i < pdata->nr_channels; i++) {
+ dma_chan = &dma->dma_chan[i];
+ dma_chan->id = i;
+ dma_chan->reg_base = dma->reg_base;
+
+ dma_chan->irq = platform_get_irq(pdev, i);
+ if (dma_chan->irq < 0) {
+ dev_err(dev, "failed to get IRQ: %d\n", dma_chan->irq);
+ return -EINVAL;
+ }
+
+ ret =
+ devm_request_irq(dev, dma_chan->irq, ls1x_dma_irq_handler,
+ IRQF_SHARED, dev_name(dev), dma_chan);
+ if (ret) {
+ dev_err(dev, "failed to request IRQ %u!\n",
+ dma_chan->irq);
+ return -EINVAL;
+ }
+
+ dma_chan->vchan.desc_free = ls1x_dma_free_desc;
+ vchan_init(&dma_chan->vchan, dma_dev);
+ }
+ dma->nr_dma_chans = i;
+
+ dma->clk = devm_clk_get(dev, pdev->name);
+ if (IS_ERR(dma->clk)) {
+ dev_err(dev, "failed to get %s clock\n", pdev->name);
+ return PTR_ERR(dma->clk);
+ }
+ clk_prepare_enable(dma->clk);
+
+ ret = dma_async_device_register(dma_dev);
+ if (ret) {
+ dev_err(dev, "failed to register DMA device\n");
+ clk_disable_unprepare(dma->clk);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, dma);
+ dev_info(dev, "Loongson1 DMA driver registered\n");
+ for (i = 0; i < pdata->nr_channels; i++) {
+ dma_chan = &dma->dma_chan[i];
+ dev = &dma_chan->vchan.chan.dev->device;
+ dev_info(dev, "channel %d at 0x%p (irq %d)\n", dma_chan->id,
+ dma_chan->reg_base, dma_chan->irq);
+ }
+
+ return 0;
+}
+
+static int ls1x_dma_remove(struct platform_device *pdev)
+{
+ struct ls1x_dma *dma = platform_get_drvdata(pdev);
+
+ dma_async_device_unregister(&dma->dma_dev);
+ clk_disable_unprepare(dma->clk);
+ return 0;
+}
+
+static struct platform_driver ls1x_dma_driver = {
+ .probe = ls1x_dma_probe,
+ .remove = ls1x_dma_remove,
+ .driver = {
+ .name = "ls1x-dma",
+ },
+};
+
+module_platform_driver(ls1x_dma_driver);
+
+MODULE_AUTHOR("Kelvin Cheung <keguang.zhang@gmail.com>");
+MODULE_DESCRIPTION("Loongson1 DMA driver");
+MODULE_LICENSE("GPL");
--
1.9.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 4/7] mtd: nand: add Loongson1 NAND driver
2016-04-06 12:09 [PATCH 0/7] MIPS: Loongson1B: add NAND, DMA and GPIO support Keguang Zhang
` (2 preceding siblings ...)
2016-04-06 12:09 ` [PATCH 3/7] dmaengine: Loongson1: add Loongson1 dmaengine driver Keguang Zhang
@ 2016-04-06 12:09 ` Keguang Zhang
2016-04-06 12:09 ` [PATCH 5/7] gpio: Loongson1: add Loongson1 GPIO driver Keguang Zhang
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Keguang Zhang @ 2016-04-06 12:09 UTC (permalink / raw)
To: linux-kernel, linux-mips, linux-clk, linux-pm, dmaengine,
linux-gpio, linux-mtd
Cc: Ralf Baechle, Michael Turquette, Stephen Boyd, Rafael J. Wysocki,
Viresh Kumar, Vinod Koul, Dan Williams, Linus Walleij,
Alexandre Courbot, Boris Brezillon, Richard Weinberger,
David Woodhouse, Brian Norris, Kelvin Cheung
From: Kelvin Cheung <keguang.zhang@gmail.com>
This patch adds NAND driver for Loongson1B.
Signed-off-by: Kelvin Cheung <keguang.zhang@gmail.com>
---
drivers/mtd/nand/Kconfig | 8 +
drivers/mtd/nand/Makefile | 1 +
drivers/mtd/nand/loongson1_nand.c | 522 ++++++++++++++++++++++++++++++++++++++
3 files changed, 531 insertions(+)
create mode 100644 drivers/mtd/nand/loongson1_nand.c
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index f05e0e9..d90f545 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -563,4 +563,12 @@ config MTD_NAND_QCOM
Enables support for NAND flash chips on SoCs containing the EBI2 NAND
controller. This controller is found on IPQ806x SoC.
+config MTD_NAND_LOONGSON1
+ tristate "Support for Loongson1 SoC NAND controller"
+ depends on MACH_LOONGSON32
+ select DMADEVICES
+ select DMA_LOONGSON1
+ help
+ Enables support for NAND Flash on Loongson1 SoC based boards.
+
endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index f553353..0310c0b 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -57,5 +57,6 @@ obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o
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_LOONGSON1) += loongson1_nand.o
nand-objs := nand_base.o nand_bbt.o nand_timings.o
diff --git a/drivers/mtd/nand/loongson1_nand.c b/drivers/mtd/nand/loongson1_nand.c
new file mode 100644
index 0000000..68a0514
--- /dev/null
+++ b/drivers/mtd/nand/loongson1_nand.c
@@ -0,0 +1,522 @@
+/*
+ * NAND Flash Driver for Loongson 1 SoC
+ *
+ * Copyright (C) 2015-2016 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/sizes.h>
+
+#include <nand.h>
+
+/* Loongson 1 NAND Register Definitions */
+#define NAND_CMD 0x0
+#define NAND_ADDRL 0x4
+#define NAND_ADDRH 0x8
+#define NAND_TIMING 0xc
+#define NAND_IDL 0x10
+#define NAND_IDH 0x14
+#define NAND_STATUS 0x14
+#define NAND_PARAM 0x18
+#define NAND_OP_NUM 0x1c
+#define NAND_CS_RDY 0x20
+
+#define NAND_DMA_ADDR 0x40
+
+/* NAND Command Register Bits */
+#define OP_DONE BIT(10)
+#define OP_SPARE BIT(9)
+#define OP_MAIN BIT(8)
+#define CMD_STATUS BIT(7)
+#define CMD_RESET BIT(6)
+#define CMD_READID BIT(5)
+#define BLOCKS_ERASE BIT(4)
+#define CMD_ERASE BIT(3)
+#define CMD_WRITE BIT(2)
+#define CMD_READ BIT(1)
+#define CMD_VALID BIT(0)
+
+#define LS1X_NAND_TIMEOUT 20
+
+/* macros for registers read/write */
+#define nand_readl(nand, off) \
+ __raw_readl((nand)->reg_base + (off))
+
+#define nand_writel(nand, off, val) \
+ __raw_writel((val), (nand)->reg_base + (off))
+
+#define set_cmd(nand, ctrl) \
+ nand_writel(nand, NAND_CMD, ctrl)
+
+#define start_nand(nand) \
+ nand_writel(nand, NAND_CMD, nand_readl(nand, NAND_CMD) | CMD_VALID)
+
+struct ls1x_nand {
+ struct platform_device *pdev;
+ struct nand_chip chip;
+
+ struct clk *clk;
+ void __iomem *reg_base;
+
+ int cmd_val;
+
+ char datareg[8];
+ char *data_ptr;
+
+ /* DMA stuff */
+ unsigned char *dma_buf;
+ unsigned int buf_off;
+ unsigned int buf_len;
+
+ /* DMA Engine stuff */
+ unsigned int dma_chan_id;
+ struct dma_chan *dma_chan;
+ dma_cookie_t dma_cookie;
+ struct completion dma_complete;
+ void __iomem *dma_desc;
+};
+
+static void dma_callback(void *data)
+{
+ struct ls1x_nand *nand = (struct ls1x_nand *)data;
+ struct mtd_info *mtd = nand_to_mtd(&nand->chip);
+ struct dma_tx_state state;
+ enum dma_status status;
+
+ status = dmaengine_tx_status(nand->dma_chan, nand->dma_cookie, &state);
+ if (likely(status == DMA_COMPLETE))
+ dev_dbg(mtd->dev.parent, "DMA complete with cookie=%d\n",
+ nand->dma_cookie);
+ else
+ dev_err(mtd->dev.parent, "DMA error with cookie=%d\n",
+ nand->dma_cookie);
+
+ complete(&nand->dma_complete);
+}
+
+static int setup_dma(struct ls1x_nand *nand)
+{
+ struct mtd_info *mtd = nand_to_mtd(&nand->chip);
+ struct dma_slave_config cfg;
+ dma_cap_mask_t mask;
+ int ret;
+
+ /* allocate DMA buffer */
+ nand->dma_buf = devm_kzalloc(mtd->dev.parent,
+ mtd->writesize + mtd->oobsize, GFP_KERNEL);
+ if (!nand->dma_buf) {
+ dev_err(mtd->dev.parent, "failed to allocate DMA buffer\n");
+ return -ENOMEM;
+ }
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ nand->dma_chan = dma_request_channel(mask, ls1x_dma_filter_fn,
+ &nand->dma_chan_id);
+ if (!nand->dma_chan) {
+ dev_err(mtd->dev.parent, "failed to request DMA channel\n");
+ return -EBUSY;
+ }
+ dev_info(mtd->dev.parent, "got %s for %s access\n",
+ dma_chan_name(nand->dma_chan), dev_name(mtd->dev.parent));
+
+ cfg.src_addr = CPHYSADDR(nand->reg_base + NAND_DMA_ADDR);
+ cfg.dst_addr = CPHYSADDR(nand->reg_base + NAND_DMA_ADDR);
+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ ret = dmaengine_slave_config(nand->dma_chan, &cfg);
+ if (ret) {
+ dev_err(mtd->dev.parent, "failed to config DMA channel\n");
+ dma_release_channel(nand->dma_chan);
+ return ret;
+ }
+
+ init_completion(&nand->dma_complete);
+
+ return 0;
+}
+
+static int start_dma(struct ls1x_nand *nand, unsigned int len, bool is_write)
+{
+ struct mtd_info *mtd = nand_to_mtd(&nand->chip);
+ struct dma_chan *chan = nand->dma_chan;
+ struct dma_async_tx_descriptor *desc;
+ enum dma_data_direction data_dir =
+ is_write ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+ enum dma_transfer_direction xfer_dir =
+ is_write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
+ dma_addr_t dma_addr;
+ int ret;
+
+ dma_addr =
+ dma_map_single(chan->device->dev, nand->dma_buf, len, data_dir);
+ if (dma_mapping_error(chan->device->dev, dma_addr)) {
+ dev_err(mtd->dev.parent, "failed to map DMA buffer\n");
+ return -ENXIO;
+ }
+
+ desc = dmaengine_prep_slave_single(chan, dma_addr, len, xfer_dir,
+ DMA_PREP_INTERRUPT);
+ if (!desc) {
+ dev_err(mtd->dev.parent,
+ "failed to prepare DMA descriptor\n");
+ ret = PTR_ERR(desc);
+ goto err;
+ }
+ desc->callback = dma_callback;
+ desc->callback_param = nand;
+
+ nand->dma_cookie = dmaengine_submit(desc);
+ ret = dma_submit_error(nand->dma_cookie);
+ if (ret) {
+ dev_err(mtd->dev.parent,
+ "failed to submit DMA descriptor\n");
+ goto err;
+ }
+
+ dev_dbg(mtd->dev.parent, "issue DMA with cookie=%d\n",
+ nand->dma_cookie);
+ dma_async_issue_pending(chan);
+
+ ret = wait_for_completion_timeout(&nand->dma_complete,
+ msecs_to_jiffies(LS1X_NAND_TIMEOUT));
+ if (ret <= 0) {
+ dev_err(mtd->dev.parent, "DMA timeout\n");
+ dmaengine_terminate_all(chan);
+ ret = -EIO;
+ }
+ ret = 0;
+err:
+ dma_unmap_single(chan->device->dev, dma_addr, len, data_dir);
+
+ return ret;
+}
+
+static void ls1x_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+}
+
+static int ls1x_nand_dev_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct ls1x_nand *nand = nand_get_controller_data(chip);
+
+ if (nand_readl(nand, NAND_CMD) & OP_DONE)
+ return 1;
+
+ return 0;
+}
+
+static uint8_t ls1x_nand_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct ls1x_nand *nand = nand_get_controller_data(chip);
+
+ return *(nand->data_ptr++);
+}
+
+static void ls1x_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct ls1x_nand *nand = nand_get_controller_data(chip);
+
+ int real_len = min_t(size_t, len, nand->buf_len - nand->buf_off);
+
+ memcpy(buf, nand->dma_buf + nand->buf_off, real_len);
+ nand->buf_off += real_len;
+}
+
+static void ls1x_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf,
+ int len)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct ls1x_nand *nand = nand_get_controller_data(chip);
+
+ int real_len = min_t(size_t, len, nand->buf_len - nand->buf_off);
+
+ memcpy(nand->dma_buf + nand->buf_off, buf, real_len);
+ nand->buf_off += real_len;
+}
+
+static inline void set_addr_len(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct ls1x_nand *nand = nand_get_controller_data(chip);
+ int page_shift, addr_low, addr_high;
+
+ if (command == NAND_CMD_ERASE1)
+ page_shift = chip->page_shift;
+ else
+ page_shift = chip->page_shift + 1;
+
+ addr_low = page_addr << page_shift;
+
+ if (column != -1) {
+ if (command == NAND_CMD_READOOB)
+ column += mtd->writesize;
+ addr_low += column;
+ nand->buf_off = 0;
+ }
+
+ addr_high =
+ page_addr >> (sizeof(page_addr) * BITS_PER_BYTE - page_shift);
+
+ if (command == NAND_CMD_ERASE1)
+ nand->buf_len = 1;
+ else
+ nand->buf_len = mtd->writesize + mtd->oobsize - column;
+
+ nand_writel(nand, NAND_ADDRL, addr_low);
+ nand_writel(nand, NAND_ADDRH, addr_high);
+ nand_writel(nand, NAND_OP_NUM, nand->buf_len);
+}
+
+static void ls1x_nand_cmdfunc(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ struct ls1x_nand *nand = nand_get_controller_data(chip);
+
+ dev_dbg(mtd->dev.parent, "cmd = 0x%02x, col = 0x%08x, page = 0x%08x\n",
+ command, column, page_addr);
+
+ if (command == NAND_CMD_RNDOUT) {
+ nand->buf_off = column;
+ return;
+ }
+
+ /*set address, buffer length and buffer offset */
+ if (column != -1 || page_addr != -1)
+ set_addr_len(mtd, command, column, page_addr);
+
+ /*prepare NAND command */
+ switch (command) {
+ case NAND_CMD_RESET:
+ nand->cmd_val = CMD_RESET;
+ break;
+ case NAND_CMD_STATUS:
+ nand->cmd_val = CMD_STATUS;
+ break;
+ case NAND_CMD_READID:
+ nand->cmd_val = CMD_READID;
+ break;
+ case NAND_CMD_READ0:
+ nand->cmd_val = OP_SPARE | OP_MAIN | CMD_READ;
+ break;
+ case NAND_CMD_READOOB:
+ nand->cmd_val = OP_SPARE | CMD_READ;
+ break;
+ case NAND_CMD_ERASE1:
+ nand->cmd_val = CMD_ERASE;
+ break;
+ case NAND_CMD_PAGEPROG:
+ break;
+ case NAND_CMD_SEQIN:
+ if (column < mtd->writesize)
+ nand->cmd_val = OP_SPARE | OP_MAIN | CMD_WRITE;
+ else
+ nand->cmd_val = OP_SPARE | CMD_WRITE;
+ default:
+ return;
+ }
+
+ /*set NAND command */
+ set_cmd(nand, nand->cmd_val);
+ /*trigger NAND operation */
+ start_nand(nand);
+ /*trigger DMA for R/W operation */
+ if (command == NAND_CMD_READ0 || command == NAND_CMD_READOOB)
+ start_dma(nand, nand->buf_len, false);
+ else if (command == NAND_CMD_PAGEPROG)
+ start_dma(nand, nand->buf_len, true);
+ nand_wait_ready(mtd);
+
+ if (command == NAND_CMD_STATUS) {
+ nand->datareg[0] = (char)(nand_readl(nand, NAND_STATUS) >> 8);
+ /*work around hardware bug for invalid STATUS */
+ nand->datareg[0] |= 0xc0;
+ nand->data_ptr = nand->datareg;
+ } else if (command == NAND_CMD_READID) {
+ nand->datareg[0] = (char)(nand_readl(nand, NAND_IDH));
+ nand->datareg[1] = (char)(nand_readl(nand, NAND_IDL) >> 24);
+ nand->datareg[2] = (char)(nand_readl(nand, NAND_IDL) >> 16);
+ nand->datareg[3] = (char)(nand_readl(nand, NAND_IDL) >> 8);
+ nand->datareg[4] = (char)(nand_readl(nand, NAND_IDL));
+ nand->data_ptr = nand->datareg;
+ }
+
+ nand->cmd_val = 0;
+}
+
+static void nand_hw_init(struct ls1x_nand *nand, int hold_cycle,
+ int wait_cycle)
+{
+ struct nand_chip *chip = &nand->chip;
+ struct mtd_info *mtd = nand_to_mtd(chip);
+ int chipsize = (int)(chip->chipsize >> 20);
+ int cell_size = 0x0;
+
+ switch (chipsize) {
+ case SZ_128: /*128M */
+ cell_size = 0x0;
+ break;
+ case SZ_256: /*256M */
+ cell_size = 0x1;
+ break;
+ case SZ_512: /*512M */
+ cell_size = 0x2;
+ break;
+ case SZ_1K: /*1G */
+ cell_size = 0x3;
+ break;
+ case SZ_2K: /*2G */
+ cell_size = 0x4;
+ break;
+ case SZ_4K: /*4G */
+ cell_size = 0x5;
+ break;
+ case SZ_8K: /*8G */
+ cell_size = 0x6;
+ break;
+ case SZ_16K: /*16G */
+ cell_size = 0x7;
+ break;
+ default:
+ dev_warn(mtd->dev.parent, "unsupported chip size: %d MB\n",
+ chipsize);
+ }
+
+ nand_writel(nand, NAND_TIMING, (hold_cycle << 8) | wait_cycle);
+ nand_writel(nand, NAND_PARAM,
+ (nand_readl(nand, NAND_PARAM) & 0xfffff0ff) | (cell_size <<
+ 8));
+}
+
+static int ls1x_nand_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct plat_ls1x_nand *pdata = dev_get_platdata(dev);
+ struct ls1x_nand *nand;
+ struct mtd_info *mtd;
+ struct nand_chip *chip;
+ struct resource *res;
+ int ret = 0;
+
+ if (!pdata) {
+ dev_err(dev, "platform data missing\n");
+ return -EINVAL;
+ }
+
+ nand = devm_kzalloc(dev, sizeof(struct ls1x_nand), GFP_KERNEL);
+ if (!nand)
+ return -ENOMEM;
+ nand->pdev = pdev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "failed to get I/O memory\n");
+ return -ENXIO;
+ }
+
+ nand->reg_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(nand->reg_base))
+ return PTR_ERR(nand->reg_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!res) {
+ dev_err(dev, "failed to get DMA information\n");
+ return -ENXIO;
+ }
+ nand->dma_chan_id = res->start;
+
+ nand->clk = devm_clk_get(dev, pdev->name);
+ if (IS_ERR(nand->clk)) {
+ dev_err(dev, "failed to get %s clock\n", pdev->name);
+ return PTR_ERR(nand->clk);
+ }
+ clk_prepare_enable(nand->clk);
+
+ chip = &nand->chip;
+ chip->read_byte = ls1x_nand_read_byte;
+ chip->read_buf = ls1x_nand_read_buf;
+ chip->write_buf = ls1x_nand_write_buf;
+ chip->select_chip = ls1x_nand_select_chip;
+ chip->dev_ready = ls1x_nand_dev_ready;
+ chip->cmdfunc = ls1x_nand_cmdfunc;
+ chip->options = NAND_NO_SUBPAGE_WRITE;
+ chip->ecc.mode = NAND_ECC_SOFT;
+ nand_set_controller_data(chip, nand);
+
+ mtd = nand_to_mtd(chip);
+ mtd->name = "ls1x-nand";
+ mtd->owner = THIS_MODULE;
+ mtd->dev.parent = dev;
+
+ ret = nand_scan_ident(mtd, 1, NULL);
+ if (ret)
+ goto err;
+
+ nand_hw_init(nand, pdata->hold_cycle, pdata->wait_cycle);
+
+ ret = setup_dma(nand);
+ if (ret)
+ goto err;
+
+ ret = nand_scan_tail(mtd);
+ if (ret)
+ goto err;
+
+ ret = mtd_device_register(mtd, pdata->parts, pdata->nr_parts);
+ if (ret) {
+ dev_err(dev, "failed to register MTD device: %d\n", ret);
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, nand);
+ dev_info(dev, "Loongson1 NAND driver registered\n");
+
+ return 0;
+err:
+ clk_disable_unprepare(nand->clk);
+
+ return ret;
+}
+
+static int ls1x_nand_remove(struct platform_device *pdev)
+{
+ struct ls1x_nand *nand = platform_get_drvdata(pdev);
+
+ if (nand->dma_chan)
+ dma_release_channel(nand->dma_chan);
+ nand_release(nand_to_mtd(&nand->chip));
+ clk_disable_unprepare(nand->clk);
+
+ return 0;
+}
+
+static struct platform_driver ls1x_nand_driver = {
+ .probe = ls1x_nand_probe,
+ .remove = ls1x_nand_remove,
+ .driver = {
+ .name = "ls1x-nand",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(ls1x_nand_driver);
+
+MODULE_AUTHOR("Kelvin Cheung <keguang.zhang@gmail.com>");
+MODULE_DESCRIPTION("Loongson1 NAND Flash driver");
+MODULE_LICENSE("GPL");
--
1.9.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 5/7] gpio: Loongson1: add Loongson1 GPIO driver
2016-04-06 12:09 [PATCH 0/7] MIPS: Loongson1B: add NAND, DMA and GPIO support Keguang Zhang
` (3 preceding siblings ...)
2016-04-06 12:09 ` [PATCH 4/7] mtd: nand: add Loongson1 NAND driver Keguang Zhang
@ 2016-04-06 12:09 ` Keguang Zhang
2016-04-06 12:09 ` [PATCH 6/7] MIPS: Loongson1B: Some updates/fixes for LS1B Keguang Zhang
2016-04-06 12:09 ` [PATCH 7/7] MAINTAINERS: add Loongson1 architecture entry Keguang Zhang
6 siblings, 0 replies; 8+ messages in thread
From: Keguang Zhang @ 2016-04-06 12:09 UTC (permalink / raw)
To: linux-kernel, linux-mips, linux-clk, linux-pm, dmaengine,
linux-gpio, linux-mtd
Cc: Ralf Baechle, Michael Turquette, Stephen Boyd, Rafael J. Wysocki,
Viresh Kumar, Vinod Koul, Dan Williams, Linus Walleij,
Alexandre Courbot, Boris Brezillon, Richard Weinberger,
David Woodhouse, Brian Norris, Kelvin Cheung
From: Kelvin Cheung <keguang.zhang@gmail.com>
This patch adds GPIO driver for Loongson1B.
Signed-off-by: Kelvin Cheung <keguang.zhang@gmail.com>
---
drivers/gpio/Kconfig | 7 +++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-loongson1.c | 102 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 110 insertions(+)
create mode 100644 drivers/gpio/gpio-loongson1.c
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 5f3429f..373b8a7 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -510,6 +510,13 @@ config GPIO_ZX
help
Say yes here to support the GPIO device on ZTE ZX SoCs.
+config GPIO_LOONGSON1
+ tristate "Loongson1 GPIO support"
+ depends on MACH_LOONGSON32
+ select GPIO_GENERIC
+ help
+ Say Y or M here to support GPIO on Loongson1 SoCs.
+
endmenu
menu "Port-mapped I/O GPIO drivers"
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 1e0b74f..40ab913 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -127,3 +127,4 @@ obj-$(CONFIG_GPIO_XTENSA) += gpio-xtensa.o
obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o
obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o
obj-$(CONFIG_GPIO_ZX) += gpio-zx.o
+obj-$(CONFIG_GPIO_LOONGSON1) += gpio-loongson1.o
diff --git a/drivers/gpio/gpio-loongson1.c b/drivers/gpio/gpio-loongson1.c
new file mode 100644
index 0000000..10c09bd
--- /dev/null
+++ b/drivers/gpio/gpio-loongson1.c
@@ -0,0 +1,102 @@
+/*
+ * GPIO Driver for Loongson 1 SoC
+ *
+ * Copyright (C) 2015-2016 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/gpio/driver.h>
+#include <linux/platform_device.h>
+
+/* Loongson 1 GPIO Register Definitions */
+#define GPIO_CFG 0x0
+#define GPIO_DIR 0x10
+#define GPIO_DATA 0x20
+#define GPIO_OUTPUT 0x30
+
+static void __iomem *gpio_reg_base;
+
+static int ls1x_gpio_request(struct gpio_chip *gc, unsigned int offset)
+{
+ unsigned long pinmask = gc->pin2mask(gc, offset);
+ unsigned long flags;
+
+ spin_lock_irqsave(&gc->bgpio_lock, flags);
+ __raw_writel(__raw_readl(gpio_reg_base + GPIO_CFG) | pinmask,
+ gpio_reg_base + GPIO_CFG);
+ spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+
+ return 0;
+}
+
+static void ls1x_gpio_free(struct gpio_chip *gc, unsigned int offset)
+{
+ unsigned long pinmask = gc->pin2mask(gc, offset);
+ unsigned long flags;
+
+ spin_lock_irqsave(&gc->bgpio_lock, flags);
+ __raw_writel(__raw_readl(gpio_reg_base + GPIO_CFG) & ~pinmask,
+ gpio_reg_base + GPIO_CFG);
+ spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+}
+
+static int ls1x_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct gpio_chip *gc;
+ struct resource *res;
+ int ret;
+
+ gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
+ if (!gc)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "failed to get I/O memory\n");
+ return -EINVAL;
+ }
+
+ gpio_reg_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(gpio_reg_base))
+ return PTR_ERR(gpio_reg_base);
+
+ ret = bgpio_init(gc, dev, 4, gpio_reg_base + GPIO_DATA,
+ gpio_reg_base + GPIO_OUTPUT, NULL,
+ NULL, gpio_reg_base + GPIO_DIR, 0);
+ if (ret)
+ goto err;
+
+ gc->owner = THIS_MODULE;
+ gc->request = ls1x_gpio_request;
+ gc->free = ls1x_gpio_free;
+ gc->base = pdev->id * 32;
+
+ ret = devm_gpiochip_add_data(dev, gc, NULL);
+ if (ret)
+ goto err;
+
+ platform_set_drvdata(pdev, gc);
+ dev_info(dev, "Loongson1 GPIO driver registered\n");
+
+ return 0;
+err:
+ dev_err(dev, "failed to register GPIO device\n");
+ return ret;
+}
+
+static struct platform_driver ls1x_gpio_driver = {
+ .probe = ls1x_gpio_probe,
+ .driver = {
+ .name = "ls1x-gpio",
+ },
+};
+
+module_platform_driver(ls1x_gpio_driver);
+
+MODULE_AUTHOR("Kelvin Cheung <keguang.zhang@gmail.com>");
+MODULE_DESCRIPTION("Loongson1 GPIO driver");
+MODULE_LICENSE("GPL");
--
1.9.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 6/7] MIPS: Loongson1B: Some updates/fixes for LS1B
2016-04-06 12:09 [PATCH 0/7] MIPS: Loongson1B: add NAND, DMA and GPIO support Keguang Zhang
` (4 preceding siblings ...)
2016-04-06 12:09 ` [PATCH 5/7] gpio: Loongson1: add Loongson1 GPIO driver Keguang Zhang
@ 2016-04-06 12:09 ` Keguang Zhang
2016-04-06 12:09 ` [PATCH 7/7] MAINTAINERS: add Loongson1 architecture entry Keguang Zhang
6 siblings, 0 replies; 8+ messages in thread
From: Keguang Zhang @ 2016-04-06 12:09 UTC (permalink / raw)
To: linux-kernel, linux-mips, linux-clk, linux-pm, dmaengine,
linux-gpio, linux-mtd
Cc: Ralf Baechle, Michael Turquette, Stephen Boyd, Rafael J. Wysocki,
Viresh Kumar, Vinod Koul, Dan Williams, Linus Walleij,
Alexandre Courbot, Boris Brezillon, Richard Weinberger,
David Woodhouse, Brian Norris, Kelvin Cheung
From: Kelvin Cheung <keguang.zhang@gmail.com>
- Add DMA device
- Add NAND device
- Add GPIO device
- Add LED device
- Update the defconfig and rename it to loongson1b_defconfig
- Fix ioremap size
- Other minor fixes
Signed-off-by: Kelvin Cheung <keguang.zhang@gmail.com>
---
arch/mips/Kconfig | 2 +
arch/mips/configs/loongson1b_defconfig | 125 ++++++++++++++++++++++
arch/mips/configs/ls1b_defconfig | 110 -------------------
arch/mips/include/asm/mach-loongson32/cpufreq.h | 1 -
arch/mips/include/asm/mach-loongson32/dma.h | 25 +++++
arch/mips/include/asm/mach-loongson32/irq.h | 1 -
arch/mips/include/asm/mach-loongson32/loongson1.h | 4 +-
arch/mips/include/asm/mach-loongson32/nand.h | 30 ++++++
arch/mips/include/asm/mach-loongson32/platform.h | 14 ++-
arch/mips/include/asm/mach-loongson32/regs-clk.h | 24 ++---
arch/mips/include/asm/mach-loongson32/regs-mux.h | 84 +++++++--------
arch/mips/include/asm/mach-loongson32/regs-pwm.h | 12 +--
arch/mips/loongson32/common/platform.c | 105 +++++++++++++++++-
arch/mips/loongson32/common/reset.c | 13 +--
arch/mips/loongson32/common/time.c | 27 ++---
arch/mips/loongson32/ls1b/board.c | 67 +++++++++++-
16 files changed, 440 insertions(+), 204 deletions(-)
create mode 100644 arch/mips/configs/loongson1b_defconfig
delete mode 100644 arch/mips/configs/ls1b_defconfig
create mode 100644 arch/mips/include/asm/mach-loongson32/dma.h
create mode 100644 arch/mips/include/asm/mach-loongson32/nand.h
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 35d92a1..53b16e8 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -1387,6 +1387,8 @@ config CPU_LOONGSON1B
bool "Loongson 1B"
depends on SYS_HAS_CPU_LOONGSON1B
select CPU_LOONGSON1
+ select ARCH_WANT_OPTIONAL_GPIOLIB
+ select LEDS_GPIO_REGISTER
help
The Loongson 1B is a 32-bit SoC, which implements the MIPS32
release 2 instruction set.
diff --git a/arch/mips/configs/loongson1b_defconfig b/arch/mips/configs/loongson1b_defconfig
new file mode 100644
index 0000000..c442f27
--- /dev/null
+++ b/arch/mips/configs/loongson1b_defconfig
@@ -0,0 +1,125 @@
+CONFIG_MACH_LOONGSON32=y
+CONFIG_PREEMPT=y
+# CONFIG_SECCOMP is not set
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_KERNEL_XZ=y
+CONFIG_SYSVIPC=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=16
+CONFIG_NAMESPACES=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_EXPERT=y
+CONFIG_PERF_EVENTS=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+# CONFIG_LBDAF is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+# CONFIG_SUSPEND is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_SYN_COOKIES=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_DIAG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_WIRELESS is not set
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+# CONFIG_STANDALONE is not set
+CONFIG_MTD=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_LOONGSON1=y
+CONFIG_MTD_UBI=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_SCSI=m
+# CONFIG_SCSI_PROC_FS is not set
+CONFIG_BLK_DEV_SD=m
+# CONFIG_SCSI_LOWLEVEL is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NET_VENDOR_BROADCOM is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SMSC is not set
+CONFIG_STMMAC_ETH=y
+# CONFIG_NET_VENDOR_WIZNET is not set
+# CONFIG_WLAN is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_SERIO is not set
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_LEGACY_PTY_COUNT=8
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+# CONFIG_HW_RANDOM is not set
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_LOONGSON1=y
+# CONFIG_HWMON is not set
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_HID_GENERIC=m
+CONFIG_USB_HID=m
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_EHCI_HCD=y
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+CONFIG_USB_EHCI_HCD_PLATFORM=y
+CONFIG_USB_STORAGE=m
+CONFIG_USB_SERIAL=m
+CONFIG_USB_SERIAL_PL2303=m
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_LOONGSON1=y
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+# CONFIG_DNOTIFY is not set
+CONFIG_VFAT_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_UBIFS_FS=y
+CONFIG_UBIFS_FS_ADVANCED_COMPR=y
+CONFIG_UBIFS_ATIME_SUPPORT=y
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
+CONFIG_NLS_CODEPAGE_437=m
+CONFIG_NLS_ISO8859_1=m
+CONFIG_DYNAMIC_DEBUG=y
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_DEBUG_FS=y
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_FTRACE is not set
+# CONFIG_EARLY_PRINTK is not set
+# CONFIG_CRYPTO_ECHAINIV is not set
+# CONFIG_CRYPTO_HW is not set
diff --git a/arch/mips/configs/ls1b_defconfig b/arch/mips/configs/ls1b_defconfig
deleted file mode 100644
index 1b2cc1f..0000000
--- a/arch/mips/configs/ls1b_defconfig
+++ /dev/null
@@ -1,110 +0,0 @@
-CONFIG_MACH_LOONGSON32=y
-CONFIG_PREEMPT=y
-# CONFIG_SECCOMP is not set
-CONFIG_EXPERIMENTAL=y
-# CONFIG_LOCALVERSION_AUTO is not set
-CONFIG_SYSVIPC=y
-CONFIG_BSD_PROCESS_ACCT=y
-CONFIG_BSD_PROCESS_ACCT_V3=y
-CONFIG_HIGH_RES_TIMERS=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_LOG_BUF_SHIFT=16
-CONFIG_NAMESPACES=y
-CONFIG_BLK_DEV_INITRD=y
-CONFIG_RD_BZIP2=y
-CONFIG_RD_LZMA=y
-CONFIG_EXPERT=y
-CONFIG_PERF_EVENTS=y
-# CONFIG_COMPAT_BRK is not set
-CONFIG_MODULES=y
-CONFIG_MODULE_UNLOAD=y
-CONFIG_MODVERSIONS=y
-# CONFIG_LBDAF is not set
-# CONFIG_BLK_DEV_BSG is not set
-# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
-# CONFIG_SUSPEND is not set
-CONFIG_NET=y
-CONFIG_PACKET=y
-CONFIG_UNIX=y
-CONFIG_INET=y
-CONFIG_IP_PNP=y
-CONFIG_IP_PNP_DHCP=y
-CONFIG_SYN_COOKIES=y
-# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
-# CONFIG_INET_XFRM_MODE_TUNNEL is not set
-# CONFIG_INET_XFRM_MODE_BEET is not set
-# CONFIG_INET_DIAG is not set
-# CONFIG_IPV6 is not set
-# CONFIG_WIRELESS is not set
-CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
-CONFIG_DEVTMPFS=y
-CONFIG_DEVTMPFS_MOUNT=y
-# CONFIG_STANDALONE is not set
-CONFIG_BLK_DEV_LOOP=y
-CONFIG_SCSI=m
-# CONFIG_SCSI_PROC_FS is not set
-CONFIG_BLK_DEV_SD=m
-# CONFIG_SCSI_LOWLEVEL is not set
-CONFIG_NETDEVICES=y
-# CONFIG_NET_VENDOR_BROADCOM is not set
-# CONFIG_NET_VENDOR_CHELSIO is not set
-# CONFIG_NET_VENDOR_INTEL is not set
-# CONFIG_NET_VENDOR_MARVELL is not set
-# CONFIG_NET_VENDOR_MICREL is not set
-# CONFIG_NET_VENDOR_NATSEMI is not set
-# CONFIG_NET_VENDOR_SEEQ is not set
-# CONFIG_NET_VENDOR_SMSC is not set
-CONFIG_STMMAC_ETH=y
-CONFIG_STMMAC_DA=y
-# CONFIG_NET_VENDOR_WIZNET is not set
-# CONFIG_WLAN is not set
-CONFIG_INPUT_EVDEV=y
-# CONFIG_INPUT_KEYBOARD is not set
-# CONFIG_INPUT_MOUSE is not set
-# CONFIG_SERIO is not set
-CONFIG_VT_HW_CONSOLE_BINDING=y
-CONFIG_LEGACY_PTY_COUNT=8
-# CONFIG_DEVKMEM is not set
-CONFIG_SERIAL_8250=y
-CONFIG_SERIAL_8250_CONSOLE=y
-# CONFIG_HW_RANDOM is not set
-# CONFIG_HWMON is not set
-# CONFIG_VGA_CONSOLE is not set
-CONFIG_USB_HID=m
-CONFIG_HID_GENERIC=m
-CONFIG_USB=y
-CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
-CONFIG_USB_EHCI_HCD=y
-CONFIG_USB_EHCI_HCD_PLATFORM=y
-# CONFIG_USB_EHCI_TT_NEWSCHED is not set
-CONFIG_USB_STORAGE=m
-CONFIG_USB_SERIAL=m
-CONFIG_USB_SERIAL_PL2303=m
-CONFIG_RTC_CLASS=y
-CONFIG_RTC_DRV_LOONGSON1=y
-# CONFIG_IOMMU_SUPPORT is not set
-CONFIG_EXT2_FS=y
-CONFIG_EXT2_FS_XATTR=y
-CONFIG_EXT2_FS_POSIX_ACL=y
-CONFIG_EXT2_FS_SECURITY=y
-CONFIG_EXT3_FS=y
-CONFIG_EXT3_FS_POSIX_ACL=y
-CONFIG_EXT3_FS_SECURITY=y
-# CONFIG_DNOTIFY is not set
-CONFIG_VFAT_FS=y
-CONFIG_PROC_KCORE=y
-CONFIG_TMPFS=y
-CONFIG_TMPFS_POSIX_ACL=y
-# CONFIG_MISC_FILESYSTEMS is not set
-CONFIG_NFS_FS=y
-CONFIG_ROOT_NFS=y
-CONFIG_NLS_CODEPAGE_437=m
-CONFIG_NLS_ISO8859_1=m
-# CONFIG_ENABLE_WARN_DEPRECATED is not set
-# CONFIG_ENABLE_MUST_CHECK is not set
-CONFIG_MAGIC_SYSRQ=y
-# CONFIG_SCHED_DEBUG is not set
-# CONFIG_DEBUG_PREEMPT is not set
-# CONFIG_FTRACE is not set
-# CONFIG_EARLY_PRINTK is not set
diff --git a/arch/mips/include/asm/mach-loongson32/cpufreq.h b/arch/mips/include/asm/mach-loongson32/cpufreq.h
index 6843fa1..2f1ecb0 100644
--- a/arch/mips/include/asm/mach-loongson32/cpufreq.h
+++ b/arch/mips/include/asm/mach-loongson32/cpufreq.h
@@ -9,7 +9,6 @@
* option) any later version.
*/
-
#ifndef __ASM_MACH_LOONGSON32_CPUFREQ_H
#define __ASM_MACH_LOONGSON32_CPUFREQ_H
diff --git a/arch/mips/include/asm/mach-loongson32/dma.h b/arch/mips/include/asm/mach-loongson32/dma.h
new file mode 100644
index 0000000..ad1dec7
--- /dev/null
+++ b/arch/mips/include/asm/mach-loongson32/dma.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * Loongson 1 NAND platform support.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __ASM_MACH_LOONGSON32_DMA_H
+#define __ASM_MACH_LOONGSON32_DMA_H
+
+#define LS1X_DMA_CHANNEL0 0
+#define LS1X_DMA_CHANNEL1 1
+#define LS1X_DMA_CHANNEL2 2
+
+struct plat_ls1x_dma {
+ int nr_channels;
+};
+
+extern struct plat_ls1x_dma ls1b_dma_pdata;
+
+#endif /* __ASM_MACH_LOONGSON32_DMA_H */
diff --git a/arch/mips/include/asm/mach-loongson32/irq.h b/arch/mips/include/asm/mach-loongson32/irq.h
index 0d35b99..c1c7441 100644
--- a/arch/mips/include/asm/mach-loongson32/irq.h
+++ b/arch/mips/include/asm/mach-loongson32/irq.h
@@ -9,7 +9,6 @@
* option) any later version.
*/
-
#ifndef __ASM_MACH_LOONGSON32_IRQ_H
#define __ASM_MACH_LOONGSON32_IRQ_H
diff --git a/arch/mips/include/asm/mach-loongson32/loongson1.h b/arch/mips/include/asm/mach-loongson32/loongson1.h
index 12aa129..978f6df 100644
--- a/arch/mips/include/asm/mach-loongson32/loongson1.h
+++ b/arch/mips/include/asm/mach-loongson32/loongson1.h
@@ -9,7 +9,6 @@
* option) any later version.
*/
-
#ifndef __ASM_MACH_LOONGSON32_LOONGSON1_H
#define __ASM_MACH_LOONGSON32_LOONGSON1_H
@@ -18,6 +17,9 @@
/* Loongson 1 Register Bases */
#define LS1X_MUX_BASE 0x1fd00420
#define LS1X_INTC_BASE 0x1fd01040
+#define LS1X_GPIO0_BASE 0x1fd010c0
+#define LS1X_GPIO1_BASE 0x1fd010c4
+#define LS1X_DMAC_BASE 0x1fd01160
#define LS1X_EHCI_BASE 0x1fe00000
#define LS1X_OHCI_BASE 0x1fe08000
#define LS1X_GMAC0_BASE 0x1fe10000
diff --git a/arch/mips/include/asm/mach-loongson32/nand.h b/arch/mips/include/asm/mach-loongson32/nand.h
new file mode 100644
index 0000000..e274912
--- /dev/null
+++ b/arch/mips/include/asm/mach-loongson32/nand.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2015 Zhang, Keguang <keguang.zhang@gmail.com>
+ *
+ * Loongson 1 NAND platform support.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __ASM_MACH_LOONGSON32_NAND_H
+#define __ASM_MACH_LOONGSON32_NAND_H
+
+#include <linux/dmaengine.h>
+#include <linux/mtd/partitions.h>
+
+struct plat_ls1x_nand {
+ struct mtd_partition *parts;
+ unsigned int nr_parts;
+
+ int hold_cycle;
+ int wait_cycle;
+};
+
+extern struct plat_ls1x_nand ls1b_nand_pdata;
+
+bool ls1x_dma_filter_fn(struct dma_chan *chan, void *param);
+
+#endif /* __ASM_MACH_LOONGSON32_NAND_H */
diff --git a/arch/mips/include/asm/mach-loongson32/platform.h b/arch/mips/include/asm/mach-loongson32/platform.h
index c32f03f..672531a 100644
--- a/arch/mips/include/asm/mach-loongson32/platform.h
+++ b/arch/mips/include/asm/mach-loongson32/platform.h
@@ -7,20 +7,28 @@
* option) any later version.
*/
-
#ifndef __ASM_MACH_LOONGSON32_PLATFORM_H
#define __ASM_MACH_LOONGSON32_PLATFORM_H
#include <linux/platform_device.h>
+#include <dma.h>
+#include <nand.h>
+
extern struct platform_device ls1x_uart_pdev;
extern struct platform_device ls1x_cpufreq_pdev;
+extern struct platform_device ls1x_dma_pdev;
extern struct platform_device ls1x_eth0_pdev;
extern struct platform_device ls1x_eth1_pdev;
extern struct platform_device ls1x_ehci_pdev;
+extern struct platform_device ls1x_gpio0_pdev;
+extern struct platform_device ls1x_gpio1_pdev;
+extern struct platform_device ls1x_nand_pdev;
extern struct platform_device ls1x_rtc_pdev;
-extern void __init ls1x_clk_init(void);
-extern void __init ls1x_serial_setup(struct platform_device *pdev);
+void __init ls1x_clk_init(void);
+void __init ls1x_dma_set_platdata(struct plat_ls1x_dma *pdata);
+void __init ls1x_nand_set_platdata(struct plat_ls1x_nand *pdata);
+void __init ls1x_serial_set_uartclk(struct platform_device *pdev);
#endif /* __ASM_MACH_LOONGSON32_PLATFORM_H */
diff --git a/arch/mips/include/asm/mach-loongson32/regs-clk.h b/arch/mips/include/asm/mach-loongson32/regs-clk.h
index 1f5a715..4d56fc3 100644
--- a/arch/mips/include/asm/mach-loongson32/regs-clk.h
+++ b/arch/mips/include/asm/mach-loongson32/regs-clk.h
@@ -19,18 +19,18 @@
#define LS1X_CLK_PLL_DIV LS1X_CLK_REG(0x4)
/* Clock PLL Divisor Register Bits */
-#define DIV_DC_EN (0x1 << 31)
-#define DIV_DC_RST (0x1 << 30)
-#define DIV_CPU_EN (0x1 << 25)
-#define DIV_CPU_RST (0x1 << 24)
-#define DIV_DDR_EN (0x1 << 19)
-#define DIV_DDR_RST (0x1 << 18)
-#define RST_DC_EN (0x1 << 5)
-#define RST_DC (0x1 << 4)
-#define RST_DDR_EN (0x1 << 3)
-#define RST_DDR (0x1 << 2)
-#define RST_CPU_EN (0x1 << 1)
-#define RST_CPU 0x1
+#define DIV_DC_EN BIT(31)
+#define DIV_DC_RST BIT(30)
+#define DIV_CPU_EN BIT(25)
+#define DIV_CPU_RST BIT(24)
+#define DIV_DDR_EN BIT(19)
+#define DIV_DDR_RST BIT(18)
+#define RST_DC_EN BIT(5)
+#define RST_DC BIT(4)
+#define RST_DDR_EN BIT(3)
+#define RST_DDR BIT(2)
+#define RST_CPU_EN BIT(1)
+#define RST_CPU BIT(0)
#define DIV_DC_SHIFT 26
#define DIV_CPU_SHIFT 20
diff --git a/arch/mips/include/asm/mach-loongson32/regs-mux.h b/arch/mips/include/asm/mach-loongson32/regs-mux.h
index 8302d92..7c394f9 100644
--- a/arch/mips/include/asm/mach-loongson32/regs-mux.h
+++ b/arch/mips/include/asm/mach-loongson32/regs-mux.h
@@ -19,49 +19,49 @@
#define LS1X_MUX_CTRL1 LS1X_MUX_REG(0x4)
/* MUX CTRL0 Register Bits */
-#define UART0_USE_PWM23 (0x1 << 28)
-#define UART0_USE_PWM01 (0x1 << 27)
-#define UART1_USE_LCD0_5_6_11 (0x1 << 26)
-#define I2C2_USE_CAN1 (0x1 << 25)
-#define I2C1_USE_CAN0 (0x1 << 24)
-#define NAND3_USE_UART5 (0x1 << 23)
-#define NAND3_USE_UART4 (0x1 << 22)
-#define NAND3_USE_UART1_DAT (0x1 << 21)
-#define NAND3_USE_UART1_CTS (0x1 << 20)
-#define NAND3_USE_PWM23 (0x1 << 19)
-#define NAND3_USE_PWM01 (0x1 << 18)
-#define NAND2_USE_UART5 (0x1 << 17)
-#define NAND2_USE_UART4 (0x1 << 16)
-#define NAND2_USE_UART1_DAT (0x1 << 15)
-#define NAND2_USE_UART1_CTS (0x1 << 14)
-#define NAND2_USE_PWM23 (0x1 << 13)
-#define NAND2_USE_PWM01 (0x1 << 12)
-#define NAND1_USE_UART5 (0x1 << 11)
-#define NAND1_USE_UART4 (0x1 << 10)
-#define NAND1_USE_UART1_DAT (0x1 << 9)
-#define NAND1_USE_UART1_CTS (0x1 << 8)
-#define NAND1_USE_PWM23 (0x1 << 7)
-#define NAND1_USE_PWM01 (0x1 << 6)
-#define GMAC1_USE_UART1 (0x1 << 4)
-#define GMAC1_USE_UART0 (0x1 << 3)
-#define LCD_USE_UART0_DAT (0x1 << 2)
-#define LCD_USE_UART15 (0x1 << 1)
-#define LCD_USE_UART0 0x1
+#define UART0_USE_PWM23 BIT(28)
+#define UART0_USE_PWM01 BIT(27)
+#define UART1_USE_LCD0_5_6_11 BIT(26)
+#define I2C2_USE_CAN1 BIT(25)
+#define I2C1_USE_CAN0 BIT(24)
+#define NAND3_USE_UART5 BIT(23)
+#define NAND3_USE_UART4 BIT(22)
+#define NAND3_USE_UART1_DAT BIT(21)
+#define NAND3_USE_UART1_CTS BIT(20)
+#define NAND3_USE_PWM23 BIT(19)
+#define NAND3_USE_PWM01 BIT(18)
+#define NAND2_USE_UART5 BIT(17)
+#define NAND2_USE_UART4 BIT(16)
+#define NAND2_USE_UART1_DAT BIT(15)
+#define NAND2_USE_UART1_CTS BIT(14)
+#define NAND2_USE_PWM23 BIT(13)
+#define NAND2_USE_PWM01 BIT(12)
+#define NAND1_USE_UART5 BIT(11)
+#define NAND1_USE_UART4 BIT(10)
+#define NAND1_USE_UART1_DAT BIT(9)
+#define NAND1_USE_UART1_CTS BIT(8)
+#define NAND1_USE_PWM23 BIT(7)
+#define NAND1_USE_PWM01 BIT(6)
+#define GMAC1_USE_UART1 BIT(4)
+#define GMAC1_USE_UART0 BIT(3)
+#define LCD_USE_UART0_DAT BIT(2)
+#define LCD_USE_UART15 BIT(1)
+#define LCD_USE_UART0 BIT(0)
/* MUX CTRL1 Register Bits */
-#define USB_RESET (0x1 << 31)
-#define SPI1_CS_USE_PWM01 (0x1 << 24)
-#define SPI1_USE_CAN (0x1 << 23)
-#define DISABLE_DDR_CONFSPACE (0x1 << 20)
-#define DDR32TO16EN (0x1 << 16)
-#define GMAC1_SHUT (0x1 << 13)
-#define GMAC0_SHUT (0x1 << 12)
-#define USB_SHUT (0x1 << 11)
-#define UART1_3_USE_CAN1 (0x1 << 5)
-#define UART1_2_USE_CAN0 (0x1 << 4)
-#define GMAC1_USE_TXCLK (0x1 << 3)
-#define GMAC0_USE_TXCLK (0x1 << 2)
-#define GMAC1_USE_PWM23 (0x1 << 1)
-#define GMAC0_USE_PWM01 0x1
+#define USB_RESET BIT(31)
+#define SPI1_CS_USE_PWM01 BIT(24)
+#define SPI1_USE_CAN BIT(23)
+#define DISABLE_DDR_CONFSPACE BIT(20)
+#define DDR32TO16EN BIT(16)
+#define GMAC1_SHUT BIT(13)
+#define GMAC0_SHUT BIT(12)
+#define USB_SHUT BIT(11)
+#define UART1_3_USE_CAN1 BIT(5)
+#define UART1_2_USE_CAN0 BIT(4)
+#define GMAC1_USE_TXCLK BIT(3)
+#define GMAC0_USE_TXCLK BIT(2)
+#define GMAC1_USE_PWM23 BIT(1)
+#define GMAC0_USE_PWM01 BIT(0)
#endif /* __ASM_MACH_LOONGSON32_REGS_MUX_H */
diff --git a/arch/mips/include/asm/mach-loongson32/regs-pwm.h b/arch/mips/include/asm/mach-loongson32/regs-pwm.h
index 69f174e..4119600 100644
--- a/arch/mips/include/asm/mach-loongson32/regs-pwm.h
+++ b/arch/mips/include/asm/mach-loongson32/regs-pwm.h
@@ -19,11 +19,11 @@
#define PWM_CTRL 0xc
/* PWM Control Register Bits */
-#define CNT_RST (0x1 << 7)
-#define INT_SR (0x1 << 6)
-#define INT_EN (0x1 << 5)
-#define PWM_SINGLE (0x1 << 4)
-#define PWM_OE (0x1 << 3)
-#define CNT_EN 0x1
+#define CNT_RST BIT(7)
+#define INT_SR BIT(6)
+#define INT_EN BIT(5)
+#define PWM_SINGLE BIT(4)
+#define PWM_OE BIT(3)
+#define CNT_EN BIT(0)
#endif /* __ASM_MACH_LOONGSON32_REGS_PWM_H */
diff --git a/arch/mips/loongson32/common/platform.c b/arch/mips/loongson32/common/platform.c
index ddf1d4c..f2c714d 100644
--- a/arch/mips/loongson32/common/platform.c
+++ b/arch/mips/loongson32/common/platform.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
+ * Copyright (c) 2011-2016 Zhang, Keguang <keguang.zhang@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -10,14 +10,17 @@
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
+#include <linux/mtd/partitions.h>
+#include <linux/sizes.h>
#include <linux/phy.h>
#include <linux/serial_8250.h>
#include <linux/stmmac.h>
#include <linux/usb/ehci_pdriver.h>
-#include <asm-generic/sizes.h>
-#include <cpufreq.h>
#include <loongson1.h>
+#include <cpufreq.h>
+#include <dma.h>
+#include <nand.h>
/* 8250/16550 compatible UART */
#define LS1X_UART(_id) \
@@ -45,7 +48,7 @@ struct platform_device ls1x_uart_pdev = {
},
};
-void __init ls1x_serial_setup(struct platform_device *pdev)
+void __init ls1x_serial_set_uartclk(struct platform_device *pdev)
{
struct clk *clk;
struct plat_serial8250_port *p;
@@ -77,6 +80,42 @@ struct platform_device ls1x_cpufreq_pdev = {
},
};
+/* DMA */
+static struct resource ls1x_dma_resources[] = {
+ [0] = {
+ .start = LS1X_DMAC_BASE,
+ .end = LS1X_DMAC_BASE + SZ_4 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = LS1X_DMA0_IRQ,
+ .end = LS1X_DMA0_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = LS1X_DMA1_IRQ,
+ .end = LS1X_DMA1_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ [3] = {
+ .start = LS1X_DMA2_IRQ,
+ .end = LS1X_DMA2_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device ls1x_dma_pdev = {
+ .name = "ls1x-dma",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(ls1x_dma_resources),
+ .resource = ls1x_dma_resources,
+};
+
+void __init ls1x_dma_set_platdata(struct plat_ls1x_dma *pdata)
+{
+ ls1x_dma_pdev.dev.platform_data = pdata;
+}
+
/* Synopsys Ethernet GMAC */
static struct stmmac_mdio_bus_data ls1x_mdio_bus_data = {
.phy_mask = 0,
@@ -198,6 +237,64 @@ struct platform_device ls1x_eth1_pdev = {
},
};
+/* GPIO */
+static struct resource ls1x_gpio0_resources[] = {
+ [0] = {
+ .start = LS1X_GPIO0_BASE,
+ .end = LS1X_GPIO0_BASE + SZ_4 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+struct platform_device ls1x_gpio0_pdev = {
+ .name = "ls1x-gpio",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(ls1x_gpio0_resources),
+ .resource = ls1x_gpio0_resources,
+};
+
+static struct resource ls1x_gpio1_resources[] = {
+ [0] = {
+ .start = LS1X_GPIO1_BASE,
+ .end = LS1X_GPIO1_BASE + SZ_4 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+struct platform_device ls1x_gpio1_pdev = {
+ .name = "ls1x-gpio",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(ls1x_gpio1_resources),
+ .resource = ls1x_gpio1_resources,
+};
+
+/* NAND Flash */
+static struct resource ls1x_nand_resources[] = {
+ [0] = {
+ .start = LS1X_NAND_BASE,
+ .end = LS1X_NAND_BASE + SZ_32 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ /* DMA channel 0 is dedicated to NAND */
+ .start = LS1X_DMA_CHANNEL0,
+ .end = LS1X_DMA_CHANNEL0,
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+struct platform_device ls1x_nand_pdev = {
+ .name = "ls1x-nand",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(ls1x_nand_resources),
+ .resource = ls1x_nand_resources,
+};
+
+void __init ls1x_nand_set_platdata(struct plat_ls1x_nand *pdata)
+{
+ ls1x_nand_pdev.dev.platform_data = pdata;
+}
+
/* USB EHCI */
static u64 ls1x_ehci_dmamask = DMA_BIT_MASK(32);
diff --git a/arch/mips/loongson32/common/reset.c b/arch/mips/loongson32/common/reset.c
index c41e4ca..8a1d9cc 100644
--- a/arch/mips/loongson32/common/reset.c
+++ b/arch/mips/loongson32/common/reset.c
@@ -9,12 +9,13 @@
#include <linux/io.h>
#include <linux/pm.h>
+#include <linux/sizes.h>
#include <asm/idle.h>
#include <asm/reboot.h>
#include <loongson1.h>
-static void __iomem *wdt_base;
+static void __iomem *wdt_reg_base;
static void ls1x_halt(void)
{
@@ -26,9 +27,9 @@ static void ls1x_halt(void)
static void ls1x_restart(char *command)
{
- __raw_writel(0x1, wdt_base + WDT_EN);
- __raw_writel(0x1, wdt_base + WDT_TIMER);
- __raw_writel(0x1, wdt_base + WDT_SET);
+ __raw_writel(0x1, wdt_reg_base + WDT_EN);
+ __raw_writel(0x1, wdt_reg_base + WDT_TIMER);
+ __raw_writel(0x1, wdt_reg_base + WDT_SET);
ls1x_halt();
}
@@ -40,8 +41,8 @@ static void ls1x_power_off(void)
static int __init ls1x_reboot_setup(void)
{
- wdt_base = ioremap_nocache(LS1X_WDT_BASE, 0x0f);
- if (!wdt_base)
+ wdt_reg_base = ioremap_nocache(LS1X_WDT_BASE, (SZ_4 + SZ_8));
+ if (!wdt_reg_base)
panic("Failed to remap watchdog registers");
_machine_restart = ls1x_restart;
diff --git a/arch/mips/loongson32/common/time.c b/arch/mips/loongson32/common/time.c
index 0996b02..ff224f0 100644
--- a/arch/mips/loongson32/common/time.c
+++ b/arch/mips/loongson32/common/time.c
@@ -9,6 +9,7 @@
#include <linux/clk.h>
#include <linux/interrupt.h>
+#include <linux/sizes.h>
#include <asm/time.h>
#include <loongson1.h>
@@ -35,25 +36,25 @@
DEFINE_RAW_SPINLOCK(ls1x_timer_lock);
-static void __iomem *timer_base;
+static void __iomem *timer_reg_base;
static uint32_t ls1x_jiffies_per_tick;
static inline void ls1x_pwmtimer_set_period(uint32_t period)
{
- __raw_writel(period, timer_base + PWM_HRC);
- __raw_writel(period, timer_base + PWM_LRC);
+ __raw_writel(period, timer_reg_base + PWM_HRC);
+ __raw_writel(period, timer_reg_base + PWM_LRC);
}
static inline void ls1x_pwmtimer_restart(void)
{
- __raw_writel(0x0, timer_base + PWM_CNT);
- __raw_writel(INT_EN | CNT_EN, timer_base + PWM_CTRL);
+ __raw_writel(0x0, timer_reg_base + PWM_CNT);
+ __raw_writel(INT_EN | CNT_EN, timer_reg_base + PWM_CTRL);
}
void __init ls1x_pwmtimer_init(void)
{
- timer_base = ioremap(LS1X_TIMER_BASE, 0xf);
- if (!timer_base)
+ timer_reg_base = ioremap_nocache(LS1X_TIMER_BASE, SZ_16);
+ if (!timer_reg_base)
panic("Failed to remap timer registers");
ls1x_jiffies_per_tick = DIV_ROUND_CLOSEST(mips_hpt_frequency, HZ);
@@ -86,7 +87,7 @@ static cycle_t ls1x_clocksource_read(struct clocksource *cs)
*/
jifs = jiffies;
/* read the count */
- count = __raw_readl(timer_base + PWM_CNT);
+ count = __raw_readl(timer_reg_base + PWM_CNT);
/*
* It's possible for count to appear to go the wrong way for this
@@ -131,7 +132,7 @@ static int ls1x_clockevent_set_state_periodic(struct clock_event_device *cd)
raw_spin_lock(&ls1x_timer_lock);
ls1x_pwmtimer_set_period(ls1x_jiffies_per_tick);
ls1x_pwmtimer_restart();
- __raw_writel(INT_EN | CNT_EN, timer_base + PWM_CTRL);
+ __raw_writel(INT_EN | CNT_EN, timer_reg_base + PWM_CTRL);
raw_spin_unlock(&ls1x_timer_lock);
return 0;
@@ -140,7 +141,7 @@ static int ls1x_clockevent_set_state_periodic(struct clock_event_device *cd)
static int ls1x_clockevent_tick_resume(struct clock_event_device *cd)
{
raw_spin_lock(&ls1x_timer_lock);
- __raw_writel(INT_EN | CNT_EN, timer_base + PWM_CTRL);
+ __raw_writel(INT_EN | CNT_EN, timer_reg_base + PWM_CTRL);
raw_spin_unlock(&ls1x_timer_lock);
return 0;
@@ -149,8 +150,8 @@ static int ls1x_clockevent_tick_resume(struct clock_event_device *cd)
static int ls1x_clockevent_set_state_shutdown(struct clock_event_device *cd)
{
raw_spin_lock(&ls1x_timer_lock);
- __raw_writel(__raw_readl(timer_base + PWM_CTRL) & ~CNT_EN,
- timer_base + PWM_CTRL);
+ __raw_writel(__raw_readl(timer_reg_base + PWM_CTRL) & ~CNT_EN,
+ timer_reg_base + PWM_CTRL);
raw_spin_unlock(&ls1x_timer_lock);
return 0;
@@ -220,7 +221,7 @@ void __init plat_time_init(void)
#ifdef CONFIG_CEVT_CSRC_LS1X
/* setup LS1X PWM timer */
- clk = clk_get(NULL, "ls1x_pwmtimer");
+ clk = clk_get(NULL, "ls1x-pwmtimer");
if (IS_ERR(clk))
panic("unable to get timer clock, err=%ld", PTR_ERR(clk));
diff --git a/arch/mips/loongson32/ls1b/board.c b/arch/mips/loongson32/ls1b/board.c
index 58daeea..38a1d40 100644
--- a/arch/mips/loongson32/ls1b/board.c
+++ b/arch/mips/loongson32/ls1b/board.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011 Zhang, Keguang <keguang.zhang@gmail.com>
+ * Copyright (c) 2011-2016 Zhang, Keguang <keguang.zhang@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -7,26 +7,83 @@
* option) any later version.
*/
+#include <linux/leds.h>
+#include <linux/mtd/partitions.h>
+#include <linux/sizes.h>
+
+#include <loongson1.h>
+#include <dma.h>
+#include <nand.h>
#include <platform.h>
+struct plat_ls1x_dma ls1x_dma_pdata = {
+ .nr_channels = 3,
+};
+
+static struct mtd_partition ls1x_nand_parts[] = {
+ {
+ .name = "kernel",
+ .offset = 0,
+ .size = SZ_16M,
+ },
+ {
+ .name = "rootfs",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL,
+ },
+};
+
+struct plat_ls1x_nand ls1x_nand_pdata = {
+ .parts = ls1x_nand_parts,
+ .nr_parts = ARRAY_SIZE(ls1x_nand_parts),
+ .hold_cycle = 0x2,
+ .wait_cycle = 0xc,
+};
+
+static const struct gpio_led ls1x_gpio_leds[] __initconst = {
+ {
+ .name = "LED9",
+ .default_trigger = "heartbeat",
+ .gpio = 38,
+ .active_low = 1,
+ .default_state = LEDS_GPIO_DEFSTATE_OFF,
+ }, {
+ .name = "LED6",
+ .default_trigger = "nand-disk",
+ .gpio = 39,
+ .active_low = 1,
+ .default_state = LEDS_GPIO_DEFSTATE_OFF,
+ },
+};
+
+static const struct gpio_led_platform_data ls1x_led_pdata __initconst = {
+ .num_leds = ARRAY_SIZE(ls1x_gpio_leds),
+ .leds = ls1x_gpio_leds,
+};
+
static struct platform_device *ls1b_platform_devices[] __initdata = {
&ls1x_uart_pdev,
&ls1x_cpufreq_pdev,
+ &ls1x_dma_pdev,
&ls1x_eth0_pdev,
&ls1x_eth1_pdev,
&ls1x_ehci_pdev,
+ &ls1x_gpio0_pdev,
+ &ls1x_gpio1_pdev,
+ &ls1x_nand_pdev,
&ls1x_rtc_pdev,
};
static int __init ls1b_platform_init(void)
{
- int err;
+ ls1x_serial_set_uartclk(&ls1x_uart_pdev);
+ ls1x_dma_set_platdata(&ls1x_dma_pdata);
+ ls1x_nand_set_platdata(&ls1x_nand_pdata);
- ls1x_serial_setup(&ls1x_uart_pdev);
+ gpio_led_register_device(-1, &ls1x_led_pdata);
- err = platform_add_devices(ls1b_platform_devices,
+ return platform_add_devices(ls1b_platform_devices,
ARRAY_SIZE(ls1b_platform_devices));
- return err;
}
arch_initcall(ls1b_platform_init);
--
1.9.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 7/7] MAINTAINERS: add Loongson1 architecture entry
2016-04-06 12:09 [PATCH 0/7] MIPS: Loongson1B: add NAND, DMA and GPIO support Keguang Zhang
` (5 preceding siblings ...)
2016-04-06 12:09 ` [PATCH 6/7] MIPS: Loongson1B: Some updates/fixes for LS1B Keguang Zhang
@ 2016-04-06 12:09 ` Keguang Zhang
6 siblings, 0 replies; 8+ messages in thread
From: Keguang Zhang @ 2016-04-06 12:09 UTC (permalink / raw)
To: linux-kernel, linux-mips, linux-clk, linux-pm, dmaengine,
linux-gpio, linux-mtd
Cc: Ralf Baechle, Michael Turquette, Stephen Boyd, Rafael J. Wysocki,
Viresh Kumar, Vinod Koul, Dan Williams, Linus Walleij,
Alexandre Courbot, Boris Brezillon, Richard Weinberger,
David Woodhouse, Brian Norris, Kelvin Cheung
From: Kelvin Cheung <keguang.zhang@gmail.com>
This patch adds Loongson1 architecture entry.
---
MAINTAINERS | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 03e00c7..f6032a5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7271,6 +7271,15 @@ S: Supported
F: Documentation/mips/
F: arch/mips/
+MIPS/LOONGSON1 ARCHITECTURE
+M: Keguang Zhang <keguang.zhang@gmail.com>
+L: linux-mips@linux-mips.org
+S: Maintained
+F: arch/mips/loongson32/
+F: arch/mips/include/asm/mach-loongson32/
+F: drivers/*/*loongson1*
+F: drivers/*/*/*loongson1*
+
MIROSOUND PCM20 FM RADIO RECEIVER DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org
--
1.9.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
end of thread, other threads:[~2016-04-06 12:13 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-04-06 12:09 [PATCH 0/7] MIPS: Loongson1B: add NAND, DMA and GPIO support Keguang Zhang
2016-04-06 12:09 ` [PATCH 1/7] clk: Loongson1: Update clocks of Loongson1B Keguang Zhang
2016-04-06 12:09 ` [PATCH 2/7] cpufreq: Loongson1: Update cpufreq " Keguang Zhang
2016-04-06 12:09 ` [PATCH 3/7] dmaengine: Loongson1: add Loongson1 dmaengine driver Keguang Zhang
2016-04-06 12:09 ` [PATCH 4/7] mtd: nand: add Loongson1 NAND driver Keguang Zhang
2016-04-06 12:09 ` [PATCH 5/7] gpio: Loongson1: add Loongson1 GPIO driver Keguang Zhang
2016-04-06 12:09 ` [PATCH 6/7] MIPS: Loongson1B: Some updates/fixes for LS1B Keguang Zhang
2016-04-06 12:09 ` [PATCH 7/7] MAINTAINERS: add Loongson1 architecture entry Keguang Zhang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).