18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/****************************************************************************
38c2ecf20Sopenharmony_ci * Driver for Solarflare network controllers and boards
48c2ecf20Sopenharmony_ci * Copyright 2005-2006 Fen Systems Ltd.
58c2ecf20Sopenharmony_ci * Copyright 2006-2013 Solarflare Communications Inc.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "net_driver.h"
148c2ecf20Sopenharmony_ci#include "efx.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define to_ef4_mtd_partition(mtd)				\
178c2ecf20Sopenharmony_ci	container_of(mtd, struct ef4_mtd_partition, mtd)
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/* MTD interface */
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic int ef4_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	struct ef4_nic *efx = mtd->priv;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	return efx->type->mtd_erase(mtd, erase->addr, erase->len);
268c2ecf20Sopenharmony_ci}
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic void ef4_mtd_sync(struct mtd_info *mtd)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	struct ef4_mtd_partition *part = to_ef4_mtd_partition(mtd);
318c2ecf20Sopenharmony_ci	struct ef4_nic *efx = mtd->priv;
328c2ecf20Sopenharmony_ci	int rc;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	rc = efx->type->mtd_sync(mtd);
358c2ecf20Sopenharmony_ci	if (rc)
368c2ecf20Sopenharmony_ci		pr_err("%s: %s sync failed (%d)\n",
378c2ecf20Sopenharmony_ci		       part->name, part->dev_type_name, rc);
388c2ecf20Sopenharmony_ci}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic void ef4_mtd_remove_partition(struct ef4_mtd_partition *part)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	int rc;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	for (;;) {
458c2ecf20Sopenharmony_ci		rc = mtd_device_unregister(&part->mtd);
468c2ecf20Sopenharmony_ci		if (rc != -EBUSY)
478c2ecf20Sopenharmony_ci			break;
488c2ecf20Sopenharmony_ci		ssleep(1);
498c2ecf20Sopenharmony_ci	}
508c2ecf20Sopenharmony_ci	WARN_ON(rc);
518c2ecf20Sopenharmony_ci	list_del(&part->node);
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ciint ef4_mtd_add(struct ef4_nic *efx, struct ef4_mtd_partition *parts,
558c2ecf20Sopenharmony_ci		size_t n_parts, size_t sizeof_part)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	struct ef4_mtd_partition *part;
588c2ecf20Sopenharmony_ci	size_t i;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	for (i = 0; i < n_parts; i++) {
618c2ecf20Sopenharmony_ci		part = (struct ef4_mtd_partition *)((char *)parts +
628c2ecf20Sopenharmony_ci						    i * sizeof_part);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci		part->mtd.writesize = 1;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci		part->mtd.owner = THIS_MODULE;
678c2ecf20Sopenharmony_ci		part->mtd.priv = efx;
688c2ecf20Sopenharmony_ci		part->mtd.name = part->name;
698c2ecf20Sopenharmony_ci		part->mtd._erase = ef4_mtd_erase;
708c2ecf20Sopenharmony_ci		part->mtd._read = efx->type->mtd_read;
718c2ecf20Sopenharmony_ci		part->mtd._write = efx->type->mtd_write;
728c2ecf20Sopenharmony_ci		part->mtd._sync = ef4_mtd_sync;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci		efx->type->mtd_rename(part);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci		if (mtd_device_register(&part->mtd, NULL, 0))
778c2ecf20Sopenharmony_ci			goto fail;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci		/* Add to list in order - ef4_mtd_remove() depends on this */
808c2ecf20Sopenharmony_ci		list_add_tail(&part->node, &efx->mtd_list);
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	return 0;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cifail:
868c2ecf20Sopenharmony_ci	while (i--) {
878c2ecf20Sopenharmony_ci		part = (struct ef4_mtd_partition *)((char *)parts +
888c2ecf20Sopenharmony_ci						    i * sizeof_part);
898c2ecf20Sopenharmony_ci		ef4_mtd_remove_partition(part);
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci	/* Failure is unlikely here, but probably means we're out of memory */
928c2ecf20Sopenharmony_ci	return -ENOMEM;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_civoid ef4_mtd_remove(struct ef4_nic *efx)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	struct ef4_mtd_partition *parts, *part, *next;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	WARN_ON(ef4_dev_registered(efx));
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (list_empty(&efx->mtd_list))
1028c2ecf20Sopenharmony_ci		return;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	parts = list_first_entry(&efx->mtd_list, struct ef4_mtd_partition,
1058c2ecf20Sopenharmony_ci				 node);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	list_for_each_entry_safe(part, next, &efx->mtd_list, node)
1088c2ecf20Sopenharmony_ci		ef4_mtd_remove_partition(part);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	kfree(parts);
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_civoid ef4_mtd_rename(struct ef4_nic *efx)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct ef4_mtd_partition *part;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	ASSERT_RTNL();
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	list_for_each_entry(part, &efx->mtd_list, node)
1208c2ecf20Sopenharmony_ci		efx->type->mtd_rename(part);
1218c2ecf20Sopenharmony_ci}
122