cobalt-flash.c 3.1 KB
Newer Older
H
Hans Verkuil 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
/*
 *  Cobalt NOR flash functions
 *
 *  Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates.
 *  All rights reserved.
 *
 *  This program is free software; you may redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; version 2 of the License.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 *  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 *  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 *  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 *  SOFTWARE.
 */

#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/cfi.h>
#include <linux/time.h>

#include "cobalt-driver.h"

#define ADRS(offset) (COBALT_BUS_FLASH_BASE + offset)

static struct map_info cobalt_flash_map = {
	.name =		"cobalt-flash",
	.bankwidth =	2,         /* 16 bits */
	.size =		0x4000000, /* 64MB */
	.phys =		0,         /* offset  */
};

static map_word flash_read16(struct map_info *map, unsigned long offset)
{
	struct cobalt *cobalt = map->virt;
	map_word r;

	r.x[0] = cobalt_bus_read32(cobalt, ADRS(offset));
	if (offset & 0x2)
		r.x[0] >>= 16;
	else
		r.x[0] &= 0x0000ffff;

	return r;
}

static void flash_write16(struct map_info *map, const map_word datum,
			  unsigned long offset)
{
	struct cobalt *cobalt = map->virt;
	u16 data = (u16)datum.x[0];

	cobalt_bus_write16(cobalt, ADRS(offset), data);
}

static void flash_copy_from(struct map_info *map, void *to,
			    unsigned long from, ssize_t len)
{
	struct cobalt *cobalt = map->virt;
	u32 src = from;
	u8 *dest = to;
	u32 data;

	while (len) {
		data = cobalt_bus_read32(cobalt, ADRS(src));
		do {
			*dest = data >> (8 * (src & 3));
			src++;
			dest++;
			len--;
		} while (len && (src % 4));
	}
}

static void flash_copy_to(struct map_info *map, unsigned long to,
			  const void *from, ssize_t len)
{
	struct cobalt *cobalt = map->virt;
	const u8 *src = from;
	u32 dest = to;

	cobalt_info("%s: offset 0x%x: length %zu\n", __func__, dest, len);
	while (len) {
		u16 data = 0xffff;

		do {
			data = *src << (8 * (dest & 1));
			src++;
			dest++;
			len--;
		} while (len && (dest % 2));

		cobalt_bus_write16(cobalt, ADRS(dest - 2), data);
	}
}

int cobalt_flash_probe(struct cobalt *cobalt)
{
	struct map_info *map = &cobalt_flash_map;
	struct mtd_info *mtd;

	BUG_ON(!map_bankwidth_supported(map->bankwidth));
	map->virt = cobalt;
	map->read = flash_read16;
	map->write = flash_write16;
	map->copy_from = flash_copy_from;
	map->copy_to = flash_copy_to;

	mtd = do_map_probe("cfi_probe", map);
	cobalt->mtd = mtd;
	if (!mtd) {
		cobalt_err("Probe CFI flash failed!\n");
		return -1;
	}

	mtd->owner = THIS_MODULE;
	mtd->dev.parent = &cobalt->pci_dev->dev;
	mtd_device_register(mtd, NULL, 0);
	return 0;
}

void cobalt_flash_remove(struct cobalt *cobalt)
{
	if (cobalt->mtd) {
		mtd_device_unregister(cobalt->mtd);
		map_destroy(cobalt->mtd);
	}
}