#ifndef __CB_BDK_DEVICE_H__ #define __CB_BDK_DEVICE_H__ /***********************license start*********************************** * Copyright (c) 2003-2017 Cavium Inc. (support@cavium.com). All rights * reserved. * * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * 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. * * * Neither the name of Cavium Inc. nor the names of * its contributors may be used to endorse or promote products * derived from this software without specific prior written * permission. * * This Software, including technical data, may be subject to U.S. export * control laws, including the U.S. Export Administration Act and its * associated regulations, and may be subject to export or import * regulations in other countries. * * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" * AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT * TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, * QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK * ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU. ***********************license end**************************************/ /** * @file * * bdk_device_t represents devices connected using ECAMs. This * are discover by scanning the ECAMs and instantiating devices * for what is found. * * The discovery process for a device is: Scan all ECAMs: * 1) Device found on an ECAM that doesn't have a bdk_device_t * 2) bdk_device_t created, put in * BDK_DEVICE_STATE_NOT_PROBED state * For all devices in state BDK_DEVICE_STATE_NOT_PROBED: * 1) Lookup driver probe() function. If not found, skip * 2) Call probe() 3) Based on probe(), transition to * either BDK_DEVICE_STATE_PROBED or * BDK_DEVICE_STATE_PROBE_FAIL * For all devices in state BDK_DEVICE_STATE_PROBED: * 1) Lookup driver init() function. If not found, skip * 2) Call init() 3) Based on init(), transition to either * BDK_DEVICE_STATE_READY or BDK_DEVICE_STATE_INIT_FAIL * In general all devices should transition to * BDK_DEVICE_STATE_PROBED before any init() functions are * called. This can be used for synchronization. For example, * the FPA should be functional after a probe() so PKI/PKO can * succeed when calling alloc in init(). * * @defgroup device ECAM Attached Devices * @addtogroup device * @{ */ /** * Possible states of a device */ typedef enum { BDK_DEVICE_STATE_NOT_PROBED, /* Device is known and offline. We haven't probed it */ BDK_DEVICE_STATE_PROBE_FAIL, /* Device failed probing and is offline */ BDK_DEVICE_STATE_PROBED, /* Device succeeded probing, about to go online */ BDK_DEVICE_STATE_INIT_FAIL, /* Device init call failed, offline */ BDK_DEVICE_STATE_READY, /* Device init call success, online */ } bdk_device_state_t; /** * The structure of a ECAM BAR entry inside if a device */ typedef struct { uint64_t address; /* Base physical address */ uint32_t size2; /* Size in bytes as 2^size */ uint32_t flags; /* Type flags for the BAR */ } bdk_device_bar_t; /** * Defines the BDK's representation of a ECAM connected device */ typedef struct { char name[16]; /* Name of the device */ bdk_device_bar_t bar[4]; /* Device BARs, first for better alignment */ bdk_device_state_t state : 8; /* Current state of bdk_device_t */ bdk_node_t node : 3; /* Node the device is on */ uint8_t ecam : 5; /* ECAM for the device */ uint8_t bus; /* ECAM bus number (0-255) */ uint8_t dev : 5; /* ECAM device (0-31) */ uint8_t func : 3; /* ECAM deivce function (0-7) */ uint32_t id; /* ECAM device ID */ uint16_t instance; /* Cavium internal instance number */ } bdk_device_t; #define BDK_NO_DEVICE_INSTANCE 0xffffu /** * Defines the main entry points for a device driver. Full * definition is in bdk-device.h */ struct bdk_driver_s; /** * Called to register a new driver with the bdk-device system. Drivers are probed * and initialized as device are found for them. If devices have already been * added before the driver was registered, the driver will be probed and * initialized before this function returns. * * @param driver Driver functions * * @return Zero on success, negative on failure */ extern int bdk_device_add_driver(struct bdk_driver_s *driver); /** * Called by the ECAM code whan a new device is detected in the system * * @param node Node the ECAM is on * @param ecam ECAM the device is on * @param bus Bus number for the device * @param dev Device number * @param func Function number * * @return Zero on success, negative on failure */ extern int bdk_device_add(bdk_node_t node, int ecam, int bus, int dev, int func); /** * Rename a device. Called by driver to give devices friendly names * * @param device Device to rename * @param format Printf style format string */ extern void bdk_device_rename(bdk_device_t *device, const char *format, ...) __attribute__ ((format(printf, 2, 3))); /** * Called by the ECAM code once all devices have been added * * @return Zero on success, negative on failure */ extern int bdk_device_init(void); /** * Lookup a device by ECAM ID and internal instance number. This can be used by * one device to find a handle to an associated device. For example, PKI would * use this function to get a handle to the FPA. * * @param node Node to lookup for * @param id ECAM ID * @param instance Cavium internal instance number * * @return Device pointer, or NULL if the device isn't found */ extern const bdk_device_t *bdk_device_lookup(bdk_node_t node, uint32_t id, int instance); /** * Read from a device BAR * * @param device Device to read from * @param bar Which BAR to read from (0-3) * @param size Size of the read * @param offset Offset into the BAR * * @return Value read */ extern uint64_t bdk_bar_read(const bdk_device_t *device, int bar, int size, uint64_t offset); /** * Write to a device BAR * * @param device Device to write to * @param bar Which BAR to read from (0-3) * @param size Size of the write * @param offset Offset into the BAR * @param value Value to write */ extern void bdk_bar_write(const bdk_device_t *device, int bar, int size, uint64_t offset, uint64_t value); /** * This macro makes it easy to define a variable of the correct * type for a BAR. */ #define BDK_BAR_DEFINE(name, REG) typedef_##REG name /** * This macro makes it easy to define a variable and initialize it * with a BAR. */ #define BDK_BAR_INIT(name, device, REG) typedef_##REG name = {.u = bdk_bar_read(device, device_bar_##REG, sizeof(typedef_##REG), REG)} /** * Macro to read a BAR */ #define BDK_BAR_READ(device, REG) bdk_bar_read(device, device_bar_##REG, sizeof(typedef_##REG), REG) /** * Macro to write a BAR */ #define BDK_BAR_WRITE(device, REG, value) bdk_bar_write(device, device_bar_##REG, sizeof(typedef_##REG), REG, value) /** * Macro to make a read, modify, and write sequence easy. The "code_block" * should be replaced with a C code block or a comma separated list of * "name.s.field = value", without the quotes. */ #define BDK_BAR_MODIFY(name, device, REG, code_block) do { \ uint64_t _tmp_address = REG; \ typedef_##REG name = {.u = bdk_bar_read(device, device_bar_##REG, sizeof(typedef_##REG), _tmp_address)}; \ code_block; \ bdk_bar_write(device, device_bar_##REG, sizeof(typedef_##REG), _tmp_address, name.u); \ } while (0) /** * This macro spins on a field waiting for it to reach a value. It * is common in code to need to wait for a specific field in a * REG to match a specific value. Conceptually this macro * expands to: * * 1) read REG * 2) Check if ("type".s."field" "op" "value") * 3) If #2 isn't true loop to #1 unless too much time has passed. */ #define BDK_BAR_WAIT_FOR_FIELD(device, REG, field, op, value, timeout_usec) \ ({int result; \ do { \ uint64_t done = bdk_clock_get_count(BDK_CLOCK_TIME) + (uint64_t)timeout_usec * \ bdk_clock_get_rate(bdk_numa_local(), BDK_CLOCK_TIME) / 1000000; \ typedef_##REG c; \ uint64_t _tmp_address = REG; \ while (1) \ { \ c.u = bdk_bar_read(device, device_bar_##REG, sizeof(typedef_##REG), _tmp_address); \ if ((c.s.field) op (value)) { \ result = 0; \ break; \ } else if (bdk_clock_get_count(BDK_CLOCK_TIME) > done) { \ result = -1; \ break; \ } \ } \ } while (0); \ result;}) /** @} */ #endif /* !__CB_BDK_DEVICE_H__ */