Commit 3e136768 authored by Mika Westerberg's avatar Mika Westerberg Committed by Greg Kroah-Hartman

thunderbolt: Add support for DMA configuration based mailbox

The DMA (NHI) port of a switch provides access to the NVM of the host
controller (and devices starting from Intel Alpine Ridge). The NVM
contains also more complete DROM for the root switch including vendor
and device identification strings.

This will look for the DMA port capability for each switch and if found
populates sw->dma_port. We then teach tb_drom_read() to read the DROM
information from NVM if available for the root switch.

The DMA port capability also supports upgrading the NVM for both host
controller and devices which will be added in subsequent patches.

This code is based on the work done by Amir Levy and Michael Jamet.
Signed-off-by: default avatarMichael Jamet <michael.jamet@intel.com>
Signed-off-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: default avatarYehezkel Bernat <yehezkel.bernat@intel.com>
Reviewed-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: default avatarAndreas Noever <andreas.noever@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 2c3c4197
obj-${CONFIG_THUNDERBOLT} := thunderbolt.o
thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel_pci.o eeprom.o
thunderbolt-objs += domain.o
thunderbolt-objs += domain.o dma_port.o
This diff is collapsed.
/*
* Thunderbolt DMA configuration based mailbox support
*
* Copyright (C) 2017, Intel Corporation
* Authors: Michael Jamet <michael.jamet@intel.com>
* Mika Westerberg <mika.westerberg@linux.intel.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef DMA_PORT_H_
#define DMA_PORT_H_
#include "tb.h"
struct tb_switch;
struct tb_dma_port;
#define DMA_PORT_CSS_ADDRESS 0x3fffff
#define DMA_PORT_CSS_MAX_SIZE SZ_128
struct tb_dma_port *dma_port_alloc(struct tb_switch *sw);
void dma_port_free(struct tb_dma_port *dma);
int dma_port_flash_read(struct tb_dma_port *dma, unsigned int address,
void *buf, size_t size);
int dma_port_flash_update_auth(struct tb_dma_port *dma);
int dma_port_flash_update_auth_status(struct tb_dma_port *dma, u32 *status);
int dma_port_flash_write(struct tb_dma_port *dma, unsigned int address,
const void *buf, size_t size);
int dma_port_power_cycle(struct tb_dma_port *dma);
#endif
......@@ -429,6 +429,50 @@ static int tb_drom_copy_efi(struct tb_switch *sw, u16 *size)
return -EINVAL;
}
static int tb_drom_copy_nvm(struct tb_switch *sw, u16 *size)
{
u32 drom_offset;
int ret;
if (!sw->dma_port)
return -ENODEV;
ret = tb_sw_read(sw, &drom_offset, TB_CFG_SWITCH,
sw->cap_plug_events + 12, 1);
if (ret)
return ret;
if (!drom_offset)
return -ENODEV;
ret = dma_port_flash_read(sw->dma_port, drom_offset + 14, size,
sizeof(*size));
if (ret)
return ret;
/* Size includes CRC8 + UID + CRC32 */
*size += 1 + 8 + 4;
sw->drom = kzalloc(*size, GFP_KERNEL);
if (!sw->drom)
return -ENOMEM;
ret = dma_port_flash_read(sw->dma_port, drom_offset, sw->drom, *size);
if (ret)
goto err_free;
/*
* Read UID from the minimal DROM because the one in NVM is just
* a placeholder.
*/
tb_drom_read_uid_only(sw, &sw->uid);
return 0;
err_free:
kfree(sw->drom);
sw->drom = NULL;
return ret;
}
/**
* tb_drom_read - copy drom to sw->drom and parse it
*/
......@@ -450,6 +494,10 @@ int tb_drom_read(struct tb_switch *sw)
if (tb_drom_copy_efi(sw, &size) == 0)
goto parse;
/* Non-Apple hardware has the DROM as part of NVM */
if (tb_drom_copy_nvm(sw, &size) == 0)
goto parse;
/*
* The root switch contains only a dummy drom (header only,
* no entries). Hardcode the configuration here.
......@@ -510,7 +558,8 @@ int tb_drom_read(struct tb_switch *sw)
header->uid_crc8, crc);
goto err;
}
sw->uid = header->uid;
if (!sw->uid)
sw->uid = header->uid;
sw->vendor = header->vendor_id;
sw->device = header->model_id;
......
......@@ -377,6 +377,8 @@ static void tb_switch_release(struct device *dev)
{
struct tb_switch *sw = tb_to_switch(dev);
dma_port_free(sw->dma_port);
kfree(sw->uuid);
kfree(sw->device_name);
kfree(sw->vendor_name);
......@@ -570,6 +572,25 @@ static void tb_switch_set_uuid(struct tb_switch *sw)
sw->uuid = kmemdup(uuid, sizeof(uuid), GFP_KERNEL);
}
static void tb_switch_add_dma_port(struct tb_switch *sw)
{
switch (sw->generation) {
case 3:
break;
case 2:
/* Only root switch can be upgraded */
if (tb_route(sw))
return;
break;
default:
return;
}
sw->dma_port = dma_port_alloc(sw);
}
/**
* tb_switch_add() - Add a switch to the domain
* @sw: Switch to add
......@@ -586,6 +607,15 @@ int tb_switch_add(struct tb_switch *sw)
{
int i, ret;
/*
* Initialize DMA control port now before we read DROM. Recent
* host controllers have more complete DROM on NVM that includes
* vendor and model identification strings which we then expose
* to the userspace. NVM can be accessed through DMA
* configuration based mailbox.
*/
tb_switch_add_dma_port(sw);
/* read drom */
ret = tb_drom_read(sw);
if (ret) {
......
......@@ -12,12 +12,16 @@
#include "tb_regs.h"
#include "ctl.h"
#include "dma_port.h"
/**
* 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).
* @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)
......@@ -34,6 +38,7 @@ 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;
uuid_be *uuid;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment