* [PATCH V2 1/7] clk: Loongson1: Update clocks of Loongson1B
@ 2016-04-08 11:42 Keguang Zhang
2016-04-08 11:42 ` [PATCH V2 2/7] cpufreq: Loongson1: Update cpufreq " Keguang Zhang
` (4 more replies)
0 siblings, 5 replies; 10+ messages in thread
From: Keguang Zhang @ 2016-04-08 11:42 UTC (permalink / raw)
To: 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>
---
V2:
Regenerate the patch to make it review-able.
---
drivers/clk/Makefile | 2 +-
drivers/clk/{clk-ls1x.c => clk-loongson1.c} | 25 +++++++++++++------------
2 files changed, 14 insertions(+), 13 deletions(-)
rename drivers/clk/{clk-ls1x.c => clk-loongson1.c} (86%)
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-ls1x.c b/drivers/clk/clk-loongson1.c
similarity index 86%
rename from drivers/clk/clk-ls1x.c
rename to drivers/clk/clk-loongson1.c
index d4c6198..ce2135c 100644
--- a/drivers/clk/clk-ls1x.c
+++ b/drivers/clk/clk-loongson1.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012 Zhang, Keguang <keguang.zhang@gmail.com>
+ * 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
@@ -58,11 +58,9 @@ static struct clk *__init clk_register_pll(struct device *dev,
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__);
+ hw = kzalloc(sizeof(*hw), GFP_KERNEL);
+ if (!hw)
return ERR_PTR(-ENOMEM);
- }
init.name = name;
init.ops = &ls1x_pll_clk_ops;
@@ -80,9 +78,9 @@ static struct clk *__init clk_register_pll(struct device *dev,
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", };
+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)
{
@@ -147,6 +145,7 @@ void __init ls1x_clk_init(void)
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 */
@@ -154,9 +153,11 @@ void __init ls1x_clk_init(void)
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, "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);
}
--
1.9.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH V2 2/7] cpufreq: Loongson1: Update cpufreq of Loongson1B
2016-04-08 11:42 [PATCH V2 1/7] clk: Loongson1: Update clocks of Loongson1B Keguang Zhang
@ 2016-04-08 11:42 ` Keguang Zhang
2016-04-11 3:52 ` Viresh Kumar
2016-04-08 11:42 ` [PATCH V2 3/7] dmaengine: Loongson1: add Loongson1 dmaengine driver Keguang Zhang
` (3 subsequent siblings)
4 siblings, 1 reply; 10+ messages in thread
From: Keguang Zhang @ 2016-04-08 11:42 UTC (permalink / raw)
To: 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>
---
V2:
Regenerate the patch to make it review-able.
---
drivers/cpufreq/Makefile | 2 +-
.../{ls1x-cpufreq.c => loongson1-cpufreq.c} | 114 ++++++++++-----------
2 files changed, 58 insertions(+), 58 deletions(-)
rename drivers/cpufreq/{ls1x-cpufreq.c => loongson1-cpufreq.c} (65%)
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/ls1x-cpufreq.c b/drivers/cpufreq/loongson1-cpufreq.c
similarity index 65%
rename from drivers/cpufreq/ls1x-cpufreq.c
rename to drivers/cpufreq/loongson1-cpufreq.c
index 262581b..0117820 100644
--- a/drivers/cpufreq/ls1x-cpufreq.c
+++ b/drivers/cpufreq/loongson1-cpufreq.c
@@ -1,7 +1,7 @@
/*
* CPU Frequency Scaling for Loongson 1 SoC
*
- * Copyright (C) 2014 Zhang, Keguang <keguang.zhang@gmail.com>
+ * 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
@@ -20,7 +20,7 @@
#include <cpufreq.h>
#include <loongson1.h>
-static struct {
+struct ls1x_cpufreq {
struct device *dev;
struct clk *clk; /* CPU clk */
struct clk *mux_clk; /* MUX of CPU clk */
@@ -28,7 +28,9 @@ static struct {
struct clk *osc_clk; /* OSC clk */
unsigned int max_freq;
unsigned int min_freq;
-} ls1x_cpufreq;
+};
+
+static struct ls1x_cpufreq *cpufreq;
static int ls1x_cpufreq_notifier(struct notifier_block *nb,
unsigned long val, void *data)
@@ -46,6 +48,7 @@ static struct notifier_block ls1x_cpufreq_notifier_block = {
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;
@@ -60,53 +63,49 @@ static int ls1x_cpufreq_target(struct cpufreq_policy *policy,
* - 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);
+ 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(ls1x_cpufreq.mux_clk, new_freq * 1000);
- clk_set_parent(policy->clk, ls1x_cpufreq.mux_clk);
+ 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(ls1x_cpufreq.pll_clk) / 1000;
+ pll_freq = clk_get_rate(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;
- }
+ 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 < ls1x_cpufreq.min_freq) ||
- (freq > ls1x_cpufreq.max_freq))
+ if ((freq < cpufreq->min_freq) || (freq > cpufreq->max_freq))
freq_tbl[i].frequency = CPUFREQ_ENTRY_INVALID;
else
freq_tbl[i].frequency = freq;
- dev_dbg(ls1x_cpufreq.dev,
+ 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 = ls1x_cpufreq.clk;
+ policy->clk = cpufreq->clk;
ret = cpufreq_generic_init(policy, freq_tbl, 0);
if (ret)
kfree(freq_tbl);
-out:
+
return ret;
}
@@ -138,85 +137,86 @@ static int ls1x_cpufreq_remove(struct platform_device *pdev)
static int ls1x_cpufreq_probe(struct platform_device *pdev)
{
- struct plat_ls1x_cpufreq *pdata = pdev->dev.platform_data;
+ struct plat_ls1x_cpufreq *pdata = dev_get_platdata(&pdev->dev);
struct clk *clk;
int ret;
- if (!pdata || !pdata->clk_name || !pdata->osc_clk_name)
+ if (!pdata || !pdata->clk_name || !pdata->osc_clk_name) {
+ dev_err(&pdev->dev, "platform data missing\n");
return -EINVAL;
+ }
- ls1x_cpufreq.dev = &pdev->dev;
+ cpufreq =
+ devm_kzalloc(&pdev->dev, sizeof(struct ls1x_cpufreq), GFP_KERNEL);
+ if (!cpufreq) {
+ return -ENOMEM;
+ }
+
+ 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",
+ dev_err(&pdev->dev, "unable to get %s clock\n",
pdata->clk_name);
- ret = PTR_ERR(clk);
- goto out;
+ return PTR_ERR(clk);
}
- ls1x_cpufreq.clk = clk;
+ 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;
+ dev_err(&pdev->dev, "unable to get parent of %s clock\n",
+ __clk_get_name(cpufreq->clk));
+ return PTR_ERR(clk);
}
- ls1x_cpufreq.mux_clk = clk;
+ 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;
+ dev_err(&pdev->dev, "unable to get parent of %s clock\n",
+ __clk_get_name(cpufreq->mux_clk));
+ return PTR_ERR(clk);
}
- ls1x_cpufreq.pll_clk = clk;
+ 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",
+ dev_err(&pdev->dev, "unable to get %s clock\n",
pdata->osc_clk_name);
- ret = PTR_ERR(clk);
- goto out;
+ return PTR_ERR(clk);
}
- ls1x_cpufreq.osc_clk = clk;
+ cpufreq->osc_clk = clk;
- ls1x_cpufreq.max_freq = pdata->max_freq;
- ls1x_cpufreq.min_freq = pdata->min_freq;
+ cpufreq->max_freq = pdata->max_freq;
+ 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;
+ dev_err(&pdev->dev,
+ "failed to register CPUFreq driver: %d\n", ret);
+ return ret;
}
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);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register CPUFreq notifier: %d\n", ret);
+ cpufreq_unregister_driver(&ls1x_cpufreq_driver);
+ }
- cpufreq_unregister_driver(&ls1x_cpufreq_driver);
-out:
return ret;
}
static struct platform_driver ls1x_cpufreq_platdrv = {
- .driver = {
+ .probe = ls1x_cpufreq_probe,
+ .remove = ls1x_cpufreq_remove,
+ .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_DESCRIPTION("Loongson1 CPUFreq driver");
MODULE_LICENSE("GPL");
--
1.9.1
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH V2 3/7] dmaengine: Loongson1: add Loongson1 dmaengine driver
2016-04-08 11:42 [PATCH V2 1/7] clk: Loongson1: Update clocks of Loongson1B Keguang Zhang
2016-04-08 11:42 ` [PATCH V2 2/7] cpufreq: Loongson1: Update cpufreq " Keguang Zhang
@ 2016-04-08 11:42 ` Keguang Zhang
2016-04-08 11:42 ` [PATCH V2 4/7] mtd: nand: add Loongson1 NAND driver Keguang Zhang
` (2 subsequent siblings)
4 siblings, 0 replies; 10+ messages in thread
From: Keguang Zhang @ 2016-04-08 11:42 UTC (permalink / raw)
To: 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>
---
V2:
Change the config from 'DMA_LOONGSON1' to 'LOONGSON1_DMA',
and rearrange it in alphabetical order in Kconfig and Makefile.
Fix comment style.
---
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..250a650 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -286,6 +286,15 @@ config K3_DMA
Support the DMA engine for Hisilicon K3 platform
devices.
+config LOONGSON1_DMA
+ 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.
+
config LPC18XX_DMAMUX
bool "NXP LPC18xx/43xx DMA MUX for PL080"
depends on ARCH_LPC18XX || COMPILE_TEST
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 6084127..f9f97bb 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_INTEL_IOATDMA) += ioat/
obj-$(CONFIG_INTEL_IOP_ADMA) += iop-adma.o
obj-$(CONFIG_INTEL_MIC_X100_DMA) += mic_x100_dma.o
obj-$(CONFIG_K3_DMA) += k3dma.o
+obj-$(CONFIG_LOONGSON1_DMA) += loongson1-dma.o
obj-$(CONFIG_LPC18XX_DMAMUX) += lpc18xx-dmamux.o
obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o
obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o
diff --git a/drivers/dma/loongson1-dma.c b/drivers/dma/loongson1-dma.c
new file mode 100644
index 0000000..93e58e0
--- /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] 10+ messages in thread
* [PATCH V2 4/7] mtd: nand: add Loongson1 NAND driver
2016-04-08 11:42 [PATCH V2 1/7] clk: Loongson1: Update clocks of Loongson1B Keguang Zhang
2016-04-08 11:42 ` [PATCH V2 2/7] cpufreq: Loongson1: Update cpufreq " Keguang Zhang
2016-04-08 11:42 ` [PATCH V2 3/7] dmaengine: Loongson1: add Loongson1 dmaengine driver Keguang Zhang
@ 2016-04-08 11:42 ` Keguang Zhang
2016-04-08 11:52 ` Richard Weinberger
2016-04-08 11:42 ` [PATCH V2 6/7] MIPS: Loongson1B: Some updates/fixes for LS1B Keguang Zhang
2016-04-16 9:41 ` [PATCH V2 1/7] clk: Loongson1: Update clocks of Loongson1B Kelvin Cheung
4 siblings, 1 reply; 10+ messages in thread
From: Keguang Zhang @ 2016-04-08 11:42 UTC (permalink / raw)
To: 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>
---
V2:
Modify the dependency in Kconfig due to the changes of DMA module.
---
drivers/mtd/nand/Kconfig | 8 +
drivers/mtd/nand/Makefile | 1 +
drivers/mtd/nand/loongson1_nand.c | 519 ++++++++++++++++++++++++++++++++++++++
3 files changed, 528 insertions(+)
create mode 100644 drivers/mtd/nand/loongson1_nand.c
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index f05e0e9..be20fb8 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 LOONGSON1_DMA
+ 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..28f7ca7
--- /dev/null
+++ b/drivers/mtd/nand/loongson1_nand.c
@@ -0,0 +1,519 @@
+/*
+ * 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)
+ 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] 10+ messages in thread
* [PATCH V2 6/7] MIPS: Loongson1B: Some updates/fixes for LS1B
2016-04-08 11:42 [PATCH V2 1/7] clk: Loongson1: Update clocks of Loongson1B Keguang Zhang
` (2 preceding siblings ...)
2016-04-08 11:42 ` [PATCH V2 4/7] mtd: nand: add Loongson1 NAND driver Keguang Zhang
@ 2016-04-08 11:42 ` Keguang Zhang
2016-04-16 9:41 ` [PATCH V2 1/7] clk: Loongson1: Update clocks of Loongson1B Kelvin Cheung
4 siblings, 0 replies; 10+ messages in thread
From: Keguang Zhang @ 2016-04-08 11:42 UTC (permalink / raw)
To: 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>
---
V2:
Regenerate the patch to make it review-able.
---
arch/mips/Kconfig | 2 +
.../{ls1b_defconfig => loongson1b_defconfig} | 35 +++++--
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 ++++++++++++-
15 files changed, 340 insertions(+), 104 deletions(-)
rename arch/mips/configs/{ls1b_defconfig => loongson1b_defconfig} (84%)
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/ls1b_defconfig b/arch/mips/configs/loongson1b_defconfig
similarity index 84%
rename from arch/mips/configs/ls1b_defconfig
rename to arch/mips/configs/loongson1b_defconfig
index 1b2cc1f..c442f27 100644
--- a/arch/mips/configs/ls1b_defconfig
+++ b/arch/mips/configs/loongson1b_defconfig
@@ -1,19 +1,17 @@
CONFIG_MACH_LOONGSON32=y
CONFIG_PREEMPT=y
# CONFIG_SECCOMP is not set
-CONFIG_EXPERIMENTAL=y
# 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_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_CC_OPTIMIZE_FOR_SIZE=y
CONFIG_EXPERT=y
CONFIG_PERF_EVENTS=y
# CONFIG_COMPAT_BRK is not set
@@ -41,6 +39,12 @@ 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
@@ -48,7 +52,6 @@ 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
@@ -56,7 +59,6 @@ CONFIG_NETDEVICES=y
# 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
@@ -69,18 +71,25 @@ CONFIG_LEGACY_PTY_COUNT=8
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_USB_HID=m
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_HCD_PLATFORM=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
@@ -96,15 +105,21 @@ CONFIG_VFAT_FS=y
CONFIG_PROC_KCORE=y
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
-# CONFIG_MISC_FILESYSTEMS is not set
+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/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] 10+ messages in thread
* Re: [PATCH V2 4/7] mtd: nand: add Loongson1 NAND driver
2016-04-08 11:42 ` [PATCH V2 4/7] mtd: nand: add Loongson1 NAND driver Keguang Zhang
@ 2016-04-08 11:52 ` Richard Weinberger
2016-04-08 14:30 ` Kelvin Cheung
0 siblings, 1 reply; 10+ messages in thread
From: Richard Weinberger @ 2016-04-08 11:52 UTC (permalink / raw)
To: Keguang Zhang, 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, David Woodhouse,
Brian Norris
Am 08.04.2016 um 13:42 schrieb Keguang Zhang:
> + chip->options = NAND_NO_SUBPAGE_WRITE;
> + chip->ecc.mode = NAND_ECC_SOFT;
Is the lack of HW ECC and subpage write a hardware limitation?
Thanks,
//richard
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH V2 4/7] mtd: nand: add Loongson1 NAND driver
2016-04-08 11:52 ` Richard Weinberger
@ 2016-04-08 14:30 ` Kelvin Cheung
0 siblings, 0 replies; 10+ messages in thread
From: Kelvin Cheung @ 2016-04-08 14:30 UTC (permalink / raw)
To: Richard Weinberger, 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, David Woodhouse,
Brian Norris
On 04/08/2016 07:52 PM, Richard Weinberger wrote:
> Am 08.04.2016 um 13:42 schrieb Keguang Zhang:
>> + chip->options = NAND_NO_SUBPAGE_WRITE;
>> + chip->ecc.mode = NAND_ECC_SOFT;
> Is the lack of HW ECC and subpage write a hardware limitation?
Yes, that's the hardware limitation of Loongson1B NAND controller.
> Thanks,
> //richard
Best regards,
Keguang Zhang
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH V2 2/7] cpufreq: Loongson1: Update cpufreq of Loongson1B
2016-04-08 11:42 ` [PATCH V2 2/7] cpufreq: Loongson1: Update cpufreq " Keguang Zhang
@ 2016-04-11 3:52 ` Viresh Kumar
0 siblings, 0 replies; 10+ messages in thread
From: Viresh Kumar @ 2016-04-11 3:52 UTC (permalink / raw)
To: Keguang Zhang
Cc: linux-mips, linux-clk, linux-pm, dmaengine, linux-gpio,
linux-mtd, Ralf Baechle, Michael Turquette, Stephen Boyd,
Rafael J. Wysocki, Vinod Koul, Dan Williams, Linus Walleij,
Alexandre Courbot, Boris Brezillon, Richard Weinberger,
David Woodhouse, Brian Norris
On 08-04-16, 19:42, Keguang Zhang wrote:
> 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
Its much better now, in the sense that I can see what you have done.
And see you have done a lot.
That's not how you are supposed to send the patches, sorry.
You need to break this patch into multiple patches, each targeting a
particular fix.
And I have no idea now, what change you have done to make it work for
Loongson1B. Which of the above changes does that?
And please, just send out a separate cpufreq series and don't send
everything in a single patchset that covers DMA, cpufreq, clk, etc..
--
viresh
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH V2 1/7] clk: Loongson1: Update clocks of Loongson1B
2016-04-08 11:42 [PATCH V2 1/7] clk: Loongson1: Update clocks of Loongson1B Keguang Zhang
` (3 preceding siblings ...)
2016-04-08 11:42 ` [PATCH V2 6/7] MIPS: Loongson1B: Some updates/fixes for LS1B Keguang Zhang
@ 2016-04-16 9:41 ` Kelvin Cheung
2016-04-18 4:56 ` Stephen Boyd
4 siblings, 1 reply; 10+ messages in thread
From: Kelvin Cheung @ 2016-04-16 9:41 UTC (permalink / raw)
To: linux-mips, linux-clk; +Cc: Ralf Baechle, Michael Turquette, Stephen Boyd
[-- Attachment #1: Type: text/plain, Size: 4443 bytes --]
Dear Stephen,
Could you check this patch?
Thanks!
2016-04-08 19:42 GMT+08:00 Keguang Zhang <keguang.zhang@gmail.com>:
> 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>
>
> ---
> V2:
> Regenerate the patch to make it review-able.
> ---
> drivers/clk/Makefile | 2 +-
> drivers/clk/{clk-ls1x.c => clk-loongson1.c} | 25 +++++++++++++------------
> 2 files changed, 14 insertions(+), 13 deletions(-)
> rename drivers/clk/{clk-ls1x.c => clk-loongson1.c} (86%)
>
> 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-ls1x.c b/drivers/clk/clk-loongson1.c
> similarity index 86%
> rename from drivers/clk/clk-ls1x.c
> rename to drivers/clk/clk-loongson1.c
> index d4c6198..ce2135c 100644
> --- a/drivers/clk/clk-ls1x.c
> +++ b/drivers/clk/clk-loongson1.c
> @@ -1,5 +1,5 @@
> /*
> - * Copyright (c) 2012 Zhang, Keguang <keguang.zhang@gmail.com>
> + * 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
> @@ -58,11 +58,9 @@ static struct clk *__init clk_register_pll(struct
> device *dev,
> 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__);
> + hw = kzalloc(sizeof(*hw), GFP_KERNEL);
> + if (!hw)
> return ERR_PTR(-ENOMEM);
> - }
>
> init.name = name;
> init.ops = &ls1x_pll_clk_ops;
> @@ -80,9 +78,9 @@ static struct clk *__init clk_register_pll(struct device
> *dev,
> 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", };
> +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)
> {
> @@ -147,6 +145,7 @@ void __init ls1x_clk_init(void)
> 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 */
> @@ -154,9 +153,11 @@ void __init ls1x_clk_init(void)
> 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, "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);
> }
> --
> 1.9.1
>
>
--
Best regards,
Kelvin Cheung
[-- Attachment #2: Type: text/html, Size: 6189 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH V2 1/7] clk: Loongson1: Update clocks of Loongson1B
2016-04-16 9:41 ` [PATCH V2 1/7] clk: Loongson1: Update clocks of Loongson1B Kelvin Cheung
@ 2016-04-18 4:56 ` Stephen Boyd
0 siblings, 0 replies; 10+ messages in thread
From: Stephen Boyd @ 2016-04-18 4:56 UTC (permalink / raw)
To: Kelvin Cheung; +Cc: linux-mips, linux-clk, Ralf Baechle, Michael Turquette
On 04/16, Kelvin Cheung wrote:
> Dear Stephen,
> Could you check this patch?
> Thanks!
>
> 2016-04-08 19:42 GMT+08:00 Keguang Zhang <keguang.zhang@gmail.com>:
>
> > 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>
> >
> > ---
Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2016-04-18 4:56 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-04-08 11:42 [PATCH V2 1/7] clk: Loongson1: Update clocks of Loongson1B Keguang Zhang
2016-04-08 11:42 ` [PATCH V2 2/7] cpufreq: Loongson1: Update cpufreq " Keguang Zhang
2016-04-11 3:52 ` Viresh Kumar
2016-04-08 11:42 ` [PATCH V2 3/7] dmaengine: Loongson1: add Loongson1 dmaengine driver Keguang Zhang
2016-04-08 11:42 ` [PATCH V2 4/7] mtd: nand: add Loongson1 NAND driver Keguang Zhang
2016-04-08 11:52 ` Richard Weinberger
2016-04-08 14:30 ` Kelvin Cheung
2016-04-08 11:42 ` [PATCH V2 6/7] MIPS: Loongson1B: Some updates/fixes for LS1B Keguang Zhang
2016-04-16 9:41 ` [PATCH V2 1/7] clk: Loongson1: Update clocks of Loongson1B Kelvin Cheung
2016-04-18 4:56 ` Stephen Boyd
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.