diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c index ca7752510d5b3a943bb2b5feb28a98ae54708318..916ae255ff641b590d7835adcb94b56d00cf7ebb 100644 --- a/drivers/bcma/sprom.c +++ b/drivers/bcma/sprom.c @@ -2,6 +2,8 @@ * Broadcom specific AMBA * SPROM reading * + * Copyright 2011, 2012, Hauke Mehrtens <hauke@hauke-m.de> + * * Licensed under the GNU/GPL. See COPYING for details. */ @@ -14,6 +16,45 @@ #include <linux/dma-mapping.h> #include <linux/slab.h> +static int(*get_fallback_sprom)(struct bcma_bus *dev, struct ssb_sprom *out); + +/** + * bcma_arch_register_fallback_sprom - Registers a method providing a + * fallback SPROM if no SPROM is found. + * + * @sprom_callback: The callback function. + * + * With this function the architecture implementation may register a + * callback handler which fills the SPROM data structure. The fallback is + * used for PCI based BCMA devices, where no valid SPROM can be found + * in the shadow registers and to provide the SPROM for SoCs where BCMA is + * to controll the system bus. + * + * This function is useful for weird architectures that have a half-assed + * BCMA device hardwired to their PCI bus. + * + * This function is available for architecture code, only. So it is not + * exported. + */ +int bcma_arch_register_fallback_sprom(int (*sprom_callback)(struct bcma_bus *bus, + struct ssb_sprom *out)) +{ + if (get_fallback_sprom) + return -EEXIST; + get_fallback_sprom = sprom_callback; + + return 0; +} + +static int bcma_fill_sprom_with_fallback(struct bcma_bus *bus, + struct ssb_sprom *out) +{ + if (!get_fallback_sprom) + return -ENOENT; + + return get_fallback_sprom(bus, out); +} + /************************************************** * R/W ops. **************************************************/ @@ -246,23 +287,43 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom) SSB_SROM8_FEM_ANTSWLUT_SHIFT); } +static bool bcma_is_sprom_available(struct bcma_bus *bus) +{ + u32 sromctrl; + + if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM)) + return false; + + if (bus->drv_cc.core->id.rev >= 32) { + sromctrl = bcma_read32(bus->drv_cc.core, BCMA_CC_SROM_CONTROL); + return sromctrl & BCMA_CC_SROM_CONTROL_PRESENT; + } + return true; +} + int bcma_sprom_get(struct bcma_bus *bus) { u16 offset; u16 *sprom; - u32 sromctrl; int err = 0; if (!bus->drv_cc.core) return -EOPNOTSUPP; - if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM)) - return -ENOENT; - - if (bus->drv_cc.core->id.rev >= 32) { - sromctrl = bcma_read32(bus->drv_cc.core, BCMA_CC_SROM_CONTROL); - if (!(sromctrl & BCMA_CC_SROM_CONTROL_PRESENT)) - return -ENOENT; + if (!bcma_is_sprom_available(bus)) { + /* + * Maybe there is no SPROM on the device? + * Now we ask the arch code if there is some sprom + * available for this device in some other storage. + */ + err = bcma_fill_sprom_with_fallback(bus, &bus->sprom); + if (err) { + pr_warn("Using fallback SPROM failed (err %d)\n", err); + } else { + pr_debug("Using SPROM revision %d provided by" + " platform.\n", bus->sprom.revision); + return 0; + } } sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16), diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 46bbd088c4ad7b367af3832520ea3f7aa67b8792..5af9a075498f144fa3c6ea6d86744201a65428b7 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -176,6 +176,12 @@ int __bcma_driver_register(struct bcma_driver *drv, struct module *owner); extern void bcma_driver_unregister(struct bcma_driver *drv); +/* Set a fallback SPROM. + * See kdoc at the function definition for complete documentation. */ +extern int bcma_arch_register_fallback_sprom( + int (*sprom_callback)(struct bcma_bus *bus, + struct ssb_sprom *out)); + struct bcma_bus { /* The MMIO area. */ void __iomem *mmio;