Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3a6a9201 authored by Sudeep Dutt's avatar Sudeep Dutt Committed by Greg Kroah-Hartman
Browse files

Intel MIC Host Driver, card OS state management.



This patch enables the following features:
a) Boots and shuts down the card via sysfs entries.
b) Allocates and maps a device page for communication with the
   card driver and updates the device page address via scratchpad
   registers.
c) Provides sysfs entries for shutdown status, kernel command line,
   ramdisk and log buffer information.

Co-author: Dasaratharaman Chandramouli <dasaratharaman.chandramouli@intel.com>
Signed-off-by: default avatarAshutosh Dixit <ashutosh.dixit@intel.com>
Signed-off-by: default avatarCaz Yokoyama <Caz.Yokoyama@intel.com>
Signed-off-by: default avatarDasaratharaman Chandramouli <dasaratharaman.chandramouli@intel.com>
Signed-off-by: default avatarHarshavardhan R Kharche <harshavardhan.r.kharche@intel.com>
Signed-off-by: default avatarNikhil Rao <nikhil.rao@intel.com>
Signed-off-by: default avatarSudeep Dutt <sudeep.dutt@intel.com>
Acked-by: default avatarYaozu (Eddie) Dong <eddie.dong@intel.com>
Reviewed-by: default avatarPeter P Waskiewicz Jr <peter.p.waskiewicz.jr@intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent a01e28f6
Loading
Loading
Loading
Loading
+113 −0
Original line number Diff line number Diff line
@@ -32,3 +32,116 @@ Contact: Sudeep Dutt <sudeep.dutt@intel.com>
Description:
		Provides information about the silicon stepping for an Intel
		MIC device. For example - "A0" or "B0"

What:		/sys/class/mic/mic(x)/state
Date:		August 2013
KernelVersion:	3.11
Contact:	Sudeep Dutt <sudeep.dutt@intel.com>
Description:
		When read, this entry provides the current state of an Intel
		MIC device in the context of the card OS. Possible values that
		will be read are:
		"offline" - The MIC device is ready to boot the card OS.
		"online" - The MIC device has initiated booting a card OS.
		"shutting_down" - The card OS is shutting down.
		"reset_failed" - The MIC device has failed to reset.

		When written, this sysfs entry triggers different state change
		operations depending upon the current state of the card OS.
		Acceptable values are:
		"boot" - Boot the card OS image specified by the combination
			 of firmware, ramdisk, cmdline and bootmode
			sysfs entries.
		"reset" - Initiates device reset.
		"shutdown" - Initiates card OS shutdown.

What:		/sys/class/mic/mic(x)/shutdown_status
Date:		August 2013
KernelVersion:	3.11
Contact:	Sudeep Dutt <sudeep.dutt@intel.com>
Description:
		An Intel MIC device runs a Linux OS during its operation. This
		OS can shutdown because of various reasons. When read, this
		entry provides the status on why the card OS was shutdown.
		Possible values are:
		"nop" -  shutdown status is not applicable, when the card OS is
			"online"
		"crashed" - Shutdown because of a HW or SW crash.
		"halted" - Shutdown because of a halt command.
		"poweroff" - Shutdown because of a poweroff command.
		"restart" - Shutdown because of a restart command.

What:		/sys/class/mic/mic(x)/cmdline
Date:		August 2013
KernelVersion:	3.11
Contact:	Sudeep Dutt <sudeep.dutt@intel.com>
Description:
		An Intel MIC device runs a Linux OS during its operation. Before
		booting this card OS, it is possible to pass kernel command line
		options to configure various features in it, similar to
		self-bootable machines. When read, this entry provides
		information about the current kernel command line options set to
		boot the card OS. This entry can be written to change the
		existing kernel command line options. Typically, the user would
		want to read the current command line options, append new ones
		or modify existing ones and then write the whole kernel command
		line back to this entry.

