On Tue, Sep 27, 2016 at 04:43:37PM +0300, Amir Levy wrote: > This patch provides the communication protocol between the > Intel Connection Manager(ICM) firmware that is operational in the > Thunderbolt controller in non-Apple hardware. > The ICM firmware-based controller is used for establishing and maintaining > the Thunderbolt Networking connection - we need to be able to communicate > with it. > > Signed-off-by: Amir Levy <amir.jer.levy@xxxxxxxxx> > --- > drivers/thunderbolt/Makefile | 1 + > drivers/thunderbolt/icm/Makefile | 2 + > drivers/thunderbolt/icm/icm_nhi.c | 1324 +++++++++++++++++++++++++++++++++++++ > drivers/thunderbolt/icm/icm_nhi.h | 92 +++ > drivers/thunderbolt/icm/net.h | 227 +++++++ > 5 files changed, 1646 insertions(+) > create mode 100644 drivers/thunderbolt/icm/Makefile > create mode 100644 drivers/thunderbolt/icm/icm_nhi.c > create mode 100644 drivers/thunderbolt/icm/icm_nhi.h > create mode 100644 drivers/thunderbolt/icm/net.h > > diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile > index 7a85bd1..b6aa6a3 100644 > --- a/drivers/thunderbolt/Makefile > +++ b/drivers/thunderbolt/Makefile > @@ -1,3 +1,4 @@ > obj-${CONFIG_THUNDERBOLT_APPLE} := thunderbolt.o > thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel_pci.o eeprom.o > > +obj-${CONFIG_THUNDERBOLT_ICM} += icm/ > diff --git a/drivers/thunderbolt/icm/Makefile b/drivers/thunderbolt/icm/Makefile > new file mode 100644 > index 0000000..f0d0fbb > --- /dev/null > +++ b/drivers/thunderbolt/icm/Makefile > @@ -0,0 +1,2 @@ > +obj-${CONFIG_THUNDERBOLT_ICM} += thunderbolt-icm.o > +thunderbolt-icm-objs := icm_nhi.o > diff --git a/drivers/thunderbolt/icm/icm_nhi.c b/drivers/thunderbolt/icm/icm_nhi.c > new file mode 100644 > index 0000000..984aa7c > --- /dev/null > +++ b/drivers/thunderbolt/icm/icm_nhi.c > @@ -0,0 +1,1324 @@ > +/******************************************************************************* > + * > + * Intel Thunderbolt(TM) driver > + * Copyright(c) 2014 - 2016 Intel Corporation. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms and conditions of the GNU General Public License, > + * version 2, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope it will be useful, but WITHOUT > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for > + * more details. > + * > + * You should have received a copy of the GNU General Public License along > + * with this program. If not, see <http://www.gnu.org/licenses/>. Why is this sentence needed? > + * > + * The full GNU General Public License is included in this distribution in > + * the file called "COPYING". Why is this sentence needed? > + * > + * Contact Information: > + * Intel Thunderbolt Mailing List <thunderbolt-software@xxxxxxxxxxxx> Shouldn't this just be in the MAINTAINERS file? > + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 Unless you are going to track the address of Intel for the next 40+ years, don't put it in a source comment. Please just drop it. > + * > + ******************************************************************************/ > + > +#include <linux/printk.h> > +#include <linux/crc32.h> > +#include <linux/delay.h> > +#include <linux/dmi.h> > +#include "icm_nhi.h" > +#include "net.h" > + > +#define NHI_GENL_VERSION 1 > +#define NHI_GENL_NAME "thunderbolt" > + > +#define DEVICE_DATA(num_ports, dma_port, nvm_ver_offset, nvm_auth_on_boot,\ > + support_full_e2e) \ > + ((num_ports) | ((dma_port) << 4) | ((nvm_ver_offset) << 10) | \ > + ((nvm_auth_on_boot) << 22) | ((support_full_e2e) << 23)) > +#define DEVICE_DATA_NUM_PORTS(device_data) ((device_data) & 0xf) > +#define DEVICE_DATA_DMA_PORT(device_data) (((device_data) >> 4) & 0x3f) > +#define DEVICE_DATA_NVM_VER_OFFSET(device_data) (((device_data) >> 10) & 0xfff) > +#define DEVICE_DATA_NVM_AUTH_ON_BOOT(device_data) (((device_data) >> 22) & 0x1) > +#define DEVICE_DATA_SUPPORT_FULL_E2E(device_data) (((device_data) >> 23) & 0x1) > + > +#define USEC_TO_256_NSECS(usec) DIV_ROUND_UP((usec) * NSEC_PER_USEC, 256) > + > +/* NHI genetlink commands */ > +enum { > + NHI_CMD_UNSPEC, > + NHI_CMD_SUBSCRIBE, > + NHI_CMD_UNSUBSCRIBE, > + NHI_CMD_QUERY_INFORMATION, > + NHI_CMD_MSG_TO_ICM, > + NHI_CMD_MSG_FROM_ICM, > + NHI_CMD_MAILBOX, > + NHI_CMD_APPROVE_TBT_NETWORKING, > + NHI_CMD_ICM_IN_SAFE_MODE, > + __NHI_CMD_MAX, > +}; > +#define NHI_CMD_MAX (__NHI_CMD_MAX - 1) > + > +/* NHI genetlink policy */ > +static const struct nla_policy nhi_genl_policy[NHI_ATTR_MAX + 1] = { > + [NHI_ATTR_DRV_VERSION] = { .type = NLA_NUL_STRING, }, > + [NHI_ATTR_NVM_VER_OFFSET] = { .type = NLA_U16, }, > + [NHI_ATTR_NUM_PORTS] = { .type = NLA_U8, }, > + [NHI_ATTR_DMA_PORT] = { .type = NLA_U8, }, > + [NHI_ATTR_SUPPORT_FULL_E2E] = { .type = NLA_FLAG, }, > + [NHI_ATTR_MAILBOX_CMD] = { .type = NLA_U32, }, > + [NHI_ATTR_PDF] = { .type = NLA_U32, }, > + [NHI_ATTR_MSG_TO_ICM] = { .type = NLA_BINARY, > + .len = TBT_ICM_RING_MAX_FRAME_SIZE }, > + [NHI_ATTR_MSG_FROM_ICM] = { .type = NLA_BINARY, > + .len = TBT_ICM_RING_MAX_FRAME_SIZE }, > +}; > + > +/* NHI genetlink family */ > +static struct genl_family nhi_genl_family = { > + .id = GENL_ID_GENERATE, > + .hdrsize = FIELD_SIZEOF(struct tbt_nhi_ctxt, id), > + .name = NHI_GENL_NAME, > + .version = NHI_GENL_VERSION, > + .maxattr = NHI_ATTR_MAX, > +}; > + > +static LIST_HEAD(controllers_list); > +static DEFINE_MUTEX(controllers_list_mutex); > +static atomic_t subscribers = ATOMIC_INIT(0); > +static u32 portid; This is really odd, why have a global single portid? > + > +static bool nhi_nvm_authenticated(struct tbt_nhi_ctxt *nhi_ctxt) > +{ > + enum icm_operation_mode op_mode; > + u32 *msg_head, port_id, reg; > + struct sk_buff *skb; > + int i; > + > + if (!nhi_ctxt->nvm_auth_on_boot) > + return true; > + > + /* > + * The check for NVM authentication can take time for iCM, > + * especially in low power configuration. > + */ > + for (i = 0; i < 5; i++) { > + u32 status = ioread32(nhi_ctxt->iobase + REG_FW_STS); > + > + if (status & REG_FW_STS_NVM_AUTH_DONE) > + break; > + > + msleep(30); > + } > + /* > + * The check for authentication is done after checking if iCM > + * is present so it shouldn't reach the max tries (=5). > + * Anyway, the check for full functionality below covers the error case. > + */ > + reg = ioread32(nhi_ctxt->iobase + REG_OUTMAIL_CMD); > + op_mode = (reg & REG_OUTMAIL_CMD_OP_MODE_MASK) >> > + REG_OUTMAIL_CMD_OP_MODE_SHIFT; > + if (op_mode == FULL_FUNCTIONALITY) > + return true; > + > + dev_warn(&nhi_ctxt->pdev->dev, "controller id %#x is in operation mode %#x status %#lx\n", > + nhi_ctxt->id, op_mode, > + (reg & REG_OUTMAIL_CMD_STS_MASK)>>REG_OUTMAIL_CMD_STS_SHIFT); > + > + skb = genlmsg_new(NLMSG_ALIGN(nhi_genl_family.hdrsize), GFP_KERNEL); > + if (!skb) { > + dev_err(&nhi_ctxt->pdev->dev, "genlmsg_new failed: not enough memory to send controller operational mode\n"); > + return false; > + } > + > + /* keeping port_id into a local variable for next use */ > + port_id = portid; > + msg_head = genlmsg_put(skb, port_id, 0, &nhi_genl_family, 0, > + NHI_CMD_ICM_IN_SAFE_MODE); > + if (!msg_head) { > + nlmsg_free(skb); > + dev_err(&nhi_ctxt->pdev->dev, "genlmsg_put failed: not enough memory to send controller operational mode\n"); > + return false; > + } > + > + *msg_head = nhi_ctxt->id; > + > + genlmsg_end(skb, msg_head); > + > + genlmsg_unicast(&init_net, skb, port_id); > + > + return false; > +} > + > +int nhi_send_message(struct tbt_nhi_ctxt *nhi_ctxt, enum pdf_value pdf, > + u32 msg_len, const void *msg, bool ignore_icm_resp) > +{ > + u32 prod_cons, prod, cons, attr; > + struct tbt_icm_ring_shared_memory *shared_mem; > + void __iomem *reg = TBT_RING_CONS_PROD_REG(nhi_ctxt->iobase, > + REG_TX_RING_BASE, > + TBT_ICM_RING_NUM); > + > + dev_dbg(&nhi_ctxt->pdev->dev, > + "send msg: controller id %#x pdf %u cmd %hhu msg len %u\n", > + nhi_ctxt->id, pdf, ((u8 *)msg)[3], msg_len); Don't put trace debug calls in your code, either use a real tracepoint, or just use ftrace. Delete these, they aren't needed once you want to submit the code for merging as it should not be needed. You do this in a lot of other places as well, please fix all of them. > + > + if (nhi_ctxt->d0_exit) { > + dev_notice(&nhi_ctxt->pdev->dev, > + "controller id %#x is exiting D0\n", > + nhi_ctxt->id); What can a user do about this? > + return -ENODEV; > + } > + > + prod_cons = ioread32(reg); > + prod = TBT_REG_RING_PROD_EXTRACT(prod_cons); > + cons = TBT_REG_RING_CONS_EXTRACT(prod_cons); > + if (prod >= TBT_ICM_RING_NUM_TX_BUFS) { > + dev_warn(&nhi_ctxt->pdev->dev, > + "controller id %#x producer %u out of range\n", > + nhi_ctxt->id, prod); What can a user do about this? > + return -ENODEV; > + } > + if (TBT_TX_RING_FULL(prod, cons, TBT_ICM_RING_NUM_TX_BUFS)) { > + dev_err(&nhi_ctxt->pdev->dev, > + "controller id %#x TX ring full\n", > + nhi_ctxt->id); What can a user do about this? > + return -ENOSPC; > + } > + > + attr = (msg_len << DESC_ATTR_LEN_SHIFT) & DESC_ATTR_LEN_MASK; > + attr |= (pdf << DESC_ATTR_EOF_SHIFT) & DESC_ATTR_EOF_MASK; > + > + shared_mem = nhi_ctxt->icm_ring_shared_mem; > + shared_mem->tx_buf_desc[prod].attributes = cpu_to_le32(attr); > + > + memcpy(shared_mem->tx_buf[prod], msg, msg_len); No zero-copy, sad :( > + > + prod_cons &= ~REG_RING_PROD_MASK; > + prod_cons |= (((prod + 1) % TBT_ICM_RING_NUM_TX_BUFS) << > + REG_RING_PROD_SHIFT) & REG_RING_PROD_MASK; > + > + if (likely(!nhi_ctxt->wait_for_icm_resp)) > + nhi_ctxt->wait_for_icm_resp = true; Can you measure the speed difference with likely() here? If so, great, if not, drop it as the cpu can guess this better than you or I can. > + else > + dev_dbg(&nhi_ctxt->pdev->dev, > + "controller id %#x wait_for_icm_resp should have been cleared\n", > + nhi_ctxt->id); Huh? So this isn't a real issue? > + > + nhi_ctxt->ignore_icm_resp = ignore_icm_resp; > + > + iowrite32(prod_cons, reg); > + > + return 0; > +} thanks, greg k-h -- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html