Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/firmware/arm/scmi.c
| Show First 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | |||||
| #include <dev/fdt/fdt_common.h> | #include <dev/fdt/fdt_common.h> | ||||
| #include <dev/ofw/ofw_bus_subr.h> | #include <dev/ofw/ofw_bus_subr.h> | ||||
| #include "dev/mailbox/arm/arm_doorbell.h" | #include "dev/mailbox/arm/arm_doorbell.h" | ||||
| #include "scmi.h" | #include "scmi.h" | ||||
| #include "scmi_protocols.h" | #include "scmi_protocols.h" | ||||
| struct scmi_softc { | |||||
| struct simplebus_softc simplebus_sc; | |||||
| device_t dev; | |||||
| device_t tx_shmem; | |||||
| struct arm_doorbell *db; | |||||
| struct mtx mtx; | |||||
| int req_done; | |||||
| }; | |||||
| static device_t | static device_t | ||||
| scmi_get_shmem(struct scmi_softc *sc, int index) | scmi_get_shmem(struct scmi_softc *sc, int index) | ||||
| { | { | ||||
| phandle_t *shmems; | phandle_t *shmems; | ||||
| phandle_t node; | phandle_t node; | ||||
| device_t dev; | device_t dev; | ||||
| size_t len; | size_t len; | ||||
| Show All 18 Lines | if (dev == NULL) | ||||
| device_printf(sc->dev, "%s: Can't get shmem device.\n", | device_printf(sc->dev, "%s: Can't get shmem device.\n", | ||||
| __func__); | __func__); | ||||
| OF_prop_free(shmems); | OF_prop_free(shmems); | ||||
| return (dev); | return (dev); | ||||
| } | } | ||||
| static void | |||||
| scmi_callback(void *arg) | |||||
| { | |||||
| struct scmi_softc *sc; | |||||
| sc = arg; | |||||
| dprintf("%s sc %p\n", __func__, sc); | |||||
| SCMI_LOCK(sc); | |||||
| sc->req_done = 1; | |||||
| wakeup(sc); | |||||
| SCMI_UNLOCK(sc); | |||||
| } | |||||
| static int | static int | ||||
| scmi_request_locked(struct scmi_softc *sc, struct scmi_req *req) | scmi_request_locked(struct scmi_softc *sc, struct scmi_req *req) | ||||
| { | { | ||||
| struct scmi_smt_header hdr; | struct scmi_smt_header hdr; | ||||
| int timeout; | int ret; | ||||
| bzero(&hdr, sizeof(struct scmi_smt_header)); | bzero(&hdr, sizeof(struct scmi_smt_header)); | ||||
| SCMI_ASSERT_LOCKED(sc); | SCMI_ASSERT_LOCKED(sc); | ||||
| /* Read header */ | /* Read header */ | ||||
| scmi_shmem_read(sc->tx_shmem, 0, &hdr, SMT_HEADER_SIZE); | scmi_shmem_read(sc->tx_shmem, 0, &hdr, SMT_HEADER_SIZE); | ||||
| if ((hdr.channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE) == 0) | if ((hdr.channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE) == 0) | ||||
| return (1); | return (1); | ||||
| /* Update header */ | /* Update header */ | ||||
| hdr.channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE; | hdr.channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE; | ||||
| hdr.msg_header = req->protocol_id << SMT_HEADER_PROTOCOL_ID_S; | hdr.msg_header = req->protocol_id << SMT_HEADER_PROTOCOL_ID_S; | ||||
| hdr.msg_header |= req->message_id << SMT_HEADER_MESSAGE_ID_S; | hdr.msg_header |= req->message_id << SMT_HEADER_MESSAGE_ID_S; | ||||
| /* TODO: Allocate a token */ | |||||
| hdr.length = sizeof(hdr.msg_header) + req->in_size; | hdr.length = sizeof(hdr.msg_header) + req->in_size; | ||||
| hdr.flags |= SCMI_SHMEM_FLAG_INTR_ENABLED; | hdr.flags |= SCMI_SHMEM_FLAG_INTR_ENABLED; | ||||
| /* Write header */ | /* Write header */ | ||||
| scmi_shmem_write(sc->tx_shmem, 0, &hdr, SMT_HEADER_SIZE); | scmi_shmem_write(sc->tx_shmem, 0, &hdr, SMT_HEADER_SIZE); | ||||
| /* Write request */ | /* Write request */ | ||||
| scmi_shmem_write(sc->tx_shmem, SMT_HEADER_SIZE, req->in_buf, | scmi_shmem_write(sc->tx_shmem, SMT_HEADER_SIZE, req->in_buf, | ||||
| req->in_size); | req->in_size); | ||||
| sc->req_done = 0; | ret = SCMI_XFER_MSG(sc->dev); | ||||
| if (ret != 0) | |||||
| return (ret); | |||||
| /* Interrupt SCP firmware. */ | |||||
| arm_doorbell_set(sc->db); | |||||
| timeout = 200; | |||||
| dprintf("%s: request\n", __func__); | |||||
| do { | |||||
| if (cold) { | |||||
| if (arm_doorbell_get(sc->db)) | |||||
| break; | |||||
| DELAY(10000); | |||||
| } else { | |||||
| msleep(sc, &sc->mtx, 0, "scmi", hz / 10); | |||||
| if (sc->req_done) | |||||
| break; | |||||
| } | |||||
| } while (timeout--); | |||||
| if (timeout <= 0) | |||||
| return (-1); | |||||
| dprintf("%s: got reply, timeout %d\n", __func__, timeout); | |||||
| /* Read header. */ | /* Read header. */ | ||||
| scmi_shmem_read(sc->tx_shmem, 0, &hdr, SMT_HEADER_SIZE); | scmi_shmem_read(sc->tx_shmem, 0, &hdr, SMT_HEADER_SIZE); | ||||
| /* Read response */ | /* Read response */ | ||||
| scmi_shmem_read(sc->tx_shmem, SMT_HEADER_SIZE, req->out_buf, | scmi_shmem_read(sc->tx_shmem, SMT_HEADER_SIZE, req->out_buf, | ||||
| req->out_size); | req->out_size); | ||||
| return (0); | return (0); | ||||
| Show All 9 Lines | scmi_request(device_t dev, struct scmi_req *req) | ||||
| SCMI_LOCK(sc); | SCMI_LOCK(sc); | ||||
| error = scmi_request_locked(sc, req); | error = scmi_request_locked(sc, req); | ||||
| SCMI_UNLOCK(sc); | SCMI_UNLOCK(sc); | ||||
| return (error); | return (error); | ||||
| } | } | ||||
| static int | int | ||||
| scmi_probe(device_t dev) | |||||
| { | |||||
| if (!ofw_bus_is_compatible(dev, "arm,scmi")) | |||||
| return (ENXIO); | |||||
| if (!ofw_bus_status_okay(dev)) | |||||
| return (ENXIO); | |||||
| device_set_desc(dev, "ARM SCMI interface driver"); | |||||
| return (BUS_PROBE_DEFAULT); | |||||
| } | |||||
| static int | |||||
| scmi_attach(device_t dev) | scmi_attach(device_t dev) | ||||
| { | { | ||||
| struct scmi_softc *sc; | struct scmi_softc *sc; | ||||
| phandle_t node; | phandle_t node; | ||||
| int error; | int error; | ||||
| sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
| sc->dev = dev; | sc->dev = dev; | ||||
| node = ofw_bus_get_node(dev); | node = ofw_bus_get_node(dev); | ||||
| if (node == -1) | if (node == -1) | ||||
| return (ENXIO); | return (ENXIO); | ||||
| sc->tx_shmem = scmi_get_shmem(sc, 0); | sc->tx_shmem = scmi_get_shmem(sc, 0); | ||||
| if (sc->tx_shmem == NULL) { | if (sc->tx_shmem == NULL) { | ||||
| device_printf(dev, "TX shmem dev not found.\n"); | device_printf(dev, "TX shmem dev not found.\n"); | ||||
| return (ENXIO); | return (ENXIO); | ||||
| } | } | ||||
| sc->db = arm_doorbell_ofw_get(sc->dev, "tx"); | |||||
| if (sc->db == NULL) { | |||||
| device_printf(dev, "Doorbell device not found.\n"); | |||||
| return (ENXIO); | |||||
| } | |||||
| mtx_init(&sc->mtx, device_get_nameunit(dev), "SCMI", MTX_DEF); | mtx_init(&sc->mtx, device_get_nameunit(dev), "SCMI", MTX_DEF); | ||||
| arm_doorbell_set_handler(sc->db, scmi_callback, sc); | |||||
| simplebus_init(dev, node); | simplebus_init(dev, node); | ||||
| /* | /* | ||||
| * Allow devices to identify. | * Allow devices to identify. | ||||
| */ | */ | ||||
| bus_generic_probe(dev); | bus_generic_probe(dev); | ||||
| /* | /* | ||||
| Show All 10 Lines | |||||
| static int | static int | ||||
| scmi_detach(device_t dev) | scmi_detach(device_t dev) | ||||
| { | { | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| static device_method_t scmi_methods[] = { | static device_method_t scmi_methods[] = { | ||||
| DEVMETHOD(device_probe, scmi_probe), | |||||
| DEVMETHOD(device_attach, scmi_attach), | DEVMETHOD(device_attach, scmi_attach), | ||||
| DEVMETHOD(device_detach, scmi_detach), | DEVMETHOD(device_detach, scmi_detach), | ||||
| DEVMETHOD_END | DEVMETHOD_END | ||||
| }; | }; | ||||
| DEFINE_CLASS_1(scmi, scmi_driver, scmi_methods, sizeof(struct scmi_softc), | DEFINE_CLASS_1(scmi, scmi_driver, scmi_methods, sizeof(struct scmi_softc), | ||||
| simplebus_driver); | simplebus_driver); | ||||
| DRIVER_MODULE(scmi, simplebus, scmi_driver, 0, 0); | DRIVER_MODULE(scmi, simplebus, scmi_driver, 0, 0); | ||||
| MODULE_VERSION(scmi, 1); | MODULE_VERSION(scmi, 1); | ||||