What:		/sys/class/mic/mic(x)/firmware
Date:		August 2013
KernelVersion:	3.11
Contact:	Sudeep Dutt <sudeep.dutt@intel.com>
Description:
		When read, this sysfs entry provides the path name under
		/lib/firmware/ where the firmware image to be booted on the
		card can be found. The entry can be written to change the
		firmware image location under /lib/firmware/.

What:		/sys/class/mic/mic(x)/ramdisk
Date:		August 2013
KernelVersion:	3.11
Contact:	Sudeep Dutt <sudeep.dutt@intel.com>
Description:
		When read, this sysfs entry provides the path name under
		/lib/firmware/ where the ramdisk image to be used during card
		OS boot can be found. The entry can be written to change
		the ramdisk image location under /lib/firmware/.

What:		/sys/class/mic/mic(x)/bootmode
Date:		August 2013
KernelVersion:	3.11
Contact:	Sudeep Dutt <sudeep.dutt@intel.com>
Description:
		When read, this sysfs entry provides the current bootmode for
		the card. This sysfs entry can be written with the following
		valid strings:
		a) linux - Boot a Linux image.
		b) elf - Boot an elf image for flash updates.

What:		/sys/class/mic/mic(x)/log_buf_addr
Date:		August 2013
KernelVersion:	3.11
Contact:	Sudeep Dutt <sudeep.dutt@intel.com>
Description:
		An Intel MIC device runs a Linux OS during its operation. For
		debugging purpose and early kernel boot messages, the user can
		access the card OS log buffer via debugfs. When read, this entry
		provides the kernel virtual address of the buffer where the card
		OS log buffer can be read. This entry is written by the host
		configuration daemon to set the log buffer address. The correct
		log buffer address to be written can be found in the System.map
		file of the card OS.

What:		/sys/class/mic/mic(x)/log_buf_len
Date:		August 2013
KernelVersion:	3.11
Contact:	Sudeep Dutt <sudeep.dutt@intel.com>
Description:
		An Intel MIC device runs a Linux OS during its operation. For
		debugging purpose and early kernel boot messages, the user can
		access the card OS log buffer via debugfs. When read, this entry
		provides the kernel virtual address where the card OS log buffer
		length can be read. This entry is written by host configuration
		daemon to set the log buffer length address. The correct log
		buffer length address to be written can be found in the
		System.map file of the card OS.
+7 −0
Original line number Diff line number Diff line
@@ -34,4 +34,11 @@ struct mic_mw {
	resource_size_t len;
};

/*
 * Scratch pad register offsets used by the host to communicate
 * device page DMA address to the card.
 */
#define MIC_DPLO_SPAD 14
#define MIC_DPHI_SPAD 15

#endif
+2 −0
Original line number Diff line number Diff line
@@ -8,3 +8,5 @@ mic_host-objs += mic_x100.o
mic_host-objs += mic_sysfs.o
mic_host-objs += mic_smpt.o
mic_host-objs += mic_intr.o
mic_host-objs += mic_boot.o
mic_host-objs += mic_debugfs.o
+184 −0
Original line number Diff line number Diff line
/*
 * Intel MIC Platform Software Stack (MPSS)
 *
 * Copyright(c) 2013 Intel Corporation.
 *
 * 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.
 *
 * This program is distributed in the hope that 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.
 *
 * The full GNU General Public License is included in this distribution in
 * the file called "COPYING".
 *
 * Intel MIC Host driver.
 *
 */
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/interrupt.h>

#include <linux/mic_common.h>
#include "../common/mic_device.h"
#include "mic_device.h"
#include "mic_smpt.h"

/**
 * mic_reset - Reset the MIC device.
 * @mdev: pointer to mic_device instance
 */
static void mic_reset(struct mic_device *mdev)
{
	int i;

#define MIC_RESET_TO (45)

	mdev->ops->reset_fw_ready(mdev);
	mdev->ops->reset(mdev);

	for (i = 0; i < MIC_RESET_TO; i++) {
		if (mdev->ops->is_fw_ready(mdev))
			return;
		/*
		 * Resets typically take 10s of seconds to complete.
		 * Since an MMIO read is required to check if the
		 * firmware is ready or not, a 1 second delay works nicely.
		 */
		msleep(1000);
	}
	mic_set_state(mdev, MIC_RESET_FAILED);
}

