1/**************************************************************************** 2 * Driver for Solarflare network controllers and boards 3 * Copyright 2005-2006 Fen Systems Ltd. 4 * Copyright 2006-2013 Solarflare Communications Inc. 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 as published 8 * by the Free Software Foundation, incorporated herein by reference. 9 */ 10 11#include <linux/module.h> 12#include <linux/mtd/mtd.h> 13#include <linux/slab.h> 14#include <linux/rtnetlink.h> 15 16#include "net_driver.h" 17#include "efx.h" 18 19#define to_efx_mtd_partition(mtd) \ 20 container_of(mtd, struct efx_mtd_partition, mtd) 21 22/* MTD interface */ 23 24static int efx_mtd_erase(struct mtd_info *mtd, struct erase_info *erase) 25{ 26 struct efx_nic *efx = mtd->priv; 27 int rc; 28 29 rc = efx->type->mtd_erase(mtd, erase->addr, erase->len); 30 if (rc == 0) { 31 erase->state = MTD_ERASE_DONE; 32 } else { 33 erase->state = MTD_ERASE_FAILED; 34 erase->fail_addr = MTD_FAIL_ADDR_UNKNOWN; 35 } 36 mtd_erase_callback(erase); 37 return rc; 38} 39 40static void efx_mtd_sync(struct mtd_info *mtd) 41{ 42 struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); 43 struct efx_nic *efx = mtd->priv; 44 int rc; 45 46 rc = efx->type->mtd_sync(mtd); 47 if (rc) 48 pr_err("%s: %s sync failed (%d)\n", 49 part->name, part->dev_type_name, rc); 50} 51 52static void efx_mtd_remove_partition(struct efx_mtd_partition *part) 53{ 54 int rc; 55 56 for (;;) { 57 rc = mtd_device_unregister(&part->mtd); 58 if (rc != -EBUSY) 59 break; 60 ssleep(1); 61 } 62 WARN_ON(rc); 63 list_del(&part->node); 64} 65 66int efx_mtd_add(struct efx_nic *efx, struct efx_mtd_partition *parts, 67 size_t n_parts, size_t sizeof_part) 68{ 69 struct efx_mtd_partition *part; 70 size_t i; 71 72 for (i = 0; i < n_parts; i++) { 73 part = (struct efx_mtd_partition *)((char *)parts + 74 i * sizeof_part); 75 76 part->mtd.writesize = 1; 77 78 part->mtd.owner = THIS_MODULE; 79 part->mtd.priv = efx; 80 part->mtd.name = part->name; 81 part->mtd._erase = efx_mtd_erase; 82 part->mtd._read = efx->type->mtd_read; 83 part->mtd._write = efx->type->mtd_write; 84 part->mtd._sync = efx_mtd_sync; 85 86 efx->type->mtd_rename(part); 87 88 if (mtd_device_register(&part->mtd, NULL, 0)) 89 goto fail; 90 91 /* Add to list in order - efx_mtd_remove() depends on this */ 92 list_add_tail(&part->node, &efx->mtd_list); 93 } 94 95 return 0; 96 97fail: 98 while (i--) { 99 part = (struct efx_mtd_partition *)((char *)parts + 100 i * sizeof_part); 101 efx_mtd_remove_partition(part); 102 } 103 /* Failure is unlikely here, but probably means we're out of memory */ 104 return -ENOMEM; 105} 106 107void efx_mtd_remove(struct efx_nic *efx) 108{ 109 struct efx_mtd_partition *parts, *part, *next; 110 111 WARN_ON(efx_dev_registered(efx)); 112 113 if (list_empty(&efx->mtd_list)) 114 return; 115 116 parts = list_first_entry(&efx->mtd_list, struct efx_mtd_partition, 117 node); 118 119 list_for_each_entry_safe(part, next, &efx->mtd_list, node) 120 efx_mtd_remove_partition(part); 121 122 kfree(parts); 123} 124 125void efx_mtd_rename(struct efx_nic *efx) 126{ 127 struct efx_mtd_partition *part; 128 129 ASSERT_RTNL(); 130 131 list_for_each_entry(part, &efx->mtd_list, node) 132 efx->type->mtd_rename(part); 133} 134