diff options
Diffstat (limited to 'drivers/thunderbolt/tb.h')
-rw-r--r-- | drivers/thunderbolt/tb.h | 251 |
1 files changed, 227 insertions, 24 deletions
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 61d57ba64035..3d9f64676e58 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -7,22 +7,120 @@ #ifndef TB_H_ #define TB_H_ +#include <linux/nvmem-provider.h> #include <linux/pci.h> +#include <linux/uuid.h> #include "tb_regs.h" #include "ctl.h" +#include "dma_port.h" + +/** + * struct tb_switch_nvm - Structure holding switch NVM information + * @major: Major version number of the active NVM portion + * @minor: Minor version number of the active NVM portion + * @id: Identifier used with both NVM portions + * @active: Active portion NVMem device + * @non_active: Non-active portion NVMem device + * @buf: Buffer where the NVM image is stored before it is written to + * the actual NVM flash device + * @buf_data_size: Number of bytes actually consumed by the new NVM + * image + * @authenticating: The switch is authenticating the new NVM + */ +struct tb_switch_nvm { + u8 major; + u8 minor; + int id; + struct nvmem_device *active; + struct nvmem_device *non_active; + void *buf; + size_t buf_data_size; + bool authenticating; +}; + +/** + * enum tb_security_level - Thunderbolt security level + * @TB_SECURITY_NONE: No security, legacy mode + * @TB_SECURITY_USER: User approval required at minimum + * @TB_SECURITY_SECURE: One time saved key required at minimum + * @TB_SECURITY_DPONLY: Only tunnel Display port (and USB) + */ +enum tb_security_level { + TB_SECURITY_NONE, + TB_SECURITY_USER, + TB_SECURITY_SECURE, + TB_SECURITY_DPONLY, +}; + +#define TB_SWITCH_KEY_SIZE 32 +/* Each physical port contains 2 links on modern controllers */ +#define TB_SWITCH_LINKS_PER_PHY_PORT 2 /** * struct tb_switch - a thunderbolt switch + * @dev: Device for the switch + * @config: Switch configuration + * @ports: Ports in this switch + * @dma_port: If the switch has port supporting DMA configuration based + * mailbox this will hold the pointer to that (%NULL + * otherwise). If set it also means the switch has + * upgradeable NVM. + * @tb: Pointer to the domain the switch belongs to + * @uid: Unique ID of the switch + * @uuid: UUID of the switch (or %NULL if not supported) + * @vendor: Vendor ID of the switch + * @device: Device ID of the switch + * @vendor_name: Name of the vendor (or %NULL if not known) + * @device_name: Name of the device (or %NULL if not known) + * @generation: Switch Thunderbolt generation + * @cap_plug_events: Offset to the plug events capability (%0 if not found) + * @is_unplugged: The switch is going away + * @drom: DROM of the switch (%NULL if not found) + * @nvm: Pointer to the NVM if the switch has one (%NULL otherwise) + * @no_nvm_upgrade: Prevent NVM upgrade of this switch + * @safe_mode: The switch is in safe-mode + * @authorized: Whether the switch is authorized by user or policy + * @work: Work used to automatically authorize a switch + * @security_level: Switch supported security level + * @key: Contains the key used to challenge the device or %NULL if not + * supported. Size of the key is %TB_SWITCH_KEY_SIZE. + * @connection_id: Connection ID used with ICM messaging + * @connection_key: Connection key used with ICM messaging + * @link: Root switch link this switch is connected (ICM only) + * @depth: Depth in the chain this switch is connected (ICM only) + * + * When the switch is being added or removed to the domain (other + * switches) you need to have domain lock held. For switch authorization + * internal switch_lock is enough. */ struct tb_switch { + struct device dev; struct tb_regs_switch_header config; struct tb_port *ports; + struct tb_dma_port *dma_port; struct tb *tb; u64 uid; - int cap_plug_events; /* offset, zero if not found */ - bool is_unplugged; /* unplugged, will go away */ + uuid_be *uuid; + u16 vendor; + u16 device; + const char *vendor_name; + const char *device_name; + unsigned int generation; + int cap_plug_events; + bool is_unplugged; u8 *drom; + struct tb_switch_nvm *nvm; + bool no_nvm_upgrade; + bool safe_mode; + unsigned int authorized; + struct work_struct work; + enum tb_security_level security_level; + u8 *key; + u8 connection_id; + u8 connection_key; + u8 link; + u8 depth; }; /** @@ -92,29 +190,71 @@ struct tb_path { int path_length; /* number of hops */ }; +/** + * struct tb_cm_ops - Connection manager specific operations vector + * @driver_ready: Called right after control channel is started. Used by + * ICM to send driver ready message to the firmware. + * @start: Starts the domain + * @stop: Stops the domain + * @suspend_noirq: Connection manager specific suspend_noirq + * @resume_noirq: Connection manager specific resume_noirq + * @suspend: Connection manager specific suspend + * @complete: Connection manager specific complete + * @handle_event: Handle thunderbolt event + * @approve_switch: Approve switch + * @add_switch_key: Add key to switch + * @challenge_switch_key: Challenge switch using key + * @disconnect_pcie_paths: Disconnects PCIe paths before NVM update + */ +struct tb_cm_ops { + int (*driver_ready)(struct tb *tb); + int (*start)(struct tb *tb); + void (*stop)(struct tb *tb); + int (*suspend_noirq)(struct tb *tb); + int (*resume_noirq)(struct tb *tb); + int (*suspend)(struct tb *tb); + void (*complete)(struct tb *tb); + void (*handle_event)(struct tb *tb, enum tb_cfg_pkg_type, + const void *buf, size_t size); + int (*approve_switch)(struct tb *tb, struct tb_switch *sw); + int (*add_switch_key)(struct tb *tb, struct tb_switch *sw); + int (*challenge_switch_key)(struct tb *tb, struct tb_switch *sw, + const u8 *challenge, u8 *response); + int (*disconnect_pcie_paths)(struct tb *tb); +}; /** * struct tb - main thunderbolt bus structure + * @dev: Domain device + * @lock: Big lock. Must be held when accessing any struct + * tb_switch / struct tb_port. + * @nhi: Pointer to the NHI structure + * @ctl: Control channel for this domain + * @wq: Ordered workqueue for all domain specific work + * @root_switch: Root switch of this domain + * @cm_ops: Connection manager specific operations vector + * @index: Linux assigned domain number + * @security_level: Current security level + * @privdata: Private connection manager specific data */ struct tb { - struct mutex lock; /* - * Big lock. Must be held when accessing cfg or - * any struct tb_switch / struct tb_port. - */ + struct device dev; + struct mutex lock; struct tb_nhi *nhi; struct tb_ctl *ctl; - struct workqueue_struct *wq; /* ordered workqueue for plug events */ + struct workqueue_struct *wq; struct tb_switch *root_switch; - struct list_head tunnel_list; /* list of active PCIe tunnels */ - bool hotplug_active; /* - * tb_handle_hotplug will stop progressing plug - * events and exit if this is not set (it needs to - * acquire the lock one more time). Used to drain - * wq after cfg has been paused. - */ - + const struct tb_cm_ops *cm_ops; + int index; + enum tb_security_level security_level; + unsigned long privdata[0]; }; +static inline void *tb_priv(struct tb *tb) +{ + return (void *)tb->privdata; +} + /* helper functions & macros */ /** @@ -137,6 +277,16 @@ static inline u64 tb_route(struct tb_switch *sw) return ((u64) sw->config.route_hi) << 32 | sw->config.route_lo; } +static inline struct tb_port *tb_port_at(u64 route, struct tb_switch *sw) +{ + u8 port; + + port = route >> (sw->config.depth * 8); + if (WARN_ON(port > sw->config.max_port_number)) + return NULL; + return &sw->ports[port]; +} + static inline int tb_sw_read(struct tb_switch *sw, void *buffer, enum tb_cfg_space space, u32 offset, u32 length) { @@ -173,7 +323,7 @@ static inline int tb_port_read(struct tb_port *port, void *buffer, length); } -static inline int tb_port_write(struct tb_port *port, void *buffer, +static inline int tb_port_write(struct tb_port *port, const void *buffer, enum tb_cfg_space space, u32 offset, u32 length) { return tb_cfg_write(port->sw->tb->ctl, @@ -215,25 +365,78 @@ static inline int tb_port_write(struct tb_port *port, void *buffer, #define tb_port_info(port, fmt, arg...) \ __TB_PORT_PRINT(tb_info, port, fmt, ##arg) +struct tb *icm_probe(struct tb_nhi *nhi); +struct tb *tb_probe(struct tb_nhi *nhi); + +extern struct bus_type tb_bus_type; +extern struct device_type tb_domain_type; +extern struct device_type tb_switch_type; + +int tb_domain_init(void); +void tb_domain_exit(void); +void tb_switch_exit(void); + +struct tb *tb_domain_alloc(struct tb_nhi *nhi, size_t privsize); +int tb_domain_add(struct tb *tb); +void tb_domain_remove(struct tb *tb); +int tb_domain_suspend_noirq(struct tb *tb); +int tb_domain_resume_noirq(struct tb *tb); +int tb_domain_suspend(struct tb *tb); +void tb_domain_complete(struct tb *tb); +int tb_domain_approve_switch(struct tb *tb, struct tb_switch *sw); +int tb_domain_approve_switch_key(struct tb *tb, struct tb_switch *sw); +int tb_domain_challenge_switch_key(struct tb *tb, struct tb_switch *sw); +int tb_domain_disconnect_pcie_paths(struct tb *tb); + +static inline void tb_domain_put(struct tb *tb) +{ + put_device(&tb->dev); +} -struct tb *thunderbolt_alloc_and_start(struct tb_nhi *nhi); -void thunderbolt_shutdown_and_free(struct tb *tb); -void thunderbolt_suspend(struct tb *tb); -void thunderbolt_resume(struct tb *tb); - -struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route); -void tb_switch_free(struct tb_switch *sw); +struct tb_switch *tb_switch_alloc(struct tb *tb, struct device *parent, + u64 route); +struct tb_switch *tb_switch_alloc_safe_mode(struct tb *tb, + struct device *parent, u64 route); +int tb_switch_configure(struct tb_switch *sw); +int tb_switch_add(struct tb_switch *sw); +void tb_switch_remove(struct tb_switch *sw); void tb_switch_suspend(struct tb_switch *sw); int tb_switch_resume(struct tb_switch *sw); int tb_switch_reset(struct tb *tb, u64 route); void tb_sw_set_unplugged(struct tb_switch *sw); struct tb_switch *get_switch_at_route(struct tb_switch *sw, u64 route); +struct tb_switch *tb_switch_find_by_link_depth(struct tb *tb, u8 link, + u8 depth); +struct tb_switch *tb_switch_find_by_uuid(struct tb *tb, const uuid_be *uuid); + +static inline unsigned int tb_switch_phy_port_from_link(unsigned int link) +{ + return (link - 1) / TB_SWITCH_LINKS_PER_PHY_PORT; +} + +static inline void tb_switch_put(struct tb_switch *sw) +{ + put_device(&sw->dev); +} + +static inline bool tb_is_switch(const struct device *dev) +{ + return dev->type == &tb_switch_type; +} + +static inline struct tb_switch *tb_to_switch(struct device *dev) +{ + if (tb_is_switch(dev)) + return container_of(dev, struct tb_switch, dev); + return NULL; +} int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged); int tb_port_add_nfc_credits(struct tb_port *port, int credits); int tb_port_clear_counter(struct tb_port *port, int counter); -int tb_find_cap(struct tb_port *port, enum tb_cfg_space space, enum tb_cap cap); +int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec); +int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap); struct tb_path *tb_path_alloc(struct tb *tb, int num_hops); void tb_path_free(struct tb_path *path); |