/* Initialize the MIC bootparams */
void mic_bootparam_init(struct mic_device *mdev)
{
	struct mic_bootparam *bootparam = mdev->dp;

	bootparam->magic = MIC_MAGIC;
	bootparam->c2h_shutdown_db = mdev->shutdown_db;
	bootparam->h2c_shutdown_db = -1;
	bootparam->h2c_config_db = -1;
	bootparam->shutdown_status = 0;
	bootparam->shutdown_card = 0;
}

/**
 * mic_start - Start the MIC.
 * @mdev: pointer to mic_device instance
 * @buf: buffer containing boot string including firmware/ramdisk path.
 *
 * This function prepares an MIC for boot and initiates boot.
 * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
 */
int mic_start(struct mic_device *mdev, const char *buf)
{
	int rc;
	mutex_lock(&mdev->mic_mutex);
retry:
	if (MIC_OFFLINE != mdev->state) {
		rc = -EINVAL;
		goto unlock_ret;
	}
	if (!mdev->ops->is_fw_ready(mdev)) {
		mic_reset(mdev);
		/*
		 * The state will either be MIC_OFFLINE if the reset succeeded
		 * or MIC_RESET_FAILED if the firmware reset failed.
		 */
		goto retry;
	}
	rc = mdev->ops->load_mic_fw(mdev, buf);
	if (rc)
		goto unlock_ret;
	mic_smpt_restore(mdev);
	mic_intr_restore(mdev);
	mdev->intr_ops->enable_interrupts(mdev);
	mdev->ops->write_spad(mdev, MIC_DPLO_SPAD, mdev->dp_dma_addr);
	mdev->ops->write_spad(mdev, MIC_DPHI_SPAD, mdev->dp_dma_addr >> 32);
	mdev->ops->send_firmware_intr(mdev);
	mic_set_state(mdev, MIC_ONLINE);
unlock_ret:
	mutex_unlock(&mdev->mic_mutex);
	return rc;
}

/**
 * mic_stop - Prepare the MIC for reset and trigger reset.
 * @mdev: pointer to mic_device instance
 * @force: force a MIC to reset even if it is already offline.
 *
 * RETURNS: None.
 */
void mic_stop(struct mic_device *mdev, bool force)
{
	mutex_lock(&mdev->mic_mutex);
	if (MIC_OFFLINE != mdev->state || force) {
		mic_bootparam_init(mdev);
		mic_reset(mdev);
		if (MIC_RESET_FAILED == mdev->state)
			goto unlock;
		mic_set_shutdown_status(mdev, MIC_NOP);
		mic_set_state(mdev, MIC_OFFLINE);
	}
unlock:
	mutex_unlock(&mdev->mic_mutex);
}

/**
 * mic_shutdown - Initiate MIC shutdown.
 * @mdev: pointer to mic_device instance
 *
 * RETURNS: None.
 */
void mic_shutdown(struct mic_device *mdev)
{
	struct mic_bootparam *bootparam = mdev->dp;
	s8 db = bootparam->h2c_shutdown_db;

	mutex_lock(&mdev->mic_mutex);
	if (MIC_ONLINE == mdev->state && db != -1) {
		bootparam->shutdown_card = 1;
		mdev->ops->send_intr(mdev, db);
		mic_set_state(mdev, MIC_SHUTTING_DOWN);
	}
	mutex_unlock(&mdev->mic_mutex);
}

/**
 * mic_shutdown_work - Handle shutdown interrupt from MIC.
 * @work: The work structure.
 *
 * This work is scheduled whenever the host has received a shutdown
 * interrupt from the MIC.
 */
