diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-04 20:32:24 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-04 20:32:24 -0400 |
| commit | 2521129a6d2fd8a81f99cf95055eddea3df914ff (patch) | |
| tree | f8b7879979f656669ce31cbc247b97ae702291fb | |
| parent | 98a96f202203fecad65b44449077c695686ad4db (diff) | |
| parent | 16eb2bfc65ef86d3ac6420d50ddc2c48f0023cee (diff) | |
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
...
140 files changed, 8646 insertions, 1083 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-mei b/Documentation/ABI/testing/sysfs-class-mei new file mode 100644 index 000000000000..0ec8b8178c41 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-mei | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | What: /sys/class/mei/ | ||
| 2 | Date: May 2014 | ||
| 3 | KernelVersion: 3.17 | ||
| 4 | Contact: Tomas Winkler <tomas.winkler@intel.com> | ||
| 5 | Description: | ||
| 6 | The mei/ class sub-directory belongs to mei device class | ||
| 7 | |||
| 8 | |||
| 9 | What: /sys/class/mei/meiN/ | ||
| 10 | Date: May 2014 | ||
| 11 | KernelVersion: 3.17 | ||
| 12 | Contact: Tomas Winkler <tomas.winkler@intel.com> | ||
| 13 | Description: | ||
| 14 | The /sys/class/mei/meiN directory is created for | ||
| 15 | each probed mei device | ||
| 16 | |||
diff --git a/Documentation/ABI/testing/sysfs-driver-genwqe b/Documentation/ABI/testing/sysfs-driver-genwqe index 1870737a1f5e..64ac6d567c4b 100644 --- a/Documentation/ABI/testing/sysfs-driver-genwqe +++ b/Documentation/ABI/testing/sysfs-driver-genwqe | |||
| @@ -25,6 +25,15 @@ Date: Oct 2013 | |||
| 25 | Contact: haver@linux.vnet.ibm.com | 25 | Contact: haver@linux.vnet.ibm.com |
| 26 | Description: Interface to set the next bitstream to be used. | 26 | Description: Interface to set the next bitstream to be used. |
| 27 | 27 | ||
| 28 | What: /sys/class/genwqe/genwqe<n>_card/reload_bitstream | ||
| 29 | Date: May 2014 | ||
| 30 | Contact: klebers@linux.vnet.ibm.com | ||
| 31 | Description: Interface to trigger a PCIe card reset to reload the bitstream. | ||
| 32 | sudo sh -c 'echo 1 > \ | ||
| 33 | /sys/class/genwqe/genwqe0_card/reload_bitstream' | ||
| 34 | If successfully, the card will come back with the bitstream set | ||
| 35 | on 'next_bitstream'. | ||
| 36 | |||
| 28 | What: /sys/class/genwqe/genwqe<n>_card/tempsens | 37 | What: /sys/class/genwqe/genwqe<n>_card/tempsens |
| 29 | Date: Oct 2013 | 38 | Date: Oct 2013 |
| 30 | Contact: haver@linux.vnet.ibm.com | 39 | Contact: haver@linux.vnet.ibm.com |
diff --git a/Documentation/devicetree/bindings/extcon/extcon-sm5502.txt b/Documentation/devicetree/bindings/extcon/extcon-sm5502.txt new file mode 100644 index 000000000000..4ecda224955f --- /dev/null +++ b/Documentation/devicetree/bindings/extcon/extcon-sm5502.txt | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | |||
| 2 | * SM5502 MUIC (Micro-USB Interface Controller) device | ||
| 3 | |||
| 4 | The Silicon Mitus SM5502 is a MUIC (Micro-USB Interface Controller) device | ||
| 5 | which can detect the state of external accessory when external accessory is | ||
| 6 | attached or detached and button is pressed or released. It is interfaced to | ||
| 7 | the host controller using an I2C interface. | ||
| 8 | |||
| 9 | Required properties: | ||
| 10 | - compatible: Should be "siliconmitus,sm5502-muic" | ||
| 11 | - reg: Specifies the I2C slave address of the MUIC block. It should be 0x25 | ||
| 12 | - interrupt-parent: Specifies the phandle of the interrupt controller to which | ||
| 13 | the interrupts from sm5502 are delivered to. | ||
| 14 | - interrupts: Interrupt specifiers for detection interrupt sources. | ||
| 15 | |||
| 16 | Example: | ||
| 17 | |||
| 18 | sm5502@25 { | ||
| 19 | compatible = "siliconmitus,sm5502-muic"; | ||
| 20 | interrupt-parent = <&gpx1>; | ||
| 21 | interrupts = <5 0>; | ||
| 22 | reg = <0x25>; | ||
| 23 | }; | ||
diff --git a/Documentation/mic/mic_overview.txt b/Documentation/mic/mic_overview.txt index b41929224804..77c541802ad9 100644 --- a/Documentation/mic/mic_overview.txt +++ b/Documentation/mic/mic_overview.txt | |||
| @@ -17,35 +17,50 @@ for applications. A key benefit of our solution is that it leverages | |||
| 17 | the standard virtio framework for network, disk and console devices, | 17 | the standard virtio framework for network, disk and console devices, |
| 18 | though in our case the virtio framework is used across a PCIe bus. | 18 | though in our case the virtio framework is used across a PCIe bus. |
| 19 | 19 | ||
| 20 | MIC PCIe card has a dma controller with 8 channels. These channels are | ||
| 21 | shared between the host s/w and the card s/w. 0 to 3 are used by host | ||
| 22 | and 4 to 7 by card. As the dma device doesn't show up as PCIe device, | ||
| 23 | a virtual bus called mic bus is created and virtual dma devices are | ||
| 24 | created on it by the host/card drivers. On host the channels are private | ||
| 25 | and used only by the host driver to transfer data for the virtio devices. | ||
| 26 | |||
| 20 | Here is a block diagram of the various components described above. The | 27 | Here is a block diagram of the various components described above. The |
| 21 | virtio backends are situated on the host rather than the card given better | 28 | virtio backends are situated on the host rather than the card given better |
| 22 | single threaded performance for the host compared to MIC, the ability of | 29 | single threaded performance for the host compared to MIC, the ability of |
| 23 | the host to initiate DMA's to/from the card using the MIC DMA engine and | 30 | the host to initiate DMA's to/from the card using the MIC DMA engine and |
| 24 | the fact that the virtio block storage backend can only be on the host. | 31 | the fact that the virtio block storage backend can only be on the host. |
| 25 | 32 | ||
| 26 | | | 33 | | |
| 27 | +----------+ | +----------+ | 34 | +----------+ | +----------+ |
| 28 | | Card OS | | | Host OS | | 35 | | Card OS | | | Host OS | |
| 29 | +----------+ | +----------+ | 36 | +----------+ | +----------+ |
| 30 | | | 37 | | |
| 31 | +-------+ +--------+ +------+ | +---------+ +--------+ +--------+ | 38 | +-------+ +--------+ +------+ | +---------+ +--------+ +--------+ |
| 32 | | Virtio| |Virtio | |Virtio| | |Virtio | |Virtio | |Virtio | | 39 | | Virtio| |Virtio | |Virtio| | |Virtio | |Virtio | |Virtio | |
| 33 | | Net | |Console | |Block | | |Net | |Console | |Block | | 40 | | Net | |Console | |Block | | |Net | |Console | |Block | |
| 34 | | Driver| |Driver | |Driver| | |backend | |backend | |backend | | 41 | | Driver| |Driver | |Driver| | |backend | |backend | |backend | |
| 35 | +-------+ +--------+ +------+ | +---------+ +--------+ +--------+ | 42 | +-------+ +--------+ +------+ | +---------+ +--------+ +--------+ |
| 36 | | | | | | | | | 43 | | | | | | | | |
| 37 | | | | |User | | | | 44 | | | | |User | | | |
| 38 | | | | |------|------------|---------|------- | 45 | | | | |------|------------|---------|------- |
| 39 | +-------------------+ |Kernel +--------------------------+ | 46 | +-------------------+ |Kernel +--------------------------+ |
| 40 | | | | Virtio over PCIe IOCTLs | | 47 | | | | Virtio over PCIe IOCTLs | |
| 41 | | | +--------------------------+ | 48 | | | +--------------------------+ |
| 42 | +--------------+ | | | 49 | +-----------+ | | | +-----------+ |
| 43 | |Intel MIC | | +---------------+ | 50 | | MIC DMA | | | | | MIC DMA | |
| 44 | |Card Driver | | |Intel MIC | | 51 | | Driver | | | | | Driver | |
| 45 | +--------------+ | |Host Driver | | 52 | +-----------+ | | | +-----------+ |
| 46 | | | +---------------+ | 53 | | | | | | |
| 47 | | | | | 54 | +---------------+ | | | +----------------+ |
| 48 | +-------------------------------------------------------------+ | 55 | |MIC virtual Bus| | | | |MIC virtual Bus | |
| 49 | | | | 56 | +---------------+ | | | +----------------+ |
| 50 | | PCIe Bus | | 57 | | | | | | |
| 51 | +-------------------------------------------------------------+ | 58 | | +--------------+ | +---------------+ | |
| 59 | | |Intel MIC | | |Intel MIC | | | ||
| 60 | +---|Card Driver | | |Host Driver | | | ||
| 61 | +--------------+ | +---------------+-----+ | ||
| 62 | | | | | ||
| 63 | +-------------------------------------------------------------+ | ||
| 64 | | | | ||
| 65 | | PCIe Bus | | ||
| 66 | +-------------------------------------------------------------+ | ||
diff --git a/Documentation/mic/mpssd/mpss b/Documentation/mic/mpssd/mpss index 3136c68dad0b..cacbdb0aefb9 100755 --- a/Documentation/mic/mpssd/mpss +++ b/Documentation/mic/mpssd/mpss | |||
| @@ -48,18 +48,18 @@ start() | |||
| 48 | fi | 48 | fi |
| 49 | 49 | ||
| 50 | echo -e $"Starting MPSS Stack" | 50 | echo -e $"Starting MPSS Stack" |
| 51 | echo -e $"Loading MIC_HOST Module" | 51 | echo -e $"Loading MIC_X100_DMA & MIC_HOST Modules" |
| 52 | 52 | ||
| 53 | # Ensure the driver is loaded | 53 | for f in "mic_host" "mic_x100_dma" |
| 54 | if [ ! -d "$sysfs" ]; then | 54 | do |
| 55 | modprobe mic_host | 55 | modprobe $f |
| 56 | RETVAL=$? | 56 | RETVAL=$? |
| 57 | if [ $RETVAL -ne 0 ]; then | 57 | if [ $RETVAL -ne 0 ]; then |
| 58 | failure | 58 | failure |
| 59 | echo | 59 | echo |
| 60 | return $RETVAL | 60 | return $RETVAL |
| 61 | fi | 61 | fi |
| 62 | fi | 62 | done |
| 63 | 63 | ||
| 64 | # Start the daemon | 64 | # Start the daemon |
| 65 | echo -n $"Starting MPSSD " | 65 | echo -n $"Starting MPSSD " |
| @@ -170,8 +170,8 @@ unload() | |||
| 170 | stop | 170 | stop |
| 171 | 171 | ||
| 172 | sleep 5 | 172 | sleep 5 |
| 173 | echo -n $"Removing MIC_HOST Module: " | 173 | echo -n $"Removing MIC_HOST & MIC_X100_DMA Modules: " |
| 174 | modprobe -r mic_host | 174 | modprobe -r mic_host mic_x100_dma |
| 175 | RETVAL=$? | 175 | RETVAL=$? |
| 176 | [ $RETVAL -ne 0 ] && failure || success | 176 | [ $RETVAL -ne 0 ] && failure || success |
| 177 | echo | 177 | echo |
diff --git a/Documentation/w1/slaves/w1_ds2406 b/Documentation/w1/slaves/w1_ds2406 new file mode 100644 index 000000000000..8137fe6f6c3d --- /dev/null +++ b/Documentation/w1/slaves/w1_ds2406 | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | w1_ds2406 kernel driver | ||
| 2 | ======================= | ||
| 3 | |||
| 4 | Supported chips: | ||
| 5 | * Maxim DS2406 (and other family 0x12) addressable switches | ||
| 6 | |||
| 7 | Author: Scott Alfter <scott@alfter.us> | ||
| 8 | |||
| 9 | Description | ||
| 10 | ----------- | ||
| 11 | |||
| 12 | The w1_ds2406 driver allows connected devices to be switched on and off. | ||
| 13 | These chips also provide 128 bytes of OTP EPROM, but reading/writing it is | ||
| 14 | not supported. In TSOC-6 form, the DS2406 provides two switch outputs and | ||
| 15 | can be provided with power on a dedicated input. In TO-92 form, it provides | ||
| 16 | one output and uses parasitic power only. | ||
| 17 | |||
| 18 | The driver provides two sysfs files. state is readable; it gives the | ||
| 19 | current state of each switch, with PIO A in bit 0 and PIO B in bit 1. The | ||
| 20 | driver ORs this state with 0x30, so shell scripts get an ASCII 0/1/2/3 to | ||
| 21 | work with. output is writable; bits 0 and 1 control PIO A and B, | ||
| 22 | respectively. Bits 2-7 are ignored, so it's safe to write ASCII data. | ||
| 23 | |||
| 24 | CRCs are checked on read and write. Failed checks cause an I/O error to be | ||
| 25 | returned. On a failed write, the switch status is not changed. | ||
diff --git a/MAINTAINERS b/MAINTAINERS index 1acc624ecfd7..211389b6182f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
| @@ -7844,6 +7844,11 @@ S: Maintained | |||
| 7844 | F: include/linux/mmc/dw_mmc.h | 7844 | F: include/linux/mmc/dw_mmc.h |
| 7845 | F: drivers/mmc/host/dw_mmc* | 7845 | F: drivers/mmc/host/dw_mmc* |
| 7846 | 7846 | ||
| 7847 | THUNDERBOLT DRIVER | ||
| 7848 | M: Andreas Noever <andreas.noever@gmail.com> | ||
| 7849 | S: Maintained | ||
| 7850 | F: drivers/thunderbolt/ | ||
| 7851 | |||
| 7847 | TIMEKEEPING, CLOCKSOURCE CORE, NTP | 7852 | TIMEKEEPING, CLOCKSOURCE CORE, NTP |
| 7848 | M: John Stultz <john.stultz@linaro.org> | 7853 | M: John Stultz <john.stultz@linaro.org> |
| 7849 | M: Thomas Gleixner <tglx@linutronix.de> | 7854 | M: Thomas Gleixner <tglx@linutronix.de> |
diff --git a/drivers/Kconfig b/drivers/Kconfig index 4e6e66c3c8d6..622fa266b29e 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig | |||
| @@ -178,4 +178,6 @@ source "drivers/mcb/Kconfig" | |||
| 178 | 178 | ||
| 179 | source "drivers/ras/Kconfig" | 179 | source "drivers/ras/Kconfig" |
| 180 | 180 | ||
| 181 | source "drivers/thunderbolt/Kconfig" | ||
| 182 | |||
| 181 | endmenu | 183 | endmenu |
diff --git a/drivers/Makefile b/drivers/Makefile index 65c32b1cea3d..54bfae1f09a4 100644 --- a/drivers/Makefile +++ b/drivers/Makefile | |||
| @@ -159,3 +159,4 @@ obj-$(CONFIG_FMC) += fmc/ | |||
| 159 | obj-$(CONFIG_POWERCAP) += powercap/ | 159 | obj-$(CONFIG_POWERCAP) += powercap/ |
| 160 | obj-$(CONFIG_MCB) += mcb/ | 160 | obj-$(CONFIG_MCB) += mcb/ |
| 161 | obj-$(CONFIG_RAS) += ras/ | 161 | obj-$(CONFIG_RAS) += ras/ |
| 162 | obj-$(CONFIG_THUNDERBOLT) += thunderbolt/ | ||
diff --git a/drivers/char/bsr.c b/drivers/char/bsr.c index 8fedbc250414..a6cef548e01e 100644 --- a/drivers/char/bsr.c +++ b/drivers/char/bsr.c | |||
| @@ -259,7 +259,7 @@ static int bsr_add_node(struct device_node *bn) | |||
| 259 | } | 259 | } |
| 260 | 260 | ||
| 261 | cur->bsr_device = device_create(bsr_class, NULL, cur->bsr_dev, | 261 | cur->bsr_device = device_create(bsr_class, NULL, cur->bsr_dev, |
| 262 | cur, cur->bsr_name); | 262 | cur, "%s", cur->bsr_name); |
| 263 | if (IS_ERR(cur->bsr_device)) { | 263 | if (IS_ERR(cur->bsr_device)) { |
| 264 | printk(KERN_ERR "device_create failed for %s\n", | 264 | printk(KERN_ERR "device_create failed for %s\n", |
| 265 | cur->bsr_name); | 265 | cur->bsr_name); |
diff --git a/drivers/char/dsp56k.c b/drivers/char/dsp56k.c index 01a5ca7425d7..8bf70e8c3f79 100644 --- a/drivers/char/dsp56k.c +++ b/drivers/char/dsp56k.c | |||
| @@ -383,7 +383,7 @@ static long dsp56k_ioctl(struct file *file, unsigned int cmd, | |||
| 383 | return put_user(status, &hf->status); | 383 | return put_user(status, &hf->status); |
| 384 | } | 384 | } |
| 385 | case DSP56K_HOST_CMD: | 385 | case DSP56K_HOST_CMD: |
| 386 | if (arg > 31 || arg < 0) | 386 | if (arg > 31) |
| 387 | return -EINVAL; | 387 | return -EINVAL; |
| 388 | mutex_lock(&dsp56k_mutex); | 388 | mutex_lock(&dsp56k_mutex); |
| 389 | dsp56k_host_interface.cvr = (u_char)((arg & DSP56K_CVR_HV_MASK) | | 389 | dsp56k_host_interface.cvr = (u_char)((arg & DSP56K_CVR_HV_MASK) | |
diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c index 93dcad0c1cbe..65525c7e903c 100644 --- a/drivers/char/i8k.c +++ b/drivers/char/i8k.c | |||
| @@ -65,6 +65,8 @@ static char bios_version[4]; | |||
| 65 | static struct device *i8k_hwmon_dev; | 65 | static struct device *i8k_hwmon_dev; |
| 66 | static u32 i8k_hwmon_flags; | 66 | static u32 i8k_hwmon_flags; |
| 67 | static int i8k_fan_mult; | 67 | static int i8k_fan_mult; |
| 68 | static int i8k_pwm_mult; | ||
| 69 | static int i8k_fan_max = I8K_FAN_HIGH; | ||
| 68 | 70 | ||
| 69 | #define I8K_HWMON_HAVE_TEMP1 (1 << 0) | 71 | #define I8K_HWMON_HAVE_TEMP1 (1 << 0) |
| 70 | #define I8K_HWMON_HAVE_TEMP2 (1 << 1) | 72 | #define I8K_HWMON_HAVE_TEMP2 (1 << 1) |
| @@ -97,6 +99,10 @@ static int fan_mult = I8K_FAN_MULT; | |||
| 97 | module_param(fan_mult, int, 0); | 99 | module_param(fan_mult, int, 0); |
| 98 | MODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with"); | 100 | MODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with"); |
| 99 | 101 | ||
| 102 | static int fan_max = I8K_FAN_HIGH; | ||
| 103 | module_param(fan_max, int, 0); | ||
| 104 | MODULE_PARM_DESC(fan_max, "Maximum configurable fan speed"); | ||
| 105 | |||
| 100 | static int i8k_open_fs(struct inode *inode, struct file *file); | 106 | static int i8k_open_fs(struct inode *inode, struct file *file); |
| 101 | static long i8k_ioctl(struct file *, unsigned int, unsigned long); | 107 | static long i8k_ioctl(struct file *, unsigned int, unsigned long); |
| 102 | 108 | ||
| @@ -276,7 +282,7 @@ static int i8k_set_fan(int fan, int speed) | |||
| 276 | { | 282 | { |
| 277 | struct smm_regs regs = { .eax = I8K_SMM_SET_FAN, }; | 283 | struct smm_regs regs = { .eax = I8K_SMM_SET_FAN, }; |
| 278 | 284 | ||
| 279 | speed = (speed < 0) ? 0 : ((speed > I8K_FAN_MAX) ? I8K_FAN_MAX : speed); | 285 | speed = (speed < 0) ? 0 : ((speed > i8k_fan_max) ? i8k_fan_max : speed); |
| 280 | regs.ebx = (fan & 0xff) | (speed << 8); | 286 | regs.ebx = (fan & 0xff) | (speed << 8); |
| 281 | 287 | ||
| 282 | return i8k_smm(®s) ? : i8k_get_fan_status(fan); | 288 | return i8k_smm(®s) ? : i8k_get_fan_status(fan); |
| @@ -521,7 +527,7 @@ static ssize_t i8k_hwmon_show_pwm(struct device *dev, | |||
| 521 | status = i8k_get_fan_status(index); | 527 | status = i8k_get_fan_status(index); |
| 522 | if (status < 0) | 528 | if (status < 0) |
| 523 | return -EIO; | 529 | return -EIO; |
| 524 | return sprintf(buf, "%d\n", clamp_val(status * 128, 0, 255)); | 530 | return sprintf(buf, "%d\n", clamp_val(status * i8k_pwm_mult, 0, 255)); |
| 525 | } | 531 | } |
| 526 | 532 | ||
| 527 | static ssize_t i8k_hwmon_set_pwm(struct device *dev, | 533 | static ssize_t i8k_hwmon_set_pwm(struct device *dev, |
| @@ -535,7 +541,7 @@ static ssize_t i8k_hwmon_set_pwm(struct device *dev, | |||
| 535 | err = kstrtoul(buf, 10, &val); | 541 | err = kstrtoul(buf, 10, &val); |
| 536 | if (err) | 542 | if (err) |
| 537 | return err; | 543 | return err; |
| 538 | val = clamp_val(DIV_ROUND_CLOSEST(val, 128), 0, 2); | 544 | val = clamp_val(DIV_ROUND_CLOSEST(val, i8k_pwm_mult), 0, i8k_fan_max); |
| 539 | 545 | ||
| 540 | mutex_lock(&i8k_mutex); | 546 | mutex_lock(&i8k_mutex); |
| 541 | err = i8k_set_fan(index, val); | 547 | err = i8k_set_fan(index, val); |
| @@ -544,20 +550,6 @@ static ssize_t i8k_hwmon_set_pwm(struct device *dev, | |||
| 544 | return err < 0 ? -EIO : count; | 550 | return err < 0 ? -EIO : count; |
| 545 | } | 551 | } |
| 546 | 552 | ||
| 547 | static ssize_t i8k_hwmon_show_label(struct device *dev, | ||
| 548 | struct device_attribute *devattr, | ||
| 549 | char *buf) | ||
| 550 | { | ||
| 551 | static const char *labels[3] = { | ||
| 552 | "CPU", | ||
| 553 | "Left Fan", | ||
| 554 | "Right Fan", | ||
| 555 | }; | ||
| 556 | int index = to_sensor_dev_attr(devattr)->index; | ||
| 557 | |||
| 558 | return sprintf(buf, "%s\n", labels[index]); | ||
| 559 | } | ||
| 560 | |||
| 561 | static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 0); | 553 | static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 0); |
| 562 | static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 1); | 554 | static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 1); |
| 563 | static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 2); | 555 | 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, | |||
| 570 | I8K_FAN_RIGHT); | 562 | I8K_FAN_RIGHT); |
| 571 | static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, i8k_hwmon_show_pwm, | 563 | static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, i8k_hwmon_show_pwm, |
| 572 | i8k_hwmon_set_pwm, I8K_FAN_RIGHT); | 564 | i8k_hwmon_set_pwm, I8K_FAN_RIGHT); |
| 573 | static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 0); | ||
| 574 | static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 1); | ||
| 575 | static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, i8k_hwmon_show_label, NULL, 2); | ||
| 576 | 565 | ||
| 577 | static struct attribute *i8k_attrs[] = { | 566 | static struct attribute *i8k_attrs[] = { |
| 578 | &sensor_dev_attr_temp1_input.dev_attr.attr, /* 0 */ | 567 | &sensor_dev_attr_temp1_input.dev_attr.attr, /* 0 */ |
| 579 | &sensor_dev_attr_temp1_label.dev_attr.attr, /* 1 */ | 568 | &sensor_dev_attr_temp2_input.dev_attr.attr, /* 1 */ |
| 580 | &sensor_dev_attr_temp2_input.dev_attr.attr, /* 2 */ | 569 | &sensor_dev_attr_temp3_input.dev_attr.attr, /* 2 */ |
| 581 | &sensor_dev_attr_temp3_input.dev_attr.attr, /* 3 */ | 570 | &sensor_dev_attr_temp4_input.dev_attr.attr, /* 3 */ |
| 582 | &sensor_dev_attr_temp4_input.dev_attr.attr, /* 4 */ | 571 | &sensor_dev_attr_fan1_input.dev_attr.attr, /* 4 */ |
| 583 | &sensor_dev_attr_fan1_input.dev_attr.attr, /* 5 */ | 572 | &sensor_dev_attr_pwm1.dev_attr.attr, /* 5 */ |
| 584 | &sensor_dev_attr_pwm1.dev_attr.attr, /* 6 */ | 573 | &sensor_dev_attr_fan2_input.dev_attr.attr, /* 6 */ |
| 585 | &sensor_dev_attr_fan1_label.dev_attr.attr, /* 7 */ | 574 | &sensor_dev_attr_pwm2.dev_attr.attr, /* 7 */ |
| 586 | &sensor_dev_attr_fan2_input.dev_attr.attr, /* 8 */ | ||
| 587 | &sensor_dev_attr_pwm2.dev_attr.attr, /* 9 */ | ||
| 588 | &sensor_dev_attr_fan2_label.dev_attr.attr, /* 10 */ | ||
| 589 | NULL | 575 | NULL |
| 590 | }; | 576 | }; |
| 591 | 577 | ||
| 592 | static umode_t i8k_is_visible(struct kobject *kobj, struct attribute *attr, | 578 | static umode_t i8k_is_visible(struct kobject *kobj, struct attribute *attr, |
| 593 | int index) | 579 | int index) |
| 594 | { | 580 | { |
| 595 | if ((index == 0 || index == 1) && | 581 | if (index == 0 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP1)) |
| 596 | !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP1)) | ||
| 597 | return 0; | 582 | return 0; |
| 598 | if (index == 2 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP2)) | 583 | if (index == 1 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP2)) |
| 599 | return 0; | 584 | return 0; |
| 600 | if (index == 3 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP3)) | 585 | if (index == 2 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP3)) |
| 601 | return 0; | 586 | return 0; |
| 602 | if (index == 4 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP4)) | 587 | if (index == 3 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP4)) |
| 603 | return 0; | 588 | return 0; |
| 604 | if (index >= 5 && index <= 7 && | 589 | if (index >= 4 && index <= 5 && |
| 605 | !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN1)) | 590 | !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN1)) |
| 606 | return 0; | 591 | return 0; |
| 607 | if (index >= 8 && index <= 10 && | 592 | if (index >= 6 && index <= 7 && |
| 608 | !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN2)) | 593 | !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN2)) |
| 609 | return 0; | 594 | return 0; |
| 610 | 595 | ||
| @@ -659,6 +644,37 @@ static int __init i8k_init_hwmon(void) | |||
| 659 | return 0; | 644 | return 0; |
| 660 | } | 645 | } |
| 661 | 646 | ||
| 647 | struct i8k_config_data { | ||
| 648 | int fan_mult; | ||
| 649 | int fan_max; | ||
| 650 | }; | ||
| 651 | |||
| 652 | enum i8k_configs { | ||
| 653 | DELL_LATITUDE_D520, | ||
| 654 | DELL_PRECISION_490, | ||
| 655 | DELL_STUDIO, | ||
| 656 | DELL_XPS_M140, | ||
| 657 | }; | ||
| 658 | |||
| 659 | static const struct i8k_config_data i8k_config_data[] = { | ||
| 660 | [DELL_LATITUDE_D520] = { | ||
| 661 | .fan_mult = 1, | ||
| 662 | .fan_max = I8K_FAN_TURBO, | ||
| 663 | }, | ||
| 664 | [DELL_PRECISION_490] = { | ||
| 665 | .fan_mult = 1, | ||
| 666 | .fan_max = I8K_FAN_TURBO, | ||
| 667 | }, | ||
| 668 | [DELL_STUDIO] = { | ||
| 669 | .fan_mult = 1, | ||
| 670 | .fan_max = I8K_FAN_HIGH, | ||
| 671 | }, | ||
| 672 | [DELL_XPS_M140] = { | ||
| 673 | .fan_mult = 1, | ||
| 674 | .fan_max = I8K_FAN_HIGH, | ||
| 675 | }, | ||
| 676 | }; | ||
| 677 | |||
| 662 | static struct dmi_system_id i8k_dmi_table[] __initdata = { | 678 | static struct dmi_system_id i8k_dmi_table[] __initdata = { |
| 663 | { | 679 | { |
| 664 | .ident = "Dell Inspiron", | 680 | .ident = "Dell Inspiron", |
| @@ -682,6 +698,14 @@ static struct dmi_system_id i8k_dmi_table[] __initdata = { | |||
| 682 | }, | 698 | }, |
| 683 | }, | 699 | }, |
| 684 | { | 700 | { |
| 701 | .ident = "Dell Latitude D520", | ||
| 702 | .matches = { | ||
| 703 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
| 704 | DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D520"), | ||
| 705 | }, | ||
| 706 | .driver_data = (void *)&i8k_config_data[DELL_LATITUDE_D520], | ||
| 707 | }, | ||
| 708 | { | ||
| 685 | .ident = "Dell Latitude 2", | 709 | .ident = "Dell Latitude 2", |
| 686 | .matches = { | 710 | .matches = { |
| 687 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | 711 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), |
| @@ -703,6 +727,15 @@ static struct dmi_system_id i8k_dmi_table[] __initdata = { | |||
| 703 | }, | 727 | }, |
| 704 | }, | 728 | }, |
| 705 | { | 729 | { |
| 730 | .ident = "Dell Precision 490", | ||
| 731 | .matches = { | ||
| 732 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
| 733 | DMI_MATCH(DMI_PRODUCT_NAME, | ||
| 734 | "Precision WorkStation 490"), | ||
| 735 | }, | ||
| 736 | .driver_data = (void *)&i8k_config_data[DELL_PRECISION_490], | ||
| 737 | }, | ||
| 738 | { | ||
| 706 | .ident = "Dell Precision", | 739 | .ident = "Dell Precision", |
| 707 | .matches = { | 740 | .matches = { |
| 708 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | 741 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), |
| @@ -729,7 +762,7 @@ static struct dmi_system_id i8k_dmi_table[] __initdata = { | |||
| 729 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | 762 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), |
| 730 | DMI_MATCH(DMI_PRODUCT_NAME, "Studio"), | 763 | DMI_MATCH(DMI_PRODUCT_NAME, "Studio"), |
| 731 | }, | 764 | }, |
| 732 | .driver_data = (void *)1, /* fan multiplier override */ | 765 | .driver_data = (void *)&i8k_config_data[DELL_STUDIO], |
| 733 | }, | 766 | }, |
| 734 | { | 767 | { |
| 735 | .ident = "Dell XPS M140", | 768 | .ident = "Dell XPS M140", |
| @@ -737,7 +770,7 @@ static struct dmi_system_id i8k_dmi_table[] __initdata = { | |||
| 737 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | 770 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), |
| 738 | DMI_MATCH(DMI_PRODUCT_NAME, "MXC051"), | 771 | DMI_MATCH(DMI_PRODUCT_NAME, "MXC051"), |
| 739 | }, | 772 | }, |
| 740 | .driver_data = (void *)1, /* fan multiplier override */ | 773 | .driver_data = (void *)&i8k_config_data[DELL_XPS_M140], |
| 741 | }, | 774 | }, |
| 742 | { } | 775 | { } |
| 743 | }; | 776 | }; |
| @@ -777,9 +810,17 @@ static int __init i8k_probe(void) | |||
| 777 | } | 810 | } |
| 778 | 811 | ||
| 779 | i8k_fan_mult = fan_mult; | 812 | i8k_fan_mult = fan_mult; |
| 813 | i8k_fan_max = fan_max ? : I8K_FAN_HIGH; /* Must not be 0 */ | ||
| 780 | id = dmi_first_match(i8k_dmi_table); | 814 | id = dmi_first_match(i8k_dmi_table); |
| 781 | if (id && fan_mult == I8K_FAN_MULT && id->driver_data) | 815 | if (id && id->driver_data) { |
| 782 | i8k_fan_mult = (unsigned long)id->driver_data; | 816 | const struct i8k_config_data *conf = id->driver_data; |
| 817 | |||
| 818 | if (fan_mult == I8K_FAN_MULT && conf->fan_mult) | ||
| 819 | i8k_fan_mult = conf->fan_mult; | ||
| 820 | if (fan_max == I8K_FAN_HIGH && conf->fan_max) | ||
| 821 | i8k_fan_max = conf->fan_max; | ||
| 822 | } | ||
| 823 | i8k_pwm_mult = DIV_ROUND_UP(255, i8k_fan_max); | ||
| 783 | 824 | ||
| 784 | return 0; | 825 | return 0; |
| 785 | } | 826 | } |
diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c index f6345f932e46..9b1a5ac4881d 100644 --- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c | |||
| @@ -661,6 +661,7 @@ static int hwicap_setup(struct device *dev, int id, | |||
| 661 | drvdata->base_address = ioremap(drvdata->mem_start, drvdata->mem_size); | 661 | drvdata->base_address = ioremap(drvdata->mem_start, drvdata->mem_size); |
| 662 | if (!drvdata->base_address) { | 662 | if (!drvdata->base_address) { |
| 663 | dev_err(dev, "ioremap() failed\n"); | 663 | dev_err(dev, "ioremap() failed\n"); |
| 664 | retval = -ENOMEM; | ||
| 664 | goto failed2; | 665 | goto failed2; |
| 665 | } | 666 | } |
| 666 | 667 | ||
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 1eca7b9760e6..8f6afbf9ba54 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig | |||
| @@ -33,6 +33,24 @@ if DMADEVICES | |||
| 33 | 33 | ||
| 34 | comment "DMA Devices" | 34 | comment "DMA Devices" |
| 35 | 35 | ||
| 36 | config INTEL_MIC_X100_DMA | ||
| 37 | tristate "Intel MIC X100 DMA Driver" | ||
| 38 | depends on 64BIT && X86 && INTEL_MIC_BUS | ||
| 39 | select DMA_ENGINE | ||
| 40 | help | ||
| 41 | This enables DMA support for the Intel Many Integrated Core | ||
| 42 | (MIC) family of PCIe form factor coprocessor X100 devices that | ||
| 43 | run a 64 bit Linux OS. This driver will be used by both MIC | ||
| 44 | host and card drivers. | ||
| 45 | |||
| 46 | If you are building host kernel with a MIC device or a card | ||
| 47 | kernel for a MIC device, then say M (recommended) or Y, else | ||
| 48 | say N. If unsure say N. | ||
| 49 | |||
| 50 | More information about the Intel MIC family as well as the Linux | ||
| 51 | OS and tools for MIC to use with this driver are available from | ||
| 52 | <http://software.intel.com/en-us/mic-developer>. | ||
| 53 | |||
| 36 | config INTEL_MID_DMAC | 54 | config INTEL_MID_DMAC |
| 37 | tristate "Intel MID DMA support for Peripheral DMA controllers" | 55 | tristate "Intel MID DMA support for Peripheral DMA controllers" |
| 38 | depends on PCI && X86 | 56 | depends on PCI && X86 |
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index c779e1eb2db2..bd9e7fa928bd 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile | |||
| @@ -47,3 +47,4 @@ obj-$(CONFIG_MOXART_DMA) += moxart-dma.o | |||
| 47 | obj-$(CONFIG_FSL_EDMA) += fsl-edma.o | 47 | obj-$(CONFIG_FSL_EDMA) += fsl-edma.o |
| 48 | obj-$(CONFIG_QCOM_BAM_DMA) += qcom_bam_dma.o | 48 | obj-$(CONFIG_QCOM_BAM_DMA) += qcom_bam_dma.o |
| 49 | obj-y += xilinx/ | 49 | obj-y += xilinx/ |
| 50 | obj-$(CONFIG_INTEL_MIC_X100_DMA) += mic_x100_dma.o | ||
diff --git a/drivers/dma/mic_x100_dma.c b/drivers/dma/mic_x100_dma.c new file mode 100644 index 000000000000..6de2e677be04 --- /dev/null +++ b/drivers/dma/mic_x100_dma.c | |||
| @@ -0,0 +1,774 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2014 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Intel MIC X100 DMA Driver. | ||
| 19 | * | ||
| 20 | * Adapted from IOAT dma driver. | ||
| 21 | */ | ||
| 22 | #include <linux/module.h> | ||
| 23 | #include <linux/io.h> | ||
| 24 | #include <linux/seq_file.h> | ||
| 25 | |||
| 26 | #include "mic_x100_dma.h" | ||
| 27 | |||
| 28 | #define MIC_DMA_MAX_XFER_SIZE_CARD (1 * 1024 * 1024 -\ | ||
| 29 | MIC_DMA_ALIGN_BYTES) | ||
| 30 | #define MIC_DMA_MAX_XFER_SIZE_HOST (1 * 1024 * 1024 >> 1) | ||
| 31 | #define MIC_DMA_DESC_TYPE_SHIFT 60 | ||
| 32 | #define MIC_DMA_MEMCPY_LEN_SHIFT 46 | ||
| 33 | #define MIC_DMA_STAT_INTR_SHIFT 59 | ||
| 34 | |||
| 35 | /* high-water mark for pushing dma descriptors */ | ||
| 36 | static int mic_dma_pending_level = 4; | ||
| 37 | |||
| 38 | /* Status descriptor is used to write a 64 bit value to a memory location */ | ||
| 39 | enum mic_dma_desc_format_type { | ||
| 40 | MIC_DMA_MEMCPY = 1, | ||
| 41 | MIC_DMA_STATUS, | ||
| 42 | }; | ||
| 43 | |||
| 44 | static inline u32 mic_dma_hw_ring_inc(u32 val) | ||
| 45 | { | ||
| 46 | return (val + 1) % MIC_DMA_DESC_RX_SIZE; | ||
| 47 | } | ||
| 48 | |||
| 49 | static inline u32 mic_dma_hw_ring_dec(u32 val) | ||
| 50 | { | ||
| 51 | return val ? val - 1 : MIC_DMA_DESC_RX_SIZE - 1; | ||
| 52 | } | ||
| 53 | |||
| 54 | static inline void mic_dma_hw_ring_inc_head(struct mic_dma_chan *ch) | ||
| 55 | { | ||
| 56 | ch->head = mic_dma_hw_ring_inc(ch->head); | ||
| 57 | } | ||
| 58 | |||
| 59 | /* Prepare a memcpy desc */ | ||
| 60 | static inline void mic_dma_memcpy_desc(struct mic_dma_desc *desc, | ||
| 61 | dma_addr_t src_phys, dma_addr_t dst_phys, u64 size) | ||
| 62 | { | ||
| 63 | u64 qw0, qw1; | ||
| 64 | |||
| 65 | qw0 = src_phys; | ||
| 66 | qw0 |= (size >> MIC_DMA_ALIGN_SHIFT) << MIC_DMA_MEMCPY_LEN_SHIFT; | ||
| 67 | qw1 = MIC_DMA_MEMCPY; | ||
| 68 | qw1 <<= MIC_DMA_DESC_TYPE_SHIFT; | ||
| 69 | qw1 |= dst_phys; | ||
| 70 | desc->qw0 = qw0; | ||
| 71 | desc->qw1 = qw1; | ||
| 72 | } | ||
| 73 | |||
| 74 | /* Prepare a status desc. with @data to be written at @dst_phys */ | ||
| 75 | static inline void mic_dma_prep_status_desc(struct mic_dma_desc *desc, u64 data, | ||
| 76 | dma_addr_t dst_phys, bool generate_intr) | ||
| 77 | { | ||
| 78 | u64 qw0, qw1; | ||
| 79 | |||
| 80 | qw0 = data; | ||
| 81 | qw1 = (u64) MIC_DMA_STATUS << MIC_DMA_DESC_TYPE_SHIFT | dst_phys; | ||
| 82 | if (generate_intr) | ||
| 83 | qw1 |= (1ULL << MIC_DMA_STAT_INTR_SHIFT); | ||
| 84 | desc->qw0 = qw0; | ||
| 85 | desc->qw1 = qw1; | ||
| 86 | } | ||
| 87 | |||
| 88 | static void mic_dma_cleanup(struct mic_dma_chan *ch) | ||
| 89 | { | ||
| 90 | struct dma_async_tx_descriptor *tx; | ||
| 91 | u32 tail; | ||
| 92 | u32 last_tail; | ||
| 93 | |||
| 94 | spin_lock(&ch->cleanup_lock); | ||
| 95 | tail = mic_dma_read_cmp_cnt(ch); | ||
| 96 | /* | ||
| 97 | * This is the barrier pair for smp_wmb() in fn. | ||
| 98 | * mic_dma_tx_submit_unlock. It's required so that we read the | ||
| 99 | * updated cookie value from tx->cookie. | ||
| 100 | */ | ||
| 101 | smp_rmb(); | ||
| 102 | for (last_tail = ch->last_tail; tail != last_tail;) { | ||
| 103 | tx = &ch->tx_array[last_tail]; | ||
| 104 | if (tx->cookie) { | ||
| 105 | dma_cookie_complete(tx); | ||
| 106 | if (tx->callback) { | ||
| 107 | tx->callback(tx->callback_param); | ||
| 108 | tx->callback = NULL; | ||
| 109 | } | ||
| 110 | } | ||
| 111 | last_tail = mic_dma_hw_ring_inc(last_tail); | ||
| 112 | } | ||
| 113 | /* finish all completion callbacks before incrementing tail */ | ||
| 114 | smp_mb(); | ||
| 115 | ch->last_tail = last_tail; | ||
| 116 | spin_unlock(&ch->cleanup_lock); | ||
| 117 | } | ||
| 118 | |||
| 119 | static u32 mic_dma_ring_count(u32 head, u32 tail) | ||
| 120 | { | ||
| 121 | u32 count; | ||
| 122 | |||
| 123 | if (head >= tail) | ||
| 124 | count = (tail - 0) + (MIC_DMA_DESC_RX_SIZE - head); | ||
| 125 | else | ||
| 126 | count = tail - head; | ||
| 127 | return count - 1; | ||
| 128 | } | ||
| 129 | |||
| 130 | /* Returns the num. of free descriptors on success, -ENOMEM on failure */ | ||
| 131 | static int mic_dma_avail_desc_ring_space(struct mic_dma_chan *ch, int required) | ||
| 132 | { | ||
| 133 | struct device *dev = mic_dma_ch_to_device(ch); | ||
| 134 | u32 count; | ||
| 135 | |||
| 136 | count = mic_dma_ring_count(ch->head, ch->last_tail); | ||
| 137 | if (count < required) { | ||
| 138 | mic_dma_cleanup(ch); | ||
| 139 | count = mic_dma_ring_count(ch->head, ch->last_tail); | ||
| 140 | } | ||
| 141 | |||
| 142 | if (count < required) { | ||
| 143 | dev_dbg(dev, "Not enough desc space"); | ||
| 144 | dev_dbg(dev, "%s %d required=%u, avail=%u\n", | ||
| 145 | __func__, __LINE__, required, count); | ||
| 146 | return -ENOMEM; | ||
| 147 | } else { | ||
| 148 | return count; | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | /* Program memcpy descriptors into the descriptor ring and update s/w head ptr*/ | ||
| 153 | static int mic_dma_prog_memcpy_desc(struct mic_dma_chan *ch, dma_addr_t src, | ||
| 154 | dma_addr_t dst, size_t len) | ||
| 155 | { | ||
| 156 | size_t current_transfer_len; | ||
| 157 | size_t max_xfer_size = to_mic_dma_dev(ch)->max_xfer_size; | ||
| 158 | /* 3 is added to make sure we have enough space for status desc */ | ||
| 159 | int num_desc = len / max_xfer_size + 3; | ||
| 160 | int ret; | ||
| 161 | |||
| 162 | if (len % max_xfer_size) | ||
| 163 | num_desc++; | ||
| 164 | |||
| 165 | ret = mic_dma_avail_desc_ring_space(ch, num_desc); | ||
| 166 | if (ret < 0) | ||
| 167 | return ret; | ||
| 168 | do { | ||
| 169 | current_transfer_len = min(len, max_xfer_size); | ||
| 170 | mic_dma_memcpy_desc(&ch->desc_ring[ch->head], | ||
| 171 | src, dst, current_transfer_len); | ||
| 172 | mic_dma_hw_ring_inc_head(ch); | ||
| 173 | len -= current_transfer_len; | ||
| 174 | dst = dst + current_transfer_len; | ||
| 175 | src = src + current_transfer_len; | ||
| 176 | } while (len > 0); | ||
| 177 | return 0; | ||
| 178 | } | ||
| 179 | |||
| 180 | /* It's a h/w quirk and h/w needs 2 status descriptors for every status desc */ | ||
| 181 | static void mic_dma_prog_intr(struct mic_dma_chan *ch) | ||
| 182 | { | ||
| 183 | mic_dma_prep_status_desc(&ch->desc_ring[ch->head], 0, | ||
| 184 | ch->status_dest_micpa, false); | ||
| 185 | mic_dma_hw_ring_inc_head(ch); | ||
| 186 | mic_dma_prep_status_desc(&ch->desc_ring[ch->head], 0, | ||
| 187 | ch->status_dest_micpa, true); | ||
| 188 | mic_dma_hw_ring_inc_head(ch); | ||
| 189 | } | ||
| 190 | |||
| 191 | /* Wrapper function to program memcpy descriptors/status descriptors */ | ||
| 192 | static int mic_dma_do_dma(struct mic_dma_chan *ch, int flags, dma_addr_t src, | ||
| 193 | dma_addr_t dst, size_t len) | ||
| 194 | { | ||
| 195 | if (-ENOMEM == mic_dma_prog_memcpy_desc(ch, src, dst, len)) | ||
| 196 | return -ENOMEM; | ||
| 197 | /* Above mic_dma_prog_memcpy_desc() makes sure we have enough space */ | ||
| 198 | if (flags & DMA_PREP_FENCE) { | ||
| 199 | mic_dma_prep_status_desc(&ch->desc_ring[ch->head], 0, | ||
| 200 | ch->status_dest_micpa, false); | ||
| 201 | mic_dma_hw_ring_inc_head(ch); | ||
| 202 | } | ||
| 203 | |||
| 204 | if (flags & DMA_PREP_INTERRUPT) | ||
| 205 | mic_dma_prog_intr(ch); | ||
| 206 | |||
| 207 | return 0; | ||
| 208 | } | ||
| 209 | |||
| 210 | static inline void mic_dma_issue_pending(struct dma_chan *ch) | ||
| 211 | { | ||
| 212 | struct mic_dma_chan *mic_ch = to_mic_dma_chan(ch); | ||
| 213 | |||
| 214 | spin_lock(&mic_ch->issue_lock); | ||
| 215 | /* | ||
| 216 | * Write to head triggers h/w to act on the descriptors. | ||
| 217 | * On MIC, writing the same head value twice causes | ||
| 218 | * a h/w error. On second write, h/w assumes we filled | ||
| 219 | * the entire ring & overwrote some of the descriptors. | ||
| 220 | */ | ||
| 221 | if (mic_ch->issued == mic_ch->submitted) | ||
| 222 | goto out; | ||
| 223 | mic_ch->issued = mic_ch->submitted; | ||
| 224 | /* | ||
| 225 | * make descriptor updates visible before advancing head, | ||
| 226 | * this is purposefully not smp_wmb() since we are also | ||
| 227 | * publishing the descriptor updates to a dma device | ||
| 228 | */ | ||
| 229 | wmb(); | ||
| 230 | mic_dma_write_reg(mic_ch, MIC_DMA_REG_DHPR, mic_ch->issued); | ||
| 231 | out: | ||
| 232 | spin_unlock(&mic_ch->issue_lock); | ||
| 233 | } | ||
| 234 | |||
| 235 | static inline void mic_dma_update_pending(struct mic_dma_chan *ch) | ||
| 236 | { | ||
| 237 | if (mic_dma_ring_count(ch->issued, ch->submitted) | ||
| 238 | > mic_dma_pending_level) | ||
| 239 | mic_dma_issue_pending(&ch->api_ch); | ||
| 240 | } | ||
| 241 | |||
| 242 | static dma_cookie_t mic_dma_tx_submit_unlock(struct dma_async_tx_descriptor *tx) | ||
| 243 | { | ||
| 244 | struct mic_dma_chan *mic_ch = to_mic_dma_chan(tx->chan); | ||
| 245 | dma_cookie_t cookie; | ||
| 246 | |||
| 247 | dma_cookie_assign(tx); | ||
| 248 | cookie = tx->cookie; | ||
| 249 | /* | ||
| 250 | * We need an smp write barrier here because another CPU might see | ||
| 251 | * an update to submitted and update h/w head even before we | ||
| 252 | * assigned a cookie to this tx. | ||
| 253 | */ | ||
| 254 | smp_wmb(); | ||
| 255 | mic_ch->submitted = mic_ch->head; | ||
| 256 | spin_unlock(&mic_ch->prep_lock); | ||
| 257 | mic_dma_update_pending(mic_ch); | ||
| 258 | return cookie; | ||
| 259 | } | ||
| 260 | |||
| 261 | static inline struct dma_async_tx_descriptor * | ||
| 262 | allocate_tx(struct mic_dma_chan *ch) | ||
| 263 | { | ||
| 264 | u32 idx = mic_dma_hw_ring_dec(ch->head); | ||
| 265 | struct dma_async_tx_descriptor *tx = &ch->tx_array[idx]; | ||
| 266 | |||
| 267 | dma_async_tx_descriptor_init(tx, &ch->api_ch); | ||
| 268 | tx->tx_submit = mic_dma_tx_submit_unlock; | ||
| 269 | return tx; | ||
| 270 | } | ||
| 271 | |||
| 272 | /* | ||
| 273 | * Prepare a memcpy descriptor to be added to the ring. | ||
| 274 | * Note that the temporary descriptor adds an extra overhead of copying the | ||
| 275 | * descriptor to ring. So, we copy directly to the descriptor ring | ||
| 276 | */ | ||
| 277 | static struct dma_async_tx_descriptor * | ||
| 278 | mic_dma_prep_memcpy_lock(struct dma_chan *ch, dma_addr_t dma_dest, | ||
| 279 | dma_addr_t dma_src, size_t len, unsigned long flags) | ||
| 280 | { | ||
| 281 | struct mic_dma_chan *mic_ch = to_mic_dma_chan(ch); | ||
| 282 | struct device *dev = mic_dma_ch_to_device(mic_ch); | ||
| 283 | int result; | ||
| 284 | |||
| 285 | if (!len && !flags) | ||
| 286 | return NULL; | ||
| 287 | |||
| 288 | spin_lock(&mic_ch->prep_lock); | ||
| 289 | result = mic_dma_do_dma(mic_ch, flags, dma_src, dma_dest, len); | ||
| 290 | if (result >= 0) | ||
| 291 | return allocate_tx(mic_ch); | ||
| 292 | dev_err(dev, "Error enqueueing dma, error=%d\n", result); | ||
| 293 | spin_unlock(&mic_ch->prep_lock); | ||
| 294 | return NULL; | ||
| 295 | } | ||
| 296 | |||
| 297 | static struct dma_async_tx_descriptor * | ||
| 298 | mic_dma_prep_interrupt_lock(struct dma_chan *ch, unsigned long flags) | ||
| 299 | { | ||
| 300 | struct mic_dma_chan *mic_ch = to_mic_dma_chan(ch); | ||
| 301 | int ret; | ||
| 302 | |||
| 303 | spin_lock(&mic_ch->prep_lock); | ||
| 304 | ret = mic_dma_do_dma(mic_ch, flags, 0, 0, 0); | ||
| 305 | if (!ret) | ||
| 306 | return allocate_tx(mic_ch); | ||
| 307 | spin_unlock(&mic_ch->prep_lock); | ||
| 308 | return NULL; | ||
| 309 | } | ||
| 310 | |||
| 311 | /* Return the status of the transaction */ | ||
| 312 | static enum dma_status | ||
| 313 | mic_dma_tx_status(struct dma_chan *ch, dma_cookie_t cookie, | ||
| 314 | struct dma_tx_state *txstate) | ||
| 315 | { | ||
| 316 | struct mic_dma_chan *mic_ch = to_mic_dma_chan(ch); | ||
| 317 | |||
| 318 | if (DMA_COMPLETE != dma_cookie_status(ch, cookie, txstate)) | ||
| 319 | mic_dma_cleanup(mic_ch); | ||
| 320 | |||
| 321 | return dma_cookie_status(ch, cookie, txstate); | ||
| 322 | } | ||
| 323 | |||
| 324 | static irqreturn_t mic_dma_thread_fn(int irq, void *data) | ||
| 325 | { | ||
| 326 | mic_dma_cleanup((struct mic_dma_chan *)data); | ||
| 327 | return IRQ_HANDLED; | ||
| 328 | } | ||
| 329 | |||
| 330 | static irqreturn_t mic_dma_intr_handler(int irq, void *data) | ||
| 331 | { | ||
| 332 | struct mic_dma_chan *ch = ((struct mic_dma_chan *)data); | ||
| 333 | |||
| 334 | mic_dma_ack_interrupt(ch); | ||
| 335 | return IRQ_WAKE_THREAD; | ||
| 336 | } | ||
| 337 | |||
| 338 | static int mic_dma_alloc_desc_ring(struct mic_dma_chan *ch) | ||
| 339 | { | ||
| 340 | u64 desc_ring_size = MIC_DMA_DESC_RX_SIZE * sizeof(*ch->desc_ring); | ||
| 341 | struct device *dev = &to_mbus_device(ch)->dev; | ||
| 342 | |||
| 343 | desc_ring_size = ALIGN(desc_ring_size, MIC_DMA_ALIGN_BYTES); | ||
| 344 | ch->desc_ring = kzalloc(desc_ring_size, GFP_KERNEL); | ||
| 345 | |||
| 346 | if (!ch->desc_ring) | ||
| 347 | return -ENOMEM; | ||
| 348 | |||
| 349 | ch->desc_ring_micpa = dma_map_single(dev, ch->desc_ring, | ||
| 350 | desc_ring_size, DMA_BIDIRECTIONAL); | ||
| 351 | if (dma_mapping_error(dev, ch->desc_ring_micpa)) | ||
| 352 | goto map_error; | ||
| 353 | |||
| 354 | ch->tx_array = vzalloc(MIC_DMA_DESC_RX_SIZE * sizeof(*ch->tx_array)); | ||
| 355 | if (!ch->tx_array) | ||
| 356 | goto tx_error; | ||
| 357 | return 0; | ||
| 358 | tx_error: | ||
| 359 | dma_unmap_single(dev, ch->desc_ring_micpa, desc_ring_size, | ||
| 360 | DMA_BIDIRECTIONAL); | ||
| 361 | map_error: | ||
| 362 | kfree(ch->desc_ring); | ||
| 363 | return -ENOMEM; | ||
| 364 | } | ||
| 365 | |||
| 366 | static void mic_dma_free_desc_ring(struct mic_dma_chan *ch) | ||
| 367 | { | ||
| 368 | u64 desc_ring_size = MIC_DMA_DESC_RX_SIZE * sizeof(*ch->desc_ring); | ||
| 369 | |||
| 370 | vfree(ch->tx_array); | ||
| 371 | desc_ring_size = ALIGN(desc_ring_size, MIC_DMA_ALIGN_BYTES); | ||
| 372 | dma_unmap_single(&to_mbus_device(ch)->dev, ch->desc_ring_micpa, | ||
| 373 | desc_ring_size, DMA_BIDIRECTIONAL); | ||
| 374 | kfree(ch->desc_ring); | ||
| 375 | ch->desc_ring = NULL; | ||
| 376 | } | ||
| 377 | |||
| 378 | static void mic_dma_free_status_dest(struct mic_dma_chan *ch) | ||
| 379 | { | ||
| 380 | dma_unmap_single(&to_mbus_device(ch)->dev, ch->status_dest_micpa, | ||
| 381 | L1_CACHE_BYTES, DMA_BIDIRECTIONAL); | ||
| 382 | kfree(ch->status_dest); | ||
| 383 | } | ||
| 384 | |||
| 385 | static int mic_dma_alloc_status_dest(struct mic_dma_chan *ch) | ||
| 386 | { | ||
| 387 | struct device *dev = &to_mbus_device(ch)->dev; | ||
| 388 | |||
| 389 | ch->status_dest = kzalloc(L1_CACHE_BYTES, GFP_KERNEL); | ||
| 390 | if (!ch->status_dest) | ||
| 391 | return -ENOMEM; | ||
| 392 | ch->status_dest_micpa = dma_map_single(dev, ch->status_dest, | ||
| 393 | L1_CACHE_BYTES, DMA_BIDIRECTIONAL); | ||
| 394 | if (dma_mapping_error(dev, ch->status_dest_micpa)) { | ||
| 395 | kfree(ch->status_dest); | ||
| 396 | ch->status_dest = NULL; | ||
| 397 | return -ENOMEM; | ||
| 398 | } | ||
| 399 | return 0; | ||
| 400 | } | ||
| 401 | |||
| 402 | static int mic_dma_check_chan(struct mic_dma_chan *ch) | ||
| 403 | { | ||
| 404 | if (mic_dma_read_reg(ch, MIC_DMA_REG_DCHERR) || | ||
| 405 | mic_dma_read_reg(ch, MIC_DMA_REG_DSTAT) & MIC_DMA_CHAN_QUIESCE) { | ||
| 406 | mic_dma_disable_chan(ch); | ||
| 407 | mic_dma_chan_mask_intr(ch); | ||
| 408 | dev_err(mic_dma_ch_to_device(ch), | ||
| 409 | "%s %d error setting up mic dma chan %d\n", | ||
| 410 | __func__, __LINE__, ch->ch_num); | ||
| 411 | return -EBUSY; | ||
| 412 | } | ||
| 413 | return 0; | ||
| 414 | } | ||
| 415 | |||
| 416 | static int mic_dma_chan_setup(struct mic_dma_chan *ch) | ||
| 417 | { | ||
| 418 | if (MIC_DMA_CHAN_MIC == ch->owner) | ||
| 419 | mic_dma_chan_set_owner(ch); | ||
| 420 | mic_dma_disable_chan(ch); | ||
| 421 | mic_dma_chan_mask_intr(ch); | ||
| 422 | mic_dma_write_reg(ch, MIC_DMA_REG_DCHERRMSK, 0); | ||
| 423 | mic_dma_chan_set_desc_ring(ch); | ||
| 424 | ch->last_tail = mic_dma_read_reg(ch, MIC_DMA_REG_DTPR); | ||
| 425 | ch->head = ch->last_tail; | ||
| 426 | ch->issued = 0; | ||
| 427 | mic_dma_chan_unmask_intr(ch); | ||
| 428 | mic_dma_enable_chan(ch); | ||
| 429 | return mic_dma_check_chan(ch); | ||
| 430 | } | ||
| 431 | |||
| 432 | static void mic_dma_chan_destroy(struct mic_dma_chan *ch) | ||
| 433 | { | ||
| 434 | mic_dma_disable_chan(ch); | ||
| 435 | mic_dma_chan_mask_intr(ch); | ||
| 436 | } | ||
| 437 | |||
| 438 | static void mic_dma_unregister_dma_device(struct mic_dma_device *mic_dma_dev) | ||
| 439 | { | ||
| 440 | dma_async_device_unregister(&mic_dma_dev->dma_dev); | ||
| 441 | } | ||
| 442 | |||
| 443 | static int mic_dma_setup_irq(struct mic_dma_chan *ch) | ||
| 444 | { | ||
| 445 | ch->cookie = | ||
| 446 | to_mbus_hw_ops(ch)->request_threaded_irq(to_mbus_device(ch), | ||
| 447 | mic_dma_intr_handler, mic_dma_thread_fn, | ||
| 448 | "mic dma_channel", ch, ch->ch_num); | ||
| 449 | if (IS_ERR(ch->cookie)) | ||
| 450 | return IS_ERR(ch->cookie); | ||
| 451 | return 0; | ||
| 452 | } | ||
| 453 | |||
| 454 | static inline void mic_dma_free_irq(struct mic_dma_chan *ch) | ||
| 455 | { | ||
| 456 | to_mbus_hw_ops(ch)->free_irq(to_mbus_device(ch), ch->cookie, ch); | ||
| 457 | } | ||
| 458 | |||
| 459 | static int mic_dma_chan_init(struct mic_dma_chan *ch) | ||
| 460 | { | ||
| 461 | int ret = mic_dma_alloc_desc_ring(ch); | ||
| 462 | |||
| 463 | if (ret) | ||
| 464 | goto ring_error; | ||
| 465 | ret = mic_dma_alloc_status_dest(ch); | ||
| 466 | if (ret) | ||
| 467 | goto status_error; | ||
| 468 | ret = mic_dma_chan_setup(ch); | ||
| 469 | if (ret) | ||
| 470 | goto chan_error; | ||
| 471 | return ret; | ||
| 472 | chan_error: | ||
| 473 | mic_dma_free_status_dest(ch); | ||
| 474 | status_error: | ||
| 475 | mic_dma_free_desc_ring(ch); | ||
| 476 | ring_error: | ||
| 477 | return ret; | ||
| 478 | } | ||
| 479 | |||
| 480 | static int mic_dma_drain_chan(struct mic_dma_chan *ch) | ||
| 481 | { | ||
| 482 | struct dma_async_tx_descriptor *tx; | ||
| 483 | int err = 0; | ||
| 484 | dma_cookie_t cookie; | ||
| 485 | |||
| 486 | tx = mic_dma_prep_memcpy_lock(&ch->api_ch, 0, 0, 0, DMA_PREP_FENCE); | ||
| 487 | if (!tx) { | ||
| 488 | err = -ENOMEM; | ||
| 489 | goto error; | ||
| 490 | } | ||
| 491 | |||
| 492 | cookie = tx->tx_submit(tx); | ||
| 493 | if (dma_submit_error(cookie)) | ||
| 494 | err = -ENOMEM; | ||
| 495 | else | ||
| 496 | err = dma_sync_wait(&ch->api_ch, cookie); | ||
| 497 | if (err) { | ||
| 498 | dev_err(mic_dma_ch_to_device(ch), "%s %d TO chan 0x%x\n", | ||
| 499 | __func__, __LINE__, ch->ch_num); | ||
| 500 | err = -EIO; | ||
| 501 | } | ||
| 502 | error: | ||
| 503 | mic_dma_cleanup(ch); | ||
| 504 | return err; | ||
| 505 | } | ||
| 506 | |||
| 507 | static inline void mic_dma_chan_uninit(struct mic_dma_chan *ch) | ||
| 508 | { | ||
| 509 | mic_dma_chan_destroy(ch); | ||
| 510 | mic_dma_cleanup(ch); | ||
| 511 | mic_dma_free_status_dest(ch); | ||
| 512 | mic_dma_free_desc_ring(ch); | ||
| 513 | } | ||
| 514 | |||
| 515 | static int mic_dma_init(struct mic_dma_device *mic_dma_dev, | ||
| 516 | enum mic_dma_chan_owner owner) | ||
| 517 | { | ||
| 518 | int i, first_chan = mic_dma_dev->start_ch; | ||
| 519 | struct mic_dma_chan *ch; | ||
| 520 | int ret; | ||
| 521 | |||
| 522 | for (i = first_chan; i < first_chan + MIC_DMA_NUM_CHAN; i++) { | ||
| 523 | unsigned long data; | ||
| 524 | ch = &mic_dma_dev->mic_ch[i]; | ||
| 525 | data = (unsigned long)ch; | ||
| 526 | ch->ch_num = i; | ||
| 527 | ch->owner = owner; | ||
| 528 | spin_lock_init(&ch->cleanup_lock); | ||
| 529 | spin_lock_init(&ch->prep_lock); | ||
| 530 | spin_lock_init(&ch->issue_lock); | ||
| 531 | ret = mic_dma_setup_irq(ch); | ||
| 532 | if (ret) | ||
| 533 | goto error; | ||
| 534 | } | ||
| 535 | return 0; | ||
| 536 | error: | ||
| 537 | for (i = i - 1; i >= first_chan; i--) | ||
| 538 | mic_dma_free_irq(ch); | ||
| 539 | return ret; | ||
| 540 | } | ||
| 541 | |||
| 542 | static void mic_dma_uninit(struct mic_dma_device *mic_dma_dev) | ||
| 543 | { | ||
| 544 | int i, first_chan = mic_dma_dev->start_ch; | ||
| 545 | struct mic_dma_chan *ch; | ||
| 546 | |||
| 547 | for (i = first_chan; i < first_chan + MIC_DMA_NUM_CHAN; i++) { | ||
| 548 | ch = &mic_dma_dev->mic_ch[i]; | ||
| 549 | mic_dma_free_irq(ch); | ||
| 550 | } | ||
| 551 | } | ||
| 552 | |||
| 553 | static int mic_dma_alloc_chan_resources(struct dma_chan *ch) | ||
| 554 | { | ||
| 555 | int ret = mic_dma_chan_init(to_mic_dma_chan(ch)); | ||
| 556 | if (ret) | ||
| 557 | return ret; | ||
| 558 | return MIC_DMA_DESC_RX_SIZE; | ||
| 559 | } | ||
| 560 | |||
| 561 | static void mic_dma_free_chan_resources(struct dma_chan *ch) | ||
| 562 | { | ||
| 563 | struct mic_dma_chan *mic_ch = to_mic_dma_chan(ch); | ||
| 564 | mic_dma_drain_chan(mic_ch); | ||
| 565 | mic_dma_chan_uninit(mic_ch); | ||
| 566 | } | ||
| 567 | |||
| 568 | /* Set the fn. handlers and register the dma device with dma api */ | ||
| 569 | static int mic_dma_register_dma_device(struct mic_dma_device *mic_dma_dev, | ||
| 570 | enum mic_dma_chan_owner owner) | ||
| 571 | { | ||
| 572 | int i, first_chan = mic_dma_dev->start_ch; | ||
| 573 | |||
| 574 | dma_cap_zero(mic_dma_dev->dma_dev.cap_mask); | ||
| 575 | /* | ||
| 576 | * This dma engine is not capable of host memory to host memory | ||
| 577 | * transfers | ||
| 578 | */ | ||
| 579 | dma_cap_set(DMA_MEMCPY, mic_dma_dev->dma_dev.cap_mask); | ||
| 580 | |||
| 581 | if (MIC_DMA_CHAN_HOST == owner) | ||
| 582 | dma_cap_set(DMA_PRIVATE, mic_dma_dev->dma_dev.cap_mask); | ||
| 583 | mic_dma_dev->dma_dev.device_alloc_chan_resources = | ||
| 584 | mic_dma_alloc_chan_resources; | ||
| 585 | mic_dma_dev->dma_dev.device_free_chan_resources = | ||
| 586 | mic_dma_free_chan_resources; | ||
| 587 | mic_dma_dev->dma_dev.device_tx_status = mic_dma_tx_status; | ||
| 588 | mic_dma_dev->dma_dev.device_prep_dma_memcpy = mic_dma_prep_memcpy_lock; | ||
| 589 | mic_dma_dev->dma_dev.device_prep_dma_interrupt = | ||
| 590 | mic_dma_prep_interrupt_lock; | ||
| 591 | mic_dma_dev->dma_dev.device_issue_pending = mic_dma_issue_pending; | ||
| 592 | mic_dma_dev->dma_dev.copy_align = MIC_DMA_ALIGN_SHIFT; | ||
| 593 | INIT_LIST_HEAD(&mic_dma_dev->dma_dev.channels); | ||
| 594 | for (i = first_chan; i < first_chan + MIC_DMA_NUM_CHAN; i++) { | ||
| 595 | mic_dma_dev->mic_ch[i].api_ch.device = &mic_dma_dev->dma_dev; | ||
| 596 | dma_cookie_init(&mic_dma_dev->mic_ch[i].api_ch); | ||
| 597 | list_add_tail(&mic_dma_dev->mic_ch[i].api_ch.device_node, | ||
| 598 | &mic_dma_dev->dma_dev.channels); | ||
| 599 | } | ||
| 600 | return dma_async_device_register(&mic_dma_dev->dma_dev); | ||
| 601 | } | ||
| 602 | |||
| 603 | /* | ||
| 604 | * Initializes dma channels and registers the dma device with the | ||
| 605 | * dma engine api. | ||
| 606 | */ | ||
| 607 | static struct mic_dma_device *mic_dma_dev_reg(struct mbus_device *mbdev, | ||
| 608 | enum mic_dma_chan_owner owner) | ||
| 609 | { | ||
| 610 | struct mic_dma_device *mic_dma_dev; | ||
| 611 | int ret; | ||
| 612 | struct device *dev = &mbdev->dev; | ||
| 613 | |||
| 614 | mic_dma_dev = kzalloc(sizeof(*mic_dma_dev), GFP_KERNEL); | ||
| 615 | if (!mic_dma_dev) { | ||
| 616 | ret = -ENOMEM; | ||
| 617 | goto alloc_error; | ||
| 618 | } | ||
| 619 | mic_dma_dev->mbdev = mbdev; | ||
| 620 | mic_dma_dev->dma_dev.dev = dev; | ||
| 621 | mic_dma_dev->mmio = mbdev->mmio_va; | ||
| 622 | if (MIC_DMA_CHAN_HOST == owner) { | ||
| 623 | mic_dma_dev->start_ch = 0; | ||
| 624 | mic_dma_dev->max_xfer_size = MIC_DMA_MAX_XFER_SIZE_HOST; | ||
| 625 | } else { | ||
| 626 | mic_dma_dev->start_ch = 4; | ||
| 627 | mic_dma_dev->max_xfer_size = MIC_DMA_MAX_XFER_SIZE_CARD; | ||
| 628 | } | ||
| 629 | ret = mic_dma_init(mic_dma_dev, owner); | ||
| 630 | if (ret) | ||
| 631 | goto init_error; | ||
| 632 | ret = mic_dma_register_dma_device(mic_dma_dev, owner); | ||
| 633 | if (ret) | ||
| 634 | goto reg_error; | ||
| 635 | return mic_dma_dev; | ||
| 636 | reg_error: | ||
| 637 | mic_dma_uninit(mic_dma_dev); | ||
| 638 | init_error: | ||
| 639 | kfree(mic_dma_dev); | ||
| 640 | mic_dma_dev = NULL; | ||
| 641 | alloc_error: | ||
| 642 | dev_err(dev, "Error at %s %d ret=%d\n", __func__, __LINE__, ret); | ||
| 643 | return mic_dma_dev; | ||
| 644 | } | ||
| 645 | |||
| 646 | static void mic_dma_dev_unreg(struct mic_dma_device *mic_dma_dev) | ||
| 647 | { | ||
| 648 | mic_dma_unregister_dma_device(mic_dma_dev); | ||
| 649 | mic_dma_uninit(mic_dma_dev); | ||
| 650 | kfree(mic_dma_dev); | ||
| 651 | } | ||
| 652 | |||
| 653 | /* DEBUGFS CODE */ | ||
| 654 | static int mic_dma_reg_seq_show(struct seq_file *s, void *pos) | ||
| 655 | { | ||
| 656 | struct mic_dma_device *mic_dma_dev = s->private; | ||
| 657 | int i, chan_num, first_chan = mic_dma_dev->start_ch; | ||
| 658 | struct mic_dma_chan *ch; | ||
| 659 | |||
| 660 | seq_printf(s, "SBOX_DCR: %#x\n", | ||
| 661 | mic_dma_mmio_read(&mic_dma_dev->mic_ch[first_chan], | ||
| 662 | MIC_DMA_SBOX_BASE + MIC_DMA_SBOX_DCR)); | ||
| 663 | seq_puts(s, "DMA Channel Registers\n"); | ||
| 664 | seq_printf(s, "%-10s| %-10s %-10s %-10s %-10s %-10s", | ||
| 665 | "Channel", "DCAR", "DTPR", "DHPR", "DRAR_HI", "DRAR_LO"); | ||
| 666 | seq_printf(s, " %-11s %-14s %-10s\n", "DCHERR", "DCHERRMSK", "DSTAT"); | ||
| 667 | for (i = first_chan; i < first_chan + MIC_DMA_NUM_CHAN; i++) { | ||
| 668 | ch = &mic_dma_dev->mic_ch[i]; | ||
| 669 | chan_num = ch->ch_num; | ||
| 670 | seq_printf(s, "%-10i| %-#10x %-#10x %-#10x %-#10x", | ||
| 671 | chan_num, | ||
| 672 | mic_dma_read_reg(ch, MIC_DMA_REG_DCAR), | ||
| 673 | mic_dma_read_reg(ch, MIC_DMA_REG_DTPR), | ||
| 674 | mic_dma_read_reg(ch, MIC_DMA_REG_DHPR), | ||
| 675 | mic_dma_read_reg(ch, MIC_DMA_REG_DRAR_HI)); | ||
| 676 | seq_printf(s, " %-#10x %-#10x %-#14x %-#10x\n", | ||
| 677 | mic_dma_read_reg(ch, MIC_DMA_REG_DRAR_LO), | ||
| 678 | mic_dma_read_reg(ch, MIC_DMA_REG_DCHERR), | ||
| 679 | mic_dma_read_reg(ch, MIC_DMA_REG_DCHERRMSK), | ||
| 680 | mic_dma_read_reg(ch, MIC_DMA_REG_DSTAT)); | ||
| 681 | } | ||
| 682 | return 0; | ||
| 683 | } | ||
| 684 | |||
| 685 | static int mic_dma_reg_debug_open(struct inode *inode, struct file *file) | ||
| 686 | { | ||
| 687 | return single_open(file, mic_dma_reg_seq_show, inode->i_private); | ||
| 688 | } | ||
| 689 | |||
| 690 | static int mic_dma_reg_debug_release(struct inode *inode, struct file *file) | ||
| 691 | { | ||
| 692 | return single_release(inode, file); | ||
| 693 | } | ||
| 694 | |||
| 695 | static const struct file_operations mic_dma_reg_ops = { | ||
| 696 | .owner = THIS_MODULE, | ||
| 697 | .open = mic_dma_reg_debug_open, | ||
| 698 | .read = seq_read, | ||
| 699 | .llseek = seq_lseek, | ||
| 700 | .release = mic_dma_reg_debug_release | ||
| 701 | }; | ||
| 702 | |||
| 703 | /* Debugfs parent dir */ | ||
| 704 | static struct dentry *mic_dma_dbg; | ||
| 705 | |||
| 706 | static int mic_dma_driver_probe(struct mbus_device *mbdev) | ||
| 707 | { | ||
| 708 | struct mic_dma_device *mic_dma_dev; | ||
| 709 | enum mic_dma_chan_owner owner; | ||
| 710 | |||
| 711 | if (MBUS_DEV_DMA_MIC == mbdev->id.device) | ||
| 712 | owner = MIC_DMA_CHAN_MIC; | ||
| 713 | else | ||
| 714 | owner = MIC_DMA_CHAN_HOST; | ||
| 715 | |||
| 716 | mic_dma_dev = mic_dma_dev_reg(mbdev, owner); | ||
| 717 | dev_set_drvdata(&mbdev->dev, mic_dma_dev); | ||
| 718 | |||
| 719 | if (mic_dma_dbg) { | ||
| 720 | mic_dma_dev->dbg_dir = debugfs_create_dir(dev_name(&mbdev->dev), | ||
| 721 | mic_dma_dbg); | ||
| 722 | if (mic_dma_dev->dbg_dir) | ||
| 723 | debugfs_create_file("mic_dma_reg", 0444, | ||
| 724 | mic_dma_dev->dbg_dir, mic_dma_dev, | ||
| 725 | &mic_dma_reg_ops); | ||
| 726 | } | ||
| 727 | return 0; | ||
| 728 | } | ||
| 729 | |||
| 730 | static void mic_dma_driver_remove(struct mbus_device *mbdev) | ||
| 731 | { | ||
| 732 | struct mic_dma_device *mic_dma_dev; | ||
| 733 | |||
| 734 | mic_dma_dev = dev_get_drvdata(&mbdev->dev); | ||
| 735 | debugfs_remove_recursive(mic_dma_dev->dbg_dir); | ||
| 736 | mic_dma_dev_unreg(mic_dma_dev); | ||
| 737 | } | ||
| 738 | |||
| 739 | static struct mbus_device_id id_table[] = { | ||
| 740 | {MBUS_DEV_DMA_MIC, MBUS_DEV_ANY_ID}, | ||
| 741 | {MBUS_DEV_DMA_HOST, MBUS_DEV_ANY_ID}, | ||
| 742 | {0}, | ||
| 743 | }; | ||
| 744 | |||
| 745 | static struct mbus_driver mic_dma_driver = { | ||
| 746 | .driver.name = KBUILD_MODNAME, | ||
| 747 | .driver.owner = THIS_MODULE, | ||
| 748 | .id_table = id_table, | ||
| 749 | .probe = mic_dma_driver_probe, | ||
| 750 | .remove = mic_dma_driver_remove, | ||
| 751 | }; | ||
| 752 | |||
| 753 | static int __init mic_x100_dma_init(void) | ||
| 754 | { | ||
| 755 | int rc = mbus_register_driver(&mic_dma_driver); | ||
| 756 | if (rc) | ||
| 757 | return rc; | ||
| 758 | mic_dma_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL); | ||
| 759 | return 0; | ||
| 760 | } | ||
| 761 | |||
| 762 | static void __exit mic_x100_dma_exit(void) | ||
| 763 | { | ||
| 764 | debugfs_remove_recursive(mic_dma_dbg); | ||
| 765 | mbus_unregister_driver(&mic_dma_driver); | ||
| 766 | } | ||
| 767 | |||
| 768 | module_init(mic_x100_dma_init); | ||
| 769 | module_exit(mic_x100_dma_exit); | ||
| 770 | |||
| 771 | MODULE_DEVICE_TABLE(mbus, id_table); | ||
| 772 | MODULE_AUTHOR("Intel Corporation"); | ||
| 773 | MODULE_DESCRIPTION("Intel(R) MIC X100 DMA Driver"); | ||
| 774 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/dma/mic_x100_dma.h b/drivers/dma/mic_x100_dma.h new file mode 100644 index 000000000000..f663b0bdd11d --- /dev/null +++ b/drivers/dma/mic_x100_dma.h | |||
| @@ -0,0 +1,286 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2014 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Intel MIC X100 DMA Driver. | ||
| 19 | * | ||
| 20 | * Adapted from IOAT dma driver. | ||
| 21 | */ | ||
| 22 | #ifndef _MIC_X100_DMA_H_ | ||
| 23 | #define _MIC_X100_DMA_H_ | ||
| 24 | |||
| 25 | #include <linux/kernel.h> | ||
| 26 | #include <linux/delay.h> | ||
| 27 | #include <linux/sched.h> | ||
| 28 | #include <linux/debugfs.h> | ||
| 29 | #include <linux/slab.h> | ||
| 30 | #include <linux/interrupt.h> | ||
| 31 | #include <linux/mic_bus.h> | ||
| 32 | |||
| 33 | #include "dmaengine.h" | ||
| 34 | |||
| 35 | /* | ||
| 36 | * MIC has a total of 8 dma channels. | ||
| 37 | * Four channels are assigned for host SW use & the remaining for MIC SW. | ||
| 38 | * MIC DMA transfer size & addresses need to be 64 byte aligned. | ||
| 39 | */ | ||
| 40 | #define MIC_DMA_MAX_NUM_CHAN 8 | ||
| 41 | #define MIC_DMA_NUM_CHAN 4 | ||
| 42 | #define MIC_DMA_ALIGN_SHIFT 6 | ||
| 43 | #define MIC_DMA_ALIGN_BYTES (1 << MIC_DMA_ALIGN_SHIFT) | ||
| 44 | #define MIC_DMA_DESC_RX_SIZE (128 * 1024 - 4) | ||
| 45 | |||
| 46 | /* | ||
| 47 | * Register descriptions | ||
| 48 | * All the registers are 32 bit registers. | ||
| 49 | * DCR is a global register and all others are per-channel. | ||
| 50 | * DCR - bits 0, 2, 4, 6, 8, 10, 12, 14 - enable bits for channels 0 to 7 | ||
| 51 | * bits 1, 3, 5, 7, 9, 11, 13, 15 - owner bits for channels 0 to 7 | ||
| 52 | * DCAR - bit 24 & 25 interrupt masks for mic owned & host owned channels | ||
| 53 | * DHPR - head of the descriptor ring updated by s/w | ||
| 54 | * DTPR - tail of the descriptor ring updated by h/w | ||
| 55 | * DRAR_LO - lower 32 bits of descriptor ring's mic address | ||
| 56 | * DRAR_HI - 3:0 - remaining 4 bits of descriptor ring's mic address | ||
| 57 | * 20:4 descriptor ring size | ||
| 58 | * 25:21 mic smpt entry number | ||
| 59 | * DSTAT - 16:0 h/w completion count; 31:28 dma engine status | ||
| 60 | * DCHERR - this register is non-zero on error | ||
| 61 | * DCHERRMSK - interrupt mask register | ||
| 62 | */ | ||
| 63 | #define MIC_DMA_HW_CMP_CNT_MASK 0x1ffff | ||
| 64 | #define MIC_DMA_CHAN_QUIESCE 0x20000000 | ||
| 65 | #define MIC_DMA_SBOX_BASE 0x00010000 | ||
| 66 | #define MIC_DMA_SBOX_DCR 0x0000A280 | ||
| 67 | #define MIC_DMA_SBOX_CH_BASE 0x0001A000 | ||
| 68 | #define MIC_DMA_SBOX_CHAN_OFF 0x40 | ||
| 69 | #define MIC_DMA_SBOX_DCAR_IM0 (0x1 << 24) | ||
| 70 | #define MIC_DMA_SBOX_DCAR_IM1 (0x1 << 25) | ||
| 71 | #define MIC_DMA_SBOX_DRARHI_SYS_MASK (0x1 << 26) | ||
| 72 | #define MIC_DMA_REG_DCAR 0 | ||
| 73 | #define MIC_DMA_REG_DHPR 4 | ||
| 74 | #define MIC_DMA_REG_DTPR 8 | ||
| 75 | #define MIC_DMA_REG_DRAR_LO 20 | ||
| 76 | #define MIC_DMA_REG_DRAR_HI 24 | ||
| 77 | #define MIC_DMA_REG_DSTAT 32 | ||
| 78 | #define MIC_DMA_REG_DCHERR 44 | ||
| 79 | #define MIC_DMA_REG_DCHERRMSK 48 | ||
| 80 | |||
| 81 | /* HW dma desc */ | ||
| 82 | struct mic_dma_desc { | ||
| 83 | u64 qw0; | ||
| 84 | u64 qw1; | ||
| 85 | }; | ||
| 86 | |||
| 87 | enum mic_dma_chan_owner { | ||
| 88 | MIC_DMA_CHAN_MIC = 0, | ||
| 89 | MIC_DMA_CHAN_HOST | ||
| 90 | }; | ||
| 91 | |||
| 92 | /* | ||
| 93 | * mic_dma_chan - channel specific information | ||
| 94 | * @ch_num: channel number | ||
| 95 | * @owner: owner of this channel | ||
| 96 | * @last_tail: cached value of descriptor ring tail | ||
| 97 | * @head: index of next descriptor in desc_ring | ||
| 98 | * @issued: hardware notification point | ||
| 99 | * @submitted: index that will be used to submit descriptors to h/w | ||
| 100 | * @api_ch: dma engine api channel | ||
| 101 | * @desc_ring: dma descriptor ring | ||
| 102 | * @desc_ring_micpa: mic physical address of desc_ring | ||
| 103 | * @status_dest: destination for status (fence) descriptor | ||
| 104 | * @status_dest_micpa: mic address for status_dest, | ||
| 105 | * DMA controller uses this address | ||
| 106 | * @tx_array: array of async_tx | ||
| 107 | * @cleanup_lock: lock held when processing completed tx | ||
| 108 | * @prep_lock: lock held in prep_memcpy & released in tx_submit | ||
| 109 | * @issue_lock: lock used to synchronize writes to head | ||
| 110 | * @cookie: mic_irq cookie used with mic irq request | ||
| 111 | */ | ||
| 112 | struct mic_dma_chan { | ||
| 113 | int ch_num; | ||
| 114 | enum mic_dma_chan_owner owner; | ||
| 115 | u32 last_tail; | ||
| 116 | u32 head; | ||
| 117 | u32 issued; | ||
| 118 | u32 submitted; | ||
| 119 | struct dma_chan api_ch; | ||
| 120 | struct mic_dma_desc *desc_ring; | ||
| 121 | dma_addr_t desc_ring_micpa; | ||
| 122 | u64 *status_dest; | ||
| 123 | dma_addr_t status_dest_micpa; | ||
| 124 | struct dma_async_tx_descriptor *tx_array; | ||
| 125 | spinlock_t cleanup_lock; | ||
| 126 | spinlock_t prep_lock; | ||
| 127 | spinlock_t issue_lock; | ||
| 128 | struct mic_irq *cookie; | ||
| 129 | }; | ||
| 130 | |||
| 131 | /* | ||
| 132 | * struct mic_dma_device - per mic device | ||
| 133 | * @mic_ch: dma channels | ||
| 134 | * @dma_dev: underlying dma device | ||
| 135 | * @mbdev: mic bus dma device | ||
| 136 | * @mmio: virtual address of the mmio space | ||
| 137 | * @dbg_dir: debugfs directory | ||
| 138 | * @start_ch: first channel number that can be used | ||
| 139 | * @max_xfer_size: maximum transfer size per dma descriptor | ||
| 140 | */ | ||
| 141 | struct mic_dma_device { | ||
| 142 | struct mic_dma_chan mic_ch[MIC_DMA_MAX_NUM_CHAN]; | ||
| 143 | struct dma_device dma_dev; | ||
| 144 | struct mbus_device *mbdev; | ||
| 145 | void __iomem *mmio; | ||
| 146 | struct dentry *dbg_dir; | ||
| 147 | int start_ch; | ||
| 148 | size_t max_xfer_size; | ||
| 149 | }; | ||
| 150 | |||
| 151 | static inline struct mic_dma_chan *to_mic_dma_chan(struct dma_chan *ch) | ||
| 152 | { | ||
| 153 | return container_of(ch, struct mic_dma_chan, api_ch); | ||
| 154 | } | ||
| 155 | |||
| 156 | static inline struct mic_dma_device *to_mic_dma_dev(struct mic_dma_chan *ch) | ||
| 157 | { | ||
| 158 | return | ||
| 159 | container_of((const typeof(((struct mic_dma_device *)0)->mic_ch)*) | ||
| 160 | (ch - ch->ch_num), struct mic_dma_device, mic_ch); | ||
| 161 | } | ||
| 162 | |||
| 163 | static inline struct mbus_device *to_mbus_device(struct mic_dma_chan *ch) | ||
| 164 | { | ||
| 165 | return to_mic_dma_dev(ch)->mbdev; | ||
| 166 | } | ||
| 167 | |||
| 168 | static inline struct mbus_hw_ops *to_mbus_hw_ops(struct mic_dma_chan *ch) | ||
| 169 | { | ||
| 170 | return to_mbus_device(ch)->hw_ops; | ||
| 171 | } | ||
| 172 | |||
| 173 | static inline struct device *mic_dma_ch_to_device(struct mic_dma_chan *ch) | ||
| 174 | { | ||
| 175 | return to_mic_dma_dev(ch)->dma_dev.dev; | ||
| 176 | } | ||
| 177 | |||
| 178 | static inline void __iomem *mic_dma_chan_to_mmio(struct mic_dma_chan *ch) | ||
| 179 | { | ||
| 180 | return to_mic_dma_dev(ch)->mmio; | ||
| 181 | } | ||
| 182 | |||
| 183 | static inline u32 mic_dma_read_reg(struct mic_dma_chan *ch, u32 reg) | ||
| 184 | { | ||
| 185 | return ioread32(mic_dma_chan_to_mmio(ch) + MIC_DMA_SBOX_CH_BASE + | ||
| 186 | ch->ch_num * MIC_DMA_SBOX_CHAN_OFF + reg); | ||
| 187 | } | ||
| 188 | |||
| 189 | static inline void mic_dma_write_reg(struct mic_dma_chan *ch, u32 reg, u32 val) | ||
| 190 | { | ||
| 191 | iowrite32(val, mic_dma_chan_to_mmio(ch) + MIC_DMA_SBOX_CH_BASE + | ||
| 192 | ch->ch_num * MIC_DMA_SBOX_CHAN_OFF + reg); | ||
| 193 | } | ||
| 194 | |||
| 195 | static inline u32 mic_dma_mmio_read(struct mic_dma_chan *ch, u32 offset) | ||
| 196 | { | ||
| 197 | return ioread32(mic_dma_chan_to_mmio(ch) + offset); | ||
| 198 | } | ||
| 199 | |||
| 200 | static inline void mic_dma_mmio_write(struct mic_dma_chan *ch, u32 val, | ||
| 201 | u32 offset) | ||
| 202 | { | ||
| 203 | iowrite32(val, mic_dma_chan_to_mmio(ch) + offset); | ||
| 204 | } | ||
| 205 | |||
| 206 | static inline u32 mic_dma_read_cmp_cnt(struct mic_dma_chan *ch) | ||
| 207 | { | ||
| 208 | return mic_dma_read_reg(ch, MIC_DMA_REG_DSTAT) & | ||
| 209 | MIC_DMA_HW_CMP_CNT_MASK; | ||
| 210 | } | ||
| 211 | |||
| 212 | static inline void mic_dma_chan_set_owner(struct mic_dma_chan *ch) | ||
| 213 | { | ||
| 214 | u32 dcr = mic_dma_mmio_read(ch, MIC_DMA_SBOX_BASE + MIC_DMA_SBOX_DCR); | ||
| 215 | u32 chan_num = ch->ch_num; | ||
| 216 | |||
| 217 | dcr = (dcr & ~(0x1 << (chan_num * 2))) | (ch->owner << (chan_num * 2)); | ||
| 218 | mic_dma_mmio_write(ch, dcr, MIC_DMA_SBOX_BASE + MIC_DMA_SBOX_DCR); | ||
| 219 | } | ||
| 220 | |||
| 221 | static inline void mic_dma_enable_chan(struct mic_dma_chan *ch) | ||
| 222 | { | ||
| 223 | u32 dcr = mic_dma_mmio_read(ch, MIC_DMA_SBOX_BASE + MIC_DMA_SBOX_DCR); | ||
| 224 | |||
| 225 | dcr |= 2 << (ch->ch_num << 1); | ||
| 226 | mic_dma_mmio_write(ch, dcr, MIC_DMA_SBOX_BASE + MIC_DMA_SBOX_DCR); | ||
| 227 | } | ||
| 228 | |||
| 229 | static inline void mic_dma_disable_chan(struct mic_dma_chan *ch) | ||
| 230 | { | ||
| 231 | u32 dcr = mic_dma_mmio_read(ch, MIC_DMA_SBOX_BASE + MIC_DMA_SBOX_DCR); | ||
| 232 | |||
| 233 | dcr &= ~(2 << (ch->ch_num << 1)); | ||
| 234 | mic_dma_mmio_write(ch, dcr, MIC_DMA_SBOX_BASE + MIC_DMA_SBOX_DCR); | ||
| 235 | } | ||
| 236 | |||
| 237 | static void mic_dma_chan_set_desc_ring(struct mic_dma_chan *ch) | ||
| 238 | { | ||
| 239 | u32 drar_hi; | ||
| 240 | dma_addr_t desc_ring_micpa = ch->desc_ring_micpa; | ||
| 241 | |||
| 242 | drar_hi = (MIC_DMA_DESC_RX_SIZE & 0x1ffff) << 4; | ||
| 243 | if (MIC_DMA_CHAN_MIC == ch->owner) { | ||
| 244 | drar_hi |= (desc_ring_micpa >> 32) & 0xf; | ||
| 245 | } else { | ||
| 246 | drar_hi |= MIC_DMA_SBOX_DRARHI_SYS_MASK; | ||
| 247 | drar_hi |= ((desc_ring_micpa >> 34) | ||
| 248 | & 0x1f) << 21; | ||
| 249 | drar_hi |= (desc_ring_micpa >> 32) & 0x3; | ||
| 250 | } | ||
| 251 | mic_dma_write_reg(ch, MIC_DMA_REG_DRAR_LO, (u32) desc_ring_micpa); | ||
| 252 | mic_dma_write_reg(ch, MIC_DMA_REG_DRAR_HI, drar_hi); | ||
| 253 | } | ||
| 254 | |||
| 255 | static inline void mic_dma_chan_mask_intr(struct mic_dma_chan *ch) | ||
| 256 | { | ||
| 257 | u32 dcar = mic_dma_read_reg(ch, MIC_DMA_REG_DCAR); | ||
| 258 | |||
| 259 | if (MIC_DMA_CHAN_MIC == ch->owner) | ||
| 260 | dcar |= MIC_DMA_SBOX_DCAR_IM0; | ||
| 261 | else | ||
| 262 | dcar |= MIC_DMA_SBOX_DCAR_IM1; | ||
| 263 | mic_dma_write_reg(ch, MIC_DMA_REG_DCAR, dcar); | ||
| 264 | } | ||
| 265 | |||
| 266 | static inline void mic_dma_chan_unmask_intr(struct mic_dma_chan *ch) | ||
| 267 | { | ||
| 268 | u32 dcar = mic_dma_read_reg(ch, MIC_DMA_REG_DCAR); | ||
| 269 | |||
| 270 | if (MIC_DMA_CHAN_MIC == ch->owner) | ||
| 271 | dcar &= ~MIC_DMA_SBOX_DCAR_IM0; | ||
| 272 | else | ||
| 273 | dcar &= ~MIC_DMA_SBOX_DCAR_IM1; | ||
| 274 | mic_dma_write_reg(ch, MIC_DMA_REG_DCAR, dcar); | ||
| 275 | } | ||
| 276 | |||
| 277 | static void mic_dma_ack_interrupt(struct mic_dma_chan *ch) | ||
| 278 | { | ||
| 279 | if (MIC_DMA_CHAN_MIC == ch->owner) { | ||
| 280 | /* HW errata */ | ||
| 281 | mic_dma_chan_mask_intr(ch); | ||
| 282 | mic_dma_chan_unmask_intr(ch); | ||
| 283 | } | ||
| 284 | to_mbus_hw_ops(ch)->ack_interrupt(to_mbus_device(ch), ch->ch_num); | ||
| 285 | } | ||
| 286 | #endif | ||
diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index aebde489c291..6f2f4727de2c 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig | |||
| @@ -14,6 +14,20 @@ if EXTCON | |||
| 14 | 14 | ||
| 15 | comment "Extcon Device Drivers" | 15 | comment "Extcon Device Drivers" |
| 16 | 16 | ||
| 17 | config EXTCON_ADC_JACK | ||
| 18 | tristate "ADC Jack extcon support" | ||
| 19 | depends on IIO | ||
| 20 | help | ||
| 21 | Say Y here to enable extcon device driver based on ADC values. | ||
| 22 | |||
| 23 | config EXTCON_ARIZONA | ||
| 24 | tristate "Wolfson Arizona EXTCON support" | ||
| 25 | depends on MFD_ARIZONA && INPUT && SND_SOC | ||
| 26 | help | ||
| 27 | Say Y here to enable support for external accessory detection | ||
| 28 | with Wolfson Arizona devices. These are audio CODECs with | ||
| 29 | advanced audio accessory detection support. | ||
| 30 | |||
| 17 | config EXTCON_GPIO | 31 | config EXTCON_GPIO |
| 18 | tristate "GPIO extcon support" | 32 | tristate "GPIO extcon support" |
| 19 | depends on GPIOLIB | 33 | depends on GPIOLIB |
| @@ -21,12 +35,6 @@ config EXTCON_GPIO | |||
| 21 | Say Y here to enable GPIO based extcon support. Note that GPIO | 35 | Say Y here to enable GPIO based extcon support. Note that GPIO |
| 22 | extcon supports single state per extcon instance. | 36 | extcon supports single state per extcon instance. |
| 23 | 37 | ||
| 24 | config EXTCON_ADC_JACK | ||
| 25 | tristate "ADC Jack extcon support" | ||
| 26 | depends on IIO | ||
| 27 | help | ||
| 28 | Say Y here to enable extcon device driver based on ADC values. | ||
| 29 | |||
| 30 | config EXTCON_MAX14577 | 38 | config EXTCON_MAX14577 |
| 31 | tristate "MAX14577/77836 EXTCON Support" | 39 | tristate "MAX14577/77836 EXTCON Support" |
| 32 | depends on MFD_MAX14577 | 40 | depends on MFD_MAX14577 |
| @@ -55,14 +63,6 @@ config EXTCON_MAX8997 | |||
| 55 | Maxim MAX8997 PMIC. The MAX8997 MUIC is a USB port accessory | 63 | Maxim MAX8997 PMIC. The MAX8997 MUIC is a USB port accessory |
| 56 | detector and switch. | 64 | detector and switch. |
| 57 | 65 | ||
| 58 | config EXTCON_ARIZONA | ||
| 59 | tristate "Wolfson Arizona EXTCON support" | ||
| 60 | depends on MFD_ARIZONA && INPUT && SND_SOC | ||
| 61 | help | ||
| 62 | Say Y here to enable support for external accessory detection | ||
| 63 | with Wolfson Arizona devices. These are audio CODECs with | ||
| 64 | advanced audio accessory detection support. | ||
| 65 | |||
| 66 | config EXTCON_PALMAS | 66 | config EXTCON_PALMAS |
| 67 | tristate "Palmas USB EXTCON support" | 67 | tristate "Palmas USB EXTCON support" |
| 68 | depends on MFD_PALMAS | 68 | depends on MFD_PALMAS |
| @@ -70,4 +70,14 @@ config EXTCON_PALMAS | |||
| 70 | Say Y here to enable support for USB peripheral and USB host | 70 | Say Y here to enable support for USB peripheral and USB host |
| 71 | detection by palmas usb. | 71 | detection by palmas usb. |
| 72 | 72 | ||
| 73 | config EXTCON_SM5502 | ||
| 74 | tristate "SM5502 EXTCON support" | ||
| 75 | select IRQ_DOMAIN | ||
| 76 | select REGMAP_I2C | ||
| 77 | select REGMAP_IRQ | ||
| 78 | help | ||
| 79 | If you say yes here you get support for the MUIC device of | ||
| 80 | Silicon Mitus SM5502. The SM5502 is a USB port accessory | ||
| 81 | detector and switch. | ||
| 82 | |||
| 73 | endif # MULTISTATE_SWITCH | 83 | endif # MULTISTATE_SWITCH |
diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index bf7861ec0906..b38546eb522a 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile | |||
| @@ -1,12 +1,13 @@ | |||
| 1 | # | 1 | |
| 2 | # Makefile for external connector class (extcon) devices | 2 | # Makefile for external connector class (extcon) devices |
| 3 | # | 3 | # |
| 4 | 4 | ||
| 5 | obj-$(CONFIG_EXTCON) += extcon-class.o | 5 | obj-$(CONFIG_EXTCON) += extcon-class.o |
| 6 | obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o | ||
| 7 | obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o | 6 | obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o |
| 7 | obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o | ||
| 8 | obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o | ||
| 8 | obj-$(CONFIG_EXTCON_MAX14577) += extcon-max14577.o | 9 | obj-$(CONFIG_EXTCON_MAX14577) += extcon-max14577.o |
| 9 | obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o | 10 | obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o |
| 10 | obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o | 11 | obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o |
| 11 | obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o | ||
| 12 | obj-$(CONFIG_EXTCON_PALMAS) += extcon-palmas.o | 12 | obj-$(CONFIG_EXTCON_PALMAS) += extcon-palmas.o |
| 13 | obj-$(CONFIG_EXTCON_SM5502) += extcon-sm5502.o | ||
diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c index e18f95be3733..d860229e4de1 100644 --- a/drivers/extcon/extcon-adc-jack.c +++ b/drivers/extcon/extcon-adc-jack.c | |||
| @@ -112,7 +112,6 @@ static int adc_jack_probe(struct platform_device *pdev) | |||
| 112 | dev_err(&pdev->dev, "failed to allocate extcon device\n"); | 112 | dev_err(&pdev->dev, "failed to allocate extcon device\n"); |
| 113 | return -ENOMEM; | 113 | return -ENOMEM; |
| 114 | } | 114 | } |
| 115 | data->edev->dev.parent = &pdev->dev; | ||
| 116 | data->edev->name = pdata->name; | 115 | data->edev->name = pdata->name; |
| 117 | 116 | ||
| 118 | /* Check the length of array and set num_cables */ | 117 | /* Check the length of array and set num_cables */ |
diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index 6c84e3d12043..ba51588cc000 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c | |||
| @@ -39,6 +39,11 @@ | |||
| 39 | #define ARIZONA_ACCDET_MODE_HPL 1 | 39 | #define ARIZONA_ACCDET_MODE_HPL 1 |
| 40 | #define ARIZONA_ACCDET_MODE_HPR 2 | 40 | #define ARIZONA_ACCDET_MODE_HPR 2 |
| 41 | 41 | ||
| 42 | #define ARIZONA_MICD_CLAMP_MODE_JDL 0x4 | ||
| 43 | #define ARIZONA_MICD_CLAMP_MODE_JDH 0x5 | ||
| 44 | #define ARIZONA_MICD_CLAMP_MODE_JDL_GP5H 0x9 | ||
| 45 | #define ARIZONA_MICD_CLAMP_MODE_JDH_GP5H 0xb | ||
| 46 | |||
| 42 | #define ARIZONA_HPDET_MAX 10000 | 47 | #define ARIZONA_HPDET_MAX 10000 |
| 43 | 48 | ||
| 44 | #define HPDET_DEBOUNCE 500 | 49 | #define HPDET_DEBOUNCE 500 |
| @@ -324,14 +329,17 @@ static void arizona_stop_mic(struct arizona_extcon_info *info) | |||
| 324 | } | 329 | } |
| 325 | 330 | ||
| 326 | static struct { | 331 | static struct { |
| 332 | unsigned int threshold; | ||
| 327 | unsigned int factor_a; | 333 | unsigned int factor_a; |
| 328 | unsigned int factor_b; | 334 | unsigned int factor_b; |
| 329 | } arizona_hpdet_b_ranges[] = { | 335 | } arizona_hpdet_b_ranges[] = { |
| 330 | { 5528, 362464 }, | 336 | { 100, 5528, 362464 }, |
| 331 | { 11084, 6186851 }, | 337 | { 169, 11084, 6186851 }, |
| 332 | { 11065, 65460395 }, | 338 | { 169, 11065, 65460395 }, |
| 333 | }; | 339 | }; |
| 334 | 340 | ||
| 341 | #define ARIZONA_HPDET_B_RANGE_MAX 0x3fb | ||
| 342 | |||
| 335 | static struct { | 343 | static struct { |
| 336 | int min; | 344 | int min; |
| 337 | int max; | 345 | int max; |
| @@ -386,7 +394,8 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info) | |||
| 386 | >> ARIZONA_HP_IMPEDANCE_RANGE_SHIFT; | 394 | >> ARIZONA_HP_IMPEDANCE_RANGE_SHIFT; |
| 387 | 395 | ||
| 388 | if (range < ARRAY_SIZE(arizona_hpdet_b_ranges) - 1 && | 396 | if (range < ARRAY_SIZE(arizona_hpdet_b_ranges) - 1 && |
| 389 | (val < 100 || val >= 0x3fb)) { | 397 | (val < arizona_hpdet_b_ranges[range].threshold || |
| 398 | val >= ARIZONA_HPDET_B_RANGE_MAX)) { | ||
| 390 | range++; | 399 | range++; |
| 391 | dev_dbg(arizona->dev, "Moving to HPDET range %d\n", | 400 | dev_dbg(arizona->dev, "Moving to HPDET range %d\n", |
| 392 | range); | 401 | range); |
| @@ -399,7 +408,8 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info) | |||
| 399 | } | 408 | } |
| 400 | 409 | ||
| 401 | /* If we go out of range report top of range */ | 410 | /* If we go out of range report top of range */ |
| 402 | if (val < 100 || val >= 0x3fb) { | 411 | if (val < arizona_hpdet_b_ranges[range].threshold || |
| 412 | val >= ARIZONA_HPDET_B_RANGE_MAX) { | ||
| 403 | dev_dbg(arizona->dev, "Measurement out of range\n"); | 413 | dev_dbg(arizona->dev, "Measurement out of range\n"); |
| 404 | return ARIZONA_HPDET_MAX; | 414 | return ARIZONA_HPDET_MAX; |
| 405 | } | 415 | } |
| @@ -664,9 +674,8 @@ err: | |||
| 664 | ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC); | 674 | ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC); |
| 665 | 675 | ||
| 666 | /* Just report headphone */ | 676 | /* Just report headphone */ |
| 667 | ret = extcon_update_state(info->edev, | 677 | ret = extcon_set_cable_state_(info->edev, |
| 668 | 1 << ARIZONA_CABLE_HEADPHONE, | 678 | ARIZONA_CABLE_HEADPHONE, true); |
| 669 | 1 << ARIZONA_CABLE_HEADPHONE); | ||
| 670 | if (ret != 0) | 679 | if (ret != 0) |
| 671 | dev_err(arizona->dev, "Failed to report headphone: %d\n", ret); | 680 | dev_err(arizona->dev, "Failed to report headphone: %d\n", ret); |
| 672 | 681 | ||
| @@ -723,9 +732,8 @@ err: | |||
| 723 | ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC); | 732 | ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC); |
| 724 | 733 | ||
| 725 | /* Just report headphone */ | 734 | /* Just report headphone */ |
| 726 | ret = extcon_update_state(info->edev, | 735 | ret = extcon_set_cable_state_(info->edev, |
| 727 | 1 << ARIZONA_CABLE_HEADPHONE, | 736 | ARIZONA_CABLE_HEADPHONE, true); |
| 728 | 1 << ARIZONA_CABLE_HEADPHONE); | ||
| 729 | if (ret != 0) | 737 | if (ret != 0) |
| 730 | dev_err(arizona->dev, "Failed to report headphone: %d\n", ret); | 738 | dev_err(arizona->dev, "Failed to report headphone: %d\n", ret); |
| 731 | 739 | ||
| @@ -812,16 +820,15 @@ static void arizona_micd_detect(struct work_struct *work) | |||
| 812 | if (info->detecting && (val & ARIZONA_MICD_LVL_8)) { | 820 | if (info->detecting && (val & ARIZONA_MICD_LVL_8)) { |
| 813 | arizona_identify_headphone(info); | 821 | arizona_identify_headphone(info); |
| 814 | 822 | ||
| 815 | ret = extcon_update_state(info->edev, | 823 | ret = extcon_set_cable_state_(info->edev, |
| 816 | 1 << ARIZONA_CABLE_MICROPHONE, | 824 | ARIZONA_CABLE_MICROPHONE, true); |
| 817 | 1 << ARIZONA_CABLE_MICROPHONE); | ||
| 818 | 825 | ||
| 819 | if (ret != 0) | 826 | if (ret != 0) |
| 820 | dev_err(arizona->dev, "Headset report failed: %d\n", | 827 | dev_err(arizona->dev, "Headset report failed: %d\n", |
| 821 | ret); | 828 | ret); |
| 822 | 829 | ||
| 823 | /* Don't need to regulate for button detection */ | 830 | /* Don't need to regulate for button detection */ |
| 824 | ret = regulator_allow_bypass(info->micvdd, false); | 831 | ret = regulator_allow_bypass(info->micvdd, true); |
| 825 | if (ret != 0) { | 832 | if (ret != 0) { |
| 826 | dev_err(arizona->dev, "Failed to bypass MICVDD: %d\n", | 833 | dev_err(arizona->dev, "Failed to bypass MICVDD: %d\n", |
| 827 | ret); | 834 | ret); |
| @@ -962,10 +969,16 @@ static irqreturn_t arizona_jackdet(int irq, void *data) | |||
| 962 | 969 | ||
| 963 | if (arizona->pdata.jd_gpio5) { | 970 | if (arizona->pdata.jd_gpio5) { |
| 964 | mask = ARIZONA_MICD_CLAMP_STS; | 971 | mask = ARIZONA_MICD_CLAMP_STS; |
| 965 | present = 0; | 972 | if (arizona->pdata.jd_invert) |
| 973 | present = ARIZONA_MICD_CLAMP_STS; | ||
| 974 | else | ||
| 975 | present = 0; | ||
| 966 | } else { | 976 | } else { |
| 967 | mask = ARIZONA_JD1_STS; | 977 | mask = ARIZONA_JD1_STS; |
| 968 | present = ARIZONA_JD1_STS; | 978 | if (arizona->pdata.jd_invert) |
| 979 | present = 0; | ||
| 980 | else | ||
| 981 | present = ARIZONA_JD1_STS; | ||
| 969 | } | 982 | } |
| 970 | 983 | ||
| 971 | ret = regmap_read(arizona->regmap, ARIZONA_AOD_IRQ_RAW_STATUS, &val); | 984 | ret = regmap_read(arizona->regmap, ARIZONA_AOD_IRQ_RAW_STATUS, &val); |
| @@ -1096,6 +1109,7 @@ static int arizona_extcon_probe(struct platform_device *pdev) | |||
| 1096 | struct arizona_pdata *pdata = &arizona->pdata; | 1109 | struct arizona_pdata *pdata = &arizona->pdata; |
| 1097 | struct arizona_extcon_info *info; | 1110 | struct arizona_extcon_info *info; |
| 1098 | unsigned int val; | 1111 | unsigned int val; |
| 1112 | unsigned int clamp_mode; | ||
| 1099 | int jack_irq_fall, jack_irq_rise; | 1113 | int jack_irq_fall, jack_irq_rise; |
| 1100 | int ret, mode, i, j; | 1114 | int ret, mode, i, j; |
| 1101 | 1115 | ||
| @@ -1103,12 +1117,10 @@ static int arizona_extcon_probe(struct platform_device *pdev) | |||
| 1103 | return -EPROBE_DEFER; | 1117 | return -EPROBE_DEFER; |
| 1104 | 1118 | ||
| 1105 | info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); | 1119 | info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); |
| 1106 | if (!info) { | 1120 | if (!info) |
| 1107 | dev_err(&pdev->dev, "Failed to allocate memory\n"); | ||
| 1108 | return -ENOMEM; | 1121 | return -ENOMEM; |
| 1109 | } | ||
| 1110 | 1122 | ||
| 1111 | info->micvdd = devm_regulator_get(arizona->dev, "MICVDD"); | 1123 | info->micvdd = devm_regulator_get(&pdev->dev, "MICVDD"); |
| 1112 | if (IS_ERR(info->micvdd)) { | 1124 | if (IS_ERR(info->micvdd)) { |
| 1113 | ret = PTR_ERR(info->micvdd); | 1125 | ret = PTR_ERR(info->micvdd); |
| 1114 | dev_err(arizona->dev, "Failed to get MICVDD: %d\n", ret); | 1126 | dev_err(arizona->dev, "Failed to get MICVDD: %d\n", ret); |
| @@ -1156,7 +1168,6 @@ static int arizona_extcon_probe(struct platform_device *pdev) | |||
| 1156 | return -ENOMEM; | 1168 | return -ENOMEM; |
| 1157 | } | 1169 | } |
| 1158 | info->edev->name = "Headset Jack"; | 1170 | info->edev->name = "Headset Jack"; |
| 1159 | info->edev->dev.parent = arizona->dev; | ||
| 1160 | 1171 | ||
| 1161 | ret = devm_extcon_dev_register(&pdev->dev, info->edev); | 1172 | ret = devm_extcon_dev_register(&pdev->dev, info->edev); |
| 1162 | if (ret < 0) { | 1173 | if (ret < 0) { |
| @@ -1174,7 +1185,6 @@ static int arizona_extcon_probe(struct platform_device *pdev) | |||
| 1174 | 1185 | ||
| 1175 | info->input->name = "Headset"; | 1186 | info->input->name = "Headset"; |
| 1176 | info->input->phys = "arizona/extcon"; | 1187 | info->input->phys = "arizona/extcon"; |
| 1177 | info->input->dev.parent = &pdev->dev; | ||
| 1178 | 1188 | ||
| 1179 | if (pdata->num_micd_configs) { | 1189 | if (pdata->num_micd_configs) { |
| 1180 | info->micd_modes = pdata->micd_configs; | 1190 | info->micd_modes = pdata->micd_configs; |
| @@ -1305,16 +1315,22 @@ static int arizona_extcon_probe(struct platform_device *pdev) | |||
| 1305 | regmap_write(arizona->regmap, ARIZONA_GPIO5_CTRL, | 1315 | regmap_write(arizona->regmap, ARIZONA_GPIO5_CTRL, |
| 1306 | val); | 1316 | val); |
| 1307 | 1317 | ||
| 1308 | regmap_update_bits(arizona->regmap, | 1318 | if (arizona->pdata.jd_invert) |
| 1309 | ARIZONA_MICD_CLAMP_CONTROL, | 1319 | clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDH_GP5H; |
| 1310 | ARIZONA_MICD_CLAMP_MODE_MASK, 0x9); | 1320 | else |
| 1321 | clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDL_GP5H; | ||
| 1311 | } else { | 1322 | } else { |
| 1312 | regmap_update_bits(arizona->regmap, | 1323 | if (arizona->pdata.jd_invert) |
| 1313 | ARIZONA_MICD_CLAMP_CONTROL, | 1324 | clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDH; |
| 1314 | ARIZONA_MICD_CLAMP_MODE_MASK, 0x4); | 1325 | else |
| 1326 | clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDL; | ||
| 1315 | } | 1327 | } |
| 1316 | 1328 | ||
| 1317 | regmap_update_bits(arizona->regmap, | 1329 | regmap_update_bits(arizona->regmap, |
| 1330 | ARIZONA_MICD_CLAMP_CONTROL, | ||
| 1331 | ARIZONA_MICD_CLAMP_MODE_MASK, clamp_mode); | ||
| 1332 | |||
| 1333 | regmap_update_bits(arizona->regmap, | ||
| 1318 | ARIZONA_JACK_DETECT_DEBOUNCE, | 1334 | ARIZONA_JACK_DETECT_DEBOUNCE, |
| 1319 | ARIZONA_MICD_CLAMP_DB, | 1335 | ARIZONA_MICD_CLAMP_DB, |
| 1320 | ARIZONA_MICD_CLAMP_DB); | 1336 | ARIZONA_MICD_CLAMP_DB); |
diff --git a/drivers/extcon/extcon-class.c b/drivers/extcon/extcon-class.c index 18d42c0e4581..4c2f2c543bb7 100644 --- a/drivers/extcon/extcon-class.c +++ b/drivers/extcon/extcon-class.c | |||
| @@ -645,6 +645,8 @@ struct extcon_dev *devm_extcon_dev_allocate(struct device *dev, | |||
| 645 | return edev; | 645 | return edev; |
| 646 | } | 646 | } |
| 647 | 647 | ||
| 648 | edev->dev.parent = dev; | ||
| 649 | |||
| 648 | *ptr = edev; | 650 | *ptr = edev; |
| 649 | devres_add(dev, ptr); | 651 | devres_add(dev, ptr); |
| 650 | 652 | ||
diff --git a/drivers/extcon/extcon-gpio.c b/drivers/extcon/extcon-gpio.c index 645b28356819..5b7ec274cb63 100644 --- a/drivers/extcon/extcon-gpio.c +++ b/drivers/extcon/extcon-gpio.c | |||
| @@ -105,7 +105,6 @@ static int gpio_extcon_probe(struct platform_device *pdev) | |||
| 105 | return -ENOMEM; | 105 | return -ENOMEM; |
| 106 | } | 106 | } |
| 107 | extcon_data->edev->name = pdata->name; | 107 | extcon_data->edev->name = pdata->name; |
| 108 | extcon_data->edev->dev.parent = &pdev->dev; | ||
| 109 | 108 | ||
| 110 | extcon_data->gpio = pdata->gpio; | 109 | extcon_data->gpio = pdata->gpio; |
| 111 | extcon_data->gpio_active_low = pdata->gpio_active_low; | 110 | extcon_data->gpio_active_low = pdata->gpio_active_low; |
diff --git a/drivers/extcon/extcon-max14577.c b/drivers/extcon/extcon-max14577.c index d49e891b5675..7309743d0da1 100644 --- a/drivers/extcon/extcon-max14577.c +++ b/drivers/extcon/extcon-max14577.c | |||
| @@ -692,10 +692,9 @@ static int max14577_muic_probe(struct platform_device *pdev) | |||
| 692 | u8 id; | 692 | u8 id; |
| 693 | 693 | ||
| 694 | info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); | 694 | info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); |
| 695 | if (!info) { | 695 | if (!info) |
| 696 | dev_err(&pdev->dev, "failed to allocate memory\n"); | ||
| 697 | return -ENOMEM; | 696 | return -ENOMEM; |
| 698 | } | 697 | |
| 699 | info->dev = &pdev->dev; | 698 | info->dev = &pdev->dev; |
| 700 | info->max14577 = max14577; | 699 | info->max14577 = max14577; |
| 701 | 700 | ||
diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c index 2c7c3e191591..77460f2c1ca1 100644 --- a/drivers/extcon/extcon-max77693.c +++ b/drivers/extcon/extcon-max77693.c | |||
| @@ -255,10 +255,10 @@ static int max77693_muic_set_debounce_time(struct max77693_muic_info *info, | |||
| 255 | case ADC_DEBOUNCE_TIME_10MS: | 255 | case ADC_DEBOUNCE_TIME_10MS: |
| 256 | case ADC_DEBOUNCE_TIME_25MS: | 256 | case ADC_DEBOUNCE_TIME_25MS: |
| 257 | case ADC_DEBOUNCE_TIME_38_62MS: | 257 | case ADC_DEBOUNCE_TIME_38_62MS: |
| 258 | ret = max77693_update_reg(info->max77693->regmap_muic, | 258 | ret = regmap_update_bits(info->max77693->regmap_muic, |
| 259 | MAX77693_MUIC_REG_CTRL3, | 259 | MAX77693_MUIC_REG_CTRL3, |
| 260 | time << CONTROL3_ADCDBSET_SHIFT, | 260 | CONTROL3_ADCDBSET_MASK, |
| 261 | CONTROL3_ADCDBSET_MASK); | 261 | time << CONTROL3_ADCDBSET_SHIFT); |
| 262 | if (ret) { | 262 | if (ret) { |
| 263 | dev_err(info->dev, "failed to set ADC debounce time\n"); | 263 | dev_err(info->dev, "failed to set ADC debounce time\n"); |
| 264 | return ret; | 264 | return ret; |
| @@ -286,15 +286,15 @@ static int max77693_muic_set_path(struct max77693_muic_info *info, | |||
| 286 | u8 val, bool attached) | 286 | u8 val, bool attached) |
| 287 | { | 287 | { |
| 288 | int ret = 0; | 288 | int ret = 0; |
| 289 | u8 ctrl1, ctrl2 = 0; | 289 | unsigned int ctrl1, ctrl2 = 0; |
| 290 | 290 | ||
| 291 | if (attached) | 291 | if (attached) |
| 292 | ctrl1 = val; | 292 | ctrl1 = val; |
| 293 | else | 293 | else |
| 294 | ctrl1 = CONTROL1_SW_OPEN; | 294 | ctrl1 = CONTROL1_SW_OPEN; |
| 295 | 295 | ||
| 296 | ret = max77693_update_reg(info->max77693->regmap_muic, | 296 | ret = regmap_update_bits(info->max77693->regmap_muic, |
| 297 | MAX77693_MUIC_REG_CTRL1, ctrl1, COMP_SW_MASK); | 297 | MAX77693_MUIC_REG_CTRL1, COMP_SW_MASK, ctrl1); |
| 298 | if (ret < 0) { | 298 | if (ret < 0) { |
| 299 | dev_err(info->dev, "failed to update MUIC register\n"); | 299 | dev_err(info->dev, "failed to update MUIC register\n"); |
| 300 | return ret; | 300 | return ret; |
| @@ -305,9 +305,9 @@ static int max77693_muic_set_path(struct max77693_muic_info *info, | |||
| 305 | else | 305 | else |
| 306 | ctrl2 |= CONTROL2_LOWPWR_MASK; /* LowPwr=1, CPEn=0 */ | 306 | ctrl2 |= CONTROL2_LOWPWR_MASK; /* LowPwr=1, CPEn=0 */ |
| 307 | 307 | ||
| 308 | ret = max77693_update_reg(info->max77693->regmap_muic, | 308 | ret = regmap_update_bits(info->max77693->regmap_muic, |
| 309 | MAX77693_MUIC_REG_CTRL2, ctrl2, | 309 | MAX77693_MUIC_REG_CTRL2, |
| 310 | CONTROL2_LOWPWR_MASK | CONTROL2_CPEN_MASK); | 310 | CONTROL2_LOWPWR_MASK | CONTROL2_CPEN_MASK, ctrl2); |
| 311 | if (ret < 0) { | 311 | if (ret < 0) { |
| 312 | dev_err(info->dev, "failed to update MUIC register\n"); | 312 | dev_err(info->dev, "failed to update MUIC register\n"); |
| 313 | return ret; | 313 | return ret; |
| @@ -969,8 +969,8 @@ static void max77693_muic_irq_work(struct work_struct *work) | |||
| 969 | if (info->irq == muic_irqs[i].virq) | 969 | if (info->irq == muic_irqs[i].virq) |
| 970 | irq_type = muic_irqs[i].irq; | 970 | irq_type = muic_irqs[i].irq; |
| 971 | 971 | ||
| 972 | ret = max77693_bulk_read(info->max77693->regmap_muic, | 972 | ret = regmap_bulk_read(info->max77693->regmap_muic, |
| 973 | MAX77693_MUIC_REG_STATUS1, 2, info->status); | 973 | MAX77693_MUIC_REG_STATUS1, info->status, 2); |
| 974 | if (ret) { | 974 | if (ret) { |
| 975 | dev_err(info->dev, "failed to read MUIC register\n"); | 975 | dev_err(info->dev, "failed to read MUIC register\n"); |
| 976 | mutex_unlock(&info->mutex); | 976 | mutex_unlock(&info->mutex); |
| @@ -1042,8 +1042,8 @@ static int max77693_muic_detect_accessory(struct max77693_muic_info *info) | |||
| 1042 | mutex_lock(&info->mutex); | 1042 | mutex_lock(&info->mutex); |
| 1043 | 1043 | ||
| 1044 | /* Read STATUSx register to detect accessory */ | 1044 | /* Read STATUSx register to detect accessory */ |
| 1045 | ret = max77693_bulk_read(info->max77693->regmap_muic, | 1045 | ret = regmap_bulk_read(info->max77693->regmap_muic, |
| 1046 | MAX77693_MUIC_REG_STATUS1, 2, info->status); | 1046 | MAX77693_MUIC_REG_STATUS1, info->status, 2); |
| 1047 | if (ret) { | 1047 | if (ret) { |
| 1048 | dev_err(info->dev, "failed to read MUIC register\n"); | 1048 | dev_err(info->dev, "failed to read MUIC register\n"); |
| 1049 | mutex_unlock(&info->mutex); | 1049 | mutex_unlock(&info->mutex); |
| @@ -1095,14 +1095,13 @@ static int max77693_muic_probe(struct platform_device *pdev) | |||
| 1095 | int delay_jiffies; | 1095 | int delay_jiffies; |
| 1096 | int ret; | 1096 | int ret; |
| 1097 | int i; | 1097 | int i; |
| 1098 | u8 id; | 1098 | unsigned int id; |
| 1099 | 1099 | ||
| 1100 | info = devm_kzalloc(&pdev->dev, sizeof(struct max77693_muic_info), | 1100 | info = devm_kzalloc(&pdev->dev, sizeof(struct max77693_muic_info), |
| 1101 | GFP_KERNEL); | 1101 | GFP_KERNEL); |
| 1102 | if (!info) { | 1102 | if (!info) |
| 1103 | dev_err(&pdev->dev, "failed to allocate memory\n"); | ||
| 1104 | return -ENOMEM; | 1103 | return -ENOMEM; |
| 1105 | } | 1104 | |
| 1106 | info->dev = &pdev->dev; | 1105 | info->dev = &pdev->dev; |
| 1107 | info->max77693 = max77693; | 1106 | info->max77693 = max77693; |
| 1108 | if (info->max77693->regmap_muic) { | 1107 | if (info->max77693->regmap_muic) { |
| @@ -1154,7 +1153,8 @@ static int max77693_muic_probe(struct platform_device *pdev) | |||
| 1154 | struct max77693_muic_irq *muic_irq = &muic_irqs[i]; | 1153 | struct max77693_muic_irq *muic_irq = &muic_irqs[i]; |
| 1155 | unsigned int virq = 0; | 1154 | unsigned int virq = 0; |
| 1156 | 1155 | ||
| 1157 | virq = irq_create_mapping(max77693->irq_domain, muic_irq->irq); | 1156 | virq = regmap_irq_get_virq(max77693->irq_data_muic, |
| 1157 | muic_irq->irq); | ||
| 1158 | if (!virq) { | 1158 | if (!virq) { |
| 1159 | ret = -EINVAL; | 1159 | ret = -EINVAL; |
| 1160 | goto err_irq; | 1160 | goto err_irq; |
| @@ -1183,7 +1183,6 @@ static int max77693_muic_probe(struct platform_device *pdev) | |||
| 1183 | goto err_irq; | 1183 | goto err_irq; |
| 1184 | } | 1184 | } |
| 1185 | info->edev->name = DEV_NAME; | 1185 | info->edev->name = DEV_NAME; |
| 1186 | info->edev->dev.parent = &pdev->dev; | ||
| 1187 | 1186 | ||
| 1188 | ret = devm_extcon_dev_register(&pdev->dev, info->edev); | 1187 | ret = devm_extcon_dev_register(&pdev->dev, info->edev); |
| 1189 | if (ret) { | 1188 | if (ret) { |
| @@ -1204,7 +1203,7 @@ static int max77693_muic_probe(struct platform_device *pdev) | |||
| 1204 | enum max77693_irq_source irq_src | 1203 | enum max77693_irq_source irq_src |
| 1205 | = MAX77693_IRQ_GROUP_NR; | 1204 | = MAX77693_IRQ_GROUP_NR; |
| 1206 | 1205 | ||
| 1207 | max77693_write_reg(info->max77693->regmap_muic, | 1206 | regmap_write(info->max77693->regmap_muic, |
| 1208 | init_data[i].addr, | 1207 | init_data[i].addr, |
| 1209 | init_data[i].data); | 1208 | init_data[i].data); |
| 1210 | 1209 | ||
| @@ -1262,7 +1261,7 @@ static int max77693_muic_probe(struct platform_device *pdev) | |||
| 1262 | max77693_muic_set_path(info, info->path_uart, true); | 1261 | max77693_muic_set_path(info, info->path_uart, true); |
| 1263 | 1262 | ||
| 1264 | /* Check revision number of MUIC device*/ | 1263 | /* Check revision number of MUIC device*/ |
| 1265 | ret = max77693_read_reg(info->max77693->regmap_muic, | 1264 | ret = regmap_read(info->max77693->regmap_muic, |
| 1266 | MAX77693_MUIC_REG_ID, &id); | 1265 | MAX77693_MUIC_REG_ID, &id); |
| 1267 | if (ret < 0) { | 1266 | if (ret < 0) { |
| 1268 | dev_err(&pdev->dev, "failed to read revision number\n"); | 1267 | dev_err(&pdev->dev, "failed to read revision number\n"); |
diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c index d9f7f1baaa03..75e501c98005 100644 --- a/drivers/extcon/extcon-max8997.c +++ b/drivers/extcon/extcon-max8997.c | |||
| @@ -661,10 +661,8 @@ static int max8997_muic_probe(struct platform_device *pdev) | |||
| 661 | 661 | ||
| 662 | info = devm_kzalloc(&pdev->dev, sizeof(struct max8997_muic_info), | 662 | info = devm_kzalloc(&pdev->dev, sizeof(struct max8997_muic_info), |
| 663 | GFP_KERNEL); | 663 | GFP_KERNEL); |
| 664 | if (!info) { | 664 | if (!info) |
| 665 | dev_err(&pdev->dev, "failed to allocate memory\n"); | ||
| 666 | return -ENOMEM; | 665 | return -ENOMEM; |
| 667 | } | ||
| 668 | 666 | ||
| 669 | info->dev = &pdev->dev; | 667 | info->dev = &pdev->dev; |
| 670 | info->muic = max8997->muic; | 668 | info->muic = max8997->muic; |
| @@ -706,7 +704,6 @@ static int max8997_muic_probe(struct platform_device *pdev) | |||
| 706 | goto err_irq; | 704 | goto err_irq; |
| 707 | } | 705 | } |
| 708 | info->edev->name = DEV_NAME; | 706 | info->edev->name = DEV_NAME; |
| 709 | info->edev->dev.parent = &pdev->dev; | ||
| 710 | 707 | ||
| 711 | ret = devm_extcon_dev_register(&pdev->dev, info->edev); | 708 | ret = devm_extcon_dev_register(&pdev->dev, info->edev); |
| 712 | if (ret) { | 709 | if (ret) { |
diff --git a/drivers/extcon/extcon-palmas.c b/drivers/extcon/extcon-palmas.c index 7417ce84eb2d..230e1220ce48 100644 --- a/drivers/extcon/extcon-palmas.c +++ b/drivers/extcon/extcon-palmas.c | |||
| @@ -194,7 +194,6 @@ static int palmas_usb_probe(struct platform_device *pdev) | |||
| 194 | return -ENOMEM; | 194 | return -ENOMEM; |
| 195 | } | 195 | } |
| 196 | palmas_usb->edev->name = kstrdup(node->name, GFP_KERNEL); | 196 | palmas_usb->edev->name = kstrdup(node->name, GFP_KERNEL); |
| 197 | palmas_usb->edev->dev.parent = palmas_usb->dev; | ||
| 198 | palmas_usb->edev->mutually_exclusive = mutually_exclusive; | 197 | palmas_usb->edev->mutually_exclusive = mutually_exclusive; |
| 199 | 198 | ||
| 200 | status = devm_extcon_dev_register(&pdev->dev, palmas_usb->edev); | 199 | status = devm_extcon_dev_register(&pdev->dev, palmas_usb->edev); |
| @@ -278,7 +277,7 @@ static int palmas_usb_resume(struct device *dev) | |||
| 278 | 277 | ||
| 279 | static SIMPLE_DEV_PM_OPS(palmas_pm_ops, palmas_usb_suspend, palmas_usb_resume); | 278 | static SIMPLE_DEV_PM_OPS(palmas_pm_ops, palmas_usb_suspend, palmas_usb_resume); |
| 280 | 279 | ||
| 281 | static struct of_device_id of_palmas_match_tbl[] = { | 280 | static const struct of_device_id of_palmas_match_tbl[] = { |
| 282 | { .compatible = "ti,palmas-usb", }, | 281 | { .compatible = "ti,palmas-usb", }, |
| 283 | { .compatible = "ti,palmas-usb-vid", }, | 282 | { .compatible = "ti,palmas-usb-vid", }, |
| 284 | { .compatible = "ti,twl6035-usb", }, | 283 | { .compatible = "ti,twl6035-usb", }, |
diff --git a/drivers/extcon/extcon-sm5502.c b/drivers/extcon/extcon-sm5502.c new file mode 100644 index 000000000000..560d7dccec7b --- /dev/null +++ b/drivers/extcon/extcon-sm5502.c | |||
| @@ -0,0 +1,724 @@ | |||
| 1 | /* | ||
| 2 | * extcon-sm5502.c - Silicon Mitus SM5502 extcon drvier to support USB switches | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014 Samsung Electronics Co., Ltd | ||
| 5 | * Author: Chanwoo Choi <cw00.choi@samsung.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify it | ||
| 8 | * under the terms of the GNU General Public License as published by the | ||
| 9 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 10 | * option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <linux/err.h> | ||
| 19 | #include <linux/i2c.h> | ||
| 20 | #include <linux/input.h> | ||
| 21 | #include <linux/interrupt.h> | ||
| 22 | #include <linux/irqdomain.h> | ||
| 23 | #include <linux/kernel.h> | ||
| 24 | #include <linux/module.h> | ||
| 25 | #include <linux/platform_device.h> | ||
| 26 | #include <linux/regmap.h> | ||
| 27 | #include <linux/slab.h> | ||
| 28 | #include <linux/extcon.h> | ||
| 29 | #include <linux/extcon/sm5502.h> | ||
| 30 | |||
| 31 | #define DELAY_MS_DEFAULT 17000 /* unit: millisecond */ | ||
| 32 | |||
| 33 | struct muic_irq { | ||
| 34 | unsigned int irq; | ||
| 35 | const char *name; | ||
| 36 | unsigned int virq; | ||
| 37 | }; | ||
| 38 | |||
| 39 | struct reg_data { | ||
| 40 | u8 reg; | ||
| 41 | unsigned int val; | ||
| 42 | bool invert; | ||
| 43 | }; | ||
| 44 | |||
| 45 | struct sm5502_muic_info { | ||
| 46 | struct device *dev; | ||
| 47 | struct extcon_dev *edev; | ||
| 48 | |||
| 49 | struct i2c_client *i2c; | ||
| 50 | struct regmap *regmap; | ||
| 51 | |||
| 52 | struct regmap_irq_chip_data *irq_data; | ||
| 53 | struct muic_irq *muic_irqs; | ||
| 54 | unsigned int num_muic_irqs; | ||
| 55 | int irq; | ||
| 56 | bool irq_attach; | ||
| 57 | bool irq_detach; | ||
| 58 | struct work_struct irq_work; | ||
| 59 | |||
| 60 | struct reg_data *reg_data; | ||
| 61 | unsigned int num_reg_data; | ||
| 62 | |||
| 63 | struct mutex mutex; | ||
| 64 | |||
| 65 | /* | ||
| 66 | * Use delayed workqueue to detect cable state and then | ||
| 67 | * notify cable state to notifiee/platform through uevent. | ||
| 68 | * After completing the booting of platform, the extcon provider | ||
| 69 | * driver should notify cable state to upper layer. | ||
| 70 | */ | ||
| 71 | struct delayed_work wq_detcable; | ||
| 72 | }; | ||
| 73 | |||
| 74 | /* Default value of SM5502 register to bring up MUIC device. */ | ||
| 75 | static struct reg_data sm5502_reg_data[] = { | ||
| 76 | { | ||
| 77 | .reg = SM5502_REG_CONTROL, | ||
| 78 | .val = SM5502_REG_CONTROL_MASK_INT_MASK, | ||
| 79 | .invert = false, | ||
| 80 | }, { | ||
| 81 | .reg = SM5502_REG_INTMASK1, | ||
| 82 | .val = SM5502_REG_INTM1_KP_MASK | ||
| 83 | | SM5502_REG_INTM1_LKP_MASK | ||
| 84 | | SM5502_REG_INTM1_LKR_MASK, | ||
| 85 | .invert = true, | ||
| 86 | }, { | ||
| 87 | .reg = SM5502_REG_INTMASK2, | ||
| 88 | .val = SM5502_REG_INTM2_VBUS_DET_MASK | ||
| 89 | | SM5502_REG_INTM2_REV_ACCE_MASK | ||
| 90 | | SM5502_REG_INTM2_ADC_CHG_MASK | ||
| 91 | | SM5502_REG_INTM2_STUCK_KEY_MASK | ||
| 92 | | SM5502_REG_INTM2_STUCK_KEY_RCV_MASK | ||
| 93 | | SM5502_REG_INTM2_MHL_MASK, | ||
| 94 | .invert = true, | ||
| 95 | }, | ||
| 96 | { } | ||
| 97 | }; | ||
| 98 | |||
| 99 | /* List of detectable cables */ | ||
| 100 | enum { | ||
| 101 | EXTCON_CABLE_USB = 0, | ||
| 102 | EXTCON_CABLE_USB_HOST, | ||
| 103 | EXTCON_CABLE_TA, | ||
| 104 | |||
| 105 | EXTCON_CABLE_END, | ||
| 106 | }; | ||
| 107 | |||
| 108 | static const char *sm5502_extcon_cable[] = { | ||
| 109 | [EXTCON_CABLE_USB] = "USB", | ||
| 110 | [EXTCON_CABLE_USB_HOST] = "USB-Host", | ||
| 111 | [EXTCON_CABLE_TA] = "TA", | ||
| 112 | NULL, | ||
| 113 | }; | ||
| 114 | |||
| 115 | /* Define supported accessory type */ | ||
| 116 | enum sm5502_muic_acc_type { | ||
| 117 | SM5502_MUIC_ADC_GROUND = 0x0, | ||
| 118 | SM5502_MUIC_ADC_SEND_END_BUTTON, | ||
| 119 | SM5502_MUIC_ADC_REMOTE_S1_BUTTON, | ||
| 120 | SM5502_MUIC_ADC_REMOTE_S2_BUTTON, | ||
| 121 | SM5502_MUIC_ADC_REMOTE_S3_BUTTON, | ||
| 122 | SM5502_MUIC_ADC_REMOTE_S4_BUTTON, | ||
| 123 | SM5502_MUIC_ADC_REMOTE_S5_BUTTON, | ||
| 124 | SM5502_MUIC_ADC_REMOTE_S6_BUTTON, | ||
| 125 | SM5502_MUIC_ADC_REMOTE_S7_BUTTON, | ||
| 126 | SM5502_MUIC_ADC_REMOTE_S8_BUTTON, | ||
| 127 | SM5502_MUIC_ADC_REMOTE_S9_BUTTON, | ||
| 128 | SM5502_MUIC_ADC_REMOTE_S10_BUTTON, | ||
| 129 | SM5502_MUIC_ADC_REMOTE_S11_BUTTON, | ||
| 130 | SM5502_MUIC_ADC_REMOTE_S12_BUTTON, | ||
| 131 | SM5502_MUIC_ADC_RESERVED_ACC_1, | ||
| 132 | SM5502_MUIC_ADC_RESERVED_ACC_2, | ||
| 133 | SM5502_MUIC_ADC_RESERVED_ACC_3, | ||
| 134 | SM5502_MUIC_ADC_RESERVED_ACC_4, | ||
| 135 | SM5502_MUIC_ADC_RESERVED_ACC_5, | ||
| 136 | SM5502_MUIC_ADC_AUDIO_TYPE2, | ||
| 137 | SM5502_MUIC_ADC_PHONE_POWERED_DEV, | ||
| 138 | SM5502_MUIC_ADC_TTY_CONVERTER, | ||
| 139 | SM5502_MUIC_ADC_UART_CABLE, | ||
| 140 | SM5502_MUIC_ADC_TYPE1_CHARGER, | ||
| 141 | SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_USB, | ||
| 142 | SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_USB, | ||
| 143 | SM5502_MUIC_ADC_AUDIO_VIDEO_CABLE, | ||
| 144 | SM5502_MUIC_ADC_TYPE2_CHARGER, | ||
| 145 | SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_UART, | ||
| 146 | SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_UART, | ||
| 147 | SM5502_MUIC_ADC_AUDIO_TYPE1, | ||
| 148 | SM5502_MUIC_ADC_OPEN = 0x1f, | ||
| 149 | |||
| 150 | /* The below accessories have same ADC value (0x1f or 0x1e). | ||
| 151 | So, Device type1 is used to separate specific accessory. */ | ||
| 152 | /* |---------|--ADC| */ | ||
| 153 | /* | [7:5]|[4:0]| */ | ||
| 154 | SM5502_MUIC_ADC_AUDIO_TYPE1_FULL_REMOTE = 0x3e, /* | 001|11110| */ | ||
| 155 | SM5502_MUIC_ADC_AUDIO_TYPE1_SEND_END = 0x5e, /* | 010|11110| */ | ||
| 156 | /* |Dev Type1|--ADC| */ | ||
| 157 | SM5502_MUIC_ADC_OPEN_USB = 0x5f, /* | 010|11111| */ | ||
| 158 | SM5502_MUIC_ADC_OPEN_TA = 0xdf, /* | 110|11111| */ | ||
| 159 | SM5502_MUIC_ADC_OPEN_USB_OTG = 0xff, /* | 111|11111| */ | ||
| 160 | }; | ||
| 161 | |||
| 162 | /* List of supported interrupt for SM5502 */ | ||
| 163 | static struct muic_irq sm5502_muic_irqs[] = { | ||
| 164 | { SM5502_IRQ_INT1_ATTACH, "muic-attach" }, | ||
| 165 | { SM5502_IRQ_INT1_DETACH, "muic-detach" }, | ||
| 166 | { SM5502_IRQ_INT1_KP, "muic-kp" }, | ||
| 167 | { SM5502_IRQ_INT1_LKP, "muic-lkp" }, | ||
| 168 | { SM5502_IRQ_INT1_LKR, "muic-lkr" }, | ||
| 169 | { SM5502_IRQ_INT1_OVP_EVENT, "muic-ovp-event" }, | ||
| 170 | { SM5502_IRQ_INT1_OCP_EVENT, "muic-ocp-event" }, | ||
| 171 | { SM5502_IRQ_INT1_OVP_OCP_DIS, "muic-ovp-ocp-dis" }, | ||
| 172 | { SM5502_IRQ_INT2_VBUS_DET, "muic-vbus-det" }, | ||
| 173 | { SM5502_IRQ_INT2_REV_ACCE, "muic-rev-acce" }, | ||
| 174 | { SM5502_IRQ_INT2_ADC_CHG, "muic-adc-chg" }, | ||
| 175 | { SM5502_IRQ_INT2_STUCK_KEY, "muic-stuck-key" }, | ||
| 176 | { SM5502_IRQ_INT2_STUCK_KEY_RCV, "muic-stuck-key-rcv" }, | ||
| 177 | { SM5502_IRQ_INT2_MHL, "muic-mhl" }, | ||
| 178 | }; | ||
| 179 | |||
| 180 | /* Define interrupt list of SM5502 to register regmap_irq */ | ||
| 181 | static const struct regmap_irq sm5502_irqs[] = { | ||
| 182 | /* INT1 interrupts */ | ||
| 183 | { .reg_offset = 0, .mask = SM5502_IRQ_INT1_ATTACH_MASK, }, | ||
| 184 | { .reg_offset = 0, .mask = SM5502_IRQ_INT1_DETACH_MASK, }, | ||
| 185 | { .reg_offset = 0, .mask = SM5502_IRQ_INT1_KP_MASK, }, | ||
| 186 | { .reg_offset = 0, .mask = SM5502_IRQ_INT1_LKP_MASK, }, | ||
| 187 | { .reg_offset = 0, .mask = SM5502_IRQ_INT1_LKR_MASK, }, | ||
| 188 | { .reg_offset = 0, .mask = SM5502_IRQ_INT1_OVP_EVENT_MASK, }, | ||
| 189 | { .reg_offset = 0, .mask = SM5502_IRQ_INT1_OCP_EVENT_MASK, }, | ||
| 190 | { .reg_offset = 0, .mask = SM5502_IRQ_INT1_OVP_OCP_DIS_MASK, }, | ||
| 191 | |||
| 192 | /* INT2 interrupts */ | ||
| 193 | { .reg_offset = 1, .mask = SM5502_IRQ_INT2_VBUS_DET_MASK,}, | ||
| 194 | { .reg_offset = 1, .mask = SM5502_IRQ_INT2_REV_ACCE_MASK, }, | ||
| 195 | { .reg_offset = 1, .mask = SM5502_IRQ_INT2_ADC_CHG_MASK, }, | ||
| 196 | { .reg_offset = 1, .mask = SM5502_IRQ_INT2_STUCK_KEY_MASK, }, | ||
| 197 | { .reg_offset = 1, .mask = SM5502_IRQ_INT2_STUCK_KEY_RCV_MASK, }, | ||
| 198 | { .reg_offset = 1, .mask = SM5502_IRQ_INT2_MHL_MASK, }, | ||
| 199 | }; | ||
| 200 | |||
| 201 | static const struct regmap_irq_chip sm5502_muic_irq_chip = { | ||
| 202 | .name = "sm5502", | ||
| 203 | .status_base = SM5502_REG_INT1, | ||
| 204 | .mask_base = SM5502_REG_INTMASK1, | ||
| 205 | .mask_invert = false, | ||
| 206 | .num_regs = 2, | ||
| 207 | .irqs = sm5502_irqs, | ||
| 208 | .num_irqs = ARRAY_SIZE(sm5502_irqs), | ||
| 209 | }; | ||
| 210 | |||
| 211 | /* Define regmap configuration of SM5502 for I2C communication */ | ||
| 212 | static bool sm5502_muic_volatile_reg(struct device *dev, unsigned int reg) | ||
| 213 | { | ||
| 214 | switch (reg) { | ||
| 215 | case SM5502_REG_INTMASK1: | ||
| 216 | case SM5502_REG_INTMASK2: | ||
| 217 | return true; | ||
| 218 | default: | ||
| 219 | break; | ||
| 220 | } | ||
| 221 | return false; | ||
| 222 | } | ||
| 223 | |||
| 224 | static const struct regmap_config sm5502_muic_regmap_config = { | ||
| 225 | .reg_bits = 8, | ||
| 226 | .val_bits = 8, | ||
| 227 | .volatile_reg = sm5502_muic_volatile_reg, | ||
| 228 | .max_register = SM5502_REG_END, | ||
| 229 | }; | ||
| 230 | |||
| 231 | /* Change DM_CON/DP_CON/VBUSIN switch according to cable type */ | ||
| 232 | static int sm5502_muic_set_path(struct sm5502_muic_info *info, | ||
| 233 | unsigned int con_sw, unsigned int vbus_sw, | ||
| 234 | bool attached) | ||
| 235 | { | ||
| 236 | int ret; | ||
| 237 | |||
| 238 | if (!attached) { | ||
| 239 | con_sw = DM_DP_SWITCH_OPEN; | ||
| 240 | vbus_sw = VBUSIN_SWITCH_OPEN; | ||
| 241 | } | ||
| 242 | |||
| 243 | switch (con_sw) { | ||
| 244 | case DM_DP_SWITCH_OPEN: | ||
| 245 | case DM_DP_SWITCH_USB: | ||
| 246 | case DM_DP_SWITCH_AUDIO: | ||
| 247 | case DM_DP_SWITCH_UART: | ||
| 248 | ret = regmap_update_bits(info->regmap, SM5502_REG_MANUAL_SW1, | ||
| 249 | SM5502_REG_MANUAL_SW1_DP_MASK | | ||
| 250 | SM5502_REG_MANUAL_SW1_DM_MASK, | ||
| 251 | con_sw); | ||
| 252 | if (ret < 0) { | ||
| 253 | dev_err(info->dev, | ||
| 254 | "cannot update DM_CON/DP_CON switch\n"); | ||
| 255 | return ret; | ||
| 256 | } | ||
| 257 | break; | ||
| 258 | default: | ||
| 259 | dev_err(info->dev, "Unknown DM_CON/DP_CON switch type (%d)\n", | ||
| 260 | con_sw); | ||
| 261 | return -EINVAL; | ||
| 262 | }; | ||
| 263 | |||
| 264 | switch (vbus_sw) { | ||
| 265 | case VBUSIN_SWITCH_OPEN: | ||
| 266 | case VBUSIN_SWITCH_VBUSOUT: | ||
| 267 | case VBUSIN_SWITCH_MIC: | ||
| 268 | case VBUSIN_SWITCH_VBUSOUT_WITH_USB: | ||
| 269 | ret = regmap_update_bits(info->regmap, SM5502_REG_MANUAL_SW1, | ||
| 270 | SM5502_REG_MANUAL_SW1_VBUSIN_MASK, | ||
| 271 | vbus_sw); | ||
| 272 | if (ret < 0) { | ||
| 273 | dev_err(info->dev, | ||
| 274 | "cannot update VBUSIN switch\n"); | ||
| 275 | return ret; | ||
| 276 | } | ||
| 277 | break; | ||
| 278 | default: | ||
| 279 | dev_err(info->dev, "Unknown VBUS switch type (%d)\n", vbus_sw); | ||
| 280 | return -EINVAL; | ||
| 281 | }; | ||
| 282 | |||
| 283 | return 0; | ||
| 284 | } | ||
| 285 | |||
| 286 | /* Return cable type of attached or detached accessories */ | ||
| 287 | static unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info) | ||
| 288 | { | ||
| 289 | unsigned int cable_type = -1, adc, dev_type1; | ||
| 290 | int ret; | ||
| 291 | |||
| 292 | /* Read ADC value according to external cable or button */ | ||
| 293 | ret = regmap_read(info->regmap, SM5502_REG_ADC, &adc); | ||
| 294 | if (ret) { | ||
| 295 | dev_err(info->dev, "failed to read ADC register\n"); | ||
| 296 | return ret; | ||
| 297 | } | ||
| 298 | |||
| 299 | /* | ||
| 300 | * If ADC is SM5502_MUIC_ADC_GROUND(0x0), external cable hasn't | ||
| 301 | * connected with to MUIC device. | ||
| 302 | */ | ||
| 303 | cable_type &= SM5502_REG_ADC_MASK; | ||
| 304 | if (cable_type == SM5502_MUIC_ADC_GROUND) | ||
| 305 | return SM5502_MUIC_ADC_GROUND; | ||
| 306 | |||
| 307 | switch (cable_type) { | ||
| 308 | case SM5502_MUIC_ADC_GROUND: | ||
| 309 | case SM5502_MUIC_ADC_SEND_END_BUTTON: | ||
| 310 | case SM5502_MUIC_ADC_REMOTE_S1_BUTTON: | ||
| 311 | case SM5502_MUIC_ADC_REMOTE_S2_BUTTON: | ||
| 312 | case SM5502_MUIC_ADC_REMOTE_S3_BUTTON: | ||
| 313 | case SM5502_MUIC_ADC_REMOTE_S4_BUTTON: | ||
| 314 | case SM5502_MUIC_ADC_REMOTE_S5_BUTTON: | ||
| 315 | case SM5502_MUIC_ADC_REMOTE_S6_BUTTON: | ||
| 316 | case SM5502_MUIC_ADC_REMOTE_S7_BUTTON: | ||
| 317 | case SM5502_MUIC_ADC_REMOTE_S8_BUTTON: | ||
| 318 | case SM5502_MUIC_ADC_REMOTE_S9_BUTTON: | ||
| 319 | case SM5502_MUIC_ADC_REMOTE_S10_BUTTON: | ||
| 320 | case SM5502_MUIC_ADC_REMOTE_S11_BUTTON: | ||
| 321 | case SM5502_MUIC_ADC_REMOTE_S12_BUTTON: | ||
| 322 | case SM5502_MUIC_ADC_RESERVED_ACC_1: | ||
| 323 | case SM5502_MUIC_ADC_RESERVED_ACC_2: | ||
| 324 | case SM5502_MUIC_ADC_RESERVED_ACC_3: | ||
| 325 | case SM5502_MUIC_ADC_RESERVED_ACC_4: | ||
| 326 | case SM5502_MUIC_ADC_RESERVED_ACC_5: | ||
| 327 | case SM5502_MUIC_ADC_AUDIO_TYPE2: | ||
| 328 | case SM5502_MUIC_ADC_PHONE_POWERED_DEV: | ||
| 329 | case SM5502_MUIC_ADC_TTY_CONVERTER: | ||
| 330 | case SM5502_MUIC_ADC_UART_CABLE: | ||
| 331 | case SM5502_MUIC_ADC_TYPE1_CHARGER: | ||
| 332 | case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_USB: | ||
| 333 | case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_USB: | ||
| 334 | case SM5502_MUIC_ADC_AUDIO_VIDEO_CABLE: | ||
| 335 | case SM5502_MUIC_ADC_TYPE2_CHARGER: | ||
| 336 | case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_OFF_UART: | ||
| 337 | case SM5502_MUIC_ADC_FACTORY_MODE_BOOT_ON_UART: | ||
| 338 | break; | ||
| 339 | case SM5502_MUIC_ADC_AUDIO_TYPE1: | ||
| 340 | /* | ||
| 341 | * Check whether cable type is | ||
| 342 | * SM5502_MUIC_ADC_AUDIO_TYPE1_FULL_REMOTE | ||
| 343 | * or SM5502_MUIC_ADC_AUDIO_TYPE1_SEND_END | ||
| 344 | * by using Button event. | ||
| 345 | */ | ||
| 346 | break; | ||
| 347 | case SM5502_MUIC_ADC_OPEN: | ||
| 348 | ret = regmap_read(info->regmap, SM5502_REG_DEV_TYPE1, | ||
| 349 | &dev_type1); | ||
| 350 | if (ret) { | ||
| 351 | dev_err(info->dev, "failed to read DEV_TYPE1 reg\n"); | ||
| 352 | return ret; | ||
| 353 | } | ||
| 354 | |||
| 355 | switch (dev_type1) { | ||
| 356 | case SM5502_REG_DEV_TYPE1_USB_SDP_MASK: | ||
| 357 | cable_type = SM5502_MUIC_ADC_OPEN_USB; | ||
| 358 | break; | ||
| 359 | case SM5502_REG_DEV_TYPE1_DEDICATED_CHG_MASK: | ||
| 360 | cable_type = SM5502_MUIC_ADC_OPEN_TA; | ||
| 361 | break; | ||
| 362 | case SM5502_REG_DEV_TYPE1_USB_OTG_MASK: | ||
| 363 | cable_type = SM5502_MUIC_ADC_OPEN_USB_OTG; | ||
| 364 | break; | ||
| 365 | default: | ||
| 366 | dev_dbg(info->dev, | ||
| 367 | "cannot identify the cable type: adc(0x%x) " | ||
| 368 | "dev_type1(0x%x)\n", adc, dev_type1); | ||
| 369 | return -EINVAL; | ||
| 370 | }; | ||
| 371 | break; | ||
| 372 | default: | ||
| 373 | dev_err(info->dev, | ||
| 374 | "failed to identify the cable type: adc(0x%x)\n", adc); | ||
| 375 | return -EINVAL; | ||
| 376 | }; | ||
| 377 | |||
| 378 | return cable_type; | ||
| 379 | } | ||
| 380 | |||
| 381 | static int sm5502_muic_cable_handler(struct sm5502_muic_info *info, | ||
| 382 | bool attached) | ||
| 383 | { | ||
| 384 | static unsigned int prev_cable_type = SM5502_MUIC_ADC_GROUND; | ||
| 385 | const char **cable_names = info->edev->supported_cable; | ||
| 386 | unsigned int cable_type = SM5502_MUIC_ADC_GROUND; | ||
| 387 | unsigned int con_sw = DM_DP_SWITCH_OPEN; | ||
| 388 | unsigned int vbus_sw = VBUSIN_SWITCH_OPEN; | ||
| 389 | unsigned int idx = 0; | ||
| 390 | int ret; | ||
| 391 | |||
| 392 | if (!cable_names) | ||
| 393 | return 0; | ||
| 394 | |||
| 395 | /* Get the type of attached or detached cable */ | ||
| 396 | if (attached) | ||
| 397 | cable_type = sm5502_muic_get_cable_type(info); | ||
| 398 | else if (!attached) | ||
| 399 | cable_type = prev_cable_type; | ||
| 400 | prev_cable_type = cable_type; | ||
| 401 | |||
| 402 | switch (cable_type) { | ||
| 403 | case SM5502_MUIC_ADC_OPEN_USB: | ||
| 404 | idx = EXTCON_CABLE_USB; | ||
| 405 | con_sw = DM_DP_SWITCH_USB; | ||
| 406 | vbus_sw = VBUSIN_SWITCH_VBUSOUT_WITH_USB; | ||
| 407 | break; | ||
| 408 | case SM5502_MUIC_ADC_OPEN_TA: | ||
| 409 | idx = EXTCON_CABLE_TA; | ||
| 410 | con_sw = DM_DP_SWITCH_OPEN; | ||
| 411 | vbus_sw = VBUSIN_SWITCH_VBUSOUT; | ||
| 412 | break; | ||
| 413 | case SM5502_MUIC_ADC_OPEN_USB_OTG: | ||
| 414 | idx = EXTCON_CABLE_USB_HOST; | ||
| 415 | con_sw = DM_DP_SWITCH_USB; | ||
| 416 | vbus_sw = VBUSIN_SWITCH_OPEN; | ||
| 417 | break; | ||
| 418 | default: | ||
| 419 | dev_dbg(info->dev, | ||
| 420 | "cannot handle this cable_type (0x%x)\n", cable_type); | ||
| 421 | return 0; | ||
| 422 | }; | ||
| 423 | |||
| 424 | /* Change internal hardware path(DM_CON/DP_CON, VBUSIN) */ | ||
| 425 | ret = sm5502_muic_set_path(info, con_sw, vbus_sw, attached); | ||
| 426 | if (ret < 0) | ||
| 427 | return ret; | ||
| 428 | |||
| 429 | /* Change the state of external accessory */ | ||
| 430 | extcon_set_cable_state(info->edev, cable_names[idx], attached); | ||
| 431 | |||
| 432 | return 0; | ||
| 433 | } | ||
| 434 | |||
| 435 | static void sm5502_muic_irq_work(struct work_struct *work) | ||
| 436 | { | ||
| 437 | struct sm5502_muic_info *info = container_of(work, | ||
| 438 | struct sm5502_muic_info, irq_work); | ||
| 439 | int ret = 0; | ||
| 440 | |||
| 441 | if (!info->edev) | ||
| 442 | return; | ||
| 443 | |||
| 444 | mutex_lock(&info->mutex); | ||
| 445 | |||
| 446 | /* Detect attached or detached cables */ | ||
| 447 | if (info->irq_attach) { | ||
| 448 | ret = sm5502_muic_cable_handler(info, true); | ||
| 449 | info->irq_attach = false; | ||
| 450 | } | ||
| 451 | if (info->irq_detach) { | ||
| 452 | ret = sm5502_muic_cable_handler(info, false); | ||
| 453 | info->irq_detach = false; | ||
| 454 | } | ||
| 455 | |||
| 456 | if (ret < 0) | ||
| 457 | dev_err(info->dev, "failed to handle MUIC interrupt\n"); | ||
| 458 | |||
| 459 | mutex_unlock(&info->mutex); | ||
| 460 | |||
| 461 | return; | ||
| 462 | } | ||
| 463 | |||
| 464 | /* | ||
| 465 | * Sets irq_attach or irq_detach in sm5502_muic_info and returns 0. | ||
| 466 | * Returns -ESRCH if irq_type does not match registered IRQ for this dev type. | ||
| 467 | */ | ||
| 468 | static int sm5502_parse_irq(struct sm5502_muic_info *info, int irq_type) | ||
| 469 | { | ||
| 470 | switch (irq_type) { | ||
| 471 | case SM5502_IRQ_INT1_ATTACH: | ||
| 472 | info->irq_attach = true; | ||
| 473 | break; | ||
| 474 | case SM5502_IRQ_INT1_DETACH: | ||
| 475 | info->irq_detach = true; | ||
| 476 | break; | ||
| 477 | case SM5502_IRQ_INT1_KP: | ||
| 478 | case SM5502_IRQ_INT1_LKP: | ||
| 479 | case SM5502_IRQ_INT1_LKR: | ||
| 480 | case SM5502_IRQ_INT1_OVP_EVENT: | ||
| 481 | case SM5502_IRQ_INT1_OCP_EVENT: | ||
| 482 | case SM5502_IRQ_INT1_OVP_OCP_DIS: | ||
| 483 | case SM5502_IRQ_INT2_VBUS_DET: | ||
| 484 | case SM5502_IRQ_INT2_REV_ACCE: | ||
| 485 | case SM5502_IRQ_INT2_ADC_CHG: | ||
| 486 | case SM5502_IRQ_INT2_STUCK_KEY: | ||
| 487 | case SM5502_IRQ_INT2_STUCK_KEY_RCV: | ||
| 488 | case SM5502_IRQ_INT2_MHL: | ||
| 489 | default: | ||
| 490 | break; | ||
| 491 | } | ||
| 492 | |||
| 493 | return 0; | ||
| 494 | } | ||
| 495 | |||
| 496 | static irqreturn_t sm5502_muic_irq_handler(int irq, void *data) | ||
| 497 | { | ||
| 498 | struct sm5502_muic_info *info = data; | ||
| 499 | int i, irq_type = -1, ret; | ||
| 500 | |||
| 501 | for (i = 0; i < info->num_muic_irqs; i++) | ||
| 502 | if (irq == info->muic_irqs[i].virq) | ||
| 503 | irq_type = info->muic_irqs[i].irq; | ||
| 504 | |||
| 505 | ret = sm5502_parse_irq(info, irq_type); | ||
| 506 | if (ret < 0) { | ||
| 507 | dev_warn(info->dev, "cannot handle is interrupt:%d\n", | ||
| 508 | irq_type); | ||
| 509 | return IRQ_HANDLED; | ||
| 510 | } | ||
| 511 | schedule_work(&info->irq_work); | ||
| 512 | |||
| 513 | return IRQ_HANDLED; | ||
| 514 | } | ||
| 515 | |||
| 516 | static void sm5502_muic_detect_cable_wq(struct work_struct *work) | ||
| 517 | { | ||
| 518 | struct sm5502_muic_info *info = container_of(to_delayed_work(work), | ||
| 519 | struct sm5502_muic_info, wq_detcable); | ||
| 520 | int ret; | ||
| 521 | |||
| 522 | /* Notify the state of connector cable or not */ | ||
| 523 | ret = sm5502_muic_cable_handler(info, true); | ||
| 524 | if (ret < 0) | ||
| 525 | dev_warn(info->dev, "failed to detect cable state\n"); | ||
| 526 | } | ||
| 527 | |||
| 528 | static void sm5502_init_dev_type(struct sm5502_muic_info *info) | ||
| 529 | { | ||
| 530 | unsigned int reg_data, vendor_id, version_id; | ||
| 531 | int i, ret; | ||
| 532 | |||
| 533 | /* To test I2C, Print version_id and vendor_id of SM5502 */ | ||
| 534 | ret = regmap_read(info->regmap, SM5502_REG_DEVICE_ID, ®_data); | ||
| 535 | if (ret) { | ||
| 536 | dev_err(info->dev, | ||
| 537 | "failed to read DEVICE_ID register: %d\n", ret); | ||
| 538 | return; | ||
| 539 | } | ||
| 540 | |||
| 541 | vendor_id = ((reg_data & SM5502_REG_DEVICE_ID_VENDOR_MASK) >> | ||
| 542 | SM5502_REG_DEVICE_ID_VENDOR_SHIFT); | ||
| 543 | version_id = ((reg_data & SM5502_REG_DEVICE_ID_VERSION_MASK) >> | ||
| 544 | SM5502_REG_DEVICE_ID_VERSION_SHIFT); | ||
| 545 | |||
| 546 | dev_info(info->dev, "Device type: version: 0x%x, vendor: 0x%x\n", | ||
| 547 | version_id, vendor_id); | ||
| 548 | |||
| 549 | /* Initiazle the register of SM5502 device to bring-up */ | ||
| 550 | for (i = 0; i < info->num_reg_data; i++) { | ||
| 551 | unsigned int val = 0; | ||
| 552 | |||
| 553 | if (!info->reg_data[i].invert) | ||
| 554 | val |= ~info->reg_data[i].val; | ||
| 555 | else | ||
| 556 | val = info->reg_data[i].val; | ||
| 557 | regmap_write(info->regmap, info->reg_data[i].reg, val); | ||
| 558 | } | ||
| 559 | } | ||
| 560 | |||
| 561 | static int sm5022_muic_i2c_probe(struct i2c_client *i2c, | ||
| 562 | const struct i2c_device_id *id) | ||
| 563 | { | ||
| 564 | struct device_node *np = i2c->dev.of_node; | ||
| 565 | struct sm5502_muic_info *info; | ||
| 566 | int i, ret, irq_flags; | ||
| 567 | |||
| 568 | if (!np) | ||
| 569 | return -EINVAL; | ||
| 570 | |||
| 571 | info = devm_kzalloc(&i2c->dev, sizeof(*info), GFP_KERNEL); | ||
| 572 | if (!info) | ||
| 573 | return -ENOMEM; | ||
| 574 | i2c_set_clientdata(i2c, info); | ||
| 575 | |||
| 576 | info->dev = &i2c->dev; | ||
| 577 | info->i2c = i2c; | ||
| 578 | info->irq = i2c->irq; | ||
| 579 | info->muic_irqs = sm5502_muic_irqs; | ||
| 580 | info->num_muic_irqs = ARRAY_SIZE(sm5502_muic_irqs); | ||
| 581 | info->reg_data = sm5502_reg_data; | ||
| 582 | info->num_reg_data = ARRAY_SIZE(sm5502_reg_data); | ||
| 583 | |||
| 584 | mutex_init(&info->mutex); | ||
| 585 | |||
| 586 | INIT_WORK(&info->irq_work, sm5502_muic_irq_work); | ||
| 587 | |||
| 588 | info->regmap = devm_regmap_init_i2c(i2c, &sm5502_muic_regmap_config); | ||
| 589 | if (IS_ERR(info->regmap)) { | ||
| 590 | ret = PTR_ERR(info->regmap); | ||
| 591 | dev_err(info->dev, "failed to allocate register map: %d\n", | ||
| 592 | ret); | ||
| 593 | return ret; | ||
| 594 | } | ||
| 595 | |||
| 596 | /* Support irq domain for SM5502 MUIC device */ | ||
| 597 | irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED; | ||
| 598 | ret = regmap_add_irq_chip(info->regmap, info->irq, irq_flags, 0, | ||
| 599 | &sm5502_muic_irq_chip, &info->irq_data); | ||
| 600 | if (ret != 0) { | ||
| 601 | dev_err(info->dev, "failed to request IRQ %d: %d\n", | ||
| 602 | info->irq, ret); | ||
| 603 | return ret; | ||
| 604 | } | ||
| 605 | |||
| 606 | for (i = 0; i < info->num_muic_irqs; i++) { | ||
| 607 | struct muic_irq *muic_irq = &info->muic_irqs[i]; | ||
| 608 | unsigned int virq = 0; | ||
| 609 | |||
| 610 | virq = regmap_irq_get_virq(info->irq_data, muic_irq->irq); | ||
| 611 | if (virq <= 0) | ||
| 612 | return -EINVAL; | ||
| 613 | muic_irq->virq = virq; | ||
| 614 | |||
| 615 | ret = devm_request_threaded_irq(info->dev, virq, NULL, | ||
| 616 | sm5502_muic_irq_handler, | ||
| 617 | IRQF_NO_SUSPEND, | ||
| 618 | muic_irq->name, info); | ||
| 619 | if (ret) { | ||
| 620 | dev_err(info->dev, "failed: irq request (IRQ: %d," | ||
| 621 | " error :%d)\n", muic_irq->irq, ret); | ||
| 622 | return ret; | ||
| 623 | } | ||
| 624 | } | ||
| 625 | |||
| 626 | /* Allocate extcon device */ | ||
| 627 | info->edev = devm_extcon_dev_allocate(info->dev, sm5502_extcon_cable); | ||
| 628 | if (IS_ERR(info->edev)) { | ||
| 629 | dev_err(info->dev, "failed to allocate memory for extcon\n"); | ||
| 630 | return -ENOMEM; | ||
| 631 | } | ||
| 632 | info->edev->name = np->name; | ||
| 633 | |||
| 634 | /* Register extcon device */ | ||
| 635 | ret = devm_extcon_dev_register(info->dev, info->edev); | ||
| 636 | if (ret) { | ||
| 637 | dev_err(info->dev, "failed to register extcon device\n"); | ||
| 638 | return ret; | ||
| 639 | } | ||
| 640 | |||
| 641 | /* | ||
| 642 | * Detect accessory after completing the initialization of platform | ||
| 643 | * | ||
| 644 | * - Use delayed workqueue to detect cable state and then | ||
| 645 | * notify cable state to notifiee/platform through uevent. | ||
| 646 | * After completing the booting of platform, the extcon provider | ||
| 647 | * driver should notify cable state to upper layer. | ||
| 648 | */ | ||
| 649 | INIT_DELAYED_WORK(&info->wq_detcable, sm5502_muic_detect_cable_wq); | ||
| 650 | queue_delayed_work(system_power_efficient_wq, &info->wq_detcable, | ||
| 651 | msecs_to_jiffies(DELAY_MS_DEFAULT)); | ||
| 652 | |||
| 653 | /* Initialize SM5502 device and print vendor id and version id */ | ||
| 654 | sm5502_init_dev_type(info); | ||
| 655 | |||
| 656 | return 0; | ||
| 657 | } | ||
| 658 | |||
| 659 | static int sm5502_muic_i2c_remove(struct i2c_client *i2c) | ||
| 660 | { | ||
| 661 | struct sm5502_muic_info *info = i2c_get_clientdata(i2c); | ||
| 662 | |||
| 663 | regmap_del_irq_chip(info->irq, info->irq_data); | ||
| 664 | |||
| 665 | return 0; | ||
| 666 | } | ||
| 667 | |||
| 668 | static struct of_device_id sm5502_dt_match[] = { | ||
| 669 | { .compatible = "siliconmitus,sm5502-muic" }, | ||
| 670 | { }, | ||
| 671 | }; | ||
| 672 | |||
| 673 | #ifdef CONFIG_PM_SLEEP | ||
| 674 | static int sm5502_muic_suspend(struct device *dev) | ||
| 675 | { | ||
| 676 | struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); | ||
| 677 | struct sm5502_muic_info *info = i2c_get_clientdata(i2c); | ||
| 678 | |||
| 679 | enable_irq_wake(info->irq); | ||
| 680 | |||
| 681 | return 0; | ||
| 682 | } | ||
| 683 | |||
| 684 | static int sm5502_muic_resume(struct device *dev) | ||
| 685 | { | ||
| 686 | struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); | ||
| 687 | struct sm5502_muic_info *info = i2c_get_clientdata(i2c); | ||
| 688 | |||
| 689 | disable_irq_wake(info->irq); | ||
| 690 | |||
| 691 | return 0; | ||
| 692 | } | ||
| 693 | #endif | ||
| 694 | |||
| 695 | static SIMPLE_DEV_PM_OPS(sm5502_muic_pm_ops, | ||
| 696 | sm5502_muic_suspend, sm5502_muic_resume); | ||
| 697 | |||
| 698 | static const struct i2c_device_id sm5502_i2c_id[] = { | ||
| 699 | { "sm5502", TYPE_SM5502 }, | ||
| 700 | { } | ||
| 701 | }; | ||
| 702 | MODULE_DEVICE_TABLE(i2c, sm5502_i2c_id); | ||
| 703 | |||
| 704 | static struct i2c_driver sm5502_muic_i2c_driver = { | ||
| 705 | .driver = { | ||
| 706 | .name = "sm5502", | ||
| 707 | .owner = THIS_MODULE, | ||
| 708 | .pm = &sm5502_muic_pm_ops, | ||
| 709 | .of_match_table = sm5502_dt_match, | ||
| 710 | }, | ||
| 711 | .probe = sm5022_muic_i2c_probe, | ||
| 712 | .remove = sm5502_muic_i2c_remove, | ||
| 713 | .id_table = sm5502_i2c_id, | ||
| 714 | }; | ||
| 715 | |||
| 716 | static int __init sm5502_muic_i2c_init(void) | ||
| 717 | { | ||
| 718 | return i2c_add_driver(&sm5502_muic_i2c_driver); | ||
| 719 | } | ||
| 720 | subsys_initcall(sm5502_muic_i2c_init); | ||
| 721 | |||
| 722 | MODULE_DESCRIPTION("Silicon Mitus SM5502 Extcon driver"); | ||
| 723 | MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); | ||
| 724 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 284cf66489f4..531a593912ec 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c | |||
| @@ -808,12 +808,8 @@ int vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer, | |||
| 808 | 808 | ||
| 809 | *buffer_actual_len = packetlen; | 809 | *buffer_actual_len = packetlen; |
| 810 | 810 | ||
| 811 | if (packetlen > bufferlen) { | 811 | if (packetlen > bufferlen) |
| 812 | pr_err("Buffer too small - needed %d bytes but " | ||
| 813 | "got space for only %d bytes\n", | ||
| 814 | packetlen, bufferlen); | ||
| 815 | return -ENOBUFS; | 812 | return -ENOBUFS; |
| 816 | } | ||
| 817 | 813 | ||
| 818 | *requestid = desc.trans_id; | 814 | *requestid = desc.trans_id; |
| 819 | 815 | ||
diff --git a/drivers/ipack/carriers/tpci200.c b/drivers/ipack/carriers/tpci200.c index c276fde318e5..de5e32151a1e 100644 --- a/drivers/ipack/carriers/tpci200.c +++ b/drivers/ipack/carriers/tpci200.c | |||
| @@ -618,7 +618,7 @@ static void tpci200_pci_remove(struct pci_dev *dev) | |||
| 618 | __tpci200_pci_remove(tpci200); | 618 | __tpci200_pci_remove(tpci200); |
| 619 | } | 619 | } |
| 620 | 620 | ||
| 621 | static DEFINE_PCI_DEVICE_TABLE(tpci200_idtable) = { | 621 | static const struct pci_device_id tpci200_idtable[] = { |
| 622 | { TPCI200_VENDOR_ID, TPCI200_DEVICE_ID, TPCI200_SUBVENDOR_ID, | 622 | { TPCI200_VENDOR_ID, TPCI200_DEVICE_ID, TPCI200_SUBVENDOR_ID, |
| 623 | TPCI200_SUBDEVICE_ID }, | 623 | TPCI200_SUBDEVICE_ID }, |
| 624 | { 0, }, | 624 | { 0, }, |
diff --git a/drivers/ipack/devices/ipoctal.c b/drivers/ipack/devices/ipoctal.c index 141094e7c06e..e41bef048c23 100644 --- a/drivers/ipack/devices/ipoctal.c +++ b/drivers/ipack/devices/ipoctal.c | |||
| @@ -177,19 +177,20 @@ static void ipoctal_irq_tx(struct ipoctal_channel *channel) | |||
| 177 | if (channel->nb_bytes == 0) | 177 | if (channel->nb_bytes == 0) |
| 178 | return; | 178 | return; |
| 179 | 179 | ||
| 180 | spin_lock(&channel->lock); | ||
| 180 | value = channel->tty_port.xmit_buf[*pointer_write]; | 181 | value = channel->tty_port.xmit_buf[*pointer_write]; |
| 181 | iowrite8(value, &channel->regs->w.thr); | 182 | iowrite8(value, &channel->regs->w.thr); |
| 182 | channel->stats.tx++; | 183 | channel->stats.tx++; |
| 183 | (*pointer_write)++; | 184 | (*pointer_write)++; |
| 184 | *pointer_write = *pointer_write % PAGE_SIZE; | 185 | *pointer_write = *pointer_write % PAGE_SIZE; |
| 185 | channel->nb_bytes--; | 186 | channel->nb_bytes--; |
| 187 | spin_unlock(&channel->lock); | ||
| 186 | } | 188 | } |
| 187 | 189 | ||
| 188 | static void ipoctal_irq_channel(struct ipoctal_channel *channel) | 190 | static void ipoctal_irq_channel(struct ipoctal_channel *channel) |
| 189 | { | 191 | { |
| 190 | u8 isr, sr; | 192 | u8 isr, sr; |
| 191 | 193 | ||
| 192 | spin_lock(&channel->lock); | ||
| 193 | /* The HW is organized in pair of channels. See which register we need | 194 | /* The HW is organized in pair of channels. See which register we need |
| 194 | * to read from */ | 195 | * to read from */ |
| 195 | isr = ioread8(&channel->block_regs->r.isr); | 196 | isr = ioread8(&channel->block_regs->r.isr); |
| @@ -213,8 +214,6 @@ static void ipoctal_irq_channel(struct ipoctal_channel *channel) | |||
| 213 | /* TX of each character */ | 214 | /* TX of each character */ |
| 214 | if ((isr & channel->isr_tx_rdy_mask) && (sr & SR_TX_READY)) | 215 | if ((isr & channel->isr_tx_rdy_mask) && (sr & SR_TX_READY)) |
| 215 | ipoctal_irq_tx(channel); | 216 | ipoctal_irq_tx(channel); |
| 216 | |||
| 217 | spin_unlock(&channel->lock); | ||
| 218 | } | 217 | } |
| 219 | 218 | ||
| 220 | static irqreturn_t ipoctal_irq_handler(void *arg) | 219 | static irqreturn_t ipoctal_irq_handler(void *arg) |
| @@ -324,13 +323,6 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr, | |||
| 324 | &block_regs[i].w.imr); | 323 | &block_regs[i].w.imr); |
| 325 | } | 324 | } |
| 326 | 325 | ||
| 327 | /* | ||
| 328 | * IP-OCTAL has different addresses to copy its IRQ vector. | ||
| 329 | * Depending of the carrier these addresses are accesible or not. | ||
| 330 | * More info in the datasheet. | ||
| 331 | */ | ||
| 332 | ipoctal->dev->bus->ops->request_irq(ipoctal->dev, | ||
| 333 | ipoctal_irq_handler, ipoctal); | ||
| 334 | /* Dummy write */ | 326 | /* Dummy write */ |
| 335 | iowrite8(1, ipoctal->mem8_space + 1); | 327 | iowrite8(1, ipoctal->mem8_space + 1); |
| 336 | 328 | ||
| @@ -391,6 +383,14 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr, | |||
| 391 | dev_set_drvdata(tty_dev, channel); | 383 | dev_set_drvdata(tty_dev, channel); |
| 392 | } | 384 | } |
| 393 | 385 | ||
| 386 | /* | ||
| 387 | * IP-OCTAL has different addresses to copy its IRQ vector. | ||
| 388 | * Depending of the carrier these addresses are accesible or not. | ||
| 389 | * More info in the datasheet. | ||
| 390 | */ | ||
| 391 | ipoctal->dev->bus->ops->request_irq(ipoctal->dev, | ||
| 392 | ipoctal_irq_handler, ipoctal); | ||
| 393 | |||
| 394 | return 0; | 394 | return 0; |
| 395 | } | 395 | } |
| 396 | 396 | ||
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 6cc4b6acc22a..fb824f501197 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig | |||
| @@ -384,6 +384,7 @@ config MFD_MAX77693 | |||
| 384 | depends on I2C=y | 384 | depends on I2C=y |
| 385 | select MFD_CORE | 385 | select MFD_CORE |
| 386 | select REGMAP_I2C | 386 | select REGMAP_I2C |
| 387 | select REGMAP_IRQ | ||
| 387 | help | 388 | help |
| 388 | Say yes here to add support for Maxim Semiconductor MAX77693. | 389 | Say yes here to add support for Maxim Semiconductor MAX77693. |
| 389 | This is a companion Power Management IC with Flash, Haptic, Charger, | 390 | This is a companion Power Management IC with Flash, Haptic, Charger, |
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 8afedba535c7..8c6e7bba4660 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile | |||
| @@ -116,7 +116,7 @@ obj-$(CONFIG_MFD_DA9063) += da9063.o | |||
| 116 | 116 | ||
| 117 | obj-$(CONFIG_MFD_MAX14577) += max14577.o | 117 | obj-$(CONFIG_MFD_MAX14577) += max14577.o |
| 118 | obj-$(CONFIG_MFD_MAX77686) += max77686.o max77686-irq.o | 118 | obj-$(CONFIG_MFD_MAX77686) += max77686.o max77686-irq.o |
| 119 | obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o | 119 | obj-$(CONFIG_MFD_MAX77693) += max77693.o |
| 120 | obj-$(CONFIG_MFD_MAX8907) += max8907.o | 120 | obj-$(CONFIG_MFD_MAX8907) += max8907.o |
| 121 | max8925-objs := max8925-core.o max8925-i2c.o | 121 | max8925-objs := max8925-core.o max8925-i2c.o |
| 122 | obj-$(CONFIG_MFD_MAX8925) += max8925.o | 122 | obj-$(CONFIG_MFD_MAX8925) += max8925.o |
diff --git a/drivers/mfd/max77693-irq.c b/drivers/mfd/max77693-irq.c deleted file mode 100644 index 66b58fe77094..000000000000 --- a/drivers/mfd/max77693-irq.c +++ /dev/null | |||
| @@ -1,336 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * max77693-irq.c - Interrupt controller support for MAX77693 | ||
| 3 | * | ||
| 4 | * Copyright (C) 2012 Samsung Electronics Co.Ltd | ||
| 5 | * SangYoung Son <hello.son@samsung.com> | ||
| 6 | * | ||
| 7 | * This program is not provided / owned by Maxim Integrated Products. | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License as published by | ||
| 11 | * the Free Software Foundation; either version 2 of the License, or | ||
| 12 | * (at your option) any later version. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, | ||
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 17 | * GNU General Public License for more details. | ||
| 18 | * | ||
| 19 | * You should have received a copy of the GNU General Public License | ||
| 20 | * along with this program; if not, write to the Free Software | ||
| 21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 22 | * | ||
| 23 | * This driver is based on max8997-irq.c | ||
| 24 | */ | ||
| 25 | |||
| 26 | #include <linux/err.h> | ||
| 27 | #include <linux/irq.h> | ||
| 28 | #include <linux/interrupt.h> | ||
| 29 | #include <linux/module.h> | ||
| 30 | #include <linux/irqdomain.h> | ||
| 31 | #include <linux/mfd/max77693.h> | ||
| 32 | #include <linux/mfd/max77693-private.h> | ||
| 33 | |||
| 34 | static const u8 max77693_mask_reg[] = { | ||
| 35 | [LED_INT] = MAX77693_LED_REG_FLASH_INT_MASK, | ||
| 36 | [TOPSYS_INT] = MAX77693_PMIC_REG_TOPSYS_INT_MASK, | ||
| 37 | [CHG_INT] = MAX77693_CHG_REG_CHG_INT_MASK, | ||
| 38 | [MUIC_INT1] = MAX77693_MUIC_REG_INTMASK1, | ||
| 39 | [MUIC_INT2] = MAX77693_MUIC_REG_INTMASK2, | ||
| 40 | [MUIC_INT3] = MAX77693_MUIC_REG_INTMASK3, | ||
| 41 | }; | ||
| 42 | |||
| 43 | static struct regmap *max77693_get_regmap(struct max77693_dev *max77693, | ||
| 44 | enum max77693_irq_source src) | ||
| 45 | { | ||
| 46 | switch (src) { | ||
| 47 | case LED_INT ... CHG_INT: | ||
| 48 | return max77693->regmap; | ||
| 49 | case MUIC_INT1 ... MUIC_INT3: | ||
| 50 | return max77693->regmap_muic; | ||
| 51 | default: | ||
| 52 | return ERR_PTR(-EINVAL); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | struct max77693_irq_data { | ||
| 57 | int mask; | ||
| 58 | enum max77693_irq_source group; | ||
| 59 | }; | ||
| 60 | |||
| 61 | #define DECLARE_IRQ(idx, _group, _mask) \ | ||
| 62 | [(idx)] = { .group = (_group), .mask = (_mask) } | ||
| 63 | static const struct max77693_irq_data max77693_irqs[] = { | ||
| 64 | DECLARE_IRQ(MAX77693_LED_IRQ_FLED2_OPEN, LED_INT, 1 << 0), | ||
| 65 | DECLARE_IRQ(MAX77693_LED_IRQ_FLED2_SHORT, LED_INT, 1 << 1), | ||
| 66 | DECLARE_IRQ(MAX77693_LED_IRQ_FLED1_OPEN, LED_INT, 1 << 2), | ||
| 67 | DECLARE_IRQ(MAX77693_LED_IRQ_FLED1_SHORT, LED_INT, 1 << 3), | ||
| 68 | DECLARE_IRQ(MAX77693_LED_IRQ_MAX_FLASH, LED_INT, 1 << 4), | ||
| 69 | |||
| 70 | DECLARE_IRQ(MAX77693_TOPSYS_IRQ_T120C_INT, TOPSYS_INT, 1 << 0), | ||
| 71 | DECLARE_IRQ(MAX77693_TOPSYS_IRQ_T140C_INT, TOPSYS_INT, 1 << 1), | ||
| 72 | DECLARE_IRQ(MAX77693_TOPSYS_IRQ_LOWSYS_INT, TOPSYS_INT, 1 << 3), | ||
| 73 | |||
| 74 | DECLARE_IRQ(MAX77693_CHG_IRQ_BYP_I, CHG_INT, 1 << 0), | ||
| 75 | DECLARE_IRQ(MAX77693_CHG_IRQ_THM_I, CHG_INT, 1 << 2), | ||
| 76 | DECLARE_IRQ(MAX77693_CHG_IRQ_BAT_I, CHG_INT, 1 << 3), | ||
| 77 | DECLARE_IRQ(MAX77693_CHG_IRQ_CHG_I, CHG_INT, 1 << 4), | ||
| 78 | DECLARE_IRQ(MAX77693_CHG_IRQ_CHGIN_I, CHG_INT, 1 << 6), | ||
| 79 | |||
| 80 | DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADC, MUIC_INT1, 1 << 0), | ||
| 81 | DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADC_LOW, MUIC_INT1, 1 << 1), | ||
| 82 | DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADC_ERR, MUIC_INT1, 1 << 2), | ||
| 83 | DECLARE_IRQ(MAX77693_MUIC_IRQ_INT1_ADC1K, MUIC_INT1, 1 << 3), | ||
| 84 | |||
| 85 | DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_CHGTYP, MUIC_INT2, 1 << 0), | ||
| 86 | DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_CHGDETREUN, MUIC_INT2, 1 << 1), | ||
| 87 | DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_DCDTMR, MUIC_INT2, 1 << 2), | ||
| 88 | DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_DXOVP, MUIC_INT2, 1 << 3), | ||
| 89 | DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_VBVOLT, MUIC_INT2, 1 << 4), | ||
| 90 | DECLARE_IRQ(MAX77693_MUIC_IRQ_INT2_VIDRM, MUIC_INT2, 1 << 5), | ||
| 91 | |||
| 92 | DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_EOC, MUIC_INT3, 1 << 0), | ||
| 93 | DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_CGMBC, MUIC_INT3, 1 << 1), | ||
| 94 | DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_OVP, MUIC_INT3, 1 << 2), | ||
| 95 | DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_MBCCHG_ERR, MUIC_INT3, 1 << 3), | ||
| 96 | DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_CHG_ENABLED, MUIC_INT3, 1 << 4), | ||
| 97 | DECLARE_IRQ(MAX77693_MUIC_IRQ_INT3_BAT_DET, MUIC_INT3, 1 << 5), | ||
| 98 | }; | ||
| 99 | |||
| 100 | static void max77693_irq_lock(struct irq_data *data) | ||
| 101 | { | ||
| 102 | struct max77693_dev *max77693 = irq_get_chip_data(data->irq); | ||
| 103 | |||
| 104 | mutex_lock(&max77693->irqlock); | ||
| 105 | } | ||
| 106 | |||
| 107 | static void max77693_irq_sync_unlock(struct irq_data *data) | ||
| 108 | { | ||
| 109 | struct max77693_dev *max77693 = irq_get_chip_data(data->irq); | ||
| 110 | int i; | ||
| 111 | |||
| 112 | for (i = 0; i < MAX77693_IRQ_GROUP_NR; i++) { | ||
| 113 | u8 mask_reg = max77693_mask_reg[i]; | ||
| 114 | struct regmap *map = max77693_get_regmap(max77693, i); | ||
| 115 | |||
| 116 | if (mask_reg == MAX77693_REG_INVALID || | ||
| 117 | IS_ERR_OR_NULL(map)) | ||
| 118 | continue; | ||
| 119 | max77693->irq_masks_cache[i] = max77693->irq_masks_cur[i]; | ||
| 120 | |||
| 121 | max77693_write_reg(map, max77693_mask_reg[i], | ||
| 122 | max77693->irq_masks_cur[i]); | ||
| 123 | } | ||
| 124 | |||
| 125 | mutex_unlock(&max77693->irqlock); | ||
| 126 | } | ||
| 127 | |||
| 128 | static const inline struct max77693_irq_data * | ||
| 129 | irq_to_max77693_irq(struct max77693_dev *max77693, int irq) | ||
| 130 | { | ||
| 131 | struct irq_data *data = irq_get_irq_data(irq); | ||
| 132 | return &max77693_irqs[data->hwirq]; | ||
| 133 | } | ||
| 134 | |||
| 135 | static void max77693_irq_mask(struct irq_data *data) | ||
| 136 | { | ||
| 137 | struct max77693_dev *max77693 = irq_get_chip_data(data->irq); | ||
| 138 | const struct max77693_irq_data *irq_data = | ||
| 139 | irq_to_max77693_irq(max77693, data->irq); | ||
| 140 | |||
| 141 | if (irq_data->group >= MAX77693_IRQ_GROUP_NR) | ||
| 142 | return; | ||
| 143 | |||
| 144 | if (irq_data->group >= MUIC_INT1 && irq_data->group <= MUIC_INT3) | ||
| 145 | max77693->irq_masks_cur[irq_data->group] &= ~irq_data->mask; | ||
| 146 | else | ||
| 147 | max77693->irq_masks_cur[irq_data->group] |= irq_data->mask; | ||
| 148 | } | ||
| 149 | |||
| 150 | static void max77693_irq_unmask(struct irq_data *data) | ||
| 151 | { | ||
| 152 | struct max77693_dev *max77693 = irq_get_chip_data(data->irq); | ||
| 153 | const struct max77693_irq_data *irq_data = | ||
| 154 | irq_to_max77693_irq(max77693, data->irq); | ||
| 155 | |||
| 156 | if (irq_data->group >= MAX77693_IRQ_GROUP_NR) | ||
| 157 | return; | ||
| 158 | |||
| 159 | if (irq_data->group >= MUIC_INT1 && irq_data->group <= MUIC_INT3) | ||
| 160 | max77693->irq_masks_cur[irq_data->group] |= irq_data->mask; | ||
| 161 | else | ||
| 162 | max77693->irq_masks_cur[irq_data->group] &= ~irq_data->mask; | ||
| 163 | } | ||
| 164 | |||
| 165 | static struct irq_chip max77693_irq_chip = { | ||
| 166 | .name = "max77693", | ||
| 167 | .irq_bus_lock = max77693_irq_lock, | ||
| 168 | .irq_bus_sync_unlock = max77693_irq_sync_unlock, | ||
| 169 | .irq_mask = max77693_irq_mask, | ||
| 170 | .irq_unmask = max77693_irq_unmask, | ||
| 171 | }; | ||
| 172 | |||
| 173 | #define MAX77693_IRQSRC_CHG (1 << 0) | ||
| 174 | #define MAX77693_IRQSRC_TOP (1 << 1) | ||
| 175 | #define MAX77693_IRQSRC_FLASH (1 << 2) | ||
| 176 | #define MAX77693_IRQSRC_MUIC (1 << 3) | ||
| 177 | static irqreturn_t max77693_irq_thread(int irq, void *data) | ||
| 178 | { | ||
| 179 | struct max77693_dev *max77693 = data; | ||
| 180 | u8 irq_reg[MAX77693_IRQ_GROUP_NR] = {}; | ||
| 181 | u8 irq_src; | ||
| 182 | int ret; | ||
| 183 | int i, cur_irq; | ||
| 184 | |||
| 185 | ret = max77693_read_reg(max77693->regmap, MAX77693_PMIC_REG_INTSRC, | ||
| 186 | &irq_src); | ||
| 187 | if (ret < 0) { | ||
| 188 | dev_err(max77693->dev, "Failed to read interrupt source: %d\n", | ||
| 189 | ret); | ||
| 190 | return IRQ_NONE; | ||
| 191 | } | ||
| 192 | |||
| 193 | if (irq_src & MAX77693_IRQSRC_CHG) | ||
| 194 | /* CHG_INT */ | ||
| 195 | ret = max77693_read_reg(max77693->regmap, MAX77693_CHG_REG_CHG_INT, | ||
| 196 | &irq_reg[CHG_INT]); | ||
| 197 | |||
| 198 | if (irq_src & MAX77693_IRQSRC_TOP) | ||
| 199 | /* TOPSYS_INT */ | ||
| 200 | ret = max77693_read_reg(max77693->regmap, | ||
| 201 | MAX77693_PMIC_REG_TOPSYS_INT, &irq_reg[TOPSYS_INT]); | ||
| 202 | |||
| 203 | if (irq_src & MAX77693_IRQSRC_FLASH) | ||
| 204 | /* LED_INT */ | ||
| 205 | ret = max77693_read_reg(max77693->regmap, | ||
| 206 | MAX77693_LED_REG_FLASH_INT, &irq_reg[LED_INT]); | ||
| 207 | |||
| 208 | if (irq_src & MAX77693_IRQSRC_MUIC) | ||
| 209 | /* MUIC INT1 ~ INT3 */ | ||
| 210 | max77693_bulk_read(max77693->regmap_muic, MAX77693_MUIC_REG_INT1, | ||
| 211 | MAX77693_NUM_IRQ_MUIC_REGS, &irq_reg[MUIC_INT1]); | ||
| 212 | |||
| 213 | /* Apply masking */ | ||
| 214 | for (i = 0; i < MAX77693_IRQ_GROUP_NR; i++) { | ||
| 215 | if (i >= MUIC_INT1 && i <= MUIC_INT3) | ||
| 216 | irq_reg[i] &= max77693->irq_masks_cur[i]; | ||
| 217 | else | ||
| 218 | irq_reg[i] &= ~max77693->irq_masks_cur[i]; | ||
| 219 | } | ||
| 220 | |||
| 221 | /* Report */ | ||
| 222 | for (i = 0; i < MAX77693_IRQ_NR; i++) { | ||
| 223 | if (irq_reg[max77693_irqs[i].group] & max77693_irqs[i].mask) { | ||
| 224 | cur_irq = irq_find_mapping(max77693->irq_domain, i); | ||
| 225 | if (cur_irq) | ||
| 226 | handle_nested_irq(cur_irq); | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | return IRQ_HANDLED; | ||
| 231 | } | ||
| 232 | |||
| 233 | int max77693_irq_resume(struct max77693_dev *max77693) | ||
| 234 | { | ||
| 235 | if (max77693->irq) | ||
| 236 | max77693_irq_thread(0, max77693); | ||
| 237 | |||
| 238 | return 0; | ||
| 239 | } | ||
| 240 | |||
| 241 | static int max77693_irq_domain_map(struct irq_domain *d, unsigned int irq, | ||
| 242 | irq_hw_number_t hw) | ||
| 243 | { | ||
| 244 | struct max77693_dev *max77693 = d->host_data; | ||
| 245 | |||
| 246 | irq_set_chip_data(irq, max77693); | ||
| 247 | irq_set_chip_and_handler(irq, &max77693_irq_chip, handle_edge_irq); | ||
| 248 | irq_set_nested_thread(irq, 1); | ||
| 249 | #ifdef CONFIG_ARM | ||
| 250 | set_irq_flags(irq, IRQF_VALID); | ||
| 251 | #else | ||
| 252 | irq_set_noprobe(irq); | ||
| 253 | #endif | ||
| 254 | return 0; | ||
| 255 | } | ||
| 256 | |||
| 257 | static struct irq_domain_ops max77693_irq_domain_ops = { | ||
| 258 | .map = max77693_irq_domain_map, | ||
| 259 | }; | ||
| 260 | |||
| 261 | int max77693_irq_init(struct max77693_dev *max77693) | ||
| 262 | { | ||
| 263 | struct irq_domain *domain; | ||
| 264 | int i; | ||
| 265 | int ret = 0; | ||
| 266 | u8 intsrc_mask; | ||
| 267 | |||
| 268 | mutex_init(&max77693->irqlock); | ||
| 269 | |||
| 270 | /* Mask individual interrupt sources */ | ||
| 271 | for (i = 0; i < MAX77693_IRQ_GROUP_NR; i++) { | ||
| 272 | struct regmap *map; | ||
| 273 | /* MUIC IRQ 0:MASK 1:NOT MASK */ | ||
| 274 | /* Other IRQ 1:MASK 0:NOT MASK */ | ||
| 275 | if (i >= MUIC_INT1 && i <= MUIC_INT3) { | ||
| 276 | max77693->irq_masks_cur[i] = 0x00; | ||
| 277 | max77693->irq_masks_cache[i] = 0x00; | ||
| 278 | } else { | ||
| 279 | max77693->irq_masks_cur[i] = 0xff; | ||
| 280 | max77693->irq_masks_cache[i] = 0xff; | ||
| 281 | } | ||
| 282 | map = max77693_get_regmap(max77693, i); | ||
| 283 | |||
| 284 | if (IS_ERR_OR_NULL(map)) | ||
| 285 | continue; | ||
| 286 | if (max77693_mask_reg[i] == MAX77693_REG_INVALID) | ||
| 287 | continue; | ||
| 288 | if (i >= MUIC_INT1 && i <= MUIC_INT3) | ||
| 289 | max77693_write_reg(map, max77693_mask_reg[i], 0x00); | ||
| 290 | else | ||
| 291 | max77693_write_reg(map, max77693_mask_reg[i], 0xff); | ||
| 292 | } | ||
| 293 | |||
| 294 | domain = irq_domain_add_linear(NULL, MAX77693_IRQ_NR, | ||
| 295 | &max77693_irq_domain_ops, max77693); | ||
| 296 | if (!domain) { | ||
| 297 | dev_err(max77693->dev, "could not create irq domain\n"); | ||
| 298 | ret = -ENODEV; | ||
| 299 | goto err_irq; | ||
| 300 | } | ||
| 301 | max77693->irq_domain = domain; | ||
| 302 | |||
| 303 | /* Unmask max77693 interrupt */ | ||
| 304 | ret = max77693_read_reg(max77693->regmap, | ||
| 305 | MAX77693_PMIC_REG_INTSRC_MASK, &intsrc_mask); | ||
| 306 | if (ret < 0) { | ||
| 307 | dev_err(max77693->dev, "fail to read PMIC register\n"); | ||
| 308 | goto err_irq; | ||
| 309 | } | ||
| 310 | |||
| 311 | intsrc_mask &= ~(MAX77693_IRQSRC_CHG); | ||
| 312 | intsrc_mask &= ~(MAX77693_IRQSRC_FLASH); | ||
| 313 | intsrc_mask &= ~(MAX77693_IRQSRC_MUIC); | ||
| 314 | ret = max77693_write_reg(max77693->regmap, | ||
| 315 | MAX77693_PMIC_REG_INTSRC_MASK, intsrc_mask); | ||
| 316 | if (ret < 0) { | ||
| 317 | dev_err(max77693->dev, "fail to write PMIC register\n"); | ||
| 318 | goto err_irq; | ||
| 319 | } | ||
| 320 | |||
| 321 | ret = request_threaded_irq(max77693->irq, NULL, max77693_irq_thread, | ||
| 322 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, | ||
| 323 | "max77693-irq", max77693); | ||
| 324 | if (ret) | ||
| 325 | dev_err(max77693->dev, "Failed to request IRQ %d: %d\n", | ||
| 326 | max77693->irq, ret); | ||
| 327 | |||
| 328 | err_irq: | ||
| 329 | return ret; | ||
| 330 | } | ||
| 331 | |||
| 332 | void max77693_irq_exit(struct max77693_dev *max77693) | ||
| 333 | { | ||
| 334 | if (max77693->irq) | ||
| 335 | free_irq(max77693->irq, max77693); | ||
| 336 | } | ||
diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c index 7e05428c756d..249c139ef04a 100644 --- a/drivers/mfd/max77693.c +++ b/drivers/mfd/max77693.c | |||
| @@ -49,62 +49,62 @@ static const struct mfd_cell max77693_devs[] = { | |||
| 49 | { .name = "max77693-haptic", }, | 49 | { .name = "max77693-haptic", }, |
| 50 | }; | 50 | }; |
| 51 | 51 | ||
| 52 | int max77693_read_reg(struct regmap *map, u8 reg, u8 *dest) | 52 | static const struct regmap_config max77693_regmap_config = { |
| 53 | { | 53 | .reg_bits = 8, |
| 54 | unsigned int val; | 54 | .val_bits = 8, |
| 55 | int ret; | 55 | .max_register = MAX77693_PMIC_REG_END, |
| 56 | 56 | }; | |
| 57 | ret = regmap_read(map, reg, &val); | ||
| 58 | *dest = val; | ||
| 59 | |||
| 60 | return ret; | ||
| 61 | } | ||
| 62 | EXPORT_SYMBOL_GPL(max77693_read_reg); | ||
| 63 | |||
| 64 | int max77693_bulk_read(struct regmap *map, u8 reg, int count, u8 *buf) | ||
| 65 | { | ||
| 66 | int ret; | ||
| 67 | |||
| 68 | ret = regmap_bulk_read(map, reg, buf, count); | ||
| 69 | |||
| 70 | return ret; | ||
| 71 | } | ||
| 72 | EXPORT_SYMBOL_GPL(max77693_bulk_read); | ||
| 73 | |||
| 74 | int max77693_write_reg(struct regmap *map, u8 reg, u8 value) | ||
| 75 | { | ||
| 76 | int ret; | ||
| 77 | |||
| 78 | ret = regmap_write(map, reg, value); | ||
| 79 | |||
| 80 | return ret; | ||
| 81 | } | ||
| 82 | EXPORT_SYMBOL_GPL(max77693_write_reg); | ||
| 83 | |||
| 84 | int max77693_bulk_write(struct regmap *map, u8 reg, int count, u8 *buf) | ||
| 85 | { | ||
| 86 | int ret; | ||
| 87 | 57 | ||
| 88 | ret = regmap_bulk_write(map, reg, buf, count); | 58 | static const struct regmap_irq max77693_led_irqs[] = { |
| 59 | { .mask = LED_IRQ_FLED2_OPEN, }, | ||
| 60 | { .mask = LED_IRQ_FLED2_SHORT, }, | ||
| 61 | { .mask = LED_IRQ_FLED1_OPEN, }, | ||
| 62 | { .mask = LED_IRQ_FLED1_SHORT, }, | ||
| 63 | { .mask = LED_IRQ_MAX_FLASH, }, | ||
| 64 | }; | ||
| 89 | 65 | ||
| 90 | return ret; | 66 | static const struct regmap_irq_chip max77693_led_irq_chip = { |
| 91 | } | 67 | .name = "max77693-led", |
| 92 | EXPORT_SYMBOL_GPL(max77693_bulk_write); | 68 | .status_base = MAX77693_LED_REG_FLASH_INT, |
| 69 | .mask_base = MAX77693_LED_REG_FLASH_INT_MASK, | ||
| 70 | .mask_invert = false, | ||
| 71 | .num_regs = 1, | ||
| 72 | .irqs = max77693_led_irqs, | ||
| 73 | .num_irqs = ARRAY_SIZE(max77693_led_irqs), | ||
| 74 | }; | ||
| 93 | 75 | ||
| 94 | int max77693_update_reg(struct regmap *map, u8 reg, u8 val, u8 mask) | 76 | static const struct regmap_irq max77693_topsys_irqs[] = { |
| 95 | { | 77 | { .mask = TOPSYS_IRQ_T120C_INT, }, |
| 96 | int ret; | 78 | { .mask = TOPSYS_IRQ_T140C_INT, }, |
| 79 | { .mask = TOPSYS_IRQ_LOWSYS_INT, }, | ||
| 80 | }; | ||
| 97 | 81 | ||
| 98 | ret = regmap_update_bits(map, reg, mask, val); | 82 | static const struct regmap_irq_chip max77693_topsys_irq_chip = { |
| 83 | .name = "max77693-topsys", | ||
| 84 | .status_base = MAX77693_PMIC_REG_TOPSYS_INT, | ||
| 85 | .mask_base = MAX77693_PMIC_REG_TOPSYS_INT_MASK, | ||
| 86 | .mask_invert = false, | ||
| 87 | .num_regs = 1, | ||
| 88 | .irqs = max77693_topsys_irqs, | ||
| 89 | .num_irqs = ARRAY_SIZE(max77693_topsys_irqs), | ||
| 90 | }; | ||
| 99 | 91 | ||
| 100 | return ret; | 92 | static const struct regmap_irq max77693_charger_irqs[] = { |
| 101 | } | 93 | { .mask = CHG_IRQ_BYP_I, }, |
| 102 | EXPORT_SYMBOL_GPL(max77693_update_reg); | 94 | { .mask = CHG_IRQ_THM_I, }, |
| 95 | { .mask = CHG_IRQ_BAT_I, }, | ||
| 96 | { .mask = CHG_IRQ_CHG_I, }, | ||
| 97 | { .mask = CHG_IRQ_CHGIN_I, }, | ||
| 98 | }; | ||
| 103 | 99 | ||
| 104 | static const struct regmap_config max77693_regmap_config = { | 100 | static const struct regmap_irq_chip max77693_charger_irq_chip = { |
| 105 | .reg_bits = 8, | 101 | .name = "max77693-charger", |
| 106 | .val_bits = 8, | 102 | .status_base = MAX77693_CHG_REG_CHG_INT, |
| 107 | .max_register = MAX77693_PMIC_REG_END, | 103 | .mask_base = MAX77693_CHG_REG_CHG_INT_MASK, |
| 104 | .mask_invert = false, | ||
| 105 | .num_regs = 1, | ||
| 106 | .irqs = max77693_charger_irqs, | ||
| 107 | .num_irqs = ARRAY_SIZE(max77693_charger_irqs), | ||
| 108 | }; | 108 | }; |
| 109 | 109 | ||
| 110 | static const struct regmap_config max77693_regmap_muic_config = { | 110 | static const struct regmap_config max77693_regmap_muic_config = { |
| @@ -113,11 +113,42 @@ static const struct regmap_config max77693_regmap_muic_config = { | |||
| 113 | .max_register = MAX77693_MUIC_REG_END, | 113 | .max_register = MAX77693_MUIC_REG_END, |
| 114 | }; | 114 | }; |
| 115 | 115 | ||
| 116 | static const struct regmap_irq max77693_muic_irqs[] = { | ||
| 117 | { .reg_offset = 0, .mask = MUIC_IRQ_INT1_ADC, }, | ||
| 118 | { .reg_offset = 0, .mask = MUIC_IRQ_INT1_ADC_LOW, }, | ||
| 119 | { .reg_offset = 0, .mask = MUIC_IRQ_INT1_ADC_ERR, }, | ||
| 120 | { .reg_offset = 0, .mask = MUIC_IRQ_INT1_ADC1K, }, | ||
| 121 | |||
| 122 | { .reg_offset = 1, .mask = MUIC_IRQ_INT2_CHGTYP, }, | ||
| 123 | { .reg_offset = 1, .mask = MUIC_IRQ_INT2_CHGDETREUN, }, | ||
| 124 | { .reg_offset = 1, .mask = MUIC_IRQ_INT2_DCDTMR, }, | ||
| 125 | { .reg_offset = 1, .mask = MUIC_IRQ_INT2_DXOVP, }, | ||
| 126 | { .reg_offset = 1, .mask = MUIC_IRQ_INT2_VBVOLT, }, | ||
| 127 | { .reg_offset = 1, .mask = MUIC_IRQ_INT2_VIDRM, }, | ||
| 128 | |||
| 129 | { .reg_offset = 2, .mask = MUIC_IRQ_INT3_EOC, }, | ||
| 130 | { .reg_offset = 2, .mask = MUIC_IRQ_INT3_CGMBC, }, | ||
| 131 | { .reg_offset = 2, .mask = MUIC_IRQ_INT3_OVP, }, | ||
| 132 | { .reg_offset = 2, .mask = MUIC_IRQ_INT3_MBCCHG_ERR, }, | ||
| 133 | { .reg_offset = 2, .mask = MUIC_IRQ_INT3_CHG_ENABLED, }, | ||
| 134 | { .reg_offset = 2, .mask = MUIC_IRQ_INT3_BAT_DET, }, | ||
| 135 | }; | ||
| 136 | |||
| 137 | static const struct regmap_irq_chip max77693_muic_irq_chip = { | ||
| 138 | .name = "max77693-muic", | ||
| 139 | .status_base = MAX77693_MUIC_REG_INT1, | ||
| 140 | .mask_base = MAX77693_MUIC_REG_INTMASK1, | ||
| 141 | .mask_invert = true, | ||
| 142 | .num_regs = 3, | ||
| 143 | .irqs = max77693_muic_irqs, | ||
| 144 | .num_irqs = ARRAY_SIZE(max77693_muic_irqs), | ||
| 145 | }; | ||
| 146 | |||
| 116 | static int max77693_i2c_probe(struct i2c_client *i2c, | 147 | static int max77693_i2c_probe(struct i2c_client *i2c, |
| 117 | const struct i2c_device_id *id) | 148 | const struct i2c_device_id *id) |
| 118 | { | 149 | { |
| 119 | struct max77693_dev *max77693; | 150 | struct max77693_dev *max77693; |
| 120 | u8 reg_data; | 151 | unsigned int reg_data; |
| 121 | int ret = 0; | 152 | int ret = 0; |
| 122 | 153 | ||
| 123 | max77693 = devm_kzalloc(&i2c->dev, | 154 | max77693 = devm_kzalloc(&i2c->dev, |
| @@ -139,7 +170,7 @@ static int max77693_i2c_probe(struct i2c_client *i2c, | |||
| 139 | return ret; | 170 | return ret; |
| 140 | } | 171 | } |
| 141 | 172 | ||
| 142 | ret = max77693_read_reg(max77693->regmap, MAX77693_PMIC_REG_PMIC_ID2, | 173 | ret = regmap_read(max77693->regmap, MAX77693_PMIC_REG_PMIC_ID2, |
| 143 | ®_data); | 174 | ®_data); |
| 144 | if (ret < 0) { | 175 | if (ret < 0) { |
| 145 | dev_err(max77693->dev, "device not found on this channel\n"); | 176 | dev_err(max77693->dev, "device not found on this channel\n"); |
| @@ -176,9 +207,45 @@ static int max77693_i2c_probe(struct i2c_client *i2c, | |||
| 176 | goto err_regmap_muic; | 207 | goto err_regmap_muic; |
| 177 | } | 208 | } |
| 178 | 209 | ||
| 179 | ret = max77693_irq_init(max77693); | 210 | ret = regmap_add_irq_chip(max77693->regmap, max77693->irq, |
| 180 | if (ret < 0) | 211 | IRQF_ONESHOT | IRQF_SHARED | |
| 181 | goto err_irq; | 212 | IRQF_TRIGGER_FALLING, 0, |
| 213 | &max77693_led_irq_chip, | ||
| 214 | &max77693->irq_data_led); | ||
| 215 | if (ret) { | ||
| 216 | dev_err(max77693->dev, "failed to add irq chip: %d\n", ret); | ||
| 217 | goto err_regmap_muic; | ||
| 218 | } | ||
| 219 | |||
| 220 | ret = regmap_add_irq_chip(max77693->regmap, max77693->irq, | ||
| 221 | IRQF_ONESHOT | IRQF_SHARED | | ||
| 222 | IRQF_TRIGGER_FALLING, 0, | ||
| 223 | &max77693_topsys_irq_chip, | ||
| 224 | &max77693->irq_data_topsys); | ||
| 225 | if (ret) { | ||
| 226 | dev_err(max77693->dev, "failed to add irq chip: %d\n", ret); | ||
| 227 | goto err_irq_topsys; | ||
| 228 | } | ||
| 229 | |||
| 230 | ret = regmap_add_irq_chip(max77693->regmap, max77693->irq, | ||
| 231 | IRQF_ONESHOT | IRQF_SHARED | | ||
| 232 | IRQF_TRIGGER_FALLING, 0, | ||
| 233 | &max77693_charger_irq_chip, | ||
| 234 | &max77693->irq_data_charger); | ||
| 235 | if (ret) { | ||
| 236 | dev_err(max77693->dev, "failed to add irq chip: %d\n", ret); | ||
| 237 | goto err_irq_charger; | ||
| 238 | } | ||
| 239 | |||
| 240 | ret = regmap_add_irq_chip(max77693->regmap, max77693->irq, | ||
| 241 | IRQF_ONESHOT | IRQF_SHARED | | ||
| 242 | IRQF_TRIGGER_FALLING, 0, | ||
| 243 | &max77693_muic_irq_chip, | ||
| 244 | &max77693->irq_data_muic); | ||
| 245 | if (ret) { | ||
| 246 | dev_err(max77693->dev, "failed to add irq chip: %d\n", ret); | ||
| 247 | goto err_irq_muic; | ||
| 248 | } | ||
| 182 | 249 | ||
| 183 | pm_runtime_set_active(max77693->dev); | 250 | pm_runtime_set_active(max77693->dev); |
| 184 | 251 | ||
| @@ -190,8 +257,14 @@ static int max77693_i2c_probe(struct i2c_client *i2c, | |||
| 190 | return ret; | 257 | return ret; |
| 191 | 258 | ||
| 192 | err_mfd: | 259 | err_mfd: |
| 193 | max77693_irq_exit(max77693); | 260 | mfd_remove_devices(max77693->dev); |
| 194 | err_irq: | 261 | regmap_del_irq_chip(max77693->irq, max77693->irq_data_muic); |
| 262 | err_irq_muic: | ||
| 263 | regmap_del_irq_chip(max77693->irq, max77693->irq_data_charger); | ||
| 264 | err_irq_charger: | ||
| 265 | regmap_del_irq_chip(max77693->irq, max77693->irq_data_topsys); | ||
| 266 | err_irq_topsys: | ||
| 267 | regmap_del_irq_chip(max77693->irq, max77693->irq_data_led); | ||
| 195 | err_regmap_muic: | 268 | err_regmap_muic: |
| 196 | i2c_unregister_device(max77693->haptic); | 269 | i2c_unregister_device(max77693->haptic); |
| 197 | err_i2c_haptic: | 270 | err_i2c_haptic: |
| @@ -204,7 +277,12 @@ static int max77693_i2c_remove(struct i2c_client *i2c) | |||
| 204 | struct max77693_dev *max77693 = i2c_get_clientdata(i2c); | 277 | struct max77693_dev *max77693 = i2c_get_clientdata(i2c); |
| 205 | 278 | ||
| 206 | mfd_remove_devices(max77693->dev); | 279 | mfd_remove_devices(max77693->dev); |
| 207 | max77693_irq_exit(max77693); | 280 | |
| 281 | regmap_del_irq_chip(max77693->irq, max77693->irq_data_muic); | ||
| 282 | regmap_del_irq_chip(max77693->irq, max77693->irq_data_charger); | ||
| 283 | regmap_del_irq_chip(max77693->irq, max77693->irq_data_topsys); | ||
| 284 | regmap_del_irq_chip(max77693->irq, max77693->irq_data_led); | ||
| 285 | |||
| 208 | i2c_unregister_device(max77693->muic); | 286 | i2c_unregister_device(max77693->muic); |
| 209 | i2c_unregister_device(max77693->haptic); | 287 | i2c_unregister_device(max77693->haptic); |
| 210 | 288 | ||
| @@ -222,8 +300,11 @@ static int max77693_suspend(struct device *dev) | |||
| 222 | struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); | 300 | struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); |
| 223 | struct max77693_dev *max77693 = i2c_get_clientdata(i2c); | 301 | struct max77693_dev *max77693 = i2c_get_clientdata(i2c); |
| 224 | 302 | ||
| 225 | if (device_may_wakeup(dev)) | 303 | if (device_may_wakeup(dev)) { |
| 226 | irq_set_irq_wake(max77693->irq, 1); | 304 | enable_irq_wake(max77693->irq); |
| 305 | disable_irq(max77693->irq); | ||
| 306 | } | ||
| 307 | |||
| 227 | return 0; | 308 | return 0; |
| 228 | } | 309 | } |
| 229 | 310 | ||
| @@ -232,9 +313,12 @@ static int max77693_resume(struct device *dev) | |||
| 232 | struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); | 313 | struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); |
| 233 | struct max77693_dev *max77693 = i2c_get_clientdata(i2c); | 314 | struct max77693_dev *max77693 = i2c_get_clientdata(i2c); |
| 234 | 315 | ||
| 235 | if (device_may_wakeup(dev)) | 316 | if (device_may_wakeup(dev)) { |
| 236 | irq_set_irq_wake(max77693->irq, 0); | 317 | disable_irq_wake(max77693->irq); |
| 237 | return max77693_irq_resume(max77693); | 318 | enable_irq(max77693->irq); |
| 319 | } | ||
| 320 | |||
| 321 | return 0; | ||
| 238 | } | 322 | } |
| 239 | 323 | ||
| 240 | static const struct dev_pm_ops max77693_pm = { | 324 | static const struct dev_pm_ops max77693_pm = { |
diff --git a/drivers/misc/bh1770glc.c b/drivers/misc/bh1770glc.c index 99a04686e45f..7b55f8a152d4 100644 --- a/drivers/misc/bh1770glc.c +++ b/drivers/misc/bh1770glc.c | |||
| @@ -1185,7 +1185,7 @@ static int bh1770_probe(struct i2c_client *client, | |||
| 1185 | struct bh1770_chip *chip; | 1185 | struct bh1770_chip *chip; |
| 1186 | int err; | 1186 | int err; |
| 1187 | 1187 | ||
| 1188 | chip = kzalloc(sizeof *chip, GFP_KERNEL); | 1188 | chip = devm_kzalloc(&client->dev, sizeof *chip, GFP_KERNEL); |
| 1189 | if (!chip) | 1189 | if (!chip) |
| 1190 | return -ENOMEM; | 1190 | return -ENOMEM; |
| 1191 | 1191 | ||
| @@ -1198,8 +1198,7 @@ static int bh1770_probe(struct i2c_client *client, | |||
| 1198 | 1198 | ||
| 1199 | if (client->dev.platform_data == NULL) { | 1199 | if (client->dev.platform_data == NULL) { |
| 1200 | dev_err(&client->dev, "platform data is mandatory\n"); | 1200 | dev_err(&client->dev, "platform data is mandatory\n"); |
| 1201 | err = -EINVAL; | 1201 | return -EINVAL; |
| 1202 | goto fail1; | ||
| 1203 | } | 1202 | } |
| 1204 | 1203 | ||
| 1205 | chip->pdata = client->dev.platform_data; | 1204 | chip->pdata = client->dev.platform_data; |
| @@ -1224,24 +1223,24 @@ static int bh1770_probe(struct i2c_client *client, | |||
| 1224 | chip->regs[0].supply = reg_vcc; | 1223 | chip->regs[0].supply = reg_vcc; |
| 1225 | chip->regs[1].supply = reg_vleds; | 1224 | chip->regs[1].supply = reg_vleds; |
| 1226 | 1225 | ||
| 1227 | err = regulator_bulk_get(&client->dev, | 1226 | err = devm_regulator_bulk_get(&client->dev, |
| 1228 | ARRAY_SIZE(chip->regs), chip->regs); | 1227 | ARRAY_SIZE(chip->regs), chip->regs); |
| 1229 | if (err < 0) { | 1228 | if (err < 0) { |
| 1230 | dev_err(&client->dev, "Cannot get regulators\n"); | 1229 | dev_err(&client->dev, "Cannot get regulators\n"); |
| 1231 | goto fail1; | 1230 | return err; |
| 1232 | } | 1231 | } |
| 1233 | 1232 | ||
| 1234 | err = regulator_bulk_enable(ARRAY_SIZE(chip->regs), | 1233 | err = regulator_bulk_enable(ARRAY_SIZE(chip->regs), |
| 1235 | chip->regs); | 1234 | chip->regs); |
| 1236 | if (err < 0) { | 1235 | if (err < 0) { |
| 1237 | dev_err(&client->dev, "Cannot enable regulators\n"); | 1236 | dev_err(&client->dev, "Cannot enable regulators\n"); |
| 1238 | goto fail2; | 1237 | return err; |
| 1239 | } | 1238 | } |
| 1240 | 1239 | ||
| 1241 | usleep_range(BH1770_STARTUP_DELAY, BH1770_STARTUP_DELAY * 2); | 1240 | usleep_range(BH1770_STARTUP_DELAY, BH1770_STARTUP_DELAY * 2); |
| 1242 | err = bh1770_detect(chip); | 1241 | err = bh1770_detect(chip); |
| 1243 | if (err < 0) | 1242 | if (err < 0) |
| 1244 | goto fail3; | 1243 | goto fail0; |
| 1245 | 1244 | ||
| 1246 | /* Start chip */ | 1245 | /* Start chip */ |
| 1247 | bh1770_chip_on(chip); | 1246 | bh1770_chip_on(chip); |
| @@ -1252,14 +1251,14 @@ static int bh1770_probe(struct i2c_client *client, | |||
| 1252 | if (chip->lux_corr == 0) { | 1251 | if (chip->lux_corr == 0) { |
| 1253 | dev_err(&client->dev, "Improper correction values\n"); | 1252 | dev_err(&client->dev, "Improper correction values\n"); |
| 1254 | err = -EINVAL; | 1253 | err = -EINVAL; |
| 1255 | goto fail3; | 1254 | goto fail0; |
| 1256 | } | 1255 | } |
| 1257 | 1256 | ||
| 1258 | if (chip->pdata->setup_resources) { | 1257 | if (chip->pdata->setup_resources) { |
| 1259 | err = chip->pdata->setup_resources(); | 1258 | err = chip->pdata->setup_resources(); |
| 1260 | if (err) { | 1259 | if (err) { |
| 1261 | err = -EINVAL; | 1260 | err = -EINVAL; |
| 1262 | goto fail3; | 1261 | goto fail0; |
| 1263 | } | 1262 | } |
| 1264 | } | 1263 | } |
| 1265 | 1264 | ||
| @@ -1267,7 +1266,7 @@ static int bh1770_probe(struct i2c_client *client, | |||
| 1267 | &bh1770_attribute_group); | 1266 | &bh1770_attribute_group); |
| 1268 | if (err < 0) { | 1267 | if (err < 0) { |
| 1269 | dev_err(&chip->client->dev, "Sysfs registration failed\n"); | 1268 | dev_err(&chip->client->dev, "Sysfs registration failed\n"); |
| 1270 | goto fail4; | 1269 | goto fail1; |
| 1271 | } | 1270 | } |
| 1272 | 1271 | ||
| 1273 | /* | 1272 | /* |
| @@ -1283,22 +1282,18 @@ static int bh1770_probe(struct i2c_client *client, | |||
| 1283 | if (err) { | 1282 | if (err) { |
| 1284 | dev_err(&client->dev, "could not get IRQ %d\n", | 1283 | dev_err(&client->dev, "could not get IRQ %d\n", |
| 1285 | client->irq); | 1284 | client->irq); |
| 1286 | goto fail5; | 1285 | goto fail2; |
| 1287 | } | 1286 | } |
| 1288 | regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs); | 1287 | regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs); |
| 1289 | return err; | 1288 | return err; |
| 1290 | fail5: | 1289 | fail2: |
| 1291 | sysfs_remove_group(&chip->client->dev.kobj, | 1290 | sysfs_remove_group(&chip->client->dev.kobj, |
| 1292 | &bh1770_attribute_group); | 1291 | &bh1770_attribute_group); |
| 1293 | fail4: | 1292 | fail1: |
| 1294 | if (chip->pdata->release_resources) | 1293 | if (chip->pdata->release_resources) |
| 1295 | chip->pdata->release_resources(); | 1294 | chip->pdata->release_resources(); |
| 1296 | fail3: | 1295 | fail0: |
| 1297 | regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs); | 1296 | regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs); |
| 1298 | fail2: | ||
| 1299 | regulator_bulk_free(ARRAY_SIZE(chip->regs), chip->regs); | ||
| 1300 | fail1: | ||
| 1301 | kfree(chip); | ||
| 1302 | return err; | 1297 | return err; |
| 1303 | } | 1298 | } |
| 1304 | 1299 | ||
| @@ -1322,8 +1317,6 @@ static int bh1770_remove(struct i2c_client *client) | |||
| 1322 | pm_runtime_disable(&client->dev); | 1317 | pm_runtime_disable(&client->dev); |
| 1323 | pm_runtime_set_suspended(&client->dev); | 1318 | pm_runtime_set_suspended(&client->dev); |
| 1324 | 1319 | ||
| 1325 | regulator_bulk_free(ARRAY_SIZE(chip->regs), chip->regs); | ||
| 1326 | kfree(chip); | ||
| 1327 | return 0; | 1320 | return 0; |
| 1328 | } | 1321 | } |
| 1329 | 1322 | ||
diff --git a/drivers/misc/bh1780gli.c b/drivers/misc/bh1780gli.c index 48ea33d15a79..4c4a59b25537 100644 --- a/drivers/misc/bh1780gli.c +++ b/drivers/misc/bh1780gli.c | |||
| @@ -149,50 +149,35 @@ static int bh1780_probe(struct i2c_client *client, | |||
| 149 | const struct i2c_device_id *id) | 149 | const struct i2c_device_id *id) |
| 150 | { | 150 | { |
| 151 | int ret; | 151 | int ret; |
| 152 | struct bh1780_data *ddata = NULL; | 152 | struct bh1780_data *ddata; |
| 153 | struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); | 153 | struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); |
| 154 | 154 | ||
| 155 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) { | 155 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) |
| 156 | ret = -EIO; | 156 | return -EIO; |
| 157 | goto err_op_failed; | ||
| 158 | } | ||
| 159 | 157 | ||
| 160 | ddata = kzalloc(sizeof(struct bh1780_data), GFP_KERNEL); | 158 | ddata = devm_kzalloc(&client->dev, sizeof(struct bh1780_data), |
| 161 | if (ddata == NULL) { | 159 | GFP_KERNEL); |
| 162 | ret = -ENOMEM; | 160 | if (ddata == NULL) |
| 163 | goto err_op_failed; | 161 | return -ENOMEM; |
| 164 | } | ||
| 165 | 162 | ||
| 166 | ddata->client = client; | 163 | ddata->client = client; |
| 167 | i2c_set_clientdata(client, ddata); | 164 | i2c_set_clientdata(client, ddata); |
| 168 | 165 | ||
| 169 | ret = bh1780_read(ddata, BH1780_REG_PARTID, "PART ID"); | 166 | ret = bh1780_read(ddata, BH1780_REG_PARTID, "PART ID"); |
| 170 | if (ret < 0) | 167 | if (ret < 0) |
| 171 | goto err_op_failed; | 168 | return ret; |
| 172 | 169 | ||
| 173 | dev_info(&client->dev, "Ambient Light Sensor, Rev : %d\n", | 170 | dev_info(&client->dev, "Ambient Light Sensor, Rev : %d\n", |
| 174 | (ret & BH1780_REVMASK)); | 171 | (ret & BH1780_REVMASK)); |
| 175 | 172 | ||
| 176 | mutex_init(&ddata->lock); | 173 | mutex_init(&ddata->lock); |
| 177 | 174 | ||
| 178 | ret = sysfs_create_group(&client->dev.kobj, &bh1780_attr_group); | 175 | return sysfs_create_group(&client->dev.kobj, &bh1780_attr_group); |
| 179 | if (ret) | ||
| 180 | goto err_op_failed; | ||
| 181 | |||
| 182 | return 0; | ||
| 183 | |||
| 184 | err_op_failed: | ||
| 185 | kfree(ddata); | ||
| 186 | return ret; | ||
| 187 | } | 176 | } |
| 188 | 177 | ||
| 189 | static int bh1780_remove(struct i2c_client *client) | 178 | static int bh1780_remove(struct i2c_client *client) |
| 190 | { | 179 | { |
| 191 | struct bh1780_data *ddata; | ||
| 192 | |||
| 193 | ddata = i2c_get_clientdata(client); | ||
| 194 | sysfs_remove_group(&client->dev.kobj, &bh1780_attr_group); | 180 | sysfs_remove_group(&client->dev.kobj, &bh1780_attr_group); |
| 195 | kfree(ddata); | ||
| 196 | 181 | ||
| 197 | return 0; | 182 | return 0; |
| 198 | } | 183 | } |
diff --git a/drivers/misc/carma/carma-fpga.c b/drivers/misc/carma/carma-fpga.c index 14d90eae605b..55e913b7eb11 100644 --- a/drivers/misc/carma/carma-fpga.c +++ b/drivers/misc/carma/carma-fpga.c | |||
| @@ -954,10 +954,7 @@ static int data_debugfs_init(struct fpga_device *priv) | |||
| 954 | { | 954 | { |
| 955 | priv->dbg_entry = debugfs_create_file(drv_name, S_IRUGO, NULL, priv, | 955 | priv->dbg_entry = debugfs_create_file(drv_name, S_IRUGO, NULL, priv, |
| 956 | &data_debug_fops); | 956 | &data_debug_fops); |
| 957 | if (IS_ERR(priv->dbg_entry)) | 957 | return PTR_ERR_OR_ZERO(priv->dbg_entry); |
| 958 | return PTR_ERR(priv->dbg_entry); | ||
| 959 | |||
| 960 | return 0; | ||
| 961 | } | 958 | } |
| 962 | 959 | ||
| 963 | static void data_debugfs_exit(struct fpga_device *priv) | 960 | static void data_debugfs_exit(struct fpga_device *priv) |
diff --git a/drivers/misc/dummy-irq.c b/drivers/misc/dummy-irq.c index 4d0db15df115..acbbe0390be4 100644 --- a/drivers/misc/dummy-irq.c +++ b/drivers/misc/dummy-irq.c | |||
| @@ -61,3 +61,4 @@ MODULE_LICENSE("GPL"); | |||
| 61 | MODULE_AUTHOR("Jiri Kosina"); | 61 | MODULE_AUTHOR("Jiri Kosina"); |
| 62 | module_param(irq, uint, 0444); | 62 | module_param(irq, uint, 0444); |
| 63 | MODULE_PARM_DESC(irq, "The IRQ to register for"); | 63 | MODULE_PARM_DESC(irq, "The IRQ to register for"); |
| 64 | MODULE_DESCRIPTION("Dummy IRQ handler driver"); | ||
diff --git a/drivers/misc/genwqe/Kconfig b/drivers/misc/genwqe/Kconfig index 6069d8cd79d7..4c0a033cbfdb 100644 --- a/drivers/misc/genwqe/Kconfig +++ b/drivers/misc/genwqe/Kconfig | |||
| @@ -11,3 +11,9 @@ menuconfig GENWQE | |||
| 11 | Enables PCIe card driver for IBM GenWQE accelerators. | 11 | Enables PCIe card driver for IBM GenWQE accelerators. |
| 12 | The user-space interface is described in | 12 | The user-space interface is described in |
| 13 | include/linux/genwqe/genwqe_card.h. | 13 | include/linux/genwqe/genwqe_card.h. |
| 14 | |||
| 15 | config GENWQE_PLATFORM_ERROR_RECOVERY | ||
| 16 | int "Use platform recovery procedures (0=off, 1=on)" | ||
| 17 | depends on GENWQE | ||
| 18 | default 1 if PPC64 | ||
| 19 | default 0 | ||
diff --git a/drivers/misc/genwqe/card_base.c b/drivers/misc/genwqe/card_base.c index 74d51c9bb858..43bbabc96b6c 100644 --- a/drivers/misc/genwqe/card_base.c +++ b/drivers/misc/genwqe/card_base.c | |||
| @@ -38,7 +38,6 @@ | |||
| 38 | #include <linux/notifier.h> | 38 | #include <linux/notifier.h> |
| 39 | #include <linux/device.h> | 39 | #include <linux/device.h> |
| 40 | #include <linux/log2.h> | 40 | #include <linux/log2.h> |
| 41 | #include <linux/genwqe/genwqe_card.h> | ||
| 42 | 41 | ||
| 43 | #include "card_base.h" | 42 | #include "card_base.h" |
| 44 | #include "card_ddcb.h" | 43 | #include "card_ddcb.h" |
| @@ -58,7 +57,7 @@ static struct dentry *debugfs_genwqe; | |||
| 58 | static struct genwqe_dev *genwqe_devices[GENWQE_CARD_NO_MAX]; | 57 | static struct genwqe_dev *genwqe_devices[GENWQE_CARD_NO_MAX]; |
| 59 | 58 | ||
| 60 | /* PCI structure for identifying device by PCI vendor and device ID */ | 59 | /* PCI structure for identifying device by PCI vendor and device ID */ |
| 61 | static DEFINE_PCI_DEVICE_TABLE(genwqe_device_table) = { | 60 | static const struct pci_device_id genwqe_device_table[] = { |
| 62 | { .vendor = PCI_VENDOR_ID_IBM, | 61 | { .vendor = PCI_VENDOR_ID_IBM, |
| 63 | .device = PCI_DEVICE_GENWQE, | 62 | .device = PCI_DEVICE_GENWQE, |
| 64 | .subvendor = PCI_SUBVENDOR_ID_IBM, | 63 | .subvendor = PCI_SUBVENDOR_ID_IBM, |
| @@ -140,6 +139,12 @@ static struct genwqe_dev *genwqe_dev_alloc(void) | |||
| 140 | cd->class_genwqe = class_genwqe; | 139 | cd->class_genwqe = class_genwqe; |
| 141 | cd->debugfs_genwqe = debugfs_genwqe; | 140 | cd->debugfs_genwqe = debugfs_genwqe; |
| 142 | 141 | ||
| 142 | /* | ||
| 143 | * This comes from kernel config option and can be overritten via | ||
| 144 | * debugfs. | ||
| 145 | */ | ||
| 146 | cd->use_platform_recovery = CONFIG_GENWQE_PLATFORM_ERROR_RECOVERY; | ||
| 147 | |||
| 143 | init_waitqueue_head(&cd->queue_waitq); | 148 | init_waitqueue_head(&cd->queue_waitq); |
| 144 | 149 | ||
| 145 | spin_lock_init(&cd->file_lock); | 150 | spin_lock_init(&cd->file_lock); |
| @@ -761,6 +766,124 @@ static u64 genwqe_fir_checking(struct genwqe_dev *cd) | |||
| 761 | } | 766 | } |
| 762 | 767 | ||
| 763 | /** | 768 | /** |
| 769 | * genwqe_pci_fundamental_reset() - trigger a PCIe fundamental reset on the slot | ||
| 770 | * | ||
| 771 | * Note: pci_set_pcie_reset_state() is not implemented on all archs, so this | ||
| 772 | * reset method will not work in all cases. | ||
| 773 | * | ||
| 774 | * Return: 0 on success or error code from pci_set_pcie_reset_state() | ||
| 775 | */ | ||
| 776 | static int genwqe_pci_fundamental_reset(struct pci_dev *pci_dev) | ||
| 777 | { | ||
| 778 | int rc; | ||
| 779 | |||
| 780 | /* | ||
| 781 | * lock pci config space access from userspace, | ||
| 782 | * save state and issue PCIe fundamental reset | ||
| 783 | */ | ||
| 784 | pci_cfg_access_lock(pci_dev); | ||
| 785 | pci_save_state(pci_dev); | ||
| 786 | rc = pci_set_pcie_reset_state(pci_dev, pcie_warm_reset); | ||
| 787 | if (!rc) { | ||
| 788 | /* keep PCIe reset asserted for 250ms */ | ||
| 789 | msleep(250); | ||
| 790 | pci_set_pcie_reset_state(pci_dev, pcie_deassert_reset); | ||
| 791 | /* Wait for 2s to reload flash and train the link */ | ||
| 792 | msleep(2000); | ||
| 793 | } | ||
| 794 | pci_restore_state(pci_dev); | ||
| 795 | pci_cfg_access_unlock(pci_dev); | ||
| 796 | return rc; | ||
| 797 | } | ||
| 798 | |||
| 799 | |||
| 800 | static int genwqe_platform_recovery(struct genwqe_dev *cd) | ||
| 801 | { | ||
| 802 | struct pci_dev *pci_dev = cd->pci_dev; | ||
| 803 | int rc; | ||
| 804 | |||
| 805 | dev_info(&pci_dev->dev, | ||
| 806 | "[%s] resetting card for error recovery\n", __func__); | ||
| 807 | |||
| 808 | /* Clear out error injection flags */ | ||
| 809 | cd->err_inject &= ~(GENWQE_INJECT_HARDWARE_FAILURE | | ||
| 810 | GENWQE_INJECT_GFIR_FATAL | | ||
| 811 | GENWQE_INJECT_GFIR_INFO); | ||
| 812 | |||
| 813 | genwqe_stop(cd); | ||
| 814 | |||
| 815 | /* Try recoverying the card with fundamental reset */ | ||
| 816 | rc = genwqe_pci_fundamental_reset(pci_dev); | ||
| 817 | if (!rc) { | ||
| 818 | rc = genwqe_start(cd); | ||
| 819 | if (!rc) | ||
| 820 | dev_info(&pci_dev->dev, | ||
| 821 | "[%s] card recovered\n", __func__); | ||
| 822 | else | ||
| 823 | dev_err(&pci_dev->dev, | ||
| 824 | "[%s] err: cannot start card services! (err=%d)\n", | ||
| 825 | __func__, rc); | ||
| 826 | } else { | ||
| 827 | dev_err(&pci_dev->dev, | ||
| 828 | "[%s] card reset failed\n", __func__); | ||
| 829 | } | ||
| 830 | |||
| 831 | return rc; | ||
| 832 | } | ||
| 833 | |||
| 834 | /* | ||
| 835 | * genwqe_reload_bistream() - reload card bitstream | ||
| 836 | * | ||
| 837 | * Set the appropriate register and call fundamental reset to reaload the card | ||
| 838 | * bitstream. | ||
| 839 | * | ||
| 840 | * Return: 0 on success, error code otherwise | ||
| 841 | */ | ||
| 842 | static int genwqe_reload_bistream(struct genwqe_dev *cd) | ||
| 843 | { | ||
| 844 | struct pci_dev *pci_dev = cd->pci_dev; | ||
| 845 | int rc; | ||
| 846 | |||
| 847 | dev_info(&pci_dev->dev, | ||
| 848 | "[%s] resetting card for bitstream reload\n", | ||
| 849 | __func__); | ||
| 850 | |||
| 851 | genwqe_stop(cd); | ||
| 852 | |||
| 853 | /* | ||
| 854 | * Cause a CPLD reprogram with the 'next_bitstream' | ||
| 855 | * partition on PCIe hot or fundamental reset | ||
| 856 | */ | ||
| 857 | __genwqe_writeq(cd, IO_SLC_CFGREG_SOFTRESET, | ||
| 858 | (cd->softreset & 0xcull) | 0x70ull); | ||
| 859 | |||
| 860 | rc = genwqe_pci_fundamental_reset(pci_dev); | ||
| 861 | if (rc) { | ||
| 862 | /* | ||
| 863 | * A fundamental reset failure can be caused | ||
| 864 | * by lack of support on the arch, so we just | ||
| 865 | * log the error and try to start the card | ||
| 866 | * again. | ||
| 867 | */ | ||
| 868 | dev_err(&pci_dev->dev, | ||
| 869 | "[%s] err: failed to reset card for bitstream reload\n", | ||
| 870 | __func__); | ||
| 871 | } | ||
| 872 | |||
| 873 | rc = genwqe_start(cd); | ||
| 874 | if (rc) { | ||
| 875 | dev_err(&pci_dev->dev, | ||
| 876 | "[%s] err: cannot start card services! (err=%d)\n", | ||
| 877 | __func__, rc); | ||
| 878 | return rc; | ||
| 879 | } | ||
| 880 | dev_info(&pci_dev->dev, | ||
| 881 | "[%s] card reloaded\n", __func__); | ||
| 882 | return 0; | ||
| 883 | } | ||
| 884 | |||
| 885 | |||
| 886 | /** | ||
| 764 | * genwqe_health_thread() - Health checking thread | 887 | * genwqe_health_thread() - Health checking thread |
| 765 | * | 888 | * |
| 766 | * This thread is only started for the PF of the card. | 889 | * This thread is only started for the PF of the card. |
| @@ -786,6 +909,7 @@ static int genwqe_health_thread(void *data) | |||
| 786 | struct pci_dev *pci_dev = cd->pci_dev; | 909 | struct pci_dev *pci_dev = cd->pci_dev; |
| 787 | u64 gfir, gfir_masked, slu_unitcfg, app_unitcfg; | 910 | u64 gfir, gfir_masked, slu_unitcfg, app_unitcfg; |
| 788 | 911 | ||
| 912 | health_thread_begin: | ||
| 789 | while (!kthread_should_stop()) { | 913 | while (!kthread_should_stop()) { |
| 790 | rc = wait_event_interruptible_timeout(cd->health_waitq, | 914 | rc = wait_event_interruptible_timeout(cd->health_waitq, |
| 791 | (genwqe_health_check_cond(cd, &gfir) || | 915 | (genwqe_health_check_cond(cd, &gfir) || |
| @@ -846,6 +970,13 @@ static int genwqe_health_thread(void *data) | |||
| 846 | } | 970 | } |
| 847 | } | 971 | } |
| 848 | 972 | ||
| 973 | if (cd->card_state == GENWQE_CARD_RELOAD_BITSTREAM) { | ||
| 974 | /* Userspace requested card bitstream reload */ | ||
| 975 | rc = genwqe_reload_bistream(cd); | ||
| 976 | if (rc) | ||
| 977 | goto fatal_error; | ||
| 978 | } | ||
| 979 | |||
| 849 | cd->last_gfir = gfir; | 980 | cd->last_gfir = gfir; |
| 850 | cond_resched(); | 981 | cond_resched(); |
| 851 | } | 982 | } |
| @@ -853,6 +984,28 @@ static int genwqe_health_thread(void *data) | |||
| 853 | return 0; | 984 | return 0; |
| 854 | 985 | ||
| 855 | fatal_error: | 986 | fatal_error: |
| 987 | if (cd->use_platform_recovery) { | ||
| 988 | /* | ||
| 989 | * Since we use raw accessors, EEH errors won't be detected | ||
| 990 | * by the platform until we do a non-raw MMIO or config space | ||
| 991 | * read | ||
| 992 | */ | ||
| 993 | readq(cd->mmio + IO_SLC_CFGREG_GFIR); | ||
| 994 | |||
| 995 | /* We do nothing if the card is going over PCI recovery */ | ||
| 996 | if (pci_channel_offline(pci_dev)) | ||
| 997 | return -EIO; | ||
| 998 | |||
| 999 | /* | ||
| 1000 | * If it's supported by the platform, we try a fundamental reset | ||
| 1001 | * to recover from a fatal error. Otherwise, we continue to wait | ||
| 1002 | * for an external recovery procedure to take care of it. | ||
| 1003 | */ | ||
| 1004 | rc = genwqe_platform_recovery(cd); | ||
| 1005 | if (!rc) | ||
| 1006 | goto health_thread_begin; | ||
| 1007 | } | ||
| 1008 | |||
| 856 | dev_err(&pci_dev->dev, | 1009 | dev_err(&pci_dev->dev, |
| 857 | "[%s] card unusable. Please trigger unbind!\n", __func__); | 1010 | "[%s] card unusable. Please trigger unbind!\n", __func__); |
| 858 | 1011 | ||
| @@ -958,6 +1111,9 @@ static int genwqe_pci_setup(struct genwqe_dev *cd) | |||
| 958 | pci_set_master(pci_dev); | 1111 | pci_set_master(pci_dev); |
| 959 | pci_enable_pcie_error_reporting(pci_dev); | 1112 | pci_enable_pcie_error_reporting(pci_dev); |
| 960 | 1113 | ||
| 1114 | /* EEH recovery requires PCIe fundamental reset */ | ||
| 1115 | pci_dev->needs_freset = 1; | ||
| 1116 | |||
| 961 | /* request complete BAR-0 space (length = 0) */ | 1117 | /* request complete BAR-0 space (length = 0) */ |
| 962 | cd->mmio_len = pci_resource_len(pci_dev, 0); | 1118 | cd->mmio_len = pci_resource_len(pci_dev, 0); |
| 963 | cd->mmio = pci_iomap(pci_dev, 0, 0); | 1119 | 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, | |||
| 1096 | 1252 | ||
| 1097 | dev_err(&pci_dev->dev, "[%s] state=%d\n", __func__, state); | 1253 | dev_err(&pci_dev->dev, "[%s] state=%d\n", __func__, state); |
| 1098 | 1254 | ||
| 1099 | if (pci_dev == NULL) | ||
| 1100 | return PCI_ERS_RESULT_NEED_RESET; | ||
| 1101 | |||
| 1102 | cd = dev_get_drvdata(&pci_dev->dev); | 1255 | cd = dev_get_drvdata(&pci_dev->dev); |
| 1103 | if (cd == NULL) | 1256 | if (cd == NULL) |
| 1104 | return PCI_ERS_RESULT_NEED_RESET; | 1257 | return PCI_ERS_RESULT_DISCONNECT; |
| 1105 | 1258 | ||
| 1106 | switch (state) { | 1259 | /* Stop the card */ |
| 1107 | case pci_channel_io_normal: | 1260 | genwqe_health_check_stop(cd); |
| 1108 | return PCI_ERS_RESULT_CAN_RECOVER; | 1261 | genwqe_stop(cd); |
| 1109 | case pci_channel_io_frozen: | 1262 | |
| 1110 | return PCI_ERS_RESULT_NEED_RESET; | 1263 | /* |
| 1111 | case pci_channel_io_perm_failure: | 1264 | * On permanent failure, the PCI code will call device remove |
| 1265 | * after the return of this function. | ||
| 1266 | * genwqe_stop() can be called twice. | ||
| 1267 | */ | ||
| 1268 | if (state == pci_channel_io_perm_failure) { | ||
| 1112 | return PCI_ERS_RESULT_DISCONNECT; | 1269 | return PCI_ERS_RESULT_DISCONNECT; |
| 1270 | } else { | ||
| 1271 | genwqe_pci_remove(cd); | ||
| 1272 | return PCI_ERS_RESULT_NEED_RESET; | ||
| 1113 | } | 1273 | } |
| 1274 | } | ||
| 1275 | |||
| 1276 | static pci_ers_result_t genwqe_err_slot_reset(struct pci_dev *pci_dev) | ||
| 1277 | { | ||
| 1278 | int rc; | ||
| 1279 | struct genwqe_dev *cd = dev_get_drvdata(&pci_dev->dev); | ||
| 1114 | 1280 | ||
| 1115 | return PCI_ERS_RESULT_NEED_RESET; | 1281 | rc = genwqe_pci_setup(cd); |
| 1282 | if (!rc) { | ||
| 1283 | return PCI_ERS_RESULT_RECOVERED; | ||
| 1284 | } else { | ||
| 1285 | dev_err(&pci_dev->dev, | ||
| 1286 | "err: problems with PCI setup (err=%d)\n", rc); | ||
| 1287 | return PCI_ERS_RESULT_DISCONNECT; | ||
| 1288 | } | ||
| 1116 | } | 1289 | } |
| 1117 | 1290 | ||
| 1118 | static pci_ers_result_t genwqe_err_result_none(struct pci_dev *dev) | 1291 | 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) | |||
| 1120 | return PCI_ERS_RESULT_NONE; | 1293 | return PCI_ERS_RESULT_NONE; |
| 1121 | } | 1294 | } |
| 1122 | 1295 | ||
| 1123 | static void genwqe_err_resume(struct pci_dev *dev) | 1296 | static void genwqe_err_resume(struct pci_dev *pci_dev) |
| 1124 | { | 1297 | { |
| 1298 | int rc; | ||
| 1299 | struct genwqe_dev *cd = dev_get_drvdata(&pci_dev->dev); | ||
| 1300 | |||
| 1301 | rc = genwqe_start(cd); | ||
| 1302 | if (!rc) { | ||
| 1303 | rc = genwqe_health_check_start(cd); | ||
| 1304 | if (rc) | ||
| 1305 | dev_err(&pci_dev->dev, | ||
| 1306 | "err: cannot start health checking! (err=%d)\n", | ||
| 1307 | rc); | ||
| 1308 | } else { | ||
| 1309 | dev_err(&pci_dev->dev, | ||
| 1310 | "err: cannot start card services! (err=%d)\n", rc); | ||
| 1311 | } | ||
| 1125 | } | 1312 | } |
| 1126 | 1313 | ||
| 1127 | static int genwqe_sriov_configure(struct pci_dev *dev, int numvfs) | 1314 | static int genwqe_sriov_configure(struct pci_dev *dev, int numvfs) |
| @@ -1144,7 +1331,7 @@ static struct pci_error_handlers genwqe_err_handler = { | |||
| 1144 | .error_detected = genwqe_err_error_detected, | 1331 | .error_detected = genwqe_err_error_detected, |
| 1145 | .mmio_enabled = genwqe_err_result_none, | 1332 | .mmio_enabled = genwqe_err_result_none, |
| 1146 | .link_reset = genwqe_err_result_none, | 1333 | .link_reset = genwqe_err_result_none, |
| 1147 | .slot_reset = genwqe_err_result_none, | 1334 | .slot_reset = genwqe_err_slot_reset, |
| 1148 | .resume = genwqe_err_resume, | 1335 | .resume = genwqe_err_resume, |
| 1149 | }; | 1336 | }; |
| 1150 | 1337 | ||
diff --git a/drivers/misc/genwqe/card_base.h b/drivers/misc/genwqe/card_base.h index 0e608a288603..67abd8cb2247 100644 --- a/drivers/misc/genwqe/card_base.h +++ b/drivers/misc/genwqe/card_base.h | |||
| @@ -291,6 +291,8 @@ struct genwqe_dev { | |||
| 291 | struct task_struct *health_thread; | 291 | struct task_struct *health_thread; |
| 292 | wait_queue_head_t health_waitq; | 292 | wait_queue_head_t health_waitq; |
| 293 | 293 | ||
| 294 | int use_platform_recovery; /* use platform recovery mechanisms */ | ||
| 295 | |||
| 294 | /* char device */ | 296 | /* char device */ |
| 295 | dev_t devnum_genwqe; /* major/minor num card */ | 297 | dev_t devnum_genwqe; /* major/minor num card */ |
| 296 | struct class *class_genwqe; /* reference to class object */ | 298 | struct class *class_genwqe; /* reference to class object */ |
diff --git a/drivers/misc/genwqe/card_ddcb.c b/drivers/misc/genwqe/card_ddcb.c index c8046db2d5a2..dc9851a5540e 100644 --- a/drivers/misc/genwqe/card_ddcb.c +++ b/drivers/misc/genwqe/card_ddcb.c | |||
| @@ -1118,7 +1118,21 @@ static irqreturn_t genwqe_pf_isr(int irq, void *dev_id) | |||
| 1118 | * safer, but slower for the good-case ... See above. | 1118 | * safer, but slower for the good-case ... See above. |
| 1119 | */ | 1119 | */ |
| 1120 | gfir = __genwqe_readq(cd, IO_SLC_CFGREG_GFIR); | 1120 | gfir = __genwqe_readq(cd, IO_SLC_CFGREG_GFIR); |
| 1121 | if ((gfir & GFIR_ERR_TRIGGER) != 0x0) { | 1121 | if (((gfir & GFIR_ERR_TRIGGER) != 0x0) && |
| 1122 | !pci_channel_offline(pci_dev)) { | ||
| 1123 | |||
| 1124 | if (cd->use_platform_recovery) { | ||
| 1125 | /* | ||
| 1126 | * Since we use raw accessors, EEH errors won't be | ||
| 1127 | * detected by the platform until we do a non-raw | ||
| 1128 | * MMIO or config space read | ||
| 1129 | */ | ||
| 1130 | readq(cd->mmio + IO_SLC_CFGREG_GFIR); | ||
| 1131 | |||
| 1132 | /* Don't do anything if the PCI channel is frozen */ | ||
| 1133 | if (pci_channel_offline(pci_dev)) | ||
| 1134 | goto exit; | ||
| 1135 | } | ||
| 1122 | 1136 | ||
| 1123 | wake_up_interruptible(&cd->health_waitq); | 1137 | wake_up_interruptible(&cd->health_waitq); |
| 1124 | 1138 | ||
| @@ -1126,12 +1140,12 @@ static irqreturn_t genwqe_pf_isr(int irq, void *dev_id) | |||
| 1126 | * By default GFIRs causes recovery actions. This | 1140 | * By default GFIRs causes recovery actions. This |
| 1127 | * count is just for debug when recovery is masked. | 1141 | * count is just for debug when recovery is masked. |
| 1128 | */ | 1142 | */ |
| 1129 | printk_ratelimited(KERN_ERR | 1143 | dev_err_ratelimited(&pci_dev->dev, |
| 1130 | "%s %s: [%s] GFIR=%016llx\n", | 1144 | "[%s] GFIR=%016llx\n", |
| 1131 | GENWQE_DEVNAME, dev_name(&pci_dev->dev), | 1145 | __func__, gfir); |
| 1132 | __func__, gfir); | ||
| 1133 | } | 1146 | } |
| 1134 | 1147 | ||
| 1148 | exit: | ||
| 1135 | return IRQ_HANDLED; | 1149 | return IRQ_HANDLED; |
| 1136 | } | 1150 | } |
| 1137 | 1151 | ||
| @@ -1237,9 +1251,7 @@ int genwqe_setup_service_layer(struct genwqe_dev *cd) | |||
| 1237 | } | 1251 | } |
| 1238 | 1252 | ||
| 1239 | rc = genwqe_set_interrupt_capability(cd, GENWQE_MSI_IRQS); | 1253 | rc = genwqe_set_interrupt_capability(cd, GENWQE_MSI_IRQS); |
| 1240 | if (rc > 0) | 1254 | if (rc) { |
| 1241 | rc = genwqe_set_interrupt_capability(cd, rc); | ||
| 1242 | if (rc != 0) { | ||
| 1243 | rc = -ENODEV; | 1255 | rc = -ENODEV; |
| 1244 | goto stop_kthread; | 1256 | goto stop_kthread; |
| 1245 | } | 1257 | } |
diff --git a/drivers/misc/genwqe/card_debugfs.c b/drivers/misc/genwqe/card_debugfs.c index 0a33ade64109..c9b4d6d0eb99 100644 --- a/drivers/misc/genwqe/card_debugfs.c +++ b/drivers/misc/genwqe/card_debugfs.c | |||
| @@ -485,6 +485,13 @@ int genwqe_init_debugfs(struct genwqe_dev *cd) | |||
| 485 | goto err1; | 485 | goto err1; |
| 486 | } | 486 | } |
| 487 | 487 | ||
| 488 | file = debugfs_create_u32("use_platform_recovery", 0666, root, | ||
| 489 | &cd->use_platform_recovery); | ||
| 490 | if (!file) { | ||
| 491 | ret = -ENOMEM; | ||
| 492 | goto err1; | ||
| 493 | } | ||
| 494 | |||
| 488 | cd->debugfs_root = root; | 495 | cd->debugfs_root = root; |
| 489 | return 0; | 496 | return 0; |
| 490 | err1: | 497 | err1: |
diff --git a/drivers/misc/genwqe/card_dev.c b/drivers/misc/genwqe/card_dev.c index 1d2f163a1906..aae42555e2ca 100644 --- a/drivers/misc/genwqe/card_dev.c +++ b/drivers/misc/genwqe/card_dev.c | |||
| @@ -1048,10 +1048,15 @@ static long genwqe_ioctl(struct file *filp, unsigned int cmd, | |||
| 1048 | int rc = 0; | 1048 | int rc = 0; |
| 1049 | struct genwqe_file *cfile = (struct genwqe_file *)filp->private_data; | 1049 | struct genwqe_file *cfile = (struct genwqe_file *)filp->private_data; |
| 1050 | struct genwqe_dev *cd = cfile->cd; | 1050 | struct genwqe_dev *cd = cfile->cd; |
| 1051 | struct pci_dev *pci_dev = cd->pci_dev; | ||
| 1051 | struct genwqe_reg_io __user *io; | 1052 | struct genwqe_reg_io __user *io; |
| 1052 | u64 val; | 1053 | u64 val; |
| 1053 | u32 reg_offs; | 1054 | u32 reg_offs; |
| 1054 | 1055 | ||
| 1056 | /* Return -EIO if card hit EEH */ | ||
| 1057 | if (pci_channel_offline(pci_dev)) | ||
| 1058 | return -EIO; | ||
| 1059 | |||
| 1055 | if (_IOC_TYPE(cmd) != GENWQE_IOC_CODE) | 1060 | if (_IOC_TYPE(cmd) != GENWQE_IOC_CODE) |
| 1056 | return -EINVAL; | 1061 | return -EINVAL; |
| 1057 | 1062 | ||
diff --git a/drivers/misc/genwqe/card_sysfs.c b/drivers/misc/genwqe/card_sysfs.c index a72a99266c3c..7232e40a3ad9 100644 --- a/drivers/misc/genwqe/card_sysfs.c +++ b/drivers/misc/genwqe/card_sysfs.c | |||
| @@ -223,6 +223,30 @@ static ssize_t next_bitstream_store(struct device *dev, | |||
| 223 | } | 223 | } |
| 224 | static DEVICE_ATTR_RW(next_bitstream); | 224 | static DEVICE_ATTR_RW(next_bitstream); |
| 225 | 225 | ||
| 226 | static ssize_t reload_bitstream_store(struct device *dev, | ||
| 227 | struct device_attribute *attr, | ||
| 228 | const char *buf, size_t count) | ||
| 229 | { | ||
| 230 | int reload; | ||
| 231 | struct genwqe_dev *cd = dev_get_drvdata(dev); | ||
| 232 | |||
| 233 | if (kstrtoint(buf, 0, &reload) < 0) | ||
| 234 | return -EINVAL; | ||
| 235 | |||
| 236 | if (reload == 0x1) { | ||
| 237 | if (cd->card_state == GENWQE_CARD_UNUSED || | ||
| 238 | cd->card_state == GENWQE_CARD_USED) | ||
| 239 | cd->card_state = GENWQE_CARD_RELOAD_BITSTREAM; | ||
| 240 | else | ||
| 241 | return -EIO; | ||
| 242 | } else { | ||
| 243 | return -EINVAL; | ||
| 244 | } | ||
| 245 | |||
| 246 | return count; | ||
| 247 | } | ||
| 248 | static DEVICE_ATTR_WO(reload_bitstream); | ||
| 249 | |||
| 226 | /* | 250 | /* |
| 227 | * Create device_attribute structures / params: name, mode, show, store | 251 | * Create device_attribute structures / params: name, mode, show, store |
| 228 | * additional flag if valid in VF | 252 | * additional flag if valid in VF |
| @@ -239,6 +263,7 @@ static struct attribute *genwqe_attributes[] = { | |||
| 239 | &dev_attr_status.attr, | 263 | &dev_attr_status.attr, |
| 240 | &dev_attr_freerunning_timer.attr, | 264 | &dev_attr_freerunning_timer.attr, |
| 241 | &dev_attr_queue_working_time.attr, | 265 | &dev_attr_queue_working_time.attr, |
| 266 | &dev_attr_reload_bitstream.attr, | ||
| 242 | NULL, | 267 | NULL, |
| 243 | }; | 268 | }; |
| 244 | 269 | ||
diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c index 62cc6bb3f62e..a6400f09229c 100644 --- a/drivers/misc/genwqe/card_utils.c +++ b/drivers/misc/genwqe/card_utils.c | |||
| @@ -53,12 +53,17 @@ | |||
| 53 | */ | 53 | */ |
| 54 | int __genwqe_writeq(struct genwqe_dev *cd, u64 byte_offs, u64 val) | 54 | int __genwqe_writeq(struct genwqe_dev *cd, u64 byte_offs, u64 val) |
| 55 | { | 55 | { |
| 56 | struct pci_dev *pci_dev = cd->pci_dev; | ||
| 57 | |||
| 56 | if (cd->err_inject & GENWQE_INJECT_HARDWARE_FAILURE) | 58 | if (cd->err_inject & GENWQE_INJECT_HARDWARE_FAILURE) |
| 57 | return -EIO; | 59 | return -EIO; |
| 58 | 60 | ||
| 59 | if (cd->mmio == NULL) | 61 | if (cd->mmio == NULL) |
| 60 | return -EIO; | 62 | return -EIO; |
| 61 | 63 | ||
| 64 | if (pci_channel_offline(pci_dev)) | ||
| 65 | return -EIO; | ||
| 66 | |||
| 62 | __raw_writeq((__force u64)cpu_to_be64(val), cd->mmio + byte_offs); | 67 | __raw_writeq((__force u64)cpu_to_be64(val), cd->mmio + byte_offs); |
| 63 | return 0; | 68 | return 0; |
| 64 | } | 69 | } |
| @@ -99,12 +104,17 @@ u64 __genwqe_readq(struct genwqe_dev *cd, u64 byte_offs) | |||
| 99 | */ | 104 | */ |
| 100 | int __genwqe_writel(struct genwqe_dev *cd, u64 byte_offs, u32 val) | 105 | int __genwqe_writel(struct genwqe_dev *cd, u64 byte_offs, u32 val) |
| 101 | { | 106 | { |
| 107 | struct pci_dev *pci_dev = cd->pci_dev; | ||
| 108 | |||
| 102 | if (cd->err_inject & GENWQE_INJECT_HARDWARE_FAILURE) | 109 | if (cd->err_inject & GENWQE_INJECT_HARDWARE_FAILURE) |
| 103 | return -EIO; | 110 | return -EIO; |
| 104 | 111 | ||
| 105 | if (cd->mmio == NULL) | 112 | if (cd->mmio == NULL) |
| 106 | return -EIO; | 113 | return -EIO; |
| 107 | 114 | ||
| 115 | if (pci_channel_offline(pci_dev)) | ||
| 116 | return -EIO; | ||
| 117 | |||
| 108 | __raw_writel((__force u32)cpu_to_be32(val), cd->mmio + byte_offs); | 118 | __raw_writel((__force u32)cpu_to_be32(val), cd->mmio + byte_offs); |
| 109 | return 0; | 119 | return 0; |
| 110 | } | 120 | } |
| @@ -718,10 +728,12 @@ int genwqe_set_interrupt_capability(struct genwqe_dev *cd, int count) | |||
| 718 | int rc; | 728 | int rc; |
| 719 | struct pci_dev *pci_dev = cd->pci_dev; | 729 | struct pci_dev *pci_dev = cd->pci_dev; |
| 720 | 730 | ||
| 721 | rc = pci_enable_msi_exact(pci_dev, count); | 731 | rc = pci_enable_msi_range(pci_dev, 1, count); |
| 722 | if (rc == 0) | 732 | if (rc < 0) |
| 723 | cd->flags |= GENWQE_FLAG_MSI_ENABLED; | 733 | return rc; |
| 724 | return rc; | 734 | |
| 735 | cd->flags |= GENWQE_FLAG_MSI_ENABLED; | ||
| 736 | return 0; | ||
| 725 | } | 737 | } |
| 726 | 738 | ||
| 727 | /** | 739 | /** |
diff --git a/drivers/misc/genwqe/genwqe_driver.h b/drivers/misc/genwqe/genwqe_driver.h index cd5263163a6e..a506e9aa2d57 100644 --- a/drivers/misc/genwqe/genwqe_driver.h +++ b/drivers/misc/genwqe/genwqe_driver.h | |||
| @@ -36,7 +36,7 @@ | |||
| 36 | #include <asm/byteorder.h> | 36 | #include <asm/byteorder.h> |
| 37 | #include <linux/genwqe/genwqe_card.h> | 37 | #include <linux/genwqe/genwqe_card.h> |
| 38 | 38 | ||
| 39 | #define DRV_VERS_STRING "2.0.15" | 39 | #define DRV_VERS_STRING "2.0.21" |
| 40 | 40 | ||
| 41 | /* | 41 | /* |
| 42 | * Static minor number assignement, until we decide/implement | 42 | * Static minor number assignement, until we decide/implement |
diff --git a/drivers/misc/lattice-ecp3-config.c b/drivers/misc/lattice-ecp3-config.c index 0a1565e63c71..7ffdb589841e 100644 --- a/drivers/misc/lattice-ecp3-config.c +++ b/drivers/misc/lattice-ecp3-config.c | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include <linux/spi/spi.h> | 15 | #include <linux/spi/spi.h> |
| 16 | #include <linux/platform_device.h> | 16 | #include <linux/platform_device.h> |
| 17 | #include <linux/delay.h> | 17 | #include <linux/delay.h> |
| 18 | #include <asm/unaligned.h> | ||
| 18 | 19 | ||
| 19 | #define FIRMWARE_NAME "lattice-ecp3.bit" | 20 | #define FIRMWARE_NAME "lattice-ecp3.bit" |
| 20 | 21 | ||
| @@ -91,8 +92,8 @@ static void firmware_load(const struct firmware *fw, void *context) | |||
| 91 | /* Trying to speak with the FPGA via SPI... */ | 92 | /* Trying to speak with the FPGA via SPI... */ |
| 92 | txbuf[0] = FPGA_CMD_READ_ID; | 93 | txbuf[0] = FPGA_CMD_READ_ID; |
| 93 | ret = spi_write_then_read(spi, txbuf, 8, rxbuf, rx_len); | 94 | ret = spi_write_then_read(spi, txbuf, 8, rxbuf, rx_len); |
| 94 | dev_dbg(&spi->dev, "FPGA JTAG ID=%08x\n", *(u32 *)&rxbuf[4]); | 95 | jedec_id = get_unaligned_be32(&rxbuf[4]); |
| 95 | jedec_id = *(u32 *)&rxbuf[4]; | 96 | dev_dbg(&spi->dev, "FPGA JTAG ID=%08x\n", jedec_id); |
| 96 | 97 | ||
| 97 | for (i = 0; i < ARRAY_SIZE(ecp3_dev); i++) { | 98 | for (i = 0; i < ARRAY_SIZE(ecp3_dev); i++) { |
| 98 | if (jedec_id == ecp3_dev[i].jedec_id) | 99 | if (jedec_id == ecp3_dev[i].jedec_id) |
| @@ -109,7 +110,8 @@ static void firmware_load(const struct firmware *fw, void *context) | |||
| 109 | 110 | ||
| 110 | txbuf[0] = FPGA_CMD_READ_STATUS; | 111 | txbuf[0] = FPGA_CMD_READ_STATUS; |
| 111 | ret = spi_write_then_read(spi, txbuf, 8, rxbuf, rx_len); | 112 | ret = spi_write_then_read(spi, txbuf, 8, rxbuf, rx_len); |
| 112 | dev_dbg(&spi->dev, "FPGA Status=%08x\n", *(u32 *)&rxbuf[4]); | 113 | status = get_unaligned_be32(&rxbuf[4]); |
| 114 | dev_dbg(&spi->dev, "FPGA Status=%08x\n", status); | ||
| 113 | 115 | ||
| 114 | buffer = kzalloc(fw->size + 8, GFP_KERNEL); | 116 | buffer = kzalloc(fw->size + 8, GFP_KERNEL); |
| 115 | if (!buffer) { | 117 | if (!buffer) { |
| @@ -141,7 +143,7 @@ static void firmware_load(const struct firmware *fw, void *context) | |||
| 141 | for (i = 0; i < FPGA_CLEAR_LOOP_COUNT; i++) { | 143 | for (i = 0; i < FPGA_CLEAR_LOOP_COUNT; i++) { |
| 142 | txbuf[0] = FPGA_CMD_READ_STATUS; | 144 | txbuf[0] = FPGA_CMD_READ_STATUS; |
| 143 | ret = spi_write_then_read(spi, txbuf, 8, rxbuf, rx_len); | 145 | ret = spi_write_then_read(spi, txbuf, 8, rxbuf, rx_len); |
| 144 | status = *(u32 *)&rxbuf[4]; | 146 | status = get_unaligned_be32(&rxbuf[4]); |
| 145 | if (status == FPGA_STATUS_CLEARED) | 147 | if (status == FPGA_STATUS_CLEARED) |
| 146 | break; | 148 | break; |
| 147 | 149 | ||
| @@ -164,8 +166,8 @@ static void firmware_load(const struct firmware *fw, void *context) | |||
| 164 | 166 | ||
| 165 | txbuf[0] = FPGA_CMD_READ_STATUS; | 167 | txbuf[0] = FPGA_CMD_READ_STATUS; |
| 166 | ret = spi_write_then_read(spi, txbuf, 8, rxbuf, rx_len); | 168 | ret = spi_write_then_read(spi, txbuf, 8, rxbuf, rx_len); |
| 167 | dev_dbg(&spi->dev, "FPGA Status=%08x\n", *(u32 *)&rxbuf[4]); | 169 | status = get_unaligned_be32(&rxbuf[4]); |
| 168 | status = *(u32 *)&rxbuf[4]; | 170 | dev_dbg(&spi->dev, "FPGA Status=%08x\n", status); |
| 169 | 171 | ||
| 170 | /* Check result */ | 172 | /* Check result */ |
| 171 | if (status & FPGA_STATUS_DONE) | 173 | if (status & FPGA_STATUS_DONE) |
| @@ -196,7 +198,7 @@ static int lattice_ecp3_probe(struct spi_device *spi) | |||
| 196 | spi_set_drvdata(spi, data); | 198 | spi_set_drvdata(spi, data); |
| 197 | 199 | ||
| 198 | init_completion(&data->fw_loaded); | 200 | init_completion(&data->fw_loaded); |
| 199 | err = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, | 201 | err = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, |
| 200 | FIRMWARE_NAME, &spi->dev, | 202 | FIRMWARE_NAME, &spi->dev, |
| 201 | GFP_KERNEL, spi, firmware_load); | 203 | GFP_KERNEL, spi, firmware_load); |
| 202 | if (err) { | 204 | if (err) { |
diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c index d66a2f24f6b3..b5abe34120b8 100644 --- a/drivers/misc/lkdtm.c +++ b/drivers/misc/lkdtm.c | |||
| @@ -870,3 +870,4 @@ module_init(lkdtm_module_init); | |||
| 870 | module_exit(lkdtm_module_exit); | 870 | module_exit(lkdtm_module_exit); |
| 871 | 871 | ||
| 872 | MODULE_LICENSE("GPL"); | 872 | MODULE_LICENSE("GPL"); |
| 873 | MODULE_DESCRIPTION("Kprobe module for testing crash dumps"); | ||
diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 59d20c599b16..324e1de93687 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c | |||
| @@ -459,7 +459,7 @@ int mei_cl_disconnect(struct mei_cl *cl) | |||
| 459 | { | 459 | { |
| 460 | struct mei_device *dev; | 460 | struct mei_device *dev; |
| 461 | struct mei_cl_cb *cb; | 461 | struct mei_cl_cb *cb; |
| 462 | int rets, err; | 462 | int rets; |
| 463 | 463 | ||
| 464 | if (WARN_ON(!cl || !cl->dev)) | 464 | if (WARN_ON(!cl || !cl->dev)) |
| 465 | return -ENODEV; | 465 | return -ENODEV; |
| @@ -491,6 +491,7 @@ int mei_cl_disconnect(struct mei_cl *cl) | |||
| 491 | cl_err(dev, cl, "failed to disconnect.\n"); | 491 | cl_err(dev, cl, "failed to disconnect.\n"); |
| 492 | goto free; | 492 | goto free; |
| 493 | } | 493 | } |
| 494 | cl->timer_count = MEI_CONNECT_TIMEOUT; | ||
| 494 | mdelay(10); /* Wait for hardware disconnection ready */ | 495 | mdelay(10); /* Wait for hardware disconnection ready */ |
| 495 | list_add_tail(&cb->list, &dev->ctrl_rd_list.list); | 496 | list_add_tail(&cb->list, &dev->ctrl_rd_list.list); |
| 496 | } else { | 497 | } else { |
| @@ -500,23 +501,18 @@ int mei_cl_disconnect(struct mei_cl *cl) | |||
| 500 | } | 501 | } |
| 501 | mutex_unlock(&dev->device_lock); | 502 | mutex_unlock(&dev->device_lock); |
| 502 | 503 | ||
| 503 | err = wait_event_timeout(dev->wait_recvd_msg, | 504 | wait_event_timeout(dev->wait_recvd_msg, |
| 504 | MEI_FILE_DISCONNECTED == cl->state, | 505 | MEI_FILE_DISCONNECTED == cl->state, |
| 505 | mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); | 506 | mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); |
| 506 | 507 | ||
| 507 | mutex_lock(&dev->device_lock); | 508 | mutex_lock(&dev->device_lock); |
| 509 | |||
| 508 | if (MEI_FILE_DISCONNECTED == cl->state) { | 510 | if (MEI_FILE_DISCONNECTED == cl->state) { |
| 509 | rets = 0; | 511 | rets = 0; |
| 510 | cl_dbg(dev, cl, "successfully disconnected from FW client.\n"); | 512 | cl_dbg(dev, cl, "successfully disconnected from FW client.\n"); |
| 511 | } else { | 513 | } else { |
| 512 | rets = -ENODEV; | 514 | cl_dbg(dev, cl, "timeout on disconnect from FW client.\n"); |
| 513 | if (MEI_FILE_DISCONNECTED != cl->state) | 515 | rets = -ETIME; |
| 514 | cl_err(dev, cl, "wrong status client disconnect.\n"); | ||
| 515 | |||
| 516 | if (err) | ||
| 517 | cl_dbg(dev, cl, "wait failed disconnect err=%d\n", err); | ||
| 518 | |||
| 519 | cl_err(dev, cl, "failed to disconnect from FW client.\n"); | ||
| 520 | } | 516 | } |
| 521 | 517 | ||
| 522 | mei_io_list_flush(&dev->ctrl_rd_list, cl); | 518 | mei_io_list_flush(&dev->ctrl_rd_list, cl); |
| @@ -616,6 +612,7 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file) | |||
| 616 | mutex_lock(&dev->device_lock); | 612 | mutex_lock(&dev->device_lock); |
| 617 | 613 | ||
| 618 | if (cl->state != MEI_FILE_CONNECTED) { | 614 | if (cl->state != MEI_FILE_CONNECTED) { |
| 615 | cl->state = MEI_FILE_DISCONNECTED; | ||
| 619 | /* something went really wrong */ | 616 | /* something went really wrong */ |
| 620 | if (!cl->status) | 617 | if (!cl->status) |
| 621 | cl->status = -EFAULT; | 618 | cl->status = -EFAULT; |
diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index a7856c0ac576..c5feafdd58a8 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h | |||
| @@ -115,6 +115,7 @@ | |||
| 115 | #define MEI_DEV_ID_LPT_HR 0x8CBA /* Lynx Point H Refresh */ | 115 | #define MEI_DEV_ID_LPT_HR 0x8CBA /* Lynx Point H Refresh */ |
| 116 | 116 | ||
| 117 | #define MEI_DEV_ID_WPT_LP 0x9CBA /* Wildcat Point LP */ | 117 | #define MEI_DEV_ID_WPT_LP 0x9CBA /* Wildcat Point LP */ |
| 118 | #define MEI_DEV_ID_WPT_LP_2 0x9CBB /* Wildcat Point LP 2 */ | ||
| 118 | 119 | ||
| 119 | /* Host Firmware Status Registers in PCI Config Space */ | 120 | /* Host Firmware Status Registers in PCI Config Space */ |
| 120 | #define PCI_CFG_HFS_1 0x40 | 121 | #define PCI_CFG_HFS_1 0x40 |
diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 6a2d272cea43..a9a0d08f758e 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c | |||
| @@ -710,64 +710,10 @@ end: | |||
| 710 | return IRQ_HANDLED; | 710 | return IRQ_HANDLED; |
| 711 | } | 711 | } |
| 712 | 712 | ||
| 713 | /** | ||
| 714 | * mei_me_fw_status - retrieve fw status from the pci config space | ||
| 715 | * | ||
| 716 | * @dev: the device structure | ||
| 717 | * @fw_status: fw status registers storage | ||
| 718 | * | ||
| 719 | * returns 0 on success an error code otherwise | ||
| 720 | */ | ||
| 721 | static int mei_me_fw_status(struct mei_device *dev, | ||
| 722 | struct mei_fw_status *fw_status) | ||
| 723 | { | ||
| 724 | const u32 pci_cfg_reg[] = {PCI_CFG_HFS_1, PCI_CFG_HFS_2}; | ||
| 725 | int i; | ||
| 726 | |||
| 727 | if (!fw_status) | ||
| 728 | return -EINVAL; | ||
| 729 | |||
| 730 | switch (dev->pdev->device) { | ||
| 731 | case MEI_DEV_ID_IBXPK_1: | ||
| 732 | case MEI_DEV_ID_IBXPK_2: | ||
| 733 | case MEI_DEV_ID_CPT_1: | ||
| 734 | case MEI_DEV_ID_PBG_1: | ||
| 735 | case MEI_DEV_ID_PPT_1: | ||
| 736 | case MEI_DEV_ID_PPT_2: | ||
| 737 | case MEI_DEV_ID_PPT_3: | ||
| 738 | case MEI_DEV_ID_LPT_H: | ||
| 739 | case MEI_DEV_ID_LPT_W: | ||
| 740 | case MEI_DEV_ID_LPT_LP: | ||
| 741 | case MEI_DEV_ID_LPT_HR: | ||
| 742 | case MEI_DEV_ID_WPT_LP: | ||
| 743 | fw_status->count = 2; | ||
| 744 | break; | ||
| 745 | case MEI_DEV_ID_ICH10_1: | ||
| 746 | case MEI_DEV_ID_ICH10_2: | ||
| 747 | case MEI_DEV_ID_ICH10_3: | ||
| 748 | case MEI_DEV_ID_ICH10_4: | ||
| 749 | fw_status->count = 1; | ||
| 750 | break; | ||
| 751 | default: | ||
| 752 | fw_status->count = 0; | ||
| 753 | break; | ||
| 754 | } | ||
| 755 | |||
| 756 | for (i = 0; i < fw_status->count && i < MEI_FW_STATUS_MAX; i++) { | ||
| 757 | int ret; | ||
| 758 | ret = pci_read_config_dword(dev->pdev, | ||
| 759 | pci_cfg_reg[i], &fw_status->status[i]); | ||
| 760 | if (ret) | ||
| 761 | return ret; | ||
| 762 | } | ||
| 763 | return 0; | ||
| 764 | } | ||
| 765 | |||
| 766 | static const struct mei_hw_ops mei_me_hw_ops = { | 713 | static const struct mei_hw_ops mei_me_hw_ops = { |
| 767 | 714 | ||
| 768 | .pg_state = mei_me_pg_state, | 715 | .pg_state = mei_me_pg_state, |
| 769 | 716 | ||
| 770 | .fw_status = mei_me_fw_status, | ||
| 771 | .host_is_ready = mei_me_host_is_ready, | 717 | .host_is_ready = mei_me_host_is_ready, |
| 772 | 718 | ||
| 773 | .hw_is_ready = mei_me_hw_is_ready, | 719 | .hw_is_ready = mei_me_hw_is_ready, |
diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c index 93273783dec5..f1cd166094f2 100644 --- a/drivers/misc/mei/hw-txe.c +++ b/drivers/misc/mei/hw-txe.c | |||
| @@ -1042,40 +1042,8 @@ end: | |||
| 1042 | return IRQ_HANDLED; | 1042 | return IRQ_HANDLED; |
| 1043 | } | 1043 | } |
| 1044 | 1044 | ||
| 1045 | |||
| 1046 | /** | ||
| 1047 | * mei_txe_fw_status - retrieve fw status from the pci config space | ||
| 1048 | * | ||
| 1049 | * @dev: the device structure | ||
| 1050 | * @fw_status: fw status registers storage | ||
| 1051 | * | ||
| 1052 | * returns: 0 on success an error code otherwise | ||
| 1053 | */ | ||
| 1054 | static int mei_txe_fw_status(struct mei_device *dev, | ||
| 1055 | struct mei_fw_status *fw_status) | ||
| 1056 | { | ||
| 1057 | const u32 pci_cfg_reg[] = {PCI_CFG_TXE_FW_STS0, PCI_CFG_TXE_FW_STS1}; | ||
| 1058 | int i; | ||
| 1059 | |||
| 1060 | if (!fw_status) | ||
| 1061 | return -EINVAL; | ||
| 1062 | |||
| 1063 | fw_status->count = 2; | ||
| 1064 | |||
| 1065 | for (i = 0; i < fw_status->count && i < MEI_FW_STATUS_MAX; i++) { | ||
| 1066 | int ret; | ||
| 1067 | ret = pci_read_config_dword(dev->pdev, | ||
| 1068 | pci_cfg_reg[i], &fw_status->status[i]); | ||
| 1069 | if (ret) | ||
| 1070 | return ret; | ||
| 1071 | } | ||
| 1072 | |||
| 1073 | return 0; | ||
| 1074 | } | ||
| 1075 | |||
| 1076 | static const struct mei_hw_ops mei_txe_hw_ops = { | 1045 | static const struct mei_hw_ops mei_txe_hw_ops = { |
| 1077 | 1046 | ||
| 1078 | .fw_status = mei_txe_fw_status, | ||
| 1079 | .host_is_ready = mei_txe_host_is_ready, | 1047 | .host_is_ready = mei_txe_host_is_ready, |
| 1080 | 1048 | ||
| 1081 | .pg_state = mei_txe_pg_state, | 1049 | .pg_state = mei_txe_pg_state, |
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 66f0a1a06451..401a3d526cd0 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c | |||
| @@ -32,7 +32,6 @@ | |||
| 32 | #include <linux/compat.h> | 32 | #include <linux/compat.h> |
| 33 | #include <linux/jiffies.h> | 33 | #include <linux/jiffies.h> |
| 34 | #include <linux/interrupt.h> | 34 | #include <linux/interrupt.h> |
| 35 | #include <linux/miscdevice.h> | ||
| 36 | 35 | ||
| 37 | #include <linux/mei.h> | 36 | #include <linux/mei.h> |
| 38 | 37 | ||
| @@ -49,19 +48,12 @@ | |||
| 49 | */ | 48 | */ |
| 50 | static int mei_open(struct inode *inode, struct file *file) | 49 | static int mei_open(struct inode *inode, struct file *file) |
| 51 | { | 50 | { |
| 52 | struct miscdevice *misc = file->private_data; | ||
| 53 | struct pci_dev *pdev; | ||
| 54 | struct mei_cl *cl; | ||
| 55 | struct mei_device *dev; | 51 | struct mei_device *dev; |
| 52 | struct mei_cl *cl; | ||
| 56 | 53 | ||
| 57 | int err; | 54 | int err; |
| 58 | 55 | ||
| 59 | if (!misc->parent) | 56 | dev = container_of(inode->i_cdev, struct mei_device, cdev); |
| 60 | return -ENODEV; | ||
| 61 | |||
| 62 | pdev = container_of(misc->parent, struct pci_dev, dev); | ||
| 63 | |||
| 64 | dev = pci_get_drvdata(pdev); | ||
| 65 | if (!dev) | 57 | if (!dev) |
| 66 | return -ENODEV; | 58 | return -ENODEV; |
| 67 | 59 | ||
| @@ -667,46 +659,148 @@ static const struct file_operations mei_fops = { | |||
| 667 | .llseek = no_llseek | 659 | .llseek = no_llseek |
| 668 | }; | 660 | }; |
| 669 | 661 | ||
| 670 | /* | 662 | static struct class *mei_class; |
| 671 | * Misc Device Struct | 663 | static dev_t mei_devt; |
| 664 | #define MEI_MAX_DEVS MINORMASK | ||
| 665 | static DEFINE_MUTEX(mei_minor_lock); | ||
| 666 | static DEFINE_IDR(mei_idr); | ||
| 667 | |||
| 668 | /** | ||
| 669 | * mei_minor_get - obtain next free device minor number | ||
| 670 | * | ||
| 671 | * @dev: device pointer | ||
| 672 | * | ||
| 673 | * returns allocated minor, or -ENOSPC if no free minor left | ||
| 672 | */ | 674 | */ |
| 673 | static struct miscdevice mei_misc_device = { | 675 | static int mei_minor_get(struct mei_device *dev) |
| 674 | .name = "mei", | 676 | { |
| 675 | .fops = &mei_fops, | 677 | int ret; |
| 676 | .minor = MISC_DYNAMIC_MINOR, | 678 | |
| 677 | }; | 679 | mutex_lock(&mei_minor_lock); |
| 680 | ret = idr_alloc(&mei_idr, dev, 0, MEI_MAX_DEVS, GFP_KERNEL); | ||
| 681 | if (ret >= 0) | ||
| 682 | dev->minor = ret; | ||
| 683 | else if (ret == -ENOSPC) | ||
| 684 | dev_err(&dev->pdev->dev, "too many mei devices\n"); | ||
| 678 | 685 | ||
| 686 | mutex_unlock(&mei_minor_lock); | ||
| 687 | return ret; | ||
| 688 | } | ||
| 679 | 689 | ||
| 680 | int mei_register(struct mei_device *dev) | 690 | /** |
| 691 | * mei_minor_free - mark device minor number as free | ||
| 692 | * | ||
| 693 | * @dev: device pointer | ||
| 694 | */ | ||
| 695 | static void mei_minor_free(struct mei_device *dev) | ||
| 681 | { | 696 | { |
| 682 | int ret; | 697 | mutex_lock(&mei_minor_lock); |
| 683 | mei_misc_device.parent = &dev->pdev->dev; | 698 | idr_remove(&mei_idr, dev->minor); |
| 684 | ret = misc_register(&mei_misc_device); | 699 | mutex_unlock(&mei_minor_lock); |
| 685 | if (ret) | 700 | } |
| 701 | |||
| 702 | int mei_register(struct mei_device *dev, struct device *parent) | ||
| 703 | { | ||
| 704 | struct device *clsdev; /* class device */ | ||
| 705 | int ret, devno; | ||
| 706 | |||
| 707 | ret = mei_minor_get(dev); | ||
| 708 | if (ret < 0) | ||
| 686 | return ret; | 709 | return ret; |
| 687 | 710 | ||
| 688 | if (mei_dbgfs_register(dev, mei_misc_device.name)) | 711 | /* Fill in the data structures */ |
| 689 | dev_err(&dev->pdev->dev, "cannot register debugfs\n"); | 712 | devno = MKDEV(MAJOR(mei_devt), dev->minor); |
| 713 | cdev_init(&dev->cdev, &mei_fops); | ||
| 714 | dev->cdev.owner = mei_fops.owner; | ||
| 715 | |||
| 716 | /* Add the device */ | ||
| 717 | ret = cdev_add(&dev->cdev, devno, 1); | ||
| 718 | if (ret) { | ||
| 719 | dev_err(parent, "unable to add device %d:%d\n", | ||
| 720 | MAJOR(mei_devt), dev->minor); | ||
| 721 | goto err_dev_add; | ||
| 722 | } | ||
| 723 | |||
| 724 | clsdev = device_create(mei_class, parent, devno, | ||
| 725 | NULL, "mei%d", dev->minor); | ||
| 726 | |||
| 727 | if (IS_ERR(clsdev)) { | ||
| 728 | dev_err(parent, "unable to create device %d:%d\n", | ||
| 729 | MAJOR(mei_devt), dev->minor); | ||
| 730 | ret = PTR_ERR(clsdev); | ||
| 731 | goto err_dev_create; | ||
| 732 | } | ||
| 733 | |||
| 734 | ret = mei_dbgfs_register(dev, dev_name(clsdev)); | ||
| 735 | if (ret) { | ||
| 736 | dev_err(clsdev, "cannot register debugfs ret = %d\n", ret); | ||
| 737 | goto err_dev_dbgfs; | ||
| 738 | } | ||
| 690 | 739 | ||
| 691 | return 0; | 740 | return 0; |
| 741 | |||
| 742 | err_dev_dbgfs: | ||
| 743 | device_destroy(mei_class, devno); | ||
| 744 | err_dev_create: | ||
| 745 | cdev_del(&dev->cdev); | ||
| 746 | err_dev_add: | ||
| 747 | mei_minor_free(dev); | ||
| 748 | return ret; | ||
| 692 | } | 749 | } |
| 693 | EXPORT_SYMBOL_GPL(mei_register); | 750 | EXPORT_SYMBOL_GPL(mei_register); |
| 694 | 751 | ||
| 695 | void mei_deregister(struct mei_device *dev) | 752 | void mei_deregister(struct mei_device *dev) |
| 696 | { | 753 | { |
| 754 | int devno; | ||
| 755 | |||
| 756 | devno = dev->cdev.dev; | ||
| 757 | cdev_del(&dev->cdev); | ||
| 758 | |||
| 697 | mei_dbgfs_deregister(dev); | 759 | mei_dbgfs_deregister(dev); |
| 698 | misc_deregister(&mei_misc_device); | 760 | |
| 699 | mei_misc_device.parent = NULL; | 761 | device_destroy(mei_class, devno); |
| 762 | |||
| 763 | mei_minor_free(dev); | ||
| 700 | } | 764 | } |
| 701 | EXPORT_SYMBOL_GPL(mei_deregister); | 765 | EXPORT_SYMBOL_GPL(mei_deregister); |
| 702 | 766 | ||
| 703 | static int __init mei_init(void) | 767 | static int __init mei_init(void) |
| 704 | { | 768 | { |
| 705 | return mei_cl_bus_init(); | 769 | int ret; |
| 770 | |||
| 771 | mei_class = class_create(THIS_MODULE, "mei"); | ||
| 772 | if (IS_ERR(mei_class)) { | ||
| 773 | pr_err("couldn't create class\n"); | ||
| 774 | ret = PTR_ERR(mei_class); | ||
| 775 | goto err; | ||
| 776 | } | ||
| 777 | |||
| 778 | ret = alloc_chrdev_region(&mei_devt, 0, MEI_MAX_DEVS, "mei"); | ||
| 779 | if (ret < 0) { | ||
| 780 | pr_err("unable to allocate char dev region\n"); | ||
| 781 | goto err_class; | ||
| 782 | } | ||
| 783 | |||
| 784 | ret = mei_cl_bus_init(); | ||
| 785 | if (ret < 0) { | ||
| 786 | pr_err("unable to initialize bus\n"); | ||
| 787 | goto err_chrdev; | ||
| 788 | } | ||
| 789 | |||
| 790 | return 0; | ||
| 791 | |||
| 792 | err_chrdev: | ||
| 793 | unregister_chrdev_region(mei_devt, MEI_MAX_DEVS); | ||
| 794 | err_class: | ||
| 795 | class_destroy(mei_class); | ||
| 796 | err: | ||
| 797 | return ret; | ||
| 706 | } | 798 | } |
| 707 | 799 | ||
| 708 | static void __exit mei_exit(void) | 800 | static void __exit mei_exit(void) |
| 709 | { | 801 | { |
| 802 | unregister_chrdev_region(mei_devt, MEI_MAX_DEVS); | ||
| 803 | class_destroy(mei_class); | ||
| 710 | mei_cl_bus_exit(); | 804 | mei_cl_bus_exit(); |
| 711 | } | 805 | } |
| 712 | 806 | ||
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 5c7e990e2f22..0b0d6135543b 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h | |||
| @@ -227,7 +227,6 @@ struct mei_cl { | |||
| 227 | 227 | ||
| 228 | /** struct mei_hw_ops | 228 | /** struct mei_hw_ops |
| 229 | * | 229 | * |
| 230 | * @fw_status - read FW status from PCI config space | ||
| 231 | * @host_is_ready - query for host readiness | 230 | * @host_is_ready - query for host readiness |
| 232 | 231 | ||
| 233 | * @hw_is_ready - query if hw is ready | 232 | * @hw_is_ready - query if hw is ready |
| @@ -255,8 +254,6 @@ struct mei_cl { | |||
| 255 | */ | 254 | */ |
| 256 | struct mei_hw_ops { | 255 | struct mei_hw_ops { |
| 257 | 256 | ||
| 258 | int (*fw_status)(struct mei_device *dev, | ||
| 259 | struct mei_fw_status *fw_status); | ||
| 260 | bool (*host_is_ready)(struct mei_device *dev); | 257 | bool (*host_is_ready)(struct mei_device *dev); |
| 261 | 258 | ||
| 262 | bool (*hw_is_ready)(struct mei_device *dev); | 259 | bool (*hw_is_ready)(struct mei_device *dev); |
| @@ -400,6 +397,10 @@ struct mei_cfg { | |||
| 400 | /** | 397 | /** |
| 401 | * struct mei_device - MEI private device struct | 398 | * struct mei_device - MEI private device struct |
| 402 | 399 | ||
| 400 | * @pdev - pointer to pci device struct | ||
| 401 | * @cdev - character device | ||
| 402 | * @minor - minor number allocated for device | ||
| 403 | * | ||
| 403 | * @reset_count - limits the number of consecutive resets | 404 | * @reset_count - limits the number of consecutive resets |
| 404 | * @hbm_state - state of host bus message protocol | 405 | * @hbm_state - state of host bus message protocol |
| 405 | * @pg_event - power gating event | 406 | * @pg_event - power gating event |
| @@ -412,6 +413,9 @@ struct mei_cfg { | |||
| 412 | */ | 413 | */ |
| 413 | struct mei_device { | 414 | struct mei_device { |
| 414 | struct pci_dev *pdev; /* pointer to pci device struct */ | 415 | struct pci_dev *pdev; /* pointer to pci device struct */ |
| 416 | struct cdev cdev; | ||
| 417 | int minor; | ||
| 418 | |||
| 415 | /* | 419 | /* |
| 416 | * lists of queues | 420 | * lists of queues |
| 417 | */ | 421 | */ |
| @@ -741,7 +745,7 @@ static inline int mei_dbgfs_register(struct mei_device *dev, const char *name) | |||
| 741 | static inline void mei_dbgfs_deregister(struct mei_device *dev) {} | 745 | static inline void mei_dbgfs_deregister(struct mei_device *dev) {} |
| 742 | #endif /* CONFIG_DEBUG_FS */ | 746 | #endif /* CONFIG_DEBUG_FS */ |
| 743 | 747 | ||
| 744 | int mei_register(struct mei_device *dev); | 748 | int mei_register(struct mei_device *dev, struct device *parent); |
| 745 | void mei_deregister(struct mei_device *dev); | 749 | void mei_deregister(struct mei_device *dev); |
| 746 | 750 | ||
| 747 | #define MEI_HDR_FMT "hdr:host=%02d me=%02d len=%d internal=%1d comp=%1d" | 751 | #define MEI_HDR_FMT "hdr:host=%02d me=%02d len=%d internal=%1d comp=%1d" |
diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 1b46c64a649f..a0e9422b55a2 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c | |||
| @@ -31,7 +31,6 @@ | |||
| 31 | #include <linux/compat.h> | 31 | #include <linux/compat.h> |
| 32 | #include <linux/jiffies.h> | 32 | #include <linux/jiffies.h> |
| 33 | #include <linux/interrupt.h> | 33 | #include <linux/interrupt.h> |
| 34 | #include <linux/miscdevice.h> | ||
| 35 | 34 | ||
| 36 | #include <linux/pm_runtime.h> | 35 | #include <linux/pm_runtime.h> |
| 37 | 36 | ||
| @@ -82,6 +81,7 @@ static const struct pci_device_id mei_me_pci_tbl[] = { | |||
| 82 | {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_LP, mei_me_pch_cfg)}, | 81 | {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_LP, mei_me_pch_cfg)}, |
| 83 | {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_HR, mei_me_lpt_cfg)}, | 82 | {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_HR, mei_me_lpt_cfg)}, |
| 84 | {MEI_PCI_DEVICE(MEI_DEV_ID_WPT_LP, mei_me_pch_cfg)}, | 83 | {MEI_PCI_DEVICE(MEI_DEV_ID_WPT_LP, mei_me_pch_cfg)}, |
| 84 | {MEI_PCI_DEVICE(MEI_DEV_ID_WPT_LP_2, mei_me_pch_cfg)}, | ||
| 85 | 85 | ||
| 86 | /* required last entry */ | 86 | /* required last entry */ |
| 87 | {0, } | 87 | {0, } |
| @@ -207,7 +207,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
| 207 | pm_runtime_set_autosuspend_delay(&pdev->dev, MEI_ME_RPM_TIMEOUT); | 207 | pm_runtime_set_autosuspend_delay(&pdev->dev, MEI_ME_RPM_TIMEOUT); |
| 208 | pm_runtime_use_autosuspend(&pdev->dev); | 208 | pm_runtime_use_autosuspend(&pdev->dev); |
| 209 | 209 | ||
| 210 | err = mei_register(dev); | 210 | err = mei_register(dev, &pdev->dev); |
| 211 | if (err) | 211 | if (err) |
| 212 | goto release_irq; | 212 | goto release_irq; |
| 213 | 213 | ||
| @@ -369,7 +369,7 @@ static int mei_me_pm_runtime_idle(struct device *device) | |||
| 369 | if (!dev) | 369 | if (!dev) |
| 370 | return -ENODEV; | 370 | return -ENODEV; |
| 371 | if (mei_write_is_idle(dev)) | 371 | if (mei_write_is_idle(dev)) |
| 372 | pm_schedule_suspend(device, MEI_ME_RPM_TIMEOUT * 2); | 372 | pm_runtime_autosuspend(device); |
| 373 | 373 | ||
| 374 | return -EBUSY; | 374 | return -EBUSY; |
| 375 | } | 375 | } |
diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c index 2343c6236df9..19de57368b7a 100644 --- a/drivers/misc/mei/pci-txe.c +++ b/drivers/misc/mei/pci-txe.c | |||
| @@ -149,7 +149,7 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
| 149 | pm_runtime_set_autosuspend_delay(&pdev->dev, MEI_TXI_RPM_TIMEOUT); | 149 | pm_runtime_set_autosuspend_delay(&pdev->dev, MEI_TXI_RPM_TIMEOUT); |
| 150 | pm_runtime_use_autosuspend(&pdev->dev); | 150 | pm_runtime_use_autosuspend(&pdev->dev); |
| 151 | 151 | ||
| 152 | err = mei_register(dev); | 152 | err = mei_register(dev, &pdev->dev); |
| 153 | if (err) | 153 | if (err) |
| 154 | goto release_irq; | 154 | goto release_irq; |
| 155 | 155 | ||
| @@ -306,7 +306,7 @@ static int mei_txe_pm_runtime_idle(struct device *device) | |||
| 306 | if (!dev) | 306 | if (!dev) |
| 307 | return -ENODEV; | 307 | return -ENODEV; |
| 308 | if (mei_write_is_idle(dev)) | 308 | if (mei_write_is_idle(dev)) |
| 309 | pm_schedule_suspend(device, MEI_TXI_RPM_TIMEOUT * 2); | 309 | pm_runtime_autosuspend(device); |
| 310 | 310 | ||
| 311 | return -EBUSY; | 311 | return -EBUSY; |
| 312 | } | 312 | } |
diff --git a/drivers/misc/mic/Kconfig b/drivers/misc/mic/Kconfig index 462a5b1d8651..cc4eef040c14 100644 --- a/drivers/misc/mic/Kconfig +++ b/drivers/misc/mic/Kconfig | |||
| @@ -1,8 +1,25 @@ | |||
| 1 | comment "Intel MIC Bus Driver" | ||
| 2 | |||
| 3 | config INTEL_MIC_BUS | ||
| 4 | tristate "Intel MIC Bus Driver" | ||
| 5 | depends on 64BIT && PCI && X86 && X86_DEV_DMA_OPS | ||
| 6 | help | ||
| 7 | This option is selected by any driver which registers a | ||
| 8 | device or driver on the MIC Bus, such as CONFIG_INTEL_MIC_HOST, | ||
| 9 | CONFIG_INTEL_MIC_CARD, CONFIG_INTEL_MIC_X100_DMA etc. | ||
| 10 | |||
| 11 | If you are building a host/card kernel with an Intel MIC device | ||
| 12 | then say M (recommended) or Y, else say N. If unsure say N. | ||
| 13 | |||
| 14 | More information about the Intel MIC family as well as the Linux | ||
| 15 | OS and tools for MIC to use with this driver are available from | ||
| 16 | <http://software.intel.com/en-us/mic-developer>. | ||
| 17 | |||
| 1 | comment "Intel MIC Host Driver" | 18 | comment "Intel MIC Host Driver" |
| 2 | 19 | ||
| 3 | config INTEL_MIC_HOST | 20 | config INTEL_MIC_HOST |
| 4 | tristate "Intel MIC Host Driver" | 21 | tristate "Intel MIC Host Driver" |
| 5 | depends on 64BIT && PCI && X86 | 22 | depends on 64BIT && PCI && X86 && INTEL_MIC_BUS |
| 6 | select VHOST_RING | 23 | select VHOST_RING |
| 7 | help | 24 | help |
| 8 | This enables Host Driver support for the Intel Many Integrated | 25 | This enables Host Driver support for the Intel Many Integrated |
| @@ -22,7 +39,7 @@ comment "Intel MIC Card Driver" | |||
| 22 | 39 | ||
| 23 | config INTEL_MIC_CARD | 40 | config INTEL_MIC_CARD |
| 24 | tristate "Intel MIC Card Driver" | 41 | tristate "Intel MIC Card Driver" |
| 25 | depends on 64BIT && X86 | 42 | depends on 64BIT && X86 && INTEL_MIC_BUS |
| 26 | select VIRTIO | 43 | select VIRTIO |
| 27 | help | 44 | help |
| 28 | This enables card driver support for the Intel Many Integrated | 45 | This enables card driver support for the Intel Many Integrated |
diff --git a/drivers/misc/mic/Makefile b/drivers/misc/mic/Makefile index 05b34d683a58..e9bf148755e2 100644 --- a/drivers/misc/mic/Makefile +++ b/drivers/misc/mic/Makefile | |||
| @@ -4,3 +4,4 @@ | |||
| 4 | # | 4 | # |
| 5 | obj-$(CONFIG_INTEL_MIC_HOST) += host/ | 5 | obj-$(CONFIG_INTEL_MIC_HOST) += host/ |
| 6 | obj-$(CONFIG_INTEL_MIC_CARD) += card/ | 6 | obj-$(CONFIG_INTEL_MIC_CARD) += card/ |
| 7 | obj-$(CONFIG_INTEL_MIC_BUS) += bus/ | ||
diff --git a/drivers/misc/mic/bus/Makefile b/drivers/misc/mic/bus/Makefile new file mode 100644 index 000000000000..d85c7f2a0af4 --- /dev/null +++ b/drivers/misc/mic/bus/Makefile | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | # | ||
| 2 | # Makefile - Intel MIC Linux driver. | ||
| 3 | # Copyright(c) 2014, Intel Corporation. | ||
| 4 | # | ||
| 5 | obj-$(CONFIG_INTEL_MIC_BUS) += mic_bus.o | ||
diff --git a/drivers/misc/mic/bus/mic_bus.c b/drivers/misc/mic/bus/mic_bus.c new file mode 100644 index 000000000000..961ae90aae47 --- /dev/null +++ b/drivers/misc/mic/bus/mic_bus.c | |||
| @@ -0,0 +1,218 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2014 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Intel MIC Bus driver. | ||
| 19 | * | ||
| 20 | * This implementation is very similar to the the virtio bus driver | ||
| 21 | * implementation @ drivers/virtio/virtio.c | ||
| 22 | */ | ||
| 23 | #include <linux/slab.h> | ||
| 24 | #include <linux/module.h> | ||
| 25 | #include <linux/idr.h> | ||
| 26 | #include <linux/mic_bus.h> | ||
| 27 | |||
| 28 | /* Unique numbering for mbus devices. */ | ||
| 29 | static DEFINE_IDA(mbus_index_ida); | ||
| 30 | |||
| 31 | static ssize_t device_show(struct device *d, | ||
| 32 | struct device_attribute *attr, char *buf) | ||
| 33 | { | ||
| 34 | struct mbus_device *dev = dev_to_mbus(d); | ||
| 35 | return sprintf(buf, "0x%04x\n", dev->id.device); | ||
| 36 | } | ||
| 37 | static DEVICE_ATTR_RO(device); | ||
| 38 | |||
| 39 | static ssize_t vendor_show(struct device *d, | ||
| 40 | struct device_attribute *attr, char *buf) | ||
| 41 | { | ||
| 42 | struct mbus_device *dev = dev_to_mbus(d); | ||
| 43 | return sprintf(buf, "0x%04x\n", dev->id.vendor); | ||
| 44 | } | ||
| 45 | static DEVICE_ATTR_RO(vendor); | ||
| 46 | |||
| 47 | static ssize_t modalias_show(struct device *d, | ||
| 48 | struct device_attribute *attr, char *buf) | ||
| 49 | { | ||
| 50 | struct mbus_device *dev = dev_to_mbus(d); | ||
| 51 | return sprintf(buf, "mbus:d%08Xv%08X\n", | ||
| 52 | dev->id.device, dev->id.vendor); | ||
| 53 | } | ||
| 54 | static DEVICE_ATTR_RO(modalias); | ||
| 55 | |||
| 56 | static struct attribute *mbus_dev_attrs[] = { | ||
| 57 | &dev_attr_device.attr, | ||
| 58 | &dev_attr_vendor.attr, | ||
| 59 | &dev_attr_modalias.attr, | ||
| 60 | NULL, | ||
| 61 | }; | ||
| 62 | ATTRIBUTE_GROUPS(mbus_dev); | ||
| 63 | |||
| 64 | static inline int mbus_id_match(const struct mbus_device *dev, | ||
| 65 | const struct mbus_device_id *id) | ||
| 66 | { | ||
| 67 | if (id->device != dev->id.device && id->device != MBUS_DEV_ANY_ID) | ||
| 68 | return 0; | ||
| 69 | |||
| 70 | return id->vendor == MBUS_DEV_ANY_ID || id->vendor == dev->id.vendor; | ||
| 71 | } | ||
| 72 | |||
| 73 | /* | ||
| 74 | * This looks through all the IDs a driver claims to support. If any of them | ||
| 75 | * match, we return 1 and the kernel will call mbus_dev_probe(). | ||
| 76 | */ | ||
| 77 | static int mbus_dev_match(struct device *dv, struct device_driver *dr) | ||
| 78 | { | ||
| 79 | unsigned int i; | ||
| 80 | struct mbus_device *dev = dev_to_mbus(dv); | ||
| 81 | const struct mbus_device_id *ids; | ||
| 82 | |||
| 83 | ids = drv_to_mbus(dr)->id_table; | ||
| 84 | for (i = 0; ids[i].device; i++) | ||
| 85 | if (mbus_id_match(dev, &ids[i])) | ||
| 86 | return 1; | ||
| 87 | return 0; | ||
| 88 | } | ||
| 89 | |||
| 90 | static int mbus_uevent(struct device *dv, struct kobj_uevent_env *env) | ||
| 91 | { | ||
| 92 | struct mbus_device *dev = dev_to_mbus(dv); | ||
| 93 | |||
| 94 | return add_uevent_var(env, "MODALIAS=mbus:d%08Xv%08X", | ||
| 95 | dev->id.device, dev->id.vendor); | ||
| 96 | } | ||
| 97 | |||
| 98 | static int mbus_dev_probe(struct device *d) | ||
| 99 | { | ||
| 100 | int err; | ||
| 101 | struct mbus_device *dev = dev_to_mbus(d); | ||
| 102 | struct mbus_driver *drv = drv_to_mbus(dev->dev.driver); | ||
| 103 | |||
| 104 | err = drv->probe(dev); | ||
| 105 | if (!err) | ||
| 106 | if (drv->scan) | ||
| 107 | drv->scan(dev); | ||
| 108 | return err; | ||
| 109 | } | ||
| 110 | |||
| 111 | static int mbus_dev_remove(struct device *d) | ||
| 112 | { | ||
| 113 | struct mbus_device *dev = dev_to_mbus(d); | ||
| 114 | struct mbus_driver *drv = drv_to_mbus(dev->dev.driver); | ||
| 115 | |||
| 116 | drv->remove(dev); | ||
| 117 | return 0; | ||
| 118 | } | ||
| 119 | |||
| 120 | static struct bus_type mic_bus = { | ||
| 121 | .name = "mic_bus", | ||
| 122 | .match = mbus_dev_match, | ||
| 123 | .dev_groups = mbus_dev_groups, | ||
| 124 | .uevent = mbus_uevent, | ||
| 125 | .probe = mbus_dev_probe, | ||
| 126 | .remove = mbus_dev_remove, | ||
| 127 | }; | ||
| 128 | |||
| 129 | int mbus_register_driver(struct mbus_driver *driver) | ||
| 130 | { | ||
| 131 | driver->driver.bus = &mic_bus; | ||
| 132 | return driver_register(&driver->driver); | ||
| 133 | } | ||
| 134 | EXPORT_SYMBOL_GPL(mbus_register_driver); | ||
| 135 | |||
| 136 | void mbus_unregister_driver(struct mbus_driver *driver) | ||
| 137 | { | ||
| 138 | driver_unregister(&driver->driver); | ||
| 139 | } | ||
| 140 | EXPORT_SYMBOL_GPL(mbus_unregister_driver); | ||
| 141 | |||
| 142 | static void mbus_release_dev(struct device *d) | ||
| 143 | { | ||
| 144 | struct mbus_device *mbdev = dev_to_mbus(d); | ||
| 145 | kfree(mbdev); | ||
| 146 | } | ||
| 147 | |||
| 148 | struct mbus_device * | ||
| 149 | mbus_register_device(struct device *pdev, int id, struct dma_map_ops *dma_ops, | ||
| 150 | struct mbus_hw_ops *hw_ops, void __iomem *mmio_va) | ||
| 151 | { | ||
| 152 | int ret; | ||
| 153 | struct mbus_device *mbdev; | ||
| 154 | |||
| 155 | mbdev = kzalloc(sizeof(*mbdev), GFP_KERNEL); | ||
| 156 | if (!mbdev) | ||
| 157 | return ERR_PTR(-ENOMEM); | ||
| 158 | |||
| 159 | mbdev->mmio_va = mmio_va; | ||
| 160 | mbdev->dev.parent = pdev; | ||
| 161 | mbdev->id.device = id; | ||
| 162 | mbdev->id.vendor = MBUS_DEV_ANY_ID; | ||
| 163 | mbdev->dev.archdata.dma_ops = dma_ops; | ||
| 164 | mbdev->dev.dma_mask = &mbdev->dev.coherent_dma_mask; | ||
| 165 | dma_set_mask(&mbdev->dev, DMA_BIT_MASK(64)); | ||
| 166 | mbdev->dev.release = mbus_release_dev; | ||
| 167 | mbdev->hw_ops = hw_ops; | ||
| 168 | mbdev->dev.bus = &mic_bus; | ||
| 169 | |||
| 170 | /* Assign a unique device index and hence name. */ | ||
| 171 | ret = ida_simple_get(&mbus_index_ida, 0, 0, GFP_KERNEL); | ||
| 172 | if (ret < 0) | ||
| 173 | goto free_mbdev; | ||
| 174 | |||
| 175 | mbdev->index = ret; | ||
| 176 | dev_set_name(&mbdev->dev, "mbus-dev%u", mbdev->index); | ||
| 177 | /* | ||
| 178 | * device_register() causes the bus infrastructure to look for a | ||
| 179 | * matching driver. | ||
| 180 | */ | ||
| 181 | ret = device_register(&mbdev->dev); | ||
| 182 | if (ret) | ||
| 183 | goto ida_remove; | ||
| 184 | return mbdev; | ||
| 185 | ida_remove: | ||
| 186 | ida_simple_remove(&mbus_index_ida, mbdev->index); | ||
| 187 | free_mbdev: | ||
| 188 | kfree(mbdev); | ||
| 189 | return ERR_PTR(ret); | ||
| 190 | } | ||
| 191 | EXPORT_SYMBOL_GPL(mbus_register_device); | ||
| 192 | |||
| 193 | void mbus_unregister_device(struct mbus_device *mbdev) | ||
| 194 | { | ||
| 195 | int index = mbdev->index; /* save for after device release */ | ||
| 196 | |||
| 197 | device_unregister(&mbdev->dev); | ||
| 198 | ida_simple_remove(&mbus_index_ida, index); | ||
| 199 | } | ||
| 200 | EXPORT_SYMBOL_GPL(mbus_unregister_device); | ||
| 201 | |||
| 202 | static int __init mbus_init(void) | ||
| 203 | { | ||
| 204 | return bus_register(&mic_bus); | ||
| 205 | } | ||
| 206 | |||
| 207 | static void __exit mbus_exit(void) | ||
| 208 | { | ||
| 209 | bus_unregister(&mic_bus); | ||
| 210 | ida_destroy(&mbus_index_ida); | ||
| 211 | } | ||
| 212 | |||
| 213 | core_initcall(mbus_init); | ||
| 214 | module_exit(mbus_exit); | ||
| 215 | |||
| 216 | MODULE_AUTHOR("Intel Corporation"); | ||
| 217 | MODULE_DESCRIPTION("Intel(R) MIC Bus driver"); | ||
| 218 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/misc/mic/card/mic_device.c b/drivers/misc/mic/card/mic_device.c index d0980ff96833..83819eee553b 100644 --- a/drivers/misc/mic/card/mic_device.c +++ b/drivers/misc/mic/card/mic_device.c | |||
| @@ -83,8 +83,8 @@ static int mic_shutdown_init(void) | |||
| 83 | int shutdown_db; | 83 | int shutdown_db; |
| 84 | 84 | ||
| 85 | shutdown_db = mic_next_card_db(); | 85 | shutdown_db = mic_next_card_db(); |
| 86 | shutdown_cookie = mic_request_card_irq(mic_shutdown_isr, | 86 | shutdown_cookie = mic_request_card_irq(mic_shutdown_isr, NULL, |
| 87 | "Shutdown", mdrv, shutdown_db); | 87 | "Shutdown", mdrv, shutdown_db); |
| 88 | if (IS_ERR(shutdown_cookie)) | 88 | if (IS_ERR(shutdown_cookie)) |
| 89 | rc = PTR_ERR(shutdown_cookie); | 89 | rc = PTR_ERR(shutdown_cookie); |
| 90 | else | 90 | else |
| @@ -136,7 +136,8 @@ static void mic_dp_uninit(void) | |||
| 136 | /** | 136 | /** |
| 137 | * mic_request_card_irq - request an irq. | 137 | * mic_request_card_irq - request an irq. |
| 138 | * | 138 | * |
| 139 | * @func: The callback function that handles the interrupt. | 139 | * @handler: interrupt handler passed to request_threaded_irq. |
| 140 | * @thread_fn: thread fn. passed to request_threaded_irq. | ||
| 140 | * @name: The ASCII name of the callee requesting the irq. | 141 | * @name: The ASCII name of the callee requesting the irq. |
| 141 | * @data: private data that is returned back when calling the | 142 | * @data: private data that is returned back when calling the |
| 142 | * function handler. | 143 | * function handler. |
| @@ -149,17 +150,19 @@ static void mic_dp_uninit(void) | |||
| 149 | * error code. | 150 | * error code. |
| 150 | * | 151 | * |
| 151 | */ | 152 | */ |
| 152 | struct mic_irq *mic_request_card_irq(irqreturn_t (*func)(int irq, void *data), | 153 | struct mic_irq * |
| 153 | const char *name, void *data, int index) | 154 | mic_request_card_irq(irq_handler_t handler, |
| 155 | irq_handler_t thread_fn, const char *name, | ||
| 156 | void *data, int index) | ||
| 154 | { | 157 | { |
| 155 | int rc = 0; | 158 | int rc = 0; |
| 156 | unsigned long cookie; | 159 | unsigned long cookie; |
| 157 | struct mic_driver *mdrv = g_drv; | 160 | struct mic_driver *mdrv = g_drv; |
| 158 | 161 | ||
| 159 | rc = request_irq(mic_db_to_irq(mdrv, index), func, | 162 | rc = request_threaded_irq(mic_db_to_irq(mdrv, index), handler, |
| 160 | 0, name, data); | 163 | thread_fn, 0, name, data); |
| 161 | if (rc) { | 164 | if (rc) { |
| 162 | dev_err(mdrv->dev, "request_irq failed rc = %d\n", rc); | 165 | dev_err(mdrv->dev, "request_threaded_irq failed rc = %d\n", rc); |
| 163 | goto err; | 166 | goto err; |
| 164 | } | 167 | } |
| 165 | mdrv->irq_info.irq_usage_count[index]++; | 168 | mdrv->irq_info.irq_usage_count[index]++; |
| @@ -172,9 +175,9 @@ err: | |||
| 172 | /** | 175 | /** |
| 173 | * mic_free_card_irq - free irq. | 176 | * mic_free_card_irq - free irq. |
| 174 | * | 177 | * |
| 175 | * @cookie: cookie obtained during a successful call to mic_request_irq | 178 | * @cookie: cookie obtained during a successful call to mic_request_threaded_irq |
| 176 | * @data: private data specified by the calling function during the | 179 | * @data: private data specified by the calling function during the |
| 177 | * mic_request_irq | 180 | * mic_request_threaded_irq |
| 178 | * | 181 | * |
| 179 | * returns: none. | 182 | * returns: none. |
| 180 | */ | 183 | */ |
diff --git a/drivers/misc/mic/card/mic_device.h b/drivers/misc/mic/card/mic_device.h index 306f502be95e..844be8fc9b22 100644 --- a/drivers/misc/mic/card/mic_device.h +++ b/drivers/misc/mic/card/mic_device.h | |||
| @@ -30,6 +30,8 @@ | |||
| 30 | #include <linux/workqueue.h> | 30 | #include <linux/workqueue.h> |
| 31 | #include <linux/io.h> | 31 | #include <linux/io.h> |
| 32 | #include <linux/irqreturn.h> | 32 | #include <linux/irqreturn.h> |
| 33 | #include <linux/interrupt.h> | ||
| 34 | #include <linux/mic_bus.h> | ||
| 33 | 35 | ||
| 34 | /** | 36 | /** |
| 35 | * struct mic_intr_info - Contains h/w specific interrupt sources info | 37 | * struct mic_intr_info - Contains h/w specific interrupt sources info |
| @@ -70,6 +72,7 @@ struct mic_device { | |||
| 70 | * @hotplug_work: Hot plug work for adding/removing virtio devices. | 72 | * @hotplug_work: Hot plug work for adding/removing virtio devices. |
| 71 | * @irq_info: The OS specific irq information | 73 | * @irq_info: The OS specific irq information |
| 72 | * @intr_info: H/W specific interrupt information. | 74 | * @intr_info: H/W specific interrupt information. |
| 75 | * @dma_mbdev: dma device on the MIC virtual bus. | ||
| 73 | */ | 76 | */ |
| 74 | struct mic_driver { | 77 | struct mic_driver { |
| 75 | char name[20]; | 78 | char name[20]; |
| @@ -80,6 +83,7 @@ struct mic_driver { | |||
| 80 | struct work_struct hotplug_work; | 83 | struct work_struct hotplug_work; |
| 81 | struct mic_irq_info irq_info; | 84 | struct mic_irq_info irq_info; |
| 82 | struct mic_intr_info intr_info; | 85 | struct mic_intr_info intr_info; |
| 86 | struct mbus_device *dma_mbdev; | ||
| 83 | }; | 87 | }; |
| 84 | 88 | ||
| 85 | /** | 89 | /** |
| @@ -116,8 +120,9 @@ mic_mmio_write(struct mic_mw *mw, u32 val, u32 offset) | |||
| 116 | int mic_driver_init(struct mic_driver *mdrv); | 120 | int mic_driver_init(struct mic_driver *mdrv); |
| 117 | void mic_driver_uninit(struct mic_driver *mdrv); | 121 | void mic_driver_uninit(struct mic_driver *mdrv); |
| 118 | int mic_next_card_db(void); | 122 | int mic_next_card_db(void); |
| 119 | struct mic_irq *mic_request_card_irq(irqreturn_t (*func)(int irq, void *data), | 123 | struct mic_irq * |
| 120 | const char *name, void *data, int intr_src); | 124 | mic_request_card_irq(irq_handler_t handler, irq_handler_t thread_fn, |
| 125 | const char *name, void *data, int intr_src); | ||
| 121 | void mic_free_card_irq(struct mic_irq *cookie, void *data); | 126 | void mic_free_card_irq(struct mic_irq *cookie, void *data); |
| 122 | u32 mic_read_spad(struct mic_device *mdev, unsigned int idx); | 127 | u32 mic_read_spad(struct mic_device *mdev, unsigned int idx); |
| 123 | void mic_send_intr(struct mic_device *mdev, int doorbell); | 128 | void mic_send_intr(struct mic_device *mdev, int doorbell); |
diff --git a/drivers/misc/mic/card/mic_virtio.c b/drivers/misc/mic/card/mic_virtio.c index 653799b96bfa..f14b60080c21 100644 --- a/drivers/misc/mic/card/mic_virtio.c +++ b/drivers/misc/mic/card/mic_virtio.c | |||
| @@ -417,7 +417,7 @@ static int mic_add_device(struct mic_device_desc __iomem *d, | |||
| 417 | 417 | ||
| 418 | virtio_db = mic_next_card_db(); | 418 | virtio_db = mic_next_card_db(); |
| 419 | mvdev->virtio_cookie = mic_request_card_irq(mic_virtio_intr_handler, | 419 | mvdev->virtio_cookie = mic_request_card_irq(mic_virtio_intr_handler, |
| 420 | "virtio intr", mvdev, virtio_db); | 420 | NULL, "virtio intr", mvdev, virtio_db); |
| 421 | if (IS_ERR(mvdev->virtio_cookie)) { | 421 | if (IS_ERR(mvdev->virtio_cookie)) { |
| 422 | ret = PTR_ERR(mvdev->virtio_cookie); | 422 | ret = PTR_ERR(mvdev->virtio_cookie); |
| 423 | goto kfree; | 423 | goto kfree; |
| @@ -606,8 +606,9 @@ int mic_devices_init(struct mic_driver *mdrv) | |||
| 606 | mic_scan_devices(mdrv, !REMOVE_DEVICES); | 606 | mic_scan_devices(mdrv, !REMOVE_DEVICES); |
| 607 | 607 | ||
| 608 | config_db = mic_next_card_db(); | 608 | config_db = mic_next_card_db(); |
| 609 | virtio_config_cookie = mic_request_card_irq(mic_extint_handler, | 609 | virtio_config_cookie = mic_request_card_irq(mic_extint_handler, NULL, |
| 610 | "virtio_config_intr", mdrv, config_db); | 610 | "virtio_config_intr", mdrv, |
| 611 | config_db); | ||
| 611 | if (IS_ERR(virtio_config_cookie)) { | 612 | if (IS_ERR(virtio_config_cookie)) { |
| 612 | rc = PTR_ERR(virtio_config_cookie); | 613 | rc = PTR_ERR(virtio_config_cookie); |
| 613 | goto exit; | 614 | goto exit; |
diff --git a/drivers/misc/mic/card/mic_x100.c b/drivers/misc/mic/card/mic_x100.c index 2868945c9a4d..9d57545d64f6 100644 --- a/drivers/misc/mic/card/mic_x100.c +++ b/drivers/misc/mic/card/mic_x100.c | |||
| @@ -148,6 +148,47 @@ void mic_card_unmap(struct mic_device *mdev, void __iomem *addr) | |||
| 148 | iounmap(addr); | 148 | iounmap(addr); |
| 149 | } | 149 | } |
| 150 | 150 | ||
| 151 | static inline struct mic_driver *mbdev_to_mdrv(struct mbus_device *mbdev) | ||
| 152 | { | ||
| 153 | return dev_get_drvdata(mbdev->dev.parent); | ||
| 154 | } | ||
| 155 | |||
| 156 | static struct mic_irq * | ||
| 157 | _mic_request_threaded_irq(struct mbus_device *mbdev, | ||
| 158 | irq_handler_t handler, irq_handler_t thread_fn, | ||
| 159 | const char *name, void *data, int intr_src) | ||
| 160 | { | ||
| 161 | int rc = 0; | ||
| 162 | unsigned int irq = intr_src; | ||
| 163 | unsigned long cookie = irq; | ||
| 164 | |||
| 165 | rc = request_threaded_irq(irq, handler, thread_fn, 0, name, data); | ||
| 166 | if (rc) { | ||
| 167 | dev_err(mbdev_to_mdrv(mbdev)->dev, | ||
| 168 | "request_threaded_irq failed rc = %d\n", rc); | ||
| 169 | return ERR_PTR(rc); | ||
| 170 | } | ||
| 171 | return (struct mic_irq *)cookie; | ||
| 172 | } | ||
| 173 | |||
| 174 | static void _mic_free_irq(struct mbus_device *mbdev, | ||
| 175 | struct mic_irq *cookie, void *data) | ||
| 176 | { | ||
| 177 | unsigned long irq = (unsigned long)cookie; | ||
| 178 | free_irq(irq, data); | ||
| 179 | } | ||
| 180 | |||
| 181 | static void _mic_ack_interrupt(struct mbus_device *mbdev, int num) | ||
| 182 | { | ||
| 183 | mic_ack_interrupt(&mbdev_to_mdrv(mbdev)->mdev); | ||
| 184 | } | ||
| 185 | |||
| 186 | static struct mbus_hw_ops mbus_hw_ops = { | ||
| 187 | .request_threaded_irq = _mic_request_threaded_irq, | ||
| 188 | .free_irq = _mic_free_irq, | ||
| 189 | .ack_interrupt = _mic_ack_interrupt, | ||
| 190 | }; | ||
| 191 | |||
| 151 | static int __init mic_probe(struct platform_device *pdev) | 192 | static int __init mic_probe(struct platform_device *pdev) |
| 152 | { | 193 | { |
| 153 | struct mic_driver *mdrv = &g_drv; | 194 | struct mic_driver *mdrv = &g_drv; |
| @@ -159,32 +200,41 @@ static int __init mic_probe(struct platform_device *pdev) | |||
| 159 | 200 | ||
| 160 | mdev->mmio.pa = MIC_X100_MMIO_BASE; | 201 | mdev->mmio.pa = MIC_X100_MMIO_BASE; |
| 161 | mdev->mmio.len = MIC_X100_MMIO_LEN; | 202 | mdev->mmio.len = MIC_X100_MMIO_LEN; |
| 162 | mdev->mmio.va = ioremap(MIC_X100_MMIO_BASE, MIC_X100_MMIO_LEN); | 203 | mdev->mmio.va = devm_ioremap(&pdev->dev, MIC_X100_MMIO_BASE, |
| 204 | MIC_X100_MMIO_LEN); | ||
| 163 | if (!mdev->mmio.va) { | 205 | if (!mdev->mmio.va) { |
| 164 | dev_err(&pdev->dev, "Cannot remap MMIO BAR\n"); | 206 | dev_err(&pdev->dev, "Cannot remap MMIO BAR\n"); |
| 165 | rc = -EIO; | 207 | rc = -EIO; |
| 166 | goto done; | 208 | goto done; |
| 167 | } | 209 | } |
| 168 | mic_hw_intr_init(mdrv); | 210 | mic_hw_intr_init(mdrv); |
| 211 | platform_set_drvdata(pdev, mdrv); | ||
| 212 | mdrv->dma_mbdev = mbus_register_device(mdrv->dev, MBUS_DEV_DMA_MIC, | ||
| 213 | NULL, &mbus_hw_ops, | ||
| 214 | mdrv->mdev.mmio.va); | ||
| 215 | if (IS_ERR(mdrv->dma_mbdev)) { | ||
| 216 | rc = PTR_ERR(mdrv->dma_mbdev); | ||
| 217 | dev_err(&pdev->dev, "mbus_add_device failed rc %d\n", rc); | ||
| 218 | goto done; | ||
| 219 | } | ||
| 169 | rc = mic_driver_init(mdrv); | 220 | rc = mic_driver_init(mdrv); |
| 170 | if (rc) { | 221 | if (rc) { |
| 171 | dev_err(&pdev->dev, "mic_driver_init failed rc %d\n", rc); | 222 | dev_err(&pdev->dev, "mic_driver_init failed rc %d\n", rc); |
| 172 | goto iounmap; | 223 | goto remove_dma; |
| 173 | } | 224 | } |
| 174 | done: | 225 | done: |
| 175 | return rc; | 226 | return rc; |
| 176 | iounmap: | 227 | remove_dma: |
| 177 | iounmap(mdev->mmio.va); | 228 | mbus_unregister_device(mdrv->dma_mbdev); |
| 178 | return rc; | 229 | return rc; |
| 179 | } | 230 | } |
| 180 | 231 | ||
| 181 | static int mic_remove(struct platform_device *pdev) | 232 | static int mic_remove(struct platform_device *pdev) |
| 182 | { | 233 | { |
| 183 | struct mic_driver *mdrv = &g_drv; | 234 | struct mic_driver *mdrv = &g_drv; |
| 184 | struct mic_device *mdev = &mdrv->mdev; | ||
| 185 | 235 | ||
| 186 | mic_driver_uninit(mdrv); | 236 | mic_driver_uninit(mdrv); |
| 187 | iounmap(mdev->mmio.va); | 237 | mbus_unregister_device(mdrv->dma_mbdev); |
| 188 | return 0; | 238 | return 0; |
| 189 | } | 239 | } |
| 190 | 240 | ||
diff --git a/drivers/misc/mic/host/mic_boot.c b/drivers/misc/mic/host/mic_boot.c index b75c6b5cc20f..ff2b0fb1a6be 100644 --- a/drivers/misc/mic/host/mic_boot.c +++ b/drivers/misc/mic/host/mic_boot.c | |||
| @@ -23,11 +23,70 @@ | |||
| 23 | #include <linux/pci.h> | 23 | #include <linux/pci.h> |
| 24 | 24 | ||
| 25 | #include <linux/mic_common.h> | 25 | #include <linux/mic_common.h> |
| 26 | #include <linux/mic_bus.h> | ||
| 26 | #include "../common/mic_dev.h" | 27 | #include "../common/mic_dev.h" |
| 27 | #include "mic_device.h" | 28 | #include "mic_device.h" |
| 28 | #include "mic_smpt.h" | 29 | #include "mic_smpt.h" |
| 29 | #include "mic_virtio.h" | 30 | #include "mic_virtio.h" |
| 30 | 31 | ||
| 32 | static inline struct mic_device *mbdev_to_mdev(struct mbus_device *mbdev) | ||
| 33 | { | ||
| 34 | return dev_get_drvdata(mbdev->dev.parent); | ||
| 35 | } | ||
| 36 | |||
| 37 | static dma_addr_t | ||
| 38 | mic_dma_map_page(struct device *dev, struct page *page, | ||
| 39 | unsigned long offset, size_t size, enum dma_data_direction dir, | ||
| 40 | struct dma_attrs *attrs) | ||
| 41 | { | ||
| 42 | void *va = phys_to_virt(page_to_phys(page)) + offset; | ||
| 43 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
| 44 | |||
| 45 | return mic_map_single(mdev, va, size); | ||
| 46 | } | ||
| 47 | |||
| 48 | static void | ||
| 49 | mic_dma_unmap_page(struct device *dev, dma_addr_t dma_addr, | ||
| 50 | size_t size, enum dma_data_direction dir, | ||
| 51 | struct dma_attrs *attrs) | ||
| 52 | { | ||
| 53 | struct mic_device *mdev = dev_get_drvdata(dev->parent); | ||
| 54 | mic_unmap_single(mdev, dma_addr, size); | ||
| 55 | } | ||
| 56 | |||
| 57 | static struct dma_map_ops mic_dma_ops = { | ||
| 58 | .map_page = mic_dma_map_page, | ||
| 59 | .unmap_page = mic_dma_unmap_page, | ||
| 60 | }; | ||
| 61 | |||
| 62 | static struct mic_irq * | ||
| 63 | _mic_request_threaded_irq(struct mbus_device *mbdev, | ||
| 64 | irq_handler_t handler, irq_handler_t thread_fn, | ||
| 65 | const char *name, void *data, int intr_src) | ||
| 66 | { | ||
| 67 | return mic_request_threaded_irq(mbdev_to_mdev(mbdev), handler, | ||
| 68 | thread_fn, name, data, | ||
| 69 | intr_src, MIC_INTR_DMA); | ||
| 70 | } | ||
| 71 | |||
| 72 | static void _mic_free_irq(struct mbus_device *mbdev, | ||
| 73 | struct mic_irq *cookie, void *data) | ||
| 74 | { | ||
| 75 | return mic_free_irq(mbdev_to_mdev(mbdev), cookie, data); | ||
| 76 | } | ||
| 77 | |||
| 78 | static void _mic_ack_interrupt(struct mbus_device *mbdev, int num) | ||
| 79 | { | ||
| 80 | struct mic_device *mdev = mbdev_to_mdev(mbdev); | ||
| 81 | mdev->ops->intr_workarounds(mdev); | ||
| 82 | } | ||
| 83 | |||
| 84 | static struct mbus_hw_ops mbus_hw_ops = { | ||
| 85 | .request_threaded_irq = _mic_request_threaded_irq, | ||
| 86 | .free_irq = _mic_free_irq, | ||
| 87 | .ack_interrupt = _mic_ack_interrupt, | ||
| 88 | }; | ||
| 89 | |||
| 31 | /** | 90 | /** |
| 32 | * mic_reset - Reset the MIC device. | 91 | * mic_reset - Reset the MIC device. |
| 33 | * @mdev: pointer to mic_device instance | 92 | * @mdev: pointer to mic_device instance |
| @@ -95,9 +154,21 @@ retry: | |||
| 95 | */ | 154 | */ |
| 96 | goto retry; | 155 | goto retry; |
| 97 | } | 156 | } |
| 157 | mdev->dma_mbdev = mbus_register_device(mdev->sdev->parent, | ||
| 158 | MBUS_DEV_DMA_HOST, &mic_dma_ops, | ||
| 159 | &mbus_hw_ops, mdev->mmio.va); | ||
| 160 | if (IS_ERR(mdev->dma_mbdev)) { | ||
| 161 | rc = PTR_ERR(mdev->dma_mbdev); | ||
| 162 | goto unlock_ret; | ||
| 163 | } | ||
| 164 | mdev->dma_ch = mic_request_dma_chan(mdev); | ||
| 165 | if (!mdev->dma_ch) { | ||
| 166 | rc = -ENXIO; | ||
| 167 | goto dma_remove; | ||
| 168 | } | ||
| 98 | rc = mdev->ops->load_mic_fw(mdev, buf); | 169 | rc = mdev->ops->load_mic_fw(mdev, buf); |
| 99 | if (rc) | 170 | if (rc) |
| 100 | goto unlock_ret; | 171 | goto dma_release; |
| 101 | mic_smpt_restore(mdev); | 172 | mic_smpt_restore(mdev); |
| 102 | mic_intr_restore(mdev); | 173 | mic_intr_restore(mdev); |
| 103 | mdev->intr_ops->enable_interrupts(mdev); | 174 | mdev->intr_ops->enable_interrupts(mdev); |
| @@ -105,6 +176,11 @@ retry: | |||
| 105 | mdev->ops->write_spad(mdev, MIC_DPHI_SPAD, mdev->dp_dma_addr >> 32); | 176 | mdev->ops->write_spad(mdev, MIC_DPHI_SPAD, mdev->dp_dma_addr >> 32); |
| 106 | mdev->ops->send_firmware_intr(mdev); | 177 | mdev->ops->send_firmware_intr(mdev); |
| 107 | mic_set_state(mdev, MIC_ONLINE); | 178 | mic_set_state(mdev, MIC_ONLINE); |
| 179 | goto unlock_ret; | ||
| 180 | dma_release: | ||
| 181 | dma_release_channel(mdev->dma_ch); | ||
| 182 | dma_remove: | ||
| 183 | mbus_unregister_device(mdev->dma_mbdev); | ||
| 108 | unlock_ret: | 184 | unlock_ret: |
| 109 | mutex_unlock(&mdev->mic_mutex); | 185 | mutex_unlock(&mdev->mic_mutex); |
| 110 | return rc; | 186 | return rc; |
| @@ -122,6 +198,11 @@ void mic_stop(struct mic_device *mdev, bool force) | |||
| 122 | mutex_lock(&mdev->mic_mutex); | 198 | mutex_lock(&mdev->mic_mutex); |
| 123 | if (MIC_OFFLINE != mdev->state || force) { | 199 | if (MIC_OFFLINE != mdev->state || force) { |
| 124 | mic_virtio_reset_devices(mdev); | 200 | mic_virtio_reset_devices(mdev); |
| 201 | if (mdev->dma_ch) { | ||
| 202 | dma_release_channel(mdev->dma_ch); | ||
| 203 | mdev->dma_ch = NULL; | ||
| 204 | } | ||
| 205 | mbus_unregister_device(mdev->dma_mbdev); | ||
| 125 | mic_bootparam_init(mdev); | 206 | mic_bootparam_init(mdev); |
| 126 | mic_reset(mdev); | 207 | mic_reset(mdev); |
| 127 | if (MIC_RESET_FAILED == mdev->state) | 208 | if (MIC_RESET_FAILED == mdev->state) |
diff --git a/drivers/misc/mic/host/mic_device.h b/drivers/misc/mic/host/mic_device.h index 0398c696d257..016bd15a7bd1 100644 --- a/drivers/misc/mic/host/mic_device.h +++ b/drivers/misc/mic/host/mic_device.h | |||
| @@ -25,6 +25,8 @@ | |||
| 25 | #include <linux/idr.h> | 25 | #include <linux/idr.h> |
| 26 | #include <linux/notifier.h> | 26 | #include <linux/notifier.h> |
| 27 | #include <linux/irqreturn.h> | 27 | #include <linux/irqreturn.h> |
| 28 | #include <linux/dmaengine.h> | ||
| 29 | #include <linux/mic_bus.h> | ||
| 28 | 30 | ||
| 29 | #include "mic_intr.h" | 31 | #include "mic_intr.h" |
| 30 | 32 | ||
| @@ -87,6 +89,8 @@ enum mic_stepping { | |||
| 87 | * @cdev: Character device for MIC. | 89 | * @cdev: Character device for MIC. |
| 88 | * @vdev_list: list of virtio devices. | 90 | * @vdev_list: list of virtio devices. |
| 89 | * @pm_notifier: Handles PM notifications from the OS. | 91 | * @pm_notifier: Handles PM notifications from the OS. |
| 92 | * @dma_mbdev: MIC BUS DMA device. | ||
| 93 | * @dma_ch: DMA channel reserved by this driver for use by virtio devices. | ||
| 90 | */ | 94 | */ |
| 91 | struct mic_device { | 95 | struct mic_device { |
| 92 | struct mic_mw mmio; | 96 | struct mic_mw mmio; |
| @@ -124,6 +128,8 @@ struct mic_device { | |||
| 124 | struct cdev cdev; | 128 | struct cdev cdev; |
| 125 | struct list_head vdev_list; | 129 | struct list_head vdev_list; |
| 126 | struct notifier_block pm_notifier; | 130 | struct notifier_block pm_notifier; |
| 131 | struct mbus_device *dma_mbdev; | ||
| 132 | struct dma_chan *dma_ch; | ||
| 127 | }; | 133 | }; |
| 128 | 134 | ||
| 129 | /** | 135 | /** |
| @@ -144,6 +150,7 @@ struct mic_device { | |||
| 144 | * @load_mic_fw: Load firmware segments required to boot the card | 150 | * @load_mic_fw: Load firmware segments required to boot the card |
| 145 | * into card memory. This includes the kernel, command line, ramdisk etc. | 151 | * into card memory. This includes the kernel, command line, ramdisk etc. |
| 146 | * @get_postcode: Get post code status from firmware. | 152 | * @get_postcode: Get post code status from firmware. |
| 153 | * @dma_filter: DMA filter function to be used. | ||
| 147 | */ | 154 | */ |
| 148 | struct mic_hw_ops { | 155 | struct mic_hw_ops { |
| 149 | u8 aper_bar; | 156 | u8 aper_bar; |
| @@ -159,6 +166,7 @@ struct mic_hw_ops { | |||
| 159 | void (*send_firmware_intr)(struct mic_device *mdev); | 166 | void (*send_firmware_intr)(struct mic_device *mdev); |
| 160 | int (*load_mic_fw)(struct mic_device *mdev, const char *buf); | 167 | int (*load_mic_fw)(struct mic_device *mdev, const char *buf); |
| 161 | u32 (*get_postcode)(struct mic_device *mdev); | 168 | u32 (*get_postcode)(struct mic_device *mdev); |
| 169 | bool (*dma_filter)(struct dma_chan *chan, void *param); | ||
| 162 | }; | 170 | }; |
| 163 | 171 | ||
| 164 | /** | 172 | /** |
| @@ -187,6 +195,22 @@ mic_mmio_write(struct mic_mw *mw, u32 val, u32 offset) | |||
| 187 | iowrite32(val, mw->va + offset); | 195 | iowrite32(val, mw->va + offset); |
| 188 | } | 196 | } |
| 189 | 197 | ||
| 198 | static inline struct dma_chan *mic_request_dma_chan(struct mic_device *mdev) | ||
| 199 | { | ||
| 200 | dma_cap_mask_t mask; | ||
| 201 | struct dma_chan *chan; | ||
| 202 | |||
| 203 | dma_cap_zero(mask); | ||
| 204 | dma_cap_set(DMA_MEMCPY, mask); | ||
| 205 | chan = dma_request_channel(mask, mdev->ops->dma_filter, | ||
| 206 | mdev->sdev->parent); | ||
| 207 | if (chan) | ||
| 208 | return chan; | ||
| 209 | dev_err(mdev->sdev->parent, "%s %d unable to acquire channel\n", | ||
| 210 | __func__, __LINE__); | ||
| 211 | return NULL; | ||
| 212 | } | ||
| 213 | |||
| 190 | void mic_sysfs_init(struct mic_device *mdev); | 214 | void mic_sysfs_init(struct mic_device *mdev); |
| 191 | int mic_start(struct mic_device *mdev, const char *buf); | 215 | int mic_start(struct mic_device *mdev, const char *buf); |
| 192 | void mic_stop(struct mic_device *mdev, bool force); | 216 | void mic_stop(struct mic_device *mdev, bool force); |
diff --git a/drivers/misc/mic/host/mic_intr.c b/drivers/misc/mic/host/mic_intr.c index dbc5afde1392..d686f2846ac7 100644 --- a/drivers/misc/mic/host/mic_intr.c +++ b/drivers/misc/mic/host/mic_intr.c | |||
| @@ -24,28 +24,29 @@ | |||
| 24 | #include "../common/mic_dev.h" | 24 | #include "../common/mic_dev.h" |
| 25 | #include "mic_device.h" | 25 | #include "mic_device.h" |
| 26 | 26 | ||
| 27 | /* | 27 | static irqreturn_t mic_thread_fn(int irq, void *dev) |
| 28 | * mic_invoke_callback - Invoke callback functions registered for | ||
| 29 | * the corresponding source id. | ||
| 30 | * | ||
| 31 | * @mdev: pointer to the mic_device instance | ||
| 32 | * @idx: The interrupt source id. | ||
| 33 | * | ||
| 34 | * Returns none. | ||
| 35 | */ | ||
| 36 | static inline void mic_invoke_callback(struct mic_device *mdev, int idx) | ||
| 37 | { | 28 | { |
| 29 | struct mic_device *mdev = dev; | ||
| 30 | struct mic_intr_info *intr_info = mdev->intr_info; | ||
| 31 | struct mic_irq_info *irq_info = &mdev->irq_info; | ||
| 38 | struct mic_intr_cb *intr_cb; | 32 | struct mic_intr_cb *intr_cb; |
| 39 | struct pci_dev *pdev = container_of(mdev->sdev->parent, | 33 | struct pci_dev *pdev = container_of(mdev->sdev->parent, |
| 40 | struct pci_dev, dev); | 34 | struct pci_dev, dev); |
| 35 | int i; | ||
| 41 | 36 | ||
| 42 | spin_lock(&mdev->irq_info.mic_intr_lock); | 37 | spin_lock(&irq_info->mic_thread_lock); |
| 43 | list_for_each_entry(intr_cb, &mdev->irq_info.cb_list[idx], list) | 38 | for (i = intr_info->intr_start_idx[MIC_INTR_DB]; |
| 44 | if (intr_cb->func) | 39 | i < intr_info->intr_len[MIC_INTR_DB]; i++) |
| 45 | intr_cb->func(pdev->irq, intr_cb->data); | 40 | if (test_and_clear_bit(i, &irq_info->mask)) { |
| 46 | spin_unlock(&mdev->irq_info.mic_intr_lock); | 41 | list_for_each_entry(intr_cb, &irq_info->cb_list[i], |
| 42 | list) | ||
| 43 | if (intr_cb->thread_fn) | ||
| 44 | intr_cb->thread_fn(pdev->irq, | ||
| 45 | intr_cb->data); | ||
| 46 | } | ||
| 47 | spin_unlock(&irq_info->mic_thread_lock); | ||
| 48 | return IRQ_HANDLED; | ||
| 47 | } | 49 | } |
| 48 | |||
| 49 | /** | 50 | /** |
| 50 | * mic_interrupt - Generic interrupt handler for | 51 | * mic_interrupt - Generic interrupt handler for |
| 51 | * MSI and INTx based interrupts. | 52 | * MSI and INTx based interrupts. |
| @@ -53,7 +54,11 @@ static inline void mic_invoke_callback(struct mic_device *mdev, int idx) | |||
| 53 | static irqreturn_t mic_interrupt(int irq, void *dev) | 54 | static irqreturn_t mic_interrupt(int irq, void *dev) |
| 54 | { | 55 | { |
| 55 | struct mic_device *mdev = dev; | 56 | struct mic_device *mdev = dev; |
| 56 | struct mic_intr_info *info = mdev->intr_info; | 57 | struct mic_intr_info *intr_info = mdev->intr_info; |
| 58 | struct mic_irq_info *irq_info = &mdev->irq_info; | ||
| 59 | struct mic_intr_cb *intr_cb; | ||
| 60 | struct pci_dev *pdev = container_of(mdev->sdev->parent, | ||
| 61 | struct pci_dev, dev); | ||
| 57 | u32 mask; | 62 | u32 mask; |
| 58 | int i; | 63 | int i; |
| 59 | 64 | ||
| @@ -61,12 +66,19 @@ static irqreturn_t mic_interrupt(int irq, void *dev) | |||
| 61 | if (!mask) | 66 | if (!mask) |
| 62 | return IRQ_NONE; | 67 | return IRQ_NONE; |
| 63 | 68 | ||
| 64 | for (i = info->intr_start_idx[MIC_INTR_DB]; | 69 | spin_lock(&irq_info->mic_intr_lock); |
| 65 | i < info->intr_len[MIC_INTR_DB]; i++) | 70 | for (i = intr_info->intr_start_idx[MIC_INTR_DB]; |
| 66 | if (mask & BIT(i)) | 71 | i < intr_info->intr_len[MIC_INTR_DB]; i++) |
| 67 | mic_invoke_callback(mdev, i); | 72 | if (mask & BIT(i)) { |
| 68 | 73 | list_for_each_entry(intr_cb, &irq_info->cb_list[i], | |
| 69 | return IRQ_HANDLED; | 74 | list) |
| 75 | if (intr_cb->handler) | ||
| 76 | intr_cb->handler(pdev->irq, | ||
| 77 | intr_cb->data); | ||
| 78 | set_bit(i, &irq_info->mask); | ||
| 79 | } | ||
| 80 | spin_unlock(&irq_info->mic_intr_lock); | ||
| 81 | return IRQ_WAKE_THREAD; | ||
| 70 | } | 82 | } |
| 71 | 83 | ||
| 72 | /* Return the interrupt offset from the index. Index is 0 based. */ | 84 | /* Return the interrupt offset from the index. Index is 0 based. */ |
| @@ -99,14 +111,15 @@ static struct msix_entry *mic_get_available_vector(struct mic_device *mdev) | |||
| 99 | * | 111 | * |
| 100 | * @mdev: pointer to the mic_device instance | 112 | * @mdev: pointer to the mic_device instance |
| 101 | * @idx: The source id to be registered. | 113 | * @idx: The source id to be registered. |
| 102 | * @func: The function to be called when the source id receives | 114 | * @handler: The function to be called when the source id receives |
| 103 | * the interrupt. | 115 | * the interrupt. |
| 116 | * @thread_fn: thread fn. corresponding to the handler | ||
| 104 | * @data: Private data of the requester. | 117 | * @data: Private data of the requester. |
| 105 | * Return the callback structure that was registered or an | 118 | * Return the callback structure that was registered or an |
| 106 | * appropriate error on failure. | 119 | * appropriate error on failure. |
| 107 | */ | 120 | */ |
| 108 | static struct mic_intr_cb *mic_register_intr_callback(struct mic_device *mdev, | 121 | static struct mic_intr_cb *mic_register_intr_callback(struct mic_device *mdev, |
| 109 | u8 idx, irqreturn_t (*func) (int irq, void *dev), | 122 | u8 idx, irq_handler_t handler, irq_handler_t thread_fn, |
| 110 | void *data) | 123 | void *data) |
| 111 | { | 124 | { |
| 112 | struct mic_intr_cb *intr_cb; | 125 | struct mic_intr_cb *intr_cb; |
| @@ -117,7 +130,8 @@ static struct mic_intr_cb *mic_register_intr_callback(struct mic_device *mdev, | |||
| 117 | if (!intr_cb) | 130 | if (!intr_cb) |
| 118 | return ERR_PTR(-ENOMEM); | 131 | return ERR_PTR(-ENOMEM); |
| 119 | 132 | ||
| 120 | intr_cb->func = func; | 133 | intr_cb->handler = handler; |
| 134 | intr_cb->thread_fn = thread_fn; | ||
| 121 | intr_cb->data = data; | 135 | intr_cb->data = data; |
| 122 | intr_cb->cb_id = ida_simple_get(&mdev->irq_info.cb_ida, | 136 | intr_cb->cb_id = ida_simple_get(&mdev->irq_info.cb_ida, |
| 123 | 0, 0, GFP_KERNEL); | 137 | 0, 0, GFP_KERNEL); |
| @@ -126,9 +140,11 @@ static struct mic_intr_cb *mic_register_intr_callback(struct mic_device *mdev, | |||
| 126 | goto ida_fail; | 140 | goto ida_fail; |
| 127 | } | 141 | } |
| 128 | 142 | ||
| 143 | spin_lock(&mdev->irq_info.mic_thread_lock); | ||
| 129 | spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); | 144 | spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); |
| 130 | list_add_tail(&intr_cb->list, &mdev->irq_info.cb_list[idx]); | 145 | list_add_tail(&intr_cb->list, &mdev->irq_info.cb_list[idx]); |
| 131 | spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); | 146 | spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); |
| 147 | spin_unlock(&mdev->irq_info.mic_thread_lock); | ||
| 132 | 148 | ||
| 133 | return intr_cb; | 149 | return intr_cb; |
| 134 | ida_fail: | 150 | ida_fail: |
| @@ -152,8 +168,9 @@ static u8 mic_unregister_intr_callback(struct mic_device *mdev, u32 idx) | |||
| 152 | unsigned long flags; | 168 | unsigned long flags; |
| 153 | int i; | 169 | int i; |
| 154 | 170 | ||
| 171 | spin_lock(&mdev->irq_info.mic_thread_lock); | ||
| 172 | spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); | ||
| 155 | for (i = 0; i < MIC_NUM_OFFSETS; i++) { | 173 | for (i = 0; i < MIC_NUM_OFFSETS; i++) { |
| 156 | spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); | ||
| 157 | list_for_each_safe(pos, tmp, &mdev->irq_info.cb_list[i]) { | 174 | list_for_each_safe(pos, tmp, &mdev->irq_info.cb_list[i]) { |
| 158 | intr_cb = list_entry(pos, struct mic_intr_cb, list); | 175 | intr_cb = list_entry(pos, struct mic_intr_cb, list); |
| 159 | if (intr_cb->cb_id == idx) { | 176 | if (intr_cb->cb_id == idx) { |
| @@ -163,11 +180,13 @@ static u8 mic_unregister_intr_callback(struct mic_device *mdev, u32 idx) | |||
| 163 | kfree(intr_cb); | 180 | kfree(intr_cb); |
| 164 | spin_unlock_irqrestore( | 181 | spin_unlock_irqrestore( |
| 165 | &mdev->irq_info.mic_intr_lock, flags); | 182 | &mdev->irq_info.mic_intr_lock, flags); |
| 183 | spin_unlock(&mdev->irq_info.mic_thread_lock); | ||
| 166 | return i; | 184 | return i; |
| 167 | } | 185 | } |
| 168 | } | 186 | } |
| 169 | spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); | ||
| 170 | } | 187 | } |
| 188 | spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); | ||
| 189 | spin_unlock(&mdev->irq_info.mic_thread_lock); | ||
| 171 | return MIC_NUM_OFFSETS; | 190 | return MIC_NUM_OFFSETS; |
| 172 | } | 191 | } |
| 173 | 192 | ||
| @@ -242,6 +261,7 @@ static int mic_setup_callbacks(struct mic_device *mdev) | |||
| 242 | INIT_LIST_HEAD(&mdev->irq_info.cb_list[i]); | 261 | INIT_LIST_HEAD(&mdev->irq_info.cb_list[i]); |
| 243 | ida_init(&mdev->irq_info.cb_ida); | 262 | ida_init(&mdev->irq_info.cb_ida); |
| 244 | spin_lock_init(&mdev->irq_info.mic_intr_lock); | 263 | spin_lock_init(&mdev->irq_info.mic_intr_lock); |
| 264 | spin_lock_init(&mdev->irq_info.mic_thread_lock); | ||
| 245 | return 0; | 265 | return 0; |
| 246 | } | 266 | } |
| 247 | 267 | ||
| @@ -258,14 +278,12 @@ static void mic_release_callbacks(struct mic_device *mdev) | |||
| 258 | struct mic_intr_cb *intr_cb; | 278 | struct mic_intr_cb *intr_cb; |
| 259 | int i; | 279 | int i; |
| 260 | 280 | ||
| 281 | spin_lock(&mdev->irq_info.mic_thread_lock); | ||
| 282 | spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); | ||
| 261 | for (i = 0; i < MIC_NUM_OFFSETS; i++) { | 283 | for (i = 0; i < MIC_NUM_OFFSETS; i++) { |
| 262 | spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); | ||
| 263 | 284 | ||
| 264 | if (list_empty(&mdev->irq_info.cb_list[i])) { | 285 | if (list_empty(&mdev->irq_info.cb_list[i])) |
| 265 | spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, | ||
| 266 | flags); | ||
| 267 | break; | 286 | break; |
| 268 | } | ||
| 269 | 287 | ||
| 270 | list_for_each_safe(pos, tmp, &mdev->irq_info.cb_list[i]) { | 288 | list_for_each_safe(pos, tmp, &mdev->irq_info.cb_list[i]) { |
| 271 | intr_cb = list_entry(pos, struct mic_intr_cb, list); | 289 | intr_cb = list_entry(pos, struct mic_intr_cb, list); |
| @@ -274,8 +292,9 @@ static void mic_release_callbacks(struct mic_device *mdev) | |||
| 274 | intr_cb->cb_id); | 292 | intr_cb->cb_id); |
| 275 | kfree(intr_cb); | 293 | kfree(intr_cb); |
| 276 | } | 294 | } |
| 277 | spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); | ||
| 278 | } | 295 | } |
| 296 | spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); | ||
| 297 | spin_unlock(&mdev->irq_info.mic_thread_lock); | ||
| 279 | ida_destroy(&mdev->irq_info.cb_ida); | 298 | ida_destroy(&mdev->irq_info.cb_ida); |
| 280 | kfree(mdev->irq_info.cb_list); | 299 | kfree(mdev->irq_info.cb_list); |
| 281 | } | 300 | } |
| @@ -313,7 +332,8 @@ static int mic_setup_msi(struct mic_device *mdev, struct pci_dev *pdev) | |||
| 313 | goto err_nomem2; | 332 | goto err_nomem2; |
| 314 | } | 333 | } |
| 315 | 334 | ||
| 316 | rc = request_irq(pdev->irq, mic_interrupt, 0 , "mic-msi", mdev); | 335 | rc = request_threaded_irq(pdev->irq, mic_interrupt, mic_thread_fn, |
| 336 | 0, "mic-msi", mdev); | ||
| 317 | if (rc) { | 337 | if (rc) { |
| 318 | dev_err(&pdev->dev, "Error allocating MSI interrupt\n"); | 338 | dev_err(&pdev->dev, "Error allocating MSI interrupt\n"); |
| 319 | goto err_irq_req_fail; | 339 | goto err_irq_req_fail; |
| @@ -353,8 +373,8 @@ static int mic_setup_intx(struct mic_device *mdev, struct pci_dev *pdev) | |||
| 353 | goto err_nomem; | 373 | goto err_nomem; |
| 354 | } | 374 | } |
| 355 | 375 | ||
| 356 | rc = request_irq(pdev->irq, mic_interrupt, | 376 | rc = request_threaded_irq(pdev->irq, mic_interrupt, mic_thread_fn, |
| 357 | IRQF_SHARED, "mic-intx", mdev); | 377 | IRQF_SHARED, "mic-intx", mdev); |
| 358 | if (rc) | 378 | if (rc) |
| 359 | goto err; | 379 | goto err; |
| 360 | 380 | ||
| @@ -391,13 +411,14 @@ int mic_next_db(struct mic_device *mdev) | |||
| 391 | #define MK_COOKIE(x, y) ((x) | (y) << COOKIE_ID_SHIFT) | 411 | #define MK_COOKIE(x, y) ((x) | (y) << COOKIE_ID_SHIFT) |
| 392 | 412 | ||
| 393 | /** | 413 | /** |
| 394 | * mic_request_irq - request an irq. mic_mutex needs | 414 | * mic_request_threaded_irq - request an irq. mic_mutex needs |
| 395 | * to be held before calling this function. | 415 | * to be held before calling this function. |
| 396 | * | 416 | * |
| 397 | * @mdev: pointer to mic_device instance | 417 | * @mdev: pointer to mic_device instance |
| 398 | * @func: The callback function that handles the interrupt. | 418 | * @handler: The callback function that handles the interrupt. |
| 399 | * The function needs to call ack_interrupts | 419 | * The function needs to call ack_interrupts |
| 400 | * (mdev->ops->ack_interrupt(mdev)) when handling the interrupts. | 420 | * (mdev->ops->ack_interrupt(mdev)) when handling the interrupts. |
| 421 | * @thread_fn: thread fn required by request_threaded_irq. | ||
| 401 | * @name: The ASCII name of the callee requesting the irq. | 422 | * @name: The ASCII name of the callee requesting the irq. |
| 402 | * @data: private data that is returned back when calling the | 423 | * @data: private data that is returned back when calling the |
| 403 | * function handler. | 424 | * function handler. |
| @@ -412,10 +433,11 @@ int mic_next_db(struct mic_device *mdev) | |||
| 412 | * error code. | 433 | * error code. |
| 413 | * | 434 | * |
| 414 | */ | 435 | */ |
| 415 | struct mic_irq *mic_request_irq(struct mic_device *mdev, | 436 | struct mic_irq * |
| 416 | irqreturn_t (*func)(int irq, void *dev), | 437 | mic_request_threaded_irq(struct mic_device *mdev, |
| 417 | const char *name, void *data, int intr_src, | 438 | irq_handler_t handler, irq_handler_t thread_fn, |
| 418 | enum mic_intr_type type) | 439 | const char *name, void *data, int intr_src, |
| 440 | enum mic_intr_type type) | ||
| 419 | { | 441 | { |
| 420 | u16 offset; | 442 | u16 offset; |
| 421 | int rc = 0; | 443 | int rc = 0; |
| @@ -444,7 +466,8 @@ struct mic_irq *mic_request_irq(struct mic_device *mdev, | |||
| 444 | goto err; | 466 | goto err; |
| 445 | } | 467 | } |
| 446 | 468 | ||
| 447 | rc = request_irq(msix->vector, func, 0, name, data); | 469 | rc = request_threaded_irq(msix->vector, handler, thread_fn, |
| 470 | 0, name, data); | ||
| 448 | if (rc) { | 471 | if (rc) { |
| 449 | dev_dbg(mdev->sdev->parent, | 472 | dev_dbg(mdev->sdev->parent, |
| 450 | "request irq failed rc = %d\n", rc); | 473 | "request irq failed rc = %d\n", rc); |
| @@ -458,8 +481,8 @@ struct mic_irq *mic_request_irq(struct mic_device *mdev, | |||
| 458 | dev_dbg(mdev->sdev->parent, "irq: %d assigned for src: %d\n", | 481 | dev_dbg(mdev->sdev->parent, "irq: %d assigned for src: %d\n", |
| 459 | msix->vector, intr_src); | 482 | msix->vector, intr_src); |
| 460 | } else { | 483 | } else { |
| 461 | intr_cb = mic_register_intr_callback(mdev, | 484 | intr_cb = mic_register_intr_callback(mdev, offset, handler, |
| 462 | offset, func, data); | 485 | thread_fn, data); |
| 463 | if (IS_ERR(intr_cb)) { | 486 | if (IS_ERR(intr_cb)) { |
| 464 | dev_err(mdev->sdev->parent, | 487 | dev_err(mdev->sdev->parent, |
| 465 | "No available callback entries for use\n"); | 488 | "No available callback entries for use\n"); |
| @@ -487,9 +510,9 @@ err: | |||
| 487 | * needs to be held before calling this function. | 510 | * needs to be held before calling this function. |
| 488 | * | 511 | * |
| 489 | * @mdev: pointer to mic_device instance | 512 | * @mdev: pointer to mic_device instance |
| 490 | * @cookie: cookie obtained during a successful call to mic_request_irq | 513 | * @cookie: cookie obtained during a successful call to mic_request_threaded_irq |
| 491 | * @data: private data specified by the calling function during the | 514 | * @data: private data specified by the calling function during the |
| 492 | * mic_request_irq | 515 | * mic_request_threaded_irq |
| 493 | * | 516 | * |
| 494 | * returns: none. | 517 | * returns: none. |
| 495 | */ | 518 | */ |
diff --git a/drivers/misc/mic/host/mic_intr.h b/drivers/misc/mic/host/mic_intr.h index 6091aa97e116..9f783d4ad7f1 100644 --- a/drivers/misc/mic/host/mic_intr.h +++ b/drivers/misc/mic/host/mic_intr.h | |||
| @@ -21,12 +21,15 @@ | |||
| 21 | #ifndef _MIC_INTR_H_ | 21 | #ifndef _MIC_INTR_H_ |
| 22 | #define _MIC_INTR_H_ | 22 | #define _MIC_INTR_H_ |
| 23 | 23 | ||
| 24 | #include <linux/bitops.h> | ||
| 25 | #include <linux/interrupt.h> | ||
| 24 | /* | 26 | /* |
| 25 | * The minimum number of msix vectors required for normal operation. | 27 | * The minimum number of msix vectors required for normal operation. |
| 26 | * 3 for virtio network, console and block devices. | 28 | * 3 for virtio network, console and block devices. |
| 27 | * 1 for card shutdown notifications. | 29 | * 1 for card shutdown notifications. |
| 30 | * 4 for host owned DMA channels. | ||
| 28 | */ | 31 | */ |
| 29 | #define MIC_MIN_MSIX 4 | 32 | #define MIC_MIN_MSIX 8 |
| 30 | #define MIC_NUM_OFFSETS 32 | 33 | #define MIC_NUM_OFFSETS 32 |
| 31 | 34 | ||
| 32 | /** | 35 | /** |
| @@ -68,7 +71,11 @@ struct mic_intr_info { | |||
| 68 | * @num_vectors: The number of MSI/MSI-x vectors that have been allocated. | 71 | * @num_vectors: The number of MSI/MSI-x vectors that have been allocated. |
| 69 | * @cb_ida: callback ID allocator to track the callbacks registered. | 72 | * @cb_ida: callback ID allocator to track the callbacks registered. |
| 70 | * @mic_intr_lock: spinlock to protect the interrupt callback list. | 73 | * @mic_intr_lock: spinlock to protect the interrupt callback list. |
| 74 | * @mic_thread_lock: spinlock to protect the thread callback list. | ||
| 75 | * This lock is used to protect against thread_fn while | ||
| 76 | * mic_intr_lock is used to protect against interrupt handler. | ||
| 71 | * @cb_list: Array of callback lists one for each source. | 77 | * @cb_list: Array of callback lists one for each source. |
| 78 | * @mask: Mask used by the main thread fn to call the underlying thread fns. | ||
| 72 | */ | 79 | */ |
| 73 | struct mic_irq_info { | 80 | struct mic_irq_info { |
| 74 | int next_avail_src; | 81 | int next_avail_src; |
| @@ -77,19 +84,23 @@ struct mic_irq_info { | |||
| 77 | u16 num_vectors; | 84 | u16 num_vectors; |
| 78 | struct ida cb_ida; | 85 | struct ida cb_ida; |
| 79 | spinlock_t mic_intr_lock; | 86 | spinlock_t mic_intr_lock; |
| 87 | spinlock_t mic_thread_lock; | ||
| 80 | struct list_head *cb_list; | 88 | struct list_head *cb_list; |
| 89 | unsigned long mask; | ||
| 81 | }; | 90 | }; |
| 82 | 91 | ||
| 83 | /** | 92 | /** |
| 84 | * struct mic_intr_cb - Interrupt callback structure. | 93 | * struct mic_intr_cb - Interrupt callback structure. |
| 85 | * | 94 | * |
| 86 | * @func: The callback function | 95 | * @handler: The callback function |
| 96 | * @thread_fn: The thread_fn. | ||
| 87 | * @data: Private data of the requester. | 97 | * @data: Private data of the requester. |
| 88 | * @cb_id: The callback id. Identifies this callback. | 98 | * @cb_id: The callback id. Identifies this callback. |
| 89 | * @list: list head pointing to the next callback structure. | 99 | * @list: list head pointing to the next callback structure. |
| 90 | */ | 100 | */ |
| 91 | struct mic_intr_cb { | 101 | struct mic_intr_cb { |
| 92 | irqreturn_t (*func) (int irq, void *data); | 102 | irq_handler_t handler; |
| 103 | irq_handler_t thread_fn; | ||
| 93 | void *data; | 104 | void *data; |
| 94 | int cb_id; | 105 | int cb_id; |
| 95 | struct list_head list; | 106 | struct list_head list; |
| @@ -124,11 +135,11 @@ struct mic_hw_intr_ops { | |||
| 124 | }; | 135 | }; |
| 125 | 136 | ||
| 126 | int mic_next_db(struct mic_device *mdev); | 137 | int mic_next_db(struct mic_device *mdev); |
| 127 | struct mic_irq *mic_request_irq(struct mic_device *mdev, | 138 | struct mic_irq * |
| 128 | irqreturn_t (*func)(int irq, void *data), | 139 | mic_request_threaded_irq(struct mic_device *mdev, |
| 129 | const char *name, void *data, int intr_src, | 140 | irq_handler_t handler, irq_handler_t thread_fn, |
| 130 | enum mic_intr_type type); | 141 | const char *name, void *data, int intr_src, |
| 131 | 142 | enum mic_intr_type type); | |
| 132 | void mic_free_irq(struct mic_device *mdev, | 143 | void mic_free_irq(struct mic_device *mdev, |
| 133 | struct mic_irq *cookie, void *data); | 144 | struct mic_irq *cookie, void *data); |
| 134 | int mic_setup_interrupts(struct mic_device *mdev, struct pci_dev *pdev); | 145 | int mic_setup_interrupts(struct mic_device *mdev, struct pci_dev *pdev); |
diff --git a/drivers/misc/mic/host/mic_main.c b/drivers/misc/mic/host/mic_main.c index c04a021e20c7..ab37a3117d23 100644 --- a/drivers/misc/mic/host/mic_main.c +++ b/drivers/misc/mic/host/mic_main.c | |||
| @@ -38,7 +38,7 @@ | |||
| 38 | 38 | ||
| 39 | static const char mic_driver_name[] = "mic"; | 39 | static const char mic_driver_name[] = "mic"; |
| 40 | 40 | ||
| 41 | static DEFINE_PCI_DEVICE_TABLE(mic_pci_tbl) = { | 41 | static const struct pci_device_id mic_pci_tbl[] = { |
| 42 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2250)}, | 42 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2250)}, |
| 43 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2251)}, | 43 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2251)}, |
| 44 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2252)}, | 44 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2252)}, |
| @@ -389,8 +389,9 @@ static int mic_probe(struct pci_dev *pdev, | |||
| 389 | mutex_lock(&mdev->mic_mutex); | 389 | mutex_lock(&mdev->mic_mutex); |
| 390 | 390 | ||
| 391 | mdev->shutdown_db = mic_next_db(mdev); | 391 | mdev->shutdown_db = mic_next_db(mdev); |
| 392 | mdev->shutdown_cookie = mic_request_irq(mdev, mic_shutdown_db, | 392 | mdev->shutdown_cookie = mic_request_threaded_irq(mdev, mic_shutdown_db, |
| 393 | "shutdown-interrupt", mdev, mdev->shutdown_db, MIC_INTR_DB); | 393 | NULL, "shutdown-interrupt", mdev, |
| 394 | mdev->shutdown_db, MIC_INTR_DB); | ||
| 394 | if (IS_ERR(mdev->shutdown_cookie)) { | 395 | if (IS_ERR(mdev->shutdown_cookie)) { |
| 395 | rc = PTR_ERR(mdev->shutdown_cookie); | 396 | rc = PTR_ERR(mdev->shutdown_cookie); |
| 396 | mutex_unlock(&mdev->mic_mutex); | 397 | mutex_unlock(&mdev->mic_mutex); |
diff --git a/drivers/misc/mic/host/mic_virtio.c b/drivers/misc/mic/host/mic_virtio.c index 7e1ef0ebbb80..a020e4eb435a 100644 --- a/drivers/misc/mic/host/mic_virtio.c +++ b/drivers/misc/mic/host/mic_virtio.c | |||
| @@ -21,60 +21,157 @@ | |||
| 21 | #include <linux/pci.h> | 21 | #include <linux/pci.h> |
| 22 | #include <linux/sched.h> | 22 | #include <linux/sched.h> |
| 23 | #include <linux/uaccess.h> | 23 | #include <linux/uaccess.h> |
| 24 | 24 | #include <linux/dmaengine.h> | |
| 25 | #include <linux/mic_common.h> | 25 | #include <linux/mic_common.h> |
| 26 | |||
| 26 | #include "../common/mic_dev.h" | 27 | #include "../common/mic_dev.h" |
| 27 | #include "mic_device.h" | 28 | #include "mic_device.h" |
| 28 | #include "mic_smpt.h" | 29 | #include "mic_smpt.h" |
| 29 | #include "mic_virtio.h" | 30 | #include "mic_virtio.h" |
| 30 | 31 | ||
| 31 | /* | 32 | /* |
| 32 | * Initiates the copies across the PCIe bus from card memory to | 33 | * Size of the internal buffer used during DMA's as an intermediate buffer |
| 33 | * a user space buffer. | 34 | * for copy to/from user. |
| 34 | */ | 35 | */ |
| 35 | static int mic_virtio_copy_to_user(struct mic_vdev *mvdev, | 36 | #define MIC_INT_DMA_BUF_SIZE PAGE_ALIGN(64 * 1024ULL) |
| 36 | void __user *ubuf, size_t len, u64 addr) | 37 | |
| 38 | static int mic_sync_dma(struct mic_device *mdev, dma_addr_t dst, | ||
| 39 | dma_addr_t src, size_t len) | ||
| 37 | { | 40 | { |
| 38 | int err; | 41 | int err = 0; |
| 39 | void __iomem *dbuf = mvdev->mdev->aper.va + addr; | 42 | struct dma_async_tx_descriptor *tx; |
| 40 | /* | 43 | struct dma_chan *mic_ch = mdev->dma_ch; |
| 41 | * We are copying from IO below an should ideally use something | 44 | |
| 42 | * like copy_to_user_fromio(..) if it existed. | 45 | if (!mic_ch) { |
| 43 | */ | 46 | err = -EBUSY; |
| 44 | if (copy_to_user(ubuf, (void __force *)dbuf, len)) { | 47 | goto error; |
| 45 | err = -EFAULT; | 48 | } |
| 46 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | 49 | |
| 50 | tx = mic_ch->device->device_prep_dma_memcpy(mic_ch, dst, src, len, | ||
| 51 | DMA_PREP_FENCE); | ||
| 52 | if (!tx) { | ||
| 53 | err = -ENOMEM; | ||
| 54 | goto error; | ||
| 55 | } else { | ||
| 56 | dma_cookie_t cookie = tx->tx_submit(tx); | ||
| 57 | |||
| 58 | err = dma_submit_error(cookie); | ||
| 59 | if (err) | ||
| 60 | goto error; | ||
| 61 | err = dma_sync_wait(mic_ch, cookie); | ||
| 62 | } | ||
| 63 | error: | ||
| 64 | if (err) | ||
| 65 | dev_err(mdev->sdev->parent, "%s %d err %d\n", | ||
| 47 | __func__, __LINE__, err); | 66 | __func__, __LINE__, err); |
| 48 | goto err; | 67 | return err; |
| 68 | } | ||
| 69 | |||
| 70 | /* | ||
| 71 | * Initiates the copies across the PCIe bus from card memory to a user | ||
| 72 | * space buffer. When transfers are done using DMA, source/destination | ||
| 73 | * addresses and transfer length must follow the alignment requirements of | ||
| 74 | * the MIC DMA engine. | ||
| 75 | */ | ||
| 76 | static int mic_virtio_copy_to_user(struct mic_vdev *mvdev, void __user *ubuf, | ||
| 77 | size_t len, u64 daddr, size_t dlen, | ||
| 78 | int vr_idx) | ||
| 79 | { | ||
| 80 | struct mic_device *mdev = mvdev->mdev; | ||
| 81 | void __iomem *dbuf = mdev->aper.va + daddr; | ||
| 82 | struct mic_vringh *mvr = &mvdev->mvr[vr_idx]; | ||
| 83 | size_t dma_alignment = 1 << mdev->dma_ch->device->copy_align; | ||
| 84 | size_t dma_offset; | ||
| 85 | size_t partlen; | ||
| 86 | int err; | ||
| 87 | |||
| 88 | dma_offset = daddr - round_down(daddr, dma_alignment); | ||
| 89 | daddr -= dma_offset; | ||
| 90 | len += dma_offset; | ||
| 91 | |||
| 92 | while (len) { | ||
| 93 | partlen = min_t(size_t, len, MIC_INT_DMA_BUF_SIZE); | ||
| 94 | |||
| 95 | err = mic_sync_dma(mdev, mvr->buf_da, daddr, | ||
| 96 | ALIGN(partlen, dma_alignment)); | ||
| 97 | if (err) | ||
| 98 | goto err; | ||
| 99 | |||
| 100 | if (copy_to_user(ubuf, mvr->buf + dma_offset, | ||
| 101 | partlen - dma_offset)) { | ||
| 102 | err = -EFAULT; | ||
| 103 | goto err; | ||
| 104 | } | ||
| 105 | daddr += partlen; | ||
| 106 | ubuf += partlen; | ||
| 107 | dbuf += partlen; | ||
| 108 | mvdev->in_bytes_dma += partlen; | ||
| 109 | mvdev->in_bytes += partlen; | ||
| 110 | len -= partlen; | ||
| 111 | dma_offset = 0; | ||
| 49 | } | 112 | } |
| 50 | mvdev->in_bytes += len; | 113 | return 0; |
| 51 | err = 0; | ||
| 52 | err: | 114 | err: |
| 115 | dev_err(mic_dev(mvdev), "%s %d err %d\n", __func__, __LINE__, err); | ||
| 53 | return err; | 116 | return err; |
| 54 | } | 117 | } |
| 55 | 118 | ||
| 56 | /* | 119 | /* |
| 57 | * Initiates copies across the PCIe bus from a user space | 120 | * Initiates copies across the PCIe bus from a user space buffer to card |
| 58 | * buffer to card memory. | 121 | * memory. When transfers are done using DMA, source/destination addresses |
| 122 | * and transfer length must follow the alignment requirements of the MIC | ||
| 123 | * DMA engine. | ||
| 59 | */ | 124 | */ |
| 60 | static int mic_virtio_copy_from_user(struct mic_vdev *mvdev, | 125 | static int mic_virtio_copy_from_user(struct mic_vdev *mvdev, void __user *ubuf, |
| 61 | void __user *ubuf, size_t len, u64 addr) | 126 | size_t len, u64 daddr, size_t dlen, |
| 127 | int vr_idx) | ||
| 62 | { | 128 | { |
| 129 | struct mic_device *mdev = mvdev->mdev; | ||
| 130 | void __iomem *dbuf = mdev->aper.va + daddr; | ||
| 131 | struct mic_vringh *mvr = &mvdev->mvr[vr_idx]; | ||
| 132 | size_t dma_alignment = 1 << mdev->dma_ch->device->copy_align; | ||
| 133 | size_t partlen; | ||
| 63 | int err; | 134 | int err; |
| 64 | void __iomem *dbuf = mvdev->mdev->aper.va + addr; | 135 | |
| 136 | if (daddr & (dma_alignment - 1)) { | ||
| 137 | mvdev->tx_dst_unaligned += len; | ||
| 138 | goto memcpy; | ||
| 139 | } else if (ALIGN(len, dma_alignment) > dlen) { | ||
| 140 | mvdev->tx_len_unaligned += len; | ||
| 141 | goto memcpy; | ||
| 142 | } | ||
| 143 | |||
| 144 | while (len) { | ||
| 145 | partlen = min_t(size_t, len, MIC_INT_DMA_BUF_SIZE); | ||
| 146 | |||
| 147 | if (copy_from_user(mvr->buf, ubuf, partlen)) { | ||
| 148 | err = -EFAULT; | ||
| 149 | goto err; | ||
| 150 | } | ||
| 151 | err = mic_sync_dma(mdev, daddr, mvr->buf_da, | ||
| 152 | ALIGN(partlen, dma_alignment)); | ||
| 153 | if (err) | ||
| 154 | goto err; | ||
| 155 | daddr += partlen; | ||
| 156 | ubuf += partlen; | ||
| 157 | dbuf += partlen; | ||
| 158 | mvdev->out_bytes_dma += partlen; | ||
| 159 | mvdev->out_bytes += partlen; | ||
| 160 | len -= partlen; | ||
| 161 | } | ||
| 162 | memcpy: | ||
| 65 | /* | 163 | /* |
| 66 | * We are copying to IO below and should ideally use something | 164 | * We are copying to IO below and should ideally use something |
| 67 | * like copy_from_user_toio(..) if it existed. | 165 | * like copy_from_user_toio(..) if it existed. |
| 68 | */ | 166 | */ |
| 69 | if (copy_from_user((void __force *)dbuf, ubuf, len)) { | 167 | if (copy_from_user((void __force *)dbuf, ubuf, len)) { |
| 70 | err = -EFAULT; | 168 | err = -EFAULT; |
| 71 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | ||
| 72 | __func__, __LINE__, err); | ||
| 73 | goto err; | 169 | goto err; |
| 74 | } | 170 | } |
| 75 | mvdev->out_bytes += len; | 171 | mvdev->out_bytes += len; |
| 76 | err = 0; | 172 | return 0; |
| 77 | err: | 173 | err: |
| 174 | dev_err(mic_dev(mvdev), "%s %d err %d\n", __func__, __LINE__, err); | ||
| 78 | return err; | 175 | return err; |
| 79 | } | 176 | } |
| 80 | 177 | ||
| @@ -110,7 +207,8 @@ static inline u32 mic_vringh_iov_consumed(struct vringh_kiov *iov) | |||
| 110 | * way to override the VRINGH xfer(..) routines as of v3.10. | 207 | * way to override the VRINGH xfer(..) routines as of v3.10. |
| 111 | */ | 208 | */ |
| 112 | static int mic_vringh_copy(struct mic_vdev *mvdev, struct vringh_kiov *iov, | 209 | static int mic_vringh_copy(struct mic_vdev *mvdev, struct vringh_kiov *iov, |
| 113 | void __user *ubuf, size_t len, bool read, size_t *out_len) | 210 | void __user *ubuf, size_t len, bool read, int vr_idx, |
| 211 | size_t *out_len) | ||
| 114 | { | 212 | { |
| 115 | int ret = 0; | 213 | int ret = 0; |
| 116 | size_t partlen, tot_len = 0; | 214 | size_t partlen, tot_len = 0; |
| @@ -118,13 +216,15 @@ static int mic_vringh_copy(struct mic_vdev *mvdev, struct vringh_kiov *iov, | |||
| 118 | while (len && iov->i < iov->used) { | 216 | while (len && iov->i < iov->used) { |
| 119 | partlen = min(iov->iov[iov->i].iov_len, len); | 217 | partlen = min(iov->iov[iov->i].iov_len, len); |
| 120 | if (read) | 218 | if (read) |
| 121 | ret = mic_virtio_copy_to_user(mvdev, | 219 | ret = mic_virtio_copy_to_user(mvdev, ubuf, partlen, |
| 122 | ubuf, partlen, | 220 | (u64)iov->iov[iov->i].iov_base, |
| 123 | (u64)iov->iov[iov->i].iov_base); | 221 | iov->iov[iov->i].iov_len, |
| 222 | vr_idx); | ||
| 124 | else | 223 | else |
| 125 | ret = mic_virtio_copy_from_user(mvdev, | 224 | ret = mic_virtio_copy_from_user(mvdev, ubuf, partlen, |
| 126 | ubuf, partlen, | 225 | (u64)iov->iov[iov->i].iov_base, |
| 127 | (u64)iov->iov[iov->i].iov_base); | 226 | iov->iov[iov->i].iov_len, |
| 227 | vr_idx); | ||
| 128 | if (ret) { | 228 | if (ret) { |
| 129 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | 229 | dev_err(mic_dev(mvdev), "%s %d err %d\n", |
| 130 | __func__, __LINE__, ret); | 230 | __func__, __LINE__, ret); |
| @@ -192,8 +292,8 @@ static int _mic_virtio_copy(struct mic_vdev *mvdev, | |||
| 192 | ubuf = iov.iov_base; | 292 | ubuf = iov.iov_base; |
| 193 | } | 293 | } |
| 194 | /* Issue all the read descriptors first */ | 294 | /* Issue all the read descriptors first */ |
| 195 | ret = mic_vringh_copy(mvdev, riov, ubuf, len, | 295 | ret = mic_vringh_copy(mvdev, riov, ubuf, len, MIC_VRINGH_READ, |
| 196 | MIC_VRINGH_READ, &out_len); | 296 | copy->vr_idx, &out_len); |
| 197 | if (ret) { | 297 | if (ret) { |
| 198 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | 298 | dev_err(mic_dev(mvdev), "%s %d err %d\n", |
| 199 | __func__, __LINE__, ret); | 299 | __func__, __LINE__, ret); |
| @@ -203,8 +303,8 @@ static int _mic_virtio_copy(struct mic_vdev *mvdev, | |||
| 203 | ubuf += out_len; | 303 | ubuf += out_len; |
| 204 | copy->out_len += out_len; | 304 | copy->out_len += out_len; |
| 205 | /* Issue the write descriptors next */ | 305 | /* Issue the write descriptors next */ |
| 206 | ret = mic_vringh_copy(mvdev, wiov, ubuf, len, | 306 | ret = mic_vringh_copy(mvdev, wiov, ubuf, len, !MIC_VRINGH_READ, |
| 207 | !MIC_VRINGH_READ, &out_len); | 307 | copy->vr_idx, &out_len); |
| 208 | if (ret) { | 308 | if (ret) { |
| 209 | dev_err(mic_dev(mvdev), "%s %d err %d\n", | 309 | dev_err(mic_dev(mvdev), "%s %d err %d\n", |
| 210 | __func__, __LINE__, ret); | 310 | __func__, __LINE__, ret); |
| @@ -589,13 +689,19 @@ int mic_virtio_add_device(struct mic_vdev *mvdev, | |||
| 589 | dev_dbg(mdev->sdev->parent, | 689 | dev_dbg(mdev->sdev->parent, |
| 590 | "%s %d index %d va %p info %p vr_size 0x%x\n", | 690 | "%s %d index %d va %p info %p vr_size 0x%x\n", |
| 591 | __func__, __LINE__, i, vr->va, vr->info, vr_size); | 691 | __func__, __LINE__, i, vr->va, vr->info, vr_size); |
| 692 | mvr->buf = (void *)__get_free_pages(GFP_KERNEL, | ||
| 693 | get_order(MIC_INT_DMA_BUF_SIZE)); | ||
| 694 | mvr->buf_da = mic_map_single(mvdev->mdev, mvr->buf, | ||
| 695 | MIC_INT_DMA_BUF_SIZE); | ||
| 592 | } | 696 | } |
| 593 | 697 | ||
| 594 | snprintf(irqname, sizeof(irqname), "mic%dvirtio%d", mdev->id, | 698 | snprintf(irqname, sizeof(irqname), "mic%dvirtio%d", mdev->id, |
| 595 | mvdev->virtio_id); | 699 | mvdev->virtio_id); |
| 596 | mvdev->virtio_db = mic_next_db(mdev); | 700 | mvdev->virtio_db = mic_next_db(mdev); |
| 597 | mvdev->virtio_cookie = mic_request_irq(mdev, mic_virtio_intr_handler, | 701 | mvdev->virtio_cookie = mic_request_threaded_irq(mdev, |
| 598 | irqname, mvdev, mvdev->virtio_db, MIC_INTR_DB); | 702 | mic_virtio_intr_handler, |
| 703 | NULL, irqname, mvdev, | ||
| 704 | mvdev->virtio_db, MIC_INTR_DB); | ||
| 599 | if (IS_ERR(mvdev->virtio_cookie)) { | 705 | if (IS_ERR(mvdev->virtio_cookie)) { |
| 600 | ret = PTR_ERR(mvdev->virtio_cookie); | 706 | ret = PTR_ERR(mvdev->virtio_cookie); |
| 601 | dev_dbg(mdev->sdev->parent, "request irq failed\n"); | 707 | dev_dbg(mdev->sdev->parent, "request irq failed\n"); |
| @@ -671,6 +777,11 @@ skip_hot_remove: | |||
| 671 | vqconfig = mic_vq_config(mvdev->dd); | 777 | vqconfig = mic_vq_config(mvdev->dd); |
| 672 | for (i = 0; i < mvdev->dd->num_vq; i++) { | 778 | for (i = 0; i < mvdev->dd->num_vq; i++) { |
| 673 | struct mic_vringh *mvr = &mvdev->mvr[i]; | 779 | struct mic_vringh *mvr = &mvdev->mvr[i]; |
| 780 | |||
| 781 | mic_unmap_single(mvdev->mdev, mvr->buf_da, | ||
| 782 | MIC_INT_DMA_BUF_SIZE); | ||
| 783 | free_pages((unsigned long)mvr->buf, | ||
| 784 | get_order(MIC_INT_DMA_BUF_SIZE)); | ||
| 674 | vringh_kiov_cleanup(&mvr->riov); | 785 | vringh_kiov_cleanup(&mvr->riov); |
| 675 | vringh_kiov_cleanup(&mvr->wiov); | 786 | vringh_kiov_cleanup(&mvr->wiov); |
| 676 | mic_unmap_single(mdev, le64_to_cpu(vqconfig[i].address), | 787 | mic_unmap_single(mdev, le64_to_cpu(vqconfig[i].address), |
diff --git a/drivers/misc/mic/host/mic_virtio.h b/drivers/misc/mic/host/mic_virtio.h index 184f3c84805b..d574efb853d9 100644 --- a/drivers/misc/mic/host/mic_virtio.h +++ b/drivers/misc/mic/host/mic_virtio.h | |||
| @@ -46,18 +46,23 @@ | |||
| 46 | * @vrh: The host VRINGH used for accessing the card vrings. | 46 | * @vrh: The host VRINGH used for accessing the card vrings. |
| 47 | * @riov: The VRINGH read kernel IOV. | 47 | * @riov: The VRINGH read kernel IOV. |
| 48 | * @wiov: The VRINGH write kernel IOV. | 48 | * @wiov: The VRINGH write kernel IOV. |
| 49 | * @head: The VRINGH head index address passed to vringh_getdesc_kern(..). | ||
| 50 | * @vr_mutex: Mutex for synchronizing access to the VRING. | 49 | * @vr_mutex: Mutex for synchronizing access to the VRING. |
| 50 | * @buf: Temporary kernel buffer used to copy in/out data | ||
| 51 | * from/to the card via DMA. | ||
| 52 | * @buf_da: dma address of buf. | ||
| 51 | * @mvdev: Back pointer to MIC virtio device for vringh_notify(..). | 53 | * @mvdev: Back pointer to MIC virtio device for vringh_notify(..). |
| 54 | * @head: The VRINGH head index address passed to vringh_getdesc_kern(..). | ||
| 52 | */ | 55 | */ |
| 53 | struct mic_vringh { | 56 | struct mic_vringh { |
| 54 | struct mic_vring vring; | 57 | struct mic_vring vring; |
| 55 | struct vringh vrh; | 58 | struct vringh vrh; |
| 56 | struct vringh_kiov riov; | 59 | struct vringh_kiov riov; |
| 57 | struct vringh_kiov wiov; | 60 | struct vringh_kiov wiov; |
| 58 | u16 head; | ||
| 59 | struct mutex vr_mutex; | 61 | struct mutex vr_mutex; |
| 62 | void *buf; | ||
| 63 | dma_addr_t buf_da; | ||
| 60 | struct mic_vdev *mvdev; | 64 | struct mic_vdev *mvdev; |
| 65 | u16 head; | ||
| 61 | }; | 66 | }; |
| 62 | 67 | ||
| 63 | /** | 68 | /** |
| @@ -69,6 +74,14 @@ struct mic_vringh { | |||
| 69 | * @poll_wake - Used for waking up threads blocked in poll. | 74 | * @poll_wake - Used for waking up threads blocked in poll. |
| 70 | * @out_bytes - Debug stats for number of bytes copied from host to card. | 75 | * @out_bytes - Debug stats for number of bytes copied from host to card. |
| 71 | * @in_bytes - Debug stats for number of bytes copied from card to host. | 76 | * @in_bytes - Debug stats for number of bytes copied from card to host. |
| 77 | * @out_bytes_dma - Debug stats for number of bytes copied from host to card | ||
| 78 | * using DMA. | ||
| 79 | * @in_bytes_dma - Debug stats for number of bytes copied from card to host | ||
| 80 | * using DMA. | ||
| 81 | * @tx_len_unaligned - Debug stats for number of bytes copied to the card where | ||
| 82 | * the transfer length did not have the required DMA alignment. | ||
| 83 | * @tx_dst_unaligned - Debug stats for number of bytes copied where the | ||
| 84 | * destination address on the card did not have the required DMA alignment. | ||
| 72 | * @mvr - Store per VRING data structures. | 85 | * @mvr - Store per VRING data structures. |
| 73 | * @virtio_bh_work - Work struct used to schedule virtio bottom half handling. | 86 | * @virtio_bh_work - Work struct used to schedule virtio bottom half handling. |
| 74 | * @dd - Virtio device descriptor. | 87 | * @dd - Virtio device descriptor. |
| @@ -84,6 +97,10 @@ struct mic_vdev { | |||
| 84 | int poll_wake; | 97 | int poll_wake; |
| 85 | unsigned long out_bytes; | 98 | unsigned long out_bytes; |
| 86 | unsigned long in_bytes; | 99 | unsigned long in_bytes; |
| 100 | unsigned long out_bytes_dma; | ||
| 101 | unsigned long in_bytes_dma; | ||
| 102 | unsigned long tx_len_unaligned; | ||
| 103 | unsigned long tx_dst_unaligned; | ||
| 87 | struct mic_vringh mvr[MIC_MAX_VRINGS]; | 104 | struct mic_vringh mvr[MIC_MAX_VRINGS]; |
| 88 | struct work_struct virtio_bh_work; | 105 | struct work_struct virtio_bh_work; |
| 89 | struct mic_device_desc *dd; | 106 | struct mic_device_desc *dd; |
diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c index 5562fdd3ef4e..b7a21e11dcdf 100644 --- a/drivers/misc/mic/host/mic_x100.c +++ b/drivers/misc/mic/host/mic_x100.c | |||
| @@ -549,6 +549,13 @@ struct mic_smpt_ops mic_x100_smpt_ops = { | |||
| 549 | .set = mic_x100_smpt_set, | 549 | .set = mic_x100_smpt_set, |
| 550 | }; | 550 | }; |
| 551 | 551 | ||
| 552 | static bool mic_x100_dma_filter(struct dma_chan *chan, void *param) | ||
| 553 | { | ||
| 554 | if (chan->device->dev->parent == (struct device *)param) | ||
| 555 | return true; | ||
| 556 | return false; | ||
| 557 | } | ||
| 558 | |||
| 552 | struct mic_hw_ops mic_x100_ops = { | 559 | struct mic_hw_ops mic_x100_ops = { |
| 553 | .aper_bar = MIC_X100_APER_BAR, | 560 | .aper_bar = MIC_X100_APER_BAR, |
| 554 | .mmio_bar = MIC_X100_MMIO_BAR, | 561 | .mmio_bar = MIC_X100_MMIO_BAR, |
| @@ -563,6 +570,7 @@ struct mic_hw_ops mic_x100_ops = { | |||
| 563 | .send_firmware_intr = mic_x100_send_firmware_intr, | 570 | .send_firmware_intr = mic_x100_send_firmware_intr, |
| 564 | .load_mic_fw = mic_x100_load_firmware, | 571 | .load_mic_fw = mic_x100_load_firmware, |
| 565 | .get_postcode = mic_x100_get_postcode, | 572 | .get_postcode = mic_x100_get_postcode, |
| 573 | .dma_filter = mic_x100_dma_filter, | ||
| 566 | }; | 574 | }; |
| 567 | 575 | ||
| 568 | struct mic_hw_intr_ops mic_x100_intr_ops = { | 576 | struct mic_hw_intr_ops mic_x100_intr_ops = { |
diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c index 9d3dbb28734b..21c2337bad68 100644 --- a/drivers/misc/ti-st/st_kim.c +++ b/drivers/misc/ti-st/st_kim.c | |||
| @@ -244,7 +244,8 @@ static long read_local_version(struct kim_data_s *kim_gdata, char *bts_scr_name) | |||
| 244 | if (version & 0x8000) | 244 | if (version & 0x8000) |
| 245 | maj_ver |= 0x0008; | 245 | maj_ver |= 0x0008; |
| 246 | 246 | ||
| 247 | sprintf(bts_scr_name, "TIInit_%d.%d.%d.bts", chip, maj_ver, min_ver); | 247 | sprintf(bts_scr_name, "ti-connectivity/TIInit_%d.%d.%d.bts", |
| 248 | chip, maj_ver, min_ver); | ||
| 248 | 249 | ||
| 249 | /* to be accessed later via sysfs entry */ | 250 | /* to be accessed later via sysfs entry */ |
| 250 | kim_gdata->version.full = version; | 251 | kim_gdata->version.full = version; |
| @@ -287,7 +288,7 @@ static long download_firmware(struct kim_data_s *kim_gdata) | |||
| 287 | long len = 0; | 288 | long len = 0; |
| 288 | unsigned char *ptr = NULL; | 289 | unsigned char *ptr = NULL; |
| 289 | unsigned char *action_ptr = NULL; | 290 | unsigned char *action_ptr = NULL; |
| 290 | unsigned char bts_scr_name[30] = { 0 }; /* 30 char long bts scr name? */ | 291 | unsigned char bts_scr_name[40] = { 0 }; /* 40 char long bts scr name? */ |
| 291 | int wr_room_space; | 292 | int wr_room_space; |
| 292 | int cmd_size; | 293 | int cmd_size; |
| 293 | unsigned long timeout; | 294 | unsigned long timeout; |
| @@ -778,7 +779,7 @@ static int kim_probe(struct platform_device *pdev) | |||
| 778 | pr_info("sysfs entries created\n"); | 779 | pr_info("sysfs entries created\n"); |
| 779 | 780 | ||
| 780 | kim_debugfs_dir = debugfs_create_dir("ti-st", NULL); | 781 | kim_debugfs_dir = debugfs_create_dir("ti-st", NULL); |
| 781 | if (IS_ERR(kim_debugfs_dir)) { | 782 | if (!kim_debugfs_dir) { |
| 782 | pr_err(" debugfs entries creation failed "); | 783 | pr_err(" debugfs entries creation failed "); |
| 783 | err = -EIO; | 784 | err = -EIO; |
| 784 | goto err_debugfs_dir; | 785 | goto err_debugfs_dir; |
| @@ -788,7 +789,6 @@ static int kim_probe(struct platform_device *pdev) | |||
| 788 | kim_gdata, &version_debugfs_fops); | 789 | kim_gdata, &version_debugfs_fops); |
| 789 | debugfs_create_file("protocols", S_IRUGO, kim_debugfs_dir, | 790 | debugfs_create_file("protocols", S_IRUGO, kim_debugfs_dir, |
| 790 | kim_gdata, &list_debugfs_fops); | 791 | kim_gdata, &list_debugfs_fops); |
| 791 | pr_info(" debugfs entries created "); | ||
| 792 | return 0; | 792 | return 0; |
| 793 | 793 | ||
| 794 | err_debugfs_dir: | 794 | err_debugfs_dir: |
diff --git a/drivers/misc/vexpress-syscfg.c b/drivers/misc/vexpress-syscfg.c index 3250fc1df0aa..b3a812384a6f 100644 --- a/drivers/misc/vexpress-syscfg.c +++ b/drivers/misc/vexpress-syscfg.c | |||
| @@ -130,7 +130,7 @@ static int vexpress_syscfg_write(void *context, unsigned int index, | |||
| 130 | return vexpress_syscfg_exec(func, index, true, &val); | 130 | return vexpress_syscfg_exec(func, index, true, &val); |
| 131 | } | 131 | } |
| 132 | 132 | ||
| 133 | struct regmap_config vexpress_syscfg_regmap_config = { | 133 | static struct regmap_config vexpress_syscfg_regmap_config = { |
| 134 | .lock = vexpress_config_lock, | 134 | .lock = vexpress_config_lock, |
| 135 | .unlock = vexpress_config_unlock, | 135 | .unlock = vexpress_config_unlock, |
| 136 | .reg_bits = 32, | 136 | .reg_bits = 32, |
| @@ -276,7 +276,7 @@ int vexpress_syscfg_device_register(struct platform_device *pdev) | |||
| 276 | } | 276 | } |
| 277 | 277 | ||
| 278 | 278 | ||
| 279 | int vexpress_syscfg_probe(struct platform_device *pdev) | 279 | static int vexpress_syscfg_probe(struct platform_device *pdev) |
| 280 | { | 280 | { |
| 281 | struct vexpress_syscfg *syscfg; | 281 | struct vexpress_syscfg *syscfg; |
| 282 | struct resource *res; | 282 | struct resource *res; |
diff --git a/drivers/misc/vmw_vmci/vmci_guest.c b/drivers/misc/vmw_vmci/vmci_guest.c index e0d5017785e5..248399a881af 100644 --- a/drivers/misc/vmw_vmci/vmci_guest.c +++ b/drivers/misc/vmw_vmci/vmci_guest.c | |||
| @@ -748,7 +748,7 @@ static void vmci_guest_remove_device(struct pci_dev *pdev) | |||
| 748 | /* The rest are managed resources and will be freed by PCI core */ | 748 | /* The rest are managed resources and will be freed by PCI core */ |
| 749 | } | 749 | } |
| 750 | 750 | ||
| 751 | static DEFINE_PCI_DEVICE_TABLE(vmci_ids) = { | 751 | static const struct pci_device_id vmci_ids[] = { |
| 752 | { PCI_DEVICE(PCI_VENDOR_ID_VMWARE, PCI_DEVICE_ID_VMWARE_VMCI), }, | 752 | { PCI_DEVICE(PCI_VENDOR_ID_VMWARE, PCI_DEVICE_ID_VMWARE_VMCI), }, |
| 753 | { 0 }, | 753 | { 0 }, |
| 754 | }; | 754 | }; |
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 3f8e3dbcaa7c..d04c5adafc16 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c | |||
| @@ -582,7 +582,7 @@ static int pci_legacy_suspend_late(struct device *dev, pm_message_t state) | |||
| 582 | WARN_ONCE(pci_dev->current_state != prev, | 582 | WARN_ONCE(pci_dev->current_state != prev, |
| 583 | "PCI PM: Device state not saved by %pF\n", | 583 | "PCI PM: Device state not saved by %pF\n", |
| 584 | drv->suspend_late); | 584 | drv->suspend_late); |
| 585 | return 0; | 585 | goto Fixup; |
| 586 | } | 586 | } |
| 587 | } | 587 | } |
| 588 | 588 | ||
| @@ -591,6 +591,9 @@ static int pci_legacy_suspend_late(struct device *dev, pm_message_t state) | |||
| 591 | 591 | ||
| 592 | pci_pm_set_unknown_state(pci_dev); | 592 | pci_pm_set_unknown_state(pci_dev); |
| 593 | 593 | ||
| 594 | Fixup: | ||
| 595 | pci_fixup_device(pci_fixup_suspend_late, pci_dev); | ||
| 596 | |||
| 594 | return 0; | 597 | return 0; |
| 595 | } | 598 | } |
| 596 | 599 | ||
| @@ -734,7 +737,7 @@ static int pci_pm_suspend_noirq(struct device *dev) | |||
| 734 | 737 | ||
| 735 | if (!pm) { | 738 | if (!pm) { |
| 736 | pci_save_state(pci_dev); | 739 | pci_save_state(pci_dev); |
| 737 | return 0; | 740 | goto Fixup; |
| 738 | } | 741 | } |
| 739 | 742 | ||
| 740 | if (pm->suspend_noirq) { | 743 | if (pm->suspend_noirq) { |
| @@ -751,7 +754,7 @@ static int pci_pm_suspend_noirq(struct device *dev) | |||
| 751 | WARN_ONCE(pci_dev->current_state != prev, | 754 | WARN_ONCE(pci_dev->current_state != prev, |
| 752 | "PCI PM: State of device not saved by %pF\n", | 755 | "PCI PM: State of device not saved by %pF\n", |
| 753 | pm->suspend_noirq); | 756 | pm->suspend_noirq); |
| 754 | return 0; | 757 | goto Fixup; |
| 755 | } | 758 | } |
| 756 | } | 759 | } |
| 757 | 760 | ||
| @@ -775,6 +778,9 @@ static int pci_pm_suspend_noirq(struct device *dev) | |||
| 775 | if (pci_dev->class == PCI_CLASS_SERIAL_USB_EHCI) | 778 | if (pci_dev->class == PCI_CLASS_SERIAL_USB_EHCI) |
| 776 | pci_write_config_word(pci_dev, PCI_COMMAND, 0); | 779 | pci_write_config_word(pci_dev, PCI_COMMAND, 0); |
| 777 | 780 | ||
| 781 | Fixup: | ||
| 782 | pci_fixup_device(pci_fixup_suspend_late, pci_dev); | ||
| 783 | |||
| 778 | return 0; | 784 | return 0; |
| 779 | } | 785 | } |
| 780 | 786 | ||
| @@ -999,8 +1005,10 @@ static int pci_pm_poweroff_noirq(struct device *dev) | |||
| 999 | if (pci_has_legacy_pm_support(to_pci_dev(dev))) | 1005 | if (pci_has_legacy_pm_support(to_pci_dev(dev))) |
| 1000 | return pci_legacy_suspend_late(dev, PMSG_HIBERNATE); | 1006 | return pci_legacy_suspend_late(dev, PMSG_HIBERNATE); |
| 1001 | 1007 | ||
| 1002 | if (!drv || !drv->pm) | 1008 | if (!drv || !drv->pm) { |
| 1009 | pci_fixup_device(pci_fixup_suspend_late, pci_dev); | ||
| 1003 | return 0; | 1010 | return 0; |
| 1011 | } | ||
| 1004 | 1012 | ||
| 1005 | if (drv->pm->poweroff_noirq) { | 1013 | if (drv->pm->poweroff_noirq) { |
| 1006 | int error; | 1014 | int error; |
| @@ -1021,6 +1029,8 @@ static int pci_pm_poweroff_noirq(struct device *dev) | |||
| 1021 | if (pci_dev->class == PCI_CLASS_SERIAL_USB_EHCI) | 1029 | if (pci_dev->class == PCI_CLASS_SERIAL_USB_EHCI) |
| 1022 | pci_write_config_word(pci_dev, PCI_COMMAND, 0); | 1030 | pci_write_config_word(pci_dev, PCI_COMMAND, 0); |
| 1023 | 1031 | ||
| 1032 | pci_fixup_device(pci_fixup_suspend_late, pci_dev); | ||
| 1033 | |||
| 1024 | if (pcibios_pm_ops.poweroff_noirq) | 1034 | if (pcibios_pm_ops.poweroff_noirq) |
| 1025 | return pcibios_pm_ops.poweroff_noirq(dev); | 1035 | return pcibios_pm_ops.poweroff_noirq(dev); |
| 1026 | 1036 | ||
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index ad566827b547..80c2d014283d 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c | |||
| @@ -2986,6 +2986,103 @@ DECLARE_PCI_FIXUP_HEADER(0x1814, 0x0601, /* Ralink RT2800 802.11n PCI */ | |||
| 2986 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_REALTEK, 0x8169, | 2986 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_REALTEK, 0x8169, |
| 2987 | quirk_broken_intx_masking); | 2987 | quirk_broken_intx_masking); |
| 2988 | 2988 | ||
| 2989 | #ifdef CONFIG_ACPI | ||
| 2990 | /* | ||
| 2991 | * Apple: Shutdown Cactus Ridge Thunderbolt controller. | ||
| 2992 | * | ||
| 2993 | * On Apple hardware the Cactus Ridge Thunderbolt controller needs to be | ||
| 2994 | * shutdown before suspend. Otherwise the native host interface (NHI) will not | ||
| 2995 | * be present after resume if a device was plugged in before suspend. | ||
| 2996 | * | ||
| 2997 | * The thunderbolt controller consists of a pcie switch with downstream | ||
| 2998 | * bridges leading to the NHI and to the tunnel pci bridges. | ||
| 2999 | * | ||
| 3000 | * This quirk cuts power to the whole chip. Therefore we have to apply it | ||
| 3001 | * during suspend_noirq of the upstream bridge. | ||
| 3002 | * | ||
| 3003 | * Power is automagically restored before resume. No action is needed. | ||
| 3004 | */ | ||
| 3005 | static void quirk_apple_poweroff_thunderbolt(struct pci_dev *dev) | ||
| 3006 | { | ||
| 3007 | acpi_handle bridge, SXIO, SXFP, SXLV; | ||
| 3008 | |||
| 3009 | if (!dmi_match(DMI_BOARD_VENDOR, "Apple Inc.")) | ||
| 3010 | return; | ||
| 3011 | if (pci_pcie_type(dev) != PCI_EXP_TYPE_UPSTREAM) | ||
| 3012 | return; | ||
| 3013 | bridge = ACPI_HANDLE(&dev->dev); | ||
| 3014 | if (!bridge) | ||
| 3015 | return; | ||
| 3016 | /* | ||
| 3017 | * SXIO and SXLV are present only on machines requiring this quirk. | ||
| 3018 | * TB bridges in external devices might have the same device id as those | ||
| 3019 | * on the host, but they will not have the associated ACPI methods. This | ||
| 3020 | * implicitly checks that we are at the right bridge. | ||
| 3021 | */ | ||
| 3022 | if (ACPI_FAILURE(acpi_get_handle(bridge, "DSB0.NHI0.SXIO", &SXIO)) | ||
| 3023 | || ACPI_FAILURE(acpi_get_handle(bridge, "DSB0.NHI0.SXFP", &SXFP)) | ||
| 3024 | || ACPI_FAILURE(acpi_get_handle(bridge, "DSB0.NHI0.SXLV", &SXLV))) | ||
| 3025 | return; | ||
| 3026 | dev_info(&dev->dev, "quirk: cutting power to thunderbolt controller...\n"); | ||
| 3027 | |||
| 3028 | /* magic sequence */ | ||
| 3029 | acpi_execute_simple_method(SXIO, NULL, 1); | ||
| 3030 | acpi_execute_simple_method(SXFP, NULL, 0); | ||
| 3031 | msleep(300); | ||
| 3032 | acpi_execute_simple_method(SXLV, NULL, 0); | ||
| 3033 | acpi_execute_simple_method(SXIO, NULL, 0); | ||
| 3034 | acpi_execute_simple_method(SXLV, NULL, 0); | ||
| 3035 | } | ||
| 3036 | DECLARE_PCI_FIXUP_SUSPEND_LATE(PCI_VENDOR_ID_INTEL, 0x1547, | ||
| 3037 | quirk_apple_poweroff_thunderbolt); | ||
| 3038 | |||
| 3039 | /* | ||
| 3040 | * Apple: Wait for the thunderbolt controller to reestablish pci tunnels. | ||
| 3041 | * | ||
| 3042 | * During suspend the thunderbolt controller is reset and all pci | ||
| 3043 | * tunnels are lost. The NHI driver will try to reestablish all tunnels | ||
| 3044 | * during resume. We have to manually wait for the NHI since there is | ||
| 3045 | * no parent child relationship between the NHI and the tunneled | ||
| 3046 | * bridges. | ||
| 3047 | */ | ||
| 3048 | static void quirk_apple_wait_for_thunderbolt(struct pci_dev *dev) | ||
| 3049 | { | ||
| 3050 | struct pci_dev *sibling = NULL; | ||
| 3051 | struct pci_dev *nhi = NULL; | ||
| 3052 | |||
| 3053 | if (!dmi_match(DMI_BOARD_VENDOR, "Apple Inc.")) | ||
| 3054 | return; | ||
| 3055 | if (pci_pcie_type(dev) != PCI_EXP_TYPE_DOWNSTREAM) | ||
| 3056 | return; | ||
| 3057 | /* | ||
| 3058 | * Find the NHI and confirm that we are a bridge on the tb host | ||
| 3059 | * controller and not on a tb endpoint. | ||
| 3060 | */ | ||
| 3061 | sibling = pci_get_slot(dev->bus, 0x0); | ||
| 3062 | if (sibling == dev) | ||
| 3063 | goto out; /* we are the downstream bridge to the NHI */ | ||
| 3064 | if (!sibling || !sibling->subordinate) | ||
| 3065 | goto out; | ||
| 3066 | nhi = pci_get_slot(sibling->subordinate, 0x0); | ||
| 3067 | if (!nhi) | ||
| 3068 | goto out; | ||
| 3069 | if (nhi->vendor != PCI_VENDOR_ID_INTEL | ||
| 3070 | || (nhi->device != 0x1547 && nhi->device != 0x156c) | ||
| 3071 | || nhi->subsystem_vendor != 0x2222 | ||
| 3072 | || nhi->subsystem_device != 0x1111) | ||
| 3073 | goto out; | ||
| 3074 | dev_info(&dev->dev, "quirk: wating for thunderbolt to reestablish pci tunnels...\n"); | ||
| 3075 | device_pm_wait_for_dev(&dev->dev, &nhi->dev); | ||
| 3076 | out: | ||
| 3077 | pci_dev_put(nhi); | ||
| 3078 | pci_dev_put(sibling); | ||
| 3079 | } | ||
| 3080 | DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, 0x1547, | ||
| 3081 | quirk_apple_wait_for_thunderbolt); | ||
| 3082 | DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, 0x156d, | ||
| 3083 | quirk_apple_wait_for_thunderbolt); | ||
| 3084 | #endif | ||
| 3085 | |||
| 2989 | static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, | 3086 | static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, |
| 2990 | struct pci_fixup *end) | 3087 | struct pci_fixup *end) |
| 2991 | { | 3088 | { |
| @@ -3018,6 +3115,8 @@ extern struct pci_fixup __start_pci_fixups_resume_early[]; | |||
| 3018 | extern struct pci_fixup __end_pci_fixups_resume_early[]; | 3115 | extern struct pci_fixup __end_pci_fixups_resume_early[]; |
| 3019 | extern struct pci_fixup __start_pci_fixups_suspend[]; | 3116 | extern struct pci_fixup __start_pci_fixups_suspend[]; |
| 3020 | extern struct pci_fixup __end_pci_fixups_suspend[]; | 3117 | extern struct pci_fixup __end_pci_fixups_suspend[]; |
| 3118 | extern struct pci_fixup __start_pci_fixups_suspend_late[]; | ||
| 3119 | extern struct pci_fixup __end_pci_fixups_suspend_late[]; | ||
| 3021 | 3120 | ||
| 3022 | static bool pci_apply_fixup_final_quirks; | 3121 | static bool pci_apply_fixup_final_quirks; |
| 3023 | 3122 | ||
| @@ -3063,6 +3162,11 @@ void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) | |||
| 3063 | end = __end_pci_fixups_suspend; | 3162 | end = __end_pci_fixups_suspend; |
| 3064 | break; | 3163 | break; |
| 3065 | 3164 | ||
| 3165 | case pci_fixup_suspend_late: | ||
| 3166 | start = __start_pci_fixups_suspend_late; | ||
| 3167 | end = __end_pci_fixups_suspend_late; | ||
| 3168 | break; | ||
| 3169 | |||
| 3066 | default: | 3170 | default: |
| 3067 | /* stupid compiler warning, you would think with an enum... */ | 3171 | /* stupid compiler warning, you would think with an enum... */ |
| 3068 | return; | 3172 | return; |
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index 0c657d6af03d..51cf8083b299 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig | |||
| @@ -202,6 +202,7 @@ config PCMCIA_SA1111 | |||
| 202 | depends on ARM && SA1111 && PCMCIA | 202 | depends on ARM && SA1111 && PCMCIA |
| 203 | select PCMCIA_SOC_COMMON | 203 | select PCMCIA_SOC_COMMON |
| 204 | select PCMCIA_SA11XX_BASE if ARCH_SA1100 | 204 | select PCMCIA_SA11XX_BASE if ARCH_SA1100 |
| 205 | select PCMCIA_PXA2XX if ARCH_LUBBOCK && SA1111 | ||
| 205 | help | 206 | help |
| 206 | Say Y here to include support for SA1111-based PCMCIA or CF | 207 | Say Y here to include support for SA1111-based PCMCIA or CF |
| 207 | sockets, found on the Jornada 720, Graphicsmaster and other | 208 | sockets, found on the Jornada 720, Graphicsmaster and other |
| @@ -217,7 +218,6 @@ config PCMCIA_PXA2XX | |||
| 217 | || ARCOM_PCMCIA || ARCH_PXA_ESERIES || MACH_STARGATE2 \ | 218 | || ARCOM_PCMCIA || ARCH_PXA_ESERIES || MACH_STARGATE2 \ |
| 218 | || MACH_VPAC270 || MACH_BALLOON3 || MACH_COLIBRI \ | 219 | || MACH_VPAC270 || MACH_BALLOON3 || MACH_COLIBRI \ |
| 219 | || MACH_COLIBRI320 || MACH_H4700) | 220 | || MACH_COLIBRI320 || MACH_H4700) |
| 220 | select PCMCIA_SA1111 if ARCH_LUBBOCK && SA1111 | ||
| 221 | select PCMCIA_SOC_COMMON | 221 | select PCMCIA_SOC_COMMON |
| 222 | help | 222 | help |
| 223 | Say Y here to include support for the PXA2xx PCMCIA controller | 223 | Say Y here to include support for the PXA2xx PCMCIA controller |
diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 7745b512a87c..fd55a6951402 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile | |||
| @@ -49,6 +49,7 @@ sa1100_cs-y += sa1100_generic.o | |||
| 49 | sa1100_cs-$(CONFIG_SA1100_ASSABET) += sa1100_assabet.o | 49 | sa1100_cs-$(CONFIG_SA1100_ASSABET) += sa1100_assabet.o |
| 50 | sa1100_cs-$(CONFIG_SA1100_CERF) += sa1100_cerf.o | 50 | sa1100_cs-$(CONFIG_SA1100_CERF) += sa1100_cerf.o |
| 51 | sa1100_cs-$(CONFIG_SA1100_COLLIE) += pxa2xx_sharpsl.o | 51 | sa1100_cs-$(CONFIG_SA1100_COLLIE) += pxa2xx_sharpsl.o |
| 52 | sa1100_cs-$(CONFIG_SA1100_H3100) += sa1100_h3600.o | ||
| 52 | sa1100_cs-$(CONFIG_SA1100_H3600) += sa1100_h3600.o | 53 | sa1100_cs-$(CONFIG_SA1100_H3600) += sa1100_h3600.o |
| 53 | sa1100_cs-$(CONFIG_SA1100_NANOENGINE) += sa1100_nanoengine.o | 54 | sa1100_cs-$(CONFIG_SA1100_NANOENGINE) += sa1100_nanoengine.o |
| 54 | sa1100_cs-$(CONFIG_SA1100_SHANNON) += sa1100_shannon.o | 55 | sa1100_cs-$(CONFIG_SA1100_SHANNON) += sa1100_shannon.o |
diff --git a/drivers/pcmcia/bcm63xx_pcmcia.c b/drivers/pcmcia/bcm63xx_pcmcia.c index 0c6aac1232fc..0802e0bc7d0c 100644 --- a/drivers/pcmcia/bcm63xx_pcmcia.c +++ b/drivers/pcmcia/bcm63xx_pcmcia.c | |||
| @@ -475,7 +475,7 @@ static void bcm63xx_cb_exit(struct pci_dev *dev) | |||
| 475 | bcm63xx_cb_dev = NULL; | 475 | bcm63xx_cb_dev = NULL; |
| 476 | } | 476 | } |
| 477 | 477 | ||
| 478 | static DEFINE_PCI_DEVICE_TABLE(bcm63xx_cb_table) = { | 478 | static const struct pci_device_id bcm63xx_cb_table[] = { |
| 479 | { | 479 | { |
| 480 | .vendor = PCI_VENDOR_ID_BROADCOM, | 480 | .vendor = PCI_VENDOR_ID_BROADCOM, |
| 481 | .device = BCM6348_CPU_ID, | 481 | .device = BCM6348_CPU_ID, |
diff --git a/drivers/pcmcia/i82092.c b/drivers/pcmcia/i82092.c index 7d47456429a1..aae7e6df99cd 100644 --- a/drivers/pcmcia/i82092.c +++ b/drivers/pcmcia/i82092.c | |||
| @@ -25,7 +25,7 @@ | |||
| 25 | MODULE_LICENSE("GPL"); | 25 | MODULE_LICENSE("GPL"); |
| 26 | 26 | ||
| 27 | /* PCI core routines */ | 27 | /* PCI core routines */ |
| 28 | static DEFINE_PCI_DEVICE_TABLE(i82092aa_pci_ids) = { | 28 | static const struct pci_device_id i82092aa_pci_ids[] = { |
| 29 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82092AA_0) }, | 29 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82092AA_0) }, |
| 30 | { } | 30 | { } |
| 31 | }; | 31 | }; |
diff --git a/drivers/pcmcia/pd6729.c b/drivers/pcmcia/pd6729.c index 622dd6fe7347..34ace4854dc2 100644 --- a/drivers/pcmcia/pd6729.c +++ b/drivers/pcmcia/pd6729.c | |||
| @@ -764,7 +764,7 @@ static void pd6729_pci_remove(struct pci_dev *dev) | |||
| 764 | kfree(socket); | 764 | kfree(socket); |
| 765 | } | 765 | } |
| 766 | 766 | ||
| 767 | static DEFINE_PCI_DEVICE_TABLE(pd6729_pci_ids) = { | 767 | static const struct pci_device_id pd6729_pci_ids[] = { |
| 768 | { PCI_DEVICE(PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_6729) }, | 768 | { PCI_DEVICE(PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_6729) }, |
| 769 | { } | 769 | { } |
| 770 | }; | 770 | }; |
diff --git a/drivers/pcmcia/sa1111_jornada720.c b/drivers/pcmcia/sa1111_jornada720.c index 3baa3ef09682..40e040314503 100644 --- a/drivers/pcmcia/sa1111_jornada720.c +++ b/drivers/pcmcia/sa1111_jornada720.c | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <linux/device.h> | 9 | #include <linux/device.h> |
| 10 | #include <linux/errno.h> | 10 | #include <linux/errno.h> |
| 11 | #include <linux/init.h> | 11 | #include <linux/init.h> |
| 12 | #include <linux/io.h> | ||
| 12 | 13 | ||
| 13 | #include <mach/hardware.h> | 14 | #include <mach/hardware.h> |
| 14 | #include <asm/hardware/sa1111.h> | 15 | #include <asm/hardware/sa1111.h> |
| @@ -94,6 +95,7 @@ static struct pcmcia_low_level jornada720_pcmcia_ops = { | |||
| 94 | int pcmcia_jornada720_init(struct device *dev) | 95 | int pcmcia_jornada720_init(struct device *dev) |
| 95 | { | 96 | { |
| 96 | int ret = -ENODEV; | 97 | int ret = -ENODEV; |
| 98 | struct sa1111_dev *sadev = SA1111_DEV(dev); | ||
| 97 | 99 | ||
| 98 | if (machine_is_jornada720()) { | 100 | if (machine_is_jornada720()) { |
| 99 | unsigned int pin = GPIO_A0 | GPIO_A1 | GPIO_A2 | GPIO_A3; | 101 | unsigned int pin = GPIO_A0 | GPIO_A1 | GPIO_A2 | GPIO_A3; |
| @@ -101,12 +103,12 @@ int pcmcia_jornada720_init(struct device *dev) | |||
| 101 | GRER |= 0x00000002; | 103 | GRER |= 0x00000002; |
| 102 | 104 | ||
| 103 | /* Set GPIO_A<3:1> to be outputs for PCMCIA/CF power controller: */ | 105 | /* Set GPIO_A<3:1> to be outputs for PCMCIA/CF power controller: */ |
| 104 | sa1111_set_io_dir(dev, pin, 0, 0); | 106 | sa1111_set_io_dir(sadev, pin, 0, 0); |
| 105 | sa1111_set_io(dev, pin, 0); | 107 | sa1111_set_io(sadev, pin, 0); |
| 106 | sa1111_set_sleep_io(dev, pin, 0); | 108 | sa1111_set_sleep_io(sadev, pin, 0); |
| 107 | 109 | ||
| 108 | sa11xx_drv_pcmcia_ops(&jornada720_pcmcia_ops); | 110 | sa11xx_drv_pcmcia_ops(&jornada720_pcmcia_ops); |
| 109 | ret = sa1111_pcmcia_add(dev, &jornada720_pcmcia_ops, | 111 | ret = sa1111_pcmcia_add(sadev, &jornada720_pcmcia_ops, |
| 110 | sa11xx_drv_pcmcia_add_one); | 112 | sa11xx_drv_pcmcia_add_one); |
| 111 | } | 113 | } |
| 112 | 114 | ||
diff --git a/drivers/pcmcia/vrc4173_cardu.c b/drivers/pcmcia/vrc4173_cardu.c index d92692056e24..9fb0c3addfd4 100644 --- a/drivers/pcmcia/vrc4173_cardu.c +++ b/drivers/pcmcia/vrc4173_cardu.c | |||
| @@ -563,7 +563,7 @@ static int vrc4173_cardu_setup(char *options) | |||
| 563 | 563 | ||
| 564 | __setup("vrc4173_cardu=", vrc4173_cardu_setup); | 564 | __setup("vrc4173_cardu=", vrc4173_cardu_setup); |
| 565 | 565 | ||
| 566 | static DEFINE_PCI_DEVICE_TABLE(vrc4173_cardu_id_table) = { | 566 | static const struct pci_device_id vrc4173_cardu_id_table[] = { |
| 567 | { PCI_DEVICE(PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_NAPCCARD) }, | 567 | { PCI_DEVICE(PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_NAPCCARD) }, |
| 568 | {0, } | 568 | {0, } |
| 569 | }; | 569 | }; |
diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index 946f90ef6020..8a23ccb41213 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c | |||
| @@ -1352,7 +1352,7 @@ static const struct dev_pm_ops yenta_pm_ops = { | |||
| 1352 | .driver_data = CARDBUS_TYPE_##type, \ | 1352 | .driver_data = CARDBUS_TYPE_##type, \ |
| 1353 | } | 1353 | } |
| 1354 | 1354 | ||
| 1355 | static DEFINE_PCI_DEVICE_TABLE(yenta_table) = { | 1355 | static const struct pci_device_id yenta_table[] = { |
| 1356 | CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1031, TI), | 1356 | CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1031, TI), |
| 1357 | 1357 | ||
| 1358 | /* | 1358 | /* |
diff --git a/drivers/regulator/max77693.c b/drivers/regulator/max77693.c index 653a58b49cdf..c67ff05fc1dd 100644 --- a/drivers/regulator/max77693.c +++ b/drivers/regulator/max77693.c | |||
| @@ -31,6 +31,7 @@ | |||
| 31 | #include <linux/mfd/max77693.h> | 31 | #include <linux/mfd/max77693.h> |
| 32 | #include <linux/mfd/max77693-private.h> | 32 | #include <linux/mfd/max77693-private.h> |
| 33 | #include <linux/regulator/of_regulator.h> | 33 | #include <linux/regulator/of_regulator.h> |
| 34 | #include <linux/regmap.h> | ||
| 34 | 35 | ||
| 35 | #define CHGIN_ILIM_STEP_20mA 20000 | 36 | #define CHGIN_ILIM_STEP_20mA 20000 |
| 36 | 37 | ||
| @@ -39,9 +40,9 @@ | |||
| 39 | static int max77693_chg_is_enabled(struct regulator_dev *rdev) | 40 | static int max77693_chg_is_enabled(struct regulator_dev *rdev) |
| 40 | { | 41 | { |
| 41 | int ret; | 42 | int ret; |
| 42 | u8 val; | 43 | unsigned int val; |
| 43 | 44 | ||
| 44 | ret = max77693_read_reg(rdev->regmap, rdev->desc->enable_reg, &val); | 45 | ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &val); |
| 45 | if (ret) | 46 | if (ret) |
| 46 | return ret; | 47 | return ret; |
| 47 | 48 | ||
| @@ -57,12 +58,11 @@ static int max77693_chg_get_current_limit(struct regulator_dev *rdev) | |||
| 57 | { | 58 | { |
| 58 | unsigned int chg_min_uA = rdev->constraints->min_uA; | 59 | unsigned int chg_min_uA = rdev->constraints->min_uA; |
| 59 | unsigned int chg_max_uA = rdev->constraints->max_uA; | 60 | unsigned int chg_max_uA = rdev->constraints->max_uA; |
| 60 | u8 reg, sel; | 61 | unsigned int reg, sel; |
| 61 | unsigned int val; | 62 | unsigned int val; |
| 62 | int ret; | 63 | int ret; |
| 63 | 64 | ||
| 64 | ret = max77693_read_reg(rdev->regmap, | 65 | ret = regmap_read(rdev->regmap, MAX77693_CHG_REG_CHG_CNFG_09, ®); |
| 65 | MAX77693_CHG_REG_CHG_CNFG_09, ®); | ||
| 66 | if (ret < 0) | 66 | if (ret < 0) |
| 67 | return ret; | 67 | return ret; |
| 68 | 68 | ||
| @@ -96,7 +96,7 @@ static int max77693_chg_set_current_limit(struct regulator_dev *rdev, | |||
| 96 | /* the first four codes for charger current are all 60mA */ | 96 | /* the first four codes for charger current are all 60mA */ |
| 97 | sel += 3; | 97 | sel += 3; |
| 98 | 98 | ||
| 99 | return max77693_write_reg(rdev->regmap, | 99 | return regmap_write(rdev->regmap, |
| 100 | MAX77693_CHG_REG_CHG_CNFG_09, sel); | 100 | MAX77693_CHG_REG_CHG_CNFG_09, sel); |
| 101 | } | 101 | } |
| 102 | /* end of CHARGER regulator ops */ | 102 | /* end of CHARGER regulator ops */ |
diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c index 3b5780710d50..1d92f5103ebf 100644 --- a/drivers/spmi/spmi.c +++ b/drivers/spmi/spmi.c | |||
| @@ -18,7 +18,6 @@ | |||
| 18 | #include <linux/of_device.h> | 18 | #include <linux/of_device.h> |
| 19 | #include <linux/platform_device.h> | 19 | #include <linux/platform_device.h> |
| 20 | #include <linux/spmi.h> | 20 | #include <linux/spmi.h> |
| 21 | #include <linux/module.h> | ||
| 22 | #include <linux/pm_runtime.h> | 21 | #include <linux/pm_runtime.h> |
| 23 | 22 | ||
| 24 | #include <dt-bindings/spmi/spmi.h> | 23 | #include <dt-bindings/spmi/spmi.h> |
diff --git a/drivers/thunderbolt/Kconfig b/drivers/thunderbolt/Kconfig new file mode 100644 index 000000000000..c121acc15bfe --- /dev/null +++ b/drivers/thunderbolt/Kconfig | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | menuconfig THUNDERBOLT | ||
| 2 | tristate "Thunderbolt support for Apple devices" | ||
| 3 | depends on PCI | ||
| 4 | select CRC32 | ||
| 5 | help | ||
| 6 | Cactus Ridge Thunderbolt Controller driver | ||
| 7 | This driver is required if you want to hotplug Thunderbolt devices on | ||
| 8 | Apple hardware. | ||
| 9 | |||
| 10 | Device chaining is currently not supported. | ||
| 11 | |||
| 12 | To compile this driver a module, choose M here. The module will be | ||
| 13 | called thunderbolt. | ||
diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile new file mode 100644 index 000000000000..5d1053cdfa54 --- /dev/null +++ b/drivers/thunderbolt/Makefile | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | obj-${CONFIG_THUNDERBOLT} := thunderbolt.o | ||
| 2 | thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel_pci.o eeprom.o | ||
| 3 | |||
diff --git a/drivers/thunderbolt/cap.c b/drivers/thunderbolt/cap.c new file mode 100644 index 000000000000..a7b47e7cddbd --- /dev/null +++ b/drivers/thunderbolt/cap.c | |||
| @@ -0,0 +1,116 @@ | |||
| 1 | /* | ||
| 2 | * Thunderbolt Cactus Ridge driver - capabilities lookup | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include <linux/slab.h> | ||
| 8 | #include <linux/errno.h> | ||
| 9 | |||
| 10 | #include "tb.h" | ||
| 11 | |||
| 12 | |||
| 13 | struct tb_cap_any { | ||
| 14 | union { | ||
| 15 | struct tb_cap_basic basic; | ||
| 16 | struct tb_cap_extended_short extended_short; | ||
| 17 | struct tb_cap_extended_long extended_long; | ||
| 18 | }; | ||
| 19 | } __packed; | ||
| 20 | |||
| 21 | static bool tb_cap_is_basic(struct tb_cap_any *cap) | ||
| 22 | { | ||
| 23 | /* basic.cap is u8. This checks only the lower 8 bit of cap. */ | ||
| 24 | return cap->basic.cap != 5; | ||
| 25 | } | ||
| 26 | |||
| 27 | static bool tb_cap_is_long(struct tb_cap_any *cap) | ||
| 28 | { | ||
| 29 | return !tb_cap_is_basic(cap) | ||
| 30 | && cap->extended_short.next == 0 | ||
| 31 | && cap->extended_short.length == 0; | ||
| 32 | } | ||
| 33 | |||
| 34 | static enum tb_cap tb_cap(struct tb_cap_any *cap) | ||
| 35 | { | ||
| 36 | if (tb_cap_is_basic(cap)) | ||
| 37 | return cap->basic.cap; | ||
| 38 | else | ||
| 39 | /* extended_short/long have cap at the same offset. */ | ||
| 40 | return cap->extended_short.cap; | ||
| 41 | } | ||
| 42 | |||
| 43 | static u32 tb_cap_next(struct tb_cap_any *cap, u32 offset) | ||
| 44 | { | ||
| 45 | int next; | ||
| 46 | if (offset == 1) { | ||
| 47 | /* | ||
| 48 | * The first pointer is part of the switch header and always | ||
| 49 | * a simple pointer. | ||
| 50 | */ | ||
| 51 | next = cap->basic.next; | ||
| 52 | } else { | ||
| 53 | /* | ||
| 54 | * Somehow Intel decided to use 3 different types of capability | ||
| 55 | * headers. It is not like anyone could have predicted that | ||
| 56 | * single byte offsets are not enough... | ||
| 57 | */ | ||
| 58 | if (tb_cap_is_basic(cap)) | ||
| 59 | next = cap->basic.next; | ||
| 60 | else if (!tb_cap_is_long(cap)) | ||
| 61 | next = cap->extended_short.next; | ||
| 62 | else | ||
| 63 | next = cap->extended_long.next; | ||
| 64 | } | ||
| 65 | /* | ||
| 66 | * "Hey, we could terminate some capability lists with a null offset | ||
| 67 | * and others with a pointer to the last element." - "Great idea!" | ||
| 68 | */ | ||
| 69 | if (next == offset) | ||
| 70 | return 0; | ||
| 71 | return next; | ||
| 72 | } | ||
| 73 | |||
| 74 | /** | ||
| 75 | * tb_find_cap() - find a capability | ||
| 76 | * | ||
| 77 | * Return: Returns a positive offset if the capability was found and 0 if not. | ||
| 78 | * Returns an error code on failure. | ||
| 79 | */ | ||
| 80 | int tb_find_cap(struct tb_port *port, enum tb_cfg_space space, enum tb_cap cap) | ||
| 81 | { | ||
| 82 | u32 offset = 1; | ||
| 83 | struct tb_cap_any header; | ||
| 84 | int res; | ||
| 85 | int retries = 10; | ||
| 86 | while (retries--) { | ||
| 87 | res = tb_port_read(port, &header, space, offset, 1); | ||
| 88 | if (res) { | ||
| 89 | /* Intel needs some help with linked lists. */ | ||
| 90 | if (space == TB_CFG_PORT && offset == 0xa | ||
| 91 | && port->config.type == TB_TYPE_DP_HDMI_OUT) { | ||
| 92 | offset = 0x39; | ||
| 93 | continue; | ||
| 94 | } | ||
| 95 | return res; | ||
| 96 | } | ||
| 97 | if (offset != 1) { | ||
| 98 | if (tb_cap(&header) == cap) | ||
| 99 | return offset; | ||
| 100 | if (tb_cap_is_long(&header)) { | ||
| 101 | /* tb_cap_extended_long is 2 dwords */ | ||
| 102 | res = tb_port_read(port, &header, space, | ||
| 103 | offset, 2); | ||
| 104 | if (res) | ||
| 105 | return res; | ||
| 106 | } | ||
| 107 | } | ||
| 108 | offset = tb_cap_next(&header, offset); | ||
| 109 | if (!offset) | ||
| 110 | return 0; | ||
| 111 | } | ||
| 112 | tb_port_WARN(port, | ||
| 113 | "run out of retries while looking for cap %#x in config space %d, last offset: %#x\n", | ||
| 114 | cap, space, offset); | ||
| 115 | return -EIO; | ||
| 116 | } | ||
diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c new file mode 100644 index 000000000000..799634b382c6 --- /dev/null +++ b/drivers/thunderbolt/ctl.c | |||
| @@ -0,0 +1,731 @@ | |||
| 1 | /* | ||
| 2 | * Thunderbolt Cactus Ridge driver - control channel and configuration commands | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include <linux/crc32.h> | ||
| 8 | #include <linux/slab.h> | ||
| 9 | #include <linux/pci.h> | ||
| 10 | #include <linux/dmapool.h> | ||
| 11 | #include <linux/workqueue.h> | ||
| 12 | #include <linux/kfifo.h> | ||
| 13 | |||
| 14 | #include "ctl.h" | ||
| 15 | |||
| 16 | |||
| 17 | struct ctl_pkg { | ||
| 18 | struct tb_ctl *ctl; | ||
| 19 | void *buffer; | ||
| 20 | struct ring_frame frame; | ||
| 21 | }; | ||
| 22 | |||
| 23 | #define TB_CTL_RX_PKG_COUNT 10 | ||
| 24 | |||
| 25 | /** | ||
| 26 | * struct tb_cfg - thunderbolt control channel | ||
| 27 | */ | ||
| 28 | struct tb_ctl { | ||
| 29 | struct tb_nhi *nhi; | ||
| 30 | struct tb_ring *tx; | ||
| 31 | struct tb_ring *rx; | ||
| 32 | |||
| 33 | struct dma_pool *frame_pool; | ||
| 34 | struct ctl_pkg *rx_packets[TB_CTL_RX_PKG_COUNT]; | ||
| 35 | DECLARE_KFIFO(response_fifo, struct ctl_pkg*, 16); | ||
| 36 | struct completion response_ready; | ||
| 37 | |||
| 38 | hotplug_cb callback; | ||
| 39 | void *callback_data; | ||
| 40 | }; | ||
| 41 | |||
| 42 | |||
| 43 | #define tb_ctl_WARN(ctl, format, arg...) \ | ||
| 44 | dev_WARN(&(ctl)->nhi->pdev->dev, format, ## arg) | ||
| 45 | |||
| 46 | #define tb_ctl_err(ctl, format, arg...) \ | ||
| 47 | dev_err(&(ctl)->nhi->pdev->dev, format, ## arg) | ||
| 48 | |||
| 49 | #define tb_ctl_warn(ctl, format, arg...) \ | ||
| 50 | dev_warn(&(ctl)->nhi->pdev->dev, format, ## arg) | ||
| 51 | |||
| 52 | #define tb_ctl_info(ctl, format, arg...) \ | ||
| 53 | dev_info(&(ctl)->nhi->pdev->dev, format, ## arg) | ||
| 54 | |||
| 55 | |||
| 56 | /* configuration packets definitions */ | ||
| 57 | |||
| 58 | enum tb_cfg_pkg_type { | ||
| 59 | TB_CFG_PKG_READ = 1, | ||
| 60 | TB_CFG_PKG_WRITE = 2, | ||
| 61 | TB_CFG_PKG_ERROR = 3, | ||
| 62 | TB_CFG_PKG_NOTIFY_ACK = 4, | ||
| 63 | TB_CFG_PKG_EVENT = 5, | ||
| 64 | TB_CFG_PKG_XDOMAIN_REQ = 6, | ||
| 65 | TB_CFG_PKG_XDOMAIN_RESP = 7, | ||
| 66 | TB_CFG_PKG_OVERRIDE = 8, | ||
| 67 | TB_CFG_PKG_RESET = 9, | ||
| 68 | TB_CFG_PKG_PREPARE_TO_SLEEP = 0xd, | ||
| 69 | }; | ||
| 70 | |||
| 71 | /* common header */ | ||
| 72 | struct tb_cfg_header { | ||
| 73 | u32 route_hi:22; | ||
| 74 | u32 unknown:10; /* highest order bit is set on replies */ | ||
| 75 | u32 route_lo; | ||
| 76 | } __packed; | ||
| 77 | |||
| 78 | /* additional header for read/write packets */ | ||
| 79 | struct tb_cfg_address { | ||
| 80 | u32 offset:13; /* in dwords */ | ||
| 81 | u32 length:6; /* in dwords */ | ||
| 82 | u32 port:6; | ||
| 83 | enum tb_cfg_space space:2; | ||
| 84 | u32 seq:2; /* sequence number */ | ||
| 85 | u32 zero:3; | ||
| 86 | } __packed; | ||
| 87 | |||
| 88 | /* TB_CFG_PKG_READ, response for TB_CFG_PKG_WRITE */ | ||
| 89 | struct cfg_read_pkg { | ||
| 90 | struct tb_cfg_header header; | ||
| 91 | struct tb_cfg_address addr; | ||
| 92 | } __packed; | ||
| 93 | |||
| 94 | /* TB_CFG_PKG_WRITE, response for TB_CFG_PKG_READ */ | ||
| 95 | struct cfg_write_pkg { | ||
| 96 | struct tb_cfg_header header; | ||
| 97 | struct tb_cfg_address addr; | ||
| 98 | u32 data[64]; /* maximum size, tb_cfg_address.length has 6 bits */ | ||
| 99 | } __packed; | ||
| 100 | |||
| 101 | /* TB_CFG_PKG_ERROR */ | ||
| 102 | struct cfg_error_pkg { | ||
| 103 | struct tb_cfg_header header; | ||
| 104 | enum tb_cfg_error error:4; | ||
| 105 | u32 zero1:4; | ||
| 106 | u32 port:6; | ||
| 107 | u32 zero2:2; /* Both should be zero, still they are different fields. */ | ||
| 108 | u32 zero3:16; | ||
| 109 | } __packed; | ||
| 110 | |||
| 111 | /* TB_CFG_PKG_EVENT */ | ||
| 112 | struct cfg_event_pkg { | ||
| 113 | struct tb_cfg_header header; | ||
| 114 | u32 port:6; | ||
| 115 | u32 zero:25; | ||
| 116 | bool unplug:1; | ||
| 117 | } __packed; | ||
| 118 | |||
| 119 | /* TB_CFG_PKG_RESET */ | ||
| 120 | struct cfg_reset_pkg { | ||
| 121 | struct tb_cfg_header header; | ||
| 122 | } __packed; | ||
| 123 | |||
| 124 | /* TB_CFG_PKG_PREPARE_TO_SLEEP */ | ||
| 125 | struct cfg_pts_pkg { | ||
| 126 | struct tb_cfg_header header; | ||
| 127 | u32 data; | ||
| 128 | } __packed; | ||
| 129 | |||
| 130 | |||
| 131 | /* utility functions */ | ||
| 132 | |||
| 133 | static u64 get_route(struct tb_cfg_header header) | ||
| 134 | { | ||
| 135 | return (u64) header.route_hi << 32 | header.route_lo; | ||
| 136 | } | ||
| 137 | |||
| 138 | static struct tb_cfg_header make_header(u64 route) | ||
| 139 | { | ||
| 140 | struct tb_cfg_header header = { | ||
| 141 | .route_hi = route >> 32, | ||
| 142 | .route_lo = route, | ||
| 143 | }; | ||
| 144 | /* check for overflow, route_hi is not 32 bits! */ | ||
| 145 | WARN_ON(get_route(header) != route); | ||
| 146 | return header; | ||
| 147 | } | ||
| 148 | |||
| 149 | static int check_header(struct ctl_pkg *pkg, u32 len, enum tb_cfg_pkg_type type, | ||
| 150 | u64 route) | ||
| 151 | { | ||
| 152 | struct tb_cfg_header *header = pkg->buffer; | ||
| 153 | |||
| 154 | /* check frame, TODO: frame flags */ | ||
| 155 | if (WARN(len != pkg->frame.size, | ||
| 156 | "wrong framesize (expected %#x, got %#x)\n", | ||
| 157 | len, pkg->frame.size)) | ||
| 158 | return -EIO; | ||
| 159 | if (WARN(type != pkg->frame.eof, "wrong eof (expected %#x, got %#x)\n", | ||
| 160 | type, pkg->frame.eof)) | ||
| 161 | return -EIO; | ||
| 162 | if (WARN(pkg->frame.sof, "wrong sof (expected 0x0, got %#x)\n", | ||
| 163 | pkg->frame.sof)) | ||
| 164 | return -EIO; | ||
| 165 | |||
| 166 | /* check header */ | ||
| 167 | if (WARN(header->unknown != 1 << 9, | ||
| 168 | "header->unknown is %#x\n", header->unknown)) | ||
| 169 | return -EIO; | ||
| 170 | if (WARN(route != get_route(*header), | ||
| 171 | "wrong route (expected %llx, got %llx)", | ||
| 172 | route, get_route(*header))) | ||
| 173 | return -EIO; | ||
| 174 | return 0; | ||
| 175 | } | ||
| 176 | |||
| 177 | static int check_config_address(struct tb_cfg_address addr, | ||
| 178 | enum tb_cfg_space space, u32 offset, | ||
| 179 | u32 length) | ||
| 180 | { | ||
| 181 | if (WARN(addr.zero, "addr.zero is %#x\n", addr.zero)) | ||
| 182 | return -EIO; | ||
| 183 | if (WARN(space != addr.space, "wrong space (expected %x, got %x\n)", | ||
| 184 | space, addr.space)) | ||
| 185 | return -EIO; | ||
| 186 | if (WARN(offset != addr.offset, "wrong offset (expected %x, got %x\n)", | ||
| 187 | offset, addr.offset)) | ||
| 188 | return -EIO; | ||
| 189 | if (WARN(length != addr.length, "wrong space (expected %x, got %x\n)", | ||
| 190 | length, addr.length)) | ||
| 191 | return -EIO; | ||
| 192 | if (WARN(addr.seq, "addr.seq is %#x\n", addr.seq)) | ||
| 193 | return -EIO; | ||
| 194 | /* | ||
| 195 | * We cannot check addr->port as it is set to the upstream port of the | ||
| 196 | * sender. | ||
| 197 | */ | ||
| 198 | return 0; | ||
| 199 | } | ||
| 200 | |||
| 201 | static struct tb_cfg_result decode_error(struct ctl_pkg *response) | ||
| 202 | { | ||
| 203 | struct cfg_error_pkg *pkg = response->buffer; | ||
| 204 | struct tb_cfg_result res = { 0 }; | ||
| 205 | res.response_route = get_route(pkg->header); | ||
| 206 | res.response_port = 0; | ||
| 207 | res.err = check_header(response, sizeof(*pkg), TB_CFG_PKG_ERROR, | ||
| 208 | get_route(pkg->header)); | ||
| 209 | if (res.err) | ||
| 210 | return res; | ||
| 211 | |||
| 212 | WARN(pkg->zero1, "pkg->zero1 is %#x\n", pkg->zero1); | ||
| 213 | WARN(pkg->zero2, "pkg->zero1 is %#x\n", pkg->zero1); | ||
| 214 | WARN(pkg->zero3, "pkg->zero1 is %#x\n", pkg->zero1); | ||
| 215 | res.err = 1; | ||
| 216 | res.tb_error = pkg->error; | ||
| 217 | res.response_port = pkg->port; | ||
| 218 | return res; | ||
| 219 | |||
| 220 | } | ||
| 221 | |||
| 222 | static struct tb_cfg_result parse_header(struct ctl_pkg *pkg, u32 len, | ||
| 223 | enum tb_cfg_pkg_type type, u64 route) | ||
| 224 | { | ||
| 225 | struct tb_cfg_header *header = pkg->buffer; | ||
| 226 | struct tb_cfg_result res = { 0 }; | ||
| 227 | |||
| 228 | if (pkg->frame.eof == TB_CFG_PKG_ERROR) | ||
| 229 | return decode_error(pkg); | ||
| 230 | |||
| 231 | res.response_port = 0; /* will be updated later for cfg_read/write */ | ||
| 232 | res.response_route = get_route(*header); | ||
| 233 | res.err = check_header(pkg, len, type, route); | ||
| 234 | return res; | ||
| 235 | } | ||
| 236 | |||
| 237 | static void tb_cfg_print_error(struct tb_ctl *ctl, | ||
| 238 | const struct tb_cfg_result *res) | ||
| 239 | { | ||
| 240 | WARN_ON(res->err != 1); | ||
| 241 | switch (res->tb_error) { | ||
| 242 | case TB_CFG_ERROR_PORT_NOT_CONNECTED: | ||
| 243 | /* Port is not connected. This can happen during surprise | ||
| 244 | * removal. Do not warn. */ | ||
| 245 | return; | ||
| 246 | case TB_CFG_ERROR_INVALID_CONFIG_SPACE: | ||
| 247 | /* | ||
| 248 | * Invalid cfg_space/offset/length combination in | ||
| 249 | * cfg_read/cfg_write. | ||
| 250 | */ | ||
| 251 | tb_ctl_WARN(ctl, | ||
| 252 | "CFG_ERROR(%llx:%x): Invalid config space of offset\n", | ||
| 253 | res->response_route, res->response_port); | ||
| 254 | return; | ||
| 255 | case TB_CFG_ERROR_NO_SUCH_PORT: | ||
| 256 | /* | ||
| 257 | * - The route contains a non-existent port. | ||
| 258 | * - The route contains a non-PHY port (e.g. PCIe). | ||
| 259 | * - The port in cfg_read/cfg_write does not exist. | ||
| 260 | */ | ||
| 261 | tb_ctl_WARN(ctl, "CFG_ERROR(%llx:%x): Invalid port\n", | ||
| 262 | res->response_route, res->response_port); | ||
| 263 | return; | ||
| 264 | case TB_CFG_ERROR_LOOP: | ||
| 265 | tb_ctl_WARN(ctl, "CFG_ERROR(%llx:%x): Route contains a loop\n", | ||
| 266 | res->response_route, res->response_port); | ||
| 267 | return; | ||
| 268 | default: | ||
| 269 | /* 5,6,7,9 and 11 are also valid error codes */ | ||
| 270 | tb_ctl_WARN(ctl, "CFG_ERROR(%llx:%x): Unknown error\n", | ||
| 271 | res->response_route, res->response_port); | ||
| 272 | return; | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 276 | static void cpu_to_be32_array(__be32 *dst, u32 *src, size_t len) | ||
| 277 | { | ||
| 278 | int i; | ||
| 279 | for (i = 0; i < len; i++) | ||
| 280 | dst[i] = cpu_to_be32(src[i]); | ||
| 281 | } | ||
| 282 | |||
| 283 | static void be32_to_cpu_array(u32 *dst, __be32 *src, size_t len) | ||
| 284 | { | ||
| 285 | int i; | ||
| 286 | for (i = 0; i < len; i++) | ||
| 287 | dst[i] = be32_to_cpu(src[i]); | ||
| 288 | } | ||
| 289 | |||
| 290 | static __be32 tb_crc(void *data, size_t len) | ||
| 291 | { | ||
| 292 | return cpu_to_be32(~__crc32c_le(~0, data, len)); | ||
| 293 | } | ||
| 294 | |||
| 295 | static void tb_ctl_pkg_free(struct ctl_pkg *pkg) | ||
| 296 | { | ||
| 297 | if (pkg) { | ||
| 298 | dma_pool_free(pkg->ctl->frame_pool, | ||
| 299 | pkg->buffer, pkg->frame.buffer_phy); | ||
| 300 | kfree(pkg); | ||
| 301 | } | ||
| 302 | } | ||
| 303 | |||
| 304 | static struct ctl_pkg *tb_ctl_pkg_alloc(struct tb_ctl *ctl) | ||
| 305 | { | ||
| 306 | struct ctl_pkg *pkg = kzalloc(sizeof(*pkg), GFP_KERNEL); | ||
| 307 | if (!pkg) | ||
| 308 | return NULL; | ||
| 309 | pkg->ctl = ctl; | ||
| 310 | pkg->buffer = dma_pool_alloc(ctl->frame_pool, GFP_KERNEL, | ||
| 311 | &pkg->frame.buffer_phy); | ||
| 312 | if (!pkg->buffer) { | ||
| 313 | kfree(pkg); | ||
| 314 | return NULL; | ||
| 315 | } | ||
| 316 | return pkg; | ||
| 317 | } | ||
| 318 | |||
| 319 | |||
| 320 | /* RX/TX handling */ | ||
| 321 | |||
| 322 | static void tb_ctl_tx_callback(struct tb_ring *ring, struct ring_frame *frame, | ||
| 323 | bool canceled) | ||
| 324 | { | ||
| 325 | struct ctl_pkg *pkg = container_of(frame, typeof(*pkg), frame); | ||
| 326 | tb_ctl_pkg_free(pkg); | ||
| 327 | } | ||
| 328 | |||
| 329 | /** | ||
| 330 | * tb_cfg_tx() - transmit a packet on the control channel | ||
| 331 | * | ||
| 332 | * len must be a multiple of four. | ||
| 333 | * | ||
| 334 | * Return: Returns 0 on success or an error code on failure. | ||
| 335 | */ | ||
| 336 | static int tb_ctl_tx(struct tb_ctl *ctl, void *data, size_t len, | ||
| 337 | enum tb_cfg_pkg_type type) | ||
| 338 | { | ||
| 339 | int res; | ||
| 340 | struct ctl_pkg *pkg; | ||
| 341 | if (len % 4 != 0) { /* required for le->be conversion */ | ||
| 342 | tb_ctl_WARN(ctl, "TX: invalid size: %zu\n", len); | ||
| 343 | return -EINVAL; | ||
| 344 | } | ||
| 345 | if (len > TB_FRAME_SIZE - 4) { /* checksum is 4 bytes */ | ||
| 346 | tb_ctl_WARN(ctl, "TX: packet too large: %zu/%d\n", | ||
| 347 | len, TB_FRAME_SIZE - 4); | ||
| 348 | return -EINVAL; | ||
| 349 | } | ||
| 350 | pkg = tb_ctl_pkg_alloc(ctl); | ||
| 351 | if (!pkg) | ||
| 352 | return -ENOMEM; | ||
| 353 | pkg->frame.callback = tb_ctl_tx_callback; | ||
| 354 | pkg->frame.size = len + 4; | ||
| 355 | pkg->frame.sof = type; | ||
| 356 | pkg->frame.eof = type; | ||
| 357 | cpu_to_be32_array(pkg->buffer, data, len / 4); | ||
| 358 | *(__be32 *) (pkg->buffer + len) = tb_crc(pkg->buffer, len); | ||
| 359 | |||
| 360 | res = ring_tx(ctl->tx, &pkg->frame); | ||
| 361 | if (res) /* ring is stopped */ | ||
| 362 | tb_ctl_pkg_free(pkg); | ||
| 363 | return res; | ||
| 364 | } | ||
| 365 | |||
| 366 | /** | ||
| 367 | * tb_ctl_handle_plug_event() - acknowledge a plug event, invoke ctl->callback | ||
| 368 | */ | ||
| 369 | static void tb_ctl_handle_plug_event(struct tb_ctl *ctl, | ||
| 370 | struct ctl_pkg *response) | ||
| 371 | { | ||
| 372 | struct cfg_event_pkg *pkg = response->buffer; | ||
| 373 | u64 route = get_route(pkg->header); | ||
| 374 | |||
| 375 | if (check_header(response, sizeof(*pkg), TB_CFG_PKG_EVENT, route)) { | ||
| 376 | tb_ctl_warn(ctl, "malformed TB_CFG_PKG_EVENT\n"); | ||
| 377 | return; | ||
| 378 | } | ||
| 379 | |||
| 380 | if (tb_cfg_error(ctl, route, pkg->port, TB_CFG_ERROR_ACK_PLUG_EVENT)) | ||
| 381 | tb_ctl_warn(ctl, "could not ack plug event on %llx:%x\n", | ||
| 382 | route, pkg->port); | ||
| 383 | WARN(pkg->zero, "pkg->zero is %#x\n", pkg->zero); | ||
| 384 | ctl->callback(ctl->callback_data, route, pkg->port, pkg->unplug); | ||
| 385 | } | ||
| 386 | |||
| 387 | static void tb_ctl_rx_submit(struct ctl_pkg *pkg) | ||
| 388 | { | ||
| 389 | ring_rx(pkg->ctl->rx, &pkg->frame); /* | ||
| 390 | * We ignore failures during stop. | ||
| 391 | * All rx packets are referenced | ||
| 392 | * from ctl->rx_packets, so we do | ||
| 393 | * not loose them. | ||
| 394 | */ | ||
| 395 | } | ||
| 396 | |||
| 397 | static void tb_ctl_rx_callback(struct tb_ring *ring, struct ring_frame *frame, | ||
| 398 | bool canceled) | ||
| 399 | { | ||
| 400 | struct ctl_pkg *pkg = container_of(frame, typeof(*pkg), frame); | ||
| 401 | |||
| 402 | if (canceled) | ||
| 403 | return; /* | ||
| 404 | * ring is stopped, packet is referenced from | ||
| 405 | * ctl->rx_packets. | ||
| 406 | */ | ||
| 407 | |||
| 408 | if (frame->size < 4 || frame->size % 4 != 0) { | ||
| 409 | tb_ctl_err(pkg->ctl, "RX: invalid size %#x, dropping packet\n", | ||
| 410 | frame->size); | ||
| 411 | goto rx; | ||
| 412 | } | ||
| 413 | |||
| 414 | frame->size -= 4; /* remove checksum */ | ||
| 415 | if (*(__be32 *) (pkg->buffer + frame->size) | ||
| 416 | != tb_crc(pkg->buffer, frame->size)) { | ||
| 417 | tb_ctl_err(pkg->ctl, | ||
| 418 | "RX: checksum mismatch, dropping packet\n"); | ||
| 419 | goto rx; | ||
| 420 | } | ||
| 421 | be32_to_cpu_array(pkg->buffer, pkg->buffer, frame->size / 4); | ||
| 422 | |||
| 423 | if (frame->eof == TB_CFG_PKG_EVENT) { | ||
| 424 | tb_ctl_handle_plug_event(pkg->ctl, pkg); | ||
| 425 | goto rx; | ||
| 426 | } | ||
| 427 | if (!kfifo_put(&pkg->ctl->response_fifo, pkg)) { | ||
| 428 | tb_ctl_err(pkg->ctl, "RX: fifo is full\n"); | ||
| 429 | goto rx; | ||
| 430 | } | ||
| 431 | complete(&pkg->ctl->response_ready); | ||
| 432 | return; | ||
| 433 | rx: | ||
| 434 | tb_ctl_rx_submit(pkg); | ||
| 435 | } | ||
| 436 | |||
| 437 | /** | ||
| 438 | * tb_ctl_rx() - receive a packet from the control channel | ||
| 439 | */ | ||
| 440 | static struct tb_cfg_result tb_ctl_rx(struct tb_ctl *ctl, void *buffer, | ||
| 441 | size_t length, int timeout_msec, | ||
| 442 | u64 route, enum tb_cfg_pkg_type type) | ||
| 443 | { | ||
| 444 | struct tb_cfg_result res; | ||
| 445 | struct ctl_pkg *pkg; | ||
| 446 | |||
| 447 | if (!wait_for_completion_timeout(&ctl->response_ready, | ||
| 448 | msecs_to_jiffies(timeout_msec))) { | ||
| 449 | tb_ctl_WARN(ctl, "RX: timeout\n"); | ||
| 450 | return (struct tb_cfg_result) { .err = -ETIMEDOUT }; | ||
| 451 | } | ||
| 452 | if (!kfifo_get(&ctl->response_fifo, &pkg)) { | ||
| 453 | tb_ctl_WARN(ctl, "empty kfifo\n"); | ||
| 454 | return (struct tb_cfg_result) { .err = -EIO }; | ||
| 455 | } | ||
| 456 | |||
| 457 | res = parse_header(pkg, length, type, route); | ||
| 458 | if (!res.err) | ||
| 459 | memcpy(buffer, pkg->buffer, length); | ||
| 460 | tb_ctl_rx_submit(pkg); | ||
| 461 | return res; | ||
| 462 | } | ||
| 463 | |||
| 464 | |||
| 465 | /* public interface, alloc/start/stop/free */ | ||
| 466 | |||
| 467 | /** | ||
| 468 | * tb_ctl_alloc() - allocate a control channel | ||
| 469 | * | ||
| 470 | * cb will be invoked once for every hot plug event. | ||
| 471 | * | ||
| 472 | * Return: Returns a pointer on success or NULL on failure. | ||
| 473 | */ | ||
| 474 | struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, hotplug_cb cb, void *cb_data) | ||
| 475 | { | ||
| 476 | int i; | ||
| 477 | struct tb_ctl *ctl = kzalloc(sizeof(*ctl), GFP_KERNEL); | ||
| 478 | if (!ctl) | ||
| 479 | return NULL; | ||
| 480 | ctl->nhi = nhi; | ||
| 481 | ctl->callback = cb; | ||
| 482 | ctl->callback_data = cb_data; | ||
| 483 | |||
| 484 | init_completion(&ctl->response_ready); | ||
| 485 | INIT_KFIFO(ctl->response_fifo); | ||
| 486 | ctl->frame_pool = dma_pool_create("thunderbolt_ctl", &nhi->pdev->dev, | ||
| 487 | TB_FRAME_SIZE, 4, 0); | ||
| 488 | if (!ctl->frame_pool) | ||
| 489 | goto err; | ||
| 490 | |||
| 491 | ctl->tx = ring_alloc_tx(nhi, 0, 10); | ||
| 492 | if (!ctl->tx) | ||
| 493 | goto err; | ||
| 494 | |||
| 495 | ctl->rx = ring_alloc_rx(nhi, 0, 10); | ||
| 496 | if (!ctl->rx) | ||
| 497 | goto err; | ||
| 498 | |||
| 499 | for (i = 0; i < TB_CTL_RX_PKG_COUNT; i++) { | ||
| 500 | ctl->rx_packets[i] = tb_ctl_pkg_alloc(ctl); | ||
| 501 | if (!ctl->rx_packets[i]) | ||
| 502 | goto err; | ||
| 503 | ctl->rx_packets[i]->frame.callback = tb_ctl_rx_callback; | ||
| 504 | } | ||
| 505 | |||
| 506 | tb_ctl_info(ctl, "control channel created\n"); | ||
| 507 | return ctl; | ||
| 508 | err: | ||
| 509 | tb_ctl_free(ctl); | ||
| 510 | return NULL; | ||
| 511 | } | ||
| 512 | |||
| 513 | /** | ||
| 514 | * tb_ctl_free() - free a control channel | ||
| 515 | * | ||
| 516 | * Must be called after tb_ctl_stop. | ||
| 517 | * | ||
| 518 | * Must NOT be called from ctl->callback. | ||
| 519 | */ | ||
| 520 | void tb_ctl_free(struct tb_ctl *ctl) | ||
| 521 | { | ||
| 522 | int i; | ||
| 523 | if (ctl->rx) | ||
| 524 | ring_free(ctl->rx); | ||
| 525 | if (ctl->tx) | ||
| 526 | ring_free(ctl->tx); | ||
| 527 | |||
| 528 | /* free RX packets */ | ||
| 529 | for (i = 0; i < TB_CTL_RX_PKG_COUNT; i++) | ||
| 530 | tb_ctl_pkg_free(ctl->rx_packets[i]); | ||
| 531 | |||
| 532 | |||
| 533 | if (ctl->frame_pool) | ||
| 534 | dma_pool_destroy(ctl->frame_pool); | ||
| 535 | kfree(ctl); | ||
| 536 | } | ||
| 537 | |||
| 538 | /** | ||
| 539 | * tb_cfg_start() - start/resume the control channel | ||
| 540 | */ | ||
| 541 | void tb_ctl_start(struct tb_ctl *ctl) | ||
| 542 | { | ||
| 543 | int i; | ||
| 544 | tb_ctl_info(ctl, "control channel starting...\n"); | ||
| 545 | ring_start(ctl->tx); /* is used to ack hotplug packets, start first */ | ||
| 546 | ring_start(ctl->rx); | ||
| 547 | for (i = 0; i < TB_CTL_RX_PKG_COUNT; i++) | ||
| 548 | tb_ctl_rx_submit(ctl->rx_packets[i]); | ||
| 549 | } | ||
| 550 | |||
| 551 | /** | ||
| 552 | * control() - pause the control channel | ||
| 553 | * | ||
| 554 | * All invocations of ctl->callback will have finished after this method | ||
| 555 | * returns. | ||
| 556 | * | ||
| 557 | * Must NOT be called from ctl->callback. | ||
| 558 | */ | ||
| 559 | void tb_ctl_stop(struct tb_ctl *ctl) | ||
| 560 | { | ||
| 561 | ring_stop(ctl->rx); | ||
| 562 | ring_stop(ctl->tx); | ||
| 563 | |||
| 564 | if (!kfifo_is_empty(&ctl->response_fifo)) | ||
| 565 | tb_ctl_WARN(ctl, "dangling response in response_fifo\n"); | ||
| 566 | kfifo_reset(&ctl->response_fifo); | ||
| 567 | tb_ctl_info(ctl, "control channel stopped\n"); | ||
| 568 | } | ||
| 569 | |||
| 570 | /* public interface, commands */ | ||
| 571 | |||
| 572 | /** | ||
| 573 | * tb_cfg_error() - send error packet | ||
| 574 | * | ||
| 575 | * Return: Returns 0 on success or an error code on failure. | ||
| 576 | */ | ||
| 577 | int tb_cfg_error(struct tb_ctl *ctl, u64 route, u32 port, | ||
| 578 | enum tb_cfg_error error) | ||
| 579 | { | ||
| 580 | struct cfg_error_pkg pkg = { | ||
| 581 | .header = make_header(route), | ||
| 582 | .port = port, | ||
| 583 | .error = error, | ||
| 584 | }; | ||
| 585 | tb_ctl_info(ctl, "resetting error on %llx:%x.\n", route, port); | ||
| 586 | return tb_ctl_tx(ctl, &pkg, sizeof(pkg), TB_CFG_PKG_ERROR); | ||
| 587 | } | ||
| 588 | |||
| 589 | /** | ||
| 590 | * tb_cfg_reset() - send a reset packet and wait for a response | ||
| 591 | * | ||
| 592 | * If the switch at route is incorrectly configured then we will not receive a | ||
| 593 | * reply (even though the switch will reset). The caller should check for | ||
| 594 | * -ETIMEDOUT and attempt to reconfigure the switch. | ||
| 595 | */ | ||
| 596 | struct tb_cfg_result tb_cfg_reset(struct tb_ctl *ctl, u64 route, | ||
| 597 | int timeout_msec) | ||
| 598 | { | ||
| 599 | int err; | ||
| 600 | struct cfg_reset_pkg request = { .header = make_header(route) }; | ||
| 601 | struct tb_cfg_header reply; | ||
| 602 | |||
| 603 | err = tb_ctl_tx(ctl, &request, sizeof(request), TB_CFG_PKG_RESET); | ||
| 604 | if (err) | ||
| 605 | return (struct tb_cfg_result) { .err = err }; | ||
| 606 | |||
| 607 | return tb_ctl_rx(ctl, &reply, sizeof(reply), timeout_msec, route, | ||
| 608 | TB_CFG_PKG_RESET); | ||
| 609 | } | ||
| 610 | |||
| 611 | /** | ||
| 612 | * tb_cfg_read() - read from config space into buffer | ||
| 613 | * | ||
| 614 | * Offset and length are in dwords. | ||
| 615 | */ | ||
| 616 | struct tb_cfg_result tb_cfg_read_raw(struct tb_ctl *ctl, void *buffer, | ||
| 617 | u64 route, u32 port, enum tb_cfg_space space, | ||
| 618 | u32 offset, u32 length, int timeout_msec) | ||
| 619 | { | ||
| 620 | struct tb_cfg_result res = { 0 }; | ||
| 621 | struct cfg_read_pkg request = { | ||
| 622 | .header = make_header(route), | ||
| 623 | .addr = { | ||
| 624 | .port = port, | ||
| 625 | .space = space, | ||
| 626 | .offset = offset, | ||
| 627 | .length = length, | ||
| 628 | }, | ||
| 629 | }; | ||
| 630 | struct cfg_write_pkg reply; | ||
| 631 | |||
| 632 | res.err = tb_ctl_tx(ctl, &request, sizeof(request), TB_CFG_PKG_READ); | ||
| 633 | if (res.err) | ||
| 634 | return res; | ||
| 635 | |||
| 636 | res = tb_ctl_rx(ctl, &reply, 12 + 4 * length, timeout_msec, route, | ||
| 637 | TB_CFG_PKG_READ); | ||
| 638 | if (res.err) | ||
| 639 | return res; | ||
| 640 | |||
| 641 | res.response_port = reply.addr.port; | ||
| 642 | res.err = check_config_address(reply.addr, space, offset, length); | ||
| 643 | if (!res.err) | ||
| 644 | memcpy(buffer, &reply.data, 4 * length); | ||
| 645 | return res; | ||
| 646 | } | ||
| 647 | |||
| 648 | /** | ||
| 649 | * tb_cfg_write() - write from buffer into config space | ||
| 650 | * | ||
| 651 | * Offset and length are in dwords. | ||
| 652 | */ | ||
| 653 | struct tb_cfg_result tb_cfg_write_raw(struct tb_ctl *ctl, void *buffer, | ||
| 654 | u64 route, u32 port, enum tb_cfg_space space, | ||
| 655 | u32 offset, u32 length, int timeout_msec) | ||
| 656 | { | ||
| 657 | struct tb_cfg_result res = { 0 }; | ||
| 658 | struct cfg_write_pkg request = { | ||
| 659 | .header = make_header(route), | ||
| 660 | .addr = { | ||
| 661 | .port = port, | ||
| 662 | .space = space, | ||
| 663 | .offset = offset, | ||
| 664 | .length = length, | ||
| 665 | }, | ||
| 666 | }; | ||
| 667 | struct cfg_read_pkg reply; | ||
| 668 | |||
| 669 | memcpy(&request.data, buffer, length * 4); | ||
| 670 | |||
| 671 | res.err = tb_ctl_tx(ctl, &request, 12 + 4 * length, TB_CFG_PKG_WRITE); | ||
| 672 | if (res.err) | ||
| 673 | return res; | ||
| 674 | |||
| 675 | res = tb_ctl_rx(ctl, &reply, sizeof(reply), timeout_msec, route, | ||
| 676 | TB_CFG_PKG_WRITE); | ||
| 677 | if (res.err) | ||
| 678 | return res; | ||
| 679 | |||
| 680 | res.response_port = reply.addr.port; | ||
| 681 | res.err = check_config_address(reply.addr, space, offset, length); | ||
| 682 | return res; | ||
| 683 | } | ||
| 684 | |||
| 685 | int tb_cfg_read(struct tb_ctl *ctl, void *buffer, u64 route, u32 port, | ||
| 686 | enum tb_cfg_space space, u32 offset, u32 length) | ||
| 687 | { | ||
| 688 | struct tb_cfg_result res = tb_cfg_read_raw(ctl, buffer, route, port, | ||
| 689 | space, offset, length, TB_CFG_DEFAULT_TIMEOUT); | ||
| 690 | if (res.err == 1) { | ||
| 691 | tb_cfg_print_error(ctl, &res); | ||
| 692 | return -EIO; | ||
| 693 | } | ||
| 694 | WARN(res.err, "tb_cfg_read: %d\n", res.err); | ||
| 695 | return res.err; | ||
| 696 | } | ||
| 697 | |||
| 698 | int tb_cfg_write(struct tb_ctl *ctl, void *buffer, u64 route, u32 port, | ||
| 699 | enum tb_cfg_space space, u32 offset, u32 length) | ||
| 700 | { | ||
| 701 | struct tb_cfg_result res = tb_cfg_write_raw(ctl, buffer, route, port, | ||
| 702 | space, offset, length, TB_CFG_DEFAULT_TIMEOUT); | ||
| 703 | if (res.err == 1) { | ||
| 704 | tb_cfg_print_error(ctl, &res); | ||
| 705 | return -EIO; | ||
| 706 | } | ||
| 707 | WARN(res.err, "tb_cfg_write: %d\n", res.err); | ||
| 708 | return res.err; | ||
| 709 | } | ||
| 710 | |||
| 711 | /** | ||
| 712 | * tb_cfg_get_upstream_port() - get upstream port number of switch at route | ||
| 713 | * | ||
| 714 | * Reads the first dword from the switches TB_CFG_SWITCH config area and | ||
| 715 | * returns the port number from which the reply originated. | ||
| 716 | * | ||
| 717 | * Return: Returns the upstream port number on success or an error code on | ||
| 718 | * failure. | ||
| 719 | */ | ||
| 720 | int tb_cfg_get_upstream_port(struct tb_ctl *ctl, u64 route) | ||
| 721 | { | ||
| 722 | u32 dummy; | ||
| 723 | struct tb_cfg_result res = tb_cfg_read_raw(ctl, &dummy, route, 0, | ||
| 724 | TB_CFG_SWITCH, 0, 1, | ||
| 725 | TB_CFG_DEFAULT_TIMEOUT); | ||
| 726 | if (res.err == 1) | ||
| 727 | return -EIO; | ||
| 728 | if (res.err) | ||
| 729 | return res.err; | ||
| 730 | return res.response_port; | ||
| 731 | } | ||
diff --git a/drivers/thunderbolt/ctl.h b/drivers/thunderbolt/ctl.h new file mode 100644 index 000000000000..ba87d6e731dd --- /dev/null +++ b/drivers/thunderbolt/ctl.h | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | /* | ||
| 2 | * Thunderbolt Cactus Ridge driver - control channel and configuration commands | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> | ||
| 5 | */ | ||
| 6 | |||
| 7 | #ifndef _TB_CFG | ||
| 8 | #define _TB_CFG | ||
| 9 | |||
| 10 | #include "nhi.h" | ||
| 11 | |||
| 12 | /* control channel */ | ||
| 13 | struct tb_ctl; | ||
| 14 | |||
| 15 | typedef void (*hotplug_cb)(void *data, u64 route, u8 port, bool unplug); | ||
| 16 | |||
| 17 | struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, hotplug_cb cb, void *cb_data); | ||
| 18 | void tb_ctl_start(struct tb_ctl *ctl); | ||
| 19 | void tb_ctl_stop(struct tb_ctl *ctl); | ||
| 20 | void tb_ctl_free(struct tb_ctl *ctl); | ||
| 21 | |||
| 22 | /* configuration commands */ | ||
| 23 | |||
| 24 | #define TB_CFG_DEFAULT_TIMEOUT 5000 /* msec */ | ||
| 25 | |||
| 26 | enum tb_cfg_space { | ||
| 27 | TB_CFG_HOPS = 0, | ||
| 28 | TB_CFG_PORT = 1, | ||
| 29 | TB_CFG_SWITCH = 2, | ||
| 30 | TB_CFG_COUNTERS = 3, | ||
| 31 | }; | ||
| 32 | |||
| 33 | enum tb_cfg_error { | ||
| 34 | TB_CFG_ERROR_PORT_NOT_CONNECTED = 0, | ||
| 35 | TB_CFG_ERROR_INVALID_CONFIG_SPACE = 2, | ||
| 36 | TB_CFG_ERROR_NO_SUCH_PORT = 4, | ||
| 37 | TB_CFG_ERROR_ACK_PLUG_EVENT = 7, /* send as reply to TB_CFG_PKG_EVENT */ | ||
| 38 | TB_CFG_ERROR_LOOP = 8, | ||
| 39 | }; | ||
| 40 | |||
| 41 | struct tb_cfg_result { | ||
| 42 | u64 response_route; | ||
| 43 | u32 response_port; /* | ||
| 44 | * If err = 1 then this is the port that send the | ||
| 45 | * error. | ||
| 46 | * If err = 0 and if this was a cfg_read/write then | ||
| 47 | * this is the the upstream port of the responding | ||
| 48 | * switch. | ||
| 49 | * Otherwise the field is set to zero. | ||
| 50 | */ | ||
| 51 | int err; /* negative errors, 0 for success, 1 for tb errors */ | ||
| 52 | enum tb_cfg_error tb_error; /* valid if err == 1 */ | ||
| 53 | }; | ||
| 54 | |||
| 55 | |||
| 56 | int tb_cfg_error(struct tb_ctl *ctl, u64 route, u32 port, | ||
| 57 | enum tb_cfg_error error); | ||
| 58 | struct tb_cfg_result tb_cfg_reset(struct tb_ctl *ctl, u64 route, | ||
| 59 | int timeout_msec); | ||
| 60 | struct tb_cfg_result tb_cfg_read_raw(struct tb_ctl *ctl, void *buffer, | ||
| 61 | u64 route, u32 port, | ||
| 62 | enum tb_cfg_space space, u32 offset, | ||
| 63 | u32 length, int timeout_msec); | ||
| 64 | struct tb_cfg_result tb_cfg_write_raw(struct tb_ctl *ctl, void *buffer, | ||
| 65 | u64 route, u32 port, | ||
| 66 | enum tb_cfg_space space, u32 offset, | ||
| 67 | u32 length, int timeout_msec); | ||
| 68 | int tb_cfg_read(struct tb_ctl *ctl, void *buffer, u64 route, u32 port, | ||
| 69 | enum tb_cfg_space space, u32 offset, u32 length); | ||
| 70 | int tb_cfg_write(struct tb_ctl *ctl, void *buffer, u64 route, u32 port, | ||
| 71 | enum tb_cfg_space space, u32 offset, u32 length); | ||
| 72 | int tb_cfg_get_upstream_port(struct tb_ctl *ctl, u64 route); | ||
| 73 | |||
| 74 | |||
| 75 | #endif | ||
diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c new file mode 100644 index 000000000000..0dde34e3a7c5 --- /dev/null +++ b/drivers/thunderbolt/eeprom.c | |||
| @@ -0,0 +1,449 @@ | |||
| 1 | /* | ||
| 2 | * Thunderbolt Cactus Ridge driver - eeprom access | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include <linux/crc32.h> | ||
| 8 | #include <linux/slab.h> | ||
| 9 | #include "tb.h" | ||
| 10 | |||
| 11 | /** | ||
| 12 | * tb_eeprom_ctl_write() - write control word | ||
| 13 | */ | ||
| 14 | static int tb_eeprom_ctl_write(struct tb_switch *sw, struct tb_eeprom_ctl *ctl) | ||
| 15 | { | ||
| 16 | return tb_sw_write(sw, ctl, TB_CFG_SWITCH, sw->cap_plug_events + 4, 1); | ||
| 17 | } | ||
| 18 | |||
| 19 | /** | ||
| 20 | * tb_eeprom_ctl_write() - read control word | ||
| 21 | */ | ||
| 22 | static int tb_eeprom_ctl_read(struct tb_switch *sw, struct tb_eeprom_ctl *ctl) | ||
| 23 | { | ||
| 24 | return tb_sw_read(sw, ctl, TB_CFG_SWITCH, sw->cap_plug_events + 4, 1); | ||
| 25 | } | ||
| 26 | |||
| 27 | enum tb_eeprom_transfer { | ||
| 28 | TB_EEPROM_IN, | ||
| 29 | TB_EEPROM_OUT, | ||
| 30 | }; | ||
| 31 | |||
| 32 | /** | ||
| 33 | * tb_eeprom_active - enable rom access | ||
| 34 | * | ||
| 35 | * WARNING: Always disable access after usage. Otherwise the controller will | ||
| 36 | * fail to reprobe. | ||
| 37 | */ | ||
| 38 | static int tb_eeprom_active(struct tb_switch *sw, bool enable) | ||
| 39 | { | ||
| 40 | struct tb_eeprom_ctl ctl; | ||
| 41 | int res = tb_eeprom_ctl_read(sw, &ctl); | ||
| 42 | if (res) | ||
| 43 | return res; | ||
| 44 | if (enable) { | ||
| 45 | ctl.access_high = 1; | ||
| 46 | res = tb_eeprom_ctl_write(sw, &ctl); | ||
| 47 | if (res) | ||
| 48 | return res; | ||
| 49 | ctl.access_low = 0; | ||
| 50 | return tb_eeprom_ctl_write(sw, &ctl); | ||
| 51 | } else { | ||
| 52 | ctl.access_low = 1; | ||
| 53 | res = tb_eeprom_ctl_write(sw, &ctl); | ||
| 54 | if (res) | ||
| 55 | return res; | ||
| 56 | ctl.access_high = 0; | ||
| 57 | return tb_eeprom_ctl_write(sw, &ctl); | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | /** | ||
| 62 | * tb_eeprom_transfer - transfer one bit | ||
| 63 | * | ||
| 64 | * If TB_EEPROM_IN is passed, then the bit can be retrieved from ctl->data_in. | ||
| 65 | * If TB_EEPROM_OUT is passed, then ctl->data_out will be written. | ||
| 66 | */ | ||
| 67 | static int tb_eeprom_transfer(struct tb_switch *sw, struct tb_eeprom_ctl *ctl, | ||
| 68 | enum tb_eeprom_transfer direction) | ||
| 69 | { | ||
| 70 | int res; | ||
| 71 | if (direction == TB_EEPROM_OUT) { | ||
| 72 | res = tb_eeprom_ctl_write(sw, ctl); | ||
| 73 | if (res) | ||
| 74 | return res; | ||
| 75 | } | ||
| 76 | ctl->clock = 1; | ||
| 77 | res = tb_eeprom_ctl_write(sw, ctl); | ||
| 78 | if (res) | ||
| 79 | return res; | ||
| 80 | if (direction == TB_EEPROM_IN) { | ||
| 81 | res = tb_eeprom_ctl_read(sw, ctl); | ||
| 82 | if (res) | ||
| 83 | return res; | ||
| 84 | } | ||
| 85 | ctl->clock = 0; | ||
| 86 | return tb_eeprom_ctl_write(sw, ctl); | ||
| 87 | } | ||
| 88 | |||
| 89 | /** | ||
| 90 | * tb_eeprom_out - write one byte to the bus | ||
| 91 | */ | ||
| 92 | static int tb_eeprom_out(struct tb_switch *sw, u8 val) | ||
| 93 | { | ||
| 94 | struct tb_eeprom_ctl ctl; | ||
| 95 | int i; | ||
| 96 | int res = tb_eeprom_ctl_read(sw, &ctl); | ||
| 97 | if (res) | ||
| 98 | return res; | ||
| 99 | for (i = 0; i < 8; i++) { | ||
| 100 | ctl.data_out = val & 0x80; | ||
| 101 | res = tb_eeprom_transfer(sw, &ctl, TB_EEPROM_OUT); | ||
| 102 | if (res) | ||
| 103 | return res; | ||
| 104 | val <<= 1; | ||
| 105 | } | ||
| 106 | return 0; | ||
| 107 | } | ||
| 108 | |||
| 109 | /** | ||
| 110 | * tb_eeprom_in - read one byte from the bus | ||
| 111 | */ | ||
| 112 | static int tb_eeprom_in(struct tb_switch *sw, u8 *val) | ||
| 113 | { | ||
| 114 | struct tb_eeprom_ctl ctl; | ||
| 115 | int i; | ||
| 116 | int res = tb_eeprom_ctl_read(sw, &ctl); | ||
| 117 | if (res) | ||
| 118 | return res; | ||
| 119 | *val = 0; | ||
| 120 | for (i = 0; i < 8; i++) { | ||
| 121 | *val <<= 1; | ||
| 122 | res = tb_eeprom_transfer(sw, &ctl, TB_EEPROM_IN); | ||
| 123 | if (res) | ||
| 124 | return res; | ||
| 125 | *val |= ctl.data_in; | ||
| 126 | } | ||
| 127 | return 0; | ||
| 128 | } | ||
| 129 | |||
| 130 | /** | ||
| 131 | * tb_eeprom_read_n - read count bytes from offset into val | ||
| 132 | */ | ||
| 133 | static int tb_eeprom_read_n(struct tb_switch *sw, u16 offset, u8 *val, | ||
| 134 | size_t count) | ||
| 135 | { | ||
| 136 | int i, res; | ||
| 137 | res = tb_eeprom_active(sw, true); | ||
| 138 | if (res) | ||
| 139 | return res; | ||
| 140 | res = tb_eeprom_out(sw, 3); | ||
| 141 | if (res) | ||
| 142 | return res; | ||
| 143 | res = tb_eeprom_out(sw, offset >> 8); | ||
| 144 | if (res) | ||
| 145 | return res; | ||
| 146 | res = tb_eeprom_out(sw, offset); | ||
| 147 | if (res) | ||
| 148 | return res; | ||
| 149 | for (i = 0; i < count; i++) { | ||
| 150 | res = tb_eeprom_in(sw, val + i); | ||
| 151 | if (res) | ||
| 152 | return res; | ||
| 153 | } | ||
| 154 | return tb_eeprom_active(sw, false); | ||
| 155 | } | ||
| 156 | |||
| 157 | static u8 tb_crc8(u8 *data, int len) | ||
| 158 | { | ||
| 159 | int i, j; | ||
| 160 | u8 val = 0xff; | ||
| 161 | for (i = 0; i < len; i++) { | ||
| 162 | val ^= data[i]; | ||
| 163 | for (j = 0; j < 8; j++) | ||
| 164 | val = (val << 1) ^ ((val & 0x80) ? 7 : 0); | ||
| 165 | } | ||
| 166 | return val; | ||
| 167 | } | ||
| 168 | |||
| 169 | static u32 tb_crc32(void *data, size_t len) | ||
| 170 | { | ||
| 171 | return ~__crc32c_le(~0, data, len); | ||
| 172 | } | ||
| 173 | |||
| 174 | #define TB_DROM_DATA_START 13 | ||
| 175 | struct tb_drom_header { | ||
| 176 | /* BYTE 0 */ | ||
| 177 | u8 uid_crc8; /* checksum for uid */ | ||
| 178 | /* BYTES 1-8 */ | ||
| 179 | u64 uid; | ||
| 180 | /* BYTES 9-12 */ | ||
| 181 | u32 data_crc32; /* checksum for data_len bytes starting at byte 13 */ | ||
| 182 | /* BYTE 13 */ | ||
| 183 | u8 device_rom_revision; /* should be <= 1 */ | ||
| 184 | u16 data_len:10; | ||
| 185 | u8 __unknown1:6; | ||
| 186 | /* BYTES 16-21 */ | ||
| 187 | u16 vendor_id; | ||
| 188 | u16 model_id; | ||
| 189 | u8 model_rev; | ||
| 190 | u8 eeprom_rev; | ||
| 191 | } __packed; | ||
| 192 | |||
| 193 | enum tb_drom_entry_type { | ||
| 194 | /* force unsigned to prevent "one-bit signed bitfield" warning */ | ||
| 195 | TB_DROM_ENTRY_GENERIC = 0U, | ||
| 196 | TB_DROM_ENTRY_PORT, | ||
| 197 | }; | ||
| 198 | |||
| 199 | struct tb_drom_entry_header { | ||
| 200 | u8 len; | ||
| 201 | u8 index:6; | ||
| 202 | bool port_disabled:1; /* only valid if type is TB_DROM_ENTRY_PORT */ | ||
| 203 | enum tb_drom_entry_type type:1; | ||
| 204 | } __packed; | ||
| 205 | |||
| 206 | struct tb_drom_entry_port { | ||
| 207 | /* BYTES 0-1 */ | ||
| 208 | struct tb_drom_entry_header header; | ||
| 209 | /* BYTE 2 */ | ||
| 210 | u8 dual_link_port_rid:4; | ||
| 211 | u8 link_nr:1; | ||
| 212 | u8 unknown1:2; | ||
| 213 | bool has_dual_link_port:1; | ||
| 214 | |||
| 215 | /* BYTE 3 */ | ||
| 216 | u8 dual_link_port_nr:6; | ||
| 217 | u8 unknown2:2; | ||
| 218 | |||
| 219 | /* BYTES 4 - 5 TODO decode */ | ||
| 220 | u8 micro2:4; | ||
| 221 | u8 micro1:4; | ||
| 222 | u8 micro3; | ||
| 223 | |||
| 224 | /* BYTES 5-6, TODO: verify (find hardware that has these set) */ | ||
| 225 | u8 peer_port_rid:4; | ||
| 226 | u8 unknown3:3; | ||
| 227 | bool has_peer_port:1; | ||
| 228 | u8 peer_port_nr:6; | ||
| 229 | u8 unknown4:2; | ||
| 230 | } __packed; | ||
| 231 | |||
| 232 | |||
| 233 | /** | ||
| 234 | * tb_eeprom_get_drom_offset - get drom offset within eeprom | ||
| 235 | */ | ||
| 236 | static int tb_eeprom_get_drom_offset(struct tb_switch *sw, u16 *offset) | ||
| 237 | { | ||
| 238 | struct tb_cap_plug_events cap; | ||
| 239 | int res; | ||
| 240 | if (!sw->cap_plug_events) { | ||
| 241 | tb_sw_warn(sw, "no TB_CAP_PLUG_EVENTS, cannot read eeprom\n"); | ||
| 242 | return -ENOSYS; | ||
| 243 | } | ||
| 244 | res = tb_sw_read(sw, &cap, TB_CFG_SWITCH, sw->cap_plug_events, | ||
| 245 | sizeof(cap) / 4); | ||
| 246 | if (res) | ||
| 247 | return res; | ||
| 248 | |||
| 249 | if (!cap.eeprom_ctl.present || cap.eeprom_ctl.not_present) { | ||
| 250 | tb_sw_warn(sw, "no NVM\n"); | ||
| 251 | return -ENOSYS; | ||
| 252 | } | ||
| 253 | |||
| 254 | if (cap.drom_offset > 0xffff) { | ||
| 255 | tb_sw_warn(sw, "drom offset is larger than 0xffff: %#x\n", | ||
| 256 | cap.drom_offset); | ||
| 257 | return -ENXIO; | ||
| 258 | } | ||
| 259 | *offset = cap.drom_offset; | ||
| 260 | return 0; | ||
| 261 | } | ||
| 262 | |||
| 263 | /** | ||
| 264 | * tb_drom_read_uid_only - read uid directly from drom | ||
| 265 | * | ||
| 266 | * Does not use the cached copy in sw->drom. Used during resume to check switch | ||
| 267 | * identity. | ||
| 268 | */ | ||
| 269 | int tb_drom_read_uid_only(struct tb_switch *sw, u64 *uid) | ||
| 270 | { | ||
| 271 | u8 data[9]; | ||
| 272 | u16 drom_offset; | ||
| 273 | u8 crc; | ||
| 274 | int res = tb_eeprom_get_drom_offset(sw, &drom_offset); | ||
| 275 | if (res) | ||
| 276 | return res; | ||
| 277 | |||
| 278 | /* read uid */ | ||
| 279 | res = tb_eeprom_read_n(sw, drom_offset, data, 9); | ||
| 280 | if (res) | ||
| 281 | return res; | ||
| 282 | |||
| 283 | crc = tb_crc8(data + 1, 8); | ||
| 284 | if (crc != data[0]) { | ||
| 285 | tb_sw_warn(sw, "uid crc8 missmatch (expected: %#x, got: %#x)\n", | ||
| 286 | data[0], crc); | ||
| 287 | return -EIO; | ||
| 288 | } | ||
| 289 | |||
| 290 | *uid = *(u64 *)(data+1); | ||
| 291 | return 0; | ||
| 292 | } | ||
| 293 | |||
| 294 | static void tb_drom_parse_port_entry(struct tb_port *port, | ||
| 295 | struct tb_drom_entry_port *entry) | ||
| 296 | { | ||
| 297 | port->link_nr = entry->link_nr; | ||
| 298 | if (entry->has_dual_link_port) | ||
| 299 | port->dual_link_port = | ||
| 300 | &port->sw->ports[entry->dual_link_port_nr]; | ||
| 301 | } | ||
| 302 | |||
| 303 | static int tb_drom_parse_entry(struct tb_switch *sw, | ||
| 304 | struct tb_drom_entry_header *header) | ||
| 305 | { | ||
| 306 | struct tb_port *port; | ||
| 307 | int res; | ||
| 308 | enum tb_port_type type; | ||
| 309 | |||
| 310 | if (header->type != TB_DROM_ENTRY_PORT) | ||
| 311 | return 0; | ||
| 312 | |||
| 313 | port = &sw->ports[header->index]; | ||
| 314 | port->disabled = header->port_disabled; | ||
| 315 | if (port->disabled) | ||
| 316 | return 0; | ||
| 317 | |||
| 318 | res = tb_port_read(port, &type, TB_CFG_PORT, 2, 1); | ||
| 319 | if (res) | ||
| 320 | return res; | ||
| 321 | type &= 0xffffff; | ||
| 322 | |||
| 323 | if (type == TB_TYPE_PORT) { | ||
| 324 | struct tb_drom_entry_port *entry = (void *) header; | ||
| 325 | if (header->len != sizeof(*entry)) { | ||
| 326 | tb_sw_warn(sw, | ||
| 327 | "port entry has size %#x (expected %#zx)\n", | ||
| 328 | header->len, sizeof(struct tb_drom_entry_port)); | ||
| 329 | return -EIO; | ||
| 330 | } | ||
| 331 | tb_drom_parse_port_entry(port, entry); | ||
| 332 | } | ||
| 333 | return 0; | ||
| 334 | } | ||
| 335 | |||
| 336 | /** | ||
| 337 | * tb_drom_parse_entries - parse the linked list of drom entries | ||
| 338 | * | ||
| 339 | * Drom must have been copied to sw->drom. | ||
| 340 | */ | ||
| 341 | static int tb_drom_parse_entries(struct tb_switch *sw) | ||
| 342 | { | ||
| 343 | struct tb_drom_header *header = (void *) sw->drom; | ||
| 344 | u16 pos = sizeof(*header); | ||
| 345 | u16 drom_size = header->data_len + TB_DROM_DATA_START; | ||
| 346 | |||
| 347 | while (pos < drom_size) { | ||
| 348 | struct tb_drom_entry_header *entry = (void *) (sw->drom + pos); | ||
| 349 | if (pos + 1 == drom_size || pos + entry->len > drom_size | ||
| 350 | || !entry->len) { | ||
| 351 | tb_sw_warn(sw, "drom buffer overrun, aborting\n"); | ||
| 352 | return -EIO; | ||
| 353 | } | ||
| 354 | |||
| 355 | tb_drom_parse_entry(sw, entry); | ||
| 356 | |||
| 357 | pos += entry->len; | ||
| 358 | } | ||
| 359 | return 0; | ||
| 360 | } | ||
| 361 | |||
| 362 | /** | ||
| 363 | * tb_drom_read - copy drom to sw->drom and parse it | ||
| 364 | */ | ||
| 365 | int tb_drom_read(struct tb_switch *sw) | ||
| 366 | { | ||
| 367 | u16 drom_offset; | ||
| 368 | u16 size; | ||
| 369 | u32 crc; | ||
| 370 | struct tb_drom_header *header; | ||
| 371 | int res; | ||
| 372 | if (sw->drom) | ||
| 373 | return 0; | ||
| 374 | |||
| 375 | if (tb_route(sw) == 0) { | ||
| 376 | /* | ||
| 377 | * The root switch contains only a dummy drom (header only, | ||
| 378 | * no entries). Hardcode the configuration here. | ||
| 379 | */ | ||
| 380 | tb_drom_read_uid_only(sw, &sw->uid); | ||
| 381 | |||
| 382 | sw->ports[1].link_nr = 0; | ||
| 383 | sw->ports[2].link_nr = 1; | ||
| 384 | sw->ports[1].dual_link_port = &sw->ports[2]; | ||
| 385 | sw->ports[2].dual_link_port = &sw->ports[1]; | ||
| 386 | |||
| 387 | sw->ports[3].link_nr = 0; | ||
| 388 | sw->ports[4].link_nr = 1; | ||
| 389 | sw->ports[3].dual_link_port = &sw->ports[4]; | ||
| 390 | sw->ports[4].dual_link_port = &sw->ports[3]; | ||
| 391 | return 0; | ||
| 392 | } | ||
| 393 | |||
| 394 | res = tb_eeprom_get_drom_offset(sw, &drom_offset); | ||
| 395 | if (res) | ||
| 396 | return res; | ||
| 397 | |||
| 398 | res = tb_eeprom_read_n(sw, drom_offset + 14, (u8 *) &size, 2); | ||
| 399 | if (res) | ||
| 400 | return res; | ||
| 401 | size &= 0x3ff; | ||
| 402 | size += TB_DROM_DATA_START; | ||
| 403 | tb_sw_info(sw, "reading drom (length: %#x)\n", size); | ||
| 404 | if (size < sizeof(*header)) { | ||
| 405 | tb_sw_warn(sw, "drom too small, aborting\n"); | ||
| 406 | return -EIO; | ||
| 407 | } | ||
| 408 | |||
| 409 | sw->drom = kzalloc(size, GFP_KERNEL); | ||
| 410 | if (!sw->drom) | ||
| 411 | return -ENOMEM; | ||
| 412 | res = tb_eeprom_read_n(sw, drom_offset, sw->drom, size); | ||
| 413 | if (res) | ||
| 414 | goto err; | ||
| 415 | |||
| 416 | header = (void *) sw->drom; | ||
| 417 | |||
| 418 | if (header->data_len + TB_DROM_DATA_START != size) { | ||
| 419 | tb_sw_warn(sw, "drom size mismatch, aborting\n"); | ||
| 420 | goto err; | ||
| 421 | } | ||
| 422 | |||
| 423 | crc = tb_crc8((u8 *) &header->uid, 8); | ||
| 424 | if (crc != header->uid_crc8) { | ||
| 425 | tb_sw_warn(sw, | ||
| 426 | "drom uid crc8 mismatch (expected: %#x, got: %#x), aborting\n", | ||
| 427 | header->uid_crc8, crc); | ||
| 428 | goto err; | ||
| 429 | } | ||
| 430 | sw->uid = header->uid; | ||
| 431 | |||
| 432 | crc = tb_crc32(sw->drom + TB_DROM_DATA_START, header->data_len); | ||
| 433 | if (crc != header->data_crc32) { | ||
| 434 | tb_sw_warn(sw, | ||
| 435 | "drom data crc32 mismatch (expected: %#x, got: %#x), aborting\n", | ||
| 436 | header->data_crc32, crc); | ||
| 437 | goto err; | ||
| 438 | } | ||
| 439 | |||
| 440 | if (header->device_rom_revision > 1) | ||
| 441 | tb_sw_warn(sw, "drom device_rom_revision %#x unknown\n", | ||
| 442 | header->device_rom_revision); | ||
| 443 | |||
| 444 | return tb_drom_parse_entries(sw); | ||
| 445 | err: | ||
| 446 | kfree(sw->drom); | ||
| 447 | return -EIO; | ||
| 448 | |||
| 449 | } | ||
diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c new file mode 100644 index 000000000000..c68fe1222c16 --- /dev/null +++ b/drivers/thunderbolt/nhi.c | |||
| @@ -0,0 +1,675 @@ | |||
| 1 | /* | ||
| 2 | * Thunderbolt Cactus Ridge driver - NHI driver | ||
| 3 | * | ||
| 4 | * The NHI (native host interface) is the pci device that allows us to send and | ||
| 5 | * receive frames from the thunderbolt bus. | ||
| 6 | * | ||
| 7 | * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> | ||
| 8 | */ | ||
| 9 | |||
| 10 | #include <linux/pm_runtime.h> | ||
| 11 | #include <linux/slab.h> | ||
| 12 | #include <linux/errno.h> | ||
| 13 | #include <linux/pci.h> | ||
| 14 | #include <linux/interrupt.h> | ||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/dmi.h> | ||
| 17 | |||
| 18 | #include "nhi.h" | ||
| 19 | #include "nhi_regs.h" | ||
| 20 | #include "tb.h" | ||
| 21 | |||
| 22 | #define RING_TYPE(ring) ((ring)->is_tx ? "TX ring" : "RX ring") | ||
| 23 | |||
| 24 | |||
| 25 | static int ring_interrupt_index(struct tb_ring *ring) | ||
| 26 | { | ||
| 27 | int bit = ring->hop; | ||
| 28 | if (!ring->is_tx) | ||
| 29 | bit += ring->nhi->hop_count; | ||
| 30 | return bit; | ||
| 31 | } | ||
| 32 | |||
| 33 | /** | ||
| 34 | * ring_interrupt_active() - activate/deactivate interrupts for a single ring | ||
| 35 | * | ||
| 36 | * ring->nhi->lock must be held. | ||
| 37 | */ | ||
| 38 | static void ring_interrupt_active(struct tb_ring *ring, bool active) | ||
| 39 | { | ||
| 40 | int reg = REG_RING_INTERRUPT_BASE + ring_interrupt_index(ring) / 32; | ||
| 41 | int bit = ring_interrupt_index(ring) & 31; | ||
| 42 | int mask = 1 << bit; | ||
| 43 | u32 old, new; | ||
| 44 | old = ioread32(ring->nhi->iobase + reg); | ||
| 45 | if (active) | ||
| 46 | new = old | mask; | ||
| 47 | else | ||
| 48 | new = old & ~mask; | ||
| 49 | |||
| 50 | dev_info(&ring->nhi->pdev->dev, | ||
| 51 | "%s interrupt at register %#x bit %d (%#x -> %#x)\n", | ||
| 52 | active ? "enabling" : "disabling", reg, bit, old, new); | ||
| 53 | |||
| 54 | if (new == old) | ||
| 55 | dev_WARN(&ring->nhi->pdev->dev, | ||
| 56 | "interrupt for %s %d is already %s\n", | ||
| 57 | RING_TYPE(ring), ring->hop, | ||
| 58 | active ? "enabled" : "disabled"); | ||
| 59 | iowrite32(new, ring->nhi->iobase + reg); | ||
| 60 | } | ||
| 61 | |||
| 62 | /** | ||
| 63 | * nhi_disable_interrupts() - disable interrupts for all rings | ||
| 64 | * | ||
| 65 | * Use only during init and shutdown. | ||
| 66 | */ | ||
| 67 | static void nhi_disable_interrupts(struct tb_nhi *nhi) | ||
| 68 | { | ||
| 69 | int i = 0; | ||
| 70 | /* disable interrupts */ | ||
| 71 | for (i = 0; i < RING_INTERRUPT_REG_COUNT(nhi); i++) | ||
| 72 | iowrite32(0, nhi->iobase + REG_RING_INTERRUPT_BASE + 4 * i); | ||
| 73 | |||
| 74 | /* clear interrupt status bits */ | ||
| 75 | for (i = 0; i < RING_NOTIFY_REG_COUNT(nhi); i++) | ||
| 76 | ioread32(nhi->iobase + REG_RING_NOTIFY_BASE + 4 * i); | ||
| 77 | } | ||
| 78 | |||
| 79 | /* ring helper methods */ | ||
| 80 | |||
| 81 | static void __iomem *ring_desc_base(struct tb_ring *ring) | ||
| 82 | { | ||
| 83 | void __iomem *io = ring->nhi->iobase; | ||
| 84 | io += ring->is_tx ? REG_TX_RING_BASE : REG_RX_RING_BASE; | ||
| 85 | io += ring->hop * 16; | ||
| 86 | return io; | ||
| 87 | } | ||
| 88 | |||
| 89 | static void __iomem *ring_options_base(struct tb_ring *ring) | ||
| 90 | { | ||
| 91 | void __iomem *io = ring->nhi->iobase; | ||
| 92 | io += ring->is_tx ? REG_TX_OPTIONS_BASE : REG_RX_OPTIONS_BASE; | ||
| 93 | io += ring->hop * 32; | ||
| 94 | return io; | ||
| 95 | } | ||
| 96 | |||
| 97 | static void ring_iowrite16desc(struct tb_ring *ring, u32 value, u32 offset) | ||
| 98 | { | ||
| 99 | iowrite16(value, ring_desc_base(ring) + offset); | ||
| 100 | } | ||
| 101 | |||
| 102 | static void ring_iowrite32desc(struct tb_ring *ring, u32 value, u32 offset) | ||
| 103 | { | ||
| 104 | iowrite32(value, ring_desc_base(ring) + offset); | ||
| 105 | } | ||
| 106 | |||
| 107 | static void ring_iowrite64desc(struct tb_ring *ring, u64 value, u32 offset) | ||
| 108 | { | ||
| 109 | iowrite32(value, ring_desc_base(ring) + offset); | ||
| 110 | iowrite32(value >> 32, ring_desc_base(ring) + offset + 4); | ||
| 111 | } | ||
| 112 | |||
| 113 | static void ring_iowrite32options(struct tb_ring *ring, u32 value, u32 offset) | ||
| 114 | { | ||
| 115 | iowrite32(value, ring_options_base(ring) + offset); | ||
| 116 | } | ||
| 117 | |||
| 118 | static bool ring_full(struct tb_ring *ring) | ||
| 119 | { | ||
| 120 | return ((ring->head + 1) % ring->size) == ring->tail; | ||
| 121 | } | ||
| 122 | |||
| 123 | static bool ring_empty(struct tb_ring *ring) | ||
| 124 | { | ||
| 125 | return ring->head == ring->tail; | ||
| 126 | } | ||
| 127 | |||
| 128 | /** | ||
| 129 | * ring_write_descriptors() - post frames from ring->queue to the controller | ||
| 130 | * | ||
| 131 | * ring->lock is held. | ||
| 132 | */ | ||
| 133 | static void ring_write_descriptors(struct tb_ring *ring) | ||
| 134 | { | ||
| 135 | struct ring_frame *frame, *n; | ||
| 136 | struct ring_desc *descriptor; | ||
| 137 | list_for_each_entry_safe(frame, n, &ring->queue, list) { | ||
| 138 | if (ring_full(ring)) | ||
| 139 | break; | ||
| 140 | list_move_tail(&frame->list, &ring->in_flight); | ||
| 141 | descriptor = &ring->descriptors[ring->head]; | ||
| 142 | descriptor->phys = frame->buffer_phy; | ||
| 143 | descriptor->time = 0; | ||
| 144 | descriptor->flags = RING_DESC_POSTED | RING_DESC_INTERRUPT; | ||
| 145 | if (ring->is_tx) { | ||
| 146 | descriptor->length = frame->size; | ||
| 147 | descriptor->eof = frame->eof; | ||
| 148 | descriptor->sof = frame->sof; | ||
| 149 | } | ||
| 150 | ring->head = (ring->head + 1) % ring->size; | ||
| 151 | ring_iowrite16desc(ring, ring->head, ring->is_tx ? 10 : 8); | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | /** | ||
| 156 | * ring_work() - progress completed frames | ||
| 157 | * | ||
| 158 | * If the ring is shutting down then all frames are marked as canceled and | ||
| 159 | * their callbacks are invoked. | ||
| 160 | * | ||
| 161 | * Otherwise we collect all completed frame from the ring buffer, write new | ||
| 162 | * frame to the ring buffer and invoke the callbacks for the completed frames. | ||
| 163 | */ | ||
| 164 | static void ring_work(struct work_struct *work) | ||
| 165 | { | ||
| 166 | struct tb_ring *ring = container_of(work, typeof(*ring), work); | ||
| 167 | struct ring_frame *frame; | ||
| 168 | bool canceled = false; | ||
| 169 | LIST_HEAD(done); | ||
| 170 | mutex_lock(&ring->lock); | ||
| 171 | |||
| 172 | if (!ring->running) { | ||
| 173 | /* Move all frames to done and mark them as canceled. */ | ||
| 174 | list_splice_tail_init(&ring->in_flight, &done); | ||
| 175 | list_splice_tail_init(&ring->queue, &done); | ||
| 176 | canceled = true; | ||
| 177 | goto invoke_callback; | ||
| 178 | } | ||
| 179 | |||
| 180 | while (!ring_empty(ring)) { | ||
| 181 | if (!(ring->descriptors[ring->tail].flags | ||
| 182 | & RING_DESC_COMPLETED)) | ||
| 183 | break; | ||
| 184 | frame = list_first_entry(&ring->in_flight, typeof(*frame), | ||
| 185 | list); | ||
| 186 | list_move_tail(&frame->list, &done); | ||
| 187 | if (!ring->is_tx) { | ||
| 188 | frame->size = ring->descriptors[ring->tail].length; | ||
| 189 | frame->eof = ring->descriptors[ring->tail].eof; | ||
| 190 | frame->sof = ring->descriptors[ring->tail].sof; | ||
| 191 | frame->flags = ring->descriptors[ring->tail].flags; | ||
| 192 | if (frame->sof != 0) | ||
| 193 | dev_WARN(&ring->nhi->pdev->dev, | ||
| 194 | "%s %d got unexpected SOF: %#x\n", | ||
| 195 | RING_TYPE(ring), ring->hop, | ||
| 196 | frame->sof); | ||
| 197 | /* | ||
| 198 | * known flags: | ||
| 199 | * raw not enabled, interupt not set: 0x2=0010 | ||
| 200 | * raw enabled: 0xa=1010 | ||
| 201 | * raw not enabled: 0xb=1011 | ||
| 202 | * partial frame (>MAX_FRAME_SIZE): 0xe=1110 | ||
| 203 | */ | ||
| 204 | if (frame->flags != 0xa) | ||
| 205 | dev_WARN(&ring->nhi->pdev->dev, | ||
| 206 | "%s %d got unexpected flags: %#x\n", | ||
| 207 | RING_TYPE(ring), ring->hop, | ||
| 208 | frame->flags); | ||
| 209 | } | ||
| 210 | ring->tail = (ring->tail + 1) % ring->size; | ||
| 211 | } | ||
| 212 | ring_write_descriptors(ring); | ||
| 213 | |||
| 214 | invoke_callback: | ||
| 215 | mutex_unlock(&ring->lock); /* allow callbacks to schedule new work */ | ||
| 216 | while (!list_empty(&done)) { | ||
| 217 | frame = list_first_entry(&done, typeof(*frame), list); | ||
| 218 | /* | ||
| 219 | * The callback may reenqueue or delete frame. | ||
| 220 | * Do not hold on to it. | ||
| 221 | */ | ||
| 222 | list_del_init(&frame->list); | ||
| 223 | frame->callback(ring, frame, canceled); | ||
| 224 | } | ||
| 225 | } | ||
| 226 | |||
| 227 | int __ring_enqueue(struct tb_ring *ring, struct ring_frame *frame) | ||
| 228 | { | ||
| 229 | int ret = 0; | ||
| 230 | mutex_lock(&ring->lock); | ||
| 231 | if (ring->running) { | ||
| 232 | list_add_tail(&frame->list, &ring->queue); | ||
| 233 | ring_write_descriptors(ring); | ||
| 234 | } else { | ||
| 235 | ret = -ESHUTDOWN; | ||
| 236 | } | ||
| 237 | mutex_unlock(&ring->lock); | ||
| 238 | return ret; | ||
| 239 | } | ||
| 240 | |||
| 241 | static struct tb_ring *ring_alloc(struct tb_nhi *nhi, u32 hop, int size, | ||
| 242 | bool transmit) | ||
| 243 | { | ||
| 244 | struct tb_ring *ring = NULL; | ||
| 245 | dev_info(&nhi->pdev->dev, "allocating %s ring %d of size %d\n", | ||
| 246 | transmit ? "TX" : "RX", hop, size); | ||
| 247 | |||
| 248 | mutex_lock(&nhi->lock); | ||
| 249 | if (hop >= nhi->hop_count) { | ||
| 250 | dev_WARN(&nhi->pdev->dev, "invalid hop: %d\n", hop); | ||
| 251 | goto err; | ||
| 252 | } | ||
| 253 | if (transmit && nhi->tx_rings[hop]) { | ||
| 254 | dev_WARN(&nhi->pdev->dev, "TX hop %d already allocated\n", hop); | ||
| 255 | goto err; | ||
| 256 | } else if (!transmit && nhi->rx_rings[hop]) { | ||
| 257 | dev_WARN(&nhi->pdev->dev, "RX hop %d already allocated\n", hop); | ||
| 258 | goto err; | ||
| 259 | } | ||
| 260 | ring = kzalloc(sizeof(*ring), GFP_KERNEL); | ||
| 261 | if (!ring) | ||
| 262 | goto err; | ||
| 263 | |||
| 264 | mutex_init(&ring->lock); | ||
| 265 | INIT_LIST_HEAD(&ring->queue); | ||
| 266 | INIT_LIST_HEAD(&ring->in_flight); | ||
| 267 | INIT_WORK(&ring->work, ring_work); | ||
| 268 | |||
| 269 | ring->nhi = nhi; | ||
| 270 | ring->hop = hop; | ||
| 271 | ring->is_tx = transmit; | ||
| 272 | ring->size = size; | ||
| 273 | ring->head = 0; | ||
| 274 | ring->tail = 0; | ||
| 275 | ring->running = false; | ||
| 276 | ring->descriptors = dma_alloc_coherent(&ring->nhi->pdev->dev, | ||
| 277 | size * sizeof(*ring->descriptors), | ||
| 278 | &ring->descriptors_dma, GFP_KERNEL | __GFP_ZERO); | ||
| 279 | if (!ring->descriptors) | ||
| 280 | goto err; | ||
| 281 | |||
| 282 | if (transmit) | ||
| 283 | nhi->tx_rings[hop] = ring; | ||
| 284 | else | ||
| 285 | nhi->rx_rings[hop] = ring; | ||
| 286 | mutex_unlock(&nhi->lock); | ||
| 287 | return ring; | ||
| 288 | |||
| 289 | err: | ||
| 290 | if (ring) | ||
| 291 | mutex_destroy(&ring->lock); | ||
| 292 | kfree(ring); | ||
| 293 | mutex_unlock(&nhi->lock); | ||
| 294 | return NULL; | ||
| 295 | } | ||
| 296 | |||
| 297 | struct tb_ring *ring_alloc_tx(struct tb_nhi *nhi, int hop, int size) | ||
| 298 | { | ||
| 299 | return ring_alloc(nhi, hop, size, true); | ||
| 300 | } | ||
| 301 | |||
| 302 | struct tb_ring *ring_alloc_rx(struct tb_nhi *nhi, int hop, int size) | ||
| 303 | { | ||
| 304 | return ring_alloc(nhi, hop, size, false); | ||
| 305 | } | ||
| 306 | |||
| 307 | /** | ||
| 308 | * ring_start() - enable a ring | ||
| 309 | * | ||
| 310 | * Must not be invoked in parallel with ring_stop(). | ||
| 311 | */ | ||
| 312 | void ring_start(struct tb_ring *ring) | ||
| 313 | { | ||
| 314 | mutex_lock(&ring->nhi->lock); | ||
| 315 | mutex_lock(&ring->lock); | ||
| 316 | if (ring->running) { | ||
| 317 | dev_WARN(&ring->nhi->pdev->dev, "ring already started\n"); | ||
| 318 | goto err; | ||
| 319 | } | ||
| 320 | dev_info(&ring->nhi->pdev->dev, "starting %s %d\n", | ||
| 321 | RING_TYPE(ring), ring->hop); | ||
| 322 | |||
| 323 | ring_iowrite64desc(ring, ring->descriptors_dma, 0); | ||
| 324 | if (ring->is_tx) { | ||
| 325 | ring_iowrite32desc(ring, ring->size, 12); | ||
| 326 | ring_iowrite32options(ring, 0, 4); /* time releated ? */ | ||
| 327 | ring_iowrite32options(ring, | ||
| 328 | RING_FLAG_ENABLE | RING_FLAG_RAW, 0); | ||
| 329 | } else { | ||
| 330 | ring_iowrite32desc(ring, | ||
| 331 | (TB_FRAME_SIZE << 16) | ring->size, 12); | ||
| 332 | ring_iowrite32options(ring, 0xffffffff, 4); /* SOF EOF mask */ | ||
| 333 | ring_iowrite32options(ring, | ||
| 334 | RING_FLAG_ENABLE | RING_FLAG_RAW, 0); | ||
| 335 | } | ||
| 336 | ring_interrupt_active(ring, true); | ||
| 337 | ring->running = true; | ||
| 338 | err: | ||
| 339 | mutex_unlock(&ring->lock); | ||
| 340 | mutex_unlock(&ring->nhi->lock); | ||
| 341 | } | ||
| 342 | |||
| 343 | |||
| 344 | /** | ||
| 345 | * ring_stop() - shutdown a ring | ||
| 346 | * | ||
| 347 | * Must not be invoked from a callback. | ||
| 348 | * | ||
| 349 | * This method will disable the ring. Further calls to ring_tx/ring_rx will | ||
| 350 | * return -ESHUTDOWN until ring_stop has been called. | ||
| 351 | * | ||
| 352 | * All enqueued frames will be canceled and their callbacks will be executed | ||
| 353 | * with frame->canceled set to true (on the callback thread). This method | ||
| 354 | * returns only after all callback invocations have finished. | ||
| 355 | */ | ||
| 356 | void ring_stop(struct tb_ring *ring) | ||
| 357 | { | ||
| 358 | mutex_lock(&ring->nhi->lock); | ||
| 359 | mutex_lock(&ring->lock); | ||
| 360 | dev_info(&ring->nhi->pdev->dev, "stopping %s %d\n", | ||
| 361 | RING_TYPE(ring), ring->hop); | ||
| 362 | if (!ring->running) { | ||
| 363 | dev_WARN(&ring->nhi->pdev->dev, "%s %d already stopped\n", | ||
| 364 | RING_TYPE(ring), ring->hop); | ||
| 365 | goto err; | ||
| 366 | } | ||
| 367 | ring_interrupt_active(ring, false); | ||
| 368 | |||
| 369 | ring_iowrite32options(ring, 0, 0); | ||
| 370 | ring_iowrite64desc(ring, 0, 0); | ||
| 371 | ring_iowrite16desc(ring, 0, ring->is_tx ? 10 : 8); | ||
| 372 | ring_iowrite32desc(ring, 0, 12); | ||
| 373 | ring->head = 0; | ||
| 374 | ring->tail = 0; | ||
| 375 | ring->running = false; | ||
| 376 | |||
| 377 | err: | ||
| 378 | mutex_unlock(&ring->lock); | ||
| 379 | mutex_unlock(&ring->nhi->lock); | ||
| 380 | |||
| 381 | /* | ||
| 382 | * schedule ring->work to invoke callbacks on all remaining frames. | ||
| 383 | */ | ||
| 384 | schedule_work(&ring->work); | ||
| 385 | flush_work(&ring->work); | ||
| 386 | } | ||
| 387 | |||
| 388 | /* | ||
| 389 | * ring_free() - free ring | ||
| 390 | * | ||
| 391 | * When this method returns all invocations of ring->callback will have | ||
| 392 | * finished. | ||
| 393 | * | ||
| 394 | * Ring must be stopped. | ||
| 395 | * | ||
| 396 | * Must NOT be called from ring_frame->callback! | ||
| 397 | */ | ||
| 398 | void ring_free(struct tb_ring *ring) | ||
| 399 | { | ||
| 400 | mutex_lock(&ring->nhi->lock); | ||
| 401 | /* | ||
| 402 | * Dissociate the ring from the NHI. This also ensures that | ||
| 403 | * nhi_interrupt_work cannot reschedule ring->work. | ||
| 404 | */ | ||
| 405 | if (ring->is_tx) | ||
| 406 | ring->nhi->tx_rings[ring->hop] = NULL; | ||
| 407 | else | ||
| 408 | ring->nhi->rx_rings[ring->hop] = NULL; | ||
| 409 | |||
| 410 | if (ring->running) { | ||
| 411 | dev_WARN(&ring->nhi->pdev->dev, "%s %d still running\n", | ||
| 412 | RING_TYPE(ring), ring->hop); | ||
| 413 | } | ||
| 414 | |||
| 415 | dma_free_coherent(&ring->nhi->pdev->dev, | ||
| 416 | ring->size * sizeof(*ring->descriptors), | ||
| 417 | ring->descriptors, ring->descriptors_dma); | ||
| 418 | |||
| 419 | ring->descriptors = NULL; | ||
| 420 | ring->descriptors_dma = 0; | ||
| 421 | |||
| 422 | |||
| 423 | dev_info(&ring->nhi->pdev->dev, | ||
| 424 | "freeing %s %d\n", | ||
| 425 | RING_TYPE(ring), | ||
| 426 | ring->hop); | ||
| 427 | |||
| 428 | mutex_unlock(&ring->nhi->lock); | ||
| 429 | /** | ||
| 430 | * ring->work can no longer be scheduled (it is scheduled only by | ||
| 431 | * nhi_interrupt_work and ring_stop). Wait for it to finish before | ||
| 432 | * freeing the ring. | ||
| 433 | */ | ||
| 434 | flush_work(&ring->work); | ||
| 435 | mutex_destroy(&ring->lock); | ||
| 436 | kfree(ring); | ||
| 437 | } | ||
| 438 | |||
| 439 | static void nhi_interrupt_work(struct work_struct *work) | ||
| 440 | { | ||
| 441 | struct tb_nhi *nhi = container_of(work, typeof(*nhi), interrupt_work); | ||
| 442 | int value = 0; /* Suppress uninitialized usage warning. */ | ||
| 443 | int bit; | ||
| 444 | int hop = -1; | ||
| 445 | int type = 0; /* current interrupt type 0: TX, 1: RX, 2: RX overflow */ | ||
| 446 | struct tb_ring *ring; | ||
| 447 | |||
| 448 | mutex_lock(&nhi->lock); | ||
| 449 | |||
| 450 | /* | ||
| 451 | * Starting at REG_RING_NOTIFY_BASE there are three status bitfields | ||
| 452 | * (TX, RX, RX overflow). We iterate over the bits and read a new | ||
| 453 | * dwords as required. The registers are cleared on read. | ||
| 454 | */ | ||
| 455 | for (bit = 0; bit < 3 * nhi->hop_count; bit++) { | ||
| 456 | if (bit % 32 == 0) | ||
| 457 | value = ioread32(nhi->iobase | ||
| 458 | + REG_RING_NOTIFY_BASE | ||
| 459 | + 4 * (bit / 32)); | ||
| 460 | if (++hop == nhi->hop_count) { | ||
| 461 | hop = 0; | ||
| 462 | type++; | ||
| 463 | } | ||
| 464 | if ((value & (1 << (bit % 32))) == 0) | ||
| 465 | continue; | ||
| 466 | if (type == 2) { | ||
| 467 | dev_warn(&nhi->pdev->dev, | ||
| 468 | "RX overflow for ring %d\n", | ||
| 469 | hop); | ||
| 470 | continue; | ||
| 471 | } | ||
| 472 | if (type == 0) | ||
| 473 | ring = nhi->tx_rings[hop]; | ||
| 474 | else | ||
| 475 | ring = nhi->rx_rings[hop]; | ||
| 476 | if (ring == NULL) { | ||
| 477 | dev_warn(&nhi->pdev->dev, | ||
| 478 | "got interrupt for inactive %s ring %d\n", | ||
| 479 | type ? "RX" : "TX", | ||
| 480 | hop); | ||
| 481 | continue; | ||
| 482 | } | ||
| 483 | /* we do not check ring->running, this is done in ring->work */ | ||
| 484 | schedule_work(&ring->work); | ||
| 485 | } | ||
| 486 | mutex_unlock(&nhi->lock); | ||
| 487 | } | ||
| 488 | |||
| 489 | static irqreturn_t nhi_msi(int irq, void *data) | ||
| 490 | { | ||
| 491 | struct tb_nhi *nhi = data; | ||
| 492 | schedule_work(&nhi->interrupt_work); | ||
| 493 | return IRQ_HANDLED; | ||
| 494 | } | ||
| 495 | |||
| 496 | static int nhi_suspend_noirq(struct device *dev) | ||
| 497 | { | ||
| 498 | struct pci_dev *pdev = to_pci_dev(dev); | ||
| 499 | struct tb *tb = pci_get_drvdata(pdev); | ||
| 500 | thunderbolt_suspend(tb); | ||
| 501 | return 0; | ||
| 502 | } | ||
| 503 | |||
| 504 | static int nhi_resume_noirq(struct device *dev) | ||
| 505 | { | ||
| 506 | struct pci_dev *pdev = to_pci_dev(dev); | ||
| 507 | struct tb *tb = pci_get_drvdata(pdev); | ||
| 508 | thunderbolt_resume(tb); | ||
| 509 | return 0; | ||
| 510 | } | ||
| 511 | |||
| 512 | static void nhi_shutdown(struct tb_nhi *nhi) | ||
| 513 | { | ||
| 514 | int i; | ||
| 515 | dev_info(&nhi->pdev->dev, "shutdown\n"); | ||
| 516 | |||
| 517 | for (i = 0; i < nhi->hop_count; i++) { | ||
| 518 | if (nhi->tx_rings[i]) | ||
| 519 | dev_WARN(&nhi->pdev->dev, | ||
| 520 | "TX ring %d is still active\n", i); | ||
| 521 | if (nhi->rx_rings[i]) | ||
| 522 | dev_WARN(&nhi->pdev->dev, | ||
| 523 | "RX ring %d is still active\n", i); | ||
| 524 | } | ||
| 525 | nhi_disable_interrupts(nhi); | ||
| 526 | /* | ||
| 527 | * We have to release the irq before calling flush_work. Otherwise an | ||
| 528 | * already executing IRQ handler could call schedule_work again. | ||
| 529 | */ | ||
| 530 | devm_free_irq(&nhi->pdev->dev, nhi->pdev->irq, nhi); | ||
| 531 | flush_work(&nhi->interrupt_work); | ||
| 532 | mutex_destroy(&nhi->lock); | ||
| 533 | } | ||
| 534 | |||
| 535 | static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) | ||
| 536 | { | ||
| 537 | struct tb_nhi *nhi; | ||
| 538 | struct tb *tb; | ||
| 539 | int res; | ||
| 540 | |||
| 541 | res = pcim_enable_device(pdev); | ||
| 542 | if (res) { | ||
| 543 | dev_err(&pdev->dev, "cannot enable PCI device, aborting\n"); | ||
| 544 | return res; | ||
| 545 | } | ||
| 546 | |||
| 547 | res = pci_enable_msi(pdev); | ||
| 548 | if (res) { | ||
| 549 | dev_err(&pdev->dev, "cannot enable MSI, aborting\n"); | ||
| 550 | return res; | ||
| 551 | } | ||
| 552 | |||
| 553 | res = pcim_iomap_regions(pdev, 1 << 0, "thunderbolt"); | ||
| 554 | if (res) { | ||
| 555 | dev_err(&pdev->dev, "cannot obtain PCI resources, aborting\n"); | ||
| 556 | return res; | ||
| 557 | } | ||
| 558 | |||
| 559 | nhi = devm_kzalloc(&pdev->dev, sizeof(*nhi), GFP_KERNEL); | ||
| 560 | if (!nhi) | ||
| 561 | return -ENOMEM; | ||
| 562 | |||
| 563 | nhi->pdev = pdev; | ||
| 564 | /* cannot fail - table is allocated bin pcim_iomap_regions */ | ||
| 565 | nhi->iobase = pcim_iomap_table(pdev)[0]; | ||
| 566 | nhi->hop_count = ioread32(nhi->iobase + REG_HOP_COUNT) & 0x3ff; | ||
| 567 | if (nhi->hop_count != 12) | ||
| 568 | dev_warn(&pdev->dev, "unexpected hop count: %d\n", | ||
| 569 | nhi->hop_count); | ||
| 570 | INIT_WORK(&nhi->interrupt_work, nhi_interrupt_work); | ||
| 571 | |||
| 572 | nhi->tx_rings = devm_kcalloc(&pdev->dev, nhi->hop_count, | ||
| 573 | sizeof(*nhi->tx_rings), GFP_KERNEL); | ||
| 574 | nhi->rx_rings = devm_kcalloc(&pdev->dev, nhi->hop_count, | ||
| 575 | sizeof(*nhi->rx_rings), GFP_KERNEL); | ||
| 576 | if (!nhi->tx_rings || !nhi->rx_rings) | ||
| 577 | return -ENOMEM; | ||
| 578 | |||
| 579 | nhi_disable_interrupts(nhi); /* In case someone left them on. */ | ||
| 580 | res = devm_request_irq(&pdev->dev, pdev->irq, nhi_msi, | ||
| 581 | IRQF_NO_SUSPEND, /* must work during _noirq */ | ||
| 582 | "thunderbolt", nhi); | ||
| 583 | if (res) { | ||
| 584 | dev_err(&pdev->dev, "request_irq failed, aborting\n"); | ||
| 585 | return res; | ||
| 586 | } | ||
| 587 | |||
| 588 | mutex_init(&nhi->lock); | ||
| 589 | |||
| 590 | pci_set_master(pdev); | ||
| 591 | |||
| 592 | /* magic value - clock related? */ | ||
| 593 | iowrite32(3906250 / 10000, nhi->iobase + 0x38c00); | ||
| 594 | |||
| 595 | dev_info(&nhi->pdev->dev, "NHI initialized, starting thunderbolt\n"); | ||
| 596 | tb = thunderbolt_alloc_and_start(nhi); | ||
| 597 | if (!tb) { | ||
| 598 | /* | ||
| 599 | * At this point the RX/TX rings might already have been | ||
| 600 | * activated. Do a proper shutdown. | ||
| 601 | */ | ||
| 602 | nhi_shutdown(nhi); | ||
| 603 | return -EIO; | ||
| 604 | } | ||
| 605 | pci_set_drvdata(pdev, tb); | ||
| 606 | |||
| 607 | return 0; | ||
| 608 | } | ||
| 609 | |||
| 610 | static void nhi_remove(struct pci_dev *pdev) | ||
| 611 | { | ||
| 612 | struct tb *tb = pci_get_drvdata(pdev); | ||
| 613 | struct tb_nhi *nhi = tb->nhi; | ||
| 614 | thunderbolt_shutdown_and_free(tb); | ||
| 615 | nhi_shutdown(nhi); | ||
| 616 | } | ||
| 617 | |||
| 618 | /* | ||
| 619 | * The tunneled pci bridges are siblings of us. Use resume_noirq to reenable | ||
| 620 | * the tunnels asap. A corresponding pci quirk blocks the downstream bridges | ||
| 621 | * resume_noirq until we are done. | ||
| 622 | */ | ||
| 623 | static const struct dev_pm_ops nhi_pm_ops = { | ||
| 624 | .suspend_noirq = nhi_suspend_noirq, | ||
| 625 | .resume_noirq = nhi_resume_noirq, | ||
| 626 | .freeze_noirq = nhi_suspend_noirq, /* | ||
| 627 | * we just disable hotplug, the | ||
| 628 | * pci-tunnels stay alive. | ||
| 629 | */ | ||
| 630 | .restore_noirq = nhi_resume_noirq, | ||
| 631 | }; | ||
| 632 | |||
| 633 | static struct pci_device_id nhi_ids[] = { | ||
| 634 | /* | ||
| 635 | * We have to specify class, the TB bridges use the same device and | ||
| 636 | * vendor (sub)id. | ||
| 637 | */ | ||
| 638 | { | ||
| 639 | .class = PCI_CLASS_SYSTEM_OTHER << 8, .class_mask = ~0, | ||
| 640 | .vendor = PCI_VENDOR_ID_INTEL, .device = 0x1547, | ||
| 641 | .subvendor = 0x2222, .subdevice = 0x1111, | ||
| 642 | }, | ||
| 643 | { | ||
| 644 | .class = PCI_CLASS_SYSTEM_OTHER << 8, .class_mask = ~0, | ||
| 645 | .vendor = PCI_VENDOR_ID_INTEL, .device = 0x156c, | ||
| 646 | .subvendor = 0x2222, .subdevice = 0x1111, | ||
| 647 | }, | ||
| 648 | { 0,} | ||
| 649 | }; | ||
| 650 | |||
| 651 | MODULE_DEVICE_TABLE(pci, nhi_ids); | ||
| 652 | MODULE_LICENSE("GPL"); | ||
| 653 | |||
| 654 | static struct pci_driver nhi_driver = { | ||
| 655 | .name = "thunderbolt", | ||
| 656 | .id_table = nhi_ids, | ||
| 657 | .probe = nhi_probe, | ||
| 658 | .remove = nhi_remove, | ||
| 659 | .driver.pm = &nhi_pm_ops, | ||
| 660 | }; | ||
| 661 | |||
| 662 | static int __init nhi_init(void) | ||
| 663 | { | ||
| 664 | if (!dmi_match(DMI_BOARD_VENDOR, "Apple Inc.")) | ||
| 665 | return -ENOSYS; | ||
| 666 | return pci_register_driver(&nhi_driver); | ||
| 667 | } | ||
| 668 | |||
| 669 | static void __exit nhi_unload(void) | ||
| 670 | { | ||
| 671 | pci_unregister_driver(&nhi_driver); | ||
| 672 | } | ||
| 673 | |||
| 674 | module_init(nhi_init); | ||
| 675 | module_exit(nhi_unload); | ||
diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h new file mode 100644 index 000000000000..317242939b31 --- /dev/null +++ b/drivers/thunderbolt/nhi.h | |||
| @@ -0,0 +1,114 @@ | |||
| 1 | /* | ||
| 2 | * Thunderbolt Cactus Ridge driver - NHI driver | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> | ||
| 5 | */ | ||
| 6 | |||
| 7 | #ifndef DSL3510_H_ | ||
| 8 | #define DSL3510_H_ | ||
| 9 | |||
| 10 | #include <linux/mutex.h> | ||
| 11 | #include <linux/workqueue.h> | ||
| 12 | |||
| 13 | /** | ||
| 14 | * struct tb_nhi - thunderbolt native host interface | ||
| 15 | */ | ||
| 16 | struct tb_nhi { | ||
| 17 | struct mutex lock; /* | ||
| 18 | * Must be held during ring creation/destruction. | ||
| 19 | * Is acquired by interrupt_work when dispatching | ||
| 20 | * interrupts to individual rings. | ||
| 21 | **/ | ||
| 22 | struct pci_dev *pdev; | ||
| 23 | void __iomem *iobase; | ||
| 24 | struct tb_ring **tx_rings; | ||
| 25 | struct tb_ring **rx_rings; | ||
| 26 | struct work_struct interrupt_work; | ||
| 27 | u32 hop_count; /* Number of rings (end point hops) supported by NHI. */ | ||
| 28 | }; | ||
| 29 | |||
| 30 | /** | ||
| 31 | * struct tb_ring - thunderbolt TX or RX ring associated with a NHI | ||
| 32 | */ | ||
| 33 | struct tb_ring { | ||
| 34 | struct mutex lock; /* must be acquired after nhi->lock */ | ||
| 35 | struct tb_nhi *nhi; | ||
| 36 | int size; | ||
| 37 | int hop; | ||
| 38 | int head; /* write next descriptor here */ | ||
| 39 | int tail; /* complete next descriptor here */ | ||
| 40 | struct ring_desc *descriptors; | ||
| 41 | dma_addr_t descriptors_dma; | ||
| 42 | struct list_head queue; | ||
| 43 | struct list_head in_flight; | ||
| 44 | struct work_struct work; | ||
| 45 | bool is_tx:1; /* rx otherwise */ | ||
| 46 | bool running:1; | ||
| 47 | }; | ||
| 48 | |||
| 49 | struct ring_frame; | ||
| 50 | typedef void (*ring_cb)(struct tb_ring*, struct ring_frame*, bool canceled); | ||
| 51 | |||
| 52 | /** | ||
| 53 | * struct ring_frame - for use with ring_rx/ring_tx | ||
| 54 | */ | ||
| 55 | struct ring_frame { | ||
| 56 | dma_addr_t buffer_phy; | ||
| 57 | ring_cb callback; | ||
| 58 | struct list_head list; | ||
| 59 | u32 size:12; /* TX: in, RX: out*/ | ||
| 60 | u32 flags:12; /* RX: out */ | ||
| 61 | u32 eof:4; /* TX:in, RX: out */ | ||
| 62 | u32 sof:4; /* TX:in, RX: out */ | ||
| 63 | }; | ||
| 64 | |||
| 65 | #define TB_FRAME_SIZE 0x100 /* minimum size for ring_rx */ | ||
| 66 | |||
| 67 | struct tb_ring *ring_alloc_tx(struct tb_nhi *nhi, int hop, int size); | ||
| 68 | struct tb_ring *ring_alloc_rx(struct tb_nhi *nhi, int hop, int size); | ||
| 69 | void ring_start(struct tb_ring *ring); | ||
| 70 | void ring_stop(struct tb_ring *ring); | ||
| 71 | void ring_free(struct tb_ring *ring); | ||
| 72 | |||
| 73 | int __ring_enqueue(struct tb_ring *ring, struct ring_frame *frame); | ||
| 74 | |||
| 75 | /** | ||
| 76 | * ring_rx() - enqueue a frame on an RX ring | ||
| 77 | * | ||
| 78 | * frame->buffer, frame->buffer_phy and frame->callback have to be set. The | ||
| 79 | * buffer must contain at least TB_FRAME_SIZE bytes. | ||
| 80 | * | ||
| 81 | * frame->callback will be invoked with frame->size, frame->flags, frame->eof, | ||
| 82 | * frame->sof set once the frame has been received. | ||
| 83 | * | ||
| 84 | * If ring_stop is called after the packet has been enqueued frame->callback | ||
| 85 | * will be called with canceled set to true. | ||
| 86 | * | ||
| 87 | * Return: Returns ESHUTDOWN if ring_stop has been called. Zero otherwise. | ||
| 88 | */ | ||
| 89 | static inline int ring_rx(struct tb_ring *ring, struct ring_frame *frame) | ||
| 90 | { | ||
| 91 | WARN_ON(ring->is_tx); | ||
| 92 | return __ring_enqueue(ring, frame); | ||
| 93 | } | ||
| 94 | |||
| 95 | /** | ||
| 96 | * ring_tx() - enqueue a frame on an TX ring | ||
| 97 | * | ||
| 98 | * frame->buffer, frame->buffer_phy, frame->callback, frame->size, frame->eof | ||
| 99 | * and frame->sof have to be set. | ||
| 100 | * | ||
| 101 | * frame->callback will be invoked with once the frame has been transmitted. | ||
| 102 | * | ||
| 103 | * If ring_stop is called after the packet has been enqueued frame->callback | ||
| 104 | * will be called with canceled set to true. | ||
| 105 | * | ||
| 106 | * Return: Returns ESHUTDOWN if ring_stop has been called. Zero otherwise. | ||
| 107 | */ | ||
| 108 | static inline int ring_tx(struct tb_ring *ring, struct ring_frame *frame) | ||
| 109 | { | ||
| 110 | WARN_ON(!ring->is_tx); | ||
| 111 | return __ring_enqueue(ring, frame); | ||
| 112 | } | ||
| 113 | |||
| 114 | #endif | ||
diff --git a/drivers/thunderbolt/nhi_regs.h b/drivers/thunderbolt/nhi_regs.h new file mode 100644 index 000000000000..86b996c702a0 --- /dev/null +++ b/drivers/thunderbolt/nhi_regs.h | |||
| @@ -0,0 +1,101 @@ | |||
| 1 | /* | ||
| 2 | * Thunderbolt Cactus Ridge driver - NHI registers | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> | ||
| 5 | */ | ||
| 6 | |||
| 7 | #ifndef DSL3510_REGS_H_ | ||
| 8 | #define DSL3510_REGS_H_ | ||
| 9 | |||
| 10 | #include <linux/types.h> | ||
| 11 | |||
| 12 | enum ring_flags { | ||
| 13 | RING_FLAG_ISOCH_ENABLE = 1 << 27, /* TX only? */ | ||
| 14 | RING_FLAG_E2E_FLOW_CONTROL = 1 << 28, | ||
| 15 | RING_FLAG_PCI_NO_SNOOP = 1 << 29, | ||
| 16 | RING_FLAG_RAW = 1 << 30, /* ignore EOF/SOF mask, include checksum */ | ||
| 17 | RING_FLAG_ENABLE = 1 << 31, | ||
| 18 | }; | ||
| 19 | |||
| 20 | enum ring_desc_flags { | ||
| 21 | RING_DESC_ISOCH = 0x1, /* TX only? */ | ||
| 22 | RING_DESC_COMPLETED = 0x2, /* set by NHI */ | ||
| 23 | RING_DESC_POSTED = 0x4, /* always set this */ | ||
| 24 | RING_DESC_INTERRUPT = 0x8, /* request an interrupt on completion */ | ||
| 25 | }; | ||
| 26 | |||
| 27 | /** | ||
| 28 | * struct ring_desc - TX/RX ring entry | ||
| 29 | * | ||
| 30 | * For TX set length/eof/sof. | ||
| 31 | * For RX length/eof/sof are set by the NHI. | ||
| 32 | */ | ||
| 33 | struct ring_desc { | ||
| 34 | u64 phys; | ||
| 35 | u32 length:12; | ||
| 36 | u32 eof:4; | ||
| 37 | u32 sof:4; | ||
| 38 | enum ring_desc_flags flags:12; | ||
| 39 | u32 time; /* write zero */ | ||
| 40 | } __packed; | ||
| 41 | |||
| 42 | /* NHI registers in bar 0 */ | ||
| 43 | |||
| 44 | /* | ||
| 45 | * 16 bytes per entry, one entry for every hop (REG_HOP_COUNT) | ||
| 46 | * 00: physical pointer to an array of struct ring_desc | ||
| 47 | * 08: ring tail (set by NHI) | ||
| 48 | * 10: ring head (index of first non posted descriptor) | ||
| 49 | * 12: descriptor count | ||
| 50 | */ | ||
| 51 | #define REG_TX_RING_BASE 0x00000 | ||
| 52 | |||
| 53 | /* | ||
| 54 | * 16 bytes per entry, one entry for every hop (REG_HOP_COUNT) | ||
| 55 | * 00: physical pointer to an array of struct ring_desc | ||
| 56 | * 08: ring head (index of first not posted descriptor) | ||
| 57 | * 10: ring tail (set by NHI) | ||
| 58 | * 12: descriptor count | ||
| 59 | * 14: max frame sizes (anything larger than 0x100 has no effect) | ||
| 60 | */ | ||
| 61 | #define REG_RX_RING_BASE 0x08000 | ||
| 62 | |||
| 63 | /* | ||
| 64 | * 32 bytes per entry, one entry for every hop (REG_HOP_COUNT) | ||
| 65 | * 00: enum_ring_flags | ||
| 66 | * 04: isoch time stamp ?? (write 0) | ||
| 67 | * ..: unknown | ||
| 68 | */ | ||
| 69 | #define REG_TX_OPTIONS_BASE 0x19800 | ||
| 70 | |||
| 71 | /* | ||
| 72 | * 32 bytes per entry, one entry for every hop (REG_HOP_COUNT) | ||
| 73 | * 00: enum ring_flags | ||
| 74 | * If RING_FLAG_E2E_FLOW_CONTROL is set then bits 13-23 must be set to | ||
| 75 | * the corresponding TX hop id. | ||
| 76 | * 04: EOF/SOF mask (ignored for RING_FLAG_RAW rings) | ||
| 77 | * ..: unknown | ||
| 78 | */ | ||
| 79 | #define REG_RX_OPTIONS_BASE 0x29800 | ||
| 80 | |||
| 81 | /* | ||
| 82 | * three bitfields: tx, rx, rx overflow | ||
| 83 | * Every bitfield contains one bit for every hop (REG_HOP_COUNT). Registers are | ||
| 84 | * cleared on read. New interrupts are fired only after ALL registers have been | ||
| 85 | * read (even those containing only disabled rings). | ||
| 86 | */ | ||
| 87 | #define REG_RING_NOTIFY_BASE 0x37800 | ||
| 88 | #define RING_NOTIFY_REG_COUNT(nhi) ((31 + 3 * nhi->hop_count) / 32) | ||
| 89 | |||
| 90 | /* | ||
| 91 | * two bitfields: rx, tx | ||
| 92 | * Both bitfields contains one bit for every hop (REG_HOP_COUNT). To | ||
| 93 | * enable/disable interrupts set/clear the corresponding bits. | ||
| 94 | */ | ||
| 95 | #define REG_RING_INTERRUPT_BASE 0x38200 | ||
| 96 | #define RING_INTERRUPT_REG_COUNT(nhi) ((31 + 2 * nhi->hop_count) / 32) | ||
| 97 | |||
| 98 | /* The last 11 bits contain the number of hops supported by the NHI port. */ | ||
| 99 | #define REG_HOP_COUNT 0x39640 | ||
| 100 | |||
| 101 | #endif | ||
diff --git a/drivers/thunderbolt/path.c b/drivers/thunderbolt/path.c new file mode 100644 index 000000000000..8fcf8a7b6c22 --- /dev/null +++ b/drivers/thunderbolt/path.c | |||
| @@ -0,0 +1,215 @@ | |||
| 1 | /* | ||
| 2 | * Thunderbolt Cactus Ridge driver - path/tunnel functionality | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include <linux/slab.h> | ||
| 8 | #include <linux/errno.h> | ||
| 9 | |||
| 10 | #include "tb.h" | ||
| 11 | |||
| 12 | |||
| 13 | static void tb_dump_hop(struct tb_port *port, struct tb_regs_hop *hop) | ||
| 14 | { | ||
| 15 | tb_port_info(port, " Hop through port %d to hop %d (%s)\n", | ||
| 16 | hop->out_port, hop->next_hop, | ||
| 17 | hop->enable ? "enabled" : "disabled"); | ||
| 18 | tb_port_info(port, " Weight: %d Priority: %d Credits: %d Drop: %d\n", | ||
| 19 | hop->weight, hop->priority, | ||
| 20 | hop->initial_credits, hop->drop_packages); | ||
| 21 | tb_port_info(port, " Counter enabled: %d Counter index: %d\n", | ||
| 22 | hop->counter_enable, hop->counter); | ||
| 23 | tb_port_info(port, " Flow Control (In/Eg): %d/%d Shared Buffer (In/Eg): %d/%d\n", | ||
| 24 | hop->ingress_fc, hop->egress_fc, | ||
| 25 | hop->ingress_shared_buffer, hop->egress_shared_buffer); | ||
| 26 | tb_port_info(port, " Unknown1: %#x Unknown2: %#x Unknown3: %#x\n", | ||
| 27 | hop->unknown1, hop->unknown2, hop->unknown3); | ||
| 28 | } | ||
| 29 | |||
| 30 | /** | ||
| 31 | * tb_path_alloc() - allocate a thunderbolt path | ||
| 32 | * | ||
| 33 | * Return: Returns a tb_path on success or NULL on failure. | ||
| 34 | */ | ||
| 35 | struct tb_path *tb_path_alloc(struct tb *tb, int num_hops) | ||
| 36 | { | ||
| 37 | struct tb_path *path = kzalloc(sizeof(*path), GFP_KERNEL); | ||
| 38 | if (!path) | ||
| 39 | return NULL; | ||
| 40 | path->hops = kcalloc(num_hops, sizeof(*path->hops), GFP_KERNEL); | ||
| 41 | if (!path->hops) { | ||
| 42 | kfree(path); | ||
| 43 | return NULL; | ||
| 44 | } | ||
| 45 | path->tb = tb; | ||
| 46 | path->path_length = num_hops; | ||
| 47 | return path; | ||
| 48 | } | ||
| 49 | |||
| 50 | /** | ||
| 51 | * tb_path_free() - free a deactivated path | ||
| 52 | */ | ||
| 53 | void tb_path_free(struct tb_path *path) | ||
| 54 | { | ||
| 55 | if (path->activated) { | ||
| 56 | tb_WARN(path->tb, "trying to free an activated path\n") | ||
| 57 | return; | ||
| 58 | } | ||
| 59 | kfree(path->hops); | ||
| 60 | kfree(path); | ||
| 61 | } | ||
| 62 | |||
| 63 | static void __tb_path_deallocate_nfc(struct tb_path *path, int first_hop) | ||
| 64 | { | ||
| 65 | int i, res; | ||
| 66 | for (i = first_hop; i < path->path_length; i++) { | ||
| 67 | res = tb_port_add_nfc_credits(path->hops[i].in_port, | ||
| 68 | -path->nfc_credits); | ||
| 69 | if (res) | ||
| 70 | tb_port_warn(path->hops[i].in_port, | ||
| 71 | "nfc credits deallocation failed for hop %d\n", | ||
| 72 | i); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | static void __tb_path_deactivate_hops(struct tb_path *path, int first_hop) | ||
| 77 | { | ||
| 78 | int i, res; | ||
| 79 | struct tb_regs_hop hop = { }; | ||
| 80 | for (i = first_hop; i < path->path_length; i++) { | ||
| 81 | res = tb_port_write(path->hops[i].in_port, &hop, TB_CFG_HOPS, | ||
| 82 | 2 * path->hops[i].in_hop_index, 2); | ||
| 83 | if (res) | ||
| 84 | tb_port_warn(path->hops[i].in_port, | ||
| 85 | "hop deactivation failed for hop %d, index %d\n", | ||
| 86 | i, path->hops[i].in_hop_index); | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | void tb_path_deactivate(struct tb_path *path) | ||
| 91 | { | ||
| 92 | if (!path->activated) { | ||
| 93 | tb_WARN(path->tb, "trying to deactivate an inactive path\n"); | ||
| 94 | return; | ||
| 95 | } | ||
| 96 | tb_info(path->tb, | ||
| 97 | "deactivating path from %llx:%x to %llx:%x\n", | ||
| 98 | tb_route(path->hops[0].in_port->sw), | ||
| 99 | path->hops[0].in_port->port, | ||
| 100 | tb_route(path->hops[path->path_length - 1].out_port->sw), | ||
| 101 | path->hops[path->path_length - 1].out_port->port); | ||
| 102 | __tb_path_deactivate_hops(path, 0); | ||
| 103 | __tb_path_deallocate_nfc(path, 0); | ||
| 104 | path->activated = false; | ||
| 105 | } | ||
| 106 | |||
| 107 | /** | ||
| 108 | * tb_path_activate() - activate a path | ||
| 109 | * | ||
| 110 | * Activate a path starting with the last hop and iterating backwards. The | ||
| 111 | * caller must fill path->hops before calling tb_path_activate(). | ||
| 112 | * | ||
| 113 | * Return: Returns 0 on success or an error code on failure. | ||
| 114 | */ | ||
| 115 | int tb_path_activate(struct tb_path *path) | ||
| 116 | { | ||
| 117 | int i, res; | ||
| 118 | enum tb_path_port out_mask, in_mask; | ||
| 119 | if (path->activated) { | ||
| 120 | tb_WARN(path->tb, "trying to activate already activated path\n"); | ||
| 121 | return -EINVAL; | ||
| 122 | } | ||
| 123 | |||
| 124 | tb_info(path->tb, | ||
| 125 | "activating path from %llx:%x to %llx:%x\n", | ||
| 126 | tb_route(path->hops[0].in_port->sw), | ||
| 127 | path->hops[0].in_port->port, | ||
| 128 | tb_route(path->hops[path->path_length - 1].out_port->sw), | ||
| 129 | path->hops[path->path_length - 1].out_port->port); | ||
| 130 | |||
| 131 | /* Clear counters. */ | ||
| 132 | for (i = path->path_length - 1; i >= 0; i--) { | ||
| 133 | if (path->hops[i].in_counter_index == -1) | ||
| 134 | continue; | ||
| 135 | res = tb_port_clear_counter(path->hops[i].in_port, | ||
| 136 | path->hops[i].in_counter_index); | ||
| 137 | if (res) | ||
| 138 | goto err; | ||
| 139 | } | ||
| 140 | |||
| 141 | /* Add non flow controlled credits. */ | ||
| 142 | for (i = path->path_length - 1; i >= 0; i--) { | ||
| 143 | res = tb_port_add_nfc_credits(path->hops[i].in_port, | ||
| 144 | path->nfc_credits); | ||
| 145 | if (res) { | ||
| 146 | __tb_path_deallocate_nfc(path, i); | ||
| 147 | goto err; | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | /* Activate hops. */ | ||
| 152 | for (i = path->path_length - 1; i >= 0; i--) { | ||
| 153 | struct tb_regs_hop hop; | ||
| 154 | |||
| 155 | /* dword 0 */ | ||
| 156 | hop.next_hop = path->hops[i].next_hop_index; | ||
| 157 | hop.out_port = path->hops[i].out_port->port; | ||
| 158 | /* TODO: figure out why these are good values */ | ||
| 159 | hop.initial_credits = (i == path->path_length - 1) ? 16 : 7; | ||
| 160 | hop.unknown1 = 0; | ||
| 161 | hop.enable = 1; | ||
| 162 | |||
| 163 | /* dword 1 */ | ||
| 164 | out_mask = (i == path->path_length - 1) ? | ||
| 165 | TB_PATH_DESTINATION : TB_PATH_INTERNAL; | ||
| 166 | in_mask = (i == 0) ? TB_PATH_SOURCE : TB_PATH_INTERNAL; | ||
| 167 | hop.weight = path->weight; | ||
| 168 | hop.unknown2 = 0; | ||
| 169 | hop.priority = path->priority; | ||
| 170 | hop.drop_packages = path->drop_packages; | ||
| 171 | hop.counter = path->hops[i].in_counter_index; | ||
| 172 | hop.counter_enable = path->hops[i].in_counter_index != -1; | ||
| 173 | hop.ingress_fc = path->ingress_fc_enable & in_mask; | ||
| 174 | hop.egress_fc = path->egress_fc_enable & out_mask; | ||
| 175 | hop.ingress_shared_buffer = path->ingress_shared_buffer | ||
| 176 | & in_mask; | ||
| 177 | hop.egress_shared_buffer = path->egress_shared_buffer | ||
| 178 | & out_mask; | ||
| 179 | hop.unknown3 = 0; | ||
| 180 | |||
| 181 | tb_port_info(path->hops[i].in_port, "Writing hop %d, index %d", | ||
| 182 | i, path->hops[i].in_hop_index); | ||
| 183 | tb_dump_hop(path->hops[i].in_port, &hop); | ||
| 184 | res = tb_port_write(path->hops[i].in_port, &hop, TB_CFG_HOPS, | ||
| 185 | 2 * path->hops[i].in_hop_index, 2); | ||
| 186 | if (res) { | ||
| 187 | __tb_path_deactivate_hops(path, i); | ||
| 188 | __tb_path_deallocate_nfc(path, 0); | ||
| 189 | goto err; | ||
| 190 | } | ||
| 191 | } | ||
| 192 | path->activated = true; | ||
| 193 | tb_info(path->tb, "path activation complete\n"); | ||
| 194 | return 0; | ||
| 195 | err: | ||
| 196 | tb_WARN(path->tb, "path activation failed\n"); | ||
| 197 | return res; | ||
| 198 | } | ||
| 199 | |||
| 200 | /** | ||
| 201 | * tb_path_is_invalid() - check whether any ports on the path are invalid | ||
| 202 | * | ||
| 203 | * Return: Returns true if the path is invalid, false otherwise. | ||
| 204 | */ | ||
| 205 | bool tb_path_is_invalid(struct tb_path *path) | ||
| 206 | { | ||
| 207 | int i = 0; | ||
| 208 | for (i = 0; i < path->path_length; i++) { | ||
| 209 | if (path->hops[i].in_port->sw->is_unplugged) | ||
| 210 | return true; | ||
| 211 | if (path->hops[i].out_port->sw->is_unplugged) | ||
| 212 | return true; | ||
| 213 | } | ||
| 214 | return false; | ||
| 215 | } | ||
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c new file mode 100644 index 000000000000..aeb982969629 --- /dev/null +++ b/drivers/thunderbolt/switch.c | |||
| @@ -0,0 +1,507 @@ | |||
| 1 | /* | ||
| 2 | * Thunderbolt Cactus Ridge driver - switch/port utility functions | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include <linux/delay.h> | ||
| 8 | #include <linux/slab.h> | ||
| 9 | |||
| 10 | #include "tb.h" | ||
| 11 | |||
| 12 | /* port utility functions */ | ||
| 13 | |||
| 14 | static const char *tb_port_type(struct tb_regs_port_header *port) | ||
| 15 | { | ||
| 16 | switch (port->type >> 16) { | ||
| 17 | case 0: | ||
| 18 | switch ((u8) port->type) { | ||
| 19 | case 0: | ||
| 20 | return "Inactive"; | ||
| 21 | case 1: | ||
| 22 | return "Port"; | ||
| 23 | case 2: | ||
| 24 | return "NHI"; | ||
| 25 | default: | ||
| 26 | return "unknown"; | ||
| 27 | } | ||
| 28 | case 0x2: | ||
| 29 | return "Ethernet"; | ||
| 30 | case 0x8: | ||
| 31 | return "SATA"; | ||
| 32 | case 0xe: | ||
| 33 | return "DP/HDMI"; | ||
| 34 | case 0x10: | ||
| 35 | return "PCIe"; | ||
| 36 | case 0x20: | ||
| 37 | return "USB"; | ||
| 38 | default: | ||
| 39 | return "unknown"; | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | static void tb_dump_port(struct tb *tb, struct tb_regs_port_header *port) | ||
| 44 | { | ||
| 45 | tb_info(tb, | ||
| 46 | " Port %d: %x:%x (Revision: %d, TB Version: %d, Type: %s (%#x))\n", | ||
| 47 | port->port_number, port->vendor_id, port->device_id, | ||
| 48 | port->revision, port->thunderbolt_version, tb_port_type(port), | ||
| 49 | port->type); | ||
| 50 | tb_info(tb, " Max hop id (in/out): %d/%d\n", | ||
| 51 | port->max_in_hop_id, port->max_out_hop_id); | ||
| 52 | tb_info(tb, " Max counters: %d\n", port->max_counters); | ||
| 53 | tb_info(tb, " NFC Credits: %#x\n", port->nfc_credits); | ||
| 54 | } | ||
| 55 | |||
| 56 | /** | ||
| 57 | * tb_port_state() - get connectedness state of a port | ||
| 58 | * | ||
| 59 | * The port must have a TB_CAP_PHY (i.e. it should be a real port). | ||
| 60 | * | ||
| 61 | * Return: Returns an enum tb_port_state on success or an error code on failure. | ||
| 62 | */ | ||
| 63 | static int tb_port_state(struct tb_port *port) | ||
| 64 | { | ||
| 65 | struct tb_cap_phy phy; | ||
| 66 | int res; | ||
| 67 | if (port->cap_phy == 0) { | ||
| 68 | tb_port_WARN(port, "does not have a PHY\n"); | ||
| 69 | return -EINVAL; | ||
| 70 | } | ||
| 71 | res = tb_port_read(port, &phy, TB_CFG_PORT, port->cap_phy, 2); | ||
| 72 | if (res) | ||
| 73 | return res; | ||
| 74 | return phy.state; | ||
| 75 | } | ||
| 76 | |||
| 77 | /** | ||
| 78 | * tb_wait_for_port() - wait for a port to become ready | ||
| 79 | * | ||
| 80 | * Wait up to 1 second for a port to reach state TB_PORT_UP. If | ||
| 81 | * wait_if_unplugged is set then we also wait if the port is in state | ||
| 82 | * TB_PORT_UNPLUGGED (it takes a while for the device to be registered after | ||
| 83 | * switch resume). Otherwise we only wait if a device is registered but the link | ||
| 84 | * has not yet been established. | ||
| 85 | * | ||
| 86 | * Return: Returns an error code on failure. Returns 0 if the port is not | ||
| 87 | * connected or failed to reach state TB_PORT_UP within one second. Returns 1 | ||
| 88 | * if the port is connected and in state TB_PORT_UP. | ||
| 89 | */ | ||
| 90 | int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged) | ||
| 91 | { | ||
| 92 | int retries = 10; | ||
| 93 | int state; | ||
| 94 | if (!port->cap_phy) { | ||
| 95 | tb_port_WARN(port, "does not have PHY\n"); | ||
| 96 | return -EINVAL; | ||
| 97 | } | ||
| 98 | if (tb_is_upstream_port(port)) { | ||
| 99 | tb_port_WARN(port, "is the upstream port\n"); | ||
| 100 | return -EINVAL; | ||
| 101 | } | ||
| 102 | |||
| 103 | while (retries--) { | ||
| 104 | state = tb_port_state(port); | ||
| 105 | if (state < 0) | ||
| 106 | return state; | ||
| 107 | if (state == TB_PORT_DISABLED) { | ||
| 108 | tb_port_info(port, "is disabled (state: 0)\n"); | ||
| 109 | return 0; | ||
| 110 | } | ||
| 111 | if (state == TB_PORT_UNPLUGGED) { | ||
| 112 | if (wait_if_unplugged) { | ||
| 113 | /* used during resume */ | ||
| 114 | tb_port_info(port, | ||
| 115 | "is unplugged (state: 7), retrying...\n"); | ||
| 116 | msleep(100); | ||
| 117 | continue; | ||
| 118 | } | ||
| 119 | tb_port_info(port, "is unplugged (state: 7)\n"); | ||
| 120 | return 0; | ||
| 121 | } | ||
| 122 | if (state == TB_PORT_UP) { | ||
| 123 | tb_port_info(port, | ||
| 124 | "is connected, link is up (state: 2)\n"); | ||
| 125 | return 1; | ||
| 126 | } | ||
| 127 | |||
| 128 | /* | ||
| 129 | * After plug-in the state is TB_PORT_CONNECTING. Give it some | ||
| 130 | * time. | ||
| 131 | */ | ||
| 132 | tb_port_info(port, | ||
| 133 | "is connected, link is not up (state: %d), retrying...\n", | ||
| 134 | state); | ||
| 135 | msleep(100); | ||
| 136 | } | ||
| 137 | tb_port_warn(port, | ||
| 138 | "failed to reach state TB_PORT_UP. Ignoring port...\n"); | ||
| 139 | return 0; | ||
| 140 | } | ||
| 141 | |||
| 142 | /** | ||
| 143 | * tb_port_add_nfc_credits() - add/remove non flow controlled credits to port | ||
| 144 | * | ||
| 145 | * Change the number of NFC credits allocated to @port by @credits. To remove | ||
| 146 | * NFC credits pass a negative amount of credits. | ||
| 147 | * | ||
| 148 | * Return: Returns 0 on success or an error code on failure. | ||
| 149 | */ | ||
| 150 | int tb_port_add_nfc_credits(struct tb_port *port, int credits) | ||
| 151 | { | ||
| 152 | if (credits == 0) | ||
| 153 | return 0; | ||
| 154 | tb_port_info(port, | ||
| 155 | "adding %#x NFC credits (%#x -> %#x)", | ||
| 156 | credits, | ||
| 157 | port->config.nfc_credits, | ||
| 158 | port->config.nfc_credits + credits); | ||
| 159 | port->config.nfc_credits += credits; | ||
| 160 | return tb_port_write(port, &port->config.nfc_credits, | ||
| 161 | TB_CFG_PORT, 4, 1); | ||
| 162 | } | ||
| 163 | |||
| 164 | /** | ||
| 165 | * tb_port_clear_counter() - clear a counter in TB_CFG_COUNTER | ||
| 166 | * | ||
| 167 | * Return: Returns 0 on success or an error code on failure. | ||
| 168 | */ | ||
| 169 | int tb_port_clear_counter(struct tb_port *port, int counter) | ||
| 170 | { | ||
| 171 | u32 zero[3] = { 0, 0, 0 }; | ||
| 172 | tb_port_info(port, "clearing counter %d\n", counter); | ||
| 173 | return tb_port_write(port, zero, TB_CFG_COUNTERS, 3 * counter, 3); | ||
| 174 | } | ||
| 175 | |||
| 176 | /** | ||
| 177 | * tb_init_port() - initialize a port | ||
| 178 | * | ||
| 179 | * This is a helper method for tb_switch_alloc. Does not check or initialize | ||
| 180 | * any downstream switches. | ||
| 181 | * | ||
| 182 | * Return: Returns 0 on success or an error code on failure. | ||
| 183 | */ | ||
| 184 | static int tb_init_port(struct tb_port *port) | ||
| 185 | { | ||
| 186 | int res; | ||
| 187 | int cap; | ||
| 188 | |||
| 189 | res = tb_port_read(port, &port->config, TB_CFG_PORT, 0, 8); | ||
| 190 | if (res) | ||
| 191 | return res; | ||
| 192 | |||
| 193 | /* Port 0 is the switch itself and has no PHY. */ | ||
| 194 | if (port->config.type == TB_TYPE_PORT && port->port != 0) { | ||
| 195 | cap = tb_find_cap(port, TB_CFG_PORT, TB_CAP_PHY); | ||
| 196 | |||
| 197 | if (cap > 0) | ||
| 198 | port->cap_phy = cap; | ||
| 199 | else | ||
| 200 | tb_port_WARN(port, "non switch port without a PHY\n"); | ||
| 201 | } | ||
| 202 | |||
| 203 | tb_dump_port(port->sw->tb, &port->config); | ||
| 204 | |||
| 205 | /* TODO: Read dual link port, DP port and more from EEPROM. */ | ||
| 206 | return 0; | ||
| 207 | |||
| 208 | } | ||
| 209 | |||
| 210 | /* switch utility functions */ | ||
| 211 | |||
| 212 | static void tb_dump_switch(struct tb *tb, struct tb_regs_switch_header *sw) | ||
| 213 | { | ||
| 214 | tb_info(tb, | ||
| 215 | " Switch: %x:%x (Revision: %d, TB Version: %d)\n", | ||
| 216 | sw->vendor_id, sw->device_id, sw->revision, | ||
| 217 | sw->thunderbolt_version); | ||
| 218 | tb_info(tb, " Max Port Number: %d\n", sw->max_port_number); | ||
| 219 | tb_info(tb, " Config:\n"); | ||
| 220 | tb_info(tb, | ||
| 221 | " Upstream Port Number: %d Depth: %d Route String: %#llx Enabled: %d, PlugEventsDelay: %dms\n", | ||
| 222 | sw->upstream_port_number, sw->depth, | ||
| 223 | (((u64) sw->route_hi) << 32) | sw->route_lo, | ||
| 224 | sw->enabled, sw->plug_events_delay); | ||
| 225 | tb_info(tb, | ||
| 226 | " unknown1: %#x unknown4: %#x\n", | ||
| 227 | sw->__unknown1, sw->__unknown4); | ||
| 228 | } | ||
| 229 | |||
| 230 | /** | ||
| 231 | * reset_switch() - reconfigure route, enable and send TB_CFG_PKG_RESET | ||
| 232 | * | ||
| 233 | * Return: Returns 0 on success or an error code on failure. | ||
| 234 | */ | ||
| 235 | int tb_switch_reset(struct tb *tb, u64 route) | ||
| 236 | { | ||
| 237 | struct tb_cfg_result res; | ||
| 238 | struct tb_regs_switch_header header = { | ||
| 239 | header.route_hi = route >> 32, | ||
| 240 | header.route_lo = route, | ||
| 241 | header.enabled = true, | ||
| 242 | }; | ||
| 243 | tb_info(tb, "resetting switch at %llx\n", route); | ||
| 244 | res.err = tb_cfg_write(tb->ctl, ((u32 *) &header) + 2, route, | ||
| 245 | 0, 2, 2, 2); | ||
| 246 | if (res.err) | ||
| 247 | return res.err; | ||
| 248 | res = tb_cfg_reset(tb->ctl, route, TB_CFG_DEFAULT_TIMEOUT); | ||
| 249 | if (res.err > 0) | ||
| 250 | return -EIO; | ||
| 251 | return res.err; | ||
| 252 | } | ||
| 253 | |||
| 254 | struct tb_switch *get_switch_at_route(struct tb_switch *sw, u64 route) | ||
| 255 | { | ||
| 256 | u8 next_port = route; /* | ||
| 257 | * Routes use a stride of 8 bits, | ||
| 258 | * eventhough a port index has 6 bits at most. | ||
| 259 | * */ | ||
| 260 | if (route == 0) | ||
| 261 | return sw; | ||
| 262 | if (next_port > sw->config.max_port_number) | ||
| 263 | return NULL; | ||
| 264 | if (tb_is_upstream_port(&sw->ports[next_port])) | ||
| 265 | return NULL; | ||
| 266 | if (!sw->ports[next_port].remote) | ||
| 267 | return NULL; | ||
| 268 | return get_switch_at_route(sw->ports[next_port].remote->sw, | ||
| 269 | route >> TB_ROUTE_SHIFT); | ||
| 270 | } | ||
| 271 | |||
| 272 | /** | ||
| 273 | * tb_plug_events_active() - enable/disable plug events on a switch | ||
| 274 | * | ||
| 275 | * Also configures a sane plug_events_delay of 255ms. | ||
| 276 | * | ||
| 277 | * Return: Returns 0 on success or an error code on failure. | ||
| 278 | */ | ||
| 279 | static int tb_plug_events_active(struct tb_switch *sw, bool active) | ||
| 280 | { | ||
| 281 | u32 data; | ||
| 282 | int res; | ||
| 283 | |||
| 284 | sw->config.plug_events_delay = 0xff; | ||
| 285 | res = tb_sw_write(sw, ((u32 *) &sw->config) + 4, TB_CFG_SWITCH, 4, 1); | ||
| 286 | if (res) | ||
| 287 | return res; | ||
| 288 | |||
| 289 | res = tb_sw_read(sw, &data, TB_CFG_SWITCH, sw->cap_plug_events + 1, 1); | ||
| 290 | if (res) | ||
| 291 | return res; | ||
| 292 | |||
| 293 | if (active) { | ||
| 294 | data = data & 0xFFFFFF83; | ||
| 295 | switch (sw->config.device_id) { | ||
| 296 | case 0x1513: | ||
| 297 | case 0x151a: | ||
| 298 | case 0x1549: | ||
| 299 | break; | ||
| 300 | default: | ||
| 301 | data |= 4; | ||
| 302 | } | ||
| 303 | } else { | ||
| 304 | data = data | 0x7c; | ||
| 305 | } | ||
| 306 | return tb_sw_write(sw, &data, TB_CFG_SWITCH, | ||
| 307 | sw->cap_plug_events + 1, 1); | ||
| 308 | } | ||
| 309 | |||
| 310 | |||
| 311 | /** | ||
| 312 | * tb_switch_free() - free a tb_switch and all downstream switches | ||
| 313 | */ | ||
| 314 | void tb_switch_free(struct tb_switch *sw) | ||
| 315 | { | ||
| 316 | int i; | ||
| 317 | /* port 0 is the switch itself and never has a remote */ | ||
| 318 | for (i = 1; i <= sw->config.max_port_number; i++) { | ||
| 319 | if (tb_is_upstream_port(&sw->ports[i])) | ||
| 320 | continue; | ||
| 321 | if (sw->ports[i].remote) | ||
| 322 | tb_switch_free(sw->ports[i].remote->sw); | ||
| 323 | sw->ports[i].remote = NULL; | ||
| 324 | } | ||
| 325 | |||
| 326 | if (!sw->is_unplugged) | ||
| 327 | tb_plug_events_active(sw, false); | ||
| 328 | |||
| 329 | kfree(sw->ports); | ||
| 330 | kfree(sw->drom); | ||
| 331 | kfree(sw); | ||
| 332 | } | ||
| 333 | |||
| 334 | /** | ||
| 335 | * tb_switch_alloc() - allocate and initialize a switch | ||
| 336 | * | ||
| 337 | * Return: Returns a NULL on failure. | ||
| 338 | */ | ||
| 339 | struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route) | ||
| 340 | { | ||
| 341 | int i; | ||
| 342 | int cap; | ||
| 343 | struct tb_switch *sw; | ||
| 344 | int upstream_port = tb_cfg_get_upstream_port(tb->ctl, route); | ||
| 345 | if (upstream_port < 0) | ||
| 346 | return NULL; | ||
| 347 | |||
| 348 | sw = kzalloc(sizeof(*sw), GFP_KERNEL); | ||
| 349 | if (!sw) | ||
| 350 | return NULL; | ||
| 351 | |||
| 352 | sw->tb = tb; | ||
| 353 | if (tb_cfg_read(tb->ctl, &sw->config, route, 0, 2, 0, 5)) | ||
| 354 | goto err; | ||
| 355 | tb_info(tb, | ||
| 356 | "initializing Switch at %#llx (depth: %d, up port: %d)\n", | ||
| 357 | route, tb_route_length(route), upstream_port); | ||
| 358 | tb_info(tb, "old switch config:\n"); | ||
| 359 | tb_dump_switch(tb, &sw->config); | ||
| 360 | |||
| 361 | /* configure switch */ | ||
| 362 | sw->config.upstream_port_number = upstream_port; | ||
| 363 | sw->config.depth = tb_route_length(route); | ||
| 364 | sw->config.route_lo = route; | ||
| 365 | sw->config.route_hi = route >> 32; | ||
| 366 | sw->config.enabled = 1; | ||
| 367 | /* from here on we may use the tb_sw_* functions & macros */ | ||
| 368 | |||
| 369 | if (sw->config.vendor_id != 0x8086) | ||
| 370 | tb_sw_warn(sw, "unknown switch vendor id %#x\n", | ||
| 371 | sw->config.vendor_id); | ||
| 372 | |||
| 373 | if (sw->config.device_id != 0x1547 && sw->config.device_id != 0x1549) | ||
| 374 | tb_sw_warn(sw, "unsupported switch device id %#x\n", | ||
| 375 | sw->config.device_id); | ||
| 376 | |||
| 377 | /* upload configuration */ | ||
| 378 | if (tb_sw_write(sw, 1 + (u32 *) &sw->config, TB_CFG_SWITCH, 1, 3)) | ||
| 379 | goto err; | ||
| 380 | |||
| 381 | /* initialize ports */ | ||
| 382 | sw->ports = kcalloc(sw->config.max_port_number + 1, sizeof(*sw->ports), | ||
| 383 | GFP_KERNEL); | ||
| 384 | if (!sw->ports) | ||
| 385 | goto err; | ||
| 386 | |||
| 387 | for (i = 0; i <= sw->config.max_port_number; i++) { | ||
| 388 | /* minimum setup for tb_find_cap and tb_drom_read to work */ | ||
| 389 | sw->ports[i].sw = sw; | ||
| 390 | sw->ports[i].port = i; | ||
| 391 | } | ||
| 392 | |||
| 393 | cap = tb_find_cap(&sw->ports[0], TB_CFG_SWITCH, TB_CAP_PLUG_EVENTS); | ||
| 394 | if (cap < 0) { | ||
| 395 | tb_sw_warn(sw, "cannot find TB_CAP_PLUG_EVENTS aborting\n"); | ||
| 396 | goto err; | ||
| 397 | } | ||
| 398 | sw->cap_plug_events = cap; | ||
| 399 | |||
| 400 | /* read drom */ | ||
| 401 | if (tb_drom_read(sw)) | ||
| 402 | tb_sw_warn(sw, "tb_eeprom_read_rom failed, continuing\n"); | ||
| 403 | tb_sw_info(sw, "uid: %#llx\n", sw->uid); | ||
| 404 | |||
| 405 | for (i = 0; i <= sw->config.max_port_number; i++) { | ||
| 406 | if (sw->ports[i].disabled) { | ||
| 407 | tb_port_info(&sw->ports[i], "disabled by eeprom\n"); | ||
| 408 | continue; | ||
| 409 | } | ||
| 410 | if (tb_init_port(&sw->ports[i])) | ||
| 411 | goto err; | ||
| 412 | } | ||
| 413 | |||
| 414 | /* TODO: I2C, IECS, link controller */ | ||
| 415 | |||
| 416 | if (tb_plug_events_active(sw, true)) | ||
| 417 | goto err; | ||
| 418 | |||
| 419 | return sw; | ||
| 420 | err: | ||
| 421 | kfree(sw->ports); | ||
| 422 | kfree(sw->drom); | ||
| 423 | kfree(sw); | ||
| 424 | return NULL; | ||
| 425 | } | ||
| 426 | |||
| 427 | /** | ||
| 428 | * tb_sw_set_unpplugged() - set is_unplugged on switch and downstream switches | ||
| 429 | */ | ||
| 430 | void tb_sw_set_unpplugged(struct tb_switch *sw) | ||
| 431 | { | ||
| 432 | int i; | ||
| 433 | if (sw == sw->tb->root_switch) { | ||
| 434 | tb_sw_WARN(sw, "cannot unplug root switch\n"); | ||
| 435 | return; | ||
| 436 | } | ||
| 437 | if (sw->is_unplugged) { | ||
| 438 | tb_sw_WARN(sw, "is_unplugged already set\n"); | ||
| 439 | return; | ||
| 440 | } | ||
| 441 | sw->is_unplugged = true; | ||
| 442 | for (i = 0; i <= sw->config.max_port_number; i++) { | ||
| 443 | if (!tb_is_upstream_port(&sw->ports[i]) && sw->ports[i].remote) | ||
| 444 | tb_sw_set_unpplugged(sw->ports[i].remote->sw); | ||
| 445 | } | ||
| 446 | } | ||
| 447 | |||
| 448 | int tb_switch_resume(struct tb_switch *sw) | ||
| 449 | { | ||
| 450 | int i, err; | ||
| 451 | u64 uid; | ||
| 452 | tb_sw_info(sw, "resuming switch\n"); | ||
| 453 | |||
| 454 | err = tb_drom_read_uid_only(sw, &uid); | ||
| 455 | if (err) { | ||
| 456 | tb_sw_warn(sw, "uid read failed\n"); | ||
| 457 | return err; | ||
| 458 | } | ||
| 459 | if (sw->uid != uid) { | ||
| 460 | tb_sw_info(sw, | ||
| 461 | "changed while suspended (uid %#llx -> %#llx)\n", | ||
| 462 | sw->uid, uid); | ||
| 463 | return -ENODEV; | ||
| 464 | } | ||
| 465 | |||
| 466 | /* upload configuration */ | ||
| 467 | err = tb_sw_write(sw, 1 + (u32 *) &sw->config, TB_CFG_SWITCH, 1, 3); | ||
| 468 | if (err) | ||
| 469 | return err; | ||
| 470 | |||
| 471 | err = tb_plug_events_active(sw, true); | ||
| 472 | if (err) | ||
| 473 | return err; | ||
| 474 | |||
| 475 | /* check for surviving downstream switches */ | ||
| 476 | for (i = 1; i <= sw->config.max_port_number; i++) { | ||
| 477 | struct tb_port *port = &sw->ports[i]; | ||
| 478 | if (tb_is_upstream_port(port)) | ||
| 479 | continue; | ||
| 480 | if (!port->remote) | ||
| 481 | continue; | ||
| 482 | if (tb_wait_for_port(port, true) <= 0 | ||
| 483 | || tb_switch_resume(port->remote->sw)) { | ||
| 484 | tb_port_warn(port, | ||
| 485 | "lost during suspend, disconnecting\n"); | ||
| 486 | tb_sw_set_unpplugged(port->remote->sw); | ||
| 487 | } | ||
| 488 | } | ||
| 489 | return 0; | ||
| 490 | } | ||
| 491 | |||
| 492 | void tb_switch_suspend(struct tb_switch *sw) | ||
| 493 | { | ||
| 494 | int i, err; | ||
| 495 | err = tb_plug_events_active(sw, false); | ||
| 496 | if (err) | ||
| 497 | return; | ||
| 498 | |||
| 499 | for (i = 1; i <= sw->config.max_port_number; i++) { | ||
| 500 | if (!tb_is_upstream_port(&sw->ports[i]) && sw->ports[i].remote) | ||
| 501 | tb_switch_suspend(sw->ports[i].remote->sw); | ||
| 502 | } | ||
| 503 | /* | ||
| 504 | * TODO: invoke tb_cfg_prepare_to_sleep here? does not seem to have any | ||
| 505 | * effect? | ||
| 506 | */ | ||
| 507 | } | ||
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c new file mode 100644 index 000000000000..d2c3fe346e91 --- /dev/null +++ b/drivers/thunderbolt/tb.c | |||
| @@ -0,0 +1,436 @@ | |||
| 1 | /* | ||
| 2 | * Thunderbolt Cactus Ridge driver - bus logic (NHI independent) | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include <linux/slab.h> | ||
| 8 | #include <linux/errno.h> | ||
| 9 | #include <linux/delay.h> | ||
| 10 | |||
| 11 | #include "tb.h" | ||
| 12 | #include "tb_regs.h" | ||
| 13 | #include "tunnel_pci.h" | ||
| 14 | |||
| 15 | |||
| 16 | /* enumeration & hot plug handling */ | ||
| 17 | |||
| 18 | |||
| 19 | static void tb_scan_port(struct tb_port *port); | ||
| 20 | |||
| 21 | /** | ||
| 22 | * tb_scan_switch() - scan for and initialize downstream switches | ||
| 23 | */ | ||
| 24 | static void tb_scan_switch(struct tb_switch *sw) | ||
| 25 | { | ||
| 26 | int i; | ||
| 27 | for (i = 1; i <= sw->config.max_port_number; i++) | ||
| 28 | tb_scan_port(&sw->ports[i]); | ||
| 29 | } | ||
| 30 | |||
| 31 | /** | ||
| 32 | * tb_scan_port() - check for and initialize switches below port | ||
| 33 | */ | ||
| 34 | static void tb_scan_port(struct tb_port *port) | ||
| 35 | { | ||
| 36 | struct tb_switch *sw; | ||
| 37 | if (tb_is_upstream_port(port)) | ||
| 38 | return; | ||
| 39 | if (port->config.type != TB_TYPE_PORT) | ||
| 40 | return; | ||
| 41 | if (port->dual_link_port && port->link_nr) | ||
| 42 | return; /* | ||
| 43 | * Downstream switch is reachable through two ports. | ||
| 44 | * Only scan on the primary port (link_nr == 0). | ||
| 45 | */ | ||
| 46 | if (tb_wait_for_port(port, false) <= 0) | ||
| 47 | return; | ||
| 48 | if (port->remote) { | ||
| 49 | tb_port_WARN(port, "port already has a remote!\n"); | ||
| 50 | return; | ||
| 51 | } | ||
| 52 | sw = tb_switch_alloc(port->sw->tb, tb_downstream_route(port)); | ||
| 53 | if (!sw) | ||
| 54 | return; | ||
| 55 | port->remote = tb_upstream_port(sw); | ||
| 56 | tb_upstream_port(sw)->remote = port; | ||
| 57 | tb_scan_switch(sw); | ||
| 58 | } | ||
| 59 | |||
| 60 | /** | ||
| 61 | * tb_free_invalid_tunnels() - destroy tunnels of devices that have gone away | ||
| 62 | */ | ||
| 63 | static void tb_free_invalid_tunnels(struct tb *tb) | ||
| 64 | { | ||
| 65 | struct tb_pci_tunnel *tunnel; | ||
| 66 | struct tb_pci_tunnel *n; | ||
| 67 | list_for_each_entry_safe(tunnel, n, &tb->tunnel_list, list) | ||
| 68 | { | ||
| 69 | if (tb_pci_is_invalid(tunnel)) { | ||
| 70 | tb_pci_deactivate(tunnel); | ||
| 71 | tb_pci_free(tunnel); | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | /** | ||
| 77 | * tb_free_unplugged_children() - traverse hierarchy and free unplugged switches | ||
| 78 | */ | ||
| 79 | static void tb_free_unplugged_children(struct tb_switch *sw) | ||
| 80 | { | ||
| 81 | int i; | ||
| 82 | for (i = 1; i <= sw->config.max_port_number; i++) { | ||
| 83 | struct tb_port *port = &sw->ports[i]; | ||
| 84 | if (tb_is_upstream_port(port)) | ||
| 85 | continue; | ||
| 86 | if (!port->remote) | ||
| 87 | continue; | ||
| 88 | if (port->remote->sw->is_unplugged) { | ||
| 89 | tb_switch_free(port->remote->sw); | ||
| 90 | port->remote = NULL; | ||
| 91 | } else { | ||
| 92 | tb_free_unplugged_children(port->remote->sw); | ||
| 93 | } | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | |||
| 98 | /** | ||
| 99 | * find_pci_up_port() - return the first PCIe up port on @sw or NULL | ||
| 100 | */ | ||
| 101 | static struct tb_port *tb_find_pci_up_port(struct tb_switch *sw) | ||
| 102 | { | ||
| 103 | int i; | ||
| 104 | for (i = 1; i <= sw->config.max_port_number; i++) | ||
| 105 | if (sw->ports[i].config.type == TB_TYPE_PCIE_UP) | ||
| 106 | return &sw->ports[i]; | ||
| 107 | return NULL; | ||
| 108 | } | ||
| 109 | |||
| 110 | /** | ||
| 111 | * find_unused_down_port() - return the first inactive PCIe down port on @sw | ||
| 112 | */ | ||
| 113 | static struct tb_port *tb_find_unused_down_port(struct tb_switch *sw) | ||
| 114 | { | ||
| 115 | int i; | ||
| 116 | int cap; | ||
| 117 | int res; | ||
| 118 | int data; | ||
| 119 | for (i = 1; i <= sw->config.max_port_number; i++) { | ||
| 120 | if (tb_is_upstream_port(&sw->ports[i])) | ||
| 121 | continue; | ||
| 122 | if (sw->ports[i].config.type != TB_TYPE_PCIE_DOWN) | ||
| 123 | continue; | ||
| 124 | cap = tb_find_cap(&sw->ports[i], TB_CFG_PORT, TB_CAP_PCIE); | ||
| 125 | if (cap <= 0) | ||
| 126 | continue; | ||
| 127 | res = tb_port_read(&sw->ports[i], &data, TB_CFG_PORT, cap, 1); | ||
| 128 | if (res < 0) | ||
| 129 | continue; | ||
| 130 | if (data & 0x80000000) | ||
| 131 | continue; | ||
| 132 | return &sw->ports[i]; | ||
| 133 | } | ||
| 134 | return NULL; | ||
| 135 | } | ||
| 136 | |||
| 137 | /** | ||
| 138 | * tb_activate_pcie_devices() - scan for and activate PCIe devices | ||
| 139 | * | ||
| 140 | * This method is somewhat ad hoc. For now it only supports one device | ||
| 141 | * per port and only devices at depth 1. | ||
| 142 | */ | ||
| 143 | static void tb_activate_pcie_devices(struct tb *tb) | ||
| 144 | { | ||
| 145 | int i; | ||
| 146 | int cap; | ||
| 147 | u32 data; | ||
| 148 | struct tb_switch *sw; | ||
| 149 | struct tb_port *up_port; | ||
| 150 | struct tb_port *down_port; | ||
| 151 | struct tb_pci_tunnel *tunnel; | ||
| 152 | /* scan for pcie devices at depth 1*/ | ||
| 153 | for (i = 1; i <= tb->root_switch->config.max_port_number; i++) { | ||
| 154 | if (tb_is_upstream_port(&tb->root_switch->ports[i])) | ||
| 155 | continue; | ||
| 156 | if (tb->root_switch->ports[i].config.type != TB_TYPE_PORT) | ||
| 157 | continue; | ||
| 158 | if (!tb->root_switch->ports[i].remote) | ||
| 159 | continue; | ||
| 160 | sw = tb->root_switch->ports[i].remote->sw; | ||
| 161 | up_port = tb_find_pci_up_port(sw); | ||
| 162 | if (!up_port) { | ||
| 163 | tb_sw_info(sw, "no PCIe devices found, aborting\n"); | ||
| 164 | continue; | ||
| 165 | } | ||
| 166 | |||
| 167 | /* check whether port is already activated */ | ||
| 168 | cap = tb_find_cap(up_port, TB_CFG_PORT, TB_CAP_PCIE); | ||
| 169 | if (cap <= 0) | ||
| 170 | continue; | ||
| 171 | if (tb_port_read(up_port, &data, TB_CFG_PORT, cap, 1)) | ||
| 172 | continue; | ||
| 173 | if (data & 0x80000000) { | ||
| 174 | tb_port_info(up_port, | ||
| 175 | "PCIe port already activated, aborting\n"); | ||
| 176 | continue; | ||
| 177 | } | ||
| 178 | |||
| 179 | down_port = tb_find_unused_down_port(tb->root_switch); | ||
| 180 | if (!down_port) { | ||
| 181 | tb_port_info(up_port, | ||
| 182 | "All PCIe down ports are occupied, aborting\n"); | ||
| 183 | continue; | ||
| 184 | } | ||
| 185 | tunnel = tb_pci_alloc(tb, up_port, down_port); | ||
| 186 | if (!tunnel) { | ||
| 187 | tb_port_info(up_port, | ||
| 188 | "PCIe tunnel allocation failed, aborting\n"); | ||
| 189 | continue; | ||
| 190 | } | ||
| 191 | |||
| 192 | if (tb_pci_activate(tunnel)) { | ||
| 193 | tb_port_info(up_port, | ||
| 194 | "PCIe tunnel activation failed, aborting\n"); | ||
| 195 | tb_pci_free(tunnel); | ||
| 196 | } | ||
| 197 | |||
| 198 | } | ||
| 199 | } | ||
| 200 | |||
| 201 | /* hotplug handling */ | ||
| 202 | |||
| 203 | struct tb_hotplug_event { | ||
| 204 | struct work_struct work; | ||
| 205 | struct tb *tb; | ||
| 206 | u64 route; | ||
| 207 | u8 port; | ||
| 208 | bool unplug; | ||
| 209 | }; | ||
| 210 | |||
| 211 | /** | ||
| 212 | * tb_handle_hotplug() - handle hotplug event | ||
| 213 | * | ||
| 214 | * Executes on tb->wq. | ||
| 215 | */ | ||
| 216 | static void tb_handle_hotplug(struct work_struct *work) | ||
| 217 | { | ||
| 218 | struct tb_hotplug_event *ev = container_of(work, typeof(*ev), work); | ||
| 219 | struct tb *tb = ev->tb; | ||
| 220 | struct tb_switch *sw; | ||
| 221 | struct tb_port *port; | ||
| 222 | mutex_lock(&tb->lock); | ||
| 223 | if (!tb->hotplug_active) | ||
| 224 | goto out; /* during init, suspend or shutdown */ | ||
| 225 | |||
| 226 | sw = get_switch_at_route(tb->root_switch, ev->route); | ||
| 227 | if (!sw) { | ||
| 228 | tb_warn(tb, | ||
| 229 | "hotplug event from non existent switch %llx:%x (unplug: %d)\n", | ||
| 230 | ev->route, ev->port, ev->unplug); | ||
| 231 | goto out; | ||
| 232 | } | ||
| 233 | if (ev->port > sw->config.max_port_number) { | ||
| 234 | tb_warn(tb, | ||
| 235 | "hotplug event from non existent port %llx:%x (unplug: %d)\n", | ||
| 236 | ev->route, ev->port, ev->unplug); | ||
| 237 | goto out; | ||
| 238 | } | ||
| 239 | port = &sw->ports[ev->port]; | ||
| 240 | if (tb_is_upstream_port(port)) { | ||
| 241 | tb_warn(tb, | ||
| 242 | "hotplug event for upstream port %llx:%x (unplug: %d)\n", | ||
| 243 | ev->route, ev->port, ev->unplug); | ||
| 244 | goto out; | ||
| 245 | } | ||
| 246 | if (ev->unplug) { | ||
| 247 | if (port->remote) { | ||
| 248 | tb_port_info(port, "unplugged\n"); | ||
| 249 | tb_sw_set_unpplugged(port->remote->sw); | ||
| 250 | tb_free_invalid_tunnels(tb); | ||
| 251 | tb_switch_free(port->remote->sw); | ||
| 252 | port->remote = NULL; | ||
| 253 | } else { | ||
| 254 | tb_port_info(port, | ||
| 255 | "got unplug event for disconnected port, ignoring\n"); | ||
| 256 | } | ||
| 257 | } else if (port->remote) { | ||
| 258 | tb_port_info(port, | ||
| 259 | "got plug event for connected port, ignoring\n"); | ||
| 260 | } else { | ||
| 261 | tb_port_info(port, "hotplug: scanning\n"); | ||
| 262 | tb_scan_port(port); | ||
| 263 | if (!port->remote) { | ||
| 264 | tb_port_info(port, "hotplug: no switch found\n"); | ||
| 265 | } else if (port->remote->sw->config.depth > 1) { | ||
| 266 | tb_sw_warn(port->remote->sw, | ||
| 267 | "hotplug: chaining not supported\n"); | ||
| 268 | } else { | ||
| 269 | tb_sw_info(port->remote->sw, | ||
| 270 | "hotplug: activating pcie devices\n"); | ||
| 271 | tb_activate_pcie_devices(tb); | ||
| 272 | } | ||
| 273 | } | ||
| 274 | out: | ||
| 275 | mutex_unlock(&tb->lock); | ||
| 276 | kfree(ev); | ||
| 277 | } | ||
| 278 | |||
| 279 | /** | ||
| 280 | * tb_schedule_hotplug_handler() - callback function for the control channel | ||
| 281 | * | ||
| 282 | * Delegates to tb_handle_hotplug. | ||
| 283 | */ | ||
| 284 | static void tb_schedule_hotplug_handler(void *data, u64 route, u8 port, | ||
| 285 | bool unplug) | ||
| 286 | { | ||
| 287 | struct tb *tb = data; | ||
| 288 | struct tb_hotplug_event *ev = kmalloc(sizeof(*ev), GFP_KERNEL); | ||
| 289 | if (!ev) | ||
| 290 | return; | ||
| 291 | INIT_WORK(&ev->work, tb_handle_hotplug); | ||
| 292 | ev->tb = tb; | ||
| 293 | ev->route = route; | ||
| 294 | ev->port = port; | ||
| 295 | ev->unplug = unplug; | ||
| 296 | queue_work(tb->wq, &ev->work); | ||
| 297 | } | ||
| 298 | |||
| 299 | /** | ||
| 300 | * thunderbolt_shutdown_and_free() - shutdown everything | ||
| 301 | * | ||
| 302 | * Free all switches and the config channel. | ||
| 303 | * | ||
| 304 | * Used in the error path of thunderbolt_alloc_and_start. | ||
| 305 | */ | ||
| 306 | void thunderbolt_shutdown_and_free(struct tb *tb) | ||
| 307 | { | ||
| 308 | struct tb_pci_tunnel *tunnel; | ||
| 309 | struct tb_pci_tunnel *n; | ||
| 310 | |||
| 311 | mutex_lock(&tb->lock); | ||
| 312 | |||
| 313 | /* tunnels are only present after everything has been initialized */ | ||
| 314 | list_for_each_entry_safe(tunnel, n, &tb->tunnel_list, list) { | ||
| 315 | tb_pci_deactivate(tunnel); | ||
| 316 | tb_pci_free(tunnel); | ||
| 317 | } | ||
| 318 | |||
| 319 | if (tb->root_switch) | ||
| 320 | tb_switch_free(tb->root_switch); | ||
| 321 | tb->root_switch = NULL; | ||
| 322 | |||
| 323 | if (tb->ctl) { | ||
| 324 | tb_ctl_stop(tb->ctl); | ||
| 325 | tb_ctl_free(tb->ctl); | ||
| 326 | } | ||
| 327 | tb->ctl = NULL; | ||
| 328 | tb->hotplug_active = false; /* signal tb_handle_hotplug to quit */ | ||
| 329 | |||
| 330 | /* allow tb_handle_hotplug to acquire the lock */ | ||
| 331 | mutex_unlock(&tb->lock); | ||
| 332 | if (tb->wq) { | ||
| 333 | flush_workqueue(tb->wq); | ||
| 334 | destroy_workqueue(tb->wq); | ||
| 335 | tb->wq = NULL; | ||
| 336 | } | ||
| 337 | mutex_destroy(&tb->lock); | ||
| 338 | kfree(tb); | ||
| 339 | } | ||
| 340 | |||
| 341 | /** | ||
| 342 | * thunderbolt_alloc_and_start() - setup the thunderbolt bus | ||
| 343 | * | ||
| 344 | * Allocates a tb_cfg control channel, initializes the root switch, enables | ||
| 345 | * plug events and activates pci devices. | ||
| 346 | * | ||
| 347 | * Return: Returns NULL on error. | ||
| 348 | */ | ||
| 349 | struct tb *thunderbolt_alloc_and_start(struct tb_nhi *nhi) | ||
| 350 | { | ||
| 351 | struct tb *tb; | ||
| 352 | |||
| 353 | BUILD_BUG_ON(sizeof(struct tb_regs_switch_header) != 5 * 4); | ||
| 354 | BUILD_BUG_ON(sizeof(struct tb_regs_port_header) != 8 * 4); | ||
| 355 | BUILD_BUG_ON(sizeof(struct tb_regs_hop) != 2 * 4); | ||
| 356 | |||
| 357 | tb = kzalloc(sizeof(*tb), GFP_KERNEL); | ||
| 358 | if (!tb) | ||
| 359 | return NULL; | ||
| 360 | |||
| 361 | tb->nhi = nhi; | ||
| 362 | mutex_init(&tb->lock); | ||
| 363 | mutex_lock(&tb->lock); | ||
| 364 | INIT_LIST_HEAD(&tb->tunnel_list); | ||
| 365 | |||
| 366 | tb->wq = alloc_ordered_workqueue("thunderbolt", 0); | ||
| 367 | if (!tb->wq) | ||
| 368 | goto err_locked; | ||
| 369 | |||
| 370 | tb->ctl = tb_ctl_alloc(tb->nhi, tb_schedule_hotplug_handler, tb); | ||
| 371 | if (!tb->ctl) | ||
| 372 | goto err_locked; | ||
| 373 | /* | ||
| 374 | * tb_schedule_hotplug_handler may be called as soon as the config | ||
| 375 | * channel is started. Thats why we have to hold the lock here. | ||
| 376 | */ | ||
| 377 | tb_ctl_start(tb->ctl); | ||
| 378 | |||
| 379 | tb->root_switch = tb_switch_alloc(tb, 0); | ||
| 380 | if (!tb->root_switch) | ||
| 381 | goto err_locked; | ||
| 382 | |||
| 383 | /* Full scan to discover devices added before the driver was loaded. */ | ||
| 384 | tb_scan_switch(tb->root_switch); | ||
| 385 | tb_activate_pcie_devices(tb); | ||
| 386 | |||
| 387 | /* Allow tb_handle_hotplug to progress events */ | ||
| 388 | tb->hotplug_active = true; | ||
| 389 | mutex_unlock(&tb->lock); | ||
| 390 | return tb; | ||
| 391 | |||
| 392 | err_locked: | ||
| 393 | mutex_unlock(&tb->lock); | ||
| 394 | thunderbolt_shutdown_and_free(tb); | ||
| 395 | return NULL; | ||
| 396 | } | ||
| 397 | |||
| 398 | void thunderbolt_suspend(struct tb *tb) | ||
| 399 | { | ||
| 400 | tb_info(tb, "suspending...\n"); | ||
| 401 | mutex_lock(&tb->lock); | ||
| 402 | tb_switch_suspend(tb->root_switch); | ||
| 403 | tb_ctl_stop(tb->ctl); | ||
| 404 | tb->hotplug_active = false; /* signal tb_handle_hotplug to quit */ | ||
| 405 | mutex_unlock(&tb->lock); | ||
| 406 | tb_info(tb, "suspend finished\n"); | ||
| 407 | } | ||
| 408 | |||
| 409 | void thunderbolt_resume(struct tb *tb) | ||
| 410 | { | ||
| 411 | struct tb_pci_tunnel *tunnel, *n; | ||
| 412 | tb_info(tb, "resuming...\n"); | ||
| 413 | mutex_lock(&tb->lock); | ||
| 414 | tb_ctl_start(tb->ctl); | ||
| 415 | |||
| 416 | /* remove any pci devices the firmware might have setup */ | ||
| 417 | tb_switch_reset(tb, 0); | ||
| 418 | |||
| 419 | tb_switch_resume(tb->root_switch); | ||
| 420 | tb_free_invalid_tunnels(tb); | ||
| 421 | tb_free_unplugged_children(tb->root_switch); | ||
| 422 | list_for_each_entry_safe(tunnel, n, &tb->tunnel_list, list) | ||
| 423 | tb_pci_restart(tunnel); | ||
| 424 | if (!list_empty(&tb->tunnel_list)) { | ||
| 425 | /* | ||
| 426 | * the pcie links need some time to get going. | ||
| 427 | * 100ms works for me... | ||
| 428 | */ | ||
| 429 | tb_info(tb, "tunnels restarted, sleeping for 100ms\n"); | ||
| 430 | msleep(100); | ||
| 431 | } | ||
| 432 | /* Allow tb_handle_hotplug to progress events */ | ||
| 433 | tb->hotplug_active = true; | ||
| 434 | mutex_unlock(&tb->lock); | ||
| 435 | tb_info(tb, "resume finished\n"); | ||
| 436 | } | ||
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h new file mode 100644 index 000000000000..8b0d7cf2b6d6 --- /dev/null +++ b/drivers/thunderbolt/tb.h | |||
| @@ -0,0 +1,271 @@ | |||
| 1 | /* | ||
| 2 | * Thunderbolt Cactus Ridge driver - bus logic (NHI independent) | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> | ||
| 5 | */ | ||
| 6 | |||
| 7 | #ifndef TB_H_ | ||
| 8 | #define TB_H_ | ||
| 9 | |||
| 10 | #include <linux/pci.h> | ||
| 11 | |||
| 12 | #include "tb_regs.h" | ||
| 13 | #include "ctl.h" | ||
| 14 | |||
| 15 | /** | ||
| 16 | * struct tb_switch - a thunderbolt switch | ||
| 17 | */ | ||
| 18 | struct tb_switch { | ||
| 19 | struct tb_regs_switch_header config; | ||
| 20 | struct tb_port *ports; | ||
| 21 | struct tb *tb; | ||
| 22 | u64 uid; | ||
| 23 | int cap_plug_events; /* offset, zero if not found */ | ||
| 24 | bool is_unplugged; /* unplugged, will go away */ | ||
| 25 | u8 *drom; | ||
| 26 | }; | ||
| 27 | |||
| 28 | /** | ||
| 29 | * struct tb_port - a thunderbolt port, part of a tb_switch | ||
| 30 | */ | ||
| 31 | struct tb_port { | ||
| 32 | struct tb_regs_port_header config; | ||
| 33 | struct tb_switch *sw; | ||
| 34 | struct tb_port *remote; /* remote port, NULL if not connected */ | ||
| 35 | int cap_phy; /* offset, zero if not found */ | ||
| 36 | u8 port; /* port number on switch */ | ||
| 37 | bool disabled; /* disabled by eeprom */ | ||
| 38 | struct tb_port *dual_link_port; | ||
| 39 | u8 link_nr:1; | ||
| 40 | }; | ||
| 41 | |||
| 42 | /** | ||
| 43 | * struct tb_path_hop - routing information for a tb_path | ||
| 44 | * | ||
| 45 | * Hop configuration is always done on the IN port of a switch. | ||
| 46 | * in_port and out_port have to be on the same switch. Packets arriving on | ||
| 47 | * in_port with "hop" = in_hop_index will get routed to through out_port. The | ||
| 48 | * next hop to take (on out_port->remote) is determined by next_hop_index. | ||
| 49 | * | ||
| 50 | * in_counter_index is the index of a counter (in TB_CFG_COUNTERS) on the in | ||
| 51 | * port. | ||
| 52 | */ | ||
| 53 | struct tb_path_hop { | ||
| 54 | struct tb_port *in_port; | ||
| 55 | struct tb_port *out_port; | ||
| 56 | int in_hop_index; | ||
| 57 | int in_counter_index; /* write -1 to disable counters for this hop. */ | ||
| 58 | int next_hop_index; | ||
| 59 | }; | ||
| 60 | |||
| 61 | /** | ||
| 62 | * enum tb_path_port - path options mask | ||
| 63 | */ | ||
| 64 | enum tb_path_port { | ||
| 65 | TB_PATH_NONE = 0, | ||
| 66 | TB_PATH_SOURCE = 1, /* activate on the first hop (out of src) */ | ||
| 67 | TB_PATH_INTERNAL = 2, /* activate on other hops (not the first/last) */ | ||
| 68 | TB_PATH_DESTINATION = 4, /* activate on the last hop (into dst) */ | ||
| 69 | TB_PATH_ALL = 7, | ||
| 70 | }; | ||
| 71 | |||
| 72 | /** | ||
| 73 | * struct tb_path - a unidirectional path between two ports | ||
| 74 | * | ||
| 75 | * A path consists of a number of hops (see tb_path_hop). To establish a PCIe | ||
| 76 | * tunnel two paths have to be created between the two PCIe ports. | ||
| 77 | * | ||
| 78 | */ | ||
| 79 | struct tb_path { | ||
| 80 | struct tb *tb; | ||
| 81 | int nfc_credits; /* non flow controlled credits */ | ||
| 82 | enum tb_path_port ingress_shared_buffer; | ||
| 83 | enum tb_path_port egress_shared_buffer; | ||
| 84 | enum tb_path_port ingress_fc_enable; | ||
| 85 | enum tb_path_port egress_fc_enable; | ||
| 86 | |||
| 87 | int priority:3; | ||
| 88 | int weight:4; | ||
| 89 | bool drop_packages; | ||
| 90 | bool activated; | ||
| 91 | struct tb_path_hop *hops; | ||
| 92 | int path_length; /* number of hops */ | ||
| 93 | }; | ||
| 94 | |||
| 95 | |||
| 96 | /** | ||
| 97 | * struct tb - main thunderbolt bus structure | ||
| 98 | */ | ||
| 99 | struct tb { | ||
| 100 | struct mutex lock; /* | ||
| 101 | * Big lock. Must be held when accessing cfg or | ||
| 102 | * any struct tb_switch / struct tb_port. | ||
| 103 | */ | ||
| 104 | struct tb_nhi *nhi; | ||
| 105 | struct tb_ctl *ctl; | ||
| 106 | struct workqueue_struct *wq; /* ordered workqueue for plug events */ | ||
| 107 | struct tb_switch *root_switch; | ||
| 108 | struct list_head tunnel_list; /* list of active PCIe tunnels */ | ||
| 109 | bool hotplug_active; /* | ||
| 110 | * tb_handle_hotplug will stop progressing plug | ||
| 111 | * events and exit if this is not set (it needs to | ||
| 112 | * acquire the lock one more time). Used to drain | ||
| 113 | * wq after cfg has been paused. | ||
| 114 | */ | ||
| 115 | |||
| 116 | }; | ||
| 117 | |||
| 118 | /* helper functions & macros */ | ||
| 119 | |||
| 120 | /** | ||
| 121 | * tb_upstream_port() - return the upstream port of a switch | ||
| 122 | * | ||
| 123 | * Every switch has an upstream port (for the root switch it is the NHI). | ||
| 124 | * | ||
| 125 | * During switch alloc/init tb_upstream_port()->remote may be NULL, even for | ||
| 126 | * non root switches (on the NHI port remote is always NULL). | ||
| 127 | * | ||
| 128 | * Return: Returns the upstream port of the switch. | ||
| 129 | */ | ||
| 130 | static inline struct tb_port *tb_upstream_port(struct tb_switch *sw) | ||
| 131 | { | ||
| 132 | return &sw->ports[sw->config.upstream_port_number]; | ||
| 133 | } | ||
| 134 | |||
| 135 | static inline u64 tb_route(struct tb_switch *sw) | ||
| 136 | { | ||
| 137 | return ((u64) sw->config.route_hi) << 32 | sw->config.route_lo; | ||
| 138 | } | ||
| 139 | |||
| 140 | static inline int tb_sw_read(struct tb_switch *sw, void *buffer, | ||
| 141 | enum tb_cfg_space space, u32 offset, u32 length) | ||
| 142 | { | ||
| 143 | return tb_cfg_read(sw->tb->ctl, | ||
| 144 | buffer, | ||
| 145 | tb_route(sw), | ||
| 146 | 0, | ||
| 147 | space, | ||
| 148 | offset, | ||
| 149 | length); | ||
| 150 | } | ||
| 151 | |||
| 152 | static inline int tb_sw_write(struct tb_switch *sw, void *buffer, | ||
| 153 | enum tb_cfg_space space, u32 offset, u32 length) | ||
| 154 | { | ||
| 155 | return tb_cfg_write(sw->tb->ctl, | ||
| 156 | buffer, | ||
| 157 | tb_route(sw), | ||
| 158 | 0, | ||
| 159 | space, | ||
| 160 | offset, | ||
| 161 | length); | ||
| 162 | } | ||
| 163 | |||
| 164 | static inline int tb_port_read(struct tb_port *port, void *buffer, | ||
| 165 | enum tb_cfg_space space, u32 offset, u32 length) | ||
| 166 | { | ||
| 167 | return tb_cfg_read(port->sw->tb->ctl, | ||
| 168 | buffer, | ||
| 169 | tb_route(port->sw), | ||
| 170 | port->port, | ||
| 171 | space, | ||
| 172 | offset, | ||
| 173 | length); | ||
| 174 | } | ||
| 175 | |||
| 176 | static inline int tb_port_write(struct tb_port *port, void *buffer, | ||
| 177 | enum tb_cfg_space space, u32 offset, u32 length) | ||
| 178 | { | ||
| 179 | return tb_cfg_write(port->sw->tb->ctl, | ||
| 180 | buffer, | ||
| 181 | tb_route(port->sw), | ||
| 182 | port->port, | ||
| 183 | space, | ||
| 184 | offset, | ||
| 185 | length); | ||
| 186 | } | ||
| 187 | |||
| 188 | #define tb_err(tb, fmt, arg...) dev_err(&(tb)->nhi->pdev->dev, fmt, ## arg) | ||
| 189 | #define tb_WARN(tb, fmt, arg...) dev_WARN(&(tb)->nhi->pdev->dev, fmt, ## arg) | ||
| 190 | #define tb_warn(tb, fmt, arg...) dev_warn(&(tb)->nhi->pdev->dev, fmt, ## arg) | ||
| 191 | #define tb_info(tb, fmt, arg...) dev_info(&(tb)->nhi->pdev->dev, fmt, ## arg) | ||
| 192 | |||
| 193 | |||
| 194 | #define __TB_SW_PRINT(level, sw, fmt, arg...) \ | ||
| 195 | do { \ | ||
| 196 | struct tb_switch *__sw = (sw); \ | ||
| 197 | level(__sw->tb, "%llx: " fmt, \ | ||
| 198 | tb_route(__sw), ## arg); \ | ||
| 199 | } while (0) | ||
| 200 | #define tb_sw_WARN(sw, fmt, arg...) __TB_SW_PRINT(tb_WARN, sw, fmt, ##arg) | ||
| 201 | #define tb_sw_warn(sw, fmt, arg...) __TB_SW_PRINT(tb_warn, sw, fmt, ##arg) | ||
| 202 | #define tb_sw_info(sw, fmt, arg...) __TB_SW_PRINT(tb_info, sw, fmt, ##arg) | ||
| 203 | |||
| 204 | |||
| 205 | #define __TB_PORT_PRINT(level, _port, fmt, arg...) \ | ||
| 206 | do { \ | ||
| 207 | struct tb_port *__port = (_port); \ | ||
| 208 | level(__port->sw->tb, "%llx:%x: " fmt, \ | ||
| 209 | tb_route(__port->sw), __port->port, ## arg); \ | ||
| 210 | } while (0) | ||
| 211 | #define tb_port_WARN(port, fmt, arg...) \ | ||
| 212 | __TB_PORT_PRINT(tb_WARN, port, fmt, ##arg) | ||
| 213 | #define tb_port_warn(port, fmt, arg...) \ | ||
| 214 | __TB_PORT_PRINT(tb_warn, port, fmt, ##arg) | ||
| 215 | #define tb_port_info(port, fmt, arg...) \ | ||
| 216 | __TB_PORT_PRINT(tb_info, port, fmt, ##arg) | ||
| 217 | |||
| 218 | |||
| 219 | struct tb *thunderbolt_alloc_and_start(struct tb_nhi *nhi); | ||
| 220 | void thunderbolt_shutdown_and_free(struct tb *tb); | ||
| 221 | void thunderbolt_suspend(struct tb *tb); | ||
| 222 | void thunderbolt_resume(struct tb *tb); | ||
| 223 | |||
| 224 | struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route); | ||
| 225 | void tb_switch_free(struct tb_switch *sw); | ||
| 226 | void tb_switch_suspend(struct tb_switch *sw); | ||
| 227 | int tb_switch_resume(struct tb_switch *sw); | ||
| 228 | int tb_switch_reset(struct tb *tb, u64 route); | ||
| 229 | void tb_sw_set_unpplugged(struct tb_switch *sw); | ||
| 230 | struct tb_switch *get_switch_at_route(struct tb_switch *sw, u64 route); | ||
| 231 | |||
| 232 | int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged); | ||
| 233 | int tb_port_add_nfc_credits(struct tb_port *port, int credits); | ||
| 234 | int tb_port_clear_counter(struct tb_port *port, int counter); | ||
| 235 | |||
| 236 | int tb_find_cap(struct tb_port *port, enum tb_cfg_space space, enum tb_cap cap); | ||
| 237 | |||
| 238 | struct tb_path *tb_path_alloc(struct tb *tb, int num_hops); | ||
| 239 | void tb_path_free(struct tb_path *path); | ||
| 240 | int tb_path_activate(struct tb_path *path); | ||
| 241 | void tb_path_deactivate(struct tb_path *path); | ||
| 242 | bool tb_path_is_invalid(struct tb_path *path); | ||
| 243 | |||
| 244 | int tb_drom_read(struct tb_switch *sw); | ||
| 245 | int tb_drom_read_uid_only(struct tb_switch *sw, u64 *uid); | ||
| 246 | |||
| 247 | |||
| 248 | static inline int tb_route_length(u64 route) | ||
| 249 | { | ||
| 250 | return (fls64(route) + TB_ROUTE_SHIFT - 1) / TB_ROUTE_SHIFT; | ||
| 251 | } | ||
| 252 | |||
| 253 | static inline bool tb_is_upstream_port(struct tb_port *port) | ||
| 254 | { | ||
| 255 | return port == tb_upstream_port(port->sw); | ||
| 256 | } | ||
| 257 | |||
| 258 | /** | ||
| 259 | * tb_downstream_route() - get route to downstream switch | ||
| 260 | * | ||
| 261 | * Port must not be the upstream port (otherwise a loop is created). | ||
| 262 | * | ||
| 263 | * Return: Returns a route to the switch behind @port. | ||
| 264 | */ | ||
| 265 | static inline u64 tb_downstream_route(struct tb_port *port) | ||
| 266 | { | ||
| 267 | return tb_route(port->sw) | ||
| 268 | | ((u64) port->port << (port->sw->config.depth * 8)); | ||
| 269 | } | ||
| 270 | |||
| 271 | #endif | ||
diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h new file mode 100644 index 000000000000..6577af75d9dc --- /dev/null +++ b/drivers/thunderbolt/tb_regs.h | |||
| @@ -0,0 +1,213 @@ | |||
| 1 | /* | ||
| 2 | * Thunderbolt Cactus Ridge driver - Port/Switch config area registers | ||
| 3 | * | ||
| 4 | * Every thunderbolt device consists (logically) of a switch with multiple | ||
| 5 | * ports. Every port contains up to four config regions (HOPS, PORT, SWITCH, | ||
| 6 | * COUNTERS) which are used to configure the device. | ||
| 7 | * | ||
| 8 | * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> | ||
| 9 | */ | ||
| 10 | |||
| 11 | #ifndef _TB_REGS | ||
| 12 | #define _TB_REGS | ||
| 13 | |||
| 14 | #include <linux/types.h> | ||
| 15 | |||
| 16 | |||
| 17 | #define TB_ROUTE_SHIFT 8 /* number of bits in a port entry of a route */ | ||
| 18 | |||
| 19 | |||
| 20 | /* | ||
| 21 | * TODO: should be 63? But we do not know how to receive frames larger than 256 | ||
| 22 | * bytes at the frame level. (header + checksum = 16, 60*4 = 240) | ||
| 23 | */ | ||
| 24 | #define TB_MAX_CONFIG_RW_LENGTH 60 | ||
| 25 | |||
| 26 | enum tb_cap { | ||
| 27 | TB_CAP_PHY = 0x0001, | ||
| 28 | TB_CAP_TIME1 = 0x0003, | ||
| 29 | TB_CAP_PCIE = 0x0004, | ||
| 30 | TB_CAP_I2C = 0x0005, | ||
| 31 | TB_CAP_PLUG_EVENTS = 0x0105, /* also EEPROM */ | ||
| 32 | TB_CAP_TIME2 = 0x0305, | ||
| 33 | TB_CAL_IECS = 0x0405, | ||
| 34 | TB_CAP_LINK_CONTROLLER = 0x0605, /* also IECS */ | ||
| 35 | }; | ||
| 36 | |||
| 37 | enum tb_port_state { | ||
| 38 | TB_PORT_DISABLED = 0, /* tb_cap_phy.disable == 1 */ | ||
| 39 | TB_PORT_CONNECTING = 1, /* retry */ | ||
| 40 | TB_PORT_UP = 2, | ||
| 41 | TB_PORT_UNPLUGGED = 7, | ||
| 42 | }; | ||
| 43 | |||
| 44 | /* capability headers */ | ||
| 45 | |||
| 46 | struct tb_cap_basic { | ||
| 47 | u8 next; | ||
| 48 | /* enum tb_cap cap:8; prevent "narrower than values of its type" */ | ||
| 49 | u8 cap; /* if cap == 0x05 then we have a extended capability */ | ||
| 50 | } __packed; | ||
| 51 | |||
| 52 | struct tb_cap_extended_short { | ||
| 53 | u8 next; /* if next and length are zero then we have a long cap */ | ||
| 54 | enum tb_cap cap:16; | ||
| 55 | u8 length; | ||
| 56 | } __packed; | ||
| 57 | |||
| 58 | struct tb_cap_extended_long { | ||
| 59 | u8 zero1; | ||
| 60 | enum tb_cap cap:16; | ||
| 61 | u8 zero2; | ||
| 62 | u16 next; | ||
| 63 | u16 length; | ||
| 64 | } __packed; | ||
| 65 | |||
| 66 | /* capabilities */ | ||
| 67 | |||
| 68 | struct tb_cap_link_controller { | ||
| 69 | struct tb_cap_extended_long cap_header; | ||
| 70 | u32 count:4; /* number of link controllers */ | ||
| 71 | u32 unknown1:4; | ||
| 72 | u32 base_offset:8; /* | ||
| 73 | * offset (into this capability) of the configuration | ||
| 74 | * area of the first link controller | ||
| 75 | */ | ||
| 76 | u32 length:12; /* link controller configuration area length */ | ||
| 77 | u32 unknown2:4; /* TODO check that length is correct */ | ||
| 78 | } __packed; | ||
| 79 | |||
| 80 | struct tb_cap_phy { | ||
| 81 | struct tb_cap_basic cap_header; | ||
| 82 | u32 unknown1:16; | ||
| 83 | u32 unknown2:14; | ||
| 84 | bool disable:1; | ||
| 85 | u32 unknown3:11; | ||
| 86 | enum tb_port_state state:4; | ||
| 87 | u32 unknown4:2; | ||
| 88 | } __packed; | ||
| 89 | |||
| 90 | struct tb_eeprom_ctl { | ||
| 91 | bool clock:1; /* send pulse to transfer one bit */ | ||
| 92 | bool access_low:1; /* set to 0 before access */ | ||
| 93 | bool data_out:1; /* to eeprom */ | ||
| 94 | bool data_in:1; /* from eeprom */ | ||
| 95 | bool access_high:1; /* set to 1 before access */ | ||
| 96 | bool not_present:1; /* should be 0 */ | ||
| 97 | bool unknown1:1; | ||
| 98 | bool present:1; /* should be 1 */ | ||
| 99 | u32 unknown2:24; | ||
| 100 | } __packed; | ||
| 101 | |||
| 102 | struct tb_cap_plug_events { | ||
| 103 | struct tb_cap_extended_short cap_header; | ||
| 104 | u32 __unknown1:2; | ||
| 105 | u32 plug_events:5; | ||
| 106 | u32 __unknown2:25; | ||
| 107 | u32 __unknown3; | ||
| 108 | u32 __unknown4; | ||
| 109 | struct tb_eeprom_ctl eeprom_ctl; | ||
| 110 | u32 __unknown5[7]; | ||
| 111 | u32 drom_offset; /* 32 bit register, but eeprom addresses are 16 bit */ | ||
| 112 | } __packed; | ||
| 113 | |||
| 114 | /* device headers */ | ||
| 115 | |||
| 116 | /* Present on port 0 in TB_CFG_SWITCH at address zero. */ | ||
| 117 | struct tb_regs_switch_header { | ||
| 118 | /* DWORD 0 */ | ||
| 119 | u16 vendor_id; | ||
| 120 | u16 device_id; | ||
| 121 | /* DWORD 1 */ | ||
| 122 | u32 first_cap_offset:8; | ||
| 123 | u32 upstream_port_number:6; | ||
| 124 | u32 max_port_number:6; | ||
| 125 | u32 depth:3; | ||
| 126 | u32 __unknown1:1; | ||
| 127 | u32 revision:8; | ||
| 128 | /* DWORD 2 */ | ||
| 129 | u32 route_lo; | ||
| 130 | /* DWORD 3 */ | ||
| 131 | u32 route_hi:31; | ||
| 132 | bool enabled:1; | ||
| 133 | /* DWORD 4 */ | ||
| 134 | u32 plug_events_delay:8; /* | ||
| 135 | * RW, pause between plug events in | ||
| 136 | * milliseconds. Writing 0x00 is interpreted | ||
| 137 | * as 255ms. | ||
| 138 | */ | ||
| 139 | u32 __unknown4:16; | ||
| 140 | u32 thunderbolt_version:8; | ||
| 141 | } __packed; | ||
| 142 | |||
| 143 | enum tb_port_type { | ||
| 144 | TB_TYPE_INACTIVE = 0x000000, | ||
| 145 | TB_TYPE_PORT = 0x000001, | ||
| 146 | TB_TYPE_NHI = 0x000002, | ||
| 147 | /* TB_TYPE_ETHERNET = 0x020000, lower order bits are not known */ | ||
| 148 | /* TB_TYPE_SATA = 0x080000, lower order bits are not known */ | ||
| 149 | TB_TYPE_DP_HDMI_IN = 0x0e0101, | ||
| 150 | TB_TYPE_DP_HDMI_OUT = 0x0e0102, | ||
| 151 | TB_TYPE_PCIE_DOWN = 0x100101, | ||
| 152 | TB_TYPE_PCIE_UP = 0x100102, | ||
| 153 | /* TB_TYPE_USB = 0x200000, lower order bits are not known */ | ||
| 154 | }; | ||
| 155 | |||
| 156 | /* Present on every port in TB_CF_PORT at address zero. */ | ||
| 157 | struct tb_regs_port_header { | ||
| 158 | /* DWORD 0 */ | ||
| 159 | u16 vendor_id; | ||
| 160 | u16 device_id; | ||
| 161 | /* DWORD 1 */ | ||
| 162 | u32 first_cap_offset:8; | ||
| 163 | u32 max_counters:11; | ||
| 164 | u32 __unknown1:5; | ||
| 165 | u32 revision:8; | ||
| 166 | /* DWORD 2 */ | ||
| 167 | enum tb_port_type type:24; | ||
| 168 | u32 thunderbolt_version:8; | ||
| 169 | /* DWORD 3 */ | ||
| 170 | u32 __unknown2:20; | ||
| 171 | u32 port_number:6; | ||
| 172 | u32 __unknown3:6; | ||
| 173 | /* DWORD 4 */ | ||
| 174 | u32 nfc_credits; | ||
| 175 | /* DWORD 5 */ | ||
| 176 | u32 max_in_hop_id:11; | ||
| 177 | u32 max_out_hop_id:11; | ||
| 178 | u32 __unkown4:10; | ||
| 179 | /* DWORD 6 */ | ||
| 180 | u32 __unknown5; | ||
| 181 | /* DWORD 7 */ | ||
| 182 | u32 __unknown6; | ||
| 183 | |||
| 184 | } __packed; | ||
| 185 | |||
| 186 | /* Hop register from TB_CFG_HOPS. 8 byte per entry. */ | ||
| 187 | struct tb_regs_hop { | ||
| 188 | /* DWORD 0 */ | ||
| 189 | u32 next_hop:11; /* | ||
| 190 | * hop to take after sending the packet through | ||
| 191 | * out_port (on the incoming port of the next switch) | ||
| 192 | */ | ||
| 193 | u32 out_port:6; /* next port of the path (on the same switch) */ | ||
| 194 | u32 initial_credits:8; | ||
| 195 | u32 unknown1:6; /* set to zero */ | ||
| 196 | bool enable:1; | ||
| 197 | |||
| 198 | /* DWORD 1 */ | ||
| 199 | u32 weight:4; | ||
| 200 | u32 unknown2:4; /* set to zero */ | ||
| 201 | u32 priority:3; | ||
| 202 | bool drop_packages:1; | ||
| 203 | u32 counter:11; /* index into TB_CFG_COUNTERS on this port */ | ||
| 204 | bool counter_enable:1; | ||
| 205 | bool ingress_fc:1; | ||
| 206 | bool egress_fc:1; | ||
| 207 | bool ingress_shared_buffer:1; | ||
| 208 | bool egress_shared_buffer:1; | ||
| 209 | u32 unknown3:4; /* set to zero */ | ||
| 210 | } __packed; | ||
| 211 | |||
| 212 | |||
| 213 | #endif | ||
diff --git a/drivers/thunderbolt/tunnel_pci.c b/drivers/thunderbolt/tunnel_pci.c new file mode 100644 index 000000000000..baf1cd370446 --- /dev/null +++ b/drivers/thunderbolt/tunnel_pci.c | |||
| @@ -0,0 +1,232 @@ | |||
| 1 | /* | ||
| 2 | * Thunderbolt Cactus Ridge driver - PCIe tunnel | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include <linux/slab.h> | ||
| 8 | #include <linux/list.h> | ||
| 9 | |||
| 10 | #include "tunnel_pci.h" | ||
| 11 | #include "tb.h" | ||
| 12 | |||
| 13 | #define __TB_TUNNEL_PRINT(level, tunnel, fmt, arg...) \ | ||
| 14 | do { \ | ||
| 15 | struct tb_pci_tunnel *__tunnel = (tunnel); \ | ||
| 16 | level(__tunnel->tb, "%llx:%x <-> %llx:%x (PCI): " fmt, \ | ||
| 17 | tb_route(__tunnel->down_port->sw), \ | ||
| 18 | __tunnel->down_port->port, \ | ||
| 19 | tb_route(__tunnel->up_port->sw), \ | ||
| 20 | __tunnel->up_port->port, \ | ||
| 21 | ## arg); \ | ||
| 22 | } while (0) | ||
| 23 | |||
| 24 | #define tb_tunnel_WARN(tunnel, fmt, arg...) \ | ||
| 25 | __TB_TUNNEL_PRINT(tb_WARN, tunnel, fmt, ##arg) | ||
| 26 | #define tb_tunnel_warn(tunnel, fmt, arg...) \ | ||
| 27 | __TB_TUNNEL_PRINT(tb_warn, tunnel, fmt, ##arg) | ||
| 28 | #define tb_tunnel_info(tunnel, fmt, arg...) \ | ||
| 29 | __TB_TUNNEL_PRINT(tb_info, tunnel, fmt, ##arg) | ||
| 30 | |||
| 31 | static void tb_pci_init_path(struct tb_path *path) | ||
| 32 | { | ||
| 33 | path->egress_fc_enable = TB_PATH_SOURCE | TB_PATH_INTERNAL; | ||
| 34 | path->egress_shared_buffer = TB_PATH_NONE; | ||
| 35 | path->ingress_fc_enable = TB_PATH_ALL; | ||
| 36 | path->ingress_shared_buffer = TB_PATH_NONE; | ||
| 37 | path->priority = 3; | ||
| 38 | path->weight = 1; | ||
| 39 | path->drop_packages = 0; | ||
| 40 | path->nfc_credits = 0; | ||
| 41 | } | ||
| 42 | |||
| 43 | /** | ||
| 44 | * tb_pci_alloc() - allocate a pci tunnel | ||
| 45 | * | ||
| 46 | * Allocate a PCI tunnel. The ports must be of type TB_TYPE_PCIE_UP and | ||
| 47 | * TB_TYPE_PCIE_DOWN. | ||
| 48 | * | ||
| 49 | * Currently only paths consisting of two hops are supported (that is the | ||
| 50 | * ports must be on "adjacent" switches). | ||
| 51 | * | ||
| 52 | * The paths are hard-coded to use hop 8 (the only working hop id available on | ||
| 53 | * my thunderbolt devices). Therefore at most ONE path per device may be | ||
| 54 | * activated. | ||
| 55 | * | ||
| 56 | * Return: Returns a tb_pci_tunnel on success or NULL on failure. | ||
| 57 | */ | ||
| 58 | struct tb_pci_tunnel *tb_pci_alloc(struct tb *tb, struct tb_port *up, | ||
| 59 | struct tb_port *down) | ||
| 60 | { | ||
| 61 | struct tb_pci_tunnel *tunnel = kzalloc(sizeof(*tunnel), GFP_KERNEL); | ||
| 62 | if (!tunnel) | ||
| 63 | goto err; | ||
| 64 | tunnel->tb = tb; | ||
| 65 | tunnel->down_port = down; | ||
| 66 | tunnel->up_port = up; | ||
| 67 | INIT_LIST_HEAD(&tunnel->list); | ||
| 68 | tunnel->path_to_up = tb_path_alloc(up->sw->tb, 2); | ||
| 69 | if (!tunnel->path_to_up) | ||
| 70 | goto err; | ||
| 71 | tunnel->path_to_down = tb_path_alloc(up->sw->tb, 2); | ||
| 72 | if (!tunnel->path_to_down) | ||
| 73 | goto err; | ||
| 74 | tb_pci_init_path(tunnel->path_to_up); | ||
| 75 | tb_pci_init_path(tunnel->path_to_down); | ||
| 76 | |||
| 77 | tunnel->path_to_up->hops[0].in_port = down; | ||
| 78 | tunnel->path_to_up->hops[0].in_hop_index = 8; | ||
| 79 | tunnel->path_to_up->hops[0].in_counter_index = -1; | ||
| 80 | tunnel->path_to_up->hops[0].out_port = tb_upstream_port(up->sw)->remote; | ||
| 81 | tunnel->path_to_up->hops[0].next_hop_index = 8; | ||
| 82 | |||
| 83 | tunnel->path_to_up->hops[1].in_port = tb_upstream_port(up->sw); | ||
| 84 | tunnel->path_to_up->hops[1].in_hop_index = 8; | ||
| 85 | tunnel->path_to_up->hops[1].in_counter_index = -1; | ||
| 86 | tunnel->path_to_up->hops[1].out_port = up; | ||
| 87 | tunnel->path_to_up->hops[1].next_hop_index = 8; | ||
| 88 | |||
| 89 | tunnel->path_to_down->hops[0].in_port = up; | ||
| 90 | tunnel->path_to_down->hops[0].in_hop_index = 8; | ||
| 91 | tunnel->path_to_down->hops[0].in_counter_index = -1; | ||
| 92 | tunnel->path_to_down->hops[0].out_port = tb_upstream_port(up->sw); | ||
| 93 | tunnel->path_to_down->hops[0].next_hop_index = 8; | ||
| 94 | |||
| 95 | tunnel->path_to_down->hops[1].in_port = | ||
| 96 | tb_upstream_port(up->sw)->remote; | ||
| 97 | tunnel->path_to_down->hops[1].in_hop_index = 8; | ||
| 98 | tunnel->path_to_down->hops[1].in_counter_index = -1; | ||
| 99 | tunnel->path_to_down->hops[1].out_port = down; | ||
| 100 | tunnel->path_to_down->hops[1].next_hop_index = 8; | ||
| 101 | return tunnel; | ||
| 102 | |||
| 103 | err: | ||
| 104 | if (tunnel) { | ||
| 105 | if (tunnel->path_to_down) | ||
| 106 | tb_path_free(tunnel->path_to_down); | ||
| 107 | if (tunnel->path_to_up) | ||
| 108 | tb_path_free(tunnel->path_to_up); | ||
| 109 | kfree(tunnel); | ||
| 110 | } | ||
| 111 | return NULL; | ||
| 112 | } | ||
| 113 | |||
| 114 | /** | ||
| 115 | * tb_pci_free() - free a tunnel | ||
| 116 | * | ||
| 117 | * The tunnel must have been deactivated. | ||
| 118 | */ | ||
| 119 | void tb_pci_free(struct tb_pci_tunnel *tunnel) | ||
| 120 | { | ||
| 121 | if (tunnel->path_to_up->activated || tunnel->path_to_down->activated) { | ||
| 122 | tb_tunnel_WARN(tunnel, "trying to free an activated tunnel\n"); | ||
| 123 | return; | ||
| 124 | } | ||
| 125 | tb_path_free(tunnel->path_to_up); | ||
| 126 | tb_path_free(tunnel->path_to_down); | ||
| 127 | kfree(tunnel); | ||
| 128 | } | ||
| 129 | |||
| 130 | /** | ||
| 131 | * tb_pci_is_invalid - check whether an activated path is still valid | ||
| 132 | */ | ||
| 133 | bool tb_pci_is_invalid(struct tb_pci_tunnel *tunnel) | ||
| 134 | { | ||
| 135 | WARN_ON(!tunnel->path_to_up->activated); | ||
| 136 | WARN_ON(!tunnel->path_to_down->activated); | ||
| 137 | |||
| 138 | return tb_path_is_invalid(tunnel->path_to_up) | ||
| 139 | || tb_path_is_invalid(tunnel->path_to_down); | ||
| 140 | } | ||
| 141 | |||
| 142 | /** | ||
| 143 | * tb_pci_port_active() - activate/deactivate PCI capability | ||
| 144 | * | ||
| 145 | * Return: Returns 0 on success or an error code on failure. | ||
| 146 | */ | ||
| 147 | static int tb_pci_port_active(struct tb_port *port, bool active) | ||
| 148 | { | ||
| 149 | u32 word = active ? 0x80000000 : 0x0; | ||
| 150 | int cap = tb_find_cap(port, TB_CFG_PORT, TB_CAP_PCIE); | ||
| 151 | if (cap <= 0) { | ||
| 152 | tb_port_warn(port, "TB_CAP_PCIE not found: %d\n", cap); | ||
| 153 | return cap ? cap : -ENXIO; | ||
| 154 | } | ||
| 155 | return tb_port_write(port, &word, TB_CFG_PORT, cap, 1); | ||
| 156 | } | ||
| 157 | |||
| 158 | /** | ||
| 159 | * tb_pci_restart() - activate a tunnel after a hardware reset | ||
| 160 | */ | ||
| 161 | int tb_pci_restart(struct tb_pci_tunnel *tunnel) | ||
| 162 | { | ||
| 163 | int res; | ||
| 164 | tunnel->path_to_up->activated = false; | ||
| 165 | tunnel->path_to_down->activated = false; | ||
| 166 | |||
| 167 | tb_tunnel_info(tunnel, "activating\n"); | ||
| 168 | |||
| 169 | res = tb_path_activate(tunnel->path_to_up); | ||
| 170 | if (res) | ||
| 171 | goto err; | ||
| 172 | res = tb_path_activate(tunnel->path_to_down); | ||
| 173 | if (res) | ||
| 174 | goto err; | ||
| 175 | |||
| 176 | res = tb_pci_port_active(tunnel->down_port, true); | ||
| 177 | if (res) | ||
| 178 | goto err; | ||
| 179 | |||
| 180 | res = tb_pci_port_active(tunnel->up_port, true); | ||
| 181 | if (res) | ||
| 182 | goto err; | ||
| 183 | return 0; | ||
| 184 | err: | ||
| 185 | tb_tunnel_warn(tunnel, "activation failed\n"); | ||
| 186 | tb_pci_deactivate(tunnel); | ||
| 187 | return res; | ||
| 188 | } | ||
| 189 | |||
| 190 | /** | ||
| 191 | * tb_pci_activate() - activate a tunnel | ||
| 192 | * | ||
| 193 | * Return: Returns 0 on success or an error code on failure. | ||
| 194 | */ | ||
| 195 | int tb_pci_activate(struct tb_pci_tunnel *tunnel) | ||
| 196 | { | ||
| 197 | int res; | ||
| 198 | if (tunnel->path_to_up->activated || tunnel->path_to_down->activated) { | ||
| 199 | tb_tunnel_WARN(tunnel, | ||
| 200 | "trying to activate an already activated tunnel\n"); | ||
| 201 | return -EINVAL; | ||
| 202 | } | ||
| 203 | |||
| 204 | res = tb_pci_restart(tunnel); | ||
| 205 | if (res) | ||
| 206 | return res; | ||
| 207 | |||
| 208 | list_add(&tunnel->list, &tunnel->tb->tunnel_list); | ||
| 209 | return 0; | ||
| 210 | } | ||
| 211 | |||
| 212 | |||
| 213 | |||
| 214 | /** | ||
| 215 | * tb_pci_deactivate() - deactivate a tunnel | ||
| 216 | */ | ||
| 217 | void tb_pci_deactivate(struct tb_pci_tunnel *tunnel) | ||
| 218 | { | ||
| 219 | tb_tunnel_info(tunnel, "deactivating\n"); | ||
| 220 | /* | ||
| 221 | * TODO: enable reset by writing 0x04000000 to TB_CAP_PCIE + 1 on up | ||
| 222 | * port. Seems to have no effect? | ||
| 223 | */ | ||
| 224 | tb_pci_port_active(tunnel->up_port, false); | ||
| 225 | tb_pci_port_active(tunnel->down_port, false); | ||
| 226 | if (tunnel->path_to_down->activated) | ||
| 227 | tb_path_deactivate(tunnel->path_to_down); | ||
| 228 | if (tunnel->path_to_up->activated) | ||
| 229 | tb_path_deactivate(tunnel->path_to_up); | ||
| 230 | list_del_init(&tunnel->list); | ||
| 231 | } | ||
| 232 | |||
diff --git a/drivers/thunderbolt/tunnel_pci.h b/drivers/thunderbolt/tunnel_pci.h new file mode 100644 index 000000000000..a67f93c140fa --- /dev/null +++ b/drivers/thunderbolt/tunnel_pci.h | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | /* | ||
| 2 | * Thunderbolt Cactus Ridge driver - PCIe tunnel | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> | ||
| 5 | */ | ||
| 6 | |||
| 7 | #ifndef TB_PCI_H_ | ||
| 8 | #define TB_PCI_H_ | ||
| 9 | |||
| 10 | #include "tb.h" | ||
| 11 | |||
| 12 | struct tb_pci_tunnel { | ||
| 13 | struct tb *tb; | ||
| 14 | struct tb_port *up_port; | ||
| 15 | struct tb_port *down_port; | ||
| 16 | struct tb_path *path_to_up; | ||
| 17 | struct tb_path *path_to_down; | ||
| 18 | struct list_head list; | ||
| 19 | }; | ||
| 20 | |||
| 21 | struct tb_pci_tunnel *tb_pci_alloc(struct tb *tb, struct tb_port *up, | ||
| 22 | struct tb_port *down); | ||
| 23 | void tb_pci_free(struct tb_pci_tunnel *tunnel); | ||
| 24 | int tb_pci_activate(struct tb_pci_tunnel *tunnel); | ||
| 25 | int tb_pci_restart(struct tb_pci_tunnel *tunnel); | ||
| 26 | void tb_pci_deactivate(struct tb_pci_tunnel *tunnel); | ||
| 27 | bool tb_pci_is_invalid(struct tb_pci_tunnel *tunnel); | ||
| 28 | |||
| 29 | #endif | ||
| 30 | |||
diff --git a/drivers/uio/uio_pruss.c b/drivers/uio/uio_pruss.c index 96c4a19b1918..c28d6e2e3df2 100644 --- a/drivers/uio/uio_pruss.c +++ b/drivers/uio/uio_pruss.c | |||
| @@ -91,8 +91,7 @@ static irqreturn_t pruss_handler(int irq, struct uio_info *info) | |||
| 91 | return IRQ_HANDLED; | 91 | return IRQ_HANDLED; |
| 92 | } | 92 | } |
| 93 | 93 | ||
| 94 | static void pruss_cleanup(struct platform_device *dev, | 94 | static void pruss_cleanup(struct device *dev, struct uio_pruss_dev *gdev) |
| 95 | struct uio_pruss_dev *gdev) | ||
| 96 | { | 95 | { |
| 97 | int cnt; | 96 | int cnt; |
| 98 | struct uio_info *p = gdev->info; | 97 | struct uio_info *p = gdev->info; |
| @@ -103,7 +102,7 @@ static void pruss_cleanup(struct platform_device *dev, | |||
| 103 | } | 102 | } |
| 104 | iounmap(gdev->prussio_vaddr); | 103 | iounmap(gdev->prussio_vaddr); |
| 105 | if (gdev->ddr_vaddr) { | 104 | if (gdev->ddr_vaddr) { |
| 106 | dma_free_coherent(&dev->dev, extram_pool_sz, gdev->ddr_vaddr, | 105 | dma_free_coherent(dev, extram_pool_sz, gdev->ddr_vaddr, |
| 107 | gdev->ddr_paddr); | 106 | gdev->ddr_paddr); |
| 108 | } | 107 | } |
| 109 | if (gdev->sram_vaddr) | 108 | if (gdev->sram_vaddr) |
| @@ -115,13 +114,14 @@ static void pruss_cleanup(struct platform_device *dev, | |||
| 115 | kfree(gdev); | 114 | kfree(gdev); |
| 116 | } | 115 | } |
| 117 | 116 | ||
| 118 | static int pruss_probe(struct platform_device *dev) | 117 | static int pruss_probe(struct platform_device *pdev) |
| 119 | { | 118 | { |
| 120 | struct uio_info *p; | 119 | struct uio_info *p; |
| 121 | struct uio_pruss_dev *gdev; | 120 | struct uio_pruss_dev *gdev; |
| 122 | struct resource *regs_prussio; | 121 | struct resource *regs_prussio; |
| 122 | struct device *dev = &pdev->dev; | ||
| 123 | int ret = -ENODEV, cnt = 0, len; | 123 | int ret = -ENODEV, cnt = 0, len; |
| 124 | struct uio_pruss_pdata *pdata = dev_get_platdata(&dev->dev); | 124 | struct uio_pruss_pdata *pdata = dev_get_platdata(dev); |
| 125 | 125 | ||
| 126 | gdev = kzalloc(sizeof(struct uio_pruss_dev), GFP_KERNEL); | 126 | gdev = kzalloc(sizeof(struct uio_pruss_dev), GFP_KERNEL); |
| 127 | if (!gdev) | 127 | if (!gdev) |
| @@ -132,10 +132,11 @@ static int pruss_probe(struct platform_device *dev) | |||
| 132 | kfree(gdev); | 132 | kfree(gdev); |
| 133 | return -ENOMEM; | 133 | return -ENOMEM; |
| 134 | } | 134 | } |
| 135 | |||
| 135 | /* Power on PRU in case its not done as part of boot-loader */ | 136 | /* Power on PRU in case its not done as part of boot-loader */ |
| 136 | gdev->pruss_clk = clk_get(&dev->dev, "pruss"); | 137 | gdev->pruss_clk = clk_get(dev, "pruss"); |
| 137 | if (IS_ERR(gdev->pruss_clk)) { | 138 | if (IS_ERR(gdev->pruss_clk)) { |
| 138 | dev_err(&dev->dev, "Failed to get clock\n"); | 139 | dev_err(dev, "Failed to get clock\n"); |
| 139 | ret = PTR_ERR(gdev->pruss_clk); | 140 | ret = PTR_ERR(gdev->pruss_clk); |
| 140 | kfree(gdev->info); | 141 | kfree(gdev->info); |
| 141 | kfree(gdev); | 142 | kfree(gdev); |
| @@ -144,14 +145,14 @@ static int pruss_probe(struct platform_device *dev) | |||
| 144 | clk_enable(gdev->pruss_clk); | 145 | clk_enable(gdev->pruss_clk); |
| 145 | } | 146 | } |
| 146 | 147 | ||
| 147 | regs_prussio = platform_get_resource(dev, IORESOURCE_MEM, 0); | 148 | regs_prussio = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 148 | if (!regs_prussio) { | 149 | if (!regs_prussio) { |
| 149 | dev_err(&dev->dev, "No PRUSS I/O resource specified\n"); | 150 | dev_err(dev, "No PRUSS I/O resource specified\n"); |
| 150 | goto out_free; | 151 | goto out_free; |
| 151 | } | 152 | } |
| 152 | 153 | ||
| 153 | if (!regs_prussio->start) { | 154 | if (!regs_prussio->start) { |
| 154 | dev_err(&dev->dev, "Invalid memory resource\n"); | 155 | dev_err(dev, "Invalid memory resource\n"); |
| 155 | goto out_free; | 156 | goto out_free; |
| 156 | } | 157 | } |
| 157 | 158 | ||
| @@ -161,27 +162,27 @@ static int pruss_probe(struct platform_device *dev) | |||
| 161 | (unsigned long)gen_pool_dma_alloc(gdev->sram_pool, | 162 | (unsigned long)gen_pool_dma_alloc(gdev->sram_pool, |
| 162 | sram_pool_sz, &gdev->sram_paddr); | 163 | sram_pool_sz, &gdev->sram_paddr); |
| 163 | if (!gdev->sram_vaddr) { | 164 | if (!gdev->sram_vaddr) { |
| 164 | dev_err(&dev->dev, "Could not allocate SRAM pool\n"); | 165 | dev_err(dev, "Could not allocate SRAM pool\n"); |
| 165 | goto out_free; | 166 | goto out_free; |
| 166 | } | 167 | } |
| 167 | } | 168 | } |
| 168 | 169 | ||
| 169 | gdev->ddr_vaddr = dma_alloc_coherent(&dev->dev, extram_pool_sz, | 170 | gdev->ddr_vaddr = dma_alloc_coherent(dev, extram_pool_sz, |
| 170 | &(gdev->ddr_paddr), GFP_KERNEL | GFP_DMA); | 171 | &(gdev->ddr_paddr), GFP_KERNEL | GFP_DMA); |
| 171 | if (!gdev->ddr_vaddr) { | 172 | if (!gdev->ddr_vaddr) { |
| 172 | dev_err(&dev->dev, "Could not allocate external memory\n"); | 173 | dev_err(dev, "Could not allocate external memory\n"); |
| 173 | goto out_free; | 174 | goto out_free; |
| 174 | } | 175 | } |
| 175 | 176 | ||
| 176 | len = resource_size(regs_prussio); | 177 | len = resource_size(regs_prussio); |
| 177 | gdev->prussio_vaddr = ioremap(regs_prussio->start, len); | 178 | gdev->prussio_vaddr = ioremap(regs_prussio->start, len); |
| 178 | if (!gdev->prussio_vaddr) { | 179 | if (!gdev->prussio_vaddr) { |
| 179 | dev_err(&dev->dev, "Can't remap PRUSS I/O address range\n"); | 180 | dev_err(dev, "Can't remap PRUSS I/O address range\n"); |
| 180 | goto out_free; | 181 | goto out_free; |
| 181 | } | 182 | } |
| 182 | 183 | ||
| 183 | gdev->pintc_base = pdata->pintc_base; | 184 | gdev->pintc_base = pdata->pintc_base; |
| 184 | gdev->hostirq_start = platform_get_irq(dev, 0); | 185 | gdev->hostirq_start = platform_get_irq(pdev, 0); |
| 185 | 186 | ||
| 186 | for (cnt = 0, p = gdev->info; cnt < MAX_PRUSS_EVT; cnt++, p++) { | 187 | for (cnt = 0, p = gdev->info; cnt < MAX_PRUSS_EVT; cnt++, p++) { |
| 187 | p->mem[0].addr = regs_prussio->start; | 188 | p->mem[0].addr = regs_prussio->start; |
| @@ -204,12 +205,12 @@ static int pruss_probe(struct platform_device *dev) | |||
| 204 | p->handler = pruss_handler; | 205 | p->handler = pruss_handler; |
| 205 | p->priv = gdev; | 206 | p->priv = gdev; |
| 206 | 207 | ||
| 207 | ret = uio_register_device(&dev->dev, p); | 208 | ret = uio_register_device(dev, p); |
| 208 | if (ret < 0) | 209 | if (ret < 0) |
| 209 | goto out_free; | 210 | goto out_free; |
| 210 | } | 211 | } |
| 211 | 212 | ||
| 212 | platform_set_drvdata(dev, gdev); | 213 | platform_set_drvdata(pdev, gdev); |
| 213 | return 0; | 214 | return 0; |
| 214 | 215 | ||
| 215 | out_free: | 216 | out_free: |
| @@ -221,7 +222,7 @@ static int pruss_remove(struct platform_device *dev) | |||
| 221 | { | 222 | { |
| 222 | struct uio_pruss_dev *gdev = platform_get_drvdata(dev); | 223 | struct uio_pruss_dev *gdev = platform_get_drvdata(dev); |
| 223 | 224 | ||
| 224 | pruss_cleanup(dev, gdev); | 225 | pruss_cleanup(&dev->dev, gdev); |
| 225 | return 0; | 226 | return 0; |
| 226 | } | 227 | } |
| 227 | 228 | ||
diff --git a/drivers/vme/bridges/vme_ca91cx42.h b/drivers/vme/bridges/vme_ca91cx42.h index 02a7c794db05..d46b12dc3b82 100644 --- a/drivers/vme/bridges/vme_ca91cx42.h +++ b/drivers/vme/bridges/vme_ca91cx42.h | |||
| @@ -360,7 +360,6 @@ static const int CA91CX42_VSI_TO[] = { VSI0_TO, VSI1_TO, VSI2_TO, VSI3_TO, | |||
| 360 | */ | 360 | */ |
| 361 | #define CA91CX42_DCTL_L2V (1<<31) | 361 | #define CA91CX42_DCTL_L2V (1<<31) |
| 362 | #define CA91CX42_DCTL_VDW_M (3<<22) | 362 | #define CA91CX42_DCTL_VDW_M (3<<22) |
| 363 | #define CA91CX42_DCTL_VDW_M (3<<22) | ||
| 364 | #define CA91CX42_DCTL_VDW_D8 0 | 363 | #define CA91CX42_DCTL_VDW_D8 0 |
| 365 | #define CA91CX42_DCTL_VDW_D16 (1<<22) | 364 | #define CA91CX42_DCTL_VDW_D16 (1<<22) |
| 366 | #define CA91CX42_DCTL_VDW_D32 (1<<23) | 365 | #define CA91CX42_DCTL_VDW_D32 (1<<23) |
diff --git a/drivers/w1/masters/ds1wm.c b/drivers/w1/masters/ds1wm.c index 02df3b1381d2..e0b8a4bc73df 100644 --- a/drivers/w1/masters/ds1wm.c +++ b/drivers/w1/masters/ds1wm.c | |||
| @@ -563,7 +563,7 @@ static struct platform_driver ds1wm_driver = { | |||
| 563 | 563 | ||
| 564 | static int __init ds1wm_init(void) | 564 | static int __init ds1wm_init(void) |
| 565 | { | 565 | { |
| 566 | printk("DS1WM w1 busmaster driver - (c) 2004 Szabolcs Gyurko\n"); | 566 | pr_info("DS1WM w1 busmaster driver - (c) 2004 Szabolcs Gyurko\n"); |
| 567 | return platform_driver_register(&ds1wm_driver); | 567 | return platform_driver_register(&ds1wm_driver); |
| 568 | } | 568 | } |
| 569 | 569 | ||
diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index e033491fe308..e76a9b39abb2 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c | |||
| @@ -226,7 +226,7 @@ static int ds2482_wait_1wire_idle(struct ds2482_data *pdev) | |||
| 226 | } | 226 | } |
| 227 | 227 | ||
| 228 | if (retries >= DS2482_WAIT_IDLE_TIMEOUT) | 228 | if (retries >= DS2482_WAIT_IDLE_TIMEOUT) |
| 229 | printk(KERN_ERR "%s: timeout on channel %d\n", | 229 | pr_err("%s: timeout on channel %d\n", |
| 230 | __func__, pdev->channel); | 230 | __func__, pdev->channel); |
| 231 | 231 | ||
| 232 | return temp; | 232 | return temp; |
diff --git a/drivers/w1/masters/ds2490.c b/drivers/w1/masters/ds2490.c index 7404ad3062b7..1de6df87bfa3 100644 --- a/drivers/w1/masters/ds2490.c +++ b/drivers/w1/masters/ds2490.c | |||
| @@ -206,7 +206,7 @@ static int ds_send_control_cmd(struct ds_device *dev, u16 value, u16 index) | |||
| 206 | err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]), | 206 | err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]), |
| 207 | CONTROL_CMD, VENDOR, value, index, NULL, 0, 1000); | 207 | CONTROL_CMD, VENDOR, value, index, NULL, 0, 1000); |
| 208 | if (err < 0) { | 208 | if (err < 0) { |
| 209 | printk(KERN_ERR "Failed to send command control message %x.%x: err=%d.\n", | 209 | pr_err("Failed to send command control message %x.%x: err=%d.\n", |
| 210 | value, index, err); | 210 | value, index, err); |
| 211 | return err; | 211 | return err; |
| 212 | } | 212 | } |
| @@ -221,7 +221,7 @@ static int ds_send_control_mode(struct ds_device *dev, u16 value, u16 index) | |||
| 221 | err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]), | 221 | err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]), |
| 222 | MODE_CMD, VENDOR, value, index, NULL, 0, 1000); | 222 | MODE_CMD, VENDOR, value, index, NULL, 0, 1000); |
| 223 | if (err < 0) { | 223 | if (err < 0) { |
| 224 | printk(KERN_ERR "Failed to send mode control message %x.%x: err=%d.\n", | 224 | pr_err("Failed to send mode control message %x.%x: err=%d.\n", |
| 225 | value, index, err); | 225 | value, index, err); |
| 226 | return err; | 226 | return err; |
| 227 | } | 227 | } |
| @@ -236,7 +236,7 @@ static int ds_send_control(struct ds_device *dev, u16 value, u16 index) | |||
| 236 | err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]), | 236 | err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]), |
| 237 | COMM_CMD, VENDOR, value, index, NULL, 0, 1000); | 237 | COMM_CMD, VENDOR, value, index, NULL, 0, 1000); |
| 238 | if (err < 0) { | 238 | if (err < 0) { |
| 239 | printk(KERN_ERR "Failed to send control message %x.%x: err=%d.\n", | 239 | pr_err("Failed to send control message %x.%x: err=%d.\n", |
| 240 | value, index, err); | 240 | value, index, err); |
| 241 | return err; | 241 | return err; |
| 242 | } | 242 | } |
| @@ -255,7 +255,8 @@ static int ds_recv_status_nodump(struct ds_device *dev, struct ds_status *st, | |||
| 255 | err = usb_interrupt_msg(dev->udev, usb_rcvintpipe(dev->udev, | 255 | err = usb_interrupt_msg(dev->udev, usb_rcvintpipe(dev->udev, |
| 256 | dev->ep[EP_STATUS]), buf, size, &count, 100); | 256 | dev->ep[EP_STATUS]), buf, size, &count, 100); |
| 257 | if (err < 0) { | 257 | if (err < 0) { |
| 258 | printk(KERN_ERR "Failed to read 1-wire data from 0x%x: err=%d.\n", dev->ep[EP_STATUS], err); | 258 | pr_err("Failed to read 1-wire data from 0x%x: err=%d.\n", |
| 259 | dev->ep[EP_STATUS], err); | ||
| 259 | return err; | 260 | return err; |
| 260 | } | 261 | } |
| 261 | 262 | ||
| @@ -267,17 +268,17 @@ static int ds_recv_status_nodump(struct ds_device *dev, struct ds_status *st, | |||
| 267 | 268 | ||
| 268 | static inline void ds_print_msg(unsigned char *buf, unsigned char *str, int off) | 269 | static inline void ds_print_msg(unsigned char *buf, unsigned char *str, int off) |
| 269 | { | 270 | { |
| 270 | printk(KERN_INFO "%45s: %8x\n", str, buf[off]); | 271 | pr_info("%45s: %8x\n", str, buf[off]); |
| 271 | } | 272 | } |
| 272 | 273 | ||
| 273 | static void ds_dump_status(struct ds_device *dev, unsigned char *buf, int count) | 274 | static void ds_dump_status(struct ds_device *dev, unsigned char *buf, int count) |
| 274 | { | 275 | { |
| 275 | int i; | 276 | int i; |
| 276 | 277 | ||
| 277 | printk(KERN_INFO "0x%x: count=%d, status: ", dev->ep[EP_STATUS], count); | 278 | pr_info("0x%x: count=%d, status: ", dev->ep[EP_STATUS], count); |
| 278 | for (i=0; i<count; ++i) | 279 | for (i=0; i<count; ++i) |
| 279 | printk("%02x ", buf[i]); | 280 | pr_info("%02x ", buf[i]); |
| 280 | printk(KERN_INFO "\n"); | 281 | pr_info("\n"); |
| 281 | 282 | ||
| 282 | if (count >= 16) { | 283 | if (count >= 16) { |
| 283 | ds_print_msg(buf, "enable flag", 0); | 284 | ds_print_msg(buf, "enable flag", 0); |
| @@ -305,21 +306,21 @@ static void ds_dump_status(struct ds_device *dev, unsigned char *buf, int count) | |||
| 305 | } | 306 | } |
| 306 | ds_print_msg(buf, "Result Register Value: ", i); | 307 | ds_print_msg(buf, "Result Register Value: ", i); |
| 307 | if (buf[i] & RR_NRS) | 308 | if (buf[i] & RR_NRS) |
| 308 | printk(KERN_INFO "NRS: Reset no presence or ...\n"); | 309 | pr_info("NRS: Reset no presence or ...\n"); |
| 309 | if (buf[i] & RR_SH) | 310 | if (buf[i] & RR_SH) |
| 310 | printk(KERN_INFO "SH: short on reset or set path\n"); | 311 | pr_info("SH: short on reset or set path\n"); |
| 311 | if (buf[i] & RR_APP) | 312 | if (buf[i] & RR_APP) |
| 312 | printk(KERN_INFO "APP: alarming presence on reset\n"); | 313 | pr_info("APP: alarming presence on reset\n"); |
| 313 | if (buf[i] & RR_VPP) | 314 | if (buf[i] & RR_VPP) |
| 314 | printk(KERN_INFO "VPP: 12V expected not seen\n"); | 315 | pr_info("VPP: 12V expected not seen\n"); |
| 315 | if (buf[i] & RR_CMP) | 316 | if (buf[i] & RR_CMP) |
| 316 | printk(KERN_INFO "CMP: compare error\n"); | 317 | pr_info("CMP: compare error\n"); |
| 317 | if (buf[i] & RR_CRC) | 318 | if (buf[i] & RR_CRC) |
| 318 | printk(KERN_INFO "CRC: CRC error detected\n"); | 319 | pr_info("CRC: CRC error detected\n"); |
| 319 | if (buf[i] & RR_RDP) | 320 | if (buf[i] & RR_RDP) |
| 320 | printk(KERN_INFO "RDP: redirected page\n"); | 321 | pr_info("RDP: redirected page\n"); |
| 321 | if (buf[i] & RR_EOS) | 322 | if (buf[i] & RR_EOS) |
| 322 | printk(KERN_INFO "EOS: end of search error\n"); | 323 | pr_info("EOS: end of search error\n"); |
| 323 | } | 324 | } |
| 324 | } | 325 | } |
| 325 | 326 | ||
| @@ -330,15 +331,13 @@ static void ds_reset_device(struct ds_device *dev) | |||
| 330 | * the strong pullup. | 331 | * the strong pullup. |
| 331 | */ | 332 | */ |
| 332 | if (ds_send_control_mode(dev, MOD_PULSE_EN, PULSE_SPUE)) | 333 | if (ds_send_control_mode(dev, MOD_PULSE_EN, PULSE_SPUE)) |
| 333 | printk(KERN_ERR "ds_reset_device: " | 334 | pr_err("ds_reset_device: Error allowing strong pullup\n"); |
| 334 | "Error allowing strong pullup\n"); | ||
| 335 | /* Chip strong pullup time was cleared. */ | 335 | /* Chip strong pullup time was cleared. */ |
| 336 | if (dev->spu_sleep) { | 336 | if (dev->spu_sleep) { |
| 337 | /* lower 4 bits are 0, see ds_set_pullup */ | 337 | /* lower 4 bits are 0, see ds_set_pullup */ |
| 338 | u8 del = dev->spu_sleep>>4; | 338 | u8 del = dev->spu_sleep>>4; |
| 339 | if (ds_send_control(dev, COMM_SET_DURATION | COMM_IM, del)) | 339 | if (ds_send_control(dev, COMM_SET_DURATION | COMM_IM, del)) |
| 340 | printk(KERN_ERR "ds_reset_device: " | 340 | pr_err("ds_reset_device: Error setting duration\n"); |
| 341 | "Error setting duration\n"); | ||
| 342 | } | 341 | } |
| 343 | } | 342 | } |
| 344 | 343 | ||
| @@ -363,7 +362,7 @@ static int ds_recv_data(struct ds_device *dev, unsigned char *buf, int size) | |||
| 363 | u8 buf[ST_SIZE]; | 362 | u8 buf[ST_SIZE]; |
| 364 | int count; | 363 | int count; |
| 365 | 364 | ||
| 366 | printk(KERN_INFO "Clearing ep0x%x.\n", dev->ep[EP_DATA_IN]); | 365 | pr_info("Clearing ep0x%x.\n", dev->ep[EP_DATA_IN]); |
| 367 | usb_clear_halt(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN])); | 366 | usb_clear_halt(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN])); |
| 368 | 367 | ||
| 369 | count = ds_recv_status_nodump(dev, &st, buf, sizeof(buf)); | 368 | count = ds_recv_status_nodump(dev, &st, buf, sizeof(buf)); |
| @@ -391,7 +390,7 @@ static int ds_send_data(struct ds_device *dev, unsigned char *buf, int len) | |||
| 391 | count = 0; | 390 | count = 0; |
| 392 | err = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, dev->ep[EP_DATA_OUT]), buf, len, &count, 1000); | 391 | err = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, dev->ep[EP_DATA_OUT]), buf, len, &count, 1000); |
| 393 | if (err < 0) { | 392 | if (err < 0) { |
| 394 | printk(KERN_ERR "Failed to write 1-wire data to ep0x%x: " | 393 | pr_err("Failed to write 1-wire data to ep0x%x: " |
| 395 | "err=%d.\n", dev->ep[EP_DATA_OUT], err); | 394 | "err=%d.\n", dev->ep[EP_DATA_OUT], err); |
| 396 | return err; | 395 | return err; |
| 397 | } | 396 | } |
| @@ -475,7 +474,7 @@ static int ds_wait_status(struct ds_device *dev, struct ds_status *st) | |||
| 475 | } while (!(st->status & ST_IDLE) && !(err < 0) && ++count < 100); | 474 | } while (!(st->status & ST_IDLE) && !(err < 0) && ++count < 100); |
| 476 | 475 | ||
| 477 | if (err >= 16 && st->status & ST_EPOF) { | 476 | if (err >= 16 && st->status & ST_EPOF) { |
| 478 | printk(KERN_INFO "Resetting device after ST_EPOF.\n"); | 477 | pr_info("Resetting device after ST_EPOF.\n"); |
| 479 | ds_reset_device(dev); | 478 | ds_reset_device(dev); |
| 480 | /* Always dump the device status. */ | 479 | /* Always dump the device status. */ |
| 481 | count = 101; | 480 | count = 101; |
| @@ -992,7 +991,7 @@ static int ds_probe(struct usb_interface *intf, | |||
| 992 | 991 | ||
| 993 | dev = kzalloc(sizeof(struct ds_device), GFP_KERNEL); | 992 | dev = kzalloc(sizeof(struct ds_device), GFP_KERNEL); |
| 994 | if (!dev) { | 993 | if (!dev) { |
| 995 | printk(KERN_INFO "Failed to allocate new DS9490R structure.\n"); | 994 | pr_info("Failed to allocate new DS9490R structure.\n"); |
| 996 | return -ENOMEM; | 995 | return -ENOMEM; |
| 997 | } | 996 | } |
| 998 | dev->udev = usb_get_dev(udev); | 997 | dev->udev = usb_get_dev(udev); |
| @@ -1024,7 +1023,8 @@ static int ds_probe(struct usb_interface *intf, | |||
| 1024 | 1023 | ||
| 1025 | iface_desc = &intf->altsetting[alt]; | 1024 | iface_desc = &intf->altsetting[alt]; |
| 1026 | if (iface_desc->desc.bNumEndpoints != NUM_EP-1) { | 1025 | if (iface_desc->desc.bNumEndpoints != NUM_EP-1) { |
| 1027 | printk(KERN_INFO "Num endpoints=%d. It is not DS9490R.\n", iface_desc->desc.bNumEndpoints); | 1026 | pr_info("Num endpoints=%d. It is not DS9490R.\n", |
| 1027 | iface_desc->desc.bNumEndpoints); | ||
| 1028 | err = -EINVAL; | 1028 | err = -EINVAL; |
| 1029 | goto err_out_clear; | 1029 | goto err_out_clear; |
| 1030 | } | 1030 | } |
diff --git a/drivers/w1/masters/mxc_w1.c b/drivers/w1/masters/mxc_w1.c index a5df5e89d456..da3d0f0ad63c 100644 --- a/drivers/w1/masters/mxc_w1.c +++ b/drivers/w1/masters/mxc_w1.c | |||
| @@ -15,16 +15,13 @@ | |||
| 15 | #include <linux/clk.h> | 15 | #include <linux/clk.h> |
| 16 | #include <linux/delay.h> | 16 | #include <linux/delay.h> |
| 17 | #include <linux/io.h> | 17 | #include <linux/io.h> |
| 18 | #include <linux/jiffies.h> | ||
| 18 | #include <linux/module.h> | 19 | #include <linux/module.h> |
| 19 | #include <linux/platform_device.h> | 20 | #include <linux/platform_device.h> |
| 20 | 21 | ||
| 21 | #include "../w1.h" | 22 | #include "../w1.h" |
| 22 | #include "../w1_int.h" | 23 | #include "../w1_int.h" |
| 23 | 24 | ||
| 24 | /* According to the mx27 Datasheet the reset procedure should take up to about | ||
| 25 | * 1350us. We set the timeout to 500*100us = 50ms for sure */ | ||
| 26 | #define MXC_W1_RESET_TIMEOUT 500 | ||
| 27 | |||
| 28 | /* | 25 | /* |
| 29 | * MXC W1 Register offsets | 26 | * MXC W1 Register offsets |
| 30 | */ | 27 | */ |
| @@ -35,6 +32,7 @@ | |||
| 35 | # define MXC_W1_CONTROL_RPP BIT(7) | 32 | # define MXC_W1_CONTROL_RPP BIT(7) |
| 36 | #define MXC_W1_TIME_DIVIDER 0x02 | 33 | #define MXC_W1_TIME_DIVIDER 0x02 |
| 37 | #define MXC_W1_RESET 0x04 | 34 | #define MXC_W1_RESET 0x04 |
| 35 | # define MXC_W1_RESET_RST BIT(0) | ||
| 38 | 36 | ||
| 39 | struct mxc_w1_device { | 37 | struct mxc_w1_device { |
| 40 | void __iomem *regs; | 38 | void __iomem *regs; |
| @@ -49,24 +47,25 @@ struct mxc_w1_device { | |||
| 49 | */ | 47 | */ |
| 50 | static u8 mxc_w1_ds2_reset_bus(void *data) | 48 | static u8 mxc_w1_ds2_reset_bus(void *data) |
| 51 | { | 49 | { |
| 52 | u8 reg_val; | ||
| 53 | unsigned int timeout_cnt = 0; | ||
| 54 | struct mxc_w1_device *dev = data; | 50 | struct mxc_w1_device *dev = data; |
| 51 | unsigned long timeout; | ||
| 52 | |||
| 53 | writeb(MXC_W1_CONTROL_RPP, dev->regs + MXC_W1_CONTROL); | ||
| 55 | 54 | ||
| 56 | writeb(MXC_W1_CONTROL_RPP, (dev->regs + MXC_W1_CONTROL)); | 55 | /* Wait for reset sequence 511+512us, use 1500us for sure */ |
| 56 | timeout = jiffies + usecs_to_jiffies(1500); | ||
| 57 | 57 | ||
| 58 | while (1) { | 58 | udelay(511 + 512); |
| 59 | reg_val = readb(dev->regs + MXC_W1_CONTROL); | ||
| 60 | 59 | ||
| 61 | if (!(reg_val & MXC_W1_CONTROL_RPP) || | 60 | do { |
| 62 | timeout_cnt > MXC_W1_RESET_TIMEOUT) | 61 | u8 ctrl = readb(dev->regs + MXC_W1_CONTROL); |
| 63 | break; | ||
| 64 | else | ||
| 65 | timeout_cnt++; | ||
| 66 | 62 | ||
| 67 | udelay(100); | 63 | /* PST bit is valid after the RPP bit is self-cleared */ |
| 68 | } | 64 | if (!(ctrl & MXC_W1_CONTROL_RPP)) |
| 69 | return !(reg_val & MXC_W1_CONTROL_PST); | 65 | return !(ctrl & MXC_W1_CONTROL_PST); |
| 66 | } while (time_is_after_jiffies(timeout)); | ||
| 67 | |||
| 68 | return 1; | ||
| 70 | } | 69 | } |
| 71 | 70 | ||
| 72 | /* | 71 | /* |
| @@ -76,22 +75,25 @@ static u8 mxc_w1_ds2_reset_bus(void *data) | |||
| 76 | */ | 75 | */ |
| 77 | static u8 mxc_w1_ds2_touch_bit(void *data, u8 bit) | 76 | static u8 mxc_w1_ds2_touch_bit(void *data, u8 bit) |
| 78 | { | 77 | { |
| 79 | struct mxc_w1_device *mdev = data; | 78 | struct mxc_w1_device *dev = data; |
| 80 | void __iomem *ctrl_addr = mdev->regs + MXC_W1_CONTROL; | 79 | unsigned long timeout; |
| 81 | unsigned int timeout_cnt = 400; /* Takes max. 120us according to | ||
| 82 | * datasheet. | ||
| 83 | */ | ||
| 84 | 80 | ||
| 85 | writeb(MXC_W1_CONTROL_WR(bit), ctrl_addr); | 81 | writeb(MXC_W1_CONTROL_WR(bit), dev->regs + MXC_W1_CONTROL); |
| 86 | 82 | ||
| 87 | while (timeout_cnt--) { | 83 | /* Wait for read/write bit (60us, Max 120us), use 200us for sure */ |
| 88 | if (!(readb(ctrl_addr) & MXC_W1_CONTROL_WR(bit))) | 84 | timeout = jiffies + usecs_to_jiffies(200); |
| 89 | break; | ||
| 90 | 85 | ||
| 91 | udelay(1); | 86 | udelay(60); |
| 92 | } | ||
| 93 | 87 | ||
| 94 | return !!(readb(ctrl_addr) & MXC_W1_CONTROL_RDST); | 88 | do { |
| 89 | u8 ctrl = readb(dev->regs + MXC_W1_CONTROL); | ||
| 90 | |||
| 91 | /* RDST bit is valid after the WR1/RD bit is self-cleared */ | ||
| 92 | if (!(ctrl & MXC_W1_CONTROL_WR(bit))) | ||
| 93 | return !!(ctrl & MXC_W1_CONTROL_RDST); | ||
| 94 | } while (time_is_after_jiffies(timeout)); | ||
| 95 | |||
| 96 | return 0; | ||
| 95 | } | 97 | } |
| 96 | 98 | ||
| 97 | static int mxc_w1_probe(struct platform_device *pdev) | 99 | static int mxc_w1_probe(struct platform_device *pdev) |
| @@ -131,6 +133,10 @@ static int mxc_w1_probe(struct platform_device *pdev) | |||
| 131 | if (err) | 133 | if (err) |
| 132 | return err; | 134 | return err; |
| 133 | 135 | ||
| 136 | /* Software reset 1-Wire module */ | ||
| 137 | writeb(MXC_W1_RESET_RST, mdev->regs + MXC_W1_RESET); | ||
| 138 | writeb(0, mdev->regs + MXC_W1_RESET); | ||
| 139 | |||
| 134 | writeb(clkdiv - 1, mdev->regs + MXC_W1_TIME_DIVIDER); | 140 | writeb(clkdiv - 1, mdev->regs + MXC_W1_TIME_DIVIDER); |
| 135 | 141 | ||
| 136 | mdev->bus_master.data = mdev; | 142 | mdev->bus_master.data = mdev; |
diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index 1cdce80b6abf..cfe74d09932e 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig | |||
| @@ -38,6 +38,14 @@ config W1_SLAVE_DS2413 | |||
| 38 | Say Y here if you want to use a 1-wire | 38 | Say Y here if you want to use a 1-wire |
| 39 | DS2413 Dual Channel Addressable Switch device support | 39 | DS2413 Dual Channel Addressable Switch device support |
| 40 | 40 | ||
| 41 | config W1_SLAVE_DS2406 | ||
| 42 | tristate "Dual Channel Addressable Switch 0x12 family support (DS2406)" | ||
| 43 | select CRC16 | ||
| 44 | help | ||
| 45 | Say Y or M here if you want to use a 1-wire | ||
| 46 | DS2406 Dual Channel Addressable Switch. EPROM read/write | ||
| 47 | support for these devices is not implemented. | ||
| 48 | |||
| 41 | config W1_SLAVE_DS2423 | 49 | config W1_SLAVE_DS2423 |
| 42 | tristate "Counter 1-wire device (DS2423)" | 50 | tristate "Counter 1-wire device (DS2423)" |
| 43 | select CRC16 | 51 | select CRC16 |
diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile index 06529f3157ab..1e9989afe7bf 100644 --- a/drivers/w1/slaves/Makefile +++ b/drivers/w1/slaves/Makefile | |||
| @@ -6,6 +6,7 @@ obj-$(CONFIG_W1_SLAVE_THERM) += w1_therm.o | |||
| 6 | obj-$(CONFIG_W1_SLAVE_SMEM) += w1_smem.o | 6 | obj-$(CONFIG_W1_SLAVE_SMEM) += w1_smem.o |
| 7 | obj-$(CONFIG_W1_SLAVE_DS2408) += w1_ds2408.o | 7 | obj-$(CONFIG_W1_SLAVE_DS2408) += w1_ds2408.o |
| 8 | obj-$(CONFIG_W1_SLAVE_DS2413) += w1_ds2413.o | 8 | obj-$(CONFIG_W1_SLAVE_DS2413) += w1_ds2413.o |
| 9 | obj-$(CONFIG_W1_SLAVE_DS2406) += w1_ds2406.o | ||
| 9 | obj-$(CONFIG_W1_SLAVE_DS2423) += w1_ds2423.o | 10 | obj-$(CONFIG_W1_SLAVE_DS2423) += w1_ds2423.o |
| 10 | obj-$(CONFIG_W1_SLAVE_DS2431) += w1_ds2431.o | 11 | obj-$(CONFIG_W1_SLAVE_DS2431) += w1_ds2431.o |
| 11 | obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o | 12 | obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o |
diff --git a/drivers/w1/slaves/w1_ds2406.c b/drivers/w1/slaves/w1_ds2406.c new file mode 100644 index 000000000000..d488961a8c90 --- /dev/null +++ b/drivers/w1/slaves/w1_ds2406.c | |||
| @@ -0,0 +1,168 @@ | |||
| 1 | /* | ||
| 2 | * w1_ds2406.c - w1 family 12 (DS2406) driver | ||
| 3 | * based on w1_ds2413.c by Mariusz Bialonczyk <manio@skyboo.net> | ||
| 4 | * | ||
| 5 | * Copyright (c) 2014 Scott Alfter <scott@alfter.us> | ||
| 6 | * | ||
| 7 | * This source code is licensed under the GNU General Public License, | ||
| 8 | * Version 2. See the file COPYING for more details. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/kernel.h> | ||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/moduleparam.h> | ||
| 14 | #include <linux/device.h> | ||
| 15 | #include <linux/types.h> | ||
| 16 | #include <linux/delay.h> | ||
| 17 | #include <linux/slab.h> | ||
| 18 | #include <linux/crc16.h> | ||
| 19 | |||
| 20 | #include "../w1.h" | ||
| 21 | #include "../w1_int.h" | ||
| 22 | #include "../w1_family.h" | ||
| 23 | |||
| 24 | MODULE_LICENSE("GPL"); | ||
| 25 | MODULE_AUTHOR("Scott Alfter <scott@alfter.us>"); | ||
| 26 | MODULE_DESCRIPTION("w1 family 12 driver for DS2406 2 Pin IO"); | ||
| 27 | |||
| 28 | #define W1_F12_FUNC_READ_STATUS 0xAA | ||
| 29 | #define W1_F12_FUNC_WRITE_STATUS 0x55 | ||
| 30 | |||
| 31 | static ssize_t w1_f12_read_state( | ||
| 32 | struct file *filp, struct kobject *kobj, | ||
| 33 | struct bin_attribute *bin_attr, | ||
| 34 | char *buf, loff_t off, size_t count) | ||
| 35 | { | ||
| 36 | u8 w1_buf[6]={W1_F12_FUNC_READ_STATUS, 7, 0, 0, 0, 0}; | ||
| 37 | struct w1_slave *sl = kobj_to_w1_slave(kobj); | ||
| 38 | u16 crc=0; | ||
| 39 | int i; | ||
| 40 | ssize_t rtnval=1; | ||
| 41 | |||
| 42 | if (off != 0) | ||
| 43 | return 0; | ||
| 44 | if (!buf) | ||
| 45 | return -EINVAL; | ||
| 46 | |||
| 47 | mutex_lock(&sl->master->bus_mutex); | ||
| 48 | |||
| 49 | if (w1_reset_select_slave(sl)) { | ||
| 50 | mutex_unlock(&sl->master->bus_mutex); | ||
| 51 | return -EIO; | ||
| 52 | } | ||
| 53 | |||
| 54 | w1_write_block(sl->master, w1_buf, 3); | ||
| 55 | w1_read_block(sl->master, w1_buf+3, 3); | ||
| 56 | for (i=0; i<6; i++) | ||
| 57 | crc=crc16_byte(crc, w1_buf[i]); | ||
| 58 | if (crc==0xb001) /* good read? */ | ||
| 59 | *buf=((w1_buf[3]>>5)&3)|0x30; | ||
| 60 | else | ||
| 61 | rtnval=-EIO; | ||
| 62 | |||
| 63 | mutex_unlock(&sl->master->bus_mutex); | ||
| 64 | |||
| 65 | return rtnval; | ||
| 66 | } | ||
| 67 | |||
| 68 | static ssize_t w1_f12_write_output( | ||
| 69 | struct file *filp, struct kobject *kobj, | ||
| 70 | struct bin_attribute *bin_attr, | ||
| 71 | char *buf, loff_t off, size_t count) | ||
| 72 | { | ||
| 73 | struct w1_slave *sl = kobj_to_w1_slave(kobj); | ||
| 74 | u8 w1_buf[6]={W1_F12_FUNC_WRITE_STATUS, 7, 0, 0, 0, 0}; | ||
| 75 | u16 crc=0; | ||
| 76 | int i; | ||
| 77 | ssize_t rtnval=1; | ||
| 78 | |||
| 79 | if (count != 1 || off != 0) | ||
| 80 | return -EFAULT; | ||
| 81 | |||
| 82 | mutex_lock(&sl->master->bus_mutex); | ||
| 83 | |||
| 84 | if (w1_reset_select_slave(sl)) { | ||
| 85 | mutex_unlock(&sl->master->bus_mutex); | ||
| 86 | return -EIO; | ||
| 87 | } | ||
| 88 | |||
| 89 | w1_buf[3] = (((*buf)&3)<<5)|0x1F; | ||
| 90 | w1_write_block(sl->master, w1_buf, 4); | ||
| 91 | w1_read_block(sl->master, w1_buf+4, 2); | ||
| 92 | for (i=0; i<6; i++) | ||
| 93 | crc=crc16_byte(crc, w1_buf[i]); | ||
| 94 | if (crc==0xb001) /* good read? */ | ||
| 95 | w1_write_8(sl->master, 0xFF); | ||
| 96 | else | ||
| 97 | rtnval=-EIO; | ||
| 98 | |||
| 99 | mutex_unlock(&sl->master->bus_mutex); | ||
| 100 | return rtnval; | ||
| 101 | } | ||
| 102 | |||
| 103 | #define NB_SYSFS_BIN_FILES 2 | ||
| 104 | static struct bin_attribute w1_f12_sysfs_bin_files[NB_SYSFS_BIN_FILES] = { | ||
| 105 | { | ||
| 106 | .attr = { | ||
| 107 | .name = "state", | ||
| 108 | .mode = S_IRUGO, | ||
| 109 | }, | ||
| 110 | .size = 1, | ||
| 111 | .read = w1_f12_read_state, | ||
| 112 | }, | ||
| 113 | { | ||
| 114 | .attr = { | ||
| 115 | .name = "output", | ||
| 116 | .mode = S_IRUGO | S_IWUSR | S_IWGRP, | ||
| 117 | }, | ||
| 118 | .size = 1, | ||
| 119 | .write = w1_f12_write_output, | ||
| 120 | } | ||
| 121 | }; | ||
| 122 | |||
| 123 | static int w1_f12_add_slave(struct w1_slave *sl) | ||
| 124 | { | ||
| 125 | int err = 0; | ||
| 126 | int i; | ||
| 127 | |||
| 128 | for (i = 0; i < NB_SYSFS_BIN_FILES && !err; ++i) | ||
| 129 | err = sysfs_create_bin_file( | ||
| 130 | &sl->dev.kobj, | ||
| 131 | &(w1_f12_sysfs_bin_files[i])); | ||
| 132 | if (err) | ||
| 133 | while (--i >= 0) | ||
| 134 | sysfs_remove_bin_file(&sl->dev.kobj, | ||
| 135 | &(w1_f12_sysfs_bin_files[i])); | ||
| 136 | return err; | ||
| 137 | } | ||
| 138 | |||
| 139 | static void w1_f12_remove_slave(struct w1_slave *sl) | ||
| 140 | { | ||
| 141 | int i; | ||
| 142 | for (i = NB_SYSFS_BIN_FILES - 1; i >= 0; --i) | ||
| 143 | sysfs_remove_bin_file(&sl->dev.kobj, | ||
| 144 | &(w1_f12_sysfs_bin_files[i])); | ||
| 145 | } | ||
| 146 | |||
| 147 | static struct w1_family_ops w1_f12_fops = { | ||
| 148 | .add_slave = w1_f12_add_slave, | ||
| 149 | .remove_slave = w1_f12_remove_slave, | ||
| 150 | }; | ||
| 151 | |||
| 152 | static struct w1_family w1_family_12 = { | ||
| 153 | .fid = W1_FAMILY_DS2406, | ||
| 154 | .fops = &w1_f12_fops, | ||
| 155 | }; | ||
| 156 | |||
| 157 | static int __init w1_f12_init(void) | ||
| 158 | { | ||
| 159 | return w1_register_family(&w1_family_12); | ||
| 160 | } | ||
| 161 | |||
| 162 | static void __exit w1_f12_exit(void) | ||
| 163 | { | ||
| 164 | w1_unregister_family(&w1_family_12); | ||
| 165 | } | ||
| 166 | |||
| 167 | module_init(w1_f12_init); | ||
| 168 | module_exit(w1_f12_exit); | ||
diff --git a/drivers/w1/slaves/w1_ds2760.c b/drivers/w1/slaves/w1_ds2760.c index 65f90dccd60e..d9079d48d112 100644 --- a/drivers/w1/slaves/w1_ds2760.c +++ b/drivers/w1/slaves/w1_ds2760.c | |||
| @@ -181,8 +181,7 @@ static struct w1_family w1_ds2760_family = { | |||
| 181 | 181 | ||
| 182 | static int __init w1_ds2760_init(void) | 182 | static int __init w1_ds2760_init(void) |
| 183 | { | 183 | { |
| 184 | printk(KERN_INFO "1-Wire driver for the DS2760 battery monitor " | 184 | pr_info("1-Wire driver for the DS2760 battery monitor chip - (c) 2004-2005, Szabolcs Gyurko\n"); |
| 185 | " chip - (c) 2004-2005, Szabolcs Gyurko\n"); | ||
| 186 | ida_init(&bat_ida); | 185 | ida_init(&bat_ida); |
| 187 | return w1_register_family(&w1_ds2760_family); | 186 | return w1_register_family(&w1_ds2760_family); |
| 188 | } | 187 | } |
diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 5d7341520544..592f7edc671e 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c | |||
| @@ -1162,28 +1162,26 @@ static int __init w1_init(void) | |||
| 1162 | { | 1162 | { |
| 1163 | int retval; | 1163 | int retval; |
| 1164 | 1164 | ||
| 1165 | printk(KERN_INFO "Driver for 1-wire Dallas network protocol.\n"); | 1165 | pr_info("Driver for 1-wire Dallas network protocol.\n"); |
| 1166 | 1166 | ||
| 1167 | w1_init_netlink(); | 1167 | w1_init_netlink(); |
| 1168 | 1168 | ||
| 1169 | retval = bus_register(&w1_bus_type); | 1169 | retval = bus_register(&w1_bus_type); |
| 1170 | if (retval) { | 1170 | if (retval) { |
| 1171 | printk(KERN_ERR "Failed to register bus. err=%d.\n", retval); | 1171 | pr_err("Failed to register bus. err=%d.\n", retval); |
| 1172 | goto err_out_exit_init; | 1172 | goto err_out_exit_init; |
| 1173 | } | 1173 | } |
| 1174 | 1174 | ||
| 1175 | retval = driver_register(&w1_master_driver); | 1175 | retval = driver_register(&w1_master_driver); |
| 1176 | if (retval) { | 1176 | if (retval) { |
| 1177 | printk(KERN_ERR | 1177 | pr_err("Failed to register master driver. err=%d.\n", |
| 1178 | "Failed to register master driver. err=%d.\n", | ||
| 1179 | retval); | 1178 | retval); |
| 1180 | goto err_out_bus_unregister; | 1179 | goto err_out_bus_unregister; |
| 1181 | } | 1180 | } |
| 1182 | 1181 | ||
| 1183 | retval = driver_register(&w1_slave_driver); | 1182 | retval = driver_register(&w1_slave_driver); |
| 1184 | if (retval) { | 1183 | if (retval) { |
| 1185 | printk(KERN_ERR | 1184 | pr_err("Failed to register slave driver. err=%d.\n", |
| 1186 | "Failed to register slave driver. err=%d.\n", | ||
| 1187 | retval); | 1185 | retval); |
| 1188 | goto err_out_master_unregister; | 1186 | goto err_out_master_unregister; |
| 1189 | } | 1187 | } |
diff --git a/drivers/w1/w1_family.c b/drivers/w1/w1_family.c index 3651ec801f45..1dc3051f7d76 100644 --- a/drivers/w1/w1_family.c +++ b/drivers/w1/w1_family.c | |||
| @@ -87,7 +87,7 @@ void w1_unregister_family(struct w1_family *fent) | |||
| 87 | w1_reconnect_slaves(fent, 0); | 87 | w1_reconnect_slaves(fent, 0); |
| 88 | 88 | ||
| 89 | while (atomic_read(&fent->refcnt)) { | 89 | while (atomic_read(&fent->refcnt)) { |
| 90 | printk(KERN_INFO "Waiting for family %u to become free: refcnt=%d.\n", | 90 | pr_info("Waiting for family %u to become free: refcnt=%d.\n", |
| 91 | fent->fid, atomic_read(&fent->refcnt)); | 91 | fent->fid, atomic_read(&fent->refcnt)); |
| 92 | 92 | ||
| 93 | if (msleep_interruptible(1000)) | 93 | if (msleep_interruptible(1000)) |
diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h index 26ca1343055b..0d18365b61ad 100644 --- a/drivers/w1/w1_family.h +++ b/drivers/w1/w1_family.h | |||
| @@ -40,6 +40,7 @@ | |||
| 40 | #define W1_FAMILY_DS2760 0x30 | 40 | #define W1_FAMILY_DS2760 0x30 |
| 41 | #define W1_FAMILY_DS2780 0x32 | 41 | #define W1_FAMILY_DS2780 0x32 |
| 42 | #define W1_FAMILY_DS2413 0x3A | 42 | #define W1_FAMILY_DS2413 0x3A |
| 43 | #define W1_FAMILY_DS2406 0x12 | ||
| 43 | #define W1_THERM_DS1825 0x3B | 44 | #define W1_THERM_DS1825 0x3B |
| 44 | #define W1_FAMILY_DS2781 0x3D | 45 | #define W1_FAMILY_DS2781 0x3D |
| 45 | #define W1_THERM_DS28EA00 0x42 | 46 | #define W1_THERM_DS28EA00 0x42 |
diff --git a/drivers/w1/w1_int.c b/drivers/w1/w1_int.c index 728039d2efe1..47249a30eae3 100644 --- a/drivers/w1/w1_int.c +++ b/drivers/w1/w1_int.c | |||
| @@ -38,7 +38,7 @@ module_param_named(search_count, w1_search_count, int, 0); | |||
| 38 | static int w1_enable_pullup = 1; | 38 | static int w1_enable_pullup = 1; |
| 39 | module_param_named(enable_pullup, w1_enable_pullup, int, 0); | 39 | module_param_named(enable_pullup, w1_enable_pullup, int, 0); |
| 40 | 40 | ||
| 41 | static struct w1_master * w1_alloc_dev(u32 id, int slave_count, int slave_ttl, | 41 | static struct w1_master *w1_alloc_dev(u32 id, int slave_count, int slave_ttl, |
| 42 | struct device_driver *driver, | 42 | struct device_driver *driver, |
| 43 | struct device *device) | 43 | struct device *device) |
| 44 | { | 44 | { |
| @@ -50,8 +50,7 @@ static struct w1_master * w1_alloc_dev(u32 id, int slave_count, int slave_ttl, | |||
| 50 | */ | 50 | */ |
| 51 | dev = kzalloc(sizeof(struct w1_master) + sizeof(struct w1_bus_master), GFP_KERNEL); | 51 | dev = kzalloc(sizeof(struct w1_master) + sizeof(struct w1_bus_master), GFP_KERNEL); |
| 52 | if (!dev) { | 52 | if (!dev) { |
| 53 | printk(KERN_ERR | 53 | pr_err("Failed to allocate %zd bytes for new w1 device.\n", |
| 54 | "Failed to allocate %zd bytes for new w1 device.\n", | ||
| 55 | sizeof(struct w1_master)); | 54 | sizeof(struct w1_master)); |
| 56 | return NULL; | 55 | return NULL; |
| 57 | } | 56 | } |
| @@ -91,7 +90,7 @@ static struct w1_master * w1_alloc_dev(u32 id, int slave_count, int slave_ttl, | |||
| 91 | 90 | ||
| 92 | err = device_register(&dev->dev); | 91 | err = device_register(&dev->dev); |
| 93 | if (err) { | 92 | if (err) { |
| 94 | printk(KERN_ERR "Failed to register master device. err=%d\n", err); | 93 | pr_err("Failed to register master device. err=%d\n", err); |
| 95 | memset(dev, 0, sizeof(struct w1_master)); | 94 | memset(dev, 0, sizeof(struct w1_master)); |
| 96 | kfree(dev); | 95 | kfree(dev); |
| 97 | dev = NULL; | 96 | dev = NULL; |
| @@ -116,13 +115,13 @@ int w1_add_master_device(struct w1_bus_master *master) | |||
| 116 | struct w1_netlink_msg msg; | 115 | struct w1_netlink_msg msg; |
| 117 | int id, found; | 116 | int id, found; |
| 118 | 117 | ||
| 119 | /* validate minimum functionality */ | 118 | /* validate minimum functionality */ |
| 120 | if (!(master->touch_bit && master->reset_bus) && | 119 | if (!(master->touch_bit && master->reset_bus) && |
| 121 | !(master->write_bit && master->read_bit) && | 120 | !(master->write_bit && master->read_bit) && |
| 122 | !(master->write_byte && master->read_byte && master->reset_bus)) { | 121 | !(master->write_byte && master->read_byte && master->reset_bus)) { |
| 123 | printk(KERN_ERR "w1_add_master_device: invalid function set\n"); | 122 | pr_err("w1_add_master_device: invalid function set\n"); |
| 124 | return(-EINVAL); | 123 | return(-EINVAL); |
| 125 | } | 124 | } |
| 126 | 125 | ||
| 127 | /* Lock until the device is added (or not) to w1_masters. */ | 126 | /* Lock until the device is added (or not) to w1_masters. */ |
| 128 | mutex_lock(&w1_mlock); | 127 | mutex_lock(&w1_mlock); |
| @@ -254,7 +253,7 @@ void w1_remove_master_device(struct w1_bus_master *bm) | |||
| 254 | } | 253 | } |
| 255 | 254 | ||
| 256 | if (!found) { | 255 | if (!found) { |
| 257 | printk(KERN_ERR "Device doesn't exist.\n"); | 256 | pr_err("Device doesn't exist.\n"); |
| 258 | return; | 257 | return; |
| 259 | } | 258 | } |
| 260 | 259 | ||
diff --git a/drivers/w1/w1_log.h b/drivers/w1/w1_log.h index 9c7bd62e6bdc..f9eecff23b8d 100644 --- a/drivers/w1/w1_log.h +++ b/drivers/w1/w1_log.h | |||
| @@ -29,8 +29,8 @@ | |||
| 29 | #else | 29 | #else |
| 30 | # define assert(expr) \ | 30 | # define assert(expr) \ |
| 31 | if(unlikely(!(expr))) { \ | 31 | if(unlikely(!(expr))) { \ |
| 32 | printk(KERN_ERR "Assertion failed! %s,%s,%s,line=%d\n", \ | 32 | pr_err("Assertion failed! %s,%s,%s,line=%d\n", \ |
| 33 | #expr, __FILE__, __func__, __LINE__); \ | 33 | #expr, __FILE__, __func__, __LINE__); \ |
| 34 | } | 34 | } |
| 35 | #endif | 35 | #endif |
| 36 | 36 | ||
diff --git a/drivers/w1/w1_netlink.c b/drivers/w1/w1_netlink.c index 351a2978ba72..dd9656237274 100644 --- a/drivers/w1/w1_netlink.c +++ b/drivers/w1/w1_netlink.c | |||
| @@ -680,8 +680,7 @@ static void w1_cn_callback(struct cn_msg *cn, struct netlink_skb_parms *nsp) | |||
| 680 | if (sl) | 680 | if (sl) |
| 681 | dev = sl->master; | 681 | dev = sl->master; |
| 682 | } else { | 682 | } else { |
| 683 | printk(KERN_NOTICE | 683 | pr_notice("%s: cn: %x.%x, wrong type: %u, len: %u.\n", |
| 684 | "%s: cn: %x.%x, wrong type: %u, len: %u.\n", | ||
| 685 | __func__, cn->id.idx, cn->id.val, | 684 | __func__, cn->id.idx, cn->id.val, |
| 686 | msg->type, msg->len); | 685 | msg->type, msg->len); |
| 687 | err = -EPROTO; | 686 | err = -EPROTO; |
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index c1c0b0cf39b4..5ba0360663a7 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h | |||
| @@ -268,6 +268,9 @@ | |||
| 268 | VMLINUX_SYMBOL(__start_pci_fixups_suspend) = .; \ | 268 | VMLINUX_SYMBOL(__start_pci_fixups_suspend) = .; \ |
| 269 | *(.pci_fixup_suspend) \ | 269 | *(.pci_fixup_suspend) \ |
| 270 | VMLINUX_SYMBOL(__end_pci_fixups_suspend) = .; \ | 270 | VMLINUX_SYMBOL(__end_pci_fixups_suspend) = .; \ |
| 271 | VMLINUX_SYMBOL(__start_pci_fixups_suspend_late) = .; \ | ||
| 272 | *(.pci_fixup_suspend_late) \ | ||
| 273 | VMLINUX_SYMBOL(__end_pci_fixups_suspend_late) = .; \ | ||
| 271 | } \ | 274 | } \ |
| 272 | \ | 275 | \ |
| 273 | /* Built-in firmware blobs */ \ | 276 | /* Built-in firmware blobs */ \ |
diff --git a/include/linux/extcon/sm5502.h b/include/linux/extcon/sm5502.h new file mode 100644 index 000000000000..030526bf8d79 --- /dev/null +++ b/include/linux/extcon/sm5502.h | |||
| @@ -0,0 +1,287 @@ | |||
| 1 | /* | ||
| 2 | * sm5502.h | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014 Samsung Electronics Co., Ltd | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms of the GNU General Public License as published by the | ||
| 8 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 9 | * option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | */ | ||
| 16 | |||
| 17 | #ifndef __LINUX_EXTCON_SM5502_H | ||
| 18 | #define __LINUX_EXTCON_SM5502_H | ||
| 19 | |||
| 20 | enum sm5502_types { | ||
| 21 | TYPE_SM5502, | ||
| 22 | }; | ||
| 23 | |||
| 24 | /* SM5502 registers */ | ||
| 25 | enum sm5502_reg { | ||
| 26 | SM5502_REG_DEVICE_ID = 0x01, | ||
| 27 | SM5502_REG_CONTROL, | ||
| 28 | SM5502_REG_INT1, | ||
| 29 | SM5502_REG_INT2, | ||
| 30 | SM5502_REG_INTMASK1, | ||
| 31 | SM5502_REG_INTMASK2, | ||
| 32 | SM5502_REG_ADC, | ||
| 33 | SM5502_REG_TIMING_SET1, | ||
| 34 | SM5502_REG_TIMING_SET2, | ||
| 35 | SM5502_REG_DEV_TYPE1, | ||
| 36 | SM5502_REG_DEV_TYPE2, | ||
| 37 | SM5502_REG_BUTTON1, | ||
| 38 | SM5502_REG_BUTTON2, | ||
| 39 | SM5502_REG_CAR_KIT_STATUS, | ||
| 40 | SM5502_REG_RSVD1, | ||
| 41 | SM5502_REG_RSVD2, | ||
| 42 | SM5502_REG_RSVD3, | ||
| 43 | SM5502_REG_RSVD4, | ||
| 44 | SM5502_REG_MANUAL_SW1, | ||
| 45 | SM5502_REG_MANUAL_SW2, | ||
| 46 | SM5502_REG_DEV_TYPE3, | ||
| 47 | SM5502_REG_RSVD5, | ||
| 48 | SM5502_REG_RSVD6, | ||
| 49 | SM5502_REG_RSVD7, | ||
| 50 | SM5502_REG_RSVD8, | ||
| 51 | SM5502_REG_RSVD9, | ||
| 52 | SM5502_REG_RESET, | ||
| 53 | SM5502_REG_RSVD10, | ||
| 54 | SM5502_REG_RESERVED_ID1, | ||
| 55 | SM5502_REG_RSVD11, | ||
| 56 | SM5502_REG_RSVD12, | ||
| 57 | SM5502_REG_RESERVED_ID2, | ||
| 58 | SM5502_REG_RSVD13, | ||
| 59 | SM5502_REG_OCP, | ||
| 60 | SM5502_REG_RSVD14, | ||
| 61 | SM5502_REG_RSVD15, | ||
| 62 | SM5502_REG_RSVD16, | ||
| 63 | SM5502_REG_RSVD17, | ||
| 64 | SM5502_REG_RSVD18, | ||
| 65 | SM5502_REG_RSVD19, | ||
| 66 | SM5502_REG_RSVD20, | ||
| 67 | SM5502_REG_RSVD21, | ||
| 68 | SM5502_REG_RSVD22, | ||
| 69 | SM5502_REG_RSVD23, | ||
| 70 | SM5502_REG_RSVD24, | ||
| 71 | SM5502_REG_RSVD25, | ||
| 72 | SM5502_REG_RSVD26, | ||
| 73 | SM5502_REG_RSVD27, | ||
| 74 | SM5502_REG_RSVD28, | ||
| 75 | SM5502_REG_RSVD29, | ||
| 76 | SM5502_REG_RSVD30, | ||
| 77 | SM5502_REG_RSVD31, | ||
| 78 | SM5502_REG_RSVD32, | ||
| 79 | SM5502_REG_RSVD33, | ||
| 80 | SM5502_REG_RSVD34, | ||
| 81 | SM5502_REG_RSVD35, | ||
| 82 | SM5502_REG_RSVD36, | ||
| 83 | SM5502_REG_RESERVED_ID3, | ||
| 84 | |||
| 85 | SM5502_REG_END, | ||
| 86 | }; | ||
| 87 | |||
| 88 | /* Define SM5502 MASK/SHIFT constant */ | ||
| 89 | #define SM5502_REG_DEVICE_ID_VENDOR_SHIFT 0 | ||
| 90 | #define SM5502_REG_DEVICE_ID_VERSION_SHIFT 3 | ||
| 91 | #define SM5502_REG_DEVICE_ID_VENDOR_MASK (0x3 << SM5502_REG_DEVICE_ID_VENDOR_SHIFT) | ||
| 92 | #define SM5502_REG_DEVICE_ID_VERSION_MASK (0x1f << SM5502_REG_DEVICE_ID_VERSION_SHIFT) | ||
| 93 | |||
| 94 | #define SM5502_REG_CONTROL_MASK_INT_SHIFT 0 | ||
| 95 | #define SM5502_REG_CONTROL_WAIT_SHIFT 1 | ||
| 96 | #define SM5502_REG_CONTROL_MANUAL_SW_SHIFT 2 | ||
| 97 | #define SM5502_REG_CONTROL_RAW_DATA_SHIFT 3 | ||
| 98 | #define SM5502_REG_CONTROL_SW_OPEN_SHIFT 4 | ||
| 99 | #define SM5502_REG_CONTROL_MASK_INT_MASK (0x1 << SM5502_REG_CONTROL_MASK_INT_SHIFT) | ||
| 100 | #define SM5502_REG_CONTROL_WAIT_MASK (0x1 << SM5502_REG_CONTROL_WAIT_SHIFT) | ||
| 101 | #define SM5502_REG_CONTROL_MANUAL_SW_MASK (0x1 << SM5502_REG_CONTROL_MANUAL_SW_SHIFT) | ||
| 102 | #define SM5502_REG_CONTROL_RAW_DATA_MASK (0x1 << SM5502_REG_CONTROL_RAW_DATA_SHIFT) | ||
| 103 | #define SM5502_REG_CONTROL_SW_OPEN_MASK (0x1 << SM5502_REG_CONTROL_SW_OPEN_SHIFT) | ||
| 104 | |||
| 105 | #define SM5502_REG_INTM1_ATTACH_SHIFT 0 | ||
| 106 | #define SM5502_REG_INTM1_DETACH_SHIFT 1 | ||
| 107 | #define SM5502_REG_INTM1_KP_SHIFT 2 | ||
| 108 | #define SM5502_REG_INTM1_LKP_SHIFT 3 | ||
| 109 | #define SM5502_REG_INTM1_LKR_SHIFT 4 | ||
| 110 | #define SM5502_REG_INTM1_OVP_EVENT_SHIFT 5 | ||
| 111 | #define SM5502_REG_INTM1_OCP_EVENT_SHIFT 6 | ||
| 112 | #define SM5502_REG_INTM1_OVP_OCP_DIS_SHIFT 7 | ||
| 113 | #define SM5502_REG_INTM1_ATTACH_MASK (0x1 << SM5502_REG_INTM1_ATTACH_SHIFT) | ||
| 114 | #define SM5502_REG_INTM1_DETACH_MASK (0x1 << SM5502_REG_INTM1_DETACH_SHIFT) | ||
| 115 | #define SM5502_REG_INTM1_KP_MASK (0x1 << SM5502_REG_INTM1_KP_SHIFT) | ||
| 116 | #define SM5502_REG_INTM1_LKP_MASK (0x1 << SM5502_REG_INTM1_LKP_SHIFT) | ||
| 117 | #define SM5502_REG_INTM1_LKR_MASK (0x1 << SM5502_REG_INTM1_LKR_SHIFT) | ||
| 118 | #define SM5502_REG_INTM1_OVP_EVENT_MASK (0x1 << SM5502_REG_INTM1_OVP_EVENT_SHIFT) | ||
| 119 | #define SM5502_REG_INTM1_OCP_EVENT_MASK (0x1 << SM5502_REG_INTM1_OCP_EVENT_SHIFT) | ||
| 120 | #define SM5502_REG_INTM1_OVP_OCP_DIS_MASK (0x1 << SM5502_REG_INTM1_OVP_OCP_DIS_SHIFT) | ||
| 121 | |||
| 122 | #define SM5502_REG_INTM2_VBUS_DET_SHIFT 0 | ||
| 123 | #define SM5502_REG_INTM2_REV_ACCE_SHIFT 1 | ||
| 124 | #define SM5502_REG_INTM2_ADC_CHG_SHIFT 2 | ||
| 125 | #define SM5502_REG_INTM2_STUCK_KEY_SHIFT 3 | ||
| 126 | #define SM5502_REG_INTM2_STUCK_KEY_RCV_SHIFT 4 | ||
| 127 | #define SM5502_REG_INTM2_MHL_SHIFT 5 | ||
| 128 | #define SM5502_REG_INTM2_VBUS_DET_MASK (0x1 << SM5502_REG_INTM2_VBUS_DET_SHIFT) | ||
| 129 | #define SM5502_REG_INTM2_REV_ACCE_MASK (0x1 << SM5502_REG_INTM2_REV_ACCE_SHIFT) | ||
| 130 | #define SM5502_REG_INTM2_ADC_CHG_MASK (0x1 << SM5502_REG_INTM2_ADC_CHG_SHIFT) | ||
| 131 | #define SM5502_REG_INTM2_STUCK_KEY_MASK (0x1 << SM5502_REG_INTM2_STUCK_KEY_SHIFT) | ||
| 132 | #define SM5502_REG_INTM2_STUCK_KEY_RCV_MASK (0x1 << SM5502_REG_INTM2_STUCK_KEY_RCV_SHIFT) | ||
| 133 | #define SM5502_REG_INTM2_MHL_MASK (0x1 << SM5502_REG_INTM2_MHL_SHIFT) | ||
| 134 | |||
| 135 | #define SM5502_REG_ADC_SHIFT 0 | ||
| 136 | #define SM5502_REG_ADC_MASK (0x1f << SM5502_REG_ADC_SHIFT) | ||
| 137 | |||
| 138 | #define SM5502_REG_TIMING_SET1_KEY_PRESS_SHIFT 4 | ||
| 139 | #define SM5502_REG_TIMING_SET1_KEY_PRESS_MASK (0xf << SM5502_REG_TIMING_SET1_KEY_PRESS_SHIFT) | ||
| 140 | #define TIMING_KEY_PRESS_100MS 0x0 | ||
| 141 | #define TIMING_KEY_PRESS_200MS 0x1 | ||
| 142 | #define TIMING_KEY_PRESS_300MS 0x2 | ||
| 143 | #define TIMING_KEY_PRESS_400MS 0x3 | ||
| 144 | #define TIMING_KEY_PRESS_500MS 0x4 | ||
| 145 | #define TIMING_KEY_PRESS_600MS 0x5 | ||
| 146 | #define TIMING_KEY_PRESS_700MS 0x6 | ||
| 147 | #define TIMING_KEY_PRESS_800MS 0x7 | ||
| 148 | #define TIMING_KEY_PRESS_900MS 0x8 | ||
| 149 | #define TIMING_KEY_PRESS_1000MS 0x9 | ||
| 150 | #define SM5502_REG_TIMING_SET1_ADC_DET_SHIFT 0 | ||
| 151 | #define SM5502_REG_TIMING_SET1_ADC_DET_MASK (0xf << SM5502_REG_TIMING_SET1_ADC_DET_SHIFT) | ||
| 152 | #define TIMING_ADC_DET_50MS 0x0 | ||
| 153 | #define TIMING_ADC_DET_100MS 0x1 | ||
| 154 | #define TIMING_ADC_DET_150MS 0x2 | ||
| 155 | #define TIMING_ADC_DET_200MS 0x3 | ||
| 156 | #define TIMING_ADC_DET_300MS 0x4 | ||
| 157 | #define TIMING_ADC_DET_400MS 0x5 | ||
| 158 | #define TIMING_ADC_DET_500MS 0x6 | ||
| 159 | #define TIMING_ADC_DET_600MS 0x7 | ||
| 160 | #define TIMING_ADC_DET_700MS 0x8 | ||
| 161 | #define TIMING_ADC_DET_800MS 0x9 | ||
| 162 | #define TIMING_ADC_DET_900MS 0xA | ||
| 163 | #define TIMING_ADC_DET_1000MS 0xB | ||
| 164 | |||
| 165 | #define SM5502_REG_TIMING_SET2_SW_WAIT_SHIFT 4 | ||
| 166 | #define SM5502_REG_TIMING_SET2_SW_WAIT_MASK (0xf << SM5502_REG_TIMING_SET2_SW_WAIT_SHIFT) | ||
| 167 | #define TIMING_SW_WAIT_10MS 0x0 | ||
| 168 | #define TIMING_SW_WAIT_30MS 0x1 | ||
| 169 | #define TIMING_SW_WAIT_50MS 0x2 | ||
| 170 | #define TIMING_SW_WAIT_70MS 0x3 | ||
| 171 | #define TIMING_SW_WAIT_90MS 0x4 | ||
| 172 | #define TIMING_SW_WAIT_110MS 0x5 | ||
| 173 | #define TIMING_SW_WAIT_130MS 0x6 | ||
| 174 | #define TIMING_SW_WAIT_150MS 0x7 | ||
| 175 | #define TIMING_SW_WAIT_170MS 0x8 | ||
| 176 | #define TIMING_SW_WAIT_190MS 0x9 | ||
| 177 | #define TIMING_SW_WAIT_210MS 0xA | ||
| 178 | #define SM5502_REG_TIMING_SET2_LONG_KEY_SHIFT 0 | ||
| 179 | #define SM5502_REG_TIMING_SET2_LONG_KEY_MASK (0xf << SM5502_REG_TIMING_SET2_LONG_KEY_SHIFT) | ||
| 180 | #define TIMING_LONG_KEY_300MS 0x0 | ||
| 181 | #define TIMING_LONG_KEY_400MS 0x1 | ||
| 182 | #define TIMING_LONG_KEY_500MS 0x2 | ||
| 183 | #define TIMING_LONG_KEY_600MS 0x3 | ||
| 184 | #define TIMING_LONG_KEY_700MS 0x4 | ||
| 185 | #define TIMING_LONG_KEY_800MS 0x5 | ||
| 186 | #define TIMING_LONG_KEY_900MS 0x6 | ||
| 187 | #define TIMING_LONG_KEY_1000MS 0x7 | ||
| 188 | #define TIMING_LONG_KEY_1100MS 0x8 | ||
| 189 | #define TIMING_LONG_KEY_1200MS 0x9 | ||
| 190 | #define TIMING_LONG_KEY_1300MS 0xA | ||
| 191 | #define TIMING_LONG_KEY_1400MS 0xB | ||
| 192 | #define TIMING_LONG_KEY_1500MS 0xC | ||
| 193 | |||
| 194 | #define SM5502_REG_DEV_TYPE1_AUDIO_TYPE1_SHIFT 0 | ||
| 195 | #define SM5502_REG_DEV_TYPE1_AUDIO_TYPE2_SHIFT 1 | ||
| 196 | #define SM5502_REG_DEV_TYPE1_USB_SDP_SHIFT 2 | ||
| 197 | #define SM5502_REG_DEV_TYPE1_UART_SHIFT 3 | ||
| 198 | #define SM5502_REG_DEV_TYPE1_CAR_KIT_CHARGER_SHIFT 4 | ||
| 199 | #define SM5502_REG_DEV_TYPE1_USB_CHG_SHIFT 5 | ||
| 200 | #define SM5502_REG_DEV_TYPE1_DEDICATED_CHG_SHIFT 6 | ||
| 201 | #define SM5502_REG_DEV_TYPE1_USB_OTG_SHIFT 7 | ||
| 202 | #define SM5502_REG_DEV_TYPE1_AUDIO_TYPE1_MASK (0x1 << SM5502_REG_DEV_TYPE1_AUDIO_TYPE1_SHIFT) | ||
| 203 | #define SM5502_REG_DEV_TYPE1_AUDIO_TYPE1__MASK (0x1 << SM5502_REG_DEV_TYPE1_AUDIO_TYPE2_SHIFT) | ||
| 204 | #define SM5502_REG_DEV_TYPE1_USB_SDP_MASK (0x1 << SM5502_REG_DEV_TYPE1_USB_SDP_SHIFT) | ||
| 205 | #define SM5502_REG_DEV_TYPE1_UART_MASK (0x1 << SM5502_REG_DEV_TYPE1_UART_SHIFT) | ||
| 206 | #define SM5502_REG_DEV_TYPE1_CAR_KIT_CHARGER_MASK (0x1 << SM5502_REG_DEV_TYPE1_CAR_KIT_CHARGER_SHIFT) | ||
| 207 | #define SM5502_REG_DEV_TYPE1_USB_CHG_MASK (0x1 << SM5502_REG_DEV_TYPE1_USB_CHG_SHIFT) | ||
| 208 | #define SM5502_REG_DEV_TYPE1_DEDICATED_CHG_MASK (0x1 << SM5502_REG_DEV_TYPE1_DEDICATED_CHG_SHIFT) | ||
| 209 | #define SM5502_REG_DEV_TYPE1_USB_OTG_MASK (0x1 << SM5502_REG_DEV_TYPE1_USB_OTG_SHIFT) | ||
| 210 | |||
| 211 | #define SM5502_REG_DEV_TYPE2_JIG_USB_ON_SHIFT 0 | ||
| 212 | #define SM5502_REG_DEV_TYPE2_JIG_USB_OFF_SHIFT 1 | ||
| 213 | #define SM5502_REG_DEV_TYPE2_JIG_UART_ON_SHIFT 2 | ||
| 214 | #define SM5502_REG_DEV_TYPE2_JIG_UART_OFF_SHIFT 3 | ||
| 215 | #define SM5502_REG_DEV_TYPE2_PPD_SHIFT 4 | ||
| 216 | #define SM5502_REG_DEV_TYPE2_TTY_SHIFT 5 | ||
| 217 | #define SM5502_REG_DEV_TYPE2_AV_CABLE_SHIFT 6 | ||
| 218 | #define SM5502_REG_DEV_TYPE2_JIG_USB_ON_MASK (0x1 << SM5502_REG_DEV_TYPE2_JIG_USB_ON_SHIFT) | ||
| 219 | #define SM5502_REG_DEV_TYPE2_JIG_USB_OFF_MASK (0x1 << SM5502_REG_DEV_TYPE2_JIG_USB_OFF_SHIFT) | ||
| 220 | #define SM5502_REG_DEV_TYPE2_JIG_UART_ON_MASK (0x1 << SM5502_REG_DEV_TYPE2_JIG_UART_ON_SHIFT) | ||
| 221 | #define SM5502_REG_DEV_TYPE2_JIG_UART_OFF_MASK (0x1 << SM5502_REG_DEV_TYPE2_JIG_UART_OFF_SHIFT) | ||
| 222 | #define SM5502_REG_DEV_TYPE2_PPD_MASK (0x1 << SM5502_REG_DEV_TYPE2_PPD_SHIFT) | ||
| 223 | #define SM5502_REG_DEV_TYPE2_TTY_MASK (0x1 << SM5502_REG_DEV_TYPE2_TTY_SHIFT) | ||
| 224 | #define SM5502_REG_DEV_TYPE2_AV_CABLE_MASK (0x1 << SM5502_REG_DEV_TYPE2_AV_CABLE_SHIFT) | ||
| 225 | |||
| 226 | #define SM5502_REG_MANUAL_SW1_VBUSIN_SHIFT 0 | ||
| 227 | #define SM5502_REG_MANUAL_SW1_DP_SHIFT 2 | ||
| 228 | #define SM5502_REG_MANUAL_SW1_DM_SHIFT 5 | ||
| 229 | #define SM5502_REG_MANUAL_SW1_VBUSIN_MASK (0x3 << SM5502_REG_MANUAL_SW1_VBUSIN_SHIFT) | ||
| 230 | #define SM5502_REG_MANUAL_SW1_DP_MASK (0x7 << SM5502_REG_MANUAL_SW1_DP_SHIFT) | ||
| 231 | #define SM5502_REG_MANUAL_SW1_DM_MASK (0x7 << SM5502_REG_MANUAL_SW1_DM_SHIFT) | ||
| 232 | #define VBUSIN_SWITCH_OPEN 0x0 | ||
| 233 | #define VBUSIN_SWITCH_VBUSOUT 0x1 | ||
| 234 | #define VBUSIN_SWITCH_MIC 0x2 | ||
| 235 | #define VBUSIN_SWITCH_VBUSOUT_WITH_USB 0x3 | ||
| 236 | #define DM_DP_CON_SWITCH_OPEN 0x0 | ||
| 237 | #define DM_DP_CON_SWITCH_USB 0x1 | ||
| 238 | #define DM_DP_CON_SWITCH_AUDIO 0x2 | ||
| 239 | #define DM_DP_CON_SWITCH_UART 0x3 | ||
| 240 | #define DM_DP_SWITCH_OPEN ((DM_DP_CON_SWITCH_OPEN <<SM5502_REG_MANUAL_SW1_DP_SHIFT) \ | ||
| 241 | | (DM_DP_CON_SWITCH_OPEN <<SM5502_REG_MANUAL_SW1_DM_SHIFT)) | ||
| 242 | #define DM_DP_SWITCH_USB ((DM_DP_CON_SWITCH_USB <<SM5502_REG_MANUAL_SW1_DP_SHIFT) \ | ||
| 243 | | (DM_DP_CON_SWITCH_USB <<SM5502_REG_MANUAL_SW1_DM_SHIFT)) | ||
| 244 | #define DM_DP_SWITCH_AUDIO ((DM_DP_CON_SWITCH_AUDIO <<SM5502_REG_MANUAL_SW1_DP_SHIFT) \ | ||
| 245 | | (DM_DP_CON_SWITCH_AUDIO <<SM5502_REG_MANUAL_SW1_DM_SHIFT)) | ||
| 246 | #define DM_DP_SWITCH_UART ((DM_DP_CON_SWITCH_UART <<SM5502_REG_MANUAL_SW1_DP_SHIFT) \ | ||
| 247 | | (DM_DP_CON_SWITCH_UART <<SM5502_REG_MANUAL_SW1_DM_SHIFT)) | ||
| 248 | |||
| 249 | /* SM5502 Interrupts */ | ||
| 250 | enum sm5502_irq { | ||
| 251 | /* INT1 */ | ||
| 252 | SM5502_IRQ_INT1_ATTACH, | ||
| 253 | SM5502_IRQ_INT1_DETACH, | ||
| 254 | SM5502_IRQ_INT1_KP, | ||
| 255 | SM5502_IRQ_INT1_LKP, | ||
| 256 | SM5502_IRQ_INT1_LKR, | ||
| 257 | SM5502_IRQ_INT1_OVP_EVENT, | ||
| 258 | SM5502_IRQ_INT1_OCP_EVENT, | ||
| 259 | SM5502_IRQ_INT1_OVP_OCP_DIS, | ||
| 260 | |||
| 261 | /* INT2 */ | ||
| 262 | SM5502_IRQ_INT2_VBUS_DET, | ||
| 263 | SM5502_IRQ_INT2_REV_ACCE, | ||
| 264 | SM5502_IRQ_INT2_ADC_CHG, | ||
| 265 | SM5502_IRQ_INT2_STUCK_KEY, | ||
| 266 | SM5502_IRQ_INT2_STUCK_KEY_RCV, | ||
| 267 | SM5502_IRQ_INT2_MHL, | ||
| 268 | |||
| 269 | SM5502_IRQ_NUM, | ||
| 270 | }; | ||
| 271 | |||
| 272 | #define SM5502_IRQ_INT1_ATTACH_MASK BIT(0) | ||
| 273 | #define SM5502_IRQ_INT1_DETACH_MASK BIT(1) | ||
| 274 | #define SM5502_IRQ_INT1_KP_MASK BIT(2) | ||
| 275 | #define SM5502_IRQ_INT1_LKP_MASK BIT(3) | ||
| 276 | #define SM5502_IRQ_INT1_LKR_MASK BIT(4) | ||
| 277 | #define SM5502_IRQ_INT1_OVP_EVENT_MASK BIT(5) | ||
| 278 | #define SM5502_IRQ_INT1_OCP_EVENT_MASK BIT(6) | ||
| 279 | #define SM5502_IRQ_INT1_OVP_OCP_DIS_MASK BIT(7) | ||
| 280 | #define SM5502_IRQ_INT2_VBUS_DET_MASK BIT(0) | ||
| 281 | #define SM5502_IRQ_INT2_REV_ACCE_MASK BIT(1) | ||
| 282 | #define SM5502_IRQ_INT2_ADC_CHG_MASK BIT(2) | ||
| 283 | #define SM5502_IRQ_INT2_STUCK_KEY_MASK BIT(3) | ||
| 284 | #define SM5502_IRQ_INT2_STUCK_KEY_RCV_MASK BIT(4) | ||
| 285 | #define SM5502_IRQ_INT2_MHL_MASK BIT(5) | ||
| 286 | |||
| 287 | #endif /* __LINUX_EXTCON_SM5502_H */ | ||
diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index 12a5c135c746..4578c72c9b86 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h | |||
| @@ -127,6 +127,9 @@ struct arizona_pdata { | |||
| 127 | /** Internal pull on GPIO5 is disabled when used for jack detection */ | 127 | /** Internal pull on GPIO5 is disabled when used for jack detection */ |
| 128 | bool jd_gpio5_nopull; | 128 | bool jd_gpio5_nopull; |
| 129 | 129 | ||
| 130 | /** set to true if jackdet contact opens on insert */ | ||
| 131 | bool jd_invert; | ||
| 132 | |||
| 130 | /** Use the headphone detect circuit to identify the accessory */ | 133 | /** Use the headphone detect circuit to identify the accessory */ |
| 131 | bool hpdet_acc_id; | 134 | bool hpdet_acc_id; |
| 132 | 135 | ||
diff --git a/include/linux/mfd/max77693-private.h b/include/linux/mfd/max77693-private.h index 3e050b933dd0..c466ff3e16b8 100644 --- a/include/linux/mfd/max77693-private.h +++ b/include/linux/mfd/max77693-private.h | |||
| @@ -262,6 +262,41 @@ enum max77693_irq_source { | |||
| 262 | MAX77693_IRQ_GROUP_NR, | 262 | MAX77693_IRQ_GROUP_NR, |
| 263 | }; | 263 | }; |
| 264 | 264 | ||
| 265 | #define LED_IRQ_FLED2_OPEN BIT(0) | ||
| 266 | #define LED_IRQ_FLED2_SHORT BIT(1) | ||
| 267 | #define LED_IRQ_FLED1_OPEN BIT(2) | ||
| 268 | #define LED_IRQ_FLED1_SHORT BIT(3) | ||
| 269 | #define LED_IRQ_MAX_FLASH BIT(4) | ||
| 270 | |||
| 271 | #define TOPSYS_IRQ_T120C_INT BIT(0) | ||
| 272 | #define TOPSYS_IRQ_T140C_INT BIT(1) | ||
| 273 | #define TOPSYS_IRQ_LOWSYS_INT BIT(3) | ||
| 274 | |||
| 275 | #define CHG_IRQ_BYP_I BIT(0) | ||
| 276 | #define CHG_IRQ_THM_I BIT(2) | ||
| 277 | #define CHG_IRQ_BAT_I BIT(3) | ||
| 278 | #define CHG_IRQ_CHG_I BIT(4) | ||
| 279 | #define CHG_IRQ_CHGIN_I BIT(6) | ||
| 280 | |||
| 281 | #define MUIC_IRQ_INT1_ADC BIT(0) | ||
| 282 | #define MUIC_IRQ_INT1_ADC_LOW BIT(1) | ||
| 283 | #define MUIC_IRQ_INT1_ADC_ERR BIT(2) | ||
| 284 | #define MUIC_IRQ_INT1_ADC1K BIT(3) | ||
| 285 | |||
| 286 | #define MUIC_IRQ_INT2_CHGTYP BIT(0) | ||
| 287 | #define MUIC_IRQ_INT2_CHGDETREUN BIT(1) | ||
| 288 | #define MUIC_IRQ_INT2_DCDTMR BIT(2) | ||
| 289 | #define MUIC_IRQ_INT2_DXOVP BIT(3) | ||
| 290 | #define MUIC_IRQ_INT2_VBVOLT BIT(4) | ||
| 291 | #define MUIC_IRQ_INT2_VIDRM BIT(5) | ||
| 292 | |||
| 293 | #define MUIC_IRQ_INT3_EOC BIT(0) | ||
| 294 | #define MUIC_IRQ_INT3_CGMBC BIT(1) | ||
| 295 | #define MUIC_IRQ_INT3_OVP BIT(2) | ||
| 296 | #define MUIC_IRQ_INT3_MBCCHG_ERR BIT(3) | ||
| 297 | #define MUIC_IRQ_INT3_CHG_ENABLED BIT(4) | ||
| 298 | #define MUIC_IRQ_INT3_BAT_DET BIT(5) | ||
| 299 | |||
| 265 | enum max77693_irq { | 300 | enum max77693_irq { |
| 266 | /* PMIC - FLASH */ | 301 | /* PMIC - FLASH */ |
| 267 | MAX77693_LED_IRQ_FLED2_OPEN, | 302 | MAX77693_LED_IRQ_FLED2_OPEN, |
| @@ -282,6 +317,10 @@ enum max77693_irq { | |||
| 282 | MAX77693_CHG_IRQ_CHG_I, | 317 | MAX77693_CHG_IRQ_CHG_I, |
| 283 | MAX77693_CHG_IRQ_CHGIN_I, | 318 | MAX77693_CHG_IRQ_CHGIN_I, |
| 284 | 319 | ||
| 320 | MAX77693_IRQ_NR, | ||
| 321 | }; | ||
| 322 | |||
| 323 | enum max77693_irq_muic { | ||
| 285 | /* MUIC INT1 */ | 324 | /* MUIC INT1 */ |
| 286 | MAX77693_MUIC_IRQ_INT1_ADC, | 325 | MAX77693_MUIC_IRQ_INT1_ADC, |
| 287 | MAX77693_MUIC_IRQ_INT1_ADC_LOW, | 326 | MAX77693_MUIC_IRQ_INT1_ADC_LOW, |
| @@ -304,7 +343,7 @@ enum max77693_irq { | |||
| 304 | MAX77693_MUIC_IRQ_INT3_CHG_ENABLED, | 343 | MAX77693_MUIC_IRQ_INT3_CHG_ENABLED, |
| 305 | MAX77693_MUIC_IRQ_INT3_BAT_DET, | 344 | MAX77693_MUIC_IRQ_INT3_BAT_DET, |
| 306 | 345 | ||
| 307 | MAX77693_IRQ_NR, | 346 | MAX77693_MUIC_IRQ_NR, |
| 308 | }; | 347 | }; |
| 309 | 348 | ||
| 310 | struct max77693_dev { | 349 | struct max77693_dev { |
| @@ -319,7 +358,10 @@ struct max77693_dev { | |||
| 319 | struct regmap *regmap_muic; | 358 | struct regmap *regmap_muic; |
| 320 | struct regmap *regmap_haptic; | 359 | struct regmap *regmap_haptic; |
| 321 | 360 | ||
| 322 | struct irq_domain *irq_domain; | 361 | struct regmap_irq_chip_data *irq_data_led; |
| 362 | struct regmap_irq_chip_data *irq_data_topsys; | ||
| 363 | struct regmap_irq_chip_data *irq_data_charger; | ||
| 364 | struct regmap_irq_chip_data *irq_data_muic; | ||
| 323 | 365 | ||
| 324 | int irq; | 366 | int irq; |
| 325 | int irq_gpio; | 367 | int irq_gpio; |
| @@ -332,14 +374,6 @@ enum max77693_types { | |||
| 332 | TYPE_MAX77693, | 374 | TYPE_MAX77693, |
| 333 | }; | 375 | }; |
| 334 | 376 | ||
| 335 | extern int max77693_read_reg(struct regmap *map, u8 reg, u8 *dest); | ||
| 336 | extern int max77693_bulk_read(struct regmap *map, u8 reg, int count, | ||
| 337 | u8 *buf); | ||
| 338 | extern int max77693_write_reg(struct regmap *map, u8 reg, u8 value); | ||
| 339 | extern int max77693_bulk_write(struct regmap *map, u8 reg, int count, | ||
| 340 | u8 *buf); | ||
| 341 | extern int max77693_update_reg(struct regmap *map, u8 reg, u8 val, u8 mask); | ||
| 342 | |||
| 343 | extern int max77693_irq_init(struct max77693_dev *max77686); | 377 | extern int max77693_irq_init(struct max77693_dev *max77686); |
| 344 | extern void max77693_irq_exit(struct max77693_dev *max77686); | 378 | extern void max77693_irq_exit(struct max77693_dev *max77686); |
| 345 | extern int max77693_irq_resume(struct max77693_dev *max77686); | 379 | extern int max77693_irq_resume(struct max77693_dev *max77686); |
diff --git a/include/linux/mic_bus.h b/include/linux/mic_bus.h new file mode 100644 index 000000000000..d5b5f76d57ef --- /dev/null +++ b/include/linux/mic_bus.h | |||
| @@ -0,0 +1,110 @@ | |||
| 1 | /* | ||
| 2 | * Intel MIC Platform Software Stack (MPSS) | ||
| 3 | * | ||
| 4 | * Copyright(c) 2014 Intel Corporation. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License, version 2, as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but | ||
| 11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 13 | * General Public License for more details. | ||
| 14 | * | ||
| 15 | * The full GNU General Public License is included in this distribution in | ||
| 16 | * the file called "COPYING". | ||
| 17 | * | ||
| 18 | * Intel MIC Bus driver. | ||
| 19 | * | ||
| 20 | * This implementation is very similar to the the virtio bus driver | ||
| 21 | * implementation @ include/linux/virtio.h. | ||
| 22 | */ | ||
| 23 | #ifndef _MIC_BUS_H_ | ||
| 24 | #define _MIC_BUS_H_ | ||
| 25 | /* | ||
| 26 | * Everything a mbus driver needs to work with any particular mbus | ||
| 27 | * implementation. | ||
| 28 | */ | ||
| 29 | #include <linux/interrupt.h> | ||
| 30 | #include <linux/dma-mapping.h> | ||
| 31 | |||
| 32 | struct mbus_device_id { | ||
| 33 | __u32 device; | ||
| 34 | __u32 vendor; | ||
| 35 | }; | ||
| 36 | |||
| 37 | #define MBUS_DEV_DMA_HOST 2 | ||
| 38 | #define MBUS_DEV_DMA_MIC 3 | ||
| 39 | #define MBUS_DEV_ANY_ID 0xffffffff | ||
| 40 | |||
| 41 | /** | ||
| 42 | * mbus_device - representation of a device using mbus | ||
| 43 | * @mmio_va: virtual address of mmio space | ||
| 44 | * @hw_ops: the hardware ops supported by this device. | ||
| 45 | * @id: the device type identification (used to match it with a driver). | ||
| 46 | * @dev: underlying device. | ||
| 47 | * be used to communicate with. | ||
| 48 | * @index: unique position on the mbus bus | ||
| 49 | */ | ||
| 50 | struct mbus_device { | ||
| 51 | void __iomem *mmio_va; | ||
| 52 | struct mbus_hw_ops *hw_ops; | ||
| 53 | struct mbus_device_id id; | ||
| 54 | struct device dev; | ||
| 55 | int index; | ||
| 56 | }; | ||
| 57 | |||
| 58 | /** | ||
| 59 | * mbus_driver - operations for a mbus I/O driver | ||
| 60 | * @driver: underlying device driver (populate name and owner). | ||
| 61 | * @id_table: the ids serviced by this driver. | ||
| 62 | * @probe: the function to call when a device is found. Returns 0 or -errno. | ||
| 63 | * @remove: the function to call when a device is removed. | ||
| 64 | */ | ||
| 65 | struct mbus_driver { | ||
| 66 | struct device_driver driver; | ||
| 67 | const struct mbus_device_id *id_table; | ||
| 68 | int (*probe)(struct mbus_device *dev); | ||
| 69 | void (*scan)(struct mbus_device *dev); | ||
| 70 | void (*remove)(struct mbus_device *dev); | ||
| 71 | }; | ||
| 72 | |||
| 73 | /** | ||
| 74 | * struct mic_irq - opaque pointer used as cookie | ||
| 75 | */ | ||
| 76 | struct mic_irq; | ||
| 77 | |||
| 78 | /** | ||
| 79 | * mbus_hw_ops - Hardware operations for accessing a MIC device on the MIC bus. | ||
| 80 | */ | ||
| 81 | struct mbus_hw_ops { | ||
| 82 | struct mic_irq* (*request_threaded_irq)(struct mbus_device *mbdev, | ||
| 83 | irq_handler_t handler, | ||
| 84 | irq_handler_t thread_fn, | ||
| 85 | const char *name, void *data, | ||
| 86 | int intr_src); | ||
| 87 | void (*free_irq)(struct mbus_device *mbdev, | ||
| 88 | struct mic_irq *cookie, void *data); | ||
| 89 | void (*ack_interrupt)(struct mbus_device *mbdev, int num); | ||
| 90 | }; | ||
| 91 | |||
| 92 | struct mbus_device * | ||
| 93 | mbus_register_device(struct device *pdev, int id, struct dma_map_ops *dma_ops, | ||
| 94 | struct mbus_hw_ops *hw_ops, void __iomem *mmio_va); | ||
| 95 | void mbus_unregister_device(struct mbus_device *mbdev); | ||
| 96 | |||
| 97 | int mbus_register_driver(struct mbus_driver *drv); | ||
| 98 | void mbus_unregister_driver(struct mbus_driver *drv); | ||
| 99 | |||
| 100 | static inline struct mbus_device *dev_to_mbus(struct device *_dev) | ||
| 101 | { | ||
| 102 | return container_of(_dev, struct mbus_device, dev); | ||
| 103 | } | ||
| 104 | |||
| 105 | static inline struct mbus_driver *drv_to_mbus(struct device_driver *drv) | ||
| 106 | { | ||
| 107 | return container_of(drv, struct mbus_driver, driver); | ||
| 108 | } | ||
| 109 | |||
| 110 | #endif /* _MIC_BUS_H */ | ||
diff --git a/include/linux/pci.h b/include/linux/pci.h index 6ed3647b38df..61978a460841 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h | |||
| @@ -1477,8 +1477,9 @@ enum pci_fixup_pass { | |||
| 1477 | pci_fixup_final, /* Final phase of device fixups */ | 1477 | pci_fixup_final, /* Final phase of device fixups */ |
| 1478 | pci_fixup_enable, /* pci_enable_device() time */ | 1478 | pci_fixup_enable, /* pci_enable_device() time */ |
| 1479 | pci_fixup_resume, /* pci_device_resume() */ | 1479 | pci_fixup_resume, /* pci_device_resume() */ |
| 1480 | pci_fixup_suspend, /* pci_device_suspend */ | 1480 | pci_fixup_suspend, /* pci_device_suspend() */ |
| 1481 | pci_fixup_resume_early, /* pci_device_resume_early() */ | 1481 | pci_fixup_resume_early, /* pci_device_resume_early() */ |
| 1482 | pci_fixup_suspend_late, /* pci_device_suspend_late() */ | ||
| 1482 | }; | 1483 | }; |
| 1483 | 1484 | ||
| 1484 | /* Anonymous variables would be nice... */ | 1485 | /* Anonymous variables would be nice... */ |
| @@ -1519,6 +1520,11 @@ enum pci_fixup_pass { | |||
| 1519 | DECLARE_PCI_FIXUP_SECTION(.pci_fixup_suspend, \ | 1520 | DECLARE_PCI_FIXUP_SECTION(.pci_fixup_suspend, \ |
| 1520 | suspend##hook, vendor, device, class, \ | 1521 | suspend##hook, vendor, device, class, \ |
| 1521 | class_shift, hook) | 1522 | class_shift, hook) |
| 1523 | #define DECLARE_PCI_FIXUP_CLASS_SUSPEND_LATE(vendor, device, class, \ | ||
| 1524 | class_shift, hook) \ | ||
| 1525 | DECLARE_PCI_FIXUP_SECTION(.pci_fixup_suspend_late, \ | ||
| 1526 | suspend_late##hook, vendor, device, \ | ||
| 1527 | class, class_shift, hook) | ||
| 1522 | 1528 | ||
| 1523 | #define DECLARE_PCI_FIXUP_EARLY(vendor, device, hook) \ | 1529 | #define DECLARE_PCI_FIXUP_EARLY(vendor, device, hook) \ |
| 1524 | DECLARE_PCI_FIXUP_SECTION(.pci_fixup_early, \ | 1530 | DECLARE_PCI_FIXUP_SECTION(.pci_fixup_early, \ |
| @@ -1544,6 +1550,10 @@ enum pci_fixup_pass { | |||
| 1544 | DECLARE_PCI_FIXUP_SECTION(.pci_fixup_suspend, \ | 1550 | DECLARE_PCI_FIXUP_SECTION(.pci_fixup_suspend, \ |
| 1545 | suspend##hook, vendor, device, \ | 1551 | suspend##hook, vendor, device, \ |
| 1546 | PCI_ANY_ID, 0, hook) | 1552 | PCI_ANY_ID, 0, hook) |
| 1553 | #define DECLARE_PCI_FIXUP_SUSPEND_LATE(vendor, device, hook) \ | ||
| 1554 | DECLARE_PCI_FIXUP_SECTION(.pci_fixup_suspend_late, \ | ||
| 1555 | suspend_late##hook, vendor, device, \ | ||
| 1556 | PCI_ANY_ID, 0, hook) | ||
| 1547 | 1557 | ||
| 1548 | #ifdef CONFIG_PCI_QUIRKS | 1558 | #ifdef CONFIG_PCI_QUIRKS |
| 1549 | void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev); | 1559 | void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev); |
diff --git a/include/uapi/linux/genwqe/genwqe_card.h b/include/uapi/linux/genwqe/genwqe_card.h index 795e957bb840..4fc065f29255 100644 --- a/include/uapi/linux/genwqe/genwqe_card.h +++ b/include/uapi/linux/genwqe/genwqe_card.h | |||
| @@ -328,6 +328,7 @@ enum genwqe_card_state { | |||
| 328 | GENWQE_CARD_UNUSED = 0, | 328 | GENWQE_CARD_UNUSED = 0, |
| 329 | GENWQE_CARD_USED = 1, | 329 | GENWQE_CARD_USED = 1, |
| 330 | GENWQE_CARD_FATAL_ERROR = 2, | 330 | GENWQE_CARD_FATAL_ERROR = 2, |
| 331 | GENWQE_CARD_RELOAD_BITSTREAM = 3, | ||
| 331 | GENWQE_CARD_STATE_MAX, | 332 | GENWQE_CARD_STATE_MAX, |
| 332 | }; | 333 | }; |
| 333 | 334 | ||
diff --git a/include/uapi/linux/i8k.h b/include/uapi/linux/i8k.h index 1c45ba505115..133d02f03c25 100644 --- a/include/uapi/linux/i8k.h +++ b/include/uapi/linux/i8k.h | |||
| @@ -34,7 +34,8 @@ | |||
| 34 | #define I8K_FAN_OFF 0 | 34 | #define I8K_FAN_OFF 0 |
| 35 | #define I8K_FAN_LOW 1 | 35 | #define I8K_FAN_LOW 1 |
| 36 | #define I8K_FAN_HIGH 2 | 36 | #define I8K_FAN_HIGH 2 |
| 37 | #define I8K_FAN_MAX I8K_FAN_HIGH | 37 | #define I8K_FAN_TURBO 3 |
| 38 | #define I8K_FAN_MAX I8K_FAN_TURBO | ||
| 38 | 39 | ||
| 39 | #define I8K_VOL_UP 1 | 40 | #define I8K_VOL_UP 1 |
| 40 | #define I8K_VOL_DOWN 2 | 41 | #define I8K_VOL_DOWN 2 |
diff --git a/tools/hv/hv_fcopy_daemon.c b/tools/hv/hv_fcopy_daemon.c index fba1c75aa484..8f96b3ee0724 100644 --- a/tools/hv/hv_fcopy_daemon.c +++ b/tools/hv/hv_fcopy_daemon.c | |||
| @@ -88,7 +88,8 @@ static int hv_start_fcopy(struct hv_start_fcopy *smsg) | |||
| 88 | } | 88 | } |
| 89 | } | 89 | } |
| 90 | 90 | ||
| 91 | target_fd = open(target_fname, O_RDWR | O_CREAT | O_CLOEXEC, 0744); | 91 | target_fd = open(target_fname, |
| 92 | O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0744); | ||
| 92 | if (target_fd == -1) { | 93 | if (target_fd == -1) { |
| 93 | syslog(LOG_INFO, "Open Failed: %s", strerror(errno)); | 94 | syslog(LOG_INFO, "Open Failed: %s", strerror(errno)); |
| 94 | goto done; | 95 | goto done; |
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index e66e710cc595..4c2aa357e12f 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile | |||
| @@ -12,6 +12,9 @@ TARGETS += powerpc | |||
| 12 | TARGETS += user | 12 | TARGETS += user |
| 13 | TARGETS += sysctl | 13 | TARGETS += sysctl |
| 14 | 14 | ||
| 15 | TARGETS_HOTPLUG = cpu-hotplug | ||
| 16 | TARGETS_HOTPLUG += memory-hotplug | ||
| 17 | |||
| 15 | all: | 18 | all: |
| 16 | for TARGET in $(TARGETS); do \ | 19 | for TARGET in $(TARGETS); do \ |
| 17 | make -C $$TARGET; \ | 20 | make -C $$TARGET; \ |
| @@ -22,6 +25,21 @@ run_tests: all | |||
| 22 | make -C $$TARGET run_tests; \ | 25 | make -C $$TARGET run_tests; \ |
| 23 | done; | 26 | done; |
| 24 | 27 | ||
| 28 | hotplug: | ||
| 29 | for TARGET in $(TARGETS_HOTPLUG); do \ | ||
| 30 | make -C $$TARGET; \ | ||
| 31 | done; | ||
| 32 | |||
| 33 | run_hotplug: hotplug | ||
| 34 | for TARGET in $(TARGETS_HOTPLUG); do \ | ||
| 35 | make -C $$TARGET run_full_test; \ | ||
| 36 | done; | ||
| 37 | |||
| 38 | clean_hotplug: | ||
| 39 | for TARGET in $(TARGETS_HOTPLUG); do \ | ||
| 40 | make -C $$TARGET clean; \ | ||
| 41 | done; | ||
| 42 | |||
| 25 | clean: | 43 | clean: |
| 26 | for TARGET in $(TARGETS); do \ | 44 | for TARGET in $(TARGETS); do \ |
| 27 | make -C $$TARGET clean; \ | 45 | make -C $$TARGET clean; \ |
diff --git a/tools/testing/selftests/README.txt b/tools/testing/selftests/README.txt index 5e2faf9c55d3..2660d5ff9179 100644 --- a/tools/testing/selftests/README.txt +++ b/tools/testing/selftests/README.txt | |||
| @@ -4,8 +4,15 @@ The kernel contains a set of "self tests" under the tools/testing/selftests/ | |||
| 4 | directory. These are intended to be small unit tests to exercise individual | 4 | directory. These are intended to be small unit tests to exercise individual |
| 5 | code paths in the kernel. | 5 | code paths in the kernel. |
| 6 | 6 | ||
| 7 | Running the selftests | 7 | On some systems, hot-plug tests could hang forever waiting for cpu and |
| 8 | ===================== | 8 | memory to be ready to be offlined. A special hot-plug target is created |
| 9 | to run full range of hot-plug tests. In default mode, hot-plug tests run | ||
| 10 | in safe mode with a limited scope. In limited mode, cpu-hotplug test is | ||
| 11 | run on a single cpu as opposed to all hotplug capable cpus, and memory | ||
| 12 | hotplug test is run on 2% of hotplug capable memory instead of 10%. | ||
| 13 | |||
| 14 | Running the selftests (hotplug tests are run in limited mode) | ||
| 15 | ============================================================= | ||
| 9 | 16 | ||
| 10 | To build the tests: | 17 | To build the tests: |
| 11 | 18 | ||
| @@ -18,14 +25,26 @@ To run the tests: | |||
| 18 | 25 | ||
| 19 | - note that some tests will require root privileges. | 26 | - note that some tests will require root privileges. |
| 20 | 27 | ||
| 21 | 28 | To run only tests targeted for a single subsystem: (including | |
| 22 | To run only tests targetted for a single subsystem: | 29 | hotplug targets in limited mode) |
| 23 | 30 | ||
| 24 | $ make -C tools/testing/selftests TARGETS=cpu-hotplug run_tests | 31 | $ make -C tools/testing/selftests TARGETS=cpu-hotplug run_tests |
| 25 | 32 | ||
| 26 | See the top-level tools/testing/selftests/Makefile for the list of all possible | 33 | See the top-level tools/testing/selftests/Makefile for the list of all possible |
| 27 | targets. | 34 | targets. |
| 28 | 35 | ||
| 36 | Running the full range hotplug selftests | ||
| 37 | ======================================== | ||
| 38 | |||
| 39 | To build the tests: | ||
| 40 | |||
| 41 | $ make -C tools/testing/selftests hotplug | ||
| 42 | |||
| 43 | To run the tests: | ||
| 44 | |||
| 45 | $ make -C tools/testing/selftests run_hotplug | ||
| 46 | |||
| 47 | - note that some tests will require root privileges. | ||
| 29 | 48 | ||
| 30 | Contributing new tests | 49 | Contributing new tests |
| 31 | ====================== | 50 | ====================== |
diff --git a/tools/testing/selftests/cpu-hotplug/Makefile b/tools/testing/selftests/cpu-hotplug/Makefile index 790c23a9db44..e9c28d8dc84b 100644 --- a/tools/testing/selftests/cpu-hotplug/Makefile +++ b/tools/testing/selftests/cpu-hotplug/Makefile | |||
| @@ -3,4 +3,7 @@ all: | |||
| 3 | run_tests: | 3 | run_tests: |
| 4 | @/bin/bash ./on-off-test.sh || echo "cpu-hotplug selftests: [FAIL]" | 4 | @/bin/bash ./on-off-test.sh || echo "cpu-hotplug selftests: [FAIL]" |
| 5 | 5 | ||
| 6 | run_full_test: | ||
| 7 | @/bin/bash ./on-off-test.sh -a || echo "cpu-hotplug selftests: [FAIL]" | ||
| 8 | |||
| 6 | clean: | 9 | clean: |
diff --git a/tools/testing/selftests/cpu-hotplug/on-off-test.sh b/tools/testing/selftests/cpu-hotplug/on-off-test.sh index bdde7cf428bb..98b1d6565f2c 100644 --- a/tools/testing/selftests/cpu-hotplug/on-off-test.sh +++ b/tools/testing/selftests/cpu-hotplug/on-off-test.sh | |||
| @@ -11,6 +11,8 @@ prerequisite() | |||
| 11 | exit 0 | 11 | exit 0 |
| 12 | fi | 12 | fi |
| 13 | 13 | ||
| 14 | taskset -p 01 $$ | ||
| 15 | |||
| 14 | SYSFS=`mount -t sysfs | head -1 | awk '{ print $3 }'` | 16 | SYSFS=`mount -t sysfs | head -1 | awk '{ print $3 }'` |
| 15 | 17 | ||
| 16 | if [ ! -d "$SYSFS" ]; then | 18 | if [ ! -d "$SYSFS" ]; then |
| @@ -22,6 +24,19 @@ prerequisite() | |||
| 22 | echo $msg cpu hotplug is not supported >&2 | 24 | echo $msg cpu hotplug is not supported >&2 |
| 23 | exit 0 | 25 | exit 0 |
| 24 | fi | 26 | fi |
| 27 | |||
| 28 | echo "CPU online/offline summary:" | ||
| 29 | online_cpus=`cat $SYSFS/devices/system/cpu/online` | ||
| 30 | online_max=${online_cpus##*-} | ||
| 31 | echo -e "\t Cpus in online state: $online_cpus" | ||
| 32 | |||
| 33 | offline_cpus=`cat $SYSFS/devices/system/cpu/offline` | ||
| 34 | if [[ "a$offline_cpus" = "a" ]]; then | ||
| 35 | offline_cpus=0 | ||
| 36 | else | ||
| 37 | offline_max=${offline_cpus##*-} | ||
| 38 | fi | ||
| 39 | echo -e "\t Cpus in offline state: $offline_cpus" | ||
| 25 | } | 40 | } |
| 26 | 41 | ||
| 27 | # | 42 | # |
| @@ -113,15 +128,25 @@ offline_cpu_expect_fail() | |||
| 113 | } | 128 | } |
| 114 | 129 | ||
| 115 | error=-12 | 130 | error=-12 |
| 131 | allcpus=0 | ||
| 116 | priority=0 | 132 | priority=0 |
| 133 | online_cpus=0 | ||
| 134 | online_max=0 | ||
| 135 | offline_cpus=0 | ||
| 136 | offline_max=0 | ||
| 117 | 137 | ||
| 118 | while getopts e:hp: opt; do | 138 | while getopts e:ahp: opt; do |
| 119 | case $opt in | 139 | case $opt in |
| 120 | e) | 140 | e) |
| 121 | error=$OPTARG | 141 | error=$OPTARG |
| 122 | ;; | 142 | ;; |
| 143 | a) | ||
| 144 | allcpus=1 | ||
| 145 | ;; | ||
| 123 | h) | 146 | h) |
| 124 | echo "Usage $0 [ -e errno ] [ -p notifier-priority ]" | 147 | echo "Usage $0 [ -a ] [ -e errno ] [ -p notifier-priority ]" |
| 148 | echo -e "\t default offline one cpu" | ||
| 149 | echo -e "\t run with -a option to offline all cpus" | ||
| 125 | exit | 150 | exit |
| 126 | ;; | 151 | ;; |
| 127 | p) | 152 | p) |
| @@ -138,6 +163,29 @@ fi | |||
| 138 | prerequisite | 163 | prerequisite |
| 139 | 164 | ||
| 140 | # | 165 | # |
| 166 | # Safe test (default) - offline and online one cpu | ||
| 167 | # | ||
| 168 | if [ $allcpus -eq 0 ]; then | ||
| 169 | echo "Limited scope test: one hotplug cpu" | ||
| 170 | echo -e "\t (leaves cpu in the original state):" | ||
| 171 | echo -e "\t online to offline to online: cpu $online_max" | ||
| 172 | offline_cpu_expect_success $online_max | ||
| 173 | online_cpu_expect_success $online_max | ||
| 174 | |||
| 175 | if [[ $offline_cpus -gt 0 ]]; then | ||
| 176 | echo -e "\t offline to online to offline: cpu $offline_max" | ||
| 177 | online_cpu_expect_success $offline_max | ||
| 178 | offline_cpu_expect_success $offline_max | ||
| 179 | fi | ||
| 180 | exit 0 | ||
| 181 | else | ||
| 182 | echo "Full scope test: all hotplug cpus" | ||
| 183 | echo -e "\t online all offline cpus" | ||
| 184 | echo -e "\t offline all online cpus" | ||
| 185 | echo -e "\t online all offline cpus" | ||
| 186 | fi | ||
| 187 | |||
| 188 | # | ||
| 141 | # Online all hot-pluggable CPUs | 189 | # Online all hot-pluggable CPUs |
| 142 | # | 190 | # |
| 143 | for cpu in `hotplaggable_offline_cpus`; do | 191 | for cpu in `hotplaggable_offline_cpus`; do |
diff --git a/tools/testing/selftests/kcmp/kcmp_test.c b/tools/testing/selftests/kcmp/kcmp_test.c index fa4f1b37e045..dbba4084869c 100644 --- a/tools/testing/selftests/kcmp/kcmp_test.c +++ b/tools/testing/selftests/kcmp/kcmp_test.c | |||
| @@ -81,7 +81,7 @@ int main(int argc, char **argv) | |||
| 81 | /* Compare with self */ | 81 | /* Compare with self */ |
| 82 | ret = sys_kcmp(pid1, pid1, KCMP_VM, 0, 0); | 82 | ret = sys_kcmp(pid1, pid1, KCMP_VM, 0, 0); |
| 83 | if (ret) { | 83 | if (ret) { |
| 84 | printf("FAIL: 0 expected but %li returned (%s)\n", | 84 | printf("FAIL: 0 expected but %d returned (%s)\n", |
| 85 | ret, strerror(errno)); | 85 | ret, strerror(errno)); |
| 86 | ret = -1; | 86 | ret = -1; |
| 87 | } else | 87 | } else |
diff --git a/tools/testing/selftests/memory-hotplug/Makefile b/tools/testing/selftests/memory-hotplug/Makefile index 058c76f5d102..d46b8d489cd2 100644 --- a/tools/testing/selftests/memory-hotplug/Makefile +++ b/tools/testing/selftests/memory-hotplug/Makefile | |||
| @@ -1,6 +1,9 @@ | |||
| 1 | all: | 1 | all: |
| 2 | 2 | ||
| 3 | run_tests: | 3 | run_tests: |
| 4 | @/bin/bash ./on-off-test.sh -r 2 || echo "memory-hotplug selftests: [FAIL]" | ||
| 5 | |||
| 6 | run_full_test: | ||
| 4 | @/bin/bash ./on-off-test.sh || echo "memory-hotplug selftests: [FAIL]" | 7 | @/bin/bash ./on-off-test.sh || echo "memory-hotplug selftests: [FAIL]" |
| 5 | 8 | ||
| 6 | clean: | 9 | clean: |
diff --git a/tools/testing/selftests/memory-hotplug/on-off-test.sh b/tools/testing/selftests/memory-hotplug/on-off-test.sh index a2816f631542..6cddde0b96f8 100644 --- a/tools/testing/selftests/memory-hotplug/on-off-test.sh +++ b/tools/testing/selftests/memory-hotplug/on-off-test.sh | |||
| @@ -142,10 +142,16 @@ fi | |||
| 142 | 142 | ||
| 143 | prerequisite | 143 | prerequisite |
| 144 | 144 | ||
| 145 | echo "Test scope: $ratio% hotplug memory" | ||
| 146 | echo -e "\t online all hotplug memory in offline state" | ||
| 147 | echo -e "\t offline $ratio% hotplug memory in online state" | ||
| 148 | echo -e "\t online all hotplug memory in offline state" | ||
| 149 | |||
| 145 | # | 150 | # |
| 146 | # Online all hot-pluggable memory | 151 | # Online all hot-pluggable memory |
| 147 | # | 152 | # |
| 148 | for memory in `hotplaggable_offline_memory`; do | 153 | for memory in `hotplaggable_offline_memory`; do |
| 154 | echo offline-online $memory | ||
| 149 | online_memory_expect_success $memory | 155 | online_memory_expect_success $memory |
| 150 | done | 156 | done |
| 151 | 157 | ||
| @@ -154,6 +160,7 @@ done | |||
| 154 | # | 160 | # |
| 155 | for memory in `hotpluggable_online_memory`; do | 161 | for memory in `hotpluggable_online_memory`; do |
| 156 | if [ $((RANDOM % 100)) -lt $ratio ]; then | 162 | if [ $((RANDOM % 100)) -lt $ratio ]; then |
| 163 | echo online-offline $memory | ||
| 157 | offline_memory_expect_success $memory | 164 | offline_memory_expect_success $memory |
| 158 | fi | 165 | fi |
| 159 | done | 166 | done |
| @@ -162,6 +169,7 @@ done | |||
| 162 | # Online all hot-pluggable memory again | 169 | # Online all hot-pluggable memory again |
| 163 | # | 170 | # |
| 164 | for memory in `hotplaggable_offline_memory`; do | 171 | for memory in `hotplaggable_offline_memory`; do |
| 172 | echo offline-online $memory | ||
| 165 | online_memory_expect_success $memory | 173 | online_memory_expect_success $memory |
| 166 | done | 174 | done |
| 167 | 175 | ||
diff --git a/tools/testing/selftests/mqueue/Makefile b/tools/testing/selftests/mqueue/Makefile index 218a122c7951..8056e2e68fa4 100644 --- a/tools/testing/selftests/mqueue/Makefile +++ b/tools/testing/selftests/mqueue/Makefile | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | all: | 1 | all: |
| 2 | gcc -O2 -lrt mq_open_tests.c -o mq_open_tests | 2 | gcc -O2 mq_open_tests.c -o mq_open_tests -lrt |
| 3 | gcc -O2 -lrt -lpthread -lpopt -o mq_perf_tests mq_perf_tests.c | 3 | gcc -O2 -o mq_perf_tests mq_perf_tests.c -lrt -lpthread -lpopt |
| 4 | 4 | ||
| 5 | run_tests: | 5 | run_tests: |
| 6 | @./mq_open_tests /test1 || echo "mq_open_tests: [FAIL]" | 6 | @./mq_open_tests /test1 || echo "mq_open_tests: [FAIL]" |
diff --git a/tools/testing/selftests/mqueue/mq_open_tests.c b/tools/testing/selftests/mqueue/mq_open_tests.c index 711cc2923047..9c1a5d359055 100644 --- a/tools/testing/selftests/mqueue/mq_open_tests.c +++ b/tools/testing/selftests/mqueue/mq_open_tests.c | |||
| @@ -80,7 +80,8 @@ void shutdown(int exit_val, char *err_cause, int line_no) | |||
| 80 | if (in_shutdown++) | 80 | if (in_shutdown++) |
| 81 | return; | 81 | return; |
| 82 | 82 | ||
| 83 | seteuid(0); | 83 | if (seteuid(0) == -1) |
| 84 | perror("seteuid() failed"); | ||
| 84 | 85 | ||
| 85 | if (queue != -1) | 86 | if (queue != -1) |
| 86 | if (mq_close(queue)) | 87 | if (mq_close(queue)) |
| @@ -292,8 +293,10 @@ int main(int argc, char *argv[]) | |||
| 292 | /* Tell the user our initial state */ | 293 | /* Tell the user our initial state */ |
| 293 | printf("\nInitial system state:\n"); | 294 | printf("\nInitial system state:\n"); |
| 294 | printf("\tUsing queue path:\t\t%s\n", queue_path); | 295 | printf("\tUsing queue path:\t\t%s\n", queue_path); |
| 295 | printf("\tRLIMIT_MSGQUEUE(soft):\t\t%d\n", saved_limits.rlim_cur); | 296 | printf("\tRLIMIT_MSGQUEUE(soft):\t\t%ld\n", |
| 296 | printf("\tRLIMIT_MSGQUEUE(hard):\t\t%d\n", saved_limits.rlim_max); | 297 | (long) saved_limits.rlim_cur); |
| 298 | printf("\tRLIMIT_MSGQUEUE(hard):\t\t%ld\n", | ||
| 299 | (long) saved_limits.rlim_max); | ||
| 297 | printf("\tMaximum Message Size:\t\t%d\n", saved_max_msgsize); | 300 | printf("\tMaximum Message Size:\t\t%d\n", saved_max_msgsize); |
| 298 | printf("\tMaximum Queue Size:\t\t%d\n", saved_max_msgs); | 301 | printf("\tMaximum Queue Size:\t\t%d\n", saved_max_msgs); |
| 299 | if (default_settings) { | 302 | if (default_settings) { |
| @@ -308,8 +311,8 @@ int main(int argc, char *argv[]) | |||
| 308 | validate_current_settings(); | 311 | validate_current_settings(); |
| 309 | 312 | ||
| 310 | printf("Adjusted system state for testing:\n"); | 313 | printf("Adjusted system state for testing:\n"); |
| 311 | printf("\tRLIMIT_MSGQUEUE(soft):\t\t%d\n", cur_limits.rlim_cur); | 314 | printf("\tRLIMIT_MSGQUEUE(soft):\t\t%ld\n", (long) cur_limits.rlim_cur); |
| 312 | printf("\tRLIMIT_MSGQUEUE(hard):\t\t%d\n", cur_limits.rlim_max); | 315 | printf("\tRLIMIT_MSGQUEUE(hard):\t\t%ld\n", (long) cur_limits.rlim_max); |
| 313 | printf("\tMaximum Message Size:\t\t%d\n", cur_max_msgsize); | 316 | printf("\tMaximum Message Size:\t\t%d\n", cur_max_msgsize); |
| 314 | printf("\tMaximum Queue Size:\t\t%d\n", cur_max_msgs); | 317 | printf("\tMaximum Queue Size:\t\t%d\n", cur_max_msgs); |
| 315 | if (default_settings) { | 318 | if (default_settings) { |
| @@ -454,7 +457,12 @@ int main(int argc, char *argv[]) | |||
| 454 | else | 457 | else |
| 455 | printf("Queue open with total size > 2GB when euid = 0 " | 458 | printf("Queue open with total size > 2GB when euid = 0 " |
| 456 | "failed:\t\t\tPASS\n"); | 459 | "failed:\t\t\tPASS\n"); |
| 457 | seteuid(99); | 460 | |
| 461 | if (seteuid(99) == -1) { | ||
| 462 | perror("seteuid() failed"); | ||
| 463 | exit(1); | ||
| 464 | } | ||
| 465 | |||
| 458 | attr.mq_maxmsg = cur_max_msgs; | 466 | attr.mq_maxmsg = cur_max_msgs; |
| 459 | attr.mq_msgsize = cur_max_msgsize; | 467 | attr.mq_msgsize = cur_max_msgsize; |
| 460 | if (test_queue_fail(&attr, &result)) | 468 | if (test_queue_fail(&attr, &result)) |
diff --git a/tools/testing/selftests/mqueue/mq_perf_tests.c b/tools/testing/selftests/mqueue/mq_perf_tests.c index 2fadd4b97045..94dae65eea41 100644 --- a/tools/testing/selftests/mqueue/mq_perf_tests.c +++ b/tools/testing/selftests/mqueue/mq_perf_tests.c | |||
| @@ -296,9 +296,9 @@ static inline void open_queue(struct mq_attr *attr) | |||
| 296 | printf("\n\tQueue %s created:\n", queue_path); | 296 | printf("\n\tQueue %s created:\n", queue_path); |
| 297 | printf("\t\tmq_flags:\t\t\t%s\n", result.mq_flags & O_NONBLOCK ? | 297 | printf("\t\tmq_flags:\t\t\t%s\n", result.mq_flags & O_NONBLOCK ? |
| 298 | "O_NONBLOCK" : "(null)"); | 298 | "O_NONBLOCK" : "(null)"); |
| 299 | printf("\t\tmq_maxmsg:\t\t\t%d\n", result.mq_maxmsg); | 299 | printf("\t\tmq_maxmsg:\t\t\t%lu\n", result.mq_maxmsg); |
| 300 | printf("\t\tmq_msgsize:\t\t\t%d\n", result.mq_msgsize); | 300 | printf("\t\tmq_msgsize:\t\t\t%lu\n", result.mq_msgsize); |
| 301 | printf("\t\tmq_curmsgs:\t\t\t%d\n", result.mq_curmsgs); | 301 | printf("\t\tmq_curmsgs:\t\t\t%lu\n", result.mq_curmsgs); |
| 302 | } | 302 | } |
| 303 | 303 | ||
| 304 | void *fake_cont_thread(void *arg) | 304 | void *fake_cont_thread(void *arg) |
| @@ -440,7 +440,7 @@ void *perf_test_thread(void *arg) | |||
| 440 | shutdown(2, "clock_getres()", __LINE__); | 440 | shutdown(2, "clock_getres()", __LINE__); |
| 441 | 441 | ||
| 442 | printf("\t\tMax priorities:\t\t\t%d\n", mq_prio_max); | 442 | printf("\t\tMax priorities:\t\t\t%d\n", mq_prio_max); |
| 443 | printf("\t\tClock resolution:\t\t%d nsec%s\n", res.tv_nsec, | 443 | printf("\t\tClock resolution:\t\t%lu nsec%s\n", res.tv_nsec, |
| 444 | res.tv_nsec > 1 ? "s" : ""); | 444 | res.tv_nsec > 1 ? "s" : ""); |
| 445 | 445 | ||
| 446 | 446 | ||
| @@ -454,20 +454,20 @@ void *perf_test_thread(void *arg) | |||
| 454 | recv_total.tv_nsec = 0; | 454 | recv_total.tv_nsec = 0; |
| 455 | for (i = 0; i < TEST1_LOOPS; i++) | 455 | for (i = 0; i < TEST1_LOOPS; i++) |
| 456 | do_send_recv(); | 456 | do_send_recv(); |
| 457 | printf("\t\tSend msg:\t\t\t%d.%ds total time\n", | 457 | printf("\t\tSend msg:\t\t\t%ld.%lus total time\n", |
| 458 | send_total.tv_sec, send_total.tv_nsec); | 458 | send_total.tv_sec, send_total.tv_nsec); |
| 459 | nsec = ((unsigned long long)send_total.tv_sec * 1000000000 + | 459 | nsec = ((unsigned long long)send_total.tv_sec * 1000000000 + |
| 460 | send_total.tv_nsec) / TEST1_LOOPS; | 460 | send_total.tv_nsec) / TEST1_LOOPS; |
| 461 | printf("\t\t\t\t\t\t%d nsec/msg\n", nsec); | 461 | printf("\t\t\t\t\t\t%lld nsec/msg\n", nsec); |
| 462 | printf("\t\tRecv msg:\t\t\t%d.%ds total time\n", | 462 | printf("\t\tRecv msg:\t\t\t%ld.%lus total time\n", |
| 463 | recv_total.tv_sec, recv_total.tv_nsec); | 463 | recv_total.tv_sec, recv_total.tv_nsec); |
| 464 | nsec = ((unsigned long long)recv_total.tv_sec * 1000000000 + | 464 | nsec = ((unsigned long long)recv_total.tv_sec * 1000000000 + |
| 465 | recv_total.tv_nsec) / TEST1_LOOPS; | 465 | recv_total.tv_nsec) / TEST1_LOOPS; |
| 466 | printf("\t\t\t\t\t\t%d nsec/msg\n", nsec); | 466 | printf("\t\t\t\t\t\t%lld nsec/msg\n", nsec); |
| 467 | 467 | ||
| 468 | 468 | ||
| 469 | for (cur_test = test2; cur_test->desc != NULL; cur_test++) { | 469 | for (cur_test = test2; cur_test->desc != NULL; cur_test++) { |
| 470 | printf(cur_test->desc); | 470 | printf("%s:\n", cur_test->desc); |
| 471 | printf("\t\t(%d iterations)\n", TEST2_LOOPS); | 471 | printf("\t\t(%d iterations)\n", TEST2_LOOPS); |
| 472 | prio_out = 0; | 472 | prio_out = 0; |
| 473 | send_total.tv_sec = 0; | 473 | send_total.tv_sec = 0; |
| @@ -493,16 +493,16 @@ void *perf_test_thread(void *arg) | |||
| 493 | cur_test->func(&prio_out); | 493 | cur_test->func(&prio_out); |
| 494 | } | 494 | } |
| 495 | printf("done.\n"); | 495 | printf("done.\n"); |
| 496 | printf("\t\tSend msg:\t\t\t%d.%ds total time\n", | 496 | printf("\t\tSend msg:\t\t\t%ld.%lus total time\n", |
| 497 | send_total.tv_sec, send_total.tv_nsec); | 497 | send_total.tv_sec, send_total.tv_nsec); |
| 498 | nsec = ((unsigned long long)send_total.tv_sec * 1000000000 + | 498 | nsec = ((unsigned long long)send_total.tv_sec * 1000000000 + |
| 499 | send_total.tv_nsec) / TEST2_LOOPS; | 499 | send_total.tv_nsec) / TEST2_LOOPS; |
| 500 | printf("\t\t\t\t\t\t%d nsec/msg\n", nsec); | 500 | printf("\t\t\t\t\t\t%lld nsec/msg\n", nsec); |
| 501 | printf("\t\tRecv msg:\t\t\t%d.%ds total time\n", | 501 | printf("\t\tRecv msg:\t\t\t%ld.%lus total time\n", |
| 502 | recv_total.tv_sec, recv_total.tv_nsec); | 502 | recv_total.tv_sec, recv_total.tv_nsec); |
| 503 | nsec = ((unsigned long long)recv_total.tv_sec * 1000000000 + | 503 | nsec = ((unsigned long long)recv_total.tv_sec * 1000000000 + |
| 504 | recv_total.tv_nsec) / TEST2_LOOPS; | 504 | recv_total.tv_nsec) / TEST2_LOOPS; |
| 505 | printf("\t\t\t\t\t\t%d nsec/msg\n", nsec); | 505 | printf("\t\t\t\t\t\t%lld nsec/msg\n", nsec); |
| 506 | printf("\t\tDraining queue..."); | 506 | printf("\t\tDraining queue..."); |
| 507 | fflush(stdout); | 507 | fflush(stdout); |
| 508 | clock_gettime(clock, &start); | 508 | clock_gettime(clock, &start); |
| @@ -653,8 +653,10 @@ int main(int argc, char *argv[]) | |||
| 653 | /* Tell the user our initial state */ | 653 | /* Tell the user our initial state */ |
| 654 | printf("\nInitial system state:\n"); | 654 | printf("\nInitial system state:\n"); |
| 655 | printf("\tUsing queue path:\t\t\t%s\n", queue_path); | 655 | printf("\tUsing queue path:\t\t\t%s\n", queue_path); |
| 656 | printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t%d\n", saved_limits.rlim_cur); | 656 | printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t%ld\n", |
| 657 | printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t%d\n", saved_limits.rlim_max); | 657 | (long) saved_limits.rlim_cur); |
| 658 | printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t%ld\n", | ||
| 659 | (long) saved_limits.rlim_max); | ||
| 658 | printf("\tMaximum Message Size:\t\t\t%d\n", saved_max_msgsize); | 660 | printf("\tMaximum Message Size:\t\t\t%d\n", saved_max_msgsize); |
| 659 | printf("\tMaximum Queue Size:\t\t\t%d\n", saved_max_msgs); | 661 | printf("\tMaximum Queue Size:\t\t\t%d\n", saved_max_msgs); |
| 660 | printf("\tNice value:\t\t\t\t%d\n", cur_nice); | 662 | printf("\tNice value:\t\t\t\t%d\n", cur_nice); |
| @@ -667,10 +669,10 @@ int main(int argc, char *argv[]) | |||
| 667 | printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t(unlimited)\n"); | 669 | printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t(unlimited)\n"); |
| 668 | printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t(unlimited)\n"); | 670 | printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t(unlimited)\n"); |
| 669 | } else { | 671 | } else { |
| 670 | printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t%d\n", | 672 | printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t%ld\n", |
| 671 | cur_limits.rlim_cur); | 673 | (long) cur_limits.rlim_cur); |
| 672 | printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t%d\n", | 674 | printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t%ld\n", |
| 673 | cur_limits.rlim_max); | 675 | (long) cur_limits.rlim_max); |
| 674 | } | 676 | } |
| 675 | printf("\tMaximum Message Size:\t\t\t%d\n", cur_max_msgsize); | 677 | printf("\tMaximum Message Size:\t\t\t%d\n", cur_max_msgsize); |
| 676 | printf("\tMaximum Queue Size:\t\t\t%d\n", cur_max_msgs); | 678 | printf("\tMaximum Queue Size:\t\t\t%d\n", cur_max_msgs); |
