Merge branch 'x86-platform-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull Intel SoC changes from Ingo Molnar: "Improved Intel SoC platform support" * 'x86-platform-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: x86, tsc, apic: Unbreak static (MSR) calibration when CONFIG_X86_LOCAL_APIC=n x86, tsc: Add static (MSR) TSC calibration on Intel Atom SoCs arch: x86: New MailBox support driver for Intel SOC's
This commit is contained in:
commit
74e8ee8262
7 changed files with 466 additions and 1 deletions
|
@ -2358,6 +2358,14 @@ config X86_DMA_REMAP
|
|||
bool
|
||||
depends on STA2X11
|
||||
|
||||
config IOSF_MBI
|
||||
bool
|
||||
depends on PCI
|
||||
---help---
|
||||
To be selected by modules requiring access to the Intel OnChip System
|
||||
Fabric (IOSF) Sideband MailBox Interface (MBI). For MBI platforms
|
||||
enumerable by PCI.
|
||||
|
||||
source "net/Kconfig"
|
||||
|
||||
source "drivers/Kconfig"
|
||||
|
|
90
arch/x86/include/asm/iosf_mbi.h
Normal file
90
arch/x86/include/asm/iosf_mbi.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* iosf_mbi.h: Intel OnChip System Fabric MailBox access support
|
||||
*/
|
||||
|
||||
#ifndef IOSF_MBI_SYMS_H
|
||||
#define IOSF_MBI_SYMS_H
|
||||
|
||||
#define MBI_MCR_OFFSET 0xD0
|
||||
#define MBI_MDR_OFFSET 0xD4
|
||||
#define MBI_MCRX_OFFSET 0xD8
|
||||
|
||||
#define MBI_RD_MASK 0xFEFFFFFF
|
||||
#define MBI_WR_MASK 0X01000000
|
||||
|
||||
#define MBI_MASK_HI 0xFFFFFF00
|
||||
#define MBI_MASK_LO 0x000000FF
|
||||
#define MBI_ENABLE 0xF0
|
||||
|
||||
/* Baytrail available units */
|
||||
#define BT_MBI_UNIT_AUNIT 0x00
|
||||
#define BT_MBI_UNIT_SMC 0x01
|
||||
#define BT_MBI_UNIT_CPU 0x02
|
||||
#define BT_MBI_UNIT_BUNIT 0x03
|
||||
#define BT_MBI_UNIT_PMC 0x04
|
||||
#define BT_MBI_UNIT_GFX 0x06
|
||||
#define BT_MBI_UNIT_SMI 0x0C
|
||||
#define BT_MBI_UNIT_USB 0x43
|
||||
#define BT_MBI_UNIT_SATA 0xA3
|
||||
#define BT_MBI_UNIT_PCIE 0xA6
|
||||
|
||||
/* Baytrail read/write opcodes */
|
||||
#define BT_MBI_AUNIT_READ 0x10
|
||||
#define BT_MBI_AUNIT_WRITE 0x11
|
||||
#define BT_MBI_SMC_READ 0x10
|
||||
#define BT_MBI_SMC_WRITE 0x11
|
||||
#define BT_MBI_CPU_READ 0x10
|
||||
#define BT_MBI_CPU_WRITE 0x11
|
||||
#define BT_MBI_BUNIT_READ 0x10
|
||||
#define BT_MBI_BUNIT_WRITE 0x11
|
||||
#define BT_MBI_PMC_READ 0x06
|
||||
#define BT_MBI_PMC_WRITE 0x07
|
||||
#define BT_MBI_GFX_READ 0x00
|
||||
#define BT_MBI_GFX_WRITE 0x01
|
||||
#define BT_MBI_SMIO_READ 0x06
|
||||
#define BT_MBI_SMIO_WRITE 0x07
|
||||
#define BT_MBI_USB_READ 0x06
|
||||
#define BT_MBI_USB_WRITE 0x07
|
||||
#define BT_MBI_SATA_READ 0x00
|
||||
#define BT_MBI_SATA_WRITE 0x01
|
||||
#define BT_MBI_PCIE_READ 0x00
|
||||
#define BT_MBI_PCIE_WRITE 0x01
|
||||
|
||||
/**
|
||||
* iosf_mbi_read() - MailBox Interface read command
|
||||
* @port: port indicating subunit being accessed
|
||||
* @opcode: port specific read or write opcode
|
||||
* @offset: register address offset
|
||||
* @mdr: register data to be read
|
||||
*
|
||||
* Locking is handled by spinlock - cannot sleep.
|
||||
* Return: Nonzero on error
|
||||
*/
|
||||
int iosf_mbi_read(u8 port, u8 opcode, u32 offset, u32 *mdr);
|
||||
|
||||
/**
|
||||
* iosf_mbi_write() - MailBox unmasked write command
|
||||
* @port: port indicating subunit being accessed
|
||||
* @opcode: port specific read or write opcode
|
||||
* @offset: register address offset
|
||||
* @mdr: register data to be written
|
||||
*
|
||||
* Locking is handled by spinlock - cannot sleep.
|
||||
* Return: Nonzero on error
|
||||
*/
|
||||
int iosf_mbi_write(u8 port, u8 opcode, u32 offset, u32 mdr);
|
||||
|
||||
/**
|
||||
* iosf_mbi_modify() - MailBox masked write command
|
||||
* @port: port indicating subunit being accessed
|
||||
* @opcode: port specific read or write opcode
|
||||
* @offset: register address offset
|
||||
* @mdr: register data being modified
|
||||
* @mask: mask indicating bits in mdr to be modified
|
||||
*
|
||||
* Locking is handled by spinlock - cannot sleep.
|
||||
* Return: Nonzero on error
|
||||
*/
|
||||
int iosf_mbi_modify(u8 port, u8 opcode, u32 offset, u32 mdr, u32 mask);
|
||||
|
||||
#endif /* IOSF_MBI_SYMS_H */
|
|
@ -65,4 +65,7 @@ extern int notsc_setup(char *);
|
|||
extern void tsc_save_sched_clock_state(void);
|
||||
extern void tsc_restore_sched_clock_state(void);
|
||||
|
||||
/* MSR based TSC calibration for Intel Atom SoC platforms */
|
||||
int try_msr_calibrate_tsc(unsigned long *fast_calibrate);
|
||||
|
||||
#endif /* _ASM_X86_TSC_H */
|
||||
|
|
|
@ -33,7 +33,7 @@ obj-$(CONFIG_SYSFS) += ksysfs.o
|
|||
obj-y += bootflag.o e820.o
|
||||
obj-y += pci-dma.o quirks.o topology.o kdebugfs.o
|
||||
obj-y += alternative.o i8253.o pci-nommu.o hw_breakpoint.o
|
||||
obj-y += tsc.o io_delay.o rtc.o
|
||||
obj-y += tsc.o tsc_msr.o io_delay.o rtc.o
|
||||
obj-y += pci-iommu_table.o
|
||||
obj-y += resource.o
|
||||
|
||||
|
@ -103,6 +103,7 @@ obj-$(CONFIG_EFI) += sysfb_efi.o
|
|||
|
||||
obj-$(CONFIG_PERF_EVENTS) += perf_regs.o
|
||||
obj-$(CONFIG_TRACING) += tracepoint.o
|
||||
obj-$(CONFIG_IOSF_MBI) += iosf_mbi.o
|
||||
|
||||
###
|
||||
# 64 bit specific files
|
||||
|
|
226
arch/x86/kernel/iosf_mbi.c
Normal file
226
arch/x86/kernel/iosf_mbi.c
Normal file
|
@ -0,0 +1,226 @@
|
|||
/*
|
||||
* IOSF-SB MailBox Interface Driver
|
||||
* Copyright (c) 2013, 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.
|
||||
*
|
||||
*
|
||||
* The IOSF-SB is a fabric bus available on Atom based SOC's that uses a
|
||||
* mailbox interface (MBI) to communicate with mutiple devices. This
|
||||
* driver implements access to this interface for those platforms that can
|
||||
* enumerate the device using PCI.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include <asm/iosf_mbi.h>
|
||||
|
||||
static DEFINE_SPINLOCK(iosf_mbi_lock);
|
||||
|
||||
static inline u32 iosf_mbi_form_mcr(u8 op, u8 port, u8 offset)
|
||||
{
|
||||
return (op << 24) | (port << 16) | (offset << 8) | MBI_ENABLE;
|
||||
}
|
||||
|
||||
static struct pci_dev *mbi_pdev; /* one mbi device */
|
||||
|
||||
static int iosf_mbi_pci_read_mdr(u32 mcrx, u32 mcr, u32 *mdr)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (!mbi_pdev)
|
||||
return -ENODEV;
|
||||
|
||||
if (mcrx) {
|
||||
result = pci_write_config_dword(mbi_pdev, MBI_MCRX_OFFSET,
|
||||
mcrx);
|
||||
if (result < 0)
|
||||
goto fail_read;
|
||||
}
|
||||
|
||||
result = pci_write_config_dword(mbi_pdev, MBI_MCR_OFFSET, mcr);
|
||||
if (result < 0)
|
||||
goto fail_read;
|
||||
|
||||
result = pci_read_config_dword(mbi_pdev, MBI_MDR_OFFSET, mdr);
|
||||
if (result < 0)
|
||||
goto fail_read;
|
||||
|
||||
return 0;
|
||||
|
||||
fail_read:
|
||||
dev_err(&mbi_pdev->dev, "PCI config access failed with %d\n", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int iosf_mbi_pci_write_mdr(u32 mcrx, u32 mcr, u32 mdr)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (!mbi_pdev)
|
||||
return -ENODEV;
|
||||
|
||||
result = pci_write_config_dword(mbi_pdev, MBI_MDR_OFFSET, mdr);
|
||||
if (result < 0)
|
||||
goto fail_write;
|
||||
|
||||
if (mcrx) {
|
||||
result = pci_write_config_dword(mbi_pdev, MBI_MCRX_OFFSET,
|
||||
mcrx);
|
||||
if (result < 0)
|
||||
goto fail_write;
|
||||
}
|
||||
|
||||
result = pci_write_config_dword(mbi_pdev, MBI_MCR_OFFSET, mcr);
|
||||
if (result < 0)
|
||||
goto fail_write;
|
||||
|
||||
return 0;
|
||||
|
||||
fail_write:
|
||||
dev_err(&mbi_pdev->dev, "PCI config access failed with %d\n", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
int iosf_mbi_read(u8 port, u8 opcode, u32 offset, u32 *mdr)
|
||||
{
|
||||
u32 mcr, mcrx;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
/*Access to the GFX unit is handled by GPU code */
|
||||
if (port == BT_MBI_UNIT_GFX) {
|
||||
WARN_ON(1);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
mcr = iosf_mbi_form_mcr(opcode, port, offset & MBI_MASK_LO);
|
||||
mcrx = offset & MBI_MASK_HI;
|
||||
|
||||
spin_lock_irqsave(&iosf_mbi_lock, flags);
|
||||
ret = iosf_mbi_pci_read_mdr(mcrx, mcr, mdr);
|
||||
spin_unlock_irqrestore(&iosf_mbi_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(iosf_mbi_read);
|
||||
|
||||
int iosf_mbi_write(u8 port, u8 opcode, u32 offset, u32 mdr)
|
||||
{
|
||||
u32 mcr, mcrx;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
/*Access to the GFX unit is handled by GPU code */
|
||||
if (port == BT_MBI_UNIT_GFX) {
|
||||
WARN_ON(1);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
mcr = iosf_mbi_form_mcr(opcode, port, offset & MBI_MASK_LO);
|
||||
mcrx = offset & MBI_MASK_HI;
|
||||
|
||||
spin_lock_irqsave(&iosf_mbi_lock, flags);
|
||||
ret = iosf_mbi_pci_write_mdr(mcrx, mcr, mdr);
|
||||
spin_unlock_irqrestore(&iosf_mbi_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(iosf_mbi_write);
|
||||
|
||||
int iosf_mbi_modify(u8 port, u8 opcode, u32 offset, u32 mdr, u32 mask)
|
||||
{
|
||||
u32 mcr, mcrx;
|
||||
u32 value;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
/*Access to the GFX unit is handled by GPU code */
|
||||
if (port == BT_MBI_UNIT_GFX) {
|
||||
WARN_ON(1);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
mcr = iosf_mbi_form_mcr(opcode, port, offset & MBI_MASK_LO);
|
||||
mcrx = offset & MBI_MASK_HI;
|
||||
|
||||
spin_lock_irqsave(&iosf_mbi_lock, flags);
|
||||
|
||||
/* Read current mdr value */
|
||||
ret = iosf_mbi_pci_read_mdr(mcrx, mcr & MBI_RD_MASK, &value);
|
||||
if (ret < 0) {
|
||||
spin_unlock_irqrestore(&iosf_mbi_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Apply mask */
|
||||
value &= ~mask;
|
||||
mdr &= mask;
|
||||
value |= mdr;
|
||||
|
||||
/* Write back */
|
||||
ret = iosf_mbi_pci_write_mdr(mcrx, mcr | MBI_WR_MASK, value);
|
||||
|
||||
spin_unlock_irqrestore(&iosf_mbi_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(iosf_mbi_modify);
|
||||
|
||||
static int iosf_mbi_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *unused)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = pci_enable_device(pdev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "error: could not enable device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
mbi_pdev = pci_dev_get(pdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(iosf_mbi_pci_ids) = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0F00) },
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, iosf_mbi_pci_ids);
|
||||
|
||||
static struct pci_driver iosf_mbi_pci_driver = {
|
||||
.name = "iosf_mbi_pci",
|
||||
.probe = iosf_mbi_probe,
|
||||
.id_table = iosf_mbi_pci_ids,
|
||||
};
|
||||
|
||||
static int __init iosf_mbi_init(void)
|
||||
{
|
||||
return pci_register_driver(&iosf_mbi_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit iosf_mbi_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&iosf_mbi_pci_driver);
|
||||
if (mbi_pdev) {
|
||||
pci_dev_put(mbi_pdev);
|
||||
mbi_pdev = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
module_init(iosf_mbi_init);
|
||||
module_exit(iosf_mbi_exit);
|
||||
|
||||
MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
|
||||
MODULE_DESCRIPTION("IOSF Mailbox Interface accessor");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -651,6 +651,16 @@ unsigned long native_calibrate_tsc(void)
|
|||
unsigned long flags, latch, ms, fast_calibrate;
|
||||
int hpet = is_hpet_enabled(), i, loopmin;
|
||||
|
||||
/* Calibrate TSC using MSR for Intel Atom SoCs */
|
||||
local_irq_save(flags);
|
||||
i = try_msr_calibrate_tsc(&fast_calibrate);
|
||||
local_irq_restore(flags);
|
||||
if (i >= 0) {
|
||||
if (i == 0)
|
||||
pr_warn("Fast TSC calibration using MSR failed\n");
|
||||
return fast_calibrate;
|
||||
}
|
||||
|
||||
local_irq_save(flags);
|
||||
fast_calibrate = quick_pit_calibrate();
|
||||
local_irq_restore(flags);
|
||||
|
|
127
arch/x86/kernel/tsc_msr.c
Normal file
127
arch/x86/kernel/tsc_msr.c
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* tsc_msr.c - MSR based TSC calibration on Intel Atom SoC platforms.
|
||||
*
|
||||
* TSC in Intel Atom SoC runs at a constant rate which can be figured
|
||||
* by this formula:
|
||||
* <maximum core-clock to bus-clock ratio> * <maximum resolved frequency>
|
||||
* See Intel 64 and IA-32 System Programming Guid section 16.12 and 30.11.5
|
||||
* for details.
|
||||
* Especially some Intel Atom SoCs don't have PIT(i8254) or HPET, so MSR
|
||||
* based calibration is the only option.
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2013 Intel Corporation
|
||||
* Author: Bin Gao <bin.gao@intel.com>
|
||||
*
|
||||
* This file is released under the GPLv2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/setup.h>
|
||||
#include <asm/apic.h>
|
||||
#include <asm/param.h>
|
||||
|
||||
/* CPU reference clock frequency: in KHz */
|
||||
#define FREQ_83 83200
|
||||
#define FREQ_100 99840
|
||||
#define FREQ_133 133200
|
||||
#define FREQ_166 166400
|
||||
|
||||
#define MAX_NUM_FREQS 8
|
||||
|
||||
/*
|
||||
* According to Intel 64 and IA-32 System Programming Guide,
|
||||
* if MSR_PERF_STAT[31] is set, the maximum resolved bus ratio can be
|
||||
* read in MSR_PLATFORM_ID[12:8], otherwise in MSR_PERF_STAT[44:40].
|
||||
* Unfortunately some Intel Atom SoCs aren't quite compliant to this,
|
||||
* so we need manually differentiate SoC families. This is what the
|
||||
* field msr_plat does.
|
||||
*/
|
||||
struct freq_desc {
|
||||
u8 x86_family; /* CPU family */
|
||||
u8 x86_model; /* model */
|
||||
u8 msr_plat; /* 1: use MSR_PLATFORM_INFO, 0: MSR_IA32_PERF_STATUS */
|
||||
u32 freqs[MAX_NUM_FREQS];
|
||||
};
|
||||
|
||||
static struct freq_desc freq_desc_tables[] = {
|
||||
/* PNW */
|
||||
{ 6, 0x27, 0, { 0, 0, 0, 0, 0, FREQ_100, 0, FREQ_83 } },
|
||||
/* CLV+ */
|
||||
{ 6, 0x35, 0, { 0, FREQ_133, 0, 0, 0, FREQ_100, 0, FREQ_83 } },
|
||||
/* TNG */
|
||||
{ 6, 0x4a, 1, { 0, FREQ_100, FREQ_133, 0, 0, 0, 0, 0 } },
|
||||
/* VLV2 */
|
||||
{ 6, 0x37, 1, { 0, FREQ_100, FREQ_133, FREQ_166, 0, 0, 0, 0 } },
|
||||
/* ANN */
|
||||
{ 6, 0x5a, 1, { FREQ_83, FREQ_100, FREQ_133, FREQ_100, 0, 0, 0, 0 } },
|
||||
};
|
||||
|
||||
static int match_cpu(u8 family, u8 model)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(freq_desc_tables); i++) {
|
||||
if ((family == freq_desc_tables[i].x86_family) &&
|
||||
(model == freq_desc_tables[i].x86_model))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Map CPU reference clock freq ID(0-7) to CPU reference clock freq(KHz) */
|
||||
#define id_to_freq(cpu_index, freq_id) \
|
||||
(freq_desc_tables[cpu_index].freqs[freq_id])
|
||||
|
||||
/*
|
||||
* Do MSR calibration only for known/supported CPUs.
|
||||
* Return values:
|
||||
* -1: CPU is unknown/unsupported for MSR based calibration
|
||||
* 0: CPU is known/supported, but calibration failed
|
||||
* 1: CPU is known/supported, and calibration succeeded
|
||||
*/
|
||||
int try_msr_calibrate_tsc(unsigned long *fast_calibrate)
|
||||
{
|
||||
int cpu_index;
|
||||
u32 lo, hi, ratio, freq_id, freq;
|
||||
|
||||
cpu_index = match_cpu(boot_cpu_data.x86, boot_cpu_data.x86_model);
|
||||
if (cpu_index < 0)
|
||||
return -1;
|
||||
|
||||
*fast_calibrate = 0;
|
||||
|
||||
if (freq_desc_tables[cpu_index].msr_plat) {
|
||||
rdmsr(MSR_PLATFORM_INFO, lo, hi);
|
||||
ratio = (lo >> 8) & 0x1f;
|
||||
} else {
|
||||
rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
|
||||
ratio = (hi >> 8) & 0x1f;
|
||||
}
|
||||
pr_info("Maximum core-clock to bus-clock ratio: 0x%x\n", ratio);
|
||||
|
||||
if (!ratio)
|
||||
return 0;
|
||||
|
||||
/* Get FSB FREQ ID */
|
||||
rdmsr(MSR_FSB_FREQ, lo, hi);
|
||||
freq_id = lo & 0x7;
|
||||
freq = id_to_freq(cpu_index, freq_id);
|
||||
pr_info("Resolved frequency ID: %u, frequency: %u KHz\n",
|
||||
freq_id, freq);
|
||||
if (!freq)
|
||||
return 0;
|
||||
|
||||
/* TSC frequency = maximum resolved freq * maximum resolved bus ratio */
|
||||
*fast_calibrate = freq * ratio;
|
||||
pr_info("TSC runs at %lu KHz\n", *fast_calibrate);
|
||||
|
||||
#ifdef CONFIG_X86_LOCAL_APIC
|
||||
lapic_timer_frequency = (freq * 1000) / HZ;
|
||||
pr_info("lapic_timer_frequency = %d\n", lapic_timer_frequency);
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
Loading…
Reference in a new issue