162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Cobalt NOR flash functions
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates.
662306a36Sopenharmony_ci *  All rights reserved.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/mtd/mtd.h>
1062306a36Sopenharmony_ci#include <linux/mtd/map.h>
1162306a36Sopenharmony_ci#include <linux/mtd/cfi.h>
1262306a36Sopenharmony_ci#include <linux/time.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "cobalt-flash.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define ADRS(offset) (COBALT_BUS_FLASH_BASE + offset)
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic struct map_info cobalt_flash_map = {
1962306a36Sopenharmony_ci	.name =		"cobalt-flash",
2062306a36Sopenharmony_ci	.bankwidth =	2,         /* 16 bits */
2162306a36Sopenharmony_ci	.size =		0x4000000, /* 64MB */
2262306a36Sopenharmony_ci	.phys =		0,         /* offset  */
2362306a36Sopenharmony_ci};
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic map_word flash_read16(struct map_info *map, unsigned long offset)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	map_word r;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	r.x[0] = cobalt_bus_read32(map->virt, ADRS(offset));
3062306a36Sopenharmony_ci	if (offset & 0x2)
3162306a36Sopenharmony_ci		r.x[0] >>= 16;
3262306a36Sopenharmony_ci	else
3362306a36Sopenharmony_ci		r.x[0] &= 0x0000ffff;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	return r;
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic void flash_write16(struct map_info *map, const map_word datum,
3962306a36Sopenharmony_ci			  unsigned long offset)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	u16 data = (u16)datum.x[0];
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	cobalt_bus_write16(map->virt, ADRS(offset), data);
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic void flash_copy_from(struct map_info *map, void *to,
4762306a36Sopenharmony_ci			    unsigned long from, ssize_t len)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	u32 src = from;
5062306a36Sopenharmony_ci	u8 *dest = to;
5162306a36Sopenharmony_ci	u32 data;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	while (len) {
5462306a36Sopenharmony_ci		data = cobalt_bus_read32(map->virt, ADRS(src));
5562306a36Sopenharmony_ci		do {
5662306a36Sopenharmony_ci			*dest = data >> (8 * (src & 3));
5762306a36Sopenharmony_ci			src++;
5862306a36Sopenharmony_ci			dest++;
5962306a36Sopenharmony_ci			len--;
6062306a36Sopenharmony_ci		} while (len && (src % 4));
6162306a36Sopenharmony_ci	}
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic void flash_copy_to(struct map_info *map, unsigned long to,
6562306a36Sopenharmony_ci			  const void *from, ssize_t len)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	const u8 *src = from;
6862306a36Sopenharmony_ci	u32 dest = to;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	pr_info("%s: offset 0x%x: length %zu\n", __func__, dest, len);
7162306a36Sopenharmony_ci	while (len) {
7262306a36Sopenharmony_ci		u16 data;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci		do {
7562306a36Sopenharmony_ci			data = *src << (8 * (dest & 1));
7662306a36Sopenharmony_ci			src++;
7762306a36Sopenharmony_ci			dest++;
7862306a36Sopenharmony_ci			len--;
7962306a36Sopenharmony_ci		} while (len && (dest % 2));
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci		cobalt_bus_write16(map->virt, ADRS(dest - 2), data);
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ciint cobalt_flash_probe(struct cobalt *cobalt)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	struct map_info *map = &cobalt_flash_map;
8862306a36Sopenharmony_ci	struct mtd_info *mtd;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	BUG_ON(!map_bankwidth_supported(map->bankwidth));
9162306a36Sopenharmony_ci	map->virt = cobalt->bar1;
9262306a36Sopenharmony_ci	map->read = flash_read16;
9362306a36Sopenharmony_ci	map->write = flash_write16;
9462306a36Sopenharmony_ci	map->copy_from = flash_copy_from;
9562306a36Sopenharmony_ci	map->copy_to = flash_copy_to;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	mtd = do_map_probe("cfi_probe", map);
9862306a36Sopenharmony_ci	cobalt->mtd = mtd;
9962306a36Sopenharmony_ci	if (!mtd) {
10062306a36Sopenharmony_ci		cobalt_err("Probe CFI flash failed!\n");
10162306a36Sopenharmony_ci		return -1;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	mtd->owner = THIS_MODULE;
10562306a36Sopenharmony_ci	mtd->dev.parent = &cobalt->pci_dev->dev;
10662306a36Sopenharmony_ci	mtd_device_register(mtd, NULL, 0);
10762306a36Sopenharmony_ci	return 0;
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_civoid cobalt_flash_remove(struct cobalt *cobalt)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	if (cobalt->mtd) {
11362306a36Sopenharmony_ci		mtd_device_unregister(cobalt->mtd);
11462306a36Sopenharmony_ci		map_destroy(cobalt->mtd);
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci}
117