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 24 static int efx_mtd_erase(struct mtd_info *mtd, struct erase_info *erase) 25 { 26 struct efx_nic *efx = mtd->priv; 27 28 return efx->type->mtd_erase(mtd, erase->addr, erase->len); 29 } 30 31 static void efx_mtd_sync(struct mtd_info *mtd) 32 { 33 struct efx_mtd_partition *part = to_efx_mtd_partition(mtd); 34 struct efx_nic *efx = mtd->priv; 35 int rc; 36 37 rc = efx->type->mtd_sync(mtd); 38 if (rc) 39 pr_err("%s: %s sync failed (%d)\n", 40 part->name, part->dev_type_name, rc); 41 } 42 43 static void efx_mtd_remove_partition(struct efx_mtd_partition *part) 44 { 45 int rc; 46 47 for (;;) { 48 rc = mtd_device_unregister(&part->mtd); 49 if (rc != -EBUSY) 50 break; 51 ssleep(1); 52 } 53 WARN_ON(rc); 54 list_del(&part->node); 55 } 56 57 int efx_mtd_add(struct efx_nic *efx, struct efx_mtd_partition *parts, 58 size_t n_parts, size_t sizeof_part) 59 { 60 struct efx_mtd_partition *part; 61 size_t i; 62 63 for (i = 0; i < n_parts; i++) { 64 part = (struct efx_mtd_partition *)((char *)parts + 65 i * sizeof_part); 66 67 part->mtd.writesize = 1; 68 69 if (!(part->mtd.flags & MTD_NO_ERASE)) 70 part->mtd.flags |= MTD_WRITEABLE; 71 72 part->mtd.owner = THIS_MODULE; 73 part->mtd.priv = efx; 74 part->mtd.name = part->name; 75 part->mtd._erase = efx_mtd_erase; 76 part->mtd._read = efx->type->mtd_read; 77 part->mtd._write = efx->type->mtd_write; 78 part->mtd._sync = efx_mtd_sync; 79 80 efx->type->mtd_rename(part); 81 82 if (mtd_device_register(&part->mtd, NULL, 0)) 83 goto fail; 84 85 /* Add to list in order - efx_mtd_remove() depends on this */ 86 list_add_tail(&part->node, &efx->mtd_list); 87 } 88 89 return 0; 90 91 fail: 92 while (i--) { 93 part = (struct efx_mtd_partition *)((char *)parts + 94 i * sizeof_part); 95 efx_mtd_remove_partition(part); 96 } 97 /* Failure is unlikely here, but probably means we're out of memory */ 98 return -ENOMEM; 99 } 100 101 void efx_mtd_remove(struct efx_nic *efx) 102 { 103 struct efx_mtd_partition *parts, *part, *next; 104 105 WARN_ON(efx_dev_registered(efx)); 106 107 if (list_empty(&efx->mtd_list)) 108 return; 109 110 parts = list_first_entry(&efx->mtd_list, struct efx_mtd_partition, 111 node); 112 113 list_for_each_entry_safe(part, next, &efx->mtd_list, node) 114 efx_mtd_remove_partition(part); 115 116 kfree(parts); 117 } 118 119 void efx_mtd_rename(struct efx_nic *efx) 120 { 121 struct efx_mtd_partition *part; 122 123 ASSERT_RTNL(); 124 125 list_for_each_entry(part, &efx->mtd_list, node) 126 efx->type->mtd_rename(part); 127 } 128