Devicetree
 help / color / mirror / Atom feed
* Re: [PATCH v3] phy: Add new Exynos5 USB 3.0 PHY driver
From: Vivek Gautam @ 2014-01-30  4:19 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: Vivek Gautam, Linux USB Mailing List,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-doc-u79uwXL29TY76Z2rM5mHXA, Greg KH, Kukjin Kim,
	Felipe Balbi, Tomasz Figa, Kamil Debski, Sylwester Nawrocki,
	Julius Werner, Jingoo Han
In-Reply-To: <52E61F63.1090200-l0cyMroinI0@public.gmane.org>

Hi Kishon,


On Mon, Jan 27, 2014 at 2:27 PM, Kishon Vijay Abraham I <kishon-l0cyMroinI0@public.gmane.org> wrote:
> Hi,

Thanks for review. Please find my answers inline below.

>
> On Monday 20 January 2014 07:12 PM, Vivek Gautam wrote:
>> Add a new driver for the USB 3.0 PHY on Exynos5 series of SoCs.
>> The new driver uses the generic PHY framework and will interact
>> with DWC3 controller present on Exynos5 series of SoCs.
>> Thereby, removing old phy-samsung-usb3 driver and related code
>> used untill now which was based on usb/phy framework.
>>
>> Signed-off-by: Vivek Gautam <gautam.vivek-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
>> ---
>>
>> Changes from v2:
>> 1) Added support for multiple PHYs (UTMI+ and PIPE3) and
>>    related changes in the driver structuring.
>> 2) Added a xlate function to get the required phy out of
>>    number of PHYs in mutiple PHY scenerio.
>> 3) Changed the names of few structures and variables to
>>    have a clearer meaning.
>> 4) Added 'usb3phy_config' structure to take care of mutiple
>>    phys for a SoC having 'exynos5_usb3phy_drv_data' driver data.
>> 5) Not deleting support for old driver 'phy-samsung-usb3' until
>>    required support for generic phy is added to DWC3.
>>
>>  .../devicetree/bindings/phy/samsung-phy.txt        |   49 ++
>>  drivers/phy/Kconfig                                |    8 +
>>  drivers/phy/Makefile                               |    1 +
>>  drivers/phy/phy-exynos5-usb3.c                     |  621 ++++++++++++++++++++
>>  4 files changed, 679 insertions(+)
>>  create mode 100644 drivers/phy/phy-exynos5-usb3.c
[snip]

>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>> index 330ef2d..32f9f38 100644
>> --- a/drivers/phy/Kconfig
>> +++ b/drivers/phy/Kconfig
>> @@ -51,4 +51,12 @@ config PHY_EXYNOS_DP_VIDEO
>>       help
>>         Support for Display Port PHY found on Samsung EXYNOS SoCs.
>>
>> +config PHY_EXYNOS5_USB3
>> +     tristate "Exynos5 SoC series USB 3.0 PHY driver"
>> +     depends on ARCH_EXYNOS5
>> +     select GENERIC_PHY
>> +     select MFD_SYSCON
>
> add depends on 'HAS_IOMEM'. Someone reported getting
> undefined reference to `devm_ioremap_resource' with it.

Ok will add it.

>> +     help
>> +       Enable USB 3.0 PHY support for Exynos 5 SoC series
>> +
>>  endmenu
[snip]

>> diff --git a/drivers/phy/phy-exynos5-usb3.c b/drivers/phy/phy-exynos5-usb3.c
>> new file mode 100644
>> index 0000000..24efed0
>> --- /dev/null
>> +++ b/drivers/phy/phy-exynos5-usb3.c
>> @@ -0,0 +1,621 @@
>> +/*
>> + * Samsung EXYNOS5 SoC series USB 3.0 PHY driver
>> + *
>> + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
>> + * Author: Vivek Gautam <gautam.vivek-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/io.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/phy/phy.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/mutex.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <linux/regmap.h>
>> +
>> +/* Exynos USB PHY registers */
>> +#define EXYNOS5_FSEL_9MHZ6           0x0
>> +#define EXYNOS5_FSEL_10MHZ           0x1
>> +#define EXYNOS5_FSEL_12MHZ           0x2
>> +#define EXYNOS5_FSEL_19MHZ2          0x3
>> +#define EXYNOS5_FSEL_20MHZ           0x4
>> +#define EXYNOS5_FSEL_24MHZ           0x5
>> +#define EXYNOS5_FSEL_50MHZ           0x7
>> +
>> +/* EXYNOS5: USB 3.0 DRD PHY registers */
>> +#define EXYNOS5_DRD_LINKSYSTEM                       (0x04)
>> +
>> +#define LINKSYSTEM_FLADJ_MASK                        (0x3f << 1)
>> +#define LINKSYSTEM_FLADJ(_x)                 ((_x) << 1)
>> +#define LINKSYSTEM_XHCI_VERSION_CONTROL              (0x1 << 27)
>> +
>> +#define EXYNOS5_DRD_PHYUTMI                  (0x08)
>> +
>> +#define PHYUTMI_OTGDISABLE                   (0x1 << 6)
>> +#define PHYUTMI_FORCESUSPEND                 (0x1 << 1)
>> +#define PHYUTMI_FORCESLEEP                   (0x1 << 0)
>
> use BIT macro here and below?

Ok.

>> +
>> +#define EXYNOS5_DRD_PHYPIPE                  (0x0c)
>> +
>> +#define EXYNOS5_DRD_PHYCLKRST                        (0x10)
>> +
>> +#define PHYCLKRST_EN_UTMISUSPEND             (0x1 << 31)
>> +
>> +#define PHYCLKRST_SSC_REFCLKSEL_MASK         (0xff << 23)
>> +#define PHYCLKRST_SSC_REFCLKSEL(_x)          ((_x) << 23)
>> +
>> +#define PHYCLKRST_SSC_RANGE_MASK             (0x03 << 21)
>> +#define PHYCLKRST_SSC_RANGE(_x)                      ((_x) << 21)
>> +
>> +#define PHYCLKRST_SSC_EN                     (0x1 << 20)
>> +#define PHYCLKRST_REF_SSP_EN                 (0x1 << 19)
>> +#define PHYCLKRST_REF_CLKDIV2                        (0x1 << 18)
>> +
>> +#define PHYCLKRST_MPLL_MULTIPLIER_MASK               (0x7f << 11)
>> +#define PHYCLKRST_MPLL_MULTIPLIER_100MHZ_REF (0x19 << 11)
>> +#define PHYCLKRST_MPLL_MULTIPLIER_50M_REF    (0x32 << 11)
>> +#define PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF  (0x68 << 11)
>> +#define PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF  (0x7d << 11)
>> +#define PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF       (0x02 << 11)
>> +
>> +#define PHYCLKRST_FSEL_MASK                  (0x3f << 5)
>> +#define PHYCLKRST_FSEL(_x)                   ((_x) << 5)
>> +#define PHYCLKRST_FSEL_PAD_100MHZ            (0x27 << 5)
>> +#define PHYCLKRST_FSEL_PAD_24MHZ             (0x2a << 5)
>> +#define PHYCLKRST_FSEL_PAD_20MHZ             (0x31 << 5)
>> +#define PHYCLKRST_FSEL_PAD_19_2MHZ           (0x38 << 5)
>> +
>> +#define PHYCLKRST_RETENABLEN                 (0x1 << 4)
>> +
>> +#define PHYCLKRST_REFCLKSEL_MASK             (0x03 << 2)
>> +#define PHYCLKRST_REFCLKSEL_PAD_REFCLK               (0x2 << 2)
>> +#define PHYCLKRST_REFCLKSEL_EXT_REFCLK               (0x3 << 2)
>> +
>> +#define PHYCLKRST_PORTRESET                  (0x1 << 1)
>> +#define PHYCLKRST_COMMONONN                  (0x1 << 0)
>> +
>> +#define EXYNOS5_DRD_PHYREG0                  (0x14)
>> +#define EXYNOS5_DRD_PHYREG1                  (0x18)
>> +
>> +#define EXYNOS5_DRD_PHYPARAM0                        (0x1c)
>> +
>> +#define PHYPARAM0_REF_USE_PAD                        (0x1 << 31)
>> +#define PHYPARAM0_REF_LOSLEVEL_MASK          (0x1f << 26)
>> +#define PHYPARAM0_REF_LOSLEVEL                       (0x9 << 26)
>> +
>> +#define EXYNOS5_DRD_PHYPARAM1                        (0x20)
>> +
>> +#define PHYPARAM1_PCS_TXDEEMPH_MASK          (0x1f << 0)
>> +#define PHYPARAM1_PCS_TXDEEMPH                       (0x1c)
>> +
>> +#define EXYNOS5_DRD_PHYTERM                  (0x24)
>> +
>> +#define EXYNOS5_DRD_PHYTEST                  (0x28)
>> +
>> +#define PHYTEST_POWERDOWN_SSP                        (0x1 << 3)
>> +#define PHYTEST_POWERDOWN_HSP                        (0x1 << 2)
>> +
>> +#define EXYNOS5_DRD_PHYADP                   (0x2c)
>> +
>> +#define EXYNOS5_DRD_PHYBATCHG                        (0x30)
>> +
>> +#define PHYBATCHG_UTMI_CLKSEL                        (0x1 << 2)
>> +
>> +#define EXYNOS5_DRD_PHYRESUME                        (0x34)
>> +#define EXYNOS5_DRD_LINKPORT                 (0x44)
>> +
>> +/* Power isolation defined in power management unit */
>> +#define EXYNOS5_USB3DRD_PHY_PMU_REG_OFFSET   (0x704)
>> +#define EXYNOS5_USB3DRD_PMU_ISOL             (1 << 0)
>> +
>> +#define KHZ  1000
>> +#define MHZ  (KHZ * KHZ)
>> +
>> +enum exynos5_usb3phy_id {
>> +     EXYNOS5_USB3PHY_UTMI,
>> +     EXYNOS5_USB3PHY_PIPE3,
>> +     EXYNOS5_USB3PHYS_NUM,
>> +};
>> +
>> +struct usb3phy_config {
>> +     u32 id;
>> +     u32 reg_pmu_offset;
>> +     void (*phy_isol)(struct phy *phy, u32 on);
>> +};
>> +
>> +struct exynos5_usb3phy_drv_data {
>> +     bool has_usb30_sclk;
>> +     bool has_multi_controller;
>> +     const struct usb3phy_config *phy_cfg;
>> +};
>> +
>> +/**
>> + * struct exynos5_usb3phy_driver - driver data for USB 3.0 PHY
>
> Is this really a driver data? I think it should be just exynos5_usb3phy.
Yes, not a driver data, rather just 'exynos_usb3phy' structure. will
modify the name

>> + * @dev: pointer to device instance of this platform device
>> + * @reg_phy: usb phy controller register memory base
>> + * @clk: phy clock for register access
>> + * @usb30_sclk: additional special clock for phy operations
>> + * @drv_data: pointer to SoC level driver data structure
>> + * @phys[]: array for 'EXYNOS5_USB3PHYS_NUM' number of PHY
>> + *       instances each with its 'phy' and 'phy_cfg'.
>> + * @extrefclk: frequency select settings when using 'separate
>> + *          reference clocks' for SS and HS operations
>> + * @rate: rate of reference clock to PHY block
>> + * @channel: number of PHY channels present in SoC
>> + */
>> +struct exynos5_usb3phy_driver {
>> +     struct device *dev;
>> +     void __iomem *reg_phy;
>> +     struct clk *clk;
>> +     struct clk *usb30_sclk;
>> +     const struct exynos5_usb3phy_drv_data *drv_data;
>> +     struct phy_usb_instance {
>> +             struct phy *phy;
>> +             u32 index;
>> +             struct regmap *reg_isol;
>> +             const struct usb3phy_config *phy_cfg;
>> +     } phys[EXYNOS5_USB3PHYS_NUM];
>> +     u32 extrefclk;
>> +     unsigned long rate;
>> +     u32 channel;
>> +};
>> +
>> +#define to_usb3phy_driver(inst) \
>> +     container_of((inst), struct exynos5_usb3phy_driver, \
>> +                  phys[(inst)->index]);
>> +
>> +/*
>> + * exynos5_rate_to_clk() converts the supplied clock rate to the value that
>> + * can be written to the phy register.
>> + */
>> +static u32 exynos5_rate_to_clk(unsigned long rate)
>> +{
>> +     unsigned int clksel;
>> +
>> +     /* EXYNOS5_FSEL_MASK */
>> +
>> +     switch (rate) {
>> +     case 9600 * KHZ:
>> +             clksel = EXYNOS5_FSEL_9MHZ6;
>> +             break;
>> +     case 10 * MHZ:
>> +             clksel = EXYNOS5_FSEL_10MHZ;
>> +             break;
>> +     case 12 * MHZ:
>> +             clksel = EXYNOS5_FSEL_12MHZ;
>> +             break;
>> +     case 19200 * KHZ:
>> +             clksel = EXYNOS5_FSEL_19MHZ2;
>> +             break;
>> +     case 20 * MHZ:
>> +             clksel = EXYNOS5_FSEL_20MHZ;
>> +             break;
>> +     case 24 * MHZ:
>> +             clksel = EXYNOS5_FSEL_24MHZ;
>> +             break;
>> +     case 50 * MHZ:
>> +             clksel = EXYNOS5_FSEL_50MHZ;
>> +             break;
>> +     default:
>> +             clksel = -EINVAL;
>> +     }
>> +
>> +     return clksel;
>> +}
>> +
>> +static void exynos5_usb3phy_isol(struct phy *phy, unsigned int on)
>> +{
>> +     u32 pmu_offset;
>> +     struct phy_usb_instance *inst = phy_get_drvdata(phy);
>> +     struct exynos5_usb3phy_driver *drv = to_usb3phy_driver(inst);
>> +
>> +     pmu_offset = inst->phy_cfg->reg_pmu_offset;
>> +     if (!inst->reg_isol)
>> +             return;
>> +
>> +     switch (drv->channel) {
>> +     case 1:
>> +             /* Channel 1 is at 0x708 offset */
>> +             pmu_offset += sizeof(&pmu_offset);
>> +             break;
>> +     case 0:
>> +     default:
>> +             /* Channel 0 is at 0x704 offset */
>> +             break;
>> +     }
>
> This can be in a simple 'if' stmt no?
What if there are systems with more channels? In that case also we
will have to fall back to a switch-case statement ?

>> +
>> +     regmap_update_bits(inst->reg_isol, pmu_offset,
>> +                        EXYNOS5_USB3DRD_PMU_ISOL, ~on);
>> +}
>> +
>> +/*
>> + * Sets the phy clk as EXTREFCLK (XXTI) which is internal clock from clock core.
>> + */
>> +static u32 exynos5_usb3phy_set_refclk(struct exynos5_usb3phy_driver *drv)
>> +{
>> +     u32 reg;
>> +
>> +     reg = PHYCLKRST_REFCLKSEL_EXT_REFCLK |
>> +           PHYCLKRST_FSEL(drv->extrefclk);
>> +
>> +     switch (drv->extrefclk) {
>> +     case EXYNOS5_FSEL_50MHZ:
>> +             reg |= (PHYCLKRST_MPLL_MULTIPLIER_50M_REF |
>> +                     PHYCLKRST_SSC_REFCLKSEL(0x00));
>> +             break;
>> +     case EXYNOS5_FSEL_24MHZ:
>> +             reg |= (PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF |
>> +                     PHYCLKRST_SSC_REFCLKSEL(0x88));
>> +             break;
>> +     case EXYNOS5_FSEL_20MHZ:
>> +             reg |= (PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF |
>> +                     PHYCLKRST_SSC_REFCLKSEL(0x00));
>> +             break;
>> +     case EXYNOS5_FSEL_19MHZ2:
>> +             reg |= (PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF |
>> +                     PHYCLKRST_SSC_REFCLKSEL(0x88));
>> +             break;
>> +     default:
>> +             dev_dbg(drv->dev, "unsupported ref clk\n");
>> +             break;
>> +     }
>> +
>> +     return reg;
>> +}
>> +
>> +static int exynos5_usb3phy_init(struct phy *phy)
>> +{
>> +     int ret;
>> +     u32 phyparam0;
>> +     u32 phyparam1;
>> +     u32 linksystem;
>> +     u32 phybatchg;
>> +     u32 phytest;
>> +     u32 phyclkrst;
>
> instead you can define a single variable 'u32 reg' for register read and writes.

Right.

>> +     struct phy_usb_instance *inst = phy_get_drvdata(phy);
>> +     struct exynos5_usb3phy_driver *drv = to_usb3phy_driver(inst);
>> +
>> +     ret = clk_prepare_enable(drv->clk);
>> +     if (ret)
>> +             return ret;
>> +
>> +     drv->extrefclk = exynos5_rate_to_clk(drv->rate);
>> +     if (drv->extrefclk == -EINVAL) {
>> +             dev_err(drv->dev, "Clock rate (%ld) not supported\n",
>> +                                             drv->rate);
>> +             return -EINVAL;
>> +     }
>> +
>> +     /* Reset USB 3.0 PHY */
>> +     writel(0x0, drv->reg_phy + EXYNOS5_DRD_PHYREG0);
>> +
>> +     phyparam0 = readl(drv->reg_phy + EXYNOS5_DRD_PHYPARAM0);
>> +     /* Select PHY CLK source */
>> +     phyparam0 &= ~PHYPARAM0_REF_USE_PAD;
>> +     /* Set Loss-of-Signal Detector sensitivity */
>> +     phyparam0 &= ~PHYPARAM0_REF_LOSLEVEL_MASK;
>> +     phyparam0 |= PHYPARAM0_REF_LOSLEVEL;
>> +     writel(phyparam0, drv->reg_phy + EXYNOS5_DRD_PHYPARAM0);
>> +
>> +     writel(0x0, drv->reg_phy + EXYNOS5_DRD_PHYRESUME);
>> +
>> +     /*
>> +      * Setting the Frame length Adj value[6:1] to default 0x20
>> +      * See xHCI 1.0 spec, 5.2.4
>> +      */
>> +     linksystem = LINKSYSTEM_XHCI_VERSION_CONTROL |
>> +                  LINKSYSTEM_FLADJ(0x20);
>> +     writel(linksystem, drv->reg_phy + EXYNOS5_DRD_LINKSYSTEM);
>> +
>> +     phyparam1 = readl(drv->reg_phy + EXYNOS5_DRD_PHYPARAM1);
>> +     /* Set Tx De-Emphasis level */
>> +     phyparam1 &= ~PHYPARAM1_PCS_TXDEEMPH_MASK;
>> +     phyparam1 |= PHYPARAM1_PCS_TXDEEMPH;
>> +     writel(phyparam1, drv->reg_phy + EXYNOS5_DRD_PHYPARAM1);
>> +
>> +     phybatchg = readl(drv->reg_phy + EXYNOS5_DRD_PHYBATCHG);
>> +     phybatchg |= PHYBATCHG_UTMI_CLKSEL;
>> +     writel(phybatchg, drv->reg_phy + EXYNOS5_DRD_PHYBATCHG);
>> +
>> +     /* PHYTEST POWERDOWN Control */
>> +     phytest = readl(drv->reg_phy + EXYNOS5_DRD_PHYTEST);
>> +     phytest &= ~(PHYTEST_POWERDOWN_SSP |
>> +                  PHYTEST_POWERDOWN_HSP);
>> +     writel(phytest, drv->reg_phy + EXYNOS5_DRD_PHYTEST);
>> +
>> +     /* UTMI Power Control */
>> +     writel(PHYUTMI_OTGDISABLE, drv->reg_phy + EXYNOS5_DRD_PHYUTMI);
>
> All these UTMI configuration should be done in usb2 init.
Ok, will move this to separate function.

>> +
>> +     phyclkrst = exynos5_usb3phy_set_refclk(drv);
>> +
>> +     phyclkrst |= PHYCLKRST_PORTRESET |
>> +                  /* Digital power supply in normal operating mode */
>> +                  PHYCLKRST_RETENABLEN |
>> +                  /* Enable ref clock for SS function */
>> +                  PHYCLKRST_REF_SSP_EN |
>> +                  /* Enable spread spectrum */
>> +                  PHYCLKRST_SSC_EN |
>> +                  /* Power down HS Bias and PLL blocks in suspend mode */
>> +                  PHYCLKRST_COMMONONN;
>> +
>> +     writel(phyclkrst, drv->reg_phy + EXYNOS5_DRD_PHYCLKRST);
>> +
>> +     udelay(10);
>> +
>> +     phyclkrst &= ~PHYCLKRST_PORTRESET;
>> +     writel(phyclkrst, drv->reg_phy + EXYNOS5_DRD_PHYCLKRST);
>> +
>> +     clk_disable_unprepare(drv->clk);
>> +
>> +     return 0;
>> +}
>> +
>> +static int exynos5_usb3phy_exit(struct phy *phy)
>> +{
>> +     int ret;
>> +     u32 phyutmi;
>> +     u32 phyclkrst;
>> +     u32 phytest;
>
> same here..
right, will do it.

>> +     struct phy_usb_instance *inst = phy_get_drvdata(phy);
>> +     struct exynos5_usb3phy_driver *drv = to_usb3phy_driver(inst);
>> +
>> +     ret = clk_prepare_enable(drv->clk);
>> +     if (ret)
>> +             return ret;
>> +
>> +     phyutmi = PHYUTMI_OTGDISABLE |
>> +               PHYUTMI_FORCESUSPEND |
>> +               PHYUTMI_FORCESLEEP;
>> +     writel(phyutmi, drv->reg_phy + EXYNOS5_DRD_PHYUTMI);
>
> here too.. UTMI configuration should be part of USB2.
ok.

>> +
[snip]

>> +
>> +static struct phy_ops exynos5_usb3phy_ops = {
>> +     .init           = exynos5_usb3phy_init,
>> +     .exit           = exynos5_usb3phy_exit,
>> +     .power_on       = exynos5_usb3phy_power_on,
>> +     .power_off      = exynos5_usb3phy_power_off,
>> +     .owner          = THIS_MODULE,
>> +};
>> +
>> +const struct usb3phy_config exynos5_usb3phy_cfg[] = {
>> +     {
>> +             .id             = EXYNOS5_USB3PHY_UTMI,
>
> This should be USB2 no?
Actually the thought was to have similar naming for enums.
EXYNOS5_USB3PHY_UTMI
EXYNOS5_USB3PHY_PIPE3

Since the entire driver was going that way.
But will change these to a more common name
EXYNOS5_DRDPHY_UTMI
EXYNOS5_DRDPHY_PIPE3,
in the same fashion the register names are defined.
Will that be fine ?

>> +             .reg_pmu_offset = EXYNOS5_USB3DRD_PHY_PMU_REG_OFFSET,
>> +             .phy_isol       = exynos5_usb3phy_isol,
>> +     },
>> +     {
>> +             .id             = EXYNOS5_USB3PHY_PIPE3,
>> +             .reg_pmu_offset = EXYNOS5_USB3DRD_PHY_PMU_REG_OFFSET,
>> +             .phy_isol       = exynos5_usb3phy_isol,
>> +     },
>> +     {},
>> +};
>> +
>> +const struct exynos5_usb3phy_drv_data exynos5420_usb3phy = {
>> +     .has_usb30_sclk         = true,
>> +     .has_multi_controller   = true,
>> +     .phy_cfg                = exynos5_usb3phy_cfg,
>> +};
>> +
>> +const struct exynos5_usb3phy_drv_data exynos5250_usb3phy = {
>> +     .has_usb30_sclk         = false,
>> +     .has_multi_controller   = false,
>> +     .phy_cfg                = exynos5_usb3phy_cfg,
>> +};
>> +
>> +static const struct of_device_id exynos5_usb3phy_of_match[] = {
>> +     {
>> +             .compatible = "samsung,exynos5250-usb3phy",
>> +             .data = &exynos5250_usb3phy
>> +     }, {
>> +             .compatible = "samsung,exynos5420-usb3phy",
>> +             .data = &exynos5420_usb3phy
>> +     },
>> +     { },
>> +};
>> +
>> +static int exynos5_usb3phy_probe(struct platform_device *pdev)
>> +{
>> +     struct device *dev = &pdev->dev;
>> +     struct device_node *node = dev->of_node;
>> +     struct exynos5_usb3phy_driver *drv;
>> +     struct phy_provider *phy_provider;
>> +     struct resource *res;
>> +     struct clk *clk;
>> +     const struct of_device_id *match;
>> +     const struct exynos5_usb3phy_drv_data *drv_data;
>> +     struct regmap *reg_isol;
>> +     int i;
>> +
>> +     /*
>> +      * Exynos systems are completely DT enabled,
>> +      * so lets not have any platform data support for this driver.
>> +      */
>
> Then you should add depend on OF for this driver.
Right. depends on CONFIG_OF. Will add one.

>> +     if (!node) {
>> +             dev_err(dev, "no device node found\n");
>> +             return -ENODEV;
>> +     }
>> +
>> +     match = of_match_node(exynos5_usb3phy_of_match, pdev->dev.of_node);
>> +     if (!match) {
>> +             dev_err(dev, "of_match_node() failed\n");
>> +             return -EINVAL;
>> +     }
>> +     drv_data = match->data;
>> +
>> +     drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
>> +     if (!drv) {
>> +             dev_err(dev, "Failed to allocate memory\n");
>
> dev_err is not needed here.

Right, will remove it.

>> +             return -ENOMEM;
>> +     }
>> +
[snip]

>> +     dev_dbg(dev, "Creating usb3drd phy\n");
> dev_vdbg?

Sure.

>
> Cheers
> Kishon
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html



-- 
Best Regards
Vivek Gautam
Samsung R&D Institute, Bangalore
India
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v2 0/5] Smart Card(SC) interface, TI USIM & NxP SC phy driver
From: Satish Patel @ 2014-01-30  5:52 UTC (permalink / raw)
  To: Satish Patel, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-omap-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	rob-VoJi6FS/r0vR7s880joybQ, tony-4v6yS6AI5VpBDgjK7y7TUQ
  Cc: grant.likely-QSEj5FYQhm4dnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A
In-Reply-To: <1390192434-19386-1-git-send-email-satish.patel-l0cyMroinI0@public.gmane.org>


On 1/20/2014 10:03 AM, Satish Patel wrote:
> Changes from v1:
> * RFC(v1) comments are fixed
>
> ** removed "gpio_to_irq" as GPIO controller process  cell from DT and
> give it to DT node
> ** comments on documentation
> ** few other comments on null checks are resolved
>
> * BWT timing configuration is added to ti-usim driver
>
> v1 cover letter link#
> https://lkml.org/lkml/2014/1/6/250
>
> Satish Patel (5):
>   sc_phy:SmartCard(SC) PHY interface to SC controller
>   misc: tda8026: Add NXP TDA8026 PHY driver
>   char: ti-usim: Add driver for USIM module on AM43xx
>   ARM: dts: AM43xx: DT entries added for ti-usim
>   ARM: dts: AM43xx-epos-evm: DT entries  for ti-usim and phy
>
>  Documentation/devicetree/bindings/misc/tda8026.txt |   19 +
>  .../devicetree/bindings/ti-usim/ti-usim.txt        |   31 +
>  Documentation/sc_phy.txt                           |  171 ++
>  arch/arm/boot/dts/am4372.dtsi                      |   10 +
>  arch/arm/boot/dts/am43x-epos-evm.dts               |   43 +
>  drivers/char/Kconfig                               |    7 +
>  drivers/char/Makefile                              |    1 +
>  drivers/char/ti-usim-hw.h                          |  863 +++++++++
>  drivers/char/ti-usim.c                             | 1859 ++++++++++++++++++++
>  drivers/misc/Kconfig                               |    7 +
>  drivers/misc/Makefile                              |    1 +
>  drivers/misc/tda8026.c                             | 1255 +++++++++++++
>  include/linux/sc_phy.h                             |  132 ++
>  include/linux/ti-usim.h                            |   98 +
>  14 files changed, 4497 insertions(+), 0 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/misc/tda8026.txt
>  create mode 100644 Documentation/devicetree/bindings/ti-usim/ti-usim.txt
>  create mode 100644 Documentation/sc_phy.txt
>  create mode 100644 drivers/char/ti-usim-hw.h
>  create mode 100644 drivers/char/ti-usim.c
>  create mode 100644 drivers/misc/tda8026.c
>  create mode 100644 include/linux/sc_phy.h
>  create mode 100644 include/linux/ti-usim.h
Any comments on this patch series ?

If not,
Can you accept these patches for next merge window

Thanks
Satish

>
>

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v3] phy: Add new Exynos5 USB 3.0 PHY driver
From: Kishon Vijay Abraham I @ 2014-01-30  6:18 UTC (permalink / raw)
  To: Vivek Gautam
  Cc: Vivek Gautam, Linux USB Mailing List,
	linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org,
	devicetree@vger.kernel.org, linux-doc, Greg KH, Kukjin Kim,
	Felipe Balbi, Tomasz Figa, Kamil Debski, Sylwester Nawrocki,
	Julius Werner, Jingoo Han
In-Reply-To: <CAFp+6iHGchyZFRGyT1D3O3mv6TFOMGUdYvc4qmLEanCKwMrDkw@mail.gmail.com>

Hi,

On Thursday 30 January 2014 09:49 AM, Vivek Gautam wrote:
> Hi Kishon,
> 
> 
> On Mon, Jan 27, 2014 at 2:27 PM, Kishon Vijay Abraham I <kishon@ti.com> wrote:
>> Hi,
> 
> Thanks for review. Please find my answers inline below.
> 
>>
>> On Monday 20 January 2014 07:12 PM, Vivek Gautam wrote:
>>> Add a new driver for the USB 3.0 PHY on Exynos5 series of SoCs.
>>> The new driver uses the generic PHY framework and will interact
>>> with DWC3 controller present on Exynos5 series of SoCs.
>>> Thereby, removing old phy-samsung-usb3 driver and related code
>>> used untill now which was based on usb/phy framework.
>>>
>>> Signed-off-by: Vivek Gautam <gautam.vivek@samsung.com>
>>> ---
>>>
>>> Changes from v2:
>>> 1) Added support for multiple PHYs (UTMI+ and PIPE3) and
>>>    related changes in the driver structuring.
>>> 2) Added a xlate function to get the required phy out of
>>>    number of PHYs in mutiple PHY scenerio.
>>> 3) Changed the names of few structures and variables to
>>>    have a clearer meaning.
>>> 4) Added 'usb3phy_config' structure to take care of mutiple
>>>    phys for a SoC having 'exynos5_usb3phy_drv_data' driver data.
>>> 5) Not deleting support for old driver 'phy-samsung-usb3' until
>>>    required support for generic phy is added to DWC3.
>>>
>>>  .../devicetree/bindings/phy/samsung-phy.txt        |   49 ++
>>>  drivers/phy/Kconfig                                |    8 +
>>>  drivers/phy/Makefile                               |    1 +
>>>  drivers/phy/phy-exynos5-usb3.c                     |  621 ++++++++++++++++++++
>>>  4 files changed, 679 insertions(+)
>>>  create mode 100644 drivers/phy/phy-exynos5-usb3.c
> [snip]
> 
>>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>>> index 330ef2d..32f9f38 100644
>>> --- a/drivers/phy/Kconfig
>>> +++ b/drivers/phy/Kconfig
>>> @@ -51,4 +51,12 @@ config PHY_EXYNOS_DP_VIDEO
>>>       help
>>>         Support for Display Port PHY found on Samsung EXYNOS SoCs.
>>>
>>> +config PHY_EXYNOS5_USB3

This shouldn't be USB3 since this driver has support for both USB2 and USB3.
maybe just PHY_EXYNOS5_USB?
>>> +     tristate "Exynos5 SoC series USB 3.0 PHY driver"
>>> +     depends on ARCH_EXYNOS5
>>> +     select GENERIC_PHY
>>> +     select MFD_SYSCON
>>
>> add depends on 'HAS_IOMEM'. Someone reported getting
>> undefined reference to `devm_ioremap_resource' with it.
> 
> Ok will add it.
> 
>>> +     help
>>> +       Enable USB 3.0 PHY support for Exynos 5 SoC series
>>> +
>>>  endmenu
> [snip]
> 
>>> diff --git a/drivers/phy/phy-exynos5-usb3.c b/drivers/phy/phy-exynos5-usb3.c
>>> new file mode 100644
>>> index 0000000..24efed0
>>> --- /dev/null
>>> +++ b/drivers/phy/phy-exynos5-usb3.c
>>> @@ -0,0 +1,621 @@
>>> +/*
>>> + * Samsung EXYNOS5 SoC series USB 3.0 PHY driver
>>> + *
>>> + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
>>> + * Author: Vivek Gautam <gautam.vivek@samsung.com>
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify
>>> + * it under the terms of the GNU General Public License version 2 as
>>> + * published by the Free Software Foundation.
>>> + */
>>> +
>>> +#include <linux/clk.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/io.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of.h>
>>> +#include <linux/of_address.h>
>>> +#include <linux/phy/phy.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/mutex.h>
>>> +#include <linux/mfd/syscon.h>
>>> +#include <linux/regmap.h>
>>> +
>>> +/* Exynos USB PHY registers */
>>> +#define EXYNOS5_FSEL_9MHZ6           0x0
>>> +#define EXYNOS5_FSEL_10MHZ           0x1
>>> +#define EXYNOS5_FSEL_12MHZ           0x2
>>> +#define EXYNOS5_FSEL_19MHZ2          0x3
>>> +#define EXYNOS5_FSEL_20MHZ           0x4
>>> +#define EXYNOS5_FSEL_24MHZ           0x5
>>> +#define EXYNOS5_FSEL_50MHZ           0x7
>>> +
>>> +/* EXYNOS5: USB 3.0 DRD PHY registers */
>>> +#define EXYNOS5_DRD_LINKSYSTEM                       (0x04)
>>> +
>>> +#define LINKSYSTEM_FLADJ_MASK                        (0x3f << 1)
>>> +#define LINKSYSTEM_FLADJ(_x)                 ((_x) << 1)
>>> +#define LINKSYSTEM_XHCI_VERSION_CONTROL              (0x1 << 27)
>>> +
>>> +#define EXYNOS5_DRD_PHYUTMI                  (0x08)
>>> +
>>> +#define PHYUTMI_OTGDISABLE                   (0x1 << 6)
>>> +#define PHYUTMI_FORCESUSPEND                 (0x1 << 1)
>>> +#define PHYUTMI_FORCESLEEP                   (0x1 << 0)
>>
>> use BIT macro here and below?
> 
> Ok.
> 
>>> +
>>> +#define EXYNOS5_DRD_PHYPIPE                  (0x0c)
>>> +
>>> +#define EXYNOS5_DRD_PHYCLKRST                        (0x10)
>>> +
>>> +#define PHYCLKRST_EN_UTMISUSPEND             (0x1 << 31)
>>> +
>>> +#define PHYCLKRST_SSC_REFCLKSEL_MASK         (0xff << 23)
>>> +#define PHYCLKRST_SSC_REFCLKSEL(_x)          ((_x) << 23)
>>> +
>>> +#define PHYCLKRST_SSC_RANGE_MASK             (0x03 << 21)
>>> +#define PHYCLKRST_SSC_RANGE(_x)                      ((_x) << 21)
>>> +
>>> +#define PHYCLKRST_SSC_EN                     (0x1 << 20)
>>> +#define PHYCLKRST_REF_SSP_EN                 (0x1 << 19)
>>> +#define PHYCLKRST_REF_CLKDIV2                        (0x1 << 18)
>>> +
>>> +#define PHYCLKRST_MPLL_MULTIPLIER_MASK               (0x7f << 11)
>>> +#define PHYCLKRST_MPLL_MULTIPLIER_100MHZ_REF (0x19 << 11)
>>> +#define PHYCLKRST_MPLL_MULTIPLIER_50M_REF    (0x32 << 11)
>>> +#define PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF  (0x68 << 11)
>>> +#define PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF  (0x7d << 11)
>>> +#define PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF       (0x02 << 11)
>>> +
>>> +#define PHYCLKRST_FSEL_MASK                  (0x3f << 5)
>>> +#define PHYCLKRST_FSEL(_x)                   ((_x) << 5)
>>> +#define PHYCLKRST_FSEL_PAD_100MHZ            (0x27 << 5)
>>> +#define PHYCLKRST_FSEL_PAD_24MHZ             (0x2a << 5)
>>> +#define PHYCLKRST_FSEL_PAD_20MHZ             (0x31 << 5)
>>> +#define PHYCLKRST_FSEL_PAD_19_2MHZ           (0x38 << 5)
>>> +
>>> +#define PHYCLKRST_RETENABLEN                 (0x1 << 4)
>>> +
>>> +#define PHYCLKRST_REFCLKSEL_MASK             (0x03 << 2)
>>> +#define PHYCLKRST_REFCLKSEL_PAD_REFCLK               (0x2 << 2)
>>> +#define PHYCLKRST_REFCLKSEL_EXT_REFCLK               (0x3 << 2)
>>> +
>>> +#define PHYCLKRST_PORTRESET                  (0x1 << 1)
>>> +#define PHYCLKRST_COMMONONN                  (0x1 << 0)
>>> +
>>> +#define EXYNOS5_DRD_PHYREG0                  (0x14)
>>> +#define EXYNOS5_DRD_PHYREG1                  (0x18)
>>> +
>>> +#define EXYNOS5_DRD_PHYPARAM0                        (0x1c)
>>> +
>>> +#define PHYPARAM0_REF_USE_PAD                        (0x1 << 31)
>>> +#define PHYPARAM0_REF_LOSLEVEL_MASK          (0x1f << 26)
>>> +#define PHYPARAM0_REF_LOSLEVEL                       (0x9 << 26)
>>> +
>>> +#define EXYNOS5_DRD_PHYPARAM1                        (0x20)
>>> +
>>> +#define PHYPARAM1_PCS_TXDEEMPH_MASK          (0x1f << 0)
>>> +#define PHYPARAM1_PCS_TXDEEMPH                       (0x1c)
>>> +
>>> +#define EXYNOS5_DRD_PHYTERM                  (0x24)
>>> +
>>> +#define EXYNOS5_DRD_PHYTEST                  (0x28)
>>> +
>>> +#define PHYTEST_POWERDOWN_SSP                        (0x1 << 3)
>>> +#define PHYTEST_POWERDOWN_HSP                        (0x1 << 2)
>>> +
>>> +#define EXYNOS5_DRD_PHYADP                   (0x2c)
>>> +
>>> +#define EXYNOS5_DRD_PHYBATCHG                        (0x30)
>>> +
>>> +#define PHYBATCHG_UTMI_CLKSEL                        (0x1 << 2)
>>> +
>>> +#define EXYNOS5_DRD_PHYRESUME                        (0x34)
>>> +#define EXYNOS5_DRD_LINKPORT                 (0x44)
>>> +
>>> +/* Power isolation defined in power management unit */
>>> +#define EXYNOS5_USB3DRD_PHY_PMU_REG_OFFSET   (0x704)
>>> +#define EXYNOS5_USB3DRD_PMU_ISOL             (1 << 0)
>>> +
>>> +#define KHZ  1000
>>> +#define MHZ  (KHZ * KHZ)
>>> +
>>> +enum exynos5_usb3phy_id {
>>> +     EXYNOS5_USB3PHY_UTMI,
>>> +     EXYNOS5_USB3PHY_PIPE3,
>>> +     EXYNOS5_USB3PHYS_NUM,
>>> +};

