| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * TI OMAP general purpose memory controller emulation. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2007-2009 Nokia Corporation | 
					
						
							|  |  |  |  * Original code written by Andrzej Zaborowski <andrew@openedhand.com> | 
					
						
							|  |  |  |  * Enhancements for OMAP3 and NAND support written by Juha Riihimäki | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or | 
					
						
							|  |  |  |  * modify it under the terms of the GNU General Public License as | 
					
						
							|  |  |  |  * published by the Free Software Foundation; either version 2 or | 
					
						
							|  |  |  |  * (at your option) any later version of the License. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  |  * GNU General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * You should have received a copy of the GNU General Public License along | 
					
						
							|  |  |  |  * with this program; if not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2019-08-12 07:23:42 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-26 18:17:17 +00:00
										 |  |  | #include "qemu/osdep.h"
 | 
					
						
							| 
									
										
										
										
											2019-08-12 07:23:42 +02:00
										 |  |  | #include "hw/irq.h"
 | 
					
						
							| 
									
										
										
										
											2013-02-05 17:06:20 +01:00
										 |  |  | #include "hw/block/flash.h"
 | 
					
						
							|  |  |  | #include "hw/arm/omap.h"
 | 
					
						
							| 
									
										
										
										
											2012-12-17 18:19:49 +01:00
										 |  |  | #include "exec/memory.h"
 | 
					
						
							|  |  |  | #include "exec/address-spaces.h"
 | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* General-Purpose Memory Controller */ | 
					
						
							|  |  |  | struct omap_gpmc_s { | 
					
						
							|  |  |  |     qemu_irq irq; | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |     qemu_irq drq; | 
					
						
							| 
									
										
										
										
											2011-08-15 17:17:23 +03:00
										 |  |  |     MemoryRegion iomem; | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |     int accept_256; | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  |     uint8_t revision; | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  |     uint8_t sysconfig; | 
					
						
							|  |  |  |     uint16_t irqst; | 
					
						
							|  |  |  |     uint16_t irqen; | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |     uint16_t lastirq; | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  |     uint16_t timeout; | 
					
						
							|  |  |  |     uint16_t config; | 
					
						
							|  |  |  |     struct omap_gpmc_cs_file_s { | 
					
						
							|  |  |  |         uint32_t config[7]; | 
					
						
							| 
									
										
										
										
											2011-08-15 17:17:23 +03:00
										 |  |  |         MemoryRegion *iomem; | 
					
						
							|  |  |  |         MemoryRegion container; | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  |         MemoryRegion nandiomem; | 
					
						
							|  |  |  |         DeviceState *dev; | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  |     } cs_file[8]; | 
					
						
							|  |  |  |     int ecc_cs; | 
					
						
							|  |  |  |     int ecc_ptr; | 
					
						
							|  |  |  |     uint32_t ecc_cfg; | 
					
						
							|  |  |  |     ECCState ecc[9]; | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |     struct prefetch { | 
					
						
							|  |  |  |         uint32_t config1; /* GPMC_PREFETCH_CONFIG1 */ | 
					
						
							|  |  |  |         uint32_t transfercount; /* GPMC_PREFETCH_CONFIG2:TRANSFERCOUNT */ | 
					
						
							|  |  |  |         int startengine; /* GPMC_PREFETCH_CONTROL:STARTENGINE */ | 
					
						
							|  |  |  |         int fifopointer; /* GPMC_PREFETCH_STATUS:FIFOPOINTER */ | 
					
						
							|  |  |  |         int count; /* GPMC_PREFETCH_STATUS:COUNTVALUE */ | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |         MemoryRegion iomem; | 
					
						
							|  |  |  |         uint8_t fifo[64]; | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |     } prefetch; | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  | #define OMAP_GPMC_8BIT 0
 | 
					
						
							|  |  |  | #define OMAP_GPMC_16BIT 1
 | 
					
						
							|  |  |  | #define OMAP_GPMC_NOR 0
 | 
					
						
							|  |  |  | #define OMAP_GPMC_NAND 2
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int omap_gpmc_devtype(struct omap_gpmc_cs_file_s *f) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return (f->config[0] >> 10) & 3; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int omap_gpmc_devsize(struct omap_gpmc_cs_file_s *f) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     /* devsize field is really 2 bits but we ignore the high
 | 
					
						
							|  |  |  |      * bit to ensure consistent behaviour if the guest sets | 
					
						
							|  |  |  |      * it (values 2 and 3 are reserved in the TRM) | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     return (f->config[0] >> 12) & 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  | /* Extract the chip-select value from the prefetch config1 register */ | 
					
						
							|  |  |  | static int prefetch_cs(uint32_t config1) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return (config1 >> 24) & 7; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int prefetch_threshold(uint32_t config1) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return (config1 >> 8) & 0x7f; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  | static void omap_gpmc_int_update(struct omap_gpmc_s *s) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |     /* The TRM is a bit unclear, but it seems to say that
 | 
					
						
							|  |  |  |      * the TERMINALCOUNTSTATUS bit is set only on the | 
					
						
							|  |  |  |      * transition when the prefetch engine goes from | 
					
						
							|  |  |  |      * active to inactive, whereas the FIFOEVENTSTATUS | 
					
						
							|  |  |  |      * bit is held high as long as the fifo has at | 
					
						
							|  |  |  |      * least THRESHOLD bytes available. | 
					
						
							|  |  |  |      * So we do the latter here, but TERMINALCOUNTSTATUS | 
					
						
							|  |  |  |      * is set elsewhere. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     if (s->prefetch.fifopointer >= prefetch_threshold(s->prefetch.config1)) { | 
					
						
							|  |  |  |         s->irqst |= 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if ((s->irqen & s->irqst) != s->lastirq) { | 
					
						
							|  |  |  |         s->lastirq = s->irqen & s->irqst; | 
					
						
							|  |  |  |         qemu_set_irq(s->irq, s->lastirq); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void omap_gpmc_dma_update(struct omap_gpmc_s *s, int value) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (s->prefetch.config1 & 4) { | 
					
						
							|  |  |  |         qemu_set_irq(s->drq, value); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  | /* Access functions for when a NAND-like device is mapped into memory:
 | 
					
						
							|  |  |  |  * all addresses in the region behave like accesses to the relevant | 
					
						
							|  |  |  |  * GPMC_NAND_DATA_i register (which is actually implemented to call these) | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-10-23 12:30:10 +02:00
										 |  |  | static uint64_t omap_nand_read(void *opaque, hwaddr addr, | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  |                                unsigned size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct omap_gpmc_cs_file_s *f = (struct omap_gpmc_cs_file_s *)opaque; | 
					
						
							|  |  |  |     uint64_t v; | 
					
						
							|  |  |  |     nand_setpins(f->dev, 0, 0, 0, 1, 0); | 
					
						
							|  |  |  |     switch (omap_gpmc_devsize(f)) { | 
					
						
							|  |  |  |     case OMAP_GPMC_8BIT: | 
					
						
							|  |  |  |         v = nand_getio(f->dev); | 
					
						
							|  |  |  |         if (size == 1) { | 
					
						
							|  |  |  |             return v; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         v |= (nand_getio(f->dev) << 8); | 
					
						
							|  |  |  |         if (size == 2) { | 
					
						
							|  |  |  |             return v; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         v |= (nand_getio(f->dev) << 16); | 
					
						
							|  |  |  |         v |= (nand_getio(f->dev) << 24); | 
					
						
							|  |  |  |         return v; | 
					
						
							|  |  |  |     case OMAP_GPMC_16BIT: | 
					
						
							|  |  |  |         v = nand_getio(f->dev); | 
					
						
							|  |  |  |         if (size == 1) { | 
					
						
							|  |  |  |             /* 8 bit read from 16 bit device : probably a guest bug */ | 
					
						
							|  |  |  |             return v & 0xff; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (size == 2) { | 
					
						
							|  |  |  |             return v; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         v |= (nand_getio(f->dev) << 16); | 
					
						
							|  |  |  |         return v; | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |         abort(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void omap_nand_setio(DeviceState *dev, uint64_t value, | 
					
						
							|  |  |  |                             int nandsize, int size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     /* Write the specified value to the NAND device, respecting
 | 
					
						
							|  |  |  |      * both size of the NAND device and size of the write access. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     switch (nandsize) { | 
					
						
							|  |  |  |     case OMAP_GPMC_8BIT: | 
					
						
							|  |  |  |         switch (size) { | 
					
						
							|  |  |  |         case 1: | 
					
						
							|  |  |  |             nand_setio(dev, value & 0xff); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case 2: | 
					
						
							|  |  |  |             nand_setio(dev, value & 0xff); | 
					
						
							|  |  |  |             nand_setio(dev, (value >> 8) & 0xff); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case 4: | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |             nand_setio(dev, value & 0xff); | 
					
						
							|  |  |  |             nand_setio(dev, (value >> 8) & 0xff); | 
					
						
							|  |  |  |             nand_setio(dev, (value >> 16) & 0xff); | 
					
						
							|  |  |  |             nand_setio(dev, (value >> 24) & 0xff); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2011-11-09 17:42:23 +00:00
										 |  |  |         break; | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  |     case OMAP_GPMC_16BIT: | 
					
						
							|  |  |  |         switch (size) { | 
					
						
							|  |  |  |         case 1: | 
					
						
							|  |  |  |             /* writing to a 16bit device with 8bit access is probably a guest
 | 
					
						
							|  |  |  |              * bug; pass the value through anyway. | 
					
						
							|  |  |  |              */ | 
					
						
							|  |  |  |         case 2: | 
					
						
							|  |  |  |             nand_setio(dev, value & 0xffff); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case 4: | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |             nand_setio(dev, value & 0xffff); | 
					
						
							|  |  |  |             nand_setio(dev, (value >> 16) & 0xffff); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2011-11-09 17:42:23 +00:00
										 |  |  |         break; | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-23 12:30:10 +02:00
										 |  |  | static void omap_nand_write(void *opaque, hwaddr addr, | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  |                             uint64_t value, unsigned size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct omap_gpmc_cs_file_s *f = (struct omap_gpmc_cs_file_s *)opaque; | 
					
						
							|  |  |  |     nand_setpins(f->dev, 0, 0, 0, 1, 0); | 
					
						
							|  |  |  |     omap_nand_setio(f->dev, value, omap_gpmc_devsize(f), size); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const MemoryRegionOps omap_nand_ops = { | 
					
						
							|  |  |  |     .read = omap_nand_read, | 
					
						
							|  |  |  |     .write = omap_nand_write, | 
					
						
							|  |  |  |     .endianness = DEVICE_NATIVE_ENDIAN, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  | static void fill_prefetch_fifo(struct omap_gpmc_s *s) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     /* Fill the prefetch FIFO by reading data from NAND.
 | 
					
						
							|  |  |  |      * We do this synchronously, unlike the hardware which | 
					
						
							|  |  |  |      * will do this asynchronously. We refill when the | 
					
						
							|  |  |  |      * FIFO has THRESHOLD bytes free, and we always refill | 
					
						
							|  |  |  |      * as much data as possible starting at the top end | 
					
						
							|  |  |  |      * of the FIFO. | 
					
						
							|  |  |  |      * (We have to refill at THRESHOLD rather than waiting | 
					
						
							|  |  |  |      * for the FIFO to empty to allow for the case where | 
					
						
							|  |  |  |      * the FIFO size isn't an exact multiple of THRESHOLD | 
					
						
							|  |  |  |      * and we're doing DMA transfers.) | 
					
						
							|  |  |  |      * This means we never need to handle wrap-around in | 
					
						
							|  |  |  |      * the fifo-reading code, and the next byte of data | 
					
						
							|  |  |  |      * to read is always fifo[63 - fifopointer]. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     int fptr; | 
					
						
							|  |  |  |     int cs = prefetch_cs(s->prefetch.config1); | 
					
						
							|  |  |  |     int is16bit = (((s->cs_file[cs].config[0] >> 12) & 3) != 0); | 
					
						
							|  |  |  |     int bytes; | 
					
						
							|  |  |  |     /* Don't believe the bit of the OMAP TRM that says that COUNTVALUE
 | 
					
						
							|  |  |  |      * and TRANSFERCOUNT are in units of 16 bit words for 16 bit NAND. | 
					
						
							|  |  |  |      * Instead believe the bit that says it is always a byte count. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     bytes = 64 - s->prefetch.fifopointer; | 
					
						
							|  |  |  |     if (bytes > s->prefetch.count) { | 
					
						
							|  |  |  |         bytes = s->prefetch.count; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2014-05-13 16:09:39 +01:00
										 |  |  |     if (is16bit) { | 
					
						
							|  |  |  |         bytes &= ~1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |     s->prefetch.count -= bytes; | 
					
						
							|  |  |  |     s->prefetch.fifopointer += bytes; | 
					
						
							|  |  |  |     fptr = 64 - s->prefetch.fifopointer; | 
					
						
							|  |  |  |     /* Move the existing data in the FIFO so it sits just
 | 
					
						
							|  |  |  |      * before what we're about to read in | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     while (fptr < (64 - bytes)) { | 
					
						
							|  |  |  |         s->prefetch.fifo[fptr] = s->prefetch.fifo[fptr + bytes]; | 
					
						
							|  |  |  |         fptr++; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     while (fptr < 64) { | 
					
						
							|  |  |  |         if (is16bit) { | 
					
						
							|  |  |  |             uint32_t v = omap_nand_read(&s->cs_file[cs], 0, 2); | 
					
						
							|  |  |  |             s->prefetch.fifo[fptr++] = v & 0xff; | 
					
						
							|  |  |  |             s->prefetch.fifo[fptr++] = (v >> 8) & 0xff; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             s->prefetch.fifo[fptr++] = omap_nand_read(&s->cs_file[cs], 0, 1); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (s->prefetch.startengine && (s->prefetch.count == 0)) { | 
					
						
							|  |  |  |         /* This was the final transfer: raise TERMINALCOUNTSTATUS */ | 
					
						
							|  |  |  |         s->irqst |= 2; | 
					
						
							|  |  |  |         s->prefetch.startengine = 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     /* If there are any bytes in the FIFO at this point then
 | 
					
						
							|  |  |  |      * we must raise a DMA request (either this is a final part | 
					
						
							|  |  |  |      * transfer, or we filled the FIFO in which case we certainly | 
					
						
							|  |  |  |      * have THRESHOLD bytes available) | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     if (s->prefetch.fifopointer != 0) { | 
					
						
							|  |  |  |         omap_gpmc_dma_update(s, 1); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     omap_gpmc_int_update(s); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Access functions for a NAND-like device when the prefetch/postwrite
 | 
					
						
							|  |  |  |  * engine is enabled -- all addresses in the region behave alike: | 
					
						
							|  |  |  |  * data is read or written to the FIFO. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-10-23 12:30:10 +02:00
										 |  |  | static uint64_t omap_gpmc_prefetch_read(void *opaque, hwaddr addr, | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |                                         unsigned size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque; | 
					
						
							|  |  |  |     uint32_t data; | 
					
						
							|  |  |  |     if (s->prefetch.config1 & 1) { | 
					
						
							|  |  |  |         /* The TRM doesn't define the behaviour if you read from the
 | 
					
						
							|  |  |  |          * FIFO when the prefetch engine is in write mode. We choose | 
					
						
							|  |  |  |          * to always return zero. | 
					
						
							|  |  |  |          */ | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     /* Note that trying to read an empty fifo repeats the last byte */ | 
					
						
							|  |  |  |     if (s->prefetch.fifopointer) { | 
					
						
							|  |  |  |         s->prefetch.fifopointer--; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     data = s->prefetch.fifo[63 - s->prefetch.fifopointer]; | 
					
						
							|  |  |  |     if (s->prefetch.fifopointer == | 
					
						
							|  |  |  |         (64 - prefetch_threshold(s->prefetch.config1))) { | 
					
						
							|  |  |  |         /* We've drained THRESHOLD bytes now. So deassert the
 | 
					
						
							|  |  |  |          * DMA request, then refill the FIFO (which will probably | 
					
						
							|  |  |  |          * assert it again.) | 
					
						
							|  |  |  |          */ | 
					
						
							|  |  |  |         omap_gpmc_dma_update(s, 0); | 
					
						
							|  |  |  |         fill_prefetch_fifo(s); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     omap_gpmc_int_update(s); | 
					
						
							|  |  |  |     return data; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-23 12:30:10 +02:00
										 |  |  | static void omap_gpmc_prefetch_write(void *opaque, hwaddr addr, | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |                                      uint64_t value, unsigned size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque; | 
					
						
							|  |  |  |     int cs = prefetch_cs(s->prefetch.config1); | 
					
						
							|  |  |  |     if ((s->prefetch.config1 & 1) == 0) { | 
					
						
							|  |  |  |         /* The TRM doesn't define the behaviour of writing to the
 | 
					
						
							|  |  |  |          * FIFO when the prefetch engine is in read mode. We | 
					
						
							|  |  |  |          * choose to ignore the write. | 
					
						
							|  |  |  |          */ | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (s->prefetch.count == 0) { | 
					
						
							|  |  |  |         /* The TRM doesn't define the behaviour of writing to the
 | 
					
						
							|  |  |  |          * FIFO if the transfer is complete. We choose to ignore. | 
					
						
							|  |  |  |          */ | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     /* The only reason we do any data buffering in postwrite
 | 
					
						
							|  |  |  |      * mode is if we are talking to a 16 bit NAND device, in | 
					
						
							|  |  |  |      * which case we need to buffer the first byte of the | 
					
						
							|  |  |  |      * 16 bit word until the other byte arrives. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     int is16bit = (((s->cs_file[cs].config[0] >> 12) & 3) != 0); | 
					
						
							|  |  |  |     if (is16bit) { | 
					
						
							|  |  |  |         /* fifopointer alternates between 64 (waiting for first
 | 
					
						
							|  |  |  |          * byte of word) and 63 (waiting for second byte) | 
					
						
							|  |  |  |          */ | 
					
						
							|  |  |  |         if (s->prefetch.fifopointer == 64) { | 
					
						
							|  |  |  |             s->prefetch.fifo[0] = value; | 
					
						
							|  |  |  |             s->prefetch.fifopointer--; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             value = (value << 8) | s->prefetch.fifo[0]; | 
					
						
							|  |  |  |             omap_nand_write(&s->cs_file[cs], 0, value, 2); | 
					
						
							|  |  |  |             s->prefetch.count--; | 
					
						
							|  |  |  |             s->prefetch.fifopointer = 64; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         /* Just write the byte : fifopointer remains 64 at all times */ | 
					
						
							|  |  |  |         omap_nand_write(&s->cs_file[cs], 0, value, 1); | 
					
						
							|  |  |  |         s->prefetch.count--; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (s->prefetch.count == 0) { | 
					
						
							|  |  |  |         /* Final transfer: raise TERMINALCOUNTSTATUS */ | 
					
						
							|  |  |  |         s->irqst |= 2; | 
					
						
							|  |  |  |         s->prefetch.startengine = 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     omap_gpmc_int_update(s); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const MemoryRegionOps omap_prefetch_ops = { | 
					
						
							|  |  |  |     .read = omap_gpmc_prefetch_read, | 
					
						
							|  |  |  |     .write = omap_gpmc_prefetch_write, | 
					
						
							|  |  |  |     .endianness = DEVICE_NATIVE_ENDIAN, | 
					
						
							|  |  |  |     .impl.min_access_size = 1, | 
					
						
							|  |  |  |     .impl.max_access_size = 1, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  | static MemoryRegion *omap_gpmc_cs_memregion(struct omap_gpmc_s *s, int cs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     /* Return the MemoryRegion* to map/unmap for this chipselect */ | 
					
						
							|  |  |  |     struct omap_gpmc_cs_file_s *f = &s->cs_file[cs]; | 
					
						
							|  |  |  |     if (omap_gpmc_devtype(f) == OMAP_GPMC_NOR) { | 
					
						
							|  |  |  |         return f->iomem; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |     if ((s->prefetch.config1 & 0x80) && | 
					
						
							|  |  |  |         (prefetch_cs(s->prefetch.config1) == cs)) { | 
					
						
							|  |  |  |         /* The prefetch engine is enabled for this CS: map the FIFO */ | 
					
						
							|  |  |  |         return &s->prefetch.iomem; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  |     return &f->nandiomem; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:18 +00:00
										 |  |  | static void omap_gpmc_cs_map(struct omap_gpmc_s *s, int cs) | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:18 +00:00
										 |  |  |     struct omap_gpmc_cs_file_s *f = &s->cs_file[cs]; | 
					
						
							|  |  |  |     uint32_t mask = (f->config[6] >> 8) & 0xf; | 
					
						
							|  |  |  |     uint32_t base = f->config[6] & 0x3f; | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:18 +00:00
										 |  |  |     uint32_t size; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  |     if (!f->iomem && !f->dev) { | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:18 +00:00
										 |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:18 +00:00
										 |  |  |     if (!(f->config[6] & (1 << 6))) { | 
					
						
							|  |  |  |         /* Do nothing unless CSVALID */ | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  |     /* TODO: check for overlapping regions and report access errors */ | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |     if (mask != 0x8 && mask != 0xc && mask != 0xe && mask != 0xf | 
					
						
							|  |  |  |          && !(s->accept_256 && !mask)) { | 
					
						
							|  |  |  |         fprintf(stderr, "%s: invalid chip-select mask address (0x%x)\n", | 
					
						
							|  |  |  |                  __func__, mask); | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:18 +00:00
										 |  |  |     base <<= 24; | 
					
						
							|  |  |  |     size = (0x0fffffff & ~(mask << 24)) + 1; | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  |     /* TODO: rather than setting the size of the mapping (which should be
 | 
					
						
							|  |  |  |      * constant), the mask should cause wrapping of the address space, so | 
					
						
							|  |  |  |      * that the same memory becomes accessible at every <i>size</i> bytes | 
					
						
							|  |  |  |      * starting from <i>base</i>.  */ | 
					
						
							| 
									
										
										
										
											2013-06-06 05:41:28 -04:00
										 |  |  |     memory_region_init(&f->container, NULL, "omap-gpmc-file", size); | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  |     memory_region_add_subregion(&f->container, 0, | 
					
						
							|  |  |  |                                 omap_gpmc_cs_memregion(s, cs)); | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:18 +00:00
										 |  |  |     memory_region_add_subregion(get_system_memory(), base, | 
					
						
							|  |  |  |                                 &f->container); | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:18 +00:00
										 |  |  | static void omap_gpmc_cs_unmap(struct omap_gpmc_s *s, int cs) | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:18 +00:00
										 |  |  |     struct omap_gpmc_cs_file_s *f = &s->cs_file[cs]; | 
					
						
							|  |  |  |     if (!(f->config[6] & (1 << 6))) { | 
					
						
							|  |  |  |         /* Do nothing unless CSVALID */ | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  |     if (!f->iomem && !f->dev) { | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:18 +00:00
										 |  |  |         return; | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:18 +00:00
										 |  |  |     memory_region_del_subregion(get_system_memory(), &f->container); | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  |     memory_region_del_subregion(&f->container, omap_gpmc_cs_memregion(s, cs)); | 
					
						
							| 
									
										
										
										
											2014-06-11 12:42:01 +02:00
										 |  |  |     object_unparent(OBJECT(&f->container)); | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void omap_gpmc_reset(struct omap_gpmc_s *s) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     s->sysconfig = 0; | 
					
						
							|  |  |  |     s->irqst = 0; | 
					
						
							|  |  |  |     s->irqen = 0; | 
					
						
							|  |  |  |     omap_gpmc_int_update(s); | 
					
						
							| 
									
										
										
										
											2011-12-20 00:21:56 +00:00
										 |  |  |     for (i = 0; i < 8; i++) { | 
					
						
							|  |  |  |         /* This has to happen before we change any of the config
 | 
					
						
							|  |  |  |          * used to determine which memory regions are mapped or unmapped. | 
					
						
							|  |  |  |          */ | 
					
						
							|  |  |  |         omap_gpmc_cs_unmap(s, i); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  |     s->timeout = 0; | 
					
						
							|  |  |  |     s->config = 0xa00; | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |     s->prefetch.config1 = 0x00004000; | 
					
						
							|  |  |  |     s->prefetch.transfercount = 0x00000000; | 
					
						
							|  |  |  |     s->prefetch.startengine = 0; | 
					
						
							|  |  |  |     s->prefetch.fifopointer = 0; | 
					
						
							|  |  |  |     s->prefetch.count = 0; | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  |     for (i = 0; i < 8; i ++) { | 
					
						
							|  |  |  |         s->cs_file[i].config[1] = 0x101001; | 
					
						
							|  |  |  |         s->cs_file[i].config[2] = 0x020201; | 
					
						
							|  |  |  |         s->cs_file[i].config[3] = 0x10031003; | 
					
						
							|  |  |  |         s->cs_file[i].config[4] = 0x10f1111; | 
					
						
							|  |  |  |         s->cs_file[i].config[5] = 0; | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:18 +00:00
										 |  |  |         s->cs_file[i].config[6] = 0xf00; | 
					
						
							|  |  |  |         /* In theory we could probe attached devices for some CFG1
 | 
					
						
							|  |  |  |          * bits here, but we just retain them across resets as they | 
					
						
							|  |  |  |          * were set initially by omap_gpmc_attach(). | 
					
						
							|  |  |  |          */ | 
					
						
							|  |  |  |         if (i == 0) { | 
					
						
							|  |  |  |             s->cs_file[i].config[0] &= 0x00433e00; | 
					
						
							|  |  |  |             s->cs_file[i].config[6] |= 1 << 6; /* CSVALID */ | 
					
						
							|  |  |  |             omap_gpmc_cs_map(s, i); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             s->cs_file[i].config[0] &= 0x00403c00; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |     s->ecc_cs = 0; | 
					
						
							|  |  |  |     s->ecc_ptr = 0; | 
					
						
							|  |  |  |     s->ecc_cfg = 0x3fcff000; | 
					
						
							|  |  |  |     for (i = 0; i < 9; i ++) | 
					
						
							|  |  |  |         ecc_reset(&s->ecc[i]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-23 12:30:10 +02:00
										 |  |  | static int gpmc_wordaccess_only(hwaddr addr) | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  | { | 
					
						
							|  |  |  |     /* Return true if the register offset is to a register that
 | 
					
						
							|  |  |  |      * only permits word width accesses. | 
					
						
							|  |  |  |      * Non-word accesses are only OK for GPMC_NAND_DATA/ADDRESS/COMMAND | 
					
						
							|  |  |  |      * for any chipselect. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     if (addr >= 0x60 && addr <= 0x1d4) { | 
					
						
							|  |  |  |         int cs = (addr - 0x60) / 0x30; | 
					
						
							|  |  |  |         addr -= cs * 0x30; | 
					
						
							|  |  |  |         if (addr >= 0x7c && addr < 0x88) { | 
					
						
							|  |  |  |             /* GPMC_NAND_COMMAND, GPMC_NAND_ADDRESS, GPMC_NAND_DATA */ | 
					
						
							|  |  |  |             return 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-23 12:30:10 +02:00
										 |  |  | static uint64_t omap_gpmc_read(void *opaque, hwaddr addr, | 
					
						
							| 
									
										
										
										
											2011-08-15 17:17:23 +03:00
										 |  |  |                                unsigned size) | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  | { | 
					
						
							|  |  |  |     struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque; | 
					
						
							|  |  |  |     int cs; | 
					
						
							|  |  |  |     struct omap_gpmc_cs_file_s *f; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  |     if (size != 4 && gpmc_wordaccess_only(addr)) { | 
					
						
							| 
									
										
										
										
											2011-08-15 17:17:23 +03:00
										 |  |  |         return omap_badwidth_read32(opaque, addr); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  |     switch (addr) { | 
					
						
							|  |  |  |     case 0x000:	/* GPMC_REVISION */ | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  |         return s->revision; | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     case 0x010:	/* GPMC_SYSCONFIG */ | 
					
						
							|  |  |  |         return s->sysconfig; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x014:	/* GPMC_SYSSTATUS */ | 
					
						
							|  |  |  |         return 1;						/* RESETDONE */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x018:	/* GPMC_IRQSTATUS */ | 
					
						
							|  |  |  |         return s->irqst; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x01c:	/* GPMC_IRQENABLE */ | 
					
						
							|  |  |  |         return s->irqen; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x040:	/* GPMC_TIMEOUT_CONTROL */ | 
					
						
							|  |  |  |         return s->timeout; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x044:	/* GPMC_ERR_ADDRESS */ | 
					
						
							|  |  |  |     case 0x048:	/* GPMC_ERR_TYPE */ | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x050:	/* GPMC_CONFIG */ | 
					
						
							|  |  |  |         return s->config; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x054:	/* GPMC_STATUS */ | 
					
						
							|  |  |  |         return 0x001; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x060 ... 0x1d4: | 
					
						
							|  |  |  |         cs = (addr - 0x060) / 0x30; | 
					
						
							|  |  |  |         addr -= cs * 0x30; | 
					
						
							|  |  |  |         f = s->cs_file + cs; | 
					
						
							|  |  |  |         switch (addr) { | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  |         case 0x60:      /* GPMC_CONFIG1 */ | 
					
						
							|  |  |  |             return f->config[0]; | 
					
						
							|  |  |  |         case 0x64:      /* GPMC_CONFIG2 */ | 
					
						
							|  |  |  |             return f->config[1]; | 
					
						
							|  |  |  |         case 0x68:      /* GPMC_CONFIG3 */ | 
					
						
							|  |  |  |             return f->config[2]; | 
					
						
							|  |  |  |         case 0x6c:      /* GPMC_CONFIG4 */ | 
					
						
							|  |  |  |             return f->config[3]; | 
					
						
							|  |  |  |         case 0x70:      /* GPMC_CONFIG5 */ | 
					
						
							|  |  |  |             return f->config[4]; | 
					
						
							|  |  |  |         case 0x74:      /* GPMC_CONFIG6 */ | 
					
						
							|  |  |  |             return f->config[5]; | 
					
						
							|  |  |  |         case 0x78:      /* GPMC_CONFIG7 */ | 
					
						
							|  |  |  |             return f->config[6]; | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  |         case 0x84 ... 0x87: /* GPMC_NAND_DATA */ | 
					
						
							|  |  |  |             if (omap_gpmc_devtype(f) == OMAP_GPMC_NAND) { | 
					
						
							|  |  |  |                 return omap_nand_read(f, 0, size); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  |             return 0; | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x1e0:	/* GPMC_PREFETCH_CONFIG1 */ | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |         return s->prefetch.config1; | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  |     case 0x1e4:	/* GPMC_PREFETCH_CONFIG2 */ | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |         return s->prefetch.transfercount; | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  |     case 0x1ec:	/* GPMC_PREFETCH_CONTROL */ | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |         return s->prefetch.startengine; | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  |     case 0x1f0:	/* GPMC_PREFETCH_STATUS */ | 
					
						
							| 
									
										
										
										
											2011-09-17 19:51:48 +01:00
										 |  |  |         /* NB: The OMAP3 TRM is inconsistent about whether the GPMC
 | 
					
						
							|  |  |  |          * FIFOTHRESHOLDSTATUS bit should be set when | 
					
						
							|  |  |  |          * FIFOPOINTER > FIFOTHRESHOLD or when it is >= FIFOTHRESHOLD. | 
					
						
							|  |  |  |          * Apparently the underlying functional spec from which the TRM was | 
					
						
							|  |  |  |          * created states that the behaviour is ">=", and this also | 
					
						
							|  |  |  |          * makes more conceptual sense. | 
					
						
							|  |  |  |          */ | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |         return (s->prefetch.fifopointer << 24) | | 
					
						
							|  |  |  |                 ((s->prefetch.fifopointer >= | 
					
						
							|  |  |  |                   ((s->prefetch.config1 >> 8) & 0x7f) ? 1 : 0) << 16) | | 
					
						
							|  |  |  |                 s->prefetch.count; | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     case 0x1f4:	/* GPMC_ECC_CONFIG */ | 
					
						
							|  |  |  |         return s->ecc_cs; | 
					
						
							|  |  |  |     case 0x1f8:	/* GPMC_ECC_CONTROL */ | 
					
						
							|  |  |  |         return s->ecc_ptr; | 
					
						
							|  |  |  |     case 0x1fc:	/* GPMC_ECC_SIZE_CONFIG */ | 
					
						
							|  |  |  |         return s->ecc_cfg; | 
					
						
							|  |  |  |     case 0x200 ... 0x220:	/* GPMC_ECC_RESULT */ | 
					
						
							|  |  |  |         cs = (addr & 0x1f) >> 2; | 
					
						
							|  |  |  |         /* TODO: check correctness */ | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |                 ((s->ecc[cs].cp    &  0x07) <<  0) | | 
					
						
							|  |  |  |                 ((s->ecc[cs].cp    &  0x38) << 13) | | 
					
						
							|  |  |  |                 ((s->ecc[cs].lp[0] & 0x1ff) <<  3) | | 
					
						
							|  |  |  |                 ((s->ecc[cs].lp[1] & 0x1ff) << 19); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x230:	/* GPMC_TESTMODE_CTRL */ | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     case 0x234:	/* GPMC_PSA_LSB */ | 
					
						
							|  |  |  |     case 0x238:	/* GPMC_PSA_MSB */ | 
					
						
							|  |  |  |         return 0x00000000; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     OMAP_BAD_REG(addr); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-23 12:30:10 +02:00
										 |  |  | static void omap_gpmc_write(void *opaque, hwaddr addr, | 
					
						
							| 
									
										
										
										
											2011-08-15 17:17:23 +03:00
										 |  |  |                             uint64_t value, unsigned size) | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  | { | 
					
						
							|  |  |  |     struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque; | 
					
						
							|  |  |  |     int cs; | 
					
						
							|  |  |  |     struct omap_gpmc_cs_file_s *f; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  |     if (size != 4 && gpmc_wordaccess_only(addr)) { | 
					
						
							| 
									
										
										
										
											2015-03-08 19:21:13 +01:00
										 |  |  |         omap_badwidth_write32(opaque, addr, value); | 
					
						
							|  |  |  |         return; | 
					
						
							| 
									
										
										
										
											2011-08-15 17:17:23 +03:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  |     switch (addr) { | 
					
						
							|  |  |  |     case 0x000:	/* GPMC_REVISION */ | 
					
						
							|  |  |  |     case 0x014:	/* GPMC_SYSSTATUS */ | 
					
						
							|  |  |  |     case 0x054:	/* GPMC_STATUS */ | 
					
						
							|  |  |  |     case 0x1f0:	/* GPMC_PREFETCH_STATUS */ | 
					
						
							|  |  |  |     case 0x200 ... 0x220:	/* GPMC_ECC_RESULT */ | 
					
						
							|  |  |  |     case 0x234:	/* GPMC_PSA_LSB */ | 
					
						
							|  |  |  |     case 0x238:	/* GPMC_PSA_MSB */ | 
					
						
							|  |  |  |         OMAP_RO_REG(addr); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x010:	/* GPMC_SYSCONFIG */ | 
					
						
							|  |  |  |         if ((value >> 3) == 0x3) | 
					
						
							| 
									
										
										
										
											2011-08-15 17:17:23 +03:00
										 |  |  |             fprintf(stderr, "%s: bad SDRAM idle mode %"PRIi64"\n", | 
					
						
							| 
									
										
										
										
											2017-11-08 14:56:31 -08:00
										 |  |  |                             __func__, value >> 3); | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  |         if (value & 2) | 
					
						
							|  |  |  |             omap_gpmc_reset(s); | 
					
						
							|  |  |  |         s->sysconfig = value & 0x19; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x018:	/* GPMC_IRQSTATUS */ | 
					
						
							| 
									
										
										
										
											2011-09-17 19:51:49 +01:00
										 |  |  |         s->irqst &= ~value; | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  |         omap_gpmc_int_update(s); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x01c:	/* GPMC_IRQENABLE */ | 
					
						
							|  |  |  |         s->irqen = value & 0xf03; | 
					
						
							|  |  |  |         omap_gpmc_int_update(s); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x040:	/* GPMC_TIMEOUT_CONTROL */ | 
					
						
							|  |  |  |         s->timeout = value & 0x1ff1; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x044:	/* GPMC_ERR_ADDRESS */ | 
					
						
							|  |  |  |     case 0x048:	/* GPMC_ERR_TYPE */ | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x050:	/* GPMC_CONFIG */ | 
					
						
							|  |  |  |         s->config = value & 0xf13; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x060 ... 0x1d4: | 
					
						
							|  |  |  |         cs = (addr - 0x060) / 0x30; | 
					
						
							|  |  |  |         addr -= cs * 0x30; | 
					
						
							|  |  |  |         f = s->cs_file + cs; | 
					
						
							|  |  |  |         switch (addr) { | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  |         case 0x60:      /* GPMC_CONFIG1 */ | 
					
						
							|  |  |  |             f->config[0] = value & 0xffef3e13; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case 0x64:      /* GPMC_CONFIG2 */ | 
					
						
							|  |  |  |             f->config[1] = value & 0x001f1f8f; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case 0x68:      /* GPMC_CONFIG3 */ | 
					
						
							|  |  |  |             f->config[2] = value & 0x001f1f8f; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case 0x6c:      /* GPMC_CONFIG4 */ | 
					
						
							|  |  |  |             f->config[3] = value & 0x1f8f1f8f; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case 0x70:      /* GPMC_CONFIG5 */ | 
					
						
							|  |  |  |             f->config[4] = value & 0x0f1f1f1f; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case 0x74:      /* GPMC_CONFIG6 */ | 
					
						
							|  |  |  |             f->config[5] = value & 0x00000fcf; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case 0x78:      /* GPMC_CONFIG7 */ | 
					
						
							|  |  |  |             if ((f->config[6] ^ value) & 0xf7f) { | 
					
						
							|  |  |  |                 omap_gpmc_cs_unmap(s, cs); | 
					
						
							|  |  |  |                 f->config[6] = value & 0x00000f7f; | 
					
						
							|  |  |  |                 omap_gpmc_cs_map(s, cs); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  |         case 0x7c ... 0x7f: /* GPMC_NAND_COMMAND */ | 
					
						
							|  |  |  |             if (omap_gpmc_devtype(f) == OMAP_GPMC_NAND) { | 
					
						
							|  |  |  |                 nand_setpins(f->dev, 1, 0, 0, 1, 0); /* CLE */ | 
					
						
							|  |  |  |                 omap_nand_setio(f->dev, value, omap_gpmc_devsize(f), size); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case 0x80 ... 0x83: /* GPMC_NAND_ADDRESS */ | 
					
						
							|  |  |  |             if (omap_gpmc_devtype(f) == OMAP_GPMC_NAND) { | 
					
						
							|  |  |  |                 nand_setpins(f->dev, 0, 1, 0, 1, 0); /* ALE */ | 
					
						
							|  |  |  |                 omap_nand_setio(f->dev, value, omap_gpmc_devsize(f), size); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case 0x84 ... 0x87: /* GPMC_NAND_DATA */ | 
					
						
							|  |  |  |             if (omap_gpmc_devtype(f) == OMAP_GPMC_NAND) { | 
					
						
							|  |  |  |                 omap_nand_write(f, 0, value, size); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  |             break; | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |             goto bad_reg; | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x1e0:	/* GPMC_PREFETCH_CONFIG1 */ | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |         if (!s->prefetch.startengine) { | 
					
						
							| 
									
										
										
										
											2011-12-20 00:21:56 +00:00
										 |  |  |             uint32_t newconfig1 = value & 0x7f8f7fbf; | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |             uint32_t changed; | 
					
						
							| 
									
										
										
										
											2011-12-20 00:21:56 +00:00
										 |  |  |             changed = newconfig1 ^ s->prefetch.config1; | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |             if (changed & (0x80 | 0x7000000)) { | 
					
						
							|  |  |  |                 /* Turning the engine on or off, or mapping it somewhere else.
 | 
					
						
							|  |  |  |                  * cs_map() and cs_unmap() check the prefetch config and | 
					
						
							|  |  |  |                  * overall CSVALID bits, so it is sufficient to unmap-and-map | 
					
						
							| 
									
										
										
										
											2011-12-20 00:21:56 +00:00
										 |  |  |                  * both the old cs and the new one. Note that we adhere to | 
					
						
							|  |  |  |                  * the "unmap/change config/map" order (and not unmap twice | 
					
						
							|  |  |  |                  * if newcs == oldcs), otherwise we'll try to delete the wrong | 
					
						
							|  |  |  |                  * memory region. | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |                  */ | 
					
						
							| 
									
										
										
										
											2011-12-20 00:21:56 +00:00
										 |  |  |                 int oldcs = prefetch_cs(s->prefetch.config1); | 
					
						
							|  |  |  |                 int newcs = prefetch_cs(newconfig1); | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |                 omap_gpmc_cs_unmap(s, oldcs); | 
					
						
							| 
									
										
										
										
											2011-12-20 00:21:56 +00:00
										 |  |  |                 if (oldcs != newcs) { | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |                     omap_gpmc_cs_unmap(s, newcs); | 
					
						
							| 
									
										
										
										
											2011-12-20 00:21:56 +00:00
										 |  |  |                 } | 
					
						
							|  |  |  |                 s->prefetch.config1 = newconfig1; | 
					
						
							|  |  |  |                 omap_gpmc_cs_map(s, oldcs); | 
					
						
							|  |  |  |                 if (oldcs != newcs) { | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |                     omap_gpmc_cs_map(s, newcs); | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2011-12-20 00:21:56 +00:00
										 |  |  |             } else { | 
					
						
							|  |  |  |                 s->prefetch.config1 = newconfig1; | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x1e4:	/* GPMC_PREFETCH_CONFIG2 */ | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |         if (!s->prefetch.startengine) { | 
					
						
							|  |  |  |             s->prefetch.transfercount = value & 0x3fff; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x1ec:	/* GPMC_PREFETCH_CONTROL */ | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |         if (s->prefetch.startengine != (value & 1)) { | 
					
						
							|  |  |  |             s->prefetch.startengine = value & 1; | 
					
						
							|  |  |  |             if (s->prefetch.startengine) { | 
					
						
							|  |  |  |                 /* Prefetch engine start */ | 
					
						
							|  |  |  |                 s->prefetch.count = s->prefetch.transfercount; | 
					
						
							|  |  |  |                 if (s->prefetch.config1 & 1) { | 
					
						
							|  |  |  |                     /* Write */ | 
					
						
							|  |  |  |                     s->prefetch.fifopointer = 64; | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                     /* Read */ | 
					
						
							|  |  |  |                     s->prefetch.fifopointer = 0; | 
					
						
							|  |  |  |                     fill_prefetch_fifo(s); | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |                 /* Prefetch engine forcibly stopped. The TRM
 | 
					
						
							|  |  |  |                  * doesn't define the behaviour if you do this. | 
					
						
							|  |  |  |                  * We clear the prefetch count, which means that | 
					
						
							|  |  |  |                  * we permit no more writes, and don't read any | 
					
						
							|  |  |  |                  * more data from NAND. The CPU can still drain | 
					
						
							|  |  |  |                  * the FIFO of unread data. | 
					
						
							|  |  |  |                  */ | 
					
						
							|  |  |  |                 s->prefetch.count = 0; | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |             omap_gpmc_int_update(s); | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     case 0x1f4:	/* GPMC_ECC_CONFIG */ | 
					
						
							|  |  |  |         s->ecc_cs = 0x8f; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 0x1f8:	/* GPMC_ECC_CONTROL */ | 
					
						
							|  |  |  |         if (value & (1 << 8)) | 
					
						
							|  |  |  |             for (cs = 0; cs < 9; cs ++) | 
					
						
							|  |  |  |                 ecc_reset(&s->ecc[cs]); | 
					
						
							|  |  |  |         s->ecc_ptr = value & 0xf; | 
					
						
							|  |  |  |         if (s->ecc_ptr == 0 || s->ecc_ptr > 9) { | 
					
						
							|  |  |  |             s->ecc_ptr = 0; | 
					
						
							|  |  |  |             s->ecc_cs &= ~1; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 0x1fc:	/* GPMC_ECC_SIZE_CONFIG */ | 
					
						
							|  |  |  |         s->ecc_cfg = value & 0x3fcff1ff; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case 0x230:	/* GPMC_TESTMODE_CTRL */ | 
					
						
							|  |  |  |         if (value & 7) | 
					
						
							| 
									
										
										
										
											2017-11-08 14:56:31 -08:00
										 |  |  |             fprintf(stderr, "%s: test mode enable attempt\n", __func__); | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  |         break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |     bad_reg: | 
					
						
							|  |  |  |         OMAP_BAD_REG(addr); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-15 17:17:23 +03:00
										 |  |  | static const MemoryRegionOps omap_gpmc_ops = { | 
					
						
							|  |  |  |     .read = omap_gpmc_read, | 
					
						
							|  |  |  |     .write = omap_gpmc_write, | 
					
						
							|  |  |  |     .endianness = DEVICE_NATIVE_ENDIAN, | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  | struct omap_gpmc_s *omap_gpmc_init(struct omap_mpu_state_s *mpu, | 
					
						
							| 
									
										
										
										
											2012-10-23 12:30:10 +02:00
										 |  |  |                                    hwaddr base, | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |                                    qemu_irq irq, qemu_irq drq) | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  |     int cs; | 
					
						
							| 
									
										
											  
											
												arm: Use g_new() & friends where that makes obvious sense
g_new(T, n) is neater than g_malloc(sizeof(T) * n).  It's also safer,
for two reasons.  One, it catches multiplication overflowing size_t.
Two, it returns T * rather than void *, which lets the compiler catch
more type errors.
This commit only touches allocations with size arguments of the form
sizeof(T).
Coccinelle semantic patch:
    @@
    type T;
    @@
    -g_malloc(sizeof(T))
    +g_new(T, 1)
    @@
    type T;
    @@
    -g_try_malloc(sizeof(T))
    +g_try_new(T, 1)
    @@
    type T;
    @@
    -g_malloc0(sizeof(T))
    +g_new0(T, 1)
    @@
    type T;
    @@
    -g_try_malloc0(sizeof(T))
    +g_try_new0(T, 1)
    @@
    type T;
    expression n;
    @@
    -g_malloc(sizeof(T) * (n))
    +g_new(T, n)
    @@
    type T;
    expression n;
    @@
    -g_try_malloc(sizeof(T) * (n))
    +g_try_new(T, n)
    @@
    type T;
    expression n;
    @@
    -g_malloc0(sizeof(T) * (n))
    +g_new0(T, n)
    @@
    type T;
    expression n;
    @@
    -g_try_malloc0(sizeof(T) * (n))
    +g_try_new0(T, n)
    @@
    type T;
    expression p, n;
    @@
    -g_realloc(p, sizeof(T) * (n))
    +g_renew(T, p, n)
    @@
    type T;
    expression p, n;
    @@
    -g_try_realloc(p, sizeof(T) * (n))
    +g_try_renew(T, p, n)
    @@
    type T;
    expression n;
    @@
    -(T *)g_new(T, n)
    +g_new(T, n)
    @@
    type T;
    expression n;
    @@
    -(T *)g_new0(T, n)
    +g_new0(T, n)
    @@
    type T;
    expression p, n;
    @@
    -(T *)g_renew(T, p, n)
    +g_renew(T, p, n)
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-id: 1440524394-15640-1-git-send-email-armbru@redhat.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
											
										 
											2015-09-07 10:39:27 +01:00
										 |  |  |     struct omap_gpmc_s *s = g_new0(struct omap_gpmc_s, 1); | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-06 05:41:28 -04:00
										 |  |  |     memory_region_init_io(&s->iomem, NULL, &omap_gpmc_ops, s, "omap-gpmc", 0x1000); | 
					
						
							| 
									
										
										
										
											2011-08-15 17:17:23 +03:00
										 |  |  |     memory_region_add_subregion(get_system_memory(), base, &s->iomem); | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:18 +00:00
										 |  |  |     s->irq = irq; | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |     s->drq = drq; | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |     s->accept_256 = cpu_is_omap3630(mpu); | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  |     s->revision = cpu_class_omap3(mpu) ? 0x50 : 0x20; | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |     s->lastirq = 0; | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:18 +00:00
										 |  |  |     omap_gpmc_reset(s); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  |     /* We have to register a different IO memory handler for each
 | 
					
						
							|  |  |  |      * chip select region in case a NAND device is mapped there. We | 
					
						
							|  |  |  |      * make the region the worst-case size of 256MB and rely on the | 
					
						
							|  |  |  |      * container memory region in cs_map to chop it down to the actual | 
					
						
							|  |  |  |      * guest-requested size. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     for (cs = 0; cs < 8; cs++) { | 
					
						
							| 
									
										
										
										
											2013-06-06 05:41:28 -04:00
										 |  |  |         memory_region_init_io(&s->cs_file[cs].nandiomem, NULL, | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  |                               &omap_nand_ops, | 
					
						
							|  |  |  |                               &s->cs_file[cs], | 
					
						
							|  |  |  |                               "omap-nand", | 
					
						
							|  |  |  |                               256 * 1024 * 1024); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-06 05:41:28 -04:00
										 |  |  |     memory_region_init_io(&s->prefetch.iomem, NULL, &omap_prefetch_ops, s, | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:20 +00:00
										 |  |  |                           "omap-gpmc-prefetch", 256 * 1024 * 1024); | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  |     return s; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:18 +00:00
										 |  |  | void omap_gpmc_attach(struct omap_gpmc_s *s, int cs, MemoryRegion *iomem) | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  | { | 
					
						
							|  |  |  |     struct omap_gpmc_cs_file_s *f; | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:18 +00:00
										 |  |  |     assert(iomem); | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (cs < 0 || cs >= 8) { | 
					
						
							| 
									
										
										
										
											2017-11-08 14:56:31 -08:00
										 |  |  |         fprintf(stderr, "%s: bad chip-select %i\n", __func__, cs); | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  |         exit(-1); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     f = &s->cs_file[cs]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:18 +00:00
										 |  |  |     omap_gpmc_cs_unmap(s, cs); | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  |     f->config[0] &= ~(0xf << 10); | 
					
						
							| 
									
										
										
										
											2011-08-15 17:17:23 +03:00
										 |  |  |     f->iomem = iomem; | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:18 +00:00
										 |  |  |     omap_gpmc_cs_map(s, cs); | 
					
						
							| 
									
										
										
										
											2010-05-31 23:54:17 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2011-08-28 16:22:19 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | void omap_gpmc_attach_nand(struct omap_gpmc_s *s, int cs, DeviceState *nand) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     struct omap_gpmc_cs_file_s *f; | 
					
						
							|  |  |  |     assert(nand); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (cs < 0 || cs >= 8) { | 
					
						
							|  |  |  |         fprintf(stderr, "%s: bad chip-select %i\n", __func__, cs); | 
					
						
							|  |  |  |         exit(-1); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     f = &s->cs_file[cs]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     omap_gpmc_cs_unmap(s, cs); | 
					
						
							|  |  |  |     f->config[0] &= ~(0xf << 10); | 
					
						
							|  |  |  |     f->config[0] |= (OMAP_GPMC_NAND << 10); | 
					
						
							|  |  |  |     f->dev = nand; | 
					
						
							|  |  |  |     if (nand_getbuswidth(f->dev) == 16) { | 
					
						
							|  |  |  |         f->config[0] |= OMAP_GPMC_16BIT << 12; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     omap_gpmc_cs_map(s, cs); | 
					
						
							|  |  |  | } |