|
@@ -16,6 +16,7 @@
|
|
|
#include <linux/time.h>
|
|
|
|
|
|
#include <linux/platform_data/mtd-davinci-aemif.h>
|
|
|
+#include <linux/platform_data/mtd-davinci.h>
|
|
|
|
|
|
/* Timing value configuration */
|
|
|
|
|
@@ -43,6 +44,17 @@
|
|
|
WSTROBE(WSTROBE_MAX) | \
|
|
|
WSETUP(WSETUP_MAX))
|
|
|
|
|
|
+static inline unsigned int davinci_aemif_readl(void __iomem *base, int offset)
|
|
|
+{
|
|
|
+ return readl_relaxed(base + offset);
|
|
|
+}
|
|
|
+
|
|
|
+static inline void davinci_aemif_writel(void __iomem *base,
|
|
|
+ int offset, unsigned long value)
|
|
|
+{
|
|
|
+ writel_relaxed(value, base + offset);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* aemif_calc_rate - calculate timing data.
|
|
|
* @wanted: The cycle time needed in nanoseconds.
|
|
@@ -76,6 +88,7 @@ static int aemif_calc_rate(int wanted, unsigned long clk, int max)
|
|
|
* @t: timing values to be progammed
|
|
|
* @base: The virtual base address of the AEMIF interface
|
|
|
* @cs: chip-select to program the timing values for
|
|
|
+ * @clkrate: the AEMIF clkrate
|
|
|
*
|
|
|
* This function programs the given timing values (in real clock) into the
|
|
|
* AEMIF registers taking the AEMIF clock into account.
|
|
@@ -86,24 +99,17 @@ static int aemif_calc_rate(int wanted, unsigned long clk, int max)
|
|
|
*
|
|
|
* Returns 0 on success, else negative errno.
|
|
|
*/
|
|
|
-int davinci_aemif_setup_timing(struct davinci_aemif_timing *t,
|
|
|
- void __iomem *base, unsigned cs)
|
|
|
+static int davinci_aemif_setup_timing(struct davinci_aemif_timing *t,
|
|
|
+ void __iomem *base, unsigned cs,
|
|
|
+ unsigned long clkrate)
|
|
|
{
|
|
|
unsigned set, val;
|
|
|
int ta, rhold, rstrobe, rsetup, whold, wstrobe, wsetup;
|
|
|
unsigned offset = A1CR_OFFSET + cs * 4;
|
|
|
- struct clk *aemif_clk;
|
|
|
- unsigned long clkrate;
|
|
|
|
|
|
if (!t)
|
|
|
return 0; /* Nothing to do */
|
|
|
|
|
|
- aemif_clk = clk_get(NULL, "aemif");
|
|
|
- if (IS_ERR(aemif_clk))
|
|
|
- return PTR_ERR(aemif_clk);
|
|
|
-
|
|
|
- clkrate = clk_get_rate(aemif_clk);
|
|
|
-
|
|
|
clkrate /= 1000; /* turn clock into kHz for ease of use */
|
|
|
|
|
|
ta = aemif_calc_rate(t->ta, clkrate, TA_MAX);
|
|
@@ -130,4 +136,83 @@ int davinci_aemif_setup_timing(struct davinci_aemif_timing *t,
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
-EXPORT_SYMBOL(davinci_aemif_setup_timing);
|
|
|
+
|
|
|
+/**
|
|
|
+ * davinci_aemif_setup - setup AEMIF interface by davinci_nand_pdata
|
|
|
+ * @pdev - link to platform device to setup settings for
|
|
|
+ *
|
|
|
+ * This function does not use any locking while programming the AEMIF
|
|
|
+ * because it is expected that there is only one user of a given
|
|
|
+ * chip-select.
|
|
|
+ *
|
|
|
+ * Returns 0 on success, else negative errno.
|
|
|
+ */
|
|
|
+int davinci_aemif_setup(struct platform_device *pdev)
|
|
|
+{
|
|
|
+ struct davinci_nand_pdata *pdata = dev_get_platdata(&pdev->dev);
|
|
|
+ uint32_t val;
|
|
|
+ unsigned long clkrate;
|
|
|
+ struct resource *res;
|
|
|
+ void __iomem *base;
|
|
|
+ struct clk *clk;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ clk = clk_get(&pdev->dev, "aemif");
|
|
|
+ if (IS_ERR(clk)) {
|
|
|
+ ret = PTR_ERR(clk);
|
|
|
+ dev_dbg(&pdev->dev, "unable to get AEMIF clock, err %d\n", ret);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = clk_prepare_enable(clk);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_dbg(&pdev->dev, "unable to enable AEMIF clock, err %d\n",
|
|
|
+ ret);
|
|
|
+ goto err_put;
|
|
|
+ }
|
|
|
+
|
|
|
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
|
|
+ if (!res) {
|
|
|
+ dev_err(&pdev->dev, "cannot get IORESOURCE_MEM\n");
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+
|
|
|
+ base = ioremap(res->start, resource_size(res));
|
|
|
+ if (!base) {
|
|
|
+ dev_err(&pdev->dev, "ioremap failed for resource %pR\n", res);
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Setup Async configuration register in case we did not boot
|
|
|
+ * from NAND and so bootloader did not bother to set it up.
|
|
|
+ */
|
|
|
+ val = davinci_aemif_readl(base, A1CR_OFFSET + pdev->id * 4);
|
|
|
+ /*
|
|
|
+ * Extended Wait is not valid and Select Strobe mode is not
|
|
|
+ * used
|
|
|
+ */
|
|
|
+ val &= ~(ACR_ASIZE_MASK | ACR_EW_MASK | ACR_SS_MASK);
|
|
|
+ if (pdata->options & NAND_BUSWIDTH_16)
|
|
|
+ val |= 0x1;
|
|
|
+
|
|
|
+ davinci_aemif_writel(base, A1CR_OFFSET + pdev->id * 4, val);
|
|
|
+
|
|
|
+ clkrate = clk_get_rate(clk);
|
|
|
+
|
|
|
+ if (pdata->timing)
|
|
|
+ ret = davinci_aemif_setup_timing(pdata->timing, base, pdev->id,
|
|
|
+ clkrate);
|
|
|
+
|
|
|
+ if (ret < 0)
|
|
|
+ dev_dbg(&pdev->dev, "NAND timing values setup fail\n");
|
|
|
+
|
|
|
+ iounmap(base);
|
|
|
+err:
|
|
|
+ clk_disable_unprepare(clk);
|
|
|
+err_put:
|
|
|
+ clk_put(clk);
|
|
|
+ return ret;
|
|
|
+}
|