57b8f9ca1d
The (eventually) upcoming ath(4) changes will include being able to load ath(4) devices on the AHB bus (ie the on-die wifi part of the SoC) as modules. In order for this to happen, a copy of the calibration data needs to be copied away before the SPI driver runs or the memory map access hack won't work. Now, ideally (!) there'd be some driver that can come up after the MTD pieces (eg, SPI, NAND, etc) and load into a firmware chunk the calibration data. (Or, really really nicely, would be an actual async firmware API that would lead itself to having a driver schedule a file read - or a raw device read - to get to the calibration data.) Now, until all of the above is done - I'm going to perpetuate the layer breaking atrocity here by simply doing the PCI bus fixup EEPROM/calibration data hack here. This will work for any AR71xx (and later on, AR231x/AR531x) device, as well as the handful of QCA MIPS + QCA9880v2 802.11ac boards with NOR flash. To use, this goes into the kernel config: # Enable EEPROM hacks options AR71XX_ATH_EEPROM device ar71xx_caldata device firmware # This enables the ath_ahb driver (when I commit the change!) to # pull data out of the firmware hack. options ATH_EEPROM_FIRMWARE In the hints file: # ART calibration data mapping device hint.ar71xx_caldata.0.at="nexus0" hint.ar71xx_caldata.0.order=0 # Where the ART is - last 64k in the first 8MB of flash hint.ar71xx_caldata.0.map.0.ath_fixup_addr=0x1fff0000 hint.ar71xx_caldata.0.map.0.ath_fixup_size=16384 # And now tell the ath(4) driver where to look! hint.ath.0.eeprom_firmware="ar71xx_caldata.0.map.0.eeprom_firmware" Tested: * carambola2, AR933x SoC, using a set of ath and ath_hal modules to load TODO: * unify this bit of firmware loading code, as I will definitely need to include both the PCI bus firmware version (for PCI ID fixups too!) as well as AHB/on-chip calibration data. * Commit the ath_ahb bus code * Convert .. everything over. That'll take the majority of the time.
177 lines
5.0 KiB
C
177 lines
5.0 KiB
C
/*-
|
|
* Copyright (c) 2017, Adrian Chadd <adrian@FreeBSD.org>.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice unmodified, this list of conditions, and the following
|
|
* disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include "opt_ar71xx.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/bus.h>
|
|
#include <sys/interrupt.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/module.h>
|
|
#include <sys/rman.h>
|
|
#include <sys/lock.h>
|
|
|
|
#include <vm/vm.h>
|
|
#include <vm/pmap.h>
|
|
#include <vm/vm_extern.h>
|
|
|
|
#include <machine/bus.h>
|
|
#include <machine/cpu.h>
|
|
#include <machine/intr_machdep.h>
|
|
|
|
#include <sys/linker.h>
|
|
#include <sys/firmware.h>
|
|
|
|
struct ar71xx_caldata_softc {
|
|
device_t sc_dev;
|
|
};
|
|
|
|
static int
|
|
ar71xx_caldata_probe(device_t dev)
|
|
{
|
|
|
|
return (BUS_PROBE_NOWILDCARD);
|
|
}
|
|
|
|
/* XXX TODO: unify with what's in ar71xx_fixup.c */
|
|
|
|
/*
|
|
* Create a calibration block from memory mapped SPI data for use by
|
|
* various drivers. Right now it's just ath(4) but later board support
|
|
* will include 802.11ac NICs with calibration data in NOR flash.
|
|
*
|
|
* (Yes, there are a handful of QCA MIPS boards with QCA9880v2 802.11ac chips
|
|
* with calibration data in flash..)
|
|
*/
|
|
static void
|
|
ar71xx_platform_create_cal_data(device_t dev, int id, long int flash_addr,
|
|
int size)
|
|
{
|
|
char buf[64];
|
|
uint16_t *cal_data = (uint16_t *) MIPS_PHYS_TO_KSEG1(flash_addr);
|
|
void *eeprom = NULL;
|
|
const struct firmware *fw = NULL;
|
|
|
|
device_printf(dev, "EEPROM firmware: 0x%lx @ %d bytes\n",
|
|
flash_addr, size);
|
|
|
|
eeprom = malloc(size, M_DEVBUF, M_WAITOK | M_ZERO);
|
|
if (! eeprom) {
|
|
device_printf(dev, "%s: malloc failed for '%s', aborting EEPROM\n",
|
|
__func__, buf);
|
|
return;
|
|
}
|
|
|
|
memcpy(eeprom, cal_data, size);
|
|
|
|
/*
|
|
* Generate a flash EEPROM 'firmware' from the given memory
|
|
* region. Since the SPI controller will eventually
|
|
* go into port-IO mode instead of memory-mapped IO
|
|
* mode, a copy of the EEPROM contents is required.
|
|
*/
|
|
|
|
snprintf(buf, sizeof(buf), "%s.%d.map.%d.eeprom_firmware",
|
|
device_get_name(dev),
|
|
device_get_unit(dev),
|
|
id);
|
|
|
|
fw = firmware_register(buf, eeprom, size, 1, NULL);
|
|
if (fw == NULL) {
|
|
device_printf(dev, "%s: firmware_register (%s) failed\n",
|
|
__func__, buf);
|
|
free(eeprom, M_DEVBUF);
|
|
return;
|
|
}
|
|
device_printf(dev, "device EEPROM '%s' registered\n", buf);
|
|
}
|
|
|
|
/*
|
|
* Iterate through a list of early-boot hints creating calibration
|
|
* data firmware chunks for AHB (ie, non-PCI) devices with calibration
|
|
* data.
|
|
*/
|
|
static int
|
|
ar71xx_platform_check_eeprom_hints(device_t dev)
|
|
{
|
|
char buf[64];
|
|
long int addr;
|
|
int size;
|
|
int i;
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
snprintf(buf, sizeof(buf), "map.%d.ath_fixup_addr", i);
|
|
if (resource_long_value(device_get_name(dev),
|
|
device_get_unit(dev), buf, &addr) != 0)
|
|
break;
|
|
snprintf(buf, sizeof(buf), "map.%d.ath_fixup_size", i);
|
|
if (resource_int_value(device_get_name(dev),
|
|
device_get_unit(dev), buf, &size) != 0)
|
|
break;
|
|
device_printf(dev, "map.%d.ath_fixup_addr=0x%08x; size=%d\n",
|
|
i, (int) addr, size);
|
|
(void) ar71xx_platform_create_cal_data(dev, i, addr, size);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
ar71xx_caldata_attach(device_t dev)
|
|
{
|
|
|
|
device_add_child(dev, "nexus", -1);
|
|
ar71xx_platform_check_eeprom_hints(dev);
|
|
return (bus_generic_attach(dev));
|
|
}
|
|
|
|
static device_method_t ar71xx_caldata_methods[] = {
|
|
/* Device interface */
|
|
DEVMETHOD(device_probe, ar71xx_caldata_probe),
|
|
DEVMETHOD(device_attach, ar71xx_caldata_attach),
|
|
DEVMETHOD(device_shutdown, bus_generic_shutdown),
|
|
DEVMETHOD(device_suspend, bus_generic_suspend),
|
|
DEVMETHOD(device_resume, bus_generic_resume),
|
|
DEVMETHOD_END
|
|
};
|
|
|
|
static driver_t ar71xx_caldata_driver = {
|
|
"ar71xx_caldata",
|
|
ar71xx_caldata_methods,
|
|
sizeof(struct ar71xx_caldata_softc),
|
|
};
|
|
|
|
static devclass_t ar71xx_caldata_devclass;
|
|
|
|
DRIVER_MODULE(ar71xx_caldata, nexus, ar71xx_caldata_driver, ar71xx_caldata_devclass, 0, 0);
|