here and all the structure names below shouldn't have usb3 in their names since
this is not just a 'usb3' phy driver..
>>> +
>>> +struct usb3phy_config {
>>> +     u32 id;
>>> +     u32 reg_pmu_offset;
>>> +     void (*phy_isol)(struct phy *phy, u32 on);
>>> +};
>>> +
>>> +struct exynos5_usb3phy_drv_data {
>>> +     bool has_usb30_sclk;
>>> +     bool has_multi_controller;
>>> +     const struct usb3phy_config *phy_cfg;
>>> +};
>>> +
>>> +/**
>>> + * struct exynos5_usb3phy_driver - driver data for USB 3.0 PHY
>>
>> Is this really a driver data? I think it should be just exynos5_usb3phy.
> Yes, not a driver data, rather just 'exynos_usb3phy' structure. will
> modify the name
> 
>>> + * @dev: pointer to device instance of this platform device
>>> + * @reg_phy: usb phy controller register memory base
>>> + * @clk: phy clock for register access
>>> + * @usb30_sclk: additional special clock for phy operations
>>> + * @drv_data: pointer to SoC level driver data structure
>>> + * @phys[]: array for 'EXYNOS5_USB3PHYS_NUM' number of PHY
>>> + *       instances each with its 'phy' and 'phy_cfg'.
>>> + * @extrefclk: frequency select settings when using 'separate
>>> + *          reference clocks' for SS and HS operations
>>> + * @rate: rate of reference clock to PHY block
>>> + * @channel: number of PHY channels present in SoC
>>> + */
>>> +struct exynos5_usb3phy_driver {
>>> +     struct device *dev;
>>> +     void __iomem *reg_phy;
>>> +     struct clk *clk;
>>> +     struct clk *usb30_sclk;
>>> +     const struct exynos5_usb3phy_drv_data *drv_data;
>>> +     struct phy_usb_instance {
>>> +             struct phy *phy;
>>> +             u32 index;
>>> +             struct regmap *reg_isol;
>>> +             const struct usb3phy_config *phy_cfg;
>>> +     } phys[EXYNOS5_USB3PHYS_NUM];
>>> +     u32 extrefclk;
>>> +     unsigned long rate;
>>> +     u32 channel;
>>> +};
>>> +
>>> +#define to_usb3phy_driver(inst) \
>>> +     container_of((inst), struct exynos5_usb3phy_driver, \
>>> +                  phys[(inst)->index]);
>>> +
>>> +/*
>>> + * exynos5_rate_to_clk() converts the supplied clock rate to the value that
>>> + * can be written to the phy register.
>>> + */
>>> +static u32 exynos5_rate_to_clk(unsigned long rate)
>>> +{
>>> +     unsigned int clksel;
>>> +
>>> +     /* EXYNOS5_FSEL_MASK */
>>> +
>>> +     switch (rate) {
>>> +     case 9600 * KHZ:
>>> +             clksel = EXYNOS5_FSEL_9MHZ6;
>>> +             break;
>>> +     case 10 * MHZ:
>>> +             clksel = EXYNOS5_FSEL_10MHZ;
>>> +             break;
>>> +     case 12 * MHZ:
>>> +             clksel = EXYNOS5_FSEL_12MHZ;
>>> +             break;
>>> +     case 19200 * KHZ:
>>> +             clksel = EXYNOS5_FSEL_19MHZ2;
>>> +             break;
>>> +     case 20 * MHZ:
>>> +             clksel = EXYNOS5_FSEL_20MHZ;
>>> +             break;
>>> +     case 24 * MHZ:
>>> +             clksel = EXYNOS5_FSEL_24MHZ;
>>> +             break;
>>> +     case 50 * MHZ:
>>> +             clksel = EXYNOS5_FSEL_50MHZ;
>>> +             break;
>>> +     default:
>>> +             clksel = -EINVAL;
>>> +     }
>>> +
>>> +     return clksel;
>>> +}
>>> +
>>> +static void exynos5_usb3phy_isol(struct phy *phy, unsigned int on)
>>> +{
>>> +     u32 pmu_offset;
>>> +     struct phy_usb_instance *inst = phy_get_drvdata(phy);
>>> +     struct exynos5_usb3phy_driver *drv = to_usb3phy_driver(inst);
>>> +
>>> +     pmu_offset = inst->phy_cfg->reg_pmu_offset;
>>> +     if (!inst->reg_isol)
>>> +             return;
>>> +
>>> +     switch (drv->channel) {
>>> +     case 1:
>>> +             /* Channel 1 is at 0x708 offset */
>>> +             pmu_offset += sizeof(&pmu_offset);
>>> +             break;
>>> +     case 0:
>>> +     default:
>>> +             /* Channel 0 is at 0x704 offset */
>>> +             break;
>>> +     }
>>
>> This can be in a simple 'if' stmt no?
> What if there are systems with more channels? In that case also we
> will have to fall back to a switch-case statement ?

right.
> 
>>> +
>>> +     regmap_update_bits(inst->reg_isol, pmu_offset,
>>> +                        EXYNOS5_USB3DRD_PMU_ISOL, ~on);
>>> +}
>>> +
>>> +/*
>>> + * Sets the phy clk as EXTREFCLK (XXTI) which is internal clock from clock core.
>>> + */
>>> +static u32 exynos5_usb3phy_set_refclk(struct exynos5_usb3phy_driver *drv)
>>> +{
>>> +     u32 reg;
>>> +
>>> +     reg = PHYCLKRST_REFCLKSEL_EXT_REFCLK |
>>> +           PHYCLKRST_FSEL(drv->extrefclk);
>>> +
>>> +     switch (drv->extrefclk) {
>>> +     case EXYNOS5_FSEL_50MHZ:
>>> +             reg |= (PHYCLKRST_MPLL_MULTIPLIER_50M_REF |
>>> +                     PHYCLKRST_SSC_REFCLKSEL(0x00));
>>> +             break;
>>> +     case EXYNOS5_FSEL_24MHZ:
>>> +             reg |= (PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF |
>>> +                     PHYCLKRST_SSC_REFCLKSEL(0x88));
>>> +             break;
>>> +     case EXYNOS5_FSEL_20MHZ:
>>> +             reg |= (PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF |
>>> +                     PHYCLKRST_SSC_REFCLKSEL(0x00));
>>> +             break;
>>> +     case EXYNOS5_FSEL_19MHZ2:
>>> +             reg |= (PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF |
>>> +                     PHYCLKRST_SSC_REFCLKSEL(0x88));
>>> +             break;
>>> +     default:
>>> +             dev_dbg(drv->dev, "unsupported ref clk\n");
>>> +             break;
>>> +     }
>>> +
>>> +     return reg;
>>> +}
>>> +
>>> +static int exynos5_usb3phy_init(struct phy *phy)
>>> +{
>>> +     int ret;
>>> +     u32 phyparam0;
>>> +     u32 phyparam1;
>>> +     u32 linksystem;
>>> +     u32 phybatchg;
>>> +     u32 phytest;
>>> +     u32 phyclkrst;
>>
>> instead you can define a single variable 'u32 reg' for register read and writes.
> 
> Right.
> 
>>> +     struct phy_usb_instance *inst = phy_get_drvdata(phy);
>>> +     struct exynos5_usb3phy_driver *drv = to_usb3phy_driver(inst);
>>> +
>>> +     ret = clk_prepare_enable(drv->clk);
>>> +     if (ret)
>>> +             return ret;
>>> +
>>> +     drv->extrefclk = exynos5_rate_to_clk(drv->rate);
>>> +     if (drv->extrefclk == -EINVAL) {
>>> +             dev_err(drv->dev, "Clock rate (%ld) not supported\n",
>>> +                                             drv->rate);
>>> +             return -EINVAL;
>>> +     }
>>> +
>>> +     /* Reset USB 3.0 PHY */
>>> +     writel(0x0, drv->reg_phy + EXYNOS5_DRD_PHYREG0);
>>> +
>>> +     phyparam0 = readl(drv->reg_phy + EXYNOS5_DRD_PHYPARAM0);
>>> +     /* Select PHY CLK source */
>>> +     phyparam0 &= ~PHYPARAM0_REF_USE_PAD;
>>> +     /* Set Loss-of-Signal Detector sensitivity */
>>> +     phyparam0 &= ~PHYPARAM0_REF_LOSLEVEL_MASK;
>>> +     phyparam0 |= PHYPARAM0_REF_LOSLEVEL;
>>> +     writel(phyparam0, drv->reg_phy + EXYNOS5_DRD_PHYPARAM0);
>>> +
>>> +     writel(0x0, drv->reg_phy + EXYNOS5_DRD_PHYRESUME);
>>> +
>>> +     /*
>>> +      * Setting the Frame length Adj value[6:1] to default 0x20
>>> +      * See xHCI 1.0 spec, 5.2.4
>>> +      */
>>> +     linksystem = LINKSYSTEM_XHCI_VERSION_CONTROL |
>>> +                  LINKSYSTEM_FLADJ(0x20);
>>> +     writel(linksystem, drv->reg_phy + EXYNOS5_DRD_LINKSYSTEM);
>>> +
>>> +     phyparam1 = readl(drv->reg_phy + EXYNOS5_DRD_PHYPARAM1);
>>> +     /* Set Tx De-Emphasis level */
>>> +     phyparam1 &= ~PHYPARAM1_PCS_TXDEEMPH_MASK;
>>> +     phyparam1 |= PHYPARAM1_PCS_TXDEEMPH;
>>> +     writel(phyparam1, drv->reg_phy + EXYNOS5_DRD_PHYPARAM1);
>>> +
>>> +     phybatchg = readl(drv->reg_phy + EXYNOS5_DRD_PHYBATCHG);
>>> +     phybatchg |= PHYBATCHG_UTMI_CLKSEL;
>>> +     writel(phybatchg, drv->reg_phy + EXYNOS5_DRD_PHYBATCHG);
>>> +
>>> +     /* PHYTEST POWERDOWN Control */
>>> +     phytest = readl(drv->reg_phy + EXYNOS5_DRD_PHYTEST);
>>> +     phytest &= ~(PHYTEST_POWERDOWN_SSP |
>>> +                  PHYTEST_POWERDOWN_HSP);
>>> +     writel(phytest, drv->reg_phy + EXYNOS5_DRD_PHYTEST);
>>> +
>>> +     /* UTMI Power Control */
>>> +     writel(PHYUTMI_OTGDISABLE, drv->reg_phy + EXYNOS5_DRD_PHYUTMI);
>>
>> All these UTMI configuration should be done in usb2 init.
> Ok, will move this to separate function.
> 
>>> +
>>> +     phyclkrst = exynos5_usb3phy_set_refclk(drv);
>>> +
>>> +     phyclkrst |= PHYCLKRST_PORTRESET |
>>> +                  /* Digital power supply in normal operating mode */
>>> +                  PHYCLKRST_RETENABLEN |
>>> +                  /* Enable ref clock for SS function */
>>> +                  PHYCLKRST_REF_SSP_EN |
>>> +                  /* Enable spread spectrum */
>>> +                  PHYCLKRST_SSC_EN |
>>> +                  /* Power down HS Bias and PLL blocks in suspend mode */
>>> +                  PHYCLKRST_COMMONONN;
>>> +
>>> +     writel(phyclkrst, drv->reg_phy + EXYNOS5_DRD_PHYCLKRST);
>>> +
>>> +     udelay(10);
>>> +
>>> +     phyclkrst &= ~PHYCLKRST_PORTRESET;
>>> +     writel(phyclkrst, drv->reg_phy + EXYNOS5_DRD_PHYCLKRST);
>>> +
>>> +     clk_disable_unprepare(drv->clk);
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static int exynos5_usb3phy_exit(struct phy *phy)
>>> +{
>>> +     int ret;
>>> +     u32 phyutmi;
>>> +     u32 phyclkrst;
>>> +     u32 phytest;
>>
>> same here..
> right, will do it.
> 
>>> +     struct phy_usb_instance *inst = phy_get_drvdata(phy);
>>> +     struct exynos5_usb3phy_driver *drv = to_usb3phy_driver(inst);
>>> +
>>> +     ret = clk_prepare_enable(drv->clk);
>>> +     if (ret)
>>> +             return ret;
>>> +
>>> +     phyutmi = PHYUTMI_OTGDISABLE |
>>> +               PHYUTMI_FORCESUSPEND |
>>> +               PHYUTMI_FORCESLEEP;
>>> +     writel(phyutmi, drv->reg_phy + EXYNOS5_DRD_PHYUTMI);
>>
>> here too.. UTMI configuration should be part of USB2.
> ok.
> 
>>> +
> [snip]
> 
>>> +
>>> +static struct phy_ops exynos5_usb3phy_ops = {
>>> +     .init           = exynos5_usb3phy_init,
>>> +     .exit           = exynos5_usb3phy_exit,
>>> +     .power_on       = exynos5_usb3phy_power_on,
>>> +     .power_off      = exynos5_usb3phy_power_off,
>>> +     .owner          = THIS_MODULE,
>>> +};
>>> +
>>> +const struct usb3phy_config exynos5_usb3phy_cfg[] = {
>>> +     {
>>> +             .id             = EXYNOS5_USB3PHY_UTMI,
>>
>> This should be USB2 no?
> Actually the thought was to have similar naming for enums.
> EXYNOS5_USB3PHY_UTMI
> EXYNOS5_USB3PHY_PIPE3
> 
> Since the entire driver was going that way.
> But will change these to a more common name
> EXYNOS5_DRDPHY_UTMI
> EXYNOS5_DRDPHY_PIPE3,
> in the same fashion the register names are defined.
> Will that be fine ?

Yeah.

Thanks
Kishon

^ permalink raw reply

* Re: [Patch v3 2/2] dmaengine: qcom_bam_dma: Add device tree binding
From: Andy Gross @ 2014-01-30  6:23 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Lars-Peter Clausen, Vinod Koul, Dan Williams, dmaengine,
	devicetree, linux-kernel, linux-arm-kernel, linux-arm-msm
In-Reply-To: <4697306.PPWWh8UGTE@wuerfel>

On Tue, Jan 28, 2014 at 10:16:53AM +0100, Arnd Bergmann wrote:
> On Tuesday 28 January 2014 10:05:35 Lars-Peter Clausen wrote:
> > > +
> > > +Clients must use the format described in the dma.txt file, using a three cell
> > > +specifier for each channel.
> > > +
> > > +The three cells in order are:
> > > +  1. A phandle pointing to the DMA controller
> > > +  2. The channel number
> > > +  3. Direction of the fixed unidirectional channel
> > > +     0 - Memory to Device
> > > +     1 - Device to Memory
> > > +     2 - Device to Device
> > > +
> > 
> > Why does the direction needs to be specified in specifier? I see two
> > options, either the direction per is fixed in hardware. In that case the DMA
> > controller node should describe which channel is which direction. Or the
> > direction is not fixed in hardware and can be changed at runtime in which
> > case it should be set on a per descriptor basis.
> 
> Normally the direction is implied by dmaengine_slave_config().
> Note that neither the dma slave API nor the generic DT binding
> can actually support device-to-device transfers, since this
> normally implies using two dma-request lines rather than one.
> 
> There might be a case where the direction is required in order
> to allocate a channel, because the engine has specialized channels
> per direction, and might connect any of them to any dma request
> line. This does not seem to be the case for "bam", because
> the DMA specifier already contains a specific channel number, not
> a request line or slave ID number.

After some deliberation, I think the best solution is removing the direction
from the DT for now.  It doesn't add anything except some verification
of direction.

As for the device to device:
As I mentioned before, each bam dma node is attached to a specific peripheral
(with one exception, but lets skip over that).  The peripherals allow for more
than one execution environment to access the peripheral and attached bam.  2 bam
channels can be connected to form a unidirectional pipe from one execution
environment to another.  Once the pipe is configured, the actually transfer
resembles a cyclical dma transfer and continues until you explicitly stop it.

That functionality will come later.

-- 
sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation

^ permalink raw reply

* Re: [PATCH v3] phy: Add new Exynos5 USB 3.0 PHY driver
From: Vivek Gautam @ 2014-01-30  7:33 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: Vivek Gautam, Linux USB Mailing List,
	linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org,
	devicetree@vger.kernel.org, linux-doc, Greg KH, Kukjin Kim,
	Felipe Balbi, Tomasz Figa, Kamil Debski, Sylwester Nawrocki,
	Julius Werner, Jingoo Han
In-Reply-To: <52E9EEBC.8040000@ti.com>

Hi,


On Thu, Jan 30, 2014 at 11:48 AM, Kishon Vijay Abraham I <kishon@ti.com> wrote:
> Hi,
>
> On Thursday 30 January 2014 09:49 AM, Vivek Gautam wrote:
>> Hi Kishon,
>>
>>
>> On Mon, Jan 27, 2014 at 2:27 PM, Kishon Vijay Abraham I <kishon@ti.com> wrote:
>>> Hi,
>>
>> Thanks for review. Please find my answers inline below.
>>
>>>
>>> On Monday 20 January 2014 07:12 PM, Vivek Gautam wrote:
>>>> Add a new driver for the USB 3.0 PHY on Exynos5 series of SoCs.
>>>> The new driver uses the generic PHY framework and will interact
>>>> with DWC3 controller present on Exynos5 series of SoCs.
>>>> Thereby, removing old phy-samsung-usb3 driver and related code
>>>> used untill now which was based on usb/phy framework.
>>>>
>>>> Signed-off-by: Vivek Gautam <gautam.vivek@samsung.com>
>>>> ---
>>>>
>>>> Changes from v2:
>>>> 1) Added support for multiple PHYs (UTMI+ and PIPE3) and
>>>>    related changes in the driver structuring.
>>>> 2) Added a xlate function to get the required phy out of
>>>>    number of PHYs in mutiple PHY scenerio.
>>>> 3) Changed the names of few structures and variables to
>>>>    have a clearer meaning.
>>>> 4) Added 'usb3phy_config' structure to take care of mutiple
>>>>    phys for a SoC having 'exynos5_usb3phy_drv_data' driver data.
>>>> 5) Not deleting support for old driver 'phy-samsung-usb3' until
>>>>    required support for generic phy is added to DWC3.
>>>>
>>>>  .../devicetree/bindings/phy/samsung-phy.txt        |   49 ++
>>>>  drivers/phy/Kconfig                                |    8 +
>>>>  drivers/phy/Makefile                               |    1 +
>>>>  drivers/phy/phy-exynos5-usb3.c                     |  621 ++++++++++++++++++++
>>>>  4 files changed, 679 insertions(+)
>>>>  create mode 100644 drivers/phy/phy-exynos5-usb3.c
>> [snip]
>>
>>>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>>>> index 330ef2d..32f9f38 100644
>>>> --- a/drivers/phy/Kconfig
>>>> +++ b/drivers/phy/Kconfig
>>>> @@ -51,4 +51,12 @@ config PHY_EXYNOS_DP_VIDEO
>>>>       help
>>>>         Support for Display Port PHY found on Samsung EXYNOS SoCs.
>>>>
>>>> +config PHY_EXYNOS5_USB3
>
> This shouldn't be USB3 since this driver has support for both USB2 and USB3.
> maybe just PHY_EXYNOS5_USB?

