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_ef4_mtd_partition(mtd) \ 20 container_of(mtd, struct ef4_mtd_partition, mtd) 21 22 /* MTD interface */ 23 24 static int ef4_mtd_erase(struct mtd_info *mtd, struct erase_info *erase) 25 { 26 struct ef4_nic *efx = mtd->priv; 27 28 return efx->type->mtd_erase(mtd, erase->addr, erase->len); 29 } 30 31 static void ef4_mtd_sync(struct mtd_info *mtd) 32 { 33 struct ef4_mtd_partition *part = to_ef4_mtd_partition(mtd); 34 struct ef4_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 ef4_mtd_remove_partition(struct ef4_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 ef4_mtd_add(struct ef4_nic *efx, struct ef4_mtd_partition *parts, 58 size_t n_parts, size_t sizeof_part) 59 { 60 struct ef4_mtd_partition *part; 61 size_t i; 62 63 for (i = 0; i < n_parts; i++) { 64 part = (struct ef4_mtd_partition *)((char *)parts + 65 i * sizeof_part); 66 67 part->mtd.writesize = 1; 68 69 part->mtd.owner = THIS_MODULE; 70 part->mtd.priv = efx; 71 part->mtd.name = part->name; 72 part->mtd._erase = ef4_mtd_erase; 73 part->mtd._read = efx->type->mtd_read; 74 part->mtd._write = efx->type->mtd_write; 75 part->mtd._sync = ef4_mtd_sync; 76 77 efx->type->mtd_rename(part); 78 79 if (mtd_device_register(&part->mtd, NULL, 0)) 80 goto fail; 81 82 /* Add to list in order - ef4_mtd_remove() depends on this */ 83 list_add_tail(&part->node, &efx->mtd_list); 84 } 85 86 return 0; 87 88 fail: 89 while (i--) { 90 part = (struct ef4_mtd_partition *)((char *)parts + 91 i * sizeof_part); 92 ef4_mtd_remove_partition(part); 93 } 94 /* Failure is unlikely here, but probably means we're out of memory */ 95 return -ENOMEM; 96 } 97 98 void ef4_mtd_remove(struct ef4_nic *efx) 99 { 100 struct ef4_mtd_partition *parts, *part, *next; 101 102 WARN_ON(ef4_dev_registered(efx)); 103 104 if (list_empty(&efx->mtd_list)) 105 return; 106 107 parts = list_first_entry(&efx->mtd_list, struct ef4_mtd_partition, 108 node); 109 110 list_for_each_entry_safe(part, next, &efx->mtd_list, node) 111 ef4_mtd_remove_partition(part); 112 113 kfree(parts); 114 } 115 116 void ef4_mtd_rename(struct ef4_nic *efx) 117 { 118 struct ef4_mtd_partition *part; 119 120 ASSERT_RTNL(); 121 122 list_for_each_entry(part, &efx->mtd_list, node) 123 efx->type->mtd_rename(part); 124 } 125