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