lpddr_cmds.c revision a0e5cc581b3fc0e0a909e3cab48d9ec286c2a276
1c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev/* 2c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * LPDDR flash memory device operations. This module provides read, write, 3c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * erase, lock/unlock support for LPDDR flash memories 4c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * (C) 2008 Korolev Alexey <akorolev@infradead.org> 5c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * (C) 2008 Vasiliy Leonenko <vasiliy.leonenko@gmail.com> 625985edcedea6396277003854657b5f3cb31a628Lucas De Marchi * Many thanks to Roman Borisov for initial enabling 7c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * 8c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * This program is free software; you can redistribute it and/or 9c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * modify it under the terms of the GNU General Public License 10c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * as published by the Free Software Foundation; either version 2 11c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * of the License, or (at your option) any later version. 12c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * 13c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * This program is distributed in the hope that it will be useful, 14c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * but WITHOUT ANY WARRANTY; without even the implied warranty of 15c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * GNU General Public License for more details. 17c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * 18c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * You should have received a copy of the GNU General Public License 19c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * along with this program; if not, write to the Free Software 20c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 21c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * 02110-1301, USA. 22c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * TODO: 23c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * Implement VPP management 24c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * Implement XIP support 25c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * Implement OTP support 26c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev */ 27c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev#include <linux/mtd/pfow.h> 28c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev#include <linux/mtd/qinfo.h> 295a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h> 30a0e5cc581b3fc0e0a909e3cab48d9ec286c2a276Paul Gortmaker#include <linux/module.h> 31c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 32c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevstatic int lpddr_read(struct mtd_info *mtd, loff_t adr, size_t len, 33c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev size_t *retlen, u_char *buf); 34c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevstatic int lpddr_write_buffers(struct mtd_info *mtd, loff_t to, 35c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev size_t len, size_t *retlen, const u_char *buf); 36c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevstatic int lpddr_writev(struct mtd_info *mtd, const struct kvec *vecs, 37c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev unsigned long count, loff_t to, size_t *retlen); 38c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevstatic int lpddr_erase(struct mtd_info *mtd, struct erase_info *instr); 39c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevstatic int lpddr_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); 40c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevstatic int lpddr_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); 41c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevstatic int lpddr_point(struct mtd_info *mtd, loff_t adr, size_t len, 42c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev size_t *retlen, void **mtdbuf, resource_size_t *phys); 43c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevstatic void lpddr_unpoint(struct mtd_info *mtd, loff_t adr, size_t len); 44c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevstatic int get_chip(struct map_info *map, struct flchip *chip, int mode); 45c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevstatic int chip_ready(struct map_info *map, struct flchip *chip, int mode); 46c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevstatic void put_chip(struct map_info *map, struct flchip *chip); 47c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 48c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevstruct mtd_info *lpddr_cmdset(struct map_info *map) 49c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev{ 50c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct lpddr_private *lpddr = map->fldrv_priv; 51c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct flchip_shared *shared; 52c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct flchip *chip; 53c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct mtd_info *mtd; 54c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev int numchips; 55c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev int i, j; 56c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 57c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); 58c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (!mtd) { 59c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev printk(KERN_ERR "Failed to allocate memory for MTD device\n"); 60c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return NULL; 61c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 62c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev mtd->priv = map; 63c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev mtd->type = MTD_NORFLASH; 64c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 65c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* Fill in the default mtd operations */ 66c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev mtd->read = lpddr_read; 67c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev mtd->type = MTD_NORFLASH; 68c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev mtd->flags = MTD_CAP_NORFLASH; 69c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev mtd->flags &= ~MTD_BIT_WRITEABLE; 70c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev mtd->erase = lpddr_erase; 71c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev mtd->write = lpddr_write_buffers; 72c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev mtd->writev = lpddr_writev; 73c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev mtd->read_oob = NULL; 74c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev mtd->write_oob = NULL; 75c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev mtd->sync = NULL; 76c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev mtd->lock = lpddr_lock; 77c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev mtd->unlock = lpddr_unlock; 78c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev mtd->suspend = NULL; 79c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev mtd->resume = NULL; 80c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (map_is_linear(map)) { 81c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev mtd->point = lpddr_point; 82c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev mtd->unpoint = lpddr_unpoint; 83c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 84c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev mtd->block_isbad = NULL; 85c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev mtd->block_markbad = NULL; 86c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev mtd->size = 1 << lpddr->qinfo->DevSizeShift; 87c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev mtd->erasesize = 1 << lpddr->qinfo->UniformBlockSizeShift; 88c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev mtd->writesize = 1 << lpddr->qinfo->BufSizeShift; 89c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 90c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev shared = kmalloc(sizeof(struct flchip_shared) * lpddr->numchips, 91c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev GFP_KERNEL); 92c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (!shared) { 93c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev kfree(lpddr); 94c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev kfree(mtd); 95c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return NULL; 96c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 97c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 98c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev chip = &lpddr->chips[0]; 99c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev numchips = lpddr->numchips / lpddr->qinfo->HWPartsNum; 100c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev for (i = 0; i < numchips; i++) { 101c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev shared[i].writing = shared[i].erasing = NULL; 1028ae664184c45def51ff0b61d4bd6c6671db6cb4fStefani Seibold mutex_init(&shared[i].lock); 103c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev for (j = 0; j < lpddr->qinfo->HWPartsNum; j++) { 104c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev *chip = lpddr->chips[i]; 105c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev chip->start += j << lpddr->chipshift; 106c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev chip->oldstate = chip->state = FL_READY; 107c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev chip->priv = &shared[i]; 108c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* those should be reset too since 109c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev they create memory references. */ 110c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev init_waitqueue_head(&chip->wq); 111c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_init(&chip->mutex); 112c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev chip++; 113c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 114c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 115c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 116c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return mtd; 117c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev} 118c68264711ca6caf87794caf9e79c30a4ba73c032Alexey KorolevEXPORT_SYMBOL(lpddr_cmdset); 119c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 120c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevstatic int wait_for_ready(struct map_info *map, struct flchip *chip, 121c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev unsigned int chip_op_time) 122c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev{ 123c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev unsigned int timeo, reset_timeo, sleep_time; 124c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev unsigned int dsr; 125c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev flstate_t chip_state = chip->state; 126c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev int ret = 0; 127c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 128c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* set our timeout to 8 times the expected delay */ 129c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev timeo = chip_op_time * 8; 130c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (!timeo) 131c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev timeo = 500000; 132c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev reset_timeo = timeo; 133c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev sleep_time = chip_op_time / 2; 134c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 135c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev for (;;) { 136c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev dsr = CMDVAL(map_read(map, map->pfow_base + PFOW_DSR)); 137c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (dsr & DSR_READY_STATUS) 138c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev break; 139c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (!timeo) { 140c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev printk(KERN_ERR "%s: Flash timeout error state %d \n", 141c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev map->name, chip_state); 142c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev ret = -ETIME; 143c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev break; 144c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 145c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 146c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* OK Still waiting. Drop the lock, wait a while and retry. */ 147c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_unlock(&chip->mutex); 148c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (sleep_time >= 1000000/HZ) { 149c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* 150c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * Half of the normal delay still remaining 151c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * can be performed with a sleeping delay instead 152c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * of busy waiting. 153c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev */ 154c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev msleep(sleep_time/1000); 155c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev timeo -= sleep_time; 156c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev sleep_time = 1000000/HZ; 157c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } else { 158c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev udelay(1); 159c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev cond_resched(); 160c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev timeo--; 161c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 162c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_lock(&chip->mutex); 163c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 164c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev while (chip->state != chip_state) { 165c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* Someone's suspended the operation: sleep */ 166c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev DECLARE_WAITQUEUE(wait, current); 167c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev set_current_state(TASK_UNINTERRUPTIBLE); 168c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev add_wait_queue(&chip->wq, &wait); 169c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_unlock(&chip->mutex); 170c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev schedule(); 171c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev remove_wait_queue(&chip->wq, &wait); 172c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_lock(&chip->mutex); 173c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 174c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (chip->erase_suspended || chip->write_suspended) { 17525985edcedea6396277003854657b5f3cb31a628Lucas De Marchi /* Suspend has occurred while sleep: reset timeout */ 176c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev timeo = reset_timeo; 177c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev chip->erase_suspended = chip->write_suspended = 0; 178c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 179c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 180c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* check status for errors */ 181c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (dsr & DSR_ERR) { 182c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* Clear DSR*/ 183c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev map_write(map, CMD(~(DSR_ERR)), map->pfow_base + PFOW_DSR); 184c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev printk(KERN_WARNING"%s: Bad status on wait: 0x%x \n", 185c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev map->name, dsr); 186c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev print_drs_error(dsr); 187c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev ret = -EIO; 188c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 189c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev chip->state = FL_READY; 190c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return ret; 191c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev} 192c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 193c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevstatic int get_chip(struct map_info *map, struct flchip *chip, int mode) 194c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev{ 195c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev int ret; 196c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev DECLARE_WAITQUEUE(wait, current); 197c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 198c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev retry: 199c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING) 200c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev && chip->state != FL_SYNCING) { 201c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* 202c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * OK. We have possibility for contension on the write/erase 203c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * operations which are global to the real chip and not per 204c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * partition. So let's fight it over in the partition which 205c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * currently has authority on the operation. 206c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * 207c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * The rules are as follows: 208c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * 209c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * - any write operation must own shared->writing. 210c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * 211c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * - any erase operation must own _both_ shared->writing and 212c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * shared->erasing. 213c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * 214c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * - contension arbitration is handled in the owner's context. 215c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * 216c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * The 'shared' struct can be read and/or written only when 217c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * its lock is taken. 218c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev */ 219c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct flchip_shared *shared = chip->priv; 220c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct flchip *contender; 2218ae664184c45def51ff0b61d4bd6c6671db6cb4fStefani Seibold mutex_lock(&shared->lock); 222c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev contender = shared->writing; 223c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (contender && contender != chip) { 224c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* 225c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * The engine to perform desired operation on this 226c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * partition is already in use by someone else. 227c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * Let's fight over it in the context of the chip 228c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * currently using it. If it is possible to suspend, 229c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * that other partition will do just that, otherwise 230c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * it'll happily send us to sleep. In any case, when 231c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * get_chip returns success we're clear to go ahead. 232c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev */ 233c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold ret = mutex_trylock(&contender->mutex); 2348ae664184c45def51ff0b61d4bd6c6671db6cb4fStefani Seibold mutex_unlock(&shared->lock); 235c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (!ret) 236c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev goto retry; 237c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_unlock(&chip->mutex); 238c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev ret = chip_ready(map, contender, mode); 239c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_lock(&chip->mutex); 240c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 241c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (ret == -EAGAIN) { 242c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_unlock(&contender->mutex); 243c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev goto retry; 244c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 245c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (ret) { 246c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_unlock(&contender->mutex); 247c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return ret; 248c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 2498ae664184c45def51ff0b61d4bd6c6671db6cb4fStefani Seibold mutex_lock(&shared->lock); 250c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 251c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* We should not own chip if it is already in FL_SYNCING 252c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * state. Put contender and retry. */ 253c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (chip->state == FL_SYNCING) { 254c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev put_chip(map, contender); 255c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_unlock(&contender->mutex); 256c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev goto retry; 257c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 258c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_unlock(&contender->mutex); 259c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 260c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 261c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* Check if we have suspended erase on this chip. 262c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev Must sleep in such a case. */ 263c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (mode == FL_ERASING && shared->erasing 264c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev && shared->erasing->oldstate == FL_ERASING) { 2658ae664184c45def51ff0b61d4bd6c6671db6cb4fStefani Seibold mutex_unlock(&shared->lock); 266c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev set_current_state(TASK_UNINTERRUPTIBLE); 267c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev add_wait_queue(&chip->wq, &wait); 268c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_unlock(&chip->mutex); 269c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev schedule(); 270c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev remove_wait_queue(&chip->wq, &wait); 271c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_lock(&chip->mutex); 272c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev goto retry; 273c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 274c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 275c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* We now own it */ 276c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev shared->writing = chip; 277c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (mode == FL_ERASING) 278c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev shared->erasing = chip; 2798ae664184c45def51ff0b61d4bd6c6671db6cb4fStefani Seibold mutex_unlock(&shared->lock); 280c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 281c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 282c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev ret = chip_ready(map, chip, mode); 283c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (ret == -EAGAIN) 284c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev goto retry; 285c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 286c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return ret; 287c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev} 288c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 289c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevstatic int chip_ready(struct map_info *map, struct flchip *chip, int mode) 290c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev{ 291c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct lpddr_private *lpddr = map->fldrv_priv; 292c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev int ret = 0; 293c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev DECLARE_WAITQUEUE(wait, current); 294c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 295c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* Prevent setting state FL_SYNCING for chip in suspended state. */ 296c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (FL_SYNCING == mode && FL_READY != chip->oldstate) 297c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev goto sleep; 298c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 299c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev switch (chip->state) { 300c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev case FL_READY: 301c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev case FL_JEDEC_QUERY: 302c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return 0; 303c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 304c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev case FL_ERASING: 305c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (!lpddr->qinfo->SuspEraseSupp || 306c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev !(mode == FL_READY || mode == FL_POINT)) 307c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev goto sleep; 308c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 309c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev map_write(map, CMD(LPDDR_SUSPEND), 310c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev map->pfow_base + PFOW_PROGRAM_ERASE_SUSPEND); 311c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev chip->oldstate = FL_ERASING; 312c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev chip->state = FL_ERASE_SUSPENDING; 313c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev ret = wait_for_ready(map, chip, 0); 314c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (ret) { 315c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* Oops. something got wrong. */ 316c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* Resume and pretend we weren't here. */ 317100f2341e305f98de3aa12fb472771ab029cbda7Tadashi Abe put_chip(map, chip); 318c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev printk(KERN_ERR "%s: suspend operation failed." 319c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev "State may be wrong \n", map->name); 320c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return -EIO; 321c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 322c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev chip->erase_suspended = 1; 323c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev chip->state = FL_READY; 324c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return 0; 325c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* Erase suspend */ 326c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev case FL_POINT: 327c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* Only if there's no operation suspended... */ 328c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (mode == FL_READY && chip->oldstate == FL_READY) 329c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return 0; 330c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 331c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev default: 332c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevsleep: 333c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev set_current_state(TASK_UNINTERRUPTIBLE); 334c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev add_wait_queue(&chip->wq, &wait); 335c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_unlock(&chip->mutex); 336c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev schedule(); 337c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev remove_wait_queue(&chip->wq, &wait); 338c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_lock(&chip->mutex); 339c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return -EAGAIN; 340c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 341c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev} 342c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 343c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevstatic void put_chip(struct map_info *map, struct flchip *chip) 344c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev{ 345c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (chip->priv) { 346c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct flchip_shared *shared = chip->priv; 3478ae664184c45def51ff0b61d4bd6c6671db6cb4fStefani Seibold mutex_lock(&shared->lock); 348c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (shared->writing == chip && chip->oldstate == FL_READY) { 349c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* We own the ability to write, but we're done */ 350c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev shared->writing = shared->erasing; 351c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (shared->writing && shared->writing != chip) { 352c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* give back the ownership */ 353c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct flchip *loaner = shared->writing; 354c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_lock(&loaner->mutex); 3558ae664184c45def51ff0b61d4bd6c6671db6cb4fStefani Seibold mutex_unlock(&shared->lock); 356c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_unlock(&chip->mutex); 357c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev put_chip(map, loaner); 358c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_lock(&chip->mutex); 359c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_unlock(&loaner->mutex); 360c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev wake_up(&chip->wq); 361c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return; 362c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 363c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev shared->erasing = NULL; 364c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev shared->writing = NULL; 365c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } else if (shared->erasing == chip && shared->writing != chip) { 366c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* 367c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * We own the ability to erase without the ability 368c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * to write, which means the erase was suspended 369c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * and some other partition is currently writing. 370c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * Don't let the switch below mess things up since 371c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * we don't have ownership to resume anything. 372c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev */ 3738ae664184c45def51ff0b61d4bd6c6671db6cb4fStefani Seibold mutex_unlock(&shared->lock); 374c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev wake_up(&chip->wq); 375c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return; 376c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 3778ae664184c45def51ff0b61d4bd6c6671db6cb4fStefani Seibold mutex_unlock(&shared->lock); 378c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 379c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 380c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev switch (chip->oldstate) { 381c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev case FL_ERASING: 382c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev map_write(map, CMD(LPDDR_RESUME), 383c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev map->pfow_base + PFOW_COMMAND_CODE); 384c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev map_write(map, CMD(LPDDR_START_EXECUTION), 385c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev map->pfow_base + PFOW_COMMAND_EXECUTE); 386c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev chip->oldstate = FL_READY; 387c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev chip->state = FL_ERASING; 388c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev break; 389c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev case FL_READY: 390c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev break; 391c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev default: 392c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev printk(KERN_ERR "%s: put_chip() called with oldstate %d!\n", 393c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev map->name, chip->oldstate); 394c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 395c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev wake_up(&chip->wq); 396c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev} 397c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 398c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevint do_write_buffer(struct map_info *map, struct flchip *chip, 399c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev unsigned long adr, const struct kvec **pvec, 400c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev unsigned long *pvec_seek, int len) 401c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev{ 402c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct lpddr_private *lpddr = map->fldrv_priv; 403c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev map_word datum; 404c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev int ret, wbufsize, word_gap, words; 405c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev const struct kvec *vec; 406c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev unsigned long vec_seek; 407c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev unsigned long prog_buf_ofs; 408c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 409c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev wbufsize = 1 << lpddr->qinfo->BufSizeShift; 410c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 411c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_lock(&chip->mutex); 412c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev ret = get_chip(map, chip, FL_WRITING); 413c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (ret) { 414c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_unlock(&chip->mutex); 415c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return ret; 416c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 417c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* Figure out the number of words to write */ 418c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev word_gap = (-adr & (map_bankwidth(map)-1)); 419c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev words = (len - word_gap + map_bankwidth(map) - 1) / map_bankwidth(map); 420c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (!word_gap) { 421c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev words--; 422c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } else { 423c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev word_gap = map_bankwidth(map) - word_gap; 424c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev adr -= word_gap; 425c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev datum = map_word_ff(map); 426c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 427c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* Write data */ 428c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* Get the program buffer offset from PFOW register data first*/ 429c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev prog_buf_ofs = map->pfow_base + CMDVAL(map_read(map, 430c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev map->pfow_base + PFOW_PROGRAM_BUFFER_OFFSET)); 431c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev vec = *pvec; 432c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev vec_seek = *pvec_seek; 433c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev do { 434c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev int n = map_bankwidth(map) - word_gap; 435c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 436c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (n > vec->iov_len - vec_seek) 437c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev n = vec->iov_len - vec_seek; 438c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (n > len) 439c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev n = len; 440c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 441c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (!word_gap && (len < map_bankwidth(map))) 442c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev datum = map_word_ff(map); 443c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 444c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev datum = map_word_load_partial(map, datum, 445c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev vec->iov_base + vec_seek, word_gap, n); 446c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 447c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev len -= n; 448c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev word_gap += n; 449c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (!len || word_gap == map_bankwidth(map)) { 450c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev map_write(map, datum, prog_buf_ofs); 451c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev prog_buf_ofs += map_bankwidth(map); 452c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev word_gap = 0; 453c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 454c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 455c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev vec_seek += n; 456c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (vec_seek == vec->iov_len) { 457c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev vec++; 458c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev vec_seek = 0; 459c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 460c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } while (len); 461c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev *pvec = vec; 462c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev *pvec_seek = vec_seek; 463c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 464c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* GO GO GO */ 465c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev send_pfow_command(map, LPDDR_BUFF_PROGRAM, adr, wbufsize, NULL); 466c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev chip->state = FL_WRITING; 467c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev ret = wait_for_ready(map, chip, (1<<lpddr->qinfo->ProgBufferTime)); 468c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (ret) { 469c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev printk(KERN_WARNING"%s Buffer program error: %d at %lx; \n", 470c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev map->name, ret, adr); 471c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev goto out; 472c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 473c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 474c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev out: put_chip(map, chip); 475c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_unlock(&chip->mutex); 476c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return ret; 477c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev} 478c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 479c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevint do_erase_oneblock(struct mtd_info *mtd, loff_t adr) 480c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev{ 481c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct map_info *map = mtd->priv; 482c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct lpddr_private *lpddr = map->fldrv_priv; 483c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev int chipnum = adr >> lpddr->chipshift; 484c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct flchip *chip = &lpddr->chips[chipnum]; 485c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev int ret; 486c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 487c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_lock(&chip->mutex); 488c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev ret = get_chip(map, chip, FL_ERASING); 489c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (ret) { 490c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_unlock(&chip->mutex); 491c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return ret; 492c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 493c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev send_pfow_command(map, LPDDR_BLOCK_ERASE, adr, 0, NULL); 494c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev chip->state = FL_ERASING; 495c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev ret = wait_for_ready(map, chip, (1<<lpddr->qinfo->BlockEraseTime)*1000); 496c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (ret) { 497c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev printk(KERN_WARNING"%s Erase block error %d at : %llx\n", 498c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev map->name, ret, adr); 499c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev goto out; 500c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 501c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev out: put_chip(map, chip); 502c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_unlock(&chip->mutex); 503c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return ret; 504c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev} 505c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 506c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevstatic int lpddr_read(struct mtd_info *mtd, loff_t adr, size_t len, 507c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev size_t *retlen, u_char *buf) 508c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev{ 509c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct map_info *map = mtd->priv; 510c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct lpddr_private *lpddr = map->fldrv_priv; 511c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev int chipnum = adr >> lpddr->chipshift; 512c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct flchip *chip = &lpddr->chips[chipnum]; 513c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev int ret = 0; 514c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 515c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_lock(&chip->mutex); 516c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev ret = get_chip(map, chip, FL_READY); 517c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (ret) { 518c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_unlock(&chip->mutex); 519c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return ret; 520c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 521c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 522c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev map_copy_from(map, buf, adr, len); 523c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev *retlen = len; 524c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 525c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev put_chip(map, chip); 526c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_unlock(&chip->mutex); 527c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return ret; 528c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev} 529c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 530c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevstatic int lpddr_point(struct mtd_info *mtd, loff_t adr, size_t len, 531c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev size_t *retlen, void **mtdbuf, resource_size_t *phys) 532c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev{ 533c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct map_info *map = mtd->priv; 534c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct lpddr_private *lpddr = map->fldrv_priv; 535c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev int chipnum = adr >> lpddr->chipshift; 536c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev unsigned long ofs, last_end = 0; 537c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct flchip *chip = &lpddr->chips[chipnum]; 538c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev int ret = 0; 539c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 540c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (!map->virt || (adr + len > mtd->size)) 541c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return -EINVAL; 542c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 543c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* ofs: offset within the first chip that the first read should start */ 544c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev ofs = adr - (chipnum << lpddr->chipshift); 545c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 546c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev *mtdbuf = (void *)map->virt + chip->start + ofs; 547c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev *retlen = 0; 548c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 549c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev while (len) { 550c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev unsigned long thislen; 551c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 552c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (chipnum >= lpddr->numchips) 553c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev break; 554c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 555c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* We cannot point across chips that are virtually disjoint */ 556c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (!last_end) 557c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev last_end = chip->start; 558c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev else if (chip->start != last_end) 559c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev break; 560c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 561c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if ((len + ofs - 1) >> lpddr->chipshift) 562c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev thislen = (1<<lpddr->chipshift) - ofs; 563c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev else 564c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev thislen = len; 565c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* get the chip */ 566c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_lock(&chip->mutex); 567c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev ret = get_chip(map, chip, FL_POINT); 568c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_unlock(&chip->mutex); 569c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (ret) 570c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev break; 571c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 572c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev chip->state = FL_POINT; 573c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev chip->ref_point_counter++; 574c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev *retlen += thislen; 575c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev len -= thislen; 576c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 577c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev ofs = 0; 578c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev last_end += 1 << lpddr->chipshift; 579c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev chipnum++; 580c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev chip = &lpddr->chips[chipnum]; 581c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 582c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return 0; 583c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev} 584c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 585c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevstatic void lpddr_unpoint (struct mtd_info *mtd, loff_t adr, size_t len) 586c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev{ 587c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct map_info *map = mtd->priv; 588c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct lpddr_private *lpddr = map->fldrv_priv; 589c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev int chipnum = adr >> lpddr->chipshift; 590c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev unsigned long ofs; 591c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 592c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* ofs: offset within the first chip that the first read should start */ 593c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev ofs = adr - (chipnum << lpddr->chipshift); 594c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 595c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev while (len) { 596c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev unsigned long thislen; 597c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct flchip *chip; 598c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 599c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev chip = &lpddr->chips[chipnum]; 600c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (chipnum >= lpddr->numchips) 601c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev break; 602c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 603c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if ((len + ofs - 1) >> lpddr->chipshift) 604c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev thislen = (1<<lpddr->chipshift) - ofs; 605c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev else 606c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev thislen = len; 607c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 608c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_lock(&chip->mutex); 609c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (chip->state == FL_POINT) { 610c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev chip->ref_point_counter--; 611c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (chip->ref_point_counter == 0) 612c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev chip->state = FL_READY; 613c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } else 614c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev printk(KERN_WARNING "%s: Warning: unpoint called on non" 615c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev "pointed region\n", map->name); 616c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 617c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev put_chip(map, chip); 618c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_unlock(&chip->mutex); 619c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 620c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev len -= thislen; 621c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev ofs = 0; 622c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev chipnum++; 623c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 624c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev} 625c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 626c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevstatic int lpddr_write_buffers(struct mtd_info *mtd, loff_t to, size_t len, 627c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev size_t *retlen, const u_char *buf) 628c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev{ 629c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct kvec vec; 630c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 631c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev vec.iov_base = (void *) buf; 632c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev vec.iov_len = len; 633c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 634c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return lpddr_writev(mtd, &vec, 1, to, retlen); 635c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev} 636c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 637c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 638c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevstatic int lpddr_writev(struct mtd_info *mtd, const struct kvec *vecs, 639c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev unsigned long count, loff_t to, size_t *retlen) 640c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev{ 641c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct map_info *map = mtd->priv; 642c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct lpddr_private *lpddr = map->fldrv_priv; 643c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev int ret = 0; 644c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev int chipnum; 645c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev unsigned long ofs, vec_seek, i; 646c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev int wbufsize = 1 << lpddr->qinfo->BufSizeShift; 647c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 648c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev size_t len = 0; 649c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 650c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev for (i = 0; i < count; i++) 651c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev len += vecs[i].iov_len; 652c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 653c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev *retlen = 0; 654c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (!len) 655c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return 0; 656c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 657c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev chipnum = to >> lpddr->chipshift; 658c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 659c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev ofs = to; 660c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev vec_seek = 0; 661c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 662c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev do { 663c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* We must not cross write block boundaries */ 664c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev int size = wbufsize - (ofs & (wbufsize-1)); 665c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 666c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (size > len) 667c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev size = len; 668c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 669c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev ret = do_write_buffer(map, &lpddr->chips[chipnum], 670c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev ofs, &vecs, &vec_seek, size); 671c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (ret) 672c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return ret; 673c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 674c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev ofs += size; 675c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev (*retlen) += size; 676c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev len -= size; 677c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 678c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev /* Be nice and reschedule with the chip in a usable 679c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev * state for other processes */ 680c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev cond_resched(); 681c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 682c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } while (len); 683c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 684c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return 0; 685c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev} 686c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 687c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevstatic int lpddr_erase(struct mtd_info *mtd, struct erase_info *instr) 688c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev{ 689c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev unsigned long ofs, len; 690c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev int ret; 691c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct map_info *map = mtd->priv; 692c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct lpddr_private *lpddr = map->fldrv_priv; 693c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev int size = 1 << lpddr->qinfo->UniformBlockSizeShift; 694c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 695c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev ofs = instr->addr; 696c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev len = instr->len; 697c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 698c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (ofs > mtd->size || (len + ofs) > mtd->size) 699c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return -EINVAL; 700c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 701c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev while (len > 0) { 702c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev ret = do_erase_oneblock(mtd, ofs); 703c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (ret) 704c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return ret; 705c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev ofs += size; 706c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev len -= size; 707c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 708c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev instr->state = MTD_ERASE_DONE; 709c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev mtd_erase_callback(instr); 710c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 711c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return 0; 712c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev} 713c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 714c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev#define DO_XXLOCK_LOCK 1 715c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev#define DO_XXLOCK_UNLOCK 2 716c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevint do_xxlock(struct mtd_info *mtd, loff_t adr, uint32_t len, int thunk) 717c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev{ 718c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev int ret = 0; 719c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct map_info *map = mtd->priv; 720c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct lpddr_private *lpddr = map->fldrv_priv; 721c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev int chipnum = adr >> lpddr->chipshift; 722c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct flchip *chip = &lpddr->chips[chipnum]; 723c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 724c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_lock(&chip->mutex); 725c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev ret = get_chip(map, chip, FL_LOCKING); 726c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (ret) { 727c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_unlock(&chip->mutex); 728c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return ret; 729c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 730c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 731c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (thunk == DO_XXLOCK_LOCK) { 732c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev send_pfow_command(map, LPDDR_LOCK_BLOCK, adr, adr + len, NULL); 733c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev chip->state = FL_LOCKING; 734c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } else if (thunk == DO_XXLOCK_UNLOCK) { 735c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev send_pfow_command(map, LPDDR_UNLOCK_BLOCK, adr, adr + len, NULL); 736c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev chip->state = FL_UNLOCKING; 737c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } else 738c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev BUG(); 739c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 740c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev ret = wait_for_ready(map, chip, 1); 741c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (ret) { 742c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev printk(KERN_ERR "%s: block unlock error status %d \n", 743c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev map->name, ret); 744c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev goto out; 745c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 746c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevout: put_chip(map, chip); 747c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_unlock(&chip->mutex); 748c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return ret; 749c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev} 750c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 751c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevstatic int lpddr_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) 752c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev{ 753c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return do_xxlock(mtd, ofs, len, DO_XXLOCK_LOCK); 754c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev} 755c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 756c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevstatic int lpddr_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) 757c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev{ 758c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return do_xxlock(mtd, ofs, len, DO_XXLOCK_UNLOCK); 759c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev} 760c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 761c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevint word_program(struct map_info *map, loff_t adr, uint32_t curval) 762c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev{ 763c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev int ret; 764c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct lpddr_private *lpddr = map->fldrv_priv; 765c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev int chipnum = adr >> lpddr->chipshift; 766c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev struct flchip *chip = &lpddr->chips[chipnum]; 767c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 768c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_lock(&chip->mutex); 769c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev ret = get_chip(map, chip, FL_WRITING); 770c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (ret) { 771c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_unlock(&chip->mutex); 772c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return ret; 773c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 774c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 775c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev send_pfow_command(map, LPDDR_WORD_PROGRAM, adr, 0x00, (map_word *)&curval); 776c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 777c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev ret = wait_for_ready(map, chip, (1<<lpddr->qinfo->SingleWordProgTime)); 778c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev if (ret) { 779c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev printk(KERN_WARNING"%s word_program error at: %llx; val: %x\n", 780c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev map->name, adr, curval); 781c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev goto out; 782c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev } 783c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 784c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolevout: put_chip(map, chip); 785c4e773764cead9358fd4b036d1b883fff3968513Stefani Seibold mutex_unlock(&chip->mutex); 786c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev return ret; 787c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev} 788c68264711ca6caf87794caf9e79c30a4ba73c032Alexey Korolev 789c68264711ca6caf87794caf9e79c30a4ba73c032Alexey KorolevMODULE_LICENSE("GPL"); 790c68264711ca6caf87794caf9e79c30a4ba73c032Alexey KorolevMODULE_AUTHOR("Alexey Korolev <akorolev@infradead.org>"); 791c68264711ca6caf87794caf9e79c30a4ba73c032Alexey KorolevMODULE_DESCRIPTION("MTD driver for LPDDR flash chips"); 792