162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Thunderbolt DMA configuration based mailbox support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2017, Intel Corporation 662306a36Sopenharmony_ci * Authors: Michael Jamet <michael.jamet@intel.com> 762306a36Sopenharmony_ci * Mika Westerberg <mika.westerberg@linux.intel.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "dma_port.h" 1462306a36Sopenharmony_ci#include "tb_regs.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define DMA_PORT_CAP 0x3e 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define MAIL_DATA 1 1962306a36Sopenharmony_ci#define MAIL_DATA_DWORDS 16 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define MAIL_IN 17 2262306a36Sopenharmony_ci#define MAIL_IN_CMD_SHIFT 28 2362306a36Sopenharmony_ci#define MAIL_IN_CMD_MASK GENMASK(31, 28) 2462306a36Sopenharmony_ci#define MAIL_IN_CMD_FLASH_WRITE 0x0 2562306a36Sopenharmony_ci#define MAIL_IN_CMD_FLASH_UPDATE_AUTH 0x1 2662306a36Sopenharmony_ci#define MAIL_IN_CMD_FLASH_READ 0x2 2762306a36Sopenharmony_ci#define MAIL_IN_CMD_POWER_CYCLE 0x4 2862306a36Sopenharmony_ci#define MAIL_IN_DWORDS_SHIFT 24 2962306a36Sopenharmony_ci#define MAIL_IN_DWORDS_MASK GENMASK(27, 24) 3062306a36Sopenharmony_ci#define MAIL_IN_ADDRESS_SHIFT 2 3162306a36Sopenharmony_ci#define MAIL_IN_ADDRESS_MASK GENMASK(23, 2) 3262306a36Sopenharmony_ci#define MAIL_IN_CSS BIT(1) 3362306a36Sopenharmony_ci#define MAIL_IN_OP_REQUEST BIT(0) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define MAIL_OUT 18 3662306a36Sopenharmony_ci#define MAIL_OUT_STATUS_RESPONSE BIT(29) 3762306a36Sopenharmony_ci#define MAIL_OUT_STATUS_CMD_SHIFT 4 3862306a36Sopenharmony_ci#define MAIL_OUT_STATUS_CMD_MASK GENMASK(7, 4) 3962306a36Sopenharmony_ci#define MAIL_OUT_STATUS_MASK GENMASK(3, 0) 4062306a36Sopenharmony_ci#define MAIL_OUT_STATUS_COMPLETED 0 4162306a36Sopenharmony_ci#define MAIL_OUT_STATUS_ERR_AUTH 1 4262306a36Sopenharmony_ci#define MAIL_OUT_STATUS_ERR_ACCESS 2 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define DMA_PORT_TIMEOUT 5000 /* ms */ 4562306a36Sopenharmony_ci#define DMA_PORT_RETRIES 3 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/** 4862306a36Sopenharmony_ci * struct tb_dma_port - DMA control port 4962306a36Sopenharmony_ci * @sw: Switch the DMA port belongs to 5062306a36Sopenharmony_ci * @port: Switch port number where DMA capability is found 5162306a36Sopenharmony_ci * @base: Start offset of the mailbox registers 5262306a36Sopenharmony_ci * @buf: Temporary buffer to store a single block 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_cistruct tb_dma_port { 5562306a36Sopenharmony_ci struct tb_switch *sw; 5662306a36Sopenharmony_ci u8 port; 5762306a36Sopenharmony_ci u32 base; 5862306a36Sopenharmony_ci u8 *buf; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* 6262306a36Sopenharmony_ci * When the switch is in safe mode it supports very little functionality 6362306a36Sopenharmony_ci * so we don't validate that much here. 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_cistatic bool dma_port_match(const struct tb_cfg_request *req, 6662306a36Sopenharmony_ci const struct ctl_pkg *pkg) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci u64 route = tb_cfg_get_route(pkg->buffer) & ~BIT_ULL(63); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (pkg->frame.eof == TB_CFG_PKG_ERROR) 7162306a36Sopenharmony_ci return true; 7262306a36Sopenharmony_ci if (pkg->frame.eof != req->response_type) 7362306a36Sopenharmony_ci return false; 7462306a36Sopenharmony_ci if (route != tb_cfg_get_route(req->request)) 7562306a36Sopenharmony_ci return false; 7662306a36Sopenharmony_ci if (pkg->frame.size != req->response_size) 7762306a36Sopenharmony_ci return false; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci return true; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic bool dma_port_copy(struct tb_cfg_request *req, const struct ctl_pkg *pkg) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci memcpy(req->response, pkg->buffer, req->response_size); 8562306a36Sopenharmony_ci return true; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic int dma_port_read(struct tb_ctl *ctl, void *buffer, u64 route, 8962306a36Sopenharmony_ci u32 port, u32 offset, u32 length, int timeout_msec) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct cfg_read_pkg request = { 9262306a36Sopenharmony_ci .header = tb_cfg_make_header(route), 9362306a36Sopenharmony_ci .addr = { 9462306a36Sopenharmony_ci .seq = 1, 9562306a36Sopenharmony_ci .port = port, 9662306a36Sopenharmony_ci .space = TB_CFG_PORT, 9762306a36Sopenharmony_ci .offset = offset, 9862306a36Sopenharmony_ci .length = length, 9962306a36Sopenharmony_ci }, 10062306a36Sopenharmony_ci }; 10162306a36Sopenharmony_ci struct tb_cfg_request *req; 10262306a36Sopenharmony_ci struct cfg_write_pkg reply; 10362306a36Sopenharmony_ci struct tb_cfg_result res; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci req = tb_cfg_request_alloc(); 10662306a36Sopenharmony_ci if (!req) 10762306a36Sopenharmony_ci return -ENOMEM; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci req->match = dma_port_match; 11062306a36Sopenharmony_ci req->copy = dma_port_copy; 11162306a36Sopenharmony_ci req->request = &request; 11262306a36Sopenharmony_ci req->request_size = sizeof(request); 11362306a36Sopenharmony_ci req->request_type = TB_CFG_PKG_READ; 11462306a36Sopenharmony_ci req->response = &reply; 11562306a36Sopenharmony_ci req->response_size = 12 + 4 * length; 11662306a36Sopenharmony_ci req->response_type = TB_CFG_PKG_READ; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci res = tb_cfg_request_sync(ctl, req, timeout_msec); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci tb_cfg_request_put(req); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (res.err) 12362306a36Sopenharmony_ci return res.err; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci memcpy(buffer, &reply.data, 4 * length); 12662306a36Sopenharmony_ci return 0; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic int dma_port_write(struct tb_ctl *ctl, const void *buffer, u64 route, 13062306a36Sopenharmony_ci u32 port, u32 offset, u32 length, int timeout_msec) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct cfg_write_pkg request = { 13362306a36Sopenharmony_ci .header = tb_cfg_make_header(route), 13462306a36Sopenharmony_ci .addr = { 13562306a36Sopenharmony_ci .seq = 1, 13662306a36Sopenharmony_ci .port = port, 13762306a36Sopenharmony_ci .space = TB_CFG_PORT, 13862306a36Sopenharmony_ci .offset = offset, 13962306a36Sopenharmony_ci .length = length, 14062306a36Sopenharmony_ci }, 14162306a36Sopenharmony_ci }; 14262306a36Sopenharmony_ci struct tb_cfg_request *req; 14362306a36Sopenharmony_ci struct cfg_read_pkg reply; 14462306a36Sopenharmony_ci struct tb_cfg_result res; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci memcpy(&request.data, buffer, length * 4); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci req = tb_cfg_request_alloc(); 14962306a36Sopenharmony_ci if (!req) 15062306a36Sopenharmony_ci return -ENOMEM; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci req->match = dma_port_match; 15362306a36Sopenharmony_ci req->copy = dma_port_copy; 15462306a36Sopenharmony_ci req->request = &request; 15562306a36Sopenharmony_ci req->request_size = 12 + 4 * length; 15662306a36Sopenharmony_ci req->request_type = TB_CFG_PKG_WRITE; 15762306a36Sopenharmony_ci req->response = &reply; 15862306a36Sopenharmony_ci req->response_size = sizeof(reply); 15962306a36Sopenharmony_ci req->response_type = TB_CFG_PKG_WRITE; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci res = tb_cfg_request_sync(ctl, req, timeout_msec); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci tb_cfg_request_put(req); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci return res.err; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic int dma_find_port(struct tb_switch *sw) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci static const int ports[] = { 3, 5, 7 }; 17162306a36Sopenharmony_ci int i; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* 17462306a36Sopenharmony_ci * The DMA (NHI) port is either 3, 5 or 7 depending on the 17562306a36Sopenharmony_ci * controller. Try all of them. 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ports); i++) { 17862306a36Sopenharmony_ci u32 type; 17962306a36Sopenharmony_ci int ret; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci ret = dma_port_read(sw->tb->ctl, &type, tb_route(sw), ports[i], 18262306a36Sopenharmony_ci 2, 1, DMA_PORT_TIMEOUT); 18362306a36Sopenharmony_ci if (!ret && (type & 0xffffff) == TB_TYPE_NHI) 18462306a36Sopenharmony_ci return ports[i]; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return -ENODEV; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/** 19162306a36Sopenharmony_ci * dma_port_alloc() - Finds DMA control port from a switch pointed by route 19262306a36Sopenharmony_ci * @sw: Switch from where find the DMA port 19362306a36Sopenharmony_ci * 19462306a36Sopenharmony_ci * Function checks if the switch NHI port supports DMA configuration 19562306a36Sopenharmony_ci * based mailbox capability and if it does, allocates and initializes 19662306a36Sopenharmony_ci * DMA port structure. Returns %NULL if the capabity was not found. 19762306a36Sopenharmony_ci * 19862306a36Sopenharmony_ci * The DMA control port is functional also when the switch is in safe 19962306a36Sopenharmony_ci * mode. 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_cistruct tb_dma_port *dma_port_alloc(struct tb_switch *sw) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci struct tb_dma_port *dma; 20462306a36Sopenharmony_ci int port; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci port = dma_find_port(sw); 20762306a36Sopenharmony_ci if (port < 0) 20862306a36Sopenharmony_ci return NULL; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci dma = kzalloc(sizeof(*dma), GFP_KERNEL); 21162306a36Sopenharmony_ci if (!dma) 21262306a36Sopenharmony_ci return NULL; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci dma->buf = kmalloc_array(MAIL_DATA_DWORDS, sizeof(u32), GFP_KERNEL); 21562306a36Sopenharmony_ci if (!dma->buf) { 21662306a36Sopenharmony_ci kfree(dma); 21762306a36Sopenharmony_ci return NULL; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci dma->sw = sw; 22162306a36Sopenharmony_ci dma->port = port; 22262306a36Sopenharmony_ci dma->base = DMA_PORT_CAP; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci return dma; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci/** 22862306a36Sopenharmony_ci * dma_port_free() - Release DMA control port structure 22962306a36Sopenharmony_ci * @dma: DMA control port 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_civoid dma_port_free(struct tb_dma_port *dma) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci if (dma) { 23462306a36Sopenharmony_ci kfree(dma->buf); 23562306a36Sopenharmony_ci kfree(dma); 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic int dma_port_wait_for_completion(struct tb_dma_port *dma, 24062306a36Sopenharmony_ci unsigned int timeout) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci unsigned long end = jiffies + msecs_to_jiffies(timeout); 24362306a36Sopenharmony_ci struct tb_switch *sw = dma->sw; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci do { 24662306a36Sopenharmony_ci int ret; 24762306a36Sopenharmony_ci u32 in; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci ret = dma_port_read(sw->tb->ctl, &in, tb_route(sw), dma->port, 25062306a36Sopenharmony_ci dma->base + MAIL_IN, 1, 50); 25162306a36Sopenharmony_ci if (ret) { 25262306a36Sopenharmony_ci if (ret != -ETIMEDOUT) 25362306a36Sopenharmony_ci return ret; 25462306a36Sopenharmony_ci } else if (!(in & MAIL_IN_OP_REQUEST)) { 25562306a36Sopenharmony_ci return 0; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci usleep_range(50, 100); 25962306a36Sopenharmony_ci } while (time_before(jiffies, end)); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci return -ETIMEDOUT; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic int status_to_errno(u32 status) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci switch (status & MAIL_OUT_STATUS_MASK) { 26762306a36Sopenharmony_ci case MAIL_OUT_STATUS_COMPLETED: 26862306a36Sopenharmony_ci return 0; 26962306a36Sopenharmony_ci case MAIL_OUT_STATUS_ERR_AUTH: 27062306a36Sopenharmony_ci return -EINVAL; 27162306a36Sopenharmony_ci case MAIL_OUT_STATUS_ERR_ACCESS: 27262306a36Sopenharmony_ci return -EACCES; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci return -EIO; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic int dma_port_request(struct tb_dma_port *dma, u32 in, 27962306a36Sopenharmony_ci unsigned int timeout) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci struct tb_switch *sw = dma->sw; 28262306a36Sopenharmony_ci u32 out; 28362306a36Sopenharmony_ci int ret; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci ret = dma_port_write(sw->tb->ctl, &in, tb_route(sw), dma->port, 28662306a36Sopenharmony_ci dma->base + MAIL_IN, 1, DMA_PORT_TIMEOUT); 28762306a36Sopenharmony_ci if (ret) 28862306a36Sopenharmony_ci return ret; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci ret = dma_port_wait_for_completion(dma, timeout); 29162306a36Sopenharmony_ci if (ret) 29262306a36Sopenharmony_ci return ret; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci ret = dma_port_read(sw->tb->ctl, &out, tb_route(sw), dma->port, 29562306a36Sopenharmony_ci dma->base + MAIL_OUT, 1, DMA_PORT_TIMEOUT); 29662306a36Sopenharmony_ci if (ret) 29762306a36Sopenharmony_ci return ret; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci return status_to_errno(out); 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic int dma_port_flash_read_block(void *data, unsigned int dwaddress, 30362306a36Sopenharmony_ci void *buf, size_t dwords) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci struct tb_dma_port *dma = data; 30662306a36Sopenharmony_ci struct tb_switch *sw = dma->sw; 30762306a36Sopenharmony_ci int ret; 30862306a36Sopenharmony_ci u32 in; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci in = MAIL_IN_CMD_FLASH_READ << MAIL_IN_CMD_SHIFT; 31162306a36Sopenharmony_ci if (dwords < MAIL_DATA_DWORDS) 31262306a36Sopenharmony_ci in |= (dwords << MAIL_IN_DWORDS_SHIFT) & MAIL_IN_DWORDS_MASK; 31362306a36Sopenharmony_ci in |= (dwaddress << MAIL_IN_ADDRESS_SHIFT) & MAIL_IN_ADDRESS_MASK; 31462306a36Sopenharmony_ci in |= MAIL_IN_OP_REQUEST; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci ret = dma_port_request(dma, in, DMA_PORT_TIMEOUT); 31762306a36Sopenharmony_ci if (ret) 31862306a36Sopenharmony_ci return ret; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci return dma_port_read(sw->tb->ctl, buf, tb_route(sw), dma->port, 32162306a36Sopenharmony_ci dma->base + MAIL_DATA, dwords, DMA_PORT_TIMEOUT); 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic int dma_port_flash_write_block(void *data, unsigned int dwaddress, 32562306a36Sopenharmony_ci const void *buf, size_t dwords) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci struct tb_dma_port *dma = data; 32862306a36Sopenharmony_ci struct tb_switch *sw = dma->sw; 32962306a36Sopenharmony_ci int ret; 33062306a36Sopenharmony_ci u32 in; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci /* Write the block to MAIL_DATA registers */ 33362306a36Sopenharmony_ci ret = dma_port_write(sw->tb->ctl, buf, tb_route(sw), dma->port, 33462306a36Sopenharmony_ci dma->base + MAIL_DATA, dwords, DMA_PORT_TIMEOUT); 33562306a36Sopenharmony_ci if (ret) 33662306a36Sopenharmony_ci return ret; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci in = MAIL_IN_CMD_FLASH_WRITE << MAIL_IN_CMD_SHIFT; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* CSS header write is always done to the same magic address */ 34162306a36Sopenharmony_ci if (dwaddress >= DMA_PORT_CSS_ADDRESS) 34262306a36Sopenharmony_ci in |= MAIL_IN_CSS; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci in |= ((dwords - 1) << MAIL_IN_DWORDS_SHIFT) & MAIL_IN_DWORDS_MASK; 34562306a36Sopenharmony_ci in |= (dwaddress << MAIL_IN_ADDRESS_SHIFT) & MAIL_IN_ADDRESS_MASK; 34662306a36Sopenharmony_ci in |= MAIL_IN_OP_REQUEST; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci return dma_port_request(dma, in, DMA_PORT_TIMEOUT); 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci/** 35262306a36Sopenharmony_ci * dma_port_flash_read() - Read from active flash region 35362306a36Sopenharmony_ci * @dma: DMA control port 35462306a36Sopenharmony_ci * @address: Address relative to the start of active region 35562306a36Sopenharmony_ci * @buf: Buffer where the data is read 35662306a36Sopenharmony_ci * @size: Size of the buffer 35762306a36Sopenharmony_ci */ 35862306a36Sopenharmony_ciint dma_port_flash_read(struct tb_dma_port *dma, unsigned int address, 35962306a36Sopenharmony_ci void *buf, size_t size) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci return tb_nvm_read_data(address, buf, size, DMA_PORT_RETRIES, 36262306a36Sopenharmony_ci dma_port_flash_read_block, dma); 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci/** 36662306a36Sopenharmony_ci * dma_port_flash_write() - Write to non-active flash region 36762306a36Sopenharmony_ci * @dma: DMA control port 36862306a36Sopenharmony_ci * @address: Address relative to the start of non-active region 36962306a36Sopenharmony_ci * @buf: Data to write 37062306a36Sopenharmony_ci * @size: Size of the buffer 37162306a36Sopenharmony_ci * 37262306a36Sopenharmony_ci * Writes block of data to the non-active flash region of the switch. If 37362306a36Sopenharmony_ci * the address is given as %DMA_PORT_CSS_ADDRESS the block is written 37462306a36Sopenharmony_ci * using CSS command. 37562306a36Sopenharmony_ci */ 37662306a36Sopenharmony_ciint dma_port_flash_write(struct tb_dma_port *dma, unsigned int address, 37762306a36Sopenharmony_ci const void *buf, size_t size) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci if (address >= DMA_PORT_CSS_ADDRESS && size > DMA_PORT_CSS_MAX_SIZE) 38062306a36Sopenharmony_ci return -E2BIG; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci return tb_nvm_write_data(address, buf, size, DMA_PORT_RETRIES, 38362306a36Sopenharmony_ci dma_port_flash_write_block, dma); 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci/** 38762306a36Sopenharmony_ci * dma_port_flash_update_auth() - Starts flash authenticate cycle 38862306a36Sopenharmony_ci * @dma: DMA control port 38962306a36Sopenharmony_ci * 39062306a36Sopenharmony_ci * Starts the flash update authentication cycle. If the image in the 39162306a36Sopenharmony_ci * non-active area was valid, the switch starts upgrade process where 39262306a36Sopenharmony_ci * active and non-active area get swapped in the end. Caller should call 39362306a36Sopenharmony_ci * dma_port_flash_update_auth_status() to get status of this command. 39462306a36Sopenharmony_ci * This is because if the switch in question is root switch the 39562306a36Sopenharmony_ci * thunderbolt host controller gets reset as well. 39662306a36Sopenharmony_ci */ 39762306a36Sopenharmony_ciint dma_port_flash_update_auth(struct tb_dma_port *dma) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci u32 in; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci in = MAIL_IN_CMD_FLASH_UPDATE_AUTH << MAIL_IN_CMD_SHIFT; 40262306a36Sopenharmony_ci in |= MAIL_IN_OP_REQUEST; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci return dma_port_request(dma, in, 150); 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci/** 40862306a36Sopenharmony_ci * dma_port_flash_update_auth_status() - Reads status of update auth command 40962306a36Sopenharmony_ci * @dma: DMA control port 41062306a36Sopenharmony_ci * @status: Status code of the operation 41162306a36Sopenharmony_ci * 41262306a36Sopenharmony_ci * The function checks if there is status available from the last update 41362306a36Sopenharmony_ci * auth command. Returns %0 if there is no status and no further 41462306a36Sopenharmony_ci * action is required. If there is status, %1 is returned instead and 41562306a36Sopenharmony_ci * @status holds the failure code. 41662306a36Sopenharmony_ci * 41762306a36Sopenharmony_ci * Negative return means there was an error reading status from the 41862306a36Sopenharmony_ci * switch. 41962306a36Sopenharmony_ci */ 42062306a36Sopenharmony_ciint dma_port_flash_update_auth_status(struct tb_dma_port *dma, u32 *status) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci struct tb_switch *sw = dma->sw; 42362306a36Sopenharmony_ci u32 out, cmd; 42462306a36Sopenharmony_ci int ret; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci ret = dma_port_read(sw->tb->ctl, &out, tb_route(sw), dma->port, 42762306a36Sopenharmony_ci dma->base + MAIL_OUT, 1, DMA_PORT_TIMEOUT); 42862306a36Sopenharmony_ci if (ret) 42962306a36Sopenharmony_ci return ret; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* Check if the status relates to flash update auth */ 43262306a36Sopenharmony_ci cmd = (out & MAIL_OUT_STATUS_CMD_MASK) >> MAIL_OUT_STATUS_CMD_SHIFT; 43362306a36Sopenharmony_ci if (cmd == MAIL_IN_CMD_FLASH_UPDATE_AUTH) { 43462306a36Sopenharmony_ci if (status) 43562306a36Sopenharmony_ci *status = out & MAIL_OUT_STATUS_MASK; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* Reset is needed in any case */ 43862306a36Sopenharmony_ci return 1; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci return 0; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci/** 44562306a36Sopenharmony_ci * dma_port_power_cycle() - Power cycles the switch 44662306a36Sopenharmony_ci * @dma: DMA control port 44762306a36Sopenharmony_ci * 44862306a36Sopenharmony_ci * Triggers power cycle to the switch. 44962306a36Sopenharmony_ci */ 45062306a36Sopenharmony_ciint dma_port_power_cycle(struct tb_dma_port *dma) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci u32 in; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci in = MAIL_IN_CMD_POWER_CYCLE << MAIL_IN_CMD_SHIFT; 45562306a36Sopenharmony_ci in |= MAIL_IN_OP_REQUEST; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci return dma_port_request(dma, in, 150); 45862306a36Sopenharmony_ci} 459