1/* 2 * $Id: ixp2000.c,v 1.1.1.1 2007/08/03 18:52:43 Exp $ 3 * 4 * drivers/mtd/maps/ixp2000.c 5 * 6 * Mapping for the Intel XScale IXP2000 based systems 7 * 8 * Copyright (C) 2002 Intel Corp. 9 * Copyright (C) 2003-2004 MontaVista Software, Inc. 10 * 11 * Original Author: Naeem M Afzal <naeem.m.afzal@intel.com> 12 * Maintainer: Deepak Saxena <dsaxena@plexity.net> 13 * 14 * This program is free software; you can redistribute it and/or modify 15 * it under the terms of the GNU General Public License version 2 as 16 * published by the Free Software Foundation. 17 * 18 */ 19 20#include <linux/module.h> 21#include <linux/types.h> 22#include <linux/init.h> 23#include <linux/kernel.h> 24#include <linux/string.h> 25#include <linux/slab.h> 26#include <linux/ioport.h> 27#include <linux/device.h> 28#include <linux/platform_device.h> 29 30#include <linux/mtd/mtd.h> 31#include <linux/mtd/map.h> 32#include <linux/mtd/partitions.h> 33 34#include <asm/io.h> 35#include <asm/hardware.h> 36#include <asm/mach/flash.h> 37 38#include <linux/reboot.h> 39 40struct ixp2000_flash_info { 41 struct mtd_info *mtd; 42 struct map_info map; 43 struct mtd_partition *partitions; 44 struct resource *res; 45}; 46 47static inline unsigned long flash_bank_setup(struct map_info *map, unsigned long ofs) 48{ 49 unsigned long (*set_bank)(unsigned long) = 50 (unsigned long(*)(unsigned long))map->map_priv_2; 51 52 return (set_bank ? set_bank(ofs) : ofs); 53} 54 55#ifdef __ARMEB__ 56/* 57 * Rev A0 and A1 of IXP2400 silicon have a broken addressing unit which 58 * causes the lower address bits to be XORed with 0x11 on 8 bit accesses 59 * and XORed with 0x10 on 16 bit accesses. See the spec update, erratum 44. 60 */ 61static int erratum44_workaround = 0; 62 63static inline unsigned long address_fix8_write(unsigned long addr) 64{ 65 if (erratum44_workaround) { 66 return (addr ^ 3); 67 } 68 return addr; 69} 70#else 71 72#define address_fix8_write(x) (x) 73#endif 74 75static map_word ixp2000_flash_read8(struct map_info *map, unsigned long ofs) 76{ 77 map_word val; 78 79 val.x[0] = *((u8 *)(map->map_priv_1 + flash_bank_setup(map, ofs))); 80 return val; 81} 82 83/* 84 * We can't use the standard memcpy due to the broken SlowPort 85 * address translation on rev A0 and A1 silicon and the fact that 86 * we have banked flash. 87 */ 88static void ixp2000_flash_copy_from(struct map_info *map, void *to, 89 unsigned long from, ssize_t len) 90{ 91 from = flash_bank_setup(map, from); 92 while(len--) 93 *(__u8 *) to++ = *(__u8 *)(map->map_priv_1 + from++); 94} 95 96static void ixp2000_flash_write8(struct map_info *map, map_word d, unsigned long ofs) 97{ 98 *(__u8 *) (address_fix8_write(map->map_priv_1 + 99 flash_bank_setup(map, ofs))) = d.x[0]; 100} 101 102static void ixp2000_flash_copy_to(struct map_info *map, unsigned long to, 103 const void *from, ssize_t len) 104{ 105 to = flash_bank_setup(map, to); 106 while(len--) { 107 unsigned long tmp = address_fix8_write(map->map_priv_1 + to++); 108 *(__u8 *)(tmp) = *(__u8 *)(from++); 109 } 110} 111 112 113static int ixp2000_flash_remove(struct platform_device *dev) 114{ 115 struct flash_platform_data *plat = dev->dev.platform_data; 116 struct ixp2000_flash_info *info = platform_get_drvdata(dev); 117 118 platform_set_drvdata(dev, NULL); 119 120 if(!info) 121 return 0; 122 123 if (info->mtd) { 124 del_mtd_partitions(info->mtd); 125 map_destroy(info->mtd); 126 } 127 if (info->map.map_priv_1) 128 iounmap((void *) info->map.map_priv_1); 129 130 kfree(info->partitions); 131 132 if (info->res) { 133 release_resource(info->res); 134 kfree(info->res); 135 } 136 137 if (plat->exit) 138 plat->exit(); 139 140 return 0; 141} 142 143 144static int ixp2000_flash_probe(struct platform_device *dev) 145{ 146 static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; 147 struct ixp2000_flash_data *ixp_data = dev->dev.platform_data; 148 struct flash_platform_data *plat; 149 struct ixp2000_flash_info *info; 150 unsigned long window_size; 151 int err = -1; 152 153 if (!ixp_data) 154 return -ENODEV; 155 156 plat = ixp_data->platform_data; 157 if (!plat) 158 return -ENODEV; 159 160 window_size = dev->resource->end - dev->resource->start + 1; 161 dev_info(&dev->dev, "Probe of IXP2000 flash(%d banks x %dMiB)\n", 162 ixp_data->nr_banks, ((u32)window_size >> 20)); 163 164 if (plat->width != 1) { 165 dev_err(&dev->dev, "IXP2000 MTD map only supports 8-bit mode, asking for %d\n", 166 plat->width * 8); 167 return -EIO; 168 } 169 170 info = kmalloc(sizeof(struct ixp2000_flash_info), GFP_KERNEL); 171 if(!info) { 172 err = -ENOMEM; 173 goto Error; 174 } 175 memzero(info, sizeof(struct ixp2000_flash_info)); 176 177 platform_set_drvdata(dev, info); 178 179 /* 180 * Tell the MTD layer we're not 1:1 mapped so that it does 181 * not attempt to do a direct access on us. 182 */ 183 info->map.phys = NO_XIP; 184 185 info->map.size = ixp_data->nr_banks * window_size; 186 info->map.bankwidth = 1; 187 188 /* 189 * map_priv_2 is used to store a ptr to to the bank_setup routine 190 */ 191 info->map.map_priv_2 = (unsigned long) ixp_data->bank_setup; 192 193 info->map.name = dev->dev.bus_id; 194 info->map.read = ixp2000_flash_read8; 195 info->map.write = ixp2000_flash_write8; 196 info->map.copy_from = ixp2000_flash_copy_from; 197 info->map.copy_to = ixp2000_flash_copy_to; 198 199 info->res = request_mem_region(dev->resource->start, 200 dev->resource->end - dev->resource->start + 1, 201 dev->dev.bus_id); 202 if (!info->res) { 203 dev_err(&dev->dev, "Could not reserve memory region\n"); 204 err = -ENOMEM; 205 goto Error; 206 } 207 208 info->map.map_priv_1 = (unsigned long) ioremap(dev->resource->start, 209 dev->resource->end - dev->resource->start + 1); 210 if (!info->map.map_priv_1) { 211 dev_err(&dev->dev, "Failed to ioremap flash region\n"); 212 err = -EIO; 213 goto Error; 214 } 215 216#if defined(__ARMEB__) 217 218 erratum44_workaround = ixp2000_has_broken_slowport(); 219 dev_info(&dev->dev, "Erratum 44 workaround %s\n", 220 erratum44_workaround ? "enabled" : "disabled"); 221#endif 222 223 info->mtd = do_map_probe(plat->map_name, &info->map); 224 if (!info->mtd) { 225 dev_err(&dev->dev, "map_probe failed\n"); 226 err = -ENXIO; 227 goto Error; 228 } 229 info->mtd->owner = THIS_MODULE; 230 231 err = parse_mtd_partitions(info->mtd, probes, &info->partitions, 0); 232 if (err > 0) { 233 err = add_mtd_partitions(info->mtd, info->partitions, err); 234 if(err) 235 dev_err(&dev->dev, "Could not parse partitions\n"); 236 } 237 238 if (err) 239 goto Error; 240 241 return 0; 242 243Error: 244 ixp2000_flash_remove(dev); 245 return err; 246} 247 248static struct platform_driver ixp2000_flash_driver = { 249 .probe = ixp2000_flash_probe, 250 .remove = ixp2000_flash_remove, 251 .driver = { 252 .name = "IXP2000-Flash", 253 }, 254}; 255 256static int __init ixp2000_flash_init(void) 257{ 258 return platform_driver_register(&ixp2000_flash_driver); 259} 260 261static void __exit ixp2000_flash_exit(void) 262{ 263 platform_driver_unregister(&ixp2000_flash_driver); 264} 265 266module_init(ixp2000_flash_init); 267module_exit(ixp2000_flash_exit); 268MODULE_LICENSE("GPL"); 269MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net>"); 270