18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Cobalt NOR flash functions
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright 2012-2015 Cisco Systems, Inc. and/or its affiliates.
68c2ecf20Sopenharmony_ci *  All rights reserved.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h>
108c2ecf20Sopenharmony_ci#include <linux/mtd/map.h>
118c2ecf20Sopenharmony_ci#include <linux/mtd/cfi.h>
128c2ecf20Sopenharmony_ci#include <linux/time.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "cobalt-flash.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define ADRS(offset) (COBALT_BUS_FLASH_BASE + offset)
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic struct map_info cobalt_flash_map = {
198c2ecf20Sopenharmony_ci	.name =		"cobalt-flash",
208c2ecf20Sopenharmony_ci	.bankwidth =	2,         /* 16 bits */
218c2ecf20Sopenharmony_ci	.size =		0x4000000, /* 64MB */
228c2ecf20Sopenharmony_ci	.phys =		0,         /* offset  */
238c2ecf20Sopenharmony_ci};
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic map_word flash_read16(struct map_info *map, unsigned long offset)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	map_word r;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	r.x[0] = cobalt_bus_read32(map->virt, ADRS(offset));
308c2ecf20Sopenharmony_ci	if (offset & 0x2)
318c2ecf20Sopenharmony_ci		r.x[0] >>= 16;
328c2ecf20Sopenharmony_ci	else
338c2ecf20Sopenharmony_ci		r.x[0] &= 0x0000ffff;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	return r;
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic void flash_write16(struct map_info *map, const map_word datum,
398c2ecf20Sopenharmony_ci			  unsigned long offset)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	u16 data = (u16)datum.x[0];
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	cobalt_bus_write16(map->virt, ADRS(offset), data);
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic void flash_copy_from(struct map_info *map, void *to,
478c2ecf20Sopenharmony_ci			    unsigned long from, ssize_t len)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	u32 src = from;
508c2ecf20Sopenharmony_ci	u8 *dest = to;
518c2ecf20Sopenharmony_ci	u32 data;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	while (len) {
548c2ecf20Sopenharmony_ci		data = cobalt_bus_read32(map->virt, ADRS(src));
558c2ecf20Sopenharmony_ci		do {
568c2ecf20Sopenharmony_ci			*dest = data >> (8 * (src & 3));
578c2ecf20Sopenharmony_ci			src++;
588c2ecf20Sopenharmony_ci			dest++;
598c2ecf20Sopenharmony_ci			len--;
608c2ecf20Sopenharmony_ci		} while (len && (src % 4));
618c2ecf20Sopenharmony_ci	}
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic void flash_copy_to(struct map_info *map, unsigned long to,
658c2ecf20Sopenharmony_ci			  const void *from, ssize_t len)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	const u8 *src = from;
688c2ecf20Sopenharmony_ci	u32 dest = to;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	pr_info("%s: offset 0x%x: length %zu\n", __func__, dest, len);
718c2ecf20Sopenharmony_ci	while (len) {
728c2ecf20Sopenharmony_ci		u16 data;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci		do {
758c2ecf20Sopenharmony_ci			data = *src << (8 * (dest & 1));
768c2ecf20Sopenharmony_ci			src++;
778c2ecf20Sopenharmony_ci			dest++;
788c2ecf20Sopenharmony_ci			len--;
798c2ecf20Sopenharmony_ci		} while (len && (dest % 2));
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci		cobalt_bus_write16(map->virt, ADRS(dest - 2), data);
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ciint cobalt_flash_probe(struct cobalt *cobalt)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	struct map_info *map = &cobalt_flash_map;
888c2ecf20Sopenharmony_ci	struct mtd_info *mtd;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	BUG_ON(!map_bankwidth_supported(map->bankwidth));
918c2ecf20Sopenharmony_ci	map->virt = cobalt->bar1;
928c2ecf20Sopenharmony_ci	map->read = flash_read16;
938c2ecf20Sopenharmony_ci	map->write = flash_write16;
948c2ecf20Sopenharmony_ci	map->copy_from = flash_copy_from;
958c2ecf20Sopenharmony_ci	map->copy_to = flash_copy_to;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	mtd = do_map_probe("cfi_probe", map);
988c2ecf20Sopenharmony_ci	cobalt->mtd = mtd;
998c2ecf20Sopenharmony_ci	if (!mtd) {
1008c2ecf20Sopenharmony_ci		cobalt_err("Probe CFI flash failed!\n");
1018c2ecf20Sopenharmony_ci		return -1;
1028c2ecf20Sopenharmony_ci	}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	mtd->owner = THIS_MODULE;
1058c2ecf20Sopenharmony_ci	mtd->dev.parent = &cobalt->pci_dev->dev;
1068c2ecf20Sopenharmony_ci	mtd_device_register(mtd, NULL, 0);
1078c2ecf20Sopenharmony_ci	return 0;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_civoid cobalt_flash_remove(struct cobalt *cobalt)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	if (cobalt->mtd) {
1138c2ecf20Sopenharmony_ci		mtd_device_unregister(cobalt->mtd);
1148c2ecf20Sopenharmony_ci		map_destroy(cobalt->mtd);
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci}
117