Commit 2521129a authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'char-misc-3.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc

Pull char / misc driver patches from Greg KH:
 "Here's the big driver misc / char pull request for 3.17-rc1.

  Lots of things in here, the thunderbolt support for Apple laptops,
  some other new drivers, testing fixes, and other good things.  All
  have been in linux-next for a long time"

* tag 'char-misc-3.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (119 commits)
  misc: bh1780: Introduce the use of devm_kzalloc
  Lattice ECP3 FPGA: Correct endianness
  drivers/misc/ti-st: Load firmware from ti-connectivity directory.
  dt-bindings: extcon: Add support for SM5502 MUIC device
  extcon: sm5502: Change internal hardware switch according to cable type
  extcon: sm5502: Detect cable state after completing platform booting
  extcon: sm5502: Add support new SM5502 extcon device driver
  extcon: arizona: Get MICVDD against extcon device
  extcon: Remove unnecessary OOM messages
  misc: vexpress: Fix sparse non static symbol warnings
  mei: drop unused hw dependent fw status functions
  misc: bh1770glc: Use managed functions
  pcmcia: remove DEFINE_PCI_DEVICE_TABLE usage
  misc: remove DEFINE_PCI_DEVICE_TABLE usage
  ipack: Replace DEFINE_PCI_DEVICE_TABLE macro use
  drivers/char/dsp56k.c: drop check for negativity of unsigned parameter
  mei: fix return value on disconnect timeout
  mei: don't schedule suspend in pm idle
  mei: start disconnect request timer consistently
  mei: reset client connection state on timeout
  ...
