162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/****************************************************************************
362306a36Sopenharmony_ci * Driver for Solarflare network controllers and boards
462306a36Sopenharmony_ci * Copyright 2005-2006 Fen Systems Ltd.
562306a36Sopenharmony_ci * Copyright 2006-2013 Solarflare Communications Inc.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/mtd/mtd.h>
1062306a36Sopenharmony_ci#include <linux/slab.h>
1162306a36Sopenharmony_ci#include <linux/rtnetlink.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "net_driver.h"
1462306a36Sopenharmony_ci#include "efx.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define to_ef4_mtd_partition(mtd)				\
1762306a36Sopenharmony_ci	container_of(mtd, struct ef4_mtd_partition, mtd)
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/* MTD interface */
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic int ef4_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	struct ef4_nic *efx = mtd->priv;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	return efx->type->mtd_erase(mtd, erase->addr, erase->len);
2662306a36Sopenharmony_ci}
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic void ef4_mtd_sync(struct mtd_info *mtd)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	struct ef4_mtd_partition *part = to_ef4_mtd_partition(mtd);
3162306a36Sopenharmony_ci	struct ef4_nic *efx = mtd->priv;
3262306a36Sopenharmony_ci	int rc;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	rc = efx->type->mtd_sync(mtd);
3562306a36Sopenharmony_ci	if (rc)
3662306a36Sopenharmony_ci		pr_err("%s: %s sync failed (%d)\n",
3762306a36Sopenharmony_ci		       part->name, part->dev_type_name, rc);
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic void ef4_mtd_remove_partition(struct ef4_mtd_partition *part)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	int rc;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	for (;;) {
4562306a36Sopenharmony_ci		rc = mtd_device_unregister(&part->mtd);
4662306a36Sopenharmony_ci		if (rc != -EBUSY)
4762306a36Sopenharmony_ci			break;
4862306a36Sopenharmony_ci		ssleep(1);
4962306a36Sopenharmony_ci	}
5062306a36Sopenharmony_ci	WARN_ON(rc);
5162306a36Sopenharmony_ci	list_del(&part->node);
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ciint ef4_mtd_add(struct ef4_nic *efx, struct ef4_mtd_partition *parts,
5562306a36Sopenharmony_ci		size_t n_parts, size_t sizeof_part)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	struct ef4_mtd_partition *part;
5862306a36Sopenharmony_ci	size_t i;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	for (i = 0; i < n_parts; i++) {
6162306a36Sopenharmony_ci		part = (struct ef4_mtd_partition *)((char *)parts +
6262306a36Sopenharmony_ci						    i * sizeof_part);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci		part->mtd.writesize = 1;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci		part->mtd.owner = THIS_MODULE;
6762306a36Sopenharmony_ci		part->mtd.priv = efx;
6862306a36Sopenharmony_ci		part->mtd.name = part->name;
6962306a36Sopenharmony_ci		part->mtd._erase = ef4_mtd_erase;
7062306a36Sopenharmony_ci		part->mtd._read = efx->type->mtd_read;
7162306a36Sopenharmony_ci		part->mtd._write = efx->type->mtd_write;
7262306a36Sopenharmony_ci		part->mtd._sync = ef4_mtd_sync;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci		efx->type->mtd_rename(part);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci		if (mtd_device_register(&part->mtd, NULL, 0))
7762306a36Sopenharmony_ci			goto fail;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci		/* Add to list in order - ef4_mtd_remove() depends on this */
8062306a36Sopenharmony_ci		list_add_tail(&part->node, &efx->mtd_list);
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	return 0;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cifail:
8662306a36Sopenharmony_ci	while (i--) {
8762306a36Sopenharmony_ci		part = (struct ef4_mtd_partition *)((char *)parts +
8862306a36Sopenharmony_ci						    i * sizeof_part);
8962306a36Sopenharmony_ci		ef4_mtd_remove_partition(part);
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci	/* Failure is unlikely here, but probably means we're out of memory */
9262306a36Sopenharmony_ci	return -ENOMEM;
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_civoid ef4_mtd_remove(struct ef4_nic *efx)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	struct ef4_mtd_partition *parts, *part, *next;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	WARN_ON(ef4_dev_registered(efx));
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (list_empty(&efx->mtd_list))
10262306a36Sopenharmony_ci		return;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	parts = list_first_entry(&efx->mtd_list, struct ef4_mtd_partition,
10562306a36Sopenharmony_ci				 node);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	list_for_each_entry_safe(part, next, &efx->mtd_list, node)
10862306a36Sopenharmony_ci		ef4_mtd_remove_partition(part);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	kfree(parts);
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_civoid ef4_mtd_rename(struct ef4_nic *efx)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	struct ef4_mtd_partition *part;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	ASSERT_RTNL();
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	list_for_each_entry(part, &efx->mtd_list, node)
12062306a36Sopenharmony_ci		efx->type->mtd_rename(part);
12162306a36Sopenharmony_ci}
122