All of lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-01-15  9:27 ` Viet Nga Dao
  0 siblings, 0 replies; 39+ messages in thread
From: Viet Nga Dao @ 2015-01-15  9:27 UTC (permalink / raw)
  To: Brian Norris; +Cc: vndao, dwmw2, linux-mtd, linux-kernel, devicetree, nga_chi86

On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
<computersforpeace@gmail.com> wrote:
> On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao@altera.com wrote:
>> From: Viet Nga Dao <vndao@altera.com>
>>
>> Altera EPCQ Controller is a soft IP which enables access to Altera EPCQ and
>> EPCS flash chips. This patch adds driver for these devices.
>>
>> Signed-off-by: Viet Nga Dao <vndao@altera.com>
>
> This drivers seems awfully similar to (and so I infer it is likely
> copy-and-pasted from) m25p80.c / spi-nor.c. Do you think it can be
> rewritten as a SPI NOR driver, under drivers/mtd/spi-nor/ ? It looks
> like these flash share most (all?) the same basic opcodes.
>
For Altera EPCQ flashes, almost operations are performed underline
hardware. Software only able to perform the following through
registers:
 -  read status register
 -  read id
 -  write status registers bit SR_BP0,SR_BP1, SR_BP2,SR_BP3, SR_TB
(http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf)
For read/write data: all the operations like QUAD_READ/WRITE,
FAST_READ/WRITE are handled by hardware as well. From software point
of view, there is no difference between these 2 modes.
That is why if rewrite the drivers to follow spi-nor structure, it
will require extra decoding works for the only few used opcodes.

>> ---
>>  .../devicetree/bindings/mtd/altera_epcq.txt        |   45 ++
>>  drivers/mtd/devices/Kconfig                        |   12 +
>>  drivers/mtd/devices/Makefile                       |    2 +-
>>  drivers/mtd/devices/altera_epcq.c                  |  804 ++++++++++++++++++++
>>  drivers/mtd/devices/altera_epcq.h                  |  130 ++++
>>  5 files changed, 992 insertions(+), 1 deletions(-)
>>  create mode 100644 Documentation/devicetree/bindings/mtd/altera_epcq.txt
>>  create mode 100644 drivers/mtd/devices/altera_epcq.c
>>  create mode 100644 drivers/mtd/devices/altera_epcq.h
>>
>> diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>> new file mode 100644
>> index 0000000..d14f50e
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>> @@ -0,0 +1,45 @@
>> +* MTD Altera EPCQ driver
>> +
>> +Required properties:
>> +- compatible: Should be "altr,epcq-1.0"
>> +- reg: Address and length of the register set  for the device. It contains
>> +  the information of registers in the same order as described by reg-names
>> +- reg-names: Should contain the reg names
>> +  "csr_base": Should contain the register configuration base address
>> +  "data_base": Should contain the data base address
>> +- is-epcs: boolean type.
>> +             If present, the device contains EPCS flashes.
>> +             Otherwise, it contains EPCQ flashes.
>> +- #address-cells: Must be <1>.
>> +- #size-cells: Must be <0>.
>> +- flash device tree subnode, there must be a node with the following fields:
>
> These subnodes definitely require a 'compatible' string. Perhaps they
> should be used to differentiate EPCS vs. EPCQ. Does "is-epcs" really
> need to be in the top-level controller node?
>
>> +     - reg: Should contain the flash id
>
> Should, or must? (This question is relevant, because you seem to make it
> optional in your code.) And what does the "flash ID" mean? It seems like
> you're using as a chip-select or bank index.
>
>> +     - #address-cells: please refer to /mtd/partition.txt
>> +     - #size-cells: please refer to /mtd/partition.txt
>> +     For partitions inside each flash, please refer to /mtd/partition.txt
>> +
>> +Example:
>> +
>> +                     epcq_controller_0: epcq@0x000000000 {
>> +                             compatible = "altr,epcq-1.0";
>> +                             reg = <0x00000001 0x00000000 0x00000020>,
>> +                                     <0x00000000 0x00000000 0x02000000>;
>> +                             reg-names = "csr_base", "data_base";
>> +                             #address-cells = <1>;
>> +                             #size-cells = <0>;
>> +                             flash0: epcq256@0 {
>> +                                     reg = <0>;
>> +                                     #address-cells = <1>;
>> +                                     #size-cells = <1>;
>> +                                     partition@0 {
>> +                                             /* 16 MB for raw data. */
>> +                                             label = "EPCQ Flash 0 raw data";
>> +                                             reg = <0x0 0x1000000>;
>> +                                     };
>> +                                     partition@1000000 {
>> +                                             /* 16 MB for jffs2 data. */
>> +                                             label = "EPCQ Flash 0 JFFS 2";
>> +                                             reg = <0x1000000 0x1000000>;
>> +                                     };
>> +                             };
>> +                     }; //end epcq@0x000000000 (epcq_controller_0)
>> diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
>> index c49d0b1..020b864 100644
>> --- a/drivers/mtd/devices/Kconfig
>> +++ b/drivers/mtd/devices/Kconfig
>> @@ -218,6 +218,18 @@ config MTD_ST_SPI_FSM
>>         SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
>>         for a subset of connected Serial Flash devices.
>>
>> +config MTD_ALTERA_EPCQ
>> +     tristate "Support Altera EPCQ/EPCS Flash chips"
>> +     depends on OF
>> +     help
>> +       This enables access to Altera EPCQ/EPCS flash chips, used for data
>> +       storage. See the driver source for the current list,
>> +       or to add other chips.
>> +
>> +       If you want to compile this driver as a module ( = code which can be
>> +       inserted in and removed from the running kernel whenever you want),
>> +       say M here and read <file:Documentation/kbuild/modules.txt>.
>> +
>>  if MTD_DOCG3
>>  config BCH_CONST_M
>>       default 14
>> diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
>> index f0b0e61..b429c4d 100644
>> --- a/drivers/mtd/devices/Makefile
>> +++ b/drivers/mtd/devices/Makefile
>> @@ -16,6 +16,6 @@ obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
>>  obj-$(CONFIG_MTD_SST25L)     += sst25l.o
>>  obj-$(CONFIG_MTD_BCM47XXSFLASH)      += bcm47xxsflash.o
>>  obj-$(CONFIG_MTD_ST_SPI_FSM)    += st_spi_fsm.o
>> -
>> +obj-$(CONFIG_MTD_ALTERA_EPCQ)                += altera_epcq.o
>>
>>  CFLAGS_docg3.o                       += -I$(src)
>> diff --git a/drivers/mtd/devices/altera_epcq.c b/drivers/mtd/devices/altera_epcq.c
>> new file mode 100644
>> index 0000000..09213d5
>> --- /dev/null
>> +++ b/drivers/mtd/devices/altera_epcq.c
>> @@ -0,0 +1,804 @@
>> +/*
>> + * Copyright (C) 2014 Altera Corporation. All rights reserved
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms and conditions of the GNU General Public License,
>> + * version 2, as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope 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.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/device.h>
>> +#include <linux/err.h>
>> +#include <linux/errno.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/ioport.h>
>> +#include <linux/jiffies.h>
>> +#include <linux/kernel.h>
>> +#include <linux/log2.h>
>> +#include <linux/module.h>
>> +#include <linux/mtd/mtd.h>
>> +#include <linux/mtd/partitions.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/param.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pm.h>
>> +#include <linux/sched.h>
>> +#include <linux/slab.h>
>> +#include <linux/wait.h>
>> +
>> +#include "altera_epcq.h"
>> +
>> +/* data structure to maintain flash ids from different vendors */
>> +struct flash_device {
>> +     char *name;
>> +     bool is_epcs;
>> +     u32 device_id;
>> +     uint32_t sectorsize_inbytes;
>> +     uint64_t size_inbytes;
>> +     u32 pagesize;
>> +};
>> +
>> +#define FLASH_ID(_n, _is_epcs, _id, _ssize, _size, _psize)   \
>> +{                            \
>> +     .name = (_n),           \
>> +     .is_epcs = (_is_epcs),          \
>> +     .device_id = (_id),     \
>> +     .sectorsize_inbytes = (_ssize), \
>> +     .size_inbytes = (_size),        \
>> +     .pagesize = (_psize),   \
>> +}
>> +
>> +static struct flash_device flash_devices[] = {
>> +     FLASH_ID("epcq16"    , 0, 0x15, 0x10000, 0x200000, 0x100),
>> +     FLASH_ID("epcq32"    , 0, 0x16, 0x10000, 0x400000, 0x100),
>> +     FLASH_ID("epcq64"    , 0, 0x17, 0x10000, 0x800000, 0x100),
>> +     FLASH_ID("epcq128"   , 0, 0x18, 0x10000, 0x1000000, 0x100),
>> +     FLASH_ID("epcq256"   , 0, 0x19, 0x10000, 0x2000000, 0x100),
>> +     FLASH_ID("epcq512"   , 0, 0x20, 0x10000, 0x4000000, 0x100),
>> +
>> +     FLASH_ID("epcs16"    , 1, 0x14, 0x10000, 0x200000, 0x100),
>> +     FLASH_ID("epcs64"    , 1, 0x16, 0x10000, 0x800000, 0x100),
>> +     FLASH_ID("epcs128"   , 1, 0x18, 0x40000, 0x1000000, 0x100),
>> +};
>> +
>> +static inline struct altera_epcq_flash *get_flash_data(struct mtd_info *mtd)
>> +{
>> +     return container_of(mtd, struct altera_epcq_flash, mtd);
>> +}
>> +
>> +static u32 altera_epcq_read_sr(struct altera_epcq *dev)
>> +{
>> +     return readl(dev->csr_base + EPCQ_SR_REG);
>> +}
>> +
>> +static int altera_epcq_wait_till_ready(struct altera_epcq *dev)
>> +{
>> +     unsigned long finish;
>> +     int status;
>> +
>> +     finish = jiffies + EPCQ_MAX_TIME_OUT;
>> +     do {
>> +             status = altera_epcq_read_sr(dev);
>> +             if (status < 0)
>> +                     return status;
>> +             else if (!(status & EPCQ_SR_WIP))
>> +                     return 0;
>> +
>> +             cond_resched();
>> +     } while (!time_after_eq(jiffies, finish));
>> +
>> +     dev_err(&dev->pdev->dev, "epcq controller is busy, timeout\n");
>> +     return -EBUSY;
>> +}
>> +
>> +static int get_flash_index(u32 flash_id, bool is_epcs)
>> +{
>> +     int index;
>> +
>> +     for (index = 0; index < ARRAY_SIZE(flash_devices); index++) {
>> +             if (flash_devices[index].device_id == flash_id &&
>> +                 flash_devices[index].is_epcs == is_epcs)
>> +                     return index;
>> +     }
>> +
>> +     /* Memory chip is not listed and not supported */
>> +     return -ENODEV;
>> +}
>> +
>> +static int altera_epcq_write_erase_check(struct altera_epcq *dev,
>> +                                      bool write_erase)
>> +{
>> +     u32 val;
>> +     u32 mask;
>> +
>> +     if (write_erase)
>> +             mask = EPCQ_ISR_ILLEGAL_WRITE_MASK;
>> +     else
>> +             mask = EPCQ_ISR_ILLEGAL_ERASE_MASK;
>> +
>> +     val = readl(dev->csr_base + EPCQ_ISR_REG);
>> +     if (val & mask) {
>> +             dev_err(&dev->pdev->dev,
>> +                     "write/erase failed, sector might be protected\n");
>> +             /*clear this status for next use*/
>> +             writel(val, dev->csr_base + EPCQ_ISR_REG);
>> +             return -EIO;
>> +     }
>> +     return 0;
>> +}
>> +
>> +static int altera_epcq_erase_chip(struct mtd_info *mtd)
>> +{
>> +     int ret;
>> +     u32 val;
>> +     struct altera_epcq *dev = mtd->priv;
>> +
>> +     /* Wait until finished previous write command. */
>> +     ret = altera_epcq_wait_till_ready(dev);
>
> This pattern is pretty silly. We don't need to have every operation
> check the status of the previous operation. Each operation should check
> itself. You obviously copied this one... but we recently changed that:
>
>     commit dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3
>     Author: Brian Norris <computersforpeace@gmail.com>
>     Date:   Wed Aug 6 18:16:57 2014 -0700
>
>         mtd: spi-nor: move "wait-till-ready" checks into erase/write
>         functions
>
>
>     https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3
>
>> +     if (ret)
>> +             return ret;
>> +
>> +     /* erase chip. */
>> +     val = EPCQ_MEM_OP_BULK_ERASE_CMD;
>> +     writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
>> +
>> +     /* Wait until finished previous write command. */
>> +     ret = altera_epcq_wait_till_ready(dev);
>> +     if (ret)
>> +             return ret;
>> +
>> +      /* check whether write triggered a illegal write interrupt */
>> +     ret = altera_epcq_write_erase_check(dev, 0);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     return 0;
>> +}
>> +
>> +static int altera_epcq_addr_to_sector(struct altera_epcq_flash *flash,
>> +                                   int addr_offset)
>> +{
>> +     int sector = 0;
>> +     int i;
>> +
>> +     for (i = 0; i < flash->num_sectors; i++) {
>> +             if (addr_offset >= i * flash->mtd.erasesize && addr_offset <
>> +                 (i * flash->mtd.erasesize + flash->mtd.erasesize)) {
>> +                     sector = i;
>> +                     return sector;
>> +             }
>> +     }
>
> Uh, really? Am I crazy, or shouldn't this whole function just be a
> really simple arithmetic operation? Like:
>
>         return addr_offset >> mtd->erasesize_shift;
>
>> +     return -1;
>> +}
>> +
>> +static int altera_epcq_erase_sector(struct mtd_info *mtd,
>> +                                 int addr_offset)
>> +{
>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>> +     struct altera_epcq *dev = mtd->priv;
>> +     u32 val;
>> +     int ret;
>> +     int sector_value;
>> +
>> +     ret = altera_epcq_wait_till_ready(dev);
>> +     if (ret)
>> +             return ret;
>> +
>> +     sector_value = altera_epcq_addr_to_sector(flash, addr_offset);
>> +
>> +     /* sanity check that block_offset is a valid sector number */
>> +     if (sector_value < 0)
>> +             return -EINVAL;
>> +
>> +     /* sector value should occupy bits 17:8 */
>> +     val = (sector_value << 8) & EPCQ_MEM_OP_SECTOR_VALUE_MASK;
>> +
>> +     /* sector erase commands occupies lower 2 bits */
>> +     val |= EPCQ_MEM_OP_SECTOR_ERASE_CMD;
>> +
>> +     /* write sector erase command to EPCQ_MEM_OP register*/
>> +     writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
>> +
>> +     ret = altera_epcq_wait_till_ready(dev);
>> +     if (ret)
>> +             return ret;
>> +
>> +      /* check whether write triggered a illegal write interrupt */
>
> ^^ extra space here? You have <TAB><SPACE>/* check [...]
>
>> +     ret = altera_epcq_write_erase_check(dev, 0);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     return 0;
>> +}
>> +
>> +static int altera_epcq_erase(struct mtd_info *mtd, struct erase_info *e_info)
>> +{
>> +     u32 addr;
>> +     int ret;
>> +     u32 len;
>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>> +     struct altera_epcq *dev = mtd->priv;
>> +
>> +     addr = e_info->addr;
>> +     len = e_info->len;
>> +
>> +     if (flash->bank > dev->num_flashes - 1) {
>> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
>> +             return -EINVAL;
>> +     }
>> +     mutex_lock(&flash->lock);
>> +
>> +     /*erase whole chip*/
>> +     if (len == mtd->size) {
>> +             if (altera_epcq_erase_chip(mtd)) {
>> +                     e_info->state = MTD_ERASE_FAILED;
>> +                     mutex_unlock(&flash->lock);
>> +                     return -EIO;
>> +             }
>> +     /*"sector"-at-a-time erase*/
>> +     } else {
>> +             while (len) {
>> +                     ret = altera_epcq_erase_sector(mtd, addr);
>> +                     if (ret) {
>> +                             e_info->state = MTD_ERASE_FAILED;
>> +                             mutex_unlock(&flash->lock);
>> +                             return -EIO;
>> +                     }
>> +                     addr += mtd->erasesize;
>> +                     if (len < mtd->erasesize)
>> +                             len = 0;
>> +                     else
>> +                             len -= mtd->erasesize;
>> +             }
>> +     }
>> +     mutex_unlock(&flash->lock);
>> +     e_info->state = MTD_ERASE_DONE;
>> +     mtd_erase_callback(e_info);
>> +
>> +     return 0;
>> +}
>> +
>> +static int altera_epcq_read(struct mtd_info *mtd, loff_t from, size_t len,
>> +                         size_t *retlen, u8 *buf)
>> +{
>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>> +     struct altera_epcq *dev = mtd->priv;
>> +     void *src;
>> +     int ret = 0;
>> +
>> +     if (!flash || !dev)
>> +             return -ENODEV;
>
> Why would either of these be 0? If they are, you have much bigger
> problems, since you aren't checking these almost anywhere else. And the
> !flash check is pretty odd, since get_flash_data() is using
> container_of(), which is not likely to give you exactly 0...
>
>> +
>> +     if (flash->bank > dev->num_flashes - 1) {
>> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     src = dev->data_base + from;
>> +
>> +     mutex_lock(&flash->lock);
>> +     /* wait till previous write/erase is done. */
>> +     ret = altera_epcq_wait_till_ready(dev);
>> +     if (ret)
>> +             goto err;
>> +
>> +     memcpy_fromio(buf, (u8 *)src, len);
>
> Drop the cast. Also, you get sparse warnings because you're dropping the
> __iomem. Why not just this?
>
>         memcpy_fromio(buf, dev->data_base + from, len);
>
> (BTW, you might consider running sparse on all your code. See
> Documentation/sparse.txt. It tells you how to get it, but you can often
> just download it through your distro's package manager.)
>
>> +     *retlen = len;
>> +
>> +err:
>> +     mutex_unlock(&flash->lock);
>> +     return ret;
>> +}
>> +
>> +static int altera_epcq_write(struct mtd_info *mtd, loff_t to, size_t len,
>> +                          size_t *retlen, const u8 *buf)
>> +{
>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>> +     struct altera_epcq *dev = mtd->priv;
>> +     void *dest;
>> +     int ret = 0;
>> +
>> +     if (!flash || !dev)
>> +             return -ENODEV;
>
> Also here. I don't think you need these checks.
>
>> +
>> +     if (flash->bank > dev->num_flashes - 1) {
>> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
>> +             return -EINVAL;
>> +     }
>> +     dest = dev->data_base + to;
>> +
>> +     mutex_lock(&flash->lock);
>> +
>> +     /* wait until finished previous write command. */
>> +     ret = altera_epcq_wait_till_ready(dev);
>> +     if (ret)
>> +             goto err;
>> +
>> +     memcpy_toio(dest, buf, len);
>
> Similar. Why not just this?
>
>         memcpy_toio(dev->data_base + to, buf, len);
>
>> +     *retlen += len;
>> +
>> +     /* wait until finished previous write command. */
>> +     ret = altera_epcq_wait_till_ready(dev);
>> +     if (ret)
>> +             goto err;
>> +
>> +      /* check whether write triggered a illegal write interrupt */
>> +     ret = altera_epcq_write_erase_check(dev, 1);
>> +     if (ret < 0)
>> +             goto err;
>> +
>> +err:
>> +     mutex_unlock(&flash->lock);
>> +     return ret;
>> +}
>> +
>> +static int altera_epcq_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
>> +{
>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>> +     struct altera_epcq *dev = mtd->priv;
>> +     uint32_t offset = ofs;
>> +     int ret = 0;
>> +     u32 sector_start, sector_end;
>> +     u32 num_sectors;
>> +     u32 mem_op;
>> +     unsigned sr_bp = 0;
>> +     unsigned sr_tb = 0;
>> +
>> +     sector_start = altera_epcq_addr_to_sector(flash, offset);
>> +     sector_end = altera_epcq_addr_to_sector(flash, offset + len);
>> +     num_sectors = flash->num_sectors;
>> +     dev_dbg(&dev->pdev->dev,
>> +             "%s: num_setor is %u,sector start is %u,sector end is %u\n",
>> +             __func__, num_sectors, sector_start, sector_end);
>> +
>> +     mutex_lock(&flash->lock);
>> +     /* wait until finished previous write command. */
>> +     ret = altera_epcq_wait_till_ready(dev);
>> +     if (ret)
>> +             goto err;
>> +
>> +     if (sector_start >= num_sectors/2) {
>
> It's preferable to have spaces around operators. So 'num_sectors / 2'.
> The same thing comes up in several other places.
>
>> +             if (sector_start < num_sectors-(num_sectors / 4))
>> +                     sr_bp = __ilog2_u32(num_sectors);
>> +             else if (sector_start < num_sectors-(num_sectors / 8))
>> +                     sr_bp = __ilog2_u32(num_sectors) - 1;
>> +             else if (sector_start < num_sectors-(num_sectors / 16))
>> +                     sr_bp = __ilog2_u32(num_sectors) - 2;
>> +             else if (sector_start < num_sectors-(num_sectors / 32))
>> +                     sr_bp = __ilog2_u32(num_sectors) - 3;
>> +             else if (sector_start < num_sectors-(num_sectors / 64))
>> +                     sr_bp = __ilog2_u32(num_sectors) - 4;
>> +             else if (sector_start < num_sectors-(num_sectors / 128))
>> +                     sr_bp = __ilog2_u32(num_sectors) - 5;
>> +             else if (sector_start < num_sectors-(num_sectors / 256))
>> +                     sr_bp = __ilog2_u32(num_sectors) - 6;
>> +             else if (sector_start < num_sectors-(num_sectors / 512))
>> +                     sr_bp = __ilog2_u32(num_sectors) - 7;
>> +             else if (sector_start < num_sectors-(num_sectors / 1024))
>> +                     sr_bp = __ilog2_u32(num_sectors) - 8;
>> +             else
>> +                     sr_bp = 0;  /* non area protected */
>
> Yikes, that's ugly! And I'm not sure it matches the EPCQ doc I found.
> I'm pretty sure you can rewrite this if/else-if/else block in about 1
> line though.
>
>> +
>> +             if (sr_bp < 0) {
>
> sr_bp is unsigned, so this is never true.
>
>> +                     dev_err(&dev->pdev->dev, "%s: address is out of range\n"
>> +                             , __func__);
>> +                     ret = -EINVAL;
>> +                     goto err;
>> +             }
>> +             /*set TB = 0*/
>> +             sr_tb = 0;
>> +
>> +     } else {
>> +             if (sector_end < 1)
>> +                     sr_bp = 1;
>> +             else if (sector_end < 2)
>> +                     sr_bp = 2;
>> +             else if (sector_end < 4)
>> +                     sr_bp = 3;
>> +             else if (sector_end < 8)
>> +                     sr_bp = 4;
>> +             else if (sector_end < 16)
>> +                     sr_bp = 5;
>> +             else if (sector_end < 32)
>> +                     sr_bp = 6;
>> +             else if (sector_end < 64)
>> +                     sr_bp = 7;
>> +             else if (sector_end < 128)
>> +                     sr_bp = 8;
>> +             else if (sector_end < 256)
>> +                     sr_bp = 9;
>> +             else if (sector_end < 512)
>> +                     sr_bp = 10;
>> +             else
>> +                     sr_bp = 16; /*protect all areas*/
>
> Again, this is ugly. How about this?
>
>         sr_bp = fls(sector_end) + 1;
>
>> +
>> +             sr_tb = 1;
>> +     }
>> +
>> +     mem_op = (sr_tb << 12) | (sr_bp << 8);
>> +     mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
>> +     mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
>> +     writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
>> +
>> +err:
>> +     mutex_unlock(&flash->lock);
>> +     return ret;
>> +}
>> +
>> +static int altera_epcq_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
>> +{
>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>> +     struct altera_epcq *dev = mtd->priv;
>> +
>> +     int ret = 0;
>> +     u32 mem_op;
>> +
>> +     mutex_lock(&flash->lock);
>> +     /* wait until finished previous write command. */
>> +     ret = altera_epcq_wait_till_ready(dev);
>> +     if (ret)
>> +             goto err;
>> +     dev_info(&dev->pdev->dev, "Unlock all protected area\n");
>> +     mem_op = 0;
>> +     mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
>> +     mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
>> +     writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
>> +
>> +err:
>> +     mutex_unlock(&flash->lock);
>> +     return ret;
>> +}
>> +
>> +static void altera_epcq_chip_select(struct altera_epcq *dev, u32 bank)
>> +{
>> +     u32 val = 0;
>> +
>> +     switch (bank) {
>> +     case 0:
>> +             val = EPCQ_CHIP_SELECT_0;
>> +             break;
>> +     case 1:
>> +             val = EPCQ_CHIP_SELECT_1;
>> +             break;
>> +     case 2:
>> +             val = EPCQ_CHIP_SELECT_2;
>> +             break;
>> +     default:
>> +             return;
>> +     }
>> +
>> +     writel(val, dev->csr_base + EPCQ_CHIP_SELECT_REG);
>> +}
>> +
>> +static int altera_epcq_probe_flash(struct altera_epcq *dev, u32 bank)
>> +{
>> +     int ret = 0;
>> +     u32 val = 0;
>> +
>> +     mutex_lock(&dev->lock);
>> +
>> +     /*select bank*/
>
> Comment spacing, here and elsewhere:
>
>         /* select bank */
>
>> +     altera_epcq_chip_select(dev, bank);
>> +
>> +     ret = altera_epcq_wait_till_ready(dev);
>> +     if (ret)
>> +             goto err;
>> +
>> +     /* get device sillicon id */
>> +     if (dev->is_epcs)
>> +             val = readl(dev->csr_base + EPCQ_SID_REG);
>> +     else
>> +             val = readl(dev->csr_base + EPCQ_RDID_REG);
>> +
>> +     /* get flash index based on the device list*/
>> +     ret = get_flash_index(val, dev->is_epcs);
>> +     return 0;
>> +err:
>> +     mutex_unlock(&dev->lock);
>> +     return ret;
>> +}
>> +
>> +static int altera_epcq_probe_config_dt(struct platform_device *pdev,
>> +                                    struct device_node *np,
>> +                                    struct altera_epcq_plat_data *pdata)
>> +{
>> +     struct device_node *pp = NULL;
>> +     struct resource *epcq_res;
>> +     int i = 0;
>> +     u32 id;
>> +
>> +     pdata->is_epcs = of_property_read_bool(np, "is-epcs");
>> +
>> +     epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
>> +                                             "csr_base");
>> +     if (!epcq_res) {
>
> devm_ioremap_resource() will check the that 'epcq_res' is valid, so you
> don't need this whole 'if' block.
>
>> +             dev_err(&pdev->dev, "resource csr base is not defined\n");
>> +             return -ENODEV;
>> +     }
>> +
>> +     pdata->csr_base = devm_ioremap_resource(&pdev->dev, epcq_res);
>> +     if (IS_ERR(pdata->csr_base)) {
>> +             dev_err(&pdev->dev, "%s: ERROR: failed to map csr base\n",
>> +                     __func__);
>> +             return PTR_ERR(pdata->csr_base);
>> +     }
>> +
>> +     epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
>> +                                             "data_base");
>> +     if (!epcq_res) {
>
> Same here.
>
>> +             dev_err(&pdev->dev, "resource data base is not defined\n");
>> +             return -ENODEV;
>> +     }
>> +
>> +     pdata->data_base = devm_ioremap_resource(&pdev->dev, epcq_res);
>> +     if (IS_ERR(pdata->data_base)) {
>> +             dev_err(&pdev->dev, "%s: ERROR: failed to map data base\n",
>> +                     __func__);
>> +             return PTR_ERR(pdata->data_base);
>> +     }
>> +
>> +     pdata->board_flash_info = devm_kzalloc(&pdev->dev,
>> +                                            sizeof(*pdata->board_flash_info),
>> +                                            GFP_KERNEL);
>> +
>> +     /* Fill structs for each subnode (flash device) */
>> +     while ((pp = of_get_next_child(np, pp))) {
>
> for_each_available_child_of_node()?
>
>> +             struct altera_epcq_flash_info *flash_info;
>> +
>> +             flash_info = &pdata->board_flash_info[i];
>
> This is never used. Either use it below, or drop the local variable.
>
>> +             pdata->np[i] = pp;
>> +
>> +             /* Read bank id from DT */
>> +             if (of_get_property(pp, "reg", &id))
>
> Is this property optional? Your DT binding doc doesn't make it clear,
> but it seems like a property which would be wise to require (i.e., not
> optional).
>
>> +                     pdata->board_flash_info[i].bank = id;
>> +             i++;
>> +     }
>> +     pdata->num_flashes = i;
>> +     return 0;
>> +}
>> +
>> +static int altera_epcq_setup_banks(struct platform_device *pdev,
>> +                                u32 bank, struct device_node *np,
>> +                                struct altera_epcq_plat_data *pdata)
>> +{
>> +     struct altera_epcq *dev = platform_get_drvdata(pdev);
>> +     struct mtd_part_parser_data ppdata = {};
>> +     struct altera_epcq_flash_info *flash_info;
>> +     struct altera_epcq_flash *flash;
>> +     struct mtd_partition *parts = NULL;
>
> Drop this variable. Just use NULL directly below.
>
>> +     int count = 0;
>
> Same. Just use 0 below.
>
>> +     int flash_index;
>> +     int ret = 0;
>> +     uint64_t size;
>> +     uint32_t sector_size;
>> +
>> +     flash_info = &pdata->board_flash_info[bank];
>> +     if (!flash_info)
>> +             return -ENODEV;
>> +
>> +     if (bank > pdata->num_flashes - 1)
>> +             return -EINVAL;
>> +
>> +     flash = devm_kzalloc(&pdev->dev, sizeof(*flash), GFP_KERNEL);
>> +     if (!flash)
>> +             return -ENOMEM;
>> +     flash->bank = bank;
>> +
>> +     mutex_init(&flash->lock);
>> +
>> +     /* verify whether flash is really present on board */
>> +     flash_index = altera_epcq_probe_flash(dev, bank);
>> +     if (flash_index < 0) {
>> +             dev_info(&dev->pdev->dev, "epcq:flash%d not found\n",
>> +                      flash->bank);
>> +             return flash_index;
>> +     }
>> +
>> +     dev->flash[bank] = flash;
>> +
>> +     size = flash_devices[flash_index].size_inbytes;
>> +     sector_size = flash_devices[flash_index].sectorsize_inbytes;
>> +     /*use do_div instead of plain div to avoid linker err*/
>> +     do_div(size, sector_size);
>> +     flash->num_sectors = size;
>> +
>> +     /*mtd framework */
>> +     flash->mtd.priv = dev;
>> +     flash->mtd.name = flash_devices[flash_index].name;
>> +     flash->mtd.type = MTD_NORFLASH;
>> +     flash->mtd.writesize = 1;
>> +     flash->mtd.flags = MTD_CAP_NORFLASH;
>> +     flash->mtd.size = flash_devices[flash_index].size_inbytes;
>> +     flash->mtd.erasesize = flash_devices[flash_index].sectorsize_inbytes;
>> +     flash->mtd._erase = altera_epcq_erase;
>> +     flash->mtd._read = altera_epcq_read;
>> +     flash->mtd._write = altera_epcq_write;
>> +     flash->mtd._lock = altera_epcq_lock;
>> +     flash->mtd._unlock = altera_epcq_unlock;
>> +
>> +     dev_info(&pdev->dev, "mtd .name=%s .size=0x%llx (%lluM)\n",
>> +              flash->mtd.name, (long long)flash->mtd.size,
>> +              (long long)(flash->mtd.size >> 20));
>> +
>> +     dev_info(&pdev->dev, ".erasesize = 0x%x(%uK)\n",
>> +              flash->mtd.erasesize, flash->mtd.erasesize >> 10);
>> +
>> +     ppdata.of_node = np;
>> +
>> +     ret = mtd_device_parse_register(&flash->mtd, NULL, &ppdata, parts,
>> +                                     count);
>> +     if (ret) {
>> +             dev_err(&pdev->dev, "Err MTD partition=%d\n", ret);
>> +             goto err;
>
> You don't want to unregister a MTD that failed to register. The MTD core
> code should handle that itself.
>
> Instead of this if (ret) {} block, I'd just make this:
>
>         return mtd_device_parse_register(&flash->mtd, NULL, &ppdata, NULL, 0);
>
>> +     }
>> +
>> +     return 0;
>> +
>> +err:
>> +     mtd_device_unregister(&flash->mtd);
>> +     return ret;
>> +}
>> +
>> +static int altera_epcq_probe(struct platform_device *pdev)
>> +{
>> +     struct device_node *np = pdev->dev.of_node;
>> +     struct altera_epcq_plat_data *pdata = NULL;
>> +     struct altera_epcq *dev;
>> +     int ret = 0;
>> +     int i;
>> +
>> +     if (!np) {
>> +             ret = -ENODEV;
>> +             dev_err(&pdev->dev, "no device found\n");
>> +             goto err;
>> +     }
>> +
>> +     pdata = devm_kzalloc(&pdev->dev,
>> +                          sizeof(struct altera_epcq_plat_data),
>> +                          GFP_KERNEL);
>> +
>> +     if (!pdata) {
>> +             ret = -ENOMEM;
>> +             dev_err(&pdev->dev, "%s: ERROR: no memory\n", __func__);
>
> Unnecessary print. The MM code will print plenty of info if you're
> out of memory.
>
>> +             goto err;
>> +     }
>> +     ret = altera_epcq_probe_config_dt(pdev, np, pdata);
>> +     if (ret) {
>> +             ret = -ENODEV;
>> +             dev_err(&pdev->dev, "probe fail\n");
>> +             goto err;
>> +     }
>> +
>> +     dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
>> +     if (!dev) {
>> +             ret = -ENOMEM;
>> +             dev_err(&pdev->dev, "mem alloc fail\n");
>
> Same here.
>
>> +             goto err;
>> +     }
>> +     mutex_init(&dev->lock);
>> +     dev->pdev = pdev;
>> +     dev->is_epcs = pdata->is_epcs;
>> +     dev->csr_base = pdata->csr_base;
>> +     dev->data_base = pdata->data_base;
>> +
>> +     /*check number of flashes*/
>> +     dev->num_flashes = pdata->num_flashes;
>> +     if (dev->num_flashes > MAX_NUM_FLASH_CHIP) {
>> +             dev_err(&pdev->dev, "exceeding max number of flashes\n");
>> +             dev->num_flashes = MAX_NUM_FLASH_CHIP;
>> +     }
>> +
>> +     /* check clock*/
>> +     dev->clk = devm_clk_get(&pdev->dev, NULL);
>> +     if (IS_ERR(dev->clk)) {
>> +             ret = PTR_ERR(dev->clk);
>> +             goto err;
>> +     }
>> +     ret = clk_prepare_enable(dev->clk);
>> +     if (ret)
>> +             goto err;
>> +
>> +     platform_set_drvdata(pdev, dev);
>> +
>> +     /* loop for each serial flash which is connected to epcq */
>> +     for (i = 0; i < dev->num_flashes; i++) {
>> +             ret = altera_epcq_setup_banks(pdev, i, pdata->np[i], pdata);
>> +             if (ret) {
>> +                     dev_err(&pdev->dev, "bank setup failed\n");
>> +                     goto err_bank_setup;
>> +             }
>> +     }
>> +
>> +     return 0;
>> +
>> +err_bank_setup:
>> +     clk_disable_unprepare(dev->clk);
>> +err:
>> +     return ret;
>> +}
>> +
>> +static int altera_epcq_remove(struct platform_device *pdev)
>> +{
>> +     struct altera_epcq *dev;
>> +     struct altera_epcq_flash *flash;
>> +     int ret, i;
>> +
>> +     dev = platform_get_drvdata(pdev);
>> +
>> +     /* clean up for all nor flash */
>> +     for (i = 0; i < dev->num_flashes; i++) {
>> +             flash = dev->flash[i];
>> +             if (!flash)
>> +                     continue;
>> +
>> +             /* clean up mtd stuff */
>> +             ret = mtd_device_unregister(&flash->mtd);
>> +             if (ret)
>> +                     dev_err(&pdev->dev, "error removing mtd\n");
>> +     }
>> +
>> +     clk_disable_unprepare(dev->clk);
>> +
>> +     return 0;
>> +}
>> +
>> +#ifdef CONFIG_PM
>
> Make this CONFIG_PM_SLEEP.
>
>> +static int altera_epcq_suspend(struct device *dev)
>> +{
>> +     struct altera_epcq *sdev = dev_get_drvdata(dev);
>> +
>> +     clk_disable_unprepare(sdev->clk);
>> +
>> +     return 0;
>> +}
>> +
>> +static int altera_epcq_resume(struct device *dev)
>> +{
>> +     struct altera_epcq *sdev = dev_get_drvdata(dev);
>> +     int ret = -EPERM;
>
> Drop 'ret'.
>
>> +
>> +     ret = clk_prepare_enable(sdev->clk);
>
> Just:
>
>         return clk_prepare_enable(sdev->clk);
>
>> +
>> +     return ret;
>> +}
>> +
>> +static SIMPLE_DEV_PM_OPS(altera_epcq_pm_ops, altera_epcq_suspend,
>> +                      altera_epcq_resume);
>> +#endif
>> +
>> +static const struct of_device_id altera_epcq_id_table[] = {
>> +     { .compatible = "altr,epcq-1.0" },
>> +     {}
>> +};
>> +MODULE_DEVICE_TABLE(of, altera_epcq_id_table);
>> +
>> +static struct platform_driver altera_epcq_driver = {
>> +     .driver = {
>> +             .name = ALTERA_EPCQ_RESOURCE_NAME,
>> +             .bus = &platform_bus_type,
>> +             .owner = THIS_MODULE,
>
> The .owner assignment is no longer necessary.
>
>> +             .of_match_table = altera_epcq_id_table,
>> +#ifdef CONFIG_PM
>
> Also CONFIG_PM_SLEEP.
>
>> +             .pm = &altera_epcq_pm_ops,
>> +#endif
>> +     },
>> +     .probe = altera_epcq_probe,
>> +     .remove = altera_epcq_remove,
>> +};
>> +module_platform_driver(altera_epcq_driver);
>> +
>> +MODULE_AUTHOR("Viet Nga Dao <vndao@altera.com>");
>> +MODULE_DESCRIPTION("MTD Altera EPCQ Driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/mtd/devices/altera_epcq.h b/drivers/mtd/devices/altera_epcq.h
>> new file mode 100644
>> index 0000000..c3d15e5
>> --- /dev/null
>> +++ b/drivers/mtd/devices/altera_epcq.h
>> @@ -0,0 +1,130 @@
>> +/*
>> + * Copyright (C) 2014 Altera Corporation. All rights reserved
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms and conditions of the GNU General Public License,
>> + * version 2, as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope 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.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#ifndef __ALTERA_ECPQ_H
>> +#define __ALTERA_ECPQ_H
>> +
>> +#define ALTERA_EPCQ_RESOURCE_NAME        "altera_epcq"
>> +/* max possible slots for serial flash chip in the EPCQ controller */
>> +#define MAX_NUM_FLASH_CHIP   3
>> +
>> +/* macro to define partitions for flash devices */
>> +#define DEFINE_PARTS(n, of, s)               \
>
> This macro is not used anywhere. Drop it?
>
>> +{                                    \
>> +     .name = n,                      \
>> +     .offset = of,                   \
>> +     .size = s,                      \
>> +}
>> +
>> +struct altera_epcq_flash_info {
>> +     u32 bank;
>> +     struct mtd_partition *partitions;
>> +     int nr_partitions;
>> +};
>> +
>> +struct altera_epcq_plat_data {
>> +     void __iomem *csr_base;
>> +     void __iomem *data_base;
>> +     bool is_epcs;
>> +     u32 num_flashes;
>> +     struct altera_epcq_flash_info *board_flash_info;
>> +     struct device_node *np[MAX_NUM_FLASH_CHIP];
>> +};
>> +
>> +struct altera_epcq {
>> +     struct clk *clk;
>> +     bool is_epcs;
>> +     struct mutex lock;      /*device lock*/
>> +     void __iomem *csr_base;
>> +     void __iomem *data_base;
>> +     struct platform_device *pdev;
>> +     u32 num_flashes;
>> +     struct altera_epcq_flash *flash[MAX_NUM_FLASH_CHIP];
>> +};
>> +
>> +struct altera_epcq_flash {
>> +     u32 bank;
>> +     u32 num_sectors;
>> +     u32 num_parts;
>> +     struct mtd_partition *parts;
>> +     struct mutex lock;      /*flash lock*/
>> +     struct mtd_info mtd;
>> +};
>> +
>> +/* Define max times to check status register before we give up. */
>> +#define EPCQ_MAX_TIME_OUT                    (40 * HZ)
>> +
>> +/* defines for status register */
>> +#define EPCQ_SR_REG                          0x0
>> +#define EPCQ_SR_WIP_MASK                     0x00000001
>> +#define EPCQ_SR_WIP                          0x1
>> +#define EPCQ_SR_WEL                          0x2
>> +#define EPCQ_SR_BP0                          0x4
>> +#define EPCQ_SR_BP1                          0x8
>> +#define EPCQ_SR_BP2                          0x10
>> +#define EPCQ_SR_BP3                          0x40
>> +#define EPCQ_SR_TB                           0x20
>> +
>> +/* defines for device id register */
>> +#define EPCQ_SID_REG                         0x4
>> +#define EPCQ_RDID_REG                                0x8
>> +#define EPCQ_RDID_MASK                               0x000000FF
>> +/*
>> + * EPCQ_MEM_OP register offset
>> + *
>> + * The EPCQ_MEM_OP register is used to do memory protect and erase operations
>> + *
>> + */
>> +#define EPCQ_MEM_OP_REG                              0xC
>> +
>> +#define EPCQ_MEM_OP_CMD_MASK                 0x00000003
>> +#define EPCQ_MEM_OP_BULK_ERASE_CMD           0x00000001
>> +#define EPCQ_MEM_OP_SECTOR_ERASE_CMD         0x00000002
>> +#define EPCQ_MEM_OP_SECTOR_PROTECT_CMD               0x00000003
>> +#define EPCQ_MEM_OP_SECTOR_VALUE_MASK                0x0003FF00
>> +#define EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK        0x00001F00
>> +#define EPCQ_MEM_OP_SECTOR_PROTECT_SHIFT     8
>> +/*
>> + * EPCQ_ISR register offset
>> + *
>> + * The EPCQ_ISR register is used to determine whether an invalid write or erase
>> + * operation trigerred an interrupt
>> + *
>> + */
>> +#define EPCQ_ISR_REG                         0x10
>> +
>> +#define EPCQ_ISR_ILLEGAL_ERASE_MASK          0x00000001
>> +#define EPCQ_ISR_ILLEGAL_WRITE_MASK          0x00000002
>> +
>> +/*
>> + * EPCQ_IMR register offset
>> + *
>> + * The EPCQ_IMR register is used to mask the invalid erase or the invalid write
>> + * interrupts.
>> + *
>> + */
>> +#define EPCQ_IMR_REG                         0x14
>> +#define EPCQ_IMR_ILLEGAL_ERASE_MASK          0x00000001
>> +
>> +#define EPCQ_IMR_ILLEGAL_WRITE_MASK          0x00000002
>> +
>> +#define EPCQ_CHIP_SELECT_REG                 0x18
>> +#define EPCQ_CHIP_SELECT_MASK                        0x00000007
>> +#define EPCQ_CHIP_SELECT_0                   0x00000001
>> +#define EPCQ_CHIP_SELECT_1                   0x00000002
>> +#define EPCQ_CHIP_SELECT_2                   0x00000004
>> +
>> +#endif /* __ALTERA_ECPQ_H */
>
> Brian

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-01-15  9:27 ` Viet Nga Dao
  0 siblings, 0 replies; 39+ messages in thread
From: Viet Nga Dao @ 2015-01-15  9:27 UTC (permalink / raw)
  To: Brian Norris
  Cc: vndao-EIB2kfCEclfQT0dZR+AlfA, dwmw2-wEGCiKHe2LqWVfeAwA7xHQ,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, nga_chi86

On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
<computersforpeace-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org wrote:
>> From: Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org>
>>
>> Altera EPCQ Controller is a soft IP which enables access to Altera EPCQ and
>> EPCS flash chips. This patch adds driver for these devices.
>>
>> Signed-off-by: Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org>
>
> This drivers seems awfully similar to (and so I infer it is likely
> copy-and-pasted from) m25p80.c / spi-nor.c. Do you think it can be
> rewritten as a SPI NOR driver, under drivers/mtd/spi-nor/ ? It looks
> like these flash share most (all?) the same basic opcodes.
>
For Altera EPCQ flashes, almost operations are performed underline
hardware. Software only able to perform the following through
registers:
 -  read status register
 -  read id
 -  write status registers bit SR_BP0,SR_BP1, SR_BP2,SR_BP3, SR_TB
(http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf)
For read/write data: all the operations like QUAD_READ/WRITE,
FAST_READ/WRITE are handled by hardware as well. From software point
of view, there is no difference between these 2 modes.
That is why if rewrite the drivers to follow spi-nor structure, it
will require extra decoding works for the only few used opcodes.

>> ---
>>  .../devicetree/bindings/mtd/altera_epcq.txt        |   45 ++
>>  drivers/mtd/devices/Kconfig                        |   12 +
>>  drivers/mtd/devices/Makefile                       |    2 +-
>>  drivers/mtd/devices/altera_epcq.c                  |  804 ++++++++++++++++++++
>>  drivers/mtd/devices/altera_epcq.h                  |  130 ++++
>>  5 files changed, 992 insertions(+), 1 deletions(-)
>>  create mode 100644 Documentation/devicetree/bindings/mtd/altera_epcq.txt
>>  create mode 100644 drivers/mtd/devices/altera_epcq.c
>>  create mode 100644 drivers/mtd/devices/altera_epcq.h
>>
>> diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>> new file mode 100644
>> index 0000000..d14f50e
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>> @@ -0,0 +1,45 @@
>> +* MTD Altera EPCQ driver
>> +
>> +Required properties:
>> +- compatible: Should be "altr,epcq-1.0"
>> +- reg: Address and length of the register set  for the device. It contains
>> +  the information of registers in the same order as described by reg-names
>> +- reg-names: Should contain the reg names
>> +  "csr_base": Should contain the register configuration base address
>> +  "data_base": Should contain the data base address
>> +- is-epcs: boolean type.
>> +             If present, the device contains EPCS flashes.
>> +             Otherwise, it contains EPCQ flashes.
>> +- #address-cells: Must be <1>.
>> +- #size-cells: Must be <0>.
>> +- flash device tree subnode, there must be a node with the following fields:
>
> These subnodes definitely require a 'compatible' string. Perhaps they
> should be used to differentiate EPCS vs. EPCQ. Does "is-epcs" really
> need to be in the top-level controller node?
>
>> +     - reg: Should contain the flash id
>
> Should, or must? (This question is relevant, because you seem to make it
> optional in your code.) And what does the "flash ID" mean? It seems like
> you're using as a chip-select or bank index.
>
>> +     - #address-cells: please refer to /mtd/partition.txt
>> +     - #size-cells: please refer to /mtd/partition.txt
>> +     For partitions inside each flash, please refer to /mtd/partition.txt
>> +
>> +Example:
>> +
>> +                     epcq_controller_0: epcq@0x000000000 {
>> +                             compatible = "altr,epcq-1.0";
>> +                             reg = <0x00000001 0x00000000 0x00000020>,
>> +                                     <0x00000000 0x00000000 0x02000000>;
>> +                             reg-names = "csr_base", "data_base";
>> +                             #address-cells = <1>;
>> +                             #size-cells = <0>;
>> +                             flash0: epcq256@0 {
>> +                                     reg = <0>;
>> +                                     #address-cells = <1>;
>> +                                     #size-cells = <1>;
>> +                                     partition@0 {
>> +                                             /* 16 MB for raw data. */
>> +                                             label = "EPCQ Flash 0 raw data";
>> +                                             reg = <0x0 0x1000000>;
>> +                                     };
>> +                                     partition@1000000 {
>> +                                             /* 16 MB for jffs2 data. */
>> +                                             label = "EPCQ Flash 0 JFFS 2";
>> +                                             reg = <0x1000000 0x1000000>;
>> +                                     };
>> +                             };
>> +                     }; //end epcq@0x000000000 (epcq_controller_0)
>> diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
>> index c49d0b1..020b864 100644
>> --- a/drivers/mtd/devices/Kconfig
>> +++ b/drivers/mtd/devices/Kconfig
>> @@ -218,6 +218,18 @@ config MTD_ST_SPI_FSM
>>         SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
>>         for a subset of connected Serial Flash devices.
>>
>> +config MTD_ALTERA_EPCQ
>> +     tristate "Support Altera EPCQ/EPCS Flash chips"
>> +     depends on OF
>> +     help
>> +       This enables access to Altera EPCQ/EPCS flash chips, used for data
>> +       storage. See the driver source for the current list,
>> +       or to add other chips.
>> +
>> +       If you want to compile this driver as a module ( = code which can be
>> +       inserted in and removed from the running kernel whenever you want),
>> +       say M here and read <file:Documentation/kbuild/modules.txt>.
>> +
>>  if MTD_DOCG3
>>  config BCH_CONST_M
>>       default 14
>> diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
>> index f0b0e61..b429c4d 100644
>> --- a/drivers/mtd/devices/Makefile
>> +++ b/drivers/mtd/devices/Makefile
>> @@ -16,6 +16,6 @@ obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
>>  obj-$(CONFIG_MTD_SST25L)     += sst25l.o
>>  obj-$(CONFIG_MTD_BCM47XXSFLASH)      += bcm47xxsflash.o
>>  obj-$(CONFIG_MTD_ST_SPI_FSM)    += st_spi_fsm.o
>> -
>> +obj-$(CONFIG_MTD_ALTERA_EPCQ)                += altera_epcq.o
>>
>>  CFLAGS_docg3.o                       += -I$(src)
>> diff --git a/drivers/mtd/devices/altera_epcq.c b/drivers/mtd/devices/altera_epcq.c
>> new file mode 100644
>> index 0000000..09213d5
>> --- /dev/null
>> +++ b/drivers/mtd/devices/altera_epcq.c
>> @@ -0,0 +1,804 @@
>> +/*
>> + * Copyright (C) 2014 Altera Corporation. All rights reserved
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms and conditions of the GNU General Public License,
>> + * version 2, as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope 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.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/device.h>
>> +#include <linux/err.h>
>> +#include <linux/errno.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/ioport.h>
>> +#include <linux/jiffies.h>
>> +#include <linux/kernel.h>
>> +#include <linux/log2.h>
>> +#include <linux/module.h>
>> +#include <linux/mtd/mtd.h>
>> +#include <linux/mtd/partitions.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/param.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pm.h>
>> +#include <linux/sched.h>
>> +#include <linux/slab.h>
>> +#include <linux/wait.h>
>> +
>> +#include "altera_epcq.h"
>> +
>> +/* data structure to maintain flash ids from different vendors */
>> +struct flash_device {
>> +     char *name;
>> +     bool is_epcs;
>> +     u32 device_id;
>> +     uint32_t sectorsize_inbytes;
>> +     uint64_t size_inbytes;
>> +     u32 pagesize;
>> +};
>> +
>> +#define FLASH_ID(_n, _is_epcs, _id, _ssize, _size, _psize)   \
>> +{                            \
>> +     .name = (_n),           \
>> +     .is_epcs = (_is_epcs),          \
>> +     .device_id = (_id),     \
>> +     .sectorsize_inbytes = (_ssize), \
>> +     .size_inbytes = (_size),        \
>> +     .pagesize = (_psize),   \
>> +}
>> +
>> +static struct flash_device flash_devices[] = {
>> +     FLASH_ID("epcq16"    , 0, 0x15, 0x10000, 0x200000, 0x100),
>> +     FLASH_ID("epcq32"    , 0, 0x16, 0x10000, 0x400000, 0x100),
>> +     FLASH_ID("epcq64"    , 0, 0x17, 0x10000, 0x800000, 0x100),
>> +     FLASH_ID("epcq128"   , 0, 0x18, 0x10000, 0x1000000, 0x100),
>> +     FLASH_ID("epcq256"   , 0, 0x19, 0x10000, 0x2000000, 0x100),
>> +     FLASH_ID("epcq512"   , 0, 0x20, 0x10000, 0x4000000, 0x100),
>> +
>> +     FLASH_ID("epcs16"    , 1, 0x14, 0x10000, 0x200000, 0x100),
>> +     FLASH_ID("epcs64"    , 1, 0x16, 0x10000, 0x800000, 0x100),
>> +     FLASH_ID("epcs128"   , 1, 0x18, 0x40000, 0x1000000, 0x100),
>> +};
>> +
>> +static inline struct altera_epcq_flash *get_flash_data(struct mtd_info *mtd)
>> +{
>> +     return container_of(mtd, struct altera_epcq_flash, mtd);
>> +}
>> +
>> +static u32 altera_epcq_read_sr(struct altera_epcq *dev)
>> +{
>> +     return readl(dev->csr_base + EPCQ_SR_REG);
>> +}
>> +
>> +static int altera_epcq_wait_till_ready(struct altera_epcq *dev)
>> +{
>> +     unsigned long finish;
>> +     int status;
>> +
>> +     finish = jiffies + EPCQ_MAX_TIME_OUT;
>> +     do {
>> +             status = altera_epcq_read_sr(dev);
>> +             if (status < 0)
>> +                     return status;
>> +             else if (!(status & EPCQ_SR_WIP))
>> +                     return 0;
>> +
>> +             cond_resched();
>> +     } while (!time_after_eq(jiffies, finish));
>> +
>> +     dev_err(&dev->pdev->dev, "epcq controller is busy, timeout\n");
>> +     return -EBUSY;
>> +}
>> +
>> +static int get_flash_index(u32 flash_id, bool is_epcs)
>> +{
>> +     int index;
>> +
>> +     for (index = 0; index < ARRAY_SIZE(flash_devices); index++) {
>> +             if (flash_devices[index].device_id == flash_id &&
>> +                 flash_devices[index].is_epcs == is_epcs)
>> +                     return index;
>> +     }
>> +
>> +     /* Memory chip is not listed and not supported */
>> +     return -ENODEV;
>> +}
>> +
>> +static int altera_epcq_write_erase_check(struct altera_epcq *dev,
>> +                                      bool write_erase)
>> +{
>> +     u32 val;
>> +     u32 mask;
>> +
>> +     if (write_erase)
>> +             mask = EPCQ_ISR_ILLEGAL_WRITE_MASK;
>> +     else
>> +             mask = EPCQ_ISR_ILLEGAL_ERASE_MASK;
>> +
>> +     val = readl(dev->csr_base + EPCQ_ISR_REG);
>> +     if (val & mask) {
>> +             dev_err(&dev->pdev->dev,
>> +                     "write/erase failed, sector might be protected\n");
>> +             /*clear this status for next use*/
>> +             writel(val, dev->csr_base + EPCQ_ISR_REG);
>> +             return -EIO;
>> +     }
>> +     return 0;
>> +}
>> +
>> +static int altera_epcq_erase_chip(struct mtd_info *mtd)
>> +{
>> +     int ret;
>> +     u32 val;
>> +     struct altera_epcq *dev = mtd->priv;
>> +
>> +     /* Wait until finished previous write command. */
>> +     ret = altera_epcq_wait_till_ready(dev);
>
> This pattern is pretty silly. We don't need to have every operation
> check the status of the previous operation. Each operation should check
> itself. You obviously copied this one... but we recently changed that:
>
>     commit dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3
>     Author: Brian Norris <computersforpeace-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>     Date:   Wed Aug 6 18:16:57 2014 -0700
>
>         mtd: spi-nor: move "wait-till-ready" checks into erase/write
>         functions
>
>
>     https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3
>
>> +     if (ret)
>> +             return ret;
>> +
>> +     /* erase chip. */
>> +     val = EPCQ_MEM_OP_BULK_ERASE_CMD;
>> +     writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
>> +
>> +     /* Wait until finished previous write command. */
>> +     ret = altera_epcq_wait_till_ready(dev);
>> +     if (ret)
>> +             return ret;
>> +
>> +      /* check whether write triggered a illegal write interrupt */
>> +     ret = altera_epcq_write_erase_check(dev, 0);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     return 0;
>> +}
>> +
>> +static int altera_epcq_addr_to_sector(struct altera_epcq_flash *flash,
>> +                                   int addr_offset)
>> +{
>> +     int sector = 0;
>> +     int i;
>> +
>> +     for (i = 0; i < flash->num_sectors; i++) {
>> +             if (addr_offset >= i * flash->mtd.erasesize && addr_offset <
>> +                 (i * flash->mtd.erasesize + flash->mtd.erasesize)) {
>> +                     sector = i;
>> +                     return sector;
>> +             }
>> +     }
>
> Uh, really? Am I crazy, or shouldn't this whole function just be a
> really simple arithmetic operation? Like:
>
>         return addr_offset >> mtd->erasesize_shift;
>
>> +     return -1;
>> +}
>> +
>> +static int altera_epcq_erase_sector(struct mtd_info *mtd,
>> +                                 int addr_offset)
>> +{
>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>> +     struct altera_epcq *dev = mtd->priv;
>> +     u32 val;
>> +     int ret;
>> +     int sector_value;
>> +
>> +     ret = altera_epcq_wait_till_ready(dev);
>> +     if (ret)
>> +             return ret;
>> +
>> +     sector_value = altera_epcq_addr_to_sector(flash, addr_offset);
>> +
>> +     /* sanity check that block_offset is a valid sector number */
>> +     if (sector_value < 0)
>> +             return -EINVAL;
>> +
>> +     /* sector value should occupy bits 17:8 */
>> +     val = (sector_value << 8) & EPCQ_MEM_OP_SECTOR_VALUE_MASK;
>> +
>> +     /* sector erase commands occupies lower 2 bits */
>> +     val |= EPCQ_MEM_OP_SECTOR_ERASE_CMD;
>> +
>> +     /* write sector erase command to EPCQ_MEM_OP register*/
>> +     writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
>> +
>> +     ret = altera_epcq_wait_till_ready(dev);
>> +     if (ret)
>> +             return ret;
>> +
>> +      /* check whether write triggered a illegal write interrupt */
>
> ^^ extra space here? You have <TAB><SPACE>/* check [...]
>
>> +     ret = altera_epcq_write_erase_check(dev, 0);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     return 0;
>> +}
>> +
>> +static int altera_epcq_erase(struct mtd_info *mtd, struct erase_info *e_info)
>> +{
>> +     u32 addr;
>> +     int ret;
>> +     u32 len;
>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>> +     struct altera_epcq *dev = mtd->priv;
>> +
>> +     addr = e_info->addr;
>> +     len = e_info->len;
>> +
>> +     if (flash->bank > dev->num_flashes - 1) {
>> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
>> +             return -EINVAL;
>> +     }
>> +     mutex_lock(&flash->lock);
>> +
>> +     /*erase whole chip*/
>> +     if (len == mtd->size) {
>> +             if (altera_epcq_erase_chip(mtd)) {
>> +                     e_info->state = MTD_ERASE_FAILED;
>> +                     mutex_unlock(&flash->lock);
>> +                     return -EIO;
>> +             }
>> +     /*"sector"-at-a-time erase*/
>> +     } else {
>> +             while (len) {
>> +                     ret = altera_epcq_erase_sector(mtd, addr);
>> +                     if (ret) {
>> +                             e_info->state = MTD_ERASE_FAILED;
>> +                             mutex_unlock(&flash->lock);
>> +                             return -EIO;
>> +                     }
>> +                     addr += mtd->erasesize;
>> +                     if (len < mtd->erasesize)
>> +                             len = 0;
>> +                     else
>> +                             len -= mtd->erasesize;
>> +             }
>> +     }
>> +     mutex_unlock(&flash->lock);
>> +     e_info->state = MTD_ERASE_DONE;
>> +     mtd_erase_callback(e_info);
>> +
>> +     return 0;
>> +}
>> +
>> +static int altera_epcq_read(struct mtd_info *mtd, loff_t from, size_t len,
>> +                         size_t *retlen, u8 *buf)
>> +{
>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>> +     struct altera_epcq *dev = mtd->priv;
>> +     void *src;
>> +     int ret = 0;
>> +
>> +     if (!flash || !dev)
>> +             return -ENODEV;
>
> Why would either of these be 0? If they are, you have much bigger
> problems, since you aren't checking these almost anywhere else. And the
> !flash check is pretty odd, since get_flash_data() is using
> container_of(), which is not likely to give you exactly 0...
>
>> +
>> +     if (flash->bank > dev->num_flashes - 1) {
>> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     src = dev->data_base + from;
>> +
>> +     mutex_lock(&flash->lock);
>> +     /* wait till previous write/erase is done. */
>> +     ret = altera_epcq_wait_till_ready(dev);
>> +     if (ret)
>> +             goto err;
>> +
>> +     memcpy_fromio(buf, (u8 *)src, len);
>
> Drop the cast. Also, you get sparse warnings because you're dropping the
> __iomem. Why not just this?
>
>         memcpy_fromio(buf, dev->data_base + from, len);
>
> (BTW, you might consider running sparse on all your code. See
> Documentation/sparse.txt. It tells you how to get it, but you can often
> just download it through your distro's package manager.)
>
>> +     *retlen = len;
>> +
>> +err:
>> +     mutex_unlock(&flash->lock);
>> +     return ret;
>> +}
>> +
>> +static int altera_epcq_write(struct mtd_info *mtd, loff_t to, size_t len,
>> +                          size_t *retlen, const u8 *buf)
>> +{
>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>> +     struct altera_epcq *dev = mtd->priv;
>> +     void *dest;
>> +     int ret = 0;
>> +
>> +     if (!flash || !dev)
>> +             return -ENODEV;
>
> Also here. I don't think you need these checks.
>
>> +
>> +     if (flash->bank > dev->num_flashes - 1) {
>> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
>> +             return -EINVAL;
>> +     }
>> +     dest = dev->data_base + to;
>> +
>> +     mutex_lock(&flash->lock);
>> +
>> +     /* wait until finished previous write command. */
>> +     ret = altera_epcq_wait_till_ready(dev);
>> +     if (ret)
>> +             goto err;
>> +
>> +     memcpy_toio(dest, buf, len);
>
> Similar. Why not just this?
>
>         memcpy_toio(dev->data_base + to, buf, len);
>
>> +     *retlen += len;
>> +
>> +     /* wait until finished previous write command. */
>> +     ret = altera_epcq_wait_till_ready(dev);
>> +     if (ret)
>> +             goto err;
>> +
>> +      /* check whether write triggered a illegal write interrupt */
>> +     ret = altera_epcq_write_erase_check(dev, 1);
>> +     if (ret < 0)
>> +             goto err;
>> +
>> +err:
>> +     mutex_unlock(&flash->lock);
>> +     return ret;
>> +}
>> +
>> +static int altera_epcq_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
>> +{
>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>> +     struct altera_epcq *dev = mtd->priv;
>> +     uint32_t offset = ofs;
>> +     int ret = 0;
>> +     u32 sector_start, sector_end;
>> +     u32 num_sectors;
>> +     u32 mem_op;
>> +     unsigned sr_bp = 0;
>> +     unsigned sr_tb = 0;
>> +
>> +     sector_start = altera_epcq_addr_to_sector(flash, offset);
>> +     sector_end = altera_epcq_addr_to_sector(flash, offset + len);
>> +     num_sectors = flash->num_sectors;
>> +     dev_dbg(&dev->pdev->dev,
>> +             "%s: num_setor is %u,sector start is %u,sector end is %u\n",
>> +             __func__, num_sectors, sector_start, sector_end);
>> +
>> +     mutex_lock(&flash->lock);
>> +     /* wait until finished previous write command. */
>> +     ret = altera_epcq_wait_till_ready(dev);
>> +     if (ret)
>> +             goto err;
>> +
>> +     if (sector_start >= num_sectors/2) {
>
> It's preferable to have spaces around operators. So 'num_sectors / 2'.
> The same thing comes up in several other places.
>
>> +             if (sector_start < num_sectors-(num_sectors / 4))
>> +                     sr_bp = __ilog2_u32(num_sectors);
>> +             else if (sector_start < num_sectors-(num_sectors / 8))
>> +                     sr_bp = __ilog2_u32(num_sectors) - 1;
>> +             else if (sector_start < num_sectors-(num_sectors / 16))
>> +                     sr_bp = __ilog2_u32(num_sectors) - 2;
>> +             else if (sector_start < num_sectors-(num_sectors / 32))
>> +                     sr_bp = __ilog2_u32(num_sectors) - 3;
>> +             else if (sector_start < num_sectors-(num_sectors / 64))
>> +                     sr_bp = __ilog2_u32(num_sectors) - 4;
>> +             else if (sector_start < num_sectors-(num_sectors / 128))
>> +                     sr_bp = __ilog2_u32(num_sectors) - 5;
>> +             else if (sector_start < num_sectors-(num_sectors / 256))
>> +                     sr_bp = __ilog2_u32(num_sectors) - 6;
>> +             else if (sector_start < num_sectors-(num_sectors / 512))
>> +                     sr_bp = __ilog2_u32(num_sectors) - 7;
>> +             else if (sector_start < num_sectors-(num_sectors / 1024))
>> +                     sr_bp = __ilog2_u32(num_sectors) - 8;
>> +             else
>> +                     sr_bp = 0;  /* non area protected */
>
> Yikes, that's ugly! And I'm not sure it matches the EPCQ doc I found.
> I'm pretty sure you can rewrite this if/else-if/else block in about 1
> line though.
>
>> +
>> +             if (sr_bp < 0) {
>
> sr_bp is unsigned, so this is never true.
>
>> +                     dev_err(&dev->pdev->dev, "%s: address is out of range\n"
>> +                             , __func__);
>> +                     ret = -EINVAL;
>> +                     goto err;
>> +             }
>> +             /*set TB = 0*/
>> +             sr_tb = 0;
>> +
>> +     } else {
>> +             if (sector_end < 1)
>> +                     sr_bp = 1;
>> +             else if (sector_end < 2)
>> +                     sr_bp = 2;
>> +             else if (sector_end < 4)
>> +                     sr_bp = 3;
>> +             else if (sector_end < 8)
>> +                     sr_bp = 4;
>> +             else if (sector_end < 16)
>> +                     sr_bp = 5;
>> +             else if (sector_end < 32)
>> +                     sr_bp = 6;
>> +             else if (sector_end < 64)
>> +                     sr_bp = 7;
>> +             else if (sector_end < 128)
>> +                     sr_bp = 8;
>> +             else if (sector_end < 256)
>> +                     sr_bp = 9;
>> +             else if (sector_end < 512)
>> +                     sr_bp = 10;
>> +             else
>> +                     sr_bp = 16; /*protect all areas*/
>
> Again, this is ugly. How about this?
>
>         sr_bp = fls(sector_end) + 1;
>
>> +
>> +             sr_tb = 1;
>> +     }
>> +
>> +     mem_op = (sr_tb << 12) | (sr_bp << 8);
>> +     mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
>> +     mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
>> +     writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
>> +
>> +err:
>> +     mutex_unlock(&flash->lock);
>> +     return ret;
>> +}
>> +
>> +static int altera_epcq_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
>> +{
>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>> +     struct altera_epcq *dev = mtd->priv;
>> +
>> +     int ret = 0;
>> +     u32 mem_op;
>> +
>> +     mutex_lock(&flash->lock);
>> +     /* wait until finished previous write command. */
>> +     ret = altera_epcq_wait_till_ready(dev);
>> +     if (ret)
>> +             goto err;
>> +     dev_info(&dev->pdev->dev, "Unlock all protected area\n");
>> +     mem_op = 0;
>> +     mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
>> +     mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
>> +     writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
>> +
>> +err:
>> +     mutex_unlock(&flash->lock);
>> +     return ret;
>> +}
>> +
>> +static void altera_epcq_chip_select(struct altera_epcq *dev, u32 bank)
>> +{
>> +     u32 val = 0;
>> +
>> +     switch (bank) {
>> +     case 0:
>> +             val = EPCQ_CHIP_SELECT_0;
>> +             break;
>> +     case 1:
>> +             val = EPCQ_CHIP_SELECT_1;
>> +             break;
>> +     case 2:
>> +             val = EPCQ_CHIP_SELECT_2;
>> +             break;
>> +     default:
>> +             return;
>> +     }
>> +
>> +     writel(val, dev->csr_base + EPCQ_CHIP_SELECT_REG);
>> +}
>> +
>> +static int altera_epcq_probe_flash(struct altera_epcq *dev, u32 bank)
>> +{
>> +     int ret = 0;
>> +     u32 val = 0;
>> +
>> +     mutex_lock(&dev->lock);
>> +
>> +     /*select bank*/
>
> Comment spacing, here and elsewhere:
>
>         /* select bank */
>
>> +     altera_epcq_chip_select(dev, bank);
>> +
>> +     ret = altera_epcq_wait_till_ready(dev);
>> +     if (ret)
>> +             goto err;
>> +
>> +     /* get device sillicon id */
>> +     if (dev->is_epcs)
>> +             val = readl(dev->csr_base + EPCQ_SID_REG);
>> +     else
>> +             val = readl(dev->csr_base + EPCQ_RDID_REG);
>> +
>> +     /* get flash index based on the device list*/
>> +     ret = get_flash_index(val, dev->is_epcs);
>> +     return 0;
>> +err:
>> +     mutex_unlock(&dev->lock);
>> +     return ret;
>> +}
>> +
>> +static int altera_epcq_probe_config_dt(struct platform_device *pdev,
>> +                                    struct device_node *np,
>> +                                    struct altera_epcq_plat_data *pdata)
>> +{
>> +     struct device_node *pp = NULL;
>> +     struct resource *epcq_res;
>> +     int i = 0;
>> +     u32 id;
>> +
>> +     pdata->is_epcs = of_property_read_bool(np, "is-epcs");
>> +
>> +     epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
>> +                                             "csr_base");
>> +     if (!epcq_res) {
>
> devm_ioremap_resource() will check the that 'epcq_res' is valid, so you
> don't need this whole 'if' block.
>
>> +             dev_err(&pdev->dev, "resource csr base is not defined\n");
>> +             return -ENODEV;
>> +     }
>> +
>> +     pdata->csr_base = devm_ioremap_resource(&pdev->dev, epcq_res);
>> +     if (IS_ERR(pdata->csr_base)) {
>> +             dev_err(&pdev->dev, "%s: ERROR: failed to map csr base\n",
>> +                     __func__);
>> +             return PTR_ERR(pdata->csr_base);
>> +     }
>> +
>> +     epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
>> +                                             "data_base");
>> +     if (!epcq_res) {
>
> Same here.
>
>> +             dev_err(&pdev->dev, "resource data base is not defined\n");
>> +             return -ENODEV;
>> +     }
>> +
>> +     pdata->data_base = devm_ioremap_resource(&pdev->dev, epcq_res);
>> +     if (IS_ERR(pdata->data_base)) {
>> +             dev_err(&pdev->dev, "%s: ERROR: failed to map data base\n",
>> +                     __func__);
>> +             return PTR_ERR(pdata->data_base);
>> +     }
>> +
>> +     pdata->board_flash_info = devm_kzalloc(&pdev->dev,
>> +                                            sizeof(*pdata->board_flash_info),
>> +                                            GFP_KERNEL);
>> +
>> +     /* Fill structs for each subnode (flash device) */
>> +     while ((pp = of_get_next_child(np, pp))) {
>
> for_each_available_child_of_node()?
>
>> +             struct altera_epcq_flash_info *flash_info;
>> +
>> +             flash_info = &pdata->board_flash_info[i];
>
> This is never used. Either use it below, or drop the local variable.
>
>> +             pdata->np[i] = pp;
>> +
>> +             /* Read bank id from DT */
>> +             if (of_get_property(pp, "reg", &id))
>
> Is this property optional? Your DT binding doc doesn't make it clear,
> but it seems like a property which would be wise to require (i.e., not
> optional).
>
>> +                     pdata->board_flash_info[i].bank = id;
>> +             i++;
>> +     }
>> +     pdata->num_flashes = i;
>> +     return 0;
>> +}
>> +
>> +static int altera_epcq_setup_banks(struct platform_device *pdev,
>> +                                u32 bank, struct device_node *np,
>> +                                struct altera_epcq_plat_data *pdata)
>> +{
>> +     struct altera_epcq *dev = platform_get_drvdata(pdev);
>> +     struct mtd_part_parser_data ppdata = {};
>> +     struct altera_epcq_flash_info *flash_info;
>> +     struct altera_epcq_flash *flash;
>> +     struct mtd_partition *parts = NULL;
>
> Drop this variable. Just use NULL directly below.
>
>> +     int count = 0;
>
> Same. Just use 0 below.
>
>> +     int flash_index;
>> +     int ret = 0;
>> +     uint64_t size;
>> +     uint32_t sector_size;
>> +
>> +     flash_info = &pdata->board_flash_info[bank];
>> +     if (!flash_info)
>> +             return -ENODEV;
>> +
>> +     if (bank > pdata->num_flashes - 1)
>> +             return -EINVAL;
>> +
>> +     flash = devm_kzalloc(&pdev->dev, sizeof(*flash), GFP_KERNEL);
>> +     if (!flash)
>> +             return -ENOMEM;
>> +     flash->bank = bank;
>> +
>> +     mutex_init(&flash->lock);
>> +
>> +     /* verify whether flash is really present on board */
>> +     flash_index = altera_epcq_probe_flash(dev, bank);
>> +     if (flash_index < 0) {
>> +             dev_info(&dev->pdev->dev, "epcq:flash%d not found\n",
>> +                      flash->bank);
>> +             return flash_index;
>> +     }
>> +
>> +     dev->flash[bank] = flash;
>> +
>> +     size = flash_devices[flash_index].size_inbytes;
>> +     sector_size = flash_devices[flash_index].sectorsize_inbytes;
>> +     /*use do_div instead of plain div to avoid linker err*/
>> +     do_div(size, sector_size);
>> +     flash->num_sectors = size;
>> +
>> +     /*mtd framework */
>> +     flash->mtd.priv = dev;
>> +     flash->mtd.name = flash_devices[flash_index].name;
>> +     flash->mtd.type = MTD_NORFLASH;
>> +     flash->mtd.writesize = 1;
>> +     flash->mtd.flags = MTD_CAP_NORFLASH;
>> +     flash->mtd.size = flash_devices[flash_index].size_inbytes;
>> +     flash->mtd.erasesize = flash_devices[flash_index].sectorsize_inbytes;
>> +     flash->mtd._erase = altera_epcq_erase;
>> +     flash->mtd._read = altera_epcq_read;
>> +     flash->mtd._write = altera_epcq_write;
>> +     flash->mtd._lock = altera_epcq_lock;
>> +     flash->mtd._unlock = altera_epcq_unlock;
>> +
>> +     dev_info(&pdev->dev, "mtd .name=%s .size=0x%llx (%lluM)\n",
>> +              flash->mtd.name, (long long)flash->mtd.size,
>> +              (long long)(flash->mtd.size >> 20));
>> +
>> +     dev_info(&pdev->dev, ".erasesize = 0x%x(%uK)\n",
>> +              flash->mtd.erasesize, flash->mtd.erasesize >> 10);
>> +
>> +     ppdata.of_node = np;
>> +
>> +     ret = mtd_device_parse_register(&flash->mtd, NULL, &ppdata, parts,
>> +                                     count);
>> +     if (ret) {
>> +             dev_err(&pdev->dev, "Err MTD partition=%d\n", ret);
>> +             goto err;
>
> You don't want to unregister a MTD that failed to register. The MTD core
> code should handle that itself.
>
> Instead of this if (ret) {} block, I'd just make this:
>
>         return mtd_device_parse_register(&flash->mtd, NULL, &ppdata, NULL, 0);
>
>> +     }
>> +
>> +     return 0;
>> +
>> +err:
>> +     mtd_device_unregister(&flash->mtd);
>> +     return ret;
>> +}
>> +
>> +static int altera_epcq_probe(struct platform_device *pdev)
>> +{
>> +     struct device_node *np = pdev->dev.of_node;
>> +     struct altera_epcq_plat_data *pdata = NULL;
>> +     struct altera_epcq *dev;
>> +     int ret = 0;
>> +     int i;
>> +
>> +     if (!np) {
>> +             ret = -ENODEV;
>> +             dev_err(&pdev->dev, "no device found\n");
>> +             goto err;
>> +     }
>> +
>> +     pdata = devm_kzalloc(&pdev->dev,
>> +                          sizeof(struct altera_epcq_plat_data),
>> +                          GFP_KERNEL);
>> +
>> +     if (!pdata) {
>> +             ret = -ENOMEM;
>> +             dev_err(&pdev->dev, "%s: ERROR: no memory\n", __func__);
>
> Unnecessary print. The MM code will print plenty of info if you're
> out of memory.
>
>> +             goto err;
>> +     }
>> +     ret = altera_epcq_probe_config_dt(pdev, np, pdata);
>> +     if (ret) {
>> +             ret = -ENODEV;
>> +             dev_err(&pdev->dev, "probe fail\n");
>> +             goto err;
>> +     }
>> +
>> +     dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
>> +     if (!dev) {
>> +             ret = -ENOMEM;
>> +             dev_err(&pdev->dev, "mem alloc fail\n");
>
> Same here.
>
>> +             goto err;
>> +     }
>> +     mutex_init(&dev->lock);
>> +     dev->pdev = pdev;
>> +     dev->is_epcs = pdata->is_epcs;
>> +     dev->csr_base = pdata->csr_base;
>> +     dev->data_base = pdata->data_base;
>> +
>> +     /*check number of flashes*/
>> +     dev->num_flashes = pdata->num_flashes;
>> +     if (dev->num_flashes > MAX_NUM_FLASH_CHIP) {
>> +             dev_err(&pdev->dev, "exceeding max number of flashes\n");
>> +             dev->num_flashes = MAX_NUM_FLASH_CHIP;
>> +     }
>> +
>> +     /* check clock*/
>> +     dev->clk = devm_clk_get(&pdev->dev, NULL);
>> +     if (IS_ERR(dev->clk)) {
>> +             ret = PTR_ERR(dev->clk);
>> +             goto err;
>> +     }
>> +     ret = clk_prepare_enable(dev->clk);
>> +     if (ret)
>> +             goto err;
>> +
>> +     platform_set_drvdata(pdev, dev);
>> +
>> +     /* loop for each serial flash which is connected to epcq */
>> +     for (i = 0; i < dev->num_flashes; i++) {
>> +             ret = altera_epcq_setup_banks(pdev, i, pdata->np[i], pdata);
>> +             if (ret) {
>> +                     dev_err(&pdev->dev, "bank setup failed\n");
>> +                     goto err_bank_setup;
>> +             }
>> +     }
>> +
>> +     return 0;
>> +
>> +err_bank_setup:
>> +     clk_disable_unprepare(dev->clk);
>> +err:
>> +     return ret;
>> +}
>> +
>> +static int altera_epcq_remove(struct platform_device *pdev)
>> +{
>> +     struct altera_epcq *dev;
>> +     struct altera_epcq_flash *flash;
>> +     int ret, i;
>> +
>> +     dev = platform_get_drvdata(pdev);
>> +
>> +     /* clean up for all nor flash */
>> +     for (i = 0; i < dev->num_flashes; i++) {
>> +             flash = dev->flash[i];
>> +             if (!flash)
>> +                     continue;
>> +
>> +             /* clean up mtd stuff */
>> +             ret = mtd_device_unregister(&flash->mtd);
>> +             if (ret)
>> +                     dev_err(&pdev->dev, "error removing mtd\n");
>> +     }
>> +
>> +     clk_disable_unprepare(dev->clk);
>> +
>> +     return 0;
>> +}
>> +
>> +#ifdef CONFIG_PM
>
> Make this CONFIG_PM_SLEEP.
>
>> +static int altera_epcq_suspend(struct device *dev)
>> +{
>> +     struct altera_epcq *sdev = dev_get_drvdata(dev);
>> +
>> +     clk_disable_unprepare(sdev->clk);
>> +
>> +     return 0;
>> +}
>> +
>> +static int altera_epcq_resume(struct device *dev)
>> +{
>> +     struct altera_epcq *sdev = dev_get_drvdata(dev);
>> +     int ret = -EPERM;
>
> Drop 'ret'.
>
>> +
>> +     ret = clk_prepare_enable(sdev->clk);
>
> Just:
>
>         return clk_prepare_enable(sdev->clk);
>
>> +
>> +     return ret;
>> +}
>> +
>> +static SIMPLE_DEV_PM_OPS(altera_epcq_pm_ops, altera_epcq_suspend,
>> +                      altera_epcq_resume);
>> +#endif
>> +
>> +static const struct of_device_id altera_epcq_id_table[] = {
>> +     { .compatible = "altr,epcq-1.0" },
>> +     {}
>> +};
>> +MODULE_DEVICE_TABLE(of, altera_epcq_id_table);
>> +
>> +static struct platform_driver altera_epcq_driver = {
>> +     .driver = {
>> +             .name = ALTERA_EPCQ_RESOURCE_NAME,
>> +             .bus = &platform_bus_type,
>> +             .owner = THIS_MODULE,
>
> The .owner assignment is no longer necessary.
>
>> +             .of_match_table = altera_epcq_id_table,
>> +#ifdef CONFIG_PM
>
> Also CONFIG_PM_SLEEP.
>
>> +             .pm = &altera_epcq_pm_ops,
>> +#endif
>> +     },
>> +     .probe = altera_epcq_probe,
>> +     .remove = altera_epcq_remove,
>> +};
>> +module_platform_driver(altera_epcq_driver);
>> +
>> +MODULE_AUTHOR("Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org>");
>> +MODULE_DESCRIPTION("MTD Altera EPCQ Driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/mtd/devices/altera_epcq.h b/drivers/mtd/devices/altera_epcq.h
>> new file mode 100644
>> index 0000000..c3d15e5
>> --- /dev/null
>> +++ b/drivers/mtd/devices/altera_epcq.h
>> @@ -0,0 +1,130 @@
>> +/*
>> + * Copyright (C) 2014 Altera Corporation. All rights reserved
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms and conditions of the GNU General Public License,
>> + * version 2, as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope 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.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#ifndef __ALTERA_ECPQ_H
>> +#define __ALTERA_ECPQ_H
>> +
>> +#define ALTERA_EPCQ_RESOURCE_NAME        "altera_epcq"
>> +/* max possible slots for serial flash chip in the EPCQ controller */
>> +#define MAX_NUM_FLASH_CHIP   3
>> +
>> +/* macro to define partitions for flash devices */
>> +#define DEFINE_PARTS(n, of, s)               \
>
> This macro is not used anywhere. Drop it?
>
>> +{                                    \
>> +     .name = n,                      \
>> +     .offset = of,                   \
>> +     .size = s,                      \
>> +}
>> +
>> +struct altera_epcq_flash_info {
>> +     u32 bank;
>> +     struct mtd_partition *partitions;
>> +     int nr_partitions;
>> +};
>> +
>> +struct altera_epcq_plat_data {
>> +     void __iomem *csr_base;
>> +     void __iomem *data_base;
>> +     bool is_epcs;
>> +     u32 num_flashes;
>> +     struct altera_epcq_flash_info *board_flash_info;
>> +     struct device_node *np[MAX_NUM_FLASH_CHIP];
>> +};
>> +
>> +struct altera_epcq {
>> +     struct clk *clk;
>> +     bool is_epcs;
>> +     struct mutex lock;      /*device lock*/
>> +     void __iomem *csr_base;
>> +     void __iomem *data_base;
>> +     struct platform_device *pdev;
>> +     u32 num_flashes;
>> +     struct altera_epcq_flash *flash[MAX_NUM_FLASH_CHIP];
>> +};
>> +
>> +struct altera_epcq_flash {
>> +     u32 bank;
>> +     u32 num_sectors;
>> +     u32 num_parts;
>> +     struct mtd_partition *parts;
>> +     struct mutex lock;      /*flash lock*/
>> +     struct mtd_info mtd;
>> +};
>> +
>> +/* Define max times to check status register before we give up. */
>> +#define EPCQ_MAX_TIME_OUT                    (40 * HZ)
>> +
>> +/* defines for status register */
>> +#define EPCQ_SR_REG                          0x0
>> +#define EPCQ_SR_WIP_MASK                     0x00000001
>> +#define EPCQ_SR_WIP                          0x1
>> +#define EPCQ_SR_WEL                          0x2
>> +#define EPCQ_SR_BP0                          0x4
>> +#define EPCQ_SR_BP1                          0x8
>> +#define EPCQ_SR_BP2                          0x10
>> +#define EPCQ_SR_BP3                          0x40
>> +#define EPCQ_SR_TB                           0x20
>> +
>> +/* defines for device id register */
>> +#define EPCQ_SID_REG                         0x4
>> +#define EPCQ_RDID_REG                                0x8
>> +#define EPCQ_RDID_MASK                               0x000000FF
>> +/*
>> + * EPCQ_MEM_OP register offset
>> + *
>> + * The EPCQ_MEM_OP register is used to do memory protect and erase operations
>> + *
>> + */
>> +#define EPCQ_MEM_OP_REG                              0xC
>> +
>> +#define EPCQ_MEM_OP_CMD_MASK                 0x00000003
>> +#define EPCQ_MEM_OP_BULK_ERASE_CMD           0x00000001
>> +#define EPCQ_MEM_OP_SECTOR_ERASE_CMD         0x00000002
>> +#define EPCQ_MEM_OP_SECTOR_PROTECT_CMD               0x00000003
>> +#define EPCQ_MEM_OP_SECTOR_VALUE_MASK                0x0003FF00
>> +#define EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK        0x00001F00
>> +#define EPCQ_MEM_OP_SECTOR_PROTECT_SHIFT     8
>> +/*
>> + * EPCQ_ISR register offset
>> + *
>> + * The EPCQ_ISR register is used to determine whether an invalid write or erase
>> + * operation trigerred an interrupt
>> + *
>> + */
>> +#define EPCQ_ISR_REG                         0x10
>> +
>> +#define EPCQ_ISR_ILLEGAL_ERASE_MASK          0x00000001
>> +#define EPCQ_ISR_ILLEGAL_WRITE_MASK          0x00000002
>> +
>> +/*
>> + * EPCQ_IMR register offset
>> + *
>> + * The EPCQ_IMR register is used to mask the invalid erase or the invalid write
>> + * interrupts.
>> + *
>> + */
>> +#define EPCQ_IMR_REG                         0x14
>> +#define EPCQ_IMR_ILLEGAL_ERASE_MASK          0x00000001
>> +
>> +#define EPCQ_IMR_ILLEGAL_WRITE_MASK          0x00000002
>> +
>> +#define EPCQ_CHIP_SELECT_REG                 0x18
>> +#define EPCQ_CHIP_SELECT_MASK                        0x00000007
>> +#define EPCQ_CHIP_SELECT_0                   0x00000001
>> +#define EPCQ_CHIP_SELECT_1                   0x00000002
>> +#define EPCQ_CHIP_SELECT_2                   0x00000004
>> +
>> +#endif /* __ALTERA_ECPQ_H */
>
> Brian
--
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	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-01-15  9:27 ` Viet Nga Dao
  0 siblings, 0 replies; 39+ messages in thread
From: Viet Nga Dao @ 2015-01-15  9:27 UTC (permalink / raw)
  To: Brian Norris; +Cc: vndao, devicetree, linux-kernel, linux-mtd, dwmw2, nga_chi86

On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
<computersforpeace@gmail.com> wrote:
> On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao@altera.com wrote:
>> From: Viet Nga Dao <vndao@altera.com>
>>
>> Altera EPCQ Controller is a soft IP which enables access to Altera EPCQ and
>> EPCS flash chips. This patch adds driver for these devices.
>>
>> Signed-off-by: Viet Nga Dao <vndao@altera.com>
>
> This drivers seems awfully similar to (and so I infer it is likely
> copy-and-pasted from) m25p80.c / spi-nor.c. Do you think it can be
> rewritten as a SPI NOR driver, under drivers/mtd/spi-nor/ ? It looks
> like these flash share most (all?) the same basic opcodes.
>
For Altera EPCQ flashes, almost operations are performed underline
hardware. Software only able to perform the following through
registers:
 -  read status register
 -  read id
 -  write status registers bit SR_BP0,SR_BP1, SR_BP2,SR_BP3, SR_TB
(http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf)
For read/write data: all the operations like QUAD_READ/WRITE,
FAST_READ/WRITE are handled by hardware as well. From software point
of view, there is no difference between these 2 modes.
That is why if rewrite the drivers to follow spi-nor structure, it
will require extra decoding works for the only few used opcodes.

>> ---
>>  .../devicetree/bindings/mtd/altera_epcq.txt        |   45 ++
>>  drivers/mtd/devices/Kconfig                        |   12 +
>>  drivers/mtd/devices/Makefile                       |    2 +-
>>  drivers/mtd/devices/altera_epcq.c                  |  804 ++++++++++++++++++++
>>  drivers/mtd/devices/altera_epcq.h                  |  130 ++++
>>  5 files changed, 992 insertions(+), 1 deletions(-)
>>  create mode 100644 Documentation/devicetree/bindings/mtd/altera_epcq.txt
>>  create mode 100644 drivers/mtd/devices/altera_epcq.c
>>  create mode 100644 drivers/mtd/devices/altera_epcq.h
>>
>> diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>> new file mode 100644
>> index 0000000..d14f50e
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>> @@ -0,0 +1,45 @@
>> +* MTD Altera EPCQ driver
>> +
>> +Required properties:
>> +- compatible: Should be "altr,epcq-1.0"
>> +- reg: Address and length of the register set  for the device. It contains
>> +  the information of registers in the same order as described by reg-names
>> +- reg-names: Should contain the reg names
>> +  "csr_base": Should contain the register configuration base address
>> +  "data_base": Should contain the data base address
>> +- is-epcs: boolean type.
>> +             If present, the device contains EPCS flashes.
>> +             Otherwise, it contains EPCQ flashes.
>> +- #address-cells: Must be <1>.
>> +- #size-cells: Must be <0>.
>> +- flash device tree subnode, there must be a node with the following fields:
>
> These subnodes definitely require a 'compatible' string. Perhaps they
> should be used to differentiate EPCS vs. EPCQ. Does "is-epcs" really
> need to be in the top-level controller node?
>
>> +     - reg: Should contain the flash id
>
> Should, or must? (This question is relevant, because you seem to make it
> optional in your code.) And what does the "flash ID" mean? It seems like
> you're using as a chip-select or bank index.
>
>> +     - #address-cells: please refer to /mtd/partition.txt
>> +     - #size-cells: please refer to /mtd/partition.txt
>> +     For partitions inside each flash, please refer to /mtd/partition.txt
>> +
>> +Example:
>> +
>> +                     epcq_controller_0: epcq@0x000000000 {
>> +                             compatible = "altr,epcq-1.0";
>> +                             reg = <0x00000001 0x00000000 0x00000020>,
>> +                                     <0x00000000 0x00000000 0x02000000>;
>> +                             reg-names = "csr_base", "data_base";
>> +                             #address-cells = <1>;
>> +                             #size-cells = <0>;
>> +                             flash0: epcq256@0 {
>> +                                     reg = <0>;
>> +                                     #address-cells = <1>;
>> +                                     #size-cells = <1>;
>> +                                     partition@0 {
>> +                                             /* 16 MB for raw data. */
>> +                                             label = "EPCQ Flash 0 raw data";
>> +                                             reg = <0x0 0x1000000>;
>> +                                     };
>> +                                     partition@1000000 {
>> +                                             /* 16 MB for jffs2 data. */
>> +                                             label = "EPCQ Flash 0 JFFS 2";
>> +                                             reg = <0x1000000 0x1000000>;
>> +                                     };
>> +                             };
>> +                     }; //end epcq@0x000000000 (epcq_controller_0)
>> diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
>> index c49d0b1..020b864 100644
>> --- a/drivers/mtd/devices/Kconfig
>> +++ b/drivers/mtd/devices/Kconfig
>> @@ -218,6 +218,18 @@ config MTD_ST_SPI_FSM
>>         SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
>>         for a subset of connected Serial Flash devices.
>>
>> +config MTD_ALTERA_EPCQ
>> +     tristate "Support Altera EPCQ/EPCS Flash chips"
>> +     depends on OF
>> +     help
>> +       This enables access to Altera EPCQ/EPCS flash chips, used for data
>> +       storage. See the driver source for the current list,
>> +       or to add other chips.
>> +
>> +       If you want to compile this driver as a module ( = code which can be
>> +       inserted in and removed from the running kernel whenever you want),
>> +       say M here and read <file:Documentation/kbuild/modules.txt>.
>> +
>>  if MTD_DOCG3
>>  config BCH_CONST_M
>>       default 14
>> diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
>> index f0b0e61..b429c4d 100644
>> --- a/drivers/mtd/devices/Makefile
>> +++ b/drivers/mtd/devices/Makefile
>> @@ -16,6 +16,6 @@ obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
>>  obj-$(CONFIG_MTD_SST25L)     += sst25l.o
>>  obj-$(CONFIG_MTD_BCM47XXSFLASH)      += bcm47xxsflash.o
>>  obj-$(CONFIG_MTD_ST_SPI_FSM)    += st_spi_fsm.o
>> -
>> +obj-$(CONFIG_MTD_ALTERA_EPCQ)                += altera_epcq.o
>>
>>  CFLAGS_docg3.o                       += -I$(src)
>> diff --git a/drivers/mtd/devices/altera_epcq.c b/drivers/mtd/devices/altera_epcq.c
>> new file mode 100644
>> index 0000000..09213d5
>> --- /dev/null
>> +++ b/drivers/mtd/devices/altera_epcq.c
>> @@ -0,0 +1,804 @@
>> +/*
>> + * Copyright (C) 2014 Altera Corporation. All rights reserved
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms and conditions of the GNU General Public License,
>> + * version 2, as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope 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.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/device.h>
>> +#include <linux/err.h>
>> +#include <linux/errno.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/ioport.h>
>> +#include <linux/jiffies.h>
>> +#include <linux/kernel.h>
>> +#include <linux/log2.h>
>> +#include <linux/module.h>
>> +#include <linux/mtd/mtd.h>
>> +#include <linux/mtd/partitions.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/param.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pm.h>
>> +#include <linux/sched.h>
>> +#include <linux/slab.h>
>> +#include <linux/wait.h>
>> +
>> +#include "altera_epcq.h"
>> +
>> +/* data structure to maintain flash ids from different vendors */
>> +struct flash_device {
>> +     char *name;
>> +     bool is_epcs;
>> +     u32 device_id;
>> +     uint32_t sectorsize_inbytes;
>> +     uint64_t size_inbytes;
>> +     u32 pagesize;
>> +};
>> +
>> +#define FLASH_ID(_n, _is_epcs, _id, _ssize, _size, _psize)   \
>> +{                            \
>> +     .name = (_n),           \
>> +     .is_epcs = (_is_epcs),          \
>> +     .device_id = (_id),     \
>> +     .sectorsize_inbytes = (_ssize), \
>> +     .size_inbytes = (_size),        \
>> +     .pagesize = (_psize),   \
>> +}
>> +
>> +static struct flash_device flash_devices[] = {
>> +     FLASH_ID("epcq16"    , 0, 0x15, 0x10000, 0x200000, 0x100),
>> +     FLASH_ID("epcq32"    , 0, 0x16, 0x10000, 0x400000, 0x100),
>> +     FLASH_ID("epcq64"    , 0, 0x17, 0x10000, 0x800000, 0x100),
>> +     FLASH_ID("epcq128"   , 0, 0x18, 0x10000, 0x1000000, 0x100),
>> +     FLASH_ID("epcq256"   , 0, 0x19, 0x10000, 0x2000000, 0x100),
>> +     FLASH_ID("epcq512"   , 0, 0x20, 0x10000, 0x4000000, 0x100),
>> +
>> +     FLASH_ID("epcs16"    , 1, 0x14, 0x10000, 0x200000, 0x100),
>> +     FLASH_ID("epcs64"    , 1, 0x16, 0x10000, 0x800000, 0x100),
>> +     FLASH_ID("epcs128"   , 1, 0x18, 0x40000, 0x1000000, 0x100),
>> +};
>> +
>> +static inline struct altera_epcq_flash *get_flash_data(struct mtd_info *mtd)
>> +{
>> +     return container_of(mtd, struct altera_epcq_flash, mtd);
>> +}
>> +
>> +static u32 altera_epcq_read_sr(struct altera_epcq *dev)
>> +{
>> +     return readl(dev->csr_base + EPCQ_SR_REG);
>> +}
>> +
>> +static int altera_epcq_wait_till_ready(struct altera_epcq *dev)
>> +{
>> +     unsigned long finish;
>> +     int status;
>> +
>> +     finish = jiffies + EPCQ_MAX_TIME_OUT;
>> +     do {
>> +             status = altera_epcq_read_sr(dev);
>> +             if (status < 0)
>> +                     return status;
>> +             else if (!(status & EPCQ_SR_WIP))
>> +                     return 0;
>> +
>> +             cond_resched();
>> +     } while (!time_after_eq(jiffies, finish));
>> +
>> +     dev_err(&dev->pdev->dev, "epcq controller is busy, timeout\n");
>> +     return -EBUSY;
>> +}
>> +
>> +static int get_flash_index(u32 flash_id, bool is_epcs)
>> +{
>> +     int index;
>> +
>> +     for (index = 0; index < ARRAY_SIZE(flash_devices); index++) {
>> +             if (flash_devices[index].device_id == flash_id &&
>> +                 flash_devices[index].is_epcs == is_epcs)
>> +                     return index;
>> +     }
>> +
>> +     /* Memory chip is not listed and not supported */
>> +     return -ENODEV;
>> +}
>> +
>> +static int altera_epcq_write_erase_check(struct altera_epcq *dev,
>> +                                      bool write_erase)
>> +{
>> +     u32 val;
>> +     u32 mask;
>> +
>> +     if (write_erase)
>> +             mask = EPCQ_ISR_ILLEGAL_WRITE_MASK;
>> +     else
>> +             mask = EPCQ_ISR_ILLEGAL_ERASE_MASK;
>> +
>> +     val = readl(dev->csr_base + EPCQ_ISR_REG);
>> +     if (val & mask) {
>> +             dev_err(&dev->pdev->dev,
>> +                     "write/erase failed, sector might be protected\n");
>> +             /*clear this status for next use*/
>> +             writel(val, dev->csr_base + EPCQ_ISR_REG);
>> +             return -EIO;
>> +     }
>> +     return 0;
>> +}
>> +
>> +static int altera_epcq_erase_chip(struct mtd_info *mtd)
>> +{
>> +     int ret;
>> +     u32 val;
>> +     struct altera_epcq *dev = mtd->priv;
>> +
>> +     /* Wait until finished previous write command. */
>> +     ret = altera_epcq_wait_till_ready(dev);
>
> This pattern is pretty silly. We don't need to have every operation
> check the status of the previous operation. Each operation should check
> itself. You obviously copied this one... but we recently changed that:
>
>     commit dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3
>     Author: Brian Norris <computersforpeace@gmail.com>
>     Date:   Wed Aug 6 18:16:57 2014 -0700
>
>         mtd: spi-nor: move "wait-till-ready" checks into erase/write
>         functions
>
>
>     https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3
>
>> +     if (ret)
>> +             return ret;
>> +
>> +     /* erase chip. */
>> +     val = EPCQ_MEM_OP_BULK_ERASE_CMD;
>> +     writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
>> +
>> +     /* Wait until finished previous write command. */
>> +     ret = altera_epcq_wait_till_ready(dev);
>> +     if (ret)
>> +             return ret;
>> +
>> +      /* check whether write triggered a illegal write interrupt */
>> +     ret = altera_epcq_write_erase_check(dev, 0);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     return 0;
>> +}
>> +
>> +static int altera_epcq_addr_to_sector(struct altera_epcq_flash *flash,
>> +                                   int addr_offset)
>> +{
>> +     int sector = 0;
>> +     int i;
>> +
>> +     for (i = 0; i < flash->num_sectors; i++) {
>> +             if (addr_offset >= i * flash->mtd.erasesize && addr_offset <
>> +                 (i * flash->mtd.erasesize + flash->mtd.erasesize)) {
>> +                     sector = i;
>> +                     return sector;
>> +             }
>> +     }
>
> Uh, really? Am I crazy, or shouldn't this whole function just be a
> really simple arithmetic operation? Like:
>
>         return addr_offset >> mtd->erasesize_shift;
>
>> +     return -1;
>> +}
>> +
>> +static int altera_epcq_erase_sector(struct mtd_info *mtd,
>> +                                 int addr_offset)
>> +{
>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>> +     struct altera_epcq *dev = mtd->priv;
>> +     u32 val;
>> +     int ret;
>> +     int sector_value;
>> +
>> +     ret = altera_epcq_wait_till_ready(dev);
>> +     if (ret)
>> +             return ret;
>> +
>> +     sector_value = altera_epcq_addr_to_sector(flash, addr_offset);
>> +
>> +     /* sanity check that block_offset is a valid sector number */
>> +     if (sector_value < 0)
>> +             return -EINVAL;
>> +
>> +     /* sector value should occupy bits 17:8 */
>> +     val = (sector_value << 8) & EPCQ_MEM_OP_SECTOR_VALUE_MASK;
>> +
>> +     /* sector erase commands occupies lower 2 bits */
>> +     val |= EPCQ_MEM_OP_SECTOR_ERASE_CMD;
>> +
>> +     /* write sector erase command to EPCQ_MEM_OP register*/
>> +     writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
>> +
>> +     ret = altera_epcq_wait_till_ready(dev);
>> +     if (ret)
>> +             return ret;
>> +
>> +      /* check whether write triggered a illegal write interrupt */
>
> ^^ extra space here? You have <TAB><SPACE>/* check [...]
>
>> +     ret = altera_epcq_write_erase_check(dev, 0);
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     return 0;
>> +}
>> +
>> +static int altera_epcq_erase(struct mtd_info *mtd, struct erase_info *e_info)
>> +{
>> +     u32 addr;
>> +     int ret;
>> +     u32 len;
>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>> +     struct altera_epcq *dev = mtd->priv;
>> +
>> +     addr = e_info->addr;
>> +     len = e_info->len;
>> +
>> +     if (flash->bank > dev->num_flashes - 1) {
>> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
>> +             return -EINVAL;
>> +     }
>> +     mutex_lock(&flash->lock);
>> +
>> +     /*erase whole chip*/
>> +     if (len == mtd->size) {
>> +             if (altera_epcq_erase_chip(mtd)) {
>> +                     e_info->state = MTD_ERASE_FAILED;
>> +                     mutex_unlock(&flash->lock);
>> +                     return -EIO;
>> +             }
>> +     /*"sector"-at-a-time erase*/
>> +     } else {
>> +             while (len) {
>> +                     ret = altera_epcq_erase_sector(mtd, addr);
>> +                     if (ret) {
>> +                             e_info->state = MTD_ERASE_FAILED;
>> +                             mutex_unlock(&flash->lock);
>> +                             return -EIO;
>> +                     }
>> +                     addr += mtd->erasesize;
>> +                     if (len < mtd->erasesize)
>> +                             len = 0;
>> +                     else
>> +                             len -= mtd->erasesize;
>> +             }
>> +     }
>> +     mutex_unlock(&flash->lock);
>> +     e_info->state = MTD_ERASE_DONE;
>> +     mtd_erase_callback(e_info);
>> +
>> +     return 0;
>> +}
>> +
>> +static int altera_epcq_read(struct mtd_info *mtd, loff_t from, size_t len,
>> +                         size_t *retlen, u8 *buf)
>> +{
>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>> +     struct altera_epcq *dev = mtd->priv;
>> +     void *src;
>> +     int ret = 0;
>> +
>> +     if (!flash || !dev)
>> +             return -ENODEV;
>
> Why would either of these be 0? If they are, you have much bigger
> problems, since you aren't checking these almost anywhere else. And the
> !flash check is pretty odd, since get_flash_data() is using
> container_of(), which is not likely to give you exactly 0...
>
>> +
>> +     if (flash->bank > dev->num_flashes - 1) {
>> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     src = dev->data_base + from;
>> +
>> +     mutex_lock(&flash->lock);
>> +     /* wait till previous write/erase is done. */
>> +     ret = altera_epcq_wait_till_ready(dev);
>> +     if (ret)
>> +             goto err;
>> +
>> +     memcpy_fromio(buf, (u8 *)src, len);
>
> Drop the cast. Also, you get sparse warnings because you're dropping the
> __iomem. Why not just this?
>
>         memcpy_fromio(buf, dev->data_base + from, len);
>
> (BTW, you might consider running sparse on all your code. See
> Documentation/sparse.txt. It tells you how to get it, but you can often
> just download it through your distro's package manager.)
>
>> +     *retlen = len;
>> +
>> +err:
>> +     mutex_unlock(&flash->lock);
>> +     return ret;
>> +}
>> +
>> +static int altera_epcq_write(struct mtd_info *mtd, loff_t to, size_t len,
>> +                          size_t *retlen, const u8 *buf)
>> +{
>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>> +     struct altera_epcq *dev = mtd->priv;
>> +     void *dest;
>> +     int ret = 0;
>> +
>> +     if (!flash || !dev)
>> +             return -ENODEV;
>
> Also here. I don't think you need these checks.
>
>> +
>> +     if (flash->bank > dev->num_flashes - 1) {
>> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
>> +             return -EINVAL;
>> +     }
>> +     dest = dev->data_base + to;
>> +
>> +     mutex_lock(&flash->lock);
>> +
>> +     /* wait until finished previous write command. */
>> +     ret = altera_epcq_wait_till_ready(dev);
>> +     if (ret)
>> +             goto err;
>> +
>> +     memcpy_toio(dest, buf, len);
>
> Similar. Why not just this?
>
>         memcpy_toio(dev->data_base + to, buf, len);
>
>> +     *retlen += len;
>> +
>> +     /* wait until finished previous write command. */
>> +     ret = altera_epcq_wait_till_ready(dev);
>> +     if (ret)
>> +             goto err;
>> +
>> +      /* check whether write triggered a illegal write interrupt */
>> +     ret = altera_epcq_write_erase_check(dev, 1);
>> +     if (ret < 0)
>> +             goto err;
>> +
>> +err:
>> +     mutex_unlock(&flash->lock);
>> +     return ret;
>> +}
>> +
>> +static int altera_epcq_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
>> +{
>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>> +     struct altera_epcq *dev = mtd->priv;
>> +     uint32_t offset = ofs;
>> +     int ret = 0;
>> +     u32 sector_start, sector_end;
>> +     u32 num_sectors;
>> +     u32 mem_op;
>> +     unsigned sr_bp = 0;
>> +     unsigned sr_tb = 0;
>> +
>> +     sector_start = altera_epcq_addr_to_sector(flash, offset);
>> +     sector_end = altera_epcq_addr_to_sector(flash, offset + len);
>> +     num_sectors = flash->num_sectors;
>> +     dev_dbg(&dev->pdev->dev,
>> +             "%s: num_setor is %u,sector start is %u,sector end is %u\n",
>> +             __func__, num_sectors, sector_start, sector_end);
>> +
>> +     mutex_lock(&flash->lock);
>> +     /* wait until finished previous write command. */
>> +     ret = altera_epcq_wait_till_ready(dev);
>> +     if (ret)
>> +             goto err;
>> +
>> +     if (sector_start >= num_sectors/2) {
>
> It's preferable to have spaces around operators. So 'num_sectors / 2'.
> The same thing comes up in several other places.
>
>> +             if (sector_start < num_sectors-(num_sectors / 4))
>> +                     sr_bp = __ilog2_u32(num_sectors);
>> +             else if (sector_start < num_sectors-(num_sectors / 8))
>> +                     sr_bp = __ilog2_u32(num_sectors) - 1;
>> +             else if (sector_start < num_sectors-(num_sectors / 16))
>> +                     sr_bp = __ilog2_u32(num_sectors) - 2;
>> +             else if (sector_start < num_sectors-(num_sectors / 32))
>> +                     sr_bp = __ilog2_u32(num_sectors) - 3;
>> +             else if (sector_start < num_sectors-(num_sectors / 64))
>> +                     sr_bp = __ilog2_u32(num_sectors) - 4;
>> +             else if (sector_start < num_sectors-(num_sectors / 128))
>> +                     sr_bp = __ilog2_u32(num_sectors) - 5;
>> +             else if (sector_start < num_sectors-(num_sectors / 256))
>> +                     sr_bp = __ilog2_u32(num_sectors) - 6;
>> +             else if (sector_start < num_sectors-(num_sectors / 512))
>> +                     sr_bp = __ilog2_u32(num_sectors) - 7;
>> +             else if (sector_start < num_sectors-(num_sectors / 1024))
>> +                     sr_bp = __ilog2_u32(num_sectors) - 8;
>> +             else
>> +                     sr_bp = 0;  /* non area protected */
>
> Yikes, that's ugly! And I'm not sure it matches the EPCQ doc I found.
> I'm pretty sure you can rewrite this if/else-if/else block in about 1
> line though.
>
>> +
>> +             if (sr_bp < 0) {
>
> sr_bp is unsigned, so this is never true.
>
>> +                     dev_err(&dev->pdev->dev, "%s: address is out of range\n"
>> +                             , __func__);
>> +                     ret = -EINVAL;
>> +                     goto err;
>> +             }
>> +             /*set TB = 0*/
>> +             sr_tb = 0;
>> +
>> +     } else {
>> +             if (sector_end < 1)
>> +                     sr_bp = 1;
>> +             else if (sector_end < 2)
>> +                     sr_bp = 2;
>> +             else if (sector_end < 4)
>> +                     sr_bp = 3;
>> +             else if (sector_end < 8)
>> +                     sr_bp = 4;
>> +             else if (sector_end < 16)
>> +                     sr_bp = 5;
>> +             else if (sector_end < 32)
>> +                     sr_bp = 6;
>> +             else if (sector_end < 64)
>> +                     sr_bp = 7;
>> +             else if (sector_end < 128)
>> +                     sr_bp = 8;
>> +             else if (sector_end < 256)
>> +                     sr_bp = 9;
>> +             else if (sector_end < 512)
>> +                     sr_bp = 10;
>> +             else
>> +                     sr_bp = 16; /*protect all areas*/
>
> Again, this is ugly. How about this?
>
>         sr_bp = fls(sector_end) + 1;
>
>> +
>> +             sr_tb = 1;
>> +     }
>> +
>> +     mem_op = (sr_tb << 12) | (sr_bp << 8);
>> +     mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
>> +     mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
>> +     writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
>> +
>> +err:
>> +     mutex_unlock(&flash->lock);
>> +     return ret;
>> +}
>> +
>> +static int altera_epcq_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
>> +{
>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>> +     struct altera_epcq *dev = mtd->priv;
>> +
>> +     int ret = 0;
>> +     u32 mem_op;
>> +
>> +     mutex_lock(&flash->lock);
>> +     /* wait until finished previous write command. */
>> +     ret = altera_epcq_wait_till_ready(dev);
>> +     if (ret)
>> +             goto err;
>> +     dev_info(&dev->pdev->dev, "Unlock all protected area\n");
>> +     mem_op = 0;
>> +     mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
>> +     mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
>> +     writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
>> +
>> +err:
>> +     mutex_unlock(&flash->lock);
>> +     return ret;
>> +}
>> +
>> +static void altera_epcq_chip_select(struct altera_epcq *dev, u32 bank)
>> +{
>> +     u32 val = 0;
>> +
>> +     switch (bank) {
>> +     case 0:
>> +             val = EPCQ_CHIP_SELECT_0;
>> +             break;
>> +     case 1:
>> +             val = EPCQ_CHIP_SELECT_1;
>> +             break;
>> +     case 2:
>> +             val = EPCQ_CHIP_SELECT_2;
>> +             break;
>> +     default:
>> +             return;
>> +     }
>> +
>> +     writel(val, dev->csr_base + EPCQ_CHIP_SELECT_REG);
>> +}
>> +
>> +static int altera_epcq_probe_flash(struct altera_epcq *dev, u32 bank)
>> +{
>> +     int ret = 0;
>> +     u32 val = 0;
>> +
>> +     mutex_lock(&dev->lock);
>> +
>> +     /*select bank*/
>
> Comment spacing, here and elsewhere:
>
>         /* select bank */
>
>> +     altera_epcq_chip_select(dev, bank);
>> +
>> +     ret = altera_epcq_wait_till_ready(dev);
>> +     if (ret)
>> +             goto err;
>> +
>> +     /* get device sillicon id */
>> +     if (dev->is_epcs)
>> +             val = readl(dev->csr_base + EPCQ_SID_REG);
>> +     else
>> +             val = readl(dev->csr_base + EPCQ_RDID_REG);
>> +
>> +     /* get flash index based on the device list*/
>> +     ret = get_flash_index(val, dev->is_epcs);
>> +     return 0;
>> +err:
>> +     mutex_unlock(&dev->lock);
>> +     return ret;
>> +}
>> +
>> +static int altera_epcq_probe_config_dt(struct platform_device *pdev,
>> +                                    struct device_node *np,
>> +                                    struct altera_epcq_plat_data *pdata)
>> +{
>> +     struct device_node *pp = NULL;
>> +     struct resource *epcq_res;
>> +     int i = 0;
>> +     u32 id;
>> +
>> +     pdata->is_epcs = of_property_read_bool(np, "is-epcs");
>> +
>> +     epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
>> +                                             "csr_base");
>> +     if (!epcq_res) {
>
> devm_ioremap_resource() will check the that 'epcq_res' is valid, so you
> don't need this whole 'if' block.
>
>> +             dev_err(&pdev->dev, "resource csr base is not defined\n");
>> +             return -ENODEV;
>> +     }
>> +
>> +     pdata->csr_base = devm_ioremap_resource(&pdev->dev, epcq_res);
>> +     if (IS_ERR(pdata->csr_base)) {
>> +             dev_err(&pdev->dev, "%s: ERROR: failed to map csr base\n",
>> +                     __func__);
>> +             return PTR_ERR(pdata->csr_base);
>> +     }
>> +
>> +     epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
>> +                                             "data_base");
>> +     if (!epcq_res) {
>
> Same here.
>
>> +             dev_err(&pdev->dev, "resource data base is not defined\n");
>> +             return -ENODEV;
>> +     }
>> +
>> +     pdata->data_base = devm_ioremap_resource(&pdev->dev, epcq_res);
>> +     if (IS_ERR(pdata->data_base)) {
>> +             dev_err(&pdev->dev, "%s: ERROR: failed to map data base\n",
>> +                     __func__);
>> +             return PTR_ERR(pdata->data_base);
>> +     }
>> +
>> +     pdata->board_flash_info = devm_kzalloc(&pdev->dev,
>> +                                            sizeof(*pdata->board_flash_info),
>> +                                            GFP_KERNEL);
>> +
>> +     /* Fill structs for each subnode (flash device) */
>> +     while ((pp = of_get_next_child(np, pp))) {
>
> for_each_available_child_of_node()?
>
>> +             struct altera_epcq_flash_info *flash_info;
>> +
>> +             flash_info = &pdata->board_flash_info[i];
>
> This is never used. Either use it below, or drop the local variable.
>
>> +             pdata->np[i] = pp;
>> +
>> +             /* Read bank id from DT */
>> +             if (of_get_property(pp, "reg", &id))
>
> Is this property optional? Your DT binding doc doesn't make it clear,
> but it seems like a property which would be wise to require (i.e., not
> optional).
>
>> +                     pdata->board_flash_info[i].bank = id;
>> +             i++;
>> +     }
>> +     pdata->num_flashes = i;
>> +     return 0;
>> +}
>> +
>> +static int altera_epcq_setup_banks(struct platform_device *pdev,
>> +                                u32 bank, struct device_node *np,
>> +                                struct altera_epcq_plat_data *pdata)
>> +{
>> +     struct altera_epcq *dev = platform_get_drvdata(pdev);
>> +     struct mtd_part_parser_data ppdata = {};
>> +     struct altera_epcq_flash_info *flash_info;
>> +     struct altera_epcq_flash *flash;
>> +     struct mtd_partition *parts = NULL;
>
> Drop this variable. Just use NULL directly below.
>
>> +     int count = 0;
>
> Same. Just use 0 below.
>
>> +     int flash_index;
>> +     int ret = 0;
>> +     uint64_t size;
>> +     uint32_t sector_size;
>> +
>> +     flash_info = &pdata->board_flash_info[bank];
>> +     if (!flash_info)
>> +             return -ENODEV;
>> +
>> +     if (bank > pdata->num_flashes - 1)
>> +             return -EINVAL;
>> +
>> +     flash = devm_kzalloc(&pdev->dev, sizeof(*flash), GFP_KERNEL);
>> +     if (!flash)
>> +             return -ENOMEM;
>> +     flash->bank = bank;
>> +
>> +     mutex_init(&flash->lock);
>> +
>> +     /* verify whether flash is really present on board */
>> +     flash_index = altera_epcq_probe_flash(dev, bank);
>> +     if (flash_index < 0) {
>> +             dev_info(&dev->pdev->dev, "epcq:flash%d not found\n",
>> +                      flash->bank);
>> +             return flash_index;
>> +     }
>> +
>> +     dev->flash[bank] = flash;
>> +
>> +     size = flash_devices[flash_index].size_inbytes;
>> +     sector_size = flash_devices[flash_index].sectorsize_inbytes;
>> +     /*use do_div instead of plain div to avoid linker err*/
>> +     do_div(size, sector_size);
>> +     flash->num_sectors = size;
>> +
>> +     /*mtd framework */
>> +     flash->mtd.priv = dev;
>> +     flash->mtd.name = flash_devices[flash_index].name;
>> +     flash->mtd.type = MTD_NORFLASH;
>> +     flash->mtd.writesize = 1;
>> +     flash->mtd.flags = MTD_CAP_NORFLASH;
>> +     flash->mtd.size = flash_devices[flash_index].size_inbytes;
>> +     flash->mtd.erasesize = flash_devices[flash_index].sectorsize_inbytes;
>> +     flash->mtd._erase = altera_epcq_erase;
>> +     flash->mtd._read = altera_epcq_read;
>> +     flash->mtd._write = altera_epcq_write;
>> +     flash->mtd._lock = altera_epcq_lock;
>> +     flash->mtd._unlock = altera_epcq_unlock;
>> +
>> +     dev_info(&pdev->dev, "mtd .name=%s .size=0x%llx (%lluM)\n",
>> +              flash->mtd.name, (long long)flash->mtd.size,
>> +              (long long)(flash->mtd.size >> 20));
>> +
>> +     dev_info(&pdev->dev, ".erasesize = 0x%x(%uK)\n",
>> +              flash->mtd.erasesize, flash->mtd.erasesize >> 10);
>> +
>> +     ppdata.of_node = np;
>> +
>> +     ret = mtd_device_parse_register(&flash->mtd, NULL, &ppdata, parts,
>> +                                     count);
>> +     if (ret) {
>> +             dev_err(&pdev->dev, "Err MTD partition=%d\n", ret);
>> +             goto err;
>
> You don't want to unregister a MTD that failed to register. The MTD core
> code should handle that itself.
>
> Instead of this if (ret) {} block, I'd just make this:
>
>         return mtd_device_parse_register(&flash->mtd, NULL, &ppdata, NULL, 0);
>
>> +     }
>> +
>> +     return 0;
>> +
>> +err:
>> +     mtd_device_unregister(&flash->mtd);
>> +     return ret;
>> +}
>> +
>> +static int altera_epcq_probe(struct platform_device *pdev)
>> +{
>> +     struct device_node *np = pdev->dev.of_node;
>> +     struct altera_epcq_plat_data *pdata = NULL;
>> +     struct altera_epcq *dev;
>> +     int ret = 0;
>> +     int i;
>> +
>> +     if (!np) {
>> +             ret = -ENODEV;
>> +             dev_err(&pdev->dev, "no device found\n");
>> +             goto err;
>> +     }
>> +
>> +     pdata = devm_kzalloc(&pdev->dev,
>> +                          sizeof(struct altera_epcq_plat_data),
>> +                          GFP_KERNEL);
>> +
>> +     if (!pdata) {
>> +             ret = -ENOMEM;
>> +             dev_err(&pdev->dev, "%s: ERROR: no memory\n", __func__);
>
> Unnecessary print. The MM code will print plenty of info if you're
> out of memory.
>
>> +             goto err;
>> +     }
>> +     ret = altera_epcq_probe_config_dt(pdev, np, pdata);
>> +     if (ret) {
>> +             ret = -ENODEV;
>> +             dev_err(&pdev->dev, "probe fail\n");
>> +             goto err;
>> +     }
>> +
>> +     dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
>> +     if (!dev) {
>> +             ret = -ENOMEM;
>> +             dev_err(&pdev->dev, "mem alloc fail\n");
>
> Same here.
>
>> +             goto err;
>> +     }
>> +     mutex_init(&dev->lock);
>> +     dev->pdev = pdev;
>> +     dev->is_epcs = pdata->is_epcs;
>> +     dev->csr_base = pdata->csr_base;
>> +     dev->data_base = pdata->data_base;
>> +
>> +     /*check number of flashes*/
>> +     dev->num_flashes = pdata->num_flashes;
>> +     if (dev->num_flashes > MAX_NUM_FLASH_CHIP) {
>> +             dev_err(&pdev->dev, "exceeding max number of flashes\n");
>> +             dev->num_flashes = MAX_NUM_FLASH_CHIP;
>> +     }
>> +
>> +     /* check clock*/
>> +     dev->clk = devm_clk_get(&pdev->dev, NULL);
>> +     if (IS_ERR(dev->clk)) {
>> +             ret = PTR_ERR(dev->clk);
>> +             goto err;
>> +     }
>> +     ret = clk_prepare_enable(dev->clk);
>> +     if (ret)
>> +             goto err;
>> +
>> +     platform_set_drvdata(pdev, dev);
>> +
>> +     /* loop for each serial flash which is connected to epcq */
>> +     for (i = 0; i < dev->num_flashes; i++) {
>> +             ret = altera_epcq_setup_banks(pdev, i, pdata->np[i], pdata);
>> +             if (ret) {
>> +                     dev_err(&pdev->dev, "bank setup failed\n");
>> +                     goto err_bank_setup;
>> +             }
>> +     }
>> +
>> +     return 0;
>> +
>> +err_bank_setup:
>> +     clk_disable_unprepare(dev->clk);
>> +err:
>> +     return ret;
>> +}
>> +
>> +static int altera_epcq_remove(struct platform_device *pdev)
>> +{
>> +     struct altera_epcq *dev;
>> +     struct altera_epcq_flash *flash;
>> +     int ret, i;
>> +
>> +     dev = platform_get_drvdata(pdev);
>> +
>> +     /* clean up for all nor flash */
>> +     for (i = 0; i < dev->num_flashes; i++) {
>> +             flash = dev->flash[i];
>> +             if (!flash)
>> +                     continue;
>> +
>> +             /* clean up mtd stuff */
>> +             ret = mtd_device_unregister(&flash->mtd);
>> +             if (ret)
>> +                     dev_err(&pdev->dev, "error removing mtd\n");
>> +     }
>> +
>> +     clk_disable_unprepare(dev->clk);
>> +
>> +     return 0;
>> +}
>> +
>> +#ifdef CONFIG_PM
>
> Make this CONFIG_PM_SLEEP.
>
>> +static int altera_epcq_suspend(struct device *dev)
>> +{
>> +     struct altera_epcq *sdev = dev_get_drvdata(dev);
>> +
>> +     clk_disable_unprepare(sdev->clk);
>> +
>> +     return 0;
>> +}
>> +
>> +static int altera_epcq_resume(struct device *dev)
>> +{
>> +     struct altera_epcq *sdev = dev_get_drvdata(dev);
>> +     int ret = -EPERM;
>
> Drop 'ret'.
>
>> +
>> +     ret = clk_prepare_enable(sdev->clk);
>
> Just:
>
>         return clk_prepare_enable(sdev->clk);
>
>> +
>> +     return ret;
>> +}
>> +
>> +static SIMPLE_DEV_PM_OPS(altera_epcq_pm_ops, altera_epcq_suspend,
>> +                      altera_epcq_resume);
>> +#endif
>> +
>> +static const struct of_device_id altera_epcq_id_table[] = {
>> +     { .compatible = "altr,epcq-1.0" },
>> +     {}
>> +};
>> +MODULE_DEVICE_TABLE(of, altera_epcq_id_table);
>> +
>> +static struct platform_driver altera_epcq_driver = {
>> +     .driver = {
>> +             .name = ALTERA_EPCQ_RESOURCE_NAME,
>> +             .bus = &platform_bus_type,
>> +             .owner = THIS_MODULE,
>
> The .owner assignment is no longer necessary.
>
>> +             .of_match_table = altera_epcq_id_table,
>> +#ifdef CONFIG_PM
>
> Also CONFIG_PM_SLEEP.
>
>> +             .pm = &altera_epcq_pm_ops,
>> +#endif
>> +     },
>> +     .probe = altera_epcq_probe,
>> +     .remove = altera_epcq_remove,
>> +};
>> +module_platform_driver(altera_epcq_driver);
>> +
>> +MODULE_AUTHOR("Viet Nga Dao <vndao@altera.com>");
>> +MODULE_DESCRIPTION("MTD Altera EPCQ Driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/mtd/devices/altera_epcq.h b/drivers/mtd/devices/altera_epcq.h
>> new file mode 100644
>> index 0000000..c3d15e5
>> --- /dev/null
>> +++ b/drivers/mtd/devices/altera_epcq.h
>> @@ -0,0 +1,130 @@
>> +/*
>> + * Copyright (C) 2014 Altera Corporation. All rights reserved
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms and conditions of the GNU General Public License,
>> + * version 2, as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope 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.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#ifndef __ALTERA_ECPQ_H
>> +#define __ALTERA_ECPQ_H
>> +
>> +#define ALTERA_EPCQ_RESOURCE_NAME        "altera_epcq"
>> +/* max possible slots for serial flash chip in the EPCQ controller */
>> +#define MAX_NUM_FLASH_CHIP   3
>> +
>> +/* macro to define partitions for flash devices */
>> +#define DEFINE_PARTS(n, of, s)               \
>
> This macro is not used anywhere. Drop it?
>
>> +{                                    \
>> +     .name = n,                      \
>> +     .offset = of,                   \
>> +     .size = s,                      \
>> +}
>> +
>> +struct altera_epcq_flash_info {
>> +     u32 bank;
>> +     struct mtd_partition *partitions;
>> +     int nr_partitions;
>> +};
>> +
>> +struct altera_epcq_plat_data {
>> +     void __iomem *csr_base;
>> +     void __iomem *data_base;
>> +     bool is_epcs;
>> +     u32 num_flashes;
>> +     struct altera_epcq_flash_info *board_flash_info;
>> +     struct device_node *np[MAX_NUM_FLASH_CHIP];
>> +};
>> +
>> +struct altera_epcq {
>> +     struct clk *clk;
>> +     bool is_epcs;
>> +     struct mutex lock;      /*device lock*/
>> +     void __iomem *csr_base;
>> +     void __iomem *data_base;
>> +     struct platform_device *pdev;
>> +     u32 num_flashes;
>> +     struct altera_epcq_flash *flash[MAX_NUM_FLASH_CHIP];
>> +};
>> +
>> +struct altera_epcq_flash {
>> +     u32 bank;
>> +     u32 num_sectors;
>> +     u32 num_parts;
>> +     struct mtd_partition *parts;
>> +     struct mutex lock;      /*flash lock*/
>> +     struct mtd_info mtd;
>> +};
>> +
>> +/* Define max times to check status register before we give up. */
>> +#define EPCQ_MAX_TIME_OUT                    (40 * HZ)
>> +
>> +/* defines for status register */
>> +#define EPCQ_SR_REG                          0x0
>> +#define EPCQ_SR_WIP_MASK                     0x00000001
>> +#define EPCQ_SR_WIP                          0x1
>> +#define EPCQ_SR_WEL                          0x2
>> +#define EPCQ_SR_BP0                          0x4
>> +#define EPCQ_SR_BP1                          0x8
>> +#define EPCQ_SR_BP2                          0x10
>> +#define EPCQ_SR_BP3                          0x40
>> +#define EPCQ_SR_TB                           0x20
>> +
>> +/* defines for device id register */
>> +#define EPCQ_SID_REG                         0x4
>> +#define EPCQ_RDID_REG                                0x8
>> +#define EPCQ_RDID_MASK                               0x000000FF
>> +/*
>> + * EPCQ_MEM_OP register offset
>> + *
>> + * The EPCQ_MEM_OP register is used to do memory protect and erase operations
>> + *
>> + */
>> +#define EPCQ_MEM_OP_REG                              0xC
>> +
>> +#define EPCQ_MEM_OP_CMD_MASK                 0x00000003
>> +#define EPCQ_MEM_OP_BULK_ERASE_CMD           0x00000001
>> +#define EPCQ_MEM_OP_SECTOR_ERASE_CMD         0x00000002
>> +#define EPCQ_MEM_OP_SECTOR_PROTECT_CMD               0x00000003
>> +#define EPCQ_MEM_OP_SECTOR_VALUE_MASK                0x0003FF00
>> +#define EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK        0x00001F00
>> +#define EPCQ_MEM_OP_SECTOR_PROTECT_SHIFT     8
>> +/*
>> + * EPCQ_ISR register offset
>> + *
>> + * The EPCQ_ISR register is used to determine whether an invalid write or erase
>> + * operation trigerred an interrupt
>> + *
>> + */
>> +#define EPCQ_ISR_REG                         0x10
>> +
>> +#define EPCQ_ISR_ILLEGAL_ERASE_MASK          0x00000001
>> +#define EPCQ_ISR_ILLEGAL_WRITE_MASK          0x00000002
>> +
>> +/*
>> + * EPCQ_IMR register offset
>> + *
>> + * The EPCQ_IMR register is used to mask the invalid erase or the invalid write
>> + * interrupts.
>> + *
>> + */
>> +#define EPCQ_IMR_REG                         0x14
>> +#define EPCQ_IMR_ILLEGAL_ERASE_MASK          0x00000001
>> +
>> +#define EPCQ_IMR_ILLEGAL_WRITE_MASK          0x00000002
>> +
>> +#define EPCQ_CHIP_SELECT_REG                 0x18
>> +#define EPCQ_CHIP_SELECT_MASK                        0x00000007
>> +#define EPCQ_CHIP_SELECT_0                   0x00000001
>> +#define EPCQ_CHIP_SELECT_1                   0x00000002
>> +#define EPCQ_CHIP_SELECT_2                   0x00000004
>> +
>> +#endif /* __ALTERA_ECPQ_H */
>
> Brian

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-01-20  2:05   ` Viet Nga Dao
  0 siblings, 0 replies; 39+ messages in thread
From: Viet Nga Dao @ 2015-01-20  2:05 UTC (permalink / raw)
  To: Viet Nga Dao
  Cc: Brian Norris, David Woodhouse, linux-mtd, linux-kernel,
	devicetree, nga_chi86

On Thu, Jan 15, 2015 at 5:27 PM, Viet Nga Dao <vndao@altera.com> wrote:
> On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
> <computersforpeace@gmail.com> wrote:
>> On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao@altera.com wrote:
>>> From: Viet Nga Dao <vndao@altera.com>
>>>
>>> Altera EPCQ Controller is a soft IP which enables access to Altera EPCQ and
>>> EPCS flash chips. This patch adds driver for these devices.
>>>
>>> Signed-off-by: Viet Nga Dao <vndao@altera.com>
>>
>> This drivers seems awfully similar to (and so I infer it is likely
>> copy-and-pasted from) m25p80.c / spi-nor.c. Do you think it can be
>> rewritten as a SPI NOR driver, under drivers/mtd/spi-nor/ ? It looks
>> like these flash share most (all?) the same basic opcodes.
>>
> For Altera EPCQ flashes, almost operations are performed underline
> hardware. Software only able to perform the following through
> registers:
>  -  read status register
>  -  read id
>  -  write status registers bit SR_BP0,SR_BP1, SR_BP2,SR_BP3, SR_TB
> (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf)
> For read/write data: all the operations like QUAD_READ/WRITE,
> FAST_READ/WRITE are handled by hardware as well. From software point
> of view, there is no difference between these 2 modes.
> That is why if rewrite the drivers to follow spi-nor structure, it
> will require extra decoding works for the only few used opcodes.
>
Is it OK to remain this driver structure?
>>> ---
>>>  .../devicetree/bindings/mtd/altera_epcq.txt        |   45 ++
>>>  drivers/mtd/devices/Kconfig                        |   12 +
>>>  drivers/mtd/devices/Makefile                       |    2 +-
>>>  drivers/mtd/devices/altera_epcq.c                  |  804 ++++++++++++++++++++
>>>  drivers/mtd/devices/altera_epcq.h                  |  130 ++++
>>>  5 files changed, 992 insertions(+), 1 deletions(-)
>>>  create mode 100644 Documentation/devicetree/bindings/mtd/altera_epcq.txt
>>>  create mode 100644 drivers/mtd/devices/altera_epcq.c
>>>  create mode 100644 drivers/mtd/devices/altera_epcq.h
>>>
>>> diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>>> new file mode 100644
>>> index 0000000..d14f50e
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>>> @@ -0,0 +1,45 @@
>>> +* MTD Altera EPCQ driver
>>> +
>>> +Required properties:
>>> +- compatible: Should be "altr,epcq-1.0"
>>> +- reg: Address and length of the register set  for the device. It contains
>>> +  the information of registers in the same order as described by reg-names
>>> +- reg-names: Should contain the reg names
>>> +  "csr_base": Should contain the register configuration base address
>>> +  "data_base": Should contain the data base address
>>> +- is-epcs: boolean type.
>>> +             If present, the device contains EPCS flashes.
>>> +             Otherwise, it contains EPCQ flashes.
>>> +- #address-cells: Must be <1>.
>>> +- #size-cells: Must be <0>.
>>> +- flash device tree subnode, there must be a node with the following fields:
>>
>> These subnodes definitely require a 'compatible' string. Perhaps they
>> should be used to differentiate EPCS vs. EPCQ. Does "is-epcs" really
>> need to be in the top-level controller node?
>>
>>> +     - reg: Should contain the flash id
>>
>> Should, or must? (This question is relevant, because you seem to make it
>> optional in your code.) And what does the "flash ID" mean? It seems like
>> you're using as a chip-select or bank index.
>>
>>> +     - #address-cells: please refer to /mtd/partition.txt
>>> +     - #size-cells: please refer to /mtd/partition.txt
>>> +     For partitions inside each flash, please refer to /mtd/partition.txt
>>> +
>>> +Example:
>>> +
>>> +                     epcq_controller_0: epcq@0x000000000 {
>>> +                             compatible = "altr,epcq-1.0";
>>> +                             reg = <0x00000001 0x00000000 0x00000020>,
>>> +                                     <0x00000000 0x00000000 0x02000000>;
>>> +                             reg-names = "csr_base", "data_base";
>>> +                             #address-cells = <1>;
>>> +                             #size-cells = <0>;
>>> +                             flash0: epcq256@0 {
>>> +                                     reg = <0>;
>>> +                                     #address-cells = <1>;
>>> +                                     #size-cells = <1>;
>>> +                                     partition@0 {
>>> +                                             /* 16 MB for raw data. */
>>> +                                             label = "EPCQ Flash 0 raw data";
>>> +                                             reg = <0x0 0x1000000>;
>>> +                                     };
>>> +                                     partition@1000000 {
>>> +                                             /* 16 MB for jffs2 data. */
>>> +                                             label = "EPCQ Flash 0 JFFS 2";
>>> +                                             reg = <0x1000000 0x1000000>;
>>> +                                     };
>>> +                             };
>>> +                     }; //end epcq@0x000000000 (epcq_controller_0)
>>> diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
>>> index c49d0b1..020b864 100644
>>> --- a/drivers/mtd/devices/Kconfig
>>> +++ b/drivers/mtd/devices/Kconfig
>>> @@ -218,6 +218,18 @@ config MTD_ST_SPI_FSM
>>>         SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
>>>         for a subset of connected Serial Flash devices.
>>>
>>> +config MTD_ALTERA_EPCQ
>>> +     tristate "Support Altera EPCQ/EPCS Flash chips"
>>> +     depends on OF
>>> +     help
>>> +       This enables access to Altera EPCQ/EPCS flash chips, used for data
>>> +       storage. See the driver source for the current list,
>>> +       or to add other chips.
>>> +
>>> +       If you want to compile this driver as a module ( = code which can be
>>> +       inserted in and removed from the running kernel whenever you want),
>>> +       say M here and read <file:Documentation/kbuild/modules.txt>.
>>> +
>>>  if MTD_DOCG3
>>>  config BCH_CONST_M
>>>       default 14
>>> diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
>>> index f0b0e61..b429c4d 100644
>>> --- a/drivers/mtd/devices/Makefile
>>> +++ b/drivers/mtd/devices/Makefile
>>> @@ -16,6 +16,6 @@ obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
>>>  obj-$(CONFIG_MTD_SST25L)     += sst25l.o
>>>  obj-$(CONFIG_MTD_BCM47XXSFLASH)      += bcm47xxsflash.o
>>>  obj-$(CONFIG_MTD_ST_SPI_FSM)    += st_spi_fsm.o
>>> -
>>> +obj-$(CONFIG_MTD_ALTERA_EPCQ)                += altera_epcq.o
>>>
>>>  CFLAGS_docg3.o                       += -I$(src)
>>> diff --git a/drivers/mtd/devices/altera_epcq.c b/drivers/mtd/devices/altera_epcq.c
>>> new file mode 100644
>>> index 0000000..09213d5
>>> --- /dev/null
>>> +++ b/drivers/mtd/devices/altera_epcq.c
>>> @@ -0,0 +1,804 @@
>>> +/*
>>> + * Copyright (C) 2014 Altera Corporation. All rights reserved
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms and conditions of the GNU General Public License,
>>> + * version 2, as published by the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope 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.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along with
>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>> + */
>>> +
>>> +#include <linux/clk.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/device.h>
>>> +#include <linux/err.h>
>>> +#include <linux/errno.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/io.h>
>>> +#include <linux/ioport.h>
>>> +#include <linux/jiffies.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/log2.h>
>>> +#include <linux/module.h>
>>> +#include <linux/mtd/mtd.h>
>>> +#include <linux/mtd/partitions.h>
>>> +#include <linux/mutex.h>
>>> +#include <linux/of.h>
>>> +#include <linux/of_address.h>
>>> +#include <linux/param.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/pm.h>
>>> +#include <linux/sched.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/wait.h>
>>> +
>>> +#include "altera_epcq.h"
>>> +
>>> +/* data structure to maintain flash ids from different vendors */
>>> +struct flash_device {
>>> +     char *name;
>>> +     bool is_epcs;
>>> +     u32 device_id;
>>> +     uint32_t sectorsize_inbytes;
>>> +     uint64_t size_inbytes;
>>> +     u32 pagesize;
>>> +};
>>> +
>>> +#define FLASH_ID(_n, _is_epcs, _id, _ssize, _size, _psize)   \
>>> +{                            \
>>> +     .name = (_n),           \
>>> +     .is_epcs = (_is_epcs),          \
>>> +     .device_id = (_id),     \
>>> +     .sectorsize_inbytes = (_ssize), \
>>> +     .size_inbytes = (_size),        \
>>> +     .pagesize = (_psize),   \
>>> +}
>>> +
>>> +static struct flash_device flash_devices[] = {
>>> +     FLASH_ID("epcq16"    , 0, 0x15, 0x10000, 0x200000, 0x100),
>>> +     FLASH_ID("epcq32"    , 0, 0x16, 0x10000, 0x400000, 0x100),
>>> +     FLASH_ID("epcq64"    , 0, 0x17, 0x10000, 0x800000, 0x100),
>>> +     FLASH_ID("epcq128"   , 0, 0x18, 0x10000, 0x1000000, 0x100),
>>> +     FLASH_ID("epcq256"   , 0, 0x19, 0x10000, 0x2000000, 0x100),
>>> +     FLASH_ID("epcq512"   , 0, 0x20, 0x10000, 0x4000000, 0x100),
>>> +
>>> +     FLASH_ID("epcs16"    , 1, 0x14, 0x10000, 0x200000, 0x100),
>>> +     FLASH_ID("epcs64"    , 1, 0x16, 0x10000, 0x800000, 0x100),
>>> +     FLASH_ID("epcs128"   , 1, 0x18, 0x40000, 0x1000000, 0x100),
>>> +};
>>> +
>>> +static inline struct altera_epcq_flash *get_flash_data(struct mtd_info *mtd)
>>> +{
>>> +     return container_of(mtd, struct altera_epcq_flash, mtd);
>>> +}
>>> +
>>> +static u32 altera_epcq_read_sr(struct altera_epcq *dev)
>>> +{
>>> +     return readl(dev->csr_base + EPCQ_SR_REG);
>>> +}
>>> +
>>> +static int altera_epcq_wait_till_ready(struct altera_epcq *dev)
>>> +{
>>> +     unsigned long finish;
>>> +     int status;
>>> +
>>> +     finish = jiffies + EPCQ_MAX_TIME_OUT;
>>> +     do {
>>> +             status = altera_epcq_read_sr(dev);
>>> +             if (status < 0)
>>> +                     return status;
>>> +             else if (!(status & EPCQ_SR_WIP))
>>> +                     return 0;
>>> +
>>> +             cond_resched();
>>> +     } while (!time_after_eq(jiffies, finish));
>>> +
>>> +     dev_err(&dev->pdev->dev, "epcq controller is busy, timeout\n");
>>> +     return -EBUSY;
>>> +}
>>> +
>>> +static int get_flash_index(u32 flash_id, bool is_epcs)
>>> +{
>>> +     int index;
>>> +
>>> +     for (index = 0; index < ARRAY_SIZE(flash_devices); index++) {
>>> +             if (flash_devices[index].device_id == flash_id &&
>>> +                 flash_devices[index].is_epcs == is_epcs)
>>> +                     return index;
>>> +     }
>>> +
>>> +     /* Memory chip is not listed and not supported */
>>> +     return -ENODEV;
>>> +}
>>> +
>>> +static int altera_epcq_write_erase_check(struct altera_epcq *dev,
>>> +                                      bool write_erase)
>>> +{
>>> +     u32 val;
>>> +     u32 mask;
>>> +
>>> +     if (write_erase)
>>> +             mask = EPCQ_ISR_ILLEGAL_WRITE_MASK;
>>> +     else
>>> +             mask = EPCQ_ISR_ILLEGAL_ERASE_MASK;
>>> +
>>> +     val = readl(dev->csr_base + EPCQ_ISR_REG);
>>> +     if (val & mask) {
>>> +             dev_err(&dev->pdev->dev,
>>> +                     "write/erase failed, sector might be protected\n");
>>> +             /*clear this status for next use*/
>>> +             writel(val, dev->csr_base + EPCQ_ISR_REG);
>>> +             return -EIO;
>>> +     }
>>> +     return 0;
>>> +}
>>> +
>>> +static int altera_epcq_erase_chip(struct mtd_info *mtd)
>>> +{
>>> +     int ret;
>>> +     u32 val;
>>> +     struct altera_epcq *dev = mtd->priv;
>>> +
>>> +     /* Wait until finished previous write command. */
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>
>> This pattern is pretty silly. We don't need to have every operation
>> check the status of the previous operation. Each operation should check
>> itself. You obviously copied this one... but we recently changed that:
>>
>>     commit dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3
>>     Author: Brian Norris <computersforpeace@gmail.com>
>>     Date:   Wed Aug 6 18:16:57 2014 -0700
>>
>>         mtd: spi-nor: move "wait-till-ready" checks into erase/write
>>         functions
>>
>>
>>     https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3
>>
>>> +     if (ret)
>>> +             return ret;
>>> +
>>> +     /* erase chip. */
>>> +     val = EPCQ_MEM_OP_BULK_ERASE_CMD;
>>> +     writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
>>> +
>>> +     /* Wait until finished previous write command. */
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>> +     if (ret)
>>> +             return ret;
>>> +
>>> +      /* check whether write triggered a illegal write interrupt */
>>> +     ret = altera_epcq_write_erase_check(dev, 0);
>>> +     if (ret < 0)
>>> +             return ret;
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static int altera_epcq_addr_to_sector(struct altera_epcq_flash *flash,
>>> +                                   int addr_offset)
>>> +{
>>> +     int sector = 0;
>>> +     int i;
>>> +
>>> +     for (i = 0; i < flash->num_sectors; i++) {
>>> +             if (addr_offset >= i * flash->mtd.erasesize && addr_offset <
>>> +                 (i * flash->mtd.erasesize + flash->mtd.erasesize)) {
>>> +                     sector = i;
>>> +                     return sector;
>>> +             }
>>> +     }
>>
>> Uh, really? Am I crazy, or shouldn't this whole function just be a
>> really simple arithmetic operation? Like:
>>
>>         return addr_offset >> mtd->erasesize_shift;
>>
>>> +     return -1;
>>> +}
>>> +
>>> +static int altera_epcq_erase_sector(struct mtd_info *mtd,
>>> +                                 int addr_offset)
>>> +{
>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>> +     struct altera_epcq *dev = mtd->priv;
>>> +     u32 val;
>>> +     int ret;
>>> +     int sector_value;
>>> +
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>> +     if (ret)
>>> +             return ret;
>>> +
>>> +     sector_value = altera_epcq_addr_to_sector(flash, addr_offset);
>>> +
>>> +     /* sanity check that block_offset is a valid sector number */
>>> +     if (sector_value < 0)
>>> +             return -EINVAL;
>>> +
>>> +     /* sector value should occupy bits 17:8 */
>>> +     val = (sector_value << 8) & EPCQ_MEM_OP_SECTOR_VALUE_MASK;
>>> +
>>> +     /* sector erase commands occupies lower 2 bits */
>>> +     val |= EPCQ_MEM_OP_SECTOR_ERASE_CMD;
>>> +
>>> +     /* write sector erase command to EPCQ_MEM_OP register*/
>>> +     writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
>>> +
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>> +     if (ret)
>>> +             return ret;
>>> +
>>> +      /* check whether write triggered a illegal write interrupt */
>>
>> ^^ extra space here? You have <TAB><SPACE>/* check [...]
>>
>>> +     ret = altera_epcq_write_erase_check(dev, 0);
>>> +     if (ret < 0)
>>> +             return ret;
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static int altera_epcq_erase(struct mtd_info *mtd, struct erase_info *e_info)
>>> +{
>>> +     u32 addr;
>>> +     int ret;
>>> +     u32 len;
>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>> +     struct altera_epcq *dev = mtd->priv;
>>> +
>>> +     addr = e_info->addr;
>>> +     len = e_info->len;
>>> +
>>> +     if (flash->bank > dev->num_flashes - 1) {
>>> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
>>> +             return -EINVAL;
>>> +     }
>>> +     mutex_lock(&flash->lock);
>>> +
>>> +     /*erase whole chip*/
>>> +     if (len == mtd->size) {
>>> +             if (altera_epcq_erase_chip(mtd)) {
>>> +                     e_info->state = MTD_ERASE_FAILED;
>>> +                     mutex_unlock(&flash->lock);
>>> +                     return -EIO;
>>> +             }
>>> +     /*"sector"-at-a-time erase*/
>>> +     } else {
>>> +             while (len) {
>>> +                     ret = altera_epcq_erase_sector(mtd, addr);
>>> +                     if (ret) {
>>> +                             e_info->state = MTD_ERASE_FAILED;
>>> +                             mutex_unlock(&flash->lock);
>>> +                             return -EIO;
>>> +                     }
>>> +                     addr += mtd->erasesize;
>>> +                     if (len < mtd->erasesize)
>>> +                             len = 0;
>>> +                     else
>>> +                             len -= mtd->erasesize;
>>> +             }
>>> +     }
>>> +     mutex_unlock(&flash->lock);
>>> +     e_info->state = MTD_ERASE_DONE;
>>> +     mtd_erase_callback(e_info);
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static int altera_epcq_read(struct mtd_info *mtd, loff_t from, size_t len,
>>> +                         size_t *retlen, u8 *buf)
>>> +{
>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>> +     struct altera_epcq *dev = mtd->priv;
>>> +     void *src;
>>> +     int ret = 0;
>>> +
>>> +     if (!flash || !dev)
>>> +             return -ENODEV;
>>
>> Why would either of these be 0? If they are, you have much bigger
>> problems, since you aren't checking these almost anywhere else. And the
>> !flash check is pretty odd, since get_flash_data() is using
>> container_of(), which is not likely to give you exactly 0...
>>
>>> +
>>> +     if (flash->bank > dev->num_flashes - 1) {
>>> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
>>> +             return -EINVAL;
>>> +     }
>>> +
>>> +     src = dev->data_base + from;
>>> +
>>> +     mutex_lock(&flash->lock);
>>> +     /* wait till previous write/erase is done. */
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>> +     if (ret)
>>> +             goto err;
>>> +
>>> +     memcpy_fromio(buf, (u8 *)src, len);
>>
>> Drop the cast. Also, you get sparse warnings because you're dropping the
>> __iomem. Why not just this?
>>
>>         memcpy_fromio(buf, dev->data_base + from, len);
>>
>> (BTW, you might consider running sparse on all your code. See
>> Documentation/sparse.txt. It tells you how to get it, but you can often
>> just download it through your distro's package manager.)
>>
>>> +     *retlen = len;
>>> +
>>> +err:
>>> +     mutex_unlock(&flash->lock);
>>> +     return ret;
>>> +}
>>> +
>>> +static int altera_epcq_write(struct mtd_info *mtd, loff_t to, size_t len,
>>> +                          size_t *retlen, const u8 *buf)
>>> +{
>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>> +     struct altera_epcq *dev = mtd->priv;
>>> +     void *dest;
>>> +     int ret = 0;
>>> +
>>> +     if (!flash || !dev)
>>> +             return -ENODEV;
>>
>> Also here. I don't think you need these checks.
>>
>>> +
>>> +     if (flash->bank > dev->num_flashes - 1) {
>>> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
>>> +             return -EINVAL;
>>> +     }
>>> +     dest = dev->data_base + to;
>>> +
>>> +     mutex_lock(&flash->lock);
>>> +
>>> +     /* wait until finished previous write command. */
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>> +     if (ret)
>>> +             goto err;
>>> +
>>> +     memcpy_toio(dest, buf, len);
>>
>> Similar. Why not just this?
>>
>>         memcpy_toio(dev->data_base + to, buf, len);
>>
>>> +     *retlen += len;
>>> +
>>> +     /* wait until finished previous write command. */
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>> +     if (ret)
>>> +             goto err;
>>> +
>>> +      /* check whether write triggered a illegal write interrupt */
>>> +     ret = altera_epcq_write_erase_check(dev, 1);
>>> +     if (ret < 0)
>>> +             goto err;
>>> +
>>> +err:
>>> +     mutex_unlock(&flash->lock);
>>> +     return ret;
>>> +}
>>> +
>>> +static int altera_epcq_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
>>> +{
>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>> +     struct altera_epcq *dev = mtd->priv;
>>> +     uint32_t offset = ofs;
>>> +     int ret = 0;
>>> +     u32 sector_start, sector_end;
>>> +     u32 num_sectors;
>>> +     u32 mem_op;
>>> +     unsigned sr_bp = 0;
>>> +     unsigned sr_tb = 0;
>>> +
>>> +     sector_start = altera_epcq_addr_to_sector(flash, offset);
>>> +     sector_end = altera_epcq_addr_to_sector(flash, offset + len);
>>> +     num_sectors = flash->num_sectors;
>>> +     dev_dbg(&dev->pdev->dev,
>>> +             "%s: num_setor is %u,sector start is %u,sector end is %u\n",
>>> +             __func__, num_sectors, sector_start, sector_end);
>>> +
>>> +     mutex_lock(&flash->lock);
>>> +     /* wait until finished previous write command. */
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>> +     if (ret)
>>> +             goto err;
>>> +
>>> +     if (sector_start >= num_sectors/2) {
>>
>> It's preferable to have spaces around operators. So 'num_sectors / 2'.
>> The same thing comes up in several other places.
>>
>>> +             if (sector_start < num_sectors-(num_sectors / 4))
>>> +                     sr_bp = __ilog2_u32(num_sectors);
>>> +             else if (sector_start < num_sectors-(num_sectors / 8))
>>> +                     sr_bp = __ilog2_u32(num_sectors) - 1;
>>> +             else if (sector_start < num_sectors-(num_sectors / 16))
>>> +                     sr_bp = __ilog2_u32(num_sectors) - 2;
>>> +             else if (sector_start < num_sectors-(num_sectors / 32))
>>> +                     sr_bp = __ilog2_u32(num_sectors) - 3;
>>> +             else if (sector_start < num_sectors-(num_sectors / 64))
>>> +                     sr_bp = __ilog2_u32(num_sectors) - 4;
>>> +             else if (sector_start < num_sectors-(num_sectors / 128))
>>> +                     sr_bp = __ilog2_u32(num_sectors) - 5;
>>> +             else if (sector_start < num_sectors-(num_sectors / 256))
>>> +                     sr_bp = __ilog2_u32(num_sectors) - 6;
>>> +             else if (sector_start < num_sectors-(num_sectors / 512))
>>> +                     sr_bp = __ilog2_u32(num_sectors) - 7;
>>> +             else if (sector_start < num_sectors-(num_sectors / 1024))
>>> +                     sr_bp = __ilog2_u32(num_sectors) - 8;
>>> +             else
>>> +                     sr_bp = 0;  /* non area protected */
>>
>> Yikes, that's ugly! And I'm not sure it matches the EPCQ doc I found.
>> I'm pretty sure you can rewrite this if/else-if/else block in about 1
>> line though.
>>
>>> +
>>> +             if (sr_bp < 0) {
>>
>> sr_bp is unsigned, so this is never true.
>>
>>> +                     dev_err(&dev->pdev->dev, "%s: address is out of range\n"
>>> +                             , __func__);
>>> +                     ret = -EINVAL;
>>> +                     goto err;
>>> +             }
>>> +             /*set TB = 0*/
>>> +             sr_tb = 0;
>>> +
>>> +     } else {
>>> +             if (sector_end < 1)
>>> +                     sr_bp = 1;
>>> +             else if (sector_end < 2)
>>> +                     sr_bp = 2;
>>> +             else if (sector_end < 4)
>>> +                     sr_bp = 3;
>>> +             else if (sector_end < 8)
>>> +                     sr_bp = 4;
>>> +             else if (sector_end < 16)
>>> +                     sr_bp = 5;
>>> +             else if (sector_end < 32)
>>> +                     sr_bp = 6;
>>> +             else if (sector_end < 64)
>>> +                     sr_bp = 7;
>>> +             else if (sector_end < 128)
>>> +                     sr_bp = 8;
>>> +             else if (sector_end < 256)
>>> +                     sr_bp = 9;
>>> +             else if (sector_end < 512)
>>> +                     sr_bp = 10;
>>> +             else
>>> +                     sr_bp = 16; /*protect all areas*/
>>
>> Again, this is ugly. How about this?
>>
>>         sr_bp = fls(sector_end) + 1;
>>
>>> +
>>> +             sr_tb = 1;
>>> +     }
>>> +
>>> +     mem_op = (sr_tb << 12) | (sr_bp << 8);
>>> +     mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
>>> +     mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
>>> +     writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
>>> +
>>> +err:
>>> +     mutex_unlock(&flash->lock);
>>> +     return ret;
>>> +}
>>> +
>>> +static int altera_epcq_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
>>> +{
>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>> +     struct altera_epcq *dev = mtd->priv;
>>> +
>>> +     int ret = 0;
>>> +     u32 mem_op;
>>> +
>>> +     mutex_lock(&flash->lock);
>>> +     /* wait until finished previous write command. */
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>> +     if (ret)
>>> +             goto err;
>>> +     dev_info(&dev->pdev->dev, "Unlock all protected area\n");
>>> +     mem_op = 0;
>>> +     mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
>>> +     mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
>>> +     writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
>>> +
>>> +err:
>>> +     mutex_unlock(&flash->lock);
>>> +     return ret;
>>> +}
>>> +
>>> +static void altera_epcq_chip_select(struct altera_epcq *dev, u32 bank)
>>> +{
>>> +     u32 val = 0;
>>> +
>>> +     switch (bank) {
>>> +     case 0:
>>> +             val = EPCQ_CHIP_SELECT_0;
>>> +             break;
>>> +     case 1:
>>> +             val = EPCQ_CHIP_SELECT_1;
>>> +             break;
>>> +     case 2:
>>> +             val = EPCQ_CHIP_SELECT_2;
>>> +             break;
>>> +     default:
>>> +             return;
>>> +     }
>>> +
>>> +     writel(val, dev->csr_base + EPCQ_CHIP_SELECT_REG);
>>> +}
>>> +
>>> +static int altera_epcq_probe_flash(struct altera_epcq *dev, u32 bank)
>>> +{
>>> +     int ret = 0;
>>> +     u32 val = 0;
>>> +
>>> +     mutex_lock(&dev->lock);
>>> +
>>> +     /*select bank*/
>>
>> Comment spacing, here and elsewhere:
>>
>>         /* select bank */
>>
>>> +     altera_epcq_chip_select(dev, bank);
>>> +
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>> +     if (ret)
>>> +             goto err;
>>> +
>>> +     /* get device sillicon id */
>>> +     if (dev->is_epcs)
>>> +             val = readl(dev->csr_base + EPCQ_SID_REG);
>>> +     else
>>> +             val = readl(dev->csr_base + EPCQ_RDID_REG);
>>> +
>>> +     /* get flash index based on the device list*/
>>> +     ret = get_flash_index(val, dev->is_epcs);
>>> +     return 0;
>>> +err:
>>> +     mutex_unlock(&dev->lock);
>>> +     return ret;
>>> +}
>>> +
>>> +static int altera_epcq_probe_config_dt(struct platform_device *pdev,
>>> +                                    struct device_node *np,
>>> +                                    struct altera_epcq_plat_data *pdata)
>>> +{
>>> +     struct device_node *pp = NULL;
>>> +     struct resource *epcq_res;
>>> +     int i = 0;
>>> +     u32 id;
>>> +
>>> +     pdata->is_epcs = of_property_read_bool(np, "is-epcs");
>>> +
>>> +     epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
>>> +                                             "csr_base");
>>> +     if (!epcq_res) {
>>
>> devm_ioremap_resource() will check the that 'epcq_res' is valid, so you
>> don't need this whole 'if' block.
>>
>>> +             dev_err(&pdev->dev, "resource csr base is not defined\n");
>>> +             return -ENODEV;
>>> +     }
>>> +
>>> +     pdata->csr_base = devm_ioremap_resource(&pdev->dev, epcq_res);
>>> +     if (IS_ERR(pdata->csr_base)) {
>>> +             dev_err(&pdev->dev, "%s: ERROR: failed to map csr base\n",
>>> +                     __func__);
>>> +             return PTR_ERR(pdata->csr_base);
>>> +     }
>>> +
>>> +     epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
>>> +                                             "data_base");
>>> +     if (!epcq_res) {
>>
>> Same here.
>>
>>> +             dev_err(&pdev->dev, "resource data base is not defined\n");
>>> +             return -ENODEV;
>>> +     }
>>> +
>>> +     pdata->data_base = devm_ioremap_resource(&pdev->dev, epcq_res);
>>> +     if (IS_ERR(pdata->data_base)) {
>>> +             dev_err(&pdev->dev, "%s: ERROR: failed to map data base\n",
>>> +                     __func__);
>>> +             return PTR_ERR(pdata->data_base);
>>> +     }
>>> +
>>> +     pdata->board_flash_info = devm_kzalloc(&pdev->dev,
>>> +                                            sizeof(*pdata->board_flash_info),
>>> +                                            GFP_KERNEL);
>>> +
>>> +     /* Fill structs for each subnode (flash device) */
>>> +     while ((pp = of_get_next_child(np, pp))) {
>>
>> for_each_available_child_of_node()?
>>
>>> +             struct altera_epcq_flash_info *flash_info;
>>> +
>>> +             flash_info = &pdata->board_flash_info[i];
>>
>> This is never used. Either use it below, or drop the local variable.
>>
>>> +             pdata->np[i] = pp;
>>> +
>>> +             /* Read bank id from DT */
>>> +             if (of_get_property(pp, "reg", &id))
>>
>> Is this property optional? Your DT binding doc doesn't make it clear,
>> but it seems like a property which would be wise to require (i.e., not
>> optional).
>>
>>> +                     pdata->board_flash_info[i].bank = id;
>>> +             i++;
>>> +     }
>>> +     pdata->num_flashes = i;
>>> +     return 0;
>>> +}
>>> +
>>> +static int altera_epcq_setup_banks(struct platform_device *pdev,
>>> +                                u32 bank, struct device_node *np,
>>> +                                struct altera_epcq_plat_data *pdata)
>>> +{
>>> +     struct altera_epcq *dev = platform_get_drvdata(pdev);
>>> +     struct mtd_part_parser_data ppdata = {};
>>> +     struct altera_epcq_flash_info *flash_info;
>>> +     struct altera_epcq_flash *flash;
>>> +     struct mtd_partition *parts = NULL;
>>
>> Drop this variable. Just use NULL directly below.
>>
>>> +     int count = 0;
>>
>> Same. Just use 0 below.
>>
>>> +     int flash_index;
>>> +     int ret = 0;
>>> +     uint64_t size;
>>> +     uint32_t sector_size;
>>> +
>>> +     flash_info = &pdata->board_flash_info[bank];
>>> +     if (!flash_info)
>>> +             return -ENODEV;
>>> +
>>> +     if (bank > pdata->num_flashes - 1)
>>> +             return -EINVAL;
>>> +
>>> +     flash = devm_kzalloc(&pdev->dev, sizeof(*flash), GFP_KERNEL);
>>> +     if (!flash)
>>> +             return -ENOMEM;
>>> +     flash->bank = bank;
>>> +
>>> +     mutex_init(&flash->lock);
>>> +
>>> +     /* verify whether flash is really present on board */
>>> +     flash_index = altera_epcq_probe_flash(dev, bank);
>>> +     if (flash_index < 0) {
>>> +             dev_info(&dev->pdev->dev, "epcq:flash%d not found\n",
>>> +                      flash->bank);
>>> +             return flash_index;
>>> +     }
>>> +
>>> +     dev->flash[bank] = flash;
>>> +
>>> +     size = flash_devices[flash_index].size_inbytes;
>>> +     sector_size = flash_devices[flash_index].sectorsize_inbytes;
>>> +     /*use do_div instead of plain div to avoid linker err*/
>>> +     do_div(size, sector_size);
>>> +     flash->num_sectors = size;
>>> +
>>> +     /*mtd framework */
>>> +     flash->mtd.priv = dev;
>>> +     flash->mtd.name = flash_devices[flash_index].name;
>>> +     flash->mtd.type = MTD_NORFLASH;
>>> +     flash->mtd.writesize = 1;
>>> +     flash->mtd.flags = MTD_CAP_NORFLASH;
>>> +     flash->mtd.size = flash_devices[flash_index].size_inbytes;
>>> +     flash->mtd.erasesize = flash_devices[flash_index].sectorsize_inbytes;
>>> +     flash->mtd._erase = altera_epcq_erase;
>>> +     flash->mtd._read = altera_epcq_read;
>>> +     flash->mtd._write = altera_epcq_write;
>>> +     flash->mtd._lock = altera_epcq_lock;
>>> +     flash->mtd._unlock = altera_epcq_unlock;
>>> +
>>> +     dev_info(&pdev->dev, "mtd .name=%s .size=0x%llx (%lluM)\n",
>>> +              flash->mtd.name, (long long)flash->mtd.size,
>>> +              (long long)(flash->mtd.size >> 20));
>>> +
>>> +     dev_info(&pdev->dev, ".erasesize = 0x%x(%uK)\n",
>>> +              flash->mtd.erasesize, flash->mtd.erasesize >> 10);
>>> +
>>> +     ppdata.of_node = np;
>>> +
>>> +     ret = mtd_device_parse_register(&flash->mtd, NULL, &ppdata, parts,
>>> +                                     count);
>>> +     if (ret) {
>>> +             dev_err(&pdev->dev, "Err MTD partition=%d\n", ret);
>>> +             goto err;
>>
>> You don't want to unregister a MTD that failed to register. The MTD core
>> code should handle that itself.
>>
>> Instead of this if (ret) {} block, I'd just make this:
>>
>>         return mtd_device_parse_register(&flash->mtd, NULL, &ppdata, NULL, 0);
>>
>>> +     }
>>> +
>>> +     return 0;
>>> +
>>> +err:
>>> +     mtd_device_unregister(&flash->mtd);
>>> +     return ret;
>>> +}
>>> +
>>> +static int altera_epcq_probe(struct platform_device *pdev)
>>> +{
>>> +     struct device_node *np = pdev->dev.of_node;
>>> +     struct altera_epcq_plat_data *pdata = NULL;
>>> +     struct altera_epcq *dev;
>>> +     int ret = 0;
>>> +     int i;
>>> +
>>> +     if (!np) {
>>> +             ret = -ENODEV;
>>> +             dev_err(&pdev->dev, "no device found\n");
>>> +             goto err;
>>> +     }
>>> +
>>> +     pdata = devm_kzalloc(&pdev->dev,
>>> +                          sizeof(struct altera_epcq_plat_data),
>>> +                          GFP_KERNEL);
>>> +
>>> +     if (!pdata) {
>>> +             ret = -ENOMEM;
>>> +             dev_err(&pdev->dev, "%s: ERROR: no memory\n", __func__);
>>
>> Unnecessary print. The MM code will print plenty of info if you're
>> out of memory.
>>
>>> +             goto err;
>>> +     }
>>> +     ret = altera_epcq_probe_config_dt(pdev, np, pdata);
>>> +     if (ret) {
>>> +             ret = -ENODEV;
>>> +             dev_err(&pdev->dev, "probe fail\n");
>>> +             goto err;
>>> +     }
>>> +
>>> +     dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
>>> +     if (!dev) {
>>> +             ret = -ENOMEM;
>>> +             dev_err(&pdev->dev, "mem alloc fail\n");
>>
>> Same here.
>>
>>> +             goto err;
>>> +     }
>>> +     mutex_init(&dev->lock);
>>> +     dev->pdev = pdev;
>>> +     dev->is_epcs = pdata->is_epcs;
>>> +     dev->csr_base = pdata->csr_base;
>>> +     dev->data_base = pdata->data_base;
>>> +
>>> +     /*check number of flashes*/
>>> +     dev->num_flashes = pdata->num_flashes;
>>> +     if (dev->num_flashes > MAX_NUM_FLASH_CHIP) {
>>> +             dev_err(&pdev->dev, "exceeding max number of flashes\n");
>>> +             dev->num_flashes = MAX_NUM_FLASH_CHIP;
>>> +     }
>>> +
>>> +     /* check clock*/
>>> +     dev->clk = devm_clk_get(&pdev->dev, NULL);
>>> +     if (IS_ERR(dev->clk)) {
>>> +             ret = PTR_ERR(dev->clk);
>>> +             goto err;
>>> +     }
>>> +     ret = clk_prepare_enable(dev->clk);
>>> +     if (ret)
>>> +             goto err;
>>> +
>>> +     platform_set_drvdata(pdev, dev);
>>> +
>>> +     /* loop for each serial flash which is connected to epcq */
>>> +     for (i = 0; i < dev->num_flashes; i++) {
>>> +             ret = altera_epcq_setup_banks(pdev, i, pdata->np[i], pdata);
>>> +             if (ret) {
>>> +                     dev_err(&pdev->dev, "bank setup failed\n");
>>> +                     goto err_bank_setup;
>>> +             }
>>> +     }
>>> +
>>> +     return 0;
>>> +
>>> +err_bank_setup:
>>> +     clk_disable_unprepare(dev->clk);
>>> +err:
>>> +     return ret;
>>> +}
>>> +
>>> +static int altera_epcq_remove(struct platform_device *pdev)
>>> +{
>>> +     struct altera_epcq *dev;
>>> +     struct altera_epcq_flash *flash;
>>> +     int ret, i;
>>> +
>>> +     dev = platform_get_drvdata(pdev);
>>> +
>>> +     /* clean up for all nor flash */
>>> +     for (i = 0; i < dev->num_flashes; i++) {
>>> +             flash = dev->flash[i];
>>> +             if (!flash)
>>> +                     continue;
>>> +
>>> +             /* clean up mtd stuff */
>>> +             ret = mtd_device_unregister(&flash->mtd);
>>> +             if (ret)
>>> +                     dev_err(&pdev->dev, "error removing mtd\n");
>>> +     }
>>> +
>>> +     clk_disable_unprepare(dev->clk);
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +#ifdef CONFIG_PM
>>
>> Make this CONFIG_PM_SLEEP.
>>
>>> +static int altera_epcq_suspend(struct device *dev)
>>> +{
>>> +     struct altera_epcq *sdev = dev_get_drvdata(dev);
>>> +
>>> +     clk_disable_unprepare(sdev->clk);
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static int altera_epcq_resume(struct device *dev)
>>> +{
>>> +     struct altera_epcq *sdev = dev_get_drvdata(dev);
>>> +     int ret = -EPERM;
>>
>> Drop 'ret'.
>>
>>> +
>>> +     ret = clk_prepare_enable(sdev->clk);
>>
>> Just:
>>
>>         return clk_prepare_enable(sdev->clk);
>>
>>> +
>>> +     return ret;
>>> +}
>>> +
>>> +static SIMPLE_DEV_PM_OPS(altera_epcq_pm_ops, altera_epcq_suspend,
>>> +                      altera_epcq_resume);
>>> +#endif
>>> +
>>> +static const struct of_device_id altera_epcq_id_table[] = {
>>> +     { .compatible = "altr,epcq-1.0" },
>>> +     {}
>>> +};
>>> +MODULE_DEVICE_TABLE(of, altera_epcq_id_table);
>>> +
>>> +static struct platform_driver altera_epcq_driver = {
>>> +     .driver = {
>>> +             .name = ALTERA_EPCQ_RESOURCE_NAME,
>>> +             .bus = &platform_bus_type,
>>> +             .owner = THIS_MODULE,
>>
>> The .owner assignment is no longer necessary.
>>
>>> +             .of_match_table = altera_epcq_id_table,
>>> +#ifdef CONFIG_PM
>>
>> Also CONFIG_PM_SLEEP.
>>
>>> +             .pm = &altera_epcq_pm_ops,
>>> +#endif
>>> +     },
>>> +     .probe = altera_epcq_probe,
>>> +     .remove = altera_epcq_remove,
>>> +};
>>> +module_platform_driver(altera_epcq_driver);
>>> +
>>> +MODULE_AUTHOR("Viet Nga Dao <vndao@altera.com>");
>>> +MODULE_DESCRIPTION("MTD Altera EPCQ Driver");
>>> +MODULE_LICENSE("GPL v2");
>>> diff --git a/drivers/mtd/devices/altera_epcq.h b/drivers/mtd/devices/altera_epcq.h
>>> new file mode 100644
>>> index 0000000..c3d15e5
>>> --- /dev/null
>>> +++ b/drivers/mtd/devices/altera_epcq.h
>>> @@ -0,0 +1,130 @@
>>> +/*
>>> + * Copyright (C) 2014 Altera Corporation. All rights reserved
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms and conditions of the GNU General Public License,
>>> + * version 2, as published by the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope 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.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along with
>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>> + */
>>> +
>>> +#ifndef __ALTERA_ECPQ_H
>>> +#define __ALTERA_ECPQ_H
>>> +
>>> +#define ALTERA_EPCQ_RESOURCE_NAME        "altera_epcq"
>>> +/* max possible slots for serial flash chip in the EPCQ controller */
>>> +#define MAX_NUM_FLASH_CHIP   3
>>> +
>>> +/* macro to define partitions for flash devices */
>>> +#define DEFINE_PARTS(n, of, s)               \
>>
>> This macro is not used anywhere. Drop it?
>>
>>> +{                                    \
>>> +     .name = n,                      \
>>> +     .offset = of,                   \
>>> +     .size = s,                      \
>>> +}
>>> +
>>> +struct altera_epcq_flash_info {
>>> +     u32 bank;
>>> +     struct mtd_partition *partitions;
>>> +     int nr_partitions;
>>> +};
>>> +
>>> +struct altera_epcq_plat_data {
>>> +     void __iomem *csr_base;
>>> +     void __iomem *data_base;
>>> +     bool is_epcs;
>>> +     u32 num_flashes;
>>> +     struct altera_epcq_flash_info *board_flash_info;
>>> +     struct device_node *np[MAX_NUM_FLASH_CHIP];
>>> +};
>>> +
>>> +struct altera_epcq {
>>> +     struct clk *clk;
>>> +     bool is_epcs;
>>> +     struct mutex lock;      /*device lock*/
>>> +     void __iomem *csr_base;
>>> +     void __iomem *data_base;
>>> +     struct platform_device *pdev;
>>> +     u32 num_flashes;
>>> +     struct altera_epcq_flash *flash[MAX_NUM_FLASH_CHIP];
>>> +};
>>> +
>>> +struct altera_epcq_flash {
>>> +     u32 bank;
>>> +     u32 num_sectors;
>>> +     u32 num_parts;
>>> +     struct mtd_partition *parts;
>>> +     struct mutex lock;      /*flash lock*/
>>> +     struct mtd_info mtd;
>>> +};
>>> +
>>> +/* Define max times to check status register before we give up. */
>>> +#define EPCQ_MAX_TIME_OUT                    (40 * HZ)
>>> +
>>> +/* defines for status register */
>>> +#define EPCQ_SR_REG                          0x0
>>> +#define EPCQ_SR_WIP_MASK                     0x00000001
>>> +#define EPCQ_SR_WIP                          0x1
>>> +#define EPCQ_SR_WEL                          0x2
>>> +#define EPCQ_SR_BP0                          0x4
>>> +#define EPCQ_SR_BP1                          0x8
>>> +#define EPCQ_SR_BP2                          0x10
>>> +#define EPCQ_SR_BP3                          0x40
>>> +#define EPCQ_SR_TB                           0x20
>>> +
>>> +/* defines for device id register */
>>> +#define EPCQ_SID_REG                         0x4
>>> +#define EPCQ_RDID_REG                                0x8
>>> +#define EPCQ_RDID_MASK                               0x000000FF
>>> +/*
>>> + * EPCQ_MEM_OP register offset
>>> + *
>>> + * The EPCQ_MEM_OP register is used to do memory protect and erase operations
>>> + *
>>> + */
>>> +#define EPCQ_MEM_OP_REG                              0xC
>>> +
>>> +#define EPCQ_MEM_OP_CMD_MASK                 0x00000003
>>> +#define EPCQ_MEM_OP_BULK_ERASE_CMD           0x00000001
>>> +#define EPCQ_MEM_OP_SECTOR_ERASE_CMD         0x00000002
>>> +#define EPCQ_MEM_OP_SECTOR_PROTECT_CMD               0x00000003
>>> +#define EPCQ_MEM_OP_SECTOR_VALUE_MASK                0x0003FF00
>>> +#define EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK        0x00001F00
>>> +#define EPCQ_MEM_OP_SECTOR_PROTECT_SHIFT     8
>>> +/*
>>> + * EPCQ_ISR register offset
>>> + *
>>> + * The EPCQ_ISR register is used to determine whether an invalid write or erase
>>> + * operation trigerred an interrupt
>>> + *
>>> + */
>>> +#define EPCQ_ISR_REG                         0x10
>>> +
>>> +#define EPCQ_ISR_ILLEGAL_ERASE_MASK          0x00000001
>>> +#define EPCQ_ISR_ILLEGAL_WRITE_MASK          0x00000002
>>> +
>>> +/*
>>> + * EPCQ_IMR register offset
>>> + *
>>> + * The EPCQ_IMR register is used to mask the invalid erase or the invalid write
>>> + * interrupts.
>>> + *
>>> + */
>>> +#define EPCQ_IMR_REG                         0x14
>>> +#define EPCQ_IMR_ILLEGAL_ERASE_MASK          0x00000001
>>> +
>>> +#define EPCQ_IMR_ILLEGAL_WRITE_MASK          0x00000002
>>> +
>>> +#define EPCQ_CHIP_SELECT_REG                 0x18
>>> +#define EPCQ_CHIP_SELECT_MASK                        0x00000007
>>> +#define EPCQ_CHIP_SELECT_0                   0x00000001
>>> +#define EPCQ_CHIP_SELECT_1                   0x00000002
>>> +#define EPCQ_CHIP_SELECT_2                   0x00000004
>>> +
>>> +#endif /* __ALTERA_ECPQ_H */
>>
>> Brian

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-01-20  2:05   ` Viet Nga Dao
  0 siblings, 0 replies; 39+ messages in thread
From: Viet Nga Dao @ 2015-01-20  2:05 UTC (permalink / raw)
  To: Viet Nga Dao
  Cc: Brian Norris, David Woodhouse,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, nga_chi86

On Thu, Jan 15, 2015 at 5:27 PM, Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org> wrote:
> On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
> <computersforpeace-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>> On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org wrote:
>>> From: Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org>
>>>
>>> Altera EPCQ Controller is a soft IP which enables access to Altera EPCQ and
>>> EPCS flash chips. This patch adds driver for these devices.
>>>
>>> Signed-off-by: Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org>
>>
>> This drivers seems awfully similar to (and so I infer it is likely
>> copy-and-pasted from) m25p80.c / spi-nor.c. Do you think it can be
>> rewritten as a SPI NOR driver, under drivers/mtd/spi-nor/ ? It looks
>> like these flash share most (all?) the same basic opcodes.
>>
> For Altera EPCQ flashes, almost operations are performed underline
> hardware. Software only able to perform the following through
> registers:
>  -  read status register
>  -  read id
>  -  write status registers bit SR_BP0,SR_BP1, SR_BP2,SR_BP3, SR_TB
> (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf)
> For read/write data: all the operations like QUAD_READ/WRITE,
> FAST_READ/WRITE are handled by hardware as well. From software point
> of view, there is no difference between these 2 modes.
> That is why if rewrite the drivers to follow spi-nor structure, it
> will require extra decoding works for the only few used opcodes.
>
Is it OK to remain this driver structure?
>>> ---
>>>  .../devicetree/bindings/mtd/altera_epcq.txt        |   45 ++
>>>  drivers/mtd/devices/Kconfig                        |   12 +
>>>  drivers/mtd/devices/Makefile                       |    2 +-
>>>  drivers/mtd/devices/altera_epcq.c                  |  804 ++++++++++++++++++++
>>>  drivers/mtd/devices/altera_epcq.h                  |  130 ++++
>>>  5 files changed, 992 insertions(+), 1 deletions(-)
>>>  create mode 100644 Documentation/devicetree/bindings/mtd/altera_epcq.txt
>>>  create mode 100644 drivers/mtd/devices/altera_epcq.c
>>>  create mode 100644 drivers/mtd/devices/altera_epcq.h
>>>
>>> diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>>> new file mode 100644
>>> index 0000000..d14f50e
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>>> @@ -0,0 +1,45 @@
>>> +* MTD Altera EPCQ driver
>>> +
>>> +Required properties:
>>> +- compatible: Should be "altr,epcq-1.0"
>>> +- reg: Address and length of the register set  for the device. It contains
>>> +  the information of registers in the same order as described by reg-names
>>> +- reg-names: Should contain the reg names
>>> +  "csr_base": Should contain the register configuration base address
>>> +  "data_base": Should contain the data base address
>>> +- is-epcs: boolean type.
>>> +             If present, the device contains EPCS flashes.
>>> +             Otherwise, it contains EPCQ flashes.
>>> +- #address-cells: Must be <1>.
>>> +- #size-cells: Must be <0>.
>>> +- flash device tree subnode, there must be a node with the following fields:
>>
>> These subnodes definitely require a 'compatible' string. Perhaps they
>> should be used to differentiate EPCS vs. EPCQ. Does "is-epcs" really
>> need to be in the top-level controller node?
>>
>>> +     - reg: Should contain the flash id
>>
>> Should, or must? (This question is relevant, because you seem to make it
>> optional in your code.) And what does the "flash ID" mean? It seems like
>> you're using as a chip-select or bank index.
>>
>>> +     - #address-cells: please refer to /mtd/partition.txt
>>> +     - #size-cells: please refer to /mtd/partition.txt
>>> +     For partitions inside each flash, please refer to /mtd/partition.txt
>>> +
>>> +Example:
>>> +
>>> +                     epcq_controller_0: epcq@0x000000000 {
>>> +                             compatible = "altr,epcq-1.0";
>>> +                             reg = <0x00000001 0x00000000 0x00000020>,
>>> +                                     <0x00000000 0x00000000 0x02000000>;
>>> +                             reg-names = "csr_base", "data_base";
>>> +                             #address-cells = <1>;
>>> +                             #size-cells = <0>;
>>> +                             flash0: epcq256@0 {
>>> +                                     reg = <0>;
>>> +                                     #address-cells = <1>;
>>> +                                     #size-cells = <1>;
>>> +                                     partition@0 {
>>> +                                             /* 16 MB for raw data. */
>>> +                                             label = "EPCQ Flash 0 raw data";
>>> +                                             reg = <0x0 0x1000000>;
>>> +                                     };
>>> +                                     partition@1000000 {
>>> +                                             /* 16 MB for jffs2 data. */
>>> +                                             label = "EPCQ Flash 0 JFFS 2";
>>> +                                             reg = <0x1000000 0x1000000>;
>>> +                                     };
>>> +                             };
>>> +                     }; //end epcq@0x000000000 (epcq_controller_0)
>>> diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
>>> index c49d0b1..020b864 100644
>>> --- a/drivers/mtd/devices/Kconfig
>>> +++ b/drivers/mtd/devices/Kconfig
>>> @@ -218,6 +218,18 @@ config MTD_ST_SPI_FSM
>>>         SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
>>>         for a subset of connected Serial Flash devices.
>>>
>>> +config MTD_ALTERA_EPCQ
>>> +     tristate "Support Altera EPCQ/EPCS Flash chips"
>>> +     depends on OF
>>> +     help
>>> +       This enables access to Altera EPCQ/EPCS flash chips, used for data
>>> +       storage. See the driver source for the current list,
>>> +       or to add other chips.
>>> +
>>> +       If you want to compile this driver as a module ( = code which can be
>>> +       inserted in and removed from the running kernel whenever you want),
>>> +       say M here and read <file:Documentation/kbuild/modules.txt>.
>>> +
>>>  if MTD_DOCG3
>>>  config BCH_CONST_M
>>>       default 14
>>> diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
>>> index f0b0e61..b429c4d 100644
>>> --- a/drivers/mtd/devices/Makefile
>>> +++ b/drivers/mtd/devices/Makefile
>>> @@ -16,6 +16,6 @@ obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
>>>  obj-$(CONFIG_MTD_SST25L)     += sst25l.o
>>>  obj-$(CONFIG_MTD_BCM47XXSFLASH)      += bcm47xxsflash.o
>>>  obj-$(CONFIG_MTD_ST_SPI_FSM)    += st_spi_fsm.o
>>> -
>>> +obj-$(CONFIG_MTD_ALTERA_EPCQ)                += altera_epcq.o
>>>
>>>  CFLAGS_docg3.o                       += -I$(src)
>>> diff --git a/drivers/mtd/devices/altera_epcq.c b/drivers/mtd/devices/altera_epcq.c
>>> new file mode 100644
>>> index 0000000..09213d5
>>> --- /dev/null
>>> +++ b/drivers/mtd/devices/altera_epcq.c
>>> @@ -0,0 +1,804 @@
>>> +/*
>>> + * Copyright (C) 2014 Altera Corporation. All rights reserved
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms and conditions of the GNU General Public License,
>>> + * version 2, as published by the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope 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.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along with
>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>> + */
>>> +
>>> +#include <linux/clk.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/device.h>
>>> +#include <linux/err.h>
>>> +#include <linux/errno.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/io.h>
>>> +#include <linux/ioport.h>
>>> +#include <linux/jiffies.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/log2.h>
>>> +#include <linux/module.h>
>>> +#include <linux/mtd/mtd.h>
>>> +#include <linux/mtd/partitions.h>
>>> +#include <linux/mutex.h>
>>> +#include <linux/of.h>
>>> +#include <linux/of_address.h>
>>> +#include <linux/param.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/pm.h>
>>> +#include <linux/sched.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/wait.h>
>>> +
>>> +#include "altera_epcq.h"
>>> +
>>> +/* data structure to maintain flash ids from different vendors */
>>> +struct flash_device {
>>> +     char *name;
>>> +     bool is_epcs;
>>> +     u32 device_id;
>>> +     uint32_t sectorsize_inbytes;
>>> +     uint64_t size_inbytes;
>>> +     u32 pagesize;
>>> +};
>>> +
>>> +#define FLASH_ID(_n, _is_epcs, _id, _ssize, _size, _psize)   \
>>> +{                            \
>>> +     .name = (_n),           \
>>> +     .is_epcs = (_is_epcs),          \
>>> +     .device_id = (_id),     \
>>> +     .sectorsize_inbytes = (_ssize), \
>>> +     .size_inbytes = (_size),        \
>>> +     .pagesize = (_psize),   \
>>> +}
>>> +
>>> +static struct flash_device flash_devices[] = {
>>> +     FLASH_ID("epcq16"    , 0, 0x15, 0x10000, 0x200000, 0x100),
>>> +     FLASH_ID("epcq32"    , 0, 0x16, 0x10000, 0x400000, 0x100),
>>> +     FLASH_ID("epcq64"    , 0, 0x17, 0x10000, 0x800000, 0x100),
>>> +     FLASH_ID("epcq128"   , 0, 0x18, 0x10000, 0x1000000, 0x100),
>>> +     FLASH_ID("epcq256"   , 0, 0x19, 0x10000, 0x2000000, 0x100),
>>> +     FLASH_ID("epcq512"   , 0, 0x20, 0x10000, 0x4000000, 0x100),
>>> +
>>> +     FLASH_ID("epcs16"    , 1, 0x14, 0x10000, 0x200000, 0x100),
>>> +     FLASH_ID("epcs64"    , 1, 0x16, 0x10000, 0x800000, 0x100),
>>> +     FLASH_ID("epcs128"   , 1, 0x18, 0x40000, 0x1000000, 0x100),
>>> +};
>>> +
>>> +static inline struct altera_epcq_flash *get_flash_data(struct mtd_info *mtd)
>>> +{
>>> +     return container_of(mtd, struct altera_epcq_flash, mtd);
>>> +}
>>> +
>>> +static u32 altera_epcq_read_sr(struct altera_epcq *dev)
>>> +{
>>> +     return readl(dev->csr_base + EPCQ_SR_REG);
>>> +}
>>> +
>>> +static int altera_epcq_wait_till_ready(struct altera_epcq *dev)
>>> +{
>>> +     unsigned long finish;
>>> +     int status;
>>> +
>>> +     finish = jiffies + EPCQ_MAX_TIME_OUT;
>>> +     do {
>>> +             status = altera_epcq_read_sr(dev);
>>> +             if (status < 0)
>>> +                     return status;
>>> +             else if (!(status & EPCQ_SR_WIP))
>>> +                     return 0;
>>> +
>>> +             cond_resched();
>>> +     } while (!time_after_eq(jiffies, finish));
>>> +
>>> +     dev_err(&dev->pdev->dev, "epcq controller is busy, timeout\n");
>>> +     return -EBUSY;
>>> +}
>>> +
>>> +static int get_flash_index(u32 flash_id, bool is_epcs)
>>> +{
>>> +     int index;
>>> +
>>> +     for (index = 0; index < ARRAY_SIZE(flash_devices); index++) {
>>> +             if (flash_devices[index].device_id == flash_id &&
>>> +                 flash_devices[index].is_epcs == is_epcs)
>>> +                     return index;
>>> +     }
>>> +
>>> +     /* Memory chip is not listed and not supported */
>>> +     return -ENODEV;
>>> +}
>>> +
>>> +static int altera_epcq_write_erase_check(struct altera_epcq *dev,
>>> +                                      bool write_erase)
>>> +{
>>> +     u32 val;
>>> +     u32 mask;
>>> +
>>> +     if (write_erase)
>>> +             mask = EPCQ_ISR_ILLEGAL_WRITE_MASK;
>>> +     else
>>> +             mask = EPCQ_ISR_ILLEGAL_ERASE_MASK;
>>> +
>>> +     val = readl(dev->csr_base + EPCQ_ISR_REG);
>>> +     if (val & mask) {
>>> +             dev_err(&dev->pdev->dev,
>>> +                     "write/erase failed, sector might be protected\n");
>>> +             /*clear this status for next use*/
>>> +             writel(val, dev->csr_base + EPCQ_ISR_REG);
>>> +             return -EIO;
>>> +     }
>>> +     return 0;
>>> +}
>>> +
>>> +static int altera_epcq_erase_chip(struct mtd_info *mtd)
>>> +{
>>> +     int ret;
>>> +     u32 val;
>>> +     struct altera_epcq *dev = mtd->priv;
>>> +
>>> +     /* Wait until finished previous write command. */
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>
>> This pattern is pretty silly. We don't need to have every operation
>> check the status of the previous operation. Each operation should check
>> itself. You obviously copied this one... but we recently changed that:
>>
>>     commit dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3
>>     Author: Brian Norris <computersforpeace-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>>     Date:   Wed Aug 6 18:16:57 2014 -0700
>>
>>         mtd: spi-nor: move "wait-till-ready" checks into erase/write
>>         functions
>>
>>
>>     https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3
>>
>>> +     if (ret)
>>> +             return ret;
>>> +
>>> +     /* erase chip. */
>>> +     val = EPCQ_MEM_OP_BULK_ERASE_CMD;
>>> +     writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
>>> +
>>> +     /* Wait until finished previous write command. */
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>> +     if (ret)
>>> +             return ret;
>>> +
>>> +      /* check whether write triggered a illegal write interrupt */
>>> +     ret = altera_epcq_write_erase_check(dev, 0);
>>> +     if (ret < 0)
>>> +             return ret;
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static int altera_epcq_addr_to_sector(struct altera_epcq_flash *flash,
>>> +                                   int addr_offset)
>>> +{
>>> +     int sector = 0;
>>> +     int i;
>>> +
>>> +     for (i = 0; i < flash->num_sectors; i++) {
>>> +             if (addr_offset >= i * flash->mtd.erasesize && addr_offset <
>>> +                 (i * flash->mtd.erasesize + flash->mtd.erasesize)) {
>>> +                     sector = i;
>>> +                     return sector;
>>> +             }
>>> +     }
>>
>> Uh, really? Am I crazy, or shouldn't this whole function just be a
>> really simple arithmetic operation? Like:
>>
>>         return addr_offset >> mtd->erasesize_shift;
>>
>>> +     return -1;
>>> +}
>>> +
>>> +static int altera_epcq_erase_sector(struct mtd_info *mtd,
>>> +                                 int addr_offset)
>>> +{
>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>> +     struct altera_epcq *dev = mtd->priv;
>>> +     u32 val;
>>> +     int ret;
>>> +     int sector_value;
>>> +
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>> +     if (ret)
>>> +             return ret;
>>> +
>>> +     sector_value = altera_epcq_addr_to_sector(flash, addr_offset);
>>> +
>>> +     /* sanity check that block_offset is a valid sector number */
>>> +     if (sector_value < 0)
>>> +             return -EINVAL;
>>> +
>>> +     /* sector value should occupy bits 17:8 */
>>> +     val = (sector_value << 8) & EPCQ_MEM_OP_SECTOR_VALUE_MASK;
>>> +
>>> +     /* sector erase commands occupies lower 2 bits */
>>> +     val |= EPCQ_MEM_OP_SECTOR_ERASE_CMD;
>>> +
>>> +     /* write sector erase command to EPCQ_MEM_OP register*/
>>> +     writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
>>> +
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>> +     if (ret)
>>> +             return ret;
>>> +
>>> +      /* check whether write triggered a illegal write interrupt */
>>
>> ^^ extra space here? You have <TAB><SPACE>/* check [...]
>>
>>> +     ret = altera_epcq_write_erase_check(dev, 0);
>>> +     if (ret < 0)
>>> +             return ret;
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static int altera_epcq_erase(struct mtd_info *mtd, struct erase_info *e_info)
>>> +{
>>> +     u32 addr;
>>> +     int ret;
>>> +     u32 len;
>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>> +     struct altera_epcq *dev = mtd->priv;
>>> +
>>> +     addr = e_info->addr;
>>> +     len = e_info->len;
>>> +
>>> +     if (flash->bank > dev->num_flashes - 1) {
>>> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
>>> +             return -EINVAL;
>>> +     }
>>> +     mutex_lock(&flash->lock);
>>> +
>>> +     /*erase whole chip*/
>>> +     if (len == mtd->size) {
>>> +             if (altera_epcq_erase_chip(mtd)) {
>>> +                     e_info->state = MTD_ERASE_FAILED;
>>> +                     mutex_unlock(&flash->lock);
>>> +                     return -EIO;
>>> +             }
>>> +     /*"sector"-at-a-time erase*/
>>> +     } else {
>>> +             while (len) {
>>> +                     ret = altera_epcq_erase_sector(mtd, addr);
>>> +                     if (ret) {
>>> +                             e_info->state = MTD_ERASE_FAILED;
>>> +                             mutex_unlock(&flash->lock);
>>> +                             return -EIO;
>>> +                     }
>>> +                     addr += mtd->erasesize;
>>> +                     if (len < mtd->erasesize)
>>> +                             len = 0;
>>> +                     else
>>> +                             len -= mtd->erasesize;
>>> +             }
>>> +     }
>>> +     mutex_unlock(&flash->lock);
>>> +     e_info->state = MTD_ERASE_DONE;
>>> +     mtd_erase_callback(e_info);
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static int altera_epcq_read(struct mtd_info *mtd, loff_t from, size_t len,
>>> +                         size_t *retlen, u8 *buf)
>>> +{
>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>> +     struct altera_epcq *dev = mtd->priv;
>>> +     void *src;
>>> +     int ret = 0;
>>> +
>>> +     if (!flash || !dev)
>>> +             return -ENODEV;
>>
>> Why would either of these be 0? If they are, you have much bigger
>> problems, since you aren't checking these almost anywhere else. And the
>> !flash check is pretty odd, since get_flash_data() is using
>> container_of(), which is not likely to give you exactly 0...
>>
>>> +
>>> +     if (flash->bank > dev->num_flashes - 1) {
>>> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
>>> +             return -EINVAL;
>>> +     }
>>> +
>>> +     src = dev->data_base + from;
>>> +
>>> +     mutex_lock(&flash->lock);
>>> +     /* wait till previous write/erase is done. */
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>> +     if (ret)
>>> +             goto err;
>>> +
>>> +     memcpy_fromio(buf, (u8 *)src, len);
>>
>> Drop the cast. Also, you get sparse warnings because you're dropping the
>> __iomem. Why not just this?
>>
>>         memcpy_fromio(buf, dev->data_base + from, len);
>>
>> (BTW, you might consider running sparse on all your code. See
>> Documentation/sparse.txt. It tells you how to get it, but you can often
>> just download it through your distro's package manager.)
>>
>>> +     *retlen = len;
>>> +
>>> +err:
>>> +     mutex_unlock(&flash->lock);
>>> +     return ret;
>>> +}
>>> +
>>> +static int altera_epcq_write(struct mtd_info *mtd, loff_t to, size_t len,
>>> +                          size_t *retlen, const u8 *buf)
>>> +{
>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>> +     struct altera_epcq *dev = mtd->priv;
>>> +     void *dest;
>>> +     int ret = 0;
>>> +
>>> +     if (!flash || !dev)
>>> +             return -ENODEV;
>>
>> Also here. I don't think you need these checks.
>>
>>> +
>>> +     if (flash->bank > dev->num_flashes - 1) {
>>> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
>>> +             return -EINVAL;
>>> +     }
>>> +     dest = dev->data_base + to;
>>> +
>>> +     mutex_lock(&flash->lock);
>>> +
>>> +     /* wait until finished previous write command. */
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>> +     if (ret)
>>> +             goto err;
>>> +
>>> +     memcpy_toio(dest, buf, len);
>>
>> Similar. Why not just this?
>>
>>         memcpy_toio(dev->data_base + to, buf, len);
>>
>>> +     *retlen += len;
>>> +
>>> +     /* wait until finished previous write command. */
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>> +     if (ret)
>>> +             goto err;
>>> +
>>> +      /* check whether write triggered a illegal write interrupt */
>>> +     ret = altera_epcq_write_erase_check(dev, 1);
>>> +     if (ret < 0)
>>> +             goto err;
>>> +
>>> +err:
>>> +     mutex_unlock(&flash->lock);
>>> +     return ret;
>>> +}
>>> +
>>> +static int altera_epcq_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
>>> +{
>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>> +     struct altera_epcq *dev = mtd->priv;
>>> +     uint32_t offset = ofs;
>>> +     int ret = 0;
>>> +     u32 sector_start, sector_end;
>>> +     u32 num_sectors;
>>> +     u32 mem_op;
>>> +     unsigned sr_bp = 0;
>>> +     unsigned sr_tb = 0;
>>> +
>>> +     sector_start = altera_epcq_addr_to_sector(flash, offset);
>>> +     sector_end = altera_epcq_addr_to_sector(flash, offset + len);
>>> +     num_sectors = flash->num_sectors;
>>> +     dev_dbg(&dev->pdev->dev,
>>> +             "%s: num_setor is %u,sector start is %u,sector end is %u\n",
>>> +             __func__, num_sectors, sector_start, sector_end);
>>> +
>>> +     mutex_lock(&flash->lock);
>>> +     /* wait until finished previous write command. */
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>> +     if (ret)
>>> +             goto err;
>>> +
>>> +     if (sector_start >= num_sectors/2) {
>>
>> It's preferable to have spaces around operators. So 'num_sectors / 2'.
>> The same thing comes up in several other places.
>>
>>> +             if (sector_start < num_sectors-(num_sectors / 4))
>>> +                     sr_bp = __ilog2_u32(num_sectors);
>>> +             else if (sector_start < num_sectors-(num_sectors / 8))
>>> +                     sr_bp = __ilog2_u32(num_sectors) - 1;
>>> +             else if (sector_start < num_sectors-(num_sectors / 16))
>>> +                     sr_bp = __ilog2_u32(num_sectors) - 2;
>>> +             else if (sector_start < num_sectors-(num_sectors / 32))
>>> +                     sr_bp = __ilog2_u32(num_sectors) - 3;
>>> +             else if (sector_start < num_sectors-(num_sectors / 64))
>>> +                     sr_bp = __ilog2_u32(num_sectors) - 4;
>>> +             else if (sector_start < num_sectors-(num_sectors / 128))
>>> +                     sr_bp = __ilog2_u32(num_sectors) - 5;
>>> +             else if (sector_start < num_sectors-(num_sectors / 256))
>>> +                     sr_bp = __ilog2_u32(num_sectors) - 6;
>>> +             else if (sector_start < num_sectors-(num_sectors / 512))
>>> +                     sr_bp = __ilog2_u32(num_sectors) - 7;
>>> +             else if (sector_start < num_sectors-(num_sectors / 1024))
>>> +                     sr_bp = __ilog2_u32(num_sectors) - 8;
>>> +             else
>>> +                     sr_bp = 0;  /* non area protected */
>>
>> Yikes, that's ugly! And I'm not sure it matches the EPCQ doc I found.
>> I'm pretty sure you can rewrite this if/else-if/else block in about 1
>> line though.
>>
>>> +
>>> +             if (sr_bp < 0) {
>>
>> sr_bp is unsigned, so this is never true.
>>
>>> +                     dev_err(&dev->pdev->dev, "%s: address is out of range\n"
>>> +                             , __func__);
>>> +                     ret = -EINVAL;
>>> +                     goto err;
>>> +             }
>>> +             /*set TB = 0*/
>>> +             sr_tb = 0;
>>> +
>>> +     } else {
>>> +             if (sector_end < 1)
>>> +                     sr_bp = 1;
>>> +             else if (sector_end < 2)
>>> +                     sr_bp = 2;
>>> +             else if (sector_end < 4)
>>> +                     sr_bp = 3;
>>> +             else if (sector_end < 8)
>>> +                     sr_bp = 4;
>>> +             else if (sector_end < 16)
>>> +                     sr_bp = 5;
>>> +             else if (sector_end < 32)
>>> +                     sr_bp = 6;
>>> +             else if (sector_end < 64)
>>> +                     sr_bp = 7;
>>> +             else if (sector_end < 128)
>>> +                     sr_bp = 8;
>>> +             else if (sector_end < 256)
>>> +                     sr_bp = 9;
>>> +             else if (sector_end < 512)
>>> +                     sr_bp = 10;
>>> +             else
>>> +                     sr_bp = 16; /*protect all areas*/
>>
>> Again, this is ugly. How about this?
>>
>>         sr_bp = fls(sector_end) + 1;
>>
>>> +
>>> +             sr_tb = 1;
>>> +     }
>>> +
>>> +     mem_op = (sr_tb << 12) | (sr_bp << 8);
>>> +     mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
>>> +     mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
>>> +     writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
>>> +
>>> +err:
>>> +     mutex_unlock(&flash->lock);
>>> +     return ret;
>>> +}
>>> +
>>> +static int altera_epcq_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
>>> +{
>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>> +     struct altera_epcq *dev = mtd->priv;
>>> +
>>> +     int ret = 0;
>>> +     u32 mem_op;
>>> +
>>> +     mutex_lock(&flash->lock);
>>> +     /* wait until finished previous write command. */
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>> +     if (ret)
>>> +             goto err;
>>> +     dev_info(&dev->pdev->dev, "Unlock all protected area\n");
>>> +     mem_op = 0;
>>> +     mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
>>> +     mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
>>> +     writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
>>> +
>>> +err:
>>> +     mutex_unlock(&flash->lock);
>>> +     return ret;
>>> +}
>>> +
>>> +static void altera_epcq_chip_select(struct altera_epcq *dev, u32 bank)
>>> +{
>>> +     u32 val = 0;
>>> +
>>> +     switch (bank) {
>>> +     case 0:
>>> +             val = EPCQ_CHIP_SELECT_0;
>>> +             break;
>>> +     case 1:
>>> +             val = EPCQ_CHIP_SELECT_1;
>>> +             break;
>>> +     case 2:
>>> +             val = EPCQ_CHIP_SELECT_2;
>>> +             break;
>>> +     default:
>>> +             return;
>>> +     }
>>> +
>>> +     writel(val, dev->csr_base + EPCQ_CHIP_SELECT_REG);
>>> +}
>>> +
>>> +static int altera_epcq_probe_flash(struct altera_epcq *dev, u32 bank)
>>> +{
>>> +     int ret = 0;
>>> +     u32 val = 0;
>>> +
>>> +     mutex_lock(&dev->lock);
>>> +
>>> +     /*select bank*/
>>
>> Comment spacing, here and elsewhere:
>>
>>         /* select bank */
>>
>>> +     altera_epcq_chip_select(dev, bank);
>>> +
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>> +     if (ret)
>>> +             goto err;
>>> +
>>> +     /* get device sillicon id */
>>> +     if (dev->is_epcs)
>>> +             val = readl(dev->csr_base + EPCQ_SID_REG);
>>> +     else
>>> +             val = readl(dev->csr_base + EPCQ_RDID_REG);
>>> +
>>> +     /* get flash index based on the device list*/
>>> +     ret = get_flash_index(val, dev->is_epcs);
>>> +     return 0;
>>> +err:
>>> +     mutex_unlock(&dev->lock);
>>> +     return ret;
>>> +}
>>> +
>>> +static int altera_epcq_probe_config_dt(struct platform_device *pdev,
>>> +                                    struct device_node *np,
>>> +                                    struct altera_epcq_plat_data *pdata)
>>> +{
>>> +     struct device_node *pp = NULL;
>>> +     struct resource *epcq_res;
>>> +     int i = 0;
>>> +     u32 id;
>>> +
>>> +     pdata->is_epcs = of_property_read_bool(np, "is-epcs");
>>> +
>>> +     epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
>>> +                                             "csr_base");
>>> +     if (!epcq_res) {
>>
>> devm_ioremap_resource() will check the that 'epcq_res' is valid, so you
>> don't need this whole 'if' block.
>>
>>> +             dev_err(&pdev->dev, "resource csr base is not defined\n");
>>> +             return -ENODEV;
>>> +     }
>>> +
>>> +     pdata->csr_base = devm_ioremap_resource(&pdev->dev, epcq_res);
>>> +     if (IS_ERR(pdata->csr_base)) {
>>> +             dev_err(&pdev->dev, "%s: ERROR: failed to map csr base\n",
>>> +                     __func__);
>>> +             return PTR_ERR(pdata->csr_base);
>>> +     }
>>> +
>>> +     epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
>>> +                                             "data_base");
>>> +     if (!epcq_res) {
>>
>> Same here.
>>
>>> +             dev_err(&pdev->dev, "resource data base is not defined\n");
>>> +             return -ENODEV;
>>> +     }
>>> +
>>> +     pdata->data_base = devm_ioremap_resource(&pdev->dev, epcq_res);
>>> +     if (IS_ERR(pdata->data_base)) {
>>> +             dev_err(&pdev->dev, "%s: ERROR: failed to map data base\n",
>>> +                     __func__);
>>> +             return PTR_ERR(pdata->data_base);
>>> +     }
>>> +
>>> +     pdata->board_flash_info = devm_kzalloc(&pdev->dev,
>>> +                                            sizeof(*pdata->board_flash_info),
>>> +                                            GFP_KERNEL);
>>> +
>>> +     /* Fill structs for each subnode (flash device) */
>>> +     while ((pp = of_get_next_child(np, pp))) {
>>
>> for_each_available_child_of_node()?
>>
>>> +             struct altera_epcq_flash_info *flash_info;
>>> +
>>> +             flash_info = &pdata->board_flash_info[i];
>>
>> This is never used. Either use it below, or drop the local variable.
>>
>>> +             pdata->np[i] = pp;
>>> +
>>> +             /* Read bank id from DT */
>>> +             if (of_get_property(pp, "reg", &id))
>>
>> Is this property optional? Your DT binding doc doesn't make it clear,
>> but it seems like a property which would be wise to require (i.e., not
>> optional).
>>
>>> +                     pdata->board_flash_info[i].bank = id;
>>> +             i++;
>>> +     }
>>> +     pdata->num_flashes = i;
>>> +     return 0;
>>> +}
>>> +
>>> +static int altera_epcq_setup_banks(struct platform_device *pdev,
>>> +                                u32 bank, struct device_node *np,
>>> +                                struct altera_epcq_plat_data *pdata)
>>> +{
>>> +     struct altera_epcq *dev = platform_get_drvdata(pdev);
>>> +     struct mtd_part_parser_data ppdata = {};
>>> +     struct altera_epcq_flash_info *flash_info;
>>> +     struct altera_epcq_flash *flash;
>>> +     struct mtd_partition *parts = NULL;
>>
>> Drop this variable. Just use NULL directly below.
>>
>>> +     int count = 0;
>>
>> Same. Just use 0 below.
>>
>>> +     int flash_index;
>>> +     int ret = 0;
>>> +     uint64_t size;
>>> +     uint32_t sector_size;
>>> +
>>> +     flash_info = &pdata->board_flash_info[bank];
>>> +     if (!flash_info)
>>> +             return -ENODEV;
>>> +
>>> +     if (bank > pdata->num_flashes - 1)
>>> +             return -EINVAL;
>>> +
>>> +     flash = devm_kzalloc(&pdev->dev, sizeof(*flash), GFP_KERNEL);
>>> +     if (!flash)
>>> +             return -ENOMEM;
>>> +     flash->bank = bank;
>>> +
>>> +     mutex_init(&flash->lock);
>>> +
>>> +     /* verify whether flash is really present on board */
>>> +     flash_index = altera_epcq_probe_flash(dev, bank);
>>> +     if (flash_index < 0) {
>>> +             dev_info(&dev->pdev->dev, "epcq:flash%d not found\n",
>>> +                      flash->bank);
>>> +             return flash_index;
>>> +     }
>>> +
>>> +     dev->flash[bank] = flash;
>>> +
>>> +     size = flash_devices[flash_index].size_inbytes;
>>> +     sector_size = flash_devices[flash_index].sectorsize_inbytes;
>>> +     /*use do_div instead of plain div to avoid linker err*/
>>> +     do_div(size, sector_size);
>>> +     flash->num_sectors = size;
>>> +
>>> +     /*mtd framework */
>>> +     flash->mtd.priv = dev;
>>> +     flash->mtd.name = flash_devices[flash_index].name;
>>> +     flash->mtd.type = MTD_NORFLASH;
>>> +     flash->mtd.writesize = 1;
>>> +     flash->mtd.flags = MTD_CAP_NORFLASH;
>>> +     flash->mtd.size = flash_devices[flash_index].size_inbytes;
>>> +     flash->mtd.erasesize = flash_devices[flash_index].sectorsize_inbytes;
>>> +     flash->mtd._erase = altera_epcq_erase;
>>> +     flash->mtd._read = altera_epcq_read;
>>> +     flash->mtd._write = altera_epcq_write;
>>> +     flash->mtd._lock = altera_epcq_lock;
>>> +     flash->mtd._unlock = altera_epcq_unlock;
>>> +
>>> +     dev_info(&pdev->dev, "mtd .name=%s .size=0x%llx (%lluM)\n",
>>> +              flash->mtd.name, (long long)flash->mtd.size,
>>> +              (long long)(flash->mtd.size >> 20));
>>> +
>>> +     dev_info(&pdev->dev, ".erasesize = 0x%x(%uK)\n",
>>> +              flash->mtd.erasesize, flash->mtd.erasesize >> 10);
>>> +
>>> +     ppdata.of_node = np;
>>> +
>>> +     ret = mtd_device_parse_register(&flash->mtd, NULL, &ppdata, parts,
>>> +                                     count);
>>> +     if (ret) {
>>> +             dev_err(&pdev->dev, "Err MTD partition=%d\n", ret);
>>> +             goto err;
>>
>> You don't want to unregister a MTD that failed to register. The MTD core
>> code should handle that itself.
>>
>> Instead of this if (ret) {} block, I'd just make this:
>>
>>         return mtd_device_parse_register(&flash->mtd, NULL, &ppdata, NULL, 0);
>>
>>> +     }
>>> +
>>> +     return 0;
>>> +
>>> +err:
>>> +     mtd_device_unregister(&flash->mtd);
>>> +     return ret;
>>> +}
>>> +
>>> +static int altera_epcq_probe(struct platform_device *pdev)
>>> +{
>>> +     struct device_node *np = pdev->dev.of_node;
>>> +     struct altera_epcq_plat_data *pdata = NULL;
>>> +     struct altera_epcq *dev;
>>> +     int ret = 0;
>>> +     int i;
>>> +
>>> +     if (!np) {
>>> +             ret = -ENODEV;
>>> +             dev_err(&pdev->dev, "no device found\n");
>>> +             goto err;
>>> +     }
>>> +
>>> +     pdata = devm_kzalloc(&pdev->dev,
>>> +                          sizeof(struct altera_epcq_plat_data),
>>> +                          GFP_KERNEL);
>>> +
>>> +     if (!pdata) {
>>> +             ret = -ENOMEM;
>>> +             dev_err(&pdev->dev, "%s: ERROR: no memory\n", __func__);
>>
>> Unnecessary print. The MM code will print plenty of info if you're
>> out of memory.
>>
>>> +             goto err;
>>> +     }
>>> +     ret = altera_epcq_probe_config_dt(pdev, np, pdata);
>>> +     if (ret) {
>>> +             ret = -ENODEV;
>>> +             dev_err(&pdev->dev, "probe fail\n");
>>> +             goto err;
>>> +     }
>>> +
>>> +     dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
>>> +     if (!dev) {
>>> +             ret = -ENOMEM;
>>> +             dev_err(&pdev->dev, "mem alloc fail\n");
>>
>> Same here.
>>
>>> +             goto err;
>>> +     }
>>> +     mutex_init(&dev->lock);
>>> +     dev->pdev = pdev;
>>> +     dev->is_epcs = pdata->is_epcs;
>>> +     dev->csr_base = pdata->csr_base;
>>> +     dev->data_base = pdata->data_base;
>>> +
>>> +     /*check number of flashes*/
>>> +     dev->num_flashes = pdata->num_flashes;
>>> +     if (dev->num_flashes > MAX_NUM_FLASH_CHIP) {
>>> +             dev_err(&pdev->dev, "exceeding max number of flashes\n");
>>> +             dev->num_flashes = MAX_NUM_FLASH_CHIP;
>>> +     }
>>> +
>>> +     /* check clock*/
>>> +     dev->clk = devm_clk_get(&pdev->dev, NULL);
>>> +     if (IS_ERR(dev->clk)) {
>>> +             ret = PTR_ERR(dev->clk);
>>> +             goto err;
>>> +     }
>>> +     ret = clk_prepare_enable(dev->clk);
>>> +     if (ret)
>>> +             goto err;
>>> +
>>> +     platform_set_drvdata(pdev, dev);
>>> +
>>> +     /* loop for each serial flash which is connected to epcq */
>>> +     for (i = 0; i < dev->num_flashes; i++) {
>>> +             ret = altera_epcq_setup_banks(pdev, i, pdata->np[i], pdata);
>>> +             if (ret) {
>>> +                     dev_err(&pdev->dev, "bank setup failed\n");
>>> +                     goto err_bank_setup;
>>> +             }
>>> +     }
>>> +
>>> +     return 0;
>>> +
>>> +err_bank_setup:
>>> +     clk_disable_unprepare(dev->clk);
>>> +err:
>>> +     return ret;
>>> +}
>>> +
>>> +static int altera_epcq_remove(struct platform_device *pdev)
>>> +{
>>> +     struct altera_epcq *dev;
>>> +     struct altera_epcq_flash *flash;
>>> +     int ret, i;
>>> +
>>> +     dev = platform_get_drvdata(pdev);
>>> +
>>> +     /* clean up for all nor flash */
>>> +     for (i = 0; i < dev->num_flashes; i++) {
>>> +             flash = dev->flash[i];
>>> +             if (!flash)
>>> +                     continue;
>>> +
>>> +             /* clean up mtd stuff */
>>> +             ret = mtd_device_unregister(&flash->mtd);
>>> +             if (ret)
>>> +                     dev_err(&pdev->dev, "error removing mtd\n");
>>> +     }
>>> +
>>> +     clk_disable_unprepare(dev->clk);
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +#ifdef CONFIG_PM
>>
>> Make this CONFIG_PM_SLEEP.
>>
>>> +static int altera_epcq_suspend(struct device *dev)
>>> +{
>>> +     struct altera_epcq *sdev = dev_get_drvdata(dev);
>>> +
>>> +     clk_disable_unprepare(sdev->clk);
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static int altera_epcq_resume(struct device *dev)
>>> +{
>>> +     struct altera_epcq *sdev = dev_get_drvdata(dev);
>>> +     int ret = -EPERM;
>>
>> Drop 'ret'.
>>
>>> +
>>> +     ret = clk_prepare_enable(sdev->clk);
>>
>> Just:
>>
>>         return clk_prepare_enable(sdev->clk);
>>
>>> +
>>> +     return ret;
>>> +}
>>> +
>>> +static SIMPLE_DEV_PM_OPS(altera_epcq_pm_ops, altera_epcq_suspend,
>>> +                      altera_epcq_resume);
>>> +#endif
>>> +
>>> +static const struct of_device_id altera_epcq_id_table[] = {
>>> +     { .compatible = "altr,epcq-1.0" },
>>> +     {}
>>> +};
>>> +MODULE_DEVICE_TABLE(of, altera_epcq_id_table);
>>> +
>>> +static struct platform_driver altera_epcq_driver = {
>>> +     .driver = {
>>> +             .name = ALTERA_EPCQ_RESOURCE_NAME,
>>> +             .bus = &platform_bus_type,
>>> +             .owner = THIS_MODULE,
>>
>> The .owner assignment is no longer necessary.
>>
>>> +             .of_match_table = altera_epcq_id_table,
>>> +#ifdef CONFIG_PM
>>
>> Also CONFIG_PM_SLEEP.
>>
>>> +             .pm = &altera_epcq_pm_ops,
>>> +#endif
>>> +     },
>>> +     .probe = altera_epcq_probe,
>>> +     .remove = altera_epcq_remove,
>>> +};
>>> +module_platform_driver(altera_epcq_driver);
>>> +
>>> +MODULE_AUTHOR("Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org>");
>>> +MODULE_DESCRIPTION("MTD Altera EPCQ Driver");
>>> +MODULE_LICENSE("GPL v2");
>>> diff --git a/drivers/mtd/devices/altera_epcq.h b/drivers/mtd/devices/altera_epcq.h
>>> new file mode 100644
>>> index 0000000..c3d15e5
>>> --- /dev/null
>>> +++ b/drivers/mtd/devices/altera_epcq.h
>>> @@ -0,0 +1,130 @@
>>> +/*
>>> + * Copyright (C) 2014 Altera Corporation. All rights reserved
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms and conditions of the GNU General Public License,
>>> + * version 2, as published by the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope 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.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along with
>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>> + */
>>> +
>>> +#ifndef __ALTERA_ECPQ_H
>>> +#define __ALTERA_ECPQ_H
>>> +
>>> +#define ALTERA_EPCQ_RESOURCE_NAME        "altera_epcq"
>>> +/* max possible slots for serial flash chip in the EPCQ controller */
>>> +#define MAX_NUM_FLASH_CHIP   3
>>> +
>>> +/* macro to define partitions for flash devices */
>>> +#define DEFINE_PARTS(n, of, s)               \
>>
>> This macro is not used anywhere. Drop it?
>>
>>> +{                                    \
>>> +     .name = n,                      \
>>> +     .offset = of,                   \
>>> +     .size = s,                      \
>>> +}
>>> +
>>> +struct altera_epcq_flash_info {
>>> +     u32 bank;
>>> +     struct mtd_partition *partitions;
>>> +     int nr_partitions;
>>> +};
>>> +
>>> +struct altera_epcq_plat_data {
>>> +     void __iomem *csr_base;
>>> +     void __iomem *data_base;
>>> +     bool is_epcs;
>>> +     u32 num_flashes;
>>> +     struct altera_epcq_flash_info *board_flash_info;
>>> +     struct device_node *np[MAX_NUM_FLASH_CHIP];
>>> +};
>>> +
>>> +struct altera_epcq {
>>> +     struct clk *clk;
>>> +     bool is_epcs;
>>> +     struct mutex lock;      /*device lock*/
>>> +     void __iomem *csr_base;
>>> +     void __iomem *data_base;
>>> +     struct platform_device *pdev;
>>> +     u32 num_flashes;
>>> +     struct altera_epcq_flash *flash[MAX_NUM_FLASH_CHIP];
>>> +};
>>> +
>>> +struct altera_epcq_flash {
>>> +     u32 bank;
>>> +     u32 num_sectors;
>>> +     u32 num_parts;
>>> +     struct mtd_partition *parts;
>>> +     struct mutex lock;      /*flash lock*/
>>> +     struct mtd_info mtd;
>>> +};
>>> +
>>> +/* Define max times to check status register before we give up. */
>>> +#define EPCQ_MAX_TIME_OUT                    (40 * HZ)
>>> +
>>> +/* defines for status register */
>>> +#define EPCQ_SR_REG                          0x0
>>> +#define EPCQ_SR_WIP_MASK                     0x00000001
>>> +#define EPCQ_SR_WIP                          0x1
>>> +#define EPCQ_SR_WEL                          0x2
>>> +#define EPCQ_SR_BP0                          0x4
>>> +#define EPCQ_SR_BP1                          0x8
>>> +#define EPCQ_SR_BP2                          0x10
>>> +#define EPCQ_SR_BP3                          0x40
>>> +#define EPCQ_SR_TB                           0x20
>>> +
>>> +/* defines for device id register */
>>> +#define EPCQ_SID_REG                         0x4
>>> +#define EPCQ_RDID_REG                                0x8
>>> +#define EPCQ_RDID_MASK                               0x000000FF
>>> +/*
>>> + * EPCQ_MEM_OP register offset
>>> + *
>>> + * The EPCQ_MEM_OP register is used to do memory protect and erase operations
>>> + *
>>> + */
>>> +#define EPCQ_MEM_OP_REG                              0xC
>>> +
>>> +#define EPCQ_MEM_OP_CMD_MASK                 0x00000003
>>> +#define EPCQ_MEM_OP_BULK_ERASE_CMD           0x00000001
>>> +#define EPCQ_MEM_OP_SECTOR_ERASE_CMD         0x00000002
>>> +#define EPCQ_MEM_OP_SECTOR_PROTECT_CMD               0x00000003
>>> +#define EPCQ_MEM_OP_SECTOR_VALUE_MASK                0x0003FF00
>>> +#define EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK        0x00001F00
>>> +#define EPCQ_MEM_OP_SECTOR_PROTECT_SHIFT     8
>>> +/*
>>> + * EPCQ_ISR register offset
>>> + *
>>> + * The EPCQ_ISR register is used to determine whether an invalid write or erase
>>> + * operation trigerred an interrupt
>>> + *
>>> + */
>>> +#define EPCQ_ISR_REG                         0x10
>>> +
>>> +#define EPCQ_ISR_ILLEGAL_ERASE_MASK          0x00000001
>>> +#define EPCQ_ISR_ILLEGAL_WRITE_MASK          0x00000002
>>> +
>>> +/*
>>> + * EPCQ_IMR register offset
>>> + *
>>> + * The EPCQ_IMR register is used to mask the invalid erase or the invalid write
>>> + * interrupts.
>>> + *
>>> + */
>>> +#define EPCQ_IMR_REG                         0x14
>>> +#define EPCQ_IMR_ILLEGAL_ERASE_MASK          0x00000001
>>> +
>>> +#define EPCQ_IMR_ILLEGAL_WRITE_MASK          0x00000002
>>> +
>>> +#define EPCQ_CHIP_SELECT_REG                 0x18
>>> +#define EPCQ_CHIP_SELECT_MASK                        0x00000007
>>> +#define EPCQ_CHIP_SELECT_0                   0x00000001
>>> +#define EPCQ_CHIP_SELECT_1                   0x00000002
>>> +#define EPCQ_CHIP_SELECT_2                   0x00000004
>>> +
>>> +#endif /* __ALTERA_ECPQ_H */
>>
>> Brian
--
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	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-01-20  2:05   ` Viet Nga Dao
  0 siblings, 0 replies; 39+ messages in thread
From: Viet Nga Dao @ 2015-01-20  2:05 UTC (permalink / raw)
  To: Viet Nga Dao
  Cc: devicetree, linux-kernel, linux-mtd, Brian Norris,
	David Woodhouse, nga_chi86

On Thu, Jan 15, 2015 at 5:27 PM, Viet Nga Dao <vndao@altera.com> wrote:
> On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
> <computersforpeace@gmail.com> wrote:
>> On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao@altera.com wrote:
>>> From: Viet Nga Dao <vndao@altera.com>
>>>
>>> Altera EPCQ Controller is a soft IP which enables access to Altera EPCQ and
>>> EPCS flash chips. This patch adds driver for these devices.
>>>
>>> Signed-off-by: Viet Nga Dao <vndao@altera.com>
>>
>> This drivers seems awfully similar to (and so I infer it is likely
>> copy-and-pasted from) m25p80.c / spi-nor.c. Do you think it can be
>> rewritten as a SPI NOR driver, under drivers/mtd/spi-nor/ ? It looks
>> like these flash share most (all?) the same basic opcodes.
>>
> For Altera EPCQ flashes, almost operations are performed underline
> hardware. Software only able to perform the following through
> registers:
>  -  read status register
>  -  read id
>  -  write status registers bit SR_BP0,SR_BP1, SR_BP2,SR_BP3, SR_TB
> (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf)
> For read/write data: all the operations like QUAD_READ/WRITE,
> FAST_READ/WRITE are handled by hardware as well. From software point
> of view, there is no difference between these 2 modes.
> That is why if rewrite the drivers to follow spi-nor structure, it
> will require extra decoding works for the only few used opcodes.
>
Is it OK to remain this driver structure?
>>> ---
>>>  .../devicetree/bindings/mtd/altera_epcq.txt        |   45 ++
>>>  drivers/mtd/devices/Kconfig                        |   12 +
>>>  drivers/mtd/devices/Makefile                       |    2 +-
>>>  drivers/mtd/devices/altera_epcq.c                  |  804 ++++++++++++++++++++
>>>  drivers/mtd/devices/altera_epcq.h                  |  130 ++++
>>>  5 files changed, 992 insertions(+), 1 deletions(-)
>>>  create mode 100644 Documentation/devicetree/bindings/mtd/altera_epcq.txt
>>>  create mode 100644 drivers/mtd/devices/altera_epcq.c
>>>  create mode 100644 drivers/mtd/devices/altera_epcq.h
>>>
>>> diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>>> new file mode 100644
>>> index 0000000..d14f50e
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>>> @@ -0,0 +1,45 @@
>>> +* MTD Altera EPCQ driver
>>> +
>>> +Required properties:
>>> +- compatible: Should be "altr,epcq-1.0"
>>> +- reg: Address and length of the register set  for the device. It contains
>>> +  the information of registers in the same order as described by reg-names
>>> +- reg-names: Should contain the reg names
>>> +  "csr_base": Should contain the register configuration base address
>>> +  "data_base": Should contain the data base address
>>> +- is-epcs: boolean type.
>>> +             If present, the device contains EPCS flashes.
>>> +             Otherwise, it contains EPCQ flashes.
>>> +- #address-cells: Must be <1>.
>>> +- #size-cells: Must be <0>.
>>> +- flash device tree subnode, there must be a node with the following fields:
>>
>> These subnodes definitely require a 'compatible' string. Perhaps they
>> should be used to differentiate EPCS vs. EPCQ. Does "is-epcs" really
>> need to be in the top-level controller node?
>>
>>> +     - reg: Should contain the flash id
>>
>> Should, or must? (This question is relevant, because you seem to make it
>> optional in your code.) And what does the "flash ID" mean? It seems like
>> you're using as a chip-select or bank index.
>>
>>> +     - #address-cells: please refer to /mtd/partition.txt
>>> +     - #size-cells: please refer to /mtd/partition.txt
>>> +     For partitions inside each flash, please refer to /mtd/partition.txt
>>> +
>>> +Example:
>>> +
>>> +                     epcq_controller_0: epcq@0x000000000 {
>>> +                             compatible = "altr,epcq-1.0";
>>> +                             reg = <0x00000001 0x00000000 0x00000020>,
>>> +                                     <0x00000000 0x00000000 0x02000000>;
>>> +                             reg-names = "csr_base", "data_base";
>>> +                             #address-cells = <1>;
>>> +                             #size-cells = <0>;
>>> +                             flash0: epcq256@0 {
>>> +                                     reg = <0>;
>>> +                                     #address-cells = <1>;
>>> +                                     #size-cells = <1>;
>>> +                                     partition@0 {
>>> +                                             /* 16 MB for raw data. */
>>> +                                             label = "EPCQ Flash 0 raw data";
>>> +                                             reg = <0x0 0x1000000>;
>>> +                                     };
>>> +                                     partition@1000000 {
>>> +                                             /* 16 MB for jffs2 data. */
>>> +                                             label = "EPCQ Flash 0 JFFS 2";
>>> +                                             reg = <0x1000000 0x1000000>;
>>> +                                     };
>>> +                             };
>>> +                     }; //end epcq@0x000000000 (epcq_controller_0)
>>> diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
>>> index c49d0b1..020b864 100644
>>> --- a/drivers/mtd/devices/Kconfig
>>> +++ b/drivers/mtd/devices/Kconfig
>>> @@ -218,6 +218,18 @@ config MTD_ST_SPI_FSM
>>>         SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
>>>         for a subset of connected Serial Flash devices.
>>>
>>> +config MTD_ALTERA_EPCQ
>>> +     tristate "Support Altera EPCQ/EPCS Flash chips"
>>> +     depends on OF
>>> +     help
>>> +       This enables access to Altera EPCQ/EPCS flash chips, used for data
>>> +       storage. See the driver source for the current list,
>>> +       or to add other chips.
>>> +
>>> +       If you want to compile this driver as a module ( = code which can be
>>> +       inserted in and removed from the running kernel whenever you want),
>>> +       say M here and read <file:Documentation/kbuild/modules.txt>.
>>> +
>>>  if MTD_DOCG3
>>>  config BCH_CONST_M
>>>       default 14
>>> diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
>>> index f0b0e61..b429c4d 100644
>>> --- a/drivers/mtd/devices/Makefile
>>> +++ b/drivers/mtd/devices/Makefile
>>> @@ -16,6 +16,6 @@ obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
>>>  obj-$(CONFIG_MTD_SST25L)     += sst25l.o
>>>  obj-$(CONFIG_MTD_BCM47XXSFLASH)      += bcm47xxsflash.o
>>>  obj-$(CONFIG_MTD_ST_SPI_FSM)    += st_spi_fsm.o
>>> -
>>> +obj-$(CONFIG_MTD_ALTERA_EPCQ)                += altera_epcq.o
>>>
>>>  CFLAGS_docg3.o                       += -I$(src)
>>> diff --git a/drivers/mtd/devices/altera_epcq.c b/drivers/mtd/devices/altera_epcq.c
>>> new file mode 100644
>>> index 0000000..09213d5
>>> --- /dev/null
>>> +++ b/drivers/mtd/devices/altera_epcq.c
>>> @@ -0,0 +1,804 @@
>>> +/*
>>> + * Copyright (C) 2014 Altera Corporation. All rights reserved
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms and conditions of the GNU General Public License,
>>> + * version 2, as published by the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope 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.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along with
>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>> + */
>>> +
>>> +#include <linux/clk.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/device.h>
>>> +#include <linux/err.h>
>>> +#include <linux/errno.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/io.h>
>>> +#include <linux/ioport.h>
>>> +#include <linux/jiffies.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/log2.h>
>>> +#include <linux/module.h>
>>> +#include <linux/mtd/mtd.h>
>>> +#include <linux/mtd/partitions.h>
>>> +#include <linux/mutex.h>
>>> +#include <linux/of.h>
>>> +#include <linux/of_address.h>
>>> +#include <linux/param.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/pm.h>
>>> +#include <linux/sched.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/wait.h>
>>> +
>>> +#include "altera_epcq.h"
>>> +
>>> +/* data structure to maintain flash ids from different vendors */
>>> +struct flash_device {
>>> +     char *name;
>>> +     bool is_epcs;
>>> +     u32 device_id;
>>> +     uint32_t sectorsize_inbytes;
>>> +     uint64_t size_inbytes;
>>> +     u32 pagesize;
>>> +};
>>> +
>>> +#define FLASH_ID(_n, _is_epcs, _id, _ssize, _size, _psize)   \
>>> +{                            \
>>> +     .name = (_n),           \
>>> +     .is_epcs = (_is_epcs),          \
>>> +     .device_id = (_id),     \
>>> +     .sectorsize_inbytes = (_ssize), \
>>> +     .size_inbytes = (_size),        \
>>> +     .pagesize = (_psize),   \
>>> +}
>>> +
>>> +static struct flash_device flash_devices[] = {
>>> +     FLASH_ID("epcq16"    , 0, 0x15, 0x10000, 0x200000, 0x100),
>>> +     FLASH_ID("epcq32"    , 0, 0x16, 0x10000, 0x400000, 0x100),
>>> +     FLASH_ID("epcq64"    , 0, 0x17, 0x10000, 0x800000, 0x100),
>>> +     FLASH_ID("epcq128"   , 0, 0x18, 0x10000, 0x1000000, 0x100),
>>> +     FLASH_ID("epcq256"   , 0, 0x19, 0x10000, 0x2000000, 0x100),
>>> +     FLASH_ID("epcq512"   , 0, 0x20, 0x10000, 0x4000000, 0x100),
>>> +
>>> +     FLASH_ID("epcs16"    , 1, 0x14, 0x10000, 0x200000, 0x100),
>>> +     FLASH_ID("epcs64"    , 1, 0x16, 0x10000, 0x800000, 0x100),
>>> +     FLASH_ID("epcs128"   , 1, 0x18, 0x40000, 0x1000000, 0x100),
>>> +};
>>> +
>>> +static inline struct altera_epcq_flash *get_flash_data(struct mtd_info *mtd)
>>> +{
>>> +     return container_of(mtd, struct altera_epcq_flash, mtd);
>>> +}
>>> +
>>> +static u32 altera_epcq_read_sr(struct altera_epcq *dev)
>>> +{
>>> +     return readl(dev->csr_base + EPCQ_SR_REG);
>>> +}
>>> +
>>> +static int altera_epcq_wait_till_ready(struct altera_epcq *dev)
>>> +{
>>> +     unsigned long finish;
>>> +     int status;
>>> +
>>> +     finish = jiffies + EPCQ_MAX_TIME_OUT;
>>> +     do {
>>> +             status = altera_epcq_read_sr(dev);
>>> +             if (status < 0)
>>> +                     return status;
>>> +             else if (!(status & EPCQ_SR_WIP))
>>> +                     return 0;
>>> +
>>> +             cond_resched();
>>> +     } while (!time_after_eq(jiffies, finish));
>>> +
>>> +     dev_err(&dev->pdev->dev, "epcq controller is busy, timeout\n");
>>> +     return -EBUSY;
>>> +}
>>> +
>>> +static int get_flash_index(u32 flash_id, bool is_epcs)
>>> +{
>>> +     int index;
>>> +
>>> +     for (index = 0; index < ARRAY_SIZE(flash_devices); index++) {
>>> +             if (flash_devices[index].device_id == flash_id &&
>>> +                 flash_devices[index].is_epcs == is_epcs)
>>> +                     return index;
>>> +     }
>>> +
>>> +     /* Memory chip is not listed and not supported */
>>> +     return -ENODEV;
>>> +}
>>> +
>>> +static int altera_epcq_write_erase_check(struct altera_epcq *dev,
>>> +                                      bool write_erase)
>>> +{
>>> +     u32 val;
>>> +     u32 mask;
>>> +
>>> +     if (write_erase)
>>> +             mask = EPCQ_ISR_ILLEGAL_WRITE_MASK;
>>> +     else
>>> +             mask = EPCQ_ISR_ILLEGAL_ERASE_MASK;
>>> +
>>> +     val = readl(dev->csr_base + EPCQ_ISR_REG);
>>> +     if (val & mask) {
>>> +             dev_err(&dev->pdev->dev,
>>> +                     "write/erase failed, sector might be protected\n");
>>> +             /*clear this status for next use*/
>>> +             writel(val, dev->csr_base + EPCQ_ISR_REG);
>>> +             return -EIO;
>>> +     }
>>> +     return 0;
>>> +}
>>> +
>>> +static int altera_epcq_erase_chip(struct mtd_info *mtd)
>>> +{
>>> +     int ret;
>>> +     u32 val;
>>> +     struct altera_epcq *dev = mtd->priv;
>>> +
>>> +     /* Wait until finished previous write command. */
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>
>> This pattern is pretty silly. We don't need to have every operation
>> check the status of the previous operation. Each operation should check
>> itself. You obviously copied this one... but we recently changed that:
>>
>>     commit dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3
>>     Author: Brian Norris <computersforpeace@gmail.com>
>>     Date:   Wed Aug 6 18:16:57 2014 -0700
>>
>>         mtd: spi-nor: move "wait-till-ready" checks into erase/write
>>         functions
>>
>>
>>     https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3
>>
>>> +     if (ret)
>>> +             return ret;
>>> +
>>> +     /* erase chip. */
>>> +     val = EPCQ_MEM_OP_BULK_ERASE_CMD;
>>> +     writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
>>> +
>>> +     /* Wait until finished previous write command. */
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>> +     if (ret)
>>> +             return ret;
>>> +
>>> +      /* check whether write triggered a illegal write interrupt */
>>> +     ret = altera_epcq_write_erase_check(dev, 0);
>>> +     if (ret < 0)
>>> +             return ret;
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static int altera_epcq_addr_to_sector(struct altera_epcq_flash *flash,
>>> +                                   int addr_offset)
>>> +{
>>> +     int sector = 0;
>>> +     int i;
>>> +
>>> +     for (i = 0; i < flash->num_sectors; i++) {
>>> +             if (addr_offset >= i * flash->mtd.erasesize && addr_offset <
>>> +                 (i * flash->mtd.erasesize + flash->mtd.erasesize)) {
>>> +                     sector = i;
>>> +                     return sector;
>>> +             }
>>> +     }
>>
>> Uh, really? Am I crazy, or shouldn't this whole function just be a
>> really simple arithmetic operation? Like:
>>
>>         return addr_offset >> mtd->erasesize_shift;
>>
>>> +     return -1;
>>> +}
>>> +
>>> +static int altera_epcq_erase_sector(struct mtd_info *mtd,
>>> +                                 int addr_offset)
>>> +{
>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>> +     struct altera_epcq *dev = mtd->priv;
>>> +     u32 val;
>>> +     int ret;
>>> +     int sector_value;
>>> +
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>> +     if (ret)
>>> +             return ret;
>>> +
>>> +     sector_value = altera_epcq_addr_to_sector(flash, addr_offset);
>>> +
>>> +     /* sanity check that block_offset is a valid sector number */
>>> +     if (sector_value < 0)
>>> +             return -EINVAL;
>>> +
>>> +     /* sector value should occupy bits 17:8 */
>>> +     val = (sector_value << 8) & EPCQ_MEM_OP_SECTOR_VALUE_MASK;
>>> +
>>> +     /* sector erase commands occupies lower 2 bits */
>>> +     val |= EPCQ_MEM_OP_SECTOR_ERASE_CMD;
>>> +
>>> +     /* write sector erase command to EPCQ_MEM_OP register*/
>>> +     writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
>>> +
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>> +     if (ret)
>>> +             return ret;
>>> +
>>> +      /* check whether write triggered a illegal write interrupt */
>>
>> ^^ extra space here? You have <TAB><SPACE>/* check [...]
>>
>>> +     ret = altera_epcq_write_erase_check(dev, 0);
>>> +     if (ret < 0)
>>> +             return ret;
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static int altera_epcq_erase(struct mtd_info *mtd, struct erase_info *e_info)
>>> +{
>>> +     u32 addr;
>>> +     int ret;
>>> +     u32 len;
>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>> +     struct altera_epcq *dev = mtd->priv;
>>> +
>>> +     addr = e_info->addr;
>>> +     len = e_info->len;
>>> +
>>> +     if (flash->bank > dev->num_flashes - 1) {
>>> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
>>> +             return -EINVAL;
>>> +     }
>>> +     mutex_lock(&flash->lock);
>>> +
>>> +     /*erase whole chip*/
>>> +     if (len == mtd->size) {
>>> +             if (altera_epcq_erase_chip(mtd)) {
>>> +                     e_info->state = MTD_ERASE_FAILED;
>>> +                     mutex_unlock(&flash->lock);
>>> +                     return -EIO;
>>> +             }
>>> +     /*"sector"-at-a-time erase*/
>>> +     } else {
>>> +             while (len) {
>>> +                     ret = altera_epcq_erase_sector(mtd, addr);
>>> +                     if (ret) {
>>> +                             e_info->state = MTD_ERASE_FAILED;
>>> +                             mutex_unlock(&flash->lock);
>>> +                             return -EIO;
>>> +                     }
>>> +                     addr += mtd->erasesize;
>>> +                     if (len < mtd->erasesize)
>>> +                             len = 0;
>>> +                     else
>>> +                             len -= mtd->erasesize;
>>> +             }
>>> +     }
>>> +     mutex_unlock(&flash->lock);
>>> +     e_info->state = MTD_ERASE_DONE;
>>> +     mtd_erase_callback(e_info);
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static int altera_epcq_read(struct mtd_info *mtd, loff_t from, size_t len,
>>> +                         size_t *retlen, u8 *buf)
>>> +{
>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>> +     struct altera_epcq *dev = mtd->priv;
>>> +     void *src;
>>> +     int ret = 0;
>>> +
>>> +     if (!flash || !dev)
>>> +             return -ENODEV;
>>
>> Why would either of these be 0? If they are, you have much bigger
>> problems, since you aren't checking these almost anywhere else. And the
>> !flash check is pretty odd, since get_flash_data() is using
>> container_of(), which is not likely to give you exactly 0...
>>
>>> +
>>> +     if (flash->bank > dev->num_flashes - 1) {
>>> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
>>> +             return -EINVAL;
>>> +     }
>>> +
>>> +     src = dev->data_base + from;
>>> +
>>> +     mutex_lock(&flash->lock);
>>> +     /* wait till previous write/erase is done. */
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>> +     if (ret)
>>> +             goto err;
>>> +
>>> +     memcpy_fromio(buf, (u8 *)src, len);
>>
>> Drop the cast. Also, you get sparse warnings because you're dropping the
>> __iomem. Why not just this?
>>
>>         memcpy_fromio(buf, dev->data_base + from, len);
>>
>> (BTW, you might consider running sparse on all your code. See
>> Documentation/sparse.txt. It tells you how to get it, but you can often
>> just download it through your distro's package manager.)
>>
>>> +     *retlen = len;
>>> +
>>> +err:
>>> +     mutex_unlock(&flash->lock);
>>> +     return ret;
>>> +}
>>> +
>>> +static int altera_epcq_write(struct mtd_info *mtd, loff_t to, size_t len,
>>> +                          size_t *retlen, const u8 *buf)
>>> +{
>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>> +     struct altera_epcq *dev = mtd->priv;
>>> +     void *dest;
>>> +     int ret = 0;
>>> +
>>> +     if (!flash || !dev)
>>> +             return -ENODEV;
>>
>> Also here. I don't think you need these checks.
>>
>>> +
>>> +     if (flash->bank > dev->num_flashes - 1) {
>>> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
>>> +             return -EINVAL;
>>> +     }
>>> +     dest = dev->data_base + to;
>>> +
>>> +     mutex_lock(&flash->lock);
>>> +
>>> +     /* wait until finished previous write command. */
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>> +     if (ret)
>>> +             goto err;
>>> +
>>> +     memcpy_toio(dest, buf, len);
>>
>> Similar. Why not just this?
>>
>>         memcpy_toio(dev->data_base + to, buf, len);
>>
>>> +     *retlen += len;
>>> +
>>> +     /* wait until finished previous write command. */
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>> +     if (ret)
>>> +             goto err;
>>> +
>>> +      /* check whether write triggered a illegal write interrupt */
>>> +     ret = altera_epcq_write_erase_check(dev, 1);
>>> +     if (ret < 0)
>>> +             goto err;
>>> +
>>> +err:
>>> +     mutex_unlock(&flash->lock);
>>> +     return ret;
>>> +}
>>> +
>>> +static int altera_epcq_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
>>> +{
>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>> +     struct altera_epcq *dev = mtd->priv;
>>> +     uint32_t offset = ofs;
>>> +     int ret = 0;
>>> +     u32 sector_start, sector_end;
>>> +     u32 num_sectors;
>>> +     u32 mem_op;
>>> +     unsigned sr_bp = 0;
>>> +     unsigned sr_tb = 0;
>>> +
>>> +     sector_start = altera_epcq_addr_to_sector(flash, offset);
>>> +     sector_end = altera_epcq_addr_to_sector(flash, offset + len);
>>> +     num_sectors = flash->num_sectors;
>>> +     dev_dbg(&dev->pdev->dev,
>>> +             "%s: num_setor is %u,sector start is %u,sector end is %u\n",
>>> +             __func__, num_sectors, sector_start, sector_end);
>>> +
>>> +     mutex_lock(&flash->lock);
>>> +     /* wait until finished previous write command. */
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>> +     if (ret)
>>> +             goto err;
>>> +
>>> +     if (sector_start >= num_sectors/2) {
>>
>> It's preferable to have spaces around operators. So 'num_sectors / 2'.
>> The same thing comes up in several other places.
>>
>>> +             if (sector_start < num_sectors-(num_sectors / 4))
>>> +                     sr_bp = __ilog2_u32(num_sectors);
>>> +             else if (sector_start < num_sectors-(num_sectors / 8))
>>> +                     sr_bp = __ilog2_u32(num_sectors) - 1;
>>> +             else if (sector_start < num_sectors-(num_sectors / 16))
>>> +                     sr_bp = __ilog2_u32(num_sectors) - 2;
>>> +             else if (sector_start < num_sectors-(num_sectors / 32))
>>> +                     sr_bp = __ilog2_u32(num_sectors) - 3;
>>> +             else if (sector_start < num_sectors-(num_sectors / 64))
>>> +                     sr_bp = __ilog2_u32(num_sectors) - 4;
>>> +             else if (sector_start < num_sectors-(num_sectors / 128))
>>> +                     sr_bp = __ilog2_u32(num_sectors) - 5;
>>> +             else if (sector_start < num_sectors-(num_sectors / 256))
>>> +                     sr_bp = __ilog2_u32(num_sectors) - 6;
>>> +             else if (sector_start < num_sectors-(num_sectors / 512))
>>> +                     sr_bp = __ilog2_u32(num_sectors) - 7;
>>> +             else if (sector_start < num_sectors-(num_sectors / 1024))
>>> +                     sr_bp = __ilog2_u32(num_sectors) - 8;
>>> +             else
>>> +                     sr_bp = 0;  /* non area protected */
>>
>> Yikes, that's ugly! And I'm not sure it matches the EPCQ doc I found.
>> I'm pretty sure you can rewrite this if/else-if/else block in about 1
>> line though.
>>
>>> +
>>> +             if (sr_bp < 0) {
>>
>> sr_bp is unsigned, so this is never true.
>>
>>> +                     dev_err(&dev->pdev->dev, "%s: address is out of range\n"
>>> +                             , __func__);
>>> +                     ret = -EINVAL;
>>> +                     goto err;
>>> +             }
>>> +             /*set TB = 0*/
>>> +             sr_tb = 0;
>>> +
>>> +     } else {
>>> +             if (sector_end < 1)
>>> +                     sr_bp = 1;
>>> +             else if (sector_end < 2)
>>> +                     sr_bp = 2;
>>> +             else if (sector_end < 4)
>>> +                     sr_bp = 3;
>>> +             else if (sector_end < 8)
>>> +                     sr_bp = 4;
>>> +             else if (sector_end < 16)
>>> +                     sr_bp = 5;
>>> +             else if (sector_end < 32)
>>> +                     sr_bp = 6;
>>> +             else if (sector_end < 64)
>>> +                     sr_bp = 7;
>>> +             else if (sector_end < 128)
>>> +                     sr_bp = 8;
>>> +             else if (sector_end < 256)
>>> +                     sr_bp = 9;
>>> +             else if (sector_end < 512)
>>> +                     sr_bp = 10;
>>> +             else
>>> +                     sr_bp = 16; /*protect all areas*/
>>
>> Again, this is ugly. How about this?
>>
>>         sr_bp = fls(sector_end) + 1;
>>
>>> +
>>> +             sr_tb = 1;
>>> +     }
>>> +
>>> +     mem_op = (sr_tb << 12) | (sr_bp << 8);
>>> +     mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
>>> +     mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
>>> +     writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
>>> +
>>> +err:
>>> +     mutex_unlock(&flash->lock);
>>> +     return ret;
>>> +}
>>> +
>>> +static int altera_epcq_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
>>> +{
>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>> +     struct altera_epcq *dev = mtd->priv;
>>> +
>>> +     int ret = 0;
>>> +     u32 mem_op;
>>> +
>>> +     mutex_lock(&flash->lock);
>>> +     /* wait until finished previous write command. */
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>> +     if (ret)
>>> +             goto err;
>>> +     dev_info(&dev->pdev->dev, "Unlock all protected area\n");
>>> +     mem_op = 0;
>>> +     mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
>>> +     mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
>>> +     writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
>>> +
>>> +err:
>>> +     mutex_unlock(&flash->lock);
>>> +     return ret;
>>> +}
>>> +
>>> +static void altera_epcq_chip_select(struct altera_epcq *dev, u32 bank)
>>> +{
>>> +     u32 val = 0;
>>> +
>>> +     switch (bank) {
>>> +     case 0:
>>> +             val = EPCQ_CHIP_SELECT_0;
>>> +             break;
>>> +     case 1:
>>> +             val = EPCQ_CHIP_SELECT_1;
>>> +             break;
>>> +     case 2:
>>> +             val = EPCQ_CHIP_SELECT_2;
>>> +             break;
>>> +     default:
>>> +             return;
>>> +     }
>>> +
>>> +     writel(val, dev->csr_base + EPCQ_CHIP_SELECT_REG);
>>> +}
>>> +
>>> +static int altera_epcq_probe_flash(struct altera_epcq *dev, u32 bank)
>>> +{
>>> +     int ret = 0;
>>> +     u32 val = 0;
>>> +
>>> +     mutex_lock(&dev->lock);
>>> +
>>> +     /*select bank*/
>>
>> Comment spacing, here and elsewhere:
>>
>>         /* select bank */
>>
>>> +     altera_epcq_chip_select(dev, bank);
>>> +
>>> +     ret = altera_epcq_wait_till_ready(dev);
>>> +     if (ret)
>>> +             goto err;
>>> +
>>> +     /* get device sillicon id */
>>> +     if (dev->is_epcs)
>>> +             val = readl(dev->csr_base + EPCQ_SID_REG);
>>> +     else
>>> +             val = readl(dev->csr_base + EPCQ_RDID_REG);
>>> +
>>> +     /* get flash index based on the device list*/
>>> +     ret = get_flash_index(val, dev->is_epcs);
>>> +     return 0;
>>> +err:
>>> +     mutex_unlock(&dev->lock);
>>> +     return ret;
>>> +}
>>> +
>>> +static int altera_epcq_probe_config_dt(struct platform_device *pdev,
>>> +                                    struct device_node *np,
>>> +                                    struct altera_epcq_plat_data *pdata)
>>> +{
>>> +     struct device_node *pp = NULL;
>>> +     struct resource *epcq_res;
>>> +     int i = 0;
>>> +     u32 id;
>>> +
>>> +     pdata->is_epcs = of_property_read_bool(np, "is-epcs");
>>> +
>>> +     epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
>>> +                                             "csr_base");
>>> +     if (!epcq_res) {
>>
>> devm_ioremap_resource() will check the that 'epcq_res' is valid, so you
>> don't need this whole 'if' block.
>>
>>> +             dev_err(&pdev->dev, "resource csr base is not defined\n");
>>> +             return -ENODEV;
>>> +     }
>>> +
>>> +     pdata->csr_base = devm_ioremap_resource(&pdev->dev, epcq_res);
>>> +     if (IS_ERR(pdata->csr_base)) {
>>> +             dev_err(&pdev->dev, "%s: ERROR: failed to map csr base\n",
>>> +                     __func__);
>>> +             return PTR_ERR(pdata->csr_base);
>>> +     }
>>> +
>>> +     epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
>>> +                                             "data_base");
>>> +     if (!epcq_res) {
>>
>> Same here.
>>
>>> +             dev_err(&pdev->dev, "resource data base is not defined\n");
>>> +             return -ENODEV;
>>> +     }
>>> +
>>> +     pdata->data_base = devm_ioremap_resource(&pdev->dev, epcq_res);
>>> +     if (IS_ERR(pdata->data_base)) {
>>> +             dev_err(&pdev->dev, "%s: ERROR: failed to map data base\n",
>>> +                     __func__);
>>> +             return PTR_ERR(pdata->data_base);
>>> +     }
>>> +
>>> +     pdata->board_flash_info = devm_kzalloc(&pdev->dev,
>>> +                                            sizeof(*pdata->board_flash_info),
>>> +                                            GFP_KERNEL);
>>> +
>>> +     /* Fill structs for each subnode (flash device) */
>>> +     while ((pp = of_get_next_child(np, pp))) {
>>
>> for_each_available_child_of_node()?
>>
>>> +             struct altera_epcq_flash_info *flash_info;
>>> +
>>> +             flash_info = &pdata->board_flash_info[i];
>>
>> This is never used. Either use it below, or drop the local variable.
>>
>>> +             pdata->np[i] = pp;
>>> +
>>> +             /* Read bank id from DT */
>>> +             if (of_get_property(pp, "reg", &id))
>>
>> Is this property optional? Your DT binding doc doesn't make it clear,
>> but it seems like a property which would be wise to require (i.e., not
>> optional).
>>
>>> +                     pdata->board_flash_info[i].bank = id;
>>> +             i++;
>>> +     }
>>> +     pdata->num_flashes = i;
>>> +     return 0;
>>> +}
>>> +
>>> +static int altera_epcq_setup_banks(struct platform_device *pdev,
>>> +                                u32 bank, struct device_node *np,
>>> +                                struct altera_epcq_plat_data *pdata)
>>> +{
>>> +     struct altera_epcq *dev = platform_get_drvdata(pdev);
>>> +     struct mtd_part_parser_data ppdata = {};
>>> +     struct altera_epcq_flash_info *flash_info;
>>> +     struct altera_epcq_flash *flash;
>>> +     struct mtd_partition *parts = NULL;
>>
>> Drop this variable. Just use NULL directly below.
>>
>>> +     int count = 0;
>>
>> Same. Just use 0 below.
>>
>>> +     int flash_index;
>>> +     int ret = 0;
>>> +     uint64_t size;
>>> +     uint32_t sector_size;
>>> +
>>> +     flash_info = &pdata->board_flash_info[bank];
>>> +     if (!flash_info)
>>> +             return -ENODEV;
>>> +
>>> +     if (bank > pdata->num_flashes - 1)
>>> +             return -EINVAL;
>>> +
>>> +     flash = devm_kzalloc(&pdev->dev, sizeof(*flash), GFP_KERNEL);
>>> +     if (!flash)
>>> +             return -ENOMEM;
>>> +     flash->bank = bank;
>>> +
>>> +     mutex_init(&flash->lock);
>>> +
>>> +     /* verify whether flash is really present on board */
>>> +     flash_index = altera_epcq_probe_flash(dev, bank);
>>> +     if (flash_index < 0) {
>>> +             dev_info(&dev->pdev->dev, "epcq:flash%d not found\n",
>>> +                      flash->bank);
>>> +             return flash_index;
>>> +     }
>>> +
>>> +     dev->flash[bank] = flash;
>>> +
>>> +     size = flash_devices[flash_index].size_inbytes;
>>> +     sector_size = flash_devices[flash_index].sectorsize_inbytes;
>>> +     /*use do_div instead of plain div to avoid linker err*/
>>> +     do_div(size, sector_size);
>>> +     flash->num_sectors = size;
>>> +
>>> +     /*mtd framework */
>>> +     flash->mtd.priv = dev;
>>> +     flash->mtd.name = flash_devices[flash_index].name;
>>> +     flash->mtd.type = MTD_NORFLASH;
>>> +     flash->mtd.writesize = 1;
>>> +     flash->mtd.flags = MTD_CAP_NORFLASH;
>>> +     flash->mtd.size = flash_devices[flash_index].size_inbytes;
>>> +     flash->mtd.erasesize = flash_devices[flash_index].sectorsize_inbytes;
>>> +     flash->mtd._erase = altera_epcq_erase;
>>> +     flash->mtd._read = altera_epcq_read;
>>> +     flash->mtd._write = altera_epcq_write;
>>> +     flash->mtd._lock = altera_epcq_lock;
>>> +     flash->mtd._unlock = altera_epcq_unlock;
>>> +
>>> +     dev_info(&pdev->dev, "mtd .name=%s .size=0x%llx (%lluM)\n",
>>> +              flash->mtd.name, (long long)flash->mtd.size,
>>> +              (long long)(flash->mtd.size >> 20));
>>> +
>>> +     dev_info(&pdev->dev, ".erasesize = 0x%x(%uK)\n",
>>> +              flash->mtd.erasesize, flash->mtd.erasesize >> 10);
>>> +
>>> +     ppdata.of_node = np;
>>> +
>>> +     ret = mtd_device_parse_register(&flash->mtd, NULL, &ppdata, parts,
>>> +                                     count);
>>> +     if (ret) {
>>> +             dev_err(&pdev->dev, "Err MTD partition=%d\n", ret);
>>> +             goto err;
>>
>> You don't want to unregister a MTD that failed to register. The MTD core
>> code should handle that itself.
>>
>> Instead of this if (ret) {} block, I'd just make this:
>>
>>         return mtd_device_parse_register(&flash->mtd, NULL, &ppdata, NULL, 0);
>>
>>> +     }
>>> +
>>> +     return 0;
>>> +
>>> +err:
>>> +     mtd_device_unregister(&flash->mtd);
>>> +     return ret;
>>> +}
>>> +
>>> +static int altera_epcq_probe(struct platform_device *pdev)
>>> +{
>>> +     struct device_node *np = pdev->dev.of_node;
>>> +     struct altera_epcq_plat_data *pdata = NULL;
>>> +     struct altera_epcq *dev;
>>> +     int ret = 0;
>>> +     int i;
>>> +
>>> +     if (!np) {
>>> +             ret = -ENODEV;
>>> +             dev_err(&pdev->dev, "no device found\n");
>>> +             goto err;
>>> +     }
>>> +
>>> +     pdata = devm_kzalloc(&pdev->dev,
>>> +                          sizeof(struct altera_epcq_plat_data),
>>> +                          GFP_KERNEL);
>>> +
>>> +     if (!pdata) {
>>> +             ret = -ENOMEM;
>>> +             dev_err(&pdev->dev, "%s: ERROR: no memory\n", __func__);
>>
>> Unnecessary print. The MM code will print plenty of info if you're
>> out of memory.
>>
>>> +             goto err;
>>> +     }
>>> +     ret = altera_epcq_probe_config_dt(pdev, np, pdata);
>>> +     if (ret) {
>>> +             ret = -ENODEV;
>>> +             dev_err(&pdev->dev, "probe fail\n");
>>> +             goto err;
>>> +     }
>>> +
>>> +     dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
>>> +     if (!dev) {
>>> +             ret = -ENOMEM;
>>> +             dev_err(&pdev->dev, "mem alloc fail\n");
>>
>> Same here.
>>
>>> +             goto err;
>>> +     }
>>> +     mutex_init(&dev->lock);
>>> +     dev->pdev = pdev;
>>> +     dev->is_epcs = pdata->is_epcs;
>>> +     dev->csr_base = pdata->csr_base;
>>> +     dev->data_base = pdata->data_base;
>>> +
>>> +     /*check number of flashes*/
>>> +     dev->num_flashes = pdata->num_flashes;
>>> +     if (dev->num_flashes > MAX_NUM_FLASH_CHIP) {
>>> +             dev_err(&pdev->dev, "exceeding max number of flashes\n");
>>> +             dev->num_flashes = MAX_NUM_FLASH_CHIP;
>>> +     }
>>> +
>>> +     /* check clock*/
>>> +     dev->clk = devm_clk_get(&pdev->dev, NULL);
>>> +     if (IS_ERR(dev->clk)) {
>>> +             ret = PTR_ERR(dev->clk);
>>> +             goto err;
>>> +     }
>>> +     ret = clk_prepare_enable(dev->clk);
>>> +     if (ret)
>>> +             goto err;
>>> +
>>> +     platform_set_drvdata(pdev, dev);
>>> +
>>> +     /* loop for each serial flash which is connected to epcq */
>>> +     for (i = 0; i < dev->num_flashes; i++) {
>>> +             ret = altera_epcq_setup_banks(pdev, i, pdata->np[i], pdata);
>>> +             if (ret) {
>>> +                     dev_err(&pdev->dev, "bank setup failed\n");
>>> +                     goto err_bank_setup;
>>> +             }
>>> +     }
>>> +
>>> +     return 0;
>>> +
>>> +err_bank_setup:
>>> +     clk_disable_unprepare(dev->clk);
>>> +err:
>>> +     return ret;
>>> +}
>>> +
>>> +static int altera_epcq_remove(struct platform_device *pdev)
>>> +{
>>> +     struct altera_epcq *dev;
>>> +     struct altera_epcq_flash *flash;
>>> +     int ret, i;
>>> +
>>> +     dev = platform_get_drvdata(pdev);
>>> +
>>> +     /* clean up for all nor flash */
>>> +     for (i = 0; i < dev->num_flashes; i++) {
>>> +             flash = dev->flash[i];
>>> +             if (!flash)
>>> +                     continue;
>>> +
>>> +             /* clean up mtd stuff */
>>> +             ret = mtd_device_unregister(&flash->mtd);
>>> +             if (ret)
>>> +                     dev_err(&pdev->dev, "error removing mtd\n");
>>> +     }
>>> +
>>> +     clk_disable_unprepare(dev->clk);
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +#ifdef CONFIG_PM
>>
>> Make this CONFIG_PM_SLEEP.
>>
>>> +static int altera_epcq_suspend(struct device *dev)
>>> +{
>>> +     struct altera_epcq *sdev = dev_get_drvdata(dev);
>>> +
>>> +     clk_disable_unprepare(sdev->clk);
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static int altera_epcq_resume(struct device *dev)
>>> +{
>>> +     struct altera_epcq *sdev = dev_get_drvdata(dev);
>>> +     int ret = -EPERM;
>>
>> Drop 'ret'.
>>
>>> +
>>> +     ret = clk_prepare_enable(sdev->clk);
>>
>> Just:
>>
>>         return clk_prepare_enable(sdev->clk);
>>
>>> +
>>> +     return ret;
>>> +}
>>> +
>>> +static SIMPLE_DEV_PM_OPS(altera_epcq_pm_ops, altera_epcq_suspend,
>>> +                      altera_epcq_resume);
>>> +#endif
>>> +
>>> +static const struct of_device_id altera_epcq_id_table[] = {
>>> +     { .compatible = "altr,epcq-1.0" },
>>> +     {}
>>> +};
>>> +MODULE_DEVICE_TABLE(of, altera_epcq_id_table);
>>> +
>>> +static struct platform_driver altera_epcq_driver = {
>>> +     .driver = {
>>> +             .name = ALTERA_EPCQ_RESOURCE_NAME,
>>> +             .bus = &platform_bus_type,
>>> +             .owner = THIS_MODULE,
>>
>> The .owner assignment is no longer necessary.
>>
>>> +             .of_match_table = altera_epcq_id_table,
>>> +#ifdef CONFIG_PM
>>
>> Also CONFIG_PM_SLEEP.
>>
>>> +             .pm = &altera_epcq_pm_ops,
>>> +#endif
>>> +     },
>>> +     .probe = altera_epcq_probe,
>>> +     .remove = altera_epcq_remove,
>>> +};
>>> +module_platform_driver(altera_epcq_driver);
>>> +
>>> +MODULE_AUTHOR("Viet Nga Dao <vndao@altera.com>");
>>> +MODULE_DESCRIPTION("MTD Altera EPCQ Driver");
>>> +MODULE_LICENSE("GPL v2");
>>> diff --git a/drivers/mtd/devices/altera_epcq.h b/drivers/mtd/devices/altera_epcq.h
>>> new file mode 100644
>>> index 0000000..c3d15e5
>>> --- /dev/null
>>> +++ b/drivers/mtd/devices/altera_epcq.h
>>> @@ -0,0 +1,130 @@
>>> +/*
>>> + * Copyright (C) 2014 Altera Corporation. All rights reserved
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms and conditions of the GNU General Public License,
>>> + * version 2, as published by the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope 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.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along with
>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>> + */
>>> +
>>> +#ifndef __ALTERA_ECPQ_H
>>> +#define __ALTERA_ECPQ_H
>>> +
>>> +#define ALTERA_EPCQ_RESOURCE_NAME        "altera_epcq"
>>> +/* max possible slots for serial flash chip in the EPCQ controller */
>>> +#define MAX_NUM_FLASH_CHIP   3
>>> +
>>> +/* macro to define partitions for flash devices */
>>> +#define DEFINE_PARTS(n, of, s)               \
>>
>> This macro is not used anywhere. Drop it?
>>
>>> +{                                    \
>>> +     .name = n,                      \
>>> +     .offset = of,                   \
>>> +     .size = s,                      \
>>> +}
>>> +
>>> +struct altera_epcq_flash_info {
>>> +     u32 bank;
>>> +     struct mtd_partition *partitions;
>>> +     int nr_partitions;
>>> +};
>>> +
>>> +struct altera_epcq_plat_data {
>>> +     void __iomem *csr_base;
>>> +     void __iomem *data_base;
>>> +     bool is_epcs;
>>> +     u32 num_flashes;
>>> +     struct altera_epcq_flash_info *board_flash_info;
>>> +     struct device_node *np[MAX_NUM_FLASH_CHIP];
>>> +};
>>> +
>>> +struct altera_epcq {
>>> +     struct clk *clk;
>>> +     bool is_epcs;
>>> +     struct mutex lock;      /*device lock*/
>>> +     void __iomem *csr_base;
>>> +     void __iomem *data_base;
>>> +     struct platform_device *pdev;
>>> +     u32 num_flashes;
>>> +     struct altera_epcq_flash *flash[MAX_NUM_FLASH_CHIP];
>>> +};
>>> +
>>> +struct altera_epcq_flash {
>>> +     u32 bank;
>>> +     u32 num_sectors;
>>> +     u32 num_parts;
>>> +     struct mtd_partition *parts;
>>> +     struct mutex lock;      /*flash lock*/
>>> +     struct mtd_info mtd;
>>> +};
>>> +
>>> +/* Define max times to check status register before we give up. */
>>> +#define EPCQ_MAX_TIME_OUT                    (40 * HZ)
>>> +
>>> +/* defines for status register */
>>> +#define EPCQ_SR_REG                          0x0
>>> +#define EPCQ_SR_WIP_MASK                     0x00000001
>>> +#define EPCQ_SR_WIP                          0x1
>>> +#define EPCQ_SR_WEL                          0x2
>>> +#define EPCQ_SR_BP0                          0x4
>>> +#define EPCQ_SR_BP1                          0x8
>>> +#define EPCQ_SR_BP2                          0x10
>>> +#define EPCQ_SR_BP3                          0x40
>>> +#define EPCQ_SR_TB                           0x20
>>> +
>>> +/* defines for device id register */
>>> +#define EPCQ_SID_REG                         0x4
>>> +#define EPCQ_RDID_REG                                0x8
>>> +#define EPCQ_RDID_MASK                               0x000000FF
>>> +/*
>>> + * EPCQ_MEM_OP register offset
>>> + *
>>> + * The EPCQ_MEM_OP register is used to do memory protect and erase operations
>>> + *
>>> + */
>>> +#define EPCQ_MEM_OP_REG                              0xC
>>> +
>>> +#define EPCQ_MEM_OP_CMD_MASK                 0x00000003
>>> +#define EPCQ_MEM_OP_BULK_ERASE_CMD           0x00000001
>>> +#define EPCQ_MEM_OP_SECTOR_ERASE_CMD         0x00000002
>>> +#define EPCQ_MEM_OP_SECTOR_PROTECT_CMD               0x00000003
>>> +#define EPCQ_MEM_OP_SECTOR_VALUE_MASK                0x0003FF00
>>> +#define EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK        0x00001F00
>>> +#define EPCQ_MEM_OP_SECTOR_PROTECT_SHIFT     8
>>> +/*
>>> + * EPCQ_ISR register offset
>>> + *
>>> + * The EPCQ_ISR register is used to determine whether an invalid write or erase
>>> + * operation trigerred an interrupt
>>> + *
>>> + */
>>> +#define EPCQ_ISR_REG                         0x10
>>> +
>>> +#define EPCQ_ISR_ILLEGAL_ERASE_MASK          0x00000001
>>> +#define EPCQ_ISR_ILLEGAL_WRITE_MASK          0x00000002
>>> +
>>> +/*
>>> + * EPCQ_IMR register offset
>>> + *
>>> + * The EPCQ_IMR register is used to mask the invalid erase or the invalid write
>>> + * interrupts.
>>> + *
>>> + */
>>> +#define EPCQ_IMR_REG                         0x14
>>> +#define EPCQ_IMR_ILLEGAL_ERASE_MASK          0x00000001
>>> +
>>> +#define EPCQ_IMR_ILLEGAL_WRITE_MASK          0x00000002
>>> +
>>> +#define EPCQ_CHIP_SELECT_REG                 0x18
>>> +#define EPCQ_CHIP_SELECT_MASK                        0x00000007
>>> +#define EPCQ_CHIP_SELECT_0                   0x00000001
>>> +#define EPCQ_CHIP_SELECT_1                   0x00000002
>>> +#define EPCQ_CHIP_SELECT_2                   0x00000004
>>> +
>>> +#endif /* __ALTERA_ECPQ_H */
>>
>> Brian

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-01-22  2:56     ` Viet Nga Dao
  0 siblings, 0 replies; 39+ messages in thread
From: Viet Nga Dao @ 2015-01-22  2:56 UTC (permalink / raw)
  To: Brian Norris, David Woodhouse
  Cc: linux-mtd, linux-kernel, devicetree, Viet Nga Dao

On Tue, Jan 20, 2015 at 10:05 AM, Viet Nga Dao <vndao@altera.com> wrote:
> On Thu, Jan 15, 2015 at 5:27 PM, Viet Nga Dao <vndao@altera.com> wrote:
>> On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
>> <computersforpeace@gmail.com> wrote:
>>> On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao@altera.com wrote:
>>>> From: Viet Nga Dao <vndao@altera.com>
>>>>
>>>> Altera EPCQ Controller is a soft IP which enables access to Altera EPCQ and
>>>> EPCS flash chips. This patch adds driver for these devices.
>>>>
>>>> Signed-off-by: Viet Nga Dao <vndao@altera.com>
>>>
>>> This drivers seems awfully similar to (and so I infer it is likely
>>> copy-and-pasted from) m25p80.c / spi-nor.c. Do you think it can be
>>> rewritten as a SPI NOR driver, under drivers/mtd/spi-nor/ ? It looks
>>> like these flash share most (all?) the same basic opcodes.
>>>
>> For Altera EPCQ flashes, almost operations are performed underline
>> hardware. Software only able to perform the following through
>> registers:
>>  -  read status register
>>  -  read id
>>  -  write status registers bit SR_BP0,SR_BP1, SR_BP2,SR_BP3, SR_TB
>> (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf)
>> For read/write data: all the operations like QUAD_READ/WRITE,
>> FAST_READ/WRITE are handled by hardware as well. From software point
>> of view, there is no difference between these 2 modes.
>> That is why if rewrite the drivers to follow spi-nor structure, it
>> will require extra decoding works for the only few used opcodes.
>>
> Is it OK to remain this driver structure?
Can someone please reply my question as it is been a while?
Viet Nga

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-01-22  2:56     ` Viet Nga Dao
  0 siblings, 0 replies; 39+ messages in thread
From: Viet Nga Dao @ 2015-01-22  2:56 UTC (permalink / raw)
  To: Brian Norris, David Woodhouse
  Cc: linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Viet Nga Dao

On Tue, Jan 20, 2015 at 10:05 AM, Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org> wrote:
> On Thu, Jan 15, 2015 at 5:27 PM, Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org> wrote:
>> On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
>> <computersforpeace-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>>> On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org wrote:
>>>> From: Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org>
>>>>
>>>> Altera EPCQ Controller is a soft IP which enables access to Altera EPCQ and
>>>> EPCS flash chips. This patch adds driver for these devices.
>>>>
>>>> Signed-off-by: Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org>
>>>
>>> This drivers seems awfully similar to (and so I infer it is likely
>>> copy-and-pasted from) m25p80.c / spi-nor.c. Do you think it can be
>>> rewritten as a SPI NOR driver, under drivers/mtd/spi-nor/ ? It looks
>>> like these flash share most (all?) the same basic opcodes.
>>>
>> For Altera EPCQ flashes, almost operations are performed underline
>> hardware. Software only able to perform the following through
>> registers:
>>  -  read status register
>>  -  read id
>>  -  write status registers bit SR_BP0,SR_BP1, SR_BP2,SR_BP3, SR_TB
>> (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf)
>> For read/write data: all the operations like QUAD_READ/WRITE,
>> FAST_READ/WRITE are handled by hardware as well. From software point
>> of view, there is no difference between these 2 modes.
>> That is why if rewrite the drivers to follow spi-nor structure, it
>> will require extra decoding works for the only few used opcodes.
>>
> Is it OK to remain this driver structure?
Can someone please reply my question as it is been a while?
Viet Nga
--
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	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-01-22  2:56     ` Viet Nga Dao
  0 siblings, 0 replies; 39+ messages in thread
From: Viet Nga Dao @ 2015-01-22  2:56 UTC (permalink / raw)
  To: Brian Norris, David Woodhouse
  Cc: Viet Nga Dao, devicetree, linux-mtd, linux-kernel

On Tue, Jan 20, 2015 at 10:05 AM, Viet Nga Dao <vndao@altera.com> wrote:
> On Thu, Jan 15, 2015 at 5:27 PM, Viet Nga Dao <vndao@altera.com> wrote:
>> On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
>> <computersforpeace@gmail.com> wrote:
>>> On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao@altera.com wrote:
>>>> From: Viet Nga Dao <vndao@altera.com>
>>>>
>>>> Altera EPCQ Controller is a soft IP which enables access to Altera EPCQ and
>>>> EPCS flash chips. This patch adds driver for these devices.
>>>>
>>>> Signed-off-by: Viet Nga Dao <vndao@altera.com>
>>>
>>> This drivers seems awfully similar to (and so I infer it is likely
>>> copy-and-pasted from) m25p80.c / spi-nor.c. Do you think it can be
>>> rewritten as a SPI NOR driver, under drivers/mtd/spi-nor/ ? It looks
>>> like these flash share most (all?) the same basic opcodes.
>>>
>> For Altera EPCQ flashes, almost operations are performed underline
>> hardware. Software only able to perform the following through
>> registers:
>>  -  read status register
>>  -  read id
>>  -  write status registers bit SR_BP0,SR_BP1, SR_BP2,SR_BP3, SR_TB
>> (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf)
>> For read/write data: all the operations like QUAD_READ/WRITE,
>> FAST_READ/WRITE are handled by hardware as well. From software point
>> of view, there is no difference between these 2 modes.
>> That is why if rewrite the drivers to follow spi-nor structure, it
>> will require extra decoding works for the only few used opcodes.
>>
> Is it OK to remain this driver structure?
Can someone please reply my question as it is been a while?
Viet Nga

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-01-22  8:28   ` Brian Norris
  0 siblings, 0 replies; 39+ messages in thread
From: Brian Norris @ 2015-01-22  8:28 UTC (permalink / raw)
  To: Viet Nga Dao; +Cc: dwmw2, linux-mtd, linux-kernel, devicetree, nga_chi86

Hi Viet,

On Thu, Jan 15, 2015 at 05:27:10PM +0800, Viet Nga Dao wrote:
> On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
> <computersforpeace@gmail.com> wrote:
> > On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao@altera.com wrote:
> >> From: Viet Nga Dao <vndao@altera.com>
> >>
> >> Altera EPCQ Controller is a soft IP which enables access to Altera EPCQ and
> >> EPCS flash chips. This patch adds driver for these devices.
> >>
> >> Signed-off-by: Viet Nga Dao <vndao@altera.com>
> >
> > This drivers seems awfully similar to (and so I infer it is likely
> > copy-and-pasted from) m25p80.c / spi-nor.c. Do you think it can be
> > rewritten as a SPI NOR driver, under drivers/mtd/spi-nor/ ? It looks
> > like these flash share most (all?) the same basic opcodes.
> >
> For Altera EPCQ flashes, almost operations are performed underline
> hardware.

Right, that's understandable. But that's what spi-nor.c was designed
for: implementing hardware-specific functionality that is targeted
directly at SPI flash. Did you take a look at the callbacks available in
'struct spi_nor'?

> Software only able to perform the following through
> registers:
>  -  read status register
>  -  read id
>  -  write status registers bit SR_BP0,SR_BP1, SR_BP2,SR_BP3, SR_TB
> (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf)

OK.

> For read/write data: all the operations like QUAD_READ/WRITE,
> FAST_READ/WRITE are handled by hardware as well. From software point
> of view, there is no difference between these 2 modes.

OK, so you don't have to hook up the dual/quad mode infrastructure. And
you'd just implement dead-simple spi_nor.{read,write,erase}() callbacks.

> That is why if rewrite the drivers to follow spi-nor structure, it
> will require extra decoding works for the only few used opcodes.

I think you'd only have some very trivial work here.

There would be some small work to reintroduce a properly-replaceable ID
table, and callbacks like ->lock() and ->unlock(), but those should be
implemented in spi-nor.c sooner or later anyway.

Anyway, if you take another look at spi-nor.{c,h} and determine that
it's too difficult, then I suppose I don't mind accepting your driver
under its current design. Your hardware is pretty esoteric anyway, the
driver is still pretty simple, and it'll never be supporting any common
SPI NOR vendors (right?), so it's not too big of a maintenance problem,
I expect. But I do want to make sure that we don't copy/paste to repeat
the mistakes of old drivers. As I noted already, your driver inherited
some of the quirks of the old m25p80.c code.

So please, give drivers/mtd/spi-nor/ another look, and then we can
resume this discussion.

[Snip the rest; please don't forget to address these comments in your
next patch revision. BTW, I recall you sent me some other replies to my
code review comments in personal email. Please reply to the mailing list
for all code review.]

Brian

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-01-22  8:28   ` Brian Norris
  0 siblings, 0 replies; 39+ messages in thread
From: Brian Norris @ 2015-01-22  8:28 UTC (permalink / raw)
  To: Viet Nga Dao
  Cc: dwmw2-wEGCiKHe2LqWVfeAwA7xHQ,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, nga_chi86

Hi Viet,

On Thu, Jan 15, 2015 at 05:27:10PM +0800, Viet Nga Dao wrote:
> On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
> <computersforpeace-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> > On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org wrote:
> >> From: Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org>
> >>
> >> Altera EPCQ Controller is a soft IP which enables access to Altera EPCQ and
> >> EPCS flash chips. This patch adds driver for these devices.
> >>
> >> Signed-off-by: Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org>
> >
> > This drivers seems awfully similar to (and so I infer it is likely
> > copy-and-pasted from) m25p80.c / spi-nor.c. Do you think it can be
> > rewritten as a SPI NOR driver, under drivers/mtd/spi-nor/ ? It looks
> > like these flash share most (all?) the same basic opcodes.
> >
> For Altera EPCQ flashes, almost operations are performed underline
> hardware.

Right, that's understandable. But that's what spi-nor.c was designed
for: implementing hardware-specific functionality that is targeted
directly at SPI flash. Did you take a look at the callbacks available in
'struct spi_nor'?

> Software only able to perform the following through
> registers:
>  -  read status register
>  -  read id
>  -  write status registers bit SR_BP0,SR_BP1, SR_BP2,SR_BP3, SR_TB
> (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf)

OK.

> For read/write data: all the operations like QUAD_READ/WRITE,
> FAST_READ/WRITE are handled by hardware as well. From software point
> of view, there is no difference between these 2 modes.

OK, so you don't have to hook up the dual/quad mode infrastructure. And
you'd just implement dead-simple spi_nor.{read,write,erase}() callbacks.

> That is why if rewrite the drivers to follow spi-nor structure, it
> will require extra decoding works for the only few used opcodes.

I think you'd only have some very trivial work here.

There would be some small work to reintroduce a properly-replaceable ID
table, and callbacks like ->lock() and ->unlock(), but those should be
implemented in spi-nor.c sooner or later anyway.

Anyway, if you take another look at spi-nor.{c,h} and determine that
it's too difficult, then I suppose I don't mind accepting your driver
under its current design. Your hardware is pretty esoteric anyway, the
driver is still pretty simple, and it'll never be supporting any common
SPI NOR vendors (right?), so it's not too big of a maintenance problem,
I expect. But I do want to make sure that we don't copy/paste to repeat
the mistakes of old drivers. As I noted already, your driver inherited
some of the quirks of the old m25p80.c code.

So please, give drivers/mtd/spi-nor/ another look, and then we can
resume this discussion.

[Snip the rest; please don't forget to address these comments in your
next patch revision. BTW, I recall you sent me some other replies to my
code review comments in personal email. Please reply to the mailing list
for all code review.]

Brian
--
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	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-01-22  8:28   ` Brian Norris
  0 siblings, 0 replies; 39+ messages in thread
From: Brian Norris @ 2015-01-22  8:28 UTC (permalink / raw)
  To: Viet Nga Dao; +Cc: linux-mtd, nga_chi86, dwmw2, linux-kernel, devicetree

Hi Viet,

On Thu, Jan 15, 2015 at 05:27:10PM +0800, Viet Nga Dao wrote:
> On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
> <computersforpeace@gmail.com> wrote:
> > On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao@altera.com wrote:
> >> From: Viet Nga Dao <vndao@altera.com>
> >>
> >> Altera EPCQ Controller is a soft IP which enables access to Altera EPCQ and
> >> EPCS flash chips. This patch adds driver for these devices.
> >>
> >> Signed-off-by: Viet Nga Dao <vndao@altera.com>
> >
> > This drivers seems awfully similar to (and so I infer it is likely
> > copy-and-pasted from) m25p80.c / spi-nor.c. Do you think it can be
> > rewritten as a SPI NOR driver, under drivers/mtd/spi-nor/ ? It looks
> > like these flash share most (all?) the same basic opcodes.
> >
> For Altera EPCQ flashes, almost operations are performed underline
> hardware.

Right, that's understandable. But that's what spi-nor.c was designed
for: implementing hardware-specific functionality that is targeted
directly at SPI flash. Did you take a look at the callbacks available in
'struct spi_nor'?

> Software only able to perform the following through
> registers:
>  -  read status register
>  -  read id
>  -  write status registers bit SR_BP0,SR_BP1, SR_BP2,SR_BP3, SR_TB
> (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf)

OK.

> For read/write data: all the operations like QUAD_READ/WRITE,
> FAST_READ/WRITE are handled by hardware as well. From software point
> of view, there is no difference between these 2 modes.

OK, so you don't have to hook up the dual/quad mode infrastructure. And
you'd just implement dead-simple spi_nor.{read,write,erase}() callbacks.

> That is why if rewrite the drivers to follow spi-nor structure, it
> will require extra decoding works for the only few used opcodes.

I think you'd only have some very trivial work here.

There would be some small work to reintroduce a properly-replaceable ID
table, and callbacks like ->lock() and ->unlock(), but those should be
implemented in spi-nor.c sooner or later anyway.

Anyway, if you take another look at spi-nor.{c,h} and determine that
it's too difficult, then I suppose I don't mind accepting your driver
under its current design. Your hardware is pretty esoteric anyway, the
driver is still pretty simple, and it'll never be supporting any common
SPI NOR vendors (right?), so it's not too big of a maintenance problem,
I expect. But I do want to make sure that we don't copy/paste to repeat
the mistakes of old drivers. As I noted already, your driver inherited
some of the quirks of the old m25p80.c code.

So please, give drivers/mtd/spi-nor/ another look, and then we can
resume this discussion.

[Snip the rest; please don't forget to address these comments in your
next patch revision. BTW, I recall you sent me some other replies to my
code review comments in personal email. Please reply to the mailing list
for all code review.]

Brian

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-01-27  6:53     ` Viet Nga Dao
  0 siblings, 0 replies; 39+ messages in thread
From: Viet Nga Dao @ 2015-01-27  6:53 UTC (permalink / raw)
  To: Viet Nga Dao, Brian Norris
  Cc: David Woodhouse, linux-mtd, linux-kernel, devicetree, nga_chi86

On Tue, Jan 20, 2015 at 10:05 AM, Viet Nga Dao <vndao@altera.com> wrote:
> On Thu, Jan 15, 2015 at 5:27 PM, Viet Nga Dao <vndao@altera.com> wrote:
>> On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
>> <computersforpeace@gmail.com> wrote:
>>> On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao@altera.com wrote:
>>>> From: Viet Nga Dao <vndao@altera.com>
>>>>
>>>> Altera EPCQ Controller is a soft IP which enables access to Altera EPCQ and
>>>> EPCS flash chips. This patch adds driver for these devices.
>>>>
>>>> Signed-off-by: Viet Nga Dao <vndao@altera.com>
>>>
>>> This drivers seems awfully similar to (and so I infer it is likely
>>> copy-and-pasted from) m25p80.c / spi-nor.c. Do you think it can be
>>> rewritten as a SPI NOR driver, under drivers/mtd/spi-nor/ ? It looks
>>> like these flash share most (all?) the same basic opcodes.
>>>
>> For Altera EPCQ flashes, almost operations are performed underline
>> hardware.
>
>Right, that's understandable. But that's what spi-nor.c was designed
>for: implementing hardware-specific functionality that is targeted
>directly at SPI flash. Did you take a look at the callbacks available in
>'struct spi_nor'?
>
>>Software only able to perform the following through
>> registers:
>>  -  read status register
>>  -  read id
>>  -  write status registers bit SR_BP0,SR_BP1, SR_BP2,SR_BP3, SR_TB
>> (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf)
>
>OK
>
>> For read/write data: all the operations like QUAD_READ/WRITE,
>> FAST_READ/WRITE are handled by hardware as well. From software point
>> of view, there is no difference between these 2 modes.
>
>OK, so you don't have to hook up the dual/quad mode infrastructure. And
>you'd just implement dead-simple spi_nor.{read,write,erase}() callbacks.
>
>> That is why if rewrite the drivers to follow spi-nor structure, it
>> will require extra decoding works for the only few used opcodes.
>>
>I think you'd only have some very trivial work here.
>
>There would be some small work to reintroduce a properly-replaceable ID
>table, and callbacks like ->lock() and ->unlock(), but those should be
>implemented in spi-nor.c sooner or later anyway.
>

I am trying to modify the code to follow your recommendation. However,
for lock and unlock functions, i have to implement it in spi_nor.c ,
am i right? If yes, currently we already have existing spi_nor_lock
and spi_nor_unlock functions but they could not apply for my driver.
Should i create a new functions named altera_epcq_lock and unlock and
then map it to callback functions or i should the put  those code
sunder existing spi_nor_lock/unlock?

>Anyway, if you take another look at spi-nor.{c,h} and determine that
>it's too difficult, then I suppose I don't mind accepting your driver
>under its current design. Your hardware is pretty esoteric anyway, the
>driver is still pretty simple, and it'll never be supporting any common
>SPI NOR vendors (right?), so it's not too big of a maintenance problem,
>I expect. But I do want to make sure that we don't copy/paste to repeat
>the mistakes of old drivers. As I noted already, your driver inherited
>some of the quirks of the old m25p80.c code.
>
>So please, give drivers/mtd/spi-nor/ another look, and then we can
>resume this discussion.
>>>> ---
>>>>  .../devicetree/bindings/mtd/altera_epcq.txt        |   45 ++
>>>>  drivers/mtd/devices/Kconfig                        |   12 +
>>>>  drivers/mtd/devices/Makefile                       |    2 +-
>>>>  drivers/mtd/devices/altera_epcq.c                  |  804 ++++++++++++++++++++
>>>>  drivers/mtd/devices/altera_epcq.h                  |  130 ++++
>>>>  5 files changed, 992 insertions(+), 1 deletions(-)
>>>>  create mode 100644 Documentation/devicetree/bindings/mtd/altera_epcq.txt
>>>>  create mode 100644 drivers/mtd/devices/altera_epcq.c
>>>>  create mode 100644 drivers/mtd/devices/altera_epcq.h
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>>>> new file mode 100644
>>>> index 0000000..d14f50e
>>>> --- /dev/null
>>>> +++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>>>> @@ -0,0 +1,45 @@
>>>> +* MTD Altera EPCQ driver
>>>> +
>>>> +Required properties:
>>>> +- compatible: Should be "altr,epcq-1.0"
>>>> +- reg: Address and length of the register set  for the device. It contains
>>>> +  the information of registers in the same order as described by reg-names
>>>> +- reg-names: Should contain the reg names
>>>> +  "csr_base": Should contain the register configuration base address
>>>> +  "data_base": Should contain the data base address
>>>> +- is-epcs: boolean type.
>>>> +             If present, the device contains EPCS flashes.
>>>> +             Otherwise, it contains EPCQ flashes.
>>>> +- #address-cells: Must be <1>.
>>>> +- #size-cells: Must be <0>.
>>>> +- flash device tree subnode, there must be a node with the following fields:
>>>
>>> These subnodes definitely require a 'compatible' string. Perhaps they
>>> should be used to differentiate EPCS vs. EPCQ. Does "is-epcs" really
>>> need to be in the top-level controller node?
>>>
>>>> +     - reg: Should contain the flash id
>>>
>>> Should, or must? (This question is relevant, because you seem to make it
>>> optional in your code.) And what does the "flash ID" mean? It seems like
>>> you're using as a chip-select or bank index.
>>>
Yes, this is used for chip select. It is required, not optional. This
to ID for each flash in the device
>>>> +     - #address-cells: please refer to /mtd/partition.txt
>>>> +     - #size-cells: please refer to /mtd/partition.txt
>>>> +     For partitions inside each flash, please refer to /mtd/partition.txt
>>>> +
>>>> +Example:
>>>> +
>>>> +                     epcq_controller_0: epcq@0x000000000 {
>>>> +                             compatible = "altr,epcq-1.0";
>>>> +                             reg = <0x00000001 0x00000000 0x00000020>,
>>>> +                                     <0x00000000 0x00000000 0x02000000>;
>>>> +                             reg-names = "csr_base", "data_base";
>>>> +                             #address-cells = <1>;
>>>> +                             #size-cells = <0>;
>>>> +                             flash0: epcq256@0 {
>>>> +                                     reg = <0>;
>>>> +                                     #address-cells = <1>;
>>>> +                                     #size-cells = <1>;
>>>> +                                     partition@0 {
>>>> +                                             /* 16 MB for raw data. */
>>>> +                                             label = "EPCQ Flash 0 raw data";
>>>> +                                             reg = <0x0 0x1000000>;
>>>> +                                     };
>>>> +                                     partition@1000000 {
>>>> +                                             /* 16 MB for jffs2 data. */
>>>> +                                             label = "EPCQ Flash 0 JFFS 2";
>>>> +                                             reg = <0x1000000 0x1000000>;
>>>> +                                     };
>>>> +                             };
>>>> +                     }; //end epcq@0x000000000 (epcq_controller_0)
>>>> diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
>>>> index c49d0b1..020b864 100644
>>>> --- a/drivers/mtd/devices/Kconfig
>>>> +++ b/drivers/mtd/devices/Kconfig
>>>> @@ -218,6 +218,18 @@ config MTD_ST_SPI_FSM
>>>>         SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
>>>>         for a subset of connected Serial Flash devices.
>>>>
>>>> +config MTD_ALTERA_EPCQ
>>>> +     tristate "Support Altera EPCQ/EPCS Flash chips"
>>>> +     depends on OF
>>>> +     help
>>>> +       This enables access to Altera EPCQ/EPCS flash chips, used for data
>>>> +       storage. See the driver source for the current list,
>>>> +       or to add other chips.
>>>> +
>>>> +       If you want to compile this driver as a module ( = code which can be
>>>> +       inserted in and removed from the running kernel whenever you want),
>>>> +       say M here and read <file:Documentation/kbuild/modules.txt>.
>>>> +
>>>>  if MTD_DOCG3
>>>>  config BCH_CONST_M
>>>>       default 14
>>>> diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
>>>> index f0b0e61..b429c4d 100644
>>>> --- a/drivers/mtd/devices/Makefile
>>>> +++ b/drivers/mtd/devices/Makefile
>>>> @@ -16,6 +16,6 @@ obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
>>>>  obj-$(CONFIG_MTD_SST25L)     += sst25l.o
>>>>  obj-$(CONFIG_MTD_BCM47XXSFLASH)      += bcm47xxsflash.o
>>>>  obj-$(CONFIG_MTD_ST_SPI_FSM)    += st_spi_fsm.o
>>>> -
>>>> +obj-$(CONFIG_MTD_ALTERA_EPCQ)                += altera_epcq.o
>>>>
>>>>  CFLAGS_docg3.o                       += -I$(src)
>>>> diff --git a/drivers/mtd/devices/altera_epcq.c b/drivers/mtd/devices/altera_epcq.c
>>>> new file mode 100644
>>>> index 0000000..09213d5
>>>> --- /dev/null
>>>> +++ b/drivers/mtd/devices/altera_epcq.c
>>>> @@ -0,0 +1,804 @@
>>>> +/*
>>>> + * Copyright (C) 2014 Altera Corporation. All rights reserved
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or modify it
>>>> + * under the terms and conditions of the GNU General Public License,
>>>> + * version 2, as published by the Free Software Foundation.
>>>> + *
>>>> + * This program is distributed in the hope 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.
>>>> + *
>>>> + * You should have received a copy of the GNU General Public License along with
>>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>>> + */
>>>> +
>>>> +#include <linux/clk.h>
>>>> +#include <linux/delay.h>
>>>> +#include <linux/device.h>
>>>> +#include <linux/err.h>
>>>> +#include <linux/errno.h>
>>>> +#include <linux/interrupt.h>
>>>> +#include <linux/io.h>
>>>> +#include <linux/ioport.h>
>>>> +#include <linux/jiffies.h>
>>>> +#include <linux/kernel.h>
>>>> +#include <linux/log2.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/mtd/mtd.h>
>>>> +#include <linux/mtd/partitions.h>
>>>> +#include <linux/mutex.h>
>>>> +#include <linux/of.h>
>>>> +#include <linux/of_address.h>
>>>> +#include <linux/param.h>
>>>> +#include <linux/platform_device.h>
>>>> +#include <linux/pm.h>
>>>> +#include <linux/sched.h>
>>>> +#include <linux/slab.h>
>>>> +#include <linux/wait.h>
>>>> +
>>>> +#include "altera_epcq.h"
>>>> +
>>>> +/* data structure to maintain flash ids from different vendors */
>>>> +struct flash_device {
>>>> +     char *name;
>>>> +     bool is_epcs;
>>>> +     u32 device_id;
>>>> +     uint32_t sectorsize_inbytes;
>>>> +     uint64_t size_inbytes;
>>>> +     u32 pagesize;
>>>> +};
>>>> +
>>>> +#define FLASH_ID(_n, _is_epcs, _id, _ssize, _size, _psize)   \
>>>> +{                            \
>>>> +     .name = (_n),           \
>>>> +     .is_epcs = (_is_epcs),          \
>>>> +     .device_id = (_id),     \
>>>> +     .sectorsize_inbytes = (_ssize), \
>>>> +     .size_inbytes = (_size),        \
>>>> +     .pagesize = (_psize),   \
>>>> +}
>>>> +
>>>> +static struct flash_device flash_devices[] = {
>>>> +     FLASH_ID("epcq16"    , 0, 0x15, 0x10000, 0x200000, 0x100),
>>>> +     FLASH_ID("epcq32"    , 0, 0x16, 0x10000, 0x400000, 0x100),
>>>> +     FLASH_ID("epcq64"    , 0, 0x17, 0x10000, 0x800000, 0x100),
>>>> +     FLASH_ID("epcq128"   , 0, 0x18, 0x10000, 0x1000000, 0x100),
>>>> +     FLASH_ID("epcq256"   , 0, 0x19, 0x10000, 0x2000000, 0x100),
>>>> +     FLASH_ID("epcq512"   , 0, 0x20, 0x10000, 0x4000000, 0x100),
>>>> +
>>>> +     FLASH_ID("epcs16"    , 1, 0x14, 0x10000, 0x200000, 0x100),
>>>> +     FLASH_ID("epcs64"    , 1, 0x16, 0x10000, 0x800000, 0x100),
>>>> +     FLASH_ID("epcs128"   , 1, 0x18, 0x40000, 0x1000000, 0x100),
>>>> +};
>>>> +
>>>> +static inline struct altera_epcq_flash *get_flash_data(struct mtd_info *mtd)
>>>> +{
>>>> +     return container_of(mtd, struct altera_epcq_flash, mtd);
>>>> +}
>>>> +
>>>> +static u32 altera_epcq_read_sr(struct altera_epcq *dev)
>>>> +{
>>>> +     return readl(dev->csr_base + EPCQ_SR_REG);
>>>> +}
>>>> +
>>>> +static int altera_epcq_wait_till_ready(struct altera_epcq *dev)
>>>> +{
>>>> +     unsigned long finish;
>>>> +     int status;
>>>> +
>>>> +     finish = jiffies + EPCQ_MAX_TIME_OUT;
>>>> +     do {
>>>> +             status = altera_epcq_read_sr(dev);
>>>> +             if (status < 0)
>>>> +                     return status;
>>>> +             else if (!(status & EPCQ_SR_WIP))
>>>> +                     return 0;
>>>> +
>>>> +             cond_resched();
>>>> +     } while (!time_after_eq(jiffies, finish));
>>>> +
>>>> +     dev_err(&dev->pdev->dev, "epcq controller is busy, timeout\n");
>>>> +     return -EBUSY;
>>>> +}
>>>> +
>>>> +static int get_flash_index(u32 flash_id, bool is_epcs)
>>>> +{
>>>> +     int index;
>>>> +
>>>> +     for (index = 0; index < ARRAY_SIZE(flash_devices); index++) {
>>>> +             if (flash_devices[index].device_id == flash_id &&
>>>> +                 flash_devices[index].is_epcs == is_epcs)
>>>> +                     return index;
>>>> +     }
>>>> +
>>>> +     /* Memory chip is not listed and not supported */
>>>> +     return -ENODEV;
>>>> +}
>>>> +
>>>> +static int altera_epcq_write_erase_check(struct altera_epcq *dev,
>>>> +                                      bool write_erase)
>>>> +{
>>>> +     u32 val;
>>>> +     u32 mask;
>>>> +
>>>> +     if (write_erase)
>>>> +             mask = EPCQ_ISR_ILLEGAL_WRITE_MASK;
>>>> +     else
>>>> +             mask = EPCQ_ISR_ILLEGAL_ERASE_MASK;
>>>> +
>>>> +     val = readl(dev->csr_base + EPCQ_ISR_REG);
>>>> +     if (val & mask) {
>>>> +             dev_err(&dev->pdev->dev,
>>>> +                     "write/erase failed, sector might be protected\n");
>>>> +             /*clear this status for next use*/
>>>> +             writel(val, dev->csr_base + EPCQ_ISR_REG);
>>>> +             return -EIO;
>>>> +     }
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static int altera_epcq_erase_chip(struct mtd_info *mtd)
>>>> +{
>>>> +     int ret;
>>>> +     u32 val;
>>>> +     struct altera_epcq *dev = mtd->priv;
>>>> +
>>>> +     /* Wait until finished previous write command. */
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>
>>> This pattern is pretty silly. We don't need to have every operation
>>> check the status of the previous operation. Each operation should check
>>> itself. You obviously copied this one... but we recently changed that:
>>>
>>>     commit dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3
>>>     Author: Brian Norris <computersforpeace@gmail.com>
>>>     Date:   Wed Aug 6 18:16:57 2014 -0700
>>>
>>>         mtd: spi-nor: move "wait-till-ready" checks into erase/write
>>>         functions
>>>
>>>
>>>     https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3
>>>
Noted
>>>> +     if (ret)
>>>> +             return ret;
>>>> +
>>>> +     /* erase chip. */
>>>> +     val = EPCQ_MEM_OP_BULK_ERASE_CMD;
>>>> +     writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
>>>> +
>>>> +     /* Wait until finished previous write command. */
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>> +     if (ret)
>>>> +             return ret;
>>>> +
>>>> +      /* check whether write triggered a illegal write interrupt */
>>>> +     ret = altera_epcq_write_erase_check(dev, 0);
>>>> +     if (ret < 0)
>>>> +             return ret;
>>>> +
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static int altera_epcq_addr_to_sector(struct altera_epcq_flash *flash,
>>>> +                                   int addr_offset)
>>>> +{
>>>> +     int sector = 0;
>>>> +     int i;
>>>> +
>>>> +     for (i = 0; i < flash->num_sectors; i++) {
>>>> +             if (addr_offset >= i * flash->mtd.erasesize && addr_offset <
>>>> +                 (i * flash->mtd.erasesize + flash->mtd.erasesize)) {
>>>> +                     sector = i;
>>>> +                     return sector;
>>>> +             }
>>>> +     }
>>>
>>> Uh, really? Am I crazy, or shouldn't this whole function just be a
>>> really simple arithmetic operation? Like:
>>>
>>>         return addr_offset >> mtd->erasesize_shift;
>>>
My bad. This is so much easier this way.
>>>> +     return -1;
>>>> +}
>>>> +
>>>> +static int altera_epcq_erase_sector(struct mtd_info *mtd,
>>>> +                                 int addr_offset)
>>>> +{
>>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>>> +     struct altera_epcq *dev = mtd->priv;
>>>> +     u32 val;
>>>> +     int ret;
>>>> +     int sector_value;
>>>> +
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>> +     if (ret)
>>>> +             return ret;
>>>> +
>>>> +     sector_value = altera_epcq_addr_to_sector(flash, addr_offset);
>>>> +
>>>> +     /* sanity check that block_offset is a valid sector number */
>>>> +     if (sector_value < 0)
>>>> +             return -EINVAL;
>>>> +
>>>> +     /* sector value should occupy bits 17:8 */
>>>> +     val = (sector_value << 8) & EPCQ_MEM_OP_SECTOR_VALUE_MASK;
>>>> +
>>>> +     /* sector erase commands occupies lower 2 bits */
>>>> +     val |= EPCQ_MEM_OP_SECTOR_ERASE_CMD;
>>>> +
>>>> +     /* write sector erase command to EPCQ_MEM_OP register*/
>>>> +     writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
>>>> +
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>> +     if (ret)
>>>> +             return ret;
>>>> +
>>>> +      /* check whether write triggered a illegal write interrupt */
>>>
>>> ^^ extra space here? You have <TAB><SPACE>/* check [...]
>>>
Noted :P
>>>> +     ret = altera_epcq_write_erase_check(dev, 0);
>>>> +     if (ret < 0)
>>>> +             return ret;
>>>> +
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static int altera_epcq_erase(struct mtd_info *mtd, struct erase_info *e_info)
>>>> +{
>>>> +     u32 addr;
>>>> +     int ret;
>>>> +     u32 len;
>>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>>> +     struct altera_epcq *dev = mtd->priv;
>>>> +
>>>> +     addr = e_info->addr;
>>>> +     len = e_info->len;
>>>> +
>>>> +     if (flash->bank > dev->num_flashes - 1) {
>>>> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
>>>> +             return -EINVAL;
>>>> +     }
>>>> +     mutex_lock(&flash->lock);
>>>> +
>>>> +     /*erase whole chip*/
>>>> +     if (len == mtd->size) {
>>>> +             if (altera_epcq_erase_chip(mtd)) {
>>>> +                     e_info->state = MTD_ERASE_FAILED;
>>>> +                     mutex_unlock(&flash->lock);
>>>> +                     return -EIO;
>>>> +             }
>>>> +     /*"sector"-at-a-time erase*/
>>>> +     } else {
>>>> +             while (len) {
>>>> +                     ret = altera_epcq_erase_sector(mtd, addr);
>>>> +                     if (ret) {
>>>> +                             e_info->state = MTD_ERASE_FAILED;
>>>> +                             mutex_unlock(&flash->lock);
>>>> +                             return -EIO;
>>>> +                     }
>>>> +                     addr += mtd->erasesize;
>>>> +                     if (len < mtd->erasesize)
>>>> +                             len = 0;
>>>> +                     else
>>>> +                             len -= mtd->erasesize;
>>>> +             }
>>>> +     }
>>>> +     mutex_unlock(&flash->lock);
>>>> +     e_info->state = MTD_ERASE_DONE;
>>>> +     mtd_erase_callback(e_info);
>>>> +
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static int altera_epcq_read(struct mtd_info *mtd, loff_t from, size_t len,
>>>> +                         size_t *retlen, u8 *buf)
>>>> +{
>>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>>> +     struct altera_epcq *dev = mtd->priv;
>>>> +     void *src;
>>>> +     int ret = 0;
>>>> +
>>>> +     if (!flash || !dev)
>>>> +             return -ENODEV;
>>>
>>> Why would either of these be 0? If they are, you have much bigger
>>> problems, since you aren't checking these almost anywhere else. And the
>>> !flash check is pretty odd, since get_flash_data() is using
>>> container_of(), which is not likely to give you exactly 0...
>>>
Ok
>>>> +
>>>> +     if (flash->bank > dev->num_flashes - 1) {
>>>> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
>>>> +             return -EINVAL;
>>>> +     }
>>>> +
>>>> +     src = dev->data_base + from;
>>>> +
>>>> +     mutex_lock(&flash->lock);
>>>> +     /* wait till previous write/erase is done. */
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>> +     if (ret)
>>>> +             goto err;
>>>> +
>>>> +     memcpy_fromio(buf, (u8 *)src, len);
>>>
>>> Drop the cast. Also, you get sparse warnings because you're dropping the
>>> __iomem. Why not just this?
>>>
>>>         memcpy_fromio(buf, dev->data_base + from, len);
>>>
>>> (BTW, you might consider running sparse on all your code. See
>>> Documentation/sparse.txt. It tells you how to get it, but you can often
>>> just download it through your distro's package manager.)
>>>
Noted
>>>> +     *retlen = len;
>>>> +
>>>> +err:
>>>> +     mutex_unlock(&flash->lock);
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static int altera_epcq_write(struct mtd_info *mtd, loff_t to, size_t len,
>>>> +                          size_t *retlen, const u8 *buf)
>>>> +{
>>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>>> +     struct altera_epcq *dev = mtd->priv;
>>>> +     void *dest;
>>>> +     int ret = 0;
>>>> +
>>>> +     if (!flash || !dev)
>>>> +             return -ENODEV;
>>>
>>> Also here. I don't think you need these checks.
>>>
Noted
>>>> +
>>>> +     if (flash->bank > dev->num_flashes - 1) {
>>>> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
>>>> +             return -EINVAL;
>>>> +     }
>>>> +     dest = dev->data_base + to;
>>>> +
>>>> +     mutex_lock(&flash->lock);
>>>> +
>>>> +     /* wait until finished previous write command. */
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>> +     if (ret)
>>>> +             goto err;
>>>> +
>>>> +     memcpy_toio(dest, buf, len);
>>>
>>> Similar. Why not just this?
>>>
>>>         memcpy_toio(dev->data_base + to, buf, len);
>>>
Noted
>>>> +     *retlen += len;
>>>> +
>>>> +     /* wait until finished previous write command. */
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>> +     if (ret)
>>>> +             goto err;
>>>> +
>>>> +      /* check whether write triggered a illegal write interrupt */
>>>> +     ret = altera_epcq_write_erase_check(dev, 1);
>>>> +     if (ret < 0)
>>>> +             goto err;
>>>> +
>>>> +err:
>>>> +     mutex_unlock(&flash->lock);
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static int altera_epcq_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
>>>> +{
>>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>>> +     struct altera_epcq *dev = mtd->priv;
>>>> +     uint32_t offset = ofs;
>>>> +     int ret = 0;
>>>> +     u32 sector_start, sector_end;
>>>> +     u32 num_sectors;
>>>> +     u32 mem_op;
>>>> +     unsigned sr_bp = 0;
>>>> +     unsigned sr_tb = 0;
>>>> +
>>>> +     sector_start = altera_epcq_addr_to_sector(flash, offset);
>>>> +     sector_end = altera_epcq_addr_to_sector(flash, offset + len);
>>>> +     num_sectors = flash->num_sectors;
>>>> +     dev_dbg(&dev->pdev->dev,
>>>> +             "%s: num_setor is %u,sector start is %u,sector end is %u\n",
>>>> +             __func__, num_sectors, sector_start, sector_end);
>>>> +
>>>> +     mutex_lock(&flash->lock);
>>>> +     /* wait until finished previous write command. */
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>> +     if (ret)
>>>> +             goto err;
>>>> +
>>>> +     if (sector_start >= num_sectors/2) {
>>>
>>> It's preferable to have spaces around operators. So 'num_sectors / 2'.
>>> The same thing comes up in several other places.
>>>
Noted
>>>> +             if (sector_start < num_sectors-(num_sectors / 4))
>>>> +                     sr_bp = __ilog2_u32(num_sectors);
>>>> +             else if (sector_start < num_sectors-(num_sectors / 8))
>>>> +                     sr_bp = __ilog2_u32(num_sectors) - 1;
>>>> +             else if (sector_start < num_sectors-(num_sectors / 16))
>>>> +                     sr_bp = __ilog2_u32(num_sectors) - 2;
>>>> +             else if (sector_start < num_sectors-(num_sectors / 32))
>>>> +                     sr_bp = __ilog2_u32(num_sectors) - 3;
>>>> +             else if (sector_start < num_sectors-(num_sectors / 64))
>>>> +                     sr_bp = __ilog2_u32(num_sectors) - 4;
>>>> +             else if (sector_start < num_sectors-(num_sectors / 128))
>>>> +                     sr_bp = __ilog2_u32(num_sectors) - 5;
>>>> +             else if (sector_start < num_sectors-(num_sectors / 256))
>>>> +                     sr_bp = __ilog2_u32(num_sectors) - 6;
>>>> +             else if (sector_start < num_sectors-(num_sectors / 512))
>>>> +                     sr_bp = __ilog2_u32(num_sectors) - 7;
>>>> +             else if (sector_start < num_sectors-(num_sectors / 1024))
>>>> +                     sr_bp = __ilog2_u32(num_sectors) - 8;
>>>> +             else
>>>> +                     sr_bp = 0;  /* non area protected */
>>>
>>> Yikes, that's ugly! And I'm not sure it matches the EPCQ doc I found.
>>> I'm pretty sure you can rewrite this if/else-if/else block in about 1
>>> line though.
>>>
Yes, i understand that it looks ugly. But it is the best i can do
since this function has to satisfy for all the supported devices
(http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf, start
from page 19)
>>>> +
>>>> +             if (sr_bp < 0) {
>>>
>>> sr_bp is unsigned, so this is never true.
>>>
Ok.  I will change to int type.
>>>> +                     dev_err(&dev->pdev->dev, "%s: address is out of range\n"
>>>> +                             , __func__);
>>>> +                     ret = -EINVAL;
>>>> +                     goto err;
>>>> +             }
>>>> +             /*set TB = 0*/
>>>> +             sr_tb = 0;
>>>> +
>>>> +     } else {
>>>> +             if (sector_end < 1)
>>>> +                     sr_bp = 1;
>>>> +             else if (sector_end < 2)
>>>> +                     sr_bp = 2;
>>>> +             else if (sector_end < 4)
>>>> +                     sr_bp = 3;
>>>> +             else if (sector_end < 8)
>>>> +                     sr_bp = 4;
>>>> +             else if (sector_end < 16)
>>>> +                     sr_bp = 5;
>>>> +             else if (sector_end < 32)
>>>> +                     sr_bp = 6;
>>>> +             else if (sector_end < 64)
>>>> +                     sr_bp = 7;
>>>> +             else if (sector_end < 128)
>>>> +                     sr_bp = 8;
>>>> +             else if (sector_end < 256)
>>>> +                     sr_bp = 9;
>>>> +             else if (sector_end < 512)
>>>> +                     sr_bp = 10;
>>>> +             else
>>>> +                     sr_bp = 16; /*protect all areas*/
>>>
>>> Again, this is ugly. How about this?
>>>
>>>         sr_bp = fls(sector_end) + 1;
>>>
Oh. I did not know got this fls function. I will apply it.
>>>> +
>>>> +             sr_tb = 1;
>>>> +     }
>>>> +
>>>> +     mem_op = (sr_tb << 12) | (sr_bp << 8);
>>>> +     mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
>>>> +     mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
>>>> +     writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
>>>> +
>>>> +err:
>>>> +     mutex_unlock(&flash->lock);
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static int altera_epcq_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
>>>> +{
>>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>>> +     struct altera_epcq *dev = mtd->priv;
>>>> +
>>>> +     int ret = 0;
>>>> +     u32 mem_op;
>>>> +
>>>> +     mutex_lock(&flash->lock);
>>>> +     /* wait until finished previous write command. */
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>> +     if (ret)
>>>> +             goto err;
>>>> +     dev_info(&dev->pdev->dev, "Unlock all protected area\n");
>>>> +     mem_op = 0;
>>>> +     mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
>>>> +     mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
>>>> +     writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
>>>> +
>>>> +err:
>>>> +     mutex_unlock(&flash->lock);
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static void altera_epcq_chip_select(struct altera_epcq *dev, u32 bank)
>>>> +{
>>>> +     u32 val = 0;
>>>> +
>>>> +     switch (bank) {
>>>> +     case 0:
>>>> +             val = EPCQ_CHIP_SELECT_0;
>>>> +             break;
>>>> +     case 1:
>>>> +             val = EPCQ_CHIP_SELECT_1;
>>>> +             break;
>>>> +     case 2:
>>>> +             val = EPCQ_CHIP_SELECT_2;
>>>> +             break;
>>>> +     default:
>>>> +             return;
>>>> +     }
>>>> +
>>>> +     writel(val, dev->csr_base + EPCQ_CHIP_SELECT_REG);
>>>> +}
>>>> +
>>>> +static int altera_epcq_probe_flash(struct altera_epcq *dev, u32 bank)
>>>> +{
>>>> +     int ret = 0;
>>>> +     u32 val = 0;
>>>> +
>>>> +     mutex_lock(&dev->lock);
>>>> +
>>>> +     /*select bank*/
>>>
>>> Comment spacing, here and elsewhere:
>>>
Ok
>>>         /* select bank */
>>>
>>>> +     altera_epcq_chip_select(dev, bank);
>>>> +
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>> +     if (ret)
>>>> +             goto err;
>>>> +
>>>> +     /* get device sillicon id */
>>>> +     if (dev->is_epcs)
>>>> +             val = readl(dev->csr_base + EPCQ_SID_REG);
>>>> +     else
>>>> +             val = readl(dev->csr_base + EPCQ_RDID_REG);
>>>> +
>>>> +     /* get flash index based on the device list*/
>>>> +     ret = get_flash_index(val, dev->is_epcs);
>>>> +     return 0;
>>>> +err:
>>>> +     mutex_unlock(&dev->lock);
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static int altera_epcq_probe_config_dt(struct platform_device *pdev,
>>>> +                                    struct device_node *np,
>>>> +                                    struct altera_epcq_plat_data *pdata)
>>>> +{
>>>> +     struct device_node *pp = NULL;
>>>> +     struct resource *epcq_res;
>>>> +     int i = 0;
>>>> +     u32 id;
>>>> +
>>>> +     pdata->is_epcs = of_property_read_bool(np, "is-epcs");
>>>> +
>>>> +     epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
>>>> +                                             "csr_base");
>>>> +     if (!epcq_res) {
>>>
>>> devm_ioremap_resource() will check the that 'epcq_res' is valid, so you
>>> don't need this whole 'if' block.
>>>
Ok
>>>> +             dev_err(&pdev->dev, "resource csr base is not defined\n");
>>>> +             return -ENODEV;
>>>> +     }
>>>> +
>>>> +     pdata->csr_base = devm_ioremap_resource(&pdev->dev, epcq_res);
>>>> +     if (IS_ERR(pdata->csr_base)) {
>>>> +             dev_err(&pdev->dev, "%s: ERROR: failed to map csr base\n",
>>>> +                     __func__);
>>>> +             return PTR_ERR(pdata->csr_base);
>>>> +     }
>>>> +
>>>> +     epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
>>>> +                                             "data_base");
>>>> +     if (!epcq_res) {
>>>
>>> Same here.
Ok
>>>
>>>> +             dev_err(&pdev->dev, "resource data base is not defined\n");
>>>> +             return -ENODEV;
>>>> +     }
>>>> +
>>>> +     pdata->data_base = devm_ioremap_resource(&pdev->dev, epcq_res);
>>>> +     if (IS_ERR(pdata->data_base)) {
>>>> +             dev_err(&pdev->dev, "%s: ERROR: failed to map data base\n",
>>>> +                     __func__);
>>>> +             return PTR_ERR(pdata->data_base);
>>>> +     }
>>>> +
>>>> +     pdata->board_flash_info = devm_kzalloc(&pdev->dev,
>>>> +                                            sizeof(*pdata->board_flash_info),
>>>> +                                            GFP_KERNEL);
>>>> +
>>>> +     /* Fill structs for each subnode (flash device) */
>>>> +     while ((pp = of_get_next_child(np, pp))) {
>>>
>>> for_each_available_child_of_node()?
>>>
>>>> +             struct altera_epcq_flash_info *flash_info;
>>>> +
>>>> +             flash_info = &pdata->board_flash_info[i];
>>>
>>> This is never used. Either use it below, or drop the local variable.
>>>
Ok
>>>> +             pdata->np[i] = pp;
>>>> +
>>>> +             /* Read bank id from DT */
>>>> +             if (of_get_property(pp, "reg", &id))
>>>
>>> Is this property optional? Your DT binding doc doesn't make it clear,
>>> but it seems like a property which would be wise to require (i.e., not
>>> optional).
>>>
>>>> +                     pdata->board_flash_info[i].bank = id;
>>>> +             i++;
>>>> +     }
>>>> +     pdata->num_flashes = i;
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static int altera_epcq_setup_banks(struct platform_device *pdev,
>>>> +                                u32 bank, struct device_node *np,
>>>> +                                struct altera_epcq_plat_data *pdata)
>>>> +{
>>>> +     struct altera_epcq *dev = platform_get_drvdata(pdev);
>>>> +     struct mtd_part_parser_data ppdata = {};
>>>> +     struct altera_epcq_flash_info *flash_info;
>>>> +     struct altera_epcq_flash *flash;
>>>> +     struct mtd_partition *parts = NULL;
>>>
>>> Drop this variable. Just use NULL directly below.
>>>
>>>> +     int count = 0;
>>>
>>> Same. Just use 0 below.
>>>
Ok
>>>> +     int flash_index;
>>>> +     int ret = 0;
>>>> +     uint64_t size;
>>>> +     uint32_t sector_size;
>>>> +
>>>> +     flash_info = &pdata->board_flash_info[bank];
>>>> +     if (!flash_info)
>>>> +             return -ENODEV;
>>>> +
>>>> +     if (bank > pdata->num_flashes - 1)
>>>> +             return -EINVAL;
>>>> +
>>>> +     flash = devm_kzalloc(&pdev->dev, sizeof(*flash), GFP_KERNEL);
>>>> +     if (!flash)
>>>> +             return -ENOMEM;
>>>> +     flash->bank = bank;
>>>> +
>>>> +     mutex_init(&flash->lock);
>>>> +
>>>> +     /* verify whether flash is really present on board */
>>>> +     flash_index = altera_epcq_probe_flash(dev, bank);
>>>> +     if (flash_index < 0) {
>>>> +             dev_info(&dev->pdev->dev, "epcq:flash%d not found\n",
>>>> +                      flash->bank);
>>>> +             return flash_index;
>>>> +     }
>>>> +
>>>> +     dev->flash[bank] = flash;
>>>> +
>>>> +     size = flash_devices[flash_index].size_inbytes;
>>>> +     sector_size = flash_devices[flash_index].sectorsize_inbytes;
>>>> +     /*use do_div instead of plain div to avoid linker err*/
>>>> +     do_div(size, sector_size);
>>>> +     flash->num_sectors = size;
>>>> +
>>>> +     /*mtd framework */
>>>> +     flash->mtd.priv = dev;
>>>> +     flash->mtd.name = flash_devices[flash_index].name;
>>>> +     flash->mtd.type = MTD_NORFLASH;
>>>> +     flash->mtd.writesize = 1;
>>>> +     flash->mtd.flags = MTD_CAP_NORFLASH;
>>>> +     flash->mtd.size = flash_devices[flash_index].size_inbytes;
>>>> +     flash->mtd.erasesize = flash_devices[flash_index].sectorsize_inbytes;
>>>> +     flash->mtd._erase = altera_epcq_erase;
>>>> +     flash->mtd._read = altera_epcq_read;
>>>> +     flash->mtd._write = altera_epcq_write;
>>>> +     flash->mtd._lock = altera_epcq_lock;
>>>> +     flash->mtd._unlock = altera_epcq_unlock;
>>>> +
>>>> +     dev_info(&pdev->dev, "mtd .name=%s .size=0x%llx (%lluM)\n",
>>>> +              flash->mtd.name, (long long)flash->mtd.size,
>>>> +              (long long)(flash->mtd.size >> 20));
>>>> +
>>>> +     dev_info(&pdev->dev, ".erasesize = 0x%x(%uK)\n",
>>>> +              flash->mtd.erasesize, flash->mtd.erasesize >> 10);
>>>> +
>>>> +     ppdata.of_node = np;
>>>> +
>>>> +     ret = mtd_device_parse_register(&flash->mtd, NULL, &ppdata, parts,
>>>> +                                     count);
>>>> +     if (ret) {
>>>> +             dev_err(&pdev->dev, "Err MTD partition=%d\n", ret);
>>>> +             goto err;
>>>
>>> You don't want to unregister a MTD that failed to register. The MTD core
>>> code should handle that itself.
>>>
>>> Instead of this if (ret) {} block, I'd just make this:
>>>
>>>         return mtd_device_parse_register(&flash->mtd, NULL, &ppdata, NULL, 0);
>>>
Ok
>>>> +     }
>>>> +
>>>> +     return 0;
>>>> +
>>>> +err:
>>>> +     mtd_device_unregister(&flash->mtd);
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static int altera_epcq_probe(struct platform_device *pdev)
>>>> +{
>>>> +     struct device_node *np = pdev->dev.of_node;
>>>> +     struct altera_epcq_plat_data *pdata = NULL;
>>>> +     struct altera_epcq *dev;
>>>> +     int ret = 0;
>>>> +     int i;
>>>> +
>>>> +     if (!np) {
>>>> +             ret = -ENODEV;
>>>> +             dev_err(&pdev->dev, "no device found\n");
>>>> +             goto err;
>>>> +     }
>>>> +
>>>> +     pdata = devm_kzalloc(&pdev->dev,
>>>> +                          sizeof(struct altera_epcq_plat_data),
>>>> +                          GFP_KERNEL);
>>>> +
>>>> +     if (!pdata) {
>>>> +             ret = -ENOMEM;
>>>> +             dev_err(&pdev->dev, "%s: ERROR: no memory\n", __func__);
>>>
>>> Unnecessary print. The MM code will print plenty of info if you're
>>> out of memory.
>>>
>>>> +             goto err;
>>>> +     }
>>>> +     ret = altera_epcq_probe_config_dt(pdev, np, pdata);
>>>> +     if (ret) {
>>>> +             ret = -ENODEV;
>>>> +             dev_err(&pdev->dev, "probe fail\n");
>>>> +             goto err;
>>>> +     }
>>>> +
>>>> +     dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
>>>> +     if (!dev) {
>>>> +             ret = -ENOMEM;
>>>> +             dev_err(&pdev->dev, "mem alloc fail\n");
>>>
>>> Same here.
>>>
>>>> +             goto err;
>>>> +     }
>>>> +     mutex_init(&dev->lock);
>>>> +     dev->pdev = pdev;
>>>> +     dev->is_epcs = pdata->is_epcs;
>>>> +     dev->csr_base = pdata->csr_base;
>>>> +     dev->data_base = pdata->data_base;
>>>> +
>>>> +     /*check number of flashes*/
>>>> +     dev->num_flashes = pdata->num_flashes;
>>>> +     if (dev->num_flashes > MAX_NUM_FLASH_CHIP) {
>>>> +             dev_err(&pdev->dev, "exceeding max number of flashes\n");
>>>> +             dev->num_flashes = MAX_NUM_FLASH_CHIP;
>>>> +     }
>>>> +
>>>> +     /* check clock*/
>>>> +     dev->clk = devm_clk_get(&pdev->dev, NULL);
>>>> +     if (IS_ERR(dev->clk)) {
>>>> +             ret = PTR_ERR(dev->clk);
>>>> +             goto err;
>>>> +     }
>>>> +     ret = clk_prepare_enable(dev->clk);
>>>> +     if (ret)
>>>> +             goto err;
>>>> +
>>>> +     platform_set_drvdata(pdev, dev);
>>>> +
>>>> +     /* loop for each serial flash which is connected to epcq */
>>>> +     for (i = 0; i < dev->num_flashes; i++) {
>>>> +             ret = altera_epcq_setup_banks(pdev, i, pdata->np[i], pdata);
>>>> +             if (ret) {
>>>> +                     dev_err(&pdev->dev, "bank setup failed\n");
>>>> +                     goto err_bank_setup;
>>>> +             }
>>>> +     }
>>>> +
>>>> +     return 0;
>>>> +
>>>> +err_bank_setup:
>>>> +     clk_disable_unprepare(dev->clk);
>>>> +err:
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static int altera_epcq_remove(struct platform_device *pdev)
>>>> +{
>>>> +     struct altera_epcq *dev;
>>>> +     struct altera_epcq_flash *flash;
>>>> +     int ret, i;
>>>> +
>>>> +     dev = platform_get_drvdata(pdev);
>>>> +
>>>> +     /* clean up for all nor flash */
>>>> +     for (i = 0; i < dev->num_flashes; i++) {
>>>> +             flash = dev->flash[i];
>>>> +             if (!flash)
>>>> +                     continue;
>>>> +
>>>> +             /* clean up mtd stuff */
>>>> +             ret = mtd_device_unregister(&flash->mtd);
>>>> +             if (ret)
>>>> +                     dev_err(&pdev->dev, "error removing mtd\n");
>>>> +     }
>>>> +
>>>> +     clk_disable_unprepare(dev->clk);
>>>> +
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +#ifdef CONFIG_PM
>>>
>>> Make this CONFIG_PM_SLEEP.
>>>
>>>> +static int altera_epcq_suspend(struct device *dev)
>>>> +{
>>>> +     struct altera_epcq *sdev = dev_get_drvdata(dev);
>>>> +
>>>> +     clk_disable_unprepare(sdev->clk);
>>>> +
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static int altera_epcq_resume(struct device *dev)
>>>> +{
>>>> +     struct altera_epcq *sdev = dev_get_drvdata(dev);
>>>> +     int ret = -EPERM;
>>>
>>> Drop 'ret'.
>>>
>>>> +
>>>> +     ret = clk_prepare_enable(sdev->clk);
>>>
>>> Just:
>>>
>>>         return clk_prepare_enable(sdev->clk);
>>>
>>>> +
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static SIMPLE_DEV_PM_OPS(altera_epcq_pm_ops, altera_epcq_suspend,
>>>> +                      altera_epcq_resume);
>>>> +#endif
>>>> +
>>>> +static const struct of_device_id altera_epcq_id_table[] = {
>>>> +     { .compatible = "altr,epcq-1.0" },
>>>> +     {}
>>>> +};
>>>> +MODULE_DEVICE_TABLE(of, altera_epcq_id_table);
>>>> +
>>>> +static struct platform_driver altera_epcq_driver = {
>>>> +     .driver = {
>>>> +             .name = ALTERA_EPCQ_RESOURCE_NAME,
>>>> +             .bus = &platform_bus_type,
>>>> +             .owner = THIS_MODULE,
>>>
>>> The .owner assignment is no longer necessary.
>>>
>>>> +             .of_match_table = altera_epcq_id_table,
>>>> +#ifdef CONFIG_PM
>>>
>>> Also CONFIG_PM_SLEEP.
>>>
>>>> +             .pm = &altera_epcq_pm_ops,
>>>> +#endif
>>>> +     },
>>>> +     .probe = altera_epcq_probe,
>>>> +     .remove = altera_epcq_remove,
>>>> +};
>>>> +module_platform_driver(altera_epcq_driver);
>>>> +
>>>> +MODULE_AUTHOR("Viet Nga Dao <vndao@altera.com>");
>>>> +MODULE_DESCRIPTION("MTD Altera EPCQ Driver");
>>>> +MODULE_LICENSE("GPL v2");
>>>> diff --git a/drivers/mtd/devices/altera_epcq.h b/drivers/mtd/devices/altera_epcq.h
>>>> new file mode 100644
>>>> index 0000000..c3d15e5
>>>> --- /dev/null
>>>> +++ b/drivers/mtd/devices/altera_epcq.h
>>>> @@ -0,0 +1,130 @@
>>>> +/*
>>>> + * Copyright (C) 2014 Altera Corporation. All rights reserved
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or modify it
>>>> + * under the terms and conditions of the GNU General Public License,
>>>> + * version 2, as published by the Free Software Foundation.
>>>> + *
>>>> + * This program is distributed in the hope 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.
>>>> + *
>>>> + * You should have received a copy of the GNU General Public License along with
>>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>>> + */
>>>> +
>>>> +#ifndef __ALTERA_ECPQ_H
>>>> +#define __ALTERA_ECPQ_H
>>>> +
>>>> +#define ALTERA_EPCQ_RESOURCE_NAME        "altera_epcq"
>>>> +/* max possible slots for serial flash chip in the EPCQ controller */
>>>> +#define MAX_NUM_FLASH_CHIP   3
>>>> +
>>>> +/* macro to define partitions for flash devices */
>>>> +#define DEFINE_PARTS(n, of, s)               \
>>>
>>> This macro is not used anywhere. Drop it?
>>>
Ok
>>>> +{                                    \
>>>> +     .name = n,                      \
>>>> +     .offset = of,                   \
>>>> +     .size = s,                      \
>>>> +}
>>>> +
>>>> +struct altera_epcq_flash_info {
>>>> +     u32 bank;
>>>> +     struct mtd_partition *partitions;
>>>> +     int nr_partitions;
>>>> +};
>>>> +
>>>> +struct altera_epcq_plat_data {
>>>> +     void __iomem *csr_base;
>>>> +     void __iomem *data_base;
>>>> +     bool is_epcs;
>>>> +     u32 num_flashes;
>>>> +     struct altera_epcq_flash_info *board_flash_info;
>>>> +     struct device_node *np[MAX_NUM_FLASH_CHIP];
>>>> +};
>>>> +
>>>> +struct altera_epcq {
>>>> +     struct clk *clk;
>>>> +     bool is_epcs;
>>>> +     struct mutex lock;      /*device lock*/
>>>> +     void __iomem *csr_base;
>>>> +     void __iomem *data_base;
>>>> +     struct platform_device *pdev;
>>>> +     u32 num_flashes;
>>>> +     struct altera_epcq_flash *flash[MAX_NUM_FLASH_CHIP];
>>>> +};
>>>> +
>>>> +struct altera_epcq_flash {
>>>> +     u32 bank;
>>>> +     u32 num_sectors;
>>>> +     u32 num_parts;
>>>> +     struct mtd_partition *parts;
>>>> +     struct mutex lock;      /*flash lock*/
>>>> +     struct mtd_info mtd;
>>>> +};
>>>> +
>>>> +/* Define max times to check status register before we give up. */
>>>> +#define EPCQ_MAX_TIME_OUT                    (40 * HZ)
>>>> +
>>>> +/* defines for status register */
>>>> +#define EPCQ_SR_REG                          0x0
>>>> +#define EPCQ_SR_WIP_MASK                     0x00000001
>>>> +#define EPCQ_SR_WIP                          0x1
>>>> +#define EPCQ_SR_WEL                          0x2
>>>> +#define EPCQ_SR_BP0                          0x4
>>>> +#define EPCQ_SR_BP1                          0x8
>>>> +#define EPCQ_SR_BP2                          0x10
>>>> +#define EPCQ_SR_BP3                          0x40
>>>> +#define EPCQ_SR_TB                           0x20
>>>> +
>>>> +/* defines for device id register */
>>>> +#define EPCQ_SID_REG                         0x4
>>>> +#define EPCQ_RDID_REG                                0x8
>>>> +#define EPCQ_RDID_MASK                               0x000000FF
>>>> +/*
>>>> + * EPCQ_MEM_OP register offset
>>>> + *
>>>> + * The EPCQ_MEM_OP register is used to do memory protect and erase operations
>>>> + *
>>>> + */
>>>> +#define EPCQ_MEM_OP_REG                              0xC
>>>> +
>>>> +#define EPCQ_MEM_OP_CMD_MASK                 0x00000003
>>>> +#define EPCQ_MEM_OP_BULK_ERASE_CMD           0x00000001
>>>> +#define EPCQ_MEM_OP_SECTOR_ERASE_CMD         0x00000002
>>>> +#define EPCQ_MEM_OP_SECTOR_PROTECT_CMD               0x00000003
>>>> +#define EPCQ_MEM_OP_SECTOR_VALUE_MASK                0x0003FF00
>>>> +#define EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK        0x00001F00
>>>> +#define EPCQ_MEM_OP_SECTOR_PROTECT_SHIFT     8
>>>> +/*
>>>> + * EPCQ_ISR register offset
>>>> + *
>>>> + * The EPCQ_ISR register is used to determine whether an invalid write or erase
>>>> + * operation trigerred an interrupt
>>>> + *
>>>> + */
>>>> +#define EPCQ_ISR_REG                         0x10
>>>> +
>>>> +#define EPCQ_ISR_ILLEGAL_ERASE_MASK          0x00000001
>>>> +#define EPCQ_ISR_ILLEGAL_WRITE_MASK          0x00000002
>>>> +
>>>> +/*
>>>> + * EPCQ_IMR register offset
>>>> + *
>>>> + * The EPCQ_IMR register is used to mask the invalid erase or the invalid write
>>>> + * interrupts.
>>>> + *
>>>> + */
>>>> +#define EPCQ_IMR_REG                         0x14
>>>> +#define EPCQ_IMR_ILLEGAL_ERASE_MASK          0x00000001
>>>> +
>>>> +#define EPCQ_IMR_ILLEGAL_WRITE_MASK          0x00000002
>>>> +
>>>> +#define EPCQ_CHIP_SELECT_REG                 0x18
>>>> +#define EPCQ_CHIP_SELECT_MASK                        0x00000007
>>>> +#define EPCQ_CHIP_SELECT_0                   0x00000001
>>>> +#define EPCQ_CHIP_SELECT_1                   0x00000002
>>>> +#define EPCQ_CHIP_SELECT_2                   0x00000004
>>>> +
>>>> +#endif /* __ALTERA_ECPQ_H */
>>>

Nga

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-01-27  6:53     ` Viet Nga Dao
  0 siblings, 0 replies; 39+ messages in thread
From: Viet Nga Dao @ 2015-01-27  6:53 UTC (permalink / raw)
  To: Viet Nga Dao, Brian Norris
  Cc: David Woodhouse, linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, nga_chi86

On Tue, Jan 20, 2015 at 10:05 AM, Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org> wrote:
> On Thu, Jan 15, 2015 at 5:27 PM, Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org> wrote:
>> On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
>> <computersforpeace-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>>> On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org wrote:
>>>> From: Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org>
>>>>
>>>> Altera EPCQ Controller is a soft IP which enables access to Altera EPCQ and
>>>> EPCS flash chips. This patch adds driver for these devices.
>>>>
>>>> Signed-off-by: Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org>
>>>
>>> This drivers seems awfully similar to (and so I infer it is likely
>>> copy-and-pasted from) m25p80.c / spi-nor.c. Do you think it can be
>>> rewritten as a SPI NOR driver, under drivers/mtd/spi-nor/ ? It looks
>>> like these flash share most (all?) the same basic opcodes.
>>>
>> For Altera EPCQ flashes, almost operations are performed underline
>> hardware.
>
>Right, that's understandable. But that's what spi-nor.c was designed
>for: implementing hardware-specific functionality that is targeted
>directly at SPI flash. Did you take a look at the callbacks available in
>'struct spi_nor'?
>
>>Software only able to perform the following through
>> registers:
>>  -  read status register
>>  -  read id
>>  -  write status registers bit SR_BP0,SR_BP1, SR_BP2,SR_BP3, SR_TB
>> (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf)
>
>OK
>
>> For read/write data: all the operations like QUAD_READ/WRITE,
>> FAST_READ/WRITE are handled by hardware as well. From software point
>> of view, there is no difference between these 2 modes.
>
>OK, so you don't have to hook up the dual/quad mode infrastructure. And
>you'd just implement dead-simple spi_nor.{read,write,erase}() callbacks.
>
>> That is why if rewrite the drivers to follow spi-nor structure, it
>> will require extra decoding works for the only few used opcodes.
>>
>I think you'd only have some very trivial work here.
>
>There would be some small work to reintroduce a properly-replaceable ID
>table, and callbacks like ->lock() and ->unlock(), but those should be
>implemented in spi-nor.c sooner or later anyway.
>

I am trying to modify the code to follow your recommendation. However,
for lock and unlock functions, i have to implement it in spi_nor.c ,
am i right? If yes, currently we already have existing spi_nor_lock
and spi_nor_unlock functions but they could not apply for my driver.
Should i create a new functions named altera_epcq_lock and unlock and
then map it to callback functions or i should the put  those code
sunder existing spi_nor_lock/unlock?

>Anyway, if you take another look at spi-nor.{c,h} and determine that
>it's too difficult, then I suppose I don't mind accepting your driver
>under its current design. Your hardware is pretty esoteric anyway, the
>driver is still pretty simple, and it'll never be supporting any common
>SPI NOR vendors (right?), so it's not too big of a maintenance problem,
>I expect. But I do want to make sure that we don't copy/paste to repeat
>the mistakes of old drivers. As I noted already, your driver inherited
>some of the quirks of the old m25p80.c code.
>
>So please, give drivers/mtd/spi-nor/ another look, and then we can
>resume this discussion.
>>>> ---
>>>>  .../devicetree/bindings/mtd/altera_epcq.txt        |   45 ++
>>>>  drivers/mtd/devices/Kconfig                        |   12 +
>>>>  drivers/mtd/devices/Makefile                       |    2 +-
>>>>  drivers/mtd/devices/altera_epcq.c                  |  804 ++++++++++++++++++++
>>>>  drivers/mtd/devices/altera_epcq.h                  |  130 ++++
>>>>  5 files changed, 992 insertions(+), 1 deletions(-)
>>>>  create mode 100644 Documentation/devicetree/bindings/mtd/altera_epcq.txt
>>>>  create mode 100644 drivers/mtd/devices/altera_epcq.c
>>>>  create mode 100644 drivers/mtd/devices/altera_epcq.h
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>>>> new file mode 100644
>>>> index 0000000..d14f50e
>>>> --- /dev/null
>>>> +++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>>>> @@ -0,0 +1,45 @@
>>>> +* MTD Altera EPCQ driver
>>>> +
>>>> +Required properties:
>>>> +- compatible: Should be "altr,epcq-1.0"
>>>> +- reg: Address and length of the register set  for the device. It contains
>>>> +  the information of registers in the same order as described by reg-names
>>>> +- reg-names: Should contain the reg names
>>>> +  "csr_base": Should contain the register configuration base address
>>>> +  "data_base": Should contain the data base address
>>>> +- is-epcs: boolean type.
>>>> +             If present, the device contains EPCS flashes.
>>>> +             Otherwise, it contains EPCQ flashes.
>>>> +- #address-cells: Must be <1>.
>>>> +- #size-cells: Must be <0>.
>>>> +- flash device tree subnode, there must be a node with the following fields:
>>>
>>> These subnodes definitely require a 'compatible' string. Perhaps they
>>> should be used to differentiate EPCS vs. EPCQ. Does "is-epcs" really
>>> need to be in the top-level controller node?
>>>
>>>> +     - reg: Should contain the flash id
>>>
>>> Should, or must? (This question is relevant, because you seem to make it
>>> optional in your code.) And what does the "flash ID" mean? It seems like
>>> you're using as a chip-select or bank index.
>>>
Yes, this is used for chip select. It is required, not optional. This
to ID for each flash in the device
>>>> +     - #address-cells: please refer to /mtd/partition.txt
>>>> +     - #size-cells: please refer to /mtd/partition.txt
>>>> +     For partitions inside each flash, please refer to /mtd/partition.txt
>>>> +
>>>> +Example:
>>>> +
>>>> +                     epcq_controller_0: epcq@0x000000000 {
>>>> +                             compatible = "altr,epcq-1.0";
>>>> +                             reg = <0x00000001 0x00000000 0x00000020>,
>>>> +                                     <0x00000000 0x00000000 0x02000000>;
>>>> +                             reg-names = "csr_base", "data_base";
>>>> +                             #address-cells = <1>;
>>>> +                             #size-cells = <0>;
>>>> +                             flash0: epcq256@0 {
>>>> +                                     reg = <0>;
>>>> +                                     #address-cells = <1>;
>>>> +                                     #size-cells = <1>;
>>>> +                                     partition@0 {
>>>> +                                             /* 16 MB for raw data. */
>>>> +                                             label = "EPCQ Flash 0 raw data";
>>>> +                                             reg = <0x0 0x1000000>;
>>>> +                                     };
>>>> +                                     partition@1000000 {
>>>> +                                             /* 16 MB for jffs2 data. */
>>>> +                                             label = "EPCQ Flash 0 JFFS 2";
>>>> +                                             reg = <0x1000000 0x1000000>;
>>>> +                                     };
>>>> +                             };
>>>> +                     }; //end epcq@0x000000000 (epcq_controller_0)
>>>> diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
>>>> index c49d0b1..020b864 100644
>>>> --- a/drivers/mtd/devices/Kconfig
>>>> +++ b/drivers/mtd/devices/Kconfig
>>>> @@ -218,6 +218,18 @@ config MTD_ST_SPI_FSM
>>>>         SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
>>>>         for a subset of connected Serial Flash devices.
>>>>
>>>> +config MTD_ALTERA_EPCQ
>>>> +     tristate "Support Altera EPCQ/EPCS Flash chips"
>>>> +     depends on OF
>>>> +     help
>>>> +       This enables access to Altera EPCQ/EPCS flash chips, used for data
>>>> +       storage. See the driver source for the current list,
>>>> +       or to add other chips.
>>>> +
>>>> +       If you want to compile this driver as a module ( = code which can be
>>>> +       inserted in and removed from the running kernel whenever you want),
>>>> +       say M here and read <file:Documentation/kbuild/modules.txt>.
>>>> +
>>>>  if MTD_DOCG3
>>>>  config BCH_CONST_M
>>>>       default 14
>>>> diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
>>>> index f0b0e61..b429c4d 100644
>>>> --- a/drivers/mtd/devices/Makefile
>>>> +++ b/drivers/mtd/devices/Makefile
>>>> @@ -16,6 +16,6 @@ obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
>>>>  obj-$(CONFIG_MTD_SST25L)     += sst25l.o
>>>>  obj-$(CONFIG_MTD_BCM47XXSFLASH)      += bcm47xxsflash.o
>>>>  obj-$(CONFIG_MTD_ST_SPI_FSM)    += st_spi_fsm.o
>>>> -
>>>> +obj-$(CONFIG_MTD_ALTERA_EPCQ)                += altera_epcq.o
>>>>
>>>>  CFLAGS_docg3.o                       += -I$(src)
>>>> diff --git a/drivers/mtd/devices/altera_epcq.c b/drivers/mtd/devices/altera_epcq.c
>>>> new file mode 100644
>>>> index 0000000..09213d5
>>>> --- /dev/null
>>>> +++ b/drivers/mtd/devices/altera_epcq.c
>>>> @@ -0,0 +1,804 @@
>>>> +/*
>>>> + * Copyright (C) 2014 Altera Corporation. All rights reserved
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or modify it
>>>> + * under the terms and conditions of the GNU General Public License,
>>>> + * version 2, as published by the Free Software Foundation.
>>>> + *
>>>> + * This program is distributed in the hope 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.
>>>> + *
>>>> + * You should have received a copy of the GNU General Public License along with
>>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>>> + */
>>>> +
>>>> +#include <linux/clk.h>
>>>> +#include <linux/delay.h>
>>>> +#include <linux/device.h>
>>>> +#include <linux/err.h>
>>>> +#include <linux/errno.h>
>>>> +#include <linux/interrupt.h>
>>>> +#include <linux/io.h>
>>>> +#include <linux/ioport.h>
>>>> +#include <linux/jiffies.h>
>>>> +#include <linux/kernel.h>
>>>> +#include <linux/log2.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/mtd/mtd.h>
>>>> +#include <linux/mtd/partitions.h>
>>>> +#include <linux/mutex.h>
>>>> +#include <linux/of.h>
>>>> +#include <linux/of_address.h>
>>>> +#include <linux/param.h>
>>>> +#include <linux/platform_device.h>
>>>> +#include <linux/pm.h>
>>>> +#include <linux/sched.h>
>>>> +#include <linux/slab.h>
>>>> +#include <linux/wait.h>
>>>> +
>>>> +#include "altera_epcq.h"
>>>> +
>>>> +/* data structure to maintain flash ids from different vendors */
>>>> +struct flash_device {
>>>> +     char *name;
>>>> +     bool is_epcs;
>>>> +     u32 device_id;
>>>> +     uint32_t sectorsize_inbytes;
>>>> +     uint64_t size_inbytes;
>>>> +     u32 pagesize;
>>>> +};
>>>> +
>>>> +#define FLASH_ID(_n, _is_epcs, _id, _ssize, _size, _psize)   \
>>>> +{                            \
>>>> +     .name = (_n),           \
>>>> +     .is_epcs = (_is_epcs),          \
>>>> +     .device_id = (_id),     \
>>>> +     .sectorsize_inbytes = (_ssize), \
>>>> +     .size_inbytes = (_size),        \
>>>> +     .pagesize = (_psize),   \
>>>> +}
>>>> +
>>>> +static struct flash_device flash_devices[] = {
>>>> +     FLASH_ID("epcq16"    , 0, 0x15, 0x10000, 0x200000, 0x100),
>>>> +     FLASH_ID("epcq32"    , 0, 0x16, 0x10000, 0x400000, 0x100),
>>>> +     FLASH_ID("epcq64"    , 0, 0x17, 0x10000, 0x800000, 0x100),
>>>> +     FLASH_ID("epcq128"   , 0, 0x18, 0x10000, 0x1000000, 0x100),
>>>> +     FLASH_ID("epcq256"   , 0, 0x19, 0x10000, 0x2000000, 0x100),
>>>> +     FLASH_ID("epcq512"   , 0, 0x20, 0x10000, 0x4000000, 0x100),
>>>> +
>>>> +     FLASH_ID("epcs16"    , 1, 0x14, 0x10000, 0x200000, 0x100),
>>>> +     FLASH_ID("epcs64"    , 1, 0x16, 0x10000, 0x800000, 0x100),
>>>> +     FLASH_ID("epcs128"   , 1, 0x18, 0x40000, 0x1000000, 0x100),
>>>> +};
>>>> +
>>>> +static inline struct altera_epcq_flash *get_flash_data(struct mtd_info *mtd)
>>>> +{
>>>> +     return container_of(mtd, struct altera_epcq_flash, mtd);
>>>> +}
>>>> +
>>>> +static u32 altera_epcq_read_sr(struct altera_epcq *dev)
>>>> +{
>>>> +     return readl(dev->csr_base + EPCQ_SR_REG);
>>>> +}
>>>> +
>>>> +static int altera_epcq_wait_till_ready(struct altera_epcq *dev)
>>>> +{
>>>> +     unsigned long finish;
>>>> +     int status;
>>>> +
>>>> +     finish = jiffies + EPCQ_MAX_TIME_OUT;
>>>> +     do {
>>>> +             status = altera_epcq_read_sr(dev);
>>>> +             if (status < 0)
>>>> +                     return status;
>>>> +             else if (!(status & EPCQ_SR_WIP))
>>>> +                     return 0;
>>>> +
>>>> +             cond_resched();
>>>> +     } while (!time_after_eq(jiffies, finish));
>>>> +
>>>> +     dev_err(&dev->pdev->dev, "epcq controller is busy, timeout\n");
>>>> +     return -EBUSY;
>>>> +}
>>>> +
>>>> +static int get_flash_index(u32 flash_id, bool is_epcs)
>>>> +{
>>>> +     int index;
>>>> +
>>>> +     for (index = 0; index < ARRAY_SIZE(flash_devices); index++) {
>>>> +             if (flash_devices[index].device_id == flash_id &&
>>>> +                 flash_devices[index].is_epcs == is_epcs)
>>>> +                     return index;
>>>> +     }
>>>> +
>>>> +     /* Memory chip is not listed and not supported */
>>>> +     return -ENODEV;
>>>> +}
>>>> +
>>>> +static int altera_epcq_write_erase_check(struct altera_epcq *dev,
>>>> +                                      bool write_erase)
>>>> +{
>>>> +     u32 val;
>>>> +     u32 mask;
>>>> +
>>>> +     if (write_erase)
>>>> +             mask = EPCQ_ISR_ILLEGAL_WRITE_MASK;
>>>> +     else
>>>> +             mask = EPCQ_ISR_ILLEGAL_ERASE_MASK;
>>>> +
>>>> +     val = readl(dev->csr_base + EPCQ_ISR_REG);
>>>> +     if (val & mask) {
>>>> +             dev_err(&dev->pdev->dev,
>>>> +                     "write/erase failed, sector might be protected\n");
>>>> +             /*clear this status for next use*/
>>>> +             writel(val, dev->csr_base + EPCQ_ISR_REG);
>>>> +             return -EIO;
>>>> +     }
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static int altera_epcq_erase_chip(struct mtd_info *mtd)
>>>> +{
>>>> +     int ret;
>>>> +     u32 val;
>>>> +     struct altera_epcq *dev = mtd->priv;
>>>> +
>>>> +     /* Wait until finished previous write command. */
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>
>>> This pattern is pretty silly. We don't need to have every operation
>>> check the status of the previous operation. Each operation should check
>>> itself. You obviously copied this one... but we recently changed that:
>>>
>>>     commit dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3
>>>     Author: Brian Norris <computersforpeace-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>>>     Date:   Wed Aug 6 18:16:57 2014 -0700
>>>
>>>         mtd: spi-nor: move "wait-till-ready" checks into erase/write
>>>         functions
>>>
>>>
>>>     https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3
>>>
Noted
>>>> +     if (ret)
>>>> +             return ret;
>>>> +
>>>> +     /* erase chip. */
>>>> +     val = EPCQ_MEM_OP_BULK_ERASE_CMD;
>>>> +     writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
>>>> +
>>>> +     /* Wait until finished previous write command. */
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>> +     if (ret)
>>>> +             return ret;
>>>> +
>>>> +      /* check whether write triggered a illegal write interrupt */
>>>> +     ret = altera_epcq_write_erase_check(dev, 0);
>>>> +     if (ret < 0)
>>>> +             return ret;
>>>> +
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static int altera_epcq_addr_to_sector(struct altera_epcq_flash *flash,
>>>> +                                   int addr_offset)
>>>> +{
>>>> +     int sector = 0;
>>>> +     int i;
>>>> +
>>>> +     for (i = 0; i < flash->num_sectors; i++) {
>>>> +             if (addr_offset >= i * flash->mtd.erasesize && addr_offset <
>>>> +                 (i * flash->mtd.erasesize + flash->mtd.erasesize)) {
>>>> +                     sector = i;
>>>> +                     return sector;
>>>> +             }
>>>> +     }
>>>
>>> Uh, really? Am I crazy, or shouldn't this whole function just be a
>>> really simple arithmetic operation? Like:
>>>
>>>         return addr_offset >> mtd->erasesize_shift;
>>>
My bad. This is so much easier this way.
>>>> +     return -1;
>>>> +}
>>>> +
>>>> +static int altera_epcq_erase_sector(struct mtd_info *mtd,
>>>> +                                 int addr_offset)
>>>> +{
>>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>>> +     struct altera_epcq *dev = mtd->priv;
>>>> +     u32 val;
>>>> +     int ret;
>>>> +     int sector_value;
>>>> +
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>> +     if (ret)
>>>> +             return ret;
>>>> +
>>>> +     sector_value = altera_epcq_addr_to_sector(flash, addr_offset);
>>>> +
>>>> +     /* sanity check that block_offset is a valid sector number */
>>>> +     if (sector_value < 0)
>>>> +             return -EINVAL;
>>>> +
>>>> +     /* sector value should occupy bits 17:8 */
>>>> +     val = (sector_value << 8) & EPCQ_MEM_OP_SECTOR_VALUE_MASK;
>>>> +
>>>> +     /* sector erase commands occupies lower 2 bits */
>>>> +     val |= EPCQ_MEM_OP_SECTOR_ERASE_CMD;
>>>> +
>>>> +     /* write sector erase command to EPCQ_MEM_OP register*/
>>>> +     writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
>>>> +
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>> +     if (ret)
>>>> +             return ret;
>>>> +
>>>> +      /* check whether write triggered a illegal write interrupt */
>>>
>>> ^^ extra space here? You have <TAB><SPACE>/* check [...]
>>>
Noted :P
>>>> +     ret = altera_epcq_write_erase_check(dev, 0);
>>>> +     if (ret < 0)
>>>> +             return ret;
>>>> +
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static int altera_epcq_erase(struct mtd_info *mtd, struct erase_info *e_info)
>>>> +{
>>>> +     u32 addr;
>>>> +     int ret;
>>>> +     u32 len;
>>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>>> +     struct altera_epcq *dev = mtd->priv;
>>>> +
>>>> +     addr = e_info->addr;
>>>> +     len = e_info->len;
>>>> +
>>>> +     if (flash->bank > dev->num_flashes - 1) {
>>>> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
>>>> +             return -EINVAL;
>>>> +     }
>>>> +     mutex_lock(&flash->lock);
>>>> +
>>>> +     /*erase whole chip*/
>>>> +     if (len == mtd->size) {
>>>> +             if (altera_epcq_erase_chip(mtd)) {
>>>> +                     e_info->state = MTD_ERASE_FAILED;
>>>> +                     mutex_unlock(&flash->lock);
>>>> +                     return -EIO;
>>>> +             }
>>>> +     /*"sector"-at-a-time erase*/
>>>> +     } else {
>>>> +             while (len) {
>>>> +                     ret = altera_epcq_erase_sector(mtd, addr);
>>>> +                     if (ret) {
>>>> +                             e_info->state = MTD_ERASE_FAILED;
>>>> +                             mutex_unlock(&flash->lock);
>>>> +                             return -EIO;
>>>> +                     }
>>>> +                     addr += mtd->erasesize;
>>>> +                     if (len < mtd->erasesize)
>>>> +                             len = 0;
>>>> +                     else
>>>> +                             len -= mtd->erasesize;
>>>> +             }
>>>> +     }
>>>> +     mutex_unlock(&flash->lock);
>>>> +     e_info->state = MTD_ERASE_DONE;
>>>> +     mtd_erase_callback(e_info);
>>>> +
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static int altera_epcq_read(struct mtd_info *mtd, loff_t from, size_t len,
>>>> +                         size_t *retlen, u8 *buf)
>>>> +{
>>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>>> +     struct altera_epcq *dev = mtd->priv;
>>>> +     void *src;
>>>> +     int ret = 0;
>>>> +
>>>> +     if (!flash || !dev)
>>>> +             return -ENODEV;
>>>
>>> Why would either of these be 0? If they are, you have much bigger
>>> problems, since you aren't checking these almost anywhere else. And the
>>> !flash check is pretty odd, since get_flash_data() is using
>>> container_of(), which is not likely to give you exactly 0...
>>>
Ok
>>>> +
>>>> +     if (flash->bank > dev->num_flashes - 1) {
>>>> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
>>>> +             return -EINVAL;
>>>> +     }
>>>> +
>>>> +     src = dev->data_base + from;
>>>> +
>>>> +     mutex_lock(&flash->lock);
>>>> +     /* wait till previous write/erase is done. */
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>> +     if (ret)
>>>> +             goto err;
>>>> +
>>>> +     memcpy_fromio(buf, (u8 *)src, len);
>>>
>>> Drop the cast. Also, you get sparse warnings because you're dropping the
>>> __iomem. Why not just this?
>>>
>>>         memcpy_fromio(buf, dev->data_base + from, len);
>>>
>>> (BTW, you might consider running sparse on all your code. See
>>> Documentation/sparse.txt. It tells you how to get it, but you can often
>>> just download it through your distro's package manager.)
>>>
Noted
>>>> +     *retlen = len;
>>>> +
>>>> +err:
>>>> +     mutex_unlock(&flash->lock);
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static int altera_epcq_write(struct mtd_info *mtd, loff_t to, size_t len,
>>>> +                          size_t *retlen, const u8 *buf)
>>>> +{
>>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>>> +     struct altera_epcq *dev = mtd->priv;
>>>> +     void *dest;
>>>> +     int ret = 0;
>>>> +
>>>> +     if (!flash || !dev)
>>>> +             return -ENODEV;
>>>
>>> Also here. I don't think you need these checks.
>>>
Noted
>>>> +
>>>> +     if (flash->bank > dev->num_flashes - 1) {
>>>> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
>>>> +             return -EINVAL;
>>>> +     }
>>>> +     dest = dev->data_base + to;
>>>> +
>>>> +     mutex_lock(&flash->lock);
>>>> +
>>>> +     /* wait until finished previous write command. */
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>> +     if (ret)
>>>> +             goto err;
>>>> +
>>>> +     memcpy_toio(dest, buf, len);
>>>
>>> Similar. Why not just this?
>>>
>>>         memcpy_toio(dev->data_base + to, buf, len);
>>>
Noted
>>>> +     *retlen += len;
>>>> +
>>>> +     /* wait until finished previous write command. */
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>> +     if (ret)
>>>> +             goto err;
>>>> +
>>>> +      /* check whether write triggered a illegal write interrupt */
>>>> +     ret = altera_epcq_write_erase_check(dev, 1);
>>>> +     if (ret < 0)
>>>> +             goto err;
>>>> +
>>>> +err:
>>>> +     mutex_unlock(&flash->lock);
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static int altera_epcq_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
>>>> +{
>>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>>> +     struct altera_epcq *dev = mtd->priv;
>>>> +     uint32_t offset = ofs;
>>>> +     int ret = 0;
>>>> +     u32 sector_start, sector_end;
>>>> +     u32 num_sectors;
>>>> +     u32 mem_op;
>>>> +     unsigned sr_bp = 0;
>>>> +     unsigned sr_tb = 0;
>>>> +
>>>> +     sector_start = altera_epcq_addr_to_sector(flash, offset);
>>>> +     sector_end = altera_epcq_addr_to_sector(flash, offset + len);
>>>> +     num_sectors = flash->num_sectors;
>>>> +     dev_dbg(&dev->pdev->dev,
>>>> +             "%s: num_setor is %u,sector start is %u,sector end is %u\n",
>>>> +             __func__, num_sectors, sector_start, sector_end);
>>>> +
>>>> +     mutex_lock(&flash->lock);
>>>> +     /* wait until finished previous write command. */
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>> +     if (ret)
>>>> +             goto err;
>>>> +
>>>> +     if (sector_start >= num_sectors/2) {
>>>
>>> It's preferable to have spaces around operators. So 'num_sectors / 2'.
>>> The same thing comes up in several other places.
>>>
Noted
>>>> +             if (sector_start < num_sectors-(num_sectors / 4))
>>>> +                     sr_bp = __ilog2_u32(num_sectors);
>>>> +             else if (sector_start < num_sectors-(num_sectors / 8))
>>>> +                     sr_bp = __ilog2_u32(num_sectors) - 1;
>>>> +             else if (sector_start < num_sectors-(num_sectors / 16))
>>>> +                     sr_bp = __ilog2_u32(num_sectors) - 2;
>>>> +             else if (sector_start < num_sectors-(num_sectors / 32))
>>>> +                     sr_bp = __ilog2_u32(num_sectors) - 3;
>>>> +             else if (sector_start < num_sectors-(num_sectors / 64))
>>>> +                     sr_bp = __ilog2_u32(num_sectors) - 4;
>>>> +             else if (sector_start < num_sectors-(num_sectors / 128))
>>>> +                     sr_bp = __ilog2_u32(num_sectors) - 5;
>>>> +             else if (sector_start < num_sectors-(num_sectors / 256))
>>>> +                     sr_bp = __ilog2_u32(num_sectors) - 6;
>>>> +             else if (sector_start < num_sectors-(num_sectors / 512))
>>>> +                     sr_bp = __ilog2_u32(num_sectors) - 7;
>>>> +             else if (sector_start < num_sectors-(num_sectors / 1024))
>>>> +                     sr_bp = __ilog2_u32(num_sectors) - 8;
>>>> +             else
>>>> +                     sr_bp = 0;  /* non area protected */
>>>
>>> Yikes, that's ugly! And I'm not sure it matches the EPCQ doc I found.
>>> I'm pretty sure you can rewrite this if/else-if/else block in about 1
>>> line though.
>>>
Yes, i understand that it looks ugly. But it is the best i can do
since this function has to satisfy for all the supported devices
(http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf, start
from page 19)
>>>> +
>>>> +             if (sr_bp < 0) {
>>>
>>> sr_bp is unsigned, so this is never true.
>>>
Ok.  I will change to int type.
>>>> +                     dev_err(&dev->pdev->dev, "%s: address is out of range\n"
>>>> +                             , __func__);
>>>> +                     ret = -EINVAL;
>>>> +                     goto err;
>>>> +             }
>>>> +             /*set TB = 0*/
>>>> +             sr_tb = 0;
>>>> +
>>>> +     } else {
>>>> +             if (sector_end < 1)
>>>> +                     sr_bp = 1;
>>>> +             else if (sector_end < 2)
>>>> +                     sr_bp = 2;
>>>> +             else if (sector_end < 4)
>>>> +                     sr_bp = 3;
>>>> +             else if (sector_end < 8)
>>>> +                     sr_bp = 4;
>>>> +             else if (sector_end < 16)
>>>> +                     sr_bp = 5;
>>>> +             else if (sector_end < 32)
>>>> +                     sr_bp = 6;
>>>> +             else if (sector_end < 64)
>>>> +                     sr_bp = 7;
>>>> +             else if (sector_end < 128)
>>>> +                     sr_bp = 8;
>>>> +             else if (sector_end < 256)
>>>> +                     sr_bp = 9;
>>>> +             else if (sector_end < 512)
>>>> +                     sr_bp = 10;
>>>> +             else
>>>> +                     sr_bp = 16; /*protect all areas*/
>>>
>>> Again, this is ugly. How about this?
>>>
>>>         sr_bp = fls(sector_end) + 1;
>>>
Oh. I did not know got this fls function. I will apply it.
>>>> +
>>>> +             sr_tb = 1;
>>>> +     }
>>>> +
>>>> +     mem_op = (sr_tb << 12) | (sr_bp << 8);
>>>> +     mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
>>>> +     mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
>>>> +     writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
>>>> +
>>>> +err:
>>>> +     mutex_unlock(&flash->lock);
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static int altera_epcq_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
>>>> +{
>>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>>> +     struct altera_epcq *dev = mtd->priv;
>>>> +
>>>> +     int ret = 0;
>>>> +     u32 mem_op;
>>>> +
>>>> +     mutex_lock(&flash->lock);
>>>> +     /* wait until finished previous write command. */
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>> +     if (ret)
>>>> +             goto err;
>>>> +     dev_info(&dev->pdev->dev, "Unlock all protected area\n");
>>>> +     mem_op = 0;
>>>> +     mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
>>>> +     mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
>>>> +     writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
>>>> +
>>>> +err:
>>>> +     mutex_unlock(&flash->lock);
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static void altera_epcq_chip_select(struct altera_epcq *dev, u32 bank)
>>>> +{
>>>> +     u32 val = 0;
>>>> +
>>>> +     switch (bank) {
>>>> +     case 0:
>>>> +             val = EPCQ_CHIP_SELECT_0;
>>>> +             break;
>>>> +     case 1:
>>>> +             val = EPCQ_CHIP_SELECT_1;
>>>> +             break;
>>>> +     case 2:
>>>> +             val = EPCQ_CHIP_SELECT_2;
>>>> +             break;
>>>> +     default:
>>>> +             return;
>>>> +     }
>>>> +
>>>> +     writel(val, dev->csr_base + EPCQ_CHIP_SELECT_REG);
>>>> +}
>>>> +
>>>> +static int altera_epcq_probe_flash(struct altera_epcq *dev, u32 bank)
>>>> +{
>>>> +     int ret = 0;
>>>> +     u32 val = 0;
>>>> +
>>>> +     mutex_lock(&dev->lock);
>>>> +
>>>> +     /*select bank*/
>>>
>>> Comment spacing, here and elsewhere:
>>>
Ok
>>>         /* select bank */
>>>
>>>> +     altera_epcq_chip_select(dev, bank);
>>>> +
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>> +     if (ret)
>>>> +             goto err;
>>>> +
>>>> +     /* get device sillicon id */
>>>> +     if (dev->is_epcs)
>>>> +             val = readl(dev->csr_base + EPCQ_SID_REG);
>>>> +     else
>>>> +             val = readl(dev->csr_base + EPCQ_RDID_REG);
>>>> +
>>>> +     /* get flash index based on the device list*/
>>>> +     ret = get_flash_index(val, dev->is_epcs);
>>>> +     return 0;
>>>> +err:
>>>> +     mutex_unlock(&dev->lock);
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static int altera_epcq_probe_config_dt(struct platform_device *pdev,
>>>> +                                    struct device_node *np,
>>>> +                                    struct altera_epcq_plat_data *pdata)
>>>> +{
>>>> +     struct device_node *pp = NULL;
>>>> +     struct resource *epcq_res;
>>>> +     int i = 0;
>>>> +     u32 id;
>>>> +
>>>> +     pdata->is_epcs = of_property_read_bool(np, "is-epcs");
>>>> +
>>>> +     epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
>>>> +                                             "csr_base");
>>>> +     if (!epcq_res) {
>>>
>>> devm_ioremap_resource() will check the that 'epcq_res' is valid, so you
>>> don't need this whole 'if' block.
>>>
Ok
>>>> +             dev_err(&pdev->dev, "resource csr base is not defined\n");
>>>> +             return -ENODEV;
>>>> +     }
>>>> +
>>>> +     pdata->csr_base = devm_ioremap_resource(&pdev->dev, epcq_res);
>>>> +     if (IS_ERR(pdata->csr_base)) {
>>>> +             dev_err(&pdev->dev, "%s: ERROR: failed to map csr base\n",
>>>> +                     __func__);
>>>> +             return PTR_ERR(pdata->csr_base);
>>>> +     }
>>>> +
>>>> +     epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
>>>> +                                             "data_base");
>>>> +     if (!epcq_res) {
>>>
>>> Same here.
Ok
>>>
>>>> +             dev_err(&pdev->dev, "resource data base is not defined\n");
>>>> +             return -ENODEV;
>>>> +     }
>>>> +
>>>> +     pdata->data_base = devm_ioremap_resource(&pdev->dev, epcq_res);
>>>> +     if (IS_ERR(pdata->data_base)) {
>>>> +             dev_err(&pdev->dev, "%s: ERROR: failed to map data base\n",
>>>> +                     __func__);
>>>> +             return PTR_ERR(pdata->data_base);
>>>> +     }
>>>> +
>>>> +     pdata->board_flash_info = devm_kzalloc(&pdev->dev,
>>>> +                                            sizeof(*pdata->board_flash_info),
>>>> +                                            GFP_KERNEL);
>>>> +
>>>> +     /* Fill structs for each subnode (flash device) */
>>>> +     while ((pp = of_get_next_child(np, pp))) {
>>>
>>> for_each_available_child_of_node()?
>>>
>>>> +             struct altera_epcq_flash_info *flash_info;
>>>> +
>>>> +             flash_info = &pdata->board_flash_info[i];
>>>
>>> This is never used. Either use it below, or drop the local variable.
>>>
Ok
>>>> +             pdata->np[i] = pp;
>>>> +
>>>> +             /* Read bank id from DT */
>>>> +             if (of_get_property(pp, "reg", &id))
>>>
>>> Is this property optional? Your DT binding doc doesn't make it clear,
>>> but it seems like a property which would be wise to require (i.e., not
>>> optional).
>>>
>>>> +                     pdata->board_flash_info[i].bank = id;
>>>> +             i++;
>>>> +     }
>>>> +     pdata->num_flashes = i;
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static int altera_epcq_setup_banks(struct platform_device *pdev,
>>>> +                                u32 bank, struct device_node *np,
>>>> +                                struct altera_epcq_plat_data *pdata)
>>>> +{
>>>> +     struct altera_epcq *dev = platform_get_drvdata(pdev);
>>>> +     struct mtd_part_parser_data ppdata = {};
>>>> +     struct altera_epcq_flash_info *flash_info;
>>>> +     struct altera_epcq_flash *flash;
>>>> +     struct mtd_partition *parts = NULL;
>>>
>>> Drop this variable. Just use NULL directly below.
>>>
>>>> +     int count = 0;
>>>
>>> Same. Just use 0 below.
>>>
Ok
>>>> +     int flash_index;
>>>> +     int ret = 0;
>>>> +     uint64_t size;
>>>> +     uint32_t sector_size;
>>>> +
>>>> +     flash_info = &pdata->board_flash_info[bank];
>>>> +     if (!flash_info)
>>>> +             return -ENODEV;
>>>> +
>>>> +     if (bank > pdata->num_flashes - 1)
>>>> +             return -EINVAL;
>>>> +
>>>> +     flash = devm_kzalloc(&pdev->dev, sizeof(*flash), GFP_KERNEL);
>>>> +     if (!flash)
>>>> +             return -ENOMEM;
>>>> +     flash->bank = bank;
>>>> +
>>>> +     mutex_init(&flash->lock);
>>>> +
>>>> +     /* verify whether flash is really present on board */
>>>> +     flash_index = altera_epcq_probe_flash(dev, bank);
>>>> +     if (flash_index < 0) {
>>>> +             dev_info(&dev->pdev->dev, "epcq:flash%d not found\n",
>>>> +                      flash->bank);
>>>> +             return flash_index;
>>>> +     }
>>>> +
>>>> +     dev->flash[bank] = flash;
>>>> +
>>>> +     size = flash_devices[flash_index].size_inbytes;
>>>> +     sector_size = flash_devices[flash_index].sectorsize_inbytes;
>>>> +     /*use do_div instead of plain div to avoid linker err*/
>>>> +     do_div(size, sector_size);
>>>> +     flash->num_sectors = size;
>>>> +
>>>> +     /*mtd framework */
>>>> +     flash->mtd.priv = dev;
>>>> +     flash->mtd.name = flash_devices[flash_index].name;
>>>> +     flash->mtd.type = MTD_NORFLASH;
>>>> +     flash->mtd.writesize = 1;
>>>> +     flash->mtd.flags = MTD_CAP_NORFLASH;
>>>> +     flash->mtd.size = flash_devices[flash_index].size_inbytes;
>>>> +     flash->mtd.erasesize = flash_devices[flash_index].sectorsize_inbytes;
>>>> +     flash->mtd._erase = altera_epcq_erase;
>>>> +     flash->mtd._read = altera_epcq_read;
>>>> +     flash->mtd._write = altera_epcq_write;
>>>> +     flash->mtd._lock = altera_epcq_lock;
>>>> +     flash->mtd._unlock = altera_epcq_unlock;
>>>> +
>>>> +     dev_info(&pdev->dev, "mtd .name=%s .size=0x%llx (%lluM)\n",
>>>> +              flash->mtd.name, (long long)flash->mtd.size,
>>>> +              (long long)(flash->mtd.size >> 20));
>>>> +
>>>> +     dev_info(&pdev->dev, ".erasesize = 0x%x(%uK)\n",
>>>> +              flash->mtd.erasesize, flash->mtd.erasesize >> 10);
>>>> +
>>>> +     ppdata.of_node = np;
>>>> +
>>>> +     ret = mtd_device_parse_register(&flash->mtd, NULL, &ppdata, parts,
>>>> +                                     count);
>>>> +     if (ret) {
>>>> +             dev_err(&pdev->dev, "Err MTD partition=%d\n", ret);
>>>> +             goto err;
>>>
>>> You don't want to unregister a MTD that failed to register. The MTD core
>>> code should handle that itself.
>>>
>>> Instead of this if (ret) {} block, I'd just make this:
>>>
>>>         return mtd_device_parse_register(&flash->mtd, NULL, &ppdata, NULL, 0);
>>>
Ok
>>>> +     }
>>>> +
>>>> +     return 0;
>>>> +
>>>> +err:
>>>> +     mtd_device_unregister(&flash->mtd);
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static int altera_epcq_probe(struct platform_device *pdev)
>>>> +{
>>>> +     struct device_node *np = pdev->dev.of_node;
>>>> +     struct altera_epcq_plat_data *pdata = NULL;
>>>> +     struct altera_epcq *dev;
>>>> +     int ret = 0;
>>>> +     int i;
>>>> +
>>>> +     if (!np) {
>>>> +             ret = -ENODEV;
>>>> +             dev_err(&pdev->dev, "no device found\n");
>>>> +             goto err;
>>>> +     }
>>>> +
>>>> +     pdata = devm_kzalloc(&pdev->dev,
>>>> +                          sizeof(struct altera_epcq_plat_data),
>>>> +                          GFP_KERNEL);
>>>> +
>>>> +     if (!pdata) {
>>>> +             ret = -ENOMEM;
>>>> +             dev_err(&pdev->dev, "%s: ERROR: no memory\n", __func__);
>>>
>>> Unnecessary print. The MM code will print plenty of info if you're
>>> out of memory.
>>>
>>>> +             goto err;
>>>> +     }
>>>> +     ret = altera_epcq_probe_config_dt(pdev, np, pdata);
>>>> +     if (ret) {
>>>> +             ret = -ENODEV;
>>>> +             dev_err(&pdev->dev, "probe fail\n");
>>>> +             goto err;
>>>> +     }
>>>> +
>>>> +     dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
>>>> +     if (!dev) {
>>>> +             ret = -ENOMEM;
>>>> +             dev_err(&pdev->dev, "mem alloc fail\n");
>>>
>>> Same here.
>>>
>>>> +             goto err;
>>>> +     }
>>>> +     mutex_init(&dev->lock);
>>>> +     dev->pdev = pdev;
>>>> +     dev->is_epcs = pdata->is_epcs;
>>>> +     dev->csr_base = pdata->csr_base;
>>>> +     dev->data_base = pdata->data_base;
>>>> +
>>>> +     /*check number of flashes*/
>>>> +     dev->num_flashes = pdata->num_flashes;
>>>> +     if (dev->num_flashes > MAX_NUM_FLASH_CHIP) {
>>>> +             dev_err(&pdev->dev, "exceeding max number of flashes\n");
>>>> +             dev->num_flashes = MAX_NUM_FLASH_CHIP;
>>>> +     }
>>>> +
>>>> +     /* check clock*/
>>>> +     dev->clk = devm_clk_get(&pdev->dev, NULL);
>>>> +     if (IS_ERR(dev->clk)) {
>>>> +             ret = PTR_ERR(dev->clk);
>>>> +             goto err;
>>>> +     }
>>>> +     ret = clk_prepare_enable(dev->clk);
>>>> +     if (ret)
>>>> +             goto err;
>>>> +
>>>> +     platform_set_drvdata(pdev, dev);
>>>> +
>>>> +     /* loop for each serial flash which is connected to epcq */
>>>> +     for (i = 0; i < dev->num_flashes; i++) {
>>>> +             ret = altera_epcq_setup_banks(pdev, i, pdata->np[i], pdata);
>>>> +             if (ret) {
>>>> +                     dev_err(&pdev->dev, "bank setup failed\n");
>>>> +                     goto err_bank_setup;
>>>> +             }
>>>> +     }
>>>> +
>>>> +     return 0;
>>>> +
>>>> +err_bank_setup:
>>>> +     clk_disable_unprepare(dev->clk);
>>>> +err:
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static int altera_epcq_remove(struct platform_device *pdev)
>>>> +{
>>>> +     struct altera_epcq *dev;
>>>> +     struct altera_epcq_flash *flash;
>>>> +     int ret, i;
>>>> +
>>>> +     dev = platform_get_drvdata(pdev);
>>>> +
>>>> +     /* clean up for all nor flash */
>>>> +     for (i = 0; i < dev->num_flashes; i++) {
>>>> +             flash = dev->flash[i];
>>>> +             if (!flash)
>>>> +                     continue;
>>>> +
>>>> +             /* clean up mtd stuff */
>>>> +             ret = mtd_device_unregister(&flash->mtd);
>>>> +             if (ret)
>>>> +                     dev_err(&pdev->dev, "error removing mtd\n");
>>>> +     }
>>>> +
>>>> +     clk_disable_unprepare(dev->clk);
>>>> +
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +#ifdef CONFIG_PM
>>>
>>> Make this CONFIG_PM_SLEEP.
>>>
>>>> +static int altera_epcq_suspend(struct device *dev)
>>>> +{
>>>> +     struct altera_epcq *sdev = dev_get_drvdata(dev);
>>>> +
>>>> +     clk_disable_unprepare(sdev->clk);
>>>> +
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static int altera_epcq_resume(struct device *dev)
>>>> +{
>>>> +     struct altera_epcq *sdev = dev_get_drvdata(dev);
>>>> +     int ret = -EPERM;
>>>
>>> Drop 'ret'.
>>>
>>>> +
>>>> +     ret = clk_prepare_enable(sdev->clk);
>>>
>>> Just:
>>>
>>>         return clk_prepare_enable(sdev->clk);
>>>
>>>> +
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static SIMPLE_DEV_PM_OPS(altera_epcq_pm_ops, altera_epcq_suspend,
>>>> +                      altera_epcq_resume);
>>>> +#endif
>>>> +
>>>> +static const struct of_device_id altera_epcq_id_table[] = {
>>>> +     { .compatible = "altr,epcq-1.0" },
>>>> +     {}
>>>> +};
>>>> +MODULE_DEVICE_TABLE(of, altera_epcq_id_table);
>>>> +
>>>> +static struct platform_driver altera_epcq_driver = {
>>>> +     .driver = {
>>>> +             .name = ALTERA_EPCQ_RESOURCE_NAME,
>>>> +             .bus = &platform_bus_type,
>>>> +             .owner = THIS_MODULE,
>>>
>>> The .owner assignment is no longer necessary.
>>>
>>>> +             .of_match_table = altera_epcq_id_table,
>>>> +#ifdef CONFIG_PM
>>>
>>> Also CONFIG_PM_SLEEP.
>>>
>>>> +             .pm = &altera_epcq_pm_ops,
>>>> +#endif
>>>> +     },
>>>> +     .probe = altera_epcq_probe,
>>>> +     .remove = altera_epcq_remove,
>>>> +};
>>>> +module_platform_driver(altera_epcq_driver);
>>>> +
>>>> +MODULE_AUTHOR("Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org>");
>>>> +MODULE_DESCRIPTION("MTD Altera EPCQ Driver");
>>>> +MODULE_LICENSE("GPL v2");
>>>> diff --git a/drivers/mtd/devices/altera_epcq.h b/drivers/mtd/devices/altera_epcq.h
>>>> new file mode 100644
>>>> index 0000000..c3d15e5
>>>> --- /dev/null
>>>> +++ b/drivers/mtd/devices/altera_epcq.h
>>>> @@ -0,0 +1,130 @@
>>>> +/*
>>>> + * Copyright (C) 2014 Altera Corporation. All rights reserved
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or modify it
>>>> + * under the terms and conditions of the GNU General Public License,
>>>> + * version 2, as published by the Free Software Foundation.
>>>> + *
>>>> + * This program is distributed in the hope 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.
>>>> + *
>>>> + * You should have received a copy of the GNU General Public License along with
>>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>>> + */
>>>> +
>>>> +#ifndef __ALTERA_ECPQ_H
>>>> +#define __ALTERA_ECPQ_H
>>>> +
>>>> +#define ALTERA_EPCQ_RESOURCE_NAME        "altera_epcq"
>>>> +/* max possible slots for serial flash chip in the EPCQ controller */
>>>> +#define MAX_NUM_FLASH_CHIP   3
>>>> +
>>>> +/* macro to define partitions for flash devices */
>>>> +#define DEFINE_PARTS(n, of, s)               \
>>>
>>> This macro is not used anywhere. Drop it?
>>>
Ok
>>>> +{                                    \
>>>> +     .name = n,                      \
>>>> +     .offset = of,                   \
>>>> +     .size = s,                      \
>>>> +}
>>>> +
>>>> +struct altera_epcq_flash_info {
>>>> +     u32 bank;
>>>> +     struct mtd_partition *partitions;
>>>> +     int nr_partitions;
>>>> +};
>>>> +
>>>> +struct altera_epcq_plat_data {
>>>> +     void __iomem *csr_base;
>>>> +     void __iomem *data_base;
>>>> +     bool is_epcs;
>>>> +     u32 num_flashes;
>>>> +     struct altera_epcq_flash_info *board_flash_info;
>>>> +     struct device_node *np[MAX_NUM_FLASH_CHIP];
>>>> +};
>>>> +
>>>> +struct altera_epcq {
>>>> +     struct clk *clk;
>>>> +     bool is_epcs;
>>>> +     struct mutex lock;      /*device lock*/
>>>> +     void __iomem *csr_base;
>>>> +     void __iomem *data_base;
>>>> +     struct platform_device *pdev;
>>>> +     u32 num_flashes;
>>>> +     struct altera_epcq_flash *flash[MAX_NUM_FLASH_CHIP];
>>>> +};
>>>> +
>>>> +struct altera_epcq_flash {
>>>> +     u32 bank;
>>>> +     u32 num_sectors;
>>>> +     u32 num_parts;
>>>> +     struct mtd_partition *parts;
>>>> +     struct mutex lock;      /*flash lock*/
>>>> +     struct mtd_info mtd;
>>>> +};
>>>> +
>>>> +/* Define max times to check status register before we give up. */
>>>> +#define EPCQ_MAX_TIME_OUT                    (40 * HZ)
>>>> +
>>>> +/* defines for status register */
>>>> +#define EPCQ_SR_REG                          0x0
>>>> +#define EPCQ_SR_WIP_MASK                     0x00000001
>>>> +#define EPCQ_SR_WIP                          0x1
>>>> +#define EPCQ_SR_WEL                          0x2
>>>> +#define EPCQ_SR_BP0                          0x4
>>>> +#define EPCQ_SR_BP1                          0x8
>>>> +#define EPCQ_SR_BP2                          0x10
>>>> +#define EPCQ_SR_BP3                          0x40
>>>> +#define EPCQ_SR_TB                           0x20
>>>> +
>>>> +/* defines for device id register */
>>>> +#define EPCQ_SID_REG                         0x4
>>>> +#define EPCQ_RDID_REG                                0x8
>>>> +#define EPCQ_RDID_MASK                               0x000000FF
>>>> +/*
>>>> + * EPCQ_MEM_OP register offset
>>>> + *
>>>> + * The EPCQ_MEM_OP register is used to do memory protect and erase operations
>>>> + *
>>>> + */
>>>> +#define EPCQ_MEM_OP_REG                              0xC
>>>> +
>>>> +#define EPCQ_MEM_OP_CMD_MASK                 0x00000003
>>>> +#define EPCQ_MEM_OP_BULK_ERASE_CMD           0x00000001
>>>> +#define EPCQ_MEM_OP_SECTOR_ERASE_CMD         0x00000002
>>>> +#define EPCQ_MEM_OP_SECTOR_PROTECT_CMD               0x00000003
>>>> +#define EPCQ_MEM_OP_SECTOR_VALUE_MASK                0x0003FF00
>>>> +#define EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK        0x00001F00
>>>> +#define EPCQ_MEM_OP_SECTOR_PROTECT_SHIFT     8
>>>> +/*
>>>> + * EPCQ_ISR register offset
>>>> + *
>>>> + * The EPCQ_ISR register is used to determine whether an invalid write or erase
>>>> + * operation trigerred an interrupt
>>>> + *
>>>> + */
>>>> +#define EPCQ_ISR_REG                         0x10
>>>> +
>>>> +#define EPCQ_ISR_ILLEGAL_ERASE_MASK          0x00000001
>>>> +#define EPCQ_ISR_ILLEGAL_WRITE_MASK          0x00000002
>>>> +
>>>> +/*
>>>> + * EPCQ_IMR register offset
>>>> + *
>>>> + * The EPCQ_IMR register is used to mask the invalid erase or the invalid write
>>>> + * interrupts.
>>>> + *
>>>> + */
>>>> +#define EPCQ_IMR_REG                         0x14
>>>> +#define EPCQ_IMR_ILLEGAL_ERASE_MASK          0x00000001
>>>> +
>>>> +#define EPCQ_IMR_ILLEGAL_WRITE_MASK          0x00000002
>>>> +
>>>> +#define EPCQ_CHIP_SELECT_REG                 0x18
>>>> +#define EPCQ_CHIP_SELECT_MASK                        0x00000007
>>>> +#define EPCQ_CHIP_SELECT_0                   0x00000001
>>>> +#define EPCQ_CHIP_SELECT_1                   0x00000002
>>>> +#define EPCQ_CHIP_SELECT_2                   0x00000004
>>>> +
>>>> +#endif /* __ALTERA_ECPQ_H */
>>>

Nga
--
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	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-01-27  6:53     ` Viet Nga Dao
  0 siblings, 0 replies; 39+ messages in thread
From: Viet Nga Dao @ 2015-01-27  6:53 UTC (permalink / raw)
  To: Viet Nga Dao, Brian Norris
  Cc: linux-mtd, nga_chi86, David Woodhouse, linux-kernel, devicetree

On Tue, Jan 20, 2015 at 10:05 AM, Viet Nga Dao <vndao@altera.com> wrote:
> On Thu, Jan 15, 2015 at 5:27 PM, Viet Nga Dao <vndao@altera.com> wrote:
>> On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
>> <computersforpeace@gmail.com> wrote:
>>> On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao@altera.com wrote:
>>>> From: Viet Nga Dao <vndao@altera.com>
>>>>
>>>> Altera EPCQ Controller is a soft IP which enables access to Altera EPCQ and
>>>> EPCS flash chips. This patch adds driver for these devices.
>>>>
>>>> Signed-off-by: Viet Nga Dao <vndao@altera.com>
>>>
>>> This drivers seems awfully similar to (and so I infer it is likely
>>> copy-and-pasted from) m25p80.c / spi-nor.c. Do you think it can be
>>> rewritten as a SPI NOR driver, under drivers/mtd/spi-nor/ ? It looks
>>> like these flash share most (all?) the same basic opcodes.
>>>
>> For Altera EPCQ flashes, almost operations are performed underline
>> hardware.
>
>Right, that's understandable. But that's what spi-nor.c was designed
>for: implementing hardware-specific functionality that is targeted
>directly at SPI flash. Did you take a look at the callbacks available in
>'struct spi_nor'?
>
>>Software only able to perform the following through
>> registers:
>>  -  read status register
>>  -  read id
>>  -  write status registers bit SR_BP0,SR_BP1, SR_BP2,SR_BP3, SR_TB
>> (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf)
>
>OK
>
>> For read/write data: all the operations like QUAD_READ/WRITE,
>> FAST_READ/WRITE are handled by hardware as well. From software point
>> of view, there is no difference between these 2 modes.
>
>OK, so you don't have to hook up the dual/quad mode infrastructure. And
>you'd just implement dead-simple spi_nor.{read,write,erase}() callbacks.
>
>> That is why if rewrite the drivers to follow spi-nor structure, it
>> will require extra decoding works for the only few used opcodes.
>>
>I think you'd only have some very trivial work here.
>
>There would be some small work to reintroduce a properly-replaceable ID
>table, and callbacks like ->lock() and ->unlock(), but those should be
>implemented in spi-nor.c sooner or later anyway.
>

I am trying to modify the code to follow your recommendation. However,
for lock and unlock functions, i have to implement it in spi_nor.c ,
am i right? If yes, currently we already have existing spi_nor_lock
and spi_nor_unlock functions but they could not apply for my driver.
Should i create a new functions named altera_epcq_lock and unlock and
then map it to callback functions or i should the put  those code
sunder existing spi_nor_lock/unlock?

>Anyway, if you take another look at spi-nor.{c,h} and determine that
>it's too difficult, then I suppose I don't mind accepting your driver
>under its current design. Your hardware is pretty esoteric anyway, the
>driver is still pretty simple, and it'll never be supporting any common
>SPI NOR vendors (right?), so it's not too big of a maintenance problem,
>I expect. But I do want to make sure that we don't copy/paste to repeat
>the mistakes of old drivers. As I noted already, your driver inherited
>some of the quirks of the old m25p80.c code.
>
>So please, give drivers/mtd/spi-nor/ another look, and then we can
>resume this discussion.
>>>> ---
>>>>  .../devicetree/bindings/mtd/altera_epcq.txt        |   45 ++
>>>>  drivers/mtd/devices/Kconfig                        |   12 +
>>>>  drivers/mtd/devices/Makefile                       |    2 +-
>>>>  drivers/mtd/devices/altera_epcq.c                  |  804 ++++++++++++++++++++
>>>>  drivers/mtd/devices/altera_epcq.h                  |  130 ++++
>>>>  5 files changed, 992 insertions(+), 1 deletions(-)
>>>>  create mode 100644 Documentation/devicetree/bindings/mtd/altera_epcq.txt
>>>>  create mode 100644 drivers/mtd/devices/altera_epcq.c
>>>>  create mode 100644 drivers/mtd/devices/altera_epcq.h
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>>>> new file mode 100644
>>>> index 0000000..d14f50e
>>>> --- /dev/null
>>>> +++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>>>> @@ -0,0 +1,45 @@
>>>> +* MTD Altera EPCQ driver
>>>> +
>>>> +Required properties:
>>>> +- compatible: Should be "altr,epcq-1.0"
>>>> +- reg: Address and length of the register set  for the device. It contains
>>>> +  the information of registers in the same order as described by reg-names
>>>> +- reg-names: Should contain the reg names
>>>> +  "csr_base": Should contain the register configuration base address
>>>> +  "data_base": Should contain the data base address
>>>> +- is-epcs: boolean type.
>>>> +             If present, the device contains EPCS flashes.
>>>> +             Otherwise, it contains EPCQ flashes.
>>>> +- #address-cells: Must be <1>.
>>>> +- #size-cells: Must be <0>.
>>>> +- flash device tree subnode, there must be a node with the following fields:
>>>
>>> These subnodes definitely require a 'compatible' string. Perhaps they
>>> should be used to differentiate EPCS vs. EPCQ. Does "is-epcs" really
>>> need to be in the top-level controller node?
>>>
>>>> +     - reg: Should contain the flash id
>>>
>>> Should, or must? (This question is relevant, because you seem to make it
>>> optional in your code.) And what does the "flash ID" mean? It seems like
>>> you're using as a chip-select or bank index.
>>>
Yes, this is used for chip select. It is required, not optional. This
to ID for each flash in the device
>>>> +     - #address-cells: please refer to /mtd/partition.txt
>>>> +     - #size-cells: please refer to /mtd/partition.txt
>>>> +     For partitions inside each flash, please refer to /mtd/partition.txt
>>>> +
>>>> +Example:
>>>> +
>>>> +                     epcq_controller_0: epcq@0x000000000 {
>>>> +                             compatible = "altr,epcq-1.0";
>>>> +                             reg = <0x00000001 0x00000000 0x00000020>,
>>>> +                                     <0x00000000 0x00000000 0x02000000>;
>>>> +                             reg-names = "csr_base", "data_base";
>>>> +                             #address-cells = <1>;
>>>> +                             #size-cells = <0>;
>>>> +                             flash0: epcq256@0 {
>>>> +                                     reg = <0>;
>>>> +                                     #address-cells = <1>;
>>>> +                                     #size-cells = <1>;
>>>> +                                     partition@0 {
>>>> +                                             /* 16 MB for raw data. */
>>>> +                                             label = "EPCQ Flash 0 raw data";
>>>> +                                             reg = <0x0 0x1000000>;
>>>> +                                     };
>>>> +                                     partition@1000000 {
>>>> +                                             /* 16 MB for jffs2 data. */
>>>> +                                             label = "EPCQ Flash 0 JFFS 2";
>>>> +                                             reg = <0x1000000 0x1000000>;
>>>> +                                     };
>>>> +                             };
>>>> +                     }; //end epcq@0x000000000 (epcq_controller_0)
>>>> diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
>>>> index c49d0b1..020b864 100644
>>>> --- a/drivers/mtd/devices/Kconfig
>>>> +++ b/drivers/mtd/devices/Kconfig
>>>> @@ -218,6 +218,18 @@ config MTD_ST_SPI_FSM
>>>>         SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
>>>>         for a subset of connected Serial Flash devices.
>>>>
>>>> +config MTD_ALTERA_EPCQ
>>>> +     tristate "Support Altera EPCQ/EPCS Flash chips"
>>>> +     depends on OF
>>>> +     help
>>>> +       This enables access to Altera EPCQ/EPCS flash chips, used for data
>>>> +       storage. See the driver source for the current list,
>>>> +       or to add other chips.
>>>> +
>>>> +       If you want to compile this driver as a module ( = code which can be
>>>> +       inserted in and removed from the running kernel whenever you want),
>>>> +       say M here and read <file:Documentation/kbuild/modules.txt>.
>>>> +
>>>>  if MTD_DOCG3
>>>>  config BCH_CONST_M
>>>>       default 14
>>>> diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
>>>> index f0b0e61..b429c4d 100644
>>>> --- a/drivers/mtd/devices/Makefile
>>>> +++ b/drivers/mtd/devices/Makefile
>>>> @@ -16,6 +16,6 @@ obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
>>>>  obj-$(CONFIG_MTD_SST25L)     += sst25l.o
>>>>  obj-$(CONFIG_MTD_BCM47XXSFLASH)      += bcm47xxsflash.o
>>>>  obj-$(CONFIG_MTD_ST_SPI_FSM)    += st_spi_fsm.o
>>>> -
>>>> +obj-$(CONFIG_MTD_ALTERA_EPCQ)                += altera_epcq.o
>>>>
>>>>  CFLAGS_docg3.o                       += -I$(src)
>>>> diff --git a/drivers/mtd/devices/altera_epcq.c b/drivers/mtd/devices/altera_epcq.c
>>>> new file mode 100644
>>>> index 0000000..09213d5
>>>> --- /dev/null
>>>> +++ b/drivers/mtd/devices/altera_epcq.c
>>>> @@ -0,0 +1,804 @@
>>>> +/*
>>>> + * Copyright (C) 2014 Altera Corporation. All rights reserved
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or modify it
>>>> + * under the terms and conditions of the GNU General Public License,
>>>> + * version 2, as published by the Free Software Foundation.
>>>> + *
>>>> + * This program is distributed in the hope 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.
>>>> + *
>>>> + * You should have received a copy of the GNU General Public License along with
>>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>>> + */
>>>> +
>>>> +#include <linux/clk.h>
>>>> +#include <linux/delay.h>
>>>> +#include <linux/device.h>
>>>> +#include <linux/err.h>
>>>> +#include <linux/errno.h>
>>>> +#include <linux/interrupt.h>
>>>> +#include <linux/io.h>
>>>> +#include <linux/ioport.h>
>>>> +#include <linux/jiffies.h>
>>>> +#include <linux/kernel.h>
>>>> +#include <linux/log2.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/mtd/mtd.h>
>>>> +#include <linux/mtd/partitions.h>
>>>> +#include <linux/mutex.h>
>>>> +#include <linux/of.h>
>>>> +#include <linux/of_address.h>
>>>> +#include <linux/param.h>
>>>> +#include <linux/platform_device.h>
>>>> +#include <linux/pm.h>
>>>> +#include <linux/sched.h>
>>>> +#include <linux/slab.h>
>>>> +#include <linux/wait.h>
>>>> +
>>>> +#include "altera_epcq.h"
>>>> +
>>>> +/* data structure to maintain flash ids from different vendors */
>>>> +struct flash_device {
>>>> +     char *name;
>>>> +     bool is_epcs;
>>>> +     u32 device_id;
>>>> +     uint32_t sectorsize_inbytes;
>>>> +     uint64_t size_inbytes;
>>>> +     u32 pagesize;
>>>> +};
>>>> +
>>>> +#define FLASH_ID(_n, _is_epcs, _id, _ssize, _size, _psize)   \
>>>> +{                            \
>>>> +     .name = (_n),           \
>>>> +     .is_epcs = (_is_epcs),          \
>>>> +     .device_id = (_id),     \
>>>> +     .sectorsize_inbytes = (_ssize), \
>>>> +     .size_inbytes = (_size),        \
>>>> +     .pagesize = (_psize),   \
>>>> +}
>>>> +
>>>> +static struct flash_device flash_devices[] = {
>>>> +     FLASH_ID("epcq16"    , 0, 0x15, 0x10000, 0x200000, 0x100),
>>>> +     FLASH_ID("epcq32"    , 0, 0x16, 0x10000, 0x400000, 0x100),
>>>> +     FLASH_ID("epcq64"    , 0, 0x17, 0x10000, 0x800000, 0x100),
>>>> +     FLASH_ID("epcq128"   , 0, 0x18, 0x10000, 0x1000000, 0x100),
>>>> +     FLASH_ID("epcq256"   , 0, 0x19, 0x10000, 0x2000000, 0x100),
>>>> +     FLASH_ID("epcq512"   , 0, 0x20, 0x10000, 0x4000000, 0x100),
>>>> +
>>>> +     FLASH_ID("epcs16"    , 1, 0x14, 0x10000, 0x200000, 0x100),
>>>> +     FLASH_ID("epcs64"    , 1, 0x16, 0x10000, 0x800000, 0x100),
>>>> +     FLASH_ID("epcs128"   , 1, 0x18, 0x40000, 0x1000000, 0x100),
>>>> +};
>>>> +
>>>> +static inline struct altera_epcq_flash *get_flash_data(struct mtd_info *mtd)
>>>> +{
>>>> +     return container_of(mtd, struct altera_epcq_flash, mtd);
>>>> +}
>>>> +
>>>> +static u32 altera_epcq_read_sr(struct altera_epcq *dev)
>>>> +{
>>>> +     return readl(dev->csr_base + EPCQ_SR_REG);
>>>> +}
>>>> +
>>>> +static int altera_epcq_wait_till_ready(struct altera_epcq *dev)
>>>> +{
>>>> +     unsigned long finish;
>>>> +     int status;
>>>> +
>>>> +     finish = jiffies + EPCQ_MAX_TIME_OUT;
>>>> +     do {
>>>> +             status = altera_epcq_read_sr(dev);
>>>> +             if (status < 0)
>>>> +                     return status;
>>>> +             else if (!(status & EPCQ_SR_WIP))
>>>> +                     return 0;
>>>> +
>>>> +             cond_resched();
>>>> +     } while (!time_after_eq(jiffies, finish));
>>>> +
>>>> +     dev_err(&dev->pdev->dev, "epcq controller is busy, timeout\n");
>>>> +     return -EBUSY;
>>>> +}
>>>> +
>>>> +static int get_flash_index(u32 flash_id, bool is_epcs)
>>>> +{
>>>> +     int index;
>>>> +
>>>> +     for (index = 0; index < ARRAY_SIZE(flash_devices); index++) {
>>>> +             if (flash_devices[index].device_id == flash_id &&
>>>> +                 flash_devices[index].is_epcs == is_epcs)
>>>> +                     return index;
>>>> +     }
>>>> +
>>>> +     /* Memory chip is not listed and not supported */
>>>> +     return -ENODEV;
>>>> +}
>>>> +
>>>> +static int altera_epcq_write_erase_check(struct altera_epcq *dev,
>>>> +                                      bool write_erase)
>>>> +{
>>>> +     u32 val;
>>>> +     u32 mask;
>>>> +
>>>> +     if (write_erase)
>>>> +             mask = EPCQ_ISR_ILLEGAL_WRITE_MASK;
>>>> +     else
>>>> +             mask = EPCQ_ISR_ILLEGAL_ERASE_MASK;
>>>> +
>>>> +     val = readl(dev->csr_base + EPCQ_ISR_REG);
>>>> +     if (val & mask) {
>>>> +             dev_err(&dev->pdev->dev,
>>>> +                     "write/erase failed, sector might be protected\n");
>>>> +             /*clear this status for next use*/
>>>> +             writel(val, dev->csr_base + EPCQ_ISR_REG);
>>>> +             return -EIO;
>>>> +     }
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static int altera_epcq_erase_chip(struct mtd_info *mtd)
>>>> +{
>>>> +     int ret;
>>>> +     u32 val;
>>>> +     struct altera_epcq *dev = mtd->priv;
>>>> +
>>>> +     /* Wait until finished previous write command. */
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>
>>> This pattern is pretty silly. We don't need to have every operation
>>> check the status of the previous operation. Each operation should check
>>> itself. You obviously copied this one... but we recently changed that:
>>>
>>>     commit dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3
>>>     Author: Brian Norris <computersforpeace@gmail.com>
>>>     Date:   Wed Aug 6 18:16:57 2014 -0700
>>>
>>>         mtd: spi-nor: move "wait-till-ready" checks into erase/write
>>>         functions
>>>
>>>
>>>     https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3
>>>
Noted
>>>> +     if (ret)
>>>> +             return ret;
>>>> +
>>>> +     /* erase chip. */
>>>> +     val = EPCQ_MEM_OP_BULK_ERASE_CMD;
>>>> +     writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
>>>> +
>>>> +     /* Wait until finished previous write command. */
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>> +     if (ret)
>>>> +             return ret;
>>>> +
>>>> +      /* check whether write triggered a illegal write interrupt */
>>>> +     ret = altera_epcq_write_erase_check(dev, 0);
>>>> +     if (ret < 0)
>>>> +             return ret;
>>>> +
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static int altera_epcq_addr_to_sector(struct altera_epcq_flash *flash,
>>>> +                                   int addr_offset)
>>>> +{
>>>> +     int sector = 0;
>>>> +     int i;
>>>> +
>>>> +     for (i = 0; i < flash->num_sectors; i++) {
>>>> +             if (addr_offset >= i * flash->mtd.erasesize && addr_offset <
>>>> +                 (i * flash->mtd.erasesize + flash->mtd.erasesize)) {
>>>> +                     sector = i;
>>>> +                     return sector;
>>>> +             }
>>>> +     }
>>>
>>> Uh, really? Am I crazy, or shouldn't this whole function just be a
>>> really simple arithmetic operation? Like:
>>>
>>>         return addr_offset >> mtd->erasesize_shift;
>>>
My bad. This is so much easier this way.
>>>> +     return -1;
>>>> +}
>>>> +
>>>> +static int altera_epcq_erase_sector(struct mtd_info *mtd,
>>>> +                                 int addr_offset)
>>>> +{
>>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>>> +     struct altera_epcq *dev = mtd->priv;
>>>> +     u32 val;
>>>> +     int ret;
>>>> +     int sector_value;
>>>> +
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>> +     if (ret)
>>>> +             return ret;
>>>> +
>>>> +     sector_value = altera_epcq_addr_to_sector(flash, addr_offset);
>>>> +
>>>> +     /* sanity check that block_offset is a valid sector number */
>>>> +     if (sector_value < 0)
>>>> +             return -EINVAL;
>>>> +
>>>> +     /* sector value should occupy bits 17:8 */
>>>> +     val = (sector_value << 8) & EPCQ_MEM_OP_SECTOR_VALUE_MASK;
>>>> +
>>>> +     /* sector erase commands occupies lower 2 bits */
>>>> +     val |= EPCQ_MEM_OP_SECTOR_ERASE_CMD;
>>>> +
>>>> +     /* write sector erase command to EPCQ_MEM_OP register*/
>>>> +     writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
>>>> +
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>> +     if (ret)
>>>> +             return ret;
>>>> +
>>>> +      /* check whether write triggered a illegal write interrupt */
>>>
>>> ^^ extra space here? You have <TAB><SPACE>/* check [...]
>>>
Noted :P
>>>> +     ret = altera_epcq_write_erase_check(dev, 0);
>>>> +     if (ret < 0)
>>>> +             return ret;
>>>> +
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static int altera_epcq_erase(struct mtd_info *mtd, struct erase_info *e_info)
>>>> +{
>>>> +     u32 addr;
>>>> +     int ret;
>>>> +     u32 len;
>>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>>> +     struct altera_epcq *dev = mtd->priv;
>>>> +
>>>> +     addr = e_info->addr;
>>>> +     len = e_info->len;
>>>> +
>>>> +     if (flash->bank > dev->num_flashes - 1) {
>>>> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
>>>> +             return -EINVAL;
>>>> +     }
>>>> +     mutex_lock(&flash->lock);
>>>> +
>>>> +     /*erase whole chip*/
>>>> +     if (len == mtd->size) {
>>>> +             if (altera_epcq_erase_chip(mtd)) {
>>>> +                     e_info->state = MTD_ERASE_FAILED;
>>>> +                     mutex_unlock(&flash->lock);
>>>> +                     return -EIO;
>>>> +             }
>>>> +     /*"sector"-at-a-time erase*/
>>>> +     } else {
>>>> +             while (len) {
>>>> +                     ret = altera_epcq_erase_sector(mtd, addr);
>>>> +                     if (ret) {
>>>> +                             e_info->state = MTD_ERASE_FAILED;
>>>> +                             mutex_unlock(&flash->lock);
>>>> +                             return -EIO;
>>>> +                     }
>>>> +                     addr += mtd->erasesize;
>>>> +                     if (len < mtd->erasesize)
>>>> +                             len = 0;
>>>> +                     else
>>>> +                             len -= mtd->erasesize;
>>>> +             }
>>>> +     }
>>>> +     mutex_unlock(&flash->lock);
>>>> +     e_info->state = MTD_ERASE_DONE;
>>>> +     mtd_erase_callback(e_info);
>>>> +
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static int altera_epcq_read(struct mtd_info *mtd, loff_t from, size_t len,
>>>> +                         size_t *retlen, u8 *buf)
>>>> +{
>>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>>> +     struct altera_epcq *dev = mtd->priv;
>>>> +     void *src;
>>>> +     int ret = 0;
>>>> +
>>>> +     if (!flash || !dev)
>>>> +             return -ENODEV;
>>>
>>> Why would either of these be 0? If they are, you have much bigger
>>> problems, since you aren't checking these almost anywhere else. And the
>>> !flash check is pretty odd, since get_flash_data() is using
>>> container_of(), which is not likely to give you exactly 0...
>>>
Ok
>>>> +
>>>> +     if (flash->bank > dev->num_flashes - 1) {
>>>> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
>>>> +             return -EINVAL;
>>>> +     }
>>>> +
>>>> +     src = dev->data_base + from;
>>>> +
>>>> +     mutex_lock(&flash->lock);
>>>> +     /* wait till previous write/erase is done. */
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>> +     if (ret)
>>>> +             goto err;
>>>> +
>>>> +     memcpy_fromio(buf, (u8 *)src, len);
>>>
>>> Drop the cast. Also, you get sparse warnings because you're dropping the
>>> __iomem. Why not just this?
>>>
>>>         memcpy_fromio(buf, dev->data_base + from, len);
>>>
>>> (BTW, you might consider running sparse on all your code. See
>>> Documentation/sparse.txt. It tells you how to get it, but you can often
>>> just download it through your distro's package manager.)
>>>
Noted
>>>> +     *retlen = len;
>>>> +
>>>> +err:
>>>> +     mutex_unlock(&flash->lock);
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static int altera_epcq_write(struct mtd_info *mtd, loff_t to, size_t len,
>>>> +                          size_t *retlen, const u8 *buf)
>>>> +{
>>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>>> +     struct altera_epcq *dev = mtd->priv;
>>>> +     void *dest;
>>>> +     int ret = 0;
>>>> +
>>>> +     if (!flash || !dev)
>>>> +             return -ENODEV;
>>>
>>> Also here. I don't think you need these checks.
>>>
Noted
>>>> +
>>>> +     if (flash->bank > dev->num_flashes - 1) {
>>>> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
>>>> +             return -EINVAL;
>>>> +     }
>>>> +     dest = dev->data_base + to;
>>>> +
>>>> +     mutex_lock(&flash->lock);
>>>> +
>>>> +     /* wait until finished previous write command. */
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>> +     if (ret)
>>>> +             goto err;
>>>> +
>>>> +     memcpy_toio(dest, buf, len);
>>>
>>> Similar. Why not just this?
>>>
>>>         memcpy_toio(dev->data_base + to, buf, len);
>>>
Noted
>>>> +     *retlen += len;
>>>> +
>>>> +     /* wait until finished previous write command. */
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>> +     if (ret)
>>>> +             goto err;
>>>> +
>>>> +      /* check whether write triggered a illegal write interrupt */
>>>> +     ret = altera_epcq_write_erase_check(dev, 1);
>>>> +     if (ret < 0)
>>>> +             goto err;
>>>> +
>>>> +err:
>>>> +     mutex_unlock(&flash->lock);
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static int altera_epcq_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
>>>> +{
>>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>>> +     struct altera_epcq *dev = mtd->priv;
>>>> +     uint32_t offset = ofs;
>>>> +     int ret = 0;
>>>> +     u32 sector_start, sector_end;
>>>> +     u32 num_sectors;
>>>> +     u32 mem_op;
>>>> +     unsigned sr_bp = 0;
>>>> +     unsigned sr_tb = 0;
>>>> +
>>>> +     sector_start = altera_epcq_addr_to_sector(flash, offset);
>>>> +     sector_end = altera_epcq_addr_to_sector(flash, offset + len);
>>>> +     num_sectors = flash->num_sectors;
>>>> +     dev_dbg(&dev->pdev->dev,
>>>> +             "%s: num_setor is %u,sector start is %u,sector end is %u\n",
>>>> +             __func__, num_sectors, sector_start, sector_end);
>>>> +
>>>> +     mutex_lock(&flash->lock);
>>>> +     /* wait until finished previous write command. */
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>> +     if (ret)
>>>> +             goto err;
>>>> +
>>>> +     if (sector_start >= num_sectors/2) {
>>>
>>> It's preferable to have spaces around operators. So 'num_sectors / 2'.
>>> The same thing comes up in several other places.
>>>
Noted
>>>> +             if (sector_start < num_sectors-(num_sectors / 4))
>>>> +                     sr_bp = __ilog2_u32(num_sectors);
>>>> +             else if (sector_start < num_sectors-(num_sectors / 8))
>>>> +                     sr_bp = __ilog2_u32(num_sectors) - 1;
>>>> +             else if (sector_start < num_sectors-(num_sectors / 16))
>>>> +                     sr_bp = __ilog2_u32(num_sectors) - 2;
>>>> +             else if (sector_start < num_sectors-(num_sectors / 32))
>>>> +                     sr_bp = __ilog2_u32(num_sectors) - 3;
>>>> +             else if (sector_start < num_sectors-(num_sectors / 64))
>>>> +                     sr_bp = __ilog2_u32(num_sectors) - 4;
>>>> +             else if (sector_start < num_sectors-(num_sectors / 128))
>>>> +                     sr_bp = __ilog2_u32(num_sectors) - 5;
>>>> +             else if (sector_start < num_sectors-(num_sectors / 256))
>>>> +                     sr_bp = __ilog2_u32(num_sectors) - 6;
>>>> +             else if (sector_start < num_sectors-(num_sectors / 512))
>>>> +                     sr_bp = __ilog2_u32(num_sectors) - 7;
>>>> +             else if (sector_start < num_sectors-(num_sectors / 1024))
>>>> +                     sr_bp = __ilog2_u32(num_sectors) - 8;
>>>> +             else
>>>> +                     sr_bp = 0;  /* non area protected */
>>>
>>> Yikes, that's ugly! And I'm not sure it matches the EPCQ doc I found.
>>> I'm pretty sure you can rewrite this if/else-if/else block in about 1
>>> line though.
>>>
Yes, i understand that it looks ugly. But it is the best i can do
since this function has to satisfy for all the supported devices
(http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf, start
from page 19)
>>>> +
>>>> +             if (sr_bp < 0) {
>>>
>>> sr_bp is unsigned, so this is never true.
>>>
Ok.  I will change to int type.
>>>> +                     dev_err(&dev->pdev->dev, "%s: address is out of range\n"
>>>> +                             , __func__);
>>>> +                     ret = -EINVAL;
>>>> +                     goto err;
>>>> +             }
>>>> +             /*set TB = 0*/
>>>> +             sr_tb = 0;
>>>> +
>>>> +     } else {
>>>> +             if (sector_end < 1)
>>>> +                     sr_bp = 1;
>>>> +             else if (sector_end < 2)
>>>> +                     sr_bp = 2;
>>>> +             else if (sector_end < 4)
>>>> +                     sr_bp = 3;
>>>> +             else if (sector_end < 8)
>>>> +                     sr_bp = 4;
>>>> +             else if (sector_end < 16)
>>>> +                     sr_bp = 5;
>>>> +             else if (sector_end < 32)
>>>> +                     sr_bp = 6;
>>>> +             else if (sector_end < 64)
>>>> +                     sr_bp = 7;
>>>> +             else if (sector_end < 128)
>>>> +                     sr_bp = 8;
>>>> +             else if (sector_end < 256)
>>>> +                     sr_bp = 9;
>>>> +             else if (sector_end < 512)
>>>> +                     sr_bp = 10;
>>>> +             else
>>>> +                     sr_bp = 16; /*protect all areas*/
>>>
>>> Again, this is ugly. How about this?
>>>
>>>         sr_bp = fls(sector_end) + 1;
>>>
Oh. I did not know got this fls function. I will apply it.
>>>> +
>>>> +             sr_tb = 1;
>>>> +     }
>>>> +
>>>> +     mem_op = (sr_tb << 12) | (sr_bp << 8);
>>>> +     mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
>>>> +     mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
>>>> +     writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
>>>> +
>>>> +err:
>>>> +     mutex_unlock(&flash->lock);
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static int altera_epcq_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
>>>> +{
>>>> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
>>>> +     struct altera_epcq *dev = mtd->priv;
>>>> +
>>>> +     int ret = 0;
>>>> +     u32 mem_op;
>>>> +
>>>> +     mutex_lock(&flash->lock);
>>>> +     /* wait until finished previous write command. */
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>> +     if (ret)
>>>> +             goto err;
>>>> +     dev_info(&dev->pdev->dev, "Unlock all protected area\n");
>>>> +     mem_op = 0;
>>>> +     mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
>>>> +     mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
>>>> +     writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
>>>> +
>>>> +err:
>>>> +     mutex_unlock(&flash->lock);
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static void altera_epcq_chip_select(struct altera_epcq *dev, u32 bank)
>>>> +{
>>>> +     u32 val = 0;
>>>> +
>>>> +     switch (bank) {
>>>> +     case 0:
>>>> +             val = EPCQ_CHIP_SELECT_0;
>>>> +             break;
>>>> +     case 1:
>>>> +             val = EPCQ_CHIP_SELECT_1;
>>>> +             break;
>>>> +     case 2:
>>>> +             val = EPCQ_CHIP_SELECT_2;
>>>> +             break;
>>>> +     default:
>>>> +             return;
>>>> +     }
>>>> +
>>>> +     writel(val, dev->csr_base + EPCQ_CHIP_SELECT_REG);
>>>> +}
>>>> +
>>>> +static int altera_epcq_probe_flash(struct altera_epcq *dev, u32 bank)
>>>> +{
>>>> +     int ret = 0;
>>>> +     u32 val = 0;
>>>> +
>>>> +     mutex_lock(&dev->lock);
>>>> +
>>>> +     /*select bank*/
>>>
>>> Comment spacing, here and elsewhere:
>>>
Ok
>>>         /* select bank */
>>>
>>>> +     altera_epcq_chip_select(dev, bank);
>>>> +
>>>> +     ret = altera_epcq_wait_till_ready(dev);
>>>> +     if (ret)
>>>> +             goto err;
>>>> +
>>>> +     /* get device sillicon id */
>>>> +     if (dev->is_epcs)
>>>> +             val = readl(dev->csr_base + EPCQ_SID_REG);
>>>> +     else
>>>> +             val = readl(dev->csr_base + EPCQ_RDID_REG);
>>>> +
>>>> +     /* get flash index based on the device list*/
>>>> +     ret = get_flash_index(val, dev->is_epcs);
>>>> +     return 0;
>>>> +err:
>>>> +     mutex_unlock(&dev->lock);
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static int altera_epcq_probe_config_dt(struct platform_device *pdev,
>>>> +                                    struct device_node *np,
>>>> +                                    struct altera_epcq_plat_data *pdata)
>>>> +{
>>>> +     struct device_node *pp = NULL;
>>>> +     struct resource *epcq_res;
>>>> +     int i = 0;
>>>> +     u32 id;
>>>> +
>>>> +     pdata->is_epcs = of_property_read_bool(np, "is-epcs");
>>>> +
>>>> +     epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
>>>> +                                             "csr_base");
>>>> +     if (!epcq_res) {
>>>
>>> devm_ioremap_resource() will check the that 'epcq_res' is valid, so you
>>> don't need this whole 'if' block.
>>>
Ok
>>>> +             dev_err(&pdev->dev, "resource csr base is not defined\n");
>>>> +             return -ENODEV;
>>>> +     }
>>>> +
>>>> +     pdata->csr_base = devm_ioremap_resource(&pdev->dev, epcq_res);
>>>> +     if (IS_ERR(pdata->csr_base)) {
>>>> +             dev_err(&pdev->dev, "%s: ERROR: failed to map csr base\n",
>>>> +                     __func__);
>>>> +             return PTR_ERR(pdata->csr_base);
>>>> +     }
>>>> +
>>>> +     epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
>>>> +                                             "data_base");
>>>> +     if (!epcq_res) {
>>>
>>> Same here.
Ok
>>>
>>>> +             dev_err(&pdev->dev, "resource data base is not defined\n");
>>>> +             return -ENODEV;
>>>> +     }
>>>> +
>>>> +     pdata->data_base = devm_ioremap_resource(&pdev->dev, epcq_res);
>>>> +     if (IS_ERR(pdata->data_base)) {
>>>> +             dev_err(&pdev->dev, "%s: ERROR: failed to map data base\n",
>>>> +                     __func__);
>>>> +             return PTR_ERR(pdata->data_base);
>>>> +     }
>>>> +
>>>> +     pdata->board_flash_info = devm_kzalloc(&pdev->dev,
>>>> +                                            sizeof(*pdata->board_flash_info),
>>>> +                                            GFP_KERNEL);
>>>> +
>>>> +     /* Fill structs for each subnode (flash device) */
>>>> +     while ((pp = of_get_next_child(np, pp))) {
>>>
>>> for_each_available_child_of_node()?
>>>
>>>> +             struct altera_epcq_flash_info *flash_info;
>>>> +
>>>> +             flash_info = &pdata->board_flash_info[i];
>>>
>>> This is never used. Either use it below, or drop the local variable.
>>>
Ok
>>>> +             pdata->np[i] = pp;
>>>> +
>>>> +             /* Read bank id from DT */
>>>> +             if (of_get_property(pp, "reg", &id))
>>>
>>> Is this property optional? Your DT binding doc doesn't make it clear,
>>> but it seems like a property which would be wise to require (i.e., not
>>> optional).
>>>
>>>> +                     pdata->board_flash_info[i].bank = id;
>>>> +             i++;
>>>> +     }
>>>> +     pdata->num_flashes = i;
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static int altera_epcq_setup_banks(struct platform_device *pdev,
>>>> +                                u32 bank, struct device_node *np,
>>>> +                                struct altera_epcq_plat_data *pdata)
>>>> +{
>>>> +     struct altera_epcq *dev = platform_get_drvdata(pdev);
>>>> +     struct mtd_part_parser_data ppdata = {};
>>>> +     struct altera_epcq_flash_info *flash_info;
>>>> +     struct altera_epcq_flash *flash;
>>>> +     struct mtd_partition *parts = NULL;
>>>
>>> Drop this variable. Just use NULL directly below.
>>>
>>>> +     int count = 0;
>>>
>>> Same. Just use 0 below.
>>>
Ok
>>>> +     int flash_index;
>>>> +     int ret = 0;
>>>> +     uint64_t size;
>>>> +     uint32_t sector_size;
>>>> +
>>>> +     flash_info = &pdata->board_flash_info[bank];
>>>> +     if (!flash_info)
>>>> +             return -ENODEV;
>>>> +
>>>> +     if (bank > pdata->num_flashes - 1)
>>>> +             return -EINVAL;
>>>> +
>>>> +     flash = devm_kzalloc(&pdev->dev, sizeof(*flash), GFP_KERNEL);
>>>> +     if (!flash)
>>>> +             return -ENOMEM;
>>>> +     flash->bank = bank;
>>>> +
>>>> +     mutex_init(&flash->lock);
>>>> +
>>>> +     /* verify whether flash is really present on board */
>>>> +     flash_index = altera_epcq_probe_flash(dev, bank);
>>>> +     if (flash_index < 0) {
>>>> +             dev_info(&dev->pdev->dev, "epcq:flash%d not found\n",
>>>> +                      flash->bank);
>>>> +             return flash_index;
>>>> +     }
>>>> +
>>>> +     dev->flash[bank] = flash;
>>>> +
>>>> +     size = flash_devices[flash_index].size_inbytes;
>>>> +     sector_size = flash_devices[flash_index].sectorsize_inbytes;
>>>> +     /*use do_div instead of plain div to avoid linker err*/
>>>> +     do_div(size, sector_size);
>>>> +     flash->num_sectors = size;
>>>> +
>>>> +     /*mtd framework */
>>>> +     flash->mtd.priv = dev;
>>>> +     flash->mtd.name = flash_devices[flash_index].name;
>>>> +     flash->mtd.type = MTD_NORFLASH;
>>>> +     flash->mtd.writesize = 1;
>>>> +     flash->mtd.flags = MTD_CAP_NORFLASH;
>>>> +     flash->mtd.size = flash_devices[flash_index].size_inbytes;
>>>> +     flash->mtd.erasesize = flash_devices[flash_index].sectorsize_inbytes;
>>>> +     flash->mtd._erase = altera_epcq_erase;
>>>> +     flash->mtd._read = altera_epcq_read;
>>>> +     flash->mtd._write = altera_epcq_write;
>>>> +     flash->mtd._lock = altera_epcq_lock;
>>>> +     flash->mtd._unlock = altera_epcq_unlock;
>>>> +
>>>> +     dev_info(&pdev->dev, "mtd .name=%s .size=0x%llx (%lluM)\n",
>>>> +              flash->mtd.name, (long long)flash->mtd.size,
>>>> +              (long long)(flash->mtd.size >> 20));
>>>> +
>>>> +     dev_info(&pdev->dev, ".erasesize = 0x%x(%uK)\n",
>>>> +              flash->mtd.erasesize, flash->mtd.erasesize >> 10);
>>>> +
>>>> +     ppdata.of_node = np;
>>>> +
>>>> +     ret = mtd_device_parse_register(&flash->mtd, NULL, &ppdata, parts,
>>>> +                                     count);
>>>> +     if (ret) {
>>>> +             dev_err(&pdev->dev, "Err MTD partition=%d\n", ret);
>>>> +             goto err;
>>>
>>> You don't want to unregister a MTD that failed to register. The MTD core
>>> code should handle that itself.
>>>
>>> Instead of this if (ret) {} block, I'd just make this:
>>>
>>>         return mtd_device_parse_register(&flash->mtd, NULL, &ppdata, NULL, 0);
>>>
Ok
>>>> +     }
>>>> +
>>>> +     return 0;
>>>> +
>>>> +err:
>>>> +     mtd_device_unregister(&flash->mtd);
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static int altera_epcq_probe(struct platform_device *pdev)
>>>> +{
>>>> +     struct device_node *np = pdev->dev.of_node;
>>>> +     struct altera_epcq_plat_data *pdata = NULL;
>>>> +     struct altera_epcq *dev;
>>>> +     int ret = 0;
>>>> +     int i;
>>>> +
>>>> +     if (!np) {
>>>> +             ret = -ENODEV;
>>>> +             dev_err(&pdev->dev, "no device found\n");
>>>> +             goto err;
>>>> +     }
>>>> +
>>>> +     pdata = devm_kzalloc(&pdev->dev,
>>>> +                          sizeof(struct altera_epcq_plat_data),
>>>> +                          GFP_KERNEL);
>>>> +
>>>> +     if (!pdata) {
>>>> +             ret = -ENOMEM;
>>>> +             dev_err(&pdev->dev, "%s: ERROR: no memory\n", __func__);
>>>
>>> Unnecessary print. The MM code will print plenty of info if you're
>>> out of memory.
>>>
>>>> +             goto err;
>>>> +     }
>>>> +     ret = altera_epcq_probe_config_dt(pdev, np, pdata);
>>>> +     if (ret) {
>>>> +             ret = -ENODEV;
>>>> +             dev_err(&pdev->dev, "probe fail\n");
>>>> +             goto err;
>>>> +     }
>>>> +
>>>> +     dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
>>>> +     if (!dev) {
>>>> +             ret = -ENOMEM;
>>>> +             dev_err(&pdev->dev, "mem alloc fail\n");
>>>
>>> Same here.
>>>
>>>> +             goto err;
>>>> +     }
>>>> +     mutex_init(&dev->lock);
>>>> +     dev->pdev = pdev;
>>>> +     dev->is_epcs = pdata->is_epcs;
>>>> +     dev->csr_base = pdata->csr_base;
>>>> +     dev->data_base = pdata->data_base;
>>>> +
>>>> +     /*check number of flashes*/
>>>> +     dev->num_flashes = pdata->num_flashes;
>>>> +     if (dev->num_flashes > MAX_NUM_FLASH_CHIP) {
>>>> +             dev_err(&pdev->dev, "exceeding max number of flashes\n");
>>>> +             dev->num_flashes = MAX_NUM_FLASH_CHIP;
>>>> +     }
>>>> +
>>>> +     /* check clock*/
>>>> +     dev->clk = devm_clk_get(&pdev->dev, NULL);
>>>> +     if (IS_ERR(dev->clk)) {
>>>> +             ret = PTR_ERR(dev->clk);
>>>> +             goto err;
>>>> +     }
>>>> +     ret = clk_prepare_enable(dev->clk);
>>>> +     if (ret)
>>>> +             goto err;
>>>> +
>>>> +     platform_set_drvdata(pdev, dev);
>>>> +
>>>> +     /* loop for each serial flash which is connected to epcq */
>>>> +     for (i = 0; i < dev->num_flashes; i++) {
>>>> +             ret = altera_epcq_setup_banks(pdev, i, pdata->np[i], pdata);
>>>> +             if (ret) {
>>>> +                     dev_err(&pdev->dev, "bank setup failed\n");
>>>> +                     goto err_bank_setup;
>>>> +             }
>>>> +     }
>>>> +
>>>> +     return 0;
>>>> +
>>>> +err_bank_setup:
>>>> +     clk_disable_unprepare(dev->clk);
>>>> +err:
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static int altera_epcq_remove(struct platform_device *pdev)
>>>> +{
>>>> +     struct altera_epcq *dev;
>>>> +     struct altera_epcq_flash *flash;
>>>> +     int ret, i;
>>>> +
>>>> +     dev = platform_get_drvdata(pdev);
>>>> +
>>>> +     /* clean up for all nor flash */
>>>> +     for (i = 0; i < dev->num_flashes; i++) {
>>>> +             flash = dev->flash[i];
>>>> +             if (!flash)
>>>> +                     continue;
>>>> +
>>>> +             /* clean up mtd stuff */
>>>> +             ret = mtd_device_unregister(&flash->mtd);
>>>> +             if (ret)
>>>> +                     dev_err(&pdev->dev, "error removing mtd\n");
>>>> +     }
>>>> +
>>>> +     clk_disable_unprepare(dev->clk);
>>>> +
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +#ifdef CONFIG_PM
>>>
>>> Make this CONFIG_PM_SLEEP.
>>>
>>>> +static int altera_epcq_suspend(struct device *dev)
>>>> +{
>>>> +     struct altera_epcq *sdev = dev_get_drvdata(dev);
>>>> +
>>>> +     clk_disable_unprepare(sdev->clk);
>>>> +
>>>> +     return 0;
>>>> +}
>>>> +
>>>> +static int altera_epcq_resume(struct device *dev)
>>>> +{
>>>> +     struct altera_epcq *sdev = dev_get_drvdata(dev);
>>>> +     int ret = -EPERM;
>>>
>>> Drop 'ret'.
>>>
>>>> +
>>>> +     ret = clk_prepare_enable(sdev->clk);
>>>
>>> Just:
>>>
>>>         return clk_prepare_enable(sdev->clk);
>>>
>>>> +
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static SIMPLE_DEV_PM_OPS(altera_epcq_pm_ops, altera_epcq_suspend,
>>>> +                      altera_epcq_resume);
>>>> +#endif
>>>> +
>>>> +static const struct of_device_id altera_epcq_id_table[] = {
>>>> +     { .compatible = "altr,epcq-1.0" },
>>>> +     {}
>>>> +};
>>>> +MODULE_DEVICE_TABLE(of, altera_epcq_id_table);
>>>> +
>>>> +static struct platform_driver altera_epcq_driver = {
>>>> +     .driver = {
>>>> +             .name = ALTERA_EPCQ_RESOURCE_NAME,
>>>> +             .bus = &platform_bus_type,
>>>> +             .owner = THIS_MODULE,
>>>
>>> The .owner assignment is no longer necessary.
>>>
>>>> +             .of_match_table = altera_epcq_id_table,
>>>> +#ifdef CONFIG_PM
>>>
>>> Also CONFIG_PM_SLEEP.
>>>
>>>> +             .pm = &altera_epcq_pm_ops,
>>>> +#endif
>>>> +     },
>>>> +     .probe = altera_epcq_probe,
>>>> +     .remove = altera_epcq_remove,
>>>> +};
>>>> +module_platform_driver(altera_epcq_driver);
>>>> +
>>>> +MODULE_AUTHOR("Viet Nga Dao <vndao@altera.com>");
>>>> +MODULE_DESCRIPTION("MTD Altera EPCQ Driver");
>>>> +MODULE_LICENSE("GPL v2");
>>>> diff --git a/drivers/mtd/devices/altera_epcq.h b/drivers/mtd/devices/altera_epcq.h
>>>> new file mode 100644
>>>> index 0000000..c3d15e5
>>>> --- /dev/null
>>>> +++ b/drivers/mtd/devices/altera_epcq.h
>>>> @@ -0,0 +1,130 @@
>>>> +/*
>>>> + * Copyright (C) 2014 Altera Corporation. All rights reserved
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or modify it
>>>> + * under the terms and conditions of the GNU General Public License,
>>>> + * version 2, as published by the Free Software Foundation.
>>>> + *
>>>> + * This program is distributed in the hope 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.
>>>> + *
>>>> + * You should have received a copy of the GNU General Public License along with
>>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>>> + */
>>>> +
>>>> +#ifndef __ALTERA_ECPQ_H
>>>> +#define __ALTERA_ECPQ_H
>>>> +
>>>> +#define ALTERA_EPCQ_RESOURCE_NAME        "altera_epcq"
>>>> +/* max possible slots for serial flash chip in the EPCQ controller */
>>>> +#define MAX_NUM_FLASH_CHIP   3
>>>> +
>>>> +/* macro to define partitions for flash devices */
>>>> +#define DEFINE_PARTS(n, of, s)               \
>>>
>>> This macro is not used anywhere. Drop it?
>>>
Ok
>>>> +{                                    \
>>>> +     .name = n,                      \
>>>> +     .offset = of,                   \
>>>> +     .size = s,                      \
>>>> +}
>>>> +
>>>> +struct altera_epcq_flash_info {
>>>> +     u32 bank;
>>>> +     struct mtd_partition *partitions;
>>>> +     int nr_partitions;
>>>> +};
>>>> +
>>>> +struct altera_epcq_plat_data {
>>>> +     void __iomem *csr_base;
>>>> +     void __iomem *data_base;
>>>> +     bool is_epcs;
>>>> +     u32 num_flashes;
>>>> +     struct altera_epcq_flash_info *board_flash_info;
>>>> +     struct device_node *np[MAX_NUM_FLASH_CHIP];
>>>> +};
>>>> +
>>>> +struct altera_epcq {
>>>> +     struct clk *clk;
>>>> +     bool is_epcs;
>>>> +     struct mutex lock;      /*device lock*/
>>>> +     void __iomem *csr_base;
>>>> +     void __iomem *data_base;
>>>> +     struct platform_device *pdev;
>>>> +     u32 num_flashes;
>>>> +     struct altera_epcq_flash *flash[MAX_NUM_FLASH_CHIP];
>>>> +};
>>>> +
>>>> +struct altera_epcq_flash {
>>>> +     u32 bank;
>>>> +     u32 num_sectors;
>>>> +     u32 num_parts;
>>>> +     struct mtd_partition *parts;
>>>> +     struct mutex lock;      /*flash lock*/
>>>> +     struct mtd_info mtd;
>>>> +};
>>>> +
>>>> +/* Define max times to check status register before we give up. */
>>>> +#define EPCQ_MAX_TIME_OUT                    (40 * HZ)
>>>> +
>>>> +/* defines for status register */
>>>> +#define EPCQ_SR_REG                          0x0
>>>> +#define EPCQ_SR_WIP_MASK                     0x00000001
>>>> +#define EPCQ_SR_WIP                          0x1
>>>> +#define EPCQ_SR_WEL                          0x2
>>>> +#define EPCQ_SR_BP0                          0x4
>>>> +#define EPCQ_SR_BP1                          0x8
>>>> +#define EPCQ_SR_BP2                          0x10
>>>> +#define EPCQ_SR_BP3                          0x40
>>>> +#define EPCQ_SR_TB                           0x20
>>>> +
>>>> +/* defines for device id register */
>>>> +#define EPCQ_SID_REG                         0x4
>>>> +#define EPCQ_RDID_REG                                0x8
>>>> +#define EPCQ_RDID_MASK                               0x000000FF
>>>> +/*
>>>> + * EPCQ_MEM_OP register offset
>>>> + *
>>>> + * The EPCQ_MEM_OP register is used to do memory protect and erase operations
>>>> + *
>>>> + */
>>>> +#define EPCQ_MEM_OP_REG                              0xC
>>>> +
>>>> +#define EPCQ_MEM_OP_CMD_MASK                 0x00000003
>>>> +#define EPCQ_MEM_OP_BULK_ERASE_CMD           0x00000001
>>>> +#define EPCQ_MEM_OP_SECTOR_ERASE_CMD         0x00000002
>>>> +#define EPCQ_MEM_OP_SECTOR_PROTECT_CMD               0x00000003
>>>> +#define EPCQ_MEM_OP_SECTOR_VALUE_MASK                0x0003FF00
>>>> +#define EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK        0x00001F00
>>>> +#define EPCQ_MEM_OP_SECTOR_PROTECT_SHIFT     8
>>>> +/*
>>>> + * EPCQ_ISR register offset
>>>> + *
>>>> + * The EPCQ_ISR register is used to determine whether an invalid write or erase
>>>> + * operation trigerred an interrupt
>>>> + *
>>>> + */
>>>> +#define EPCQ_ISR_REG                         0x10
>>>> +
>>>> +#define EPCQ_ISR_ILLEGAL_ERASE_MASK          0x00000001
>>>> +#define EPCQ_ISR_ILLEGAL_WRITE_MASK          0x00000002
>>>> +
>>>> +/*
>>>> + * EPCQ_IMR register offset
>>>> + *
>>>> + * The EPCQ_IMR register is used to mask the invalid erase or the invalid write
>>>> + * interrupts.
>>>> + *
>>>> + */
>>>> +#define EPCQ_IMR_REG                         0x14
>>>> +#define EPCQ_IMR_ILLEGAL_ERASE_MASK          0x00000001
>>>> +
>>>> +#define EPCQ_IMR_ILLEGAL_WRITE_MASK          0x00000002
>>>> +
>>>> +#define EPCQ_CHIP_SELECT_REG                 0x18
>>>> +#define EPCQ_CHIP_SELECT_MASK                        0x00000007
>>>> +#define EPCQ_CHIP_SELECT_0                   0x00000001
>>>> +#define EPCQ_CHIP_SELECT_1                   0x00000002
>>>> +#define EPCQ_CHIP_SELECT_2                   0x00000004
>>>> +
>>>> +#endif /* __ALTERA_ECPQ_H */
>>>

Nga

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-02-04 20:37       ` Brian Norris
  0 siblings, 0 replies; 39+ messages in thread
From: Brian Norris @ 2015-02-04 20:37 UTC (permalink / raw)
  To: Viet Nga Dao
  Cc: David Woodhouse, linux-mtd, linux-kernel, devicetree, nga_chi86

On Tue, Jan 27, 2015 at 02:53:59PM +0800, Viet Nga Dao wrote:
> On Tue, Jan 20, 2015 at 10:05 AM, Viet Nga Dao <vndao@altera.com> wrote:
> > On Thu, Jan 15, 2015 at 5:27 PM, Viet Nga Dao <vndao@altera.com> wrote:
> >> On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
> >> <computersforpeace@gmail.com> wrote:
> >>> On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao@altera.com wrote:
> >>>> From: Viet Nga Dao <vndao@altera.com>

> >> That is why if rewrite the drivers to follow spi-nor structure, it
> >> will require extra decoding works for the only few used opcodes.
> >>
> >I think you'd only have some very trivial work here.
> >
> >There would be some small work to reintroduce a properly-replaceable ID
> >table, and callbacks like ->lock() and ->unlock(), but those should be
> >implemented in spi-nor.c sooner or later anyway.
> >
> 
> I am trying to modify the code to follow your recommendation. However,
> for lock and unlock functions, i have to implement it in spi_nor.c ,
> am i right? If yes, currently we already have existing spi_nor_lock
> and spi_nor_unlock functions but they could not apply for my driver.
> Should i create a new functions named altera_epcq_lock and unlock and
> then map it to callback functions or i should the put  those code
> sunder existing spi_nor_lock/unlock?

We probably want a replaceable spi_nor callback that does the
flash-specific unlock. spi_nor_lock/unlock would then call the
nor->unlock() / nor->lock() functions for you, when present.
Some of the existing code should move into their own spi_nor_st_lock() /
spi_nor_st_unlock() functions.

> >>>> diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
> >>>> new file mode 100644
> >>>> index 0000000..d14f50e
> >>>> --- /dev/null
> >>>> +++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
> >>>> @@ -0,0 +1,45 @@
> >>>> +* MTD Altera EPCQ driver
> >>>> +
> >>>> +Required properties:
> >>>> +- compatible: Should be "altr,epcq-1.0"
> >>>> +- reg: Address and length of the register set  for the device. It contains
> >>>> +  the information of registers in the same order as described by reg-names
> >>>> +- reg-names: Should contain the reg names
> >>>> +  "csr_base": Should contain the register configuration base address
> >>>> +  "data_base": Should contain the data base address
> >>>> +- is-epcs: boolean type.
> >>>> +             If present, the device contains EPCS flashes.
> >>>> +             Otherwise, it contains EPCQ flashes.
> >>>> +- #address-cells: Must be <1>.
> >>>> +- #size-cells: Must be <0>.
> >>>> +- flash device tree subnode, there must be a node with the following fields:
> >>>
> >>> These subnodes definitely require a 'compatible' string. Perhaps they
> >>> should be used to differentiate EPCS vs. EPCQ. Does "is-epcs" really
> >>> need to be in the top-level controller node?
> >>>
> >>>> +     - reg: Should contain the flash id
> >>>
> >>> Should, or must? (This question is relevant, because you seem to make it
> >>> optional in your code.) And what does the "flash ID" mean? It seems like
> >>> you're using as a chip-select or bank index.
> >>>
> Yes, this is used for chip select. It is required, not optional. This
> to ID for each flash in the device

OK, so correct the language here and enforce this in your driver. Right
now, you don't fail gracefully if this is missing.

> >>>> +             if (sector_start < num_sectors-(num_sectors / 4))
> >>>> +                     sr_bp = __ilog2_u32(num_sectors);
> >>>> +             else if (sector_start < num_sectors-(num_sectors / 8))
> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 1;
> >>>> +             else if (sector_start < num_sectors-(num_sectors / 16))
> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 2;
> >>>> +             else if (sector_start < num_sectors-(num_sectors / 32))
> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 3;
> >>>> +             else if (sector_start < num_sectors-(num_sectors / 64))
> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 4;
> >>>> +             else if (sector_start < num_sectors-(num_sectors / 128))
> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 5;
> >>>> +             else if (sector_start < num_sectors-(num_sectors / 256))
> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 6;
> >>>> +             else if (sector_start < num_sectors-(num_sectors / 512))
> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 7;
> >>>> +             else if (sector_start < num_sectors-(num_sectors / 1024))
> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 8;
> >>>> +             else
> >>>> +                     sr_bp = 0;  /* non area protected */
> >>>
> >>> Yikes, that's ugly! And I'm not sure it matches the EPCQ doc I found.
> >>> I'm pretty sure you can rewrite this if/else-if/else block in about 1
> >>> line though.
> >>>
> Yes, i understand that it looks ugly. But it is the best i can do
> since this function has to satisfy for all the supported devices
> (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf, start
> from page 19)

Did you even try? It was possible to simplify the other case, and I'm
pretty sure this case can be simplified too. How about this? I hacked
this together and it seems to match:

        if (sector_start <= num_sectors / 2)
		sr_bp = __ilog2_u32(num_sectors);
	else
		sr_bp = fls(num_sectors - 1 - sector_start) + 1;

> >>>> +
> >>>> +             if (sr_bp < 0) {
> >>>
> >>> sr_bp is unsigned, so this is never true.
> >>>
> Ok.  I will change to int type.

Are there ever negative values?

> >>>> +static int altera_epcq_probe_config_dt(struct platform_device *pdev,
> >>>> +                                    struct device_node *np,
> >>>> +                                    struct altera_epcq_plat_data *pdata)
> >>>> +{
> >>>> +     struct device_node *pp = NULL;
> >>>> +     struct resource *epcq_res;
> >>>> +     int i = 0;
> >>>> +     u32 id;
...
> >>>> +             pdata->np[i] = pp;
> >>>> +
> >>>> +             /* Read bank id from DT */
> >>>> +             if (of_get_property(pp, "reg", &id))

I just realized; you're not using this correctly. of_get_property()
returns the *length* in the third parameter, so you're not actually
saving the bank ID here. You probably want of_property_read_u32()
instead.

> >>>
> >>> Is this property optional? Your DT binding doc doesn't make it clear,
> >>> but it seems like a property which would be wise to require (i.e., not
> >>> optional).

^^^ so there should be a failure case, where you return failure if the
property is missing.

> >>>> +                     pdata->board_flash_info[i].bank = id;
> >>>> +             i++;
> >>>> +     }
> >>>> +     pdata->num_flashes = i;
> >>>> +     return 0;
> >>>> +}

Brian

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-02-04 20:37       ` Brian Norris
  0 siblings, 0 replies; 39+ messages in thread
From: Brian Norris @ 2015-02-04 20:37 UTC (permalink / raw)
  To: Viet Nga Dao
  Cc: David Woodhouse, linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, nga_chi86

On Tue, Jan 27, 2015 at 02:53:59PM +0800, Viet Nga Dao wrote:
> On Tue, Jan 20, 2015 at 10:05 AM, Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org> wrote:
> > On Thu, Jan 15, 2015 at 5:27 PM, Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org> wrote:
> >> On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
> >> <computersforpeace-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> >>> On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org wrote:
> >>>> From: Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org>

> >> That is why if rewrite the drivers to follow spi-nor structure, it
> >> will require extra decoding works for the only few used opcodes.
> >>
> >I think you'd only have some very trivial work here.
> >
> >There would be some small work to reintroduce a properly-replaceable ID
> >table, and callbacks like ->lock() and ->unlock(), but those should be
> >implemented in spi-nor.c sooner or later anyway.
> >
> 
> I am trying to modify the code to follow your recommendation. However,
> for lock and unlock functions, i have to implement it in spi_nor.c ,
> am i right? If yes, currently we already have existing spi_nor_lock
> and spi_nor_unlock functions but they could not apply for my driver.
> Should i create a new functions named altera_epcq_lock and unlock and
> then map it to callback functions or i should the put  those code
> sunder existing spi_nor_lock/unlock?

We probably want a replaceable spi_nor callback that does the
flash-specific unlock. spi_nor_lock/unlock would then call the
nor->unlock() / nor->lock() functions for you, when present.
Some of the existing code should move into their own spi_nor_st_lock() /
spi_nor_st_unlock() functions.

> >>>> diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
> >>>> new file mode 100644
> >>>> index 0000000..d14f50e
> >>>> --- /dev/null
> >>>> +++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
> >>>> @@ -0,0 +1,45 @@
> >>>> +* MTD Altera EPCQ driver
> >>>> +
> >>>> +Required properties:
> >>>> +- compatible: Should be "altr,epcq-1.0"
> >>>> +- reg: Address and length of the register set  for the device. It contains
> >>>> +  the information of registers in the same order as described by reg-names
> >>>> +- reg-names: Should contain the reg names
> >>>> +  "csr_base": Should contain the register configuration base address
> >>>> +  "data_base": Should contain the data base address
> >>>> +- is-epcs: boolean type.
> >>>> +             If present, the device contains EPCS flashes.
> >>>> +             Otherwise, it contains EPCQ flashes.
> >>>> +- #address-cells: Must be <1>.
> >>>> +- #size-cells: Must be <0>.
> >>>> +- flash device tree subnode, there must be a node with the following fields:
> >>>
> >>> These subnodes definitely require a 'compatible' string. Perhaps they
> >>> should be used to differentiate EPCS vs. EPCQ. Does "is-epcs" really
> >>> need to be in the top-level controller node?
> >>>
> >>>> +     - reg: Should contain the flash id
> >>>
> >>> Should, or must? (This question is relevant, because you seem to make it
> >>> optional in your code.) And what does the "flash ID" mean? It seems like
> >>> you're using as a chip-select or bank index.
> >>>
> Yes, this is used for chip select. It is required, not optional. This
> to ID for each flash in the device

OK, so correct the language here and enforce this in your driver. Right
now, you don't fail gracefully if this is missing.

> >>>> +             if (sector_start < num_sectors-(num_sectors / 4))
> >>>> +                     sr_bp = __ilog2_u32(num_sectors);
> >>>> +             else if (sector_start < num_sectors-(num_sectors / 8))
> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 1;
> >>>> +             else if (sector_start < num_sectors-(num_sectors / 16))
> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 2;
> >>>> +             else if (sector_start < num_sectors-(num_sectors / 32))
> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 3;
> >>>> +             else if (sector_start < num_sectors-(num_sectors / 64))
> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 4;
> >>>> +             else if (sector_start < num_sectors-(num_sectors / 128))
> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 5;
> >>>> +             else if (sector_start < num_sectors-(num_sectors / 256))
> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 6;
> >>>> +             else if (sector_start < num_sectors-(num_sectors / 512))
> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 7;
> >>>> +             else if (sector_start < num_sectors-(num_sectors / 1024))
> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 8;
> >>>> +             else
> >>>> +                     sr_bp = 0;  /* non area protected */
> >>>
> >>> Yikes, that's ugly! And I'm not sure it matches the EPCQ doc I found.
> >>> I'm pretty sure you can rewrite this if/else-if/else block in about 1
> >>> line though.
> >>>
> Yes, i understand that it looks ugly. But it is the best i can do
> since this function has to satisfy for all the supported devices
> (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf, start
> from page 19)

Did you even try? It was possible to simplify the other case, and I'm
pretty sure this case can be simplified too. How about this? I hacked
this together and it seems to match:

        if (sector_start <= num_sectors / 2)
		sr_bp = __ilog2_u32(num_sectors);
	else
		sr_bp = fls(num_sectors - 1 - sector_start) + 1;

> >>>> +
> >>>> +             if (sr_bp < 0) {
> >>>
> >>> sr_bp is unsigned, so this is never true.
> >>>
> Ok.  I will change to int type.

Are there ever negative values?

> >>>> +static int altera_epcq_probe_config_dt(struct platform_device *pdev,
> >>>> +                                    struct device_node *np,
> >>>> +                                    struct altera_epcq_plat_data *pdata)
> >>>> +{
> >>>> +     struct device_node *pp = NULL;
> >>>> +     struct resource *epcq_res;
> >>>> +     int i = 0;
> >>>> +     u32 id;
...
> >>>> +             pdata->np[i] = pp;
> >>>> +
> >>>> +             /* Read bank id from DT */
> >>>> +             if (of_get_property(pp, "reg", &id))

I just realized; you're not using this correctly. of_get_property()
returns the *length* in the third parameter, so you're not actually
saving the bank ID here. You probably want of_property_read_u32()
instead.

> >>>
> >>> Is this property optional? Your DT binding doc doesn't make it clear,
> >>> but it seems like a property which would be wise to require (i.e., not
> >>> optional).

^^^ so there should be a failure case, where you return failure if the
property is missing.

> >>>> +                     pdata->board_flash_info[i].bank = id;
> >>>> +             i++;
> >>>> +     }
> >>>> +     pdata->num_flashes = i;
> >>>> +     return 0;
> >>>> +}

Brian
--
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	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-02-04 20:37       ` Brian Norris
  0 siblings, 0 replies; 39+ messages in thread
From: Brian Norris @ 2015-02-04 20:37 UTC (permalink / raw)
  To: Viet Nga Dao
  Cc: linux-mtd, nga_chi86, David Woodhouse, linux-kernel, devicetree

On Tue, Jan 27, 2015 at 02:53:59PM +0800, Viet Nga Dao wrote:
> On Tue, Jan 20, 2015 at 10:05 AM, Viet Nga Dao <vndao@altera.com> wrote:
> > On Thu, Jan 15, 2015 at 5:27 PM, Viet Nga Dao <vndao@altera.com> wrote:
> >> On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
> >> <computersforpeace@gmail.com> wrote:
> >>> On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao@altera.com wrote:
> >>>> From: Viet Nga Dao <vndao@altera.com>

> >> That is why if rewrite the drivers to follow spi-nor structure, it
> >> will require extra decoding works for the only few used opcodes.
> >>
> >I think you'd only have some very trivial work here.
> >
> >There would be some small work to reintroduce a properly-replaceable ID
> >table, and callbacks like ->lock() and ->unlock(), but those should be
> >implemented in spi-nor.c sooner or later anyway.
> >
> 
> I am trying to modify the code to follow your recommendation. However,
> for lock and unlock functions, i have to implement it in spi_nor.c ,
> am i right? If yes, currently we already have existing spi_nor_lock
> and spi_nor_unlock functions but they could not apply for my driver.
> Should i create a new functions named altera_epcq_lock and unlock and
> then map it to callback functions or i should the put  those code
> sunder existing spi_nor_lock/unlock?

We probably want a replaceable spi_nor callback that does the
flash-specific unlock. spi_nor_lock/unlock would then call the
nor->unlock() / nor->lock() functions for you, when present.
Some of the existing code should move into their own spi_nor_st_lock() /
spi_nor_st_unlock() functions.

> >>>> diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
> >>>> new file mode 100644
> >>>> index 0000000..d14f50e
> >>>> --- /dev/null
> >>>> +++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
> >>>> @@ -0,0 +1,45 @@
> >>>> +* MTD Altera EPCQ driver
> >>>> +
> >>>> +Required properties:
> >>>> +- compatible: Should be "altr,epcq-1.0"
> >>>> +- reg: Address and length of the register set  for the device. It contains
> >>>> +  the information of registers in the same order as described by reg-names
> >>>> +- reg-names: Should contain the reg names
> >>>> +  "csr_base": Should contain the register configuration base address
> >>>> +  "data_base": Should contain the data base address
> >>>> +- is-epcs: boolean type.
> >>>> +             If present, the device contains EPCS flashes.
> >>>> +             Otherwise, it contains EPCQ flashes.
> >>>> +- #address-cells: Must be <1>.
> >>>> +- #size-cells: Must be <0>.
> >>>> +- flash device tree subnode, there must be a node with the following fields:
> >>>
> >>> These subnodes definitely require a 'compatible' string. Perhaps they
> >>> should be used to differentiate EPCS vs. EPCQ. Does "is-epcs" really
> >>> need to be in the top-level controller node?
> >>>
> >>>> +     - reg: Should contain the flash id
> >>>
> >>> Should, or must? (This question is relevant, because you seem to make it
> >>> optional in your code.) And what does the "flash ID" mean? It seems like
> >>> you're using as a chip-select or bank index.
> >>>
> Yes, this is used for chip select. It is required, not optional. This
> to ID for each flash in the device

OK, so correct the language here and enforce this in your driver. Right
now, you don't fail gracefully if this is missing.

> >>>> +             if (sector_start < num_sectors-(num_sectors / 4))
> >>>> +                     sr_bp = __ilog2_u32(num_sectors);
> >>>> +             else if (sector_start < num_sectors-(num_sectors / 8))
> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 1;
> >>>> +             else if (sector_start < num_sectors-(num_sectors / 16))
> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 2;
> >>>> +             else if (sector_start < num_sectors-(num_sectors / 32))
> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 3;
> >>>> +             else if (sector_start < num_sectors-(num_sectors / 64))
> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 4;
> >>>> +             else if (sector_start < num_sectors-(num_sectors / 128))
> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 5;
> >>>> +             else if (sector_start < num_sectors-(num_sectors / 256))
> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 6;
> >>>> +             else if (sector_start < num_sectors-(num_sectors / 512))
> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 7;
> >>>> +             else if (sector_start < num_sectors-(num_sectors / 1024))
> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 8;
> >>>> +             else
> >>>> +                     sr_bp = 0;  /* non area protected */
> >>>
> >>> Yikes, that's ugly! And I'm not sure it matches the EPCQ doc I found.
> >>> I'm pretty sure you can rewrite this if/else-if/else block in about 1
> >>> line though.
> >>>
> Yes, i understand that it looks ugly. But it is the best i can do
> since this function has to satisfy for all the supported devices
> (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf, start
> from page 19)

Did you even try? It was possible to simplify the other case, and I'm
pretty sure this case can be simplified too. How about this? I hacked
this together and it seems to match:

        if (sector_start <= num_sectors / 2)
		sr_bp = __ilog2_u32(num_sectors);
	else
		sr_bp = fls(num_sectors - 1 - sector_start) + 1;

> >>>> +
> >>>> +             if (sr_bp < 0) {
> >>>
> >>> sr_bp is unsigned, so this is never true.
> >>>
> Ok.  I will change to int type.

Are there ever negative values?

> >>>> +static int altera_epcq_probe_config_dt(struct platform_device *pdev,
> >>>> +                                    struct device_node *np,
> >>>> +                                    struct altera_epcq_plat_data *pdata)
> >>>> +{
> >>>> +     struct device_node *pp = NULL;
> >>>> +     struct resource *epcq_res;
> >>>> +     int i = 0;
> >>>> +     u32 id;
...
> >>>> +             pdata->np[i] = pp;
> >>>> +
> >>>> +             /* Read bank id from DT */
> >>>> +             if (of_get_property(pp, "reg", &id))

I just realized; you're not using this correctly. of_get_property()
returns the *length* in the third parameter, so you're not actually
saving the bank ID here. You probably want of_property_read_u32()
instead.

> >>>
> >>> Is this property optional? Your DT binding doc doesn't make it clear,
> >>> but it seems like a property which would be wise to require (i.e., not
> >>> optional).

^^^ so there should be a failure case, where you return failure if the
property is missing.

> >>>> +                     pdata->board_flash_info[i].bank = id;
> >>>> +             i++;
> >>>> +     }
> >>>> +     pdata->num_flashes = i;
> >>>> +     return 0;
> >>>> +}

Brian

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
  2015-02-04 20:37       ` Brian Norris
  (?)
@ 2015-02-05  4:13         ` Nga Chi
  -1 siblings, 0 replies; 39+ messages in thread
From: Nga Chi @ 2015-02-05  4:13 UTC (permalink / raw)
  To: Brian Norris
  Cc: Viet Nga Dao, David Woodhouse, linux-mtd, linux-kernel, devicetree

On Thu, Feb 5, 2015 at 4:37 AM, Brian Norris
<computersforpeace@gmail.com> wrote:
> On Tue, Jan 27, 2015 at 02:53:59PM +0800, Viet Nga Dao wrote:
>> On Tue, Jan 20, 2015 at 10:05 AM, Viet Nga Dao <vndao@altera.com> wrote:
>> > On Thu, Jan 15, 2015 at 5:27 PM, Viet Nga Dao <vndao@altera.com> wrote:
>> >> On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
>> >> <computersforpeace@gmail.com> wrote:
>> >>> On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao@altera.com wrote:
>> >>>> From: Viet Nga Dao <vndao@altera.com>
>
>> >> That is why if rewrite the drivers to follow spi-nor structure, it
>> >> will require extra decoding works for the only few used opcodes.
>> >>
>> >I think you'd only have some very trivial work here.
>> >
>> >There would be some small work to reintroduce a properly-replaceable ID
>> >table, and callbacks like ->lock() and ->unlock(), but those should be
>> >implemented in spi-nor.c sooner or later anyway.
>> >
>>
>> I am trying to modify the code to follow your recommendation. However,
>> for lock and unlock functions, i have to implement it in spi_nor.c ,
>> am i right? If yes, currently we already have existing spi_nor_lock
>> and spi_nor_unlock functions but they could not apply for my driver.
>> Should i create a new functions named altera_epcq_lock and unlock and
>> then map it to callback functions or i should the put  those code
>> sunder existing spi_nor_lock/unlock?
>
> We probably want a replaceable spi_nor callback that does the
> flash-specific unlock. spi_nor_lock/unlock would then call the
> nor->unlock() / nor->lock() functions for you, when present.
> Some of the existing code should move into their own spi_nor_st_lock() /
> spi_nor_st_unlock() functions.
>
When this will be implemented? For this time being, what i should do then?

>> >>>> diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>> >>>> new file mode 100644
>> >>>> index 0000000..d14f50e
>> >>>> --- /dev/null
>> >>>> +++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>> >>>> @@ -0,0 +1,45 @@
>> >>>> +* MTD Altera EPCQ driver
>> >>>> +
>> >>>> +Required properties:
>> >>>> +- compatible: Should be "altr,epcq-1.0"
>> >>>> +- reg: Address and length of the register set  for the device. It contains
>> >>>> +  the information of registers in the same order as described by reg-names
>> >>>> +- reg-names: Should contain the reg names
>> >>>> +  "csr_base": Should contain the register configuration base address
>> >>>> +  "data_base": Should contain the data base address
>> >>>> +- is-epcs: boolean type.
>> >>>> +             If present, the device contains EPCS flashes.
>> >>>> +             Otherwise, it contains EPCQ flashes.
>> >>>> +- #address-cells: Must be <1>.
>> >>>> +- #size-cells: Must be <0>.
>> >>>> +- flash device tree subnode, there must be a node with the following fields:
>> >>>
>> >>> These subnodes definitely require a 'compatible' string. Perhaps they
>> >>> should be used to differentiate EPCS vs. EPCQ. Does "is-epcs" really
>> >>> need to be in the top-level controller node?
>> >>>
>> >>>> +     - reg: Should contain the flash id
>> >>>
>> >>> Should, or must? (This question is relevant, because you seem to make it
>> >>> optional in your code.) And what does the "flash ID" mean? It seems like
>> >>> you're using as a chip-select or bank index.
>> >>>
>> Yes, this is used for chip select. It is required, not optional. This
>> to ID for each flash in the device
>
> OK, so correct the language here and enforce this in your driver. Right
> now, you don't fail gracefully if this is missing.
>
>> >>>> +             if (sector_start < num_sectors-(num_sectors / 4))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors);
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 8))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 1;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 16))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 2;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 32))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 3;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 64))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 4;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 128))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 5;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 256))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 6;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 512))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 7;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 1024))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 8;
>> >>>> +             else
>> >>>> +                     sr_bp = 0;  /* non area protected */
>> >>>
>> >>> Yikes, that's ugly! And I'm not sure it matches the EPCQ doc I found.
>> >>> I'm pretty sure you can rewrite this if/else-if/else block in about 1
>> >>> line though.
>> >>>
>> Yes, i understand that it looks ugly. But it is the best i can do
>> since this function has to satisfy for all the supported devices
>> (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf, start
>> from page 19)
>
> Did you even try? It was possible to simplify the other case, and I'm
> pretty sure this case can be simplified too. How about this? I hacked
> this together and it seems to match:
>
>         if (sector_start <= num_sectors / 2)
>                 sr_bp = __ilog2_u32(num_sectors);
>         else
>                 sr_bp = fls(num_sectors - 1 - sector_start) + 1;
>
>> >>>> +
>> >>>> +             if (sr_bp < 0) {
>> >>>
>> >>> sr_bp is unsigned, so this is never true.
>> >>>
>> Ok.  I will change to int type.
>
> Are there ever negative values?
>
>> >>>> +static int altera_epcq_probe_config_dt(struct platform_device *pdev,
>> >>>> +                                    struct device_node *np,
>> >>>> +                                    struct altera_epcq_plat_data *pdata)
>> >>>> +{
>> >>>> +     struct device_node *pp = NULL;
>> >>>> +     struct resource *epcq_res;
>> >>>> +     int i = 0;
>> >>>> +     u32 id;
> ...
>> >>>> +             pdata->np[i] = pp;
>> >>>> +
>> >>>> +             /* Read bank id from DT */
>> >>>> +             if (of_get_property(pp, "reg", &id))
>
> I just realized; you're not using this correctly. of_get_property()
> returns the *length* in the third parameter, so you're not actually
> saving the bank ID here. You probably want of_property_read_u32()
> instead.
>
>> >>>
>> >>> Is this property optional? Your DT binding doc doesn't make it clear,
>> >>> but it seems like a property which would be wise to require (i.e., not
>> >>> optional).
>
> ^^^ so there should be a failure case, where you return failure if the
> property is missing.
>
>> >>>> +                     pdata->board_flash_info[i].bank = id;
>> >>>> +             i++;
>> >>>> +     }
>> >>>> +     pdata->num_flashes = i;
>> >>>> +     return 0;
>> >>>> +}
>
> Brian



-- 
Nga

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-02-05  4:13         ` Nga Chi
  0 siblings, 0 replies; 39+ messages in thread
From: Nga Chi @ 2015-02-05  4:13 UTC (permalink / raw)
  To: Brian Norris
  Cc: Viet Nga Dao, David Woodhouse,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On Thu, Feb 5, 2015 at 4:37 AM, Brian Norris
<computersforpeace-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> On Tue, Jan 27, 2015 at 02:53:59PM +0800, Viet Nga Dao wrote:
>> On Tue, Jan 20, 2015 at 10:05 AM, Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org> wrote:
>> > On Thu, Jan 15, 2015 at 5:27 PM, Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org> wrote:
>> >> On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
>> >> <computersforpeace-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>> >>> On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org wrote:
>> >>>> From: Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org>
>
>> >> That is why if rewrite the drivers to follow spi-nor structure, it
>> >> will require extra decoding works for the only few used opcodes.
>> >>
>> >I think you'd only have some very trivial work here.
>> >
>> >There would be some small work to reintroduce a properly-replaceable ID
>> >table, and callbacks like ->lock() and ->unlock(), but those should be
>> >implemented in spi-nor.c sooner or later anyway.
>> >
>>
>> I am trying to modify the code to follow your recommendation. However,
>> for lock and unlock functions, i have to implement it in spi_nor.c ,
>> am i right? If yes, currently we already have existing spi_nor_lock
>> and spi_nor_unlock functions but they could not apply for my driver.
>> Should i create a new functions named altera_epcq_lock and unlock and
>> then map it to callback functions or i should the put  those code
>> sunder existing spi_nor_lock/unlock?
>
> We probably want a replaceable spi_nor callback that does the
> flash-specific unlock. spi_nor_lock/unlock would then call the
> nor->unlock() / nor->lock() functions for you, when present.
> Some of the existing code should move into their own spi_nor_st_lock() /
> spi_nor_st_unlock() functions.
>
When this will be implemented? For this time being, what i should do then?

>> >>>> diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>> >>>> new file mode 100644
>> >>>> index 0000000..d14f50e
>> >>>> --- /dev/null
>> >>>> +++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>> >>>> @@ -0,0 +1,45 @@
>> >>>> +* MTD Altera EPCQ driver
>> >>>> +
>> >>>> +Required properties:
>> >>>> +- compatible: Should be "altr,epcq-1.0"
>> >>>> +- reg: Address and length of the register set  for the device. It contains
>> >>>> +  the information of registers in the same order as described by reg-names
>> >>>> +- reg-names: Should contain the reg names
>> >>>> +  "csr_base": Should contain the register configuration base address
>> >>>> +  "data_base": Should contain the data base address
>> >>>> +- is-epcs: boolean type.
>> >>>> +             If present, the device contains EPCS flashes.
>> >>>> +             Otherwise, it contains EPCQ flashes.
>> >>>> +- #address-cells: Must be <1>.
>> >>>> +- #size-cells: Must be <0>.
>> >>>> +- flash device tree subnode, there must be a node with the following fields:
>> >>>
>> >>> These subnodes definitely require a 'compatible' string. Perhaps they
>> >>> should be used to differentiate EPCS vs. EPCQ. Does "is-epcs" really
>> >>> need to be in the top-level controller node?
>> >>>
>> >>>> +     - reg: Should contain the flash id
>> >>>
>> >>> Should, or must? (This question is relevant, because you seem to make it
>> >>> optional in your code.) And what does the "flash ID" mean? It seems like
>> >>> you're using as a chip-select or bank index.
>> >>>
>> Yes, this is used for chip select. It is required, not optional. This
>> to ID for each flash in the device
>
> OK, so correct the language here and enforce this in your driver. Right
> now, you don't fail gracefully if this is missing.
>
>> >>>> +             if (sector_start < num_sectors-(num_sectors / 4))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors);
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 8))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 1;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 16))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 2;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 32))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 3;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 64))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 4;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 128))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 5;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 256))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 6;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 512))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 7;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 1024))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 8;
>> >>>> +             else
>> >>>> +                     sr_bp = 0;  /* non area protected */
>> >>>
>> >>> Yikes, that's ugly! And I'm not sure it matches the EPCQ doc I found.
>> >>> I'm pretty sure you can rewrite this if/else-if/else block in about 1
>> >>> line though.
>> >>>
>> Yes, i understand that it looks ugly. But it is the best i can do
>> since this function has to satisfy for all the supported devices
>> (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf, start
>> from page 19)
>
> Did you even try? It was possible to simplify the other case, and I'm
> pretty sure this case can be simplified too. How about this? I hacked
> this together and it seems to match:
>
>         if (sector_start <= num_sectors / 2)
>                 sr_bp = __ilog2_u32(num_sectors);
>         else
>                 sr_bp = fls(num_sectors - 1 - sector_start) + 1;
>
>> >>>> +
>> >>>> +             if (sr_bp < 0) {
>> >>>
>> >>> sr_bp is unsigned, so this is never true.
>> >>>
>> Ok.  I will change to int type.
>
> Are there ever negative values?
>
>> >>>> +static int altera_epcq_probe_config_dt(struct platform_device *pdev,
>> >>>> +                                    struct device_node *np,
>> >>>> +                                    struct altera_epcq_plat_data *pdata)
>> >>>> +{
>> >>>> +     struct device_node *pp = NULL;
>> >>>> +     struct resource *epcq_res;
>> >>>> +     int i = 0;
>> >>>> +     u32 id;
> ...
>> >>>> +             pdata->np[i] = pp;
>> >>>> +
>> >>>> +             /* Read bank id from DT */
>> >>>> +             if (of_get_property(pp, "reg", &id))
>
> I just realized; you're not using this correctly. of_get_property()
> returns the *length* in the third parameter, so you're not actually
> saving the bank ID here. You probably want of_property_read_u32()
> instead.
>
>> >>>
>> >>> Is this property optional? Your DT binding doc doesn't make it clear,
>> >>> but it seems like a property which would be wise to require (i.e., not
>> >>> optional).
>
> ^^^ so there should be a failure case, where you return failure if the
> property is missing.
>
>> >>>> +                     pdata->board_flash_info[i].bank = id;
>> >>>> +             i++;
>> >>>> +     }
>> >>>> +     pdata->num_flashes = i;
>> >>>> +     return 0;
>> >>>> +}
>
> Brian



-- 
Nga
--
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	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-02-05  4:13         ` Nga Chi
  0 siblings, 0 replies; 39+ messages in thread
From: Nga Chi @ 2015-02-05  4:13 UTC (permalink / raw)
  To: Brian Norris
  Cc: Viet Nga Dao, linux-mtd, David Woodhouse, linux-kernel, devicetree

On Thu, Feb 5, 2015 at 4:37 AM, Brian Norris
<computersforpeace@gmail.com> wrote:
> On Tue, Jan 27, 2015 at 02:53:59PM +0800, Viet Nga Dao wrote:
>> On Tue, Jan 20, 2015 at 10:05 AM, Viet Nga Dao <vndao@altera.com> wrote:
>> > On Thu, Jan 15, 2015 at 5:27 PM, Viet Nga Dao <vndao@altera.com> wrote:
>> >> On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
>> >> <computersforpeace@gmail.com> wrote:
>> >>> On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao@altera.com wrote:
>> >>>> From: Viet Nga Dao <vndao@altera.com>
>
>> >> That is why if rewrite the drivers to follow spi-nor structure, it
>> >> will require extra decoding works for the only few used opcodes.
>> >>
>> >I think you'd only have some very trivial work here.
>> >
>> >There would be some small work to reintroduce a properly-replaceable ID
>> >table, and callbacks like ->lock() and ->unlock(), but those should be
>> >implemented in spi-nor.c sooner or later anyway.
>> >
>>
>> I am trying to modify the code to follow your recommendation. However,
>> for lock and unlock functions, i have to implement it in spi_nor.c ,
>> am i right? If yes, currently we already have existing spi_nor_lock
>> and spi_nor_unlock functions but they could not apply for my driver.
>> Should i create a new functions named altera_epcq_lock and unlock and
>> then map it to callback functions or i should the put  those code
>> sunder existing spi_nor_lock/unlock?
>
> We probably want a replaceable spi_nor callback that does the
> flash-specific unlock. spi_nor_lock/unlock would then call the
> nor->unlock() / nor->lock() functions for you, when present.
> Some of the existing code should move into their own spi_nor_st_lock() /
> spi_nor_st_unlock() functions.
>
When this will be implemented? For this time being, what i should do then?

>> >>>> diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>> >>>> new file mode 100644
>> >>>> index 0000000..d14f50e
>> >>>> --- /dev/null
>> >>>> +++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>> >>>> @@ -0,0 +1,45 @@
>> >>>> +* MTD Altera EPCQ driver
>> >>>> +
>> >>>> +Required properties:
>> >>>> +- compatible: Should be "altr,epcq-1.0"
>> >>>> +- reg: Address and length of the register set  for the device. It contains
>> >>>> +  the information of registers in the same order as described by reg-names
>> >>>> +- reg-names: Should contain the reg names
>> >>>> +  "csr_base": Should contain the register configuration base address
>> >>>> +  "data_base": Should contain the data base address
>> >>>> +- is-epcs: boolean type.
>> >>>> +             If present, the device contains EPCS flashes.
>> >>>> +             Otherwise, it contains EPCQ flashes.
>> >>>> +- #address-cells: Must be <1>.
>> >>>> +- #size-cells: Must be <0>.
>> >>>> +- flash device tree subnode, there must be a node with the following fields:
>> >>>
>> >>> These subnodes definitely require a 'compatible' string. Perhaps they
>> >>> should be used to differentiate EPCS vs. EPCQ. Does "is-epcs" really
>> >>> need to be in the top-level controller node?
>> >>>
>> >>>> +     - reg: Should contain the flash id
>> >>>
>> >>> Should, or must? (This question is relevant, because you seem to make it
>> >>> optional in your code.) And what does the "flash ID" mean? It seems like
>> >>> you're using as a chip-select or bank index.
>> >>>
>> Yes, this is used for chip select. It is required, not optional. This
>> to ID for each flash in the device
>
> OK, so correct the language here and enforce this in your driver. Right
> now, you don't fail gracefully if this is missing.
>
>> >>>> +             if (sector_start < num_sectors-(num_sectors / 4))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors);
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 8))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 1;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 16))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 2;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 32))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 3;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 64))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 4;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 128))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 5;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 256))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 6;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 512))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 7;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 1024))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 8;
>> >>>> +             else
>> >>>> +                     sr_bp = 0;  /* non area protected */
>> >>>
>> >>> Yikes, that's ugly! And I'm not sure it matches the EPCQ doc I found.
>> >>> I'm pretty sure you can rewrite this if/else-if/else block in about 1
>> >>> line though.
>> >>>
>> Yes, i understand that it looks ugly. But it is the best i can do
>> since this function has to satisfy for all the supported devices
>> (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf, start
>> from page 19)
>
> Did you even try? It was possible to simplify the other case, and I'm
> pretty sure this case can be simplified too. How about this? I hacked
> this together and it seems to match:
>
>         if (sector_start <= num_sectors / 2)
>                 sr_bp = __ilog2_u32(num_sectors);
>         else
>                 sr_bp = fls(num_sectors - 1 - sector_start) + 1;
>
>> >>>> +
>> >>>> +             if (sr_bp < 0) {
>> >>>
>> >>> sr_bp is unsigned, so this is never true.
>> >>>
>> Ok.  I will change to int type.
>
> Are there ever negative values?
>
>> >>>> +static int altera_epcq_probe_config_dt(struct platform_device *pdev,
>> >>>> +                                    struct device_node *np,
>> >>>> +                                    struct altera_epcq_plat_data *pdata)
>> >>>> +{
>> >>>> +     struct device_node *pp = NULL;
>> >>>> +     struct resource *epcq_res;
>> >>>> +     int i = 0;
>> >>>> +     u32 id;
> ...
>> >>>> +             pdata->np[i] = pp;
>> >>>> +
>> >>>> +             /* Read bank id from DT */
>> >>>> +             if (of_get_property(pp, "reg", &id))
>
> I just realized; you're not using this correctly. of_get_property()
> returns the *length* in the third parameter, so you're not actually
> saving the bank ID here. You probably want of_property_read_u32()
> instead.
>
>> >>>
>> >>> Is this property optional? Your DT binding doc doesn't make it clear,
>> >>> but it seems like a property which would be wise to require (i.e., not
>> >>> optional).
>
> ^^^ so there should be a failure case, where you return failure if the
> property is missing.
>
>> >>>> +                     pdata->board_flash_info[i].bank = id;
>> >>>> +             i++;
>> >>>> +     }
>> >>>> +     pdata->num_flashes = i;
>> >>>> +     return 0;
>> >>>> +}
>
> Brian



-- 
Nga

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
  2015-02-04 20:37       ` Brian Norris
@ 2015-02-05  4:15         ` Viet Nga Dao
  -1 siblings, 0 replies; 39+ messages in thread
From: Viet Nga Dao @ 2015-02-05  4:15 UTC (permalink / raw)
  To: Brian Norris
  Cc: Viet Nga Dao, David Woodhouse, linux-mtd, linux-kernel, devicetree

On Thu, Feb 5, 2015 at 4:37 AM, Brian Norris
<computersforpeace@gmail.com> wrote:
> On Tue, Jan 27, 2015 at 02:53:59PM +0800, Viet Nga Dao wrote:
>> On Tue, Jan 20, 2015 at 10:05 AM, Viet Nga Dao <vndao@altera.com> wrote:
>> > On Thu, Jan 15, 2015 at 5:27 PM, Viet Nga Dao <vndao@altera.com> wrote:
>> >> On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
>> >> <computersforpeace@gmail.com> wrote:
>> >>> On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao@altera.com wrote:
>> >>>> From: Viet Nga Dao <vndao@altera.com>
>
>> >> That is why if rewrite the drivers to follow spi-nor structure, it
>> >> will require extra decoding works for the only few used opcodes.
>> >>
>> >I think you'd only have some very trivial work here.
>> >
>> >There would be some small work to reintroduce a properly-replaceable ID
>> >table, and callbacks like ->lock() and ->unlock(), but those should be
>> >implemented in spi-nor.c sooner or later anyway.
>> >
>>
>> I am trying to modify the code to follow your recommendation. However,
>> for lock and unlock functions, i have to implement it in spi_nor.c ,
>> am i right? If yes, currently we already have existing spi_nor_lock
>> and spi_nor_unlock functions but they could not apply for my driver.
>> Should i create a new functions named altera_epcq_lock and unlock and
>> then map it to callback functions or i should the put  those code
>> sunder existing spi_nor_lock/unlock?
>
> We probably want a replaceable spi_nor callback that does the
> flash-specific unlock. spi_nor_lock/unlock would then call the
> nor->unlock() / nor->lock() functions for you, when present.
> Some of the existing code should move into their own spi_nor_st_lock() /
> spi_nor_st_unlock() functions.
>

When this will be implemented? As for time being, what should i do for
these 2 functions?

>> >>>> diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>> >>>> new file mode 100644
>> >>>> index 0000000..d14f50e
>> >>>> --- /dev/null
>> >>>> +++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>> >>>> @@ -0,0 +1,45 @@
>> >>>> +* MTD Altera EPCQ driver
>> >>>> +
>> >>>> +Required properties:
>> >>>> +- compatible: Should be "altr,epcq-1.0"
>> >>>> +- reg: Address and length of the register set  for the device. It contains
>> >>>> +  the information of registers in the same order as described by reg-names
>> >>>> +- reg-names: Should contain the reg names
>> >>>> +  "csr_base": Should contain the register configuration base address
>> >>>> +  "data_base": Should contain the data base address
>> >>>> +- is-epcs: boolean type.
>> >>>> +             If present, the device contains EPCS flashes.
>> >>>> +             Otherwise, it contains EPCQ flashes.
>> >>>> +- #address-cells: Must be <1>.
>> >>>> +- #size-cells: Must be <0>.
>> >>>> +- flash device tree subnode, there must be a node with the following fields:
>> >>>
>> >>> These subnodes definitely require a 'compatible' string. Perhaps they
>> >>> should be used to differentiate EPCS vs. EPCQ. Does "is-epcs" really
>> >>> need to be in the top-level controller node?
>> >>>
>> >>>> +     - reg: Should contain the flash id
>> >>>
>> >>> Should, or must? (This question is relevant, because you seem to make it
>> >>> optional in your code.) And what does the "flash ID" mean? It seems like
>> >>> you're using as a chip-select or bank index.
>> >>>
>> Yes, this is used for chip select. It is required, not optional. This
>> to ID for each flash in the device
>
> OK, so correct the language here and enforce this in your driver. Right
> now, you don't fail gracefully if this is missing.
>
>> >>>> +             if (sector_start < num_sectors-(num_sectors / 4))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors);
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 8))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 1;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 16))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 2;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 32))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 3;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 64))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 4;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 128))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 5;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 256))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 6;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 512))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 7;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 1024))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 8;
>> >>>> +             else
>> >>>> +                     sr_bp = 0;  /* non area protected */
>> >>>
>> >>> Yikes, that's ugly! And I'm not sure it matches the EPCQ doc I found.
>> >>> I'm pretty sure you can rewrite this if/else-if/else block in about 1
>> >>> line though.
>> >>>
>> Yes, i understand that it looks ugly. But it is the best i can do
>> since this function has to satisfy for all the supported devices
>> (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf, start
>> from page 19)
>
> Did you even try? It was possible to simplify the other case, and I'm
> pretty sure this case can be simplified too. How about this? I hacked
> this together and it seems to match:
>
>         if (sector_start <= num_sectors / 2)
>                 sr_bp = __ilog2_u32(num_sectors);
>         else
>                 sr_bp = fls(num_sectors - 1 - sector_start) + 1;
>
>> >>>> +
>> >>>> +             if (sr_bp < 0) {
>> >>>
>> >>> sr_bp is unsigned, so this is never true.
>> >>>
>> Ok.  I will change to int type.
>
> Are there ever negative values?
>
>> >>>> +static int altera_epcq_probe_config_dt(struct platform_device *pdev,
>> >>>> +                                    struct device_node *np,
>> >>>> +                                    struct altera_epcq_plat_data *pdata)
>> >>>> +{
>> >>>> +     struct device_node *pp = NULL;
>> >>>> +     struct resource *epcq_res;
>> >>>> +     int i = 0;
>> >>>> +     u32 id;
> ...
>> >>>> +             pdata->np[i] = pp;
>> >>>> +
>> >>>> +             /* Read bank id from DT */
>> >>>> +             if (of_get_property(pp, "reg", &id))
>
> I just realized; you're not using this correctly. of_get_property()
> returns the *length* in the third parameter, so you're not actually
> saving the bank ID here. You probably want of_property_read_u32()
> instead.
>
>> >>>
>> >>> Is this property optional? Your DT binding doc doesn't make it clear,
>> >>> but it seems like a property which would be wise to require (i.e., not
>> >>> optional).
>
> ^^^ so there should be a failure case, where you return failure if the
> property is missing.
>
>> >>>> +                     pdata->board_flash_info[i].bank = id;
>> >>>> +             i++;
>> >>>> +     }
>> >>>> +     pdata->num_flashes = i;
>> >>>> +     return 0;
>> >>>> +}
>
> Brian

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-02-05  4:15         ` Viet Nga Dao
  0 siblings, 0 replies; 39+ messages in thread
From: Viet Nga Dao @ 2015-02-05  4:15 UTC (permalink / raw)
  To: Brian Norris
  Cc: Viet Nga Dao, linux-mtd, David Woodhouse, linux-kernel, devicetree

On Thu, Feb 5, 2015 at 4:37 AM, Brian Norris
<computersforpeace@gmail.com> wrote:
> On Tue, Jan 27, 2015 at 02:53:59PM +0800, Viet Nga Dao wrote:
>> On Tue, Jan 20, 2015 at 10:05 AM, Viet Nga Dao <vndao@altera.com> wrote:
>> > On Thu, Jan 15, 2015 at 5:27 PM, Viet Nga Dao <vndao@altera.com> wrote:
>> >> On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
>> >> <computersforpeace@gmail.com> wrote:
>> >>> On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao@altera.com wrote:
>> >>>> From: Viet Nga Dao <vndao@altera.com>
>
>> >> That is why if rewrite the drivers to follow spi-nor structure, it
>> >> will require extra decoding works for the only few used opcodes.
>> >>
>> >I think you'd only have some very trivial work here.
>> >
>> >There would be some small work to reintroduce a properly-replaceable ID
>> >table, and callbacks like ->lock() and ->unlock(), but those should be
>> >implemented in spi-nor.c sooner or later anyway.
>> >
>>
>> I am trying to modify the code to follow your recommendation. However,
>> for lock and unlock functions, i have to implement it in spi_nor.c ,
>> am i right? If yes, currently we already have existing spi_nor_lock
>> and spi_nor_unlock functions but they could not apply for my driver.
>> Should i create a new functions named altera_epcq_lock and unlock and
>> then map it to callback functions or i should the put  those code
>> sunder existing spi_nor_lock/unlock?
>
> We probably want a replaceable spi_nor callback that does the
> flash-specific unlock. spi_nor_lock/unlock would then call the
> nor->unlock() / nor->lock() functions for you, when present.
> Some of the existing code should move into their own spi_nor_st_lock() /
> spi_nor_st_unlock() functions.
>

When this will be implemented? As for time being, what should i do for
these 2 functions?

>> >>>> diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>> >>>> new file mode 100644
>> >>>> index 0000000..d14f50e
>> >>>> --- /dev/null
>> >>>> +++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>> >>>> @@ -0,0 +1,45 @@
>> >>>> +* MTD Altera EPCQ driver
>> >>>> +
>> >>>> +Required properties:
>> >>>> +- compatible: Should be "altr,epcq-1.0"
>> >>>> +- reg: Address and length of the register set  for the device. It contains
>> >>>> +  the information of registers in the same order as described by reg-names
>> >>>> +- reg-names: Should contain the reg names
>> >>>> +  "csr_base": Should contain the register configuration base address
>> >>>> +  "data_base": Should contain the data base address
>> >>>> +- is-epcs: boolean type.
>> >>>> +             If present, the device contains EPCS flashes.
>> >>>> +             Otherwise, it contains EPCQ flashes.
>> >>>> +- #address-cells: Must be <1>.
>> >>>> +- #size-cells: Must be <0>.
>> >>>> +- flash device tree subnode, there must be a node with the following fields:
>> >>>
>> >>> These subnodes definitely require a 'compatible' string. Perhaps they
>> >>> should be used to differentiate EPCS vs. EPCQ. Does "is-epcs" really
>> >>> need to be in the top-level controller node?
>> >>>
>> >>>> +     - reg: Should contain the flash id
>> >>>
>> >>> Should, or must? (This question is relevant, because you seem to make it
>> >>> optional in your code.) And what does the "flash ID" mean? It seems like
>> >>> you're using as a chip-select or bank index.
>> >>>
>> Yes, this is used for chip select. It is required, not optional. This
>> to ID for each flash in the device
>
> OK, so correct the language here and enforce this in your driver. Right
> now, you don't fail gracefully if this is missing.
>
>> >>>> +             if (sector_start < num_sectors-(num_sectors / 4))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors);
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 8))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 1;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 16))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 2;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 32))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 3;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 64))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 4;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 128))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 5;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 256))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 6;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 512))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 7;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 1024))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 8;
>> >>>> +             else
>> >>>> +                     sr_bp = 0;  /* non area protected */
>> >>>
>> >>> Yikes, that's ugly! And I'm not sure it matches the EPCQ doc I found.
>> >>> I'm pretty sure you can rewrite this if/else-if/else block in about 1
>> >>> line though.
>> >>>
>> Yes, i understand that it looks ugly. But it is the best i can do
>> since this function has to satisfy for all the supported devices
>> (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf, start
>> from page 19)
>
> Did you even try? It was possible to simplify the other case, and I'm
> pretty sure this case can be simplified too. How about this? I hacked
> this together and it seems to match:
>
>         if (sector_start <= num_sectors / 2)
>                 sr_bp = __ilog2_u32(num_sectors);
>         else
>                 sr_bp = fls(num_sectors - 1 - sector_start) + 1;
>
>> >>>> +
>> >>>> +             if (sr_bp < 0) {
>> >>>
>> >>> sr_bp is unsigned, so this is never true.
>> >>>
>> Ok.  I will change to int type.
>
> Are there ever negative values?
>
>> >>>> +static int altera_epcq_probe_config_dt(struct platform_device *pdev,
>> >>>> +                                    struct device_node *np,
>> >>>> +                                    struct altera_epcq_plat_data *pdata)
>> >>>> +{
>> >>>> +     struct device_node *pp = NULL;
>> >>>> +     struct resource *epcq_res;
>> >>>> +     int i = 0;
>> >>>> +     u32 id;
> ...
>> >>>> +             pdata->np[i] = pp;
>> >>>> +
>> >>>> +             /* Read bank id from DT */
>> >>>> +             if (of_get_property(pp, "reg", &id))
>
> I just realized; you're not using this correctly. of_get_property()
> returns the *length* in the third parameter, so you're not actually
> saving the bank ID here. You probably want of_property_read_u32()
> instead.
>
>> >>>
>> >>> Is this property optional? Your DT binding doc doesn't make it clear,
>> >>> but it seems like a property which would be wise to require (i.e., not
>> >>> optional).
>
> ^^^ so there should be a failure case, where you return failure if the
> property is missing.
>
>> >>>> +                     pdata->board_flash_info[i].bank = id;
>> >>>> +             i++;
>> >>>> +     }
>> >>>> +     pdata->num_flashes = i;
>> >>>> +     return 0;
>> >>>> +}
>
> Brian

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
  2015-02-04 20:37       ` Brian Norris
  (?)
@ 2015-02-09  6:42         ` Viet Nga Dao
  -1 siblings, 0 replies; 39+ messages in thread
From: Viet Nga Dao @ 2015-02-09  6:42 UTC (permalink / raw)
  To: Brian Norris
  Cc: Viet Nga Dao, David Woodhouse, linux-mtd, linux-kernel, devicetree

Please ignore previous 2 emails of mine ^^

On Thu, Feb 5, 2015 at 4:37 AM, Brian Norris
<computersforpeace@gmail.com> wrote:
> On Tue, Jan 27, 2015 at 02:53:59PM +0800, Viet Nga Dao wrote:
>> On Tue, Jan 20, 2015 at 10:05 AM, Viet Nga Dao <vndao@altera.com> wrote:
>> > On Thu, Jan 15, 2015 at 5:27 PM, Viet Nga Dao <vndao@altera.com> wrote:
>> >> On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
>> >> <computersforpeace@gmail.com> wrote:
>> >>> On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao@altera.com wrote:
>> >>>> From: Viet Nga Dao <vndao@altera.com>
>
>> >> That is why if rewrite the drivers to follow spi-nor structure, it
>> >> will require extra decoding works for the only few used opcodes.
>> >>
>> >I think you'd only have some very trivial work here.
>> >
>> >There would be some small work to reintroduce a properly-replaceable ID
>> >table, and callbacks like ->lock() and ->unlock(), but those should be
>> >implemented in spi-nor.c sooner or later anyway.
>> >
>>
>> I am trying to modify the code to follow your recommendation. However,
>> for lock and unlock functions, i have to implement it in spi_nor.c ,
>> am i right? If yes, currently we already have existing spi_nor_lock
>> and spi_nor_unlock functions but they could not apply for my driver.
>> Should i create a new functions named altera_epcq_lock and unlock and
>> then map it to callback functions or i should the put  those code
>> sunder existing spi_nor_lock/unlock?
>
> We probably want a replaceable spi_nor callback that does the
> flash-specific unlock. spi_nor_lock/unlock would then call the
> nor->unlock() / nor->lock() functions for you, when present.
> Some of the existing code should move into their own spi_nor_st_lock() /
> spi_nor_st_unlock() functions.
>

This changes will be made by me or maintainers? If current this
functions are not supported, i decide not to implement my lock, unlock
function as well.

>> >>>> diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>> >>>> new file mode 100644
>> >>>> index 0000000..d14f50e
>> >>>> --- /dev/null
>> >>>> +++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>> >>>> @@ -0,0 +1,45 @@
>> >>>> +* MTD Altera EPCQ driver
>> >>>> +
>> >>>> +Required properties:
>> >>>> +- compatible: Should be "altr,epcq-1.0"
>> >>>> +- reg: Address and length of the register set  for the device. It contains
>> >>>> +  the information of registers in the same order as described by reg-names
>> >>>> +- reg-names: Should contain the reg names
>> >>>> +  "csr_base": Should contain the register configuration base address
>> >>>> +  "data_base": Should contain the data base address
>> >>>> +- is-epcs: boolean type.
>> >>>> +             If present, the device contains EPCS flashes.
>> >>>> +             Otherwise, it contains EPCQ flashes.
>> >>>> +- #address-cells: Must be <1>.
>> >>>> +- #size-cells: Must be <0>.
>> >>>> +- flash device tree subnode, there must be a node with the following fields:
>> >>>
>> >>> These subnodes definitely require a 'compatible' string. Perhaps they
>> >>> should be used to differentiate EPCS vs. EPCQ. Does "is-epcs" really
>> >>> need to be in the top-level controller node?
>> >>>
>> >>>> +     - reg: Should contain the flash id
>> >>>
>> >>> Should, or must? (This question is relevant, because you seem to make it
>> >>> optional in your code.) And what does the "flash ID" mean? It seems like
>> >>> you're using as a chip-select or bank index.
>> >>>
>> Yes, this is used for chip select. It is required, not optional. This
>> to ID for each flash in the device
>
> OK, so correct the language here and enforce this in your driver. Right
> now, you don't fail gracefully if this is missing.
>

Sorry, you are right. This field is unnecessary for my driver.
Instead, compatible is replaced. I will update it with 2nd version.

>> >>>> +             if (sector_start < num_sectors-(num_sectors / 4))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors);
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 8))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 1;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 16))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 2;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 32))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 3;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 64))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 4;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 128))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 5;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 256))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 6;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 512))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 7;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 1024))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 8;
>> >>>> +             else
>> >>>> +                     sr_bp = 0;  /* non area protected */
>> >>>
>> >>> Yikes, that's ugly! And I'm not sure it matches the EPCQ doc I found.
>> >>> I'm pretty sure you can rewrite this if/else-if/else block in about 1
>> >>> line though.
>> >>>
>> Yes, i understand that it looks ugly. But it is the best i can do
>> since this function has to satisfy for all the supported devices
>> (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf, start
>> from page 19)
>
> Did you even try? It was possible to simplify the other case, and I'm
> pretty sure this case can be simplified too. How about this? I hacked
> this together and it seems to match:
>
>         if (sector_start <= num_sectors / 2)
>                 sr_bp = __ilog2_u32(num_sectors);
>         else
>                 sr_bp = fls(num_sectors - 1 - sector_start) + 1;
>

Umm. This does not seem right to me. Just look at sector_start <
number_sector / 2. From the altera EPCQ datasheet
(http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf), such as
EPCQ16 (page 19,20). Sector start <number_sector/2 is the case where
TB = 1 (table 17). In this table we have few options and sr_bp =
__ilog2_u32(num_sectors) is only cover for the sector from 0 to 15 to
be protected.


>> >>>> +
>> >>>> +             if (sr_bp < 0) {
>> >>>
>> >>> sr_bp is unsigned, so this is never true.
>> >>>
>> Ok.  I will change to int type.
>
> Are there ever negative values?
>
>> >>>> +static int altera_epcq_probe_config_dt(struct platform_device *pdev,
>> >>>> +                                    struct device_node *np,
>> >>>> +                                    struct altera_epcq_plat_data *pdata)
>> >>>> +{
>> >>>> +     struct device_node *pp = NULL;
>> >>>> +     struct resource *epcq_res;
>> >>>> +     int i = 0;
>> >>>> +     u32 id;
> ...
>> >>>> +             pdata->np[i] = pp;
>> >>>> +
>> >>>> +             /* Read bank id from DT */
>> >>>> +             if (of_get_property(pp, "reg", &id))
>
> I just realized; you're not using this correctly. of_get_property()
> returns the *length* in the third parameter, so you're not actually
> saving the bank ID here. You probably want of_property_read_u32()
> instead.
>

I will remove this since it is unnecessary.

>> >>>
>> >>> Is this property optional? Your DT binding doc doesn't make it clear,
>> >>> but it seems like a property which would be wise to require (i.e., not
>> >>> optional).
>
> ^^^ so there should be a failure case, where you return failure if the
> property is missing.
>
>> >>>> +                     pdata->board_flash_info[i].bank = id;
>> >>>> +             i++;
>> >>>> +     }
>> >>>> +     pdata->num_flashes = i;
>> >>>> +     return 0;
>> >>>> +}
>
> Brian

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-02-09  6:42         ` Viet Nga Dao
  0 siblings, 0 replies; 39+ messages in thread
From: Viet Nga Dao @ 2015-02-09  6:42 UTC (permalink / raw)
  To: Brian Norris
  Cc: Viet Nga Dao, David Woodhouse,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Please ignore previous 2 emails of mine ^^

On Thu, Feb 5, 2015 at 4:37 AM, Brian Norris
<computersforpeace-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> On Tue, Jan 27, 2015 at 02:53:59PM +0800, Viet Nga Dao wrote:
>> On Tue, Jan 20, 2015 at 10:05 AM, Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org> wrote:
>> > On Thu, Jan 15, 2015 at 5:27 PM, Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org> wrote:
>> >> On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
>> >> <computersforpeace-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>> >>> On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org wrote:
>> >>>> From: Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org>
>
>> >> That is why if rewrite the drivers to follow spi-nor structure, it
>> >> will require extra decoding works for the only few used opcodes.
>> >>
>> >I think you'd only have some very trivial work here.
>> >
>> >There would be some small work to reintroduce a properly-replaceable ID
>> >table, and callbacks like ->lock() and ->unlock(), but those should be
>> >implemented in spi-nor.c sooner or later anyway.
>> >
>>
>> I am trying to modify the code to follow your recommendation. However,
>> for lock and unlock functions, i have to implement it in spi_nor.c ,
>> am i right? If yes, currently we already have existing spi_nor_lock
>> and spi_nor_unlock functions but they could not apply for my driver.
>> Should i create a new functions named altera_epcq_lock and unlock and
>> then map it to callback functions or i should the put  those code
>> sunder existing spi_nor_lock/unlock?
>
> We probably want a replaceable spi_nor callback that does the
> flash-specific unlock. spi_nor_lock/unlock would then call the
> nor->unlock() / nor->lock() functions for you, when present.
> Some of the existing code should move into their own spi_nor_st_lock() /
> spi_nor_st_unlock() functions.
>

This changes will be made by me or maintainers? If current this
functions are not supported, i decide not to implement my lock, unlock
function as well.

>> >>>> diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>> >>>> new file mode 100644
>> >>>> index 0000000..d14f50e
>> >>>> --- /dev/null
>> >>>> +++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>> >>>> @@ -0,0 +1,45 @@
>> >>>> +* MTD Altera EPCQ driver
>> >>>> +
>> >>>> +Required properties:
>> >>>> +- compatible: Should be "altr,epcq-1.0"
>> >>>> +- reg: Address and length of the register set  for the device. It contains
>> >>>> +  the information of registers in the same order as described by reg-names
>> >>>> +- reg-names: Should contain the reg names
>> >>>> +  "csr_base": Should contain the register configuration base address
>> >>>> +  "data_base": Should contain the data base address
>> >>>> +- is-epcs: boolean type.
>> >>>> +             If present, the device contains EPCS flashes.
>> >>>> +             Otherwise, it contains EPCQ flashes.
>> >>>> +- #address-cells: Must be <1>.
>> >>>> +- #size-cells: Must be <0>.
>> >>>> +- flash device tree subnode, there must be a node with the following fields:
>> >>>
>> >>> These subnodes definitely require a 'compatible' string. Perhaps they
>> >>> should be used to differentiate EPCS vs. EPCQ. Does "is-epcs" really
>> >>> need to be in the top-level controller node?
>> >>>
>> >>>> +     - reg: Should contain the flash id
>> >>>
>> >>> Should, or must? (This question is relevant, because you seem to make it
>> >>> optional in your code.) And what does the "flash ID" mean? It seems like
>> >>> you're using as a chip-select or bank index.
>> >>>
>> Yes, this is used for chip select. It is required, not optional. This
>> to ID for each flash in the device
>
> OK, so correct the language here and enforce this in your driver. Right
> now, you don't fail gracefully if this is missing.
>

Sorry, you are right. This field is unnecessary for my driver.
Instead, compatible is replaced. I will update it with 2nd version.

>> >>>> +             if (sector_start < num_sectors-(num_sectors / 4))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors);
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 8))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 1;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 16))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 2;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 32))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 3;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 64))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 4;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 128))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 5;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 256))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 6;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 512))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 7;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 1024))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 8;
>> >>>> +             else
>> >>>> +                     sr_bp = 0;  /* non area protected */
>> >>>
>> >>> Yikes, that's ugly! And I'm not sure it matches the EPCQ doc I found.
>> >>> I'm pretty sure you can rewrite this if/else-if/else block in about 1
>> >>> line though.
>> >>>
>> Yes, i understand that it looks ugly. But it is the best i can do
>> since this function has to satisfy for all the supported devices
>> (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf, start
>> from page 19)
>
> Did you even try? It was possible to simplify the other case, and I'm
> pretty sure this case can be simplified too. How about this? I hacked
> this together and it seems to match:
>
>         if (sector_start <= num_sectors / 2)
>                 sr_bp = __ilog2_u32(num_sectors);
>         else
>                 sr_bp = fls(num_sectors - 1 - sector_start) + 1;
>

Umm. This does not seem right to me. Just look at sector_start <
number_sector / 2. From the altera EPCQ datasheet
(http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf), such as
EPCQ16 (page 19,20). Sector start <number_sector/2 is the case where
TB = 1 (table 17). In this table we have few options and sr_bp =
__ilog2_u32(num_sectors) is only cover for the sector from 0 to 15 to
be protected.


>> >>>> +
>> >>>> +             if (sr_bp < 0) {
>> >>>
>> >>> sr_bp is unsigned, so this is never true.
>> >>>
>> Ok.  I will change to int type.
>
> Are there ever negative values?
>
>> >>>> +static int altera_epcq_probe_config_dt(struct platform_device *pdev,
>> >>>> +                                    struct device_node *np,
>> >>>> +                                    struct altera_epcq_plat_data *pdata)
>> >>>> +{
>> >>>> +     struct device_node *pp = NULL;
>> >>>> +     struct resource *epcq_res;
>> >>>> +     int i = 0;
>> >>>> +     u32 id;
> ...
>> >>>> +             pdata->np[i] = pp;
>> >>>> +
>> >>>> +             /* Read bank id from DT */
>> >>>> +             if (of_get_property(pp, "reg", &id))
>
> I just realized; you're not using this correctly. of_get_property()
> returns the *length* in the third parameter, so you're not actually
> saving the bank ID here. You probably want of_property_read_u32()
> instead.
>

I will remove this since it is unnecessary.

>> >>>
>> >>> Is this property optional? Your DT binding doc doesn't make it clear,
>> >>> but it seems like a property which would be wise to require (i.e., not
>> >>> optional).
>
> ^^^ so there should be a failure case, where you return failure if the
> property is missing.
>
>> >>>> +                     pdata->board_flash_info[i].bank = id;
>> >>>> +             i++;
>> >>>> +     }
>> >>>> +     pdata->num_flashes = i;
>> >>>> +     return 0;
>> >>>> +}
>
> Brian
--
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	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-02-09  6:42         ` Viet Nga Dao
  0 siblings, 0 replies; 39+ messages in thread
From: Viet Nga Dao @ 2015-02-09  6:42 UTC (permalink / raw)
  To: Brian Norris
  Cc: Viet Nga Dao, linux-mtd, David Woodhouse, linux-kernel, devicetree

Please ignore previous 2 emails of mine ^^

On Thu, Feb 5, 2015 at 4:37 AM, Brian Norris
<computersforpeace@gmail.com> wrote:
> On Tue, Jan 27, 2015 at 02:53:59PM +0800, Viet Nga Dao wrote:
>> On Tue, Jan 20, 2015 at 10:05 AM, Viet Nga Dao <vndao@altera.com> wrote:
>> > On Thu, Jan 15, 2015 at 5:27 PM, Viet Nga Dao <vndao@altera.com> wrote:
>> >> On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
>> >> <computersforpeace@gmail.com> wrote:
>> >>> On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao@altera.com wrote:
>> >>>> From: Viet Nga Dao <vndao@altera.com>
>
>> >> That is why if rewrite the drivers to follow spi-nor structure, it
>> >> will require extra decoding works for the only few used opcodes.
>> >>
>> >I think you'd only have some very trivial work here.
>> >
>> >There would be some small work to reintroduce a properly-replaceable ID
>> >table, and callbacks like ->lock() and ->unlock(), but those should be
>> >implemented in spi-nor.c sooner or later anyway.
>> >
>>
>> I am trying to modify the code to follow your recommendation. However,
>> for lock and unlock functions, i have to implement it in spi_nor.c ,
>> am i right? If yes, currently we already have existing spi_nor_lock
>> and spi_nor_unlock functions but they could not apply for my driver.
>> Should i create a new functions named altera_epcq_lock and unlock and
>> then map it to callback functions or i should the put  those code
>> sunder existing spi_nor_lock/unlock?
>
> We probably want a replaceable spi_nor callback that does the
> flash-specific unlock. spi_nor_lock/unlock would then call the
> nor->unlock() / nor->lock() functions for you, when present.
> Some of the existing code should move into their own spi_nor_st_lock() /
> spi_nor_st_unlock() functions.
>

This changes will be made by me or maintainers? If current this
functions are not supported, i decide not to implement my lock, unlock
function as well.

>> >>>> diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>> >>>> new file mode 100644
>> >>>> index 0000000..d14f50e
>> >>>> --- /dev/null
>> >>>> +++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>> >>>> @@ -0,0 +1,45 @@
>> >>>> +* MTD Altera EPCQ driver
>> >>>> +
>> >>>> +Required properties:
>> >>>> +- compatible: Should be "altr,epcq-1.0"
>> >>>> +- reg: Address and length of the register set  for the device. It contains
>> >>>> +  the information of registers in the same order as described by reg-names
>> >>>> +- reg-names: Should contain the reg names
>> >>>> +  "csr_base": Should contain the register configuration base address
>> >>>> +  "data_base": Should contain the data base address
>> >>>> +- is-epcs: boolean type.
>> >>>> +             If present, the device contains EPCS flashes.
>> >>>> +             Otherwise, it contains EPCQ flashes.
>> >>>> +- #address-cells: Must be <1>.
>> >>>> +- #size-cells: Must be <0>.
>> >>>> +- flash device tree subnode, there must be a node with the following fields:
>> >>>
>> >>> These subnodes definitely require a 'compatible' string. Perhaps they
>> >>> should be used to differentiate EPCS vs. EPCQ. Does "is-epcs" really
>> >>> need to be in the top-level controller node?
>> >>>
>> >>>> +     - reg: Should contain the flash id
>> >>>
>> >>> Should, or must? (This question is relevant, because you seem to make it
>> >>> optional in your code.) And what does the "flash ID" mean? It seems like
>> >>> you're using as a chip-select or bank index.
>> >>>
>> Yes, this is used for chip select. It is required, not optional. This
>> to ID for each flash in the device
>
> OK, so correct the language here and enforce this in your driver. Right
> now, you don't fail gracefully if this is missing.
>

Sorry, you are right. This field is unnecessary for my driver.
Instead, compatible is replaced. I will update it with 2nd version.

>> >>>> +             if (sector_start < num_sectors-(num_sectors / 4))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors);
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 8))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 1;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 16))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 2;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 32))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 3;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 64))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 4;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 128))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 5;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 256))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 6;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 512))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 7;
>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 1024))
>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 8;
>> >>>> +             else
>> >>>> +                     sr_bp = 0;  /* non area protected */
>> >>>
>> >>> Yikes, that's ugly! And I'm not sure it matches the EPCQ doc I found.
>> >>> I'm pretty sure you can rewrite this if/else-if/else block in about 1
>> >>> line though.
>> >>>
>> Yes, i understand that it looks ugly. But it is the best i can do
>> since this function has to satisfy for all the supported devices
>> (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf, start
>> from page 19)
>
> Did you even try? It was possible to simplify the other case, and I'm
> pretty sure this case can be simplified too. How about this? I hacked
> this together and it seems to match:
>
>         if (sector_start <= num_sectors / 2)
>                 sr_bp = __ilog2_u32(num_sectors);
>         else
>                 sr_bp = fls(num_sectors - 1 - sector_start) + 1;
>

Umm. This does not seem right to me. Just look at sector_start <
number_sector / 2. From the altera EPCQ datasheet
(http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf), such as
EPCQ16 (page 19,20). Sector start <number_sector/2 is the case where
TB = 1 (table 17). In this table we have few options and sr_bp =
__ilog2_u32(num_sectors) is only cover for the sector from 0 to 15 to
be protected.


>> >>>> +
>> >>>> +             if (sr_bp < 0) {
>> >>>
>> >>> sr_bp is unsigned, so this is never true.
>> >>>
>> Ok.  I will change to int type.
>
> Are there ever negative values?
>
>> >>>> +static int altera_epcq_probe_config_dt(struct platform_device *pdev,
>> >>>> +                                    struct device_node *np,
>> >>>> +                                    struct altera_epcq_plat_data *pdata)
>> >>>> +{
>> >>>> +     struct device_node *pp = NULL;
>> >>>> +     struct resource *epcq_res;
>> >>>> +     int i = 0;
>> >>>> +     u32 id;
> ...
>> >>>> +             pdata->np[i] = pp;
>> >>>> +
>> >>>> +             /* Read bank id from DT */
>> >>>> +             if (of_get_property(pp, "reg", &id))
>
> I just realized; you're not using this correctly. of_get_property()
> returns the *length* in the third parameter, so you're not actually
> saving the bank ID here. You probably want of_property_read_u32()
> instead.
>

I will remove this since it is unnecessary.

>> >>>
>> >>> Is this property optional? Your DT binding doc doesn't make it clear,
>> >>> but it seems like a property which would be wise to require (i.e., not
>> >>> optional).
>
> ^^^ so there should be a failure case, where you return failure if the
> property is missing.
>
>> >>>> +                     pdata->board_flash_info[i].bank = id;
>> >>>> +             i++;
>> >>>> +     }
>> >>>> +     pdata->num_flashes = i;
>> >>>> +     return 0;
>> >>>> +}
>
> Brian

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
  2015-02-09  6:42         ` Viet Nga Dao
@ 2015-02-10  4:35           ` Viet Nga Dao
  -1 siblings, 0 replies; 39+ messages in thread
From: Viet Nga Dao @ 2015-02-10  4:35 UTC (permalink / raw)
  To: Brian Norris
  Cc: David Woodhouse, linux-mtd, linux-kernel, devicetree, Viet Nga Dao

Sorry for multiple emails.

On Mon, Feb 9, 2015 at 2:42 PM, Viet Nga Dao <vndao@altera.com> wrote:
> Please ignore previous 2 emails of mine ^^
>
> On Thu, Feb 5, 2015 at 4:37 AM, Brian Norris
> <computersforpeace@gmail.com> wrote:
>> On Tue, Jan 27, 2015 at 02:53:59PM +0800, Viet Nga Dao wrote:
>>> On Tue, Jan 20, 2015 at 10:05 AM, Viet Nga Dao <vndao@altera.com> wrote:
>>> > On Thu, Jan 15, 2015 at 5:27 PM, Viet Nga Dao <vndao@altera.com> wrote:
>>> >> On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
>>> >> <computersforpeace@gmail.com> wrote:
>>> >>> On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao@altera.com wrote:
>>> >>>> From: Viet Nga Dao <vndao@altera.com>
>>
>>> >> That is why if rewrite the drivers to follow spi-nor structure, it
>>> >> will require extra decoding works for the only few used opcodes.
>>> >>
>>> >I think you'd only have some very trivial work here.
>>> >
>>> >There would be some small work to reintroduce a properly-replaceable ID
>>> >table, and callbacks like ->lock() and ->unlock(), but those should be
>>> >implemented in spi-nor.c sooner or later anyway.
>>> >
>>>
>>> I am trying to modify the code to follow your recommendation. However,
>>> for lock and unlock functions, i have to implement it in spi_nor.c ,
>>> am i right? If yes, currently we already have existing spi_nor_lock
>>> and spi_nor_unlock functions but they could not apply for my driver.
>>> Should i create a new functions named altera_epcq_lock and unlock and
>>> then map it to callback functions or i should the put  those code
>>> sunder existing spi_nor_lock/unlock?
>>
>> We probably want a replaceable spi_nor callback that does the
>> flash-specific unlock. spi_nor_lock/unlock would then call the
>> nor->unlock() / nor->lock() functions for you, when present.
>> Some of the existing code should move into their own spi_nor_st_lock() /
>> spi_nor_st_unlock() functions.
>>
>
> This changes will be made by me or maintainers? If current this
> functions are not supported, i decide not to implement my lock, unlock
> function as well.
>

I made the changes at my side here. However, there are something i
want to confirm with you:
 - in spi-nor.h, i add 2 functions. _lock and _unlock instead of lock
and unlock. It is because we already have struct mutex lock with the
same name.
    int (*_lock)(struct spi_nor *nor, loff_t offs, uint64_t len);
    int (*_unlock)(struct spi_nor *nor, loff_t offs, uint64_t len);
- in spi-nor.c, i change the current spi_nor_lock and spi_nor_unlock
to stmicro_spi_nor_lock and stmirco_spi_nor_unlock
- in spi-nor.c, i add 2 functions:

static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
    struct spi_nor *nor = mtd_to_spi_nor(mtd);
    int ret = 0;

    ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_LOCK);
    if (ret)
        return ret;

    /* Wait until finished previous command */
    ret = wait_till_ready(nor);
    if (ret)
        goto err;

    ret = nor->_lock(nor, ofs, len);

err:
    spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK);
    return ret;
}

static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
    struct spi_nor *nor = mtd_to_spi_nor(mtd);
    int ret = 0;

    ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_LOCK);
    if (ret)
        return ret;

    /* Wait until finished previous command */
    ret = wait_till_ready(nor);
    if (ret)
        goto err;

    ret = nor->_unlock(nor, ofs, len);

err:
    spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK);
    return ret;
}

- Then under spi_nor_scan function, i add these few lines:
    /* nor protection support for STmicro chips */
    if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) {
        mtd->_lock = stmicro_spi_nor_lock;
        mtd->_unlock = stmicro_spi_nor_unlock;
    } else {
        mtd->_lock = spi_nor_lock;
        mtd->_unlock = spi_nor_unlock;
    }

What is your comment?

>>> >>>> diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>>> >>>> new file mode 100644
>>> >>>> index 0000000..d14f50e
>>> >>>> --- /dev/null
>>> >>>> +++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>>> >>>> @@ -0,0 +1,45 @@
>>> >>>> +* MTD Altera EPCQ driver
>>> >>>> +
>>> >>>> +Required properties:
>>> >>>> +- compatible: Should be "altr,epcq-1.0"
>>> >>>> +- reg: Address and length of the register set  for the device. It contains
>>> >>>> +  the information of registers in the same order as described by reg-names
>>> >>>> +- reg-names: Should contain the reg names
>>> >>>> +  "csr_base": Should contain the register configuration base address
>>> >>>> +  "data_base": Should contain the data base address
>>> >>>> +- is-epcs: boolean type.
>>> >>>> +             If present, the device contains EPCS flashes.
>>> >>>> +             Otherwise, it contains EPCQ flashes.
>>> >>>> +- #address-cells: Must be <1>.
>>> >>>> +- #size-cells: Must be <0>.
>>> >>>> +- flash device tree subnode, there must be a node with the following fields:
>>> >>>
>>> >>> These subnodes definitely require a 'compatible' string. Perhaps they
>>> >>> should be used to differentiate EPCS vs. EPCQ. Does "is-epcs" really
>>> >>> need to be in the top-level controller node?
>>> >>>
>>> >>>> +     - reg: Should contain the flash id
>>> >>>
>>> >>> Should, or must? (This question is relevant, because you seem to make it
>>> >>> optional in your code.) And what does the "flash ID" mean? It seems like
>>> >>> you're using as a chip-select or bank index.
>>> >>>
>>> Yes, this is used for chip select. It is required, not optional. This
>>> to ID for each flash in the device
>>
>> OK, so correct the language here and enforce this in your driver. Right
>> now, you don't fail gracefully if this is missing.
>>
>
> Sorry, you are right. This field is unnecessary for my driver.
> Instead, compatible is replaced. I will update it with 2nd version.
>
>>> >>>> +             if (sector_start < num_sectors-(num_sectors / 4))
>>> >>>> +                     sr_bp = __ilog2_u32(num_sectors);
>>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 8))
>>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 1;
>>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 16))
>>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 2;
>>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 32))
>>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 3;
>>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 64))
>>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 4;
>>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 128))
>>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 5;
>>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 256))
>>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 6;
>>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 512))
>>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 7;
>>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 1024))
>>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 8;
>>> >>>> +             else
>>> >>>> +                     sr_bp = 0;  /* non area protected */
>>> >>>
>>> >>> Yikes, that's ugly! And I'm not sure it matches the EPCQ doc I found.
>>> >>> I'm pretty sure you can rewrite this if/else-if/else block in about 1
>>> >>> line though.
>>> >>>
>>> Yes, i understand that it looks ugly. But it is the best i can do
>>> since this function has to satisfy for all the supported devices
>>> (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf, start
>>> from page 19)
>>
>> Did you even try? It was possible to simplify the other case, and I'm
>> pretty sure this case can be simplified too. How about this? I hacked
>> this together and it seems to match:
>>
>>         if (sector_start <= num_sectors / 2)
>>                 sr_bp = __ilog2_u32(num_sectors);
>>         else
>>                 sr_bp = fls(num_sectors - 1 - sector_start) + 1;
>>
>
> Umm. This does not seem right to me. Just look at sector_start <
> number_sector / 2. From the altera EPCQ datasheet
> (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf), such as
> EPCQ16 (page 19,20). Sector start <number_sector/2 is the case where
> TB = 1 (table 17). In this table we have few options and sr_bp =
> __ilog2_u32(num_sectors) is only cover for the sector from 0 to 15 to
> be protected.
>

I think the code should be like this:

    if (sector_start >= num_sectors / 2)
        sr_bp = fls(num_sectors - 1 - sector_start) + 1;
    else if ((sector_end < num_sectors / 2)
        sr_bp = fls(sector_end) + 1;
    else
        sr_bp = 16; /* lock all areas */


>>> >>>> +
>>> >>>> +             if (sr_bp < 0) {
>>> >>>
>>> >>> sr_bp is unsigned, so this is never true.
>>> >>>
>>> Ok.  I will change to int type.
>>
>> Are there ever negative values?
>>
>>> >>>> +static int altera_epcq_probe_config_dt(struct platform_device *pdev,
>>> >>>> +                                    struct device_node *np,
>>> >>>> +                                    struct altera_epcq_plat_data *pdata)
>>> >>>> +{
>>> >>>> +     struct device_node *pp = NULL;
>>> >>>> +     struct resource *epcq_res;
>>> >>>> +     int i = 0;
>>> >>>> +     u32 id;
>> ...
>>> >>>> +             pdata->np[i] = pp;
>>> >>>> +
>>> >>>> +             /* Read bank id from DT */
>>> >>>> +             if (of_get_property(pp, "reg", &id))
>>
>> I just realized; you're not using this correctly. of_get_property()
>> returns the *length* in the third parameter, so you're not actually
>> saving the bank ID here. You probably want of_property_read_u32()
>> instead.
>>
>
> I will remove this since it is unnecessary.
>
>>> >>>
>>> >>> Is this property optional? Your DT binding doc doesn't make it clear,
>>> >>> but it seems like a property which would be wise to require (i.e., not
>>> >>> optional).
>>
>> ^^^ so there should be a failure case, where you return failure if the
>> property is missing.
>>
>>> >>>> +                     pdata->board_flash_info[i].bank = id;
>>> >>>> +             i++;
>>> >>>> +     }
>>> >>>> +     pdata->num_flashes = i;
>>> >>>> +     return 0;
>>> >>>> +}
>>
>> Brian

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-02-10  4:35           ` Viet Nga Dao
  0 siblings, 0 replies; 39+ messages in thread
From: Viet Nga Dao @ 2015-02-10  4:35 UTC (permalink / raw)
  To: Brian Norris
  Cc: Viet Nga Dao, linux-mtd, David Woodhouse, linux-kernel, devicetree

Sorry for multiple emails.

On Mon, Feb 9, 2015 at 2:42 PM, Viet Nga Dao <vndao@altera.com> wrote:
> Please ignore previous 2 emails of mine ^^
>
> On Thu, Feb 5, 2015 at 4:37 AM, Brian Norris
> <computersforpeace@gmail.com> wrote:
>> On Tue, Jan 27, 2015 at 02:53:59PM +0800, Viet Nga Dao wrote:
>>> On Tue, Jan 20, 2015 at 10:05 AM, Viet Nga Dao <vndao@altera.com> wrote:
>>> > On Thu, Jan 15, 2015 at 5:27 PM, Viet Nga Dao <vndao@altera.com> wrote:
>>> >> On Tue, Jan 13, 2015 at 11:33 AM, Brian Norris
>>> >> <computersforpeace@gmail.com> wrote:
>>> >>> On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao@altera.com wrote:
>>> >>>> From: Viet Nga Dao <vndao@altera.com>
>>
>>> >> That is why if rewrite the drivers to follow spi-nor structure, it
>>> >> will require extra decoding works for the only few used opcodes.
>>> >>
>>> >I think you'd only have some very trivial work here.
>>> >
>>> >There would be some small work to reintroduce a properly-replaceable ID
>>> >table, and callbacks like ->lock() and ->unlock(), but those should be
>>> >implemented in spi-nor.c sooner or later anyway.
>>> >
>>>
>>> I am trying to modify the code to follow your recommendation. However,
>>> for lock and unlock functions, i have to implement it in spi_nor.c ,
>>> am i right? If yes, currently we already have existing spi_nor_lock
>>> and spi_nor_unlock functions but they could not apply for my driver.
>>> Should i create a new functions named altera_epcq_lock and unlock and
>>> then map it to callback functions or i should the put  those code
>>> sunder existing spi_nor_lock/unlock?
>>
>> We probably want a replaceable spi_nor callback that does the
>> flash-specific unlock. spi_nor_lock/unlock would then call the
>> nor->unlock() / nor->lock() functions for you, when present.
>> Some of the existing code should move into their own spi_nor_st_lock() /
>> spi_nor_st_unlock() functions.
>>
>
> This changes will be made by me or maintainers? If current this
> functions are not supported, i decide not to implement my lock, unlock
> function as well.
>

I made the changes at my side here. However, there are something i
want to confirm with you:
 - in spi-nor.h, i add 2 functions. _lock and _unlock instead of lock
and unlock. It is because we already have struct mutex lock with the
same name.
    int (*_lock)(struct spi_nor *nor, loff_t offs, uint64_t len);
    int (*_unlock)(struct spi_nor *nor, loff_t offs, uint64_t len);
- in spi-nor.c, i change the current spi_nor_lock and spi_nor_unlock
to stmicro_spi_nor_lock and stmirco_spi_nor_unlock
- in spi-nor.c, i add 2 functions:

static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
    struct spi_nor *nor = mtd_to_spi_nor(mtd);
    int ret = 0;

    ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_LOCK);
    if (ret)
        return ret;

    /* Wait until finished previous command */
    ret = wait_till_ready(nor);
    if (ret)
        goto err;

    ret = nor->_lock(nor, ofs, len);

err:
    spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK);
    return ret;
}

static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
    struct spi_nor *nor = mtd_to_spi_nor(mtd);
    int ret = 0;

    ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_LOCK);
    if (ret)
        return ret;

    /* Wait until finished previous command */
    ret = wait_till_ready(nor);
    if (ret)
        goto err;

    ret = nor->_unlock(nor, ofs, len);

err:
    spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK);
    return ret;
}

- Then under spi_nor_scan function, i add these few lines:
    /* nor protection support for STmicro chips */
    if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) {
        mtd->_lock = stmicro_spi_nor_lock;
        mtd->_unlock = stmicro_spi_nor_unlock;
    } else {
        mtd->_lock = spi_nor_lock;
        mtd->_unlock = spi_nor_unlock;
    }

What is your comment?

>>> >>>> diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>>> >>>> new file mode 100644
>>> >>>> index 0000000..d14f50e
>>> >>>> --- /dev/null
>>> >>>> +++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
>>> >>>> @@ -0,0 +1,45 @@
>>> >>>> +* MTD Altera EPCQ driver
>>> >>>> +
>>> >>>> +Required properties:
>>> >>>> +- compatible: Should be "altr,epcq-1.0"
>>> >>>> +- reg: Address and length of the register set  for the device. It contains
>>> >>>> +  the information of registers in the same order as described by reg-names
>>> >>>> +- reg-names: Should contain the reg names
>>> >>>> +  "csr_base": Should contain the register configuration base address
>>> >>>> +  "data_base": Should contain the data base address
>>> >>>> +- is-epcs: boolean type.
>>> >>>> +             If present, the device contains EPCS flashes.
>>> >>>> +             Otherwise, it contains EPCQ flashes.
>>> >>>> +- #address-cells: Must be <1>.
>>> >>>> +- #size-cells: Must be <0>.
>>> >>>> +- flash device tree subnode, there must be a node with the following fields:
>>> >>>
>>> >>> These subnodes definitely require a 'compatible' string. Perhaps they
>>> >>> should be used to differentiate EPCS vs. EPCQ. Does "is-epcs" really
>>> >>> need to be in the top-level controller node?
>>> >>>
>>> >>>> +     - reg: Should contain the flash id
>>> >>>
>>> >>> Should, or must? (This question is relevant, because you seem to make it
>>> >>> optional in your code.) And what does the "flash ID" mean? It seems like
>>> >>> you're using as a chip-select or bank index.
>>> >>>
>>> Yes, this is used for chip select. It is required, not optional. This
>>> to ID for each flash in the device
>>
>> OK, so correct the language here and enforce this in your driver. Right
>> now, you don't fail gracefully if this is missing.
>>
>
> Sorry, you are right. This field is unnecessary for my driver.
> Instead, compatible is replaced. I will update it with 2nd version.
>
>>> >>>> +             if (sector_start < num_sectors-(num_sectors / 4))
>>> >>>> +                     sr_bp = __ilog2_u32(num_sectors);
>>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 8))
>>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 1;
>>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 16))
>>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 2;
>>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 32))
>>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 3;
>>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 64))
>>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 4;
>>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 128))
>>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 5;
>>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 256))
>>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 6;
>>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 512))
>>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 7;
>>> >>>> +             else if (sector_start < num_sectors-(num_sectors / 1024))
>>> >>>> +                     sr_bp = __ilog2_u32(num_sectors) - 8;
>>> >>>> +             else
>>> >>>> +                     sr_bp = 0;  /* non area protected */
>>> >>>
>>> >>> Yikes, that's ugly! And I'm not sure it matches the EPCQ doc I found.
>>> >>> I'm pretty sure you can rewrite this if/else-if/else block in about 1
>>> >>> line though.
>>> >>>
>>> Yes, i understand that it looks ugly. But it is the best i can do
>>> since this function has to satisfy for all the supported devices
>>> (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf, start
>>> from page 19)
>>
>> Did you even try? It was possible to simplify the other case, and I'm
>> pretty sure this case can be simplified too. How about this? I hacked
>> this together and it seems to match:
>>
>>         if (sector_start <= num_sectors / 2)
>>                 sr_bp = __ilog2_u32(num_sectors);
>>         else
>>                 sr_bp = fls(num_sectors - 1 - sector_start) + 1;
>>
>
> Umm. This does not seem right to me. Just look at sector_start <
> number_sector / 2. From the altera EPCQ datasheet
> (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf), such as
> EPCQ16 (page 19,20). Sector start <number_sector/2 is the case where
> TB = 1 (table 17). In this table we have few options and sr_bp =
> __ilog2_u32(num_sectors) is only cover for the sector from 0 to 15 to
> be protected.
>

I think the code should be like this:

    if (sector_start >= num_sectors / 2)
        sr_bp = fls(num_sectors - 1 - sector_start) + 1;
    else if ((sector_end < num_sectors / 2)
        sr_bp = fls(sector_end) + 1;
    else
        sr_bp = 16; /* lock all areas */


>>> >>>> +
>>> >>>> +             if (sr_bp < 0) {
>>> >>>
>>> >>> sr_bp is unsigned, so this is never true.
>>> >>>
>>> Ok.  I will change to int type.
>>
>> Are there ever negative values?
>>
>>> >>>> +static int altera_epcq_probe_config_dt(struct platform_device *pdev,
>>> >>>> +                                    struct device_node *np,
>>> >>>> +                                    struct altera_epcq_plat_data *pdata)
>>> >>>> +{
>>> >>>> +     struct device_node *pp = NULL;
>>> >>>> +     struct resource *epcq_res;
>>> >>>> +     int i = 0;
>>> >>>> +     u32 id;
>> ...
>>> >>>> +             pdata->np[i] = pp;
>>> >>>> +
>>> >>>> +             /* Read bank id from DT */
>>> >>>> +             if (of_get_property(pp, "reg", &id))
>>
>> I just realized; you're not using this correctly. of_get_property()
>> returns the *length* in the third parameter, so you're not actually
>> saving the bank ID here. You probably want of_property_read_u32()
>> instead.
>>
>
> I will remove this since it is unnecessary.
>
>>> >>>
>>> >>> Is this property optional? Your DT binding doc doesn't make it clear,
>>> >>> but it seems like a property which would be wise to require (i.e., not
>>> >>> optional).
>>
>> ^^^ so there should be a failure case, where you return failure if the
>> property is missing.
>>
>>> >>>> +                     pdata->board_flash_info[i].bank = id;
>>> >>>> +             i++;
>>> >>>> +     }
>>> >>>> +     pdata->num_flashes = i;
>>> >>>> +     return 0;
>>> >>>> +}
>>
>> Brian

^ permalink raw reply	[flat|nested] 39+ messages in thread

* RE: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
  2015-01-13  3:33   ` Brian Norris
  (?)
@ 2015-01-15  3:05     ` Viet Nga Dao
  -1 siblings, 0 replies; 39+ messages in thread
From: Viet Nga Dao @ 2015-01-15  3:05 UTC (permalink / raw)
  To: Brian Norris; +Cc: dwmw2, linux-mtd, linux-kernel, devicetree, ngachi86

Hi Brian,
For Altera EPCQ flashes, almost operations are performed underline hardware. Software only able to perform the following through registers:
 -  read status register
 -  read id
 -  write status registers bit SR_BP0,SR_BP1, SR_BP2,SR_BP3, SR_TB  (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf)
For read/write data: all the operations like QUAD_READ/WRITE, FAST_READ/WRITE are handled by hardware as well. From software point of view, there is no difference between these 2 modes.

That is why if rewrite the drivers to follow spi-nor structure, it will require extra decoding works for the only few used opcodes.

Thanks,
Viet Nga



-----Original Message-----
From: Brian Norris [mailto:computersforpeace@gmail.com]
Sent: Tuesday, January 13, 2015 11:33 AM
To: Viet Nga Dao
Cc: dwmw2@infradead.org; linux-mtd@lists.infradead.org; linux-kernel@vger.kernel.org; devicetree@vger.kernel.org; ngachi86@gmail.com
Subject: Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver

On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao@altera.com wrote:
> From: Viet Nga Dao <vndao@altera.com>
>
> Altera EPCQ Controller is a soft IP which enables access to Altera
> EPCQ and EPCS flash chips. This patch adds driver for these devices.
>
> Signed-off-by: Viet Nga Dao <vndao@altera.com>

This drivers seems awfully similar to (and so I infer it is likely copy-and-pasted from) m25p80.c / spi-nor.c. Do you think it can be rewritten as a SPI NOR driver, under drivers/mtd/spi-nor/ ? It looks like these flash share most (all?) the same basic opcodes.

> ---
>  .../devicetree/bindings/mtd/altera_epcq.txt        |   45 ++
>  drivers/mtd/devices/Kconfig                        |   12 +
>  drivers/mtd/devices/Makefile                       |    2 +-
>  drivers/mtd/devices/altera_epcq.c                  |  804 ++++++++++++++++++++
>  drivers/mtd/devices/altera_epcq.h                  |  130 ++++
>  5 files changed, 992 insertions(+), 1 deletions(-)  create mode
> 100644 Documentation/devicetree/bindings/mtd/altera_epcq.txt
>  create mode 100644 drivers/mtd/devices/altera_epcq.c  create mode
> 100644 drivers/mtd/devices/altera_epcq.h
>
> diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt
> b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
> new file mode 100644
> index 0000000..d14f50e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
> @@ -0,0 +1,45 @@
> +* MTD Altera EPCQ driver
> +
> +Required properties:
> +- compatible: Should be "altr,epcq-1.0"
> +- reg: Address and length of the register set  for the device. It
> +contains
> +  the information of registers in the same order as described by
> +reg-names
> +- reg-names: Should contain the reg names
> +  "csr_base": Should contain the register configuration base address
> +  "data_base": Should contain the data base address
> +- is-epcs: boolean type.
> +             If present, the device contains EPCS flashes.
> +             Otherwise, it contains EPCQ flashes.
> +- #address-cells: Must be <1>.
> +- #size-cells: Must be <0>.
> +- flash device tree subnode, there must be a node with the following fields:

These subnodes definitely require a 'compatible' string. Perhaps they should be used to differentiate EPCS vs. EPCQ. Does "is-epcs" really need to be in the top-level controller node?

> +     - reg: Should contain the flash id

Should, or must? (This question is relevant, because you seem to make it optional in your code.) And what does the "flash ID" mean? It seems like you're using as a chip-select or bank index.

> +     - #address-cells: please refer to /mtd/partition.txt
> +     - #size-cells: please refer to /mtd/partition.txt
> +     For partitions inside each flash, please refer to /mtd/partition.txt
> +
> +Example:
> +
> +                     epcq_controller_0: epcq@0x000000000 {
> +                             compatible = "altr,epcq-1.0";
> +                             reg = <0x00000001 0x00000000 0x00000020>,
> +                                     <0x00000000 0x00000000 0x02000000>;
> +                             reg-names = "csr_base", "data_base";
> +                             #address-cells = <1>;
> +                             #size-cells = <0>;
> +                             flash0: epcq256@0 {
> +                                     reg = <0>;
> +                                     #address-cells = <1>;
> +                                     #size-cells = <1>;
> +                                     partition@0 {
> +                                             /* 16 MB for raw data. */
> +                                             label = "EPCQ Flash 0 raw data";
> +                                             reg = <0x0 0x1000000>;
> +                                     };
> +                                     partition@1000000 {
> +                                             /* 16 MB for jffs2 data. */
> +                                             label = "EPCQ Flash 0 JFFS 2";
> +                                             reg = <0x1000000 0x1000000>;
> +                                     };
> +                             };
> +                     }; //end epcq@0x000000000 (epcq_controller_0)
> diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
> index c49d0b1..020b864 100644
> --- a/drivers/mtd/devices/Kconfig
> +++ b/drivers/mtd/devices/Kconfig
> @@ -218,6 +218,18 @@ config MTD_ST_SPI_FSM
>         SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
>         for a subset of connected Serial Flash devices.
>
> +config MTD_ALTERA_EPCQ
> +     tristate "Support Altera EPCQ/EPCS Flash chips"
> +     depends on OF
> +     help
> +       This enables access to Altera EPCQ/EPCS flash chips, used for data
> +       storage. See the driver source for the current list,
> +       or to add other chips.
> +
> +       If you want to compile this driver as a module ( = code which can be
> +       inserted in and removed from the running kernel whenever you want),
> +       say M here and read <file:Documentation/kbuild/modules.txt>.
> +
>  if MTD_DOCG3
>  config BCH_CONST_M
>       default 14
> diff --git a/drivers/mtd/devices/Makefile
> b/drivers/mtd/devices/Makefile index f0b0e61..b429c4d 100644
> --- a/drivers/mtd/devices/Makefile
> +++ b/drivers/mtd/devices/Makefile
> @@ -16,6 +16,6 @@ obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
>  obj-$(CONFIG_MTD_SST25L)     += sst25l.o
>  obj-$(CONFIG_MTD_BCM47XXSFLASH)      += bcm47xxsflash.o
>  obj-$(CONFIG_MTD_ST_SPI_FSM)    += st_spi_fsm.o
> -
> +obj-$(CONFIG_MTD_ALTERA_EPCQ)                += altera_epcq.o
>
>  CFLAGS_docg3.o                       += -I$(src)
> diff --git a/drivers/mtd/devices/altera_epcq.c
> b/drivers/mtd/devices/altera_epcq.c
> new file mode 100644
> index 0000000..09213d5
> --- /dev/null
> +++ b/drivers/mtd/devices/altera_epcq.c
> @@ -0,0 +1,804 @@
> +/*
> + * Copyright (C) 2014 Altera Corporation. All rights reserved
> + *
> + * This program is free software; you can redistribute it and/or
> +modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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.
> + *
> + * You should have received a copy of the GNU General Public License
> +along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/errno.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/ioport.h>
> +#include <linux/jiffies.h>
> +#include <linux/kernel.h>
> +#include <linux/log2.h>
> +#include <linux/module.h>
> +#include <linux/mtd/mtd.h>
> +#include <linux/mtd/partitions.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/param.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/wait.h>
> +
> +#include "altera_epcq.h"
> +
> +/* data structure to maintain flash ids from different vendors */
> +struct flash_device {
> +     char *name;
> +     bool is_epcs;
> +     u32 device_id;
> +     uint32_t sectorsize_inbytes;
> +     uint64_t size_inbytes;
> +     u32 pagesize;
> +};
> +
> +#define FLASH_ID(_n, _is_epcs, _id, _ssize, _size, _psize)   \
> +{                            \
> +     .name = (_n),           \
> +     .is_epcs = (_is_epcs),          \
> +     .device_id = (_id),     \
> +     .sectorsize_inbytes = (_ssize), \
> +     .size_inbytes = (_size),        \
> +     .pagesize = (_psize),   \
> +}
> +
> +static struct flash_device flash_devices[] = {
> +     FLASH_ID("epcq16"    , 0, 0x15, 0x10000, 0x200000, 0x100),
> +     FLASH_ID("epcq32"    , 0, 0x16, 0x10000, 0x400000, 0x100),
> +     FLASH_ID("epcq64"    , 0, 0x17, 0x10000, 0x800000, 0x100),
> +     FLASH_ID("epcq128"   , 0, 0x18, 0x10000, 0x1000000, 0x100),
> +     FLASH_ID("epcq256"   , 0, 0x19, 0x10000, 0x2000000, 0x100),
> +     FLASH_ID("epcq512"   , 0, 0x20, 0x10000, 0x4000000, 0x100),
> +
> +     FLASH_ID("epcs16"    , 1, 0x14, 0x10000, 0x200000, 0x100),
> +     FLASH_ID("epcs64"    , 1, 0x16, 0x10000, 0x800000, 0x100),
> +     FLASH_ID("epcs128"   , 1, 0x18, 0x40000, 0x1000000, 0x100),
> +};
> +
> +static inline struct altera_epcq_flash *get_flash_data(struct
> +mtd_info *mtd) {
> +     return container_of(mtd, struct altera_epcq_flash, mtd); }
> +
> +static u32 altera_epcq_read_sr(struct altera_epcq *dev) {
> +     return readl(dev->csr_base + EPCQ_SR_REG); }
> +
> +static int altera_epcq_wait_till_ready(struct altera_epcq *dev) {
> +     unsigned long finish;
> +     int status;
> +
> +     finish = jiffies + EPCQ_MAX_TIME_OUT;
> +     do {
> +             status = altera_epcq_read_sr(dev);
> +             if (status < 0)
> +                     return status;
> +             else if (!(status & EPCQ_SR_WIP))
> +                     return 0;
> +
> +             cond_resched();
> +     } while (!time_after_eq(jiffies, finish));
> +
> +     dev_err(&dev->pdev->dev, "epcq controller is busy, timeout\n");
> +     return -EBUSY;
> +}
> +
> +static int get_flash_index(u32 flash_id, bool is_epcs) {
> +     int index;
> +
> +     for (index = 0; index < ARRAY_SIZE(flash_devices); index++) {
> +             if (flash_devices[index].device_id == flash_id &&
> +                 flash_devices[index].is_epcs == is_epcs)
> +                     return index;
> +     }
> +
> +     /* Memory chip is not listed and not supported */
> +     return -ENODEV;
> +}
> +
> +static int altera_epcq_write_erase_check(struct altera_epcq *dev,
> +                                      bool write_erase)
> +{
> +     u32 val;
> +     u32 mask;
> +
> +     if (write_erase)
> +             mask = EPCQ_ISR_ILLEGAL_WRITE_MASK;
> +     else
> +             mask = EPCQ_ISR_ILLEGAL_ERASE_MASK;
> +
> +     val = readl(dev->csr_base + EPCQ_ISR_REG);
> +     if (val & mask) {
> +             dev_err(&dev->pdev->dev,
> +                     "write/erase failed, sector might be protected\n");
> +             /*clear this status for next use*/
> +             writel(val, dev->csr_base + EPCQ_ISR_REG);
> +             return -EIO;
> +     }
> +     return 0;
> +}
> +
> +static int altera_epcq_erase_chip(struct mtd_info *mtd) {
> +     int ret;
> +     u32 val;
> +     struct altera_epcq *dev = mtd->priv;
> +
> +     /* Wait until finished previous write command. */
> +     ret = altera_epcq_wait_till_ready(dev);

This pattern is pretty silly. We don't need to have every operation check the status of the previous operation. Each operation should check itself. You obviously copied this one... but we recently changed that:

    commit dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3
    Author: Brian Norris <computersforpeace@gmail.com>
    Date:   Wed Aug 6 18:16:57 2014 -0700

        mtd: spi-nor: move "wait-till-ready" checks into erase/write
        functions


    https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3

> +     if (ret)
> +             return ret;
> +
> +     /* erase chip. */
> +     val = EPCQ_MEM_OP_BULK_ERASE_CMD;
> +     writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
> +
> +     /* Wait until finished previous write command. */
> +     ret = altera_epcq_wait_till_ready(dev);
> +     if (ret)
> +             return ret;
> +
> +      /* check whether write triggered a illegal write interrupt */
> +     ret = altera_epcq_write_erase_check(dev, 0);
> +     if (ret < 0)
> +             return ret;
> +
> +     return 0;
> +}
> +
> +static int altera_epcq_addr_to_sector(struct altera_epcq_flash *flash,
> +                                   int addr_offset)
> +{
> +     int sector = 0;
> +     int i;
> +
> +     for (i = 0; i < flash->num_sectors; i++) {
> +             if (addr_offset >= i * flash->mtd.erasesize && addr_offset <
> +                 (i * flash->mtd.erasesize + flash->mtd.erasesize)) {
> +                     sector = i;
> +                     return sector;
> +             }
> +     }

Uh, really? Am I crazy, or shouldn't this whole function just be a really simple arithmetic operation? Like:

        return addr_offset >> mtd->erasesize_shift;

> +     return -1;
> +}
> +
> +static int altera_epcq_erase_sector(struct mtd_info *mtd,
> +                                 int addr_offset)
> +{
> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
> +     struct altera_epcq *dev = mtd->priv;
> +     u32 val;
> +     int ret;
> +     int sector_value;
> +
> +     ret = altera_epcq_wait_till_ready(dev);
> +     if (ret)
> +             return ret;
> +
> +     sector_value = altera_epcq_addr_to_sector(flash, addr_offset);
> +
> +     /* sanity check that block_offset is a valid sector number */
> +     if (sector_value < 0)
> +             return -EINVAL;
> +
> +     /* sector value should occupy bits 17:8 */
> +     val = (sector_value << 8) & EPCQ_MEM_OP_SECTOR_VALUE_MASK;
> +
> +     /* sector erase commands occupies lower 2 bits */
> +     val |= EPCQ_MEM_OP_SECTOR_ERASE_CMD;
> +
> +     /* write sector erase command to EPCQ_MEM_OP register*/
> +     writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
> +
> +     ret = altera_epcq_wait_till_ready(dev);
> +     if (ret)
> +             return ret;
> +
> +      /* check whether write triggered a illegal write interrupt */

^^ extra space here? You have <TAB><SPACE>/* check [...]

> +     ret = altera_epcq_write_erase_check(dev, 0);
> +     if (ret < 0)
> +             return ret;
> +
> +     return 0;
> +}
> +
> +static int altera_epcq_erase(struct mtd_info *mtd, struct erase_info
> +*e_info) {
> +     u32 addr;
> +     int ret;
> +     u32 len;
> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
> +     struct altera_epcq *dev = mtd->priv;
> +
> +     addr = e_info->addr;
> +     len = e_info->len;
> +
> +     if (flash->bank > dev->num_flashes - 1) {
> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
> +             return -EINVAL;
> +     }
> +     mutex_lock(&flash->lock);
> +
> +     /*erase whole chip*/
> +     if (len == mtd->size) {
> +             if (altera_epcq_erase_chip(mtd)) {
> +                     e_info->state = MTD_ERASE_FAILED;
> +                     mutex_unlock(&flash->lock);
> +                     return -EIO;
> +             }
> +     /*"sector"-at-a-time erase*/
> +     } else {
> +             while (len) {
> +                     ret = altera_epcq_erase_sector(mtd, addr);
> +                     if (ret) {
> +                             e_info->state = MTD_ERASE_FAILED;
> +                             mutex_unlock(&flash->lock);
> +                             return -EIO;
> +                     }
> +                     addr += mtd->erasesize;
> +                     if (len < mtd->erasesize)
> +                             len = 0;
> +                     else
> +                             len -= mtd->erasesize;
> +             }
> +     }
> +     mutex_unlock(&flash->lock);
> +     e_info->state = MTD_ERASE_DONE;
> +     mtd_erase_callback(e_info);
> +
> +     return 0;
> +}
> +
> +static int altera_epcq_read(struct mtd_info *mtd, loff_t from, size_t len,
> +                         size_t *retlen, u8 *buf)
> +{
> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
> +     struct altera_epcq *dev = mtd->priv;
> +     void *src;
> +     int ret = 0;
> +
> +     if (!flash || !dev)
> +             return -ENODEV;

Why would either of these be 0? If they are, you have much bigger problems, since you aren't checking these almost anywhere else. And the !flash check is pretty odd, since get_flash_data() is using container_of(), which is not likely to give you exactly 0...

> +
> +     if (flash->bank > dev->num_flashes - 1) {
> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
> +             return -EINVAL;
> +     }
> +
> +     src = dev->data_base + from;
> +
> +     mutex_lock(&flash->lock);
> +     /* wait till previous write/erase is done. */
> +     ret = altera_epcq_wait_till_ready(dev);
> +     if (ret)
> +             goto err;
> +
> +     memcpy_fromio(buf, (u8 *)src, len);

Drop the cast. Also, you get sparse warnings because you're dropping the __iomem. Why not just this?

        memcpy_fromio(buf, dev->data_base + from, len);

(BTW, you might consider running sparse on all your code. See Documentation/sparse.txt. It tells you how to get it, but you can often just download it through your distro's package manager.)

> +     *retlen = len;
> +
> +err:
> +     mutex_unlock(&flash->lock);
> +     return ret;
> +}
> +
> +static int altera_epcq_write(struct mtd_info *mtd, loff_t to, size_t len,
> +                          size_t *retlen, const u8 *buf) {
> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
> +     struct altera_epcq *dev = mtd->priv;
> +     void *dest;
> +     int ret = 0;
> +
> +     if (!flash || !dev)
> +             return -ENODEV;

Also here. I don't think you need these checks.

> +
> +     if (flash->bank > dev->num_flashes - 1) {
> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
> +             return -EINVAL;
> +     }
> +     dest = dev->data_base + to;
> +
> +     mutex_lock(&flash->lock);
> +
> +     /* wait until finished previous write command. */
> +     ret = altera_epcq_wait_till_ready(dev);
> +     if (ret)
> +             goto err;
> +
> +     memcpy_toio(dest, buf, len);

Similar. Why not just this?

        memcpy_toio(dev->data_base + to, buf, len);

> +     *retlen += len;
> +
> +     /* wait until finished previous write command. */
> +     ret = altera_epcq_wait_till_ready(dev);
> +     if (ret)
> +             goto err;
> +
> +      /* check whether write triggered a illegal write interrupt */
> +     ret = altera_epcq_write_erase_check(dev, 1);
> +     if (ret < 0)
> +             goto err;
> +
> +err:
> +     mutex_unlock(&flash->lock);
> +     return ret;
> +}
> +
> +static int altera_epcq_lock(struct mtd_info *mtd, loff_t ofs,
> +uint64_t len) {
> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
> +     struct altera_epcq *dev = mtd->priv;
> +     uint32_t offset = ofs;
> +     int ret = 0;
> +     u32 sector_start, sector_end;
> +     u32 num_sectors;
> +     u32 mem_op;
> +     unsigned sr_bp = 0;
> +     unsigned sr_tb = 0;
> +
> +     sector_start = altera_epcq_addr_to_sector(flash, offset);
> +     sector_end = altera_epcq_addr_to_sector(flash, offset + len);
> +     num_sectors = flash->num_sectors;
> +     dev_dbg(&dev->pdev->dev,
> +             "%s: num_setor is %u,sector start is %u,sector end is %u\n",
> +             __func__, num_sectors, sector_start, sector_end);
> +
> +     mutex_lock(&flash->lock);
> +     /* wait until finished previous write command. */
> +     ret = altera_epcq_wait_till_ready(dev);
> +     if (ret)
> +             goto err;
> +
> +     if (sector_start >= num_sectors/2) {

It's preferable to have spaces around operators. So 'num_sectors / 2'.
The same thing comes up in several other places.

> +             if (sector_start < num_sectors-(num_sectors / 4))
> +                     sr_bp = __ilog2_u32(num_sectors);
> +             else if (sector_start < num_sectors-(num_sectors / 8))
> +                     sr_bp = __ilog2_u32(num_sectors) - 1;
> +             else if (sector_start < num_sectors-(num_sectors / 16))
> +                     sr_bp = __ilog2_u32(num_sectors) - 2;
> +             else if (sector_start < num_sectors-(num_sectors / 32))
> +                     sr_bp = __ilog2_u32(num_sectors) - 3;
> +             else if (sector_start < num_sectors-(num_sectors / 64))
> +                     sr_bp = __ilog2_u32(num_sectors) - 4;
> +             else if (sector_start < num_sectors-(num_sectors / 128))
> +                     sr_bp = __ilog2_u32(num_sectors) - 5;
> +             else if (sector_start < num_sectors-(num_sectors / 256))
> +                     sr_bp = __ilog2_u32(num_sectors) - 6;
> +             else if (sector_start < num_sectors-(num_sectors / 512))
> +                     sr_bp = __ilog2_u32(num_sectors) - 7;
> +             else if (sector_start < num_sectors-(num_sectors / 1024))
> +                     sr_bp = __ilog2_u32(num_sectors) - 8;
> +             else
> +                     sr_bp = 0;  /* non area protected */

Yikes, that's ugly! And I'm not sure it matches the EPCQ doc I found.
I'm pretty sure you can rewrite this if/else-if/else block in about 1 line though.

> +
> +             if (sr_bp < 0) {

sr_bp is unsigned, so this is never true.

> +                     dev_err(&dev->pdev->dev, "%s: address is out of range\n"
> +                             , __func__);
> +                     ret = -EINVAL;
> +                     goto err;
> +             }
> +             /*set TB = 0*/
> +             sr_tb = 0;
> +
> +     } else {
> +             if (sector_end < 1)
> +                     sr_bp = 1;
> +             else if (sector_end < 2)
> +                     sr_bp = 2;
> +             else if (sector_end < 4)
> +                     sr_bp = 3;
> +             else if (sector_end < 8)
> +                     sr_bp = 4;
> +             else if (sector_end < 16)
> +                     sr_bp = 5;
> +             else if (sector_end < 32)
> +                     sr_bp = 6;
> +             else if (sector_end < 64)
> +                     sr_bp = 7;
> +             else if (sector_end < 128)
> +                     sr_bp = 8;
> +             else if (sector_end < 256)
> +                     sr_bp = 9;
> +             else if (sector_end < 512)
> +                     sr_bp = 10;
> +             else
> +                     sr_bp = 16; /*protect all areas*/

Again, this is ugly. How about this?

        sr_bp = fls(sector_end) + 1;

> +
> +             sr_tb = 1;
> +     }
> +
> +     mem_op = (sr_tb << 12) | (sr_bp << 8);
> +     mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
> +     mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
> +     writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
> +
> +err:
> +     mutex_unlock(&flash->lock);
> +     return ret;
> +}
> +
> +static int altera_epcq_unlock(struct mtd_info *mtd, loff_t ofs,
> +uint64_t len) {
> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
> +     struct altera_epcq *dev = mtd->priv;
> +
> +     int ret = 0;
> +     u32 mem_op;
> +
> +     mutex_lock(&flash->lock);
> +     /* wait until finished previous write command. */
> +     ret = altera_epcq_wait_till_ready(dev);
> +     if (ret)
> +             goto err;
> +     dev_info(&dev->pdev->dev, "Unlock all protected area\n");
> +     mem_op = 0;
> +     mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
> +     mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
> +     writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
> +
> +err:
> +     mutex_unlock(&flash->lock);
> +     return ret;
> +}
> +
> +static void altera_epcq_chip_select(struct altera_epcq *dev, u32
> +bank) {
> +     u32 val = 0;
> +
> +     switch (bank) {
> +     case 0:
> +             val = EPCQ_CHIP_SELECT_0;
> +             break;
> +     case 1:
> +             val = EPCQ_CHIP_SELECT_1;
> +             break;
> +     case 2:
> +             val = EPCQ_CHIP_SELECT_2;
> +             break;
> +     default:
> +             return;
> +     }
> +
> +     writel(val, dev->csr_base + EPCQ_CHIP_SELECT_REG); }
> +
> +static int altera_epcq_probe_flash(struct altera_epcq *dev, u32 bank)
> +{
> +     int ret = 0;
> +     u32 val = 0;
> +
> +     mutex_lock(&dev->lock);
> +
> +     /*select bank*/

Comment spacing, here and elsewhere:

        /* select bank */

> +     altera_epcq_chip_select(dev, bank);
> +
> +     ret = altera_epcq_wait_till_ready(dev);
> +     if (ret)
> +             goto err;
> +
> +     /* get device sillicon id */
> +     if (dev->is_epcs)
> +             val = readl(dev->csr_base + EPCQ_SID_REG);
> +     else
> +             val = readl(dev->csr_base + EPCQ_RDID_REG);
> +
> +     /* get flash index based on the device list*/
> +     ret = get_flash_index(val, dev->is_epcs);
> +     return 0;
> +err:
> +     mutex_unlock(&dev->lock);
> +     return ret;
> +}
> +
> +static int altera_epcq_probe_config_dt(struct platform_device *pdev,
> +                                    struct device_node *np,
> +                                    struct altera_epcq_plat_data *pdata) {
> +     struct device_node *pp = NULL;
> +     struct resource *epcq_res;
> +     int i = 0;
> +     u32 id;
> +
> +     pdata->is_epcs = of_property_read_bool(np, "is-epcs");
> +
> +     epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> +                                             "csr_base");
> +     if (!epcq_res) {

devm_ioremap_resource() will check the that 'epcq_res' is valid, so you don't need this whole 'if' block.

> +             dev_err(&pdev->dev, "resource csr base is not defined\n");
> +             return -ENODEV;
> +     }
> +
> +     pdata->csr_base = devm_ioremap_resource(&pdev->dev, epcq_res);
> +     if (IS_ERR(pdata->csr_base)) {
> +             dev_err(&pdev->dev, "%s: ERROR: failed to map csr base\n",
> +                     __func__);
> +             return PTR_ERR(pdata->csr_base);
> +     }
> +
> +     epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> +                                             "data_base");
> +     if (!epcq_res) {

Same here.

> +             dev_err(&pdev->dev, "resource data base is not defined\n");
> +             return -ENODEV;
> +     }
> +
> +     pdata->data_base = devm_ioremap_resource(&pdev->dev, epcq_res);
> +     if (IS_ERR(pdata->data_base)) {
> +             dev_err(&pdev->dev, "%s: ERROR: failed to map data base\n",
> +                     __func__);
> +             return PTR_ERR(pdata->data_base);
> +     }
> +
> +     pdata->board_flash_info = devm_kzalloc(&pdev->dev,
> +                                            sizeof(*pdata->board_flash_info),
> +                                            GFP_KERNEL);
> +
> +     /* Fill structs for each subnode (flash device) */
> +     while ((pp = of_get_next_child(np, pp))) {

for_each_available_child_of_node()?

> +             struct altera_epcq_flash_info *flash_info;
> +
> +             flash_info = &pdata->board_flash_info[i];

This is never used. Either use it below, or drop the local variable.

> +             pdata->np[i] = pp;
> +
> +             /* Read bank id from DT */
> +             if (of_get_property(pp, "reg", &id))

Is this property optional? Your DT binding doc doesn't make it clear, but it seems like a property which would be wise to require (i.e., not optional).

> +                     pdata->board_flash_info[i].bank = id;
> +             i++;
> +     }
> +     pdata->num_flashes = i;
> +     return 0;
> +}
> +
> +static int altera_epcq_setup_banks(struct platform_device *pdev,
> +                                u32 bank, struct device_node *np,
> +                                struct altera_epcq_plat_data *pdata) {
> +     struct altera_epcq *dev = platform_get_drvdata(pdev);
> +     struct mtd_part_parser_data ppdata = {};
> +     struct altera_epcq_flash_info *flash_info;
> +     struct altera_epcq_flash *flash;
> +     struct mtd_partition *parts = NULL;

Drop this variable. Just use NULL directly below.

> +     int count = 0;

Same. Just use 0 below.

> +     int flash_index;
> +     int ret = 0;
> +     uint64_t size;
> +     uint32_t sector_size;
> +
> +     flash_info = &pdata->board_flash_info[bank];
> +     if (!flash_info)
> +             return -ENODEV;
> +
> +     if (bank > pdata->num_flashes - 1)
> +             return -EINVAL;
> +
> +     flash = devm_kzalloc(&pdev->dev, sizeof(*flash), GFP_KERNEL);
> +     if (!flash)
> +             return -ENOMEM;
> +     flash->bank = bank;
> +
> +     mutex_init(&flash->lock);
> +
> +     /* verify whether flash is really present on board */
> +     flash_index = altera_epcq_probe_flash(dev, bank);
> +     if (flash_index < 0) {
> +             dev_info(&dev->pdev->dev, "epcq:flash%d not found\n",
> +                      flash->bank);
> +             return flash_index;
> +     }
> +
> +     dev->flash[bank] = flash;
> +
> +     size = flash_devices[flash_index].size_inbytes;
> +     sector_size = flash_devices[flash_index].sectorsize_inbytes;
> +     /*use do_div instead of plain div to avoid linker err*/
> +     do_div(size, sector_size);
> +     flash->num_sectors = size;
> +
> +     /*mtd framework */
> +     flash->mtd.priv = dev;
> +     flash->mtd.name = flash_devices[flash_index].name;
> +     flash->mtd.type = MTD_NORFLASH;
> +     flash->mtd.writesize = 1;
> +     flash->mtd.flags = MTD_CAP_NORFLASH;
> +     flash->mtd.size = flash_devices[flash_index].size_inbytes;
> +     flash->mtd.erasesize = flash_devices[flash_index].sectorsize_inbytes;
> +     flash->mtd._erase = altera_epcq_erase;
> +     flash->mtd._read = altera_epcq_read;
> +     flash->mtd._write = altera_epcq_write;
> +     flash->mtd._lock = altera_epcq_lock;
> +     flash->mtd._unlock = altera_epcq_unlock;
> +
> +     dev_info(&pdev->dev, "mtd .name=%s .size=0x%llx (%lluM)\n",
> +              flash->mtd.name, (long long)flash->mtd.size,
> +              (long long)(flash->mtd.size >> 20));
> +
> +     dev_info(&pdev->dev, ".erasesize = 0x%x(%uK)\n",
> +              flash->mtd.erasesize, flash->mtd.erasesize >> 10);
> +
> +     ppdata.of_node = np;
> +
> +     ret = mtd_device_parse_register(&flash->mtd, NULL, &ppdata, parts,
> +                                     count);
> +     if (ret) {
> +             dev_err(&pdev->dev, "Err MTD partition=%d\n", ret);
> +             goto err;

You don't want to unregister a MTD that failed to register. The MTD core code should handle that itself.

Instead of this if (ret) {} block, I'd just make this:

        return mtd_device_parse_register(&flash->mtd, NULL, &ppdata, NULL, 0);

> +     }
> +
> +     return 0;
> +
> +err:
> +     mtd_device_unregister(&flash->mtd);
> +     return ret;
> +}
> +
> +static int altera_epcq_probe(struct platform_device *pdev) {
> +     struct device_node *np = pdev->dev.of_node;
> +     struct altera_epcq_plat_data *pdata = NULL;
> +     struct altera_epcq *dev;
> +     int ret = 0;
> +     int i;
> +
> +     if (!np) {
> +             ret = -ENODEV;
> +             dev_err(&pdev->dev, "no device found\n");
> +             goto err;
> +     }
> +
> +     pdata = devm_kzalloc(&pdev->dev,
> +                          sizeof(struct altera_epcq_plat_data),
> +                          GFP_KERNEL);
> +
> +     if (!pdata) {
> +             ret = -ENOMEM;
> +             dev_err(&pdev->dev, "%s: ERROR: no memory\n", __func__);

Unnecessary print. The MM code will print plenty of info if you're out of memory.

> +             goto err;
> +     }
> +     ret = altera_epcq_probe_config_dt(pdev, np, pdata);
> +     if (ret) {
> +             ret = -ENODEV;
> +             dev_err(&pdev->dev, "probe fail\n");
> +             goto err;
> +     }
> +
> +     dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
> +     if (!dev) {
> +             ret = -ENOMEM;
> +             dev_err(&pdev->dev, "mem alloc fail\n");

Same here.

> +             goto err;
> +     }
> +     mutex_init(&dev->lock);
> +     dev->pdev = pdev;
> +     dev->is_epcs = pdata->is_epcs;
> +     dev->csr_base = pdata->csr_base;
> +     dev->data_base = pdata->data_base;
> +
> +     /*check number of flashes*/
> +     dev->num_flashes = pdata->num_flashes;
> +     if (dev->num_flashes > MAX_NUM_FLASH_CHIP) {
> +             dev_err(&pdev->dev, "exceeding max number of flashes\n");
> +             dev->num_flashes = MAX_NUM_FLASH_CHIP;
> +     }
> +
> +     /* check clock*/
> +     dev->clk = devm_clk_get(&pdev->dev, NULL);
> +     if (IS_ERR(dev->clk)) {
> +             ret = PTR_ERR(dev->clk);
> +             goto err;
> +     }
> +     ret = clk_prepare_enable(dev->clk);
> +     if (ret)
> +             goto err;
> +
> +     platform_set_drvdata(pdev, dev);
> +
> +     /* loop for each serial flash which is connected to epcq */
> +     for (i = 0; i < dev->num_flashes; i++) {
> +             ret = altera_epcq_setup_banks(pdev, i, pdata->np[i], pdata);
> +             if (ret) {
> +                     dev_err(&pdev->dev, "bank setup failed\n");
> +                     goto err_bank_setup;
> +             }
> +     }
> +
> +     return 0;
> +
> +err_bank_setup:
> +     clk_disable_unprepare(dev->clk);
> +err:
> +     return ret;
> +}
> +
> +static int altera_epcq_remove(struct platform_device *pdev) {
> +     struct altera_epcq *dev;
> +     struct altera_epcq_flash *flash;
> +     int ret, i;
> +
> +     dev = platform_get_drvdata(pdev);
> +
> +     /* clean up for all nor flash */
> +     for (i = 0; i < dev->num_flashes; i++) {
> +             flash = dev->flash[i];
> +             if (!flash)
> +                     continue;
> +
> +             /* clean up mtd stuff */
> +             ret = mtd_device_unregister(&flash->mtd);
> +             if (ret)
> +                     dev_err(&pdev->dev, "error removing mtd\n");
> +     }
> +
> +     clk_disable_unprepare(dev->clk);
> +
> +     return 0;
> +}
> +
> +#ifdef CONFIG_PM

Make this CONFIG_PM_SLEEP.

> +static int altera_epcq_suspend(struct device *dev) {
> +     struct altera_epcq *sdev = dev_get_drvdata(dev);
> +
> +     clk_disable_unprepare(sdev->clk);
> +
> +     return 0;
> +}
> +
> +static int altera_epcq_resume(struct device *dev) {
> +     struct altera_epcq *sdev = dev_get_drvdata(dev);
> +     int ret = -EPERM;

Drop 'ret'.

> +
> +     ret = clk_prepare_enable(sdev->clk);

Just:

        return clk_prepare_enable(sdev->clk);

> +
> +     return ret;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(altera_epcq_pm_ops, altera_epcq_suspend,
> +                      altera_epcq_resume);
> +#endif
> +
> +static const struct of_device_id altera_epcq_id_table[] = {
> +     { .compatible = "altr,epcq-1.0" },
> +     {}
> +};
> +MODULE_DEVICE_TABLE(of, altera_epcq_id_table);
> +
> +static struct platform_driver altera_epcq_driver = {
> +     .driver = {
> +             .name = ALTERA_EPCQ_RESOURCE_NAME,
> +             .bus = &platform_bus_type,
> +             .owner = THIS_MODULE,

The .owner assignment is no longer necessary.

> +             .of_match_table = altera_epcq_id_table, #ifdef CONFIG_PM

Also CONFIG_PM_SLEEP.

> +             .pm = &altera_epcq_pm_ops,
> +#endif
> +     },
> +     .probe = altera_epcq_probe,
> +     .remove = altera_epcq_remove,
> +};
> +module_platform_driver(altera_epcq_driver);
> +
> +MODULE_AUTHOR("Viet Nga Dao <vndao@altera.com>");
> +MODULE_DESCRIPTION("MTD Altera EPCQ Driver"); MODULE_LICENSE("GPL
> +v2");
> diff --git a/drivers/mtd/devices/altera_epcq.h
> b/drivers/mtd/devices/altera_epcq.h
> new file mode 100644
> index 0000000..c3d15e5
> --- /dev/null
> +++ b/drivers/mtd/devices/altera_epcq.h
> @@ -0,0 +1,130 @@
> +/*
> + * Copyright (C) 2014 Altera Corporation. All rights reserved
> + *
> + * This program is free software; you can redistribute it and/or
> +modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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.
> + *
> + * You should have received a copy of the GNU General Public License
> +along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef __ALTERA_ECPQ_H
> +#define __ALTERA_ECPQ_H
> +
> +#define ALTERA_EPCQ_RESOURCE_NAME        "altera_epcq"
> +/* max possible slots for serial flash chip in the EPCQ controller */
> +#define MAX_NUM_FLASH_CHIP   3
> +
> +/* macro to define partitions for flash devices */
> +#define DEFINE_PARTS(n, of, s)               \

This macro is not used anywhere. Drop it?

> +{                                    \
> +     .name = n,                      \
> +     .offset = of,                   \
> +     .size = s,                      \
> +}
> +
> +struct altera_epcq_flash_info {
> +     u32 bank;
> +     struct mtd_partition *partitions;
> +     int nr_partitions;
> +};
> +
> +struct altera_epcq_plat_data {
> +     void __iomem *csr_base;
> +     void __iomem *data_base;
> +     bool is_epcs;
> +     u32 num_flashes;
> +     struct altera_epcq_flash_info *board_flash_info;
> +     struct device_node *np[MAX_NUM_FLASH_CHIP]; };
> +
> +struct altera_epcq {
> +     struct clk *clk;
> +     bool is_epcs;
> +     struct mutex lock;      /*device lock*/
> +     void __iomem *csr_base;
> +     void __iomem *data_base;
> +     struct platform_device *pdev;
> +     u32 num_flashes;
> +     struct altera_epcq_flash *flash[MAX_NUM_FLASH_CHIP]; };
> +
> +struct altera_epcq_flash {
> +     u32 bank;
> +     u32 num_sectors;
> +     u32 num_parts;
> +     struct mtd_partition *parts;
> +     struct mutex lock;      /*flash lock*/
> +     struct mtd_info mtd;
> +};
> +
> +/* Define max times to check status register before we give up. */
> +#define EPCQ_MAX_TIME_OUT                    (40 * HZ)
> +
> +/* defines for status register */
> +#define EPCQ_SR_REG                          0x0
> +#define EPCQ_SR_WIP_MASK                     0x00000001
> +#define EPCQ_SR_WIP                          0x1
> +#define EPCQ_SR_WEL                          0x2
> +#define EPCQ_SR_BP0                          0x4
> +#define EPCQ_SR_BP1                          0x8
> +#define EPCQ_SR_BP2                          0x10
> +#define EPCQ_SR_BP3                          0x40
> +#define EPCQ_SR_TB                           0x20
> +
> +/* defines for device id register */
> +#define EPCQ_SID_REG                         0x4
> +#define EPCQ_RDID_REG                                0x8
> +#define EPCQ_RDID_MASK                               0x000000FF
> +/*
> + * EPCQ_MEM_OP register offset
> + *
> + * The EPCQ_MEM_OP register is used to do memory protect and erase
> +operations
> + *
> + */
> +#define EPCQ_MEM_OP_REG                              0xC
> +
> +#define EPCQ_MEM_OP_CMD_MASK                 0x00000003
> +#define EPCQ_MEM_OP_BULK_ERASE_CMD           0x00000001
> +#define EPCQ_MEM_OP_SECTOR_ERASE_CMD         0x00000002
> +#define EPCQ_MEM_OP_SECTOR_PROTECT_CMD               0x00000003
> +#define EPCQ_MEM_OP_SECTOR_VALUE_MASK                0x0003FF00
> +#define EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK        0x00001F00
> +#define EPCQ_MEM_OP_SECTOR_PROTECT_SHIFT     8
> +/*
> + * EPCQ_ISR register offset
> + *
> + * The EPCQ_ISR register is used to determine whether an invalid
> +write or erase
> + * operation trigerred an interrupt
> + *
> + */
> +#define EPCQ_ISR_REG                         0x10
> +
> +#define EPCQ_ISR_ILLEGAL_ERASE_MASK          0x00000001
> +#define EPCQ_ISR_ILLEGAL_WRITE_MASK          0x00000002
> +
> +/*
> + * EPCQ_IMR register offset
> + *
> + * The EPCQ_IMR register is used to mask the invalid erase or the
> +invalid write
> + * interrupts.
> + *
> + */
> +#define EPCQ_IMR_REG                         0x14
> +#define EPCQ_IMR_ILLEGAL_ERASE_MASK          0x00000001
> +
> +#define EPCQ_IMR_ILLEGAL_WRITE_MASK          0x00000002
> +
> +#define EPCQ_CHIP_SELECT_REG                 0x18
> +#define EPCQ_CHIP_SELECT_MASK                        0x00000007
> +#define EPCQ_CHIP_SELECT_0                   0x00000001
> +#define EPCQ_CHIP_SELECT_1                   0x00000002
> +#define EPCQ_CHIP_SELECT_2                   0x00000004
> +
> +#endif /* __ALTERA_ECPQ_H */

Brian

________________________________

Confidentiality Notice.
This message may contain information that is confidential or otherwise protected from disclosure. If you are not the intended recipient, you are hereby notified that any use, disclosure, dissemination, distribution, or copying of this message, or any attachments, is strictly prohibited. If you have received this message in error, please advise the sender by reply e-mail, and delete the message and any attachments. Thank you.

^ permalink raw reply	[flat|nested] 39+ messages in thread

* RE: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-01-15  3:05     ` Viet Nga Dao
  0 siblings, 0 replies; 39+ messages in thread
From: Viet Nga Dao @ 2015-01-15  3:05 UTC (permalink / raw)
  To: Brian Norris
  Cc: dwmw2-wEGCiKHe2LqWVfeAwA7xHQ,
	linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	ngachi86-Re5JQEeQqe8AvxtiuMwx3w

Hi Brian,
For Altera EPCQ flashes, almost operations are performed underline hardware. Software only able to perform the following through registers:
 -  read status register
 -  read id
 -  write status registers bit SR_BP0,SR_BP1, SR_BP2,SR_BP3, SR_TB  (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf)
For read/write data: all the operations like QUAD_READ/WRITE, FAST_READ/WRITE are handled by hardware as well. From software point of view, there is no difference between these 2 modes.

That is why if rewrite the drivers to follow spi-nor structure, it will require extra decoding works for the only few used opcodes.

Thanks,
Viet Nga



-----Original Message-----
From: Brian Norris [mailto:computersforpeace-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org]
Sent: Tuesday, January 13, 2015 11:33 AM
To: Viet Nga Dao
Cc: dwmw2-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org; linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org; linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; ngachi86-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
Subject: Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver

On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org wrote:
> From: Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org>
>
> Altera EPCQ Controller is a soft IP which enables access to Altera
> EPCQ and EPCS flash chips. This patch adds driver for these devices.
>
> Signed-off-by: Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org>

This drivers seems awfully similar to (and so I infer it is likely copy-and-pasted from) m25p80.c / spi-nor.c. Do you think it can be rewritten as a SPI NOR driver, under drivers/mtd/spi-nor/ ? It looks like these flash share most (all?) the same basic opcodes.

> ---
>  .../devicetree/bindings/mtd/altera_epcq.txt        |   45 ++
>  drivers/mtd/devices/Kconfig                        |   12 +
>  drivers/mtd/devices/Makefile                       |    2 +-
>  drivers/mtd/devices/altera_epcq.c                  |  804 ++++++++++++++++++++
>  drivers/mtd/devices/altera_epcq.h                  |  130 ++++
>  5 files changed, 992 insertions(+), 1 deletions(-)  create mode
> 100644 Documentation/devicetree/bindings/mtd/altera_epcq.txt
>  create mode 100644 drivers/mtd/devices/altera_epcq.c  create mode
> 100644 drivers/mtd/devices/altera_epcq.h
>
> diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt
> b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
> new file mode 100644
> index 0000000..d14f50e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
> @@ -0,0 +1,45 @@
> +* MTD Altera EPCQ driver
> +
> +Required properties:
> +- compatible: Should be "altr,epcq-1.0"
> +- reg: Address and length of the register set  for the device. It
> +contains
> +  the information of registers in the same order as described by
> +reg-names
> +- reg-names: Should contain the reg names
> +  "csr_base": Should contain the register configuration base address
> +  "data_base": Should contain the data base address
> +- is-epcs: boolean type.
> +             If present, the device contains EPCS flashes.
> +             Otherwise, it contains EPCQ flashes.
> +- #address-cells: Must be <1>.
> +- #size-cells: Must be <0>.
> +- flash device tree subnode, there must be a node with the following fields:

These subnodes definitely require a 'compatible' string. Perhaps they should be used to differentiate EPCS vs. EPCQ. Does "is-epcs" really need to be in the top-level controller node?

> +     - reg: Should contain the flash id

Should, or must? (This question is relevant, because you seem to make it optional in your code.) And what does the "flash ID" mean? It seems like you're using as a chip-select or bank index.

> +     - #address-cells: please refer to /mtd/partition.txt
> +     - #size-cells: please refer to /mtd/partition.txt
> +     For partitions inside each flash, please refer to /mtd/partition.txt
> +
> +Example:
> +
> +                     epcq_controller_0: epcq@0x000000000 {
> +                             compatible = "altr,epcq-1.0";
> +                             reg = <0x00000001 0x00000000 0x00000020>,
> +                                     <0x00000000 0x00000000 0x02000000>;
> +                             reg-names = "csr_base", "data_base";
> +                             #address-cells = <1>;
> +                             #size-cells = <0>;
> +                             flash0: epcq256@0 {
> +                                     reg = <0>;
> +                                     #address-cells = <1>;
> +                                     #size-cells = <1>;
> +                                     partition@0 {
> +                                             /* 16 MB for raw data. */
> +                                             label = "EPCQ Flash 0 raw data";
> +                                             reg = <0x0 0x1000000>;
> +                                     };
> +                                     partition@1000000 {
> +                                             /* 16 MB for jffs2 data. */
> +                                             label = "EPCQ Flash 0 JFFS 2";
> +                                             reg = <0x1000000 0x1000000>;
> +                                     };
> +                             };
> +                     }; //end epcq@0x000000000 (epcq_controller_0)
> diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
> index c49d0b1..020b864 100644
> --- a/drivers/mtd/devices/Kconfig
> +++ b/drivers/mtd/devices/Kconfig
> @@ -218,6 +218,18 @@ config MTD_ST_SPI_FSM
>         SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
>         for a subset of connected Serial Flash devices.
>
> +config MTD_ALTERA_EPCQ
> +     tristate "Support Altera EPCQ/EPCS Flash chips"
> +     depends on OF
> +     help
> +       This enables access to Altera EPCQ/EPCS flash chips, used for data
> +       storage. See the driver source for the current list,
> +       or to add other chips.
> +
> +       If you want to compile this driver as a module ( = code which can be
> +       inserted in and removed from the running kernel whenever you want),
> +       say M here and read <file:Documentation/kbuild/modules.txt>.
> +
>  if MTD_DOCG3
>  config BCH_CONST_M
>       default 14
> diff --git a/drivers/mtd/devices/Makefile
> b/drivers/mtd/devices/Makefile index f0b0e61..b429c4d 100644
> --- a/drivers/mtd/devices/Makefile
> +++ b/drivers/mtd/devices/Makefile
> @@ -16,6 +16,6 @@ obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
>  obj-$(CONFIG_MTD_SST25L)     += sst25l.o
>  obj-$(CONFIG_MTD_BCM47XXSFLASH)      += bcm47xxsflash.o
>  obj-$(CONFIG_MTD_ST_SPI_FSM)    += st_spi_fsm.o
> -
> +obj-$(CONFIG_MTD_ALTERA_EPCQ)                += altera_epcq.o
>
>  CFLAGS_docg3.o                       += -I$(src)
> diff --git a/drivers/mtd/devices/altera_epcq.c
> b/drivers/mtd/devices/altera_epcq.c
> new file mode 100644
> index 0000000..09213d5
> --- /dev/null
> +++ b/drivers/mtd/devices/altera_epcq.c
> @@ -0,0 +1,804 @@
> +/*
> + * Copyright (C) 2014 Altera Corporation. All rights reserved
> + *
> + * This program is free software; you can redistribute it and/or
> +modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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.
> + *
> + * You should have received a copy of the GNU General Public License
> +along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/errno.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/ioport.h>
> +#include <linux/jiffies.h>
> +#include <linux/kernel.h>
> +#include <linux/log2.h>
> +#include <linux/module.h>
> +#include <linux/mtd/mtd.h>
> +#include <linux/mtd/partitions.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/param.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/wait.h>
> +
> +#include "altera_epcq.h"
> +
> +/* data structure to maintain flash ids from different vendors */
> +struct flash_device {
> +     char *name;
> +     bool is_epcs;
> +     u32 device_id;
> +     uint32_t sectorsize_inbytes;
> +     uint64_t size_inbytes;
> +     u32 pagesize;
> +};
> +
> +#define FLASH_ID(_n, _is_epcs, _id, _ssize, _size, _psize)   \
> +{                            \
> +     .name = (_n),           \
> +     .is_epcs = (_is_epcs),          \
> +     .device_id = (_id),     \
> +     .sectorsize_inbytes = (_ssize), \
> +     .size_inbytes = (_size),        \
> +     .pagesize = (_psize),   \
> +}
> +
> +static struct flash_device flash_devices[] = {
> +     FLASH_ID("epcq16"    , 0, 0x15, 0x10000, 0x200000, 0x100),
> +     FLASH_ID("epcq32"    , 0, 0x16, 0x10000, 0x400000, 0x100),
> +     FLASH_ID("epcq64"    , 0, 0x17, 0x10000, 0x800000, 0x100),
> +     FLASH_ID("epcq128"   , 0, 0x18, 0x10000, 0x1000000, 0x100),
> +     FLASH_ID("epcq256"   , 0, 0x19, 0x10000, 0x2000000, 0x100),
> +     FLASH_ID("epcq512"   , 0, 0x20, 0x10000, 0x4000000, 0x100),
> +
> +     FLASH_ID("epcs16"    , 1, 0x14, 0x10000, 0x200000, 0x100),
> +     FLASH_ID("epcs64"    , 1, 0x16, 0x10000, 0x800000, 0x100),
> +     FLASH_ID("epcs128"   , 1, 0x18, 0x40000, 0x1000000, 0x100),
> +};
> +
> +static inline struct altera_epcq_flash *get_flash_data(struct
> +mtd_info *mtd) {
> +     return container_of(mtd, struct altera_epcq_flash, mtd); }
> +
> +static u32 altera_epcq_read_sr(struct altera_epcq *dev) {
> +     return readl(dev->csr_base + EPCQ_SR_REG); }
> +
> +static int altera_epcq_wait_till_ready(struct altera_epcq *dev) {
> +     unsigned long finish;
> +     int status;
> +
> +     finish = jiffies + EPCQ_MAX_TIME_OUT;
> +     do {
> +             status = altera_epcq_read_sr(dev);
> +             if (status < 0)
> +                     return status;
> +             else if (!(status & EPCQ_SR_WIP))
> +                     return 0;
> +
> +             cond_resched();
> +     } while (!time_after_eq(jiffies, finish));
> +
> +     dev_err(&dev->pdev->dev, "epcq controller is busy, timeout\n");
> +     return -EBUSY;
> +}
> +
> +static int get_flash_index(u32 flash_id, bool is_epcs) {
> +     int index;
> +
> +     for (index = 0; index < ARRAY_SIZE(flash_devices); index++) {
> +             if (flash_devices[index].device_id == flash_id &&
> +                 flash_devices[index].is_epcs == is_epcs)
> +                     return index;
> +     }
> +
> +     /* Memory chip is not listed and not supported */
> +     return -ENODEV;
> +}
> +
> +static int altera_epcq_write_erase_check(struct altera_epcq *dev,
> +                                      bool write_erase)
> +{
> +     u32 val;
> +     u32 mask;
> +
> +     if (write_erase)
> +             mask = EPCQ_ISR_ILLEGAL_WRITE_MASK;
> +     else
> +             mask = EPCQ_ISR_ILLEGAL_ERASE_MASK;
> +
> +     val = readl(dev->csr_base + EPCQ_ISR_REG);
> +     if (val & mask) {
> +             dev_err(&dev->pdev->dev,
> +                     "write/erase failed, sector might be protected\n");
> +             /*clear this status for next use*/
> +             writel(val, dev->csr_base + EPCQ_ISR_REG);
> +             return -EIO;
> +     }
> +     return 0;
> +}
> +
> +static int altera_epcq_erase_chip(struct mtd_info *mtd) {
> +     int ret;
> +     u32 val;
> +     struct altera_epcq *dev = mtd->priv;
> +
> +     /* Wait until finished previous write command. */
> +     ret = altera_epcq_wait_till_ready(dev);

This pattern is pretty silly. We don't need to have every operation check the status of the previous operation. Each operation should check itself. You obviously copied this one... but we recently changed that:

    commit dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3
    Author: Brian Norris <computersforpeace-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
    Date:   Wed Aug 6 18:16:57 2014 -0700

        mtd: spi-nor: move "wait-till-ready" checks into erase/write
        functions


    https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3

> +     if (ret)
> +             return ret;
> +
> +     /* erase chip. */
> +     val = EPCQ_MEM_OP_BULK_ERASE_CMD;
> +     writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
> +
> +     /* Wait until finished previous write command. */
> +     ret = altera_epcq_wait_till_ready(dev);
> +     if (ret)
> +             return ret;
> +
> +      /* check whether write triggered a illegal write interrupt */
> +     ret = altera_epcq_write_erase_check(dev, 0);
> +     if (ret < 0)
> +             return ret;
> +
> +     return 0;
> +}
> +
> +static int altera_epcq_addr_to_sector(struct altera_epcq_flash *flash,
> +                                   int addr_offset)
> +{
> +     int sector = 0;
> +     int i;
> +
> +     for (i = 0; i < flash->num_sectors; i++) {
> +             if (addr_offset >= i * flash->mtd.erasesize && addr_offset <
> +                 (i * flash->mtd.erasesize + flash->mtd.erasesize)) {
> +                     sector = i;
> +                     return sector;
> +             }
> +     }

Uh, really? Am I crazy, or shouldn't this whole function just be a really simple arithmetic operation? Like:

        return addr_offset >> mtd->erasesize_shift;

> +     return -1;
> +}
> +
> +static int altera_epcq_erase_sector(struct mtd_info *mtd,
> +                                 int addr_offset)
> +{
> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
> +     struct altera_epcq *dev = mtd->priv;
> +     u32 val;
> +     int ret;
> +     int sector_value;
> +
> +     ret = altera_epcq_wait_till_ready(dev);
> +     if (ret)
> +             return ret;
> +
> +     sector_value = altera_epcq_addr_to_sector(flash, addr_offset);
> +
> +     /* sanity check that block_offset is a valid sector number */
> +     if (sector_value < 0)
> +             return -EINVAL;
> +
> +     /* sector value should occupy bits 17:8 */
> +     val = (sector_value << 8) & EPCQ_MEM_OP_SECTOR_VALUE_MASK;
> +
> +     /* sector erase commands occupies lower 2 bits */
> +     val |= EPCQ_MEM_OP_SECTOR_ERASE_CMD;
> +
> +     /* write sector erase command to EPCQ_MEM_OP register*/
> +     writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
> +
> +     ret = altera_epcq_wait_till_ready(dev);
> +     if (ret)
> +             return ret;
> +
> +      /* check whether write triggered a illegal write interrupt */

^^ extra space here? You have <TAB><SPACE>/* check [...]

> +     ret = altera_epcq_write_erase_check(dev, 0);
> +     if (ret < 0)
> +             return ret;
> +
> +     return 0;
> +}
> +
> +static int altera_epcq_erase(struct mtd_info *mtd, struct erase_info
> +*e_info) {
> +     u32 addr;
> +     int ret;
> +     u32 len;
> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
> +     struct altera_epcq *dev = mtd->priv;
> +
> +     addr = e_info->addr;
> +     len = e_info->len;
> +
> +     if (flash->bank > dev->num_flashes - 1) {
> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
> +             return -EINVAL;
> +     }
> +     mutex_lock(&flash->lock);
> +
> +     /*erase whole chip*/
> +     if (len == mtd->size) {
> +             if (altera_epcq_erase_chip(mtd)) {
> +                     e_info->state = MTD_ERASE_FAILED;
> +                     mutex_unlock(&flash->lock);
> +                     return -EIO;
> +             }
> +     /*"sector"-at-a-time erase*/
> +     } else {
> +             while (len) {
> +                     ret = altera_epcq_erase_sector(mtd, addr);
> +                     if (ret) {
> +                             e_info->state = MTD_ERASE_FAILED;
> +                             mutex_unlock(&flash->lock);
> +                             return -EIO;
> +                     }
> +                     addr += mtd->erasesize;
> +                     if (len < mtd->erasesize)
> +                             len = 0;
> +                     else
> +                             len -= mtd->erasesize;
> +             }
> +     }
> +     mutex_unlock(&flash->lock);
> +     e_info->state = MTD_ERASE_DONE;
> +     mtd_erase_callback(e_info);
> +
> +     return 0;
> +}
> +
> +static int altera_epcq_read(struct mtd_info *mtd, loff_t from, size_t len,
> +                         size_t *retlen, u8 *buf)
> +{
> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
> +     struct altera_epcq *dev = mtd->priv;
> +     void *src;
> +     int ret = 0;
> +
> +     if (!flash || !dev)
> +             return -ENODEV;

Why would either of these be 0? If they are, you have much bigger problems, since you aren't checking these almost anywhere else. And the !flash check is pretty odd, since get_flash_data() is using container_of(), which is not likely to give you exactly 0...

> +
> +     if (flash->bank > dev->num_flashes - 1) {
> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
> +             return -EINVAL;
> +     }
> +
> +     src = dev->data_base + from;
> +
> +     mutex_lock(&flash->lock);
> +     /* wait till previous write/erase is done. */
> +     ret = altera_epcq_wait_till_ready(dev);
> +     if (ret)
> +             goto err;
> +
> +     memcpy_fromio(buf, (u8 *)src, len);

Drop the cast. Also, you get sparse warnings because you're dropping the __iomem. Why not just this?

        memcpy_fromio(buf, dev->data_base + from, len);

(BTW, you might consider running sparse on all your code. See Documentation/sparse.txt. It tells you how to get it, but you can often just download it through your distro's package manager.)

> +     *retlen = len;
> +
> +err:
> +     mutex_unlock(&flash->lock);
> +     return ret;
> +}
> +
> +static int altera_epcq_write(struct mtd_info *mtd, loff_t to, size_t len,
> +                          size_t *retlen, const u8 *buf) {
> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
> +     struct altera_epcq *dev = mtd->priv;
> +     void *dest;
> +     int ret = 0;
> +
> +     if (!flash || !dev)
> +             return -ENODEV;

Also here. I don't think you need these checks.

> +
> +     if (flash->bank > dev->num_flashes - 1) {
> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
> +             return -EINVAL;
> +     }
> +     dest = dev->data_base + to;
> +
> +     mutex_lock(&flash->lock);
> +
> +     /* wait until finished previous write command. */
> +     ret = altera_epcq_wait_till_ready(dev);
> +     if (ret)
> +             goto err;
> +
> +     memcpy_toio(dest, buf, len);

Similar. Why not just this?

        memcpy_toio(dev->data_base + to, buf, len);

> +     *retlen += len;
> +
> +     /* wait until finished previous write command. */
> +     ret = altera_epcq_wait_till_ready(dev);
> +     if (ret)
> +             goto err;
> +
> +      /* check whether write triggered a illegal write interrupt */
> +     ret = altera_epcq_write_erase_check(dev, 1);
> +     if (ret < 0)
> +             goto err;
> +
> +err:
> +     mutex_unlock(&flash->lock);
> +     return ret;
> +}
> +
> +static int altera_epcq_lock(struct mtd_info *mtd, loff_t ofs,
> +uint64_t len) {
> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
> +     struct altera_epcq *dev = mtd->priv;
> +     uint32_t offset = ofs;
> +     int ret = 0;
> +     u32 sector_start, sector_end;
> +     u32 num_sectors;
> +     u32 mem_op;
> +     unsigned sr_bp = 0;
> +     unsigned sr_tb = 0;
> +
> +     sector_start = altera_epcq_addr_to_sector(flash, offset);
> +     sector_end = altera_epcq_addr_to_sector(flash, offset + len);
> +     num_sectors = flash->num_sectors;
> +     dev_dbg(&dev->pdev->dev,
> +             "%s: num_setor is %u,sector start is %u,sector end is %u\n",
> +             __func__, num_sectors, sector_start, sector_end);
> +
> +     mutex_lock(&flash->lock);
> +     /* wait until finished previous write command. */
> +     ret = altera_epcq_wait_till_ready(dev);
> +     if (ret)
> +             goto err;
> +
> +     if (sector_start >= num_sectors/2) {

It's preferable to have spaces around operators. So 'num_sectors / 2'.
The same thing comes up in several other places.

> +             if (sector_start < num_sectors-(num_sectors / 4))
> +                     sr_bp = __ilog2_u32(num_sectors);
> +             else if (sector_start < num_sectors-(num_sectors / 8))
> +                     sr_bp = __ilog2_u32(num_sectors) - 1;
> +             else if (sector_start < num_sectors-(num_sectors / 16))
> +                     sr_bp = __ilog2_u32(num_sectors) - 2;
> +             else if (sector_start < num_sectors-(num_sectors / 32))
> +                     sr_bp = __ilog2_u32(num_sectors) - 3;
> +             else if (sector_start < num_sectors-(num_sectors / 64))
> +                     sr_bp = __ilog2_u32(num_sectors) - 4;
> +             else if (sector_start < num_sectors-(num_sectors / 128))
> +                     sr_bp = __ilog2_u32(num_sectors) - 5;
> +             else if (sector_start < num_sectors-(num_sectors / 256))
> +                     sr_bp = __ilog2_u32(num_sectors) - 6;
> +             else if (sector_start < num_sectors-(num_sectors / 512))
> +                     sr_bp = __ilog2_u32(num_sectors) - 7;
> +             else if (sector_start < num_sectors-(num_sectors / 1024))
> +                     sr_bp = __ilog2_u32(num_sectors) - 8;
> +             else
> +                     sr_bp = 0;  /* non area protected */

Yikes, that's ugly! And I'm not sure it matches the EPCQ doc I found.
I'm pretty sure you can rewrite this if/else-if/else block in about 1 line though.

> +
> +             if (sr_bp < 0) {

sr_bp is unsigned, so this is never true.

> +                     dev_err(&dev->pdev->dev, "%s: address is out of range\n"
> +                             , __func__);
> +                     ret = -EINVAL;
> +                     goto err;
> +             }
> +             /*set TB = 0*/
> +             sr_tb = 0;
> +
> +     } else {
> +             if (sector_end < 1)
> +                     sr_bp = 1;
> +             else if (sector_end < 2)
> +                     sr_bp = 2;
> +             else if (sector_end < 4)
> +                     sr_bp = 3;
> +             else if (sector_end < 8)
> +                     sr_bp = 4;
> +             else if (sector_end < 16)
> +                     sr_bp = 5;
> +             else if (sector_end < 32)
> +                     sr_bp = 6;
> +             else if (sector_end < 64)
> +                     sr_bp = 7;
> +             else if (sector_end < 128)
> +                     sr_bp = 8;
> +             else if (sector_end < 256)
> +                     sr_bp = 9;
> +             else if (sector_end < 512)
> +                     sr_bp = 10;
> +             else
> +                     sr_bp = 16; /*protect all areas*/

Again, this is ugly. How about this?

        sr_bp = fls(sector_end) + 1;

> +
> +             sr_tb = 1;
> +     }
> +
> +     mem_op = (sr_tb << 12) | (sr_bp << 8);
> +     mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
> +     mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
> +     writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
> +
> +err:
> +     mutex_unlock(&flash->lock);
> +     return ret;
> +}
> +
> +static int altera_epcq_unlock(struct mtd_info *mtd, loff_t ofs,
> +uint64_t len) {
> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
> +     struct altera_epcq *dev = mtd->priv;
> +
> +     int ret = 0;
> +     u32 mem_op;
> +
> +     mutex_lock(&flash->lock);
> +     /* wait until finished previous write command. */
> +     ret = altera_epcq_wait_till_ready(dev);
> +     if (ret)
> +             goto err;
> +     dev_info(&dev->pdev->dev, "Unlock all protected area\n");
> +     mem_op = 0;
> +     mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
> +     mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
> +     writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
> +
> +err:
> +     mutex_unlock(&flash->lock);
> +     return ret;
> +}
> +
> +static void altera_epcq_chip_select(struct altera_epcq *dev, u32
> +bank) {
> +     u32 val = 0;
> +
> +     switch (bank) {
> +     case 0:
> +             val = EPCQ_CHIP_SELECT_0;
> +             break;
> +     case 1:
> +             val = EPCQ_CHIP_SELECT_1;
> +             break;
> +     case 2:
> +             val = EPCQ_CHIP_SELECT_2;
> +             break;
> +     default:
> +             return;
> +     }
> +
> +     writel(val, dev->csr_base + EPCQ_CHIP_SELECT_REG); }
> +
> +static int altera_epcq_probe_flash(struct altera_epcq *dev, u32 bank)
> +{
> +     int ret = 0;
> +     u32 val = 0;
> +
> +     mutex_lock(&dev->lock);
> +
> +     /*select bank*/

Comment spacing, here and elsewhere:

        /* select bank */

> +     altera_epcq_chip_select(dev, bank);
> +
> +     ret = altera_epcq_wait_till_ready(dev);
> +     if (ret)
> +             goto err;
> +
> +     /* get device sillicon id */
> +     if (dev->is_epcs)
> +             val = readl(dev->csr_base + EPCQ_SID_REG);
> +     else
> +             val = readl(dev->csr_base + EPCQ_RDID_REG);
> +
> +     /* get flash index based on the device list*/
> +     ret = get_flash_index(val, dev->is_epcs);
> +     return 0;
> +err:
> +     mutex_unlock(&dev->lock);
> +     return ret;
> +}
> +
> +static int altera_epcq_probe_config_dt(struct platform_device *pdev,
> +                                    struct device_node *np,
> +                                    struct altera_epcq_plat_data *pdata) {
> +     struct device_node *pp = NULL;
> +     struct resource *epcq_res;
> +     int i = 0;
> +     u32 id;
> +
> +     pdata->is_epcs = of_property_read_bool(np, "is-epcs");
> +
> +     epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> +                                             "csr_base");
> +     if (!epcq_res) {

devm_ioremap_resource() will check the that 'epcq_res' is valid, so you don't need this whole 'if' block.

> +             dev_err(&pdev->dev, "resource csr base is not defined\n");
> +             return -ENODEV;
> +     }
> +
> +     pdata->csr_base = devm_ioremap_resource(&pdev->dev, epcq_res);
> +     if (IS_ERR(pdata->csr_base)) {
> +             dev_err(&pdev->dev, "%s: ERROR: failed to map csr base\n",
> +                     __func__);
> +             return PTR_ERR(pdata->csr_base);
> +     }
> +
> +     epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> +                                             "data_base");
> +     if (!epcq_res) {

Same here.

> +             dev_err(&pdev->dev, "resource data base is not defined\n");
> +             return -ENODEV;
> +     }
> +
> +     pdata->data_base = devm_ioremap_resource(&pdev->dev, epcq_res);
> +     if (IS_ERR(pdata->data_base)) {
> +             dev_err(&pdev->dev, "%s: ERROR: failed to map data base\n",
> +                     __func__);
> +             return PTR_ERR(pdata->data_base);
> +     }
> +
> +     pdata->board_flash_info = devm_kzalloc(&pdev->dev,
> +                                            sizeof(*pdata->board_flash_info),
> +                                            GFP_KERNEL);
> +
> +     /* Fill structs for each subnode (flash device) */
> +     while ((pp = of_get_next_child(np, pp))) {

for_each_available_child_of_node()?

> +             struct altera_epcq_flash_info *flash_info;
> +
> +             flash_info = &pdata->board_flash_info[i];

This is never used. Either use it below, or drop the local variable.

> +             pdata->np[i] = pp;
> +
> +             /* Read bank id from DT */
> +             if (of_get_property(pp, "reg", &id))

Is this property optional? Your DT binding doc doesn't make it clear, but it seems like a property which would be wise to require (i.e., not optional).

> +                     pdata->board_flash_info[i].bank = id;
> +             i++;
> +     }
> +     pdata->num_flashes = i;
> +     return 0;
> +}
> +
> +static int altera_epcq_setup_banks(struct platform_device *pdev,
> +                                u32 bank, struct device_node *np,
> +                                struct altera_epcq_plat_data *pdata) {
> +     struct altera_epcq *dev = platform_get_drvdata(pdev);
> +     struct mtd_part_parser_data ppdata = {};
> +     struct altera_epcq_flash_info *flash_info;
> +     struct altera_epcq_flash *flash;
> +     struct mtd_partition *parts = NULL;

Drop this variable. Just use NULL directly below.

> +     int count = 0;

Same. Just use 0 below.

> +     int flash_index;
> +     int ret = 0;
> +     uint64_t size;
> +     uint32_t sector_size;
> +
> +     flash_info = &pdata->board_flash_info[bank];
> +     if (!flash_info)
> +             return -ENODEV;
> +
> +     if (bank > pdata->num_flashes - 1)
> +             return -EINVAL;
> +
> +     flash = devm_kzalloc(&pdev->dev, sizeof(*flash), GFP_KERNEL);
> +     if (!flash)
> +             return -ENOMEM;
> +     flash->bank = bank;
> +
> +     mutex_init(&flash->lock);
> +
> +     /* verify whether flash is really present on board */
> +     flash_index = altera_epcq_probe_flash(dev, bank);
> +     if (flash_index < 0) {
> +             dev_info(&dev->pdev->dev, "epcq:flash%d not found\n",
> +                      flash->bank);
> +             return flash_index;
> +     }
> +
> +     dev->flash[bank] = flash;
> +
> +     size = flash_devices[flash_index].size_inbytes;
> +     sector_size = flash_devices[flash_index].sectorsize_inbytes;
> +     /*use do_div instead of plain div to avoid linker err*/
> +     do_div(size, sector_size);
> +     flash->num_sectors = size;
> +
> +     /*mtd framework */
> +     flash->mtd.priv = dev;
> +     flash->mtd.name = flash_devices[flash_index].name;
> +     flash->mtd.type = MTD_NORFLASH;
> +     flash->mtd.writesize = 1;
> +     flash->mtd.flags = MTD_CAP_NORFLASH;
> +     flash->mtd.size = flash_devices[flash_index].size_inbytes;
> +     flash->mtd.erasesize = flash_devices[flash_index].sectorsize_inbytes;
> +     flash->mtd._erase = altera_epcq_erase;
> +     flash->mtd._read = altera_epcq_read;
> +     flash->mtd._write = altera_epcq_write;
> +     flash->mtd._lock = altera_epcq_lock;
> +     flash->mtd._unlock = altera_epcq_unlock;
> +
> +     dev_info(&pdev->dev, "mtd .name=%s .size=0x%llx (%lluM)\n",
> +              flash->mtd.name, (long long)flash->mtd.size,
> +              (long long)(flash->mtd.size >> 20));
> +
> +     dev_info(&pdev->dev, ".erasesize = 0x%x(%uK)\n",
> +              flash->mtd.erasesize, flash->mtd.erasesize >> 10);
> +
> +     ppdata.of_node = np;
> +
> +     ret = mtd_device_parse_register(&flash->mtd, NULL, &ppdata, parts,
> +                                     count);
> +     if (ret) {
> +             dev_err(&pdev->dev, "Err MTD partition=%d\n", ret);
> +             goto err;

You don't want to unregister a MTD that failed to register. The MTD core code should handle that itself.

Instead of this if (ret) {} block, I'd just make this:

        return mtd_device_parse_register(&flash->mtd, NULL, &ppdata, NULL, 0);

> +     }
> +
> +     return 0;
> +
> +err:
> +     mtd_device_unregister(&flash->mtd);
> +     return ret;
> +}
> +
> +static int altera_epcq_probe(struct platform_device *pdev) {
> +     struct device_node *np = pdev->dev.of_node;
> +     struct altera_epcq_plat_data *pdata = NULL;
> +     struct altera_epcq *dev;
> +     int ret = 0;
> +     int i;
> +
> +     if (!np) {
> +             ret = -ENODEV;
> +             dev_err(&pdev->dev, "no device found\n");
> +             goto err;
> +     }
> +
> +     pdata = devm_kzalloc(&pdev->dev,
> +                          sizeof(struct altera_epcq_plat_data),
> +                          GFP_KERNEL);
> +
> +     if (!pdata) {
> +             ret = -ENOMEM;
> +             dev_err(&pdev->dev, "%s: ERROR: no memory\n", __func__);

Unnecessary print. The MM code will print plenty of info if you're out of memory.

> +             goto err;
> +     }
> +     ret = altera_epcq_probe_config_dt(pdev, np, pdata);
> +     if (ret) {
> +             ret = -ENODEV;
> +             dev_err(&pdev->dev, "probe fail\n");
> +             goto err;
> +     }
> +
> +     dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
> +     if (!dev) {
> +             ret = -ENOMEM;
> +             dev_err(&pdev->dev, "mem alloc fail\n");

Same here.

> +             goto err;
> +     }
> +     mutex_init(&dev->lock);
> +     dev->pdev = pdev;
> +     dev->is_epcs = pdata->is_epcs;
> +     dev->csr_base = pdata->csr_base;
> +     dev->data_base = pdata->data_base;
> +
> +     /*check number of flashes*/
> +     dev->num_flashes = pdata->num_flashes;
> +     if (dev->num_flashes > MAX_NUM_FLASH_CHIP) {
> +             dev_err(&pdev->dev, "exceeding max number of flashes\n");
> +             dev->num_flashes = MAX_NUM_FLASH_CHIP;
> +     }
> +
> +     /* check clock*/
> +     dev->clk = devm_clk_get(&pdev->dev, NULL);
> +     if (IS_ERR(dev->clk)) {
> +             ret = PTR_ERR(dev->clk);
> +             goto err;
> +     }
> +     ret = clk_prepare_enable(dev->clk);
> +     if (ret)
> +             goto err;
> +
> +     platform_set_drvdata(pdev, dev);
> +
> +     /* loop for each serial flash which is connected to epcq */
> +     for (i = 0; i < dev->num_flashes; i++) {
> +             ret = altera_epcq_setup_banks(pdev, i, pdata->np[i], pdata);
> +             if (ret) {
> +                     dev_err(&pdev->dev, "bank setup failed\n");
> +                     goto err_bank_setup;
> +             }
> +     }
> +
> +     return 0;
> +
> +err_bank_setup:
> +     clk_disable_unprepare(dev->clk);
> +err:
> +     return ret;
> +}
> +
> +static int altera_epcq_remove(struct platform_device *pdev) {
> +     struct altera_epcq *dev;
> +     struct altera_epcq_flash *flash;
> +     int ret, i;
> +
> +     dev = platform_get_drvdata(pdev);
> +
> +     /* clean up for all nor flash */
> +     for (i = 0; i < dev->num_flashes; i++) {
> +             flash = dev->flash[i];
> +             if (!flash)
> +                     continue;
> +
> +             /* clean up mtd stuff */
> +             ret = mtd_device_unregister(&flash->mtd);
> +             if (ret)
> +                     dev_err(&pdev->dev, "error removing mtd\n");
> +     }
> +
> +     clk_disable_unprepare(dev->clk);
> +
> +     return 0;
> +}
> +
> +#ifdef CONFIG_PM

Make this CONFIG_PM_SLEEP.

> +static int altera_epcq_suspend(struct device *dev) {
> +     struct altera_epcq *sdev = dev_get_drvdata(dev);
> +
> +     clk_disable_unprepare(sdev->clk);
> +
> +     return 0;
> +}
> +
> +static int altera_epcq_resume(struct device *dev) {
> +     struct altera_epcq *sdev = dev_get_drvdata(dev);
> +     int ret = -EPERM;

Drop 'ret'.

> +
> +     ret = clk_prepare_enable(sdev->clk);

Just:

        return clk_prepare_enable(sdev->clk);

> +
> +     return ret;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(altera_epcq_pm_ops, altera_epcq_suspend,
> +                      altera_epcq_resume);
> +#endif
> +
> +static const struct of_device_id altera_epcq_id_table[] = {
> +     { .compatible = "altr,epcq-1.0" },
> +     {}
> +};
> +MODULE_DEVICE_TABLE(of, altera_epcq_id_table);
> +
> +static struct platform_driver altera_epcq_driver = {
> +     .driver = {
> +             .name = ALTERA_EPCQ_RESOURCE_NAME,
> +             .bus = &platform_bus_type,
> +             .owner = THIS_MODULE,

The .owner assignment is no longer necessary.

> +             .of_match_table = altera_epcq_id_table, #ifdef CONFIG_PM

Also CONFIG_PM_SLEEP.

> +             .pm = &altera_epcq_pm_ops,
> +#endif
> +     },
> +     .probe = altera_epcq_probe,
> +     .remove = altera_epcq_remove,
> +};
> +module_platform_driver(altera_epcq_driver);
> +
> +MODULE_AUTHOR("Viet Nga Dao <vndao-EIB2kfCEclfQT0dZR+AlfA@public.gmane.org>");
> +MODULE_DESCRIPTION("MTD Altera EPCQ Driver"); MODULE_LICENSE("GPL
> +v2");
> diff --git a/drivers/mtd/devices/altera_epcq.h
> b/drivers/mtd/devices/altera_epcq.h
> new file mode 100644
> index 0000000..c3d15e5
> --- /dev/null
> +++ b/drivers/mtd/devices/altera_epcq.h
> @@ -0,0 +1,130 @@
> +/*
> + * Copyright (C) 2014 Altera Corporation. All rights reserved
> + *
> + * This program is free software; you can redistribute it and/or
> +modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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.
> + *
> + * You should have received a copy of the GNU General Public License
> +along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef __ALTERA_ECPQ_H
> +#define __ALTERA_ECPQ_H
> +
> +#define ALTERA_EPCQ_RESOURCE_NAME        "altera_epcq"
> +/* max possible slots for serial flash chip in the EPCQ controller */
> +#define MAX_NUM_FLASH_CHIP   3
> +
> +/* macro to define partitions for flash devices */
> +#define DEFINE_PARTS(n, of, s)               \

This macro is not used anywhere. Drop it?

> +{                                    \
> +     .name = n,                      \
> +     .offset = of,                   \
> +     .size = s,                      \
> +}
> +
> +struct altera_epcq_flash_info {
> +     u32 bank;
> +     struct mtd_partition *partitions;
> +     int nr_partitions;
> +};
> +
> +struct altera_epcq_plat_data {
> +     void __iomem *csr_base;
> +     void __iomem *data_base;
> +     bool is_epcs;
> +     u32 num_flashes;
> +     struct altera_epcq_flash_info *board_flash_info;
> +     struct device_node *np[MAX_NUM_FLASH_CHIP]; };
> +
> +struct altera_epcq {
> +     struct clk *clk;
> +     bool is_epcs;
> +     struct mutex lock;      /*device lock*/
> +     void __iomem *csr_base;
> +     void __iomem *data_base;
> +     struct platform_device *pdev;
> +     u32 num_flashes;
> +     struct altera_epcq_flash *flash[MAX_NUM_FLASH_CHIP]; };
> +
> +struct altera_epcq_flash {
> +     u32 bank;
> +     u32 num_sectors;
> +     u32 num_parts;
> +     struct mtd_partition *parts;
> +     struct mutex lock;      /*flash lock*/
> +     struct mtd_info mtd;
> +};
> +
> +/* Define max times to check status register before we give up. */
> +#define EPCQ_MAX_TIME_OUT                    (40 * HZ)
> +
> +/* defines for status register */
> +#define EPCQ_SR_REG                          0x0
> +#define EPCQ_SR_WIP_MASK                     0x00000001
> +#define EPCQ_SR_WIP                          0x1
> +#define EPCQ_SR_WEL                          0x2
> +#define EPCQ_SR_BP0                          0x4
> +#define EPCQ_SR_BP1                          0x8
> +#define EPCQ_SR_BP2                          0x10
> +#define EPCQ_SR_BP3                          0x40
> +#define EPCQ_SR_TB                           0x20
> +
> +/* defines for device id register */
> +#define EPCQ_SID_REG                         0x4
> +#define EPCQ_RDID_REG                                0x8
> +#define EPCQ_RDID_MASK                               0x000000FF
> +/*
> + * EPCQ_MEM_OP register offset
> + *
> + * The EPCQ_MEM_OP register is used to do memory protect and erase
> +operations
> + *
> + */
> +#define EPCQ_MEM_OP_REG                              0xC
> +
> +#define EPCQ_MEM_OP_CMD_MASK                 0x00000003
> +#define EPCQ_MEM_OP_BULK_ERASE_CMD           0x00000001
> +#define EPCQ_MEM_OP_SECTOR_ERASE_CMD         0x00000002
> +#define EPCQ_MEM_OP_SECTOR_PROTECT_CMD               0x00000003
> +#define EPCQ_MEM_OP_SECTOR_VALUE_MASK                0x0003FF00
> +#define EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK        0x00001F00
> +#define EPCQ_MEM_OP_SECTOR_PROTECT_SHIFT     8
> +/*
> + * EPCQ_ISR register offset
> + *
> + * The EPCQ_ISR register is used to determine whether an invalid
> +write or erase
> + * operation trigerred an interrupt
> + *
> + */
> +#define EPCQ_ISR_REG                         0x10
> +
> +#define EPCQ_ISR_ILLEGAL_ERASE_MASK          0x00000001
> +#define EPCQ_ISR_ILLEGAL_WRITE_MASK          0x00000002
> +
> +/*
> + * EPCQ_IMR register offset
> + *
> + * The EPCQ_IMR register is used to mask the invalid erase or the
> +invalid write
> + * interrupts.
> + *
> + */
> +#define EPCQ_IMR_REG                         0x14
> +#define EPCQ_IMR_ILLEGAL_ERASE_MASK          0x00000001
> +
> +#define EPCQ_IMR_ILLEGAL_WRITE_MASK          0x00000002
> +
> +#define EPCQ_CHIP_SELECT_REG                 0x18
> +#define EPCQ_CHIP_SELECT_MASK                        0x00000007
> +#define EPCQ_CHIP_SELECT_0                   0x00000001
> +#define EPCQ_CHIP_SELECT_1                   0x00000002
> +#define EPCQ_CHIP_SELECT_2                   0x00000004
> +
> +#endif /* __ALTERA_ECPQ_H */

Brian

________________________________

Confidentiality Notice.
This message may contain information that is confidential or otherwise protected from disclosure. If you are not the intended recipient, you are hereby notified that any use, disclosure, dissemination, distribution, or copying of this message, or any attachments, is strictly prohibited. If you have received this message in error, please advise the sender by reply e-mail, and delete the message and any attachments. Thank you.
--
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	[flat|nested] 39+ messages in thread

* RE: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-01-15  3:05     ` Viet Nga Dao
  0 siblings, 0 replies; 39+ messages in thread
From: Viet Nga Dao @ 2015-01-15  3:05 UTC (permalink / raw)
  To: Brian Norris; +Cc: linux-mtd, ngachi86, dwmw2, linux-kernel, devicetree

Hi Brian,
For Altera EPCQ flashes, almost operations are performed underline hardware. Software only able to perform the following through registers:
 -  read status register
 -  read id
 -  write status registers bit SR_BP0,SR_BP1, SR_BP2,SR_BP3, SR_TB  (http://www.altera.com.my/literature/hb/cfg/cfg_cf52012.pdf)
For read/write data: all the operations like QUAD_READ/WRITE, FAST_READ/WRITE are handled by hardware as well. From software point of view, there is no difference between these 2 modes.

That is why if rewrite the drivers to follow spi-nor structure, it will require extra decoding works for the only few used opcodes.

Thanks,
Viet Nga



-----Original Message-----
From: Brian Norris [mailto:computersforpeace@gmail.com]
Sent: Tuesday, January 13, 2015 11:33 AM
To: Viet Nga Dao
Cc: dwmw2@infradead.org; linux-mtd@lists.infradead.org; linux-kernel@vger.kernel.org; devicetree@vger.kernel.org; ngachi86@gmail.com
Subject: Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver

On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao@altera.com wrote:
> From: Viet Nga Dao <vndao@altera.com>
>
> Altera EPCQ Controller is a soft IP which enables access to Altera
> EPCQ and EPCS flash chips. This patch adds driver for these devices.
>
> Signed-off-by: Viet Nga Dao <vndao@altera.com>

This drivers seems awfully similar to (and so I infer it is likely copy-and-pasted from) m25p80.c / spi-nor.c. Do you think it can be rewritten as a SPI NOR driver, under drivers/mtd/spi-nor/ ? It looks like these flash share most (all?) the same basic opcodes.

> ---
>  .../devicetree/bindings/mtd/altera_epcq.txt        |   45 ++
>  drivers/mtd/devices/Kconfig                        |   12 +
>  drivers/mtd/devices/Makefile                       |    2 +-
>  drivers/mtd/devices/altera_epcq.c                  |  804 ++++++++++++++++++++
>  drivers/mtd/devices/altera_epcq.h                  |  130 ++++
>  5 files changed, 992 insertions(+), 1 deletions(-)  create mode
> 100644 Documentation/devicetree/bindings/mtd/altera_epcq.txt
>  create mode 100644 drivers/mtd/devices/altera_epcq.c  create mode
> 100644 drivers/mtd/devices/altera_epcq.h
>
> diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt
> b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
> new file mode 100644
> index 0000000..d14f50e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
> @@ -0,0 +1,45 @@
> +* MTD Altera EPCQ driver
> +
> +Required properties:
> +- compatible: Should be "altr,epcq-1.0"
> +- reg: Address and length of the register set  for the device. It
> +contains
> +  the information of registers in the same order as described by
> +reg-names
> +- reg-names: Should contain the reg names
> +  "csr_base": Should contain the register configuration base address
> +  "data_base": Should contain the data base address
> +- is-epcs: boolean type.
> +             If present, the device contains EPCS flashes.
> +             Otherwise, it contains EPCQ flashes.
> +- #address-cells: Must be <1>.
> +- #size-cells: Must be <0>.
> +- flash device tree subnode, there must be a node with the following fields:

These subnodes definitely require a 'compatible' string. Perhaps they should be used to differentiate EPCS vs. EPCQ. Does "is-epcs" really need to be in the top-level controller node?

> +     - reg: Should contain the flash id

Should, or must? (This question is relevant, because you seem to make it optional in your code.) And what does the "flash ID" mean? It seems like you're using as a chip-select or bank index.

> +     - #address-cells: please refer to /mtd/partition.txt
> +     - #size-cells: please refer to /mtd/partition.txt
> +     For partitions inside each flash, please refer to /mtd/partition.txt
> +
> +Example:
> +
> +                     epcq_controller_0: epcq@0x000000000 {
> +                             compatible = "altr,epcq-1.0";
> +                             reg = <0x00000001 0x00000000 0x00000020>,
> +                                     <0x00000000 0x00000000 0x02000000>;
> +                             reg-names = "csr_base", "data_base";
> +                             #address-cells = <1>;
> +                             #size-cells = <0>;
> +                             flash0: epcq256@0 {
> +                                     reg = <0>;
> +                                     #address-cells = <1>;
> +                                     #size-cells = <1>;
> +                                     partition@0 {
> +                                             /* 16 MB for raw data. */
> +                                             label = "EPCQ Flash 0 raw data";
> +                                             reg = <0x0 0x1000000>;
> +                                     };
> +                                     partition@1000000 {
> +                                             /* 16 MB for jffs2 data. */
> +                                             label = "EPCQ Flash 0 JFFS 2";
> +                                             reg = <0x1000000 0x1000000>;
> +                                     };
> +                             };
> +                     }; //end epcq@0x000000000 (epcq_controller_0)
> diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
> index c49d0b1..020b864 100644
> --- a/drivers/mtd/devices/Kconfig
> +++ b/drivers/mtd/devices/Kconfig
> @@ -218,6 +218,18 @@ config MTD_ST_SPI_FSM
>         SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
>         for a subset of connected Serial Flash devices.
>
> +config MTD_ALTERA_EPCQ
> +     tristate "Support Altera EPCQ/EPCS Flash chips"
> +     depends on OF
> +     help
> +       This enables access to Altera EPCQ/EPCS flash chips, used for data
> +       storage. See the driver source for the current list,
> +       or to add other chips.
> +
> +       If you want to compile this driver as a module ( = code which can be
> +       inserted in and removed from the running kernel whenever you want),
> +       say M here and read <file:Documentation/kbuild/modules.txt>.
> +
>  if MTD_DOCG3
>  config BCH_CONST_M
>       default 14
> diff --git a/drivers/mtd/devices/Makefile
> b/drivers/mtd/devices/Makefile index f0b0e61..b429c4d 100644
> --- a/drivers/mtd/devices/Makefile
> +++ b/drivers/mtd/devices/Makefile
> @@ -16,6 +16,6 @@ obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
>  obj-$(CONFIG_MTD_SST25L)     += sst25l.o
>  obj-$(CONFIG_MTD_BCM47XXSFLASH)      += bcm47xxsflash.o
>  obj-$(CONFIG_MTD_ST_SPI_FSM)    += st_spi_fsm.o
> -
> +obj-$(CONFIG_MTD_ALTERA_EPCQ)                += altera_epcq.o
>
>  CFLAGS_docg3.o                       += -I$(src)
> diff --git a/drivers/mtd/devices/altera_epcq.c
> b/drivers/mtd/devices/altera_epcq.c
> new file mode 100644
> index 0000000..09213d5
> --- /dev/null
> +++ b/drivers/mtd/devices/altera_epcq.c
> @@ -0,0 +1,804 @@
> +/*
> + * Copyright (C) 2014 Altera Corporation. All rights reserved
> + *
> + * This program is free software; you can redistribute it and/or
> +modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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.
> + *
> + * You should have received a copy of the GNU General Public License
> +along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/errno.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/ioport.h>
> +#include <linux/jiffies.h>
> +#include <linux/kernel.h>
> +#include <linux/log2.h>
> +#include <linux/module.h>
> +#include <linux/mtd/mtd.h>
> +#include <linux/mtd/partitions.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/param.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/wait.h>
> +
> +#include "altera_epcq.h"
> +
> +/* data structure to maintain flash ids from different vendors */
> +struct flash_device {
> +     char *name;
> +     bool is_epcs;
> +     u32 device_id;
> +     uint32_t sectorsize_inbytes;
> +     uint64_t size_inbytes;
> +     u32 pagesize;
> +};
> +
> +#define FLASH_ID(_n, _is_epcs, _id, _ssize, _size, _psize)   \
> +{                            \
> +     .name = (_n),           \
> +     .is_epcs = (_is_epcs),          \
> +     .device_id = (_id),     \
> +     .sectorsize_inbytes = (_ssize), \
> +     .size_inbytes = (_size),        \
> +     .pagesize = (_psize),   \
> +}
> +
> +static struct flash_device flash_devices[] = {
> +     FLASH_ID("epcq16"    , 0, 0x15, 0x10000, 0x200000, 0x100),
> +     FLASH_ID("epcq32"    , 0, 0x16, 0x10000, 0x400000, 0x100),
> +     FLASH_ID("epcq64"    , 0, 0x17, 0x10000, 0x800000, 0x100),
> +     FLASH_ID("epcq128"   , 0, 0x18, 0x10000, 0x1000000, 0x100),
> +     FLASH_ID("epcq256"   , 0, 0x19, 0x10000, 0x2000000, 0x100),
> +     FLASH_ID("epcq512"   , 0, 0x20, 0x10000, 0x4000000, 0x100),
> +
> +     FLASH_ID("epcs16"    , 1, 0x14, 0x10000, 0x200000, 0x100),
> +     FLASH_ID("epcs64"    , 1, 0x16, 0x10000, 0x800000, 0x100),
> +     FLASH_ID("epcs128"   , 1, 0x18, 0x40000, 0x1000000, 0x100),
> +};
> +
> +static inline struct altera_epcq_flash *get_flash_data(struct
> +mtd_info *mtd) {
> +     return container_of(mtd, struct altera_epcq_flash, mtd); }
> +
> +static u32 altera_epcq_read_sr(struct altera_epcq *dev) {
> +     return readl(dev->csr_base + EPCQ_SR_REG); }
> +
> +static int altera_epcq_wait_till_ready(struct altera_epcq *dev) {
> +     unsigned long finish;
> +     int status;
> +
> +     finish = jiffies + EPCQ_MAX_TIME_OUT;
> +     do {
> +             status = altera_epcq_read_sr(dev);
> +             if (status < 0)
> +                     return status;
> +             else if (!(status & EPCQ_SR_WIP))
> +                     return 0;
> +
> +             cond_resched();
> +     } while (!time_after_eq(jiffies, finish));
> +
> +     dev_err(&dev->pdev->dev, "epcq controller is busy, timeout\n");
> +     return -EBUSY;
> +}
> +
> +static int get_flash_index(u32 flash_id, bool is_epcs) {
> +     int index;
> +
> +     for (index = 0; index < ARRAY_SIZE(flash_devices); index++) {
> +             if (flash_devices[index].device_id == flash_id &&
> +                 flash_devices[index].is_epcs == is_epcs)
> +                     return index;
> +     }
> +
> +     /* Memory chip is not listed and not supported */
> +     return -ENODEV;
> +}
> +
> +static int altera_epcq_write_erase_check(struct altera_epcq *dev,
> +                                      bool write_erase)
> +{
> +     u32 val;
> +     u32 mask;
> +
> +     if (write_erase)
> +             mask = EPCQ_ISR_ILLEGAL_WRITE_MASK;
> +     else
> +             mask = EPCQ_ISR_ILLEGAL_ERASE_MASK;
> +
> +     val = readl(dev->csr_base + EPCQ_ISR_REG);
> +     if (val & mask) {
> +             dev_err(&dev->pdev->dev,
> +                     "write/erase failed, sector might be protected\n");
> +             /*clear this status for next use*/
> +             writel(val, dev->csr_base + EPCQ_ISR_REG);
> +             return -EIO;
> +     }
> +     return 0;
> +}
> +
> +static int altera_epcq_erase_chip(struct mtd_info *mtd) {
> +     int ret;
> +     u32 val;
> +     struct altera_epcq *dev = mtd->priv;
> +
> +     /* Wait until finished previous write command. */
> +     ret = altera_epcq_wait_till_ready(dev);

This pattern is pretty silly. We don't need to have every operation check the status of the previous operation. Each operation should check itself. You obviously copied this one... but we recently changed that:

    commit dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3
    Author: Brian Norris <computersforpeace@gmail.com>
    Date:   Wed Aug 6 18:16:57 2014 -0700

        mtd: spi-nor: move "wait-till-ready" checks into erase/write
        functions


    https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3

> +     if (ret)
> +             return ret;
> +
> +     /* erase chip. */
> +     val = EPCQ_MEM_OP_BULK_ERASE_CMD;
> +     writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
> +
> +     /* Wait until finished previous write command. */
> +     ret = altera_epcq_wait_till_ready(dev);
> +     if (ret)
> +             return ret;
> +
> +      /* check whether write triggered a illegal write interrupt */
> +     ret = altera_epcq_write_erase_check(dev, 0);
> +     if (ret < 0)
> +             return ret;
> +
> +     return 0;
> +}
> +
> +static int altera_epcq_addr_to_sector(struct altera_epcq_flash *flash,
> +                                   int addr_offset)
> +{
> +     int sector = 0;
> +     int i;
> +
> +     for (i = 0; i < flash->num_sectors; i++) {
> +             if (addr_offset >= i * flash->mtd.erasesize && addr_offset <
> +                 (i * flash->mtd.erasesize + flash->mtd.erasesize)) {
> +                     sector = i;
> +                     return sector;
> +             }
> +     }

Uh, really? Am I crazy, or shouldn't this whole function just be a really simple arithmetic operation? Like:

        return addr_offset >> mtd->erasesize_shift;

> +     return -1;
> +}
> +
> +static int altera_epcq_erase_sector(struct mtd_info *mtd,
> +                                 int addr_offset)
> +{
> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
> +     struct altera_epcq *dev = mtd->priv;
> +     u32 val;
> +     int ret;
> +     int sector_value;
> +
> +     ret = altera_epcq_wait_till_ready(dev);
> +     if (ret)
> +             return ret;
> +
> +     sector_value = altera_epcq_addr_to_sector(flash, addr_offset);
> +
> +     /* sanity check that block_offset is a valid sector number */
> +     if (sector_value < 0)
> +             return -EINVAL;
> +
> +     /* sector value should occupy bits 17:8 */
> +     val = (sector_value << 8) & EPCQ_MEM_OP_SECTOR_VALUE_MASK;
> +
> +     /* sector erase commands occupies lower 2 bits */
> +     val |= EPCQ_MEM_OP_SECTOR_ERASE_CMD;
> +
> +     /* write sector erase command to EPCQ_MEM_OP register*/
> +     writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
> +
> +     ret = altera_epcq_wait_till_ready(dev);
> +     if (ret)
> +             return ret;
> +
> +      /* check whether write triggered a illegal write interrupt */

^^ extra space here? You have <TAB><SPACE>/* check [...]

> +     ret = altera_epcq_write_erase_check(dev, 0);
> +     if (ret < 0)
> +             return ret;
> +
> +     return 0;
> +}
> +
> +static int altera_epcq_erase(struct mtd_info *mtd, struct erase_info
> +*e_info) {
> +     u32 addr;
> +     int ret;
> +     u32 len;
> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
> +     struct altera_epcq *dev = mtd->priv;
> +
> +     addr = e_info->addr;
> +     len = e_info->len;
> +
> +     if (flash->bank > dev->num_flashes - 1) {
> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
> +             return -EINVAL;
> +     }
> +     mutex_lock(&flash->lock);
> +
> +     /*erase whole chip*/
> +     if (len == mtd->size) {
> +             if (altera_epcq_erase_chip(mtd)) {
> +                     e_info->state = MTD_ERASE_FAILED;
> +                     mutex_unlock(&flash->lock);
> +                     return -EIO;
> +             }
> +     /*"sector"-at-a-time erase*/
> +     } else {
> +             while (len) {
> +                     ret = altera_epcq_erase_sector(mtd, addr);
> +                     if (ret) {
> +                             e_info->state = MTD_ERASE_FAILED;
> +                             mutex_unlock(&flash->lock);
> +                             return -EIO;
> +                     }
> +                     addr += mtd->erasesize;
> +                     if (len < mtd->erasesize)
> +                             len = 0;
> +                     else
> +                             len -= mtd->erasesize;
> +             }
> +     }
> +     mutex_unlock(&flash->lock);
> +     e_info->state = MTD_ERASE_DONE;
> +     mtd_erase_callback(e_info);
> +
> +     return 0;
> +}
> +
> +static int altera_epcq_read(struct mtd_info *mtd, loff_t from, size_t len,
> +                         size_t *retlen, u8 *buf)
> +{
> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
> +     struct altera_epcq *dev = mtd->priv;
> +     void *src;
> +     int ret = 0;
> +
> +     if (!flash || !dev)
> +             return -ENODEV;

Why would either of these be 0? If they are, you have much bigger problems, since you aren't checking these almost anywhere else. And the !flash check is pretty odd, since get_flash_data() is using container_of(), which is not likely to give you exactly 0...

> +
> +     if (flash->bank > dev->num_flashes - 1) {
> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
> +             return -EINVAL;
> +     }
> +
> +     src = dev->data_base + from;
> +
> +     mutex_lock(&flash->lock);
> +     /* wait till previous write/erase is done. */
> +     ret = altera_epcq_wait_till_ready(dev);
> +     if (ret)
> +             goto err;
> +
> +     memcpy_fromio(buf, (u8 *)src, len);

Drop the cast. Also, you get sparse warnings because you're dropping the __iomem. Why not just this?

        memcpy_fromio(buf, dev->data_base + from, len);

(BTW, you might consider running sparse on all your code. See Documentation/sparse.txt. It tells you how to get it, but you can often just download it through your distro's package manager.)

> +     *retlen = len;
> +
> +err:
> +     mutex_unlock(&flash->lock);
> +     return ret;
> +}
> +
> +static int altera_epcq_write(struct mtd_info *mtd, loff_t to, size_t len,
> +                          size_t *retlen, const u8 *buf) {
> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
> +     struct altera_epcq *dev = mtd->priv;
> +     void *dest;
> +     int ret = 0;
> +
> +     if (!flash || !dev)
> +             return -ENODEV;

Also here. I don't think you need these checks.

> +
> +     if (flash->bank > dev->num_flashes - 1) {
> +             dev_err(&dev->pdev->dev, "Invalid chip id\n");
> +             return -EINVAL;
> +     }
> +     dest = dev->data_base + to;
> +
> +     mutex_lock(&flash->lock);
> +
> +     /* wait until finished previous write command. */
> +     ret = altera_epcq_wait_till_ready(dev);
> +     if (ret)
> +             goto err;
> +
> +     memcpy_toio(dest, buf, len);

Similar. Why not just this?

        memcpy_toio(dev->data_base + to, buf, len);

> +     *retlen += len;
> +
> +     /* wait until finished previous write command. */
> +     ret = altera_epcq_wait_till_ready(dev);
> +     if (ret)
> +             goto err;
> +
> +      /* check whether write triggered a illegal write interrupt */
> +     ret = altera_epcq_write_erase_check(dev, 1);
> +     if (ret < 0)
> +             goto err;
> +
> +err:
> +     mutex_unlock(&flash->lock);
> +     return ret;
> +}
> +
> +static int altera_epcq_lock(struct mtd_info *mtd, loff_t ofs,
> +uint64_t len) {
> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
> +     struct altera_epcq *dev = mtd->priv;
> +     uint32_t offset = ofs;
> +     int ret = 0;
> +     u32 sector_start, sector_end;
> +     u32 num_sectors;
> +     u32 mem_op;
> +     unsigned sr_bp = 0;
> +     unsigned sr_tb = 0;
> +
> +     sector_start = altera_epcq_addr_to_sector(flash, offset);
> +     sector_end = altera_epcq_addr_to_sector(flash, offset + len);
> +     num_sectors = flash->num_sectors;
> +     dev_dbg(&dev->pdev->dev,
> +             "%s: num_setor is %u,sector start is %u,sector end is %u\n",
> +             __func__, num_sectors, sector_start, sector_end);
> +
> +     mutex_lock(&flash->lock);
> +     /* wait until finished previous write command. */
> +     ret = altera_epcq_wait_till_ready(dev);
> +     if (ret)
> +             goto err;
> +
> +     if (sector_start >= num_sectors/2) {

It's preferable to have spaces around operators. So 'num_sectors / 2'.
The same thing comes up in several other places.

> +             if (sector_start < num_sectors-(num_sectors / 4))
> +                     sr_bp = __ilog2_u32(num_sectors);
> +             else if (sector_start < num_sectors-(num_sectors / 8))
> +                     sr_bp = __ilog2_u32(num_sectors) - 1;
> +             else if (sector_start < num_sectors-(num_sectors / 16))
> +                     sr_bp = __ilog2_u32(num_sectors) - 2;
> +             else if (sector_start < num_sectors-(num_sectors / 32))
> +                     sr_bp = __ilog2_u32(num_sectors) - 3;
> +             else if (sector_start < num_sectors-(num_sectors / 64))
> +                     sr_bp = __ilog2_u32(num_sectors) - 4;
> +             else if (sector_start < num_sectors-(num_sectors / 128))
> +                     sr_bp = __ilog2_u32(num_sectors) - 5;
> +             else if (sector_start < num_sectors-(num_sectors / 256))
> +                     sr_bp = __ilog2_u32(num_sectors) - 6;
> +             else if (sector_start < num_sectors-(num_sectors / 512))
> +                     sr_bp = __ilog2_u32(num_sectors) - 7;
> +             else if (sector_start < num_sectors-(num_sectors / 1024))
> +                     sr_bp = __ilog2_u32(num_sectors) - 8;
> +             else
> +                     sr_bp = 0;  /* non area protected */

Yikes, that's ugly! And I'm not sure it matches the EPCQ doc I found.
I'm pretty sure you can rewrite this if/else-if/else block in about 1 line though.

> +
> +             if (sr_bp < 0) {

sr_bp is unsigned, so this is never true.

> +                     dev_err(&dev->pdev->dev, "%s: address is out of range\n"
> +                             , __func__);
> +                     ret = -EINVAL;
> +                     goto err;
> +             }
> +             /*set TB = 0*/
> +             sr_tb = 0;
> +
> +     } else {
> +             if (sector_end < 1)
> +                     sr_bp = 1;
> +             else if (sector_end < 2)
> +                     sr_bp = 2;
> +             else if (sector_end < 4)
> +                     sr_bp = 3;
> +             else if (sector_end < 8)
> +                     sr_bp = 4;
> +             else if (sector_end < 16)
> +                     sr_bp = 5;
> +             else if (sector_end < 32)
> +                     sr_bp = 6;
> +             else if (sector_end < 64)
> +                     sr_bp = 7;
> +             else if (sector_end < 128)
> +                     sr_bp = 8;
> +             else if (sector_end < 256)
> +                     sr_bp = 9;
> +             else if (sector_end < 512)
> +                     sr_bp = 10;
> +             else
> +                     sr_bp = 16; /*protect all areas*/

Again, this is ugly. How about this?

        sr_bp = fls(sector_end) + 1;

> +
> +             sr_tb = 1;
> +     }
> +
> +     mem_op = (sr_tb << 12) | (sr_bp << 8);
> +     mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
> +     mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
> +     writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
> +
> +err:
> +     mutex_unlock(&flash->lock);
> +     return ret;
> +}
> +
> +static int altera_epcq_unlock(struct mtd_info *mtd, loff_t ofs,
> +uint64_t len) {
> +     struct altera_epcq_flash *flash = get_flash_data(mtd);
> +     struct altera_epcq *dev = mtd->priv;
> +
> +     int ret = 0;
> +     u32 mem_op;
> +
> +     mutex_lock(&flash->lock);
> +     /* wait until finished previous write command. */
> +     ret = altera_epcq_wait_till_ready(dev);
> +     if (ret)
> +             goto err;
> +     dev_info(&dev->pdev->dev, "Unlock all protected area\n");
> +     mem_op = 0;
> +     mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
> +     mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
> +     writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
> +
> +err:
> +     mutex_unlock(&flash->lock);
> +     return ret;
> +}
> +
> +static void altera_epcq_chip_select(struct altera_epcq *dev, u32
> +bank) {
> +     u32 val = 0;
> +
> +     switch (bank) {
> +     case 0:
> +             val = EPCQ_CHIP_SELECT_0;
> +             break;
> +     case 1:
> +             val = EPCQ_CHIP_SELECT_1;
> +             break;
> +     case 2:
> +             val = EPCQ_CHIP_SELECT_2;
> +             break;
> +     default:
> +             return;
> +     }
> +
> +     writel(val, dev->csr_base + EPCQ_CHIP_SELECT_REG); }
> +
> +static int altera_epcq_probe_flash(struct altera_epcq *dev, u32 bank)
> +{
> +     int ret = 0;
> +     u32 val = 0;
> +
> +     mutex_lock(&dev->lock);
> +
> +     /*select bank*/

Comment spacing, here and elsewhere:

        /* select bank */

> +     altera_epcq_chip_select(dev, bank);
> +
> +     ret = altera_epcq_wait_till_ready(dev);
> +     if (ret)
> +             goto err;
> +
> +     /* get device sillicon id */
> +     if (dev->is_epcs)
> +             val = readl(dev->csr_base + EPCQ_SID_REG);
> +     else
> +             val = readl(dev->csr_base + EPCQ_RDID_REG);
> +
> +     /* get flash index based on the device list*/
> +     ret = get_flash_index(val, dev->is_epcs);
> +     return 0;
> +err:
> +     mutex_unlock(&dev->lock);
> +     return ret;
> +}
> +
> +static int altera_epcq_probe_config_dt(struct platform_device *pdev,
> +                                    struct device_node *np,
> +                                    struct altera_epcq_plat_data *pdata) {
> +     struct device_node *pp = NULL;
> +     struct resource *epcq_res;
> +     int i = 0;
> +     u32 id;
> +
> +     pdata->is_epcs = of_property_read_bool(np, "is-epcs");
> +
> +     epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> +                                             "csr_base");
> +     if (!epcq_res) {

devm_ioremap_resource() will check the that 'epcq_res' is valid, so you don't need this whole 'if' block.

> +             dev_err(&pdev->dev, "resource csr base is not defined\n");
> +             return -ENODEV;
> +     }
> +
> +     pdata->csr_base = devm_ioremap_resource(&pdev->dev, epcq_res);
> +     if (IS_ERR(pdata->csr_base)) {
> +             dev_err(&pdev->dev, "%s: ERROR: failed to map csr base\n",
> +                     __func__);
> +             return PTR_ERR(pdata->csr_base);
> +     }
> +
> +     epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> +                                             "data_base");
> +     if (!epcq_res) {

Same here.

> +             dev_err(&pdev->dev, "resource data base is not defined\n");
> +             return -ENODEV;
> +     }
> +
> +     pdata->data_base = devm_ioremap_resource(&pdev->dev, epcq_res);
> +     if (IS_ERR(pdata->data_base)) {
> +             dev_err(&pdev->dev, "%s: ERROR: failed to map data base\n",
> +                     __func__);
> +             return PTR_ERR(pdata->data_base);
> +     }
> +
> +     pdata->board_flash_info = devm_kzalloc(&pdev->dev,
> +                                            sizeof(*pdata->board_flash_info),
> +                                            GFP_KERNEL);
> +
> +     /* Fill structs for each subnode (flash device) */
> +     while ((pp = of_get_next_child(np, pp))) {

for_each_available_child_of_node()?

> +             struct altera_epcq_flash_info *flash_info;
> +
> +             flash_info = &pdata->board_flash_info[i];

This is never used. Either use it below, or drop the local variable.

> +             pdata->np[i] = pp;
> +
> +             /* Read bank id from DT */
> +             if (of_get_property(pp, "reg", &id))

Is this property optional? Your DT binding doc doesn't make it clear, but it seems like a property which would be wise to require (i.e., not optional).

> +                     pdata->board_flash_info[i].bank = id;
> +             i++;
> +     }
> +     pdata->num_flashes = i;
> +     return 0;
> +}
> +
> +static int altera_epcq_setup_banks(struct platform_device *pdev,
> +                                u32 bank, struct device_node *np,
> +                                struct altera_epcq_plat_data *pdata) {
> +     struct altera_epcq *dev = platform_get_drvdata(pdev);
> +     struct mtd_part_parser_data ppdata = {};
> +     struct altera_epcq_flash_info *flash_info;
> +     struct altera_epcq_flash *flash;
> +     struct mtd_partition *parts = NULL;

Drop this variable. Just use NULL directly below.

> +     int count = 0;

Same. Just use 0 below.

> +     int flash_index;
> +     int ret = 0;
> +     uint64_t size;
> +     uint32_t sector_size;
> +
> +     flash_info = &pdata->board_flash_info[bank];
> +     if (!flash_info)
> +             return -ENODEV;
> +
> +     if (bank > pdata->num_flashes - 1)
> +             return -EINVAL;
> +
> +     flash = devm_kzalloc(&pdev->dev, sizeof(*flash), GFP_KERNEL);
> +     if (!flash)
> +             return -ENOMEM;
> +     flash->bank = bank;
> +
> +     mutex_init(&flash->lock);
> +
> +     /* verify whether flash is really present on board */
> +     flash_index = altera_epcq_probe_flash(dev, bank);
> +     if (flash_index < 0) {
> +             dev_info(&dev->pdev->dev, "epcq:flash%d not found\n",
> +                      flash->bank);
> +             return flash_index;
> +     }
> +
> +     dev->flash[bank] = flash;
> +
> +     size = flash_devices[flash_index].size_inbytes;
> +     sector_size = flash_devices[flash_index].sectorsize_inbytes;
> +     /*use do_div instead of plain div to avoid linker err*/
> +     do_div(size, sector_size);
> +     flash->num_sectors = size;
> +
> +     /*mtd framework */
> +     flash->mtd.priv = dev;
> +     flash->mtd.name = flash_devices[flash_index].name;
> +     flash->mtd.type = MTD_NORFLASH;
> +     flash->mtd.writesize = 1;
> +     flash->mtd.flags = MTD_CAP_NORFLASH;
> +     flash->mtd.size = flash_devices[flash_index].size_inbytes;
> +     flash->mtd.erasesize = flash_devices[flash_index].sectorsize_inbytes;
> +     flash->mtd._erase = altera_epcq_erase;
> +     flash->mtd._read = altera_epcq_read;
> +     flash->mtd._write = altera_epcq_write;
> +     flash->mtd._lock = altera_epcq_lock;
> +     flash->mtd._unlock = altera_epcq_unlock;
> +
> +     dev_info(&pdev->dev, "mtd .name=%s .size=0x%llx (%lluM)\n",
> +              flash->mtd.name, (long long)flash->mtd.size,
> +              (long long)(flash->mtd.size >> 20));
> +
> +     dev_info(&pdev->dev, ".erasesize = 0x%x(%uK)\n",
> +              flash->mtd.erasesize, flash->mtd.erasesize >> 10);
> +
> +     ppdata.of_node = np;
> +
> +     ret = mtd_device_parse_register(&flash->mtd, NULL, &ppdata, parts,
> +                                     count);
> +     if (ret) {
> +             dev_err(&pdev->dev, "Err MTD partition=%d\n", ret);
> +             goto err;

You don't want to unregister a MTD that failed to register. The MTD core code should handle that itself.

Instead of this if (ret) {} block, I'd just make this:

        return mtd_device_parse_register(&flash->mtd, NULL, &ppdata, NULL, 0);

> +     }
> +
> +     return 0;
> +
> +err:
> +     mtd_device_unregister(&flash->mtd);
> +     return ret;
> +}
> +
> +static int altera_epcq_probe(struct platform_device *pdev) {
> +     struct device_node *np = pdev->dev.of_node;
> +     struct altera_epcq_plat_data *pdata = NULL;
> +     struct altera_epcq *dev;
> +     int ret = 0;
> +     int i;
> +
> +     if (!np) {
> +             ret = -ENODEV;
> +             dev_err(&pdev->dev, "no device found\n");
> +             goto err;
> +     }
> +
> +     pdata = devm_kzalloc(&pdev->dev,
> +                          sizeof(struct altera_epcq_plat_data),
> +                          GFP_KERNEL);
> +
> +     if (!pdata) {
> +             ret = -ENOMEM;
> +             dev_err(&pdev->dev, "%s: ERROR: no memory\n", __func__);

Unnecessary print. The MM code will print plenty of info if you're out of memory.

> +             goto err;
> +     }
> +     ret = altera_epcq_probe_config_dt(pdev, np, pdata);
> +     if (ret) {
> +             ret = -ENODEV;
> +             dev_err(&pdev->dev, "probe fail\n");
> +             goto err;
> +     }
> +
> +     dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
> +     if (!dev) {
> +             ret = -ENOMEM;
> +             dev_err(&pdev->dev, "mem alloc fail\n");

Same here.

> +             goto err;
> +     }
> +     mutex_init(&dev->lock);
> +     dev->pdev = pdev;
> +     dev->is_epcs = pdata->is_epcs;
> +     dev->csr_base = pdata->csr_base;
> +     dev->data_base = pdata->data_base;
> +
> +     /*check number of flashes*/
> +     dev->num_flashes = pdata->num_flashes;
> +     if (dev->num_flashes > MAX_NUM_FLASH_CHIP) {
> +             dev_err(&pdev->dev, "exceeding max number of flashes\n");
> +             dev->num_flashes = MAX_NUM_FLASH_CHIP;
> +     }
> +
> +     /* check clock*/
> +     dev->clk = devm_clk_get(&pdev->dev, NULL);
> +     if (IS_ERR(dev->clk)) {
> +             ret = PTR_ERR(dev->clk);
> +             goto err;
> +     }
> +     ret = clk_prepare_enable(dev->clk);
> +     if (ret)
> +             goto err;
> +
> +     platform_set_drvdata(pdev, dev);
> +
> +     /* loop for each serial flash which is connected to epcq */
> +     for (i = 0; i < dev->num_flashes; i++) {
> +             ret = altera_epcq_setup_banks(pdev, i, pdata->np[i], pdata);
> +             if (ret) {
> +                     dev_err(&pdev->dev, "bank setup failed\n");
> +                     goto err_bank_setup;
> +             }
> +     }
> +
> +     return 0;
> +
> +err_bank_setup:
> +     clk_disable_unprepare(dev->clk);
> +err:
> +     return ret;
> +}
> +
> +static int altera_epcq_remove(struct platform_device *pdev) {
> +     struct altera_epcq *dev;
> +     struct altera_epcq_flash *flash;
> +     int ret, i;
> +
> +     dev = platform_get_drvdata(pdev);
> +
> +     /* clean up for all nor flash */
> +     for (i = 0; i < dev->num_flashes; i++) {
> +             flash = dev->flash[i];
> +             if (!flash)
> +                     continue;
> +
> +             /* clean up mtd stuff */
> +             ret = mtd_device_unregister(&flash->mtd);
> +             if (ret)
> +                     dev_err(&pdev->dev, "error removing mtd\n");
> +     }
> +
> +     clk_disable_unprepare(dev->clk);
> +
> +     return 0;
> +}
> +
> +#ifdef CONFIG_PM

Make this CONFIG_PM_SLEEP.

> +static int altera_epcq_suspend(struct device *dev) {
> +     struct altera_epcq *sdev = dev_get_drvdata(dev);
> +
> +     clk_disable_unprepare(sdev->clk);
> +
> +     return 0;
> +}
> +
> +static int altera_epcq_resume(struct device *dev) {
> +     struct altera_epcq *sdev = dev_get_drvdata(dev);
> +     int ret = -EPERM;

Drop 'ret'.

> +
> +     ret = clk_prepare_enable(sdev->clk);

Just:

        return clk_prepare_enable(sdev->clk);

> +
> +     return ret;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(altera_epcq_pm_ops, altera_epcq_suspend,
> +                      altera_epcq_resume);
> +#endif
> +
> +static const struct of_device_id altera_epcq_id_table[] = {
> +     { .compatible = "altr,epcq-1.0" },
> +     {}
> +};
> +MODULE_DEVICE_TABLE(of, altera_epcq_id_table);
> +
> +static struct platform_driver altera_epcq_driver = {
> +     .driver = {
> +             .name = ALTERA_EPCQ_RESOURCE_NAME,
> +             .bus = &platform_bus_type,
> +             .owner = THIS_MODULE,

The .owner assignment is no longer necessary.

> +             .of_match_table = altera_epcq_id_table, #ifdef CONFIG_PM

Also CONFIG_PM_SLEEP.

> +             .pm = &altera_epcq_pm_ops,
> +#endif
> +     },
> +     .probe = altera_epcq_probe,
> +     .remove = altera_epcq_remove,
> +};
> +module_platform_driver(altera_epcq_driver);
> +
> +MODULE_AUTHOR("Viet Nga Dao <vndao@altera.com>");
> +MODULE_DESCRIPTION("MTD Altera EPCQ Driver"); MODULE_LICENSE("GPL
> +v2");
> diff --git a/drivers/mtd/devices/altera_epcq.h
> b/drivers/mtd/devices/altera_epcq.h
> new file mode 100644
> index 0000000..c3d15e5
> --- /dev/null
> +++ b/drivers/mtd/devices/altera_epcq.h
> @@ -0,0 +1,130 @@
> +/*
> + * Copyright (C) 2014 Altera Corporation. All rights reserved
> + *
> + * This program is free software; you can redistribute it and/or
> +modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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.
> + *
> + * You should have received a copy of the GNU General Public License
> +along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef __ALTERA_ECPQ_H
> +#define __ALTERA_ECPQ_H
> +
> +#define ALTERA_EPCQ_RESOURCE_NAME        "altera_epcq"
> +/* max possible slots for serial flash chip in the EPCQ controller */
> +#define MAX_NUM_FLASH_CHIP   3
> +
> +/* macro to define partitions for flash devices */
> +#define DEFINE_PARTS(n, of, s)               \

This macro is not used anywhere. Drop it?

> +{                                    \
> +     .name = n,                      \
> +     .offset = of,                   \
> +     .size = s,                      \
> +}
> +
> +struct altera_epcq_flash_info {
> +     u32 bank;
> +     struct mtd_partition *partitions;
> +     int nr_partitions;
> +};
> +
> +struct altera_epcq_plat_data {
> +     void __iomem *csr_base;
> +     void __iomem *data_base;
> +     bool is_epcs;
> +     u32 num_flashes;
> +     struct altera_epcq_flash_info *board_flash_info;
> +     struct device_node *np[MAX_NUM_FLASH_CHIP]; };
> +
> +struct altera_epcq {
> +     struct clk *clk;
> +     bool is_epcs;
> +     struct mutex lock;      /*device lock*/
> +     void __iomem *csr_base;
> +     void __iomem *data_base;
> +     struct platform_device *pdev;
> +     u32 num_flashes;
> +     struct altera_epcq_flash *flash[MAX_NUM_FLASH_CHIP]; };
> +
> +struct altera_epcq_flash {
> +     u32 bank;
> +     u32 num_sectors;
> +     u32 num_parts;
> +     struct mtd_partition *parts;
> +     struct mutex lock;      /*flash lock*/
> +     struct mtd_info mtd;
> +};
> +
> +/* Define max times to check status register before we give up. */
> +#define EPCQ_MAX_TIME_OUT                    (40 * HZ)
> +
> +/* defines for status register */
> +#define EPCQ_SR_REG                          0x0
> +#define EPCQ_SR_WIP_MASK                     0x00000001
> +#define EPCQ_SR_WIP                          0x1
> +#define EPCQ_SR_WEL                          0x2
> +#define EPCQ_SR_BP0                          0x4
> +#define EPCQ_SR_BP1                          0x8
> +#define EPCQ_SR_BP2                          0x10
> +#define EPCQ_SR_BP3                          0x40
> +#define EPCQ_SR_TB                           0x20
> +
> +/* defines for device id register */
> +#define EPCQ_SID_REG                         0x4
> +#define EPCQ_RDID_REG                                0x8
> +#define EPCQ_RDID_MASK                               0x000000FF
> +/*
> + * EPCQ_MEM_OP register offset
> + *
> + * The EPCQ_MEM_OP register is used to do memory protect and erase
> +operations
> + *
> + */
> +#define EPCQ_MEM_OP_REG                              0xC
> +
> +#define EPCQ_MEM_OP_CMD_MASK                 0x00000003
> +#define EPCQ_MEM_OP_BULK_ERASE_CMD           0x00000001
> +#define EPCQ_MEM_OP_SECTOR_ERASE_CMD         0x00000002
> +#define EPCQ_MEM_OP_SECTOR_PROTECT_CMD               0x00000003
> +#define EPCQ_MEM_OP_SECTOR_VALUE_MASK                0x0003FF00
> +#define EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK        0x00001F00
> +#define EPCQ_MEM_OP_SECTOR_PROTECT_SHIFT     8
> +/*
> + * EPCQ_ISR register offset
> + *
> + * The EPCQ_ISR register is used to determine whether an invalid
> +write or erase
> + * operation trigerred an interrupt
> + *
> + */
> +#define EPCQ_ISR_REG                         0x10
> +
> +#define EPCQ_ISR_ILLEGAL_ERASE_MASK          0x00000001
> +#define EPCQ_ISR_ILLEGAL_WRITE_MASK          0x00000002
> +
> +/*
> + * EPCQ_IMR register offset
> + *
> + * The EPCQ_IMR register is used to mask the invalid erase or the
> +invalid write
> + * interrupts.
> + *
> + */
> +#define EPCQ_IMR_REG                         0x14
> +#define EPCQ_IMR_ILLEGAL_ERASE_MASK          0x00000001
> +
> +#define EPCQ_IMR_ILLEGAL_WRITE_MASK          0x00000002
> +
> +#define EPCQ_CHIP_SELECT_REG                 0x18
> +#define EPCQ_CHIP_SELECT_MASK                        0x00000007
> +#define EPCQ_CHIP_SELECT_0                   0x00000001
> +#define EPCQ_CHIP_SELECT_1                   0x00000002
> +#define EPCQ_CHIP_SELECT_2                   0x00000004
> +
> +#endif /* __ALTERA_ECPQ_H */

Brian

________________________________

Confidentiality Notice.
This message may contain information that is confidential or otherwise protected from disclosure. If you are not the intended recipient, you are hereby notified that any use, disclosure, dissemination, distribution, or copying of this message, or any attachments, is strictly prohibited. If you have received this message in error, please advise the sender by reply e-mail, and delete the message and any attachments. Thank you.

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
  2014-12-18  8:23 ` vndao
@ 2015-01-13  3:33   ` Brian Norris
  -1 siblings, 0 replies; 39+ messages in thread
From: Brian Norris @ 2015-01-13  3:33 UTC (permalink / raw)
  To: vndao; +Cc: dwmw2, linux-mtd, linux-kernel, devicetree, ngachi86

On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao@altera.com wrote:
> From: Viet Nga Dao <vndao@altera.com>
> 
> Altera EPCQ Controller is a soft IP which enables access to Altera EPCQ and
> EPCS flash chips. This patch adds driver for these devices.
> 
> Signed-off-by: Viet Nga Dao <vndao@altera.com>

This drivers seems awfully similar to (and so I infer it is likely
copy-and-pasted from) m25p80.c / spi-nor.c. Do you think it can be
rewritten as a SPI NOR driver, under drivers/mtd/spi-nor/ ? It looks
like these flash share most (all?) the same basic opcodes.

> ---
>  .../devicetree/bindings/mtd/altera_epcq.txt        |   45 ++
>  drivers/mtd/devices/Kconfig                        |   12 +
>  drivers/mtd/devices/Makefile                       |    2 +-
>  drivers/mtd/devices/altera_epcq.c                  |  804 ++++++++++++++++++++
>  drivers/mtd/devices/altera_epcq.h                  |  130 ++++
>  5 files changed, 992 insertions(+), 1 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/mtd/altera_epcq.txt
>  create mode 100644 drivers/mtd/devices/altera_epcq.c
>  create mode 100644 drivers/mtd/devices/altera_epcq.h
> 
> diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
> new file mode 100644
> index 0000000..d14f50e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
> @@ -0,0 +1,45 @@
> +* MTD Altera EPCQ driver
> +
> +Required properties:
> +- compatible: Should be "altr,epcq-1.0"
> +- reg: Address and length of the register set  for the device. It contains
> +  the information of registers in the same order as described by reg-names
> +- reg-names: Should contain the reg names
> +  "csr_base": Should contain the register configuration base address
> +  "data_base": Should contain the data base address
> +- is-epcs: boolean type.
> +		If present, the device contains EPCS flashes.
> +		Otherwise, it contains EPCQ flashes.
> +- #address-cells: Must be <1>.
> +- #size-cells: Must be <0>.
> +- flash device tree subnode, there must be a node with the following fields:

These subnodes definitely require a 'compatible' string. Perhaps they
should be used to differentiate EPCS vs. EPCQ. Does "is-epcs" really
need to be in the top-level controller node?

> +	- reg: Should contain the flash id

Should, or must? (This question is relevant, because you seem to make it
optional in your code.) And what does the "flash ID" mean? It seems like
you're using as a chip-select or bank index.

> +	- #address-cells: please refer to /mtd/partition.txt
> +	- #size-cells: please refer to /mtd/partition.txt
> +	For partitions inside each flash, please refer to /mtd/partition.txt
> +
> +Example:
> +
> +			epcq_controller_0: epcq@0x000000000 {
> +				compatible = "altr,epcq-1.0";
> +				reg = <0x00000001 0x00000000 0x00000020>,
> +					<0x00000000 0x00000000 0x02000000>;
> +				reg-names = "csr_base", "data_base";
> +				#address-cells = <1>;
> +				#size-cells = <0>;
> +				flash0: epcq256@0 {
> +					reg = <0>;
> +					#address-cells = <1>;
> +					#size-cells = <1>;
> +					partition@0 {
> +						/* 16 MB for raw data. */
> +						label = "EPCQ Flash 0 raw data";
> +						reg = <0x0 0x1000000>;
> +					};
> +					partition@1000000 {
> +						/* 16 MB for jffs2 data. */
> +						label = "EPCQ Flash 0 JFFS 2";
> +						reg = <0x1000000 0x1000000>;
> +					};
> +				};
> +			}; //end epcq@0x000000000 (epcq_controller_0)
> diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
> index c49d0b1..020b864 100644
> --- a/drivers/mtd/devices/Kconfig
> +++ b/drivers/mtd/devices/Kconfig
> @@ -218,6 +218,18 @@ config MTD_ST_SPI_FSM
>  	  SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
>  	  for a subset of connected Serial Flash devices.
>  
> +config MTD_ALTERA_EPCQ
> +	tristate "Support Altera EPCQ/EPCS Flash chips"
> +	depends on OF
> +	help
> +	  This enables access to Altera EPCQ/EPCS flash chips, used for data
> +	  storage. See the driver source for the current list,
> +	  or to add other chips.
> +
> +	  If you want to compile this driver as a module ( = code which can be
> +	  inserted in and removed from the running kernel whenever you want),
> +	  say M here and read <file:Documentation/kbuild/modules.txt>.
> +
>  if MTD_DOCG3
>  config BCH_CONST_M
>  	default 14
> diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
> index f0b0e61..b429c4d 100644
> --- a/drivers/mtd/devices/Makefile
> +++ b/drivers/mtd/devices/Makefile
> @@ -16,6 +16,6 @@ obj-$(CONFIG_MTD_SPEAR_SMI)	+= spear_smi.o
>  obj-$(CONFIG_MTD_SST25L)	+= sst25l.o
>  obj-$(CONFIG_MTD_BCM47XXSFLASH)	+= bcm47xxsflash.o
>  obj-$(CONFIG_MTD_ST_SPI_FSM)    += st_spi_fsm.o
> -
> +obj-$(CONFIG_MTD_ALTERA_EPCQ)		+= altera_epcq.o
>  
>  CFLAGS_docg3.o			+= -I$(src)
> diff --git a/drivers/mtd/devices/altera_epcq.c b/drivers/mtd/devices/altera_epcq.c
> new file mode 100644
> index 0000000..09213d5
> --- /dev/null
> +++ b/drivers/mtd/devices/altera_epcq.c
> @@ -0,0 +1,804 @@
> +/*
> + * Copyright (C) 2014 Altera Corporation. All rights reserved
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/errno.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/ioport.h>
> +#include <linux/jiffies.h>
> +#include <linux/kernel.h>
> +#include <linux/log2.h>
> +#include <linux/module.h>
> +#include <linux/mtd/mtd.h>
> +#include <linux/mtd/partitions.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/param.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/wait.h>
> +
> +#include "altera_epcq.h"
> +
> +/* data structure to maintain flash ids from different vendors */
> +struct flash_device {
> +	char *name;
> +	bool is_epcs;
> +	u32 device_id;
> +	uint32_t sectorsize_inbytes;
> +	uint64_t size_inbytes;
> +	u32 pagesize;
> +};
> +
> +#define FLASH_ID(_n, _is_epcs, _id, _ssize, _size, _psize)	\
> +{				\
> +	.name = (_n),		\
> +	.is_epcs = (_is_epcs),		\
> +	.device_id = (_id),	\
> +	.sectorsize_inbytes = (_ssize),	\
> +	.size_inbytes = (_size),	\
> +	.pagesize = (_psize),	\
> +}
> +
> +static struct flash_device flash_devices[] = {
> +	FLASH_ID("epcq16"    , 0, 0x15, 0x10000, 0x200000, 0x100),
> +	FLASH_ID("epcq32"    , 0, 0x16, 0x10000, 0x400000, 0x100),
> +	FLASH_ID("epcq64"    , 0, 0x17, 0x10000, 0x800000, 0x100),
> +	FLASH_ID("epcq128"   , 0, 0x18, 0x10000, 0x1000000, 0x100),
> +	FLASH_ID("epcq256"   , 0, 0x19, 0x10000, 0x2000000, 0x100),
> +	FLASH_ID("epcq512"   , 0, 0x20, 0x10000, 0x4000000, 0x100),
> +
> +	FLASH_ID("epcs16"    , 1, 0x14, 0x10000, 0x200000, 0x100),
> +	FLASH_ID("epcs64"    , 1, 0x16, 0x10000, 0x800000, 0x100),
> +	FLASH_ID("epcs128"   , 1, 0x18, 0x40000, 0x1000000, 0x100),
> +};
> +
> +static inline struct altera_epcq_flash *get_flash_data(struct mtd_info *mtd)
> +{
> +	return container_of(mtd, struct altera_epcq_flash, mtd);
> +}
> +
> +static u32 altera_epcq_read_sr(struct altera_epcq *dev)
> +{
> +	return readl(dev->csr_base + EPCQ_SR_REG);
> +}
> +
> +static int altera_epcq_wait_till_ready(struct altera_epcq *dev)
> +{
> +	unsigned long finish;
> +	int status;
> +
> +	finish = jiffies + EPCQ_MAX_TIME_OUT;
> +	do {
> +		status = altera_epcq_read_sr(dev);
> +		if (status < 0)
> +			return status;
> +		else if (!(status & EPCQ_SR_WIP))
> +			return 0;
> +
> +		cond_resched();
> +	} while (!time_after_eq(jiffies, finish));
> +
> +	dev_err(&dev->pdev->dev, "epcq controller is busy, timeout\n");
> +	return -EBUSY;
> +}
> +
> +static int get_flash_index(u32 flash_id, bool is_epcs)
> +{
> +	int index;
> +
> +	for (index = 0; index < ARRAY_SIZE(flash_devices); index++) {
> +		if (flash_devices[index].device_id == flash_id &&
> +		    flash_devices[index].is_epcs == is_epcs)
> +			return index;
> +	}
> +
> +	/* Memory chip is not listed and not supported */
> +	return -ENODEV;
> +}
> +
> +static int altera_epcq_write_erase_check(struct altera_epcq *dev,
> +					 bool write_erase)
> +{
> +	u32 val;
> +	u32 mask;
> +
> +	if (write_erase)
> +		mask = EPCQ_ISR_ILLEGAL_WRITE_MASK;
> +	else
> +		mask = EPCQ_ISR_ILLEGAL_ERASE_MASK;
> +
> +	val = readl(dev->csr_base + EPCQ_ISR_REG);
> +	if (val & mask) {
> +		dev_err(&dev->pdev->dev,
> +			"write/erase failed, sector might be protected\n");
> +		/*clear this status for next use*/
> +		writel(val, dev->csr_base + EPCQ_ISR_REG);
> +		return -EIO;
> +	}
> +	return 0;
> +}
> +
> +static int altera_epcq_erase_chip(struct mtd_info *mtd)
> +{
> +	int ret;
> +	u32 val;
> +	struct altera_epcq *dev = mtd->priv;
> +
> +	/* Wait until finished previous write command. */
> +	ret = altera_epcq_wait_till_ready(dev);

This pattern is pretty silly. We don't need to have every operation
check the status of the previous operation. Each operation should check
itself. You obviously copied this one... but we recently changed that:

    commit dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3
    Author: Brian Norris <computersforpeace@gmail.com>
    Date:   Wed Aug 6 18:16:57 2014 -0700

        mtd: spi-nor: move "wait-till-ready" checks into erase/write
	functions


    https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3

> +	if (ret)
> +		return ret;
> +
> +	/* erase chip. */
> +	val = EPCQ_MEM_OP_BULK_ERASE_CMD;
> +	writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
> +
> +	/* Wait until finished previous write command. */
> +	ret = altera_epcq_wait_till_ready(dev);
> +	if (ret)
> +		return ret;
> +
> +	 /* check whether write triggered a illegal write interrupt */
> +	ret = altera_epcq_write_erase_check(dev, 0);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int altera_epcq_addr_to_sector(struct altera_epcq_flash *flash,
> +				      int addr_offset)
> +{
> +	int sector = 0;
> +	int i;
> +
> +	for (i = 0; i < flash->num_sectors; i++) {
> +		if (addr_offset >= i * flash->mtd.erasesize && addr_offset <
> +		    (i * flash->mtd.erasesize + flash->mtd.erasesize)) {
> +			sector = i;
> +			return sector;
> +		}
> +	}

Uh, really? Am I crazy, or shouldn't this whole function just be a
really simple arithmetic operation? Like:

	return addr_offset >> mtd->erasesize_shift;

> +	return -1;
> +}
> +
> +static int altera_epcq_erase_sector(struct mtd_info *mtd,
> +				    int addr_offset)
> +{
> +	struct altera_epcq_flash *flash = get_flash_data(mtd);
> +	struct altera_epcq *dev = mtd->priv;
> +	u32 val;
> +	int ret;
> +	int sector_value;
> +
> +	ret = altera_epcq_wait_till_ready(dev);
> +	if (ret)
> +		return ret;
> +
> +	sector_value = altera_epcq_addr_to_sector(flash, addr_offset);
> +
> +	/* sanity check that block_offset is a valid sector number */
> +	if (sector_value < 0)
> +		return -EINVAL;
> +
> +	/* sector value should occupy bits 17:8 */
> +	val = (sector_value << 8) & EPCQ_MEM_OP_SECTOR_VALUE_MASK;
> +
> +	/* sector erase commands occupies lower 2 bits */
> +	val |= EPCQ_MEM_OP_SECTOR_ERASE_CMD;
> +
> +	/* write sector erase command to EPCQ_MEM_OP register*/
> +	writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
> +
> +	ret = altera_epcq_wait_till_ready(dev);
> +	if (ret)
> +		return ret;
> +
> +	 /* check whether write triggered a illegal write interrupt */

^^ extra space here? You have <TAB><SPACE>/* check [...]

> +	ret = altera_epcq_write_erase_check(dev, 0);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int altera_epcq_erase(struct mtd_info *mtd, struct erase_info *e_info)
> +{
> +	u32 addr;
> +	int ret;
> +	u32 len;
> +	struct altera_epcq_flash *flash = get_flash_data(mtd);
> +	struct altera_epcq *dev = mtd->priv;
> +
> +	addr = e_info->addr;
> +	len = e_info->len;
> +
> +	if (flash->bank > dev->num_flashes - 1) {
> +		dev_err(&dev->pdev->dev, "Invalid chip id\n");
> +		return -EINVAL;
> +	}
> +	mutex_lock(&flash->lock);
> +
> +	/*erase whole chip*/
> +	if (len == mtd->size) {
> +		if (altera_epcq_erase_chip(mtd)) {
> +			e_info->state = MTD_ERASE_FAILED;
> +			mutex_unlock(&flash->lock);
> +			return -EIO;
> +		}
> +	/*"sector"-at-a-time erase*/
> +	} else {
> +		while (len) {
> +			ret = altera_epcq_erase_sector(mtd, addr);
> +			if (ret) {
> +				e_info->state = MTD_ERASE_FAILED;
> +				mutex_unlock(&flash->lock);
> +				return -EIO;
> +			}
> +			addr += mtd->erasesize;
> +			if (len < mtd->erasesize)
> +				len = 0;
> +			else
> +				len -= mtd->erasesize;
> +		}
> +	}
> +	mutex_unlock(&flash->lock);
> +	e_info->state = MTD_ERASE_DONE;
> +	mtd_erase_callback(e_info);
> +
> +	return 0;
> +}
> +
> +static int altera_epcq_read(struct mtd_info *mtd, loff_t from, size_t len,
> +			    size_t *retlen, u8 *buf)
> +{
> +	struct altera_epcq_flash *flash = get_flash_data(mtd);
> +	struct altera_epcq *dev = mtd->priv;
> +	void *src;
> +	int ret = 0;
> +
> +	if (!flash || !dev)
> +		return -ENODEV;

Why would either of these be 0? If they are, you have much bigger
problems, since you aren't checking these almost anywhere else. And the
!flash check is pretty odd, since get_flash_data() is using
container_of(), which is not likely to give you exactly 0...

> +
> +	if (flash->bank > dev->num_flashes - 1) {
> +		dev_err(&dev->pdev->dev, "Invalid chip id\n");
> +		return -EINVAL;
> +	}
> +
> +	src = dev->data_base + from;
> +
> +	mutex_lock(&flash->lock);
> +	/* wait till previous write/erase is done. */
> +	ret = altera_epcq_wait_till_ready(dev);
> +	if (ret)
> +		goto err;
> +
> +	memcpy_fromio(buf, (u8 *)src, len);

Drop the cast. Also, you get sparse warnings because you're dropping the
__iomem. Why not just this?

	memcpy_fromio(buf, dev->data_base + from, len);

(BTW, you might consider running sparse on all your code. See
Documentation/sparse.txt. It tells you how to get it, but you can often
just download it through your distro's package manager.)

> +	*retlen = len;
> +
> +err:
> +	mutex_unlock(&flash->lock);
> +	return ret;
> +}
> +
> +static int altera_epcq_write(struct mtd_info *mtd, loff_t to, size_t len,
> +			     size_t *retlen, const u8 *buf)
> +{
> +	struct altera_epcq_flash *flash = get_flash_data(mtd);
> +	struct altera_epcq *dev = mtd->priv;
> +	void *dest;
> +	int ret = 0;
> +
> +	if (!flash || !dev)
> +		return -ENODEV;

Also here. I don't think you need these checks.

> +
> +	if (flash->bank > dev->num_flashes - 1) {
> +		dev_err(&dev->pdev->dev, "Invalid chip id\n");
> +		return -EINVAL;
> +	}
> +	dest = dev->data_base + to;
> +
> +	mutex_lock(&flash->lock);
> +
> +	/* wait until finished previous write command. */
> +	ret = altera_epcq_wait_till_ready(dev);
> +	if (ret)
> +		goto err;
> +
> +	memcpy_toio(dest, buf, len);

Similar. Why not just this?

	memcpy_toio(dev->data_base + to, buf, len);

> +	*retlen += len;
> +
> +	/* wait until finished previous write command. */
> +	ret = altera_epcq_wait_till_ready(dev);
> +	if (ret)
> +		goto err;
> +
> +	 /* check whether write triggered a illegal write interrupt */
> +	ret = altera_epcq_write_erase_check(dev, 1);
> +	if (ret < 0)
> +		goto err;
> +
> +err:
> +	mutex_unlock(&flash->lock);
> +	return ret;
> +}
> +
> +static int altera_epcq_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
> +{
> +	struct altera_epcq_flash *flash = get_flash_data(mtd);
> +	struct altera_epcq *dev = mtd->priv;
> +	uint32_t offset = ofs;
> +	int ret = 0;
> +	u32 sector_start, sector_end;
> +	u32 num_sectors;
> +	u32 mem_op;
> +	unsigned sr_bp = 0;
> +	unsigned sr_tb = 0;
> +
> +	sector_start = altera_epcq_addr_to_sector(flash, offset);
> +	sector_end = altera_epcq_addr_to_sector(flash, offset + len);
> +	num_sectors = flash->num_sectors;
> +	dev_dbg(&dev->pdev->dev,
> +		"%s: num_setor is %u,sector start is %u,sector end is %u\n",
> +		__func__, num_sectors, sector_start, sector_end);
> +
> +	mutex_lock(&flash->lock);
> +	/* wait until finished previous write command. */
> +	ret = altera_epcq_wait_till_ready(dev);
> +	if (ret)
> +		goto err;
> +
> +	if (sector_start >= num_sectors/2) {

It's preferable to have spaces around operators. So 'num_sectors / 2'.
The same thing comes up in several other places.

> +		if (sector_start < num_sectors-(num_sectors / 4))
> +			sr_bp = __ilog2_u32(num_sectors);
> +		else if (sector_start < num_sectors-(num_sectors / 8))
> +			sr_bp = __ilog2_u32(num_sectors) - 1;
> +		else if (sector_start < num_sectors-(num_sectors / 16))
> +			sr_bp = __ilog2_u32(num_sectors) - 2;
> +		else if (sector_start < num_sectors-(num_sectors / 32))
> +			sr_bp = __ilog2_u32(num_sectors) - 3;
> +		else if (sector_start < num_sectors-(num_sectors / 64))
> +			sr_bp = __ilog2_u32(num_sectors) - 4;
> +		else if (sector_start < num_sectors-(num_sectors / 128))
> +			sr_bp = __ilog2_u32(num_sectors) - 5;
> +		else if (sector_start < num_sectors-(num_sectors / 256))
> +			sr_bp = __ilog2_u32(num_sectors) - 6;
> +		else if (sector_start < num_sectors-(num_sectors / 512))
> +			sr_bp = __ilog2_u32(num_sectors) - 7;
> +		else if (sector_start < num_sectors-(num_sectors / 1024))
> +			sr_bp = __ilog2_u32(num_sectors) - 8;
> +		else
> +			sr_bp = 0;  /* non area protected */

Yikes, that's ugly! And I'm not sure it matches the EPCQ doc I found.
I'm pretty sure you can rewrite this if/else-if/else block in about 1
line though.

> +
> +		if (sr_bp < 0) {

sr_bp is unsigned, so this is never true.

> +			dev_err(&dev->pdev->dev, "%s: address is out of range\n"
> +				, __func__);
> +			ret = -EINVAL;
> +			goto err;
> +		}
> +		/*set TB = 0*/
> +		sr_tb = 0;
> +
> +	} else {
> +		if (sector_end < 1)
> +			sr_bp = 1;
> +		else if (sector_end < 2)
> +			sr_bp = 2;
> +		else if (sector_end < 4)
> +			sr_bp = 3;
> +		else if (sector_end < 8)
> +			sr_bp = 4;
> +		else if (sector_end < 16)
> +			sr_bp = 5;
> +		else if (sector_end < 32)
> +			sr_bp = 6;
> +		else if (sector_end < 64)
> +			sr_bp = 7;
> +		else if (sector_end < 128)
> +			sr_bp = 8;
> +		else if (sector_end < 256)
> +			sr_bp = 9;
> +		else if (sector_end < 512)
> +			sr_bp = 10;
> +		else
> +			sr_bp = 16; /*protect all areas*/

Again, this is ugly. How about this?

	sr_bp = fls(sector_end) + 1;

> +
> +		sr_tb = 1;
> +	}
> +
> +	mem_op = (sr_tb << 12) | (sr_bp << 8);
> +	mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
> +	mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
> +	writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
> +
> +err:
> +	mutex_unlock(&flash->lock);
> +	return ret;
> +}
> +
> +static int altera_epcq_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
> +{
> +	struct altera_epcq_flash *flash = get_flash_data(mtd);
> +	struct altera_epcq *dev = mtd->priv;
> +
> +	int ret = 0;
> +	u32 mem_op;
> +
> +	mutex_lock(&flash->lock);
> +	/* wait until finished previous write command. */
> +	ret = altera_epcq_wait_till_ready(dev);
> +	if (ret)
> +		goto err;
> +	dev_info(&dev->pdev->dev, "Unlock all protected area\n");
> +	mem_op = 0;
> +	mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
> +	mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
> +	writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
> +
> +err:
> +	mutex_unlock(&flash->lock);
> +	return ret;
> +}
> +
> +static void altera_epcq_chip_select(struct altera_epcq *dev, u32 bank)
> +{
> +	u32 val = 0;
> +
> +	switch (bank) {
> +	case 0:
> +		val = EPCQ_CHIP_SELECT_0;
> +		break;
> +	case 1:
> +		val = EPCQ_CHIP_SELECT_1;
> +		break;
> +	case 2:
> +		val = EPCQ_CHIP_SELECT_2;
> +		break;
> +	default:
> +		return;
> +	}
> +
> +	writel(val, dev->csr_base + EPCQ_CHIP_SELECT_REG);
> +}
> +
> +static int altera_epcq_probe_flash(struct altera_epcq *dev, u32 bank)
> +{
> +	int ret = 0;
> +	u32 val = 0;
> +
> +	mutex_lock(&dev->lock);
> +
> +	/*select bank*/

Comment spacing, here and elsewhere:

	/* select bank */

> +	altera_epcq_chip_select(dev, bank);
> +
> +	ret = altera_epcq_wait_till_ready(dev);
> +	if (ret)
> +		goto err;
> +
> +	/* get device sillicon id */
> +	if (dev->is_epcs)
> +		val = readl(dev->csr_base + EPCQ_SID_REG);
> +	else
> +		val = readl(dev->csr_base + EPCQ_RDID_REG);
> +
> +	/* get flash index based on the device list*/
> +	ret = get_flash_index(val, dev->is_epcs);
> +	return 0;
> +err:
> +	mutex_unlock(&dev->lock);
> +	return ret;
> +}
> +
> +static int altera_epcq_probe_config_dt(struct platform_device *pdev,
> +				       struct device_node *np,
> +				       struct altera_epcq_plat_data *pdata)
> +{
> +	struct device_node *pp = NULL;
> +	struct resource *epcq_res;
> +	int i = 0;
> +	u32 id;
> +
> +	pdata->is_epcs = of_property_read_bool(np, "is-epcs");
> +
> +	epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> +						"csr_base");
> +	if (!epcq_res) {

devm_ioremap_resource() will check the that 'epcq_res' is valid, so you
don't need this whole 'if' block.

> +		dev_err(&pdev->dev, "resource csr base is not defined\n");
> +		return -ENODEV;
> +	}
> +
> +	pdata->csr_base = devm_ioremap_resource(&pdev->dev, epcq_res);
> +	if (IS_ERR(pdata->csr_base)) {
> +		dev_err(&pdev->dev, "%s: ERROR: failed to map csr base\n",
> +			__func__);
> +		return PTR_ERR(pdata->csr_base);
> +	}
> +
> +	epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> +						"data_base");
> +	if (!epcq_res) {

Same here.

> +		dev_err(&pdev->dev, "resource data base is not defined\n");
> +		return -ENODEV;
> +	}
> +
> +	pdata->data_base = devm_ioremap_resource(&pdev->dev, epcq_res);
> +	if (IS_ERR(pdata->data_base)) {
> +		dev_err(&pdev->dev, "%s: ERROR: failed to map data base\n",
> +			__func__);
> +		return PTR_ERR(pdata->data_base);
> +	}
> +
> +	pdata->board_flash_info = devm_kzalloc(&pdev->dev,
> +					       sizeof(*pdata->board_flash_info),
> +					       GFP_KERNEL);
> +
> +	/* Fill structs for each subnode (flash device) */
> +	while ((pp = of_get_next_child(np, pp))) {

for_each_available_child_of_node()?

> +		struct altera_epcq_flash_info *flash_info;
> +
> +		flash_info = &pdata->board_flash_info[i];

This is never used. Either use it below, or drop the local variable.

> +		pdata->np[i] = pp;
> +
> +		/* Read bank id from DT */
> +		if (of_get_property(pp, "reg", &id))

Is this property optional? Your DT binding doc doesn't make it clear,
but it seems like a property which would be wise to require (i.e., not
optional).

> +			pdata->board_flash_info[i].bank = id;
> +		i++;
> +	}
> +	pdata->num_flashes = i;
> +	return 0;
> +}
> +
> +static int altera_epcq_setup_banks(struct platform_device *pdev,
> +				   u32 bank, struct device_node *np,
> +				   struct altera_epcq_plat_data *pdata)
> +{
> +	struct altera_epcq *dev = platform_get_drvdata(pdev);
> +	struct mtd_part_parser_data ppdata = {};
> +	struct altera_epcq_flash_info *flash_info;
> +	struct altera_epcq_flash *flash;
> +	struct mtd_partition *parts = NULL;

Drop this variable. Just use NULL directly below.

> +	int count = 0;

Same. Just use 0 below.

> +	int flash_index;
> +	int ret = 0;
> +	uint64_t size;
> +	uint32_t sector_size;
> +
> +	flash_info = &pdata->board_flash_info[bank];
> +	if (!flash_info)
> +		return -ENODEV;
> +
> +	if (bank > pdata->num_flashes - 1)
> +		return -EINVAL;
> +
> +	flash = devm_kzalloc(&pdev->dev, sizeof(*flash), GFP_KERNEL);
> +	if (!flash)
> +		return -ENOMEM;
> +	flash->bank = bank;
> +
> +	mutex_init(&flash->lock);
> +
> +	/* verify whether flash is really present on board */
> +	flash_index = altera_epcq_probe_flash(dev, bank);
> +	if (flash_index < 0) {
> +		dev_info(&dev->pdev->dev, "epcq:flash%d not found\n",
> +			 flash->bank);
> +		return flash_index;
> +	}
> +
> +	dev->flash[bank] = flash;
> +
> +	size = flash_devices[flash_index].size_inbytes;
> +	sector_size = flash_devices[flash_index].sectorsize_inbytes;
> +	/*use do_div instead of plain div to avoid linker err*/
> +	do_div(size, sector_size);
> +	flash->num_sectors = size;
> +
> +	/*mtd framework */
> +	flash->mtd.priv = dev;
> +	flash->mtd.name = flash_devices[flash_index].name;
> +	flash->mtd.type = MTD_NORFLASH;
> +	flash->mtd.writesize = 1;
> +	flash->mtd.flags = MTD_CAP_NORFLASH;
> +	flash->mtd.size = flash_devices[flash_index].size_inbytes;
> +	flash->mtd.erasesize = flash_devices[flash_index].sectorsize_inbytes;
> +	flash->mtd._erase = altera_epcq_erase;
> +	flash->mtd._read = altera_epcq_read;
> +	flash->mtd._write = altera_epcq_write;
> +	flash->mtd._lock = altera_epcq_lock;
> +	flash->mtd._unlock = altera_epcq_unlock;
> +
> +	dev_info(&pdev->dev, "mtd .name=%s .size=0x%llx (%lluM)\n",
> +		 flash->mtd.name, (long long)flash->mtd.size,
> +		 (long long)(flash->mtd.size >> 20));
> +
> +	dev_info(&pdev->dev, ".erasesize = 0x%x(%uK)\n",
> +		 flash->mtd.erasesize, flash->mtd.erasesize >> 10);
> +
> +	ppdata.of_node = np;
> +
> +	ret = mtd_device_parse_register(&flash->mtd, NULL, &ppdata, parts,
> +					count);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Err MTD partition=%d\n", ret);
> +		goto err;

You don't want to unregister a MTD that failed to register. The MTD core
code should handle that itself.

Instead of this if (ret) {} block, I'd just make this:

	return mtd_device_parse_register(&flash->mtd, NULL, &ppdata, NULL, 0);

> +	}
> +
> +	return 0;
> +
> +err:
> +	mtd_device_unregister(&flash->mtd);
> +	return ret;
> +}
> +
> +static int altera_epcq_probe(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	struct altera_epcq_plat_data *pdata = NULL;
> +	struct altera_epcq *dev;
> +	int ret = 0;
> +	int i;
> +
> +	if (!np) {
> +		ret = -ENODEV;
> +		dev_err(&pdev->dev, "no device found\n");
> +		goto err;
> +	}
> +
> +	pdata = devm_kzalloc(&pdev->dev,
> +			     sizeof(struct altera_epcq_plat_data),
> +			     GFP_KERNEL);
> +
> +	if (!pdata) {
> +		ret = -ENOMEM;
> +		dev_err(&pdev->dev, "%s: ERROR: no memory\n", __func__);

Unnecessary print. The MM code will print plenty of info if you're
out of memory.

> +		goto err;
> +	}
> +	ret = altera_epcq_probe_config_dt(pdev, np, pdata);
> +	if (ret) {
> +		ret = -ENODEV;
> +		dev_err(&pdev->dev, "probe fail\n");
> +		goto err;
> +	}
> +
> +	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
> +	if (!dev) {
> +		ret = -ENOMEM;
> +		dev_err(&pdev->dev, "mem alloc fail\n");

Same here.

> +		goto err;
> +	}
> +	mutex_init(&dev->lock);
> +	dev->pdev = pdev;
> +	dev->is_epcs = pdata->is_epcs;
> +	dev->csr_base = pdata->csr_base;
> +	dev->data_base = pdata->data_base;
> +
> +	/*check number of flashes*/
> +	dev->num_flashes = pdata->num_flashes;
> +	if (dev->num_flashes > MAX_NUM_FLASH_CHIP) {
> +		dev_err(&pdev->dev, "exceeding max number of flashes\n");
> +		dev->num_flashes = MAX_NUM_FLASH_CHIP;
> +	}
> +
> +	/* check clock*/
> +	dev->clk = devm_clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(dev->clk)) {
> +		ret = PTR_ERR(dev->clk);
> +		goto err;
> +	}
> +	ret = clk_prepare_enable(dev->clk);
> +	if (ret)
> +		goto err;
> +
> +	platform_set_drvdata(pdev, dev);
> +
> +	/* loop for each serial flash which is connected to epcq */
> +	for (i = 0; i < dev->num_flashes; i++) {
> +		ret = altera_epcq_setup_banks(pdev, i, pdata->np[i], pdata);
> +		if (ret) {
> +			dev_err(&pdev->dev, "bank setup failed\n");
> +			goto err_bank_setup;
> +		}
> +	}
> +
> +	return 0;
> +
> +err_bank_setup:
> +	clk_disable_unprepare(dev->clk);
> +err:
> +	return ret;
> +}
> +
> +static int altera_epcq_remove(struct platform_device *pdev)
> +{
> +	struct altera_epcq *dev;
> +	struct altera_epcq_flash *flash;
> +	int ret, i;
> +
> +	dev = platform_get_drvdata(pdev);
> +
> +	/* clean up for all nor flash */
> +	for (i = 0; i < dev->num_flashes; i++) {
> +		flash = dev->flash[i];
> +		if (!flash)
> +			continue;
> +
> +		/* clean up mtd stuff */
> +		ret = mtd_device_unregister(&flash->mtd);
> +		if (ret)
> +			dev_err(&pdev->dev, "error removing mtd\n");
> +	}
> +
> +	clk_disable_unprepare(dev->clk);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM

Make this CONFIG_PM_SLEEP.

> +static int altera_epcq_suspend(struct device *dev)
> +{
> +	struct altera_epcq *sdev = dev_get_drvdata(dev);
> +
> +	clk_disable_unprepare(sdev->clk);
> +
> +	return 0;
> +}
> +
> +static int altera_epcq_resume(struct device *dev)
> +{
> +	struct altera_epcq *sdev = dev_get_drvdata(dev);
> +	int ret = -EPERM;

Drop 'ret'.

> +
> +	ret = clk_prepare_enable(sdev->clk);

Just:

	return clk_prepare_enable(sdev->clk);

> +
> +	return ret;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(altera_epcq_pm_ops, altera_epcq_suspend,
> +			 altera_epcq_resume);
> +#endif
> +
> +static const struct of_device_id altera_epcq_id_table[] = {
> +	{ .compatible = "altr,epcq-1.0" },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, altera_epcq_id_table);
> +
> +static struct platform_driver altera_epcq_driver = {
> +	.driver = {
> +		.name = ALTERA_EPCQ_RESOURCE_NAME,
> +		.bus = &platform_bus_type,
> +		.owner = THIS_MODULE,

The .owner assignment is no longer necessary.

> +		.of_match_table = altera_epcq_id_table,
> +#ifdef CONFIG_PM

Also CONFIG_PM_SLEEP.

> +		.pm = &altera_epcq_pm_ops,
> +#endif
> +	},
> +	.probe = altera_epcq_probe,
> +	.remove = altera_epcq_remove,
> +};
> +module_platform_driver(altera_epcq_driver);
> +
> +MODULE_AUTHOR("Viet Nga Dao <vndao@altera.com>");
> +MODULE_DESCRIPTION("MTD Altera EPCQ Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/mtd/devices/altera_epcq.h b/drivers/mtd/devices/altera_epcq.h
> new file mode 100644
> index 0000000..c3d15e5
> --- /dev/null
> +++ b/drivers/mtd/devices/altera_epcq.h
> @@ -0,0 +1,130 @@
> +/*
> + * Copyright (C) 2014 Altera Corporation. All rights reserved
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef __ALTERA_ECPQ_H
> +#define __ALTERA_ECPQ_H
> +
> +#define ALTERA_EPCQ_RESOURCE_NAME        "altera_epcq"
> +/* max possible slots for serial flash chip in the EPCQ controller */
> +#define MAX_NUM_FLASH_CHIP	3
> +
> +/* macro to define partitions for flash devices */
> +#define DEFINE_PARTS(n, of, s)		\

This macro is not used anywhere. Drop it?

> +{					\
> +	.name = n,			\
> +	.offset = of,			\
> +	.size = s,			\
> +}
> +
> +struct altera_epcq_flash_info {
> +	u32 bank;
> +	struct mtd_partition *partitions;
> +	int nr_partitions;
> +};
> +
> +struct altera_epcq_plat_data {
> +	void __iomem *csr_base;
> +	void __iomem *data_base;
> +	bool is_epcs;
> +	u32 num_flashes;
> +	struct altera_epcq_flash_info *board_flash_info;
> +	struct device_node *np[MAX_NUM_FLASH_CHIP];
> +};
> +
> +struct altera_epcq {
> +	struct clk *clk;
> +	bool is_epcs;
> +	struct mutex lock;	/*device lock*/
> +	void __iomem *csr_base;
> +	void __iomem *data_base;
> +	struct platform_device *pdev;
> +	u32 num_flashes;
> +	struct altera_epcq_flash *flash[MAX_NUM_FLASH_CHIP];
> +};
> +
> +struct altera_epcq_flash {
> +	u32 bank;
> +	u32 num_sectors;
> +	u32 num_parts;
> +	struct mtd_partition *parts;
> +	struct mutex lock;	/*flash lock*/
> +	struct mtd_info mtd;
> +};
> +
> +/* Define max times to check status register before we give up. */
> +#define EPCQ_MAX_TIME_OUT			(40 * HZ)
> +
> +/* defines for status register */
> +#define EPCQ_SR_REG				0x0
> +#define EPCQ_SR_WIP_MASK			0x00000001
> +#define EPCQ_SR_WIP				0x1
> +#define EPCQ_SR_WEL				0x2
> +#define EPCQ_SR_BP0				0x4
> +#define EPCQ_SR_BP1				0x8
> +#define EPCQ_SR_BP2				0x10
> +#define EPCQ_SR_BP3				0x40
> +#define EPCQ_SR_TB				0x20
> +
> +/* defines for device id register */
> +#define EPCQ_SID_REG				0x4
> +#define EPCQ_RDID_REG				0x8
> +#define EPCQ_RDID_MASK				0x000000FF
> +/*
> + * EPCQ_MEM_OP register offset
> + *
> + * The EPCQ_MEM_OP register is used to do memory protect and erase operations
> + *
> + */
> +#define EPCQ_MEM_OP_REG				0xC
> +
> +#define EPCQ_MEM_OP_CMD_MASK			0x00000003
> +#define EPCQ_MEM_OP_BULK_ERASE_CMD		0x00000001
> +#define EPCQ_MEM_OP_SECTOR_ERASE_CMD		0x00000002
> +#define EPCQ_MEM_OP_SECTOR_PROTECT_CMD		0x00000003
> +#define EPCQ_MEM_OP_SECTOR_VALUE_MASK		0x0003FF00
> +#define EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK	0x00001F00
> +#define EPCQ_MEM_OP_SECTOR_PROTECT_SHIFT	8
> +/*
> + * EPCQ_ISR register offset
> + *
> + * The EPCQ_ISR register is used to determine whether an invalid write or erase
> + * operation trigerred an interrupt
> + *
> + */
> +#define EPCQ_ISR_REG				0x10
> +
> +#define EPCQ_ISR_ILLEGAL_ERASE_MASK		0x00000001
> +#define EPCQ_ISR_ILLEGAL_WRITE_MASK		0x00000002
> +
> +/*
> + * EPCQ_IMR register offset
> + *
> + * The EPCQ_IMR register is used to mask the invalid erase or the invalid write
> + * interrupts.
> + *
> + */
> +#define EPCQ_IMR_REG				0x14
> +#define EPCQ_IMR_ILLEGAL_ERASE_MASK		0x00000001
> +
> +#define EPCQ_IMR_ILLEGAL_WRITE_MASK		0x00000002
> +
> +#define EPCQ_CHIP_SELECT_REG			0x18
> +#define EPCQ_CHIP_SELECT_MASK			0x00000007
> +#define EPCQ_CHIP_SELECT_0			0x00000001
> +#define EPCQ_CHIP_SELECT_1			0x00000002
> +#define EPCQ_CHIP_SELECT_2			0x00000004
> +
> +#endif /* __ALTERA_ECPQ_H */

Brian

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-01-13  3:33   ` Brian Norris
  0 siblings, 0 replies; 39+ messages in thread
From: Brian Norris @ 2015-01-13  3:33 UTC (permalink / raw)
  To: vndao; +Cc: linux-mtd, ngachi86, dwmw2, linux-kernel, devicetree

On Thu, Dec 18, 2014 at 12:23:16AM -0800, vndao@altera.com wrote:
> From: Viet Nga Dao <vndao@altera.com>
> 
> Altera EPCQ Controller is a soft IP which enables access to Altera EPCQ and
> EPCS flash chips. This patch adds driver for these devices.
> 
> Signed-off-by: Viet Nga Dao <vndao@altera.com>

This drivers seems awfully similar to (and so I infer it is likely
copy-and-pasted from) m25p80.c / spi-nor.c. Do you think it can be
rewritten as a SPI NOR driver, under drivers/mtd/spi-nor/ ? It looks
like these flash share most (all?) the same basic opcodes.

> ---
>  .../devicetree/bindings/mtd/altera_epcq.txt        |   45 ++
>  drivers/mtd/devices/Kconfig                        |   12 +
>  drivers/mtd/devices/Makefile                       |    2 +-
>  drivers/mtd/devices/altera_epcq.c                  |  804 ++++++++++++++++++++
>  drivers/mtd/devices/altera_epcq.h                  |  130 ++++
>  5 files changed, 992 insertions(+), 1 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/mtd/altera_epcq.txt
>  create mode 100644 drivers/mtd/devices/altera_epcq.c
>  create mode 100644 drivers/mtd/devices/altera_epcq.h
> 
> diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
> new file mode 100644
> index 0000000..d14f50e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
> @@ -0,0 +1,45 @@
> +* MTD Altera EPCQ driver
> +
> +Required properties:
> +- compatible: Should be "altr,epcq-1.0"
> +- reg: Address and length of the register set  for the device. It contains
> +  the information of registers in the same order as described by reg-names
> +- reg-names: Should contain the reg names
> +  "csr_base": Should contain the register configuration base address
> +  "data_base": Should contain the data base address
> +- is-epcs: boolean type.
> +		If present, the device contains EPCS flashes.
> +		Otherwise, it contains EPCQ flashes.
> +- #address-cells: Must be <1>.
> +- #size-cells: Must be <0>.
> +- flash device tree subnode, there must be a node with the following fields:

These subnodes definitely require a 'compatible' string. Perhaps they
should be used to differentiate EPCS vs. EPCQ. Does "is-epcs" really
need to be in the top-level controller node?

> +	- reg: Should contain the flash id

Should, or must? (This question is relevant, because you seem to make it
optional in your code.) And what does the "flash ID" mean? It seems like
you're using as a chip-select or bank index.

> +	- #address-cells: please refer to /mtd/partition.txt
> +	- #size-cells: please refer to /mtd/partition.txt
> +	For partitions inside each flash, please refer to /mtd/partition.txt
> +
> +Example:
> +
> +			epcq_controller_0: epcq@0x000000000 {
> +				compatible = "altr,epcq-1.0";
> +				reg = <0x00000001 0x00000000 0x00000020>,
> +					<0x00000000 0x00000000 0x02000000>;
> +				reg-names = "csr_base", "data_base";
> +				#address-cells = <1>;
> +				#size-cells = <0>;
> +				flash0: epcq256@0 {
> +					reg = <0>;
> +					#address-cells = <1>;
> +					#size-cells = <1>;
> +					partition@0 {
> +						/* 16 MB for raw data. */
> +						label = "EPCQ Flash 0 raw data";
> +						reg = <0x0 0x1000000>;
> +					};
> +					partition@1000000 {
> +						/* 16 MB for jffs2 data. */
> +						label = "EPCQ Flash 0 JFFS 2";
> +						reg = <0x1000000 0x1000000>;
> +					};
> +				};
> +			}; //end epcq@0x000000000 (epcq_controller_0)
> diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
> index c49d0b1..020b864 100644
> --- a/drivers/mtd/devices/Kconfig
> +++ b/drivers/mtd/devices/Kconfig
> @@ -218,6 +218,18 @@ config MTD_ST_SPI_FSM
>  	  SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
>  	  for a subset of connected Serial Flash devices.
>  
> +config MTD_ALTERA_EPCQ
> +	tristate "Support Altera EPCQ/EPCS Flash chips"
> +	depends on OF
> +	help
> +	  This enables access to Altera EPCQ/EPCS flash chips, used for data
> +	  storage. See the driver source for the current list,
> +	  or to add other chips.
> +
> +	  If you want to compile this driver as a module ( = code which can be
> +	  inserted in and removed from the running kernel whenever you want),
> +	  say M here and read <file:Documentation/kbuild/modules.txt>.
> +
>  if MTD_DOCG3
>  config BCH_CONST_M
>  	default 14
> diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
> index f0b0e61..b429c4d 100644
> --- a/drivers/mtd/devices/Makefile
> +++ b/drivers/mtd/devices/Makefile
> @@ -16,6 +16,6 @@ obj-$(CONFIG_MTD_SPEAR_SMI)	+= spear_smi.o
>  obj-$(CONFIG_MTD_SST25L)	+= sst25l.o
>  obj-$(CONFIG_MTD_BCM47XXSFLASH)	+= bcm47xxsflash.o
>  obj-$(CONFIG_MTD_ST_SPI_FSM)    += st_spi_fsm.o
> -
> +obj-$(CONFIG_MTD_ALTERA_EPCQ)		+= altera_epcq.o
>  
>  CFLAGS_docg3.o			+= -I$(src)
> diff --git a/drivers/mtd/devices/altera_epcq.c b/drivers/mtd/devices/altera_epcq.c
> new file mode 100644
> index 0000000..09213d5
> --- /dev/null
> +++ b/drivers/mtd/devices/altera_epcq.c
> @@ -0,0 +1,804 @@
> +/*
> + * Copyright (C) 2014 Altera Corporation. All rights reserved
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/errno.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/ioport.h>
> +#include <linux/jiffies.h>
> +#include <linux/kernel.h>
> +#include <linux/log2.h>
> +#include <linux/module.h>
> +#include <linux/mtd/mtd.h>
> +#include <linux/mtd/partitions.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/param.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/wait.h>
> +
> +#include "altera_epcq.h"
> +
> +/* data structure to maintain flash ids from different vendors */
> +struct flash_device {
> +	char *name;
> +	bool is_epcs;
> +	u32 device_id;
> +	uint32_t sectorsize_inbytes;
> +	uint64_t size_inbytes;
> +	u32 pagesize;
> +};
> +
> +#define FLASH_ID(_n, _is_epcs, _id, _ssize, _size, _psize)	\
> +{				\
> +	.name = (_n),		\
> +	.is_epcs = (_is_epcs),		\
> +	.device_id = (_id),	\
> +	.sectorsize_inbytes = (_ssize),	\
> +	.size_inbytes = (_size),	\
> +	.pagesize = (_psize),	\
> +}
> +
> +static struct flash_device flash_devices[] = {
> +	FLASH_ID("epcq16"    , 0, 0x15, 0x10000, 0x200000, 0x100),
> +	FLASH_ID("epcq32"    , 0, 0x16, 0x10000, 0x400000, 0x100),
> +	FLASH_ID("epcq64"    , 0, 0x17, 0x10000, 0x800000, 0x100),
> +	FLASH_ID("epcq128"   , 0, 0x18, 0x10000, 0x1000000, 0x100),
> +	FLASH_ID("epcq256"   , 0, 0x19, 0x10000, 0x2000000, 0x100),
> +	FLASH_ID("epcq512"   , 0, 0x20, 0x10000, 0x4000000, 0x100),
> +
> +	FLASH_ID("epcs16"    , 1, 0x14, 0x10000, 0x200000, 0x100),
> +	FLASH_ID("epcs64"    , 1, 0x16, 0x10000, 0x800000, 0x100),
> +	FLASH_ID("epcs128"   , 1, 0x18, 0x40000, 0x1000000, 0x100),
> +};
> +
> +static inline struct altera_epcq_flash *get_flash_data(struct mtd_info *mtd)
> +{
> +	return container_of(mtd, struct altera_epcq_flash, mtd);
> +}
> +
> +static u32 altera_epcq_read_sr(struct altera_epcq *dev)
> +{
> +	return readl(dev->csr_base + EPCQ_SR_REG);
> +}
> +
> +static int altera_epcq_wait_till_ready(struct altera_epcq *dev)
> +{
> +	unsigned long finish;
> +	int status;
> +
> +	finish = jiffies + EPCQ_MAX_TIME_OUT;
> +	do {
> +		status = altera_epcq_read_sr(dev);
> +		if (status < 0)
> +			return status;
> +		else if (!(status & EPCQ_SR_WIP))
> +			return 0;
> +
> +		cond_resched();
> +	} while (!time_after_eq(jiffies, finish));
> +
> +	dev_err(&dev->pdev->dev, "epcq controller is busy, timeout\n");
> +	return -EBUSY;
> +}
> +
> +static int get_flash_index(u32 flash_id, bool is_epcs)
> +{
> +	int index;
> +
> +	for (index = 0; index < ARRAY_SIZE(flash_devices); index++) {
> +		if (flash_devices[index].device_id == flash_id &&
> +		    flash_devices[index].is_epcs == is_epcs)
> +			return index;
> +	}
> +
> +	/* Memory chip is not listed and not supported */
> +	return -ENODEV;
> +}
> +
> +static int altera_epcq_write_erase_check(struct altera_epcq *dev,
> +					 bool write_erase)
> +{
> +	u32 val;
> +	u32 mask;
> +
> +	if (write_erase)
> +		mask = EPCQ_ISR_ILLEGAL_WRITE_MASK;
> +	else
> +		mask = EPCQ_ISR_ILLEGAL_ERASE_MASK;
> +
> +	val = readl(dev->csr_base + EPCQ_ISR_REG);
> +	if (val & mask) {
> +		dev_err(&dev->pdev->dev,
> +			"write/erase failed, sector might be protected\n");
> +		/*clear this status for next use*/
> +		writel(val, dev->csr_base + EPCQ_ISR_REG);
> +		return -EIO;
> +	}
> +	return 0;
> +}
> +
> +static int altera_epcq_erase_chip(struct mtd_info *mtd)
> +{
> +	int ret;
> +	u32 val;
> +	struct altera_epcq *dev = mtd->priv;
> +
> +	/* Wait until finished previous write command. */
> +	ret = altera_epcq_wait_till_ready(dev);

This pattern is pretty silly. We don't need to have every operation
check the status of the previous operation. Each operation should check
itself. You obviously copied this one... but we recently changed that:

    commit dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3
    Author: Brian Norris <computersforpeace@gmail.com>
    Date:   Wed Aug 6 18:16:57 2014 -0700

        mtd: spi-nor: move "wait-till-ready" checks into erase/write
	functions


    https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=dfa9c0cba4ea20e766bbb4f89152b05d00ab9ab3

> +	if (ret)
> +		return ret;
> +
> +	/* erase chip. */
> +	val = EPCQ_MEM_OP_BULK_ERASE_CMD;
> +	writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
> +
> +	/* Wait until finished previous write command. */
> +	ret = altera_epcq_wait_till_ready(dev);
> +	if (ret)
> +		return ret;
> +
> +	 /* check whether write triggered a illegal write interrupt */
> +	ret = altera_epcq_write_erase_check(dev, 0);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int altera_epcq_addr_to_sector(struct altera_epcq_flash *flash,
> +				      int addr_offset)
> +{
> +	int sector = 0;
> +	int i;
> +
> +	for (i = 0; i < flash->num_sectors; i++) {
> +		if (addr_offset >= i * flash->mtd.erasesize && addr_offset <
> +		    (i * flash->mtd.erasesize + flash->mtd.erasesize)) {
> +			sector = i;
> +			return sector;
> +		}
> +	}

Uh, really? Am I crazy, or shouldn't this whole function just be a
really simple arithmetic operation? Like:

	return addr_offset >> mtd->erasesize_shift;

> +	return -1;
> +}
> +
> +static int altera_epcq_erase_sector(struct mtd_info *mtd,
> +				    int addr_offset)
> +{
> +	struct altera_epcq_flash *flash = get_flash_data(mtd);
> +	struct altera_epcq *dev = mtd->priv;
> +	u32 val;
> +	int ret;
> +	int sector_value;
> +
> +	ret = altera_epcq_wait_till_ready(dev);
> +	if (ret)
> +		return ret;
> +
> +	sector_value = altera_epcq_addr_to_sector(flash, addr_offset);
> +
> +	/* sanity check that block_offset is a valid sector number */
> +	if (sector_value < 0)
> +		return -EINVAL;
> +
> +	/* sector value should occupy bits 17:8 */
> +	val = (sector_value << 8) & EPCQ_MEM_OP_SECTOR_VALUE_MASK;
> +
> +	/* sector erase commands occupies lower 2 bits */
> +	val |= EPCQ_MEM_OP_SECTOR_ERASE_CMD;
> +
> +	/* write sector erase command to EPCQ_MEM_OP register*/
> +	writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
> +
> +	ret = altera_epcq_wait_till_ready(dev);
> +	if (ret)
> +		return ret;
> +
> +	 /* check whether write triggered a illegal write interrupt */

^^ extra space here? You have <TAB><SPACE>/* check [...]

> +	ret = altera_epcq_write_erase_check(dev, 0);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int altera_epcq_erase(struct mtd_info *mtd, struct erase_info *e_info)
> +{
> +	u32 addr;
> +	int ret;
> +	u32 len;
> +	struct altera_epcq_flash *flash = get_flash_data(mtd);
> +	struct altera_epcq *dev = mtd->priv;
> +
> +	addr = e_info->addr;
> +	len = e_info->len;
> +
> +	if (flash->bank > dev->num_flashes - 1) {
> +		dev_err(&dev->pdev->dev, "Invalid chip id\n");
> +		return -EINVAL;
> +	}
> +	mutex_lock(&flash->lock);
> +
> +	/*erase whole chip*/
> +	if (len == mtd->size) {
> +		if (altera_epcq_erase_chip(mtd)) {
> +			e_info->state = MTD_ERASE_FAILED;
> +			mutex_unlock(&flash->lock);
> +			return -EIO;
> +		}
> +	/*"sector"-at-a-time erase*/
> +	} else {
> +		while (len) {
> +			ret = altera_epcq_erase_sector(mtd, addr);
> +			if (ret) {
> +				e_info->state = MTD_ERASE_FAILED;
> +				mutex_unlock(&flash->lock);
> +				return -EIO;
> +			}
> +			addr += mtd->erasesize;
> +			if (len < mtd->erasesize)
> +				len = 0;
> +			else
> +				len -= mtd->erasesize;
> +		}
> +	}
> +	mutex_unlock(&flash->lock);
> +	e_info->state = MTD_ERASE_DONE;
> +	mtd_erase_callback(e_info);
> +
> +	return 0;
> +}
> +
> +static int altera_epcq_read(struct mtd_info *mtd, loff_t from, size_t len,
> +			    size_t *retlen, u8 *buf)
> +{
> +	struct altera_epcq_flash *flash = get_flash_data(mtd);
> +	struct altera_epcq *dev = mtd->priv;
> +	void *src;
> +	int ret = 0;
> +
> +	if (!flash || !dev)
> +		return -ENODEV;

Why would either of these be 0? If they are, you have much bigger
problems, since you aren't checking these almost anywhere else. And the
!flash check is pretty odd, since get_flash_data() is using
container_of(), which is not likely to give you exactly 0...

> +
> +	if (flash->bank > dev->num_flashes - 1) {
> +		dev_err(&dev->pdev->dev, "Invalid chip id\n");
> +		return -EINVAL;
> +	}
> +
> +	src = dev->data_base + from;
> +
> +	mutex_lock(&flash->lock);
> +	/* wait till previous write/erase is done. */
> +	ret = altera_epcq_wait_till_ready(dev);
> +	if (ret)
> +		goto err;
> +
> +	memcpy_fromio(buf, (u8 *)src, len);

Drop the cast. Also, you get sparse warnings because you're dropping the
__iomem. Why not just this?

	memcpy_fromio(buf, dev->data_base + from, len);

(BTW, you might consider running sparse on all your code. See
Documentation/sparse.txt. It tells you how to get it, but you can often
just download it through your distro's package manager.)

> +	*retlen = len;
> +
> +err:
> +	mutex_unlock(&flash->lock);
> +	return ret;
> +}
> +
> +static int altera_epcq_write(struct mtd_info *mtd, loff_t to, size_t len,
> +			     size_t *retlen, const u8 *buf)
> +{
> +	struct altera_epcq_flash *flash = get_flash_data(mtd);
> +	struct altera_epcq *dev = mtd->priv;
> +	void *dest;
> +	int ret = 0;
> +
> +	if (!flash || !dev)
> +		return -ENODEV;

Also here. I don't think you need these checks.

> +
> +	if (flash->bank > dev->num_flashes - 1) {
> +		dev_err(&dev->pdev->dev, "Invalid chip id\n");
> +		return -EINVAL;
> +	}
> +	dest = dev->data_base + to;
> +
> +	mutex_lock(&flash->lock);
> +
> +	/* wait until finished previous write command. */
> +	ret = altera_epcq_wait_till_ready(dev);
> +	if (ret)
> +		goto err;
> +
> +	memcpy_toio(dest, buf, len);

Similar. Why not just this?

	memcpy_toio(dev->data_base + to, buf, len);

> +	*retlen += len;
> +
> +	/* wait until finished previous write command. */
> +	ret = altera_epcq_wait_till_ready(dev);
> +	if (ret)
> +		goto err;
> +
> +	 /* check whether write triggered a illegal write interrupt */
> +	ret = altera_epcq_write_erase_check(dev, 1);
> +	if (ret < 0)
> +		goto err;
> +
> +err:
> +	mutex_unlock(&flash->lock);
> +	return ret;
> +}
> +
> +static int altera_epcq_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
> +{
> +	struct altera_epcq_flash *flash = get_flash_data(mtd);
> +	struct altera_epcq *dev = mtd->priv;
> +	uint32_t offset = ofs;
> +	int ret = 0;
> +	u32 sector_start, sector_end;
> +	u32 num_sectors;
> +	u32 mem_op;
> +	unsigned sr_bp = 0;
> +	unsigned sr_tb = 0;
> +
> +	sector_start = altera_epcq_addr_to_sector(flash, offset);
> +	sector_end = altera_epcq_addr_to_sector(flash, offset + len);
> +	num_sectors = flash->num_sectors;
> +	dev_dbg(&dev->pdev->dev,
> +		"%s: num_setor is %u,sector start is %u,sector end is %u\n",
> +		__func__, num_sectors, sector_start, sector_end);
> +
> +	mutex_lock(&flash->lock);
> +	/* wait until finished previous write command. */
> +	ret = altera_epcq_wait_till_ready(dev);
> +	if (ret)
> +		goto err;
> +
> +	if (sector_start >= num_sectors/2) {

It's preferable to have spaces around operators. So 'num_sectors / 2'.
The same thing comes up in several other places.

> +		if (sector_start < num_sectors-(num_sectors / 4))
> +			sr_bp = __ilog2_u32(num_sectors);
> +		else if (sector_start < num_sectors-(num_sectors / 8))
> +			sr_bp = __ilog2_u32(num_sectors) - 1;
> +		else if (sector_start < num_sectors-(num_sectors / 16))
> +			sr_bp = __ilog2_u32(num_sectors) - 2;
> +		else if (sector_start < num_sectors-(num_sectors / 32))
> +			sr_bp = __ilog2_u32(num_sectors) - 3;
> +		else if (sector_start < num_sectors-(num_sectors / 64))
> +			sr_bp = __ilog2_u32(num_sectors) - 4;
> +		else if (sector_start < num_sectors-(num_sectors / 128))
> +			sr_bp = __ilog2_u32(num_sectors) - 5;
> +		else if (sector_start < num_sectors-(num_sectors / 256))
> +			sr_bp = __ilog2_u32(num_sectors) - 6;
> +		else if (sector_start < num_sectors-(num_sectors / 512))
> +			sr_bp = __ilog2_u32(num_sectors) - 7;
> +		else if (sector_start < num_sectors-(num_sectors / 1024))
> +			sr_bp = __ilog2_u32(num_sectors) - 8;
> +		else
> +			sr_bp = 0;  /* non area protected */

Yikes, that's ugly! And I'm not sure it matches the EPCQ doc I found.
I'm pretty sure you can rewrite this if/else-if/else block in about 1
line though.

> +
> +		if (sr_bp < 0) {

sr_bp is unsigned, so this is never true.

> +			dev_err(&dev->pdev->dev, "%s: address is out of range\n"
> +				, __func__);
> +			ret = -EINVAL;
> +			goto err;
> +		}
> +		/*set TB = 0*/
> +		sr_tb = 0;
> +
> +	} else {
> +		if (sector_end < 1)
> +			sr_bp = 1;
> +		else if (sector_end < 2)
> +			sr_bp = 2;
> +		else if (sector_end < 4)
> +			sr_bp = 3;
> +		else if (sector_end < 8)
> +			sr_bp = 4;
> +		else if (sector_end < 16)
> +			sr_bp = 5;
> +		else if (sector_end < 32)
> +			sr_bp = 6;
> +		else if (sector_end < 64)
> +			sr_bp = 7;
> +		else if (sector_end < 128)
> +			sr_bp = 8;
> +		else if (sector_end < 256)
> +			sr_bp = 9;
> +		else if (sector_end < 512)
> +			sr_bp = 10;
> +		else
> +			sr_bp = 16; /*protect all areas*/

Again, this is ugly. How about this?

	sr_bp = fls(sector_end) + 1;

> +
> +		sr_tb = 1;
> +	}
> +
> +	mem_op = (sr_tb << 12) | (sr_bp << 8);
> +	mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
> +	mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
> +	writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
> +
> +err:
> +	mutex_unlock(&flash->lock);
> +	return ret;
> +}
> +
> +static int altera_epcq_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
> +{
> +	struct altera_epcq_flash *flash = get_flash_data(mtd);
> +	struct altera_epcq *dev = mtd->priv;
> +
> +	int ret = 0;
> +	u32 mem_op;
> +
> +	mutex_lock(&flash->lock);
> +	/* wait until finished previous write command. */
> +	ret = altera_epcq_wait_till_ready(dev);
> +	if (ret)
> +		goto err;
> +	dev_info(&dev->pdev->dev, "Unlock all protected area\n");
> +	mem_op = 0;
> +	mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
> +	mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
> +	writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
> +
> +err:
> +	mutex_unlock(&flash->lock);
> +	return ret;
> +}
> +
> +static void altera_epcq_chip_select(struct altera_epcq *dev, u32 bank)
> +{
> +	u32 val = 0;
> +
> +	switch (bank) {
> +	case 0:
> +		val = EPCQ_CHIP_SELECT_0;
> +		break;
> +	case 1:
> +		val = EPCQ_CHIP_SELECT_1;
> +		break;
> +	case 2:
> +		val = EPCQ_CHIP_SELECT_2;
> +		break;
> +	default:
> +		return;
> +	}
> +
> +	writel(val, dev->csr_base + EPCQ_CHIP_SELECT_REG);
> +}
> +
> +static int altera_epcq_probe_flash(struct altera_epcq *dev, u32 bank)
> +{
> +	int ret = 0;
> +	u32 val = 0;
> +
> +	mutex_lock(&dev->lock);
> +
> +	/*select bank*/

Comment spacing, here and elsewhere:

	/* select bank */

> +	altera_epcq_chip_select(dev, bank);
> +
> +	ret = altera_epcq_wait_till_ready(dev);
> +	if (ret)
> +		goto err;
> +
> +	/* get device sillicon id */
> +	if (dev->is_epcs)
> +		val = readl(dev->csr_base + EPCQ_SID_REG);
> +	else
> +		val = readl(dev->csr_base + EPCQ_RDID_REG);
> +
> +	/* get flash index based on the device list*/
> +	ret = get_flash_index(val, dev->is_epcs);
> +	return 0;
> +err:
> +	mutex_unlock(&dev->lock);
> +	return ret;
> +}
> +
> +static int altera_epcq_probe_config_dt(struct platform_device *pdev,
> +				       struct device_node *np,
> +				       struct altera_epcq_plat_data *pdata)
> +{
> +	struct device_node *pp = NULL;
> +	struct resource *epcq_res;
> +	int i = 0;
> +	u32 id;
> +
> +	pdata->is_epcs = of_property_read_bool(np, "is-epcs");
> +
> +	epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> +						"csr_base");
> +	if (!epcq_res) {

devm_ioremap_resource() will check the that 'epcq_res' is valid, so you
don't need this whole 'if' block.

> +		dev_err(&pdev->dev, "resource csr base is not defined\n");
> +		return -ENODEV;
> +	}
> +
> +	pdata->csr_base = devm_ioremap_resource(&pdev->dev, epcq_res);
> +	if (IS_ERR(pdata->csr_base)) {
> +		dev_err(&pdev->dev, "%s: ERROR: failed to map csr base\n",
> +			__func__);
> +		return PTR_ERR(pdata->csr_base);
> +	}
> +
> +	epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
> +						"data_base");
> +	if (!epcq_res) {

Same here.

> +		dev_err(&pdev->dev, "resource data base is not defined\n");
> +		return -ENODEV;
> +	}
> +
> +	pdata->data_base = devm_ioremap_resource(&pdev->dev, epcq_res);
> +	if (IS_ERR(pdata->data_base)) {
> +		dev_err(&pdev->dev, "%s: ERROR: failed to map data base\n",
> +			__func__);
> +		return PTR_ERR(pdata->data_base);
> +	}
> +
> +	pdata->board_flash_info = devm_kzalloc(&pdev->dev,
> +					       sizeof(*pdata->board_flash_info),
> +					       GFP_KERNEL);
> +
> +	/* Fill structs for each subnode (flash device) */
> +	while ((pp = of_get_next_child(np, pp))) {

for_each_available_child_of_node()?

> +		struct altera_epcq_flash_info *flash_info;
> +
> +		flash_info = &pdata->board_flash_info[i];

This is never used. Either use it below, or drop the local variable.

> +		pdata->np[i] = pp;
> +
> +		/* Read bank id from DT */
> +		if (of_get_property(pp, "reg", &id))

Is this property optional? Your DT binding doc doesn't make it clear,
but it seems like a property which would be wise to require (i.e., not
optional).

> +			pdata->board_flash_info[i].bank = id;
> +		i++;
> +	}
> +	pdata->num_flashes = i;
> +	return 0;
> +}
> +
> +static int altera_epcq_setup_banks(struct platform_device *pdev,
> +				   u32 bank, struct device_node *np,
> +				   struct altera_epcq_plat_data *pdata)
> +{
> +	struct altera_epcq *dev = platform_get_drvdata(pdev);
> +	struct mtd_part_parser_data ppdata = {};
> +	struct altera_epcq_flash_info *flash_info;
> +	struct altera_epcq_flash *flash;
> +	struct mtd_partition *parts = NULL;

Drop this variable. Just use NULL directly below.

> +	int count = 0;

Same. Just use 0 below.

> +	int flash_index;
> +	int ret = 0;
> +	uint64_t size;
> +	uint32_t sector_size;
> +
> +	flash_info = &pdata->board_flash_info[bank];
> +	if (!flash_info)
> +		return -ENODEV;
> +
> +	if (bank > pdata->num_flashes - 1)
> +		return -EINVAL;
> +
> +	flash = devm_kzalloc(&pdev->dev, sizeof(*flash), GFP_KERNEL);
> +	if (!flash)
> +		return -ENOMEM;
> +	flash->bank = bank;
> +
> +	mutex_init(&flash->lock);
> +
> +	/* verify whether flash is really present on board */
> +	flash_index = altera_epcq_probe_flash(dev, bank);
> +	if (flash_index < 0) {
> +		dev_info(&dev->pdev->dev, "epcq:flash%d not found\n",
> +			 flash->bank);
> +		return flash_index;
> +	}
> +
> +	dev->flash[bank] = flash;
> +
> +	size = flash_devices[flash_index].size_inbytes;
> +	sector_size = flash_devices[flash_index].sectorsize_inbytes;
> +	/*use do_div instead of plain div to avoid linker err*/
> +	do_div(size, sector_size);
> +	flash->num_sectors = size;
> +
> +	/*mtd framework */
> +	flash->mtd.priv = dev;
> +	flash->mtd.name = flash_devices[flash_index].name;
> +	flash->mtd.type = MTD_NORFLASH;
> +	flash->mtd.writesize = 1;
> +	flash->mtd.flags = MTD_CAP_NORFLASH;
> +	flash->mtd.size = flash_devices[flash_index].size_inbytes;
> +	flash->mtd.erasesize = flash_devices[flash_index].sectorsize_inbytes;
> +	flash->mtd._erase = altera_epcq_erase;
> +	flash->mtd._read = altera_epcq_read;
> +	flash->mtd._write = altera_epcq_write;
> +	flash->mtd._lock = altera_epcq_lock;
> +	flash->mtd._unlock = altera_epcq_unlock;
> +
> +	dev_info(&pdev->dev, "mtd .name=%s .size=0x%llx (%lluM)\n",
> +		 flash->mtd.name, (long long)flash->mtd.size,
> +		 (long long)(flash->mtd.size >> 20));
> +
> +	dev_info(&pdev->dev, ".erasesize = 0x%x(%uK)\n",
> +		 flash->mtd.erasesize, flash->mtd.erasesize >> 10);
> +
> +	ppdata.of_node = np;
> +
> +	ret = mtd_device_parse_register(&flash->mtd, NULL, &ppdata, parts,
> +					count);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Err MTD partition=%d\n", ret);
> +		goto err;

You don't want to unregister a MTD that failed to register. The MTD core
code should handle that itself.

Instead of this if (ret) {} block, I'd just make this:

	return mtd_device_parse_register(&flash->mtd, NULL, &ppdata, NULL, 0);

> +	}
> +
> +	return 0;
> +
> +err:
> +	mtd_device_unregister(&flash->mtd);
> +	return ret;
> +}
> +
> +static int altera_epcq_probe(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	struct altera_epcq_plat_data *pdata = NULL;
> +	struct altera_epcq *dev;
> +	int ret = 0;
> +	int i;
> +
> +	if (!np) {
> +		ret = -ENODEV;
> +		dev_err(&pdev->dev, "no device found\n");
> +		goto err;
> +	}
> +
> +	pdata = devm_kzalloc(&pdev->dev,
> +			     sizeof(struct altera_epcq_plat_data),
> +			     GFP_KERNEL);
> +
> +	if (!pdata) {
> +		ret = -ENOMEM;
> +		dev_err(&pdev->dev, "%s: ERROR: no memory\n", __func__);

Unnecessary print. The MM code will print plenty of info if you're
out of memory.

> +		goto err;
> +	}
> +	ret = altera_epcq_probe_config_dt(pdev, np, pdata);
> +	if (ret) {
> +		ret = -ENODEV;
> +		dev_err(&pdev->dev, "probe fail\n");
> +		goto err;
> +	}
> +
> +	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
> +	if (!dev) {
> +		ret = -ENOMEM;
> +		dev_err(&pdev->dev, "mem alloc fail\n");

Same here.

> +		goto err;
> +	}
> +	mutex_init(&dev->lock);
> +	dev->pdev = pdev;
> +	dev->is_epcs = pdata->is_epcs;
> +	dev->csr_base = pdata->csr_base;
> +	dev->data_base = pdata->data_base;
> +
> +	/*check number of flashes*/
> +	dev->num_flashes = pdata->num_flashes;
> +	if (dev->num_flashes > MAX_NUM_FLASH_CHIP) {
> +		dev_err(&pdev->dev, "exceeding max number of flashes\n");
> +		dev->num_flashes = MAX_NUM_FLASH_CHIP;
> +	}
> +
> +	/* check clock*/
> +	dev->clk = devm_clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(dev->clk)) {
> +		ret = PTR_ERR(dev->clk);
> +		goto err;
> +	}
> +	ret = clk_prepare_enable(dev->clk);
> +	if (ret)
> +		goto err;
> +
> +	platform_set_drvdata(pdev, dev);
> +
> +	/* loop for each serial flash which is connected to epcq */
> +	for (i = 0; i < dev->num_flashes; i++) {
> +		ret = altera_epcq_setup_banks(pdev, i, pdata->np[i], pdata);
> +		if (ret) {
> +			dev_err(&pdev->dev, "bank setup failed\n");
> +			goto err_bank_setup;
> +		}
> +	}
> +
> +	return 0;
> +
> +err_bank_setup:
> +	clk_disable_unprepare(dev->clk);
> +err:
> +	return ret;
> +}
> +
> +static int altera_epcq_remove(struct platform_device *pdev)
> +{
> +	struct altera_epcq *dev;
> +	struct altera_epcq_flash *flash;
> +	int ret, i;
> +
> +	dev = platform_get_drvdata(pdev);
> +
> +	/* clean up for all nor flash */
> +	for (i = 0; i < dev->num_flashes; i++) {
> +		flash = dev->flash[i];
> +		if (!flash)
> +			continue;
> +
> +		/* clean up mtd stuff */
> +		ret = mtd_device_unregister(&flash->mtd);
> +		if (ret)
> +			dev_err(&pdev->dev, "error removing mtd\n");
> +	}
> +
> +	clk_disable_unprepare(dev->clk);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM

Make this CONFIG_PM_SLEEP.

> +static int altera_epcq_suspend(struct device *dev)
> +{
> +	struct altera_epcq *sdev = dev_get_drvdata(dev);
> +
> +	clk_disable_unprepare(sdev->clk);
> +
> +	return 0;
> +}
> +
> +static int altera_epcq_resume(struct device *dev)
> +{
> +	struct altera_epcq *sdev = dev_get_drvdata(dev);
> +	int ret = -EPERM;

Drop 'ret'.

> +
> +	ret = clk_prepare_enable(sdev->clk);

Just:

	return clk_prepare_enable(sdev->clk);

> +
> +	return ret;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(altera_epcq_pm_ops, altera_epcq_suspend,
> +			 altera_epcq_resume);
> +#endif
> +
> +static const struct of_device_id altera_epcq_id_table[] = {
> +	{ .compatible = "altr,epcq-1.0" },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, altera_epcq_id_table);
> +
> +static struct platform_driver altera_epcq_driver = {
> +	.driver = {
> +		.name = ALTERA_EPCQ_RESOURCE_NAME,
> +		.bus = &platform_bus_type,
> +		.owner = THIS_MODULE,

The .owner assignment is no longer necessary.

> +		.of_match_table = altera_epcq_id_table,
> +#ifdef CONFIG_PM

Also CONFIG_PM_SLEEP.

> +		.pm = &altera_epcq_pm_ops,
> +#endif
> +	},
> +	.probe = altera_epcq_probe,
> +	.remove = altera_epcq_remove,
> +};
> +module_platform_driver(altera_epcq_driver);
> +
> +MODULE_AUTHOR("Viet Nga Dao <vndao@altera.com>");
> +MODULE_DESCRIPTION("MTD Altera EPCQ Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/mtd/devices/altera_epcq.h b/drivers/mtd/devices/altera_epcq.h
> new file mode 100644
> index 0000000..c3d15e5
> --- /dev/null
> +++ b/drivers/mtd/devices/altera_epcq.h
> @@ -0,0 +1,130 @@
> +/*
> + * Copyright (C) 2014 Altera Corporation. All rights reserved
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef __ALTERA_ECPQ_H
> +#define __ALTERA_ECPQ_H
> +
> +#define ALTERA_EPCQ_RESOURCE_NAME        "altera_epcq"
> +/* max possible slots for serial flash chip in the EPCQ controller */
> +#define MAX_NUM_FLASH_CHIP	3
> +
> +/* macro to define partitions for flash devices */
> +#define DEFINE_PARTS(n, of, s)		\

This macro is not used anywhere. Drop it?

> +{					\
> +	.name = n,			\
> +	.offset = of,			\
> +	.size = s,			\
> +}
> +
> +struct altera_epcq_flash_info {
> +	u32 bank;
> +	struct mtd_partition *partitions;
> +	int nr_partitions;
> +};
> +
> +struct altera_epcq_plat_data {
> +	void __iomem *csr_base;
> +	void __iomem *data_base;
> +	bool is_epcs;
> +	u32 num_flashes;
> +	struct altera_epcq_flash_info *board_flash_info;
> +	struct device_node *np[MAX_NUM_FLASH_CHIP];
> +};
> +
> +struct altera_epcq {
> +	struct clk *clk;
> +	bool is_epcs;
> +	struct mutex lock;	/*device lock*/
> +	void __iomem *csr_base;
> +	void __iomem *data_base;
> +	struct platform_device *pdev;
> +	u32 num_flashes;
> +	struct altera_epcq_flash *flash[MAX_NUM_FLASH_CHIP];
> +};
> +
> +struct altera_epcq_flash {
> +	u32 bank;
> +	u32 num_sectors;
> +	u32 num_parts;
> +	struct mtd_partition *parts;
> +	struct mutex lock;	/*flash lock*/
> +	struct mtd_info mtd;
> +};
> +
> +/* Define max times to check status register before we give up. */
> +#define EPCQ_MAX_TIME_OUT			(40 * HZ)
> +
> +/* defines for status register */
> +#define EPCQ_SR_REG				0x0
> +#define EPCQ_SR_WIP_MASK			0x00000001
> +#define EPCQ_SR_WIP				0x1
> +#define EPCQ_SR_WEL				0x2
> +#define EPCQ_SR_BP0				0x4
> +#define EPCQ_SR_BP1				0x8
> +#define EPCQ_SR_BP2				0x10
> +#define EPCQ_SR_BP3				0x40
> +#define EPCQ_SR_TB				0x20
> +
> +/* defines for device id register */
> +#define EPCQ_SID_REG				0x4
> +#define EPCQ_RDID_REG				0x8
> +#define EPCQ_RDID_MASK				0x000000FF
> +/*
> + * EPCQ_MEM_OP register offset
> + *
> + * The EPCQ_MEM_OP register is used to do memory protect and erase operations
> + *
> + */
> +#define EPCQ_MEM_OP_REG				0xC
> +
> +#define EPCQ_MEM_OP_CMD_MASK			0x00000003
> +#define EPCQ_MEM_OP_BULK_ERASE_CMD		0x00000001
> +#define EPCQ_MEM_OP_SECTOR_ERASE_CMD		0x00000002
> +#define EPCQ_MEM_OP_SECTOR_PROTECT_CMD		0x00000003
> +#define EPCQ_MEM_OP_SECTOR_VALUE_MASK		0x0003FF00
> +#define EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK	0x00001F00
> +#define EPCQ_MEM_OP_SECTOR_PROTECT_SHIFT	8
> +/*
> + * EPCQ_ISR register offset
> + *
> + * The EPCQ_ISR register is used to determine whether an invalid write or erase
> + * operation trigerred an interrupt
> + *
> + */
> +#define EPCQ_ISR_REG				0x10
> +
> +#define EPCQ_ISR_ILLEGAL_ERASE_MASK		0x00000001
> +#define EPCQ_ISR_ILLEGAL_WRITE_MASK		0x00000002
> +
> +/*
> + * EPCQ_IMR register offset
> + *
> + * The EPCQ_IMR register is used to mask the invalid erase or the invalid write
> + * interrupts.
> + *
> + */
> +#define EPCQ_IMR_REG				0x14
> +#define EPCQ_IMR_ILLEGAL_ERASE_MASK		0x00000001
> +
> +#define EPCQ_IMR_ILLEGAL_WRITE_MASK		0x00000002
> +
> +#define EPCQ_CHIP_SELECT_REG			0x18
> +#define EPCQ_CHIP_SELECT_MASK			0x00000007
> +#define EPCQ_CHIP_SELECT_0			0x00000001
> +#define EPCQ_CHIP_SELECT_1			0x00000002
> +#define EPCQ_CHIP_SELECT_2			0x00000004
> +
> +#endif /* __ALTERA_ECPQ_H */

Brian

^ permalink raw reply	[flat|nested] 39+ messages in thread

* RE: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
  2014-12-18  8:23 ` vndao
  (?)
@ 2015-01-13  1:21   ` Viet Nga Dao
  -1 siblings, 0 replies; 39+ messages in thread
From: Viet Nga Dao @ 2015-01-13  1:21 UTC (permalink / raw)
  To: dwmw2, computersforpeace; +Cc: linux-mtd, linux-kernel, devicetree, ngachi86

Hi Linux Community,
It is been a while since I submitted this patch. Could you please help me to review this driver?
Thanks and Regards,
Viet Nga Dao

-----Original Message-----
From: Viet Nga Dao
Sent: Thursday, December 18, 2014 4:23 PM
To: dwmw2@infradead.org; computersforpeace@gmail.com
Cc: linux-mtd@lists.infradead.org; linux-kernel@vger.kernel.org; devicetree@vger.kernel.org; ngachi86@gmail.com; Viet Nga Dao
Subject: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver

From: Viet Nga Dao <vndao@altera.com>

Altera EPCQ Controller is a soft IP which enables access to Altera EPCQ and EPCS flash chips. This patch adds driver for these devices.

Signed-off-by: Viet Nga Dao <vndao@altera.com>
---
 .../devicetree/bindings/mtd/altera_epcq.txt        |   45 ++
 drivers/mtd/devices/Kconfig                        |   12 +
 drivers/mtd/devices/Makefile                       |    2 +-
 drivers/mtd/devices/altera_epcq.c                  |  804 ++++++++++++++++++++
 drivers/mtd/devices/altera_epcq.h                  |  130 ++++
 5 files changed, 992 insertions(+), 1 deletions(-)  create mode 100644 Documentation/devicetree/bindings/mtd/altera_epcq.txt
 create mode 100644 drivers/mtd/devices/altera_epcq.c  create mode 100644 drivers/mtd/devices/altera_epcq.h

diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
new file mode 100644
index 0000000..d14f50e
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
@@ -0,0 +1,45 @@
+* MTD Altera EPCQ driver
+
+Required properties:
+- compatible: Should be "altr,epcq-1.0"
+- reg: Address and length of the register set  for the device. It
+contains
+  the information of registers in the same order as described by
+reg-names
+- reg-names: Should contain the reg names
+  "csr_base": Should contain the register configuration base address
+  "data_base": Should contain the data base address
+- is-epcs: boolean type.
+               If present, the device contains EPCS flashes.
+               Otherwise, it contains EPCQ flashes.
+- #address-cells: Must be <1>.
+- #size-cells: Must be <0>.
+- flash device tree subnode, there must be a node with the following fields:
+       - reg: Should contain the flash id
+       - #address-cells: please refer to /mtd/partition.txt
+       - #size-cells: please refer to /mtd/partition.txt
+       For partitions inside each flash, please refer to /mtd/partition.txt
+
+Example:
+
+                       epcq_controller_0: epcq@0x000000000 {
+                               compatible = "altr,epcq-1.0";
+                               reg = <0x00000001 0x00000000 0x00000020>,
+                                       <0x00000000 0x00000000 0x02000000>;
+                               reg-names = "csr_base", "data_base";
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               flash0: epcq256@0 {
+                                       reg = <0>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+                                       partition@0 {
+                                               /* 16 MB for raw data. */
+                                               label = "EPCQ Flash 0 raw data";
+                                               reg = <0x0 0x1000000>;
+                                       };
+                                       partition@1000000 {
+                                               /* 16 MB for jffs2 data. */
+                                               label = "EPCQ Flash 0 JFFS 2";
+                                               reg = <0x1000000 0x1000000>;
+                                       };
+                               };
+                       }; //end epcq@0x000000000 (epcq_controller_0)
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index c49d0b1..020b864 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -218,6 +218,18 @@ config MTD_ST_SPI_FSM
          SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
          for a subset of connected Serial Flash devices.

+config MTD_ALTERA_EPCQ
+       tristate "Support Altera EPCQ/EPCS Flash chips"
+       depends on OF
+       help
+         This enables access to Altera EPCQ/EPCS flash chips, used for data
+         storage. See the driver source for the current list,
+         or to add other chips.
+
+         If you want to compile this driver as a module ( = code which can be
+         inserted in and removed from the running kernel whenever you want),
+         say M here and read <file:Documentation/kbuild/modules.txt>.
+
 if MTD_DOCG3
 config BCH_CONST_M
        default 14
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index f0b0e61..b429c4d 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -16,6 +16,6 @@ obj-$(CONFIG_MTD_SPEAR_SMI)   += spear_smi.o
 obj-$(CONFIG_MTD_SST25L)       += sst25l.o
 obj-$(CONFIG_MTD_BCM47XXSFLASH)        += bcm47xxsflash.o
 obj-$(CONFIG_MTD_ST_SPI_FSM)    += st_spi_fsm.o
-
+obj-$(CONFIG_MTD_ALTERA_EPCQ)          += altera_epcq.o

 CFLAGS_docg3.o                 += -I$(src)
diff --git a/drivers/mtd/devices/altera_epcq.c b/drivers/mtd/devices/altera_epcq.c
new file mode 100644
index 0000000..09213d5
--- /dev/null
+++ b/drivers/mtd/devices/altera_epcq.c
@@ -0,0 +1,804 @@
+/*
+ * Copyright (C) 2014 Altera Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/param.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include "altera_epcq.h"
+
+/* data structure to maintain flash ids from different vendors */
+struct flash_device {
+       char *name;
+       bool is_epcs;
+       u32 device_id;
+       uint32_t sectorsize_inbytes;
+       uint64_t size_inbytes;
+       u32 pagesize;
+};
+
+#define FLASH_ID(_n, _is_epcs, _id, _ssize, _size, _psize)     \
+{                              \
+       .name = (_n),           \
+       .is_epcs = (_is_epcs),          \
+       .device_id = (_id),     \
+       .sectorsize_inbytes = (_ssize), \
+       .size_inbytes = (_size),        \
+       .pagesize = (_psize),   \
+}
+
+static struct flash_device flash_devices[] = {
+       FLASH_ID("epcq16"    , 0, 0x15, 0x10000, 0x200000, 0x100),
+       FLASH_ID("epcq32"    , 0, 0x16, 0x10000, 0x400000, 0x100),
+       FLASH_ID("epcq64"    , 0, 0x17, 0x10000, 0x800000, 0x100),
+       FLASH_ID("epcq128"   , 0, 0x18, 0x10000, 0x1000000, 0x100),
+       FLASH_ID("epcq256"   , 0, 0x19, 0x10000, 0x2000000, 0x100),
+       FLASH_ID("epcq512"   , 0, 0x20, 0x10000, 0x4000000, 0x100),
+
+       FLASH_ID("epcs16"    , 1, 0x14, 0x10000, 0x200000, 0x100),
+       FLASH_ID("epcs64"    , 1, 0x16, 0x10000, 0x800000, 0x100),
+       FLASH_ID("epcs128"   , 1, 0x18, 0x40000, 0x1000000, 0x100),
+};
+
+static inline struct altera_epcq_flash *get_flash_data(struct mtd_info
+*mtd) {
+       return container_of(mtd, struct altera_epcq_flash, mtd); }
+
+static u32 altera_epcq_read_sr(struct altera_epcq *dev) {
+       return readl(dev->csr_base + EPCQ_SR_REG); }
+
+static int altera_epcq_wait_till_ready(struct altera_epcq *dev) {
+       unsigned long finish;
+       int status;
+
+       finish = jiffies + EPCQ_MAX_TIME_OUT;
+       do {
+               status = altera_epcq_read_sr(dev);
+               if (status < 0)
+                       return status;
+               else if (!(status & EPCQ_SR_WIP))
+                       return 0;
+
+               cond_resched();
+       } while (!time_after_eq(jiffies, finish));
+
+       dev_err(&dev->pdev->dev, "epcq controller is busy, timeout\n");
+       return -EBUSY;
+}
+
+static int get_flash_index(u32 flash_id, bool is_epcs) {
+       int index;
+
+       for (index = 0; index < ARRAY_SIZE(flash_devices); index++) {
+               if (flash_devices[index].device_id == flash_id &&
+                   flash_devices[index].is_epcs == is_epcs)
+                       return index;
+       }
+
+       /* Memory chip is not listed and not supported */
+       return -ENODEV;
+}
+
+static int altera_epcq_write_erase_check(struct altera_epcq *dev,
+                                        bool write_erase)
+{
+       u32 val;
+       u32 mask;
+
+       if (write_erase)
+               mask = EPCQ_ISR_ILLEGAL_WRITE_MASK;
+       else
+               mask = EPCQ_ISR_ILLEGAL_ERASE_MASK;
+
+       val = readl(dev->csr_base + EPCQ_ISR_REG);
+       if (val & mask) {
+               dev_err(&dev->pdev->dev,
+                       "write/erase failed, sector might be protected\n");
+               /*clear this status for next use*/
+               writel(val, dev->csr_base + EPCQ_ISR_REG);
+               return -EIO;
+       }
+       return 0;
+}
+
+static int altera_epcq_erase_chip(struct mtd_info *mtd) {
+       int ret;
+       u32 val;
+       struct altera_epcq *dev = mtd->priv;
+
+       /* Wait until finished previous write command. */
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               return ret;
+
+       /* erase chip. */
+       val = EPCQ_MEM_OP_BULK_ERASE_CMD;
+       writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
+
+       /* Wait until finished previous write command. */
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               return ret;
+
+        /* check whether write triggered a illegal write interrupt */
+       ret = altera_epcq_write_erase_check(dev, 0);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int altera_epcq_addr_to_sector(struct altera_epcq_flash *flash,
+                                     int addr_offset)
+{
+       int sector = 0;
+       int i;
+
+       for (i = 0; i < flash->num_sectors; i++) {
+               if (addr_offset >= i * flash->mtd.erasesize && addr_offset <
+                   (i * flash->mtd.erasesize + flash->mtd.erasesize)) {
+                       sector = i;
+                       return sector;
+               }
+       }
+       return -1;
+}
+
+static int altera_epcq_erase_sector(struct mtd_info *mtd,
+                                   int addr_offset)
+{
+       struct altera_epcq_flash *flash = get_flash_data(mtd);
+       struct altera_epcq *dev = mtd->priv;
+       u32 val;
+       int ret;
+       int sector_value;
+
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               return ret;
+
+       sector_value = altera_epcq_addr_to_sector(flash, addr_offset);
+
+       /* sanity check that block_offset is a valid sector number */
+       if (sector_value < 0)
+               return -EINVAL;
+
+       /* sector value should occupy bits 17:8 */
+       val = (sector_value << 8) & EPCQ_MEM_OP_SECTOR_VALUE_MASK;
+
+       /* sector erase commands occupies lower 2 bits */
+       val |= EPCQ_MEM_OP_SECTOR_ERASE_CMD;
+
+       /* write sector erase command to EPCQ_MEM_OP register*/
+       writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
+
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               return ret;
+
+        /* check whether write triggered a illegal write interrupt */
+       ret = altera_epcq_write_erase_check(dev, 0);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int altera_epcq_erase(struct mtd_info *mtd, struct erase_info
+*e_info) {
+       u32 addr;
+       int ret;
+       u32 len;
+       struct altera_epcq_flash *flash = get_flash_data(mtd);
+       struct altera_epcq *dev = mtd->priv;
+
+       addr = e_info->addr;
+       len = e_info->len;
+
+       if (flash->bank > dev->num_flashes - 1) {
+               dev_err(&dev->pdev->dev, "Invalid chip id\n");
+               return -EINVAL;
+       }
+       mutex_lock(&flash->lock);
+
+       /*erase whole chip*/
+       if (len == mtd->size) {
+               if (altera_epcq_erase_chip(mtd)) {
+                       e_info->state = MTD_ERASE_FAILED;
+                       mutex_unlock(&flash->lock);
+                       return -EIO;
+               }
+       /*"sector"-at-a-time erase*/
+       } else {
+               while (len) {
+                       ret = altera_epcq_erase_sector(mtd, addr);
+                       if (ret) {
+                               e_info->state = MTD_ERASE_FAILED;
+                               mutex_unlock(&flash->lock);
+                               return -EIO;
+                       }
+                       addr += mtd->erasesize;
+                       if (len < mtd->erasesize)
+                               len = 0;
+                       else
+                               len -= mtd->erasesize;
+               }
+       }
+       mutex_unlock(&flash->lock);
+       e_info->state = MTD_ERASE_DONE;
+       mtd_erase_callback(e_info);
+
+       return 0;
+}
+
+static int altera_epcq_read(struct mtd_info *mtd, loff_t from, size_t len,
+                           size_t *retlen, u8 *buf)
+{
+       struct altera_epcq_flash *flash = get_flash_data(mtd);
+       struct altera_epcq *dev = mtd->priv;
+       void *src;
+       int ret = 0;
+
+       if (!flash || !dev)
+               return -ENODEV;
+
+       if (flash->bank > dev->num_flashes - 1) {
+               dev_err(&dev->pdev->dev, "Invalid chip id\n");
+               return -EINVAL;
+       }
+
+       src = dev->data_base + from;
+
+       mutex_lock(&flash->lock);
+       /* wait till previous write/erase is done. */
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               goto err;
+
+       memcpy_fromio(buf, (u8 *)src, len);
+       *retlen = len;
+
+err:
+       mutex_unlock(&flash->lock);
+       return ret;
+}
+
+static int altera_epcq_write(struct mtd_info *mtd, loff_t to, size_t len,
+                            size_t *retlen, const u8 *buf)
+{
+       struct altera_epcq_flash *flash = get_flash_data(mtd);
+       struct altera_epcq *dev = mtd->priv;
+       void *dest;
+       int ret = 0;
+
+       if (!flash || !dev)
+               return -ENODEV;
+
+       if (flash->bank > dev->num_flashes - 1) {
+               dev_err(&dev->pdev->dev, "Invalid chip id\n");
+               return -EINVAL;
+       }
+       dest = dev->data_base + to;
+
+       mutex_lock(&flash->lock);
+
+       /* wait until finished previous write command. */
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               goto err;
+
+       memcpy_toio(dest, buf, len);
+       *retlen += len;
+
+       /* wait until finished previous write command. */
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               goto err;
+
+        /* check whether write triggered a illegal write interrupt */
+       ret = altera_epcq_write_erase_check(dev, 1);
+       if (ret < 0)
+               goto err;
+
+err:
+       mutex_unlock(&flash->lock);
+       return ret;
+}
+
+static int altera_epcq_lock(struct mtd_info *mtd, loff_t ofs, uint64_t
+len) {
+       struct altera_epcq_flash *flash = get_flash_data(mtd);
+       struct altera_epcq *dev = mtd->priv;
+       uint32_t offset = ofs;
+       int ret = 0;
+       u32 sector_start, sector_end;
+       u32 num_sectors;
+       u32 mem_op;
+       unsigned sr_bp = 0;
+       unsigned sr_tb = 0;
+
+       sector_start = altera_epcq_addr_to_sector(flash, offset);
+       sector_end = altera_epcq_addr_to_sector(flash, offset + len);
+       num_sectors = flash->num_sectors;
+       dev_dbg(&dev->pdev->dev,
+               "%s: num_setor is %u,sector start is %u,sector end is %u\n",
+               __func__, num_sectors, sector_start, sector_end);
+
+       mutex_lock(&flash->lock);
+       /* wait until finished previous write command. */
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               goto err;
+
+       if (sector_start >= num_sectors/2) {
+               if (sector_start < num_sectors-(num_sectors / 4))
+                       sr_bp = __ilog2_u32(num_sectors);
+               else if (sector_start < num_sectors-(num_sectors / 8))
+                       sr_bp = __ilog2_u32(num_sectors) - 1;
+               else if (sector_start < num_sectors-(num_sectors / 16))
+                       sr_bp = __ilog2_u32(num_sectors) - 2;
+               else if (sector_start < num_sectors-(num_sectors / 32))
+                       sr_bp = __ilog2_u32(num_sectors) - 3;
+               else if (sector_start < num_sectors-(num_sectors / 64))
+                       sr_bp = __ilog2_u32(num_sectors) - 4;
+               else if (sector_start < num_sectors-(num_sectors / 128))
+                       sr_bp = __ilog2_u32(num_sectors) - 5;
+               else if (sector_start < num_sectors-(num_sectors / 256))
+                       sr_bp = __ilog2_u32(num_sectors) - 6;
+               else if (sector_start < num_sectors-(num_sectors / 512))
+                       sr_bp = __ilog2_u32(num_sectors) - 7;
+               else if (sector_start < num_sectors-(num_sectors / 1024))
+                       sr_bp = __ilog2_u32(num_sectors) - 8;
+               else
+                       sr_bp = 0;  /* non area protected */
+
+               if (sr_bp < 0) {
+                       dev_err(&dev->pdev->dev, "%s: address is out of range\n"
+                               , __func__);
+                       ret = -EINVAL;
+                       goto err;
+               }
+               /*set TB = 0*/
+               sr_tb = 0;
+
+       } else {
+               if (sector_end < 1)
+                       sr_bp = 1;
+               else if (sector_end < 2)
+                       sr_bp = 2;
+               else if (sector_end < 4)
+                       sr_bp = 3;
+               else if (sector_end < 8)
+                       sr_bp = 4;
+               else if (sector_end < 16)
+                       sr_bp = 5;
+               else if (sector_end < 32)
+                       sr_bp = 6;
+               else if (sector_end < 64)
+                       sr_bp = 7;
+               else if (sector_end < 128)
+                       sr_bp = 8;
+               else if (sector_end < 256)
+                       sr_bp = 9;
+               else if (sector_end < 512)
+                       sr_bp = 10;
+               else
+                       sr_bp = 16; /*protect all areas*/
+
+               sr_tb = 1;
+       }
+
+       mem_op = (sr_tb << 12) | (sr_bp << 8);
+       mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
+       mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
+       writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
+
+err:
+       mutex_unlock(&flash->lock);
+       return ret;
+}
+
+static int altera_epcq_unlock(struct mtd_info *mtd, loff_t ofs,
+uint64_t len) {
+       struct altera_epcq_flash *flash = get_flash_data(mtd);
+       struct altera_epcq *dev = mtd->priv;
+
+       int ret = 0;
+       u32 mem_op;
+
+       mutex_lock(&flash->lock);
+       /* wait until finished previous write command. */
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               goto err;
+       dev_info(&dev->pdev->dev, "Unlock all protected area\n");
+       mem_op = 0;
+       mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
+       mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
+       writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
+
+err:
+       mutex_unlock(&flash->lock);
+       return ret;
+}
+
+static void altera_epcq_chip_select(struct altera_epcq *dev, u32 bank)
+{
+       u32 val = 0;
+
+       switch (bank) {
+       case 0:
+               val = EPCQ_CHIP_SELECT_0;
+               break;
+       case 1:
+               val = EPCQ_CHIP_SELECT_1;
+               break;
+       case 2:
+               val = EPCQ_CHIP_SELECT_2;
+               break;
+       default:
+               return;
+       }
+
+       writel(val, dev->csr_base + EPCQ_CHIP_SELECT_REG); }
+
+static int altera_epcq_probe_flash(struct altera_epcq *dev, u32 bank) {
+       int ret = 0;
+       u32 val = 0;
+
+       mutex_lock(&dev->lock);
+
+       /*select bank*/
+       altera_epcq_chip_select(dev, bank);
+
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               goto err;
+
+       /* get device sillicon id */
+       if (dev->is_epcs)
+               val = readl(dev->csr_base + EPCQ_SID_REG);
+       else
+               val = readl(dev->csr_base + EPCQ_RDID_REG);
+
+       /* get flash index based on the device list*/
+       ret = get_flash_index(val, dev->is_epcs);
+       return 0;
+err:
+       mutex_unlock(&dev->lock);
+       return ret;
+}
+
+static int altera_epcq_probe_config_dt(struct platform_device *pdev,
+                                      struct device_node *np,
+                                      struct altera_epcq_plat_data *pdata) {
+       struct device_node *pp = NULL;
+       struct resource *epcq_res;
+       int i = 0;
+       u32 id;
+
+       pdata->is_epcs = of_property_read_bool(np, "is-epcs");
+
+       epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                               "csr_base");
+       if (!epcq_res) {
+               dev_err(&pdev->dev, "resource csr base is not defined\n");
+               return -ENODEV;
+       }
+
+       pdata->csr_base = devm_ioremap_resource(&pdev->dev, epcq_res);
+       if (IS_ERR(pdata->csr_base)) {
+               dev_err(&pdev->dev, "%s: ERROR: failed to map csr base\n",
+                       __func__);
+               return PTR_ERR(pdata->csr_base);
+       }
+
+       epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                               "data_base");
+       if (!epcq_res) {
+               dev_err(&pdev->dev, "resource data base is not defined\n");
+               return -ENODEV;
+       }
+
+       pdata->data_base = devm_ioremap_resource(&pdev->dev, epcq_res);
+       if (IS_ERR(pdata->data_base)) {
+               dev_err(&pdev->dev, "%s: ERROR: failed to map data base\n",
+                       __func__);
+               return PTR_ERR(pdata->data_base);
+       }
+
+       pdata->board_flash_info = devm_kzalloc(&pdev->dev,
+                                              sizeof(*pdata->board_flash_info),
+                                              GFP_KERNEL);
+
+       /* Fill structs for each subnode (flash device) */
+       while ((pp = of_get_next_child(np, pp))) {
+               struct altera_epcq_flash_info *flash_info;
+
+               flash_info = &pdata->board_flash_info[i];
+               pdata->np[i] = pp;
+
+               /* Read bank id from DT */
+               if (of_get_property(pp, "reg", &id))
+                       pdata->board_flash_info[i].bank = id;
+               i++;
+       }
+       pdata->num_flashes = i;
+       return 0;
+}
+
+static int altera_epcq_setup_banks(struct platform_device *pdev,
+                                  u32 bank, struct device_node *np,
+                                  struct altera_epcq_plat_data *pdata) {
+       struct altera_epcq *dev = platform_get_drvdata(pdev);
+       struct mtd_part_parser_data ppdata = {};
+       struct altera_epcq_flash_info *flash_info;
+       struct altera_epcq_flash *flash;
+       struct mtd_partition *parts = NULL;
+       int count = 0;
+       int flash_index;
+       int ret = 0;
+       uint64_t size;
+       uint32_t sector_size;
+
+       flash_info = &pdata->board_flash_info[bank];
+       if (!flash_info)
+               return -ENODEV;
+
+       if (bank > pdata->num_flashes - 1)
+               return -EINVAL;
+
+       flash = devm_kzalloc(&pdev->dev, sizeof(*flash), GFP_KERNEL);
+       if (!flash)
+               return -ENOMEM;
+       flash->bank = bank;
+
+       mutex_init(&flash->lock);
+
+       /* verify whether flash is really present on board */
+       flash_index = altera_epcq_probe_flash(dev, bank);
+       if (flash_index < 0) {
+               dev_info(&dev->pdev->dev, "epcq:flash%d not found\n",
+                        flash->bank);
+               return flash_index;
+       }
+
+       dev->flash[bank] = flash;
+
+       size = flash_devices[flash_index].size_inbytes;
+       sector_size = flash_devices[flash_index].sectorsize_inbytes;
+       /*use do_div instead of plain div to avoid linker err*/
+       do_div(size, sector_size);
+       flash->num_sectors = size;
+
+       /*mtd framework */
+       flash->mtd.priv = dev;
+       flash->mtd.name = flash_devices[flash_index].name;
+       flash->mtd.type = MTD_NORFLASH;
+       flash->mtd.writesize = 1;
+       flash->mtd.flags = MTD_CAP_NORFLASH;
+       flash->mtd.size = flash_devices[flash_index].size_inbytes;
+       flash->mtd.erasesize = flash_devices[flash_index].sectorsize_inbytes;
+       flash->mtd._erase = altera_epcq_erase;
+       flash->mtd._read = altera_epcq_read;
+       flash->mtd._write = altera_epcq_write;
+       flash->mtd._lock = altera_epcq_lock;
+       flash->mtd._unlock = altera_epcq_unlock;
+
+       dev_info(&pdev->dev, "mtd .name=%s .size=0x%llx (%lluM)\n",
+                flash->mtd.name, (long long)flash->mtd.size,
+                (long long)(flash->mtd.size >> 20));
+
+       dev_info(&pdev->dev, ".erasesize = 0x%x(%uK)\n",
+                flash->mtd.erasesize, flash->mtd.erasesize >> 10);
+
+       ppdata.of_node = np;
+
+       ret = mtd_device_parse_register(&flash->mtd, NULL, &ppdata, parts,
+                                       count);
+       if (ret) {
+               dev_err(&pdev->dev, "Err MTD partition=%d\n", ret);
+               goto err;
+       }
+
+       return 0;
+
+err:
+       mtd_device_unregister(&flash->mtd);
+       return ret;
+}
+
+static int altera_epcq_probe(struct platform_device *pdev) {
+       struct device_node *np = pdev->dev.of_node;
+       struct altera_epcq_plat_data *pdata = NULL;
+       struct altera_epcq *dev;
+       int ret = 0;
+       int i;
+
+       if (!np) {
+               ret = -ENODEV;
+               dev_err(&pdev->dev, "no device found\n");
+               goto err;
+       }
+
+       pdata = devm_kzalloc(&pdev->dev,
+                            sizeof(struct altera_epcq_plat_data),
+                            GFP_KERNEL);
+
+       if (!pdata) {
+               ret = -ENOMEM;
+               dev_err(&pdev->dev, "%s: ERROR: no memory\n", __func__);
+               goto err;
+       }
+       ret = altera_epcq_probe_config_dt(pdev, np, pdata);
+       if (ret) {
+               ret = -ENODEV;
+               dev_err(&pdev->dev, "probe fail\n");
+               goto err;
+       }
+
+       dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
+       if (!dev) {
+               ret = -ENOMEM;
+               dev_err(&pdev->dev, "mem alloc fail\n");
+               goto err;
+       }
+       mutex_init(&dev->lock);
+       dev->pdev = pdev;
+       dev->is_epcs = pdata->is_epcs;
+       dev->csr_base = pdata->csr_base;
+       dev->data_base = pdata->data_base;
+
+       /*check number of flashes*/
+       dev->num_flashes = pdata->num_flashes;
+       if (dev->num_flashes > MAX_NUM_FLASH_CHIP) {
+               dev_err(&pdev->dev, "exceeding max number of flashes\n");
+               dev->num_flashes = MAX_NUM_FLASH_CHIP;
+       }
+
+       /* check clock*/
+       dev->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(dev->clk)) {
+               ret = PTR_ERR(dev->clk);
+               goto err;
+       }
+       ret = clk_prepare_enable(dev->clk);
+       if (ret)
+               goto err;
+
+       platform_set_drvdata(pdev, dev);
+
+       /* loop for each serial flash which is connected to epcq */
+       for (i = 0; i < dev->num_flashes; i++) {
+               ret = altera_epcq_setup_banks(pdev, i, pdata->np[i], pdata);
+               if (ret) {
+                       dev_err(&pdev->dev, "bank setup failed\n");
+                       goto err_bank_setup;
+               }
+       }
+
+       return 0;
+
+err_bank_setup:
+       clk_disable_unprepare(dev->clk);
+err:
+       return ret;
+}
+
+static int altera_epcq_remove(struct platform_device *pdev) {
+       struct altera_epcq *dev;
+       struct altera_epcq_flash *flash;
+       int ret, i;
+
+       dev = platform_get_drvdata(pdev);
+
+       /* clean up for all nor flash */
+       for (i = 0; i < dev->num_flashes; i++) {
+               flash = dev->flash[i];
+               if (!flash)
+                       continue;
+
+               /* clean up mtd stuff */
+               ret = mtd_device_unregister(&flash->mtd);
+               if (ret)
+                       dev_err(&pdev->dev, "error removing mtd\n");
+       }
+
+       clk_disable_unprepare(dev->clk);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int altera_epcq_suspend(struct device *dev) {
+       struct altera_epcq *sdev = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(sdev->clk);
+
+       return 0;
+}
+
+static int altera_epcq_resume(struct device *dev) {
+       struct altera_epcq *sdev = dev_get_drvdata(dev);
+       int ret = -EPERM;
+
+       ret = clk_prepare_enable(sdev->clk);
+
+       return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(altera_epcq_pm_ops, altera_epcq_suspend,
+                        altera_epcq_resume);
+#endif
+
+static const struct of_device_id altera_epcq_id_table[] = {
+       { .compatible = "altr,epcq-1.0" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, altera_epcq_id_table);
+
+static struct platform_driver altera_epcq_driver = {
+       .driver = {
+               .name = ALTERA_EPCQ_RESOURCE_NAME,
+               .bus = &platform_bus_type,
+               .owner = THIS_MODULE,
+               .of_match_table = altera_epcq_id_table, #ifdef CONFIG_PM
+               .pm = &altera_epcq_pm_ops,
+#endif
+       },
+       .probe = altera_epcq_probe,
+       .remove = altera_epcq_remove,
+};
+module_platform_driver(altera_epcq_driver);
+
+MODULE_AUTHOR("Viet Nga Dao <vndao@altera.com>");
+MODULE_DESCRIPTION("MTD Altera EPCQ Driver"); MODULE_LICENSE("GPL v2");
diff --git a/drivers/mtd/devices/altera_epcq.h b/drivers/mtd/devices/altera_epcq.h
new file mode 100644
index 0000000..c3d15e5
--- /dev/null
+++ b/drivers/mtd/devices/altera_epcq.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2014 Altera Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ALTERA_ECPQ_H
+#define __ALTERA_ECPQ_H
+
+#define ALTERA_EPCQ_RESOURCE_NAME        "altera_epcq"
+/* max possible slots for serial flash chip in the EPCQ controller */
+#define MAX_NUM_FLASH_CHIP     3
+
+/* macro to define partitions for flash devices */
+#define DEFINE_PARTS(n, of, s)         \
+{                                      \
+       .name = n,                      \
+       .offset = of,                   \
+       .size = s,                      \
+}
+
+struct altera_epcq_flash_info {
+       u32 bank;
+       struct mtd_partition *partitions;
+       int nr_partitions;
+};
+
+struct altera_epcq_plat_data {
+       void __iomem *csr_base;
+       void __iomem *data_base;
+       bool is_epcs;
+       u32 num_flashes;
+       struct altera_epcq_flash_info *board_flash_info;
+       struct device_node *np[MAX_NUM_FLASH_CHIP]; };
+
+struct altera_epcq {
+       struct clk *clk;
+       bool is_epcs;
+       struct mutex lock;      /*device lock*/
+       void __iomem *csr_base;
+       void __iomem *data_base;
+       struct platform_device *pdev;
+       u32 num_flashes;
+       struct altera_epcq_flash *flash[MAX_NUM_FLASH_CHIP]; };
+
+struct altera_epcq_flash {
+       u32 bank;
+       u32 num_sectors;
+       u32 num_parts;
+       struct mtd_partition *parts;
+       struct mutex lock;      /*flash lock*/
+       struct mtd_info mtd;
+};
+
+/* Define max times to check status register before we give up. */
+#define EPCQ_MAX_TIME_OUT                      (40 * HZ)
+
+/* defines for status register */
+#define EPCQ_SR_REG                            0x0
+#define EPCQ_SR_WIP_MASK                       0x00000001
+#define EPCQ_SR_WIP                            0x1
+#define EPCQ_SR_WEL                            0x2
+#define EPCQ_SR_BP0                            0x4
+#define EPCQ_SR_BP1                            0x8
+#define EPCQ_SR_BP2                            0x10
+#define EPCQ_SR_BP3                            0x40
+#define EPCQ_SR_TB                             0x20
+
+/* defines for device id register */
+#define EPCQ_SID_REG                           0x4
+#define EPCQ_RDID_REG                          0x8
+#define EPCQ_RDID_MASK                         0x000000FF
+/*
+ * EPCQ_MEM_OP register offset
+ *
+ * The EPCQ_MEM_OP register is used to do memory protect and erase
+operations
+ *
+ */
+#define EPCQ_MEM_OP_REG                                0xC
+
+#define EPCQ_MEM_OP_CMD_MASK                   0x00000003
+#define EPCQ_MEM_OP_BULK_ERASE_CMD             0x00000001
+#define EPCQ_MEM_OP_SECTOR_ERASE_CMD           0x00000002
+#define EPCQ_MEM_OP_SECTOR_PROTECT_CMD         0x00000003
+#define EPCQ_MEM_OP_SECTOR_VALUE_MASK          0x0003FF00
+#define EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK  0x00001F00
+#define EPCQ_MEM_OP_SECTOR_PROTECT_SHIFT       8
+/*
+ * EPCQ_ISR register offset
+ *
+ * The EPCQ_ISR register is used to determine whether an invalid write
+or erase
+ * operation trigerred an interrupt
+ *
+ */
+#define EPCQ_ISR_REG                           0x10
+
+#define EPCQ_ISR_ILLEGAL_ERASE_MASK            0x00000001
+#define EPCQ_ISR_ILLEGAL_WRITE_MASK            0x00000002
+
+/*
+ * EPCQ_IMR register offset
+ *
+ * The EPCQ_IMR register is used to mask the invalid erase or the
+invalid write
+ * interrupts.
+ *
+ */
+#define EPCQ_IMR_REG                           0x14
+#define EPCQ_IMR_ILLEGAL_ERASE_MASK            0x00000001
+
+#define EPCQ_IMR_ILLEGAL_WRITE_MASK            0x00000002
+
+#define EPCQ_CHIP_SELECT_REG                   0x18
+#define EPCQ_CHIP_SELECT_MASK                  0x00000007
+#define EPCQ_CHIP_SELECT_0                     0x00000001
+#define EPCQ_CHIP_SELECT_1                     0x00000002
+#define EPCQ_CHIP_SELECT_2                     0x00000004
+
+#endif /* __ALTERA_ECPQ_H */
--
1.7.7.4


________________________________

Confidentiality Notice.
This message may contain information that is confidential or otherwise protected from disclosure. If you are not the intended recipient, you are hereby notified that any use, disclosure, dissemination, distribution, or copying of this message, or any attachments, is strictly prohibited. If you have received this message in error, please advise the sender by reply e-mail, and delete the message and any attachments. Thank you.

^ permalink raw reply related	[flat|nested] 39+ messages in thread

* RE: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-01-13  1:21   ` Viet Nga Dao
  0 siblings, 0 replies; 39+ messages in thread
From: Viet Nga Dao @ 2015-01-13  1:21 UTC (permalink / raw)
  To: dwmw2, computersforpeace; +Cc: devicetree, linux-mtd, linux-kernel, ngachi86

Hi Linux Community,
It is been a while since I submitted this patch. Could you please help me to review this driver?
Thanks and Regards,
Viet Nga Dao

-----Original Message-----
From: Viet Nga Dao
Sent: Thursday, December 18, 2014 4:23 PM
To: dwmw2@infradead.org; computersforpeace@gmail.com
Cc: linux-mtd@lists.infradead.org; linux-kernel@vger.kernel.org; devicetree@vger.kernel.org; ngachi86@gmail.com; Viet Nga Dao
Subject: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver

From: Viet Nga Dao <vndao@altera.com>

Altera EPCQ Controller is a soft IP which enables access to Altera EPCQ and EPCS flash chips. This patch adds driver for these devices.

Signed-off-by: Viet Nga Dao <vndao@altera.com>
---
 .../devicetree/bindings/mtd/altera_epcq.txt        |   45 ++
 drivers/mtd/devices/Kconfig                        |   12 +
 drivers/mtd/devices/Makefile                       |    2 +-
 drivers/mtd/devices/altera_epcq.c                  |  804 ++++++++++++++++++++
 drivers/mtd/devices/altera_epcq.h                  |  130 ++++
 5 files changed, 992 insertions(+), 1 deletions(-)  create mode 100644 Documentation/devicetree/bindings/mtd/altera_epcq.txt
 create mode 100644 drivers/mtd/devices/altera_epcq.c  create mode 100644 drivers/mtd/devices/altera_epcq.h

diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
new file mode 100644
index 0000000..d14f50e
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
@@ -0,0 +1,45 @@
+* MTD Altera EPCQ driver
+
+Required properties:
+- compatible: Should be "altr,epcq-1.0"
+- reg: Address and length of the register set  for the device. It
+contains
+  the information of registers in the same order as described by
+reg-names
+- reg-names: Should contain the reg names
+  "csr_base": Should contain the register configuration base address
+  "data_base": Should contain the data base address
+- is-epcs: boolean type.
+               If present, the device contains EPCS flashes.
+               Otherwise, it contains EPCQ flashes.
+- #address-cells: Must be <1>.
+- #size-cells: Must be <0>.
+- flash device tree subnode, there must be a node with the following fields:
+       - reg: Should contain the flash id
+       - #address-cells: please refer to /mtd/partition.txt
+       - #size-cells: please refer to /mtd/partition.txt
+       For partitions inside each flash, please refer to /mtd/partition.txt
+
+Example:
+
+                       epcq_controller_0: epcq@0x000000000 {
+                               compatible = "altr,epcq-1.0";
+                               reg = <0x00000001 0x00000000 0x00000020>,
+                                       <0x00000000 0x00000000 0x02000000>;
+                               reg-names = "csr_base", "data_base";
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               flash0: epcq256@0 {
+                                       reg = <0>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+                                       partition@0 {
+                                               /* 16 MB for raw data. */
+                                               label = "EPCQ Flash 0 raw data";
+                                               reg = <0x0 0x1000000>;
+                                       };
+                                       partition@1000000 {
+                                               /* 16 MB for jffs2 data. */
+                                               label = "EPCQ Flash 0 JFFS 2";
+                                               reg = <0x1000000 0x1000000>;
+                                       };
+                               };
+                       }; //end epcq@0x000000000 (epcq_controller_0)
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index c49d0b1..020b864 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -218,6 +218,18 @@ config MTD_ST_SPI_FSM
          SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
          for a subset of connected Serial Flash devices.

+config MTD_ALTERA_EPCQ
+       tristate "Support Altera EPCQ/EPCS Flash chips"
+       depends on OF
+       help
+         This enables access to Altera EPCQ/EPCS flash chips, used for data
+         storage. See the driver source for the current list,
+         or to add other chips.
+
+         If you want to compile this driver as a module ( = code which can be
+         inserted in and removed from the running kernel whenever you want),
+         say M here and read <file:Documentation/kbuild/modules.txt>.
+
 if MTD_DOCG3
 config BCH_CONST_M
        default 14
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index f0b0e61..b429c4d 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -16,6 +16,6 @@ obj-$(CONFIG_MTD_SPEAR_SMI)   += spear_smi.o
 obj-$(CONFIG_MTD_SST25L)       += sst25l.o
 obj-$(CONFIG_MTD_BCM47XXSFLASH)        += bcm47xxsflash.o
 obj-$(CONFIG_MTD_ST_SPI_FSM)    += st_spi_fsm.o
-
+obj-$(CONFIG_MTD_ALTERA_EPCQ)          += altera_epcq.o

 CFLAGS_docg3.o                 += -I$(src)
diff --git a/drivers/mtd/devices/altera_epcq.c b/drivers/mtd/devices/altera_epcq.c
new file mode 100644
index 0000000..09213d5
--- /dev/null
+++ b/drivers/mtd/devices/altera_epcq.c
@@ -0,0 +1,804 @@
+/*
+ * Copyright (C) 2014 Altera Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/param.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include "altera_epcq.h"
+
+/* data structure to maintain flash ids from different vendors */
+struct flash_device {
+       char *name;
+       bool is_epcs;
+       u32 device_id;
+       uint32_t sectorsize_inbytes;
+       uint64_t size_inbytes;
+       u32 pagesize;
+};
+
+#define FLASH_ID(_n, _is_epcs, _id, _ssize, _size, _psize)     \
+{                              \
+       .name = (_n),           \
+       .is_epcs = (_is_epcs),          \
+       .device_id = (_id),     \
+       .sectorsize_inbytes = (_ssize), \
+       .size_inbytes = (_size),        \
+       .pagesize = (_psize),   \
+}
+
+static struct flash_device flash_devices[] = {
+       FLASH_ID("epcq16"    , 0, 0x15, 0x10000, 0x200000, 0x100),
+       FLASH_ID("epcq32"    , 0, 0x16, 0x10000, 0x400000, 0x100),
+       FLASH_ID("epcq64"    , 0, 0x17, 0x10000, 0x800000, 0x100),
+       FLASH_ID("epcq128"   , 0, 0x18, 0x10000, 0x1000000, 0x100),
+       FLASH_ID("epcq256"   , 0, 0x19, 0x10000, 0x2000000, 0x100),
+       FLASH_ID("epcq512"   , 0, 0x20, 0x10000, 0x4000000, 0x100),
+
+       FLASH_ID("epcs16"    , 1, 0x14, 0x10000, 0x200000, 0x100),
+       FLASH_ID("epcs64"    , 1, 0x16, 0x10000, 0x800000, 0x100),
+       FLASH_ID("epcs128"   , 1, 0x18, 0x40000, 0x1000000, 0x100),
+};
+
+static inline struct altera_epcq_flash *get_flash_data(struct mtd_info
+*mtd) {
+       return container_of(mtd, struct altera_epcq_flash, mtd); }
+
+static u32 altera_epcq_read_sr(struct altera_epcq *dev) {
+       return readl(dev->csr_base + EPCQ_SR_REG); }
+
+static int altera_epcq_wait_till_ready(struct altera_epcq *dev) {
+       unsigned long finish;
+       int status;
+
+       finish = jiffies + EPCQ_MAX_TIME_OUT;
+       do {
+               status = altera_epcq_read_sr(dev);
+               if (status < 0)
+                       return status;
+               else if (!(status & EPCQ_SR_WIP))
+                       return 0;
+
+               cond_resched();
+       } while (!time_after_eq(jiffies, finish));
+
+       dev_err(&dev->pdev->dev, "epcq controller is busy, timeout\n");
+       return -EBUSY;
+}
+
+static int get_flash_index(u32 flash_id, bool is_epcs) {
+       int index;
+
+       for (index = 0; index < ARRAY_SIZE(flash_devices); index++) {
+               if (flash_devices[index].device_id == flash_id &&
+                   flash_devices[index].is_epcs == is_epcs)
+                       return index;
+       }
+
+       /* Memory chip is not listed and not supported */
+       return -ENODEV;
+}
+
+static int altera_epcq_write_erase_check(struct altera_epcq *dev,
+                                        bool write_erase)
+{
+       u32 val;
+       u32 mask;
+
+       if (write_erase)
+               mask = EPCQ_ISR_ILLEGAL_WRITE_MASK;
+       else
+               mask = EPCQ_ISR_ILLEGAL_ERASE_MASK;
+
+       val = readl(dev->csr_base + EPCQ_ISR_REG);
+       if (val & mask) {
+               dev_err(&dev->pdev->dev,
+                       "write/erase failed, sector might be protected\n");
+               /*clear this status for next use*/
+               writel(val, dev->csr_base + EPCQ_ISR_REG);
+               return -EIO;
+       }
+       return 0;
+}
+
+static int altera_epcq_erase_chip(struct mtd_info *mtd) {
+       int ret;
+       u32 val;
+       struct altera_epcq *dev = mtd->priv;
+
+       /* Wait until finished previous write command. */
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               return ret;
+
+       /* erase chip. */
+       val = EPCQ_MEM_OP_BULK_ERASE_CMD;
+       writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
+
+       /* Wait until finished previous write command. */
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               return ret;
+
+        /* check whether write triggered a illegal write interrupt */
+       ret = altera_epcq_write_erase_check(dev, 0);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int altera_epcq_addr_to_sector(struct altera_epcq_flash *flash,
+                                     int addr_offset)
+{
+       int sector = 0;
+       int i;
+
+       for (i = 0; i < flash->num_sectors; i++) {
+               if (addr_offset >= i * flash->mtd.erasesize && addr_offset <
+                   (i * flash->mtd.erasesize + flash->mtd.erasesize)) {
+                       sector = i;
+                       return sector;
+               }
+       }
+       return -1;
+}
+
+static int altera_epcq_erase_sector(struct mtd_info *mtd,
+                                   int addr_offset)
+{
+       struct altera_epcq_flash *flash = get_flash_data(mtd);
+       struct altera_epcq *dev = mtd->priv;
+       u32 val;
+       int ret;
+       int sector_value;
+
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               return ret;
+
+       sector_value = altera_epcq_addr_to_sector(flash, addr_offset);
+
+       /* sanity check that block_offset is a valid sector number */
+       if (sector_value < 0)
+               return -EINVAL;
+
+       /* sector value should occupy bits 17:8 */
+       val = (sector_value << 8) & EPCQ_MEM_OP_SECTOR_VALUE_MASK;
+
+       /* sector erase commands occupies lower 2 bits */
+       val |= EPCQ_MEM_OP_SECTOR_ERASE_CMD;
+
+       /* write sector erase command to EPCQ_MEM_OP register*/
+       writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
+
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               return ret;
+
+        /* check whether write triggered a illegal write interrupt */
+       ret = altera_epcq_write_erase_check(dev, 0);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int altera_epcq_erase(struct mtd_info *mtd, struct erase_info
+*e_info) {
+       u32 addr;
+       int ret;
+       u32 len;
+       struct altera_epcq_flash *flash = get_flash_data(mtd);
+       struct altera_epcq *dev = mtd->priv;
+
+       addr = e_info->addr;
+       len = e_info->len;
+
+       if (flash->bank > dev->num_flashes - 1) {
+               dev_err(&dev->pdev->dev, "Invalid chip id\n");
+               return -EINVAL;
+       }
+       mutex_lock(&flash->lock);
+
+       /*erase whole chip*/
+       if (len == mtd->size) {
+               if (altera_epcq_erase_chip(mtd)) {
+                       e_info->state = MTD_ERASE_FAILED;
+                       mutex_unlock(&flash->lock);
+                       return -EIO;
+               }
+       /*"sector"-at-a-time erase*/
+       } else {
+               while (len) {
+                       ret = altera_epcq_erase_sector(mtd, addr);
+                       if (ret) {
+                               e_info->state = MTD_ERASE_FAILED;
+                               mutex_unlock(&flash->lock);
+                               return -EIO;
+                       }
+                       addr += mtd->erasesize;
+                       if (len < mtd->erasesize)
+                               len = 0;
+                       else
+                               len -= mtd->erasesize;
+               }
+       }
+       mutex_unlock(&flash->lock);
+       e_info->state = MTD_ERASE_DONE;
+       mtd_erase_callback(e_info);
+
+       return 0;
+}
+
+static int altera_epcq_read(struct mtd_info *mtd, loff_t from, size_t len,
+                           size_t *retlen, u8 *buf)
+{
+       struct altera_epcq_flash *flash = get_flash_data(mtd);
+       struct altera_epcq *dev = mtd->priv;
+       void *src;
+       int ret = 0;
+
+       if (!flash || !dev)
+               return -ENODEV;
+
+       if (flash->bank > dev->num_flashes - 1) {
+               dev_err(&dev->pdev->dev, "Invalid chip id\n");
+               return -EINVAL;
+       }
+
+       src = dev->data_base + from;
+
+       mutex_lock(&flash->lock);
+       /* wait till previous write/erase is done. */
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               goto err;
+
+       memcpy_fromio(buf, (u8 *)src, len);
+       *retlen = len;
+
+err:
+       mutex_unlock(&flash->lock);
+       return ret;
+}
+
+static int altera_epcq_write(struct mtd_info *mtd, loff_t to, size_t len,
+                            size_t *retlen, const u8 *buf)
+{
+       struct altera_epcq_flash *flash = get_flash_data(mtd);
+       struct altera_epcq *dev = mtd->priv;
+       void *dest;
+       int ret = 0;
+
+       if (!flash || !dev)
+               return -ENODEV;
+
+       if (flash->bank > dev->num_flashes - 1) {
+               dev_err(&dev->pdev->dev, "Invalid chip id\n");
+               return -EINVAL;
+       }
+       dest = dev->data_base + to;
+
+       mutex_lock(&flash->lock);
+
+       /* wait until finished previous write command. */
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               goto err;
+
+       memcpy_toio(dest, buf, len);
+       *retlen += len;
+
+       /* wait until finished previous write command. */
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               goto err;
+
+        /* check whether write triggered a illegal write interrupt */
+       ret = altera_epcq_write_erase_check(dev, 1);
+       if (ret < 0)
+               goto err;
+
+err:
+       mutex_unlock(&flash->lock);
+       return ret;
+}
+
+static int altera_epcq_lock(struct mtd_info *mtd, loff_t ofs, uint64_t
+len) {
+       struct altera_epcq_flash *flash = get_flash_data(mtd);
+       struct altera_epcq *dev = mtd->priv;
+       uint32_t offset = ofs;
+       int ret = 0;
+       u32 sector_start, sector_end;
+       u32 num_sectors;
+       u32 mem_op;
+       unsigned sr_bp = 0;
+       unsigned sr_tb = 0;
+
+       sector_start = altera_epcq_addr_to_sector(flash, offset);
+       sector_end = altera_epcq_addr_to_sector(flash, offset + len);
+       num_sectors = flash->num_sectors;
+       dev_dbg(&dev->pdev->dev,
+               "%s: num_setor is %u,sector start is %u,sector end is %u\n",
+               __func__, num_sectors, sector_start, sector_end);
+
+       mutex_lock(&flash->lock);
+       /* wait until finished previous write command. */
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               goto err;
+
+       if (sector_start >= num_sectors/2) {
+               if (sector_start < num_sectors-(num_sectors / 4))
+                       sr_bp = __ilog2_u32(num_sectors);
+               else if (sector_start < num_sectors-(num_sectors / 8))
+                       sr_bp = __ilog2_u32(num_sectors) - 1;
+               else if (sector_start < num_sectors-(num_sectors / 16))
+                       sr_bp = __ilog2_u32(num_sectors) - 2;
+               else if (sector_start < num_sectors-(num_sectors / 32))
+                       sr_bp = __ilog2_u32(num_sectors) - 3;
+               else if (sector_start < num_sectors-(num_sectors / 64))
+                       sr_bp = __ilog2_u32(num_sectors) - 4;
+               else if (sector_start < num_sectors-(num_sectors / 128))
+                       sr_bp = __ilog2_u32(num_sectors) - 5;
+               else if (sector_start < num_sectors-(num_sectors / 256))
+                       sr_bp = __ilog2_u32(num_sectors) - 6;
+               else if (sector_start < num_sectors-(num_sectors / 512))
+                       sr_bp = __ilog2_u32(num_sectors) - 7;
+               else if (sector_start < num_sectors-(num_sectors / 1024))
+                       sr_bp = __ilog2_u32(num_sectors) - 8;
+               else
+                       sr_bp = 0;  /* non area protected */
+
+               if (sr_bp < 0) {
+                       dev_err(&dev->pdev->dev, "%s: address is out of range\n"
+                               , __func__);
+                       ret = -EINVAL;
+                       goto err;
+               }
+               /*set TB = 0*/
+               sr_tb = 0;
+
+       } else {
+               if (sector_end < 1)
+                       sr_bp = 1;
+               else if (sector_end < 2)
+                       sr_bp = 2;
+               else if (sector_end < 4)
+                       sr_bp = 3;
+               else if (sector_end < 8)
+                       sr_bp = 4;
+               else if (sector_end < 16)
+                       sr_bp = 5;
+               else if (sector_end < 32)
+                       sr_bp = 6;
+               else if (sector_end < 64)
+                       sr_bp = 7;
+               else if (sector_end < 128)
+                       sr_bp = 8;
+               else if (sector_end < 256)
+                       sr_bp = 9;
+               else if (sector_end < 512)
+                       sr_bp = 10;
+               else
+                       sr_bp = 16; /*protect all areas*/
+
+               sr_tb = 1;
+       }
+
+       mem_op = (sr_tb << 12) | (sr_bp << 8);
+       mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
+       mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
+       writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
+
+err:
+       mutex_unlock(&flash->lock);
+       return ret;
+}
+
+static int altera_epcq_unlock(struct mtd_info *mtd, loff_t ofs,
+uint64_t len) {
+       struct altera_epcq_flash *flash = get_flash_data(mtd);
+       struct altera_epcq *dev = mtd->priv;
+
+       int ret = 0;
+       u32 mem_op;
+
+       mutex_lock(&flash->lock);
+       /* wait until finished previous write command. */
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               goto err;
+       dev_info(&dev->pdev->dev, "Unlock all protected area\n");
+       mem_op = 0;
+       mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
+       mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
+       writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
+
+err:
+       mutex_unlock(&flash->lock);
+       return ret;
+}
+
+static void altera_epcq_chip_select(struct altera_epcq *dev, u32 bank)
+{
+       u32 val = 0;
+
+       switch (bank) {
+       case 0:
+               val = EPCQ_CHIP_SELECT_0;
+               break;
+       case 1:
+               val = EPCQ_CHIP_SELECT_1;
+               break;
+       case 2:
+               val = EPCQ_CHIP_SELECT_2;
+               break;
+       default:
+               return;
+       }
+
+       writel(val, dev->csr_base + EPCQ_CHIP_SELECT_REG); }
+
+static int altera_epcq_probe_flash(struct altera_epcq *dev, u32 bank) {
+       int ret = 0;
+       u32 val = 0;
+
+       mutex_lock(&dev->lock);
+
+       /*select bank*/
+       altera_epcq_chip_select(dev, bank);
+
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               goto err;
+
+       /* get device sillicon id */
+       if (dev->is_epcs)
+               val = readl(dev->csr_base + EPCQ_SID_REG);
+       else
+               val = readl(dev->csr_base + EPCQ_RDID_REG);
+
+       /* get flash index based on the device list*/
+       ret = get_flash_index(val, dev->is_epcs);
+       return 0;
+err:
+       mutex_unlock(&dev->lock);
+       return ret;
+}
+
+static int altera_epcq_probe_config_dt(struct platform_device *pdev,
+                                      struct device_node *np,
+                                      struct altera_epcq_plat_data *pdata) {
+       struct device_node *pp = NULL;
+       struct resource *epcq_res;
+       int i = 0;
+       u32 id;
+
+       pdata->is_epcs = of_property_read_bool(np, "is-epcs");
+
+       epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                               "csr_base");
+       if (!epcq_res) {
+               dev_err(&pdev->dev, "resource csr base is not defined\n");
+               return -ENODEV;
+       }
+
+       pdata->csr_base = devm_ioremap_resource(&pdev->dev, epcq_res);
+       if (IS_ERR(pdata->csr_base)) {
+               dev_err(&pdev->dev, "%s: ERROR: failed to map csr base\n",
+                       __func__);
+               return PTR_ERR(pdata->csr_base);
+       }
+
+       epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                               "data_base");
+       if (!epcq_res) {
+               dev_err(&pdev->dev, "resource data base is not defined\n");
+               return -ENODEV;
+       }
+
+       pdata->data_base = devm_ioremap_resource(&pdev->dev, epcq_res);
+       if (IS_ERR(pdata->data_base)) {
+               dev_err(&pdev->dev, "%s: ERROR: failed to map data base\n",
+                       __func__);
+               return PTR_ERR(pdata->data_base);
+       }
+
+       pdata->board_flash_info = devm_kzalloc(&pdev->dev,
+                                              sizeof(*pdata->board_flash_info),
+                                              GFP_KERNEL);
+
+       /* Fill structs for each subnode (flash device) */
+       while ((pp = of_get_next_child(np, pp))) {
+               struct altera_epcq_flash_info *flash_info;
+
+               flash_info = &pdata->board_flash_info[i];
+               pdata->np[i] = pp;
+
+               /* Read bank id from DT */
+               if (of_get_property(pp, "reg", &id))
+                       pdata->board_flash_info[i].bank = id;
+               i++;
+       }
+       pdata->num_flashes = i;
+       return 0;
+}
+
+static int altera_epcq_setup_banks(struct platform_device *pdev,
+                                  u32 bank, struct device_node *np,
+                                  struct altera_epcq_plat_data *pdata) {
+       struct altera_epcq *dev = platform_get_drvdata(pdev);
+       struct mtd_part_parser_data ppdata = {};
+       struct altera_epcq_flash_info *flash_info;
+       struct altera_epcq_flash *flash;
+       struct mtd_partition *parts = NULL;
+       int count = 0;
+       int flash_index;
+       int ret = 0;
+       uint64_t size;
+       uint32_t sector_size;
+
+       flash_info = &pdata->board_flash_info[bank];
+       if (!flash_info)
+               return -ENODEV;
+
+       if (bank > pdata->num_flashes - 1)
+               return -EINVAL;
+
+       flash = devm_kzalloc(&pdev->dev, sizeof(*flash), GFP_KERNEL);
+       if (!flash)
+               return -ENOMEM;
+       flash->bank = bank;
+
+       mutex_init(&flash->lock);
+
+       /* verify whether flash is really present on board */
+       flash_index = altera_epcq_probe_flash(dev, bank);
+       if (flash_index < 0) {
+               dev_info(&dev->pdev->dev, "epcq:flash%d not found\n",
+                        flash->bank);
+               return flash_index;
+       }
+
+       dev->flash[bank] = flash;
+
+       size = flash_devices[flash_index].size_inbytes;
+       sector_size = flash_devices[flash_index].sectorsize_inbytes;
+       /*use do_div instead of plain div to avoid linker err*/
+       do_div(size, sector_size);
+       flash->num_sectors = size;
+
+       /*mtd framework */
+       flash->mtd.priv = dev;
+       flash->mtd.name = flash_devices[flash_index].name;
+       flash->mtd.type = MTD_NORFLASH;
+       flash->mtd.writesize = 1;
+       flash->mtd.flags = MTD_CAP_NORFLASH;
+       flash->mtd.size = flash_devices[flash_index].size_inbytes;
+       flash->mtd.erasesize = flash_devices[flash_index].sectorsize_inbytes;
+       flash->mtd._erase = altera_epcq_erase;
+       flash->mtd._read = altera_epcq_read;
+       flash->mtd._write = altera_epcq_write;
+       flash->mtd._lock = altera_epcq_lock;
+       flash->mtd._unlock = altera_epcq_unlock;
+
+       dev_info(&pdev->dev, "mtd .name=%s .size=0x%llx (%lluM)\n",
+                flash->mtd.name, (long long)flash->mtd.size,
+                (long long)(flash->mtd.size >> 20));
+
+       dev_info(&pdev->dev, ".erasesize = 0x%x(%uK)\n",
+                flash->mtd.erasesize, flash->mtd.erasesize >> 10);
+
+       ppdata.of_node = np;
+
+       ret = mtd_device_parse_register(&flash->mtd, NULL, &ppdata, parts,
+                                       count);
+       if (ret) {
+               dev_err(&pdev->dev, "Err MTD partition=%d\n", ret);
+               goto err;
+       }
+
+       return 0;
+
+err:
+       mtd_device_unregister(&flash->mtd);
+       return ret;
+}
+
+static int altera_epcq_probe(struct platform_device *pdev) {
+       struct device_node *np = pdev->dev.of_node;
+       struct altera_epcq_plat_data *pdata = NULL;
+       struct altera_epcq *dev;
+       int ret = 0;
+       int i;
+
+       if (!np) {
+               ret = -ENODEV;
+               dev_err(&pdev->dev, "no device found\n");
+               goto err;
+       }
+
+       pdata = devm_kzalloc(&pdev->dev,
+                            sizeof(struct altera_epcq_plat_data),
+                            GFP_KERNEL);
+
+       if (!pdata) {
+               ret = -ENOMEM;
+               dev_err(&pdev->dev, "%s: ERROR: no memory\n", __func__);
+               goto err;
+       }
+       ret = altera_epcq_probe_config_dt(pdev, np, pdata);
+       if (ret) {
+               ret = -ENODEV;
+               dev_err(&pdev->dev, "probe fail\n");
+               goto err;
+       }
+
+       dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
+       if (!dev) {
+               ret = -ENOMEM;
+               dev_err(&pdev->dev, "mem alloc fail\n");
+               goto err;
+       }
+       mutex_init(&dev->lock);
+       dev->pdev = pdev;
+       dev->is_epcs = pdata->is_epcs;
+       dev->csr_base = pdata->csr_base;
+       dev->data_base = pdata->data_base;
+
+       /*check number of flashes*/
+       dev->num_flashes = pdata->num_flashes;
+       if (dev->num_flashes > MAX_NUM_FLASH_CHIP) {
+               dev_err(&pdev->dev, "exceeding max number of flashes\n");
+               dev->num_flashes = MAX_NUM_FLASH_CHIP;
+       }
+
+       /* check clock*/
+       dev->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(dev->clk)) {
+               ret = PTR_ERR(dev->clk);
+               goto err;
+       }
+       ret = clk_prepare_enable(dev->clk);
+       if (ret)
+               goto err;
+
+       platform_set_drvdata(pdev, dev);
+
+       /* loop for each serial flash which is connected to epcq */
+       for (i = 0; i < dev->num_flashes; i++) {
+               ret = altera_epcq_setup_banks(pdev, i, pdata->np[i], pdata);
+               if (ret) {
+                       dev_err(&pdev->dev, "bank setup failed\n");
+                       goto err_bank_setup;
+               }
+       }
+
+       return 0;
+
+err_bank_setup:
+       clk_disable_unprepare(dev->clk);
+err:
+       return ret;
+}
+
+static int altera_epcq_remove(struct platform_device *pdev) {
+       struct altera_epcq *dev;
+       struct altera_epcq_flash *flash;
+       int ret, i;
+
+       dev = platform_get_drvdata(pdev);
+
+       /* clean up for all nor flash */
+       for (i = 0; i < dev->num_flashes; i++) {
+               flash = dev->flash[i];
+               if (!flash)
+                       continue;
+
+               /* clean up mtd stuff */
+               ret = mtd_device_unregister(&flash->mtd);
+               if (ret)
+                       dev_err(&pdev->dev, "error removing mtd\n");
+       }
+
+       clk_disable_unprepare(dev->clk);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int altera_epcq_suspend(struct device *dev) {
+       struct altera_epcq *sdev = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(sdev->clk);
+
+       return 0;
+}
+
+static int altera_epcq_resume(struct device *dev) {
+       struct altera_epcq *sdev = dev_get_drvdata(dev);
+       int ret = -EPERM;
+
+       ret = clk_prepare_enable(sdev->clk);
+
+       return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(altera_epcq_pm_ops, altera_epcq_suspend,
+                        altera_epcq_resume);
+#endif
+
+static const struct of_device_id altera_epcq_id_table[] = {
+       { .compatible = "altr,epcq-1.0" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, altera_epcq_id_table);
+
+static struct platform_driver altera_epcq_driver = {
+       .driver = {
+               .name = ALTERA_EPCQ_RESOURCE_NAME,
+               .bus = &platform_bus_type,
+               .owner = THIS_MODULE,
+               .of_match_table = altera_epcq_id_table, #ifdef CONFIG_PM
+               .pm = &altera_epcq_pm_ops,
+#endif
+       },
+       .probe = altera_epcq_probe,
+       .remove = altera_epcq_remove,
+};
+module_platform_driver(altera_epcq_driver);
+
+MODULE_AUTHOR("Viet Nga Dao <vndao@altera.com>");
+MODULE_DESCRIPTION("MTD Altera EPCQ Driver"); MODULE_LICENSE("GPL v2");
diff --git a/drivers/mtd/devices/altera_epcq.h b/drivers/mtd/devices/altera_epcq.h
new file mode 100644
index 0000000..c3d15e5
--- /dev/null
+++ b/drivers/mtd/devices/altera_epcq.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2014 Altera Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ALTERA_ECPQ_H
+#define __ALTERA_ECPQ_H
+
+#define ALTERA_EPCQ_RESOURCE_NAME        "altera_epcq"
+/* max possible slots for serial flash chip in the EPCQ controller */
+#define MAX_NUM_FLASH_CHIP     3
+
+/* macro to define partitions for flash devices */
+#define DEFINE_PARTS(n, of, s)         \
+{                                      \
+       .name = n,                      \
+       .offset = of,                   \
+       .size = s,                      \
+}
+
+struct altera_epcq_flash_info {
+       u32 bank;
+       struct mtd_partition *partitions;
+       int nr_partitions;
+};
+
+struct altera_epcq_plat_data {
+       void __iomem *csr_base;
+       void __iomem *data_base;
+       bool is_epcs;
+       u32 num_flashes;
+       struct altera_epcq_flash_info *board_flash_info;
+       struct device_node *np[MAX_NUM_FLASH_CHIP]; };
+
+struct altera_epcq {
+       struct clk *clk;
+       bool is_epcs;
+       struct mutex lock;      /*device lock*/
+       void __iomem *csr_base;
+       void __iomem *data_base;
+       struct platform_device *pdev;
+       u32 num_flashes;
+       struct altera_epcq_flash *flash[MAX_NUM_FLASH_CHIP]; };
+
+struct altera_epcq_flash {
+       u32 bank;
+       u32 num_sectors;
+       u32 num_parts;
+       struct mtd_partition *parts;
+       struct mutex lock;      /*flash lock*/
+       struct mtd_info mtd;
+};
+
+/* Define max times to check status register before we give up. */
+#define EPCQ_MAX_TIME_OUT                      (40 * HZ)
+
+/* defines for status register */
+#define EPCQ_SR_REG                            0x0
+#define EPCQ_SR_WIP_MASK                       0x00000001
+#define EPCQ_SR_WIP                            0x1
+#define EPCQ_SR_WEL                            0x2
+#define EPCQ_SR_BP0                            0x4
+#define EPCQ_SR_BP1                            0x8
+#define EPCQ_SR_BP2                            0x10
+#define EPCQ_SR_BP3                            0x40
+#define EPCQ_SR_TB                             0x20
+
+/* defines for device id register */
+#define EPCQ_SID_REG                           0x4
+#define EPCQ_RDID_REG                          0x8
+#define EPCQ_RDID_MASK                         0x000000FF
+/*
+ * EPCQ_MEM_OP register offset
+ *
+ * The EPCQ_MEM_OP register is used to do memory protect and erase
+operations
+ *
+ */
+#define EPCQ_MEM_OP_REG                                0xC
+
+#define EPCQ_MEM_OP_CMD_MASK                   0x00000003
+#define EPCQ_MEM_OP_BULK_ERASE_CMD             0x00000001
+#define EPCQ_MEM_OP_SECTOR_ERASE_CMD           0x00000002
+#define EPCQ_MEM_OP_SECTOR_PROTECT_CMD         0x00000003
+#define EPCQ_MEM_OP_SECTOR_VALUE_MASK          0x0003FF00
+#define EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK  0x00001F00
+#define EPCQ_MEM_OP_SECTOR_PROTECT_SHIFT       8
+/*
+ * EPCQ_ISR register offset
+ *
+ * The EPCQ_ISR register is used to determine whether an invalid write
+or erase
+ * operation trigerred an interrupt
+ *
+ */
+#define EPCQ_ISR_REG                           0x10
+
+#define EPCQ_ISR_ILLEGAL_ERASE_MASK            0x00000001
+#define EPCQ_ISR_ILLEGAL_WRITE_MASK            0x00000002
+
+/*
+ * EPCQ_IMR register offset
+ *
+ * The EPCQ_IMR register is used to mask the invalid erase or the
+invalid write
+ * interrupts.
+ *
+ */
+#define EPCQ_IMR_REG                           0x14
+#define EPCQ_IMR_ILLEGAL_ERASE_MASK            0x00000001
+
+#define EPCQ_IMR_ILLEGAL_WRITE_MASK            0x00000002
+
+#define EPCQ_CHIP_SELECT_REG                   0x18
+#define EPCQ_CHIP_SELECT_MASK                  0x00000007
+#define EPCQ_CHIP_SELECT_0                     0x00000001
+#define EPCQ_CHIP_SELECT_1                     0x00000002
+#define EPCQ_CHIP_SELECT_2                     0x00000004
+
+#endif /* __ALTERA_ECPQ_H */
--
1.7.7.4


________________________________

Confidentiality Notice.
This message may contain information that is confidential or otherwise protected from disclosure. If you are not the intended recipient, you are hereby notified that any use, disclosure, dissemination, distribution, or copying of this message, or any attachments, is strictly prohibited. If you have received this message in error, please advise the sender by reply e-mail, and delete the message and any attachments. Thank you.

______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply related	[flat|nested] 39+ messages in thread

* RE: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2015-01-13  1:21   ` Viet Nga Dao
  0 siblings, 0 replies; 39+ messages in thread
From: Viet Nga Dao @ 2015-01-13  1:21 UTC (permalink / raw)
  To: dwmw2, computersforpeace; +Cc: devicetree, linux-mtd, linux-kernel, ngachi86

Hi Linux Community,
It is been a while since I submitted this patch. Could you please help me to review this driver?
Thanks and Regards,
Viet Nga Dao

-----Original Message-----
From: Viet Nga Dao
Sent: Thursday, December 18, 2014 4:23 PM
To: dwmw2@infradead.org; computersforpeace@gmail.com
Cc: linux-mtd@lists.infradead.org; linux-kernel@vger.kernel.org; devicetree@vger.kernel.org; ngachi86@gmail.com; Viet Nga Dao
Subject: [PATCH mtd] mtd:devices: Add Altera EPCQ Driver

From: Viet Nga Dao <vndao@altera.com>

Altera EPCQ Controller is a soft IP which enables access to Altera EPCQ and EPCS flash chips. This patch adds driver for these devices.

Signed-off-by: Viet Nga Dao <vndao@altera.com>
---
 .../devicetree/bindings/mtd/altera_epcq.txt        |   45 ++
 drivers/mtd/devices/Kconfig                        |   12 +
 drivers/mtd/devices/Makefile                       |    2 +-
 drivers/mtd/devices/altera_epcq.c                  |  804 ++++++++++++++++++++
 drivers/mtd/devices/altera_epcq.h                  |  130 ++++
 5 files changed, 992 insertions(+), 1 deletions(-)  create mode 100644 Documentation/devicetree/bindings/mtd/altera_epcq.txt
 create mode 100644 drivers/mtd/devices/altera_epcq.c  create mode 100644 drivers/mtd/devices/altera_epcq.h

diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
new file mode 100644
index 0000000..d14f50e
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
@@ -0,0 +1,45 @@
+* MTD Altera EPCQ driver
+
+Required properties:
+- compatible: Should be "altr,epcq-1.0"
+- reg: Address and length of the register set  for the device. It
+contains
+  the information of registers in the same order as described by
+reg-names
+- reg-names: Should contain the reg names
+  "csr_base": Should contain the register configuration base address
+  "data_base": Should contain the data base address
+- is-epcs: boolean type.
+               If present, the device contains EPCS flashes.
+               Otherwise, it contains EPCQ flashes.
+- #address-cells: Must be <1>.
+- #size-cells: Must be <0>.
+- flash device tree subnode, there must be a node with the following fields:
+       - reg: Should contain the flash id
+       - #address-cells: please refer to /mtd/partition.txt
+       - #size-cells: please refer to /mtd/partition.txt
+       For partitions inside each flash, please refer to /mtd/partition.txt
+
+Example:
+
+                       epcq_controller_0: epcq@0x000000000 {
+                               compatible = "altr,epcq-1.0";
+                               reg = <0x00000001 0x00000000 0x00000020>,
+                                       <0x00000000 0x00000000 0x02000000>;
+                               reg-names = "csr_base", "data_base";
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               flash0: epcq256@0 {
+                                       reg = <0>;
+                                       #address-cells = <1>;
+                                       #size-cells = <1>;
+                                       partition@0 {
+                                               /* 16 MB for raw data. */
+                                               label = "EPCQ Flash 0 raw data";
+                                               reg = <0x0 0x1000000>;
+                                       };
+                                       partition@1000000 {
+                                               /* 16 MB for jffs2 data. */
+                                               label = "EPCQ Flash 0 JFFS 2";
+                                               reg = <0x1000000 0x1000000>;
+                                       };
+                               };
+                       }; //end epcq@0x000000000 (epcq_controller_0)
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index c49d0b1..020b864 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -218,6 +218,18 @@ config MTD_ST_SPI_FSM
          SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
          for a subset of connected Serial Flash devices.

+config MTD_ALTERA_EPCQ
+       tristate "Support Altera EPCQ/EPCS Flash chips"
+       depends on OF
+       help
+         This enables access to Altera EPCQ/EPCS flash chips, used for data
+         storage. See the driver source for the current list,
+         or to add other chips.
+
+         If you want to compile this driver as a module ( = code which can be
+         inserted in and removed from the running kernel whenever you want),
+         say M here and read <file:Documentation/kbuild/modules.txt>.
+
 if MTD_DOCG3
 config BCH_CONST_M
        default 14
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index f0b0e61..b429c4d 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -16,6 +16,6 @@ obj-$(CONFIG_MTD_SPEAR_SMI)   += spear_smi.o
 obj-$(CONFIG_MTD_SST25L)       += sst25l.o
 obj-$(CONFIG_MTD_BCM47XXSFLASH)        += bcm47xxsflash.o
 obj-$(CONFIG_MTD_ST_SPI_FSM)    += st_spi_fsm.o
-
+obj-$(CONFIG_MTD_ALTERA_EPCQ)          += altera_epcq.o

 CFLAGS_docg3.o                 += -I$(src)
diff --git a/drivers/mtd/devices/altera_epcq.c b/drivers/mtd/devices/altera_epcq.c
new file mode 100644
index 0000000..09213d5
--- /dev/null
+++ b/drivers/mtd/devices/altera_epcq.c
@@ -0,0 +1,804 @@
+/*
+ * Copyright (C) 2014 Altera Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/param.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include "altera_epcq.h"
+
+/* data structure to maintain flash ids from different vendors */
+struct flash_device {
+       char *name;
+       bool is_epcs;
+       u32 device_id;
+       uint32_t sectorsize_inbytes;
+       uint64_t size_inbytes;
+       u32 pagesize;
+};
+
+#define FLASH_ID(_n, _is_epcs, _id, _ssize, _size, _psize)     \
+{                              \
+       .name = (_n),           \
+       .is_epcs = (_is_epcs),          \
+       .device_id = (_id),     \
+       .sectorsize_inbytes = (_ssize), \
+       .size_inbytes = (_size),        \
+       .pagesize = (_psize),   \
+}
+
+static struct flash_device flash_devices[] = {
+       FLASH_ID("epcq16"    , 0, 0x15, 0x10000, 0x200000, 0x100),
+       FLASH_ID("epcq32"    , 0, 0x16, 0x10000, 0x400000, 0x100),
+       FLASH_ID("epcq64"    , 0, 0x17, 0x10000, 0x800000, 0x100),
+       FLASH_ID("epcq128"   , 0, 0x18, 0x10000, 0x1000000, 0x100),
+       FLASH_ID("epcq256"   , 0, 0x19, 0x10000, 0x2000000, 0x100),
+       FLASH_ID("epcq512"   , 0, 0x20, 0x10000, 0x4000000, 0x100),
+
+       FLASH_ID("epcs16"    , 1, 0x14, 0x10000, 0x200000, 0x100),
+       FLASH_ID("epcs64"    , 1, 0x16, 0x10000, 0x800000, 0x100),
+       FLASH_ID("epcs128"   , 1, 0x18, 0x40000, 0x1000000, 0x100),
+};
+
+static inline struct altera_epcq_flash *get_flash_data(struct mtd_info
+*mtd) {
+       return container_of(mtd, struct altera_epcq_flash, mtd); }
+
+static u32 altera_epcq_read_sr(struct altera_epcq *dev) {
+       return readl(dev->csr_base + EPCQ_SR_REG); }
+
+static int altera_epcq_wait_till_ready(struct altera_epcq *dev) {
+       unsigned long finish;
+       int status;
+
+       finish = jiffies + EPCQ_MAX_TIME_OUT;
+       do {
+               status = altera_epcq_read_sr(dev);
+               if (status < 0)
+                       return status;
+               else if (!(status & EPCQ_SR_WIP))
+                       return 0;
+
+               cond_resched();
+       } while (!time_after_eq(jiffies, finish));
+
+       dev_err(&dev->pdev->dev, "epcq controller is busy, timeout\n");
+       return -EBUSY;
+}
+
+static int get_flash_index(u32 flash_id, bool is_epcs) {
+       int index;
+
+       for (index = 0; index < ARRAY_SIZE(flash_devices); index++) {
+               if (flash_devices[index].device_id == flash_id &&
+                   flash_devices[index].is_epcs == is_epcs)
+                       return index;
+       }
+
+       /* Memory chip is not listed and not supported */
+       return -ENODEV;
+}
+
+static int altera_epcq_write_erase_check(struct altera_epcq *dev,
+                                        bool write_erase)
+{
+       u32 val;
+       u32 mask;
+
+       if (write_erase)
+               mask = EPCQ_ISR_ILLEGAL_WRITE_MASK;
+       else
+               mask = EPCQ_ISR_ILLEGAL_ERASE_MASK;
+
+       val = readl(dev->csr_base + EPCQ_ISR_REG);
+       if (val & mask) {
+               dev_err(&dev->pdev->dev,
+                       "write/erase failed, sector might be protected\n");
+               /*clear this status for next use*/
+               writel(val, dev->csr_base + EPCQ_ISR_REG);
+               return -EIO;
+       }
+       return 0;
+}
+
+static int altera_epcq_erase_chip(struct mtd_info *mtd) {
+       int ret;
+       u32 val;
+       struct altera_epcq *dev = mtd->priv;
+
+       /* Wait until finished previous write command. */
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               return ret;
+
+       /* erase chip. */
+       val = EPCQ_MEM_OP_BULK_ERASE_CMD;
+       writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
+
+       /* Wait until finished previous write command. */
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               return ret;
+
+        /* check whether write triggered a illegal write interrupt */
+       ret = altera_epcq_write_erase_check(dev, 0);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int altera_epcq_addr_to_sector(struct altera_epcq_flash *flash,
+                                     int addr_offset)
+{
+       int sector = 0;
+       int i;
+
+       for (i = 0; i < flash->num_sectors; i++) {
+               if (addr_offset >= i * flash->mtd.erasesize && addr_offset <
+                   (i * flash->mtd.erasesize + flash->mtd.erasesize)) {
+                       sector = i;
+                       return sector;
+               }
+       }
+       return -1;
+}
+
+static int altera_epcq_erase_sector(struct mtd_info *mtd,
+                                   int addr_offset)
+{
+       struct altera_epcq_flash *flash = get_flash_data(mtd);
+       struct altera_epcq *dev = mtd->priv;
+       u32 val;
+       int ret;
+       int sector_value;
+
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               return ret;
+
+       sector_value = altera_epcq_addr_to_sector(flash, addr_offset);
+
+       /* sanity check that block_offset is a valid sector number */
+       if (sector_value < 0)
+               return -EINVAL;
+
+       /* sector value should occupy bits 17:8 */
+       val = (sector_value << 8) & EPCQ_MEM_OP_SECTOR_VALUE_MASK;
+
+       /* sector erase commands occupies lower 2 bits */
+       val |= EPCQ_MEM_OP_SECTOR_ERASE_CMD;
+
+       /* write sector erase command to EPCQ_MEM_OP register*/
+       writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
+
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               return ret;
+
+        /* check whether write triggered a illegal write interrupt */
+       ret = altera_epcq_write_erase_check(dev, 0);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int altera_epcq_erase(struct mtd_info *mtd, struct erase_info
+*e_info) {
+       u32 addr;
+       int ret;
+       u32 len;
+       struct altera_epcq_flash *flash = get_flash_data(mtd);
+       struct altera_epcq *dev = mtd->priv;
+
+       addr = e_info->addr;
+       len = e_info->len;
+
+       if (flash->bank > dev->num_flashes - 1) {
+               dev_err(&dev->pdev->dev, "Invalid chip id\n");
+               return -EINVAL;
+       }
+       mutex_lock(&flash->lock);
+
+       /*erase whole chip*/
+       if (len == mtd->size) {
+               if (altera_epcq_erase_chip(mtd)) {
+                       e_info->state = MTD_ERASE_FAILED;
+                       mutex_unlock(&flash->lock);
+                       return -EIO;
+               }
+       /*"sector"-at-a-time erase*/
+       } else {
+               while (len) {
+                       ret = altera_epcq_erase_sector(mtd, addr);
+                       if (ret) {
+                               e_info->state = MTD_ERASE_FAILED;
+                               mutex_unlock(&flash->lock);
+                               return -EIO;
+                       }
+                       addr += mtd->erasesize;
+                       if (len < mtd->erasesize)
+                               len = 0;
+                       else
+                               len -= mtd->erasesize;
+               }
+       }
+       mutex_unlock(&flash->lock);
+       e_info->state = MTD_ERASE_DONE;
+       mtd_erase_callback(e_info);
+
+       return 0;
+}
+
+static int altera_epcq_read(struct mtd_info *mtd, loff_t from, size_t len,
+                           size_t *retlen, u8 *buf)
+{
+       struct altera_epcq_flash *flash = get_flash_data(mtd);
+       struct altera_epcq *dev = mtd->priv;
+       void *src;
+       int ret = 0;
+
+       if (!flash || !dev)
+               return -ENODEV;
+
+       if (flash->bank > dev->num_flashes - 1) {
+               dev_err(&dev->pdev->dev, "Invalid chip id\n");
+               return -EINVAL;
+       }
+
+       src = dev->data_base + from;
+
+       mutex_lock(&flash->lock);
+       /* wait till previous write/erase is done. */
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               goto err;
+
+       memcpy_fromio(buf, (u8 *)src, len);
+       *retlen = len;
+
+err:
+       mutex_unlock(&flash->lock);
+       return ret;
+}
+
+static int altera_epcq_write(struct mtd_info *mtd, loff_t to, size_t len,
+                            size_t *retlen, const u8 *buf)
+{
+       struct altera_epcq_flash *flash = get_flash_data(mtd);
+       struct altera_epcq *dev = mtd->priv;
+       void *dest;
+       int ret = 0;
+
+       if (!flash || !dev)
+               return -ENODEV;
+
+       if (flash->bank > dev->num_flashes - 1) {
+               dev_err(&dev->pdev->dev, "Invalid chip id\n");
+               return -EINVAL;
+       }
+       dest = dev->data_base + to;
+
+       mutex_lock(&flash->lock);
+
+       /* wait until finished previous write command. */
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               goto err;
+
+       memcpy_toio(dest, buf, len);
+       *retlen += len;
+
+       /* wait until finished previous write command. */
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               goto err;
+
+        /* check whether write triggered a illegal write interrupt */
+       ret = altera_epcq_write_erase_check(dev, 1);
+       if (ret < 0)
+               goto err;
+
+err:
+       mutex_unlock(&flash->lock);
+       return ret;
+}
+
+static int altera_epcq_lock(struct mtd_info *mtd, loff_t ofs, uint64_t
+len) {
+       struct altera_epcq_flash *flash = get_flash_data(mtd);
+       struct altera_epcq *dev = mtd->priv;
+       uint32_t offset = ofs;
+       int ret = 0;
+       u32 sector_start, sector_end;
+       u32 num_sectors;
+       u32 mem_op;
+       unsigned sr_bp = 0;
+       unsigned sr_tb = 0;
+
+       sector_start = altera_epcq_addr_to_sector(flash, offset);
+       sector_end = altera_epcq_addr_to_sector(flash, offset + len);
+       num_sectors = flash->num_sectors;
+       dev_dbg(&dev->pdev->dev,
+               "%s: num_setor is %u,sector start is %u,sector end is %u\n",
+               __func__, num_sectors, sector_start, sector_end);
+
+       mutex_lock(&flash->lock);
+       /* wait until finished previous write command. */
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               goto err;
+
+       if (sector_start >= num_sectors/2) {
+               if (sector_start < num_sectors-(num_sectors / 4))
+                       sr_bp = __ilog2_u32(num_sectors);
+               else if (sector_start < num_sectors-(num_sectors / 8))
+                       sr_bp = __ilog2_u32(num_sectors) - 1;
+               else if (sector_start < num_sectors-(num_sectors / 16))
+                       sr_bp = __ilog2_u32(num_sectors) - 2;
+               else if (sector_start < num_sectors-(num_sectors / 32))
+                       sr_bp = __ilog2_u32(num_sectors) - 3;
+               else if (sector_start < num_sectors-(num_sectors / 64))
+                       sr_bp = __ilog2_u32(num_sectors) - 4;
+               else if (sector_start < num_sectors-(num_sectors / 128))
+                       sr_bp = __ilog2_u32(num_sectors) - 5;
+               else if (sector_start < num_sectors-(num_sectors / 256))
+                       sr_bp = __ilog2_u32(num_sectors) - 6;
+               else if (sector_start < num_sectors-(num_sectors / 512))
+                       sr_bp = __ilog2_u32(num_sectors) - 7;
+               else if (sector_start < num_sectors-(num_sectors / 1024))
+                       sr_bp = __ilog2_u32(num_sectors) - 8;
+               else
+                       sr_bp = 0;  /* non area protected */
+
+               if (sr_bp < 0) {
+                       dev_err(&dev->pdev->dev, "%s: address is out of range\n"
+                               , __func__);
+                       ret = -EINVAL;
+                       goto err;
+               }
+               /*set TB = 0*/
+               sr_tb = 0;
+
+       } else {
+               if (sector_end < 1)
+                       sr_bp = 1;
+               else if (sector_end < 2)
+                       sr_bp = 2;
+               else if (sector_end < 4)
+                       sr_bp = 3;
+               else if (sector_end < 8)
+                       sr_bp = 4;
+               else if (sector_end < 16)
+                       sr_bp = 5;
+               else if (sector_end < 32)
+                       sr_bp = 6;
+               else if (sector_end < 64)
+                       sr_bp = 7;
+               else if (sector_end < 128)
+                       sr_bp = 8;
+               else if (sector_end < 256)
+                       sr_bp = 9;
+               else if (sector_end < 512)
+                       sr_bp = 10;
+               else
+                       sr_bp = 16; /*protect all areas*/
+
+               sr_tb = 1;
+       }
+
+       mem_op = (sr_tb << 12) | (sr_bp << 8);
+       mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
+       mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
+       writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
+
+err:
+       mutex_unlock(&flash->lock);
+       return ret;
+}
+
+static int altera_epcq_unlock(struct mtd_info *mtd, loff_t ofs,
+uint64_t len) {
+       struct altera_epcq_flash *flash = get_flash_data(mtd);
+       struct altera_epcq *dev = mtd->priv;
+
+       int ret = 0;
+       u32 mem_op;
+
+       mutex_lock(&flash->lock);
+       /* wait until finished previous write command. */
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               goto err;
+       dev_info(&dev->pdev->dev, "Unlock all protected area\n");
+       mem_op = 0;
+       mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
+       mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
+       writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
+
+err:
+       mutex_unlock(&flash->lock);
+       return ret;
+}
+
+static void altera_epcq_chip_select(struct altera_epcq *dev, u32 bank)
+{
+       u32 val = 0;
+
+       switch (bank) {
+       case 0:
+               val = EPCQ_CHIP_SELECT_0;
+               break;
+       case 1:
+               val = EPCQ_CHIP_SELECT_1;
+               break;
+       case 2:
+               val = EPCQ_CHIP_SELECT_2;
+               break;
+       default:
+               return;
+       }
+
+       writel(val, dev->csr_base + EPCQ_CHIP_SELECT_REG); }
+
+static int altera_epcq_probe_flash(struct altera_epcq *dev, u32 bank) {
+       int ret = 0;
+       u32 val = 0;
+
+       mutex_lock(&dev->lock);
+
+       /*select bank*/
+       altera_epcq_chip_select(dev, bank);
+
+       ret = altera_epcq_wait_till_ready(dev);
+       if (ret)
+               goto err;
+
+       /* get device sillicon id */
+       if (dev->is_epcs)
+               val = readl(dev->csr_base + EPCQ_SID_REG);
+       else
+               val = readl(dev->csr_base + EPCQ_RDID_REG);
+
+       /* get flash index based on the device list*/
+       ret = get_flash_index(val, dev->is_epcs);
+       return 0;
+err:
+       mutex_unlock(&dev->lock);
+       return ret;
+}
+
+static int altera_epcq_probe_config_dt(struct platform_device *pdev,
+                                      struct device_node *np,
+                                      struct altera_epcq_plat_data *pdata) {
+       struct device_node *pp = NULL;
+       struct resource *epcq_res;
+       int i = 0;
+       u32 id;
+
+       pdata->is_epcs = of_property_read_bool(np, "is-epcs");
+
+       epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                               "csr_base");
+       if (!epcq_res) {
+               dev_err(&pdev->dev, "resource csr base is not defined\n");
+               return -ENODEV;
+       }
+
+       pdata->csr_base = devm_ioremap_resource(&pdev->dev, epcq_res);
+       if (IS_ERR(pdata->csr_base)) {
+               dev_err(&pdev->dev, "%s: ERROR: failed to map csr base\n",
+                       __func__);
+               return PTR_ERR(pdata->csr_base);
+       }
+
+       epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                               "data_base");
+       if (!epcq_res) {
+               dev_err(&pdev->dev, "resource data base is not defined\n");
+               return -ENODEV;
+       }
+
+       pdata->data_base = devm_ioremap_resource(&pdev->dev, epcq_res);
+       if (IS_ERR(pdata->data_base)) {
+               dev_err(&pdev->dev, "%s: ERROR: failed to map data base\n",
+                       __func__);
+               return PTR_ERR(pdata->data_base);
+       }
+
+       pdata->board_flash_info = devm_kzalloc(&pdev->dev,
+                                              sizeof(*pdata->board_flash_info),
+                                              GFP_KERNEL);
+
+       /* Fill structs for each subnode (flash device) */
+       while ((pp = of_get_next_child(np, pp))) {
+               struct altera_epcq_flash_info *flash_info;
+
+               flash_info = &pdata->board_flash_info[i];
+               pdata->np[i] = pp;
+
+               /* Read bank id from DT */
+               if (of_get_property(pp, "reg", &id))
+                       pdata->board_flash_info[i].bank = id;
+               i++;
+       }
+       pdata->num_flashes = i;
+       return 0;
+}
+
+static int altera_epcq_setup_banks(struct platform_device *pdev,
+                                  u32 bank, struct device_node *np,
+                                  struct altera_epcq_plat_data *pdata) {
+       struct altera_epcq *dev = platform_get_drvdata(pdev);
+       struct mtd_part_parser_data ppdata = {};
+       struct altera_epcq_flash_info *flash_info;
+       struct altera_epcq_flash *flash;
+       struct mtd_partition *parts = NULL;
+       int count = 0;
+       int flash_index;
+       int ret = 0;
+       uint64_t size;
+       uint32_t sector_size;
+
+       flash_info = &pdata->board_flash_info[bank];
+       if (!flash_info)
+               return -ENODEV;
+
+       if (bank > pdata->num_flashes - 1)
+               return -EINVAL;
+
+       flash = devm_kzalloc(&pdev->dev, sizeof(*flash), GFP_KERNEL);
+       if (!flash)
+               return -ENOMEM;
+       flash->bank = bank;
+
+       mutex_init(&flash->lock);
+
+       /* verify whether flash is really present on board */
+       flash_index = altera_epcq_probe_flash(dev, bank);
+       if (flash_index < 0) {
+               dev_info(&dev->pdev->dev, "epcq:flash%d not found\n",
+                        flash->bank);
+               return flash_index;
+       }
+
+       dev->flash[bank] = flash;
+
+       size = flash_devices[flash_index].size_inbytes;
+       sector_size = flash_devices[flash_index].sectorsize_inbytes;
+       /*use do_div instead of plain div to avoid linker err*/
+       do_div(size, sector_size);
+       flash->num_sectors = size;
+
+       /*mtd framework */
+       flash->mtd.priv = dev;
+       flash->mtd.name = flash_devices[flash_index].name;
+       flash->mtd.type = MTD_NORFLASH;
+       flash->mtd.writesize = 1;
+       flash->mtd.flags = MTD_CAP_NORFLASH;
+       flash->mtd.size = flash_devices[flash_index].size_inbytes;
+       flash->mtd.erasesize = flash_devices[flash_index].sectorsize_inbytes;
+       flash->mtd._erase = altera_epcq_erase;
+       flash->mtd._read = altera_epcq_read;
+       flash->mtd._write = altera_epcq_write;
+       flash->mtd._lock = altera_epcq_lock;
+       flash->mtd._unlock = altera_epcq_unlock;
+
+       dev_info(&pdev->dev, "mtd .name=%s .size=0x%llx (%lluM)\n",
+                flash->mtd.name, (long long)flash->mtd.size,
+                (long long)(flash->mtd.size >> 20));
+
+       dev_info(&pdev->dev, ".erasesize = 0x%x(%uK)\n",
+                flash->mtd.erasesize, flash->mtd.erasesize >> 10);
+
+       ppdata.of_node = np;
+
+       ret = mtd_device_parse_register(&flash->mtd, NULL, &ppdata, parts,
+                                       count);
+       if (ret) {
+               dev_err(&pdev->dev, "Err MTD partition=%d\n", ret);
+               goto err;
+       }
+
+       return 0;
+
+err:
+       mtd_device_unregister(&flash->mtd);
+       return ret;
+}
+
+static int altera_epcq_probe(struct platform_device *pdev) {
+       struct device_node *np = pdev->dev.of_node;
+       struct altera_epcq_plat_data *pdata = NULL;
+       struct altera_epcq *dev;
+       int ret = 0;
+       int i;
+
+       if (!np) {
+               ret = -ENODEV;
+               dev_err(&pdev->dev, "no device found\n");
+               goto err;
+       }
+
+       pdata = devm_kzalloc(&pdev->dev,
+                            sizeof(struct altera_epcq_plat_data),
+                            GFP_KERNEL);
+
+       if (!pdata) {
+               ret = -ENOMEM;
+               dev_err(&pdev->dev, "%s: ERROR: no memory\n", __func__);
+               goto err;
+       }
+       ret = altera_epcq_probe_config_dt(pdev, np, pdata);
+       if (ret) {
+               ret = -ENODEV;
+               dev_err(&pdev->dev, "probe fail\n");
+               goto err;
+       }
+
+       dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
+       if (!dev) {
+               ret = -ENOMEM;
+               dev_err(&pdev->dev, "mem alloc fail\n");
+               goto err;
+       }
+       mutex_init(&dev->lock);
+       dev->pdev = pdev;
+       dev->is_epcs = pdata->is_epcs;
+       dev->csr_base = pdata->csr_base;
+       dev->data_base = pdata->data_base;
+
+       /*check number of flashes*/
+       dev->num_flashes = pdata->num_flashes;
+       if (dev->num_flashes > MAX_NUM_FLASH_CHIP) {
+               dev_err(&pdev->dev, "exceeding max number of flashes\n");
+               dev->num_flashes = MAX_NUM_FLASH_CHIP;
+       }
+
+       /* check clock*/
+       dev->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(dev->clk)) {
+               ret = PTR_ERR(dev->clk);
+               goto err;
+       }
+       ret = clk_prepare_enable(dev->clk);
+       if (ret)
+               goto err;
+
+       platform_set_drvdata(pdev, dev);
+
+       /* loop for each serial flash which is connected to epcq */
+       for (i = 0; i < dev->num_flashes; i++) {
+               ret = altera_epcq_setup_banks(pdev, i, pdata->np[i], pdata);
+               if (ret) {
+                       dev_err(&pdev->dev, "bank setup failed\n");
+                       goto err_bank_setup;
+               }
+       }
+
+       return 0;
+
+err_bank_setup:
+       clk_disable_unprepare(dev->clk);
+err:
+       return ret;
+}
+
+static int altera_epcq_remove(struct platform_device *pdev) {
+       struct altera_epcq *dev;
+       struct altera_epcq_flash *flash;
+       int ret, i;
+
+       dev = platform_get_drvdata(pdev);
+
+       /* clean up for all nor flash */
+       for (i = 0; i < dev->num_flashes; i++) {
+               flash = dev->flash[i];
+               if (!flash)
+                       continue;
+
+               /* clean up mtd stuff */
+               ret = mtd_device_unregister(&flash->mtd);
+               if (ret)
+                       dev_err(&pdev->dev, "error removing mtd\n");
+       }
+
+       clk_disable_unprepare(dev->clk);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int altera_epcq_suspend(struct device *dev) {
+       struct altera_epcq *sdev = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(sdev->clk);
+
+       return 0;
+}
+
+static int altera_epcq_resume(struct device *dev) {
+       struct altera_epcq *sdev = dev_get_drvdata(dev);
+       int ret = -EPERM;
+
+       ret = clk_prepare_enable(sdev->clk);
+
+       return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(altera_epcq_pm_ops, altera_epcq_suspend,
+                        altera_epcq_resume);
+#endif
+
+static const struct of_device_id altera_epcq_id_table[] = {
+       { .compatible = "altr,epcq-1.0" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, altera_epcq_id_table);
+
+static struct platform_driver altera_epcq_driver = {
+       .driver = {
+               .name = ALTERA_EPCQ_RESOURCE_NAME,
+               .bus = &platform_bus_type,
+               .owner = THIS_MODULE,
+               .of_match_table = altera_epcq_id_table, #ifdef CONFIG_PM
+               .pm = &altera_epcq_pm_ops,
+#endif
+       },
+       .probe = altera_epcq_probe,
+       .remove = altera_epcq_remove,
+};
+module_platform_driver(altera_epcq_driver);
+
+MODULE_AUTHOR("Viet Nga Dao <vndao@altera.com>");
+MODULE_DESCRIPTION("MTD Altera EPCQ Driver"); MODULE_LICENSE("GPL v2");
diff --git a/drivers/mtd/devices/altera_epcq.h b/drivers/mtd/devices/altera_epcq.h
new file mode 100644
index 0000000..c3d15e5
--- /dev/null
+++ b/drivers/mtd/devices/altera_epcq.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2014 Altera Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License
+along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ALTERA_ECPQ_H
+#define __ALTERA_ECPQ_H
+
+#define ALTERA_EPCQ_RESOURCE_NAME        "altera_epcq"
+/* max possible slots for serial flash chip in the EPCQ controller */
+#define MAX_NUM_FLASH_CHIP     3
+
+/* macro to define partitions for flash devices */
+#define DEFINE_PARTS(n, of, s)         \
+{                                      \
+       .name = n,                      \
+       .offset = of,                   \
+       .size = s,                      \
+}
+
+struct altera_epcq_flash_info {
+       u32 bank;
+       struct mtd_partition *partitions;
+       int nr_partitions;
+};
+
+struct altera_epcq_plat_data {
+       void __iomem *csr_base;
+       void __iomem *data_base;
+       bool is_epcs;
+       u32 num_flashes;
+       struct altera_epcq_flash_info *board_flash_info;
+       struct device_node *np[MAX_NUM_FLASH_CHIP]; };
+
+struct altera_epcq {
+       struct clk *clk;
+       bool is_epcs;
+       struct mutex lock;      /*device lock*/
+       void __iomem *csr_base;
+       void __iomem *data_base;
+       struct platform_device *pdev;
+       u32 num_flashes;
+       struct altera_epcq_flash *flash[MAX_NUM_FLASH_CHIP]; };
+
+struct altera_epcq_flash {
+       u32 bank;
+       u32 num_sectors;
+       u32 num_parts;
+       struct mtd_partition *parts;
+       struct mutex lock;      /*flash lock*/
+       struct mtd_info mtd;
+};
+
+/* Define max times to check status register before we give up. */
+#define EPCQ_MAX_TIME_OUT                      (40 * HZ)
+
+/* defines for status register */
+#define EPCQ_SR_REG                            0x0
+#define EPCQ_SR_WIP_MASK                       0x00000001
+#define EPCQ_SR_WIP                            0x1
+#define EPCQ_SR_WEL                            0x2
+#define EPCQ_SR_BP0                            0x4
+#define EPCQ_SR_BP1                            0x8
+#define EPCQ_SR_BP2                            0x10
+#define EPCQ_SR_BP3                            0x40
+#define EPCQ_SR_TB                             0x20
+
+/* defines for device id register */
+#define EPCQ_SID_REG                           0x4
+#define EPCQ_RDID_REG                          0x8
+#define EPCQ_RDID_MASK                         0x000000FF
+/*
+ * EPCQ_MEM_OP register offset
+ *
+ * The EPCQ_MEM_OP register is used to do memory protect and erase
+operations
+ *
+ */
+#define EPCQ_MEM_OP_REG                                0xC
+
+#define EPCQ_MEM_OP_CMD_MASK                   0x00000003
+#define EPCQ_MEM_OP_BULK_ERASE_CMD             0x00000001
+#define EPCQ_MEM_OP_SECTOR_ERASE_CMD           0x00000002
+#define EPCQ_MEM_OP_SECTOR_PROTECT_CMD         0x00000003
+#define EPCQ_MEM_OP_SECTOR_VALUE_MASK          0x0003FF00
+#define EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK  0x00001F00
+#define EPCQ_MEM_OP_SECTOR_PROTECT_SHIFT       8
+/*
+ * EPCQ_ISR register offset
+ *
+ * The EPCQ_ISR register is used to determine whether an invalid write
+or erase
+ * operation trigerred an interrupt
+ *
+ */
+#define EPCQ_ISR_REG                           0x10
+
+#define EPCQ_ISR_ILLEGAL_ERASE_MASK            0x00000001
+#define EPCQ_ISR_ILLEGAL_WRITE_MASK            0x00000002
+
+/*
+ * EPCQ_IMR register offset
+ *
+ * The EPCQ_IMR register is used to mask the invalid erase or the
+invalid write
+ * interrupts.
+ *
+ */
+#define EPCQ_IMR_REG                           0x14
+#define EPCQ_IMR_ILLEGAL_ERASE_MASK            0x00000001
+
+#define EPCQ_IMR_ILLEGAL_WRITE_MASK            0x00000002
+
+#define EPCQ_CHIP_SELECT_REG                   0x18
+#define EPCQ_CHIP_SELECT_MASK                  0x00000007
+#define EPCQ_CHIP_SELECT_0                     0x00000001
+#define EPCQ_CHIP_SELECT_1                     0x00000002
+#define EPCQ_CHIP_SELECT_2                     0x00000004
+
+#endif /* __ALTERA_ECPQ_H */
--
1.7.7.4


________________________________

Confidentiality Notice.
This message may contain information that is confidential or otherwise protected from disclosure. If you are not the intended recipient, you are hereby notified that any use, disclosure, dissemination, distribution, or copying of this message, or any attachments, is strictly prohibited. If you have received this message in error, please advise the sender by reply e-mail, and delete the message and any attachments. Thank you.

^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2014-12-18  8:23 ` vndao
  0 siblings, 0 replies; 39+ messages in thread
From: vndao @ 2014-12-18  8:23 UTC (permalink / raw)
  To: dwmw2, computersforpeace
  Cc: linux-mtd, linux-kernel, devicetree, ngachi86, Viet Nga Dao

From: Viet Nga Dao <vndao@altera.com>

Altera EPCQ Controller is a soft IP which enables access to Altera EPCQ and
EPCS flash chips. This patch adds driver for these devices.

Signed-off-by: Viet Nga Dao <vndao@altera.com>
---
 .../devicetree/bindings/mtd/altera_epcq.txt        |   45 ++
 drivers/mtd/devices/Kconfig                        |   12 +
 drivers/mtd/devices/Makefile                       |    2 +-
 drivers/mtd/devices/altera_epcq.c                  |  804 ++++++++++++++++++++
 drivers/mtd/devices/altera_epcq.h                  |  130 ++++
 5 files changed, 992 insertions(+), 1 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mtd/altera_epcq.txt
 create mode 100644 drivers/mtd/devices/altera_epcq.c
 create mode 100644 drivers/mtd/devices/altera_epcq.h

diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
new file mode 100644
index 0000000..d14f50e
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
@@ -0,0 +1,45 @@
+* MTD Altera EPCQ driver
+
+Required properties:
+- compatible: Should be "altr,epcq-1.0"
+- reg: Address and length of the register set  for the device. It contains
+  the information of registers in the same order as described by reg-names
+- reg-names: Should contain the reg names
+  "csr_base": Should contain the register configuration base address
+  "data_base": Should contain the data base address
+- is-epcs: boolean type.
+		If present, the device contains EPCS flashes.
+		Otherwise, it contains EPCQ flashes.
+- #address-cells: Must be <1>.
+- #size-cells: Must be <0>.
+- flash device tree subnode, there must be a node with the following fields:
+	- reg: Should contain the flash id
+	- #address-cells: please refer to /mtd/partition.txt
+	- #size-cells: please refer to /mtd/partition.txt
+	For partitions inside each flash, please refer to /mtd/partition.txt
+
+Example:
+
+			epcq_controller_0: epcq@0x000000000 {
+				compatible = "altr,epcq-1.0";
+				reg = <0x00000001 0x00000000 0x00000020>,
+					<0x00000000 0x00000000 0x02000000>;
+				reg-names = "csr_base", "data_base";
+				#address-cells = <1>;
+				#size-cells = <0>;
+				flash0: epcq256@0 {
+					reg = <0>;
+					#address-cells = <1>;
+					#size-cells = <1>;
+					partition@0 {
+						/* 16 MB for raw data. */
+						label = "EPCQ Flash 0 raw data";
+						reg = <0x0 0x1000000>;
+					};
+					partition@1000000 {
+						/* 16 MB for jffs2 data. */
+						label = "EPCQ Flash 0 JFFS 2";
+						reg = <0x1000000 0x1000000>;
+					};
+				};
+			}; //end epcq@0x000000000 (epcq_controller_0)
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index c49d0b1..020b864 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -218,6 +218,18 @@ config MTD_ST_SPI_FSM
 	  SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
 	  for a subset of connected Serial Flash devices.
 
+config MTD_ALTERA_EPCQ
+	tristate "Support Altera EPCQ/EPCS Flash chips"
+	depends on OF
+	help
+	  This enables access to Altera EPCQ/EPCS flash chips, used for data
+	  storage. See the driver source for the current list,
+	  or to add other chips.
+
+	  If you want to compile this driver as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you want),
+	  say M here and read <file:Documentation/kbuild/modules.txt>.
+
 if MTD_DOCG3
 config BCH_CONST_M
 	default 14
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index f0b0e61..b429c4d 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -16,6 +16,6 @@ obj-$(CONFIG_MTD_SPEAR_SMI)	+= spear_smi.o
 obj-$(CONFIG_MTD_SST25L)	+= sst25l.o
 obj-$(CONFIG_MTD_BCM47XXSFLASH)	+= bcm47xxsflash.o
 obj-$(CONFIG_MTD_ST_SPI_FSM)    += st_spi_fsm.o
-
+obj-$(CONFIG_MTD_ALTERA_EPCQ)		+= altera_epcq.o
 
 CFLAGS_docg3.o			+= -I$(src)
diff --git a/drivers/mtd/devices/altera_epcq.c b/drivers/mtd/devices/altera_epcq.c
new file mode 100644
index 0000000..09213d5
--- /dev/null
+++ b/drivers/mtd/devices/altera_epcq.c
@@ -0,0 +1,804 @@
+/*
+ * Copyright (C) 2014 Altera Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/param.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include "altera_epcq.h"
+
+/* data structure to maintain flash ids from different vendors */
+struct flash_device {
+	char *name;
+	bool is_epcs;
+	u32 device_id;
+	uint32_t sectorsize_inbytes;
+	uint64_t size_inbytes;
+	u32 pagesize;
+};
+
+#define FLASH_ID(_n, _is_epcs, _id, _ssize, _size, _psize)	\
+{				\
+	.name = (_n),		\
+	.is_epcs = (_is_epcs),		\
+	.device_id = (_id),	\
+	.sectorsize_inbytes = (_ssize),	\
+	.size_inbytes = (_size),	\
+	.pagesize = (_psize),	\
+}
+
+static struct flash_device flash_devices[] = {
+	FLASH_ID("epcq16"    , 0, 0x15, 0x10000, 0x200000, 0x100),
+	FLASH_ID("epcq32"    , 0, 0x16, 0x10000, 0x400000, 0x100),
+	FLASH_ID("epcq64"    , 0, 0x17, 0x10000, 0x800000, 0x100),
+	FLASH_ID("epcq128"   , 0, 0x18, 0x10000, 0x1000000, 0x100),
+	FLASH_ID("epcq256"   , 0, 0x19, 0x10000, 0x2000000, 0x100),
+	FLASH_ID("epcq512"   , 0, 0x20, 0x10000, 0x4000000, 0x100),
+
+	FLASH_ID("epcs16"    , 1, 0x14, 0x10000, 0x200000, 0x100),
+	FLASH_ID("epcs64"    , 1, 0x16, 0x10000, 0x800000, 0x100),
+	FLASH_ID("epcs128"   , 1, 0x18, 0x40000, 0x1000000, 0x100),
+};
+
+static inline struct altera_epcq_flash *get_flash_data(struct mtd_info *mtd)
+{
+	return container_of(mtd, struct altera_epcq_flash, mtd);
+}
+
+static u32 altera_epcq_read_sr(struct altera_epcq *dev)
+{
+	return readl(dev->csr_base + EPCQ_SR_REG);
+}
+
+static int altera_epcq_wait_till_ready(struct altera_epcq *dev)
+{
+	unsigned long finish;
+	int status;
+
+	finish = jiffies + EPCQ_MAX_TIME_OUT;
+	do {
+		status = altera_epcq_read_sr(dev);
+		if (status < 0)
+			return status;
+		else if (!(status & EPCQ_SR_WIP))
+			return 0;
+
+		cond_resched();
+	} while (!time_after_eq(jiffies, finish));
+
+	dev_err(&dev->pdev->dev, "epcq controller is busy, timeout\n");
+	return -EBUSY;
+}
+
+static int get_flash_index(u32 flash_id, bool is_epcs)
+{
+	int index;
+
+	for (index = 0; index < ARRAY_SIZE(flash_devices); index++) {
+		if (flash_devices[index].device_id == flash_id &&
+		    flash_devices[index].is_epcs == is_epcs)
+			return index;
+	}
+
+	/* Memory chip is not listed and not supported */
+	return -ENODEV;
+}
+
+static int altera_epcq_write_erase_check(struct altera_epcq *dev,
+					 bool write_erase)
+{
+	u32 val;
+	u32 mask;
+
+	if (write_erase)
+		mask = EPCQ_ISR_ILLEGAL_WRITE_MASK;
+	else
+		mask = EPCQ_ISR_ILLEGAL_ERASE_MASK;
+
+	val = readl(dev->csr_base + EPCQ_ISR_REG);
+	if (val & mask) {
+		dev_err(&dev->pdev->dev,
+			"write/erase failed, sector might be protected\n");
+		/*clear this status for next use*/
+		writel(val, dev->csr_base + EPCQ_ISR_REG);
+		return -EIO;
+	}
+	return 0;
+}
+
+static int altera_epcq_erase_chip(struct mtd_info *mtd)
+{
+	int ret;
+	u32 val;
+	struct altera_epcq *dev = mtd->priv;
+
+	/* Wait until finished previous write command. */
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		return ret;
+
+	/* erase chip. */
+	val = EPCQ_MEM_OP_BULK_ERASE_CMD;
+	writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
+
+	/* Wait until finished previous write command. */
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		return ret;
+
+	 /* check whether write triggered a illegal write interrupt */
+	ret = altera_epcq_write_erase_check(dev, 0);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int altera_epcq_addr_to_sector(struct altera_epcq_flash *flash,
+				      int addr_offset)
+{
+	int sector = 0;
+	int i;
+
+	for (i = 0; i < flash->num_sectors; i++) {
+		if (addr_offset >= i * flash->mtd.erasesize && addr_offset <
+		    (i * flash->mtd.erasesize + flash->mtd.erasesize)) {
+			sector = i;
+			return sector;
+		}
+	}
+	return -1;
+}
+
+static int altera_epcq_erase_sector(struct mtd_info *mtd,
+				    int addr_offset)
+{
+	struct altera_epcq_flash *flash = get_flash_data(mtd);
+	struct altera_epcq *dev = mtd->priv;
+	u32 val;
+	int ret;
+	int sector_value;
+
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		return ret;
+
+	sector_value = altera_epcq_addr_to_sector(flash, addr_offset);
+
+	/* sanity check that block_offset is a valid sector number */
+	if (sector_value < 0)
+		return -EINVAL;
+
+	/* sector value should occupy bits 17:8 */
+	val = (sector_value << 8) & EPCQ_MEM_OP_SECTOR_VALUE_MASK;
+
+	/* sector erase commands occupies lower 2 bits */
+	val |= EPCQ_MEM_OP_SECTOR_ERASE_CMD;
+
+	/* write sector erase command to EPCQ_MEM_OP register*/
+	writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
+
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		return ret;
+
+	 /* check whether write triggered a illegal write interrupt */
+	ret = altera_epcq_write_erase_check(dev, 0);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int altera_epcq_erase(struct mtd_info *mtd, struct erase_info *e_info)
+{
+	u32 addr;
+	int ret;
+	u32 len;
+	struct altera_epcq_flash *flash = get_flash_data(mtd);
+	struct altera_epcq *dev = mtd->priv;
+
+	addr = e_info->addr;
+	len = e_info->len;
+
+	if (flash->bank > dev->num_flashes - 1) {
+		dev_err(&dev->pdev->dev, "Invalid chip id\n");
+		return -EINVAL;
+	}
+	mutex_lock(&flash->lock);
+
+	/*erase whole chip*/
+	if (len == mtd->size) {
+		if (altera_epcq_erase_chip(mtd)) {
+			e_info->state = MTD_ERASE_FAILED;
+			mutex_unlock(&flash->lock);
+			return -EIO;
+		}
+	/*"sector"-at-a-time erase*/
+	} else {
+		while (len) {
+			ret = altera_epcq_erase_sector(mtd, addr);
+			if (ret) {
+				e_info->state = MTD_ERASE_FAILED;
+				mutex_unlock(&flash->lock);
+				return -EIO;
+			}
+			addr += mtd->erasesize;
+			if (len < mtd->erasesize)
+				len = 0;
+			else
+				len -= mtd->erasesize;
+		}
+	}
+	mutex_unlock(&flash->lock);
+	e_info->state = MTD_ERASE_DONE;
+	mtd_erase_callback(e_info);
+
+	return 0;
+}
+
+static int altera_epcq_read(struct mtd_info *mtd, loff_t from, size_t len,
+			    size_t *retlen, u8 *buf)
+{
+	struct altera_epcq_flash *flash = get_flash_data(mtd);
+	struct altera_epcq *dev = mtd->priv;
+	void *src;
+	int ret = 0;
+
+	if (!flash || !dev)
+		return -ENODEV;
+
+	if (flash->bank > dev->num_flashes - 1) {
+		dev_err(&dev->pdev->dev, "Invalid chip id\n");
+		return -EINVAL;
+	}
+
+	src = dev->data_base + from;
+
+	mutex_lock(&flash->lock);
+	/* wait till previous write/erase is done. */
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		goto err;
+
+	memcpy_fromio(buf, (u8 *)src, len);
+	*retlen = len;
+
+err:
+	mutex_unlock(&flash->lock);
+	return ret;
+}
+
+static int altera_epcq_write(struct mtd_info *mtd, loff_t to, size_t len,
+			     size_t *retlen, const u8 *buf)
+{
+	struct altera_epcq_flash *flash = get_flash_data(mtd);
+	struct altera_epcq *dev = mtd->priv;
+	void *dest;
+	int ret = 0;
+
+	if (!flash || !dev)
+		return -ENODEV;
+
+	if (flash->bank > dev->num_flashes - 1) {
+		dev_err(&dev->pdev->dev, "Invalid chip id\n");
+		return -EINVAL;
+	}
+	dest = dev->data_base + to;
+
+	mutex_lock(&flash->lock);
+
+	/* wait until finished previous write command. */
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		goto err;
+
+	memcpy_toio(dest, buf, len);
+	*retlen += len;
+
+	/* wait until finished previous write command. */
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		goto err;
+
+	 /* check whether write triggered a illegal write interrupt */
+	ret = altera_epcq_write_erase_check(dev, 1);
+	if (ret < 0)
+		goto err;
+
+err:
+	mutex_unlock(&flash->lock);
+	return ret;
+}
+
+static int altera_epcq_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+	struct altera_epcq_flash *flash = get_flash_data(mtd);
+	struct altera_epcq *dev = mtd->priv;
+	uint32_t offset = ofs;
+	int ret = 0;
+	u32 sector_start, sector_end;
+	u32 num_sectors;
+	u32 mem_op;
+	unsigned sr_bp = 0;
+	unsigned sr_tb = 0;
+
+	sector_start = altera_epcq_addr_to_sector(flash, offset);
+	sector_end = altera_epcq_addr_to_sector(flash, offset + len);
+	num_sectors = flash->num_sectors;
+	dev_dbg(&dev->pdev->dev,
+		"%s: num_setor is %u,sector start is %u,sector end is %u\n",
+		__func__, num_sectors, sector_start, sector_end);
+
+	mutex_lock(&flash->lock);
+	/* wait until finished previous write command. */
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		goto err;
+
+	if (sector_start >= num_sectors/2) {
+		if (sector_start < num_sectors-(num_sectors / 4))
+			sr_bp = __ilog2_u32(num_sectors);
+		else if (sector_start < num_sectors-(num_sectors / 8))
+			sr_bp = __ilog2_u32(num_sectors) - 1;
+		else if (sector_start < num_sectors-(num_sectors / 16))
+			sr_bp = __ilog2_u32(num_sectors) - 2;
+		else if (sector_start < num_sectors-(num_sectors / 32))
+			sr_bp = __ilog2_u32(num_sectors) - 3;
+		else if (sector_start < num_sectors-(num_sectors / 64))
+			sr_bp = __ilog2_u32(num_sectors) - 4;
+		else if (sector_start < num_sectors-(num_sectors / 128))
+			sr_bp = __ilog2_u32(num_sectors) - 5;
+		else if (sector_start < num_sectors-(num_sectors / 256))
+			sr_bp = __ilog2_u32(num_sectors) - 6;
+		else if (sector_start < num_sectors-(num_sectors / 512))
+			sr_bp = __ilog2_u32(num_sectors) - 7;
+		else if (sector_start < num_sectors-(num_sectors / 1024))
+			sr_bp = __ilog2_u32(num_sectors) - 8;
+		else
+			sr_bp = 0;  /* non area protected */
+
+		if (sr_bp < 0) {
+			dev_err(&dev->pdev->dev, "%s: address is out of range\n"
+				, __func__);
+			ret = -EINVAL;
+			goto err;
+		}
+		/*set TB = 0*/
+		sr_tb = 0;
+
+	} else {
+		if (sector_end < 1)
+			sr_bp = 1;
+		else if (sector_end < 2)
+			sr_bp = 2;
+		else if (sector_end < 4)
+			sr_bp = 3;
+		else if (sector_end < 8)
+			sr_bp = 4;
+		else if (sector_end < 16)
+			sr_bp = 5;
+		else if (sector_end < 32)
+			sr_bp = 6;
+		else if (sector_end < 64)
+			sr_bp = 7;
+		else if (sector_end < 128)
+			sr_bp = 8;
+		else if (sector_end < 256)
+			sr_bp = 9;
+		else if (sector_end < 512)
+			sr_bp = 10;
+		else
+			sr_bp = 16; /*protect all areas*/
+
+		sr_tb = 1;
+	}
+
+	mem_op = (sr_tb << 12) | (sr_bp << 8);
+	mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
+	mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
+	writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
+
+err:
+	mutex_unlock(&flash->lock);
+	return ret;
+}
+
+static int altera_epcq_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+	struct altera_epcq_flash *flash = get_flash_data(mtd);
+	struct altera_epcq *dev = mtd->priv;
+
+	int ret = 0;
+	u32 mem_op;
+
+	mutex_lock(&flash->lock);
+	/* wait until finished previous write command. */
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		goto err;
+	dev_info(&dev->pdev->dev, "Unlock all protected area\n");
+	mem_op = 0;
+	mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
+	mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
+	writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
+
+err:
+	mutex_unlock(&flash->lock);
+	return ret;
+}
+
+static void altera_epcq_chip_select(struct altera_epcq *dev, u32 bank)
+{
+	u32 val = 0;
+
+	switch (bank) {
+	case 0:
+		val = EPCQ_CHIP_SELECT_0;
+		break;
+	case 1:
+		val = EPCQ_CHIP_SELECT_1;
+		break;
+	case 2:
+		val = EPCQ_CHIP_SELECT_2;
+		break;
+	default:
+		return;
+	}
+
+	writel(val, dev->csr_base + EPCQ_CHIP_SELECT_REG);
+}
+
+static int altera_epcq_probe_flash(struct altera_epcq *dev, u32 bank)
+{
+	int ret = 0;
+	u32 val = 0;
+
+	mutex_lock(&dev->lock);
+
+	/*select bank*/
+	altera_epcq_chip_select(dev, bank);
+
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		goto err;
+
+	/* get device sillicon id */
+	if (dev->is_epcs)
+		val = readl(dev->csr_base + EPCQ_SID_REG);
+	else
+		val = readl(dev->csr_base + EPCQ_RDID_REG);
+
+	/* get flash index based on the device list*/
+	ret = get_flash_index(val, dev->is_epcs);
+	return 0;
+err:
+	mutex_unlock(&dev->lock);
+	return ret;
+}
+
+static int altera_epcq_probe_config_dt(struct platform_device *pdev,
+				       struct device_node *np,
+				       struct altera_epcq_plat_data *pdata)
+{
+	struct device_node *pp = NULL;
+	struct resource *epcq_res;
+	int i = 0;
+	u32 id;
+
+	pdata->is_epcs = of_property_read_bool(np, "is-epcs");
+
+	epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						"csr_base");
+	if (!epcq_res) {
+		dev_err(&pdev->dev, "resource csr base is not defined\n");
+		return -ENODEV;
+	}
+
+	pdata->csr_base = devm_ioremap_resource(&pdev->dev, epcq_res);
+	if (IS_ERR(pdata->csr_base)) {
+		dev_err(&pdev->dev, "%s: ERROR: failed to map csr base\n",
+			__func__);
+		return PTR_ERR(pdata->csr_base);
+	}
+
+	epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						"data_base");
+	if (!epcq_res) {
+		dev_err(&pdev->dev, "resource data base is not defined\n");
+		return -ENODEV;
+	}
+
+	pdata->data_base = devm_ioremap_resource(&pdev->dev, epcq_res);
+	if (IS_ERR(pdata->data_base)) {
+		dev_err(&pdev->dev, "%s: ERROR: failed to map data base\n",
+			__func__);
+		return PTR_ERR(pdata->data_base);
+	}
+
+	pdata->board_flash_info = devm_kzalloc(&pdev->dev,
+					       sizeof(*pdata->board_flash_info),
+					       GFP_KERNEL);
+
+	/* Fill structs for each subnode (flash device) */
+	while ((pp = of_get_next_child(np, pp))) {
+		struct altera_epcq_flash_info *flash_info;
+
+		flash_info = &pdata->board_flash_info[i];
+		pdata->np[i] = pp;
+
+		/* Read bank id from DT */
+		if (of_get_property(pp, "reg", &id))
+			pdata->board_flash_info[i].bank = id;
+		i++;
+	}
+	pdata->num_flashes = i;
+	return 0;
+}
+
+static int altera_epcq_setup_banks(struct platform_device *pdev,
+				   u32 bank, struct device_node *np,
+				   struct altera_epcq_plat_data *pdata)
+{
+	struct altera_epcq *dev = platform_get_drvdata(pdev);
+	struct mtd_part_parser_data ppdata = {};
+	struct altera_epcq_flash_info *flash_info;
+	struct altera_epcq_flash *flash;
+	struct mtd_partition *parts = NULL;
+	int count = 0;
+	int flash_index;
+	int ret = 0;
+	uint64_t size;
+	uint32_t sector_size;
+
+	flash_info = &pdata->board_flash_info[bank];
+	if (!flash_info)
+		return -ENODEV;
+
+	if (bank > pdata->num_flashes - 1)
+		return -EINVAL;
+
+	flash = devm_kzalloc(&pdev->dev, sizeof(*flash), GFP_KERNEL);
+	if (!flash)
+		return -ENOMEM;
+	flash->bank = bank;
+
+	mutex_init(&flash->lock);
+
+	/* verify whether flash is really present on board */
+	flash_index = altera_epcq_probe_flash(dev, bank);
+	if (flash_index < 0) {
+		dev_info(&dev->pdev->dev, "epcq:flash%d not found\n",
+			 flash->bank);
+		return flash_index;
+	}
+
+	dev->flash[bank] = flash;
+
+	size = flash_devices[flash_index].size_inbytes;
+	sector_size = flash_devices[flash_index].sectorsize_inbytes;
+	/*use do_div instead of plain div to avoid linker err*/
+	do_div(size, sector_size);
+	flash->num_sectors = size;
+
+	/*mtd framework */
+	flash->mtd.priv = dev;
+	flash->mtd.name = flash_devices[flash_index].name;
+	flash->mtd.type = MTD_NORFLASH;
+	flash->mtd.writesize = 1;
+	flash->mtd.flags = MTD_CAP_NORFLASH;
+	flash->mtd.size = flash_devices[flash_index].size_inbytes;
+	flash->mtd.erasesize = flash_devices[flash_index].sectorsize_inbytes;
+	flash->mtd._erase = altera_epcq_erase;
+	flash->mtd._read = altera_epcq_read;
+	flash->mtd._write = altera_epcq_write;
+	flash->mtd._lock = altera_epcq_lock;
+	flash->mtd._unlock = altera_epcq_unlock;
+
+	dev_info(&pdev->dev, "mtd .name=%s .size=0x%llx (%lluM)\n",
+		 flash->mtd.name, (long long)flash->mtd.size,
+		 (long long)(flash->mtd.size >> 20));
+
+	dev_info(&pdev->dev, ".erasesize = 0x%x(%uK)\n",
+		 flash->mtd.erasesize, flash->mtd.erasesize >> 10);
+
+	ppdata.of_node = np;
+
+	ret = mtd_device_parse_register(&flash->mtd, NULL, &ppdata, parts,
+					count);
+	if (ret) {
+		dev_err(&pdev->dev, "Err MTD partition=%d\n", ret);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	mtd_device_unregister(&flash->mtd);
+	return ret;
+}
+
+static int altera_epcq_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct altera_epcq_plat_data *pdata = NULL;
+	struct altera_epcq *dev;
+	int ret = 0;
+	int i;
+
+	if (!np) {
+		ret = -ENODEV;
+		dev_err(&pdev->dev, "no device found\n");
+		goto err;
+	}
+
+	pdata = devm_kzalloc(&pdev->dev,
+			     sizeof(struct altera_epcq_plat_data),
+			     GFP_KERNEL);
+
+	if (!pdata) {
+		ret = -ENOMEM;
+		dev_err(&pdev->dev, "%s: ERROR: no memory\n", __func__);
+		goto err;
+	}
+	ret = altera_epcq_probe_config_dt(pdev, np, pdata);
+	if (ret) {
+		ret = -ENODEV;
+		dev_err(&pdev->dev, "probe fail\n");
+		goto err;
+	}
+
+	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
+	if (!dev) {
+		ret = -ENOMEM;
+		dev_err(&pdev->dev, "mem alloc fail\n");
+		goto err;
+	}
+	mutex_init(&dev->lock);
+	dev->pdev = pdev;
+	dev->is_epcs = pdata->is_epcs;
+	dev->csr_base = pdata->csr_base;
+	dev->data_base = pdata->data_base;
+
+	/*check number of flashes*/
+	dev->num_flashes = pdata->num_flashes;
+	if (dev->num_flashes > MAX_NUM_FLASH_CHIP) {
+		dev_err(&pdev->dev, "exceeding max number of flashes\n");
+		dev->num_flashes = MAX_NUM_FLASH_CHIP;
+	}
+
+	/* check clock*/
+	dev->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(dev->clk)) {
+		ret = PTR_ERR(dev->clk);
+		goto err;
+	}
+	ret = clk_prepare_enable(dev->clk);
+	if (ret)
+		goto err;
+
+	platform_set_drvdata(pdev, dev);
+
+	/* loop for each serial flash which is connected to epcq */
+	for (i = 0; i < dev->num_flashes; i++) {
+		ret = altera_epcq_setup_banks(pdev, i, pdata->np[i], pdata);
+		if (ret) {
+			dev_err(&pdev->dev, "bank setup failed\n");
+			goto err_bank_setup;
+		}
+	}
+
+	return 0;
+
+err_bank_setup:
+	clk_disable_unprepare(dev->clk);
+err:
+	return ret;
+}
+
+static int altera_epcq_remove(struct platform_device *pdev)
+{
+	struct altera_epcq *dev;
+	struct altera_epcq_flash *flash;
+	int ret, i;
+
+	dev = platform_get_drvdata(pdev);
+
+	/* clean up for all nor flash */
+	for (i = 0; i < dev->num_flashes; i++) {
+		flash = dev->flash[i];
+		if (!flash)
+			continue;
+
+		/* clean up mtd stuff */
+		ret = mtd_device_unregister(&flash->mtd);
+		if (ret)
+			dev_err(&pdev->dev, "error removing mtd\n");
+	}
+
+	clk_disable_unprepare(dev->clk);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int altera_epcq_suspend(struct device *dev)
+{
+	struct altera_epcq *sdev = dev_get_drvdata(dev);
+
+	clk_disable_unprepare(sdev->clk);
+
+	return 0;
+}
+
+static int altera_epcq_resume(struct device *dev)
+{
+	struct altera_epcq *sdev = dev_get_drvdata(dev);
+	int ret = -EPERM;
+
+	ret = clk_prepare_enable(sdev->clk);
+
+	return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(altera_epcq_pm_ops, altera_epcq_suspend,
+			 altera_epcq_resume);
+#endif
+
+static const struct of_device_id altera_epcq_id_table[] = {
+	{ .compatible = "altr,epcq-1.0" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, altera_epcq_id_table);
+
+static struct platform_driver altera_epcq_driver = {
+	.driver = {
+		.name = ALTERA_EPCQ_RESOURCE_NAME,
+		.bus = &platform_bus_type,
+		.owner = THIS_MODULE,
+		.of_match_table = altera_epcq_id_table,
+#ifdef CONFIG_PM
+		.pm = &altera_epcq_pm_ops,
+#endif
+	},
+	.probe = altera_epcq_probe,
+	.remove = altera_epcq_remove,
+};
+module_platform_driver(altera_epcq_driver);
+
+MODULE_AUTHOR("Viet Nga Dao <vndao@altera.com>");
+MODULE_DESCRIPTION("MTD Altera EPCQ Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mtd/devices/altera_epcq.h b/drivers/mtd/devices/altera_epcq.h
new file mode 100644
index 0000000..c3d15e5
--- /dev/null
+++ b/drivers/mtd/devices/altera_epcq.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2014 Altera Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ALTERA_ECPQ_H
+#define __ALTERA_ECPQ_H
+
+#define ALTERA_EPCQ_RESOURCE_NAME        "altera_epcq"
+/* max possible slots for serial flash chip in the EPCQ controller */
+#define MAX_NUM_FLASH_CHIP	3
+
+/* macro to define partitions for flash devices */
+#define DEFINE_PARTS(n, of, s)		\
+{					\
+	.name = n,			\
+	.offset = of,			\
+	.size = s,			\
+}
+
+struct altera_epcq_flash_info {
+	u32 bank;
+	struct mtd_partition *partitions;
+	int nr_partitions;
+};
+
+struct altera_epcq_plat_data {
+	void __iomem *csr_base;
+	void __iomem *data_base;
+	bool is_epcs;
+	u32 num_flashes;
+	struct altera_epcq_flash_info *board_flash_info;
+	struct device_node *np[MAX_NUM_FLASH_CHIP];
+};
+
+struct altera_epcq {
+	struct clk *clk;
+	bool is_epcs;
+	struct mutex lock;	/*device lock*/
+	void __iomem *csr_base;
+	void __iomem *data_base;
+	struct platform_device *pdev;
+	u32 num_flashes;
+	struct altera_epcq_flash *flash[MAX_NUM_FLASH_CHIP];
+};
+
+struct altera_epcq_flash {
+	u32 bank;
+	u32 num_sectors;
+	u32 num_parts;
+	struct mtd_partition *parts;
+	struct mutex lock;	/*flash lock*/
+	struct mtd_info mtd;
+};
+
+/* Define max times to check status register before we give up. */
+#define EPCQ_MAX_TIME_OUT			(40 * HZ)
+
+/* defines for status register */
+#define EPCQ_SR_REG				0x0
+#define EPCQ_SR_WIP_MASK			0x00000001
+#define EPCQ_SR_WIP				0x1
+#define EPCQ_SR_WEL				0x2
+#define EPCQ_SR_BP0				0x4
+#define EPCQ_SR_BP1				0x8
+#define EPCQ_SR_BP2				0x10
+#define EPCQ_SR_BP3				0x40
+#define EPCQ_SR_TB				0x20
+
+/* defines for device id register */
+#define EPCQ_SID_REG				0x4
+#define EPCQ_RDID_REG				0x8
+#define EPCQ_RDID_MASK				0x000000FF
+/*
+ * EPCQ_MEM_OP register offset
+ *
+ * The EPCQ_MEM_OP register is used to do memory protect and erase operations
+ *
+ */
+#define EPCQ_MEM_OP_REG				0xC
+
+#define EPCQ_MEM_OP_CMD_MASK			0x00000003
+#define EPCQ_MEM_OP_BULK_ERASE_CMD		0x00000001
+#define EPCQ_MEM_OP_SECTOR_ERASE_CMD		0x00000002
+#define EPCQ_MEM_OP_SECTOR_PROTECT_CMD		0x00000003
+#define EPCQ_MEM_OP_SECTOR_VALUE_MASK		0x0003FF00
+#define EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK	0x00001F00
+#define EPCQ_MEM_OP_SECTOR_PROTECT_SHIFT	8
+/*
+ * EPCQ_ISR register offset
+ *
+ * The EPCQ_ISR register is used to determine whether an invalid write or erase
+ * operation trigerred an interrupt
+ *
+ */
+#define EPCQ_ISR_REG				0x10
+
+#define EPCQ_ISR_ILLEGAL_ERASE_MASK		0x00000001
+#define EPCQ_ISR_ILLEGAL_WRITE_MASK		0x00000002
+
+/*
+ * EPCQ_IMR register offset
+ *
+ * The EPCQ_IMR register is used to mask the invalid erase or the invalid write
+ * interrupts.
+ *
+ */
+#define EPCQ_IMR_REG				0x14
+#define EPCQ_IMR_ILLEGAL_ERASE_MASK		0x00000001
+
+#define EPCQ_IMR_ILLEGAL_WRITE_MASK		0x00000002
+
+#define EPCQ_CHIP_SELECT_REG			0x18
+#define EPCQ_CHIP_SELECT_MASK			0x00000007
+#define EPCQ_CHIP_SELECT_0			0x00000001
+#define EPCQ_CHIP_SELECT_1			0x00000002
+#define EPCQ_CHIP_SELECT_2			0x00000004
+
+#endif /* __ALTERA_ECPQ_H */
-- 
1.7.7.4


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2014-12-18  8:23 ` vndao
  0 siblings, 0 replies; 39+ messages in thread
From: vndao @ 2014-12-18  8:23 UTC (permalink / raw)
  To: dwmw2, computersforpeace
  Cc: Viet Nga Dao, devicetree, linux-mtd, linux-kernel, ngachi86

From: Viet Nga Dao <vndao@altera.com>

Altera EPCQ Controller is a soft IP which enables access to Altera EPCQ and
EPCS flash chips. This patch adds driver for these devices.

Signed-off-by: Viet Nga Dao <vndao@altera.com>
---
 .../devicetree/bindings/mtd/altera_epcq.txt        |   45 ++
 drivers/mtd/devices/Kconfig                        |   12 +
 drivers/mtd/devices/Makefile                       |    2 +-
 drivers/mtd/devices/altera_epcq.c                  |  804 ++++++++++++++++++++
 drivers/mtd/devices/altera_epcq.h                  |  130 ++++
 5 files changed, 992 insertions(+), 1 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mtd/altera_epcq.txt
 create mode 100644 drivers/mtd/devices/altera_epcq.c
 create mode 100644 drivers/mtd/devices/altera_epcq.h

diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
new file mode 100644
index 0000000..d14f50e
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
@@ -0,0 +1,45 @@
+* MTD Altera EPCQ driver
+
+Required properties:
+- compatible: Should be "altr,epcq-1.0"
+- reg: Address and length of the register set  for the device. It contains
+  the information of registers in the same order as described by reg-names
+- reg-names: Should contain the reg names
+  "csr_base": Should contain the register configuration base address
+  "data_base": Should contain the data base address
+- is-epcs: boolean type.
+		If present, the device contains EPCS flashes.
+		Otherwise, it contains EPCQ flashes.
+- #address-cells: Must be <1>.
+- #size-cells: Must be <0>.
+- flash device tree subnode, there must be a node with the following fields:
+	- reg: Should contain the flash id
+	- #address-cells: please refer to /mtd/partition.txt
+	- #size-cells: please refer to /mtd/partition.txt
+	For partitions inside each flash, please refer to /mtd/partition.txt
+
+Example:
+
+			epcq_controller_0: epcq@0x000000000 {
+				compatible = "altr,epcq-1.0";
+				reg = <0x00000001 0x00000000 0x00000020>,
+					<0x00000000 0x00000000 0x02000000>;
+				reg-names = "csr_base", "data_base";
+				#address-cells = <1>;
+				#size-cells = <0>;
+				flash0: epcq256@0 {
+					reg = <0>;
+					#address-cells = <1>;
+					#size-cells = <1>;
+					partition@0 {
+						/* 16 MB for raw data. */
+						label = "EPCQ Flash 0 raw data";
+						reg = <0x0 0x1000000>;
+					};
+					partition@1000000 {
+						/* 16 MB for jffs2 data. */
+						label = "EPCQ Flash 0 JFFS 2";
+						reg = <0x1000000 0x1000000>;
+					};
+				};
+			}; //end epcq@0x000000000 (epcq_controller_0)
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index c49d0b1..020b864 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -218,6 +218,18 @@ config MTD_ST_SPI_FSM
 	  SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
 	  for a subset of connected Serial Flash devices.
 
+config MTD_ALTERA_EPCQ
+	tristate "Support Altera EPCQ/EPCS Flash chips"
+	depends on OF
+	help
+	  This enables access to Altera EPCQ/EPCS flash chips, used for data
+	  storage. See the driver source for the current list,
+	  or to add other chips.
+
+	  If you want to compile this driver as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you want),
+	  say M here and read <file:Documentation/kbuild/modules.txt>.
+
 if MTD_DOCG3
 config BCH_CONST_M
 	default 14
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index f0b0e61..b429c4d 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -16,6 +16,6 @@ obj-$(CONFIG_MTD_SPEAR_SMI)	+= spear_smi.o
 obj-$(CONFIG_MTD_SST25L)	+= sst25l.o
 obj-$(CONFIG_MTD_BCM47XXSFLASH)	+= bcm47xxsflash.o
 obj-$(CONFIG_MTD_ST_SPI_FSM)    += st_spi_fsm.o
-
+obj-$(CONFIG_MTD_ALTERA_EPCQ)		+= altera_epcq.o
 
 CFLAGS_docg3.o			+= -I$(src)
diff --git a/drivers/mtd/devices/altera_epcq.c b/drivers/mtd/devices/altera_epcq.c
new file mode 100644
index 0000000..09213d5
--- /dev/null
+++ b/drivers/mtd/devices/altera_epcq.c
@@ -0,0 +1,804 @@
+/*
+ * Copyright (C) 2014 Altera Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/param.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include "altera_epcq.h"
+
+/* data structure to maintain flash ids from different vendors */
+struct flash_device {
+	char *name;
+	bool is_epcs;
+	u32 device_id;
+	uint32_t sectorsize_inbytes;
+	uint64_t size_inbytes;
+	u32 pagesize;
+};
+
+#define FLASH_ID(_n, _is_epcs, _id, _ssize, _size, _psize)	\
+{				\
+	.name = (_n),		\
+	.is_epcs = (_is_epcs),		\
+	.device_id = (_id),	\
+	.sectorsize_inbytes = (_ssize),	\
+	.size_inbytes = (_size),	\
+	.pagesize = (_psize),	\
+}
+
+static struct flash_device flash_devices[] = {
+	FLASH_ID("epcq16"    , 0, 0x15, 0x10000, 0x200000, 0x100),
+	FLASH_ID("epcq32"    , 0, 0x16, 0x10000, 0x400000, 0x100),
+	FLASH_ID("epcq64"    , 0, 0x17, 0x10000, 0x800000, 0x100),
+	FLASH_ID("epcq128"   , 0, 0x18, 0x10000, 0x1000000, 0x100),
+	FLASH_ID("epcq256"   , 0, 0x19, 0x10000, 0x2000000, 0x100),
+	FLASH_ID("epcq512"   , 0, 0x20, 0x10000, 0x4000000, 0x100),
+
+	FLASH_ID("epcs16"    , 1, 0x14, 0x10000, 0x200000, 0x100),
+	FLASH_ID("epcs64"    , 1, 0x16, 0x10000, 0x800000, 0x100),
+	FLASH_ID("epcs128"   , 1, 0x18, 0x40000, 0x1000000, 0x100),
+};
+
+static inline struct altera_epcq_flash *get_flash_data(struct mtd_info *mtd)
+{
+	return container_of(mtd, struct altera_epcq_flash, mtd);
+}
+
+static u32 altera_epcq_read_sr(struct altera_epcq *dev)
+{
+	return readl(dev->csr_base + EPCQ_SR_REG);
+}
+
+static int altera_epcq_wait_till_ready(struct altera_epcq *dev)
+{
+	unsigned long finish;
+	int status;
+
+	finish = jiffies + EPCQ_MAX_TIME_OUT;
+	do {
+		status = altera_epcq_read_sr(dev);
+		if (status < 0)
+			return status;
+		else if (!(status & EPCQ_SR_WIP))
+			return 0;
+
+		cond_resched();
+	} while (!time_after_eq(jiffies, finish));
+
+	dev_err(&dev->pdev->dev, "epcq controller is busy, timeout\n");
+	return -EBUSY;
+}
+
+static int get_flash_index(u32 flash_id, bool is_epcs)
+{
+	int index;
+
+	for (index = 0; index < ARRAY_SIZE(flash_devices); index++) {
+		if (flash_devices[index].device_id == flash_id &&
+		    flash_devices[index].is_epcs == is_epcs)
+			return index;
+	}
+
+	/* Memory chip is not listed and not supported */
+	return -ENODEV;
+}
+
+static int altera_epcq_write_erase_check(struct altera_epcq *dev,
+					 bool write_erase)
+{
+	u32 val;
+	u32 mask;
+
+	if (write_erase)
+		mask = EPCQ_ISR_ILLEGAL_WRITE_MASK;
+	else
+		mask = EPCQ_ISR_ILLEGAL_ERASE_MASK;
+
+	val = readl(dev->csr_base + EPCQ_ISR_REG);
+	if (val & mask) {
+		dev_err(&dev->pdev->dev,
+			"write/erase failed, sector might be protected\n");
+		/*clear this status for next use*/
+		writel(val, dev->csr_base + EPCQ_ISR_REG);
+		return -EIO;
+	}
+	return 0;
+}
+
+static int altera_epcq_erase_chip(struct mtd_info *mtd)
+{
+	int ret;
+	u32 val;
+	struct altera_epcq *dev = mtd->priv;
+
+	/* Wait until finished previous write command. */
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		return ret;
+
+	/* erase chip. */
+	val = EPCQ_MEM_OP_BULK_ERASE_CMD;
+	writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
+
+	/* Wait until finished previous write command. */
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		return ret;
+
+	 /* check whether write triggered a illegal write interrupt */
+	ret = altera_epcq_write_erase_check(dev, 0);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int altera_epcq_addr_to_sector(struct altera_epcq_flash *flash,
+				      int addr_offset)
+{
+	int sector = 0;
+	int i;
+
+	for (i = 0; i < flash->num_sectors; i++) {
+		if (addr_offset >= i * flash->mtd.erasesize && addr_offset <
+		    (i * flash->mtd.erasesize + flash->mtd.erasesize)) {
+			sector = i;
+			return sector;
+		}
+	}
+	return -1;
+}
+
+static int altera_epcq_erase_sector(struct mtd_info *mtd,
+				    int addr_offset)
+{
+	struct altera_epcq_flash *flash = get_flash_data(mtd);
+	struct altera_epcq *dev = mtd->priv;
+	u32 val;
+	int ret;
+	int sector_value;
+
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		return ret;
+
+	sector_value = altera_epcq_addr_to_sector(flash, addr_offset);
+
+	/* sanity check that block_offset is a valid sector number */
+	if (sector_value < 0)
+		return -EINVAL;
+
+	/* sector value should occupy bits 17:8 */
+	val = (sector_value << 8) & EPCQ_MEM_OP_SECTOR_VALUE_MASK;
+
+	/* sector erase commands occupies lower 2 bits */
+	val |= EPCQ_MEM_OP_SECTOR_ERASE_CMD;
+
+	/* write sector erase command to EPCQ_MEM_OP register*/
+	writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
+
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		return ret;
+
+	 /* check whether write triggered a illegal write interrupt */
+	ret = altera_epcq_write_erase_check(dev, 0);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int altera_epcq_erase(struct mtd_info *mtd, struct erase_info *e_info)
+{
+	u32 addr;
+	int ret;
+	u32 len;
+	struct altera_epcq_flash *flash = get_flash_data(mtd);
+	struct altera_epcq *dev = mtd->priv;
+
+	addr = e_info->addr;
+	len = e_info->len;
+
+	if (flash->bank > dev->num_flashes - 1) {
+		dev_err(&dev->pdev->dev, "Invalid chip id\n");
+		return -EINVAL;
+	}
+	mutex_lock(&flash->lock);
+
+	/*erase whole chip*/
+	if (len == mtd->size) {
+		if (altera_epcq_erase_chip(mtd)) {
+			e_info->state = MTD_ERASE_FAILED;
+			mutex_unlock(&flash->lock);
+			return -EIO;
+		}
+	/*"sector"-at-a-time erase*/
+	} else {
+		while (len) {
+			ret = altera_epcq_erase_sector(mtd, addr);
+			if (ret) {
+				e_info->state = MTD_ERASE_FAILED;
+				mutex_unlock(&flash->lock);
+				return -EIO;
+			}
+			addr += mtd->erasesize;
+			if (len < mtd->erasesize)
+				len = 0;
+			else
+				len -= mtd->erasesize;
+		}
+	}
+	mutex_unlock(&flash->lock);
+	e_info->state = MTD_ERASE_DONE;
+	mtd_erase_callback(e_info);
+
+	return 0;
+}
+
+static int altera_epcq_read(struct mtd_info *mtd, loff_t from, size_t len,
+			    size_t *retlen, u8 *buf)
+{
+	struct altera_epcq_flash *flash = get_flash_data(mtd);
+	struct altera_epcq *dev = mtd->priv;
+	void *src;
+	int ret = 0;
+
+	if (!flash || !dev)
+		return -ENODEV;
+
+	if (flash->bank > dev->num_flashes - 1) {
+		dev_err(&dev->pdev->dev, "Invalid chip id\n");
+		return -EINVAL;
+	}
+
+	src = dev->data_base + from;
+
+	mutex_lock(&flash->lock);
+	/* wait till previous write/erase is done. */
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		goto err;
+
+	memcpy_fromio(buf, (u8 *)src, len);
+	*retlen = len;
+
+err:
+	mutex_unlock(&flash->lock);
+	return ret;
+}
+
+static int altera_epcq_write(struct mtd_info *mtd, loff_t to, size_t len,
+			     size_t *retlen, const u8 *buf)
+{
+	struct altera_epcq_flash *flash = get_flash_data(mtd);
+	struct altera_epcq *dev = mtd->priv;
+	void *dest;
+	int ret = 0;
+
+	if (!flash || !dev)
+		return -ENODEV;
+
+	if (flash->bank > dev->num_flashes - 1) {
+		dev_err(&dev->pdev->dev, "Invalid chip id\n");
+		return -EINVAL;
+	}
+	dest = dev->data_base + to;
+
+	mutex_lock(&flash->lock);
+
+	/* wait until finished previous write command. */
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		goto err;
+
+	memcpy_toio(dest, buf, len);
+	*retlen += len;
+
+	/* wait until finished previous write command. */
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		goto err;
+
+	 /* check whether write triggered a illegal write interrupt */
+	ret = altera_epcq_write_erase_check(dev, 1);
+	if (ret < 0)
+		goto err;
+
+err:
+	mutex_unlock(&flash->lock);
+	return ret;
+}
+
+static int altera_epcq_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+	struct altera_epcq_flash *flash = get_flash_data(mtd);
+	struct altera_epcq *dev = mtd->priv;
+	uint32_t offset = ofs;
+	int ret = 0;
+	u32 sector_start, sector_end;
+	u32 num_sectors;
+	u32 mem_op;
+	unsigned sr_bp = 0;
+	unsigned sr_tb = 0;
+
+	sector_start = altera_epcq_addr_to_sector(flash, offset);
+	sector_end = altera_epcq_addr_to_sector(flash, offset + len);
+	num_sectors = flash->num_sectors;
+	dev_dbg(&dev->pdev->dev,
+		"%s: num_setor is %u,sector start is %u,sector end is %u\n",
+		__func__, num_sectors, sector_start, sector_end);
+
+	mutex_lock(&flash->lock);
+	/* wait until finished previous write command. */
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		goto err;
+
+	if (sector_start >= num_sectors/2) {
+		if (sector_start < num_sectors-(num_sectors / 4))
+			sr_bp = __ilog2_u32(num_sectors);
+		else if (sector_start < num_sectors-(num_sectors / 8))
+			sr_bp = __ilog2_u32(num_sectors) - 1;
+		else if (sector_start < num_sectors-(num_sectors / 16))
+			sr_bp = __ilog2_u32(num_sectors) - 2;
+		else if (sector_start < num_sectors-(num_sectors / 32))
+			sr_bp = __ilog2_u32(num_sectors) - 3;
+		else if (sector_start < num_sectors-(num_sectors / 64))
+			sr_bp = __ilog2_u32(num_sectors) - 4;
+		else if (sector_start < num_sectors-(num_sectors / 128))
+			sr_bp = __ilog2_u32(num_sectors) - 5;
+		else if (sector_start < num_sectors-(num_sectors / 256))
+			sr_bp = __ilog2_u32(num_sectors) - 6;
+		else if (sector_start < num_sectors-(num_sectors / 512))
+			sr_bp = __ilog2_u32(num_sectors) - 7;
+		else if (sector_start < num_sectors-(num_sectors / 1024))
+			sr_bp = __ilog2_u32(num_sectors) - 8;
+		else
+			sr_bp = 0;  /* non area protected */
+
+		if (sr_bp < 0) {
+			dev_err(&dev->pdev->dev, "%s: address is out of range\n"
+				, __func__);
+			ret = -EINVAL;
+			goto err;
+		}
+		/*set TB = 0*/
+		sr_tb = 0;
+
+	} else {
+		if (sector_end < 1)
+			sr_bp = 1;
+		else if (sector_end < 2)
+			sr_bp = 2;
+		else if (sector_end < 4)
+			sr_bp = 3;
+		else if (sector_end < 8)
+			sr_bp = 4;
+		else if (sector_end < 16)
+			sr_bp = 5;
+		else if (sector_end < 32)
+			sr_bp = 6;
+		else if (sector_end < 64)
+			sr_bp = 7;
+		else if (sector_end < 128)
+			sr_bp = 8;
+		else if (sector_end < 256)
+			sr_bp = 9;
+		else if (sector_end < 512)
+			sr_bp = 10;
+		else
+			sr_bp = 16; /*protect all areas*/
+
+		sr_tb = 1;
+	}
+
+	mem_op = (sr_tb << 12) | (sr_bp << 8);
+	mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
+	mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
+	writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
+
+err:
+	mutex_unlock(&flash->lock);
+	return ret;
+}
+
+static int altera_epcq_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+	struct altera_epcq_flash *flash = get_flash_data(mtd);
+	struct altera_epcq *dev = mtd->priv;
+
+	int ret = 0;
+	u32 mem_op;
+
+	mutex_lock(&flash->lock);
+	/* wait until finished previous write command. */
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		goto err;
+	dev_info(&dev->pdev->dev, "Unlock all protected area\n");
+	mem_op = 0;
+	mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
+	mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
+	writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
+
+err:
+	mutex_unlock(&flash->lock);
+	return ret;
+}
+
+static void altera_epcq_chip_select(struct altera_epcq *dev, u32 bank)
+{
+	u32 val = 0;
+
+	switch (bank) {
+	case 0:
+		val = EPCQ_CHIP_SELECT_0;
+		break;
+	case 1:
+		val = EPCQ_CHIP_SELECT_1;
+		break;
+	case 2:
+		val = EPCQ_CHIP_SELECT_2;
+		break;
+	default:
+		return;
+	}
+
+	writel(val, dev->csr_base + EPCQ_CHIP_SELECT_REG);
+}
+
+static int altera_epcq_probe_flash(struct altera_epcq *dev, u32 bank)
+{
+	int ret = 0;
+	u32 val = 0;
+
+	mutex_lock(&dev->lock);
+
+	/*select bank*/
+	altera_epcq_chip_select(dev, bank);
+
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		goto err;
+
+	/* get device sillicon id */
+	if (dev->is_epcs)
+		val = readl(dev->csr_base + EPCQ_SID_REG);
+	else
+		val = readl(dev->csr_base + EPCQ_RDID_REG);
+
+	/* get flash index based on the device list*/
+	ret = get_flash_index(val, dev->is_epcs);
+	return 0;
+err:
+	mutex_unlock(&dev->lock);
+	return ret;
+}
+
+static int altera_epcq_probe_config_dt(struct platform_device *pdev,
+				       struct device_node *np,
+				       struct altera_epcq_plat_data *pdata)
+{
+	struct device_node *pp = NULL;
+	struct resource *epcq_res;
+	int i = 0;
+	u32 id;
+
+	pdata->is_epcs = of_property_read_bool(np, "is-epcs");
+
+	epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						"csr_base");
+	if (!epcq_res) {
+		dev_err(&pdev->dev, "resource csr base is not defined\n");
+		return -ENODEV;
+	}
+
+	pdata->csr_base = devm_ioremap_resource(&pdev->dev, epcq_res);
+	if (IS_ERR(pdata->csr_base)) {
+		dev_err(&pdev->dev, "%s: ERROR: failed to map csr base\n",
+			__func__);
+		return PTR_ERR(pdata->csr_base);
+	}
+
+	epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						"data_base");
+	if (!epcq_res) {
+		dev_err(&pdev->dev, "resource data base is not defined\n");
+		return -ENODEV;
+	}
+
+	pdata->data_base = devm_ioremap_resource(&pdev->dev, epcq_res);
+	if (IS_ERR(pdata->data_base)) {
+		dev_err(&pdev->dev, "%s: ERROR: failed to map data base\n",
+			__func__);
+		return PTR_ERR(pdata->data_base);
+	}
+
+	pdata->board_flash_info = devm_kzalloc(&pdev->dev,
+					       sizeof(*pdata->board_flash_info),
+					       GFP_KERNEL);
+
+	/* Fill structs for each subnode (flash device) */
+	while ((pp = of_get_next_child(np, pp))) {
+		struct altera_epcq_flash_info *flash_info;
+
+		flash_info = &pdata->board_flash_info[i];
+		pdata->np[i] = pp;
+
+		/* Read bank id from DT */
+		if (of_get_property(pp, "reg", &id))
+			pdata->board_flash_info[i].bank = id;
+		i++;
+	}
+	pdata->num_flashes = i;
+	return 0;
+}
+
+static int altera_epcq_setup_banks(struct platform_device *pdev,
+				   u32 bank, struct device_node *np,
+				   struct altera_epcq_plat_data *pdata)
+{
+	struct altera_epcq *dev = platform_get_drvdata(pdev);
+	struct mtd_part_parser_data ppdata = {};
+	struct altera_epcq_flash_info *flash_info;
+	struct altera_epcq_flash *flash;
+	struct mtd_partition *parts = NULL;
+	int count = 0;
+	int flash_index;
+	int ret = 0;
+	uint64_t size;
+	uint32_t sector_size;
+
+	flash_info = &pdata->board_flash_info[bank];
+	if (!flash_info)
+		return -ENODEV;
+
+	if (bank > pdata->num_flashes - 1)
+		return -EINVAL;
+
+	flash = devm_kzalloc(&pdev->dev, sizeof(*flash), GFP_KERNEL);
+	if (!flash)
+		return -ENOMEM;
+	flash->bank = bank;
+
+	mutex_init(&flash->lock);
+
+	/* verify whether flash is really present on board */
+	flash_index = altera_epcq_probe_flash(dev, bank);
+	if (flash_index < 0) {
+		dev_info(&dev->pdev->dev, "epcq:flash%d not found\n",
+			 flash->bank);
+		return flash_index;
+	}
+
+	dev->flash[bank] = flash;
+
+	size = flash_devices[flash_index].size_inbytes;
+	sector_size = flash_devices[flash_index].sectorsize_inbytes;
+	/*use do_div instead of plain div to avoid linker err*/
+	do_div(size, sector_size);
+	flash->num_sectors = size;
+
+	/*mtd framework */
+	flash->mtd.priv = dev;
+	flash->mtd.name = flash_devices[flash_index].name;
+	flash->mtd.type = MTD_NORFLASH;
+	flash->mtd.writesize = 1;
+	flash->mtd.flags = MTD_CAP_NORFLASH;
+	flash->mtd.size = flash_devices[flash_index].size_inbytes;
+	flash->mtd.erasesize = flash_devices[flash_index].sectorsize_inbytes;
+	flash->mtd._erase = altera_epcq_erase;
+	flash->mtd._read = altera_epcq_read;
+	flash->mtd._write = altera_epcq_write;
+	flash->mtd._lock = altera_epcq_lock;
+	flash->mtd._unlock = altera_epcq_unlock;
+
+	dev_info(&pdev->dev, "mtd .name=%s .size=0x%llx (%lluM)\n",
+		 flash->mtd.name, (long long)flash->mtd.size,
+		 (long long)(flash->mtd.size >> 20));
+
+	dev_info(&pdev->dev, ".erasesize = 0x%x(%uK)\n",
+		 flash->mtd.erasesize, flash->mtd.erasesize >> 10);
+
+	ppdata.of_node = np;
+
+	ret = mtd_device_parse_register(&flash->mtd, NULL, &ppdata, parts,
+					count);
+	if (ret) {
+		dev_err(&pdev->dev, "Err MTD partition=%d\n", ret);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	mtd_device_unregister(&flash->mtd);
+	return ret;
+}
+
+static int altera_epcq_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct altera_epcq_plat_data *pdata = NULL;
+	struct altera_epcq *dev;
+	int ret = 0;
+	int i;
+
+	if (!np) {
+		ret = -ENODEV;
+		dev_err(&pdev->dev, "no device found\n");
+		goto err;
+	}
+
+	pdata = devm_kzalloc(&pdev->dev,
+			     sizeof(struct altera_epcq_plat_data),
+			     GFP_KERNEL);
+
+	if (!pdata) {
+		ret = -ENOMEM;
+		dev_err(&pdev->dev, "%s: ERROR: no memory\n", __func__);
+		goto err;
+	}
+	ret = altera_epcq_probe_config_dt(pdev, np, pdata);
+	if (ret) {
+		ret = -ENODEV;
+		dev_err(&pdev->dev, "probe fail\n");
+		goto err;
+	}
+
+	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
+	if (!dev) {
+		ret = -ENOMEM;
+		dev_err(&pdev->dev, "mem alloc fail\n");
+		goto err;
+	}
+	mutex_init(&dev->lock);
+	dev->pdev = pdev;
+	dev->is_epcs = pdata->is_epcs;
+	dev->csr_base = pdata->csr_base;
+	dev->data_base = pdata->data_base;
+
+	/*check number of flashes*/
+	dev->num_flashes = pdata->num_flashes;
+	if (dev->num_flashes > MAX_NUM_FLASH_CHIP) {
+		dev_err(&pdev->dev, "exceeding max number of flashes\n");
+		dev->num_flashes = MAX_NUM_FLASH_CHIP;
+	}
+
+	/* check clock*/
+	dev->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(dev->clk)) {
+		ret = PTR_ERR(dev->clk);
+		goto err;
+	}
+	ret = clk_prepare_enable(dev->clk);
+	if (ret)
+		goto err;
+
+	platform_set_drvdata(pdev, dev);
+
+	/* loop for each serial flash which is connected to epcq */
+	for (i = 0; i < dev->num_flashes; i++) {
+		ret = altera_epcq_setup_banks(pdev, i, pdata->np[i], pdata);
+		if (ret) {
+			dev_err(&pdev->dev, "bank setup failed\n");
+			goto err_bank_setup;
+		}
+	}
+
+	return 0;
+
+err_bank_setup:
+	clk_disable_unprepare(dev->clk);
+err:
+	return ret;
+}
+
+static int altera_epcq_remove(struct platform_device *pdev)
+{
+	struct altera_epcq *dev;
+	struct altera_epcq_flash *flash;
+	int ret, i;
+
+	dev = platform_get_drvdata(pdev);
+
+	/* clean up for all nor flash */
+	for (i = 0; i < dev->num_flashes; i++) {
+		flash = dev->flash[i];
+		if (!flash)
+			continue;
+
+		/* clean up mtd stuff */
+		ret = mtd_device_unregister(&flash->mtd);
+		if (ret)
+			dev_err(&pdev->dev, "error removing mtd\n");
+	}
+
+	clk_disable_unprepare(dev->clk);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int altera_epcq_suspend(struct device *dev)
+{
+	struct altera_epcq *sdev = dev_get_drvdata(dev);
+
+	clk_disable_unprepare(sdev->clk);
+
+	return 0;
+}
+
+static int altera_epcq_resume(struct device *dev)
+{
+	struct altera_epcq *sdev = dev_get_drvdata(dev);
+	int ret = -EPERM;
+
+	ret = clk_prepare_enable(sdev->clk);
+
+	return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(altera_epcq_pm_ops, altera_epcq_suspend,
+			 altera_epcq_resume);
+#endif
+
+static const struct of_device_id altera_epcq_id_table[] = {
+	{ .compatible = "altr,epcq-1.0" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, altera_epcq_id_table);
+
+static struct platform_driver altera_epcq_driver = {
+	.driver = {
+		.name = ALTERA_EPCQ_RESOURCE_NAME,
+		.bus = &platform_bus_type,
+		.owner = THIS_MODULE,
+		.of_match_table = altera_epcq_id_table,
+#ifdef CONFIG_PM
+		.pm = &altera_epcq_pm_ops,
+#endif
+	},
+	.probe = altera_epcq_probe,
+	.remove = altera_epcq_remove,
+};
+module_platform_driver(altera_epcq_driver);
+
+MODULE_AUTHOR("Viet Nga Dao <vndao@altera.com>");
+MODULE_DESCRIPTION("MTD Altera EPCQ Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mtd/devices/altera_epcq.h b/drivers/mtd/devices/altera_epcq.h
new file mode 100644
index 0000000..c3d15e5
--- /dev/null
+++ b/drivers/mtd/devices/altera_epcq.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2014 Altera Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ALTERA_ECPQ_H
+#define __ALTERA_ECPQ_H
+
+#define ALTERA_EPCQ_RESOURCE_NAME        "altera_epcq"
+/* max possible slots for serial flash chip in the EPCQ controller */
+#define MAX_NUM_FLASH_CHIP	3
+
+/* macro to define partitions for flash devices */
+#define DEFINE_PARTS(n, of, s)		\
+{					\
+	.name = n,			\
+	.offset = of,			\
+	.size = s,			\
+}
+
+struct altera_epcq_flash_info {
+	u32 bank;
+	struct mtd_partition *partitions;
+	int nr_partitions;
+};
+
+struct altera_epcq_plat_data {
+	void __iomem *csr_base;
+	void __iomem *data_base;
+	bool is_epcs;
+	u32 num_flashes;
+	struct altera_epcq_flash_info *board_flash_info;
+	struct device_node *np[MAX_NUM_FLASH_CHIP];
+};
+
+struct altera_epcq {
+	struct clk *clk;
+	bool is_epcs;
+	struct mutex lock;	/*device lock*/
+	void __iomem *csr_base;
+	void __iomem *data_base;
+	struct platform_device *pdev;
+	u32 num_flashes;
+	struct altera_epcq_flash *flash[MAX_NUM_FLASH_CHIP];
+};
+
+struct altera_epcq_flash {
+	u32 bank;
+	u32 num_sectors;
+	u32 num_parts;
+	struct mtd_partition *parts;
+	struct mutex lock;	/*flash lock*/
+	struct mtd_info mtd;
+};
+
+/* Define max times to check status register before we give up. */
+#define EPCQ_MAX_TIME_OUT			(40 * HZ)
+
+/* defines for status register */
+#define EPCQ_SR_REG				0x0
+#define EPCQ_SR_WIP_MASK			0x00000001
+#define EPCQ_SR_WIP				0x1
+#define EPCQ_SR_WEL				0x2
+#define EPCQ_SR_BP0				0x4
+#define EPCQ_SR_BP1				0x8
+#define EPCQ_SR_BP2				0x10
+#define EPCQ_SR_BP3				0x40
+#define EPCQ_SR_TB				0x20
+
+/* defines for device id register */
+#define EPCQ_SID_REG				0x4
+#define EPCQ_RDID_REG				0x8
+#define EPCQ_RDID_MASK				0x000000FF
+/*
+ * EPCQ_MEM_OP register offset
+ *
+ * The EPCQ_MEM_OP register is used to do memory protect and erase operations
+ *
+ */
+#define EPCQ_MEM_OP_REG				0xC
+
+#define EPCQ_MEM_OP_CMD_MASK			0x00000003
+#define EPCQ_MEM_OP_BULK_ERASE_CMD		0x00000001
+#define EPCQ_MEM_OP_SECTOR_ERASE_CMD		0x00000002
+#define EPCQ_MEM_OP_SECTOR_PROTECT_CMD		0x00000003
+#define EPCQ_MEM_OP_SECTOR_VALUE_MASK		0x0003FF00
+#define EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK	0x00001F00
+#define EPCQ_MEM_OP_SECTOR_PROTECT_SHIFT	8
+/*
+ * EPCQ_ISR register offset
+ *
+ * The EPCQ_ISR register is used to determine whether an invalid write or erase
+ * operation trigerred an interrupt
+ *
+ */
+#define EPCQ_ISR_REG				0x10
+
+#define EPCQ_ISR_ILLEGAL_ERASE_MASK		0x00000001
+#define EPCQ_ISR_ILLEGAL_WRITE_MASK		0x00000002
+
+/*
+ * EPCQ_IMR register offset
+ *
+ * The EPCQ_IMR register is used to mask the invalid erase or the invalid write
+ * interrupts.
+ *
+ */
+#define EPCQ_IMR_REG				0x14
+#define EPCQ_IMR_ILLEGAL_ERASE_MASK		0x00000001
+
+#define EPCQ_IMR_ILLEGAL_WRITE_MASK		0x00000002
+
+#define EPCQ_CHIP_SELECT_REG			0x18
+#define EPCQ_CHIP_SELECT_MASK			0x00000007
+#define EPCQ_CHIP_SELECT_0			0x00000001
+#define EPCQ_CHIP_SELECT_1			0x00000002
+#define EPCQ_CHIP_SELECT_2			0x00000004
+
+#endif /* __ALTERA_ECPQ_H */
-- 
1.7.7.4


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH mtd] mtd:devices: Add Altera EPCQ Driver
@ 2014-12-18  8:23 ` vndao
  0 siblings, 0 replies; 39+ messages in thread
From: vndao @ 2014-12-18  8:23 UTC (permalink / raw)
  To: dwmw2, computersforpeace
  Cc: Viet Nga Dao, devicetree, linux-mtd, linux-kernel, ngachi86

From: Viet Nga Dao <vndao@altera.com>

Altera EPCQ Controller is a soft IP which enables access to Altera EPCQ and
EPCS flash chips. This patch adds driver for these devices.

Signed-off-by: Viet Nga Dao <vndao@altera.com>
---
 .../devicetree/bindings/mtd/altera_epcq.txt        |   45 ++
 drivers/mtd/devices/Kconfig                        |   12 +
 drivers/mtd/devices/Makefile                       |    2 +-
 drivers/mtd/devices/altera_epcq.c                  |  804 ++++++++++++++++++++
 drivers/mtd/devices/altera_epcq.h                  |  130 ++++
 5 files changed, 992 insertions(+), 1 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mtd/altera_epcq.txt
 create mode 100644 drivers/mtd/devices/altera_epcq.c
 create mode 100644 drivers/mtd/devices/altera_epcq.h

diff --git a/Documentation/devicetree/bindings/mtd/altera_epcq.txt b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
new file mode 100644
index 0000000..d14f50e
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/altera_epcq.txt
@@ -0,0 +1,45 @@
+* MTD Altera EPCQ driver
+
+Required properties:
+- compatible: Should be "altr,epcq-1.0"
+- reg: Address and length of the register set  for the device. It contains
+  the information of registers in the same order as described by reg-names
+- reg-names: Should contain the reg names
+  "csr_base": Should contain the register configuration base address
+  "data_base": Should contain the data base address
+- is-epcs: boolean type.
+		If present, the device contains EPCS flashes.
+		Otherwise, it contains EPCQ flashes.
+- #address-cells: Must be <1>.
+- #size-cells: Must be <0>.
+- flash device tree subnode, there must be a node with the following fields:
+	- reg: Should contain the flash id
+	- #address-cells: please refer to /mtd/partition.txt
+	- #size-cells: please refer to /mtd/partition.txt
+	For partitions inside each flash, please refer to /mtd/partition.txt
+
+Example:
+
+			epcq_controller_0: epcq@0x000000000 {
+				compatible = "altr,epcq-1.0";
+				reg = <0x00000001 0x00000000 0x00000020>,
+					<0x00000000 0x00000000 0x02000000>;
+				reg-names = "csr_base", "data_base";
+				#address-cells = <1>;
+				#size-cells = <0>;
+				flash0: epcq256@0 {
+					reg = <0>;
+					#address-cells = <1>;
+					#size-cells = <1>;
+					partition@0 {
+						/* 16 MB for raw data. */
+						label = "EPCQ Flash 0 raw data";
+						reg = <0x0 0x1000000>;
+					};
+					partition@1000000 {
+						/* 16 MB for jffs2 data. */
+						label = "EPCQ Flash 0 JFFS 2";
+						reg = <0x1000000 0x1000000>;
+					};
+				};
+			}; //end epcq@0x000000000 (epcq_controller_0)
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index c49d0b1..020b864 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -218,6 +218,18 @@ config MTD_ST_SPI_FSM
 	  SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
 	  for a subset of connected Serial Flash devices.
 
+config MTD_ALTERA_EPCQ
+	tristate "Support Altera EPCQ/EPCS Flash chips"
+	depends on OF
+	help
+	  This enables access to Altera EPCQ/EPCS flash chips, used for data
+	  storage. See the driver source for the current list,
+	  or to add other chips.
+
+	  If you want to compile this driver as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you want),
+	  say M here and read <file:Documentation/kbuild/modules.txt>.
+
 if MTD_DOCG3
 config BCH_CONST_M
 	default 14
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index f0b0e61..b429c4d 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -16,6 +16,6 @@ obj-$(CONFIG_MTD_SPEAR_SMI)	+= spear_smi.o
 obj-$(CONFIG_MTD_SST25L)	+= sst25l.o
 obj-$(CONFIG_MTD_BCM47XXSFLASH)	+= bcm47xxsflash.o
 obj-$(CONFIG_MTD_ST_SPI_FSM)    += st_spi_fsm.o
-
+obj-$(CONFIG_MTD_ALTERA_EPCQ)		+= altera_epcq.o
 
 CFLAGS_docg3.o			+= -I$(src)
diff --git a/drivers/mtd/devices/altera_epcq.c b/drivers/mtd/devices/altera_epcq.c
new file mode 100644
index 0000000..09213d5
--- /dev/null
+++ b/drivers/mtd/devices/altera_epcq.c
@@ -0,0 +1,804 @@
+/*
+ * Copyright (C) 2014 Altera Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/param.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include "altera_epcq.h"
+
+/* data structure to maintain flash ids from different vendors */
+struct flash_device {
+	char *name;
+	bool is_epcs;
+	u32 device_id;
+	uint32_t sectorsize_inbytes;
+	uint64_t size_inbytes;
+	u32 pagesize;
+};
+
+#define FLASH_ID(_n, _is_epcs, _id, _ssize, _size, _psize)	\
+{				\
+	.name = (_n),		\
+	.is_epcs = (_is_epcs),		\
+	.device_id = (_id),	\
+	.sectorsize_inbytes = (_ssize),	\
+	.size_inbytes = (_size),	\
+	.pagesize = (_psize),	\
+}
+
+static struct flash_device flash_devices[] = {
+	FLASH_ID("epcq16"    , 0, 0x15, 0x10000, 0x200000, 0x100),
+	FLASH_ID("epcq32"    , 0, 0x16, 0x10000, 0x400000, 0x100),
+	FLASH_ID("epcq64"    , 0, 0x17, 0x10000, 0x800000, 0x100),
+	FLASH_ID("epcq128"   , 0, 0x18, 0x10000, 0x1000000, 0x100),
+	FLASH_ID("epcq256"   , 0, 0x19, 0x10000, 0x2000000, 0x100),
+	FLASH_ID("epcq512"   , 0, 0x20, 0x10000, 0x4000000, 0x100),
+
+	FLASH_ID("epcs16"    , 1, 0x14, 0x10000, 0x200000, 0x100),
+	FLASH_ID("epcs64"    , 1, 0x16, 0x10000, 0x800000, 0x100),
+	FLASH_ID("epcs128"   , 1, 0x18, 0x40000, 0x1000000, 0x100),
+};
+
+static inline struct altera_epcq_flash *get_flash_data(struct mtd_info *mtd)
+{
+	return container_of(mtd, struct altera_epcq_flash, mtd);
+}
+
+static u32 altera_epcq_read_sr(struct altera_epcq *dev)
+{
+	return readl(dev->csr_base + EPCQ_SR_REG);
+}
+
+static int altera_epcq_wait_till_ready(struct altera_epcq *dev)
+{
+	unsigned long finish;
+	int status;
+
+	finish = jiffies + EPCQ_MAX_TIME_OUT;
+	do {
+		status = altera_epcq_read_sr(dev);
+		if (status < 0)
+			return status;
+		else if (!(status & EPCQ_SR_WIP))
+			return 0;
+
+		cond_resched();
+	} while (!time_after_eq(jiffies, finish));
+
+	dev_err(&dev->pdev->dev, "epcq controller is busy, timeout\n");
+	return -EBUSY;
+}
+
+static int get_flash_index(u32 flash_id, bool is_epcs)
+{
+	int index;
+
+	for (index = 0; index < ARRAY_SIZE(flash_devices); index++) {
+		if (flash_devices[index].device_id == flash_id &&
+		    flash_devices[index].is_epcs == is_epcs)
+			return index;
+	}
+
+	/* Memory chip is not listed and not supported */
+	return -ENODEV;
+}
+
+static int altera_epcq_write_erase_check(struct altera_epcq *dev,
+					 bool write_erase)
+{
+	u32 val;
+	u32 mask;
+
+	if (write_erase)
+		mask = EPCQ_ISR_ILLEGAL_WRITE_MASK;
+	else
+		mask = EPCQ_ISR_ILLEGAL_ERASE_MASK;
+
+	val = readl(dev->csr_base + EPCQ_ISR_REG);
+	if (val & mask) {
+		dev_err(&dev->pdev->dev,
+			"write/erase failed, sector might be protected\n");
+		/*clear this status for next use*/
+		writel(val, dev->csr_base + EPCQ_ISR_REG);
+		return -EIO;
+	}
+	return 0;
+}
+
+static int altera_epcq_erase_chip(struct mtd_info *mtd)
+{
+	int ret;
+	u32 val;
+	struct altera_epcq *dev = mtd->priv;
+
+	/* Wait until finished previous write command. */
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		return ret;
+
+	/* erase chip. */
+	val = EPCQ_MEM_OP_BULK_ERASE_CMD;
+	writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
+
+	/* Wait until finished previous write command. */
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		return ret;
+
+	 /* check whether write triggered a illegal write interrupt */
+	ret = altera_epcq_write_erase_check(dev, 0);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int altera_epcq_addr_to_sector(struct altera_epcq_flash *flash,
+				      int addr_offset)
+{
+	int sector = 0;
+	int i;
+
+	for (i = 0; i < flash->num_sectors; i++) {
+		if (addr_offset >= i * flash->mtd.erasesize && addr_offset <
+		    (i * flash->mtd.erasesize + flash->mtd.erasesize)) {
+			sector = i;
+			return sector;
+		}
+	}
+	return -1;
+}
+
+static int altera_epcq_erase_sector(struct mtd_info *mtd,
+				    int addr_offset)
+{
+	struct altera_epcq_flash *flash = get_flash_data(mtd);
+	struct altera_epcq *dev = mtd->priv;
+	u32 val;
+	int ret;
+	int sector_value;
+
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		return ret;
+
+	sector_value = altera_epcq_addr_to_sector(flash, addr_offset);
+
+	/* sanity check that block_offset is a valid sector number */
+	if (sector_value < 0)
+		return -EINVAL;
+
+	/* sector value should occupy bits 17:8 */
+	val = (sector_value << 8) & EPCQ_MEM_OP_SECTOR_VALUE_MASK;
+
+	/* sector erase commands occupies lower 2 bits */
+	val |= EPCQ_MEM_OP_SECTOR_ERASE_CMD;
+
+	/* write sector erase command to EPCQ_MEM_OP register*/
+	writel(val, dev->csr_base + EPCQ_MEM_OP_REG);
+
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		return ret;
+
+	 /* check whether write triggered a illegal write interrupt */
+	ret = altera_epcq_write_erase_check(dev, 0);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int altera_epcq_erase(struct mtd_info *mtd, struct erase_info *e_info)
+{
+	u32 addr;
+	int ret;
+	u32 len;
+	struct altera_epcq_flash *flash = get_flash_data(mtd);
+	struct altera_epcq *dev = mtd->priv;
+
+	addr = e_info->addr;
+	len = e_info->len;
+
+	if (flash->bank > dev->num_flashes - 1) {
+		dev_err(&dev->pdev->dev, "Invalid chip id\n");
+		return -EINVAL;
+	}
+	mutex_lock(&flash->lock);
+
+	/*erase whole chip*/
+	if (len == mtd->size) {
+		if (altera_epcq_erase_chip(mtd)) {
+			e_info->state = MTD_ERASE_FAILED;
+			mutex_unlock(&flash->lock);
+			return -EIO;
+		}
+	/*"sector"-at-a-time erase*/
+	} else {
+		while (len) {
+			ret = altera_epcq_erase_sector(mtd, addr);
+			if (ret) {
+				e_info->state = MTD_ERASE_FAILED;
+				mutex_unlock(&flash->lock);
+				return -EIO;
+			}
+			addr += mtd->erasesize;
+			if (len < mtd->erasesize)
+				len = 0;
+			else
+				len -= mtd->erasesize;
+		}
+	}
+	mutex_unlock(&flash->lock);
+	e_info->state = MTD_ERASE_DONE;
+	mtd_erase_callback(e_info);
+
+	return 0;
+}
+
+static int altera_epcq_read(struct mtd_info *mtd, loff_t from, size_t len,
+			    size_t *retlen, u8 *buf)
+{
+	struct altera_epcq_flash *flash = get_flash_data(mtd);
+	struct altera_epcq *dev = mtd->priv;
+	void *src;
+	int ret = 0;
+
+	if (!flash || !dev)
+		return -ENODEV;
+
+	if (flash->bank > dev->num_flashes - 1) {
+		dev_err(&dev->pdev->dev, "Invalid chip id\n");
+		return -EINVAL;
+	}
+
+	src = dev->data_base + from;
+
+	mutex_lock(&flash->lock);
+	/* wait till previous write/erase is done. */
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		goto err;
+
+	memcpy_fromio(buf, (u8 *)src, len);
+	*retlen = len;
+
+err:
+	mutex_unlock(&flash->lock);
+	return ret;
+}
+
+static int altera_epcq_write(struct mtd_info *mtd, loff_t to, size_t len,
+			     size_t *retlen, const u8 *buf)
+{
+	struct altera_epcq_flash *flash = get_flash_data(mtd);
+	struct altera_epcq *dev = mtd->priv;
+	void *dest;
+	int ret = 0;
+
+	if (!flash || !dev)
+		return -ENODEV;
+
+	if (flash->bank > dev->num_flashes - 1) {
+		dev_err(&dev->pdev->dev, "Invalid chip id\n");
+		return -EINVAL;
+	}
+	dest = dev->data_base + to;
+
+	mutex_lock(&flash->lock);
+
+	/* wait until finished previous write command. */
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		goto err;
+
+	memcpy_toio(dest, buf, len);
+	*retlen += len;
+
+	/* wait until finished previous write command. */
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		goto err;
+
+	 /* check whether write triggered a illegal write interrupt */
+	ret = altera_epcq_write_erase_check(dev, 1);
+	if (ret < 0)
+		goto err;
+
+err:
+	mutex_unlock(&flash->lock);
+	return ret;
+}
+
+static int altera_epcq_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+	struct altera_epcq_flash *flash = get_flash_data(mtd);
+	struct altera_epcq *dev = mtd->priv;
+	uint32_t offset = ofs;
+	int ret = 0;
+	u32 sector_start, sector_end;
+	u32 num_sectors;
+	u32 mem_op;
+	unsigned sr_bp = 0;
+	unsigned sr_tb = 0;
+
+	sector_start = altera_epcq_addr_to_sector(flash, offset);
+	sector_end = altera_epcq_addr_to_sector(flash, offset + len);
+	num_sectors = flash->num_sectors;
+	dev_dbg(&dev->pdev->dev,
+		"%s: num_setor is %u,sector start is %u,sector end is %u\n",
+		__func__, num_sectors, sector_start, sector_end);
+
+	mutex_lock(&flash->lock);
+	/* wait until finished previous write command. */
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		goto err;
+
+	if (sector_start >= num_sectors/2) {
+		if (sector_start < num_sectors-(num_sectors / 4))
+			sr_bp = __ilog2_u32(num_sectors);
+		else if (sector_start < num_sectors-(num_sectors / 8))
+			sr_bp = __ilog2_u32(num_sectors) - 1;
+		else if (sector_start < num_sectors-(num_sectors / 16))
+			sr_bp = __ilog2_u32(num_sectors) - 2;
+		else if (sector_start < num_sectors-(num_sectors / 32))
+			sr_bp = __ilog2_u32(num_sectors) - 3;
+		else if (sector_start < num_sectors-(num_sectors / 64))
+			sr_bp = __ilog2_u32(num_sectors) - 4;
+		else if (sector_start < num_sectors-(num_sectors / 128))
+			sr_bp = __ilog2_u32(num_sectors) - 5;
+		else if (sector_start < num_sectors-(num_sectors / 256))
+			sr_bp = __ilog2_u32(num_sectors) - 6;
+		else if (sector_start < num_sectors-(num_sectors / 512))
+			sr_bp = __ilog2_u32(num_sectors) - 7;
+		else if (sector_start < num_sectors-(num_sectors / 1024))
+			sr_bp = __ilog2_u32(num_sectors) - 8;
+		else
+			sr_bp = 0;  /* non area protected */
+
+		if (sr_bp < 0) {
+			dev_err(&dev->pdev->dev, "%s: address is out of range\n"
+				, __func__);
+			ret = -EINVAL;
+			goto err;
+		}
+		/*set TB = 0*/
+		sr_tb = 0;
+
+	} else {
+		if (sector_end < 1)
+			sr_bp = 1;
+		else if (sector_end < 2)
+			sr_bp = 2;
+		else if (sector_end < 4)
+			sr_bp = 3;
+		else if (sector_end < 8)
+			sr_bp = 4;
+		else if (sector_end < 16)
+			sr_bp = 5;
+		else if (sector_end < 32)
+			sr_bp = 6;
+		else if (sector_end < 64)
+			sr_bp = 7;
+		else if (sector_end < 128)
+			sr_bp = 8;
+		else if (sector_end < 256)
+			sr_bp = 9;
+		else if (sector_end < 512)
+			sr_bp = 10;
+		else
+			sr_bp = 16; /*protect all areas*/
+
+		sr_tb = 1;
+	}
+
+	mem_op = (sr_tb << 12) | (sr_bp << 8);
+	mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
+	mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
+	writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
+
+err:
+	mutex_unlock(&flash->lock);
+	return ret;
+}
+
+static int altera_epcq_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+	struct altera_epcq_flash *flash = get_flash_data(mtd);
+	struct altera_epcq *dev = mtd->priv;
+
+	int ret = 0;
+	u32 mem_op;
+
+	mutex_lock(&flash->lock);
+	/* wait until finished previous write command. */
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		goto err;
+	dev_info(&dev->pdev->dev, "Unlock all protected area\n");
+	mem_op = 0;
+	mem_op &= EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK;
+	mem_op |= EPCQ_MEM_OP_SECTOR_PROTECT_CMD;
+	writel(mem_op, dev->csr_base + EPCQ_MEM_OP_REG);
+
+err:
+	mutex_unlock(&flash->lock);
+	return ret;
+}
+
+static void altera_epcq_chip_select(struct altera_epcq *dev, u32 bank)
+{
+	u32 val = 0;
+
+	switch (bank) {
+	case 0:
+		val = EPCQ_CHIP_SELECT_0;
+		break;
+	case 1:
+		val = EPCQ_CHIP_SELECT_1;
+		break;
+	case 2:
+		val = EPCQ_CHIP_SELECT_2;
+		break;
+	default:
+		return;
+	}
+
+	writel(val, dev->csr_base + EPCQ_CHIP_SELECT_REG);
+}
+
+static int altera_epcq_probe_flash(struct altera_epcq *dev, u32 bank)
+{
+	int ret = 0;
+	u32 val = 0;
+
+	mutex_lock(&dev->lock);
+
+	/*select bank*/
+	altera_epcq_chip_select(dev, bank);
+
+	ret = altera_epcq_wait_till_ready(dev);
+	if (ret)
+		goto err;
+
+	/* get device sillicon id */
+	if (dev->is_epcs)
+		val = readl(dev->csr_base + EPCQ_SID_REG);
+	else
+		val = readl(dev->csr_base + EPCQ_RDID_REG);
+
+	/* get flash index based on the device list*/
+	ret = get_flash_index(val, dev->is_epcs);
+	return 0;
+err:
+	mutex_unlock(&dev->lock);
+	return ret;
+}
+
+static int altera_epcq_probe_config_dt(struct platform_device *pdev,
+				       struct device_node *np,
+				       struct altera_epcq_plat_data *pdata)
+{
+	struct device_node *pp = NULL;
+	struct resource *epcq_res;
+	int i = 0;
+	u32 id;
+
+	pdata->is_epcs = of_property_read_bool(np, "is-epcs");
+
+	epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						"csr_base");
+	if (!epcq_res) {
+		dev_err(&pdev->dev, "resource csr base is not defined\n");
+		return -ENODEV;
+	}
+
+	pdata->csr_base = devm_ioremap_resource(&pdev->dev, epcq_res);
+	if (IS_ERR(pdata->csr_base)) {
+		dev_err(&pdev->dev, "%s: ERROR: failed to map csr base\n",
+			__func__);
+		return PTR_ERR(pdata->csr_base);
+	}
+
+	epcq_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						"data_base");
+	if (!epcq_res) {
+		dev_err(&pdev->dev, "resource data base is not defined\n");
+		return -ENODEV;
+	}
+
+	pdata->data_base = devm_ioremap_resource(&pdev->dev, epcq_res);
+	if (IS_ERR(pdata->data_base)) {
+		dev_err(&pdev->dev, "%s: ERROR: failed to map data base\n",
+			__func__);
+		return PTR_ERR(pdata->data_base);
+	}
+
+	pdata->board_flash_info = devm_kzalloc(&pdev->dev,
+					       sizeof(*pdata->board_flash_info),
+					       GFP_KERNEL);
+
+	/* Fill structs for each subnode (flash device) */
+	while ((pp = of_get_next_child(np, pp))) {
+		struct altera_epcq_flash_info *flash_info;
+
+		flash_info = &pdata->board_flash_info[i];
+		pdata->np[i] = pp;
+
+		/* Read bank id from DT */
+		if (of_get_property(pp, "reg", &id))
+			pdata->board_flash_info[i].bank = id;
+		i++;
+	}
+	pdata->num_flashes = i;
+	return 0;
+}
+
+static int altera_epcq_setup_banks(struct platform_device *pdev,
+				   u32 bank, struct device_node *np,
+				   struct altera_epcq_plat_data *pdata)
+{
+	struct altera_epcq *dev = platform_get_drvdata(pdev);
+	struct mtd_part_parser_data ppdata = {};
+	struct altera_epcq_flash_info *flash_info;
+	struct altera_epcq_flash *flash;
+	struct mtd_partition *parts = NULL;
+	int count = 0;
+	int flash_index;
+	int ret = 0;
+	uint64_t size;
+	uint32_t sector_size;
+
+	flash_info = &pdata->board_flash_info[bank];
+	if (!flash_info)
+		return -ENODEV;
+
+	if (bank > pdata->num_flashes - 1)
+		return -EINVAL;
+
+	flash = devm_kzalloc(&pdev->dev, sizeof(*flash), GFP_KERNEL);
+	if (!flash)
+		return -ENOMEM;
+	flash->bank = bank;
+
+	mutex_init(&flash->lock);
+
+	/* verify whether flash is really present on board */
+	flash_index = altera_epcq_probe_flash(dev, bank);
+	if (flash_index < 0) {
+		dev_info(&dev->pdev->dev, "epcq:flash%d not found\n",
+			 flash->bank);
+		return flash_index;
+	}
+
+	dev->flash[bank] = flash;
+
+	size = flash_devices[flash_index].size_inbytes;
+	sector_size = flash_devices[flash_index].sectorsize_inbytes;
+	/*use do_div instead of plain div to avoid linker err*/
+	do_div(size, sector_size);
+	flash->num_sectors = size;
+
+	/*mtd framework */
+	flash->mtd.priv = dev;
+	flash->mtd.name = flash_devices[flash_index].name;
+	flash->mtd.type = MTD_NORFLASH;
+	flash->mtd.writesize = 1;
+	flash->mtd.flags = MTD_CAP_NORFLASH;
+	flash->mtd.size = flash_devices[flash_index].size_inbytes;
+	flash->mtd.erasesize = flash_devices[flash_index].sectorsize_inbytes;
+	flash->mtd._erase = altera_epcq_erase;
+	flash->mtd._read = altera_epcq_read;
+	flash->mtd._write = altera_epcq_write;
+	flash->mtd._lock = altera_epcq_lock;
+	flash->mtd._unlock = altera_epcq_unlock;
+
+	dev_info(&pdev->dev, "mtd .name=%s .size=0x%llx (%lluM)\n",
+		 flash->mtd.name, (long long)flash->mtd.size,
+		 (long long)(flash->mtd.size >> 20));
+
+	dev_info(&pdev->dev, ".erasesize = 0x%x(%uK)\n",
+		 flash->mtd.erasesize, flash->mtd.erasesize >> 10);
+
+	ppdata.of_node = np;
+
+	ret = mtd_device_parse_register(&flash->mtd, NULL, &ppdata, parts,
+					count);
+	if (ret) {
+		dev_err(&pdev->dev, "Err MTD partition=%d\n", ret);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	mtd_device_unregister(&flash->mtd);
+	return ret;
+}
+
+static int altera_epcq_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct altera_epcq_plat_data *pdata = NULL;
+	struct altera_epcq *dev;
+	int ret = 0;
+	int i;
+
+	if (!np) {
+		ret = -ENODEV;
+		dev_err(&pdev->dev, "no device found\n");
+		goto err;
+	}
+
+	pdata = devm_kzalloc(&pdev->dev,
+			     sizeof(struct altera_epcq_plat_data),
+			     GFP_KERNEL);
+
+	if (!pdata) {
+		ret = -ENOMEM;
+		dev_err(&pdev->dev, "%s: ERROR: no memory\n", __func__);
+		goto err;
+	}
+	ret = altera_epcq_probe_config_dt(pdev, np, pdata);
+	if (ret) {
+		ret = -ENODEV;
+		dev_err(&pdev->dev, "probe fail\n");
+		goto err;
+	}
+
+	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
+	if (!dev) {
+		ret = -ENOMEM;
+		dev_err(&pdev->dev, "mem alloc fail\n");
+		goto err;
+	}
+	mutex_init(&dev->lock);
+	dev->pdev = pdev;
+	dev->is_epcs = pdata->is_epcs;
+	dev->csr_base = pdata->csr_base;
+	dev->data_base = pdata->data_base;
+
+	/*check number of flashes*/
+	dev->num_flashes = pdata->num_flashes;
+	if (dev->num_flashes > MAX_NUM_FLASH_CHIP) {
+		dev_err(&pdev->dev, "exceeding max number of flashes\n");
+		dev->num_flashes = MAX_NUM_FLASH_CHIP;
+	}
+
+	/* check clock*/
+	dev->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(dev->clk)) {
+		ret = PTR_ERR(dev->clk);
+		goto err;
+	}
+	ret = clk_prepare_enable(dev->clk);
+	if (ret)
+		goto err;
+
+	platform_set_drvdata(pdev, dev);
+
+	/* loop for each serial flash which is connected to epcq */
+	for (i = 0; i < dev->num_flashes; i++) {
+		ret = altera_epcq_setup_banks(pdev, i, pdata->np[i], pdata);
+		if (ret) {
+			dev_err(&pdev->dev, "bank setup failed\n");
+			goto err_bank_setup;
+		}
+	}
+
+	return 0;
+
+err_bank_setup:
+	clk_disable_unprepare(dev->clk);
+err:
+	return ret;
+}
+
+static int altera_epcq_remove(struct platform_device *pdev)
+{
+	struct altera_epcq *dev;
+	struct altera_epcq_flash *flash;
+	int ret, i;
+
+	dev = platform_get_drvdata(pdev);
+
+	/* clean up for all nor flash */
+	for (i = 0; i < dev->num_flashes; i++) {
+		flash = dev->flash[i];
+		if (!flash)
+			continue;
+
+		/* clean up mtd stuff */
+		ret = mtd_device_unregister(&flash->mtd);
+		if (ret)
+			dev_err(&pdev->dev, "error removing mtd\n");
+	}
+
+	clk_disable_unprepare(dev->clk);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int altera_epcq_suspend(struct device *dev)
+{
+	struct altera_epcq *sdev = dev_get_drvdata(dev);
+
+	clk_disable_unprepare(sdev->clk);
+
+	return 0;
+}
+
+static int altera_epcq_resume(struct device *dev)
+{
+	struct altera_epcq *sdev = dev_get_drvdata(dev);
+	int ret = -EPERM;
+
+	ret = clk_prepare_enable(sdev->clk);
+
+	return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(altera_epcq_pm_ops, altera_epcq_suspend,
+			 altera_epcq_resume);
+#endif
+
+static const struct of_device_id altera_epcq_id_table[] = {
+	{ .compatible = "altr,epcq-1.0" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, altera_epcq_id_table);
+
+static struct platform_driver altera_epcq_driver = {
+	.driver = {
+		.name = ALTERA_EPCQ_RESOURCE_NAME,
+		.bus = &platform_bus_type,
+		.owner = THIS_MODULE,
+		.of_match_table = altera_epcq_id_table,
+#ifdef CONFIG_PM
+		.pm = &altera_epcq_pm_ops,
+#endif
+	},
+	.probe = altera_epcq_probe,
+	.remove = altera_epcq_remove,
+};
+module_platform_driver(altera_epcq_driver);
+
+MODULE_AUTHOR("Viet Nga Dao <vndao@altera.com>");
+MODULE_DESCRIPTION("MTD Altera EPCQ Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mtd/devices/altera_epcq.h b/drivers/mtd/devices/altera_epcq.h
new file mode 100644
index 0000000..c3d15e5
--- /dev/null
+++ b/drivers/mtd/devices/altera_epcq.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2014 Altera Corporation. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ALTERA_ECPQ_H
+#define __ALTERA_ECPQ_H
+
+#define ALTERA_EPCQ_RESOURCE_NAME        "altera_epcq"
+/* max possible slots for serial flash chip in the EPCQ controller */
+#define MAX_NUM_FLASH_CHIP	3
+
+/* macro to define partitions for flash devices */
+#define DEFINE_PARTS(n, of, s)		\
+{					\
+	.name = n,			\
+	.offset = of,			\
+	.size = s,			\
+}
+
+struct altera_epcq_flash_info {
+	u32 bank;
+	struct mtd_partition *partitions;
+	int nr_partitions;
+};
+
+struct altera_epcq_plat_data {
+	void __iomem *csr_base;
+	void __iomem *data_base;
+	bool is_epcs;
+	u32 num_flashes;
+	struct altera_epcq_flash_info *board_flash_info;
+	struct device_node *np[MAX_NUM_FLASH_CHIP];
+};
+
+struct altera_epcq {
+	struct clk *clk;
+	bool is_epcs;
+	struct mutex lock;	/*device lock*/
+	void __iomem *csr_base;
+	void __iomem *data_base;
+	struct platform_device *pdev;
+	u32 num_flashes;
+	struct altera_epcq_flash *flash[MAX_NUM_FLASH_CHIP];
+};
+
+struct altera_epcq_flash {
+	u32 bank;
+	u32 num_sectors;
+	u32 num_parts;
+	struct mtd_partition *parts;
+	struct mutex lock;	/*flash lock*/
+	struct mtd_info mtd;
+};
+
+/* Define max times to check status register before we give up. */
+#define EPCQ_MAX_TIME_OUT			(40 * HZ)
+
+/* defines for status register */
+#define EPCQ_SR_REG				0x0
+#define EPCQ_SR_WIP_MASK			0x00000001
+#define EPCQ_SR_WIP				0x1
+#define EPCQ_SR_WEL				0x2
+#define EPCQ_SR_BP0				0x4
+#define EPCQ_SR_BP1				0x8
+#define EPCQ_SR_BP2				0x10
+#define EPCQ_SR_BP3				0x40
+#define EPCQ_SR_TB				0x20
+
+/* defines for device id register */
+#define EPCQ_SID_REG				0x4
+#define EPCQ_RDID_REG				0x8
+#define EPCQ_RDID_MASK				0x000000FF
+/*
+ * EPCQ_MEM_OP register offset
+ *
+ * The EPCQ_MEM_OP register is used to do memory protect and erase operations
+ *
+ */
+#define EPCQ_MEM_OP_REG				0xC
+
+#define EPCQ_MEM_OP_CMD_MASK			0x00000003
+#define EPCQ_MEM_OP_BULK_ERASE_CMD		0x00000001
+#define EPCQ_MEM_OP_SECTOR_ERASE_CMD		0x00000002
+#define EPCQ_MEM_OP_SECTOR_PROTECT_CMD		0x00000003
+#define EPCQ_MEM_OP_SECTOR_VALUE_MASK		0x0003FF00
+#define EPCQ_MEM_OP_SECTOR_PROTECT_VALUE_MASK	0x00001F00
+#define EPCQ_MEM_OP_SECTOR_PROTECT_SHIFT	8
+/*
+ * EPCQ_ISR register offset
+ *
+ * The EPCQ_ISR register is used to determine whether an invalid write or erase
+ * operation trigerred an interrupt
+ *
+ */
+#define EPCQ_ISR_REG				0x10
+
+#define EPCQ_ISR_ILLEGAL_ERASE_MASK		0x00000001
+#define EPCQ_ISR_ILLEGAL_WRITE_MASK		0x00000002
+
+/*
+ * EPCQ_IMR register offset
+ *
+ * The EPCQ_IMR register is used to mask the invalid erase or the invalid write
+ * interrupts.
+ *
+ */
+#define EPCQ_IMR_REG				0x14
+#define EPCQ_IMR_ILLEGAL_ERASE_MASK		0x00000001
+
+#define EPCQ_IMR_ILLEGAL_WRITE_MASK		0x00000002
+
+#define EPCQ_CHIP_SELECT_REG			0x18
+#define EPCQ_CHIP_SELECT_MASK			0x00000007
+#define EPCQ_CHIP_SELECT_0			0x00000001
+#define EPCQ_CHIP_SELECT_1			0x00000002
+#define EPCQ_CHIP_SELECT_2			0x00000004
+
+#endif /* __ALTERA_ECPQ_H */
-- 
1.7.7.4

^ permalink raw reply related	[flat|nested] 39+ messages in thread

end of thread, other threads:[~2015-02-10  4:35 UTC | newest]

Thread overview: 39+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-01-15  9:27 [PATCH mtd] mtd:devices: Add Altera EPCQ Driver Viet Nga Dao
2015-01-15  9:27 ` Viet Nga Dao
2015-01-15  9:27 ` Viet Nga Dao
2015-01-20  2:05 ` Viet Nga Dao
2015-01-20  2:05   ` Viet Nga Dao
2015-01-20  2:05   ` Viet Nga Dao
2015-01-22  2:56   ` Viet Nga Dao
2015-01-22  2:56     ` Viet Nga Dao
2015-01-22  2:56     ` Viet Nga Dao
2015-01-27  6:53   ` Viet Nga Dao
2015-01-27  6:53     ` Viet Nga Dao
2015-01-27  6:53     ` Viet Nga Dao
2015-02-04 20:37     ` Brian Norris
2015-02-04 20:37       ` Brian Norris
2015-02-04 20:37       ` Brian Norris
2015-02-05  4:13       ` Nga Chi
2015-02-05  4:13         ` Nga Chi
2015-02-05  4:13         ` Nga Chi
2015-02-05  4:15       ` Viet Nga Dao
2015-02-05  4:15         ` Viet Nga Dao
2015-02-09  6:42       ` Viet Nga Dao
2015-02-09  6:42         ` Viet Nga Dao
2015-02-09  6:42         ` Viet Nga Dao
2015-02-10  4:35         ` Viet Nga Dao
2015-02-10  4:35           ` Viet Nga Dao
2015-01-22  8:28 ` Brian Norris
2015-01-22  8:28   ` Brian Norris
2015-01-22  8:28   ` Brian Norris
  -- strict thread matches above, loose matches on Subject: below --
2014-12-18  8:23 vndao
2014-12-18  8:23 ` vndao
2014-12-18  8:23 ` vndao
2015-01-13  1:21 ` Viet Nga Dao
2015-01-13  1:21   ` Viet Nga Dao
2015-01-13  1:21   ` Viet Nga Dao
2015-01-13  3:33 ` Brian Norris
2015-01-13  3:33   ` Brian Norris
2015-01-15  3:05   ` Viet Nga Dao
2015-01-15  3:05     ` Viet Nga Dao
2015-01-15  3:05     ` Viet Nga Dao

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.