parents 98a96f20 16eb2bfc
What: /sys/class/mei/
Date: May 2014
KernelVersion: 3.17
Contact: Tomas Winkler <tomas.winkler@intel.com>
Description:
The mei/ class sub-directory belongs to mei device class
What: /sys/class/mei/meiN/
Date: May 2014
KernelVersion: 3.17
Contact: Tomas Winkler <tomas.winkler@intel.com>
Description:
The /sys/class/mei/meiN directory is created for
each probed mei device
......@@ -25,6 +25,15 @@ Date: Oct 2013
Contact: haver@linux.vnet.ibm.com
Description: Interface to set the next bitstream to be used.
What: /sys/class/genwqe/genwqe<n>_card/reload_bitstream
Date: May 2014
Contact: klebers@linux.vnet.ibm.com
Description: Interface to trigger a PCIe card reset to reload the bitstream.
sudo sh -c 'echo 1 > \
/sys/class/genwqe/genwqe0_card/reload_bitstream'
If successfully, the card will come back with the bitstream set
on 'next_bitstream'.
What: /sys/class/genwqe/genwqe<n>_card/tempsens
Date: Oct 2013
Contact: haver@linux.vnet.ibm.com
......
* SM5502 MUIC (Micro-USB Interface Controller) device
The Silicon Mitus SM5502 is a MUIC (Micro-USB Interface Controller) device
which can detect the state of external accessory when external accessory is
attached or detached and button is pressed or released. It is interfaced to
the host controller using an I2C interface.
Required properties:
- compatible: Should be "siliconmitus,sm5502-muic"
- reg: Specifies the I2C slave address of the MUIC block. It should be 0x25
- interrupt-parent: Specifies the phandle of the interrupt controller to which
the interrupts from sm5502 are delivered to.
- interrupts: Interrupt specifiers for detection interrupt sources.
Example:
sm5502@25 {
compatible = "siliconmitus,sm5502-muic";
interrupt-parent = <&gpx1>;
interrupts = <5 0>;
reg = <0x25>;
};
......@@ -17,35 +17,50 @@ for applications. A key benefit of our solution is that it leverages
the standard virtio framework for network, disk and console devices,
though in our case the virtio framework is used across a PCIe bus.
MIC PCIe card has a dma controller with 8 channels. These channels are
shared between the host s/w and the card s/w. 0 to 3 are used by host
and 4 to 7 by card. As the dma device doesn't show up as PCIe device,
a virtual bus called mic bus is created and virtual dma devices are
created on it by the host/card drivers. On host the channels are private
and used only by the host driver to transfer data for the virtio devices.
Here is a block diagram of the various components described above. The
virtio backends are situated on the host rather than the card given better
single threaded performance for the host compared to MIC, the ability of
the host to initiate DMA's to/from the card using the MIC DMA engine and
the fact that the virtio block storage backend can only be on the host.
|
+----------+ | +----------+
| Card OS | | | Host OS |
+----------+ | +----------+
|
+-------+ +--------+ +------+ | +---------+ +--------+ +--------+
| Virtio| |Virtio | |Virtio| | |Virtio | |Virtio | |Virtio |
| Net | |Console | |Block | | |Net | |Console | |Block |
| Driver| |Driver | |Driver| | |backend | |backend | |backend |
+-------+ +--------+ +------+ | +---------+ +--------+ +--------+
| | | | | | |
| | | |User | | |
| | | |------|------------|---------|-------
+-------------------+ |Kernel +--------------------------+
| | | Virtio over PCIe IOCTLs |
| | +--------------------------+
+--------------+ | |
|Intel MIC | | +---------------+
|Card Driver | | |Intel MIC |
+--------------+ | |Host Driver |
| | +---------------+
| | |
+-------------------------------------------------------------+
| |
| PCIe Bus |
+-------------------------------------------------------------+
|
+----------+ | +----------+
| Card OS | | | Host OS |
+----------+ | +----------+
|
+-------+ +--------+ +------+ | +---------+ +--------+ +--------+
| Virtio| |Virtio | |Virtio| | |Virtio | |Virtio | |Virtio |
| Net | |Console | |Block | | |Net | |Console | |Block |
| Driver| |Driver | |Driver| | |backend | |backend | |backend |
+-------+ +--------+ +------+ | +---------+ +--------+ +--------+
| | | | | | |
| | | |User | | |
| | | |------|------------|---------|-------
+-------------------+ |Kernel +--------------------------+
| | | Virtio over PCIe IOCTLs |
| | +--------------------------+
+-----------+ | | | +-----------+
| MIC DMA | | | | | MIC DMA |
| Driver | | | | | Driver |
+-----------+ | | | +-----------+
| | | | |
+---------------+ | | | +----------------+
|MIC virtual Bus| | | | |MIC virtual Bus |
+---------------+ | | | +----------------+
| | | | |
| +--------------+ | +---------------+ |
| |Intel MIC | | |Intel MIC | |
+---|Card Driver | | |Host Driver | |
+--------------+ | +---------------+-----+
| | |
+-------------------------------------------------------------+
| |
| PCIe Bus |
+-------------------------------------------------------------+
......@@ -48,18 +48,18 @@ start()
fi
echo -e $"Starting MPSS Stack"
echo -e $"Loading MIC_HOST Module"
echo -e $"Loading MIC_X100_DMA & MIC_HOST Modules"
# Ensure the driver is loaded
if [ ! -d "$sysfs" ]; then
modprobe mic_host
for f in "mic_host" "mic_x100_dma"
do
modprobe $f
RETVAL=$?
if [ $RETVAL -ne 0 ]; then
failure
echo
return $RETVAL
fi
fi
done
# Start the daemon
echo -n $"Starting MPSSD "
......@@ -170,8 +170,8 @@ unload()
stop
sleep 5
echo -n $"Removing MIC_HOST Module: "
modprobe -r mic_host
echo -n $"Removing MIC_HOST & MIC_X100_DMA Modules: "
modprobe -r mic_host mic_x100_dma
RETVAL=$?
[ $RETVAL -ne 0 ] && failure || success
echo
......
w1_ds2406 kernel driver
=======================
Supported chips:
* Maxim DS2406 (and other family 0x12) addressable switches
Author: Scott Alfter <scott@alfter.us>
Description
-----------
The w1_ds2406 driver allows connected devices to be switched on and off.
These chips also provide 128 bytes of OTP EPROM, but reading/writing it is
not supported. In TSOC-6 form, the DS2406 provides two switch outputs and
can be provided with power on a dedicated input. In TO-92 form, it provides
one output and uses parasitic power only.
The driver provides two sysfs files. state is readable; it gives the
current state of each switch, with PIO A in bit 0 and PIO B in bit 1. The
driver ORs this state with 0x30, so shell scripts get an ASCII 0/1/2/3 to
work with. output is writable; bits 0 and 1 control PIO A and B,
respectively. Bits 2-7 are ignored, so it's safe to write ASCII data.
CRCs are checked on read and write. Failed checks cause an I/O error to be
returned. On a failed write, the switch status is not changed.
......@@ -7844,6 +7844,11 @@ S: Maintained
F: include/linux/mmc/dw_mmc.h
F: drivers/mmc/host/dw_mmc*
THUNDERBOLT DRIVER
M: Andreas Noever <andreas.noever@gmail.com>
S: Maintained
F: drivers/thunderbolt/
TIMEKEEPING, CLOCKSOURCE CORE, NTP
M: John Stultz <john.stultz@linaro.org>
M: Thomas Gleixner <tglx@linutronix.de>
......
......@@ -178,4 +178,6 @@ source "drivers/mcb/Kconfig"
source "drivers/ras/Kconfig"
source "drivers/thunderbolt/Kconfig"
endmenu
......@@ -159,3 +159,4 @@ obj-$(CONFIG_FMC) += fmc/
obj-$(CONFIG_POWERCAP) += powercap/
obj-$(CONFIG_MCB) += mcb/
obj-$(CONFIG_RAS) += ras/
obj-$(CONFIG_THUNDERBOLT) += thunderbolt/
......@@ -259,7 +259,7 @@ static int bsr_add_node(struct device_node *bn)
}
cur->bsr_device = device_create(bsr_class, NULL, cur->bsr_dev,
cur, cur->bsr_name);
cur, "%s", cur->bsr_name);
if (IS_ERR(cur->bsr_device)) {
printk(KERN_ERR "device_create failed for %s\n",
cur->bsr_name);
......
......@@ -383,7 +383,7 @@ static long dsp56k_ioctl(struct file *file, unsigned int cmd,
return put_user(status, &hf->status);
}
case DSP56K_HOST_CMD:
if (arg > 31 || arg < 0)
if (arg > 31)
return -EINVAL;
mutex_lock(&dsp56k_mutex);
dsp56k_host_interface.cvr = (u_char)((arg & DSP56K_CVR_HV_MASK) |
......
......@@ -65,6 +65,8 @@ static char bios_version[4];
static struct device *i8k_hwmon_dev;
static u32 i8k_hwmon_flags;
static int i8k_fan_mult;
static int i8k_pwm_mult;
static int i8k_fan_max = I8K_FAN_HIGH;
#define I8K_HWMON_HAVE_TEMP1 (1 << 0)
#define I8K_HWMON_HAVE_TEMP2 (1 << 1)
......@@ -97,6 +99,10 @@ static int fan_mult = I8K_FAN_MULT;
module_param(fan_mult, int, 0);
MODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with");
static int fan_max = I8K_FAN_HIGH;
module_param(fan_max, int, 0);
MODULE_PARM_DESC(fan_max, "Maximum configurable fan speed");
static int i8k_open_fs(struct inode *inode, struct file *file);
static long i8k_ioctl(struct file *, unsigned int, unsigned long);
......@@ -276,7 +282,7 @@ static int i8k_set_fan(int fan, int speed)
{
struct smm_regs regs = { .eax = I8K_SMM_SET_FAN, };
speed = (speed < 0) ? 0 : ((speed > I8K_FAN_MAX) ? I8K_FAN_MAX : speed);
speed = (speed < 0) ? 0 : ((speed > i8k_fan_max) ? i8k_fan_max : speed);
regs.ebx = (fan & 0xff) | (speed << 8);
return i8k_smm(&regs) ? : i8k_get_fan_status(fan);
......@@ -521,7 +527,7 @@ static ssize_t i8k_hwmon_show_pwm(struct device *dev,
status = i8k_get_fan_status(index);
if (status < 0)
return -EIO;
return sprintf(buf, "%d\n", clamp_val(status * 128, 0, 255));
return sprintf(buf, "%d\n", clamp_val(status * i8k_pwm_mult, 0, 255));
}
static ssize_t i8k_hwmon_set_pwm(struct device *dev,
......@@ -535,7 +541,7 @@ static ssize_t i8k_hwmon_set_pwm(struct device *dev,
err = kstrtoul(buf, 10, &val);
if (err)
return err;
val = clamp_val(DIV_ROUND_CLOSEST(val, 128), 0, 2);
val = clamp_val(DIV_ROUND_CLOSEST(val, i8k_pwm_mult), 0, i8k_fan_max);
mutex_lock(&i8k_mutex);
err = i8k_set_fan(index, val);
......@@ -544,20 +550,6 @@ static ssize_t i8k_hwmon_set_pwm(struct device *dev,
return err < 0 ? -EIO : count;
}
static ssize_t i8k_hwmon_show_label(struct device *dev,
struct device_attribute *devattr,
char *buf)
{
static const char *labels[3] = {
"CPU",
"Left Fan",
"Right Fan",
};
int index = to_sensor_dev_attr(devattr)->index;
return sprintf(buf, "%s\n", labels[index]);
}
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 2);
......@@ -570,41 +562,34 @@ static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, i8k_hwmon_show_fan, NULL,
I8K_FAN_RIGHT);
static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, i8k_hwmon_show_pwm,
i8k_hwmon_set_pwm, I8K_FAN_RIGHT);
static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 0);
static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 1);
static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, i8k_hwmon_show_label, NULL, 2);
static struct attribute *i8k_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr, /* 0 */
&sensor_dev_attr_temp1_label.dev_attr.attr, /* 1 */
&sensor_dev_attr_temp2_input.dev_attr.attr, /* 2 */
&sensor_dev_attr_temp3_input.dev_attr.attr, /* 3 */
&sensor_dev_attr_temp4_input.dev_attr.attr, /* 4 */
&sensor_dev_attr_fan1_input.dev_attr.attr, /* 5 */
&sensor_dev_attr_pwm1.dev_attr.attr, /* 6 */
&sensor_dev_attr_fan1_label.dev_attr.attr, /* 7 */
&sensor_dev_attr_fan2_input.dev_attr.attr, /* 8 */
&sensor_dev_attr_pwm2.dev_attr.attr, /* 9 */
&sensor_dev_attr_fan2_label.dev_attr.attr, /* 10 */
&sensor_dev_attr_temp2_input.dev_attr.attr, /* 1 */
&sensor_dev_attr_temp3_input.dev_attr.attr, /* 2 */
&sensor_dev_attr_temp4_input.dev_attr.attr, /* 3 */
&sensor_dev_attr_fan1_input.dev_attr.attr, /* 4 */
&sensor_dev_attr_pwm1.dev_attr.attr, /* 5 */
&sensor_dev_attr_fan2_input.dev_attr.attr, /* 6 */
&sensor_dev_attr_pwm2.dev_attr.attr, /* 7 */
NULL
};
static umode_t i8k_is_visible(struct kobject *kobj, struct attribute *attr,
int index)
{
if ((index == 0 || index == 1) &&
!(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP1))
if (index == 0 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP1))
return 0;
if (index == 2 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP2))
if (index == 1 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP2))
return 0;
if (index == 3 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP3))
if (index == 2 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP3))
return 0;
if (index == 4 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP4))
if (index == 3 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP4))
return 0;
if (index >= 5 && index <= 7 &&
if (index >= 4 && index <= 5 &&
!(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN1))
return 0;
if (index >= 8 && index <= 10 &&
if (index >= 6 && index <= 7 &&
!(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN2))
return 0;
......@@ -659,6 +644,37 @@ static int __init i8k_init_hwmon(void)
return 0;
}
struct i8k_config_data {
int fan_mult;
int fan_max;
};
enum i8k_configs {
DELL_LATITUDE_D520,
DELL_PRECISION_490,
DELL_STUDIO,
DELL_XPS_M140,
};
static const struct i8k_config_data i8k_config_data[] = {
[DELL_LATITUDE_D520] = {
.fan_mult = 1,
.fan_max = I8K_FAN_TURBO,
},
[DELL_PRECISION_490] = {
.fan_mult = 1,
.fan_max = I8K_FAN_TURBO,
},
[DELL_STUDIO] = {
.fan_mult = 1,
.fan_max = I8K_FAN_HIGH,
},
[DELL_XPS_M140] = {
.fan_mult = 1,
.fan_max = I8K_FAN_HIGH,
},
};
static struct dmi_system_id i8k_dmi_table[] __initdata = {
{
.ident = "Dell Inspiron",
......@@ -681,6 +697,14 @@ static struct dmi_system_id i8k_dmi_table[] __initdata = {
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"),
},
},
{
.ident = "Dell Latitude D520",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D520"),
},
.driver_data = (void *)&i8k_config_data[DELL_LATITUDE_D520],
},
{
.ident = "Dell Latitude 2",
.matches = {
......@@ -702,6 +726,15 @@ static struct dmi_system_id i8k_dmi_table[] __initdata = {
DMI_MATCH(DMI_PRODUCT_NAME, "MP061"),
},
},
{
.ident = "Dell Precision 490",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME,
"Precision WorkStation 490"),
},
.driver_data = (void *)&i8k_config_data[DELL_PRECISION_490],
},
{
.ident = "Dell Precision",
.matches = {
......@@ -729,7 +762,7 @@ static struct dmi_system_id i8k_dmi_table[] __initdata = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Studio"),
},
.driver_data = (void *)1, /* fan multiplier override */
.driver_data = (void *)&i8k_config_data[DELL_STUDIO],
},
{
.ident = "Dell XPS M140",
......@@ -737,7 +770,7 @@ static struct dmi_system_id i8k_dmi_table[] __initdata = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "MXC051"),
},
.driver_data = (void *)1, /* fan multiplier override */
.driver_data = (void *)&i8k_config_data[DELL_XPS_M140],
},
{ }
};
......@@ -777,9 +810,17 @@ static int __init i8k_probe(void)
}
i8k_fan_mult = fan_mult;
i8k_fan_max = fan_max ? : I8K_FAN_HIGH; /* Must not be 0 */
id = dmi_first_match(i8k_dmi_table);
if (id && fan_mult == I8K_FAN_MULT && id->driver_data)
i8k_fan_mult = (unsigned long)id->driver_data;
if (id && id->driver_data) {
const struct i8k_config_data *conf = id->driver_data;
if (fan_mult == I8K_FAN_MULT && conf->fan_mult)
i8k_fan_mult = conf->fan_mult;
if (fan_max == I8K_FAN_HIGH && conf->fan_max)
i8k_fan_max = conf->fan_max;
}
i8k_pwm_mult = DIV_ROUND_UP(255, i8k_fan_max);
return 0;
}
......
......@@ -661,6 +661,7 @@ static int hwicap_setup(struct device *dev, int id,
drvdata->base_address = ioremap(drvdata->mem_start, drvdata->mem_size);
if (!drvdata->base_address) {
dev_err(dev, "ioremap() failed\n");
retval = -ENOMEM;
goto failed2;
}
......
......@@ -33,6 +33,24 @@ if DMADEVICES
comment "DMA Devices"
config INTEL_MIC_X100_DMA
tristate "Intel MIC X100 DMA Driver"
depends on 64BIT && X86 && INTEL_MIC_BUS
select DMA_ENGINE
help
This enables DMA support for the Intel Many Integrated Core
(MIC) family of PCIe form factor coprocessor X100 devices that
run a 64 bit Linux OS. This driver will be used by both MIC
host and card drivers.
If you are building host kernel with a MIC device or a card
kernel for a MIC device, then say M (recommended) or Y, else
say N. If unsure say N.
More information about the Intel MIC family as well as the Linux
OS and tools for MIC to use with this driver are available from
<http://software.intel.com/en-us/mic-developer>.
config INTEL_MID_DMAC
tristate "Intel MID DMA support for Peripheral DMA controllers"
depends on PCI && X86
......
......@@ -47,3 +47,4 @@ obj-$(CONFIG_MOXART_DMA) += moxart-dma.o
obj-$(CONFIG_FSL_EDMA) += fsl-edma.o
obj-$(CONFIG_QCOM_BAM_DMA) += qcom_bam_dma.o
obj-y += xilinx/
obj-$(CONFIG_INTEL_MIC_X100_DMA) += mic_x100_dma.o
This diff is collapsed.
/*
* Intel MIC Platform Software Stack (MPSS)
*
* Copyright(c) 2014 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 X100 DMA Driver.
*
* Adapted from IOAT dma driver.
*/
#ifndef _MIC_X100_DMA_H_
#define _MIC_X100_DMA_H_
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/mic_bus.h>
#include "dmaengine.h"
/*
* MIC has a total of 8 dma channels.
* Four channels are assigned for host SW use & the remaining for MIC SW.
* MIC DMA transfer size & addresses need to be 64 byte aligned.
*/
#define MIC_DMA_MAX_NUM_CHAN 8
#define MIC_DMA_NUM_CHAN 4
#define MIC_DMA_ALIGN_SHIFT 6
#define MIC_DMA_ALIGN_BYTES (1 << MIC_DMA_ALIGN_SHIFT)
#define MIC_DMA_DESC_RX_SIZE (128 * 1024 - 4)
/*
* Register descriptions
* All the registers are 32 bit registers.
* DCR is a global register and all others are per-channel.
* DCR - bits 0, 2, 4, 6, 8, 10, 12, 14 - enable bits for channels 0 to 7
* bits 1, 3, 5, 7, 9, 11, 13, 15 - owner bits for channels 0 to 7
* DCAR - bit 24 & 25 interrupt masks for mic owned & host owned channels
* DHPR - head of the descriptor ring updated by s/w
* DTPR - tail of the descriptor ring updated by h/w
* DRAR_LO - lower 32 bits of descriptor ring's mic address
* DRAR_HI - 3:0 - remaining 4 bits of descriptor ring's mic address
* 20:4 descriptor ring size
* 25:21 mic smpt entry number
* DSTAT - 16:0 h/w completion count; 31:28 dma engine status
* DCHERR - this register is non-zero on error
* DCHERRMSK - interrupt mask register
*/
#define MIC_DMA_HW_CMP_CNT_MASK 0x1ffff
#define MIC_DMA_CHAN_QUIESCE 0x20000000
#define MIC_DMA_SBOX_BASE 0x00010000
#define MIC_DMA_SBOX_DCR 0x0000A280
#define MIC_DMA_SBOX_CH_BASE 0x0001A000
#define MIC_DMA_SBOX_CHAN_OFF 0x40
#define MIC_DMA_SBOX_DCAR_IM0 (0x1 << 24)
#define MIC_DMA_SBOX_DCAR_IM1 (0x1 << 25)
#define MIC_DMA_SBOX_DRARHI_SYS_MASK (0x1 << 26)
#define MIC_DMA_REG_DCAR 0
#define MIC_DMA_REG_DHPR 4
#define MIC_DMA_REG_DTPR 8
#define MIC_DMA_REG_DRAR_LO 20
#define MIC_DMA_REG_DRAR_HI 24
#define MIC_DMA_REG_DSTAT 32
#define MIC_DMA_REG_DCHERR 44
#define MIC_DMA_REG_DCHERRMSK 48
/* HW dma desc */
struct mic_dma_desc {
u64 qw0;
u64 qw1;
};
enum mic_dma_chan_owner {
MIC_DMA_CHAN_MIC = 0,
MIC_DMA_CHAN_HOST
};
/*
* mic_dma_chan - channel specific information
* @ch_num: channel number
* @owner: owner of this channel
* @last_tail: cached value of descriptor ring tail
* @head: index of next descriptor in desc_ring
* @issued: hardware notification point
* @submitted: index that will be used to submit descriptors to h/w
* @api_ch: dma engine api channel
* @desc_ring: dma descriptor ring
* @desc_ring_micpa: mic physical address of desc_ring
* @status_dest: destination for status (fence) descriptor
* @status_dest_micpa: mic address for status_dest,
* DMA controller uses this address
* @tx_array: array of async_tx
* @cleanup_lock: lock held when processing completed tx
* @prep_lock: lock held in prep_memcpy & released in tx_submit
* @issue_lock: lock used to synchronize writes to head
* @cookie: mic_irq cookie used with mic irq request
*/
struct mic_dma_chan {
int ch_num;
enum mic_dma_chan_owner owner;
u32 last_tail;
u32 head;
u32 issued;
u32 submitted;
struct dma_chan api_ch;
struct mic_dma_desc *desc_ring;
dma_addr_t desc_ring_micpa;
u64 *status_dest;
dma_addr_t status_dest_micpa;
struct dma_async_tx_descriptor *tx_array;
spinlock_t cleanup_lock;
spinlock_t prep_lock;
spinlock_t issue_lock;
struct mic_irq *cookie;
};
/*
* struct mic_dma_device - per mic device
* @mic_ch: dma channels
* @dma_dev: underlying dma device
* @mbdev: mic bus dma device
* @mmio: virtual address of the mmio space
* @dbg_dir: debugfs directory
* @start_ch: first channel number that can be used
* @max_xfer_size: maximum transfer size per dma descriptor
*/
struct mic_dma_device {
struct mic_dma_chan mic_ch[MIC_DMA_MAX_NUM_CHAN];
struct dma_device dma_dev;
struct mbus_device *mbdev;
void __iomem *mmio;
struct dentry *dbg_dir;
int start_ch;
size_t max_xfer_size;
};
static inline struct mic_dma_chan *to_mic_dma_chan(struct dma_chan *ch)
{
return container_of(ch, struct mic_dma_chan, api_ch);
}
static inline struct mic_dma_device *to_mic_dma_dev(struct mic_dma_chan *ch)
{
return
container_of((const typeof(((struct mic_dma_device *)0)->mic_ch)*)
(ch - ch->ch_num), struct mic_dma_device, mic_ch);
}
static inline struct mbus_device *to_mbus_device(struct mic_dma_chan *ch)
{
return to_mic_dma_dev(ch)->mbdev;
}
static inline struct mbus_hw_ops *to_mbus_hw_ops(struct mic_dma_chan *ch)
{
return to_mbus_device(ch)->hw_ops;
}
static inline struct device *mic_dma_ch_to_device(struct mic_dma_chan *ch)
{
return to_mic_dma_dev(ch)->dma_dev.dev;
}
static inline void __iomem *mic_dma_chan_to_mmio(struct mic_dma_chan *ch)
{
return to_mic_dma_dev(ch)->mmio;
}
static inline u32 mic_dma_read_reg(struct mic_dma_chan *ch, u32 reg)
{
return ioread32(mic_dma_chan_to_mmio(ch) + MIC_DMA_SBOX_CH_BASE +
ch->ch_num * MIC_DMA_SBOX_CHAN_OFF + reg);
}
static inline void mic_dma_write_reg(struct mic_dma_chan *ch, u32 reg, u32 val)
{
iowrite32(val, mic_dma_chan_to_mmio(ch) + MIC_DMA_SBOX_CH_BASE +
ch->ch_num * MIC_DMA_SBOX_CHAN_OFF + reg);
}
static inline u32 mic_dma_mmio_read(struct mic_dma_chan *ch, u32 offset)
{
return ioread32(mic_dma_chan_to_mmio(ch) + offset);
}
static inline void mic_dma_mmio_write(struct mic_dma_chan *ch, u32 val,
u32 offset)
{
iowrite32(val, mic_dma_chan_to_mmio(ch) + offset);
}
static inline u32 mic_dma_read_cmp_cnt(struct mic_dma_chan *ch)
{
return mic_dma_read_reg(ch, MIC_DMA_REG_DSTAT) &
MIC_DMA_HW_CMP_CNT_MASK;
}
static inline void mic_dma_chan_set_owner(struct mic_dma_chan *ch)
{
u32 dcr = mic_dma_mmio_read(ch, MIC_DMA_SBOX_BASE + MIC_DMA_SBOX_DCR);
u32 chan_num = ch->ch_num;
dcr = (dcr & ~(0x1 << (chan_num * 2))) | (ch->owner << (chan_num * 2));
mic_dma_mmio_write(ch, dcr, MIC_DMA_SBOX_BASE + MIC_DMA_SBOX_DCR);
}
static inline void mic_dma_enable_chan(struct mic_dma_chan *ch)
{
u32 dcr = mic_dma_mmio_read(ch, MIC_DMA_SBOX_BASE + MIC_DMA_SBOX_DCR);
dcr |= 2 << (ch->ch_num << 1);
mic_dma_mmio_write(ch, dcr, MIC_DMA_SBOX_BASE + MIC_DMA_SBOX_DCR);
}
static inline void mic_dma_disable_chan(struct mic_dma_chan *ch)
{
u32 dcr = mic_dma_mmio_read(ch, MIC_DMA_SBOX_BASE + MIC_DMA_SBOX_DCR);
dcr &= ~(2 << (ch->ch_num << 1));
mic_dma_mmio_write(ch, dcr, MIC_DMA_SBOX_BASE + MIC_DMA_SBOX_DCR);
}
static void mic_dma_chan_set_desc_ring(struct mic_dma_chan *ch)
{
u32 drar_hi;
dma_addr_t desc_ring_micpa = ch->desc_ring_micpa;
drar_hi = (MIC_DMA_DESC_RX_SIZE & 0x1ffff) << 4;
if (MIC_DMA_CHAN_MIC == ch->owner) {
drar_hi |= (desc_ring_micpa >> 32) & 0xf;
} else {
drar_hi |= MIC_DMA_SBOX_DRARHI_SYS_MASK;
drar_hi |= ((desc_ring_micpa >> 34)
& 0x1f) << 21;
drar_hi |= (desc_ring_micpa >> 32) & 0x3;
}
mic_dma_write_reg(ch, MIC_DMA_REG_DRAR_LO, (u32) desc_ring_micpa);
mic_dma_write_reg(ch, MIC_DMA_REG_DRAR_HI, drar_hi);
}
static inline void mic_dma_chan_mask_intr(struct mic_dma_chan *ch)
{
u32 dcar = mic_dma_read_reg(ch, MIC_DMA_REG_DCAR);
if (MIC_DMA_CHAN_MIC == ch->owner)
dcar |= MIC_DMA_SBOX_DCAR_IM0;
else
dcar |= MIC_DMA_SBOX_DCAR_IM1;
mic_dma_write_reg(ch, MIC_DMA_REG_DCAR, dcar);
}
static inline void mic_dma_chan_unmask_intr(struct mic_dma_chan *ch)
{
u32 dcar = mic_dma_read_reg(ch, MIC_DMA_REG_DCAR);
if (MIC_DMA_CHAN_MIC == ch->owner)
dcar &= ~MIC_DMA_SBOX_DCAR_IM0;
else
dcar &= ~MIC_DMA_SBOX_DCAR_IM1;
mic_dma_write_reg(ch, MIC_DMA_REG_DCAR, dcar);
}
static void mic_dma_ack_interrupt(struct mic_dma_chan *ch)
{
if (MIC_DMA_CHAN_MIC == ch->owner) {
/* HW errata */
mic_dma_chan_mask_intr(ch);
mic_dma_chan_unmask_intr(ch);
}
to_mbus_hw_ops(ch)->ack_interrupt(to_mbus_device(ch), ch->ch_num);
}
#endif
......@@ -14,6 +14,20 @@ if EXTCON
comment "Extcon Device Drivers"
config EXTCON_ADC_JACK
tristate "ADC Jack extcon support"
depends on IIO
help
Say Y here to enable extcon device driver based on ADC values.
config EXTCON_ARIZONA
tristate "Wolfson Arizona EXTCON support"
depends on MFD_ARIZONA && INPUT && SND_SOC
help
Say Y here to enable support for external accessory detection
with Wolfson Arizona devices. These are audio CODECs with
advanced audio accessory detection support.
config EXTCON_GPIO
tristate "GPIO extcon support"
depends on GPIOLIB
......@@ -21,12 +35,6 @@ config EXTCON_GPIO
Say Y here to enable GPIO based extcon support. Note that GPIO
extcon supports single state per extcon instance.
config EXTCON_ADC_JACK
tristate "ADC Jack extcon support"
depends on IIO
help
Say Y here to enable extcon device driver based on ADC values.
config EXTCON_MAX14577
tristate "MAX14577/77836 EXTCON Support"
depends on MFD_MAX14577
......@@ -55,14 +63,6 @@ config EXTCON_MAX8997
Maxim MAX8997 PMIC. The MAX8997 MUIC is a USB port accessory
detector and switch.
config EXTCON_ARIZONA
tristate "Wolfson Arizona EXTCON support"
depends on MFD_ARIZONA && INPUT && SND_SOC
help
Say Y here to enable support for external accessory detection
with Wolfson Arizona devices. These are audio CODECs with
advanced audio accessory detection support.
config EXTCON_PALMAS
tristate "Palmas USB EXTCON support"
depends on MFD_PALMAS
......@@ -70,4 +70,14 @@ config EXTCON_PALMAS
Say Y here to enable support for USB peripheral and USB host
detection by palmas usb.
config EXTCON_SM5502
tristate "SM5502 EXTCON support"
select IRQ_DOMAIN
select REGMAP_I2C
select REGMAP_IRQ
help
If you say yes here you get support for the MUIC device of
Silicon Mitus SM5502. The SM5502 is a USB port accessory
detector and switch.
endif # MULTISTATE_SWITCH
#
# Makefile for external connector class (extcon) devices
#
obj-$(CONFIG_EXTCON) += extcon-class.o
obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o
obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o
obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o
obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o
obj-$(CONFIG_EXTCON_MAX14577) += extcon-max14577.o
obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o
obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o
obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o
obj-$(CONFIG_EXTCON_PALMAS) += extcon-palmas.o
obj-$(CONFIG_EXTCON_SM5502) += extcon-sm5502.o
......@@ -112,7 +112,6 @@ static int adc_jack_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to allocate extcon device\n");
return -ENOMEM;
}
data->edev->dev.parent = &pdev->dev;
data->edev->name = pdata->name;
/* Check the length of array and set num_cables */
......
......@@ -39,6 +39,11 @@
#define ARIZONA_ACCDET_MODE_HPL 1
#define ARIZONA_ACCDET_MODE_HPR 2
#define ARIZONA_MICD_CLAMP_MODE_JDL 0x4
#define ARIZONA_MICD_CLAMP_MODE_JDH 0x5
#define ARIZONA_MICD_CLAMP_MODE_JDL_GP5H 0x9
#define ARIZONA_MICD_CLAMP_MODE_JDH_GP5H 0xb
#define ARIZONA_HPDET_MAX 10000
#define HPDET_DEBOUNCE 500
......@@ -324,14 +329,17 @@ static void arizona_stop_mic(struct arizona_extcon_info *info)
}
static struct {
unsigned int threshold;
unsigned int factor_a;
unsigned int factor_b;
} arizona_hpdet_b_ranges[] = {
{ 5528, 362464 },
{ 11084, 6186851 },
{ 11065, 65460395 },
{ 100, 5528, 362464 },
{ 169, 11084, 6186851 },
{ 169, 11065, 65460395 },
};
#define ARIZONA_HPDET_B_RANGE_MAX 0x3fb
static struct {
int min;
int max;
......@@ -386,7 +394,8 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
>> ARIZONA_HP_IMPEDANCE_RANGE_SHIFT;
if (range < ARRAY_SIZE(arizona_hpdet_b_ranges) - 1 &&
(val < 100 || val >= 0x3fb)) {
(val < arizona_hpdet_b_ranges[range].threshold ||
val >= ARIZONA_HPDET_B_RANGE_MAX)) {
range++;
dev_dbg(arizona->dev, "Moving to HPDET range %d\n",
range);
......@@ -399,7 +408,8 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
}
/* If we go out of range report top of range */
if (val < 100 || val >= 0x3fb) {
if (val < arizona_hpdet_b_ranges[range].threshold ||
val >= ARIZONA_HPDET_B_RANGE_MAX) {
dev_dbg(arizona->dev, "Measurement out of range\n");
return ARIZONA_HPDET_MAX;
}
......@@ -664,9 +674,8 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info)
ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
/* Just report headphone */
ret = extcon_update_state(info->edev,
1 << ARIZONA_CABLE_HEADPHONE,
1 << ARIZONA_CABLE_HEADPHONE);
ret = extcon_set_cable_state_(info->edev,
ARIZONA_CABLE_HEADPHONE, true);
if (ret != 0)
dev_err(arizona->dev, "Failed to report headphone: %d\n", ret);
......@@ -723,9 +732,8 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC);
/* Just report headphone */
ret = extcon_update_state(info->edev,
1 << ARIZONA_CABLE_HEADPHONE,
1 << ARIZONA_CABLE_HEADPHONE);
ret = extcon_set_cable_state_(info->edev,
ARIZONA_CABLE_HEADPHONE, true);
if (ret != 0)
dev_err(arizona->dev, "Failed to report headphone: %d\n", ret);
......@@ -812,16 +820,15 @@ static void arizona_micd_detect(struct work_struct *work)
if (info->detecting && (val & ARIZONA_MICD_LVL_8)) {
arizona_identify_headphone(info);
ret = extcon_update_state(info->edev,
1 << ARIZONA_CABLE_MICROPHONE,
1 << ARIZONA_CABLE_MICROPHONE);
ret = extcon_set_cable_state_(info->edev,
ARIZONA_CABLE_MICROPHONE, true);
if (ret != 0)
dev_err(arizona->dev, "Headset report failed: %d\n",
ret);
/* Don't need to regulate for button detection */
ret = regulator_allow_bypass(info->micvdd, false);
ret = regulator_allow_bypass(info->micvdd, true);
if (ret != 0) {
dev_err(arizona->dev, "Failed to bypass MICVDD: %d\n",
ret);
......@@ -962,10 +969,16 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
if (arizona->pdata.jd_gpio5) {
mask = ARIZONA_MICD_CLAMP_STS;
present = 0;
if (arizona->pdata.jd_invert)
present = ARIZONA_MICD_CLAMP_STS;
else
present = 0;
} else {
mask = ARIZONA_JD1_STS;
present = ARIZONA_JD1_STS;
if (arizona->pdata.jd_invert)
present = 0;
else
present = ARIZONA_JD1_STS;
}
ret = regmap_read(arizona->regmap, ARIZONA_AOD_IRQ_RAW_STATUS, &val);
......@@ -1096,6 +1109,7 @@ static int arizona_extcon_probe(struct platform_device *pdev)
struct arizona_pdata *pdata = &arizona->pdata;
struct arizona_extcon_info *info;
unsigned int val;
unsigned int clamp_mode;
int jack_irq_fall, jack_irq_rise;
int ret, mode, i, j;
......@@ -1103,12 +1117,10 @@ static int arizona_extcon_probe(struct platform_device *pdev)
return -EPROBE_DEFER;
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info) {
dev_err(&pdev->dev, "Failed to allocate memory\n");
if (!info)
return -ENOMEM;
}
info->micvdd = devm_regulator_get(arizona->dev, "MICVDD");
info->micvdd = devm_regulator_get(&pdev->dev, "MICVDD");
if (IS_ERR(info->micvdd)) {
ret = PTR_ERR(info->micvdd);
dev_err(arizona->dev, "Failed to get MICVDD: %d\n", ret);
......@@ -1156,7 +1168,6 @@ static int arizona_extcon_probe(struct platform_device *pdev)
return -ENOMEM;
}
info->edev->name = "Headset Jack";
info->edev->dev.parent = arizona->dev;
ret = devm_extcon_dev_register(&pdev->dev, info->edev);
if (ret < 0) {
......@@ -1174,7 +1185,6 @@ static int arizona_extcon_probe(struct platform_device *pdev)
info->input->name = "Headset";
info->input->phys = "arizona/extcon";
info->input->dev.parent = &pdev->dev;
if (pdata->num_micd_configs) {
info->micd_modes = pdata->micd_configs;
......@@ -1305,15 +1315,21 @@ static int arizona_extcon_probe(struct platform_device *pdev)
regmap_write(arizona->regmap, ARIZONA_GPIO5_CTRL,
val);
regmap_update_bits(arizona->regmap,
ARIZONA_MICD_CLAMP_CONTROL,
ARIZONA_MICD_CLAMP_MODE_MASK, 0x9);
if (arizona->pdata.jd_invert)
clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDH_GP5H;
else
clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDL_GP5H;
} else {
regmap_update_bits(arizona->regmap,
ARIZONA_MICD_CLAMP_CONTROL,
ARIZONA_MICD_CLAMP_MODE_MASK, 0x4);
if (arizona->pdata.jd_invert)
clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDH;
else
clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDL;
}
regmap_update_bits(arizona->regmap,
ARIZONA_MICD_CLAMP_CONTROL,
ARIZONA_MICD_CLAMP_MODE_MASK, clamp_mode);
regmap_update_bits(arizona->regmap,
ARIZONA_JACK_DETECT_DEBOUNCE,
ARIZONA_MICD_CLAMP_DB,
......
......@@ -645,6 +645,8 @@ struct extcon_dev *devm_extcon_dev_allocate(struct device *dev,
return edev;
}
edev->dev.parent = dev;
*ptr = edev;
devres_add(dev, ptr);
......
......@@ -105,7 +105,6 @@ static int gpio_extcon_probe(struct platform_device *pdev)
return -ENOMEM;
}
extcon_data->edev->name = pdata->name;
extcon_data->edev->dev.parent = &pdev->dev;
extcon_data->gpio = pdata->gpio;
extcon_data->gpio_active_low = pdata->gpio_active_low;
......
......@@ -692,10 +692,9 @@ static int max14577_muic_probe(struct platform_device *pdev)
u8 id;
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info) {
dev_err(&pdev->dev, "failed to allocate memory\n");
if (!info)
return -ENOMEM;
}
info->dev = &pdev->dev;
info->max14577 = max14577;
......
......@@ -255,10 +255,10 @@ static int max77693_muic_set_debounce_time(struct max77693_muic_info *info,
case ADC_DEBOUNCE_TIME_10MS:
case ADC_DEBOUNCE_TIME_25MS:
case ADC_DEBOUNCE_TIME_38_62MS:
ret = max77693_update_reg(info->max77693->regmap_muic,
ret = regmap_update_bits(info->max77693->regmap_muic,
MAX77693_MUIC_REG_CTRL3,
time << CONTROL3_ADCDBSET_SHIFT,
CONTROL3_ADCDBSET_MASK);
CONTROL3_ADCDBSET_MASK,
time << CONTROL3_ADCDBSET_SHIFT);
if (ret) {
dev_err(info->dev, "failed to set ADC debounce time\n");
return ret;
......@@ -286,15 +286,15 @@ static int max77693_muic_set_path(struct max77693_muic_info *info,
u8 val, bool attached)
{
int ret = 0;
u8 ctrl1, ctrl2 = 0;
unsigned int ctrl1, ctrl2 = 0;
if (attached)
ctrl1 = val;
else
ctrl1 = CONTROL1_SW_OPEN;
ret = max77693_update_reg(info->max77693->regmap_muic,
MAX77693_MUIC_REG_CTRL1, ctrl1, COMP_SW_MASK);
ret = regmap_update_bits(info->max77693->regmap_muic,
MAX77693_MUIC_REG_CTRL1, COMP_SW_MASK, ctrl1);
if (ret < 0) {
dev_err(info->dev, "failed to update MUIC register\n");
return ret;
......@@ -305,9 +305,9 @@ static int max77693_muic_set_path(struct max77693_muic_info *info,
else
ctrl2 |= CONTROL2_LOWPWR_MASK; /* LowPwr=1, CPEn=0 */
ret = max77693_update_reg(info->max77693->regmap_muic,
MAX77693_MUIC_REG_CTRL2, ctrl2,
CONTROL2_LOWPWR_MASK | CONTROL2_CPEN_MASK);
ret = regmap_update_bits(info->max77693->regmap_muic,
MAX77693_MUIC_REG_CTRL2,
CONTROL2_LOWPWR_MASK | CONTROL2_CPEN_MASK, ctrl2);
if (ret < 0) {
dev_err(info->dev, "failed to update MUIC register\n");
return ret;
......@@ -969,8 +969,8 @@ static void max77693_muic_irq_work(struct work_struct *work)
if (info->irq == muic_irqs[i].virq)
irq_type = muic_irqs[i].irq;
ret = max77693_bulk_read(info->max77693->regmap_muic,
MAX77693_MUIC_REG_STATUS1, 2, info->status);
ret = regmap_bulk_read(info->max77693->regmap_muic,
MAX77693_MUIC_REG_STATUS1, info->status, 2);
if (ret) {
dev_err(info->dev, "failed to read MUIC register\n");
mutex_unlock(&info->mutex);
......@@ -1042,8 +1042,8 @@ static int max77693_muic_detect_accessory(struct max77693_muic_info *info)
mutex_lock(&info->mutex);
/* Read STATUSx register to detect accessory */
ret = max77693_bulk_read(info->max77693->regmap_muic,
MAX77693_MUIC_REG_STATUS1, 2, info->status);
ret = regmap_bulk_read(info->max77693->regmap_muic,
MAX77693_MUIC_REG_STATUS1, info->status, 2);
if (ret) {
dev_err(info->dev, "failed to read MUIC register\n");
mutex_unlock(&info->mutex);
......@@ -1095,14 +1095,13 @@ static int max77693_muic_probe(struct platform_device *pdev)
int delay_jiffies;
int ret;
int i;
u8 id;
unsigned int id;
info = devm_kzalloc(&pdev->dev, sizeof(struct max77693_muic_info),
GFP_KERNEL);
if (!info) {
dev_err(&pdev->dev, "failed to allocate memory\n");
if (!info)
return -ENOMEM;
}
info->dev = &pdev->dev;
info->max77693 = max77693;
if (info->max77693->regmap_muic) {
......@@ -1154,7 +1153,8 @@ static int max77693_muic_probe(struct platform_device *pdev)
struct max77693_muic_irq *muic_irq = &muic_irqs[i];
unsigned int virq = 0;
virq = irq_create_mapping(max77693->irq_domain, muic_irq->irq);
virq = regmap_irq_get_virq(max77693->irq_data_muic,
muic_irq->irq);
if (!virq) {
ret = -EINVAL;
goto err_irq;
......@@ -1183,7 +1183,6 @@ static int max77693_muic_probe(struct platform_device *pdev)
goto err_irq;
}
info->edev->name = DEV_NAME;
info->edev->dev.parent = &pdev->dev;
ret = devm_extcon_dev_register(&pdev->dev, info->edev);
if (ret) {
......@@ -1204,7 +1203,7 @@ static int max77693_muic_probe(struct platform_device *pdev)
enum max77693_irq_source irq_src
= MAX77693_IRQ_GROUP_NR;
max77693_write_reg(info->max77693->regmap_muic,
regmap_write(info->max77693->regmap_muic,
init_data[i].addr,
init_data[i].data);
......@@ -1262,7 +1261,7 @@ static int max77693_muic_probe(struct platform_device *pdev)
max77693_muic_set_path(info, info->path_uart, true);
/* Check revision number of MUIC device*/
ret = max77693_read_reg(info->max77693->regmap_muic,
ret = regmap_read(info->max77693->regmap_muic,
MAX77693_MUIC_REG_ID, &id);
if (ret < 0) {
dev_err(&pdev->dev, "failed to read revision number\n");
......
......@@ -661,10 +661,8 @@ static int max8997_muic_probe(struct platform_device *pdev)
info = devm_kzalloc(&pdev->dev, sizeof(struct max8997_muic_info),
GFP_KERNEL);
if (!info) {
dev_err(&pdev->dev, "failed to allocate memory\n");
if (!info)
return -ENOMEM;
}
info->dev = &pdev->dev;
info->muic = max8997->muic;
......@@ -706,7 +704,6 @@ static int max8997_muic_probe(struct platform_device *pdev)
goto err_irq;
}
info->edev->name = DEV_NAME;
info->edev->dev.parent = &pdev->dev;
ret = devm_extcon_dev_register(&pdev->dev, info->edev);
if (ret) {
......
......@@ -194,7 +194,6 @@ static int palmas_usb_probe(struct platform_device *pdev)
return -ENOMEM;
}
palmas_usb->edev->name = kstrdup(node->name, GFP_KERNEL);
palmas_usb->edev->dev.parent = palmas_usb->dev;
palmas_usb->edev->mutually_exclusive = mutually_exclusive;
status = devm_extcon_dev_register(&pdev->dev, palmas_usb->edev);
......@@ -278,7 +277,7 @@ static int palmas_usb_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(palmas_pm_ops, palmas_usb_suspend, palmas_usb_resume);
static struct of_device_id of_palmas_match_tbl[] = {
static const struct of_device_id of_palmas_match_tbl[] = {
{ .compatible = "ti,palmas-usb", },
{ .compatible = "ti,palmas-usb-vid", },
{ .compatible = "ti,twl6035-usb", },
......
This diff is collapsed.
......@@ -808,12 +808,8 @@ int vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer,
*buffer_actual_len = packetlen;
if (packetlen > bufferlen) {
pr_err("Buffer too small - needed %d bytes but "
"got space for only %d bytes\n",
packetlen, bufferlen);
if (packetlen > bufferlen)
return -ENOBUFS;
}
*requestid = desc.trans_id;
......
......@@ -618,7 +618,7 @@ static void tpci200_pci_remove(struct pci_dev *dev)
__tpci200_pci_remove(tpci200);
}
static DEFINE_PCI_DEVICE_TABLE(tpci200_idtable) = {
static const struct pci_device_id tpci200_idtable[] = {
{ TPCI200_VENDOR_ID, TPCI200_DEVICE_ID, TPCI200_SUBVENDOR_ID,
TPCI200_SUBDEVICE_ID },
{ 0, },
......
......@@ -177,19 +177,20 @@ static void ipoctal_irq_tx(struct ipoctal_channel *channel)
if (channel->nb_bytes == 0)
return;
spin_lock(&channel->lock);
value = channel->tty_port.xmit_buf[*pointer_write];
iowrite8(value, &channel->regs->w.thr);
channel->stats.tx++;
(*pointer_write)++;
*pointer_write = *pointer_write % PAGE_SIZE;
channel->nb_bytes--;
spin_unlock(&channel->lock);
}
static void ipoctal_irq_channel(struct ipoctal_channel *channel)
{
u8 isr, sr;
spin_lock(&channel->lock);
/* The HW is organized in pair of channels. See which register we need
* to read from */
isr = ioread8(&channel->block_regs->r.isr);
......@@ -213,8 +214,6 @@ static void ipoctal_irq_channel(struct ipoctal_channel *channel)
/* TX of each character */
if ((isr & channel->isr_tx_rdy_mask) && (sr & SR_TX_READY))
ipoctal_irq_tx(channel);
spin_unlock(&channel->lock);
}
static irqreturn_t ipoctal_irq_handler(void *arg)
......@@ -324,13 +323,6 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr,
&block_regs[i].w.imr);
}
/*
* IP-OCTAL has different addresses to copy its IRQ vector.
* Depending of the carrier these addresses are accesible or not.
* More info in the datasheet.
*/
ipoctal->dev->bus->ops->request_irq(ipoctal->dev,
ipoctal_irq_handler, ipoctal);
/* Dummy write */
iowrite8(1, ipoctal->mem8_space + 1);
......@@ -391,6 +383,14 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr,
dev_set_drvdata(tty_dev, channel);
}
/*
* IP-OCTAL has different addresses to copy its IRQ vector.
* Depending of the carrier these addresses are accesible or not.
* More info in the datasheet.
*/
ipoctal->dev->bus->ops->request_irq(ipoctal->dev,
ipoctal_irq_handler, ipoctal);
return 0;
}
......
......@@ -384,6 +384,7 @@ config MFD_MAX77693
depends on I2C=y
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
help
Say yes here to add support for Maxim Semiconductor MAX77693.
This is a companion Power Management IC with Flash, Haptic, Charger,
......
......@@ -116,7 +116,7 @@ obj-$(CONFIG_MFD_DA9063) += da9063.o
obj-$(CONFIG_MFD_MAX14577) += max14577.o
obj-$(CONFIG_MFD_MAX77686) += max77686.o max77686-irq.o
obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o
obj-$(CONFIG_MFD_MAX77693) += max77693.o
obj-$(CONFIG_MFD_MAX8907) += max8907.o
max8925-objs := max8925-core.o max8925-i2c.o
obj-$(CONFIG_MFD_MAX8925) += max8925.o
......
This diff is collapsed.
......@@ -49,62 +49,62 @@ static const struct mfd_cell max77693_devs[] = {
{ .name = "max77693-haptic", },
};
int max77693_read_reg(struct regmap *map, u8 reg, u8 *dest)
{
unsigned int val;
int ret;
ret = regmap_read(map, reg, &val);
*dest = val;
return ret;
}
EXPORT_SYMBOL_GPL(max77693_read_reg);
int max77693_bulk_read(struct regmap *map, u8 reg, int count, u8 *buf)
{
int ret;
ret = regmap_bulk_read(map, reg, buf, count);
return ret;
}
EXPORT_SYMBOL_GPL(max77693_bulk_read);
int max77693_write_reg(struct regmap *map, u8 reg, u8 value)
{
int ret;
ret = regmap_write(map, reg, value);
return ret;
}
EXPORT_SYMBOL_GPL(max77693_write_reg);
int max77693_bulk_write(struct regmap *map, u8 reg, int count, u8 *buf)
{
int ret;
static const struct regmap_config max77693_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = MAX77693_PMIC_REG_END,
};
ret = regmap_bulk_write(map, reg, buf, count);
static const struct regmap_irq max77693_led_irqs[] = {
{ .mask = LED_IRQ_FLED2_OPEN, },
{ .mask = LED_IRQ_FLED2_SHORT, },
{ .mask = LED_IRQ_FLED1_OPEN, },
{ .mask = LED_IRQ_FLED1_SHORT, },
{ .mask = LED_IRQ_MAX_FLASH, },
};
return ret;
}
EXPORT_SYMBOL_GPL(max77693_bulk_write);
static const struct regmap_irq_chip max77693_led_irq_chip = {
.name = "max77693-led",
.status_base = MAX77693_LED_REG_FLASH_INT,
.mask_base = MAX77693_LED_REG_FLASH_INT_MASK,
.mask_invert = false,
.num_regs = 1,
.irqs = max77693_led_irqs,
.num_irqs = ARRAY_SIZE(max77693_led_irqs),
};
int max77693_update_reg(struct regmap *map, u8 reg, u8 val, u8 mask)
{
int ret;
static const struct regmap_irq max77693_topsys_irqs[] = {
{ .mask = TOPSYS_IRQ_T120C_INT, },
{ .mask = TOPSYS_IRQ_T140C_INT, },
{ .mask = TOPSYS_IRQ_LOWSYS_INT, },
};
ret = regmap_update_bits(map, reg, mask, val);
static const struct regmap_irq_chip max77693_topsys_irq_chip = {
.name = "max77693-topsys",
.status_base = MAX77693_PMIC_REG_TOPSYS_INT,
.mask_base = MAX77693_PMIC_REG_TOPSYS_INT_MASK,
.mask_invert = false,
.num_regs = 1,
.irqs = max77693_topsys_irqs,
.num_irqs = ARRAY_SIZE(max77693_topsys_irqs),
};
return ret;
}
EXPORT_SYMBOL_GPL(max77693_update_reg);
static const struct regmap_irq max77693_charger_irqs[] = {
{ .mask = CHG_IRQ_BYP_I, },
{ .mask = CHG_IRQ_THM_I, },
{ .mask = CHG_IRQ_BAT_I, },
{ .mask = CHG_IRQ_CHG_I, },
{ .mask = CHG_IRQ_CHGIN_I, },
};
static const struct regmap_config max77693_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = MAX77693_PMIC_REG_END,
static const struct regmap_irq_chip max77693_charger_irq_chip = {
.name = "max77693-charger",
.status_base = MAX77693_CHG_REG_CHG_INT,
.mask_base = MAX77693_CHG_REG_CHG_INT_MASK,
.mask_invert = false,
.num_regs = 1,
.irqs = max77693_charger_irqs,
.num_irqs = ARRAY_SIZE(max77693_charger_irqs),
};
static const struct regmap_config max77693_regmap_muic_config = {
......@@ -113,11 +113,42 @@ static const struct regmap_config max77693_regmap_muic_config = {
.max_register = MAX77693_MUIC_REG_END,
};
static const struct regmap_irq max77693_muic_irqs[] = {
{ .reg_offset = 0, .mask = MUIC_IRQ_INT1_ADC, },
{ .reg_offset = 0, .mask = MUIC_IRQ_INT1_ADC_LOW, },
{ .reg_offset = 0, .mask = MUIC_IRQ_INT1_ADC_ERR, },
{ .reg_offset = 0, .mask = MUIC_IRQ_INT1_ADC1K, },
{ .reg_offset = 1, .mask = MUIC_IRQ_INT2_CHGTYP, },
{ .reg_offset = 1, .mask = MUIC_IRQ_INT2_CHGDETREUN, },
{ .reg_offset = 1, .mask = MUIC_IRQ_INT2_DCDTMR, },
{ .reg_offset = 1, .mask = MUIC_IRQ_INT2_DXOVP, },
{ .reg_offset = 1, .mask = MUIC_IRQ_INT2_VBVOLT, },
{ .reg_offset = 1, .mask = MUIC_IRQ_INT2_VIDRM, },
{ .reg_offset = 2, .mask = MUIC_IRQ_INT3_EOC, },
{ .reg_offset = 2, .mask = MUIC_IRQ_INT3_CGMBC, },
{ .reg_offset = 2, .mask = MUIC_IRQ_INT3_OVP, },
{ .reg_offset = 2, .mask = MUIC_IRQ_INT3_MBCCHG_ERR, },
{ .reg_offset = 2, .mask = MUIC_IRQ_INT3_CHG_ENABLED, },
{ .reg_offset = 2, .mask = MUIC_IRQ_INT3_BAT_DET, },
};
static const struct regmap_irq_chip max77693_muic_irq_chip = {
.name = "max77693-muic",
.status_base = MAX77693_MUIC_REG_INT1,
.mask_base = MAX77693_MUIC_REG_INTMASK1,
.mask_invert = true,
.num_regs = 3,
.irqs = max77693_muic_irqs,
.num_irqs = ARRAY_SIZE(max77693_muic_irqs),
};
static int max77693_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct max77693_dev *max77693;
u8 reg_data;
unsigned int reg_data;
int ret = 0;
max77693 = devm_kzalloc(&i2c->dev,
......@@ -139,7 +170,7 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
return ret;
}
ret = max77693_read_reg(max77693->regmap, MAX77693_PMIC_REG_PMIC_ID2,
ret = regmap_read(max77693->regmap, MAX77693_PMIC_REG_PMIC_ID2,
&reg_data);
if (ret < 0) {
dev_err(max77693->dev, "device not found on this channel\n");
......@@ -176,9 +207,45 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
goto err_regmap_muic;
}
ret = max77693_irq_init(max77693);
if (ret < 0)
goto err_irq;
ret = regmap_add_irq_chip(max77693->regmap, max77693->irq,
IRQF_ONESHOT | IRQF_SHARED |
IRQF_TRIGGER_FALLING, 0,
&max77693_led_irq_chip,
&max77693->irq_data_led);
if (ret) {
dev_err(max77693->dev, "failed to add irq chip: %d\n", ret);
goto err_regmap_muic;
}
ret = regmap_add_irq_chip(max77693->regmap, max77693->irq,
IRQF_ONESHOT | IRQF_SHARED |
IRQF_TRIGGER_FALLING, 0,
&max77693_topsys_irq_chip,
&max77693->irq_data_topsys);
if (ret) {
dev_err(max77693->dev, "failed to add irq chip: %d\n", ret);
goto err_irq_topsys;
}
ret = regmap_add_irq_chip(max77693->regmap, max77693->irq,
IRQF_ONESHOT | IRQF_SHARED |
IRQF_TRIGGER_FALLING, 0,
&max77693_charger_irq_chip,
&max77693->irq_data_charger);
if (ret) {
dev_err(max77693->dev, "failed to add irq chip: %d\n", ret);
goto err_irq_charger;
}
ret = regmap_add_irq_chip(max77693->regmap, max77693->irq,
IRQF_ONESHOT | IRQF_SHARED |
IRQF_TRIGGER_FALLING, 0,
&max77693_muic_irq_chip,
&max77693->irq_data_muic);
if (ret) {
dev_err(max77693->dev, "failed to add irq chip: %d\n", ret);
goto err_irq_muic;
}
pm_runtime_set_active(max77693->dev);
......@@ -190,8 +257,14 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
return ret;
err_mfd:
max77693_irq_exit(max77693);
err_irq:
mfd_remove_devices(max77693->dev);
regmap_del_irq_chip(max77693->irq, max77693->irq_data_muic);
err_irq_muic:
regmap_del_irq_chip(max77693->irq, max77693->irq_data_charger);
err_irq_charger:
regmap_del_irq_chip(max77693->irq, max77693->irq_data_topsys);
err_irq_topsys:
regmap_del_irq_chip(max77693->irq, max77693->irq_data_led);
err_regmap_muic:
i2c_unregister_device(max77693->haptic);
err_i2c_haptic:
......@@ -204,7 +277,12 @@ static int max77693_i2c_remove(struct i2c_client *i2c)
struct max77693_dev *max77693 = i2c_get_clientdata(i2c);
mfd_remove_devices(max77693->dev);
max77693_irq_exit(max77693);
regmap_del_irq_chip(max77693->irq, max77693->irq_data_muic);
regmap_del_irq_chip(max77693->irq, max77693->irq_data_charger);
regmap_del_irq_chip(max77693->irq, max77693->irq_data_topsys);
regmap_del_irq_chip(max77693->irq, max77693->irq_data_led);
i2c_unregister_device(max77693->muic);
i2c_unregister_device(max77693->haptic);
......@@ -222,8 +300,11 @@ static int max77693_suspend(struct device *dev)
struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
struct max77693_dev *max77693 = i2c_get_clientdata(i2c);
if (device_may_wakeup(dev))
irq_set_irq_wake(max77693->irq, 1);
if (device_may_wakeup(dev)) {
enable_irq_wake(max77693->irq);
disable_irq(max77693->irq);
}
return 0;
}
......@@ -232,9 +313,12 @@ static int max77693_resume(struct device *dev)
struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
struct max77693_dev *max77693 = i2c_get_clientdata(i2c);
if (device_may_wakeup(dev))
irq_set_irq_wake(max77693->irq, 0);
return max77693_irq_resume(max77693);
if (device_may_wakeup(dev)) {
disable_irq_wake(max77693->irq);
enable_irq(max77693->irq);
}
return 0;
}
static const struct dev_pm_ops max77693_pm = {
......
......@@ -1185,7 +1185,7 @@ static int bh1770_probe(struct i2c_client *client,
struct bh1770_chip *chip;
int err;
chip = kzalloc(sizeof *chip, GFP_KERNEL);
chip = devm_kzalloc(&client->dev, sizeof *chip, GFP_KERNEL);
if (!chip)
return -ENOMEM;
......@@ -1198,8 +1198,7 @@ static int bh1770_probe(struct i2c_client *client,
if (client->dev.platform_data == NULL) {
dev_err(&client->dev, "platform data is mandatory\n");
err = -EINVAL;
goto fail1;
return -EINVAL;
}
chip->pdata = client->dev.platform_data;
......@@ -1224,24 +1223,24 @@ static int bh1770_probe(struct i2c_client *client,
chip->regs[0].supply = reg_vcc;
chip->regs[1].supply = reg_vleds;
err = regulator_bulk_get(&client->dev,
ARRAY_SIZE(chip->regs), chip->regs);
err = devm_regulator_bulk_get(&client->dev,
ARRAY_SIZE(chip->regs), chip->regs);
if (err < 0) {
dev_err(&client->dev, "Cannot get regulators\n");
goto fail1;
return err;
}
err = regulator_bulk_enable(ARRAY_SIZE(chip->regs),
chip->regs);
if (err < 0) {
dev_err(&client->dev, "Cannot enable regulators\n");
goto fail2;
return err;
}
usleep_range(BH1770_STARTUP_DELAY, BH1770_STARTUP_DELAY * 2);
err = bh1770_detect(chip);
if (err < 0)
goto fail3;
goto fail0;
/* Start chip */
bh1770_chip_on(chip);
......@@ -1252,14 +1251,14 @@ static int bh1770_probe(struct i2c_client *client,
if (chip->lux_corr == 0) {
dev_err(&client->dev, "Improper correction values\n");
err = -EINVAL;
goto fail3;
goto fail0;
}
if (chip->pdata->setup_resources) {
err = chip->pdata->setup_resources();
if (err) {
err = -EINVAL;
goto fail3;
goto fail0;
}
}
......@@ -1267,7 +1266,7 @@ static int bh1770_probe(struct i2c_client *client,
&bh1770_attribute_group);
if (err < 0) {
dev_err(&chip->client->dev, "Sysfs registration failed\n");
goto fail4;
goto fail1;
}
/*
......@@ -1283,22 +1282,18 @@ static int bh1770_probe(struct i2c_client *client,
if (err) {
dev_err(&client->dev, "could not get IRQ %d\n",
client->irq);
goto fail5;
goto fail2;
}
regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs);
return err;
fail5:
fail2:
sysfs_remove_group(&chip->client->dev.kobj,
&bh1770_attribute_group);
fail4:
fail1:
if (chip->pdata->release_resources)
chip->pdata->release_resources();
fail3:
fail0:
regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs);
fail2:
regulator_bulk_free(ARRAY_SIZE(chip->regs), chip->regs);
fail1:
kfree(chip);
return err;
}
......@@ -1322,8 +1317,6 @@ static int bh1770_remove(struct i2c_client *client)
pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
regulator_bulk_free(ARRAY_SIZE(chip->regs), chip->regs);
kfree(chip);
return 0;
}
......
......@@ -149,50 +149,35 @@ static int bh1780_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret;
struct bh1780_data *ddata = NULL;
struct bh1780_data *ddata;
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) {
ret = -EIO;
goto err_op_failed;
}
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
return -EIO;
ddata = kzalloc(sizeof(struct bh1780_data), GFP_KERNEL);
if (ddata == NULL) {
ret = -ENOMEM;
goto err_op_failed;
}
ddata = devm_kzalloc(&client->dev, sizeof(struct bh1780_data),
GFP_KERNEL);
if (ddata == NULL)
return -ENOMEM;
ddata->client = client;
i2c_set_clientdata(client, ddata);
ret = bh1780_read(ddata, BH1780_REG_PARTID, "PART ID");
if (ret < 0)
goto err_op_failed;
return ret;
dev_info(&client->dev, "Ambient Light Sensor, Rev : %d\n",
(ret & BH1780_REVMASK));
mutex_init(&ddata->lock);
ret = sysfs_create_group(&client->dev.kobj, &bh1780_attr_group);
if (ret)
goto err_op_failed;
return 0;
err_op_failed:
kfree(ddata);
return ret;
return sysfs_create_group(&client->dev.kobj, &bh1780_attr_group);
}
static int bh1780_remove(struct i2c_client *client)
{
struct bh1780_data *ddata;
ddata = i2c_get_clientdata(client);
sysfs_remove_group(&client->dev.kobj, &bh1780_attr_group);
kfree(ddata);
return 0;
}
......
......@@ -954,10 +954,7 @@ static int data_debugfs_init(struct fpga_device *priv)
{
priv->dbg_entry = debugfs_create_file(drv_name, S_IRUGO, NULL, priv,
&data_debug_fops);
if (IS_ERR(priv->dbg_entry))
return PTR_ERR(priv->dbg_entry);
return 0;
return PTR_ERR_OR_ZERO(priv->dbg_entry);
}
static void data_debugfs_exit(struct fpga_device *priv)
......
......@@ -61,3 +61,4 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jiri Kosina");
module_param(irq, uint, 0444);
MODULE_PARM_DESC(irq, "The IRQ to register for");
MODULE_DESCRIPTION("Dummy IRQ handler driver");
......@@ -11,3 +11,9 @@ menuconfig GENWQE
Enables PCIe card driver for IBM GenWQE accelerators.
The user-space interface is described in
include/linux/genwqe/genwqe_card.h.
config GENWQE_PLATFORM_ERROR_RECOVERY
int "Use platform recovery procedures (0=off, 1=on)"
depends on GENWQE
default 1 if PPC64
default 0
......@@ -38,7 +38,6 @@
#include <linux/notifier.h>
#include <linux/device.h>
#include <linux/log2.h>
#include <linux/genwqe/genwqe_card.h>
#include "card_base.h"
#include "card_ddcb.h"
......@@ -58,7 +57,7 @@ static struct dentry *debugfs_genwqe;
static struct genwqe_dev *genwqe_devices[GENWQE_CARD_NO_MAX];
/* PCI structure for identifying device by PCI vendor and device ID */
static DEFINE_PCI_DEVICE_TABLE(genwqe_device_table) = {
static const struct pci_device_id genwqe_device_table[] = {
{ .vendor = PCI_VENDOR_ID_IBM,
.device = PCI_DEVICE_GENWQE,
.subvendor = PCI_SUBVENDOR_ID_IBM,
......@@ -140,6 +139,12 @@ static struct genwqe_dev *genwqe_dev_alloc(void)
cd->class_genwqe = class_genwqe;
cd->debugfs_genwqe = debugfs_genwqe;
/*
* This comes from kernel config option and can be overritten via
* debugfs.
*/
cd->use_platform_recovery = CONFIG_GENWQE_PLATFORM_ERROR_RECOVERY;
init_waitqueue_head(&cd->queue_waitq);
spin_lock_init(&cd->file_lock);
......@@ -760,6 +765,124 @@ static u64 genwqe_fir_checking(struct genwqe_dev *cd)
return IO_ILLEGAL_VALUE;
}
/**
* genwqe_pci_fundamental_reset() - trigger a PCIe fundamental reset on the slot
*
* Note: pci_set_pcie_reset_state() is not implemented on all archs, so this
* reset method will not work in all cases.
*
* Return: 0 on success or error code from pci_set_pcie_reset_state()
*/
static int genwqe_pci_fundamental_reset(struct pci_dev *pci_dev)
{
int rc;
/*
* lock pci config space access from userspace,
* save state and issue PCIe fundamental reset
*/
pci_cfg_access_lock(pci_dev);
pci_save_state(pci_dev);
rc = pci_set_pcie_reset_state(pci_dev, pcie_warm_reset);
if (!rc) {
/* keep PCIe reset asserted for 250ms */
msleep(250);
pci_set_pcie_reset_state(pci_dev, pcie_deassert_reset);
/* Wait for 2s to reload flash and train the link */
msleep(2000);
}
pci_restore_state(pci_dev);
pci_cfg_access_unlock(pci_dev);
return rc;
}
static int genwqe_platform_recovery(struct genwqe_dev *cd)
{
struct pci_dev *pci_dev = cd->pci_dev;
int rc;
dev_info(&pci_dev->dev,
"[%s] resetting card for error recovery\n", __func__);
/* Clear out error injection flags */
cd->err_inject &= ~(GENWQE_INJECT_HARDWARE_FAILURE |
GENWQE_INJECT_GFIR_FATAL |
GENWQE_INJECT_GFIR_INFO);
genwqe_stop(cd);
/* Try recoverying the card with fundamental reset */
rc = genwqe_pci_fundamental_reset(pci_dev);
if (!rc) {
rc = genwqe_start(cd);
if (!rc)
dev_info(&pci_dev->dev,
"[%s] card recovered\n", __func__);
else
dev_err(&pci_dev->dev,
"[%s] err: cannot start card services! (err=%d)\n",
__func__, rc);
} else {
dev_err(&pci_dev->dev,
"[%s] card reset failed\n", __func__);
}
return rc;
}
/*
* genwqe_reload_bistream() - reload card bitstream
*
* Set the appropriate register and call fundamental reset to reaload the card
* bitstream.
*
* Return: 0 on success, error code otherwise
*/
static int genwqe_reload_bistream(struct genwqe_dev *cd)
{
struct pci_dev *pci_dev = cd->pci_dev;
int rc;
dev_info(&pci_dev->dev,
"[%s] resetting card for bitstream reload\n",
__func__);
genwqe_stop(cd);
/*
* Cause a CPLD reprogram with the 'next_bitstream'
* partition on PCIe hot or fundamental reset
*/
__genwqe_writeq(cd, IO_SLC_CFGREG_SOFTRESET,
(cd->softreset & 0xcull) | 0x70ull);
rc = genwqe_pci_fundamental_reset(pci_dev);
if (rc) {
/*
* A fundamental reset failure can be caused
* by lack of support on the arch, so we just
* log the error and try to start the card
* again.
*/
dev_err(&pci_dev->dev,
"[%s] err: failed to reset card for bitstream reload\n",
__func__);
}
rc = genwqe_start(cd);
if (rc) {
dev_err(&pci_dev->dev,
"[%s] err: cannot start card services! (err=%d)\n",
__func__, rc);
return rc;
}
dev_info(&pci_dev->dev,
"[%s] card reloaded\n", __func__);
return 0;
}
/**
* genwqe_health_thread() - Health checking thread
*
......@@ -786,6 +909,7 @@ static int genwqe_health_thread(void *data)
struct pci_dev *pci_dev = cd->pci_dev;
u64 gfir, gfir_masked, slu_unitcfg, app_unitcfg;
health_thread_begin:
while (!kthread_should_stop()) {
rc = wait_event_interruptible_timeout(cd->health_waitq,
(genwqe_health_check_cond(cd, &gfir) ||
......@@ -846,6 +970,13 @@ static int genwqe_health_thread(void *data)
}
}
if (cd->card_state == GENWQE_CARD_RELOAD_BITSTREAM) {
/* Userspace requested card bitstream reload */
rc = genwqe_reload_bistream(cd);
if (rc)
goto fatal_error;
}
cd->last_gfir = gfir;
cond_resched();
}
......@@ -853,6 +984,28 @@ static int genwqe_health_thread(void *data)
return 0;
fatal_error:
if (cd->use_platform_recovery) {
/*
* Since we use raw accessors, EEH errors won't be detected
* by the platform until we do a non-raw MMIO or config space
* read
*/
readq(cd->mmio + IO_SLC_CFGREG_GFIR);
/* We do nothing if the card is going over PCI recovery */
if (pci_channel_offline(pci_dev))
return -EIO;
/*
* If it's supported by the platform, we try a fundamental reset
* to recover from a fatal error. Otherwise, we continue to wait
* for an external recovery procedure to take care of it.
*/
rc = genwqe_platform_recovery(cd);
if (!rc)
goto health_thread_begin;
}
dev_err(&pci_dev->dev,
"[%s] card unusable. Please trigger unbind!\n", __func__);
......@@ -958,6 +1111,9 @@ static int genwqe_pci_setup(struct genwqe_dev *cd)
pci_set_master(pci_dev);
pci_enable_pcie_error_reporting(pci_dev);
/* EEH recovery requires PCIe fundamental reset */
pci_dev->needs_freset = 1;
/* request complete BAR-0 space (length = 0) */
cd->mmio_len = pci_resource_len(pci_dev, 0);
cd->mmio = pci_iomap(pci_dev, 0, 0);
......@@ -1096,23 +1252,40 @@ static pci_ers_result_t genwqe_err_error_detected(struct pci_dev *pci_dev,
dev_err(&pci_dev->dev, "[%s] state=%d\n", __func__, state);
if (pci_dev == NULL)
return PCI_ERS_RESULT_NEED_RESET;
cd = dev_get_drvdata(&pci_dev->dev);
if (cd == NULL)
return PCI_ERS_RESULT_NEED_RESET;
return PCI_ERS_RESULT_DISCONNECT;
switch (state) {
case pci_channel_io_normal:
return PCI_ERS_RESULT_CAN_RECOVER;
case pci_channel_io_frozen:
return PCI_ERS_RESULT_NEED_RESET;
case pci_channel_io_perm_failure:
/* Stop the card */
genwqe_health_check_stop(cd);
genwqe_stop(cd);
/*
* On permanent failure, the PCI code will call device remove
* after the return of this function.
* genwqe_stop() can be called twice.
*/
if (state == pci_channel_io_perm_failure) {
return PCI_ERS_RESULT_DISCONNECT;
} else {
genwqe_pci_remove(cd);
return PCI_ERS_RESULT_NEED_RESET;
}
}
static pci_ers_result_t genwqe_err_slot_reset(struct pci_dev *pci_dev)
{
int rc;
struct genwqe_dev *cd = dev_get_drvdata(&pci_dev->dev);
return PCI_ERS_RESULT_NEED_RESET;
rc = genwqe_pci_setup(cd);
if (!rc) {
return PCI_ERS_RESULT_RECOVERED;
} else {
dev_err(&pci_dev->dev,
"err: problems with PCI setup (err=%d)\n", rc);
return PCI_ERS_RESULT_DISCONNECT;
}
}
static pci_ers_result_t genwqe_err_result_none(struct pci_dev *dev)
......@@ -1120,8 +1293,22 @@ static pci_ers_result_t genwqe_err_result_none(struct pci_dev *dev)
return PCI_ERS_RESULT_NONE;
}
static void genwqe_err_resume(struct pci_dev *dev)
static void genwqe_err_resume(struct pci_dev *pci_dev)
{
int rc;
struct genwqe_dev *cd = dev_get_drvdata(&pci_dev->dev);
rc = genwqe_start(cd);
if (!rc) {
rc = genwqe_health_check_start(cd);
if (rc)
dev_err(&pci_dev->dev,
"err: cannot start health checking! (err=%d)\n",
rc);
} else {
dev_err(&pci_dev->dev,
"err: cannot start card services! (err=%d)\n", rc);
}
}
static int genwqe_sriov_configure(struct pci_dev *dev, int numvfs)
......@@ -1144,7 +1331,7 @@ static struct pci_error_handlers genwqe_err_handler = {
.error_detected = genwqe_err_error_detected,
.mmio_enabled = genwqe_err_result_none,
.link_reset = genwqe_err_result_none,
.slot_reset = genwqe_err_result_none,
.slot_reset = genwqe_err_slot_reset,
.resume = genwqe_err_resume,
};
......
......@@ -291,6 +291,8 @@ struct genwqe_dev {
struct task_struct *health_thread;
wait_queue_head_t health_waitq;
int use_platform_recovery; /* use platform recovery mechanisms */
/* char device */
dev_t devnum_genwqe; /* major/minor num card */
struct class *class_genwqe; /* reference to class object */
......
......@@ -1118,7 +1118,21 @@ static irqreturn_t genwqe_pf_isr(int irq, void *dev_id)
* safer, but slower for the good-case ... See above.
*/
gfir = __genwqe_readq(cd, IO_SLC_CFGREG_GFIR);
if ((gfir & GFIR_ERR_TRIGGER) != 0x0) {
if (((gfir & GFIR_ERR_TRIGGER) != 0x0) &&
!pci_channel_offline(pci_dev)) {
if (cd->use_platform_recovery) {
/*
* Since we use raw accessors, EEH errors won't be
* detected by the platform until we do a non-raw
* MMIO or config space read
*/
readq(cd->mmio + IO_SLC_CFGREG_GFIR);
/* Don't do anything if the PCI channel is frozen */
if (pci_channel_offline(pci_dev))
goto exit;
}
wake_up_interruptible(&cd->health_waitq);
......@@ -1126,12 +1140,12 @@ static irqreturn_t genwqe_pf_isr(int irq, void *dev_id)
* By default GFIRs causes recovery actions. This
* count is just for debug when recovery is masked.
*/
printk_ratelimited(KERN_ERR
"%s %s: [%s] GFIR=%016llx\n",
GENWQE_DEVNAME, dev_name(&pci_dev->dev),
__func__, gfir);
dev_err_ratelimited(&pci_dev->dev,
"[%s] GFIR=%016llx\n",
__func__, gfir);
}
exit:
return IRQ_HANDLED;
}
......@@ -1237,9 +1251,7 @@ int genwqe_setup_service_layer(struct genwqe_dev *cd)
}
rc = genwqe_set_interrupt_capability(cd, GENWQE_MSI_IRQS);
if (rc > 0)
rc = genwqe_set_interrupt_capability(cd, rc);
if (rc != 0) {
if (rc) {
rc = -ENODEV;
goto stop_kthread;
}
......
......@@ -485,6 +485,13 @@ int genwqe_init_debugfs(struct genwqe_dev *cd)
goto err1;
}
file = debugfs_create_u32("use_platform_recovery", 0666, root,
&cd->use_platform_recovery);
if (!file) {
ret = -ENOMEM;
goto err1;
}
cd->debugfs_root = root;
return 0;
err1:
......
......@@ -1048,10 +1048,15 @@ static long genwqe_ioctl(struct file *filp, unsigned int cmd,
int rc = 0;
struct genwqe_file *cfile = (struct genwqe_file *)filp->private_data;
struct genwqe_dev *cd = cfile->cd;
struct pci_dev *pci_dev = cd->pci_dev;
struct genwqe_reg_io __user *io;
u64 val;
u32 reg_offs;
/* Return -EIO if card hit EEH */
if (pci_channel_offline(pci_dev))
return -EIO;
if (_IOC_TYPE(cmd) != GENWQE_IOC_CODE)
return -EINVAL;
......
......@@ -223,6 +223,30 @@ static ssize_t next_bitstream_store(struct device *dev,
}
static DEVICE_ATTR_RW(next_bitstream);
static ssize_t reload_bitstream_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int reload;
struct genwqe_dev *cd = dev_get_drvdata(dev);
if (kstrtoint(buf, 0, &reload) < 0)
return -EINVAL;
if (reload == 0x1) {
if (cd->card_state == GENWQE_CARD_UNUSED ||
cd->card_state == GENWQE_CARD_USED)
cd->card_state = GENWQE_CARD_RELOAD_BITSTREAM;
else
return -EIO;
} else {
return -EINVAL;
}
return count;
}
static DEVICE_ATTR_WO(reload_bitstream);
/*
* Create device_attribute structures / params: name, mode, show, store
* additional flag if valid in VF
......@@ -239,6 +263,7 @@ static struct attribute *genwqe_attributes[] = {
&dev_attr_status.attr,
&dev_attr_freerunning_timer.attr,
&dev_attr_queue_working_time.attr,
&dev_attr_reload_bitstream.attr,
NULL,
};
......
......@@ -53,12 +53,17 @@
*/
int __genwqe_writeq(struct genwqe_dev *cd, u64 byte_offs, u64 val)
{
struct pci_dev *pci_dev = cd->pci_dev;
if (cd->err_inject & GENWQE_INJECT_HARDWARE_FAILURE)
return -EIO;
if (cd->mmio == NULL)
return -EIO;
if (pci_channel_offline(pci_dev))
return -EIO;
__raw_writeq((__force u64)cpu_to_be64(val), cd->mmio + byte_offs);
return 0;
}
......@@ -99,12 +104,17 @@ u64 __genwqe_readq(struct genwqe_dev *cd, u64 byte_offs)
*/
int __genwqe_writel(struct genwqe_dev *cd, u64 byte_offs, u32 val)
{
struct pci_dev *pci_dev = cd->pci_dev;
if (cd->err_inject & GENWQE_INJECT_HARDWARE_FAILURE)
return -EIO;
if (cd->mmio == NULL)
return -EIO;
if (pci_channel_offline(pci_dev))
return -EIO;
__raw_writel((__force u32)cpu_to_be32(val), cd->mmio + byte_offs);
return 0;
}
......@@ -718,10 +728,12 @@ int genwqe_set_interrupt_capability(struct genwqe_dev *cd, int count)
int rc;
struct pci_dev *pci_dev = cd->pci_dev;
rc = pci_enable_msi_exact(pci_dev, count);
if (rc == 0)
cd->flags |= GENWQE_FLAG_MSI_ENABLED;
return rc;
rc = pci_enable_msi_range(pci_dev, 1, count);
if (rc < 0)
return rc;
cd->flags |= GENWQE_FLAG_MSI_ENABLED;
return 0;
}
/**
......
......@@ -36,7 +36,7 @@
#include <asm/byteorder.h>
#include <linux/genwqe/genwqe_card.h>
#define DRV_VERS_STRING "2.0.15"
#define DRV_VERS_STRING "2.0.21"
/*
* Static minor number assignement, until we decide/implement
......
......@@ -15,6 +15,7 @@
#include <linux/spi/spi.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <asm/unaligned.h>
#define FIRMWARE_NAME "lattice-ecp3.bit"
......@@ -91,8 +92,8 @@ static void firmware_load(const struct firmware *fw, void *context)
/* Trying to speak with the FPGA via SPI... */
txbuf[0] = FPGA_CMD_READ_ID;
ret = spi_write_then_read(spi, txbuf, 8, rxbuf, rx_len);
dev_dbg(&spi->dev, "FPGA JTAG ID=%08x\n", *(u32 *)&rxbuf[4]);
jedec_id = *(u32 *)&rxbuf[4];
jedec_id = get_unaligned_be32(&rxbuf[4]);
dev_dbg(&spi->dev, "FPGA JTAG ID=%08x\n", jedec_id);
for (i = 0; i < ARRAY_SIZE(ecp3_dev); i++) {
if (jedec_id == ecp3_dev[i].jedec_id)
......@@ -109,7 +110,8 @@ static void firmware_load(const struct firmware *fw, void *context)
txbuf[0] = FPGA_CMD_READ_STATUS;
ret = spi_write_then_read(spi, txbuf, 8, rxbuf, rx_len);
dev_dbg(&spi->dev, "FPGA Status=%08x\n", *(u32 *)&rxbuf[4]);
status = get_unaligned_be32(&rxbuf[4]);
dev_dbg(&spi->dev, "FPGA Status=%08x\n", status);
buffer = kzalloc(fw->size + 8, GFP_KERNEL);
if (!buffer) {
......@@ -141,7 +143,7 @@ static void firmware_load(const struct firmware *fw, void *context)
for (i = 0; i < FPGA_CLEAR_LOOP_COUNT; i++) {
txbuf[0] = FPGA_CMD_READ_STATUS;
ret = spi_write_then_read(spi, txbuf, 8, rxbuf, rx_len);
status = *(u32 *)&rxbuf[4];
status = get_unaligned_be32(&rxbuf[4]);
if (status == FPGA_STATUS_CLEARED)
break;
......@@ -164,8 +166,8 @@ static void firmware_load(const struct firmware *fw, void *context)
txbuf[0] = FPGA_CMD_READ_STATUS;
ret = spi_write_then_read(spi, txbuf, 8, rxbuf, rx_len);
dev_dbg(&spi->dev, "FPGA Status=%08x\n", *(u32 *)&rxbuf[4]);
status = *(u32 *)&rxbuf[4];
status = get_unaligned_be32(&rxbuf[4]);
dev_dbg(&spi->dev, "FPGA Status=%08x\n", status);
/* Check result */
if (status & FPGA_STATUS_DONE)
......@@ -196,7 +198,7 @@ static int lattice_ecp3_probe(struct spi_device *spi)
spi_set_drvdata(spi, data);
init_completion(&data->fw_loaded);
err = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG,
err = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
FIRMWARE_NAME, &spi->dev,
GFP_KERNEL, spi, firmware_load);
if (err) {
......
......@@ -870,3 +870,4 @@ module_init(lkdtm_module_init);
module_exit(lkdtm_module_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Kprobe module for testing crash dumps");
......@@ -459,7 +459,7 @@ int mei_cl_disconnect(struct mei_cl *cl)
{
struct mei_device *dev;
struct mei_cl_cb *cb;
int rets, err;
int rets;
if (WARN_ON(!cl || !cl->dev))
return -ENODEV;
......@@ -491,6 +491,7 @@ int mei_cl_disconnect(struct mei_cl *cl)
cl_err(dev, cl, "failed to disconnect.\n");
goto free;
}
cl->timer_count = MEI_CONNECT_TIMEOUT;
mdelay(10); /* Wait for hardware disconnection ready */
list_add_tail(&cb->list, &dev->ctrl_rd_list.list);
} else {
......@@ -500,23 +501,18 @@ int mei_cl_disconnect(struct mei_cl *cl)
}
mutex_unlock(&dev->device_lock);
err = wait_event_timeout(dev->wait_recvd_msg,
wait_event_timeout(dev->wait_recvd_msg,
MEI_FILE_DISCONNECTED == cl->state,
mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
mutex_lock(&dev->device_lock);
if (MEI_FILE_DISCONNECTED == cl->state) {
rets = 0;
cl_dbg(dev, cl, "successfully disconnected from FW client.\n");
} else {
rets = -ENODEV;
if (MEI_FILE_DISCONNECTED != cl->state)
cl_err(dev, cl, "wrong status client disconnect.\n");
if (err)
cl_dbg(dev, cl, "wait failed disconnect err=%d\n", err);
cl_err(dev, cl, "failed to disconnect from FW client.\n");
cl_dbg(dev, cl, "timeout on disconnect from FW client.\n");
rets = -ETIME;
}
mei_io_list_flush(&dev->ctrl_rd_list, cl);
......@@ -616,6 +612,7 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file)
mutex_lock(&dev->device_lock);
if (cl->state != MEI_FILE_CONNECTED) {
cl->state = MEI_FILE_DISCONNECTED;
/* something went really wrong */
if (!cl->status)
cl->status = -EFAULT;
......
......@@ -115,6 +115,7 @@
#define MEI_DEV_ID_LPT_HR 0x8CBA /* Lynx Point H Refresh */
#define MEI_DEV_ID_WPT_LP 0x9CBA /* Wildcat Point LP */
#define MEI_DEV_ID_WPT_LP_2 0x9CBB /* Wildcat Point LP 2 */
/* Host Firmware Status Registers in PCI Config Space */
#define PCI_CFG_HFS_1 0x40
......
......@@ -710,64 +710,10 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}
/**
* mei_me_fw_status - retrieve fw status from the pci config space
*
* @dev: the device structure
* @fw_status: fw status registers storage
*
* returns 0 on success an error code otherwise
*/
static int mei_me_fw_status(struct mei_device *dev,
struct mei_fw_status *fw_status)
{
const u32 pci_cfg_reg[] = {PCI_CFG_HFS_1, PCI_CFG_HFS_2};
int i;
if (!fw_status)
return -EINVAL;
switch (dev->pdev->device) {
case MEI_DEV_ID_IBXPK_1:
case MEI_DEV_ID_IBXPK_2:
case MEI_DEV_ID_CPT_1:
case MEI_DEV_ID_PBG_1:
case MEI_DEV_ID_PPT_1:
case MEI_DEV_ID_PPT_2:
case MEI_DEV_ID_PPT_3:
case MEI_DEV_ID_LPT_H:
case MEI_DEV_ID_LPT_W:
case MEI_DEV_ID_LPT_LP:
case MEI_DEV_ID_LPT_HR:
case MEI_DEV_ID_WPT_LP:
fw_status->count = 2;
break;
case MEI_DEV_ID_ICH10_1:
case MEI_DEV_ID_ICH10_2:
case MEI_DEV_ID_ICH10_3:
case MEI_DEV_ID_ICH10_4:
fw_status->count = 1;
break;
default:
fw_status->count = 0;
break;
}
for (i = 0; i < fw_status->count && i < MEI_FW_STATUS_MAX; i++) {
int ret;
ret = pci_read_config_dword(dev->pdev,
pci_cfg_reg[i], &fw_status->status[i]);
if (ret)
return ret;
}
return 0;
}
static const struct mei_hw_ops mei_me_hw_ops = {
.pg_state = mei_me_pg_state,
.fw_status = mei_me_fw_status,
.host_is_ready = mei_me_host_is_ready,
.hw_is_ready = mei_me_hw_is_ready,
......
......@@ -1042,40 +1042,8 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}
/**
* mei_txe_fw_status - retrieve fw status from the pci config space
*
* @dev: the device structure
* @fw_status: fw status registers storage
*
* returns: 0 on success an error code otherwise
*/
static int mei_txe_fw_status(struct mei_device *dev,
struct mei_fw_status *fw_status)
{
const u32 pci_cfg_reg[] = {PCI_CFG_TXE_FW_STS0, PCI_CFG_TXE_FW_STS1};
int i;
if (!fw_status)
return -EINVAL;
fw_status->count = 2;
for (i = 0; i < fw_status->count && i < MEI_FW_STATUS_MAX; i++) {
int ret;
ret = pci_read_config_dword(dev->pdev,
pci_cfg_reg[i], &fw_status->status[i]);
if (ret)
return ret;
}
return 0;
}
static const struct mei_hw_ops mei_txe_hw_ops = {
.fw_status = mei_txe_fw_status,
.host_is_ready = mei_txe_host_is_ready,
.pg_state = mei_txe_pg_state,
......
......@@ -32,7 +32,6 @@
#include <linux/compat.h>
#include <linux/jiffies.h>
#include <linux/interrupt.h>
#include <linux/miscdevice.h>
#include <linux/mei.h>
......@@ -49,19 +48,12 @@
*/
static int mei_open(struct inode *inode, struct file *file)
{
struct miscdevice *misc = file->private_data;
struct pci_dev *pdev;
struct mei_cl *cl;
struct mei_device *dev;
struct mei_cl *cl;
int err;
if (!misc->parent)
return -ENODEV;
pdev = container_of(misc->parent, struct pci_dev, dev);
dev = pci_get_drvdata(pdev);
dev = container_of(inode->i_cdev, struct mei_device, cdev);
if (!dev)
return -ENODEV;
......@@ -667,46 +659,148 @@ static const struct file_operations mei_fops = {
.llseek = no_llseek
};
/*
* Misc Device Struct
static struct class *mei_class;
static dev_t mei_devt;
#define MEI_MAX_DEVS MINORMASK
static DEFINE_MUTEX(mei_minor_lock);
static DEFINE_IDR(mei_idr);
/**
* mei_minor_get - obtain next free device minor number
*
* @dev: device pointer
*
* returns allocated minor, or -ENOSPC if no free minor left
*/
static struct miscdevice mei_misc_device = {
.name = "mei",
.fops = &mei_fops,
.minor = MISC_DYNAMIC_MINOR,
};
static int mei_minor_get(struct mei_device *dev)
{
int ret;
mutex_lock(&mei_minor_lock);
ret = idr_alloc(&mei_idr, dev, 0, MEI_MAX_DEVS, GFP_KERNEL);
if (ret >= 0)
dev->minor = ret;
else if (ret == -ENOSPC)
dev_err(&dev->pdev->dev, "too many mei devices\n");
mutex_unlock(&mei_minor_lock);
return ret;
}
int mei_register(struct mei_device *dev)
/**
* mei_minor_free - mark device minor number as free
*
* @dev: device pointer
*/
static void mei_minor_free(struct mei_device *dev)
{
int ret;
mei_misc_device.parent = &dev->pdev->dev;
ret = misc_register(&mei_misc_device);
if (ret)
mutex_lock(&mei_minor_lock);
idr_remove(&mei_idr, dev->minor);
mutex_unlock(&mei_minor_lock);
}
int mei_register(struct mei_device *dev, struct device *parent)
{
struct device *clsdev; /* class device */
int ret, devno;
ret = mei_minor_get(dev);
if (ret < 0)
return ret;
if (mei_dbgfs_register(dev, mei_misc_device.name))
dev_err(&dev->pdev->dev, "cannot register debugfs\n");
/* Fill in the data structures */
devno = MKDEV(MAJOR(mei_devt), dev->minor);
cdev_init(&dev->cdev, &mei_fops);
dev->cdev.owner = mei_fops.owner;
/* Add the device */
ret = cdev_add(&dev->cdev, devno, 1);
if (ret) {
dev_err(parent, "unable to add device %d:%d\n",
MAJOR(mei_devt), dev->minor);
goto err_dev_add;
}
clsdev = device_create(mei_class, parent, devno,
NULL, "mei%d", dev->minor);
if (IS_ERR(clsdev)) {
dev_err(parent, "unable to create device %d:%d\n",
MAJOR(mei_devt), dev->minor);
ret = PTR_ERR(clsdev);
goto err_dev_create;
}
ret = mei_dbgfs_register(dev, dev_name(clsdev));
if (ret) {
dev_err(clsdev, "cannot register debugfs ret = %d\n", ret);
goto err_dev_dbgfs;
}
return 0;
err_dev_dbgfs:
device_destroy(mei_class, devno);
err_dev_create:
cdev_del(&dev->cdev);
err_dev_add:
mei_minor_free(dev);
return ret;
}
EXPORT_SYMBOL_GPL(mei_register);
void mei_deregister(struct mei_device *dev)
{
int devno;
devno = dev->cdev.dev;
cdev_del(&dev->cdev);
mei_dbgfs_deregister(dev);
misc_deregister(&mei_misc_device);
mei_misc_device.parent = NULL;
device_destroy(mei_class, devno);
mei_minor_free(dev);
}
EXPORT_SYMBOL_GPL(mei_deregister);
static int __init mei_init(void)
{
return mei_cl_bus_init();
int ret;
mei_class = class_create(THIS_MODULE, "mei");
if (IS_ERR(mei_class)) {
pr_err("couldn't create class\n");
ret = PTR_ERR(mei_class);
goto err;
}
ret = alloc_chrdev_region(&mei_devt, 0, MEI_MAX_DEVS, "mei");
if (ret < 0) {
pr_err("unable to allocate char dev region\n");
goto err_class;
}
ret = mei_cl_bus_init();
if (ret < 0) {
pr_err("unable to initialize bus\n");
goto err_chrdev;
}
return 0;
err_chrdev:
unregister_chrdev_region(mei_devt, MEI_MAX_DEVS);
err_class:
class_destroy(mei_class);
err:
return ret;
}
static void __exit mei_exit(void)
{
unregister_chrdev_region(mei_devt, MEI_MAX_DEVS);
class_destroy(mei_class);
mei_cl_bus_exit();
}
......
......@@ -227,7 +227,6 @@ struct mei_cl {
/** struct mei_hw_ops
*
* @fw_status - read FW status from PCI config space
* @host_is_ready - query for host readiness
* @hw_is_ready - query if hw is ready
......@@ -255,8 +254,6 @@ struct mei_cl {
*/
struct mei_hw_ops {
int (*fw_status)(struct mei_device *dev,
struct mei_fw_status *fw_status);
bool (*host_is_ready)(struct mei_device *dev);
bool (*hw_is_ready)(struct mei_device *dev);
......@@ -400,6 +397,10 @@ struct mei_cfg {
/**
* struct mei_device - MEI private device struct
* @pdev - pointer to pci device struct
* @cdev - character device
* @minor - minor number allocated for device
*
* @reset_count - limits the number of consecutive resets
* @hbm_state - state of host bus message protocol
* @pg_event - power gating event
......@@ -412,6 +413,9 @@ struct mei_cfg {
*/
struct mei_device {
struct pci_dev *pdev; /* pointer to pci device struct */
struct cdev cdev;
int minor;
/*
* lists of queues
*/
......@@ -741,7 +745,7 @@ static inline int mei_dbgfs_register(struct mei_device *dev, const char *name)
static inline void mei_dbgfs_deregister(struct mei_device *dev) {}
#endif /* CONFIG_DEBUG_FS */
int mei_register(struct mei_device *dev);
int mei_register(struct mei_device *dev, struct device *parent);
void mei_deregister(struct mei_device *dev);
#define MEI_HDR_FMT "hdr:host=%02d me=%02d len=%d internal=%1d comp=%1d"
......
......@@ -31,7 +31,6 @@
#include <linux/compat.h>
#include <linux/jiffies.h>
#include <linux/interrupt.h>
#include <linux/miscdevice.h>
#include <linux/pm_runtime.h>
......@@ -82,6 +81,7 @@ static const struct pci_device_id mei_me_pci_tbl[] = {
{MEI_PCI_DEVICE(MEI_DEV_ID_LPT_LP, mei_me_pch_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_LPT_HR, mei_me_lpt_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_WPT_LP, mei_me_pch_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_WPT_LP_2, mei_me_pch_cfg)},
/* required last entry */
{0, }
......@@ -207,7 +207,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
pm_runtime_set_autosuspend_delay(&pdev->dev, MEI_ME_RPM_TIMEOUT);
pm_runtime_use_autosuspend(&pdev->dev);
err = mei_register(dev);
err = mei_register(dev, &pdev->dev);
if (err)
goto release_irq;
......@@ -369,7 +369,7 @@ static int mei_me_pm_runtime_idle(struct device *device)
if (!dev)
return -ENODEV;
if (mei_write_is_idle(dev))
pm_schedule_suspend(device, MEI_ME_RPM_TIMEOUT * 2);
pm_runtime_autosuspend(device);
return -EBUSY;
}
......
......@@ -149,7 +149,7 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
pm_runtime_set_autosuspend_delay(&pdev->dev, MEI_TXI_RPM_TIMEOUT);
pm_runtime_use_autosuspend(&pdev->dev);
err = mei_register(dev);
err = mei_register(dev, &pdev->dev);
if (err)
goto release_irq;
......@@ -306,7 +306,7 @@ static int mei_txe_pm_runtime_idle(struct device *device)
if (!dev)
return -ENODEV;
if (mei_write_is_idle(dev))
pm_schedule_suspend(device, MEI_TXI_RPM_TIMEOUT * 2);
pm_runtime_autosuspend(device);
return -EBUSY;
}
......
comment "Intel MIC Bus Driver"
config INTEL_MIC_BUS
tristate "Intel MIC Bus Driver"
depends on 64BIT && PCI && X86 && X86_DEV_DMA_OPS
help
This option is selected by any driver which registers a
device or driver on the MIC Bus, such as CONFIG_INTEL_MIC_HOST,
CONFIG_INTEL_MIC_CARD, CONFIG_INTEL_MIC_X100_DMA etc.
If you are building a host/card kernel with an Intel MIC device
then say M (recommended) or Y, else say N. If unsure say N.
More information about the Intel MIC family as well as the Linux
OS and tools for MIC to use with this driver are available from
<http://software.intel.com/en-us/mic-developer>.
comment "Intel MIC Host Driver"
config INTEL_MIC_HOST
tristate "Intel MIC Host Driver"
depends on 64BIT && PCI && X86
depends on 64BIT && PCI && X86 && INTEL_MIC_BUS
select VHOST_RING
help
This enables Host Driver support for the Intel Many Integrated
......@@ -22,7 +39,7 @@ comment "Intel MIC Card Driver"
config INTEL_MIC_CARD
tristate "Intel MIC Card Driver"
depends on 64BIT && X86
depends on 64BIT && X86 && INTEL_MIC_BUS
select VIRTIO
help
This enables card driver support for the Intel Many Integrated
......
......@@ -4,3 +4,4 @@
#
obj-$(CONFIG_INTEL_MIC_HOST) += host/
obj-$(CONFIG_INTEL_MIC_CARD) += card/
obj-$(CONFIG_INTEL_MIC_BUS) += bus/
#
# Makefile - Intel MIC Linux driver.
# Copyright(c) 2014, Intel Corporation.
#
obj-$(CONFIG_INTEL_MIC_BUS) += mic_bus.o
/*
* Intel MIC Platform Software Stack (MPSS)
*
* Copyright(c) 2014 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 Bus driver.
*
* This implementation is very similar to the the virtio bus driver
* implementation @ drivers/virtio/virtio.c
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/idr.h>
#include <linux/mic_bus.h>
/* Unique numbering for mbus devices. */
static DEFINE_IDA(mbus_index_ida);
static ssize_t device_show(struct device *d,
struct device_attribute *attr, char *buf)
{
struct mbus_device *dev = dev_to_mbus(d);
return sprintf(buf, "0x%04x\n", dev->id.device);
}
static DEVICE_ATTR_RO(device);
static ssize_t vendor_show(struct device *d,
struct device_attribute *attr, char *buf)
{
struct mbus_device *dev = dev_to_mbus(d);
return sprintf(buf, "0x%04x\n", dev->id.vendor);
}
static DEVICE_ATTR_RO(vendor);
static ssize_t modalias_show(struct device *d,
struct device_attribute *attr, char *buf)
{
struct mbus_device *dev = dev_to_mbus(d);
return sprintf(buf, "mbus:d%08Xv%08X\n",
dev->id.device, dev->id.vendor);
}
static DEVICE_ATTR_RO(modalias);
static struct attribute *mbus_dev_attrs[] = {
&dev_attr_device.attr,
&dev_attr_vendor.attr,
&dev_attr_modalias.attr,
NULL,
};
ATTRIBUTE_GROUPS(mbus_dev);
static inline int mbus_id_match(const struct mbus_device *dev,
const struct mbus_device_id *id)
{
if (id->device != dev->id.device && id->device != MBUS_DEV_ANY_ID)
return 0;
return id->vendor == MBUS_DEV_ANY_ID || id->vendor == dev->id.vendor;
}
/*
* This looks through all the IDs a driver claims to support. If any of them
* match, we return 1 and the kernel will call mbus_dev_probe().
*/
static int mbus_dev_match(struct device *dv, struct device_driver *dr)
{
unsigned int i;
struct mbus_device *dev = dev_to_mbus(dv);
const struct mbus_device_id *ids;
ids = drv_to_mbus(dr)->id_table;
for (i = 0; ids[i].device; i++)
if (mbus_id_match(dev, &ids[i]))
return 1;
return 0;
}
static int mbus_uevent(struct device *dv, struct kobj_uevent_env *env)
{
struct mbus_device *dev = dev_to_mbus(dv);
return add_uevent_var(env, "MODALIAS=mbus:d%08Xv%08X",
dev->id.device, dev->id.vendor);
}
static int mbus_dev_probe(struct device *d)
{
int err;
struct mbus_device *dev = dev_to_mbus(d);
struct mbus_driver *drv = drv_to_mbus(dev->dev.driver);
err = drv->probe(dev);
if (!err)
if (drv->scan)
drv->scan(dev);
return err;
}
static int mbus_dev_remove(struct device *d)
{
struct mbus_device *dev = dev_to_mbus(d);
struct mbus_driver *drv = drv_to_mbus(dev->dev.driver);
drv->remove(dev);
return 0;
}
static struct bus_type mic_bus = {
.name = "mic_bus",
.match = mbus_dev_match,
.dev_groups = mbus_dev_groups,
.uevent = mbus_uevent,
.probe = mbus_dev_probe,
.remove = mbus_dev_remove,
};
int mbus_register_driver(struct mbus_driver *driver)
{
driver->driver.bus = &mic_bus;
return driver_register(&driver->driver);
}
EXPORT_SYMBOL_GPL(mbus_register_driver);
void mbus_unregister_driver(struct mbus_driver *driver)
{
driver_unregister(&driver->driver);
}
EXPORT_SYMBOL_GPL(mbus_unregister_driver);
static void mbus_release_dev(struct device *d)
{
struct mbus_device *mbdev = dev_to_mbus(d);
kfree(mbdev);
}
struct mbus_device *
mbus_register_device(struct device *pdev, int id, struct dma_map_ops *dma_ops,
struct mbus_hw_ops *hw_ops, void __iomem *mmio_va)
{
int ret;
struct mbus_device *mbdev;
mbdev = kzalloc(sizeof(*mbdev), GFP_KERNEL);
if (!mbdev)
return ERR_PTR(-ENOMEM);
mbdev->mmio_va = mmio_va;
mbdev->dev.parent = pdev;
mbdev->id.device = id;
mbdev->id.vendor = MBUS_DEV_ANY_ID;
mbdev->dev.archdata.dma_ops = dma_ops;
mbdev->dev.dma_mask = &mbdev->dev.coherent_dma_mask;
dma_set_mask(&mbdev->dev, DMA_BIT_MASK(64));
mbdev->dev.release = mbus_release_dev;
mbdev->hw_ops = hw_ops;
mbdev->dev.bus = &mic_bus;
/* Assign a unique device index and hence name. */
ret = ida_simple_get(&mbus_index_ida, 0, 0, GFP_KERNEL);
if (ret < 0)
goto free_mbdev;
mbdev->index = ret;
dev_set_name(&mbdev->dev, "mbus-dev%u", mbdev->index);
/*
* device_register() causes the bus infrastructure to look for a
* matching driver.
*/
ret = device_register(&mbdev->dev);
if (ret)
goto ida_remove;
return mbdev;
ida_remove:
ida_simple_remove(&mbus_index_ida, mbdev->index);
free_mbdev:
kfree(mbdev);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(mbus_register_device);
void mbus_unregister_device(struct mbus_device *mbdev)
{
int index = mbdev->index; /* save for after device release */
device_unregister(&mbdev->dev);
ida_simple_remove(&mbus_index_ida, index);
}
EXPORT_SYMBOL_GPL(mbus_unregister_device);
static int __init mbus_init(void)
{
return bus_register(&mic_bus);
}
static void __exit mbus_exit(void)
{
bus_unregister(&mic_bus);
ida_destroy(&mbus_index_ida);
}
core_initcall(mbus_init);
module_exit(mbus_exit);
MODULE_AUTHOR("Intel Corporation");
MODULE_DESCRIPTION("Intel(R) MIC Bus driver");
MODULE_LICENSE("GPL v2");
......@@ -83,8 +83,8 @@ static int mic_shutdown_init(void)
int shutdown_db;
shutdown_db = mic_next_card_db();
shutdown_cookie = mic_request_card_irq(mic_shutdown_isr,
"Shutdown", mdrv, shutdown_db);
shutdown_cookie = mic_request_card_irq(mic_shutdown_isr, NULL,
"Shutdown", mdrv, shutdown_db);
if (IS_ERR(shutdown_cookie))
rc = PTR_ERR(shutdown_cookie);
else
......@@ -136,7 +136,8 @@ static void mic_dp_uninit(void)
/**
* mic_request_card_irq - request an irq.
*
* @func: The callback function that handles the interrupt.
* @handler: interrupt handler passed to request_threaded_irq.
* @thread_fn: thread fn. passed to request_threaded_irq.
* @name: The ASCII name of the callee requesting the irq.
* @data: private data that is returned back when calling the
* function handler.
......@@ -149,17 +150,19 @@ static void mic_dp_uninit(void)
* error code.
*
*/
struct mic_irq *mic_request_card_irq(irqreturn_t (*func)(int irq, void *data),
const char *name, void *data, int index)
struct mic_irq *
mic_request_card_irq(irq_handler_t handler,
irq_handler_t thread_fn, const char *name,
void *data, int index)
{
int rc = 0;
unsigned long cookie;
struct mic_driver *mdrv = g_drv;
rc = request_irq(mic_db_to_irq(mdrv, index), func,
0, name, data);
rc = request_threaded_irq(mic_db_to_irq(mdrv, index), handler,
thread_fn, 0, name, data);
if (rc) {
dev_err(mdrv->dev, "request_irq failed rc = %d\n", rc);
dev_err(mdrv->dev, "request_threaded_irq failed rc = %d\n", rc);
goto err;
}
mdrv->irq_info.irq_usage_count[index]++;
......@@ -172,9 +175,9 @@ struct mic_irq *mic_request_card_irq(irqreturn_t (*func)(int irq, void *data),
/**
* mic_free_card_irq - free irq.
*
* @cookie: cookie obtained during a successful call to mic_request_irq
* @cookie: cookie obtained during a successful call to mic_request_threaded_irq
* @data: private data specified by the calling function during the
* mic_request_irq
* mic_request_threaded_irq
*
* returns: none.
*/
......
......@@ -30,6 +30,8 @@
#include <linux/workqueue.h>
#include <linux/io.h>
#include <linux/irqreturn.h>
#include <linux/interrupt.h>
#include <linux/mic_bus.h>
/**
* struct mic_intr_info - Contains h/w specific interrupt sources info
......@@ -70,6 +72,7 @@ struct mic_device {
* @hotplug_work: Hot plug work for adding/removing virtio devices.
* @irq_info: The OS specific irq information
* @intr_info: H/W specific interrupt information.
* @dma_mbdev: dma device on the MIC virtual bus.
*/
struct mic_driver {
char name[20];
......@@ -80,6 +83,7 @@ struct mic_driver {
struct work_struct hotplug_work;
struct mic_irq_info irq_info;
struct mic_intr_info intr_info;
struct mbus_device *dma_mbdev;
};
/**
......@@ -116,8 +120,9 @@ mic_mmio_write(struct mic_mw *mw, u32 val, u32 offset)
int mic_driver_init(struct mic_driver *mdrv);
void mic_driver_uninit(struct mic_driver *mdrv);
int mic_next_card_db(void);
struct mic_irq *mic_request_card_irq(irqreturn_t (*func)(int irq, void *data),
const char *name, void *data, int intr_src);
struct mic_irq *
mic_request_card_irq(irq_handler_t handler, irq_handler_t thread_fn,
const char *name, void *data, int intr_src);
void mic_free_card_irq(struct mic_irq *cookie, void *data);
u32 mic_read_spad(struct mic_device *mdev, unsigned int idx);
void mic_send_intr(struct mic_device *mdev, int doorbell);
......
......@@ -417,7 +417,7 @@ static int mic_add_device(struct mic_device_desc __iomem *d,
virtio_db = mic_next_card_db();
mvdev->virtio_cookie = mic_request_card_irq(mic_virtio_intr_handler,
"virtio intr", mvdev, virtio_db);
NULL, "virtio intr", mvdev, virtio_db);
if (IS_ERR(mvdev->virtio_cookie)) {
ret = PTR_ERR(mvdev->virtio_cookie);
goto kfree;
......@@ -606,8 +606,9 @@ int mic_devices_init(struct mic_driver *mdrv)
mic_scan_devices(mdrv, !REMOVE_DEVICES);
config_db = mic_next_card_db();
virtio_config_cookie = mic_request_card_irq(mic_extint_handler,
"virtio_config_intr", mdrv, config_db);
virtio_config_cookie = mic_request_card_irq(mic_extint_handler, NULL,
"virtio_config_intr", mdrv,
config_db);
if (IS_ERR(virtio_config_cookie)) {
rc = PTR_ERR(virtio_config_cookie);
goto exit;
......
......@@ -148,6 +148,47 @@ void mic_card_unmap(struct mic_device *mdev, void __iomem *addr)
iounmap(addr);
}
static inline struct mic_driver *mbdev_to_mdrv(struct mbus_device *mbdev)
{
return dev_get_drvdata(mbdev->dev.parent);
}
static struct mic_irq *
_mic_request_threaded_irq(struct mbus_device *mbdev,
irq_handler_t handler, irq_handler_t thread_fn,
const char *name, void *data, int intr_src)
{
int rc = 0;
unsigned int irq = intr_src;
unsigned long cookie = irq;
rc = request_threaded_irq(irq, handler, thread_fn, 0, name, data);
if (rc) {
dev_err(mbdev_to_mdrv(mbdev)->dev,
"request_threaded_irq failed rc = %d\n", rc);
return ERR_PTR(rc);
}
return (struct mic_irq *)cookie;
}
static void _mic_free_irq(struct mbus_device *mbdev,
struct mic_irq *cookie, void *data)
{
unsigned long irq = (unsigned long)cookie;
free_irq(irq, data);
}
static void _mic_ack_interrupt(struct mbus_device *mbdev, int num)
{
mic_ack_interrupt(&mbdev_to_mdrv(mbdev)->mdev);
}
static struct mbus_hw_ops mbus_hw_ops = {
.request_threaded_irq = _mic_request_threaded_irq,
.free_irq = _mic_free_irq,
.ack_interrupt = _mic_ack_interrupt,
};
static int __init mic_probe(struct platform_device *pdev)
{
struct mic_driver *mdrv = &g_drv;
......@@ -159,32 +200,41 @@ static int __init mic_probe(struct platform_device *pdev)
mdev->mmio.pa = MIC_X100_MMIO_BASE;
mdev->mmio.len = MIC_X100_MMIO_LEN;
mdev->mmio.va = ioremap(MIC_X100_MMIO_BASE, MIC_X100_MMIO_LEN);
mdev->mmio.va = devm_ioremap(&pdev->dev, MIC_X100_MMIO_BASE,
MIC_X100_MMIO_LEN);
if (!mdev->mmio.va) {
dev_err(&pdev->dev, "Cannot remap MMIO BAR\n");
rc = -EIO;
goto done;
}
mic_hw_intr_init(mdrv);
platform_set_drvdata(pdev, mdrv);
mdrv->dma_mbdev = mbus_register_device(mdrv->dev, MBUS_DEV_DMA_MIC,
NULL, &mbus_hw_ops,
mdrv->mdev.mmio.va);
if (IS_ERR(mdrv->dma_mbdev)) {
rc = PTR_ERR(mdrv->dma_mbdev);
dev_err(&pdev->dev, "mbus_add_device failed rc %d\n", rc);
goto done;
}
rc = mic_driver_init(mdrv);
if (rc) {
dev_err(&pdev->dev, "mic_driver_init failed rc %d\n", rc);
goto iounmap;
goto remove_dma;
}
done:
return rc;
iounmap:
iounmap(mdev->mmio.va);
remove_dma:
mbus_unregister_device(mdrv->dma_mbdev);
return rc;
}
static int mic_remove(struct platform_device *pdev)
{
struct mic_driver *mdrv = &g_drv;
struct mic_device *mdev = &mdrv->mdev;
mic_driver_uninit(mdrv);
iounmap(mdev->mmio.va);
mbus_unregister_device(mdrv->dma_mbdev);
return 0;
}
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
obj-${CONFIG_THUNDERBOLT} := thunderbolt.o
thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel_pci.o eeprom.o
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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