| #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__ */ |