void mic_shutdown_work(struct work_struct *work)
{
	struct mic_device *mdev = container_of(work, struct mic_device,
			shutdown_work);
	struct mic_bootparam *bootparam = mdev->dp;

	mutex_lock(&mdev->mic_mutex);
	mic_set_shutdown_status(mdev, bootparam->shutdown_status);
	bootparam->shutdown_status = 0;
	if (MIC_SHUTTING_DOWN != mdev->state)
		mic_set_state(mdev, MIC_SHUTTING_DOWN);
	mutex_unlock(&mdev->mic_mutex);
}

/**
 * mic_reset_trigger_work - Trigger MIC reset.
 * @work: The work structure.
 *
 * This work is scheduled whenever the host wants to reset the MIC.
 */
void mic_reset_trigger_work(struct work_struct *work)
{
	struct mic_device *mdev = container_of(work, struct mic_device,
			reset_trigger_work);

	mic_stop(mdev, false);
}
+355 −0
Original line number Diff line number Diff line
/*
 * Intel MIC Platform Software Stack (MPSS)
 *
 * Copyright(c) 2013 Intel Corporation.
 *
 * 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.
 *
 * This program is distributed in the hope that 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.
 *
 * The full GNU General Public License is included in this distribution in
 * the file called "COPYING".
 *
 * Intel MIC Host driver.
 *
 */
#include <linux/debugfs.h>
#include <linux/pci.h>
#include <linux/seq_file.h>

#include <linux/mic_common.h>
#include "../common/mic_device.h"
#include "mic_device.h"
#include "mic_smpt.h"

/* Debugfs parent dir */
static struct dentry *mic_dbg;

/**
 * mic_log_buf_show - Display MIC kernel log buffer.
 *
 * log_buf addr/len is read from System.map by user space
 * and populated in sysfs entries.
 */
static int mic_log_buf_show(struct seq_file *s, void *unused)
{
	void __iomem *log_buf_va;
	int __iomem *log_buf_len_va;
	struct mic_device *mdev = s->private;
	void *kva;
	int size;
	unsigned long aper_offset;

	if (!mdev || !mdev->log_buf_addr || !mdev->log_buf_len)
		goto done;
	/*
	 * Card kernel will never be relocated and any kernel text/data mapping
	 * can be translated to phys address by subtracting __START_KERNEL_map.
	 */
	aper_offset = (unsigned long)mdev->log_buf_len - __START_KERNEL_map;
	log_buf_len_va = mdev->aper.va + aper_offset;
	aper_offset = (unsigned long)mdev->log_buf_addr - __START_KERNEL_map;
	log_buf_va = mdev->aper.va + aper_offset;
	size = ioread32(log_buf_len_va);

	kva = kmalloc(size, GFP_KERNEL);
	if (!kva)
		goto done;
	mutex_lock(&mdev->mic_mutex);
	memcpy_fromio(kva, log_buf_va, size);
	switch (mdev->state) {
	case MIC_ONLINE:
		/* Fall through */
	case MIC_SHUTTING_DOWN:
		seq_write(s, kva, size);
		break;
	default:
		break;
	}
	mutex_unlock(&mdev->mic_mutex);
	kfree(kva);
done:
	return 0;
}

static int mic_log_buf_open(struct inode *inode, struct file *file)
{
	return single_open(file, mic_log_buf_show, inode->i_private);
}

static int mic_log_buf_release(struct inode *inode, struct file *file)
{
	return single_release(inode, file);
}

static const struct file_operations log_buf_ops = {
	.owner   = THIS_MODULE,
	.open    = mic_log_buf_open,
	.read    = seq_read,
	.llseek  = seq_lseek,
	.release = mic_log_buf_release
};