Ok, will change this.

>>>> +     tristate "Exynos5 SoC series USB 3.0 PHY driver"
>>>> +     depends on ARCH_EXYNOS5
>>>> +     select GENERIC_PHY
>>>> +     select MFD_SYSCON
>>>
>>> add depends on 'HAS_IOMEM'. Someone reported getting
>>> undefined reference to `devm_ioremap_resource' with it.
>>
>> Ok will add it.
>>
>>>> +     help
>>>> +       Enable USB 3.0 PHY support for Exynos 5 SoC series
>>>> +
>>>>  endmenu
>> [snip]
>>
>>>> diff --git a/drivers/phy/phy-exynos5-usb3.c b/drivers/phy/phy-exynos5-usb3.c
>>>> new file mode 100644
>>>> index 0000000..24efed0
>>>> --- /dev/null
>>>> +++ b/drivers/phy/phy-exynos5-usb3.c
>>>> @@ -0,0 +1,621 @@
>>>> +/*
>>>> + * Samsung EXYNOS5 SoC series USB 3.0 PHY driver
>>>> + *
>>>> + * Copyright (C) 2013 Samsung Electronics Co., Ltd.
>>>> + * Author: Vivek Gautam <gautam.vivek@samsung.com>
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or modify
>>>> + * it under the terms of the GNU General Public License version 2 as
>>>> + * published by the Free Software Foundation.
>>>> + */
>>>> +
>>>> +#include <linux/clk.h>
>>>> +#include <linux/delay.h>
>>>> +#include <linux/io.h>
>>>> +#include <linux/kernel.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/of.h>
>>>> +#include <linux/of_address.h>
>>>> +#include <linux/phy/phy.h>
>>>> +#include <linux/platform_device.h>
>>>> +#include <linux/mutex.h>
>>>> +#include <linux/mfd/syscon.h>
>>>> +#include <linux/regmap.h>
>>>> +
>>>> +/* Exynos USB PHY registers */
>>>> +#define EXYNOS5_FSEL_9MHZ6           0x0
>>>> +#define EXYNOS5_FSEL_10MHZ           0x1
>>>> +#define EXYNOS5_FSEL_12MHZ           0x2
>>>> +#define EXYNOS5_FSEL_19MHZ2          0x3
>>>> +#define EXYNOS5_FSEL_20MHZ           0x4
>>>> +#define EXYNOS5_FSEL_24MHZ           0x5
>>>> +#define EXYNOS5_FSEL_50MHZ           0x7
>>>> +
>>>> +/* EXYNOS5: USB 3.0 DRD PHY registers */
>>>> +#define EXYNOS5_DRD_LINKSYSTEM                       (0x04)
>>>> +
>>>> +#define LINKSYSTEM_FLADJ_MASK                        (0x3f << 1)
>>>> +#define LINKSYSTEM_FLADJ(_x)                 ((_x) << 1)
>>>> +#define LINKSYSTEM_XHCI_VERSION_CONTROL              (0x1 << 27)
>>>> +
>>>> +#define EXYNOS5_DRD_PHYUTMI                  (0x08)
>>>> +
>>>> +#define PHYUTMI_OTGDISABLE                   (0x1 << 6)
>>>> +#define PHYUTMI_FORCESUSPEND                 (0x1 << 1)
>>>> +#define PHYUTMI_FORCESLEEP                   (0x1 << 0)
>>>
>>> use BIT macro here and below?
>>
>> Ok.
>>
>>>> +
>>>> +#define EXYNOS5_DRD_PHYPIPE                  (0x0c)
>>>> +
>>>> +#define EXYNOS5_DRD_PHYCLKRST                        (0x10)
>>>> +
>>>> +#define PHYCLKRST_EN_UTMISUSPEND             (0x1 << 31)
>>>> +
>>>> +#define PHYCLKRST_SSC_REFCLKSEL_MASK         (0xff << 23)
>>>> +#define PHYCLKRST_SSC_REFCLKSEL(_x)          ((_x) << 23)
>>>> +
>>>> +#define PHYCLKRST_SSC_RANGE_MASK             (0x03 << 21)
>>>> +#define PHYCLKRST_SSC_RANGE(_x)                      ((_x) << 21)
>>>> +
>>>> +#define PHYCLKRST_SSC_EN                     (0x1 << 20)
>>>> +#define PHYCLKRST_REF_SSP_EN                 (0x1 << 19)
>>>> +#define PHYCLKRST_REF_CLKDIV2                        (0x1 << 18)
>>>> +
>>>> +#define PHYCLKRST_MPLL_MULTIPLIER_MASK               (0x7f << 11)
>>>> +#define PHYCLKRST_MPLL_MULTIPLIER_100MHZ_REF (0x19 << 11)
>>>> +#define PHYCLKRST_MPLL_MULTIPLIER_50M_REF    (0x32 << 11)
>>>> +#define PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF  (0x68 << 11)
>>>> +#define PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF  (0x7d << 11)
>>>> +#define PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF       (0x02 << 11)
>>>> +
>>>> +#define PHYCLKRST_FSEL_MASK                  (0x3f << 5)
>>>> +#define PHYCLKRST_FSEL(_x)                   ((_x) << 5)
>>>> +#define PHYCLKRST_FSEL_PAD_100MHZ            (0x27 << 5)
>>>> +#define PHYCLKRST_FSEL_PAD_24MHZ             (0x2a << 5)
>>>> +#define PHYCLKRST_FSEL_PAD_20MHZ             (0x31 << 5)
>>>> +#define PHYCLKRST_FSEL_PAD_19_2MHZ           (0x38 << 5)
>>>> +
>>>> +#define PHYCLKRST_RETENABLEN                 (0x1 << 4)
>>>> +
>>>> +#define PHYCLKRST_REFCLKSEL_MASK             (0x03 << 2)
>>>> +#define PHYCLKRST_REFCLKSEL_PAD_REFCLK               (0x2 << 2)
>>>> +#define PHYCLKRST_REFCLKSEL_EXT_REFCLK               (0x3 << 2)
>>>> +
>>>> +#define PHYCLKRST_PORTRESET                  (0x1 << 1)
>>>> +#define PHYCLKRST_COMMONONN                  (0x1 << 0)
>>>> +
>>>> +#define EXYNOS5_DRD_PHYREG0                  (0x14)
>>>> +#define EXYNOS5_DRD_PHYREG1                  (0x18)
>>>> +
>>>> +#define EXYNOS5_DRD_PHYPARAM0                        (0x1c)
>>>> +
>>>> +#define PHYPARAM0_REF_USE_PAD                        (0x1 << 31)
>>>> +#define PHYPARAM0_REF_LOSLEVEL_MASK          (0x1f << 26)
>>>> +#define PHYPARAM0_REF_LOSLEVEL                       (0x9 << 26)
>>>> +
>>>> +#define EXYNOS5_DRD_PHYPARAM1                        (0x20)
>>>> +
>>>> +#define PHYPARAM1_PCS_TXDEEMPH_MASK          (0x1f << 0)
>>>> +#define PHYPARAM1_PCS_TXDEEMPH                       (0x1c)
>>>> +
>>>> +#define EXYNOS5_DRD_PHYTERM                  (0x24)
>>>> +
>>>> +#define EXYNOS5_DRD_PHYTEST                  (0x28)
>>>> +
>>>> +#define PHYTEST_POWERDOWN_SSP                        (0x1 << 3)
>>>> +#define PHYTEST_POWERDOWN_HSP                        (0x1 << 2)
>>>> +
>>>> +#define EXYNOS5_DRD_PHYADP                   (0x2c)
>>>> +
>>>> +#define EXYNOS5_DRD_PHYBATCHG                        (0x30)
>>>> +
>>>> +#define PHYBATCHG_UTMI_CLKSEL                        (0x1 << 2)
>>>> +
>>>> +#define EXYNOS5_DRD_PHYRESUME                        (0x34)
>>>> +#define EXYNOS5_DRD_LINKPORT                 (0x44)
>>>> +
>>>> +/* Power isolation defined in power management unit */
>>>> +#define EXYNOS5_USB3DRD_PHY_PMU_REG_OFFSET   (0x704)
>>>> +#define EXYNOS5_USB3DRD_PMU_ISOL             (1 << 0)
>>>> +
>>>> +#define KHZ  1000
>>>> +#define MHZ  (KHZ * KHZ)
>>>> +
>>>> +enum exynos5_usb3phy_id {
>>>> +     EXYNOS5_USB3PHY_UTMI,
>>>> +     EXYNOS5_USB3PHY_PIPE3,
>>>> +     EXYNOS5_USB3PHYS_NUM,
>>>> +};
>
> here and all the structure names below shouldn't have usb3 in their names since
> this is not just a 'usb3' phy driver..

right, will change the nomenclature driver-wide (including function names too).

>>>> +
>>>> +struct usb3phy_config {
>>>> +     u32 id;
>>>> +     u32 reg_pmu_offset;
>>>> +     void (*phy_isol)(struct phy *phy, u32 on);
>>>> +};
>>>> +
>>>> +struct exynos5_usb3phy_drv_data {
>>>> +     bool has_usb30_sclk;
>>>> +     bool has_multi_controller;
>>>> +     const struct usb3phy_config *phy_cfg;
>>>> +};
>>>> +
>>>> +/**
>>>> + * struct exynos5_usb3phy_driver - driver data for USB 3.0 PHY
>>>
>>> Is this really a driver data? I think it should be just exynos5_usb3phy.
>> Yes, not a driver data, rather just 'exynos_usb3phy' structure. will
>> modify the name
>>
>>>> + * @dev: pointer to device instance of this platform device
>>>> + * @reg_phy: usb phy controller register memory base
>>>> + * @clk: phy clock for register access
>>>> + * @usb30_sclk: additional special clock for phy operations
>>>> + * @drv_data: pointer to SoC level driver data structure
>>>> + * @phys[]: array for 'EXYNOS5_USB3PHYS_NUM' number of PHY
>>>> + *       instances each with its 'phy' and 'phy_cfg'.
>>>> + * @extrefclk: frequency select settings when using 'separate
>>>> + *          reference clocks' for SS and HS operations
>>>> + * @rate: rate of reference clock to PHY block
>>>> + * @channel: number of PHY channels present in SoC
>>>> + */
>>>> +struct exynos5_usb3phy_driver {
>>>> +     struct device *dev;
>>>> +     void __iomem *reg_phy;
>>>> +     struct clk *clk;
>>>> +     struct clk *usb30_sclk;
>>>> +     const struct exynos5_usb3phy_drv_data *drv_data;
>>>> +     struct phy_usb_instance {
>>>> +             struct phy *phy;
>>>> +             u32 index;
>>>> +             struct regmap *reg_isol;
>>>> +             const struct usb3phy_config *phy_cfg;
>>>> +     } phys[EXYNOS5_USB3PHYS_NUM];
>>>> +     u32 extrefclk;
>>>> +     unsigned long rate;
>>>> +     u32 channel;
>>>> +};
>>>> +
>>>> +#define to_usb3phy_driver(inst) \
>>>> +     container_of((inst), struct exynos5_usb3phy_driver, \
>>>> +                  phys[(inst)->index]);
>>>> +
>>>> +/*
>>>> + * exynos5_rate_to_clk() converts the supplied clock rate to the value that
>>>> + * can be written to the phy register.
>>>> + */
>>>> +static u32 exynos5_rate_to_clk(unsigned long rate)
>>>> +{
>>>> +     unsigned int clksel;
>>>> +
>>>> +     /* EXYNOS5_FSEL_MASK */
>>>> +
>>>> +     switch (rate) {
>>>> +     case 9600 * KHZ:
>>>> +             clksel = EXYNOS5_FSEL_9MHZ6;
>>>> +             break;
>>>> +     case 10 * MHZ:
>>>> +             clksel = EXYNOS5_FSEL_10MHZ;
>>>> +             break;
>>>> +     case 12 * MHZ:
>>>> +             clksel = EXYNOS5_FSEL_12MHZ;
>>>> +             break;
>>>> +     case 19200 * KHZ:
>>>> +             clksel = EXYNOS5_FSEL_19MHZ2;
>>>> +             break;
>>>> +     case 20 * MHZ:
>>>> +             clksel = EXYNOS5_FSEL_20MHZ;
>>>> +             break;
>>>> +     case 24 * MHZ:
>>>> +             clksel = EXYNOS5_FSEL_24MHZ;
>>>> +             break;
>>>> +     case 50 * MHZ:
>>>> +             clksel = EXYNOS5_FSEL_50MHZ;
>>>> +             break;
>>>> +     default:
>>>> +             clksel = -EINVAL;
>>>> +     }
>>>> +
>>>> +     return clksel;
>>>> +}
>>>> +
>>>> +static void exynos5_usb3phy_isol(struct phy *phy, unsigned int on)
>>>> +{
>>>> +     u32 pmu_offset;
>>>> +     struct phy_usb_instance *inst = phy_get_drvdata(phy);
>>>> +     struct exynos5_usb3phy_driver *drv = to_usb3phy_driver(inst);
>>>> +
>>>> +     pmu_offset = inst->phy_cfg->reg_pmu_offset;
>>>> +     if (!inst->reg_isol)
>>>> +             return;
>>>> +
>>>> +     switch (drv->channel) {
>>>> +     case 1:
>>>> +             /* Channel 1 is at 0x708 offset */
>>>> +             pmu_offset += sizeof(&pmu_offset);
>>>> +             break;
>>>> +     case 0:
>>>> +     default:
>>>> +             /* Channel 0 is at 0x704 offset */
>>>> +             break;
>>>> +     }
>>>
>>> This can be in a simple 'if' stmt no?
>> What if there are systems with more channels? In that case also we
>> will have to fall back to a switch-case statement ?
>
> right.
>>
>>>> +
>>>> +     regmap_update_bits(inst->reg_isol, pmu_offset,
>>>> +                        EXYNOS5_USB3DRD_PMU_ISOL, ~on);
>>>> +}
>>>> +
>>>> +/*
>>>> + * Sets the phy clk as EXTREFCLK (XXTI) which is internal clock from clock core.
>>>> + */
>>>> +static u32 exynos5_usb3phy_set_refclk(struct exynos5_usb3phy_driver *drv)
>>>> +{
>>>> +     u32 reg;
>>>> +
>>>> +     reg = PHYCLKRST_REFCLKSEL_EXT_REFCLK |
>>>> +           PHYCLKRST_FSEL(drv->extrefclk);
>>>> +
>>>> +     switch (drv->extrefclk) {
>>>> +     case EXYNOS5_FSEL_50MHZ:
>>>> +             reg |= (PHYCLKRST_MPLL_MULTIPLIER_50M_REF |
>>>> +                     PHYCLKRST_SSC_REFCLKSEL(0x00));
>>>> +             break;
>>>> +     case EXYNOS5_FSEL_24MHZ:
>>>> +             reg |= (PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF |
>>>> +                     PHYCLKRST_SSC_REFCLKSEL(0x88));
>>>> +             break;
>>>> +     case EXYNOS5_FSEL_20MHZ:
>>>> +             reg |= (PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF |
>>>> +                     PHYCLKRST_SSC_REFCLKSEL(0x00));
>>>> +             break;
>>>> +     case EXYNOS5_FSEL_19MHZ2:
>>>> +             reg |= (PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF |
>>>> +                     PHYCLKRST_SSC_REFCLKSEL(0x88));
>>>> +             break;
>>>> +     default:
>>>> +             dev_dbg(drv->dev, "unsupported ref clk\n");
>>>> +             break;
>>>> +     }
>>>> +
>>>> +     return reg;
>>>> +}
>>>> +
>>>> +static int exynos5_usb3phy_init(struct phy *phy)
>>>> +{
>>>> +     int ret;
>>>> +     u32 phyparam0;
>>>> +     u32 phyparam1;
>>>> +     u32 linksystem;
>>>> +     u32 phybatchg;
>>>> +     u32 phytest;
>>>> +     u32 phyclkrst;
>>>
>>> instead you can define a single variable 'u32 reg' for register read and writes.
>>
>> Right.
>>
>>>> +     struct phy_usb_instance *inst = phy_get_drvdata(phy);
>>>> +     struct exynos5_usb3phy_driver *drv = to_usb3phy_driver(inst);
>>>> +
>>>> +     ret = clk_prepare_enable(drv->clk);
>>>> +     if (ret)
>>>> +             return ret;
>>>> +
>>>> +     drv->extrefclk = exynos5_rate_to_clk(drv->rate);
>>>> +     if (drv->extrefclk == -EINVAL) {
>>>> +             dev_err(drv->dev, "Clock rate (%ld) not supported\n",
>>>> +                                             drv->rate);
>>>> +             return -EINVAL;
>>>> +     }
>>>> +
>>>> +     /* Reset USB 3.0 PHY */
>>>> +     writel(0x0, drv->reg_phy + EXYNOS5_DRD_PHYREG0);
>>>> +
>>>> +     phyparam0 = readl(drv->reg_phy + EXYNOS5_DRD_PHYPARAM0);
>>>> +     /* Select PHY CLK source */
>>>> +     phyparam0 &= ~PHYPARAM0_REF_USE_PAD;
>>>> +     /* Set Loss-of-Signal Detector sensitivity */
>>>> +     phyparam0 &= ~PHYPARAM0_REF_LOSLEVEL_MASK;
>>>> +     phyparam0 |= PHYPARAM0_REF_LOSLEVEL;
>>>> +     writel(phyparam0, drv->reg_phy + EXYNOS5_DRD_PHYPARAM0);
>>>> +
>>>> +     writel(0x0, drv->reg_phy + EXYNOS5_DRD_PHYRESUME);
>>>> +
>>>> +     /*
>>>> +      * Setting the Frame length Adj value[6:1] to default 0x20
>>>> +      * See xHCI 1.0 spec, 5.2.4
>>>> +      */
>>>> +     linksystem = LINKSYSTEM_XHCI_VERSION_CONTROL |
>>>> +                  LINKSYSTEM_FLADJ(0x20);
>>>> +     writel(linksystem, drv->reg_phy + EXYNOS5_DRD_LINKSYSTEM);
>>>> +
>>>> +     phyparam1 = readl(drv->reg_phy + EXYNOS5_DRD_PHYPARAM1);
>>>> +     /* Set Tx De-Emphasis level */
>>>> +     phyparam1 &= ~PHYPARAM1_PCS_TXDEEMPH_MASK;
>>>> +     phyparam1 |= PHYPARAM1_PCS_TXDEEMPH;
>>>> +     writel(phyparam1, drv->reg_phy + EXYNOS5_DRD_PHYPARAM1);
>>>> +
>>>> +     phybatchg = readl(drv->reg_phy + EXYNOS5_DRD_PHYBATCHG);
>>>> +     phybatchg |= PHYBATCHG_UTMI_CLKSEL;
>>>> +     writel(phybatchg, drv->reg_phy + EXYNOS5_DRD_PHYBATCHG);
>>>> +
>>>> +     /* PHYTEST POWERDOWN Control */
>>>> +     phytest = readl(drv->reg_phy + EXYNOS5_DRD_PHYTEST);
>>>> +     phytest &= ~(PHYTEST_POWERDOWN_SSP |
>>>> +                  PHYTEST_POWERDOWN_HSP);
>>>> +     writel(phytest, drv->reg_phy + EXYNOS5_DRD_PHYTEST);
>>>> +
>>>> +     /* UTMI Power Control */
>>>> +     writel(PHYUTMI_OTGDISABLE, drv->reg_phy + EXYNOS5_DRD_PHYUTMI);
>>>
>>> All these UTMI configuration should be done in usb2 init.
>> Ok, will move this to separate function.
>>
>>>> +
>>>> +     phyclkrst = exynos5_usb3phy_set_refclk(drv);
>>>> +
>>>> +     phyclkrst |= PHYCLKRST_PORTRESET |
>>>> +                  /* Digital power supply in normal operating mode */
>>>> +                  PHYCLKRST_RETENABLEN |
>>>> +                  /* Enable ref clock for SS function */
>>>> +                  PHYCLKRST_REF_SSP_EN |
>>>> +                  /* Enable spread spectrum */
>>>> +                  PHYCLKRST_SSC_EN |
>>>> +                  /* Power down HS Bias and PLL blocks in suspend mode */
>>>> +                  PHYCLKRST_COMMONONN;
>>>> +
>>>> +     writel(phyclkrst, drv->reg_phy + EXYNOS5_DRD_PHYCLKRST);
>>>> +
>>>> +     udelay(10);
>>>> +
>>>> +     phyclkrst &= ~PHYCLKRST_PORTRESET;
>>>> +     writel(phyclkrst, drv->reg_phy + EXYNOS5_DRD_PHYCLKRST);
>>>> +
>>>> +     clk_disable_unprepare(drv->clk);
>>>> +
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static int exynos5_usb3phy_exit(struct phy *phy)
>>>> +{
>>>> +     int ret;
>>>> +     u32 phyutmi;
>>>> +     u32 phyclkrst;
>>>> +     u32 phytest;
>>>
>>> same here..
>> right, will do it.
>>
>>>> +     struct phy_usb_instance *inst = phy_get_drvdata(phy);
>>>> +     struct exynos5_usb3phy_driver *drv = to_usb3phy_driver(inst);
>>>> +
>>>> +     ret = clk_prepare_enable(drv->clk);
>>>> +     if (ret)
>>>> +             return ret;
>>>> +
>>>> +     phyutmi = PHYUTMI_OTGDISABLE |
>>>> +               PHYUTMI_FORCESUSPEND |
>>>> +               PHYUTMI_FORCESLEEP;
>>>> +     writel(phyutmi, drv->reg_phy + EXYNOS5_DRD_PHYUTMI);
>>>
>>> here too.. UTMI configuration should be part of USB2.
>> ok.
>>
>>>> +
>> [snip]
>>
>>>> +
>>>> +static struct phy_ops exynos5_usb3phy_ops = {
>>>> +     .init           = exynos5_usb3phy_init,
>>>> +     .exit           = exynos5_usb3phy_exit,
>>>> +     .power_on       = exynos5_usb3phy_power_on,
>>>> +     .power_off      = exynos5_usb3phy_power_off,
>>>> +     .owner          = THIS_MODULE,
>>>> +};
>>>> +
>>>> +const struct usb3phy_config exynos5_usb3phy_cfg[] = {
>>>> +     {
>>>> +             .id             = EXYNOS5_USB3PHY_UTMI,
>>>
>>> This should be USB2 no?
>> Actually the thought was to have similar naming for enums.
>> EXYNOS5_USB3PHY_UTMI
>> EXYNOS5_USB3PHY_PIPE3
>>
>> Since the entire driver was going that way.
>> But will change these to a more common name
>> EXYNOS5_DRDPHY_UTMI
>> EXYNOS5_DRDPHY_PIPE3,
>> in the same fashion the register names are defined.
>> Will that be fine ?
>
> Yeah.
>
> Thanks
> Kishon



-- 
Best Regards
Vivek Gautam
Samsung R&D Institute, Bangalore
India

^ permalink raw reply

* Re: Re: [RFC PATCH v2 09/14] mtd: nand: add sunxi NFC dt bindings doc
From: boris brezillon dev @ 2014-01-30  8:38 UTC (permalink / raw)
  To: Henrik Nordström, linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
  Cc: Maxime Ripard, Rob Landley, Russell King, David Woodhouse,
	Grant Likely, Brian Norris, Jason Gunthorpe, Arnd Bergmann,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-doc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	dev-3kdeTeqwOZ9EV1b7eY7vFQ, Rob Herring
In-Reply-To: <1391035079.14902.3.camel@localhost>

Hello Henrik,

On 29/01/2014 23:37, Henrik Nordström wrote:
> ons 2014-01-29 klockan 11:11 -0600 skrev Rob Herring:
>
>> Isn't allwinner,rb implied by a lack of rb-gpios property. Or no R/B
>> pin is an option? If so, don't you need some fixed time delay
>> properties like max erase time?
>>
>> rb-gpios could be added to the generic nand binding as well.
> The Allwinner NAND controller have dedicated RB pins when NAND is
> enabled, only MUXed with other functions when NAND is not enabled.
>
> Leaving RB unconnected is not a valid hardware configuration. The
> controller internal timing engine depends on being able to sense RB to
> sequence NAND commands properly.

This is not true (at least in this driver). It was in yuq's driver because
he was using the NFC_WAIT_FLAG ,and in this case the controller wait
for the native R/B pin to be high before considering the CMD is complete.

This driver choose the appropriate way to test the R/B state of the
NAND chip according to what was specified in the DT:
- allwinner,rb: native R/B id. These pins will be used by the NAND
   controller to test the R/B state. Only 0 and 1 are valid because the
   NAND controller only support 2 R/B pins.
- rb-gpios: gpio used for R/B tests. This is a simple GPIO and will
   use the GPIO subsystem to test the R/B pin state.
- none: the NAND base code will wait some time before and send
   STATUS cmd to the NAND to check its status.

BTW, the controller supports 8 CS (8 NAND chips), but only have 2 native
R/B pins, this means you'll have to use the GPIO or standard GET_STATUS
method if you connect 3 or more NAND chips.

And for the record, I still think the rb-gpios property (or whatever
common name you choose: nand-rb-gpios ?) should be part of
the generic NAND binding, because other controllers (at least the
atmel one :)) use GPIOs to test R/B state.

Best Regards,

Boris

>
> Regards
> Henrik
>

-- 
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/groups/opt_out.

^ permalink raw reply

* Re: Re: [RFC PATCH v2 09/14] mtd: nand: add sunxi NFC dt bindings doc
From: boris brezillon dev @ 2014-01-30  8:46 UTC (permalink / raw)
  To: Henrik Nordström, linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
  Cc: Maxime Ripard, Rob Landley, Russell King, David Woodhouse,
	Grant Likely, Brian Norris, Jason Gunthorpe, Arnd Bergmann,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-doc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	dev-3kdeTeqwOZ9EV1b7eY7vFQ, Rob Herring
In-Reply-To: <1391035079.14902.3.camel@localhost>

Hello Henrik,

Sorry for the noise, I sent the mail to Rob's old address.

On 29/01/2014 23:37, Henrik Nordström wrote:
> ons 2014-01-29 klockan 11:11 -0600 skrev Rob Herring:
>
>> Isn't allwinner,rb implied by a lack of rb-gpios property. Or no R/B
>> pin is an option? If so, don't you need some fixed time delay
>> properties like max erase time?
>>
>> rb-gpios could be added to the generic nand binding as well.
> The Allwinner NAND controller have dedicated RB pins when NAND is
> enabled, only MUXed with other functions when NAND is not enabled.
>
> Leaving RB unconnected is not a valid hardware configuration. The
> controller internal timing engine depends on being able to sense RB to
> sequence NAND commands properly.

This is not true (at least in this driver). It was in yuq's driver because
he was using the NFC_WAIT_FLAG ,and in this case the controller wait
for the native R/B pin to be high before considering the CMD is complete.

This driver choose the appropriate way to test the R/B state of the
NAND chip according to what was specified in the DT:
- allwinner,rb: native R/B id. These pins will be used by the NAND
   controller to test the R/B state. Only 0 and 1 are valid because the
   NAND controller only support 2 R/B pins.
- rb-gpios: gpio used for R/B tests. This is a simple GPIO and will
   use the GPIO subsystem to test the R/B pin state.
- none: the NAND base code will wait some time before and send
   STATUS cmd to the NAND to check its status.

BTW, the controller supports 8 CS (8 NAND chips), but only have 2 native
R/B pins, this means you'll have to use the GPIO or standard GET_STATUS
method if you connect 3 or more NAND chips.

And for the record, I still think the rb-gpios property (or whatever
common name you choose: nand-rb-gpios ?) should be part of
the generic NAND binding, because other controllers (at least the
atmel one :)) use GPIOs to test R/B state.

Best Regards,

Boris

>
> Regards
> Henrik
>

-- 
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/groups/opt_out.

^ permalink raw reply

* Re: Re: [RFC PATCH v2 08/14] mtd: nand: add sunxi NAND flash controller support
From: Boris BREZILLON @ 2014-01-30  8:57 UTC (permalink / raw)
  To: linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Ezequiel Garcia
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Russell King, Arnd Bergmann,
	linux-doc-u79uwXL29TY76Z2rM5mHXA, dev-3kdeTeqwOZ9EV1b7eY7vFQ,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Rob Landley,
	Grant Likely, Maxime Ripard, Brian Norris, David Woodhouse,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20140129191005.GG1427-ePGOBjL8dl3ta4EC/59zMFaTQe2KTcn/@public.gmane.org>

On 29/01/2014 20:10, Jason Gunthorpe wrote:
> On Wed, Jan 29, 2014 at 03:46:20PM -0300, Ezequiel Garcia wrote:
>
>> After CE# has been pulled high and then transitioned low again, the host
>> should issue a Set Features to select the appropriate asynchronous timing mode.
>> """
> Oh, I had forgot you should do a set feature too
>
> Boris, I think the core core should handle this dance and the driver
> should just implement a call back to change the timing mode on the
> interface..
>
> If I ever get a moment I can work on support for timing setting in the
> mvebu driver, I have boards here with ONFI NAND..

Okay, I'll wait :).

Thanks.

>
> Regards,
> Jason
>

^ permalink raw reply

* recommended action for bootloaders regarding modifying device-tree nodes
From: Tim Harvey @ 2014-01-30  9:11 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	u-boot-0aAXYlwwYIKGBzrmiIFOJg,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	Grant Likely

Greetings,

I develop the boot-loader and kernel for a family of boards that have
an on-board EEPROM which contains information as to what options are
physically loaded on the board such as memory size/config, and
peripheral IC's.  We allow customers to create special builds of our
standard products with sub-loaded components and while each
combination of options ends up with a unique model number, it seems
silly to create a different static device-trees for each possible
option (not to mention we don't create the unique model number until
an order is placed).

My approach has been to define a per-baseboard device-tree in Linux
for a 'fully loaded' board, then remove nodes which the EEPROM claims
are not present in the bootloader before it passes the DTB to the
kernel.  I do this by defining aliases in the device-tree for the
peripherals that are 'optional' so that the bootloader itself does not
need to know the details about how the device is connected.

Is it more appropriate for the bootloader to 'remove' nodes for
devices that are not physically present or should I be setting their
status property to 'disabled' instead?  I'm not clear if either option
really has any pros or cons.

Thanks for any suggestions or comments,

Tim

Tim Harvey - Principal Software Engineer
Gateworks Corporation
3026 S. Higuera St. San Luis Obispo CA 93401
805-781-2000
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [U-Boot] recommended action for bootloaders regarding modifying device-tree nodes
From: Michal Suchanek @ 2014-01-30  9:22 UTC (permalink / raw)
  To: Tim Harvey
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, uboot,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	Grant Likely
In-Reply-To: <CAJ+vNU366GiY4k_rnh1Jf0kra+PU99w4tDdM2sNioUAJCiZqOA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

Hello,

On 30 January 2014 10:11, Tim Harvey <tharvey-UMMOYl/HMS+akBO8gow8eQ@public.gmane.org> wrote:
> Greetings,
>
>
> Is it more appropriate for the bootloader to 'remove' nodes for
> devices that are not physically present or should I be setting their
> status property to 'disabled' instead?  I'm not clear if either option
> really has any pros or cons.
>

I am not a DT or u-boot developer but I observe these things:

1) DT include for a SoC has all supported devices. Some which require
external parts (eg. physical connectors, extra PHY, ...) are listed as
disabled and particular board DT enables them.

2) there is code for setting mac address on ethernet nodes with a
specific alias so you could perhaps reuse this code to set disabled
option on particular nodes.

Thanks

Michal
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH v2 3/5] ASoC: tda998x: add DT documentation
From: Jean-Francois Moine @ 2014-01-30 10:32 UTC (permalink / raw)
  To: alsa-devel, devicetree
  Cc: Russell King - ARM Linux, linux-kernel, dri-devel, broonie,
	linux-arm-kernel
In-Reply-To: <cover.1391081933.git.moinejf@free.fr>

This patch adds the DT documentation of the NXP TDA998x CODEC.

Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
---
 Documentation/devicetree/bindings/sound/tda998x.txt | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/tda998x.txt

diff --git a/Documentation/devicetree/bindings/sound/tda998x.txt b/Documentation/devicetree/bindings/sound/tda998x.txt
new file mode 100644
index 0000000..13b73ca
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/tda998x.txt
@@ -0,0 +1,16 @@
+Device-Tree bindings for the NXP TDA998x HDMI transmitter
+
+Required properties:
+	- compatible: must be "nxp,tda998x-codec".
+	- audio-ports: one or two values corresponding to entries in
+		the audio-port-names property.
+	- audio-port-names: must contain "i2s", "spdif" entries
+		matching entries in the audio-ports property.
+
+Example node:
+
+	hdmi_codec: hdmi-codec {
+		compatible = "nxp,tda998x-codec";
+		audio-ports = <0x03>, <0x04>;
+		audio-port-names = "i2s", "spdif";
+	};
-- 
1.9.rc1

^ permalink raw reply related

* [PATCH V3 0/8] PCI:Add SPEAr13xx PCie support
From: Mohit Kumar @ 2014-01-30 10:48 UTC (permalink / raw)
  To: arnd
  Cc: Mohit Kumar, linux-arm-kernel, devicetree, linux-ide, linux-pci,
	spear-devel, Tejun Heo

First three patches are improvement and fixes for SPEAr13xx support.
Next patch modify phy-core driver for its initialization during
subsys_initcall(). Platform ahci driver is modified for phy hooks.
Patch#6 and 7 modify/add support for SPEAr13xx SATA and PCIe.

These patches are tested with SPEAr1310 evaluation board:
	- INTEL PRO 100/100 EP card
	- USB xhci gen2 card
 	- Above cards connected through LeCROY PTC switch

Modifications for SATA are tested with SPEAr1340-evb board

Changes since v2:
- Incorporated comments to move SPEAr13xx PCIe and SATA phy specific routines to
  the phy framework
- Modify ahci driver to include phy hooks
- phy-core driver modifications for subsys_initcall() 
 
Changes since v1:
- Few patches of the series are already accepted and applied to mainline e.g.
 pcie designware driver improvements,fixes for IO translation bug, PCIe dw
 driver maintainer. So dropped these from v2.
- Incorporated comment to move the common/reset PCIe code to the seperate driver
- PCIe and SATA share common PHY configuration registers, so move SATA
 platform code to the system config driver
Fourth patch is improves pcie designware driver and fixes the IO
translation bug. IO translation bug fix leads to the working of PCIe EP devices
connected to RC through switch.

PCIe driver support for SPEAr1310/40 platform board is added.

These patches are tested with SPEAr1310 evaluation board:
	- INTEL PRO 100/100 EP card
	- USB xhci gen2 card
 	- Above cards connected through LeCROY PTC switch

Mohit Kumar (2):
  SPEAr13xx: defconfig: Update
  MAINTAINERS: Add ST SPEAr13xx PCIe driver maintainer

Pratyush Anand (6):
  clk: SPEAr13xx: Fix pcie clock name
  SPEAr13xx: Fix static mapping table
  phy: Initialize phy core with subsys_initcall
  ata: ahci platform: Add phy hooks to make it more generic
  SPEAr13xx: Fixup: Move SPEAr1340 SATA platform code to phy driver
  pcie: SPEAr13xx: Add designware pcie support

Cc: linux-arm-kernel@lists.infradead.org
Cc: devicetree@vger.kernel.org
Cc: linux-ide@vger.kernel.org
Cc: linux-pci@vger.kernel.org
Cc: spear-devel@list.st.com
Cc: Tejun Heo <tj@kernel.org>

 .../devicetree/bindings/ata/ahci-platform.txt      |    2 +
 .../devicetree/bindings/pci/spear13xx-pcie.txt     |    7 +
 .../devicetree/bindings/phy/spear13xx-miphy.txt    |    8 +
 MAINTAINERS                                        |    6 +
 arch/arm/boot/dts/spear1310-evb.dts                |    4 +
 arch/arm/boot/dts/spear1310.dtsi                   |   87 ++++-
 arch/arm/boot/dts/spear1340-evb.dts                |    4 +
 arch/arm/boot/dts/spear1340.dtsi                   |   29 ++-
 arch/arm/boot/dts/spear13xx.dtsi                   |   10 +-
 arch/arm/configs/spear13xx_defconfig               |   16 +
 arch/arm/mach-spear/Kconfig                        |    3 +
 arch/arm/mach-spear/include/mach/spear.h           |    4 +-
 arch/arm/mach-spear/spear1340.c                    |  127 +-----
 arch/arm/mach-spear/spear13xx.c                    |    2 +-
 drivers/ata/ahci.h                                 |    2 +
 drivers/ata/ahci_platform.c                        |   20 +
 drivers/clk/spear/spear1310_clock.c                |    6 +-
 drivers/clk/spear/spear1340_clock.c                |    2 +-
 drivers/pci/host/Kconfig                           |    5 +
 drivers/pci/host/Makefile                          |    1 +
 drivers/pci/host/pcie-spear13xx.c                  |  407 +++++++++++++++++
 drivers/phy/Kconfig                                |    6 +
 drivers/phy/Makefile                               |    1 +
 drivers/phy/phy-core.c                             |    2 +-
 drivers/phy/phy-spear13xx-sata-pcie.c              |  481 ++++++++++++++++++++
 25 files changed, 1102 insertions(+), 140 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/pci/spear13xx-pcie.txt
 create mode 100644 Documentation/devicetree/bindings/phy/spear13xx-miphy.txt
 create mode 100644 drivers/pci/host/pcie-spear13xx.c
 create mode 100644 drivers/phy/phy-spear13xx-sata-pcie.c

^ permalink raw reply

* [PATCH V3 6/8] SPEAr13xx: Fixup: Move SPEAr1340 SATA platform code to phy driver
From: Mohit Kumar @ 2014-01-30 10:48 UTC (permalink / raw)
  To: arnd-r2nGTMty4D4
  Cc: Pratyush Anand, Viresh Kumar, Tejun Heo, Kishon Vijay Abraham I,
	spear-devel-nkJGhpqTU55BDgjK7y7TUQ,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-ide-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <cover.1391077731.git.mohit.kumar-qxv4g6HH51o@public.gmane.org>

From: Pratyush Anand <pratyush.anand-qxv4g6HH51o@public.gmane.org>

ahci driver needs some platform specific functions which are called at
init, exit, suspend and resume conditions. Till now these functions were
present in a platform driver with a fixme notes.

Similar functions modifying same set of registers will also be needed in
case of PCIe phy init/exit.

So move all these SATA platform code to a proper phy driver.

Same phy driver will be used to add PCIe init/exit routine.

Signed-off-by: Pratyush Anand <pratyush.anand-qxv4g6HH51o@public.gmane.org>
Tested-by: Mohit Kumar <mohit.kumar-qxv4g6HH51o@public.gmane.org>
Cc: Viresh Kumar <viresh.linux-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Cc: Tejun Heo <tj-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Cc: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
Cc: Kishon Vijay Abraham I <kishon-l0cyMroinI0@public.gmane.org>
Cc: spear-devel-nkJGhpqTU55BDgjK7y7TUQ@public.gmane.org
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: linux-ide-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
---
 .../devicetree/bindings/phy/spear13xx-miphy.txt    |    8 +
 arch/arm/boot/dts/spear1310-evb.dts                |    4 +
 arch/arm/boot/dts/spear1310.dtsi                   |   36 +++-
 arch/arm/boot/dts/spear1340-evb.dts                |    4 +
 arch/arm/boot/dts/spear1340.dtsi                   |   12 +-
 arch/arm/boot/dts/spear13xx.dtsi                   |    5 +
 arch/arm/mach-spear/Kconfig                        |    2 +
 arch/arm/mach-spear/spear1340.c                    |  127 +--------
 drivers/phy/Kconfig                                |    6 +
 drivers/phy/Makefile                               |    1 +
 drivers/phy/phy-spear13xx-sata-pcie.c              |  305 ++++++++++++++++++++
 11 files changed, 380 insertions(+), 130 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/phy/spear13xx-miphy.txt
 create mode 100644 drivers/phy/phy-spear13xx-sata-pcie.c

diff --git a/Documentation/devicetree/bindings/phy/spear13xx-miphy.txt b/Documentation/devicetree/bindings/phy/spear13xx-miphy.txt
new file mode 100644
index 0000000..208b37d
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/spear13xx-miphy.txt
@@ -0,0 +1,8 @@
+Required properties:
+- compatible : should be "st,spear1340-sata-pcie-phy".
+- reg : offset and length of the PHY register set.
+- misc: phandle for the syscon node to access misc registers
+- #phy-cells : from the generic PHY bindings, must be 2.
+	- 1st arg: phandle to the phy node.
+	- 2nd arg: 0 if phy (in 1st arg) is to be used for sata else 1.
+	- 3rd arg: Instance id of the phy (in 1st arg).
diff --git a/arch/arm/boot/dts/spear1310-evb.dts b/arch/arm/boot/dts/spear1310-evb.dts
index b56a801..d42c84b 100644
--- a/arch/arm/boot/dts/spear1310-evb.dts
+++ b/arch/arm/boot/dts/spear1310-evb.dts
@@ -106,6 +106,10 @@
 			status = "okay";
 		};
 
+		miphy@eb800000 {
+			status = "okay";
+		};
+
 		cf@b2800000 {
 			status = "okay";
 		};
diff --git a/arch/arm/boot/dts/spear1310.dtsi b/arch/arm/boot/dts/spear1310.dtsi
index 122ae94..0d62418 100644
--- a/arch/arm/boot/dts/spear1310.dtsi
+++ b/arch/arm/boot/dts/spear1310.dtsi
@@ -29,24 +29,54 @@
 			#gpio-cells = <2>;
 		};
 
-		ahci@b1000000 {
+		miphy0: miphy@eb800000 {
+			compatible = "st,spear1340-sata-pcie-phy";
+			reg = <0xeb800000 0x4000>;
+			misc = <&misc>;
+			#phy-cells = <2>;
+			status = "disabled";
+		};
+
+		miphy1: miphy@eb804000 {
+			compatible = "st,spear1340-sata-pcie-phy";
+			reg = <0xeb804000 0x4000>;
+			misc = <&misc>;
+			#phy-cells = <2>;
+			status = "disabled";
+		};
+
+		miphy2: miphy@eb808000 {
+			compatible = "st,spear1340-sata-pcie-phy";
+			reg = <0xeb808000 0x4000>;
+			misc = <&misc>;
+			#phy-cells = <2>;
+			status = "disabled";
+		};
+
+		ahci0: ahci@b1000000 {
 			compatible = "snps,spear-ahci";
 			reg = <0xb1000000 0x10000>;
 			interrupts = <0 68 0x4>;
+			phys = <&miphy0 0 0>;
+			phy-names = "ahci-phy";
 			status = "disabled";
 		};
 
-		ahci@b1800000 {
+		ahci1: ahci@b1800000 {
 			compatible = "snps,spear-ahci";
 			reg = <0xb1800000 0x10000>;
 			interrupts = <0 69 0x4>;
+			phys = <&miphy1 0 1>;
+			phy-names = "ahci-phy";
 			status = "disabled";
 		};
 
-		ahci@b4000000 {
+		ahci2: ahci@b4000000 {
 			compatible = "snps,spear-ahci";
 			reg = <0xb4000000 0x10000>;
 			interrupts = <0 70 0x4>;
+			phys = <&miphy2 0 2>;
+			phy-names = "ahci-phy";
 			status = "disabled";
 		};
 
diff --git a/arch/arm/boot/dts/spear1340-evb.dts b/arch/arm/boot/dts/spear1340-evb.dts
index d6c30ae..b23e05e 100644
--- a/arch/arm/boot/dts/spear1340-evb.dts
+++ b/arch/arm/boot/dts/spear1340-evb.dts
@@ -122,6 +122,10 @@
 			status = "okay";
 		};
 
+		miphy@eb800000 {
+			status = "okay";
+		};
+
 		dma@ea800000 {
 			status = "okay";
 		};
diff --git a/arch/arm/boot/dts/spear1340.dtsi b/arch/arm/boot/dts/spear1340.dtsi
index 54d128d..c6b0e34 100644
--- a/arch/arm/boot/dts/spear1340.dtsi
+++ b/arch/arm/boot/dts/spear1340.dtsi
@@ -31,10 +31,20 @@
 			status = "disabled";
 		};
 
-		ahci@b1000000 {
+		miphy0: miphy@eb800000 {
+			compatible = "st,spear1340-sata-pcie-phy";
+			reg = <0xeb800000 0x4000>;
+			misc = <&misc>;
+			#phy-cells = <2>;
+			status = "disabled";
+		};
+
+		ahci0: ahci@b1000000 {
 			compatible = "snps,spear-ahci";
 			reg = <0xb1000000 0x10000>;
 			interrupts = <0 72 0x4>;
+			phys = <&miphy0 0 0>;
+			phy-names = "ahci-phy";
 			status = "disabled";
 		};
 
diff --git a/arch/arm/boot/dts/spear13xx.dtsi b/arch/arm/boot/dts/spear13xx.dtsi
index 4382547..3a72508 100644
--- a/arch/arm/boot/dts/spear13xx.dtsi
+++ b/arch/arm/boot/dts/spear13xx.dtsi
@@ -220,6 +220,11 @@
 				  0xd8000000 0xd8000000 0x01000000
 				  0xe0000000 0xe0000000 0x10000000>;
 
+			misc: syscon@e0700000 {
+				compatible = "st,spear1340-misc", "syscon";
+				reg = <0xe0700000 0x1000>;
+			};
+
 			gpio0: gpio@e0600000 {
 				compatible = "arm,pl061", "arm,primecell";
 				reg = <0xe0600000 0x1000>;
diff --git a/arch/arm/mach-spear/Kconfig b/arch/arm/mach-spear/Kconfig
index ac1710e..44d8543 100644
--- a/arch/arm/mach-spear/Kconfig
+++ b/arch/arm/mach-spear/Kconfig
@@ -26,6 +26,8 @@ config ARCH_SPEAR13XX
 	select MIGHT_HAVE_CACHE_L2X0
 	select PINCTRL
 	select USE_OF
+	select MFD_SYSCON
+	select PHY_SPEAR13XX_SATA_PCIE
 	help
 	  Supports for ARM's SPEAR13XX family
 
diff --git a/arch/arm/mach-spear/spear1340.c b/arch/arm/mach-spear/spear1340.c
index 3fb6834..8e27093 100644
--- a/arch/arm/mach-spear/spear1340.c
+++ b/arch/arm/mach-spear/spear1340.c
@@ -11,138 +11,13 @@
  * warranty of any kind, whether express or implied.
  */
 
-#define pr_fmt(fmt) "SPEAr1340: " fmt
-
-#include <linux/ahci_platform.h>
-#include <linux/amba/serial.h>
-#include <linux/delay.h>
 #include <linux/of_platform.h>
 #include <asm/mach/arch.h>
 #include "generic.h"
-#include <mach/spear.h>
-
-/* FIXME: Move SATA PHY code into a standalone driver */
-
-/* Base addresses */
-#define SPEAR1340_SATA_BASE			UL(0xB1000000)
-
-/* Power Management Registers */
-#define SPEAR1340_PCM_CFG			(VA_MISC_BASE + 0x100)
-#define SPEAR1340_PCM_WKUP_CFG			(VA_MISC_BASE + 0x104)
-#define SPEAR1340_SWITCH_CTR			(VA_MISC_BASE + 0x108)
-
-#define SPEAR1340_PERIP1_SW_RST			(VA_MISC_BASE + 0x318)
-#define SPEAR1340_PERIP2_SW_RST			(VA_MISC_BASE + 0x31C)
-#define SPEAR1340_PERIP3_SW_RST			(VA_MISC_BASE + 0x320)
-
-/* PCIE - SATA configuration registers */
-#define SPEAR1340_PCIE_SATA_CFG			(VA_MISC_BASE + 0x424)
-	/* PCIE CFG MASks */
-	#define SPEAR1340_PCIE_CFG_DEVICE_PRESENT	(1 << 11)
-	#define SPEAR1340_PCIE_CFG_POWERUP_RESET	(1 << 10)
-	#define SPEAR1340_PCIE_CFG_CORE_CLK_EN		(1 << 9)
-	#define SPEAR1340_PCIE_CFG_AUX_CLK_EN		(1 << 8)
-	#define SPEAR1340_SATA_CFG_TX_CLK_EN		(1 << 4)
-	#define SPEAR1340_SATA_CFG_RX_CLK_EN		(1 << 3)
-	#define SPEAR1340_SATA_CFG_POWERUP_RESET	(1 << 2)
-	#define SPEAR1340_SATA_CFG_PM_CLK_EN		(1 << 1)
-	#define SPEAR1340_PCIE_SATA_SEL_PCIE		(0)
-	#define SPEAR1340_PCIE_SATA_SEL_SATA		(1)
-	#define SPEAR1340_SATA_PCIE_CFG_MASK		0xF1F
-	#define SPEAR1340_PCIE_CFG_VAL	(SPEAR1340_PCIE_SATA_SEL_PCIE | \
-			SPEAR1340_PCIE_CFG_AUX_CLK_EN | \
-			SPEAR1340_PCIE_CFG_CORE_CLK_EN | \
-			SPEAR1340_PCIE_CFG_POWERUP_RESET | \
-			SPEAR1340_PCIE_CFG_DEVICE_PRESENT)
-	#define SPEAR1340_SATA_CFG_VAL	(SPEAR1340_PCIE_SATA_SEL_SATA | \
-			SPEAR1340_SATA_CFG_PM_CLK_EN | \
-			SPEAR1340_SATA_CFG_POWERUP_RESET | \
-			SPEAR1340_SATA_CFG_RX_CLK_EN | \
-			SPEAR1340_SATA_CFG_TX_CLK_EN)
-
-#define SPEAR1340_PCIE_MIPHY_CFG		(VA_MISC_BASE + 0x428)
-	#define SPEAR1340_MIPHY_OSC_BYPASS_EXT		(1 << 31)
-	#define SPEAR1340_MIPHY_CLK_REF_DIV2		(1 << 27)
-	#define SPEAR1340_MIPHY_CLK_REF_DIV4		(2 << 27)
-	#define SPEAR1340_MIPHY_CLK_REF_DIV8		(3 << 27)
-	#define SPEAR1340_MIPHY_PLL_RATIO_TOP(x)	(x << 0)
-	#define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA \
-			(SPEAR1340_MIPHY_OSC_BYPASS_EXT | \
-			SPEAR1340_MIPHY_CLK_REF_DIV2 | \
-			SPEAR1340_MIPHY_PLL_RATIO_TOP(60))
-	#define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK \
-			(SPEAR1340_MIPHY_PLL_RATIO_TOP(120))
-	#define SPEAR1340_PCIE_SATA_MIPHY_CFG_PCIE \
-			(SPEAR1340_MIPHY_OSC_BYPASS_EXT | \
-			SPEAR1340_MIPHY_PLL_RATIO_TOP(25))
-
-/* SATA device registration */
-static int sata_miphy_init(struct device *dev, void __iomem *addr)
-{
-	writel(SPEAR1340_SATA_CFG_VAL, SPEAR1340_PCIE_SATA_CFG);
-	writel(SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK,
-			SPEAR1340_PCIE_MIPHY_CFG);
-	/* Switch on sata power domain */
-	writel((readl(SPEAR1340_PCM_CFG) | (0x800)), SPEAR1340_PCM_CFG);
-	msleep(20);
-	/* Disable PCIE SATA Controller reset */
-	writel((readl(SPEAR1340_PERIP1_SW_RST) & (~0x1000)),
-			SPEAR1340_PERIP1_SW_RST);
-	msleep(20);
-
-	return 0;
-}
-
-void sata_miphy_exit(struct device *dev)
-{
-	writel(0, SPEAR1340_PCIE_SATA_CFG);
-	writel(0, SPEAR1340_PCIE_MIPHY_CFG);
-
-	/* Enable PCIE SATA Controller reset */
-	writel((readl(SPEAR1340_PERIP1_SW_RST) | (0x1000)),
-			SPEAR1340_PERIP1_SW_RST);
-	msleep(20);
-	/* Switch off sata power domain */
-	writel((readl(SPEAR1340_PCM_CFG) & (~0x800)), SPEAR1340_PCM_CFG);
-	msleep(20);
-}
-
-int sata_suspend(struct device *dev)
-{
-	if (dev->power.power_state.event == PM_EVENT_FREEZE)
-		return 0;
-
-	sata_miphy_exit(dev);
-
-	return 0;
-}
-
-int sata_resume(struct device *dev)
-{
-	if (dev->power.power_state.event == PM_EVENT_THAW)
-		return 0;
-
-	return sata_miphy_init(dev, NULL);
-}
-
-static struct ahci_platform_data sata_pdata = {
-	.init = sata_miphy_init,
-	.exit = sata_miphy_exit,
-	.suspend = sata_suspend,
-	.resume = sata_resume,
-};
-
-/* Add SPEAr1340 auxdata to pass platform data */
-static struct of_dev_auxdata spear1340_auxdata_lookup[] __initdata = {
-	OF_DEV_AUXDATA("snps,spear-ahci", SPEAR1340_SATA_BASE, NULL,
-			&sata_pdata),
-	{}
-};
 
 static void __init spear1340_dt_init(void)
 {
-	of_platform_populate(NULL, of_default_bus_match_table,
-			spear1340_auxdata_lookup, NULL);
+	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
 }
 
 static const char * const spear1340_dt_board_compat[] = {
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index a344f3d..ae34fb8 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -51,4 +51,10 @@ config PHY_EXYNOS_DP_VIDEO
 	help
 	  Support for Display Port PHY found on Samsung EXYNOS SoCs.
 
+config PHY_SPEAR13XX_SATA_PCIE
+	tristate "SPEAr13xx SoC SATA PCIe PHY driver"
+	help
+	  Support for SATA and PCIe PHY for SPEAr13xx SoC
+	select GENERIC_PHY
+
 endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index d0caae9..8941283 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -7,3 +7,4 @@ obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO)	+= phy-exynos-dp-video.o
 obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO)	+= phy-exynos-mipi-video.o
 obj-$(CONFIG_OMAP_USB2)			+= phy-omap-usb2.o
 obj-$(CONFIG_TWL4030_USB)		+= phy-twl4030-usb.o
+obj-$(CONFIG_PHY_SPEAR13XX_SATA_PCIE)	+= phy-spear13xx-sata-pcie.o
diff --git a/drivers/phy/phy-spear13xx-sata-pcie.c b/drivers/phy/phy-spear13xx-sata-pcie.c
new file mode 100644
index 0000000..6adfa64
--- /dev/null
+++ b/drivers/phy/phy-spear13xx-sata-pcie.c
@@ -0,0 +1,305 @@
+/*
+ * ST SPEAr13xx SATA PCIe PHY driver
+ *
+ * Copyright (C) 2014 ST Microelectronics
+ * Pratyush Anand <pratyush.anand-qxv4g6HH51o@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+
+/* SPEAr1340 Registers */
+/* Power Management Registers */
+#define SPEAR1340_PCM_CFG			0x100
+	#define SPEAR1340_PCM_CFG_SATA_POWER_EN	0x800
+#define SPEAR1340_PCM_WKUP_CFG			0x104
+#define SPEAR1340_SWITCH_CTR			0x108
+
+#define SPEAR1340_PERIP1_SW_RST			0x318
+	#define SPEAR1340_PERIP1_SW_RST_SATA	0x1000
+#define SPEAR1340_PERIP2_SW_RST			0x31C
+#define SPEAR1340_PERIP3_SW_RST			0x320
+
+/* PCIE - SATA configuration registers */
+#define SPEAR1340_PCIE_SATA_CFG			0x424
+	/* PCIE CFG MASks */
+	#define SPEAR1340_PCIE_CFG_DEVICE_PRESENT	(1 << 11)
+	#define SPEAR1340_PCIE_CFG_POWERUP_RESET	(1 << 10)
+	#define SPEAR1340_PCIE_CFG_CORE_CLK_EN		(1 << 9)
+	#define SPEAR1340_PCIE_CFG_AUX_CLK_EN		(1 << 8)
+	#define SPEAR1340_SATA_CFG_TX_CLK_EN		(1 << 4)
+	#define SPEAR1340_SATA_CFG_RX_CLK_EN		(1 << 3)
+	#define SPEAR1340_SATA_CFG_POWERUP_RESET	(1 << 2)
+	#define SPEAR1340_SATA_CFG_PM_CLK_EN		(1 << 1)
+	#define SPEAR1340_PCIE_SATA_SEL_PCIE		(0)
+	#define SPEAR1340_PCIE_SATA_SEL_SATA		(1)
+	#define SPEAR1340_PCIE_SATA_CFG_MASK		0xF1F
+	#define SPEAR1340_PCIE_CFG_VAL	(SPEAR1340_PCIE_SATA_SEL_PCIE | \
+			SPEAR1340_PCIE_CFG_AUX_CLK_EN | \
+			SPEAR1340_PCIE_CFG_CORE_CLK_EN | \
+			SPEAR1340_PCIE_CFG_POWERUP_RESET | \
+			SPEAR1340_PCIE_CFG_DEVICE_PRESENT)
+	#define SPEAR1340_SATA_CFG_VAL	(SPEAR1340_PCIE_SATA_SEL_SATA | \
+			SPEAR1340_SATA_CFG_PM_CLK_EN | \
+			SPEAR1340_SATA_CFG_POWERUP_RESET | \
+			SPEAR1340_SATA_CFG_RX_CLK_EN | \
+			SPEAR1340_SATA_CFG_TX_CLK_EN)
+
+#define SPEAR1340_PCIE_MIPHY_CFG		0x428
+	#define SPEAR1340_MIPHY_OSC_BYPASS_EXT		(1 << 31)
+	#define SPEAR1340_MIPHY_CLK_REF_DIV2		(1 << 27)
+	#define SPEAR1340_MIPHY_CLK_REF_DIV4		(2 << 27)
+	#define SPEAR1340_MIPHY_CLK_REF_DIV8		(3 << 27)
+	#define SPEAR1340_MIPHY_PLL_RATIO_TOP(x)	(x << 0)
+	#define SPEAR1340_PCIE_MIPHY_CFG_MASK		0xF80000FF
+	#define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA \
+			(SPEAR1340_MIPHY_OSC_BYPASS_EXT | \
+			SPEAR1340_MIPHY_CLK_REF_DIV2 | \
+			SPEAR1340_MIPHY_PLL_RATIO_TOP(60))
+	#define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK \
+			(SPEAR1340_MIPHY_PLL_RATIO_TOP(120))
+	#define SPEAR1340_PCIE_SATA_MIPHY_CFG_PCIE \
+			(SPEAR1340_MIPHY_OSC_BYPASS_EXT | \
+			SPEAR1340_MIPHY_PLL_RATIO_TOP(25))
+
+enum phy_mode {
+	SATA,
+	PCIE
+};
+
+struct spear13xx_phy_priv {
+	/* regmap for SPEAr13xx misc registers */
+	struct regmap		*misc;
+	/* phy struct pointer */
+	struct phy		*phy;
+	/* phy mode: 0 for SATA and 1 for PCIe */
+	enum phy_mode		mode;
+	/* instance id of this phy */
+	u32			id;
+};
+
+static int spear1340_sata_miphy_init(struct spear13xx_phy_priv *phypriv)
+{
+	regmap_update_bits(phypriv->misc, SPEAR1340_PCIE_SATA_CFG,
+			SPEAR1340_PCIE_SATA_CFG_MASK, SPEAR1340_SATA_CFG_VAL);
+	regmap_update_bits(phypriv->misc, SPEAR1340_PCIE_MIPHY_CFG,
+			SPEAR1340_PCIE_MIPHY_CFG_MASK,
+			SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK);
+	/* Switch on sata power domain */
+	regmap_update_bits(phypriv->misc, SPEAR1340_PCM_CFG,
+			SPEAR1340_PCM_CFG_SATA_POWER_EN,
+			SPEAR1340_PCM_CFG_SATA_POWER_EN);
+	msleep(20);
+	/* Disable PCIE SATA Controller reset */
+	regmap_update_bits(phypriv->misc, SPEAR1340_PERIP1_SW_RST,
+			SPEAR1340_PERIP1_SW_RST_SATA, 0);
+	msleep(20);
+
+	return 0;
+}
+
+static int spear1340_sata_miphy_exit(struct spear13xx_phy_priv *phypriv)
+{
+	regmap_update_bits(phypriv->misc, SPEAR1340_PCIE_SATA_CFG,
+			SPEAR1340_PCIE_SATA_CFG_MASK, 0);
+	regmap_update_bits(phypriv->misc, SPEAR1340_PCIE_MIPHY_CFG,
+			SPEAR1340_PCIE_MIPHY_CFG_MASK, 0);
+
+	/* Enable PCIE SATA Controller reset */
+	regmap_update_bits(phypriv->misc, SPEAR1340_PERIP1_SW_RST,
+			SPEAR1340_PERIP1_SW_RST_SATA,
+			SPEAR1340_PERIP1_SW_RST_SATA);
+	msleep(20);
+	/* Switch off sata power domain */
+	regmap_update_bits(phypriv->misc, SPEAR1340_PCM_CFG,
+			SPEAR1340_PCM_CFG_SATA_POWER_EN, 0);
+	msleep(20);
+
+	return 0;
+}
+
+static int sata_miphy_init(struct spear13xx_phy_priv *phypriv)
+{
+	if (of_machine_is_compatible("st,spear1340"))
+		return spear1340_sata_miphy_init(phypriv);
+	else
+		return -EINVAL;
+}
+
+static int sata_miphy_exit(struct spear13xx_phy_priv *phypriv)
+{
+	if (of_machine_is_compatible("st,spear1340"))
+		return spear1340_sata_miphy_exit(phypriv);
+	else
+		return -EINVAL;
+}
+
+static int sata_miphy_suspend(struct spear13xx_phy_priv *phypriv)
+{
+	return sata_miphy_exit(phypriv);
+}
+
+static int sata_miphy_resume(struct spear13xx_phy_priv *phypriv)
+{
+	return sata_miphy_init(phypriv);
+}
+
+static int miphy_init(struct phy *phy)
+{
+	struct spear13xx_phy_priv *phypriv = phy_get_drvdata(phy);
+
+	switch (phypriv->mode) {
+	case SATA:
+		return sata_miphy_init(phypriv);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int miphy_exit(struct phy *phy)
+{
+	struct spear13xx_phy_priv *phypriv = phy_get_drvdata(phy);
+
+	switch (phypriv->mode) {
+	case SATA:
+		return sata_miphy_exit(phypriv);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int miphy_power_off(struct phy *phy)
+{
+	struct spear13xx_phy_priv *phypriv = phy_get_drvdata(phy);
+	struct device *dev = &phy->dev;
+
+	if (dev->power.power_state.event == PM_EVENT_FREEZE)
+		return 0;
+
+	switch (phypriv->mode) {
+	case SATA:
+		return sata_miphy_suspend(phypriv);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int miphy_power_on(struct phy *phy)
+{
+	struct spear13xx_phy_priv *phypriv = phy_get_drvdata(phy);
+	struct device *dev = &phy->dev;
+
+	if (dev->power.power_state.event == PM_EVENT_THAW)
+		return 0;
+
+	switch (phypriv->mode) {
+	case SATA:
+		return sata_miphy_resume(phypriv);
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct of_device_id spear13xx_phy_of_match[] = {
+	{ .compatible = "st,spear1340-sata-pcie-phy" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, spear13xx_phy_of_match);
+
+static struct phy_ops spear13xx_sata_pcie_phy_ops = {
+	.init = miphy_init,
+	.exit = miphy_exit,
+	.power_off = miphy_power_off,
+	.power_on = miphy_power_on,
+	.owner		= THIS_MODULE,
+};
+
+static struct phy *spear13xx_sata_pcie_phy_xlate(struct device *dev,
+					struct of_phandle_args *args)
+{
+	struct spear13xx_phy_priv *phypriv = dev_get_drvdata(dev);
+
+	if (args->args_count < 2) {
+		dev_err(dev, "DT did not pass correct no of args\n");
+		return NULL;
+	}
+
+	phypriv->mode = args->args[0];
+	phypriv->id = args->args[1];
+
+	return phypriv->phy;
+}
+
+static int __init spear13xx_phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct spear13xx_phy_priv *phypriv;
+	struct phy_provider *phy_provider;
+
+	phypriv = devm_kzalloc(dev, sizeof(*phypriv), GFP_KERNEL);
+	if (!phypriv) {
+		dev_err(dev, "can't alloc sata pcie private date memory\n");
+		return -ENOMEM;
+	}
+
+	phypriv->misc =
+		syscon_regmap_lookup_by_phandle(dev->of_node, "misc");
+	if (IS_ERR(phypriv->misc)) {
+		dev_err(dev, "failed to find SPEAr13xx misc regmap\n");
+		return PTR_ERR(phypriv->misc);
+	}
+
+	phy_provider = devm_of_phy_provider_register(dev,
+			spear13xx_sata_pcie_phy_xlate);
+	if (IS_ERR(phy_provider)) {
+		dev_err(dev, "failed to register phy provider\n");
+		return PTR_ERR(phy_provider);
+	}
+
+	phypriv->phy = devm_phy_create(dev, &spear13xx_sata_pcie_phy_ops, NULL);
+	if (IS_ERR(phypriv->phy)) {
+		dev_err(dev, "failed to create SATA PCIe PHY\n");
+		return PTR_ERR(phypriv->phy);
+	}
+
+	dev_set_drvdata(dev, phypriv);
+	phy_set_drvdata(phypriv->phy, phypriv);
+
+	return 0;
+}
+
+static int __exit spear13xx_phy_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static struct platform_driver spear13xx_phy_driver = {
+	.remove		= __exit_p(spear13xx_phy_remove),
+	.driver = {
+		.name = "spear13xx-sata_pcie-phy",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(spear13xx_phy_of_match),
+	},
+};
+
+static int __init spear13xx_phy_init(void)
+{
+
+	return platform_driver_probe(&spear13xx_phy_driver,
+				spear13xx_phy_probe);
+}
+subsys_initcall(spear13xx_phy_init);
+
+MODULE_DESCRIPTION("ST SPEAr13xx SATA PCIe PHY driver");
+MODULE_AUTHOR("Pratyush Anand <pratyush.anand-qxv4g6HH51o@public.gmane.org>");
+MODULE_LICENSE("GPL v2");
-- 
1.7.0.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* Re: [RFC PATCH v2 08/14] mtd: nand: add sunxi NAND flash controller support
From: Boris BREZILLON @ 2014-01-30 11:22 UTC (permalink / raw)
  To: Maxime Ripard, Rob Landley, Russell King, David Woodhouse,
	Grant Likely, Brian Norris, Jason Gunthorpe, Arnd Bergmann
  Cc: Boris BREZILLON, devicetree, linux-doc, linux-kernel,
	linux-arm-kernel, linux-mtd, dev
In-Reply-To: <1391006064-28890-9-git-send-email-b.brezillon.dev@gmail.com>

On 29/01/2014 15:34, Boris BREZILLON wrote:
> Add support for the sunxi NAND Flash Controller (NFC).
>
> Signed-off-by: Boris BREZILLON <b.brezillon.dev@gmail.com>
> ---
>   drivers/mtd/nand/Kconfig      |    6 +
>   drivers/mtd/nand/Makefile     |    1 +
>   drivers/mtd/nand/sunxi_nand.c |  744 +++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 751 insertions(+)
>   create mode 100644 drivers/mtd/nand/sunxi_nand.c
>
> diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
> index 93ae6a6..784dd42 100644
> --- a/drivers/mtd/nand/Kconfig
> +++ b/drivers/mtd/nand/Kconfig
> @@ -510,4 +510,10 @@ config MTD_NAND_XWAY
>   	  Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached
>   	  to the External Bus Unit (EBU).
>   
> +config MTD_NAND_SUNXI
> +	tristate "Support for NAND on Allwinner SoCs"
> +	depends on ARCH_SUNXI
> +	help
> +	  Enables support for NAND Flash chips on Allwinner SoCs.
> +
>   endif # MTD_NAND
> diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
> index bbea7a6..e3b4a34 100644
> --- a/drivers/mtd/nand/Makefile
> +++ b/drivers/mtd/nand/Makefile
> @@ -49,5 +49,6 @@ obj-$(CONFIG_MTD_NAND_JZ4740)		+= jz4740_nand.o
>   obj-$(CONFIG_MTD_NAND_GPMI_NAND)	+= gpmi-nand/
>   obj-$(CONFIG_MTD_NAND_XWAY)		+= xway_nand.o
>   obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH)	+= bcm47xxnflash/
> +obj-$(CONFIG_MTD_NAND_SUNXI)		+= sunxi_nand.o
>   
>   nand-objs := nand_base.o nand_bbt.o
> diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
> new file mode 100644
> index 0000000..d3da810
> --- /dev/null
> +++ b/drivers/mtd/nand/sunxi_nand.c
> @@ -0,0 +1,744 @@
> +/*
> + * Copyright (C) 2013 Boris BREZILLON <b.brezillon.dev@gmail.com>
> + *
> + * Derived from:
> + *	https://github.com/yuq/sunxi-nfc-mtd
> + *	Copyright (C) 2013 Qiang Yu <yuq825@gmail.com>
> + *
> + *	https://github.com/hno/Allwinner-Info
> + *	Copyright (C) 2013 Henrik Nordström <Henrik Nordström>
> + *
> + *	Copyright (C) 2013 Dmitriy B. <rzk333@gmail.com>
> + *	Copyright (C) 2013 Sergey Lapin <slapin@ossfans.org>
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/dma-mapping.h>
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/platform_device.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/of_gpio.h>
> +#include <linux/of_mtd.h>
> +#include <linux/mtd/mtd.h>
> +#include <linux/mtd/nand.h>
> +#include <linux/mtd/partitions.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/dmaengine.h>
> +#include <linux/gpio.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +
> +#define NFC_REG_CTL		0x0000
> +#define NFC_REG_ST		0x0004
> +#define NFC_REG_INT		0x0008
> +#define NFC_REG_TIMING_CTL	0x000C
> +#define NFC_REG_TIMING_CFG	0x0010
> +#define NFC_REG_ADDR_LOW	0x0014
> +#define NFC_REG_ADDR_HIGH	0x0018
> +#define NFC_REG_SECTOR_NUM	0x001C
> +#define NFC_REG_CNT		0x0020
> +#define NFC_REG_CMD		0x0024
> +#define NFC_REG_RCMD_SET	0x0028
> +#define NFC_REG_WCMD_SET	0x002C
> +#define NFC_REG_IO_DATA		0x0030
> +#define NFC_REG_ECC_CTL		0x0034
> +#define NFC_REG_ECC_ST		0x0038
> +#define NFC_REG_DEBUG		0x003C
> +#define NFC_REG_ECC_CNT0	0x0040
> +#define NFC_REG_ECC_CNT1	0x0044
> +#define NFC_REG_ECC_CNT2	0x0048
> +#define NFC_REG_ECC_CNT3	0x004c
> +#define NFC_REG_USER_DATA_BASE	0x0050
> +#define NFC_REG_SPARE_AREA	0x00A0
> +#define NFC_RAM0_BASE		0x0400
> +#define NFC_RAM1_BASE		0x0800
> +
> +/*define bit use in NFC_CTL*/
> +#define NFC_EN				(1 << 0)
> +#define NFC_RESET			(1 << 1)
> +#define NFC_BUS_WIDYH			(1 << 2)
> +#define NFC_RB_SEL			(1 << 3)
> +#define NFC_CE_SEL			(7 << 24)
> +#define NFC_CE_CTL			(1 << 6)
> +#define NFC_CE_CTL1			(1 << 7)
> +#define NFC_PAGE_SIZE			(0xf << 8)
> +#define NFC_SAM				(1 << 12)
> +#define NFC_RAM_METHOD			(1 << 14)
> +#define NFC_DEBUG_CTL			(1 << 31)
> +
> +/*define bit use in NFC_ST*/
> +#define NFC_RB_B2R			(1 << 0)
> +#define NFC_CMD_INT_FLAG		(1 << 1)
> +#define NFC_DMA_INT_FLAG		(1 << 2)
> +#define NFC_CMD_FIFO_STATUS		(1 << 3)
> +#define NFC_STA				(1 << 4)
> +#define NFC_NATCH_INT_FLAG		(1 << 5)
> +#define NFC_RB_STATE0			(1 << 8)
> +#define NFC_RB_STATE1			(1 << 9)
> +#define NFC_RB_STATE2			(1 << 10)
> +#define NFC_RB_STATE3			(1 << 11)
> +
> +/*define bit use in NFC_INT*/
> +#define NFC_B2R_INT_ENABLE		(1 << 0)
> +#define NFC_CMD_INT_ENABLE		(1 << 1)
> +#define NFC_DMA_INT_ENABLE		(1 << 2)
> +#define NFC_INT_MASK			(NFC_B2R_INT_ENABLE | \
> +					 NFC_CMD_INT_ENABLE | \
> +					 NFC_DMA_INT_ENABLE)
> +
> +
> +/*define bit use in NFC_CMD*/
> +#define NFC_CMD_LOW_BYTE		(0xff << 0)
> +#define NFC_CMD_HIGH_BYTE		(0xff << 8)
> +#define NFC_ADR_NUM			(0x7 << 16)
> +#define NFC_SEND_ADR			(1 << 19)
> +#define NFC_ACCESS_DIR			(1 << 20)
> +#define NFC_DATA_TRANS			(1 << 21)
> +#define NFC_SEND_CMD1			(1 << 22)
> +#define NFC_WAIT_FLAG			(1 << 23)
> +#define NFC_SEND_CMD2			(1 << 24)
> +#define NFC_SEQ				(1 << 25)
> +#define NFC_DATA_SWAP_METHOD		(1 << 26)
> +#define NFC_ROW_AUTO_INC		(1 << 27)
> +#define NFC_SEND_CMD3			(1 << 28)
> +#define NFC_SEND_CMD4			(1 << 29)
> +#define NFC_CMD_TYPE			(3 << 30)
> +
> +/* define bit use in NFC_RCMD_SET*/
> +#define NFC_READ_CMD			(0xff << 0)
> +#define NFC_RANDOM_READ_CMD0		(0xff << 8)
> +#define NFC_RANDOM_READ_CMD1		(0xff << 16)
> +
> +/*define bit use in NFC_WCMD_SET*/
> +#define NFC_PROGRAM_CMD			(0xff << 0)
> +#define NFC_RANDOM_WRITE_CMD		(0xff << 8)
> +#define NFC_READ_CMD0			(0xff << 16)
> +#define NFC_READ_CMD1			(0xff << 24)
> +
> +/*define bit use in NFC_ECC_CTL*/
> +#define NFC_ECC_EN			(1 << 0)
> +#define NFC_ECC_PIPELINE		(1 << 3)
> +#define NFC_ECC_EXCEPTION		(1 << 4)
> +#define NFC_ECC_BLOCK_SIZE		(1 << 5)
> +#define NFC_RANDOM_EN			(1 << 9)
> +#define NFC_RANDOM_DIRECTION		(1 << 10)
> +#define NFC_ECC_MODE_SHIFT		12
> +#define NFC_ECC_MODE			(0xf << NFC_ECC_MODE_SHIFT)
> +#define NFC_RANDOM_SEED			(0x7fff << 16)
> +
> +
> +
> +enum sunxi_nand_rb_type {
> +	RB_NONE,
> +	RB_NATIVE,
> +	RB_GPIO,
> +};
> +
> +struct sunxi_nand_rb {
> +	enum sunxi_nand_rb_type type;
> +	union {
> +		int gpio;
> +		int nativeid;
> +	} info;
> +};
> +
> +struct sunxi_nand_chip_sel {
> +	u8 cs;
> +	struct sunxi_nand_rb rb;
> +};
> +
> +#define DEFAULT_NAME_FORMAT	"nand@%d"
> +#define MAX_NAME_SIZE		(sizeof("nand@") + 2)
> +
> +struct sunxi_nand_chip {
> +	struct list_head node;
> +	struct nand_chip nand;
> +	struct mtd_info mtd;
> +	char default_name[MAX_NAME_SIZE];
> +	unsigned long clk_rate;
> +	int selected;
> +	int nsels;
> +	struct sunxi_nand_chip_sel sels[0];
> +};
> +
> +static inline struct sunxi_nand_chip *to_sunxi_nand(struct mtd_info *mtd)
> +{
> +	return container_of(mtd, struct sunxi_nand_chip, mtd);
> +}
> +
> +struct sunxi_nfc {
> +	struct nand_hw_control controller;
> +	void __iomem *regs;
> +	int irq;
> +	struct clk *ahb_clk;
> +	struct clk *sclk;
> +	unsigned long assigned_cs;
> +	unsigned long clk_rate;
> +	struct list_head chips;
> +	struct completion complete;
> +};
> +
> +static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_hw_control *ctrl)
> +{
> +	return container_of(ctrl, struct sunxi_nfc, controller);
> +}
> +
> +static irqreturn_t sunxi_nfc_interrupt(int irq, void *dev_id)
> +{
> +	struct sunxi_nfc *nfc = dev_id;
> +	u32 st = readl(nfc->regs + NFC_REG_ST);
> +	u32 ien = readl(nfc->regs + NFC_REG_INT);
> +
> +	if (!(ien & st))
> +		return IRQ_NONE;
> +
> +	if ((ien & st) == ien)
> +		complete(&nfc->complete);
> +
> +	writel(st & NFC_INT_MASK, nfc->regs + NFC_REG_ST);
> +	writel(~st & ien & NFC_INT_MASK, nfc->regs + NFC_REG_INT);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int sunxi_nfc_wait_int(struct sunxi_nfc *nfc, u32 flags,
> +			      unsigned int timeout_ms)
> +{
> +	init_completion(&nfc->complete);
> +
> +	writel(flags, nfc->regs + NFC_REG_INT);
> +	if (!timeout_ms)
> +		wait_for_completion(&nfc->complete);
> +	else if (!wait_for_completion_timeout(&nfc->complete,
> +					      msecs_to_jiffies(timeout_ms)))
> +		return -ETIMEDOUT;
> +
> +	return 0;
> +}
> +
> +static int sunxi_nfc_dev_ready(struct mtd_info *mtd)
> +{
> +	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(mtd);
> +	struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
> +	struct sunxi_nand_rb *rb;
> +	unsigned long timeo = (sunxi_nand->nand.state == FL_ERASING ? 400 : 20);
> +	int ret;
> +
> +	if (sunxi_nand->selected < 0)
> +		return 0;
> +
> +	rb = &sunxi_nand->sels[sunxi_nand->selected].rb;
> +
> +	switch (rb->type) {
> +	case RB_NATIVE:
> +		ret = !!(readl(nfc->regs + NFC_REG_ST) &
> +			 (NFC_RB_STATE0 << rb->info.nativeid));
> +		if (ret)
> +			break;
> +
> +		sunxi_nfc_wait_int(nfc, NFC_RB_B2R, timeo);
> +		ret = !!(readl(nfc->regs + NFC_REG_ST) &
> +			 (NFC_RB_STATE0 << rb->info.nativeid));
> +		break;
> +	case RB_GPIO:
> +		ret = gpio_get_value(rb->info.gpio);
> +		break;
> +	case RB_NONE:
> +	default:
> +		ret = 0;
> +		dev_err(&mtd->dev, "cannot check R/B NAND status!");
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip)
> +{
> +	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(mtd);
> +	struct nand_chip *nand = &sunxi_nand->nand;
> +	struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
> +	struct sunxi_nand_chip_sel *sel;
> +	u32 ctl;
> +
> +	if (chip > 0 && chip >= sunxi_nand->nsels)
> +		return;
> +
> +	if (chip == sunxi_nand->selected)
> +		return;
> +
> +	ctl = readl(nfc->regs + NFC_REG_CTL) &
> +	      ~(NFC_CE_SEL | NFC_RB_SEL | NFC_EN);
> +
> +	if (chip >= 0) {
> +		sel = &sunxi_nand->sels[chip];
> +
> +		ctl |= (sel->cs << 24) | NFC_EN |
> +		       (((nand->page_shift - 10) & 0xf) << 8);
> +		if (sel->rb.type == RB_NONE) {
> +			nand->dev_ready = NULL;
> +		} else {
> +			nand->dev_ready = sunxi_nfc_dev_ready;
> +			if (sel->rb.type == RB_NATIVE)
> +				ctl |= (sel->rb.info.nativeid << 3);
> +		}
> +
> +		writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA);
> +
> +		if (nfc->clk_rate != sunxi_nand->clk_rate) {
> +			clk_set_rate(nfc->sclk, sunxi_nand->clk_rate);
> +			nfc->clk_rate = sunxi_nand->clk_rate;
> +		}
> +	}
> +
> +	writel(ctl, nfc->regs + NFC_REG_CTL);
> +
> +	sunxi_nand->selected = chip;
> +}
> +
> +static void sunxi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
> +{
> +	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(mtd);
> +	struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
> +	int cnt;
> +	int offs = 0;
> +	u32 tmp;
> +
> +	while (len > offs) {
> +		cnt = len - offs;
> +		if (cnt > 1024)
> +			cnt = 1024;
> +
> +		while ((readl(nfc->regs + NFC_REG_ST) & NFC_CMD_FIFO_STATUS))
> +			;
> +		writel(cnt, nfc->regs + NFC_REG_CNT);
> +		tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD;
> +		writel(tmp, nfc->regs + NFC_REG_CMD);
> +		sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
> +		if (buf)
> +			memcpy_fromio(buf + offs, nfc->regs + NFC_RAM0_BASE,
> +				      cnt);
> +		offs += cnt;
> +	}
> +}
> +
> +static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
> +				int len)
> +{
> +	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(mtd);
> +	struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
> +	int cnt;
> +	int offs = 0;
> +	u32 tmp;
> +
> +	while (len > offs) {
> +		cnt = len - offs;
> +		if (cnt > 1024)
> +			cnt = 1024;
> +
> +		while ((readl(nfc->regs + NFC_REG_ST) & NFC_CMD_FIFO_STATUS))
> +			;
> +		writel(cnt, nfc->regs + NFC_REG_CNT);
> +		memcpy_toio(nfc->regs + NFC_RAM0_BASE, buf + offs, cnt);
> +		tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
> +		      NFC_ACCESS_DIR;
> +		writel(tmp, nfc->regs + NFC_REG_CMD);
> +		sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
> +		offs += cnt;
> +	}
> +}
> +
> +static uint8_t sunxi_nfc_read_byte(struct mtd_info *mtd)
> +{
> +	uint8_t ret;
> +
> +	sunxi_nfc_read_buf(mtd, &ret, 1);
> +
> +	return ret;
> +}
> +
> +static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat,
> +			       unsigned int ctrl)
> +{
> +	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(mtd);
> +	struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
> +	u32 tmp;
> +
> +	while ((readl(nfc->regs + NFC_REG_ST) & NFC_CMD_FIFO_STATUS))
> +		;
> +
> +	if (ctrl & NAND_CTRL_CHANGE) {
> +		tmp = readl(nfc->regs + NFC_REG_CTL);
> +		if (ctrl & NAND_NCE)
> +			tmp |= NFC_CE_CTL;
> +		else
> +			tmp &= ~NFC_CE_CTL;
> +		writel(tmp, nfc->regs + NFC_REG_CTL);
> +	}
> +
> +	if (dat == NAND_CMD_NONE)
> +		return;
> +
> +	if (ctrl & NAND_CLE) {
> +		writel(NFC_SEND_CMD1 | dat, nfc->regs + NFC_REG_CMD);
> +	} else {
> +		writel(dat, nfc->regs + NFC_REG_ADDR_LOW);
> +		writel(NFC_SEND_ADR, nfc->regs + NFC_REG_CMD);
> +	}
> +
> +	sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
> +}
> +
> +static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip,
> +					struct device_node *np)
> +{
> +	const struct nand_sdr_timings *timings;
> +	u32 min_clk_period = 0;
> +	int ret;
> +
> +	ret = onfi_get_async_timing_mode(&chip->nand);
> +	if (ret == ONFI_TIMING_MODE_UNKNOWN) {
> +		ret = of_get_nand_onfi_timing_mode(np);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	ret = fls(ret);
> +	if (!ret)
> +		return -EINVAL;
> +
> +	timings = onfi_async_timing_mode_to_sdr_timings(ret - 1);
> +	if (IS_ERR(timings))
> +		return PTR_ERR(timings);
> +
> +	/* NFC timings defined in Allwinner Datasheets */
> +
> +	/* T1 <=> tCLS */
> +	if (timings->tCLS_min > min_clk_period)
> +		min_clk_period = timings->tCLS_min;
> +
> +	/* T2 <=> tCLH */
> +	if (timings->tCLH_min > min_clk_period)
> +		min_clk_period = timings->tCLH_min;
> +
> +	/* T3 <=> tCS */
> +	if (timings->tCS_min > min_clk_period)
> +		min_clk_period = timings->tCS_min;
> +
> +	/* T4 <=> tCH */
> +	if (timings->tCH_min > min_clk_period)
> +		min_clk_period = timings->tCH_min;
> +
> +	/* T5 <=> tWP */
> +	if (timings->tWP_min > min_clk_period)
> +		min_clk_period = timings->tWP_min;
> +
> +	/* T6 <=> tWH */
> +	if (timings->tWH_min > min_clk_period)
> +		min_clk_period = timings->tWH_min;
> +
> +	/* T7 <=> tALS */
> +	if (timings->tALS_min > min_clk_period)
> +		min_clk_period = timings->tALS_min;
> +
> +	/* T8 <=> tDS */
> +	if (timings->tDS_min > min_clk_period)
> +		min_clk_period = timings->tDS_min;
> +
> +	/* T9 <=> tDH */
> +	if (timings->tDH_min > min_clk_period)
> +		min_clk_period = timings->tDH_min;
> +
> +	/* T10 <=> tRR */
> +	if (timings->tRR_min > (min_clk_period * 3))
> +		min_clk_period = (timings->tRR_min + 2) / 3;
> +
> +	/* T11 <=> tALH */
> +	if (timings->tALH_min > min_clk_period)
> +		min_clk_period = timings->tALH_min;
> +
> +	/* T12 <=> tRP */
> +	if (timings->tRP_min > min_clk_period)
> +		min_clk_period = timings->tRP_min;
> +
> +	/* T13 <=> tREH */
> +	if (timings->tREH_min > min_clk_period)
> +		min_clk_period = timings->tREH_min;
> +
> +	/* T14 <=> tRC */
> +	if (timings->tRC_min > (min_clk_period * 2))
> +		min_clk_period = (timings->tRC_min + 1) / 2;
> +
> +	/* T15 <=> tWC */
> +	if (timings->tWC_min > (min_clk_period * 2))
> +		min_clk_period = (timings->tWC_min + 1) / 2;
> +
> +
> +	/* min_clk_period = (NAND-clk-period * 2) */
> +	if (!min_clk_period) {
> +		chip->clk_rate = 20000000;
> +	} else {
> +		min_clk_period /= 1000;
> +		if (!min_clk_period)
> +			min_clk_period = 1;
> +		chip->clk_rate = (2 * 1000000000) / min_clk_period;
> +	}
> +
> +	/* TODO: configure T16-T19 */
> +
> +	return 0;
> +}
> +
> +static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
> +				struct device_node *np)
> +{
> +	struct sunxi_nand_chip *chip;
> +	struct mtd_part_parser_data ppdata;
> +	struct mtd_info *mtd;
> +	struct nand_chip *nand;
> +	u32 strength;
> +	u32 blk_size;
> +	int nsels;
> +	int ret;
> +	int i;
> +	u32 tmp;
> +
> +	if (!of_get_property(np, "reg", &nsels))
> +		return -EINVAL;
> +
> +	nsels /= sizeof(u32);
> +	if (!nsels)
> +		return -EINVAL;
> +
> +	chip = devm_kzalloc(dev,
> +			    sizeof(*chip) +
> +			    (nsels * sizeof(struct sunxi_nand_chip_sel)),
> +			    GFP_KERNEL);
> +	if (!chip)
> +		return -ENOMEM;
> +
> +	chip->nsels = nsels;
> +	chip->selected = -1;
> +
> +	for (i = 0; i < nsels; i++) {
> +		ret = of_property_read_u32_index(np, "reg", i, &tmp);
> +		if (ret)
> +			return ret;
> +
> +		if (tmp > 7)
> +			return -EINVAL;
> +
> +		if (test_and_set_bit(tmp, &nfc->assigned_cs))
> +			return -EINVAL;
> +
> +		chip->sels[i].cs = tmp;
> +
> +		if (!of_property_read_u32_index(np, "allwinner,rb", i, &tmp) &&
> +		    tmp < 2) {
> +			chip->sels[i].rb.type = RB_NATIVE;
> +			chip->sels[i].rb.info.nativeid = tmp;
> +		} else {
> +			ret = of_get_named_gpio(np, "rb-gpios", i);
> +			if (ret >= 0) {
> +				chip->sels[i].rb.type = RB_GPIO;
> +				chip->sels[i].rb.info.gpio = tmp;
> +				ret = devm_gpio_request(dev, tmp, "nand-rb");
> +				if (ret)
> +					return ret;

While testing the GPIO handling of R/B state, I found 2 bugs:
- the GPIO number is stored in ret not tmp
- we need to configure the GPIO as an input

Here's a patch fixing these bugs:

diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
index 7e1cefc..41fb3b8 100644
--- a/drivers/mtd/nand/sunxi_nand.c
+++ b/drivers/mtd/nand/sunxi_nand.c
@@ -814,11 +814,16 @@ static int sunxi_nand_chip_init(struct device 
*dev, struct sunxi_nfc *nfc,
                 } else {
                         ret = of_get_named_gpio(np, "rb-gpios", i);
                         if (ret >= 0) {
+                               tmp = ret;
                                 chip->sels[i].rb.type = RB_GPIO;
                                 chip->sels[i].rb.info.gpio = tmp;
                                 ret = devm_gpio_request(dev, tmp, 
"nand-rb");
                                 if (ret)
                                         return ret;
+
+                               ret = gpio_direction_input(tmp);
+                               if (ret)
+                                       return ret;
                         } else {
                                 chip->sels[i].rb.type = RB_NONE;
                         }


I'll fix it for the next version.

> +			} else {
> +				chip->sels[i].rb.type = RB_NONE;
> +			}
> +		}
> +	}
> +
> +	ret = sunxi_nand_chip_init_timings(chip, np);
> +	if (ret)
> +		return ret;
> +
> +	nand = &chip->nand;
> +	nand->controller = &nfc->controller;
> +	nand->select_chip = sunxi_nfc_select_chip;
> +	nand->cmd_ctrl = sunxi_nfc_cmd_ctrl;
> +	nand->read_buf = sunxi_nfc_read_buf;
> +	nand->write_buf = sunxi_nfc_write_buf;
> +	nand->read_byte = sunxi_nfc_read_byte;
> +
> +	nand->ecc.mode = of_get_nand_ecc_mode(np);
> +	if (of_get_nand_on_flash_bbt(np))
> +		nand->bbt_options |= NAND_BBT_USE_FLASH;
> +
> +	mtd = &chip->mtd;
> +	mtd->priv = nand;
> +	mtd->owner = THIS_MODULE;
> +
> +	ret = nand_scan_ident(mtd, nsels, NULL);
> +	if (ret)
> +		return ret;
> +
> +	if (nand->ecc.mode == NAND_ECC_SOFT_BCH) {
> +		if (!of_get_nand_ecc_level(np, &strength, &blk_size)) {
> +			nand->ecc_step_ds = blk_size;
> +			nand->ecc_strength_ds = strength;
> +		}
> +
> +		nand->ecc.size = nand->ecc_step_ds;
> +		nand->ecc.bytes = (((nand->ecc_strength_ds *
> +				     fls(8 * nand->ecc_step_ds)) + 7) / 8);
> +	}
> +
> +	ret = nand_scan_tail(mtd);
> +	if (ret)
> +		return ret;
> +
> +	if (of_property_read_string(np, "nand-name", &mtd->name)) {
> +		snprintf(chip->default_name, MAX_NAME_SIZE,
> +			 DEFAULT_NAME_FORMAT, chip->sels[i].cs);
> +		mtd->name = chip->default_name;
> +	}
> +
> +	ppdata.of_node = np;
> +	ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
> +	if (!ret)
> +		return ret;
> +
> +	list_add_tail(&chip->node, &nfc->chips);
> +
> +	return 0;
> +}
> +
> +static int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc)
> +{
> +	struct device_node *np = dev->of_node;
> +	struct device_node *nand_np;
> +	int nchips = of_get_child_count(np);
> +	int ret;
> +
> +	if (nchips > 8)
> +		return -EINVAL;
> +
> +	for_each_child_of_node(np, nand_np) {
> +		ret = sunxi_nand_chip_init(dev, nfc, nand_np);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int sunxi_nfc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct resource *r;
> +	struct sunxi_nfc *nfc;
> +	int ret;
> +
> +	nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
> +	if (!nfc) {
> +		dev_err(dev, "failed to allocate NFC struct\n");
> +		return -ENOMEM;
> +	}
> +
> +	spin_lock_init(&nfc->controller.lock);
> +	init_waitqueue_head(&nfc->controller.wq);
> +	INIT_LIST_HEAD(&nfc->chips);
> +
> +	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	nfc->regs = devm_ioremap_resource(dev, r);
> +	if (IS_ERR(nfc->regs)) {
> +		dev_err(dev, "failed to remap iomem\n");
> +		return PTR_ERR(nfc->regs);
> +	}
> +
> +	nfc->irq = platform_get_irq(pdev, 0);
> +	if (nfc->irq < 0) {
> +		dev_err(dev, "failed to retrieve irq\n");
> +		return nfc->irq;
> +	}
> +
> +	nfc->ahb_clk = devm_clk_get(dev, "ahb_clk");
> +	if (IS_ERR(nfc->ahb_clk)) {
> +		dev_err(dev, "failed to retrieve ahb_clk\n");
> +		return PTR_ERR(nfc->ahb_clk);
> +	}
> +
> +	ret = clk_prepare_enable(nfc->ahb_clk);
> +	if (ret)
> +		return ret;
> +
> +	nfc->sclk = devm_clk_get(dev, "sclk");
> +	if (IS_ERR(nfc->sclk)) {
> +		dev_err(dev, "failed to retrieve nand_clk\n");
> +		ret = PTR_ERR(nfc->sclk);
> +		goto out_ahb_clk_unprepare;
> +	}
> +
> +	ret = clk_prepare_enable(nfc->sclk);
> +	if (ret)
> +		goto out_ahb_clk_unprepare;
> +
> +	/* Reset NFC */
> +	writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RESET,
> +	       nfc->regs + NFC_REG_CTL);
> +	while (readl(nfc->regs + NFC_REG_CTL) & NFC_RESET)
> +		;
> +
> +	writel(0, nfc->regs + NFC_REG_INT);
> +	ret = devm_request_irq(dev, nfc->irq, sunxi_nfc_interrupt,
> +			       0, "sunxi-nand", nfc);
> +	if (ret)
> +		goto out_sclk_unprepare;
> +
> +	platform_set_drvdata(pdev, nfc);
> +
> +	writel(0x100, nfc->regs + NFC_REG_TIMING_CTL);
> +	writel(0x7ff, nfc->regs + NFC_REG_TIMING_CFG);
> +
> +	ret = sunxi_nand_chips_init(dev, nfc);
> +	if (ret) {
> +		dev_err(dev, "failed to init nand chips\n");
> +		goto out_sclk_unprepare;
> +	}
> +
> +	return 0;
> +
> +out_sclk_unprepare:
> +	clk_disable_unprepare(nfc->sclk);
> +out_ahb_clk_unprepare:
> +	clk_disable_unprepare(nfc->ahb_clk);
> +
> +	return ret;
> +}
> +
> +static const struct of_device_id sunxi_nfc_ids[] = {
> +	{ .compatible = "allwinner,sun4i-nand" },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, sunxi_nfc_ids);
> +
> +static struct platform_driver sunxi_nfc_driver = {
> +	.driver = {
> +		.name = "sunxi_nand",
> +		.owner = THIS_MODULE,
> +		.of_match_table = of_match_ptr(sunxi_nfc_ids),
> +	},
> +	.probe = sunxi_nfc_probe,
> +};
> +module_platform_driver(sunxi_nfc_driver);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR("Boris BREZILLON");
> +MODULE_DESCRIPTION("Allwinner NAND Flash Controller driver");
> +MODULE_ALIAS("platform:sunxi_nfc");


^ permalink raw reply related

* Re: [PATCH v5] spi: rspi: Add DT support
From: Mark Brown @ 2014-01-30 11:25 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, linux-spi,
	Linux-sh list,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Geert Uytterhoeven
In-Reply-To: <CAMuHMdX+EUUHxOgc8RF=C=qdYiOc8h+su4r-=HAhWXUqEivSvw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

[-- Attachment #1: Type: text/plain, Size: 671 bytes --]

On Wed, Jan 29, 2014 at 08:10:34PM +0100, Geert Uytterhoeven wrote:
> On Wed, Jan 29, 2014 at 7:00 PM, Mark Brown <broonie-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:

> > well?

> As there were no code changes in v5 of "spi: rspi: Add DT support",
> Patch "[14/14] spi: rspi: Add support for Quad and Dual SPI Transfers on QSPI"
> should still apply fine.

> Do you want me to resubmit?

I see you did actually resend before I saw this but yes, in general it's
better to resend stuff.  If you start applying version X of one patch
with version Y of another it's more work and there's a lot of things
that can go wrong between user error and unexpected dependencies.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* [PATCH] ARM: keystone: dts: disable "msmcsram" clock
From: Ivan Khoronzhuk @ 2014-01-30 11:45 UTC (permalink / raw)
  To: robh+dt, galak
  Cc: mark.rutland, devicetree, linux, pawel.moll, ijc+devicetree,
	linux-kernel, santosh.shilimkar, Ivan Khoronzhuk,
	linux-arm-kernel

At late init all unused clocks are disabled. So clocks that were not
get before will be gated. In Keysone 2 SoC we have at least one
necessary clock that is not used by any driver - "msmcsram". This
clock is necessary, because it supplies the Multicore Shared Memory
Controller (MSMC). The MSMC provides memory protection for accesses to
the MSMC SRAM and DDR3 memory from system masters. It also manages
traffic among mastering peripherals and the EMIF.

This means that MSMC clock is always needed by SoC and cannot be gated.
It is only one from necessary clocks that was not used by any driver.
So to avoid its gating at late init we have to disable it in DT.

Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>
---
 arch/arm/boot/dts/keystone-clocks.dtsi | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/boot/dts/keystone-clocks.dtsi b/arch/arm/boot/dts/keystone-clocks.dtsi
index 2363593..e7aea2e 100644
--- a/arch/arm/boot/dts/keystone-clocks.dtsi
+++ b/arch/arm/boot/dts/keystone-clocks.dtsi
@@ -332,6 +332,7 @@ clocks {
 		compatible = "ti,keystone,psc-clock";
 		clocks = <&chipclk1>;
 		clock-output-names = "msmcsram";
+		status = "disabled";
 		reg = <0x02350038 0xb00>, <0x0235001c 0x400>;
 		reg-names = "control", "domain";
 		domain-id = <7>;
-- 
1.8.3.2

^ permalink raw reply related

* Re: [patch] mmc: omap_hsmmc: precedence bug in omap_hsmmc_context_restore()
From: Dan Carpenter @ 2014-01-30 12:15 UTC (permalink / raw)
  To: Balaji T K
  Cc: Chris Ball, Grant Likely, Rob Herring, linux-mmc, linux-omap,
	devicetree, kernel-janitors
In-Reply-To: <52162444.1050908@ti.com>

On Thu, Aug 22, 2013 at 08:16:28PM +0530, Balaji T K wrote:
> On Thursday 22 August 2013 06:26 PM, Dan Carpenter wrote:
> >'!' has higher precedence than '&' so this doesn't work as intended
> >although since RESETDONE is 1 it would work if none of the other bits
> >are set.
> >
> Hi Dan,
> 
> Thanks for the patch, Indeed other bits are reserved.
> however ...
> 
> >Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
> >---
> >Untested.
> >
> >diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
> >index 1865321..7346b15 100644
> >--- a/drivers/mmc/host/omap_hsmmc.c
> >+++ b/drivers/mmc/host/omap_hsmmc.c
> >@@ -612,7 +612,7 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
> >  	if (host->context_loss == context_loss)
> >  		return 1;
> >
> >-	if (!OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE)
> >+	if (!(OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE))
> >  		return 1;
> 
> This check is unnecessary, will send a patch to remove this.

What happened with this?

regards,
dan carpenter


^ permalink raw reply

* [RFC] dtc: add ability to make nodes conditional on them being referenced
From: Heiko Stübner @ 2014-01-30 12:25 UTC (permalink / raw)
  To: Grant Likely
  Cc: Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Shawn Guo,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

From: Heiko Stuebner <heiko.stuebner-HCpLIkUQxWGakBO8gow8eQ@public.gmane.org>

On i.MX, which carries a lot of pin-groups of which most are unused on
individual boards, they noticed that this plehora of nodes also results
in the runtime-lookup-performance also degrading [0].

A i.MX-specific solution defining the pingroups in the board files but
using macros to reference the pingroup-data was not well received

This patch is trying to solve this issue in a more general way, by
adding the ability to mark nodes as needing to be referenced somewhere
in the tree.

To mark a node a needing to be referenced it must be prefixed with
/delete-unreferenced/. This makes dtc check the nodes reference-status
when creating the flattened tree, dropping it if unreferenced.

For example, the i.MX6SL pingroup

	/delete-uneferenced/ pinctrl_ecspi1_1: ecspi1grp-1 {
		fsl,pins = <
			MX6SL_PAD_ECSPI1_MISO__ECSPI1_MISO 0x100b1
			MX6SL_PAD_ECSPI1_MOSI__ECSPI1_MOSI 0x100b1
			MX6SL_PAD_ECSPI1_SCLK__ECSPI1_SCLK 0x100b1
		>;
	};

would only be included in the dtb if it got referenced somewhere
as pingroup via

	node {
		pinctrl-0 <&pinctrl_ecscpi1_1>;
	};

[0] http://thread.gmane.org/gmane.linux.ports.arm.kernel/275912/

Signed-off-by: Heiko Stuebner <heiko.stuebner-HCpLIkUQxWGakBO8gow8eQ@public.gmane.org>
---
This is just the idea I had in [1] explored a bit more. I'm definitely
not sure if this is a valid approach to the problem.
Also this is my first venture into dtc as well as flex and bison :-) .

[1] http://www.spinics.net/lists/arm-kernel/msg303731.html

 scripts/dtc/checks.c     |    2 ++
 scripts/dtc/dtc-lexer.l  |    7 +++++++
 scripts/dtc/dtc-parser.y |    5 +++++
 scripts/dtc/dtc.h        |    4 ++++
 scripts/dtc/flattree.c   |    3 +++
 scripts/dtc/livetree.c   |   14 ++++++++++++++
 6 files changed, 35 insertions(+)

diff --git a/scripts/dtc/checks.c b/scripts/dtc/checks.c
index ee96a25..747ada8 100644
--- a/scripts/dtc/checks.c
+++ b/scripts/dtc/checks.c
@@ -472,6 +472,8 @@ static void fixup_phandle_references(struct check *c, struct node *dt,
 
 		phandle = get_node_phandle(dt, refnode);
 		*((cell_t *)(prop->val.val + m->offset)) = cpu_to_fdt32(phandle);
+
+		reference_node(refnode);
 	}
 }
 ERROR(phandle_references, NULL, NULL, fixup_phandle_references, NULL,
diff --git a/scripts/dtc/dtc-lexer.l b/scripts/dtc/dtc-lexer.l
index 3b41bfc..3b18e97 100644
--- a/scripts/dtc/dtc-lexer.l
+++ b/scripts/dtc/dtc-lexer.l
@@ -138,6 +138,13 @@ static int pop_input_file(void);
 			return DT_DEL_NODE;
 		}
 
+<*>"/delete-unreferenced/"	{
+			DPRINT("Keyword: /delete-unreferenced/\n");
+			DPRINT("<PROPNODENAME>\n");
+			BEGIN(PROPNODENAME);
+			return DT_DEL_UNREFERENCED;
+		}
+
 <*>{LABEL}:	{
 			DPRINT("Label: %s\n", yytext);
 			yylval.labelref = xstrdup(yytext);
diff --git a/scripts/dtc/dtc-parser.y b/scripts/dtc/dtc-parser.y
index f412460..ae9108b 100644
--- a/scripts/dtc/dtc-parser.y
+++ b/scripts/dtc/dtc-parser.y
@@ -64,6 +64,7 @@ static unsigned char eval_char_literal(const char *s);
 %token DT_BITS
 %token DT_DEL_PROP
 %token DT_DEL_NODE
+%token DT_DEL_UNREFERENCED
 %token <propnodename> DT_PROPNODENAME
 %token <literal> DT_LITERAL
 %token <literal> DT_CHAR_LITERAL
@@ -461,6 +462,10 @@ subnode:
 		{
 			$$ = name_node(build_node_delete(), $2);
 		}
+	| DT_DEL_UNREFERENCED subnode
+		{
+			$$ = check_node_referenced($2);
+		}
 	| DT_LABEL subnode
 		{
 			add_label(&$2->labels, $1);
diff --git a/scripts/dtc/dtc.h b/scripts/dtc/dtc.h
index 3e42a07..c10c440 100644
--- a/scripts/dtc/dtc.h
+++ b/scripts/dtc/dtc.h
@@ -159,6 +159,8 @@ struct node {
 	int addr_cells, size_cells;
 
 	struct label *labels;
+
+	int needs_reference, is_referenced;
 };
 
 #define for_each_label_withdel(l0, l) \
@@ -193,6 +195,8 @@ struct property *reverse_properties(struct property *first);
 struct node *build_node(struct property *proplist, struct node *children);
 struct node *build_node_delete(void);
 struct node *name_node(struct node *node, char *name);
+struct node *check_node_referenced(struct node *node);
+struct node *reference_node(struct node *node);
 struct node *chain_node(struct node *first, struct node *list);
 struct node *merge_nodes(struct node *old_node, struct node *new_node);
 
diff --git a/scripts/dtc/flattree.c b/scripts/dtc/flattree.c
index 665dad7..a327592 100644
--- a/scripts/dtc/flattree.c
+++ b/scripts/dtc/flattree.c
@@ -266,6 +266,9 @@ static void flatten_tree(struct node *tree, struct emitter *emit,
 	if (tree->deleted)
 		return;
 
+	if (tree->needs_reference && !tree->is_referenced)
+		return;
+
 	emit->beginnode(etarget, tree->labels);
 
 	if (vi->flags & FTF_FULLPATH)
diff --git a/scripts/dtc/livetree.c b/scripts/dtc/livetree.c
index b61465f..98bb33d 100644
--- a/scripts/dtc/livetree.c
+++ b/scripts/dtc/livetree.c
@@ -134,6 +134,20 @@ struct node *name_node(struct node *node, char *name)
 	return node;
 }
 
+struct node *check_node_referenced(struct node *node)
+{
+	node->needs_reference = 1;
+
+	return node;
+}
+
+struct node *reference_node(struct node *node)
+{
+	node->is_referenced = 1;
+
+	return node;
+}
+
 struct node *merge_nodes(struct node *old_node, struct node *new_node)
 {
 	struct property *new_prop, *old_prop;
-- 
1.7.10.4


--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* Re: [RFC] dtc: add ability to make nodes conditional on them being referenced
From: Lothar Waßmann @ 2014-01-30 12:44 UTC (permalink / raw)
  To: Heiko Stübner
  Cc: Grant Likely, devicetree, Rob Herring, linux-kernel,
	linux-arm-kernel, Shawn Guo
In-Reply-To: <1578575.rVWgTPdq1W@phil>

Hi,

Heiko Stübner wrote:
> From: Heiko Stuebner <heiko.stuebner@bqreaders.com>
> 
> On i.MX, which carries a lot of pin-groups of which most are unused on
> individual boards, they noticed that this plehora of nodes also results
> in the runtime-lookup-performance also degrading [0].
> 
> A i.MX-specific solution defining the pingroups in the board files but
> using macros to reference the pingroup-data was not well received
> 
> This patch is trying to solve this issue in a more general way, by
> adding the ability to mark nodes as needing to be referenced somewhere
> in the tree.
> 
> To mark a node a needing to be referenced it must be prefixed with
> /delete-unreferenced/. This makes dtc check the nodes reference-status
> when creating the flattened tree, dropping it if unreferenced.
> 
> For example, the i.MX6SL pingroup
> 
> 	/delete-uneferenced/ pinctrl_ecspi1_1: ecspi1grp-1 {
                 ^^
s/unef/unref/
not really important here, but bad for copy/pasters


Lothar Waßmann
-- 
___________________________________________________________

Ka-Ro electronics GmbH | Pascalstraße 22 | D - 52076 Aachen
Phone: +49 2408 1402-0 | Fax: +49 2408 1402-10
Geschäftsführer: Matthias Kaussen
Handelsregistereintrag: Amtsgericht Aachen, HRB 4996

www.karo-electronics.de | info@karo-electronics.de
___________________________________________________________

^ permalink raw reply

* Re: [patch] mmc: omap_hsmmc: precedence bug in omap_hsmmc_context_restore()
From: Balaji T K @ 2014-01-30 13:04 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Chris Ball, Grant Likely, Rob Herring, linux-mmc, linux-omap,
	devicetree, kernel-janitors
In-Reply-To: <20140130121526.GO4815@mwanda>

On Thursday 30 January 2014 05:45 PM, Dan Carpenter wrote:
> On Thu, Aug 22, 2013 at 08:16:28PM +0530, Balaji T K wrote:
>> On Thursday 22 August 2013 06:26 PM, Dan Carpenter wrote:
>>> '!' has higher precedence than '&' so this doesn't work as intended
>>> although since RESETDONE is 1 it would work if none of the other bits
>>> are set.
>>>
>> Hi Dan,
>>
>> Thanks for the patch, Indeed other bits are reserved.
>> however ...
>>
>>> Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
>>> ---
>>> Untested.
>>>
>>> diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
>>> index 1865321..7346b15 100644
>>> --- a/drivers/mmc/host/omap_hsmmc.c
>>> +++ b/drivers/mmc/host/omap_hsmmc.c
>>> @@ -612,7 +612,7 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
>>>   	if (host->context_loss == context_loss)
>>>   		return 1;
>>>
>>> -	if (!OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE)
>>> +	if (!(OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE))
>>>   		return 1;
>>
>> This check is unnecessary, will send a patch to remove this.
>
> What happened with this?

My bad, I missed it, will fix it.

Thanks and Regards,
Balaji T K

^ permalink raw reply

* Re: [PATCH v2 0/5] Smart Card(SC) interface, TI USIM & NxP SC phy driver
From: Greg KH @ 2014-01-30 13:05 UTC (permalink / raw)
  To: Satish Patel
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-omap-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, rob-VoJi6FS/r0vR7s880joybQ,
	tony-4v6yS6AI5VpBDgjK7y7TUQ, grant.likely-QSEj5FYQhm4dnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A
In-Reply-To: <52E9E8B0.2020000-l0cyMroinI0@public.gmane.org>

On Thu, Jan 30, 2014 at 11:22:48AM +0530, Satish Patel wrote:
> 
> On 1/20/2014 10:03 AM, Satish Patel wrote:
> > Changes from v1:
> > * RFC(v1) comments are fixed
> >
> > ** removed "gpio_to_irq" as GPIO controller process  cell from DT and
> > give it to DT node
> > ** comments on documentation
> > ** few other comments on null checks are resolved
> >
> > * BWT timing configuration is added to ti-usim driver
> >
> > v1 cover letter link#
> > https://lkml.org/lkml/2014/1/6/250
> >
> > Satish Patel (5):
> >   sc_phy:SmartCard(SC) PHY interface to SC controller
> >   misc: tda8026: Add NXP TDA8026 PHY driver
> >   char: ti-usim: Add driver for USIM module on AM43xx
> >   ARM: dts: AM43xx: DT entries added for ti-usim
> >   ARM: dts: AM43xx-epos-evm: DT entries  for ti-usim and phy
> >
> >  Documentation/devicetree/bindings/misc/tda8026.txt |   19 +
> >  .../devicetree/bindings/ti-usim/ti-usim.txt        |   31 +
> >  Documentation/sc_phy.txt                           |  171 ++
> >  arch/arm/boot/dts/am4372.dtsi                      |   10 +
> >  arch/arm/boot/dts/am43x-epos-evm.dts               |   43 +
> >  drivers/char/Kconfig                               |    7 +
> >  drivers/char/Makefile                              |    1 +
> >  drivers/char/ti-usim-hw.h                          |  863 +++++++++
> >  drivers/char/ti-usim.c                             | 1859 ++++++++++++++++++++
> >  drivers/misc/Kconfig                               |    7 +
> >  drivers/misc/Makefile                              |    1 +
> >  drivers/misc/tda8026.c                             | 1255 +++++++++++++
> >  include/linux/sc_phy.h                             |  132 ++
> >  include/linux/ti-usim.h                            |   98 +
> >  14 files changed, 4497 insertions(+), 0 deletions(-)
> >  create mode 100644 Documentation/devicetree/bindings/misc/tda8026.txt
> >  create mode 100644 Documentation/devicetree/bindings/ti-usim/ti-usim.txt
> >  create mode 100644 Documentation/sc_phy.txt
> >  create mode 100644 drivers/char/ti-usim-hw.h
> >  create mode 100644 drivers/char/ti-usim.c
> >  create mode 100644 drivers/misc/tda8026.c
> >  create mode 100644 include/linux/sc_phy.h
> >  create mode 100644 include/linux/ti-usim.h
> Any comments on this patch series ?
> 
> If not,
> Can you accept these patches for next merge window

It's the middle of this merge window, and I can't accept any patches
until after 3.14-rc1 is out, at which point I'll start to work on my
patch backlog.

thanks,

greg k-h
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH V3 6/8] SPEAr13xx: Fixup: Move SPEAr1340 SATA platform code to phy driver
From: Arnd Bergmann @ 2014-01-30 13:21 UTC (permalink / raw)
  To: Mohit Kumar
  Cc: Pratyush Anand, Viresh Kumar, Tejun Heo, Kishon Vijay Abraham I,
	spear-devel, linux-arm-kernel, devicetree, linux-ide
In-Reply-To: <7b9e0a98a6873f66e519791dfba20418e2a68c5a.1391077731.git.mohit.kumar@st.com>

On Thursday 30 January 2014, Mohit Kumar wrote:
> 
> diff --git a/Documentation/devicetree/bindings/phy/spear13xx-miphy.txt b/Documentation/devicetree/bindings/phy/spear13xx-miphy.txt
> new file mode 100644
> index 0000000..208b37d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/phy/spear13xx-miphy.txt
> @@ -0,0 +1,8 @@
> +Required properties:
> +- compatible : should be "st,spear1340-sata-pcie-phy".

Just for confirmation: This phy is by design only capable of driving
sata or pcie, but nothing else if reused in a different SoC, right?

If the phy is actually more generic than that, I'd suggest changing
the name, otherwise it's ok.

> +- reg : offset and length of the PHY register set.
> +- misc: phandle for the syscon node to access misc registers
> +- #phy-cells : from the generic PHY bindings, must be 2.
> +	- 1st arg: phandle to the phy node.
> +	- 2nd arg: 0 if phy (in 1st arg) is to be used for sata else 1.
> +	- 3rd arg: Instance id of the phy (in 1st arg).

I would count "arg" differently: There are three cells, and the first
cell is the phandle, while the second and third cells contain the first
and second argument.

The third cell seems redundant, more on that below.

> +		ahci0: ahci@b1000000 {
>  			compatible = "snps,spear-ahci";
>  			reg = <0xb1000000 0x10000>;
>  			interrupts = <0 68 0x4>;
> +			phys = <&miphy0 0 0>;
> +			phy-names = "ahci-phy";
>  			status = "disabled";
>  		};
>  
> -		ahci@b1800000 {
> +		ahci1: ahci@b1800000 {
>  			compatible = "snps,spear-ahci";
>  			reg = <0xb1800000 0x10000>;
>  			interrupts = <0 69 0x4>;
> +			phys = <&miphy1 0 1>;
> +			phy-names = "ahci-phy";
>  			status = "disabled";
>  		};
>  
> -		ahci@b4000000 {
> +		ahci2: ahci@b4000000 {
>  			compatible = "snps,spear-ahci";
>  			reg = <0xb4000000 0x10000>;
>  			interrupts = <0 70 0x4>;
> +			phys = <&miphy2 0 2>;
> +			phy-names = "ahci-phy";
>  			status = "disabled";
>  		};

In each case, the number of the phy 'miphyX' is identical to the
third cell, and I suspect this is by design. In the driver, the
'id' field is set in the xlate function, but I could not find any
place where it actually gets used, so unless you know that it's
needed, I'd suggest simply removing it.

Even if you need it, it may be better to have the instance encoded
in the phy node itself, since it's a property of the phy hardware
(e.g. if you have to pass the number into a generic register that
is global to all phys.

Alternatively, you could have a different representation, where you
have a single DT device node representing all three PHYs, with
"reg = <0xeb800000 0xc000>;" In that case, all sata devices would
point to the same phy node and pass the instance id so the phy
driver can operated the correct register set.

> +static int spear1340_sata_miphy_init(struct spear13xx_phy_priv *phypriv)
> +{
> +	regmap_update_bits(phypriv->misc, SPEAR1340_PCIE_SATA_CFG,
> +			SPEAR1340_PCIE_SATA_CFG_MASK, SPEAR1340_SATA_CFG_VAL);
> +	regmap_update_bits(phypriv->misc, SPEAR1340_PCIE_MIPHY_CFG,
> +			SPEAR1340_PCIE_MIPHY_CFG_MASK,
> +			SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK);
> +	/* Switch on sata power domain */
> +	regmap_update_bits(phypriv->misc, SPEAR1340_PCM_CFG,
> +			SPEAR1340_PCM_CFG_SATA_POWER_EN,
> +			SPEAR1340_PCM_CFG_SATA_POWER_EN);
> +	msleep(20);
> +	/* Disable PCIE SATA Controller reset */
> +	regmap_update_bits(phypriv->misc, SPEAR1340_PERIP1_SW_RST,
> +			SPEAR1340_PERIP1_SW_RST_SATA, 0);
> +	msleep(20);
> +
> +	return 0;
> +}

I guess some of the parts above can eventually get moved into other
drivers (reset controller, power domains) that get called directly
by the SATA driver (e.g. though reset_device()). Since that won't
impact the PHY binding, it seems fine to leave it here for now.

	Arnd

^ permalink raw reply

* RE: [PATCH] ARM: keystone: dts: disable "msmcsram" clock
From: Shilimkar, Santosh @ 2014-01-30 13:25 UTC (permalink / raw)
  To: robh+dt@kernel.org, galak@codeaurora.org, Khoronzhuk, Ivan
  Cc: pawel.moll@arm.com, mark.rutland@arm.com,
	ijc+devicetree@hellion.org.uk, linux@arm.linux.org.uk,
	devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org
In-Reply-To: <1391082343-13371-1-git-send-email-ivan.khoronzhuk@ti.com>

[-- Attachment #1: Type: text/plain, Size: 2336 bytes --]

Disable is not good idea since it conveys wrong info....

Hyperlink case was different.

Sent from my Android phone using TouchDown (www.nitrodesk.com)

-----Original Message-----
From: Khoronzhuk, Ivan [ivan.khoronzhuk@ti.com]
Received: Thursday, 30 Jan 2014, 6:45am
To: robh+dt@kernel.org [robh+dt@kernel.org]; galak@codeaurora.org [galak@codeaurora.org]
CC: pawel.moll@arm.com [pawel.moll@arm.com]; mark.rutland@arm.com [mark.rutland@arm.com]; ijc+devicetree@hellion.org.uk [ijc+devicetree@hellion.org.uk]; linux@arm.linux.org.uk [linux@arm.linux.org.uk]; devicetree@vger.kernel.org [devicetree@vger.kernel.org]; linux-arm-kernel@lists.infradead.org [linux-arm-kernel@lists.infradead.org]; linux-kernel@vger.kernel.org [linux-kernel@vger.kernel.org]; Shilimkar, Santosh [santosh.shilimkar@ti.com]; Khoronzhuk, Ivan [ivan.khoronzhuk@ti.com]
Subject: [PATCH] ARM: keystone: dts: disable "msmcsram" clock

At late init all unused clocks are disabled. So clocks that were not
get before will be gated. In Keysone 2 SoC we have at least one
necessary clock that is not used by any driver - "msmcsram". This
clock is necessary, because it supplies the Multicore Shared Memory
Controller (MSMC). The MSMC provides memory protection for accesses to
the MSMC SRAM and DDR3 memory from system masters. It also manages
traffic among mastering peripherals and the EMIF.

This means that MSMC clock is always needed by SoC and cannot be gated.
It is only one from necessary clocks that was not used by any driver.
So to avoid its gating at late init we have to disable it in DT.

Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>
---
 arch/arm/boot/dts/keystone-clocks.dtsi | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/boot/dts/keystone-clocks.dtsi b/arch/arm/boot/dts/keystone-clocks.dtsi
index 2363593..e7aea2e 100644
--- a/arch/arm/boot/dts/keystone-clocks.dtsi
+++ b/arch/arm/boot/dts/keystone-clocks.dtsi
@@ -332,6 +332,7 @@ clocks {
                 compatible = "ti,keystone,psc-clock";
                 clocks = <&chipclk1>;
                 clock-output-names = "msmcsram";
+               status = "disabled";
                 reg = <0x02350038 0xb00>, <0x0235001c 0x400>;
                 reg-names = "control", "domain";
                 domain-id = <7>;
--
1.8.3.2


[-- Attachment #2: Type: text/html, Size: 3910 bytes --]

^ permalink raw reply related

* [RFC PATCH pre-v3 08/14] mtd: nand: add sunxi NAND flash controller support
From: Boris BREZILLON @ 2014-01-30 13:39 UTC (permalink / raw)
  To: Maxime Ripard, Rob Landley, Russell King, David Woodhouse,
	Grant Likely, Brian Norris, Jason Gunthorpe, Arnd Bergmann
  Cc: Boris BREZILLON, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	dev-3kdeTeqwOZ9EV1b7eY7vFQ
In-Reply-To: <1391006064-28890-1-git-send-email-b.brezillon.dev-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

Add support for the sunxi NAND Flash Controller (NFC).

Signed-off-by: Boris BREZILLON <b.brezillon.dev-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
Hello,

This version fixes a bug in the R/B GPIO config block.
The timing config order is now respected, but I'll wait for Jason work
regarding timing config in NAND core code before posting the 3rd version
of this series.

Best Regards,

Boris

Changes since v2:
 - fix R/B GPIO retrieval/config bug
 - fix timings configuration order (set mode 0 -> scan -> set best supported
   mode)

 drivers/mtd/nand/Kconfig      |    6 +
 drivers/mtd/nand/Makefile     |    1 +
 drivers/mtd/nand/sunxi_nand.c |  758 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 765 insertions(+)
 create mode 100644 drivers/mtd/nand/sunxi_nand.c

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 93ae6a6..784dd42 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -510,4 +510,10 @@ config MTD_NAND_XWAY
 	  Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached
 	  to the External Bus Unit (EBU).
 
+config MTD_NAND_SUNXI
+	tristate "Support for NAND on Allwinner SoCs"
+	depends on ARCH_SUNXI
+	help
+	  Enables support for NAND Flash chips on Allwinner SoCs.
+
 endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index bbea7a6..e3b4a34 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -49,5 +49,6 @@ obj-$(CONFIG_MTD_NAND_JZ4740)		+= jz4740_nand.o
 obj-$(CONFIG_MTD_NAND_GPMI_NAND)	+= gpmi-nand/
 obj-$(CONFIG_MTD_NAND_XWAY)		+= xway_nand.o
 obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH)	+= bcm47xxnflash/
+obj-$(CONFIG_MTD_NAND_SUNXI)		+= sunxi_nand.o
 
 nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
new file mode 100644
index 0000000..1014b2a
--- /dev/null
+++ b/drivers/mtd/nand/sunxi_nand.c
@@ -0,0 +1,758 @@
+/*
+ * Copyright (C) 2013 Boris BREZILLON <b.brezillon.dev-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ *
+ * Derived from:
+ *	https://github.com/yuq/sunxi-nfc-mtd
+ *	Copyright (C) 2013 Qiang Yu <yuq825-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ *
+ *	https://github.com/hno/Allwinner-Info
+ *	Copyright (C) 2013 Henrik Nordström <Henrik Nordström>
+ *
+ *	Copyright (C) 2013 Dmitriy B. <rzk333-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ *	Copyright (C) 2013 Sergey Lapin <slapin-9cOl001CZnBAfugRpC6u6w@public.gmane.org>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_mtd.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+#define NFC_REG_CTL		0x0000
+#define NFC_REG_ST		0x0004
+#define NFC_REG_INT		0x0008
+#define NFC_REG_TIMING_CTL	0x000C
+#define NFC_REG_TIMING_CFG	0x0010
+#define NFC_REG_ADDR_LOW	0x0014
+#define NFC_REG_ADDR_HIGH	0x0018
+#define NFC_REG_SECTOR_NUM	0x001C
+#define NFC_REG_CNT		0x0020
+#define NFC_REG_CMD		0x0024
+#define NFC_REG_RCMD_SET	0x0028
+#define NFC_REG_WCMD_SET	0x002C
+#define NFC_REG_IO_DATA		0x0030
+#define NFC_REG_ECC_CTL		0x0034
+#define NFC_REG_ECC_ST		0x0038
+#define NFC_REG_DEBUG		0x003C
+#define NFC_REG_ECC_CNT0	0x0040
+#define NFC_REG_ECC_CNT1	0x0044
+#define NFC_REG_ECC_CNT2	0x0048
+#define NFC_REG_ECC_CNT3	0x004c
+#define NFC_REG_USER_DATA_BASE	0x0050
+#define NFC_REG_SPARE_AREA	0x00A0
+#define NFC_RAM0_BASE		0x0400
+#define NFC_RAM1_BASE		0x0800
+
+/*define bit use in NFC_CTL*/
+#define NFC_EN				(1 << 0)
+#define NFC_RESET			(1 << 1)
+#define NFC_BUS_WIDYH			(1 << 2)
+#define NFC_RB_SEL			(1 << 3)
+#define NFC_CE_SEL			(7 << 24)
+#define NFC_CE_CTL			(1 << 6)
+#define NFC_CE_CTL1			(1 << 7)
+#define NFC_PAGE_SIZE			(0xf << 8)
+#define NFC_SAM				(1 << 12)
+#define NFC_RAM_METHOD			(1 << 14)
+#define NFC_DEBUG_CTL			(1 << 31)
+
+/*define bit use in NFC_ST*/
+#define NFC_RB_B2R			(1 << 0)
+#define NFC_CMD_INT_FLAG		(1 << 1)
+#define NFC_DMA_INT_FLAG		(1 << 2)
+#define NFC_CMD_FIFO_STATUS		(1 << 3)
+#define NFC_STA				(1 << 4)
+#define NFC_NATCH_INT_FLAG		(1 << 5)
+#define NFC_RB_STATE0			(1 << 8)
+#define NFC_RB_STATE1			(1 << 9)
+#define NFC_RB_STATE2			(1 << 10)
+#define NFC_RB_STATE3			(1 << 11)
+
+/*define bit use in NFC_INT*/
+#define NFC_B2R_INT_ENABLE		(1 << 0)
+#define NFC_CMD_INT_ENABLE		(1 << 1)
+#define NFC_DMA_INT_ENABLE		(1 << 2)
+#define NFC_INT_MASK			(NFC_B2R_INT_ENABLE | \
+					 NFC_CMD_INT_ENABLE | \
+					 NFC_DMA_INT_ENABLE)
+
+
+/*define bit use in NFC_CMD*/
+#define NFC_CMD_LOW_BYTE		(0xff << 0)
+#define NFC_CMD_HIGH_BYTE		(0xff << 8)
+#define NFC_ADR_NUM			(0x7 << 16)
+#define NFC_SEND_ADR			(1 << 19)
+#define NFC_ACCESS_DIR			(1 << 20)
+#define NFC_DATA_TRANS			(1 << 21)
+#define NFC_SEND_CMD1			(1 << 22)
+#define NFC_WAIT_FLAG			(1 << 23)
+#define NFC_SEND_CMD2			(1 << 24)
+#define NFC_SEQ				(1 << 25)
+#define NFC_DATA_SWAP_METHOD		(1 << 26)
+#define NFC_ROW_AUTO_INC		(1 << 27)
+#define NFC_SEND_CMD3			(1 << 28)
+#define NFC_SEND_CMD4			(1 << 29)
+#define NFC_CMD_TYPE			(3 << 30)
+
+/* define bit use in NFC_RCMD_SET*/
+#define NFC_READ_CMD			(0xff << 0)
+#define NFC_RANDOM_READ_CMD0		(0xff << 8)
+#define NFC_RANDOM_READ_CMD1		(0xff << 16)
+
+/*define bit use in NFC_WCMD_SET*/
+#define NFC_PROGRAM_CMD			(0xff << 0)
+#define NFC_RANDOM_WRITE_CMD		(0xff << 8)
+#define NFC_READ_CMD0			(0xff << 16)
+#define NFC_READ_CMD1			(0xff << 24)
+
+/*define bit use in NFC_ECC_CTL*/
+#define NFC_ECC_EN			(1 << 0)
+#define NFC_ECC_PIPELINE		(1 << 3)
+#define NFC_ECC_EXCEPTION		(1 << 4)
+#define NFC_ECC_BLOCK_SIZE		(1 << 5)
+#define NFC_RANDOM_EN			(1 << 9)
+#define NFC_RANDOM_DIRECTION		(1 << 10)
+#define NFC_ECC_MODE_SHIFT		12
+#define NFC_ECC_MODE			(0xf << NFC_ECC_MODE_SHIFT)
+#define NFC_RANDOM_SEED			(0x7fff << 16)
+
+
+
+enum sunxi_nand_rb_type {
+	RB_NONE,
+	RB_NATIVE,
+	RB_GPIO,
+};
+
+struct sunxi_nand_rb {
+	enum sunxi_nand_rb_type type;
+	union {
+		int gpio;
+		int nativeid;
+	} info;
+};
+
+struct sunxi_nand_chip_sel {
+	u8 cs;
+	struct sunxi_nand_rb rb;
+};
+
+#define DEFAULT_NAME_FORMAT	"nand@%d"
+#define MAX_NAME_SIZE		(sizeof("nand@") + 2)
+
+struct sunxi_nand_chip {
+	struct list_head node;
+	struct nand_chip nand;
+	struct mtd_info mtd;
+	char default_name[MAX_NAME_SIZE];
+	unsigned long clk_rate;
+	int selected;
+	int nsels;
+	struct sunxi_nand_chip_sel sels[0];
+};
+
+static inline struct sunxi_nand_chip *to_sunxi_nand(struct mtd_info *mtd)
+{
+	return container_of(mtd, struct sunxi_nand_chip, mtd);
+}
+
+struct sunxi_nfc {
+	struct nand_hw_control controller;
+	void __iomem *regs;
+	int irq;
+	struct clk *ahb_clk;
+	struct clk *sclk;
+	unsigned long assigned_cs;
+	unsigned long clk_rate;
+	struct list_head chips;
+	struct completion complete;
+};
+
+static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_hw_control *ctrl)
+{
+	return container_of(ctrl, struct sunxi_nfc, controller);
+}
+
+static irqreturn_t sunxi_nfc_interrupt(int irq, void *dev_id)
+{
+	struct sunxi_nfc *nfc = dev_id;
+	u32 st = readl(nfc->regs + NFC_REG_ST);
+	u32 ien = readl(nfc->regs + NFC_REG_INT);
+
+	if (!(ien & st))
+		return IRQ_NONE;
+
+	if ((ien & st) == ien)
+		complete(&nfc->complete);
+
+	writel(st & NFC_INT_MASK, nfc->regs + NFC_REG_ST);
+	writel(~st & ien & NFC_INT_MASK, nfc->regs + NFC_REG_INT);
+
+	return IRQ_HANDLED;
+}
+
+static int sunxi_nfc_wait_int(struct sunxi_nfc *nfc, u32 flags,
+			      unsigned int timeout_ms)
+{
+	init_completion(&nfc->complete);
+
+	writel(flags, nfc->regs + NFC_REG_INT);
+	if (!timeout_ms)
+		wait_for_completion(&nfc->complete);
+	else if (!wait_for_completion_timeout(&nfc->complete,
+					      msecs_to_jiffies(timeout_ms)))
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+static int sunxi_nfc_dev_ready(struct mtd_info *mtd)
+{
+	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(mtd);
+	struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+	struct sunxi_nand_rb *rb;
+	unsigned long timeo = (sunxi_nand->nand.state == FL_ERASING ? 400 : 20);
+	int ret;
+
+	if (sunxi_nand->selected < 0)
+		return 0;
+
+	rb = &sunxi_nand->sels[sunxi_nand->selected].rb;
+
+	switch (rb->type) {
+	case RB_NATIVE:
+		ret = !!(readl(nfc->regs + NFC_REG_ST) &
+			 (NFC_RB_STATE0 << rb->info.nativeid));
+		if (ret)
+			break;
+
+		sunxi_nfc_wait_int(nfc, NFC_RB_B2R, timeo);
+		ret = !!(readl(nfc->regs + NFC_REG_ST) &
+			 (NFC_RB_STATE0 << rb->info.nativeid));
+		break;
+	case RB_GPIO:
+		ret = gpio_get_value(rb->info.gpio);
+		break;
+	case RB_NONE:
+	default:
+		ret = 0;
+		dev_err(&mtd->dev, "cannot check R/B NAND status!");
+		break;
+	}
+
+	return ret;
+}
+
+static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip)
+{
+	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(mtd);
+	struct nand_chip *nand = &sunxi_nand->nand;
+	struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+	struct sunxi_nand_chip_sel *sel;
+	u32 ctl;
+
+	if (chip > 0 && chip >= sunxi_nand->nsels)
+		return;
+
+	if (chip == sunxi_nand->selected)
+		return;
+
+	ctl = readl(nfc->regs + NFC_REG_CTL) &
+	      ~(NFC_CE_SEL | NFC_RB_SEL | NFC_EN);
+
+	if (chip >= 0) {
+		sel = &sunxi_nand->sels[chip];
+
+		ctl |= (sel->cs << 24) | NFC_EN |
+		       (((nand->page_shift - 10) & 0xf) << 8);
+		if (sel->rb.type == RB_NONE) {
+			nand->dev_ready = NULL;
+		} else {
+			nand->dev_ready = sunxi_nfc_dev_ready;
+			if (sel->rb.type == RB_NATIVE)
+				ctl |= (sel->rb.info.nativeid << 3);
+		}
+
+		writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA);
+
+		if (nfc->clk_rate != sunxi_nand->clk_rate) {
+			clk_set_rate(nfc->sclk, sunxi_nand->clk_rate);
+			nfc->clk_rate = sunxi_nand->clk_rate;
+		}
+	}
+
+	writel(ctl, nfc->regs + NFC_REG_CTL);
+
+	sunxi_nand->selected = chip;
+}
+
+static void sunxi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(mtd);
+	struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+	int cnt;
+	int offs = 0;
+	u32 tmp;
+
+	while (len > offs) {
+		cnt = len - offs;
+		if (cnt > 1024)
+			cnt = 1024;
+
+		while ((readl(nfc->regs + NFC_REG_ST) & NFC_CMD_FIFO_STATUS))
+			;
+		writel(cnt, nfc->regs + NFC_REG_CNT);
+		tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD;
+		writel(tmp, nfc->regs + NFC_REG_CMD);
+		sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+		if (buf)
+			memcpy_fromio(buf + offs, nfc->regs + NFC_RAM0_BASE,
+				      cnt);
+		offs += cnt;
+	}
+}
+
+static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
+				int len)
+{
+	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(mtd);
+	struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+	int cnt;
+	int offs = 0;
+	u32 tmp;
+
+	while (len > offs) {
+		cnt = len - offs;
+		if (cnt > 1024)
+			cnt = 1024;
+
+		while ((readl(nfc->regs + NFC_REG_ST) & NFC_CMD_FIFO_STATUS))
+			;
+		writel(cnt, nfc->regs + NFC_REG_CNT);
+		memcpy_toio(nfc->regs + NFC_RAM0_BASE, buf + offs, cnt);
+		tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
+		      NFC_ACCESS_DIR;
+		writel(tmp, nfc->regs + NFC_REG_CMD);
+		sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+		offs += cnt;
+	}
+}
+
+static uint8_t sunxi_nfc_read_byte(struct mtd_info *mtd)
+{
+	uint8_t ret;
+
+	sunxi_nfc_read_buf(mtd, &ret, 1);
+
+	return ret;
+}
+
+static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat,
+			       unsigned int ctrl)
+{
+	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(mtd);
+	struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+	u32 tmp;
+
+	while ((readl(nfc->regs + NFC_REG_ST) & NFC_CMD_FIFO_STATUS))
+		;
+
+	if (ctrl & NAND_CTRL_CHANGE) {
+		tmp = readl(nfc->regs + NFC_REG_CTL);
+		if (ctrl & NAND_NCE)
+			tmp |= NFC_CE_CTL;
+		else
+			tmp &= ~NFC_CE_CTL;
+		writel(tmp, nfc->regs + NFC_REG_CTL);
+	}
+
+	if (dat == NAND_CMD_NONE)
+		return;
+
+	if (ctrl & NAND_CLE) {
+		writel(NFC_SEND_CMD1 | dat, nfc->regs + NFC_REG_CMD);
+	} else {
+		writel(dat, nfc->regs + NFC_REG_ADDR_LOW);
+		writel(NFC_SEND_ADR, nfc->regs + NFC_REG_CMD);
+	}
+
+	sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+}
+
+static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
+				       const struct nand_sdr_timings *timings)
+{
+	u32 min_clk_period = 0;
+
+	/* T1 <=> tCLS */
+	if (timings->tCLS_min > min_clk_period)
+		min_clk_period = timings->tCLS_min;
+
+	/* T2 <=> tCLH */
+	if (timings->tCLH_min > min_clk_period)
+		min_clk_period = timings->tCLH_min;
+
+	/* T3 <=> tCS */
+	if (timings->tCS_min > min_clk_period)
+		min_clk_period = timings->tCS_min;
+
+	/* T4 <=> tCH */
+	if (timings->tCH_min > min_clk_period)
+		min_clk_period = timings->tCH_min;
+
+	/* T5 <=> tWP */
+	if (timings->tWP_min > min_clk_period)
+		min_clk_period = timings->tWP_min;
+
+	/* T6 <=> tWH */
+	if (timings->tWH_min > min_clk_period)
+		min_clk_period = timings->tWH_min;
+
+	/* T7 <=> tALS */
+	if (timings->tALS_min > min_clk_period)
+		min_clk_period = timings->tALS_min;
+
+	/* T8 <=> tDS */
+	if (timings->tDS_min > min_clk_period)
+		min_clk_period = timings->tDS_min;
+
+	/* T9 <=> tDH */
+	if (timings->tDH_min > min_clk_period)
+		min_clk_period = timings->tDH_min;
+
+	/* T10 <=> tRR */
+	if (timings->tRR_min > (min_clk_period * 3))
+		min_clk_period = (timings->tRR_min + 2) / 3;
+
+	/* T11 <=> tALH */
+	if (timings->tALH_min > min_clk_period)
+		min_clk_period = timings->tALH_min;
+
+	/* T12 <=> tRP */
+	if (timings->tRP_min > min_clk_period)
+		min_clk_period = timings->tRP_min;
+
+	/* T13 <=> tREH */
+	if (timings->tREH_min > min_clk_period)
+		min_clk_period = timings->tREH_min;
+
+	/* T14 <=> tRC */
+	if (timings->tRC_min > (min_clk_period * 2))
+		min_clk_period = (timings->tRC_min + 1) / 2;
+
+	/* T15 <=> tWC */
+	if (timings->tWC_min > (min_clk_period * 2))
+		min_clk_period = (timings->tWC_min + 1) / 2;
+
+
+	/* min_clk_period = (NAND-clk-period * 2) */
+	if (min_clk_period < 1000)
+		min_clk_period = 1000;
+
+	min_clk_period /= 1000;
+	chip->clk_rate = (2 * 1000000000) / min_clk_period;
+
+	/* TODO: configure T16-T19 */
+
+	return 0;
+}
+
+static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip,
+					struct device_node *np)
+{
+	const struct nand_sdr_timings *timings;
+	int ret;
+
+	ret = onfi_get_async_timing_mode(&chip->nand);
+	if (ret == ONFI_TIMING_MODE_UNKNOWN) {
+		ret = of_get_nand_onfi_timing_mode(np);
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = fls(ret);
+	if (!ret)
+		return -EINVAL;
+
+	timings = onfi_async_timing_mode_to_sdr_timings(ret - 1);
+	if (IS_ERR(timings))
+		return PTR_ERR(timings);
+
+	return sunxi_nand_chip_set_timings(chip, timings);
+}
+
+static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
+				struct device_node *np)
+{
+	const struct nand_sdr_timings *timings;
+	struct sunxi_nand_chip *chip;
+	struct mtd_part_parser_data ppdata;
+	struct mtd_info *mtd;
+	struct nand_chip *nand;
+	u32 strength;
+	u32 blk_size;
+	int nsels;
+	int ret;
+	int i;
+	u32 tmp;
+
+	if (!of_get_property(np, "reg", &nsels))
+		return -EINVAL;
+
+	nsels /= sizeof(u32);
+	if (!nsels)
+		return -EINVAL;
+
+	chip = devm_kzalloc(dev,
+			    sizeof(*chip) +
+			    (nsels * sizeof(struct sunxi_nand_chip_sel)),
+			    GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	chip->nsels = nsels;
+	chip->selected = -1;
+
+	for (i = 0; i < nsels; i++) {
+		ret = of_property_read_u32_index(np, "reg", i, &tmp);
+		if (ret)
+			return ret;
+
+		if (tmp > 7)
+			return -EINVAL;
+
+		if (test_and_set_bit(tmp, &nfc->assigned_cs))
+			return -EINVAL;
+
+		chip->sels[i].cs = tmp;
+
+		if (!of_property_read_u32_index(np, "allwinner,rb", i, &tmp) &&
+		    tmp < 2) {
+			chip->sels[i].rb.type = RB_NATIVE;
+			chip->sels[i].rb.info.nativeid = tmp;
+		} else {
+			ret = of_get_named_gpio(np, "rb-gpios", i);
+			if (ret >= 0) {
+				tmp = ret;
+				chip->sels[i].rb.type = RB_GPIO;
+				chip->sels[i].rb.info.gpio = tmp;
+				ret = devm_gpio_request(dev, tmp, "nand-rb");
+				if (ret)
+					return ret;
+
+				ret = gpio_direction_input(tmp);
+				if (ret)
+					return ret;
+			} else {
+				chip->sels[i].rb.type = RB_NONE;
+			}
+		}
+	}
+
+	timings = onfi_async_timing_mode_to_sdr_timings(0);
+	if (IS_ERR(timings))
+		return PTR_ERR(timings);
+
+	ret = sunxi_nand_chip_set_timings(chip, timings);
+
+	nand = &chip->nand;
+	nand->controller = &nfc->controller;
+	nand->select_chip = sunxi_nfc_select_chip;
+	nand->cmd_ctrl = sunxi_nfc_cmd_ctrl;
+	nand->read_buf = sunxi_nfc_read_buf;
+	nand->write_buf = sunxi_nfc_write_buf;
+	nand->read_byte = sunxi_nfc_read_byte;
+
+	nand->ecc.mode = of_get_nand_ecc_mode(np);
+	if (of_get_nand_on_flash_bbt(np))
+		nand->bbt_options |= NAND_BBT_USE_FLASH;
+
+	mtd = &chip->mtd;
+	mtd->priv = nand;
+	mtd->owner = THIS_MODULE;
+
+	ret = nand_scan_ident(mtd, nsels, NULL);
+	if (ret)
+		return ret;
+
+	ret = sunxi_nand_chip_init_timings(chip, np);
+	if (ret)
+		return ret;
+
+	if (nand->ecc.mode == NAND_ECC_SOFT_BCH) {
+		if (!of_get_nand_ecc_level(np, &strength, &blk_size)) {
+			nand->ecc_step_ds = blk_size;
+			nand->ecc_strength_ds = strength;
+		}
+
+		nand->ecc.size = nand->ecc_step_ds;
+		nand->ecc.bytes = (((nand->ecc_strength_ds *
+				     fls(8 * nand->ecc_step_ds)) + 7) / 8);
+	}
+
+	ret = nand_scan_tail(mtd);
+	if (ret)
+		return ret;
+
+	if (of_property_read_string(np, "nand-name", &mtd->name)) {
+		snprintf(chip->default_name, MAX_NAME_SIZE,
+			 DEFAULT_NAME_FORMAT, chip->sels[i].cs);
+		mtd->name = chip->default_name;
+	}
+
+	ppdata.of_node = np;
+	ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
+	if (!ret)
+		return ret;
+
+	list_add_tail(&chip->node, &nfc->chips);
+
+	return 0;
+}
+
+static int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc)
+{
+	struct device_node *np = dev->of_node;
+	struct device_node *nand_np;
+	int nchips = of_get_child_count(np);
+	int ret;
+
+	if (nchips > 8)
+		return -EINVAL;
+
+	for_each_child_of_node(np, nand_np) {
+		ret = sunxi_nand_chip_init(dev, nfc, nand_np);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int sunxi_nfc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *r;
+	struct sunxi_nfc *nfc;
+	int ret;
+
+	nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
+	if (!nfc) {
+		dev_err(dev, "failed to allocate NFC struct\n");
+		return -ENOMEM;
+	}
+
+	spin_lock_init(&nfc->controller.lock);
+	init_waitqueue_head(&nfc->controller.wq);
+	INIT_LIST_HEAD(&nfc->chips);
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	nfc->regs = devm_ioremap_resource(dev, r);
+	if (IS_ERR(nfc->regs)) {
+		dev_err(dev, "failed to remap iomem\n");
+		return PTR_ERR(nfc->regs);
+	}
+
+	nfc->irq = platform_get_irq(pdev, 0);
+	if (nfc->irq < 0) {
+		dev_err(dev, "failed to retrieve irq\n");
+		return nfc->irq;
+	}
+
+	nfc->ahb_clk = devm_clk_get(dev, "ahb_clk");
+	if (IS_ERR(nfc->ahb_clk)) {
+		dev_err(dev, "failed to retrieve ahb_clk\n");
+		return PTR_ERR(nfc->ahb_clk);
+	}
+
+	ret = clk_prepare_enable(nfc->ahb_clk);
+	if (ret)
+		return ret;
+
+	nfc->sclk = devm_clk_get(dev, "sclk");
+	if (IS_ERR(nfc->sclk)) {
+		dev_err(dev, "failed to retrieve nand_clk\n");
+		ret = PTR_ERR(nfc->sclk);
+		goto out_ahb_clk_unprepare;
+	}
+
+	ret = clk_prepare_enable(nfc->sclk);
+	if (ret)
+		goto out_ahb_clk_unprepare;
+
+	/* Reset NFC */
+	writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RESET,
+	       nfc->regs + NFC_REG_CTL);
+	while (readl(nfc->regs + NFC_REG_CTL) & NFC_RESET)
+		;
+
+	writel(0, nfc->regs + NFC_REG_INT);
+	ret = devm_request_irq(dev, nfc->irq, sunxi_nfc_interrupt,
+			       0, "sunxi-nand", nfc);
+	if (ret)
+		goto out_sclk_unprepare;
+
+	platform_set_drvdata(pdev, nfc);
+
+	writel(0x100, nfc->regs + NFC_REG_TIMING_CTL);
+	writel(0x7ff, nfc->regs + NFC_REG_TIMING_CFG);
+
+	ret = sunxi_nand_chips_init(dev, nfc);
+	if (ret) {
+		dev_err(dev, "failed to init nand chips\n");
+		goto out_sclk_unprepare;
+	}
+
+	return 0;
+
+out_sclk_unprepare:
+	clk_disable_unprepare(nfc->sclk);
+out_ahb_clk_unprepare:
+	clk_disable_unprepare(nfc->ahb_clk);
+
+	return ret;
+}
+
+static const struct of_device_id sunxi_nfc_ids[] = {
+	{ .compatible = "allwinner,sun4i-nand" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sunxi_nfc_ids);
+
+static struct platform_driver sunxi_nfc_driver = {
+	.driver = {
+		.name = "sunxi_nand",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(sunxi_nfc_ids),
+	},
+	.probe = sunxi_nfc_probe,
+};
+module_platform_driver(sunxi_nfc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Boris BREZILLON");
+MODULE_DESCRIPTION("Allwinner NAND Flash Controller driver");
+MODULE_ALIAS("platform:sunxi_nfc");
-- 
1.7.9.5

-- 
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/groups/opt_out.

^ permalink raw reply related

* [RFC PATCH pre-v3 13/14] mtd: nand: add sunxi HW ECC support
From: Boris BREZILLON @ 2014-01-30 13:41 UTC (permalink / raw)
  To: Maxime Ripard, Rob Landley, Russell King, David Woodhouse,
	Grant Likely, Brian Norris, Jason Gunthorpe, Arnd Bergmann
  Cc: Boris BREZILLON, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	dev-3kdeTeqwOZ9EV1b7eY7vFQ
In-Reply-To: <1391006064-28890-1-git-send-email-b.brezillon.dev-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

Add HW ECC support for the sunxi NAND Flash Controller.

Signed-off-by: Boris BREZILLON <b.brezillon.dev-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
 drivers/mtd/nand/sunxi_nand.c |  279 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 266 insertions(+), 13 deletions(-)

diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
index 1014b2a..b90268f 100644
--- a/drivers/mtd/nand/sunxi_nand.c
+++ b/drivers/mtd/nand/sunxi_nand.c
@@ -163,6 +163,11 @@ struct sunxi_nand_chip_sel {
 #define DEFAULT_NAME_FORMAT	"nand@%d"
 #define MAX_NAME_SIZE		(sizeof("nand@") + 2)
 
+struct sunxi_nand_hw_ecc {
+	int mode;
+	struct nand_ecclayout layout;
+};
+
 struct sunxi_nand_chip {
 	struct list_head node;
 	struct nand_chip nand;
@@ -402,6 +407,126 @@ static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat,
 	sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
 }
 
+static int sunxi_nfc_hwecc_read_page(struct mtd_info *mtd,
+				     struct nand_chip *chip, uint8_t *buf,
+				     int oob_required, int page)
+{
+	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(mtd);
+	struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+	struct nand_ecc_ctrl *ecc = &chip->ecc;
+	struct nand_ecclayout *layout = ecc->layout;
+	struct sunxi_nand_hw_ecc *data = ecc->priv;
+	unsigned int max_bitflips = 0;
+	int offset;
+	u32 tmp;
+	int i;
+
+	tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
+	tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE |
+		 NFC_ECC_BLOCK_SIZE);
+	tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT);
+	writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
+
+	for (i = 0; i < mtd->writesize / ecc->size; i++) {
+		if (i)
+			chip->cmdfunc(mtd, NAND_CMD_RNDOUT, i * ecc->size, -1);
+		chip->read_buf(mtd, NULL, chip->ecc.size);
+		offset = mtd->writesize + layout->eccpos[i * ecc->bytes] - 4;
+		chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
+		while ((readl(nfc->regs + NFC_REG_ST) & NFC_CMD_FIFO_STATUS))
+			;
+		tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | (1 << 30);
+		writel(tmp, nfc->regs + NFC_REG_CMD);
+		sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+		memcpy_fromio(buf + (i * ecc->size), nfc->regs + NFC_RAM0_BASE,
+			      chip->ecc.size);
+
+		if (readl(nfc->regs + NFC_REG_ECC_ST) & 0x1) {
+			mtd->ecc_stats.failed++;
+		} else {
+			tmp = readl(nfc->regs + NFC_REG_ECC_CNT0) & 0xff;
+			mtd->ecc_stats.corrected += tmp;
+			max_bitflips = max_t(unsigned int, max_bitflips, tmp);
+		}
+	}
+
+	if (oob_required) {
+		chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
+		chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+	}
+
+	tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
+	tmp &= ~NFC_ECC_EN;
+
+	writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
+
+	return max_bitflips;
+}
+
+static int sunxi_nfc_hwecc_write_page(struct mtd_info *mtd,
+				      struct nand_chip *chip,
+				      const uint8_t *buf,
+				      int oob_required)
+{
+	struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(mtd);
+	struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+	struct nand_ecc_ctrl *ecc = &chip->ecc;
+	struct nand_ecclayout *layout = ecc->layout;
+	struct sunxi_nand_hw_ecc *data = ecc->priv;
+	int offset;
+	u32 tmp;
+	int i;
+	int j;
+
+	tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
+	tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE |
+		 NFC_ECC_BLOCK_SIZE);
+	tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT);
+
+	writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
+
+	for (i = 0; i < mtd->writesize / ecc->size; i++) {
+		if (i)
+			chip->cmdfunc(mtd, NAND_CMD_RNDIN, i * ecc->size, -1);
+
+		chip->write_buf(mtd, buf + (i * ecc->size), ecc->size);
+		offset = mtd->writesize + layout->eccpos[i * ecc->bytes] - 4;
+		chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1);
+		while ((readl(nfc->regs + NFC_REG_ST) & NFC_CMD_FIFO_STATUS))
+			;
+
+		/* Fill OOB data in */
+		for (j = 0; j < 4; j++) {
+			if (oob_required) {
+				offset = layout->eccpos[i * ecc->size] - 4;
+				writeb(chip->oob_poi[offset + j],
+				       nfc->regs + NFC_REG_USER_DATA_BASE + j);
+			} else {
+				writeb(0xff,
+				       nfc->regs + NFC_REG_USER_DATA_BASE + j);
+			}
+		}
+
+		tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
+		      NFC_ACCESS_DIR | (1 << 30);
+		writel(tmp, nfc->regs + NFC_REG_CMD);
+		sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+	}
+
+	if (oob_required && chip->ecc.layout->oobfree[0].length > 2) {
+		chip->cmdfunc(mtd, NAND_CMD_RNDIN, mtd->writesize, -1);
+		chip->write_buf(mtd, chip->oob_poi,
+				chip->ecc.layout->oobfree[0].length - 2);
+	}
+
+	tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
+	tmp &= ~(NFC_ECC_EN | NFC_ECC_PIPELINE);
+
+	writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
+
+	return 0;
+}
+
 static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
 				       const struct nand_sdr_timings *timings)
 {
@@ -504,6 +629,144 @@ static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip,
 	return sunxi_nand_chip_set_timings(chip, timings);
 }
 
+static int sunxi_nand_chip_hwecc_init(struct device *dev,
+				      struct sunxi_nand_chip *chip,
+				      struct mtd_info *mtd,
+				      struct device_node *np)
+{
+	struct nand_chip *nand = &chip->nand;
+	struct nand_ecc_ctrl *ecc = &nand->ecc;
+	struct sunxi_nand_hw_ecc *data;
+	struct nand_ecclayout *layout;
+	int nsectors;
+	int i;
+	int j;
+
+	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	ecc->read_page = sunxi_nfc_hwecc_read_page;
+	ecc->write_page = sunxi_nfc_hwecc_write_page;
+
+	if (nand->ecc_strength_ds <= 16) {
+		nand->ecc_strength_ds = 16;
+		data->mode = 0;
+	} else if (nand->ecc_strength_ds <= 24) {
+		nand->ecc_strength_ds = 24;
+		data->mode = 1;
+	} else if (nand->ecc_strength_ds <= 28) {
+		nand->ecc_strength_ds = 28;
+		data->mode = 2;
+	} else if (nand->ecc_strength_ds <= 32) {
+		nand->ecc_strength_ds = 32;
+		data->mode = 3;
+	} else if (nand->ecc_strength_ds <= 40) {
+		nand->ecc_strength_ds = 40;
+		data->mode = 4;
+	} else if (nand->ecc_strength_ds <= 48) {
+		nand->ecc_strength_ds = 48;
+		data->mode = 5;
+	} else if (nand->ecc_strength_ds <= 56) {
+		nand->ecc_strength_ds = 56;
+		data->mode = 6;
+	} else if (nand->ecc_strength_ds <= 60) {
+		nand->ecc_strength_ds = 60;
+		data->mode = 7;
+	} else if (nand->ecc_strength_ds <= 64) {
+		nand->ecc_strength_ds = 64;
+		data->mode = 8;
+	} else {
+		dev_err(dev, "unsupported strength\n");
+		return -ENOTSUPP;
+	}
+
+	/* HW ECC always request ECC bytes for 1024 bytes blocks */
+	ecc->bytes = ((nand->ecc_strength_ds * fls(8 * 1024)) + 7) / 8;
+
+	/* HW ECC always work with even numbers of ECC bytes */
+	if (ecc->bytes % 2)
+		ecc->bytes++;
+	ecc->strength = nand->ecc_strength_ds;
+	ecc->size = nand->ecc_step_ds;
+
+	layout = &data->layout;
+	nsectors = mtd->writesize / ecc->size;
+
+	if (mtd->oobsize < ((ecc->bytes + 4) * nsectors))
+		return -EINVAL;
+
+	layout->eccbytes = (ecc->bytes * nsectors);
+
+	/*
+	 * The first 2 bytes are used for BB markers.
+	 * We merge the 4 user available bytes from HW ECC with this
+	 * first section, hence why the + 2 operation (- 2 + 4).
+	 */
+	layout->oobfree[0].length = mtd->oobsize + 2 -
+				    ((ecc->bytes + 4) * nsectors);
+	layout->oobfree[0].offset = 2;
+	for (i = 0; i < nsectors; i++) {
+		/*
+		 * The first 4 ECC block bytes are already counted in the first
+		 * obbfree entry.
+		 */
+		if (i) {
+			layout->oobfree[i].offset =
+				layout->oobfree[i - 1].offset +
+				layout->oobfree[i - 1].length +
+				ecc->bytes;
+			layout->oobfree[i].length = 4;
+		}
+
+		for (j = 0; j < ecc->bytes; j++)
+			layout->eccpos[(ecc->bytes * i) + j] =
+					layout->oobfree[i].offset +
+					layout->oobfree[i].length + j;
+	}
+
+	ecc->layout = layout;
+	ecc->priv = data;
+
+	return 0;
+}
+
+static int sunxi_nand_chip_ecc_init(struct device *dev,
+				    struct sunxi_nand_chip *chip,
+				    struct mtd_info *mtd,
+				    struct device_node *np)
+{
+	struct nand_chip *nand = &chip->nand;
+	u32 strength;
+	u32 blk_size;
+	int ret;
+
+	nand->ecc.mode = of_get_nand_ecc_mode(np);
+
+	if (!of_get_nand_ecc_level(np, &strength, &blk_size)) {
+		nand->ecc_step_ds = blk_size;
+		nand->ecc_strength_ds = strength;
+	}
+
+	switch (nand->ecc.mode) {
+	case NAND_ECC_SOFT_BCH:
+		nand->ecc.size = nand->ecc_step_ds;
+		nand->ecc.bytes = ((nand->ecc_strength_ds *
+				    fls(8 * nand->ecc_step_ds)) + 7) / 8;
+		break;
+	case NAND_ECC_HW:
+		ret = sunxi_nand_chip_hwecc_init(dev, chip, mtd, np);
+		if (ret)
+			return ret;
+		break;
+	case NAND_ECC_NONE:
+	default:
+		break;
+	}
+
+	return 0;
+}
+
 static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
 				struct device_node *np)
 {
@@ -512,8 +775,6 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
 	struct mtd_part_parser_data ppdata;
 	struct mtd_info *mtd;
 	struct nand_chip *nand;
-	u32 strength;
-	u32 blk_size;
 	int nsels;
 	int ret;
 	int i;
@@ -586,7 +847,6 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
 	nand->write_buf = sunxi_nfc_write_buf;
 	nand->read_byte = sunxi_nfc_read_byte;
 
-	nand->ecc.mode = of_get_nand_ecc_mode(np);
 	if (of_get_nand_on_flash_bbt(np))
 		nand->bbt_options |= NAND_BBT_USE_FLASH;
 
@@ -602,16 +862,9 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
 	if (ret)
 		return ret;
 
-	if (nand->ecc.mode == NAND_ECC_SOFT_BCH) {
-		if (!of_get_nand_ecc_level(np, &strength, &blk_size)) {
-			nand->ecc_step_ds = blk_size;
-			nand->ecc_strength_ds = strength;
-		}
-
-		nand->ecc.size = nand->ecc_step_ds;
-		nand->ecc.bytes = (((nand->ecc_strength_ds *
-				     fls(8 * nand->ecc_step_ds)) + 7) / 8);
-	}
+	ret = sunxi_nand_chip_ecc_init(dev, chip, mtd, np);
+	if (ret)
+		return ret;
 
 	ret = nand_scan_tail(mtd);
 	if (ret)
-- 
1.7.9.5

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox