Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/bhnd/cores/chipc/chipc_spi.c
| /*- | /*- | ||||
| * Copyright (c) 2016 Michael Zhilin <mizhka@gmail.com> | * Copyright (c) 2016 Michael Zhilin <mizhka@gmail.com> | ||||
| * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org> | |||||
| * All rights reserved. | * All rights reserved. | ||||
| * | * | ||||
| * Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
| * modification, are permitted provided that the following conditions | * modification, are permitted provided that the following conditions | ||||
| * are met: | * are met: | ||||
| * 1. Redistributions of source code must retain the above copyright | * 1. Redistributions of source code must retain the above copyright | ||||
| * notice, this list of conditions and the following disclaimer, | * notice, this list of conditions and the following disclaimer, | ||||
| * without modification. | * without modification. | ||||
| Show All 25 Lines | |||||
| #include <sys/module.h> | #include <sys/module.h> | ||||
| #include <sys/errno.h> | #include <sys/errno.h> | ||||
| #include <sys/rman.h> | #include <sys/rman.h> | ||||
| #include <sys/bus.h> | #include <sys/bus.h> | ||||
| #include <machine/bus.h> | #include <machine/bus.h> | ||||
| #include <dev/bhnd/bhndvar.h> | #include <dev/bhnd/bhndvar.h> | ||||
| /* | |||||
| * SPI BUS interface | |||||
| */ | |||||
| #include <dev/spibus/spi.h> | #include <dev/spibus/spi.h> | ||||
| #include "bhnd_chipc_if.h" | |||||
| #include "spibus_if.h" | #include "spibus_if.h" | ||||
| #include "chipcreg.h" | #include "chipcreg.h" | ||||
| #include "chipcvar.h" | #include "chipcvar.h" | ||||
| #include "chipc_spi.h" | |||||
| #include "bhnd_chipc_if.h" | |||||
| /* | |||||
| * Flash slicer | |||||
| */ | |||||
| #include "chipc_slicer.h" | #include "chipc_slicer.h" | ||||
| /* | #include "chipc_spi.h" | ||||
| * **************************** PROTOTYPES **************************** | |||||
| */ | |||||
| static void chipc_spi_identify(driver_t *driver, device_t parent); | |||||
| static int chipc_spi_probe(device_t dev); | static int chipc_spi_probe(device_t dev); | ||||
| static int chipc_spi_attach(device_t dev); | static int chipc_spi_attach(device_t dev); | ||||
| static int chipc_spi_detach(device_t dev); | |||||
| static int chipc_spi_transfer(device_t dev, device_t child, | static int chipc_spi_transfer(device_t dev, device_t child, | ||||
| struct spi_command *cmd); | struct spi_command *cmd); | ||||
| static int chipc_spi_txrx(struct chipc_spi_softc *sc, uint8_t in, | static int chipc_spi_txrx(struct chipc_spi_softc *sc, uint8_t in, | ||||
| uint8_t* out); | uint8_t* out); | ||||
| static int chipc_spi_wait(struct chipc_spi_softc *sc); | static int chipc_spi_wait(struct chipc_spi_softc *sc); | ||||
| /* | static int | ||||
| * **************************** IMPLEMENTATION ************************ | chipc_spi_probe(device_t dev) | ||||
| */ | { | ||||
| device_set_desc(dev, "Broadcom ChipCommon SPI"); | |||||
| return (BUS_PROBE_NOWILDCARD); | |||||
| } | |||||
| static void | static int | ||||
| chipc_spi_identify(driver_t *driver, device_t parent) | chipc_spi_attach(device_t dev) | ||||
| { | { | ||||
| struct chipc_caps *caps; | struct chipc_spi_softc *sc; | ||||
| device_t spidev; | struct chipc_caps *ccaps; | ||||
| device_t flash_dev; | |||||
| device_t spibus; | device_t spibus; | ||||
| device_t flash; | const char *flash_name; | ||||
| char* flash_name; | int error; | ||||
| int err; | |||||
| flash_name = NULL; | sc = device_get_softc(dev); | ||||
| if (device_find_child(parent, "spi", -1) != NULL) | /* Allocate SPI controller registers */ | ||||
| return; | sc->sc_rid = 1; | ||||
| sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, | |||||
| caps = BHND_CHIPC_GET_CAPS(parent); | RF_ACTIVE); | ||||
| if (caps == NULL) { | if (sc->sc_res == NULL) { | ||||
| BHND_ERROR_DEV(parent, "can't retrieve ChipCommon capabilities"); | device_printf(dev, "failed to allocate device registers\n"); | ||||
| return; | return (ENXIO); | ||||
| } | } | ||||
| switch (caps->flash_type) { | /* Allocate flash shadow region */ | ||||
| case CHIPC_SFLASH_AT: | sc->sc_flash_rid = 0; | ||||
| flash_name = "at45d"; | sc->sc_flash_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, | ||||
| break; | &sc->sc_flash_rid, RF_ACTIVE); | ||||
| case CHIPC_SFLASH_ST: | if (sc->sc_flash_res == NULL) { | ||||
| flash_name = "mx25l"; | device_printf(dev, "failed to allocate flash region\n"); | ||||
| break; | error = ENXIO; | ||||
| default: | goto failed; | ||||
| return; | |||||
| } | } | ||||
| spidev = BUS_ADD_CHILD(parent, 0, "spi", -1); | /* | ||||
| if (spidev == NULL) { | * Add flash device | ||||
| BHND_ERROR_DEV(parent, "can't add chipc_spi to ChipCommon"); | * | ||||
| return; | * XXX: This should be replaced with a DEVICE_IDENTIFY implementation | ||||
| * in chipc-specific subclasses of the mx25l and at45d drivers. | |||||
| */ | |||||
| if ((spibus = device_add_child(dev, "spibus", -1)) == NULL) { | |||||
| device_printf(dev, "failed to add spibus\n"); | |||||
| error = ENXIO; | |||||
| goto failed; | |||||
| } | } | ||||
| err = device_probe_and_attach(spidev); | /* Let spibus perform full attach before we try to call | ||||
| if (err) { | * BUS_ADD_CHILD() */ | ||||
| BHND_ERROR_DEV(spidev, "failed attach chipc_spi: %d", err); | if ((error = bus_generic_attach(dev))) | ||||
| return; | goto failed; | ||||
| } | |||||
| spibus = device_find_child(spidev, "spibus", -1); | /* Determine flash type and add the flash child */ | ||||
| if (spibus == NULL) { | ccaps = BHND_CHIPC_GET_CAPS(device_get_parent(dev)); | ||||
| BHND_ERROR_DEV(spidev, "can't find spibus under chipc_spi"); | flash_name = chipc_sflash_device_name(ccaps->flash_type); | ||||
| return; | if (flash_name != NULL) { | ||||
| flash_dev = BUS_ADD_CHILD(spibus, 0, flash_name, -1); | |||||
| if (flash_dev == NULL) { | |||||
| device_printf(dev, "failed to add %s\n", flash_name); | |||||
| error = ENXIO; | |||||
| goto failed; | |||||
| } | } | ||||
| flash = BUS_ADD_CHILD(spibus, 0, flash_name, -1); | chipc_register_slicer(ccaps->flash_type); | ||||
| if (flash == NULL) { | |||||
| BHND_ERROR_DEV(spibus, "can't add %s to spibus", flash_name); | if ((error = device_probe_and_attach(flash_dev))) { | ||||
| return; | device_printf(dev, "failed to attach %s: %d\n", | ||||
| flash_name, error); | |||||
| goto failed; | |||||
| } | } | ||||
| } | |||||
| err = device_probe_and_attach(flash); | return (0); | ||||
| if (err) | |||||
| BHND_ERROR_DEV(flash, "failed attach flash %s: %d", flash_name, | |||||
| err); | |||||
| return; | failed: | ||||
| } | device_delete_children(dev); | ||||
| static int | if (sc->sc_res != NULL) | ||||
| chipc_spi_probe(device_t dev) | bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, | ||||
| { | sc->sc_res); | ||||
| device_set_desc(dev, "ChipCommon SPI"); | |||||
| return (BUS_PROBE_DEFAULT); | |||||
| } | |||||
| struct resource_spec spec_mem[] = { | if (sc->sc_flash_res != NULL) | ||||
| {SYS_RES_MEMORY, 0, RF_ACTIVE}, | bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_flash_rid, | ||||
| {SYS_RES_MEMORY, 1, RF_ACTIVE}, | sc->sc_flash_res); | ||||
| { -1, -1, 0 } | |||||
| }; | |||||
| return (error); | |||||
| } | |||||
| static int | static int | ||||
| chipc_spi_attach(device_t dev) | chipc_spi_detach(device_t dev) | ||||
| { | { | ||||
| int err; | |||||
| struct chipc_spi_softc *sc; | struct chipc_spi_softc *sc; | ||||
| struct resource *mem[2]; | int error; | ||||
| sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
| err = bus_alloc_resources(dev, spec_mem, mem); | |||||
| if (err != 0) | |||||
| return (ENXIO); | |||||
| sc->sc_res = mem[0]; | if ((error = bus_generic_detach(dev))) | ||||
| sc->sc_mem_res = mem[1]; | return (error); | ||||
| flash_register_slicer(chipc_slicer_spi); | bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res); | ||||
| device_add_child(dev, "spibus", 0); | bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_flash_rid, | ||||
| return (bus_generic_attach(dev)); | sc->sc_flash_res); | ||||
| return (0); | |||||
| } | } | ||||
| static int | static int | ||||
| chipc_spi_wait(struct chipc_spi_softc *sc) | chipc_spi_wait(struct chipc_spi_softc *sc) | ||||
| { | { | ||||
| int i; | int i; | ||||
| for (i = CHIPC_SPI_MAXTRIES; i > 0; i--) | for (i = CHIPC_SPI_MAXTRIES; i > 0; i--) | ||||
| if (!(SPI_READ(sc, CHIPC_SPI_FLASHCTL) & CHIPC_SPI_FLASHCTL_START)) | if (!(SPI_READ(sc, CHIPC_SPI_FLASHCTL) & CHIPC_SPI_FLASHCTL_START)) | ||||
| break; | break; | ||||
| if (i > 0) | if (i > 0) | ||||
| return (0); | return (0); | ||||
| BHND_DEBUG_DEV(sc->dev, "busy"); | BHND_DEBUG_DEV(sc->sc_dev, "busy"); | ||||
| return (-1); | return (-1); | ||||
| } | } | ||||
| static int | static int | ||||
| chipc_spi_txrx(struct chipc_spi_softc *sc, uint8_t out, uint8_t* in) | chipc_spi_txrx(struct chipc_spi_softc *sc, uint8_t out, uint8_t* in) | ||||
| { | { | ||||
| uint32_t ctl; | uint32_t ctl; | ||||
| ▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | chipc_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) | ||||
| */ | */ | ||||
| SPI_BARRIER_WRITE(sc); | SPI_BARRIER_WRITE(sc); | ||||
| SPI_WRITE(sc, CHIPC_SPI_FLASHCTL, 0); | SPI_WRITE(sc, CHIPC_SPI_FLASHCTL, 0); | ||||
| SPI_BARRIER_WRITE(sc); | SPI_BARRIER_WRITE(sc); | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| /* | |||||
| * **************************** METADATA ************************ | |||||
| */ | |||||
| static device_method_t chipc_spi_methods[] = { | static device_method_t chipc_spi_methods[] = { | ||||
| DEVMETHOD(device_identify, chipc_spi_identify), | |||||
| DEVMETHOD(device_probe, chipc_spi_probe), | DEVMETHOD(device_probe, chipc_spi_probe), | ||||
| DEVMETHOD(device_attach, chipc_spi_attach), | DEVMETHOD(device_attach, chipc_spi_attach), | ||||
| DEVMETHOD(device_detach, chipc_spi_detach), | |||||
| /* SPI */ | /* SPI */ | ||||
| DEVMETHOD(spibus_transfer, chipc_spi_transfer), | DEVMETHOD(spibus_transfer, chipc_spi_transfer), | ||||
| DEVMETHOD_END | DEVMETHOD_END | ||||
| }; | }; | ||||
| static driver_t chipc_spi_driver = { | static driver_t chipc_spi_driver = { | ||||
| "spi", | "spi", | ||||
| chipc_spi_methods, | chipc_spi_methods, | ||||
| sizeof(struct chipc_spi_softc), | sizeof(struct chipc_spi_softc), | ||||
| }; | }; | ||||
| static devclass_t chipc_spi_devclass; | static devclass_t chipc_spi_devclass; | ||||
| DRIVER_MODULE(chipc_spi, bhnd_chipc, chipc_spi_driver, chipc_spi_devclass, | DRIVER_MODULE(chipc_spi, bhnd_chipc, chipc_spi_driver, chipc_spi_devclass, | ||||
| 0, 0); | 0, 0); | ||||