static int mic_smpt_show(struct seq_file *s, void *pos)
{
	int i;
	struct mic_device *mdev = s->private;
	unsigned long flags;

	seq_printf(s, "MIC %-2d |%-10s| %-14s %-10s\n",
		mdev->id, "SMPT entry", "SW DMA addr", "RefCount");
	seq_puts(s, "====================================================\n");

	if (mdev->smpt) {
		struct mic_smpt_info *smpt_info = mdev->smpt;
		spin_lock_irqsave(&smpt_info->smpt_lock, flags);
		for (i = 0; i < smpt_info->info.num_reg; i++) {
			seq_printf(s, "%9s|%-10d| %-#14llx %-10lld\n",
				" ",  i, smpt_info->entry[i].dma_addr,
				smpt_info->entry[i].ref_count);
		}
		spin_unlock_irqrestore(&smpt_info->smpt_lock, flags);
	}
	seq_puts(s, "====================================================\n");
	return 0;
}

static int mic_smpt_debug_open(struct inode *inode, struct file *file)
{
	return single_open(file, mic_smpt_show, inode->i_private);
}

static int mic_smpt_debug_release(struct inode *inode, struct file *file)
{
	return single_release(inode, file);
}

static const struct file_operations smpt_file_ops = {
	.owner   = THIS_MODULE,
	.open    = mic_smpt_debug_open,
	.read    = seq_read,
	.llseek  = seq_lseek,
	.release = mic_smpt_debug_release
};

static int mic_soft_reset_show(struct seq_file *s, void *pos)
{
	struct mic_device *mdev = s->private;

	mic_stop(mdev, true);
	return 0;
}

static int mic_soft_reset_debug_open(struct inode *inode, struct file *file)
{
	return single_open(file, mic_soft_reset_show, inode->i_private);
}

static int mic_soft_reset_debug_release(struct inode *inode, struct file *file)
{
	return single_release(inode, file);
}

static const struct file_operations soft_reset_ops = {
	.owner   = THIS_MODULE,
	.open    = mic_soft_reset_debug_open,
	.read    = seq_read,
	.llseek  = seq_lseek,
	.release = mic_soft_reset_debug_release
};

static int mic_post_code_show(struct seq_file *s, void *pos)
{
	struct mic_device *mdev = s->private;
	u32 reg = mdev->ops->get_postcode(mdev);

	seq_printf(s, "%c%c", reg & 0xff, (reg >> 8) & 0xff);
	return 0;
}

static int mic_post_code_debug_open(struct inode *inode, struct file *file)
{
	return single_open(file, mic_post_code_show, inode->i_private);
}

static int mic_post_code_debug_release(struct inode *inode, struct file *file)
{
	return single_release(inode, file);
}

static const struct file_operations post_code_ops = {
	.owner   = THIS_MODULE,
	.open    = mic_post_code_debug_open,
	.read    = seq_read,
	.llseek  = seq_lseek,
	.release = mic_post_code_debug_release
};

static int mic_dp_show(struct seq_file *s, void *pos)
{
	struct mic_device *mdev = s->private;
	struct mic_bootparam *bootparam = mdev->dp;

	seq_printf(s, "Bootparam: magic 0x%x\n",
		bootparam->magic);
	seq_printf(s, "Bootparam: h2c_shutdown_db %d\n",
		bootparam->h2c_shutdown_db);
	seq_printf(s, "Bootparam: h2c_config_db %d\n",
		bootparam->h2c_config_db);
	seq_printf(s, "Bootparam: c2h_shutdown_db %d\n",
		bootparam->c2h_shutdown_db);
	seq_printf(s, "Bootparam: shutdown_status %d\n",
		bootparam->shutdown_status);
	seq_printf(s, "Bootparam: shutdown_card %d\n",
		bootparam->shutdown_card);

	return 0;
}

static int mic_dp_debug_open(struct inode *inode, struct file *file)
{
	return single_open(file, mic_dp_show, inode->i_private);
}

static int mic_dp_debug_release(struct inode *inode, struct file *file)
{
	return single_release(inode, file);
}

static const struct file_operations dp_ops = {
	.owner   = THIS_MODULE,
	.open    = mic_dp_debug_open,
	.read    = seq_read,
	.llseek  = seq_lseek,
	.release = mic_dp_debug_release
};

