| 1 | From 332b8f6ca7da3197c631928b6bd1e7fdca87e109 Mon Sep 17 00:00:00 2001 |
| 2 | From: Hauke Mehrtens <hauke@hauke-m.de> |
| 3 | Date: Sat, 18 Feb 2012 01:16:35 +0100 |
| 4 | Subject: [PATCH 195/202] bcma: add support for sprom not found on the device. |
| 5 | |
| 6 | On SoCs the sprom is stored in the nvram in a special partition on the |
| 7 | flash chip. The nvram contains the sprom for the main bus, but |
| 8 | sometimes also for a pci devices using bcma. This patch makes it |
| 9 | possible for the arch code to register a function to fetch the needed |
| 10 | sprom from the nvram and provide it to the bcma code. |
| 11 | |
| 12 | Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> |
| 13 | --- |
| 14 | drivers/bcma/sprom.c | 80 ++++++++++++++++++++++++++++++++++++++++---- |
| 15 | include/linux/bcma/bcma.h | 6 +++ |
| 16 | 2 files changed, 78 insertions(+), 8 deletions(-) |
| 17 | |
| 18 | --- a/drivers/bcma/sprom.c |
| 19 | +++ b/drivers/bcma/sprom.c |
| 20 | @@ -16,6 +16,49 @@ |
| 21 | |
| 22 | #define SPOFF(offset) ((offset) / sizeof(u16)) |
| 23 | |
| 24 | +static int(*get_fallback_sprom)(struct bcma_bus *dev, struct ssb_sprom *out); |
| 25 | + |
| 26 | +/** |
| 27 | + * ssb_arch_register_fallback_sprom - Registers a method providing a |
| 28 | + * fallback SPROM if no SPROM is found. |
| 29 | + * |
| 30 | + * @sprom_callback: The callback function. |
| 31 | + * |
| 32 | + * With this function the architecture implementation may register a |
| 33 | + * callback handler which fills the SPROM data structure. The fallback is |
| 34 | + * only used for PCI based SSB devices, where no valid SPROM can be found |
| 35 | + * in the shadow registers. |
| 36 | + * |
| 37 | + * This function is useful for weird architectures that have a half-assed |
| 38 | + * SSB device hardwired to their PCI bus. |
| 39 | + * |
| 40 | + * Note that it does only work with PCI attached SSB devices. PCMCIA |
| 41 | + * devices currently don't use this fallback. |
| 42 | + * Architectures must provide the SPROM for native SSB devices anyway, so |
| 43 | + * the fallback also isn't used for native devices. |
| 44 | + * |
| 45 | + * This function is available for architecture code, only. So it is not |
| 46 | + * exported. |
| 47 | + */ |
| 48 | +int bcma_arch_register_fallback_sprom(int (*sprom_callback)(struct bcma_bus *bus, |
| 49 | + struct ssb_sprom *out)) |
| 50 | +{ |
| 51 | + if (get_fallback_sprom) |
| 52 | + return -EEXIST; |
| 53 | + get_fallback_sprom = sprom_callback; |
| 54 | + |
| 55 | + return 0; |
| 56 | +} |
| 57 | + |
| 58 | +static int bcma_fill_sprom_with_fallback(struct bcma_bus *bus, |
| 59 | + struct ssb_sprom *out) |
| 60 | +{ |
| 61 | + if (!get_fallback_sprom) |
| 62 | + return -ENOENT; |
| 63 | + |
| 64 | + return get_fallback_sprom(bus, out); |
| 65 | +} |
| 66 | + |
| 67 | /************************************************** |
| 68 | * R/W ops. |
| 69 | **************************************************/ |
| 70 | @@ -205,23 +248,44 @@ static void bcma_sprom_extract_r8(struct |
| 71 | SSB_SROM8_FEM_ANTSWLUT) >> SSB_SROM8_FEM_ANTSWLUT_SHIFT; |
| 72 | } |
| 73 | |
| 74 | +static bool bcma_is_sprom_available(struct bcma_bus *bus) |
| 75 | +{ |
| 76 | + u32 sromctrl; |
| 77 | + |
| 78 | + if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM)) |
| 79 | + return false; |
| 80 | + |
| 81 | + if (bus->drv_cc.core->id.rev >= 32) { |
| 82 | + sromctrl = bcma_read32(bus->drv_cc.core, BCMA_CC_SROM_CONTROL); |
| 83 | + if (!(sromctrl & BCMA_CC_SROM_CONTROL_PRESENT)) |
| 84 | + return false; |
| 85 | + } |
| 86 | + return true; |
| 87 | +} |
| 88 | + |
| 89 | int bcma_sprom_get(struct bcma_bus *bus) |
| 90 | { |
| 91 | u16 offset; |
| 92 | u16 *sprom; |
| 93 | - u32 sromctrl; |
| 94 | int err = 0; |
| 95 | |
| 96 | if (!bus->drv_cc.core) |
| 97 | return -EOPNOTSUPP; |
| 98 | |
| 99 | - if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM)) |
| 100 | - return -ENOENT; |
| 101 | - |
| 102 | - if (bus->drv_cc.core->id.rev >= 32) { |
| 103 | - sromctrl = bcma_read32(bus->drv_cc.core, BCMA_CC_SROM_CONTROL); |
| 104 | - if (!(sromctrl & BCMA_CC_SROM_CONTROL_PRESENT)) |
| 105 | - return -ENOENT; |
| 106 | + if (!bcma_is_sprom_available(bus)) { |
| 107 | + /* |
| 108 | + * Maybe there is no SPROM on the device? |
| 109 | + * Now we ask the arch code if there is some sprom |
| 110 | + * available for this device in some other storage |
| 111 | + */ |
| 112 | + err = bcma_fill_sprom_with_fallback(bus, &bus->sprom); |
| 113 | + if (err) { |
| 114 | + pr_warn("Using fallback SPROM failed (err %d)\n", err); |
| 115 | + } else { |
| 116 | + pr_debug("Using SPROM revision %d provided by" |
| 117 | + " platform.\n", bus->sprom.revision); |
| 118 | + return 0; |
| 119 | + } |
| 120 | } |
| 121 | |
| 122 | sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16), |
| 123 | --- a/include/linux/bcma/bcma.h |
| 124 | +++ b/include/linux/bcma/bcma.h |
| 125 | @@ -177,6 +177,12 @@ int __bcma_driver_register(struct bcma_d |
| 126 | |
| 127 | extern void bcma_driver_unregister(struct bcma_driver *drv); |
| 128 | |
| 129 | +/* Set a fallback SPROM. |
| 130 | + * See kdoc at the function definition for complete documentation. */ |
| 131 | +extern int bcma_arch_register_fallback_sprom( |
| 132 | + int (*sprom_callback)(struct bcma_bus *bus, |
| 133 | + struct ssb_sprom *out)); |
| 134 | + |
| 135 | struct bcma_bus { |
| 136 | /* The MMIO area. */ |
| 137 | void __iomem *mmio; |
| 138 | |