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 ...
Unified · Split
| @@ -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 | + | |
| @@ -25,6 +25,15 @@ Date: Oct 2013 · 24 unmodified lines | ||
| 25 | 25 | Contact: haver@linux.vnet.ibm.com |
| 26 | 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 | 37 | What: /sys/class/genwqe/genwqe<n>_card/tempsens |
| 29 | 38 | Date: Oct 2013 |
| 30 | 39 | Contact: haver@linux.vnet.ibm.com |
| @@ -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 | + }; | |
| @@ -17,35 +17,50 @@ for applications. A key benefit of our solution is that it leverages · 16 unmodified lines | ||
| 17 | 17 | the standard virtio framework for network, disk and console devices, |
| 18 | 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 | 27 | Here is a block diagram of the various components described above. The |
| 21 | 28 | virtio backends are situated on the host rather than the card given better |
| 22 | 29 | single threaded performance for the host compared to MIC, the ability of |
| 23 | 30 | the host to initiate DMA's to/from the card using the MIC DMA engine and |
| 24 | 31 | the fact that the virtio block storage backend can only be on the host. |
| 25 | 32 | |
| 26 | − | | |
| 27 | − +----------+ | +----------+ | |
| 28 | − | Card OS | | | Host OS | | |
| 29 | − +----------+ | +----------+ | |
| 30 | − | | |
| 31 | −+-------+ +--------+ +------+ | +---------+ +--------+ +--------+ | |
| 32 | −| Virtio| |Virtio | |Virtio| | |Virtio | |Virtio | |Virtio | | |
| 33 | −| Net | |Console | |Block | | |Net | |Console | |Block | | |
| 34 | −| Driver| |Driver | |Driver| | |backend | |backend | |backend | | |
| 35 | −+-------+ +--------+ +------+ | +---------+ +--------+ +--------+ | |
| 36 | − | | | | | | | | |
| 37 | − | | | |User | | | | |
| 38 | − | | | |------|------------|---------|------- | |
| 39 | − +-------------------+ |Kernel +--------------------------+ | |
| 40 | − | | | Virtio over PCIe IOCTLs | | |
| 41 | − | | +--------------------------+ | |
| 42 | − +--------------+ | | | |
| 43 | − |Intel MIC | | +---------------+ | |
| 44 | − |Card Driver | | |Intel MIC | | |
| 45 | − +--------------+ | |Host Driver | | |
| 46 | − | | +---------------+ | |
| 47 | − | | | | |
| 48 | − +-------------------------------------------------------------+ | |
| 49 | − | | | |
| 50 | − | PCIe Bus | | |
| 51 | − +-------------------------------------------------------------+ | |
| 33 | + | | |
| 34 | + +----------+ | +----------+ | |
| 35 | + | Card OS | | | Host OS | | |
| 36 | + +----------+ | +----------+ | |
| 37 | + | | |
| 38 | + +-------+ +--------+ +------+ | +---------+ +--------+ +--------+ | |
| 39 | + | Virtio| |Virtio | |Virtio| | |Virtio | |Virtio | |Virtio | | |
| 40 | + | Net | |Console | |Block | | |Net | |Console | |Block | | |
| 41 | + | Driver| |Driver | |Driver| | |backend | |backend | |backend | | |
| 42 | + +-------+ +--------+ +------+ | +---------+ +--------+ +--------+ | |
| 43 | + | | | | | | | | |
| 44 | + | | | |User | | | | |
| 45 | + | | | |------|------------|---------|------- | |
| 46 | + +-------------------+ |Kernel +--------------------------+ | |
| 47 | + | | | Virtio over PCIe IOCTLs | | |
| 48 | + | | +--------------------------+ | |
| 49 | ++-----------+ | | | +-----------+ | |
| 50 | +| MIC DMA | | | | | MIC DMA | | |
| 51 | +| Driver | | | | | Driver | | |
| 52 | ++-----------+ | | | +-----------+ | |
| 53 | + | | | | | | |
| 54 | ++---------------+ | | | +----------------+ | |
| 55 | +|MIC virtual Bus| | | | |MIC virtual Bus | | |
| 56 | ++---------------+ | | | +----------------+ | |
| 57 | + | | | | | | |
| 58 | + | +--------------+ | +---------------+ | | |
| 59 | + | |Intel MIC | | |Intel MIC | | | |
| 60 | + +---|Card Driver | | |Host Driver | | | |
| 61 | + +--------------+ | +---------------+-----+ | |
| 62 | + | | | | |
| 63 | + +-------------------------------------------------------------+ | |
| 64 | + | | | |
| 65 | + | PCIe Bus | | |
| 66 | + +-------------------------------------------------------------+ | |
| @@ -48,18 +48,18 @@ start() · 47 unmodified lines | ||
| 48 | 48 | fi |
| 49 | 49 | |
| 50 | 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 | |
| 54 | − if [ ! -d "$sysfs" ]; then | |
| 55 | − modprobe mic_host | |
| 53 | + for f in "mic_host" "mic_x100_dma" | |
| 54 | + do | |
| 55 | + modprobe $f | |
| 56 | 56 | RETVAL=$? |
| 57 | 57 | if [ $RETVAL -ne 0 ]; then |
| 58 | 58 | failure |
| 59 | 59 | echo |
| 60 | 60 | return $RETVAL |
| 61 | 61 | fi |
| 62 | − fi | |
| 62 | + done | |
| 63 | 63 | |
| 64 | 64 | # Start the daemon |
| 65 | 65 | echo -n $"Starting MPSSD " |
| @@ -170,8 +170,8 @@ unload() · 104 unmodified lines | ||
| 170 | 170 | stop |
| 171 | 171 | |
| 172 | 172 | sleep 5 |
| 173 | − echo -n $"Removing MIC_HOST Module: " | |
| 174 | − modprobe -r mic_host | |
| 173 | + echo -n $"Removing MIC_HOST & MIC_X100_DMA Modules: " | |
| 174 | + modprobe -r mic_host mic_x100_dma | |
| 175 | 175 | RETVAL=$? |
| 176 | 176 | [ $RETVAL -ne 0 ] && failure || success |
| 177 | 177 | echo |
| @@ -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. | |
| @@ -7844,6 +7844,11 @@ S: Maintained · 7843 unmodified lines | ||
| 7844 | 7844 | F: include/linux/mmc/dw_mmc.h |
| 7845 | 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 | 7852 | TIMEKEEPING, CLOCKSOURCE CORE, NTP |
| 7848 | 7853 | M: John Stultz <john.stultz@linaro.org> |
| 7849 | 7854 | M: Thomas Gleixner <tglx@linutronix.de> |
| @@ -178,4 +178,6 @@ source "drivers/mcb/Kconfig" · 177 unmodified lines | ||
| 178 | 178 | |
| 179 | 179 | source "drivers/ras/Kconfig" |
| 180 | 180 | |
| 181 | +source "drivers/thunderbolt/Kconfig" | |
| 182 | + | |
| 181 | 183 | endmenu |
| @@ -159,3 +159,4 @@ obj-$(CONFIG_FMC) += fmc/ · 158 unmodified lines | ||
| 159 | 159 | obj-$(CONFIG_POWERCAP) += powercap/ |
| 160 | 160 | obj-$(CONFIG_MCB) += mcb/ |
| 161 | 161 | obj-$(CONFIG_RAS) += ras/ |
| 162 | +obj-$(CONFIG_THUNDERBOLT) += thunderbolt/ | |
| @@ -259,7 +259,7 @@ static int bsr_add_node(struct device_node *bn) · 258 unmodified lines | ||
| 259 | 259 | } |
| 260 | 260 | |
| 261 | 261 | cur->bsr_device = device_create(bsr_class, NULL, cur->bsr_dev, |
| 262 | − cur, cur->bsr_name); | |
| 262 | + cur, "%s", cur->bsr_name); | |
| 263 | 263 | if (IS_ERR(cur->bsr_device)) { |
| 264 | 264 | printk(KERN_ERR "device_create failed for %s\n", |
| 265 | 265 | cur->bsr_name); |
| @@ -383,7 +383,7 @@ static long dsp56k_ioctl(struct file *file, unsigned int cmd, · 382 unmodified lines | ||
| 383 | 383 | return put_user(status, &hf->status); |
| 384 | 384 | } |
| 385 | 385 | case DSP56K_HOST_CMD: |
| 386 | − if (arg > 31 || arg < 0) | |
| 386 | + if (arg > 31) | |
| 387 | 387 | return -EINVAL; |
| 388 | 388 | mutex_lock(&dsp56k_mutex); |
| 389 | 389 | dsp56k_host_interface.cvr = (u_char)((arg & DSP56K_CVR_HV_MASK) | |
| @@ -65,6 +65,8 @@ static char bios_version[4]; · 64 unmodified lines | ||
| 65 | 65 | static struct device *i8k_hwmon_dev; |
| 66 | 66 | static u32 i8k_hwmon_flags; |
| 67 | 67 | static int i8k_fan_mult; |
| 68 | +static int i8k_pwm_mult; | |
| 69 | +static int i8k_fan_max = I8K_FAN_HIGH; | |
| 68 | 70 | |
| 69 | 71 | #define I8K_HWMON_HAVE_TEMP1 (1 << 0) |
| 70 | 72 | #define I8K_HWMON_HAVE_TEMP2 (1 << 1) |
| @@ -97,6 +99,10 @@ static int fan_mult = I8K_FAN_MULT; · 26 unmodified lines | ||
| 97 | 99 | module_param(fan_mult, int, 0); |
| 98 | 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 | 106 | static int i8k_open_fs(struct inode *inode, struct file *file); |
| 101 | 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) · 173 unmodified lines | ||
| 276 | 282 | { |
| 277 | 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 | 286 | regs.ebx = (fan & 0xff) | (speed << 8); |
| 281 | 287 | |
| 282 | 288 | return i8k_smm(®s) ? : i8k_get_fan_status(fan); |
| @@ -521,7 +527,7 @@ static ssize_t i8k_hwmon_show_pwm(struct device *dev, · 238 unmodified lines | ||
| 521 | 527 | status = i8k_get_fan_status(index); |
| 522 | 528 | if (status < 0) |
| 523 | 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 | 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, · 7 unmodified lines | ||
| 535 | 541 | err = kstrtoul(buf, 10, &val); |
| 536 | 542 | if (err) |
| 537 | 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 | 546 | mutex_lock(&i8k_mutex); |
| 541 | 547 | err = i8k_set_fan(index, val); |
| @@ -544,20 +550,6 @@ static ssize_t i8k_hwmon_set_pwm(struct device *dev, · 2 unmodified lines | ||
| 544 | 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 | 553 | static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 0); |
| 562 | 554 | static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 1); |
| 563 | 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, · 6 unmodified lines | ||
| 570 | 562 | I8K_FAN_RIGHT); |
| 571 | 563 | static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, i8k_hwmon_show_pwm, |
| 572 | 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 | 566 | static struct attribute *i8k_attrs[] = { |
| 578 | 567 | &sensor_dev_attr_temp1_input.dev_attr.attr, /* 0 */ |
| 579 | − &sensor_dev_attr_temp1_label.dev_attr.attr, /* 1 */ | |
| 580 | − &sensor_dev_attr_temp2_input.dev_attr.attr, /* 2 */ | |
| 581 | − &sensor_dev_attr_temp3_input.dev_attr.attr, /* 3 */ | |
| 582 | − &sensor_dev_attr_temp4_input.dev_attr.attr, /* 4 */ | |
| 583 | − &sensor_dev_attr_fan1_input.dev_attr.attr, /* 5 */ | |
| 584 | − &sensor_dev_attr_pwm1.dev_attr.attr, /* 6 */ | |
| 585 | − &sensor_dev_attr_fan1_label.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 */ | |
| 568 | + &sensor_dev_attr_temp2_input.dev_attr.attr, /* 1 */ | |
| 569 | + &sensor_dev_attr_temp3_input.dev_attr.attr, /* 2 */ | |
| 570 | + &sensor_dev_attr_temp4_input.dev_attr.attr, /* 3 */ | |
| 571 | + &sensor_dev_attr_fan1_input.dev_attr.attr, /* 4 */ | |
| 572 | + &sensor_dev_attr_pwm1.dev_attr.attr, /* 5 */ | |
| 573 | + &sensor_dev_attr_fan2_input.dev_attr.attr, /* 6 */ | |
| 574 | + &sensor_dev_attr_pwm2.dev_attr.attr, /* 7 */ | |
| 589 | 575 | NULL |
| 590 | 576 | }; |
| 591 | 577 | |
| 592 | 578 | static umode_t i8k_is_visible(struct kobject *kobj, struct attribute *attr, |
| 593 | 579 | int index) |
| 594 | 580 | { |
| 595 | − if ((index == 0 || index == 1) && | |
| 596 | − !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP1)) | |
| 581 | + if (index == 0 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP1)) | |
| 597 | 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 | 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 | 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 | 588 | return 0; |
| 604 | − if (index >= 5 && index <= 7 && | |
| 589 | + if (index >= 4 && index <= 5 && | |
| 605 | 590 | !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN1)) |
| 606 | 591 | return 0; |
| 607 | − if (index >= 8 && index <= 10 && | |
| 592 | + if (index >= 6 && index <= 7 && | |
| 608 | 593 | !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN2)) |
| 609 | 594 | return 0; |
| 610 | 595 | |
| @@ -659,6 +644,37 @@ static int __init i8k_init_hwmon(void) · 48 unmodified lines | ||
| 659 | 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 | 678 | static struct dmi_system_id i8k_dmi_table[] __initdata = { |
| 663 | 679 | { |
| 664 | 680 | .ident = "Dell Inspiron", |
| @@ -681,6 +697,14 @@ static struct dmi_system_id i8k_dmi_table[] __initdata = { · 16 unmodified lines | ||
| 681 | 697 | DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"), |
| 682 | 698 | }, |
| 683 | 699 | }, |
| 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 | + }, | |
| 684 | 708 | { |
| 685 | 709 | .ident = "Dell Latitude 2", |
| 686 | 710 | .matches = { |
| @@ -702,6 +726,15 @@ static struct dmi_system_id i8k_dmi_table[] __initdata = { · 15 unmodified lines | ||
| 702 | 726 | DMI_MATCH(DMI_PRODUCT_NAME, "MP061"), |
| 703 | 727 | }, |
| 704 | 728 | }, |
| 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 | + }, | |
| 705 | 738 | { |
| 706 | 739 | .ident = "Dell Precision", |
| 707 | 740 | .matches = { |
| @@ -729,7 +762,7 @@ static struct dmi_system_id i8k_dmi_table[] __initdata = { · 21 unmodified lines | ||
| 729 | 762 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), |
| 730 | 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 | 768 | .ident = "Dell XPS M140", |
| @@ -737,7 +770,7 @@ static struct dmi_system_id i8k_dmi_table[] __initdata = { · 1 unmodified line | ||
| 737 | 770 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), |
| 738 | 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) · 33 unmodified lines | ||
| 777 | 810 | } |
| 778 | 811 | |
| 779 | 812 | i8k_fan_mult = fan_mult; |
| 813 | + i8k_fan_max = fan_max ? : I8K_FAN_HIGH; /* Must not be 0 */ | |
| 780 | 814 | id = dmi_first_match(i8k_dmi_table); |
| 781 | − if (id && fan_mult == I8K_FAN_MULT && id->driver_data) | |
| 782 | − i8k_fan_mult = (unsigned long)id->driver_data; | |
| 815 | + if (id && 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 | 825 | return 0; |
| 785 | 826 | } |
| @@ -661,6 +661,7 @@ static int hwicap_setup(struct device *dev, int id, · 660 unmodified lines | ||
| 661 | 661 | drvdata->base_address = ioremap(drvdata->mem_start, drvdata->mem_size); |
| 662 | 662 | if (!drvdata->base_address) { |
| 663 | 663 | dev_err(dev, "ioremap() failed\n"); |
| 664 | + retval = -ENOMEM; | |
| 664 | 665 | goto failed2; |
| 665 | 666 | } |
| 666 | 667 | |
| @@ -33,6 +33,24 @@ if DMADEVICES · 32 unmodified lines | ||
| 33 | 33 | |
| 34 | 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 | 54 | config INTEL_MID_DMAC |
| 37 | 55 | tristate "Intel MID DMA support for Peripheral DMA controllers" |
| 38 | 56 | depends on PCI && X86 |
| @@ -47,3 +47,4 @@ obj-$(CONFIG_MOXART_DMA) += moxart-dma.o · 46 unmodified lines | ||
| 47 | 47 | obj-$(CONFIG_FSL_EDMA) += fsl-edma.o |
| 48 | 48 | obj-$(CONFIG_QCOM_BAM_DMA) += qcom_bam_dma.o |
| 49 | 49 | obj-y += xilinx/ |
| 50 | +obj-$(CONFIG_INTEL_MIC_X100_DMA) += mic_x100_dma.o | |
| @@ -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"); | |
| @@ -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 | |
| @@ -14,6 +14,20 @@ if EXTCON · 13 unmodified lines | ||
| 14 | 14 | |
| 15 | 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 | 31 | config EXTCON_GPIO |
| 18 | 32 | tristate "GPIO extcon support" |
| 19 | 33 | depends on GPIOLIB |
| @@ -21,12 +35,6 @@ config EXTCON_GPIO · 1 unmodified line | ||
| 21 | 35 | Say Y here to enable GPIO based extcon support. Note that GPIO |
| 22 | 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 | 38 | config EXTCON_MAX14577 |
| 31 | 39 | tristate "MAX14577/77836 EXTCON Support" |
| 32 | 40 | depends on MFD_MAX14577 |
| @@ -55,14 +63,6 @@ config EXTCON_MAX8997 · 22 unmodified lines | ||
| 55 | 63 | Maxim MAX8997 PMIC. The MAX8997 MUIC is a USB port accessory |
| 56 | 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 | 66 | config EXTCON_PALMAS |
| 67 | 67 | tristate "Palmas USB EXTCON support" |
| 68 | 68 | depends on MFD_PALMAS |
| @@ -70,4 +70,14 @@ config EXTCON_PALMAS · 1 unmodified line | ||
| 70 | 70 | Say Y here to enable support for USB peripheral and USB host |
| 71 | 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 | 83 | endif # MULTISTATE_SWITCH |
| @@ -1,12 +1,13 @@ | ||
| 1 | −# | |
| 1 | + | |
| 2 | 2 | # Makefile for external connector class (extcon) devices |
| 3 | 3 | # |
| 4 | 4 | |
| 5 | 5 | obj-$(CONFIG_EXTCON) += extcon-class.o |
| 6 | −obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o | |
| 7 | 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 | 9 | obj-$(CONFIG_EXTCON_MAX14577) += extcon-max14577.o |
| 9 | 10 | obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o |
| 10 | 11 | obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o |
| 11 | −obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o | |
| 12 | 12 | obj-$(CONFIG_EXTCON_PALMAS) += extcon-palmas.o |
| 13 | +obj-$(CONFIG_EXTCON_SM5502) += extcon-sm5502.o | |
| @@ -112,7 +112,6 @@ static int adc_jack_probe(struct platform_device *pdev) · 111 unmodified lines | ||
| 112 | 112 | dev_err(&pdev->dev, "failed to allocate extcon device\n"); |
| 113 | 113 | return -ENOMEM; |
| 114 | 114 | } |
| 115 | − data->edev->dev.parent = &pdev->dev; | |
| 116 | 115 | data->edev->name = pdata->name; |
| 117 | 116 | |
| 118 | 117 | /* Check the length of array and set num_cables */ |
| @@ -39,6 +39,11 @@ · 38 unmodified lines | ||
| 39 | 39 | #define ARIZONA_ACCDET_MODE_HPL 1 |
| 40 | 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 | 47 | #define ARIZONA_HPDET_MAX 10000 |
| 43 | 48 | |
| 44 | 49 | #define HPDET_DEBOUNCE 500 |
| @@ -324,14 +329,17 @@ static void arizona_stop_mic(struct arizona_extcon_info *info) · 279 unmodified lines | ||
| 324 | 329 | } |
| 325 | 330 | |
| 326 | 331 | static struct { |
| 332 | + unsigned int threshold; | |
| 327 | 333 | unsigned int factor_a; |
| 328 | 334 | unsigned int factor_b; |
| 329 | 335 | } arizona_hpdet_b_ranges[] = { |
| 330 | − { 5528, 362464 }, | |
| 331 | − { 11084, 6186851 }, | |
| 332 | − { 11065, 65460395 }, | |
| 336 | + { 100, 5528, 362464 }, | |
| 337 | + { 169, 11084, 6186851 }, | |
| 338 | + { 169, 11065, 65460395 }, | |
| 333 | 339 | }; |
| 334 | 340 | |
| 341 | +#define ARIZONA_HPDET_B_RANGE_MAX 0x3fb | |
| 342 | + | |
| 335 | 343 | static struct { |
| 336 | 344 | int min; |
| 337 | 345 | int max; |
| @@ -386,7 +394,8 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info) · 48 unmodified lines | ||
| 386 | 394 | >> ARIZONA_HP_IMPEDANCE_RANGE_SHIFT; |
| 387 | 395 | |
| 388 | 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 | 399 | range++; |
| 391 | 400 | dev_dbg(arizona->dev, "Moving to HPDET range %d\n", |
| 392 | 401 | range); |
| @@ -399,7 +408,8 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info) · 6 unmodified lines | ||
| 399 | 408 | } |
| 400 | 409 | |
| 401 | 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 | 413 | dev_dbg(arizona->dev, "Measurement out of range\n"); |
| 404 | 414 | return ARIZONA_HPDET_MAX; |
| 405 | 415 | } |
| @@ -664,9 +674,8 @@ err: · 258 unmodified lines | ||
| 664 | 674 | ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC); |
| 665 | 675 | |
| 666 | 676 | /* Just report headphone */ |
| 667 | − ret = extcon_update_state(info->edev, | |
| 668 | − 1 << ARIZONA_CABLE_HEADPHONE, | |
| 669 | − 1 << ARIZONA_CABLE_HEADPHONE); | |
| 677 | + ret = extcon_set_cable_state_(info->edev, | |
| 678 | + ARIZONA_CABLE_HEADPHONE, true); | |
| 670 | 679 | if (ret != 0) |
| 671 | 680 | dev_err(arizona->dev, "Failed to report headphone: %d\n", ret); |
| 672 | 681 | |
| @@ -723,9 +732,8 @@ err: · 50 unmodified lines | ||
| 723 | 732 | ARIZONA_ACCDET_MODE_MASK, ARIZONA_ACCDET_MODE_MIC); |
| 724 | 733 | |
| 725 | 734 | /* Just report headphone */ |
| 726 | − ret = extcon_update_state(info->edev, | |
| 727 | − 1 << ARIZONA_CABLE_HEADPHONE, | |
| 728 | − 1 << ARIZONA_CABLE_HEADPHONE); | |
| 735 | + ret = extcon_set_cable_state_(info->edev, | |
| 736 | + ARIZONA_CABLE_HEADPHONE, true); | |
| 729 | 737 | if (ret != 0) |
| 730 | 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) · 80 unmodified lines | ||
| 812 | 820 | if (info->detecting && (val & ARIZONA_MICD_LVL_8)) { |
| 813 | 821 | arizona_identify_headphone(info); |
| 814 | 822 | |
| 815 | − ret = extcon_update_state(info->edev, | |
| 816 | − 1 << ARIZONA_CABLE_MICROPHONE, | |
| 817 | − 1 << ARIZONA_CABLE_MICROPHONE); | |
| 823 | + ret = extcon_set_cable_state_(info->edev, | |
| 824 | + ARIZONA_CABLE_MICROPHONE, true); | |
| 818 | 825 | |
| 819 | 826 | if (ret != 0) |
| 820 | 827 | dev_err(arizona->dev, "Headset report failed: %d\n", |
| 821 | 828 | ret); |
| 822 | 829 | |
| 823 | 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 | 832 | if (ret != 0) { |
| 826 | 833 | dev_err(arizona->dev, "Failed to bypass MICVDD: %d\n", |
| 827 | 834 | ret); |
| @@ -962,10 +969,16 @@ static irqreturn_t arizona_jackdet(int irq, void *data) · 134 unmodified lines | ||
| 962 | 969 | |
| 963 | 970 | if (arizona->pdata.jd_gpio5) { |
| 964 | 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 | 976 | } else { |
| 967 | 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 | 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) · 124 unmodified lines | ||
| 1096 | 1109 | struct arizona_pdata *pdata = &arizona->pdata; |
| 1097 | 1110 | struct arizona_extcon_info *info; |
| 1098 | 1111 | unsigned int val; |
| 1112 | + unsigned int clamp_mode; | |
| 1099 | 1113 | int jack_irq_fall, jack_irq_rise; |
| 1100 | 1114 | int ret, mode, i, j; |
| 1101 | 1115 | |
| @@ -1103,12 +1117,10 @@ static int arizona_extcon_probe(struct platform_device *pdev) · 1 unmodified line | ||
| 1103 | 1117 | return -EPROBE_DEFER; |
| 1104 | 1118 | |
| 1105 | 1119 | info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); |
| 1106 | − if (!info) { | |
| 1107 | − dev_err(&pdev->dev, "Failed to allocate memory\n"); | |
| 1120 | + if (!info) | |
| 1108 | 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 | 1124 | if (IS_ERR(info->micvdd)) { |
| 1113 | 1125 | ret = PTR_ERR(info->micvdd); |
| 1114 | 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) · 41 unmodified lines | ||
| 1156 | 1168 | return -ENOMEM; |
| 1157 | 1169 | } |
| 1158 | 1170 | info->edev->name = "Headset Jack"; |
| 1159 | − info->edev->dev.parent = arizona->dev; | |
| 1160 | 1171 | |
| 1161 | 1172 | ret = devm_extcon_dev_register(&pdev->dev, info->edev); |
| 1162 | 1173 | if (ret < 0) { |
| @@ -1174,7 +1185,6 @@ static int arizona_extcon_probe(struct platform_device *pdev) · 11 unmodified lines | ||
| 1174 | 1185 | |
| 1175 | 1186 | info->input->name = "Headset"; |
| 1176 | 1187 | info->input->phys = "arizona/extcon"; |
| 1177 | − info->input->dev.parent = &pdev->dev; | |
| 1178 | 1188 | |
| 1179 | 1189 | if (pdata->num_micd_configs) { |
| 1180 | 1190 | info->micd_modes = pdata->micd_configs; |
| @@ -1305,15 +1315,21 @@ static int arizona_extcon_probe(struct platform_device *pdev) · 124 unmodified lines | ||
| 1305 | 1315 | regmap_write(arizona->regmap, ARIZONA_GPIO5_CTRL, |
| 1306 | 1316 | val); |
| 1307 | 1317 | |
| 1308 | − regmap_update_bits(arizona->regmap, | |
| 1309 | − ARIZONA_MICD_CLAMP_CONTROL, | |
| 1310 | − ARIZONA_MICD_CLAMP_MODE_MASK, 0x9); | |
| 1318 | + if (arizona->pdata.jd_invert) | |
| 1319 | + clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDH_GP5H; | |
| 1320 | + else | |
| 1321 | + clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDL_GP5H; | |
| 1311 | 1322 | } else { |
| 1312 | − regmap_update_bits(arizona->regmap, | |
| 1313 | − ARIZONA_MICD_CLAMP_CONTROL, | |
| 1314 | − ARIZONA_MICD_CLAMP_MODE_MASK, 0x4); | |
| 1323 | + if (arizona->pdata.jd_invert) | |
| 1324 | + clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDH; | |
| 1325 | + else | |
| 1326 | + clamp_mode = ARIZONA_MICD_CLAMP_MODE_JDL; | |
| 1315 | 1327 | } |
| 1316 | 1328 | |
| 1329 | + regmap_update_bits(arizona->regmap, | |
| 1330 | + ARIZONA_MICD_CLAMP_CONTROL, | |
| 1331 | + ARIZONA_MICD_CLAMP_MODE_MASK, clamp_mode); | |
| 1332 | + | |
| 1317 | 1333 | regmap_update_bits(arizona->regmap, |
| 1318 | 1334 | ARIZONA_JACK_DETECT_DEBOUNCE, |
| 1319 | 1335 | ARIZONA_MICD_CLAMP_DB, |
| @@ -645,6 +645,8 @@ struct extcon_dev *devm_extcon_dev_allocate(struct device *dev, · 644 unmodified lines | ||
| 645 | 645 | return edev; |
| 646 | 646 | } |
| 647 | 647 | |
| 648 | + edev->dev.parent = dev; | |
| 649 | + | |
| 648 | 650 | *ptr = edev; |
| 649 | 651 | devres_add(dev, ptr); |
| 650 | 652 | |
| @@ -105,7 +105,6 @@ static int gpio_extcon_probe(struct platform_device *pdev) · 104 unmodified lines | ||
| 105 | 105 | return -ENOMEM; |
| 106 | 106 | } |
| 107 | 107 | extcon_data->edev->name = pdata->name; |
| 108 | − extcon_data->edev->dev.parent = &pdev->dev; | |
| 109 | 108 | |
| 110 | 109 | extcon_data->gpio = pdata->gpio; |
| 111 | 110 | extcon_data->gpio_active_low = pdata->gpio_active_low; |
| @@ -692,10 +692,9 @@ static int max14577_muic_probe(struct platform_device *pdev) · 691 unmodified lines | ||
| 692 | 692 | u8 id; |
| 693 | 693 | |
| 694 | 694 | info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); |
| 695 | − if (!info) { | |
| 696 | − dev_err(&pdev->dev, "failed to allocate memory\n"); | |
| 695 | + if (!info) | |
| 697 | 696 | return -ENOMEM; |
| 698 | − } | |
| 697 | + | |
| 699 | 698 | info->dev = &pdev->dev; |
| 700 | 699 | info->max14577 = max14577; |
| 701 | 700 | |
| @@ -255,10 +255,10 @@ static int max77693_muic_set_debounce_time(struct max77693_muic_info *info, · 254 unmodified lines | ||
| 255 | 255 | case ADC_DEBOUNCE_TIME_10MS: |
| 256 | 256 | case ADC_DEBOUNCE_TIME_25MS: |
| 257 | 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 | 259 | MAX77693_MUIC_REG_CTRL3, |
| 260 | − time << CONTROL3_ADCDBSET_SHIFT, | |
| 261 | − CONTROL3_ADCDBSET_MASK); | |
| 260 | + CONTROL3_ADCDBSET_MASK, | |
| 261 | + time << CONTROL3_ADCDBSET_SHIFT); | |
| 262 | 262 | if (ret) { |
| 263 | 263 | dev_err(info->dev, "failed to set ADC debounce time\n"); |
| 264 | 264 | return ret; |
| @@ -286,15 +286,15 @@ static int max77693_muic_set_path(struct max77693_muic_info *info, · 21 unmodified lines | ||
| 286 | 286 | u8 val, bool attached) |
| 287 | 287 | { |
| 288 | 288 | int ret = 0; |
| 289 | − u8 ctrl1, ctrl2 = 0; | |
| 289 | + unsigned int ctrl1, ctrl2 = 0; | |
| 290 | 290 | |
| 291 | 291 | if (attached) |
| 292 | 292 | ctrl1 = val; |
| 293 | 293 | else |
| 294 | 294 | ctrl1 = CONTROL1_SW_OPEN; |
| 295 | 295 | |
| 296 | − ret = max77693_update_reg(info->max77693->regmap_muic, | |
| 297 | − MAX77693_MUIC_REG_CTRL1, ctrl1, COMP_SW_MASK); | |
| 296 | + ret = regmap_update_bits(info->max77693->regmap_muic, | |
| 297 | + MAX77693_MUIC_REG_CTRL1, COMP_SW_MASK, ctrl1); | |
| 298 | 298 | if (ret < 0) { |
| 299 | 299 | dev_err(info->dev, "failed to update MUIC register\n"); |
| 300 | 300 | return ret; |
| @@ -305,9 +305,9 @@ static int max77693_muic_set_path(struct max77693_muic_info *info, · 4 unmodified lines | ||
| 305 | 305 | else |
| 306 | 306 | ctrl2 |= CONTROL2_LOWPWR_MASK; /* LowPwr=1, CPEn=0 */ |
| 307 | 307 | |
| 308 | − ret = max77693_update_reg(info->max77693->regmap_muic, | |
| 309 | − MAX77693_MUIC_REG_CTRL2, ctrl2, | |
| 310 | − CONTROL2_LOWPWR_MASK | CONTROL2_CPEN_MASK); | |
| 308 | + ret = regmap_update_bits(info->max77693->regmap_muic, | |
| 309 | + MAX77693_MUIC_REG_CTRL2, | |
| 310 | + CONTROL2_LOWPWR_MASK | CONTROL2_CPEN_MASK, ctrl2); | |
| 311 | 311 | if (ret < 0) { |
| 312 | 312 | dev_err(info->dev, "failed to update MUIC register\n"); |
| 313 | 313 | return ret; |
| @@ -969,8 +969,8 @@ static void max77693_muic_irq_work(struct work_struct *work) · 655 unmodified lines | ||
| 969 | 969 | if (info->irq == muic_irqs[i].virq) |
| 970 | 970 | irq_type = muic_irqs[i].irq; |
| 971 | 971 | |
| 972 | − ret = max77693_bulk_read(info->max77693->regmap_muic, | |
| 973 | − MAX77693_MUIC_REG_STATUS1, 2, info->status); | |
| 972 | + ret = regmap_bulk_read(info->max77693->regmap_muic, | |
| 973 | + MAX77693_MUIC_REG_STATUS1, info->status, 2); | |
| 974 | 974 | if (ret) { |
| 975 | 975 | dev_err(info->dev, "failed to read MUIC register\n"); |
| 976 | 976 | mutex_unlock(&info->mutex); |
| @@ -1042,8 +1042,8 @@ static int max77693_muic_detect_accessory(struct max77693_muic_info *info) · 65 unmodified lines | ||
| 1042 | 1042 | mutex_lock(&info->mutex); |
| 1043 | 1043 | |
| 1044 | 1044 | /* Read STATUSx register to detect accessory */ |
| 1045 | − ret = max77693_bulk_read(info->max77693->regmap_muic, | |
| 1046 | − MAX77693_MUIC_REG_STATUS1, 2, info->status); | |
| 1045 | + ret = regmap_bulk_read(info->max77693->regmap_muic, | |
| 1046 | + MAX77693_MUIC_REG_STATUS1, info->status, 2); | |
| 1047 | 1047 | if (ret) { |
| 1048 | 1048 | dev_err(info->dev, "failed to read MUIC register\n"); |
| 1049 | 1049 | mutex_unlock(&info->mutex); |
| @@ -1095,14 +1095,13 @@ static int max77693_muic_probe(struct platform_device *pdev) · 45 unmodified lines | ||
| 1095 | 1095 | int delay_jiffies; |
| 1096 | 1096 | int ret; |
| 1097 | 1097 | int i; |
| 1098 | − u8 id; | |
| 1098 | + unsigned int id; | |
| 1099 | 1099 | |
| 1100 | 1100 | info = devm_kzalloc(&pdev->dev, sizeof(struct max77693_muic_info), |
| 1101 | 1101 | GFP_KERNEL); |
| 1102 | − if (!info) { | |
| 1103 | − dev_err(&pdev->dev, "failed to allocate memory\n"); | |
| 1102 | + if (!info) | |
| 1104 | 1103 | return -ENOMEM; |
| 1105 | − } | |
| 1104 | + | |
| 1106 | 1105 | info->dev = &pdev->dev; |
| 1107 | 1106 | info->max77693 = max77693; |
| 1108 | 1107 | if (info->max77693->regmap_muic) { |
| @@ -1154,7 +1153,8 @@ static int max77693_muic_probe(struct platform_device *pdev) · 45 unmodified lines | ||
| 1154 | 1153 | struct max77693_muic_irq *muic_irq = &muic_irqs[i]; |
| 1155 | 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 | 1158 | if (!virq) { |
| 1159 | 1159 | ret = -EINVAL; |
| 1160 | 1160 | goto err_irq; |
| @@ -1183,7 +1183,6 @@ static int max77693_muic_probe(struct platform_device *pdev) · 22 unmodified lines | ||
| 1183 | 1183 | goto err_irq; |
| 1184 | 1184 | } |
| 1185 | 1185 | info->edev->name = DEV_NAME; |
| 1186 | − info->edev->dev.parent = &pdev->dev; | |
| 1187 | 1186 | |
| 1188 | 1187 | ret = devm_extcon_dev_register(&pdev->dev, info->edev); |
| 1189 | 1188 | if (ret) { |
| @@ -1204,7 +1203,7 @@ static int max77693_muic_probe(struct platform_device *pdev) · 14 unmodified lines | ||
| 1204 | 1203 | enum max77693_irq_source irq_src |
| 1205 | 1204 | = MAX77693_IRQ_GROUP_NR; |
| 1206 | 1205 | |
| 1207 | − max77693_write_reg(info->max77693->regmap_muic, | |
| 1206 | + regmap_write(info->max77693->regmap_muic, | |
| 1208 | 1207 | init_data[i].addr, |
| 1209 | 1208 | init_data[i].data); |
| 1210 | 1209 | |
| @@ -1262,7 +1261,7 @@ static int max77693_muic_probe(struct platform_device *pdev) · 51 unmodified lines | ||
| 1262 | 1261 | max77693_muic_set_path(info, info->path_uart, true); |
| 1263 | 1262 | |
| 1264 | 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 | 1265 | MAX77693_MUIC_REG_ID, &id); |
| 1267 | 1266 | if (ret < 0) { |
| 1268 | 1267 | dev_err(&pdev->dev, "failed to read revision number\n"); |
| @@ -661,10 +661,8 @@ static int max8997_muic_probe(struct platform_device *pdev) · 660 unmodified lines | ||
| 661 | 661 | |
| 662 | 662 | info = devm_kzalloc(&pdev->dev, sizeof(struct max8997_muic_info), |
| 663 | 663 | GFP_KERNEL); |
| 664 | − if (!info) { | |
| 665 | − dev_err(&pdev->dev, "failed to allocate memory\n"); | |
| 664 | + if (!info) | |
| 666 | 665 | return -ENOMEM; |
| 667 | − } | |
| 668 | 666 | |
| 669 | 667 | info->dev = &pdev->dev; |
| 670 | 668 | info->muic = max8997->muic; |
| @@ -706,7 +704,6 @@ static int max8997_muic_probe(struct platform_device *pdev) · 35 unmodified lines | ||
| 706 | 704 | goto err_irq; |
| 707 | 705 | } |
| 708 | 706 | info->edev->name = DEV_NAME; |
| 709 | − info->edev->dev.parent = &pdev->dev; | |
| 710 | 707 | |
| 711 | 708 | ret = devm_extcon_dev_register(&pdev->dev, info->edev); |
| 712 | 709 | if (ret) { |
| @@ -194,7 +194,6 @@ static int palmas_usb_probe(struct platform_device *pdev) · 193 unmodified lines | ||
| 194 | 194 | return -ENOMEM; |
| 195 | 195 | } |
| 196 | 196 | palmas_usb->edev->name = kstrdup(node->name, GFP_KERNEL); |
| 197 | − palmas_usb->edev->dev.parent = palmas_usb->dev; | |
| 198 | 197 | palmas_usb->edev->mutually_exclusive = mutually_exclusive; |
| 199 | 198 | |
| 200 | 199 | status = devm_extcon_dev_register(&pdev->dev, palmas_usb->edev); |
| @@ -278,7 +277,7 @@ static int palmas_usb_resume(struct device *dev) · 77 unmodified lines | ||
| 278 | 277 | |
| 279 | 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 | 281 | { .compatible = "ti,palmas-usb", }, |
| 283 | 282 | { .compatible = "ti,palmas-usb-vid", }, |
| 284 | 283 | { .compatible = "ti,twl6035-usb", }, |
| @@ -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"); | |
| @@ -808,12 +808,8 @@ int vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer, · 807 unmodified lines | ||
| 808 | 808 | |
| 809 | 809 | *buffer_actual_len = packetlen; |
| 810 | 810 | |
| 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); | |
| 811 | + if (packetlen > bufferlen) | |
| 815 | 812 | return -ENOBUFS; |
| 816 | − } | |
| 817 | 813 | |
| 818 | 814 | *requestid = desc.trans_id; |
| 819 | 815 | |
| @@ -618,7 +618,7 @@ static void tpci200_pci_remove(struct pci_dev *dev) · 617 unmodified lines | ||
| 618 | 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 | 622 | { TPCI200_VENDOR_ID, TPCI200_DEVICE_ID, TPCI200_SUBVENDOR_ID, |
| 623 | 623 | TPCI200_SUBDEVICE_ID }, |
| 624 | 624 | { 0, }, |
| @@ -177,19 +177,20 @@ static void ipoctal_irq_tx(struct ipoctal_channel *channel) · 176 unmodified lines | ||
| 177 | 177 | if (channel->nb_bytes == 0) |
| 178 | 178 | return; |
| 179 | 179 | |
| 180 | + spin_lock(&channel->lock); | |
| 180 | 181 | value = channel->tty_port.xmit_buf[*pointer_write]; |
| 181 | 182 | iowrite8(value, &channel->regs->w.thr); |
| 182 | 183 | channel->stats.tx++; |
| 183 | 184 | (*pointer_write)++; |
| 184 | 185 | *pointer_write = *pointer_write % PAGE_SIZE; |
| 185 | 186 | channel->nb_bytes--; |
| 187 | + spin_unlock(&channel->lock); | |
| 186 | 188 | } |
| 187 | 189 | |
| 188 | 190 | static void ipoctal_irq_channel(struct ipoctal_channel *channel) |
| 189 | 191 | { |
| 190 | 192 | u8 isr, sr; |
| 191 | 193 | |
| 192 | − spin_lock(&channel->lock); | |
| 193 | 194 | /* The HW is organized in pair of channels. See which register we need |
| 194 | 195 | * to read from */ |
| 195 | 196 | isr = ioread8(&channel->block_regs->r.isr); |
| @@ -213,8 +214,6 @@ static void ipoctal_irq_channel(struct ipoctal_channel *channel) · 17 unmodified lines | ||
| 213 | 214 | /* TX of each character */ |
| 214 | 215 | if ((isr & channel->isr_tx_rdy_mask) && (sr & SR_TX_READY)) |
| 215 | 216 | ipoctal_irq_tx(channel); |
| 216 | − | |
| 217 | − spin_unlock(&channel->lock); | |
| 218 | 217 | } |
| 219 | 218 | |
| 220 | 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, · 103 unmodified lines | ||
| 324 | 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 | 326 | /* Dummy write */ |
| 335 | 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, · 54 unmodified lines | ||
| 391 | 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 | 394 | return 0; |
| 395 | 395 | } |
| 396 | 396 | |
| @@ -384,6 +384,7 @@ config MFD_MAX77693 · 383 unmodified lines | ||
| 384 | 384 | depends on I2C=y |
| 385 | 385 | select MFD_CORE |
| 386 | 386 | select REGMAP_I2C |
| 387 | + select REGMAP_IRQ | |
| 387 | 388 | help |
| 388 | 389 | Say yes here to add support for Maxim Semiconductor MAX77693. |
| 389 | 390 | This is a companion Power Management IC with Flash, Haptic, Charger, |
| @@ -116,7 +116,7 @@ obj-$(CONFIG_MFD_DA9063) += da9063.o · 115 unmodified lines | ||
| 116 | 116 | |
| 117 | 117 | obj-$(CONFIG_MFD_MAX14577) += max14577.o |
| 118 | 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 | 120 | obj-$(CONFIG_MFD_MAX8907) += max8907.o |
| 121 | 121 | max8925-objs := max8925-core.o max8925-i2c.o |
| 122 | 122 | obj-$(CONFIG_MFD_MAX8925) += max8925.o |
| @@ -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 | −} | |
| @@ -49,62 +49,62 @@ static const struct mfd_cell max77693_devs[] = { · 48 unmodified lines | ||
| 49 | 49 | { .name = "max77693-haptic", }, |
| 50 | 50 | }; |
| 51 | 51 | |
| 52 | −int max77693_read_reg(struct regmap *map, u8 reg, u8 *dest) | |
| 53 | −{ | |
| 54 | − unsigned int val; | |
| 55 | − int ret; | |
| 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; | |
| 52 | +static const struct regmap_config max77693_regmap_config = { | |
| 53 | + .reg_bits = 8, | |
| 54 | + .val_bits = 8, | |
| 55 | + .max_register = MAX77693_PMIC_REG_END, | |
| 56 | +}; | |
| 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; | |
| 91 | −} | |
| 92 | −EXPORT_SYMBOL_GPL(max77693_bulk_write); | |
| 66 | +static const struct regmap_irq_chip max77693_led_irq_chip = { | |
| 67 | + .name = "max77693-led", | |
| 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) | |
| 95 | −{ | |
| 96 | − int ret; | |
| 76 | +static const struct regmap_irq max77693_topsys_irqs[] = { | |
| 77 | + { .mask = TOPSYS_IRQ_T120C_INT, }, | |
| 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; | |
| 101 | −} | |
| 102 | −EXPORT_SYMBOL_GPL(max77693_update_reg); | |
| 92 | +static const struct regmap_irq max77693_charger_irqs[] = { | |
| 93 | + { .mask = CHG_IRQ_BYP_I, }, | |
| 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 = { | |
| 105 | − .reg_bits = 8, | |
| 106 | − .val_bits = 8, | |
| 107 | − .max_register = MAX77693_PMIC_REG_END, | |
| 100 | +static const struct regmap_irq_chip max77693_charger_irq_chip = { | |
| 101 | + .name = "max77693-charger", | |
| 102 | + .status_base = MAX77693_CHG_REG_CHG_INT, | |
| 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 | 110 | static const struct regmap_config max77693_regmap_muic_config = { |
| @@ -113,11 +113,42 @@ static const struct regmap_config max77693_regmap_muic_config = { · 2 unmodified lines | ||
| 113 | 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 | 147 | static int max77693_i2c_probe(struct i2c_client *i2c, |
| 117 | 148 | const struct i2c_device_id *id) |
| 118 | 149 | { |
| 119 | 150 | struct max77693_dev *max77693; |
| 120 | − u8 reg_data; | |
| 151 | + unsigned int reg_data; | |
| 121 | 152 | int ret = 0; |
| 122 | 153 | |
| 123 | 154 | max77693 = devm_kzalloc(&i2c->dev, |
| @@ -139,7 +170,7 @@ static int max77693_i2c_probe(struct i2c_client *i2c, · 15 unmodified lines | ||
| 139 | 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 | 174 | ®_data); |
| 144 | 175 | if (ret < 0) { |
| 145 | 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, · 30 unmodified lines | ||
| 176 | 207 | goto err_regmap_muic; |
| 177 | 208 | } |
| 178 | 209 | |
| 179 | − ret = max77693_irq_init(max77693); | |
| 180 | − if (ret < 0) | |
| 181 | − goto err_irq; | |
| 210 | + ret = regmap_add_irq_chip(max77693->regmap, max77693->irq, | |
| 211 | + IRQF_ONESHOT | IRQF_SHARED | | |
| 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 | 250 | pm_runtime_set_active(max77693->dev); |
| 184 | 251 | |
| @@ -190,8 +257,14 @@ static int max77693_i2c_probe(struct i2c_client *i2c, · 5 unmodified lines | ||
| 190 | 257 | return ret; |
| 191 | 258 | |
| 192 | 259 | err_mfd: |
| 193 | − max77693_irq_exit(max77693); | |
| 194 | −err_irq: | |
| 260 | + mfd_remove_devices(max77693->dev); | |
| 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 | 268 | err_regmap_muic: |
| 196 | 269 | i2c_unregister_device(max77693->haptic); |
| 197 | 270 | err_i2c_haptic: |
| @@ -204,7 +277,12 @@ static int max77693_i2c_remove(struct i2c_client *i2c) · 6 unmodified lines | ||
| 204 | 277 | struct max77693_dev *max77693 = i2c_get_clientdata(i2c); |
| 205 | 278 | |
| 206 | 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 | 286 | i2c_unregister_device(max77693->muic); |
| 209 | 287 | i2c_unregister_device(max77693->haptic); |
| 210 | 288 | |
| @@ -222,8 +300,11 @@ static int max77693_suspend(struct device *dev) · 11 unmodified lines | ||
| 222 | 300 | struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); |
| 223 | 301 | struct max77693_dev *max77693 = i2c_get_clientdata(i2c); |
| 224 | 302 | |
| 225 | − if (device_may_wakeup(dev)) | |
| 226 | − irq_set_irq_wake(max77693->irq, 1); | |
| 303 | + if (device_may_wakeup(dev)) { | |
| 304 | + enable_irq_wake(max77693->irq); | |
| 305 | + disable_irq(max77693->irq); | |
| 306 | + } | |
| 307 | + | |
| 227 | 308 | return 0; |
| 228 | 309 | } |
| 229 | 310 | |
| @@ -232,9 +313,12 @@ static int max77693_resume(struct device *dev) · 2 unmodified lines | ||
| 232 | 313 | struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); |
| 233 | 314 | struct max77693_dev *max77693 = i2c_get_clientdata(i2c); |
| 234 | 315 | |
| 235 | − if (device_may_wakeup(dev)) | |
| 236 | − irq_set_irq_wake(max77693->irq, 0); | |
| 237 | − return max77693_irq_resume(max77693); | |
| 316 | + if (device_may_wakeup(dev)) { | |
| 317 | + disable_irq_wake(max77693->irq); | |
| 318 | + enable_irq(max77693->irq); | |
| 319 | + } | |
| 320 | + | |
| 321 | + return 0; | |
| 238 | 322 | } |
| 239 | 323 | |
| 240 | 324 | static const struct dev_pm_ops max77693_pm = { |
| @@ -1185,7 +1185,7 @@ static int bh1770_probe(struct i2c_client *client, · 1184 unmodified lines | ||
| 1185 | 1185 | struct bh1770_chip *chip; |
| 1186 | 1186 | int err; |
| 1187 | 1187 | |
| 1188 | − chip = kzalloc(sizeof *chip, GFP_KERNEL); | |
| 1188 | + chip = devm_kzalloc(&client->dev, sizeof *chip, GFP_KERNEL); | |
| 1189 | 1189 | if (!chip) |
| 1190 | 1190 | return -ENOMEM; |
| 1191 | 1191 | |
| @@ -1198,8 +1198,7 @@ static int bh1770_probe(struct i2c_client *client, · 6 unmodified lines | ||
| 1198 | 1198 | |
| 1199 | 1199 | if (client->dev.platform_data == NULL) { |
| 1200 | 1200 | dev_err(&client->dev, "platform data is mandatory\n"); |
| 1201 | − err = -EINVAL; | |
| 1202 | − goto fail1; | |
| 1201 | + return -EINVAL; | |
| 1203 | 1202 | } |
| 1204 | 1203 | |
| 1205 | 1204 | chip->pdata = client->dev.platform_data; |
| @@ -1224,24 +1223,24 @@ static int bh1770_probe(struct i2c_client *client, · 18 unmodified lines | ||
| 1224 | 1223 | chip->regs[0].supply = reg_vcc; |
| 1225 | 1224 | chip->regs[1].supply = reg_vleds; |
| 1226 | 1225 | |
| 1227 | − err = regulator_bulk_get(&client->dev, | |
| 1228 | − ARRAY_SIZE(chip->regs), chip->regs); | |
| 1226 | + err = devm_regulator_bulk_get(&client->dev, | |
| 1227 | + ARRAY_SIZE(chip->regs), chip->regs); | |
| 1229 | 1228 | if (err < 0) { |
| 1230 | 1229 | dev_err(&client->dev, "Cannot get regulators\n"); |
| 1231 | − goto fail1; | |
| 1230 | + return err; | |
| 1232 | 1231 | } |
| 1233 | 1232 | |
| 1234 | 1233 | err = regulator_bulk_enable(ARRAY_SIZE(chip->regs), |
| 1235 | 1234 | chip->regs); |
| 1236 | 1235 | if (err < 0) { |
| 1237 | 1236 | dev_err(&client->dev, "Cannot enable regulators\n"); |
| 1238 | − goto fail2; | |
| 1237 | + return err; | |
| 1239 | 1238 | } |
| 1240 | 1239 | |
| 1241 | 1240 | usleep_range(BH1770_STARTUP_DELAY, BH1770_STARTUP_DELAY * 2); |
| 1242 | 1241 | err = bh1770_detect(chip); |
| 1243 | 1242 | if (err < 0) |
| 1244 | − goto fail3; | |
| 1243 | + goto fail0; | |
| 1245 | 1244 | |
| 1246 | 1245 | /* Start chip */ |
| 1247 | 1246 | bh1770_chip_on(chip); |
| @@ -1252,14 +1251,14 @@ static int bh1770_probe(struct i2c_client *client, · 4 unmodified lines | ||
| 1252 | 1251 | if (chip->lux_corr == 0) { |
| 1253 | 1252 | dev_err(&client->dev, "Improper correction values\n"); |
| 1254 | 1253 | err = -EINVAL; |
| 1255 | − goto fail3; | |
| 1254 | + goto fail0; | |
| 1256 | 1255 | } |
| 1257 | 1256 | |
| 1258 | 1257 | if (chip->pdata->setup_resources) { |
| 1259 | 1258 | err = chip->pdata->setup_resources(); |
| 1260 | 1259 | if (err) { |
| 1261 | 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, · 1 unmodified line | ||
| 1267 | 1266 | &bh1770_attribute_group); |
| 1268 | 1267 | if (err < 0) { |
| 1269 | 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, · 9 unmodified lines | ||
| 1283 | 1282 | if (err) { |
| 1284 | 1283 | dev_err(&client->dev, "could not get IRQ %d\n", |
| 1285 | 1284 | client->irq); |
| 1286 | − goto fail5; | |
| 1285 | + goto fail2; | |
| 1287 | 1286 | } |
| 1288 | 1287 | regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs); |
| 1289 | 1288 | return err; |
| 1290 | −fail5: | |
| 1289 | +fail2: | |
| 1291 | 1290 | sysfs_remove_group(&chip->client->dev.kobj, |
| 1292 | 1291 | &bh1770_attribute_group); |
| 1293 | −fail4: | |
| 1292 | +fail1: | |
| 1294 | 1293 | if (chip->pdata->release_resources) |
| 1295 | 1294 | chip->pdata->release_resources(); |
| 1296 | −fail3: | |
| 1295 | +fail0: | |
| 1297 | 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 | 1297 | return err; |
| 1303 | 1298 | } |
| 1304 | 1299 | |
| @@ -1322,8 +1317,6 @@ static int bh1770_remove(struct i2c_client *client) · 17 unmodified lines | ||
| 1322 | 1317 | pm_runtime_disable(&client->dev); |
| 1323 | 1318 | pm_runtime_set_suspended(&client->dev); |
| 1324 | 1319 | |
| 1325 | − regulator_bulk_free(ARRAY_SIZE(chip->regs), chip->regs); | |
| 1326 | − kfree(chip); | |
| 1327 | 1320 | return 0; |
| 1328 | 1321 | } |
| 1329 | 1322 | |
| @@ -149,50 +149,35 @@ static int bh1780_probe(struct i2c_client *client, · 148 unmodified lines | ||
| 149 | 149 | const struct i2c_device_id *id) |
| 150 | 150 | { |
| 151 | 151 | int ret; |
| 152 | − struct bh1780_data *ddata = NULL; | |
| 152 | + struct bh1780_data *ddata; | |
| 153 | 153 | struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); |
| 154 | 154 | |
| 155 | − if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) { | |
| 156 | − ret = -EIO; | |
| 157 | − goto err_op_failed; | |
| 158 | − } | |
| 155 | + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) | |
| 156 | + return -EIO; | |
| 159 | 157 | |
| 160 | − ddata = kzalloc(sizeof(struct bh1780_data), GFP_KERNEL); | |
| 161 | − if (ddata == NULL) { | |
| 162 | − ret = -ENOMEM; | |
| 163 | − goto err_op_failed; | |
| 164 | − } | |
| 158 | + ddata = devm_kzalloc(&client->dev, sizeof(struct bh1780_data), | |
| 159 | + GFP_KERNEL); | |
| 160 | + if (ddata == NULL) | |
| 161 | + return -ENOMEM; | |
| 165 | 162 | |
| 166 | 163 | ddata->client = client; |
| 167 | 164 | i2c_set_clientdata(client, ddata); |
| 168 | 165 | |
| 169 | 166 | ret = bh1780_read(ddata, BH1780_REG_PARTID, "PART ID"); |
| 170 | 167 | if (ret < 0) |
| 171 | − goto err_op_failed; | |
| 168 | + return ret; | |
| 172 | 169 | |
| 173 | 170 | dev_info(&client->dev, "Ambient Light Sensor, Rev : %d\n", |
| 174 | 171 | (ret & BH1780_REVMASK)); |
| 175 | 172 | |
| 176 | 173 | mutex_init(&ddata->lock); |
| 177 | 174 | |
| 178 | − ret = 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; | |
| 175 | + return sysfs_create_group(&client->dev.kobj, &bh1780_attr_group); | |
| 187 | 176 | } |
| 188 | 177 | |
| 189 | 178 | static int bh1780_remove(struct i2c_client *client) |
| 190 | 179 | { |
| 191 | − struct bh1780_data *ddata; | |
| 192 | − | |
| 193 | − ddata = i2c_get_clientdata(client); | |
| 194 | 180 | sysfs_remove_group(&client->dev.kobj, &bh1780_attr_group); |
| 195 | − kfree(ddata); | |
| 196 | 181 | |
| 197 | 182 | return 0; |
| 198 | 183 | } |
| @@ -954,10 +954,7 @@ static int data_debugfs_init(struct fpga_device *priv) · 953 unmodified lines | ||
| 954 | 954 | { |
| 955 | 955 | priv->dbg_entry = debugfs_create_file(drv_name, S_IRUGO, NULL, priv, |
| 956 | 956 | &data_debug_fops); |
| 957 | − if (IS_ERR(priv->dbg_entry)) | |
| 958 | − return PTR_ERR(priv->dbg_entry); | |
| 959 | − | |
| 960 | − return 0; | |
| 957 | + return PTR_ERR_OR_ZERO(priv->dbg_entry); | |
| 961 | 958 | } |
| 962 | 959 | |
| 963 | 960 | static void data_debugfs_exit(struct fpga_device *priv) |
| @@ -61,3 +61,4 @@ MODULE_LICENSE("GPL"); · 60 unmodified lines | ||
| 61 | 61 | MODULE_AUTHOR("Jiri Kosina"); |
| 62 | 62 | module_param(irq, uint, 0444); |
| 63 | 63 | MODULE_PARM_DESC(irq, "The IRQ to register for"); |
| 64 | +MODULE_DESCRIPTION("Dummy IRQ handler driver"); | |
| @@ -11,3 +11,9 @@ menuconfig GENWQE · 10 unmodified lines | ||
| 11 | 11 | Enables PCIe card driver for IBM GenWQE accelerators. |
| 12 | 12 | The user-space interface is described in |
| 13 | 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 | |
| @@ -38,7 +38,6 @@ · 37 unmodified lines | ||
| 38 | 38 | #include <linux/notifier.h> |
| 39 | 39 | #include <linux/device.h> |
| 40 | 40 | #include <linux/log2.h> |
| 41 | −#include <linux/genwqe/genwqe_card.h> | |
| 42 | 41 | |
| 43 | 42 | #include "card_base.h" |
| 44 | 43 | #include "card_ddcb.h" |
| @@ -58,7 +57,7 @@ static struct dentry *debugfs_genwqe; · 13 unmodified lines | ||
| 58 | 57 | static struct genwqe_dev *genwqe_devices[GENWQE_CARD_NO_MAX]; |
| 59 | 58 | |
| 60 | 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 | 61 | { .vendor = PCI_VENDOR_ID_IBM, |
| 63 | 62 | .device = PCI_DEVICE_GENWQE, |
| 64 | 63 | .subvendor = PCI_SUBVENDOR_ID_IBM, |
| @@ -140,6 +139,12 @@ static struct genwqe_dev *genwqe_dev_alloc(void) · 75 unmodified lines | ||
| 140 | 139 | cd->class_genwqe = class_genwqe; |
| 141 | 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 | 148 | init_waitqueue_head(&cd->queue_waitq); |
| 144 | 149 | |
| 145 | 150 | spin_lock_init(&cd->file_lock); |
| @@ -760,6 +765,124 @@ static u64 genwqe_fir_checking(struct genwqe_dev *cd) · 614 unmodified lines | ||
| 760 | 765 | return IO_ILLEGAL_VALUE; |
| 761 | 766 | } |
| 762 | 767 | |
| 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 | + | |
| 763 | 886 | /** |
| 764 | 887 | * genwqe_health_thread() - Health checking thread |
| 765 | 888 | * |
| @@ -786,6 +909,7 @@ static int genwqe_health_thread(void *data) · 20 unmodified lines | ||
| 786 | 909 | struct pci_dev *pci_dev = cd->pci_dev; |
| 787 | 910 | u64 gfir, gfir_masked, slu_unitcfg, app_unitcfg; |
| 788 | 911 | |
| 912 | + health_thread_begin: | |
| 789 | 913 | while (!kthread_should_stop()) { |
| 790 | 914 | rc = wait_event_interruptible_timeout(cd->health_waitq, |
| 791 | 915 | (genwqe_health_check_cond(cd, &gfir) || |
| @@ -846,6 +970,13 @@ static int genwqe_health_thread(void *data) · 54 unmodified lines | ||
| 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 | 980 | cd->last_gfir = gfir; |
| 850 | 981 | cond_resched(); |
| 851 | 982 | } |
| @@ -853,6 +984,28 @@ static int genwqe_health_thread(void *data) · 1 unmodified line | ||
| 853 | 984 | return 0; |
| 854 | 985 | |
| 855 | 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 | 1009 | dev_err(&pci_dev->dev, |
| 857 | 1010 | "[%s] card unusable. Please trigger unbind!\n", __func__); |
| 858 | 1011 | |
| @@ -958,6 +1111,9 @@ static int genwqe_pci_setup(struct genwqe_dev *cd) · 99 unmodified lines | ||
| 958 | 1111 | pci_set_master(pci_dev); |
| 959 | 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 | 1117 | /* request complete BAR-0 space (length = 0) */ |
| 962 | 1118 | cd->mmio_len = pci_resource_len(pci_dev, 0); |
| 963 | 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, · 132 unmodified lines | ||
| 1096 | 1252 | |
| 1097 | 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 | 1255 | cd = dev_get_drvdata(&pci_dev->dev); |
| 1103 | 1256 | if (cd == NULL) |
| 1104 | − return PCI_ERS_RESULT_NEED_RESET; | |
| 1257 | + return PCI_ERS_RESULT_DISCONNECT; | |
| 1105 | 1258 | |
| 1106 | − switch (state) { | |
| 1107 | − case pci_channel_io_normal: | |
| 1108 | − return PCI_ERS_RESULT_CAN_RECOVER; | |
| 1109 | − case pci_channel_io_frozen: | |
| 1110 | − return PCI_ERS_RESULT_NEED_RESET; | |
| 1111 | − case pci_channel_io_perm_failure: | |
| 1259 | + /* Stop the card */ | |
| 1260 | + genwqe_health_check_stop(cd); | |
| 1261 | + genwqe_stop(cd); | |
| 1262 | + | |
| 1263 | + /* | |
| 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 | 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 | 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) · 1 unmodified line | ||
| 1120 | 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 | 1314 | static int genwqe_sriov_configure(struct pci_dev *dev, int numvfs) |
| @@ -1144,7 +1331,7 @@ static struct pci_error_handlers genwqe_err_handler = { · 16 unmodified lines | ||
| 1144 | 1331 | .error_detected = genwqe_err_error_detected, |
| 1145 | 1332 | .mmio_enabled = genwqe_err_result_none, |
| 1146 | 1333 | .link_reset = genwqe_err_result_none, |
| 1147 | − .slot_reset = genwqe_err_result_none, | |
| 1334 | + .slot_reset = genwqe_err_slot_reset, | |
| 1148 | 1335 | .resume = genwqe_err_resume, |
| 1149 | 1336 | }; |
| 1150 | 1337 | |
| @@ -291,6 +291,8 @@ struct genwqe_dev { · 290 unmodified lines | ||
| 291 | 291 | struct task_struct *health_thread; |
| 292 | 292 | wait_queue_head_t health_waitq; |
| 293 | 293 | |
| 294 | + int use_platform_recovery; /* use platform recovery mechanisms */ | |
| 295 | + | |
| 294 | 296 | /* char device */ |
| 295 | 297 | dev_t devnum_genwqe; /* major/minor num card */ |
| 296 | 298 | struct class *class_genwqe; /* reference to class object */ |
| @@ -1118,7 +1118,21 @@ static irqreturn_t genwqe_pf_isr(int irq, void *dev_id) · 1117 unmodified lines | ||
| 1118 | 1118 | * safer, but slower for the good-case ... See above. |
| 1119 | 1119 | */ |
| 1120 | 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 | 1137 | wake_up_interruptible(&cd->health_waitq); |
| 1124 | 1138 | |
| @@ -1126,12 +1140,12 @@ static irqreturn_t genwqe_pf_isr(int irq, void *dev_id) · 1 unmodified line | ||
| 1126 | 1140 | * By default GFIRs causes recovery actions. This |
| 1127 | 1141 | * count is just for debug when recovery is masked. |
| 1128 | 1142 | */ |
| 1129 | − printk_ratelimited(KERN_ERR | |
| 1130 | − "%s %s: [%s] GFIR=%016llx\n", | |
| 1131 | − GENWQE_DEVNAME, dev_name(&pci_dev->dev), | |
| 1132 | − __func__, gfir); | |
| 1143 | + dev_err_ratelimited(&pci_dev->dev, | |
| 1144 | + "[%s] GFIR=%016llx\n", | |
| 1145 | + __func__, gfir); | |
| 1133 | 1146 | } |
| 1134 | 1147 | |
| 1148 | + exit: | |
| 1135 | 1149 | return IRQ_HANDLED; |
| 1136 | 1150 | } |
| 1137 | 1151 | |
| @@ -1237,9 +1251,7 @@ int genwqe_setup_service_layer(struct genwqe_dev *cd) · 99 unmodified lines | ||
| 1237 | 1251 | } |
| 1238 | 1252 | |
| 1239 | 1253 | rc = genwqe_set_interrupt_capability(cd, GENWQE_MSI_IRQS); |
| 1240 | − if (rc > 0) | |
| 1241 | − rc = genwqe_set_interrupt_capability(cd, rc); | |
| 1242 | − if (rc != 0) { | |
| 1254 | + if (rc) { | |
| 1243 | 1255 | rc = -ENODEV; |
| 1244 | 1256 | goto stop_kthread; |
| 1245 | 1257 | } |
| @@ -485,6 +485,13 @@ int genwqe_init_debugfs(struct genwqe_dev *cd) · 484 unmodified lines | ||
| 485 | 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 | 495 | cd->debugfs_root = root; |
| 489 | 496 | return 0; |
| 490 | 497 | err1: |
| @@ -1048,10 +1048,15 @@ static long genwqe_ioctl(struct file *filp, unsigned int cmd, · 1047 unmodified lines | ||
| 1048 | 1048 | int rc = 0; |
| 1049 | 1049 | struct genwqe_file *cfile = (struct genwqe_file *)filp->private_data; |
| 1050 | 1050 | struct genwqe_dev *cd = cfile->cd; |
| 1051 | + struct pci_dev *pci_dev = cd->pci_dev; | |
| 1051 | 1052 | struct genwqe_reg_io __user *io; |
| 1052 | 1053 | u64 val; |
| 1053 | 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 | 1060 | if (_IOC_TYPE(cmd) != GENWQE_IOC_CODE) |
| 1056 | 1061 | return -EINVAL; |
| 1057 | 1062 | |
| @@ -223,6 +223,30 @@ static ssize_t next_bitstream_store(struct device *dev, · 222 unmodified lines | ||
| 223 | 223 | } |
| 224 | 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 | 251 | * Create device_attribute structures / params: name, mode, show, store |
| 228 | 252 | * additional flag if valid in VF |
| @@ -239,6 +263,7 @@ static struct attribute *genwqe_attributes[] = { · 10 unmodified lines | ||
| 239 | 263 | &dev_attr_status.attr, |
| 240 | 264 | &dev_attr_freerunning_timer.attr, |
| 241 | 265 | &dev_attr_queue_working_time.attr, |
| 266 | + &dev_attr_reload_bitstream.attr, | |
| 242 | 267 | NULL, |
| 243 | 268 | }; |
| 244 | 269 | |
| @@ -53,12 +53,17 @@ · 52 unmodified lines | ||
| 53 | 53 | */ |
| 54 | 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 | 58 | if (cd->err_inject & GENWQE_INJECT_HARDWARE_FAILURE) |
| 57 | 59 | return -EIO; |
| 58 | 60 | |
| 59 | 61 | if (cd->mmio == NULL) |
| 60 | 62 | return -EIO; |
| 61 | 63 | |
| 64 | + if (pci_channel_offline(pci_dev)) | |
| 65 | + return -EIO; | |
| 66 | + | |
| 62 | 67 | __raw_writeq((__force u64)cpu_to_be64(val), cd->mmio + byte_offs); |
| 63 | 68 | return 0; |
| 64 | 69 | } |
| @@ -99,12 +104,17 @@ u64 __genwqe_readq(struct genwqe_dev *cd, u64 byte_offs) · 34 unmodified lines | ||
| 99 | 104 | */ |
| 100 | 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 | 109 | if (cd->err_inject & GENWQE_INJECT_HARDWARE_FAILURE) |
| 103 | 110 | return -EIO; |
| 104 | 111 | |
| 105 | 112 | if (cd->mmio == NULL) |
| 106 | 113 | return -EIO; |
| 107 | 114 | |
| 115 | + if (pci_channel_offline(pci_dev)) | |
| 116 | + return -EIO; | |
| 117 | + | |
| 108 | 118 | __raw_writel((__force u32)cpu_to_be32(val), cd->mmio + byte_offs); |
| 109 | 119 | return 0; |
| 110 | 120 | } |
| @@ -718,10 +728,12 @@ int genwqe_set_interrupt_capability(struct genwqe_dev *cd, int count) · 607 unmodified lines | ||
| 718 | 728 | int rc; |
| 719 | 729 | struct pci_dev *pci_dev = cd->pci_dev; |
| 720 | 730 | |
| 721 | − rc = pci_enable_msi_exact(pci_dev, count); | |
| 722 | − if (rc == 0) | |
| 723 | − cd->flags |= GENWQE_FLAG_MSI_ENABLED; | |
| 724 | − return rc; | |
| 731 | + rc = pci_enable_msi_range(pci_dev, 1, count); | |
| 732 | + if (rc < 0) | |
| 733 | + return rc; | |
| 734 | + | |
| 735 | + cd->flags |= GENWQE_FLAG_MSI_ENABLED; | |
| 736 | + return 0; | |
| 725 | 737 | } |
| 726 | 738 | |
| 727 | 739 | /** |
| @@ -36,7 +36,7 @@ · 35 unmodified lines | ||
| 36 | 36 | #include <asm/byteorder.h> |
| 37 | 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 | 42 | * Static minor number assignement, until we decide/implement |
| @@ -15,6 +15,7 @@ · 14 unmodified lines | ||
| 15 | 15 | #include <linux/spi/spi.h> |
| 16 | 16 | #include <linux/platform_device.h> |
| 17 | 17 | #include <linux/delay.h> |
| 18 | +#include <asm/unaligned.h> | |
| 18 | 19 | |
| 19 | 20 | #define FIRMWARE_NAME "lattice-ecp3.bit" |
| 20 | 21 | |
| @@ -91,8 +92,8 @@ static void firmware_load(const struct firmware *fw, void *context) · 70 unmodified lines | ||
| 91 | 92 | /* Trying to speak with the FPGA via SPI... */ |
| 92 | 93 | txbuf[0] = FPGA_CMD_READ_ID; |
| 93 | 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 = *(u32 *)&rxbuf[4]; | |
| 95 | + jedec_id = get_unaligned_be32(&rxbuf[4]); | |
| 96 | + dev_dbg(&spi->dev, "FPGA JTAG ID=%08x\n", jedec_id); | |
| 96 | 97 | |
| 97 | 98 | for (i = 0; i < ARRAY_SIZE(ecp3_dev); i++) { |
| 98 | 99 | if (jedec_id == ecp3_dev[i].jedec_id) |
| @@ -109,7 +110,8 @@ static void firmware_load(const struct firmware *fw, void *context) · 10 unmodified lines | ||
| 109 | 110 | |
| 110 | 111 | txbuf[0] = FPGA_CMD_READ_STATUS; |
| 111 | 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 | 116 | buffer = kzalloc(fw->size + 8, GFP_KERNEL); |
| 115 | 117 | if (!buffer) { |
| @@ -141,7 +143,7 @@ static void firmware_load(const struct firmware *fw, void *context) · 25 unmodified lines | ||
| 141 | 143 | for (i = 0; i < FPGA_CLEAR_LOOP_COUNT; i++) { |
| 142 | 144 | txbuf[0] = FPGA_CMD_READ_STATUS; |
| 143 | 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 | 147 | if (status == FPGA_STATUS_CLEARED) |
| 146 | 148 | break; |
| 147 | 149 | |
| @@ -164,8 +166,8 @@ static void firmware_load(const struct firmware *fw, void *context) · 16 unmodified lines | ||
| 164 | 166 | |
| 165 | 167 | txbuf[0] = FPGA_CMD_READ_STATUS; |
| 166 | 168 | ret = spi_write_then_read(spi, txbuf, 8, rxbuf, rx_len); |
| 167 | − dev_dbg(&spi->dev, "FPGA Status=%08x\n", *(u32 *)&rxbuf[4]); | |
| 168 | − status = *(u32 *)&rxbuf[4]; | |
| 169 | + status = get_unaligned_be32(&rxbuf[4]); | |
| 170 | + dev_dbg(&spi->dev, "FPGA Status=%08x\n", status); | |
| 169 | 171 | |
| 170 | 172 | /* Check result */ |
| 171 | 173 | if (status & FPGA_STATUS_DONE) |
| @@ -196,7 +198,7 @@ static int lattice_ecp3_probe(struct spi_device *spi) · 24 unmodified lines | ||
| 196 | 198 | spi_set_drvdata(spi, data); |
| 197 | 199 | |
| 198 | 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 | 202 | FIRMWARE_NAME, &spi->dev, |
| 201 | 203 | GFP_KERNEL, spi, firmware_load); |
| 202 | 204 | if (err) { |
| @@ -870,3 +870,4 @@ module_init(lkdtm_module_init); · 869 unmodified lines | ||
| 870 | 870 | module_exit(lkdtm_module_exit); |
| 871 | 871 | |
| 872 | 872 | MODULE_LICENSE("GPL"); |
| 873 | +MODULE_DESCRIPTION("Kprobe module for testing crash dumps"); | |
| @@ -459,7 +459,7 @@ int mei_cl_disconnect(struct mei_cl *cl) · 458 unmodified lines | ||
| 459 | 459 | { |
| 460 | 460 | struct mei_device *dev; |
| 461 | 461 | struct mei_cl_cb *cb; |
| 462 | − int rets, err; | |
| 462 | + int rets; | |
| 463 | 463 | |
| 464 | 464 | if (WARN_ON(!cl || !cl->dev)) |
| 465 | 465 | return -ENODEV; |
| @@ -491,6 +491,7 @@ int mei_cl_disconnect(struct mei_cl *cl) · 25 unmodified lines | ||
| 491 | 491 | cl_err(dev, cl, "failed to disconnect.\n"); |
| 492 | 492 | goto free; |
| 493 | 493 | } |
| 494 | + cl->timer_count = MEI_CONNECT_TIMEOUT; | |
| 494 | 495 | mdelay(10); /* Wait for hardware disconnection ready */ |
| 495 | 496 | list_add_tail(&cb->list, &dev->ctrl_rd_list.list); |
| 496 | 497 | } else { |
| @@ -500,23 +501,18 @@ int mei_cl_disconnect(struct mei_cl *cl) · 3 unmodified lines | ||
| 500 | 501 | } |
| 501 | 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 | 505 | MEI_FILE_DISCONNECTED == cl->state, |
| 505 | 506 | mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); |
| 506 | 507 | |
| 507 | 508 | mutex_lock(&dev->device_lock); |
| 509 | + | |
| 508 | 510 | if (MEI_FILE_DISCONNECTED == cl->state) { |
| 509 | 511 | rets = 0; |
| 510 | 512 | cl_dbg(dev, cl, "successfully disconnected from FW client.\n"); |
| 511 | 513 | } else { |
| 512 | − rets = -ENODEV; | |
| 513 | − if (MEI_FILE_DISCONNECTED != cl->state) | |
| 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"); | |
| 514 | + cl_dbg(dev, cl, "timeout on disconnect from FW client.\n"); | |
| 515 | + rets = -ETIME; | |
| 520 | 516 | } |
| 521 | 517 | |
| 522 | 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) · 93 unmodified lines | ||
| 616 | 612 | mutex_lock(&dev->device_lock); |
| 617 | 613 | |
| 618 | 614 | if (cl->state != MEI_FILE_CONNECTED) { |
| 615 | + cl->state = MEI_FILE_DISCONNECTED; | |
| 619 | 616 | /* something went really wrong */ |
| 620 | 617 | if (!cl->status) |
| 621 | 618 | cl->status = -EFAULT; |
| @@ -115,6 +115,7 @@ · 114 unmodified lines | ||
| 115 | 115 | #define MEI_DEV_ID_LPT_HR 0x8CBA /* Lynx Point H Refresh */ |
| 116 | 116 | |
| 117 | 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 | 120 | /* Host Firmware Status Registers in PCI Config Space */ |
| 120 | 121 | #define PCI_CFG_HFS_1 0x40 |
| @@ -710,64 +710,10 @@ end: · 709 unmodified lines | ||
| 710 | 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 | 713 | static const struct mei_hw_ops mei_me_hw_ops = { |
| 767 | 714 | |
| 768 | 715 | .pg_state = mei_me_pg_state, |
| 769 | 716 | |
| 770 | − .fw_status = mei_me_fw_status, | |
| 771 | 717 | .host_is_ready = mei_me_host_is_ready, |
| 772 | 718 | |
| 773 | 719 | .hw_is_ready = mei_me_hw_is_ready, |
| @@ -1042,40 +1042,8 @@ end: · 1041 unmodified lines | ||
| 1042 | 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 | 1045 | static const struct mei_hw_ops mei_txe_hw_ops = { |
| 1077 | 1046 | |
| 1078 | − .fw_status = mei_txe_fw_status, | |
| 1079 | 1047 | .host_is_ready = mei_txe_host_is_ready, |
| 1080 | 1048 | |
| 1081 | 1049 | .pg_state = mei_txe_pg_state, |
| @@ -32,7 +32,6 @@ · 31 unmodified lines | ||
| 32 | 32 | #include <linux/compat.h> |
| 33 | 33 | #include <linux/jiffies.h> |
| 34 | 34 | #include <linux/interrupt.h> |
| 35 | −#include <linux/miscdevice.h> | |
| 36 | 35 | |
| 37 | 36 | #include <linux/mei.h> |
| 38 | 37 | |
| @@ -49,19 +48,12 @@ · 10 unmodified lines | ||
| 49 | 48 | */ |
| 50 | 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 | 51 | struct mei_device *dev; |
| 52 | + struct mei_cl *cl; | |
| 56 | 53 | |
| 57 | 54 | int err; |
| 58 | 55 | |
| 59 | − if (!misc->parent) | |
| 60 | − return -ENODEV; | |
| 61 | − | |
| 62 | − pdev = container_of(misc->parent, struct pci_dev, dev); | |
| 63 | − | |
| 64 | − dev = pci_get_drvdata(pdev); | |
| 56 | + dev = container_of(inode->i_cdev, struct mei_device, cdev); | |
| 65 | 57 | if (!dev) |
| 66 | 58 | return -ENODEV; |
| 67 | 59 | |
| @@ -667,46 +659,148 @@ static const struct file_operations mei_fops = { · 599 unmodified lines | ||
| 667 | 659 | .llseek = no_llseek |
| 668 | 660 | }; |
| 669 | 661 | |
| 670 | −/* | |
| 671 | − * Misc Device Struct | |
| 662 | +static struct class *mei_class; | |
| 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 = { | |
| 674 | − .name = "mei", | |
| 675 | − .fops = &mei_fops, | |
| 676 | − .minor = MISC_DYNAMIC_MINOR, | |
| 677 | −}; | |
| 675 | +static int mei_minor_get(struct mei_device *dev) | |
| 676 | +{ | |
| 677 | + int ret; | |
| 678 | + | |
| 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; | |
| 683 | − mei_misc_device.parent = &dev->pdev->dev; | |
| 684 | − ret = misc_register(&mei_misc_device); | |
| 685 | − if (ret) | |
| 697 | + mutex_lock(&mei_minor_lock); | |
| 698 | + idr_remove(&mei_idr, dev->minor); | |
| 699 | + mutex_unlock(&mei_minor_lock); | |
| 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 | 709 | return ret; |
| 687 | 710 | |
| 688 | − if (mei_dbgfs_register(dev, mei_misc_device.name)) | |
| 689 | − dev_err(&dev->pdev->dev, "cannot register debugfs\n"); | |
| 711 | + /* Fill in the data structures */ | |
| 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 | 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 | 750 | EXPORT_SYMBOL_GPL(mei_register); |
| 694 | 751 | |
| 695 | 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 | 759 | mei_dbgfs_deregister(dev); |
| 698 | − misc_deregister(&mei_misc_device); | |
| 699 | − mei_misc_device.parent = NULL; | |
| 760 | + | |
| 761 | + device_destroy(mei_class, devno); | |
| 762 | + | |
| 763 | + mei_minor_free(dev); | |
| 700 | 764 | } |
| 701 | 765 | EXPORT_SYMBOL_GPL(mei_deregister); |
| 702 | 766 | |
| 703 | 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 | 800 | static void __exit mei_exit(void) |
| 709 | 801 | { |
| 802 | + unregister_chrdev_region(mei_devt, MEI_MAX_DEVS); | |
| 803 | + class_destroy(mei_class); | |
| 710 | 804 | mei_cl_bus_exit(); |
| 711 | 805 | } |
| 712 | 806 | |
| @@ -227,7 +227,6 @@ struct mei_cl { · 226 unmodified lines | ||
| 227 | 227 | |
| 228 | 228 | /** struct mei_hw_ops |
| 229 | 229 | * |
| 230 | − * @fw_status - read FW status from PCI config space | |
| 231 | 230 | * @host_is_ready - query for host readiness |
| 232 | 231 | |
| 233 | 232 | * @hw_is_ready - query if hw is ready |
| @@ -255,8 +254,6 @@ struct mei_cl { · 21 unmodified lines | ||
| 255 | 254 | */ |
| 256 | 255 | struct mei_hw_ops { |
| 257 | 256 | |
| 258 | − int (*fw_status)(struct mei_device *dev, | |
| 259 | − struct mei_fw_status *fw_status); | |
| 260 | 257 | bool (*host_is_ready)(struct mei_device *dev); |
| 261 | 258 | |
| 262 | 259 | bool (*hw_is_ready)(struct mei_device *dev); |
| @@ -400,6 +397,10 @@ struct mei_cfg { · 137 unmodified lines | ||
| 400 | 397 | /** |
| 401 | 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 | 404 | * @reset_count - limits the number of consecutive resets |
| 404 | 405 | * @hbm_state - state of host bus message protocol |
| 405 | 406 | * @pg_event - power gating event |
| @@ -412,6 +413,9 @@ struct mei_cfg { · 6 unmodified lines | ||
| 412 | 413 | */ |
| 413 | 414 | struct mei_device { |
| 414 | 415 | struct pci_dev *pdev; /* pointer to pci device struct */ |
| 416 | + struct cdev cdev; | |
| 417 | + int minor; | |
| 418 | + | |
| 415 | 419 | /* |
| 416 | 420 | * lists of queues |
| 417 | 421 | */ |
| @@ -741,7 +745,7 @@ static inline int mei_dbgfs_register(struct mei_device *dev, const char *name) · 323 unmodified lines | ||
| 741 | 745 | static inline void mei_dbgfs_deregister(struct mei_device *dev) {} |
| 742 | 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 | 749 | void mei_deregister(struct mei_device *dev); |
| 746 | 750 | |
| 747 | 751 | #define MEI_HDR_FMT "hdr:host=%02d me=%02d len=%d internal=%1d comp=%1d" |
| @@ -31,7 +31,6 @@ · 30 unmodified lines | ||
| 31 | 31 | #include <linux/compat.h> |
| 32 | 32 | #include <linux/jiffies.h> |
| 33 | 33 | #include <linux/interrupt.h> |
| 34 | −#include <linux/miscdevice.h> | |
| 35 | 34 | |
| 36 | 35 | #include <linux/pm_runtime.h> |
| 37 | 36 | |
| @@ -82,6 +81,7 @@ static const struct pci_device_id mei_me_pci_tbl[] = { · 44 unmodified lines | ||
| 82 | 81 | {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_LP, mei_me_pch_cfg)}, |
| 83 | 82 | {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_HR, mei_me_lpt_cfg)}, |
| 84 | 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 | 86 | /* required last entry */ |
| 87 | 87 | {0, } |
| @@ -207,7 +207,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) · 119 unmodified lines | ||
| 207 | 207 | pm_runtime_set_autosuspend_delay(&pdev->dev, MEI_ME_RPM_TIMEOUT); |
| 208 | 208 | pm_runtime_use_autosuspend(&pdev->dev); |
| 209 | 209 | |
| 210 | − err = mei_register(dev); | |
| 210 | + err = mei_register(dev, &pdev->dev); | |
| 211 | 211 | if (err) |
| 212 | 212 | goto release_irq; |
| 213 | 213 | |
| @@ -369,7 +369,7 @@ static int mei_me_pm_runtime_idle(struct device *device) · 155 unmodified lines | ||
| 369 | 369 | if (!dev) |
| 370 | 370 | return -ENODEV; |
| 371 | 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 | 374 | return -EBUSY; |
| 375 | 375 | } |
| @@ -149,7 +149,7 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) · 148 unmodified lines | ||
| 149 | 149 | pm_runtime_set_autosuspend_delay(&pdev->dev, MEI_TXI_RPM_TIMEOUT); |
| 150 | 150 | pm_runtime_use_autosuspend(&pdev->dev); |
| 151 | 151 | |
| 152 | − err = mei_register(dev); | |
| 152 | + err = mei_register(dev, &pdev->dev); | |
| 153 | 153 | if (err) |
| 154 | 154 | goto release_irq; |
| 155 | 155 | |
| @@ -306,7 +306,7 @@ static int mei_txe_pm_runtime_idle(struct device *device) · 150 unmodified lines | ||
| 306 | 306 | if (!dev) |
| 307 | 307 | return -ENODEV; |
| 308 | 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 | 311 | return -EBUSY; |
| 312 | 312 | } |
| @@ -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 | 18 | comment "Intel MIC Host Driver" |
| 2 | 19 | |
| 3 | 20 | config INTEL_MIC_HOST |
| 4 | 21 | tristate "Intel MIC Host Driver" |
| 5 | − depends on 64BIT && PCI && X86 | |
| 22 | + depends on 64BIT && PCI && X86 && INTEL_MIC_BUS | |
| 6 | 23 | select VHOST_RING |
| 7 | 24 | help |
| 8 | 25 | This enables Host Driver support for the Intel Many Integrated |
| @@ -22,7 +39,7 @@ comment "Intel MIC Card Driver" · 13 unmodified lines | ||
| 22 | 39 | |
| 23 | 40 | config INTEL_MIC_CARD |
| 24 | 41 | tristate "Intel MIC Card Driver" |
| 25 | − depends on 64BIT && X86 | |
| 42 | + depends on 64BIT && X86 && INTEL_MIC_BUS | |
| 26 | 43 | select VIRTIO |
| 27 | 44 | help |
| 28 | 45 | This enables card driver support for the Intel Many Integrated |
| @@ -4,3 +4,4 @@ · 3 unmodified lines | ||
| 4 | 4 | # |
| 5 | 5 | obj-$(CONFIG_INTEL_MIC_HOST) += host/ |
| 6 | 6 | obj-$(CONFIG_INTEL_MIC_CARD) += card/ |
| 7 | +obj-$(CONFIG_INTEL_MIC_BUS) += bus/ | |
| @@ -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 | |
| @@ -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"); | |
| @@ -83,8 +83,8 @@ static int mic_shutdown_init(void) · 82 unmodified lines | ||
| 83 | 83 | int shutdown_db; |
| 84 | 84 | |
| 85 | 85 | shutdown_db = mic_next_card_db(); |
| 86 | − shutdown_cookie = mic_request_card_irq(mic_shutdown_isr, | |
| 87 | − "Shutdown", mdrv, shutdown_db); | |
| 86 | + shutdown_cookie = mic_request_card_irq(mic_shutdown_isr, NULL, | |
| 87 | + "Shutdown", mdrv, shutdown_db); | |
| 88 | 88 | if (IS_ERR(shutdown_cookie)) |
| 89 | 89 | rc = PTR_ERR(shutdown_cookie); |
| 90 | 90 | else |
| @@ -136,7 +136,8 @@ static void mic_dp_uninit(void) · 45 unmodified lines | ||
| 136 | 136 | /** |
| 137 | 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 | 141 | * @name: The ASCII name of the callee requesting the irq. |
| 141 | 142 | * @data: private data that is returned back when calling the |
| 142 | 143 | * function handler. |
| @@ -149,17 +150,19 @@ static void mic_dp_uninit(void) · 6 unmodified lines | ||
| 149 | 150 | * error code. |
| 150 | 151 | * |
| 151 | 152 | */ |
| 152 | −struct mic_irq *mic_request_card_irq(irqreturn_t (*func)(int irq, void *data), | |
| 153 | − const char *name, void *data, int index) | |
| 153 | +struct mic_irq * | |
| 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 | 158 | int rc = 0; |
| 156 | 159 | unsigned long cookie; |
| 157 | 160 | struct mic_driver *mdrv = g_drv; |
| 158 | 161 | |
| 159 | − rc = request_irq(mic_db_to_irq(mdrv, index), func, | |
| 160 | − 0, name, data); | |
| 162 | + rc = request_threaded_irq(mic_db_to_irq(mdrv, index), handler, | |
| 163 | + thread_fn, 0, name, data); | |
| 161 | 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 | 166 | goto err; |
| 164 | 167 | } |
| 165 | 168 | mdrv->irq_info.irq_usage_count[index]++; |
| @@ -172,9 +175,9 @@ err: · 6 unmodified lines | ||
| 172 | 175 | /** |
| 173 | 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 | 179 | * @data: private data specified by the calling function during the |
| 177 | − * mic_request_irq | |
| 180 | + * mic_request_threaded_irq | |
| 178 | 181 | * |
| 179 | 182 | * returns: none. |
| 180 | 183 | */ |
| @@ -30,6 +30,8 @@ · 29 unmodified lines | ||
| 30 | 30 | #include <linux/workqueue.h> |
| 31 | 31 | #include <linux/io.h> |
| 32 | 32 | #include <linux/irqreturn.h> |
| 33 | +#include <linux/interrupt.h> | |
| 34 | +#include <linux/mic_bus.h> | |
| 33 | 35 | |
| 34 | 36 | /** |
| 35 | 37 | * struct mic_intr_info - Contains h/w specific interrupt sources info |
| @@ -70,6 +72,7 @@ struct mic_device { · 34 unmodified lines | ||
| 70 | 72 | * @hotplug_work: Hot plug work for adding/removing virtio devices. |
| 71 | 73 | * @irq_info: The OS specific irq information |
| 72 | 74 | * @intr_info: H/W specific interrupt information. |
| 75 | + * @dma_mbdev: dma device on the MIC virtual bus. | |
| 73 | 76 | */ |
| 74 | 77 | struct mic_driver { |
| 75 | 78 | char name[20]; |
| @@ -80,6 +83,7 @@ struct mic_driver { · 4 unmodified lines | ||
| 80 | 83 | struct work_struct hotplug_work; |
| 81 | 84 | struct mic_irq_info irq_info; |
| 82 | 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) · 30 unmodified lines | ||
| 116 | 120 | int mic_driver_init(struct mic_driver *mdrv); |
| 117 | 121 | void mic_driver_uninit(struct mic_driver *mdrv); |
| 118 | 122 | int mic_next_card_db(void); |
| 119 | −struct mic_irq *mic_request_card_irq(irqreturn_t (*func)(int irq, void *data), | |
| 120 | − const char *name, void *data, int intr_src); | |
| 123 | +struct mic_irq * | |
| 124 | +mic_request_card_irq(irq_handler_t handler, irq_handler_t thread_fn, | |
| 125 | + const char *name, void *data, int intr_src); | |
| 121 | 126 | void mic_free_card_irq(struct mic_irq *cookie, void *data); |
| 122 | 127 | u32 mic_read_spad(struct mic_device *mdev, unsigned int idx); |
| 123 | 128 | void mic_send_intr(struct mic_device *mdev, int doorbell); |
| @@ -417,7 +417,7 @@ static int mic_add_device(struct mic_device_desc __iomem *d, · 416 unmodified lines | ||
| 417 | 417 | |
| 418 | 418 | virtio_db = mic_next_card_db(); |
| 419 | 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 | 421 | if (IS_ERR(mvdev->virtio_cookie)) { |
| 422 | 422 | ret = PTR_ERR(mvdev->virtio_cookie); |
| 423 | 423 | goto kfree; |
| @@ -606,8 +606,9 @@ int mic_devices_init(struct mic_driver *mdrv) · 182 unmodified lines | ||
| 606 | 606 | mic_scan_devices(mdrv, !REMOVE_DEVICES); |
| 607 | 607 | |
| 608 | 608 | config_db = mic_next_card_db(); |
| 609 | − virtio_config_cookie = mic_request_card_irq(mic_extint_handler, | |
| 610 | − "virtio_config_intr", mdrv, config_db); | |
| 609 | + virtio_config_cookie = mic_request_card_irq(mic_extint_handler, NULL, | |
| 610 | + "virtio_config_intr", mdrv, | |
| 611 | + config_db); | |
| 611 | 612 | if (IS_ERR(virtio_config_cookie)) { |
| 612 | 613 | rc = PTR_ERR(virtio_config_cookie); |
| 613 | 614 | goto exit; |
| @@ -148,6 +148,47 @@ void mic_card_unmap(struct mic_device *mdev, void __iomem *addr) · 147 unmodified lines | ||
| 148 | 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 | 192 | static int __init mic_probe(struct platform_device *pdev) |
| 152 | 193 | { |
| 153 | 194 | struct mic_driver *mdrv = &g_drv; |
| @@ -159,32 +200,41 @@ static int __init mic_probe(struct platform_device *pdev) · 5 unmodified lines | ||
| 159 | 200 | |
| 160 | 201 | mdev->mmio.pa = MIC_X100_MMIO_BASE; |
| 161 | 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 | 205 | if (!mdev->mmio.va) { |
| 164 | 206 | dev_err(&pdev->dev, "Cannot remap MMIO BAR\n"); |
| 165 | 207 | rc = -EIO; |
| 166 | 208 | goto done; |
| 167 | 209 | } |
| 168 | 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 | 220 | rc = mic_driver_init(mdrv); |
| 170 | 221 | if (rc) { |
| 171 | 222 | dev_err(&pdev->dev, "mic_driver_init failed rc %d\n", rc); |
| 172 | − goto iounmap; | |
| 223 | + goto remove_dma; | |
| 173 | 224 | } |
| 174 | 225 | done: |
| 175 | 226 | return rc; |
| 176 | −iounmap: | |
| 177 | − iounmap(mdev->mmio.va); | |
| 227 | +remove_dma: | |
| 228 | + mbus_unregister_device(mdrv->dma_mbdev); | |
| 178 | 229 | return rc; |
| 179 | 230 | } |
| 180 | 231 | |
| 181 | 232 | static int mic_remove(struct platform_device *pdev) |
| 182 | 233 | { |
| 183 | 234 | struct mic_driver *mdrv = &g_drv; |
| 184 | − struct mic_device *mdev = &mdrv->mdev; | |
| 185 | 235 | |
| 186 | 236 | mic_driver_uninit(mdrv); |
| 187 | − iounmap(mdev->mmio.va); | |
| 237 | + mbus_unregister_device(mdrv->dma_mbdev); | |
| 188 | 238 | return 0; |
| 189 | 239 | } |
| 190 | 240 | |
| @@ -23,11 +23,70 @@ · 22 unmodified lines | ||
| 23 | 23 | #include <linux/pci.h> |
| 24 | 24 | |
| 25 | 25 | #include <linux/mic_common.h> |
| 26 | +#include <linux/mic_bus.h> | |
| 26 | 27 | #include "../common/mic_dev.h" |
| 27 | 28 | #include "mic_device.h" |
| 28 | 29 | #include "mic_smpt.h" |
| 29 | 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 | 91 | * mic_reset - Reset the MIC device. |
| 33 | 92 | * @mdev: pointer to mic_device instance |
| @@ -95,9 +154,21 @@ retry: · 61 unmodified lines | ||
| 95 | 154 | */ |
| 96 | 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 | 169 | rc = mdev->ops->load_mic_fw(mdev, buf); |
| 99 | 170 | if (rc) |
| 100 | − goto unlock_ret; | |
| 171 | + goto dma_release; | |
| 101 | 172 | mic_smpt_restore(mdev); |
| 102 | 173 | mic_intr_restore(mdev); |
| 103 | 174 | mdev->intr_ops->enable_interrupts(mdev); |
| @@ -105,6 +176,11 @@ retry: · 1 unmodified line | ||
| 105 | 176 | mdev->ops->write_spad(mdev, MIC_DPHI_SPAD, mdev->dp_dma_addr >> 32); |
| 106 | 177 | mdev->ops->send_firmware_intr(mdev); |
| 107 | 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 | 184 | unlock_ret: |
| 109 | 185 | mutex_unlock(&mdev->mic_mutex); |
| 110 | 186 | return rc; |
| @@ -122,6 +198,11 @@ void mic_stop(struct mic_device *mdev, bool force) · 11 unmodified lines | ||
| 122 | 198 | mutex_lock(&mdev->mic_mutex); |
| 123 | 199 | if (MIC_OFFLINE != mdev->state || force) { |
| 124 | 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 | 206 | mic_bootparam_init(mdev); |
| 126 | 207 | mic_reset(mdev); |
| 127 | 208 | if (MIC_RESET_FAILED == mdev->state) |
| @@ -25,6 +25,8 @@ · 24 unmodified lines | ||
| 25 | 25 | #include <linux/idr.h> |
| 26 | 26 | #include <linux/notifier.h> |
| 27 | 27 | #include <linux/irqreturn.h> |
| 28 | +#include <linux/dmaengine.h> | |
| 29 | +#include <linux/mic_bus.h> | |
| 28 | 30 | |
| 29 | 31 | #include "mic_intr.h" |
| 30 | 32 | |
| @@ -87,6 +89,8 @@ enum mic_stepping { · 56 unmodified lines | ||
| 87 | 89 | * @cdev: Character device for MIC. |
| 88 | 90 | * @vdev_list: list of virtio devices. |
| 89 | 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 | 95 | struct mic_device { |
| 92 | 96 | struct mic_mw mmio; |
| @@ -124,6 +128,8 @@ struct mic_device { · 31 unmodified lines | ||
| 124 | 128 | struct cdev cdev; |
| 125 | 129 | struct list_head vdev_list; |
| 126 | 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 { · 14 unmodified lines | ||
| 144 | 150 | * @load_mic_fw: Load firmware segments required to boot the card |
| 145 | 151 | * into card memory. This includes the kernel, command line, ramdisk etc. |
| 146 | 152 | * @get_postcode: Get post code status from firmware. |
| 153 | + * @dma_filter: DMA filter function to be used. | |
| 147 | 154 | */ |
| 148 | 155 | struct mic_hw_ops { |
| 149 | 156 | u8 aper_bar; |
| @@ -159,6 +166,7 @@ struct mic_hw_ops { · 9 unmodified lines | ||
| 159 | 166 | void (*send_firmware_intr)(struct mic_device *mdev); |
| 160 | 167 | int (*load_mic_fw)(struct mic_device *mdev, const char *buf); |
| 161 | 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) · 22 unmodified lines | ||
| 187 | 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 | 214 | void mic_sysfs_init(struct mic_device *mdev); |
| 191 | 215 | int mic_start(struct mic_device *mdev, const char *buf); |
| 192 | 216 | void mic_stop(struct mic_device *mdev, bool force); |
| @@ -24,28 +24,29 @@ · 23 unmodified lines | ||
| 24 | 24 | #include "../common/mic_dev.h" |
| 25 | 25 | #include "mic_device.h" |
| 26 | 26 | |
| 27 | −/* | |
| 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) | |
| 27 | +static irqreturn_t mic_thread_fn(int irq, void *dev) | |
| 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 | 32 | struct mic_intr_cb *intr_cb; |
| 39 | 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); | |
| 43 | − list_for_each_entry(intr_cb, &mdev->irq_info.cb_list[idx], list) | |
| 44 | − if (intr_cb->func) | |
| 45 | − intr_cb->func(pdev->irq, intr_cb->data); | |
| 46 | − spin_unlock(&mdev->irq_info.mic_intr_lock); | |
| 37 | + spin_lock(&irq_info->mic_thread_lock); | |
| 38 | + for (i = intr_info->intr_start_idx[MIC_INTR_DB]; | |
| 39 | + i < intr_info->intr_len[MIC_INTR_DB]; i++) | |
| 40 | + if (test_and_clear_bit(i, &irq_info->mask)) { | |
| 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 | 51 | * mic_interrupt - Generic interrupt handler for |
| 51 | 52 | * MSI and INTx based interrupts. |
| @@ -53,7 +54,11 @@ static inline void mic_invoke_callback(struct mic_device *mdev, int idx) · 1 unmodified line | ||
| 53 | 54 | static irqreturn_t mic_interrupt(int irq, void *dev) |
| 54 | 55 | { |
| 55 | 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 | 62 | u32 mask; |
| 58 | 63 | int i; |
| 59 | 64 | |
| @@ -61,12 +66,19 @@ static irqreturn_t mic_interrupt(int irq, void *dev) · 1 unmodified line | ||
| 61 | 66 | if (!mask) |
| 62 | 67 | return IRQ_NONE; |
| 63 | 68 | |
| 64 | − for (i = info->intr_start_idx[MIC_INTR_DB]; | |
| 65 | − i < info->intr_len[MIC_INTR_DB]; i++) | |
| 66 | − if (mask & BIT(i)) | |
| 67 | − mic_invoke_callback(mdev, i); | |
| 68 | − | |
| 69 | − return IRQ_HANDLED; | |
| 69 | + spin_lock(&irq_info->mic_intr_lock); | |
| 70 | + for (i = intr_info->intr_start_idx[MIC_INTR_DB]; | |
| 71 | + i < intr_info->intr_len[MIC_INTR_DB]; i++) | |
| 72 | + if (mask & BIT(i)) { | |
| 73 | + list_for_each_entry(intr_cb, &irq_info->cb_list[i], | |
| 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 | 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) · 26 unmodified lines | ||
| 99 | 111 | * |
| 100 | 112 | * @mdev: pointer to the mic_device instance |
| 101 | 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 | 115 | * the interrupt. |
| 116 | + * @thread_fn: thread fn. corresponding to the handler | |
| 104 | 117 | * @data: Private data of the requester. |
| 105 | 118 | * Return the callback structure that was registered or an |
| 106 | 119 | * appropriate error on failure. |
| 107 | 120 | */ |
| 108 | 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 | 123 | void *data) |
| 111 | 124 | { |
| 112 | 125 | struct mic_intr_cb *intr_cb; |
| @@ -117,7 +130,8 @@ static struct mic_intr_cb *mic_register_intr_callback(struct mic_device *mdev, · 4 unmodified lines | ||
| 117 | 130 | if (!intr_cb) |
| 118 | 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 | 135 | intr_cb->data = data; |
| 122 | 136 | intr_cb->cb_id = ida_simple_get(&mdev->irq_info.cb_ida, |
| 123 | 137 | 0, 0, GFP_KERNEL); |
| @@ -126,9 +140,11 @@ static struct mic_intr_cb *mic_register_intr_callback(struct mic_device *mdev, · 2 unmodified lines | ||
| 126 | 140 | goto ida_fail; |
| 127 | 141 | } |
| 128 | 142 | |
| 143 | + spin_lock(&mdev->irq_info.mic_thread_lock); | |
| 129 | 144 | spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); |
| 130 | 145 | list_add_tail(&intr_cb->list, &mdev->irq_info.cb_list[idx]); |
| 131 | 146 | spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); |
| 147 | + spin_unlock(&mdev->irq_info.mic_thread_lock); | |
| 132 | 148 | |
| 133 | 149 | return intr_cb; |
| 134 | 150 | ida_fail: |
| @@ -152,8 +168,9 @@ static u8 mic_unregister_intr_callback(struct mic_device *mdev, u32 idx) · 17 unmodified lines | ||
| 152 | 168 | unsigned long flags; |
| 153 | 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 | 173 | for (i = 0; i < MIC_NUM_OFFSETS; i++) { |
| 156 | − spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); | |
| 157 | 174 | list_for_each_safe(pos, tmp, &mdev->irq_info.cb_list[i]) { |
| 158 | 175 | intr_cb = list_entry(pos, struct mic_intr_cb, list); |
| 159 | 176 | if (intr_cb->cb_id == idx) { |
| @@ -163,11 +180,13 @@ static u8 mic_unregister_intr_callback(struct mic_device *mdev, u32 idx) · 3 unmodified lines | ||
| 163 | 180 | kfree(intr_cb); |
| 164 | 181 | spin_unlock_irqrestore( |
| 165 | 182 | &mdev->irq_info.mic_intr_lock, flags); |
| 183 | + spin_unlock(&mdev->irq_info.mic_thread_lock); | |
| 166 | 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 | 190 | return MIC_NUM_OFFSETS; |
| 172 | 191 | } |
| 173 | 192 | |
| @@ -242,6 +261,7 @@ static int mic_setup_callbacks(struct mic_device *mdev) · 68 unmodified lines | ||
| 242 | 261 | INIT_LIST_HEAD(&mdev->irq_info.cb_list[i]); |
| 243 | 262 | ida_init(&mdev->irq_info.cb_ida); |
| 244 | 263 | spin_lock_init(&mdev->irq_info.mic_intr_lock); |
| 264 | + spin_lock_init(&mdev->irq_info.mic_thread_lock); | |
| 245 | 265 | return 0; |
| 246 | 266 | } |
| 247 | 267 | |
| @@ -258,14 +278,12 @@ static void mic_release_callbacks(struct mic_device *mdev) · 10 unmodified lines | ||
| 258 | 278 | struct mic_intr_cb *intr_cb; |
| 259 | 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 | 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])) { | |
| 265 | − spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, | |
| 266 | − flags); | |
| 285 | + if (list_empty(&mdev->irq_info.cb_list[i])) | |
| 267 | 286 | break; |
| 268 | − } | |
| 269 | 287 | |
| 270 | 288 | list_for_each_safe(pos, tmp, &mdev->irq_info.cb_list[i]) { |
| 271 | 289 | intr_cb = list_entry(pos, struct mic_intr_cb, list); |
| @@ -274,8 +292,9 @@ static void mic_release_callbacks(struct mic_device *mdev) · 2 unmodified lines | ||
| 274 | 292 | intr_cb->cb_id); |
| 275 | 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 | 298 | ida_destroy(&mdev->irq_info.cb_ida); |
| 280 | 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) · 31 unmodified lines | ||
| 313 | 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 | 337 | if (rc) { |
| 318 | 338 | dev_err(&pdev->dev, "Error allocating MSI interrupt\n"); |
| 319 | 339 | goto err_irq_req_fail; |
| @@ -353,8 +373,8 @@ static int mic_setup_intx(struct mic_device *mdev, struct pci_dev *pdev) · 33 unmodified lines | ||
| 353 | 373 | goto err_nomem; |
| 354 | 374 | } |
| 355 | 375 | |
| 356 | − rc = request_irq(pdev->irq, mic_interrupt, | |
| 357 | − IRQF_SHARED, "mic-intx", mdev); | |
| 376 | + rc = request_threaded_irq(pdev->irq, mic_interrupt, mic_thread_fn, | |
| 377 | + IRQF_SHARED, "mic-intx", mdev); | |
| 358 | 378 | if (rc) |
| 359 | 379 | goto err; |
| 360 | 380 | |
| @@ -391,13 +411,14 @@ int mic_next_db(struct mic_device *mdev) · 30 unmodified lines | ||
| 391 | 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 | 415 | * to be held before calling this function. |
| 396 | 416 | * |
| 397 | 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 | 419 | * The function needs to call ack_interrupts |
| 400 | 420 | * (mdev->ops->ack_interrupt(mdev)) when handling the interrupts. |
| 421 | + * @thread_fn: thread fn required by request_threaded_irq. | |
| 401 | 422 | * @name: The ASCII name of the callee requesting the irq. |
| 402 | 423 | * @data: private data that is returned back when calling the |
| 403 | 424 | * function handler. |
| @@ -412,10 +433,11 @@ int mic_next_db(struct mic_device *mdev) · 8 unmodified lines | ||
| 412 | 433 | * error code. |
| 413 | 434 | * |
| 414 | 435 | */ |
| 415 | −struct mic_irq *mic_request_irq(struct mic_device *mdev, | |
| 416 | − irqreturn_t (*func)(int irq, void *dev), | |
| 417 | − const char *name, void *data, int intr_src, | |
| 418 | − enum mic_intr_type type) | |
| 436 | +struct mic_irq * | |
| 437 | +mic_request_threaded_irq(struct mic_device *mdev, | |
| 438 | + irq_handler_t handler, irq_handler_t thread_fn, | |
| 439 | + const char *name, void *data, int intr_src, | |
| 440 | + enum mic_intr_type type) | |
| 419 | 441 | { |
| 420 | 442 | u16 offset; |
| 421 | 443 | int rc = 0; |
| @@ -444,7 +466,8 @@ struct mic_irq *mic_request_irq(struct mic_device *mdev, · 22 unmodified lines | ||
| 444 | 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 | 471 | if (rc) { |
| 449 | 472 | dev_dbg(mdev->sdev->parent, |
| 450 | 473 | "request irq failed rc = %d\n", rc); |
| @@ -458,8 +481,8 @@ struct mic_irq *mic_request_irq(struct mic_device *mdev, · 7 unmodified lines | ||
| 458 | 481 | dev_dbg(mdev->sdev->parent, "irq: %d assigned for src: %d\n", |
| 459 | 482 | msix->vector, intr_src); |
| 460 | 483 | } else { |
| 461 | − intr_cb = mic_register_intr_callback(mdev, | |
| 462 | − offset, func, data); | |
| 484 | + intr_cb = mic_register_intr_callback(mdev, offset, handler, | |
| 485 | + thread_fn, data); | |
| 463 | 486 | if (IS_ERR(intr_cb)) { |
| 464 | 487 | dev_err(mdev->sdev->parent, |
| 465 | 488 | "No available callback entries for use\n"); |
| @@ -487,9 +510,9 @@ err: · 21 unmodified lines | ||
| 487 | 510 | * needs to be held before calling this function. |
| 488 | 511 | * |
| 489 | 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 | 514 | * @data: private data specified by the calling function during the |
| 492 | − * mic_request_irq | |
| 515 | + * mic_request_threaded_irq | |
| 493 | 516 | * |
| 494 | 517 | * returns: none. |
| 495 | 518 | */ |
| @@ -21,12 +21,15 @@ · 20 unmodified lines | ||
| 21 | 21 | #ifndef _MIC_INTR_H_ |
| 22 | 22 | #define _MIC_INTR_H_ |
| 23 | 23 | |
| 24 | +#include <linux/bitops.h> | |
| 25 | +#include <linux/interrupt.h> | |
| 24 | 26 | /* |
| 25 | 27 | * The minimum number of msix vectors required for normal operation. |
| 26 | 28 | * 3 for virtio network, console and block devices. |
| 27 | 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 | 33 | #define MIC_NUM_OFFSETS 32 |
| 31 | 34 | |
| 32 | 35 | /** |
| @@ -68,7 +71,11 @@ struct mic_intr_info { · 35 unmodified lines | ||
| 68 | 71 | * @num_vectors: The number of MSI/MSI-x vectors that have been allocated. |
| 69 | 72 | * @cb_ida: callback ID allocator to track the callbacks registered. |
| 70 | 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 | 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 | 80 | struct mic_irq_info { |
| 74 | 81 | int next_avail_src; |
| @@ -77,19 +84,23 @@ struct mic_irq_info { · 2 unmodified lines | ||
| 77 | 84 | u16 num_vectors; |
| 78 | 85 | struct ida cb_ida; |
| 79 | 86 | spinlock_t mic_intr_lock; |
| 87 | + spinlock_t mic_thread_lock; | |
| 80 | 88 | struct list_head *cb_list; |
| 89 | + unsigned long mask; | |
| 81 | 90 | }; |
| 82 | 91 | |
| 83 | 92 | /** |
| 84 | 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 | 97 | * @data: Private data of the requester. |
| 88 | 98 | * @cb_id: The callback id. Identifies this callback. |
| 89 | 99 | * @list: list head pointing to the next callback structure. |
| 90 | 100 | */ |
| 91 | 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 | 104 | void *data; |
| 94 | 105 | int cb_id; |
| 95 | 106 | struct list_head list; |
| @@ -124,11 +135,11 @@ struct mic_hw_intr_ops { · 28 unmodified lines | ||
| 124 | 135 | }; |
| 125 | 136 | |
| 126 | 137 | int mic_next_db(struct mic_device *mdev); |
| 127 | −struct mic_irq *mic_request_irq(struct mic_device *mdev, | |
| 128 | − irqreturn_t (*func)(int irq, void *data), | |
| 129 | − const char *name, void *data, int intr_src, | |
| 130 | − enum mic_intr_type type); | |
| 131 | − | |
| 138 | +struct mic_irq * | |
| 139 | +mic_request_threaded_irq(struct mic_device *mdev, | |
| 140 | + irq_handler_t handler, irq_handler_t thread_fn, | |
| 141 | + const char *name, void *data, int intr_src, | |
| 142 | + enum mic_intr_type type); | |
| 132 | 143 | void mic_free_irq(struct mic_device *mdev, |
| 133 | 144 | struct mic_irq *cookie, void *data); |
| 134 | 145 | int mic_setup_interrupts(struct mic_device *mdev, struct pci_dev *pdev); |
| @@ -38,7 +38,7 @@ · 37 unmodified lines | ||
| 38 | 38 | |
| 39 | 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 | 42 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2250)}, |
| 43 | 43 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2251)}, |
| 44 | 44 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2252)}, |
| @@ -389,8 +389,9 @@ static int mic_probe(struct pci_dev *pdev, · 344 unmodified lines | ||
| 389 | 389 | mutex_lock(&mdev->mic_mutex); |
| 390 | 390 | |
| 391 | 391 | mdev->shutdown_db = mic_next_db(mdev); |
| 392 | − mdev->shutdown_cookie = mic_request_irq(mdev, mic_shutdown_db, | |
| 393 | − "shutdown-interrupt", mdev, mdev->shutdown_db, MIC_INTR_DB); | |
| 392 | + mdev->shutdown_cookie = mic_request_threaded_irq(mdev, mic_shutdown_db, | |
| 393 | + NULL, "shutdown-interrupt", mdev, | |
| 394 | + mdev->shutdown_db, MIC_INTR_DB); | |
| 394 | 395 | if (IS_ERR(mdev->shutdown_cookie)) { |
| 395 | 396 | rc = PTR_ERR(mdev->shutdown_cookie); |
| 396 | 397 | mutex_unlock(&mdev->mic_mutex); |
| @@ -21,60 +21,157 @@ · 20 unmodified lines | ||
| 21 | 21 | #include <linux/pci.h> |
| 22 | 22 | #include <linux/sched.h> |
| 23 | 23 | #include <linux/uaccess.h> |
| 24 | − | |
| 24 | +#include <linux/dmaengine.h> | |
| 25 | 25 | #include <linux/mic_common.h> |
| 26 | + | |
| 26 | 27 | #include "../common/mic_dev.h" |
| 27 | 28 | #include "mic_device.h" |
| 28 | 29 | #include "mic_smpt.h" |
| 29 | 30 | #include "mic_virtio.h" |
| 30 | 31 | |
| 31 | 32 | /* |
| 32 | − * Initiates the copies across the PCIe bus from card memory to | |
| 33 | − * a user space buffer. | |
| 33 | + * Size of the internal buffer used during DMA's as an intermediate buffer | |
| 34 | + * for copy to/from user. | |
| 34 | 35 | */ |
| 35 | −static int mic_virtio_copy_to_user(struct mic_vdev *mvdev, | |
| 36 | − void __user *ubuf, size_t len, u64 addr) | |
| 36 | +#define MIC_INT_DMA_BUF_SIZE PAGE_ALIGN(64 * 1024ULL) | |
| 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; | |
| 39 | − void __iomem *dbuf = mvdev->mdev->aper.va + addr; | |
| 40 | − /* | |
| 41 | − * We are copying from IO below an should ideally use something | |
| 42 | − * like copy_to_user_fromio(..) if it existed. | |
| 43 | − */ | |
| 44 | − if (copy_to_user(ubuf, (void __force *)dbuf, len)) { | |
| 45 | − err = -EFAULT; | |
| 46 | − dev_err(mic_dev(mvdev), "%s %d err %d\n", | |
| 41 | + int err = 0; | |
| 42 | + struct dma_async_tx_descriptor *tx; | |
| 43 | + struct dma_chan *mic_ch = mdev->dma_ch; | |
| 44 | + | |
| 45 | + if (!mic_ch) { | |
| 46 | + err = -EBUSY; | |
| 47 | + goto error; | |
| 48 | + } | |
| 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 | 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; | |
| 51 | − err = 0; | |
| 113 | + return 0; | |
| 52 | 114 | err: |
| 115 | + dev_err(mic_dev(mvdev), "%s %d err %d\n", __func__, __LINE__, err); | |
| 53 | 116 | return err; |
| 54 | 117 | } |
| 55 | 118 | |
| 56 | 119 | /* |
| 57 | − * Initiates copies across the PCIe bus from a user space | |
| 58 | − * buffer to card memory. | |
| 120 | + * Initiates copies across the PCIe bus from a user space buffer to card | |
| 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, | |
| 61 | − void __user *ubuf, size_t len, u64 addr) | |
| 125 | +static int mic_virtio_copy_from_user(struct mic_vdev *mvdev, void __user *ubuf, | |
| 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 | 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 | 164 | * We are copying to IO below and should ideally use something |
| 67 | 165 | * like copy_from_user_toio(..) if it existed. |
| 68 | 166 | */ |
| 69 | 167 | if (copy_from_user((void __force *)dbuf, ubuf, len)) { |
| 70 | 168 | err = -EFAULT; |
| 71 | − dev_err(mic_dev(mvdev), "%s %d err %d\n", | |
| 72 | − __func__, __LINE__, err); | |
| 73 | 169 | goto err; |
| 74 | 170 | } |
| 75 | 171 | mvdev->out_bytes += len; |
| 76 | − err = 0; | |
| 172 | + return 0; | |
| 77 | 173 | err: |
| 174 | + dev_err(mic_dev(mvdev), "%s %d err %d\n", __func__, __LINE__, err); | |
| 78 | 175 | return err; |
| 79 | 176 | } |
| 80 | 177 | |
| @@ -110,7 +207,8 @@ static inline u32 mic_vringh_iov_consumed(struct vringh_kiov *iov) · 29 unmodified lines | ||
| 110 | 207 | * way to override the VRINGH xfer(..) routines as of v3.10. |
| 111 | 208 | */ |
| 112 | 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 | 213 | int ret = 0; |
| 116 | 214 | size_t partlen, tot_len = 0; |
| @@ -118,13 +216,15 @@ static int mic_vringh_copy(struct mic_vdev *mvdev, struct vringh_kiov *iov, · 1 unmodified line | ||
| 118 | 216 | while (len && iov->i < iov->used) { |
| 119 | 217 | partlen = min(iov->iov[iov->i].iov_len, len); |
| 120 | 218 | if (read) |
| 121 | − ret = mic_virtio_copy_to_user(mvdev, | |
| 122 | − ubuf, partlen, | |
| 123 | − (u64)iov->iov[iov->i].iov_base); | |
| 219 | + ret = mic_virtio_copy_to_user(mvdev, ubuf, partlen, | |
| 220 | + (u64)iov->iov[iov->i].iov_base, | |
| 221 | + iov->iov[iov->i].iov_len, | |
| 222 | + vr_idx); | |
| 124 | 223 | else |
| 125 | − ret = mic_virtio_copy_from_user(mvdev, | |
| 126 | − ubuf, partlen, | |
| 127 | − (u64)iov->iov[iov->i].iov_base); | |
| 224 | + ret = mic_virtio_copy_from_user(mvdev, ubuf, partlen, | |
| 225 | + (u64)iov->iov[iov->i].iov_base, | |
| 226 | + iov->iov[iov->i].iov_len, | |
| 227 | + vr_idx); | |
| 128 | 228 | if (ret) { |
| 129 | 229 | dev_err(mic_dev(mvdev), "%s %d err %d\n", |
| 130 | 230 | __func__, __LINE__, ret); |
| @@ -192,8 +292,8 @@ static int _mic_virtio_copy(struct mic_vdev *mvdev, · 61 unmodified lines | ||
| 192 | 292 | ubuf = iov.iov_base; |
| 193 | 293 | } |
| 194 | 294 | /* Issue all the read descriptors first */ |
| 195 | − ret = mic_vringh_copy(mvdev, riov, ubuf, len, | |
| 196 | − MIC_VRINGH_READ, &out_len); | |
| 295 | + ret = mic_vringh_copy(mvdev, riov, ubuf, len, MIC_VRINGH_READ, | |
| 296 | + copy->vr_idx, &out_len); | |
| 197 | 297 | if (ret) { |
| 198 | 298 | dev_err(mic_dev(mvdev), "%s %d err %d\n", |
| 199 | 299 | __func__, __LINE__, ret); |
| @@ -203,8 +303,8 @@ static int _mic_virtio_copy(struct mic_vdev *mvdev, · 3 unmodified lines | ||
| 203 | 303 | ubuf += out_len; |
| 204 | 304 | copy->out_len += out_len; |
| 205 | 305 | /* Issue the write descriptors next */ |
| 206 | − ret = mic_vringh_copy(mvdev, wiov, ubuf, len, | |
| 207 | − !MIC_VRINGH_READ, &out_len); | |
| 306 | + ret = mic_vringh_copy(mvdev, wiov, ubuf, len, !MIC_VRINGH_READ, | |
| 307 | + copy->vr_idx, &out_len); | |
| 208 | 308 | if (ret) { |
| 209 | 309 | dev_err(mic_dev(mvdev), "%s %d err %d\n", |
| 210 | 310 | __func__, __LINE__, ret); |
| @@ -589,13 +689,19 @@ int mic_virtio_add_device(struct mic_vdev *mvdev, · 378 unmodified lines | ||
| 589 | 689 | dev_dbg(mdev->sdev->parent, |
| 590 | 690 | "%s %d index %d va %p info %p vr_size 0x%x\n", |
| 591 | 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 | 698 | snprintf(irqname, sizeof(irqname), "mic%dvirtio%d", mdev->id, |
| 595 | 699 | mvdev->virtio_id); |
| 596 | 700 | mvdev->virtio_db = mic_next_db(mdev); |
| 597 | − mvdev->virtio_cookie = mic_request_irq(mdev, mic_virtio_intr_handler, | |
| 598 | − irqname, mvdev, mvdev->virtio_db, MIC_INTR_DB); | |
| 701 | + mvdev->virtio_cookie = mic_request_threaded_irq(mdev, | |
| 702 | + mic_virtio_intr_handler, | |
| 703 | + NULL, irqname, mvdev, | |
| 704 | + mvdev->virtio_db, MIC_INTR_DB); | |
| 599 | 705 | if (IS_ERR(mvdev->virtio_cookie)) { |
| 600 | 706 | ret = PTR_ERR(mvdev->virtio_cookie); |
| 601 | 707 | dev_dbg(mdev->sdev->parent, "request irq failed\n"); |
| @@ -671,6 +777,11 @@ skip_hot_remove: · 69 unmodified lines | ||
| 671 | 777 | vqconfig = mic_vq_config(mvdev->dd); |
| 672 | 778 | for (i = 0; i < mvdev->dd->num_vq; i++) { |
| 673 | 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 | 785 | vringh_kiov_cleanup(&mvr->riov); |
| 675 | 786 | vringh_kiov_cleanup(&mvr->wiov); |
| 676 | 787 | mic_unmap_single(mdev, le64_to_cpu(vqconfig[i].address), |
| @@ -46,18 +46,23 @@ · 45 unmodified lines | ||
| 46 | 46 | * @vrh: The host VRINGH used for accessing the card vrings. |
| 47 | 47 | * @riov: The VRINGH read kernel IOV. |
| 48 | 48 | * @wiov: The VRINGH write kernel IOV. |
| 49 | − * @head: The VRINGH head index address passed to vringh_getdesc_kern(..). | |
| 50 | 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 | 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 | 56 | struct mic_vringh { |
| 54 | 57 | struct mic_vring vring; |
| 55 | 58 | struct vringh vrh; |
| 56 | 59 | struct vringh_kiov riov; |
| 57 | 60 | struct vringh_kiov wiov; |
| 58 | − u16 head; | |
| 59 | 61 | struct mutex vr_mutex; |
| 62 | + void *buf; | |
| 63 | + dma_addr_t buf_da; | |
| 60 | 64 | struct mic_vdev *mvdev; |
| 65 | + u16 head; | |
| 61 | 66 | }; |
| 62 | 67 | |
| 63 | 68 | /** |
| @@ -69,6 +74,14 @@ struct mic_vringh { · 5 unmodified lines | ||
| 69 | 74 | * @poll_wake - Used for waking up threads blocked in poll. |
| 70 | 75 | * @out_bytes - Debug stats for number of bytes copied from host to card. |
| 71 | 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 | 85 | * @mvr - Store per VRING data structures. |
| 73 | 86 | * @virtio_bh_work - Work struct used to schedule virtio bottom half handling. |
| 74 | 87 | * @dd - Virtio device descriptor. |
| @@ -84,6 +97,10 @@ struct mic_vdev { · 9 unmodified lines | ||
| 84 | 97 | int poll_wake; |
| 85 | 98 | unsigned long out_bytes; |
| 86 | 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 | 104 | struct mic_vringh mvr[MIC_MAX_VRINGS]; |
| 88 | 105 | struct work_struct virtio_bh_work; |
| 89 | 106 | struct mic_device_desc *dd; |
| @@ -549,6 +549,13 @@ struct mic_smpt_ops mic_x100_smpt_ops = { · 548 unmodified lines | ||
| 549 | 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 | 559 | struct mic_hw_ops mic_x100_ops = { |
| 553 | 560 | .aper_bar = MIC_X100_APER_BAR, |
| 554 | 561 | .mmio_bar = MIC_X100_MMIO_BAR, |
| @@ -563,6 +570,7 @@ struct mic_hw_ops mic_x100_ops = { · 8 unmodified lines | ||
| 563 | 570 | .send_firmware_intr = mic_x100_send_firmware_intr, |
| 564 | 571 | .load_mic_fw = mic_x100_load_firmware, |
| 565 | 572 | .get_postcode = mic_x100_get_postcode, |
| 573 | + .dma_filter = mic_x100_dma_filter, | |
| 566 | 574 | }; |
| 567 | 575 | |
| 568 | 576 | struct mic_hw_intr_ops mic_x100_intr_ops = { |
| @@ -244,7 +244,8 @@ static long read_local_version(struct kim_data_s *kim_gdata, char *bts_scr_name) · 243 unmodified lines | ||
| 244 | 244 | if (version & 0x8000) |
| 245 | 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 | 250 | /* to be accessed later via sysfs entry */ |
| 250 | 251 | kim_gdata->version.full = version; |
| @@ -287,7 +288,7 @@ static long download_firmware(struct kim_data_s *kim_gdata) · 36 unmodified lines | ||
| 287 | 288 | long len = 0; |
| 288 | 289 | unsigned char *ptr = NULL; |
| 289 | 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 | 292 | int wr_room_space; |
| 292 | 293 | int cmd_size; |
| 293 | 294 | unsigned long timeout; |
| @@ -778,7 +779,7 @@ static int kim_probe(struct platform_device *pdev) · 484 unmodified lines | ||
| 778 | 779 | pr_info("sysfs entries created\n"); |
| 779 | 780 | |
| 780 | 781 | kim_debugfs_dir = debugfs_create_dir("ti-st", NULL); |
| 781 | − if (IS_ERR(kim_debugfs_dir)) { | |
| 782 | + if (!kim_debugfs_dir) { | |
| 782 | 783 | pr_err(" debugfs entries creation failed "); |
| 783 | 784 | err = -EIO; |
| 784 | 785 | goto err_debugfs_dir; |
| @@ -788,7 +789,6 @@ static int kim_probe(struct platform_device *pdev) · 3 unmodified lines | ||
| 788 | 789 | kim_gdata, &version_debugfs_fops); |
| 789 | 790 | debugfs_create_file("protocols", S_IRUGO, kim_debugfs_dir, |
| 790 | 791 | kim_gdata, &list_debugfs_fops); |
| 791 | − pr_info(" debugfs entries created "); | |
| 792 | 792 | return 0; |
| 793 | 793 | |
| 794 | 794 | err_debugfs_dir: |
| @@ -130,7 +130,7 @@ static int vexpress_syscfg_write(void *context, unsigned int index, · 129 unmodified lines | ||
| 130 | 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 | 134 | .lock = vexpress_config_lock, |
| 135 | 135 | .unlock = vexpress_config_unlock, |
| 136 | 136 | .reg_bits = 32, |
| @@ -276,7 +276,7 @@ int vexpress_syscfg_device_register(struct platform_device *pdev) · 139 unmodified lines | ||
| 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 | 281 | struct vexpress_syscfg *syscfg; |
| 282 | 282 | struct resource *res; |
| @@ -748,7 +748,7 @@ static void vmci_guest_remove_device(struct pci_dev *pdev) · 747 unmodified lines | ||
| 748 | 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 | 752 | { PCI_DEVICE(PCI_VENDOR_ID_VMWARE, PCI_DEVICE_ID_VMWARE_VMCI), }, |
| 753 | 753 | { 0 }, |
| 754 | 754 | }; |
| @@ -582,7 +582,7 @@ static int pci_legacy_suspend_late(struct device *dev, pm_message_t state) · 581 unmodified lines | ||
| 582 | 582 | WARN_ONCE(pci_dev->current_state != prev, |
| 583 | 583 | "PCI PM: Device state not saved by %pF\n", |
| 584 | 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) · 2 unmodified lines | ||
| 591 | 591 | |
| 592 | 592 | pci_pm_set_unknown_state(pci_dev); |
| 593 | 593 | |
| 594 | +Fixup: | |
| 595 | + pci_fixup_device(pci_fixup_suspend_late, pci_dev); | |
| 596 | + | |
| 594 | 597 | return 0; |
| 595 | 598 | } |
| 596 | 599 | |
| @@ -734,7 +737,7 @@ static int pci_pm_suspend_noirq(struct device *dev) · 137 unmodified lines | ||
| 734 | 737 | |
| 735 | 738 | if (!pm) { |
| 736 | 739 | pci_save_state(pci_dev); |
| 737 | − return 0; | |
| 740 | + goto Fixup; | |
| 738 | 741 | } |
| 739 | 742 | |
| 740 | 743 | if (pm->suspend_noirq) { |
| @@ -751,7 +754,7 @@ static int pci_pm_suspend_noirq(struct device *dev) · 10 unmodified lines | ||
| 751 | 754 | WARN_ONCE(pci_dev->current_state != prev, |
| 752 | 755 | "PCI PM: State of device not saved by %pF\n", |
| 753 | 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) · 17 unmodified lines | ||
| 775 | 778 | if (pci_dev->class == PCI_CLASS_SERIAL_USB_EHCI) |
| 776 | 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 | 784 | return 0; |
| 779 | 785 | } |
| 780 | 786 | |
| @@ -999,8 +1005,10 @@ static int pci_pm_poweroff_noirq(struct device *dev) · 218 unmodified lines | ||
| 999 | 1005 | if (pci_has_legacy_pm_support(to_pci_dev(dev))) |
| 1000 | 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 | 1010 | return 0; |
| 1011 | + } | |
| 1004 | 1012 | |
| 1005 | 1013 | if (drv->pm->poweroff_noirq) { |
| 1006 | 1014 | int error; |
| @@ -1021,6 +1029,8 @@ static int pci_pm_poweroff_noirq(struct device *dev) · 14 unmodified lines | ||
| 1021 | 1029 | if (pci_dev->class == PCI_CLASS_SERIAL_USB_EHCI) |
| 1022 | 1030 | pci_write_config_word(pci_dev, PCI_COMMAND, 0); |
| 1023 | 1031 | |
| 1032 | + pci_fixup_device(pci_fixup_suspend_late, pci_dev); | |
| 1033 | + | |
| 1024 | 1034 | if (pcibios_pm_ops.poweroff_noirq) |
| 1025 | 1035 | return pcibios_pm_ops.poweroff_noirq(dev); |
| 1026 | 1036 | |
| @@ -2986,6 +2986,103 @@ DECLARE_PCI_FIXUP_HEADER(0x1814, 0x0601, /* Ralink RT2800 802.11n PCI */ · 2985 unmodified lines | ||
| 2986 | 2986 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_REALTEK, 0x8169, |
| 2987 | 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 | 3086 | static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, |
| 2990 | 3087 | struct pci_fixup *end) |
| 2991 | 3088 | { |
| @@ -3018,6 +3115,8 @@ extern struct pci_fixup __start_pci_fixups_resume_early[]; · 26 unmodified lines | ||
| 3018 | 3115 | extern struct pci_fixup __end_pci_fixups_resume_early[]; |
| 3019 | 3116 | extern struct pci_fixup __start_pci_fixups_suspend[]; |
| 3020 | 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 | 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) · 39 unmodified lines | ||
| 3063 | 3162 | end = __end_pci_fixups_suspend; |
| 3064 | 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 | 3170 | default: |
| 3067 | 3171 | /* stupid compiler warning, you would think with an enum... */ |
| 3068 | 3172 | return; |
| @@ -202,6 +202,7 @@ config PCMCIA_SA1111 · 201 unmodified lines | ||
| 202 | 202 | depends on ARM && SA1111 && PCMCIA |
| 203 | 203 | select PCMCIA_SOC_COMMON |
| 204 | 204 | select PCMCIA_SA11XX_BASE if ARCH_SA1100 |
| 205 | + select PCMCIA_PXA2XX if ARCH_LUBBOCK && SA1111 | |
| 205 | 206 | help |
| 206 | 207 | Say Y here to include support for SA1111-based PCMCIA or CF |
| 207 | 208 | sockets, found on the Jornada 720, Graphicsmaster and other |
| @@ -217,7 +218,6 @@ config PCMCIA_PXA2XX · 9 unmodified lines | ||
| 217 | 218 | || ARCOM_PCMCIA || ARCH_PXA_ESERIES || MACH_STARGATE2 \ |
| 218 | 219 | || MACH_VPAC270 || MACH_BALLOON3 || MACH_COLIBRI \ |
| 219 | 220 | || MACH_COLIBRI320 || MACH_H4700) |
| 220 | − select PCMCIA_SA1111 if ARCH_LUBBOCK && SA1111 | |
| 221 | 221 | select PCMCIA_SOC_COMMON |
| 222 | 222 | help |
| 223 | 223 | Say Y here to include support for the PXA2xx PCMCIA controller |
| @@ -49,6 +49,7 @@ sa1100_cs-y += sa1100_generic.o · 48 unmodified lines | ||
| 49 | 49 | sa1100_cs-$(CONFIG_SA1100_ASSABET) += sa1100_assabet.o |
| 50 | 50 | sa1100_cs-$(CONFIG_SA1100_CERF) += sa1100_cerf.o |
| 51 | 51 | sa1100_cs-$(CONFIG_SA1100_COLLIE) += pxa2xx_sharpsl.o |
| 52 | +sa1100_cs-$(CONFIG_SA1100_H3100) += sa1100_h3600.o | |
| 52 | 53 | sa1100_cs-$(CONFIG_SA1100_H3600) += sa1100_h3600.o |
| 53 | 54 | sa1100_cs-$(CONFIG_SA1100_NANOENGINE) += sa1100_nanoengine.o |
| 54 | 55 | sa1100_cs-$(CONFIG_SA1100_SHANNON) += sa1100_shannon.o |
| @@ -475,7 +475,7 @@ static void bcm63xx_cb_exit(struct pci_dev *dev) · 474 unmodified lines | ||
| 475 | 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 | 480 | .vendor = PCI_VENDOR_ID_BROADCOM, |
| 481 | 481 | .device = BCM6348_CPU_ID, |
| @@ -25,7 +25,7 @@ · 24 unmodified lines | ||
| 25 | 25 | MODULE_LICENSE("GPL"); |
| 26 | 26 | |
| 27 | 27 | /* PCI core routines */ |
| 28 | −static DEFINE_PCI_DEVICE_TABLE(i82092aa_pci_ids) = { | |
| 28 | +static const struct pci_device_id i82092aa_pci_ids[] = { | |
| 29 | 29 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82092AA_0) }, |
| 30 | 30 | { } |
| 31 | 31 | }; |
| @@ -764,7 +764,7 @@ static void pd6729_pci_remove(struct pci_dev *dev) · 763 unmodified lines | ||
| 764 | 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 | 768 | { PCI_DEVICE(PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_6729) }, |
| 769 | 769 | { } |
| 770 | 770 | }; |
| @@ -9,6 +9,7 @@ · 8 unmodified lines | ||
| 9 | 9 | #include <linux/device.h> |
| 10 | 10 | #include <linux/errno.h> |
| 11 | 11 | #include <linux/init.h> |
| 12 | +#include <linux/io.h> | |
| 12 | 13 | |
| 13 | 14 | #include <mach/hardware.h> |
| 14 | 15 | #include <asm/hardware/sa1111.h> |
| @@ -94,6 +95,7 @@ static struct pcmcia_low_level jornada720_pcmcia_ops = { · 79 unmodified lines | ||
| 94 | 95 | int pcmcia_jornada720_init(struct device *dev) |
| 95 | 96 | { |
| 96 | 97 | int ret = -ENODEV; |
| 98 | + struct sa1111_dev *sadev = SA1111_DEV(dev); | |
| 97 | 99 | |
| 98 | 100 | if (machine_is_jornada720()) { |
| 99 | 101 | unsigned int pin = GPIO_A0 | GPIO_A1 | GPIO_A2 | GPIO_A3; |
| @@ -101,12 +103,12 @@ int pcmcia_jornada720_init(struct device *dev) · 1 unmodified line | ||
| 101 | 103 | GRER |= 0x00000002; |
| 102 | 104 | |
| 103 | 105 | /* Set GPIO_A<3:1> to be outputs for PCMCIA/CF power controller: */ |
| 104 | − sa1111_set_io_dir(dev, pin, 0, 0); | |
| 105 | − sa1111_set_io(dev, pin, 0); | |
| 106 | − sa1111_set_sleep_io(dev, pin, 0); | |
| 106 | + sa1111_set_io_dir(sadev, pin, 0, 0); | |
| 107 | + sa1111_set_io(sadev, pin, 0); | |
| 108 | + sa1111_set_sleep_io(sadev, pin, 0); | |
| 107 | 109 | |
| 108 | 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 | 112 | sa11xx_drv_pcmcia_add_one); |
| 111 | 113 | } |
| 112 | 114 | |
| @@ -563,7 +563,7 @@ static int vrc4173_cardu_setup(char *options) · 562 unmodified lines | ||
| 563 | 563 | |
| 564 | 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 | 567 | { PCI_DEVICE(PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_NAPCCARD) }, |
| 568 | 568 | {0, } |
| 569 | 569 | }; |
| @@ -1352,7 +1352,7 @@ static const struct dev_pm_ops yenta_pm_ops = { · 1351 unmodified lines | ||
| 1352 | 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 | 1356 | CB_ID(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1031, TI), |
| 1357 | 1357 | |
| 1358 | 1358 | /* |
| @@ -31,6 +31,7 @@ · 30 unmodified lines | ||
| 31 | 31 | #include <linux/mfd/max77693.h> |
| 32 | 32 | #include <linux/mfd/max77693-private.h> |
| 33 | 33 | #include <linux/regulator/of_regulator.h> |
| 34 | +#include <linux/regmap.h> | |
| 34 | 35 | |
| 35 | 36 | #define CHGIN_ILIM_STEP_20mA 20000 |
| 36 | 37 | |
| @@ -39,9 +40,9 @@ · 2 unmodified lines | ||
| 39 | 40 | static int max77693_chg_is_enabled(struct regulator_dev *rdev) |
| 40 | 41 | { |
| 41 | 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 | 46 | if (ret) |
| 46 | 47 | return ret; |
| 47 | 48 | |
| @@ -57,12 +58,11 @@ static int max77693_chg_get_current_limit(struct regulator_dev *rdev) · 9 unmodified lines | ||
| 57 | 58 | { |
| 58 | 59 | unsigned int chg_min_uA = rdev->constraints->min_uA; |
| 59 | 60 | unsigned int chg_max_uA = rdev->constraints->max_uA; |
| 60 | − u8 reg, sel; | |
| 61 | + unsigned int reg, sel; | |
| 61 | 62 | unsigned int val; |
| 62 | 63 | int ret; |
| 63 | 64 | |
| 64 | − ret = max77693_read_reg(rdev->regmap, | |
| 65 | − MAX77693_CHG_REG_CHG_CNFG_09, ®); | |
| 65 | + ret = regmap_read(rdev->regmap, MAX77693_CHG_REG_CHG_CNFG_09, ®); | |
| 66 | 66 | if (ret < 0) |
| 67 | 67 | return ret; |
| 68 | 68 | |
| @@ -96,7 +96,7 @@ static int max77693_chg_set_current_limit(struct regulator_dev *rdev, · 27 unmodified lines | ||
| 96 | 96 | /* the first four codes for charger current are all 60mA */ |
| 97 | 97 | sel += 3; |
| 98 | 98 | |
| 99 | − return max77693_write_reg(rdev->regmap, | |
| 99 | + return regmap_write(rdev->regmap, | |
| 100 | 100 | MAX77693_CHG_REG_CHG_CNFG_09, sel); |
| 101 | 101 | } |
| 102 | 102 | /* end of CHARGER regulator ops */ |
| @@ -18,7 +18,6 @@ · 17 unmodified lines | ||
| 18 | 18 | #include <linux/of_device.h> |
| 19 | 19 | #include <linux/platform_device.h> |
| 20 | 20 | #include <linux/spmi.h> |
| 21 | −#include <linux/module.h> | |
| 22 | 21 | #include <linux/pm_runtime.h> |
| 23 | 22 | |
| 24 | 23 | #include <dt-bindings/spmi/spmi.h> |
| @@ -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. | |
| @@ -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 | + | |
| @@ -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 | +} | |
| @@ -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 | +} | |
| @@ -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 | |
| @@ -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 | +} | |
| @@ -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); | |
| @@ -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 | |
| @@ -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 | |
| @@ -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 | +} | |
| @@ -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 | +} | |
| @@ -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 | +} | |
| @@ -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 | |
| @@ -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 | |
| @@ -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 | + | |
| @@ -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 | + | |
| @@ -91,8 +91,7 @@ static irqreturn_t pruss_handler(int irq, struct uio_info *info) · 90 unmodified lines | ||
| 91 | 91 | return IRQ_HANDLED; |
| 92 | 92 | } |
| 93 | 93 | |
| 94 | −static void pruss_cleanup(struct platform_device *dev, | |
| 95 | − struct uio_pruss_dev *gdev) | |
| 94 | +static void pruss_cleanup(struct device *dev, struct uio_pruss_dev *gdev) | |
| 96 | 95 | { |
| 97 | 96 | int cnt; |
| 98 | 97 | struct uio_info *p = gdev->info; |
| @@ -103,7 +102,7 @@ static void pruss_cleanup(struct platform_device *dev, · 4 unmodified lines | ||
| 103 | 102 | } |
| 104 | 103 | iounmap(gdev->prussio_vaddr); |
| 105 | 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 | 106 | gdev->ddr_paddr); |
| 108 | 107 | } |
| 109 | 108 | if (gdev->sram_vaddr) |
| @@ -115,13 +114,14 @@ static void pruss_cleanup(struct platform_device *dev, · 5 unmodified lines | ||
| 115 | 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 | 119 | struct uio_info *p; |
| 121 | 120 | struct uio_pruss_dev *gdev; |
| 122 | 121 | struct resource *regs_prussio; |
| 122 | + struct device *dev = &pdev->dev; | |
| 123 | 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 | 126 | gdev = kzalloc(sizeof(struct uio_pruss_dev), GFP_KERNEL); |
| 127 | 127 | if (!gdev) |
| @@ -132,10 +132,11 @@ static int pruss_probe(struct platform_device *dev) · 4 unmodified lines | ||
| 132 | 132 | kfree(gdev); |
| 133 | 133 | return -ENOMEM; |
| 134 | 134 | } |
| 135 | + | |
| 135 | 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 | 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 | 140 | ret = PTR_ERR(gdev->pruss_clk); |
| 140 | 141 | kfree(gdev->info); |
| 141 | 142 | kfree(gdev); |
| @@ -144,14 +145,14 @@ static int pruss_probe(struct platform_device *dev) · 2 unmodified lines | ||
| 144 | 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 | 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 | 151 | goto out_free; |
| 151 | 152 | } |
| 152 | 153 | |
| 153 | 154 | if (!regs_prussio->start) { |
| 154 | − dev_err(&dev->dev, "Invalid memory resource\n"); | |
| 155 | + dev_err(dev, "Invalid memory resource\n"); | |
| 155 | 156 | goto out_free; |
| 156 | 157 | } |
| 157 | 158 | |
| @@ -161,27 +162,27 @@ static int pruss_probe(struct platform_device *dev) · 3 unmodified lines | ||
| 161 | 162 | (unsigned long)gen_pool_dma_alloc(gdev->sram_pool, |
| 162 | 163 | sram_pool_sz, &gdev->sram_paddr); |
| 163 | 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 | 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 | 171 | &(gdev->ddr_paddr), GFP_KERNEL | GFP_DMA); |
| 171 | 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 | 174 | goto out_free; |
| 174 | 175 | } |
| 175 | 176 | |
| 176 | 177 | len = resource_size(regs_prussio); |
| 177 | 178 | gdev->prussio_vaddr = ioremap(regs_prussio->start, len); |
| 178 | 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 | 181 | goto out_free; |
| 181 | 182 | } |
| 182 | 183 | |
| 183 | 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 | 187 | for (cnt = 0, p = gdev->info; cnt < MAX_PRUSS_EVT; cnt++, p++) { |
| 187 | 188 | p->mem[0].addr = regs_prussio->start; |
| @@ -204,12 +205,12 @@ static int pruss_probe(struct platform_device *dev) · 16 unmodified lines | ||
| 204 | 205 | p->handler = pruss_handler; |
| 205 | 206 | p->priv = gdev; |
| 206 | 207 | |
| 207 | − ret = uio_register_device(&dev->dev, p); | |
| 208 | + ret = uio_register_device(dev, p); | |
| 208 | 209 | if (ret < 0) |
| 209 | 210 | goto out_free; |
| 210 | 211 | } |
| 211 | 212 | |
| 212 | − platform_set_drvdata(dev, gdev); | |
| 213 | + platform_set_drvdata(pdev, gdev); | |
| 213 | 214 | return 0; |
| 214 | 215 | |
| 215 | 216 | out_free: |
| @@ -221,7 +222,7 @@ static int pruss_remove(struct platform_device *dev) · 5 unmodified lines | ||
| 221 | 222 | { |
| 222 | 223 | struct uio_pruss_dev *gdev = platform_get_drvdata(dev); |
| 223 | 224 | |
| 224 | − pruss_cleanup(dev, gdev); | |
| 225 | + pruss_cleanup(&dev->dev, gdev); | |
| 225 | 226 | return 0; |
| 226 | 227 | } |
| 227 | 228 | |
| @@ -360,7 +360,6 @@ static const int CA91CX42_VSI_TO[] = { VSI0_TO, VSI1_TO, VSI2_TO, VSI3_TO, · 359 unmodified lines | ||
| 360 | 360 | */ |
| 361 | 361 | #define CA91CX42_DCTL_L2V (1<<31) |
| 362 | 362 | #define CA91CX42_DCTL_VDW_M (3<<22) |
| 363 | −#define CA91CX42_DCTL_VDW_M (3<<22) | |
| 364 | 363 | #define CA91CX42_DCTL_VDW_D8 0 |
| 365 | 364 | #define CA91CX42_DCTL_VDW_D16 (1<<22) |
| 366 | 365 | #define CA91CX42_DCTL_VDW_D32 (1<<23) |
| @@ -563,7 +563,7 @@ static struct platform_driver ds1wm_driver = { · 562 unmodified lines | ||
| 563 | 563 | |
| 564 | 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 | 567 | return platform_driver_register(&ds1wm_driver); |
| 568 | 568 | } |
| 569 | 569 | |
| @@ -226,7 +226,7 @@ static int ds2482_wait_1wire_idle(struct ds2482_data *pdev) · 225 unmodified lines | ||
| 226 | 226 | } |
| 227 | 227 | |
| 228 | 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 | 230 | __func__, pdev->channel); |
| 231 | 231 | |
| 232 | 232 | return temp; |
| @@ -206,7 +206,7 @@ static int ds_send_control_cmd(struct ds_device *dev, u16 value, u16 index) · 205 unmodified lines | ||
| 206 | 206 | err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]), |
| 207 | 207 | CONTROL_CMD, VENDOR, value, index, NULL, 0, 1000); |
| 208 | 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 | 210 | value, index, err); |
| 211 | 211 | return err; |
| 212 | 212 | } |
| @@ -221,7 +221,7 @@ static int ds_send_control_mode(struct ds_device *dev, u16 value, u16 index) · 8 unmodified lines | ||
| 221 | 221 | err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]), |
| 222 | 222 | MODE_CMD, VENDOR, value, index, NULL, 0, 1000); |
| 223 | 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 | 225 | value, index, err); |
| 226 | 226 | return err; |
| 227 | 227 | } |
| @@ -236,7 +236,7 @@ static int ds_send_control(struct ds_device *dev, u16 value, u16 index) · 8 unmodified lines | ||
| 236 | 236 | err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]), |
| 237 | 237 | COMM_CMD, VENDOR, value, index, NULL, 0, 1000); |
| 238 | 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 | 240 | value, index, err); |
| 241 | 241 | return err; |
| 242 | 242 | } |
| @@ -255,7 +255,8 @@ static int ds_recv_status_nodump(struct ds_device *dev, struct ds_status *st, · 12 unmodified lines | ||
| 255 | 255 | err = usb_interrupt_msg(dev->udev, usb_rcvintpipe(dev->udev, |
| 256 | 256 | dev->ep[EP_STATUS]), buf, size, &count, 100); |
| 257 | 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 | 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, · 5 unmodified lines | ||
| 267 | 268 | |
| 268 | 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 | 274 | static void ds_dump_status(struct ds_device *dev, unsigned char *buf, int count) |
| 274 | 275 | { |
| 275 | 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 | 279 | for (i=0; i<count; ++i) |
| 279 | − printk("%02x ", buf[i]); | |
| 280 | − printk(KERN_INFO "\n"); | |
| 280 | + pr_info("%02x ", buf[i]); | |
| 281 | + pr_info("\n"); | |
| 281 | 282 | |
| 282 | 283 | if (count >= 16) { |
| 283 | 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) · 21 unmodified lines | ||
| 305 | 306 | } |
| 306 | 307 | ds_print_msg(buf, "Result Register Value: ", i); |
| 307 | 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 | 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 | 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 | 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 | 316 | if (buf[i] & RR_CMP) |
| 316 | − printk(KERN_INFO "CMP: compare error\n"); | |
| 317 | + pr_info("CMP: compare error\n"); | |
| 317 | 318 | if (buf[i] & RR_CRC) |
| 318 | − printk(KERN_INFO "CRC: CRC error detected\n"); | |
| 319 | + pr_info("CRC: CRC error detected\n"); | |
| 319 | 320 | if (buf[i] & RR_RDP) |
| 320 | − printk(KERN_INFO "RDP: redirected page\n"); | |
| 321 | + pr_info("RDP: redirected page\n"); | |
| 321 | 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) · 4 unmodified lines | ||
| 330 | 331 | * the strong pullup. |
| 331 | 332 | */ |
| 332 | 333 | if (ds_send_control_mode(dev, MOD_PULSE_EN, PULSE_SPUE)) |
| 333 | − printk(KERN_ERR "ds_reset_device: " | |
| 334 | − "Error allowing strong pullup\n"); | |
| 334 | + pr_err("ds_reset_device: Error allowing strong pullup\n"); | |
| 335 | 335 | /* Chip strong pullup time was cleared. */ |
| 336 | 336 | if (dev->spu_sleep) { |
| 337 | 337 | /* lower 4 bits are 0, see ds_set_pullup */ |
| 338 | 338 | u8 del = dev->spu_sleep>>4; |
| 339 | 339 | if (ds_send_control(dev, COMM_SET_DURATION | COMM_IM, del)) |
| 340 | − printk(KERN_ERR "ds_reset_device: " | |
| 341 | − "Error setting duration\n"); | |
| 340 | + pr_err("ds_reset_device: 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) · 18 unmodified lines | ||
| 363 | 362 | u8 buf[ST_SIZE]; |
| 364 | 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 | 366 | usb_clear_halt(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN])); |
| 368 | 367 | |
| 369 | 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) · 21 unmodified lines | ||
| 391 | 390 | count = 0; |
| 392 | 391 | err = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, dev->ep[EP_DATA_OUT]), buf, len, &count, 1000); |
| 393 | 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 | 394 | "err=%d.\n", dev->ep[EP_DATA_OUT], err); |
| 396 | 395 | return err; |
| 397 | 396 | } |
| @@ -475,7 +474,7 @@ static int ds_wait_status(struct ds_device *dev, struct ds_status *st) · 77 unmodified lines | ||
| 475 | 474 | } while (!(st->status & ST_IDLE) && !(err < 0) && ++count < 100); |
| 476 | 475 | |
| 477 | 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 | 478 | ds_reset_device(dev); |
| 480 | 479 | /* Always dump the device status. */ |
| 481 | 480 | count = 101; |
| @@ -992,7 +991,7 @@ static int ds_probe(struct usb_interface *intf, · 510 unmodified lines | ||
| 992 | 991 | |
| 993 | 992 | dev = kzalloc(sizeof(struct ds_device), GFP_KERNEL); |
| 994 | 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 | 995 | return -ENOMEM; |
| 997 | 996 | } |
| 998 | 997 | dev->udev = usb_get_dev(udev); |
| @@ -1024,7 +1023,8 @@ static int ds_probe(struct usb_interface *intf, · 25 unmodified lines | ||
| 1024 | 1023 | |
| 1025 | 1024 | iface_desc = &intf->altsetting[alt]; |
| 1026 | 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 | 1028 | err = -EINVAL; |
| 1029 | 1029 | goto err_out_clear; |
| 1030 | 1030 | } |
| @@ -15,16 +15,13 @@ · 14 unmodified lines | ||
| 15 | 15 | #include <linux/clk.h> |
| 16 | 16 | #include <linux/delay.h> |
| 17 | 17 | #include <linux/io.h> |
| 18 | +#include <linux/jiffies.h> | |
| 18 | 19 | #include <linux/module.h> |
| 19 | 20 | #include <linux/platform_device.h> |
| 20 | 21 | |
| 21 | 22 | #include "../w1.h" |
| 22 | 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 | 26 | * MXC W1 Register offsets |
| 30 | 27 | */ |
| @@ -35,6 +32,7 @@ · 4 unmodified lines | ||
| 35 | 32 | # define MXC_W1_CONTROL_RPP BIT(7) |
| 36 | 33 | #define MXC_W1_TIME_DIVIDER 0x02 |
| 37 | 34 | #define MXC_W1_RESET 0x04 |
| 35 | +# define MXC_W1_RESET_RST BIT(0) | |
| 38 | 36 | |
| 39 | 37 | struct mxc_w1_device { |
| 40 | 38 | void __iomem *regs; |
| @@ -49,24 +47,25 @@ struct mxc_w1_device { · 8 unmodified lines | ||
| 49 | 47 | */ |
| 50 | 48 | static u8 mxc_w1_ds2_reset_bus(void *data) |
| 51 | 49 | { |
| 52 | − u8 reg_val; | |
| 53 | − unsigned int timeout_cnt = 0; | |
| 54 | 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) { | |
| 59 | − reg_val = readb(dev->regs + MXC_W1_CONTROL); | |
| 58 | + udelay(511 + 512); | |
| 60 | 59 | |
| 61 | − if (!(reg_val & MXC_W1_CONTROL_RPP) || | |
| 62 | − timeout_cnt > MXC_W1_RESET_TIMEOUT) | |
| 63 | − break; | |
| 64 | − else | |
| 65 | − timeout_cnt++; | |
| 60 | + do { | |
| 61 | + u8 ctrl = readb(dev->regs + MXC_W1_CONTROL); | |
| 66 | 62 | |
| 67 | − udelay(100); | |
| 68 | − } | |
| 69 | − return !(reg_val & MXC_W1_CONTROL_PST); | |
| 63 | + /* PST bit is valid after the RPP bit is self-cleared */ | |
| 64 | + if (!(ctrl & MXC_W1_CONTROL_RPP)) | |
| 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) · 3 unmodified lines | ||
| 76 | 75 | */ |
| 77 | 76 | static u8 mxc_w1_ds2_touch_bit(void *data, u8 bit) |
| 78 | 77 | { |
| 79 | − struct mxc_w1_device *mdev = data; | |
| 80 | − void __iomem *ctrl_addr = mdev->regs + MXC_W1_CONTROL; | |
| 81 | − unsigned int timeout_cnt = 400; /* Takes max. 120us according to | |
| 82 | − * datasheet. | |
| 83 | − */ | |
| 78 | + struct mxc_w1_device *dev = data; | |
| 79 | + unsigned long timeout; | |
| 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--) { | |
| 88 | − if (!(readb(ctrl_addr) & MXC_W1_CONTROL_WR(bit))) | |
| 89 | − break; | |
| 83 | + /* Wait for read/write bit (60us, Max 120us), use 200us for sure */ | |
| 84 | + timeout = jiffies + usecs_to_jiffies(200); | |
| 90 | 85 | |
| 91 | − udelay(1); | |
| 92 | − } | |
| 86 | + udelay(60); | |
| 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 | 99 | static int mxc_w1_probe(struct platform_device *pdev) |
| @@ -131,6 +133,10 @@ static int mxc_w1_probe(struct platform_device *pdev) · 33 unmodified lines | ||
| 131 | 133 | if (err) |
| 132 | 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 | 140 | writeb(clkdiv - 1, mdev->regs + MXC_W1_TIME_DIVIDER); |
| 135 | 141 | |
| 136 | 142 | mdev->bus_master.data = mdev; |
| @@ -38,6 +38,14 @@ config W1_SLAVE_DS2413 · 37 unmodified lines | ||
| 38 | 38 | Say Y here if you want to use a 1-wire |
| 39 | 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 | 49 | config W1_SLAVE_DS2423 |
| 42 | 50 | tristate "Counter 1-wire device (DS2423)" |
| 43 | 51 | select CRC16 |
| @@ -6,6 +6,7 @@ obj-$(CONFIG_W1_SLAVE_THERM) += w1_therm.o · 5 unmodified lines | ||
| 6 | 6 | obj-$(CONFIG_W1_SLAVE_SMEM) += w1_smem.o |
| 7 | 7 | obj-$(CONFIG_W1_SLAVE_DS2408) += w1_ds2408.o |
| 8 | 8 | obj-$(CONFIG_W1_SLAVE_DS2413) += w1_ds2413.o |
| 9 | +obj-$(CONFIG_W1_SLAVE_DS2406) += w1_ds2406.o | |
| 9 | 10 | obj-$(CONFIG_W1_SLAVE_DS2423) += w1_ds2423.o |
| 10 | 11 | obj-$(CONFIG_W1_SLAVE_DS2431) += w1_ds2431.o |
| 11 | 12 | obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o |
| @@ -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); | |
| @@ -181,8 +181,7 @@ static struct w1_family w1_ds2760_family = { · 180 unmodified lines | ||
| 181 | 181 | |
| 182 | 182 | static int __init w1_ds2760_init(void) |
| 183 | 183 | { |
| 184 | − printk(KERN_INFO "1-Wire driver for the DS2760 battery monitor " | |
| 185 | − " chip - (c) 2004-2005, Szabolcs Gyurko\n"); | |
| 184 | + pr_info("1-Wire driver for the DS2760 battery monitor chip - (c) 2004-2005, Szabolcs Gyurko\n"); | |
| 186 | 185 | ida_init(&bat_ida); |
| 187 | 186 | return w1_register_family(&w1_ds2760_family); |
| 188 | 187 | } |
| @@ -1162,28 +1162,26 @@ static int __init w1_init(void) · 1161 unmodified lines | ||
| 1162 | 1162 | { |
| 1163 | 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 | 1167 | w1_init_netlink(); |
| 1168 | 1168 | |
| 1169 | 1169 | retval = bus_register(&w1_bus_type); |
| 1170 | 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 | 1172 | goto err_out_exit_init; |
| 1173 | 1173 | } |
| 1174 | 1174 | |
| 1175 | 1175 | retval = driver_register(&w1_master_driver); |
| 1176 | 1176 | if (retval) { |
| 1177 | − printk(KERN_ERR | |
| 1178 | − "Failed to register master driver. err=%d.\n", | |
| 1177 | + pr_err("Failed to register master driver. err=%d.\n", | |
| 1179 | 1178 | retval); |
| 1180 | 1179 | goto err_out_bus_unregister; |
| 1181 | 1180 | } |
| 1182 | 1181 | |
| 1183 | 1182 | retval = driver_register(&w1_slave_driver); |
| 1184 | 1183 | if (retval) { |
| 1185 | − printk(KERN_ERR | |
| 1186 | − "Failed to register slave driver. err=%d.\n", | |
| 1184 | + pr_err("Failed to register slave driver. err=%d.\n", | |
| 1187 | 1185 | retval); |
| 1188 | 1186 | goto err_out_master_unregister; |
| 1189 | 1187 | } |
| @@ -87,7 +87,7 @@ void w1_unregister_family(struct w1_family *fent) · 86 unmodified lines | ||
| 87 | 87 | w1_reconnect_slaves(fent, 0); |
| 88 | 88 | |
| 89 | 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 | 91 | fent->fid, atomic_read(&fent->refcnt)); |
| 92 | 92 | |
| 93 | 93 | if (msleep_interruptible(1000)) |
| @@ -40,6 +40,7 @@ · 39 unmodified lines | ||
| 40 | 40 | #define W1_FAMILY_DS2760 0x30 |
| 41 | 41 | #define W1_FAMILY_DS2780 0x32 |
| 42 | 42 | #define W1_FAMILY_DS2413 0x3A |
| 43 | +#define W1_FAMILY_DS2406 0x12 | |
| 43 | 44 | #define W1_THERM_DS1825 0x3B |
| 44 | 45 | #define W1_FAMILY_DS2781 0x3D |
| 45 | 46 | #define W1_THERM_DS28EA00 0x42 |
| @@ -38,7 +38,7 @@ module_param_named(search_count, w1_search_count, int, 0); · 37 unmodified lines | ||
| 38 | 38 | static int w1_enable_pullup = 1; |
| 39 | 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 | 42 | struct device_driver *driver, |
| 43 | 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, · 5 unmodified lines | ||
| 50 | 50 | */ |
| 51 | 51 | dev = kzalloc(sizeof(struct w1_master) + sizeof(struct w1_bus_master), GFP_KERNEL); |
| 52 | 52 | if (!dev) { |
| 53 | − printk(KERN_ERR | |
| 54 | − "Failed to allocate %zd bytes for new w1 device.\n", | |
| 53 | + pr_err("Failed to allocate %zd bytes for new w1 device.\n", | |
| 55 | 54 | sizeof(struct w1_master)); |
| 56 | 55 | return NULL; |
| 57 | 56 | } |
| @@ -91,7 +90,7 @@ static struct w1_master * w1_alloc_dev(u32 id, int slave_count, int slave_ttl, · 33 unmodified lines | ||
| 91 | 90 | |
| 92 | 91 | err = device_register(&dev->dev); |
| 93 | 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 | 94 | memset(dev, 0, sizeof(struct w1_master)); |
| 96 | 95 | kfree(dev); |
| 97 | 96 | dev = NULL; |
| @@ -116,13 +115,13 @@ int w1_add_master_device(struct w1_bus_master *master) · 18 unmodified lines | ||
| 116 | 115 | struct w1_netlink_msg msg; |
| 117 | 116 | int id, found; |
| 118 | 117 | |
| 119 | − /* validate minimum functionality */ | |
| 120 | − if (!(master->touch_bit && master->reset_bus) && | |
| 121 | − !(master->write_bit && master->read_bit) && | |
| 118 | + /* validate minimum functionality */ | |
| 119 | + if (!(master->touch_bit && master->reset_bus) && | |
| 120 | + !(master->write_bit && master->read_bit) && | |
| 122 | 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 | 123 | return(-EINVAL); |
| 125 | − } | |
| 124 | + } | |
| 126 | 125 | |
| 127 | 126 | /* Lock until the device is added (or not) to w1_masters. */ |
| 128 | 127 | mutex_lock(&w1_mlock); |
| @@ -254,7 +253,7 @@ void w1_remove_master_device(struct w1_bus_master *bm) · 125 unmodified lines | ||
| 254 | 253 | } |
| 255 | 254 | |
| 256 | 255 | if (!found) { |
| 257 | − printk(KERN_ERR "Device doesn't exist.\n"); | |
| 256 | + pr_err("Device doesn't exist.\n"); | |
| 258 | 257 | return; |
| 259 | 258 | } |
| 260 | 259 | |
| @@ -29,8 +29,8 @@ · 28 unmodified lines | ||
| 29 | 29 | #else |
| 30 | 30 | # define assert(expr) \ |
| 31 | 31 | if(unlikely(!(expr))) { \ |
| 32 | − printk(KERN_ERR "Assertion failed! %s,%s,%s,line=%d\n", \ | |
| 33 | − #expr, __FILE__, __func__, __LINE__); \ | |
| 32 | + pr_err("Assertion failed! %s,%s,%s,line=%d\n", \ | |
| 33 | + #expr, __FILE__, __func__, __LINE__); \ | |
| 34 | 34 | } |
| 35 | 35 | #endif |
| 36 | 36 | |
| @@ -680,8 +680,7 @@ static void w1_cn_callback(struct cn_msg *cn, struct netlink_skb_parms *nsp) · 679 unmodified lines | ||
| 680 | 680 | if (sl) |
| 681 | 681 | dev = sl->master; |
| 682 | 682 | } else { |
| 683 | − printk(KERN_NOTICE | |
| 684 | − "%s: cn: %x.%x, wrong type: %u, len: %u.\n", | |
| 683 | + pr_notice("%s: cn: %x.%x, wrong type: %u, len: %u.\n", | |
| 685 | 684 | __func__, cn->id.idx, cn->id.val, |
| 686 | 685 | msg->type, msg->len); |
| 687 | 686 | err = -EPROTO; |
| @@ -268,6 +268,9 @@ · 267 unmodified lines | ||
| 268 | 268 | VMLINUX_SYMBOL(__start_pci_fixups_suspend) = .; \ |
| 269 | 269 | *(.pci_fixup_suspend) \ |
| 270 | 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 | 276 | /* Built-in firmware blobs */ \ |
| @@ -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 */ | |
| @@ -127,6 +127,9 @@ struct arizona_pdata { · 126 unmodified lines | ||
| 127 | 127 | /** Internal pull on GPIO5 is disabled when used for jack detection */ |
| 128 | 128 | bool jd_gpio5_nopull; |
| 129 | 129 | |
| 130 | + /** set to true if jackdet contact opens on insert */ | |
| 131 | + bool jd_invert; | |
| 132 | + | |
| 130 | 133 | /** Use the headphone detect circuit to identify the accessory */ |
| 131 | 134 | bool hpdet_acc_id; |
| 132 | 135 | |
| @@ -262,6 +262,41 @@ enum max77693_irq_source { · 261 unmodified lines | ||
| 262 | 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 | 300 | enum max77693_irq { |
| 266 | 301 | /* PMIC - FLASH */ |
| 267 | 302 | MAX77693_LED_IRQ_FLED2_OPEN, |
| @@ -282,6 +317,10 @@ enum max77693_irq { · 14 unmodified lines | ||
| 282 | 317 | MAX77693_CHG_IRQ_CHG_I, |
| 283 | 318 | MAX77693_CHG_IRQ_CHGIN_I, |
| 284 | 319 | |
| 320 | + MAX77693_IRQ_NR, | |
| 321 | +}; | |
| 322 | + | |
| 323 | +enum max77693_irq_muic { | |
| 285 | 324 | /* MUIC INT1 */ |
| 286 | 325 | MAX77693_MUIC_IRQ_INT1_ADC, |
| 287 | 326 | MAX77693_MUIC_IRQ_INT1_ADC_LOW, |
| @@ -304,7 +343,7 @@ enum max77693_irq { · 16 unmodified lines | ||
| 304 | 343 | MAX77693_MUIC_IRQ_INT3_CHG_ENABLED, |
| 305 | 344 | MAX77693_MUIC_IRQ_INT3_BAT_DET, |
| 306 | 345 | |
| 307 | − MAX77693_IRQ_NR, | |
| 346 | + MAX77693_MUIC_IRQ_NR, | |
| 308 | 347 | }; |
| 309 | 348 | |
| 310 | 349 | struct max77693_dev { |
| @@ -319,7 +358,10 @@ struct max77693_dev { · 8 unmodified lines | ||
| 319 | 358 | struct regmap *regmap_muic; |
| 320 | 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 | 366 | int irq; |
| 325 | 367 | int irq_gpio; |
| @@ -332,14 +374,6 @@ enum max77693_types { · 6 unmodified lines | ||
| 332 | 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 | 377 | extern int max77693_irq_init(struct max77693_dev *max77686); |
| 344 | 378 | extern void max77693_irq_exit(struct max77693_dev *max77686); |
| 345 | 379 | extern int max77693_irq_resume(struct max77693_dev *max77686); |
| @@ -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 */ | |
| @@ -1477,8 +1477,9 @@ enum pci_fixup_pass { · 1476 unmodified lines | ||
| 1477 | 1477 | pci_fixup_final, /* Final phase of device fixups */ |
| 1478 | 1478 | pci_fixup_enable, /* pci_enable_device() time */ |
| 1479 | 1479 | pci_fixup_resume, /* pci_device_resume() */ |
| 1480 | − pci_fixup_suspend, /* pci_device_suspend */ | |
| 1480 | + pci_fixup_suspend, /* pci_device_suspend() */ | |
| 1481 | 1481 | pci_fixup_resume_early, /* pci_device_resume_early() */ |
| 1482 | + pci_fixup_suspend_late, /* pci_device_suspend_late() */ | |
| 1482 | 1483 | }; |
| 1483 | 1484 | |
| 1484 | 1485 | /* Anonymous variables would be nice... */ |
| @@ -1519,6 +1520,11 @@ enum pci_fixup_pass { · 34 unmodified lines | ||
| 1519 | 1520 | DECLARE_PCI_FIXUP_SECTION(.pci_fixup_suspend, \ |
| 1520 | 1521 | suspend##hook, vendor, device, class, \ |
| 1521 | 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 | 1529 | #define DECLARE_PCI_FIXUP_EARLY(vendor, device, hook) \ |
| 1524 | 1530 | DECLARE_PCI_FIXUP_SECTION(.pci_fixup_early, \ |
| @@ -1544,6 +1550,10 @@ enum pci_fixup_pass { · 19 unmodified lines | ||
| 1544 | 1550 | DECLARE_PCI_FIXUP_SECTION(.pci_fixup_suspend, \ |
| 1545 | 1551 | suspend##hook, vendor, device, \ |
| 1546 | 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 | 1558 | #ifdef CONFIG_PCI_QUIRKS |
| 1549 | 1559 | void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev); |
| @@ -328,6 +328,7 @@ enum genwqe_card_state { · 327 unmodified lines | ||
| 328 | 328 | GENWQE_CARD_UNUSED = 0, |
| 329 | 329 | GENWQE_CARD_USED = 1, |
| 330 | 330 | GENWQE_CARD_FATAL_ERROR = 2, |
| 331 | + GENWQE_CARD_RELOAD_BITSTREAM = 3, | |
| 331 | 332 | GENWQE_CARD_STATE_MAX, |
| 332 | 333 | }; |
| 333 | 334 | |
| @@ -34,7 +34,8 @@ · 33 unmodified lines | ||
| 34 | 34 | #define I8K_FAN_OFF 0 |
| 35 | 35 | #define I8K_FAN_LOW 1 |
| 36 | 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 | 40 | #define I8K_VOL_UP 1 |
| 40 | 41 | #define I8K_VOL_DOWN 2 |
| @@ -88,7 +88,8 @@ static int hv_start_fcopy(struct hv_start_fcopy *smsg) · 87 unmodified lines | ||
| 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 | 93 | if (target_fd == -1) { |
| 93 | 94 | syslog(LOG_INFO, "Open Failed: %s", strerror(errno)); |
| 94 | 95 | goto done; |
| @@ -12,6 +12,9 @@ TARGETS += powerpc · 11 unmodified lines | ||
| 12 | 12 | TARGETS += user |
| 13 | 13 | TARGETS += sysctl |
| 14 | 14 | |
| 15 | +TARGETS_HOTPLUG = cpu-hotplug | |
| 16 | +TARGETS_HOTPLUG += memory-hotplug | |
| 17 | + | |
| 15 | 18 | all: |
| 16 | 19 | for TARGET in $(TARGETS); do \ |
| 17 | 20 | make -C $$TARGET; \ |
| @@ -22,6 +25,21 @@ run_tests: all · 4 unmodified lines | ||
| 22 | 25 | make -C $$TARGET run_tests; \ |
| 23 | 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 | 43 | clean: |
| 26 | 44 | for TARGET in $(TARGETS); do \ |
| 27 | 45 | make -C $$TARGET clean; \ |
| @@ -4,8 +4,15 @@ The kernel contains a set of "self tests" under the tools/testing/selftests/ · 3 unmodified lines | ||
| 4 | 4 | directory. These are intended to be small unit tests to exercise individual |
| 5 | 5 | code paths in the kernel. |
| 6 | 6 | |
| 7 | −Running the selftests | |
| 8 | −===================== | |
| 7 | +On some systems, hot-plug tests could hang forever waiting for cpu and | |
| 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 | 17 | To build the tests: |
| 11 | 18 | |
| @@ -18,14 +25,26 @@ To run the tests: · 6 unmodified lines | ||
| 18 | 25 | |
| 19 | 26 | - note that some tests will require root privileges. |
| 20 | 27 | |
| 21 | − | |
| 22 | −To run only tests targetted for a single subsystem: | |
| 28 | +To run only tests targeted for a single subsystem: (including | |
| 29 | +hotplug targets in limited mode) | |
| 23 | 30 | |
| 24 | 31 | $ make -C tools/testing/selftests TARGETS=cpu-hotplug run_tests |
| 25 | 32 | |
| 26 | 33 | See the top-level tools/testing/selftests/Makefile for the list of all possible |
| 27 | 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 | 49 | Contributing new tests |
| 31 | 50 | ====================== |
| @@ -3,4 +3,7 @@ all: · 2 unmodified lines | ||
| 3 | 3 | run_tests: |
| 4 | 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 | 9 | clean: |
| @@ -11,6 +11,8 @@ prerequisite() · 10 unmodified lines | ||
| 11 | 11 | exit 0 |
| 12 | 12 | fi |
| 13 | 13 | |
| 14 | + taskset -p 01 $$ | |
| 15 | + | |
| 14 | 16 | SYSFS=`mount -t sysfs | head -1 | awk '{ print $3 }'` |
| 15 | 17 | |
| 16 | 18 | if [ ! -d "$SYSFS" ]; then |
| @@ -22,6 +24,19 @@ prerequisite() · 5 unmodified lines | ||
| 22 | 24 | echo $msg cpu hotplug is not supported >&2 |
| 23 | 25 | exit 0 |
| 24 | 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() · 85 unmodified lines | ||
| 113 | 128 | } |
| 114 | 129 | |
| 115 | 130 | error=-12 |
| 131 | +allcpus=0 | |
| 116 | 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 | 139 | case $opt in |
| 120 | 140 | e) |
| 121 | 141 | error=$OPTARG |
| 122 | 142 | ;; |
| 143 | + a) | |
| 144 | + allcpus=1 | |
| 145 | + ;; | |
| 123 | 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 | 150 | exit |
| 126 | 151 | ;; |
| 127 | 152 | p) |
| @@ -137,6 +162,29 @@ fi · 9 unmodified lines | ||
| 137 | 162 | |
| 138 | 163 | prerequisite |
| 139 | 164 | |
| 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 | + | |
| 140 | 188 | # |
| 141 | 189 | # Online all hot-pluggable CPUs |
| 142 | 190 | # |
| @@ -81,7 +81,7 @@ int main(int argc, char **argv) · 80 unmodified lines | ||
| 81 | 81 | /* Compare with self */ |
| 82 | 82 | ret = sys_kcmp(pid1, pid1, KCMP_VM, 0, 0); |
| 83 | 83 | if (ret) { |
| 84 | − printf("FAIL: 0 expected but %li returned (%s)\n", | |
| 84 | + printf("FAIL: 0 expected but %d returned (%s)\n", | |
| 85 | 85 | ret, strerror(errno)); |
| 86 | 86 | ret = -1; |
| 87 | 87 | } else |
| @@ -1,6 +1,9 @@ | ||
| 1 | 1 | all: |
| 2 | 2 | |
| 3 | 3 | run_tests: |
| 4 | + @/bin/bash ./on-off-test.sh -r 2 || echo "memory-hotplug selftests: [FAIL]" | |
| 5 | + | |
| 6 | +run_full_test: | |
| 4 | 7 | @/bin/bash ./on-off-test.sh || echo "memory-hotplug selftests: [FAIL]" |
| 5 | 8 | |
| 6 | 9 | clean: |
| @@ -142,10 +142,16 @@ fi · 141 unmodified lines | ||
| 142 | 142 | |
| 143 | 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 | 151 | # Online all hot-pluggable memory |
| 147 | 152 | # |
| 148 | 153 | for memory in `hotplaggable_offline_memory`; do |
| 154 | + echo offline-online $memory | |
| 149 | 155 | online_memory_expect_success $memory |
| 150 | 156 | done |
| 151 | 157 | |
| @@ -154,6 +160,7 @@ done · 2 unmodified lines | ||
| 154 | 160 | # |
| 155 | 161 | for memory in `hotpluggable_online_memory`; do |
| 156 | 162 | if [ $((RANDOM % 100)) -lt $ratio ]; then |
| 163 | + echo online-offline $memory | |
| 157 | 164 | offline_memory_expect_success $memory |
| 158 | 165 | fi |
| 159 | 166 | done |
| @@ -162,6 +169,7 @@ done · 2 unmodified lines | ||
| 162 | 169 | # Online all hot-pluggable memory again |
| 163 | 170 | # |
| 164 | 171 | for memory in `hotplaggable_offline_memory`; do |
| 172 | + echo offline-online $memory | |
| 165 | 173 | online_memory_expect_success $memory |
| 166 | 174 | done |
| 167 | 175 | |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | all: |
| 2 | − gcc -O2 -lrt mq_open_tests.c -o mq_open_tests | |
| 3 | − gcc -O2 -lrt -lpthread -lpopt -o mq_perf_tests mq_perf_tests.c | |
| 2 | + gcc -O2 mq_open_tests.c -o mq_open_tests -lrt | |
| 3 | + gcc -O2 -o mq_perf_tests mq_perf_tests.c -lrt -lpthread -lpopt | |
| 4 | 4 | |
| 5 | 5 | run_tests: |
| 6 | 6 | @./mq_open_tests /test1 || echo "mq_open_tests: [FAIL]" |
| @@ -80,7 +80,8 @@ void shutdown(int exit_val, char *err_cause, int line_no) · 79 unmodified lines | ||
| 80 | 80 | if (in_shutdown++) |
| 81 | 81 | return; |
| 82 | 82 | |
| 83 | − seteuid(0); | |
| 83 | + if (seteuid(0) == -1) | |
| 84 | + perror("seteuid() failed"); | |
| 84 | 85 | |
| 85 | 86 | if (queue != -1) |
| 86 | 87 | if (mq_close(queue)) |
| @@ -292,8 +293,10 @@ int main(int argc, char *argv[]) · 205 unmodified lines | ||
| 292 | 293 | /* Tell the user our initial state */ |
| 293 | 294 | printf("\nInitial system state:\n"); |
| 294 | 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(hard):\t\t%d\n", saved_limits.rlim_max); | |
| 296 | + printf("\tRLIMIT_MSGQUEUE(soft):\t\t%ld\n", | |
| 297 | + (long) saved_limits.rlim_cur); | |
| 298 | + printf("\tRLIMIT_MSGQUEUE(hard):\t\t%ld\n", | |
| 299 | + (long) saved_limits.rlim_max); | |
| 297 | 300 | printf("\tMaximum Message Size:\t\t%d\n", saved_max_msgsize); |
| 298 | 301 | printf("\tMaximum Queue Size:\t\t%d\n", saved_max_msgs); |
| 299 | 302 | if (default_settings) { |
| @@ -308,8 +311,8 @@ int main(int argc, char *argv[]) · 8 unmodified lines | ||
| 308 | 311 | validate_current_settings(); |
| 309 | 312 | |
| 310 | 313 | printf("Adjusted system state for testing:\n"); |
| 311 | − printf("\tRLIMIT_MSGQUEUE(soft):\t\t%d\n", cur_limits.rlim_cur); | |
| 312 | − printf("\tRLIMIT_MSGQUEUE(hard):\t\t%d\n", cur_limits.rlim_max); | |
| 314 | + printf("\tRLIMIT_MSGQUEUE(soft):\t\t%ld\n", (long) cur_limits.rlim_cur); | |
| 315 | + printf("\tRLIMIT_MSGQUEUE(hard):\t\t%ld\n", (long) cur_limits.rlim_max); | |
| 313 | 316 | printf("\tMaximum Message Size:\t\t%d\n", cur_max_msgsize); |
| 314 | 317 | printf("\tMaximum Queue Size:\t\t%d\n", cur_max_msgs); |
| 315 | 318 | if (default_settings) { |
| @@ -454,7 +457,12 @@ int main(int argc, char *argv[]) · 138 unmodified lines | ||
| 454 | 457 | else |
| 455 | 458 | printf("Queue open with total size > 2GB when euid = 0 " |
| 456 | 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 | 466 | attr.mq_maxmsg = cur_max_msgs; |
| 459 | 467 | attr.mq_msgsize = cur_max_msgsize; |
| 460 | 468 | if (test_queue_fail(&attr, &result)) |
| @@ -296,9 +296,9 @@ static inline void open_queue(struct mq_attr *attr) · 295 unmodified lines | ||
| 296 | 296 | printf("\n\tQueue %s created:\n", queue_path); |
| 297 | 297 | printf("\t\tmq_flags:\t\t\t%s\n", result.mq_flags & O_NONBLOCK ? |
| 298 | 298 | "O_NONBLOCK" : "(null)"); |
| 299 | − printf("\t\tmq_maxmsg:\t\t\t%d\n", result.mq_maxmsg); | |
| 300 | − printf("\t\tmq_msgsize:\t\t\t%d\n", result.mq_msgsize); | |
| 301 | − printf("\t\tmq_curmsgs:\t\t\t%d\n", result.mq_curmsgs); | |
| 299 | + printf("\t\tmq_maxmsg:\t\t\t%lu\n", result.mq_maxmsg); | |
| 300 | + printf("\t\tmq_msgsize:\t\t\t%lu\n", result.mq_msgsize); | |
| 301 | + printf("\t\tmq_curmsgs:\t\t\t%lu\n", result.mq_curmsgs); | |
| 302 | 302 | } |
| 303 | 303 | |
| 304 | 304 | void *fake_cont_thread(void *arg) |
| @@ -440,7 +440,7 @@ void *perf_test_thread(void *arg) · 135 unmodified lines | ||
| 440 | 440 | shutdown(2, "clock_getres()", __LINE__); |
| 441 | 441 | |
| 442 | 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 | 444 | res.tv_nsec > 1 ? "s" : ""); |
| 445 | 445 | |
| 446 | 446 | |
| @@ -454,20 +454,20 @@ void *perf_test_thread(void *arg) · 7 unmodified lines | ||
| 454 | 454 | recv_total.tv_nsec = 0; |
| 455 | 455 | for (i = 0; i < TEST1_LOOPS; i++) |
| 456 | 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 | 458 | send_total.tv_sec, send_total.tv_nsec); |
| 459 | 459 | nsec = ((unsigned long long)send_total.tv_sec * 1000000000 + |
| 460 | 460 | send_total.tv_nsec) / TEST1_LOOPS; |
| 461 | − printf("\t\t\t\t\t\t%d nsec/msg\n", nsec); | |
| 462 | − printf("\t\tRecv msg:\t\t\t%d.%ds total time\n", | |
| 461 | + printf("\t\t\t\t\t\t%lld nsec/msg\n", nsec); | |
| 462 | + printf("\t\tRecv msg:\t\t\t%ld.%lus total time\n", | |
| 463 | 463 | recv_total.tv_sec, recv_total.tv_nsec); |
| 464 | 464 | nsec = ((unsigned long long)recv_total.tv_sec * 1000000000 + |
| 465 | 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 | 469 | for (cur_test = test2; cur_test->desc != NULL; cur_test++) { |
| 470 | − printf(cur_test->desc); | |
| 470 | + printf("%s:\n", cur_test->desc); | |
| 471 | 471 | printf("\t\t(%d iterations)\n", TEST2_LOOPS); |
| 472 | 472 | prio_out = 0; |
| 473 | 473 | send_total.tv_sec = 0; |
| @@ -493,16 +493,16 @@ void *perf_test_thread(void *arg) · 19 unmodified lines | ||
| 493 | 493 | cur_test->func(&prio_out); |
| 494 | 494 | } |
| 495 | 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 | 497 | send_total.tv_sec, send_total.tv_nsec); |
| 498 | 498 | nsec = ((unsigned long long)send_total.tv_sec * 1000000000 + |
| 499 | 499 | send_total.tv_nsec) / TEST2_LOOPS; |
| 500 | − printf("\t\t\t\t\t\t%d nsec/msg\n", nsec); | |
| 501 | − printf("\t\tRecv msg:\t\t\t%d.%ds total time\n", | |
| 500 | + printf("\t\t\t\t\t\t%lld nsec/msg\n", nsec); | |
| 501 | + printf("\t\tRecv msg:\t\t\t%ld.%lus total time\n", | |
| 502 | 502 | recv_total.tv_sec, recv_total.tv_nsec); |
| 503 | 503 | nsec = ((unsigned long long)recv_total.tv_sec * 1000000000 + |
| 504 | 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 | 506 | printf("\t\tDraining queue..."); |
| 507 | 507 | fflush(stdout); |
| 508 | 508 | clock_gettime(clock, &start); |
| @@ -653,8 +653,10 @@ int main(int argc, char *argv[]) · 144 unmodified lines | ||
| 653 | 653 | /* Tell the user our initial state */ |
| 654 | 654 | printf("\nInitial system state:\n"); |
| 655 | 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); | |
| 657 | − printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t%d\n", saved_limits.rlim_max); | |
| 656 | + printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t%ld\n", | |
| 657 | + (long) saved_limits.rlim_cur); | |
| 658 | + printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t%ld\n", | |
| 659 | + (long) saved_limits.rlim_max); | |
| 658 | 660 | printf("\tMaximum Message Size:\t\t\t%d\n", saved_max_msgsize); |
| 659 | 661 | printf("\tMaximum Queue Size:\t\t\t%d\n", saved_max_msgs); |
| 660 | 662 | printf("\tNice value:\t\t\t\t%d\n", cur_nice); |
| @@ -667,10 +669,10 @@ int main(int argc, char *argv[]) · 6 unmodified lines | ||
| 667 | 669 | printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t(unlimited)\n"); |
| 668 | 670 | printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t(unlimited)\n"); |
| 669 | 671 | } else { |
| 670 | − printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t%d\n", | |
| 671 | − cur_limits.rlim_cur); | |
| 672 | − printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t%d\n", | |
| 673 | − cur_limits.rlim_max); | |
| 672 | + printf("\tRLIMIT_MSGQUEUE(soft):\t\t\t%ld\n", | |
| 673 | + (long) cur_limits.rlim_cur); | |
| 674 | + printf("\tRLIMIT_MSGQUEUE(hard):\t\t\t%ld\n", | |
| 675 | + (long) cur_limits.rlim_max); | |
| 674 | 676 | } |
| 675 | 677 | printf("\tMaximum Message Size:\t\t\t%d\n", cur_max_msgsize); |
| 676 | 678 | printf("\tMaximum Queue Size:\t\t\t%d\n", cur_max_msgs); |