static int mic_msi_irq_info_show(struct seq_file *s, void *pos)
{
	struct mic_device *mdev  = s->private;
	int reg;
	int i, j;
	u16 entry;
	u16 vector;
	struct pci_dev *pdev = container_of(mdev->sdev->parent,
		struct pci_dev, dev);

	if (pci_dev_msi_enabled(pdev)) {
		for (i = 0; i < mdev->irq_info.num_vectors; i++) {
			if (pdev->msix_enabled) {
				entry = mdev->irq_info.msix_entries[i].entry;
				vector = mdev->irq_info.msix_entries[i].vector;
			} else {
				entry = 0;
				vector = pdev->irq;
			}

			reg = mdev->intr_ops->read_msi_to_src_map(mdev, entry);

			seq_printf(s, "%s %-10d %s %-10d MXAR[%d]: %08X\n",
				"IRQ:", vector, "Entry:", entry, i, reg);

			seq_printf(s, "%-10s", "offset:");
			for (j = (MIC_NUM_OFFSETS - 1); j >= 0; j--)
				seq_printf(s, "%4d ", j);
			seq_puts(s, "\n");


			seq_printf(s, "%-10s", "count:");
			for (j = (MIC_NUM_OFFSETS - 1); j >= 0; j--)
				seq_printf(s, "%4d ",
				(mdev->irq_info.mic_msi_map[i] & BIT(j)) ?
					1 : 0);
			seq_puts(s, "\n\n");
		}
	} else {
		seq_puts(s, "MSI/MSIx interrupts not enabled\n");
	}

	return 0;

}

static int mic_msi_irq_info_debug_open(struct inode *inode, struct file *file)
{
	return single_open(file, mic_msi_irq_info_show, inode->i_private);
}

static int
mic_msi_irq_info_debug_release(struct inode *inode, struct file *file)
{
	return single_release(inode, file);
}

static const struct file_operations msi_irq_info_ops = {
	.owner   = THIS_MODULE,
	.open    = mic_msi_irq_info_debug_open,
	.read    = seq_read,
	.llseek  = seq_lseek,
	.release = mic_msi_irq_info_debug_release
};

/**
 * mic_create_debug_dir - Initialize MIC debugfs entries.
 */
void mic_create_debug_dir(struct mic_device *mdev)
{
	if (!mic_dbg)
		return;

	mdev->dbg_dir = debugfs_create_dir(dev_name(mdev->sdev), mic_dbg);
	if (!mdev->dbg_dir)
		return;

	debugfs_create_file("log_buf", 0444, mdev->dbg_dir,
		mdev, &log_buf_ops);

	debugfs_create_file("smpt", 0444, mdev->dbg_dir,
		mdev, &smpt_file_ops);

	debugfs_create_file("soft_reset", 0444, mdev->dbg_dir,
		mdev, &soft_reset_ops);

	debugfs_create_file("post_code", 0444, mdev->dbg_dir,
		mdev, &post_code_ops);

	debugfs_create_file("dp", 0444, mdev->dbg_dir,
		mdev, &dp_ops);

	debugfs_create_file("msi_irq_info", 0444, mdev->dbg_dir,
		mdev, &msi_irq_info_ops);
}

/**
 * mic_delete_debug_dir - Uninitialize MIC debugfs entries.
 */
void mic_delete_debug_dir(struct mic_device *mdev)
{
	if (!mdev->dbg_dir)
		return;

	debugfs_remove_recursive(mdev->dbg_dir);
}

/**
 * mic_init_debugfs - Initialize global debugfs entry.
 */
void __init mic_init_debugfs(void)
{
	mic_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL);
	if (!mic_dbg)
		pr_err("can't create debugfs dir\n");
}

/**
 * mic_exit_debugfs - Uninitialize global debugfs entry
 */
void mic_exit_debugfs(void)
{
	debugfs_remove(mic_dbg);
}
Loading