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