only safe with partial memory descriptor list support enabled
(allow_ext_sg=1).
* comp_vector, a number in the range 0..n-1 specifying the
- MSI-X completion vector. Some HCA's allocate multiple (n)
- MSI-X vectors per HCA port. If the IRQ affinity masks of
- these interrupts have been configured such that each MSI-X
- interrupt is handled by a different CPU then the comp_vector
- parameter can be used to spread the SRP completion workload
- over multiple CPU's.
+ MSI-X completion vector of the first RDMA channel. Some
+ HCA's allocate multiple (n) MSI-X vectors per HCA port. If
+ the IRQ affinity masks of these interrupts have been
+ configured such that each MSI-X interrupt is handled by a
+ different CPU then the comp_vector parameter can be used to
+ spread the SRP completion workload over multiple CPU's.
* tl_retry_count, a number in the range 2..7 specifying the
IB RC retry count.
* queue_size, the maximum number of commands that the
descriptor list in an SRP_CMD when communicating with an SRP
target.
+What: /sys/class/scsi_host/host<n>/ch_count
+Date: April 1, 2015
+KernelVersion: 3.19
+Contact: linux-rdma@vger.kernel.org
+Description: Number of RDMA channels used for communication with the SRP
+ target.
+
What: /sys/class/scsi_host/host<n>/cmd_sg_entries
Date: May 19, 2011
KernelVersion: 2.6.39
Description: Maximum number of data buffer descriptors that may be sent to
the target in a single SRP_CMD request.
+What: /sys/class/scsi_host/host<n>/comp_vector
+Date: September 2, 2013
+KernelVersion: 3.11
+Contact: linux-rdma@vger.kernel.org
+Description: Completion vector used for the first RDMA channel.
+
What: /sys/class/scsi_host/host<n>/dgid
Date: June 17, 2006
KernelVersion: 2.6.17
--- /dev/null
+The arm64 port of the Linux kernel provides infrastructure to support
+emulation of instructions which have been deprecated, or obsoleted in
+the architecture. The infrastructure code uses undefined instruction
+hooks to support emulation. Where available it also allows turning on
+the instruction execution in hardware.
+
+The emulation mode can be controlled by writing to sysctl nodes
+(/proc/sys/abi). The following explains the different execution
+behaviours and the corresponding values of the sysctl nodes -
+
+* Undef
+ Value: 0
+ Generates undefined instruction abort. Default for instructions that
+ have been obsoleted in the architecture, e.g., SWP
+
+* Emulate
+ Value: 1
+ Uses software emulation. To aid migration of software, in this mode
+ usage of emulated instruction is traced as well as rate limited
+ warnings are issued. This is the default for deprecated
+ instructions, .e.g., CP15 barriers
+
+* Hardware Execution
+ Value: 2
+ Although marked as deprecated, some implementations may support the
+ enabling/disabling of hardware support for the execution of these
+ instructions. Using hardware execution generally provides better
+ performance, but at the loss of ability to gather runtime statistics
+ about the use of the deprecated instructions.
+
+The default mode depends on the status of the instruction in the
+architecture. Deprecated instructions should default to emulation
+while obsolete instructions must be undefined by default.
+
+Supported legacy instructions
+-----------------------------
+* SWP{B}
+Node: /proc/sys/abi/swp
+Status: Obsolete
+Default: Undef (0)
+
+* CP15 Barriers
+Node: /proc/sys/abi/cp15_barrier
+Status: Deprecated
+Default: Emulate (1)
that requests are restarted in the order they were queue. This may happen
if the driver needs to use blk_queue_invalidate_tags().
-Tagging also defines a new request flag, REQ_QUEUED. This is set whenever
-a request is currently tagged. You should not use this flag directly,
-blk_rq_tagged(rq) is the portable way to do so.
-
3.3 I/O Submission
The routine submit_bio() is used to submit a single io. Higher level i/o
'discard_promote_adjustment <value>'
The sequential threshold indicates the number of contiguous I/Os
-required before a stream is treated as sequential. The random threshold
+required before a stream is treated as sequential. Once a stream is
+considered sequential it will bypass the cache. The random threshold
is the number of intervening non-contiguous I/Os that must be seen
before the stream is treated as random again.
The sequential and random thresholds default to 512 and 4 respectively.
-Large, sequential ios are probably better left on the origin device
-since spindles tend to have good bandwidth. The io_tracker counts
-contiguous I/Os to try to spot when the io is in one of these sequential
-modes.
-
-Internally the mq policy maintains a promotion threshold variable. If
-the hit count of a block not in the cache goes above this threshold it
-gets promoted to the cache. The read, write and discard promote adjustment
+Large, sequential I/Os are probably better left on the origin device
+since spindles tend to have good sequential I/O bandwidth. The
+io_tracker counts contiguous I/Os to try to spot when the I/O is in one
+of these sequential modes. But there are use-cases for wanting to
+promote sequential blocks to the cache (e.g. fast application startup).
+If sequential threshold is set to 0 the sequential I/O detection is
+disabled and sequential I/O will no longer implicitly bypass the cache.
+Setting the random threshold to 0 does _not_ disable the random I/O
+stream detection.
+
+Internally the mq policy determines a promotion threshold. If the hit
+count of a block not in the cache goes above this threshold it gets
+promoted to the cache. The read, write and discard promote adjustment
tunables allow you to tweak the promotion threshold by adding a small
value based on the io type. They default to 4, 8 and 1 respectively.
If you're trying to quickly warm a new cache device you may wish to
Required properties:
- compatible : should contain one of the following:
- "renesas,sata-r8a7779" for R-Car H1
- - "renesas,sata-r8a7790" for R-Car H2
- - "renesas,sata-r8a7791" for R-Car M2
+ - "renesas,sata-r8a7790-es1" for R-Car H2 ES1
+ - "renesas,sata-r8a7790" for R-Car H2 other than ES1
+ - "renesas,sata-r8a7791" for R-Car M2-W
+ - "renesas,sata-r8a7793" for R-Car M2-N
- reg : address and length of the SATA registers;
- interrupts : must consist of one interrupt specifier.
--- /dev/null
+ltc2978
+
+Required properties:
+- compatible: should contain one of:
+ * "lltc,ltc2974"
+ * "lltc,ltc2977"
+ * "lltc,ltc2978"
+ * "lltc,ltc3880"
+ * "lltc,ltc3883"
+ * "lltc,ltm4676"
+- reg: I2C slave address
+
+Optional properties:
+- regulators: A node that houses a sub-node for each regulator controlled by
+ the device. Each sub-node is identified using the node's name, with valid
+ values listed below. The content of each sub-node is defined by the
+ standard binding for regulators; see regulator.txt.
+
+Valid names of regulators depend on number of supplies supported per device:
+ * ltc2974 : vout0 - vout3
+ * ltc2977 : vout0 - vout7
+ * ltc2978 : vout0 - vout7
+ * ltc3880 : vout0 - vout1
+ * ltc3883 : vout0
+ * ltm4676 : vout0 - vout1
+
+Example:
+ltc2978@5e {
+ compatible = "lltc,ltc2978";
+ reg = <0x5e>;
+ regulators {
+ vout0 {
+ regulator-name = "FPGA-2.5V";
+ };
+ vout2 {
+ regulator-name = "FPGA-1.5V";
+ };
+ };
+};
Example:
interrupts-extended = <&intc1 5 1>, <&intc2 1 0>;
-A device node may contain either "interrupts" or "interrupts-extended", but not
-both. If both properties are present, then the operating system should log an
-error and use only the data in "interrupts".
-
2) Interrupt controller nodes
-----------------------------
--- /dev/null
+Device-Tree bindings for Atmel's HLCDC (High LCD Controller) MFD driver
+
+Required properties:
+ - compatible: value should be one of the following:
+ "atmel,sama5d3-hlcdc"
+ - reg: base address and size of the HLCDC device registers.
+ - clock-names: the name of the 3 clocks requested by the HLCDC device.
+ Should contain "periph_clk", "sys_clk" and "slow_clk".
+ - clocks: should contain the 3 clocks requested by the HLCDC device.
+ - interrupts: should contain the description of the HLCDC interrupt line
+
+The HLCDC IP exposes two subdevices:
+ - a PWM chip: see ../pwm/atmel-hlcdc-pwm.txt
+ - a Display Controller: see ../drm/atmel-hlcdc-dc.txt
+
+Example:
+
+ hlcdc: hlcdc@f0030000 {
+ compatible = "atmel,sama5d3-hlcdc";
+ reg = <0xf0030000 0x2000>;
+ clocks = <&lcdc_clk>, <&lcdck>, <&clk32k>;
+ clock-names = "periph_clk","sys_clk", "slow_clk";
+ interrupts = <36 IRQ_TYPE_LEVEL_HIGH 0>;
+ status = "disabled";
+
+ hlcdc-display-controller {
+ compatible = "atmel,hlcdc-display-controller";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_lcd_base &pinctrl_lcd_rgb888>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+
+ hlcdc_panel_output: endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&panel_input>;
+ };
+ };
+ };
+
+ hlcdc_pwm: hlcdc-pwm {
+ compatible = "atmel,hlcdc-pwm";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_lcd_pwm>;
+ #pwm-cells = <3>;
+ };
+ };
-BUCKn : for BUCKs, where n can lie in range 1 to 9.
example: BUCK1, BUCK5, BUCK9.
+ Regulators which can be turned off during system suspend:
+ -LDOn : 2, 6-8, 10-12, 14-16,
+ -BUCKn : 1-4.
+ Use standard regulator bindings for it ('regulator-off-in-suspend').
+
+
Example:
max77686@09 {
[*] refer Documentation/devicetree/bindings/regulator/regulator.txt
+- haptic : The MAX77693 haptic device utilises a PWM controlled motor to provide
+ users with tactile feedback. PWM period and duty-cycle are varied in
+ order to provide the approprite level of feedback.
+
+ Required properties:
+ - compatible : Must be "maxim,max77693-hpatic"
+ - haptic-supply : power supply for the haptic motor
+ [*] refer Documentation/devicetree/bindings/regulator/regulator.txt
+ - pwms : phandle to the physical PWM(Pulse Width Modulation) device.
+ PWM properties should be named "pwms". And number of cell is different
+ for each pwm device.
+ To get more informations, please refer to documentaion.
+ [*] refer Documentation/devicetree/bindings/pwm/pwm.txt
+
Example:
max77693@66 {
compatible = "maxim,max77693";
regulator-boot-on;
};
};
+
+ haptic {
+ compatible = "maxim,max77693-haptic";
+ haptic-supply = <&haptic_supply>;
+ pwms = <&pwm 0 40000 0>;
+ pwm-names = "haptic";
+ };
};
-* Samsung S2MPS11, S2MPS14 and S2MPU02 Voltage and Current Regulator
+* Samsung S2MPS11, S2MPS13, S2MPS14 and S2MPU02 Voltage and Current Regulator
The Samsung S2MPS11 is a multi-function device which includes voltage and
current regulators, RTC, charger controller and other sub-blocks. It is
addressed by the host system using different I2C slave addresses.
Required properties:
-- compatible: Should be "samsung,s2mps11-pmic" or "samsung,s2mps14-pmic"
- or "samsung,s2mpu02-pmic".
+- compatible: Should be "samsung,s2mps11-pmic" or "samsung,s2mps13-pmic"
+ or "samsung,s2mps14-pmic" or "samsung,s2mpu02-pmic".
- reg: Specifies the I2C slave address of the pmic block. It should be 0x66.
Optional properties:
- interrupts: Interrupt specifiers for interrupt sources.
Optional nodes:
-- clocks: s2mps11 and s5m8767 provide three(AP/CP/BT) buffered 32.768 KHz
- outputs, so to register these as clocks with common clock framework
+- clocks: s2mps11, s2mps13 and s5m8767 provide three(AP/CP/BT) buffered 32.768
+ KHz outputs, so to register these as clocks with common clock framework
instantiate a sub-node named "clocks". It uses the common clock binding
documented in :
[Documentation/devicetree/bindings/clock/clock-bindings.txt]
the clock which they consume.
Clock ID Devices
----------------------------------------------------------
- 32KhzAP 0 S2MPS11, S2MPS14, S5M8767
- 32KhzCP 1 S2MPS11, S5M8767
- 32KhzBT 2 S2MPS11, S2MPS14, S5M8767
+ 32KhzAP 0 S2MPS11, S2MPS13, S2MPS14, S5M8767
+ 32KhzCP 1 S2MPS11, S2MPS13, S5M8767
+ 32KhzBT 2 S2MPS11, S2MPS13, S2MPS14, S5M8767
- - compatible: Should be one of: "samsung,s2mps11-clk", "samsung,s2mps14-clk",
- "samsung,s5m8767-clk"
+ - compatible: Should be one of: "samsung,s2mps11-clk", "samsung,s2mps13-clk",
+ "samsung,s2mps14-clk", "samsung,s5m8767-clk"
- regulators: The regulators of s2mps11 that have to be instantiated should be
included in a sub-node named 'regulators'. Regulator nodes included in this
- LDOn
- valid values for n are:
- S2MPS11: 1 to 38
+ - S2MPS13: 1 to 40
- S2MPS14: 1 to 25
- S2MPU02: 1 to 28
- Example: LDO1, LDO2, LDO28
- BUCKn
- valid values for n are:
- S2MPS11: 1 to 10
+ - S2MPS13: 1 to 10
- S2MPS14: 1 to 5
- S2MPU02: 1 to 7
- Example: BUCK1, BUCK2, BUCK9
specific extensions.
- "samsung,exynos5420-dw-mshc": for controllers with Samsung Exynos5420
specific extensions.
+ - "samsung,exynos7-dw-mshc": for controllers with Samsung Exynos7
+ specific extensions.
+ - "samsung,exynos7-dw-mshc-smu": for controllers with Samsung Exynos7
+ specific extensions having an SMU.
* samsung,dw-mshc-ciu-div: Specifies the divider value for the card interface
unit (ciu) clock. This property is applicable only for Exynos5 SoC's and
--- /dev/null
+* Imagination specific extensions to the Synopsys Designware Mobile Storage
+ Host Controller
+
+The Synopsys designware mobile storage host controller is used to interface
+a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
+differences between the core Synopsys dw mshc controller properties described
+by synopsys-dw-mshc.txt and the properties used by the Imagination specific
+extensions to the Synopsys Designware Mobile Storage Host Controller.
+
+Required Properties:
+
+* compatible: should be
+ - "img,pistachio-dw-mshc": for Pistachio SoCs
+
+Example:
+
+ mmc@18142000 {
+ compatible = "img,pistachio-dw-mshc";
+ reg = <0x18142000 0x400>;
+ interrupts = <GIC_SHARED 39 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&system_clk>, <&sdhost_clk>;
+ clock-names = "biu", "ciu";
+
+ fifo-depth = <0x20>;
+ bus-width = <4>;
+ num-slots = <1>;
+ disable-wp;
+ };
* for "marvell,armada-380-sdhci", two register areas. The first one
for the SDHCI registers themselves, and the second one for the
AXI/Mbus bridge registers of the SDHCI unit.
+- clocks: Array of clocks required for SDHCI; requires at least one for
+ I/O clock.
+- clock-names: Array of names corresponding to clocks property; shall be
+ "io" for I/O clock and "core" for optional core clock.
Optional properties:
- mrvl,clk-delay-cycles: Specify a number of cycles to delay for tuning.
reg = <0xd4280800 0x800>;
bus-width = <8>;
interrupts = <27>;
+ clocks = <&chip CLKID_SDIO1XIN>, <&chip CLKID_SDIO1>;
+ clock-names = "io", "core";
non-removable;
mrvl,clk-delay-cycles = <31>;
};
reg = <0xd8000 0x1000>, <0xdc000 0x100>;
interrupts = <0 25 0x4>;
clocks = <&gateclk 17>;
+ clock-names = "io";
mrvl,clk-delay-cycles = <0x1F>;
};
--- /dev/null
+* Nios II Processor Binding
+
+This binding specifies what properties available in the device tree
+representation of a Nios II Processor Core.
+
+Users can use sopc2dts tool for generating device tree sources (dts) from a
+Qsys system. See more detail in: http://www.alterawiki.com/wiki/Sopc2dts
+
+Required properties:
+
+- compatible: Compatible property value should be "altr,nios2-1.0".
+- reg: Contains CPU index.
+- interrupt-controller: Specifies that the node is an interrupt controller
+- #interrupt-cells: Specifies the number of cells needed to encode an
+ interrupt source, should be 1.
+- clock-frequency: Contains the clock frequency for CPU, in Hz.
+- dcache-line-size: Contains data cache line size.
+- icache-line-size: Contains instruction line size.
+- dcache-size: Contains data cache size.
+- icache-size: Contains instruction cache size.
+- altr,pid-num-bits: Specifies the number of bits to use to represent the process
+ identifier (PID).
+- altr,tlb-num-ways: Specifies the number of set-associativity ways in the TLB.
+- altr,tlb-num-entries: Specifies the number of entries in the TLB.
+- altr,tlb-ptr-sz: Specifies size of TLB pointer.
+- altr,has-mul: Specifies CPU hardware multipy support, should be 1.
+- altr,has-mmu: Specifies CPU support MMU support, should be 1.
+- altr,has-initda: Specifies CPU support initda instruction, should be 1.
+- altr,reset-addr: Specifies CPU reset address
+- altr,fast-tlb-miss-addr: Specifies CPU fast TLB miss exception address
+- altr,exception-addr: Specifies CPU exception address
+
+Optional properties:
+- altr,has-div: Specifies CPU hardware divide support
+- altr,implementation: Nios II core implementation, this should be "fast";
+
+Example:
+
+cpu@0x0 {
+ device_type = "cpu";
+ compatible = "altr,nios2-1.0";
+ reg = <0>;
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ clock-frequency = <125000000>;
+ dcache-line-size = <32>;
+ icache-line-size = <32>;
+ dcache-size = <32768>;
+ icache-size = <32768>;
+ altr,implementation = "fast";
+ altr,pid-num-bits = <8>;
+ altr,tlb-num-ways = <16>;
+ altr,tlb-num-entries = <128>;
+ altr,tlb-ptr-sz = <7>;
+ altr,has-div = <1>;
+ altr,has-mul = <1>;
+ altr,reset-addr = <0xc2800000>;
+ altr,fast-tlb-miss-addr = <0xc7fff400>;
+ altr,exception-addr = <0xd0000020>;
+ altr,has-initda = <1>;
+ altr,has-mmu = <1>;
+};
--- /dev/null
+Altera Timer
+
+Required properties:
+
+- compatible : should be "altr,timer-1.0"
+- reg : Specifies base physical address and size of the registers.
+- interrupt-parent: phandle of the interrupt controller
+- interrupts : Should contain the timer interrupt number
+- clock-frequency : The frequency of the clock that drives the counter, in Hz.
+
+Example:
+
+timer {
+ compatible = "altr,timer-1.0";
+ reg = <0x00400000 0x00000020>;
+ interrupt-parent = <&cpu>;
+ interrupts = <11>;
+ clock-frequency = <125000000>;
+};
Open Firmware Recommended Practice: Interrupt Mapping
http://www.openfirmware.org/1275/practice/imap/imap0_9d.pdf
+
+Additionally to the properties specified in the above standards a host bridge
+driver implementation may support the following properties:
+
+- linux,pci-domain:
+ If present this property assigns a fixed PCI domain number to a host bridge,
+ otherwise an unstable (across boots) unique number will be assigned.
+ It is required to either not set this property at all or set it for all
+ host bridges in the system, otherwise potentially conflicting domain numbers
+ may be assigned to root buses behind different host bridges. The domain
+ number for each host bridge in the system must be unique.
common pinctrl bindings used by client devices, including the meaning of the
phrase "pin configuration node".
-TZ1090-PDC's pin configuration nodes act as a container for an abitrary number
+TZ1090-PDC's pin configuration nodes act as a container for an arbitrary number
of subnodes. Each of these subnodes represents some desired configuration for a
pin, a group, or a list of pins or groups. This configuration can include the
mux function to select on those pin(s)/group(s), and various pin configuration
common pinctrl bindings used by client devices, including the meaning of the
phrase "pin configuration node".
-TZ1090's pin configuration nodes act as a container for an abitrary number of
+TZ1090's pin configuration nodes act as a container for an arbitrary number of
subnodes. Each of these subnodes represents some desired configuration for a
pin, a group, or a list of pins or groups. This configuration can include the
mux function to select on those pin(s)/group(s), and various pin configuration
common pinctrl bindings used by client devices, including the meaning of the
phrase "pin configuration node".
-Lantiq's pin configuration nodes act as a container for an abitrary number of
+Lantiq's pin configuration nodes act as a container for an arbitrary number of
subnodes. Each of these subnodes represents some desired configuration for a
pin, a group, or a list of pins or groups. This configuration can include the
mux function to select on those group(s), and two pin configuration parameters:
common pinctrl bindings used by client devices, including the meaning of the
phrase "pin configuration node".
-Lantiq's pin configuration nodes act as a container for an abitrary number of
+Lantiq's pin configuration nodes act as a container for an arbitrary number of
subnodes. Each of these subnodes represents some desired configuration for a
pin, a group, or a list of pins or groups. This configuration can include the
mux function to select on those group(s), and two pin configuration parameters:
common pinctrl bindings used by client devices, including the meaning of the
phrase "pin configuration node".
-Tegra's pin configuration nodes act as a container for an abitrary number of
+Tegra's pin configuration nodes act as a container for an arbitrary number of
subnodes. Each of these subnodes represents some desired configuration for a
pin, a group, or a list of pins or groups. This configuration can include the
mux function to select on those pin(s)/group(s), and various pin configuration
Please refer to pinctrl-bindings.txt in this directory for details of the common
pinctrl bindings used by client devices.
-SiRFprimaII's pinmux nodes act as a container for an abitrary number of subnodes.
+SiRFprimaII's pinmux nodes act as a container for an arbitrary number of subnodes.
Each of these subnodes represents some desired configuration for a group of pins.
Required subnode-properties:
Please refer to pinctrl-bindings.txt in this directory for details of the common
pinctrl bindings used by client devices.
-SPEAr's pinmux nodes act as a container for an abitrary number of subnodes. Each
+SPEAr's pinmux nodes act as a container for an arbitrary number of subnodes. Each
of these subnodes represents muxing for a pin, a group, or a list of pins or
groups.
common pinctrl bindings used by client devices, including the meaning of the
phrase "pin configuration node".
-Qualcomm's pin configuration nodes act as a container for an abitrary number of
+Qualcomm's pin configuration nodes act as a container for an arbitrary number of
subnodes. Each of these subnodes represents some desired configuration for a
pin, a group, or a list of pins or groups. This configuration can include the
mux function to select on those pin(s)/group(s), and various pin configuration
common pinctrl bindings used by client devices, including the meaning of the
phrase "pin configuration node".
-The pin configuration nodes act as a container for an abitrary number of
+The pin configuration nodes act as a container for an arbitrary number of
subnodes. Each of these subnodes represents some desired configuration for a
pin, a group, or a list of pins or groups. This configuration can include the
mux function to select on those pin(s)/group(s), and various pin configuration
common pinctrl bindings used by client devices, including the meaning of the
phrase "pin configuration node".
-Qualcomm's pin configuration nodes act as a container for an abitrary number of
+Qualcomm's pin configuration nodes act as a container for an arbitrary number of
subnodes. Each of these subnodes represents some desired configuration for a
pin, a group, or a list of pins or groups. This configuration can include the
mux function to select on those pin(s)/group(s), and various pin configuration
common pinctrl bindings used by client devices, including the meaning of the
phrase "pin configuration node".
-The pin configuration nodes act as a container for an abitrary number of
+The pin configuration nodes act as a container for an arbitrary number of
subnodes. Each of these subnodes represents some desired configuration for a
pin, a group, or a list of pins or groups. This configuration can include the
mux function to select on those pin(s)/group(s), and various pin configuration
common pinctrl bindings used by client devices, including the meaning of the
phrase "pin configuration node".
-Qualcomm's pin configuration nodes act as a container for an abitrary number of
+Qualcomm's pin configuration nodes act as a container for an arbitrary number of
subnodes. Each of these subnodes represents some desired configuration for a
pin, a group, or a list of pins or groups. This configuration can include the
mux function to select on those pin(s)/group(s), and various pin configuration
--- /dev/null
+* Generic system power control capability
+
+Power-management integrated circuits or miscellaneous hardware components are
+sometimes able to control the system power. The device driver associated with these
+components might need to define this capability, which tells the kernel that
+it can be used to switch off the system. The corresponding device must have the
+standard property "system-power-controller" in its device node. This property
+marks the device as able to control the system power. In order to test if this
+property is found programmatically, use the helper function
+"of_device_is_system_power_controller" from of.h .
+
+Example:
+
+act8846: act8846@5 {
+ compatible = "active-semi,act8846";
+ status = "okay";
+ system-power-controller;
+}
- compatible: "active-semi,act8846" or "active-semi,act8865"
- reg: I2C slave address
+Optional properties:
+- system-power-controller: Telling whether or not this pmic is controlling
+ the system power. See Documentation/devicetree/bindings/power/power-controller.txt .
+
Any standard regulator properties can be used to configure the single regulator.
The valid names for regulators are:
example: LDO1, LDO2, LDO35.
-BUCKn : for BUCKs, where n can lie in range 1 to 10.
example: BUCK1, BUCK5, BUCK10.
+
+The max77802 regulator supports two different operating modes: Normal and Low
+Power Mode. Some regulators support the modes to be changed at startup or by
+the consumers during normal operation while others only support to change the
+mode during system suspend. The standard regulator suspend states binding can
+be used to configure the regulator operating mode.
+
+The regulators that support the standard "regulator-initial-mode" property,
+changing their mode during normal operation are: LDOs 1, 3, 20 and 21.
+
+The possible values for "regulator-initial-mode" and "regulator-mode" are:
+ 1: Normal regulator voltage output mode.
+ 3: Low Power which reduces the quiescent current down to only 1uA
+
+The list of valid modes are defined in the dt-bindings/clock/maxim,max77802.h
+header and can be included by device tree source files.
+
+The standard "regulator-mode" property can only be used for regulators that
+support changing their mode to Low Power Mode during suspend. These regulators
+are: BUCKs 2-4 and LDOs 1-35. Also, it only takes effect if the regulator has
+been enabled for the given suspend state using "regulator-on-in-suspend" and
+has not been disabled for that state using "regulator-off-in-suspend".
+
Example:
max77802@09 {
#size-cells = <0>;
regulators {
+ ldo1_reg: LDO1 {
+ regulator-name = "vdd_1v0";
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <1000000>;
+ regulator-always-on;
+ regulator-initial-mode = <MAX77802_OPMODE_LP>;
+ };
+
ldo11_reg: LDO11 {
regulator-name = "vdd_ldo11";
regulator-min-microvolt = <1900000>;
regulator-max-microvolt = <1900000>;
regulator-always-on;
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-mode = <MAX77802_OPMODE_LP>;
+ };
};
buck1_reg: BUCK1 {
design requires. This property describes the total system ramp time
required due to the combination of internal ramping of the regulator itself,
and board design issues such as trace capacitance and load on the supply.
+- regulator-state-mem sub-root node for Suspend-to-RAM mode
+ : suspend to memory, the device goes to sleep, but all data stored in memory,
+ only some external interrupt can wake the device.
+- regulator-state-disk sub-root node for Suspend-to-DISK mode
+ : suspend to disk, this state operates similarly to Suspend-to-RAM,
+ but includes a final step of writing memory contents to disk.
+- regulator-state-[mem/disk] node has following common properties:
+ - regulator-on-in-suspend: regulator should be on in suspend state.
+ - regulator-off-in-suspend: regulator should be off in suspend state.
+ - regulator-suspend-microvolt: regulator should be set to this voltage
+ in suspend.
+ - regulator-mode: operating mode in the given suspend state.
+ The set of possible operating modes depends on the capabilities of
+ every hardware so the valid modes are documented on each regulator
+ device tree binding document.
+- regulator-initial-mode: initial operating mode. The set of possible operating
+ modes depends on the capabilities of every hardware so each device binding
+ documentation explains which values the regulator supports.
Deprecated properties:
- regulator-compatible: If a regulator chip contains multiple
regulator-max-microvolt = <2500000>;
regulator-always-on;
vin-supply = <&vin>;
+
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ };
};
Regulator Consumers:
SKY81452 voltage regulator
Required properties:
+- regulator node named lout.
- any required generic properties defined in regulator.txt
Optional properties:
Example:
regulator {
- /* generic regulator properties */
- regulator-name = "touch_en";
- regulator-min-microvolt = <4500000>;
- regulator-max-microvolt = <8000000>;
+ lout {
+ regulator-name = "sky81452-lout";
+ regulator-min-microvolt = <4500000>;
+ regulator-max-microvolt = <8000000>;
+ };
};
- "renesas,thermal-r8a73a4" (R-Mobile AP6)
- "renesas,thermal-r8a7779" (R-Car H1)
- "renesas,thermal-r8a7790" (R-Car H2)
- - "renesas,thermal-r8a7791" (R-Car M2)
+ - "renesas,thermal-r8a7791" (R-Car M2-W)
+ - "renesas,thermal-r8a7792" (R-Car V2H)
+ - "renesas,thermal-r8a7793" (R-Car M2-N)
+ - "renesas,thermal-r8a7794" (R-Car E2)
- reg : Address range of the thermal registers.
The 1st reg will be recognized as common register
if it has "interrupts".
chrp Common Hardware Reference Platform
chunghwa Chunghwa Picture Tubes Ltd.
cirrus Cirrus Logic, Inc.
+cnm Chips&Media, Inc.
cortina Cortina Systems, Inc.
crystalfontz Crystalfontz America, Inc.
dallas Maxim Integrated Products (formerly Dallas Semiconductor)
mediatek MediaTek Inc.
micrel Micrel Inc.
microchip Microchip Technology Inc.
+micron Micron Technology Inc.
mitsubishi Mitsubishi Electric Corporation
mosaixtech Mosaix Technologies, Inc.
moxa Moxa
ricoh Ricoh Co. Ltd.
rockchip Fuzhou Rockchip Electronics Co., Ltd
samsung Samsung Semiconductor
+sandisk Sandisk Corporation
sbs Smart Battery System
schindler Schindler
seagate Seagate Technology PLC
sirf SiRF Technology, Inc.
sitronix Sitronix Technology Corporation
smsc Standard Microsystems Corporation
-snps Synopsys, Inc.
+snps Synopsys, Inc.
solidrun SolidRun
sony Sony Corporation
spansion Spansion Inc.
At mount time, the two directories given as mount options "lowerdir" and
"upperdir" are combined into a merged directory:
- mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper,\
+ mount -t overlay overlay -olowerdir=/lower,upperdir=/upper,\
workdir=/work /merged
The "workdir" needs to be an empty directory on the same filesystem
http://www.ti.com/product/tmp75
http://www.ti.com/product/tmp175
http://www.ti.com/product/tmp275
+ * NXP LM75B
+ Prefix: 'lm75b'
+ Addresses scanned: none
+ Datasheet: Publicly available at the NXP website
+ http://www.nxp.com/documents/data_sheet/LM75B.pdf
Author: Frodo Looijaard <frodol@dds.nl>
=====================
Supported chips:
+ * National Semiconductor / Texas Instruments LM95233
+ Addresses scanned: I2C 0x18, 0x2a, 0x2b
+ Datasheet: Publicly available at the Texas Instruments website
+ http://www.ti.com/product/lm95233
* National Semiconductor / Texas Instruments LM95234
Addresses scanned: I2C 0x18, 0x4d, 0x4e
Datasheet: Publicly available at the Texas Instruments website
Description
-----------
-LM95234 is an 11-bit digital temperature sensor with a 2-wire System Management
-Bus (SMBus) interface and TrueTherm technology that can very accurately monitor
-the temperature of four remote diodes as well as its own temperature.
-The four remote diodes can be external devices such as microprocessors,
-graphics processors or diode-connected 2N3904s. The LM95234's TruTherm
+LM95233 and LM95234 are 11-bit digital temperature sensors with a 2-wire
+System Management Bus (SMBus) interface and TrueTherm technology
+that can very accurately monitor the temperature of two (LM95233)
+or four (LM95234) remote diodes as well as its own temperature.
+The remote diodes can be external devices such as microprocessors,
+graphics processors or diode-connected 2N3904s. The chip's TruTherm
beta compensation technology allows sensing of 90 nm or 65 nm process
thermal diodes accurately.
==================
Supported chips:
- * National Semiconductor LM95245
+ * TI LM95235
+ Addresses scanned: I2C 0x18, 0x29, 0x4c
+ Datasheet: Publicly available at the TI website
+ http://www.ti.com/lit/ds/symlink/lm95235.pdf
+ * TI / National Semiconductor LM95245
Addresses scanned: I2C 0x18, 0x19, 0x29, 0x4c, 0x4d
- Datasheet: Publicly available at the National Semiconductor website
- http://www.national.com/mpf/LM/LM95245.html
+ Datasheet: Publicly available at the TI website
+ http://www.ti.com/lit/ds/symlink/lm95245.pdf
Author: Alexander Stein <alexander.stein@systec-electronic.com>
Description
-----------
-The LM95245 is an 11-bit digital temperature sensor with a 2-wire System
+LM95235 and LM95245 are 11-bit digital temperature sensors with a 2-wire System
Management Bus (SMBus) interface and TruTherm technology that can monitor
the temperature of a remote diode as well as its own temperature.
-The LM95245 can be used to very accurately monitor the temperature of
+The chips can be used to very accurately monitor the temperature of
external devices such as microprocessors.
All temperature values are given in millidegrees Celsius. Local temperature
=====================
Supported chips:
+ * Nuvoton NCT6102D/NCT6104D/NCT6106D
+ Prefix: 'nct6106'
+ Addresses scanned: ISA address retrieved from Super I/O registers
+ Datasheet: Available from the Nuvoton web site
* Nuvoton NCT5572D/NCT6771F/NCT6772F/NCT6775F/W83677HG-I
Prefix: 'nct6775'
Addresses scanned: ISA address retrieved from Super I/O registers
Datasheet: Available from Nuvoton upon request
- * Nuvoton NCT5577D/NCT6776D/NCT6776F
+ * Nuvoton NCT5573D/NCT5577D/NCT6776D/NCT6776F
Prefix: 'nct6776'
Addresses scanned: ISA address retrieved from Super I/O registers
Datasheet: Available from Nuvoton upon request
Prefix: 'nct6779'
Addresses scanned: ISA address retrieved from Super I/O registers
Datasheet: Available from Nuvoton upon request
+ * Nuvoton NCT6791D
+ Prefix: 'nct6791'
+ Addresses scanned: ISA address retrieved from Super I/O registers
+ Datasheet: Available from Nuvoton upon request
+ * Nuvoton NCT6792D
+ Prefix: 'nct6792'
+ Addresses scanned: ISA address retrieved from Super I/O registers
+ Datasheet: Available from Nuvoton upon request
Authors:
Guenter Roeck <linux@roeck-us.net>
--- /dev/null
+Kernel driver nct7802
+=====================
+
+Supported chips:
+ * Nuvoton NCT7802Y
+ Prefix: 'nct7802'
+ Addresses scanned: I2C 0x28..0x2f
+ Datasheet: Available from Nuvoton web site
+
+Authors:
+ Guenter Roeck <linux@roeck-us.net>
+
+Description
+-----------
+
+This driver implements support for the Nuvoton NCT7802Y hardware monitoring
+chip. NCT7802Y supports 6 temperature sensors, 5 voltage sensors, and 3 fan
+speed sensors.
+
+The chip also supports intelligent fan speed control. This functionality is
+not currently supported by the driver.
+
+Tested Boards and BIOS Versions
+-------------------------------
+
+The driver has been reported to work with the following boards and
+BIOS versions.
+
+Board BIOS version
+---------------------------------------------------------------
+Kontron COMe-bSC2 CHR2E934.001.GGO
+Kontron COMe-bIP2 CCR2E212
Prefix: 'tmp432'
Addresses scanned: I2C 0x4c, 0x4d
Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp432.html
+ * Texas Instruments TMP435
+ Prefix: 'tmp435'
+ Addresses scanned: I2C 0x37, 0x48 - 0x4f
+ Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp435.html
Authors:
Hans de Goede <hdegoede@redhat.com>
-----------
This driver implements support for Texas Instruments TMP401, TMP411,
-TMP431, and TMP432 chips. These chips implement one or two remote and
-one local temperature sensors. Temperature is measured in degrees
+TMP431, TMP432 and TMP435 chips. These chips implement one or two remote
+and one local temperature sensors. Temperature is measured in degrees
Celsius. Resolution of the remote sensor is 0.0625 degree. Local
sensor resolution can be set to 0.5, 0.25, 0.125 or 0.0625 degree (not
supported by the driver so far, so using the default resolution of 0.5
7.2.1 Status packet
7.2.2 Head packet
7.2.3 Motion packet
+ 8. Trackpoint (for Hardware version 3 and 4)
+ 8.1 Registers
+ 8.2 Native relative mode 6 byte packet format
+ 8.2.1 Status Packet
1. Introduction
~~~~~~~~~~~~
-Currently the Linux Elantech touchpad driver is aware of two different
-hardware versions unimaginatively called version 1 and version 2. Version 1
-is found in "older" laptops and uses 4 bytes per packet. Version 2 seems to
-be introduced with the EeePC and uses 6 bytes per packet, and provides
-additional features such as position of two fingers, and width of the touch.
+Currently the Linux Elantech touchpad driver is aware of four different
+hardware versions unimaginatively called version 1,version 2, version 3
+and version 4. Version 1 is found in "older" laptops and uses 4 bytes per
+packet. Version 2 seems to be introduced with the EeePC and uses 6 bytes
+per packet, and provides additional features such as position of two fingers,
+and width of the touch. Hardware version 3 uses 6 bytes per packet (and
+for 2 fingers the concatenation of two 6 bytes packets) and allows tracking
+of up to 3 fingers. Hardware version 4 uses 6 bytes per packet, and can
+combine a status packet with multiple head or motion packets. Hardware version
+4 allows tracking up to 5 fingers.
+
+Some Hardware version 3 and version 4 also have a trackpoint which uses a
+separate packet format. It is also 6 bytes per packet.
The driver tries to support both hardware versions and should be compatible
with the Xorg Synaptics touchpad driver and its graphical configuration
utilities.
+Note that a mouse button is also associated with either the touchpad or the
+trackpoint when a trackpoint is available. Disabling the Touchpad in xorg
+(TouchPadOff=0) will also disable the buttons associated with the touchpad.
+
Additionally the operation of the touchpad can be altered by adjusting the
contents of some of its internal registers. These registers are represented
by the driver as sysfs entries under /sys/bus/serio/drivers/psmouse/serio?
2. Extra knobs
~~~~~~~~~~~
-Currently the Linux Elantech touchpad driver provides two extra knobs under
+Currently the Linux Elantech touchpad driver provides three extra knobs under
/sys/bus/serio/drivers/psmouse/serio? for the user.
* debug
data consistency checking can be done. For now checking is disabled by
default. Currently even turning it on will do nothing.
+* crc_enabled
+
+ Sets crc_enabled to 0/1. The name "crc_enabled" is the official name of
+ this integrity check, even though it is not an actual cyclic redundancy
+ check.
+
+ Depending on the state of crc_enabled, certain basic data integrity
+ verification is done by the driver on hardware version 3 and 4. The
+ driver will reject any packet that appears corrupted. Using this knob,
+ The state of crc_enabled can be altered with this knob.
+
+ Reading the crc_enabled value will show the active value. Echoing
+ "0" or "1" to this file will set the state to "0" or "1".
+
/////////////////////////////////////////////////////////////////////////////
3. Differentiating hardware versions
byte 0 ~ 2 for one finger
byte 3 ~ 5 for another
+
+
+8. Trackpoint (for Hardware version 3 and 4)
+ =========================================
+8.1 Registers
+ ~~~~~~~~~
+No special registers have been identified.
+
+8.2 Native relative mode 6 byte packet format
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+8.2.1 Status Packet
+ ~~~~~~~~~~~~~
+
+byte 0:
+ bit 7 6 5 4 3 2 1 0
+ 0 0 sx sy 0 M R L
+byte 1:
+ bit 7 6 5 4 3 2 1 0
+ ~sx 0 0 0 0 0 0 0
+byte 2:
+ bit 7 6 5 4 3 2 1 0
+ ~sy 0 0 0 0 0 0 0
+byte 3:
+ bit 7 6 5 4 3 2 1 0
+ 0 0 ~sy ~sx 0 1 1 0
+byte 4:
+ bit 7 6 5 4 3 2 1 0
+ x7 x6 x5 x4 x3 x2 x1 x0
+byte 5:
+ bit 7 6 5 4 3 2 1 0
+ y7 y6 y5 y4 y3 y2 y1 y0
+
+
+ x and y are written in two's complement spread
+ over 9 bits with sx/sy the relative top bit and
+ x7..x0 and y7..y0 the lower bits.
+ ~sx is the inverse of sx, ~sy is the inverse of sy.
+ The sign of y is opposite to what the input driver
+ expects for a relative movement
usb-storage.delay_use=
[UMS] The delay in seconds before a new device is
- scanned for Logical Units (default 5).
+ scanned for Logical Units (default 1).
usb-storage.quirks=
[UMS] A list of quirks entries to supplement or
0 - disabled
1 - enabled
+fwmark_reflect - BOOLEAN
+ Controls the fwmark of kernel-generated IPv4 reply packets that are not
+ associated with a socket for example, TCP RSTs or ICMP echo replies).
+ If unset, these packets have a fwmark of zero. If set, they have the
+ fwmark of the packet they are replying to.
+ Default: 0
+
route/max_size - INTEGER
Maximum number of routes allowed in the kernel. Increase
this when using large numbers of interfaces and/or routes.
proxy_ndp - BOOLEAN
Do proxy ndp.
+fwmark_reflect - BOOLEAN
+ Controls the fwmark of kernel-generated IPv6 reply packets that are not
+ associated with a socket for example, TCP RSTs or ICMPv6 echo replies).
+ If unset, these packets have a fwmark of zero. If set, they have the
+ fwmark of the packet they are replying to.
+ Default: 0
+
conf/interface/*:
Change special settings per interface.
This option is implemented only for transmit timestamps. There, the
timestamp is always looped along with a struct sock_extended_err.
- The option modifies field ee_info to pass an id that is unique
+ The option modifies field ee_data to pass an id that is unique
among all possibly concurrently outstanding timestamp requests for
that socket. In practice, it is a monotonically increasing u32
(that wraps).
--- /dev/null
+Linux on the Nios II architecture
+=================================
+
+This is a port of Linux to Nios II (nios2) processor.
+
+In order to compile for Nios II, you need a version of GCC with support for the generic
+system call ABI. Please see this link for more information on how compiling and booting
+software for the Nios II platform:
+http://www.rocketboards.org/foswiki/Documentation/NiosIILinuxUserManual
+
+For reference, please see the following link:
+http://www.altera.com/literature/lit-nio2.jsp
+
+What is Nios II?
+================
+Nios II is a 32-bit embedded-processor architecture designed specifically for the
+Altera family of FPGAs. In order to support Linux, Nios II needs to be configured
+with MMU and hardware multiplier enabled.
+
+Nios II ABI
+===========
+Please refer to chapter "Application Binary Interface" in Nios II Processor Reference
+Handbook.
my_ha->sas_ha.lldd_dev_found = my_dev_found;
my_ha->sas_ha.lldd_dev_gone = my_dev_gone;
- my_ha->sas_ha.lldd_max_execute_num = lldd_max_execute_num; (1)
-
- my_ha->sas_ha.lldd_queue_size = ha_can_queue;
my_ha->sas_ha.lldd_execute_task = my_execute_task;
my_ha->sas_ha.lldd_abort_task = my_abort_task;
return sas_register_ha(&my_ha->sas_ha);
}
-(1) This is normally a LLDD parameter, something of the
-lines of a task collector. What it tells the SAS Layer is
-whether the SAS layer should run in Direct Mode (default:
-value 0 or 1) or Task Collector Mode (value greater than 1).
-
-In Direct Mode, the SAS Layer calls Execute Task as soon as
-it has a command to send to the SDS, _and_ this is a single
-command, i.e. not linked.
-
-Some hardware (e.g. aic94xx) has the capability to DMA more
-than one task at a time (interrupt) from host memory. Task
-Collector Mode is an optional feature for HAs which support
-this in their hardware. (Again, it is completely optional
-even if your hardware supports it.)
-
-In Task Collector Mode, the SAS Layer would do _natural_
-coalescing of tasks and at the appropriate moment it would
-call your driver to DMA more than one task in a single HA
-interrupt. DMBS may want to use this by insmod/modprobe
-setting the lldd_max_execute_num to something greater than
-1.
-
(2) SAS 1.1 does not define I_T Nexus Reset TMF.
Events
The Execute Command SCSI RPC:
- int (*lldd_execute_task)(struct sas_task *, int num,
- unsigned long gfp_flags);
+ int (*lldd_execute_task)(struct sas_task *, gfp_t gfp_flags);
-Used to queue a task to the SAS LLDD. @task is the tasks to
-be executed. @num should be the number of tasks being
-queued at this function call (they are linked listed via
-task::list), @gfp_mask should be the gfp_mask defining the
-context of the caller.
+Used to queue a task to the SAS LLDD. @task is the task to be executed.
+@gfp_mask is the gfp_mask defining the context of the caller.
This function should implement the Execute Command SCSI RPC,
-or if you're sending a SCSI Task as linked commands, you
-should also use this function.
-That is, when lldd_execute_task() is called, the command(s)
+That is, when lldd_execute_task() is called, the command
go out on the transport *immediately*. There is *no*
queuing of any sort and at any level in a SAS LLDD.
-The use of task::list is two-fold, one for linked commands,
-the other discussed below.
-
-It is possible to queue up more than one task at a time, by
-initializing the list element of struct sas_task, and
-passing the number of tasks enlisted in this manner in num.
-
Returns: -SAS_QUEUE_FULL, -ENOMEM, nothing was queued;
0, the task(s) were queued.
-If you want to pass num > 1, then either
-A) you're the only caller of this function and keep track
- of what you've queued to the LLDD, or
-B) you know what you're doing and have a strategy of
- retrying.
-
-As opposed to queuing one task at a time (function call),
-batch queuing of tasks, by having num > 1, greatly
-simplifies LLDD code, sequencer code, and _hardware design_,
-and has some performance advantages in certain situations
-(DBMS).
-
-The LLDD advertises if it can take more than one command at
-a time at lldd_execute_task(), by setting the
-lldd_max_execute_num parameter (controlled by "collector"
-module parameter in aic94xx SAS LLDD).
-
-You should leave this to the default 1, unless you know what
-you're doing.
-
-This is a function of the LLDD, to which the SAS layer can
-cater to.
-
-int lldd_queue_size
- The host adapter's queue size. This is the maximum
-number of commands the lldd can have pending to domain
-devices on behalf of all upper layers submitting through
-lldd_execute_task().
-
-You really want to set this to something (much) larger than
-1.
-
-This _really_ has absolutely nothing to do with queuing.
-There is no queuing in SAS LLDDs.
-
struct sas_task {
dev -- the device this task is destined to
- list -- must be initialized (INIT_LIST_HEAD)
task_proto -- _one_ of enum sas_proto
scatter -- pointer to scatter gather list array
num_scatter -- number of elements in scatter
scsi_scan_host() -------+
|
slave_alloc()
- slave_configure() --> scsi_adjust_queue_depth()
+ slave_configure() --> scsi_change_queue_depth()
|
slave_alloc()
slave_configure()
------------------------------------------------------------
If the LLD wants to adjust the default queue settings, it can invoke
-scsi_adjust_queue_depth() in its slave_configure() routine.
+scsi_change_queue_depth() in its slave_configure() routine.
*** For scsi devices that the mid level tries to scan but do not
respond, a slave_alloc(), slave_destroy() pair is called.
scsi_add_device() ------+
|
slave_alloc()
- slave_configure() [--> scsi_adjust_queue_depth()]
+ slave_configure() [--> scsi_change_queue_depth()]
------------------------------------------------------------
In a similar fashion, an LLD may become aware that a SCSI device has been
| scsi_register()
|
slave_alloc()
- slave_configure() --> scsi_adjust_queue_depth()
+ slave_configure() --> scsi_change_queue_depth()
slave_alloc() ***
slave_destroy() ***
|
slave_destroy() ***
------------------------------------------------------------
-The mid level invokes scsi_adjust_queue_depth() with tagged queuing off and
-"cmd_per_lun" for that host as the queue length. These settings can be
-overridden by a slave_configure() supplied by the LLD.
+The mid level invokes scsi_change_queue_depth() with "cmd_per_lun" for that
+host as the queue length. These settings can be overridden by a
+slave_configure() supplied by the LLD.
*** For scsi devices that the mid level tries to scan but do not
respond, a slave_alloc(), slave_destroy() pair is called.
names all start with "scsi_".
Summary:
- scsi_activate_tcq - turn on tag command queueing
scsi_add_device - creates new scsi device (lu) instance
scsi_add_host - perform sysfs registration and set up transport class
- scsi_adjust_queue_depth - change the queue depth on a SCSI device
+ scsi_change_queue_depth - change the queue depth on a SCSI device
scsi_bios_ptable - return copy of block device's partition table
scsi_block_requests - prevent further commands being queued to given host
- scsi_deactivate_tcq - turn off tag command queueing
scsi_host_alloc - return a new scsi_host instance whose refcount==1
scsi_host_get - increments Scsi_Host instance's refcount
scsi_host_put - decrements Scsi_Host instance's refcount (free if 0)
Details:
-/**
- * scsi_activate_tcq - turn on tag command queueing ("ordered" task attribute)
- * @sdev: device to turn on TCQ for
- * @depth: queue depth
- *
- * Returns nothing
- *
- * Might block: no
- *
- * Notes: Eventually, it is hoped depth would be the maximum depth
- * the device could cope with and the real queue depth
- * would be adjustable from 0 to depth.
- *
- * Defined (inline) in: include/scsi/scsi_tcq.h
- **/
-void scsi_activate_tcq(struct scsi_device *sdev, int depth)
-
-
/**
* scsi_add_device - creates new scsi device (lu) instance
* @shost: pointer to scsi host instance
/**
- * scsi_adjust_queue_depth - allow LLD to change queue depth on a SCSI device
+ * scsi_change_queue_depth - allow LLD to change queue depth on a SCSI device
* @sdev: pointer to SCSI device to change queue depth on
- * @tagged: 0 - no tagged queuing
- * MSG_SIMPLE_TAG - simple tagged queuing
- * MSG_ORDERED_TAG - ordered tagged queuing
* @tags Number of tags allowed if tagged queuing enabled,
* or number of commands the LLD can queue up
* in non-tagged mode (as per cmd_per_lun).
*
* Notes: Can be invoked any time on a SCSI device controlled by this
* LLD. [Specifically during and after slave_configure() and prior to
- * slave_destroy().] Can safely be invoked from interrupt code. Actual
- * queue depth change may be delayed until the next command is being
- * processed. See also scsi_activate_tcq() and scsi_deactivate_tcq().
+ * slave_destroy().] Can safely be invoked from interrupt code.
*
* Defined in: drivers/scsi/scsi.c [see source code for more notes]
*
**/
-void scsi_adjust_queue_depth(struct scsi_device * sdev, int tagged,
- int tags)
+int scsi_change_queue_depth(struct scsi_device *sdev, int tags)
/**
void scsi_block_requests(struct Scsi_Host * shost)
-/**
- * scsi_deactivate_tcq - turn off tag command queueing
- * @sdev: device to turn off TCQ for
- * @depth: queue depth (stored in sdev)
- *
- * Returns nothing
- *
- * Might block: no
- *
- * Defined (inline) in: include/scsi/scsi_tcq.h
- **/
-void scsi_deactivate_tcq(struct scsi_device *sdev, int depth)
-
-
/**
* scsi_host_alloc - create a scsi host adapter instance and perform basic
* initialization.
for disk firmware uploads.
cmd_per_lun - maximum number of commands that can be queued on devices
controlled by the host. Overridden by LLD calls to
- scsi_adjust_queue_depth().
+ scsi_change_queue_depth().
unchecked_isa_dma - 1=>only use bottom 16 MB of ram (ISA DMA addressing
restriction), 0=>can use full 32 bit (or better) DMA
address space
Instances of this structure convey SCSI commands to the LLD and responses
back to the mid level. The SCSI mid level will ensure that no more SCSI
commands become queued against the LLD than are indicated by
-scsi_adjust_queue_depth() (or struct Scsi_Host::cmd_per_lun). There will
+scsi_change_queue_depth() (or struct Scsi_Host::cmd_per_lun). There will
be at least one instance of struct scsi_cmnd available for each SCSI device.
Members of interest:
cmnd - array containing SCSI command
DEBUGGING HINTS
-To enable debugging messages, edit st.c and #define DEBUG 1. As seen
-above, debugging can be switched off with an ioctl if debugging is
-compiled into the driver. The debugging output is not voluminous.
+Debugging code is now compiled in by default but debugging is turned off
+with the kernel module parameter debug_flag defaulting to 0. Debugging
+can still be switched on and off with an ioctl. To enable debug at
+module load time add debug_flag=1 to the module load options, the
+debugging output is not voluminous.
If the tape seems to hang, I would be very interested to hear where
the driver is waiting. With the command 'ps -l' you can see the state
--- /dev/null
+Driver for Western Digital WD7193, WD7197 and WD7296 SCSI cards
+---------------------------------------------------------------
+
+The card requires firmware that can be cut out of the Windows NT driver that
+can be downloaded from WD at:
+http://support.wdc.com/product/download.asp?groupid=801&sid=27&lang=en
+
+There is no license anywhere in the file or on the page - so the firmware
+probably cannot be added to linux-firmware.
+
+This script downloads and extracts the firmware, creating wd719x-risc.bin and
+d719x-wcs.bin files. Put them in /lib/firmware/.
+
+#!/bin/sh
+wget http://support.wdc.com/download/archive/pciscsi.exe
+lha xi pciscsi.exe pci-scsi.exe
+lha xi pci-scsi.exe nt/wd7296a.sys
+rm pci-scsi.exe
+dd if=wd7296a.sys of=wd719x-risc.bin bs=1 skip=5760 count=14336
+dd if=wd7296a.sys of=wd719x-wcs.bin bs=1 skip=20096 count=514
+rm wd7296a.sys
key, not quality.
multiplanar: select whether each device instance supports multi-planar formats,
- and thus the V4L2 multi-planar API. By default the first device instance
- is single-planar, the second multi-planar, and it keeps alternating.
+ and thus the V4L2 multi-planar API. By default device instances are
+ single-planar.
This module option can override that for each instance. Values are:
- 0: use alternating single and multi-planar devices.
1: this is a single-planar instance.
2: this is a multi-planar instance.
0 otherwise.
The driver has to be configured to support the multiplanar formats. By default
-the first driver instance is single-planar, the second is multi-planar, and it
-keeps alternating. This can be changed by setting the multiplanar module option,
-see section 1 for more details on that option.
+the driver instances are single-planar. This can be changed by setting the
+multiplanar module option, see section 1 for more details on that option.
If the driver instance is using the multiplanar formats/API, then the first
single planar format (YUYV) and the multiplanar NV16M and NV61M formats the
to see the blended framebuffer overlay that's being written to by the second
instance. This setup would require the following commands:
- $ sudo modprobe vivid n_devs=2 node_types=0x10101,0x1 multiplanar=1,1
+ $ sudo modprobe vivid n_devs=2 node_types=0x10101,0x1
$ v4l2-ctl -d1 --find-fb
/dev/fb1 is the framebuffer associated with base address 0x12800000
$ sudo v4l2-ctl -d2 --set-fbuf fb=1
F: drivers/*/*/*rockchip*
F: sound/soc/rockchip/
-ARM/SAMSUNG ARM ARCHITECTURES
-M: Ben Dooks <ben-linux@fluff.org>
-M: Kukjin Kim <kgene.kim@samsung.com>
+ARM/SAMSUNG EXYNOS ARM ARCHITECTURES
+M: Kukjin Kim <kgene@kernel.org>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
L: linux-samsung-soc@vger.kernel.org (moderated for non-subscribers)
-W: http://www.fluff.org/ben/linux/
S: Maintained
F: arch/arm/boot/dts/s3c*
F: arch/arm/boot/dts/exynos*
F: arch/arm/plat-samsung/
F: arch/arm/mach-s3c24*/
F: arch/arm/mach-s3c64xx/
+F: arch/arm/mach-s5p*/
+F: arch/arm/mach-exynos*/
F: drivers/*/*s3c2410*
F: drivers/*/*/*s3c2410*
F: drivers/spi/spi-s3c*
F: sound/soc/samsung/*
-
-ARM/S5P EXYNOS ARM ARCHITECTURES
-M: Kukjin Kim <kgene.kim@samsung.com>
-L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
-L: linux-samsung-soc@vger.kernel.org (moderated for non-subscribers)
-S: Maintained
-F: arch/arm/mach-s5p*/
-F: arch/arm/mach-exynos*/
N: exynos
ARM/SAMSUNG MOBILE MACHINE SUPPORT
F: drivers/usb/dwc3/dwc3-st.c
F: drivers/usb/host/ehci-st.c
F: drivers/usb/host/ohci-st.c
+F: drivers/ata/ahci_st.c
ARM/TECHNOLOGIC SYSTEMS TS7250 MACHINE SUPPORT
M: Lennert Buytenhek <kernel@wantstofly.org>
F: drivers/clk/ux500/
F: include/linux/platform_data/clk-ux500.h
+ARM/VERSATILE EXPRESS PLATFORM
+M: Liviu Dudau <liviu.dudau@arm.com>
+M: Sudeep Holla <sudeep.holla@arm.com>
+M: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm/boot/dts/vexpress*
+F: arch/arm/mach-vexpress/
+F: */*/vexpress*
+F: */*/*/vexpress*
+F: drivers/clk/versatile/clk-vexpress-osc.c
+F: drivers/clocksource/versatile.c
+
ARM/VFP SUPPORT
M: Russell King <linux@arm.linux.org.uk>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
ARM/ZYNQ ARCHITECTURE
M: Michal Simek <michal.simek@xilinx.com>
+R: Sören Brinkmann <soren.brinkmann@xilinx.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
W: http://wiki.xilinx.com
T: git git://git.xilinx.com/linux-xlnx.git
F: net/ax25/
AZ6007 DVB DRIVER
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
BROADCOM BCM2835 ARM ARCHITECTURE
M: Stephen Warren <swarren@wwwdotorg.org>
+M: Lee Jones <lee@kernel.org>
L: linux-rpi-kernel@lists.infradead.org (moderated for non-subscribers)
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-rpi.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/rpi/linux-rpi.git
S: Maintained
N: bcm2835
BROADCOM BCM7XXX ARM ARCHITECTURE
M: Marc Carino <marc.ceeeee@gmail.com>
M: Brian Norris <computersforpeace@gmail.com>
+M: Gregory Fong <gregory.0xf0@gmail.com>
+M: Florian Fainelli <f.fainelli@gmail.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: arch/arm/mach-bcm/*brcmstb*
F: arch/arm/boot/dts/bcm7*.dts*
+F: drivers/bus/brcmstb_gisb.c
BROADCOM TG3 GIGABIT ETHERNET DRIVER
M: Prashant Sreedharan <prashant@broadcom.com>
S: Supported
F: drivers/scsi/bnx2i/
+BROADCOM CYGNUS/IPROC ARM ARCHITECTURE
+M: Ray Jui <rjui@broadcom.com>
+M: Scott Branden <sbranden@broadcom.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+L: bcm-kernel-feedback-list@broadcom.com
+T: git git://git.github.com/brcm/linux.git
+S: Maintained
+N: iproc
+N: cygnus
+N: bcm9113*
+N: bcm9583*
+N: bcm583*
+N: bcm113*
+
BROADCOM KONA GPIO DRIVER
M: Ray Jui <rjui@broadcom.com>
L: bcm-kernel-feedback-list@broadcom.com
F: fs/btrfs/
BTTV VIDEO4LINUX DRIVER
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
F: include/media/cx2341x*
CX88 VIDEO4LINUX DRIVER
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
S: Supported
F: drivers/net/ethernet/chelsio/cxgb3/
+CXGB3 ISCSI DRIVER (CXGB3I)
+M: Karen Xie <kxie@chelsio.com>
+L: linux-scsi@vger.kernel.org
+W: http://www.chelsio.com
+S: Supported
+F: drivers/scsi/cxgbi/cxgb3i
+
CXGB3 IWARP RNIC DRIVER (IW_CXGB3)
M: Steve Wise <swise@chelsio.com>
L: linux-rdma@vger.kernel.org
S: Supported
F: drivers/net/ethernet/chelsio/cxgb4/
+CXGB4 ISCSI DRIVER (CXGB4I)
+M: Karen Xie <kxie@chelsio.com>
+L: linux-scsi@vger.kernel.org
+W: http://www.chelsio.com
+S: Supported
+F: drivers/scsi/cxgbi/cxgb4i
+
CXGB4 IWARP RNIC DRIVER (IW_CXGB4)
M: Steve Wise <swise@chelsio.com>
L: linux-rdma@vger.kernel.org
F: drivers/net/ethernet/dec/tulip/dmfe.c
DC390/AM53C974 SCSI driver
-M: Kurt Garloff <garloff@suse.de>
-W: http://www.garloff.de/kurt/linux/dc390/
-M: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+M: Hannes Reinecke <hare@suse.de>
+L: linux-scsi@vger.kernel.org
S: Maintained
-F: drivers/scsi/tmscsim.*
+F: drivers/scsi/am53c974.c
DC395x SCSI driver
M: Oliver Neukum <oliver@neukum.org>
EDAC-CORE
M: Doug Thompson <dougthompson@xmission.com>
M: Borislav Petkov <bp@alien8.de>
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Supported
F: drivers/edac/e7xxx_edac.c
EDAC-GHES
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/i5000_edac.c
EDAC-I5400
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/i5400_edac.c
EDAC-I7300
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/i7300_edac.c
EDAC-I7CORE
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/r82600_edac.c
EDAC-SBRIDGE
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/net/ethernet/ibm/ehea/
EM28XX VIDEO4LINUX DRIVER
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
S: Maintained
F: drivers/iio/
F: drivers/staging/iio/
+F: include/linux/iio/
IKANOS/ADI EAGLE ADSL USB DRIVER
M: Matthieu Castet <castet.matthieu@free.fr>
F: drivers/media/radio/radio-maxiradio*
MEDIA INPUT INFRASTRUCTURE (V4L/DVB)
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
P: LinuxTV.org Project
L: linux-media@vger.kernel.org
W: http://linuxtv.org
S: Odd Fixes
F: drivers/media/parport/pms*
-MEGARAID SCSI DRIVERS
-M: Neela Syam Kolli <megaraidlinux@lsi.com>
+MEGARAID SCSI/SAS DRIVERS
+M: Kashyap Desai <kashyap.desai@avagotech.com>
+M: Sumit Saxena <sumit.saxena@avagotech.com>
+M: Uday Lingala <uday.lingala@avagotech.com>
+L: megaraidlinux.pdl@avagotech.com
L: linux-scsi@vger.kernel.org
-W: http://megaraid.lsilogic.com
+W: http://www.lsi.com
S: Maintained
F: Documentation/scsi/megaraid.txt
F: drivers/scsi/megaraid.*
F: drivers/scsi/g_NCR5380_mmio.c
F: drivers/scsi/mac_scsi.*
F: drivers/scsi/pas16.*
-F: drivers/scsi/sun3_NCR5380.c
F: drivers/scsi/sun3_scsi.*
F: drivers/scsi/sun3_scsi_vme.c
F: drivers/scsi/t128.*
F: Documentation/scsi/NinjaSCSI.txt
F: drivers/scsi/nsp32*
+NIOS2 ARCHITECTURE
+M: Ley Foon Tan <lftan@altera.com>
+L: nios2-dev@lists.rocketboards.org (moderated for non-subscribers)
+T: git git://git.rocketboards.org/linux-socfpga.git
+S: Maintained
+F: arch/nios2/
+
NTB DRIVER
M: Jon Mason <jdmason@kudzu.us>
M: Dave Jiang <dave.jiang@intel.com>
S: Maintained
F: arch/arm/*omap*/
F: drivers/i2c/busses/i2c-omap.c
+F: drivers/irqchip/irq-omap-intc.c
+F: drivers/mfd/*omap*.c
+F: drivers/mfd/menelaus.c
+F: drivers/mfd/palmas.c
+F: drivers/mfd/tps65217.c
+F: drivers/mfd/tps65218.c
+F: drivers/mfd/tps65910.c
+F: drivers/mfd/twl-core.[ch]
+F: drivers/mfd/twl4030*.c
+F: drivers/mfd/twl6030*.c
+F: drivers/mfd/twl6040*.c
+F: drivers/regulator/palmas-regulator*.c
+F: drivers/regulator/pbias-regulator.c
+F: drivers/regulator/tps65217-regulator.c
+F: drivers/regulator/tps65218-regulator.c
+F: drivers/regulator/tps65910-regulator.c
+F: drivers/regulator/twl-regulator.c
F: include/linux/i2c-omap.h
OMAP DEVICE TREE SUPPORT
S: Maintained
F: arch/arm/boot/dts/*omap*
F: arch/arm/boot/dts/*am3*
+F: arch/arm/boot/dts/*am4*
+F: arch/arm/boot/dts/*am5*
+F: arch/arm/boot/dts/*dra7*
OMAP CLOCK FRAMEWORK SUPPORT
M: Paul Walmsley <paul@pwsan.com>
F: include/scsi/osd_*
F: fs/exofs/
-OVERLAYFS FILESYSTEM
+OVERLAY FILESYSTEM
M: Miklos Szeredi <miklos@szeredi.hu>
-L: linux-fsdevel@vger.kernel.org
+L: linux-unionfs@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs.git
S: Supported
-F: fs/overlayfs/*
+F: fs/overlayfs/
F: Documentation/filesystems/overlayfs.txt
P54 WIRELESS DRIVER
PIN CONTROL SUBSYSTEM
M: Linus Walleij <linus.walleij@linaro.org>
+L: linux-gpio@vger.kernel.org
S: Maintained
F: drivers/pinctrl/
F: include/linux/pinctrl/
F: drivers/media/i2c/saa6588*
SAA7134 VIDEO4LINUX DRIVER
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
F: drivers/media/radio/si4713/radio-usb-si4713.c
SIANO DVB DRIVER
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
TI DAVINCI MACHINE SUPPORT
M: Sekhar Nori <nsekhar@ti.com>
M: Kevin Hilman <khilman@deeprootsystems.com>
-L: davinci-linux-open-source@linux.davincidsp.com (moderated for non-subscribers)
T: git git://gitorious.org/linux-davinci/linux-davinci.git
Q: http://patchwork.kernel.org/project/linux-davinci/list/
S: Supported
TI DAVINCI SERIES MEDIA DRIVER
M: Lad, Prabhakar <prabhakar.csengg@gmail.com>
L: linux-media@vger.kernel.org
-L: davinci-linux-open-source@linux.davincidsp.com (moderated for non-subscribers)
W: http://linuxtv.org/
Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/mhadli/v4l-dvb-davinci_devices.git
F: drivers/leds/leds-net48xx.c
SOFTLOGIC 6x10 MPEG CODEC
-M: Ismael Luceno <ismael.luceno@corp.bluecherry.net>
+M: Bluecherry Maintainers <maintainers@bluecherrydvr.com>
+M: Andrey Utkin <andrey.utkin@corp.bluecherry.net>
+M: Andrey Utkin <andrey.krieger.utkin@gmail.com>
L: linux-media@vger.kernel.org
S: Supported
F: drivers/media/pci/solo6x10/
F: drivers/media/i2c/tda9840*
TEA5761 TUNER DRIVER
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
F: drivers/media/tuners/tea5761.*
TEA5767 TUNER DRIVER
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
F: mm/shmem.c
TM6000 VIDEO4LINUX DRIVER
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
F: Documentation/hid/hiddev.txt
F: drivers/hid/usbhid/
-USB/IP DRIVERS
-L: linux-usb@vger.kernel.org
-S: Orphan
-F: drivers/staging/usbip/
-
USB ISP116X DRIVER
M: Olav Kongas <ok@artecdesign.ee>
L: linux-usb@vger.kernel.org
F: arch/x86/kernel/cpu/mcheck/*
XC2028/3028 TUNER DRIVER
-M: Mauro Carvalho Chehab <m.chehab@samsung.com>
+M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
VERSION = 3
PATCHLEVEL = 18
SUBLEVEL = 0
-EXTRAVERSION = -rc3
+EXTRAVERSION =
NAME = Diseased Newt
# *DOCUMENTATION*
HOSTCC = gcc
HOSTCXX = g++
-HOSTCFLAGS = -Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer
+HOSTCFLAGS = -Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu89
HOSTCXXFLAGS = -O2
ifeq ($(shell $(HOSTCC) -v 2>&1 | grep -c "clang version"), 1)
KBUILD_CFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
-fno-strict-aliasing -fno-common \
-Werror-implicit-function-declaration \
- -Wno-format-security
+ -Wno-format-security \
+ -std=gnu89
KBUILD_AFLAGS_KERNEL :=
KBUILD_CFLAGS_KERNEL :=
default 0xf1c28000 if DEBUG_SUNXI_UART0
default 0xf1c28400 if DEBUG_SUNXI_UART1
default 0xf1f02800 if DEBUG_SUNXI_R_UART
- default 0xf2100000 if DEBUG_PXA_UART1
+ default 0xf6200000 if DEBUG_PXA_UART1
default 0xf4090000 if ARCH_LPC32XX
default 0xf4200000 if ARCH_GEMINI
default 0xf7000000 if DEBUG_S3C24XX_UART && (DEBUG_S3C_UART0 || \
add sp, sp, r6
#endif
- tst r4, #1
- bleq cache_clean_flush
+ bl cache_clean_flush
adr r0, BSYM(restart)
add r0, r0, r6
b call_cache_fn
__armv4_mpu_cache_flush:
+ tst r4, #1
+ movne pc, lr
mov r2, #1
mov r3, #0
mcr p15, 0, ip, c7, c6, 0 @ invalidate D cache
mov pc, lr
__fa526_cache_flush:
+ tst r4, #1
+ movne pc, lr
mov r1, #0
mcr p15, 0, r1, c7, c14, 0 @ clean and invalidate D cache
mcr p15, 0, r1, c7, c5, 0 @ flush I cache
__armv6_mmu_cache_flush:
mov r1, #0
- mcr p15, 0, r1, c7, c14, 0 @ clean+invalidate D
+ tst r4, #1
+ mcreq p15, 0, r1, c7, c14, 0 @ clean+invalidate D
mcr p15, 0, r1, c7, c5, 0 @ invalidate I+BTB
- mcr p15, 0, r1, c7, c15, 0 @ clean+invalidate unified
+ mcreq p15, 0, r1, c7, c15, 0 @ clean+invalidate unified
mcr p15, 0, r1, c7, c10, 4 @ drain WB
mov pc, lr
__armv7_mmu_cache_flush:
+ tst r4, #1
+ bne iflush
mrc p15, 0, r10, c0, c1, 5 @ read ID_MMFR1
tst r10, #0xf << 16 @ hierarchical cache (ARMv7)
mov r10, #0
mov pc, lr
__armv5tej_mmu_cache_flush:
+ tst r4, #1
+ movne pc, lr
1: mrc p15, 0, r15, c7, c14, 3 @ test,clean,invalidate D cache
bne 1b
mcr p15, 0, r0, c7, c5, 0 @ flush I cache
mov pc, lr
__armv4_mmu_cache_flush:
+ tst r4, #1
+ movne pc, lr
mov r2, #64*1024 @ default: 32K dcache size (*2)
mov r11, #32 @ default: 32 byte line size
mrc p15, 0, r3, c0, c0, 1 @ read cache type
__armv3_mmu_cache_flush:
__armv3_mpu_cache_flush:
+ tst r4, #1
+ movne pc, lr
mov r1, #0
mcr p15, 0, r1, c7, c0, 0 @ invalidate whole cache v3
mov pc, lr
reg = <0x00060000 0x00020000>;
};
partition@4 {
- label = "NAND.u-boot-spl";
+ label = "NAND.u-boot-spl-os";
reg = <0x00080000 0x00040000>;
};
partition@5 {
dcdc3: regulator-dcdc3 {
compatible = "ti,tps65218-dcdc3";
regulator-name = "vdcdc3";
- regulator-min-microvolt = <1350000>;
- regulator-max-microvolt = <1350000>;
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <1500000>;
regulator-boot-on;
regulator-always-on;
};
dcdc3: regulator-dcdc3 {
compatible = "ti,tps65218-dcdc3";
regulator-name = "vdds_ddr";
- regulator-min-microvolt = <1350000>;
- regulator-max-microvolt = <1350000>;
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <1500000>;
regulator-boot-on;
regulator-always-on;
};
dcdc3: regulator-dcdc3 {
compatible = "ti,tps65218-dcdc3";
regulator-name = "vdcdc3";
- regulator-min-microvolt = <1350000>;
- regulator-max-microvolt = <1350000>;
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <1500000>;
regulator-boot-on;
regulator-always-on;
};
num-cs = <1>;
};
+&usbdrd_dwc3 {
+ dr_mode = "host";
+};
+
#include "cros-ec-keyboard.dtsi"
#size-cells = <1>;
ranges;
- dwc3 {
+ usbdrd_dwc3: dwc3 {
compatible = "synopsys,dwc3";
reg = <0x12000000 0x10000>;
interrupts = <0 72 0>;
* Common support for omap3 EVM boards
*/
+#include <dt-bindings/input/input.h>
#include "omap-gpmc-smsc911x.dtsi"
/ {
ti,use-leds;
};
+&twl_keypad {
+ linux,keymap = <
+ MATRIX_KEY(2, 2, KEY_1)
+ MATRIX_KEY(1, 1, KEY_2)
+ MATRIX_KEY(0, 0, KEY_3)
+ MATRIX_KEY(3, 2, KEY_4)
+ MATRIX_KEY(2, 1, KEY_5)
+ MATRIX_KEY(1, 0, KEY_6)
+ MATRIX_KEY(1, 3, KEY_7)
+ MATRIX_KEY(3, 1, KEY_8)
+ MATRIX_KEY(2, 0, KEY_9)
+ MATRIX_KEY(2, 3, KEY_KPASTERISK)
+ MATRIX_KEY(0, 2, KEY_0)
+ MATRIX_KEY(3, 0, KEY_KPDOT)
+ /* s4 not wired */
+ MATRIX_KEY(1, 2, KEY_BACKSPACE)
+ MATRIX_KEY(0, 1, KEY_ENTER)
+ >;
+};
+
&usb_otg_hs {
interface-type = <0>;
usb-phy = <&usb2_phy>;
*/
/dts-v1/;
+#include <dt-bindings/input/input.h>
#include "omap34xx.dtsi"
#include "omap-gpmc-smsc911x.dtsi"
};
partition@2000000 {
label = "Filesystem";
- reg = <0x2000000 0xe000000>;
+ reg = <0x2000000 0x6000000>;
};
};
};
};
+&twl_keypad {
+ linux,keymap = <MATRIX_KEY(0, 0, KEY_1)
+ MATRIX_KEY(0, 1, KEY_2)
+ MATRIX_KEY(0, 2, KEY_3)
+ MATRIX_KEY(1, 0, KEY_4)
+ MATRIX_KEY(1, 1, KEY_5)
+ MATRIX_KEY(1, 2, KEY_6)
+ MATRIX_KEY(1, 3, KEY_F5)
+ MATRIX_KEY(2, 0, KEY_7)
+ MATRIX_KEY(2, 1, KEY_8)
+ MATRIX_KEY(2, 2, KEY_9)
+ MATRIX_KEY(2, 3, KEY_F6)
+ MATRIX_KEY(3, 0, KEY_F7)
+ MATRIX_KEY(3, 1, KEY_0)
+ MATRIX_KEY(3, 2, KEY_F8)
+ MATRIX_KEY(5, 4, KEY_RESERVED)
+ MATRIX_KEY(4, 4, KEY_VOLUMEUP)
+ MATRIX_KEY(5, 5, KEY_VOLUMEDOWN)>;
+};
+
&uart3 {
interrupts-extended = <&intc 74 &omap3_pmx_core OMAP3_UART3_RX>;
};
* hierarchy.
*/
ocp {
- compatible = "simple-bus";
+ compatible = "ti,omap3-l3-smx", "simple-bus";
reg = <0x68000000 0x10000>;
interrupts = <9 10>;
#address-cells = <1>;
clocks = <&cpg_clocks R8A7740_CLK_S>,
<&cpg_clocks R8A7740_CLK_S>, <&sub_clk>,
<&cpg_clocks R8A7740_CLK_B>,
- <&sub_clk>, <&sub_clk>,
+ <&cpg_clocks R8A7740_CLK_HPP>, <&sub_clk>,
<&cpg_clocks R8A7740_CLK_B>;
#clock-cells = <1>;
renesas,clock-indices = <
#clock-cells = <0>;
clock-output-names = "sd2";
};
- sd3_clk: sd3_clk@e615007c {
+ sd3_clk: sd3_clk@e615026c {
compatible = "renesas,r8a7790-div6-clock", "renesas,cpg-div6-clock";
- reg = <0 0xe615007c 0 4>;
+ reg = <0 0xe615026c 0 4>;
clocks = <&pll1_div2_clk>;
#clock-cells = <0>;
clock-output-names = "sd3";
#include "sama5d3_uart.dtsi"
/ {
- compatible = "atmel,samad31", "atmel,sama5d3", "atmel,sama5";
+ compatible = "atmel,sama5d31", "atmel,sama5d3", "atmel,sama5";
};
#include "sama5d3_gmac.dtsi"
/ {
- compatible = "atmel,samad33", "atmel,sama5d3", "atmel,sama5";
+ compatible = "atmel,sama5d33", "atmel,sama5d3", "atmel,sama5";
};
#include "sama5d3_mci2.dtsi"
/ {
- compatible = "atmel,samad34", "atmel,sama5d3", "atmel,sama5";
+ compatible = "atmel,sama5d34", "atmel,sama5d3", "atmel,sama5";
};
#include "sama5d3_tcb1.dtsi"
/ {
- compatible = "atmel,samad35", "atmel,sama5d3", "atmel,sama5";
+ compatible = "atmel,sama5d35", "atmel,sama5d3", "atmel,sama5";
};
#include "sama5d3_uart.dtsi"
/ {
- compatible = "atmel,samad36", "atmel,sama5d3", "atmel,sama5";
+ compatible = "atmel,sama5d36", "atmel,sama5d3", "atmel,sama5";
};
*/
/ {
- compatible = "atmel,samad3xcm", "atmel,sama5d3", "atmel,sama5";
+ compatible = "atmel,sama5d3xcm", "atmel,sama5d3", "atmel,sama5";
chosen {
bootargs = "console=ttyS0,115200 rootfstype=ubifs ubi.mtd=5 root=ubi0:rootfs";
clocks = <&ahb1_gates 6>;
resets = <&ahb1_rst 6>;
#dma-cells = <1>;
+
+ /* DMA controller requires AHB1 clocked from PLL6 */
+ assigned-clocks = <&ahb1_mux>;
+ assigned-clock-parents = <&pll6>;
};
mmc0: mmc@01c0f000 {
aliases {
rtc0 = "/i2c@7000d000/tps65913@58";
rtc1 = "/rtc@7000e000";
+ serial0 = &uartd;
};
memory {
linux,initrd-end = <0x82800000>;
};
+ aliases {
+ serial0 = &uartd;
+ };
+
firmware {
trusted-foundations {
compatible = "tlm,trusted-foundations";
regulator-name = "vddio-sdmmc3";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
- regulator-always-on;
- regulator-boot-on;
};
ldousb {
sdhci@78000400 {
status = "okay";
bus-width = <4>;
- vmmc-supply = <&vddio_sdmmc3>;
+ vqmmc-supply = <&vddio_sdmmc3>;
cd-gpios = <&gpio TEGRA_GPIO(V, 2) GPIO_ACTIVE_LOW>;
power-gpios = <&gpio TEGRA_GPIO(H, 0) GPIO_ACTIVE_HIGH>;
};
sdhci@78000600 {
status = "okay";
bus-width = <8>;
- vmmc-supply = <&vdd_1v8>;
non-removable;
};
linux,initrd-end = <0x82800000>;
};
+ aliases {
+ serial0 = &uartd;
+ };
+
firmware {
trusted-foundations {
compatible = "tlm,trusted-foundations";
sdhci@78000600 {
status = "okay";
bus-width = <8>;
- vmmc-supply = <&vdd_1v8>;
non-removable;
};
compatible = "nvidia,tegra114";
interrupt-parent = <&gic>;
- aliases {
- serial0 = &uarta;
- serial1 = &uartb;
- serial2 = &uartc;
- serial3 = &uartd;
- };
-
host1x@50000000 {
compatible = "nvidia,tegra114-host1x", "simple-bus";
reg = <0x50000000 0x00028000>;
aliases {
rtc0 = "/i2c@0,7000d000/pmic@40";
rtc1 = "/rtc@0,7000e000";
+ serial0 = &uartd;
};
memory {
aliases {
rtc0 = "/i2c@0,7000d000/pmic@40";
rtc1 = "/rtc@0,7000e000";
+ serial0 = &uarta;
};
memory {
aliases {
rtc0 = "/i2c@0,7000d000/pmic@40";
rtc1 = "/rtc@0,7000e000";
+ serial0 = &uarta;
};
memory {
* the APB DMA based serial driver, the comptible is
* "nvidia,tegra124-hsuart", "nvidia,tegra30-hsuart".
*/
- serial@0,70006000 {
+ uarta: serial@0,70006000 {
compatible = "nvidia,tegra124-uart", "nvidia,tegra20-uart";
reg = <0x0 0x70006000 0x0 0x40>;
reg-shift = <2>;
status = "disabled";
};
- serial@0,70006040 {
+ uartb: serial@0,70006040 {
compatible = "nvidia,tegra124-uart", "nvidia,tegra20-uart";
reg = <0x0 0x70006040 0x0 0x40>;
reg-shift = <2>;
status = "disabled";
};
- serial@0,70006200 {
+ uartc: serial@0,70006200 {
compatible = "nvidia,tegra124-uart", "nvidia,tegra20-uart";
reg = <0x0 0x70006200 0x0 0x40>;
reg-shift = <2>;
status = "disabled";
};
- serial@0,70006300 {
+ uartd: serial@0,70006300 {
compatible = "nvidia,tegra124-uart", "nvidia,tegra20-uart";
reg = <0x0 0x70006300 0x0 0x40>;
reg-shift = <2>;
aliases {
rtc0 = "/i2c@7000d000/tps6586x@34";
rtc1 = "/rtc@7000e000";
+ serial0 = &uartd;
};
memory {
model = "Toradex Colibri T20 512MB on Iris";
compatible = "toradex,iris", "toradex,colibri_t20-512", "nvidia,tegra20";
+ aliases {
+ serial0 = &uarta;
+ serial1 = &uartd;
+ };
+
host1x@50000000 {
hdmi@54280000 {
status = "okay";
model = "Avionic Design Medcom-Wide board";
compatible = "ad,medcom-wide", "ad,tamonten", "nvidia,tegra20";
+ aliases {
+ serial0 = &uartd;
+ };
+
pwm@7000a000 {
status = "okay";
};
aliases {
rtc0 = "/i2c@7000d000/tps6586x@34";
rtc1 = "/rtc@7000e000";
+ serial0 = &uarta;
+ serial1 = &uartc;
};
memory {
aliases {
rtc0 = "/i2c@7000d000/tps6586x@34";
rtc1 = "/rtc@7000e000";
+ serial0 = &uartd;
};
memory {
aliases {
rtc0 = "/i2c@7000d000/tps6586x@34";
rtc1 = "/rtc@7000e000";
+ serial0 = &uartd;
};
memory {
aliases {
rtc0 = "/i2c@7000c500/rtc@56";
rtc1 = "/rtc@7000e000";
+ serial0 = &uarta;
};
memory {
aliases {
rtc0 = "/i2c@7000d000/tps6586x@34";
rtc1 = "/rtc@7000e000";
+ serial0 = &uartd;
};
memory {
aliases {
rtc0 = "/i2c@7000d000/max8907@3c";
rtc1 = "/rtc@7000e000";
+ serial0 = &uarta;
};
memory {
compatible = "nvidia,tegra20";
interrupt-parent = <&intc>;
- aliases {
- serial0 = &uarta;
- serial1 = &uartb;
- serial2 = &uartc;
- serial3 = &uartd;
- serial4 = &uarte;
- };
-
host1x@50000000 {
compatible = "nvidia,tegra20-host1x", "simple-bus";
reg = <0x50000000 0x00024000>;
rtc0 = "/i2c@7000c000/rtc@68";
rtc1 = "/i2c@7000d000/tps65911@2d";
rtc2 = "/rtc@7000e000";
+ serial0 = &uarta;
+ serial1 = &uartb;
+ serial2 = &uartc;
+ serial3 = &uartd;
};
pcie-controller@00003000 {
aliases {
rtc0 = "/i2c@7000d000/tps65911@2d";
rtc1 = "/rtc@7000e000";
+ serial0 = &uarta;
};
memory {
aliases {
rtc0 = "/i2c@7000d000/tps65911@2d";
rtc1 = "/rtc@7000e000";
+ serial0 = &uarta;
+ serial1 = &uartc;
};
memory {
rtc0 = "/i2c@7000c000/rtc@68";
rtc1 = "/i2c@7000d000/tps65911@2d";
rtc2 = "/rtc@7000e000";
+ serial0 = &uarta;
+ serial1 = &uartb;
+ serial2 = &uartd;
};
host1x@50000000 {
compatible = "nvidia,tegra30";
interrupt-parent = <&intc>;
- aliases {
- serial0 = &uarta;
- serial1 = &uartb;
- serial2 = &uartc;
- serial3 = &uartd;
- serial4 = &uarte;
- };
-
pcie-controller@00003000 {
compatible = "nvidia,tegra30-pcie";
device_type = "pci";
};
+&esdhc1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_esdhc1>;
+ bus-width = <4>;
+ status = "okay";
+};
+
&fec1 {
phy-mode = "rmii";
pinctrl-names = "default";
&iomuxc {
vf610-cosmic {
+ pinctrl_esdhc1: esdhc1grp {
+ fsl,pins = <
+ VF610_PAD_PTA24__ESDHC1_CLK 0x31ef
+ VF610_PAD_PTA25__ESDHC1_CMD 0x31ef
+ VF610_PAD_PTA26__ESDHC1_DAT0 0x31ef
+ VF610_PAD_PTA27__ESDHC1_DAT1 0x31ef
+ VF610_PAD_PTA28__ESDHC1_DATA2 0x31ef
+ VF610_PAD_PTA29__ESDHC1_DAT3 0x31ef
+ VF610_PAD_PTB28__GPIO_98 0x219d
+ >;
+ };
+
pinctrl_fec1: fec1grp {
fsl,pins = <
VF610_PAD_PTC9__ENET_RMII1_MDC 0x30d2
};
};
+&clkc {
+ fclk-enable = <0xf>;
+};
+
&gem0 {
status = "okay";
phy-mode = "rgmii-id";
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/edma.h>
+#include <linux/dma-mapping.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_dma.h>
struct device_node *node = pdev->dev.of_node;
struct device *dev = &pdev->dev;
int ret;
+ struct platform_device_info edma_dev_info = {
+ .name = "edma-dma-engine",
+ .dma_mask = DMA_BIT_MASK(32),
+ .parent = &pdev->dev,
+ };
if (node) {
/* Check if this is a second instance registered */
edma_write_array(j, EDMA_QRAE, i, 0x0);
}
arch_num_cc++;
+
+ edma_dev_info.id = j;
+ platform_device_register_full(&edma_dev_info);
}
return 0;
CONFIG_MMC_DW_EXYNOS=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_MAX77686=y
+CONFIG_RTC_DRV_MAX77802=y
CONFIG_RTC_DRV_S5M=y
CONFIG_RTC_DRV_S3C=y
CONFIG_DMADEVICES=y
CONFIG_PL330_DMA=y
CONFIG_COMMON_CLK_MAX77686=y
+CONFIG_COMMON_CLK_MAX77802=y
CONFIG_COMMON_CLK_S2MPS11=y
CONFIG_EXYNOS_IOMMU=y
CONFIG_IIO=y
# CONFIG_HW_RANDOM is not set
CONFIG_I2C_CHARDEV=y
CONFIG_I2C_IMX=y
+CONFIG_SPI=y
CONFIG_SPI_IMX=y
CONFIG_SPI_SPIDEV=y
CONFIG_GPIO_SYSFS=y
CONFIG_I2C_ALGOPCF=m
CONFIG_I2C_ALGOPCA=m
CONFIG_I2C_IMX=y
+CONFIG_SPI=y
CONFIG_SPI_IMX=y
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_MC9S08DZ60=y
CONFIG_I2C_DESIGNWARE_PLATFORM=y
CONFIG_I2C_EXYNOS5=y
CONFIG_I2C_MV64XXX=y
+CONFIG_I2C_S3C2410=y
CONFIG_I2C_SIRF=y
CONFIG_I2C_TEGRA=y
CONFIG_I2C_ST=y
CONFIG_SPI_XILINX=y
CONFIG_PINCTRL_AS3722=y
CONFIG_PINCTRL_PALMAS=y
+CONFIG_PINCTRL_APQ8084=y
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_GENERIC_PLATFORM=y
CONFIG_GPIO_DWAPB=y
CONFIG_NVEC_PAZ00=y
CONFIG_QCOM_GSBI=y
CONFIG_COMMON_CLK_QCOM=y
+CONFIG_APQ_MMCC_8084=y
CONFIG_MSM_GCC_8660=y
CONFIG_MSM_MMCC_8960=y
CONFIG_MSM_MMCC_8974=y
CONFIG_IP_PNP_BOOTP=y
CONFIG_IP_PNP_RARP=y
# CONFIG_INET_LRO is not set
-CONFIG_IPV6=y
CONFIG_NETFILTER=y
CONFIG_CAN=m
CONFIG_CAN_C_CAN=m
CONFIG_MTD_CFI=y
CONFIG_MTD_CFI_INTELEXT=y
CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_ECC_BCH=y
CONFIG_MTD_NAND_OMAP2=y
CONFIG_MTD_ONENAND=y
CONFIG_MTD_ONENAND_VERIFY_WRITE=y
CONFIG_FANOTIFY=y
CONFIG_QUOTA=y
CONFIG_QFMT_V2=y
-CONFIG_AUTOFS4_FS=y
+CONFIG_AUTOFS4_FS=m
CONFIG_MSDOS_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
-CONFIG_EXPERIMENTAL=y
CONFIG_SYSVIPC=y
+CONFIG_FHANDLE=y
+CONFIG_HIGH_RES_TIMERS=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_BUF_SHIFT=14
CONFIG_OPROFILE=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
-CONFIG_HOTPLUG=y
# CONFIG_LBDAF is not set
# CONFIG_BLK_DEV_BSG is not set
# CONFIG_IOSCHED_DEADLINE is not set
# CONFIG_IOSCHED_CFQ is not set
CONFIG_ARCH_SOCFPGA=y
-CONFIG_MACH_SOCFPGA_CYCLONE5=y
CONFIG_ARM_THUMBEE=y
-# CONFIG_ARCH_VEXPRESS_CORTEX_A5_A9_ERRATA is not set
-# CONFIG_CACHE_L2X0 is not set
-CONFIG_HIGH_RES_TIMERS=y
CONFIG_SMP=y
CONFIG_NR_CPUS=2
CONFIG_AEABI=y
CONFIG_ZBOOT_ROM_TEXT=0x0
CONFIG_ZBOOT_ROM_BSS=0x0
-CONFIG_CMDLINE=""
CONFIG_VFP=y
CONFIG_NEON=y
CONFIG_NET=y
CONFIG_IP_PNP_DHCP=y
CONFIG_IP_PNP_BOOTP=y
CONFIG_IP_PNP_RARP=y
+CONFIG_IPV6=y
+CONFIG_NETWORK_PHY_TIMESTAMPING=y
+CONFIG_VLAN_8021Q=y
+CONFIG_VLAN_8021Q_GVRP=y
CONFIG_CAN=y
-CONFIG_CAN_RAW=y
-CONFIG_CAN_BCM=y
-CONFIG_CAN_GW=y
-CONFIG_CAN_DEV=y
-CONFIG_CAN_CALC_BITTIMING=y
CONFIG_CAN_C_CAN=y
CONFIG_CAN_C_CAN_PLATFORM=y
CONFIG_CAN_DEBUG_DEVICES=y
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_DEVTMPFS=y
-CONFIG_PROC_DEVICETREE=y
+CONFIG_DEVTMPFS_MOUNT=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_COUNT=2
CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_SRAM=y
CONFIG_SCSI=y
# CONFIG_SCSI_PROC_FS is not set
CONFIG_BLK_DEV_SD=y
# CONFIG_SCSI_LOWLEVEL is not set
CONFIG_NETDEVICES=y
CONFIG_STMMAC_ETH=y
+CONFIG_DWMAC_SOCFPGA=y
CONFIG_MICREL_PHY=y
-# CONFIG_STMMAC_PHY_ID_ZERO_WORKAROUND is not set
CONFIG_INPUT_EVDEV=y
-CONFIG_DWMAC_SOCFPGA=y
-CONFIG_PPS=y
-CONFIG_NETWORK_PHY_TIMESTAMPING=y
-CONFIG_PTP_1588_CLOCK=y
-CONFIG_VLAN_8021Q=y
-CONFIG_VLAN_8021Q_GVRP=y
-CONFIG_GARP=y
-CONFIG_IPV6=y
# CONFIG_SERIO_SERPORT is not set
CONFIG_SERIO_AMBAKMI=y
CONFIG_LEGACY_PTY_COUNT=16
CONFIG_SERIAL_8250_NR_UARTS=2
CONFIG_SERIAL_8250_RUNTIME_UARTS=2
CONFIG_SERIAL_8250_DW=y
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_DESIGNWARE_PLATFORM=y
CONFIG_GPIOLIB=y
CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_DWAPB=y
-# CONFIG_RTC_HCTOSYS is not set
+CONFIG_PMBUS=y
+CONFIG_SENSORS_LTC2978=y
+CONFIG_SENSORS_LTC2978_REGULATOR=y
CONFIG_WATCHDOG=y
CONFIG_DW_WATCHDOG=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_USB=y
+CONFIG_USB_DWC2=y
+CONFIG_USB_DWC2_HOST=y
+CONFIG_MMC=y
+CONFIG_MMC_DW=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT2_FS_POSIX_ACL=y
CONFIG_EXT3_FS=y
-CONFIG_NFS_FS=y
-CONFIG_ROOT_NFS=y
-# CONFIG_DNOTIFY is not set
-# CONFIG_INOTIFY_USER is not set
-CONFIG_FHANDLE=y
+CONFIG_EXT4_FS=y
CONFIG_VFAT_FS=y
CONFIG_NTFS_FS=y
CONFIG_NTFS_RW=y
CONFIG_TMPFS=y
-CONFIG_JFFS2_FS=y
+CONFIG_CONFIGFS_FS=y
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_INFO=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_DETECT_HUNG_TASK=y
# CONFIG_SCHED_DEBUG is not set
-CONFIG_DEBUG_INFO=y
CONFIG_ENABLE_DEFAULT_TRACERS=y
CONFIG_DEBUG_USER=y
CONFIG_XZ_DEC=y
-CONFIG_I2C=y
-CONFIG_I2C_DESIGNWARE_CORE=y
-CONFIG_I2C_DESIGNWARE_PLATFORM=y
-CONFIG_I2C_CHARDEV=y
-CONFIG_MMC=y
-CONFIG_MMC_DW=y
-CONFIG_PM=y
-CONFIG_SUSPEND=y
-CONFIG_MMC_UNSAFE_RESUME=y
-CONFIG_USB=y
-CONFIG_USB_DWC2=y
-CONFIG_USB_DWC2_HOST=y
-CONFIG_USB_DWC2_PLATFORM=y
__u32 extra[2]; /* Xscale 'acc' register, etc */
};
-struct arm_restart_block {
- union {
- /* For user cache flushing */
- struct {
- unsigned long start;
- unsigned long end;
- } cache;
- };
-};
-
/*
* low level task data that entry.S needs immediate access to.
* __switch_to() assumes cpu_context follows immediately after cpu_domain.
unsigned long thumbee_state; /* ThumbEE Handler Base register */
#endif
struct restart_block restart_block;
- struct arm_restart_block arm_restart_block;
};
#define INIT_THREAD_INFO(tsk) \
return regs->ARM_r0;
}
-static long do_cache_op_restart(struct restart_block *);
-
static inline int
__do_cache_op(unsigned long start, unsigned long end)
{
do {
unsigned long chunk = min(PAGE_SIZE, end - start);
- if (signal_pending(current)) {
- struct thread_info *ti = current_thread_info();
-
- ti->restart_block = (struct restart_block) {
- .fn = do_cache_op_restart,
- };
-
- ti->arm_restart_block = (struct arm_restart_block) {
- {
- .cache = {
- .start = start,
- .end = end,
- },
- },
- };
-
- return -ERESTART_RESTARTBLOCK;
- }
+ if (fatal_signal_pending(current))
+ return 0;
ret = flush_cache_user_range(start, start + chunk);
if (ret)
return 0;
}
-static long do_cache_op_restart(struct restart_block *unused)
-{
- struct arm_restart_block *restart_block;
-
- restart_block = ¤t_thread_info()->arm_restart_block;
- return __do_cache_op(restart_block->cache.start,
- restart_block->cache.end);
-}
-
static inline int
do_cache_op(unsigned long start, unsigned long end, int flags)
{
pgd = pgdp + pgd_index(addr);
do {
next = kvm_pgd_addr_end(addr, end);
- unmap_puds(kvm, pgd, addr, next);
+ if (!pgd_none(*pgd))
+ unmap_puds(kvm, pgd, addr, next);
} while (pgd++, addr = next, addr != end);
}
return kvm_vcpu_dabt_iswrite(vcpu);
}
+static bool kvm_is_device_pfn(unsigned long pfn)
+{
+ return !pfn_valid(pfn);
+}
+
static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
struct kvm_memory_slot *memslot, unsigned long hva,
unsigned long fault_status)
if (is_error_pfn(pfn))
return -EFAULT;
- if (kvm_is_mmio_pfn(pfn))
+ if (kvm_is_device_pfn(pfn))
mem_type = PAGE_S2_DEVICE;
spin_lock(&kvm->mmu_lock);
+++ /dev/null
-#ifndef __MACH_ATMEL_MCI_H
-#define __MACH_ATMEL_MCI_H
-
-#include <linux/platform_data/dma-atmel.h>
-
-/**
- * struct mci_dma_data - DMA data for MCI interface
- */
-struct mci_dma_data {
- struct at_dma_slave sdata;
-};
-
-/* accessor macros */
-#define slave_data_ptr(s) (&(s)->sdata)
-#define find_slave_dev(s) ((s)->sdata.dma_dev)
-
-#endif /* __MACH_ATMEL_MCI_H */
.num_channels = ARRAY_SIZE(ep93xx_dma_m2p_channels),
};
+static u64 ep93xx_dma_m2p_mask = DMA_BIT_MASK(32);
+
static struct platform_device ep93xx_dma_m2p_device = {
.name = "ep93xx-dma-m2p",
.id = -1,
.dev = {
- .platform_data = &ep93xx_dma_m2p_data,
+ .platform_data = &ep93xx_dma_m2p_data,
+ .dma_mask = &ep93xx_dma_m2p_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
},
};
.num_channels = ARRAY_SIZE(ep93xx_dma_m2m_channels),
};
+static u64 ep93xx_dma_m2m_mask = DMA_BIT_MASK(32);
+
static struct platform_device ep93xx_dma_m2m_device = {
.name = "ep93xx-dma-m2m",
.id = -1,
.dev = {
- .platform_data = &ep93xx_dma_m2m_data,
+ .platform_data = &ep93xx_dma_m2m_data,
+ .dma_mask = &ep93xx_dma_m2m_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
},
};
EXYNOS5_TOP_PWR_SYSMEM_OPTION,
};
-static unsigned int const exynos5_list_diable_wfi_wfe[] = {
+static unsigned int const exynos5_list_disable_wfi_wfe[] = {
EXYNOS5_ARM_CORE1_OPTION,
EXYNOS5_FSYS_ARM_OPTION,
EXYNOS5_ISP_ARM_OPTION,
/*
* Disable WFI/WFE on XXX_OPTION
*/
- for (i = 0 ; i < ARRAY_SIZE(exynos5_list_diable_wfi_wfe) ; i++) {
- tmp = pmu_raw_readl(exynos5_list_diable_wfi_wfe[i]);
+ for (i = 0 ; i < ARRAY_SIZE(exynos5_list_disable_wfi_wfe) ; i++) {
+ tmp = pmu_raw_readl(exynos5_list_disable_wfi_wfe[i]);
tmp &= ~(EXYNOS5_OPTION_USE_STANDBYWFE |
EXYNOS5_OPTION_USE_STANDBYWFI);
- pmu_raw_writel(tmp, exynos5_list_diable_wfi_wfe[i]);
+ pmu_raw_writel(tmp, exynos5_list_disable_wfi_wfe[i]);
}
}
#define PFD_PLL1_BASE (anatop_base + 0x2b0)
#define PFD_PLL2_BASE (anatop_base + 0x100)
#define PFD_PLL3_BASE (anatop_base + 0xf0)
+#define PLL1_CTRL (anatop_base + 0x270)
+#define PLL2_CTRL (anatop_base + 0x30)
#define PLL3_CTRL (anatop_base + 0x10)
+#define PLL4_CTRL (anatop_base + 0x70)
+#define PLL5_CTRL (anatop_base + 0xe0)
+#define PLL6_CTRL (anatop_base + 0xa0)
#define PLL7_CTRL (anatop_base + 0x20)
+#define ANA_MISC1 (anatop_base + 0x160)
static void __iomem *anatop_base;
static void __iomem *ccm_base;
/* sources for multiplexer clocks, this is used multiple times */
static const char *fast_sels[] = { "firc", "fxosc", };
static const char *slow_sels[] = { "sirc_32k", "sxosc", };
-static const char *pll1_sels[] = { "pll1_main", "pll1_pfd1", "pll1_pfd2", "pll1_pfd3", "pll1_pfd4", };
-static const char *pll2_sels[] = { "pll2_main", "pll2_pfd1", "pll2_pfd2", "pll2_pfd3", "pll2_pfd4", };
-static const char *sys_sels[] = { "fast_clk_sel", "slow_clk_sel", "pll2_pfd_sel", "pll2_main", "pll1_pfd_sel", "pll3_main", };
+static const char *pll1_sels[] = { "pll1_sys", "pll1_pfd1", "pll1_pfd2", "pll1_pfd3", "pll1_pfd4", };
+static const char *pll2_sels[] = { "pll2_bus", "pll2_pfd1", "pll2_pfd2", "pll2_pfd3", "pll2_pfd4", };
+static const char *pll_bypass_src_sels[] = { "fast_clk_sel", "lvds1_in", };
+static const char *pll1_bypass_sels[] = { "pll1", "pll1_bypass_src", };
+static const char *pll2_bypass_sels[] = { "pll2", "pll2_bypass_src", };
+static const char *pll3_bypass_sels[] = { "pll3", "pll3_bypass_src", };
+static const char *pll4_bypass_sels[] = { "pll4", "pll4_bypass_src", };
+static const char *pll5_bypass_sels[] = { "pll5", "pll5_bypass_src", };
+static const char *pll6_bypass_sels[] = { "pll6", "pll6_bypass_src", };
+static const char *pll7_bypass_sels[] = { "pll7", "pll7_bypass_src", };
+static const char *sys_sels[] = { "fast_clk_sel", "slow_clk_sel", "pll2_pfd_sel", "pll2_bus", "pll1_pfd_sel", "pll3_usb_otg", };
static const char *ddr_sels[] = { "pll2_pfd2", "sys_sel", };
static const char *rmii_sels[] = { "enet_ext", "audio_ext", "enet_50m", "enet_25m", };
static const char *enet_ts_sels[] = { "enet_ext", "fxosc", "audio_ext", "usb", "enet_ts", "enet_25m", "enet_50m", };
-static const char *esai_sels[] = { "audio_ext", "mlb", "spdif_rx", "pll4_main_div", };
-static const char *sai_sels[] = { "audio_ext", "mlb", "spdif_rx", "pll4_main_div", };
+static const char *esai_sels[] = { "audio_ext", "mlb", "spdif_rx", "pll4_audio_div", };
+static const char *sai_sels[] = { "audio_ext", "mlb", "spdif_rx", "pll4_audio_div", };
static const char *nfc_sels[] = { "platform_bus", "pll1_pfd1", "pll3_pfd1", "pll3_pfd3", };
-static const char *qspi_sels[] = { "pll3_main", "pll3_pfd4", "pll2_pfd4", "pll1_pfd4", };
-static const char *esdhc_sels[] = { "pll3_main", "pll3_pfd3", "pll1_pfd3", "platform_bus", };
-static const char *dcu_sels[] = { "pll1_pfd2", "pll3_main", };
+static const char *qspi_sels[] = { "pll3_usb_otg", "pll3_pfd4", "pll2_pfd4", "pll1_pfd4", };
+static const char *esdhc_sels[] = { "pll3_usb_otg", "pll3_pfd3", "pll1_pfd3", "platform_bus", };
+static const char *dcu_sels[] = { "pll1_pfd2", "pll3_usb_otg", };
static const char *gpu_sels[] = { "pll2_pfd2", "pll3_pfd2", };
-static const char *vadc_sels[] = { "pll6_main_div", "pll3_main_div", "pll3_main", };
+static const char *vadc_sels[] = { "pll6_video_div", "pll3_usb_otg_div", "pll3_usb_otg", };
/* FTM counter clock source, not module clock */
static const char *ftm_ext_sels[] = {"sirc_128k", "sxosc", "fxosc_half", "audio_ext", };
static const char *ftm_fix_sels[] = { "sxosc", "ipg_bus", };
-static struct clk_div_table pll4_main_div_table[] = {
+
+static struct clk_div_table pll4_audio_div_table[] = {
{ .val = 0, .div = 1 },
{ .val = 1, .div = 2 },
{ .val = 2, .div = 6 },
clk[VF610_CLK_AUDIO_EXT] = imx_obtain_fixed_clock("audio_ext", 0);
clk[VF610_CLK_ENET_EXT] = imx_obtain_fixed_clock("enet_ext", 0);
+ /* Clock source from external clock via LVDs PAD */
+ clk[VF610_CLK_ANACLK1] = imx_obtain_fixed_clock("anaclk1", 0);
+
clk[VF610_CLK_FXOSC_HALF] = imx_clk_fixed_factor("fxosc_half", "fxosc", 1, 2);
np = of_find_compatible_node(NULL, NULL, "fsl,vf610-anatop");
clk[VF610_CLK_SLOW_CLK_SEL] = imx_clk_mux("slow_clk_sel", CCM_CCSR, 4, 1, slow_sels, ARRAY_SIZE(slow_sels));
clk[VF610_CLK_FASK_CLK_SEL] = imx_clk_mux("fast_clk_sel", CCM_CCSR, 5, 1, fast_sels, ARRAY_SIZE(fast_sels));
- clk[VF610_CLK_PLL1_MAIN] = imx_clk_fixed_factor("pll1_main", "fast_clk_sel", 22, 1);
- clk[VF610_CLK_PLL1_PFD1] = imx_clk_pfd("pll1_pfd1", "pll1_main", PFD_PLL1_BASE, 0);
- clk[VF610_CLK_PLL1_PFD2] = imx_clk_pfd("pll1_pfd2", "pll1_main", PFD_PLL1_BASE, 1);
- clk[VF610_CLK_PLL1_PFD3] = imx_clk_pfd("pll1_pfd3", "pll1_main", PFD_PLL1_BASE, 2);
- clk[VF610_CLK_PLL1_PFD4] = imx_clk_pfd("pll1_pfd4", "pll1_main", PFD_PLL1_BASE, 3);
-
- clk[VF610_CLK_PLL2_MAIN] = imx_clk_fixed_factor("pll2_main", "fast_clk_sel", 22, 1);
- clk[VF610_CLK_PLL2_PFD1] = imx_clk_pfd("pll2_pfd1", "pll2_main", PFD_PLL2_BASE, 0);
- clk[VF610_CLK_PLL2_PFD2] = imx_clk_pfd("pll2_pfd2", "pll2_main", PFD_PLL2_BASE, 1);
- clk[VF610_CLK_PLL2_PFD3] = imx_clk_pfd("pll2_pfd3", "pll2_main", PFD_PLL2_BASE, 2);
- clk[VF610_CLK_PLL2_PFD4] = imx_clk_pfd("pll2_pfd4", "pll2_main", PFD_PLL2_BASE, 3);
-
- clk[VF610_CLK_PLL3_MAIN] = imx_clk_fixed_factor("pll3_main", "fast_clk_sel", 20, 1);
- clk[VF610_CLK_PLL3_PFD1] = imx_clk_pfd("pll3_pfd1", "pll3_main", PFD_PLL3_BASE, 0);
- clk[VF610_CLK_PLL3_PFD2] = imx_clk_pfd("pll3_pfd2", "pll3_main", PFD_PLL3_BASE, 1);
- clk[VF610_CLK_PLL3_PFD3] = imx_clk_pfd("pll3_pfd3", "pll3_main", PFD_PLL3_BASE, 2);
- clk[VF610_CLK_PLL3_PFD4] = imx_clk_pfd("pll3_pfd4", "pll3_main", PFD_PLL3_BASE, 3);
-
- clk[VF610_CLK_PLL4_MAIN] = imx_clk_fixed_factor("pll4_main", "fast_clk_sel", 25, 1);
- /* Enet pll: fixed 50Mhz */
- clk[VF610_CLK_PLL5_MAIN] = imx_clk_fixed_factor("pll5_main", "fast_clk_sel", 125, 6);
- /* pll6: default 960Mhz */
- clk[VF610_CLK_PLL6_MAIN] = imx_clk_fixed_factor("pll6_main", "fast_clk_sel", 40, 1);
- /* pll7: USB1 PLL at 480MHz */
- clk[VF610_CLK_PLL7_MAIN] = imx_clk_pllv3(IMX_PLLV3_USB, "pll7_main", "fast_clk_sel", PLL7_CTRL, 0x2);
+ clk[VF610_CLK_PLL1_BYPASS_SRC] = imx_clk_mux("pll1_bypass_src", PLL1_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels));
+ clk[VF610_CLK_PLL2_BYPASS_SRC] = imx_clk_mux("pll2_bypass_src", PLL2_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels));
+ clk[VF610_CLK_PLL3_BYPASS_SRC] = imx_clk_mux("pll3_bypass_src", PLL3_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels));
+ clk[VF610_CLK_PLL4_BYPASS_SRC] = imx_clk_mux("pll4_bypass_src", PLL4_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels));
+ clk[VF610_CLK_PLL5_BYPASS_SRC] = imx_clk_mux("pll5_bypass_src", PLL5_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels));
+ clk[VF610_CLK_PLL6_BYPASS_SRC] = imx_clk_mux("pll6_bypass_src", PLL6_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels));
+ clk[VF610_CLK_PLL7_BYPASS_SRC] = imx_clk_mux("pll7_bypass_src", PLL7_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels));
+
+ clk[VF610_CLK_PLL1] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll1", "pll1_bypass_src", PLL1_CTRL, 0x1);
+ clk[VF610_CLK_PLL2] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2", "pll2_bypass_src", PLL2_CTRL, 0x1);
+ clk[VF610_CLK_PLL3] = imx_clk_pllv3(IMX_PLLV3_USB, "pll3", "pll3_bypass_src", PLL3_CTRL, 0x1);
+ clk[VF610_CLK_PLL4] = imx_clk_pllv3(IMX_PLLV3_AV, "pll4", "pll4_bypass_src", PLL4_CTRL, 0x7f);
+ clk[VF610_CLK_PLL5] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll5", "pll5_bypass_src", PLL5_CTRL, 0x3);
+ clk[VF610_CLK_PLL6] = imx_clk_pllv3(IMX_PLLV3_AV, "pll6", "pll6_bypass_src", PLL6_CTRL, 0x7f);
+ clk[VF610_CLK_PLL7] = imx_clk_pllv3(IMX_PLLV3_USB, "pll7", "pll7_bypass_src", PLL7_CTRL, 0x1);
+
+ clk[VF610_PLL1_BYPASS] = imx_clk_mux_flags("pll1_bypass", PLL1_CTRL, 16, 1, pll1_bypass_sels, ARRAY_SIZE(pll1_bypass_sels), CLK_SET_RATE_PARENT);
+ clk[VF610_PLL2_BYPASS] = imx_clk_mux_flags("pll2_bypass", PLL2_CTRL, 16, 1, pll2_bypass_sels, ARRAY_SIZE(pll2_bypass_sels), CLK_SET_RATE_PARENT);
+ clk[VF610_PLL3_BYPASS] = imx_clk_mux_flags("pll3_bypass", PLL3_CTRL, 16, 1, pll3_bypass_sels, ARRAY_SIZE(pll3_bypass_sels), CLK_SET_RATE_PARENT);
+ clk[VF610_PLL4_BYPASS] = imx_clk_mux_flags("pll4_bypass", PLL4_CTRL, 16, 1, pll4_bypass_sels, ARRAY_SIZE(pll4_bypass_sels), CLK_SET_RATE_PARENT);
+ clk[VF610_PLL5_BYPASS] = imx_clk_mux_flags("pll5_bypass", PLL5_CTRL, 16, 1, pll5_bypass_sels, ARRAY_SIZE(pll5_bypass_sels), CLK_SET_RATE_PARENT);
+ clk[VF610_PLL6_BYPASS] = imx_clk_mux_flags("pll6_bypass", PLL6_CTRL, 16, 1, pll6_bypass_sels, ARRAY_SIZE(pll6_bypass_sels), CLK_SET_RATE_PARENT);
+ clk[VF610_PLL7_BYPASS] = imx_clk_mux_flags("pll7_bypass", PLL7_CTRL, 16, 1, pll7_bypass_sels, ARRAY_SIZE(pll7_bypass_sels), CLK_SET_RATE_PARENT);
+
+ /* Do not bypass PLLs initially */
+ clk_set_parent(clk[VF610_PLL1_BYPASS], clk[VF610_CLK_PLL1]);
+ clk_set_parent(clk[VF610_PLL2_BYPASS], clk[VF610_CLK_PLL2]);
+ clk_set_parent(clk[VF610_PLL3_BYPASS], clk[VF610_CLK_PLL3]);
+ clk_set_parent(clk[VF610_PLL4_BYPASS], clk[VF610_CLK_PLL4]);
+ clk_set_parent(clk[VF610_PLL5_BYPASS], clk[VF610_CLK_PLL5]);
+ clk_set_parent(clk[VF610_PLL6_BYPASS], clk[VF610_CLK_PLL6]);
+ clk_set_parent(clk[VF610_PLL7_BYPASS], clk[VF610_CLK_PLL7]);
+
+ clk[VF610_CLK_PLL1_SYS] = imx_clk_gate("pll1_sys", "pll1_bypass", PLL1_CTRL, 13);
+ clk[VF610_CLK_PLL2_BUS] = imx_clk_gate("pll2_bus", "pll2_bypass", PLL2_CTRL, 13);
+ clk[VF610_CLK_PLL3_USB_OTG] = imx_clk_gate("pll3_usb_otg", "pll3_bypass", PLL3_CTRL, 13);
+ clk[VF610_CLK_PLL4_AUDIO] = imx_clk_gate("pll4_audio", "pll4_bypass", PLL4_CTRL, 13);
+ clk[VF610_CLK_PLL5_ENET] = imx_clk_gate("pll5_enet", "pll5_bypass", PLL5_CTRL, 13);
+ clk[VF610_CLK_PLL6_VIDEO] = imx_clk_gate("pll6_video", "pll6_bypass", PLL6_CTRL, 13);
+ clk[VF610_CLK_PLL7_USB_HOST] = imx_clk_gate("pll7_usb_host", "pll7_bypass", PLL7_CTRL, 13);
+
+ clk[VF610_CLK_LVDS1_IN] = imx_clk_gate_exclusive("lvds1_in", "anaclk1", ANA_MISC1, 12, BIT(10));
+
+ clk[VF610_CLK_PLL1_PFD1] = imx_clk_pfd("pll1_pfd1", "pll1_sys", PFD_PLL1_BASE, 0);
+ clk[VF610_CLK_PLL1_PFD2] = imx_clk_pfd("pll1_pfd2", "pll1_sys", PFD_PLL1_BASE, 1);
+ clk[VF610_CLK_PLL1_PFD3] = imx_clk_pfd("pll1_pfd3", "pll1_sys", PFD_PLL1_BASE, 2);
+ clk[VF610_CLK_PLL1_PFD4] = imx_clk_pfd("pll1_pfd4", "pll1_sys", PFD_PLL1_BASE, 3);
+
+ clk[VF610_CLK_PLL2_PFD1] = imx_clk_pfd("pll2_pfd1", "pll2_bus", PFD_PLL2_BASE, 0);
+ clk[VF610_CLK_PLL2_PFD2] = imx_clk_pfd("pll2_pfd2", "pll2_bus", PFD_PLL2_BASE, 1);
+ clk[VF610_CLK_PLL2_PFD3] = imx_clk_pfd("pll2_pfd3", "pll2_bus", PFD_PLL2_BASE, 2);
+ clk[VF610_CLK_PLL2_PFD4] = imx_clk_pfd("pll2_pfd4", "pll2_bus", PFD_PLL2_BASE, 3);
+
+ clk[VF610_CLK_PLL3_PFD1] = imx_clk_pfd("pll3_pfd1", "pll3_usb_otg", PFD_PLL3_BASE, 0);
+ clk[VF610_CLK_PLL3_PFD2] = imx_clk_pfd("pll3_pfd2", "pll3_usb_otg", PFD_PLL3_BASE, 1);
+ clk[VF610_CLK_PLL3_PFD3] = imx_clk_pfd("pll3_pfd3", "pll3_usb_otg", PFD_PLL3_BASE, 2);
+ clk[VF610_CLK_PLL3_PFD4] = imx_clk_pfd("pll3_pfd4", "pll3_usb_otg", PFD_PLL3_BASE, 3);
clk[VF610_CLK_PLL1_PFD_SEL] = imx_clk_mux("pll1_pfd_sel", CCM_CCSR, 16, 3, pll1_sels, 5);
clk[VF610_CLK_PLL2_PFD_SEL] = imx_clk_mux("pll2_pfd_sel", CCM_CCSR, 19, 3, pll2_sels, 5);
clk[VF610_CLK_PLATFORM_BUS] = imx_clk_divider("platform_bus", "sys_bus", CCM_CACRR, 3, 3);
clk[VF610_CLK_IPG_BUS] = imx_clk_divider("ipg_bus", "platform_bus", CCM_CACRR, 11, 2);
- clk[VF610_CLK_PLL3_MAIN_DIV] = imx_clk_divider("pll3_main_div", "pll3_main", CCM_CACRR, 20, 1);
- clk[VF610_CLK_PLL4_MAIN_DIV] = clk_register_divider_table(NULL, "pll4_main_div", "pll4_main", 0, CCM_CACRR, 6, 3, 0, pll4_main_div_table, &imx_ccm_lock);
- clk[VF610_CLK_PLL6_MAIN_DIV] = imx_clk_divider("pll6_main_div", "pll6_main", CCM_CACRR, 21, 1);
+ clk[VF610_CLK_PLL3_MAIN_DIV] = imx_clk_divider("pll3_usb_otg_div", "pll3_usb_otg", CCM_CACRR, 20, 1);
+ clk[VF610_CLK_PLL4_MAIN_DIV] = clk_register_divider_table(NULL, "pll4_audio_div", "pll4_audio", 0, CCM_CACRR, 6, 3, 0, pll4_audio_div_table, &imx_ccm_lock);
+ clk[VF610_CLK_PLL6_MAIN_DIV] = imx_clk_divider("pll6_video_div", "pll6_video", CCM_CACRR, 21, 1);
- clk[VF610_CLK_USBPHY0] = imx_clk_gate("usbphy0", "pll3_main", PLL3_CTRL, 6);
- clk[VF610_CLK_USBPHY1] = imx_clk_gate("usbphy1", "pll7_main", PLL7_CTRL, 6);
+ clk[VF610_CLK_USBPHY0] = imx_clk_gate("usbphy0", "pll3_usb_otg", PLL3_CTRL, 6);
+ clk[VF610_CLK_USBPHY1] = imx_clk_gate("usbphy1", "pll7_usb_host", PLL7_CTRL, 6);
clk[VF610_CLK_USBC0] = imx_clk_gate2("usbc0", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(4));
clk[VF610_CLK_USBC1] = imx_clk_gate2("usbc1", "ipg_bus", CCM_CCGR7, CCM_CCGRx_CGn(4));
clk[VF610_CLK_QSPI1_X1_DIV] = imx_clk_divider("qspi1_x1", "qspi1_x2", CCM_CSCDR3, 11, 1);
clk[VF610_CLK_QSPI1] = imx_clk_gate2("qspi1", "qspi1_x1", CCM_CCGR8, CCM_CCGRx_CGn(4));
- clk[VF610_CLK_ENET_50M] = imx_clk_fixed_factor("enet_50m", "pll5_main", 1, 10);
- clk[VF610_CLK_ENET_25M] = imx_clk_fixed_factor("enet_25m", "pll5_main", 1, 20);
+ clk[VF610_CLK_ENET_50M] = imx_clk_fixed_factor("enet_50m", "pll5_enet", 1, 10);
+ clk[VF610_CLK_ENET_25M] = imx_clk_fixed_factor("enet_25m", "pll5_enet", 1, 20);
clk[VF610_CLK_ENET_SEL] = imx_clk_mux("enet_sel", CCM_CSCMR2, 4, 2, rmii_sels, 4);
clk[VF610_CLK_ENET_TS_SEL] = imx_clk_mux("enet_ts_sel", CCM_CSCMR2, 0, 3, enet_ts_sels, 7);
clk[VF610_CLK_ENET] = imx_clk_gate("enet", "enet_sel", CCM_CSCDR1, 24);
void imx_gpc_post_resume(void);
void imx_gpc_mask_all(void);
void imx_gpc_restore_all(void);
-void imx_gpc_irq_mask(struct irq_data *d);
-void imx_gpc_irq_unmask(struct irq_data *d);
+void imx_gpc_hwirq_mask(unsigned int hwirq);
+void imx_gpc_hwirq_unmask(unsigned int hwirq);
void imx_anatop_init(void);
void imx_anatop_pre_suspend(void);
void imx_anatop_post_resume(void);
static int imx_gpc_irq_set_wake(struct irq_data *d, unsigned int on)
{
- unsigned int idx = d->irq / 32 - 1;
+ unsigned int idx = d->hwirq / 32 - 1;
u32 mask;
/* Sanity check for SPI irq */
- if (d->irq < 32)
+ if (d->hwirq < 32)
return -EINVAL;
- mask = 1 << d->irq % 32;
+ mask = 1 << d->hwirq % 32;
gpc_wake_irqs[idx] = on ? gpc_wake_irqs[idx] | mask :
gpc_wake_irqs[idx] & ~mask;
writel_relaxed(gpc_saved_imrs[i], reg_imr1 + i * 4);
}
-void imx_gpc_irq_unmask(struct irq_data *d)
+void imx_gpc_hwirq_unmask(unsigned int hwirq)
{
void __iomem *reg;
u32 val;
- /* Sanity check for SPI irq */
- if (d->irq < 32)
- return;
-
- reg = gpc_base + GPC_IMR1 + (d->irq / 32 - 1) * 4;
+ reg = gpc_base + GPC_IMR1 + (hwirq / 32 - 1) * 4;
val = readl_relaxed(reg);
- val &= ~(1 << d->irq % 32);
+ val &= ~(1 << hwirq % 32);
writel_relaxed(val, reg);
}
-void imx_gpc_irq_mask(struct irq_data *d)
+void imx_gpc_hwirq_mask(unsigned int hwirq)
{
void __iomem *reg;
u32 val;
+ reg = gpc_base + GPC_IMR1 + (hwirq / 32 - 1) * 4;
+ val = readl_relaxed(reg);
+ val |= 1 << (hwirq % 32);
+ writel_relaxed(val, reg);
+}
+
+static void imx_gpc_irq_unmask(struct irq_data *d)
+{
+ /* Sanity check for SPI irq */
+ if (d->hwirq < 32)
+ return;
+
+ imx_gpc_hwirq_unmask(d->hwirq);
+}
+
+static void imx_gpc_irq_mask(struct irq_data *d)
+{
/* Sanity check for SPI irq */
- if (d->irq < 32)
+ if (d->hwirq < 32)
return;
- reg = gpc_base + GPC_IMR1 + (d->irq / 32 - 1) * 4;
- val = readl_relaxed(reg);
- val |= 1 << (d->irq % 32);
- writel_relaxed(val, reg);
+ imx_gpc_hwirq_mask(d->hwirq);
}
void __init imx_gpc_init(void)
int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
{
- struct irq_data *iomuxc_irq_data = irq_get_irq_data(32);
u32 val = readl_relaxed(ccm_base + CLPCR);
val &= ~BM_CLPCR_LPM;
* 3) Software should mask IRQ #32 right after CCM Low-Power mode
* is set (set bits 0-1 of CCM_CLPCR).
*/
- imx_gpc_irq_unmask(iomuxc_irq_data);
+ imx_gpc_hwirq_unmask(32);
writel_relaxed(val, ccm_base + CLPCR);
- imx_gpc_irq_mask(iomuxc_irq_data);
+ imx_gpc_hwirq_mask(32);
return 0;
}
u32 n, byte_enables, data;
if (!is_pci_memory(addr)) {
- __raw_writeb(value, addr);
+ __raw_writeb(value, p);
return;
}
u32 n, byte_enables, data;
if (!is_pci_memory(addr))
- return __raw_readb(addr);
+ return __raw_readb(p);
n = addr % 4;
byte_enables = (0xf & ~BIT(n)) << IXP4XX_PCI_NP_CBE_BESL;
static void __init mvebu_dt_init(void)
{
- if (of_machine_is_compatible("plathome,openblocks-ax3-4"))
+ if (of_machine_is_compatible("marvell,armadaxp"))
i2c_quirk();
if (of_machine_is_compatible("marvell,a375-db")) {
external_abort_quirk();
type == COHERENCY_FABRIC_TYPE_ARMADA_380)
armada_375_380_coherency_init(np);
+ of_node_put(np);
+
return 0;
}
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/usb/musb.h>
+#include <linux/mmc/host.h>
#include <linux/platform_data/spi-omap2-mcspi.h>
#include <linux/platform_data/mtd-onenand-omap2.h>
+#include <linux/platform_data/mmc-omap.h>
#include <linux/mfd/menelaus.h>
#include <sound/tlv320aic3x.h>
* Current flows to eMMC when eMMC is off and the data lines are pulled up,
* so pull them down. N.B. we pull 8 lines because we are using 8 lines.
*/
-static void rx51_mmc2_remux(struct device *dev, int slot, int power_on)
+static void rx51_mmc2_remux(struct device *dev, int power_on)
{
if (power_on)
omap_mux_write_array(partition, rx51_mmc2_on_mux);
.cover_only = true,
.gpio_cd = 160,
.gpio_wp = -EINVAL,
- .power_saving = true,
},
{
.name = "internal",
.gpio_cd = -EINVAL,
.gpio_wp = -EINVAL,
.nonremovable = true,
- .power_saving = true,
.remux = rx51_mmc2_remux,
},
{} /* Terminator */
* To avoid code running on other OMAPs in
* multi-omap builds
*/
- if (!(cpu_is_omap34xx()))
+ if (!(cpu_is_omap34xx()) || of_have_populated_dt())
return -ENODEV;
snprintf(oh_name, L3_MODULES_MAX_LEN, "l3_main");
}
omap_postcore_initcall(omap3_l3_init);
-static int __init omap4_l3_init(void)
-{
- int i;
- struct omap_hwmod *oh[3];
- struct platform_device *pdev;
- char oh_name[L3_MODULES_MAX_LEN];
-
- /* If dtb is there, the devices will be created dynamically */
- if (of_have_populated_dt())
- return -ENODEV;
-
- /*
- * To avoid code running on other OMAPs in
- * multi-omap builds
- */
- if (!cpu_is_omap44xx() && !soc_is_omap54xx())
- return -ENODEV;
-
- for (i = 0; i < L3_MODULES; i++) {
- snprintf(oh_name, L3_MODULES_MAX_LEN, "l3_main_%d", i+1);
-
- oh[i] = omap_hwmod_lookup(oh_name);
- if (!(oh[i]))
- pr_err("could not look up %s\n", oh_name);
- }
-
- pdev = omap_device_build_ss("omap_l3_noc", 0, oh, 3, NULL, 0);
-
- WARN(IS_ERR(pdev), "could not build omap_device for %s\n", oh_name);
-
- return PTR_RET(pdev);
-}
-omap_postcore_initcall(omap4_l3_init);
-
#if defined(CONFIG_VIDEO_OMAP2) || defined(CONFIG_VIDEO_OMAP2_MODULE)
static struct resource omap2cam_resources[] = {
if (gpmc_nand_data->elm_of_node == NULL)
gpmc_nand_data->elm_of_node =
of_parse_phandle(child, "elm_id", 0);
- if (gpmc_nand_data->elm_of_node == NULL)
- pr_warn("%s: ti,elm-id property not found\n", __func__);
/* select ecc-scheme for NAND */
if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) {
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/gpio.h>
+#include <linux/mmc/host.h>
#include <linux/platform_data/gpio-omap.h>
+#include <linux/platform_data/hsmmc-omap.h>
#include "soc.h"
#include "omap_device.h"
#include "omap-pm.h"
#include "mux.h"
-#include "mmc.h"
#include "hsmmc.h"
#include "control.h"
#define HSMMC_NAME_LEN 9
-#if defined(CONFIG_ARCH_OMAP3) && defined(CONFIG_PM)
-
-static int hsmmc_get_context_loss(struct device *dev)
-{
- return omap_pm_get_dev_context_loss_count(dev);
-}
-
-#else
-#define hsmmc_get_context_loss NULL
-#endif
-
-static void omap_hsmmc1_before_set_reg(struct device *dev, int slot,
- int power_on, int vdd)
+static void omap_hsmmc1_before_set_reg(struct device *dev,
+ int power_on, int vdd)
{
u32 reg, prog_io;
- struct omap_mmc_platform_data *mmc = dev->platform_data;
+ struct omap_hsmmc_platform_data *mmc = dev->platform_data;
- if (mmc->slots[0].remux)
- mmc->slots[0].remux(dev, slot, power_on);
+ if (mmc->remux)
+ mmc->remux(dev, power_on);
/*
* Assume we power both OMAP VMMC1 (for CMD, CLK, DAT0..3) and the
omap_ctrl_writel(reg, OMAP243X_CONTROL_DEVCONF1);
}
- if (mmc->slots[0].internal_clock) {
+ if (mmc->internal_clock) {
reg = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0);
reg |= OMAP2_MMCSDIO1ADPCLKISEL;
omap_ctrl_writel(reg, OMAP2_CONTROL_DEVCONF0);
}
}
-static void omap_hsmmc1_after_set_reg(struct device *dev, int slot,
- int power_on, int vdd)
+static void omap_hsmmc1_after_set_reg(struct device *dev, int power_on, int vdd)
{
u32 reg;
}
}
-static void hsmmc2_select_input_clk_src(struct omap_mmc_platform_data *mmc)
+static void hsmmc2_select_input_clk_src(struct omap_hsmmc_platform_data *mmc)
{
u32 reg;
reg = omap_ctrl_readl(control_devconf1_offset);
- if (mmc->slots[0].internal_clock)
+ if (mmc->internal_clock)
reg |= OMAP2_MMCSDIO2ADPCLKISEL;
else
reg &= ~OMAP2_MMCSDIO2ADPCLKISEL;
omap_ctrl_writel(reg, control_devconf1_offset);
}
-static void hsmmc2_before_set_reg(struct device *dev, int slot,
- int power_on, int vdd)
+static void hsmmc2_before_set_reg(struct device *dev, int power_on, int vdd)
{
- struct omap_mmc_platform_data *mmc = dev->platform_data;
+ struct omap_hsmmc_platform_data *mmc = dev->platform_data;
- if (mmc->slots[0].remux)
- mmc->slots[0].remux(dev, slot, power_on);
+ if (mmc->remux)
+ mmc->remux(dev, power_on);
if (power_on)
hsmmc2_select_input_clk_src(mmc);
}
-static int am35x_hsmmc2_set_power(struct device *dev, int slot,
- int power_on, int vdd)
+static int am35x_hsmmc2_set_power(struct device *dev, int power_on, int vdd)
{
- struct omap_mmc_platform_data *mmc = dev->platform_data;
+ struct omap_hsmmc_platform_data *mmc = dev->platform_data;
if (power_on)
hsmmc2_select_input_clk_src(mmc);
return 0;
}
-static int nop_mmc_set_power(struct device *dev, int slot, int power_on,
- int vdd)
+static int nop_mmc_set_power(struct device *dev, int power_on, int vdd)
{
return 0;
}
-static inline void omap_hsmmc_mux(struct omap_mmc_platform_data *mmc_controller,
- int controller_nr)
+static inline void omap_hsmmc_mux(struct omap_hsmmc_platform_data
+ *mmc_controller, int controller_nr)
{
- if (gpio_is_valid(mmc_controller->slots[0].switch_pin) &&
- (mmc_controller->slots[0].switch_pin < OMAP_MAX_GPIO_LINES))
- omap_mux_init_gpio(mmc_controller->slots[0].switch_pin,
- OMAP_PIN_INPUT_PULLUP);
- if (gpio_is_valid(mmc_controller->slots[0].gpio_wp) &&
- (mmc_controller->slots[0].gpio_wp < OMAP_MAX_GPIO_LINES))
- omap_mux_init_gpio(mmc_controller->slots[0].gpio_wp,
- OMAP_PIN_INPUT_PULLUP);
+ if (gpio_is_valid(mmc_controller->switch_pin) &&
+ (mmc_controller->switch_pin < OMAP_MAX_GPIO_LINES))
+ omap_mux_init_gpio(mmc_controller->switch_pin,
+ OMAP_PIN_INPUT_PULLUP);
+ if (gpio_is_valid(mmc_controller->gpio_wp) &&
+ (mmc_controller->gpio_wp < OMAP_MAX_GPIO_LINES))
+ omap_mux_init_gpio(mmc_controller->gpio_wp,
+ OMAP_PIN_INPUT_PULLUP);
if (cpu_is_omap34xx()) {
if (controller_nr == 0) {
omap_mux_init_signal("sdmmc1_clk",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc1_dat0",
OMAP_PIN_INPUT_PULLUP);
- if (mmc_controller->slots[0].caps &
+ if (mmc_controller->caps &
(MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)) {
omap_mux_init_signal("sdmmc1_dat1",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc1_dat3",
OMAP_PIN_INPUT_PULLUP);
}
- if (mmc_controller->slots[0].caps &
+ if (mmc_controller->caps &
MMC_CAP_8_BIT_DATA) {
omap_mux_init_signal("sdmmc1_dat4",
OMAP_PIN_INPUT_PULLUP);
* For 8 wire configurations, Lines DAT4, 5, 6 and 7
* need to be muxed in the board-*.c files
*/
- if (mmc_controller->slots[0].caps &
+ if (mmc_controller->caps &
(MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)) {
omap_mux_init_signal("sdmmc2_dat1",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc2_dat3",
OMAP_PIN_INPUT_PULLUP);
}
- if (mmc_controller->slots[0].caps &
+ if (mmc_controller->caps &
MMC_CAP_8_BIT_DATA) {
omap_mux_init_signal("sdmmc2_dat4.sdmmc2_dat4",
OMAP_PIN_INPUT_PULLUP);
}
static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c,
- struct omap_mmc_platform_data *mmc)
+ struct omap_hsmmc_platform_data *mmc)
{
char *hc_name;
else
snprintf(hc_name, (HSMMC_NAME_LEN + 1), "mmc%islot%i",
c->mmc, 1);
- mmc->slots[0].name = hc_name;
- mmc->nr_slots = 1;
- mmc->slots[0].caps = c->caps;
- mmc->slots[0].pm_caps = c->pm_caps;
- mmc->slots[0].internal_clock = !c->ext_clock;
- mmc->max_freq = c->max_freq;
+ mmc->name = hc_name;
+ mmc->caps = c->caps;
+ mmc->internal_clock = !c->ext_clock;
mmc->reg_offset = 0;
- mmc->get_context_loss_count = hsmmc_get_context_loss;
- mmc->slots[0].switch_pin = c->gpio_cd;
- mmc->slots[0].gpio_wp = c->gpio_wp;
+ mmc->switch_pin = c->gpio_cd;
+ mmc->gpio_wp = c->gpio_wp;
- mmc->slots[0].remux = c->remux;
- mmc->slots[0].init_card = c->init_card;
+ mmc->remux = c->remux;
+ mmc->init_card = c->init_card;
if (c->cover_only)
- mmc->slots[0].cover = 1;
+ mmc->cover = 1;
if (c->nonremovable)
- mmc->slots[0].nonremovable = 1;
-
- if (c->power_saving)
- mmc->slots[0].power_saving = 1;
-
- if (c->no_off)
- mmc->slots[0].no_off = 1;
-
- if (c->no_off_init)
- mmc->slots[0].no_regulator_off_init = c->no_off_init;
-
- if (c->vcc_aux_disable_is_sleep)
- mmc->slots[0].vcc_aux_disable_is_sleep = 1;
+ mmc->nonremovable = 1;
/*
* NOTE: MMC slots should have a Vcc regulator set up.
* temporary HACK: ocr_mask instead of fixed supply
*/
if (soc_is_am35xx())
- mmc->slots[0].ocr_mask = MMC_VDD_165_195 |
+ mmc->ocr_mask = MMC_VDD_165_195 |
MMC_VDD_26_27 |
MMC_VDD_27_28 |
MMC_VDD_29_30 |
MMC_VDD_30_31 |
MMC_VDD_31_32;
else
- mmc->slots[0].ocr_mask = c->ocr_mask;
+ mmc->ocr_mask = c->ocr_mask;
if (!soc_is_am35xx())
- mmc->slots[0].features |= HSMMC_HAS_PBIAS;
+ mmc->features |= HSMMC_HAS_PBIAS;
switch (c->mmc) {
case 1:
- if (mmc->slots[0].features & HSMMC_HAS_PBIAS) {
+ if (mmc->features & HSMMC_HAS_PBIAS) {
/* on-chip level shifting via PBIAS0/PBIAS1 */
- mmc->slots[0].before_set_reg =
+ mmc->before_set_reg =
omap_hsmmc1_before_set_reg;
- mmc->slots[0].after_set_reg =
+ mmc->after_set_reg =
omap_hsmmc1_after_set_reg;
}
if (soc_is_am35xx())
- mmc->slots[0].set_power = nop_mmc_set_power;
+ mmc->set_power = nop_mmc_set_power;
/* OMAP3630 HSMMC1 supports only 4-bit */
if (cpu_is_omap3630() &&
(c->caps & MMC_CAP_8_BIT_DATA)) {
c->caps &= ~MMC_CAP_8_BIT_DATA;
c->caps |= MMC_CAP_4_BIT_DATA;
- mmc->slots[0].caps = c->caps;
+ mmc->caps = c->caps;
}
break;
case 2:
if (soc_is_am35xx())
- mmc->slots[0].set_power = am35x_hsmmc2_set_power;
+ mmc->set_power = am35x_hsmmc2_set_power;
if (c->ext_clock)
c->transceiver = 1;
c->caps &= ~MMC_CAP_8_BIT_DATA;
c->caps |= MMC_CAP_4_BIT_DATA;
}
- if (mmc->slots[0].features & HSMMC_HAS_PBIAS) {
+ if (mmc->features & HSMMC_HAS_PBIAS) {
/* off-chip level shifting, or none */
- mmc->slots[0].before_set_reg = hsmmc2_before_set_reg;
- mmc->slots[0].after_set_reg = NULL;
+ mmc->before_set_reg = hsmmc2_before_set_reg;
+ mmc->after_set_reg = NULL;
}
break;
case 3:
case 4:
case 5:
- mmc->slots[0].before_set_reg = NULL;
- mmc->slots[0].after_set_reg = NULL;
+ mmc->before_set_reg = NULL;
+ mmc->after_set_reg = NULL;
break;
default:
pr_err("MMC%d configuration not supported!\n", c->mmc);
void omap_hsmmc_late_init(struct omap2_hsmmc_info *c)
{
struct platform_device *pdev;
- struct omap_mmc_platform_data *mmc_pdata;
+ struct omap_hsmmc_platform_data *mmc_pdata;
int res;
if (omap_hsmmc_done != 1)
if (!mmc_pdata)
continue;
- mmc_pdata->slots[0].switch_pin = c->gpio_cd;
- mmc_pdata->slots[0].gpio_wp = c->gpio_wp;
+ mmc_pdata->switch_pin = c->gpio_cd;
+ mmc_pdata->gpio_wp = c->gpio_wp;
res = omap_device_register(pdev);
if (res)
struct omap_device *od;
struct platform_device *pdev;
char oh_name[MAX_OMAP_MMC_HWMOD_NAME_LEN];
- struct omap_mmc_platform_data *mmc_data;
- struct omap_mmc_dev_attr *mmc_dev_attr;
+ struct omap_hsmmc_platform_data *mmc_data;
+ struct omap_hsmmc_dev_attr *mmc_dev_attr;
char *name;
int res;
- mmc_data = kzalloc(sizeof(struct omap_mmc_platform_data), GFP_KERNEL);
+ mmc_data = kzalloc(sizeof(*mmc_data), GFP_KERNEL);
if (!mmc_data) {
pr_err("Cannot allocate memory for mmc device!\n");
return;
}
res = platform_device_add_data(pdev, mmc_data,
- sizeof(struct omap_mmc_platform_data));
+ sizeof(struct omap_hsmmc_platform_data));
if (res) {
pr_err("Could not add pdata for %s\n", name);
goto put_pdev;
platform_device_put(pdev);
free_name:
- kfree(mmc_data->slots[0].name);
+ kfree(mmc_data->name);
free_mmc:
kfree(mmc_data);
u8 mmc; /* controller 1/2/3 */
u32 caps; /* 4/8 wires and any additional host
* capabilities OR'd (ref. linux/mmc/host.h) */
- u32 pm_caps; /* PM capabilities */
bool transceiver; /* MMC-2 option */
bool ext_clock; /* use external pin for input clock */
bool cover_only; /* No card detect - just cover switch */
bool nonremovable; /* Nonremovable e.g. eMMC */
- bool power_saving; /* Try to sleep or power off when possible */
- bool no_off; /* power_saving and power is not to go off */
- bool no_off_init; /* no power off when not in MMC sleep state */
- bool vcc_aux_disable_is_sleep; /* Regulator off remapped to sleep */
bool deferred; /* mmc needs a deferred probe */
int gpio_cd; /* or -EINVAL */
int gpio_wp; /* or -EINVAL */
char *name; /* or NULL for default */
struct platform_device *pdev; /* mmc controller instance */
int ocr_mask; /* temporary HACK */
- int max_freq; /* maximum clock, if constrained by external
- * circuitry, or 0 for default */
/* Remux (pad configuration) when powering on/off */
- void (*remux)(struct device *dev, int slot, int power_on);
+ void (*remux)(struct device *dev, int power_on);
/* init some special card */
void (*init_card)(struct mmc_card *card);
};
-#include <linux/mmc/host.h>
-#include <linux/platform_data/mmc-omap.h>
#define OMAP24XX_NR_MMC 2
#define OMAP2420_MMC_SIZE OMAP1_MMC_SIZE
#define OMAP4_MMC_REG_OFFSET 0x100
-#if defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE)
-void omap242x_init_mmc(struct omap_mmc_platform_data **mmc_data);
-#else
-static inline void omap242x_init_mmc(struct omap_mmc_platform_data **mmc_data)
-{
-}
-#endif
-
struct omap_hwmod;
int omap_msdi_reset(struct omap_hwmod *oh);
#include "soc.h"
#include "iomap.h"
#include "common.h"
-#include "mmc.h"
#include "prminst44xx.h"
#include "prcm_mpu44xx.h"
#include "omap4-sar-layout.h"
static int __init omap_device_late_init(void)
{
bus_for_each_dev(&platform_bus_type, NULL, NULL, omap_device_late_idle);
+
+ WARN(!of_have_populated_dt(),
+ "legacy booting deprecated, please update to boot with .dts\n");
+
return 0;
}
omap_late_initcall_sync(omap_device_late_init);
#include <linux/i2c-omap.h>
#include <linux/platform_data/asoc-ti-mcbsp.h>
+#include <linux/platform_data/hsmmc-omap.h>
#include <linux/platform_data/spi-omap2-mcspi.h>
#include <linux/omap-dma.h>
#include <plat/dmtimer.h>
#include "omap_hwmod.h"
-#include "mmc.h"
#include "l3_2xxx.h"
#include "soc.h"
{ .role = "dbck", .clk = "mmchsdb1_fck" },
};
-static struct omap_mmc_dev_attr mmc1_dev_attr = {
+static struct omap_hsmmc_dev_attr mmc1_dev_attr = {
.flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT,
};
*/
#include <linux/platform_data/gpio-omap.h>
+#include <linux/platform_data/hsmmc-omap.h>
#include <linux/platform_data/spi-omap2-mcspi.h>
#include "omap_hwmod.h"
#include "i2c.h"
-#include "mmc.h"
#include "wd_timer.h"
#include "cm33xx.h"
#include "prm33xx.h"
};
/* mmc0 */
-static struct omap_mmc_dev_attr am33xx_mmc0_dev_attr = {
+static struct omap_hsmmc_dev_attr am33xx_mmc0_dev_attr = {
.flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT,
};
};
/* mmc1 */
-static struct omap_mmc_dev_attr am33xx_mmc1_dev_attr = {
+static struct omap_hsmmc_dev_attr am33xx_mmc1_dev_attr = {
.flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT,
};
};
/* mmc2 */
-static struct omap_mmc_dev_attr am33xx_mmc2_dev_attr = {
+static struct omap_hsmmc_dev_attr am33xx_mmc2_dev_attr = {
.flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT,
};
struct omap_hwmod am33xx_mmc2_hwmod = {
#include "prm33xx.h"
#include "prm-regbits-33xx.h"
#include "i2c.h"
-#include "mmc.h"
#include "wd_timer.h"
#include "omap_hwmod_33xx_43xx_common_data.h"
#include <linux/i2c-omap.h>
#include <linux/power/smartreflex.h>
#include <linux/platform_data/gpio-omap.h>
+#include <linux/platform_data/hsmmc-omap.h>
#include <linux/omap-dma.h>
#include "l3_3xxx.h"
#include "cm-regbits-34xx.h"
#include "i2c.h"
-#include "mmc.h"
#include "wd_timer.h"
#include "serial.h"
{ .role = "dbck", .clk = "omap_32k_fck", },
};
-static struct omap_mmc_dev_attr mmc1_dev_attr = {
+static struct omap_hsmmc_dev_attr mmc1_dev_attr = {
.flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT,
};
/* See 35xx errata 2.1.1.128 in SPRZ278F */
-static struct omap_mmc_dev_attr mmc1_pre_es3_dev_attr = {
+static struct omap_hsmmc_dev_attr mmc1_pre_es3_dev_attr = {
.flags = (OMAP_HSMMC_SUPPORTS_DUAL_VOLT |
OMAP_HSMMC_BROKEN_MULTIBLOCK_READ),
};
};
/* See 35xx errata 2.1.1.128 in SPRZ278F */
-static struct omap_mmc_dev_attr mmc2_pre_es3_dev_attr = {
+static struct omap_hsmmc_dev_attr mmc2_pre_es3_dev_attr = {
.flags = OMAP_HSMMC_BROKEN_MULTIBLOCK_READ,
};
#include <linux/io.h>
#include <linux/platform_data/gpio-omap.h>
+#include <linux/platform_data/hsmmc-omap.h>
#include <linux/power/smartreflex.h>
#include <linux/i2c-omap.h>
#include "prm44xx.h"
#include "prm-regbits-44xx.h"
#include "i2c.h"
-#include "mmc.h"
#include "wd_timer.h"
/* Base offset for all OMAP4 interrupts external to MPUSS */
};
/* mmc1 dev_attr */
-static struct omap_mmc_dev_attr mmc1_dev_attr = {
+static struct omap_hsmmc_dev_attr mmc1_dev_attr = {
.flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT,
};
#include <linux/io.h>
#include <linux/platform_data/gpio-omap.h>
+#include <linux/platform_data/hsmmc-omap.h>
#include <linux/power/smartreflex.h>
#include <linux/i2c-omap.h>
#include "cm2_54xx.h"
#include "prm54xx.h"
#include "i2c.h"
-#include "mmc.h"
#include "wd_timer.h"
/* Base offset for all OMAP5 interrupts external to MPUSS */
};
/* mmc1 dev_attr */
-static struct omap_mmc_dev_attr mmc1_dev_attr = {
+static struct omap_hsmmc_dev_attr mmc1_dev_attr = {
.flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT,
};
#include <linux/io.h>
#include <linux/platform_data/gpio-omap.h>
+#include <linux/platform_data/hsmmc-omap.h>
#include <linux/power/smartreflex.h>
#include <linux/i2c-omap.h>
#include "cm2_7xx.h"
#include "prm7xx.h"
#include "i2c.h"
-#include "mmc.h"
#include "wd_timer.h"
#include "soc.h"
};
/* mmc1 dev_attr */
-static struct omap_mmc_dev_attr mmc1_dev_attr = {
+static struct omap_hsmmc_dev_attr mmc1_dev_attr = {
.flags = OMAP_HSMMC_SUPPORTS_DUAL_VOLT,
};
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/clk.h>
omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2);
- pr_info(KERN_INFO "Waiting for PHY clock good...\n");
+ pr_info("Waiting for PHY clock good...\n");
while (!(omap_ctrl_readl(AM35XX_CONTROL_DEVCONF2)
& CONF2_PHYCLKGD)) {
cpu_relax();
if (time_after(jiffies, timeout)) {
- pr_err(KERN_ERR "musb PHY clock good timed out\n");
+ pr_err("musb PHY clock good timed out\n");
break;
}
}
devconf2 |= CONF2_NO_OVERRIDE;
break;
default:
- pr_info(KERN_INFO "Unsupported mode %u\n", musb_mode);
+ pr_info("Unsupported mode %u\n", musb_mode);
}
omap_ctrl_writel(devconf2, AM35XX_CONTROL_DEVCONF2);
omap_up.dma_rx_timeout = info->dma_rx_timeout;
omap_up.dma_rx_poll_rate = info->dma_rx_poll_rate;
omap_up.autosuspend_timeout = info->autosuspend_timeout;
- omap_up.DTR_gpio = info->DTR_gpio;
- omap_up.DTR_inverted = info->DTR_inverted;
- omap_up.DTR_present = info->DTR_present;
pdata = &omap_up;
pdata_size = sizeof(struct omap_uart_port_info);
#define DMEMC_VIRT IOMEM(0xf6100000)
#define DMEMC_SIZE 0x00100000
+/*
+ * Reserved space for low level debug virtual addresses within
+ * 0xf6200000..0xf6201000
+ */
+
/*
* Internal Memory Controller (PXA27x and later)
*/
MSTP128, MSTP127, MSTP125,
MSTP116, MSTP111, MSTP100, MSTP117,
- MSTP230,
+ MSTP230, MSTP229,
MSTP222,
MSTP218, MSTP217, MSTP216, MSTP214,
MSTP207, MSTP206, MSTP204, MSTP203, MSTP202, MSTP201, MSTP200,
[MSTP127] = SH_CLK_MSTP32(&div4_clks[DIV4_S], SMSTPCR1, 27, 0), /* CEU20 */
[MSTP125] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR1, 25, 0), /* TMU0 */
[MSTP117] = SH_CLK_MSTP32(&div4_clks[DIV4_B], SMSTPCR1, 17, 0), /* LCDC1 */
- [MSTP116] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR1, 16, 0), /* IIC0 */
+ [MSTP116] = SH_CLK_MSTP32(&div4_clks[DIV4_HPP], SMSTPCR1, 16, 0), /* IIC0 */
[MSTP111] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR1, 11, 0), /* TMU1 */
[MSTP100] = SH_CLK_MSTP32(&div4_clks[DIV4_B], SMSTPCR1, 0, 0), /* LCDC0 */
[MSTP230] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR2, 30, 0), /* SCIFA6 */
+ [MSTP229] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR2, 29, 0), /* INTCA */
[MSTP222] = SH_CLK_MSTP32(&div6_clks[DIV6_SUB], SMSTPCR2, 22, 0), /* SCIFA7 */
[MSTP218] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR2, 18, 0), /* DMAC1 */
[MSTP217] = SH_CLK_MSTP32(&div4_clks[DIV4_HP], SMSTPCR2, 17, 0), /* DMAC2 */
CLKDEV_DEV_ID("sh-dma-engine.0", &mstp_clks[MSTP218]),
CLKDEV_DEV_ID("sh-sci.7", &mstp_clks[MSTP222]),
CLKDEV_DEV_ID("e6cd0000.serial", &mstp_clks[MSTP222]),
+ CLKDEV_DEV_ID("renesas_intc_irqpin.0", &mstp_clks[MSTP229]),
+ CLKDEV_DEV_ID("renesas_intc_irqpin.1", &mstp_clks[MSTP229]),
+ CLKDEV_DEV_ID("renesas_intc_irqpin.2", &mstp_clks[MSTP229]),
+ CLKDEV_DEV_ID("renesas_intc_irqpin.3", &mstp_clks[MSTP229]),
CLKDEV_DEV_ID("sh-sci.6", &mstp_clks[MSTP230]),
CLKDEV_DEV_ID("e6cc0000.serial", &mstp_clks[MSTP230]),
#define SDCKCR 0xE6150074
#define SD2CKCR 0xE6150078
-#define SD3CKCR 0xE615007C
+#define SD3CKCR 0xE615026C
#define MMC0CKCR 0xE6150240
#define MMC1CKCR 0xE6150244
#define SSPCKCR 0xE6150248
#include <linux/of_platform.h>
#include <linux/delay.h>
#include <linux/input.h>
+#include <linux/i2c/i2c-sh_mobile.h>
#include <linux/io.h>
#include <linux/serial_sci.h>
#include <linux/sh_dma.h>
},
};
+static struct i2c_sh_mobile_platform_data i2c_platform_data = {
+ .clks_per_count = 2,
+};
+
static struct platform_device i2c0_device = {
.name = "i2c-sh_mobile",
.id = 0,
.resource = i2c0_resources,
.num_resources = ARRAY_SIZE(i2c0_resources),
+ .dev = {
+ .platform_data = &i2c_platform_data,
+ },
};
static struct platform_device i2c1_device = {
.id = 1,
.resource = i2c1_resources,
.num_resources = ARRAY_SIZE(i2c1_resources),
+ .dev = {
+ .platform_data = &i2c_platform_data,
+ },
};
static struct platform_device i2c2_device = {
.id = 2,
.resource = i2c2_resources,
.num_resources = ARRAY_SIZE(i2c2_resources),
+ .dev = {
+ .platform_data = &i2c_platform_data,
+ },
};
static struct platform_device i2c3_device = {
.id = 3,
.resource = i2c3_resources,
.num_resources = ARRAY_SIZE(i2c3_resources),
+ .dev = {
+ .platform_data = &i2c_platform_data,
+ },
};
static struct platform_device i2c4_device = {
.id = 4,
.resource = i2c4_resources,
.num_resources = ARRAY_SIZE(i2c4_resources),
+ .dev = {
+ .platform_data = &i2c_platform_data,
+ },
};
static const struct sh_dmae_slave_config sh73a0_dmae_slaves[] = {
static void tegra_mask(struct irq_data *d)
{
- if (d->irq < FIRST_LEGACY_IRQ)
+ if (d->hwirq < FIRST_LEGACY_IRQ)
return;
- tegra_irq_write_mask(d->irq, ICTLR_CPU_IER_CLR);
+ tegra_irq_write_mask(d->hwirq, ICTLR_CPU_IER_CLR);
}
static void tegra_unmask(struct irq_data *d)
{
- if (d->irq < FIRST_LEGACY_IRQ)
+ if (d->hwirq < FIRST_LEGACY_IRQ)
return;
- tegra_irq_write_mask(d->irq, ICTLR_CPU_IER_SET);
+ tegra_irq_write_mask(d->hwirq, ICTLR_CPU_IER_SET);
}
static void tegra_ack(struct irq_data *d)
{
- if (d->irq < FIRST_LEGACY_IRQ)
+ if (d->hwirq < FIRST_LEGACY_IRQ)
return;
- tegra_irq_write_mask(d->irq, ICTLR_CPU_IEP_FIR_CLR);
+ tegra_irq_write_mask(d->hwirq, ICTLR_CPU_IEP_FIR_CLR);
}
static void tegra_eoi(struct irq_data *d)
{
- if (d->irq < FIRST_LEGACY_IRQ)
+ if (d->hwirq < FIRST_LEGACY_IRQ)
return;
- tegra_irq_write_mask(d->irq, ICTLR_CPU_IEP_FIR_CLR);
+ tegra_irq_write_mask(d->hwirq, ICTLR_CPU_IEP_FIR_CLR);
}
static int tegra_retrigger(struct irq_data *d)
{
- if (d->irq < FIRST_LEGACY_IRQ)
+ if (d->hwirq < FIRST_LEGACY_IRQ)
return 0;
- tegra_irq_write_mask(d->irq, ICTLR_CPU_IEP_FIR_SET);
+ tegra_irq_write_mask(d->hwirq, ICTLR_CPU_IEP_FIR_SET);
return 1;
}
#ifdef CONFIG_PM_SLEEP
static int tegra_set_wake(struct irq_data *d, unsigned int enable)
{
- u32 irq = d->irq;
+ u32 irq = d->hwirq;
u32 index, mask;
if (irq < FIRST_LEGACY_IRQ ||
THUMB( it ne )
bne cpu_resume @ no
+ tegra_get_soc_id TEGRA_APB_MISC_BASE, r6
/* Are we on Tegra20? */
cmp r6, #TEGRA20
beq 1f @ Yes
config KUSER_HELPERS
bool "Enable kuser helpers in vector page" if !NEED_KUSER_HELPERS
+ depends on MMU
default y
help
Warning: disabling this option may break user programs.
/* Auxiliary Debug Modes Control 1 Register */
#define PJ4B_STATIC_BP (1 << 2) /* Enable Static BP */
#define PJ4B_INTER_PARITY (1 << 8) /* Disable Internal Parity Handling */
-#define PJ4B_BCK_OFF_STREX (1 << 5) /* Enable the back off of STREX instr */
#define PJ4B_CLEAN_LINE (1 << 16) /* Disable data transfer for clean line */
/* Auxiliary Debug Modes Control 2 Register */
/* Auxiliary Debug Modes Control 1 Register */
mrc p15, 1, r0, c15, c1, 1
orr r0, r0, #PJ4B_CLEAN_LINE
- orr r0, r0, #PJ4B_BCK_OFF_STREX
orr r0, r0, #PJ4B_INTER_PARITY
bic r0, r0, #PJ4B_STATIC_BP
mcr p15, 1, r0, c15, c1, 1
mrc p15, 0, r5, c15, c1, 0 @ CP access reg
mrc p15, 0, r6, c13, c0, 0 @ PID
mrc p15, 0, r7, c3, c0, 0 @ domain ID
- mrc p15, 0, r8, c1, c1, 0 @ auxiliary control reg
+ mrc p15, 0, r8, c1, c0, 1 @ auxiliary control reg
mrc p15, 0, r9, c1, c0, 0 @ control reg
bic r4, r4, #2 @ clear frequency change bit
stmia r0, {r4 - r9} @ store cp regs
mcr p15, 0, r6, c13, c0, 0 @ PID
mcr p15, 0, r7, c3, c0, 0 @ domain ID
mcr p15, 0, r1, c2, c0, 0 @ translation table base addr
- mcr p15, 0, r8, c1, c1, 0 @ auxiliary control reg
+ mcr p15, 0, r8, c1, c0, 1 @ auxiliary control reg
mov r0, r9 @ control register
b cpu_resume_mmu
ENDPROC(cpu_xscale_do_resume)
#define orion_gpio_dbg_show NULL
#endif
+static void orion_gpio_unmask_irq(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct irq_chip_type *ct = irq_data_get_chip_type(d);
+ u32 reg_val;
+ u32 mask = d->mask;
+
+ irq_gc_lock(gc);
+ reg_val = irq_reg_readl(gc->reg_base + ct->regs.mask);
+ reg_val |= mask;
+ irq_reg_writel(reg_val, gc->reg_base + ct->regs.mask);
+ irq_gc_unlock(gc);
+}
+
+static void orion_gpio_mask_irq(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct irq_chip_type *ct = irq_data_get_chip_type(d);
+ u32 mask = d->mask;
+ u32 reg_val;
+
+ irq_gc_lock(gc);
+ reg_val = irq_reg_readl(gc->reg_base + ct->regs.mask);
+ reg_val &= ~mask;
+ irq_reg_writel(reg_val, gc->reg_base + ct->regs.mask);
+ irq_gc_unlock(gc);
+}
+
void __init orion_gpio_init(struct device_node *np,
int gpio_base, int ngpio,
void __iomem *base, int mask_offset,
ct = gc->chip_types;
ct->regs.mask = ochip->mask_offset + GPIO_LEVEL_MASK_OFF;
ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW;
- ct->chip.irq_mask = irq_gc_mask_clr_bit;
- ct->chip.irq_unmask = irq_gc_mask_set_bit;
+ ct->chip.irq_mask = orion_gpio_mask_irq;
+ ct->chip.irq_unmask = orion_gpio_unmask_irq;
ct->chip.irq_set_type = gpio_irq_set_type;
ct->chip.name = ochip->chip.label;
ct->regs.ack = GPIO_EDGE_CAUSE_OFF;
ct->type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
ct->chip.irq_ack = irq_gc_ack_clr_bit;
- ct->chip.irq_mask = irq_gc_mask_clr_bit;
- ct->chip.irq_unmask = irq_gc_mask_set_bit;
+ ct->chip.irq_mask = orion_gpio_mask_irq;
+ ct->chip.irq_unmask = orion_gpio_unmask_irq;
ct->chip.irq_set_type = gpio_irq_set_type;
ct->handler = handle_edge_irq;
ct->chip.name = ochip->chip.label;
select GENERIC_TIME_VSYSCALL
select HANDLE_DOMAIN_IRQ
select HARDIRQS_SW_RESEND
+ select HAVE_ALIGNED_STRUCT_PAGE if SLUB
select HAVE_ARCH_AUDITSYSCALL
select HAVE_ARCH_JUMP_LABEL
select HAVE_ARCH_KGDB
+ select HAVE_ARCH_SECCOMP_FILTER
select HAVE_ARCH_TRACEHOOK
select HAVE_BPF_JIT
select HAVE_C_RECORDMCOUNT
select HAVE_CC_STACKPROTECTOR
+ select HAVE_CMPXCHG_DOUBLE
select HAVE_DEBUG_BUGVERBOSE
select HAVE_DEBUG_KMEMLEAK
select HAVE_DMA_API_DEBUG
menu "Kernel Features"
+menu "ARM errata workarounds via the alternatives framework"
+
+config ARM64_ERRATUM_826319
+ bool "Cortex-A53: 826319: System might deadlock if a write cannot complete until read data is accepted"
+ default y
+ help
+ This option adds an alternative code sequence to work around ARM
+ erratum 826319 on Cortex-A53 parts up to r0p2 with an AMBA 4 ACE or
+ AXI master interface and an L2 cache.
+
+ If a Cortex-A53 uses an AMBA AXI4 ACE interface to other processors
+ and is unable to accept a certain write via this interface, it will
+ not progress on read data presented on the read data channel and the
+ system can deadlock.
+
+ The workaround promotes data cache clean instructions to
+ data cache clean-and-invalidate.
+ Please note that this does not necessarily enable the workaround,
+ as it depends on the alternative framework, which will only patch
+ the kernel if an affected CPU is detected.
+
+ If unsure, say Y.
+
+config ARM64_ERRATUM_827319
+ bool "Cortex-A53: 827319: Data cache clean instructions might cause overlapping transactions to the interconnect"
+ default y
+ help
+ This option adds an alternative code sequence to work around ARM
+ erratum 827319 on Cortex-A53 parts up to r0p2 with an AMBA 5 CHI
+ master interface and an L2 cache.
+
+ Under certain conditions this erratum can cause a clean line eviction
+ to occur at the same time as another transaction to the same address
+ on the AMBA 5 CHI interface, which can cause data corruption if the
+ interconnect reorders the two transactions.
+
+ The workaround promotes data cache clean instructions to
+ data cache clean-and-invalidate.
+ Please note that this does not necessarily enable the workaround,
+ as it depends on the alternative framework, which will only patch
+ the kernel if an affected CPU is detected.
+
+ If unsure, say Y.
+
+config ARM64_ERRATUM_824069
+ bool "Cortex-A53: 824069: Cache line might not be marked as clean after a CleanShared snoop"
+ default y
+ help
+ This option adds an alternative code sequence to work around ARM
+ erratum 824069 on Cortex-A53 parts up to r0p2 when it is connected
+ to a coherent interconnect.
+
+ If a Cortex-A53 processor is executing a store or prefetch for
+ write instruction at the same time as a processor in another
+ cluster is executing a cache maintenance operation to the same
+ address, then this erratum might cause a clean cache line to be
+ incorrectly marked as dirty.
+
+ The workaround promotes data cache clean instructions to
+ data cache clean-and-invalidate.
+ Please note that this option does not necessarily enable the
+ workaround, as it depends on the alternative framework, which will
+ only patch the kernel if an affected CPU is detected.
+
+ If unsure, say Y.
+
+config ARM64_ERRATUM_819472
+ bool "Cortex-A53: 819472: Store exclusive instructions might cause data corruption"
+ default y
+ help
+ This option adds an alternative code sequence to work around ARM
+ erratum 819472 on Cortex-A53 parts up to r0p1 with an L2 cache
+ present when it is connected to a coherent interconnect.
+
+ If the processor is executing a load and store exclusive sequence at
+ the same time as a processor in another cluster is executing a cache
+ maintenance operation to the same address, then this erratum might
+ cause data corruption.
+
+ The workaround promotes data cache clean instructions to
+ data cache clean-and-invalidate.
+ Please note that this does not necessarily enable the workaround,
+ as it depends on the alternative framework, which will only patch
+ the kernel if an affected CPU is detected.
+
+ If unsure, say Y.
+
+config ARM64_ERRATUM_832075
+ bool "Cortex-A57: 832075: possible deadlock on mixing exclusive memory accesses with device loads"
+ default y
+ help
+ This option adds an alternative code sequence to work around ARM
+ erratum 832075 on Cortex-A57 parts up to r1p2.
+
+ Affected Cortex-A57 parts might deadlock when exclusive load/store
+ instructions to Write-Back memory are mixed with Device loads.
+
+ The workaround is to promote device loads to use Load-Acquire
+ semantics.
+ Please note that this does not necessarily enable the workaround,
+ as it depends on the alternative framework, which will only patch
+ the kernel if an affected CPU is detected.
+
+ If unsure, say Y.
+
+endmenu
+
+
choice
prompt "Page size"
default ARM64_4K_PAGES
source "mm/Kconfig"
+config SECCOMP
+ bool "Enable seccomp to safely compute untrusted bytecode"
+ ---help---
+ This kernel feature is useful for number crunching applications
+ that may need to compute untrusted bytecode during their
+ execution. By using pipes or other transports made available to
+ the process as file descriptors supporting the read/write
+ syscalls, it's possible to isolate those applications in
+ their own address space using seccomp. Once seccomp is
+ enabled via prctl(PR_SET_SECCOMP), it cannot be disabled
+ and the task is only allowed to execute a few safe syscalls
+ defined by each seccomp mode.
+
config XEN_DOM0
def_bool y
depends on XEN
default "14" if (ARM64_64K_PAGES && TRANSPARENT_HUGEPAGE)
default "11"
+menuconfig ARMV8_DEPRECATED
+ bool "Emulate deprecated/obsolete ARMv8 instructions"
+ depends on COMPAT
+ help
+ Legacy software support may require certain instructions
+ that have been deprecated or obsoleted in the architecture.
+
+ Enable this config to enable selective emulation of these
+ features.
+
+ If unsure, say Y
+
+if ARMV8_DEPRECATED
+
+config SWP_EMULATION
+ bool "Emulate SWP/SWPB instructions"
+ help
+ ARMv8 obsoletes the use of A32 SWP/SWPB instructions such that
+ they are always undefined. Say Y here to enable software
+ emulation of these instructions for userspace using LDXR/STXR.
+
+ In some older versions of glibc [<=2.8] SWP is used during futex
+ trylock() operations with the assumption that the code will not
+ be preempted. This invalid assumption may be more likely to fail
+ with SWP emulation enabled, leading to deadlock of the user
+ application.
+
+ NOTE: when accessing uncached shared regions, LDXR/STXR rely
+ on an external transaction monitoring block called a global
+ monitor to maintain update atomicity. If your system does not
+ implement a global monitor, this option can cause programs that
+ perform SWP operations to uncached memory to deadlock.
+
+ If unsure, say Y
+
+config CP15_BARRIER_EMULATION
+ bool "Emulate CP15 Barrier instructions"
+ help
+ The CP15 barrier instructions - CP15ISB, CP15DSB, and
+ CP15DMB - are deprecated in ARMv8 (and ARMv7). It is
+ strongly recommended to use the ISB, DSB, and DMB
+ instructions instead.
+
+ Say Y here to enable software emulation of these
+ instructions for AArch32 userspace code. When this option is
+ enabled, CP15 barrier usage is traced which can help
+ identify software that needs updating.
+
+ If unsure, say Y
+
+endif
+
endmenu
menu "Boot options"
allow the kernel to be booted as an EFI application. This
is only useful on systems that have UEFI firmware.
+config DMI
+ bool "Enable support for SMBIOS (DMI) tables"
+ depends on EFI
+ default y
+ help
+ This enables SMBIOS/DMI feature for systems.
+
+ This option is only useful on systems that have UEFI firmware.
+ However, even with this option, the resultant kernel should
+ continue to boot on existing non-UEFI platforms.
+
endmenu
menu "Userspace binary formats"
bool
default y
+config ARM64_PTDUMP
+ bool "Export kernel pagetable layout to userspace via debugfs"
+ depends on DEBUG_KERNEL
+ select DEBUG_FS
+ help
+ Say Y here if you want to show the kernel pagetable layout in a
+ debugfs file. This information is only useful for kernel developers
+ who are working in architecture specific areas of the kernel.
+ It is probably not a good idea to enable this feature in a production
+ kernel.
+ If in doubt, say "N"
+
config STRICT_DEVMEM
bool "Filter access to /dev/mem"
depends on MMU
compatible = "apm,xgene-enet";
status = "disabled";
reg = <0x0 0x17020000 0x0 0xd100>,
- <0x0 0X17030000 0x0 0X400>,
+ <0x0 0X17030000 0x0 0Xc300>,
<0x0 0X10000000 0x0 0X200>;
reg-names = "enet_csr", "ring_csr", "ring_cmd";
interrupts = <0x0 0x3c 0x4>;
sgenet0: ethernet@1f210000 {
compatible = "apm,xgene-enet";
status = "disabled";
- reg = <0x0 0x1f210000 0x0 0x10000>,
- <0x0 0x1f200000 0x0 0X10000>,
- <0x0 0x1B000000 0x0 0X20000>;
+ reg = <0x0 0x1f210000 0x0 0xd100>,
+ <0x0 0x1f200000 0x0 0Xc300>,
+ <0x0 0x1B000000 0x0 0X200>;
reg-names = "enet_csr", "ring_csr", "ring_cmd";
interrupts = <0x0 0xA0 0x4>;
dma-coherent;
compatible = "apm,xgene-enet";
status = "disabled";
reg = <0x0 0x1f610000 0x0 0xd100>,
- <0x0 0x1f600000 0x0 0X400>,
+ <0x0 0x1f600000 0x0 0Xc300>,
<0x0 0x18000000 0x0 0X200>;
reg-names = "enet_csr", "ring_csr", "ring_cmd";
interrupts = <0x0 0x60 0x4>;
CONFIG_ARCH_THUNDER=y
CONFIG_ARCH_VEXPRESS=y
CONFIG_ARCH_XGENE=y
+CONFIG_PCI=y
+CONFIG_PCI_MSI=y
+CONFIG_PCI_XGENE=y
CONFIG_SMP=y
CONFIG_PREEMPT=y
CONFIG_KSM=y
CONFIG_IP_PNP_BOOTP=y
# CONFIG_INET_LRO is not set
# CONFIG_IPV6 is not set
+CONFIG_BPF_JIT=y
# CONFIG_WIRELESS is not set
CONFIG_NET_9P=y
CONFIG_NET_9P_VIRTIO=y
CONFIG_BLK_DEV_SD=y
# CONFIG_SCSI_LOWLEVEL is not set
CONFIG_ATA=y
+CONFIG_SATA_AHCI=y
+CONFIG_SATA_AHCI_PLATFORM=y
CONFIG_AHCI_XGENE=y
-CONFIG_PHY_XGENE=y
CONFIG_PATA_PLATFORM=y
CONFIG_PATA_OF_PLATFORM=y
CONFIG_NETDEVICES=y
CONFIG_TUN=y
CONFIG_VIRTIO_NET=y
+CONFIG_NET_XGENE=y
CONFIG_SMC91X=y
CONFIG_SMSC911X=y
-CONFIG_NET_XGENE=y
# CONFIG_WLAN is not set
CONFIG_INPUT_EVDEV=y
# CONFIG_SERIO_SERPORT is not set
CONFIG_SERIAL_OF_PLATFORM=y
CONFIG_VIRTIO_CONSOLE=y
# CONFIG_HW_RANDOM is not set
+# CONFIG_HMC_DRV is not set
+CONFIG_SPI=y
+CONFIG_SPI_PL022=y
+CONFIG_GPIO_PL061=y
+CONFIG_GPIO_XGENE=y
# CONFIG_HWMON is not set
CONFIG_REGULATOR=y
CONFIG_REGULATOR_FIXED_VOLTAGE=y
# CONFIG_LOGO_LINUX_MONO is not set
# CONFIG_LOGO_LINUX_VGA16 is not set
CONFIG_USB=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_HCD_PLATFORM=y
CONFIG_USB_ISP1760_HCD=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_OHCI_HCD_PLATFORM=y
CONFIG_USB_STORAGE=y
+CONFIG_USB_ULPI=y
CONFIG_MMC=y
CONFIG_MMC_ARMMMCI=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SPI=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_EFI=y
+CONFIG_RTC_DRV_XGENE=y
CONFIG_VIRTIO_BALLOON=y
CONFIG_VIRTIO_MMIO=y
# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_PHY_XGENE=y
CONFIG_EXT2_FS=y
CONFIG_EXT3_FS=y
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
tristate "AES core cipher using ARMv8 Crypto Extensions"
depends on ARM64 && KERNEL_MODE_NEON
select CRYPTO_ALGAPI
- select CRYPTO_AES
config CRYPTO_AES_ARM64_CE_CCM
tristate "AES in CCM mode using ARMv8 Crypto Extensions"
depends on ARM64 && KERNEL_MODE_NEON
select CRYPTO_ALGAPI
- select CRYPTO_AES
+ select CRYPTO_AES_ARM64_CE
select CRYPTO_AEAD
config CRYPTO_AES_ARM64_CE_BLK
tristate "AES in ECB/CBC/CTR/XTS modes using ARMv8 Crypto Extensions"
depends on ARM64 && KERNEL_MODE_NEON
select CRYPTO_BLKCIPHER
- select CRYPTO_AES
+ select CRYPTO_AES_ARM64_CE
select CRYPTO_ABLK_HELPER
config CRYPTO_AES_ARM64_NEON_BLK
#include <linux/crypto.h>
#include <linux/module.h>
+#include "aes-ce-setkey.h"
+
static int num_rounds(struct crypto_aes_ctx *ctx)
{
/*
struct crypto_aes_ctx *ctx = crypto_aead_ctx(tfm);
int ret;
- ret = crypto_aes_expand_key(ctx, in_key, key_len);
+ ret = ce_aes_expandkey(ctx, in_key, key_len);
if (!ret)
return 0;
#include <linux/crypto.h>
#include <linux/module.h>
+#include "aes-ce-setkey.h"
+
MODULE_DESCRIPTION("Synchronous AES cipher using ARMv8 Crypto Extensions");
MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>");
MODULE_LICENSE("GPL v2");
kernel_neon_end();
}
+/*
+ * aes_sub() - use the aese instruction to perform the AES sbox substitution
+ * on each byte in 'input'
+ */
+static u32 aes_sub(u32 input)
+{
+ u32 ret;
+
+ __asm__("dup v1.4s, %w[in] ;"
+ "movi v0.16b, #0 ;"
+ "aese v0.16b, v1.16b ;"
+ "umov %w[out], v0.4s[0] ;"
+
+ : [out] "=r"(ret)
+ : [in] "r"(input)
+ : "v0","v1");
+
+ return ret;
+}
+
+int ce_aes_expandkey(struct crypto_aes_ctx *ctx, const u8 *in_key,
+ unsigned int key_len)
+{
+ /*
+ * The AES key schedule round constants
+ */
+ static u8 const rcon[] = {
+ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36,
+ };
+
+ u32 kwords = key_len / sizeof(u32);
+ struct aes_block *key_enc, *key_dec;
+ int i, j;
+
+ if (key_len != AES_KEYSIZE_128 &&
+ key_len != AES_KEYSIZE_192 &&
+ key_len != AES_KEYSIZE_256)
+ return -EINVAL;
+
+ memcpy(ctx->key_enc, in_key, key_len);
+ ctx->key_length = key_len;
+
+ kernel_neon_begin_partial(2);
+ for (i = 0; i < sizeof(rcon); i++) {
+ u32 *rki = ctx->key_enc + (i * kwords);
+ u32 *rko = rki + kwords;
+
+ rko[0] = ror32(aes_sub(rki[kwords - 1]), 8) ^ rcon[i] ^ rki[0];
+ rko[1] = rko[0] ^ rki[1];
+ rko[2] = rko[1] ^ rki[2];
+ rko[3] = rko[2] ^ rki[3];
+
+ if (key_len == AES_KEYSIZE_192) {
+ if (i >= 7)
+ break;
+ rko[4] = rko[3] ^ rki[4];
+ rko[5] = rko[4] ^ rki[5];
+ } else if (key_len == AES_KEYSIZE_256) {
+ if (i >= 6)
+ break;
+ rko[4] = aes_sub(rko[3]) ^ rki[4];
+ rko[5] = rko[4] ^ rki[5];
+ rko[6] = rko[5] ^ rki[6];
+ rko[7] = rko[6] ^ rki[7];
+ }
+ }
+
+ /*
+ * Generate the decryption keys for the Equivalent Inverse Cipher.
+ * This involves reversing the order of the round keys, and applying
+ * the Inverse Mix Columns transformation on all but the first and
+ * the last one.
+ */
+ key_enc = (struct aes_block *)ctx->key_enc;
+ key_dec = (struct aes_block *)ctx->key_dec;
+ j = num_rounds(ctx);
+
+ key_dec[0] = key_enc[j];
+ for (i = 1, j--; j > 0; i++, j--)
+ __asm__("ld1 {v0.16b}, %[in] ;"
+ "aesimc v1.16b, v0.16b ;"
+ "st1 {v1.16b}, %[out] ;"
+
+ : [out] "=Q"(key_dec[i])
+ : [in] "Q"(key_enc[j])
+ : "v0","v1");
+ key_dec[i] = key_enc[0];
+
+ kernel_neon_end();
+ return 0;
+}
+EXPORT_SYMBOL(ce_aes_expandkey);
+
+int ce_aes_setkey(struct crypto_tfm *tfm, const u8 *in_key,
+ unsigned int key_len)
+{
+ struct crypto_aes_ctx *ctx = crypto_tfm_ctx(tfm);
+ int ret;
+
+ ret = ce_aes_expandkey(ctx, in_key, key_len);
+ if (!ret)
+ return 0;
+
+ tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+ return -EINVAL;
+}
+EXPORT_SYMBOL(ce_aes_setkey);
+
static struct crypto_alg aes_alg = {
.cra_name = "aes",
.cra_driver_name = "aes-ce",
.cra_cipher = {
.cia_min_keysize = AES_MIN_KEY_SIZE,
.cia_max_keysize = AES_MAX_KEY_SIZE,
- .cia_setkey = crypto_aes_set_key,
+ .cia_setkey = ce_aes_setkey,
.cia_encrypt = aes_cipher_encrypt,
.cia_decrypt = aes_cipher_decrypt
}
--- /dev/null
+
+int ce_aes_setkey(struct crypto_tfm *tfm, const u8 *in_key,
+ unsigned int key_len);
+int ce_aes_expandkey(struct crypto_aes_ctx *ctx, const u8 *in_key,
+ unsigned int key_len);
#include <linux/module.h>
#include <linux/cpufeature.h>
+#include "aes-ce-setkey.h"
+
#ifdef USE_V8_CRYPTO_EXTENSIONS
#define MODE "ce"
#define PRIO 300
+#define aes_setkey ce_aes_setkey
+#define aes_expandkey ce_aes_expandkey
#define aes_ecb_encrypt ce_aes_ecb_encrypt
#define aes_ecb_decrypt ce_aes_ecb_decrypt
#define aes_cbc_encrypt ce_aes_cbc_encrypt
#else
#define MODE "neon"
#define PRIO 200
+#define aes_setkey crypto_aes_set_key
+#define aes_expandkey crypto_aes_expand_key
#define aes_ecb_encrypt neon_aes_ecb_encrypt
#define aes_ecb_decrypt neon_aes_ecb_decrypt
#define aes_cbc_encrypt neon_aes_cbc_encrypt
struct crypto_aes_xts_ctx *ctx = crypto_tfm_ctx(tfm);
int ret;
- ret = crypto_aes_expand_key(&ctx->key1, in_key, key_len / 2);
+ ret = aes_expandkey(&ctx->key1, in_key, key_len / 2);
if (!ret)
- ret = crypto_aes_expand_key(&ctx->key2, &in_key[key_len / 2],
- key_len / 2);
+ ret = aes_expandkey(&ctx->key2, &in_key[key_len / 2],
+ key_len / 2);
if (!ret)
return 0;
.min_keysize = AES_MIN_KEY_SIZE,
.max_keysize = AES_MAX_KEY_SIZE,
.ivsize = AES_BLOCK_SIZE,
- .setkey = crypto_aes_set_key,
+ .setkey = aes_setkey,
.encrypt = ecb_encrypt,
.decrypt = ecb_decrypt,
},
.min_keysize = AES_MIN_KEY_SIZE,
.max_keysize = AES_MAX_KEY_SIZE,
.ivsize = AES_BLOCK_SIZE,
- .setkey = crypto_aes_set_key,
+ .setkey = aes_setkey,
.encrypt = cbc_encrypt,
.decrypt = cbc_decrypt,
},
.min_keysize = AES_MIN_KEY_SIZE,
.max_keysize = AES_MAX_KEY_SIZE,
.ivsize = AES_BLOCK_SIZE,
- .setkey = crypto_aes_set_key,
+ .setkey = aes_setkey,
.encrypt = ctr_encrypt,
.decrypt = ctr_encrypt,
},
--- /dev/null
+#ifndef __ASM_ALTERNATIVE_ASM_H
+#define __ASM_ALTERNATIVE_ASM_H
+
+#ifdef __ASSEMBLY__
+
+.macro altinstruction_entry orig_offset alt_offset feature orig_len alt_len
+ .word \orig_offset - .
+ .word \alt_offset - .
+ .hword \feature
+ .byte \orig_len
+ .byte \alt_len
+.endm
+
+.macro alternative_insn insn1 insn2 cap
+661: \insn1
+662: .pushsection .altinstructions, "a"
+ altinstruction_entry 661b, 663f, \cap, 662b-661b, 664f-663f
+ .popsection
+ .pushsection .altinstr_replacement, "ax"
+663: \insn2
+664: .popsection
+ .if ((664b-663b) != (662b-661b))
+ .error "Alternatives instruction length mismatch"
+ .endif
+.endm
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __ASM_ALTERNATIVE_ASM_H */
--- /dev/null
+#ifndef __ASM_ALTERNATIVE_H
+#define __ASM_ALTERNATIVE_H
+
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/stringify.h>
+
+struct alt_instr {
+ s32 orig_offset; /* offset to original instruction */
+ s32 alt_offset; /* offset to replacement instruction */
+ u16 cpufeature; /* cpufeature bit set for replacement */
+ u8 orig_len; /* size of original instruction(s) */
+ u8 alt_len; /* size of new instruction(s), <= orig_len */
+};
+
+void apply_alternatives_all(void);
+void apply_alternatives(void *start, size_t length);
+void free_alternatives_memory(void);
+
+#define ALTINSTR_ENTRY(feature) \
+ " .word 661b - .\n" /* label */ \
+ " .word 663f - .\n" /* new instruction */ \
+ " .hword " __stringify(feature) "\n" /* feature bit */ \
+ " .byte 662b-661b\n" /* source len */ \
+ " .byte 664f-663f\n" /* replacement len */
+
+/* alternative assembly primitive: */
+#define ALTERNATIVE(oldinstr, newinstr, feature) \
+ "661:\n\t" \
+ oldinstr "\n" \
+ "662:\n" \
+ ".pushsection .altinstructions,\"a\"\n" \
+ ALTINSTR_ENTRY(feature) \
+ ".popsection\n" \
+ ".pushsection .altinstr_replacement, \"a\"\n" \
+ "663:\n\t" \
+ newinstr "\n" \
+ "664:\n\t" \
+ ".popsection\n\t" \
+ ".if ((664b-663b) != (662b-661b))\n\t" \
+ " .error \"Alternatives instruction length mismatch\"\n\t"\
+ ".endif\n"
+
+#endif /* __ASM_ALTERNATIVE_H */
#ifndef __ASSEMBLY__
+#define __read_mostly __attribute__((__section__(".data..read_mostly")))
+
static inline int cache_line_size(void)
{
u32 cwg = cache_type_cwg();
extern void flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end);
extern void flush_icache_range(unsigned long start, unsigned long end);
extern void __flush_dcache_area(void *addr, size_t len);
-extern void __flush_cache_user_range(unsigned long start, unsigned long end);
+extern long __flush_cache_user_range(unsigned long start, unsigned long end);
static inline void flush_cache_mm(struct mm_struct *mm)
{
#define __ASM_CMPXCHG_H
#include <linux/bug.h>
+#include <linux/mmdebug.h>
#include <asm/barrier.h>
return oldval;
}
+#define system_has_cmpxchg_double() 1
+
+static inline int __cmpxchg_double(volatile void *ptr1, volatile void *ptr2,
+ unsigned long old1, unsigned long old2,
+ unsigned long new1, unsigned long new2, int size)
+{
+ unsigned long loop, lost;
+
+ switch (size) {
+ case 8:
+ VM_BUG_ON((unsigned long *)ptr2 - (unsigned long *)ptr1 != 1);
+ do {
+ asm volatile("// __cmpxchg_double8\n"
+ " ldxp %0, %1, %2\n"
+ " eor %0, %0, %3\n"
+ " eor %1, %1, %4\n"
+ " orr %1, %0, %1\n"
+ " mov %w0, #0\n"
+ " cbnz %1, 1f\n"
+ " stxp %w0, %5, %6, %2\n"
+ "1:\n"
+ : "=&r"(loop), "=&r"(lost), "+Q" (*(u64 *)ptr1)
+ : "r" (old1), "r"(old2), "r"(new1), "r"(new2));
+ } while (loop);
+ break;
+ default:
+ BUILD_BUG();
+ }
+
+ return !lost;
+}
+
+static inline int __cmpxchg_double_mb(volatile void *ptr1, volatile void *ptr2,
+ unsigned long old1, unsigned long old2,
+ unsigned long new1, unsigned long new2, int size)
+{
+ int ret;
+
+ smp_mb();
+ ret = __cmpxchg_double(ptr1, ptr2, old1, old2, new1, new2, size);
+ smp_mb();
+
+ return ret;
+}
+
static inline unsigned long __cmpxchg_mb(volatile void *ptr, unsigned long old,
unsigned long new, int size)
{
__ret; \
})
+#define cmpxchg_double(ptr1, ptr2, o1, o2, n1, n2) \
+({\
+ int __ret;\
+ __ret = __cmpxchg_double_mb((ptr1), (ptr2), (unsigned long)(o1), \
+ (unsigned long)(o2), (unsigned long)(n1), \
+ (unsigned long)(n2), sizeof(*(ptr1)));\
+ __ret; \
+})
+
+#define cmpxchg_double_local(ptr1, ptr2, o1, o2, n1, n2) \
+({\
+ int __ret;\
+ __ret = __cmpxchg_double((ptr1), (ptr2), (unsigned long)(o1), \
+ (unsigned long)(o2), (unsigned long)(n1), \
+ (unsigned long)(n2), sizeof(*(ptr1)));\
+ __ret; \
+})
+
+#define this_cpu_cmpxchg_1(ptr, o, n) cmpxchg_local(raw_cpu_ptr(&(ptr)), o, n)
+#define this_cpu_cmpxchg_2(ptr, o, n) cmpxchg_local(raw_cpu_ptr(&(ptr)), o, n)
+#define this_cpu_cmpxchg_4(ptr, o, n) cmpxchg_local(raw_cpu_ptr(&(ptr)), o, n)
+#define this_cpu_cmpxchg_8(ptr, o, n) cmpxchg_local(raw_cpu_ptr(&(ptr)), o, n)
+
+#define this_cpu_cmpxchg_double_8(ptr1, ptr2, o1, o2, n1, n2) \
+ cmpxchg_double_local(raw_cpu_ptr(&(ptr1)), raw_cpu_ptr(&(ptr2)), \
+ o1, o2, n1, n2)
+
#define cmpxchg64(ptr,o,n) cmpxchg((ptr),(o),(n))
#define cmpxchg64_local(ptr,o,n) cmpxchg_local((ptr),(o),(n))
compat_long_t _band; /* POLL_IN, POLL_OUT, POLL_MSG */
int _fd;
} _sigpoll;
+
+ /* SIGSYS */
+ struct {
+ compat_uptr_t _call_addr; /* calling user insn */
+ int _syscall; /* triggering system call number */
+ compat_uint_t _arch; /* AUDIT_ARCH_* of syscall */
+ } _sigsys;
} _sifields;
} compat_siginfo_t;
u32 reg_dczid;
u32 reg_midr;
+ u64 reg_id_aa64dfr0;
+ u64 reg_id_aa64dfr1;
u64 reg_id_aa64isar0;
u64 reg_id_aa64isar1;
u64 reg_id_aa64mmfr0;
#define MAX_CPU_FEATURES (8 * sizeof(elf_hwcap))
#define cpu_feature(x) ilog2(HWCAP_ ## x)
+#define ARM64_WORKAROUND_CLEAN_CACHE 0
+#define ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE 1
+
+#define ARM64_NCAPS 2
+
+#ifndef __ASSEMBLY__
+
+extern DECLARE_BITMAP(cpu_hwcaps, ARM64_NCAPS);
+
static inline bool cpu_have_feature(unsigned int num)
{
return elf_hwcap & (1UL << num);
}
+static inline bool cpus_have_cap(unsigned int num)
+{
+ if (num >= ARM64_NCAPS)
+ return false;
+ return test_bit(num, cpu_hwcaps);
+}
+
+static inline void cpus_set_cap(unsigned int num)
+{
+ if (num >= ARM64_NCAPS)
+ pr_warn("Attempt to set an illegal CPU capability (%d >= %d)\n",
+ num, ARM64_NCAPS);
+ else
+ __set_bit(num, cpu_hwcaps);
+}
+
+void check_local_cpu_errata(void);
+
+#endif /* __ASSEMBLY__ */
+
#endif
#define MIDR_IMPLEMENTOR(midr) \
(((midr) & MIDR_IMPLEMENTOR_MASK) >> MIDR_IMPLEMENTOR_SHIFT)
+#define MIDR_CPU_PART(imp, partnum) \
+ (((imp) << MIDR_IMPLEMENTOR_SHIFT) | \
+ (0xf << MIDR_ARCHITECTURE_SHIFT) | \
+ ((partnum) << MIDR_PARTNUM_SHIFT))
+
#define ARM_CPU_IMP_ARM 0x41
#define ARM_CPU_IMP_APM 0x50
--- /dev/null
+/*
+ * arch/arm64/include/asm/dmi.h
+ *
+ * Copyright (C) 2013 Linaro Limited.
+ * Written by: Yi Li (yi.li@linaro.org)
+ *
+ * based on arch/ia64/include/asm/dmi.h
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef __ASM_DMI_H
+#define __ASM_DMI_H
+
+#include <linux/io.h>
+#include <linux/slab.h>
+
+/*
+ * According to section 2.3.6 of the UEFI spec, the firmware should not
+ * request a virtual mapping for configuration tables such as SMBIOS.
+ * This means we have to map them before use.
+ */
+#define dmi_early_remap(x, l) ioremap_cache(x, l)
+#define dmi_early_unmap(x, l) iounmap(x)
+#define dmi_remap(x, l) ioremap_cache(x, l)
+#define dmi_unmap(x) iounmap(x)
+#define dmi_alloc(l) kzalloc(l, GFP_KERNEL)
+
+#endif
*
*/
enum fixed_addresses {
+ FIX_HOLE,
FIX_EARLYCON_MEM_BASE,
__end_of_permanent_fixed_addresses,
#define FIXMAP_PAGE_IO __pgprot(PROT_DEVICE_nGnRE)
-extern void __early_set_fixmap(enum fixed_addresses idx,
- phys_addr_t phys, pgprot_t flags);
+void __init early_fixmap_init(void);
-#define __set_fixmap __early_set_fixmap
+#define __early_set_fixmap __set_fixmap
+
+extern void __set_fixmap(enum fixed_addresses idx, phys_addr_t phys, pgprot_t prot);
#include <asm-generic/fixmap.h>
#define COMPAT_HWCAP_IDIVA (1 << 17)
#define COMPAT_HWCAP_IDIVT (1 << 18)
#define COMPAT_HWCAP_IDIV (COMPAT_HWCAP_IDIVA|COMPAT_HWCAP_IDIVT)
+#define COMPAT_HWCAP_LPAE (1 << 20)
#define COMPAT_HWCAP_EVTSTRM (1 << 21)
#define COMPAT_HWCAP2_AES (1 << 0)
int aarch64_insn_patch_text_nosync(void *addr, u32 insn);
int aarch64_insn_patch_text_sync(void *addrs[], u32 insns[], int cnt);
int aarch64_insn_patch_text(void *addrs[], u32 insns[], int cnt);
+
+bool aarch32_insn_is_wide(u32 insn);
+
+#define A32_RN_OFFSET 16
+#define A32_RT_OFFSET 12
+#define A32_RT2_OFFSET 0
+
+u32 aarch32_insn_extract_reg_num(u32 insn, int offset);
+u32 aarch32_insn_mcr_extract_opc2(u32 insn);
+u32 aarch32_insn_mcr_extract_crm(u32 insn);
#endif /* __ASSEMBLY__ */
#endif /* __ASM_INSN_H */
#include <asm/barrier.h>
#include <asm/pgtable.h>
#include <asm/early_ioremap.h>
+#include <asm/alternative.h>
+#include <asm/cpufeature.h>
#include <xen/xen.h>
static inline u8 __raw_readb(const volatile void __iomem *addr)
{
u8 val;
- asm volatile("ldrb %w0, [%1]" : "=r" (val) : "r" (addr));
+ asm volatile(ALTERNATIVE("ldrb %w0, [%1]",
+ "ldarb %w0, [%1]",
+ ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE)
+ : "=r" (val) : "r" (addr));
return val;
}
static inline u16 __raw_readw(const volatile void __iomem *addr)
{
u16 val;
- asm volatile("ldrh %w0, [%1]" : "=r" (val) : "r" (addr));
+
+ asm volatile(ALTERNATIVE("ldrh %w0, [%1]",
+ "ldarh %w0, [%1]",
+ ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE)
+ : "=r" (val) : "r" (addr));
return val;
}
static inline u32 __raw_readl(const volatile void __iomem *addr)
{
u32 val;
- asm volatile("ldr %w0, [%1]" : "=r" (val) : "r" (addr));
+ asm volatile(ALTERNATIVE("ldr %w0, [%1]",
+ "ldar %w0, [%1]",
+ ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE)
+ : "=r" (val) : "r" (addr));
return val;
}
static inline u64 __raw_readq(const volatile void __iomem *addr)
{
u64 val;
- asm volatile("ldr %0, [%1]" : "=r" (val) : "r" (addr));
+ asm volatile(ALTERNATIVE("ldr %0, [%1]",
+ "ldar %0, [%1]",
+ ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE)
+ : "=r" (val) : "r" (addr));
return val;
}
#include <asm-generic/irq.h>
-extern void (*handle_arch_irq)(struct pt_regs *);
+struct pt_regs;
+
extern void migrate_irqs(void);
extern void set_handle_irq(void (*handle_irq)(struct pt_regs *));
#ifndef __ARM64_KVM_ARM_H__
#define __ARM64_KVM_ARM_H__
+#include <asm/memory.h>
#include <asm/types.h>
/* Hyp Configuration Register (HCR) bits */
#endif
#define VTTBR_BADDR_SHIFT (VTTBR_X - 1)
-#define VTTBR_BADDR_MASK (((1LLU << (PHYS_MASK_SHIFT - VTTBR_X)) - 1) << VTTBR_BADDR_SHIFT)
-#define VTTBR_VMID_SHIFT (48LLU)
-#define VTTBR_VMID_MASK (0xffLLU << VTTBR_VMID_SHIFT)
+#define VTTBR_BADDR_MASK (((UL(1) << (PHYS_MASK_SHIFT - VTTBR_X)) - 1) << VTTBR_BADDR_SHIFT)
+#define VTTBR_VMID_SHIFT (UL(48))
+#define VTTBR_VMID_MASK (UL(0xFF) << VTTBR_VMID_SHIFT)
/* Hyp System Trap Register */
#define HSTR_EL2_TTEE (1 << 16)
/* Exception Syndrome Register (ESR) bits */
#define ESR_EL2_EC_SHIFT (26)
-#define ESR_EL2_EC (0x3fU << ESR_EL2_EC_SHIFT)
-#define ESR_EL2_IL (1U << 25)
+#define ESR_EL2_EC (UL(0x3f) << ESR_EL2_EC_SHIFT)
+#define ESR_EL2_IL (UL(1) << 25)
#define ESR_EL2_ISS (ESR_EL2_IL - 1)
#define ESR_EL2_ISV_SHIFT (24)
-#define ESR_EL2_ISV (1U << ESR_EL2_ISV_SHIFT)
+#define ESR_EL2_ISV (UL(1) << ESR_EL2_ISV_SHIFT)
#define ESR_EL2_SAS_SHIFT (22)
-#define ESR_EL2_SAS (3U << ESR_EL2_SAS_SHIFT)
+#define ESR_EL2_SAS (UL(3) << ESR_EL2_SAS_SHIFT)
#define ESR_EL2_SSE (1 << 21)
#define ESR_EL2_SRT_SHIFT (16)
#define ESR_EL2_SRT_MASK (0x1f << ESR_EL2_SRT_SHIFT)
#define ESR_EL2_FSC_TYPE (0x3c)
#define ESR_EL2_CV_SHIFT (24)
-#define ESR_EL2_CV (1U << ESR_EL2_CV_SHIFT)
+#define ESR_EL2_CV (UL(1) << ESR_EL2_CV_SHIFT)
#define ESR_EL2_COND_SHIFT (20)
-#define ESR_EL2_COND (0xfU << ESR_EL2_COND_SHIFT)
+#define ESR_EL2_COND (UL(0xf) << ESR_EL2_COND_SHIFT)
#define FSC_FAULT (0x04)
#define FSC_PERM (0x0c)
/* Hyp Prefetch Fault Address Register (HPFAR/HDFAR) */
-#define HPFAR_MASK (~0xFUL)
+#define HPFAR_MASK (~UL(0xf))
#define ESR_EL2_EC_UNKNOWN (0x00)
#define ESR_EL2_EC_WFI (0x01)
* virt_to_page(k) convert a _valid_ virtual address to struct page *
* virt_addr_valid(k) indicates whether a virtual address is valid
*/
-#define ARCH_PFN_OFFSET PHYS_PFN_OFFSET
+#define ARCH_PFN_OFFSET ((unsigned long)PHYS_PFN_OFFSET)
#define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
#define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT)
--- /dev/null
+#include <../../arm/include/asm/opcodes.h>
#endif /* CONFIG_SMP */
+#define PERCPU_OP(op, asm_op) \
+static inline unsigned long __percpu_##op(void *ptr, \
+ unsigned long val, int size) \
+{ \
+ unsigned long loop, ret; \
+ \
+ switch (size) { \
+ case 1: \
+ do { \
+ asm ("//__per_cpu_" #op "_1\n" \
+ "ldxrb %w[ret], %[ptr]\n" \
+ #asm_op " %w[ret], %w[ret], %w[val]\n" \
+ "stxrb %w[loop], %w[ret], %[ptr]\n" \
+ : [loop] "=&r" (loop), [ret] "=&r" (ret), \
+ [ptr] "+Q"(*(u8 *)ptr) \
+ : [val] "Ir" (val)); \
+ } while (loop); \
+ break; \
+ case 2: \
+ do { \
+ asm ("//__per_cpu_" #op "_2\n" \
+ "ldxrh %w[ret], %[ptr]\n" \
+ #asm_op " %w[ret], %w[ret], %w[val]\n" \
+ "stxrh %w[loop], %w[ret], %[ptr]\n" \
+ : [loop] "=&r" (loop), [ret] "=&r" (ret), \
+ [ptr] "+Q"(*(u16 *)ptr) \
+ : [val] "Ir" (val)); \
+ } while (loop); \
+ break; \
+ case 4: \
+ do { \
+ asm ("//__per_cpu_" #op "_4\n" \
+ "ldxr %w[ret], %[ptr]\n" \
+ #asm_op " %w[ret], %w[ret], %w[val]\n" \
+ "stxr %w[loop], %w[ret], %[ptr]\n" \
+ : [loop] "=&r" (loop), [ret] "=&r" (ret), \
+ [ptr] "+Q"(*(u32 *)ptr) \
+ : [val] "Ir" (val)); \
+ } while (loop); \
+ break; \
+ case 8: \
+ do { \
+ asm ("//__per_cpu_" #op "_8\n" \
+ "ldxr %[ret], %[ptr]\n" \
+ #asm_op " %[ret], %[ret], %[val]\n" \
+ "stxr %w[loop], %[ret], %[ptr]\n" \
+ : [loop] "=&r" (loop), [ret] "=&r" (ret), \
+ [ptr] "+Q"(*(u64 *)ptr) \
+ : [val] "Ir" (val)); \
+ } while (loop); \
+ break; \
+ default: \
+ BUILD_BUG(); \
+ } \
+ \
+ return ret; \
+}
+
+PERCPU_OP(add, add)
+PERCPU_OP(and, and)
+PERCPU_OP(or, orr)
+#undef PERCPU_OP
+
+static inline unsigned long __percpu_read(void *ptr, int size)
+{
+ unsigned long ret;
+
+ switch (size) {
+ case 1:
+ ret = ACCESS_ONCE(*(u8 *)ptr);
+ break;
+ case 2:
+ ret = ACCESS_ONCE(*(u16 *)ptr);
+ break;
+ case 4:
+ ret = ACCESS_ONCE(*(u32 *)ptr);
+ break;
+ case 8:
+ ret = ACCESS_ONCE(*(u64 *)ptr);
+ break;
+ default:
+ BUILD_BUG();
+ }
+
+ return ret;
+}
+
+static inline void __percpu_write(void *ptr, unsigned long val, int size)
+{
+ switch (size) {
+ case 1:
+ ACCESS_ONCE(*(u8 *)ptr) = (u8)val;
+ break;
+ case 2:
+ ACCESS_ONCE(*(u16 *)ptr) = (u16)val;
+ break;
+ case 4:
+ ACCESS_ONCE(*(u32 *)ptr) = (u32)val;
+ break;
+ case 8:
+ ACCESS_ONCE(*(u64 *)ptr) = (u64)val;
+ break;
+ default:
+ BUILD_BUG();
+ }
+}
+
+static inline unsigned long __percpu_xchg(void *ptr, unsigned long val,
+ int size)
+{
+ unsigned long ret, loop;
+
+ switch (size) {
+ case 1:
+ do {
+ asm ("//__percpu_xchg_1\n"
+ "ldxrb %w[ret], %[ptr]\n"
+ "stxrb %w[loop], %w[val], %[ptr]\n"
+ : [loop] "=&r"(loop), [ret] "=&r"(ret),
+ [ptr] "+Q"(*(u8 *)ptr)
+ : [val] "r" (val));
+ } while (loop);
+ break;
+ case 2:
+ do {
+ asm ("//__percpu_xchg_2\n"
+ "ldxrh %w[ret], %[ptr]\n"
+ "stxrh %w[loop], %w[val], %[ptr]\n"
+ : [loop] "=&r"(loop), [ret] "=&r"(ret),
+ [ptr] "+Q"(*(u16 *)ptr)
+ : [val] "r" (val));
+ } while (loop);
+ break;
+ case 4:
+ do {
+ asm ("//__percpu_xchg_4\n"
+ "ldxr %w[ret], %[ptr]\n"
+ "stxr %w[loop], %w[val], %[ptr]\n"
+ : [loop] "=&r"(loop), [ret] "=&r"(ret),
+ [ptr] "+Q"(*(u32 *)ptr)
+ : [val] "r" (val));
+ } while (loop);
+ break;
+ case 8:
+ do {
+ asm ("//__percpu_xchg_8\n"
+ "ldxr %[ret], %[ptr]\n"
+ "stxr %w[loop], %[val], %[ptr]\n"
+ : [loop] "=&r"(loop), [ret] "=&r"(ret),
+ [ptr] "+Q"(*(u64 *)ptr)
+ : [val] "r" (val));
+ } while (loop);
+ break;
+ default:
+ BUILD_BUG();
+ }
+
+ return ret;
+}
+
+#define _percpu_add(pcp, val) \
+ __percpu_add(raw_cpu_ptr(&(pcp)), val, sizeof(pcp))
+
+#define _percpu_add_return(pcp, val) (typeof(pcp)) (_percpu_add(pcp, val))
+
+#define _percpu_and(pcp, val) \
+ __percpu_and(raw_cpu_ptr(&(pcp)), val, sizeof(pcp))
+
+#define _percpu_or(pcp, val) \
+ __percpu_or(raw_cpu_ptr(&(pcp)), val, sizeof(pcp))
+
+#define _percpu_read(pcp) (typeof(pcp)) \
+ (__percpu_read(raw_cpu_ptr(&(pcp)), sizeof(pcp)))
+
+#define _percpu_write(pcp, val) \
+ __percpu_write(raw_cpu_ptr(&(pcp)), (unsigned long)(val), sizeof(pcp))
+
+#define _percpu_xchg(pcp, val) (typeof(pcp)) \
+ (__percpu_xchg(raw_cpu_ptr(&(pcp)), (unsigned long)(val), sizeof(pcp)))
+
+#define this_cpu_add_1(pcp, val) _percpu_add(pcp, val)
+#define this_cpu_add_2(pcp, val) _percpu_add(pcp, val)
+#define this_cpu_add_4(pcp, val) _percpu_add(pcp, val)
+#define this_cpu_add_8(pcp, val) _percpu_add(pcp, val)
+
+#define this_cpu_add_return_1(pcp, val) _percpu_add_return(pcp, val)
+#define this_cpu_add_return_2(pcp, val) _percpu_add_return(pcp, val)
+#define this_cpu_add_return_4(pcp, val) _percpu_add_return(pcp, val)
+#define this_cpu_add_return_8(pcp, val) _percpu_add_return(pcp, val)
+
+#define this_cpu_and_1(pcp, val) _percpu_and(pcp, val)
+#define this_cpu_and_2(pcp, val) _percpu_and(pcp, val)
+#define this_cpu_and_4(pcp, val) _percpu_and(pcp, val)
+#define this_cpu_and_8(pcp, val) _percpu_and(pcp, val)
+
+#define this_cpu_or_1(pcp, val) _percpu_or(pcp, val)
+#define this_cpu_or_2(pcp, val) _percpu_or(pcp, val)
+#define this_cpu_or_4(pcp, val) _percpu_or(pcp, val)
+#define this_cpu_or_8(pcp, val) _percpu_or(pcp, val)
+
+#define this_cpu_read_1(pcp) _percpu_read(pcp)
+#define this_cpu_read_2(pcp) _percpu_read(pcp)
+#define this_cpu_read_4(pcp) _percpu_read(pcp)
+#define this_cpu_read_8(pcp) _percpu_read(pcp)
+
+#define this_cpu_write_1(pcp, val) _percpu_write(pcp, val)
+#define this_cpu_write_2(pcp, val) _percpu_write(pcp, val)
+#define this_cpu_write_4(pcp, val) _percpu_write(pcp, val)
+#define this_cpu_write_8(pcp, val) _percpu_write(pcp, val)
+
+#define this_cpu_xchg_1(pcp, val) _percpu_xchg(pcp, val)
+#define this_cpu_xchg_2(pcp, val) _percpu_xchg(pcp, val)
+#define this_cpu_xchg_4(pcp, val) _percpu_xchg(pcp, val)
+#define this_cpu_xchg_8(pcp, val) _percpu_xchg(pcp, val)
+
#include <asm-generic/percpu.h>
#endif /* __ASM_PERCPU_H */
#define check_pgt_cache() do { } while (0)
+#define PGALLOC_GFP (GFP_KERNEL | __GFP_NOTRACK | __GFP_REPEAT | __GFP_ZERO)
+
#if CONFIG_ARM64_PGTABLE_LEVELS > 2
static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr)
{
- return (pmd_t *)get_zeroed_page(GFP_KERNEL | __GFP_REPEAT);
+ return (pmd_t *)__get_free_page(PGALLOC_GFP);
}
static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd)
static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long addr)
{
- return (pud_t *)get_zeroed_page(GFP_KERNEL | __GFP_REPEAT);
+ return (pud_t *)__get_free_page(PGALLOC_GFP);
}
static inline void pud_free(struct mm_struct *mm, pud_t *pud)
extern pgd_t *pgd_alloc(struct mm_struct *mm);
extern void pgd_free(struct mm_struct *mm, pgd_t *pgd);
-#define PGALLOC_GFP (GFP_KERNEL | __GFP_NOTRACK | __GFP_REPEAT | __GFP_ZERO)
-
static inline pte_t *
pte_alloc_one_kernel(struct mm_struct *mm, unsigned long addr)
{
--- /dev/null
+/*
+ * arch/arm64/include/asm/seccomp.h
+ *
+ * Copyright (C) 2014 Linaro Limited
+ * Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef _ASM_SECCOMP_H
+#define _ASM_SECCOMP_H
+
+#include <asm/unistd.h>
+
+#ifdef CONFIG_COMPAT
+#define __NR_seccomp_read_32 __NR_compat_read
+#define __NR_seccomp_write_32 __NR_compat_write
+#define __NR_seccomp_exit_32 __NR_compat_exit
+#define __NR_seccomp_sigreturn_32 __NR_compat_rt_sigreturn
+#endif /* CONFIG_COMPAT */
+
+#include <asm-generic/seccomp.h>
+
+#endif /* _ASM_SECCOMP_H */
#ifndef __ASM_TLB_H
#define __ASM_TLB_H
-#define __tlb_remove_pmd_tlb_entry __tlb_remove_pmd_tlb_entry
-
-#include <asm-generic/tlb.h>
-
#include <linux/pagemap.h>
#include <linux/swap.h>
#define tlb_remove_entry(tlb, entry) tlb_remove_page(tlb, entry)
#endif /* CONFIG_HAVE_RCU_TABLE_FREE */
-/*
- * There's three ways the TLB shootdown code is used:
- * 1. Unmapping a range of vmas. See zap_page_range(), unmap_region().
- * tlb->fullmm = 0, and tlb_start_vma/tlb_end_vma will be called.
- * 2. Unmapping all vmas. See exit_mmap().
- * tlb->fullmm = 1, and tlb_start_vma/tlb_end_vma will be called.
- * Page tables will be freed.
- * 3. Unmapping argument pages. See shift_arg_pages().
- * tlb->fullmm = 0, but tlb_start_vma/tlb_end_vma will not be called.
- */
+#include <asm-generic/tlb.h>
+
static inline void tlb_flush(struct mmu_gather *tlb)
{
if (tlb->fullmm) {
flush_tlb_mm(tlb->mm);
- } else if (tlb->end > 0) {
+ } else {
struct vm_area_struct vma = { .vm_mm = tlb->mm, };
flush_tlb_range(&vma, tlb->start, tlb->end);
- tlb->start = TASK_SIZE;
- tlb->end = 0;
- }
-}
-
-static inline void tlb_add_flush(struct mmu_gather *tlb, unsigned long addr)
-{
- if (!tlb->fullmm) {
- tlb->start = min(tlb->start, addr);
- tlb->end = max(tlb->end, addr + PAGE_SIZE);
- }
-}
-
-/*
- * Memorize the range for the TLB flush.
- */
-static inline void __tlb_remove_tlb_entry(struct mmu_gather *tlb, pte_t *ptep,
- unsigned long addr)
-{
- tlb_add_flush(tlb, addr);
-}
-
-/*
- * In the case of tlb vma handling, we can optimise these away in the
- * case where we're doing a full MM flush. When we're doing a munmap,
- * the vmas are adjusted to only cover the region to be torn down.
- */
-static inline void tlb_start_vma(struct mmu_gather *tlb,
- struct vm_area_struct *vma)
-{
- if (!tlb->fullmm) {
- tlb->start = TASK_SIZE;
- tlb->end = 0;
}
}
-static inline void tlb_end_vma(struct mmu_gather *tlb,
- struct vm_area_struct *vma)
-{
- if (!tlb->fullmm)
- tlb_flush(tlb);
-}
-
static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
unsigned long addr)
{
pgtable_page_dtor(pte);
- tlb_add_flush(tlb, addr);
tlb_remove_entry(tlb, pte);
}
static inline void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmdp,
unsigned long addr)
{
- tlb_add_flush(tlb, addr);
tlb_remove_entry(tlb, virt_to_page(pmdp));
}
#endif
static inline void __pud_free_tlb(struct mmu_gather *tlb, pud_t *pudp,
unsigned long addr)
{
- tlb_add_flush(tlb, addr);
tlb_remove_entry(tlb, virt_to_page(pudp));
}
#endif
-static inline void __tlb_remove_pmd_tlb_entry(struct mmu_gather *tlb, pmd_t *pmdp,
- unsigned long address)
-{
- tlb_add_flush(tlb, address);
-}
-
#endif
#ifndef __ASM_TRAP_H
#define __ASM_TRAP_H
+#include <linux/list.h>
+
+struct pt_regs;
+
+struct undef_hook {
+ struct list_head node;
+ u32 instr_mask;
+ u32 instr_val;
+ u64 pstate_mask;
+ u64 pstate_val;
+ int (*fn)(struct pt_regs *regs, u32 instr);
+};
+
+void register_undef_hook(struct undef_hook *hook);
+void unregister_undef_hook(struct undef_hook *hook);
+
static inline int in_exception_text(unsigned long ptr)
{
extern char __exception_text_start[];
* Compat syscall numbers used by the AArch64 kernel.
*/
#define __NR_compat_restart_syscall 0
+#define __NR_compat_exit 1
+#define __NR_compat_read 3
+#define __NR_compat_write 4
#define __NR_compat_sigreturn 119
#define __NR_compat_rt_sigreturn 173
__SYSCALL(__NR_sched_getattr, sys_sched_getattr)
#define __NR_renameat2 382
__SYSCALL(__NR_renameat2, sys_renameat2)
- /* 383 for seccomp */
+#define __NR_seccomp 383
+__SYSCALL(__NR_seccomp, sys_seccomp)
#define __NR_getrandom 384
__SYSCALL(__NR_getrandom, sys_getrandom)
#define __NR_memfd_create 385
__SYSCALL(__NR_memfd_create, sys_memfd_create)
+#define __NR_bpf 386
+__SYSCALL(__NR_bpf, sys_bpf)
CPPFLAGS_vmlinux.lds := -DTEXT_OFFSET=$(TEXT_OFFSET)
AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
CFLAGS_efi-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
+CFLAGS_armv8_deprecated.o := -I$(src)
CFLAGS_REMOVE_ftrace.o = -pg
CFLAGS_REMOVE_insn.o = -pg
entry-fpsimd.o process.o ptrace.o setup.o signal.o \
sys.o stacktrace.o time.o traps.o io.o vdso.o \
hyp-stub.o psci.o cpu_ops.o insn.o return_address.o \
- cpuinfo.o
+ cpuinfo.o cpu_errata.o alternative.o
arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \
- sys_compat.o
+ sys_compat.o \
+ ../../arm/kernel/opcodes.o
arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o
arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o
arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o topology.o
arm64-obj-$(CONFIG_KGDB) += kgdb.o
arm64-obj-$(CONFIG_EFI) += efi.o efi-stub.o efi-entry.o
arm64-obj-$(CONFIG_PCI) += pci.o
+arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o
obj-y += $(arm64-obj-y) vdso/
obj-m += $(arm64-obj-m)
--- /dev/null
+/*
+ * alternative runtime patching
+ * inspired by the x86 version
+ *
+ * Copyright (C) 2014 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) "alternatives: " fmt
+
+#include <linux/init.h>
+#include <linux/cpu.h>
+#include <asm/cacheflush.h>
+#include <asm/alternative.h>
+#include <asm/cpufeature.h>
+#include <linux/stop_machine.h>
+
+extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
+
+struct alt_region {
+ struct alt_instr *begin;
+ struct alt_instr *end;
+};
+
+static int __apply_alternatives(void *alt_region)
+{
+ struct alt_instr *alt;
+ struct alt_region *region = alt_region;
+ u8 *origptr, *replptr;
+
+ for (alt = region->begin; alt < region->end; alt++) {
+ if (!cpus_have_cap(alt->cpufeature))
+ continue;
+
+ BUG_ON(alt->alt_len > alt->orig_len);
+
+ pr_info_once("patching kernel code\n");
+
+ origptr = (u8 *)&alt->orig_offset + alt->orig_offset;
+ replptr = (u8 *)&alt->alt_offset + alt->alt_offset;
+ memcpy(origptr, replptr, alt->alt_len);
+ flush_icache_range((uintptr_t)origptr,
+ (uintptr_t)(origptr + alt->alt_len));
+ }
+
+ return 0;
+}
+
+void apply_alternatives_all(void)
+{
+ struct alt_region region = {
+ .begin = __alt_instructions,
+ .end = __alt_instructions_end,
+ };
+
+ /* better not try code patching on a live SMP system */
+ stop_machine(__apply_alternatives, ®ion, NULL);
+}
+
+void apply_alternatives(void *start, size_t length)
+{
+ struct alt_region region = {
+ .begin = start,
+ .end = start + length,
+ };
+
+ __apply_alternatives(®ion);
+}
+
+void free_alternatives_memory(void)
+{
+ free_reserved_area(__alt_instructions, __alt_instructions_end,
+ 0, "alternatives");
+}
--- /dev/null
+/*
+ * Copyright (C) 2014 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/cpu.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/perf_event.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/sysctl.h>
+
+#include <asm/insn.h>
+#include <asm/opcodes.h>
+#include <asm/system_misc.h>
+#include <asm/traps.h>
+#include <asm/uaccess.h>
+
+#define CREATE_TRACE_POINTS
+#include "trace-events-emulation.h"
+
+/*
+ * The runtime support for deprecated instruction support can be in one of
+ * following three states -
+ *
+ * 0 = undef
+ * 1 = emulate (software emulation)
+ * 2 = hw (supported in hardware)
+ */
+enum insn_emulation_mode {
+ INSN_UNDEF,
+ INSN_EMULATE,
+ INSN_HW,
+};
+
+enum legacy_insn_status {
+ INSN_DEPRECATED,
+ INSN_OBSOLETE,
+};
+
+struct insn_emulation_ops {
+ const char *name;
+ enum legacy_insn_status status;
+ struct undef_hook *hooks;
+ int (*set_hw_mode)(bool enable);
+};
+
+struct insn_emulation {
+ struct list_head node;
+ struct insn_emulation_ops *ops;
+ int current_mode;
+ int min;
+ int max;
+};
+
+static LIST_HEAD(insn_emulation);
+static int nr_insn_emulated;
+static DEFINE_RAW_SPINLOCK(insn_emulation_lock);
+
+static void register_emulation_hooks(struct insn_emulation_ops *ops)
+{
+ struct undef_hook *hook;
+
+ BUG_ON(!ops->hooks);
+
+ for (hook = ops->hooks; hook->instr_mask; hook++)
+ register_undef_hook(hook);
+
+ pr_notice("Registered %s emulation handler\n", ops->name);
+}
+
+static void remove_emulation_hooks(struct insn_emulation_ops *ops)
+{
+ struct undef_hook *hook;
+
+ BUG_ON(!ops->hooks);
+
+ for (hook = ops->hooks; hook->instr_mask; hook++)
+ unregister_undef_hook(hook);
+
+ pr_notice("Removed %s emulation handler\n", ops->name);
+}
+
+static int update_insn_emulation_mode(struct insn_emulation *insn,
+ enum insn_emulation_mode prev)
+{
+ int ret = 0;
+
+ switch (prev) {
+ case INSN_UNDEF: /* Nothing to be done */
+ break;
+ case INSN_EMULATE:
+ remove_emulation_hooks(insn->ops);
+ break;
+ case INSN_HW:
+ if (insn->ops->set_hw_mode) {
+ insn->ops->set_hw_mode(false);
+ pr_notice("Disabled %s support\n", insn->ops->name);
+ }
+ break;
+ }
+
+ switch (insn->current_mode) {
+ case INSN_UNDEF:
+ break;
+ case INSN_EMULATE:
+ register_emulation_hooks(insn->ops);
+ break;
+ case INSN_HW:
+ if (insn->ops->set_hw_mode && insn->ops->set_hw_mode(true))
+ pr_notice("Enabled %s support\n", insn->ops->name);
+ else
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static void register_insn_emulation(struct insn_emulation_ops *ops)
+{
+ unsigned long flags;
+ struct insn_emulation *insn;
+
+ insn = kzalloc(sizeof(*insn), GFP_KERNEL);
+ insn->ops = ops;
+ insn->min = INSN_UNDEF;
+
+ switch (ops->status) {
+ case INSN_DEPRECATED:
+ insn->current_mode = INSN_EMULATE;
+ insn->max = INSN_HW;
+ break;
+ case INSN_OBSOLETE:
+ insn->current_mode = INSN_UNDEF;
+ insn->max = INSN_EMULATE;
+ break;
+ }
+
+ raw_spin_lock_irqsave(&insn_emulation_lock, flags);
+ list_add(&insn->node, &insn_emulation);
+ nr_insn_emulated++;
+ raw_spin_unlock_irqrestore(&insn_emulation_lock, flags);
+
+ /* Register any handlers if required */
+ update_insn_emulation_mode(insn, INSN_UNDEF);
+}
+
+static int emulation_proc_handler(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp,
+ loff_t *ppos)
+{
+ int ret = 0;
+ struct insn_emulation *insn = (struct insn_emulation *) table->data;
+ enum insn_emulation_mode prev_mode = insn->current_mode;
+
+ table->data = &insn->current_mode;
+ ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
+
+ if (ret || !write || prev_mode == insn->current_mode)
+ goto ret;
+
+ ret = update_insn_emulation_mode(insn, prev_mode);
+ if (ret) {
+ /* Mode change failed, revert to previous mode. */
+ insn->current_mode = prev_mode;
+ update_insn_emulation_mode(insn, INSN_UNDEF);
+ }
+ret:
+ table->data = insn;
+ return ret;
+}
+
+static struct ctl_table ctl_abi[] = {
+ {
+ .procname = "abi",
+ .mode = 0555,
+ },
+ { }
+};
+
+static void register_insn_emulation_sysctl(struct ctl_table *table)
+{
+ unsigned long flags;
+ int i = 0;
+ struct insn_emulation *insn;
+ struct ctl_table *insns_sysctl, *sysctl;
+
+ insns_sysctl = kzalloc(sizeof(*sysctl) * (nr_insn_emulated + 1),
+ GFP_KERNEL);
+
+ raw_spin_lock_irqsave(&insn_emulation_lock, flags);
+ list_for_each_entry(insn, &insn_emulation, node) {
+ sysctl = &insns_sysctl[i];
+
+ sysctl->mode = 0644;
+ sysctl->maxlen = sizeof(int);
+
+ sysctl->procname = insn->ops->name;
+ sysctl->data = insn;
+ sysctl->extra1 = &insn->min;
+ sysctl->extra2 = &insn->max;
+ sysctl->proc_handler = emulation_proc_handler;
+ i++;
+ }
+ raw_spin_unlock_irqrestore(&insn_emulation_lock, flags);
+
+ table->child = insns_sysctl;
+ register_sysctl_table(table);
+}
+
+/*
+ * Implement emulation of the SWP/SWPB instructions using load-exclusive and
+ * store-exclusive.
+ *
+ * Syntax of SWP{B} instruction: SWP{B}<c> <Rt>, <Rt2>, [<Rn>]
+ * Where: Rt = destination
+ * Rt2 = source
+ * Rn = address
+ */
+
+/*
+ * Error-checking SWP macros implemented using ldxr{b}/stxr{b}
+ */
+#define __user_swpX_asm(data, addr, res, temp, B) \
+ __asm__ __volatile__( \
+ " mov %w2, %w1\n" \
+ "0: ldxr"B" %w1, [%3]\n" \
+ "1: stxr"B" %w0, %w2, [%3]\n" \
+ " cbz %w0, 2f\n" \
+ " mov %w0, %w4\n" \
+ "2:\n" \
+ " .pushsection .fixup,\"ax\"\n" \
+ " .align 2\n" \
+ "3: mov %w0, %w5\n" \
+ " b 2b\n" \
+ " .popsection" \
+ " .pushsection __ex_table,\"a\"\n" \
+ " .align 3\n" \
+ " .quad 0b, 3b\n" \
+ " .quad 1b, 3b\n" \
+ " .popsection" \
+ : "=&r" (res), "+r" (data), "=&r" (temp) \
+ : "r" (addr), "i" (-EAGAIN), "i" (-EFAULT) \
+ : "memory")
+
+#define __user_swp_asm(data, addr, res, temp) \
+ __user_swpX_asm(data, addr, res, temp, "")
+#define __user_swpb_asm(data, addr, res, temp) \
+ __user_swpX_asm(data, addr, res, temp, "b")
+
+/*
+ * Bit 22 of the instruction encoding distinguishes between
+ * the SWP and SWPB variants (bit set means SWPB).
+ */
+#define TYPE_SWPB (1 << 22)
+
+/*
+ * Set up process info to signal segmentation fault - called on access error.
+ */
+static void set_segfault(struct pt_regs *regs, unsigned long addr)
+{
+ siginfo_t info;
+
+ down_read(¤t->mm->mmap_sem);
+ if (find_vma(current->mm, addr) == NULL)
+ info.si_code = SEGV_MAPERR;
+ else
+ info.si_code = SEGV_ACCERR;
+ up_read(¤t->mm->mmap_sem);
+
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ info.si_addr = (void *) instruction_pointer(regs);
+
+ pr_debug("SWP{B} emulation: access caused memory abort!\n");
+ arm64_notify_die("Illegal memory access", regs, &info, 0);
+}
+
+static int emulate_swpX(unsigned int address, unsigned int *data,
+ unsigned int type)
+{
+ unsigned int res = 0;
+
+ if ((type != TYPE_SWPB) && (address & 0x3)) {
+ /* SWP to unaligned address not permitted */
+ pr_debug("SWP instruction on unaligned pointer!\n");
+ return -EFAULT;
+ }
+
+ while (1) {
+ unsigned long temp;
+
+ if (type == TYPE_SWPB)
+ __user_swpb_asm(*data, address, res, temp);
+ else
+ __user_swp_asm(*data, address, res, temp);
+
+ if (likely(res != -EAGAIN) || signal_pending(current))
+ break;
+
+ cond_resched();
+ }
+
+ return res;
+}
+
+/*
+ * swp_handler logs the id of calling process, dissects the instruction, sanity
+ * checks the memory location, calls emulate_swpX for the actual operation and
+ * deals with fixup/error handling before returning
+ */
+static int swp_handler(struct pt_regs *regs, u32 instr)
+{
+ u32 destreg, data, type, address = 0;
+ int rn, rt2, res = 0;
+
+ perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->pc);
+
+ type = instr & TYPE_SWPB;
+
+ switch (arm_check_condition(instr, regs->pstate)) {
+ case ARM_OPCODE_CONDTEST_PASS:
+ break;
+ case ARM_OPCODE_CONDTEST_FAIL:
+ /* Condition failed - return to next instruction */
+ goto ret;
+ case ARM_OPCODE_CONDTEST_UNCOND:
+ /* If unconditional encoding - not a SWP, undef */
+ return -EFAULT;
+ default:
+ return -EINVAL;
+ }
+
+ rn = aarch32_insn_extract_reg_num(instr, A32_RN_OFFSET);
+ rt2 = aarch32_insn_extract_reg_num(instr, A32_RT2_OFFSET);
+
+ address = (u32)regs->user_regs.regs[rn];
+ data = (u32)regs->user_regs.regs[rt2];
+ destreg = aarch32_insn_extract_reg_num(instr, A32_RT_OFFSET);
+
+ pr_debug("addr in r%d->0x%08x, dest is r%d, source in r%d->0x%08x)\n",
+ rn, address, destreg,
+ aarch32_insn_extract_reg_num(instr, A32_RT2_OFFSET), data);
+
+ /* Check access in reasonable access range for both SWP and SWPB */
+ if (!access_ok(VERIFY_WRITE, (address & ~3), 4)) {
+ pr_debug("SWP{B} emulation: access to 0x%08x not allowed!\n",
+ address);
+ goto fault;
+ }
+
+ res = emulate_swpX(address, &data, type);
+ if (res == -EFAULT)
+ goto fault;
+ else if (res == 0)
+ regs->user_regs.regs[destreg] = data;
+
+ret:
+ if (type == TYPE_SWPB)
+ trace_instruction_emulation("swpb", regs->pc);
+ else
+ trace_instruction_emulation("swp", regs->pc);
+
+ pr_warn_ratelimited("\"%s\" (%ld) uses obsolete SWP{B} instruction at 0x%llx\n",
+ current->comm, (unsigned long)current->pid, regs->pc);
+
+ regs->pc += 4;
+ return 0;
+
+fault:
+ set_segfault(regs, address);
+
+ return 0;
+}
+
+/*
+ * Only emulate SWP/SWPB executed in ARM state/User mode.
+ * The kernel must be SWP free and SWP{B} does not exist in Thumb.
+ */
+static struct undef_hook swp_hooks[] = {
+ {
+ .instr_mask = 0x0fb00ff0,
+ .instr_val = 0x01000090,
+ .pstate_mask = COMPAT_PSR_MODE_MASK,
+ .pstate_val = COMPAT_PSR_MODE_USR,
+ .fn = swp_handler
+ },
+ { }
+};
+
+static struct insn_emulation_ops swp_ops = {
+ .name = "swp",
+ .status = INSN_OBSOLETE,
+ .hooks = swp_hooks,
+ .set_hw_mode = NULL,
+};
+
+static int cp15barrier_handler(struct pt_regs *regs, u32 instr)
+{
+ perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->pc);
+
+ switch (arm_check_condition(instr, regs->pstate)) {
+ case ARM_OPCODE_CONDTEST_PASS:
+ break;
+ case ARM_OPCODE_CONDTEST_FAIL:
+ /* Condition failed - return to next instruction */
+ goto ret;
+ case ARM_OPCODE_CONDTEST_UNCOND:
+ /* If unconditional encoding - not a barrier instruction */
+ return -EFAULT;
+ default:
+ return -EINVAL;
+ }
+
+ switch (aarch32_insn_mcr_extract_crm(instr)) {
+ case 10:
+ /*
+ * dmb - mcr p15, 0, Rt, c7, c10, 5
+ * dsb - mcr p15, 0, Rt, c7, c10, 4
+ */
+ if (aarch32_insn_mcr_extract_opc2(instr) == 5) {
+ dmb(sy);
+ trace_instruction_emulation(
+ "mcr p15, 0, Rt, c7, c10, 5 ; dmb", regs->pc);
+ } else {
+ dsb(sy);
+ trace_instruction_emulation(
+ "mcr p15, 0, Rt, c7, c10, 4 ; dsb", regs->pc);
+ }
+ break;
+ case 5:
+ /*
+ * isb - mcr p15, 0, Rt, c7, c5, 4
+ *
+ * Taking an exception or returning from one acts as an
+ * instruction barrier. So no explicit barrier needed here.
+ */
+ trace_instruction_emulation(
+ "mcr p15, 0, Rt, c7, c5, 4 ; isb", regs->pc);
+ break;
+ }
+
+ret:
+ pr_warn_ratelimited("\"%s\" (%ld) uses deprecated CP15 Barrier instruction at 0x%llx\n",
+ current->comm, (unsigned long)current->pid, regs->pc);
+
+ regs->pc += 4;
+ return 0;
+}
+
+#define SCTLR_EL1_CP15BEN (1 << 5)
+
+static inline void config_sctlr_el1(u32 clear, u32 set)
+{
+ u32 val;
+
+ asm volatile("mrs %0, sctlr_el1" : "=r" (val));
+ val &= ~clear;
+ val |= set;
+ asm volatile("msr sctlr_el1, %0" : : "r" (val));
+}
+
+static void enable_cp15_ben(void *info)
+{
+ config_sctlr_el1(0, SCTLR_EL1_CP15BEN);
+}
+
+static void disable_cp15_ben(void *info)
+{
+ config_sctlr_el1(SCTLR_EL1_CP15BEN, 0);
+}
+
+static int cpu_hotplug_notify(struct notifier_block *b,
+ unsigned long action, void *hcpu)
+{
+ switch (action) {
+ case CPU_STARTING:
+ case CPU_STARTING_FROZEN:
+ enable_cp15_ben(NULL);
+ return NOTIFY_DONE;
+ case CPU_DYING:
+ case CPU_DYING_FROZEN:
+ disable_cp15_ben(NULL);
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block cpu_hotplug_notifier = {
+ .notifier_call = cpu_hotplug_notify,
+};
+
+static int cp15_barrier_set_hw_mode(bool enable)
+{
+ if (enable) {
+ register_cpu_notifier(&cpu_hotplug_notifier);
+ on_each_cpu(enable_cp15_ben, NULL, true);
+ } else {
+ unregister_cpu_notifier(&cpu_hotplug_notifier);
+ on_each_cpu(disable_cp15_ben, NULL, true);
+ }
+
+ return true;
+}
+
+static struct undef_hook cp15_barrier_hooks[] = {
+ {
+ .instr_mask = 0x0fff0fdf,
+ .instr_val = 0x0e070f9a,
+ .pstate_mask = COMPAT_PSR_MODE_MASK,
+ .pstate_val = COMPAT_PSR_MODE_USR,
+ .fn = cp15barrier_handler,
+ },
+ {
+ .instr_mask = 0x0fff0fff,
+ .instr_val = 0x0e070f95,
+ .pstate_mask = COMPAT_PSR_MODE_MASK,
+ .pstate_val = COMPAT_PSR_MODE_USR,
+ .fn = cp15barrier_handler,
+ },
+ { }
+};
+
+static struct insn_emulation_ops cp15_barrier_ops = {
+ .name = "cp15_barrier",
+ .status = INSN_DEPRECATED,
+ .hooks = cp15_barrier_hooks,
+ .set_hw_mode = cp15_barrier_set_hw_mode,
+};
+
+/*
+ * Invoked as late_initcall, since not needed before init spawned.
+ */
+static int __init armv8_deprecated_init(void)
+{
+ if (IS_ENABLED(CONFIG_SWP_EMULATION))
+ register_insn_emulation(&swp_ops);
+
+ if (IS_ENABLED(CONFIG_CP15_BARRIER_EMULATION))
+ register_insn_emulation(&cp15_barrier_ops);
+
+ register_insn_emulation_sysctl(ctl_abi);
+
+ return 0;
+}
+
+late_initcall(armv8_deprecated_init);
--- /dev/null
+/*
+ * Contains CPU specific errata definitions
+ *
+ * Copyright (C) 2014 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) "alternatives: " fmt
+
+#include <linux/types.h>
+#include <asm/cpu.h>
+#include <asm/cputype.h>
+#include <asm/cpufeature.h>
+
+#define MIDR_CORTEX_A53 MIDR_CPU_PART(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A53)
+#define MIDR_CORTEX_A57 MIDR_CPU_PART(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A57)
+
+/*
+ * Add a struct or another datatype to the union below if you need
+ * different means to detect an affected CPU.
+ */
+struct arm64_cpu_capabilities {
+ const char *desc;
+ u16 capability;
+ bool (*is_affected)(struct arm64_cpu_capabilities *);
+ union {
+ struct {
+ u32 midr_model;
+ u32 midr_range_min, midr_range_max;
+ };
+ };
+};
+
+#define CPU_MODEL_MASK (MIDR_IMPLEMENTOR_MASK | MIDR_PARTNUM_MASK | \
+ MIDR_ARCHITECTURE_MASK)
+
+static bool __maybe_unused
+is_affected_midr_range(struct arm64_cpu_capabilities *entry)
+{
+ u32 midr = read_cpuid_id();
+
+ if ((midr & CPU_MODEL_MASK) != entry->midr_model)
+ return false;
+
+ midr &= MIDR_REVISION_MASK | MIDR_VARIANT_MASK;
+
+ return (midr >= entry->midr_range_min && midr <= entry->midr_range_max);
+}
+
+#define MIDR_RANGE(model, min, max) \
+ .is_affected = is_affected_midr_range, \
+ .midr_model = model, \
+ .midr_range_min = min, \
+ .midr_range_max = max
+
+struct arm64_cpu_capabilities arm64_errata[] = {
+#if defined(CONFIG_ARM64_ERRATUM_826319) || \
+ defined(CONFIG_ARM64_ERRATUM_827319) || \
+ defined(CONFIG_ARM64_ERRATUM_824069)
+ {
+ /* Cortex-A53 r0p[012] */
+ .desc = "ARM errata 826319, 827319, 824069",
+ .capability = ARM64_WORKAROUND_CLEAN_CACHE,
+ MIDR_RANGE(MIDR_CORTEX_A53, 0x00, 0x02),
+ },
+#endif
+#ifdef CONFIG_ARM64_ERRATUM_819472
+ {
+ /* Cortex-A53 r0p[01] */
+ .desc = "ARM errata 819472",
+ .capability = ARM64_WORKAROUND_CLEAN_CACHE,
+ MIDR_RANGE(MIDR_CORTEX_A53, 0x00, 0x01),
+ },
+#endif
+#ifdef CONFIG_ARM64_ERRATUM_832075
+ {
+ /* Cortex-A57 r0p0 - r1p2 */
+ .desc = "ARM erratum 832075",
+ .capability = ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE,
+ MIDR_RANGE(MIDR_CORTEX_A57, 0x00, 0x12),
+ },
+#endif
+ {
+ }
+};
+
+void check_local_cpu_errata(void)
+{
+ struct arm64_cpu_capabilities *cpus = arm64_errata;
+ int i;
+
+ for (i = 0; cpus[i].desc; i++) {
+ if (!cpus[i].is_affected(&cpus[i]))
+ continue;
+
+ if (!cpus_have_cap(cpus[i].capability))
+ pr_info("enabling workaround for %s\n", cpus[i].desc);
+ cpus_set_cap(cpus[i].capability);
+ }
+}
#include <asm/cachetype.h>
#include <asm/cpu.h>
#include <asm/cputype.h>
+#include <asm/cpufeature.h>
#include <linux/bitops.h>
#include <linux/bug.h>
/* If different, timekeeping will be broken (especially with KVM) */
diff |= CHECK(cntfrq, boot, cur, cpu);
+ /*
+ * The kernel uses self-hosted debug features and expects CPUs to
+ * support identical debug features. We presently need CTX_CMPs, WRPs,
+ * and BRPs to be identical.
+ * ID_AA64DFR1 is currently RES0.
+ */
+ diff |= CHECK(id_aa64dfr0, boot, cur, cpu);
+ diff |= CHECK(id_aa64dfr1, boot, cur, cpu);
+
/*
* Even in big.LITTLE, processors should be identical instruction-set
* wise.
diff |= CHECK(id_isar3, boot, cur, cpu);
diff |= CHECK(id_isar4, boot, cur, cpu);
diff |= CHECK(id_isar5, boot, cur, cpu);
- diff |= CHECK(id_mmfr0, boot, cur, cpu);
+ /*
+ * Regardless of the value of the AuxReg field, the AIFSR, ADFSR, and
+ * ACTLR formats could differ across CPUs and therefore would have to
+ * be trapped for virtualization anyway.
+ */
+ diff |= CHECK_MASK(id_mmfr0, 0xff0fffff, boot, cur, cpu);
diff |= CHECK(id_mmfr1, boot, cur, cpu);
diff |= CHECK(id_mmfr2, boot, cur, cpu);
diff |= CHECK(id_mmfr3, boot, cur, cpu);
* pretend to support them.
*/
WARN_TAINT_ONCE(diff, TAINT_CPU_OUT_OF_SPEC,
- "Unsupported CPU feature variation.");
+ "Unsupported CPU feature variation.\n");
}
static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info)
info->reg_dczid = read_cpuid(DCZID_EL0);
info->reg_midr = read_cpuid_id();
+ info->reg_id_aa64dfr0 = read_cpuid(ID_AA64DFR0_EL1);
+ info->reg_id_aa64dfr1 = read_cpuid(ID_AA64DFR1_EL1);
info->reg_id_aa64isar0 = read_cpuid(ID_AA64ISAR0_EL1);
info->reg_id_aa64isar1 = read_cpuid(ID_AA64ISAR1_EL1);
info->reg_id_aa64mmfr0 = read_cpuid(ID_AA64MMFR0_EL1);
info->reg_id_pfr1 = read_cpuid(ID_PFR1_EL1);
cpuinfo_detect_icache_policy(info);
+
+ check_local_cpu_errata();
}
void cpuinfo_store_cpu(void)
b.eq efi_load_fail
/*
- * efi_entry() will have relocated the kernel image if necessary
- * and we return here with device tree address in x0 and the kernel
- * entry point stored at *image_addr. Save those values in registers
- * which are callee preserved.
+ * efi_entry() will have copied the kernel image if necessary and we
+ * return here with device tree address in x0 and the kernel entry
+ * point stored at *image_addr. Save those values in registers which
+ * are callee preserved.
*/
mov x20, x0 // DTB address
ldr x0, [sp, #16] // relocated _text address
- mov x21, x0
+ ldr x21, =stext_offset
+ add x21, x0, x21
/*
- * Flush dcache covering current runtime addresses
- * of kernel text/data. Then flush all of icache.
+ * Calculate size of the kernel Image (same for original and copy).
*/
adrp x1, _text
add x1, x1, #:lo12:_text
add x2, x2, #:lo12:_edata
sub x1, x2, x1
+ /*
+ * Flush the copied Image to the PoC, and ensure it is not shadowed by
+ * stale icache entries from before relocation.
+ */
bl __flush_dcache_area
ic ialluis
+ /*
+ * Ensure that the rest of this function (in the original Image) is
+ * visible when the caches are disabled. The I-cache can't have stale
+ * entries for the VA range of the current image, so no maintenance is
+ * necessary.
+ */
+ adr x0, efi_stub_entry
+ adr x1, efi_stub_entry_end
+ sub x1, x1, x0
+ bl __flush_dcache_area
+
/* Turn off Dcache and MMU */
mrs x0, CurrentEL
cmp x0, #CurrentEL_EL2
ldp x29, x30, [sp], #32
ret
+efi_stub_entry_end:
ENDPROC(efi_stub_entry)
*
*/
+#include <linux/dmi.h>
#include <linux/efi.h>
#include <linux/export.h>
#include <linux/memblock.h>
efi.systab->hdr.revision & 0xffff, vendor);
retval = efi_config_init(NULL);
- if (retval == 0)
- set_bit(EFI_CONFIG_TABLES, &efi.flags);
out:
early_memunmap(efi.systab, sizeof(efi_system_table_t));
*/
static __init int is_reserve_region(efi_memory_desc_t *md)
{
- if (!is_normal_ram(md))
+ switch (md->type) {
+ case EFI_LOADER_CODE:
+ case EFI_LOADER_DATA:
+ case EFI_BOOT_SERVICES_CODE:
+ case EFI_BOOT_SERVICES_DATA:
+ case EFI_CONVENTIONAL_MEMORY:
return 0;
-
- if (md->attribute & EFI_MEMORY_RUNTIME)
- return 1;
-
- if (md->type == EFI_ACPI_RECLAIM_MEMORY ||
- md->type == EFI_RESERVED_TYPE)
- return 1;
-
- return 0;
+ default:
+ break;
+ }
+ return is_normal_ram(md);
}
static __init void reserve_regions(void)
return -1;
}
early_initcall(arm64_enter_virtual_mode);
+
+static int __init arm64_dmi_init(void)
+{
+ /*
+ * On arm64, DMI depends on UEFI, and dmi_scan_machine() needs to
+ * be called early because dmi_id_init(), which is an arch_initcall
+ * itself, depends on dmi_scan_machine() having been called already.
+ */
+ dmi_scan_machine();
+ if (dmi_available)
+ dmi_set_dump_stack_arch_desc();
+ return 0;
+}
+core_initcall(arm64_dmi_init);
ENTRY(_mcount)
mcount_enter
- ldr x0, =ftrace_trace_function
- ldr x2, [x0]
+ adrp x0, ftrace_trace_function
+ ldr x2, [x0, #:lo12:ftrace_trace_function]
adr x0, ftrace_stub
cmp x0, x2 // if (ftrace_trace_function
b.eq skip_ftrace_call // != ftrace_stub) {
mcount_exit // return;
// }
skip_ftrace_call:
- ldr x1, =ftrace_graph_return
- ldr x2, [x1] // if ((ftrace_graph_return
- cmp x0, x2 // != ftrace_stub)
- b.ne ftrace_graph_caller
-
- ldr x1, =ftrace_graph_entry // || (ftrace_graph_entry
- ldr x2, [x1] // != ftrace_graph_entry_stub))
- ldr x0, =ftrace_graph_entry_stub
+ adrp x1, ftrace_graph_return
+ ldr x2, [x1, #:lo12:ftrace_graph_return]
+ cmp x0, x2 // if ((ftrace_graph_return
+ b.ne ftrace_graph_caller // != ftrace_stub)
+
+ adrp x1, ftrace_graph_entry // || (ftrace_graph_entry
+ adrp x0, ftrace_graph_entry_stub // != ftrace_graph_entry_stub))
+ ldr x2, [x1, #:lo12:ftrace_graph_entry]
+ add x0, x0, #:lo12:ftrace_graph_entry_stub
cmp x0, x2
b.ne ftrace_graph_caller // ftrace_graph_caller();
#define BAD_ERROR 3
.macro kernel_entry, el, regsize = 64
- sub sp, sp, #S_FRAME_SIZE - S_LR // room for LR, SP, SPSR, ELR
+ sub sp, sp, #S_FRAME_SIZE
.if \regsize == 32
mov w0, w0 // zero upper 32 bits of x0
.endif
- push x28, x29
- push x26, x27
- push x24, x25
- push x22, x23
- push x20, x21
- push x18, x19
- push x16, x17
- push x14, x15
- push x12, x13
- push x10, x11
- push x8, x9
- push x6, x7
- push x4, x5
- push x2, x3
- push x0, x1
+ stp x0, x1, [sp, #16 * 0]
+ stp x2, x3, [sp, #16 * 1]
+ stp x4, x5, [sp, #16 * 2]
+ stp x6, x7, [sp, #16 * 3]
+ stp x8, x9, [sp, #16 * 4]
+ stp x10, x11, [sp, #16 * 5]
+ stp x12, x13, [sp, #16 * 6]
+ stp x14, x15, [sp, #16 * 7]
+ stp x16, x17, [sp, #16 * 8]
+ stp x18, x19, [sp, #16 * 9]
+ stp x20, x21, [sp, #16 * 10]
+ stp x22, x23, [sp, #16 * 11]
+ stp x24, x25, [sp, #16 * 12]
+ stp x26, x27, [sp, #16 * 13]
+ stp x28, x29, [sp, #16 * 14]
+
.if \el == 0
mrs x21, sp_el0
get_thread_info tsk // Ensure MDSCR_EL1.SS is clear,
.if \el == 0
ct_user_enter
ldr x23, [sp, #S_SP] // load return stack pointer
+ msr sp_el0, x23
.endif
+ msr elr_el1, x21 // set up the return data
+ msr spsr_el1, x22
.if \ret
ldr x1, [sp, #S_X1] // preserve x0 (syscall return)
- add sp, sp, S_X2
.else
- pop x0, x1
+ ldp x0, x1, [sp, #16 * 0]
.endif
- pop x2, x3 // load the rest of the registers
- pop x4, x5
- pop x6, x7
- pop x8, x9
- msr elr_el1, x21 // set up the return data
- msr spsr_el1, x22
- .if \el == 0
- msr sp_el0, x23
- .endif
- pop x10, x11
- pop x12, x13
- pop x14, x15
- pop x16, x17
- pop x18, x19
- pop x20, x21
- pop x22, x23
- pop x24, x25
- pop x26, x27
- pop x28, x29
- ldr lr, [sp], #S_FRAME_SIZE - S_LR // load LR and restore SP
+ ldp x2, x3, [sp, #16 * 1]
+ ldp x4, x5, [sp, #16 * 2]
+ ldp x6, x7, [sp, #16 * 3]
+ ldp x8, x9, [sp, #16 * 4]
+ ldp x10, x11, [sp, #16 * 5]
+ ldp x12, x13, [sp, #16 * 6]
+ ldp x14, x15, [sp, #16 * 7]
+ ldp x16, x17, [sp, #16 * 8]
+ ldp x18, x19, [sp, #16 * 9]
+ ldp x20, x21, [sp, #16 * 10]
+ ldp x22, x23, [sp, #16 * 11]
+ ldp x24, x25, [sp, #16 * 12]
+ ldp x26, x27, [sp, #16 * 13]
+ ldp x28, x29, [sp, #16 * 14]
+ ldr lr, [sp, #S_LR]
+ add sp, sp, #S_FRAME_SIZE // restore sp
eret // return to kernel
.endm
* Interrupt handling.
*/
.macro irq_handler
- ldr x1, handle_arch_irq
+ adrp x1, handle_arch_irq
+ ldr x1, [x1, #:lo12:handle_arch_irq]
mov x0, sp
blr x1
.endm
bic x0, x26, #(0xff << 56)
mov x1, x25
mov x2, sp
- adr lr, ret_to_user
- b do_mem_abort
+ bl do_mem_abort
+ b ret_to_user
el0_ia:
/*
* Instruction abort handling
mov x0, x26
orr x1, x25, #1 << 24 // use reserved ISS bit for instruction aborts
mov x2, sp
- adr lr, ret_to_user
- b do_mem_abort
+ bl do_mem_abort
+ b ret_to_user
el0_fpsimd_acc:
/*
* Floating Point or Advanced SIMD access
ct_user_exit
mov x0, x25
mov x1, sp
- adr lr, ret_to_user
- b do_fpsimd_acc
+ bl do_fpsimd_acc
+ b ret_to_user
el0_fpsimd_exc:
/*
* Floating Point or Advanced SIMD exception
ct_user_exit
mov x0, x25
mov x1, sp
- adr lr, ret_to_user
- b do_fpsimd_exc
+ bl do_fpsimd_exc
+ b ret_to_user
el0_sp_pc:
/*
* Stack or PC alignment exception handling
mov x0, x26
mov x1, x25
mov x2, sp
- adr lr, ret_to_user
- b do_sp_pc_abort
+ bl do_sp_pc_abort
+ b ret_to_user
el0_undef:
/*
* Undefined instruction
enable_dbg_and_irq
ct_user_exit
mov x0, sp
- adr lr, ret_to_user
- b do_undefinstr
+ bl do_undefinstr
+ b ret_to_user
el0_dbg:
/*
* Debug exception handling
mov x0, sp
mov x1, #BAD_SYNC
mrs x2, esr_el1
- adr lr, ret_to_user
- b bad_mode
+ bl bad_mode
+ b ret_to_user
ENDPROC(el0_sync)
.align 6
ldr x16, [tsk, #TI_FLAGS] // check for syscall hooks
tst x16, #_TIF_SYSCALL_WORK
b.ne __sys_trace
- adr lr, ret_fast_syscall // return address
cmp scno, sc_nr // check upper syscall limit
b.hs ni_sys
ldr x16, [stbl, scno, lsl #3] // address in the syscall table
- br x16 // call sys_* routine
+ blr x16 // call sys_* routine
+ b ret_fast_syscall
ni_sys:
mov x0, sp
- b do_ni_syscall
+ bl do_ni_syscall
+ b ret_fast_syscall
ENDPROC(el0_svc)
/*
* switches, and waiting for our parent to respond.
*/
__sys_trace:
- mov x0, sp
+ mov w0, #-1 // set default errno for
+ cmp scno, x0 // user-issued syscall(-1)
+ b.ne 1f
+ mov x0, #-ENOSYS
+ str x0, [sp, #S_X0]
+1: mov x0, sp
bl syscall_trace_enter
- adr lr, __sys_trace_return // return address
+ cmp w0, #-1 // skip the syscall?
+ b.eq __sys_trace_return_skipped
uxtw scno, w0 // syscall number (possibly new)
mov x1, sp // pointer to regs
cmp scno, sc_nr // check upper syscall limit
- b.hs ni_sys
+ b.hs __ni_sys_trace
ldp x0, x1, [sp] // restore the syscall args
ldp x2, x3, [sp, #S_X2]
ldp x4, x5, [sp, #S_X4]
ldp x6, x7, [sp, #S_X6]
ldr x16, [stbl, scno, lsl #3] // address in the syscall table
- br x16 // call sys_* routine
+ blr x16 // call sys_* routine
__sys_trace_return:
- str x0, [sp] // save returned x0
+ str x0, [sp, #S_X0] // save returned x0
+__sys_trace_return_skipped:
mov x0, sp
bl syscall_trace_exit
b ret_to_user
+__ni_sys_trace:
+ mov x0, sp
+ bl do_ni_syscall
+ b __sys_trace_return
+
/*
* Special system call wrappers.
*/
mov x0, sp
b sys_rt_sigreturn
ENDPROC(sys_rt_sigreturn_wrapper)
-
-ENTRY(handle_arch_irq)
- .quad 0
#endif
#ifdef CONFIG_EFI
+ .globl stext_offset
+ .set stext_offset, stext - efi_head
.align 3
pe_header:
.ascii "PE"
.long 0 // SizeOfInitializedData
.long 0 // SizeOfUninitializedData
.long efi_stub_entry - efi_head // AddressOfEntryPoint
- .long stext - efi_head // BaseOfCode
+ .long stext_offset // BaseOfCode
extra_header_fields:
.quad 0 // ImageBase
- .long 0x20 // SectionAlignment
- .long 0x8 // FileAlignment
+ .long 0x1000 // SectionAlignment
+ .long PECOFF_FILE_ALIGNMENT // FileAlignment
.short 0 // MajorOperatingSystemVersion
.short 0 // MinorOperatingSystemVersion
.short 0 // MajorImageVersion
.long _end - efi_head // SizeOfImage
// Everything before the kernel image is considered part of the header
- .long stext - efi_head // SizeOfHeaders
+ .long stext_offset // SizeOfHeaders
.long 0 // CheckSum
.short 0xa // Subsystem (EFI application)
.short 0 // DllCharacteristics
.byte 0
.byte 0 // end of 0 padding of section name
.long _end - stext // VirtualSize
- .long stext - efi_head // VirtualAddress
+ .long stext_offset // VirtualAddress
.long _edata - stext // SizeOfRawData
- .long stext - efi_head // PointerToRawData
+ .long stext_offset // PointerToRawData
.long 0 // PointerToRelocations (0 for executables)
.long 0 // PointerToLineNumbers (0 for executables)
.short 0 // NumberOfRelocations (0 for executables)
.short 0 // NumberOfLineNumbers (0 for executables)
.long 0xe0500020 // Characteristics (section flags)
- .align 5
+
+ /*
+ * EFI will load stext onwards at the 4k section alignment
+ * described in the PE/COFF header. To ensure that instruction
+ * sequences using an adrp and a :lo12: immediate will function
+ * correctly at this alignment, we must ensure that stext is
+ * placed at a 4k boundary in the Image to begin with.
+ */
+ .align 12
#endif
ENTRY(stext)
mov x0, x22
bl lookup_processor_type
mov x23, x0 // x23=current cpu_table
- cbz x23, __error_p // invalid processor (x23=0)?
+ /*
+ * __error_p may end up out of range for cbz if text areas are
+ * aligned up to section sizes.
+ */
+ cbnz x23, 1f // invalid processor (x23=0)?
+ b __error_p
+1:
bl __vet_fdt
bl __create_page_tables // x25=TTBR0, x26=TTBR1
/*
*/
ldr x27, __switch_data // address to jump to after
// MMU has been enabled
- adr lr, __enable_mmu // return (PIC) address
+ adrp lr, __enable_mmu // return (PIC) address
+ add lr, lr, #:lo12:__enable_mmu
ldr x12, [x23, #CPU_INFO_SETUP]
add x12, x12, x28 // __virt_to_phys
br x12 // initialise processor
ENDPROC(stext)
+/*
+ * Determine validity of the x21 FDT pointer.
+ * The dtb must be 8-byte aligned and live in the first 512M of memory.
+ */
+__vet_fdt:
+ tst x21, #0x7
+ b.ne 1f
+ cmp x21, x24
+ b.lt 1f
+ mov x0, #(1 << 29)
+ add x0, x0, x24
+ cmp x21, x0
+ b.ge 1f
+ ret
+1:
+ mov x21, #0
+ ret
+ENDPROC(__vet_fdt)
+/*
+ * Macro to create a table entry to the next page.
+ *
+ * tbl: page table address
+ * virt: virtual address
+ * shift: #imm page table shift
+ * ptrs: #imm pointers per table page
+ *
+ * Preserves: virt
+ * Corrupts: tmp1, tmp2
+ * Returns: tbl -> next level table page address
+ */
+ .macro create_table_entry, tbl, virt, shift, ptrs, tmp1, tmp2
+ lsr \tmp1, \virt, #\shift
+ and \tmp1, \tmp1, #\ptrs - 1 // table index
+ add \tmp2, \tbl, #PAGE_SIZE
+ orr \tmp2, \tmp2, #PMD_TYPE_TABLE // address of next table and entry type
+ str \tmp2, [\tbl, \tmp1, lsl #3]
+ add \tbl, \tbl, #PAGE_SIZE // next level table page
+ .endm
+
+/*
+ * Macro to populate the PGD (and possibily PUD) for the corresponding
+ * block entry in the next level (tbl) for the given virtual address.
+ *
+ * Preserves: tbl, next, virt
+ * Corrupts: tmp1, tmp2
+ */
+ .macro create_pgd_entry, tbl, virt, tmp1, tmp2
+ create_table_entry \tbl, \virt, PGDIR_SHIFT, PTRS_PER_PGD, \tmp1, \tmp2
+#if SWAPPER_PGTABLE_LEVELS == 3
+ create_table_entry \tbl, \virt, TABLE_SHIFT, PTRS_PER_PTE, \tmp1, \tmp2
+#endif
+ .endm
+
+/*
+ * Macro to populate block entries in the page table for the start..end
+ * virtual range (inclusive).
+ *
+ * Preserves: tbl, flags
+ * Corrupts: phys, start, end, pstate
+ */
+ .macro create_block_map, tbl, flags, phys, start, end
+ lsr \phys, \phys, #BLOCK_SHIFT
+ lsr \start, \start, #BLOCK_SHIFT
+ and \start, \start, #PTRS_PER_PTE - 1 // table index
+ orr \phys, \flags, \phys, lsl #BLOCK_SHIFT // table entry
+ lsr \end, \end, #BLOCK_SHIFT
+ and \end, \end, #PTRS_PER_PTE - 1 // table end index
+9999: str \phys, [\tbl, \start, lsl #3] // store the entry
+ add \start, \start, #1 // next entry
+ add \phys, \phys, #BLOCK_SIZE // next block
+ cmp \start, \end
+ b.ls 9999b
+ .endm
+
+/*
+ * Setup the initial page tables. We only setup the barest amount which is
+ * required to get the kernel running. The following sections are required:
+ * - identity mapping to enable the MMU (low address, TTBR0)
+ * - first few MB of the kernel linear mapping to jump to once the MMU has
+ * been enabled, including the FDT blob (TTBR1)
+ * - pgd entry for fixed mappings (TTBR1)
+ */
+__create_page_tables:
+ pgtbl x25, x26, x28 // idmap_pg_dir and swapper_pg_dir addresses
+ mov x27, lr
+
+ /*
+ * Invalidate the idmap and swapper page tables to avoid potential
+ * dirty cache lines being evicted.
+ */
+ mov x0, x25
+ add x1, x26, #SWAPPER_DIR_SIZE
+ bl __inval_cache_range
+
+ /*
+ * Clear the idmap and swapper page tables.
+ */
+ mov x0, x25
+ add x6, x26, #SWAPPER_DIR_SIZE
+1: stp xzr, xzr, [x0], #16
+ stp xzr, xzr, [x0], #16
+ stp xzr, xzr, [x0], #16
+ stp xzr, xzr, [x0], #16
+ cmp x0, x6
+ b.lo 1b
+
+ ldr x7, =MM_MMUFLAGS
+
+ /*
+ * Create the identity mapping.
+ */
+ mov x0, x25 // idmap_pg_dir
+ ldr x3, =KERNEL_START
+ add x3, x3, x28 // __pa(KERNEL_START)
+ create_pgd_entry x0, x3, x5, x6
+ ldr x6, =KERNEL_END
+ mov x5, x3 // __pa(KERNEL_START)
+ add x6, x6, x28 // __pa(KERNEL_END)
+ create_block_map x0, x7, x3, x5, x6
+
+ /*
+ * Map the kernel image (starting with PHYS_OFFSET).
+ */
+ mov x0, x26 // swapper_pg_dir
+ mov x5, #PAGE_OFFSET
+ create_pgd_entry x0, x5, x3, x6
+ ldr x6, =KERNEL_END
+ mov x3, x24 // phys offset
+ create_block_map x0, x7, x3, x5, x6
+
+ /*
+ * Map the FDT blob (maximum 2MB; must be within 512MB of
+ * PHYS_OFFSET).
+ */
+ mov x3, x21 // FDT phys address
+ and x3, x3, #~((1 << 21) - 1) // 2MB aligned
+ mov x6, #PAGE_OFFSET
+ sub x5, x3, x24 // subtract PHYS_OFFSET
+ tst x5, #~((1 << 29) - 1) // within 512MB?
+ csel x21, xzr, x21, ne // zero the FDT pointer
+ b.ne 1f
+ add x5, x5, x6 // __va(FDT blob)
+ add x6, x5, #1 << 21 // 2MB for the FDT blob
+ sub x6, x6, #1 // inclusive range
+ create_block_map x0, x7, x3, x5, x6
+1:
+ /*
+ * Since the page tables have been populated with non-cacheable
+ * accesses (MMU disabled), invalidate the idmap and swapper page
+ * tables again to remove any speculatively loaded cache lines.
+ */
+ mov x0, x25
+ add x1, x26, #SWAPPER_DIR_SIZE
+ bl __inval_cache_range
+
+ mov lr, x27
+ ret
+ENDPROC(__create_page_tables)
+ .ltorg
+
+ .align 3
+ .type __switch_data, %object
+__switch_data:
+ .quad __mmap_switched
+ .quad __bss_start // x6
+ .quad __bss_stop // x7
+ .quad processor_id // x4
+ .quad __fdt_pointer // x5
+ .quad memstart_addr // x6
+ .quad init_thread_union + THREAD_START_SP // sp
+
+/*
+ * The following fragment of code is executed with the MMU on in MMU mode, and
+ * uses absolute addresses; this is not position independent.
+ */
+__mmap_switched:
+ adr x3, __switch_data + 8
+
+ ldp x6, x7, [x3], #16
+1: cmp x6, x7
+ b.hs 2f
+ str xzr, [x6], #8 // Clear BSS
+ b 1b
+2:
+ ldp x4, x5, [x3], #16
+ ldr x6, [x3], #8
+ ldr x16, [x3]
+ mov sp, x16
+ str x22, [x4] // Save processor ID
+ str x21, [x5] // Save FDT pointer
+ str x24, [x6] // Save PHYS_OFFSET
+ mov x29, #0
+ b start_kernel
+ENDPROC(__mmap_switched)
+
+/*
+ * end early head section, begin head code that is also used for
+ * hotplug and needs to have the same protections as the text region
+ */
+ .section ".text","ax"
/*
* If we're fortunate enough to boot at EL2, ensure that the world is
* sane before dropping to EL1.
msr vttbr_el2, xzr
/* Hypervisor stub */
- adr x0, __hyp_stub_vectors
+ adrp x0, __hyp_stub_vectors
+ add x0, x0, #:lo12:__hyp_stub_vectors
msr vbar_el2, x0
/* spsr */
1: .quad .
.quad PAGE_OFFSET
-/*
- * Macro to create a table entry to the next page.
- *
- * tbl: page table address
- * virt: virtual address
- * shift: #imm page table shift
- * ptrs: #imm pointers per table page
- *
- * Preserves: virt
- * Corrupts: tmp1, tmp2
- * Returns: tbl -> next level table page address
- */
- .macro create_table_entry, tbl, virt, shift, ptrs, tmp1, tmp2
- lsr \tmp1, \virt, #\shift
- and \tmp1, \tmp1, #\ptrs - 1 // table index
- add \tmp2, \tbl, #PAGE_SIZE
- orr \tmp2, \tmp2, #PMD_TYPE_TABLE // address of next table and entry type
- str \tmp2, [\tbl, \tmp1, lsl #3]
- add \tbl, \tbl, #PAGE_SIZE // next level table page
- .endm
-
-/*
- * Macro to populate the PGD (and possibily PUD) for the corresponding
- * block entry in the next level (tbl) for the given virtual address.
- *
- * Preserves: tbl, next, virt
- * Corrupts: tmp1, tmp2
- */
- .macro create_pgd_entry, tbl, virt, tmp1, tmp2
- create_table_entry \tbl, \virt, PGDIR_SHIFT, PTRS_PER_PGD, \tmp1, \tmp2
-#if SWAPPER_PGTABLE_LEVELS == 3
- create_table_entry \tbl, \virt, TABLE_SHIFT, PTRS_PER_PTE, \tmp1, \tmp2
-#endif
- .endm
-
-/*
- * Macro to populate block entries in the page table for the start..end
- * virtual range (inclusive).
- *
- * Preserves: tbl, flags
- * Corrupts: phys, start, end, pstate
- */
- .macro create_block_map, tbl, flags, phys, start, end
- lsr \phys, \phys, #BLOCK_SHIFT
- lsr \start, \start, #BLOCK_SHIFT
- and \start, \start, #PTRS_PER_PTE - 1 // table index
- orr \phys, \flags, \phys, lsl #BLOCK_SHIFT // table entry
- lsr \end, \end, #BLOCK_SHIFT
- and \end, \end, #PTRS_PER_PTE - 1 // table end index
-9999: str \phys, [\tbl, \start, lsl #3] // store the entry
- add \start, \start, #1 // next entry
- add \phys, \phys, #BLOCK_SIZE // next block
- cmp \start, \end
- b.ls 9999b
- .endm
-
-/*
- * Setup the initial page tables. We only setup the barest amount which is
- * required to get the kernel running. The following sections are required:
- * - identity mapping to enable the MMU (low address, TTBR0)
- * - first few MB of the kernel linear mapping to jump to once the MMU has
- * been enabled, including the FDT blob (TTBR1)
- * - pgd entry for fixed mappings (TTBR1)
- */
-__create_page_tables:
- pgtbl x25, x26, x28 // idmap_pg_dir and swapper_pg_dir addresses
- mov x27, lr
-
- /*
- * Invalidate the idmap and swapper page tables to avoid potential
- * dirty cache lines being evicted.
- */
- mov x0, x25
- add x1, x26, #SWAPPER_DIR_SIZE
- bl __inval_cache_range
-
- /*
- * Clear the idmap and swapper page tables.
- */
- mov x0, x25
- add x6, x26, #SWAPPER_DIR_SIZE
-1: stp xzr, xzr, [x0], #16
- stp xzr, xzr, [x0], #16
- stp xzr, xzr, [x0], #16
- stp xzr, xzr, [x0], #16
- cmp x0, x6
- b.lo 1b
-
- ldr x7, =MM_MMUFLAGS
-
- /*
- * Create the identity mapping.
- */
- mov x0, x25 // idmap_pg_dir
- ldr x3, =KERNEL_START
- add x3, x3, x28 // __pa(KERNEL_START)
- create_pgd_entry x0, x3, x5, x6
- ldr x6, =KERNEL_END
- mov x5, x3 // __pa(KERNEL_START)
- add x6, x6, x28 // __pa(KERNEL_END)
- create_block_map x0, x7, x3, x5, x6
-
- /*
- * Map the kernel image (starting with PHYS_OFFSET).
- */
- mov x0, x26 // swapper_pg_dir
- mov x5, #PAGE_OFFSET
- create_pgd_entry x0, x5, x3, x6
- ldr x6, =KERNEL_END
- mov x3, x24 // phys offset
- create_block_map x0, x7, x3, x5, x6
-
- /*
- * Map the FDT blob (maximum 2MB; must be within 512MB of
- * PHYS_OFFSET).
- */
- mov x3, x21 // FDT phys address
- and x3, x3, #~((1 << 21) - 1) // 2MB aligned
- mov x6, #PAGE_OFFSET
- sub x5, x3, x24 // subtract PHYS_OFFSET
- tst x5, #~((1 << 29) - 1) // within 512MB?
- csel x21, xzr, x21, ne // zero the FDT pointer
- b.ne 1f
- add x5, x5, x6 // __va(FDT blob)
- add x6, x5, #1 << 21 // 2MB for the FDT blob
- sub x6, x6, #1 // inclusive range
- create_block_map x0, x7, x3, x5, x6
-1:
- /*
- * Since the page tables have been populated with non-cacheable
- * accesses (MMU disabled), invalidate the idmap and swapper page
- * tables again to remove any speculatively loaded cache lines.
- */
- mov x0, x25
- add x1, x26, #SWAPPER_DIR_SIZE
- bl __inval_cache_range
-
- mov lr, x27
- ret
-ENDPROC(__create_page_tables)
- .ltorg
-
- .align 3
- .type __switch_data, %object
-__switch_data:
- .quad __mmap_switched
- .quad __bss_start // x6
- .quad __bss_stop // x7
- .quad processor_id // x4
- .quad __fdt_pointer // x5
- .quad memstart_addr // x6
- .quad init_thread_union + THREAD_START_SP // sp
-
-/*
- * The following fragment of code is executed with the MMU on in MMU mode, and
- * uses absolute addresses; this is not position independent.
- */
-__mmap_switched:
- adr x3, __switch_data + 8
-
- ldp x6, x7, [x3], #16
-1: cmp x6, x7
- b.hs 2f
- str xzr, [x6], #8 // Clear BSS
- b 1b
-2:
- ldp x4, x5, [x3], #16
- ldr x6, [x3], #8
- ldr x16, [x3]
- mov sp, x16
- str x22, [x4] // Save processor ID
- str x21, [x5] // Save FDT pointer
- str x24, [x6] // Save PHYS_OFFSET
- mov x29, #0
- b start_kernel
-ENDPROC(__mmap_switched)
-
/*
* Exception handling. Something went wrong and we can't proceed. We ought to
* tell the user, but since we don't have any guarantee that we're even
.quad .
.quad cpu_table
.size __lookup_processor_type_data, . - __lookup_processor_type_data
-
-/*
- * Determine validity of the x21 FDT pointer.
- * The dtb must be 8-byte aligned and live in the first 512M of memory.
- */
-__vet_fdt:
- tst x21, #0x7
- b.ne 1f
- cmp x21, x24
- b.lt 1f
- mov x0, #(1 << 29)
- add x0, x0, x24
- cmp x21, x0
- b.ge 1f
- ret
-1:
- mov x21, #0
- ret
-ENDPROC(__vet_fdt)
* which ends with "dsb; isb" pair guaranteeing global
* visibility.
*/
- atomic_set(&pp->cpu_count, -1);
+ /* Notify other processors with an additional increment. */
+ atomic_inc(&pp->cpu_count);
} else {
- while (atomic_read(&pp->cpu_count) != -1)
+ while (atomic_read(&pp->cpu_count) <= num_online_cpus())
cpu_relax();
isb();
}
return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_6, insn, shift);
}
+
+bool aarch32_insn_is_wide(u32 insn)
+{
+ return insn >= 0xe800;
+}
+
+/*
+ * Macros/defines for extracting register numbers from instruction.
+ */
+u32 aarch32_insn_extract_reg_num(u32 insn, int offset)
+{
+ return (insn & (0xf << offset)) >> offset;
+}
+
+#define OPC2_MASK 0x7
+#define OPC2_OFFSET 5
+u32 aarch32_insn_mcr_extract_opc2(u32 insn)
+{
+ return (insn & (OPC2_MASK << OPC2_OFFSET)) >> OPC2_OFFSET;
+}
+
+#define CRM_MASK 0xf
+u32 aarch32_insn_mcr_extract_crm(u32 insn)
+{
+ return insn & CRM_MASK;
+}
*/
void __memcpy_fromio(void *to, const volatile void __iomem *from, size_t count)
{
- unsigned char *t = to;
- while (count) {
+ while (count && (!IS_ALIGNED((unsigned long)from, 8) ||
+ !IS_ALIGNED((unsigned long)to, 8))) {
+ *(u8 *)to = __raw_readb(from);
+ from++;
+ to++;
count--;
- *t = readb(from);
- t++;
+ }
+
+ while (count >= 8) {
+ *(u64 *)to = __raw_readq(from);
+ from += 8;
+ to += 8;
+ count -= 8;
+ }
+
+ while (count) {
+ *(u8 *)to = __raw_readb(from);
from++;
+ to++;
+ count--;
}
}
EXPORT_SYMBOL(__memcpy_fromio);
*/
void __memcpy_toio(volatile void __iomem *to, const void *from, size_t count)
{
- const unsigned char *f = from;
- while (count) {
+ while (count && (!IS_ALIGNED((unsigned long)to, 8) ||
+ !IS_ALIGNED((unsigned long)from, 8))) {
+ __raw_writeb(*(volatile u8 *)from, to);
+ from++;
+ to++;
count--;
- writeb(*f, to);
- f++;
+ }
+
+ while (count >= 8) {
+ __raw_writeq(*(volatile u64 *)from, to);
+ from += 8;
+ to += 8;
+ count -= 8;
+ }
+
+ while (count) {
+ __raw_writeb(*(volatile u8 *)from, to);
+ from++;
to++;
+ count--;
}
}
EXPORT_SYMBOL(__memcpy_toio);
*/
void __memset_io(volatile void __iomem *dst, int c, size_t count)
{
- while (count) {
+ u64 qc = (u8)c;
+
+ qc |= qc << 8;
+ qc |= qc << 16;
+ qc |= qc << 32;
+
+ while (count && !IS_ALIGNED((unsigned long)dst, 8)) {
+ __raw_writeb(c, dst);
+ dst++;
count--;
- writeb(c, dst);
+ }
+
+ while (count >= 8) {
+ __raw_writeq(qc, dst);
+ dst += 8;
+ count -= 8;
+ }
+
+ while (count) {
+ __raw_writeb(c, dst);
dst++;
+ count--;
}
}
EXPORT_SYMBOL(__memset_io);
return 0;
}
+void (*handle_arch_irq)(struct pt_regs *) = NULL;
+
void __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
{
if (handle_arch_irq)
#ifdef HAVE_JUMP_LABEL
-static void __arch_jump_label_transform(struct jump_entry *entry,
- enum jump_label_type type,
- bool is_static)
+void arch_jump_label_transform(struct jump_entry *entry,
+ enum jump_label_type type)
{
void *addr = (void *)entry->code;
u32 insn;
insn = aarch64_insn_gen_nop();
}
- if (is_static)
- aarch64_insn_patch_text_nosync(addr, insn);
- else
- aarch64_insn_patch_text(&addr, &insn, 1);
-}
-
-void arch_jump_label_transform(struct jump_entry *entry,
- enum jump_label_type type)
-{
- __arch_jump_label_transform(entry, type, false);
+ aarch64_insn_patch_text(&addr, &insn, 1);
}
void arch_jump_label_transform_static(struct jump_entry *entry,
enum jump_label_type type)
{
- __arch_jump_label_transform(entry, type, true);
+ /*
+ * We use the architected A64 NOP in arch_static_branch, so there's no
+ * need to patch an identical A64 NOP over the top of it here. The core
+ * will call arch_jump_label_transform from a module notifier if the
+ * NOP needs to be replaced by a branch.
+ */
}
#endif /* HAVE_JUMP_LABEL */
#include <linux/moduleloader.h>
#include <linux/vmalloc.h>
#include <asm/insn.h>
+#include <asm/sections.h>
#define AARCH64_INSN_IMM_MOVNZ AARCH64_INSN_IMM_MAX
#define AARCH64_INSN_IMM_MOVK AARCH64_INSN_IMM_16
me->name, (int)ELF64_R_TYPE(rel[i].r_info), val);
return -ENOEXEC;
}
+
+int module_finalize(const Elf_Ehdr *hdr,
+ const Elf_Shdr *sechdrs,
+ struct module *me)
+{
+ const Elf_Shdr *s, *se;
+ const char *secstrs = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
+
+ for (s = sechdrs, se = sechdrs + hdr->e_shnum; s < se; s++) {
+ if (strcmp(".altinstructions", secstrs + s->sh_name) == 0) {
+ apply_alternatives((void *)s->sh_addr, s->sh_size);
+ return 0;
+ }
+ }
+
+ return 0;
+}
ret = 1;
}
- if (left > (s64)armpmu->max_period)
- left = armpmu->max_period;
+ /*
+ * Limit the maximum period to prevent the counter value
+ * from overtaking the one we are about to program. In
+ * effect we are reducing max_period to account for
+ * interrupt latency (and we are being very conservative).
+ */
+ if (left > (armpmu->max_period >> 1))
+ left = armpmu->max_period >> 1;
local64_set(&hwc->prev_count, (u64)-left);
if (WARN_ON_ONCE(!index))
return -EINVAL;
- if (state->type == PSCI_POWER_STATE_TYPE_STANDBY)
+ if (state[index - 1].type == PSCI_POWER_STATE_TYPE_STANDBY)
ret = psci_ops.cpu_suspend(state[index - 1], 0);
else
ret = __cpu_suspend(index, psci_suspend_finisher);
#include <linux/smp.h>
#include <linux/ptrace.h>
#include <linux/user.h>
+#include <linux/seccomp.h>
#include <linux/security.h>
#include <linux/init.h>
#include <linux/signal.h>
return ret;
}
+static int system_call_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ int syscallno = task_pt_regs(target)->syscallno;
+
+ return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &syscallno, 0, -1);
+}
+
+static int system_call_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int syscallno, ret;
+
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &syscallno, 0, -1);
+ if (ret)
+ return ret;
+
+ task_pt_regs(target)->syscallno = syscallno;
+ return ret;
+}
+
enum aarch64_regset {
REGSET_GPR,
REGSET_FPR,
REGSET_HW_BREAK,
REGSET_HW_WATCH,
#endif
+ REGSET_SYSTEM_CALL,
};
static const struct user_regset aarch64_regsets[] = {
.set = hw_break_set,
},
#endif
+ [REGSET_SYSTEM_CALL] = {
+ .core_note_type = NT_ARM_SYSTEM_CALL,
+ .n = 1,
+ .size = sizeof(int),
+ .align = sizeof(int),
+ .get = system_call_get,
+ .set = system_call_set,
+ },
};
static const struct user_regset_view user_aarch64_view = {
asmlinkage int syscall_trace_enter(struct pt_regs *regs)
{
+ /* Do the secure computing check first; failures should be fast. */
+ if (secure_computing() == -1)
+ return -1;
+
if (test_thread_flag(TIF_SYSCALL_TRACE))
tracehook_report_syscall(regs, PTRACE_SYSCALL_ENTER);
#include <linux/of_fdt.h>
#include <linux/of_platform.h>
#include <linux/efi.h>
+#include <linux/personality.h>
#include <asm/fixmap.h>
#include <asm/cpu.h>
#include <asm/cputype.h>
#include <asm/elf.h>
#include <asm/cputable.h>
+#include <asm/cpufeature.h>
#include <asm/cpu_ops.h>
#include <asm/sections.h>
#include <asm/setup.h>
COMPAT_HWCAP_FAST_MULT|COMPAT_HWCAP_EDSP|\
COMPAT_HWCAP_TLS|COMPAT_HWCAP_VFP|\
COMPAT_HWCAP_VFPv3|COMPAT_HWCAP_VFPv4|\
- COMPAT_HWCAP_NEON|COMPAT_HWCAP_IDIV)
+ COMPAT_HWCAP_NEON|COMPAT_HWCAP_IDIV|\
+ COMPAT_HWCAP_LPAE)
unsigned int compat_elf_hwcap __read_mostly = COMPAT_ELF_HWCAP_DEFAULT;
unsigned int compat_elf_hwcap2 __read_mostly;
#endif
+DECLARE_BITMAP(cpu_hwcaps, ARM64_NCAPS);
+
static const char *cpu_name;
-static const char *machine_name;
phys_addr_t __fdt_pointer __initdata;
/*
void __init smp_setup_processor_id(void)
{
+ u64 mpidr = read_cpuid_mpidr() & MPIDR_HWID_BITMASK;
+ cpu_logical_map(0) = mpidr;
+
/*
* clear __my_cpu_offset on boot CPU to avoid hang caused by
* using percpu variable early, for example, lockdep will
* access percpu variable inside lock_release
*/
set_my_cpu_offset(0);
+ pr_info("Booting Linux on physical CPU 0x%lx\n", (unsigned long)mpidr);
}
bool arch_match_cpu_phys_id(int cpu, u64 phys_id)
cpu_relax();
}
- machine_name = of_flat_dt_get_machine_name();
+ dump_stack_set_arch_desc("%s (DT)", of_flat_dt_get_machine_name());
}
/*
*cmdline_p = boot_command_line;
+ early_fixmap_init();
early_ioremap_init();
parse_early_param();
psci_init();
- cpu_logical_map(0) = read_cpuid_mpidr() & MPIDR_HWID_BITMASK;
cpu_read_bootcpu_ops();
#ifdef CONFIG_SMP
smp_init_cpus();
NULL
};
+#ifdef CONFIG_COMPAT
+static const char *compat_hwcap_str[] = {
+ "swp",
+ "half",
+ "thumb",
+ "26bit",
+ "fastmult",
+ "fpa",
+ "vfp",
+ "edsp",
+ "java",
+ "iwmmxt",
+ "crunch",
+ "thumbee",
+ "neon",
+ "vfpv3",
+ "vfpv3d16",
+ "tls",
+ "vfpv4",
+ "idiva",
+ "idivt",
+ "vfpd32",
+ "lpae",
+ "evtstrm"
+};
+
+static const char *compat_hwcap2_str[] = {
+ "aes",
+ "pmull",
+ "sha1",
+ "sha2",
+ "crc32",
+ NULL
+};
+#endif /* CONFIG_COMPAT */
+
static int c_show(struct seq_file *m, void *v)
{
- int i;
-
- seq_printf(m, "Processor\t: %s rev %d (%s)\n",
- cpu_name, read_cpuid_id() & 15, ELF_PLATFORM);
+ int i, j;
for_each_online_cpu(i) {
+ struct cpuinfo_arm64 *cpuinfo = &per_cpu(cpu_data, i);
+ u32 midr = cpuinfo->reg_midr;
+
/*
* glibc reads /proc/cpuinfo to determine the number of
* online processors, looking for lines beginning with
#ifdef CONFIG_SMP
seq_printf(m, "processor\t: %d\n", i);
#endif
- }
-
- /* dump out the processor features */
- seq_puts(m, "Features\t: ");
-
- for (i = 0; hwcap_str[i]; i++)
- if (elf_hwcap & (1 << i))
- seq_printf(m, "%s ", hwcap_str[i]);
-
- seq_printf(m, "\nCPU implementer\t: 0x%02x\n", read_cpuid_id() >> 24);
- seq_printf(m, "CPU architecture: AArch64\n");
- seq_printf(m, "CPU variant\t: 0x%x\n", (read_cpuid_id() >> 20) & 15);
- seq_printf(m, "CPU part\t: 0x%03x\n", (read_cpuid_id() >> 4) & 0xfff);
- seq_printf(m, "CPU revision\t: %d\n", read_cpuid_id() & 15);
- seq_puts(m, "\n");
-
- seq_printf(m, "Hardware\t: %s\n", machine_name);
+ /*
+ * Dump out the common processor features in a single line.
+ * Userspace should read the hwcaps with getauxval(AT_HWCAP)
+ * rather than attempting to parse this, but there's a body of
+ * software which does already (at least for 32-bit).
+ */
+ seq_puts(m, "Features\t:");
+ if (personality(current->personality) == PER_LINUX32) {
+#ifdef CONFIG_COMPAT
+ for (j = 0; compat_hwcap_str[j]; j++)
+ if (compat_elf_hwcap & (1 << j))
+ seq_printf(m, " %s", compat_hwcap_str[j]);
+
+ for (j = 0; compat_hwcap2_str[j]; j++)
+ if (compat_elf_hwcap2 & (1 << j))
+ seq_printf(m, " %s", compat_hwcap2_str[j]);
+#endif /* CONFIG_COMPAT */
+ } else {
+ for (j = 0; hwcap_str[j]; j++)
+ if (elf_hwcap & (1 << j))
+ seq_printf(m, " %s", hwcap_str[j]);
+ }
+ seq_puts(m, "\n");
+
+ seq_printf(m, "CPU implementer\t: 0x%02x\n",
+ MIDR_IMPLEMENTOR(midr));
+ seq_printf(m, "CPU architecture: 8\n");
+ seq_printf(m, "CPU variant\t: 0x%x\n", MIDR_VARIANT(midr));
+ seq_printf(m, "CPU part\t: 0x%03x\n", MIDR_PARTNUM(midr));
+ seq_printf(m, "CPU revision\t: %d\n\n", MIDR_REVISION(midr));
+ }
return 0;
}
err |= __put_user(from->si_uid, &to->si_uid);
err |= __put_user((compat_uptr_t)(unsigned long)from->si_ptr, &to->si_ptr);
break;
+ case __SI_SYS:
+ err |= __put_user((compat_uptr_t)(unsigned long)
+ from->si_call_addr, &to->si_call_addr);
+ err |= __put_user(from->si_syscall, &to->si_syscall);
+ err |= __put_user(from->si_arch, &to->si_arch);
+ break;
default: /* this is just in case for now ... */
err |= __put_user(from->si_pid, &to->si_pid);
err |= __put_user(from->si_uid, &to->si_uid);
ret
ENDPROC(cpu_resume_after_mmu)
- .data
ENTRY(cpu_resume)
bl el2_setup // if in EL2 drop to EL1 cleanly
#ifdef CONFIG_SMP
mrs x1, mpidr_el1
- adr x4, mpidr_hash_ptr
- ldr x5, [x4]
- add x8, x4, x5 // x8 = struct mpidr_hash phys address
+ adrp x8, mpidr_hash
+ add x8, x8, #:lo12:mpidr_hash // x8 = struct mpidr_hash phys address
/* retrieve mpidr_hash members to compute the hash */
ldr x2, [x8, #MPIDR_HASH_MASK]
ldp w3, w4, [x8, #MPIDR_HASH_SHIFTS]
#else
mov x7, xzr
#endif
- adr x0, sleep_save_sp
+ adrp x0, sleep_save_sp
+ add x0, x0, #:lo12:sleep_save_sp
ldr x0, [x0, #SLEEP_SAVE_SP_PHYS]
ldr x0, [x0, x7, lsl #3]
/* load sp from context */
ldr x2, [x0, #CPU_CTX_SP]
- adr x1, sleep_idmap_phys
+ adrp x1, sleep_idmap_phys
/* load physical address of identity map page table in x1 */
- ldr x1, [x1]
+ ldr x1, [x1, #:lo12:sleep_idmap_phys]
mov sp, x2
/*
* cpu_do_resume expects x0 to contain context physical address
bl cpu_do_resume // PC relative jump, MMU off
b cpu_resume_mmu // Resume MMU, never returns
ENDPROC(cpu_resume)
-
- .align 3
-mpidr_hash_ptr:
- /*
- * offset of mpidr_hash symbol from current location
- * used to obtain run-time mpidr_hash address with MMU off
- */
- .quad mpidr_hash - .
-/*
- * physical address of identity mapped page tables
- */
- .type sleep_idmap_phys, #object
-ENTRY(sleep_idmap_phys)
- .quad 0
-/*
- * struct sleep_save_sp {
- * phys_addr_t *save_ptr_stash;
- * phys_addr_t save_ptr_stash_phys;
- * };
- */
- .type sleep_save_sp, #object
-ENTRY(sleep_save_sp)
- .space SLEEP_SAVE_SP_SZ // struct sleep_save_sp
#include <linux/of.h>
#include <linux/irq_work.h>
+#include <asm/alternative.h>
#include <asm/atomic.h>
#include <asm/cacheflush.h>
#include <asm/cpu.h>
void __init smp_cpus_done(unsigned int max_cpus)
{
pr_info("SMP: Total of %d processors activated.\n", num_online_cpus());
+ apply_alternatives_all();
}
void __init smp_prepare_boot_cpu(void)
return ret;
}
-extern struct sleep_save_sp sleep_save_sp;
-extern phys_addr_t sleep_idmap_phys;
+struct sleep_save_sp sleep_save_sp;
+phys_addr_t sleep_idmap_phys;
static int __init cpu_suspend_init(void)
{
#include <asm/cacheflush.h>
#include <asm/unistd.h>
-static inline void
-do_compat_cache_op(unsigned long start, unsigned long end, int flags)
+static long
+__do_compat_cache_op(unsigned long start, unsigned long end)
{
- struct mm_struct *mm = current->active_mm;
- struct vm_area_struct *vma;
+ long ret;
- if (end < start || flags)
- return;
+ do {
+ unsigned long chunk = min(PAGE_SIZE, end - start);
- down_read(&mm->mmap_sem);
- vma = find_vma(mm, start);
- if (vma && vma->vm_start < end) {
- if (start < vma->vm_start)
- start = vma->vm_start;
- if (end > vma->vm_end)
- end = vma->vm_end;
- up_read(&mm->mmap_sem);
- __flush_cache_user_range(start & PAGE_MASK, PAGE_ALIGN(end));
- return;
- }
- up_read(&mm->mmap_sem);
+ if (fatal_signal_pending(current))
+ return 0;
+
+ ret = __flush_cache_user_range(start, start + chunk);
+ if (ret)
+ return ret;
+
+ cond_resched();
+ start += chunk;
+ } while (start < end);
+
+ return 0;
}
+static inline long
+do_compat_cache_op(unsigned long start, unsigned long end, int flags)
+{
+ if (end < start || flags)
+ return -EINVAL;
+
+ if (!access_ok(VERIFY_READ, start, end - start))
+ return -EFAULT;
+
+ return __do_compat_cache_op(start, end);
+}
/*
* Handle all unrecognised system calls.
*/
* the specified region).
*/
case __ARM_NR_compat_cacheflush:
- do_compat_cache_op(regs->regs[0], regs->regs[1], regs->regs[2]);
- return 0;
+ return do_compat_cache_op(regs->regs[0], regs->regs[1], regs->regs[2]);
case __ARM_NR_compat_set_tls:
current->thread.tp_value = regs->regs[0];
/* Multiprocessor system : Multi-threads per core */
cpuid_topo->thread_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
cpuid_topo->core_id = MPIDR_AFFINITY_LEVEL(mpidr, 1);
- cpuid_topo->cluster_id = MPIDR_AFFINITY_LEVEL(mpidr, 2);
+ cpuid_topo->cluster_id = MPIDR_AFFINITY_LEVEL(mpidr, 2) |
+ MPIDR_AFFINITY_LEVEL(mpidr, 3) << 8;
} else {
/* Multiprocessor system : Single-thread per core */
cpuid_topo->thread_id = -1;
cpuid_topo->core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
- cpuid_topo->cluster_id = MPIDR_AFFINITY_LEVEL(mpidr, 1);
+ cpuid_topo->cluster_id = MPIDR_AFFINITY_LEVEL(mpidr, 1) |
+ MPIDR_AFFINITY_LEVEL(mpidr, 2) << 8 |
+ MPIDR_AFFINITY_LEVEL(mpidr, 3) << 16;
}
pr_debug("CPU%u: cluster %d core %d thread %d mpidr %#016llx\n",
--- /dev/null
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM emulation
+
+#if !defined(_TRACE_EMULATION_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_EMULATION_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(instruction_emulation,
+
+ TP_PROTO(const char *instr, u64 addr),
+ TP_ARGS(instr, addr),
+
+ TP_STRUCT__entry(
+ __string(instr, instr)
+ __field(u64, addr)
+ ),
+
+ TP_fast_assign(
+ __assign_str(instr, instr);
+ __entry->addr = addr;
+ ),
+
+ TP_printk("instr=\"%s\" addr=0x%llx", __get_str(instr), __entry->addr)
+);
+
+#endif /* _TRACE_EMULATION_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_PATH .
+
+#define TRACE_INCLUDE_FILE trace-events-emulation
+#include <trace/define_trace.h>
}
}
+static LIST_HEAD(undef_hook);
+static DEFINE_RAW_SPINLOCK(undef_lock);
+
+void register_undef_hook(struct undef_hook *hook)
+{
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&undef_lock, flags);
+ list_add(&hook->node, &undef_hook);
+ raw_spin_unlock_irqrestore(&undef_lock, flags);
+}
+
+void unregister_undef_hook(struct undef_hook *hook)
+{
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&undef_lock, flags);
+ list_del(&hook->node);
+ raw_spin_unlock_irqrestore(&undef_lock, flags);
+}
+
+static int call_undef_hook(struct pt_regs *regs)
+{
+ struct undef_hook *hook;
+ unsigned long flags;
+ u32 instr;
+ int (*fn)(struct pt_regs *regs, u32 instr) = NULL;
+ void __user *pc = (void __user *)instruction_pointer(regs);
+
+ if (!user_mode(regs))
+ return 1;
+
+ if (compat_thumb_mode(regs)) {
+ /* 16-bit Thumb instruction */
+ if (get_user(instr, (u16 __user *)pc))
+ goto exit;
+ instr = le16_to_cpu(instr);
+ if (aarch32_insn_is_wide(instr)) {
+ u32 instr2;
+
+ if (get_user(instr2, (u16 __user *)(pc + 2)))
+ goto exit;
+ instr2 = le16_to_cpu(instr2);
+ instr = (instr << 16) | instr2;
+ }
+ } else {
+ /* 32-bit ARM instruction */
+ if (get_user(instr, (u32 __user *)pc))
+ goto exit;
+ instr = le32_to_cpu(instr);
+ }
+
+ raw_spin_lock_irqsave(&undef_lock, flags);
+ list_for_each_entry(hook, &undef_hook, node)
+ if ((instr & hook->instr_mask) == hook->instr_val &&
+ (regs->pstate & hook->pstate_mask) == hook->pstate_val)
+ fn = hook->fn;
+
+ raw_spin_unlock_irqrestore(&undef_lock, flags);
+exit:
+ return fn ? fn(regs, instr) : 1;
+}
+
asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
{
siginfo_t info;
if (!aarch32_break_handler(regs))
return;
+ if (call_undef_hook(regs) == 0)
+ return;
+
if (show_unhandled_signals && unhandled_signal(current, SIGILL) &&
printk_ratelimit()) {
pr_info("%s[%d]: undefined instruction: pc=%p\n",
#include "image.h"
-#define ARM_EXIT_KEEP(x)
-#define ARM_EXIT_DISCARD(x) x
+/* .exit.text needed in case of alternative patching */
+#define ARM_EXIT_KEEP(x) x
+#define ARM_EXIT_DISCARD(x)
OUTPUT_ARCH(aarch64)
ENTRY(_text)
*(.hyp.text) \
VMLINUX_SYMBOL(__hyp_text_end) = .;
+/*
+ * The size of the PE/COFF section that covers the kernel image, which
+ * runs from stext to _edata, must be a round multiple of the PE/COFF
+ * FileAlignment, which we set to its minimum value of 0x200. 'stext'
+ * itself is 4 KB aligned, so padding out _edata to a 0x200 aligned
+ * boundary should be sufficient.
+ */
+PECOFF_FILE_ALIGNMENT = 0x200;
+
+#ifdef CONFIG_EFI
+#define PECOFF_EDATA_PADDING \
+ .pecoff_edata_padding : { BYTE(0); . = ALIGN(PECOFF_FILE_ALIGNMENT); }
+#else
+#define PECOFF_EDATA_PADDING
+#endif
+
SECTIONS
{
/*
. = ALIGN(PAGE_SIZE);
__init_end = .;
+ . = ALIGN(4);
+ .altinstructions : {
+ __alt_instructions = .;
+ *(.altinstructions)
+ __alt_instructions_end = .;
+ }
+ .altinstr_replacement : {
+ *(.altinstr_replacement)
+ }
+
+ . = ALIGN(PAGE_SIZE);
_data = .;
_sdata = .;
RW_DATA_SECTION(64, PAGE_SIZE, THREAD_SIZE)
+ PECOFF_EDATA_PADDING
_edata = .;
BSS_SECTION(0, 0, 0)
.macro activate_traps
ldr x2, [x0, #VCPU_HCR_EL2]
msr hcr_el2, x2
- ldr x2, =(CPTR_EL2_TTA)
+ mov x2, #CPTR_EL2_TTA
msr cptr_el2, x2
- ldr x2, =(1 << 15) // Trap CP15 Cr=15
+ mov x2, #(1 << 15) // Trap CP15 Cr=15
msr hstr_el2, x2
mrs x2, mdcr_el2
/* VBAR_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b1100), CRm(0b0000), Op2(0b000),
NULL, reset_val, VBAR_EL1, 0 },
+
+ /* ICC_SRE_EL1 */
+ { Op0(0b11), Op1(0b000), CRn(0b1100), CRm(0b1100), Op2(0b101),
+ trap_raz_wi },
+
/* CONTEXTIDR_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b1101), CRm(0b0000), Op2(0b001),
access_vm_reg, reset_val, CONTEXTIDR_EL1, 0 },
{ Op1( 0), CRn(10), CRm( 2), Op2( 1), access_vm_reg, NULL, c10_NMRR },
{ Op1( 0), CRn(10), CRm( 3), Op2( 0), access_vm_reg, NULL, c10_AMAIR0 },
{ Op1( 0), CRn(10), CRm( 3), Op2( 1), access_vm_reg, NULL, c10_AMAIR1 },
+
+ /* ICC_SRE */
+ { Op1( 0), CRn(12), CRm(12), Op2( 5), trap_raz_wi },
+
{ Op1( 0), CRn(13), CRm( 0), Op2( 1), access_vm_reg, NULL, c13_CID },
};
sub x1, x1, #2
4: adds x1, x1, #1
b.mi 5f
- strb wzr, [x0]
+USER(9f, strb wzr, [x0] )
5: mov x0, #0
ret
ENDPROC(__clear_user)
ioremap.o mmap.o pgd.o mmu.o \
context.o proc.o pageattr.o
obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
+obj-$(CONFIG_ARM64_PTDUMP) += dump.o
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <linux/errno.h>
#include <linux/linkage.h>
#include <linux/init.h>
#include <asm/assembler.h>
+#include <asm/cpufeature.h>
+#include <asm/alternative-asm.h>
#include "proc-macros.S"
add x4, x4, x2
cmp x4, x1
b.lo 1b
-9: // ignore any faulting cache operation
dsb ish
isb
+ mov x0, #0
+ ret
+9:
+ mov x0, #-EFAULT
ret
ENDPROC(flush_icache_range)
ENDPROC(__flush_cache_user_range)
dcache_line_size x2, x3
sub x3, x2, #1
bic x0, x0, x3
-1: dc cvac, x0 // clean D / U line
+1: alternative_insn "dc cvac, x0", "dc civac, x0", ARM64_WORKAROUND_CLEAN_CACHE
add x0, x0, x2
cmp x0, x1
b.lo 1b
--- /dev/null
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ * Debug helper to dump the current kernel pagetables of the system
+ * so that we can see what the various memory ranges are set to.
+ *
+ * Derived from x86 and arm implementation:
+ * (C) Copyright 2008 Intel Corporation
+ *
+ * Author: Arjan van de Ven <arjan@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+
+#include <asm/fixmap.h>
+#include <asm/pgtable.h>
+
+#define LOWEST_ADDR (UL(0xffffffffffffffff) << VA_BITS)
+
+struct addr_marker {
+ unsigned long start_address;
+ const char *name;
+};
+
+enum address_markers_idx {
+ VMALLOC_START_NR = 0,
+ VMALLOC_END_NR,
+#ifdef CONFIG_SPARSEMEM_VMEMMAP
+ VMEMMAP_START_NR,
+ VMEMMAP_END_NR,
+#endif
+ PCI_START_NR,
+ PCI_END_NR,
+ FIXADDR_START_NR,
+ FIXADDR_END_NR,
+ MODULES_START_NR,
+ MODUELS_END_NR,
+ KERNEL_SPACE_NR,
+};
+
+static struct addr_marker address_markers[] = {
+ { VMALLOC_START, "vmalloc() Area" },
+ { VMALLOC_END, "vmalloc() End" },
+#ifdef CONFIG_SPARSEMEM_VMEMMAP
+ { 0, "vmemmap start" },
+ { 0, "vmemmap end" },
+#endif
+ { (unsigned long) PCI_IOBASE, "PCI I/O start" },
+ { (unsigned long) PCI_IOBASE + SZ_16M, "PCI I/O end" },
+ { FIXADDR_START, "Fixmap start" },
+ { FIXADDR_TOP, "Fixmap end" },
+ { MODULES_VADDR, "Modules start" },
+ { MODULES_END, "Modules end" },
+ { PAGE_OFFSET, "Kernel Mapping" },
+ { -1, NULL },
+};
+
+struct pg_state {
+ struct seq_file *seq;
+ const struct addr_marker *marker;
+ unsigned long start_address;
+ unsigned level;
+ u64 current_prot;
+};
+
+struct prot_bits {
+ u64 mask;
+ u64 val;
+ const char *set;
+ const char *clear;
+};
+
+static const struct prot_bits pte_bits[] = {
+ {
+ .mask = PTE_USER,
+ .val = PTE_USER,
+ .set = "USR",
+ .clear = " ",
+ }, {
+ .mask = PTE_RDONLY,
+ .val = PTE_RDONLY,
+ .set = "ro",
+ .clear = "RW",
+ }, {
+ .mask = PTE_PXN,
+ .val = PTE_PXN,
+ .set = "NX",
+ .clear = "x ",
+ }, {
+ .mask = PTE_SHARED,
+ .val = PTE_SHARED,
+ .set = "SHD",
+ .clear = " ",
+ }, {
+ .mask = PTE_AF,
+ .val = PTE_AF,
+ .set = "AF",
+ .clear = " ",
+ }, {
+ .mask = PTE_NG,
+ .val = PTE_NG,
+ .set = "NG",
+ .clear = " ",
+ }, {
+ .mask = PTE_UXN,
+ .val = PTE_UXN,
+ .set = "UXN",
+ }, {
+ .mask = PTE_ATTRINDX_MASK,
+ .val = PTE_ATTRINDX(MT_DEVICE_nGnRnE),
+ .set = "DEVICE/nGnRnE",
+ }, {
+ .mask = PTE_ATTRINDX_MASK,
+ .val = PTE_ATTRINDX(MT_DEVICE_nGnRE),
+ .set = "DEVICE/nGnRE",
+ }, {
+ .mask = PTE_ATTRINDX_MASK,
+ .val = PTE_ATTRINDX(MT_DEVICE_GRE),
+ .set = "DEVICE/GRE",
+ }, {
+ .mask = PTE_ATTRINDX_MASK,
+ .val = PTE_ATTRINDX(MT_NORMAL_NC),
+ .set = "MEM/NORMAL-NC",
+ }, {
+ .mask = PTE_ATTRINDX_MASK,
+ .val = PTE_ATTRINDX(MT_NORMAL),
+ .set = "MEM/NORMAL",
+ }
+};
+
+struct pg_level {
+ const struct prot_bits *bits;
+ size_t num;
+ u64 mask;
+};
+
+static struct pg_level pg_level[] = {
+ {
+ }, { /* pgd */
+ .bits = pte_bits,
+ .num = ARRAY_SIZE(pte_bits),
+ }, { /* pud */
+ .bits = pte_bits,
+ .num = ARRAY_SIZE(pte_bits),
+ }, { /* pmd */
+ .bits = pte_bits,
+ .num = ARRAY_SIZE(pte_bits),
+ }, { /* pte */
+ .bits = pte_bits,
+ .num = ARRAY_SIZE(pte_bits),
+ },
+};
+
+static void dump_prot(struct pg_state *st, const struct prot_bits *bits,
+ size_t num)
+{
+ unsigned i;
+
+ for (i = 0; i < num; i++, bits++) {
+ const char *s;
+
+ if ((st->current_prot & bits->mask) == bits->val)
+ s = bits->set;
+ else
+ s = bits->clear;
+
+ if (s)
+ seq_printf(st->seq, " %s", s);
+ }
+}
+
+static void note_page(struct pg_state *st, unsigned long addr, unsigned level,
+ u64 val)
+{
+ static const char units[] = "KMGTPE";
+ u64 prot = val & pg_level[level].mask;
+
+ if (addr < LOWEST_ADDR)
+ return;
+
+ if (!st->level) {
+ st->level = level;
+ st->current_prot = prot;
+ st->start_address = addr;
+ seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
+ } else if (prot != st->current_prot || level != st->level ||
+ addr >= st->marker[1].start_address) {
+ const char *unit = units;
+ unsigned long delta;
+
+ if (st->current_prot) {
+ seq_printf(st->seq, "0x%16lx-0x%16lx ",
+ st->start_address, addr);
+
+ delta = (addr - st->start_address) >> 10;
+ while (!(delta & 1023) && unit[1]) {
+ delta >>= 10;
+ unit++;
+ }
+ seq_printf(st->seq, "%9lu%c", delta, *unit);
+ if (pg_level[st->level].bits)
+ dump_prot(st, pg_level[st->level].bits,
+ pg_level[st->level].num);
+ seq_puts(st->seq, "\n");
+ }
+
+ if (addr >= st->marker[1].start_address) {
+ st->marker++;
+ seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
+ }
+
+ st->start_address = addr;
+ st->current_prot = prot;
+ st->level = level;
+ }
+
+ if (addr >= st->marker[1].start_address) {
+ st->marker++;
+ seq_printf(st->seq, "---[ %s ]---\n", st->marker->name);
+ }
+
+}
+
+static void walk_pte(struct pg_state *st, pmd_t *pmd, unsigned long start)
+{
+ pte_t *pte = pte_offset_kernel(pmd, 0);
+ unsigned long addr;
+ unsigned i;
+
+ for (i = 0; i < PTRS_PER_PTE; i++, pte++) {
+ addr = start + i * PAGE_SIZE;
+ note_page(st, addr, 4, pte_val(*pte));
+ }
+}
+
+static void walk_pmd(struct pg_state *st, pud_t *pud, unsigned long start)
+{
+ pmd_t *pmd = pmd_offset(pud, 0);
+ unsigned long addr;
+ unsigned i;
+
+ for (i = 0; i < PTRS_PER_PMD; i++, pmd++) {
+ addr = start + i * PMD_SIZE;
+ if (pmd_none(*pmd) || pmd_sect(*pmd) || pmd_bad(*pmd))
+ note_page(st, addr, 3, pmd_val(*pmd));
+ else
+ walk_pte(st, pmd, addr);
+ }
+}
+
+static void walk_pud(struct pg_state *st, pgd_t *pgd, unsigned long start)
+{
+ pud_t *pud = pud_offset(pgd, 0);
+ unsigned long addr;
+ unsigned i;
+
+ for (i = 0; i < PTRS_PER_PUD; i++, pud++) {
+ addr = start + i * PUD_SIZE;
+ if (pud_none(*pud) || pud_sect(*pud) || pud_bad(*pud))
+ note_page(st, addr, 2, pud_val(*pud));
+ else
+ walk_pmd(st, pud, addr);
+ }
+}
+
+static void walk_pgd(struct pg_state *st, struct mm_struct *mm, unsigned long start)
+{
+ pgd_t *pgd = pgd_offset(mm, 0);
+ unsigned i;
+ unsigned long addr;
+
+ for (i = 0; i < PTRS_PER_PGD; i++, pgd++) {
+ addr = start + i * PGDIR_SIZE;
+ if (pgd_none(*pgd) || pgd_bad(*pgd))
+ note_page(st, addr, 1, pgd_val(*pgd));
+ else
+ walk_pud(st, pgd, addr);
+ }
+}
+
+static int ptdump_show(struct seq_file *m, void *v)
+{
+ struct pg_state st = {
+ .seq = m,
+ .marker = address_markers,
+ };
+
+ walk_pgd(&st, &init_mm, LOWEST_ADDR);
+
+ note_page(&st, 0, 0, 0);
+ return 0;
+}
+
+static int ptdump_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ptdump_show, NULL);
+}
+
+static const struct file_operations ptdump_fops = {
+ .open = ptdump_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int ptdump_init(void)
+{
+ struct dentry *pe;
+ unsigned i, j;
+
+ for (i = 0; i < ARRAY_SIZE(pg_level); i++)
+ if (pg_level[i].bits)
+ for (j = 0; j < pg_level[i].num; j++)
+ pg_level[i].mask |= pg_level[i].bits[j].mask;
+
+ address_markers[VMEMMAP_START_NR].start_address =
+ (unsigned long)virt_to_page(PAGE_OFFSET);
+ address_markers[VMEMMAP_END_NR].start_address =
+ (unsigned long)virt_to_page(high_memory);
+
+ pe = debugfs_create_file("kernel_page_tables", 0400, NULL, NULL,
+ &ptdump_fops);
+ return pe ? 0 : -ENOMEM;
+}
+device_initcall(ptdump_init);
{ do_bad, SIGBUS, 0, "level 1 address size fault" },
{ do_bad, SIGBUS, 0, "level 2 address size fault" },
{ do_bad, SIGBUS, 0, "level 3 address size fault" },
- { do_translation_fault, SIGSEGV, SEGV_MAPERR, "input address range fault" },
+ { do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 0 translation fault" },
{ do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 1 translation fault" },
{ do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 2 translation fault" },
{ do_page_fault, SIGSEGV, SEGV_MAPERR, "level 3 translation fault" },
#include <asm/setup.h>
#include <asm/sizes.h>
#include <asm/tlb.h>
+#include <asm/alternative.h>
#include "mm.h"
void free_initmem(void)
{
free_initmem_default(0);
+ free_alternatives_memory();
}
#ifdef CONFIG_BLK_DEV_INITRD
}
EXPORT_SYMBOL(ioremap_cache);
-static pte_t bm_pte[PTRS_PER_PTE] __page_aligned_bss;
-#if CONFIG_ARM64_PGTABLE_LEVELS > 2
-static pmd_t bm_pmd[PTRS_PER_PMD] __page_aligned_bss;
-#endif
-#if CONFIG_ARM64_PGTABLE_LEVELS > 3
-static pud_t bm_pud[PTRS_PER_PUD] __page_aligned_bss;
-#endif
-
-static inline pud_t * __init early_ioremap_pud(unsigned long addr)
-{
- pgd_t *pgd;
-
- pgd = pgd_offset_k(addr);
- BUG_ON(pgd_none(*pgd) || pgd_bad(*pgd));
-
- return pud_offset(pgd, addr);
-}
-
-static inline pmd_t * __init early_ioremap_pmd(unsigned long addr)
-{
- pud_t *pud = early_ioremap_pud(addr);
-
- BUG_ON(pud_none(*pud) || pud_bad(*pud));
-
- return pmd_offset(pud, addr);
-}
-
-static inline pte_t * __init early_ioremap_pte(unsigned long addr)
-{
- pmd_t *pmd = early_ioremap_pmd(addr);
-
- BUG_ON(pmd_none(*pmd) || pmd_bad(*pmd));
-
- return pte_offset_kernel(pmd, addr);
-}
-
+/*
+ * Must be called after early_fixmap_init
+ */
void __init early_ioremap_init(void)
{
- pgd_t *pgd;
- pud_t *pud;
- pmd_t *pmd;
- unsigned long addr = fix_to_virt(FIX_BTMAP_BEGIN);
-
- pgd = pgd_offset_k(addr);
- pgd_populate(&init_mm, pgd, bm_pud);
- pud = pud_offset(pgd, addr);
- pud_populate(&init_mm, pud, bm_pmd);
- pmd = pmd_offset(pud, addr);
- pmd_populate_kernel(&init_mm, pmd, bm_pte);
-
- /*
- * The boot-ioremap range spans multiple pmds, for which
- * we are not prepared:
- */
- BUILD_BUG_ON((__fix_to_virt(FIX_BTMAP_BEGIN) >> PMD_SHIFT)
- != (__fix_to_virt(FIX_BTMAP_END) >> PMD_SHIFT));
-
- if (pmd != early_ioremap_pmd(fix_to_virt(FIX_BTMAP_END))) {
- WARN_ON(1);
- pr_warn("pmd %p != %p\n",
- pmd, early_ioremap_pmd(fix_to_virt(FIX_BTMAP_END)));
- pr_warn("fix_to_virt(FIX_BTMAP_BEGIN): %08lx\n",
- fix_to_virt(FIX_BTMAP_BEGIN));
- pr_warn("fix_to_virt(FIX_BTMAP_END): %08lx\n",
- fix_to_virt(FIX_BTMAP_END));
-
- pr_warn("FIX_BTMAP_END: %d\n", FIX_BTMAP_END);
- pr_warn("FIX_BTMAP_BEGIN: %d\n",
- FIX_BTMAP_BEGIN);
- }
-
early_ioremap_setup();
}
-
-void __init __early_set_fixmap(enum fixed_addresses idx,
- phys_addr_t phys, pgprot_t flags)
-{
- unsigned long addr = __fix_to_virt(idx);
- pte_t *pte;
-
- if (idx >= __end_of_fixed_addresses) {
- BUG();
- return;
- }
-
- pte = early_ioremap_pte(addr);
-
- if (pgprot_val(flags))
- set_pte(pte, pfn_pte(phys >> PAGE_SHIFT, flags));
- else {
- pte_clear(&init_mm, addr, pte);
- flush_tlb_kernel_range(addr, addr+PAGE_SIZE);
- }
-}
extern void __init bootmem_init(void);
-extern void __init arm64_swiotlb_init(void);
return sysctl_legacy_va_layout;
}
-/*
- * Since get_random_int() returns the same value within a 1 jiffy window, we
- * will almost always get the same randomisation for the stack and mmap
- * region. This will mean the relative distance between stack and mmap will be
- * the same.
- *
- * To avoid this we can shift the randomness by 1 bit.
- */
static unsigned long mmap_rnd(void)
{
unsigned long rnd = 0;
if (current->flags & PF_RANDOMIZE)
- rnd = (long)get_random_int() & (STACK_RND_MASK >> 1);
+ rnd = (long)get_random_int() & STACK_RND_MASK;
- return rnd << (PAGE_SHIFT + 1);
+ return rnd << PAGE_SHIFT;
}
static unsigned long mmap_base(void)
#include <linux/io.h>
#include <asm/cputype.h>
+#include <asm/fixmap.h>
#include <asm/sections.h>
#include <asm/setup.h>
#include <asm/sizes.h>
}
static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr,
- unsigned long end, unsigned long phys,
+ unsigned long end, phys_addr_t phys,
int map_io)
{
pud_t *pud;
{
}
#endif /* CONFIG_SPARSEMEM_VMEMMAP */
+
+static pte_t bm_pte[PTRS_PER_PTE] __page_aligned_bss;
+#if CONFIG_ARM64_PGTABLE_LEVELS > 2
+static pmd_t bm_pmd[PTRS_PER_PMD] __page_aligned_bss;
+#endif
+#if CONFIG_ARM64_PGTABLE_LEVELS > 3
+static pud_t bm_pud[PTRS_PER_PUD] __page_aligned_bss;
+#endif
+
+static inline pud_t * fixmap_pud(unsigned long addr)
+{
+ pgd_t *pgd = pgd_offset_k(addr);
+
+ BUG_ON(pgd_none(*pgd) || pgd_bad(*pgd));
+
+ return pud_offset(pgd, addr);
+}
+
+static inline pmd_t * fixmap_pmd(unsigned long addr)
+{
+ pud_t *pud = fixmap_pud(addr);
+
+ BUG_ON(pud_none(*pud) || pud_bad(*pud));
+
+ return pmd_offset(pud, addr);
+}
+
+static inline pte_t * fixmap_pte(unsigned long addr)
+{
+ pmd_t *pmd = fixmap_pmd(addr);
+
+ BUG_ON(pmd_none(*pmd) || pmd_bad(*pmd));
+
+ return pte_offset_kernel(pmd, addr);
+}
+
+void __init early_fixmap_init(void)
+{
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd;
+ unsigned long addr = FIXADDR_START;
+
+ pgd = pgd_offset_k(addr);
+ pgd_populate(&init_mm, pgd, bm_pud);
+ pud = pud_offset(pgd, addr);
+ pud_populate(&init_mm, pud, bm_pmd);
+ pmd = pmd_offset(pud, addr);
+ pmd_populate_kernel(&init_mm, pmd, bm_pte);
+
+ /*
+ * The boot-ioremap range spans multiple pmds, for which
+ * we are not preparted:
+ */
+ BUILD_BUG_ON((__fix_to_virt(FIX_BTMAP_BEGIN) >> PMD_SHIFT)
+ != (__fix_to_virt(FIX_BTMAP_END) >> PMD_SHIFT));
+
+ if ((pmd != fixmap_pmd(fix_to_virt(FIX_BTMAP_BEGIN)))
+ || pmd != fixmap_pmd(fix_to_virt(FIX_BTMAP_END))) {
+ WARN_ON(1);
+ pr_warn("pmd %p != %p, %p\n",
+ pmd, fixmap_pmd(fix_to_virt(FIX_BTMAP_BEGIN)),
+ fixmap_pmd(fix_to_virt(FIX_BTMAP_END)));
+ pr_warn("fix_to_virt(FIX_BTMAP_BEGIN): %08lx\n",
+ fix_to_virt(FIX_BTMAP_BEGIN));
+ pr_warn("fix_to_virt(FIX_BTMAP_END): %08lx\n",
+ fix_to_virt(FIX_BTMAP_END));
+
+ pr_warn("FIX_BTMAP_END: %d\n", FIX_BTMAP_END);
+ pr_warn("FIX_BTMAP_BEGIN: %d\n", FIX_BTMAP_BEGIN);
+ }
+}
+
+void __set_fixmap(enum fixed_addresses idx,
+ phys_addr_t phys, pgprot_t flags)
+{
+ unsigned long addr = __fix_to_virt(idx);
+ pte_t *pte;
+
+ if (idx >= __end_of_fixed_addresses) {
+ BUG();
+ return;
+ }
+
+ pte = fixmap_pte(addr);
+
+ if (pgprot_val(flags)) {
+ set_pte(pte, pfn_pte(phys >> PAGE_SHIFT, flags));
+ } else {
+ pte_clear(&init_mm, addr, pte);
+ flush_tlb_kernel_range(addr, addr+PAGE_SIZE);
+ }
+}
pgd_t *pgd_alloc(struct mm_struct *mm)
{
if (PGD_SIZE == PAGE_SIZE)
- return (pgd_t *)get_zeroed_page(GFP_KERNEL);
+ return (pgd_t *)__get_free_page(PGALLOC_GFP);
else
- return kmem_cache_zalloc(pgd_cache, GFP_KERNEL);
+ return kmem_cache_alloc(pgd_cache, PGALLOC_GFP);
}
void pgd_free(struct mm_struct *mm, pgd_t *pgd)
const struct bpf_prog *prog;
int idx;
int tmp_used;
- int body_offset;
+ int epilogue_offset;
int *offset;
u32 *image;
};
static inline int epilogue_offset(const struct jit_ctx *ctx)
{
- int to = ctx->offset[ctx->prog->len - 1];
- int from = ctx->idx - ctx->body_offset;
+ int to = ctx->epilogue_offset;
+ int from = ctx->idx;
return to - from;
}
}
/* function return */
case BPF_JMP | BPF_EXIT:
+ /* Optimization: when last instruction is EXIT,
+ simply fallthrough to epilogue. */
if (i == ctx->prog->len - 1)
break;
jmp_offset = epilogue_offset(ctx);
/* 1. Initial fake pass to compute ctx->idx. */
- /* Fake pass to fill in ctx->offset. */
+ /* Fake pass to fill in ctx->offset and ctx->tmp_used. */
if (build_body(&ctx))
goto out;
build_prologue(&ctx);
+
+ ctx.epilogue_offset = ctx.idx;
build_epilogue(&ctx);
/* Now we know the actual image size. */
build_prologue(&ctx);
- ctx.body_offset = ctx.idx;
if (build_body(&ctx)) {
bpf_jit_binary_free(header);
goto out;
#include <linux/spi/spi.h>
#include <linux/usb/atmel_usba_udc.h>
-#include <mach/atmel-mci.h>
+#include <linux/platform_data/mmc-atmel-mci.h>
#include <linux/atmel-mci.h>
#include <asm/io.h>
+++ /dev/null
-#ifndef __MACH_ATMEL_MCI_H
-#define __MACH_ATMEL_MCI_H
-
-#include <linux/platform_data/dma-dw.h>
-
-/**
- * struct mci_dma_data - DMA data for MCI interface
- */
-struct mci_dma_data {
- struct dw_dma_slave sdata;
-};
-
-/* accessor macros */
-#define slave_data_ptr(s) (&(s)->sdata)
-#define find_slave_dev(s) ((s)->sdata.dma_dev)
-
-#endif /* __MACH_ATMEL_MCI_H */
for (i = 0; i < npages; i++) {
pfn = gfn_to_pfn(kvm, base_gfn + i);
- if (!kvm_is_mmio_pfn(pfn)) {
+ if (!kvm_is_reserved_pfn(pfn)) {
kvm_set_pmt_entry(kvm, base_gfn + i,
pfn << PAGE_SHIFT,
_PAGE_AR_RWX | _PAGE_MA_WB);
};
#endif /* CONFIG_ATARI_ETHERNEC */
+#ifdef CONFIG_ATARI_SCSI
+static const struct resource atari_scsi_st_rsrc[] __initconst = {
+ {
+ .flags = IORESOURCE_IRQ,
+ .start = IRQ_MFP_FSCSI,
+ .end = IRQ_MFP_FSCSI,
+ },
+};
+
+static const struct resource atari_scsi_tt_rsrc[] __initconst = {
+ {
+ .flags = IORESOURCE_IRQ,
+ .start = IRQ_TT_MFP_SCSI,
+ .end = IRQ_TT_MFP_SCSI,
+ },
+};
+#endif
+
int __init atari_platform_init(void)
{
int rv = 0;
}
#endif
+#ifdef CONFIG_ATARI_SCSI
+ if (ATARIHW_PRESENT(ST_SCSI))
+ platform_device_register_simple("atari_scsi", -1,
+ atari_scsi_st_rsrc, ARRAY_SIZE(atari_scsi_st_rsrc));
+ else if (ATARIHW_PRESENT(TT_SCSI))
+ platform_device_register_simple("atari_scsi", -1,
+ atari_scsi_tt_rsrc, ARRAY_SIZE(atari_scsi_tt_rsrc));
+#endif
+
return rv;
}
/************************* End of Prototypes **************************/
+/**
+ * stdma_try_lock - attempt to acquire ST DMA interrupt "lock"
+ * @handler: interrupt handler to use after acquisition
+ *
+ * Returns !0 if lock was acquired; otherwise 0.
+ */
+
+int stdma_try_lock(irq_handler_t handler, void *data)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ if (stdma_locked) {
+ local_irq_restore(flags);
+ return 0;
+ }
+
+ stdma_locked = 1;
+ stdma_isr = handler;
+ stdma_isr_data = data;
+ local_irq_restore(flags);
+ return 1;
+}
+EXPORT_SYMBOL(stdma_try_lock);
+
/*
* Function: void stdma_lock( isrfunc isr, void *data )
void stdma_lock(irq_handler_t handler, void *data)
{
- unsigned long flags;
-
- local_irq_save(flags); /* protect lock */
-
/* Since the DMA is used for file system purposes, we
have to sleep uninterruptible (there may be locked
buffers) */
- wait_event(stdma_wait, !stdma_locked);
-
- stdma_locked = 1;
- stdma_isr = handler;
- stdma_isr_data = data;
- local_irq_restore(flags);
+ wait_event(stdma_wait, stdma_try_lock(handler, data));
}
EXPORT_SYMBOL(stdma_lock);
EXPORT_SYMBOL(stdma_release);
-/*
- * Function: int stdma_others_waiting( void )
- *
- * Purpose: Check if someone waits for the ST-DMA lock.
- *
- * Inputs: none
- *
- * Returns: 0 if no one is waiting, != 0 otherwise
+/**
+ * stdma_is_locked_by - allow lock holder to check whether it needs to release.
+ * @handler: interrupt handler previously used to acquire lock.
*
+ * Returns !0 if locked for the given handler; 0 otherwise.
*/
-int stdma_others_waiting(void)
+int stdma_is_locked_by(irq_handler_t handler)
{
- return waitqueue_active(&stdma_wait);
+ unsigned long flags;
+ int result;
+
+ local_irq_save(flags);
+ result = stdma_locked && (stdma_isr == handler);
+ local_irq_restore(flags);
+
+ return result;
}
-EXPORT_SYMBOL(stdma_others_waiting);
+EXPORT_SYMBOL(stdma_is_locked_by);
/*
/***************************** Prototypes *****************************/
+int stdma_try_lock(irq_handler_t, void *);
void stdma_lock(irq_handler_t handler, void *data);
void stdma_release( void );
-int stdma_others_waiting( void );
int stdma_islocked( void );
-void *stdma_locked_by( void );
+int stdma_is_locked_by(irq_handler_t);
void stdma_init( void );
/************************* End of Prototypes **************************/
#define MAC_SCSI_QUADRA 2
#define MAC_SCSI_QUADRA2 3
#define MAC_SCSI_QUADRA3 4
+#define MAC_SCSI_IIFX 5
+#define MAC_SCSI_DUO 6
+#define MAC_SCSI_CCL 7
+#define MAC_SCSI_LATE 8
#define MAC_IDE_NONE 0
#define MAC_IDE_QUADRA 1
#include <uapi/asm/unistd.h>
-#define NR_syscalls 354
+#define NR_syscalls 355
#define __ARCH_WANT_OLD_READDIR
#define __ARCH_WANT_OLD_STAT
#define __NR_renameat2 351
#define __NR_getrandom 352
#define __NR_memfd_create 353
+#define __NR_bpf 354
#endif /* _UAPI_ASM_M68K_UNISTD_H_ */
.long sys_renameat2
.long sys_getrandom
.long sys_memfd_create
+ .long sys_bpf
.name = "IIfx",
.adb_type = MAC_ADB_IOP,
.via_type = MAC_VIA_IICI,
- .scsi_type = MAC_SCSI_OLD,
+ .scsi_type = MAC_SCSI_IIFX,
.scc_type = MAC_SCC_IOP,
.nubus_type = MAC_NUBUS,
.floppy_type = MAC_FLOPPY_SWIM_IOP,
.name = "Color Classic",
.adb_type = MAC_ADB_CUDA,
.via_type = MAC_VIA_IICI,
- .scsi_type = MAC_SCSI_OLD,
+ .scsi_type = MAC_SCSI_CCL,
.scc_type = MAC_SCC_II,
.nubus_type = MAC_NUBUS,
.floppy_type = MAC_FLOPPY_SWIM_ADDR2,
.name = "Color Classic II",
.adb_type = MAC_ADB_CUDA,
.via_type = MAC_VIA_IICI,
- .scsi_type = MAC_SCSI_OLD,
+ .scsi_type = MAC_SCSI_CCL,
.scc_type = MAC_SCC_II,
.nubus_type = MAC_NUBUS,
.floppy_type = MAC_FLOPPY_SWIM_ADDR2,
.name = "Performa 520",
.adb_type = MAC_ADB_CUDA,
.via_type = MAC_VIA_IICI,
- .scsi_type = MAC_SCSI_OLD,
+ .scsi_type = MAC_SCSI_CCL,
.scc_type = MAC_SCC_II,
.nubus_type = MAC_NUBUS,
.floppy_type = MAC_FLOPPY_SWIM_ADDR2,
.name = "Performa 550",
.adb_type = MAC_ADB_CUDA,
.via_type = MAC_VIA_IICI,
- .scsi_type = MAC_SCSI_OLD,
+ .scsi_type = MAC_SCSI_CCL,
.scc_type = MAC_SCC_II,
.nubus_type = MAC_NUBUS,
.floppy_type = MAC_FLOPPY_SWIM_ADDR2,
.name = "TV",
.adb_type = MAC_ADB_CUDA,
.via_type = MAC_VIA_IICI,
- .scsi_type = MAC_SCSI_OLD,
+ .scsi_type = MAC_SCSI_CCL,
.scc_type = MAC_SCC_II,
.nubus_type = MAC_NUBUS,
.floppy_type = MAC_FLOPPY_SWIM_ADDR2,
.name = "PowerBook 190",
.adb_type = MAC_ADB_PB2,
.via_type = MAC_VIA_QUADRA,
- .scsi_type = MAC_SCSI_OLD,
+ .scsi_type = MAC_SCSI_LATE,
.ide_type = MAC_IDE_BABOON,
.scc_type = MAC_SCC_QUADRA,
.nubus_type = MAC_NUBUS,
.name = "PowerBook 520",
.adb_type = MAC_ADB_PB2,
.via_type = MAC_VIA_QUADRA,
- .scsi_type = MAC_SCSI_OLD,
+ .scsi_type = MAC_SCSI_LATE,
.scc_type = MAC_SCC_QUADRA,
.ether_type = MAC_ETHER_SONIC,
.nubus_type = MAC_NUBUS,
.name = "PowerBook Duo 210",
.adb_type = MAC_ADB_PB2,
.via_type = MAC_VIA_IICI,
- .scsi_type = MAC_SCSI_OLD,
+ .scsi_type = MAC_SCSI_DUO,
.scc_type = MAC_SCC_QUADRA,
.nubus_type = MAC_NUBUS,
.floppy_type = MAC_FLOPPY_SWIM_ADDR2,
.name = "PowerBook Duo 230",
.adb_type = MAC_ADB_PB2,
.via_type = MAC_VIA_IICI,
- .scsi_type = MAC_SCSI_OLD,
+ .scsi_type = MAC_SCSI_DUO,
.scc_type = MAC_SCC_QUADRA,
.nubus_type = MAC_NUBUS,
.floppy_type = MAC_FLOPPY_SWIM_ADDR2,
.name = "PowerBook Duo 250",
.adb_type = MAC_ADB_PB2,
.via_type = MAC_VIA_IICI,
- .scsi_type = MAC_SCSI_OLD,
+ .scsi_type = MAC_SCSI_DUO,
.scc_type = MAC_SCC_QUADRA,
.nubus_type = MAC_NUBUS,
.floppy_type = MAC_FLOPPY_SWIM_ADDR2,
.name = "PowerBook Duo 270c",
.adb_type = MAC_ADB_PB2,
.via_type = MAC_VIA_IICI,
- .scsi_type = MAC_SCSI_OLD,
+ .scsi_type = MAC_SCSI_DUO,
.scc_type = MAC_SCC_QUADRA,
.nubus_type = MAC_NUBUS,
.floppy_type = MAC_FLOPPY_SWIM_ADDR2,
.name = "PowerBook Duo 280",
.adb_type = MAC_ADB_PB2,
.via_type = MAC_VIA_IICI,
- .scsi_type = MAC_SCSI_OLD,
+ .scsi_type = MAC_SCSI_DUO,
.scc_type = MAC_SCC_QUADRA,
.nubus_type = MAC_NUBUS,
.floppy_type = MAC_FLOPPY_SWIM_ADDR2,
.name = "PowerBook Duo 280c",
.adb_type = MAC_ADB_PB2,
.via_type = MAC_VIA_IICI,
- .scsi_type = MAC_SCSI_OLD,
+ .scsi_type = MAC_SCSI_DUO,
.scc_type = MAC_SCC_QUADRA,
.nubus_type = MAC_NUBUS,
.floppy_type = MAC_FLOPPY_SWIM_ADDR2,
.resource = &swim_rsrc,
};
+static const struct resource mac_scsi_iifx_rsrc[] __initconst = {
+ {
+ .flags = IORESOURCE_IRQ,
+ .start = IRQ_MAC_SCSI,
+ .end = IRQ_MAC_SCSI,
+ }, {
+ .flags = IORESOURCE_MEM,
+ .start = 0x50008000,
+ .end = 0x50009FFF,
+ },
+};
+
+static const struct resource mac_scsi_duo_rsrc[] __initconst = {
+ {
+ .flags = IORESOURCE_MEM,
+ .start = 0xFEE02000,
+ .end = 0xFEE03FFF,
+ },
+};
+
+static const struct resource mac_scsi_old_rsrc[] __initconst = {
+ {
+ .flags = IORESOURCE_IRQ,
+ .start = IRQ_MAC_SCSI,
+ .end = IRQ_MAC_SCSI,
+ }, {
+ .flags = IORESOURCE_MEM,
+ .start = 0x50010000,
+ .end = 0x50011FFF,
+ }, {
+ .flags = IORESOURCE_MEM,
+ .start = 0x50006000,
+ .end = 0x50007FFF,
+ },
+};
+
+static const struct resource mac_scsi_late_rsrc[] __initconst = {
+ {
+ .flags = IORESOURCE_IRQ,
+ .start = IRQ_MAC_SCSI,
+ .end = IRQ_MAC_SCSI,
+ }, {
+ .flags = IORESOURCE_MEM,
+ .start = 0x50010000,
+ .end = 0x50011FFF,
+ },
+};
+
+static const struct resource mac_scsi_ccl_rsrc[] __initconst = {
+ {
+ .flags = IORESOURCE_IRQ,
+ .start = IRQ_MAC_SCSI,
+ .end = IRQ_MAC_SCSI,
+ }, {
+ .flags = IORESOURCE_MEM,
+ .start = 0x50F10000,
+ .end = 0x50F11FFF,
+ }, {
+ .flags = IORESOURCE_MEM,
+ .start = 0x50F06000,
+ .end = 0x50F07FFF,
+ },
+};
+
static struct platform_device esp_0_pdev = {
.name = "mac_esp",
.id = 0,
(macintosh_config->ident == MAC_MODEL_Q950))
platform_device_register(&esp_1_pdev);
break;
+ case MAC_SCSI_IIFX:
+ /* Addresses from The Guide to Mac Family Hardware.
+ * $5000 8000 - $5000 9FFF: SCSI DMA
+ * $5000 C000 - $5000 DFFF: Alternate SCSI (DMA)
+ * $5000 E000 - $5000 FFFF: Alternate SCSI (Hsk)
+ * The SCSI DMA custom IC embeds the 53C80 core. mac_scsi does
+ * not make use of its DMA or hardware handshaking logic.
+ */
+ platform_device_register_simple("mac_scsi", 0,
+ mac_scsi_iifx_rsrc, ARRAY_SIZE(mac_scsi_iifx_rsrc));
+ break;
+ case MAC_SCSI_DUO:
+ /* Addresses from the Duo Dock II Developer Note.
+ * $FEE0 2000 - $FEE0 3FFF: normal mode
+ * $FEE0 4000 - $FEE0 5FFF: pseudo DMA without /DRQ
+ * $FEE0 6000 - $FEE0 7FFF: pseudo DMA with /DRQ
+ * The NetBSD code indicates that both 5380 chips share
+ * an IRQ (?) which would need careful handling (see mac_esp).
+ */
+ platform_device_register_simple("mac_scsi", 1,
+ mac_scsi_duo_rsrc, ARRAY_SIZE(mac_scsi_duo_rsrc));
+ /* fall through */
+ case MAC_SCSI_OLD:
+ /* Addresses from Developer Notes for Duo System,
+ * PowerBook 180 & 160, 140 & 170, Macintosh IIsi
+ * and also from The Guide to Mac Family Hardware for
+ * SE/30, II, IIx, IIcx, IIci.
+ * $5000 6000 - $5000 7FFF: pseudo-DMA with /DRQ
+ * $5001 0000 - $5001 1FFF: normal mode
+ * $5001 2000 - $5001 3FFF: pseudo-DMA without /DRQ
+ * GMFH says that $5000 0000 - $50FF FFFF "wraps
+ * $5000 0000 - $5001 FFFF eight times" (!)
+ * mess.org says IIci and Color Classic do not alias
+ * I/O address space.
+ */
+ platform_device_register_simple("mac_scsi", 0,
+ mac_scsi_old_rsrc, ARRAY_SIZE(mac_scsi_old_rsrc));
+ break;
+ case MAC_SCSI_LATE:
+ /* PDMA logic in 68040 PowerBooks is somehow different to
+ * '030 models. It's probably more like Quadras (see mac_esp).
+ */
+ platform_device_register_simple("mac_scsi", 0,
+ mac_scsi_late_rsrc, ARRAY_SIZE(mac_scsi_late_rsrc));
+ break;
+ case MAC_SCSI_CCL:
+ /* Addresses from the Color Classic Developer Note.
+ * $50F0 6000 - $50F0 7FFF: SCSI handshake
+ * $50F1 0000 - $50F1 1FFF: SCSI
+ * $50F1 2000 - $50F1 3FFF: SCSI DMA
+ */
+ platform_device_register_simple("mac_scsi", 0,
+ mac_scsi_ccl_rsrc, ARRAY_SIZE(mac_scsi_ccl_rsrc));
+ break;
}
/*
high_memory = (void *) end_mem;
empty_zero_page = alloc_bootmem_pages(PAGE_SIZE);
- memset(empty_zero_page, 0, PAGE_SIZE);
/*
* Set up SFC/DFC registers (user data space).
#include <linux/console.h>
#include <linux/init.h>
#include <linux/bootmem.h>
+#include <linux/platform_device.h>
#include <asm/oplib.h>
#include <asm/setup.h>
#include <asm/sun3mmu.h>
#include <asm/rtc.h>
#include <asm/machdep.h>
+#include <asm/machines.h>
#include <asm/idprom.h>
#include <asm/intersil.h>
#include <asm/irq.h>
intersil_clear();
}
+#ifdef CONFIG_SUN3_SCSI
+
+static const struct resource sun3_scsi_vme_rsrc[] __initconst = {
+ {
+ .flags = IORESOURCE_IRQ,
+ .start = SUN3_VEC_VMESCSI0,
+ .end = SUN3_VEC_VMESCSI0,
+ }, {
+ .flags = IORESOURCE_MEM,
+ .start = 0xff200000,
+ .end = 0xff200021,
+ }, {
+ .flags = IORESOURCE_IRQ,
+ .start = SUN3_VEC_VMESCSI1,
+ .end = SUN3_VEC_VMESCSI1,
+ }, {
+ .flags = IORESOURCE_MEM,
+ .start = 0xff204000,
+ .end = 0xff204021,
+ },
+};
+
+/*
+ * Int: level 2 autovector
+ * IO: type 1, base 0x00140000, 5 bits phys space: A<4..0>
+ */
+static const struct resource sun3_scsi_rsrc[] __initconst = {
+ {
+ .flags = IORESOURCE_IRQ,
+ .start = 2,
+ .end = 2,
+ }, {
+ .flags = IORESOURCE_MEM,
+ .start = 0x00140000,
+ .end = 0x0014001f,
+ },
+};
+
+int __init sun3_platform_init(void)
+{
+ switch (idprom->id_machtype) {
+ case SM_SUN3 | SM_3_160:
+ case SM_SUN3 | SM_3_260:
+ platform_device_register_simple("sun3_scsi_vme", -1,
+ sun3_scsi_vme_rsrc, ARRAY_SIZE(sun3_scsi_vme_rsrc));
+ break;
+ case SM_SUN3 | SM_3_50:
+ case SM_SUN3 | SM_3_60:
+ platform_device_register_simple("sun3_scsi", -1,
+ sun3_scsi_rsrc, ARRAY_SIZE(sun3_scsi_rsrc));
+ break;
+ }
+ return 0;
+}
+
+arch_initcall(sun3_platform_init);
+
+#endif
#define tlb_flush(tlb) flush_tlb_mm((tlb)->mm)
#include <linux/pagemap.h>
-#include <asm-generic/tlb.h>
#ifdef CONFIG_MMU
#define tlb_start_vma(tlb, vma) do { } while (0)
#define __tlb_remove_tlb_entry(tlb, pte, address) do { } while (0)
#endif
+#include <asm-generic/tlb.h>
+
#endif /* _ASM_MICROBLAZE_TLB_H */
config ARCH_PHYS_ADDR_T_64BIT
def_bool 64BIT_PHYS_ADDR
+choice
+ prompt "SmartMIPS or microMIPS ASE support"
+
+config CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS
+ bool "None"
+ help
+ Select this if you want neither microMIPS nor SmartMIPS support
+
config CPU_HAS_SMARTMIPS
depends on SYS_SUPPORTS_SMARTMIPS
- bool "Support for the SmartMIPS ASE"
+ bool "SmartMIPS"
help
SmartMIPS is a extension of the MIPS32 architecture aimed at
increased security at both hardware and software level for
config CPU_MICROMIPS
depends on SYS_SUPPORTS_MICROMIPS
- bool "Build kernel using microMIPS ISA"
+ bool "microMIPS"
help
When this option is enabled the kernel will be built using the
microMIPS ISA
+endchoice
+
config CPU_HAS_MSA
bool "Support for the MIPS SIMD Architecture (EXPERIMENTAL)"
depends on CPU_SUPPORTS_MSA
KBUILD_AFLAGS_MODULE += -mlong-calls
KBUILD_CFLAGS_MODULE += -mlong-calls
+#
+# pass -msoft-float to GAS if it supports it. However on newer binutils
+# (specifically newer than 2.24.51.20140728) we then also need to explicitly
+# set ".set hardfloat" in all files which manipulate floating point registers.
+#
+ifneq ($(call as-option,-Wa$(comma)-msoft-float,),)
+ cflags-y += -DGAS_HAS_SET_HARDFLOAT -Wa,-msoft-float
+endif
+
cflags-y += -ffreestanding
#
.irq_set_type = octeon_irq_ciu_gpio_set_type,
#ifdef CONFIG_SMP
.irq_set_affinity = octeon_irq_ciu_set_affinity_v2,
+ .irq_cpu_offline = octeon_irq_cpu_offline_ciu,
#endif
.flags = IRQCHIP_SET_TYPE_MASKED,
};
.irq_set_type = octeon_irq_ciu_gpio_set_type,
#ifdef CONFIG_SMP
.irq_set_affinity = octeon_irq_ciu_set_affinity,
+ .irq_cpu_offline = octeon_irq_cpu_offline_ciu,
#endif
.flags = IRQCHIP_SET_TYPE_MASKED,
};
#include <asm/mipsregs.h>
.macro fpu_save_single thread tmp=t0
+ .set push
+ SET_HARDFLOAT
cfc1 \tmp, fcr31
swc1 $f0, THREAD_FPR0_LS64(\thread)
swc1 $f1, THREAD_FPR1_LS64(\thread)
swc1 $f30, THREAD_FPR30_LS64(\thread)
swc1 $f31, THREAD_FPR31_LS64(\thread)
sw \tmp, THREAD_FCR31(\thread)
+ .set pop
.endm
.macro fpu_restore_single thread tmp=t0
+ .set push
+ SET_HARDFLOAT
lw \tmp, THREAD_FCR31(\thread)
lwc1 $f0, THREAD_FPR0_LS64(\thread)
lwc1 $f1, THREAD_FPR1_LS64(\thread)
lwc1 $f30, THREAD_FPR30_LS64(\thread)
lwc1 $f31, THREAD_FPR31_LS64(\thread)
ctc1 \tmp, fcr31
+ .set pop
.endm
.macro cpu_save_nonscratch thread
#endif /* CONFIG_CPU_MIPSR2 */
.macro fpu_save_16even thread tmp=t0
+ .set push
+ SET_HARDFLOAT
cfc1 \tmp, fcr31
sdc1 $f0, THREAD_FPR0_LS64(\thread)
sdc1 $f2, THREAD_FPR2_LS64(\thread)
sdc1 $f28, THREAD_FPR28_LS64(\thread)
sdc1 $f30, THREAD_FPR30_LS64(\thread)
sw \tmp, THREAD_FCR31(\thread)
+ .set pop
.endm
.macro fpu_save_16odd thread
.set push
.set mips64r2
+ SET_HARDFLOAT
sdc1 $f1, THREAD_FPR1_LS64(\thread)
sdc1 $f3, THREAD_FPR3_LS64(\thread)
sdc1 $f5, THREAD_FPR5_LS64(\thread)
.endm
.macro fpu_restore_16even thread tmp=t0
+ .set push
+ SET_HARDFLOAT
lw \tmp, THREAD_FCR31(\thread)
ldc1 $f0, THREAD_FPR0_LS64(\thread)
ldc1 $f2, THREAD_FPR2_LS64(\thread)
.macro fpu_restore_16odd thread
.set push
.set mips64r2
+ SET_HARDFLOAT
ldc1 $f1, THREAD_FPR1_LS64(\thread)
ldc1 $f3, THREAD_FPR3_LS64(\thread)
ldc1 $f5, THREAD_FPR5_LS64(\thread)
.macro cfcmsa rd, cs
.set push
.set noat
+ SET_HARDFLOAT
.insn
.word CFC_MSA_INSN | (\cs << 11)
move \rd, $1
.macro ctcmsa cd, rs
.set push
.set noat
+ SET_HARDFLOAT
move $1, \rs
.word CTC_MSA_INSN | (\cd << 6)
.set pop
.macro ld_d wd, off, base
.set push
.set noat
+ SET_HARDFLOAT
add $1, \base, \off
.word LDD_MSA_INSN | (\wd << 6)
.set pop
.macro st_d wd, off, base
.set push
.set noat
+ SET_HARDFLOAT
add $1, \base, \off
.word STD_MSA_INSN | (\wd << 6)
.set pop
.macro copy_u_w rd, ws, n
.set push
.set noat
+ SET_HARDFLOAT
.insn
.word COPY_UW_MSA_INSN | (\n << 16) | (\ws << 11)
/* move triggers an assembler bug... */
.macro copy_u_d rd, ws, n
.set push
.set noat
+ SET_HARDFLOAT
.insn
.word COPY_UD_MSA_INSN | (\n << 16) | (\ws << 11)
/* move triggers an assembler bug... */
.macro insert_w wd, n, rs
.set push
.set noat
+ SET_HARDFLOAT
/* move triggers an assembler bug... */
or $1, \rs, zero
.word INSERT_W_MSA_INSN | (\n << 16) | (\wd << 6)
.macro insert_d wd, n, rs
.set push
.set noat
+ SET_HARDFLOAT
/* move triggers an assembler bug... */
or $1, \rs, zero
.word INSERT_D_MSA_INSN | (\n << 16) | (\wd << 6)
st_d 31, THREAD_FPR31, \thread
.set push
.set noat
+ SET_HARDFLOAT
cfcmsa $1, MSA_CSR
sw $1, THREAD_MSA_CSR(\thread)
.set pop
.macro msa_restore_all thread
.set push
.set noat
+ SET_HARDFLOAT
lw $1, THREAD_MSA_CSR(\thread)
ctcmsa MSA_CSR, $1
.set pop
.macro msa_init_all_upper
.set push
.set noat
+ SET_HARDFLOAT
not $1, zero
msa_init_upper 0
.set pop
#include <asm/sgidefs.h>
+/*
+ * starting with binutils 2.24.51.20140729, MIPS binutils warn about mixing
+ * hardfloat and softfloat object files. The kernel build uses soft-float by
+ * default, so we also need to pass -msoft-float along to GAS if it supports it.
+ * But this in turn causes assembler errors in files which access hardfloat
+ * registers. We detect if GAS supports "-msoft-float" in the Makefile and
+ * explicitly put ".set hardfloat" where floating point registers are touched.
+ */
+#ifdef GAS_HAS_SET_HARDFLOAT
+#define SET_HARDFLOAT .set hardfloat
+#else
+#define SET_HARDFLOAT
+#endif
+
#if _MIPS_SIM == _MIPS_SIM_ABI32
/*
if (is_msa_enabled()) {
if (save) {
save_msa(current);
- asm volatile("cfc1 %0, $31"
- : "=r"(current->thread.fpu.fcr31));
+ current->thread.fpu.fcr31 =
+ read_32bit_cp1_register(CP1_STATUS);
}
disable_msa();
clear_thread_flag(TIF_USEDMSA);
#define WORD_INSN ".word"
#endif
+#ifdef CONFIG_CPU_MICROMIPS
+#define NOP_INSN "nop32"
+#else
+#define NOP_INSN "nop"
+#endif
+
static __always_inline bool arch_static_branch(struct static_key *key)
{
- asm_volatile_goto("1:\tnop\n\t"
+ asm_volatile_goto("1:\t" NOP_INSN "\n\t"
"nop\n\t"
".pushsection __jump_table, \"aw\"\n\t"
WORD_INSN " 1b, %l[l_yes], %0\n\t"
#define cpu_has_mcheck 0
#define cpu_has_mdmx 0
#define cpu_has_mips16 0
-#define cpu_has_mips32r1 0
#define cpu_has_mips32r2 0
#define cpu_has_mips3d 0
-#define cpu_has_mips64r1 0
#define cpu_has_mips64r2 0
#define cpu_has_mipsmt 0
#define cpu_has_prefetch 0
#define MIPS_CONF6_SYND (_ULCAST_(1) << 13)
/* proAptiv FTLB on/off bit */
#define MIPS_CONF6_FTLBEN (_ULCAST_(1) << 15)
+/* FTLB probability bits */
+#define MIPS_CONF6_FTLBP_SHIFT (16)
#define MIPS_CONF7_WII (_ULCAST_(1) << 31)
/*
* Macros to access the floating point coprocessor control registers
*/
-#define read_32bit_cp1_register(source) \
+#define _read_32bit_cp1_register(source, gas_hardfloat) \
({ \
int __res; \
\
" # gas fails to assemble cfc1 for some archs, \n" \
" # like Octeon. \n" \
" .set mips1 \n" \
+ " "STR(gas_hardfloat)" \n" \
" cfc1 %0,"STR(source)" \n" \
" .set pop \n" \
: "=r" (__res)); \
__res; \
})
+#ifdef GAS_HAS_SET_HARDFLOAT
+#define read_32bit_cp1_register(source) \
+ _read_32bit_cp1_register(source, .set hardfloat)
+#else
+#define read_32bit_cp1_register(source) \
+ _read_32bit_cp1_register(source, )
+#endif
+
#ifdef HAVE_AS_DSP
#define rddsp(mask) \
({ \
*/
static inline void protected_writeback_dcache_line(unsigned long addr)
{
+#ifdef CONFIG_EVA
+ protected_cachee_op(Hit_Writeback_Inv_D, addr);
+#else
protected_cache_op(Hit_Writeback_Inv_D, addr);
+#endif
}
static inline void protected_writeback_scache_line(unsigned long addr)
__get_kernel_common((x), size, __gu_ptr); \
else \
__get_user_common((x), size, __gu_ptr); \
- } \
+ } else \
+ (x) = 0; \
\
__gu_err; \
})
" .insn \n" \
" .section .fixup,\"ax\" \n" \
"3: li %0, %4 \n" \
+ " move %1, $0 \n" \
" j 2b \n" \
" .previous \n" \
" .section __ex_table,\"a\" \n" \
" .insn \n" \
" .section .fixup,\"ax\" \n" \
"3: li %0, %4 \n" \
+ " move %1, $0 \n" \
" j 2b \n" \
" .previous \n" \
" .section __ex_table,\"a\" \n" \
"jal\t" #destination "\n\t"
#endif
-#ifndef CONFIG_CPU_DADDI_WORKAROUNDS
-#define DADDI_SCRATCH "$0"
-#else
+#if defined(CONFIG_CPU_DADDI_WORKAROUNDS) || (defined(CONFIG_EVA) && \
+ defined(CONFIG_CPU_HAS_PREFETCH))
#define DADDI_SCRATCH "$3"
+#else
+#define DADDI_SCRATCH "$0"
#endif
extern size_t __copy_user(void *__to, const void *__from, size_t __n);
}
/*
- * strlen_user: - Get the size of a string in user space.
+ * strnlen_user: - Get the size of a string in user space.
* @str: The string to measure.
*
* Context: User context only. This function may sleep.
*
* Returns the size of the string INCLUDING the terminating NUL.
* On exception, returns 0.
- *
- * If there is a limit on the length of a valid string, you may wish to
- * consider using strnlen_user() instead.
+ * If the string is too long, returns a value greater than @n.
*/
static inline long strnlen_user(const char __user *s, long n)
{
#define __NR_seccomp (__NR_Linux + 352)
#define __NR_getrandom (__NR_Linux + 353)
#define __NR_memfd_create (__NR_Linux + 354)
+#define __NR_bpf (__NR_Linux + 355)
/*
* Offset of the last Linux o32 flavoured syscall
*/
-#define __NR_Linux_syscalls 354
+#define __NR_Linux_syscalls 355
#endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */
#define __NR_O32_Linux 4000
-#define __NR_O32_Linux_syscalls 354
+#define __NR_O32_Linux_syscalls 355
#if _MIPS_SIM == _MIPS_SIM_ABI64
#define __NR_seccomp (__NR_Linux + 312)
#define __NR_getrandom (__NR_Linux + 313)
#define __NR_memfd_create (__NR_Linux + 314)
+#define __NR_bpf (__NR_Linux + 315)
/*
* Offset of the last Linux 64-bit flavoured syscall
*/
-#define __NR_Linux_syscalls 314
+#define __NR_Linux_syscalls 315
#endif /* _MIPS_SIM == _MIPS_SIM_ABI64 */
#define __NR_64_Linux 5000
-#define __NR_64_Linux_syscalls 314
+#define __NR_64_Linux_syscalls 315
#if _MIPS_SIM == _MIPS_SIM_NABI32
#define __NR_seccomp (__NR_Linux + 316)
#define __NR_getrandom (__NR_Linux + 317)
#define __NR_memfd_create (__NR_Linux + 318)
+#define __NR_bpf (__NR_Linux + 319)
/*
* Offset of the last N32 flavoured syscall
*/
-#define __NR_Linux_syscalls 318
+#define __NR_Linux_syscalls 319
#endif /* _MIPS_SIM == _MIPS_SIM_NABI32 */
#define __NR_N32_Linux 6000
-#define __NR_N32_Linux_syscalls 318
+#define __NR_N32_Linux_syscalls 319
#endif /* _UAPI_ASM_UNISTD_H */
END(bmips_reset_nmi_vec)
.set pop
- .previous
/***********************************************************************
* CPU1 warm restart vector (used for second and subsequent boots).
jr ra
END(bmips_enable_xks01)
-
- .previous
case mm_bc1t_op:
preempt_disable();
if (is_fpu_owner())
- asm volatile("cfc1\t%0,$31" : "=r" (fcr31));
+ fcr31 = read_32bit_cp1_register(CP1_STATUS);
else
fcr31 = current->thread.fpu.fcr31;
preempt_enable();
case cop1_op:
preempt_disable();
if (is_fpu_owner())
- asm volatile(
- ".set push\n"
- "\t.set mips1\n"
- "\tcfc1\t%0,$31\n"
- "\t.set pop" : "=r" (fcr31));
+ fcr31 = read_32bit_cp1_register(CP1_STATUS);
else
fcr31 = current->thread.fpu.fcr31;
preempt_enable();
nop
.set push
+ .set mips32r2
.set mt
/* Only allow 1 TC per VPE to execute... */
nop
.set push
+ .set mips32r2
.set mt
1: /* Enter VPE configuration state */
static char unknown_isa[] = KERN_ERR \
"Unsupported ISA type, c0.config0: %d.";
+static unsigned int calculate_ftlb_probability(struct cpuinfo_mips *c)
+{
+
+ unsigned int probability = c->tlbsize / c->tlbsizevtlb;
+
+ /*
+ * 0 = All TLBWR instructions go to FTLB
+ * 1 = 15:1: For every 16 TBLWR instructions, 15 go to the
+ * FTLB and 1 goes to the VTLB.
+ * 2 = 7:1: As above with 7:1 ratio.
+ * 3 = 3:1: As above with 3:1 ratio.
+ *
+ * Use the linear midpoint as the probability threshold.
+ */
+ if (probability >= 12)
+ return 1;
+ else if (probability >= 6)
+ return 2;
+ else
+ /*
+ * So FTLB is less than 4 times bigger than VTLB.
+ * A 3:1 ratio can still be useful though.
+ */
+ return 3;
+}
+
static void set_ftlb_enable(struct cpuinfo_mips *c, int enable)
{
unsigned int config6;
case CPU_P5600:
/* proAptiv & related cores use Config6 to enable the FTLB */
config6 = read_c0_config6();
+ /* Clear the old probability value */
+ config6 &= ~(3 << MIPS_CONF6_FTLBP_SHIFT);
if (enable)
/* Enable FTLB */
- write_c0_config6(config6 | MIPS_CONF6_FTLBEN);
+ write_c0_config6(config6 |
+ (calculate_ftlb_probability(c)
+ << MIPS_CONF6_FTLBP_SHIFT)
+ | MIPS_CONF6_FTLBEN);
else
/* Disable FTLB */
write_c0_config6(config6 & ~MIPS_CONF6_FTLBEN);
c->cputype = CPU_LOONGSON2;
__cpu_name[cpu] = "ICT Loongson-2";
set_elf_platform(cpu, "loongson2e");
+ set_isa(c, MIPS_CPU_ISA_III);
break;
case PRID_REV_LOONGSON2F:
c->cputype = CPU_LOONGSON2;
__cpu_name[cpu] = "ICT Loongson-2";
set_elf_platform(cpu, "loongson2f");
+ set_isa(c, MIPS_CPU_ISA_III);
break;
case PRID_REV_LOONGSON3A:
c->cputype = CPU_LOONGSON3;
- c->writecombine = _CACHE_UNCACHED_ACCELERATED;
__cpu_name[cpu] = "ICT Loongson-3";
set_elf_platform(cpu, "loongson3a");
+ set_isa(c, MIPS_CPU_ISA_M64R1);
break;
case PRID_REV_LOONGSON3B_R1:
case PRID_REV_LOONGSON3B_R2:
c->cputype = CPU_LOONGSON3;
__cpu_name[cpu] = "ICT Loongson-3";
set_elf_platform(cpu, "loongson3b");
+ set_isa(c, MIPS_CPU_ISA_M64R1);
break;
}
- set_isa(c, MIPS_CPU_ISA_III);
c->options = R4K_OPTS |
MIPS_CPU_FPU | MIPS_CPU_LLSC |
MIPS_CPU_32FPR;
c->tlbsize = 64;
+ c->writecombine = _CACHE_UNCACHED_ACCELERATED;
break;
case PRID_IMP_LOONGSON_32: /* Loongson-1 */
decode_configs(c);
.set push
/* gas fails to assemble cfc1 for some archs (octeon).*/ \
.set mips1
+ SET_HARDFLOAT
cfc1 a1, fcr31
li a2, ~(0x3f << 12)
and a2, a1
#ifdef HAVE_JUMP_LABEL
-#define J_RANGE_MASK ((1ul << 28) - 1)
+/*
+ * Define parameters for the standard MIPS and the microMIPS jump
+ * instruction encoding respectively:
+ *
+ * - the ISA bit of the target, either 0 or 1 respectively,
+ *
+ * - the amount the jump target address is shifted right to fit in the
+ * immediate field of the machine instruction, either 2 or 1,
+ *
+ * - the mask determining the size of the jump region relative to the
+ * delay-slot instruction, either 256MB or 128MB,
+ *
+ * - the jump target alignment, either 4 or 2 bytes.
+ */
+#define J_ISA_BIT IS_ENABLED(CONFIG_CPU_MICROMIPS)
+#define J_RANGE_SHIFT (2 - J_ISA_BIT)
+#define J_RANGE_MASK ((1ul << (26 + J_RANGE_SHIFT)) - 1)
+#define J_ALIGN_MASK ((1ul << J_RANGE_SHIFT) - 1)
void arch_jump_label_transform(struct jump_entry *e,
enum jump_label_type type)
{
+ union mips_instruction *insn_p;
union mips_instruction insn;
- union mips_instruction *insn_p =
- (union mips_instruction *)(unsigned long)e->code;
- /* Jump only works within a 256MB aligned region. */
- BUG_ON((e->target & ~J_RANGE_MASK) != (e->code & ~J_RANGE_MASK));
+ insn_p = (union mips_instruction *)msk_isa16_mode(e->code);
+
+ /* Jump only works within an aligned region its delay slot is in. */
+ BUG_ON((e->target & ~J_RANGE_MASK) != ((e->code + 4) & ~J_RANGE_MASK));
- /* Target must have 4 byte alignment. */
- BUG_ON((e->target & 3) != 0);
+ /* Target must have the right alignment and ISA must be preserved. */
+ BUG_ON((e->target & J_ALIGN_MASK) != J_ISA_BIT);
if (type == JUMP_LABEL_ENABLE) {
- insn.j_format.opcode = j_op;
- insn.j_format.target = (e->target & J_RANGE_MASK) >> 2;
+ insn.j_format.opcode = J_ISA_BIT ? mm_j32_op : j_op;
+ insn.j_format.target = e->target >> J_RANGE_SHIFT;
} else {
insn.word = 0; /* nop */
}
get_online_cpus();
mutex_lock(&text_mutex);
- *insn_p = insn;
+ if (IS_ENABLED(CONFIG_CPU_MICROMIPS)) {
+ insn_p->halfword[0] = insn.word >> 16;
+ insn_p->halfword[1] = insn.word;
+ } else
+ *insn_p = insn;
flush_icache_range((unsigned long)insn_p,
(unsigned long)insn_p + sizeof(*insn_p));
.set mips1
/* Save floating point context */
LEAF(_save_fp_context)
+ .set push
+ SET_HARDFLOAT
li v0, 0 # assume success
cfc1 t1,fcr31
EX(swc1 $f0,(SC_FPREGS+0)(a0))
EX(sw t1,(SC_FPC_CSR)(a0))
cfc1 t0,$0 # implementation/version
jr ra
+ .set pop
.set nomacro
EX(sw t0,(SC_FPC_EIR)(a0))
.set macro
* stack frame which might have been changed by the user.
*/
LEAF(_restore_fp_context)
+ .set push
+ SET_HARDFLOAT
li v0, 0 # assume success
EX(lw t0,(SC_FPC_CSR)(a0))
EX(lwc1 $f0,(SC_FPREGS+0)(a0))
EX(lwc1 $f31,(SC_FPREGS+248)(a0))
jr ra
ctc1 t0,fcr31
+ .set pop
END(_restore_fp_context)
.set reorder
#define FPU_DEFAULT 0x00000000
+ .set push
+ SET_HARDFLOAT
+
LEAF(_init_fpu)
mfc0 t0, CP0_STATUS
li t1, ST0_CU1
mtc1 t0, $f31
jr ra
END(_init_fpu)
+
+ .set pop
#include <asm/asm-offsets.h>
#include <asm/regdef.h>
+/* preprocessor replaces the fp in ".set fp=64" with $30 otherwise */
+#undef fp
+
.macro EX insn, reg, src
.set push
+ SET_HARDFLOAT
.set nomacro
.ex\@: \insn \reg, \src
.set pop
.set arch=r4000
LEAF(_save_fp_context)
+ .set push
+ SET_HARDFLOAT
cfc1 t1, fcr31
+ .set pop
#if defined(CONFIG_64BIT) || defined(CONFIG_CPU_MIPS32_R2)
.set push
+ SET_HARDFLOAT
#ifdef CONFIG_CPU_MIPS32_R2
- .set mips64r2
+ .set mips32r2
+ .set fp=64
mfc0 t0, CP0_STATUS
sll t0, t0, 5
bgez t0, 1f # skip storing odd if FR=0
1: .set pop
#endif
+ .set push
+ SET_HARDFLOAT
/* Store the 16 even double precision registers */
EX sdc1 $f0, SC_FPREGS+0(a0)
EX sdc1 $f2, SC_FPREGS+16(a0)
EX sw t1, SC_FPC_CSR(a0)
jr ra
li v0, 0 # success
+ .set pop
END(_save_fp_context)
#ifdef CONFIG_MIPS32_COMPAT
/* Save 32-bit process floating point context */
LEAF(_save_fp_context32)
+ .set push
+ SET_HARDFLOAT
cfc1 t1, fcr31
mfc0 t0, CP0_STATUS
EX sw t1, SC32_FPC_CSR(a0)
cfc1 t0, $0 # implementation/version
EX sw t0, SC32_FPC_EIR(a0)
+ .set pop
jr ra
li v0, 0 # success
#if defined(CONFIG_64BIT) || defined(CONFIG_CPU_MIPS32_R2)
.set push
+ SET_HARDFLOAT
#ifdef CONFIG_CPU_MIPS32_R2
- .set mips64r2
+ .set mips32r2
+ .set fp=64
mfc0 t0, CP0_STATUS
sll t0, t0, 5
bgez t0, 1f # skip loading odd if FR=0
EX ldc1 $f31, SC_FPREGS+248(a0)
1: .set pop
#endif
+ .set push
+ SET_HARDFLOAT
EX ldc1 $f0, SC_FPREGS+0(a0)
EX ldc1 $f2, SC_FPREGS+16(a0)
EX ldc1 $f4, SC_FPREGS+32(a0)
EX ldc1 $f28, SC_FPREGS+224(a0)
EX ldc1 $f30, SC_FPREGS+240(a0)
ctc1 t1, fcr31
+ .set pop
jr ra
li v0, 0 # success
END(_restore_fp_context)
#ifdef CONFIG_MIPS32_COMPAT
LEAF(_restore_fp_context32)
/* Restore an o32 sigcontext. */
+ .set push
+ SET_HARDFLOAT
EX lw t1, SC32_FPC_CSR(a0)
mfc0 t0, CP0_STATUS
ctc1 t1, fcr31
jr ra
li v0, 0 # success
+ .set pop
END(_restore_fp_context32)
#endif
#include <asm/asmmacro.h>
+/* preprocessor replaces the fp in ".set fp=64" with $30 otherwise */
+#undef fp
+
/*
* Offset to the current process status flags, the first 32 bytes of the
* stack are not used.
bgtz a3, 1f
/* Save 128b MSA vector context + scalar FP control & status. */
+ .set push
+ SET_HARDFLOAT
cfc1 t1, fcr31
msa_save_all a0
+ .set pop /* SET_HARDFLOAT */
+
sw t1, THREAD_FCR31(a0)
b 2f
#define FPU_DEFAULT 0x00000000
+ .set push
+ SET_HARDFLOAT
+
LEAF(_init_fpu)
mfc0 t0, CP0_STATUS
li t1, ST0_CU1
#ifdef CONFIG_CPU_MIPS32_R2
.set push
- .set mips64r2
+ .set mips32r2
+ .set fp=64
sll t0, t0, 5 # is Status.FR set?
bgez t0, 1f # no: skip setting upper 32b
#endif
jr ra
END(_init_fpu)
+
+ .set pop /* SET_HARDFLOAT */
.set noreorder
.set mips2
+ .set push
+ SET_HARDFLOAT
+
/* Save floating point context */
LEAF(_save_fp_context)
mfc0 t0,CP0_STATUS
1: jr ra
nop
END(_restore_fp_context)
+
+ .set pop /* SET_HARDFLOAT */
int ret = 0;
if (index >= RTLX_CHANNELS) {
- pr_debug(KERN_DEBUG "rtlx_open index out of range\n");
+ pr_debug("rtlx_open index out of range\n");
return -ENOSYS;
}
if (atomic_inc_return(&channel_wqs[index].in_open) > 1) {
- pr_debug(KERN_DEBUG "rtlx_open channel %d already opened\n", index);
+ pr_debug("rtlx_open channel %d already opened\n", index);
ret = -EBUSY;
goto out_fail;
}
PTR sys_seccomp
PTR sys_getrandom
PTR sys_memfd_create
+ PTR sys_bpf /* 4355 */
PTR sys_seccomp
PTR sys_getrandom
PTR sys_memfd_create
+ PTR sys_bpf /* 5315 */
.size sys_call_table,.-sys_call_table
PTR sys_seccomp
PTR sys_getrandom
PTR sys_memfd_create
+ PTR sys_bpf
.size sysn32_call_table,.-sysn32_call_table
PTR sys_seccomp
PTR sys_getrandom
PTR sys_memfd_create
+ PTR sys_bpf /* 4355 */
.size sys32_call_table,.-sys32_call_table
* NOTE: historically plat_mem_setup did the entire platform initialization.
* This was rather impractical because it meant plat_mem_setup had to
* get away without any kind of memory allocator. To keep old code from
- * breaking plat_setup was just renamed to plat_setup and a second platform
+ * breaking plat_setup was just renamed to plat_mem_setup and a second platform
* initialization hook for anything else was introduced.
*/
static int __init early_parse_mem(char *p)
{
- unsigned long start, size;
+ phys_t start, size;
/*
* If a user specifies memory size, we
dma_contiguous_reserve(PFN_PHYS(max_low_pfn));
/* Tell bootmem about cma reserved memblock section */
for_each_memblock(reserved, reg)
- reserve_bootmem(reg->base, reg->size, BOOTMEM_DEFAULT);
+ if (reg->size != 0)
+ reserve_bootmem(reg->base, reg->size, BOOTMEM_DEFAULT);
}
static void __init resource_init(void)
save_fp_context = _save_fp_context;
restore_fp_context = _restore_fp_context;
} else {
- save_fp_context = copy_fp_from_sigcontext;
- restore_fp_context = copy_fp_to_sigcontext;
+ save_fp_context = copy_fp_to_sigcontext;
+ restore_fp_context = copy_fp_from_sigcontext;
}
#endif /* CONFIG_SMP */
#else
- save_fp_context = copy_fp_from_sigcontext;;
- restore_fp_context = copy_fp_to_sigcontext;
+ save_fp_context = copy_fp_to_sigcontext;
+ restore_fp_context = copy_fp_from_sigcontext;
#endif
return 0;
STOREB(t0, NBYTES-2(dst), .Ls_exc_p1\@)
.Ldone\@:
jr ra
+ nop
.if __memcpy == 1
END(memcpy)
.set __memcpy, 0
entrylo0 = read_c0_entrylo0();
/* Unused entries have a virtual address of KSEG0. */
- if ((entryhi & 0xffffe000) != 0x80000000
+ if ((entryhi & 0xfffff000) != 0x80000000
&& (entryhi & 0xfc0) == asid) {
/*
* Only print entries in use
printk("va=%08lx asid=%08lx"
" [pa=%06lx n=%d d=%d v=%d g=%d]",
- (entryhi & 0xffffe000),
+ (entryhi & 0xfffff000),
entryhi & 0xfc0,
entrylo0 & PAGE_MASK,
(entrylo0 & (1 << 11)) ? 1 : 0,
.else
EX(lbe, t0, (v0), .Lfault\@)
.endif
- PTR_ADDIU v0, 1
+ .set noreorder
bnez t0, 1b
-1: PTR_SUBU v0, a0
+1: PTR_ADDIU v0, 1
+ .set reorder
+ PTR_SUBU v0, a0
jr ra
END(__strnlen_\func\()_asm)
# Serial port support
#
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
-obj-$(CONFIG_SERIAL_8250) += serial.o
+loongson-serial-$(CONFIG_SERIAL_8250) := serial.o
+obj-y += $(loongson-serial-m) $(loongson-serial-y)
obj-$(CONFIG_LOONGSON_UART_BASE) += uart_base.o
obj-$(CONFIG_LOONGSON_MC146818) += rtc.o
static struct node_data prealloc__node_data[MAX_NUMNODES];
unsigned char __node_distances[MAX_NUMNODES][MAX_NUMNODES];
+EXPORT_SYMBOL(__node_distances);
struct node_data *__node_data[MAX_NUMNODES];
EXPORT_SYMBOL(__node_data);
if (insn.i_format.rs == bc_op) {
preempt_disable();
if (is_fpu_owner())
- asm volatile(
- ".set push\n"
- "\t.set mips1\n"
- "\tcfc1\t%0,$31\n"
- "\t.set pop" : "=r" (fcr31));
+ fcr31 = read_32bit_cp1_register(CP1_STATUS);
else
fcr31 = current->thread.fpu.fcr31;
preempt_enable();
local_irq_save(flags);
+ htw_stop();
pid = read_c0_entryhi() & ASID_MASK;
address &= (PAGE_MASK << 1);
write_c0_entryhi(address | pid);
tlb_write_indexed();
}
tlbw_use_hazard();
+ htw_start();
flush_itlb_vm(vma);
local_irq_restore(flags);
}
local_irq_save(flags);
/* Save old context and create impossible VPN2 value */
+ htw_stop();
old_ctx = read_c0_entryhi();
old_pagemask = read_c0_pagemask();
wired = read_c0_wired();
write_c0_entryhi(old_ctx);
write_c0_pagemask(old_pagemask);
+ htw_start();
out:
local_irq_restore(flags);
return ret;
uasm_l_smp_pgtable_change(l, *p);
#endif
iPTE_LW(p, wr.r1, wr.r2); /* get even pte */
- if (!m4kc_tlbp_war())
+ if (!m4kc_tlbp_war()) {
build_tlb_probe_entry(p);
+ if (cpu_has_htw) {
+ /* race condition happens, leaving */
+ uasm_i_ehb(p);
+ uasm_i_mfc0(p, wr.r3, C0_INDEX);
+ uasm_il_bltz(p, r, wr.r3, label_leave);
+ uasm_i_nop(p);
+ }
+ }
return wr;
}
*
* Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved.
*/
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/leds.h>
#include <linux/platform_device.h>
return platform_device_register(&fled_device);
}
-module_init(led_init);
-
-MODULE_AUTHOR("Chris Dearman <chris@mips.com>");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("LED probe driver for SEAD-3");
+device_initcall(led_init);
obj-y += setup.o nlm_hal.o cop2-ex.o dt.o
obj-$(CONFIG_SMP) += wakeup.o
-obj-$(CONFIG_USB) += usb-init.o
-obj-$(CONFIG_USB) += usb-init-xlp2.o
-obj-$(CONFIG_SATA_AHCI) += ahci-init.o
-obj-$(CONFIG_SATA_AHCI) += ahci-init-xlp2.o
+ifdef CONFIG_USB
+obj-y += usb-init.o
+obj-y += usb-init-xlp2.o
+endif
+ifdef CONFIG_SATA_AHCI
+obj-y += ahci-init.o
+obj-y += ahci-init-xlp2.o
+endif
/* This marks the end of the previous function,
which means we overran. */
break;
- stack_size = (unsigned) stack_adjustment;
+ stack_size = (unsigned long) stack_adjustment;
} else if (is_ra_save_ins(&ip)) {
int ra_slot = ip.i_format.simmediate;
if (ra_slot < 0)
msg.data = 0xc00 | msixvec;
ret = irq_set_msi_desc(xirq, desc);
- if (ret < 0) {
- destroy_irq(xirq);
+ if (ret < 0)
return ret;
- }
write_msi_msg(xirq, &msg);
return 0;
}
unsigned char __node_distances[MAX_COMPACT_NODES][MAX_COMPACT_NODES];
+EXPORT_SYMBOL(__node_distances);
static int __init compute_node_distance(nasid_t nasid_a, nasid_t nasid_b)
{
--- /dev/null
+config NIOS2
+ def_bool y
+ select ARCH_WANT_OPTIONAL_GPIOLIB
+ select CLKSRC_OF
+ select GENERIC_ATOMIC64
+ select GENERIC_CLOCKEVENTS
+ select GENERIC_CPU_DEVICES
+ select GENERIC_IRQ_PROBE
+ select GENERIC_IRQ_SHOW
+ select HAVE_ARCH_TRACEHOOK
+ select IRQ_DOMAIN
+ select MODULES_USE_ELF_RELA
+ select OF
+ select OF_EARLY_FLATTREE
+ select SOC_BUS
+ select SPARSE_IRQ
+ select USB_ARCH_HAS_HCD if USB_SUPPORT
+
+config GENERIC_CSUM
+ def_bool y
+
+config GENERIC_HWEIGHT
+ def_bool y
+
+config GENERIC_CALIBRATE_DELAY
+ def_bool y
+
+config NO_IOPORT_MAP
+ def_bool y
+
+config HAS_DMA
+ def_bool y
+
+config FPU
+ def_bool n
+
+config SWAP
+ def_bool n
+
+config RWSEM_GENERIC_SPINLOCK
+ def_bool y
+
+config TRACE_IRQFLAGS_SUPPORT
+ def_bool n
+
+source "init/Kconfig"
+
+menu "Kernel features"
+
+source "kernel/Kconfig.preempt"
+
+source "kernel/Kconfig.freezer"
+
+source "kernel/Kconfig.hz"
+
+source "mm/Kconfig"
+
+config FORCE_MAX_ZONEORDER
+ int "Maximum zone order"
+ range 9 20
+ default "11"
+ help
+ The kernel memory allocator divides physically contiguous memory
+ blocks into "zones", where each zone is a power of two number of
+ pages. This option selects the largest power of two that the kernel
+ keeps in the memory allocator. If you need to allocate very large
+ blocks of physically contiguous memory, then you may need to
+ increase this value.
+
+ This config option is actually maximum order plus one. For example,
+ a value of 11 means that the largest free memory block is 2^10 pages.
+
+endmenu
+
+source "arch/nios2/platform/Kconfig.platform"
+
+menu "Processor type and features"
+
+config MMU
+ def_bool y
+
+config NR_CPUS
+ int
+ default "1"
+
+config NIOS2_ALIGNMENT_TRAP
+ bool "Catch alignment trap"
+ default y
+ help
+ Nios II CPUs cannot fetch/store data which is not bus aligned,
+ i.e., a 2 or 4 byte fetch must start at an address divisible by
+ 2 or 4. Any non-aligned load/store instructions will be trapped and
+ emulated in software if you say Y here, which has a performance
+ impact.
+
+comment "Boot options"
+
+config CMDLINE_BOOL
+ bool "Default bootloader kernel arguments"
+ default y
+
+config CMDLINE
+ string "Default kernel command string"
+ default ""
+ depends on CMDLINE_BOOL
+ help
+ On some platforms, there is currently no way for the boot loader to
+ pass arguments to the kernel. For these platforms, you can supply
+ some command-line options at build time by entering them here. In
+ other cases you can specify kernel args so that you don't have
+ to set them up in board prom initialization routines.
+
+config CMDLINE_FORCE
+ bool "Force default kernel command string"
+ depends on CMDLINE_BOOL
+ help
+ Set this to have arguments from the default kernel command string
+ override those passed by the boot loader.
+
+config NIOS2_CMDLINE_IGNORE_DTB
+ bool "Ignore kernel command string from DTB"
+ depends on CMDLINE_BOOL
+ depends on !CMDLINE_FORCE
+ default y
+ help
+ Set this to ignore the bootargs property from the devicetree's
+ chosen node and fall back to CMDLINE if nothing is passed.
+
+config NIOS2_PASS_CMDLINE
+ bool "Passed kernel command line from u-boot"
+ default n
+ help
+ Use bootargs env variable from u-boot for kernel command line.
+ will override "Default kernel command string".
+ Say N if you are unsure.
+
+endmenu
+
+menu "Advanced setup"
+
+config ADVANCED_OPTIONS
+ bool "Prompt for advanced kernel configuration options"
+ help
+
+comment "Default settings for advanced configuration options are used"
+ depends on !ADVANCED_OPTIONS
+
+config NIOS2_KERNEL_MMU_REGION_BASE_BOOL
+ bool "Set custom kernel MMU region base address"
+ depends on ADVANCED_OPTIONS
+ help
+ This option allows you to set the virtual address of the kernel MMU region.
+
+ Say N here unless you know what you are doing.
+
+config NIOS2_KERNEL_MMU_REGION_BASE
+ hex "Virtual base address of the kernel MMU region " if NIOS2_KERNEL_MMU_REGION_BASE_BOOL
+ default "0x80000000"
+ help
+ This option allows you to set the virtual base address of the kernel MMU region.
+
+config NIOS2_KERNEL_REGION_BASE_BOOL
+ bool "Set custom kernel region base address"
+ depends on ADVANCED_OPTIONS
+ help
+ This option allows you to set the virtual address of the kernel region.
+
+ Say N here unless you know what you are doing.
+
+config NIOS2_KERNEL_REGION_BASE
+ hex "Virtual base address of the kernel region " if NIOS2_KERNEL_REGION_BASE_BOOL
+ default "0xc0000000"
+
+config NIOS2_IO_REGION_BASE_BOOL
+ bool "Set custom I/O region base address"
+ depends on ADVANCED_OPTIONS
+ help
+ This option allows you to set the virtual address of the I/O region.
+
+ Say N here unless you know what you are doing.
+
+config NIOS2_IO_REGION_BASE
+ hex "Virtual base address of the I/O region" if NIOS2_IO_REGION_BASE_BOOL
+ default "0xe0000000"
+
+endmenu
+
+menu "Executable file formats"
+
+source "fs/Kconfig.binfmt"
+
+endmenu
+
+source "net/Kconfig"
+
+source "drivers/Kconfig"
+
+source "fs/Kconfig"
+
+source "arch/nios2/Kconfig.debug"
+
+source "security/Kconfig"
+
+source "crypto/Kconfig"
+
+source "lib/Kconfig"
--- /dev/null
+menu "Kernel hacking"
+
+config TRACE_IRQFLAGS_SUPPORT
+ def_bool y
+
+source "lib/Kconfig.debug"
+
+config DEBUG_STACK_USAGE
+ bool "Enable stack utilization instrumentation"
+ depends on DEBUG_KERNEL
+ help
+ Enables the display of the minimum amount of free stack which each
+ task has ever had available in the sysrq-T and sysrq-P debug output.
+
+ This option will slow down process creation somewhat.
+
+endmenu
--- /dev/null
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License. See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 2013 Altera Corporation
+# Copyright (C) 1994, 95, 96, 2003 by Wind River Systems
+# Written by Fredrik Markstrom
+#
+# This file is included by the global makefile so that you can add your own
+# architecture-specific flags and dependencies. Remember to do have actions
+# for "archclean" cleaning up for this architecture.
+#
+# Nios2 port by Wind River Systems Inc trough:
+# fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com
+
+UTS_SYSNAME = Linux
+
+export MMU
+
+LIBGCC := $(shell $(CC) $(KBUILD_CFLAGS) -print-libgcc-file-name)
+
+KBUILD_CFLAGS += -pipe -D__linux__ -D__ELF__
+KBUILD_CFLAGS += $(if $(CONFIG_NIOS2_HW_MUL_SUPPORT),-mhw-mul,-mno-hw-mul)
+KBUILD_CFLAGS += $(if $(CONFIG_NIOS2_HW_MULX_SUPPORT),-mhw-mulx,-mno-hw-mulx)
+KBUILD_CFLAGS += $(if $(CONFIG_NIOS2_HW_DIV_SUPPORT),-mhw-div,-mno-hw-div)
+KBUILD_CFLAGS += $(if $(CONFIG_NIOS2_FPU_SUPPORT),-mcustom-fpu-cfg=60-1,)
+
+KBUILD_CFLAGS += -fno-optimize-sibling-calls
+KBUILD_CFLAGS += -DUTS_SYSNAME=\"$(UTS_SYSNAME)\"
+KBUILD_CFLAGS += -fno-builtin
+KBUILD_CFLAGS += -G 0
+
+head-y := arch/nios2/kernel/head.o
+libs-y += arch/nios2/lib/ $(LIBGCC)
+core-y += arch/nios2/kernel/ arch/nios2/mm/
+core-y += arch/nios2/platform/
+
+INSTALL_PATH ?= /tftpboot
+nios2-boot := arch/$(ARCH)/boot
+BOOT_TARGETS = vmImage zImage
+PHONY += $(BOOT_TARGETS) install
+KBUILD_IMAGE := $(nios2-boot)/vmImage
+
+ifneq ($(CONFIG_NIOS2_DTB_SOURCE),"")
+ core-y += $(nios2-boot)/
+endif
+
+all: vmImage
+
+archclean:
+ $(Q)$(MAKE) $(clean)=$(nios2-boot)
+
+%.dtb:
+ $(Q)$(MAKE) $(build)=$(nios2-boot) $(nios2-boot)/$@
+
+dtbs:
+ $(Q)$(MAKE) $(build)=$(nios2-boot) $(nios2-boot)/$@
+
+$(BOOT_TARGETS): vmlinux
+ $(Q)$(MAKE) $(build)=$(nios2-boot) $(nios2-boot)/$@
+
+install:
+ $(Q)$(MAKE) $(build)=$(nios2-boot) BOOTIMAGE=$(KBUILD_IMAGE) install
+
+define archhelp
+ echo '* vmImage - Kernel-only image for U-Boot ($(KBUILD_IMAGE))'
+ echo ' install - Install kernel using'
+ echo ' (your) ~/bin/$(INSTALLKERNEL) or'
+ echo ' (distribution) /sbin/$(INSTALLKERNEL) or'
+ echo ' install to $$(INSTALL_PATH)'
+ echo ' dtbs - Build device tree blobs for enabled boards'
+endef
--- /dev/null
+#
+# arch/nios2/boot/Makefile
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License. See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+
+UIMAGE_LOADADDR = $(shell $(NM) vmlinux | awk '$$NF == "_stext" {print $$1}')
+UIMAGE_ENTRYADDR = $(shell $(NM) vmlinux | awk '$$NF == "_start" {print $$1}')
+UIMAGE_COMPRESSION = gzip
+
+OBJCOPYFLAGS_vmlinux.bin := -O binary
+
+targets += vmlinux.bin vmlinux.gz vmImage
+
+$(obj)/vmlinux.bin: vmlinux FORCE
+ $(call if_changed,objcopy)
+
+$(obj)/vmlinux.gz: $(obj)/vmlinux.bin FORCE
+ $(call if_changed,gzip)
+
+$(obj)/vmImage: $(obj)/vmlinux.gz
+ $(call if_changed,uimage)
+ @$(kecho) 'Kernel: $@ is ready'
+
+# Rule to build device tree blobs
+DTB_SRC := $(patsubst "%",%,$(CONFIG_NIOS2_DTB_SOURCE))
+
+# Make sure the generated dtb gets removed during clean
+extra-$(CONFIG_NIOS2_DTB_SOURCE_BOOL) += system.dtb
+
+$(obj)/system.dtb: $(DTB_SRC) FORCE
+ $(call cmd,dtc)
+
+# Ensure system.dtb exists
+$(obj)/linked_dtb.o: $(obj)/system.dtb
+
+obj-$(CONFIG_NIOS2_DTB_SOURCE_BOOL) += linked_dtb.o
+
+targets += $(dtb-y)
+
+# Rule to build device tree blobs with make command
+$(obj)/%.dtb: $(src)/dts/%.dts FORCE
+ $(call if_changed_dep,dtc)
+
+$(obj)/dtbs: $(addprefix $(obj)/, $(dtb-y))
+
+clean-files := *.dtb
+
+install:
+ sh $(srctree)/$(src)/install.sh $(KERNELRELEASE) $(BOOTIMAGE) System.map "$(INSTALL_PATH)"
--- /dev/null
+/*
+ * Copyright (C) 2013 Altera Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This file is generated by sopc2dts.
+ */
+
+/dts-v1/;
+
+/ {
+ model = "altr,qsys_ghrd_3c120";
+ compatible = "altr,qsys_ghrd_3c120";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu: cpu@0x0 {
+ device_type = "cpu";
+ compatible = "altr,nios2-1.0";
+ reg = <0x00000000>;
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ clock-frequency = <125000000>;
+ dcache-line-size = <32>;
+ icache-line-size = <32>;
+ dcache-size = <32768>;
+ icache-size = <32768>;
+ altr,implementation = "fast";
+ altr,pid-num-bits = <8>;
+ altr,tlb-num-ways = <16>;
+ altr,tlb-num-entries = <128>;
+ altr,tlb-ptr-sz = <7>;
+ altr,has-div = <1>;
+ altr,has-mul = <1>;
+ altr,reset-addr = <0xc2800000>;
+ altr,fast-tlb-miss-addr = <0xc7fff400>;
+ altr,exception-addr = <0xd0000020>;
+ altr,has-initda = <1>;
+ altr,has-mmu = <1>;
+ };
+ };
+
+ memory@0 {
+ device_type = "memory";
+ reg = <0x10000000 0x08000000>,
+ <0x07fff400 0x00000400>;
+ };
+
+ sopc@0 {
+ device_type = "soc";
+ ranges;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "altr,avalon", "simple-bus";
+ bus-frequency = <125000000>;
+
+ pb_cpu_to_io: bridge@0x8000000 {
+ compatible = "simple-bus";
+ reg = <0x08000000 0x00800000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x00002000 0x08002000 0x00002000>,
+ <0x00004000 0x08004000 0x00000400>,
+ <0x00004400 0x08004400 0x00000040>,
+ <0x00004800 0x08004800 0x00000040>,
+ <0x00004c80 0x08004c80 0x00000020>,
+ <0x00004d50 0x08004d50 0x00000008>,
+ <0x00008000 0x08008000 0x00000020>,
+ <0x00400000 0x08400000 0x00000020>;
+
+ timer_1ms: timer@0x400000 {
+ compatible = "altr,timer-1.0";
+ reg = <0x00400000 0x00000020>;
+ interrupt-parent = <&cpu>;
+ interrupts = <11>;
+ clock-frequency = <125000000>;
+ };
+
+ timer_0: timer@0x8000 {
+ compatible = "altr,timer-1.0";
+ reg = < 0x00008000 0x00000020 >;
+ interrupt-parent = < &cpu >;
+ interrupts = < 5 >;
+ clock-frequency = < 125000000 >;
+ };
+
+ jtag_uart: serial@0x4d50 {
+ compatible = "altr,juart-1.0";
+ reg = <0x00004d50 0x00000008>;
+ interrupt-parent = <&cpu>;
+ interrupts = <1>;
+ };
+
+ tse_mac: ethernet@0x4000 {
+ compatible = "altr,tse-1.0";
+ reg = <0x00004000 0x00000400>,
+ <0x00004400 0x00000040>,
+ <0x00004800 0x00000040>,
+ <0x00002000 0x00002000>;
+ reg-names = "control_port", "rx_csr", "tx_csr", "s1";
+ interrupt-parent = <&cpu>;
+ interrupts = <2 3>;
+ interrupt-names = "rx_irq", "tx_irq";
+ rx-fifo-depth = <8192>;
+ tx-fifo-depth = <8192>;
+ max-frame-size = <1518>;
+ local-mac-address = [ 00 00 00 00 00 00 ];
+ phy-mode = "rgmii-id";
+ phy-handle = <&phy0>;
+ tse_mac_mdio: mdio {
+ compatible = "altr,tse-mdio";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ phy0: ethernet-phy@18 {
+ reg = <18>;
+ device_type = "ethernet-phy";
+ };
+ };
+ };
+
+ uart: serial@0x4c80 {
+ compatible = "altr,uart-1.0";
+ reg = <0x00004c80 0x00000020>;
+ interrupt-parent = <&cpu>;
+ interrupts = <10>;
+ current-speed = <115200>;
+ clock-frequency = <62500000>;
+ };
+ };
+
+ cfi_flash_64m: flash@0x0 {
+ compatible = "cfi-flash";
+ reg = <0x00000000 0x04000000>;
+ bank-width = <2>;
+ device-width = <1>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ partition@800000 {
+ reg = <0x00800000 0x01e00000>;
+ label = "JFFS2 Filesystem";
+ };
+ };
+ };
+
+ chosen {
+ bootargs = "debug console=ttyJ0,115200";
+ };
+};
--- /dev/null
+#!/bin/sh
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License. See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 1995 by Linus Torvalds
+#
+# Adapted from code in arch/i386/boot/Makefile by H. Peter Anvin
+#
+# "make install" script for nios2 architecture
+#
+# Arguments:
+# $1 - kernel version
+# $2 - kernel image file
+# $3 - kernel map file
+# $4 - default install path (blank if root directory)
+#
+
+verify () {
+ if [ ! -f "$1" ]; then
+ echo "" 1>&2
+ echo " *** Missing file: $1" 1>&2
+ echo ' *** You need to run "make" before "make install".' 1>&2
+ echo "" 1>&2
+ exit 1
+ fi
+}
+
+# Make sure the files actually exist
+verify "$2"
+verify "$3"
+
+# User may have a custom install script
+
+if [ -x ~/bin/${INSTALLKERNEL} ]; then exec ~/bin/${INSTALLKERNEL} "$@"; fi
+if [ -x /sbin/${INSTALLKERNEL} ]; then exec /sbin/${INSTALLKERNEL} "$@"; fi
+
+# Default install - same as make zlilo
+
+if [ -f $4/vmlinuz ]; then
+ mv $4/vmlinuz $4/vmlinuz.old
+fi
+
+if [ -f $4/System.map ]; then
+ mv $4/System.map $4/System.old
+fi
+
+cat $2 > $4/vmlinuz
+cp $3 $4/System.map
+
+sync
--- /dev/null
+/*
+ * Copyright (C) 2011 Thomas Chou <thomas@wytron.com.tw>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+.section .dtb.init.rodata,"a"
+.incbin "arch/nios2/boot/system.dtb"
--- /dev/null
+CONFIG_SYSVIPC=y
+CONFIG_NO_HZ_IDLE=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_SYSCTL_SYSCALL=y
+# CONFIG_ELF_CORE is not set
+# CONFIG_EPOLL is not set
+# CONFIG_SIGNALFD is not set
+# CONFIG_TIMERFD is not set
+# CONFIG_EVENTFD is not set
+# CONFIG_SHMEM is not set
+# CONFIG_AIO is not set
+CONFIG_EMBEDDED=y
+CONFIG_SLAB=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_NIOS2_MEM_BASE=0x10000000
+CONFIG_NIOS2_HW_MUL_SUPPORT=y
+CONFIG_NIOS2_HW_DIV_SUPPORT=y
+CONFIG_CUSTOM_CACHE_SETTINGS=y
+CONFIG_NIOS2_DCACHE_SIZE=0x8000
+CONFIG_NIOS2_ICACHE_SIZE=0x8000
+# CONFIG_NIOS2_CMDLINE_IGNORE_DTB is not set
+CONFIG_NIOS2_PASS_CMDLINE=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_IPV6 is not set
+# CONFIG_WIRELESS is not set
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+# CONFIG_FW_LOADER is not set
+CONFIG_MTD=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_CFI_AMDSTD=y
+CONFIG_MTD_PHYSMAP_OF=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_NETDEVICES=y
+CONFIG_ALTERA_TSE=y
+CONFIG_MARVELL_PHY=y
+# CONFIG_WLAN is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_SERIO_SERPORT is not set
+# CONFIG_VT is not set
+CONFIG_SERIAL_ALTERA_JTAGUART=y
+CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE=y
+CONFIG_SERIAL_ALTERA_UART=y
+# CONFIG_HW_RANDOM is not set
+# CONFIG_HWMON is not set
+# CONFIG_USB_SUPPORT is not set
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+# CONFIG_DNOTIFY is not set
+# CONFIG_INOTIFY_USER is not set
+CONFIG_JFFS2_FS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_ROOT_NFS=y
+CONFIG_SUNRPC_DEBUG=y
+CONFIG_DEBUG_INFO=y
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
--- /dev/null
+generic-y += atomic.h
+generic-y += auxvec.h
+generic-y += barrier.h
+generic-y += bitops.h
+generic-y += bitsperlong.h
+generic-y += bug.h
+generic-y += bugs.h
+generic-y += clkdev.h
+generic-y += cputime.h
+generic-y += current.h
+generic-y += device.h
+generic-y += div64.h
+generic-y += dma.h
+generic-y += emergency-restart.h
+generic-y += errno.h
+generic-y += exec.h
+generic-y += fb.h
+generic-y += fcntl.h
+generic-y += ftrace.h
+generic-y += futex.h
+generic-y += hardirq.h
+generic-y += hash.h
+generic-y += hw_irq.h
+generic-y += ioctl.h
+generic-y += ioctls.h
+generic-y += ipcbuf.h
+generic-y += irq_regs.h
+generic-y += irq_work.h
+generic-y += kdebug.h
+generic-y += kmap_types.h
+generic-y += kvm_para.h
+generic-y += local.h
+generic-y += mcs_spinlock.h
+generic-y += mman.h
+generic-y += module.h
+generic-y += msgbuf.h
+generic-y += param.h
+generic-y += pci.h
+generic-y += percpu.h
+generic-y += poll.h
+generic-y += posix_types.h
+generic-y += preempt.h
+generic-y += resource.h
+generic-y += scatterlist.h
+generic-y += sections.h
+generic-y += segment.h
+generic-y += sembuf.h
+generic-y += serial.h
+generic-y += shmbuf.h
+generic-y += shmparam.h
+generic-y += siginfo.h
+generic-y += signal.h
+generic-y += socket.h
+generic-y += sockios.h
+generic-y += spinlock.h
+generic-y += stat.h
+generic-y += statfs.h
+generic-y += termbits.h
+generic-y += termios.h
+generic-y += topology.h
+generic-y += trace_clock.h
+generic-y += types.h
+generic-y += unaligned.h
+generic-y += user.h
+generic-y += vga.h
+generic-y += xor.h
--- /dev/null
+/*
+ * Macro used to simplify coding multi-line assembler.
+ * Some of the bit test macro can simplify down to one line
+ * depending on the mask value.
+ *
+ * Copyright (C) 2004 Microtronix Datacom Ltd.
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ */
+#ifndef _ASM_NIOS2_ASMMACROS_H
+#define _ASM_NIOS2_ASMMACROS_H
+/*
+ * ANDs reg2 with mask and places the result in reg1.
+ *
+ * You cannnot use the same register for reg1 & reg2.
+ */
+
+.macro ANDI32 reg1, reg2, mask
+.if \mask & 0xffff
+ .if \mask & 0xffff0000
+ movhi \reg1, %hi(\mask)
+ movui \reg1, %lo(\mask)
+ and \reg1, \reg1, \reg2
+ .else
+ andi \reg1, \reg2, %lo(\mask)
+ .endif
+.else
+ andhi \reg1, \reg2, %hi(\mask)
+.endif
+.endm
+
+/*
+ * ORs reg2 with mask and places the result in reg1.
+ *
+ * It is safe to use the same register for reg1 & reg2.
+ */
+
+.macro ORI32 reg1, reg2, mask
+.if \mask & 0xffff
+ .if \mask & 0xffff0000
+ orhi \reg1, \reg2, %hi(\mask)
+ ori \reg1, \reg2, %lo(\mask)
+ .else
+ ori \reg1, \reg2, %lo(\mask)
+ .endif
+.else
+ orhi \reg1, \reg2, %hi(\mask)
+.endif
+.endm
+
+/*
+ * XORs reg2 with mask and places the result in reg1.
+ *
+ * It is safe to use the same register for reg1 & reg2.
+ */
+
+.macro XORI32 reg1, reg2, mask
+.if \mask & 0xffff
+ .if \mask & 0xffff0000
+ xorhi \reg1, \reg2, %hi(\mask)
+ xori \reg1, \reg1, %lo(\mask)
+ .else
+ xori \reg1, \reg2, %lo(\mask)
+ .endif
+.else
+ xorhi \reg1, \reg2, %hi(\mask)
+.endif
+.endm
+
+/*
+ * This is a support macro for BTBZ & BTBNZ. It checks
+ * the bit to make sure it is valid 32 value.
+ *
+ * It is safe to use the same register for reg1 & reg2.
+ */
+
+.macro BT reg1, reg2, bit
+.if \bit > 31
+ .err
+.else
+ .if \bit < 16
+ andi \reg1, \reg2, (1 << \bit)
+ .else
+ andhi \reg1, \reg2, (1 << (\bit - 16))
+ .endif
+.endif
+.endm
+
+/*
+ * Tests the bit in reg2 and branches to label if the
+ * bit is zero. The result of the bit test is stored in reg1.
+ *
+ * It is safe to use the same register for reg1 & reg2.
+ */
+
+.macro BTBZ reg1, reg2, bit, label
+ BT \reg1, \reg2, \bit
+ beq \reg1, r0, \label
+.endm
+
+/*
+ * Tests the bit in reg2 and branches to label if the
+ * bit is non-zero. The result of the bit test is stored in reg1.
+ *
+ * It is safe to use the same register for reg1 & reg2.
+ */
+
+.macro BTBNZ reg1, reg2, bit, label
+ BT \reg1, \reg2, \bit
+ bne \reg1, r0, \label
+.endm
+
+/*
+ * Tests the bit in reg2 and then compliments the bit in reg2.
+ * The result of the bit test is stored in reg1.
+ *
+ * It is NOT safe to use the same register for reg1 & reg2.
+ */
+
+.macro BTC reg1, reg2, bit
+.if \bit > 31
+ .err
+.else
+ .if \bit < 16
+ andi \reg1, \reg2, (1 << \bit)
+ xori \reg2, \reg2, (1 << \bit)
+ .else
+ andhi \reg1, \reg2, (1 << (\bit - 16))
+ xorhi \reg2, \reg2, (1 << (\bit - 16))
+ .endif
+.endif
+.endm
+
+/*
+ * Tests the bit in reg2 and then sets the bit in reg2.
+ * The result of the bit test is stored in reg1.
+ *
+ * It is NOT safe to use the same register for reg1 & reg2.
+ */
+
+.macro BTS reg1, reg2, bit
+.if \bit > 31
+ .err
+.else
+ .if \bit < 16
+ andi \reg1, \reg2, (1 << \bit)
+ ori \reg2, \reg2, (1 << \bit)
+ .else
+ andhi \reg1, \reg2, (1 << (\bit - 16))
+ orhi \reg2, \reg2, (1 << (\bit - 16))
+ .endif
+.endif
+.endm
+
+/*
+ * Tests the bit in reg2 and then resets the bit in reg2.
+ * The result of the bit test is stored in reg1.
+ *
+ * It is NOT safe to use the same register for reg1 & reg2.
+ */
+
+.macro BTR reg1, reg2, bit
+.if \bit > 31
+ .err
+.else
+ .if \bit < 16
+ andi \reg1, \reg2, (1 << \bit)
+ andi \reg2, \reg2, %lo(~(1 << \bit))
+ .else
+ andhi \reg1, \reg2, (1 << (\bit - 16))
+ andhi \reg2, \reg2, %lo(~(1 << (\bit - 16)))
+ .endif
+.endif
+.endm
+
+/*
+ * Tests the bit in reg2 and then compliments the bit in reg2.
+ * The result of the bit test is stored in reg1. If the
+ * original bit was zero it branches to label.
+ *
+ * It is NOT safe to use the same register for reg1 & reg2.
+ */
+
+.macro BTCBZ reg1, reg2, bit, label
+ BTC \reg1, \reg2, \bit
+ beq \reg1, r0, \label
+.endm
+
+/*
+ * Tests the bit in reg2 and then compliments the bit in reg2.
+ * The result of the bit test is stored in reg1. If the
+ * original bit was non-zero it branches to label.
+ *
+ * It is NOT safe to use the same register for reg1 & reg2.
+ */
+
+.macro BTCBNZ reg1, reg2, bit, label
+ BTC \reg1, \reg2, \bit
+ bne \reg1, r0, \label
+.endm
+
+/*
+ * Tests the bit in reg2 and then sets the bit in reg2.
+ * The result of the bit test is stored in reg1. If the
+ * original bit was zero it branches to label.
+ *
+ * It is NOT safe to use the same register for reg1 & reg2.
+ */
+
+.macro BTSBZ reg1, reg2, bit, label
+ BTS \reg1, \reg2, \bit
+ beq \reg1, r0, \label
+.endm
+
+/*
+ * Tests the bit in reg2 and then sets the bit in reg2.
+ * The result of the bit test is stored in reg1. If the
+ * original bit was non-zero it branches to label.
+ *
+ * It is NOT safe to use the same register for reg1 & reg2.
+ */
+
+.macro BTSBNZ reg1, reg2, bit, label
+ BTS \reg1, \reg2, \bit
+ bne \reg1, r0, \label
+.endm
+
+/*
+ * Tests the bit in reg2 and then resets the bit in reg2.
+ * The result of the bit test is stored in reg1. If the
+ * original bit was zero it branches to label.
+ *
+ * It is NOT safe to use the same register for reg1 & reg2.
+ */
+
+.macro BTRBZ reg1, reg2, bit, label
+ BTR \reg1, \reg2, \bit
+ bne \reg1, r0, \label
+.endm
+
+/*
+ * Tests the bit in reg2 and then resets the bit in reg2.
+ * The result of the bit test is stored in reg1. If the
+ * original bit was non-zero it branches to label.
+ *
+ * It is NOT safe to use the same register for reg1 & reg2.
+ */
+
+.macro BTRBNZ reg1, reg2, bit, label
+ BTR \reg1, \reg2, \bit
+ bne \reg1, r0, \label
+.endm
+
+/*
+ * Tests the bits in mask against reg2 stores the result in reg1.
+ * If the all the bits in the mask are zero it branches to label.
+ *
+ * It is safe to use the same register for reg1 & reg2.
+ */
+
+.macro TSTBZ reg1, reg2, mask, label
+ ANDI32 \reg1, \reg2, \mask
+ beq \reg1, r0, \label
+.endm
+
+/*
+ * Tests the bits in mask against reg2 stores the result in reg1.
+ * If the any of the bits in the mask are 1 it branches to label.
+ *
+ * It is safe to use the same register for reg1 & reg2.
+ */
+
+.macro TSTBNZ reg1, reg2, mask, label
+ ANDI32 \reg1, \reg2, \mask
+ bne \reg1, r0, \label
+.endm
+
+/*
+ * Pushes reg onto the stack.
+ */
+
+.macro PUSH reg
+ addi sp, sp, -4
+ stw \reg, 0(sp)
+.endm
+
+/*
+ * Pops the top of the stack into reg.
+ */
+
+.macro POP reg
+ ldw \reg, 0(sp)
+ addi sp, sp, 4
+.endm
+
+
+#endif /* _ASM_NIOS2_ASMMACROS_H */
--- /dev/null
+/*
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2009 Thomas Chou <thomas@wytron.com.tw>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <generated/asm-offsets.h>
--- /dev/null
+/*
+ * Copyright (C) 2004 Microtronix Datacom Ltd.
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef _ASM_NIOS2_CACHE_H
+#define _ASM_NIOS2_CACHE_H
+
+#define NIOS2_DCACHE_SIZE CONFIG_NIOS2_DCACHE_SIZE
+#define NIOS2_ICACHE_SIZE CONFIG_NIOS2_ICACHE_SIZE
+#define NIOS2_DCACHE_LINE_SIZE CONFIG_NIOS2_DCACHE_LINE_SIZE
+#define NIOS2_ICACHE_LINE_SHIFT 5
+#define NIOS2_ICACHE_LINE_SIZE (1 << NIOS2_ICACHE_LINE_SHIFT)
+
+/* bytes per L1 cache line */
+#define L1_CACHE_SHIFT NIOS2_ICACHE_LINE_SHIFT
+#define L1_CACHE_BYTES NIOS2_ICACHE_LINE_SIZE
+
+#define ARCH_DMA_MINALIGN L1_CACHE_BYTES
+
+#define __cacheline_aligned
+#define ____cacheline_aligned
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2003 Microtronix Datacom Ltd.
+ * Copyright (C) 2000-2002 Greg Ungerer <gerg@snapgear.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef _ASM_NIOS2_CACHEFLUSH_H
+#define _ASM_NIOS2_CACHEFLUSH_H
+
+#include <linux/mm_types.h>
+
+/*
+ * This flag is used to indicate that the page pointed to by a pte is clean
+ * and does not require cleaning before returning it to the user.
+ */
+#define PG_dcache_clean PG_arch_1
+
+struct mm_struct;
+
+extern void flush_cache_all(void);
+extern void flush_cache_mm(struct mm_struct *mm);
+extern void flush_cache_dup_mm(struct mm_struct *mm);
+extern void flush_cache_range(struct vm_area_struct *vma, unsigned long start,
+ unsigned long end);
+extern void flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr,
+ unsigned long pfn);
+#define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 1
+extern void flush_dcache_page(struct page *page);
+
+extern void flush_icache_range(unsigned long start, unsigned long end);
+extern void flush_icache_page(struct vm_area_struct *vma, struct page *page);
+
+#define flush_cache_vmap(start, end) flush_dcache_range(start, end)
+#define flush_cache_vunmap(start, end) flush_dcache_range(start, end)
+
+extern void copy_to_user_page(struct vm_area_struct *vma, struct page *page,
+ unsigned long user_vaddr,
+ void *dst, void *src, int len);
+extern void copy_from_user_page(struct vm_area_struct *vma, struct page *page,
+ unsigned long user_vaddr,
+ void *dst, void *src, int len);
+
+extern void flush_dcache_range(unsigned long start, unsigned long end);
+extern void invalidate_dcache_range(unsigned long start, unsigned long end);
+
+#define flush_dcache_mmap_lock(mapping) do { } while (0)
+#define flush_dcache_mmap_unlock(mapping) do { } while (0)
+
+#endif /* _ASM_NIOS2_CACHEFLUSH_H */
--- /dev/null
+/*
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2004 Microtronix Datacom Ltd.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef _ASM_NIOS_CHECKSUM_H
+#define _ASM_NIOS_CHECKSUM_H
+
+/* Take these from lib/checksum.c */
+extern __wsum csum_partial(const void *buff, int len, __wsum sum);
+extern __wsum csum_partial_copy(const void *src, void *dst, int len,
+ __wsum sum);
+extern __wsum csum_partial_copy_from_user(const void __user *src, void *dst,
+ int len, __wsum sum, int *csum_err);
+#define csum_partial_copy_nocheck(src, dst, len, sum) \
+ csum_partial_copy((src), (dst), (len), (sum))
+
+extern __sum16 ip_fast_csum(const void *iph, unsigned int ihl);
+extern __sum16 ip_compute_csum(const void *buff, int len);
+
+/*
+ * Fold a partial checksum
+ */
+static inline __sum16 csum_fold(__wsum sum)
+{
+ __asm__ __volatile__(
+ "add %0, %1, %0\n"
+ "cmpltu r8, %0, %1\n"
+ "srli %0, %0, 16\n"
+ "add %0, %0, r8\n"
+ "nor %0, %0, %0\n"
+ : "=r" (sum)
+ : "r" (sum << 16), "0" (sum)
+ : "r8");
+ return (__force __sum16) sum;
+}
+
+/*
+ * computes the checksum of the TCP/UDP pseudo-header
+ * returns a 16-bit checksum, already complemented
+ */
+#define csum_tcpudp_nofold csum_tcpudp_nofold
+static inline __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
+ unsigned short len,
+ unsigned short proto,
+ __wsum sum)
+{
+ __asm__ __volatile__(
+ "add %0, %1, %0\n"
+ "cmpltu r8, %0, %1\n"
+ "add %0, %0, r8\n" /* add carry */
+ "add %0, %2, %0\n"
+ "cmpltu r8, %0, %2\n"
+ "add %0, %0, r8\n" /* add carry */
+ "add %0, %3, %0\n"
+ "cmpltu r8, %0, %3\n"
+ "add %0, %0, r8\n" /* add carry */
+ : "=r" (sum), "=r" (saddr)
+ : "r" (daddr), "r" ((ntohs(len) << 16) + (proto * 256)),
+ "0" (sum),
+ "1" (saddr)
+ : "r8");
+
+ return sum;
+}
+
+static inline __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
+ unsigned short len,
+ unsigned short proto, __wsum sum)
+{
+ return csum_fold(csum_tcpudp_nofold(saddr, daddr, len, proto, sum));
+}
+
+#endif /* _ASM_NIOS_CHECKSUM_H */
--- /dev/null
+/*
+ * Copyright (C) 2004 Microtronix Datacom Ltd.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef _ASM_NIOS2_CMPXCHG_H
+#define _ASM_NIOS2_CMPXCHG_H
+
+#include <linux/irqflags.h>
+
+#define xchg(ptr, x) \
+ ((__typeof__(*(ptr)))__xchg((unsigned long)(x), (ptr), sizeof(*(ptr))))
+
+struct __xchg_dummy { unsigned long a[100]; };
+#define __xg(x) ((volatile struct __xchg_dummy *)(x))
+
+static inline unsigned long __xchg(unsigned long x, volatile void *ptr,
+ int size)
+{
+ unsigned long tmp, flags;
+
+ local_irq_save(flags);
+
+ switch (size) {
+ case 1:
+ __asm__ __volatile__(
+ "ldb %0, %2\n"
+ "stb %1, %2\n"
+ : "=&r" (tmp)
+ : "r" (x), "m" (*__xg(ptr))
+ : "memory");
+ break;
+ case 2:
+ __asm__ __volatile__(
+ "ldh %0, %2\n"
+ "sth %1, %2\n"
+ : "=&r" (tmp)
+ : "r" (x), "m" (*__xg(ptr))
+ : "memory");
+ break;
+ case 4:
+ __asm__ __volatile__(
+ "ldw %0, %2\n"
+ "stw %1, %2\n"
+ : "=&r" (tmp)
+ : "r" (x), "m" (*__xg(ptr))
+ : "memory");
+ break;
+ }
+
+ local_irq_restore(flags);
+ return tmp;
+}
+
+#include <asm-generic/cmpxchg.h>
+#include <asm-generic/cmpxchg-local.h>
+
+#endif /* _ASM_NIOS2_CMPXCHG_H */
--- /dev/null
+/*
+ * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _ASM_NIOS2_CPUINFO_H
+#define _ASM_NIOS2_CPUINFO_H
+
+#include <linux/types.h>
+
+struct cpuinfo {
+ /* Core CPU configuration */
+ char cpu_impl[12];
+ u32 cpu_clock_freq;
+ u32 mmu;
+ u32 has_div;
+ u32 has_mul;
+ u32 has_mulx;
+
+ /* CPU caches */
+ u32 icache_line_size;
+ u32 icache_size;
+ u32 dcache_line_size;
+ u32 dcache_size;
+
+ /* TLB */
+ u32 tlb_pid_num_bits; /* number of bits used for the PID in TLBMISC */
+ u32 tlb_num_ways;
+ u32 tlb_num_ways_log2;
+ u32 tlb_num_entries;
+ u32 tlb_num_lines;
+ u32 tlb_ptr_sz;
+
+ /* Addresses */
+ u32 reset_addr;
+ u32 exception_addr;
+ u32 fast_tlb_miss_exc_addr;
+};
+
+extern struct cpuinfo cpuinfo;
+
+extern void setup_cpuinfo(void);
+
+#endif /* _ASM_NIOS2_CPUINFO_H */
--- /dev/null
+/*
+ * Copyright (C) 2014 Altera Corporation
+ * Copyright (C) 2004 Microtronix Datacom Ltd
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef _ASM_NIOS2_DELAY_H
+#define _ASM_NIOS2_DELAY_H
+
+#include <asm-generic/delay.h>
+
+/* Undefined functions to get compile-time errors */
+extern void __bad_udelay(void);
+extern void __bad_ndelay(void);
+
+extern unsigned long loops_per_jiffy;
+
+#endif /* _ASM_NIOS2_DELAY_H */
--- /dev/null
+/*
+ * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2009 Wind River Systems Inc
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file COPYING in the main directory of this
+ * archive for more details.
+ */
+
+#ifndef _ASM_NIOS2_DMA_MAPPING_H
+#define _ASM_NIOS2_DMA_MAPPING_H
+
+#include <linux/scatterlist.h>
+#include <linux/cache.h>
+#include <asm/cacheflush.h>
+
+static inline void __dma_sync_for_device(void *vaddr, size_t size,
+ enum dma_data_direction direction)
+{
+ switch (direction) {
+ case DMA_FROM_DEVICE:
+ invalidate_dcache_range((unsigned long)vaddr,
+ (unsigned long)(vaddr + size));
+ break;
+ case DMA_TO_DEVICE:
+ /*
+ * We just need to flush the caches here , but Nios2 flush
+ * instruction will do both writeback and invalidate.
+ */
+ case DMA_BIDIRECTIONAL: /* flush and invalidate */
+ flush_dcache_range((unsigned long)vaddr,
+ (unsigned long)(vaddr + size));
+ break;
+ default:
+ BUG();
+ }
+}
+
+static inline void __dma_sync_for_cpu(void *vaddr, size_t size,
+ enum dma_data_direction direction)
+{
+ switch (direction) {
+ case DMA_BIDIRECTIONAL:
+ case DMA_FROM_DEVICE:
+ invalidate_dcache_range((unsigned long)vaddr,
+ (unsigned long)(vaddr + size));
+ break;
+ case DMA_TO_DEVICE:
+ break;
+ default:
+ BUG();
+ }
+}
+
+#define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
+#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
+
+void *dma_alloc_coherent(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, gfp_t flag);
+
+void dma_free_coherent(struct device *dev, size_t size,
+ void *vaddr, dma_addr_t dma_handle);
+
+static inline dma_addr_t dma_map_single(struct device *dev, void *ptr,
+ size_t size,
+ enum dma_data_direction direction)
+{
+ BUG_ON(!valid_dma_direction(direction));
+ __dma_sync_for_device(ptr, size, direction);
+ return virt_to_phys(ptr);
+}
+
+static inline void dma_unmap_single(struct device *dev, dma_addr_t dma_addr,
+ size_t size, enum dma_data_direction direction)
+{
+}
+
+extern int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
+ enum dma_data_direction direction);
+extern dma_addr_t dma_map_page(struct device *dev, struct page *page,
+ unsigned long offset, size_t size, enum dma_data_direction direction);
+extern void dma_unmap_page(struct device *dev, dma_addr_t dma_address,
+ size_t size, enum dma_data_direction direction);
+extern void dma_unmap_sg(struct device *dev, struct scatterlist *sg,
+ int nhwentries, enum dma_data_direction direction);
+extern void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle,
+ size_t size, enum dma_data_direction direction);
+extern void dma_sync_single_for_device(struct device *dev,
+ dma_addr_t dma_handle, size_t size, enum dma_data_direction direction);
+extern void dma_sync_single_range_for_cpu(struct device *dev,
+ dma_addr_t dma_handle, unsigned long offset, size_t size,
+ enum dma_data_direction direction);
+extern void dma_sync_single_range_for_device(struct device *dev,
+ dma_addr_t dma_handle, unsigned long offset, size_t size,
+ enum dma_data_direction direction);
+extern void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg,
+ int nelems, enum dma_data_direction direction);
+extern void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
+ int nelems, enum dma_data_direction direction);
+
+static inline int dma_supported(struct device *dev, u64 mask)
+{
+ return 1;
+}
+
+static inline int dma_set_mask(struct device *dev, u64 mask)
+{
+ if (!dev->dma_mask || !dma_supported(dev, mask))
+ return -EIO;
+
+ *dev->dma_mask = mask;
+
+ return 0;
+}
+
+static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
+{
+ return 0;
+}
+
+/*
+* dma_alloc_noncoherent() returns non-cacheable memory, so there's no need to
+* do any flushing here.
+*/
+static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
+ enum dma_data_direction direction)
+{
+}
+
+/* drivers/base/dma-mapping.c */
+extern int dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
+ void *cpu_addr, dma_addr_t dma_addr, size_t size);
+extern int dma_common_get_sgtable(struct device *dev, struct sg_table *sgt,
+ void *cpu_addr, dma_addr_t dma_addr,
+ size_t size);
+
+#define dma_mmap_coherent(d, v, c, h, s) dma_common_mmap(d, v, c, h, s)
+#define dma_get_sgtable(d, t, v, h, s) dma_common_get_sgtable(d, t, v, h, s)
+
+#endif /* _ASM_NIOS2_DMA_MAPPING_H */
--- /dev/null
+/*
+ * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _ASM_NIOS2_ELF_H
+#define _ASM_NIOS2_ELF_H
+
+#include <uapi/asm/elf.h>
+
+/*
+ * This is used to ensure we don't load something for the wrong architecture.
+ */
+#define elf_check_arch(x) ((x)->e_machine == EM_ALTERA_NIOS2)
+
+#define ELF_PLAT_INIT(_r, load_addr)
+
+#define CORE_DUMP_USE_REGSET
+#define ELF_EXEC_PAGESIZE 4096
+
+/* This is the location that an ET_DYN program is loaded if exec'ed. Typical
+ use of this is to invoke "./ld.so someprog" to test out a new version of
+ the loader. We need to make sure that it is out of the way of the program
+ that it will "exec", and that there is sufficient room for the brk. */
+
+#define ELF_ET_DYN_BASE 0xD0000000UL
+
+/* regs is struct pt_regs, pr_reg is elf_gregset_t (which is
+ now struct_user_regs, they are different) */
+
+#define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1
+struct linux_binprm;
+extern int arch_setup_additional_pages(struct linux_binprm *bprm,
+ int uses_interp);
+#define ELF_CORE_COPY_REGS(pr_reg, regs) \
+{ do { \
+ /* Bleech. */ \
+ pr_reg[0] = regs->r8; \
+ pr_reg[1] = regs->r9; \
+ pr_reg[2] = regs->r10; \
+ pr_reg[3] = regs->r11; \
+ pr_reg[4] = regs->r12; \
+ pr_reg[5] = regs->r13; \
+ pr_reg[6] = regs->r14; \
+ pr_reg[7] = regs->r15; \
+ pr_reg[8] = regs->r1; \
+ pr_reg[9] = regs->r2; \
+ pr_reg[10] = regs->r3; \
+ pr_reg[11] = regs->r4; \
+ pr_reg[12] = regs->r5; \
+ pr_reg[13] = regs->r6; \
+ pr_reg[14] = regs->r7; \
+ pr_reg[15] = regs->orig_r2; \
+ pr_reg[16] = regs->ra; \
+ pr_reg[17] = regs->fp; \
+ pr_reg[18] = regs->sp; \
+ pr_reg[19] = regs->gp; \
+ pr_reg[20] = regs->estatus; \
+ pr_reg[21] = regs->ea; \
+ pr_reg[22] = regs->orig_r7; \
+ { \
+ struct switch_stack *sw = ((struct switch_stack *)regs) - 1; \
+ pr_reg[23] = sw->r16; \
+ pr_reg[24] = sw->r17; \
+ pr_reg[25] = sw->r18; \
+ pr_reg[26] = sw->r19; \
+ pr_reg[27] = sw->r20; \
+ pr_reg[28] = sw->r21; \
+ pr_reg[29] = sw->r22; \
+ pr_reg[30] = sw->r23; \
+ pr_reg[31] = sw->fp; \
+ pr_reg[32] = sw->gp; \
+ pr_reg[33] = sw->ra; \
+ } \
+} while (0); }
+
+/* This yields a mask that user programs can use to figure out what
+ instruction set this cpu supports. */
+
+#define ELF_HWCAP (0)
+
+/* This yields a string that ld.so will use to load implementation
+ specific libraries for optimization. This is more specific in
+ intent than poking at uname or /proc/cpuinfo. */
+
+#define ELF_PLATFORM (NULL)
+
+#endif /* _ASM_NIOS2_ELF_H */
--- /dev/null
+/*
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2004 Microtronix Datacom Ltd.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef _ASM_NIOS2_ENTRY_H
+#define _ASM_NIOS2_ENTRY_H
+
+#ifdef __ASSEMBLY__
+
+#include <asm/processor.h>
+#include <asm/registers.h>
+#include <asm/asm-offsets.h>
+
+/*
+ * Standard Nios2 interrupt entry and exit macros.
+ * Must be called with interrupts disabled.
+ */
+.macro SAVE_ALL
+ rdctl r24, estatus
+ andi r24, r24, ESTATUS_EU
+ beq r24, r0, 1f /* In supervisor mode, already on kernel stack */
+
+ movia r24, _current_thread /* Switch to current kernel stack */
+ ldw r24, 0(r24) /* using the thread_info */
+ addi r24, r24, THREAD_SIZE-PT_REGS_SIZE
+ stw sp, PT_SP(r24) /* Save user stack before changing */
+ mov sp, r24
+ br 2f
+
+1 : mov r24, sp
+ addi sp, sp, -PT_REGS_SIZE /* Backup the kernel stack pointer */
+ stw r24, PT_SP(sp)
+2 : stw r1, PT_R1(sp)
+ stw r2, PT_R2(sp)
+ stw r3, PT_R3(sp)
+ stw r4, PT_R4(sp)
+ stw r5, PT_R5(sp)
+ stw r6, PT_R6(sp)
+ stw r7, PT_R7(sp)
+ stw r8, PT_R8(sp)
+ stw r9, PT_R9(sp)
+ stw r10, PT_R10(sp)
+ stw r11, PT_R11(sp)
+ stw r12, PT_R12(sp)
+ stw r13, PT_R13(sp)
+ stw r14, PT_R14(sp)
+ stw r15, PT_R15(sp)
+ stw r2, PT_ORIG_R2(sp)
+ stw r7, PT_ORIG_R7(sp)
+
+ stw ra, PT_RA(sp)
+ stw fp, PT_FP(sp)
+ stw gp, PT_GP(sp)
+ rdctl r24, estatus
+ stw r24, PT_ESTATUS(sp)
+ stw ea, PT_EA(sp)
+.endm
+
+.macro RESTORE_ALL
+ ldw r1, PT_R1(sp) /* Restore registers */
+ ldw r2, PT_R2(sp)
+ ldw r3, PT_R3(sp)
+ ldw r4, PT_R4(sp)
+ ldw r5, PT_R5(sp)
+ ldw r6, PT_R6(sp)
+ ldw r7, PT_R7(sp)
+ ldw r8, PT_R8(sp)
+ ldw r9, PT_R9(sp)
+ ldw r10, PT_R10(sp)
+ ldw r11, PT_R11(sp)
+ ldw r12, PT_R12(sp)
+ ldw r13, PT_R13(sp)
+ ldw r14, PT_R14(sp)
+ ldw r15, PT_R15(sp)
+ ldw ra, PT_RA(sp)
+ ldw fp, PT_FP(sp)
+ ldw gp, PT_GP(sp)
+ ldw r24, PT_ESTATUS(sp)
+ wrctl estatus, r24
+ ldw ea, PT_EA(sp)
+ ldw sp, PT_SP(sp) /* Restore sp last */
+.endm
+
+.macro SAVE_SWITCH_STACK
+ addi sp, sp, -SWITCH_STACK_SIZE
+ stw r16, SW_R16(sp)
+ stw r17, SW_R17(sp)
+ stw r18, SW_R18(sp)
+ stw r19, SW_R19(sp)
+ stw r20, SW_R20(sp)
+ stw r21, SW_R21(sp)
+ stw r22, SW_R22(sp)
+ stw r23, SW_R23(sp)
+ stw fp, SW_FP(sp)
+ stw gp, SW_GP(sp)
+ stw ra, SW_RA(sp)
+.endm
+
+.macro RESTORE_SWITCH_STACK
+ ldw r16, SW_R16(sp)
+ ldw r17, SW_R17(sp)
+ ldw r18, SW_R18(sp)
+ ldw r19, SW_R19(sp)
+ ldw r20, SW_R20(sp)
+ ldw r21, SW_R21(sp)
+ ldw r22, SW_R22(sp)
+ ldw r23, SW_R23(sp)
+ ldw fp, SW_FP(sp)
+ ldw gp, SW_GP(sp)
+ ldw ra, SW_RA(sp)
+ addi sp, sp, SWITCH_STACK_SIZE
+.endm
+
+#endif /* __ASSEMBLY__ */
+#endif /* _ASM_NIOS2_ENTRY_H */
--- /dev/null
+/*
+ * Copyright (C) 2014 Altera Corporation
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2004 Microtronix Datacom Ltd.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef _ASM_NIOS2_IO_H
+#define _ASM_NIOS2_IO_H
+
+#include <linux/types.h>
+#include <asm/pgtable-bits.h>
+
+/* PCI is not supported in nios2, set this to 0. */
+#define IO_SPACE_LIMIT 0
+
+#define readb_relaxed(addr) readb(addr)
+#define readw_relaxed(addr) readw(addr)
+#define readl_relaxed(addr) readl(addr)
+
+#define writeb_relaxed(x, addr) writeb(x, addr)
+#define writew_relaxed(x, addr) writew(x, addr)
+#define writel_relaxed(x, addr) writel(x, addr)
+
+extern void __iomem *__ioremap(unsigned long physaddr, unsigned long size,
+ unsigned long cacheflag);
+extern void __iounmap(void __iomem *addr);
+
+static inline void __iomem *ioremap(unsigned long physaddr, unsigned long size)
+{
+ return __ioremap(physaddr, size, 0);
+}
+
+static inline void __iomem *ioremap_nocache(unsigned long physaddr,
+ unsigned long size)
+{
+ return __ioremap(physaddr, size, 0);
+}
+
+static inline void iounmap(void __iomem *addr)
+{
+ __iounmap(addr);
+}
+
+/* Pages to physical address... */
+#define page_to_phys(page) virt_to_phys(page_to_virt(page))
+#define page_to_bus(page) page_to_virt(page)
+
+/* Macros used for converting between virtual and physical mappings. */
+#define phys_to_virt(vaddr) \
+ ((void *)((unsigned long)(vaddr) | CONFIG_NIOS2_KERNEL_REGION_BASE))
+/* Clear top 3 bits */
+#define virt_to_phys(vaddr) \
+ ((unsigned long)((unsigned long)(vaddr) & ~0xE0000000))
+
+#include <asm-generic/io.h>
+
+#endif /* _ASM_NIOS2_IO_H */
--- /dev/null
+/*
+ * Copyright (C) 2013 Altera Corporation
+ * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _ASM_NIOS2_IRQ_H
+#define _ASM_NIOS2_IRQ_H
+
+#define NIOS2_CPU_NR_IRQS 32
+
+#include <asm-generic/irq.h>
+#include <linux/irqdomain.h>
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef _ASM_IRQFLAGS_H
+#define _ASM_IRQFLAGS_H
+
+#include <asm/registers.h>
+
+static inline unsigned long arch_local_save_flags(void)
+{
+ return RDCTL(CTL_STATUS);
+}
+
+/*
+ * This will restore ALL status register flags, not only the interrupt
+ * mask flag.
+ */
+static inline void arch_local_irq_restore(unsigned long flags)
+{
+ WRCTL(CTL_STATUS, flags);
+}
+
+static inline void arch_local_irq_disable(void)
+{
+ unsigned long flags;
+
+ flags = arch_local_save_flags();
+ arch_local_irq_restore(flags & ~STATUS_PIE);
+}
+
+static inline void arch_local_irq_enable(void)
+{
+ unsigned long flags;
+
+ flags = arch_local_save_flags();
+ arch_local_irq_restore(flags | STATUS_PIE);
+}
+
+static inline int arch_irqs_disabled_flags(unsigned long flags)
+{
+ return (flags & STATUS_PIE) == 0;
+}
+
+static inline int arch_irqs_disabled(void)
+{
+ return arch_irqs_disabled_flags(arch_local_save_flags());
+}
+
+static inline unsigned long arch_local_irq_save(void)
+{
+ unsigned long flags;
+
+ flags = arch_local_save_flags();
+ arch_local_irq_restore(flags & ~STATUS_PIE);
+ return flags;
+}
+
+#endif /* _ASM_IRQFLAGS_H */
--- /dev/null
+/*
+ * Copyright (C) 2009 Thomas Chou <thomas@wytron.com.tw>
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef _ASM_NIOS2_LINKAGE_H
+#define _ASM_NIOS2_LINKAGE_H
+
+/* This file is required by include/linux/linkage.h */
+#define __ALIGN .align 4
+#define __ALIGN_STR ".align 4"
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2004 Microtronix Datacom Ltd.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef _ASM_NIOS2_MMU_H
+#define _ASM_NIOS2_MMU_H
+
+/* Default "unsigned long" context */
+typedef unsigned long mm_context_t;
+
+#endif /* _ASM_NIOS2_MMU_H */
--- /dev/null
+/*
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 1996, 1997, 1998, 1999 by Ralf Baechle
+ * Copyright (C) 1999 Silicon Graphics, Inc.
+ *
+ * based on MIPS asm/mmu_context.h
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef _ASM_NIOS2_MMU_CONTEXT_H
+#define _ASM_NIOS2_MMU_CONTEXT_H
+
+#include <asm-generic/mm_hooks.h>
+
+extern void mmu_context_init(void);
+extern unsigned long get_pid_from_context(mm_context_t *ctx);
+
+/*
+ * For the fast tlb miss handlers, we keep a pointer to the current pgd.
+ * processor.
+ */
+extern pgd_t *pgd_current;
+
+static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
+{
+}
+
+/*
+ * Initialize the context related info for a new mm_struct instance.
+ *
+ * Set all new contexts to 0, that way the generation will never match
+ * the currently running generation when this context is switched in.
+ */
+static inline int init_new_context(struct task_struct *tsk,
+ struct mm_struct *mm)
+{
+ mm->context = 0;
+ return 0;
+}
+
+/*
+ * Destroy context related info for an mm_struct that is about
+ * to be put to rest.
+ */
+static inline void destroy_context(struct mm_struct *mm)
+{
+}
+
+void switch_mm(struct mm_struct *prev, struct mm_struct *next,
+ struct task_struct *tsk);
+
+static inline void deactivate_mm(struct task_struct *tsk,
+ struct mm_struct *mm)
+{
+}
+
+/*
+ * After we have set current->mm to a new value, this activates
+ * the context for the new mm so we see the new mappings.
+ */
+void activate_mm(struct mm_struct *prev, struct mm_struct *next);
+
+#endif /* _ASM_NIOS2_MMU_CONTEXT_H */
--- /dev/null
+#include <asm-generic/mutex-dec.h>
--- /dev/null
+/*
+ * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2004 Microtronix Datacom Ltd.
+ *
+ * MMU support based on asm/page.h from mips which is:
+ *
+ * Copyright (C) 1994 - 1999, 2000, 03 Ralf Baechle
+ * Copyright (C) 1999, 2000 Silicon Graphics, Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef _ASM_NIOS2_PAGE_H
+#define _ASM_NIOS2_PAGE_H
+
+#include <linux/pfn.h>
+#include <linux/const.h>
+
+/*
+ * PAGE_SHIFT determines the page size
+ */
+#define PAGE_SHIFT 12
+#define PAGE_SIZE (_AC(1, UL) << PAGE_SHIFT)
+#define PAGE_MASK (~(PAGE_SIZE - 1))
+
+/*
+ * PAGE_OFFSET -- the first address of the first page of memory.
+ */
+#define PAGE_OFFSET \
+ (CONFIG_NIOS2_MEM_BASE + CONFIG_NIOS2_KERNEL_REGION_BASE)
+
+#ifndef __ASSEMBLY__
+
+/*
+ * This gives the physical RAM offset.
+ */
+#define PHYS_OFFSET CONFIG_NIOS2_MEM_BASE
+
+/*
+ * It's normally defined only for FLATMEM config but it's
+ * used in our early mem init code for all memory models.
+ * So always define it.
+ */
+#define ARCH_PFN_OFFSET PFN_UP(PHYS_OFFSET)
+
+#define clear_page(page) memset((page), 0, PAGE_SIZE)
+#define copy_page(to, from) memcpy((to), (from), PAGE_SIZE)
+
+struct page;
+
+extern void clear_user_page(void *addr, unsigned long vaddr, struct page *page);
+extern void copy_user_page(void *vto, void *vfrom, unsigned long vaddr,
+ struct page *to);
+
+/*
+ * These are used to make use of C type-checking.
+ */
+typedef struct page *pgtable_t;
+typedef struct { unsigned long pte; } pte_t;
+typedef struct { unsigned long pgd; } pgd_t;
+typedef struct { unsigned long pgprot; } pgprot_t;
+
+#define pte_val(x) ((x).pte)
+#define pgd_val(x) ((x).pgd)
+#define pgprot_val(x) ((x).pgprot)
+
+#define __pte(x) ((pte_t) { (x) })
+#define __pgd(x) ((pgd_t) { (x) })
+#define __pgprot(x) ((pgprot_t) { (x) })
+
+extern unsigned long memory_start;
+extern unsigned long memory_end;
+extern unsigned long memory_size;
+
+extern struct page *mem_map;
+
+#endif /* !__ASSEMBLY__ */
+
+# define __pa(x) \
+ ((unsigned long)(x) - PAGE_OFFSET + PHYS_OFFSET)
+# define __va(x) \
+ ((void *)((unsigned long)(x) + PAGE_OFFSET - PHYS_OFFSET))
+
+#define page_to_virt(page) \
+ ((((page) - mem_map) << PAGE_SHIFT) + PAGE_OFFSET)
+
+# define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT)
+# define pfn_valid(pfn) ((pfn) >= ARCH_PFN_OFFSET && \
+ (pfn) < max_mapnr)
+
+# define virt_to_page(vaddr) pfn_to_page(PFN_DOWN(virt_to_phys(vaddr)))
+# define virt_addr_valid(vaddr) pfn_valid(PFN_DOWN(virt_to_phys(vaddr)))
+
+# define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | \
+ VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
+
+# define UNCAC_ADDR(addr) \
+ ((void *)((unsigned)(addr) | CONFIG_NIOS2_IO_REGION_BASE))
+# define CAC_ADDR(addr) \
+ ((void *)(((unsigned)(addr) & ~CONFIG_NIOS2_IO_REGION_BASE) | \
+ CONFIG_NIOS2_KERNEL_REGION_BASE))
+
+#include <asm-generic/memory_model.h>
+
+#include <asm-generic/getorder.h>
+
+#endif /* _ASM_NIOS2_PAGE_H */
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1994 - 2001, 2003 by Ralf Baechle
+ * Copyright (C) 1999, 2000, 2001 Silicon Graphics, Inc.
+ */
+
+#ifndef _ASM_NIOS2_PGALLOC_H
+#define _ASM_NIOS2_PGALLOC_H
+
+#include <linux/mm.h>
+
+static inline void pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd,
+ pte_t *pte)
+{
+ set_pmd(pmd, __pmd((unsigned long)pte));
+}
+
+static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd,
+ pgtable_t pte)
+{
+ set_pmd(pmd, __pmd((unsigned long)page_address(pte)));
+}
+#define pmd_pgtable(pmd) pmd_page(pmd)
+
+/*
+ * Initialize a new pmd table with invalid pointers.
+ */
+extern void pmd_init(unsigned long page, unsigned long pagetable);
+
+extern pgd_t *pgd_alloc(struct mm_struct *mm);
+
+static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd)
+{
+ free_pages((unsigned long)pgd, PGD_ORDER);
+}
+
+static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm,
+ unsigned long address)
+{
+ pte_t *pte;
+
+ pte = (pte_t *) __get_free_pages(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO,
+ PTE_ORDER);
+
+ return pte;
+}
+
+static inline pgtable_t pte_alloc_one(struct mm_struct *mm,
+ unsigned long address)
+{
+ struct page *pte;
+
+ pte = alloc_pages(GFP_KERNEL | __GFP_REPEAT, PTE_ORDER);
+ if (pte) {
+ if (!pgtable_page_ctor(pte)) {
+ __free_page(pte);
+ return NULL;
+ }
+ clear_highpage(pte);
+ }
+ return pte;
+}
+
+static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte)
+{
+ free_pages((unsigned long)pte, PTE_ORDER);
+}
+
+static inline void pte_free(struct mm_struct *mm, struct page *pte)
+{
+ pgtable_page_dtor(pte);
+ __free_pages(pte, PTE_ORDER);
+}
+
+#define __pte_free_tlb(tlb, pte, addr) \
+ do { \
+ pgtable_page_dtor(pte); \
+ tlb_remove_page((tlb), (pte)); \
+ } while (0)
+
+#define check_pgt_cache() do { } while (0)
+
+#endif /* _ASM_NIOS2_PGALLOC_H */
--- /dev/null
+/*
+ * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2009 Wind River Systems Inc
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef _ASM_NIOS2_PGTABLE_BITS_H
+#define _ASM_NIOS2_PGTABLE_BITS_H
+
+/*
+ * These are actual hardware defined protection bits in the tlbacc register
+ * which looks like this:
+ *
+ * 31 30 ... 26 25 24 23 22 21 20 19 18 ... 1 0
+ * ignored........ C R W X G PFN............
+ */
+#define _PAGE_GLOBAL (1<<20)
+#define _PAGE_EXEC (1<<21)
+#define _PAGE_WRITE (1<<22)
+#define _PAGE_READ (1<<23)
+#define _PAGE_CACHED (1<<24) /* C: data access cacheable */
+
+/*
+ * Software defined bits. They are ignored by the hardware and always read back
+ * as zero, but can be written as non-zero.
+ */
+#define _PAGE_PRESENT (1<<25) /* PTE contains a translation */
+#define _PAGE_ACCESSED (1<<26) /* page referenced */
+#define _PAGE_DIRTY (1<<27) /* dirty page */
+#define _PAGE_FILE (1<<28) /* PTE used for file mapping or swap */
+
+#endif /* _ASM_NIOS2_PGTABLE_BITS_H */
--- /dev/null
+/*
+ * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2009 Wind River Systems Inc
+ *
+ * Based on asm/pgtable-32.h from mips which is:
+ *
+ * Copyright (C) 1994, 95, 96, 97, 98, 99, 2000, 2003 Ralf Baechle
+ * Copyright (C) 1999, 2000, 2001 Silicon Graphics, Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef _ASM_NIOS2_PGTABLE_H
+#define _ASM_NIOS2_PGTABLE_H
+
+#include <linux/io.h>
+#include <linux/bug.h>
+#include <asm/page.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+
+#include <asm/pgtable-bits.h>
+#include <asm-generic/pgtable-nopmd.h>
+
+#define FIRST_USER_ADDRESS 0
+
+#define VMALLOC_START CONFIG_NIOS2_KERNEL_MMU_REGION_BASE
+#define VMALLOC_END (CONFIG_NIOS2_KERNEL_REGION_BASE - 1)
+
+struct mm_struct;
+
+/* Helper macro */
+#define MKP(x, w, r) __pgprot(_PAGE_PRESENT | _PAGE_CACHED | \
+ ((x) ? _PAGE_EXEC : 0) | \
+ ((r) ? _PAGE_READ : 0) | \
+ ((w) ? _PAGE_WRITE : 0))
+/*
+ * These are the macros that generic kernel code needs
+ * (to populate protection_map[])
+ */
+
+/* Remove W bit on private pages for COW support */
+#define __P000 MKP(0, 0, 0)
+#define __P001 MKP(0, 0, 1)
+#define __P010 MKP(0, 0, 0) /* COW */
+#define __P011 MKP(0, 0, 1) /* COW */
+#define __P100 MKP(1, 0, 0)
+#define __P101 MKP(1, 0, 1)
+#define __P110 MKP(1, 0, 0) /* COW */
+#define __P111 MKP(1, 0, 1) /* COW */
+
+/* Shared pages can have exact HW mapping */
+#define __S000 MKP(0, 0, 0)
+#define __S001 MKP(0, 0, 1)
+#define __S010 MKP(0, 1, 0)
+#define __S011 MKP(0, 1, 1)
+#define __S100 MKP(1, 0, 0)
+#define __S101 MKP(1, 0, 1)
+#define __S110 MKP(1, 1, 0)
+#define __S111 MKP(1, 1, 1)
+
+/* Used all over the kernel */
+#define PAGE_KERNEL __pgprot(_PAGE_PRESENT | _PAGE_CACHED | _PAGE_READ | \
+ _PAGE_WRITE | _PAGE_EXEC | _PAGE_GLOBAL)
+
+#define PAGE_SHARED __pgprot(_PAGE_PRESENT | _PAGE_CACHED | _PAGE_READ | \
+ _PAGE_WRITE | _PAGE_ACCESSED)
+
+#define PAGE_COPY MKP(0, 0, 1)
+
+#define PGD_ORDER 0
+#define PTE_ORDER 0
+
+#define PTRS_PER_PGD ((PAGE_SIZE << PGD_ORDER) / sizeof(pgd_t))
+#define PTRS_PER_PTE ((PAGE_SIZE << PTE_ORDER) / sizeof(pte_t))
+
+#define USER_PTRS_PER_PGD \
+ (CONFIG_NIOS2_KERNEL_MMU_REGION_BASE / PGDIR_SIZE)
+
+#define PGDIR_SHIFT 22
+#define PGDIR_SIZE (1UL << PGDIR_SHIFT)
+#define PGDIR_MASK (~(PGDIR_SIZE-1))
+
+/*
+ * ZERO_PAGE is a global shared page that is always zero: used
+ * for zero-mapped memory areas etc..
+ */
+extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)];
+#define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page))
+
+extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
+extern pte_t invalid_pte_table[PAGE_SIZE/sizeof(pte_t)];
+
+/*
+ * (pmds are folded into puds so this doesn't get actually called,
+ * but the define is needed for a generic inline function.)
+ */
+static inline void set_pmd(pmd_t *pmdptr, pmd_t pmdval)
+{
+ pmdptr->pud.pgd.pgd = pmdval.pud.pgd.pgd;
+}
+
+/* to find an entry in a page-table-directory */
+#define pgd_index(addr) (((addr) >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1))
+#define pgd_offset(mm, addr) ((mm)->pgd + pgd_index(addr))
+
+static inline int pte_write(pte_t pte) \
+ { return pte_val(pte) & _PAGE_WRITE; }
+static inline int pte_dirty(pte_t pte) \
+ { return pte_val(pte) & _PAGE_DIRTY; }
+static inline int pte_young(pte_t pte) \
+ { return pte_val(pte) & _PAGE_ACCESSED; }
+static inline int pte_file(pte_t pte) \
+ { return pte_val(pte) & _PAGE_FILE; }
+static inline int pte_special(pte_t pte) { return 0; }
+
+#define pgprot_noncached pgprot_noncached
+
+static inline pgprot_t pgprot_noncached(pgprot_t _prot)
+{
+ unsigned long prot = pgprot_val(_prot);
+
+ prot &= ~_PAGE_CACHED;
+
+ return __pgprot(prot);
+}
+
+static inline int pte_none(pte_t pte)
+{
+ return !(pte_val(pte) & ~(_PAGE_GLOBAL|0xf));
+}
+
+static inline int pte_present(pte_t pte) \
+ { return pte_val(pte) & _PAGE_PRESENT; }
+
+/*
+ * The following only work if pte_present() is true.
+ * Undefined behaviour if not..
+ */
+static inline pte_t pte_wrprotect(pte_t pte)
+{
+ pte_val(pte) &= ~_PAGE_WRITE;
+ return pte;
+}
+
+static inline pte_t pte_mkclean(pte_t pte)
+{
+ pte_val(pte) &= ~_PAGE_DIRTY;
+ return pte;
+}
+
+static inline pte_t pte_mkold(pte_t pte)
+{
+ pte_val(pte) &= ~_PAGE_ACCESSED;
+ return pte;
+}
+
+static inline pte_t pte_mkwrite(pte_t pte)
+{
+ pte_val(pte) |= _PAGE_WRITE;
+ return pte;
+}
+
+static inline pte_t pte_mkdirty(pte_t pte)
+{
+ pte_val(pte) |= _PAGE_DIRTY;
+ return pte;
+}
+
+static inline pte_t pte_mkspecial(pte_t pte) { return pte; }
+
+static inline pte_t pte_mkyoung(pte_t pte)
+{
+ pte_val(pte) |= _PAGE_ACCESSED;
+ return pte;
+}
+
+static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
+{
+ const unsigned long mask = _PAGE_READ | _PAGE_WRITE | _PAGE_EXEC;
+
+ pte_val(pte) = (pte_val(pte) & ~mask) | (pgprot_val(newprot) & mask);
+ return pte;
+}
+
+static inline int pmd_present(pmd_t pmd)
+{
+ return (pmd_val(pmd) != (unsigned long) invalid_pte_table)
+ && (pmd_val(pmd) != 0UL);
+}
+
+static inline void pmd_clear(pmd_t *pmdp)
+{
+ pmd_val(*pmdp) = (unsigned long) invalid_pte_table;
+}
+
+#define pte_pfn(pte) (pte_val(pte) & 0xfffff)
+#define pfn_pte(pfn, prot) (__pte(pfn | pgprot_val(prot)))
+#define pte_page(pte) (pfn_to_page(pte_pfn(pte)))
+
+/*
+ * Store a linux PTE into the linux page table.
+ */
+static inline void set_pte(pte_t *ptep, pte_t pteval)
+{
+ *ptep = pteval;
+}
+
+static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
+ pte_t *ptep, pte_t pteval)
+{
+ unsigned long paddr = page_to_virt(pte_page(pteval));
+
+ flush_dcache_range(paddr, paddr + PAGE_SIZE);
+ set_pte(ptep, pteval);
+}
+
+static inline int pmd_none(pmd_t pmd)
+{
+ return (pmd_val(pmd) ==
+ (unsigned long) invalid_pte_table) || (pmd_val(pmd) == 0UL);
+}
+
+#define pmd_bad(pmd) (pmd_val(pmd) & ~PAGE_MASK)
+
+static inline void pte_clear(struct mm_struct *mm,
+ unsigned long addr, pte_t *ptep)
+{
+ pte_t null;
+
+ pte_val(null) = (addr >> PAGE_SHIFT) & 0xf;
+
+ set_pte_at(mm, addr, ptep, null);
+ flush_tlb_one(addr);
+}
+
+/*
+ * Conversion functions: convert a page and protection to a page entry,
+ * and a page entry and page directory to the page they refer to.
+ */
+#define mk_pte(page, prot) (pfn_pte(page_to_pfn(page), prot))
+
+#define pte_unmap(pte) do { } while (0)
+
+/*
+ * Conversion functions: convert a page and protection to a page entry,
+ * and a page entry and page directory to the page they refer to.
+ */
+#define pmd_phys(pmd) virt_to_phys((void *)pmd_val(pmd))
+#define pmd_page(pmd) (pfn_to_page(pmd_phys(pmd) >> PAGE_SHIFT))
+#define pmd_page_vaddr(pmd) pmd_val(pmd)
+
+#define pte_offset_map(dir, addr) \
+ ((pte_t *) page_address(pmd_page(*dir)) + \
+ (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)))
+
+/* to find an entry in a kernel page-table-directory */
+#define pgd_offset_k(addr) pgd_offset(&init_mm, addr)
+
+/* Get the address to the PTE for a vaddr in specific directory */
+#define pte_offset_kernel(dir, addr) \
+ ((pte_t *) pmd_page_vaddr(*(dir)) + \
+ (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)))
+
+#define pte_ERROR(e) \
+ pr_err("%s:%d: bad pte %08lx.\n", \
+ __FILE__, __LINE__, pte_val(e))
+#define pgd_ERROR(e) \
+ pr_err("%s:%d: bad pgd %08lx.\n", \
+ __FILE__, __LINE__, pgd_val(e))
+
+/*
+ * Encode and decode a swap entry (must be !pte_none(pte) && !pte_present(pte)
+ * && !pte_file(pte)):
+ *
+ * 31 30 29 28 27 26 25 24 23 22 21 20 19 18 ... 1 0
+ * 0 0 0 0 type. 0 0 0 0 0 0 offset.........
+ *
+ * This gives us up to 2**2 = 4 swap files and 2**20 * 4K = 4G per swap file.
+ *
+ * Note that the offset field is always non-zero, thus !pte_none(pte) is always
+ * true.
+ */
+#define __swp_type(swp) (((swp).val >> 26) & 0x3)
+#define __swp_offset(swp) ((swp).val & 0xfffff)
+#define __swp_entry(type, off) ((swp_entry_t) { (((type) & 0x3) << 26) \
+ | ((off) & 0xfffff) })
+#define __swp_entry_to_pte(swp) ((pte_t) { (swp).val })
+#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) })
+
+/* Encode and decode a nonlinear file mapping entry */
+#define PTE_FILE_MAX_BITS 25
+#define pte_to_pgoff(pte) (pte_val(pte) & 0x1ffffff)
+#define pgoff_to_pte(off) __pte(((off) & 0x1ffffff) | _PAGE_FILE)
+
+#define kern_addr_valid(addr) (1)
+
+#include <asm-generic/pgtable.h>
+
+#define pgtable_cache_init() do { } while (0)
+
+extern void __init paging_init(void);
+extern void __init mmu_init(void);
+
+extern void update_mmu_cache(struct vm_area_struct *vma,
+ unsigned long address, pte_t *pte);
+
+#endif /* _ASM_NIOS2_PGTABLE_H */
--- /dev/null
+/*
+ * Copyright (C) 2013 Altera Corporation
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2004 Microtronix Datacom Ltd
+ * Copyright (C) 2001 Ken Hill (khill@microtronix.com)
+ * Vic Phillips (vic@microtronix.com)
+ *
+ * based on SPARC asm/processor_32.h which is:
+ *
+ * Copyright (C) 1994 David S. Miller
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef _ASM_NIOS2_PROCESSOR_H
+#define _ASM_NIOS2_PROCESSOR_H
+
+#include <asm/ptrace.h>
+#include <asm/registers.h>
+#include <asm/page.h>
+
+#define NIOS2_FLAG_KTHREAD 0x00000001 /* task is a kernel thread */
+
+#define NIOS2_OP_NOP 0x1883a
+#define NIOS2_OP_BREAK 0x3da03a
+
+#ifdef __KERNEL__
+
+#define STACK_TOP TASK_SIZE
+#define STACK_TOP_MAX STACK_TOP
+
+#endif /* __KERNEL__ */
+
+/* Kuser helpers is mapped to this user space address */
+#define KUSER_BASE 0x1000
+#define KUSER_SIZE (PAGE_SIZE)
+#ifndef __ASSEMBLY__
+
+/*
+ * Default implementation of macro that returns current
+ * instruction pointer ("program counter").
+ */
+#define current_text_addr() ({ __label__ _l; _l: &&_l; })
+
+# define TASK_SIZE 0x7FFF0000UL
+# define TASK_UNMAPPED_BASE (PAGE_ALIGN(TASK_SIZE / 3))
+
+/* The Nios processor specific thread struct. */
+struct thread_struct {
+ struct pt_regs *kregs;
+
+ /* Context switch saved kernel state. */
+ unsigned long ksp;
+ unsigned long kpsr;
+};
+
+#define INIT_MMAP \
+ { &init_mm, (0), (0), __pgprot(0x0), VM_READ | VM_WRITE | VM_EXEC }
+
+# define INIT_THREAD { \
+ .kregs = NULL, \
+ .ksp = 0, \
+ .kpsr = 0, \
+}
+
+extern void start_thread(struct pt_regs *regs, unsigned long pc,
+ unsigned long sp);
+
+struct task_struct;
+
+/* Free all resources held by a thread. */
+static inline void release_thread(struct task_struct *dead_task)
+{
+}
+
+/* Free current thread data structures etc.. */
+static inline void exit_thread(void)
+{
+}
+
+/* Return saved PC of a blocked thread. */
+#define thread_saved_pc(tsk) ((tsk)->thread.kregs->ea)
+
+extern unsigned long get_wchan(struct task_struct *p);
+
+/* Prepare to copy thread state - unlazy all lazy status */
+#define prepare_to_copy(tsk) do { } while (0)
+
+#define task_pt_regs(p) \
+ ((struct pt_regs *)(THREAD_SIZE + task_stack_page(p)) - 1)
+
+/* Used by procfs */
+#define KSTK_EIP(tsk) ((tsk)->thread.kregs->ea)
+#define KSTK_ESP(tsk) ((tsk)->thread.kregs->sp)
+
+#define cpu_relax() barrier()
+#define cpu_relax_lowlatency() cpu_relax()
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* _ASM_NIOS2_PROCESSOR_H */
--- /dev/null
+/*
+ * Copyright (C) 2013 Altera Corporation
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2004 Microtronix Datacom Ltd
+ *
+ * based on m68k asm/processor.h
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef _ASM_NIOS2_PTRACE_H
+#define _ASM_NIOS2_PTRACE_H
+
+#include <uapi/asm/ptrace.h>
+
+#ifndef __ASSEMBLY__
+#define user_mode(regs) (((regs)->estatus & ESTATUS_EU))
+
+#define instruction_pointer(regs) ((regs)->ra)
+#define profile_pc(regs) instruction_pointer(regs)
+#define user_stack_pointer(regs) ((regs)->sp)
+extern void show_regs(struct pt_regs *);
+
+#define current_pt_regs() \
+ ((struct pt_regs *)((unsigned long)current_thread_info() + THREAD_SIZE)\
+ - 1)
+
+int do_syscall_trace_enter(void);
+void do_syscall_trace_exit(void);
+#endif /* __ASSEMBLY__ */
+#endif /* _ASM_NIOS2_PTRACE_H */
--- /dev/null
+/*
+ * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _ASM_NIOS2_REGISTERS_H
+#define _ASM_NIOS2_REGISTERS_H
+
+#ifndef __ASSEMBLY__
+#include <asm/cpuinfo.h>
+#endif
+
+/* control register numbers */
+#define CTL_STATUS 0
+#define CTL_ESTATUS 1
+#define CTL_BSTATUS 2
+#define CTL_IENABLE 3
+#define CTL_IPENDING 4
+#define CTL_CPUID 5
+#define CTL_RSV1 6
+#define CTL_EXCEPTION 7
+#define CTL_PTEADDR 8
+#define CTL_TLBACC 9
+#define CTL_TLBMISC 10
+#define CTL_RSV2 11
+#define CTL_BADADDR 12
+#define CTL_CONFIG 13
+#define CTL_MPUBASE 14
+#define CTL_MPUACC 15
+
+/* access control registers using GCC builtins */
+#define RDCTL(r) __builtin_rdctl(r)
+#define WRCTL(r, v) __builtin_wrctl(r, v)
+
+/* status register bits */
+#define STATUS_PIE (1 << 0) /* processor interrupt enable */
+#define STATUS_U (1 << 1) /* user mode */
+#define STATUS_EH (1 << 2) /* Exception mode */
+
+/* estatus register bits */
+#define ESTATUS_EPIE (1 << 0) /* processor interrupt enable */
+#define ESTATUS_EU (1 << 1) /* user mode */
+#define ESTATUS_EH (1 << 2) /* Exception mode */
+
+/* tlbmisc register bits */
+#define TLBMISC_PID_SHIFT 4
+#ifndef __ASSEMBLY__
+#define TLBMISC_PID_MASK ((1UL << cpuinfo.tlb_pid_num_bits) - 1)
+#endif
+#define TLBMISC_WAY_MASK 0xf
+#define TLBMISC_WAY_SHIFT 20
+
+#define TLBMISC_PID (TLBMISC_PID_MASK << TLBMISC_PID_SHIFT) /* TLB PID */
+#define TLBMISC_WE (1 << 18) /* TLB write enable */
+#define TLBMISC_RD (1 << 19) /* TLB read */
+#define TLBMISC_WAY (TLBMISC_WAY_MASK << TLBMISC_WAY_SHIFT) /* TLB way */
+
+#endif /* _ASM_NIOS2_REGISTERS_H */
--- /dev/null
+/*
+ * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _ASM_NIOS2_SETUP_H
+#define _ASM_NIOS2_SETUP_H
+
+#include <asm-generic/setup.h>
+
+#ifndef __ASSEMBLY__
+#ifdef __KERNEL__
+
+extern char exception_handler_hook[];
+extern char fast_handler[];
+extern char fast_handler_end[];
+
+extern void pagetable_init(void);
+
+extern void setup_early_printk(void);
+
+#endif/* __KERNEL__ */
+#endif /* __ASSEMBLY__ */
+
+#endif /* _ASM_NIOS2_SETUP_H */
--- /dev/null
+/*
+ * Copyright Altera Corporation (C) 2013. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef _NIOS2_SIGNAL_H
+#define _NIOS2_SIGNAL_H
+
+#include <uapi/asm/signal.h>
+
+#endif /* _NIOS2_SIGNAL_H */
--- /dev/null
+/*
+ * Copyright (C) 2004 Microtronix Datacom Ltd
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef _ASM_NIOS2_STRING_H
+#define _ASM_NIOS2_STRING_H
+
+#ifdef __KERNEL__
+
+#define __HAVE_ARCH_MEMSET
+#define __HAVE_ARCH_MEMCPY
+#define __HAVE_ARCH_MEMMOVE
+
+extern void *memset(void *s, int c, size_t count);
+extern void *memcpy(void *d, const void *s, size_t count);
+extern void *memmove(void *d, const void *s, size_t count);
+
+#endif /* __KERNEL__ */
+
+#endif /* _ASM_NIOS2_STRING_H */
--- /dev/null
+/*
+ * Copyright (C) 2004 Microtronix Datacom Ltd.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#ifndef _ASM_NIOS2_SWITCH_TO_H
+#define _ASM_NIOS2_SWITCH_TO_H
+
+/*
+ * switch_to(n) should switch tasks to task ptr, first checking that
+ * ptr isn't the current task, in which case it does nothing. This
+ * also clears the TS-flag if the task we switched to has used the
+ * math co-processor latest.
+ */
+#define switch_to(prev, next, last) \
+{ \
+ void *_last; \
+ __asm__ __volatile__ ( \
+ "mov r4, %1\n" \
+ "mov r5, %2\n" \
+ "call resume\n" \
+ "mov %0,r4\n" \
+ : "=r" (_last) \
+ : "r" (prev), "r" (next) \
+ : "r4", "r5", "r7", "r8", "ra"); \
+ (last) = _last; \
+}
+
+#endif /* _ASM_NIOS2_SWITCH_TO_H */
--- /dev/null
+/*
+ * Copyright Altera Corporation (C) <2014>. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ASM_NIOS2_SYSCALL_H__
+#define __ASM_NIOS2_SYSCALL_H__
+
+#include <linux/err.h>
+#include <linux/sched.h>
+
+static inline int syscall_get_nr(struct task_struct *task, struct pt_regs *regs)
+{
+ return regs->r2;
+}
+
+static inline void syscall_rollback(struct task_struct *task,
+ struct pt_regs *regs)
+{
+ regs->r2 = regs->orig_r2;
+ regs->r7 = regs->orig_r7;
+}
+
+static inline long syscall_get_error(struct task_struct *task,
+ struct pt_regs *regs)
+{
+ return regs->r7 ? regs->r2 : 0;
+}
+
+static inline long syscall_get_return_value(struct task_struct *task,
+ struct pt_regs *regs)
+{
+ return regs->r2;
+}
+
+static inline void syscall_set_return_value(struct task_struct *task,
+ struct pt_regs *regs, int error, long val)
+{
+ if (error) {
+ /* error < 0, but nios2 uses > 0 return value */
+ regs->r2 = -error;
+ regs->r7 = 1;
+ } else {
+ regs->r2 = val;
+ regs->r7 = 0;
+ }
+}
+
+static inline void syscall_get_arguments(struct task_struct *task,
+ struct pt_regs *regs, unsigned int i, unsigned int n,
+ unsigned long *args)
+{
+ BUG_ON(i + n > 6);
+
+ switch (i) {
+ case 0:
+ if (!n--)
+ break;
+ *args++ = regs->r4;
+ case 1:
+ if (!n--)
+ break;
+ *args++ = regs->r5;
+ case 2:
+ if (!n--)
+ break;
+ *args++ = regs->r6;
+ case 3:
+ if (!n--)
+ break;
+ *args++ = regs->r7;
+ case 4:
+ if (!n--)
+ break;
+ *args++ = regs->r8;
+ case 5:
+ if (!n--)
+ break;
+ *args++ = regs->r9;
+ case 6:
+ if (!n--)
+ break;
+ default:
+ BUG();
+ }
+}
+
+static inline void syscall_set_arguments(struct task_struct *task,
+ struct pt_regs *regs, unsigned int i, unsigned int n,
+ const unsigned long *args)
+{
+ BUG_ON(i + n > 6);
+
+ switch (i) {
+ case 0:
+ if (!n--)
+ break;
+ regs->r4 = *args++;
+ case 1:
+ if (!n--)
+ break;
+ regs->r5 = *args++;
+ case 2:
+ if (!n--)
+ break;
+ regs->r6 = *args++;
+ case 3:
+ if (!n--)
+ break;
+ regs->r7 = *args++;
+ case 4:
+ if (!n--)
+ break;
+ regs->r8 = *args++;
+ case 5:
+ if (!n--)
+ break;
+ regs->r9 = *args++;
+ case 6:
+ if (!n)
+ break;
+ default:
+ BUG();
+ }
+}
+
+#endif
--- /dev/null
+/*
+ * Copyright Altera Corporation (C) 2013. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef __ASM_NIOS2_SYSCALLS_H
+#define __ASM_NIOS2_SYSCALLS_H
+
+int sys_cacheflush(unsigned long addr, unsigned long len,
+ unsigned int op);
+
+#include <asm-generic/syscalls.h>
+
+#endif /* __ASM_NIOS2_SYSCALLS_H */
--- /dev/null
+/*
+ * NiosII low-level thread information
+ *
+ * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2004 Microtronix Datacom Ltd.
+ *
+ * Based on asm/thread_info_no.h from m68k which is:
+ *
+ * Copyright (C) 2002 David Howells <dhowells@redhat.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef _ASM_NIOS2_THREAD_INFO_H
+#define _ASM_NIOS2_THREAD_INFO_H
+
+#ifdef __KERNEL__
+
+/*
+ * Size of the kernel stack for each process.
+ */
+#define THREAD_SIZE_ORDER 1
+#define THREAD_SIZE 8192 /* 2 * PAGE_SIZE */
+
+#ifndef __ASSEMBLY__
+
+typedef struct {
+ unsigned long seg;
+} mm_segment_t;
+
+/*
+ * low level task data that entry.S needs immediate access to
+ * - this struct should fit entirely inside of one cache line
+ * - this struct shares the supervisor stack pages
+ * - if the contents of this structure are changed, the assembly constants
+ * must also be changed
+ */
+struct thread_info {
+ struct task_struct *task; /* main task structure */
+ struct exec_domain *exec_domain; /* execution domain */
+ unsigned long flags; /* low level flags */
+ __u32 cpu; /* current CPU */
+ int preempt_count; /* 0 => preemptable,<0 => BUG */
+ mm_segment_t addr_limit; /* thread address space:
+ 0-0x7FFFFFFF for user-thead
+ 0-0xFFFFFFFF for kernel-thread
+ */
+ struct restart_block restart_block;
+ struct pt_regs *regs;
+};
+
+/*
+ * macros/functions for gaining access to the thread information structure
+ *
+ * preempt_count needs to be 1 initially, until the scheduler is functional.
+ */
+#define INIT_THREAD_INFO(tsk) \
+{ \
+ .task = &tsk, \
+ .exec_domain = &default_exec_domain, \
+ .flags = 0, \
+ .cpu = 0, \
+ .preempt_count = INIT_PREEMPT_COUNT, \
+ .addr_limit = KERNEL_DS, \
+ .restart_block = { \
+ .fn = do_no_restart_syscall, \
+ }, \
+}
+
+#define init_thread_info (init_thread_union.thread_info)
+#define init_stack (init_thread_union.stack)
+
+/* how to get the thread information struct from C */
+static inline struct thread_info *current_thread_info(void)
+{
+ register unsigned long sp asm("sp");
+
+ return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
+}
+#endif /* !__ASSEMBLY__ */
+
+/*
+ * thread information flags
+ * - these are process state flags that various assembly files may need to
+ * access
+ * - pending work-to-be-done flags are in LSW
+ * - other flags in MSW
+ */
+#define TIF_SYSCALL_TRACE 0 /* syscall trace active */
+#define TIF_NOTIFY_RESUME 1 /* resumption notification requested */
+#define TIF_SIGPENDING 2 /* signal pending */
+#define TIF_NEED_RESCHED 3 /* rescheduling necessary */
+#define TIF_MEMDIE 4 /* is terminating due to OOM killer */
+#define TIF_SECCOMP 5 /* secure computing */
+#define TIF_SYSCALL_AUDIT 6 /* syscall auditing active */
+#define TIF_RESTORE_SIGMASK 9 /* restore signal mask in do_signal() */
+
+#define TIF_POLLING_NRFLAG 16 /* true if poll_idle() is polling
+ TIF_NEED_RESCHED */
+
+#define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE)
+#define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME)
+#define _TIF_SIGPENDING (1 << TIF_SIGPENDING)
+#define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED)
+#define _TIF_SECCOMP (1 << TIF_SECCOMP)
+#define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT)
+#define _TIF_RESTORE_SIGMASK (1 << TIF_RESTORE_SIGMASK)
+#define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG)
+
+/* work to do on interrupt/exception return */
+#define _TIF_WORK_MASK 0x0000FFFE
+
+/* work to do on any return to u-space */
+# define _TIF_ALLWORK_MASK 0x0000FFFF
+
+#endif /* __KERNEL__ */
+
+#endif /* _ASM_NIOS2_THREAD_INFO_H */
--- /dev/null
+/* Copyright Altera Corporation (C) 2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _ASM_NIOS2_TIMEX_H
+#define _ASM_NIOS2_TIMEX_H
+
+typedef unsigned long cycles_t;
+
+extern cycles_t get_cycles(void);
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2009 Wind River Systems Inc
+ * Copyright (C) 2004 Microtronix Datacom Ltd.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef _ASM_NIOS2_TLB_H
+#define _ASM_NIOS2_TLB_H
+
+#define tlb_flush(tlb) flush_tlb_mm((tlb)->mm)
+
+extern void set_mmu_pid(unsigned long pid);
+
+/*
+ * NiosII doesn't need any special per-pte or per-vma handling, except
+ * we need to flush cache for the area to be unmapped.
+ */
+#define tlb_start_vma(tlb, vma) \
+ do { \
+ if (!tlb->fullmm) \
+ flush_cache_range(vma, vma->vm_start, vma->vm_end); \
+ } while (0)
+
+#define tlb_end_vma(tlb, vma) do { } while (0)
+#define __tlb_remove_tlb_entry(tlb, ptep, address) do { } while (0)
+
+#include <linux/pagemap.h>
+#include <asm-generic/tlb.h>
+
+#endif /* _ASM_NIOS2_TLB_H */
--- /dev/null
+/*
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _ASM_NIOS2_TLBFLUSH_H
+#define _ASM_NIOS2_TLBFLUSH_H
+
+struct mm_struct;
+
+/*
+ * TLB flushing:
+ *
+ * - flush_tlb_all() flushes all processes TLB entries
+ * - flush_tlb_mm(mm) flushes the specified mm context TLB entries
+ * - flush_tlb_page(vma, vmaddr) flushes one page
+ * - flush_tlb_range(vma, start, end) flushes a range of pages
+ * - flush_tlb_kernel_range(start, end) flushes a range of kernel pages
+ */
+extern void flush_tlb_all(void);
+extern void flush_tlb_mm(struct mm_struct *mm);
+extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
+ unsigned long end);
+extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);
+extern void flush_tlb_one(unsigned long vaddr);
+
+static inline void flush_tlb_page(struct vm_area_struct *vma,
+ unsigned long addr)
+{
+ flush_tlb_one(addr);
+}
+
+#endif /* _ASM_NIOS2_TLBFLUSH_H */
--- /dev/null
+/*
+ * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2004 Microtronix Datacom Ltd.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef _ASM_NIOS2_TRAPS_H
+#define _ASM_NIOS2_TRAPS_H
+
+#define TRAP_ID_SYSCALL 0
+
+#ifndef __ASSEMBLY__
+void _exception(int signo, struct pt_regs *regs, int code, unsigned long addr);
+#endif
+
+#endif /* _ASM_NIOS2_TRAPS_H */
--- /dev/null
+/*
+ * User space memory access functions for Nios II
+ *
+ * Copyright (C) 2010-2011, Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2009, Wind River Systems Inc
+ * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef _ASM_NIOS2_UACCESS_H
+#define _ASM_NIOS2_UACCESS_H
+
+#include <linux/errno.h>
+#include <linux/thread_info.h>
+#include <linux/string.h>
+
+#include <asm/page.h>
+
+#define VERIFY_READ 0
+#define VERIFY_WRITE 1
+
+/*
+ * The exception table consists of pairs of addresses: the first is the
+ * address of an instruction that is allowed to fault, and the second is
+ * the address at which the program should continue. No registers are
+ * modified, so it is entirely up to the continuation code to figure out
+ * what to do.
+ *
+ * All the routines below use bits of fixup code that are out of line
+ * with the main instruction path. This means when everything is well,
+ * we don't even have to jump over them. Further, they do not intrude
+ * on our cache or tlb entries.
+ */
+struct exception_table_entry {
+ unsigned long insn;
+ unsigned long fixup;
+};
+
+extern int fixup_exception(struct pt_regs *regs);
+
+/*
+ * Segment stuff
+ */
+#define MAKE_MM_SEG(s) ((mm_segment_t) { (s) })
+#define USER_DS MAKE_MM_SEG(0x80000000UL)
+#define KERNEL_DS MAKE_MM_SEG(0)
+
+#define get_ds() (KERNEL_DS)
+
+#define get_fs() (current_thread_info()->addr_limit)
+#define set_fs(seg) (current_thread_info()->addr_limit = (seg))
+
+#define segment_eq(a, b) ((a).seg == (b).seg)
+
+#define __access_ok(addr, len) \
+ (((signed long)(((long)get_fs().seg) & \
+ ((long)(addr) | (((long)(addr)) + (len)) | (len)))) == 0)
+
+#define access_ok(type, addr, len) \
+ likely(__access_ok((unsigned long)(addr), (unsigned long)(len)))
+
+# define __EX_TABLE_SECTION ".section __ex_table,\"a\"\n"
+
+/*
+ * Zero Userspace
+ */
+
+static inline unsigned long __must_check __clear_user(void __user *to,
+ unsigned long n)
+{
+ __asm__ __volatile__ (
+ "1: stb zero, 0(%1)\n"
+ " addi %0, %0, -1\n"
+ " addi %1, %1, 1\n"
+ " bne %0, zero, 1b\n"
+ "2:\n"
+ __EX_TABLE_SECTION
+ ".word 1b, 2b\n"
+ ".previous\n"
+ : "=r" (n), "=r" (to)
+ : "0" (n), "1" (to)
+ );
+
+ return n;
+}
+
+static inline unsigned long __must_check clear_user(void __user *to,
+ unsigned long n)
+{
+ if (!access_ok(VERIFY_WRITE, to, n))
+ return n;
+ return __clear_user(to, n);
+}
+
+extern long __copy_from_user(void *to, const void __user *from,
+ unsigned long n);
+extern long __copy_to_user(void __user *to, const void *from, unsigned long n);
+
+static inline long copy_from_user(void *to, const void __user *from,
+ unsigned long n)
+{
+ if (!access_ok(VERIFY_READ, from, n))
+ return n;
+ return __copy_from_user(to, from, n);
+}
+
+static inline long copy_to_user(void __user *to, const void *from,
+ unsigned long n)
+{
+ if (!access_ok(VERIFY_WRITE, to, n))
+ return n;
+ return __copy_to_user(to, from, n);
+}
+
+extern long strncpy_from_user(char *__to, const char __user *__from,
+ long __len);
+extern long strnlen_user(const char __user *s, long n);
+
+#define __copy_from_user_inatomic __copy_from_user
+#define __copy_to_user_inatomic __copy_to_user
+
+/* Optimized macros */
+#define __get_user_asm(val, insn, addr, err) \
+{ \
+ __asm__ __volatile__( \
+ " movi %0, %3\n" \
+ "1: " insn " %1, 0(%2)\n" \
+ " movi %0, 0\n" \
+ "2:\n" \
+ " .section __ex_table,\"a\"\n" \
+ " .word 1b, 2b\n" \
+ " .previous" \
+ : "=&r" (err), "=r" (val) \
+ : "r" (addr), "i" (-EFAULT)); \
+}
+
+#define __get_user_unknown(val, size, ptr, err) do { \
+ err = 0; \
+ if (copy_from_user(&(val), ptr, size)) { \
+ err = -EFAULT; \
+ } \
+ } while (0)
+
+#define __get_user_common(val, size, ptr, err) \
+do { \
+ switch (size) { \
+ case 1: \
+ __get_user_asm(val, "ldbu", ptr, err); \
+ break; \
+ case 2: \
+ __get_user_asm(val, "ldhu", ptr, err); \
+ break; \
+ case 4: \
+ __get_user_asm(val, "ldw", ptr, err); \
+ break; \
+ default: \
+ __get_user_unknown(val, size, ptr, err); \
+ break; \
+ } \
+} while (0)
+
+#define __get_user(x, ptr) \
+ ({ \
+ long __gu_err = -EFAULT; \
+ const __typeof__(*(ptr)) __user *__gu_ptr = (ptr); \
+ unsigned long __gu_val; \
+ __get_user_common(__gu_val, sizeof(*(ptr)), __gu_ptr, __gu_err);\
+ (x) = (__typeof__(x))__gu_val; \
+ __gu_err; \
+ })
+
+#define get_user(x, ptr) \
+({ \
+ long __gu_err = -EFAULT; \
+ const __typeof__(*(ptr)) __user *__gu_ptr = (ptr); \
+ unsigned long __gu_val = 0; \
+ if (access_ok(VERIFY_READ, __gu_ptr, sizeof(*__gu_ptr))) \
+ __get_user_common(__gu_val, sizeof(*__gu_ptr), \
+ __gu_ptr, __gu_err); \
+ (x) = (__typeof__(x))__gu_val; \
+ __gu_err; \
+})
+
+#define __put_user_asm(val, insn, ptr, err) \
+{ \
+ __asm__ __volatile__( \
+ " movi %0, %3\n" \
+ "1: " insn " %1, 0(%2)\n" \
+ " movi %0, 0\n" \
+ "2:\n" \
+ " .section __ex_table,\"a\"\n" \
+ " .word 1b, 2b\n" \
+ " .previous\n" \
+ : "=&r" (err) \
+ : "r" (val), "r" (ptr), "i" (-EFAULT)); \
+}
+
+#define put_user(x, ptr) \
+({ \
+ long __pu_err = -EFAULT; \
+ __typeof__(*(ptr)) __user *__pu_ptr = (ptr); \
+ __typeof__(*(ptr)) __pu_val = (__typeof(*ptr))(x); \
+ if (access_ok(VERIFY_WRITE, __pu_ptr, sizeof(*__pu_ptr))) { \
+ switch (sizeof(*__pu_ptr)) { \
+ case 1: \
+ __put_user_asm(__pu_val, "stb", __pu_ptr, __pu_err); \
+ break; \
+ case 2: \
+ __put_user_asm(__pu_val, "sth", __pu_ptr, __pu_err); \
+ break; \
+ case 4: \
+ __put_user_asm(__pu_val, "stw", __pu_ptr, __pu_err); \
+ break; \
+ default: \
+ /* XXX: This looks wrong... */ \
+ __pu_err = 0; \
+ if (copy_to_user(__pu_ptr, &(__pu_val), \
+ sizeof(*__pu_ptr))) \
+ __pu_err = -EFAULT; \
+ break; \
+ } \
+ } \
+ __pu_err; \
+})
+
+#define __put_user(x, ptr) put_user(x, ptr)
+
+#endif /* _ASM_NIOS2_UACCESS_H */
--- /dev/null
+/*
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2004 Microtronix Datacom Ltd
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef _ASM_NIOS2_UCONTEXT_H
+#define _ASM_NIOS2_UCONTEXT_H
+
+typedef int greg_t;
+#define NGREG 32
+typedef greg_t gregset_t[NGREG];
+
+struct mcontext {
+ int version;
+ gregset_t gregs;
+};
+
+#define MCONTEXT_VERSION 2
+
+struct ucontext {
+ unsigned long uc_flags;
+ struct ucontext *uc_link;
+ stack_t uc_stack;
+ struct mcontext uc_mcontext;
+ sigset_t uc_sigmask; /* mask last for extensibility */
+};
+
+#endif
--- /dev/null
+include include/uapi/asm-generic/Kbuild.asm
+
+header-y += elf.h
+header-y += ucontext.h
--- /dev/null
+/*
+ * Copyright (C) 2009 Thomas Chou <thomas@wytron.com.tw>
+ * Copyright (C) 2004 Microtronix Datacom Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _ASM_NIOS2_BYTEORDER_H
+#define _ASM_NIOS2_BYTEORDER_H
+
+#include <linux/byteorder/little_endian.h>
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#ifndef _UAPI_ASM_NIOS2_ELF_H
+#define _UAPI_ASM_NIOS2_ELF_H
+
+#include <linux/ptrace.h>
+
+/* Relocation types */
+#define R_NIOS2_NONE 0
+#define R_NIOS2_S16 1
+#define R_NIOS2_U16 2
+#define R_NIOS2_PCREL16 3
+#define R_NIOS2_CALL26 4
+#define R_NIOS2_IMM5 5
+#define R_NIOS2_CACHE_OPX 6
+#define R_NIOS2_IMM6 7
+#define R_NIOS2_IMM8 8
+#define R_NIOS2_HI16 9
+#define R_NIOS2_LO16 10
+#define R_NIOS2_HIADJ16 11
+#define R_NIOS2_BFD_RELOC_32 12
+#define R_NIOS2_BFD_RELOC_16 13
+#define R_NIOS2_BFD_RELOC_8 14
+#define R_NIOS2_GPREL 15
+#define R_NIOS2_GNU_VTINHERIT 16
+#define R_NIOS2_GNU_VTENTRY 17
+#define R_NIOS2_UJMP 18
+#define R_NIOS2_CJMP 19
+#define R_NIOS2_CALLR 20
+#define R_NIOS2_ALIGN 21
+/* Keep this the last entry. */
+#define R_NIOS2_NUM 22
+
+typedef unsigned long elf_greg_t;
+
+#define ELF_NGREG \
+ ((sizeof(struct pt_regs) + sizeof(struct switch_stack)) / \
+ sizeof(elf_greg_t))
+typedef elf_greg_t elf_gregset_t[ELF_NGREG];
+
+typedef unsigned long elf_fpregset_t;
+
+/*
+ * These are used to set parameters in the core dumps.
+ */
+#define ELF_CLASS ELFCLASS32
+#define ELF_DATA ELFDATA2LSB
+#define ELF_ARCH EM_ALTERA_NIOS2
+
+#endif /* _UAPI_ASM_NIOS2_ELF_H */
--- /dev/null
+/*
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2004 Microtronix Datacom Ltd
+ *
+ * based on m68k asm/processor.h
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef _UAPI_ASM_NIOS2_PTRACE_H
+#define _UAPI_ASM_NIOS2_PTRACE_H
+
+#ifndef __ASSEMBLY__
+
+/*
+ * Register numbers used by 'ptrace' system call interface.
+ */
+
+/* GP registers */
+#define PTR_R0 0
+#define PTR_R1 1
+#define PTR_R2 2
+#define PTR_R3 3
+#define PTR_R4 4
+#define PTR_R5 5
+#define PTR_R6 6
+#define PTR_R7 7
+#define PTR_R8 8
+#define PTR_R9 9
+#define PTR_R10 10
+#define PTR_R11 11
+#define PTR_R12 12
+#define PTR_R13 13
+#define PTR_R14 14
+#define PTR_R15 15
+#define PTR_R16 16
+#define PTR_R17 17
+#define PTR_R18 18
+#define PTR_R19 19
+#define PTR_R20 20
+#define PTR_R21 21
+#define PTR_R22 22
+#define PTR_R23 23
+#define PTR_R24 24
+#define PTR_R25 25
+#define PTR_GP 26
+#define PTR_SP 27
+#define PTR_FP 28
+#define PTR_EA 29
+#define PTR_BA 30
+#define PTR_RA 31
+/* Control registers */
+#define PTR_PC 32
+#define PTR_STATUS 33
+#define PTR_ESTATUS 34
+#define PTR_BSTATUS 35
+#define PTR_IENABLE 36
+#define PTR_IPENDING 37
+#define PTR_CPUID 38
+#define PTR_CTL6 39
+#define PTR_CTL7 40
+#define PTR_PTEADDR 41
+#define PTR_TLBACC 42
+#define PTR_TLBMISC 43
+
+#define NUM_PTRACE_REG (PTR_TLBMISC + 1)
+
+/* this struct defines the way the registers are stored on the
+ stack during a system call.
+
+ There is a fake_regs in setup.c that has to match pt_regs.*/
+
+struct pt_regs {
+ unsigned long r8; /* r8-r15 Caller-saved GP registers */
+ unsigned long r9;
+ unsigned long r10;
+ unsigned long r11;
+ unsigned long r12;
+ unsigned long r13;
+ unsigned long r14;
+ unsigned long r15;
+ unsigned long r1; /* Assembler temporary */
+ unsigned long r2; /* Retval LS 32bits */
+ unsigned long r3; /* Retval MS 32bits */
+ unsigned long r4; /* r4-r7 Register arguments */
+ unsigned long r5;
+ unsigned long r6;
+ unsigned long r7;
+ unsigned long orig_r2; /* Copy of r2 ?? */
+ unsigned long ra; /* Return address */
+ unsigned long fp; /* Frame pointer */
+ unsigned long sp; /* Stack pointer */
+ unsigned long gp; /* Global pointer */
+ unsigned long estatus;
+ unsigned long ea; /* Exception return address (pc) */
+ unsigned long orig_r7;
+};
+
+/*
+ * This is the extended stack used by signal handlers and the context
+ * switcher: it's pushed after the normal "struct pt_regs".
+ */
+struct switch_stack {
+ unsigned long r16; /* r16-r23 Callee-saved GP registers */
+ unsigned long r17;
+ unsigned long r18;
+ unsigned long r19;
+ unsigned long r20;
+ unsigned long r21;
+ unsigned long r22;
+ unsigned long r23;
+ unsigned long fp;
+ unsigned long gp;
+ unsigned long ra;
+};
+
+#endif /* __ASSEMBLY__ */
+#endif /* _UAPI_ASM_NIOS2_PTRACE_H */
--- /dev/null
+/*
+ * Copyright (C) 2004, Microtronix Datacom Ltd.
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+#ifndef _ASM_NIOS2_SIGCONTEXT_H
+#define _ASM_NIOS2_SIGCONTEXT_H
+
+#include <asm/ptrace.h>
+
+struct sigcontext {
+ struct pt_regs regs;
+ unsigned long sc_mask; /* old sigmask */
+};
+
+#endif
--- /dev/null
+/*
+ * Copyright Altera Corporation (C) 2013. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef _ASM_NIOS2_SIGNAL_H
+#define _ASM_NIOS2_SIGNAL_H
+
+#define SA_RESTORER 0x04000000
+#include <asm-generic/signal.h>
+
+#endif /* _ASM_NIOS2_SIGNAL_H */
--- /dev/null
+/*
+ * Copyright (C) 2012 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2011 Pyramid Technical Consultants, Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file COPYING in the main directory of this
+ * archive for more details.
+ */
+
+#ifndef _ASM_NIOS2_SWAB_H
+#define _ASM_NIOS2_SWAB_H
+
+#include <linux/types.h>
+#include <asm-generic/swab.h>
+
+#ifdef CONFIG_NIOS2_CI_SWAB_SUPPORT
+#ifdef __GNUC__
+
+#define __nios2_swab(x) \
+ __builtin_custom_ini(CONFIG_NIOS2_CI_SWAB_NO, (x))
+
+static inline __attribute__((const)) __u16 __arch_swab16(__u16 x)
+{
+ return (__u16) __nios2_swab(((__u32) x) << 16);
+}
+#define __arch_swab16 __arch_swab16
+
+static inline __attribute__((const)) __u32 __arch_swab32(__u32 x)
+{
+ return (__u32) __nios2_swab(x);
+}
+#define __arch_swab32 __arch_swab32
+
+#endif /* __GNUC__ */
+#endif /* CONFIG_NIOS2_CI_SWAB_SUPPORT */
+
+#endif /* _ASM_NIOS2_SWAB_H */
--- /dev/null
+/*
+ * Copyright (C) 2013 Altera Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+ #define sys_mmap2 sys_mmap_pgoff
+
+/* Use the standard ABI for syscalls */
+#include <asm-generic/unistd.h>
+
+/* Additional Nios II specific syscalls. */
+#define __NR_cacheflush (__NR_arch_specific_syscall)
+__SYSCALL(__NR_cacheflush, sys_cacheflush)
--- /dev/null
+#
+# Makefile for the nios2 linux kernel.
+#
+
+extra-y += head.o
+extra-y += vmlinux.lds
+
+obj-y += cpuinfo.o
+obj-y += entry.o
+obj-y += insnemu.o
+obj-y += irq.o
+obj-y += nios2_ksyms.o
+obj-y += process.o
+obj-y += prom.o
+obj-y += ptrace.o
+obj-y += setup.o
+obj-y += signal.o
+obj-y += sys_nios2.o
+obj-y += syscall_table.o
+obj-y += time.o
+obj-y += traps.o
+
+obj-$(CONFIG_MODULES) += module.o
+obj-$(CONFIG_NIOS2_ALIGNMENT_TRAP) += misaligned.o
--- /dev/null
+/*
+ * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/kernel_stat.h>
+#include <linux/ptrace.h>
+#include <linux/hardirq.h>
+#include <linux/thread_info.h>
+#include <linux/kbuild.h>
+
+int main(void)
+{
+ /* struct task_struct */
+ OFFSET(TASK_THREAD, task_struct, thread);
+ BLANK();
+
+ /* struct thread_struct */
+ OFFSET(THREAD_KSP, thread_struct, ksp);
+ OFFSET(THREAD_KPSR, thread_struct, kpsr);
+ BLANK();
+
+ /* struct pt_regs */
+ OFFSET(PT_ORIG_R2, pt_regs, orig_r2);
+ OFFSET(PT_ORIG_R7, pt_regs, orig_r7);
+
+ OFFSET(PT_R1, pt_regs, r1);
+ OFFSET(PT_R2, pt_regs, r2);
+ OFFSET(PT_R3, pt_regs, r3);
+ OFFSET(PT_R4, pt_regs, r4);
+ OFFSET(PT_R5, pt_regs, r5);
+ OFFSET(PT_R6, pt_regs, r6);
+ OFFSET(PT_R7, pt_regs, r7);
+ OFFSET(PT_R8, pt_regs, r8);
+ OFFSET(PT_R9, pt_regs, r9);
+ OFFSET(PT_R10, pt_regs, r10);
+ OFFSET(PT_R11, pt_regs, r11);
+ OFFSET(PT_R12, pt_regs, r12);
+ OFFSET(PT_R13, pt_regs, r13);
+ OFFSET(PT_R14, pt_regs, r14);
+ OFFSET(PT_R15, pt_regs, r15);
+ OFFSET(PT_EA, pt_regs, ea);
+ OFFSET(PT_RA, pt_regs, ra);
+ OFFSET(PT_FP, pt_regs, fp);
+ OFFSET(PT_SP, pt_regs, sp);
+ OFFSET(PT_GP, pt_regs, gp);
+ OFFSET(PT_ESTATUS, pt_regs, estatus);
+ DEFINE(PT_REGS_SIZE, sizeof(struct pt_regs));
+ BLANK();
+
+ /* struct switch_stack */
+ OFFSET(SW_R16, switch_stack, r16);
+ OFFSET(SW_R17, switch_stack, r17);
+ OFFSET(SW_R18, switch_stack, r18);
+ OFFSET(SW_R19, switch_stack, r19);
+ OFFSET(SW_R20, switch_stack, r20);
+ OFFSET(SW_R21, switch_stack, r21);
+ OFFSET(SW_R22, switch_stack, r22);
+ OFFSET(SW_R23, switch_stack, r23);
+ OFFSET(SW_FP, switch_stack, fp);
+ OFFSET(SW_GP, switch_stack, gp);
+ OFFSET(SW_RA, switch_stack, ra);
+ DEFINE(SWITCH_STACK_SIZE, sizeof(struct switch_stack));
+ BLANK();
+
+ /* struct thread_info */
+ OFFSET(TI_FLAGS, thread_info, flags);
+ OFFSET(TI_PREEMPT_COUNT, thread_info, preempt_count);
+ BLANK();
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2013 Altera Corporation
+ * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch>
+ *
+ * Based on cpuinfo.c from microblaze
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/seq_file.h>
+#include <linux/string.h>
+#include <linux/of.h>
+#include <asm/cpuinfo.h>
+
+struct cpuinfo cpuinfo;
+
+#define err_cpu(x) \
+ pr_err("ERROR: Nios II " x " different for kernel and DTS\n")
+
+static inline u32 fcpu(struct device_node *cpu, const char *n)
+{
+ u32 val = 0;
+
+ of_property_read_u32(cpu, n, &val);
+
+ return val;
+}
+
+static inline u32 fcpu_has(struct device_node *cpu, const char *n)
+{
+ return of_get_property(cpu, n, NULL) ? 1 : 0;
+}
+
+void __init setup_cpuinfo(void)
+{
+ struct device_node *cpu;
+ const char *str;
+ int len;
+
+ cpu = of_find_node_by_type(NULL, "cpu");
+ if (!cpu)
+ panic("%s: No CPU found in devicetree!\n", __func__);
+
+ if (!fcpu_has(cpu, "altr,has-initda"))
+ panic("initda instruction is unimplemented. Please update your "
+ "hardware system to have more than 4-byte line data "
+ "cache\n");
+
+ cpuinfo.cpu_clock_freq = fcpu(cpu, "clock-frequency");
+
+ str = of_get_property(cpu, "altr,implementation", &len);
+ if (str)
+ strlcpy(cpuinfo.cpu_impl, str, sizeof(cpuinfo.cpu_impl));
+ else
+ strcpy(cpuinfo.cpu_impl, "<unknown>");
+
+ cpuinfo.has_div = fcpu_has(cpu, "altr,has-div");
+ cpuinfo.has_mul = fcpu_has(cpu, "altr,has-mul");
+ cpuinfo.has_mulx = fcpu_has(cpu, "altr,has-mulx");
+
+ if (IS_ENABLED(CONFIG_NIOS2_HW_DIV_SUPPORT) && !cpuinfo.has_div)
+ err_cpu("DIV");
+
+ if (IS_ENABLED(CONFIG_NIOS2_HW_MUL_SUPPORT) && !cpuinfo.has_mul)
+ err_cpu("MUL");
+
+ if (IS_ENABLED(CONFIG_NIOS2_HW_MULX_SUPPORT) && !cpuinfo.has_mulx)
+ err_cpu("MULX");
+
+ cpuinfo.tlb_num_ways = fcpu(cpu, "altr,tlb-num-ways");
+ if (!cpuinfo.tlb_num_ways)
+ panic("altr,tlb-num-ways can't be 0. Please check your hardware "
+ "system\n");
+ cpuinfo.icache_line_size = fcpu(cpu, "icache-line-size");
+ cpuinfo.icache_size = fcpu(cpu, "icache-size");
+ if (CONFIG_NIOS2_ICACHE_SIZE != cpuinfo.icache_size)
+ pr_warn("Warning: icache size configuration mismatch "
+ "(0x%x vs 0x%x) of CONFIG_NIOS2_ICACHE_SIZE vs "
+ "device tree icache-size\n",
+ CONFIG_NIOS2_ICACHE_SIZE, cpuinfo.icache_size);
+
+ cpuinfo.dcache_line_size = fcpu(cpu, "dcache-line-size");
+ if (CONFIG_NIOS2_DCACHE_LINE_SIZE != cpuinfo.dcache_line_size)
+ pr_warn("Warning: dcache line size configuration mismatch "
+ "(0x%x vs 0x%x) of CONFIG_NIOS2_DCACHE_LINE_SIZE vs "
+ "device tree dcache-line-size\n",
+ CONFIG_NIOS2_DCACHE_LINE_SIZE, cpuinfo.dcache_line_size);
+ cpuinfo.dcache_size = fcpu(cpu, "dcache-size");
+ if (CONFIG_NIOS2_DCACHE_SIZE != cpuinfo.dcache_size)
+ pr_warn("Warning: dcache size configuration mismatch "
+ "(0x%x vs 0x%x) of CONFIG_NIOS2_DCACHE_SIZE vs "
+ "device tree dcache-size\n",
+ CONFIG_NIOS2_DCACHE_SIZE, cpuinfo.dcache_size);
+
+ cpuinfo.tlb_pid_num_bits = fcpu(cpu, "altr,pid-num-bits");
+ cpuinfo.tlb_num_ways_log2 = ilog2(cpuinfo.tlb_num_ways);
+ cpuinfo.tlb_num_entries = fcpu(cpu, "altr,tlb-num-entries");
+ cpuinfo.tlb_num_lines = cpuinfo.tlb_num_entries / cpuinfo.tlb_num_ways;
+ cpuinfo.tlb_ptr_sz = fcpu(cpu, "altr,tlb-ptr-sz");
+
+ cpuinfo.reset_addr = fcpu(cpu, "altr,reset-addr");
+ cpuinfo.exception_addr = fcpu(cpu, "altr,exception-addr");
+ cpuinfo.fast_tlb_miss_exc_addr = fcpu(cpu, "altr,fast-tlb-miss-addr");
+}
+
+#ifdef CONFIG_PROC_FS
+
+/*
+ * Get CPU information for use by the procfs.
+ */
+static int show_cpuinfo(struct seq_file *m, void *v)
+{
+ int count = 0;
+ const u32 clockfreq = cpuinfo.cpu_clock_freq;
+
+ count = seq_printf(m,
+ "CPU:\t\tNios II/%s\n"
+ "MMU:\t\t%s\n"
+ "FPU:\t\tnone\n"
+ "Clocking:\t%u.%02u MHz\n"
+ "BogoMips:\t%lu.%02lu\n"
+ "Calibration:\t%lu loops\n",
+ cpuinfo.cpu_impl,
+ cpuinfo.mmu ? "present" : "none",
+ clockfreq / 1000000, (clockfreq / 100000) % 10,
+ (loops_per_jiffy * HZ) / 500000,
+ ((loops_per_jiffy * HZ) / 5000) % 100,
+ (loops_per_jiffy * HZ));
+
+ count += seq_printf(m,
+ "HW:\n"
+ " MUL:\t\t%s\n"
+ " MULX:\t\t%s\n"
+ " DIV:\t\t%s\n",
+ cpuinfo.has_mul ? "yes" : "no",
+ cpuinfo.has_mulx ? "yes" : "no",
+ cpuinfo.has_div ? "yes" : "no");
+
+ count += seq_printf(m,
+ "Icache:\t\t%ukB, line length: %u\n",
+ cpuinfo.icache_size >> 10,
+ cpuinfo.icache_line_size);
+
+ count += seq_printf(m,
+ "Dcache:\t\t%ukB, line length: %u\n",
+ cpuinfo.dcache_size >> 10,
+ cpuinfo.dcache_line_size);
+
+ count += seq_printf(m,
+ "TLB:\t\t%u ways, %u entries, %u PID bits\n",
+ cpuinfo.tlb_num_ways,
+ cpuinfo.tlb_num_entries,
+ cpuinfo.tlb_pid_num_bits);
+
+ return 0;
+}
+
+static void *cpuinfo_start(struct seq_file *m, loff_t *pos)
+{
+ unsigned long i = *pos;
+
+ return i < num_possible_cpus() ? (void *) (i + 1) : NULL;
+}
+
+static void *cpuinfo_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ ++*pos;
+ return cpuinfo_start(m, pos);
+}
+
+static void cpuinfo_stop(struct seq_file *m, void *v)
+{
+}
+
+const struct seq_operations cpuinfo_op = {
+ .start = cpuinfo_start,
+ .next = cpuinfo_next,
+ .stop = cpuinfo_stop,
+ .show = show_cpuinfo
+};
+
+#endif /* CONFIG_PROC_FS */
--- /dev/null
+/*
+ * linux/arch/nios2/kernel/entry.S
+ *
+ * Copyright (C) 2013-2014 Altera Corporation
+ * Copyright (C) 2009, Wind River Systems Inc
+ *
+ * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com
+ *
+ * Copyright (C) 1999-2002, Greg Ungerer (gerg@snapgear.com)
+ * Copyright (C) 1998 D. Jeff Dionne <jeff@lineo.ca>,
+ * Kenneth Albanowski <kjahds@kjahds.com>,
+ * Copyright (C) 2000 Lineo Inc. (www.lineo.com)
+ * Copyright (C) 2004 Microtronix Datacom Ltd.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Linux/m68k support by Hamish Macdonald
+ *
+ * 68060 fixes by Jesper Skov
+ * ColdFire support by Greg Ungerer (gerg@snapgear.com)
+ * 5307 fixes by David W. Miller
+ * linux 2.4 support David McCullough <davidm@snapgear.com>
+ */
+
+#include <linux/sys.h>
+#include <linux/linkage.h>
+#include <asm/asm-offsets.h>
+#include <asm/asm-macros.h>
+#include <asm/thread_info.h>
+#include <asm/errno.h>
+#include <asm/setup.h>
+#include <asm/entry.h>
+#include <asm/unistd.h>
+#include <asm/processor.h>
+
+.macro GET_THREAD_INFO reg
+.if THREAD_SIZE & 0xffff0000
+ andhi \reg, sp, %hi(~(THREAD_SIZE-1))
+.else
+ addi \reg, r0, %lo(~(THREAD_SIZE-1))
+ and \reg, \reg, sp
+.endif
+.endm
+
+.macro kuser_cmpxchg_check
+ /*
+ * Make sure our user space atomic helper is restarted if it was
+ * interrupted in a critical region.
+ * ea-4 = address of interrupted insn (ea must be preserved).
+ * sp = saved regs.
+ * cmpxchg_ldw = first critical insn, cmpxchg_stw = last critical insn.
+ * If ea <= cmpxchg_stw and ea > cmpxchg_ldw then saved EA is set to
+ * cmpxchg_ldw + 4.
+ */
+ /* et = cmpxchg_stw + 4 */
+ movui et, (KUSER_BASE + 4 + (cmpxchg_stw - __kuser_helper_start))
+ bgtu ea, et, 1f
+
+ subi et, et, (cmpxchg_stw - cmpxchg_ldw) /* et = cmpxchg_ldw + 4 */
+ bltu ea, et, 1f
+ stw et, PT_EA(sp) /* fix up EA */
+ mov ea, et
+1:
+.endm
+
+.section .rodata
+.align 4
+exception_table:
+ .word unhandled_exception /* 0 - Reset */
+ .word unhandled_exception /* 1 - Processor-only Reset */
+ .word external_interrupt /* 2 - Interrupt */
+ .word handle_trap /* 3 - Trap Instruction */
+
+ .word instruction_trap /* 4 - Unimplemented instruction */
+ .word handle_illegal /* 5 - Illegal instruction */
+ .word handle_unaligned /* 6 - Misaligned data access */
+ .word handle_unaligned /* 7 - Misaligned destination address */
+
+ .word handle_diverror /* 8 - Division error */
+ .word protection_exception_ba /* 9 - Supervisor-only instr. address */
+ .word protection_exception_instr /* 10 - Supervisor only instruction */
+ .word protection_exception_ba /* 11 - Supervisor only data address */
+
+ .word unhandled_exception /* 12 - Double TLB miss (data) */
+ .word protection_exception_pte /* 13 - TLB permission violation (x) */
+ .word protection_exception_pte /* 14 - TLB permission violation (r) */
+ .word protection_exception_pte /* 15 - TLB permission violation (w) */
+
+ .word unhandled_exception /* 16 - MPU region violation */
+
+trap_table:
+ .word handle_system_call /* 0 */
+ .word instruction_trap /* 1 */
+ .word instruction_trap /* 2 */
+ .word instruction_trap /* 3 */
+ .word instruction_trap /* 4 */
+ .word instruction_trap /* 5 */
+ .word instruction_trap /* 6 */
+ .word instruction_trap /* 7 */
+ .word instruction_trap /* 8 */
+ .word instruction_trap /* 9 */
+ .word instruction_trap /* 10 */
+ .word instruction_trap /* 11 */
+ .word instruction_trap /* 12 */
+ .word instruction_trap /* 13 */
+ .word instruction_trap /* 14 */
+ .word instruction_trap /* 15 */
+ .word instruction_trap /* 16 */
+ .word instruction_trap /* 17 */
+ .word instruction_trap /* 18 */
+ .word instruction_trap /* 19 */
+ .word instruction_trap /* 20 */
+ .word instruction_trap /* 21 */
+ .word instruction_trap /* 22 */
+ .word instruction_trap /* 23 */
+ .word instruction_trap /* 24 */
+ .word instruction_trap /* 25 */
+ .word instruction_trap /* 26 */
+ .word instruction_trap /* 27 */
+ .word instruction_trap /* 28 */
+ .word instruction_trap /* 29 */
+ .word instruction_trap /* 30 */
+ .word handle_breakpoint /* 31 */
+
+.text
+.set noat
+.set nobreak
+
+ENTRY(inthandler)
+ SAVE_ALL
+
+ kuser_cmpxchg_check
+
+ /* Clear EH bit before we get a new excpetion in the kernel
+ * and after we have saved it to the exception frame. This is done
+ * whether it's trap, tlb-miss or interrupt. If we don't do this
+ * estatus is not updated the next exception.
+ */
+ rdctl r24, status
+ movi r9, %lo(~STATUS_EH)
+ and r24, r24, r9
+ wrctl status, r24
+
+ /* Read cause and vector and branch to the associated handler */
+ mov r4, sp
+ rdctl r5, exception
+ movia r9, exception_table
+ add r24, r9, r5
+ ldw r24, 0(r24)
+ jmp r24
+
+
+/***********************************************************************
+ * Handle traps
+ ***********************************************************************
+ */
+ENTRY(handle_trap)
+ ldw r24, -4(ea) /* instruction that caused the exception */
+ srli r24, r24, 4
+ andi r24, r24, 0x7c
+ movia r9,trap_table
+ add r24, r24, r9
+ ldw r24, 0(r24)
+ jmp r24
+
+
+/***********************************************************************
+ * Handle system calls
+ ***********************************************************************
+ */
+ENTRY(handle_system_call)
+ /* Enable interrupts */
+ rdctl r10, status
+ ori r10, r10, STATUS_PIE
+ wrctl status, r10
+
+ /* Reload registers destroyed by common code. */
+ ldw r4, PT_R4(sp)
+ ldw r5, PT_R5(sp)
+
+local_restart:
+ /* Check that the requested system call is within limits */
+ movui r1, __NR_syscalls
+ bgeu r2, r1, ret_invsyscall
+ slli r1, r2, 2
+ movhi r11, %hiadj(sys_call_table)
+ add r1, r1, r11
+ ldw r1, %lo(sys_call_table)(r1)
+ beq r1, r0, ret_invsyscall
+
+ /* Check if we are being traced */
+ GET_THREAD_INFO r11
+ ldw r11,TI_FLAGS(r11)
+ BTBNZ r11,r11,TIF_SYSCALL_TRACE,traced_system_call
+
+ /* Execute the system call */
+ callr r1
+
+ /* If the syscall returns a negative result:
+ * Set r7 to 1 to indicate error,
+ * Negate r2 to get a positive error code
+ * If the syscall returns zero or a positive value:
+ * Set r7 to 0.
+ * The sigreturn system calls will skip the code below by
+ * adding to register ra. To avoid destroying registers
+ */
+translate_rc_and_ret:
+ movi r1, 0
+ bge r2, zero, 3f
+ sub r2, zero, r2
+ movi r1, 1
+3:
+ stw r2, PT_R2(sp)
+ stw r1, PT_R7(sp)
+end_translate_rc_and_ret:
+
+ret_from_exception:
+ ldw r1, PT_ESTATUS(sp)
+ /* if so, skip resched, signals */
+ TSTBNZ r1, r1, ESTATUS_EU, Luser_return
+
+restore_all:
+ rdctl r10, status /* disable intrs */
+ andi r10, r10, %lo(~STATUS_PIE)
+ wrctl status, r10
+ RESTORE_ALL
+ eret
+
+ /* If the syscall number was invalid return ENOSYS */
+ret_invsyscall:
+ movi r2, -ENOSYS
+ br translate_rc_and_ret
+
+ /* This implements the same as above, except it calls
+ * do_syscall_trace_enter and do_syscall_trace_exit before and after the
+ * syscall in order for utilities like strace and gdb to work.
+ */
+traced_system_call:
+ SAVE_SWITCH_STACK
+ call do_syscall_trace_enter
+ RESTORE_SWITCH_STACK
+
+ /* Create system call register arguments. The 5th and 6th
+ arguments on stack are already in place at the beginning
+ of pt_regs. */
+ ldw r2, PT_R2(sp)
+ ldw r4, PT_R4(sp)
+ ldw r5, PT_R5(sp)
+ ldw r6, PT_R6(sp)
+ ldw r7, PT_R7(sp)
+
+ /* Fetch the syscall function, we don't need to check the boundaries
+ * since this is already done.
+ */
+ slli r1, r2, 2
+ movhi r11,%hiadj(sys_call_table)
+ add r1, r1, r11
+ ldw r1, %lo(sys_call_table)(r1)
+
+ callr r1
+
+ /* If the syscall returns a negative result:
+ * Set r7 to 1 to indicate error,
+ * Negate r2 to get a positive error code
+ * If the syscall returns zero or a positive value:
+ * Set r7 to 0.
+ * The sigreturn system calls will skip the code below by
+ * adding to register ra. To avoid destroying registers
+ */
+translate_rc_and_ret2:
+ movi r1, 0
+ bge r2, zero, 4f
+ sub r2, zero, r2
+ movi r1, 1
+4:
+ stw r2, PT_R2(sp)
+ stw r1, PT_R7(sp)
+end_translate_rc_and_ret2:
+ SAVE_SWITCH_STACK
+ call do_syscall_trace_exit
+ RESTORE_SWITCH_STACK
+ br ret_from_exception
+
+Luser_return:
+ GET_THREAD_INFO r11 /* get thread_info pointer */
+ ldw r10, TI_FLAGS(r11) /* get thread_info->flags */
+ ANDI32 r11, r10, _TIF_WORK_MASK
+ beq r11, r0, restore_all /* Nothing to do */
+ BTBZ r1, r10, TIF_NEED_RESCHED, Lsignal_return
+
+ /* Reschedule work */
+ call schedule
+ br ret_from_exception
+
+Lsignal_return:
+ ANDI32 r1, r10, _TIF_SIGPENDING | _TIF_NOTIFY_RESUME
+ beq r1, r0, restore_all
+ mov r4, sp /* pt_regs */
+ SAVE_SWITCH_STACK
+ call do_notify_resume
+ beq r2, r0, no_work_pending
+ RESTORE_SWITCH_STACK
+ /* prepare restart syscall here without leaving kernel */
+ ldw r2, PT_R2(sp) /* reload syscall number in r2 */
+ ldw r4, PT_R4(sp) /* reload syscall arguments r4-r9 */
+ ldw r5, PT_R5(sp)
+ ldw r6, PT_R6(sp)
+ ldw r7, PT_R7(sp)
+ ldw r8, PT_R8(sp)
+ ldw r9, PT_R9(sp)
+ br local_restart /* restart syscall */
+
+no_work_pending:
+ RESTORE_SWITCH_STACK
+ br ret_from_exception
+
+/***********************************************************************
+ * Handle external interrupts.
+ ***********************************************************************
+ */
+/*
+ * This is the generic interrupt handler (for all hardware interrupt
+ * sources). It figures out the vector number and calls the appropriate
+ * interrupt service routine directly.
+ */
+external_interrupt:
+ rdctl r12, ipending
+ rdctl r9, ienable
+ and r12, r12, r9
+ /* skip if no interrupt is pending */
+ beq r12, r0, ret_from_interrupt
+
+ movi r24, -1
+ stw r24, PT_ORIG_R2(sp)
+
+ /*
+ * Process an external hardware interrupt.
+ */
+
+ addi ea, ea, -4 /* re-issue the interrupted instruction */
+ stw ea, PT_EA(sp)
+2: movi r4, %lo(-1) /* Start from bit position 0,
+ highest priority */
+ /* This is the IRQ # for handler call */
+1: andi r10, r12, 1 /* Isolate bit we are interested in */
+ srli r12, r12, 1 /* shift count is costly without hardware
+ multiplier */
+ addi r4, r4, 1
+ beq r10, r0, 1b
+ mov r5, sp /* Setup pt_regs pointer for handler call */
+ call do_IRQ
+ rdctl r12, ipending /* check again if irq still pending */
+ rdctl r9, ienable /* Isolate possible interrupts */
+ and r12, r12, r9
+ bne r12, r0, 2b
+ /* br ret_from_interrupt */ /* fall through to ret_from_interrupt */
+
+ENTRY(ret_from_interrupt)
+ ldw r1, PT_ESTATUS(sp) /* check if returning to kernel */
+ TSTBNZ r1, r1, ESTATUS_EU, Luser_return
+
+#ifdef CONFIG_PREEMPT
+ GET_THREAD_INFO r1
+ ldw r4, TI_PREEMPT_COUNT(r1)
+ bne r4, r0, restore_all
+
+need_resched:
+ ldw r4, TI_FLAGS(r1) /* ? Need resched set */
+ BTBZ r10, r4, TIF_NEED_RESCHED, restore_all
+ ldw r4, PT_ESTATUS(sp) /* ? Interrupts off */
+ andi r10, r4, ESTATUS_EPIE
+ beq r10, r0, restore_all
+ movia r4, PREEMPT_ACTIVE
+ stw r4, TI_PREEMPT_COUNT(r1)
+ rdctl r10, status /* enable intrs again */
+ ori r10, r10 ,STATUS_PIE
+ wrctl status, r10
+ PUSH r1
+ call schedule
+ POP r1
+ mov r4, r0
+ stw r4, TI_PREEMPT_COUNT(r1)
+ rdctl r10, status /* disable intrs */
+ andi r10, r10, %lo(~STATUS_PIE)
+ wrctl status, r10
+ br need_resched
+#else
+ br restore_all
+#endif
+
+/***********************************************************************
+ * A few syscall wrappers
+ ***********************************************************************
+ */
+/*
+ * int clone(unsigned long clone_flags, unsigned long newsp,
+ * int __user * parent_tidptr, int __user * child_tidptr,
+ * int tls_val)
+ */
+ENTRY(sys_clone)
+ SAVE_SWITCH_STACK
+ addi sp, sp, -4
+ stw r7, 0(sp) /* Pass 5th arg thru stack */
+ mov r7, r6 /* 4th arg is 3rd of clone() */
+ mov r6, zero /* 3rd arg always 0 */
+ call do_fork
+ addi sp, sp, 4
+ RESTORE_SWITCH_STACK
+ ret
+
+ENTRY(sys_rt_sigreturn)
+ SAVE_SWITCH_STACK
+ mov r4, sp
+ call do_rt_sigreturn
+ RESTORE_SWITCH_STACK
+ addi ra, ra, (end_translate_rc_and_ret - translate_rc_and_ret)
+ ret
+
+/***********************************************************************
+ * A few other wrappers and stubs
+ ***********************************************************************
+ */
+protection_exception_pte:
+ rdctl r6, pteaddr
+ slli r6, r6, 10
+ call do_page_fault
+ br ret_from_exception
+
+protection_exception_ba:
+ rdctl r6, badaddr
+ call do_page_fault
+ br ret_from_exception
+
+protection_exception_instr:
+ call handle_supervisor_instr
+ br ret_from_exception
+
+handle_breakpoint:
+ call breakpoint_c
+ br ret_from_exception
+
+#ifdef CONFIG_NIOS2_ALIGNMENT_TRAP
+handle_unaligned:
+ SAVE_SWITCH_STACK
+ call handle_unaligned_c
+ RESTORE_SWITCH_STACK
+ br ret_from_exception
+#else
+handle_unaligned:
+ call handle_unaligned_c
+ br ret_from_exception
+#endif
+
+handle_illegal:
+ call handle_illegal_c
+ br ret_from_exception
+
+handle_diverror:
+ call handle_diverror_c
+ br ret_from_exception
+
+/*
+ * Beware - when entering resume, prev (the current task) is
+ * in r4, next (the new task) is in r5, don't change these
+ * registers.
+ */
+ENTRY(resume)
+
+ rdctl r7, status /* save thread status reg */
+ stw r7, TASK_THREAD + THREAD_KPSR(r4)
+
+ andi r7, r7, %lo(~STATUS_PIE) /* disable interrupts */
+ wrctl status, r7
+
+ SAVE_SWITCH_STACK
+ stw sp, TASK_THREAD + THREAD_KSP(r4)/* save kernel stack pointer */
+ ldw sp, TASK_THREAD + THREAD_KSP(r5)/* restore new thread stack */
+ movia r24, _current_thread /* save thread */
+ GET_THREAD_INFO r1
+ stw r1, 0(r24)
+ RESTORE_SWITCH_STACK
+
+ ldw r7, TASK_THREAD + THREAD_KPSR(r5)/* restore thread status reg */
+ wrctl status, r7
+ ret
+
+ENTRY(ret_from_fork)
+ call schedule_tail
+ br ret_from_exception
+
+ENTRY(ret_from_kernel_thread)
+ call schedule_tail
+ mov r4,r17 /* arg */
+ callr r16 /* function */
+ br ret_from_exception
+
+/*
+ * Kernel user helpers.
+ *
+ * Each segment is 64-byte aligned and will be mapped to the <User space>.
+ * New segments (if ever needed) must be added after the existing ones.
+ * This mechanism should be used only for things that are really small and
+ * justified, and not be abused freely.
+ *
+ */
+
+ /* Filling pads with undefined instructions. */
+.macro kuser_pad sym size
+ .if ((. - \sym) & 3)
+ .rept (4 - (. - \sym) & 3)
+ .byte 0
+ .endr
+ .endif
+ .rept ((\size - (. - \sym)) / 4)
+ .word 0xdeadbeef
+ .endr
+.endm
+
+ .align 6
+ .globl __kuser_helper_start
+__kuser_helper_start:
+
+__kuser_helper_version: /* @ 0x1000 */
+ .word ((__kuser_helper_end - __kuser_helper_start) >> 6)
+
+__kuser_cmpxchg: /* @ 0x1004 */
+ /*
+ * r4 pointer to exchange variable
+ * r5 old value
+ * r6 new value
+ */
+cmpxchg_ldw:
+ ldw r2, 0(r4) /* load current value */
+ sub r2, r2, r5 /* compare with old value */
+ bne r2, zero, cmpxchg_ret
+
+ /* We had a match, store the new value */
+cmpxchg_stw:
+ stw r6, 0(r4)
+cmpxchg_ret:
+ ret
+
+ kuser_pad __kuser_cmpxchg, 64
+
+ .globl __kuser_sigtramp
+__kuser_sigtramp:
+ movi r2, __NR_rt_sigreturn
+ trap
+
+ kuser_pad __kuser_sigtramp, 64
+
+ .globl __kuser_helper_end
+__kuser_helper_end:
--- /dev/null
+/*
+ * Copyright (C) 2009 Wind River Systems Inc
+ * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com
+ * Copyright (C) 2004 Microtronix Datacom Ltd
+ * Copyright (C) 2001 Vic Phillips, Microtronix Datacom Ltd.
+ *
+ * Based on head.S for Altera's Excalibur development board with nios processor
+ *
+ * Based on the following from the Excalibur sdk distribution:
+ * NA_MemoryMap.s, NR_JumpToStart.s, NR_Setup.s, NR_CWPManager.s
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/linkage.h>
+#include <asm/thread_info.h>
+#include <asm/processor.h>
+#include <asm/cache.h>
+#include <asm/page.h>
+#include <asm/asm-offsets.h>
+#include <asm/asm-macros.h>
+
+/*
+ * ZERO_PAGE is a special page that is used for zero-initialized
+ * data and COW.
+ */
+.data
+.global empty_zero_page
+.align 12
+empty_zero_page:
+ .space PAGE_SIZE
+
+/*
+ * This global variable is used as an extension to the nios'
+ * STATUS register to emulate a user/supervisor mode.
+ */
+ .data
+ .align 2
+ .set noat
+
+ .global _current_thread
+_current_thread:
+ .long 0
+/*
+ * Input(s): passed from u-boot
+ * r4 - Optional pointer to a board information structure.
+ * r5 - Optional pointer to the physical starting address of the init RAM
+ * disk.
+ * r6 - Optional pointer to the physical ending address of the init RAM
+ * disk.
+ * r7 - Optional pointer to the physical starting address of any kernel
+ * command-line parameters.
+ */
+
+/*
+ * First executable code - detected and jumped to by the ROM bootstrap
+ * if the code resides in flash (looks for "Nios" at offset 0x0c from
+ * the potential executable image).
+ */
+ __HEAD
+ENTRY(_start)
+ wrctl status, r0 /* Disable interrupts */
+
+ /* Initialize all cache lines within the instruction cache */
+ movia r1, NIOS2_ICACHE_SIZE
+ movui r2, NIOS2_ICACHE_LINE_SIZE
+
+icache_init:
+ initi r1
+ sub r1, r1, r2
+ bgt r1, r0, icache_init
+ br 1f
+
+ /*
+ * This is the default location for the exception handler. Code in jump
+ * to our handler
+ */
+ENTRY(exception_handler_hook)
+ movia r24, inthandler
+ jmp r24
+
+ENTRY(fast_handler)
+ nextpc et
+helper:
+ stw r3, r3save - helper(et)
+
+ rdctl r3 , pteaddr
+ srli r3, r3, 12
+ slli r3, r3, 2
+ movia et, pgd_current
+
+ ldw et, 0(et)
+ add r3, et, r3
+ ldw et, 0(r3)
+
+ rdctl r3, pteaddr
+ andi r3, r3, 0xfff
+ add et, r3, et
+ ldw et, 0(et)
+ wrctl tlbacc, et
+ nextpc et
+helper2:
+ ldw r3, r3save - helper2(et)
+ subi ea, ea, 4
+ eret
+r3save:
+ .word 0x0
+ENTRY(fast_handler_end)
+
+1:
+ /*
+ * After the instruction cache is initialized, the data cache must
+ * also be initialized.
+ */
+ movia r1, NIOS2_DCACHE_SIZE
+ movui r2, NIOS2_DCACHE_LINE_SIZE
+
+dcache_init:
+ initd 0(r1)
+ sub r1, r1, r2
+ bgt r1, r0, dcache_init
+
+ nextpc r1 /* Find out where we are */
+chkadr:
+ movia r2, chkadr
+ beq r1, r2,finish_move /* We are running in RAM done */
+ addi r1, r1,(_start - chkadr) /* Source */
+ movia r2, _start /* Destination */
+ movia r3, __bss_start /* End of copy */
+
+loop_move: /* r1: src, r2: dest, r3: last dest */
+ ldw r8, 0(r1) /* load a word from [r1] */
+ stw r8, 0(r2) /* store a word to dest [r2] */
+ flushd 0(r2) /* Flush cache for safety */
+ addi r1, r1, 4 /* inc the src addr */
+ addi r2, r2, 4 /* inc the dest addr */
+ blt r2, r3, loop_move
+
+ movia r1, finish_move /* VMA(_start)->l1 */
+ jmp r1 /* jmp to _start */
+
+finish_move:
+
+ /* Mask off all possible interrupts */
+ wrctl ienable, r0
+
+ /* Clear .bss */
+ movia r2, __bss_start
+ movia r1, __bss_stop
+1:
+ stb r0, 0(r2)
+ addi r2, r2, 1
+ bne r1, r2, 1b
+
+ movia r1, init_thread_union /* set stack at top of the task union */
+ addi sp, r1, THREAD_SIZE
+ movia r2, _current_thread /* Remember current thread */
+ stw r1, 0(r2)
+
+ movia r1, nios2_boot_init /* save args r4-r7 passed from u-boot */
+ callr r1
+
+ movia r1, start_kernel /* call start_kernel as a subroutine */
+ callr r1
+
+ /* If we return from start_kernel, break to the oci debugger and
+ * buggered we are.
+ */
+ break
+
+ /* End of startup code */
+.set at
--- /dev/null
+/*
+ * Copyright (C) 2003-2013 Altera Corporation
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <linux/linkage.h>
+#include <asm/entry.h>
+
+.set noat
+.set nobreak
+
+/*
+* Explicitly allow the use of r1 (the assembler temporary register)
+* within this code. This register is normally reserved for the use of
+* the compiler.
+*/
+
+ENTRY(instruction_trap)
+ ldw r1, PT_R1(sp) // Restore registers
+ ldw r2, PT_R2(sp)
+ ldw r3, PT_R3(sp)
+ ldw r4, PT_R4(sp)
+ ldw r5, PT_R5(sp)
+ ldw r6, PT_R6(sp)
+ ldw r7, PT_R7(sp)
+ ldw r8, PT_R8(sp)
+ ldw r9, PT_R9(sp)
+ ldw r10, PT_R10(sp)
+ ldw r11, PT_R11(sp)
+ ldw r12, PT_R12(sp)
+ ldw r13, PT_R13(sp)
+ ldw r14, PT_R14(sp)
+ ldw r15, PT_R15(sp)
+ ldw ra, PT_RA(sp)
+ ldw fp, PT_FP(sp)
+ ldw gp, PT_GP(sp)
+ ldw et, PT_ESTATUS(sp)
+ wrctl estatus, et
+ ldw ea, PT_EA(sp)
+ ldw et, PT_SP(sp) /* backup sp in et */
+
+ addi sp, sp, PT_REGS_SIZE
+
+ /* INSTRUCTION EMULATION
+ * ---------------------
+ *
+ * Nios II processors generate exceptions for unimplemented instructions.
+ * The routines below emulate these instructions. Depending on the
+ * processor core, the only instructions that might need to be emulated
+ * are div, divu, mul, muli, mulxss, mulxsu, and mulxuu.
+ *
+ * The emulations match the instructions, except for the following
+ * limitations:
+ *
+ * 1) The emulation routines do not emulate the use of the exception
+ * temporary register (et) as a source operand because the exception
+ * handler already has modified it.
+ *
+ * 2) The routines do not emulate the use of the stack pointer (sp) or
+ * the exception return address register (ea) as a destination because
+ * modifying these registers crashes the exception handler or the
+ * interrupted routine.
+ *
+ * Detailed Design
+ * ---------------
+ *
+ * The emulation routines expect the contents of integer registers r0-r31
+ * to be on the stack at addresses sp, 4(sp), 8(sp), ... 124(sp). The
+ * routines retrieve source operands from the stack and modify the
+ * destination register's value on the stack prior to the end of the
+ * exception handler. Then all registers except the destination register
+ * are restored to their previous values.
+ *
+ * The instruction that causes the exception is found at address -4(ea).
+ * The instruction's OP and OPX fields identify the operation to be
+ * performed.
+ *
+ * One instruction, muli, is an I-type instruction that is identified by
+ * an OP field of 0x24.
+ *
+ * muli AAAAA,BBBBB,IIIIIIIIIIIIIIII,-0x24-
+ * 27 22 6 0 <-- LSB of field
+ *
+ * The remaining emulated instructions are R-type and have an OP field
+ * of 0x3a. Their OPX fields identify them.
+ *
+ * R-type AAAAA,BBBBB,CCCCC,XXXXXX,NNNNN,-0x3a-
+ * 27 22 17 11 6 0 <-- LSB of field
+ *
+ *
+ * Opcode Encoding. muli is identified by its OP value. Then OPX & 0x02
+ * is used to differentiate between the division opcodes and the
+ * remaining multiplication opcodes.
+ *
+ * Instruction OP OPX OPX & 0x02
+ * ----------- ---- ---- ----------
+ * muli 0x24
+ * divu 0x3a 0x24 0
+ * div 0x3a 0x25 0
+ * mul 0x3a 0x27 != 0
+ * mulxuu 0x3a 0x07 != 0
+ * mulxsu 0x3a 0x17 != 0
+ * mulxss 0x3a 0x1f != 0
+ */
+
+
+ /*
+ * Save everything on the stack to make it easy for the emulation
+ * routines to retrieve the source register operands.
+ */
+
+ addi sp, sp, -128
+ stw zero, 0(sp) /* Save zero on stack to avoid special case for r0. */
+ stw r1, 4(sp)
+ stw r2, 8(sp)
+ stw r3, 12(sp)
+ stw r4, 16(sp)
+ stw r5, 20(sp)
+ stw r6, 24(sp)
+ stw r7, 28(sp)
+ stw r8, 32(sp)
+ stw r9, 36(sp)
+ stw r10, 40(sp)
+ stw r11, 44(sp)
+ stw r12, 48(sp)
+ stw r13, 52(sp)
+ stw r14, 56(sp)
+ stw r15, 60(sp)
+ stw r16, 64(sp)
+ stw r17, 68(sp)
+ stw r18, 72(sp)
+ stw r19, 76(sp)
+ stw r20, 80(sp)
+ stw r21, 84(sp)
+ stw r22, 88(sp)
+ stw r23, 92(sp)
+ /* Don't bother to save et. It's already been changed. */
+ rdctl r5, estatus
+ stw r5, 100(sp)
+
+ stw gp, 104(sp)
+ stw et, 108(sp) /* et contains previous sp value. */
+ stw fp, 112(sp)
+ stw ea, 116(sp)
+ stw ra, 120(sp)
+
+
+ /*
+ * Split the instruction into its fields. We need 4*A, 4*B, and 4*C as
+ * offsets to the stack pointer for access to the stored register values.
+ */
+ ldw r2,-4(ea) /* r2 = AAAAA,BBBBB,IIIIIIIIIIIIIIII,PPPPPP */
+ roli r3, r2, 7 /* r3 = BBB,IIIIIIIIIIIIIIII,PPPPPP,AAAAA,BB */
+ roli r4, r3, 3 /* r4 = IIIIIIIIIIIIIIII,PPPPPP,AAAAA,BBBBB */
+ roli r5, r4, 2 /* r5 = IIIIIIIIIIIIII,PPPPPP,AAAAA,BBBBB,II */
+ srai r4, r4, 16 /* r4 = (sign-extended) IMM16 */
+ roli r6, r5, 5 /* r6 = XXXX,NNNNN,PPPPPP,AAAAA,BBBBB,CCCCC,XX */
+ andi r2, r2, 0x3f /* r2 = 00000000000000000000000000,PPPPPP */
+ andi r3, r3, 0x7c /* r3 = 0000000000000000000000000,AAAAA,00 */
+ andi r5, r5, 0x7c /* r5 = 0000000000000000000000000,BBBBB,00 */
+ andi r6, r6, 0x7c /* r6 = 0000000000000000000000000,CCCCC,00 */
+
+ /* Now
+ * r2 = OP
+ * r3 = 4*A
+ * r4 = IMM16 (sign extended)
+ * r5 = 4*B
+ * r6 = 4*C
+ */
+
+ /*
+ * Get the operands.
+ *
+ * It is necessary to check for muli because it uses an I-type
+ * instruction format, while the other instructions are have an R-type
+ * format.
+ *
+ * Prepare for either multiplication or division loop.
+ * They both loop 32 times.
+ */
+ movi r14, 32
+
+ add r3, r3, sp /* r3 = address of A-operand. */
+ ldw r3, 0(r3) /* r3 = A-operand. */
+ movi r7, 0x24 /* muli opcode (I-type instruction format) */
+ beq r2, r7, mul_immed /* muli doesn't use the B register as a source */
+
+ add r5, r5, sp /* r5 = address of B-operand. */
+ ldw r5, 0(r5) /* r5 = B-operand. */
+ /* r4 = SSSSSSSSSSSSSSSS,-----IMM16------ */
+ /* IMM16 not needed, align OPX portion */
+ /* r4 = SSSSSSSSSSSSSSSS,CCCCC,-OPX--,00000 */
+ srli r4, r4, 5 /* r4 = 00000,SSSSSSSSSSSSSSSS,CCCCC,-OPX-- */
+ andi r4, r4, 0x3f /* r4 = 00000000000000000000000000,-OPX-- */
+
+ /* Now
+ * r2 = OP
+ * r3 = src1
+ * r5 = src2
+ * r4 = OPX (no longer can be muli)
+ * r6 = 4*C
+ */
+
+
+ /*
+ * Multiply or Divide?
+ */
+ andi r7, r4, 0x02 /* For R-type multiply instructions,
+ OPX & 0x02 != 0 */
+ bne r7, zero, multiply
+
+
+ /* DIVISION
+ *
+ * Divide an unsigned dividend by an unsigned divisor using
+ * a shift-and-subtract algorithm. The example below shows
+ * 43 div 7 = 6 for 8-bit integers. This classic algorithm uses a
+ * single register to store both the dividend and the quotient,
+ * allowing both values to be shifted with a single instruction.
+ *
+ * remainder dividend:quotient
+ * --------- -----------------
+ * initialize 00000000 00101011:
+ * shift 00000000 0101011:_
+ * remainder >= divisor? no 00000000 0101011:0
+ * shift 00000000 101011:0_
+ * remainder >= divisor? no 00000000 101011:00
+ * shift 00000001 01011:00_
+ * remainder >= divisor? no 00000001 01011:000
+ * shift 00000010 1011:000_
+ * remainder >= divisor? no 00000010 1011:0000
+ * shift 00000101 011:0000_
+ * remainder >= divisor? no 00000101 011:00000
+ * shift 00001010 11:00000_
+ * remainder >= divisor? yes 00001010 11:000001
+ * remainder -= divisor - 00000111
+ * ----------
+ * 00000011 11:000001
+ * shift 00000111 1:000001_
+ * remainder >= divisor? yes 00000111 1:0000011
+ * remainder -= divisor - 00000111
+ * ----------
+ * 00000000 1:0000011
+ * shift 00000001 :0000011_
+ * remainder >= divisor? no 00000001 :00000110
+ *
+ * The quotient is 00000110.
+ */
+
+divide:
+ /*
+ * Prepare for division by assuming the result
+ * is unsigned, and storing its "sign" as 0.
+ */
+ movi r17, 0
+
+
+ /* Which division opcode? */
+ xori r7, r4, 0x25 /* OPX of div */
+ bne r7, zero, unsigned_division
+
+
+ /*
+ * OPX is div. Determine and store the sign of the quotient.
+ * Then take the absolute value of both operands.
+ */
+ xor r17, r3, r5 /* MSB contains sign of quotient */
+ bge r3,zero,dividend_is_nonnegative
+ sub r3, zero, r3 /* -r3 */
+dividend_is_nonnegative:
+ bge r5, zero, divisor_is_nonnegative
+ sub r5, zero, r5 /* -r5 */
+divisor_is_nonnegative:
+
+
+unsigned_division:
+ /* Initialize the unsigned-division loop. */
+ movi r13, 0 /* remainder = 0 */
+
+ /* Now
+ * r3 = dividend : quotient
+ * r4 = 0x25 for div, 0x24 for divu
+ * r5 = divisor
+ * r13 = remainder
+ * r14 = loop counter (already initialized to 32)
+ * r17 = MSB contains sign of quotient
+ */
+
+
+ /*
+ * for (count = 32; count > 0; --count)
+ * {
+ */
+divide_loop:
+
+ /*
+ * Division:
+ *
+ * (remainder:dividend:quotient) <<= 1;
+ */
+ slli r13, r13, 1
+ cmplt r7, r3, zero /* r7 = MSB of r3 */
+ or r13, r13, r7
+ slli r3, r3, 1
+
+
+ /*
+ * if (remainder >= divisor)
+ * {
+ * set LSB of quotient
+ * remainder -= divisor;
+ * }
+ */
+ bltu r13, r5, div_skip
+ ori r3, r3, 1
+ sub r13, r13, r5
+div_skip:
+
+ /*
+ * }
+ */
+ subi r14, r14, 1
+ bne r14, zero, divide_loop
+
+
+ /* Now
+ * r3 = quotient
+ * r4 = 0x25 for div, 0x24 for divu
+ * r6 = 4*C
+ * r17 = MSB contains sign of quotient
+ */
+
+
+ /*
+ * Conditionally negate signed quotient. If quotient is unsigned,
+ * the sign already is initialized to 0.
+ */
+ bge r17, zero, quotient_is_nonnegative
+ sub r3, zero, r3 /* -r3 */
+ quotient_is_nonnegative:
+
+
+ /*
+ * Final quotient is in r3.
+ */
+ add r6, r6, sp
+ stw r3, 0(r6) /* write quotient to stack */
+ br restore_registers
+
+
+
+
+ /* MULTIPLICATION
+ *
+ * A "product" is the number that one gets by summing a "multiplicand"
+ * several times. The "multiplier" specifies the number of copies of the
+ * multiplicand that are summed.
+ *
+ * Actual multiplication algorithms don't use repeated addition, however.
+ * Shift-and-add algorithms get the same answer as repeated addition, and
+ * they are faster. To compute the lower half of a product (pppp below)
+ * one shifts the product left before adding in each of the partial
+ * products (a * mmmm) through (d * mmmm).
+ *
+ * To compute the upper half of a product (PPPP below), one adds in the
+ * partial products (d * mmmm) through (a * mmmm), each time following
+ * the add by a right shift of the product.
+ *
+ * mmmm
+ * * abcd
+ * ------
+ * #### = d * mmmm
+ * #### = c * mmmm
+ * #### = b * mmmm
+ * #### = a * mmmm
+ * --------
+ * PPPPpppp
+ *
+ * The example above shows 4 partial products. Computing actual Nios II
+ * products requires 32 partials.
+ *
+ * It is possible to compute the result of mulxsu from the result of
+ * mulxuu because the only difference between the results of these two
+ * opcodes is the value of the partial product associated with the sign
+ * bit of rA.
+ *
+ * mulxsu = mulxuu - (rA < 0) ? rB : 0;
+ *
+ * It is possible to compute the result of mulxss from the result of
+ * mulxsu because the only difference between the results of these two
+ * opcodes is the value of the partial product associated with the sign
+ * bit of rB.
+ *
+ * mulxss = mulxsu - (rB < 0) ? rA : 0;
+ *
+ */
+
+mul_immed:
+ /* Opcode is muli. Change it into mul for remainder of algorithm. */
+ mov r6, r5 /* Field B is dest register, not field C. */
+ mov r5, r4 /* Field IMM16 is src2, not field B. */
+ movi r4, 0x27 /* OPX of mul is 0x27 */
+
+multiply:
+ /* Initialize the multiplication loop. */
+ movi r9, 0 /* mul_product = 0 */
+ movi r10, 0 /* mulxuu_product = 0 */
+ mov r11, r5 /* save original multiplier for mulxsu and mulxss */
+ mov r12, r5 /* mulxuu_multiplier (will be shifted) */
+ movi r16, 1 /* used to create "rori B,A,1" from "ror B,A,r16" */
+
+ /* Now
+ * r3 = multiplicand
+ * r5 = mul_multiplier
+ * r6 = 4 * dest_register (used later as offset to sp)
+ * r7 = temp
+ * r9 = mul_product
+ * r10 = mulxuu_product
+ * r11 = original multiplier
+ * r12 = mulxuu_multiplier
+ * r14 = loop counter (already initialized)
+ * r16 = 1
+ */
+
+
+ /*
+ * for (count = 32; count > 0; --count)
+ * {
+ */
+multiply_loop:
+
+ /*
+ * mul_product <<= 1;
+ * lsb = multiplier & 1;
+ */
+ slli r9, r9, 1
+ andi r7, r12, 1
+
+ /*
+ * if (lsb == 1)
+ * {
+ * mulxuu_product += multiplicand;
+ * }
+ */
+ beq r7, zero, mulx_skip
+ add r10, r10, r3
+ cmpltu r7, r10, r3 /* Save the carry from the MSB of mulxuu_product. */
+ ror r7, r7, r16 /* r7 = 0x80000000 on carry, or else 0x00000000 */
+mulx_skip:
+
+ /*
+ * if (MSB of mul_multiplier == 1)
+ * {
+ * mul_product += multiplicand;
+ * }
+ */
+ bge r5, zero, mul_skip
+ add r9, r9, r3
+mul_skip:
+
+ /*
+ * mulxuu_product >>= 1; logical shift
+ * mul_multiplier <<= 1; done with MSB
+ * mulx_multiplier >>= 1; done with LSB
+ */
+ srli r10, r10, 1
+ or r10, r10, r7 /* OR in the saved carry bit. */
+ slli r5, r5, 1
+ srli r12, r12, 1
+
+
+ /*
+ * }
+ */
+ subi r14, r14, 1
+ bne r14, zero, multiply_loop
+
+
+ /*
+ * Multiply emulation loop done.
+ */
+
+ /* Now
+ * r3 = multiplicand
+ * r4 = OPX
+ * r6 = 4 * dest_register (used later as offset to sp)
+ * r7 = temp
+ * r9 = mul_product
+ * r10 = mulxuu_product
+ * r11 = original multiplier
+ */
+
+
+ /* Calculate address for result from 4 * dest_register */
+ add r6, r6, sp
+
+
+ /*
+ * Select/compute the result based on OPX.
+ */
+
+
+ /* OPX == mul? Then store. */
+ xori r7, r4, 0x27
+ beq r7, zero, store_product
+
+ /* It's one of the mulx.. opcodes. Move over the result. */
+ mov r9, r10
+
+ /* OPX == mulxuu? Then store. */
+ xori r7, r4, 0x07
+ beq r7, zero, store_product
+
+ /* Compute mulxsu
+ *
+ * mulxsu = mulxuu - (rA < 0) ? rB : 0;
+ */
+ bge r3, zero, mulxsu_skip
+ sub r9, r9, r11
+mulxsu_skip:
+
+ /* OPX == mulxsu? Then store. */
+ xori r7, r4, 0x17
+ beq r7, zero, store_product
+
+ /* Compute mulxss
+ *
+ * mulxss = mulxsu - (rB < 0) ? rA : 0;
+ */
+ bge r11,zero,mulxss_skip
+ sub r9, r9, r3
+mulxss_skip:
+ /* At this point, assume that OPX is mulxss, so store*/
+
+
+store_product:
+ stw r9, 0(r6)
+
+
+restore_registers:
+ /* No need to restore r0. */
+ ldw r5, 100(sp)
+ wrctl estatus, r5
+
+ ldw r1, 4(sp)
+ ldw r2, 8(sp)
+ ldw r3, 12(sp)
+ ldw r4, 16(sp)
+ ldw r5, 20(sp)
+ ldw r6, 24(sp)
+ ldw r7, 28(sp)
+ ldw r8, 32(sp)
+ ldw r9, 36(sp)
+ ldw r10, 40(sp)
+ ldw r11, 44(sp)
+ ldw r12, 48(sp)
+ ldw r13, 52(sp)
+ ldw r14, 56(sp)
+ ldw r15, 60(sp)
+ ldw r16, 64(sp)
+ ldw r17, 68(sp)
+ ldw r18, 72(sp)
+ ldw r19, 76(sp)
+ ldw r20, 80(sp)
+ ldw r21, 84(sp)
+ ldw r22, 88(sp)
+ ldw r23, 92(sp)
+ /* Does not need to restore et */
+ ldw gp, 104(sp)
+
+ ldw fp, 112(sp)
+ ldw ea, 116(sp)
+ ldw ra, 120(sp)
+ ldw sp, 108(sp) /* last restore sp */
+ eret
+
+.set at
+.set break
--- /dev/null
+/*
+ * Copyright (C) 2013 Altera Corporation
+ * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2008 Thomas Chou <thomas@wytron.com.tw>
+ *
+ * based on irq.c from m68k which is:
+ *
+ * Copyright (C) 2007 Greg Ungerer <gerg@snapgear.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+
+static u32 ienable;
+
+asmlinkage void do_IRQ(int hwirq, struct pt_regs *regs)
+{
+ struct pt_regs *oldregs = set_irq_regs(regs);
+ int irq;
+
+ irq_enter();
+ irq = irq_find_mapping(NULL, hwirq);
+ generic_handle_irq(irq);
+ irq_exit();
+
+ set_irq_regs(oldregs);
+}
+
+static void chip_unmask(struct irq_data *d)
+{
+ ienable |= (1 << d->hwirq);
+ WRCTL(CTL_IENABLE, ienable);
+}
+
+static void chip_mask(struct irq_data *d)
+{
+ ienable &= ~(1 << d->hwirq);
+ WRCTL(CTL_IENABLE, ienable);
+}
+
+static struct irq_chip m_irq_chip = {
+ .name = "NIOS2-INTC",
+ .irq_unmask = chip_unmask,
+ .irq_mask = chip_mask,
+};
+
+static int irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw_irq_num)
+{
+ irq_set_chip_and_handler(virq, &m_irq_chip, handle_level_irq);
+
+ return 0;
+}
+
+static struct irq_domain_ops irq_ops = {
+ .map = irq_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+void __init init_IRQ(void)
+{
+ struct irq_domain *domain;
+ struct device_node *node;
+
+ node = of_find_compatible_node(NULL, NULL, "altr,nios2-1.0");
+ if (!node)
+ node = of_find_compatible_node(NULL, NULL, "altr,nios2-1.1");
+
+ BUG_ON(!node);
+
+ domain = irq_domain_add_linear(node, NIOS2_CPU_NR_IRQS, &irq_ops, NULL);
+ BUG_ON(!domain);
+
+ irq_set_default_host(domain);
+ of_node_put(node);
+ /* Load the initial ienable value */
+ ienable = RDCTL(CTL_IENABLE);
+}
--- /dev/null
+/*
+ * linux/arch/nios2/kernel/misaligned.c
+ *
+ * basic emulation for mis-aligned accesses on the NIOS II cpu
+ * modelled after the version for arm in arm/alignment.c
+ *
+ * Brad Parker <brad@heeltoe.com>
+ * Copyright (C) 2010 Ambient Corporation
+ * Copyright (c) 2010 Altera Corporation, San Jose, California, USA.
+ * Copyright (c) 2010 Arrow Electronics, Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file COPYING in the main directory of
+ * this archive for more details.
+ */
+
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/seq_file.h>
+
+#include <asm/traps.h>
+#include <asm/unaligned.h>
+
+/* instructions we emulate */
+#define INST_LDHU 0x0b
+#define INST_STH 0x0d
+#define INST_LDH 0x0f
+#define INST_STW 0x15
+#define INST_LDW 0x17
+
+static unsigned long ma_user, ma_kern, ma_skipped, ma_half, ma_word;
+
+static unsigned int ma_usermode;
+#define UM_WARN 0x01
+#define UM_FIXUP 0x02
+#define UM_SIGNAL 0x04
+#define KM_WARN 0x08
+
+/* see arch/nios2/include/asm/ptrace.h */
+static u8 sys_stack_frame_reg_offset[] = {
+ /* struct pt_regs */
+ 8, 9, 10, 11, 12, 13, 14, 15, 1, 2, 3, 4, 5, 6, 7, 0,
+ /* struct switch_stack */
+ 16, 17, 18, 19, 20, 21, 22, 23, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static int reg_offsets[32];
+
+static inline u32 get_reg_val(struct pt_regs *fp, int reg)
+{
+ u8 *p = ((u8 *)fp) + reg_offsets[reg];
+
+ return *(u32 *)p;
+}
+
+static inline void put_reg_val(struct pt_regs *fp, int reg, u32 val)
+{
+ u8 *p = ((u8 *)fp) + reg_offsets[reg];
+ *(u32 *)p = val;
+}
+
+/*
+ * (mis)alignment handler
+ */
+asmlinkage void handle_unaligned_c(struct pt_regs *fp, int cause)
+{
+ u32 isn, addr, val;
+ int in_kernel;
+ u8 a, b, d0, d1, d2, d3;
+ u16 imm16;
+ unsigned int fault;
+
+ /* back up one instruction */
+ fp->ea -= 4;
+
+ if (fixup_exception(fp)) {
+ ma_skipped++;
+ return;
+ }
+
+ in_kernel = !user_mode(fp);
+
+ isn = *(unsigned long *)(fp->ea);
+
+ fault = 0;
+
+ /* do fixup if in kernel or mode turned on */
+ if (in_kernel || (ma_usermode & UM_FIXUP)) {
+ /* decompose instruction */
+ a = (isn >> 27) & 0x1f;
+ b = (isn >> 22) & 0x1f;
+ imm16 = (isn >> 6) & 0xffff;
+ addr = get_reg_val(fp, a) + imm16;
+
+ /* do fixup to saved registers */
+ switch (isn & 0x3f) {
+ case INST_LDHU:
+ fault |= __get_user(d0, (u8 *)(addr+0));
+ fault |= __get_user(d1, (u8 *)(addr+1));
+ val = (d1 << 8) | d0;
+ put_reg_val(fp, b, val);
+ ma_half++;
+ break;
+ case INST_STH:
+ val = get_reg_val(fp, b);
+ d1 = val >> 8;
+ d0 = val >> 0;
+
+ pr_debug("sth: ra=%d (%08x) rb=%d (%08x), imm16 %04x addr %08x val %08x\n",
+ a, get_reg_val(fp, a),
+ b, get_reg_val(fp, b),
+ imm16, addr, val);
+
+ if (in_kernel) {
+ *(u8 *)(addr+0) = d0;
+ *(u8 *)(addr+1) = d1;
+ } else {
+ fault |= __put_user(d0, (u8 *)(addr+0));
+ fault |= __put_user(d1, (u8 *)(addr+1));
+ }
+ ma_half++;
+ break;
+ case INST_LDH:
+ fault |= __get_user(d0, (u8 *)(addr+0));
+ fault |= __get_user(d1, (u8 *)(addr+1));
+ val = (short)((d1 << 8) | d0);
+ put_reg_val(fp, b, val);
+ ma_half++;
+ break;
+ case INST_STW:
+ val = get_reg_val(fp, b);
+ d3 = val >> 24;
+ d2 = val >> 16;
+ d1 = val >> 8;
+ d0 = val >> 0;
+ if (in_kernel) {
+ *(u8 *)(addr+0) = d0;
+ *(u8 *)(addr+1) = d1;
+ *(u8 *)(addr+2) = d2;
+ *(u8 *)(addr+3) = d3;
+ } else {
+ fault |= __put_user(d0, (u8 *)(addr+0));
+ fault |= __put_user(d1, (u8 *)(addr+1));
+ fault |= __put_user(d2, (u8 *)(addr+2));
+ fault |= __put_user(d3, (u8 *)(addr+3));
+ }
+ ma_word++;
+ break;
+ case INST_LDW:
+ fault |= __get_user(d0, (u8 *)(addr+0));
+ fault |= __get_user(d1, (u8 *)(addr+1));
+ fault |= __get_user(d2, (u8 *)(addr+2));
+ fault |= __get_user(d3, (u8 *)(addr+3));
+ val = (d3 << 24) | (d2 << 16) | (d1 << 8) | d0;
+ put_reg_val(fp, b, val);
+ ma_word++;
+ break;
+ }
+ }
+
+ addr = RDCTL(CTL_BADADDR);
+ cause >>= 2;
+
+ if (fault) {
+ if (in_kernel) {
+ pr_err("fault during kernel misaligned fixup @ %#lx; addr 0x%08x; isn=0x%08x\n",
+ fp->ea, (unsigned int)addr,
+ (unsigned int)isn);
+ } else {
+ pr_err("fault during user misaligned fixup @ %#lx; isn=%08x addr=0x%08x sp=0x%08lx pid=%d\n",
+ fp->ea,
+ (unsigned int)isn, addr, fp->sp,
+ current->pid);
+
+ _exception(SIGSEGV, fp, SEGV_MAPERR, fp->ea);
+ return;
+ }
+ }
+
+ /*
+ * kernel mode -
+ * note exception and skip bad instruction (return)
+ */
+ if (in_kernel) {
+ ma_kern++;
+ fp->ea += 4;
+
+ if (ma_usermode & KM_WARN) {
+ pr_err("kernel unaligned access @ %#lx; BADADDR 0x%08x; cause=%d, isn=0x%08x\n",
+ fp->ea,
+ (unsigned int)addr, cause,
+ (unsigned int)isn);
+ /* show_regs(fp); */
+ }
+
+ return;
+ }
+
+ ma_user++;
+
+ /*
+ * user mode -
+ * possibly warn,
+ * possibly send SIGBUS signal to process
+ */
+ if (ma_usermode & UM_WARN) {
+ pr_err("user unaligned access @ %#lx; isn=0x%08lx ea=0x%08lx ra=0x%08lx sp=0x%08lx\n",
+ (unsigned long)addr, (unsigned long)isn,
+ fp->ea, fp->ra, fp->sp);
+ }
+
+ if (ma_usermode & UM_SIGNAL)
+ _exception(SIGBUS, fp, BUS_ADRALN, fp->ea);
+ else
+ fp->ea += 4; /* else advance */
+}
+
+static void __init misaligned_calc_reg_offsets(void)
+{
+ int i, r, offset;
+
+ /* pre-calc offsets of registers on sys call stack frame */
+ offset = 0;
+
+ /* struct pt_regs */
+ for (i = 0; i < 16; i++) {
+ r = sys_stack_frame_reg_offset[i];
+ reg_offsets[r] = offset;
+ offset += 4;
+ }
+
+ /* struct switch_stack */
+ offset = -sizeof(struct switch_stack);
+ for (i = 16; i < 32; i++) {
+ r = sys_stack_frame_reg_offset[i];
+ reg_offsets[r] = offset;
+ offset += 4;
+ }
+}
+
+
+static int __init misaligned_init(void)
+{
+ /* default mode - silent fix */
+ ma_usermode = UM_FIXUP | KM_WARN;
+
+ misaligned_calc_reg_offsets();
+
+ return 0;
+}
+
+fs_initcall(misaligned_init);
--- /dev/null
+/*
+ * Kernel module support for Nios II.
+ *
+ * Copyright (C) 2004 Microtronix Datacom Ltd.
+ * Written by Wentao Xu <xuwentao@microtronix.com>
+ * Copyright (C) 2001, 2003 Rusty Russell
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file COPYING in the main directory of this
+ * archive for more details.
+ */
+
+#include <linux/moduleloader.h>
+#include <linux/elf.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+
+#include <asm/pgtable.h>
+#include <asm/cacheflush.h>
+
+/*
+ * Modules should NOT be allocated with kmalloc for (obvious) reasons.
+ * But we do it for now to avoid relocation issues. CALL26/PCREL26 cannot reach
+ * from 0x80000000 (vmalloc area) to 0xc00000000 (kernel) (kmalloc returns
+ * addresses in 0xc0000000)
+ */
+void *module_alloc(unsigned long size)
+{
+ if (size == 0)
+ return NULL;
+ return kmalloc(size, GFP_KERNEL);
+}
+
+/* Free memory returned from module_alloc */
+void module_free(struct module *mod, void *module_region)
+{
+ kfree(module_region);
+}
+
+int apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab,
+ unsigned int symindex, unsigned int relsec,
+ struct module *mod)
+{
+ unsigned int i;
+ Elf32_Rela *rela = (void *)sechdrs[relsec].sh_addr;
+
+ pr_debug("Applying relocate section %u to %u\n", relsec,
+ sechdrs[relsec].sh_info);
+
+ for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) {
+ /* This is where to make the change */
+ uint32_t word;
+ uint32_t *loc
+ = ((void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
+ + rela[i].r_offset);
+ /* This is the symbol it is referring to. Note that all
+ undefined symbols have been resolved. */
+ Elf32_Sym *sym
+ = ((Elf32_Sym *)sechdrs[symindex].sh_addr
+ + ELF32_R_SYM(rela[i].r_info));
+ uint32_t v = sym->st_value + rela[i].r_addend;
+
+ pr_debug("reltype %d 0x%x name:<%s>\n",
+ ELF32_R_TYPE(rela[i].r_info),
+ rela[i].r_offset, strtab + sym->st_name);
+
+ switch (ELF32_R_TYPE(rela[i].r_info)) {
+ case R_NIOS2_NONE:
+ break;
+ case R_NIOS2_BFD_RELOC_32:
+ *loc += v;
+ break;
+ case R_NIOS2_PCREL16:
+ v -= (uint32_t)loc + 4;
+ if ((int32_t)v > 0x7fff ||
+ (int32_t)v < -(int32_t)0x8000) {
+ pr_err("module %s: relocation overflow\n",
+ mod->name);
+ return -ENOEXEC;
+ }
+ word = *loc;
+ *loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) |
+ (word & 0x3f);
+ break;
+ case R_NIOS2_CALL26:
+ if (v & 3) {
+ pr_err("module %s: dangerous relocation\n",
+ mod->name);
+ return -ENOEXEC;
+ }
+ if ((v >> 28) != ((uint32_t)loc >> 28)) {
+ pr_err("module %s: relocation overflow\n",
+ mod->name);
+ return -ENOEXEC;
+ }
+ *loc = (*loc & 0x3f) | ((v >> 2) << 6);
+ break;
+ case R_NIOS2_HI16:
+ word = *loc;
+ *loc = ((((word >> 22) << 16) |
+ ((v >> 16) & 0xffff)) << 6) | (word & 0x3f);
+ break;
+ case R_NIOS2_LO16:
+ word = *loc;
+ *loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) |
+ (word & 0x3f);
+ break;
+ case R_NIOS2_HIADJ16:
+ {
+ Elf32_Addr word2;
+
+ word = *loc;
+ word2 = ((v >> 16) + ((v >> 15) & 1)) & 0xffff;
+ *loc = ((((word >> 22) << 16) | word2) << 6) |
+ (word & 0x3f);
+ }
+ break;
+
+ default:
+ pr_err("module %s: Unknown reloc: %u\n",
+ mod->name, ELF32_R_TYPE(rela[i].r_info));
+ return -ENOEXEC;
+ }
+ }
+
+ return 0;
+}
+
+int module_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
+ struct module *me)
+{
+ flush_cache_all();
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2004 Microtronix Datacom Ltd
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file COPYING in the main directory of this
+ * archive for more details.
+ */
+
+#include <linux/export.h>
+#include <linux/string.h>
+
+/* string functions */
+
+EXPORT_SYMBOL(memcpy);
+EXPORT_SYMBOL(memset);
+EXPORT_SYMBOL(memmove);
+
+/*
+ * libgcc functions - functions that are used internally by the
+ * compiler... (prototypes are not correct though, but that
+ * doesn't really matter since they're not versioned).
+ */
+#define DECLARE_EXPORT(name) extern void name(void); EXPORT_SYMBOL(name)
+
+DECLARE_EXPORT(__gcc_bcmp);
+DECLARE_EXPORT(__divsi3);
+DECLARE_EXPORT(__moddi3);
+DECLARE_EXPORT(__modsi3);
+DECLARE_EXPORT(__udivmoddi4);
+DECLARE_EXPORT(__udivsi3);
+DECLARE_EXPORT(__umoddi3);
+DECLARE_EXPORT(__umodsi3);
+DECLARE_EXPORT(__muldi3);
--- /dev/null
+/*
+ * Architecture-dependent parts of process handling.
+ *
+ * Copyright (C) 2013 Altera Corporation
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2009 Wind River Systems Inc
+ * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com
+ * Copyright (C) 2004 Microtronix Datacom Ltd
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/export.h>
+#include <linux/sched.h>
+#include <linux/tick.h>
+#include <linux/uaccess.h>
+
+#include <asm/unistd.h>
+#include <asm/traps.h>
+#include <asm/cpuinfo.h>
+
+asmlinkage void ret_from_fork(void);
+asmlinkage void ret_from_kernel_thread(void);
+
+void (*pm_power_off)(void) = NULL;
+EXPORT_SYMBOL(pm_power_off);
+
+void arch_cpu_idle(void)
+{
+ local_irq_enable();
+}
+
+/*
+ * The development boards have no way to pull a board reset. Just jump to the
+ * cpu reset address and let the boot loader or the code in head.S take care of
+ * resetting peripherals.
+ */
+void machine_restart(char *__unused)
+{
+ pr_notice("Machine restart (%08x)...\n", cpuinfo.reset_addr);
+ local_irq_disable();
+ __asm__ __volatile__ (
+ "jmp %0\n\t"
+ :
+ : "r" (cpuinfo.reset_addr)
+ : "r4");
+}
+
+void machine_halt(void)
+{
+ pr_notice("Machine halt...\n");
+ local_irq_disable();
+ for (;;)
+ ;
+}
+
+/*
+ * There is no way to power off the development boards. So just spin for now. If
+ * we ever have a way of resetting a board using a GPIO we should add that here.
+ */
+void machine_power_off(void)
+{
+ pr_notice("Machine power off...\n");
+ local_irq_disable();
+ for (;;)
+ ;
+}
+
+void show_regs(struct pt_regs *regs)
+{
+ pr_notice("\n");
+ show_regs_print_info(KERN_DEFAULT);
+
+ pr_notice("r1: %08lx r2: %08lx r3: %08lx r4: %08lx\n",
+ regs->r1, regs->r2, regs->r3, regs->r4);
+
+ pr_notice("r5: %08lx r6: %08lx r7: %08lx r8: %08lx\n",
+ regs->r5, regs->r6, regs->r7, regs->r8);
+
+ pr_notice("r9: %08lx r10: %08lx r11: %08lx r12: %08lx\n",
+ regs->r9, regs->r10, regs->r11, regs->r12);
+
+ pr_notice("r13: %08lx r14: %08lx r15: %08lx\n",
+ regs->r13, regs->r14, regs->r15);
+
+ pr_notice("ra: %08lx fp: %08lx sp: %08lx gp: %08lx\n",
+ regs->ra, regs->fp, regs->sp, regs->gp);
+
+ pr_notice("ea: %08lx estatus: %08lx\n",
+ regs->ea, regs->estatus);
+}
+
+void flush_thread(void)
+{
+ set_fs(USER_DS);
+}
+
+int copy_thread(unsigned long clone_flags,
+ unsigned long usp, unsigned long arg, struct task_struct *p)
+{
+ struct pt_regs *childregs = task_pt_regs(p);
+ struct pt_regs *regs;
+ struct switch_stack *stack;
+ struct switch_stack *childstack =
+ ((struct switch_stack *)childregs) - 1;
+
+ if (unlikely(p->flags & PF_KTHREAD)) {
+ memset(childstack, 0,
+ sizeof(struct switch_stack) + sizeof(struct pt_regs));
+
+ childstack->r16 = usp; /* fn */
+ childstack->r17 = arg;
+ childstack->ra = (unsigned long) ret_from_kernel_thread;
+ childregs->estatus = STATUS_PIE;
+ childregs->sp = (unsigned long) childstack;
+
+ p->thread.ksp = (unsigned long) childstack;
+ p->thread.kregs = childregs;
+ return 0;
+ }
+
+ regs = current_pt_regs();
+ *childregs = *regs;
+ childregs->r2 = 0; /* Set the return value for the child. */
+ childregs->r7 = 0;
+
+ stack = ((struct switch_stack *) regs) - 1;
+ *childstack = *stack;
+ childstack->ra = (unsigned long)ret_from_fork;
+ p->thread.kregs = childregs;
+ p->thread.ksp = (unsigned long) childstack;
+
+ if (usp)
+ childregs->sp = usp;
+
+ /* Initialize tls register. */
+ if (clone_flags & CLONE_SETTLS)
+ childstack->r23 = regs->r8;
+
+ return 0;
+}
+
+/*
+ * Generic dumping code. Used for panic and debug.
+ */
+void dump(struct pt_regs *fp)
+{
+ unsigned long *sp;
+ unsigned char *tp;
+ int i;
+
+ pr_emerg("\nCURRENT PROCESS:\n\n");
+ pr_emerg("COMM=%s PID=%d\n", current->comm, current->pid);
+
+ if (current->mm) {
+ pr_emerg("TEXT=%08x-%08x DATA=%08x-%08x BSS=%08x-%08x\n",
+ (int) current->mm->start_code,
+ (int) current->mm->end_code,
+ (int) current->mm->start_data,
+ (int) current->mm->end_data,
+ (int) current->mm->end_data,
+ (int) current->mm->brk);
+ pr_emerg("USER-STACK=%08x KERNEL-STACK=%08x\n\n",
+ (int) current->mm->start_stack,
+ (int)(((unsigned long) current) + THREAD_SIZE));
+ }
+
+ pr_emerg("PC: %08lx\n", fp->ea);
+ pr_emerg("SR: %08lx SP: %08lx\n",
+ (long) fp->estatus, (long) fp);
+
+ pr_emerg("r1: %08lx r2: %08lx r3: %08lx\n",
+ fp->r1, fp->r2, fp->r3);
+
+ pr_emerg("r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n",
+ fp->r4, fp->r5, fp->r6, fp->r7);
+ pr_emerg("r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n",
+ fp->r8, fp->r9, fp->r10, fp->r11);
+ pr_emerg("r12: %08lx r13: %08lx r14: %08lx r15: %08lx\n",
+ fp->r12, fp->r13, fp->r14, fp->r15);
+ pr_emerg("or2: %08lx ra: %08lx fp: %08lx sp: %08lx\n",
+ fp->orig_r2, fp->ra, fp->fp, fp->sp);
+ pr_emerg("\nUSP: %08x TRAPFRAME: %08x\n",
+ (unsigned int) fp->sp, (unsigned int) fp);
+
+ pr_emerg("\nCODE:");
+ tp = ((unsigned char *) fp->ea) - 0x20;
+ for (sp = (unsigned long *) tp, i = 0; (i < 0x40); i += 4) {
+ if ((i % 0x10) == 0)
+ pr_emerg("\n%08x: ", (int) (tp + i));
+ pr_emerg("%08x ", (int) *sp++);
+ }
+ pr_emerg("\n");
+
+ pr_emerg("\nKERNEL STACK:");
+ tp = ((unsigned char *) fp) - 0x40;
+ for (sp = (unsigned long *) tp, i = 0; (i < 0xc0); i += 4) {
+ if ((i % 0x10) == 0)
+ pr_emerg("\n%08x: ", (int) (tp + i));
+ pr_emerg("%08x ", (int) *sp++);
+ }
+ pr_emerg("\n");
+ pr_emerg("\n");
+
+ pr_emerg("\nUSER STACK:");
+ tp = (unsigned char *) (fp->sp - 0x10);
+ for (sp = (unsigned long *) tp, i = 0; (i < 0x80); i += 4) {
+ if ((i % 0x10) == 0)
+ pr_emerg("\n%08x: ", (int) (tp + i));
+ pr_emerg("%08x ", (int) *sp++);
+ }
+ pr_emerg("\n\n");
+}
+
+unsigned long get_wchan(struct task_struct *p)
+{
+ unsigned long fp, pc;
+ unsigned long stack_page;
+ int count = 0;
+
+ if (!p || p == current || p->state == TASK_RUNNING)
+ return 0;
+
+ stack_page = (unsigned long)p;
+ fp = ((struct switch_stack *)p->thread.ksp)->fp; /* ;dgt2 */
+ do {
+ if (fp < stack_page+sizeof(struct task_struct) ||
+ fp >= 8184+stack_page) /* ;dgt2;tmp */
+ return 0;
+ pc = ((unsigned long *)fp)[1];
+ if (!in_sched_functions(pc))
+ return pc;
+ fp = *(unsigned long *) fp;
+ } while (count++ < 16); /* ;dgt2;tmp */
+ return 0;
+}
+
+/*
+ * Do necessary setup to start up a newly executed thread.
+ * Will startup in user mode (status_extension = 0).
+ */
+void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp)
+{
+ memset((void *) regs, 0, sizeof(struct pt_regs));
+ regs->estatus = ESTATUS_EPIE | ESTATUS_EU;
+ regs->ea = pc;
+ regs->sp = sp;
+}
+
+#include <linux/elfcore.h>
+
+/* Fill in the FPU structure for a core dump. */
+int dump_fpu(struct pt_regs *regs, elf_fpregset_t *r)
+{
+ return 0; /* Nios2 has no FPU and thus no FPU registers */
+}
--- /dev/null
+/*
+ * Device tree support
+ *
+ * Copyright (C) 2013 Altera Corporation
+ * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
+ *
+ * Based on MIPS support for CONFIG_OF device tree support
+ *
+ * Copyright (C) 2010 Cisco Systems Inc. <dediao@cisco.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/bootmem.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/io.h>
+
+#include <asm/sections.h>
+
+void __init early_init_dt_add_memory_arch(u64 base, u64 size)
+{
+ u64 kernel_start = (u64)virt_to_phys(_text);
+
+ if (!memory_size &&
+ (kernel_start >= base) && (kernel_start < (base + size)))
+ memory_size = size;
+
+}
+
+void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align)
+{
+ return alloc_bootmem_align(size, align);
+}
+
+void __init early_init_devtree(void *params)
+{
+ __be32 *dtb = (u32 *)__dtb_start;
+#if defined(CONFIG_NIOS2_DTB_AT_PHYS_ADDR)
+ if (be32_to_cpup((__be32 *)CONFIG_NIOS2_DTB_PHYS_ADDR) ==
+ OF_DT_HEADER) {
+ params = (void *)CONFIG_NIOS2_DTB_PHYS_ADDR;
+ early_init_dt_scan(params);
+ return;
+ }
+#endif
+ if (be32_to_cpu((__be32) *dtb) == OF_DT_HEADER)
+ params = (void *)__dtb_start;
+
+ early_init_dt_scan(params);
+}
--- /dev/null
+/*
+ * Copyright (C) 2014 Altera Corporation
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file COPYING in the main directory of this
+ * archive for more details.
+ */
+
+#include <linux/elf.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/ptrace.h>
+#include <linux/regset.h>
+#include <linux/sched.h>
+#include <linux/tracehook.h>
+#include <linux/uaccess.h>
+#include <linux/user.h>
+
+static int genregs_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ const struct pt_regs *regs = task_pt_regs(target);
+ const struct switch_stack *sw = (struct switch_stack *)regs - 1;
+ int ret = 0;
+
+#define REG_O_ZERO_RANGE(START, END) \
+ if (!ret) \
+ ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, \
+ START * 4, (END * 4) + 4);
+
+#define REG_O_ONE(PTR, LOC) \
+ if (!ret) \
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, PTR, \
+ LOC * 4, (LOC * 4) + 4);
+
+#define REG_O_RANGE(PTR, START, END) \
+ if (!ret) \
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, PTR, \
+ START * 4, (END * 4) + 4);
+
+ REG_O_ZERO_RANGE(PTR_R0, PTR_R0);
+ REG_O_RANGE(®s->r1, PTR_R1, PTR_R7);
+ REG_O_RANGE(®s->r8, PTR_R8, PTR_R15);
+ REG_O_RANGE(sw, PTR_R16, PTR_R23);
+ REG_O_ZERO_RANGE(PTR_R24, PTR_R25); /* et and bt */
+ REG_O_ONE(®s->gp, PTR_GP);
+ REG_O_ONE(®s->sp, PTR_SP);
+ REG_O_ONE(®s->fp, PTR_FP);
+ REG_O_ONE(®s->ea, PTR_EA);
+ REG_O_ZERO_RANGE(PTR_BA, PTR_BA);
+ REG_O_ONE(®s->ra, PTR_RA);
+ REG_O_ONE(®s->ea, PTR_PC); /* use ea for PC */
+ if (!ret)
+ ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
+ PTR_STATUS * 4, -1);
+
+ return ret;
+}
+
+/*
+ * Set the thread state from a regset passed in via ptrace
+ */
+static int genregs_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ struct pt_regs *regs = task_pt_regs(target);
+ const struct switch_stack *sw = (struct switch_stack *)regs - 1;
+ int ret = 0;
+
+#define REG_IGNORE_RANGE(START, END) \
+ if (!ret) \
+ ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, \
+ START * 4, (END * 4) + 4);
+
+#define REG_IN_ONE(PTR, LOC) \
+ if (!ret) \
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, \
+ (void *)(PTR), LOC * 4, (LOC * 4) + 4);
+
+#define REG_IN_RANGE(PTR, START, END) \
+ if (!ret) \
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, \
+ (void *)(PTR), START * 4, (END * 4) + 4);
+
+ REG_IGNORE_RANGE(PTR_R0, PTR_R0);
+ REG_IN_RANGE(®s->r1, PTR_R1, PTR_R7);
+ REG_IN_RANGE(®s->r8, PTR_R8, PTR_R15);
+ REG_IN_RANGE(sw, PTR_R16, PTR_R23);
+ REG_IGNORE_RANGE(PTR_R24, PTR_R25); /* et and bt */
+ REG_IN_ONE(®s->gp, PTR_GP);
+ REG_IN_ONE(®s->sp, PTR_SP);
+ REG_IN_ONE(®s->fp, PTR_FP);
+ REG_IN_ONE(®s->ea, PTR_EA);
+ REG_IGNORE_RANGE(PTR_BA, PTR_BA);
+ REG_IN_ONE(®s->ra, PTR_RA);
+ REG_IN_ONE(®s->ea, PTR_PC); /* use ea for PC */
+ if (!ret)
+ ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
+ PTR_STATUS * 4, -1);
+
+ return ret;
+}
+
+/*
+ * Define the register sets available on Nios2 under Linux
+ */
+enum nios2_regset {
+ REGSET_GENERAL,
+};
+
+static const struct user_regset nios2_regsets[] = {
+ [REGSET_GENERAL] = {
+ .core_note_type = NT_PRSTATUS,
+ .n = NUM_PTRACE_REG,
+ .size = sizeof(unsigned long),
+ .align = sizeof(unsigned long),
+ .get = genregs_get,
+ .set = genregs_set,
+ }
+};
+
+static const struct user_regset_view nios2_user_view = {
+ .name = "nios2",
+ .e_machine = ELF_ARCH,
+ .ei_osabi = ELF_OSABI,
+ .regsets = nios2_regsets,
+ .n = ARRAY_SIZE(nios2_regsets)
+};
+
+const struct user_regset_view *task_user_regset_view(struct task_struct *task)
+{
+ return &nios2_user_view;
+}
+
+void ptrace_disable(struct task_struct *child)
+{
+
+}
+
+long arch_ptrace(struct task_struct *child, long request, unsigned long addr,
+ unsigned long data)
+{
+ return ptrace_request(child, request, addr, data);
+}
+
+asmlinkage int do_syscall_trace_enter(void)
+{
+ int ret = 0;
+
+ if (test_thread_flag(TIF_SYSCALL_TRACE))
+ ret = tracehook_report_syscall_entry(task_pt_regs(current));
+
+ return ret;
+}
+
+asmlinkage void do_syscall_trace_exit(void)
+{
+ if (test_thread_flag(TIF_SYSCALL_TRACE))
+ tracehook_report_syscall_exit(task_pt_regs(current), 0);
+}
--- /dev/null
+/*
+ * Nios2-specific parts of system setup
+ *
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2004 Microtronix Datacom Ltd.
+ * Copyright (C) 2001 Vic Phillips <vic@microtronix.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/console.h>
+#include <linux/bootmem.h>
+#include <linux/initrd.h>
+#include <linux/of_fdt.h>
+
+#include <asm/mmu_context.h>
+#include <asm/sections.h>
+#include <asm/setup.h>
+#include <asm/cpuinfo.h>
+
+unsigned long memory_start;
+EXPORT_SYMBOL(memory_start);
+
+unsigned long memory_end;
+EXPORT_SYMBOL(memory_end);
+
+unsigned long memory_size;
+
+static struct pt_regs fake_regs = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,
+ 0};
+
+/* Copy a short hook instruction sequence to the exception address */
+static inline void copy_exception_handler(unsigned int addr)
+{
+ unsigned int start = (unsigned int) exception_handler_hook;
+ volatile unsigned int tmp = 0;
+
+ if (start == addr) {
+ /* The CPU exception address already points to the handler. */
+ return;
+ }
+
+ __asm__ __volatile__ (
+ "ldw %2,0(%0)\n"
+ "stw %2,0(%1)\n"
+ "ldw %2,4(%0)\n"
+ "stw %2,4(%1)\n"
+ "ldw %2,8(%0)\n"
+ "stw %2,8(%1)\n"
+ "flushd 0(%1)\n"
+ "flushd 4(%1)\n"
+ "flushd 8(%1)\n"
+ "flushi %1\n"
+ "addi %1,%1,4\n"
+ "flushi %1\n"
+ "addi %1,%1,4\n"
+ "flushi %1\n"
+ "flushp\n"
+ : /* no output registers */
+ : "r" (start), "r" (addr), "r" (tmp)
+ : "memory"
+ );
+}
+
+/* Copy the fast TLB miss handler */
+static inline void copy_fast_tlb_miss_handler(unsigned int addr)
+{
+ unsigned int start = (unsigned int) fast_handler;
+ unsigned int end = (unsigned int) fast_handler_end;
+ volatile unsigned int tmp = 0;
+
+ __asm__ __volatile__ (
+ "1:\n"
+ " ldw %3,0(%0)\n"
+ " stw %3,0(%1)\n"
+ " flushd 0(%1)\n"
+ " flushi %1\n"
+ " flushp\n"
+ " addi %0,%0,4\n"
+ " addi %1,%1,4\n"
+ " bne %0,%2,1b\n"
+ : /* no output registers */
+ : "r" (start), "r" (addr), "r" (end), "r" (tmp)
+ : "memory"
+ );
+}
+
+/*
+ * save args passed from u-boot, called from head.S
+ *
+ * @r4: NIOS magic
+ * @r5: initrd start
+ * @r6: initrd end or fdt
+ * @r7: kernel command line
+ */
+asmlinkage void __init nios2_boot_init(unsigned r4, unsigned r5, unsigned r6,
+ unsigned r7)
+{
+ unsigned dtb_passed = 0;
+ char cmdline_passed[COMMAND_LINE_SIZE] = { 0, };
+
+#if defined(CONFIG_NIOS2_PASS_CMDLINE)
+ if (r4 == 0x534f494e) { /* r4 is magic NIOS */
+#if defined(CONFIG_BLK_DEV_INITRD)
+ if (r5) { /* initramfs */
+ initrd_start = r5;
+ initrd_end = r6;
+ }
+#endif /* CONFIG_BLK_DEV_INITRD */
+ dtb_passed = r6;
+
+ if (r7)
+ strncpy(cmdline_passed, (char *)r7, COMMAND_LINE_SIZE);
+ }
+#endif
+
+ early_init_devtree((void *)dtb_passed);
+
+#ifndef CONFIG_CMDLINE_FORCE
+ if (cmdline_passed[0])
+ strncpy(boot_command_line, cmdline_passed, COMMAND_LINE_SIZE);
+#ifdef CONFIG_NIOS2_CMDLINE_IGNORE_DTB
+ else
+ strncpy(boot_command_line, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
+#endif
+#endif
+}
+
+void __init setup_arch(char **cmdline_p)
+{
+ int bootmap_size;
+
+ console_verbose();
+
+ memory_start = PAGE_ALIGN((unsigned long)__pa(_end));
+ memory_end = (unsigned long) CONFIG_NIOS2_MEM_BASE + memory_size;
+
+ init_mm.start_code = (unsigned long) _stext;
+ init_mm.end_code = (unsigned long) _etext;
+ init_mm.end_data = (unsigned long) _edata;
+ init_mm.brk = (unsigned long) _end;
+ init_task.thread.kregs = &fake_regs;
+
+ /* Keep a copy of command line */
+ *cmdline_p = boot_command_line;
+
+ min_low_pfn = PFN_UP(memory_start);
+ max_low_pfn = PFN_DOWN(memory_end);
+ max_mapnr = max_low_pfn;
+
+ /*
+ * give all the memory to the bootmap allocator, tell it to put the
+ * boot mem_map at the start of memory
+ */
+ pr_debug("init_bootmem_node(?,%#lx, %#x, %#lx)\n",
+ min_low_pfn, PFN_DOWN(PHYS_OFFSET), max_low_pfn);
+ bootmap_size = init_bootmem_node(NODE_DATA(0),
+ min_low_pfn, PFN_DOWN(PHYS_OFFSET),
+ max_low_pfn);
+
+ /*
+ * free the usable memory, we have to make sure we do not free
+ * the bootmem bitmap so we then reserve it after freeing it :-)
+ */
+ pr_debug("free_bootmem(%#lx, %#lx)\n",
+ memory_start, memory_end - memory_start);
+ free_bootmem(memory_start, memory_end - memory_start);
+
+ /*
+ * Reserve the bootmem bitmap itself as well. We do this in two
+ * steps (first step was init_bootmem()) because this catches
+ * the (very unlikely) case of us accidentally initializing the
+ * bootmem allocator with an invalid RAM area.
+ *
+ * Arguments are start, size
+ */
+ pr_debug("reserve_bootmem(%#lx, %#x)\n", memory_start, bootmap_size);
+ reserve_bootmem(memory_start, bootmap_size, BOOTMEM_DEFAULT);
+
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (initrd_start) {
+ reserve_bootmem(virt_to_phys((void *)initrd_start),
+ initrd_end - initrd_start, BOOTMEM_DEFAULT);
+ }
+#endif /* CONFIG_BLK_DEV_INITRD */
+
+ unflatten_and_copy_device_tree();
+
+ setup_cpuinfo();
+
+ copy_exception_handler(cpuinfo.exception_addr);
+
+ mmu_init();
+
+ copy_fast_tlb_miss_handler(cpuinfo.fast_tlb_miss_exc_addr);
+
+ /*
+ * Initialize MMU context handling here because data from cpuinfo is
+ * needed for this.
+ */
+ mmu_context_init();
+
+ /*
+ * get kmalloc into gear
+ */
+ paging_init();
+
+#if defined(CONFIG_VT) && defined(CONFIG_DUMMY_CONSOLE)
+ conswitchp = &dummy_con;
+#endif
+}
--- /dev/null
+/*
+ * Copyright (C) 2013-2014 Altera Corporation
+ * Copyright (C) 2011-2012 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2004 Microtronix Datacom Ltd
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/uaccess.h>
+#include <linux/unistd.h>
+#include <linux/personality.h>
+#include <linux/tracehook.h>
+
+#include <asm/ucontext.h>
+#include <asm/cacheflush.h>
+
+#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
+
+/*
+ * Do a signal return; undo the signal stack.
+ *
+ * Keep the return code on the stack quadword aligned!
+ * That makes the cache flush below easier.
+ */
+
+struct rt_sigframe {
+ struct siginfo info;
+ struct ucontext uc;
+};
+
+static inline int rt_restore_ucontext(struct pt_regs *regs,
+ struct switch_stack *sw,
+ struct ucontext *uc, int *pr2)
+{
+ int temp;
+ greg_t *gregs = uc->uc_mcontext.gregs;
+ int err;
+
+ /* Always make any pending restarted system calls return -EINTR */
+ current_thread_info()->restart_block.fn = do_no_restart_syscall;
+
+ err = __get_user(temp, &uc->uc_mcontext.version);
+ if (temp != MCONTEXT_VERSION)
+ goto badframe;
+ /* restore passed registers */
+ err |= __get_user(regs->r1, &gregs[0]);
+ err |= __get_user(regs->r2, &gregs[1]);
+ err |= __get_user(regs->r3, &gregs[2]);
+ err |= __get_user(regs->r4, &gregs[3]);
+ err |= __get_user(regs->r5, &gregs[4]);
+ err |= __get_user(regs->r6, &gregs[5]);
+ err |= __get_user(regs->r7, &gregs[6]);
+ err |= __get_user(regs->r8, &gregs[7]);
+ err |= __get_user(regs->r9, &gregs[8]);
+ err |= __get_user(regs->r10, &gregs[9]);
+ err |= __get_user(regs->r11, &gregs[10]);
+ err |= __get_user(regs->r12, &gregs[11]);
+ err |= __get_user(regs->r13, &gregs[12]);
+ err |= __get_user(regs->r14, &gregs[13]);
+ err |= __get_user(regs->r15, &gregs[14]);
+ err |= __get_user(sw->r16, &gregs[15]);
+ err |= __get_user(sw->r17, &gregs[16]);
+ err |= __get_user(sw->r18, &gregs[17]);
+ err |= __get_user(sw->r19, &gregs[18]);
+ err |= __get_user(sw->r20, &gregs[19]);
+ err |= __get_user(sw->r21, &gregs[20]);
+ err |= __get_user(sw->r22, &gregs[21]);
+ err |= __get_user(sw->r23, &gregs[22]);
+ /* gregs[23] is handled below */
+ err |= __get_user(sw->fp, &gregs[24]); /* Verify, should this be
+ settable */
+ err |= __get_user(sw->gp, &gregs[25]); /* Verify, should this be
+ settable */
+
+ err |= __get_user(temp, &gregs[26]); /* Not really necessary no user
+ settable bits */
+ err |= __get_user(regs->ea, &gregs[27]);
+
+ err |= __get_user(regs->ra, &gregs[23]);
+ err |= __get_user(regs->sp, &gregs[28]);
+
+ regs->orig_r2 = -1; /* disable syscall checks */
+
+ err |= restore_altstack(&uc->uc_stack);
+ if (err)
+ goto badframe;
+
+ *pr2 = regs->r2;
+ return err;
+
+badframe:
+ return 1;
+}
+
+asmlinkage int do_rt_sigreturn(struct switch_stack *sw)
+{
+ struct pt_regs *regs = (struct pt_regs *)(sw + 1);
+ /* Verify, can we follow the stack back */
+ struct rt_sigframe *frame = (struct rt_sigframe *) regs->sp;
+ sigset_t set;
+ int rval;
+
+ if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
+ goto badframe;
+
+ if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
+ goto badframe;
+
+ set_current_blocked(&set);
+
+ if (rt_restore_ucontext(regs, sw, &frame->uc, &rval))
+ goto badframe;
+
+ return rval;
+
+badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+static inline int rt_setup_ucontext(struct ucontext *uc, struct pt_regs *regs)
+{
+ struct switch_stack *sw = (struct switch_stack *)regs - 1;
+ greg_t *gregs = uc->uc_mcontext.gregs;
+ int err = 0;
+
+ err |= __put_user(MCONTEXT_VERSION, &uc->uc_mcontext.version);
+ err |= __put_user(regs->r1, &gregs[0]);
+ err |= __put_user(regs->r2, &gregs[1]);
+ err |= __put_user(regs->r3, &gregs[2]);
+ err |= __put_user(regs->r4, &gregs[3]);
+ err |= __put_user(regs->r5, &gregs[4]);
+ err |= __put_user(regs->r6, &gregs[5]);
+ err |= __put_user(regs->r7, &gregs[6]);
+ err |= __put_user(regs->r8, &gregs[7]);
+ err |= __put_user(regs->r9, &gregs[8]);
+ err |= __put_user(regs->r10, &gregs[9]);
+ err |= __put_user(regs->r11, &gregs[10]);
+ err |= __put_user(regs->r12, &gregs[11]);
+ err |= __put_user(regs->r13, &gregs[12]);
+ err |= __put_user(regs->r14, &gregs[13]);
+ err |= __put_user(regs->r15, &gregs[14]);
+ err |= __put_user(sw->r16, &gregs[15]);
+ err |= __put_user(sw->r17, &gregs[16]);
+ err |= __put_user(sw->r18, &gregs[17]);
+ err |= __put_user(sw->r19, &gregs[18]);
+ err |= __put_user(sw->r20, &gregs[19]);
+ err |= __put_user(sw->r21, &gregs[20]);
+ err |= __put_user(sw->r22, &gregs[21]);
+ err |= __put_user(sw->r23, &gregs[22]);
+ err |= __put_user(regs->ra, &gregs[23]);
+ err |= __put_user(sw->fp, &gregs[24]);
+ err |= __put_user(sw->gp, &gregs[25]);
+ err |= __put_user(regs->ea, &gregs[27]);
+ err |= __put_user(regs->sp, &gregs[28]);
+ return err;
+}
+
+static inline void *get_sigframe(struct ksignal *ksig, struct pt_regs *regs,
+ size_t frame_size)
+{
+ unsigned long usp;
+
+ /* Default to using normal stack. */
+ usp = regs->sp;
+
+ /* This is the X/Open sanctioned signal stack switching. */
+ usp = sigsp(usp, ksig);
+
+ /* Verify, is it 32 or 64 bit aligned */
+ return (void *)((usp - frame_size) & -8UL);
+}
+
+static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,
+ struct pt_regs *regs)
+{
+ struct rt_sigframe *frame;
+ int err = 0;
+
+ frame = get_sigframe(ksig, regs, sizeof(*frame));
+
+ if (ksig->ka.sa.sa_flags & SA_SIGINFO)
+ err |= copy_siginfo_to_user(&frame->info, &ksig->info);
+
+ /* Create the ucontext. */
+ err |= __put_user(0, &frame->uc.uc_flags);
+ err |= __put_user(0, &frame->uc.uc_link);
+ err |= __save_altstack(&frame->uc.uc_stack, regs->sp);
+ err |= rt_setup_ucontext(&frame->uc, regs);
+ err |= copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
+
+ if (err)
+ goto give_sigsegv;
+
+ /* Set up to return from userspace; jump to fixed address sigreturn
+ trampoline on kuser page. */
+ regs->ra = (unsigned long) (0x1040);
+
+ /* Set up registers for signal handler */
+ regs->sp = (unsigned long) frame;
+ regs->r4 = (unsigned long) ksig->sig;
+ regs->r5 = (unsigned long) &frame->info;
+ regs->r6 = (unsigned long) &frame->uc;
+ regs->ea = (unsigned long) ksig->ka.sa.sa_handler;
+ return 0;
+
+give_sigsegv:
+ force_sigsegv(ksig->sig, current);
+ return -EFAULT;
+}
+
+/*
+ * OK, we're invoking a handler
+ */
+static void handle_signal(struct ksignal *ksig, struct pt_regs *regs)
+{
+ int ret;
+ sigset_t *oldset = sigmask_to_save();
+
+ /* set up the stack frame */
+ ret = setup_rt_frame(ksig, oldset, regs);
+
+ signal_setup_done(ret, ksig, 0);
+}
+
+static int do_signal(struct pt_regs *regs)
+{
+ unsigned int retval = 0, continue_addr = 0, restart_addr = 0;
+ int restart = 0;
+ struct ksignal ksig;
+
+ current->thread.kregs = regs;
+
+ /*
+ * If we were from a system call, check for system call restarting...
+ */
+ if (regs->orig_r2 >= 0) {
+ continue_addr = regs->ea;
+ restart_addr = continue_addr - 4;
+ retval = regs->r2;
+
+ /*
+ * Prepare for system call restart. We do this here so that a
+ * debugger will see the already changed PC.
+ */
+ switch (retval) {
+ case ERESTART_RESTARTBLOCK:
+ restart = -2;
+ case ERESTARTNOHAND:
+ case ERESTARTSYS:
+ case ERESTARTNOINTR:
+ restart++;
+ regs->r2 = regs->orig_r2;
+ regs->r7 = regs->orig_r7;
+ regs->ea = restart_addr;
+ break;
+ }
+ }
+
+ if (get_signal(&ksig)) {
+ /* handler */
+ if (unlikely(restart && regs->ea == restart_addr)) {
+ if (retval == ERESTARTNOHAND ||
+ retval == ERESTART_RESTARTBLOCK ||
+ (retval == ERESTARTSYS
+ && !(ksig.ka.sa.sa_flags & SA_RESTART))) {
+ regs->r2 = EINTR;
+ regs->r7 = 1;
+ regs->ea = continue_addr;
+ }
+ }
+ handle_signal(&ksig, regs);
+ return 0;
+ }
+
+ /*
+ * No handler present
+ */
+ if (unlikely(restart) && regs->ea == restart_addr) {
+ regs->ea = continue_addr;
+ regs->r2 = __NR_restart_syscall;
+ }
+
+ /*
+ * If there's no signal to deliver, we just put the saved sigmask back.
+ */
+ restore_saved_sigmask();
+
+ return restart;
+}
+
+asmlinkage int do_notify_resume(struct pt_regs *regs)
+{
+ /*
+ * We want the common case to go fast, which is why we may in certain
+ * cases get here from kernel mode. Just return without doing anything
+ * if so.
+ */
+ if (!user_mode(regs))
+ return 0;
+
+ if (test_thread_flag(TIF_SIGPENDING)) {
+ int restart = do_signal(regs);
+
+ if (unlikely(restart)) {
+ /*
+ * Restart without handlers.
+ * Deal with it without leaving
+ * the kernel space.
+ */
+ return restart;
+ }
+ } else if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME))
+ tracehook_notify_resume(regs);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2013 Altera Corporation
+ * Copyright (C) 2011-2012 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2004 Microtronix Datacom Ltd.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/export.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
+
+#include <asm/cacheflush.h>
+#include <asm/traps.h>
+
+/* sys_cacheflush -- flush the processor cache. */
+asmlinkage int sys_cacheflush(unsigned long addr, unsigned long len,
+ unsigned int op)
+{
+ struct vm_area_struct *vma;
+
+ if (len == 0)
+ return 0;
+
+ /* We only support op 0 now, return error if op is non-zero.*/
+ if (op)
+ return -EINVAL;
+
+ /* Check for overflow */
+ if (addr + len < addr)
+ return -EFAULT;
+
+ /*
+ * Verify that the specified address region actually belongs
+ * to this process.
+ */
+ vma = find_vma(current->mm, addr);
+ if (vma == NULL || addr < vma->vm_start || addr + len > vma->vm_end)
+ return -EFAULT;
+
+ flush_cache_range(vma, addr, addr + len);
+
+ return 0;
+}
+
+asmlinkage int sys_getpagesize(void)
+{
+ return PAGE_SIZE;
+}
--- /dev/null
+/*
+ * Copyright Altera Corporation (C) 2013. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/syscalls.h>
+#include <linux/signal.h>
+#include <linux/unistd.h>
+
+#include <asm/syscalls.h>
+
+#undef __SYSCALL
+#define __SYSCALL(nr, call) [nr] = (call),
+
+void *sys_call_table[__NR_syscalls] = {
+#include <asm/unistd.h>
+};
--- /dev/null
+/*
+ * Copyright (C) 2013-2014 Altera Corporation
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2004 Microtronix Datacom Ltd.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#define ALTERA_TIMER_STATUS_REG 0
+#define ALTERA_TIMER_CONTROL_REG 4
+#define ALTERA_TIMER_PERIODL_REG 8
+#define ALTERA_TIMER_PERIODH_REG 12
+#define ALTERA_TIMER_SNAPL_REG 16
+#define ALTERA_TIMER_SNAPH_REG 20
+
+#define ALTERA_TIMER_CONTROL_ITO_MSK (0x1)
+#define ALTERA_TIMER_CONTROL_CONT_MSK (0x2)
+#define ALTERA_TIMER_CONTROL_START_MSK (0x4)
+#define ALTERA_TIMER_CONTROL_STOP_MSK (0x8)
+
+struct nios2_timer {
+ void __iomem *base;
+ unsigned long freq;
+};
+
+struct nios2_clockevent_dev {
+ struct nios2_timer timer;
+ struct clock_event_device ced;
+};
+
+struct nios2_clocksource {
+ struct nios2_timer timer;
+ struct clocksource cs;
+};
+
+static inline struct nios2_clockevent_dev *
+ to_nios2_clkevent(struct clock_event_device *evt)
+{
+ return container_of(evt, struct nios2_clockevent_dev, ced);
+}
+
+static inline struct nios2_clocksource *
+ to_nios2_clksource(struct clocksource *cs)
+{
+ return container_of(cs, struct nios2_clocksource, cs);
+}
+
+static u16 timer_readw(struct nios2_timer *timer, u32 offs)
+{
+ return readw(timer->base + offs);
+}
+
+static void timer_writew(struct nios2_timer *timer, u16 val, u32 offs)
+{
+ writew(val, timer->base + offs);
+}
+
+static inline unsigned long read_timersnapshot(struct nios2_timer *timer)
+{
+ unsigned long count;
+
+ timer_writew(timer, 0, ALTERA_TIMER_SNAPL_REG);
+ count = timer_readw(timer, ALTERA_TIMER_SNAPH_REG) << 16 |
+ timer_readw(timer, ALTERA_TIMER_SNAPL_REG);
+
+ return count;
+}
+
+static cycle_t nios2_timer_read(struct clocksource *cs)
+{
+ struct nios2_clocksource *nios2_cs = to_nios2_clksource(cs);
+ unsigned long flags;
+ u32 count;
+
+ local_irq_save(flags);
+ count = read_timersnapshot(&nios2_cs->timer);
+ local_irq_restore(flags);
+
+ /* Counter is counting down */
+ return ~count;
+}
+
+static struct nios2_clocksource nios2_cs = {
+ .cs = {
+ .name = "nios2-clksrc",
+ .rating = 250,
+ .read = nios2_timer_read,
+ .mask = CLOCKSOURCE_MASK(32),
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+ },
+};
+
+cycles_t get_cycles(void)
+{
+ return nios2_timer_read(&nios2_cs.cs);
+}
+
+static void nios2_timer_start(struct nios2_timer *timer)
+{
+ u16 ctrl;
+
+ ctrl = timer_readw(timer, ALTERA_TIMER_CONTROL_REG);
+ ctrl |= ALTERA_TIMER_CONTROL_START_MSK;
+ timer_writew(timer, ctrl, ALTERA_TIMER_CONTROL_REG);
+}
+
+static void nios2_timer_stop(struct nios2_timer *timer)
+{
+ u16 ctrl;
+
+ ctrl = timer_readw(timer, ALTERA_TIMER_CONTROL_REG);
+ ctrl |= ALTERA_TIMER_CONTROL_STOP_MSK;
+ timer_writew(timer, ctrl, ALTERA_TIMER_CONTROL_REG);
+}
+
+static void nios2_timer_config(struct nios2_timer *timer, unsigned long period,
+ enum clock_event_mode mode)
+{
+ u16 ctrl;
+
+ /* The timer's actual period is one cycle greater than the value
+ * stored in the period register. */
+ period--;
+
+ ctrl = timer_readw(timer, ALTERA_TIMER_CONTROL_REG);
+ /* stop counter */
+ timer_writew(timer, ctrl | ALTERA_TIMER_CONTROL_STOP_MSK,
+ ALTERA_TIMER_CONTROL_REG);
+
+ /* write new count */
+ timer_writew(timer, period, ALTERA_TIMER_PERIODL_REG);
+ timer_writew(timer, period >> 16, ALTERA_TIMER_PERIODH_REG);
+
+ ctrl |= ALTERA_TIMER_CONTROL_START_MSK | ALTERA_TIMER_CONTROL_ITO_MSK;
+ if (mode == CLOCK_EVT_MODE_PERIODIC)
+ ctrl |= ALTERA_TIMER_CONTROL_CONT_MSK;
+ else
+ ctrl &= ~ALTERA_TIMER_CONTROL_CONT_MSK;
+ timer_writew(timer, ctrl, ALTERA_TIMER_CONTROL_REG);
+}
+
+static int nios2_timer_set_next_event(unsigned long delta,
+ struct clock_event_device *evt)
+{
+ struct nios2_clockevent_dev *nios2_ced = to_nios2_clkevent(evt);
+
+ nios2_timer_config(&nios2_ced->timer, delta, evt->mode);
+
+ return 0;
+}
+
+static void nios2_timer_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *evt)
+{
+ unsigned long period;
+ struct nios2_clockevent_dev *nios2_ced = to_nios2_clkevent(evt);
+ struct nios2_timer *timer = &nios2_ced->timer;
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ period = DIV_ROUND_UP(timer->freq, HZ);
+ nios2_timer_config(timer, period, CLOCK_EVT_MODE_PERIODIC);
+ break;
+ case CLOCK_EVT_MODE_ONESHOT:
+ case CLOCK_EVT_MODE_UNUSED:
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ nios2_timer_stop(timer);
+ break;
+ case CLOCK_EVT_MODE_RESUME:
+ nios2_timer_start(timer);
+ break;
+ }
+}
+
+irqreturn_t timer_interrupt(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = (struct clock_event_device *) dev_id;
+ struct nios2_clockevent_dev *nios2_ced = to_nios2_clkevent(evt);
+
+ /* Clear the interrupt condition */
+ timer_writew(&nios2_ced->timer, 0, ALTERA_TIMER_STATUS_REG);
+ evt->event_handler(evt);
+
+ return IRQ_HANDLED;
+}
+
+static void __init nios2_timer_get_base_and_freq(struct device_node *np,
+ void __iomem **base, u32 *freq)
+{
+ *base = of_iomap(np, 0);
+ if (!*base)
+ panic("Unable to map reg for %s\n", np->name);
+
+ if (of_property_read_u32(np, "clock-frequency", freq))
+ panic("Unable to get %s clock frequency\n", np->name);
+}
+
+static struct nios2_clockevent_dev nios2_ce = {
+ .ced = {
+ .name = "nios2-clkevent",
+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+ .rating = 250,
+ .shift = 32,
+ .set_next_event = nios2_timer_set_next_event,
+ .set_mode = nios2_timer_set_mode,
+ },
+};
+
+static __init void nios2_clockevent_init(struct device_node *timer)
+{
+ void __iomem *iobase;
+ u32 freq;
+ int irq;
+
+ nios2_timer_get_base_and_freq(timer, &iobase, &freq);
+
+ irq = irq_of_parse_and_map(timer, 0);
+ if (!irq)
+ panic("Unable to parse timer irq\n");
+
+ nios2_ce.timer.base = iobase;
+ nios2_ce.timer.freq = freq;
+
+ nios2_ce.ced.cpumask = cpumask_of(0);
+ nios2_ce.ced.irq = irq;
+
+ nios2_timer_stop(&nios2_ce.timer);
+ /* clear pending interrupt */
+ timer_writew(&nios2_ce.timer, 0, ALTERA_TIMER_STATUS_REG);
+
+ if (request_irq(irq, timer_interrupt, IRQF_TIMER, timer->name,
+ &nios2_ce.ced))
+ panic("Unable to setup timer irq\n");
+
+ clockevents_config_and_register(&nios2_ce.ced, freq, 1, ULONG_MAX);
+}
+
+static __init void nios2_clocksource_init(struct device_node *timer)
+{
+ unsigned int ctrl;
+ void __iomem *iobase;
+ u32 freq;
+
+ nios2_timer_get_base_and_freq(timer, &iobase, &freq);
+
+ nios2_cs.timer.base = iobase;
+ nios2_cs.timer.freq = freq;
+
+ clocksource_register_hz(&nios2_cs.cs, freq);
+
+ timer_writew(&nios2_cs.timer, USHRT_MAX, ALTERA_TIMER_PERIODL_REG);
+ timer_writew(&nios2_cs.timer, USHRT_MAX, ALTERA_TIMER_PERIODH_REG);
+
+ /* interrupt disable + continuous + start */
+ ctrl = ALTERA_TIMER_CONTROL_CONT_MSK | ALTERA_TIMER_CONTROL_START_MSK;
+ timer_writew(&nios2_cs.timer, ctrl, ALTERA_TIMER_CONTROL_REG);
+
+ /* Calibrate the delay loop directly */
+ lpj_fine = freq / HZ;
+}
+
+/*
+ * The first timer instance will use as a clockevent. If there are two or
+ * more instances, the second one gets used as clocksource and all
+ * others are unused.
+*/
+static void __init nios2_time_init(struct device_node *timer)
+{
+ static int num_called;
+
+ switch (num_called) {
+ case 0:
+ nios2_clockevent_init(timer);
+ break;
+ case 1:
+ nios2_clocksource_init(timer);
+ break;
+ default:
+ break;
+ }
+
+ num_called++;
+}
+
+void read_persistent_clock(struct timespec *ts)
+{
+ ts->tv_sec = mktime(2007, 1, 1, 0, 0, 0);
+ ts->tv_nsec = 0;
+}
+
+void __init time_init(void)
+{
+ clocksource_of_init();
+}
+
+CLOCKSOURCE_OF_DECLARE(nios2_timer, "altr,timer-1.0", nios2_time_init);
--- /dev/null
+/*
+ * Hardware exception handling
+ *
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2004 Microtronix Datacom Ltd.
+ * Copyright (C) 2001 Vic Phillips
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file COPYING in the main directory of this
+ * archive for more details.
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/export.h>
+#include <linux/mm.h>
+#include <linux/ptrace.h>
+
+#include <asm/traps.h>
+#include <asm/sections.h>
+#include <asm/uaccess.h>
+
+static DEFINE_SPINLOCK(die_lock);
+
+void die(const char *str, struct pt_regs *regs, long err)
+{
+ console_verbose();
+ spin_lock_irq(&die_lock);
+ pr_warn("Oops: %s, sig: %ld\n", str, err);
+ show_regs(regs);
+ spin_unlock_irq(&die_lock);
+ /*
+ * do_exit() should take care of panic'ing from an interrupt
+ * context so we don't handle it here
+ */
+ do_exit(err);
+}
+
+void _exception(int signo, struct pt_regs *regs, int code, unsigned long addr)
+{
+ siginfo_t info;
+
+ if (!user_mode(regs))
+ die("Exception in kernel mode", regs, signo);
+
+ info.si_signo = signo;
+ info.si_errno = 0;
+ info.si_code = code;
+ info.si_addr = (void __user *) addr;
+ force_sig_info(signo, &info, current);
+}
+
+/*
+ * The show_stack is an external API which we do not use ourselves.
+ */
+
+int kstack_depth_to_print = 48;
+
+void show_stack(struct task_struct *task, unsigned long *stack)
+{
+ unsigned long *endstack, addr;
+ int i;
+
+ if (!stack) {
+ if (task)
+ stack = (unsigned long *)task->thread.ksp;
+ else
+ stack = (unsigned long *)&stack;
+ }
+
+ addr = (unsigned long) stack;
+ endstack = (unsigned long *) PAGE_ALIGN(addr);
+
+ pr_emerg("Stack from %08lx:", (unsigned long)stack);
+ for (i = 0; i < kstack_depth_to_print; i++) {
+ if (stack + 1 > endstack)
+ break;
+ if (i % 8 == 0)
+ pr_emerg("\n ");
+ pr_emerg(" %08lx", *stack++);
+ }
+
+ pr_emerg("\nCall Trace:");
+ i = 0;
+ while (stack + 1 <= endstack) {
+ addr = *stack++;
+ /*
+ * If the address is either in the text segment of the
+ * kernel, or in the region which contains vmalloc'ed
+ * memory, it *may* be the address of a calling
+ * routine; if so, print it so that someone tracing
+ * down the cause of the crash will be able to figure
+ * out the call path that was taken.
+ */
+ if (((addr >= (unsigned long) _stext) &&
+ (addr <= (unsigned long) _etext))) {
+ if (i % 4 == 0)
+ pr_emerg("\n ");
+ pr_emerg(" [<%08lx>]", addr);
+ i++;
+ }
+ }
+ pr_emerg("\n");
+}
+
+void __init trap_init(void)
+{
+ /* Nothing to do here */
+}
+
+/* Breakpoint handler */
+asmlinkage void breakpoint_c(struct pt_regs *fp)
+{
+ /*
+ * The breakpoint entry code has moved the PC on by 4 bytes, so we must
+ * move it back. This could be done on the host but we do it here
+ * because monitor.S of JTAG gdbserver does it too.
+ */
+ fp->ea -= 4;
+ _exception(SIGTRAP, fp, TRAP_BRKPT, fp->ea);
+}
+
+#ifndef CONFIG_NIOS2_ALIGNMENT_TRAP
+/* Alignment exception handler */
+asmlinkage void handle_unaligned_c(struct pt_regs *fp, int cause)
+{
+ unsigned long addr = RDCTL(CTL_BADADDR);
+
+ cause >>= 2;
+ fp->ea -= 4;
+
+ if (fixup_exception(fp))
+ return;
+
+ if (!user_mode(fp)) {
+ pr_alert("Unaligned access from kernel mode, this might be a hardware\n");
+ pr_alert("problem, dump registers and restart the instruction\n");
+ pr_alert(" BADADDR 0x%08lx\n", addr);
+ pr_alert(" cause %d\n", cause);
+ pr_alert(" op-code 0x%08lx\n", *(unsigned long *)(fp->ea));
+ show_regs(fp);
+ return;
+ }
+
+ _exception(SIGBUS, fp, BUS_ADRALN, addr);
+}
+#endif /* CONFIG_NIOS2_ALIGNMENT_TRAP */
+
+/* Illegal instruction handler */
+asmlinkage void handle_illegal_c(struct pt_regs *fp)
+{
+ fp->ea -= 4;
+ _exception(SIGILL, fp, ILL_ILLOPC, fp->ea);
+}
+
+/* Supervisor instruction handler */
+asmlinkage void handle_supervisor_instr(struct pt_regs *fp)
+{
+ fp->ea -= 4;
+ _exception(SIGILL, fp, ILL_PRVOPC, fp->ea);
+}
+
+/* Division error handler */
+asmlinkage void handle_diverror_c(struct pt_regs *fp)
+{
+ fp->ea -= 4;
+ _exception(SIGFPE, fp, FPE_INTDIV, fp->ea);
+}
+
+/* Unhandled exception handler */
+asmlinkage void unhandled_exception(struct pt_regs *regs, int cause)
+{
+ unsigned long addr = RDCTL(CTL_BADADDR);
+
+ cause /= 4;
+
+ pr_emerg("Unhandled exception #%d in %s mode (badaddr=0x%08lx)\n",
+ cause, user_mode(regs) ? "user" : "kernel", addr);
+
+ regs->ea -= 4;
+ show_regs(regs);
+
+ pr_emerg("opcode: 0x%08lx\n", *(unsigned long *)(regs->ea));
+}
--- /dev/null
+/*
+ * Copyright (C) 2009 Thomas Chou <thomas@wytron.com.tw>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <asm/page.h>
+#include <asm-generic/vmlinux.lds.h>
+#include <asm/cache.h>
+#include <asm/thread_info.h>
+
+OUTPUT_FORMAT("elf32-littlenios2", "elf32-littlenios2", "elf32-littlenios2")
+
+OUTPUT_ARCH(nios)
+ENTRY(_start) /* Defined in head.S */
+
+jiffies = jiffies_64;
+
+SECTIONS
+{
+ . = CONFIG_NIOS2_MEM_BASE | CONFIG_NIOS2_KERNEL_REGION_BASE;
+
+ _text = .;
+ _stext = .;
+ HEAD_TEXT_SECTION
+ .text : {
+ TEXT_TEXT
+ SCHED_TEXT
+ LOCK_TEXT
+ IRQENTRY_TEXT
+ KPROBES_TEXT
+ } =0
+ _etext = .;
+
+ .got : {
+ *(.got.plt)
+ *(.igot.plt)
+ *(.got)
+ *(.igot)
+ }
+
+ EXCEPTION_TABLE(L1_CACHE_BYTES)
+
+ . = ALIGN(PAGE_SIZE);
+ __init_begin = .;
+ INIT_TEXT_SECTION(PAGE_SIZE)
+ INIT_DATA_SECTION(PAGE_SIZE)
+ PERCPU_SECTION(L1_CACHE_BYTES)
+ __init_end = .;
+
+ _sdata = .;
+ RO_DATA_SECTION(PAGE_SIZE)
+ RW_DATA_SECTION(L1_CACHE_BYTES, PAGE_SIZE, THREAD_SIZE)
+ _edata = .;
+
+ BSS_SECTION(0, 0, 0)
+ _end = .;
+
+ STABS_DEBUG
+ DWARF_DEBUG
+ NOTES
+
+ DISCARDS
+}
--- /dev/null
+#
+# Makefile for Nios2-specific library files.
+#
+
+lib-y += delay.o
+lib-y += memcpy.o
+lib-y += memmove.o
+lib-y += memset.o
--- /dev/null
+/* Copyright Altera Corporation (C) 2014. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/module.h>
+#include <asm/delay.h>
+#include <asm/param.h>
+#include <asm/processor.h>
+#include <asm/timex.h>
+
+void __delay(unsigned long cycles)
+{
+ cycles_t start = get_cycles();
+
+ while ((get_cycles() - start) < cycles)
+ cpu_relax();
+}
+EXPORT_SYMBOL(__delay);
+
+void __const_udelay(unsigned long xloops)
+{
+ u64 loops;
+
+ loops = (u64)xloops * loops_per_jiffy * HZ;
+
+ __delay(loops >> 32);
+}
+EXPORT_SYMBOL(__const_udelay);
+
+void __udelay(unsigned long usecs)
+{
+ __const_udelay(usecs * 0x10C7UL); /* 2**32 / 1000000 (rounded up) */
+}
+EXPORT_SYMBOL(__udelay);
+
+void __ndelay(unsigned long nsecs)
+{
+ __const_udelay(nsecs * 0x5UL); /* 2**32 / 1000000000 (rounded up) */
+}
+EXPORT_SYMBOL(__ndelay);
--- /dev/null
+/* Extracted from GLIBC memcpy.c and memcopy.h, which is:
+ Copyright (C) 1991, 1992, 1993, 1997, 2004 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Torbjorn Granlund (tege@sics.se).
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <linux/types.h>
+
+/* Type to use for aligned memory operations.
+ This should normally be the biggest type supported by a single load
+ and store. */
+#define op_t unsigned long int
+#define OPSIZ (sizeof(op_t))
+
+/* Optimal type for storing bytes in registers. */
+#define reg_char char
+
+#define MERGE(w0, sh_1, w1, sh_2) (((w0) >> (sh_1)) | ((w1) << (sh_2)))
+
+/* Copy exactly NBYTES bytes from SRC_BP to DST_BP,
+ without any assumptions about alignment of the pointers. */
+#define BYTE_COPY_FWD(dst_bp, src_bp, nbytes) \
+do { \
+ size_t __nbytes = (nbytes); \
+ while (__nbytes > 0) { \
+ unsigned char __x = ((unsigned char *) src_bp)[0]; \
+ src_bp += 1; \
+ __nbytes -= 1; \
+ ((unsigned char *) dst_bp)[0] = __x; \
+ dst_bp += 1; \
+ } \
+} while (0)
+
+/* Copy *up to* NBYTES bytes from SRC_BP to DST_BP, with
+ the assumption that DST_BP is aligned on an OPSIZ multiple. If
+ not all bytes could be easily copied, store remaining number of bytes
+ in NBYTES_LEFT, otherwise store 0. */
+/* extern void _wordcopy_fwd_aligned __P ((long int, long int, size_t)); */
+/* extern void _wordcopy_fwd_dest_aligned __P ((long int, long int, size_t)); */
+#define WORD_COPY_FWD(dst_bp, src_bp, nbytes_left, nbytes) \
+do { \
+ if (src_bp % OPSIZ == 0) \
+ _wordcopy_fwd_aligned(dst_bp, src_bp, (nbytes) / OPSIZ);\
+ else \
+ _wordcopy_fwd_dest_aligned(dst_bp, src_bp, (nbytes) / OPSIZ);\
+ src_bp += (nbytes) & -OPSIZ; \
+ dst_bp += (nbytes) & -OPSIZ; \
+ (nbytes_left) = (nbytes) % OPSIZ; \
+} while (0)
+
+
+/* Threshold value for when to enter the unrolled loops. */
+#define OP_T_THRES 16
+
+/* _wordcopy_fwd_aligned -- Copy block beginning at SRCP to
+ block beginning at DSTP with LEN `op_t' words (not LEN bytes!).
+ Both SRCP and DSTP should be aligned for memory operations on `op_t's. */
+/* stream-lined (read x8 + write x8) */
+static void _wordcopy_fwd_aligned(long int dstp, long int srcp, size_t len)
+{
+ while (len > 7) {
+ register op_t a0, a1, a2, a3, a4, a5, a6, a7;
+
+ a0 = ((op_t *) srcp)[0];
+ a1 = ((op_t *) srcp)[1];
+ a2 = ((op_t *) srcp)[2];
+ a3 = ((op_t *) srcp)[3];
+ a4 = ((op_t *) srcp)[4];
+ a5 = ((op_t *) srcp)[5];
+ a6 = ((op_t *) srcp)[6];
+ a7 = ((op_t *) srcp)[7];
+ ((op_t *) dstp)[0] = a0;
+ ((op_t *) dstp)[1] = a1;
+ ((op_t *) dstp)[2] = a2;
+ ((op_t *) dstp)[3] = a3;
+ ((op_t *) dstp)[4] = a4;
+ ((op_t *) dstp)[5] = a5;
+ ((op_t *) dstp)[6] = a6;
+ ((op_t *) dstp)[7] = a7;
+
+ srcp += 8 * OPSIZ;
+ dstp += 8 * OPSIZ;
+ len -= 8;
+ }
+ while (len > 0) {
+ *(op_t *)dstp = *(op_t *)srcp;
+
+ srcp += OPSIZ;
+ dstp += OPSIZ;
+ len -= 1;
+ }
+}
+
+/* _wordcopy_fwd_dest_aligned -- Copy block beginning at SRCP to
+ block beginning at DSTP with LEN `op_t' words (not LEN bytes!).
+ DSTP should be aligned for memory operations on `op_t's, but SRCP must
+ *not* be aligned. */
+/* stream-lined (read x4 + write x4) */
+static void _wordcopy_fwd_dest_aligned(long int dstp, long int srcp,
+ size_t len)
+{
+ op_t ap;
+ int sh_1, sh_2;
+
+ /* Calculate how to shift a word read at the memory operation
+ aligned srcp to make it aligned for copy. */
+
+ sh_1 = 8 * (srcp % OPSIZ);
+ sh_2 = 8 * OPSIZ - sh_1;
+
+ /* Make SRCP aligned by rounding it down to the beginning of the `op_t'
+ it points in the middle of. */
+ srcp &= -OPSIZ;
+ ap = ((op_t *) srcp)[0];
+ srcp += OPSIZ;
+
+ while (len > 3) {
+ op_t a0, a1, a2, a3;
+
+ a0 = ((op_t *) srcp)[0];
+ a1 = ((op_t *) srcp)[1];
+ a2 = ((op_t *) srcp)[2];
+ a3 = ((op_t *) srcp)[3];
+ ((op_t *) dstp)[0] = MERGE(ap, sh_1, a0, sh_2);
+ ((op_t *) dstp)[1] = MERGE(a0, sh_1, a1, sh_2);
+ ((op_t *) dstp)[2] = MERGE(a1, sh_1, a2, sh_2);
+ ((op_t *) dstp)[3] = MERGE(a2, sh_1, a3, sh_2);
+
+ ap = a3;
+ srcp += 4 * OPSIZ;
+ dstp += 4 * OPSIZ;
+ len -= 4;
+ }
+ while (len > 0) {
+ register op_t a0;
+
+ a0 = ((op_t *) srcp)[0];
+ ((op_t *) dstp)[0] = MERGE(ap, sh_1, a0, sh_2);
+
+ ap = a0;
+ srcp += OPSIZ;
+ dstp += OPSIZ;
+ len -= 1;
+ }
+}
+
+void *memcpy(void *dstpp, const void *srcpp, size_t len)
+{
+ unsigned long int dstp = (long int) dstpp;
+ unsigned long int srcp = (long int) srcpp;
+
+ /* Copy from the beginning to the end. */
+
+ /* If there not too few bytes to copy, use word copy. */
+ if (len >= OP_T_THRES) {
+ /* Copy just a few bytes to make DSTP aligned. */
+ len -= (-dstp) % OPSIZ;
+ BYTE_COPY_FWD(dstp, srcp, (-dstp) % OPSIZ);
+
+ /* Copy whole pages from SRCP to DSTP by virtual address
+ manipulation, as much as possible. */
+
+ /* PAGE_COPY_FWD_MAYBE (dstp, srcp, len, len); */
+
+ /* Copy from SRCP to DSTP taking advantage of the known
+ alignment of DSTP. Number of bytes remaining is put in the
+ third argument, i.e. in LEN. This number may vary from
+ machine to machine. */
+
+ WORD_COPY_FWD(dstp, srcp, len, len);
+
+ /* Fall out and copy the tail. */
+ }
+
+ /* There are just a few bytes to copy. Use byte memory operations. */
+ BYTE_COPY_FWD(dstp, srcp, len);
+
+ return dstpp;
+}
+
+void *memcpyb(void *dstpp, const void *srcpp, unsigned len)
+{
+ unsigned long int dstp = (long int) dstpp;
+ unsigned long int srcp = (long int) srcpp;
+
+ BYTE_COPY_FWD(dstp, srcp, len);
+
+ return dstpp;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2004 Microtronix Datacom Ltd
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/types.h>
+#include <linux/string.h>
+
+#ifdef __HAVE_ARCH_MEMMOVE
+void *memmove(void *d, const void *s, size_t count)
+{
+ unsigned long dst, src;
+
+ if (!count)
+ return d;
+
+ if (d < s) {
+ dst = (unsigned long) d;
+ src = (unsigned long) s;
+
+ if ((count < 8) || ((dst ^ src) & 3))
+ goto restup;
+
+ if (dst & 1) {
+ *(char *)dst++ = *(char *)src++;
+ count--;
+ }
+ if (dst & 2) {
+ *(short *)dst = *(short *)src;
+ src += 2;
+ dst += 2;
+ count -= 2;
+ }
+ while (count > 3) {
+ *(long *)dst = *(long *)src;
+ src += 4;
+ dst += 4;
+ count -= 4;
+ }
+restup:
+ while (count--)
+ *(char *)dst++ = *(char *)src++;
+ } else {
+ dst = (unsigned long) d + count;
+ src = (unsigned long) s + count;
+
+ if ((count < 8) || ((dst ^ src) & 3))
+ goto restdown;
+
+ if (dst & 1) {
+ src--;
+ dst--;
+ count--;
+ *(char *)dst = *(char *)src;
+ }
+ if (dst & 2) {
+ src -= 2;
+ dst -= 2;
+ count -= 2;
+ *(short *)dst = *(short *)src;
+ }
+ while (count > 3) {
+ src -= 4;
+ dst -= 4;
+ count -= 4;
+ *(long *)dst = *(long *)src;
+ }
+restdown:
+ while (count--) {
+ src--;
+ dst--;
+ *(char *)dst = *(char *)src;
+ }
+ }
+
+ return d;
+}
+#endif /* __HAVE_ARCH_MEMMOVE */
--- /dev/null
+/*
+ * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2004 Microtronix Datacom Ltd
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/types.h>
+#include <linux/string.h>
+
+#ifdef __HAVE_ARCH_MEMSET
+void *memset(void *s, int c, size_t count)
+{
+ int destptr, charcnt, dwordcnt, fill8reg, wrkrega;
+
+ if (!count)
+ return s;
+
+ c &= 0xFF;
+
+ if (count <= 8) {
+ char *xs = (char *) s;
+
+ while (count--)
+ *xs++ = c;
+ return s;
+ }
+
+ __asm__ __volatile__ (
+ /* fill8 %3, %5 (c & 0xff) */
+ " slli %4, %5, 8\n"
+ " or %4, %4, %5\n"
+ " slli %3, %4, 16\n"
+ " or %3, %3, %4\n"
+ /* Word-align %0 (s) if necessary */
+ " andi %4, %0, 0x01\n"
+ " beq %4, zero, 1f\n"
+ " addi %1, %1, -1\n"
+ " stb %3, 0(%0)\n"
+ " addi %0, %0, 1\n"
+ "1: mov %2, %1\n"
+ /* Dword-align %0 (s) if necessary */
+ " andi %4, %0, 0x02\n"
+ " beq %4, zero, 2f\n"
+ " addi %1, %1, -2\n"
+ " sth %3, 0(%0)\n"
+ " addi %0, %0, 2\n"
+ " mov %2, %1\n"
+ /* %1 and %2 are how many more bytes to set */
+ "2: srli %2, %2, 2\n"
+ /* %2 is how many dwords to set */
+ "3: stw %3, 0(%0)\n"
+ " addi %0, %0, 4\n"
+ " addi %2, %2, -1\n"
+ " bne %2, zero, 3b\n"
+ /* store residual word and/or byte if necessary */
+ " andi %4, %1, 0x02\n"
+ " beq %4, zero, 4f\n"
+ " sth %3, 0(%0)\n"
+ " addi %0, %0, 2\n"
+ /* store residual byte if necessary */
+ "4: andi %4, %1, 0x01\n"
+ " beq %4, zero, 5f\n"
+ " stb %3, 0(%0)\n"
+ "5:\n"
+ : "=r" (destptr), /* %0 Output */
+ "=r" (charcnt), /* %1 Output */
+ "=r" (dwordcnt), /* %2 Output */
+ "=r" (fill8reg), /* %3 Output */
+ "=r" (wrkrega) /* %4 Output */
+ : "r" (c), /* %5 Input */
+ "0" (s), /* %0 Input/Output */
+ "1" (count) /* %1 Input/Output */
+ : "memory" /* clobbered */
+ );
+
+ return s;
+}
+#endif /* __HAVE_ARCH_MEMSET */
--- /dev/null
+#
+# Makefile for the Nios2-specific parts of the memory manager.
+#
+
+obj-y += cacheflush.o
+obj-y += dma-mapping.o
+obj-y += extable.o
+obj-y += fault.o
+obj-y += init.o
+obj-y += ioremap.o
+obj-y += mmu_context.o
+obj-y += pgtable.o
+obj-y += tlb.o
+obj-y += uaccess.o
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2009, Wind River Systems Inc
+ * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com
+ */
+
+#include <linux/export.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+
+#include <asm/cacheflush.h>
+#include <asm/cpuinfo.h>
+
+static void __flush_dcache(unsigned long start, unsigned long end)
+{
+ unsigned long addr;
+
+ start &= ~(cpuinfo.dcache_line_size - 1);
+ end += (cpuinfo.dcache_line_size - 1);
+ end &= ~(cpuinfo.dcache_line_size - 1);
+
+ if (end > start + cpuinfo.dcache_size)
+ end = start + cpuinfo.dcache_size;
+
+ for (addr = start; addr < end; addr += cpuinfo.dcache_line_size) {
+ __asm__ __volatile__ (" flushda 0(%0)\n"
+ : /* Outputs */
+ : /* Inputs */ "r"(addr)
+ /* : No clobber */);
+ }
+}
+
+static void __flush_dcache_all(unsigned long start, unsigned long end)
+{
+ unsigned long addr;
+
+ start &= ~(cpuinfo.dcache_line_size - 1);
+ end += (cpuinfo.dcache_line_size - 1);
+ end &= ~(cpuinfo.dcache_line_size - 1);
+
+ if (end > start + cpuinfo.dcache_size)
+ end = start + cpuinfo.dcache_size;
+
+ for (addr = start; addr < end; addr += cpuinfo.dcache_line_size) {
+ __asm__ __volatile__ (" flushd 0(%0)\n"
+ : /* Outputs */
+ : /* Inputs */ "r"(addr)
+ /* : No clobber */);
+ }
+}
+
+static void __invalidate_dcache(unsigned long start, unsigned long end)
+{
+ unsigned long addr;
+
+ start &= ~(cpuinfo.dcache_line_size - 1);
+ end += (cpuinfo.dcache_line_size - 1);
+ end &= ~(cpuinfo.dcache_line_size - 1);
+
+ if (end > start + cpuinfo.dcache_size)
+ end = start + cpuinfo.dcache_size;
+
+ for (addr = start; addr < end; addr += cpuinfo.dcache_line_size) {
+ __asm__ __volatile__ (" initda 0(%0)\n"
+ : /* Outputs */
+ : /* Inputs */ "r"(addr)
+ /* : No clobber */);
+ }
+}
+
+static void __flush_icache(unsigned long start, unsigned long end)
+{
+ unsigned long addr;
+
+ start &= ~(cpuinfo.icache_line_size - 1);
+ end += (cpuinfo.icache_line_size - 1);
+ end &= ~(cpuinfo.icache_line_size - 1);
+
+ if (end > start + cpuinfo.icache_size)
+ end = start + cpuinfo.icache_size;
+
+ for (addr = start; addr < end; addr += cpuinfo.icache_line_size) {
+ __asm__ __volatile__ (" flushi %0\n"
+ : /* Outputs */
+ : /* Inputs */ "r"(addr)
+ /* : No clobber */);
+ }
+ __asm__ __volatile(" flushp\n");
+}
+
+static void flush_aliases(struct address_space *mapping, struct page *page)
+{
+ struct mm_struct *mm = current->active_mm;
+ struct vm_area_struct *mpnt;
+ pgoff_t pgoff;
+
+ pgoff = page->index;
+
+ flush_dcache_mmap_lock(mapping);
+ vma_interval_tree_foreach(mpnt, &mapping->i_mmap, pgoff, pgoff) {
+ unsigned long offset;
+
+ if (mpnt->vm_mm != mm)
+ continue;
+ if (!(mpnt->vm_flags & VM_MAYSHARE))
+ continue;
+
+ offset = (pgoff - mpnt->vm_pgoff) << PAGE_SHIFT;
+ flush_cache_page(mpnt, mpnt->vm_start + offset,
+ page_to_pfn(page));
+ }
+ flush_dcache_mmap_unlock(mapping);
+}
+
+void flush_cache_all(void)
+{
+ __flush_dcache_all(0, cpuinfo.dcache_size);
+ __flush_icache(0, cpuinfo.icache_size);
+}
+
+void flush_cache_mm(struct mm_struct *mm)
+{
+ flush_cache_all();
+}
+
+void flush_cache_dup_mm(struct mm_struct *mm)
+{
+ flush_cache_all();
+}
+
+void flush_icache_range(unsigned long start, unsigned long end)
+{
+ __flush_icache(start, end);
+}
+
+void flush_dcache_range(unsigned long start, unsigned long end)
+{
+ __flush_dcache(start, end);
+}
+EXPORT_SYMBOL(flush_dcache_range);
+
+void invalidate_dcache_range(unsigned long start, unsigned long end)
+{
+ __invalidate_dcache(start, end);
+}
+EXPORT_SYMBOL(invalidate_dcache_range);
+
+void flush_cache_range(struct vm_area_struct *vma, unsigned long start,
+ unsigned long end)
+{
+ __flush_dcache(start, end);
+ if (vma == NULL || (vma->vm_flags & VM_EXEC))
+ __flush_icache(start, end);
+}
+
+void flush_icache_page(struct vm_area_struct *vma, struct page *page)
+{
+ unsigned long start = (unsigned long) page_address(page);
+ unsigned long end = start + PAGE_SIZE;
+
+ __flush_icache(start, end);
+}
+
+void flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr,
+ unsigned long pfn)
+{
+ unsigned long start = vmaddr;
+ unsigned long end = start + PAGE_SIZE;
+
+ __flush_dcache(start, end);
+ if (vma->vm_flags & VM_EXEC)
+ __flush_icache(start, end);
+}
+
+void flush_dcache_page(struct page *page)
+{
+ struct address_space *mapping;
+
+ /*
+ * The zero page is never written to, so never has any dirty
+ * cache lines, and therefore never needs to be flushed.
+ */
+ if (page == ZERO_PAGE(0))
+ return;
+
+ mapping = page_mapping(page);
+
+ /* Flush this page if there are aliases. */
+ if (mapping && !mapping_mapped(mapping)) {
+ clear_bit(PG_dcache_clean, &page->flags);
+ } else {
+ unsigned long start = (unsigned long)page_address(page);
+
+ __flush_dcache_all(start, start + PAGE_SIZE);
+ if (mapping)
+ flush_aliases(mapping, page);
+ set_bit(PG_dcache_clean, &page->flags);
+ }
+}
+EXPORT_SYMBOL(flush_dcache_page);
+
+void update_mmu_cache(struct vm_area_struct *vma,
+ unsigned long address, pte_t *pte)
+{
+ unsigned long pfn = pte_pfn(*pte);
+ struct page *page;
+
+ if (!pfn_valid(pfn))
+ return;
+
+ /*
+ * The zero page is never written to, so never has any dirty
+ * cache lines, and therefore never needs to be flushed.
+ */
+ page = pfn_to_page(pfn);
+ if (page == ZERO_PAGE(0))
+ return;
+
+ if (!PageReserved(page) &&
+ !test_and_set_bit(PG_dcache_clean, &page->flags)) {
+ unsigned long start = page_to_virt(page);
+ struct address_space *mapping;
+
+ __flush_dcache(start, start + PAGE_SIZE);
+
+ mapping = page_mapping(page);
+ if (mapping)
+ flush_aliases(mapping, page);
+ }
+}
+
+void copy_user_page(void *vto, void *vfrom, unsigned long vaddr,
+ struct page *to)
+{
+ __flush_dcache(vaddr, vaddr + PAGE_SIZE);
+ copy_page(vto, vfrom);
+ __flush_dcache((unsigned long)vto, (unsigned long)vto + PAGE_SIZE);
+}
+
+void clear_user_page(void *addr, unsigned long vaddr, struct page *page)
+{
+ __flush_dcache(vaddr, vaddr + PAGE_SIZE);
+ clear_page(addr);
+ __flush_dcache((unsigned long)addr, (unsigned long)addr + PAGE_SIZE);
+}
+
+void copy_from_user_page(struct vm_area_struct *vma, struct page *page,
+ unsigned long user_vaddr,
+ void *dst, void *src, int len)
+{
+ flush_cache_page(vma, user_vaddr, page_to_pfn(page));
+ memcpy(dst, src, len);
+ __flush_dcache((unsigned long)src, (unsigned long)src + len);
+ if (vma->vm_flags & VM_EXEC)
+ __flush_icache((unsigned long)src, (unsigned long)src + len);
+}
+
+void copy_to_user_page(struct vm_area_struct *vma, struct page *page,
+ unsigned long user_vaddr,
+ void *dst, void *src, int len)
+{
+ flush_cache_page(vma, user_vaddr, page_to_pfn(page));
+ memcpy(dst, src, len);
+ __flush_dcache((unsigned long)dst, (unsigned long)dst + len);
+ if (vma->vm_flags & VM_EXEC)
+ __flush_icache((unsigned long)dst, (unsigned long)dst + len);
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2009 Wind River Systems Inc
+ * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com
+ *
+ * Based on DMA code from MIPS.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/export.h>
+#include <linux/string.h>
+#include <linux/scatterlist.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/cache.h>
+#include <asm/cacheflush.h>
+
+
+void *dma_alloc_coherent(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, gfp_t gfp)
+{
+ void *ret;
+
+ /* ignore region specifiers */
+ gfp &= ~(__GFP_DMA | __GFP_HIGHMEM);
+
+ /* optimized page clearing */
+ gfp |= __GFP_ZERO;
+
+ if (dev == NULL || (dev->coherent_dma_mask < 0xffffffff))
+ gfp |= GFP_DMA;
+
+ ret = (void *) __get_free_pages(gfp, get_order(size));
+ if (ret != NULL) {
+ *dma_handle = virt_to_phys(ret);
+ flush_dcache_range((unsigned long) ret,
+ (unsigned long) ret + size);
+ ret = UNCAC_ADDR(ret);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(dma_alloc_coherent);
+
+void dma_free_coherent(struct device *dev, size_t size, void *vaddr,
+ dma_addr_t dma_handle)
+{
+ unsigned long addr = (unsigned long) CAC_ADDR((unsigned long) vaddr);
+
+ free_pages(addr, get_order(size));
+}
+EXPORT_SYMBOL(dma_free_coherent);
+
+int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
+ enum dma_data_direction direction)
+{
+ int i;
+
+ BUG_ON(!valid_dma_direction(direction));
+
+ for_each_sg(sg, sg, nents, i) {
+ void *addr;
+
+ addr = sg_virt(sg);
+ if (addr) {
+ __dma_sync_for_device(addr, sg->length, direction);
+ sg->dma_address = sg_phys(sg);
+ }
+ }
+
+ return nents;
+}
+EXPORT_SYMBOL(dma_map_sg);
+
+dma_addr_t dma_map_page(struct device *dev, struct page *page,
+ unsigned long offset, size_t size,
+ enum dma_data_direction direction)
+{
+ void *addr;
+
+ BUG_ON(!valid_dma_direction(direction));
+
+ addr = page_address(page) + offset;
+ __dma_sync_for_device(addr, size, direction);
+
+ return page_to_phys(page) + offset;
+}
+EXPORT_SYMBOL(dma_map_page);
+
+void dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size,
+ enum dma_data_direction direction)
+{
+ BUG_ON(!valid_dma_direction(direction));
+
+ __dma_sync_for_cpu(phys_to_virt(dma_address), size, direction);
+}
+EXPORT_SYMBOL(dma_unmap_page);
+
+void dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nhwentries,
+ enum dma_data_direction direction)
+{
+ void *addr;
+ int i;
+
+ BUG_ON(!valid_dma_direction(direction));
+
+ if (direction == DMA_TO_DEVICE)
+ return;
+
+ for_each_sg(sg, sg, nhwentries, i) {
+ addr = sg_virt(sg);
+ if (addr)
+ __dma_sync_for_cpu(addr, sg->length, direction);
+ }
+}
+EXPORT_SYMBOL(dma_unmap_sg);
+
+void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle,
+ size_t size, enum dma_data_direction direction)
+{
+ BUG_ON(!valid_dma_direction(direction));
+
+ __dma_sync_for_cpu(phys_to_virt(dma_handle), size, direction);
+}
+EXPORT_SYMBOL(dma_sync_single_for_cpu);
+
+void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle,
+ size_t size, enum dma_data_direction direction)
+{
+ BUG_ON(!valid_dma_direction(direction));
+
+ __dma_sync_for_device(phys_to_virt(dma_handle), size, direction);
+}
+EXPORT_SYMBOL(dma_sync_single_for_device);
+
+void dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle,
+ unsigned long offset, size_t size,
+ enum dma_data_direction direction)
+{
+ BUG_ON(!valid_dma_direction(direction));
+
+ __dma_sync_for_cpu(phys_to_virt(dma_handle), size, direction);
+}
+EXPORT_SYMBOL(dma_sync_single_range_for_cpu);
+
+void dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle,
+ unsigned long offset, size_t size,
+ enum dma_data_direction direction)
+{
+ BUG_ON(!valid_dma_direction(direction));
+
+ __dma_sync_for_device(phys_to_virt(dma_handle), size, direction);
+}
+EXPORT_SYMBOL(dma_sync_single_range_for_device);
+
+void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems,
+ enum dma_data_direction direction)
+{
+ int i;
+
+ BUG_ON(!valid_dma_direction(direction));
+
+ /* Make sure that gcc doesn't leave the empty loop body. */
+ for_each_sg(sg, sg, nelems, i)
+ __dma_sync_for_cpu(sg_virt(sg), sg->length, direction);
+}
+EXPORT_SYMBOL(dma_sync_sg_for_cpu);
+
+void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
+ int nelems, enum dma_data_direction direction)
+{
+ int i;
+
+ BUG_ON(!valid_dma_direction(direction));
+
+ /* Make sure that gcc doesn't leave the empty loop body. */
+ for_each_sg(sg, sg, nelems, i)
+ __dma_sync_for_device(sg_virt(sg), sg->length, direction);
+
+}
+EXPORT_SYMBOL(dma_sync_sg_for_device);
--- /dev/null
+/*
+ * Copyright (C) 2010, Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2009, Wind River Systems Inc
+ * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/uaccess.h>
+
+int fixup_exception(struct pt_regs *regs)
+{
+ const struct exception_table_entry *fixup;
+
+ fixup = search_exception_tables(regs->ea);
+ if (fixup) {
+ regs->ea = fixup->fixup;
+ return 1;
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2009 Wind River Systems Inc
+ * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com
+ *
+ * based on arch/mips/mm/fault.c which is:
+ *
+ * Copyright (C) 1995-2000 Ralf Baechle
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/ptrace.h>
+
+#include <asm/mmu_context.h>
+#include <asm/traps.h>
+
+#define EXC_SUPERV_INSN_ACCESS 9 /* Supervisor only instruction address */
+#define EXC_SUPERV_DATA_ACCESS 11 /* Supervisor only data address */
+#define EXC_X_PROTECTION_FAULT 13 /* TLB permission violation (x) */
+#define EXC_R_PROTECTION_FAULT 14 /* TLB permission violation (r) */
+#define EXC_W_PROTECTION_FAULT 15 /* TLB permission violation (w) */
+
+/*
+ * This routine handles page faults. It determines the address,
+ * and the problem, and then passes it off to one of the appropriate
+ * routines.
+ */
+asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long cause,
+ unsigned long address)
+{
+ struct vm_area_struct *vma = NULL;
+ struct task_struct *tsk = current;
+ struct mm_struct *mm = tsk->mm;
+ int code = SEGV_MAPERR;
+ int fault;
+ unsigned int flags = 0;
+
+ cause >>= 2;
+
+ /* Restart the instruction */
+ regs->ea -= 4;
+
+ /*
+ * We fault-in kernel-space virtual memory on-demand. The
+ * 'reference' page table is init_mm.pgd.
+ *
+ * NOTE! We MUST NOT take any locks for this case. We may
+ * be in an interrupt or a critical region, and should
+ * only copy the information from the master page table,
+ * nothing more.
+ */
+ if (unlikely(address >= VMALLOC_START && address <= VMALLOC_END)) {
+ if (user_mode(regs))
+ goto bad_area_nosemaphore;
+ else
+ goto vmalloc_fault;
+ }
+
+ if (unlikely(address >= TASK_SIZE))
+ goto bad_area_nosemaphore;
+
+ /*
+ * If we're in an interrupt or have no user
+ * context, we must not take the fault..
+ */
+ if (in_atomic() || !mm)
+ goto bad_area_nosemaphore;
+
+ if (user_mode(regs))
+ flags |= FAULT_FLAG_USER;
+
+ if (!down_read_trylock(&mm->mmap_sem)) {
+ if (!user_mode(regs) && !search_exception_tables(regs->ea))
+ goto bad_area_nosemaphore;
+ down_read(&mm->mmap_sem);
+ }
+
+ vma = find_vma(mm, address);
+ if (!vma)
+ goto bad_area;
+ if (vma->vm_start <= address)
+ goto good_area;
+ if (!(vma->vm_flags & VM_GROWSDOWN))
+ goto bad_area;
+ if (expand_stack(vma, address))
+ goto bad_area;
+/*
+ * Ok, we have a good vm_area for this memory access, so
+ * we can handle it..
+ */
+good_area:
+ code = SEGV_ACCERR;
+
+ switch (cause) {
+ case EXC_SUPERV_INSN_ACCESS:
+ goto bad_area;
+ case EXC_SUPERV_DATA_ACCESS:
+ goto bad_area;
+ case EXC_X_PROTECTION_FAULT:
+ if (!(vma->vm_flags & VM_EXEC))
+ goto bad_area;
+ break;
+ case EXC_R_PROTECTION_FAULT:
+ if (!(vma->vm_flags & VM_READ))
+ goto bad_area;
+ break;
+ case EXC_W_PROTECTION_FAULT:
+ if (!(vma->vm_flags & VM_WRITE))
+ goto bad_area;
+ flags = FAULT_FLAG_WRITE;
+ break;
+ }
+
+survive:
+ /*
+ * If for any reason at all we couldn't handle the fault,
+ * make sure we exit gracefully rather than endlessly redo
+ * the fault.
+ */
+ fault = handle_mm_fault(mm, vma, address, flags);
+ if (unlikely(fault & VM_FAULT_ERROR)) {
+ if (fault & VM_FAULT_OOM)
+ goto out_of_memory;
+ else if (fault & VM_FAULT_SIGBUS)
+ goto do_sigbus;
+ BUG();
+ }
+ if (fault & VM_FAULT_MAJOR)
+ tsk->maj_flt++;
+ else
+ tsk->min_flt++;
+
+ up_read(&mm->mmap_sem);
+ return;
+
+/*
+ * Something tried to access memory that isn't in our memory map..
+ * Fix it, but check if it's kernel or user first..
+ */
+bad_area:
+ up_read(&mm->mmap_sem);
+
+bad_area_nosemaphore:
+ /* User mode accesses just cause a SIGSEGV */
+ if (user_mode(regs)) {
+ pr_alert("%s: unhandled page fault (%d) at 0x%08lx, "
+ "cause %ld\n", current->comm, SIGSEGV, address, cause);
+ show_regs(regs);
+ _exception(SIGSEGV, regs, code, address);
+ return;
+ }
+
+no_context:
+ /* Are we prepared to handle this kernel fault? */
+ if (fixup_exception(regs))
+ return;
+
+ /*
+ * Oops. The kernel tried to access some bad page. We'll have to
+ * terminate things with extreme prejudice.
+ */
+ bust_spinlocks(1);
+
+ pr_alert("Unable to handle kernel %s at virtual address %08lx",
+ address < PAGE_SIZE ? "NULL pointer dereference" :
+ "paging request", address);
+ pr_alert("ea = %08lx, ra = %08lx, cause = %ld\n", regs->ea, regs->ra,
+ cause);
+ panic("Oops");
+ return;
+
+/*
+ * We ran out of memory, or some other thing happened to us that made
+ * us unable to handle the page fault gracefully.
+ */
+out_of_memory:
+ up_read(&mm->mmap_sem);
+ if (is_global_init(tsk)) {
+ yield();
+ down_read(&mm->mmap_sem);
+ goto survive;
+ }
+ if (!user_mode(regs))
+ goto no_context;
+ pagefault_out_of_memory();
+ return;
+
+do_sigbus:
+ up_read(&mm->mmap_sem);
+
+ /* Kernel mode? Handle exceptions or die */
+ if (!user_mode(regs))
+ goto no_context;
+
+ _exception(SIGBUS, regs, BUS_ADRERR, address);
+ return;
+
+vmalloc_fault:
+ {
+ /*
+ * Synchronize this task's top level page-table
+ * with the 'reference' page table.
+ *
+ * Do _not_ use "tsk" here. We might be inside
+ * an interrupt in the middle of a task switch..
+ */
+ int offset = pgd_index(address);
+ pgd_t *pgd, *pgd_k;
+ pud_t *pud, *pud_k;
+ pmd_t *pmd, *pmd_k;
+ pte_t *pte_k;
+
+ pgd = pgd_current + offset;
+ pgd_k = init_mm.pgd + offset;
+
+ if (!pgd_present(*pgd_k))
+ goto no_context;
+ set_pgd(pgd, *pgd_k);
+
+ pud = pud_offset(pgd, address);
+ pud_k = pud_offset(pgd_k, address);
+ if (!pud_present(*pud_k))
+ goto no_context;
+ pmd = pmd_offset(pud, address);
+ pmd_k = pmd_offset(pud_k, address);
+ if (!pmd_present(*pmd_k))
+ goto no_context;
+ set_pmd(pmd, *pmd_k);
+
+ pte_k = pte_offset_kernel(pmd_k, address);
+ if (!pte_present(*pte_k))
+ goto no_context;
+
+ flush_tlb_one(address);
+ return;
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2013 Altera Corporation
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2009 Wind River Systems Inc
+ * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com
+ * Copyright (C) 2004 Microtronix Datacom Ltd
+ *
+ * based on arch/m68k/mm/init.c
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/pagemap.h>
+#include <linux/bootmem.h>
+#include <linux/slab.h>
+#include <linux/binfmts.h>
+
+#include <asm/setup.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/sections.h>
+#include <asm/tlb.h>
+#include <asm/mmu_context.h>
+#include <asm/cpuinfo.h>
+#include <asm/processor.h>
+
+pgd_t *pgd_current;
+
+/*
+ * paging_init() continues the virtual memory environment setup which
+ * was begun by the code in arch/head.S.
+ * The parameters are pointers to where to stick the starting and ending
+ * addresses of available kernel virtual memory.
+ */
+void __init paging_init(void)
+{
+ unsigned long zones_size[MAX_NR_ZONES];
+
+ memset(zones_size, 0, sizeof(zones_size));
+
+ pagetable_init();
+ pgd_current = swapper_pg_dir;
+
+ zones_size[ZONE_NORMAL] = max_mapnr;
+
+ /* pass the memory from the bootmem allocator to the main allocator */
+ free_area_init(zones_size);
+
+ flush_dcache_range((unsigned long)empty_zero_page,
+ (unsigned long)empty_zero_page + PAGE_SIZE);
+}
+
+void __init mem_init(void)
+{
+ unsigned long end_mem = memory_end; /* this must not include
+ kernel stack at top */
+
+ pr_debug("mem_init: start=%lx, end=%lx\n", memory_start, memory_end);
+
+ end_mem &= PAGE_MASK;
+ high_memory = __va(end_mem);
+
+ /* this will put all memory onto the freelists */
+ free_all_bootmem();
+ mem_init_print_info(NULL);
+}
+
+void __init mmu_init(void)
+{
+ flush_tlb_all();
+}
+
+#ifdef CONFIG_BLK_DEV_INITRD
+void __init free_initrd_mem(unsigned long start, unsigned long end)
+{
+ free_reserved_area((void *)start, (void *)end, -1, "initrd");
+}
+#endif
+
+void __init_refok free_initmem(void)
+{
+ free_initmem_default(-1);
+}
+
+#define __page_aligned(order) __aligned(PAGE_SIZE << (order))
+pgd_t swapper_pg_dir[PTRS_PER_PGD] __page_aligned(PGD_ORDER);
+pte_t invalid_pte_table[PTRS_PER_PTE] __page_aligned(PTE_ORDER);
+static struct page *kuser_page[1];
+
+static int alloc_kuser_page(void)
+{
+ extern char __kuser_helper_start[], __kuser_helper_end[];
+ int kuser_sz = __kuser_helper_end - __kuser_helper_start;
+ unsigned long vpage;
+
+ vpage = get_zeroed_page(GFP_ATOMIC);
+ if (!vpage)
+ return -ENOMEM;
+
+ /* Copy kuser helpers */
+ memcpy((void *)vpage, __kuser_helper_start, kuser_sz);
+
+ flush_icache_range(vpage, vpage + KUSER_SIZE);
+ kuser_page[0] = virt_to_page(vpage);
+
+ return 0;
+}
+arch_initcall(alloc_kuser_page);
+
+int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
+{
+ struct mm_struct *mm = current->mm;
+ int ret;
+
+ down_write(&mm->mmap_sem);
+
+ /* Map kuser helpers to user space address */
+ ret = install_special_mapping(mm, KUSER_BASE, KUSER_SIZE,
+ VM_READ | VM_EXEC | VM_MAYREAD |
+ VM_MAYEXEC, kuser_page);
+
+ up_write(&mm->mmap_sem);
+
+ return ret;
+}
+
+const char *arch_vma_name(struct vm_area_struct *vma)
+{
+ return (vma->vm_start == KUSER_BASE) ? "[kuser]" : NULL;
+}
--- /dev/null
+/*
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2009 Wind River Systems Inc
+ * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com
+ * Copyright (C) 2004 Microtronix Datacom Ltd.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/export.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/io.h>
+
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+
+static inline void remap_area_pte(pte_t *pte, unsigned long address,
+ unsigned long size, unsigned long phys_addr,
+ unsigned long flags)
+{
+ unsigned long end;
+ unsigned long pfn;
+ pgprot_t pgprot = __pgprot(_PAGE_GLOBAL | _PAGE_PRESENT | _PAGE_READ
+ | _PAGE_WRITE | flags);
+
+ address &= ~PMD_MASK;
+ end = address + size;
+ if (end > PMD_SIZE)
+ end = PMD_SIZE;
+ if (address >= end)
+ BUG();
+ pfn = PFN_DOWN(phys_addr);
+ do {
+ if (!pte_none(*pte)) {
+ pr_err("remap_area_pte: page already exists\n");
+ BUG();
+ }
+ set_pte(pte, pfn_pte(pfn, pgprot));
+ address += PAGE_SIZE;
+ pfn++;
+ pte++;
+ } while (address && (address < end));
+}
+
+static inline int remap_area_pmd(pmd_t *pmd, unsigned long address,
+ unsigned long size, unsigned long phys_addr,
+ unsigned long flags)
+{
+ unsigned long end;
+
+ address &= ~PGDIR_MASK;
+ end = address + size;
+ if (end > PGDIR_SIZE)
+ end = PGDIR_SIZE;
+ phys_addr -= address;
+ if (address >= end)
+ BUG();
+ do {
+ pte_t *pte = pte_alloc_kernel(pmd, address);
+
+ if (!pte)
+ return -ENOMEM;
+ remap_area_pte(pte, address, end - address, address + phys_addr,
+ flags);
+ address = (address + PMD_SIZE) & PMD_MASK;
+ pmd++;
+ } while (address && (address < end));
+ return 0;
+}
+
+static int remap_area_pages(unsigned long address, unsigned long phys_addr,
+ unsigned long size, unsigned long flags)
+{
+ int error;
+ pgd_t *dir;
+ unsigned long end = address + size;
+
+ phys_addr -= address;
+ dir = pgd_offset(&init_mm, address);
+ flush_cache_all();
+ if (address >= end)
+ BUG();
+ do {
+ pud_t *pud;
+ pmd_t *pmd;
+
+ error = -ENOMEM;
+ pud = pud_alloc(&init_mm, dir, address);
+ if (!pud)
+ break;
+ pmd = pmd_alloc(&init_mm, pud, address);
+ if (!pmd)
+ break;
+ if (remap_area_pmd(pmd, address, end - address,
+ phys_addr + address, flags))
+ break;
+ error = 0;
+ address = (address + PGDIR_SIZE) & PGDIR_MASK;
+ dir++;
+ } while (address && (address < end));
+ flush_tlb_all();
+ return error;
+}
+
+#define IS_MAPPABLE_UNCACHEABLE(addr) (addr < 0x20000000UL)
+
+/*
+ * Map some physical address range into the kernel address space.
+ */
+void __iomem *__ioremap(unsigned long phys_addr, unsigned long size,
+ unsigned long cacheflag)
+{
+ struct vm_struct *area;
+ unsigned long offset;
+ unsigned long last_addr;
+ void *addr;
+
+ /* Don't allow wraparound or zero size */
+ last_addr = phys_addr + size - 1;
+
+ if (!size || last_addr < phys_addr)
+ return NULL;
+
+ /* Don't allow anybody to remap normal RAM that we're using */
+ if (phys_addr > PHYS_OFFSET && phys_addr < virt_to_phys(high_memory)) {
+ char *t_addr, *t_end;
+ struct page *page;
+
+ t_addr = __va(phys_addr);
+ t_end = t_addr + (size - 1);
+ for (page = virt_to_page(t_addr);
+ page <= virt_to_page(t_end); page++)
+ if (!PageReserved(page))
+ return NULL;
+ }
+
+ /*
+ * Map uncached objects in the low part of address space to
+ * CONFIG_NIOS2_IO_REGION_BASE
+ */
+ if (IS_MAPPABLE_UNCACHEABLE(phys_addr) &&
+ IS_MAPPABLE_UNCACHEABLE(last_addr) &&
+ !(cacheflag & _PAGE_CACHED))
+ return (void __iomem *)(CONFIG_NIOS2_IO_REGION_BASE + phys_addr);
+
+ /* Mappings have to be page-aligned */
+ offset = phys_addr & ~PAGE_MASK;
+ phys_addr &= PAGE_MASK;
+ size = PAGE_ALIGN(last_addr + 1) - phys_addr;
+
+ /* Ok, go for it */
+ area = get_vm_area(size, VM_IOREMAP);
+ if (!area)
+ return NULL;
+ addr = area->addr;
+ if (remap_area_pages((unsigned long) addr, phys_addr, size,
+ cacheflag)) {
+ vunmap(addr);
+ return NULL;
+ }
+ return (void __iomem *) (offset + (char *)addr);
+}
+EXPORT_SYMBOL(__ioremap);
+
+/*
+ * __iounmap unmaps nearly everything, so be careful
+ * it doesn't free currently pointer/page tables anymore but it
+ * wasn't used anyway and might be added later.
+ */
+void __iounmap(void __iomem *addr)
+{
+ struct vm_struct *p;
+
+ if ((unsigned long) addr > CONFIG_NIOS2_IO_REGION_BASE)
+ return;
+
+ p = remove_vm_area((void *) (PAGE_MASK & (unsigned long __force) addr));
+ if (!p)
+ pr_err("iounmap: bad address %p\n", addr);
+ kfree(p);
+}
+EXPORT_SYMBOL(__iounmap);
--- /dev/null
+/*
+ * MMU context handling.
+ *
+ * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2009 Wind River Systems Inc
+ * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/mm.h>
+
+#include <asm/cpuinfo.h>
+#include <asm/mmu_context.h>
+#include <asm/tlb.h>
+
+/* The pids position and mask in context */
+#define PID_SHIFT 0
+#define PID_BITS (cpuinfo.tlb_pid_num_bits)
+#define PID_MASK ((1UL << PID_BITS) - 1)
+
+/* The versions position and mask in context */
+#define VERSION_BITS (32 - PID_BITS)
+#define VERSION_SHIFT (PID_SHIFT + PID_BITS)
+#define VERSION_MASK ((1UL << VERSION_BITS) - 1)
+
+/* Return the version part of a context */
+#define CTX_VERSION(c) (((c) >> VERSION_SHIFT) & VERSION_MASK)
+
+/* Return the pid part of a context */
+#define CTX_PID(c) (((c) >> PID_SHIFT) & PID_MASK)
+
+/* Value of the first context (version 1, pid 0) */
+#define FIRST_CTX ((1UL << VERSION_SHIFT) | (0 << PID_SHIFT))
+
+static mm_context_t next_mmu_context;
+
+/*
+ * Initialize MMU context management stuff.
+ */
+void __init mmu_context_init(void)
+{
+ /* We need to set this here because the value depends on runtime data
+ * from cpuinfo */
+ next_mmu_context = FIRST_CTX;
+}
+
+/*
+ * Set new context (pid), keep way
+ */
+static void set_context(mm_context_t context)
+{
+ set_mmu_pid(CTX_PID(context));
+}
+
+static mm_context_t get_new_context(void)
+{
+ /* Return the next pid */
+ next_mmu_context += (1UL << PID_SHIFT);
+
+ /* If the pid field wraps around we increase the version and
+ * flush the tlb */
+ if (unlikely(CTX_PID(next_mmu_context) == 0)) {
+ /* Version is incremented since the pid increment above
+ * overflows info version */
+ flush_cache_all();
+ flush_tlb_all();
+ }
+
+ /* If the version wraps we start over with the first generation, we do
+ * not need to flush the tlb here since it's always done above */
+ if (unlikely(CTX_VERSION(next_mmu_context) == 0))
+ next_mmu_context = FIRST_CTX;
+
+ return next_mmu_context;
+}
+
+void switch_mm(struct mm_struct *prev, struct mm_struct *next,
+ struct task_struct *tsk)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ /* If the process context we are swapping in has a different context
+ * generation then we have it should get a new generation/pid */
+ if (unlikely(CTX_VERSION(next->context) !=
+ CTX_VERSION(next_mmu_context)))
+ next->context = get_new_context();
+
+ /* Save the current pgd so the fast tlb handler can find it */
+ pgd_current = next->pgd;
+
+ /* Set the current context */
+ set_context(next->context);
+
+ local_irq_restore(flags);
+}
+
+/*
+ * After we have set current->mm to a new value, this activates
+ * the context for the new mm so we see the new mappings.
+ */
+void activate_mm(struct mm_struct *prev, struct mm_struct *next)
+{
+ next->context = get_new_context();
+ set_context(next->context);
+ pgd_current = next->pgd;
+}
+
+unsigned long get_pid_from_context(mm_context_t *context)
+{
+ return CTX_PID((*context));
+}
--- /dev/null
+/*
+ * Copyright (C) 2009 Wind River Systems Inc
+ * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/mm.h>
+#include <linux/sched.h>
+
+#include <asm/pgtable.h>
+#include <asm/cpuinfo.h>
+
+/* pteaddr:
+ * ptbase | vpn* | zero
+ * 31-22 | 21-2 | 1-0
+ *
+ * *vpn is preserved on double fault
+ *
+ * tlbacc:
+ * IG |*flags| pfn
+ * 31-25|24-20 | 19-0
+ *
+ * *crwxg
+ *
+ * tlbmisc:
+ * resv |way |rd | we|pid |dbl|bad|perm|d
+ * 31-24 |23-20 |19 | 20|17-4|3 |2 |1 |0
+ *
+ */
+
+/*
+ * Initialize a new pgd / pmd table with invalid pointers.
+ */
+static void pgd_init(pgd_t *pgd)
+{
+ unsigned long *p = (unsigned long *) pgd;
+ int i;
+
+ for (i = 0; i < USER_PTRS_PER_PGD; i += 8) {
+ p[i + 0] = (unsigned long) invalid_pte_table;
+ p[i + 1] = (unsigned long) invalid_pte_table;
+ p[i + 2] = (unsigned long) invalid_pte_table;
+ p[i + 3] = (unsigned long) invalid_pte_table;
+ p[i + 4] = (unsigned long) invalid_pte_table;
+ p[i + 5] = (unsigned long) invalid_pte_table;
+ p[i + 6] = (unsigned long) invalid_pte_table;
+ p[i + 7] = (unsigned long) invalid_pte_table;
+ }
+}
+
+pgd_t *pgd_alloc(struct mm_struct *mm)
+{
+ pgd_t *ret, *init;
+
+ ret = (pgd_t *) __get_free_pages(GFP_KERNEL, PGD_ORDER);
+ if (ret) {
+ init = pgd_offset(&init_mm, 0UL);
+ pgd_init(ret);
+ memcpy(ret + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD,
+ (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
+ }
+
+ return ret;
+}
+
+void __init pagetable_init(void)
+{
+ /* Initialize the entire pgd. */
+ pgd_init(swapper_pg_dir);
+ pgd_init(swapper_pg_dir + USER_PTRS_PER_PGD);
+}
--- /dev/null
+/*
+ * Nios2 TLB handling
+ *
+ * Copyright (C) 2009, Wind River Systems Inc
+ * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+
+#include <asm/tlb.h>
+#include <asm/mmu_context.h>
+#include <asm/pgtable.h>
+#include <asm/cpuinfo.h>
+
+#define TLB_INDEX_MASK \
+ ((((1UL << (cpuinfo.tlb_ptr_sz - cpuinfo.tlb_num_ways_log2))) - 1) \
+ << PAGE_SHIFT)
+
+/* Used as illegal PHYS_ADDR for TLB mappings
+ */
+#define MAX_PHYS_ADDR 0
+
+static void get_misc_and_pid(unsigned long *misc, unsigned long *pid)
+{
+ *misc = RDCTL(CTL_TLBMISC);
+ *misc &= (TLBMISC_PID | TLBMISC_WAY);
+ *pid = *misc & TLBMISC_PID;
+}
+
+/*
+ * All entries common to a mm share an asid. To effectively flush these
+ * entries, we just bump the asid.
+ */
+void flush_tlb_mm(struct mm_struct *mm)
+{
+ if (current->mm == mm)
+ flush_tlb_all();
+ else
+ memset(&mm->context, 0, sizeof(mm_context_t));
+}
+
+/*
+ * This one is only used for pages with the global bit set so we don't care
+ * much about the ASID.
+ */
+void flush_tlb_one_pid(unsigned long addr, unsigned long mmu_pid)
+{
+ unsigned int way;
+ unsigned long org_misc, pid_misc;
+
+ pr_debug("Flush tlb-entry for vaddr=%#lx\n", addr);
+
+ /* remember pid/way until we return. */
+ get_misc_and_pid(&org_misc, &pid_misc);
+
+ WRCTL(CTL_PTEADDR, (addr >> PAGE_SHIFT) << 2);
+
+ for (way = 0; way < cpuinfo.tlb_num_ways; way++) {
+ unsigned long pteaddr;
+ unsigned long tlbmisc;
+ unsigned long pid;
+
+ tlbmisc = pid_misc | TLBMISC_RD | (way << TLBMISC_WAY_SHIFT);
+ WRCTL(CTL_TLBMISC, tlbmisc);
+ pteaddr = RDCTL(CTL_PTEADDR);
+ tlbmisc = RDCTL(CTL_TLBMISC);
+ pid = (tlbmisc >> TLBMISC_PID_SHIFT) & TLBMISC_PID_MASK;
+ if (((((pteaddr >> 2) & 0xfffff)) == (addr >> PAGE_SHIFT)) &&
+ pid == mmu_pid) {
+ unsigned long vaddr = CONFIG_NIOS2_IO_REGION_BASE +
+ ((PAGE_SIZE * cpuinfo.tlb_num_lines) * way) +
+ (addr & TLB_INDEX_MASK);
+ pr_debug("Flush entry by writing %#lx way=%dl pid=%ld\n",
+ vaddr, way, (pid_misc >> TLBMISC_PID_SHIFT));
+
+ WRCTL(CTL_PTEADDR, (vaddr >> 12) << 2);
+ tlbmisc = pid_misc | TLBMISC_WE |
+ (way << TLBMISC_WAY_SHIFT);
+ WRCTL(CTL_TLBMISC, tlbmisc);
+ WRCTL(CTL_TLBACC, (MAX_PHYS_ADDR >> PAGE_SHIFT));
+ }
+ }
+
+ WRCTL(CTL_TLBMISC, org_misc);
+}
+
+void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
+ unsigned long end)
+{
+ unsigned long mmu_pid = get_pid_from_context(&vma->vm_mm->context);
+
+ while (start < end) {
+ flush_tlb_one_pid(start, mmu_pid);
+ start += PAGE_SIZE;
+ }
+}
+
+void flush_tlb_kernel_range(unsigned long start, unsigned long end)
+{
+ while (start < end) {
+ flush_tlb_one(start);
+ start += PAGE_SIZE;
+ }
+}
+
+/*
+ * This one is only used for pages with the global bit set so we don't care
+ * much about the ASID.
+ */
+void flush_tlb_one(unsigned long addr)
+{
+ unsigned int way;
+ unsigned long org_misc, pid_misc;
+
+ pr_debug("Flush tlb-entry for vaddr=%#lx\n", addr);
+
+ /* remember pid/way until we return. */
+ get_misc_and_pid(&org_misc, &pid_misc);
+
+ WRCTL(CTL_PTEADDR, (addr >> PAGE_SHIFT) << 2);
+
+ for (way = 0; way < cpuinfo.tlb_num_ways; way++) {
+ unsigned long pteaddr;
+ unsigned long tlbmisc;
+
+ tlbmisc = pid_misc | TLBMISC_RD | (way << TLBMISC_WAY_SHIFT);
+ WRCTL(CTL_TLBMISC, tlbmisc);
+ pteaddr = RDCTL(CTL_PTEADDR);
+ tlbmisc = RDCTL(CTL_TLBMISC);
+
+ if ((((pteaddr >> 2) & 0xfffff)) == (addr >> PAGE_SHIFT)) {
+ unsigned long vaddr = CONFIG_NIOS2_IO_REGION_BASE +
+ ((PAGE_SIZE * cpuinfo.tlb_num_lines) * way) +
+ (addr & TLB_INDEX_MASK);
+
+ pr_debug("Flush entry by writing %#lx way=%dl pid=%ld\n",
+ vaddr, way, (pid_misc >> TLBMISC_PID_SHIFT));
+
+ tlbmisc = pid_misc | TLBMISC_WE |
+ (way << TLBMISC_WAY_SHIFT);
+ WRCTL(CTL_PTEADDR, (vaddr >> 12) << 2);
+ WRCTL(CTL_TLBMISC, tlbmisc);
+ WRCTL(CTL_TLBACC, (MAX_PHYS_ADDR >> PAGE_SHIFT));
+ }
+ }
+
+ WRCTL(CTL_TLBMISC, org_misc);
+}
+
+void dump_tlb_line(unsigned long line)
+{
+ unsigned int way;
+ unsigned long org_misc;
+
+ pr_debug("dump tlb-entries for line=%#lx (addr %08lx)\n", line,
+ line << (PAGE_SHIFT + cpuinfo.tlb_num_ways_log2));
+
+ /* remember pid/way until we return */
+ org_misc = (RDCTL(CTL_TLBMISC) & (TLBMISC_PID | TLBMISC_WAY));
+
+ WRCTL(CTL_PTEADDR, line << 2);
+
+ for (way = 0; way < cpuinfo.tlb_num_ways; way++) {
+ unsigned long pteaddr;
+ unsigned long tlbmisc;
+ unsigned long tlbacc;
+
+ WRCTL(CTL_TLBMISC, TLBMISC_RD | (way << TLBMISC_WAY_SHIFT));
+ pteaddr = RDCTL(CTL_PTEADDR);
+ tlbmisc = RDCTL(CTL_TLBMISC);
+ tlbacc = RDCTL(CTL_TLBACC);
+
+ if ((tlbacc << PAGE_SHIFT) != (MAX_PHYS_ADDR & PAGE_MASK)) {
+ pr_debug("-- way:%02x vpn:0x%08lx phys:0x%08lx pid:0x%02lx flags:%c%c%c%c%c\n",
+ way,
+ (pteaddr << (PAGE_SHIFT-2)),
+ (tlbacc << PAGE_SHIFT),
+ ((tlbmisc >> TLBMISC_PID_SHIFT) &
+ TLBMISC_PID_MASK),
+ (tlbacc & _PAGE_READ ? 'r' : '-'),
+ (tlbacc & _PAGE_WRITE ? 'w' : '-'),
+ (tlbacc & _PAGE_EXEC ? 'x' : '-'),
+ (tlbacc & _PAGE_GLOBAL ? 'g' : '-'),
+ (tlbacc & _PAGE_CACHED ? 'c' : '-'));
+ }
+ }
+
+ WRCTL(CTL_TLBMISC, org_misc);
+}
+
+void dump_tlb(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < cpuinfo.tlb_num_lines; i++)
+ dump_tlb_line(i);
+}
+
+void flush_tlb_pid(unsigned long pid)
+{
+ unsigned int line;
+ unsigned int way;
+ unsigned long org_misc, pid_misc;
+
+ /* remember pid/way until we return */
+ get_misc_and_pid(&org_misc, &pid_misc);
+
+ for (line = 0; line < cpuinfo.tlb_num_lines; line++) {
+ WRCTL(CTL_PTEADDR, line << 2);
+
+ for (way = 0; way < cpuinfo.tlb_num_ways; way++) {
+ unsigned long pteaddr;
+ unsigned long tlbmisc;
+ unsigned long tlbacc;
+
+ tlbmisc = pid_misc | TLBMISC_RD |
+ (way << TLBMISC_WAY_SHIFT);
+ WRCTL(CTL_TLBMISC, tlbmisc);
+ pteaddr = RDCTL(CTL_PTEADDR);
+ tlbmisc = RDCTL(CTL_TLBMISC);
+ tlbacc = RDCTL(CTL_TLBACC);
+
+ if (((tlbmisc>>TLBMISC_PID_SHIFT) & TLBMISC_PID_MASK)
+ == pid) {
+ tlbmisc = pid_misc | TLBMISC_WE |
+ (way << TLBMISC_WAY_SHIFT);
+ WRCTL(CTL_TLBMISC, tlbmisc);
+ WRCTL(CTL_TLBACC,
+ (MAX_PHYS_ADDR >> PAGE_SHIFT));
+ }
+ }
+
+ WRCTL(CTL_TLBMISC, org_misc);
+ }
+}
+
+void flush_tlb_all(void)
+{
+ int i;
+ unsigned long vaddr = CONFIG_NIOS2_IO_REGION_BASE;
+ unsigned int way;
+ unsigned long org_misc, pid_misc, tlbmisc;
+
+ /* remember pid/way until we return */
+ get_misc_and_pid(&org_misc, &pid_misc);
+ pid_misc |= TLBMISC_WE;
+
+ /* Map each TLB entry to physcal address 0 with no-access and a
+ bad ptbase */
+ for (way = 0; way < cpuinfo.tlb_num_ways; way++) {
+ tlbmisc = pid_misc | (way << TLBMISC_WAY_SHIFT);
+ for (i = 0; i < cpuinfo.tlb_num_lines; i++) {
+ WRCTL(CTL_PTEADDR, ((vaddr) >> PAGE_SHIFT) << 2);
+ WRCTL(CTL_TLBMISC, tlbmisc);
+ WRCTL(CTL_TLBACC, (MAX_PHYS_ADDR >> PAGE_SHIFT));
+ vaddr += 1UL << 12;
+ }
+ }
+
+ /* restore pid/way */
+ WRCTL(CTL_TLBMISC, org_misc);
+}
+
+void set_mmu_pid(unsigned long pid)
+{
+ WRCTL(CTL_TLBMISC, (RDCTL(CTL_TLBMISC) & TLBMISC_WAY) |
+ ((pid & TLBMISC_PID_MASK) << TLBMISC_PID_SHIFT));
+}
--- /dev/null
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2009, Wind River Systems Inc
+ * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com
+ */
+
+#include <linux/export.h>
+#include <linux/uaccess.h>
+
+asm(".global __copy_from_user\n"
+ " .type __copy_from_user, @function\n"
+ "__copy_from_user:\n"
+ " movi r2,7\n"
+ " mov r3,r4\n"
+ " bge r2,r6,1f\n"
+ " xor r2,r4,r5\n"
+ " andi r2,r2,3\n"
+ " movi r7,3\n"
+ " beq r2,zero,4f\n"
+ "1: addi r6,r6,-1\n"
+ " movi r2,-1\n"
+ " beq r6,r2,3f\n"
+ " mov r7,r2\n"
+ "2: ldbu r2,0(r5)\n"
+ " addi r6,r6,-1\n"
+ " addi r5,r5,1\n"
+ " stb r2,0(r3)\n"
+ " addi r3,r3,1\n"
+ " bne r6,r7,2b\n"
+ "3:\n"
+ " addi r2,r6,1\n"
+ " ret\n"
+ "13:mov r2,r6\n"
+ " ret\n"
+ "4: andi r2,r4,1\n"
+ " cmpeq r2,r2,zero\n"
+ " beq r2,zero,7f\n"
+ "5: andi r2,r3,2\n"
+ " beq r2,zero,6f\n"
+ "9: ldhu r2,0(r5)\n"
+ " addi r6,r6,-2\n"
+ " addi r5,r5,2\n"
+ " sth r2,0(r3)\n"
+ " addi r3,r3,2\n"
+ "6: bge r7,r6,1b\n"
+ "10:ldw r2,0(r5)\n"
+ " addi r6,r6,-4\n"
+ " addi r5,r5,4\n"
+ " stw r2,0(r3)\n"
+ " addi r3,r3,4\n"
+ " br 6b\n"
+ "7: ldbu r2,0(r5)\n"
+ " addi r6,r6,-1\n"
+ " addi r5,r5,1\n"
+ " addi r3,r4,1\n"
+ " stb r2,0(r4)\n"
+ " br 5b\n"
+ ".section __ex_table,\"a\"\n"
+ ".word 2b,3b\n"
+ ".word 9b,13b\n"
+ ".word 10b,13b\n"
+ ".word 7b,13b\n"
+ ".previous\n"
+ );
+EXPORT_SYMBOL(__copy_from_user);
+
+asm(
+ " .global __copy_to_user\n"
+ " .type __copy_to_user, @function\n"
+ "__copy_to_user:\n"
+ " movi r2,7\n"
+ " mov r3,r4\n"
+ " bge r2,r6,1f\n"
+ " xor r2,r4,r5\n"
+ " andi r2,r2,3\n"
+ " movi r7,3\n"
+ " beq r2,zero,4f\n"
+ /* Bail if we try to copy zero bytes */
+ "1: addi r6,r6,-1\n"
+ " movi r2,-1\n"
+ " beq r6,r2,3f\n"
+ /* Copy byte by byte for small copies and if src^dst != 0 */
+ " mov r7,r2\n"
+ "2: ldbu r2,0(r5)\n"
+ " addi r5,r5,1\n"
+ "9: stb r2,0(r3)\n"
+ " addi r6,r6,-1\n"
+ " addi r3,r3,1\n"
+ " bne r6,r7,2b\n"
+ "3: addi r2,r6,1\n"
+ " ret\n"
+ "13:mov r2,r6\n"
+ " ret\n"
+ /* If 'to' is an odd address byte copy */
+ "4: andi r2,r4,1\n"
+ " cmpeq r2,r2,zero\n"
+ " beq r2,zero,7f\n"
+ /* If 'to' is not divideable by four copy halfwords */
+ "5: andi r2,r3,2\n"
+ " beq r2,zero,6f\n"
+ " ldhu r2,0(r5)\n"
+ " addi r5,r5,2\n"
+ "10:sth r2,0(r3)\n"
+ " addi r6,r6,-2\n"
+ " addi r3,r3,2\n"
+ /* Copy words */
+ "6: bge r7,r6,1b\n"
+ " ldw r2,0(r5)\n"
+ " addi r5,r5,4\n"
+ "11:stw r2,0(r3)\n"
+ " addi r6,r6,-4\n"
+ " addi r3,r3,4\n"
+ " br 6b\n"
+ /* Copy remaining bytes */
+ "7: ldbu r2,0(r5)\n"
+ " addi r5,r5,1\n"
+ " addi r3,r4,1\n"
+ "12: stb r2,0(r4)\n"
+ " addi r6,r6,-1\n"
+ " br 5b\n"
+ ".section __ex_table,\"a\"\n"
+ ".word 9b,3b\n"
+ ".word 10b,13b\n"
+ ".word 11b,13b\n"
+ ".word 12b,13b\n"
+ ".previous\n");
+EXPORT_SYMBOL(__copy_to_user);
+
+long strncpy_from_user(char *__to, const char __user *__from, long __len)
+{
+ int l = strnlen_user(__from, __len);
+ int is_zt = 1;
+
+ if (l > __len) {
+ is_zt = 0;
+ l = __len;
+ }
+
+ if (l == 0 || copy_from_user(__to, __from, l))
+ return -EFAULT;
+
+ if (is_zt)
+ l--;
+ return l;
+}
+
+long strnlen_user(const char __user *s, long n)
+{
+ long i;
+
+ for (i = 0; i < n; i++) {
+ char c;
+
+ if (get_user(c, s + i) == -EFAULT)
+ return 0;
+ if (c == 0)
+ return i + 1;
+ }
+ return n + 1;
+}
--- /dev/null
+menu "Platform options"
+
+comment "Memory settings"
+
+config NIOS2_MEM_BASE
+ hex "Memory base address"
+ default "0x00000000"
+ help
+ This is the physical address of the memory that the kernel will run
+ from. This address is used to link the kernel and setup initial memory
+ management. You should take the raw memory address without any MMU
+ or cache bits set.
+ Please not that this address is used directly so you have to manually
+ do address translation if it's connected to a bridge.
+
+comment "Device tree"
+
+config NIOS2_DTB_AT_PHYS_ADDR
+ bool "DTB at physical address"
+ default n
+ help
+ When enabled you can select a physical address to load the dtb from.
+ Normally this address is passed by a bootloader such as u-boot but
+ using this you can use a devicetree without a bootloader.
+ This way you can store a devicetree in NOR flash or an onchip rom.
+ Please note that this address is used directly so you have to manually
+ do address translation if it's connected to a bridge. Also take into
+ account that when using an MMU you'd have to ad 0xC0000000 to your
+ address
+
+config NIOS2_DTB_PHYS_ADDR
+ hex "DTB Address"
+ depends on NIOS2_DTB_AT_PHYS_ADDR
+ default "0xC0000000"
+ help
+ Physical address of a dtb blob.
+
+config NIOS2_DTB_SOURCE_BOOL
+ bool "Compile and link device tree into kernel image"
+ default n
+ help
+ This allows you to specify a dts (device tree source) file
+ which will be compiled and linked into the kernel image.
+
+config NIOS2_DTB_SOURCE
+ string "Device tree source file"
+ depends on NIOS2_DTB_SOURCE_BOOL
+ default ""
+ help
+ Absolute path to the device tree source (dts) file describing your
+ system.
+
+comment "Nios II instructions"
+
+config NIOS2_HW_MUL_SUPPORT
+ bool "Enable MUL instruction"
+ default n
+ help
+ Set to true if you configured the Nios II to include the MUL
+ instruction. This will enable the -mhw-mul compiler flag.
+
+config NIOS2_HW_MULX_SUPPORT
+ bool "Enable MULX instruction"
+ default n
+ help
+ Set to true if you configured the Nios II to include the MULX
+ instruction. Enables the -mhw-mulx compiler flag.
+
+config NIOS2_HW_DIV_SUPPORT
+ bool "Enable DIV instruction"
+ default n
+ help
+ Set to true if you configured the Nios II to include the DIV
+ instruction. Enables the -mhw-div compiler flag.
+
+config NIOS2_FPU_SUPPORT
+ bool "Custom floating point instr support"
+ default n
+ help
+ Enables the -mcustom-fpu-cfg=60-1 compiler flag.
+
+config NIOS2_CI_SWAB_SUPPORT
+ bool "Byteswap custom instruction"
+ default n
+ help
+ Use the byteswap (endian converter) Nios II custom instruction provided
+ by Altera and which can be enabled in QSYS builder. This accelerates
+ endian conversions in the kernel (e.g. ntohs).
+
+config NIOS2_CI_SWAB_NO
+ int "Byteswap custom instruction number" if NIOS2_CI_SWAB_SUPPORT
+ default 0
+ help
+ Number of the instruction as configured in QSYS Builder.
+
+comment "Cache settings"
+
+config CUSTOM_CACHE_SETTINGS
+ bool "Custom cache settings"
+ help
+ This option allows you to tweak the cache settings used during early
+ boot (where the information from device tree is not yet available).
+ There should be no reason to change these values. Linux will work
+ perfectly fine, even if the Nios II is configured with smaller caches.
+
+ Say N here unless you know what you are doing.
+
+config NIOS2_DCACHE_SIZE
+ hex "D-Cache size" if CUSTOM_CACHE_SETTINGS
+ range 0x200 0x10000
+ default "0x800"
+ help
+ Maximum possible data cache size.
+
+config NIOS2_DCACHE_LINE_SIZE
+ hex "D-Cache line size" if CUSTOM_CACHE_SETTINGS
+ range 0x10 0x20
+ default "0x20"
+ help
+ Minimum possible data cache line size.
+
+config NIOS2_ICACHE_SIZE
+ hex "I-Cache size" if CUSTOM_CACHE_SETTINGS
+ range 0x200 0x10000
+ default "0x1000"
+ help
+ Maximum possible instruction cache size.
+
+endmenu
--- /dev/null
+obj-y += platform.o
--- /dev/null
+/*
+ * Copyright (C) 2013 Altera Corporation
+ * Copyright (C) 2011 Thomas Chou
+ * Copyright (C) 2011 Walter Goossens
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file COPYING in the main directory of this
+ * archive for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/of_fdt.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/sys_soc.h>
+#include <linux/io.h>
+
+static int __init nios2_soc_device_init(void)
+{
+ struct soc_device *soc_dev;
+ struct soc_device_attribute *soc_dev_attr;
+ const char *machine;
+
+ soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
+ if (soc_dev_attr) {
+ machine = of_flat_dt_get_machine_name();
+ if (machine)
+ soc_dev_attr->machine = kasprintf(GFP_KERNEL, "%s",
+ machine);
+
+ soc_dev_attr->family = "Nios II";
+
+ soc_dev = soc_device_register(soc_dev_attr);
+ if (IS_ERR(soc_dev)) {
+ kfree(soc_dev_attr->machine);
+ kfree(soc_dev_attr);
+ }
+ }
+
+ return of_platform_populate(NULL, of_default_bus_match_table,
+ NULL, NULL);
+}
+
+device_initcall(nios2_soc_device_init);
#include <asm/errno.h>
#include <asm-generic/uaccess-unaligned.h>
+#include <linux/bug.h>
+
#define VERIFY_READ 0
#define VERIFY_WRITE 1
* that put_user is the same as __put_user, etc.
*/
-extern int __get_kernel_bad(void);
-extern int __get_user_bad(void);
-extern int __put_kernel_bad(void);
-extern int __put_user_bad(void);
-
static inline long access_ok(int type, const void __user * addr,
unsigned long size)
{
#define get_user __get_user
#if !defined(CONFIG_64BIT)
-#define LDD_KERNEL(ptr) __get_kernel_bad();
-#define LDD_USER(ptr) __get_user_bad();
+#define LDD_KERNEL(ptr) BUILD_BUG()
+#define LDD_USER(ptr) BUILD_BUG()
#define STD_KERNEL(x, ptr) __put_kernel_asm64(x,ptr)
#define STD_USER(x, ptr) __put_user_asm64(x,ptr)
#define ASM_WORD_INSN ".word\t"
case 2: __get_kernel_asm("ldh",ptr); break; \
case 4: __get_kernel_asm("ldw",ptr); break; \
case 8: LDD_KERNEL(ptr); break; \
- default: __get_kernel_bad(); break; \
+ default: BUILD_BUG(); break; \
} \
} \
else { \
case 2: __get_user_asm("ldh",ptr); break; \
case 4: __get_user_asm("ldw",ptr); break; \
case 8: LDD_USER(ptr); break; \
- default: __get_user_bad(); break; \
+ default: BUILD_BUG(); break; \
} \
} \
\
case 2: __put_kernel_asm("sth",__x,ptr); break; \
case 4: __put_kernel_asm("stw",__x,ptr); break; \
case 8: STD_KERNEL(__x,ptr); break; \
- default: __put_kernel_bad(); break; \
+ default: BUILD_BUG(); break; \
} \
} \
else { \
case 2: __put_user_asm("sth",__x,ptr); break; \
case 4: __put_user_asm("stw",__x,ptr); break; \
case 8: STD_USER(__x,ptr); break; \
- default: __put_user_bad(); break; \
+ default: BUILD_BUG(); break; \
} \
} \
\
#ifndef __ASM_PARISC_BITSPERLONG_H
#define __ASM_PARISC_BITSPERLONG_H
-/*
- * using CONFIG_* outside of __KERNEL__ is wrong,
- * __LP64__ was also removed from headers, so what
- * is the right approach on parisc?
- * -arnd
- */
-#if (defined(__KERNEL__) && defined(CONFIG_64BIT)) || defined (__LP64__)
+#if defined(__LP64__)
#define __BITS_PER_LONG 64
#define SHIFT_PER_LONG 6
#else
#ifndef _PARISC_MSGBUF_H
#define _PARISC_MSGBUF_H
+#include <asm/bitsperlong.h>
+
/*
* The msqid64_ds structure for parisc architecture, copied from sparc.
* Note extra padding because this structure is passed back and forth
struct msqid64_ds {
struct ipc64_perm msg_perm;
-#ifndef CONFIG_64BIT
+#if __BITS_PER_LONG != 64
unsigned int __pad1;
#endif
__kernel_time_t msg_stime; /* last msgsnd time */
-#ifndef CONFIG_64BIT
+#if __BITS_PER_LONG != 64
unsigned int __pad2;
#endif
__kernel_time_t msg_rtime; /* last msgrcv time */
-#ifndef CONFIG_64BIT
+#if __BITS_PER_LONG != 64
unsigned int __pad3;
#endif
__kernel_time_t msg_ctime; /* last change time */
#ifndef _PARISC_SEMBUF_H
#define _PARISC_SEMBUF_H
+#include <asm/bitsperlong.h>
+
/*
* The semid64_ds structure for parisc architecture.
* Note extra padding because this structure is passed back and forth
struct semid64_ds {
struct ipc64_perm sem_perm; /* permissions .. see ipc.h */
-#ifndef CONFIG_64BIT
+#if __BITS_PER_LONG != 64
unsigned int __pad1;
#endif
__kernel_time_t sem_otime; /* last semop time */
-#ifndef CONFIG_64BIT
+#if __BITS_PER_LONG != 64
unsigned int __pad2;
#endif
__kernel_time_t sem_ctime; /* last change time */
#ifndef _PARISC_SHMBUF_H
#define _PARISC_SHMBUF_H
+#include <asm/bitsperlong.h>
+
/*
* The shmid64_ds structure for parisc architecture.
* Note extra padding because this structure is passed back and forth
struct shmid64_ds {
struct ipc64_perm shm_perm; /* operation perms */
-#ifndef CONFIG_64BIT
+#if __BITS_PER_LONG != 64
unsigned int __pad1;
#endif
__kernel_time_t shm_atime; /* last attach time */
-#ifndef CONFIG_64BIT
+#if __BITS_PER_LONG != 64
unsigned int __pad2;
#endif
__kernel_time_t shm_dtime; /* last detach time */
-#ifndef CONFIG_64BIT
+#if __BITS_PER_LONG != 64
unsigned int __pad3;
#endif
__kernel_time_t shm_ctime; /* last change time */
-#ifndef CONFIG_64BIT
+#if __BITS_PER_LONG != 64
unsigned int __pad4;
#endif
size_t shm_segsz; /* size of segment (bytes) */
unsigned int __unused2;
};
-#ifdef CONFIG_64BIT
-/* The 'unsigned int' (formerly 'unsigned long') data types below will
- * ensure that a 32-bit app calling shmctl(*,IPC_INFO,*) will work on
- * a wide kernel, but if some of these values are meant to contain pointers
- * they may need to be 'long long' instead. -PB XXX FIXME
- */
-#endif
struct shminfo64 {
- unsigned int shmmax;
- unsigned int shmmin;
- unsigned int shmmni;
- unsigned int shmseg;
- unsigned int shmall;
- unsigned int __unused1;
- unsigned int __unused2;
- unsigned int __unused3;
- unsigned int __unused4;
+ unsigned long shmmax;
+ unsigned long shmmin;
+ unsigned long shmmni;
+ unsigned long shmseg;
+ unsigned long shmall;
+ unsigned long __unused1;
+ unsigned long __unused2;
+ unsigned long __unused3;
+ unsigned long __unused4;
};
#endif /* _PARISC_SHMBUF_H */
struct siginfo;
/* Type of a signal handler. */
-#ifdef CONFIG_64BIT
+#if defined(__LP64__)
/* function pointers on 64-bit parisc are pointers to little structs and the
* compiler doesn't support code which changes or tests the address of
* the function in the little struct. This is really ugly -PB
#define __NR_seccomp (__NR_Linux + 338)
#define __NR_getrandom (__NR_Linux + 339)
#define __NR_memfd_create (__NR_Linux + 340)
+#define __NR_bpf (__NR_Linux + 341)
-#define __NR_Linux_syscalls (__NR_memfd_create + 1)
+#define __NR_Linux_syscalls (__NR_bpf + 1)
#define __IGNORE_select /* newselect */
ENTRY_COMP(msgsnd)
ENTRY_COMP(msgrcv)
ENTRY_SAME(msgget) /* 190 */
- ENTRY_SAME(msgctl)
- ENTRY_SAME(shmat)
+ ENTRY_COMP(msgctl)
+ ENTRY_COMP(shmat)
ENTRY_SAME(shmdt)
ENTRY_SAME(shmget)
- ENTRY_SAME(shmctl) /* 195 */
+ ENTRY_COMP(shmctl) /* 195 */
ENTRY_SAME(ni_syscall) /* streams1 */
ENTRY_SAME(ni_syscall) /* streams2 */
ENTRY_SAME(lstat64)
ENTRY_SAME(epoll_ctl) /* 225 */
ENTRY_SAME(epoll_wait)
ENTRY_SAME(remap_file_pages)
- ENTRY_SAME(semtimedop)
+ ENTRY_COMP(semtimedop)
ENTRY_COMP(mq_open)
ENTRY_SAME(mq_unlink) /* 230 */
ENTRY_COMP(mq_timedsend)
ENTRY_SAME(seccomp)
ENTRY_SAME(getrandom)
ENTRY_SAME(memfd_create) /* 340 */
+ ENTRY_SAME(bpf)
/* Nothing yet */
#define CPU_UNKNOWN (~((u32)0))
/* Utility macros */
-#define SKIP_TO_NEXT_CPU(reg_entry) \
-({ \
- while (reg_entry->reg_id != REG_ID("CPUEND")) \
- reg_entry++; \
- reg_entry++; \
+#define SKIP_TO_NEXT_CPU(reg_entry) \
+({ \
+ while (be64_to_cpu(reg_entry->reg_id) != REG_ID("CPUEND")) \
+ reg_entry++; \
+ reg_entry++; \
})
/* Kernel Dump section info */
struct fadump_section {
- u32 request_flag;
- u16 source_data_type;
- u16 error_flags;
- u64 source_address;
- u64 source_len;
- u64 bytes_dumped;
- u64 destination_address;
+ __be32 request_flag;
+ __be16 source_data_type;
+ __be16 error_flags;
+ __be64 source_address;
+ __be64 source_len;
+ __be64 bytes_dumped;
+ __be64 destination_address;
};
/* ibm,configure-kernel-dump header. */
struct fadump_section_header {
- u32 dump_format_version;
- u16 dump_num_sections;
- u16 dump_status_flag;
- u32 offset_first_dump_section;
+ __be32 dump_format_version;
+ __be16 dump_num_sections;
+ __be16 dump_status_flag;
+ __be32 offset_first_dump_section;
/* Fields for disk dump option. */
- u32 dd_block_size;
- u64 dd_block_offset;
- u64 dd_num_blocks;
- u32 dd_offset_disk_path;
+ __be32 dd_block_size;
+ __be64 dd_block_offset;
+ __be64 dd_num_blocks;
+ __be32 dd_offset_disk_path;
/* Maximum time allowed to prevent an automatic dump-reboot. */
- u32 max_time_auto;
+ __be32 max_time_auto;
};
/*
/* Register save area header. */
struct fadump_reg_save_area_header {
- u64 magic_number;
- u32 version;
- u32 num_cpu_offset;
+ __be64 magic_number;
+ __be32 version;
+ __be32 num_cpu_offset;
};
/* Register entry. */
struct fadump_reg_entry {
- u64 reg_id;
- u64 reg_value;
+ __be64 reg_id;
+ __be64 reg_value;
};
/* fadump crash info structure */
int pci_ext_config_space; /* for pci devices */
- bool force_32bit_msi;
-
struct pci_dev *pcidev; /* back-pointer to the pci device */
#ifdef CONFIG_EEH
struct eeh_dev *edev; /* eeh device */
#ifdef __KERNEL__
#include <linux/mm.h>
-#include <asm-generic/tlb.h>
#ifdef CONFIG_PPC_BOOK3E
extern void tlb_flush_pgtable(struct mmu_gather *tlb, unsigned long address);
}
#endif /* !CONFIG_PPC_BOOK3E */
+extern void tlb_remove_table(struct mmu_gather *tlb, void *table);
+
#ifdef CONFIG_PPC64
#include <asm/pgalloc-64.h>
#else
#define tlb_start_vma(tlb, vma) do { } while (0)
#define tlb_end_vma(tlb, vma) do { } while (0)
+#define __tlb_remove_tlb_entry __tlb_remove_tlb_entry
extern void tlb_flush(struct mmu_gather *tlb);
return -ENODEV;
state = eeh_ops->get_state(edev->pe, NULL);
- return sprintf(buf, "%0x08x %0x08x\n",
+ return sprintf(buf, "0x%08x 0x%08x\n",
state, edev->pe->state);
}
3:
#endif
bl save_nvgprs
+ /*
+ * Use a non volatile GPR to save and restore our thread_info flags
+ * across the call to restore_interrupts.
+ */
+ mr r30,r4
bl restore_interrupts
+ mr r4,r30
addi r3,r1,STACK_FRAME_OVERHEAD
bl do_notify_resume
b ret_from_except
const __be32 *sections;
int i, num_sections;
int size;
- const int *token;
+ const __be32 *token;
if (depth != 1 || strcmp(uname, "rtas") != 0)
return 0;
return 1;
fw_dump.fadump_supported = 1;
- fw_dump.ibm_configure_kernel_dump = *token;
+ fw_dump.ibm_configure_kernel_dump = be32_to_cpu(*token);
/*
* The 'ibm,kernel-dump' rtas node is present only if there is
memset(fdm, 0, sizeof(struct fadump_mem_struct));
addr = addr & PAGE_MASK;
- fdm->header.dump_format_version = 0x00000001;
- fdm->header.dump_num_sections = 3;
+ fdm->header.dump_format_version = cpu_to_be32(0x00000001);
+ fdm->header.dump_num_sections = cpu_to_be16(3);
fdm->header.dump_status_flag = 0;
fdm->header.offset_first_dump_section =
- (u32)offsetof(struct fadump_mem_struct, cpu_state_data);
+ cpu_to_be32((u32)offsetof(struct fadump_mem_struct, cpu_state_data));
/*
* Fields for disk dump option.
/* Kernel dump sections */
/* cpu state data section. */
- fdm->cpu_state_data.request_flag = FADUMP_REQUEST_FLAG;
- fdm->cpu_state_data.source_data_type = FADUMP_CPU_STATE_DATA;
+ fdm->cpu_state_data.request_flag = cpu_to_be32(FADUMP_REQUEST_FLAG);
+ fdm->cpu_state_data.source_data_type = cpu_to_be16(FADUMP_CPU_STATE_DATA);
fdm->cpu_state_data.source_address = 0;
- fdm->cpu_state_data.source_len = fw_dump.cpu_state_data_size;
- fdm->cpu_state_data.destination_address = addr;
+ fdm->cpu_state_data.source_len = cpu_to_be64(fw_dump.cpu_state_data_size);
+ fdm->cpu_state_data.destination_address = cpu_to_be64(addr);
addr += fw_dump.cpu_state_data_size;
/* hpte region section */
- fdm->hpte_region.request_flag = FADUMP_REQUEST_FLAG;
- fdm->hpte_region.source_data_type = FADUMP_HPTE_REGION;
+ fdm->hpte_region.request_flag = cpu_to_be32(FADUMP_REQUEST_FLAG);
+ fdm->hpte_region.source_data_type = cpu_to_be16(FADUMP_HPTE_REGION);
fdm->hpte_region.source_address = 0;
- fdm->hpte_region.source_len = fw_dump.hpte_region_size;
- fdm->hpte_region.destination_address = addr;
+ fdm->hpte_region.source_len = cpu_to_be64(fw_dump.hpte_region_size);
+ fdm->hpte_region.destination_address = cpu_to_be64(addr);
addr += fw_dump.hpte_region_size;
/* RMA region section */
- fdm->rmr_region.request_flag = FADUMP_REQUEST_FLAG;
- fdm->rmr_region.source_data_type = FADUMP_REAL_MODE_REGION;
- fdm->rmr_region.source_address = RMA_START;
- fdm->rmr_region.source_len = fw_dump.boot_memory_size;
- fdm->rmr_region.destination_address = addr;
+ fdm->rmr_region.request_flag = cpu_to_be32(FADUMP_REQUEST_FLAG);
+ fdm->rmr_region.source_data_type = cpu_to_be16(FADUMP_REAL_MODE_REGION);
+ fdm->rmr_region.source_address = cpu_to_be64(RMA_START);
+ fdm->rmr_region.source_len = cpu_to_be64(fw_dump.boot_memory_size);
+ fdm->rmr_region.destination_address = cpu_to_be64(addr);
addr += fw_dump.boot_memory_size;
return addr;
* first kernel.
*/
if (fdm_active)
- fw_dump.boot_memory_size = fdm_active->rmr_region.source_len;
+ fw_dump.boot_memory_size = be64_to_cpu(fdm_active->rmr_region.source_len);
else
fw_dump.boot_memory_size = fadump_calculate_reserve_size();
(unsigned long)(base >> 20));
fw_dump.fadumphdr_addr =
- fdm_active->rmr_region.destination_address +
- fdm_active->rmr_region.source_len;
+ be64_to_cpu(fdm_active->rmr_region.destination_address) +
+ be64_to_cpu(fdm_active->rmr_region.source_len);
pr_debug("fadumphdr_addr = %p\n",
(void *) fw_dump.fadumphdr_addr);
} else {
{
memset(regs, 0, sizeof(struct pt_regs));
- while (reg_entry->reg_id != REG_ID("CPUEND")) {
- fadump_set_regval(regs, reg_entry->reg_id,
- reg_entry->reg_value);
+ while (be64_to_cpu(reg_entry->reg_id) != REG_ID("CPUEND")) {
+ fadump_set_regval(regs, be64_to_cpu(reg_entry->reg_id),
+ be64_to_cpu(reg_entry->reg_value));
reg_entry++;
}
reg_entry++;
if (!fdm->cpu_state_data.bytes_dumped)
return -EINVAL;
- addr = fdm->cpu_state_data.destination_address;
+ addr = be64_to_cpu(fdm->cpu_state_data.destination_address);
vaddr = __va(addr);
reg_header = vaddr;
- if (reg_header->magic_number != REGSAVE_AREA_MAGIC) {
+ if (be64_to_cpu(reg_header->magic_number) != REGSAVE_AREA_MAGIC) {
printk(KERN_ERR "Unable to read register save area.\n");
return -ENOENT;
}
pr_debug("--------CPU State Data------------\n");
- pr_debug("Magic Number: %llx\n", reg_header->magic_number);
- pr_debug("NumCpuOffset: %x\n", reg_header->num_cpu_offset);
+ pr_debug("Magic Number: %llx\n", be64_to_cpu(reg_header->magic_number));
+ pr_debug("NumCpuOffset: %x\n", be32_to_cpu(reg_header->num_cpu_offset));
- vaddr += reg_header->num_cpu_offset;
- num_cpus = *((u32 *)(vaddr));
+ vaddr += be32_to_cpu(reg_header->num_cpu_offset);
+ num_cpus = be32_to_cpu(*((__be32 *)(vaddr)));
pr_debug("NumCpus : %u\n", num_cpus);
vaddr += sizeof(u32);
reg_entry = (struct fadump_reg_entry *)vaddr;
fdh = __va(fw_dump.fadumphdr_addr);
for (i = 0; i < num_cpus; i++) {
- if (reg_entry->reg_id != REG_ID("CPUSTRT")) {
+ if (be64_to_cpu(reg_entry->reg_id) != REG_ID("CPUSTRT")) {
printk(KERN_ERR "Unable to read CPU state data\n");
rc = -ENOENT;
goto error_out;
}
/* Lower 4 bytes of reg_value contains logical cpu id */
- cpu = reg_entry->reg_value & FADUMP_CPU_ID_MASK;
+ cpu = be64_to_cpu(reg_entry->reg_value) & FADUMP_CPU_ID_MASK;
if (fdh && !cpumask_test_cpu(cpu, &fdh->cpu_online_mask)) {
SKIP_TO_NEXT_CPU(reg_entry);
continue;
return -EINVAL;
/* Check if the dump data is valid. */
- if ((fdm_active->header.dump_status_flag == FADUMP_ERROR_FLAG) ||
+ if ((be16_to_cpu(fdm_active->header.dump_status_flag) == FADUMP_ERROR_FLAG) ||
(fdm_active->cpu_state_data.error_flags != 0) ||
(fdm_active->rmr_region.error_flags != 0)) {
printk(KERN_ERR "Dump taken by platform is not valid\n");
static inline unsigned long fadump_relocate(unsigned long paddr)
{
if (paddr > RMA_START && paddr < fw_dump.boot_memory_size)
- return fdm.rmr_region.destination_address + paddr;
+ return be64_to_cpu(fdm.rmr_region.destination_address) + paddr;
else
return paddr;
}
* to the specified destination_address. Hence set
* the correct offset.
*/
- phdr->p_offset = fdm.rmr_region.destination_address;
+ phdr->p_offset = be64_to_cpu(fdm.rmr_region.destination_address);
}
phdr->p_paddr = mbase;
fadump_setup_crash_memory_ranges();
- addr = fdm.rmr_region.destination_address + fdm.rmr_region.source_len;
+ addr = be64_to_cpu(fdm.rmr_region.destination_address) + be64_to_cpu(fdm.rmr_region.source_len);
/* Initialize fadump crash info header. */
addr = init_fadump_header(addr);
vaddr = __va(addr);
/* Invalidate the registration only if dump is active. */
if (fw_dump.dump_active) {
init_fadump_mem_struct(&fdm,
- fdm_active->cpu_state_data.destination_address);
+ be64_to_cpu(fdm_active->cpu_state_data.destination_address));
fadump_invalidate_dump(&fdm);
}
}
return;
}
- destination_address = fdm_active->cpu_state_data.destination_address;
+ destination_address = be64_to_cpu(fdm_active->cpu_state_data.destination_address);
fadump_cleanup();
mutex_unlock(&fadump_mutex);
seq_printf(m,
"CPU : [%#016llx-%#016llx] %#llx bytes, "
"Dumped: %#llx\n",
- fdm_ptr->cpu_state_data.destination_address,
- fdm_ptr->cpu_state_data.destination_address +
- fdm_ptr->cpu_state_data.source_len - 1,
- fdm_ptr->cpu_state_data.source_len,
- fdm_ptr->cpu_state_data.bytes_dumped);
+ be64_to_cpu(fdm_ptr->cpu_state_data.destination_address),
+ be64_to_cpu(fdm_ptr->cpu_state_data.destination_address) +
+ be64_to_cpu(fdm_ptr->cpu_state_data.source_len) - 1,
+ be64_to_cpu(fdm_ptr->cpu_state_data.source_len),
+ be64_to_cpu(fdm_ptr->cpu_state_data.bytes_dumped));
seq_printf(m,
"HPTE: [%#016llx-%#016llx] %#llx bytes, "
"Dumped: %#llx\n",
- fdm_ptr->hpte_region.destination_address,
- fdm_ptr->hpte_region.destination_address +
- fdm_ptr->hpte_region.source_len - 1,
- fdm_ptr->hpte_region.source_len,
- fdm_ptr->hpte_region.bytes_dumped);
+ be64_to_cpu(fdm_ptr->hpte_region.destination_address),
+ be64_to_cpu(fdm_ptr->hpte_region.destination_address) +
+ be64_to_cpu(fdm_ptr->hpte_region.source_len) - 1,
+ be64_to_cpu(fdm_ptr->hpte_region.source_len),
+ be64_to_cpu(fdm_ptr->hpte_region.bytes_dumped));
seq_printf(m,
"DUMP: [%#016llx-%#016llx] %#llx bytes, "
"Dumped: %#llx\n",
- fdm_ptr->rmr_region.destination_address,
- fdm_ptr->rmr_region.destination_address +
- fdm_ptr->rmr_region.source_len - 1,
- fdm_ptr->rmr_region.source_len,
- fdm_ptr->rmr_region.bytes_dumped);
+ be64_to_cpu(fdm_ptr->rmr_region.destination_address),
+ be64_to_cpu(fdm_ptr->rmr_region.destination_address) +
+ be64_to_cpu(fdm_ptr->rmr_region.source_len) - 1,
+ be64_to_cpu(fdm_ptr->rmr_region.source_len),
+ be64_to_cpu(fdm_ptr->rmr_region.bytes_dumped));
if (!fdm_active ||
(fw_dump.reserve_dump_area_start ==
- fdm_ptr->cpu_state_data.destination_address))
+ be64_to_cpu(fdm_ptr->cpu_state_data.destination_address)))
goto out;
/* Dump is active. Show reserved memory region. */
" : [%#016llx-%#016llx] %#llx bytes, "
"Dumped: %#llx\n",
(unsigned long long)fw_dump.reserve_dump_area_start,
- fdm_ptr->cpu_state_data.destination_address - 1,
- fdm_ptr->cpu_state_data.destination_address -
+ be64_to_cpu(fdm_ptr->cpu_state_data.destination_address) - 1,
+ be64_to_cpu(fdm_ptr->cpu_state_data.destination_address) -
fw_dump.reserve_dump_area_start,
- fdm_ptr->cpu_state_data.destination_address -
+ be64_to_cpu(fdm_ptr->cpu_state_data.destination_address) -
fw_dump.reserve_dump_area_start);
out:
if (fdm_active)
}
EXPORT_SYMBOL(pcibus_to_node);
#endif
-
-static void quirk_radeon_32bit_msi(struct pci_dev *dev)
-{
- struct pci_dn *pdn = pci_get_pdn(dev);
-
- if (pdn)
- pdn->force_32bit_msi = true;
-}
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x68f2, quirk_radeon_32bit_msi);
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0xaa68, quirk_radeon_32bit_msi);
V_FUNCTION_BEGIN(__kernel_getcpu)
.cfi_startproc
mfspr r5,SPRN_SPRG_VDSO_READ
- cmpdi cr0,r3,0
- cmpdi cr1,r4,0
+ cmpwi cr0,r3,0
+ cmpwi cr1,r4,0
clrlwi r6,r5,16
rlwinm r7,r5,16,31-15,31-0
beq cr0,1f
for (i = 0; i < num_hugepd; i++, hpdp++)
hpdp->pd = 0;
- tlb->need_flush = 1;
-
#ifdef CONFIG_PPC_FSL_BOOK3E
hugepd_free(tlb, hugepte);
#else
/*
* Check for command-line options that affect what MMU_init will do.
*/
-void MMU_setup(void)
+void __init MMU_setup(void)
{
/* Check for nobats option (used in mapin_ram). */
if (strstr(boot_command_line, "nobats")) {
};
/* Print things out */
- if (hmi_evt->version != OpalHMIEvt_V1) {
+ if (hmi_evt->version < OpalHMIEvt_V1) {
pr_err("HMI Interrupt, Unknown event version %d !\n",
hmi_evt->version);
return;
&data, len);
if (rc)
return -ENXIO;
+
+ /*
+ * Now there is some trickery with the data returned by OPAL
+ * as it's the desired data right justified in a 32-bit BE
+ * word.
+ *
+ * This is a very bad interface and I'm to blame for it :-(
+ *
+ * So we can't just apply a 32-bit swap to what comes from OPAL,
+ * because user space expects the *bytes* to be in their proper
+ * respective positions (ie, LPC position).
+ *
+ * So what we really want to do here is to shift data right
+ * appropriately on a LE kernel.
+ *
+ * IE. If the LPC transaction has bytes B0, B1, B2 and B3 in that
+ * order, we have in memory written to by OPAL at the "data"
+ * pointer:
+ *
+ * Bytes: OPAL "data" LE "data"
+ * 32-bit: B0 B1 B2 B3 B0B1B2B3 B3B2B1B0
+ * 16-bit: B0 B1 0000B0B1 B1B00000
+ * 8-bit: B0 000000B0 B0000000
+ *
+ * So a BE kernel will have the leftmost of the above in the MSB
+ * and rightmost in the LSB and can just then "cast" the u32 "data"
+ * down to the appropriate quantity and write it.
+ *
+ * However, an LE kernel can't. It doesn't need to swap because a
+ * load from data followed by a store to user are going to preserve
+ * the byte ordering which is the wire byte order which is what the
+ * user wants, but in order to "crop" to the right size, we need to
+ * shift right first.
+ */
switch(len) {
case 4:
rc = __put_user((u32)data, (u32 __user *)ubuf);
break;
case 2:
+#ifdef __LITTLE_ENDIAN__
+ data >>= 16;
+#endif
rc = __put_user((u16)data, (u16 __user *)ubuf);
break;
default:
+#ifdef __LITTLE_ENDIAN__
+ data >>= 24;
+#endif
rc = __put_user((u8)data, (u8 __user *)ubuf);
break;
}
else if (todo > 1 && (pos & 1) == 0)
len = 2;
}
+
+ /*
+ * Similarly to the read case, we have some trickery here but
+ * it's different to handle. We need to pass the value to OPAL in
+ * a register whose layout depends on the access size. We want
+ * to reproduce the memory layout of the user, however we aren't
+ * doing a load from user and a store to another memory location
+ * which would achieve that. Here we pass the value to OPAL via
+ * a register which is expected to contain the "BE" interpretation
+ * of the byte sequence. IE: for a 32-bit access, byte 0 should be
+ * in the MSB. So here we *do* need to byteswap on LE.
+ *
+ * User bytes: LE "data" OPAL "data"
+ * 32-bit: B0 B1 B2 B3 B3B2B1B0 B0B1B2B3
+ * 16-bit: B0 B1 0000B1B0 0000B0B1
+ * 8-bit: B0 000000B0 000000B0
+ */
switch(len) {
case 4:
rc = __get_user(data, (u32 __user *)ubuf);
+ data = cpu_to_be32(data);
break;
case 2:
rc = __get_user(data, (u16 __user *)ubuf);
+ data = cpu_to_be16(data);
break;
default:
rc = __get_user(data, (u8 __user *)ubuf);
#include <linux/delay.h>
#include <linux/mutex.h>
+#include <linux/of_platform.h>
#include <asm/opal.h>
+#include <asm/machdep.h>
static DEFINE_MUTEX(opal_sensor_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(opal_get_sensor_data);
+
+static __init int opal_sensor_init(void)
+{
+ struct platform_device *pdev;
+ struct device_node *sensor;
+
+ sensor = of_find_node_by_path("/ibm,opal/sensors");
+ if (!sensor) {
+ pr_err("Opal node 'sensors' not found\n");
+ return -ENODEV;
+ }
+
+ pdev = of_platform_device_create(sensor, "opal-sensor", NULL);
+ of_node_put(sensor);
+
+ return PTR_ERR_OR_ZERO(pdev);
+}
+machine_subsys_initcall(powernv, opal_sensor_init);
unsigned int is_64, struct msi_msg *msg)
{
struct pnv_ioda_pe *pe = pnv_ioda_get_pe(dev);
- struct pci_dn *pdn = pci_get_pdn(dev);
unsigned int xive_num = hwirq - phb->msi_base;
__be32 data;
int rc;
return -ENXIO;
/* Force 32-bit MSI on some broken devices */
- if (pdn && pdn->force_32bit_msi)
+ if (dev->no_64bit_msi)
is_64 = 0;
/* Assign XIVE to PE */
if (is_kdump_kernel()) {
pr_info(" Issue PHB reset ...\n");
ioda_eeh_phb_reset(hose, EEH_RESET_FUNDAMENTAL);
- ioda_eeh_phb_reset(hose, OPAL_DEASSERT_RESET);
+ ioda_eeh_phb_reset(hose, EEH_RESET_DEACTIVATE);
}
/* Configure M64 window */
{
struct pci_controller *hose = pci_bus_to_host(pdev->bus);
struct pnv_phb *phb = hose->private_data;
- struct pci_dn *pdn = pci_get_pdn(pdev);
struct msi_desc *entry;
struct msi_msg msg;
int hwirq;
if (WARN_ON(!phb) || !phb->msi_bmp.bitmap)
return -ENODEV;
- if (pdn && pdn->force_32bit_msi && !phb->msi32_support)
+ if (pdev->no_64bit_msi && !phb->msi32_support)
return -ENODEV;
list_for_each_entry(entry, &pdev->msi_list, list) {
BUG_ON(get_cpu_current_state(cpu)
!= CPU_STATE_OFFLINE);
cpu_maps_update_done();
- rc = cpu_up(cpu);
+ rc = device_online(get_cpu_device(cpu));
if (rc)
goto out;
cpu_maps_update_begin();
if (get_cpu_current_state(cpu) == CPU_STATE_ONLINE) {
set_preferred_offline_state(cpu, CPU_STATE_OFFLINE);
cpu_maps_update_done();
- rc = cpu_down(cpu);
+ rc = device_offline(get_cpu_device(cpu));
if (rc)
goto out;
cpu_maps_update_begin();
#include <asm/trace.h>
#include <asm/firmware.h>
#include <asm/plpar_wrappers.h>
+#include <asm/fadump.h>
#include "pseries.h"
}
#ifdef __LITTLE_ENDIAN__
- /* Reset exceptions to big endian */
- if (firmware_has_feature(FW_FEATURE_SET_MODE)) {
+ /*
+ * Reset exceptions to big endian.
+ *
+ * FIXME this is a hack for kexec, we need to reset the exception
+ * endian before starting the new kernel and this is a convenient place
+ * to do it.
+ *
+ * This is also called on boot when a fadump happens. In that case we
+ * must not change the exception endian mode.
+ */
+ if (firmware_has_feature(FW_FEATURE_SET_MODE) && !is_fadump_active()) {
long rc;
rc = pseries_big_endian_exceptions();
*/
again:
if (type == PCI_CAP_ID_MSI) {
- if (pdn->force_32bit_msi) {
+ if (pdev->no_64bit_msi) {
rc = rtas_change_msi(pdn, RTAS_CHANGE_32MSI_FN, nvec);
if (rc < 0) {
/*
cascade_data->virq = virt_msir;
msi->cascade_array[irq_index] = cascade_data;
- ret = request_irq(virt_msir, fsl_msi_cascade, 0,
+ ret = request_irq(virt_msir, fsl_msi_cascade, IRQF_NO_THREAD,
"fsl-msi-cascade", cascade_data);
if (ret) {
dev_err(&dev->dev, "failed to request_irq(%d), ret = %d\n",
args.token = rtas_token("set-indicator");
if (args.token == RTAS_UNKNOWN_SERVICE)
return;
- args.nargs = 3;
- args.nret = 1;
+ args.nargs = cpu_to_be32(3);
+ args.nret = cpu_to_be32(1);
args.rets = &args.args[3];
- args.args[0] = SURVEILLANCE_TOKEN;
+ args.args[0] = cpu_to_be32(SURVEILLANCE_TOKEN);
args.args[1] = 0;
args.args[2] = 0;
enter_rtas(__pa(&args));
CONFIG_MODULE_FORCE_UNLOAD=y
CONFIG_MODVERSIONS=y
CONFIG_MODULE_SRCVERSION_ALL=y
-CONFIG_BLK_DEV_INTEGRITY=y
CONFIG_BLK_DEV_THROTTLING=y
CONFIG_PARTITION_ADVANCED=y
CONFIG_IBM_PARTITION=y
CONFIG_NFT_CHAIN_ROUTE_IPV4=m
CONFIG_NFT_CHAIN_NAT_IPV4=m
CONFIG_NF_TABLES_ARP=m
+CONFIG_NF_NAT_IPV4=m
CONFIG_IP_NF_IPTABLES=m
CONFIG_IP_NF_MATCH_AH=m
CONFIG_IP_NF_MATCH_ECN=m
CONFIG_IP_NF_MATCH_TTL=m
CONFIG_IP_NF_FILTER=m
CONFIG_IP_NF_TARGET_REJECT=m
-CONFIG_IP_NF_TARGET_ULOG=m
-CONFIG_NF_NAT_IPV4=m
-CONFIG_IP_NF_TARGET_MASQUERADE=m
-CONFIG_IP_NF_TARGET_NETMAP=m
-CONFIG_IP_NF_TARGET_REDIRECT=m
CONFIG_IP_NF_MANGLE=m
CONFIG_IP_NF_TARGET_CLUSTERIP=m
CONFIG_IP_NF_TARGET_ECN=m
CONFIG_NF_TABLES_IPV6=m
CONFIG_NFT_CHAIN_ROUTE_IPV6=m
CONFIG_NFT_CHAIN_NAT_IPV6=m
+CONFIG_NF_NAT_IPV6=m
CONFIG_IP6_NF_IPTABLES=m
CONFIG_IP6_NF_MATCH_AH=m
CONFIG_IP6_NF_MATCH_EUI64=m
CONFIG_IP6_NF_MANGLE=m
CONFIG_IP6_NF_RAW=m
CONFIG_IP6_NF_SECURITY=m
-CONFIG_NF_NAT_IPV6=m
-CONFIG_IP6_NF_TARGET_MASQUERADE=m
-CONFIG_IP6_NF_TARGET_NPT=m
CONFIG_NF_TABLES_BRIDGE=m
CONFIG_NET_SCTPPROBE=m
CONFIG_RDS=m
CONFIG_CHR_DEV_SG=y
CONFIG_CHR_DEV_SCH=m
CONFIG_SCSI_ENCLOSURE=m
-CONFIG_SCSI_MULTI_LUN=y
CONFIG_SCSI_CONSTANTS=y
CONFIG_SCSI_LOGGING=y
CONFIG_SCSI_SPI_ATTRS=m
+CONFIG_SCSI_FC_ATTRS=y
CONFIG_SCSI_SAS_LIBSAS=m
CONFIG_SCSI_SRP_ATTRS=m
CONFIG_ISCSI_TCP=m
-CONFIG_LIBFCOE=m
CONFIG_SCSI_DEBUG=m
CONFIG_ZFCP=y
CONFIG_SCSI_VIRTIO=m
CONFIG_NLMON=m
CONFIG_VHOST_NET=m
# CONFIG_NET_VENDOR_ARC is not set
-# CONFIG_NET_CADENCE is not set
# CONFIG_NET_VENDOR_CHELSIO is not set
# CONFIG_NET_VENDOR_INTEL is not set
# CONFIG_NET_VENDOR_MARVELL is not set
CONFIG_JFS_POSIX_ACL=y
CONFIG_JFS_SECURITY=y
CONFIG_JFS_STATISTICS=y
-CONFIG_XFS_FS=m
+CONFIG_XFS_FS=y
CONFIG_XFS_QUOTA=y
CONFIG_XFS_POSIX_ACL=y
CONFIG_XFS_RT=y
CONFIG_XFS_DEBUG=y
CONFIG_GFS2_FS=m
CONFIG_OCFS2_FS=m
-CONFIG_BTRFS_FS=m
+CONFIG_BTRFS_FS=y
CONFIG_BTRFS_FS_POSIX_ACL=y
CONFIG_NILFS2_FS=m
CONFIG_FANOTIFY=y
CONFIG_DETECT_HUNG_TASK=y
CONFIG_TIMER_STATS=y
CONFIG_DEBUG_RT_MUTEXES=y
-CONFIG_RT_MUTEX_TESTER=y
CONFIG_DEBUG_WW_MUTEX_SLOWPATH=y
CONFIG_PROVE_LOCKING=y
CONFIG_LOCK_STAT=y
CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
CONFIG_LATENCYTOP=y
CONFIG_DEBUG_STRICT_USER_COPY_CHECKS=y
+CONFIG_IRQSOFF_TRACER=y
+CONFIG_PREEMPT_TRACER=y
+CONFIG_SCHED_TRACER=y
+CONFIG_FTRACE_SYSCALLS=y
+CONFIG_STACK_TRACER=y
CONFIG_BLK_DEV_IO_TRACE=y
-# CONFIG_KPROBE_EVENT is not set
+CONFIG_UPROBE_EVENT=y
CONFIG_LKDTM=m
CONFIG_TEST_LIST_SORT=y
CONFIG_KPROBES_SANITY_TEST=y
CONFIG_INTERVAL_TREE_TEST=m
CONFIG_PERCPU_TEST=m
CONFIG_ATOMIC64_SELFTEST=y
+CONFIG_TEST_STRING_HELPERS=y
+CONFIG_TEST_KSTRTOX=y
CONFIG_DMA_API_DEBUG=y
+CONFIG_TEST_BPF=m
# CONFIG_STRICT_DEVMEM is not set
CONFIG_S390_PTDUMP=y
CONFIG_ENCRYPTED_KEYS=m
CONFIG_X509_CERTIFICATE_PARSER=m
CONFIG_CRC7=m
CONFIG_CRC8=m
-CONFIG_XZ_DEC_X86=y
-CONFIG_XZ_DEC_POWERPC=y
-CONFIG_XZ_DEC_IA64=y
-CONFIG_XZ_DEC_ARM=y
-CONFIG_XZ_DEC_ARMTHUMB=y
-CONFIG_XZ_DEC_SPARC=y
CONFIG_CORDIC=m
CONFIG_CMM=m
CONFIG_APPLDATA_BASE=y
CONFIG_MODULE_FORCE_UNLOAD=y
CONFIG_MODVERSIONS=y
CONFIG_MODULE_SRCVERSION_ALL=y
-CONFIG_BLK_DEV_INTEGRITY=y
CONFIG_BLK_DEV_THROTTLING=y
CONFIG_PARTITION_ADVANCED=y
CONFIG_IBM_PARTITION=y
CONFIG_NFT_CHAIN_ROUTE_IPV4=m
CONFIG_NFT_CHAIN_NAT_IPV4=m
CONFIG_NF_TABLES_ARP=m
+CONFIG_NF_NAT_IPV4=m
CONFIG_IP_NF_IPTABLES=m
CONFIG_IP_NF_MATCH_AH=m
CONFIG_IP_NF_MATCH_ECN=m
CONFIG_IP_NF_MATCH_TTL=m
CONFIG_IP_NF_FILTER=m
CONFIG_IP_NF_TARGET_REJECT=m
-CONFIG_IP_NF_TARGET_ULOG=m
-CONFIG_NF_NAT_IPV4=m
-CONFIG_IP_NF_TARGET_MASQUERADE=m
-CONFIG_IP_NF_TARGET_NETMAP=m
-CONFIG_IP_NF_TARGET_REDIRECT=m
CONFIG_IP_NF_MANGLE=m
CONFIG_IP_NF_TARGET_CLUSTERIP=m
CONFIG_IP_NF_TARGET_ECN=m
CONFIG_NF_TABLES_IPV6=m
CONFIG_NFT_CHAIN_ROUTE_IPV6=m
CONFIG_NFT_CHAIN_NAT_IPV6=m
+CONFIG_NF_NAT_IPV6=m
CONFIG_IP6_NF_IPTABLES=m
CONFIG_IP6_NF_MATCH_AH=m
CONFIG_IP6_NF_MATCH_EUI64=m
CONFIG_IP6_NF_MANGLE=m
CONFIG_IP6_NF_RAW=m
CONFIG_IP6_NF_SECURITY=m
-CONFIG_NF_NAT_IPV6=m
-CONFIG_IP6_NF_TARGET_MASQUERADE=m
-CONFIG_IP6_NF_TARGET_NPT=m
CONFIG_NF_TABLES_BRIDGE=m
CONFIG_NET_SCTPPROBE=m
CONFIG_RDS=m
CONFIG_CHR_DEV_SG=y
CONFIG_CHR_DEV_SCH=m
CONFIG_SCSI_ENCLOSURE=m
-CONFIG_SCSI_MULTI_LUN=y
CONFIG_SCSI_CONSTANTS=y
CONFIG_SCSI_LOGGING=y
CONFIG_SCSI_SPI_ATTRS=m
+CONFIG_SCSI_FC_ATTRS=y
CONFIG_SCSI_SAS_LIBSAS=m
CONFIG_SCSI_SRP_ATTRS=m
CONFIG_ISCSI_TCP=m
-CONFIG_LIBFCOE=m
CONFIG_SCSI_DEBUG=m
CONFIG_ZFCP=y
CONFIG_SCSI_VIRTIO=m
CONFIG_NLMON=m
CONFIG_VHOST_NET=m
# CONFIG_NET_VENDOR_ARC is not set
-# CONFIG_NET_CADENCE is not set
# CONFIG_NET_VENDOR_CHELSIO is not set
# CONFIG_NET_VENDOR_INTEL is not set
# CONFIG_NET_VENDOR_MARVELL is not set
CONFIG_JFS_POSIX_ACL=y
CONFIG_JFS_SECURITY=y
CONFIG_JFS_STATISTICS=y
-CONFIG_XFS_FS=m
+CONFIG_XFS_FS=y
CONFIG_XFS_QUOTA=y
CONFIG_XFS_POSIX_ACL=y
CONFIG_XFS_RT=y
CONFIG_GFS2_FS=m
CONFIG_OCFS2_FS=m
-CONFIG_BTRFS_FS=m
+CONFIG_BTRFS_FS=y
CONFIG_BTRFS_FS_POSIX_ACL=y
CONFIG_NILFS2_FS=m
CONFIG_FANOTIFY=y
CONFIG_X509_CERTIFICATE_PARSER=m
CONFIG_CRC7=m
CONFIG_CRC8=m
-CONFIG_XZ_DEC_X86=y
-CONFIG_XZ_DEC_POWERPC=y
-CONFIG_XZ_DEC_IA64=y
-CONFIG_XZ_DEC_ARM=y
-CONFIG_XZ_DEC_ARMTHUMB=y
-CONFIG_XZ_DEC_SPARC=y
CONFIG_CORDIC=m
CONFIG_CMM=m
CONFIG_APPLDATA_BASE=y
CONFIG_MODULE_FORCE_UNLOAD=y
CONFIG_MODVERSIONS=y
CONFIG_MODULE_SRCVERSION_ALL=y
-CONFIG_BLK_DEV_INTEGRITY=y
CONFIG_BLK_DEV_THROTTLING=y
CONFIG_PARTITION_ADVANCED=y
CONFIG_IBM_PARTITION=y
CONFIG_NFT_CHAIN_ROUTE_IPV4=m
CONFIG_NFT_CHAIN_NAT_IPV4=m
CONFIG_NF_TABLES_ARP=m
+CONFIG_NF_NAT_IPV4=m
CONFIG_IP_NF_IPTABLES=m
CONFIG_IP_NF_MATCH_AH=m
CONFIG_IP_NF_MATCH_ECN=m
CONFIG_IP_NF_MATCH_TTL=m
CONFIG_IP_NF_FILTER=m
CONFIG_IP_NF_TARGET_REJECT=m
-CONFIG_IP_NF_TARGET_ULOG=m
-CONFIG_NF_NAT_IPV4=m
-CONFIG_IP_NF_TARGET_MASQUERADE=m
-CONFIG_IP_NF_TARGET_NETMAP=m
-CONFIG_IP_NF_TARGET_REDIRECT=m
CONFIG_IP_NF_MANGLE=m
CONFIG_IP_NF_TARGET_CLUSTERIP=m
CONFIG_IP_NF_TARGET_ECN=m
CONFIG_NF_TABLES_IPV6=m
CONFIG_NFT_CHAIN_ROUTE_IPV6=m
CONFIG_NFT_CHAIN_NAT_IPV6=m
+CONFIG_NF_NAT_IPV6=m
CONFIG_IP6_NF_IPTABLES=m
CONFIG_IP6_NF_MATCH_AH=m
CONFIG_IP6_NF_MATCH_EUI64=m
CONFIG_IP6_NF_MANGLE=m
CONFIG_IP6_NF_RAW=m
CONFIG_IP6_NF_SECURITY=m
-CONFIG_NF_NAT_IPV6=m
-CONFIG_IP6_NF_TARGET_MASQUERADE=m
-CONFIG_IP6_NF_TARGET_NPT=m
CONFIG_NF_TABLES_BRIDGE=m
CONFIG_NET_SCTPPROBE=m
CONFIG_RDS=m
CONFIG_CHR_DEV_SG=y
CONFIG_CHR_DEV_SCH=m
CONFIG_SCSI_ENCLOSURE=m
-CONFIG_SCSI_MULTI_LUN=y
CONFIG_SCSI_CONSTANTS=y
CONFIG_SCSI_LOGGING=y
CONFIG_SCSI_SPI_ATTRS=m
+CONFIG_SCSI_FC_ATTRS=y
CONFIG_SCSI_SAS_LIBSAS=m
CONFIG_SCSI_SRP_ATTRS=m
CONFIG_ISCSI_TCP=m
-CONFIG_LIBFCOE=m
CONFIG_SCSI_DEBUG=m
CONFIG_ZFCP=y
CONFIG_SCSI_VIRTIO=m
CONFIG_NLMON=m
CONFIG_VHOST_NET=m
# CONFIG_NET_VENDOR_ARC is not set
-# CONFIG_NET_CADENCE is not set
# CONFIG_NET_VENDOR_CHELSIO is not set
# CONFIG_NET_VENDOR_INTEL is not set
# CONFIG_NET_VENDOR_MARVELL is not set
CONFIG_JFS_POSIX_ACL=y
CONFIG_JFS_SECURITY=y
CONFIG_JFS_STATISTICS=y
-CONFIG_XFS_FS=m
+CONFIG_XFS_FS=y
CONFIG_XFS_QUOTA=y
CONFIG_XFS_POSIX_ACL=y
CONFIG_XFS_RT=y
CONFIG_GFS2_FS=m
CONFIG_OCFS2_FS=m
-CONFIG_BTRFS_FS=m
+CONFIG_BTRFS_FS=y
CONFIG_BTRFS_FS_POSIX_ACL=y
CONFIG_NILFS2_FS=m
CONFIG_FANOTIFY=y
CONFIG_RCU_TORTURE_TEST=m
CONFIG_RCU_CPU_STALL_TIMEOUT=60
CONFIG_LATENCYTOP=y
+CONFIG_SCHED_TRACER=y
+CONFIG_FTRACE_SYSCALLS=y
+CONFIG_STACK_TRACER=y
CONFIG_BLK_DEV_IO_TRACE=y
-# CONFIG_KPROBE_EVENT is not set
+CONFIG_UPROBE_EVENT=y
CONFIG_LKDTM=m
CONFIG_PERCPU_TEST=m
CONFIG_ATOMIC64_SELFTEST=y
CONFIG_X509_CERTIFICATE_PARSER=m
CONFIG_CRC7=m
CONFIG_CRC8=m
-CONFIG_XZ_DEC_X86=y
-CONFIG_XZ_DEC_POWERPC=y
-CONFIG_XZ_DEC_IA64=y
-CONFIG_XZ_DEC_ARM=y
-CONFIG_XZ_DEC_ARMTHUMB=y
-CONFIG_XZ_DEC_SPARC=y
CONFIG_CORDIC=m
CONFIG_CMM=m
CONFIG_APPLDATA_BASE=y
CONFIG_CRASH_DUMP=y
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
# CONFIG_SECCOMP is not set
-# CONFIG_IUCV is not set
CONFIG_NET=y
+# CONFIG_IUCV is not set
CONFIG_ATM=y
CONFIG_ATM_LANE=y
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_SCSI_ENCLOSURE=y
-CONFIG_SCSI_MULTI_LUN=y
CONFIG_SCSI_CONSTANTS=y
CONFIG_SCSI_LOGGING=y
+CONFIG_SCSI_FC_ATTRS=y
CONFIG_SCSI_SRP_ATTRS=y
CONFIG_ZFCP=y
# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
CONFIG_RCU_CPU_STALL_TIMEOUT=60
# CONFIG_FTRACE is not set
# CONFIG_STRICT_DEVMEM is not set
-CONFIG_XZ_DEC_X86=y
-CONFIG_XZ_DEC_POWERPC=y
-CONFIG_XZ_DEC_IA64=y
-CONFIG_XZ_DEC_ARM=y
-CONFIG_XZ_DEC_ARMTHUMB=y
-CONFIG_XZ_DEC_SPARC=y
# CONFIG_PFAULT is not set
# CONFIG_S390_HYPFS_FS is not set
# CONFIG_VIRTUALIZATION is not set
CONFIG_BLK_DEV_SR=y
CONFIG_BLK_DEV_SR_VENDOR=y
CONFIG_CHR_DEV_SG=y
-CONFIG_SCSI_MULTI_LUN=y
CONFIG_SCSI_CONSTANTS=y
CONFIG_SCSI_LOGGING=y
CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_SCSI_FC_ATTRS=y
CONFIG_ZFCP=y
CONFIG_SCSI_VIRTIO=y
CONFIG_NETDEVICES=y
CONFIG_CRYPTO_XCBC=m
CONFIG_CRYPTO_VMAC=m
CONFIG_CRYPTO_CRC32=m
-CONFIG_CRYPTO_CRCT10DIF=m
CONFIG_CRYPTO_MD4=m
CONFIG_CRYPTO_MICHAEL_MIC=m
CONFIG_CRYPTO_RMD128=m
CONFIG_CRYPTO_RMD160=m
CONFIG_CRYPTO_RMD256=m
CONFIG_CRYPTO_RMD320=m
-CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA256=y
CONFIG_CRYPTO_SHA512=m
CONFIG_CRYPTO_TGR192=m
CONFIG_CRYPTO_WP512=m
{
struct ftrace_graph_ent trace;
+ if (unlikely(ftrace_graph_is_dead()))
+ goto out;
if (unlikely(atomic_read(¤t->tracing_graph_pause)))
goto out;
ip = (ip & PSW_ADDR_INSN) - MCOUNT_INSN_SIZE;
*/
local_irq_save(flags);
local_mcck_disable();
- /*
- * Ummm... Does this make sense at all? Copying the percpu struct
- * and then zapping it one statement later?
- */
- memcpy(&mcck, this_cpu_ptr(&cpu_mcck), sizeof(mcck));
- memset(&mcck, 0, sizeof(struct mcck_struct));
+ mcck = *this_cpu_ptr(&cpu_mcck);
+ memset(this_cpu_ptr(&cpu_mcck), 0, sizeof(mcck));
clear_cpu_flag(CIF_MCCK_PENDING);
local_mcck_enable();
local_irq_restore(flags);
.type __kernel_clock_gettime,@function
__kernel_clock_gettime:
.cfi_startproc
+ ahi %r15,-16
basr %r5,0
0: al %r5,21f-0b(%r5) /* get &_vdso_data */
chi %r2,__CLOCK_REALTIME_COARSE
1: l %r4,__VDSO_UPD_COUNT+4(%r5) /* load update counter */
tml %r4,0x0001 /* pending update ? loop */
jnz 1b
- stcke 24(%r15) /* Store TOD clock */
- lm %r0,%r1,25(%r15)
+ stcke 0(%r15) /* Store TOD clock */
+ lm %r0,%r1,1(%r15)
s %r0,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */
sl %r1,__VDSO_XTIME_STAMP+4(%r5)
brc 3,2f
8: st %r2,0(%r3) /* store tp->tv_sec */
st %r1,4(%r3) /* store tp->tv_nsec */
lhi %r2,0
+ ahi %r15,16
br %r14
/* CLOCK_MONOTONIC_COARSE */
11: l %r4,__VDSO_UPD_COUNT+4(%r5) /* load update counter */
tml %r4,0x0001 /* pending update ? loop */
jnz 11b
- stcke 24(%r15) /* Store TOD clock */
- lm %r0,%r1,25(%r15)
+ stcke 0(%r15) /* Store TOD clock */
+ lm %r0,%r1,1(%r15)
s %r0,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */
sl %r1,__VDSO_XTIME_STAMP+4(%r5)
brc 3,12f
17: st %r2,0(%r3) /* store tp->tv_sec */
st %r1,4(%r3) /* store tp->tv_nsec */
lhi %r2,0
+ ahi %r15,16
br %r14
/* Fallback to system call */
19: lhi %r1,__NR_clock_gettime
svc 0
+ ahi %r15,16
br %r14
20: .long 1000000000
.type __kernel_gettimeofday,@function
__kernel_gettimeofday:
.cfi_startproc
+ ahi %r15,-16
basr %r5,0
0: al %r5,13f-0b(%r5) /* get &_vdso_data */
1: ltr %r3,%r3 /* check if tz is NULL */
l %r4,__VDSO_UPD_COUNT+4(%r5) /* load update counter */
tml %r4,0x0001 /* pending update ? loop */
jnz 1b
- stcke 24(%r15) /* Store TOD clock */
- lm %r0,%r1,25(%r15)
+ stcke 0(%r15) /* Store TOD clock */
+ lm %r0,%r1,1(%r15)
s %r0,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */
sl %r1,__VDSO_XTIME_STAMP+4(%r5)
brc 3,3f
ahi %r0,-1
3: ms %r0,__VDSO_TK_MULT(%r5) /* * tk->mult */
- st %r0,24(%r15)
+ st %r0,0(%r15)
l %r0,__VDSO_TK_MULT(%r5)
ltr %r1,%r1
mr %r0,%r0
jnm 4f
a %r0,__VDSO_TK_MULT(%r5)
-4: al %r0,24(%r15)
+4: al %r0,0(%r15)
al %r0,__VDSO_XTIME_NSEC(%r5) /* + xtime */
al %r1,__VDSO_XTIME_NSEC+4(%r5)
brc 12,5f
ahi %r0,1
-5: mvc 24(4,%r15),__VDSO_XTIME_SEC+4(%r5)
+5: mvc 0(4,%r15),__VDSO_XTIME_SEC+4(%r5)
cl %r4,__VDSO_UPD_COUNT+4(%r5) /* check update counter */
jne 1b
l %r4,__VDSO_TK_SHIFT(%r5) /* Timekeeper shift */
srdl %r0,0(%r4) /* >> tk->shift */
- l %r4,24(%r15) /* get tv_sec from stack */
+ l %r4,0(%r15) /* get tv_sec from stack */
basr %r5,0
6: ltr %r0,%r0
jnz 7f
9: srl %r0,6
st %r0,4(%r2) /* store tv->tv_usec */
10: slr %r2,%r2
+ ahi %r15,16
br %r14
11: .long 1000000000
12: .long 274877907
.type __kernel_clock_gettime,@function
__kernel_clock_gettime:
.cfi_startproc
+ aghi %r15,-16
larl %r5,_vdso_data
cghi %r2,__CLOCK_REALTIME_COARSE
je 4f
0: lg %r4,__VDSO_UPD_COUNT(%r5) /* load update counter */
tmll %r4,0x0001 /* pending update ? loop */
jnz 0b
- stcke 48(%r15) /* Store TOD clock */
+ stcke 0(%r15) /* Store TOD clock */
lgf %r2,__VDSO_TK_SHIFT(%r5) /* Timekeeper shift */
lg %r0,__VDSO_WTOM_SEC(%r5)
- lg %r1,49(%r15)
+ lg %r1,1(%r15)
sg %r1,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */
msgf %r1,__VDSO_TK_MULT(%r5) /* * tk->mult */
alg %r1,__VDSO_WTOM_NSEC(%r5)
2: stg %r0,0(%r3) /* store tp->tv_sec */
stg %r1,8(%r3) /* store tp->tv_nsec */
lghi %r2,0
+ aghi %r15,16
br %r14
/* CLOCK_MONOTONIC_COARSE */
5: lg %r4,__VDSO_UPD_COUNT(%r5) /* load update counter */
tmll %r4,0x0001 /* pending update ? loop */
jnz 5b
- stcke 48(%r15) /* Store TOD clock */
+ stcke 0(%r15) /* Store TOD clock */
lgf %r2,__VDSO_TK_SHIFT(%r5) /* Timekeeper shift */
- lg %r1,49(%r15)
+ lg %r1,1(%r15)
sg %r1,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */
msgf %r1,__VDSO_TK_MULT(%r5) /* * tk->mult */
alg %r1,__VDSO_XTIME_NSEC(%r5) /* + tk->xtime_nsec */
7: stg %r0,0(%r3) /* store tp->tv_sec */
stg %r1,8(%r3) /* store tp->tv_nsec */
lghi %r2,0
+ aghi %r15,16
br %r14
/* CLOCK_THREAD_CPUTIME_ID for this thread */
slgr %r4,%r0 /* r4 = tv_nsec */
stg %r4,8(%r3)
lghi %r2,0
+ aghi %r15,16
br %r14
/* Fallback to system call */
12: lghi %r1,__NR_clock_gettime
svc 0
+ aghi %r15,16
br %r14
13: .quad 1000000000
.type __kernel_gettimeofday,@function
__kernel_gettimeofday:
.cfi_startproc
+ aghi %r15,-16
larl %r5,_vdso_data
0: ltgr %r3,%r3 /* check if tz is NULL */
je 1f
lg %r4,__VDSO_UPD_COUNT(%r5) /* load update counter */
tmll %r4,0x0001 /* pending update ? loop */
jnz 0b
- stcke 48(%r15) /* Store TOD clock */
- lg %r1,49(%r15)
+ stcke 0(%r15) /* Store TOD clock */
+ lg %r1,1(%r15)
sg %r1,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */
msgf %r1,__VDSO_TK_MULT(%r5) /* * tk->mult */
alg %r1,__VDSO_XTIME_NSEC(%r5) /* + tk->xtime_nsec */
srlg %r0,%r0,6
stg %r0,8(%r2) /* store tv->tv_usec */
4: lghi %r2,0
+ aghi %r15,16
br %r14
5: .quad 1000000000
.long 274877907
clock = S390_lowcore.last_update_clock;
asm volatile(
" stpt %0\n" /* Store current cpu timer value */
+#ifdef CONFIG_HAVE_MARCH_Z9_109_FEATURES
+ " stckf %1" /* Store current tod clock value */
+#else
" stck %1" /* Store current tod clock value */
+#endif
: "=m" (S390_lowcore.last_update_timer),
"=m" (S390_lowcore.last_update_clock));
S390_lowcore.system_timer += timer - S390_lowcore.last_update_timer;
int atomic_add_return(int, atomic_t *);
int atomic_cmpxchg(atomic_t *, int, int);
-#define atomic_xchg(v, new) (xchg(&((v)->counter), new))
+int atomic_xchg(atomic_t *, int);
int __atomic_add_unless(atomic_t *, int, int);
void atomic_set(atomic_t *, int);
#ifndef __ARCH_SPARC_CMPXCHG__
#define __ARCH_SPARC_CMPXCHG__
-static inline unsigned long xchg_u32(__volatile__ unsigned long *m, unsigned long val)
-{
- __asm__ __volatile__("swap [%2], %0"
- : "=&r" (val)
- : "0" (val), "r" (m)
- : "memory");
- return val;
-}
-
+unsigned long __xchg_u32(volatile u32 *m, u32 new);
void __xchg_called_with_bad_pointer(void);
static inline unsigned long __xchg(unsigned long x, __volatile__ void * ptr, int size)
{
switch (size) {
case 4:
- return xchg_u32(ptr, x);
+ return __xchg_u32(ptr, x);
}
__xchg_called_with_bad_pointer();
return x;
#define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f)
#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h)
+static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
+ enum dma_data_direction dir)
+{
+ /* Since dma_{alloc,free}_noncoherent() allocated coherent memory, this
+ * routine can be a nop.
+ */
+}
+
extern struct dma_map_ops *dma_ops;
extern struct dma_map_ops *leon_dma_ops;
extern struct dma_map_ops pci32_dma_ops;
{
__u16 ret;
- __asm__ __volatile__ ("lduha [%1] %2, %0"
+ __asm__ __volatile__ ("lduha [%2] %3, %0"
: "=r" (ret)
- : "r" (addr), "i" (ASI_PL));
+ : "m" (*addr), "r" (addr), "i" (ASI_PL));
return ret;
}
#define __arch_swab16p __arch_swab16p
{
__u32 ret;
- __asm__ __volatile__ ("lduwa [%1] %2, %0"
+ __asm__ __volatile__ ("lduwa [%2] %3, %0"
: "=r" (ret)
- : "r" (addr), "i" (ASI_PL));
+ : "m" (*addr), "r" (addr), "i" (ASI_PL));
return ret;
}
#define __arch_swab32p __arch_swab32p
{
__u64 ret;
- __asm__ __volatile__ ("ldxa [%1] %2, %0"
+ __asm__ __volatile__ ("ldxa [%2] %3, %0"
: "=r" (ret)
- : "r" (addr), "i" (ASI_PL));
+ : "m" (*addr), "r" (addr), "i" (ASI_PL));
return ret;
}
#define __arch_swab64p __arch_swab64p
{
unsigned long csr_reg, csr, csr_error_bits;
irqreturn_t ret = IRQ_NONE;
- u16 stat;
+ u32 stat;
csr_reg = pbm->pbm_regs + SCHIZO_PCI_CTRL;
csr = upa_readq(csr_reg);
pbm->name);
ret = IRQ_HANDLED;
}
- pci_read_config_word(pbm->pci_bus->self, PCI_STATUS, &stat);
+ pbm->pci_ops->read(pbm->pci_bus, 0, PCI_STATUS, 2, &stat);
if (stat & (PCI_STATUS_PARITY |
PCI_STATUS_SIG_TARGET_ABORT |
PCI_STATUS_REC_TARGET_ABORT |
PCI_STATUS_SIG_SYSTEM_ERROR)) {
printk("%s: PCI bus error, PCI_STATUS[%04x]\n",
pbm->name, stat);
- pci_write_config_word(pbm->pci_bus->self, PCI_STATUS, 0xffff);
+ pbm->pci_ops->write(pbm->pci_bus, 0, PCI_STATUS, 2, 0xffff);
ret = IRQ_HANDLED;
}
return ret;
void __irq_entry smp_call_function_client(int irq, struct pt_regs *regs)
{
clear_softint(1 << irq);
+ irq_enter();
generic_smp_call_function_interrupt();
+ irq_exit();
}
void __irq_entry smp_call_function_single_client(int irq, struct pt_regs *regs)
{
clear_softint(1 << irq);
+ irq_enter();
generic_smp_call_function_single_interrupt();
+ irq_exit();
}
static void tsb_sync(void *info)
#undef ATOMIC_OP
+int atomic_xchg(atomic_t *v, int new)
+{
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(ATOMIC_HASH(v), flags);
+ ret = v->counter;
+ v->counter = new;
+ spin_unlock_irqrestore(ATOMIC_HASH(v), flags);
+ return ret;
+}
+EXPORT_SYMBOL(atomic_xchg);
+
int atomic_cmpxchg(atomic_t *v, int old, int new)
{
int ret;
return (unsigned long)prev;
}
EXPORT_SYMBOL(__cmpxchg_u32);
+
+unsigned long __xchg_u32(volatile u32 *ptr, u32 new)
+{
+ unsigned long flags;
+ u32 prev;
+
+ spin_lock_irqsave(ATOMIC_HASH(ptr), flags);
+ prev = *ptr;
+ *ptr = new;
+ spin_unlock_irqrestore(ATOMIC_HASH(ptr), flags);
+
+ return (unsigned long)prev;
+}
+EXPORT_SYMBOL(__xchg_u32);
config PERF_EVENTS_INTEL_UNCORE
def_bool y
- depends on PERF_EVENTS && SUP_SUP_INTEL && PCI
+ depends on PERF_EVENTS && CPU_SUP_INTEL && PCI
config OUTPUT_FORMAT
string
suffix-$(CONFIG_KERNEL_LZO) := lzo
suffix-$(CONFIG_KERNEL_LZ4) := lz4
+RUN_SIZE = $(shell $(OBJDUMP) -h vmlinux | \
+ perl $(srctree)/arch/x86/tools/calc_run_size.pl)
quiet_cmd_mkpiggy = MKPIGGY $@
- cmd_mkpiggy = $(obj)/mkpiggy $< > $@ || ( rm -f $@ ; false )
+ cmd_mkpiggy = $(obj)/mkpiggy $< $(RUN_SIZE) > $@ || ( rm -f $@ ; false )
targets += piggy.S
$(obj)/piggy.S: $(obj)/vmlinux.bin.$(suffix-y) $(obj)/mkpiggy FORCE
* Do the decompression, and jump to the new kernel..
*/
/* push arguments for decompress_kernel: */
- pushl $z_output_len /* decompressed length */
+ pushl $z_run_size /* size of kernel with .bss and .brk */
+ pushl $z_output_len /* decompressed length, end of relocs */
leal z_extract_offset_negative(%ebx), %ebp
pushl %ebp /* output address */
pushl $z_input_len /* input_len */
pushl %eax /* heap area */
pushl %esi /* real mode pointer */
call decompress_kernel /* returns kernel location in %eax */
- addl $24, %esp
+ addl $28, %esp
/*
* Jump to the decompressed kernel.
* Do the decompression, and jump to the new kernel..
*/
pushq %rsi /* Save the real mode argument */
+ movq $z_run_size, %r9 /* size of kernel with .bss and .brk */
+ pushq %r9
movq %rsi, %rdi /* real mode address */
leaq boot_heap(%rip), %rsi /* malloc area for uncompression */
leaq input_data(%rip), %rdx /* input_data */
movl $z_input_len, %ecx /* input_len */
movq %rbp, %r8 /* output target address */
- movq $z_output_len, %r9 /* decompressed length */
+ movq $z_output_len, %r9 /* decompressed length, end of relocs */
call decompress_kernel /* returns kernel location in %rax */
+ popq %r9
popq %rsi
/*
unsigned char *input_data,
unsigned long input_len,
unsigned char *output,
- unsigned long output_len)
+ unsigned long output_len,
+ unsigned long run_size)
{
real_mode = rmode;
free_mem_ptr = heap; /* Heap */
free_mem_end_ptr = heap + BOOT_HEAP_SIZE;
- output = choose_kernel_location(input_data, input_len,
- output, output_len);
+ /*
+ * The memory hole needed for the kernel is the larger of either
+ * the entire decompressed kernel plus relocation table, or the
+ * entire decompressed kernel plus .bss and .brk sections.
+ */
+ output = choose_kernel_location(input_data, input_len, output,
+ output_len > run_size ? output_len
+ : run_size);
/* Validate memory location choices. */
if ((unsigned long)output & (MIN_KERNEL_ALIGN - 1))
uint32_t olen;
long ilen;
unsigned long offs;
+ unsigned long run_size;
FILE *f = NULL;
int retval = 1;
- if (argc < 2) {
- fprintf(stderr, "Usage: %s compressed_file\n", argv[0]);
+ if (argc < 3) {
+ fprintf(stderr, "Usage: %s compressed_file run_size\n",
+ argv[0]);
goto bail;
}
offs += olen >> 12; /* Add 8 bytes for each 32K block */
offs += 64*1024 + 128; /* Add 64K + 128 bytes slack */
offs = (offs+4095) & ~4095; /* Round to a 4K boundary */
+ run_size = atoi(argv[2]);
printf(".section \".rodata..compressed\",\"a\",@progbits\n");
printf(".globl z_input_len\n");
/* z_extract_offset_negative allows simplification of head_32.S */
printf(".globl z_extract_offset_negative\n");
printf("z_extract_offset_negative = -0x%lx\n", offs);
+ printf(".globl z_run_size\n");
+ printf("z_run_size = %lu\n", run_size);
printf(".globl input_data, input_data_end\n");
printf("input_data:\n");
#define THREAD_SIZE_ORDER 1
#define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER)
-#define STACKFAULT_STACK 0
#define DOUBLEFAULT_STACK 1
#define NMI_STACK 0
#define DEBUG_STACK 0
#define IRQ_STACK_ORDER 2
#define IRQ_STACK_SIZE (PAGE_SIZE << IRQ_STACK_ORDER)
-#define STACKFAULT_STACK 1
-#define DOUBLEFAULT_STACK 2
-#define NMI_STACK 3
-#define DEBUG_STACK 4
-#define MCE_STACK 5
-#define N_EXCEPTION_STACKS 5 /* hw limit: 7 */
+#define DOUBLEFAULT_STACK 1
+#define NMI_STACK 2
+#define DEBUG_STACK 3
+#define MCE_STACK 4
+#define N_EXCEPTION_STACKS 4 /* hw limit: 7 */
#define PUD_PAGE_SIZE (_AC(1, UL) << PUD_SHIFT)
#define PUD_PAGE_MASK (~(PUD_PAGE_SIZE-1))
}
void cpu_disable_common(void);
+void cpu_die_common(unsigned int cpu);
void native_smp_prepare_boot_cpu(void);
void native_smp_prepare_cpus(unsigned int max_cpus);
void native_smp_cpus_done(unsigned int max_cpus);
/* Only used for 64 bit */
#define _TIF_DO_NOTIFY_MASK \
(_TIF_SIGPENDING | _TIF_MCE_NOTIFY | _TIF_NOTIFY_RESUME | \
- _TIF_USER_RETURN_NOTIFY)
+ _TIF_USER_RETURN_NOTIFY | _TIF_UPROBE)
/* flags to check in __switch_to() */
#define _TIF_WORK_CTXSW \
#ifdef CONFIG_TRACING
asmlinkage void trace_page_fault(void);
+#define trace_stack_segment stack_segment
#define trace_divide_error divide_error
#define trace_bounds bounds
#define trace_invalid_op invalid_op
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M10H_F3) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F3) },
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F3) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_NB_F3) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F3) },
{}
static const struct pci_device_id amd_nb_link_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F4) },
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F4) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_NB_F4) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F4) },
{}
static int __init x86_xsave_setup(char *s)
{
+ if (strlen(s))
+ return 0;
setup_clear_cpu_cap(X86_FEATURE_XSAVE);
setup_clear_cpu_cap(X86_FEATURE_XSAVEOPT);
setup_clear_cpu_cap(X86_FEATURE_XSAVES);
* load_microcode_amd() to save equivalent cpu table and microcode patches in
* kernel heap memory.
*/
-static void apply_ucode_in_initrd(void *ucode, size_t size)
+static void apply_ucode_in_initrd(void *ucode, size_t size, bool save_patch)
{
struct equiv_cpu_entry *eq;
size_t *cont_sz;
u32 *header;
u8 *data, **cont;
+ u8 (*patch)[PATCH_MAX_SIZE];
u16 eq_id = 0;
int offset, left;
u32 rev, eax, ebx, ecx, edx;
new_rev = (u32 *)__pa_nodebug(&ucode_new_rev);
cont_sz = (size_t *)__pa_nodebug(&container_size);
cont = (u8 **)__pa_nodebug(&container);
+ patch = (u8 (*)[PATCH_MAX_SIZE])__pa_nodebug(&amd_ucode_patch);
#else
new_rev = &ucode_new_rev;
cont_sz = &container_size;
cont = &container;
+ patch = &amd_ucode_patch;
#endif
data = ucode;
rev = mc->hdr.patch_id;
*new_rev = rev;
- /* save ucode patch */
- memcpy(amd_ucode_patch, mc,
- min_t(u32, header[1], PATCH_MAX_SIZE));
+ if (save_patch)
+ memcpy(patch, mc,
+ min_t(u32, header[1], PATCH_MAX_SIZE));
}
}
*data = cp.data;
*size = cp.size;
- apply_ucode_in_initrd(cp.data, cp.size);
+ apply_ucode_in_initrd(cp.data, cp.size, true);
}
#ifdef CONFIG_X86_32
size_t *usize;
void **ucode;
- mc = (struct microcode_amd *)__pa(amd_ucode_patch);
+ mc = (struct microcode_amd *)__pa_nodebug(amd_ucode_patch);
if (mc->hdr.patch_id && mc->hdr.processor_rev_id) {
__apply_microcode_amd(mc);
return;
if (!*ucode || !*usize)
return;
- apply_ucode_in_initrd(*ucode, *usize);
+ apply_ucode_in_initrd(*ucode, *usize, false);
}
static void __init collect_cpu_sig_on_bsp(void *arg)
* AP has a different equivalence ID than BSP, looks like
* mixed-steppings silicon so go through the ucode blob anew.
*/
- apply_ucode_in_initrd(ucode_cpio.data, ucode_cpio.size);
+ apply_ucode_in_initrd(ucode_cpio.data, ucode_cpio.size, false);
}
}
#endif
int __init save_microcode_in_initrd_amd(void)
{
unsigned long cont;
+ int retval = 0;
enum ucode_state ret;
+ u8 *cont_va;
u32 eax;
if (!container)
#ifdef CONFIG_X86_32
get_bsp_sig();
- cont = (unsigned long)container;
+ cont = (unsigned long)container;
+ cont_va = __va(container);
#else
/*
* We need the physical address of the container for both bitness since
* boot_params.hdr.ramdisk_image is a physical address.
*/
- cont = __pa(container);
+ cont = __pa(container);
+ cont_va = container;
#endif
/*
if (relocated_ramdisk)
container = (u8 *)(__va(relocated_ramdisk) +
(cont - boot_params.hdr.ramdisk_image));
+ else
+ container = cont_va;
if (ucode_new_rev)
pr_info("microcode: updated early to new patch_level=0x%08x\n",
ret = load_microcode_amd(eax, container, container_size);
if (ret != UCODE_OK)
- return -EINVAL;
+ retval = -EINVAL;
/*
* This will be freed any msec now, stash patches for the current
container = NULL;
container_size = 0;
- return 0;
+ return retval;
}
if (uci->valid && uci->mc)
microcode_ops->apply_microcode(cpu);
+#ifdef CONFIG_X86_64
+ else if (!uci->mc)
+ /*
+ * We might resume and not have applied late microcode but still
+ * have a newer patch stashed from the early loader. We don't
+ * have it in uci->mc so we have to load it the same way we're
+ * applying patches early on the APs.
+ */
+ load_ucode_ap();
+#endif
}
static struct syscore_ops mc_syscore_ops = {
static bool check_loader_disabled_ap(void)
{
#ifdef CONFIG_X86_32
- return __pa_nodebug(dis_ucode_ldr);
+ return *((bool *)__pa_nodebug(&dis_ucode_ldr));
#else
return dis_ucode_ldr;
#endif
.attrs = snbep_uncore_qpi_formats_attr,
};
-#define SNBEP_UNCORE_MSR_OPS_COMMON_INIT() \
- .init_box = snbep_uncore_msr_init_box, \
+#define __SNBEP_UNCORE_MSR_OPS_COMMON_INIT() \
.disable_box = snbep_uncore_msr_disable_box, \
.enable_box = snbep_uncore_msr_enable_box, \
.disable_event = snbep_uncore_msr_disable_event, \
.enable_event = snbep_uncore_msr_enable_event, \
.read_counter = uncore_msr_read_counter
+#define SNBEP_UNCORE_MSR_OPS_COMMON_INIT() \
+ __SNBEP_UNCORE_MSR_OPS_COMMON_INIT(), \
+ .init_box = snbep_uncore_msr_init_box \
+
static struct intel_uncore_ops snbep_uncore_msr_ops = {
SNBEP_UNCORE_MSR_OPS_COMMON_INIT(),
};
.format_group = &hswep_uncore_cbox_format_group,
};
+/*
+ * Write SBOX Initialization register bit by bit to avoid spurious #GPs
+ */
+static void hswep_uncore_sbox_msr_init_box(struct intel_uncore_box *box)
+{
+ unsigned msr = uncore_msr_box_ctl(box);
+
+ if (msr) {
+ u64 init = SNBEP_PMON_BOX_CTL_INT;
+ u64 flags = 0;
+ int i;
+
+ for_each_set_bit(i, (unsigned long *)&init, 64) {
+ flags |= (1ULL << i);
+ wrmsrl(msr, flags);
+ }
+ }
+}
+
+static struct intel_uncore_ops hswep_uncore_sbox_msr_ops = {
+ __SNBEP_UNCORE_MSR_OPS_COMMON_INIT(),
+ .init_box = hswep_uncore_sbox_msr_init_box
+};
+
static struct attribute *hswep_uncore_sbox_formats_attr[] = {
&format_attr_event.attr,
&format_attr_umask.attr,
.event_mask = HSWEP_S_MSR_PMON_RAW_EVENT_MASK,
.box_ctl = HSWEP_S0_MSR_PMON_BOX_CTL,
.msr_offset = HSWEP_SBOX_MSR_OFFSET,
- .ops = &snbep_uncore_msr_ops,
+ .ops = &hswep_uncore_sbox_msr_ops,
.format_group = &hswep_uncore_sbox_format_group,
};
SNBEP_UNCORE_PCI_COMMON_INIT(),
};
+static unsigned hswep_uncore_irp_ctrs[] = {0xa0, 0xa8, 0xb0, 0xb8};
+
+static u64 hswep_uncore_irp_read_counter(struct intel_uncore_box *box, struct perf_event *event)
+{
+ struct pci_dev *pdev = box->pci_dev;
+ struct hw_perf_event *hwc = &event->hw;
+ u64 count = 0;
+
+ pci_read_config_dword(pdev, hswep_uncore_irp_ctrs[hwc->idx], (u32 *)&count);
+ pci_read_config_dword(pdev, hswep_uncore_irp_ctrs[hwc->idx] + 4, (u32 *)&count + 1);
+
+ return count;
+}
+
static struct intel_uncore_ops hswep_uncore_irp_ops = {
.init_box = snbep_uncore_pci_init_box,
.disable_box = snbep_uncore_pci_disable_box,
.enable_box = snbep_uncore_pci_enable_box,
.disable_event = ivbep_uncore_irp_disable_event,
.enable_event = ivbep_uncore_irp_enable_event,
- .read_counter = ivbep_uncore_irp_read_counter,
+ .read_counter = hswep_uncore_irp_read_counter,
};
static struct intel_uncore_type hswep_uncore_irp = {
[ DEBUG_STACK-1 ] = "#DB",
[ NMI_STACK-1 ] = "NMI",
[ DOUBLEFAULT_STACK-1 ] = "#DF",
- [ STACKFAULT_STACK-1 ] = "#SS",
[ MCE_STACK-1 ] = "#MC",
#if DEBUG_STKSZ > EXCEPTION_STKSZ
[ N_EXCEPTION_STACKS ...
jnz native_irq_return_ldt
#endif
+.global native_irq_return_iret
native_irq_return_iret:
+ /*
+ * This may fault. Non-paranoid faults on return to userspace are
+ * handled by fixup_bad_iret. These include #SS, #GP, and #NP.
+ * Double-faults due to espfix64 are handled in do_double_fault.
+ * Other faults here are fatal.
+ */
iretq
- _ASM_EXTABLE(native_irq_return_iret, bad_iret)
#ifdef CONFIG_X86_ESPFIX64
native_irq_return_ldt:
jmp native_irq_return_iret
#endif
- .section .fixup,"ax"
-bad_iret:
- /*
- * The iret traps when the %cs or %ss being restored is bogus.
- * We've lost the original trap vector and error code.
- * #GPF is the most likely one to get for an invalid selector.
- * So pretend we completed the iret and took the #GPF in user mode.
- *
- * We are now running with the kernel GS after exception recovery.
- * But error_entry expects us to have user GS to match the user %cs,
- * so swap back.
- */
- pushq $0
-
- SWAPGS
- jmp general_protection
-
- .previous
-
/* edi: workmask, edx: work */
retint_careful:
CFI_RESTORE_STATE
CFI_ENDPROC
END(common_interrupt)
- /*
- * If IRET takes a fault on the espfix stack, then we
- * end up promoting it to a doublefault. In that case,
- * modify the stack to make it look like we just entered
- * the #GP handler from user space, similar to bad_iret.
- */
-#ifdef CONFIG_X86_ESPFIX64
- ALIGN
-__do_double_fault:
- XCPT_FRAME 1 RDI+8
- movq RSP(%rdi),%rax /* Trap on the espfix stack? */
- sarq $PGDIR_SHIFT,%rax
- cmpl $ESPFIX_PGD_ENTRY,%eax
- jne do_double_fault /* No, just deliver the fault */
- cmpl $__KERNEL_CS,CS(%rdi)
- jne do_double_fault
- movq RIP(%rdi),%rax
- cmpq $native_irq_return_iret,%rax
- jne do_double_fault /* This shouldn't happen... */
- movq PER_CPU_VAR(kernel_stack),%rax
- subq $(6*8-KERNEL_STACK_OFFSET),%rax /* Reset to original stack */
- movq %rax,RSP(%rdi)
- movq $0,(%rax) /* Missing (lost) #GP error code */
- movq $general_protection,RIP(%rdi)
- retq
- CFI_ENDPROC
-END(__do_double_fault)
-#else
-# define __do_double_fault do_double_fault
-#endif
-
/*
* APIC interrupts.
*/
idtentry bounds do_bounds has_error_code=0
idtentry invalid_op do_invalid_op has_error_code=0
idtentry device_not_available do_device_not_available has_error_code=0
-idtentry double_fault __do_double_fault has_error_code=1 paranoid=1
+idtentry double_fault do_double_fault has_error_code=1 paranoid=1
idtentry coprocessor_segment_overrun do_coprocessor_segment_overrun has_error_code=0
idtentry invalid_TSS do_invalid_TSS has_error_code=1
idtentry segment_not_present do_segment_not_present has_error_code=1
idtentry debug do_debug has_error_code=0 paranoid=1 shift_ist=DEBUG_STACK
idtentry int3 do_int3 has_error_code=0 paranoid=1 shift_ist=DEBUG_STACK
-idtentry stack_segment do_stack_segment has_error_code=1 paranoid=1
+idtentry stack_segment do_stack_segment has_error_code=1
#ifdef CONFIG_XEN
idtentry xen_debug do_debug has_error_code=0
idtentry xen_int3 do_int3 has_error_code=0
/*
* There are two places in the kernel that can potentially fault with
- * usergs. Handle them here. The exception handlers after iret run with
- * kernel gs again, so don't set the user space flag. B stepping K8s
- * sometimes report an truncated RIP for IRET exceptions returning to
- * compat mode. Check for these here too.
+ * usergs. Handle them here. B stepping K8s sometimes report a
+ * truncated RIP for IRET exceptions returning to compat mode. Check
+ * for these here too.
*/
error_kernelspace:
CFI_REL_OFFSET rcx, RCX+8
incl %ebx
leaq native_irq_return_iret(%rip),%rcx
cmpq %rcx,RIP+8(%rsp)
- je error_swapgs
+ je error_bad_iret
movl %ecx,%eax /* zero extend */
cmpq %rax,RIP+8(%rsp)
je bstep_iret
bstep_iret:
/* Fix truncated RIP */
movq %rcx,RIP+8(%rsp)
- jmp error_swapgs
+ /* fall through */
+
+error_bad_iret:
+ SWAPGS
+ mov %rsp,%rdi
+ call fixup_bad_iret
+ mov %rax,%rsp
+ decl %ebx /* Return to usergs */
+ jmp error_sti
CFI_ENDPROC
END(error_entry)
*/
if (work & _TIF_NOHZ) {
user_exit();
- work &= ~TIF_NOHZ;
+ work &= ~_TIF_NOHZ;
}
#ifdef CONFIG_SECCOMP
numa_remove_cpu(cpu);
}
+static DEFINE_PER_CPU(struct completion, die_complete);
+
void cpu_disable_common(void)
{
int cpu = smp_processor_id();
+ init_completion(&per_cpu(die_complete, smp_processor_id()));
+
remove_siblinginfo(cpu);
/* It's now safe to remove this processor from the online map */
fixup_irqs();
}
-static DEFINE_PER_CPU(struct completion, die_complete);
-
int native_cpu_disable(void)
{
int ret;
return ret;
clear_local_APIC();
- init_completion(&per_cpu(die_complete, smp_processor_id()));
cpu_disable_common();
return 0;
}
+void cpu_die_common(unsigned int cpu)
+{
+ wait_for_completion_timeout(&per_cpu(die_complete, cpu), HZ);
+}
+
void native_cpu_die(unsigned int cpu)
{
/* We don't do anything here: idle task is faking death itself. */
- wait_for_completion_timeout(&per_cpu(die_complete, cpu), HZ);
+
+ cpu_die_common(cpu);
/* They ack this in play_dead() by setting CPU_DEAD */
if (per_cpu(cpu_state, cpu) == CPU_DEAD) {
DO_ERROR(X86_TRAP_OLD_MF, SIGFPE, "coprocessor segment overrun",coprocessor_segment_overrun)
DO_ERROR(X86_TRAP_TS, SIGSEGV, "invalid TSS", invalid_TSS)
DO_ERROR(X86_TRAP_NP, SIGBUS, "segment not present", segment_not_present)
-#ifdef CONFIG_X86_32
DO_ERROR(X86_TRAP_SS, SIGBUS, "stack segment", stack_segment)
-#endif
DO_ERROR(X86_TRAP_AC, SIGBUS, "alignment check", alignment_check)
#ifdef CONFIG_X86_64
/* Runs on IST stack */
-dotraplinkage void do_stack_segment(struct pt_regs *regs, long error_code)
-{
- enum ctx_state prev_state;
-
- prev_state = exception_enter();
- if (notify_die(DIE_TRAP, "stack segment", regs, error_code,
- X86_TRAP_SS, SIGBUS) != NOTIFY_STOP) {
- preempt_conditional_sti(regs);
- do_trap(X86_TRAP_SS, SIGBUS, "stack segment", regs, error_code, NULL);
- preempt_conditional_cli(regs);
- }
- exception_exit(prev_state);
-}
-
dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code)
{
static const char str[] = "double fault";
struct task_struct *tsk = current;
+#ifdef CONFIG_X86_ESPFIX64
+ extern unsigned char native_irq_return_iret[];
+
+ /*
+ * If IRET takes a non-IST fault on the espfix64 stack, then we
+ * end up promoting it to a doublefault. In that case, modify
+ * the stack to make it look like we just entered the #GP
+ * handler from user space, similar to bad_iret.
+ */
+ if (((long)regs->sp >> PGDIR_SHIFT) == ESPFIX_PGD_ENTRY &&
+ regs->cs == __KERNEL_CS &&
+ regs->ip == (unsigned long)native_irq_return_iret)
+ {
+ struct pt_regs *normal_regs = task_pt_regs(current);
+
+ /* Fake a #GP(0) from userspace. */
+ memmove(&normal_regs->ip, (void *)regs->sp, 5*8);
+ normal_regs->orig_ax = 0; /* Missing (lost) #GP error code */
+ regs->ip = (unsigned long)general_protection;
+ regs->sp = (unsigned long)&normal_regs->orig_ax;
+ return;
+ }
+#endif
+
exception_enter();
/* Return not checked because double check cannot be ignored */
notify_die(DIE_TRAP, str, regs, error_code, X86_TRAP_DF, SIGSEGV);
return regs;
}
NOKPROBE_SYMBOL(sync_regs);
+
+struct bad_iret_stack {
+ void *error_entry_ret;
+ struct pt_regs regs;
+};
+
+asmlinkage __visible
+struct bad_iret_stack *fixup_bad_iret(struct bad_iret_stack *s)
+{
+ /*
+ * This is called from entry_64.S early in handling a fault
+ * caused by a bad iret to user mode. To handle the fault
+ * correctly, we want move our stack frame to task_pt_regs
+ * and we want to pretend that the exception came from the
+ * iret target.
+ */
+ struct bad_iret_stack *new_stack =
+ container_of(task_pt_regs(current),
+ struct bad_iret_stack, regs);
+
+ /* Copy the IRET target to the new stack. */
+ memmove(&new_stack->regs.ip, (void *)s->regs.sp, 5*8);
+
+ /* Copy the remainder of the stack from the current stack. */
+ memmove(new_stack, s, offsetof(struct bad_iret_stack, regs.ip));
+
+ BUG_ON(!user_mode_vm(&new_stack->regs));
+ return new_stack;
+}
#endif
/*
set_intr_gate(X86_TRAP_OLD_MF, coprocessor_segment_overrun);
set_intr_gate(X86_TRAP_TS, invalid_TSS);
set_intr_gate(X86_TRAP_NP, segment_not_present);
- set_intr_gate_ist(X86_TRAP_SS, &stack_segment, STACKFAULT_STACK);
+ set_intr_gate(X86_TRAP_SS, stack_segment);
set_intr_gate(X86_TRAP_GP, general_protection);
set_intr_gate(X86_TRAP_SPURIOUS, spurious_interrupt_bug);
set_intr_gate(X86_TRAP_MF, coprocessor_error);
fetch_register_operand(op);
break;
case OpCL:
+ op->type = OP_IMM;
op->bytes = 1;
op->val = reg_read(ctxt, VCPU_REGS_RCX) & 0xff;
break;
rc = decode_imm(ctxt, op, 1, true);
break;
case OpOne:
+ op->type = OP_IMM;
op->bytes = 1;
op->val = 1;
break;
ctxt->memop.bytes = ctxt->op_bytes + 2;
goto mem_common;
case OpES:
+ op->type = OP_IMM;
op->val = VCPU_SREG_ES;
break;
case OpCS:
+ op->type = OP_IMM;
op->val = VCPU_SREG_CS;
break;
case OpSS:
+ op->type = OP_IMM;
op->val = VCPU_SREG_SS;
break;
case OpDS:
+ op->type = OP_IMM;
op->val = VCPU_SREG_DS;
break;
case OpFS:
+ op->type = OP_IMM;
op->val = VCPU_SREG_FS;
break;
case OpGS:
+ op->type = OP_IMM;
op->val = VCPU_SREG_GS;
break;
case OpImplicit:
* kvm mmu, before reclaiming the page, we should
* unmap it from mmu first.
*/
- WARN_ON(!kvm_is_mmio_pfn(pfn) && !page_count(pfn_to_page(pfn)));
+ WARN_ON(!kvm_is_reserved_pfn(pfn) && !page_count(pfn_to_page(pfn)));
if (!shadow_accessed_mask || old_spte & shadow_accessed_mask)
kvm_set_pfn_accessed(pfn);
spte |= PT_PAGE_SIZE_MASK;
if (tdp_enabled)
spte |= kvm_x86_ops->get_mt_mask(vcpu, gfn,
- kvm_is_mmio_pfn(pfn));
+ kvm_is_reserved_pfn(pfn));
if (host_writable)
spte |= SPTE_HOST_WRITEABLE;
* PT_PAGE_TABLE_LEVEL and there would be no adjustment done
* here.
*/
- if (!is_error_noslot_pfn(pfn) && !kvm_is_mmio_pfn(pfn) &&
+ if (!is_error_noslot_pfn(pfn) && !kvm_is_reserved_pfn(pfn) &&
level == PT_PAGE_TABLE_LEVEL &&
PageTransCompound(pfn_to_page(pfn)) &&
!has_wrprotected_page(vcpu->kvm, gfn, PT_DIRECTORY_LEVEL)) {
while (((unsigned long)src & 6) && len >= 2) {
__u16 val16;
- *errp = __get_user(val16, (const __u16 __user *)src);
- if (*errp)
- return isum;
+ if (__get_user(val16, (const __u16 __user *)src))
+ goto out_err;
*(__u16 *)dst = val16;
isum = (__force __wsum)add32_with_carry(
unsigned long end = (unsigned long) &__end_rodata_hpage_align;
unsigned long text_end = PFN_ALIGN(&__stop___ex_table);
unsigned long rodata_end = PFN_ALIGN(&__end_rodata);
- unsigned long all_end = PFN_ALIGN(&_end);
+ unsigned long all_end;
printk(KERN_INFO "Write protecting the kernel read-only data: %luk\n",
(end - start) >> 10);
/*
* The rodata/data/bss/brk section (but not the kernel text!)
* should also be not-executable.
+ *
+ * We align all_end to PMD_SIZE because the existing mapping
+ * is a full PMD. If we would align _brk_end to PAGE_SIZE we
+ * split the PMD and the reminder between _brk_end and the end
+ * of the PMD will remain mapped executable.
+ *
+ * Any PMD which was setup after the one which covers _brk_end
+ * has been zapped already via cleanup_highmem().
*/
+ all_end = roundup((unsigned long)_brk_end, PMD_SIZE);
set_memory_nx(rodata_start, (all_end - rodata_start) >> PAGE_SHIFT);
rodata_test();
--- /dev/null
+#!/usr/bin/perl
+#
+# Calculate the amount of space needed to run the kernel, including room for
+# the .bss and .brk sections.
+#
+# Usage:
+# objdump -h a.out | perl calc_run_size.pl
+use strict;
+
+my $mem_size = 0;
+my $file_offset = 0;
+
+my $sections=" *[0-9]+ \.(?:bss|brk) +";
+while (<>) {
+ if (/^$sections([0-9a-f]+) +(?:[0-9a-f]+ +){2}([0-9a-f]+)/) {
+ my $size = hex($1);
+ my $offset = hex($2);
+ $mem_size += $size;
+ if ($file_offset == 0) {
+ $file_offset = $offset;
+ } elsif ($file_offset != $offset) {
+ # BFD linker shows the same file offset in ELF.
+ # Gold linker shows them as consecutive.
+ next if ($file_offset + $mem_size == $offset + $size);
+
+ printf STDERR "file_offset: 0x%lx\n", $file_offset;
+ printf STDERR "mem_size: 0x%lx\n", $mem_size;
+ printf STDERR "offset: 0x%lx\n", $offset;
+ printf STDERR "size: 0x%lx\n", $size;
+
+ die ".bss and .brk are non-contiguous\n";
+ }
+ }
+}
+
+if ($file_offset == 0) {
+ die "Never found .bss or .brk file offset\n";
+}
+printf("%d\n", $mem_size + $file_offset);
current->state = TASK_UNINTERRUPTIBLE;
schedule_timeout(HZ/10);
}
+
+ cpu_die_common(cpu);
+
xen_smp_intr_free(cpu);
xen_uninit_lock_cpu(cpu);
xen_teardown_timer(cpu);
config XTENSA_PLATFORM_XTFPGA
bool "XTFPGA"
+ select ETHOC if ETHERNET
select SERIAL_CONSOLE
- select ETHOC
select XTENSA_CALIBRATE_CCOUNT
help
XTFPGA is the name of Tensilica board family (LX60, LX110, LX200, ML605).
config BLK_DEV_SIMDISK
tristate "Host file-based simulated block device support"
default n
- depends on XTENSA_PLATFORM_ISS
+ depends on XTENSA_PLATFORM_ISS && BLOCK
help
Create block devices that map to files in the host file system.
Device binding to host file may be changed at runtime via proc
--- /dev/null
+/dts-v1/;
+/include/ "xtfpga.dtsi"
+/include/ "xtfpga-flash-16m.dtsi"
+
+/ {
+ compatible = "cdns,xtensa-lx200";
+ memory@0 {
+ device_type = "memory";
+ reg = <0x00000000 0x06000000>;
+ };
+ pic: pic {
+ compatible = "cdns,xtensa-mx";
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ };
+};
--- /dev/null
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_FHANDLE=y
+CONFIG_IRQ_DOMAIN_DEBUG=y
+CONFIG_NO_HZ_IDLE=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_IRQ_TIME_ACCOUNTING=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+CONFIG_MEMCG=y
+CONFIG_NAMESPACES=y
+CONFIG_SCHED_AUTOGROUP=y
+CONFIG_RELAY=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_EXPERT=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_XTENSA_VARIANT_DC233C=y
+CONFIG_XTENSA_UNALIGNED_USER=y
+CONFIG_PREEMPT=y
+CONFIG_HIGHMEM=y
+# CONFIG_PCI is not set
+CONFIG_XTENSA_PLATFORM_XTFPGA=y
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="earlycon=uart8250,mmio32,0xfd050020,115200n8 console=ttyS0,115200n8 ip=dhcp root=/dev/nfs rw debug"
+CONFIG_USE_OF=y
+CONFIG_BUILTIN_DTB="kc705"
+# CONFIG_COMPACTION is not set
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+# CONFIG_IPV6 is not set
+CONFIG_NETFILTER=y
+# CONFIG_WIRELESS is not set
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+# CONFIG_STANDALONE is not set
+CONFIG_MTD=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_JEDECPROBE=y
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_CFI_AMDSTD=y
+CONFIG_MTD_CFI_STAA=y
+CONFIG_MTD_PHYSMAP_OF=y
+CONFIG_MTD_UBI=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_NETDEVICES=y
+# CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_VENDOR_BROADCOM is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_SAMSUNG is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SMSC is not set
+# CONFIG_NET_VENDOR_STMICRO is not set
+# CONFIG_NET_VENDOR_VIA is not set
+# CONFIG_NET_VENDOR_WIZNET is not set
+CONFIG_MARVELL_PHY=y
+# CONFIG_WLAN is not set
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_SERIO is not set
+CONFIG_SERIAL_8250=y
+# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_HW_RANDOM=y
+# CONFIG_HWMON is not set
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_NOWAYOUT=y
+CONFIG_SOFT_WATCHDOG=y
+# CONFIG_VGA_CONSOLE is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS=y
+CONFIG_FANOTIFY=y
+CONFIG_VFAT_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_UBIFS_FS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_SWAP=y
+CONFIG_ROOT_NFS=y
+CONFIG_SUNRPC_DEBUG=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_DEBUG_INFO=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_LOCKUP_DETECTOR=y
+# CONFIG_SCHED_DEBUG is not set
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_DEBUG_RT_MUTEXES=y
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_ATOMIC_SLEEP=y
+CONFIG_STACKTRACE=y
+CONFIG_RCU_TRACE=y
+# CONFIG_FTRACE is not set
+CONFIG_LD_NO_RELAX=y
+# CONFIG_S32C1I_SELFTEST is not set
+CONFIG_CRYPTO_ANSI_CPRNG=y
--- /dev/null
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_FHANDLE=y
+CONFIG_IRQ_DOMAIN_DEBUG=y
+CONFIG_NO_HZ_IDLE=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_IRQ_TIME_ACCOUNTING=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+CONFIG_MEMCG=y
+CONFIG_NAMESPACES=y
+CONFIG_SCHED_AUTOGROUP=y
+CONFIG_RELAY=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_EXPERT=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_XTENSA_VARIANT_CUSTOM=y
+CONFIG_XTENSA_VARIANT_CUSTOM_NAME="test_mmuhifi_c3"
+CONFIG_XTENSA_UNALIGNED_USER=y
+CONFIG_PREEMPT=y
+CONFIG_HAVE_SMP=y
+CONFIG_SMP=y
+CONFIG_HOTPLUG_CPU=y
+# CONFIG_INITIALIZE_XTENSA_MMU_INSIDE_VMLINUX is not set
+# CONFIG_PCI is not set
+CONFIG_XTENSA_PLATFORM_XTFPGA=y
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="earlycon=uart8250,mmio32,0xfd050020,115200n8 console=ttyS0,115200n8 ip=dhcp root=/dev/nfs rw debug"
+CONFIG_USE_OF=y
+CONFIG_BUILTIN_DTB="lx200mx"
+# CONFIG_COMPACTION is not set
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+# CONFIG_IPV6 is not set
+CONFIG_NETFILTER=y
+# CONFIG_WIRELESS is not set
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+# CONFIG_STANDALONE is not set
+CONFIG_MTD=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_JEDECPROBE=y
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_CFI_AMDSTD=y
+CONFIG_MTD_CFI_STAA=y
+CONFIG_MTD_PHYSMAP_OF=y
+CONFIG_MTD_UBI=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_NETDEVICES=y
+# CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_VENDOR_BROADCOM is not set
+# CONFIG_NET_VENDOR_INTEL is not set
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_SAMSUNG is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SMSC is not set
+# CONFIG_NET_VENDOR_STMICRO is not set
+# CONFIG_NET_VENDOR_VIA is not set
+# CONFIG_NET_VENDOR_WIZNET is not set
+CONFIG_MARVELL_PHY=y
+# CONFIG_WLAN is not set
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_SERIO is not set
+CONFIG_SERIAL_8250=y
+# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_HW_RANDOM=y
+# CONFIG_HWMON is not set
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_NOWAYOUT=y
+CONFIG_SOFT_WATCHDOG=y
+# CONFIG_VGA_CONSOLE is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS=y
+CONFIG_FANOTIFY=y
+CONFIG_VFAT_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_UBIFS_FS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_SWAP=y
+CONFIG_ROOT_NFS=y
+CONFIG_SUNRPC_DEBUG=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_DEBUG_INFO=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_VM=y
+CONFIG_LOCKUP_DETECTOR=y
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_DEBUG_RT_MUTEXES=y
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_ATOMIC_SLEEP=y
+CONFIG_STACKTRACE=y
+CONFIG_RCU_TRACE=y
+# CONFIG_FTRACE is not set
+CONFIG_LD_NO_RELAX=y
+# CONFIG_S32C1I_SELFTEST is not set
+CONFIG_CRYPTO_ANSI_CPRNG=y
static inline pte_t pte_mkspecial(pte_t pte)
{ return pte; }
+#define pgprot_noncached(prot) (__pgprot(pgprot_val(prot) & ~_PAGE_CA_MASK))
+
/*
* Conversion functions: convert a page and protection to a page entry,
* and a page entry and page directory to the page they refer to.
#define __NR_pivot_root 175
__SYSCALL(175, sys_pivot_root, 2)
#define __NR_umount 176
-__SYSCALL(176, sys_umount, 2)
+__SYSCALL(176, sys_oldumount, 1)
+#define __ARCH_WANT_SYS_OLDUMOUNT
#define __NR_swapoff 177
__SYSCALL(177, sys_swapoff, 1)
#define __NR_sync 178
#define __NR_renameat2 336
__SYSCALL(336, sys_renameat2, 5)
-#define __NR_syscall_count 337
+#define __NR_seccomp 337
+__SYSCALL(337, sys_seccomp, 3)
+#define __NR_getrandom 338
+__SYSCALL(338, sys_getrandom, 3)
+#define __NR_memfd_create 339
+__SYSCALL(339, sys_memfd_create, 2)
+
+#define __NR_syscall_count 340
/*
* sysxtensa syscall handler
{
struct blk_integrity *bi = bdev_get_integrity(bio->bi_bdev);
struct blk_integrity_iter iter;
- struct bio_vec *bv;
+ struct bvec_iter bviter;
+ struct bio_vec bv;
struct bio_integrity_payload *bip = bio_integrity(bio);
- unsigned int i, ret = 0;
+ unsigned int ret = 0;
void *prot_buf = page_address(bip->bip_vec->bv_page) +
bip->bip_vec->bv_offset;
iter.seed = bip_get_seed(bip);
iter.prot_buf = prot_buf;
- bio_for_each_segment_all(bv, bio, i) {
- void *kaddr = kmap_atomic(bv->bv_page);
+ bio_for_each_segment(bv, bio, bviter) {
+ void *kaddr = kmap_atomic(bv.bv_page);
- iter.data_buf = kaddr + bv->bv_offset;
- iter.data_size = bv->bv_len;
+ iter.data_buf = kaddr + bv.bv_offset;
+ iter.data_size = bv.bv_len;
ret = proc_fn(&iter);
if (ret) {
blk_clear_rq_complete(rq);
trace_block_rq_requeue(q, rq);
- if (blk_rq_tagged(rq))
+ if (rq->cmd_flags & REQ_QUEUED)
blk_queue_end_tag(q, rq);
BUG_ON(blk_queued_rq(rq));
*/
void blk_finish_request(struct request *req, int error)
{
- if (blk_rq_tagged(req))
+ if (req->cmd_flags & REQ_QUEUED)
blk_queue_end_tag(req->q, req);
BUG_ON(blk_queued_rq(req));
void blk_recount_segments(struct request_queue *q, struct bio *bio)
{
- bool no_sg_merge = !!test_bit(QUEUE_FLAG_NO_SG_MERGE,
- &q->queue_flags);
- bool merge_not_need = bio->bi_vcnt < queue_max_segments(q);
+ unsigned short seg_cnt;
+
+ /* estimate segment number by bi_vcnt for non-cloned bio */
+ if (bio_flagged(bio, BIO_CLONED))
+ seg_cnt = bio_segments(bio);
+ else
+ seg_cnt = bio->bi_vcnt;
- if (no_sg_merge && !bio_flagged(bio, BIO_CLONED) &&
- merge_not_need)
- bio->bi_phys_segments = bio->bi_vcnt;
+ if (test_bit(QUEUE_FLAG_NO_SG_MERGE, &q->queue_flags) &&
+ (seg_cnt < queue_max_segments(q)))
+ bio->bi_phys_segments = seg_cnt;
else {
struct bio *nxt = bio->bi_next;
bio->bi_next = NULL;
- bio->bi_phys_segments = __blk_recalc_rq_segments(q, bio,
- no_sg_merge && merge_not_need);
+ bio->bi_phys_segments = __blk_recalc_rq_segments(q, bio, false);
bio->bi_next = nxt;
}
return 0;
}
+/**
+ * blk_mq_unique_tag() - return a tag that is unique queue-wide
+ * @rq: request for which to compute a unique tag
+ *
+ * The tag field in struct request is unique per hardware queue but not over
+ * all hardware queues. Hence this function that returns a tag with the
+ * hardware context index in the upper bits and the per hardware queue tag in
+ * the lower bits.
+ *
+ * Note: When called for a request that is queued on a non-multiqueue request
+ * queue, the hardware context index is set to zero.
+ */
+u32 blk_mq_unique_tag(struct request *rq)
+{
+ struct request_queue *q = rq->q;
+ struct blk_mq_hw_ctx *hctx;
+ int hwq = 0;
+
+ if (q->mq_ops) {
+ hctx = q->mq_ops->map_queue(q, rq->mq_ctx->cpu);
+ hwq = hctx->queue_num;
+ }
+
+ return (hwq << BLK_MQ_UNIQUE_TAG_BITS) |
+ (rq->tag & BLK_MQ_UNIQUE_TAG_MASK);
+}
+EXPORT_SYMBOL(blk_mq_unique_tag);
+
ssize_t blk_mq_tag_sysfs_show(struct blk_mq_tags *tags, char *page)
{
char *orig_page = page;
wake_up_all(&q->mq_freeze_wq);
}
-/*
- * Guarantee no request is in use, so we can change any data structure of
- * the queue afterward.
- */
-void blk_mq_freeze_queue(struct request_queue *q)
+static void blk_mq_freeze_queue_start(struct request_queue *q)
{
bool freeze;
percpu_ref_kill(&q->mq_usage_counter);
blk_mq_run_queues(q, false);
}
+}
+
+static void blk_mq_freeze_queue_wait(struct request_queue *q)
+{
wait_event(q->mq_freeze_wq, percpu_ref_is_zero(&q->mq_usage_counter));
}
+/*
+ * Guarantee no request is in use, so we can change any data structure of
+ * the queue afterward.
+ */
+void blk_mq_freeze_queue(struct request_queue *q)
+{
+ blk_mq_freeze_queue_start(q);
+ blk_mq_freeze_queue_wait(q);
+}
+
static void blk_mq_unfreeze_queue(struct request_queue *q)
{
bool wake;
/* Basically redo blk_mq_init_queue with queue frozen */
static void blk_mq_queue_reinit(struct request_queue *q)
{
- blk_mq_freeze_queue(q);
+ WARN_ON_ONCE(!q->mq_freeze_depth);
blk_mq_sysfs_unregister(q);
blk_mq_map_swqueue(q);
blk_mq_sysfs_register(q);
-
- blk_mq_unfreeze_queue(q);
}
static int blk_mq_queue_reinit_notify(struct notifier_block *nb,
return NOTIFY_OK;
mutex_lock(&all_q_mutex);
+
+ /*
+ * We need to freeze and reinit all existing queues. Freezing
+ * involves synchronous wait for an RCU grace period and doing it
+ * one by one may take a long time. Start freezing all queues in
+ * one swoop and then wait for the completions so that freezing can
+ * take place in parallel.
+ */
+ list_for_each_entry(q, &all_q_list, all_q_node)
+ blk_mq_freeze_queue_start(q);
+ list_for_each_entry(q, &all_q_list, all_q_node)
+ blk_mq_freeze_queue_wait(q);
+
list_for_each_entry(q, &all_q_list, all_q_node)
blk_mq_queue_reinit(q);
+
+ list_for_each_entry(q, &all_q_list, all_q_node)
+ blk_mq_unfreeze_queue(q);
+
mutex_unlock(&all_q_mutex);
return NOTIFY_OK;
}
*/
int blk_mq_alloc_tag_set(struct blk_mq_tag_set *set)
{
+ BUILD_BUG_ON(BLK_MQ_MAX_DEPTH > 1 << BLK_MQ_UNIQUE_TAG_BITS);
+
if (!set->nr_hw_queues)
return -EINVAL;
if (!set->queue_depth)
int ioprio_best(unsigned short aprio, unsigned short bprio)
{
- unsigned short aclass = IOPRIO_PRIO_CLASS(aprio);
- unsigned short bclass = IOPRIO_PRIO_CLASS(bprio);
+ unsigned short aclass;
+ unsigned short bclass;
- if (aclass == IOPRIO_CLASS_NONE)
- aclass = IOPRIO_CLASS_BE;
- if (bclass == IOPRIO_CLASS_NONE)
- bclass = IOPRIO_CLASS_BE;
+ if (!ioprio_valid(aprio))
+ aprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, IOPRIO_NORM);
+ if (!ioprio_valid(bprio))
+ bprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, IOPRIO_NORM);
+ aclass = IOPRIO_PRIO_CLASS(aprio);
+ bclass = IOPRIO_PRIO_CLASS(bprio);
if (aclass == bclass)
return min(aprio, bprio);
if (aclass > bclass)
__set_bit(GPCMD_VERIFY_10, filter->read_ok);
__set_bit(VERIFY_16, filter->read_ok);
__set_bit(REPORT_LUNS, filter->read_ok);
- __set_bit(SERVICE_ACTION_IN, filter->read_ok);
+ __set_bit(SERVICE_ACTION_IN_16, filter->read_ok);
__set_bit(RECEIVE_DIAGNOSTIC, filter->read_ok);
__set_bit(MAINTENANCE_IN, filter->read_ok);
__set_bit(GPCMD_READ_BUFFER_CAPACITY, filter->read_ok);
rq = blk_get_request(q, in_len ? WRITE : READ, __GFP_WAIT);
if (IS_ERR(rq)) {
err = PTR_ERR(rq);
- goto error;
+ goto error_free_buffer;
}
blk_rq_set_block_pc(rq);
}
error:
+ blk_put_request(rq);
+
+error_free_buffer:
kfree(buffer);
- if (rq)
- blk_put_request(rq);
+
return err;
}
EXPORT_SYMBOL_GPL(sg_scsi_ioctl);
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3446"),
},
},
+ {
+ .callback = dmi_disable_osi_win8,
+ .ident = "Dell Vostro 3546",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3546"),
+ },
+ },
/*
* BIOS invocation of _OSI(Linux) is almost always a BIOS bug.
return 0;
target_state = acpi_target_system_state();
- wakeup = device_may_wakeup(dev);
+ wakeup = device_may_wakeup(dev) && acpi_device_can_wakeup(adev);
error = acpi_device_wakeup(adev, target_state, wakeup);
if (wakeup && error)
return error;
return true;
for (i = 0; i < video->attached_count; i++) {
- if (video->attached_array[i].bind_info == device)
+ if ((video->attached_array[i].value.int_val & 0xfff) ==
+ (device->device_id & 0xfff))
return true;
}
/* board IDs by feature in alphabetical order */
board_ahci,
board_ahci_ign_iferr,
+ board_ahci_nomsi,
board_ahci_noncq,
board_ahci_nosntf,
board_ahci_yes_fbs,
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_ops,
},
+ [board_ahci_nomsi] = {
+ AHCI_HFLAGS (AHCI_HFLAG_NO_MSI),
+ .flags = AHCI_FLAG_COMMON,
+ .pio_mask = ATA_PIO4,
+ .udma_mask = ATA_UDMA6,
+ .port_ops = &ahci_ops,
+ },
[board_ahci_noncq] = {
AHCI_HFLAGS (AHCI_HFLAG_NO_NCQ),
.flags = AHCI_FLAG_COMMON,
{ PCI_VDEVICE(INTEL, 0x8c87), board_ahci }, /* 9 Series RAID */
{ PCI_VDEVICE(INTEL, 0x8c8e), board_ahci }, /* 9 Series RAID */
{ PCI_VDEVICE(INTEL, 0x8c8f), board_ahci }, /* 9 Series RAID */
+ { PCI_VDEVICE(INTEL, 0x9d03), board_ahci }, /* Sunrise Point-LP AHCI */
+ { PCI_VDEVICE(INTEL, 0x9d05), board_ahci }, /* Sunrise Point-LP RAID */
+ { PCI_VDEVICE(INTEL, 0x9d07), board_ahci }, /* Sunrise Point-LP RAID */
+ { PCI_VDEVICE(INTEL, 0xa103), board_ahci }, /* Sunrise Point-H AHCI */
+ { PCI_VDEVICE(INTEL, 0xa103), board_ahci }, /* Sunrise Point-H RAID */
+ { PCI_VDEVICE(INTEL, 0xa105), board_ahci }, /* Sunrise Point-H RAID */
+ { PCI_VDEVICE(INTEL, 0xa107), board_ahci }, /* Sunrise Point-H RAID */
+ { PCI_VDEVICE(INTEL, 0xa10f), board_ahci }, /* Sunrise Point-H RAID */
/* JMicron 360/1/3/5/6, match class to avoid IDE function */
{ PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
{ PCI_VDEVICE(ASMEDIA, 0x0612), board_ahci }, /* ASM1062 */
/*
- * Samsung SSDs found on some macbooks. NCQ times out.
- * https://bugzilla.kernel.org/show_bug.cgi?id=60731
+ * Samsung SSDs found on some macbooks. NCQ times out if MSI is
+ * enabled. https://bugzilla.kernel.org/show_bug.cgi?id=60731
*/
- { PCI_VDEVICE(SAMSUNG, 0x1600), board_ahci_noncq },
+ { PCI_VDEVICE(SAMSUNG, 0x1600), board_ahci_nomsi },
+ { PCI_VDEVICE(SAMSUNG, 0xa800), board_ahci_nomsi },
/* Enmotus */
{ PCI_DEVICE(0x1c44, 0x8000), board_ahci },
static void ahci_pci_save_initial_config(struct pci_dev *pdev,
struct ahci_host_priv *hpriv)
{
- unsigned int force_port_map = 0;
- unsigned int mask_port_map = 0;
-
if (pdev->vendor == PCI_VENDOR_ID_JMICRON && pdev->device == 0x2361) {
dev_info(&pdev->dev, "JMB361 has only one port\n");
- force_port_map = 1;
+ hpriv->force_port_map = 1;
}
/*
*/
if (hpriv->flags & AHCI_HFLAG_MV_PATA) {
if (pdev->device == 0x6121)
- mask_port_map = 0x3;
+ hpriv->mask_port_map = 0x3;
else
- mask_port_map = 0xf;
+ hpriv->mask_port_map = 0xf;
dev_info(&pdev->dev,
"Disabling your PATA port. Use the boot option 'ahci.marvell_enable=0' to avoid this.\n");
}
}
}
-static void ahci_update_intr_status(struct ata_port *ap)
+static void ahci_port_intr(struct ata_port *ap)
{
void __iomem *port_mmio = ahci_port_base(ap);
- struct ahci_port_priv *pp = ap->private_data;
u32 status;
status = readl(port_mmio + PORT_IRQ_STAT);
writel(status, port_mmio + PORT_IRQ_STAT);
- atomic_or(status, &pp->intr_status);
+ ahci_handle_port_interrupt(ap, port_mmio, status);
}
static irqreturn_t ahci_port_thread_fn(int irq, void *dev_instance)
return IRQ_HANDLED;
}
-irqreturn_t ahci_thread_fn(int irq, void *dev_instance)
-{
- struct ata_host *host = dev_instance;
- struct ahci_host_priv *hpriv = host->private_data;
- u32 irq_masked = hpriv->port_map;
- unsigned int i;
-
- for (i = 0; i < host->n_ports; i++) {
- struct ata_port *ap;
-
- if (!(irq_masked & (1 << i)))
- continue;
-
- ap = host->ports[i];
- if (ap) {
- ahci_port_thread_fn(irq, ap);
- VPRINTK("port %u\n", i);
- } else {
- VPRINTK("port %u (no irq)\n", i);
- if (ata_ratelimit())
- dev_warn(host->dev,
- "interrupt on disabled port %u\n", i);
- }
- }
-
- return IRQ_HANDLED;
-}
-
static irqreturn_t ahci_multi_irqs_intr(int irq, void *dev_instance)
{
struct ata_port *ap = dev_instance;
irq_masked = irq_stat & hpriv->port_map;
+ spin_lock(&host->lock);
+
for (i = 0; i < host->n_ports; i++) {
struct ata_port *ap;
ap = host->ports[i];
if (ap) {
- ahci_update_intr_status(ap);
+ ahci_port_intr(ap);
VPRINTK("port %u\n", i);
} else {
VPRINTK("port %u (no irq)\n", i);
*/
writel(irq_stat, mmio + HOST_IRQ_STAT);
+ spin_unlock(&host->lock);
+
VPRINTK("EXIT\n");
- return handled ? IRQ_WAKE_THREAD : IRQ_NONE;
+ return IRQ_RETVAL(handled);
}
unsigned int ahci_qc_issue(struct ata_queued_cmd *qc)
*/
pp->intr_mask = DEF_PORT_IRQ;
- spin_lock_init(&pp->lock);
- ap->lock = &pp->lock;
+ /*
+ * Switch to per-port locking in case each port has its own MSI vector.
+ */
+ if ((hpriv->flags & AHCI_HFLAG_MULTI_MSI)) {
+ spin_lock_init(&pp->lock);
+ ap->lock = &pp->lock;
+ }
ap->private_data = pp;
return rc;
}
-static int ahci_host_activate_single_irq(struct ata_host *host, int irq,
- struct scsi_host_template *sht)
-{
- int i, rc;
-
- rc = ata_host_start(host);
- if (rc)
- return rc;
-
- rc = devm_request_threaded_irq(host->dev, irq, ahci_single_irq_intr,
- ahci_thread_fn, IRQF_SHARED,
- dev_driver_string(host->dev), host);
- if (rc)
- return rc;
-
- for (i = 0; i < host->n_ports; i++)
- ata_port_desc(host->ports[i], "irq %d", irq);
-
- rc = ata_host_register(host, sht);
- if (rc)
- devm_free_irq(host->dev, irq, host);
-
- return rc;
-}
-
/**
* ahci_host_activate - start AHCI host, request IRQs and register it
* @host: target ATA host
if (hpriv->flags & AHCI_HFLAG_MULTI_MSI)
rc = ahci_host_activate_multi_irqs(host, irq, sht);
else
- rc = ahci_host_activate_single_irq(host, irq, sht);
+ rc = ata_host_activate(host, irq, ahci_single_irq_intr,
+ IRQF_SHARED, sht);
return rc;
}
EXPORT_SYMBOL_GPL(ahci_host_activate);
depth = min(sdev->host->can_queue, ata_id_queue_depth(dev->id));
depth = min(ATA_MAX_QUEUE - 1, depth);
- scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, depth);
+ scsi_change_queue_depth(sdev, depth);
}
blk_queue_flush_queueable(q, false);
* @ap: ATA port to which the device change the queue depth
* @sdev: SCSI device to configure queue depth for
* @queue_depth: new queue depth
- * @reason: calling context
*
* libsas and libata have different approaches for associating a sdev to
* its ata_port.
*
*/
int __ata_change_queue_depth(struct ata_port *ap, struct scsi_device *sdev,
- int queue_depth, int reason)
+ int queue_depth)
{
struct ata_device *dev;
unsigned long flags;
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
-
if (queue_depth < 1 || queue_depth == sdev->queue_depth)
return sdev->queue_depth;
if (sdev->queue_depth == queue_depth)
return -EINVAL;
- scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, queue_depth);
- return queue_depth;
+ return scsi_change_queue_depth(sdev, queue_depth);
}
/**
* ata_scsi_change_queue_depth - SCSI callback for queue depth config
* @sdev: SCSI device to configure queue depth for
* @queue_depth: new queue depth
- * @reason: calling context
*
* This is libata standard hostt->change_queue_depth callback.
* SCSI will call into this callback when user tries to set queue
* RETURNS:
* Newly configured queue depth.
*/
-int ata_scsi_change_queue_depth(struct scsi_device *sdev, int queue_depth,
- int reason)
+int ata_scsi_change_queue_depth(struct scsi_device *sdev, int queue_depth)
{
struct ata_port *ap = ata_shost_to_port(sdev->host);
- return __ata_change_queue_depth(ap, sdev, queue_depth, reason);
+ return __ata_change_queue_depth(ap, sdev, queue_depth);
}
/**
ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap);
break;
- case SERVICE_ACTION_IN:
+ case SERVICE_ACTION_IN_16:
if ((scsicmd[1] & 0x1f) == SAI_READ_CAPACITY_16)
ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap);
else
host_priv->csr_base = csr_base;
irq = irq_of_parse_and_map(ofdev->dev.of_node, 0);
- if (irq < 0) {
+ if (!irq) {
dev_err(&ofdev->dev, "invalid irq from platform\n");
goto error_exit_with_cleanup;
}
ata_id_c_string(dev->id, model_num, ATA_ID_PROD, sizeof(model_num));
if (strncmp(model_num, "Maxtor", 6) == 0) {
- ata_scsi_change_queue_depth(sdev, 1, SCSI_QDEPTH_DEFAULT);
+ ata_scsi_change_queue_depth(sdev, 1);
ata_dev_notice(dev, "Disabling SWNCQ mode (depth %x)\n",
sdev->queue_depth);
}
enum sata_rcar_type {
RCAR_GEN1_SATA,
RCAR_GEN2_SATA,
+ RCAR_R8A7790_ES1_SATA,
};
struct sata_rcar_priv {
ap->udma_mask = ATA_UDMA6;
ap->flags |= ATA_FLAG_SATA;
+ if (priv->type == RCAR_R8A7790_ES1_SATA)
+ ap->flags |= ATA_FLAG_NO_DIPM;
+
ioaddr->cmd_addr = base + SDATA_REG;
ioaddr->ctl_addr = base + SSDEVCON_REG;
ioaddr->scr_addr = base + SCRSSTS_REG;
sata_rcar_gen1_phy_init(priv);
break;
case RCAR_GEN2_SATA:
+ case RCAR_R8A7790_ES1_SATA:
sata_rcar_gen2_phy_init(priv);
break;
default:
.compatible = "renesas,sata-r8a7790",
.data = (void *)RCAR_GEN2_SATA
},
+ {
+ .compatible = "renesas,sata-r8a7790-es1",
+ .data = (void *)RCAR_R8A7790_ES1_SATA
+ },
{
.compatible = "renesas,sata-r8a7791",
.data = (void *)RCAR_GEN2_SATA
},
+ {
+ .compatible = "renesas,sata-r8a7793",
+ .data = (void *)RCAR_GEN2_SATA
+ },
{ },
};
MODULE_DEVICE_TABLE(of, sata_rcar_match);
{ "sata_rcar", RCAR_GEN1_SATA }, /* Deprecated by "sata-r8a7779" */
{ "sata-r8a7779", RCAR_GEN1_SATA },
{ "sata-r8a7790", RCAR_GEN2_SATA },
+ { "sata-r8a7790-es1", RCAR_R8A7790_ES1_SATA },
{ "sata-r8a7791", RCAR_GEN2_SATA },
+ { "sata-r8a7793", RCAR_GEN2_SATA },
{ },
};
MODULE_DEVICE_TABLE(platform, sata_rcar_id_table);
card->config_regs = pci_iomap(dev, 0, CONFIG_RAM_SIZE);
if (!card->config_regs) {
dev_warn(&dev->dev, "Failed to ioremap config registers\n");
+ err = -ENOMEM;
goto out_release_regions;
}
card->buffers = pci_iomap(dev, 1, DATA_RAM_SIZE);
if (!card->buffers) {
dev_warn(&dev->dev, "Failed to ioremap data buffers\n");
+ err = -ENOMEM;
goto out_unmap_config;
}
Drivers should "select" this option if they desire to use the
device coredump mechanism.
-config DISABLE_DEV_COREDUMP
- bool "Disable device coredump" if EXPERT
+config ALLOW_DEV_COREDUMP
+ bool "Allow device coredump" if EXPERT
+ default y
help
- Disable the device coredump mechanism despite drivers wanting to
- use it; this allows for more sensitive systems or systems that
- don't want to ever access the information to not have the code,
- nor keep any data.
+ This option controls if the device coredump mechanism is available or
+ not; if disabled, the mechanism will be omitted even if drivers that
+ can use it are enabled.
+ Say 'N' for more sensitive systems or systems that don't want
+ to ever access the information to not have the code, nor keep any
+ data.
- If unsure, say N.
+ If unsure, say Y.
config DEV_COREDUMP
bool
default y if WANT_DEV_COREDUMP
- depends on !DISABLE_DEV_COREDUMP
+ depends on ALLOW_DEV_COREDUMP
config DEBUG_DRIVER
bool "Driver Core verbose debug messages"
return &dir->kobj;
}
+static DEFINE_MUTEX(gdp_mutex);
static struct kobject *get_device_parent(struct device *dev,
struct device *parent)
{
if (dev->class) {
- static DEFINE_MUTEX(gdp_mutex);
struct kobject *kobj = NULL;
struct kobject *parent_kobj;
struct kobject *k;
glue_dir->kset != &dev->class->p->glue_dirs)
return;
+ mutex_lock(&gdp_mutex);
kobject_put(glue_dir);
+ mutex_unlock(&gdp_mutex);
}
static void cleanup_device_parent(struct device *dev)
struct device *dev = pdd->dev;
int ret = 0;
- if (gpd_data->need_restore)
+ if (gpd_data->need_restore > 0)
return 0;
+ /*
+ * If the value of the need_restore flag is still unknown at this point,
+ * we trust that pm_genpd_poweroff() has verified that the device is
+ * already runtime PM suspended.
+ */
+ if (gpd_data->need_restore < 0) {
+ gpd_data->need_restore = 1;
+ return 0;
+ }
+
mutex_unlock(&genpd->lock);
genpd_start_dev(genpd, dev);
mutex_lock(&genpd->lock);
if (!ret)
- gpd_data->need_restore = true;
+ gpd_data->need_restore = 1;
return ret;
}
{
struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
struct device *dev = pdd->dev;
- bool need_restore = gpd_data->need_restore;
+ int need_restore = gpd_data->need_restore;
- gpd_data->need_restore = false;
+ gpd_data->need_restore = 0;
mutex_unlock(&genpd->lock);
genpd_start_dev(genpd, dev);
+
+ /*
+ * Call genpd_restore_dev() for recently added devices too (need_restore
+ * is negative then).
+ */
if (need_restore)
genpd_restore_dev(genpd, dev);
static int pm_genpd_runtime_suspend(struct device *dev)
{
struct generic_pm_domain *genpd;
+ struct generic_pm_domain_data *gpd_data;
bool (*stop_ok)(struct device *__dev);
int ret;
return 0;
mutex_lock(&genpd->lock);
+
+ /*
+ * If we have an unknown state of the need_restore flag, it means none
+ * of the runtime PM callbacks has been invoked yet. Let's update the
+ * flag to reflect that the current state is active.
+ */
+ gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
+ if (gpd_data->need_restore < 0)
+ gpd_data->need_restore = 0;
+
genpd->in_progress++;
pm_genpd_poweroff(genpd);
genpd->in_progress--;
spin_unlock_irq(&dev->power.lock);
if (genpd->attach_dev)
- genpd->attach_dev(dev);
+ genpd->attach_dev(genpd, dev);
mutex_lock(&gpd_data->lock);
gpd_data->base.dev = dev;
list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
- gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF;
+ gpd_data->need_restore = -1;
gpd_data->td.constraint_changed = true;
gpd_data->td.effective_constraint_ns = -1;
mutex_unlock(&gpd_data->lock);
genpd->max_off_time_changed = true;
if (genpd->detach_dev)
- genpd->detach_dev(dev);
+ genpd->detach_dev(genpd, dev);
spin_lock_irq(&dev->power.lock);
psd = dev_to_psd(dev);
if (psd && psd->domain_data)
- to_gpd_data(psd->domain_data)->need_restore = val;
+ to_gpd_data(psd->domain_data)->need_restore = val ? 1 : 0;
spin_unlock_irqrestore(&dev->power.lock, flags);
}
# subsystems should select the appropriate symbols.
config REGMAP
- default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_MMIO || REGMAP_IRQ)
+ default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ)
select LZO_COMPRESS
select LZO_DECOMPRESS
select IRQ_DOMAIN if REGMAP_IRQ
bool
+config REGMAP_AC97
+ tristate
+
config REGMAP_I2C
tristate
depends on I2C
obj-$(CONFIG_REGMAP) += regmap.o regcache.o
obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o regcache-flat.o
obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o
+obj-$(CONFIG_REGMAP_AC97) += regmap-ac97.o
obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
obj-$(CONFIG_REGMAP_SPMI) += regmap-spmi.o
* published by the Free Software Foundation.
*/
-#include <linux/slab.h>
#include <linux/device.h>
#include <linux/seq_file.h>
+#include <linux/slab.h>
#include "internal.h"
* published by the Free Software Foundation.
*/
-#include <linux/slab.h>
#include <linux/device.h>
#include <linux/lzo.h>
+#include <linux/slab.h>
#include "internal.h"
* published by the Free Software Foundation.
*/
-#include <linux/slab.h>
-#include <linux/device.h>
#include <linux/debugfs.h>
+#include <linux/device.h>
#include <linux/rbtree.h>
#include <linux/seq_file.h>
+#include <linux/slab.h>
#include "internal.h"
* published by the Free Software Foundation.
*/
-#include <linux/slab.h>
-#include <linux/export.h>
-#include <linux/device.h>
-#include <trace/events/regmap.h>
#include <linux/bsearch.h>
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/slab.h>
#include <linux/sort.h>
+#include <trace/events/regmap.h>
#include "internal.h"
if (!map->num_reg_defaults_raw)
return -EINVAL;
+ /* calculate the size of reg_defaults */
+ for (count = 0, i = 0; i < map->num_reg_defaults_raw; i++)
+ if (!regmap_volatile(map, i * map->reg_stride))
+ count++;
+
+ /* all registers are volatile, so just bypass */
+ if (!count) {
+ map->cache_bypass = true;
+ return 0;
+ }
+
+ map->num_reg_defaults = count;
+ map->reg_defaults = kmalloc_array(count, sizeof(struct reg_default),
+ GFP_KERNEL);
+ if (!map->reg_defaults)
+ return -ENOMEM;
+
if (!map->reg_defaults_raw) {
u32 cache_bypass = map->cache_bypass;
dev_warn(map->dev, "No cache defaults, reading back from HW\n");
/* Bypass the cache access till data read from HW*/
map->cache_bypass = 1;
tmp_buf = kmalloc(map->cache_size_raw, GFP_KERNEL);
- if (!tmp_buf)
- return -EINVAL;
+ if (!tmp_buf) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
ret = regmap_raw_read(map, 0, tmp_buf,
map->num_reg_defaults_raw);
map->cache_bypass = cache_bypass;
- if (ret < 0) {
- kfree(tmp_buf);
- return ret;
- }
+ if (ret < 0)
+ goto err_cache_free;
+
map->reg_defaults_raw = tmp_buf;
map->cache_free = 1;
}
- /* calculate the size of reg_defaults */
- for (count = 0, i = 0; i < map->num_reg_defaults_raw; i++) {
- val = regcache_get_val(map, map->reg_defaults_raw, i);
- if (regmap_volatile(map, i * map->reg_stride))
- continue;
- count++;
- }
-
- map->reg_defaults = kmalloc(count * sizeof(struct reg_default),
- GFP_KERNEL);
- if (!map->reg_defaults) {
- ret = -ENOMEM;
- goto err_free;
- }
-
/* fill the reg_defaults */
- map->num_reg_defaults = count;
for (i = 0, j = 0; i < map->num_reg_defaults_raw; i++) {
- val = regcache_get_val(map, map->reg_defaults_raw, i);
if (regmap_volatile(map, i * map->reg_stride))
continue;
+ val = regcache_get_val(map, map->reg_defaults_raw, i);
map->reg_defaults[j].reg = i * map->reg_stride;
map->reg_defaults[j].def = val;
j++;
return 0;
+err_cache_free:
+ kfree(tmp_buf);
err_free:
- if (map->cache_free)
- kfree(map->reg_defaults_raw);
+ kfree(map->reg_defaults);
return ret;
}
ret = regcache_hw_init(map);
if (ret < 0)
return ret;
+ if (map->cache_bypass)
+ return 0;
}
if (!map->max_register)
--- /dev/null
+/*
+ * Register map access API - AC'97 support
+ *
+ * Copyright 2013 Linaro Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <sound/ac97_codec.h>
+
+bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AC97_RESET:
+ case AC97_POWERDOWN:
+ case AC97_INT_PAGING:
+ case AC97_EXTENDED_ID:
+ case AC97_EXTENDED_STATUS:
+ case AC97_EXTENDED_MID:
+ case AC97_EXTENDED_MSTATUS:
+ case AC97_GPIO_STATUS:
+ case AC97_MISC_AFE:
+ case AC97_VENDOR_ID1:
+ case AC97_VENDOR_ID2:
+ case AC97_CODEC_CLASS_REV:
+ case AC97_PCI_SVID:
+ case AC97_PCI_SID:
+ case AC97_FUNC_SELECT:
+ case AC97_FUNC_INFO:
+ case AC97_SENSE_INFO:
+ return true;
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL_GPL(regmap_ac97_default_volatile);
+
+static int regmap_ac97_reg_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct snd_ac97 *ac97 = context;
+
+ *val = ac97->bus->ops->read(ac97, reg);
+
+ return 0;
+}
+
+static int regmap_ac97_reg_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct snd_ac97 *ac97 = context;
+
+ ac97->bus->ops->write(ac97, reg, val);
+
+ return 0;
+}
+
+static const struct regmap_bus ac97_regmap_bus = {
+ .reg_write = regmap_ac97_reg_write,
+ .reg_read = regmap_ac97_reg_read,
+};
+
+/**
+ * regmap_init_ac97(): Initialise AC'97 register map
+ *
+ * @ac97: Device that will be interacted with
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer to
+ * a struct regmap.
+ */
+struct regmap *regmap_init_ac97(struct snd_ac97 *ac97,
+ const struct regmap_config *config)
+{
+ return regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config);
+}
+EXPORT_SYMBOL_GPL(regmap_init_ac97);
+
+/**
+ * devm_regmap_init_ac97(): Initialise AC'97 register map
+ *
+ * @ac97: Device that will be interacted with
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a struct regmap. The regmap will be automatically freed by the
+ * device management code.
+ */
+struct regmap *devm_regmap_init_ac97(struct snd_ac97 *ac97,
+ const struct regmap_config *config)
+{
+ return devm_regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config);
+}
+EXPORT_SYMBOL_GPL(devm_regmap_init_ac97);
+
+MODULE_LICENSE("GPL v2");
(GET_U32_FROM_CDB(cdb, READ_CAP_16_CDB_ALLOC_LENGTH_OFFSET))
#define IS_READ_CAP_16(cdb) \
-((cdb[0] == SERVICE_ACTION_IN && cdb[1] == SAI_READ_CAPACITY_16) ? 1 : 0)
+((cdb[0] == SERVICE_ACTION_IN_16 && cdb[1] == SAI_READ_CAPACITY_16) ? 1 : 0)
/* Request Sense Helper Macros */
#define GET_REQUEST_SENSE_ALLOC_LENGTH(cdb) \
case READ_CAPACITY:
retcode = nvme_trans_read_capacity(ns, hdr, cmd);
break;
- case SERVICE_ACTION_IN:
+ case SERVICE_ACTION_IN_16:
if (IS_READ_CAP_16(cmd))
retcode = nvme_trans_read_capacity(ns, hdr, cmd);
else
struct list_head rq_queue; /* incoming rq queue */
spinlock_t lock; /* queue, flags, open_count */
- struct workqueue_struct *rq_wq;
struct work_struct rq_work;
struct rbd_image_header header;
static int rbd_major;
static DEFINE_IDA(rbd_dev_id_ida);
+static struct workqueue_struct *rbd_wq;
+
/*
* Default to false for now, as single-major requires >= 0.75 version of
* userspace rbd utility.
}
if (queued)
- queue_work(rbd_dev->rq_wq, &rbd_dev->rq_work);
+ queue_work(rbd_wq, &rbd_dev->rq_work);
}
/*
page_count = (u32) calc_pages_for(offset, length);
pages = ceph_alloc_page_vector(page_count, GFP_KERNEL);
if (IS_ERR(pages))
- ret = PTR_ERR(pages);
+ return PTR_ERR(pages);
ret = -ENOMEM;
obj_request = rbd_obj_request_create(object_name, offset, length,
set_capacity(rbd_dev->disk, rbd_dev->mapping.size / SECTOR_SIZE);
set_disk_ro(rbd_dev->disk, rbd_dev->mapping.read_only);
- rbd_dev->rq_wq = alloc_workqueue("%s", WQ_MEM_RECLAIM, 0,
- rbd_dev->disk->disk_name);
- if (!rbd_dev->rq_wq) {
- ret = -ENOMEM;
- goto err_out_mapping;
- }
-
ret = rbd_bus_add_dev(rbd_dev);
if (ret)
- goto err_out_workqueue;
+ goto err_out_mapping;
/* Everything's ready. Announce the disk to the world. */
return ret;
-err_out_workqueue:
- destroy_workqueue(rbd_dev->rq_wq);
- rbd_dev->rq_wq = NULL;
err_out_mapping:
rbd_dev_mapping_clear(rbd_dev);
err_out_disk:
{
struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
- destroy_workqueue(rbd_dev->rq_wq);
rbd_free_disk(rbd_dev);
clear_bit(RBD_DEV_FLAG_EXISTS, &rbd_dev->flags);
rbd_dev_mapping_clear(rbd_dev);
if (rc)
return rc;
+ /*
+ * The number of active work items is limited by the number of
+ * rbd devices, so leave @max_active at default.
+ */
+ rbd_wq = alloc_workqueue(RBD_DRV_NAME, WQ_MEM_RECLAIM, 0);
+ if (!rbd_wq) {
+ rc = -ENOMEM;
+ goto err_out_slab;
+ }
+
if (single_major) {
rbd_major = register_blkdev(0, RBD_DRV_NAME);
if (rbd_major < 0) {
rc = rbd_major;
- goto err_out_slab;
+ goto err_out_wq;
}
}
err_out_blkdev:
if (single_major)
unregister_blkdev(rbd_major, RBD_DRV_NAME);
+err_out_wq:
+ destroy_workqueue(rbd_wq);
err_out_slab:
rbd_slab_exit();
return rc;
rbd_sysfs_cleanup();
if (single_major)
unregister_blkdev(rbd_major, RBD_DRV_NAME);
+ destroy_workqueue(rbd_wq);
rbd_slab_exit();
}
}
if (page_zero_filled(uncmem)) {
- kunmap_atomic(user_mem);
+ if (user_mem)
+ kunmap_atomic(user_mem);
/* Free memory associated with this sector now. */
bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value);
zram_free_page(zram, index);
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
#include "omap_l3_smx.h"
static inline u64 omap3_l3_readll(void __iomem *base, u16 reg)
return ret;
}
-static int __init omap3_l3_probe(struct platform_device *pdev)
+#if IS_BUILTIN(CONFIG_OF)
+static const struct of_device_id omap3_l3_match[] = {
+ {
+ .compatible = "ti,omap3-l3-smx",
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, omap3_l3_match);
+#endif
+
+static int omap3_l3_probe(struct platform_device *pdev)
{
struct omap3_l3 *l3;
struct resource *res;
return ret;
}
-static int __exit omap3_l3_remove(struct platform_device *pdev)
+static int omap3_l3_remove(struct platform_device *pdev)
{
struct omap3_l3 *l3 = platform_get_drvdata(pdev);
}
static struct platform_driver omap3_l3_driver = {
- .remove = __exit_p(omap3_l3_remove),
+ .probe = omap3_l3_probe,
+ .remove = omap3_l3_remove,
.driver = {
- .name = "omap_l3_smx",
+ .name = "omap_l3_smx",
+ .of_match_table = of_match_ptr(omap3_l3_match),
},
};
static int __init omap3_l3_init(void)
{
- return platform_driver_probe(&omap3_l3_driver, omap3_l3_probe);
+ return platform_driver_register(&omap3_l3_driver);
}
postcore_initcall_sync(omap3_l3_init);
#include <asm/vio.h>
-static int pseries_rng_data_read(struct hwrng *rng, u32 *data)
+static int pseries_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
{
+ u64 buffer[PLPAR_HCALL_BUFSIZE];
+ size_t size = max < 8 ? max : 8;
int rc;
- rc = plpar_hcall(H_RANDOM, (unsigned long *)data);
+ rc = plpar_hcall(H_RANDOM, (unsigned long *)buffer);
if (rc != H_SUCCESS) {
pr_err_ratelimited("H_RANDOM call failed %d\n", rc);
return -EIO;
}
+ memcpy(data, buffer, size);
/* The hypervisor interface returns 64 bits */
- return 8;
+ return size;
}
/**
static struct hwrng pseries_rng = {
.name = KBUILD_MODNAME,
- .data_read = pseries_rng_data_read,
+ .read = pseries_rng_read,
};
static int __init pseries_rng_probe(struct vio_dev *dev,
spin_lock_init(&port->outvq_lock);
init_waitqueue_head(&port->waitqueue);
- virtio_device_ready(portdev->vdev);
-
/* Fill the in_vq with buffers so the host can send us data. */
nr_added_bufs = fill_queue(port->in_vq, &port->inbuf_lock);
if (!nr_added_bufs) {
spin_lock_init(&portdev->ports_lock);
INIT_LIST_HEAD(&portdev->ports);
+ virtio_device_ready(portdev->vdev);
+
if (multiport) {
unsigned int nr_added_bufs;
tmp = pmc_read(pmc, AT91_PMC_USB);
usbdiv = (tmp & AT91_PMC_OHCIUSBDIV) >> SAM9X5_USB_DIV_SHIFT;
- return parent_rate / (usbdiv + 1);
+
+ return DIV_ROUND_CLOSEST(parent_rate, (usbdiv + 1));
}
static long at91sam9x5_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
unsigned long div;
- unsigned long bestrate;
- unsigned long tmp;
+
+ if (!rate)
+ return -EINVAL;
if (rate >= *parent_rate)
return *parent_rate;
- div = *parent_rate / rate;
- if (div >= SAM9X5_USB_MAX_DIV)
- return *parent_rate / (SAM9X5_USB_MAX_DIV + 1);
-
- bestrate = *parent_rate / div;
- tmp = *parent_rate / (div + 1);
- if (bestrate - rate > rate - tmp)
- bestrate = tmp;
+ div = DIV_ROUND_CLOSEST(*parent_rate, rate);
+ if (div > SAM9X5_USB_MAX_DIV + 1)
+ div = SAM9X5_USB_MAX_DIV + 1;
- return bestrate;
+ return DIV_ROUND_CLOSEST(*parent_rate, div);
}
static int at91sam9x5_clk_usb_set_parent(struct clk_hw *hw, u8 index)
u32 tmp;
struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
struct at91_pmc *pmc = usb->pmc;
- unsigned long div = parent_rate / rate;
+ unsigned long div;
+
+ if (!rate)
+ return -EINVAL;
- if (parent_rate % rate || div < 1 || div >= SAM9X5_USB_MAX_DIV)
+ div = DIV_ROUND_CLOSEST(parent_rate, rate);
+ if (div > SAM9X5_USB_MAX_DIV + 1 || !div)
return -EINVAL;
tmp = pmc_read(pmc, AT91_PMC_USB) & ~AT91_PMC_OHCIUSBDIV;
tmp_parent_rate = rate * usb->divisors[i];
tmp_parent_rate = __clk_round_rate(parent, tmp_parent_rate);
- tmprate = tmp_parent_rate / usb->divisors[i];
+ tmprate = DIV_ROUND_CLOSEST(tmp_parent_rate, usb->divisors[i]);
if (tmprate < rate)
tmpdiff = rate - tmprate;
else
struct at91_pmc *pmc = usb->pmc;
unsigned long div;
- if (!rate || parent_rate % rate)
+ if (!rate)
return -EINVAL;
- div = parent_rate / rate;
+ div = DIV_ROUND_CLOSEST(parent_rate, rate);
for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) {
if (usb->divisors[i] == div) {
if (!rate)
rate = 1;
+ /* if read only, just return current value */
+ if (divider->flags & CLK_DIVIDER_READ_ONLY) {
+ bestdiv = readl(divider->reg) >> divider->shift;
+ bestdiv &= div_mask(divider);
+ bestdiv = _get_div(divider, bestdiv);
+ return bestdiv;
+ }
+
maxdiv = _get_maxdiv(divider);
if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) {
};
EXPORT_SYMBOL_GPL(clk_divider_ops);
-const struct clk_ops clk_divider_ro_ops = {
- .recalc_rate = clk_divider_recalc_rate,
-};
-EXPORT_SYMBOL_GPL(clk_divider_ro_ops);
-
static struct clk *_register_divider(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 shift, u8 width,
}
init.name = name;
- if (clk_divider_flags & CLK_DIVIDER_READ_ONLY)
- init.ops = &clk_divider_ro_ops;
- else
- init.ops = &clk_divider_ops;
+ init.ops = &clk_divider_ops;
init.flags = flags | CLK_IS_BASIC;
init.parent_names = (parent_name ? &parent_name: NULL);
init.num_parents = (parent_name ? 1 : 0);
#include <linux/clk-provider.h>
#include <linux/platform_device.h>
#include <linux/mfd/samsung/s2mps11.h>
+#include <linux/mfd/samsung/s2mps13.h>
#include <linux/mfd/samsung/s2mps14.h>
#include <linux/mfd/samsung/s5m8767.h>
#include <linux/mfd/samsung/core.h>
},
};
+static struct clk_init_data s2mps13_clks_init[S2MPS11_CLKS_NUM] = {
+ [S2MPS11_CLK_AP] = {
+ .name = "s2mps13_ap",
+ .ops = &s2mps11_clk_ops,
+ .flags = CLK_IS_ROOT,
+ },
+ [S2MPS11_CLK_CP] = {
+ .name = "s2mps13_cp",
+ .ops = &s2mps11_clk_ops,
+ .flags = CLK_IS_ROOT,
+ },
+ [S2MPS11_CLK_BT] = {
+ .name = "s2mps13_bt",
+ .ops = &s2mps11_clk_ops,
+ .flags = CLK_IS_ROOT,
+ },
+};
+
static struct clk_init_data s2mps14_clks_init[S2MPS11_CLKS_NUM] = {
[S2MPS11_CLK_AP] = {
.name = "s2mps14_ap",
s2mps11_reg = S2MPS11_REG_RTC_CTRL;
clks_init = s2mps11_clks_init;
break;
+ case S2MPS13X:
+ s2mps11_reg = S2MPS13_REG_RTCCTRL;
+ clks_init = s2mps13_clks_init;
+ break;
case S2MPS14X:
s2mps11_reg = S2MPS14_REG_RTCCTRL;
clks_init = s2mps14_clks_init;
static const struct platform_device_id s2mps11_clk_id[] = {
{ "s2mps11-clk", S2MPS11X},
+ { "s2mps13-clk", S2MPS13X},
{ "s2mps14-clk", S2MPS14X},
{ "s5m8767-clk", S5M8767X},
{ },
unsigned long ccsr = CCSR;
osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
- a = cccr & CCCR_A_BIT;
+ a = cccr & (1 << CCCR_A_BIT);
l = ccsr & CCSR_L_MASK;
if (osc_forced || a)
unsigned long ccsr = CCSR;
osc_forced = ccsr & (1 << CCCR_CPDIS_BIT);
- a = cccr & CCCR_A_BIT;
+ a = cccr & (1 << CCCR_A_BIT);
if (osc_forced)
return PXA_MEM_13Mhz;
if (a)
[ESC1_CLK_SRC] = &esc1_clk_src.clkr,
[HDMI_CLK_SRC] = &hdmi_clk_src.clkr,
[VSYNC_CLK_SRC] = &vsync_clk_src.clkr,
- [RBCPR_CLK_SRC] = &rbcpr_clk_src.clkr,
+ [MMSS_RBCPR_CLK_SRC] = &rbcpr_clk_src.clkr,
[RBBMTIMER_CLK_SRC] = &rbbmtimer_clk_src.clkr,
[MAPLE_CLK_SRC] = &maple_clk_src.clkr,
[VDP_CLK_SRC] = &vdp_clk_src.clkr,
div->width = div_width;
div->lock = lock;
div->table = div_table;
- div_ops = (div_flags & CLK_DIVIDER_READ_ONLY)
- ? &clk_divider_ro_ops
- : &clk_divider_ops;
+ div_ops = &clk_divider_ops;
}
clk = clk_register_composite(NULL, name, parent_names, num_parents,
/* Make sure timer is stopped before playing with interrupts */
sun4i_clkevt_time_stop(0);
+ sun4i_clockevent.cpumask = cpu_possible_mask;
+ sun4i_clockevent.irq = irq;
+
+ clockevents_config_and_register(&sun4i_clockevent, rate,
+ TIMER_SYNC_TICKS, 0xffffffff);
+
ret = setup_irq(irq, &sun4i_timer_irq);
if (ret)
pr_warn("failed to setup irq %d\n", irq);
/* Enable timer0 interrupt */
val = readl(timer_base + TIMER_IRQ_EN_REG);
writel(val | TIMER_IRQ_EN(0), timer_base + TIMER_IRQ_EN_REG);
-
- sun4i_clockevent.cpumask = cpu_possible_mask;
- sun4i_clockevent.irq = irq;
-
- clockevents_config_and_register(&sun4i_clockevent, rate,
- TIMER_SYNC_TICKS, 0xffffffff);
}
CLOCKSOURCE_OF_DECLARE(sun4i, "allwinner,sun4i-a10-timer",
sun4i_timer_init);
if (ret == -EPROBE_DEFER)
dev_dbg(cpu_dev, "cpu%d clock not ready, retry\n", cpu);
else
- dev_err(cpu_dev, "failed to get cpu%d clock: %d\n", ret,
- cpu);
+ dev_err(cpu_dev, "failed to get cpu%d clock: %d\n", cpu,
+ ret);
} else {
*cdev = cpu_dev;
*creg = cpu_reg;
read_unlock_irqrestore(&cpufreq_driver_lock, flags);
- policy->governor = NULL;
+ if (policy)
+ policy->governor = NULL;
return policy;
}
u32 *desc;
struct split_key_result result;
dma_addr_t dma_addr_in, dma_addr_out;
- int ret = 0;
+ int ret = -ENOMEM;
desc = kmalloc(CAAM_CMD_SZ * 6 + CAAM_PTR_SZ * 2, GFP_KERNEL | GFP_DMA);
if (!desc) {
dev_err(jrdev, "unable to allocate key input memory\n");
- return -ENOMEM;
+ return ret;
}
- init_job_desc(desc, 0);
-
dma_addr_in = dma_map_single(jrdev, (void *)key_in, keylen,
DMA_TO_DEVICE);
if (dma_mapping_error(jrdev, dma_addr_in)) {
dev_err(jrdev, "unable to map key input memory\n");
- kfree(desc);
- return -ENOMEM;
+ goto out_free;
}
+
+ dma_addr_out = dma_map_single(jrdev, key_out, split_key_pad_len,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(jrdev, dma_addr_out)) {
+ dev_err(jrdev, "unable to map key output memory\n");
+ goto out_unmap_in;
+ }
+
+ init_job_desc(desc, 0);
append_key(desc, dma_addr_in, keylen, CLASS_2 | KEY_DEST_CLASS_REG);
/* Sets MDHA up into an HMAC-INIT */
* FIFO_STORE with the explicit split-key content store
* (0x26 output type)
*/
- dma_addr_out = dma_map_single(jrdev, key_out, split_key_pad_len,
- DMA_FROM_DEVICE);
- if (dma_mapping_error(jrdev, dma_addr_out)) {
- dev_err(jrdev, "unable to map key output memory\n");
- kfree(desc);
- return -ENOMEM;
- }
append_fifo_store(desc, dma_addr_out, split_key_len,
LDST_CLASS_2_CCB | FIFOST_TYPE_SPLIT_KEK);
dma_unmap_single(jrdev, dma_addr_out, split_key_pad_len,
DMA_FROM_DEVICE);
+out_unmap_in:
dma_unmap_single(jrdev, dma_addr_in, keylen, DMA_TO_DEVICE);
-
+out_free:
kfree(desc);
-
return ret;
}
EXPORT_SYMBOL(gen_split_key);
struct dentry *debugfs_dir;
struct list_head list;
struct module *owner;
- uint8_t accel_id;
- uint8_t numa_node;
struct adf_accel_pci accel_pci_dev;
+ uint8_t accel_id;
} __packed;
#endif
WRITE_CSR_RING_BASE(csr_addr, bank_num, i, 0);
ring = &bank->rings[i];
if (hw_data->tx_rings_mask & (1 << i)) {
- ring->inflights = kzalloc_node(sizeof(atomic_t),
- GFP_KERNEL,
- accel_dev->numa_node);
+ ring->inflights =
+ kzalloc_node(sizeof(atomic_t),
+ GFP_KERNEL,
+ dev_to_node(&GET_DEV(accel_dev)));
if (!ring->inflights)
goto err;
} else {
int i, ret;
etr_data = kzalloc_node(sizeof(*etr_data), GFP_KERNEL,
- accel_dev->numa_node);
+ dev_to_node(&GET_DEV(accel_dev)));
if (!etr_data)
return -ENOMEM;
num_banks = GET_MAX_BANKS(accel_dev);
size = num_banks * sizeof(struct adf_etr_bank_data);
- etr_data->banks = kzalloc_node(size, GFP_KERNEL, accel_dev->numa_node);
+ etr_data->banks = kzalloc_node(size, GFP_KERNEL,
+ dev_to_node(&GET_DEV(accel_dev)));
if (!etr_data->banks) {
ret = -ENOMEM;
goto err_bank;
if (unlikely(!n))
return -EINVAL;
- bufl = kmalloc_node(sz, GFP_ATOMIC, inst->accel_dev->numa_node);
+ bufl = kmalloc_node(sz, GFP_ATOMIC,
+ dev_to_node(&GET_DEV(inst->accel_dev)));
if (unlikely(!bufl))
return -ENOMEM;
goto err;
for_each_sg(assoc, sg, assoc_n, i) {
+ if (!sg->length)
+ continue;
bufl->bufers[bufs].addr = dma_map_single(dev,
sg_virt(sg),
sg->length,
struct qat_alg_buf *bufers;
buflout = kmalloc_node(sz, GFP_ATOMIC,
- inst->accel_dev->numa_node);
+ dev_to_node(&GET_DEV(inst->accel_dev)));
if (unlikely(!buflout))
goto err;
bloutp = dma_map_single(dev, buflout, sz, DMA_TO_DEVICE);
list_for_each(itr, adf_devmgr_get_head()) {
accel_dev = list_entry(itr, struct adf_accel_dev, list);
- if (accel_dev->numa_node == node && adf_dev_started(accel_dev))
+ if ((node == dev_to_node(&GET_DEV(accel_dev)) ||
+ dev_to_node(&GET_DEV(accel_dev)) < 0)
+ && adf_dev_started(accel_dev))
break;
accel_dev = NULL;
}
if (!accel_dev) {
- pr_err("QAT: Could not find device on give node\n");
+ pr_err("QAT: Could not find device on node %d\n", node);
accel_dev = adf_devmgr_get_first();
}
if (!accel_dev || !adf_dev_started(accel_dev))
for (i = 0; i < num_inst; i++) {
inst = kzalloc_node(sizeof(*inst), GFP_KERNEL,
- accel_dev->numa_node);
+ dev_to_node(&GET_DEV(accel_dev)));
if (!inst)
goto err;
uint64_t reg_val;
admin = kzalloc_node(sizeof(*accel_dev->admin), GFP_KERNEL,
- accel_dev->numa_node);
+ dev_to_node(&GET_DEV(accel_dev)));
if (!admin)
return -ENOMEM;
admin->virt_addr = dma_zalloc_coherent(&GET_DEV(accel_dev), PAGE_SIZE,
kfree(accel_dev);
}
-static uint8_t adf_get_dev_node_id(struct pci_dev *pdev)
-{
- unsigned int bus_per_cpu = 0;
- struct cpuinfo_x86 *c = &cpu_data(num_online_cpus() - 1);
-
- if (!c->phys_proc_id)
- return 0;
-
- bus_per_cpu = 256 / (c->phys_proc_id + 1);
-
- if (bus_per_cpu != 0)
- return pdev->bus->number / bus_per_cpu;
- return 0;
-}
-
static int qat_dev_start(struct adf_accel_dev *accel_dev)
{
int cpus = num_online_cpus();
void __iomem *pmisc_bar_addr = NULL;
char name[ADF_DEVICE_NAME_LENGTH];
unsigned int i, bar_nr;
- uint8_t node;
int ret;
switch (ent->device) {
return -ENODEV;
}
- node = adf_get_dev_node_id(pdev);
- accel_dev = kzalloc_node(sizeof(*accel_dev), GFP_KERNEL, node);
+ if (num_possible_nodes() > 1 && dev_to_node(&pdev->dev) < 0) {
+ /* If the accelerator is connected to a node with no memory
+ * there is no point in using the accelerator since the remote
+ * memory transaction will be very slow. */
+ dev_err(&pdev->dev, "Invalid NUMA configuration.\n");
+ return -EINVAL;
+ }
+
+ accel_dev = kzalloc_node(sizeof(*accel_dev), GFP_KERNEL,
+ dev_to_node(&pdev->dev));
if (!accel_dev)
return -ENOMEM;
- accel_dev->numa_node = node;
INIT_LIST_HEAD(&accel_dev->crypto_list);
/* Add accel device to accel table.
accel_dev->owner = THIS_MODULE;
/* Allocate and configure device configuration structure */
- hw_data = kzalloc_node(sizeof(*hw_data), GFP_KERNEL, node);
+ hw_data = kzalloc_node(sizeof(*hw_data), GFP_KERNEL,
+ dev_to_node(&pdev->dev));
if (!hw_data) {
ret = -ENOMEM;
goto out_err;
uint32_t msix_num_entries = hw_data->num_banks + 1;
entries = kzalloc_node(msix_num_entries * sizeof(*entries),
- GFP_KERNEL, accel_dev->numa_node);
+ GFP_KERNEL, dev_to_node(&GET_DEV(accel_dev)));
if (!entries)
return -ENOMEM;
}
EXPORT_SYMBOL(edma_filter_fn);
-static struct platform_device *pdev0, *pdev1;
-
-static const struct platform_device_info edma_dev_info0 = {
- .name = "edma-dma-engine",
- .id = 0,
- .dma_mask = DMA_BIT_MASK(32),
-};
-
-static const struct platform_device_info edma_dev_info1 = {
- .name = "edma-dma-engine",
- .id = 1,
- .dma_mask = DMA_BIT_MASK(32),
-};
-
static int edma_init(void)
{
- int ret = platform_driver_register(&edma_driver);
-
- if (ret == 0) {
- pdev0 = platform_device_register_full(&edma_dev_info0);
- if (IS_ERR(pdev0)) {
- platform_driver_unregister(&edma_driver);
- ret = PTR_ERR(pdev0);
- goto out;
- }
- }
-
- if (!of_have_populated_dt() && EDMA_CTLRS == 2) {
- pdev1 = platform_device_register_full(&edma_dev_info1);
- if (IS_ERR(pdev1)) {
- platform_driver_unregister(&edma_driver);
- platform_device_unregister(pdev0);
- ret = PTR_ERR(pdev1);
- }
- }
-
-out:
- return ret;
+ return platform_driver_register(&edma_driver);
}
subsys_initcall(edma_init);
static void __exit edma_exit(void)
{
- platform_device_unregister(pdev0);
- if (pdev1)
- platform_device_unregister(pdev1);
platform_driver_unregister(&edma_driver);
}
module_exit(edma_exit);
#define DMAC_MODE_NS (1 << 0)
unsigned int mode;
unsigned int data_bus_width:10; /* In number of bits */
- unsigned int data_buf_dep:10;
+ unsigned int data_buf_dep:11;
unsigned int num_chan:4;
unsigned int num_peri:6;
u32 peri_ns;
int burst_len;
burst_len = pl330->pcfg.data_bus_width / 8;
- burst_len *= pl330->pcfg.data_buf_dep;
+ burst_len *= pl330->pcfg.data_buf_dep / pl330->pcfg.num_chan;
burst_len >>= desc->rqcfg.brst_size;
/* src/dst_burst_len can't be more than 16 */
/* Select max possible burst size */
burst = pl330->pcfg.data_bus_width / 8;
- while (burst > 1) {
- if (!(len % burst))
- break;
+ /*
+ * Make sure we use a burst size that aligns with all the memcpy
+ * parameters because our DMA programming algorithm doesn't cope with
+ * transfers which straddle an entry in the DMA device's MFIFO.
+ */
+ while ((src | dst | len) & (burst - 1))
burst /= 2;
- }
desc->rqcfg.brst_size = 0;
while (burst != (1 << desc->rqcfg.brst_size))
desc->rqcfg.brst_size++;
+ /*
+ * If burst size is smaller than bus width then make sure we only
+ * transfer one at a time to avoid a burst stradling an MFIFO entry.
+ */
+ if (desc->rqcfg.brst_size * 8 < pl330->pcfg.data_bus_width)
+ desc->rqcfg.brst_len = 1;
+
desc->rqcfg.brst_len = get_burst_len(desc, len);
desc->txd.flags = flags;
dev_info(&adev->dev,
- "Loaded driver for PL330 DMAC-%d\n", adev->periphid);
+ "Loaded driver for PL330 DMAC-%x\n", adev->periphid);
dev_info(&adev->dev,
"\tDBUFF-%ux%ubytes Num_Chans-%u Num_Peri-%u Num_Events-%u\n",
pcfg->data_buf_dep, pcfg->data_bus_width / 8, pcfg->num_chan,
readl(pchan->base + DMA_CHAN_CUR_PARA));
}
-static inline int convert_burst(u32 maxburst, u8 *burst)
+static inline s8 convert_burst(u32 maxburst)
{
switch (maxburst) {
case 1:
- *burst = 0;
- break;
+ return 0;
case 8:
- *burst = 2;
- break;
+ return 2;
default:
return -EINVAL;
}
-
- return 0;
}
-static inline int convert_buswidth(enum dma_slave_buswidth addr_width, u8 *width)
+static inline s8 convert_buswidth(enum dma_slave_buswidth addr_width)
{
if ((addr_width < DMA_SLAVE_BUSWIDTH_1_BYTE) ||
(addr_width > DMA_SLAVE_BUSWIDTH_4_BYTES))
return -EINVAL;
- *width = addr_width >> 1;
- return 0;
+ return addr_width >> 1;
}
static void *sun6i_dma_lli_add(struct sun6i_dma_lli *prev,
struct dma_slave_config *config)
{
u8 src_width, dst_width, src_burst, dst_burst;
- int ret;
if (!config)
return -EINVAL;
- ret = convert_burst(config->src_maxburst, &src_burst);
- if (ret)
- return ret;
+ src_burst = convert_burst(config->src_maxburst);
+ if (src_burst)
+ return src_burst;
- ret = convert_burst(config->dst_maxburst, &dst_burst);
- if (ret)
- return ret;
+ dst_burst = convert_burst(config->dst_maxburst);
+ if (dst_burst)
+ return dst_burst;
- ret = convert_buswidth(config->src_addr_width, &src_width);
- if (ret)
- return ret;
+ src_width = convert_buswidth(config->src_addr_width);
+ if (src_width)
+ return src_width;
- ret = convert_buswidth(config->dst_addr_width, &dst_width);
- if (ret)
- return ret;
+ dst_width = convert_buswidth(config->dst_addr_width);
+ if (dst_width)
+ return dst_width;
lli->cfg = DMA_CHAN_CFG_SRC_BURST(src_burst) |
DMA_CHAN_CFG_SRC_WIDTH(src_width) |
{
struct sun6i_dma_dev *sdev = to_sun6i_dma_dev(chan->device);
struct sun6i_vchan *vchan = to_sun6i_vchan(chan);
- struct dma_slave_config *sconfig = &vchan->cfg;
struct sun6i_dma_lli *v_lli;
struct sun6i_desc *txd;
dma_addr_t p_lli;
- int ret;
+ s8 burst, width;
dev_dbg(chan2dev(chan),
"%s; chan: %d, dest: %pad, src: %pad, len: %zu. flags: 0x%08lx\n",
goto err_txd_free;
}
- ret = sun6i_dma_cfg_lli(v_lli, src, dest, len, sconfig);
- if (ret)
- goto err_dma_free;
+ v_lli->src = src;
+ v_lli->dst = dest;
+ v_lli->len = len;
+ v_lli->para = NORMAL_WAIT;
+ burst = convert_burst(8);
+ width = convert_buswidth(DMA_SLAVE_BUSWIDTH_4_BYTES);
v_lli->cfg |= DMA_CHAN_CFG_SRC_DRQ(DRQ_SDRAM) |
DMA_CHAN_CFG_DST_DRQ(DRQ_SDRAM) |
DMA_CHAN_CFG_DST_LINEAR_MODE |
- DMA_CHAN_CFG_SRC_LINEAR_MODE;
+ DMA_CHAN_CFG_SRC_LINEAR_MODE |
+ DMA_CHAN_CFG_SRC_BURST(burst) |
+ DMA_CHAN_CFG_SRC_WIDTH(width) |
+ DMA_CHAN_CFG_DST_BURST(burst) |
+ DMA_CHAN_CFG_DST_WIDTH(width);
sun6i_dma_lli_add(NULL, v_lli, p_lli, txd);
return vchan_tx_prep(&vchan->vc, &txd->vd, flags);
-err_dma_free:
- dma_pool_free(sdev->pool, v_lli, p_lli);
err_txd_free:
kfree(txd);
return NULL;
sdc->slave.device_prep_dma_memcpy = sun6i_dma_prep_dma_memcpy;
sdc->slave.device_control = sun6i_dma_control;
sdc->slave.chancnt = NR_MAX_VCHANS;
+ sdc->slave.copy_align = 4;
sdc->slave.dev = &pdev->dev;
has been initialized.
config EDAC_MCE_INJ
- tristate "Simple MCE injection interface over /sysfs"
- depends on EDAC_DECODE_MCE
+ tristate "Simple MCE injection interface"
+ depends on EDAC_DECODE_MCE && DEBUG_FS
default n
help
- This is a simple interface to inject MCEs over /sysfs and test
- the MCE decoding code in EDAC.
+ This is a simple debugfs interface to inject MCEs and test different
+ aspects of the MCE handling code.
- This is currently AMD-only.
+ WARNING: Do not even assume this interface is staying stable!
config EDAC_MM_EDAC
tristate "Main Memory EDAC (Error Detection And Correction) reporting"
In doubt, say 'Y'.
config EDAC_AMD64
- tristate "AMD64 (Opteron, Athlon64) K8, F10h"
- depends on EDAC_MM_EDAC && AMD_NB && X86_64 && EDAC_DECODE_MCE
+ tristate "AMD64 (Opteron, Athlon64)"
+ depends on EDAC_MM_EDAC && AMD_NB && EDAC_DECODE_MCE
help
Support for error detection and correction of DRAM ECC errors on
- the AMD64 families of memory controllers (K8 and F10h)
+ the AMD64 families (>= K8) of memory controllers.
config EDAC_AMD64_ERROR_INJECTION
bool "Sysfs HW Error injection facilities"
obj-$(CONFIG_EDAC) := edac_stub.o
obj-$(CONFIG_EDAC_MM_EDAC) += edac_core.o
-edac_core-y := edac_mc.o edac_device.o edac_mc_sysfs.o edac_pci_sysfs.o
+edac_core-y := edac_mc.o edac_device.o edac_mc_sysfs.o
edac_core-y += edac_module.o edac_device_sysfs.o
ifdef CONFIG_PCI
{
edac_dbg(1, "F2x%d90 (DRAM Cfg Low): 0x%08x\n", chan, dclr);
- edac_dbg(1, " DIMM type: %sbuffered; all DIMMs support ECC: %s\n",
- (dclr & BIT(16)) ? "un" : "",
- (dclr & BIT(19)) ? "yes" : "no");
+ if (pvt->dram_type == MEM_LRDDR3) {
+ u32 dcsm = pvt->csels[chan].csmasks[0];
+ /*
+ * It's assumed all LRDIMMs in a DCT are going to be of
+ * same 'type' until proven otherwise. So, use a cs
+ * value of '0' here to get dcsm value.
+ */
+ edac_dbg(1, " LRDIMM %dx rank multiply\n", (dcsm & 0x3));
+ }
+
+ edac_dbg(1, "All DIMMs support ECC:%s\n",
+ (dclr & BIT(19)) ? "yes" : "no");
+
edac_dbg(1, " PAR/ERR parity: %s\n",
(dclr & BIT(8)) ? "enabled" : "disabled");
if (pvt->fam == 0xf && pvt->ext_model < K8_REV_F) {
pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 8;
pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 8;
- } else if (pvt->fam == 0x15 && pvt->model >= 0x30) {
+ } else if (pvt->fam == 0x15 && pvt->model == 0x30) {
pvt->csels[0].b_cnt = pvt->csels[1].b_cnt = 4;
pvt->csels[0].m_cnt = pvt->csels[1].m_cnt = 2;
} else {
}
}
-static enum mem_type determine_memory_type(struct amd64_pvt *pvt, int cs)
+static void determine_memory_type(struct amd64_pvt *pvt)
{
- enum mem_type type;
+ u32 dram_ctrl, dcsm;
- /* F15h supports only DDR3 */
- if (pvt->fam >= 0x15)
- type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3;
- else if (pvt->fam == 0x10 || pvt->ext_model >= K8_REV_F) {
+ switch (pvt->fam) {
+ case 0xf:
+ if (pvt->ext_model >= K8_REV_F)
+ goto ddr3;
+
+ pvt->dram_type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR;
+ return;
+
+ case 0x10:
if (pvt->dchr0 & DDR3_MODE)
- type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3;
+ goto ddr3;
+
+ pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2;
+ return;
+
+ case 0x15:
+ if (pvt->model < 0x60)
+ goto ddr3;
+
+ /*
+ * Model 0x60h needs special handling:
+ *
+ * We use a Chip Select value of '0' to obtain dcsm.
+ * Theoretically, it is possible to populate LRDIMMs of different
+ * 'Rank' value on a DCT. But this is not the common case. So,
+ * it's reasonable to assume all DIMMs are going to be of same
+ * 'type' until proven otherwise.
+ */
+ amd64_read_dct_pci_cfg(pvt, 0, DRAM_CONTROL, &dram_ctrl);
+ dcsm = pvt->csels[0].csmasks[0];
+
+ if (((dram_ctrl >> 8) & 0x7) == 0x2)
+ pvt->dram_type = MEM_DDR4;
+ else if (pvt->dclr0 & BIT(16))
+ pvt->dram_type = MEM_DDR3;
+ else if (dcsm & 0x3)
+ pvt->dram_type = MEM_LRDDR3;
else
- type = (pvt->dclr0 & BIT(16)) ? MEM_DDR2 : MEM_RDDR2;
- } else {
- type = (pvt->dclr0 & BIT(18)) ? MEM_DDR : MEM_RDDR;
- }
+ pvt->dram_type = MEM_RDDR3;
- amd64_info("CS%d: %s\n", cs, edac_mem_types[type]);
+ return;
- return type;
+ case 0x16:
+ goto ddr3;
+
+ default:
+ WARN(1, KERN_ERR "%s: Family??? 0x%x\n", __func__, pvt->fam);
+ pvt->dram_type = MEM_EMPTY;
+ }
+ return;
+
+ddr3:
+ pvt->dram_type = (pvt->dclr0 & BIT(16)) ? MEM_DDR3 : MEM_RDDR3;
}
/* Get the number of DCT channels the memory controller is using. */
if (WARN_ON(!nb))
return;
- pci_func = (pvt->model == 0x30) ? PCI_DEVICE_ID_AMD_15H_M30H_NB_F1
- : PCI_DEVICE_ID_AMD_15H_NB_F1;
+ if (pvt->model == 0x60)
+ pci_func = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1;
+ else if (pvt->model == 0x30)
+ pci_func = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1;
+ else
+ pci_func = PCI_DEVICE_ID_AMD_15H_NB_F1;
f1 = pci_get_related_function(nb->misc->vendor, pci_func, nb->misc);
if (WARN_ON(!f1))
}
static int k8_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
- unsigned cs_mode)
+ unsigned cs_mode, int cs_mask_nr)
{
u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
return cs_size;
}
+static int ddr3_lrdimm_cs_size(unsigned i, unsigned rank_multiply)
+{
+ unsigned shift = 0;
+ int cs_size = 0;
+
+ if (i < 4 || i == 6)
+ cs_size = -1;
+ else if (i == 12)
+ shift = 7;
+ else if (!(i & 0x1))
+ shift = i >> 1;
+ else
+ shift = (i + 1) >> 1;
+
+ if (cs_size != -1)
+ cs_size = rank_multiply * (128 << shift);
+
+ return cs_size;
+}
+
+static int ddr4_cs_size(unsigned i)
+{
+ int cs_size = 0;
+
+ if (i == 0)
+ cs_size = -1;
+ else if (i == 1)
+ cs_size = 1024;
+ else
+ /* Min cs_size = 1G */
+ cs_size = 1024 * (1 << (i >> 1));
+
+ return cs_size;
+}
+
static int f10_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
- unsigned cs_mode)
+ unsigned cs_mode, int cs_mask_nr)
{
u32 dclr = dct ? pvt->dclr1 : pvt->dclr0;
* F15h supports only 64bit DCT interfaces
*/
static int f15_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
- unsigned cs_mode)
+ unsigned cs_mode, int cs_mask_nr)
{
WARN_ON(cs_mode > 12);
return ddr3_cs_size(cs_mode, false);
}
+/* F15h M60h supports DDR4 mapping as well.. */
+static int f15_m60h_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
+ unsigned cs_mode, int cs_mask_nr)
+{
+ int cs_size;
+ u32 dcsm = pvt->csels[dct].csmasks[cs_mask_nr];
+
+ WARN_ON(cs_mode > 12);
+
+ if (pvt->dram_type == MEM_DDR4) {
+ if (cs_mode > 9)
+ return -1;
+
+ cs_size = ddr4_cs_size(cs_mode);
+ } else if (pvt->dram_type == MEM_LRDDR3) {
+ unsigned rank_multiply = dcsm & 0xf;
+
+ if (rank_multiply == 3)
+ rank_multiply = 4;
+ cs_size = ddr3_lrdimm_cs_size(cs_mode, rank_multiply);
+ } else {
+ /* Minimum cs size is 512mb for F15hM60h*/
+ if (cs_mode == 0x1)
+ return -1;
+
+ cs_size = ddr3_cs_size(cs_mode, false);
+ }
+
+ return cs_size;
+}
+
/*
* F16h and F15h model 30h have only limited cs_modes.
*/
static int f16_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
- unsigned cs_mode)
+ unsigned cs_mode, int cs_mask_nr)
{
WARN_ON(cs_mode > 12);
size0 = 0;
if (dcsb[dimm*2] & DCSB_CS_ENABLE)
+ /* For f15m60h, need multiplier for LRDIMM cs_size
+ * calculation. We pass 'dimm' value to the dbam_to_cs
+ * mapper so we can find the multiplier from the
+ * corresponding DCSM.
+ */
size0 = pvt->ops->dbam_to_cs(pvt, ctrl,
- DBAM_DIMM(dimm, dbam));
+ DBAM_DIMM(dimm, dbam),
+ dimm);
size1 = 0;
if (dcsb[dimm*2 + 1] & DCSB_CS_ENABLE)
size1 = pvt->ops->dbam_to_cs(pvt, ctrl,
- DBAM_DIMM(dimm, dbam));
+ DBAM_DIMM(dimm, dbam),
+ dimm);
amd64_info(EDAC_MC ": %d: %5dMB %d: %5dMB\n",
dimm * 2, size0,
.dbam_to_cs = f16_dbam_to_chip_select,
}
},
+ [F15_M60H_CPUS] = {
+ .ctl_name = "F15h_M60h",
+ .f1_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1,
+ .f3_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F3,
+ .ops = {
+ .early_channel_count = f1x_early_channel_count,
+ .map_sysaddr_to_csrow = f1x_map_sysaddr_to_csrow,
+ .dbam_to_cs = f15_m60h_dbam_to_chip_select,
+ }
+ },
[F16_CPUS] = {
.ctl_name = "F16h",
.f1_id = PCI_DEVICE_ID_AMD_16H_NB_F1,
}
pvt->ecc_sym_sz = 4;
+ determine_memory_type(pvt);
+ edac_dbg(1, " DIMM type: %s\n", edac_mem_types[pvt->dram_type]);
if (pvt->fam >= 0x10) {
amd64_read_pci_cfg(pvt->F3, EXT_NB_MCA_CFG, &tmp);
*/
cs_mode = DBAM_DIMM(csrow_nr / 2, dbam);
- nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode) << (20 - PAGE_SHIFT);
+ nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode, (csrow_nr / 2))
+ << (20 - PAGE_SHIFT);
edac_dbg(0, "csrow: %d, channel: %d, DBAM idx: %d\n",
csrow_nr, dct, cs_mode);
struct csrow_info *csrow;
struct dimm_info *dimm;
enum edac_type edac_mode;
- enum mem_type mtype;
int i, j, empty = 1;
int nr_pages = 0;
u32 val;
nr_pages += row_dct1_pages;
}
- mtype = determine_memory_type(pvt, i);
-
edac_dbg(1, "Total csrow%d pages: %u\n", i, nr_pages);
/*
for (j = 0; j < pvt->channel_count; j++) {
dimm = csrow->channels[j]->dimm;
- dimm->mtype = mtype;
+ dimm->mtype = pvt->dram_type;
dimm->edac_mode = edac_mode;
}
}
fam_type = &family_types[F15_M30H_CPUS];
pvt->ops = &family_types[F15_M30H_CPUS].ops;
break;
+ } else if (pvt->model == 0x60) {
+ fam_type = &family_types[F15_M60H_CPUS];
+ pvt->ops = &family_types[F15_M60H_CPUS].ops;
+ break;
}
fam_type = &family_types[F15_CPUS];
* inquiry this table to see if this driver is for a given device found.
*/
static const struct pci_device_id amd64_pci_table[] = {
- {
- .vendor = PCI_VENDOR_ID_AMD,
- .device = PCI_DEVICE_ID_AMD_K8_NB_MEMCTL,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .class = 0,
- .class_mask = 0,
- },
- {
- .vendor = PCI_VENDOR_ID_AMD,
- .device = PCI_DEVICE_ID_AMD_10H_NB_DRAM,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .class = 0,
- .class_mask = 0,
- },
- {
- .vendor = PCI_VENDOR_ID_AMD,
- .device = PCI_DEVICE_ID_AMD_15H_NB_F2,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .class = 0,
- .class_mask = 0,
- },
- {
- .vendor = PCI_VENDOR_ID_AMD,
- .device = PCI_DEVICE_ID_AMD_15H_M30H_NB_F2,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .class = 0,
- .class_mask = 0,
- },
- {
- .vendor = PCI_VENDOR_ID_AMD,
- .device = PCI_DEVICE_ID_AMD_16H_NB_F2,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .class = 0,
- .class_mask = 0,
- },
- {
- .vendor = PCI_VENDOR_ID_AMD,
- .device = PCI_DEVICE_ID_AMD_16H_M30H_NB_F2,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- .class = 0,
- .class_mask = 0,
- },
-
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_K8_NB_MEMCTL) },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_10H_NB_DRAM) },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F2) },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F2) },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F2) },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F2) },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F2) },
{0, }
};
MODULE_DEVICE_TABLE(pci, amd64_pci_table);
goto err_no_instances;
setup_pci_device();
+
+#ifdef CONFIG_X86_32
+ amd64_err("%s on 32-bit is unsupported. USE AT YOUR OWN RISK!\n", EDAC_MOD_STR);
+#endif
+
return 0;
err_no_instances:
/*
* PCI-defined configuration space registers
*/
-#define PCI_DEVICE_ID_AMD_15H_M30H_NB_F1 0x141b
-#define PCI_DEVICE_ID_AMD_15H_M30H_NB_F2 0x141c
#define PCI_DEVICE_ID_AMD_15H_NB_F1 0x1601
#define PCI_DEVICE_ID_AMD_15H_NB_F2 0x1602
+#define PCI_DEVICE_ID_AMD_15H_M30H_NB_F1 0x141b
+#define PCI_DEVICE_ID_AMD_15H_M30H_NB_F2 0x141c
+#define PCI_DEVICE_ID_AMD_15H_M60H_NB_F1 0x1571
+#define PCI_DEVICE_ID_AMD_15H_M60H_NB_F2 0x1572
#define PCI_DEVICE_ID_AMD_16H_NB_F1 0x1531
#define PCI_DEVICE_ID_AMD_16H_NB_F2 0x1532
#define PCI_DEVICE_ID_AMD_16H_M30H_NB_F1 0x1581
#define csrow_enabled(i, dct, pvt) ((pvt)->csels[(dct)].csbases[(i)] & DCSB_CS_ENABLE)
+#define DRAM_CONTROL 0x78
+
#define DBAM0 0x80
#define DBAM1 0x180
F10_CPUS,
F15_CPUS,
F15_M30H_CPUS,
+ F15_M60H_CPUS,
F16_CPUS,
F16_M30H_CPUS,
NUM_FAMILIES,
/* place to store error injection parameters prior to issue */
struct error_injection injection;
+
+ /* cache the dram_type */
+ enum mem_type dram_type;
};
enum err_codes {
int (*early_channel_count) (struct amd64_pvt *pvt);
void (*map_sysaddr_to_csrow) (struct mem_ctl_info *mci, u64 sys_addr,
struct err_info *);
- int (*dbam_to_cs) (struct amd64_pvt *pvt, u8 dct, unsigned cs_mode);
+ int (*dbam_to_cs) (struct amd64_pvt *pvt, u8 dct,
+ unsigned cs_mode, int cs_mask_nr);
};
struct amd64_family_type {
#endif /* CONFIG_EDAC_DEBUG */
-/*
- * keep those in sync with the enum mem_type
- */
const char * const edac_mem_types[] = {
- "Empty csrow",
- "Reserved csrow type",
- "Unknown csrow type",
- "Fast page mode RAM",
- "Extended data out RAM",
- "Burst Extended data out RAM",
- "Single data rate SDRAM",
- "Registered single data rate SDRAM",
- "Double data rate SDRAM",
- "Registered Double data rate SDRAM",
- "Rambus DRAM",
- "Unbuffered DDR2 RAM",
- "Fully buffered DDR2",
- "Registered DDR2 RAM",
- "Rambus XDR",
- "Unbuffered DDR3 RAM",
- "Registered DDR3 RAM",
+ [MEM_EMPTY] = "Empty csrow",
+ [MEM_RESERVED] = "Reserved csrow type",
+ [MEM_UNKNOWN] = "Unknown csrow type",
+ [MEM_FPM] = "Fast page mode RAM",
+ [MEM_EDO] = "Extended data out RAM",
+ [MEM_BEDO] = "Burst Extended data out RAM",
+ [MEM_SDR] = "Single data rate SDRAM",
+ [MEM_RDR] = "Registered single data rate SDRAM",
+ [MEM_DDR] = "Double data rate SDRAM",
+ [MEM_RDDR] = "Registered Double data rate SDRAM",
+ [MEM_RMBS] = "Rambus DRAM",
+ [MEM_DDR2] = "Unbuffered DDR2 RAM",
+ [MEM_FB_DDR2] = "Fully buffered DDR2",
+ [MEM_RDDR2] = "Registered DDR2 RAM",
+ [MEM_XDR] = "Rambus XDR",
+ [MEM_DDR3] = "Unbuffered DDR3 RAM",
+ [MEM_RDDR3] = "Registered DDR3 RAM",
+ [MEM_LRDDR3] = "Load-Reduced DDR3 RAM",
+ [MEM_DDR4] = "Unbuffered DDR4 RAM",
+ [MEM_RDDR4] = "Registered DDR4 RAM",
};
EXPORT_SYMBOL_GPL(edac_mem_types);
#include "edac_core.h"
#include "edac_module.h"
-/* Turn off this whole feature if PCI is not configured */
-#ifdef CONFIG_PCI
-
#define EDAC_PCI_SYMLINK "device"
/* data variables exported via sysfs */
module_param(edac_pci_panic_on_pe, int, 0644);
MODULE_PARM_DESC(edac_pci_panic_on_pe,
"Panic on PCI Bus Parity error: 0=off 1=on");
-
-#endif /* CONFIG_PCI */
/* Generate the trace event */
grain_bits = fls_long(e->grain);
- sprintf(pvt->detail_location, "APEI location: %s %s",
- e->location, e->other_detail);
+ snprintf(pvt->detail_location, sizeof(pvt->detail_location),
+ "APEI location: %s %s", e->location, e->other_detail);
trace_mc_event(type, e->msg, e->label, e->error_count,
mci->mc_idx, e->top_layer, e->mid_layer, e->low_layer,
PAGES_TO_MiB(e->page_frame_number) | e->offset_in_page,
pci_unregister_driver(&i3000_driver);
fail0:
- if (mci_pdev)
- pci_dev_put(mci_pdev);
+ pci_dev_put(mci_pdev);
return pci_rc;
}
pci_unregister_driver(&i3200_driver);
fail0:
- if (mci_pdev)
- pci_dev_put(mci_pdev);
+ pci_dev_put(mci_pdev);
return pci_rc;
}
if (!i82443bxgx_registered)
i82443bxgx_edacmc_remove_one(mci_pdev);
- if (mci_pdev)
- pci_dev_put(mci_pdev);
+ pci_dev_put(mci_pdev);
}
module_init(i82443bxgx_edacmc_init);
"Retire status queue"
};
+static const char * const mc6_mce_desc[] = {
+ "Hardware Assertion",
+ "Free List",
+ "Physical Register File",
+ "Retire Queue",
+ "Scheduler table",
+ "Status Register File",
+};
+
static bool f12h_mc0_mce(u16 ec, u8 xec)
{
bool ret = false;
pr_cont(": %s error in the L2 cache tags.\n", R4_MSG(ec));
else if (xec == 0x0) {
if (TLB_ERROR(ec))
- pr_cont(": %s error in a Page Descriptor Cache or "
- "Guest TLB.\n", TT_MSG(ec));
+ pr_cont("%s error in a Page Descriptor Cache or Guest TLB.\n",
+ TT_MSG(ec));
else if (BUS_ERROR(ec))
pr_cont(": %s/ECC error in data read from NB: %s.\n",
R4_MSG(ec), PP_MSG(ec));
pr_emerg(HW_ERR "MC6 Error: ");
- switch (xec) {
- case 0x0:
- pr_cont("Hardware Assertion");
- break;
-
- case 0x1:
- pr_cont("Free List");
- break;
-
- case 0x2:
- pr_cont("Physical Register File");
- break;
-
- case 0x3:
- pr_cont("Retire Queue");
- break;
-
- case 0x4:
- pr_cont("Scheduler table");
- break;
-
- case 0x5:
- pr_cont("Status Register File");
- break;
-
- default:
+ if (xec > 0x5)
goto wrong_mc6_mce;
- break;
- }
-
- pr_cont(" parity error.\n");
+ pr_cont("%s parity error.\n", mc6_mce_desc[xec]);
return;
wrong_mc6_mce:
pr_cont("]: 0x%016llx\n", m->status);
if (m->status & MCI_STATUS_ADDRV)
- pr_emerg(HW_ERR "MC%d_ADDR: 0x%016llx\n", m->bank, m->addr);
+ pr_emerg(HW_ERR "MC%d Error Address: 0x%016llx\n", m->bank, m->addr);
if (!fam_ops)
goto err_code;
/*
- * A simple MCE injection facility for testing the MCE decoding code. This
- * driver should be built as module so that it can be loaded on production
- * kernels for testing purposes.
+ * A simple MCE injection facility for testing different aspects of the RAS
+ * code. This driver should be built as module so that it can be loaded
+ * on production kernels for testing purposes.
*
* This file may be distributed under the terms of the GNU General Public
* License version 2.
*
- * Copyright (c) 2010: Borislav Petkov <bp@alien8.de>
+ * Copyright (c) 2010-14: Borislav Petkov <bp@alien8.de>
* Advanced Micro Devices Inc.
*/
#include <linux/kobject.h>
+#include <linux/debugfs.h>
#include <linux/device.h>
-#include <linux/edac.h>
#include <linux/module.h>
+#include <linux/cpu.h>
#include <asm/mce.h>
#include "mce_amd.h"
-struct edac_mce_attr {
- struct attribute attr;
- ssize_t (*show) (struct kobject *kobj, struct edac_mce_attr *attr, char *buf);
- ssize_t (*store)(struct kobject *kobj, struct edac_mce_attr *attr,
- const char *buf, size_t count);
-};
-
-#define EDAC_MCE_ATTR(_name, _mode, _show, _store) \
-static struct edac_mce_attr mce_attr_##_name = __ATTR(_name, _mode, _show, _store)
-
-static struct kobject *mce_kobj;
-
/*
* Collect all the MCi_XXX settings
*/
static struct mce i_mce;
+static struct dentry *dfs_inj;
-#define MCE_INJECT_STORE(reg) \
-static ssize_t edac_inject_##reg##_store(struct kobject *kobj, \
- struct edac_mce_attr *attr, \
- const char *data, size_t count)\
+#define MCE_INJECT_SET(reg) \
+static int inj_##reg##_set(void *data, u64 val) \
{ \
- int ret = 0; \
- unsigned long value; \
- \
- ret = kstrtoul(data, 16, &value); \
- if (ret < 0) \
- printk(KERN_ERR "Error writing MCE " #reg " field.\n"); \
+ struct mce *m = (struct mce *)data; \
\
- i_mce.reg = value; \
- \
- return count; \
+ m->reg = val; \
+ return 0; \
}
-MCE_INJECT_STORE(status);
-MCE_INJECT_STORE(misc);
-MCE_INJECT_STORE(addr);
+MCE_INJECT_SET(status);
+MCE_INJECT_SET(misc);
+MCE_INJECT_SET(addr);
-#define MCE_INJECT_SHOW(reg) \
-static ssize_t edac_inject_##reg##_show(struct kobject *kobj, \
- struct edac_mce_attr *attr, \
- char *buf) \
+#define MCE_INJECT_GET(reg) \
+static int inj_##reg##_get(void *data, u64 *val) \
{ \
- return sprintf(buf, "0x%016llx\n", i_mce.reg); \
+ struct mce *m = (struct mce *)data; \
+ \
+ *val = m->reg; \
+ return 0; \
}
-MCE_INJECT_SHOW(status);
-MCE_INJECT_SHOW(misc);
-MCE_INJECT_SHOW(addr);
+MCE_INJECT_GET(status);
+MCE_INJECT_GET(misc);
+MCE_INJECT_GET(addr);
-EDAC_MCE_ATTR(status, 0644, edac_inject_status_show, edac_inject_status_store);
-EDAC_MCE_ATTR(misc, 0644, edac_inject_misc_show, edac_inject_misc_store);
-EDAC_MCE_ATTR(addr, 0644, edac_inject_addr_show, edac_inject_addr_store);
+DEFINE_SIMPLE_ATTRIBUTE(status_fops, inj_status_get, inj_status_set, "%llx\n");
+DEFINE_SIMPLE_ATTRIBUTE(misc_fops, inj_misc_get, inj_misc_set, "%llx\n");
+DEFINE_SIMPLE_ATTRIBUTE(addr_fops, inj_addr_get, inj_addr_set, "%llx\n");
/*
- * This denotes into which bank we're injecting and triggers
- * the injection, at the same time.
+ * Caller needs to be make sure this cpu doesn't disappear
+ * from under us, i.e.: get_cpu/put_cpu.
*/
-static ssize_t edac_inject_bank_store(struct kobject *kobj,
- struct edac_mce_attr *attr,
- const char *data, size_t count)
+static int toggle_hw_mce_inject(unsigned int cpu, bool enable)
{
- int ret = 0;
- unsigned long value;
+ u32 l, h;
+ int err;
- ret = kstrtoul(data, 10, &value);
- if (ret < 0) {
- printk(KERN_ERR "Invalid bank value!\n");
- return -EINVAL;
+ err = rdmsr_on_cpu(cpu, MSR_K7_HWCR, &l, &h);
+ if (err) {
+ pr_err("%s: error reading HWCR\n", __func__);
+ return err;
}
- if (value > 5)
- if (boot_cpu_data.x86 != 0x15 || value > 6) {
- printk(KERN_ERR "Non-existent MCE bank: %lu\n", value);
- return -EINVAL;
- }
+ enable ? (l |= BIT(18)) : (l &= ~BIT(18));
- i_mce.bank = value;
+ err = wrmsr_on_cpu(cpu, MSR_K7_HWCR, l, h);
+ if (err)
+ pr_err("%s: error writing HWCR\n", __func__);
- amd_decode_mce(NULL, 0, &i_mce);
+ return err;
+}
- return count;
+static int flags_get(void *data, u64 *val)
+{
+ struct mce *m = (struct mce *)data;
+
+ *val = m->inject_flags;
+
+ return 0;
}
-static ssize_t edac_inject_bank_show(struct kobject *kobj,
- struct edac_mce_attr *attr, char *buf)
+static int flags_set(void *data, u64 val)
{
- return sprintf(buf, "%d\n", i_mce.bank);
+ struct mce *m = (struct mce *)data;
+
+ m->inject_flags = (u8)val;
+ return 0;
}
-EDAC_MCE_ATTR(bank, 0644, edac_inject_bank_show, edac_inject_bank_store);
+DEFINE_SIMPLE_ATTRIBUTE(flags_fops, flags_get, flags_set, "%llu\n");
-static struct edac_mce_attr *sysfs_attrs[] = { &mce_attr_status, &mce_attr_misc,
- &mce_attr_addr, &mce_attr_bank
-};
+/*
+ * On which CPU to inject?
+ */
+MCE_INJECT_GET(extcpu);
-static int __init edac_init_mce_inject(void)
+static int inj_extcpu_set(void *data, u64 val)
{
- struct bus_type *edac_subsys = NULL;
- int i, err = 0;
+ struct mce *m = (struct mce *)data;
- edac_subsys = edac_get_sysfs_subsys();
- if (!edac_subsys)
+ if (val >= nr_cpu_ids || !cpu_online(val)) {
+ pr_err("%s: Invalid CPU: %llu\n", __func__, val);
return -EINVAL;
+ }
+ m->extcpu = val;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(extcpu_fops, inj_extcpu_get, inj_extcpu_set, "%llu\n");
- mce_kobj = kobject_create_and_add("mce", &edac_subsys->dev_root->kobj);
- if (!mce_kobj) {
- printk(KERN_ERR "Error creating a mce kset.\n");
- err = -ENOMEM;
- goto err_mce_kobj;
+static void trigger_mce(void *info)
+{
+ asm volatile("int $18");
+}
+
+static void do_inject(void)
+{
+ u64 mcg_status = 0;
+ unsigned int cpu = i_mce.extcpu;
+ u8 b = i_mce.bank;
+
+ if (!(i_mce.inject_flags & MCJ_EXCEPTION)) {
+ amd_decode_mce(NULL, 0, &i_mce);
+ return;
}
- for (i = 0; i < ARRAY_SIZE(sysfs_attrs); i++) {
- err = sysfs_create_file(mce_kobj, &sysfs_attrs[i]->attr);
- if (err) {
- printk(KERN_ERR "Error creating %s in sysfs.\n",
- sysfs_attrs[i]->attr.name);
- goto err_sysfs_create;
+ get_online_cpus();
+ if (!cpu_online(cpu))
+ goto err;
+
+ /* prep MCE global settings for the injection */
+ mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV;
+
+ if (!(i_mce.status & MCI_STATUS_PCC))
+ mcg_status |= MCG_STATUS_RIPV;
+
+ toggle_hw_mce_inject(cpu, true);
+
+ wrmsr_on_cpu(cpu, MSR_IA32_MCG_STATUS,
+ (u32)mcg_status, (u32)(mcg_status >> 32));
+
+ wrmsr_on_cpu(cpu, MSR_IA32_MCx_STATUS(b),
+ (u32)i_mce.status, (u32)(i_mce.status >> 32));
+
+ wrmsr_on_cpu(cpu, MSR_IA32_MCx_ADDR(b),
+ (u32)i_mce.addr, (u32)(i_mce.addr >> 32));
+
+ wrmsr_on_cpu(cpu, MSR_IA32_MCx_MISC(b),
+ (u32)i_mce.misc, (u32)(i_mce.misc >> 32));
+
+ toggle_hw_mce_inject(cpu, false);
+
+ smp_call_function_single(cpu, trigger_mce, NULL, 0);
+
+err:
+ put_online_cpus();
+
+}
+
+/*
+ * This denotes into which bank we're injecting and triggers
+ * the injection, at the same time.
+ */
+static int inj_bank_set(void *data, u64 val)
+{
+ struct mce *m = (struct mce *)data;
+
+ if (val > 5) {
+ if (boot_cpu_data.x86 != 0x15 || val > 6) {
+ pr_err("Non-existent MCE bank: %llu\n", val);
+ return -EINVAL;
}
}
- return 0;
-err_sysfs_create:
- while (--i >= 0)
- sysfs_remove_file(mce_kobj, &sysfs_attrs[i]->attr);
+ m->bank = val;
+ do_inject();
- kobject_del(mce_kobj);
+ return 0;
+}
-err_mce_kobj:
- edac_put_sysfs_subsys();
+static int inj_bank_get(void *data, u64 *val)
+{
+ struct mce *m = (struct mce *)data;
- return err;
+ *val = m->bank;
+ return 0;
}
-static void __exit edac_exit_mce_inject(void)
+DEFINE_SIMPLE_ATTRIBUTE(bank_fops, inj_bank_get, inj_bank_set, "%llu\n");
+
+struct dfs_node {
+ char *name;
+ struct dentry *d;
+ const struct file_operations *fops;
+} dfs_fls[] = {
+ { .name = "status", .fops = &status_fops },
+ { .name = "misc", .fops = &misc_fops },
+ { .name = "addr", .fops = &addr_fops },
+ { .name = "bank", .fops = &bank_fops },
+ { .name = "flags", .fops = &flags_fops },
+ { .name = "cpu", .fops = &extcpu_fops },
+};
+
+static int __init init_mce_inject(void)
{
int i;
- for (i = 0; i < ARRAY_SIZE(sysfs_attrs); i++)
- sysfs_remove_file(mce_kobj, &sysfs_attrs[i]->attr);
+ dfs_inj = debugfs_create_dir("mce-inject", NULL);
+ if (!dfs_inj)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(dfs_fls); i++) {
+ dfs_fls[i].d = debugfs_create_file(dfs_fls[i].name,
+ S_IRUSR | S_IWUSR,
+ dfs_inj,
+ &i_mce,
+ dfs_fls[i].fops);
+
+ if (!dfs_fls[i].d)
+ goto err_dfs_add;
+ }
+
+ return 0;
+
+err_dfs_add:
+ while (--i >= 0)
+ debugfs_remove(dfs_fls[i].d);
- kobject_del(mce_kobj);
+ debugfs_remove(dfs_inj);
+ dfs_inj = NULL;
- edac_put_sysfs_subsys();
+ return -ENOMEM;
}
-module_init(edac_init_mce_inject);
-module_exit(edac_exit_mce_inject);
+static void __exit exit_mce_inject(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dfs_fls); i++)
+ debugfs_remove(dfs_fls[i].d);
+
+ memset(&dfs_fls, 0, sizeof(dfs_fls));
+
+ debugfs_remove(dfs_inj);
+ dfs_inj = NULL;
+}
+module_init(init_mce_inject);
+module_exit(exit_mce_inject);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Borislav Petkov <bp@alien8.de>");
MODULE_AUTHOR("AMD Inc.");
-MODULE_DESCRIPTION("MCE injection facility for testing MCE decoding");
+MODULE_DESCRIPTION("MCE injection facility for RAS testing");
res = devm_request_irq(&pdev->dev,
pdata->irq,
mv64x60_pci_isr,
- IRQF_DISABLED,
+ 0,
"[EDAC] PCI err",
pci);
if (res < 0) {
res = devm_request_irq(&pdev->dev,
pdata->irq,
mv64x60_sram_isr,
- IRQF_DISABLED,
+ 0,
"[EDAC] SRAM err",
edac_dev);
if (res < 0) {
res = devm_request_irq(&pdev->dev,
pdata->irq,
mv64x60_cpu_isr,
- IRQF_DISABLED,
+ 0,
"[EDAC] CPU err",
edac_dev);
if (res < 0) {
res = devm_request_irq(&pdev->dev,
pdata->irq,
mv64x60_mc_isr,
- IRQF_DISABLED,
+ 0,
"[EDAC] MC err",
mci);
if (res < 0) {
status = request_irq(ded_irq,
ppc4xx_edac_isr,
- IRQF_DISABLED,
+ 0,
"[EDAC] MC ECCDED",
mci);
status = request_irq(sec_irq,
ppc4xx_edac_isr,
- IRQF_DISABLED,
+ 0,
"[EDAC] MC ECCSEC",
mci);
pci_unregister_driver(&x38_driver);
fail0:
- if (mci_pdev)
- pci_dev_put(mci_pdev);
+ pci_dev_put(mci_pdev);
return pci_rc;
}
_IOC_SIZE(cmd) > sizeof(buffer))
return -ENOTTY;
- if (_IOC_DIR(cmd) == _IOC_READ)
- memset(&buffer, 0, _IOC_SIZE(cmd));
+ memset(&buffer, 0, sizeof(buffer));
if (_IOC_DIR(cmd) & _IOC_WRITE)
if (copy_from_user(&buffer, arg, _IOC_SIZE(cmd)))
while ((i < num) && (data - buf + sizeof(struct dmi_header)) <= len) {
const struct dmi_header *dm = (const struct dmi_header *)data;
+ /*
+ * 7.45 End-of-Table (Type 127) [SMBIOS reference spec v3.0.0]
+ */
+ if (dm->type == DMI_ENTRY_END_OF_TABLE)
+ break;
+
/*
* We want to know the total length (formatted area and
* strings) before decoding to make sure we won't run off the
}
}
-static u32 dmi_base;
+static phys_addr_t dmi_base;
static u16 dmi_len;
static u16 dmi_num;
if (memcmp(buf, "_SM_", 4) == 0 &&
buf[5] < 32 && dmi_checksum(buf, buf[5])) {
- smbios_ver = (buf[6] << 8) + buf[7];
+ smbios_ver = get_unaligned_be16(buf + 6);
/* Some BIOS report weird SMBIOS version, fix that up */
switch (smbios_ver) {
buf += 16;
if (memcmp(buf, "_DMI_", 5) == 0 && dmi_checksum(buf, 15)) {
- dmi_num = (buf[13] << 8) | buf[12];
- dmi_len = (buf[7] << 8) | buf[6];
- dmi_base = (buf[11] << 24) | (buf[10] << 16) |
- (buf[9] << 8) | buf[8];
+ dmi_num = get_unaligned_le16(buf + 12);
+ dmi_len = get_unaligned_le16(buf + 6);
+ dmi_base = get_unaligned_le32(buf + 8);
if (dmi_walk_early(dmi_decode) == 0) {
if (smbios_ver) {
return 1;
}
+/*
+ * Check for the SMBIOS 3.0 64-bit entry point signature. Unlike the legacy
+ * 32-bit entry point, there is no embedded DMI header (_DMI_) in here.
+ */
+static int __init dmi_smbios3_present(const u8 *buf)
+{
+ if (memcmp(buf, "_SM3_", 5) == 0 &&
+ buf[6] < 32 && dmi_checksum(buf, buf[6])) {
+ dmi_ver = get_unaligned_be16(buf + 7);
+ dmi_len = get_unaligned_le32(buf + 12);
+ dmi_base = get_unaligned_le64(buf + 16);
+
+ /*
+ * The 64-bit SMBIOS 3.0 entry point no longer has a field
+ * containing the number of structures present in the table.
+ * Instead, it defines the table size as a maximum size, and
+ * relies on the end-of-table structure type (#127) to be used
+ * to signal the end of the table.
+ * So let's define dmi_num as an upper bound as well: each
+ * structure has a 4 byte header, so dmi_len / 4 is an upper
+ * bound for the number of structures in the table.
+ */
+ dmi_num = dmi_len / 4;
+
+ if (dmi_walk_early(dmi_decode) == 0) {
+ pr_info("SMBIOS %d.%d present.\n",
+ dmi_ver >> 8, dmi_ver & 0xFF);
+ dmi_format_ids(dmi_ids_string, sizeof(dmi_ids_string));
+ pr_debug("DMI: %s\n", dmi_ids_string);
+ return 0;
+ }
+ }
+ return 1;
+}
+
void __init dmi_scan_machine(void)
{
char __iomem *p, *q;
char buf[32];
if (efi_enabled(EFI_CONFIG_TABLES)) {
+ /*
+ * According to the DMTF SMBIOS reference spec v3.0.0, it is
+ * allowed to define both the 64-bit entry point (smbios3) and
+ * the 32-bit entry point (smbios), in which case they should
+ * either both point to the same SMBIOS structure table, or the
+ * table pointed to by the 64-bit entry point should contain a
+ * superset of the table contents pointed to by the 32-bit entry
+ * point (section 5.2)
+ * This implies that the 64-bit entry point should have
+ * precedence if it is defined and supported by the OS. If we
+ * have the 64-bit entry point, but fail to decode it, fall
+ * back to the legacy one (if available)
+ */
+ if (efi.smbios3 != EFI_INVALID_TABLE_ADDR) {
+ p = dmi_early_remap(efi.smbios3, 32);
+ if (p == NULL)
+ goto error;
+ memcpy_fromio(buf, p, 32);
+ dmi_early_unmap(p, 32);
+
+ if (!dmi_smbios3_present(buf)) {
+ dmi_available = 1;
+ goto out;
+ }
+ }
if (efi.smbios == EFI_INVALID_TABLE_ADDR)
goto error;
memset(buf, 0, 16);
for (q = p; q < p + 0x10000; q += 16) {
memcpy_fromio(buf + 16, q, 16);
- if (!dmi_present(buf)) {
+ if (!dmi_smbios3_present(buf) || !dmi_present(buf)) {
dmi_available = 1;
dmi_early_unmap(p, 0x10000);
goto out;
.acpi = EFI_INVALID_TABLE_ADDR,
.acpi20 = EFI_INVALID_TABLE_ADDR,
.smbios = EFI_INVALID_TABLE_ADDR,
+ .smbios3 = EFI_INVALID_TABLE_ADDR,
.sal_systab = EFI_INVALID_TABLE_ADDR,
.boot_info = EFI_INVALID_TABLE_ADDR,
.hcdp = EFI_INVALID_TABLE_ADDR,
str += sprintf(str, "ACPI=0x%lx\n", efi.acpi);
if (efi.smbios != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios);
+ if (efi.smbios3 != EFI_INVALID_TABLE_ADDR)
+ str += sprintf(str, "SMBIOS3=0x%lx\n", efi.smbios3);
if (efi.hcdp != EFI_INVALID_TABLE_ADDR)
str += sprintf(str, "HCDP=0x%lx\n", efi.hcdp);
if (efi.boot_info != EFI_INVALID_TABLE_ADDR)
{MPS_TABLE_GUID, "MPS", &efi.mps},
{SAL_SYSTEM_TABLE_GUID, "SALsystab", &efi.sal_systab},
{SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios},
+ {SMBIOS3_TABLE_GUID, "SMBIOS 3.0", &efi.smbios3},
{UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga},
{NULL_GUID, NULL, NULL},
};
goto fail_free_cmdline;
}
}
- if (!fdt_addr)
+
+ if (fdt_addr) {
+ pr_efi(sys_table, "Using DTB from command line\n");
+ } else {
/* Look for a device tree configuration table entry. */
fdt_addr = (uintptr_t)get_fdt(sys_table);
+ if (fdt_addr)
+ pr_efi(sys_table, "Using DTB from configuration table\n");
+ }
+
+ if (!fdt_addr)
+ pr_efi(sys_table, "Generating empty DTB\n");
status = handle_cmdline_files(sys_table, image, cmdline_ptr,
"initrd=", dram_base + SZ_512M,
River Tech's viperboard.h for detailed meaning
of the module parameters.
+config GPIO_DLN2
+ tristate "Diolan DLN2 GPIO support"
+ depends on MFD_DLN2
+ select GPIOLIB_IRQCHIP
+
+ help
+ Select this option to enable GPIO driver for the Diolan DLN2
+ board.
+
+ This driver can also be built as a module. If so, the module
+ will be called gpio-dln2.
+
endif
obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o
obj-$(CONFIG_GPIO_DA9055) += gpio-da9055.o
obj-$(CONFIG_GPIO_DAVINCI) += gpio-davinci.o
+obj-$(CONFIG_GPIO_DLN2) += gpio-dln2.o
obj-$(CONFIG_GPIO_DWAPB) += gpio-dwapb.o
obj-$(CONFIG_GPIO_EM) += gpio-em.o
obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o
--- /dev/null
+/*
+ * Driver for the Diolan DLN-2 USB-GPIO adapter
+ *
+ * Copyright (c) 2014 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/irqdomain.h>
+#include <linux/irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/gpio.h>
+#include <linux/gpio/driver.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/dln2.h>
+
+#define DLN2_GPIO_ID 0x01
+
+#define DLN2_GPIO_GET_PIN_COUNT DLN2_CMD(0x01, DLN2_GPIO_ID)
+#define DLN2_GPIO_SET_DEBOUNCE DLN2_CMD(0x04, DLN2_GPIO_ID)
+#define DLN2_GPIO_GET_DEBOUNCE DLN2_CMD(0x05, DLN2_GPIO_ID)
+#define DLN2_GPIO_PORT_GET_VAL DLN2_CMD(0x06, DLN2_GPIO_ID)
+#define DLN2_GPIO_PIN_GET_VAL DLN2_CMD(0x0B, DLN2_GPIO_ID)
+#define DLN2_GPIO_PIN_SET_OUT_VAL DLN2_CMD(0x0C, DLN2_GPIO_ID)
+#define DLN2_GPIO_PIN_GET_OUT_VAL DLN2_CMD(0x0D, DLN2_GPIO_ID)
+#define DLN2_GPIO_CONDITION_MET_EV DLN2_CMD(0x0F, DLN2_GPIO_ID)
+#define DLN2_GPIO_PIN_ENABLE DLN2_CMD(0x10, DLN2_GPIO_ID)
+#define DLN2_GPIO_PIN_DISABLE DLN2_CMD(0x11, DLN2_GPIO_ID)
+#define DLN2_GPIO_PIN_SET_DIRECTION DLN2_CMD(0x13, DLN2_GPIO_ID)
+#define DLN2_GPIO_PIN_GET_DIRECTION DLN2_CMD(0x14, DLN2_GPIO_ID)
+#define DLN2_GPIO_PIN_SET_EVENT_CFG DLN2_CMD(0x1E, DLN2_GPIO_ID)
+#define DLN2_GPIO_PIN_GET_EVENT_CFG DLN2_CMD(0x1F, DLN2_GPIO_ID)
+
+#define DLN2_GPIO_EVENT_NONE 0
+#define DLN2_GPIO_EVENT_CHANGE 1
+#define DLN2_GPIO_EVENT_LVL_HIGH 2
+#define DLN2_GPIO_EVENT_LVL_LOW 3
+#define DLN2_GPIO_EVENT_CHANGE_RISING 0x11
+#define DLN2_GPIO_EVENT_CHANGE_FALLING 0x21
+#define DLN2_GPIO_EVENT_MASK 0x0F
+
+#define DLN2_GPIO_MAX_PINS 32
+
+struct dln2_irq_work {
+ struct work_struct work;
+ struct dln2_gpio *dln2;
+ int pin;
+ int type;
+};
+
+struct dln2_gpio {
+ struct platform_device *pdev;
+ struct gpio_chip gpio;
+
+ /*
+ * Cache pin direction to save us one transfer, since the hardware has
+ * separate commands to read the in and out values.
+ */
+ DECLARE_BITMAP(output_enabled, DLN2_GPIO_MAX_PINS);
+
+ DECLARE_BITMAP(irqs_masked, DLN2_GPIO_MAX_PINS);
+ DECLARE_BITMAP(irqs_enabled, DLN2_GPIO_MAX_PINS);
+ DECLARE_BITMAP(irqs_pending, DLN2_GPIO_MAX_PINS);
+ struct dln2_irq_work *irq_work;
+};
+
+struct dln2_gpio_pin {
+ __le16 pin;
+};
+
+struct dln2_gpio_pin_val {
+ __le16 pin __packed;
+ u8 value;
+};
+
+static int dln2_gpio_get_pin_count(struct platform_device *pdev)
+{
+ int ret;
+ __le16 count;
+ int len = sizeof(count);
+
+ ret = dln2_transfer_rx(pdev, DLN2_GPIO_GET_PIN_COUNT, &count, &len);
+ if (ret < 0)
+ return ret;
+ if (len < sizeof(count))
+ return -EPROTO;
+
+ return le16_to_cpu(count);
+}
+
+static int dln2_gpio_pin_cmd(struct dln2_gpio *dln2, int cmd, unsigned pin)
+{
+ struct dln2_gpio_pin req = {
+ .pin = cpu_to_le16(pin),
+ };
+
+ return dln2_transfer_tx(dln2->pdev, cmd, &req, sizeof(req));
+}
+
+static int dln2_gpio_pin_val(struct dln2_gpio *dln2, int cmd, unsigned int pin)
+{
+ int ret;
+ struct dln2_gpio_pin req = {
+ .pin = cpu_to_le16(pin),
+ };
+ struct dln2_gpio_pin_val rsp;
+ int len = sizeof(rsp);
+
+ ret = dln2_transfer(dln2->pdev, cmd, &req, sizeof(req), &rsp, &len);
+ if (ret < 0)
+ return ret;
+ if (len < sizeof(rsp) || req.pin != rsp.pin)
+ return -EPROTO;
+
+ return rsp.value;
+}
+
+static int dln2_gpio_pin_get_in_val(struct dln2_gpio *dln2, unsigned int pin)
+{
+ int ret;
+
+ ret = dln2_gpio_pin_val(dln2, DLN2_GPIO_PIN_GET_VAL, pin);
+ if (ret < 0)
+ return ret;
+ return !!ret;
+}
+
+static int dln2_gpio_pin_get_out_val(struct dln2_gpio *dln2, unsigned int pin)
+{
+ int ret;
+
+ ret = dln2_gpio_pin_val(dln2, DLN2_GPIO_PIN_GET_OUT_VAL, pin);
+ if (ret < 0)
+ return ret;
+ return !!ret;
+}
+
+static void dln2_gpio_pin_set_out_val(struct dln2_gpio *dln2,
+ unsigned int pin, int value)
+{
+ struct dln2_gpio_pin_val req = {
+ .pin = cpu_to_le16(pin),
+ .value = value,
+ };
+
+ dln2_transfer_tx(dln2->pdev, DLN2_GPIO_PIN_SET_OUT_VAL, &req,
+ sizeof(req));
+}
+
+#define DLN2_GPIO_DIRECTION_IN 0
+#define DLN2_GPIO_DIRECTION_OUT 1
+
+static int dln2_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio);
+ struct dln2_gpio_pin req = {
+ .pin = cpu_to_le16(offset),
+ };
+ struct dln2_gpio_pin_val rsp;
+ int len = sizeof(rsp);
+ int ret;
+
+ ret = dln2_gpio_pin_cmd(dln2, DLN2_GPIO_PIN_ENABLE, offset);
+ if (ret < 0)
+ return ret;
+
+ /* cache the pin direction */
+ ret = dln2_transfer(dln2->pdev, DLN2_GPIO_PIN_GET_DIRECTION,
+ &req, sizeof(req), &rsp, &len);
+ if (ret < 0)
+ return ret;
+ if (len < sizeof(rsp) || req.pin != rsp.pin) {
+ ret = -EPROTO;
+ goto out_disable;
+ }
+
+ switch (rsp.value) {
+ case DLN2_GPIO_DIRECTION_IN:
+ clear_bit(offset, dln2->output_enabled);
+ return 0;
+ case DLN2_GPIO_DIRECTION_OUT:
+ set_bit(offset, dln2->output_enabled);
+ return 0;
+ default:
+ ret = -EPROTO;
+ goto out_disable;
+ }
+
+out_disable:
+ dln2_gpio_pin_cmd(dln2, DLN2_GPIO_PIN_DISABLE, offset);
+ return ret;
+}
+
+static void dln2_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio);
+
+ dln2_gpio_pin_cmd(dln2, DLN2_GPIO_PIN_DISABLE, offset);
+}
+
+static int dln2_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
+{
+ struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio);
+
+ if (test_bit(offset, dln2->output_enabled))
+ return GPIOF_DIR_OUT;
+
+ return GPIOF_DIR_IN;
+}
+
+static int dln2_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio);
+ int dir;
+
+ dir = dln2_gpio_get_direction(chip, offset);
+ if (dir < 0)
+ return dir;
+
+ if (dir == GPIOF_DIR_IN)
+ return dln2_gpio_pin_get_in_val(dln2, offset);
+
+ return dln2_gpio_pin_get_out_val(dln2, offset);
+}
+
+static void dln2_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio);
+
+ dln2_gpio_pin_set_out_val(dln2, offset, value);
+}
+
+static int dln2_gpio_set_direction(struct gpio_chip *chip, unsigned offset,
+ unsigned dir)
+{
+ struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio);
+ struct dln2_gpio_pin_val req = {
+ .pin = cpu_to_le16(offset),
+ .value = dir,
+ };
+ int ret;
+
+ ret = dln2_transfer_tx(dln2->pdev, DLN2_GPIO_PIN_SET_DIRECTION,
+ &req, sizeof(req));
+ if (ret < 0)
+ return ret;
+
+ if (dir == DLN2_GPIO_DIRECTION_OUT)
+ set_bit(offset, dln2->output_enabled);
+ else
+ clear_bit(offset, dln2->output_enabled);
+
+ return ret;
+}
+
+static int dln2_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ return dln2_gpio_set_direction(chip, offset, DLN2_GPIO_DIRECTION_IN);
+}
+
+static int dln2_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ return dln2_gpio_set_direction(chip, offset, DLN2_GPIO_DIRECTION_OUT);
+}
+
+static int dln2_gpio_set_debounce(struct gpio_chip *chip, unsigned offset,
+ unsigned debounce)
+{
+ struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio);
+ __le32 duration = cpu_to_le32(debounce);
+
+ return dln2_transfer_tx(dln2->pdev, DLN2_GPIO_SET_DEBOUNCE,
+ &duration, sizeof(duration));
+}
+
+static int dln2_gpio_set_event_cfg(struct dln2_gpio *dln2, unsigned pin,
+ unsigned type, unsigned period)
+{
+ struct {
+ __le16 pin;
+ u8 type;
+ __le16 period;
+ } __packed req = {
+ .pin = cpu_to_le16(pin),
+ .type = type,
+ .period = cpu_to_le16(period),
+ };
+
+ return dln2_transfer_tx(dln2->pdev, DLN2_GPIO_PIN_SET_EVENT_CFG,
+ &req, sizeof(req));
+}
+
+static void dln2_irq_work(struct work_struct *w)
+{
+ struct dln2_irq_work *iw = container_of(w, struct dln2_irq_work, work);
+ struct dln2_gpio *dln2 = iw->dln2;
+ u8 type = iw->type & DLN2_GPIO_EVENT_MASK;
+
+ if (test_bit(iw->pin, dln2->irqs_enabled))
+ dln2_gpio_set_event_cfg(dln2, iw->pin, type, 0);
+ else
+ dln2_gpio_set_event_cfg(dln2, iw->pin, DLN2_GPIO_EVENT_NONE, 0);
+}
+
+static void dln2_irq_enable(struct irq_data *irqd)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
+ struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio);
+ int pin = irqd_to_hwirq(irqd);
+
+ set_bit(pin, dln2->irqs_enabled);
+ schedule_work(&dln2->irq_work[pin].work);
+}
+
+static void dln2_irq_disable(struct irq_data *irqd)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
+ struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio);
+ int pin = irqd_to_hwirq(irqd);
+
+ clear_bit(pin, dln2->irqs_enabled);
+ schedule_work(&dln2->irq_work[pin].work);
+}
+
+static void dln2_irq_mask(struct irq_data *irqd)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
+ struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio);
+ int pin = irqd_to_hwirq(irqd);
+
+ set_bit(pin, dln2->irqs_masked);
+}
+
+static void dln2_irq_unmask(struct irq_data *irqd)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
+ struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio);
+ struct device *dev = dln2->gpio.dev;
+ int pin = irqd_to_hwirq(irqd);
+
+ if (test_and_clear_bit(pin, dln2->irqs_pending)) {
+ int irq;
+
+ irq = irq_find_mapping(dln2->gpio.irqdomain, pin);
+ if (!irq) {
+ dev_err(dev, "pin %d not mapped to IRQ\n", pin);
+ return;
+ }
+
+ generic_handle_irq(irq);
+ }
+}
+
+static int dln2_irq_set_type(struct irq_data *irqd, unsigned type)
+{
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd);
+ struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio);
+ int pin = irqd_to_hwirq(irqd);
+
+ switch (type) {
+ case IRQ_TYPE_LEVEL_HIGH:
+ dln2->irq_work[pin].type = DLN2_GPIO_EVENT_LVL_HIGH;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ dln2->irq_work[pin].type = DLN2_GPIO_EVENT_LVL_LOW;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ dln2->irq_work[pin].type = DLN2_GPIO_EVENT_CHANGE;
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ dln2->irq_work[pin].type = DLN2_GPIO_EVENT_CHANGE_RISING;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ dln2->irq_work[pin].type = DLN2_GPIO_EVENT_CHANGE_FALLING;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct irq_chip dln2_gpio_irqchip = {
+ .name = "dln2-irq",
+ .irq_enable = dln2_irq_enable,
+ .irq_disable = dln2_irq_disable,
+ .irq_mask = dln2_irq_mask,
+ .irq_unmask = dln2_irq_unmask,
+ .irq_set_type = dln2_irq_set_type,
+};
+
+static void dln2_gpio_event(struct platform_device *pdev, u16 echo,
+ const void *data, int len)
+{
+ int pin, irq;
+ const struct {
+ __le16 count;
+ __u8 type;
+ __le16 pin;
+ __u8 value;
+ } __packed *event = data;
+ struct dln2_gpio *dln2 = platform_get_drvdata(pdev);
+
+ if (len < sizeof(*event)) {
+ dev_err(dln2->gpio.dev, "short event message\n");
+ return;
+ }
+
+ pin = le16_to_cpu(event->pin);
+ if (pin >= dln2->gpio.ngpio) {
+ dev_err(dln2->gpio.dev, "out of bounds pin %d\n", pin);
+ return;
+ }
+
+ irq = irq_find_mapping(dln2->gpio.irqdomain, pin);
+ if (!irq) {
+ dev_err(dln2->gpio.dev, "pin %d not mapped to IRQ\n", pin);
+ return;
+ }
+
+ if (!test_bit(pin, dln2->irqs_enabled))
+ return;
+ if (test_bit(pin, dln2->irqs_masked)) {
+ set_bit(pin, dln2->irqs_pending);
+ return;
+ }
+
+ switch (dln2->irq_work[pin].type) {
+ case DLN2_GPIO_EVENT_CHANGE_RISING:
+ if (event->value)
+ generic_handle_irq(irq);
+ break;
+ case DLN2_GPIO_EVENT_CHANGE_FALLING:
+ if (!event->value)
+ generic_handle_irq(irq);
+ break;
+ default:
+ generic_handle_irq(irq);
+ }
+}
+
+static int dln2_gpio_probe(struct platform_device *pdev)
+{
+ struct dln2_gpio *dln2;
+ struct device *dev = &pdev->dev;
+ int pins;
+ int i, ret;
+
+ pins = dln2_gpio_get_pin_count(pdev);
+ if (pins < 0) {
+ dev_err(dev, "failed to get pin count: %d\n", pins);
+ return pins;
+ }
+ if (pins > DLN2_GPIO_MAX_PINS) {
+ pins = DLN2_GPIO_MAX_PINS;
+ dev_warn(dev, "clamping pins to %d\n", DLN2_GPIO_MAX_PINS);
+ }
+
+ dln2 = devm_kzalloc(&pdev->dev, sizeof(*dln2), GFP_KERNEL);
+ if (!dln2)
+ return -ENOMEM;
+
+ dln2->irq_work = devm_kcalloc(&pdev->dev, pins,
+ sizeof(struct dln2_irq_work), GFP_KERNEL);
+ if (!dln2->irq_work)
+ return -ENOMEM;
+ for (i = 0; i < pins; i++) {
+ INIT_WORK(&dln2->irq_work[i].work, dln2_irq_work);
+ dln2->irq_work[i].pin = i;
+ dln2->irq_work[i].dln2 = dln2;
+ }
+
+ dln2->pdev = pdev;
+
+ dln2->gpio.label = "dln2";
+ dln2->gpio.dev = dev;
+ dln2->gpio.owner = THIS_MODULE;
+ dln2->gpio.base = -1;
+ dln2->gpio.ngpio = pins;
+ dln2->gpio.exported = true;
+ dln2->gpio.can_sleep = true;
+ dln2->gpio.irq_not_threaded = true;
+ dln2->gpio.set = dln2_gpio_set;
+ dln2->gpio.get = dln2_gpio_get;
+ dln2->gpio.request = dln2_gpio_request;
+ dln2->gpio.free = dln2_gpio_free;
+ dln2->gpio.get_direction = dln2_gpio_get_direction;
+ dln2->gpio.direction_input = dln2_gpio_direction_input;
+ dln2->gpio.direction_output = dln2_gpio_direction_output;
+ dln2->gpio.set_debounce = dln2_gpio_set_debounce;
+
+ platform_set_drvdata(pdev, dln2);
+
+ ret = gpiochip_add(&dln2->gpio);
+ if (ret < 0) {
+ dev_err(dev, "failed to add gpio chip: %d\n", ret);
+ goto out;
+ }
+
+ ret = gpiochip_irqchip_add(&dln2->gpio, &dln2_gpio_irqchip, 0,
+ handle_simple_irq, IRQ_TYPE_NONE);
+ if (ret < 0) {
+ dev_err(dev, "failed to add irq chip: %d\n", ret);
+ goto out_gpiochip_remove;
+ }
+
+ ret = dln2_register_event_cb(pdev, DLN2_GPIO_CONDITION_MET_EV,
+ dln2_gpio_event);
+ if (ret) {
+ dev_err(dev, "failed to register event cb: %d\n", ret);
+ goto out_gpiochip_remove;
+ }
+
+ return 0;
+
+out_gpiochip_remove:
+ gpiochip_remove(&dln2->gpio);
+out:
+ return ret;
+}
+
+static int dln2_gpio_remove(struct platform_device *pdev)
+{
+ struct dln2_gpio *dln2 = platform_get_drvdata(pdev);
+ int i;
+
+ dln2_unregister_event_cb(pdev, DLN2_GPIO_CONDITION_MET_EV);
+ for (i = 0; i < dln2->gpio.ngpio; i++)
+ flush_work(&dln2->irq_work[i].work);
+ gpiochip_remove(&dln2->gpio);
+
+ return 0;
+}
+
+static struct platform_driver dln2_gpio_driver = {
+ .driver.name = "dln2-gpio",
+ .probe = dln2_gpio_probe,
+ .remove = dln2_gpio_remove,
+};
+
+module_platform_driver(dln2_gpio_driver);
+
+MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com");
+MODULE_DESCRIPTION("Driver for the Diolan DLN2 GPIO interface");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:dln2-gpio");
tc3589x_gpio->chip = template_chip;
tc3589x_gpio->chip.ngpio = tc3589x->num_gpio;
tc3589x_gpio->chip.dev = &pdev->dev;
- tc3589x_gpio->chip.base = (pdata) ? pdata->gpio_base : -1;
+ tc3589x_gpio->chip.base = -1;
#ifdef CONFIG_OF_GPIO
tc3589x_gpio->chip.of_node = np;
mutex_lock(&drm_component_lock);
+ /* Do not retry to probe if there is no any kms driver regitered. */
+ if (list_empty(&drm_component_list)) {
+ mutex_unlock(&drm_component_lock);
+ return ERR_PTR(-ENODEV);
+ }
+
list_for_each_entry(cdev, &drm_component_list, list) {
/*
* Add components to master only in case that crtc and
goto err_unregister_mixer_drv;
#endif
+ match = exynos_drm_match_add(&pdev->dev);
+ if (IS_ERR(match)) {
+ ret = PTR_ERR(match);
+ goto err_unregister_hdmi_drv;
+ }
+
+ ret = component_master_add_with_match(&pdev->dev, &exynos_drm_ops,
+ match);
+ if (ret < 0)
+ goto err_unregister_hdmi_drv;
+
#ifdef CONFIG_DRM_EXYNOS_G2D
ret = platform_driver_register(&g2d_driver);
if (ret < 0)
- goto err_unregister_hdmi_drv;
+ goto err_del_component_master;
#endif
#ifdef CONFIG_DRM_EXYNOS_FIMC
goto err_unregister_ipp_drv;
#endif
- match = exynos_drm_match_add(&pdev->dev);
- if (IS_ERR(match)) {
- ret = PTR_ERR(match);
- goto err_unregister_resources;
- }
-
- ret = component_master_add_with_match(&pdev->dev, &exynos_drm_ops,
- match);
- if (ret < 0)
- goto err_unregister_resources;
-
return ret;
-err_unregister_resources:
-
#ifdef CONFIG_DRM_EXYNOS_IPP
- exynos_platform_device_ipp_unregister();
err_unregister_ipp_drv:
platform_driver_unregister(&ipp_driver);
err_unregister_gsc_drv:
#ifdef CONFIG_DRM_EXYNOS_G2D
platform_driver_unregister(&g2d_driver);
-err_unregister_hdmi_drv:
+err_del_component_master:
#endif
+ component_master_del(&pdev->dev, &exynos_drm_ops);
+err_unregister_hdmi_drv:
#ifdef CONFIG_DRM_EXYNOS_HDMI
platform_driver_unregister(&hdmi_driver);
err_unregister_mixer_drv:
{
int ret;
+ /*
+ * Register device object only in case of Exynos SoC.
+ *
+ * Below codes resolves temporarily infinite loop issue incurred
+ * by Exynos drm driver when using multi-platform kernel.
+ * So these codes will be replaced with more generic way later.
+ */
+ if (!of_machine_is_compatible("samsung,exynos3") &&
+ !of_machine_is_compatible("samsung,exynos4") &&
+ !of_machine_is_compatible("samsung,exynos5"))
+ return -ENODEV;
+
exynos_drm_pdev = platform_device_register_simple("exynos-drm", -1,
NULL, 0);
if (IS_ERR(exynos_drm_pdev))
struct exynos_drm_subdrv *subdrv = &g2d->subdrv;
kfree(g2d->cmdlist_node);
- dma_free_attrs(subdrv->drm_dev->dev, G2D_CMDLIST_POOL_SIZE,
- g2d->cmdlist_pool_virt,
- g2d->cmdlist_pool, &g2d->cmdlist_dma_attrs);
+
+ if (g2d->cmdlist_pool_virt && g2d->cmdlist_pool) {
+ dma_free_attrs(subdrv->drm_dev->dev, G2D_CMDLIST_POOL_SIZE,
+ g2d->cmdlist_pool_virt,
+ g2d->cmdlist_pool, &g2d->cmdlist_dma_attrs);
+ }
}
static struct g2d_cmdlist_node *g2d_get_cmdlist(struct g2d_data *g2d)
goto out_regs;
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
- ret = i915_kick_out_vgacon(dev_priv);
+ /* WARNING: Apparently we must kick fbdev drivers before vgacon,
+ * otherwise the vga fbdev driver falls over. */
+ ret = i915_kick_out_firmware_fb(dev_priv);
if (ret) {
- DRM_ERROR("failed to remove conflicting VGA console\n");
+ DRM_ERROR("failed to remove conflicting framebuffer drivers\n");
goto out_gtt;
}
- ret = i915_kick_out_firmware_fb(dev_priv);
+ ret = i915_kick_out_vgacon(dev_priv);
if (ret) {
- DRM_ERROR("failed to remove conflicting framebuffer drivers\n");
+ DRM_ERROR("failed to remove conflicting VGA console\n");
goto out_gtt;
}
}
return i915_drm_freeze(drm_dev);
}
+static int i915_pm_freeze_late(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct drm_device *drm_dev = pci_get_drvdata(pdev);
+ struct drm_i915_private *dev_priv = drm_dev->dev_private;
+
+ return intel_suspend_complete(dev_priv);
+}
+
static int i915_pm_thaw_early(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
.resume_early = i915_pm_resume_early,
.resume = i915_pm_resume,
.freeze = i915_pm_freeze,
+ .freeze_late = i915_pm_freeze_late,
.thaw_early = i915_pm_thaw_early,
.thaw = i915_pm_thaw,
.poweroff = i915_pm_poweroff,
GEN8_PPAT(6, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(2)) |
GEN8_PPAT(7, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(3));
+ if (!USES_PPGTT(dev_priv->dev))
+ /* Spec: "For GGTT, there is NO pat_sel[2:0] from the entry,
+ * so RTL will always use the value corresponding to
+ * pat_sel = 000".
+ * So let's disable cache for GGTT to avoid screen corruptions.
+ * MOCS still can be used though.
+ * - System agent ggtt writes (i.e. cpu gtt mmaps) already work
+ * before this patch, i.e. the same uncached + snooping access
+ * like on gen6/7 seems to be in effect.
+ * - So this just fixes blitter/render access. Again it looks
+ * like it's not just uncached access, but uncached + snooping.
+ * So we can still hold onto all our assumptions wrt cpu
+ * clflushing on LLC machines.
+ */
+ pat = GEN8_PPAT(0, GEN8_PPAT_UC);
+
/* XXX: spec defines this as 2 distinct registers. It's unclear if a 64b
* write would work. */
I915_WRITE(GEN8_PRIVATE_PAT, pat);
* has to also include the unfenced register the GPU uses
* whilst executing a fenced command for an untiled object.
*/
-
- obj->map_and_fenceable =
- !i915_gem_obj_ggtt_bound(obj) ||
- (i915_gem_obj_ggtt_offset(obj) +
- obj->base.size <= dev_priv->gtt.mappable_end &&
- i915_gem_object_fence_ok(obj, args->tiling_mode));
-
- /* Rebind if we need a change of alignment */
- if (!obj->map_and_fenceable) {
- u32 unfenced_align =
- i915_gem_get_gtt_alignment(dev, obj->base.size,
- args->tiling_mode,
- false);
- if (i915_gem_obj_ggtt_offset(obj) & (unfenced_align - 1))
- ret = i915_gem_object_ggtt_unbind(obj);
- }
+ if (obj->map_and_fenceable &&
+ !i915_gem_object_fence_ok(obj, args->tiling_mode))
+ ret = i915_gem_object_ggtt_unbind(obj);
if (ret == 0) {
obj->fence_dirty =
ironlake_fdi_disable(crtc);
ironlake_disable_pch_transcoder(dev_priv, pipe);
- intel_set_pch_fifo_underrun_reporting(dev, pipe, true);
if (HAS_PCH_CPT(dev)) {
/* disable TRANS_DP_CTL */
if (intel_crtc->config.has_pch_encoder) {
lpt_disable_pch_transcoder(dev_priv);
- intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, true);
intel_ddi_fdi_disable(crtc);
}
struct drm_device *dev = crtc->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
+ if (i915_reset_in_progress(&dev_priv->gpu_error) ||
+ crtc->reset_counter != atomic_read(&dev_priv->gpu_error.reset_counter))
+ return true;
+
/*
* The relevant registers doen't exist on pre-ctg.
* As the flip done interrupt doesn't trigger for mmio
* vdd might still be enabled do to the delayed vdd off.
* Make sure vdd is actually turned off here.
*/
+ cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
pps_lock(intel_dp);
edp_panel_vdd_off_sync(intel_dp);
pps_unlock(intel_dp);
int pipe;
u8 pin;
+ /*
+ * Unlock registers and just leave them unlocked. Do this before
+ * checking quirk lists to avoid bogus WARNINGs.
+ */
+ if (HAS_PCH_SPLIT(dev)) {
+ I915_WRITE(PCH_PP_CONTROL,
+ I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS);
+ } else {
+ I915_WRITE(PP_CONTROL,
+ I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS);
+ }
if (!intel_lvds_supported(dev))
return;
lvds_encoder->a3_power = I915_READ(lvds_encoder->reg) &
LVDS_A3_POWER_MASK;
- /*
- * Unlock registers and just
- * leave them unlocked
- */
- if (HAS_PCH_SPLIT(dev)) {
- I915_WRITE(PCH_PP_CONTROL,
- I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS);
- } else {
- I915_WRITE(PP_CONTROL,
- I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS);
- }
lvds_connector->lid_notifier.notifier_call = intel_lid_notify;
if (acpi_lid_notifier_register(&lvds_connector->lid_notifier)) {
DRM_DEBUG_KMS("lid notifier registration failed\n");
struct drm_device *dev = connector->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_panel *panel = &connector->panel;
+ int min;
WARN_ON(panel->backlight.max == 0);
+ /*
+ * XXX: If the vbt value is 255, it makes min equal to max, which leads
+ * to problems. There are such machines out there. Either our
+ * interpretation is wrong or the vbt has bogus data. Or both. Safeguard
+ * against this by letting the minimum be at most (arbitrarily chosen)
+ * 25% of the max.
+ */
+ min = clamp_t(int, dev_priv->vbt.backlight.min_brightness, 0, 64);
+ if (min != dev_priv->vbt.backlight.min_brightness) {
+ DRM_DEBUG_KMS("clamping VBT min backlight %d/255 to %d/255\n",
+ dev_priv->vbt.backlight.min_brightness, min);
+ }
+
/* vbt value is a coefficient in range [0..255] */
- return scale(dev_priv->vbt.backlight.min_brightness, 0, 255,
- 0, panel->backlight.max);
+ return scale(min, 0, 255, 0, panel->backlight.max);
}
static int bdw_setup_backlight(struct intel_connector *connector)
I915_WRITE(_3D_CHICKEN,
_MASKED_BIT_ENABLE(_3D_CHICKEN_HIZ_PLANE_DISABLE_MSAA_4X_SNB));
- /* WaSetupGtModeTdRowDispatch:snb */
- if (IS_SNB_GT1(dev))
- I915_WRITE(GEN6_GT_MODE,
- _MASKED_BIT_ENABLE(GEN6_TD_FOUR_ROW_DISPATCH_DISABLE));
-
/* WaDisable_RenderCache_OperationalFlush:snb */
I915_WRITE(CACHE_MODE_0, _MASKED_BIT_DISABLE(RC_OP_FLUSH_ENABLE));
device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass;
device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass;
device->oclass[NVDEV_ENGINE_COPY0 ] = &nvc0_copy0_oclass;
- device->oclass[NVDEV_ENGINE_COPY1 ] = &nvc0_copy1_oclass;
device->oclass[NVDEV_ENGINE_DISP ] = nva3_disp_oclass;
device->oclass[NVDEV_ENGINE_PERFMON] = &nvc0_perfmon_oclass;
break;
}
if (status & 0x40000000) {
- nouveau_fifo_uevent(&priv->base);
nv_wr32(priv, 0x002100, 0x40000000);
+ nouveau_fifo_uevent(&priv->base);
status &= ~0x40000000;
}
}
u32 inte = nv_rd32(priv, 0x002628);
u32 unkn;
+ nv_wr32(priv, 0x0025a8 + (engn * 0x04), intr);
+
for (unkn = 0; unkn < 8; unkn++) {
u32 ints = (intr >> (unkn * 0x04)) & inte;
if (ints & 0x1) {
nv_mask(priv, 0x002628, ints, 0);
}
}
-
- nv_wr32(priv, 0x0025a8 + (engn * 0x04), intr);
}
static void
}
if (stat & 0x80000000) {
- nve0_fifo_intr_engine(priv);
nv_wr32(priv, 0x002100, 0x80000000);
+ nve0_fifo_intr_engine(priv);
stat &= ~0x80000000;
}
struct nouveau_fb base;
};
+static int
+gk20a_fb_init(struct nouveau_object *object)
+{
+ struct gk20a_fb_priv *priv = (void *)object;
+ int ret;
+
+ ret = nouveau_fb_init(&priv->base);
+ if (ret)
+ return ret;
+
+ nv_mask(priv, 0x100c80, 0x00000001, 0x00000000); /* 128KiB lpg */
+ return 0;
+}
+
static int
gk20a_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
struct nouveau_oclass *oclass, void *data, u32 size,
.base.ofuncs = &(struct nouveau_ofuncs) {
.ctor = gk20a_fb_ctor,
.dtor = _nouveau_fb_dtor,
- .init = _nouveau_fb_init,
+ .init = gk20a_fb_init,
.fini = _nouveau_fb_fini,
},
.memtype = nvc0_fb_memtype_valid,
pci_save_state(pdev);
pci_disable_device(pdev);
- pci_ignore_hotplug(pdev);
pci_set_power_state(pdev, PCI_D3hot);
return 0;
}
ret = nouveau_do_suspend(drm_dev, true);
pci_save_state(pdev);
pci_disable_device(pdev);
+ pci_ignore_hotplug(pdev);
pci_set_power_state(pdev, PCI_D3cold);
drm_dev->switch_power_state = DRM_SWITCH_POWER_DYNAMIC_OFF;
return ret;
return container_of(fence->base.lock, struct nouveau_fence_chan, lock);
}
-static void
+static int
nouveau_fence_signal(struct nouveau_fence *fence)
{
+ int drop = 0;
+
fence_signal_locked(&fence->base);
list_del(&fence->head);
+ rcu_assign_pointer(fence->channel, NULL);
if (test_bit(FENCE_FLAG_USER_BITS, &fence->base.flags)) {
struct nouveau_fence_chan *fctx = nouveau_fctx(fence);
if (!--fctx->notify_ref)
- nvif_notify_put(&fctx->notify);
+ drop = 1;
}
fence_put(&fence->base);
+ return drop;
}
static struct nouveau_fence *
{
struct nouveau_fence *fence;
- nvif_notify_fini(&fctx->notify);
-
spin_lock_irq(&fctx->lock);
while (!list_empty(&fctx->pending)) {
fence = list_entry(fctx->pending.next, typeof(*fence), head);
- nouveau_fence_signal(fence);
- fence->channel = NULL;
+ if (nouveau_fence_signal(fence))
+ nvif_notify_put(&fctx->notify);
}
spin_unlock_irq(&fctx->lock);
+
+ nvif_notify_fini(&fctx->notify);
+ fctx->dead = 1;
+
+ /*
+ * Ensure that all accesses to fence->channel complete before freeing
+ * the channel.
+ */
+ synchronize_rcu();
}
static void
kref_put(&fctx->fence_ref, nouveau_fence_context_put);
}
-static void
+static int
nouveau_fence_update(struct nouveau_channel *chan, struct nouveau_fence_chan *fctx)
{
struct nouveau_fence *fence;
-
+ int drop = 0;
u32 seq = fctx->read(chan);
while (!list_empty(&fctx->pending)) {
fence = list_entry(fctx->pending.next, typeof(*fence), head);
if ((int)(seq - fence->base.seqno) < 0)
- return;
+ break;
- nouveau_fence_signal(fence);
+ drop |= nouveau_fence_signal(fence);
}
+
+ return drop;
}
static int
struct nouveau_fence_chan *fctx =
container_of(notify, typeof(*fctx), notify);
unsigned long flags;
+ int ret = NVIF_NOTIFY_KEEP;
spin_lock_irqsave(&fctx->lock, flags);
if (!list_empty(&fctx->pending)) {
struct nouveau_fence *fence;
+ struct nouveau_channel *chan;
fence = list_entry(fctx->pending.next, typeof(*fence), head);
- nouveau_fence_update(fence->channel, fctx);
+ chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock));
+ if (nouveau_fence_update(fence->channel, fctx))
+ ret = NVIF_NOTIFY_DROP;
}
spin_unlock_irqrestore(&fctx->lock, flags);
- /* Always return keep here. NVIF refcount is handled with nouveau_fence_update */
- return NVIF_NOTIFY_KEEP;
+ return ret;
}
void
if (!ret) {
fence_get(&fence->base);
spin_lock_irq(&fctx->lock);
- nouveau_fence_update(chan, fctx);
+
+ if (nouveau_fence_update(chan, fctx))
+ nvif_notify_put(&fctx->notify);
+
list_add_tail(&fence->head, &fctx->pending);
spin_unlock_irq(&fctx->lock);
}
if (fence->base.ops == &nouveau_fence_ops_legacy ||
fence->base.ops == &nouveau_fence_ops_uevent) {
struct nouveau_fence_chan *fctx = nouveau_fctx(fence);
+ struct nouveau_channel *chan;
unsigned long flags;
if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->base.flags))
return true;
spin_lock_irqsave(&fctx->lock, flags);
- nouveau_fence_update(fence->channel, fctx);
+ chan = rcu_dereference_protected(fence->channel, lockdep_is_held(&fctx->lock));
+ if (chan && nouveau_fence_update(chan, fctx))
+ nvif_notify_put(&fctx->notify);
spin_unlock_irqrestore(&fctx->lock, flags);
}
return fence_is_signaled(&fence->base);
if (fence && (!exclusive || !fobj || !fobj->shared_count)) {
struct nouveau_channel *prev = NULL;
+ bool must_wait = true;
f = nouveau_local_fence(fence, chan->drm);
- if (f)
- prev = f->channel;
+ if (f) {
+ rcu_read_lock();
+ prev = rcu_dereference(f->channel);
+ if (prev && (prev == chan || fctx->sync(f, prev, chan) == 0))
+ must_wait = false;
+ rcu_read_unlock();
+ }
- if (!prev || (prev != chan && (ret = fctx->sync(f, prev, chan))))
+ if (must_wait)
ret = fence_wait(fence, intr);
return ret;
for (i = 0; i < fobj->shared_count && !ret; ++i) {
struct nouveau_channel *prev = NULL;
+ bool must_wait = true;
fence = rcu_dereference_protected(fobj->shared[i],
reservation_object_held(resv));
f = nouveau_local_fence(fence, chan->drm);
- if (f)
- prev = f->channel;
+ if (f) {
+ rcu_read_lock();
+ prev = rcu_dereference(f->channel);
+ if (prev && (prev == chan || fctx->sync(f, prev, chan) == 0))
+ must_wait = false;
+ rcu_read_unlock();
+ }
- if (!prev || (prev != chan && (ret = fctx->sync(f, prev, chan))))
+ if (must_wait)
ret = fence_wait(fence, intr);
-
- if (ret)
- break;
}
return ret;
struct nouveau_fence *fence = from_fence(f);
struct nouveau_fence_chan *fctx = nouveau_fctx(fence);
- return fence->channel ? fctx->name : "dead channel";
+ return !fctx->dead ? fctx->name : "dead channel";
}
/*
{
struct nouveau_fence *fence = from_fence(f);
struct nouveau_fence_chan *fctx = nouveau_fctx(fence);
- struct nouveau_channel *chan = fence->channel;
+ struct nouveau_channel *chan;
+ bool ret = false;
+
+ rcu_read_lock();
+ chan = rcu_dereference(fence->channel);
+ if (chan)
+ ret = (int)(fctx->read(chan) - fence->base.seqno) >= 0;
+ rcu_read_unlock();
- return (int)(fctx->read(chan) - fence->base.seqno) >= 0;
+ return ret;
}
static bool nouveau_fence_no_signaling(struct fence *f)
bool sysmem;
- struct nouveau_channel *channel;
+ struct nouveau_channel __rcu *channel;
unsigned long timeout;
};
char name[32];
struct nvif_notify notify;
- int notify_ref;
+ int notify_ref, dead;
};
struct nouveau_fence_priv {
return 0;
}
+static int
+nv50_crtc_set_raster_vblank_dmi(struct nouveau_crtc *nv_crtc, u32 usec)
+{
+ struct nv50_mast *mast = nv50_mast(nv_crtc->base.dev);
+ u32 *push;
+
+ push = evo_wait(mast, 8);
+ if (!push)
+ return -ENOMEM;
+
+ evo_mthd(push, 0x0828 + (nv_crtc->index * 0x400), 1);
+ evo_data(push, usec);
+ evo_kick(push, mast);
+ return 0;
+}
+
static int
nv50_crtc_set_color_vibrance(struct nouveau_crtc *nv_crtc, bool update)
{
evo_mthd(push, 0x0804 + (nv_crtc->index * 0x400), 2);
evo_data(push, 0x00800000 | mode->clock);
evo_data(push, (ilace == 2) ? 2 : 0);
- evo_mthd(push, 0x0810 + (nv_crtc->index * 0x400), 8);
+ evo_mthd(push, 0x0810 + (nv_crtc->index * 0x400), 6);
evo_data(push, 0x00000000);
evo_data(push, (vactive << 16) | hactive);
evo_data(push, ( vsynce << 16) | hsynce);
evo_data(push, (vblanke << 16) | hblanke);
evo_data(push, (vblanks << 16) | hblanks);
evo_data(push, (vblan2e << 16) | vblan2s);
- evo_data(push, vblankus);
+ evo_mthd(push, 0x082c + (nv_crtc->index * 0x400), 1);
evo_data(push, 0x00000000);
evo_mthd(push, 0x0900 + (nv_crtc->index * 0x400), 2);
evo_data(push, 0x00000311);
nv_connector = nouveau_crtc_connector_get(nv_crtc);
nv50_crtc_set_dither(nv_crtc, false);
nv50_crtc_set_scale(nv_crtc, false);
+
+ /* G94 only accepts this after setting scale */
+ if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA)
+ nv50_crtc_set_raster_vblank_dmi(nv_crtc, vblankus);
+
nv50_crtc_set_color_vibrance(nv_crtc, false);
nv50_crtc_set_image(nv_crtc, crtc->primary->fb, x, y, false);
return 0;
return ret;
}
-int atom_execute_table(struct atom_context *ctx, int index, uint32_t * params)
+int atom_execute_table_scratch_unlocked(struct atom_context *ctx, int index, uint32_t * params)
{
int r;
return r;
}
+int atom_execute_table(struct atom_context *ctx, int index, uint32_t * params)
+{
+ int r;
+ mutex_lock(&ctx->scratch_mutex);
+ r = atom_execute_table_scratch_unlocked(ctx, index, params);
+ mutex_unlock(&ctx->scratch_mutex);
+ return r;
+}
+
static int atom_iio_len[] = { 1, 2, 3, 3, 3, 3, 4, 4, 4, 3 };
static void atom_index_iio(struct atom_context *ctx, int base)
struct atom_context {
struct card_info *card;
struct mutex mutex;
+ struct mutex scratch_mutex;
void *bios;
uint32_t cmd_table, data_table;
uint16_t *iio;
struct atom_context *atom_parse(struct card_info *, void *);
int atom_execute_table(struct atom_context *, int, uint32_t *);
+int atom_execute_table_scratch_unlocked(struct atom_context *, int, uint32_t *);
int atom_asic_init(struct atom_context *);
void atom_destroy(struct atom_context *);
bool atom_parse_data_header(struct atom_context *ctx, int index, uint16_t *size,
memset(&args, 0, sizeof(args));
mutex_lock(&chan->mutex);
+ mutex_lock(&rdev->mode_info.atom_context->scratch_mutex);
base = (unsigned char *)(rdev->mode_info.atom_context->scratch + 1);
if (ASIC_IS_DCE4(rdev))
args.v2.ucHPD_ID = chan->rec.hpd;
- atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+ atom_execute_table_scratch_unlocked(rdev->mode_info.atom_context, index, (uint32_t *)&args);
*ack = args.v1.ucReplyStatus;
r = recv_bytes;
done:
+ mutex_unlock(&rdev->mode_info.atom_context->scratch_mutex);
mutex_unlock(&chan->mutex);
return r;
memset(&args, 0, sizeof(args));
mutex_lock(&chan->mutex);
+ mutex_lock(&rdev->mode_info.atom_context->scratch_mutex);
base = (unsigned char *)rdev->mode_info.atom_context->scratch;
args.ucSlaveAddr = slave_addr << 1;
args.ucLineNumber = chan->rec.i2c_id;
- atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+ atom_execute_table_scratch_unlocked(rdev->mode_info.atom_context, index, (uint32_t *)&args);
/* error */
if (args.ucStatus != HW_ASSISTED_I2C_STATUS_SUCCESS) {
radeon_atom_copy_swap(buf, base, num, false);
done:
+ mutex_unlock(&rdev->mode_info.atom_context->scratch_mutex);
mutex_unlock(&chan->mutex);
return r;
/* init the CE partitions. CE only used for gfx on CIK */
radeon_ring_write(ring, PACKET3(PACKET3_SET_BASE, 2));
radeon_ring_write(ring, PACKET3_BASE_INDEX(CE_PARTITION_BASE));
- radeon_ring_write(ring, 0xc000);
- radeon_ring_write(ring, 0xc000);
+ radeon_ring_write(ring, 0x8000);
+ radeon_ring_write(ring, 0x8000);
/* setup clear context state */
radeon_ring_write(ring, PACKET3(PACKET3_PREAMBLE_CNTL, 0));
u32 num_heads = 0, lb_size;
int i;
+ if (!rdev->mode_info.mode_config_initialized)
+ return;
+
radeon_update_display_priority(rdev);
for (i = 0; i < rdev->num_crtc; i++) {
{
struct radeon_ib ib;
unsigned i;
+ unsigned index;
int r;
- void __iomem *ptr = (void *)rdev->vram_scratch.ptr;
u32 tmp = 0;
+ u64 gpu_addr;
- if (!ptr) {
- DRM_ERROR("invalid vram scratch pointer\n");
- return -EINVAL;
- }
+ if (ring->idx == R600_RING_TYPE_DMA_INDEX)
+ index = R600_WB_DMA_RING_TEST_OFFSET;
+ else
+ index = CAYMAN_WB_DMA1_RING_TEST_OFFSET;
+
+ gpu_addr = rdev->wb.gpu_addr + index;
tmp = 0xCAFEDEAD;
- writel(tmp, ptr);
+ rdev->wb.wb[index/4] = cpu_to_le32(tmp);
r = radeon_ib_get(rdev, ring->idx, &ib, NULL, 256);
if (r) {
}
ib.ptr[0] = SDMA_PACKET(SDMA_OPCODE_WRITE, SDMA_WRITE_SUB_OPCODE_LINEAR, 0);
- ib.ptr[1] = rdev->vram_scratch.gpu_addr & 0xfffffffc;
- ib.ptr[2] = upper_32_bits(rdev->vram_scratch.gpu_addr);
+ ib.ptr[1] = lower_32_bits(gpu_addr);
+ ib.ptr[2] = upper_32_bits(gpu_addr);
ib.ptr[3] = 1;
ib.ptr[4] = 0xDEADBEEF;
ib.length_dw = 5;
return r;
}
for (i = 0; i < rdev->usec_timeout; i++) {
- tmp = readl(ptr);
+ tmp = le32_to_cpu(rdev->wb.wb[index/4]);
if (tmp == 0xDEADBEEF)
break;
DRM_UDELAY(1);
u32 num_heads = 0, lb_size;
int i;
+ if (!rdev->mode_info.mode_config_initialized)
+ return;
+
radeon_update_display_priority(rdev);
for (i = 0; i < rdev->num_crtc; i++) {
WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 1);
tmp |= EVERGREEN_CRTC_BLANK_DATA_EN;
WREG32(EVERGREEN_CRTC_BLANK_CONTROL + crtc_offsets[i], tmp);
+ WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 0);
}
} else {
tmp = RREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i]);
uint32_t pixel_bytes1 = 0;
uint32_t pixel_bytes2 = 0;
+ if (!rdev->mode_info.mode_config_initialized)
+ return;
+
radeon_update_display_priority(rdev);
if (rdev->mode_info.crtcs[0]->base.enabled) {
{
struct radeon_ib ib;
unsigned i;
+ unsigned index;
int r;
- void __iomem *ptr = (void *)rdev->vram_scratch.ptr;
u32 tmp = 0;
+ u64 gpu_addr;
- if (!ptr) {
- DRM_ERROR("invalid vram scratch pointer\n");
- return -EINVAL;
- }
+ if (ring->idx == R600_RING_TYPE_DMA_INDEX)
+ index = R600_WB_DMA_RING_TEST_OFFSET;
+ else
+ index = CAYMAN_WB_DMA1_RING_TEST_OFFSET;
- tmp = 0xCAFEDEAD;
- writel(tmp, ptr);
+ gpu_addr = rdev->wb.gpu_addr + index;
r = radeon_ib_get(rdev, ring->idx, &ib, NULL, 256);
if (r) {
}
ib.ptr[0] = DMA_PACKET(DMA_PACKET_WRITE, 0, 0, 1);
- ib.ptr[1] = rdev->vram_scratch.gpu_addr & 0xfffffffc;
- ib.ptr[2] = upper_32_bits(rdev->vram_scratch.gpu_addr) & 0xff;
+ ib.ptr[1] = lower_32_bits(gpu_addr);
+ ib.ptr[2] = upper_32_bits(gpu_addr) & 0xff;
ib.ptr[3] = 0xDEADBEEF;
ib.length_dw = 4;
return r;
}
for (i = 0; i < rdev->usec_timeout; i++) {
- tmp = readl(ptr);
+ tmp = le32_to_cpu(rdev->wb.wb[index/4]);
if (tmp == 0xDEADBEEF)
break;
DRM_UDELAY(1);
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(ext_hdr->usPowerTuneTableOffset));
rdev->pm.dpm.dyn_state.cac_tdp_table->maximum_power_delivery_limit =
- ppt->usMaximumPowerDeliveryLimit;
+ le16_to_cpu(ppt->usMaximumPowerDeliveryLimit);
pt = &ppt->power_tune_table;
} else {
ATOM_PPLIB_POWERTUNE_Table *ppt = (ATOM_PPLIB_POWERTUNE_Table *)
}
if (!radeon_connector->edid) {
+ /* don't fetch the edid from the vbios if ddc fails and runpm is
+ * enabled so we report disconnected.
+ */
+ if ((rdev->flags & RADEON_IS_PX) && (radeon_runtime_pm != 0))
+ return;
+
if (rdev->is_atom_bios) {
/* some laptops provide a hardcoded edid in rom for LCDs */
if (((connector->connector_type == DRM_MODE_CONNECTOR_LVDS) ||
static enum drm_connector_status
radeon_lvds_detect(struct drm_connector *connector, bool force)
{
+ struct drm_device *dev = connector->dev;
+ struct radeon_device *rdev = dev->dev_private;
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
struct drm_encoder *encoder = radeon_best_single_encoder(connector);
enum drm_connector_status ret = connector_status_disconnected;
/* check if panel is valid */
if (native_mode->hdisplay >= 320 && native_mode->vdisplay >= 240)
ret = connector_status_connected;
-
+ /* don't fetch the edid from the vbios if ddc fails and runpm is
+ * enabled so we report disconnected.
+ */
+ if ((rdev->flags & RADEON_IS_PX) && (radeon_runtime_pm != 0))
+ ret = connector_status_disconnected;
}
/* check for edid as well */
/* check if panel is valid */
if (native_mode->hdisplay >= 320 && native_mode->vdisplay >= 240)
ret = connector_status_connected;
+ /* don't fetch the edid from the vbios if ddc fails and runpm is
+ * enabled so we report disconnected.
+ */
+ if ((rdev->flags & RADEON_IS_PX) && (radeon_runtime_pm != 0))
+ ret = connector_status_disconnected;
}
/* eDP is always DP */
radeon_dig_connector->dp_sink_type = CONNECTOR_OBJECT_ID_DISPLAYPORT;
static int radeon_cs_sync_rings(struct radeon_cs_parser *p)
{
- int i, r = 0;
+ struct radeon_cs_reloc *reloc;
+ int r;
- for (i = 0; i < p->nrelocs; i++) {
+ list_for_each_entry(reloc, &p->validated, tv.head) {
struct reservation_object *resv;
- if (!p->relocs[i].robj)
- continue;
-
- resv = p->relocs[i].robj->tbo.resv;
+ resv = reloc->robj->tbo.resv;
r = radeon_semaphore_sync_resv(p->rdev, p->ib.semaphore, resv,
- p->relocs[i].tv.shared);
-
+ reloc->tv.shared);
if (r)
- break;
+ return r;
}
- return r;
+ return 0;
}
/* XXX: note that this is called from the legacy UMS CS ioctl as well */
}
mutex_init(&rdev->mode_info.atom_context->mutex);
+ mutex_init(&rdev->mode_info.atom_context->scratch_mutex);
radeon_atom_initialize_bios_scratch_regs(rdev->ddev);
atom_allocate_fb_scratch(rdev->mode_info.atom_context);
return 0;
(rdev->pdev->subsystem_vendor == 0x1734) &&
(rdev->pdev->subsystem_device == 0x1107))
use_bl = false;
+ /* disable native backlight control on older asics */
+ else if (rdev->family < CHIP_R600)
+ use_bl = false;
else
use_bl = true;
}
if (rdev->flags & RADEON_IS_AGP)
return false;
+ /*
+ * Older chips have a HW limitation, they can only generate 40 bits
+ * of address for "64-bit" MSIs which breaks on some platforms, notably
+ * IBM POWER servers, so we limit them
+ */
+ if (rdev->family < CHIP_BONAIRE) {
+ dev_info(rdev->dev, "radeon: MSI limited to 32-bit\n");
+ rdev->pdev->no_64bit_msi = 1;
+ }
+
/* force MSI on */
if (radeon_msi == 1)
return true;
/* Get associated drm_crtc: */
drmcrtc = &rdev->mode_info.crtcs[crtc]->base;
+ if (!drmcrtc)
+ return -EINVAL;
/* Helper routine in DRM core does all the work: */
return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc, max_error,
if (!(rdev->flags & RADEON_IS_PCIE))
bo->flags &= ~(RADEON_GEM_GTT_WC | RADEON_GEM_GTT_UC);
+#ifdef CONFIG_X86_32
+ /* XXX: Write-combined CPU mappings of GTT seem broken on 32-bit
+ * See https://bugs.freedesktop.org/show_bug.cgi?id=84627
+ */
+ bo->flags &= ~RADEON_GEM_GTT_WC;
+#endif
+
radeon_ttm_placement_from_domain(bo, domain);
/* Kernel allocation are uninterruptible */
down_read(&rdev->pm.mclk_lock);
u32 d1mode_priority_a_cnt, d2mode_priority_a_cnt;
/* FIXME: implement full support */
+ if (!rdev->mode_info.mode_config_initialized)
+ return;
+
radeon_update_display_priority(rdev);
if (rdev->mode_info.crtcs[0]->base.enabled)
u32 d1mode_priority_a_cnt, d1mode_priority_b_cnt;
u32 d2mode_priority_a_cnt, d2mode_priority_b_cnt;
+ if (!rdev->mode_info.mode_config_initialized)
+ return;
+
radeon_update_display_priority(rdev);
if (rdev->mode_info.crtcs[0]->base.enabled)
struct drm_display_mode *mode0 = NULL;
struct drm_display_mode *mode1 = NULL;
+ if (!rdev->mode_info.mode_config_initialized)
+ return;
+
radeon_update_display_priority(rdev);
if (rdev->mode_info.crtcs[0]->base.enabled)
u32 num_heads = 0, lb_size;
int i;
+ if (!rdev->mode_info.mode_config_initialized)
+ return;
+
radeon_update_display_priority(rdev);
for (i = 0; i < rdev->num_crtc; i++) {
static void tegra_crtc_disable(struct drm_crtc *crtc)
{
- struct tegra_dc *dc = to_tegra_dc(crtc);
struct drm_device *drm = crtc->dev;
struct drm_plane *plane;
}
}
- drm_vblank_off(drm, dc->pipe);
+ drm_crtc_vblank_off(crtc);
}
static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc,
u32 value;
int err;
- drm_vblank_pre_modeset(crtc->dev, dc->pipe);
-
err = tegra_crtc_setup_clk(crtc, mode);
if (err) {
dev_err(dc->dev, "failed to setup clock for CRTC: %d\n", err);
unsigned int syncpt;
unsigned long value;
+ drm_crtc_vblank_off(crtc);
+
/* hardware initialization */
reset_control_deassert(dc->rst);
usleep_range(10000, 20000);
value = GENERAL_ACT_REQ | WIN_A_ACT_REQ;
tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
- drm_vblank_post_modeset(crtc->dev, dc->pipe);
+ drm_crtc_vblank_on(crtc);
}
static void tegra_crtc_load_lut(struct drm_crtc *crtc)
hdev->hiddev_disconnect(hdev);
if (hdev->claimed & HID_CLAIMED_HIDRAW)
hidraw_disconnect(hdev);
+ hdev->claimed = 0;
}
EXPORT_SYMBOL_GPL(hid_disconnect);
#define USB_VENDOR_ID_ELAN 0x04f3
#define USB_DEVICE_ID_ELAN_TOUCHSCREEN 0x0089
#define USB_DEVICE_ID_ELAN_TOUCHSCREEN_009B 0x009b
+#define USB_DEVICE_ID_ELAN_TOUCHSCREEN_0103 0x0103
#define USB_DEVICE_ID_ELAN_TOUCHSCREEN_016F 0x016f
#define USB_VENDOR_ID_ELECOM 0x056e
ret = -ENOMEM;
goto err_stop_hw;
}
- sd->hid_sensor_hub_client_devs[
- sd->hid_sensor_client_cnt].id =
- PLATFORM_DEVID_AUTO;
sd->hid_sensor_hub_client_devs[
sd->hid_sensor_client_cnt].name = name;
sd->hid_sensor_hub_client_devs[
if (last_hsdev)
last_hsdev->end_collection_index = i;
- ret = mfd_add_devices(&hdev->dev, 0, sd->hid_sensor_hub_client_devs,
- sd->hid_sensor_client_cnt, NULL, 0, NULL);
+ ret = mfd_add_hotplug_devices(&hdev->dev,
+ sd->hid_sensor_hub_client_devs,
+ sd->hid_sensor_client_cnt);
if (ret < 0)
goto err_stop_hw;
{ USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_009B, HID_QUIRK_ALWAYS_POLL },
+ { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_0103, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_016F, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
will be called lm93.
config SENSORS_LM95234
- tristate "National Semiconductor LM95234"
+ tristate "National Semiconductor LM95234 and compatibles"
depends on I2C
help
- If you say yes here you get support for the LM95234 temperature
- sensor.
+ If you say yes here you get support for the LM95233 and LM95234
+ temperature sensor chips.
This driver can also be built as a module. If so, the module
will be called lm95234.
will be called lm95241.
config SENSORS_LM95245
- tristate "National Semiconductor LM95245 sensor chip"
+ tristate "National Semiconductor LM95245 and compatibles"
depends on I2C
help
- If you say yes here you get support for LM95245 sensor chip.
+ If you say yes here you get support for LM95235 and LM95245
+ temperature sensor chips.
This driver can also be built as a module. If so, the module
will be called lm95245.
help
If you say yes here you get support for the hardware monitoring
functionality of the Nuvoton NCT6106D, NCT6775F, NCT6776F, NCT6779D,
- NCT6791D and compatible Super-I/O chips. This driver replaces the
- w83627ehf driver for NCT6775F and NCT6776F.
+ NCT6791D, NCT6792D and compatible Super-I/O chips. This driver
+ replaces the w83627ehf driver for NCT6775F and NCT6776F.
This driver can also be built as a module. If so, the module
will be called nct6775.
+config SENSORS_NCT7802
+ tristate "Nuvoton NCT7802Y"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for the Nuvoton NCT7802Y
+ hardware monitoring chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called nct7802.
+
config SENSORS_PCF8591
tristate "Philips PCF8591 ADC/DAC"
depends on I2C
depends on I2C
help
If you say yes here you get support for Texas Instruments TMP401,
- TMP411, TMP431, and TMP432 temperature sensor chips.
+ TMP411, TMP431, TMP432 and TMP435 temperature sensor chips.
This driver can also be built as a module. If so, the module
will be called tmp401.
obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o
obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o
obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o
+obj-$(CONFIG_SENSORS_NCT7802) += nct7802.o
obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F4) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F4) },
- { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F3) },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F4) },
{}
};
MODULE_DEVICE_TABLE(pci, fam15h_power_id_table);
if (ret)
goto clock_dis;
- data->hwmon_dev = devm_hwmon_device_register_with_groups(dev,
- client->name,
- data,
- g762_groups);
+ data->hwmon_dev = hwmon_device_register_with_groups(dev, client->name,
+ data, g762_groups);
if (IS_ERR(data->hwmon_dev)) {
ret = PTR_ERR(data->hwmon_dev);
goto clock_dis;
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
struct gpio_fan_alarm *alarm = fan_data->alarm;
- int value = gpio_get_value(alarm->gpio);
+ int value = gpio_get_value_cansleep(alarm->gpio);
if (alarm->active_low)
value = !value;
int i;
for (i = 0; i < fan_data->num_ctrl; i++)
- gpio_set_value(fan_data->ctrl[i], (ctrl_val >> i) & 1);
+ gpio_set_value_cansleep(fan_data->ctrl[i], (ctrl_val >> i) & 1);
}
static int __get_fan_ctrl(struct gpio_fan_data *fan_data)
for (i = 0; i < fan_data->num_ctrl; i++) {
int value;
- value = gpio_get_value(fan_data->ctrl[i]);
+ value = gpio_get_value_cansleep(fan_data->ctrl[i]);
ctrl_val |= (value << i);
}
return ctrl_val;
if (err)
return err;
- err = gpio_direction_output(ctrl[i], gpio_get_value(ctrl[i]));
+ err = gpio_direction_output(ctrl[i],
+ gpio_get_value_cansleep(ctrl[i]));
if (err)
return err;
}
return 0;
}
+static void gpio_fan_shutdown(struct platform_device *pdev)
+{
+ struct gpio_fan_data *fan_data = dev_get_drvdata(&pdev->dev);
+
+ if (fan_data->ctrl)
+ set_fan_speed(fan_data, 0);
+}
+
#ifdef CONFIG_PM_SLEEP
static int gpio_fan_suspend(struct device *dev)
{
static struct platform_driver gpio_fan_driver = {
.probe = gpio_fan_probe,
+ .shutdown = gpio_fan_shutdown,
.driver = {
.name = "gpio-fan",
.pm = GPIO_FAN_PM,
u32 sensors_count; /* Total count of sensors from each group */
};
-/* Platform device representing all the ibmpowernv sensors */
-static struct platform_device *pdevice;
-
static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr,
char *buf)
{
return sprintf(buf, "%u\n", x);
}
-static int __init get_sensor_index_attr(const char *name, u32 *index,
+static int get_sensor_index_attr(const char *name, u32 *index,
char *attr)
{
char *hash_pos = strchr(name, '#');
* which need to be mapped as fan2_input, temp1_max respectively before
* populating them inside hwmon device class.
*/
-static int __init create_hwmon_attr_name(struct device *dev, enum sensors type,
+static int create_hwmon_attr_name(struct device *dev, enum sensors type,
const char *node_name,
char *hwmon_attr_name)
{
return 0;
}
-static int __init populate_attr_groups(struct platform_device *pdev)
+static int populate_attr_groups(struct platform_device *pdev)
{
struct platform_data *pdata = platform_get_drvdata(pdev);
const struct attribute_group **pgroups = pdata->attr_groups;
enum sensors type;
opal = of_find_node_by_path("/ibm,opal/sensors");
- if (!opal) {
- dev_err(&pdev->dev, "Opal node 'sensors' not found\n");
- return -ENODEV;
- }
-
for_each_child_of_node(opal, np) {
if (np->name == NULL)
continue;
* to the name required by the higher 'hwmon' driver like fan1_input, temp1_max
* etc..
*/
-static int __init create_device_attrs(struct platform_device *pdev)
+static int create_device_attrs(struct platform_device *pdev)
{
struct platform_data *pdata = platform_get_drvdata(pdev);
const struct attribute_group **pgroups = pdata->attr_groups;
return err;
}
-static int __init ibmpowernv_probe(struct platform_device *pdev)
+static int ibmpowernv_probe(struct platform_device *pdev)
{
struct platform_data *pdata;
struct device *hwmon_dev;
return PTR_ERR_OR_ZERO(hwmon_dev);
}
-static struct platform_driver ibmpowernv_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = DRVNAME,
+static const struct platform_device_id opal_sensor_driver_ids[] = {
+ {
+ .name = "opal-sensor",
},
+ { }
};
+MODULE_DEVICE_TABLE(platform, opal_sensor_driver_ids);
-static int __init ibmpowernv_init(void)
-{
- int err;
-
- pdevice = platform_device_alloc(DRVNAME, 0);
- if (!pdevice) {
- pr_err("Device allocation failed\n");
- err = -ENOMEM;
- goto exit;
- }
-
- err = platform_device_add(pdevice);
- if (err) {
- pr_err("Device addition failed (%d)\n", err);
- goto exit_device_put;
- }
-
- err = platform_driver_probe(&ibmpowernv_driver, ibmpowernv_probe);
- if (err) {
- pr_err("Platfrom driver probe failed\n");
- goto exit_device_del;
- }
-
- return 0;
-
-exit_device_del:
- platform_device_del(pdevice);
-exit_device_put:
- platform_device_put(pdevice);
-exit:
- return err;
-}
+static struct platform_driver ibmpowernv_driver = {
+ .probe = ibmpowernv_probe,
+ .id_table = opal_sensor_driver_ids,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRVNAME,
+ },
+};
-static void __exit ibmpowernv_exit(void)
-{
- platform_driver_unregister(&ibmpowernv_driver);
- platform_device_unregister(pdevice);
-}
+module_platform_driver(ibmpowernv_driver);
MODULE_AUTHOR("Neelesh Gupta <neelegup@linux.vnet.ibm.com>");
MODULE_DESCRIPTION("IBM POWERNV platform sensors");
MODULE_LICENSE("GPL");
-
-module_init(ibmpowernv_init);
-module_exit(ibmpowernv_exit);
struct iio_hwmon_state *st;
struct sensor_device_attribute *a;
int ret, i;
- int in_i = 1, temp_i = 1, curr_i = 1;
+ int in_i = 1, temp_i = 1, curr_i = 1, humidity_i = 1;
enum iio_chan_type type;
struct iio_channel *channels;
const char *name = "iio_hwmon";
"curr%d_input",
curr_i++);
break;
+ case IIO_HUMIDITYRELATIVE:
+ a->dev_attr.attr.name = kasprintf(GFP_KERNEL,
+ "humidity%d_input",
+ humidity_i++);
+ break;
default:
ret = -EINVAL;
goto error_release_channels;
struct device *hwmon_dev;
long shunt = 10000; /* default shunt value 10mOhms */
u32 val;
+ int ret;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV;
data->config = &ina2xx_config[data->kind];
/* device configuration */
- i2c_smbus_write_word_swapped(client, INA2XX_CONFIG,
- data->config->config_default);
- /* set current LSB to 1mA, shunt is in uOhms */
- /* (equation 13 in datasheet) */
- i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION,
- data->config->calibration_factor / shunt);
+ ret = i2c_smbus_write_word_swapped(client, INA2XX_CONFIG,
+ data->config->config_default);
+ if (ret < 0) {
+ dev_err(dev,
+ "error writing to the config register: %d", ret);
+ return -ENODEV;
+ }
+
+ /*
+ * Set current LSB to 1mA, shunt is in uOhms
+ * (equation 13 in datasheet).
+ */
+ ret = i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION,
+ data->config->calibration_factor / shunt);
+ if (ret < 0) {
+ dev_err(dev,
+ "error writing to the calibration register: %d", ret);
+ return -ENODEV;
+ }
data->client = client;
mutex_init(&data->update_lock);
g751,
lm75,
lm75a,
+ lm75b,
max6625,
max6626,
mcp980x,
data->resolution = 9;
data->sample_time = HZ / 2;
break;
+ case lm75b:
+ data->resolution = 11;
+ data->sample_time = HZ / 4;
+ break;
case max6625:
data->resolution = 9;
data->sample_time = HZ / 4;
{ "g751", g751, },
{ "lm75", lm75, },
{ "lm75a", lm75a, },
+ { "lm75b", lm75b, },
{ "max6625", max6625, },
{ "max6626", max6626, },
{ "mcp980x", mcp980x, },
|| i2c_smbus_read_byte_data(new_client, 7) != os)
return -ENODEV;
}
+ /*
+ * It is very unlikely that this is a LM75 if both
+ * hysteresis and temperature limit registers are 0.
+ */
+ if (hyst == 0 && os == 0)
+ return -ENODEV;
/* Addresses cycling */
for (i = 8; i <= 248; i += 40) {
/*
* Driver for Texas Instruments / National Semiconductor LM95234
*
- * Copyright (c) 2013 Guenter Roeck <linux@roeck-us.net>
+ * Copyright (c) 2013, 2014 Guenter Roeck <linux@roeck-us.net>
*
* Derived from lm95241.c
* Copyright (C) 2008, 2010 Davide Rizzo <elpa.rizzo@gmail.com>
#define DRVNAME "lm95234"
-static const unsigned short normal_i2c[] = { 0x18, 0x4d, 0x4e, I2C_CLIENT_END };
+enum chips { lm95233, lm95234 };
+
+static const unsigned short normal_i2c[] = {
+ 0x18, 0x2a, 0x2b, 0x4d, 0x4e, I2C_CLIENT_END };
/* LM95234 registers */
#define LM95234_REG_MAN_ID 0xFE
#define LM95234_REG_TCRIT_HYST 0x5a
#define NATSEMI_MAN_ID 0x01
+#define LM95233_CHIP_ID 0x89
#define LM95234_CHIP_ID 0x79
/* Client data (each client gets its own) */
struct lm95234_data {
struct i2c_client *client;
+ const struct attribute_group *groups[3];
struct mutex update_lock;
unsigned long last_updated, interval; /* in jiffies */
bool valid; /* false until following fields are valid */
static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval,
set_interval);
-static struct attribute *lm95234_attrs[] = {
+static struct attribute *lm95234_common_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp2_input.dev_attr.attr,
&sensor_dev_attr_temp3_input.dev_attr.attr,
- &sensor_dev_attr_temp4_input.dev_attr.attr,
- &sensor_dev_attr_temp5_input.dev_attr.attr,
&sensor_dev_attr_temp2_fault.dev_attr.attr,
&sensor_dev_attr_temp3_fault.dev_attr.attr,
- &sensor_dev_attr_temp4_fault.dev_attr.attr,
- &sensor_dev_attr_temp5_fault.dev_attr.attr,
&sensor_dev_attr_temp2_type.dev_attr.attr,
&sensor_dev_attr_temp3_type.dev_attr.attr,
- &sensor_dev_attr_temp4_type.dev_attr.attr,
- &sensor_dev_attr_temp5_type.dev_attr.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr,
&sensor_dev_attr_temp2_max.dev_attr.attr,
&sensor_dev_attr_temp3_max.dev_attr.attr,
- &sensor_dev_attr_temp4_max.dev_attr.attr,
- &sensor_dev_attr_temp5_max.dev_attr.attr,
&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
&sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
&sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
- &sensor_dev_attr_temp4_max_hyst.dev_attr.attr,
- &sensor_dev_attr_temp5_max_hyst.dev_attr.attr,
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
- &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
- &sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_crit.dev_attr.attr,
&sensor_dev_attr_temp3_crit.dev_attr.attr,
&sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
&sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_offset.dev_attr.attr,
&sensor_dev_attr_temp3_offset.dev_attr.attr,
+ &dev_attr_update_interval.attr,
+ NULL
+};
+
+static const struct attribute_group lm95234_common_group = {
+ .attrs = lm95234_common_attrs,
+};
+
+static struct attribute *lm95234_attrs[] = {
+ &sensor_dev_attr_temp4_input.dev_attr.attr,
+ &sensor_dev_attr_temp5_input.dev_attr.attr,
+ &sensor_dev_attr_temp4_fault.dev_attr.attr,
+ &sensor_dev_attr_temp5_fault.dev_attr.attr,
+ &sensor_dev_attr_temp4_type.dev_attr.attr,
+ &sensor_dev_attr_temp5_type.dev_attr.attr,
+ &sensor_dev_attr_temp4_max.dev_attr.attr,
+ &sensor_dev_attr_temp5_max.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp5_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp4_offset.dev_attr.attr,
&sensor_dev_attr_temp5_offset.dev_attr.attr,
- &dev_attr_update_interval.attr,
NULL
};
-ATTRIBUTE_GROUPS(lm95234);
+
+static const struct attribute_group lm95234_group = {
+ .attrs = lm95234_attrs,
+};
static int lm95234_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
+ int address = client->addr;
+ u8 config_mask, model_mask;
int mfg_id, chip_id, val;
+ const char *name;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
return -ENODEV;
chip_id = i2c_smbus_read_byte_data(client, LM95234_REG_CHIP_ID);
- if (chip_id != LM95234_CHIP_ID)
+ switch (chip_id) {
+ case LM95233_CHIP_ID:
+ if (address != 0x18 && address != 0x2a && address != 0x2b)
+ return -ENODEV;
+ config_mask = 0xbf;
+ model_mask = 0xf9;
+ name = "lm95233";
+ break;
+ case LM95234_CHIP_ID:
+ if (address != 0x18 && address != 0x4d && address != 0x4e)
+ return -ENODEV;
+ config_mask = 0xbc;
+ model_mask = 0xe1;
+ name = "lm95234";
+ break;
+ default:
return -ENODEV;
+ }
val = i2c_smbus_read_byte_data(client, LM95234_REG_STATUS);
if (val & 0x30)
return -ENODEV;
val = i2c_smbus_read_byte_data(client, LM95234_REG_CONFIG);
- if (val & 0xbc)
+ if (val & config_mask)
return -ENODEV;
val = i2c_smbus_read_byte_data(client, LM95234_REG_CONVRATE);
return -ENODEV;
val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL);
- if (val & 0xe1)
+ if (val & model_mask)
return -ENODEV;
val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL_STS);
- if (val & 0xe1)
+ if (val & model_mask)
return -ENODEV;
- strlcpy(info->type, "lm95234", I2C_NAME_SIZE);
+ strlcpy(info->type, name, I2C_NAME_SIZE);
return 0;
}
if (err < 0)
return err;
+ data->groups[0] = &lm95234_common_group;
+ if (id->driver_data == lm95234)
+ data->groups[1] = &lm95234_group;
+
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
- data,
- lm95234_groups);
+ data, data->groups);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
/* Driver data (common to all clients) */
static const struct i2c_device_id lm95234_id[] = {
- { "lm95234", 0 },
+ { "lm95233", lm95233 },
+ { "lm95234", lm95234 },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm95234_id);
module_i2c_driver(lm95234_driver);
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
-MODULE_DESCRIPTION("LM95234 sensor driver");
+MODULE_DESCRIPTION("LM95233/LM95234 sensor driver");
MODULE_LICENSE("GPL");
/*
* Copyright (C) 2011 Alexander Stein <alexander.stein@systec-electronic.com>
*
- * The LM95245 is a sensor chip made by National Semiconductors.
+ * The LM95245 is a sensor chip made by TI / National Semiconductor.
* It reports up to two temperatures (its own plus an external one).
- * Complete datasheet can be obtained from National's website at:
- * http://www.national.com/ds.cgi/LM/LM95245.pdf
*
* This driver is based on lm95241.c
*
#include <linux/mutex.h>
#include <linux/sysfs.h>
-#define DEVNAME "lm95245"
-
static const unsigned short normal_i2c[] = {
0x18, 0x19, 0x29, 0x4c, 0x4d, I2C_CLIENT_END };
#define STATUS1_LOC 0x01
#define MANUFACTURER_ID 0x01
-#define DEFAULT_REVISION 0xB3
+#define LM95235_REVISION 0xB1
+#define LM95245_REVISION 0xB3
static const u8 lm95245_reg_address[] = {
LM95245_REG_R_LOCAL_TEMPH_S,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = new_client->adapter;
+ int address = new_client->addr;
+ const char *name;
+ int rev, id;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- if (i2c_smbus_read_byte_data(new_client, LM95245_REG_R_MAN_ID)
- != MANUFACTURER_ID
- || i2c_smbus_read_byte_data(new_client, LM95245_REG_R_CHIP_ID)
- != DEFAULT_REVISION)
+ id = i2c_smbus_read_byte_data(new_client, LM95245_REG_R_MAN_ID);
+ if (id != MANUFACTURER_ID)
return -ENODEV;
- strlcpy(info->type, DEVNAME, I2C_NAME_SIZE);
+ rev = i2c_smbus_read_byte_data(new_client, LM95245_REG_R_CHIP_ID);
+ switch (rev) {
+ case LM95235_REVISION:
+ if (address != 0x18 && address != 0x29 && address != 0x4c)
+ return -ENODEV;
+ name = "lm95235";
+ break;
+ case LM95245_REVISION:
+ name = "lm95245";
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ strlcpy(info->type, name, I2C_NAME_SIZE);
return 0;
}
/* Driver data (common to all clients) */
static const struct i2c_device_id lm95245_id[] = {
- { DEVNAME, 0 },
+ { "lm95235", 0 },
+ { "lm95245", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm95245_id);
static struct i2c_driver lm95245_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
- .name = DEVNAME,
+ .name = "lm95245",
},
.probe = lm95245_probe,
.id_table = lm95245_id,
module_i2c_driver(lm95245_driver);
MODULE_AUTHOR("Alexander Stein <alexander.stein@systec-electronic.com>");
-MODULE_DESCRIPTION("LM95245 sensor driver");
+MODULE_DESCRIPTION("LM95235/LM95245 sensor driver");
MODULE_LICENSE("GPL");
* nct6776f 9 5 3 6+3 0xc330 0xc1 0x5ca3
* nct6779d 15 5 5 2+6 0xc560 0xc1 0x5ca3
* nct6791d 15 6 6 2+6 0xc800 0xc1 0x5ca3
+ * nct6792d 15 6 6 2+6 0xc910 0xc1 0x5ca3
*
* #temp lists the number of monitored temperature sources (first value) plus
* the number of directly connectable temperature sensors (second value).
#define USE_ALTERNATE
-enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791 };
+enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791, nct6792 };
/* used to set data->name = nct6775_device_names[data->sio_kind] */
static const char * const nct6775_device_names[] = {
"nct6776",
"nct6779",
"nct6791",
+ "nct6792",
};
static unsigned short force_id;
#define SIO_NCT6776_ID 0xc330
#define SIO_NCT6779_ID 0xc560
#define SIO_NCT6791_ID 0xc800
+#define SIO_NCT6792_ID 0xc910
#define SIO_ID_MASK 0xFFF0
enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 };
4, 5, 13, -1, -1, -1, /* temp1..temp6 */
12, 9 }; /* intrusion0, intrusion1 */
+/* NCT6792 specific data */
+
+static const u16 NCT6792_REG_TEMP_MON[] = {
+ 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7d };
+static const u16 NCT6792_REG_BEEP[NUM_REG_BEEP] = {
+ 0xb2, 0xb3, 0xb4, 0xb5, 0xbf };
/* NCT6102D/NCT6106D specific data */
reg == 0x73 || reg == 0x75 || reg == 0x77;
case nct6779:
case nct6791:
+ case nct6792:
return reg == 0x150 || reg == 0x153 || reg == 0x155 ||
((reg & 0xfff0) == 0x4b0 && (reg & 0x000f) < 0x0b) ||
reg == 0x402 ||
reg == 0x63a || reg == 0x63c || reg == 0x63e ||
reg == 0x640 || reg == 0x642 ||
reg == 0x73 || reg == 0x75 || reg == 0x77 || reg == 0x79 ||
- reg == 0x7b;
+ reg == 0x7b || reg == 0x7d;
}
return false;
}
static inline void nct6775_set_bank(struct nct6775_data *data, u16 reg)
{
u8 bank = reg >> 8;
+
if (data->bank != bank) {
outb_p(NCT6775_REG_BANK, data->addr + ADDR_REG_OFFSET);
outb_p(bank, data->addr + DATA_REG_OFFSET);
if (!data->target_speed_tolerance[i] ||
data->pwm_enable[i] == speed_cruise) {
u8 t = fanmodecfg & 0x0f;
+
if (data->REG_TOLERANCE_H) {
t |= (nct6775_read_value(data,
data->REG_TOLERANCE_H[i]) & 0x70) >> 1;
case nct6106:
case nct6779:
case nct6791:
+ case nct6792:
reg = nct6775_read_value(data,
data->REG_CRITICAL_PWM_ENABLE[i]);
if (reg & data->CRITICAL_PWM_ENABLE_MASK)
data->alarms = 0;
for (i = 0; i < NUM_REG_ALARM; i++) {
u8 alarm;
+
if (!data->REG_ALARM[i])
continue;
alarm = nct6775_read_value(data, data->REG_ALARM[i]);
data->beeps = 0;
for (i = 0; i < NUM_REG_BEEP; i++) {
u8 beep;
+
if (!data->REG_BEEP[i])
continue;
beep = nct6775_read_value(data, data->REG_BEEP[i]);
{
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
- int nr = sattr->nr;
int index = sattr->index;
+ int nr = sattr->nr;
+
return sprintf(buf, "%ld\n", in_from_reg(data->in[nr][index], nr));
}
{
struct nct6775_data *data = dev_get_drvdata(dev);
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
- int nr = sattr->nr;
int index = sattr->index;
+ int nr = sattr->nr;
unsigned long val;
- int err = kstrtoul(buf, 10, &val);
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
if (err < 0)
return err;
mutex_lock(&data->update_lock);
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = data->ALARM_BITS[sattr->index];
+
return sprintf(buf, "%u\n",
(unsigned int)((data->alarms >> nr) & 0x01));
}
nr = find_temp_source(data, sattr->index, data->num_temp_alarms);
if (nr >= 0) {
int bit = data->ALARM_BITS[nr + TEMP_ALARM_BASE];
+
alarm = (data->alarms >> bit) & 0x01;
}
return sprintf(buf, "%u\n", alarm);
int nr = data->BEEP_BITS[sattr->index];
int regindex = nr >> 3;
unsigned long val;
+ int err;
- int err = kstrtoul(buf, 10, &val);
+ err = kstrtoul(buf, 10, &val);
if (err < 0)
return err;
if (val > 1)
nr = find_temp_source(data, sattr->index, data->num_temp_beeps);
if (nr >= 0) {
int bit = data->BEEP_BITS[nr + TEMP_ALARM_BASE];
+
beep = (data->beeps >> bit) & 0x01;
}
return sprintf(buf, "%u\n", beep);
struct nct6775_data *data = dev_get_drvdata(dev);
int nr, bit, regindex;
unsigned long val;
+ int err;
- int err = kstrtoul(buf, 10, &val);
+ err = kstrtoul(buf, 10, &val);
if (err < 0)
return err;
if (val > 1)
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
+
return sprintf(buf, "%d\n", data->rpm[nr]);
}
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
+
return sprintf(buf, "%d\n",
data->fan_from_reg_min(data->fan_min[nr],
data->fan_div[nr]));
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
+
return sprintf(buf, "%u\n", div_from_reg(data->fan_div[nr]));
}
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
unsigned long val;
- int err;
unsigned int reg;
u8 new_div;
+ int err;
err = kstrtoul(buf, 10, &val);
if (err < 0)
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
+
return sprintf(buf, "%s\n", data->temp_label[data->temp_src[nr]]);
}
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
+
return sprintf(buf, "%d\n", (int)data->temp_type[nr]);
}
case nct6106:
case nct6779:
case nct6791:
+ case nct6792:
nct6775_write_value(data, data->REG_CRITICAL_PWM[nr],
val);
reg = nct6775_read_value(data,
show_vid(struct device *dev, struct device_attribute *attr, char *buf)
{
struct nct6775_data *data = dev_get_drvdata(dev);
+
return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
}
pwm4pin = false;
pwm5pin = false;
pwm6pin = false;
- } else { /* NCT6779D or NCT6791D */
+ } else { /* NCT6779D, NCT6791D, or NCT6792D */
regval = superio_inb(sioreg, 0x1c);
fan3pin = !(regval & (1 << 5));
fan4min = fan4pin;
- if (data->kind == nct6791) {
+ if (data->kind == nct6791 || data->kind == nct6792) {
regval = superio_inb(sioreg, 0x2d);
fan6pin = (regval & (1 << 1));
pwm6pin = (regval & (1 << 0));
break;
case nct6791:
+ case nct6792:
data->in_num = 15;
data->pwm_num = 6;
data->auto_pwm_num = 4;
data->REG_WEIGHT_TEMP[1] = NCT6791_REG_WEIGHT_TEMP_STEP_TOL;
data->REG_WEIGHT_TEMP[2] = NCT6791_REG_WEIGHT_TEMP_BASE;
data->REG_ALARM = NCT6791_REG_ALARM;
- data->REG_BEEP = NCT6776_REG_BEEP;
+ if (data->kind == nct6791)
+ data->REG_BEEP = NCT6776_REG_BEEP;
+ else
+ data->REG_BEEP = NCT6792_REG_BEEP;
reg_temp = NCT6779_REG_TEMP;
- reg_temp_mon = NCT6779_REG_TEMP_MON;
num_reg_temp = ARRAY_SIZE(NCT6779_REG_TEMP);
- num_reg_temp_mon = ARRAY_SIZE(NCT6779_REG_TEMP_MON);
+ if (data->kind == nct6791) {
+ reg_temp_mon = NCT6779_REG_TEMP_MON;
+ num_reg_temp_mon = ARRAY_SIZE(NCT6779_REG_TEMP_MON);
+ } else {
+ reg_temp_mon = NCT6792_REG_TEMP_MON;
+ num_reg_temp_mon = ARRAY_SIZE(NCT6792_REG_TEMP_MON);
+ }
reg_temp_over = NCT6779_REG_TEMP_OVER;
reg_temp_hyst = NCT6779_REG_TEMP_HYST;
reg_temp_config = NCT6779_REG_TEMP_CONFIG;
case nct6106:
case nct6779:
case nct6791:
+ case nct6792:
break;
}
tmp |= 0x3e;
break;
case nct6791:
+ case nct6792:
tmp |= 0x7e;
break;
}
mutex_lock(&data->update_lock);
data->bank = 0xff; /* Force initial bank selection */
- if (data->kind == nct6791) {
+ if (data->kind == nct6791 || data->kind == nct6792) {
err = superio_enter(data->sioreg);
if (err)
goto abort;
"NCT6776D/F",
"NCT6779D",
"NCT6791D",
+ "NCT6792D",
};
/* nct6775_find() looks for a '627 in the Super-I/O config space */
case SIO_NCT6791_ID:
sio_data->kind = nct6791;
break;
+ case SIO_NCT6792_ID:
+ sio_data->kind = nct6792;
+ break;
default:
if (val != 0xffff)
pr_debug("unsupported chip ID: 0x%04x\n", val);
superio_outb(sioaddr, SIO_REG_ENABLE, val | 0x01);
}
- if (sio_data->kind == nct6791)
+ if (sio_data->kind == nct6791 || sio_data->kind == nct6792)
nct6791_enable_io_mapping(sioaddr);
superio_exit(sioaddr);
}
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
-MODULE_DESCRIPTION("NCT6106D/NCT6775F/NCT6776F/NCT6779D/NCT6791D driver");
+MODULE_DESCRIPTION("NCT6106D/NCT6775F/NCT6776F/NCT6779D/NCT6791D/NCT6792D driver");
MODULE_LICENSE("GPL");
module_init(sensors_nct6775_init);
--- /dev/null
+/*
+ * nct7802 - Driver for Nuvoton NCT7802Y
+ *
+ * Copyright (C) 2014 Guenter Roeck <linux@roeck-us.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define DRVNAME "nct7802"
+
+static const u8 REG_VOLTAGE[5] = { 0x09, 0x0a, 0x0c, 0x0d, 0x0e };
+
+static const u8 REG_VOLTAGE_LIMIT_LSB[2][5] = {
+ { 0x40, 0x00, 0x42, 0x44, 0x46 },
+ { 0x3f, 0x00, 0x41, 0x43, 0x45 },
+};
+
+static const u8 REG_VOLTAGE_LIMIT_MSB[5] = { 0x48, 0x00, 0x47, 0x47, 0x48 };
+
+static const u8 REG_VOLTAGE_LIMIT_MSB_SHIFT[2][5] = {
+ { 0, 0, 4, 0, 4 },
+ { 2, 0, 6, 2, 6 },
+};
+
+#define REG_BANK 0x00
+#define REG_TEMP_LSB 0x05
+#define REG_TEMP_PECI_LSB 0x08
+#define REG_VOLTAGE_LOW 0x0f
+#define REG_FANCOUNT_LOW 0x13
+#define REG_START 0x21
+#define REG_MODE 0x22
+#define REG_PECI_ENABLE 0x23
+#define REG_FAN_ENABLE 0x24
+#define REG_VMON_ENABLE 0x25
+#define REG_VENDOR_ID 0xfd
+#define REG_CHIP_ID 0xfe
+#define REG_VERSION_ID 0xff
+
+/*
+ * Data structures and manipulation thereof
+ */
+
+struct nct7802_data {
+ struct regmap *regmap;
+ struct mutex access_lock; /* for multi-byte read and write operations */
+};
+
+static int nct7802_read_temp(struct nct7802_data *data,
+ u8 reg_temp, u8 reg_temp_low, int *temp)
+{
+ unsigned int t1, t2 = 0;
+ int err;
+
+ *temp = 0;
+
+ mutex_lock(&data->access_lock);
+ err = regmap_read(data->regmap, reg_temp, &t1);
+ if (err < 0)
+ goto abort;
+ t1 <<= 8;
+ if (reg_temp_low) { /* 11 bit data */
+ err = regmap_read(data->regmap, reg_temp_low, &t2);
+ if (err < 0)
+ goto abort;
+ }
+ t1 |= t2 & 0xe0;
+ *temp = (s16)t1 / 32 * 125;
+abort:
+ mutex_unlock(&data->access_lock);
+ return err;
+}
+
+static int nct7802_read_fan(struct nct7802_data *data, u8 reg_fan)
+{
+ unsigned int f1, f2;
+ int ret;
+
+ mutex_lock(&data->access_lock);
+ ret = regmap_read(data->regmap, reg_fan, &f1);
+ if (ret < 0)
+ goto abort;
+ ret = regmap_read(data->regmap, REG_FANCOUNT_LOW, &f2);
+ if (ret < 0)
+ goto abort;
+ ret = (f1 << 5) | (f2 >> 3);
+ /* convert fan count to rpm */
+ if (ret == 0x1fff) /* maximum value, assume fan is stopped */
+ ret = 0;
+ else if (ret)
+ ret = DIV_ROUND_CLOSEST(1350000U, ret);
+abort:
+ mutex_unlock(&data->access_lock);
+ return ret;
+}
+
+static int nct7802_read_fan_min(struct nct7802_data *data, u8 reg_fan_low,
+ u8 reg_fan_high)
+{
+ unsigned int f1, f2;
+ int ret;
+
+ mutex_lock(&data->access_lock);
+ ret = regmap_read(data->regmap, reg_fan_low, &f1);
+ if (ret < 0)
+ goto abort;
+ ret = regmap_read(data->regmap, reg_fan_high, &f2);
+ if (ret < 0)
+ goto abort;
+ ret = f1 | ((f2 & 0xf8) << 5);
+ /* convert fan count to rpm */
+ if (ret == 0x1fff) /* maximum value, assume no limit */
+ ret = 0;
+ else if (ret)
+ ret = DIV_ROUND_CLOSEST(1350000U, ret);
+abort:
+ mutex_unlock(&data->access_lock);
+ return ret;
+}
+
+static int nct7802_write_fan_min(struct nct7802_data *data, u8 reg_fan_low,
+ u8 reg_fan_high, unsigned int limit)
+{
+ int err;
+
+ if (limit)
+ limit = DIV_ROUND_CLOSEST(1350000U, limit);
+ else
+ limit = 0x1fff;
+ limit = clamp_val(limit, 0, 0x1fff);
+
+ mutex_lock(&data->access_lock);
+ err = regmap_write(data->regmap, reg_fan_low, limit & 0xff);
+ if (err < 0)
+ goto abort;
+
+ err = regmap_write(data->regmap, reg_fan_high, (limit & 0x1f00) >> 5);
+abort:
+ mutex_unlock(&data->access_lock);
+ return err;
+}
+
+static u8 nct7802_vmul[] = { 4, 2, 2, 2, 2 };
+
+static int nct7802_read_voltage(struct nct7802_data *data, int nr, int index)
+{
+ unsigned int v1, v2;
+ int ret;
+
+ mutex_lock(&data->access_lock);
+ if (index == 0) { /* voltage */
+ ret = regmap_read(data->regmap, REG_VOLTAGE[nr], &v1);
+ if (ret < 0)
+ goto abort;
+ ret = regmap_read(data->regmap, REG_VOLTAGE_LOW, &v2);
+ if (ret < 0)
+ goto abort;
+ ret = ((v1 << 2) | (v2 >> 6)) * nct7802_vmul[nr];
+ } else { /* limit */
+ int shift = 8 - REG_VOLTAGE_LIMIT_MSB_SHIFT[index - 1][nr];
+
+ ret = regmap_read(data->regmap,
+ REG_VOLTAGE_LIMIT_LSB[index - 1][nr], &v1);
+ if (ret < 0)
+ goto abort;
+ ret = regmap_read(data->regmap, REG_VOLTAGE_LIMIT_MSB[nr],
+ &v2);
+ if (ret < 0)
+ goto abort;
+ ret = (v1 | ((v2 << shift) & 0x300)) * nct7802_vmul[nr];
+ }
+abort:
+ mutex_unlock(&data->access_lock);
+ return ret;
+}
+
+static int nct7802_write_voltage(struct nct7802_data *data, int nr, int index,
+ unsigned int voltage)
+{
+ int shift = 8 - REG_VOLTAGE_LIMIT_MSB_SHIFT[index - 1][nr];
+ int err;
+
+ voltage = DIV_ROUND_CLOSEST(voltage, nct7802_vmul[nr]);
+ voltage = clamp_val(voltage, 0, 0x3ff);
+
+ mutex_lock(&data->access_lock);
+ err = regmap_write(data->regmap,
+ REG_VOLTAGE_LIMIT_LSB[index - 1][nr],
+ voltage & 0xff);
+ if (err < 0)
+ goto abort;
+
+ err = regmap_update_bits(data->regmap, REG_VOLTAGE_LIMIT_MSB[nr],
+ 0x0300 >> shift, (voltage & 0x0300) >> shift);
+abort:
+ mutex_unlock(&data->access_lock);
+ return err;
+}
+
+static ssize_t show_in(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ int voltage;
+
+ voltage = nct7802_read_voltage(data, sattr->nr, sattr->index);
+ if (voltage < 0)
+ return voltage;
+
+ return sprintf(buf, "%d\n", voltage);
+}
+
+static ssize_t store_in(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ int index = sattr->index;
+ int nr = sattr->nr;
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ err = nct7802_write_voltage(data, nr, index, val);
+ return err ? : count;
+}
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int err, temp;
+
+ err = nct7802_read_temp(data, sattr->nr, sattr->index, &temp);
+ if (err < 0)
+ return err;
+
+ return sprintf(buf, "%d\n", temp);
+}
+
+static ssize_t store_temp(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ int nr = sattr->nr;
+ long val;
+ int err;
+
+ err = kstrtol(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127);
+
+ err = regmap_write(data->regmap, nr, val & 0xff);
+ return err ? : count;
+}
+
+static ssize_t show_fan(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ int speed;
+
+ speed = nct7802_read_fan(data, sattr->index);
+ if (speed < 0)
+ return speed;
+
+ return sprintf(buf, "%d\n", speed);
+}
+
+static ssize_t show_fan_min(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ int speed;
+
+ speed = nct7802_read_fan_min(data, sattr->nr, sattr->index);
+ if (speed < 0)
+ return speed;
+
+ return sprintf(buf, "%d\n", speed);
+}
+
+static ssize_t store_fan_min(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ err = nct7802_write_fan_min(data, sattr->nr, sattr->index, val);
+ return err ? : count;
+}
+
+static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ int bit = sattr->index;
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(data->regmap, sattr->nr, &val);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%u\n", !!(val & (1 << bit)));
+}
+
+static ssize_t
+show_beep(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ unsigned int regval;
+ int err;
+
+ err = regmap_read(data->regmap, sattr->nr, ®val);
+ if (err)
+ return err;
+
+ return sprintf(buf, "%u\n", !!(regval & (1 << sattr->index)));
+}
+
+static ssize_t
+store_beep(struct device *dev, struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+ if (val > 1)
+ return -EINVAL;
+
+ err = regmap_update_bits(data->regmap, sattr->nr, 1 << sattr->index,
+ val ? 1 << sattr->index : 0);
+ return err ? : count;
+}
+
+static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0x01,
+ REG_TEMP_LSB);
+static SENSOR_DEVICE_ATTR_2(temp1_min, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x31, 0);
+static SENSOR_DEVICE_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x30, 0);
+static SENSOR_DEVICE_ATTR_2(temp1_crit, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x3a, 0);
+
+static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0x02,
+ REG_TEMP_LSB);
+static SENSOR_DEVICE_ATTR_2(temp2_min, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x33, 0);
+static SENSOR_DEVICE_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x32, 0);
+static SENSOR_DEVICE_ATTR_2(temp2_crit, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x3b, 0);
+
+static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0x03,
+ REG_TEMP_LSB);
+static SENSOR_DEVICE_ATTR_2(temp3_min, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x35, 0);
+static SENSOR_DEVICE_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x34, 0);
+static SENSOR_DEVICE_ATTR_2(temp3_crit, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x3c, 0);
+
+static SENSOR_DEVICE_ATTR_2(temp4_input, S_IRUGO, show_temp, NULL, 0x04, 0);
+static SENSOR_DEVICE_ATTR_2(temp4_min, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x37, 0);
+static SENSOR_DEVICE_ATTR_2(temp4_max, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x36, 0);
+static SENSOR_DEVICE_ATTR_2(temp4_crit, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x3d, 0);
+
+static SENSOR_DEVICE_ATTR_2(temp5_input, S_IRUGO, show_temp, NULL, 0x06,
+ REG_TEMP_PECI_LSB);
+static SENSOR_DEVICE_ATTR_2(temp5_min, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x39, 0);
+static SENSOR_DEVICE_ATTR_2(temp5_max, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x38, 0);
+static SENSOR_DEVICE_ATTR_2(temp5_crit, S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0x3e, 0);
+
+static SENSOR_DEVICE_ATTR_2(temp6_input, S_IRUGO, show_temp, NULL, 0x07,
+ REG_TEMP_PECI_LSB);
+
+static SENSOR_DEVICE_ATTR_2(temp1_min_alarm, S_IRUGO, show_alarm, NULL,
+ 0x18, 0);
+static SENSOR_DEVICE_ATTR_2(temp2_min_alarm, S_IRUGO, show_alarm, NULL,
+ 0x18, 1);
+static SENSOR_DEVICE_ATTR_2(temp3_min_alarm, S_IRUGO, show_alarm, NULL,
+ 0x18, 2);
+static SENSOR_DEVICE_ATTR_2(temp4_min_alarm, S_IRUGO, show_alarm, NULL,
+ 0x18, 3);
+static SENSOR_DEVICE_ATTR_2(temp5_min_alarm, S_IRUGO, show_alarm, NULL,
+ 0x18, 4);
+
+static SENSOR_DEVICE_ATTR_2(temp1_max_alarm, S_IRUGO, show_alarm, NULL,
+ 0x19, 0);
+static SENSOR_DEVICE_ATTR_2(temp2_max_alarm, S_IRUGO, show_alarm, NULL,
+ 0x19, 1);
+static SENSOR_DEVICE_ATTR_2(temp3_max_alarm, S_IRUGO, show_alarm, NULL,
+ 0x19, 2);
+static SENSOR_DEVICE_ATTR_2(temp4_max_alarm, S_IRUGO, show_alarm, NULL,
+ 0x19, 3);
+static SENSOR_DEVICE_ATTR_2(temp5_max_alarm, S_IRUGO, show_alarm, NULL,
+ 0x19, 4);
+
+static SENSOR_DEVICE_ATTR_2(temp1_crit_alarm, S_IRUGO, show_alarm, NULL,
+ 0x1b, 0);
+static SENSOR_DEVICE_ATTR_2(temp2_crit_alarm, S_IRUGO, show_alarm, NULL,
+ 0x1b, 1);
+static SENSOR_DEVICE_ATTR_2(temp3_crit_alarm, S_IRUGO, show_alarm, NULL,
+ 0x1b, 2);
+static SENSOR_DEVICE_ATTR_2(temp4_crit_alarm, S_IRUGO, show_alarm, NULL,
+ 0x1b, 3);
+static SENSOR_DEVICE_ATTR_2(temp5_crit_alarm, S_IRUGO, show_alarm, NULL,
+ 0x1b, 4);
+
+static SENSOR_DEVICE_ATTR_2(temp1_fault, S_IRUGO, show_alarm, NULL, 0x17, 0);
+static SENSOR_DEVICE_ATTR_2(temp2_fault, S_IRUGO, show_alarm, NULL, 0x17, 1);
+static SENSOR_DEVICE_ATTR_2(temp3_fault, S_IRUGO, show_alarm, NULL, 0x17, 2);
+
+static SENSOR_DEVICE_ATTR_2(temp1_beep, S_IRUGO | S_IWUSR, show_beep,
+ store_beep, 0x5c, 0);
+static SENSOR_DEVICE_ATTR_2(temp2_beep, S_IRUGO | S_IWUSR, show_beep,
+ store_beep, 0x5c, 1);
+static SENSOR_DEVICE_ATTR_2(temp3_beep, S_IRUGO | S_IWUSR, show_beep,
+ store_beep, 0x5c, 2);
+static SENSOR_DEVICE_ATTR_2(temp4_beep, S_IRUGO | S_IWUSR, show_beep,
+ store_beep, 0x5c, 3);
+static SENSOR_DEVICE_ATTR_2(temp5_beep, S_IRUGO | S_IWUSR, show_beep,
+ store_beep, 0x5c, 4);
+static SENSOR_DEVICE_ATTR_2(temp6_beep, S_IRUGO | S_IWUSR, show_beep,
+ store_beep, 0x5c, 5);
+
+static struct attribute *nct7802_temp_attrs[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_fault.dev_attr.attr,
+ &sensor_dev_attr_temp1_beep.dev_attr.attr,
+
+ &sensor_dev_attr_temp2_input.dev_attr.attr, /* 9 */
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit.dev_attr.attr,
+ &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+ &sensor_dev_attr_temp2_beep.dev_attr.attr,
+
+ &sensor_dev_attr_temp3_input.dev_attr.attr, /* 18 */
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit.dev_attr.attr,
+ &sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_fault.dev_attr.attr,
+ &sensor_dev_attr_temp3_beep.dev_attr.attr,
+
+ &sensor_dev_attr_temp4_input.dev_attr.attr, /* 27 */
+ &sensor_dev_attr_temp4_min.dev_attr.attr,
+ &sensor_dev_attr_temp4_max.dev_attr.attr,
+ &sensor_dev_attr_temp4_crit.dev_attr.attr,
+ &sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_beep.dev_attr.attr,
+
+ &sensor_dev_attr_temp5_input.dev_attr.attr, /* 35 */
+ &sensor_dev_attr_temp5_min.dev_attr.attr,
+ &sensor_dev_attr_temp5_max.dev_attr.attr,
+ &sensor_dev_attr_temp5_crit.dev_attr.attr,
+ &sensor_dev_attr_temp5_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_beep.dev_attr.attr,
+
+ &sensor_dev_attr_temp6_input.dev_attr.attr, /* 43 */
+ &sensor_dev_attr_temp6_beep.dev_attr.attr,
+
+ NULL
+};
+
+static umode_t nct7802_temp_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ unsigned int reg;
+ int err;
+
+ err = regmap_read(data->regmap, REG_MODE, ®);
+ if (err < 0)
+ return 0;
+
+ if (index < 9 &&
+ (reg & 03) != 0x01 && (reg & 0x03) != 0x02) /* RD1 */
+ return 0;
+ if (index >= 9 && index < 18 &&
+ (reg & 0x0c) != 0x04 && (reg & 0x0c) != 0x08) /* RD2 */
+ return 0;
+ if (index >= 18 && index < 27 && (reg & 0x30) != 0x10) /* RD3 */
+ return 0;
+ if (index >= 27 && index < 35) /* local */
+ return attr->mode;
+
+ err = regmap_read(data->regmap, REG_PECI_ENABLE, ®);
+ if (err < 0)
+ return 0;
+
+ if (index >= 35 && index < 43 && !(reg & 0x01)) /* PECI 0 */
+ return 0;
+
+ if (index >= 0x43 && (!(reg & 0x02))) /* PECI 1 */
+ return 0;
+
+ return attr->mode;
+}
+
+static struct attribute_group nct7802_temp_group = {
+ .attrs = nct7802_temp_attrs,
+ .is_visible = nct7802_temp_is_visible,
+};
+
+static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0);
+static SENSOR_DEVICE_ATTR_2(in0_min, S_IRUGO | S_IWUSR, show_in, store_in,
+ 0, 1);
+static SENSOR_DEVICE_ATTR_2(in0_max, S_IRUGO | S_IWUSR, show_in, store_in,
+ 0, 2);
+static SENSOR_DEVICE_ATTR_2(in0_alarm, S_IRUGO, show_alarm, NULL, 0x1e, 3);
+static SENSOR_DEVICE_ATTR_2(in0_beep, S_IRUGO | S_IWUSR, show_beep, store_beep,
+ 0x5a, 3);
+
+static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 1, 0);
+
+static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 2, 0);
+static SENSOR_DEVICE_ATTR_2(in2_min, S_IRUGO | S_IWUSR, show_in, store_in,
+ 2, 1);
+static SENSOR_DEVICE_ATTR_2(in2_max, S_IRUGO | S_IWUSR, show_in, store_in,
+ 2, 2);
+static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, show_alarm, NULL, 0x1e, 0);
+static SENSOR_DEVICE_ATTR_2(in2_beep, S_IRUGO | S_IWUSR, show_beep, store_beep,
+ 0x5a, 0);
+
+static SENSOR_DEVICE_ATTR_2(in3_input, S_IRUGO, show_in, NULL, 3, 0);
+static SENSOR_DEVICE_ATTR_2(in3_min, S_IRUGO | S_IWUSR, show_in, store_in,
+ 3, 1);
+static SENSOR_DEVICE_ATTR_2(in3_max, S_IRUGO | S_IWUSR, show_in, store_in,
+ 3, 2);
+static SENSOR_DEVICE_ATTR_2(in3_alarm, S_IRUGO, show_alarm, NULL, 0x1e, 1);
+static SENSOR_DEVICE_ATTR_2(in3_beep, S_IRUGO | S_IWUSR, show_beep, store_beep,
+ 0x5a, 1);
+
+static SENSOR_DEVICE_ATTR_2(in4_input, S_IRUGO, show_in, NULL, 4, 0);
+static SENSOR_DEVICE_ATTR_2(in4_min, S_IRUGO | S_IWUSR, show_in, store_in,
+ 4, 1);
+static SENSOR_DEVICE_ATTR_2(in4_max, S_IRUGO | S_IWUSR, show_in, store_in,
+ 4, 2);
+static SENSOR_DEVICE_ATTR_2(in4_alarm, S_IRUGO, show_alarm, NULL, 0x1e, 2);
+static SENSOR_DEVICE_ATTR_2(in4_beep, S_IRUGO | S_IWUSR, show_beep, store_beep,
+ 0x5a, 2);
+
+static struct attribute *nct7802_in_attrs[] = {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in0_alarm.dev_attr.attr,
+ &sensor_dev_attr_in0_beep.dev_attr.attr,
+
+ &sensor_dev_attr_in1_input.dev_attr.attr, /* 5 */
+
+ &sensor_dev_attr_in2_input.dev_attr.attr, /* 6 */
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in2_alarm.dev_attr.attr,
+ &sensor_dev_attr_in2_beep.dev_attr.attr,
+
+ &sensor_dev_attr_in3_input.dev_attr.attr, /* 11 */
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in3_alarm.dev_attr.attr,
+ &sensor_dev_attr_in3_beep.dev_attr.attr,
+
+ &sensor_dev_attr_in4_input.dev_attr.attr, /* 17 */
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in4_alarm.dev_attr.attr,
+ &sensor_dev_attr_in4_beep.dev_attr.attr,
+
+ NULL,
+};
+
+static umode_t nct7802_in_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ unsigned int reg;
+ int err;
+
+ if (index < 6) /* VCC, VCORE */
+ return attr->mode;
+
+ err = regmap_read(data->regmap, REG_MODE, ®);
+ if (err < 0)
+ return 0;
+
+ if (index >= 6 && index < 11 && (reg & 0x03) != 0x03) /* VSEN1 */
+ return 0;
+ if (index >= 11 && index < 17 && (reg & 0x0c) != 0x0c) /* VSEN2 */
+ return 0;
+ if (index >= 17 && (reg & 0x30) != 0x30) /* VSEN3 */
+ return 0;
+
+ return attr->mode;
+}
+
+static struct attribute_group nct7802_in_group = {
+ .attrs = nct7802_in_attrs,
+ .is_visible = nct7802_in_is_visible,
+};
+
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0x10);
+static SENSOR_DEVICE_ATTR_2(fan1_min, S_IRUGO | S_IWUSR, show_fan_min,
+ store_fan_min, 0x49, 0x4c);
+static SENSOR_DEVICE_ATTR_2(fan1_alarm, S_IRUGO, show_alarm, NULL, 0x1a, 0);
+static SENSOR_DEVICE_ATTR_2(fan1_beep, S_IRUGO | S_IWUSR, show_beep, store_beep,
+ 0x5b, 0);
+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 0x11);
+static SENSOR_DEVICE_ATTR_2(fan2_min, S_IRUGO | S_IWUSR, show_fan_min,
+ store_fan_min, 0x4a, 0x4d);
+static SENSOR_DEVICE_ATTR_2(fan2_alarm, S_IRUGO, show_alarm, NULL, 0x1a, 1);
+static SENSOR_DEVICE_ATTR_2(fan2_beep, S_IRUGO | S_IWUSR, show_beep, store_beep,
+ 0x5b, 1);
+static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 0x12);
+static SENSOR_DEVICE_ATTR_2(fan3_min, S_IRUGO | S_IWUSR, show_fan_min,
+ store_fan_min, 0x4b, 0x4e);
+static SENSOR_DEVICE_ATTR_2(fan3_alarm, S_IRUGO, show_alarm, NULL, 0x1a, 2);
+static SENSOR_DEVICE_ATTR_2(fan3_beep, S_IRUGO | S_IWUSR, show_beep, store_beep,
+ 0x5b, 2);
+
+static struct attribute *nct7802_fan_attrs[] = {
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan1_beep.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan2_beep.dev_attr.attr,
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_min.dev_attr.attr,
+ &sensor_dev_attr_fan3_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan3_beep.dev_attr.attr,
+
+ NULL
+};
+
+static umode_t nct7802_fan_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct nct7802_data *data = dev_get_drvdata(dev);
+ int fan = index / 4; /* 4 attributes per fan */
+ unsigned int reg;
+ int err;
+
+ err = regmap_read(data->regmap, REG_FAN_ENABLE, ®);
+ if (err < 0 || !(reg & (1 << fan)))
+ return 0;
+
+ return attr->mode;
+}
+
+static struct attribute_group nct7802_fan_group = {
+ .attrs = nct7802_fan_attrs,
+ .is_visible = nct7802_fan_is_visible,
+};
+
+static const struct attribute_group *nct7802_groups[] = {
+ &nct7802_temp_group,
+ &nct7802_in_group,
+ &nct7802_fan_group,
+ NULL
+};
+
+static int nct7802_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ int reg;
+
+ /*
+ * Chip identification registers are only available in bank 0,
+ * so only attempt chip detection if bank 0 is selected
+ */
+ reg = i2c_smbus_read_byte_data(client, REG_BANK);
+ if (reg != 0x00)
+ return -ENODEV;
+
+ reg = i2c_smbus_read_byte_data(client, REG_VENDOR_ID);
+ if (reg != 0x50)
+ return -ENODEV;
+
+ reg = i2c_smbus_read_byte_data(client, REG_CHIP_ID);
+ if (reg != 0xc3)
+ return -ENODEV;
+
+ reg = i2c_smbus_read_byte_data(client, REG_VERSION_ID);
+ if (reg < 0 || (reg & 0xf0) != 0x20)
+ return -ENODEV;
+
+ /* Also validate lower bits of voltage and temperature registers */
+ reg = i2c_smbus_read_byte_data(client, REG_TEMP_LSB);
+ if (reg < 0 || (reg & 0x1f))
+ return -ENODEV;
+
+ reg = i2c_smbus_read_byte_data(client, REG_TEMP_PECI_LSB);
+ if (reg < 0 || (reg & 0x3f))
+ return -ENODEV;
+
+ reg = i2c_smbus_read_byte_data(client, REG_VOLTAGE_LOW);
+ if (reg < 0 || (reg & 0x3f))
+ return -ENODEV;
+
+ strlcpy(info->type, "nct7802", I2C_NAME_SIZE);
+ return 0;
+}
+
+static bool nct7802_regmap_is_volatile(struct device *dev, unsigned int reg)
+{
+ return reg != REG_BANK && reg <= 0x20;
+}
+
+static struct regmap_config nct7802_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = nct7802_regmap_is_volatile,
+};
+
+static int nct7802_init_chip(struct nct7802_data *data)
+{
+ int err;
+
+ /* Enable ADC */
+ err = regmap_update_bits(data->regmap, REG_START, 0x01, 0x01);
+ if (err)
+ return err;
+
+ /* Enable local temperature sensor */
+ err = regmap_update_bits(data->regmap, REG_MODE, 0x40, 0x40);
+ if (err)
+ return err;
+
+ /* Enable Vcore and VCC voltage monitoring */
+ return regmap_update_bits(data->regmap, REG_VMON_ENABLE, 0x03, 0x03);
+}
+
+static int nct7802_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct nct7802_data *data;
+ struct device *hwmon_dev;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ data->regmap = devm_regmap_init_i2c(client, &nct7802_regmap_config);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+
+ mutex_init(&data->access_lock);
+
+ ret = nct7802_init_chip(data);
+ if (ret < 0)
+ return ret;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ data,
+ nct7802_groups);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const unsigned short nct7802_address_list[] = {
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END
+};
+
+static const struct i2c_device_id nct7802_idtable[] = {
+ { "nct7802", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, nct7802_idtable);
+
+static struct i2c_driver nct7802_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = DRVNAME,
+ },
+ .detect = nct7802_detect,
+ .probe = nct7802_probe,
+ .id_table = nct7802_idtable,
+ .address_list = nct7802_address_list,
+};
+
+module_i2c_driver(nct7802_driver);
+
+MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
+MODULE_DESCRIPTION("NCT7802Y Hardware Monitoring Driver");
+MODULE_LICENSE("GPL v2");
be called lm25066.
config SENSORS_LTC2978
- tristate "Linear Technologies LTC2974, LTC2978, LTC3880, and LTC3883"
+ tristate "Linear Technologies LTC2978 and compatibles"
default n
help
If you say yes here you get hardware monitoring support for Linear
- Technology LTC2974, LTC2978, LTC3880, and LTC3883.
+ Technology LTC2974, LTC2977, LTC2978, LTC3880, LTC3883, and LTM4676.
This driver can also be built as a module. If so, the module will
be called ltc2978.
+config SENSORS_LTC2978_REGULATOR
+ boolean "Regulator support for LTC2978 and compatibles"
+ depends on SENSORS_LTC2978 && REGULATOR
+ help
+ If you say yes here you get regulator support for Linear
+ Technology LTC2974, LTC2977, LTC2978, LTC3880, LTC3883, and LTM4676.
+
config SENSORS_MAX16064
tristate "Maxim MAX16064"
default n
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/i2c.h>
+#include <linux/regulator/driver.h>
#include "pmbus.h"
enum chips { ltc2974, ltc2977, ltc2978, ltc3880, ltc3883, ltm4676 };
};
MODULE_DEVICE_TABLE(i2c, ltc2978_id);
+#if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR)
+static const struct regulator_desc ltc2978_reg_desc[] = {
+ PMBUS_REGULATOR("vout", 0),
+ PMBUS_REGULATOR("vout", 1),
+ PMBUS_REGULATOR("vout", 2),
+ PMBUS_REGULATOR("vout", 3),
+ PMBUS_REGULATOR("vout", 4),
+ PMBUS_REGULATOR("vout", 5),
+ PMBUS_REGULATOR("vout", 6),
+ PMBUS_REGULATOR("vout", 7),
+};
+#endif /* CONFIG_SENSORS_LTC2978_REGULATOR */
+
static int ltc2978_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
default:
return -ENODEV;
}
+
+#if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR)
+ info->num_regulators = info->pages;
+ info->reg_desc = ltc2978_reg_desc;
+ if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc)) {
+ dev_err(&client->dev, "num_regulators too large!");
+ info->num_regulators = ARRAY_SIZE(ltc2978_reg_desc);
+ }
+#endif
+
return pmbus_do_probe(client, id, info);
}
-/* This is the driver that will be inserted */
+#ifdef CONFIG_OF
+static const struct of_device_id ltc2978_of_match[] = {
+ { .compatible = "lltc,ltc2974" },
+ { .compatible = "lltc,ltc2977" },
+ { .compatible = "lltc,ltc2978" },
+ { .compatible = "lltc,ltc3880" },
+ { .compatible = "lltc,ltc3883" },
+ { .compatible = "lltc,ltm4676" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ltc2978_of_match);
+#endif
+
static struct i2c_driver ltc2978_driver = {
.driver = {
.name = "ltc2978",
+ .of_match_table = of_match_ptr(ltc2978_of_match),
},
.probe = ltc2978_probe,
.remove = pmbus_do_remove,
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include <linux/regulator/driver.h>
+
#ifndef PMBUS_H
#define PMBUS_H
#define PMBUS_VIRT_VMON_OV_FAULT_LIMIT (PMBUS_VIRT_BASE + 34)
#define PMBUS_VIRT_STATUS_VMON (PMBUS_VIRT_BASE + 35)
+/*
+ * OPERATION
+ */
+#define PB_OPERATION_CONTROL_ON (1<<7)
+
/*
* CAPABILITY
*/
*/
int (*identify)(struct i2c_client *client,
struct pmbus_driver_info *info);
+
+ /* Regulator functionality, if supported by this chip driver. */
+ int num_regulators;
+ const struct regulator_desc *reg_desc;
};
+/* Regulator ops */
+
+extern struct regulator_ops pmbus_regulator_ops;
+
+/* Macro for filling in array of struct regulator_desc */
+#define PMBUS_REGULATOR(_name, _id) \
+ [_id] = { \
+ .name = (_name # _id), \
+ .id = (_id), \
+ .of_match = of_match_ptr(_name # _id), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .ops = &pmbus_regulator_ops, \
+ .owner = THIS_MODULE, \
+ }
+
/* Function declarations */
void pmbus_clear_cache(struct i2c_client *client);
int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word);
int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg);
int pmbus_write_byte(struct i2c_client *client, int page, u8 value);
+int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg,
+ u8 value);
+int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
+ u8 mask, u8 value);
void pmbus_clear_faults(struct i2c_client *client);
bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg);
bool pmbus_check_word_register(struct i2c_client *client, int page, int reg);
#include <linux/hwmon-sysfs.h>
#include <linux/jiffies.h>
#include <linux/i2c/pmbus.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
#include "pmbus.h"
/*
}
EXPORT_SYMBOL_GPL(pmbus_read_byte_data);
+int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value)
+{
+ int rv;
+
+ rv = pmbus_set_page(client, page);
+ if (rv < 0)
+ return rv;
+
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+EXPORT_SYMBOL_GPL(pmbus_write_byte_data);
+
+int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
+ u8 mask, u8 value)
+{
+ unsigned int tmp;
+ int rv;
+
+ rv = pmbus_read_byte_data(client, page, reg);
+ if (rv < 0)
+ return rv;
+
+ tmp = (rv & ~mask) | (value & mask);
+
+ if (tmp != rv)
+ rv = pmbus_write_byte_data(client, page, reg, tmp);
+
+ return rv;
+}
+EXPORT_SYMBOL_GPL(pmbus_update_byte_data);
+
/*
* _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if
* a device specific mapping function exists and calls it if necessary.
return 0;
}
+#if IS_ENABLED(CONFIG_REGULATOR)
+static int pmbus_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct device *dev = rdev_get_dev(rdev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ u8 page = rdev_get_id(rdev);
+ int ret;
+
+ ret = pmbus_read_byte_data(client, page, PMBUS_OPERATION);
+ if (ret < 0)
+ return ret;
+
+ return !!(ret & PB_OPERATION_CONTROL_ON);
+}
+
+static int _pmbus_regulator_on_off(struct regulator_dev *rdev, bool enable)
+{
+ struct device *dev = rdev_get_dev(rdev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ u8 page = rdev_get_id(rdev);
+
+ return pmbus_update_byte_data(client, page, PMBUS_OPERATION,
+ PB_OPERATION_CONTROL_ON,
+ enable ? PB_OPERATION_CONTROL_ON : 0);
+}
+
+static int pmbus_regulator_enable(struct regulator_dev *rdev)
+{
+ return _pmbus_regulator_on_off(rdev, 1);
+}
+
+static int pmbus_regulator_disable(struct regulator_dev *rdev)
+{
+ return _pmbus_regulator_on_off(rdev, 0);
+}
+
+struct regulator_ops pmbus_regulator_ops = {
+ .enable = pmbus_regulator_enable,
+ .disable = pmbus_regulator_disable,
+ .is_enabled = pmbus_regulator_is_enabled,
+};
+EXPORT_SYMBOL_GPL(pmbus_regulator_ops);
+
+static int pmbus_regulator_register(struct pmbus_data *data)
+{
+ struct device *dev = data->dev;
+ const struct pmbus_driver_info *info = data->info;
+ const struct pmbus_platform_data *pdata = dev_get_platdata(dev);
+ struct regulator_dev *rdev;
+ int i;
+
+ for (i = 0; i < info->num_regulators; i++) {
+ struct regulator_config config = { };
+
+ config.dev = dev;
+ config.driver_data = data;
+
+ if (pdata && pdata->reg_init_data)
+ config.init_data = &pdata->reg_init_data[i];
+
+ rdev = devm_regulator_register(dev, &info->reg_desc[i],
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(dev, "Failed to register %s regulator\n",
+ info->reg_desc[i].name);
+ return PTR_ERR(rdev);
+ }
+ }
+
+ return 0;
+}
+#else
+static int pmbus_regulator_register(struct pmbus_data *data)
+{
+ return 0;
+}
+#endif
+
int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id,
struct pmbus_driver_info *info)
{
dev_err(dev, "Failed to register hwmon device\n");
goto out_kfree;
}
+
+ ret = pmbus_regulator_register(data);
+ if (ret)
+ goto out_unregister;
+
return 0;
+out_unregister:
+ hwmon_device_unregister(data->hwmon_dev);
out_kfree:
kfree(data->group.attrs);
return ret;
static int pwm_fan_resume(struct device *dev)
{
struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
+ unsigned long duty;
+ int ret;
- if (ctx->pwm_value)
- return pwm_enable(ctx->pwm);
- return 0;
+ if (ctx->pwm_value == 0)
+ return 0;
+
+ duty = DIV_ROUND_UP(ctx->pwm_value * (ctx->pwm->period - 1), MAX_PWM);
+ ret = pwm_config(ctx->pwm, duty, ctx->pwm->period);
+ if (ret)
+ return ret;
+ return pwm_enable(ctx->pwm);
}
#endif
#include <linux/sysfs.h>
/* Addresses to scan */
-static const unsigned short normal_i2c[] = { 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
+static const unsigned short normal_i2c[] = { 0x37, 0x48, 0x49, 0x4a, 0x4c, 0x4d,
+ 0x4e, 0x4f, I2C_CLIENT_END };
-enum chips { tmp401, tmp411, tmp431, tmp432 };
+enum chips { tmp401, tmp411, tmp431, tmp432, tmp435 };
/*
* The TMP401 registers, note some registers have different addresses for
#define TMP411C_DEVICE_ID 0x10
#define TMP431_DEVICE_ID 0x31
#define TMP432_DEVICE_ID 0x32
+#define TMP435_DEVICE_ID 0x35
/*
* Driver data (common to all clients)
{ "tmp411", tmp411 },
{ "tmp431", tmp431 },
{ "tmp432", tmp432 },
+ { "tmp435", tmp435 },
{ }
};
MODULE_DEVICE_TABLE(i2c, tmp401_id);
* Begin non sysfs callback code (aka Real code)
*/
-static void tmp401_init_client(struct tmp401_data *data,
- struct i2c_client *client)
+static int tmp401_init_client(struct tmp401_data *data,
+ struct i2c_client *client)
{
- int config, config_orig;
+ int config, config_orig, status = 0;
/* Set the conversion rate to 2 Hz */
i2c_smbus_write_byte_data(client, TMP401_CONVERSION_RATE_WRITE, 5);
/* Start conversions (disable shutdown if necessary) */
config = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ);
- if (config < 0) {
- dev_warn(&client->dev, "Initialization failed!\n");
- return;
- }
+ if (config < 0)
+ return config;
config_orig = config;
config &= ~TMP401_CONFIG_SHUTDOWN;
if (config != config_orig)
- i2c_smbus_write_byte_data(client, TMP401_CONFIG_WRITE, config);
+ status = i2c_smbus_write_byte_data(client,
+ TMP401_CONFIG_WRITE,
+ config);
+
+ return status;
}
static int tmp401_detect(struct i2c_client *client,
kind = tmp411;
break;
case TMP431_DEVICE_ID:
- if (client->addr == 0x4e)
+ if (client->addr != 0x4c && client->addr != 0x4d)
return -ENODEV;
kind = tmp431;
break;
case TMP432_DEVICE_ID:
- if (client->addr == 0x4e)
+ if (client->addr != 0x4c && client->addr != 0x4d)
return -ENODEV;
kind = tmp432;
break;
+ case TMP435_DEVICE_ID:
+ kind = tmp435;
+ break;
default:
return -ENODEV;
}
static int tmp401_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- const char *names[] = { "TMP401", "TMP411", "TMP431", "TMP432" };
+ static const char * const names[] = {
+ "TMP401", "TMP411", "TMP431", "TMP432", "TMP435"
+ };
struct device *dev = &client->dev;
struct device *hwmon_dev;
struct tmp401_data *data;
- int groups = 0;
+ int groups = 0, status;
data = devm_kzalloc(dev, sizeof(struct tmp401_data), GFP_KERNEL);
if (!data)
data->kind = id->driver_data;
/* Initialize the TMP401 chip */
- tmp401_init_client(data, client);
+ status = tmp401_init_client(data, client);
+ if (status < 0)
+ return status;
/* Register sysfs hooks */
data->groups[groups++] = &tmp401_group;
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- MA 02110-1301 USA.
* ------------------------------------------------------------------------- */
/* With some changes from Frodo Looijaard <frodol@dds.nl>, Kyösti Mälkki
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA.
*/
#include <linux/kernel.h>
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA.
- *
* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and
* Frodo Looijaard <frodol@dds.nl>, and also from Martin Bailey
* <mbailey@littlefeet-inc.com>
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- MA 02110-1301 USA. */
+ GNU General Public License for more details. */
/* -------------------------------------------------------------------- */
/* With some changes from Frodo Looijaard <frodol@dds.nl> */
This driver can also be built as a module. If so, the module
will be called i2c-diolan-u2c.
+config I2C_DLN2
+ tristate "Diolan DLN-2 USB I2C adapter"
+ depends on MFD_DLN2
+ help
+ If you say yes to this option, support will be included for Diolan
+ DLN2, a USB to I2C interface.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-dln2.
+
config I2C_PARPORT
tristate "Parallel port adapter"
depends on PARPORT
# External I2C/SMBus adapter drivers
obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
+obj-$(CONFIG_I2C_DLN2) += i2c-dln2.o
obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o
obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o
obj-$(CONFIG_I2C_ROBOTFUZZ_OSIF) += i2c-robotfuzz-osif.o
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
}
}
- ret = wait_for_completion_io_timeout(&dev->cmd_complete,
+ ret = wait_for_completion_timeout(&dev->cmd_complete,
dev->adapter.timeout);
if (ret == 0) {
dev_err(dev->dev, "controller timed out\n");
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/delay.h>
#define CDNS_I2C_DIVA_MAX 4
#define CDNS_I2C_DIVB_MAX 64
+#define CDNS_I2C_TIMEOUT_MAX 0xFF
+
#define cdns_i2c_readreg(offset) readl_relaxed(id->membase + offset)
#define cdns_i2c_writereg(val, offset) writel_relaxed(val, id->membase + offset)
goto err_clk_dis;
}
+ /*
+ * Cadence I2C controller has a bug wherein it generates
+ * invalid read transaction after HW timeout in master receiver mode.
+ * HW timeout is not used by this driver and the interrupt is disabled.
+ * But the feature itself cannot be disabled. Hence maximum value
+ * is written to this register to reduce the chances of error.
+ */
+ cdns_i2c_writereg(CDNS_I2C_TIMEOUT_MAX, CDNS_I2C_TIME_OUT_OFFSET);
+
dev_info(&pdev->dev, "%u kHz mmio %08lx irq %d\n",
id->i2c_clk / 1000, (unsigned long)r_mem->start, id->irq);
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* ----------------------------------------------------------------------------
*
*/
if (dev->cmd_err & DAVINCI_I2C_STR_NACK) {
if (msg->flags & I2C_M_IGNORE_NAK)
return msg->len;
- if (stop) {
- w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG);
- w |= DAVINCI_I2C_MDR_STP;
- davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w);
- }
+ w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG);
+ w |= DAVINCI_I2C_MDR_STP;
+ davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w);
return -EREMOTEIO;
}
return -EIO;
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* ----------------------------------------------------------------------------
*
*/
}
/* Configure Tx/Rx FIFO threshold levels */
- dw_writel(dev, dev->tx_fifo_depth - 1, DW_IC_TX_TL);
+ dw_writel(dev, dev->tx_fifo_depth / 2, DW_IC_TX_TL);
dw_writel(dev, 0, DW_IC_RX_TL);
/* configure the i2c master */
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* ----------------------------------------------------------------------------
*
*/
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* ----------------------------------------------------------------------------
*
*/
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* ----------------------------------------------------------------------------
*
*/
--- /dev/null
+/*
+ * Driver for the Diolan DLN-2 USB-I2C adapter
+ *
+ * Copyright (c) 2014 Intel Corporation
+ *
+ * Derived from:
+ * i2c-diolan-u2c.c
+ * Copyright (c) 2010-2011 Ericsson AB
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/dln2.h>
+
+#define DLN2_I2C_MODULE_ID 0x03
+#define DLN2_I2C_CMD(cmd) DLN2_CMD(cmd, DLN2_I2C_MODULE_ID)
+
+/* I2C commands */
+#define DLN2_I2C_GET_PORT_COUNT DLN2_I2C_CMD(0x00)
+#define DLN2_I2C_ENABLE DLN2_I2C_CMD(0x01)
+#define DLN2_I2C_DISABLE DLN2_I2C_CMD(0x02)
+#define DLN2_I2C_IS_ENABLED DLN2_I2C_CMD(0x03)
+#define DLN2_I2C_WRITE DLN2_I2C_CMD(0x06)
+#define DLN2_I2C_READ DLN2_I2C_CMD(0x07)
+#define DLN2_I2C_SCAN_DEVICES DLN2_I2C_CMD(0x08)
+#define DLN2_I2C_PULLUP_ENABLE DLN2_I2C_CMD(0x09)
+#define DLN2_I2C_PULLUP_DISABLE DLN2_I2C_CMD(0x0A)
+#define DLN2_I2C_PULLUP_IS_ENABLED DLN2_I2C_CMD(0x0B)
+#define DLN2_I2C_TRANSFER DLN2_I2C_CMD(0x0C)
+#define DLN2_I2C_SET_MAX_REPLY_COUNT DLN2_I2C_CMD(0x0D)
+#define DLN2_I2C_GET_MAX_REPLY_COUNT DLN2_I2C_CMD(0x0E)
+
+#define DLN2_I2C_MAX_XFER_SIZE 256
+#define DLN2_I2C_BUF_SIZE (DLN2_I2C_MAX_XFER_SIZE + 16)
+
+struct dln2_i2c {
+ struct platform_device *pdev;
+ struct i2c_adapter adapter;
+ u8 port;
+ /*
+ * Buffer to hold the packet for read or write transfers. One is enough
+ * since we can't have multiple transfers in parallel on the i2c bus.
+ */
+ void *buf;
+};
+
+static int dln2_i2c_enable(struct dln2_i2c *dln2, bool enable)
+{
+ u16 cmd;
+ struct {
+ u8 port;
+ } tx;
+
+ tx.port = dln2->port;
+
+ if (enable)
+ cmd = DLN2_I2C_ENABLE;
+ else
+ cmd = DLN2_I2C_DISABLE;
+
+ return dln2_transfer_tx(dln2->pdev, cmd, &tx, sizeof(tx));
+}
+
+static int dln2_i2c_write(struct dln2_i2c *dln2, u8 addr,
+ u8 *data, u16 data_len)
+{
+ int ret;
+ struct {
+ u8 port;
+ u8 addr;
+ u8 mem_addr_len;
+ __le32 mem_addr;
+ __le16 buf_len;
+ u8 buf[DLN2_I2C_MAX_XFER_SIZE];
+ } __packed *tx = dln2->buf;
+ unsigned len;
+
+ BUILD_BUG_ON(sizeof(*tx) > DLN2_I2C_BUF_SIZE);
+
+ tx->port = dln2->port;
+ tx->addr = addr;
+ tx->mem_addr_len = 0;
+ tx->mem_addr = 0;
+ tx->buf_len = cpu_to_le16(data_len);
+ memcpy(tx->buf, data, data_len);
+
+ len = sizeof(*tx) + data_len - DLN2_I2C_MAX_XFER_SIZE;
+ ret = dln2_transfer_tx(dln2->pdev, DLN2_I2C_WRITE, tx, len);
+ if (ret < 0)
+ return ret;
+
+ return data_len;
+}
+
+static int dln2_i2c_read(struct dln2_i2c *dln2, u16 addr, u8 *data,
+ u16 data_len)
+{
+ int ret;
+ struct {
+ u8 port;
+ u8 addr;
+ u8 mem_addr_len;
+ __le32 mem_addr;
+ __le16 buf_len;
+ } __packed tx;
+ struct {
+ __le16 buf_len;
+ u8 buf[DLN2_I2C_MAX_XFER_SIZE];
+ } __packed *rx = dln2->buf;
+ unsigned rx_len = sizeof(*rx);
+
+ BUILD_BUG_ON(sizeof(*rx) > DLN2_I2C_BUF_SIZE);
+
+ tx.port = dln2->port;
+ tx.addr = addr;
+ tx.mem_addr_len = 0;
+ tx.mem_addr = 0;
+ tx.buf_len = cpu_to_le16(data_len);
+
+ ret = dln2_transfer(dln2->pdev, DLN2_I2C_READ, &tx, sizeof(tx),
+ rx, &rx_len);
+ if (ret < 0)
+ return ret;
+ if (rx_len < sizeof(rx->buf_len) + data_len)
+ return -EPROTO;
+ if (le16_to_cpu(rx->buf_len) != data_len)
+ return -EPROTO;
+
+ memcpy(data, rx->buf, data_len);
+
+ return data_len;
+}
+
+static int dln2_i2c_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int num)
+{
+ struct dln2_i2c *dln2 = i2c_get_adapdata(adapter);
+ struct i2c_msg *pmsg;
+ struct device *dev = &dln2->adapter.dev;
+ int i;
+
+ for (i = 0; i < num; i++) {
+ int ret;
+
+ pmsg = &msgs[i];
+
+ if (pmsg->len > DLN2_I2C_MAX_XFER_SIZE) {
+ dev_warn(dev, "maximum transfer size exceeded\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (pmsg->flags & I2C_M_RD) {
+ ret = dln2_i2c_read(dln2, pmsg->addr, pmsg->buf,
+ pmsg->len);
+ if (ret < 0)
+ return ret;
+
+ pmsg->len = ret;
+ } else {
+ ret = dln2_i2c_write(dln2, pmsg->addr, pmsg->buf,
+ pmsg->len);
+ if (ret != pmsg->len)
+ return -EPROTO;
+ }
+ }
+
+ return num;
+}
+
+static u32 dln2_i2c_func(struct i2c_adapter *a)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
+ I2C_FUNC_SMBUS_I2C_BLOCK;
+}
+
+static const struct i2c_algorithm dln2_i2c_usb_algorithm = {
+ .master_xfer = dln2_i2c_xfer,
+ .functionality = dln2_i2c_func,
+};
+
+static int dln2_i2c_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct dln2_i2c *dln2;
+ struct device *dev = &pdev->dev;
+ struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev);
+
+ dln2 = devm_kzalloc(dev, sizeof(*dln2), GFP_KERNEL);
+ if (!dln2)
+ return -ENOMEM;
+
+ dln2->buf = devm_kmalloc(dev, DLN2_I2C_BUF_SIZE, GFP_KERNEL);
+ if (!dln2->buf)
+ return -ENOMEM;
+
+ dln2->pdev = pdev;
+ dln2->port = pdata->port;
+
+ /* setup i2c adapter description */
+ dln2->adapter.owner = THIS_MODULE;
+ dln2->adapter.class = I2C_CLASS_HWMON;
+ dln2->adapter.algo = &dln2_i2c_usb_algorithm;
+ dln2->adapter.dev.parent = dev;
+ i2c_set_adapdata(&dln2->adapter, dln2);
+ snprintf(dln2->adapter.name, sizeof(dln2->adapter.name), "%s-%s-%d",
+ "dln2-i2c", dev_name(pdev->dev.parent), dln2->port);
+
+ platform_set_drvdata(pdev, dln2);
+
+ /* initialize the i2c interface */
+ ret = dln2_i2c_enable(dln2, true);
+ if (ret < 0) {
+ dev_err(dev, "failed to initialize adapter: %d\n", ret);
+ return ret;
+ }
+
+ /* and finally attach to i2c layer */
+ ret = i2c_add_adapter(&dln2->adapter);
+ if (ret < 0) {
+ dev_err(dev, "failed to add I2C adapter: %d\n", ret);
+ goto out_disable;
+ }
+
+ return 0;
+
+out_disable:
+ dln2_i2c_enable(dln2, false);
+
+ return ret;
+}
+
+static int dln2_i2c_remove(struct platform_device *pdev)
+{
+ struct dln2_i2c *dln2 = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&dln2->adapter);
+ dln2_i2c_enable(dln2, false);
+
+ return 0;
+}
+
+static struct platform_driver dln2_i2c_driver = {
+ .driver.name = "dln2-i2c",
+ .probe = dln2_i2c_probe,
+ .remove = dln2_i2c_remove,
+};
+
+module_platform_driver(dln2_i2c_driver);
+
+MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@intel.com>");
+MODULE_DESCRIPTION("Driver for the Diolan DLN2 I2C master interface");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:dln2-i2c");
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/module.h>
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+ GNU General Public License for more details. */
/* ------------------------------------------------------------------------- */
/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
- *
* Author:
* Darius Augulis, Teltonika Inc.
*
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+ GNU General Public License for more details. */
/* ------------------------------------------------------------------------- */
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* The full GNU General Public License is included in this distribution
* in the file called LICENSE.GPL.
*
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
if (stat & OMAP_I2C_STAT_NACK) {
err |= OMAP_I2C_STAT_NACK;
omap_i2c_ack_stat(dev, OMAP_I2C_STAT_NACK);
- break;
}
if (stat & OMAP_I2C_STAT_AL) {
dev_err(dev->dev, "Arbitration lost\n");
err |= OMAP_I2C_STAT_AL;
omap_i2c_ack_stat(dev, OMAP_I2C_STAT_AL);
- break;
}
/*
if (dev->fifo_size)
num_bytes = dev->buf_len;
- omap_i2c_receive_data(dev, num_bytes, true);
-
- if (dev->errata & I2C_OMAP_ERRATA_I207)
+ if (dev->errata & I2C_OMAP_ERRATA_I207) {
i2c_omap_errata_i207(dev, stat);
+ num_bytes = (omap_i2c_read_reg(dev,
+ OMAP_I2C_BUFSTAT_REG) >> 8) & 0x3F;
+ }
+ omap_i2c_receive_data(dev, num_bytes, true);
omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RDR);
continue;
}
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* ------------------------------------------------------------------------ */
#include <linux/kernel.h>
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* ------------------------------------------------------------------------ */
#include <linux/kernel.h>
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* ------------------------------------------------------------------------ */
#define PORT_DATA 0
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
*/
#include <linux/module.h>
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/kernel.h>
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* Note: we assume there can only be one SIS5595 with one SMBus interface */
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/delay.h>
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
*
* This code was implemented by Mocean Laboratories AB when porting linux
* to the automotive development board Russellville. The copyright holder
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA.
*/
#include <linux/kernel.h>
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- MA 02110-1301 USA. */
+ GNU General Public License for more details. */
/* ------------------------------------------------------------------------- */
/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi>.
status = driver->remove(client);
}
+ if (dev->of_node)
+ irq_dispose_mapping(client->irq);
+
dev_pm_domain_detach(&client->dev, true);
return status;
}
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA.
*/
#include <linux/rwsem.h>
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- MA 02110-1301 USA.
*/
/* Note that this is a complete rewrite of Simon Vogl's i2c-dev module.
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA.
*/
#include <linux/kernel.h>
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define DEBUG 1
#define BMC150_ACCEL_REG_INT_STATUS_2 0x0B
#define BMC150_ACCEL_ANY_MOTION_MASK 0x07
+#define BMC150_ACCEL_ANY_MOTION_BIT_X BIT(0)
+#define BMC150_ACCEL_ANY_MOTION_BIT_Y BIT(1)
+#define BMC150_ACCEL_ANY_MOTION_BIT_Z BIT(2)
#define BMC150_ACCEL_ANY_MOTION_BIT_SIGN BIT(3)
#define BMC150_ACCEL_REG_PMU_LPW 0x11
#define BMC150_ACCEL_SLOPE_THRES_MASK 0xFF
/* Slope duration in terms of number of samples */
-#define BMC150_ACCEL_DEF_SLOPE_DURATION 2
+#define BMC150_ACCEL_DEF_SLOPE_DURATION 1
/* in terms of multiples of g's/LSB, based on range */
-#define BMC150_ACCEL_DEF_SLOPE_THRESHOLD 5
+#define BMC150_ACCEL_DEF_SLOPE_THRESHOLD 1
#define BMC150_ACCEL_REG_XOUT_L 0x02
if (ret < 0) {
dev_err(&data->client->dev,
"Failed: bmc150_accel_set_power_state for %d\n", on);
+ if (on)
+ pm_runtime_put_noidle(&data->client->dev);
+
return ret;
}
ret = bmc150_accel_setup_any_motion_interrupt(data, state);
if (ret < 0) {
+ bmc150_accel_set_power_state(data, false);
mutex_unlock(&data->mutex);
return ret;
}
static const struct iio_event_spec bmc150_accel_event = {
.type = IIO_EV_TYPE_ROC,
- .dir = IIO_EV_DIR_RISING | IIO_EV_DIR_FALLING,
+ .dir = IIO_EV_DIR_EITHER,
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_ENABLE) |
BIT(IIO_EV_INFO_PERIOD)
else
ret = bmc150_accel_setup_new_data_interrupt(data, state);
if (ret < 0) {
+ bmc150_accel_set_power_state(data, false);
mutex_unlock(&data->mutex);
return ret;
}
else
dir = IIO_EV_DIR_RISING;
- if (ret & BMC150_ACCEL_ANY_MOTION_MASK)
+ if (ret & BMC150_ACCEL_ANY_MOTION_BIT_X)
+ iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_X,
+ IIO_EV_TYPE_ROC,
+ dir),
+ data->timestamp);
+ if (ret & BMC150_ACCEL_ANY_MOTION_BIT_Y)
iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL,
0,
- IIO_MOD_X_OR_Y_OR_Z,
+ IIO_MOD_Y,
IIO_EV_TYPE_ROC,
- IIO_EV_DIR_EITHER),
+ dir),
+ data->timestamp);
+ if (ret & BMC150_ACCEL_ANY_MOTION_BIT_Z)
+ iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_Z,
+ IIO_EV_TYPE_ROC,
+ dir),
data->timestamp);
ack_intr_status:
if (!data->dready_trigger_on)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct bmc150_accel_data *data = iio_priv(indio_dev);
+ int ret;
dev_dbg(&data->client->dev, __func__);
+ ret = bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_SUSPEND, 0);
+ if (ret < 0)
+ return -EAGAIN;
- return bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_SUSPEND, 0);
+ return 0;
}
static int bmc150_accel_runtime_resume(struct device *dev)
return ret;
}
+ ret &= ~(KXCJK1013_REG_CTRL1_BIT_GSEL0 |
+ KXCJK1013_REG_CTRL1_BIT_GSEL1);
ret |= (KXCJK1013_scale_table[range_index].gsel_0 << 3);
ret |= (KXCJK1013_scale_table[range_index].gsel_1 << 4);
static const struct iio_event_spec kxcjk1013_event = {
.type = IIO_EV_TYPE_THRESH,
- .dir = IIO_EV_DIR_RISING | IIO_EV_DIR_FALLING,
+ .dir = IIO_EV_DIR_EITHER,
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_ENABLE) |
BIT(IIO_EV_INFO_PERIOD)
help
Say yes here to build support for Atmel AT91 ADC.
+config AXP288_ADC
+ tristate "X-Powers AXP288 ADC driver"
+ depends on MFD_AXP20X
+ help
+ Say yes here to have support for X-Powers power management IC (PMIC) ADC
+ device. Depending on platform configuration, this general purpose ADC can
+ be used for sampling sensors such as thermal resistors.
+
config EXYNOS_ADC
tristate "Exynos ADC driver support"
depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST)
obj-$(CONFIG_AD7887) += ad7887.o
obj-$(CONFIG_AD799X) += ad799x.o
obj-$(CONFIG_AT91_ADC) += at91_adc.o
+obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
obj-$(CONFIG_MAX1027) += max1027.o
--- /dev/null
+/*
+ * axp288_adc.c - X-Powers AXP288 PMIC ADC Driver
+ *
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/axp20x.h>
+#include <linux/platform_device.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/machine.h>
+#include <linux/iio/driver.h>
+
+#define AXP288_ADC_EN_MASK 0xF1
+#define AXP288_ADC_TS_PIN_GPADC 0xF2
+#define AXP288_ADC_TS_PIN_ON 0xF3
+
+enum axp288_adc_id {
+ AXP288_ADC_TS,
+ AXP288_ADC_PMIC,
+ AXP288_ADC_GP,
+ AXP288_ADC_BATT_CHRG_I,
+ AXP288_ADC_BATT_DISCHRG_I,
+ AXP288_ADC_BATT_V,
+ AXP288_ADC_NR_CHAN,
+};
+
+struct axp288_adc_info {
+ int irq;
+ struct regmap *regmap;
+};
+
+static const struct iio_chan_spec const axp288_adc_channels[] = {
+ {
+ .indexed = 1,
+ .type = IIO_TEMP,
+ .channel = 0,
+ .address = AXP288_TS_ADC_H,
+ .datasheet_name = "TS_PIN",
+ }, {
+ .indexed = 1,
+ .type = IIO_TEMP,
+ .channel = 1,
+ .address = AXP288_PMIC_ADC_H,
+ .datasheet_name = "PMIC_TEMP",
+ }, {
+ .indexed = 1,
+ .type = IIO_TEMP,
+ .channel = 2,
+ .address = AXP288_GP_ADC_H,
+ .datasheet_name = "GPADC",
+ }, {
+ .indexed = 1,
+ .type = IIO_CURRENT,
+ .channel = 3,
+ .address = AXP20X_BATT_CHRG_I_H,
+ .datasheet_name = "BATT_CHG_I",
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ }, {
+ .indexed = 1,
+ .type = IIO_CURRENT,
+ .channel = 4,
+ .address = AXP20X_BATT_DISCHRG_I_H,
+ .datasheet_name = "BATT_DISCHRG_I",
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ }, {
+ .indexed = 1,
+ .type = IIO_VOLTAGE,
+ .channel = 5,
+ .address = AXP20X_BATT_V_H,
+ .datasheet_name = "BATT_V",
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ },
+};
+
+#define AXP288_ADC_MAP(_adc_channel_label, _consumer_dev_name, \
+ _consumer_channel) \
+ { \
+ .adc_channel_label = _adc_channel_label, \
+ .consumer_dev_name = _consumer_dev_name, \
+ .consumer_channel = _consumer_channel, \
+ }
+
+/* for consumer drivers */
+static struct iio_map axp288_adc_default_maps[] = {
+ AXP288_ADC_MAP("TS_PIN", "axp288-batt", "axp288-batt-temp"),
+ AXP288_ADC_MAP("PMIC_TEMP", "axp288-pmic", "axp288-pmic-temp"),
+ AXP288_ADC_MAP("GPADC", "axp288-gpadc", "axp288-system-temp"),
+ AXP288_ADC_MAP("BATT_CHG_I", "axp288-chrg", "axp288-chrg-curr"),
+ AXP288_ADC_MAP("BATT_DISCHRG_I", "axp288-chrg", "axp288-chrg-d-curr"),
+ AXP288_ADC_MAP("BATT_V", "axp288-batt", "axp288-batt-volt"),
+ {},
+};
+
+static int axp288_adc_read_channel(int *val, unsigned long address,
+ struct regmap *regmap)
+{
+ u8 buf[2];
+
+ if (regmap_bulk_read(regmap, address, buf, 2))
+ return -EIO;
+ *val = (buf[0] << 4) + ((buf[1] >> 4) & 0x0F);
+
+ return IIO_VAL_INT;
+}
+
+static int axp288_adc_set_ts(struct regmap *regmap, unsigned int mode,
+ unsigned long address)
+{
+ /* channels other than GPADC do not need to switch TS pin */
+ if (address != AXP288_GP_ADC_H)
+ return 0;
+
+ return regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, mode);
+}
+
+static int axp288_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ int ret;
+ struct axp288_adc_info *info = iio_priv(indio_dev);
+
+ mutex_lock(&indio_dev->mlock);
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_GPADC,
+ chan->address)) {
+ dev_err(&indio_dev->dev, "GPADC mode\n");
+ ret = -EINVAL;
+ break;
+ }
+ ret = axp288_adc_read_channel(val, chan->address, info->regmap);
+ if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_ON,
+ chan->address))
+ dev_err(&indio_dev->dev, "TS pin restore\n");
+ break;
+ case IIO_CHAN_INFO_PROCESSED:
+ ret = axp288_adc_read_channel(val, chan->address, info->regmap);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static int axp288_adc_set_state(struct regmap *regmap)
+{
+ /* ADC should be always enabled for internal FG to function */
+ if (regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, AXP288_ADC_TS_PIN_ON))
+ return -EIO;
+
+ return regmap_write(regmap, AXP20X_ADC_EN1, AXP288_ADC_EN_MASK);
+}
+
+static const struct iio_info axp288_adc_iio_info = {
+ .read_raw = &axp288_adc_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int axp288_adc_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct axp288_adc_info *info;
+ struct iio_dev *indio_dev;
+ struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ info = iio_priv(indio_dev);
+ info->irq = platform_get_irq(pdev, 0);
+ if (info->irq < 0) {
+ dev_err(&pdev->dev, "no irq resource?\n");
+ return info->irq;
+ }
+ platform_set_drvdata(pdev, indio_dev);
+ info->regmap = axp20x->regmap;
+ /*
+ * Set ADC to enabled state at all time, including system suspend.
+ * otherwise internal fuel gauge functionality may be affected.
+ */
+ ret = axp288_adc_set_state(axp20x->regmap);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to enable ADC device\n");
+ return ret;
+ }
+
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->name = pdev->name;
+ indio_dev->channels = axp288_adc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(axp288_adc_channels);
+ indio_dev->info = &axp288_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ ret = iio_map_array_register(indio_dev, axp288_adc_default_maps);
+ if (ret < 0)
+ return ret;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "unable to register iio device\n");
+ goto err_array_unregister;
+ }
+ return 0;
+
+err_array_unregister:
+ iio_map_array_unregister(indio_dev);
+
+ return ret;
+}
+
+static int axp288_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+ iio_device_unregister(indio_dev);
+ iio_map_array_unregister(indio_dev);
+
+ return 0;
+}
+
+static struct platform_device_id axp288_adc_id_table[] = {
+ { .name = "axp288_adc" },
+ {},
+};
+
+static struct platform_driver axp288_adc_driver = {
+ .probe = axp288_adc_probe,
+ .remove = axp288_adc_remove,
+ .id_table = axp288_adc_id_table,
+ .driver = {
+ .name = "axp288_adc",
+ },
+};
+
+MODULE_DEVICE_TABLE(platform, axp288_adc_id_table);
+
+module_platform_driver(axp288_adc_driver);
+
+MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@linux.intel.com>");
+MODULE_DESCRIPTION("X-Powers AXP288 ADC Driver");
+MODULE_LICENSE("GPL");
static const struct mcb_device_id men_z188_ids[] = {
{ .device = 0xbc },
+ { }
};
MODULE_DEVICE_TABLE(mcb, men_z188_ids);
#define BMG160_REG_INT_EN_0 0x15
#define BMG160_DATA_ENABLE_INT BIT(7)
+#define BMG160_REG_INT_EN_1 0x16
+#define BMG160_INT1_BIT_OD BIT(1)
+
#define BMG160_REG_XOUT_L 0x02
#define BMG160_AXIS_TO_REG(axis) (BMG160_REG_XOUT_L + (axis * 2))
#define BMG160_REG_INT_STATUS_2 0x0B
#define BMG160_ANY_MOTION_MASK 0x07
+#define BMG160_ANY_MOTION_BIT_X BIT(0)
+#define BMG160_ANY_MOTION_BIT_Y BIT(1)
+#define BMG160_ANY_MOTION_BIT_Z BIT(2)
#define BMG160_REG_TEMP 0x08
#define BMG160_TEMP_CENTER_VAL 23
data->slope_thres = ret;
/* Set default interrupt mode */
+ ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_EN_1);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error reading reg_int_en_1\n");
+ return ret;
+ }
+ ret &= ~BMG160_INT1_BIT_OD;
+ ret = i2c_smbus_write_byte_data(data->client,
+ BMG160_REG_INT_EN_1, ret);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error writing reg_int_en_1\n");
+ return ret;
+ }
+
ret = i2c_smbus_write_byte_data(data->client,
BMG160_REG_INT_RST_LATCH,
BMG160_INT_MODE_LATCH_INT |
if (ret < 0) {
dev_err(&data->client->dev,
"Failed: bmg160_set_power_state for %d\n", on);
+ if (on)
+ pm_runtime_put_noidle(&data->client->dev);
+
return ret;
}
#endif
ret = bmg160_setup_any_motion_interrupt(data, state);
if (ret < 0) {
+ bmg160_set_power_state(data, false);
mutex_unlock(&data->mutex);
return ret;
}
static const struct iio_event_spec bmg160_event = {
.type = IIO_EV_TYPE_ROC,
- .dir = IIO_EV_DIR_RISING | IIO_EV_DIR_FALLING,
+ .dir = IIO_EV_DIR_EITHER,
.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_ENABLE)
};
else
ret = bmg160_setup_new_data_interrupt(data, state);
if (ret < 0) {
+ bmg160_set_power_state(data, false);
mutex_unlock(&data->mutex);
return ret;
}
else
dir = IIO_EV_DIR_FALLING;
- if (ret & BMG160_ANY_MOTION_MASK)
+ if (ret & BMG160_ANY_MOTION_BIT_X)
iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL,
0,
- IIO_MOD_X_OR_Y_OR_Z,
+ IIO_MOD_X,
+ IIO_EV_TYPE_ROC,
+ dir),
+ data->timestamp);
+ if (ret & BMG160_ANY_MOTION_BIT_Y)
+ iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL,
+ 0,
+ IIO_MOD_Y,
+ IIO_EV_TYPE_ROC,
+ dir),
+ data->timestamp);
+ if (ret & BMG160_ANY_MOTION_BIT_Z)
+ iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL,
+ 0,
+ IIO_MOD_Z,
IIO_EV_TYPE_ROC,
dir),
data->timestamp);
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct bmg160_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = bmg160_set_mode(data, BMG160_MODE_SUSPEND);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "set mode failed\n");
+ return -EAGAIN;
+ }
- return bmg160_set_mode(data, BMG160_MODE_SUSPEND);
+ return 0;
}
static int bmg160_runtime_resume(struct device *dev)
return i2c_smbus_write_byte_data(to_i2c_client(dev), TSL4531_CONTROL,
TSL4531_MODE_NORMAL);
}
-#endif
static SIMPLE_DEV_PM_OPS(tsl4531_pm_ops, tsl4531_suspend, tsl4531_resume);
+#define TSL4531_PM_OPS (&tsl4531_pm_ops)
+#else
+#define TSL4531_PM_OPS NULL
+#endif
static const struct i2c_device_id tsl4531_id[] = {
{ "tsl4531", 0 },
static struct i2c_driver tsl4531_driver = {
.driver = {
.name = TSL4531_DRV_NAME,
- .pm = &tsl4531_pm_ops,
+ .pm = TSL4531_PM_OPS,
.owner = THIS_MODULE,
},
.probe = tsl4531_probe,
return -EINVAL;
}
- indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(st));
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
.module = THIS_MODULE,
.name = "iSCSI Initiator over iSER",
.queuecommand = iscsi_queuecommand,
- .change_queue_depth = iscsi_change_queue_depth,
+ .change_queue_depth = scsi_change_queue_depth,
.sg_tablesize = ISCSI_ISER_SG_TABLESIZE,
.max_sectors = 1024,
.cmd_per_lun = ISER_DEF_CMD_PER_LUN,
.use_clustering = DISABLE_CLUSTERING,
.proc_name = "iscsi_iser",
.this_id = -1,
+ .track_queue_depth = 1,
};
static struct iscsi_transport iscsi_iser_transport = {
attr.cap.max_recv_wr = ISERT_QP_MAX_RECV_DTOS;
/*
* FIXME: Use devattr.max_sge - 2 for max_send_sge as
- * work-around for RDMA_READ..
+ * work-around for RDMA_READs with ConnectX-2.
+ *
+ * Also, still make sure to have at least two SGEs for
+ * outgoing control PDU responses.
*/
- attr.cap.max_send_sge = device->dev_attr.max_sge - 2;
+ attr.cap.max_send_sge = max(2, device->dev_attr.max_sge - 2);
isert_conn->max_sge = attr.cap.max_send_sge;
attr.cap.max_recv_sge = 1;
struct isert_cq_desc *cq_desc;
struct ib_device_attr *dev_attr;
int ret = 0, i, j;
+ int max_rx_cqe, max_tx_cqe;
dev_attr = &device->dev_attr;
ret = isert_query_device(ib_dev, dev_attr);
if (ret)
return ret;
+ max_rx_cqe = min(ISER_MAX_RX_CQ_LEN, dev_attr->max_cqe);
+ max_tx_cqe = min(ISER_MAX_TX_CQ_LEN, dev_attr->max_cqe);
+
/* asign function handlers */
if (dev_attr->device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS &&
dev_attr->device_cap_flags & IB_DEVICE_SIGNATURE_HANDOVER) {
isert_cq_rx_callback,
isert_cq_event_callback,
(void *)&cq_desc[i],
- ISER_MAX_RX_CQ_LEN, i);
+ max_rx_cqe, i);
if (IS_ERR(device->dev_rx_cq[i])) {
ret = PTR_ERR(device->dev_rx_cq[i]);
device->dev_rx_cq[i] = NULL;
isert_cq_tx_callback,
isert_cq_event_callback,
(void *)&cq_desc[i],
- ISER_MAX_TX_CQ_LEN, i);
+ max_tx_cqe, i);
if (IS_ERR(device->dev_tx_cq[i])) {
ret = PTR_ERR(device->dev_tx_cq[i]);
device->dev_tx_cq[i] = NULL;
complete(&isert_conn->conn_wait);
}
-static void
+static int
isert_disconnected_handler(struct rdma_cm_id *cma_id, bool disconnect)
{
- struct isert_conn *isert_conn = (struct isert_conn *)cma_id->context;
+ struct isert_conn *isert_conn;
+
+ if (!cma_id->qp) {
+ struct isert_np *isert_np = cma_id->context;
+
+ isert_np->np_cm_id = NULL;
+ return -1;
+ }
+
+ isert_conn = (struct isert_conn *)cma_id->context;
isert_conn->disconnect = disconnect;
INIT_WORK(&isert_conn->conn_logout_work, isert_disconnect_work);
schedule_work(&isert_conn->conn_logout_work);
+
+ return 0;
}
static int
switch (event->event) {
case RDMA_CM_EVENT_CONNECT_REQUEST:
ret = isert_connect_request(cma_id, event);
+ if (ret)
+ pr_err("isert_cma_handler failed RDMA_CM_EVENT: 0x%08x %d\n",
+ event->event, ret);
break;
case RDMA_CM_EVENT_ESTABLISHED:
isert_connected_handler(cma_id);
case RDMA_CM_EVENT_DEVICE_REMOVAL: /* FALLTHRU */
disconnect = true;
case RDMA_CM_EVENT_TIMEWAIT_EXIT: /* FALLTHRU */
- isert_disconnected_handler(cma_id, disconnect);
+ ret = isert_disconnected_handler(cma_id, disconnect);
break;
case RDMA_CM_EVENT_CONNECT_ERROR:
default:
break;
}
- if (ret != 0) {
- pr_err("isert_cma_handler failed RDMA_CM_EVENT: 0x%08x %d\n",
- event->event, ret);
- dump_stack();
- }
-
return ret;
}
{
struct isert_np *isert_np = (struct isert_np *)np->np_context;
- rdma_destroy_id(isert_np->np_cm_id);
+ if (isert_np->np_cm_id)
+ rdma_destroy_id(isert_np->np_cm_id);
np->np_context = NULL;
kfree(isert_np);
" if fast_io_fail_tmo has not been set. \"off\" means that"
" this functionality is disabled.");
+static unsigned ch_count;
+module_param(ch_count, uint, 0444);
+MODULE_PARM_DESC(ch_count,
+ "Number of RDMA channels to use for communication with an SRP target. Using more than one channel improves performance if the HCA supports multiple completion vectors. The default value is the minimum of four times the number of online CPU sockets and the number of completion vectors supported by the HCA.");
+
static void srp_add_one(struct ib_device *device);
static void srp_remove_one(struct ib_device *device);
-static void srp_recv_completion(struct ib_cq *cq, void *target_ptr);
-static void srp_send_completion(struct ib_cq *cq, void *target_ptr);
+static void srp_recv_completion(struct ib_cq *cq, void *ch_ptr);
+static void srp_send_completion(struct ib_cq *cq, void *ch_ptr);
static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event);
static struct scsi_transport_template *ib_srp_transport_template;
ret = ib_find_pkey(target->srp_host->srp_dev->dev,
target->srp_host->port,
- be16_to_cpu(target->path.pkey),
+ be16_to_cpu(target->pkey),
&attr->pkey_index);
if (ret)
goto out;
return ret;
}
-static int srp_new_cm_id(struct srp_target_port *target)
+static int srp_new_cm_id(struct srp_rdma_ch *ch)
{
+ struct srp_target_port *target = ch->target;
struct ib_cm_id *new_cm_id;
new_cm_id = ib_create_cm_id(target->srp_host->srp_dev->dev,
- srp_cm_handler, target);
+ srp_cm_handler, ch);
if (IS_ERR(new_cm_id))
return PTR_ERR(new_cm_id);
- if (target->cm_id)
- ib_destroy_cm_id(target->cm_id);
- target->cm_id = new_cm_id;
+ if (ch->cm_id)
+ ib_destroy_cm_id(ch->cm_id);
+ ch->cm_id = new_cm_id;
+ ch->path.sgid = target->sgid;
+ ch->path.dgid = target->orig_dgid;
+ ch->path.pkey = target->pkey;
+ ch->path.service_id = target->service_id;
return 0;
}
dev->max_pages_per_mr);
}
-static int srp_create_target_ib(struct srp_target_port *target)
+/**
+ * srp_destroy_qp() - destroy an RDMA queue pair
+ * @ch: SRP RDMA channel.
+ *
+ * Change a queue pair into the error state and wait until all receive
+ * completions have been processed before destroying it. This avoids that
+ * the receive completion handler can access the queue pair while it is
+ * being destroyed.
+ */
+static void srp_destroy_qp(struct srp_rdma_ch *ch)
+{
+ struct srp_target_port *target = ch->target;
+ static struct ib_qp_attr attr = { .qp_state = IB_QPS_ERR };
+ static struct ib_recv_wr wr = { .wr_id = SRP_LAST_WR_ID };
+ struct ib_recv_wr *bad_wr;
+ int ret;
+
+ /* Destroying a QP and reusing ch->done is only safe if not connected */
+ WARN_ON_ONCE(target->connected);
+
+ ret = ib_modify_qp(ch->qp, &attr, IB_QP_STATE);
+ WARN_ONCE(ret, "ib_cm_init_qp_attr() returned %d\n", ret);
+ if (ret)
+ goto out;
+
+ init_completion(&ch->done);
+ ret = ib_post_recv(ch->qp, &wr, &bad_wr);
+ WARN_ONCE(ret, "ib_post_recv() returned %d\n", ret);
+ if (ret == 0)
+ wait_for_completion(&ch->done);
+
+out:
+ ib_destroy_qp(ch->qp);
+}
+
+static int srp_create_ch_ib(struct srp_rdma_ch *ch)
{
+ struct srp_target_port *target = ch->target;
struct srp_device *dev = target->srp_host->srp_dev;
struct ib_qp_init_attr *init_attr;
struct ib_cq *recv_cq, *send_cq;
if (!init_attr)
return -ENOMEM;
- recv_cq = ib_create_cq(dev->dev, srp_recv_completion, NULL, target,
- target->queue_size, target->comp_vector);
+ /* + 1 for SRP_LAST_WR_ID */
+ recv_cq = ib_create_cq(dev->dev, srp_recv_completion, NULL, ch,
+ target->queue_size + 1, ch->comp_vector);
if (IS_ERR(recv_cq)) {
ret = PTR_ERR(recv_cq);
goto err;
}
- send_cq = ib_create_cq(dev->dev, srp_send_completion, NULL, target,
- m * target->queue_size, target->comp_vector);
+ send_cq = ib_create_cq(dev->dev, srp_send_completion, NULL, ch,
+ m * target->queue_size, ch->comp_vector);
if (IS_ERR(send_cq)) {
ret = PTR_ERR(send_cq);
goto err_recv_cq;
init_attr->event_handler = srp_qp_event;
init_attr->cap.max_send_wr = m * target->queue_size;
- init_attr->cap.max_recv_wr = target->queue_size;
+ init_attr->cap.max_recv_wr = target->queue_size + 1;
init_attr->cap.max_recv_sge = 1;
init_attr->cap.max_send_sge = 1;
init_attr->sq_sig_type = IB_SIGNAL_REQ_WR;
"FR pool allocation failed (%d)\n", ret);
goto err_qp;
}
- if (target->fr_pool)
- srp_destroy_fr_pool(target->fr_pool);
- target->fr_pool = fr_pool;
+ if (ch->fr_pool)
+ srp_destroy_fr_pool(ch->fr_pool);
+ ch->fr_pool = fr_pool;
} else if (!dev->use_fast_reg && dev->has_fmr) {
fmr_pool = srp_alloc_fmr_pool(target);
if (IS_ERR(fmr_pool)) {
"FMR pool allocation failed (%d)\n", ret);
goto err_qp;
}
- if (target->fmr_pool)
- ib_destroy_fmr_pool(target->fmr_pool);
- target->fmr_pool = fmr_pool;
+ if (ch->fmr_pool)
+ ib_destroy_fmr_pool(ch->fmr_pool);
+ ch->fmr_pool = fmr_pool;
}
- if (target->qp)
- ib_destroy_qp(target->qp);
- if (target->recv_cq)
- ib_destroy_cq(target->recv_cq);
- if (target->send_cq)
- ib_destroy_cq(target->send_cq);
+ if (ch->qp)
+ srp_destroy_qp(ch);
+ if (ch->recv_cq)
+ ib_destroy_cq(ch->recv_cq);
+ if (ch->send_cq)
+ ib_destroy_cq(ch->send_cq);
- target->qp = qp;
- target->recv_cq = recv_cq;
- target->send_cq = send_cq;
+ ch->qp = qp;
+ ch->recv_cq = recv_cq;
+ ch->send_cq = send_cq;
kfree(init_attr);
return 0;
/*
* Note: this function may be called without srp_alloc_iu_bufs() having been
- * invoked. Hence the target->[rt]x_ring checks.
+ * invoked. Hence the ch->[rt]x_ring checks.
*/
-static void srp_free_target_ib(struct srp_target_port *target)
+static void srp_free_ch_ib(struct srp_target_port *target,
+ struct srp_rdma_ch *ch)
{
struct srp_device *dev = target->srp_host->srp_dev;
int i;
+ if (!ch->target)
+ return;
+
+ if (ch->cm_id) {
+ ib_destroy_cm_id(ch->cm_id);
+ ch->cm_id = NULL;
+ }
+
+ /* If srp_new_cm_id() succeeded but srp_create_ch_ib() not, return. */
+ if (!ch->qp)
+ return;
+
if (dev->use_fast_reg) {
- if (target->fr_pool)
- srp_destroy_fr_pool(target->fr_pool);
+ if (ch->fr_pool)
+ srp_destroy_fr_pool(ch->fr_pool);
} else {
- if (target->fmr_pool)
- ib_destroy_fmr_pool(target->fmr_pool);
+ if (ch->fmr_pool)
+ ib_destroy_fmr_pool(ch->fmr_pool);
}
- ib_destroy_qp(target->qp);
- ib_destroy_cq(target->send_cq);
- ib_destroy_cq(target->recv_cq);
+ srp_destroy_qp(ch);
+ ib_destroy_cq(ch->send_cq);
+ ib_destroy_cq(ch->recv_cq);
+
+ /*
+ * Avoid that the SCSI error handler tries to use this channel after
+ * it has been freed. The SCSI error handler can namely continue
+ * trying to perform recovery actions after scsi_remove_host()
+ * returned.
+ */
+ ch->target = NULL;
- target->qp = NULL;
- target->send_cq = target->recv_cq = NULL;
+ ch->qp = NULL;
+ ch->send_cq = ch->recv_cq = NULL;
- if (target->rx_ring) {
+ if (ch->rx_ring) {
for (i = 0; i < target->queue_size; ++i)
- srp_free_iu(target->srp_host, target->rx_ring[i]);
- kfree(target->rx_ring);
- target->rx_ring = NULL;
+ srp_free_iu(target->srp_host, ch->rx_ring[i]);
+ kfree(ch->rx_ring);
+ ch->rx_ring = NULL;
}
- if (target->tx_ring) {
+ if (ch->tx_ring) {
for (i = 0; i < target->queue_size; ++i)
- srp_free_iu(target->srp_host, target->tx_ring[i]);
- kfree(target->tx_ring);
- target->tx_ring = NULL;
+ srp_free_iu(target->srp_host, ch->tx_ring[i]);
+ kfree(ch->tx_ring);
+ ch->tx_ring = NULL;
}
}
static void srp_path_rec_completion(int status,
struct ib_sa_path_rec *pathrec,
- void *target_ptr)
+ void *ch_ptr)
{
- struct srp_target_port *target = target_ptr;
+ struct srp_rdma_ch *ch = ch_ptr;
+ struct srp_target_port *target = ch->target;
- target->status = status;
+ ch->status = status;
if (status)
shost_printk(KERN_ERR, target->scsi_host,
PFX "Got failed path rec status %d\n", status);
else
- target->path = *pathrec;
- complete(&target->done);
+ ch->path = *pathrec;
+ complete(&ch->done);
}
-static int srp_lookup_path(struct srp_target_port *target)
+static int srp_lookup_path(struct srp_rdma_ch *ch)
{
+ struct srp_target_port *target = ch->target;
int ret;
- target->path.numb_path = 1;
-
- init_completion(&target->done);
-
- target->path_query_id = ib_sa_path_rec_get(&srp_sa_client,
- target->srp_host->srp_dev->dev,
- target->srp_host->port,
- &target->path,
- IB_SA_PATH_REC_SERVICE_ID |
- IB_SA_PATH_REC_DGID |
- IB_SA_PATH_REC_SGID |
- IB_SA_PATH_REC_NUMB_PATH |
- IB_SA_PATH_REC_PKEY,
- SRP_PATH_REC_TIMEOUT_MS,
- GFP_KERNEL,
- srp_path_rec_completion,
- target, &target->path_query);
- if (target->path_query_id < 0)
- return target->path_query_id;
-
- ret = wait_for_completion_interruptible(&target->done);
+ ch->path.numb_path = 1;
+
+ init_completion(&ch->done);
+
+ ch->path_query_id = ib_sa_path_rec_get(&srp_sa_client,
+ target->srp_host->srp_dev->dev,
+ target->srp_host->port,
+ &ch->path,
+ IB_SA_PATH_REC_SERVICE_ID |
+ IB_SA_PATH_REC_DGID |
+ IB_SA_PATH_REC_SGID |
+ IB_SA_PATH_REC_NUMB_PATH |
+ IB_SA_PATH_REC_PKEY,
+ SRP_PATH_REC_TIMEOUT_MS,
+ GFP_KERNEL,
+ srp_path_rec_completion,
+ ch, &ch->path_query);
+ if (ch->path_query_id < 0)
+ return ch->path_query_id;
+
+ ret = wait_for_completion_interruptible(&ch->done);
if (ret < 0)
return ret;
- if (target->status < 0)
+ if (ch->status < 0)
shost_printk(KERN_WARNING, target->scsi_host,
PFX "Path record query failed\n");
- return target->status;
+ return ch->status;
}
-static int srp_send_req(struct srp_target_port *target)
+static int srp_send_req(struct srp_rdma_ch *ch, bool multich)
{
+ struct srp_target_port *target = ch->target;
struct {
struct ib_cm_req_param param;
struct srp_login_req priv;
if (!req)
return -ENOMEM;
- req->param.primary_path = &target->path;
+ req->param.primary_path = &ch->path;
req->param.alternate_path = NULL;
req->param.service_id = target->service_id;
- req->param.qp_num = target->qp->qp_num;
- req->param.qp_type = target->qp->qp_type;
+ req->param.qp_num = ch->qp->qp_num;
+ req->param.qp_type = ch->qp->qp_type;
req->param.private_data = &req->priv;
req->param.private_data_len = sizeof req->priv;
req->param.flow_control = 1;
req->priv.req_it_iu_len = cpu_to_be32(target->max_iu_len);
req->priv.req_buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT |
SRP_BUF_FORMAT_INDIRECT);
+ req->priv.req_flags = (multich ? SRP_MULTICHAN_MULTI :
+ SRP_MULTICHAN_SINGLE);
/*
* In the published SRP specification (draft rev. 16a), the
* port identifier format is 8 bytes of ID extension followed
*/
if (target->io_class == SRP_REV10_IB_IO_CLASS) {
memcpy(req->priv.initiator_port_id,
- &target->path.sgid.global.interface_id, 8);
+ &target->sgid.global.interface_id, 8);
memcpy(req->priv.initiator_port_id + 8,
&target->initiator_ext, 8);
memcpy(req->priv.target_port_id, &target->ioc_guid, 8);
memcpy(req->priv.initiator_port_id,
&target->initiator_ext, 8);
memcpy(req->priv.initiator_port_id + 8,
- &target->path.sgid.global.interface_id, 8);
+ &target->sgid.global.interface_id, 8);
memcpy(req->priv.target_port_id, &target->id_ext, 8);
memcpy(req->priv.target_port_id + 8, &target->ioc_guid, 8);
}
&target->srp_host->srp_dev->dev->node_guid, 8);
}
- status = ib_send_cm_req(target->cm_id, &req->param);
+ status = ib_send_cm_req(ch->cm_id, &req->param);
kfree(req);
static void srp_disconnect_target(struct srp_target_port *target)
{
+ struct srp_rdma_ch *ch;
+ int i;
+
if (srp_change_conn_state(target, false)) {
/* XXX should send SRP_I_LOGOUT request */
- if (ib_send_cm_dreq(target->cm_id, NULL, 0)) {
- shost_printk(KERN_DEBUG, target->scsi_host,
- PFX "Sending CM DREQ failed\n");
+ for (i = 0; i < target->ch_count; i++) {
+ ch = &target->ch[i];
+ if (ch->cm_id && ib_send_cm_dreq(ch->cm_id, NULL, 0)) {
+ shost_printk(KERN_DEBUG, target->scsi_host,
+ PFX "Sending CM DREQ failed\n");
+ }
}
}
}
-static void srp_free_req_data(struct srp_target_port *target)
+static void srp_free_req_data(struct srp_target_port *target,
+ struct srp_rdma_ch *ch)
{
struct srp_device *dev = target->srp_host->srp_dev;
struct ib_device *ibdev = dev->dev;
struct srp_request *req;
int i;
- if (!target->req_ring)
+ if (!ch->target || !ch->req_ring)
return;
for (i = 0; i < target->req_ring_size; ++i) {
- req = &target->req_ring[i];
+ req = &ch->req_ring[i];
if (dev->use_fast_reg)
kfree(req->fr_list);
else
kfree(req->indirect_desc);
}
- kfree(target->req_ring);
- target->req_ring = NULL;
+ kfree(ch->req_ring);
+ ch->req_ring = NULL;
}
-static int srp_alloc_req_data(struct srp_target_port *target)
+static int srp_alloc_req_data(struct srp_rdma_ch *ch)
{
+ struct srp_target_port *target = ch->target;
struct srp_device *srp_dev = target->srp_host->srp_dev;
struct ib_device *ibdev = srp_dev->dev;
struct srp_request *req;
dma_addr_t dma_addr;
int i, ret = -ENOMEM;
- INIT_LIST_HEAD(&target->free_reqs);
-
- target->req_ring = kzalloc(target->req_ring_size *
- sizeof(*target->req_ring), GFP_KERNEL);
- if (!target->req_ring)
+ ch->req_ring = kcalloc(target->req_ring_size, sizeof(*ch->req_ring),
+ GFP_KERNEL);
+ if (!ch->req_ring)
goto out;
for (i = 0; i < target->req_ring_size; ++i) {
- req = &target->req_ring[i];
+ req = &ch->req_ring[i];
mr_list = kmalloc(target->cmd_sg_cnt * sizeof(void *),
GFP_KERNEL);
if (!mr_list)
goto out;
req->indirect_dma_addr = dma_addr;
- req->index = i;
- list_add_tail(&req->list, &target->free_reqs);
}
ret = 0;
static void srp_remove_target(struct srp_target_port *target)
{
+ struct srp_rdma_ch *ch;
+ int i;
+
WARN_ON_ONCE(target->state != SRP_TARGET_REMOVED);
srp_del_scsi_host_attr(target->scsi_host);
scsi_remove_host(target->scsi_host);
srp_stop_rport_timers(target->rport);
srp_disconnect_target(target);
- ib_destroy_cm_id(target->cm_id);
- srp_free_target_ib(target);
+ for (i = 0; i < target->ch_count; i++) {
+ ch = &target->ch[i];
+ srp_free_ch_ib(target, ch);
+ }
cancel_work_sync(&target->tl_err_work);
srp_rport_put(target->rport);
- srp_free_req_data(target);
+ for (i = 0; i < target->ch_count; i++) {
+ ch = &target->ch[i];
+ srp_free_req_data(target, ch);
+ }
+ kfree(target->ch);
+ target->ch = NULL;
spin_lock(&target->srp_host->target_lock);
list_del(&target->list);
srp_queue_remove_work(target);
}
-static int srp_connect_target(struct srp_target_port *target)
+static int srp_connect_ch(struct srp_rdma_ch *ch, bool multich)
{
- int retries = 3;
+ struct srp_target_port *target = ch->target;
int ret;
- WARN_ON_ONCE(target->connected);
+ WARN_ON_ONCE(!multich && target->connected);
target->qp_in_error = false;
- ret = srp_lookup_path(target);
+ ret = srp_lookup_path(ch);
if (ret)
return ret;
while (1) {
- init_completion(&target->done);
- ret = srp_send_req(target);
+ init_completion(&ch->done);
+ ret = srp_send_req(ch, multich);
if (ret)
return ret;
- ret = wait_for_completion_interruptible(&target->done);
+ ret = wait_for_completion_interruptible(&ch->done);
if (ret < 0)
return ret;
* back, or SRP_DLID_REDIRECT if we get a lid/qp
* redirect REJ back.
*/
- switch (target->status) {
+ switch (ch->status) {
case 0:
srp_change_conn_state(target, true);
return 0;
case SRP_PORT_REDIRECT:
- ret = srp_lookup_path(target);
+ ret = srp_lookup_path(ch);
if (ret)
return ret;
break;
break;
case SRP_STALE_CONN:
- /* Our current CM id was stale, and is now in timewait.
- * Try to reconnect with a new one.
- */
- if (!retries-- || srp_new_cm_id(target)) {
- shost_printk(KERN_ERR, target->scsi_host, PFX
- "giving up on stale connection\n");
- target->status = -ECONNRESET;
- return target->status;
- }
-
shost_printk(KERN_ERR, target->scsi_host, PFX
- "retrying stale connection\n");
- break;
+ "giving up on stale connection\n");
+ ch->status = -ECONNRESET;
+ return ch->status;
default:
- return target->status;
+ return ch->status;
}
}
}
-static int srp_inv_rkey(struct srp_target_port *target, u32 rkey)
+static int srp_inv_rkey(struct srp_rdma_ch *ch, u32 rkey)
{
struct ib_send_wr *bad_wr;
struct ib_send_wr wr = {
.ex.invalidate_rkey = rkey,
};
- return ib_post_send(target->qp, &wr, &bad_wr);
+ return ib_post_send(ch->qp, &wr, &bad_wr);
}
static void srp_unmap_data(struct scsi_cmnd *scmnd,
- struct srp_target_port *target,
+ struct srp_rdma_ch *ch,
struct srp_request *req)
{
+ struct srp_target_port *target = ch->target;
struct srp_device *dev = target->srp_host->srp_dev;
struct ib_device *ibdev = dev->dev;
int i, res;
struct srp_fr_desc **pfr;
for (i = req->nmdesc, pfr = req->fr_list; i > 0; i--, pfr++) {
- res = srp_inv_rkey(target, (*pfr)->mr->rkey);
+ res = srp_inv_rkey(ch, (*pfr)->mr->rkey);
if (res < 0) {
shost_printk(KERN_ERR, target->scsi_host, PFX
"Queueing INV WR for rkey %#x failed (%d)\n",
}
}
if (req->nmdesc)
- srp_fr_pool_put(target->fr_pool, req->fr_list,
+ srp_fr_pool_put(ch->fr_pool, req->fr_list,
req->nmdesc);
} else {
struct ib_pool_fmr **pfmr;
/**
* srp_claim_req - Take ownership of the scmnd associated with a request.
- * @target: SRP target port.
+ * @ch: SRP RDMA channel.
* @req: SRP request.
* @sdev: If not NULL, only take ownership for this SCSI device.
* @scmnd: If NULL, take ownership of @req->scmnd. If not NULL, only take
* Return value:
* Either NULL or a pointer to the SCSI command the caller became owner of.
*/
-static struct scsi_cmnd *srp_claim_req(struct srp_target_port *target,
+static struct scsi_cmnd *srp_claim_req(struct srp_rdma_ch *ch,
struct srp_request *req,
struct scsi_device *sdev,
struct scsi_cmnd *scmnd)
{
unsigned long flags;
- spin_lock_irqsave(&target->lock, flags);
+ spin_lock_irqsave(&ch->lock, flags);
if (req->scmnd &&
(!sdev || req->scmnd->device == sdev) &&
(!scmnd || req->scmnd == scmnd)) {
} else {
scmnd = NULL;
}
- spin_unlock_irqrestore(&target->lock, flags);
+ spin_unlock_irqrestore(&ch->lock, flags);
return scmnd;
}
/**
* srp_free_req() - Unmap data and add request to the free request list.
- * @target: SRP target port.
+ * @ch: SRP RDMA channel.
* @req: Request to be freed.
* @scmnd: SCSI command associated with @req.
* @req_lim_delta: Amount to be added to @target->req_lim.
*/
-static void srp_free_req(struct srp_target_port *target,
- struct srp_request *req, struct scsi_cmnd *scmnd,
- s32 req_lim_delta)
+static void srp_free_req(struct srp_rdma_ch *ch, struct srp_request *req,
+ struct scsi_cmnd *scmnd, s32 req_lim_delta)
{
unsigned long flags;
- srp_unmap_data(scmnd, target, req);
+ srp_unmap_data(scmnd, ch, req);
- spin_lock_irqsave(&target->lock, flags);
- target->req_lim += req_lim_delta;
- list_add_tail(&req->list, &target->free_reqs);
- spin_unlock_irqrestore(&target->lock, flags);
+ spin_lock_irqsave(&ch->lock, flags);
+ ch->req_lim += req_lim_delta;
+ spin_unlock_irqrestore(&ch->lock, flags);
}
-static void srp_finish_req(struct srp_target_port *target,
- struct srp_request *req, struct scsi_device *sdev,
- int result)
+static void srp_finish_req(struct srp_rdma_ch *ch, struct srp_request *req,
+ struct scsi_device *sdev, int result)
{
- struct scsi_cmnd *scmnd = srp_claim_req(target, req, sdev, NULL);
+ struct scsi_cmnd *scmnd = srp_claim_req(ch, req, sdev, NULL);
if (scmnd) {
- srp_free_req(target, req, scmnd, 0);
+ srp_free_req(ch, req, scmnd, 0);
scmnd->result = result;
scmnd->scsi_done(scmnd);
}
static void srp_terminate_io(struct srp_rport *rport)
{
struct srp_target_port *target = rport->lld_data;
+ struct srp_rdma_ch *ch;
struct Scsi_Host *shost = target->scsi_host;
struct scsi_device *sdev;
- int i;
+ int i, j;
/*
* Invoking srp_terminate_io() while srp_queuecommand() is running
shost_for_each_device(sdev, shost)
WARN_ON_ONCE(sdev->request_queue->request_fn_active);
- for (i = 0; i < target->req_ring_size; ++i) {
- struct srp_request *req = &target->req_ring[i];
- srp_finish_req(target, req, NULL, DID_TRANSPORT_FAILFAST << 16);
+ for (i = 0; i < target->ch_count; i++) {
+ ch = &target->ch[i];
+
+ for (j = 0; j < target->req_ring_size; ++j) {
+ struct srp_request *req = &ch->req_ring[j];
+
+ srp_finish_req(ch, req, NULL,
+ DID_TRANSPORT_FAILFAST << 16);
+ }
}
}
static int srp_rport_reconnect(struct srp_rport *rport)
{
struct srp_target_port *target = rport->lld_data;
- int i, ret;
+ struct srp_rdma_ch *ch;
+ int i, j, ret = 0;
+ bool multich = false;
srp_disconnect_target(target);
+
+ if (target->state == SRP_TARGET_SCANNING)
+ return -ENODEV;
+
/*
* Now get a new local CM ID so that we avoid confusing the target in
* case things are really fouled up. Doing so also ensures that all CM
* callbacks will have finished before a new QP is allocated.
*/
- ret = srp_new_cm_id(target);
-
- for (i = 0; i < target->req_ring_size; ++i) {
- struct srp_request *req = &target->req_ring[i];
- srp_finish_req(target, req, NULL, DID_RESET << 16);
+ for (i = 0; i < target->ch_count; i++) {
+ ch = &target->ch[i];
+ if (!ch->target)
+ break;
+ ret += srp_new_cm_id(ch);
}
+ for (i = 0; i < target->ch_count; i++) {
+ ch = &target->ch[i];
+ if (!ch->target)
+ break;
+ for (j = 0; j < target->req_ring_size; ++j) {
+ struct srp_request *req = &ch->req_ring[j];
- /*
- * Whether or not creating a new CM ID succeeded, create a new
- * QP. This guarantees that all callback functions for the old QP have
- * finished before any send requests are posted on the new QP.
- */
- ret += srp_create_target_ib(target);
-
- INIT_LIST_HEAD(&target->free_tx);
- for (i = 0; i < target->queue_size; ++i)
- list_add(&target->tx_ring[i]->list, &target->free_tx);
+ srp_finish_req(ch, req, NULL, DID_RESET << 16);
+ }
+ }
+ for (i = 0; i < target->ch_count; i++) {
+ ch = &target->ch[i];
+ if (!ch->target)
+ break;
+ /*
+ * Whether or not creating a new CM ID succeeded, create a new
+ * QP. This guarantees that all completion callback function
+ * invocations have finished before request resetting starts.
+ */
+ ret += srp_create_ch_ib(ch);
- if (ret == 0)
- ret = srp_connect_target(target);
+ INIT_LIST_HEAD(&ch->free_tx);
+ for (j = 0; j < target->queue_size; ++j)
+ list_add(&ch->tx_ring[j]->list, &ch->free_tx);
+ }
+ for (i = 0; i < target->ch_count; i++) {
+ ch = &target->ch[i];
+ if (ret || !ch->target) {
+ if (i > 1)
+ ret = 0;
+ break;
+ }
+ ret = srp_connect_ch(ch, multich);
+ multich = true;
+ }
if (ret == 0)
shost_printk(KERN_INFO, target->scsi_host,
}
static int srp_map_finish_fmr(struct srp_map_state *state,
- struct srp_target_port *target)
+ struct srp_rdma_ch *ch)
{
struct ib_pool_fmr *fmr;
u64 io_addr = 0;
- fmr = ib_fmr_pool_map_phys(target->fmr_pool, state->pages,
+ fmr = ib_fmr_pool_map_phys(ch->fmr_pool, state->pages,
state->npages, io_addr);
if (IS_ERR(fmr))
return PTR_ERR(fmr);
}
static int srp_map_finish_fr(struct srp_map_state *state,
- struct srp_target_port *target)
+ struct srp_rdma_ch *ch)
{
+ struct srp_target_port *target = ch->target;
struct srp_device *dev = target->srp_host->srp_dev;
struct ib_send_wr *bad_wr;
struct ib_send_wr wr;
struct srp_fr_desc *desc;
u32 rkey;
- desc = srp_fr_pool_get(target->fr_pool);
+ desc = srp_fr_pool_get(ch->fr_pool);
if (!desc)
return -ENOMEM;
srp_map_desc(state, state->base_dma_addr, state->dma_len,
desc->mr->rkey);
- return ib_post_send(target->qp, &wr, &bad_wr);
+ return ib_post_send(ch->qp, &wr, &bad_wr);
}
static int srp_finish_mapping(struct srp_map_state *state,
- struct srp_target_port *target)
+ struct srp_rdma_ch *ch)
{
+ struct srp_target_port *target = ch->target;
int ret = 0;
if (state->npages == 0)
target->rkey);
else
ret = target->srp_host->srp_dev->use_fast_reg ?
- srp_map_finish_fr(state, target) :
- srp_map_finish_fmr(state, target);
+ srp_map_finish_fr(state, ch) :
+ srp_map_finish_fmr(state, ch);
if (ret == 0) {
state->npages = 0;
}
static int srp_map_sg_entry(struct srp_map_state *state,
- struct srp_target_port *target,
+ struct srp_rdma_ch *ch,
struct scatterlist *sg, int sg_index,
bool use_mr)
{
+ struct srp_target_port *target = ch->target;
struct srp_device *dev = target->srp_host->srp_dev;
struct ib_device *ibdev = dev->dev;
dma_addr_t dma_addr = ib_sg_dma_address(ibdev, sg);
*/
if ((!dev->use_fast_reg && dma_addr & ~dev->mr_page_mask) ||
dma_len > dev->mr_max_size) {
- ret = srp_finish_mapping(state, target);
+ ret = srp_finish_mapping(state, ch);
if (ret)
return ret;
while (dma_len) {
unsigned offset = dma_addr & ~dev->mr_page_mask;
if (state->npages == dev->max_pages_per_mr || offset != 0) {
- ret = srp_finish_mapping(state, target);
+ ret = srp_finish_mapping(state, ch);
if (ret)
return ret;
*/
ret = 0;
if (len != dev->mr_page_size) {
- ret = srp_finish_mapping(state, target);
+ ret = srp_finish_mapping(state, ch);
if (!ret)
srp_map_update_start(state, NULL, 0, 0);
}
return ret;
}
-static int srp_map_sg(struct srp_map_state *state,
- struct srp_target_port *target, struct srp_request *req,
- struct scatterlist *scat, int count)
+static int srp_map_sg(struct srp_map_state *state, struct srp_rdma_ch *ch,
+ struct srp_request *req, struct scatterlist *scat,
+ int count)
{
+ struct srp_target_port *target = ch->target;
struct srp_device *dev = target->srp_host->srp_dev;
struct ib_device *ibdev = dev->dev;
struct scatterlist *sg;
state->pages = req->map_page;
if (dev->use_fast_reg) {
state->next_fr = req->fr_list;
- use_mr = !!target->fr_pool;
+ use_mr = !!ch->fr_pool;
} else {
state->next_fmr = req->fmr_list;
- use_mr = !!target->fmr_pool;
+ use_mr = !!ch->fmr_pool;
}
for_each_sg(scat, sg, count, i) {
- if (srp_map_sg_entry(state, target, sg, i, use_mr)) {
+ if (srp_map_sg_entry(state, ch, sg, i, use_mr)) {
/*
* Memory registration failed, so backtrack to the
* first unmapped entry and continue on without using
}
}
- if (use_mr && srp_finish_mapping(state, target))
+ if (use_mr && srp_finish_mapping(state, ch))
goto backtrack;
req->nmdesc = state->nmdesc;
return 0;
}
-static int srp_map_data(struct scsi_cmnd *scmnd, struct srp_target_port *target,
+static int srp_map_data(struct scsi_cmnd *scmnd, struct srp_rdma_ch *ch,
struct srp_request *req)
{
+ struct srp_target_port *target = ch->target;
struct scatterlist *scat;
struct srp_cmd *cmd = req->cmd->buf;
int len, nents, count;
target->indirect_size, DMA_TO_DEVICE);
memset(&state, 0, sizeof(state));
- srp_map_sg(&state, target, req, scat, count);
+ srp_map_sg(&state, ch, req, scat, count);
/* We've mapped the request, now pull as much of the indirect
* descriptor table as we can into the command buffer. If this
/*
* Return an IU and possible credit to the free pool
*/
-static void srp_put_tx_iu(struct srp_target_port *target, struct srp_iu *iu,
+static void srp_put_tx_iu(struct srp_rdma_ch *ch, struct srp_iu *iu,
enum srp_iu_type iu_type)
{
unsigned long flags;
- spin_lock_irqsave(&target->lock, flags);
- list_add(&iu->list, &target->free_tx);
+ spin_lock_irqsave(&ch->lock, flags);
+ list_add(&iu->list, &ch->free_tx);
if (iu_type != SRP_IU_RSP)
- ++target->req_lim;
- spin_unlock_irqrestore(&target->lock, flags);
+ ++ch->req_lim;
+ spin_unlock_irqrestore(&ch->lock, flags);
}
/*
- * Must be called with target->lock held to protect req_lim and free_tx.
+ * Must be called with ch->lock held to protect req_lim and free_tx.
* If IU is not sent, it must be returned using srp_put_tx_iu().
*
* Note:
* - SRP_IU_RSP: 1, since a conforming SRP target never sends more than
* one unanswered SRP request to an initiator.
*/
-static struct srp_iu *__srp_get_tx_iu(struct srp_target_port *target,
+static struct srp_iu *__srp_get_tx_iu(struct srp_rdma_ch *ch,
enum srp_iu_type iu_type)
{
+ struct srp_target_port *target = ch->target;
s32 rsv = (iu_type == SRP_IU_TSK_MGMT) ? 0 : SRP_TSK_MGMT_SQ_SIZE;
struct srp_iu *iu;
- srp_send_completion(target->send_cq, target);
+ srp_send_completion(ch->send_cq, ch);
- if (list_empty(&target->free_tx))
+ if (list_empty(&ch->free_tx))
return NULL;
/* Initiator responses to target requests do not consume credits */
if (iu_type != SRP_IU_RSP) {
- if (target->req_lim <= rsv) {
+ if (ch->req_lim <= rsv) {
++target->zero_req_lim;
return NULL;
}
- --target->req_lim;
+ --ch->req_lim;
}
- iu = list_first_entry(&target->free_tx, struct srp_iu, list);
+ iu = list_first_entry(&ch->free_tx, struct srp_iu, list);
list_del(&iu->list);
return iu;
}
-static int srp_post_send(struct srp_target_port *target,
- struct srp_iu *iu, int len)
+static int srp_post_send(struct srp_rdma_ch *ch, struct srp_iu *iu, int len)
{
+ struct srp_target_port *target = ch->target;
struct ib_sge list;
struct ib_send_wr wr, *bad_wr;
wr.opcode = IB_WR_SEND;
wr.send_flags = IB_SEND_SIGNALED;
- return ib_post_send(target->qp, &wr, &bad_wr);
+ return ib_post_send(ch->qp, &wr, &bad_wr);
}
-static int srp_post_recv(struct srp_target_port *target, struct srp_iu *iu)
+static int srp_post_recv(struct srp_rdma_ch *ch, struct srp_iu *iu)
{
+ struct srp_target_port *target = ch->target;
struct ib_recv_wr wr, *bad_wr;
struct ib_sge list;
wr.sg_list = &list;
wr.num_sge = 1;
- return ib_post_recv(target->qp, &wr, &bad_wr);
+ return ib_post_recv(ch->qp, &wr, &bad_wr);
}
-static void srp_process_rsp(struct srp_target_port *target, struct srp_rsp *rsp)
+static void srp_process_rsp(struct srp_rdma_ch *ch, struct srp_rsp *rsp)
{
+ struct srp_target_port *target = ch->target;
struct srp_request *req;
struct scsi_cmnd *scmnd;
unsigned long flags;
if (unlikely(rsp->tag & SRP_TAG_TSK_MGMT)) {
- spin_lock_irqsave(&target->lock, flags);
- target->req_lim += be32_to_cpu(rsp->req_lim_delta);
- spin_unlock_irqrestore(&target->lock, flags);
+ spin_lock_irqsave(&ch->lock, flags);
+ ch->req_lim += be32_to_cpu(rsp->req_lim_delta);
+ spin_unlock_irqrestore(&ch->lock, flags);
- target->tsk_mgmt_status = -1;
+ ch->tsk_mgmt_status = -1;
if (be32_to_cpu(rsp->resp_data_len) >= 4)
- target->tsk_mgmt_status = rsp->data[3];
- complete(&target->tsk_mgmt_done);
+ ch->tsk_mgmt_status = rsp->data[3];
+ complete(&ch->tsk_mgmt_done);
} else {
- req = &target->req_ring[rsp->tag];
- scmnd = srp_claim_req(target, req, NULL, NULL);
+ scmnd = scsi_host_find_tag(target->scsi_host, rsp->tag);
+ if (scmnd) {
+ req = (void *)scmnd->host_scribble;
+ scmnd = srp_claim_req(ch, req, NULL, scmnd);
+ }
if (!scmnd) {
shost_printk(KERN_ERR, target->scsi_host,
- "Null scmnd for RSP w/tag %016llx\n",
- (unsigned long long) rsp->tag);
+ "Null scmnd for RSP w/tag %#016llx received on ch %td / QP %#x\n",
+ rsp->tag, ch - target->ch, ch->qp->qp_num);
- spin_lock_irqsave(&target->lock, flags);
- target->req_lim += be32_to_cpu(rsp->req_lim_delta);
- spin_unlock_irqrestore(&target->lock, flags);
+ spin_lock_irqsave(&ch->lock, flags);
+ ch->req_lim += be32_to_cpu(rsp->req_lim_delta);
+ spin_unlock_irqrestore(&ch->lock, flags);
return;
}
else if (unlikely(rsp->flags & SRP_RSP_FLAG_DOOVER))
scsi_set_resid(scmnd, -be32_to_cpu(rsp->data_out_res_cnt));
- srp_free_req(target, req, scmnd,
+ srp_free_req(ch, req, scmnd,
be32_to_cpu(rsp->req_lim_delta));
scmnd->host_scribble = NULL;
}
}
-static int srp_response_common(struct srp_target_port *target, s32 req_delta,
+static int srp_response_common(struct srp_rdma_ch *ch, s32 req_delta,
void *rsp, int len)
{
+ struct srp_target_port *target = ch->target;
struct ib_device *dev = target->srp_host->srp_dev->dev;
unsigned long flags;
struct srp_iu *iu;
int err;
- spin_lock_irqsave(&target->lock, flags);
- target->req_lim += req_delta;
- iu = __srp_get_tx_iu(target, SRP_IU_RSP);
- spin_unlock_irqrestore(&target->lock, flags);
+ spin_lock_irqsave(&ch->lock, flags);
+ ch->req_lim += req_delta;
+ iu = __srp_get_tx_iu(ch, SRP_IU_RSP);
+ spin_unlock_irqrestore(&ch->lock, flags);
if (!iu) {
shost_printk(KERN_ERR, target->scsi_host, PFX
memcpy(iu->buf, rsp, len);
ib_dma_sync_single_for_device(dev, iu->dma, len, DMA_TO_DEVICE);
- err = srp_post_send(target, iu, len);
+ err = srp_post_send(ch, iu, len);
if (err) {
shost_printk(KERN_ERR, target->scsi_host, PFX
"unable to post response: %d\n", err);
- srp_put_tx_iu(target, iu, SRP_IU_RSP);
+ srp_put_tx_iu(ch, iu, SRP_IU_RSP);
}
return err;
}
-static void srp_process_cred_req(struct srp_target_port *target,
+static void srp_process_cred_req(struct srp_rdma_ch *ch,
struct srp_cred_req *req)
{
struct srp_cred_rsp rsp = {
};
s32 delta = be32_to_cpu(req->req_lim_delta);
- if (srp_response_common(target, delta, &rsp, sizeof rsp))
- shost_printk(KERN_ERR, target->scsi_host, PFX
+ if (srp_response_common(ch, delta, &rsp, sizeof(rsp)))
+ shost_printk(KERN_ERR, ch->target->scsi_host, PFX
"problems processing SRP_CRED_REQ\n");
}
-static void srp_process_aer_req(struct srp_target_port *target,
+static void srp_process_aer_req(struct srp_rdma_ch *ch,
struct srp_aer_req *req)
{
+ struct srp_target_port *target = ch->target;
struct srp_aer_rsp rsp = {
.opcode = SRP_AER_RSP,
.tag = req->tag,
shost_printk(KERN_ERR, target->scsi_host, PFX
"ignoring AER for LUN %llu\n", be64_to_cpu(req->lun));
- if (srp_response_common(target, delta, &rsp, sizeof rsp))
+ if (srp_response_common(ch, delta, &rsp, sizeof(rsp)))
shost_printk(KERN_ERR, target->scsi_host, PFX
"problems processing SRP_AER_REQ\n");
}
-static void srp_handle_recv(struct srp_target_port *target, struct ib_wc *wc)
+static void srp_handle_recv(struct srp_rdma_ch *ch, struct ib_wc *wc)
{
+ struct srp_target_port *target = ch->target;
struct ib_device *dev = target->srp_host->srp_dev->dev;
struct srp_iu *iu = (struct srp_iu *) (uintptr_t) wc->wr_id;
int res;
u8 opcode;
- ib_dma_sync_single_for_cpu(dev, iu->dma, target->max_ti_iu_len,
+ ib_dma_sync_single_for_cpu(dev, iu->dma, ch->max_ti_iu_len,
DMA_FROM_DEVICE);
opcode = *(u8 *) iu->buf;
switch (opcode) {
case SRP_RSP:
- srp_process_rsp(target, iu->buf);
+ srp_process_rsp(ch, iu->buf);
break;
case SRP_CRED_REQ:
- srp_process_cred_req(target, iu->buf);
+ srp_process_cred_req(ch, iu->buf);
break;
case SRP_AER_REQ:
- srp_process_aer_req(target, iu->buf);
+ srp_process_aer_req(ch, iu->buf);
break;
case SRP_T_LOGOUT:
break;
}
- ib_dma_sync_single_for_device(dev, iu->dma, target->max_ti_iu_len,
+ ib_dma_sync_single_for_device(dev, iu->dma, ch->max_ti_iu_len,
DMA_FROM_DEVICE);
- res = srp_post_recv(target, iu);
+ res = srp_post_recv(ch, iu);
if (res != 0)
shost_printk(KERN_ERR, target->scsi_host,
PFX "Recv failed with error code %d\n", res);
}
static void srp_handle_qp_err(u64 wr_id, enum ib_wc_status wc_status,
- bool send_err, struct srp_target_port *target)
+ bool send_err, struct srp_rdma_ch *ch)
{
+ struct srp_target_port *target = ch->target;
+
+ if (wr_id == SRP_LAST_WR_ID) {
+ complete(&ch->done);
+ return;
+ }
+
if (target->connected && !target->qp_in_error) {
if (wr_id & LOCAL_INV_WR_ID_MASK) {
shost_printk(KERN_ERR, target->scsi_host, PFX
target->qp_in_error = true;
}
-static void srp_recv_completion(struct ib_cq *cq, void *target_ptr)
+static void srp_recv_completion(struct ib_cq *cq, void *ch_ptr)
{
- struct srp_target_port *target = target_ptr;
+ struct srp_rdma_ch *ch = ch_ptr;
struct ib_wc wc;
ib_req_notify_cq(cq, IB_CQ_NEXT_COMP);
while (ib_poll_cq(cq, 1, &wc) > 0) {
if (likely(wc.status == IB_WC_SUCCESS)) {
- srp_handle_recv(target, &wc);
+ srp_handle_recv(ch, &wc);
} else {
- srp_handle_qp_err(wc.wr_id, wc.status, false, target);
+ srp_handle_qp_err(wc.wr_id, wc.status, false, ch);
}
}
}
-static void srp_send_completion(struct ib_cq *cq, void *target_ptr)
+static void srp_send_completion(struct ib_cq *cq, void *ch_ptr)
{
- struct srp_target_port *target = target_ptr;
+ struct srp_rdma_ch *ch = ch_ptr;
struct ib_wc wc;
struct srp_iu *iu;
while (ib_poll_cq(cq, 1, &wc) > 0) {
if (likely(wc.status == IB_WC_SUCCESS)) {
iu = (struct srp_iu *) (uintptr_t) wc.wr_id;
- list_add(&iu->list, &target->free_tx);
+ list_add(&iu->list, &ch->free_tx);
} else {
- srp_handle_qp_err(wc.wr_id, wc.status, true, target);
+ srp_handle_qp_err(wc.wr_id, wc.status, true, ch);
}
}
}
{
struct srp_target_port *target = host_to_target(shost);
struct srp_rport *rport = target->rport;
+ struct srp_rdma_ch *ch;
struct srp_request *req;
struct srp_iu *iu;
struct srp_cmd *cmd;
struct ib_device *dev;
unsigned long flags;
+ u32 tag;
+ u16 idx;
int len, ret;
const bool in_scsi_eh = !in_interrupt() && current == shost->ehandler;
if (unlikely(scmnd->result))
goto err;
- spin_lock_irqsave(&target->lock, flags);
- iu = __srp_get_tx_iu(target, SRP_IU_CMD);
- if (!iu)
- goto err_unlock;
+ WARN_ON_ONCE(scmnd->request->tag < 0);
+ tag = blk_mq_unique_tag(scmnd->request);
+ ch = &target->ch[blk_mq_unique_tag_to_hwq(tag)];
+ idx = blk_mq_unique_tag_to_tag(tag);
+ WARN_ONCE(idx >= target->req_ring_size, "%s: tag %#x: idx %d >= %d\n",
+ dev_name(&shost->shost_gendev), tag, idx,
+ target->req_ring_size);
- req = list_first_entry(&target->free_reqs, struct srp_request, list);
- list_del(&req->list);
- spin_unlock_irqrestore(&target->lock, flags);
+ spin_lock_irqsave(&ch->lock, flags);
+ iu = __srp_get_tx_iu(ch, SRP_IU_CMD);
+ spin_unlock_irqrestore(&ch->lock, flags);
+ if (!iu)
+ goto err;
+
+ req = &ch->req_ring[idx];
dev = target->srp_host->srp_dev->dev;
ib_dma_sync_single_for_cpu(dev, iu->dma, target->max_iu_len,
DMA_TO_DEVICE);
cmd->opcode = SRP_CMD;
cmd->lun = cpu_to_be64((u64) scmnd->device->lun << 48);
- cmd->tag = req->index;
+ cmd->tag = tag;
memcpy(cmd->cdb, scmnd->cmnd, scmnd->cmd_len);
req->scmnd = scmnd;
req->cmd = iu;
- len = srp_map_data(scmnd, target, req);
+ len = srp_map_data(scmnd, ch, req);
if (len < 0) {
shost_printk(KERN_ERR, target->scsi_host,
PFX "Failed to map data (%d)\n", len);
ib_dma_sync_single_for_device(dev, iu->dma, target->max_iu_len,
DMA_TO_DEVICE);
- if (srp_post_send(target, iu, len)) {
+ if (srp_post_send(ch, iu, len)) {
shost_printk(KERN_ERR, target->scsi_host, PFX "Send failed\n");
goto err_unmap;
}
return ret;
err_unmap:
- srp_unmap_data(scmnd, target, req);
+ srp_unmap_data(scmnd, ch, req);
err_iu:
- srp_put_tx_iu(target, iu, SRP_IU_CMD);
+ srp_put_tx_iu(ch, iu, SRP_IU_CMD);
/*
* Avoid that the loops that iterate over the request ring can
*/
req->scmnd = NULL;
- spin_lock_irqsave(&target->lock, flags);
- list_add(&req->list, &target->free_reqs);
-
-err_unlock:
- spin_unlock_irqrestore(&target->lock, flags);
-
err:
if (scmnd->result) {
scmnd->scsi_done(scmnd);
/*
* Note: the resources allocated in this function are freed in
- * srp_free_target_ib().
+ * srp_free_ch_ib().
*/
-static int srp_alloc_iu_bufs(struct srp_target_port *target)
+static int srp_alloc_iu_bufs(struct srp_rdma_ch *ch)
{
+ struct srp_target_port *target = ch->target;
int i;
- target->rx_ring = kzalloc(target->queue_size * sizeof(*target->rx_ring),
- GFP_KERNEL);
- if (!target->rx_ring)
+ ch->rx_ring = kcalloc(target->queue_size, sizeof(*ch->rx_ring),
+ GFP_KERNEL);
+ if (!ch->rx_ring)
goto err_no_ring;
- target->tx_ring = kzalloc(target->queue_size * sizeof(*target->tx_ring),
- GFP_KERNEL);
- if (!target->tx_ring)
+ ch->tx_ring = kcalloc(target->queue_size, sizeof(*ch->tx_ring),
+ GFP_KERNEL);
+ if (!ch->tx_ring)
goto err_no_ring;
for (i = 0; i < target->queue_size; ++i) {
- target->rx_ring[i] = srp_alloc_iu(target->srp_host,
- target->max_ti_iu_len,
- GFP_KERNEL, DMA_FROM_DEVICE);
- if (!target->rx_ring[i])
+ ch->rx_ring[i] = srp_alloc_iu(target->srp_host,
+ ch->max_ti_iu_len,
+ GFP_KERNEL, DMA_FROM_DEVICE);
+ if (!ch->rx_ring[i])
goto err;
}
for (i = 0; i < target->queue_size; ++i) {
- target->tx_ring[i] = srp_alloc_iu(target->srp_host,
- target->max_iu_len,
- GFP_KERNEL, DMA_TO_DEVICE);
- if (!target->tx_ring[i])
+ ch->tx_ring[i] = srp_alloc_iu(target->srp_host,
+ target->max_iu_len,
+ GFP_KERNEL, DMA_TO_DEVICE);
+ if (!ch->tx_ring[i])
goto err;
- list_add(&target->tx_ring[i]->list, &target->free_tx);
+ list_add(&ch->tx_ring[i]->list, &ch->free_tx);
}
return 0;
err:
for (i = 0; i < target->queue_size; ++i) {
- srp_free_iu(target->srp_host, target->rx_ring[i]);
- srp_free_iu(target->srp_host, target->tx_ring[i]);
+ srp_free_iu(target->srp_host, ch->rx_ring[i]);
+ srp_free_iu(target->srp_host, ch->tx_ring[i]);
}
err_no_ring:
- kfree(target->tx_ring);
- target->tx_ring = NULL;
- kfree(target->rx_ring);
- target->rx_ring = NULL;
+ kfree(ch->tx_ring);
+ ch->tx_ring = NULL;
+ kfree(ch->rx_ring);
+ ch->rx_ring = NULL;
return -ENOMEM;
}
static void srp_cm_rep_handler(struct ib_cm_id *cm_id,
struct srp_login_rsp *lrsp,
- struct srp_target_port *target)
+ struct srp_rdma_ch *ch)
{
+ struct srp_target_port *target = ch->target;
struct ib_qp_attr *qp_attr = NULL;
int attr_mask = 0;
int ret;
int i;
if (lrsp->opcode == SRP_LOGIN_RSP) {
- target->max_ti_iu_len = be32_to_cpu(lrsp->max_ti_iu_len);
- target->req_lim = be32_to_cpu(lrsp->req_lim_delta);
+ ch->max_ti_iu_len = be32_to_cpu(lrsp->max_ti_iu_len);
+ ch->req_lim = be32_to_cpu(lrsp->req_lim_delta);
/*
* Reserve credits for task management so we don't
* bounce requests back to the SCSI mid-layer.
*/
target->scsi_host->can_queue
- = min(target->req_lim - SRP_TSK_MGMT_SQ_SIZE,
+ = min(ch->req_lim - SRP_TSK_MGMT_SQ_SIZE,
target->scsi_host->can_queue);
target->scsi_host->cmd_per_lun
= min_t(int, target->scsi_host->can_queue,
goto error;
}
- if (!target->rx_ring) {
- ret = srp_alloc_iu_bufs(target);
+ if (!ch->rx_ring) {
+ ret = srp_alloc_iu_bufs(ch);
if (ret)
goto error;
}
if (ret)
goto error_free;
- ret = ib_modify_qp(target->qp, qp_attr, attr_mask);
+ ret = ib_modify_qp(ch->qp, qp_attr, attr_mask);
if (ret)
goto error_free;
for (i = 0; i < target->queue_size; i++) {
- struct srp_iu *iu = target->rx_ring[i];
- ret = srp_post_recv(target, iu);
+ struct srp_iu *iu = ch->rx_ring[i];
+
+ ret = srp_post_recv(ch, iu);
if (ret)
goto error_free;
}
target->rq_tmo_jiffies = srp_compute_rq_tmo(qp_attr, attr_mask);
- ret = ib_modify_qp(target->qp, qp_attr, attr_mask);
+ ret = ib_modify_qp(ch->qp, qp_attr, attr_mask);
if (ret)
goto error_free;
kfree(qp_attr);
error:
- target->status = ret;
+ ch->status = ret;
}
static void srp_cm_rej_handler(struct ib_cm_id *cm_id,
struct ib_cm_event *event,
- struct srp_target_port *target)
+ struct srp_rdma_ch *ch)
{
+ struct srp_target_port *target = ch->target;
struct Scsi_Host *shost = target->scsi_host;
struct ib_class_port_info *cpi;
int opcode;
switch (event->param.rej_rcvd.reason) {
case IB_CM_REJ_PORT_CM_REDIRECT:
cpi = event->param.rej_rcvd.ari;
- target->path.dlid = cpi->redirect_lid;
- target->path.pkey = cpi->redirect_pkey;
+ ch->path.dlid = cpi->redirect_lid;
+ ch->path.pkey = cpi->redirect_pkey;
cm_id->remote_cm_qpn = be32_to_cpu(cpi->redirect_qp) & 0x00ffffff;
- memcpy(target->path.dgid.raw, cpi->redirect_gid, 16);
+ memcpy(ch->path.dgid.raw, cpi->redirect_gid, 16);
- target->status = target->path.dlid ?
+ ch->status = ch->path.dlid ?
SRP_DLID_REDIRECT : SRP_PORT_REDIRECT;
break;
* reject reason code 25 when they mean 24
* (port redirect).
*/
- memcpy(target->path.dgid.raw,
+ memcpy(ch->path.dgid.raw,
event->param.rej_rcvd.ari, 16);
shost_printk(KERN_DEBUG, shost,
PFX "Topspin/Cisco redirect to target port GID %016llx%016llx\n",
- (unsigned long long) be64_to_cpu(target->path.dgid.global.subnet_prefix),
- (unsigned long long) be64_to_cpu(target->path.dgid.global.interface_id));
+ be64_to_cpu(ch->path.dgid.global.subnet_prefix),
+ be64_to_cpu(ch->path.dgid.global.interface_id));
- target->status = SRP_PORT_REDIRECT;
+ ch->status = SRP_PORT_REDIRECT;
} else {
shost_printk(KERN_WARNING, shost,
" REJ reason: IB_CM_REJ_PORT_REDIRECT\n");
- target->status = -ECONNRESET;
+ ch->status = -ECONNRESET;
}
break;
case IB_CM_REJ_DUPLICATE_LOCAL_COMM_ID:
shost_printk(KERN_WARNING, shost,
" REJ reason: IB_CM_REJ_DUPLICATE_LOCAL_COMM_ID\n");
- target->status = -ECONNRESET;
+ ch->status = -ECONNRESET;
break;
case IB_CM_REJ_CONSUMER_DEFINED:
else
shost_printk(KERN_WARNING, shost, PFX
"SRP LOGIN from %pI6 to %pI6 REJECTED, reason 0x%08x\n",
- target->path.sgid.raw,
- target->orig_dgid, reason);
+ target->sgid.raw,
+ target->orig_dgid.raw, reason);
} else
shost_printk(KERN_WARNING, shost,
" REJ reason: IB_CM_REJ_CONSUMER_DEFINED,"
" opcode 0x%02x\n", opcode);
- target->status = -ECONNRESET;
+ ch->status = -ECONNRESET;
break;
case IB_CM_REJ_STALE_CONN:
shost_printk(KERN_WARNING, shost, " REJ reason: stale connection\n");
- target->status = SRP_STALE_CONN;
+ ch->status = SRP_STALE_CONN;
break;
default:
shost_printk(KERN_WARNING, shost, " REJ reason 0x%x\n",
event->param.rej_rcvd.reason);
- target->status = -ECONNRESET;
+ ch->status = -ECONNRESET;
}
}
static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event)
{
- struct srp_target_port *target = cm_id->context;
+ struct srp_rdma_ch *ch = cm_id->context;
+ struct srp_target_port *target = ch->target;
int comp = 0;
switch (event->event) {
shost_printk(KERN_DEBUG, target->scsi_host,
PFX "Sending CM REQ failed\n");
comp = 1;
- target->status = -ECONNRESET;
+ ch->status = -ECONNRESET;
break;
case IB_CM_REP_RECEIVED:
comp = 1;
- srp_cm_rep_handler(cm_id, event->private_data, target);
+ srp_cm_rep_handler(cm_id, event->private_data, ch);
break;
case IB_CM_REJ_RECEIVED:
shost_printk(KERN_DEBUG, target->scsi_host, PFX "REJ received\n");
comp = 1;
- srp_cm_rej_handler(cm_id, event, target);
+ srp_cm_rej_handler(cm_id, event, ch);
break;
case IB_CM_DREQ_RECEIVED:
PFX "connection closed\n");
comp = 1;
- target->status = 0;
+ ch->status = 0;
break;
case IB_CM_MRA_RECEIVED:
}
if (comp)
- complete(&target->done);
+ complete(&ch->done);
return 0;
}
-/**
- * srp_change_queue_type - changing device queue tag type
- * @sdev: scsi device struct
- * @tag_type: requested tag type
- *
- * Returns queue tag type.
- */
-static int
-srp_change_queue_type(struct scsi_device *sdev, int tag_type)
-{
- if (sdev->tagged_supported) {
- scsi_set_tag_type(sdev, tag_type);
- if (tag_type)
- scsi_activate_tcq(sdev, sdev->queue_depth);
- else
- scsi_deactivate_tcq(sdev, sdev->queue_depth);
- } else
- tag_type = 0;
-
- return tag_type;
-}
-
/**
* srp_change_queue_depth - setting device queue depth
* @sdev: scsi device struct
* @qdepth: requested queue depth
- * @reason: SCSI_QDEPTH_DEFAULT/SCSI_QDEPTH_QFULL/SCSI_QDEPTH_RAMP_UP
- * (see include/scsi/scsi_host.h for definition)
*
* Returns queue depth.
*/
static int
-srp_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
+srp_change_queue_depth(struct scsi_device *sdev, int qdepth)
{
- struct Scsi_Host *shost = sdev->host;
- int max_depth;
- if (reason == SCSI_QDEPTH_DEFAULT || reason == SCSI_QDEPTH_RAMP_UP) {
- max_depth = shost->can_queue;
- if (!sdev->tagged_supported)
- max_depth = 1;
- if (qdepth > max_depth)
- qdepth = max_depth;
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
- } else if (reason == SCSI_QDEPTH_QFULL)
- scsi_track_queue_full(sdev, qdepth);
- else
- return -EOPNOTSUPP;
-
- return sdev->queue_depth;
+ if (!sdev->tagged_supported)
+ qdepth = 1;
+ return scsi_change_queue_depth(sdev, qdepth);
}
-static int srp_send_tsk_mgmt(struct srp_target_port *target,
- u64 req_tag, unsigned int lun, u8 func)
+static int srp_send_tsk_mgmt(struct srp_rdma_ch *ch, u64 req_tag,
+ unsigned int lun, u8 func)
{
+ struct srp_target_port *target = ch->target;
struct srp_rport *rport = target->rport;
struct ib_device *dev = target->srp_host->srp_dev->dev;
struct srp_iu *iu;
if (!target->connected || target->qp_in_error)
return -1;
- init_completion(&target->tsk_mgmt_done);
+ init_completion(&ch->tsk_mgmt_done);
/*
- * Lock the rport mutex to avoid that srp_create_target_ib() is
+ * Lock the rport mutex to avoid that srp_create_ch_ib() is
* invoked while a task management function is being sent.
*/
mutex_lock(&rport->mutex);
- spin_lock_irq(&target->lock);
- iu = __srp_get_tx_iu(target, SRP_IU_TSK_MGMT);
- spin_unlock_irq(&target->lock);
+ spin_lock_irq(&ch->lock);
+ iu = __srp_get_tx_iu(ch, SRP_IU_TSK_MGMT);
+ spin_unlock_irq(&ch->lock);
if (!iu) {
mutex_unlock(&rport->mutex);
ib_dma_sync_single_for_device(dev, iu->dma, sizeof *tsk_mgmt,
DMA_TO_DEVICE);
- if (srp_post_send(target, iu, sizeof *tsk_mgmt)) {
- srp_put_tx_iu(target, iu, SRP_IU_TSK_MGMT);
+ if (srp_post_send(ch, iu, sizeof(*tsk_mgmt))) {
+ srp_put_tx_iu(ch, iu, SRP_IU_TSK_MGMT);
mutex_unlock(&rport->mutex);
return -1;
}
mutex_unlock(&rport->mutex);
- if (!wait_for_completion_timeout(&target->tsk_mgmt_done,
+ if (!wait_for_completion_timeout(&ch->tsk_mgmt_done,
msecs_to_jiffies(SRP_ABORT_TIMEOUT_MS)))
return -1;
{
struct srp_target_port *target = host_to_target(scmnd->device->host);
struct srp_request *req = (struct srp_request *) scmnd->host_scribble;
+ u32 tag;
+ u16 ch_idx;
+ struct srp_rdma_ch *ch;
int ret;
shost_printk(KERN_ERR, target->scsi_host, "SRP abort called\n");
- if (!req || !srp_claim_req(target, req, NULL, scmnd))
+ if (!req)
+ return SUCCESS;
+ tag = blk_mq_unique_tag(scmnd->request);
+ ch_idx = blk_mq_unique_tag_to_hwq(tag);
+ if (WARN_ON_ONCE(ch_idx >= target->ch_count))
return SUCCESS;
- if (srp_send_tsk_mgmt(target, req->index, scmnd->device->lun,
+ ch = &target->ch[ch_idx];
+ if (!srp_claim_req(ch, req, NULL, scmnd))
+ return SUCCESS;
+ shost_printk(KERN_ERR, target->scsi_host,
+ "Sending SRP abort for tag %#x\n", tag);
+ if (srp_send_tsk_mgmt(ch, tag, scmnd->device->lun,
SRP_TSK_ABORT_TASK) == 0)
ret = SUCCESS;
else if (target->rport->state == SRP_RPORT_LOST)
ret = FAST_IO_FAIL;
else
ret = FAILED;
- srp_free_req(target, req, scmnd, 0);
+ srp_free_req(ch, req, scmnd, 0);
scmnd->result = DID_ABORT << 16;
scmnd->scsi_done(scmnd);
static int srp_reset_device(struct scsi_cmnd *scmnd)
{
struct srp_target_port *target = host_to_target(scmnd->device->host);
+ struct srp_rdma_ch *ch;
int i;
shost_printk(KERN_ERR, target->scsi_host, "SRP reset_device called\n");
- if (srp_send_tsk_mgmt(target, SRP_TAG_NO_REQ, scmnd->device->lun,
+ ch = &target->ch[0];
+ if (srp_send_tsk_mgmt(ch, SRP_TAG_NO_REQ, scmnd->device->lun,
SRP_TSK_LUN_RESET))
return FAILED;
- if (target->tsk_mgmt_status)
+ if (ch->tsk_mgmt_status)
return FAILED;
- for (i = 0; i < target->req_ring_size; ++i) {
- struct srp_request *req = &target->req_ring[i];
- srp_finish_req(target, req, scmnd->device, DID_RESET << 16);
+ for (i = 0; i < target->ch_count; i++) {
+ ch = &target->ch[i];
+ for (i = 0; i < target->req_ring_size; ++i) {
+ struct srp_request *req = &ch->req_ring[i];
+
+ srp_finish_req(ch, req, scmnd->device, DID_RESET << 16);
+ }
}
return SUCCESS;
{
struct srp_target_port *target = host_to_target(class_to_shost(dev));
- return sprintf(buf, "0x%04x\n", be16_to_cpu(target->path.pkey));
+ return sprintf(buf, "0x%04x\n", be16_to_cpu(target->pkey));
}
static ssize_t show_sgid(struct device *dev, struct device_attribute *attr,
{
struct srp_target_port *target = host_to_target(class_to_shost(dev));
- return sprintf(buf, "%pI6\n", target->path.sgid.raw);
+ return sprintf(buf, "%pI6\n", target->sgid.raw);
}
static ssize_t show_dgid(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct srp_target_port *target = host_to_target(class_to_shost(dev));
+ struct srp_rdma_ch *ch = &target->ch[0];
- return sprintf(buf, "%pI6\n", target->path.dgid.raw);
+ return sprintf(buf, "%pI6\n", ch->path.dgid.raw);
}
static ssize_t show_orig_dgid(struct device *dev,
{
struct srp_target_port *target = host_to_target(class_to_shost(dev));
- return sprintf(buf, "%pI6\n", target->orig_dgid);
+ return sprintf(buf, "%pI6\n", target->orig_dgid.raw);
}
static ssize_t show_req_lim(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct srp_target_port *target = host_to_target(class_to_shost(dev));
+ struct srp_rdma_ch *ch;
+ int i, req_lim = INT_MAX;
- return sprintf(buf, "%d\n", target->req_lim);
+ for (i = 0; i < target->ch_count; i++) {
+ ch = &target->ch[i];
+ req_lim = min(req_lim, ch->req_lim);
+ }
+ return sprintf(buf, "%d\n", req_lim);
}
static ssize_t show_zero_req_lim(struct device *dev,
return sprintf(buf, "%s\n", target->srp_host->srp_dev->dev->name);
}
+static ssize_t show_ch_count(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct srp_target_port *target = host_to_target(class_to_shost(dev));
+
+ return sprintf(buf, "%d\n", target->ch_count);
+}
+
static ssize_t show_comp_vector(struct device *dev,
struct device_attribute *attr, char *buf)
{
static DEVICE_ATTR(zero_req_lim, S_IRUGO, show_zero_req_lim, NULL);
static DEVICE_ATTR(local_ib_port, S_IRUGO, show_local_ib_port, NULL);
static DEVICE_ATTR(local_ib_device, S_IRUGO, show_local_ib_device, NULL);
+static DEVICE_ATTR(ch_count, S_IRUGO, show_ch_count, NULL);
static DEVICE_ATTR(comp_vector, S_IRUGO, show_comp_vector, NULL);
static DEVICE_ATTR(tl_retry_count, S_IRUGO, show_tl_retry_count, NULL);
static DEVICE_ATTR(cmd_sg_entries, S_IRUGO, show_cmd_sg_entries, NULL);
&dev_attr_zero_req_lim,
&dev_attr_local_ib_port,
&dev_attr_local_ib_device,
+ &dev_attr_ch_count,
&dev_attr_comp_vector,
&dev_attr_tl_retry_count,
&dev_attr_cmd_sg_entries,
.info = srp_target_info,
.queuecommand = srp_queuecommand,
.change_queue_depth = srp_change_queue_depth,
- .change_queue_type = srp_change_queue_type,
+ .change_queue_type = scsi_change_queue_type,
.eh_abort_handler = srp_abort,
.eh_device_reset_handler = srp_reset_device,
.eh_host_reset_handler = srp_reset_host,
.this_id = -1,
.cmd_per_lun = SRP_DEFAULT_CMD_SQ_SIZE,
.use_clustering = ENABLE_CLUSTERING,
- .shost_attrs = srp_host_attrs
+ .shost_attrs = srp_host_attrs,
+ .use_blk_tags = 1,
+ .track_queue_depth = 1,
};
+static int srp_sdev_count(struct Scsi_Host *host)
+{
+ struct scsi_device *sdev;
+ int c = 0;
+
+ shost_for_each_device(sdev, host)
+ c++;
+
+ return c;
+}
+
static int srp_add_target(struct srp_host *host, struct srp_target_port *target)
{
struct srp_rport_identifiers ids;
struct srp_rport *rport;
+ target->state = SRP_TARGET_SCANNING;
sprintf(target->target_name, "SRP.T10:%016llX",
(unsigned long long) be64_to_cpu(target->id_ext));
list_add_tail(&target->list, &host->target_list);
spin_unlock(&host->target_lock);
- target->state = SRP_TARGET_LIVE;
-
scsi_scan_target(&target->scsi_host->shost_gendev,
0, target->scsi_id, SCAN_WILD_CARD, 0);
+ if (!target->connected || target->qp_in_error) {
+ shost_printk(KERN_INFO, target->scsi_host,
+ PFX "SCSI scan failed - removing SCSI host\n");
+ srp_queue_remove_work(target);
+ goto out;
+ }
+
+ pr_debug(PFX "%s: SCSI scan succeeded - detected %d LUNs\n",
+ dev_name(&target->scsi_host->shost_gendev),
+ srp_sdev_count(target->scsi_host));
+
+ spin_lock_irq(&target->lock);
+ if (target->state == SRP_TARGET_SCANNING)
+ target->state = SRP_TARGET_LIVE;
+ spin_unlock_irq(&target->lock);
+
+out:
return 0;
}
}
for (i = 0; i < 16; ++i) {
- strlcpy(dgid, p + i * 2, 3);
- target->path.dgid.raw[i] = simple_strtoul(dgid, NULL, 16);
+ strlcpy(dgid, p + i * 2, sizeof(dgid));
+ if (sscanf(dgid, "%hhx",
+ &target->orig_dgid.raw[i]) < 1) {
+ ret = -EINVAL;
+ kfree(p);
+ goto out;
+ }
}
kfree(p);
- memcpy(target->orig_dgid, target->path.dgid.raw, 16);
break;
case SRP_OPT_PKEY:
pr_warn("bad P_Key parameter '%s'\n", p);
goto out;
}
- target->path.pkey = cpu_to_be16(token);
+ target->pkey = cpu_to_be16(token);
break;
case SRP_OPT_SERVICE_ID:
goto out;
}
target->service_id = cpu_to_be64(simple_strtoull(p, NULL, 16));
- target->path.service_id = target->service_id;
kfree(p);
break;
container_of(dev, struct srp_host, dev);
struct Scsi_Host *target_host;
struct srp_target_port *target;
+ struct srp_rdma_ch *ch;
struct srp_device *srp_dev = host->srp_dev;
struct ib_device *ibdev = srp_dev->dev;
- int ret;
+ int ret, node_idx, node, cpu, i;
+ bool multich = false;
target_host = scsi_host_alloc(&srp_template,
sizeof (struct srp_target_port));
target->tl_retry_count = 7;
target->queue_size = SRP_DEFAULT_QUEUE_SIZE;
+ /*
+ * Avoid that the SCSI host can be removed by srp_remove_target()
+ * before this function returns.
+ */
+ scsi_host_get(target->scsi_host);
+
mutex_lock(&host->add_target_mutex);
ret = srp_parse_options(buf, target);
if (ret)
goto err;
+ ret = scsi_init_shared_tag_map(target_host, target_host->can_queue);
+ if (ret)
+ goto err;
+
target->req_ring_size = target->queue_size - SRP_TSK_MGMT_SQ_SIZE;
if (!srp_conn_unique(target->srp_host, target)) {
INIT_WORK(&target->tl_err_work, srp_tl_err_work);
INIT_WORK(&target->remove_work, srp_remove_work);
spin_lock_init(&target->lock);
- INIT_LIST_HEAD(&target->free_tx);
- ret = srp_alloc_req_data(target);
+ ret = ib_query_gid(ibdev, host->port, 0, &target->sgid);
if (ret)
- goto err_free_mem;
+ goto err;
- ret = ib_query_gid(ibdev, host->port, 0, &target->path.sgid);
- if (ret)
- goto err_free_mem;
+ ret = -ENOMEM;
+ target->ch_count = max_t(unsigned, num_online_nodes(),
+ min(ch_count ? :
+ min(4 * num_online_nodes(),
+ ibdev->num_comp_vectors),
+ num_online_cpus()));
+ target->ch = kcalloc(target->ch_count, sizeof(*target->ch),
+ GFP_KERNEL);
+ if (!target->ch)
+ goto err;
- ret = srp_create_target_ib(target);
- if (ret)
- goto err_free_mem;
+ node_idx = 0;
+ for_each_online_node(node) {
+ const int ch_start = (node_idx * target->ch_count /
+ num_online_nodes());
+ const int ch_end = ((node_idx + 1) * target->ch_count /
+ num_online_nodes());
+ const int cv_start = (node_idx * ibdev->num_comp_vectors /
+ num_online_nodes() + target->comp_vector)
+ % ibdev->num_comp_vectors;
+ const int cv_end = ((node_idx + 1) * ibdev->num_comp_vectors /
+ num_online_nodes() + target->comp_vector)
+ % ibdev->num_comp_vectors;
+ int cpu_idx = 0;
+
+ for_each_online_cpu(cpu) {
+ if (cpu_to_node(cpu) != node)
+ continue;
+ if (ch_start + cpu_idx >= ch_end)
+ continue;
+ ch = &target->ch[ch_start + cpu_idx];
+ ch->target = target;
+ ch->comp_vector = cv_start == cv_end ? cv_start :
+ cv_start + cpu_idx % (cv_end - cv_start);
+ spin_lock_init(&ch->lock);
+ INIT_LIST_HEAD(&ch->free_tx);
+ ret = srp_new_cm_id(ch);
+ if (ret)
+ goto err_disconnect;
- ret = srp_new_cm_id(target);
- if (ret)
- goto err_free_ib;
+ ret = srp_create_ch_ib(ch);
+ if (ret)
+ goto err_disconnect;
- ret = srp_connect_target(target);
- if (ret) {
- shost_printk(KERN_ERR, target->scsi_host,
- PFX "Connection failed\n");
- goto err_cm_id;
+ ret = srp_alloc_req_data(ch);
+ if (ret)
+ goto err_disconnect;
+
+ ret = srp_connect_ch(ch, multich);
+ if (ret) {
+ shost_printk(KERN_ERR, target->scsi_host,
+ PFX "Connection %d/%d failed\n",
+ ch_start + cpu_idx,
+ target->ch_count);
+ if (node_idx == 0 && cpu_idx == 0) {
+ goto err_disconnect;
+ } else {
+ srp_free_ch_ib(target, ch);
+ srp_free_req_data(target, ch);
+ target->ch_count = ch - target->ch;
+ break;
+ }
+ }
+
+ multich = true;
+ cpu_idx++;
+ }
+ node_idx++;
}
+ target->scsi_host->nr_hw_queues = target->ch_count;
+
ret = srp_add_target(host, target);
if (ret)
goto err_disconnect;
- shost_printk(KERN_DEBUG, target->scsi_host, PFX
- "new target: id_ext %016llx ioc_guid %016llx pkey %04x service_id %016llx sgid %pI6 dgid %pI6\n",
- be64_to_cpu(target->id_ext),
- be64_to_cpu(target->ioc_guid),
- be16_to_cpu(target->path.pkey),
- be64_to_cpu(target->service_id),
- target->path.sgid.raw, target->path.dgid.raw);
+ if (target->state != SRP_TARGET_REMOVED) {
+ shost_printk(KERN_DEBUG, target->scsi_host, PFX
+ "new target: id_ext %016llx ioc_guid %016llx pkey %04x service_id %016llx sgid %pI6 dgid %pI6\n",
+ be64_to_cpu(target->id_ext),
+ be64_to_cpu(target->ioc_guid),
+ be16_to_cpu(target->pkey),
+ be64_to_cpu(target->service_id),
+ target->sgid.raw, target->orig_dgid.raw);
+ }
ret = count;
out:
mutex_unlock(&host->add_target_mutex);
+
+ scsi_host_put(target->scsi_host);
+
return ret;
err_disconnect:
srp_disconnect_target(target);
-err_cm_id:
- ib_destroy_cm_id(target->cm_id);
-
-err_free_ib:
- srp_free_target_ib(target);
+ for (i = 0; i < target->ch_count; i++) {
+ ch = &target->ch[i];
+ srp_free_ch_ib(target, ch);
+ srp_free_req_data(target, ch);
+ }
-err_free_mem:
- srp_free_req_data(target);
+ kfree(target->ch);
err:
scsi_host_put(target_host);
LOCAL_INV_WR_ID_MASK = 1,
FAST_REG_WR_ID_MASK = 2,
+
+ SRP_LAST_WR_ID = 0xfffffffcU,
};
enum srp_target_state {
+ SRP_TARGET_SCANNING,
SRP_TARGET_LIVE,
SRP_TARGET_REMOVED,
};
};
struct srp_request {
- struct list_head list;
struct scsi_cmnd *scmnd;
struct srp_iu *cmd;
union {
struct srp_direct_buf *indirect_desc;
dma_addr_t indirect_dma_addr;
short nmdesc;
- short index;
};
-struct srp_target_port {
+/**
+ * struct srp_rdma_ch
+ * @comp_vector: Completion vector used by this RDMA channel.
+ */
+struct srp_rdma_ch {
/* These are RW in the hot path, and commonly used together */
struct list_head free_tx;
- struct list_head free_reqs;
spinlock_t lock;
s32 req_lim;
/* These are read-only in the hot path */
- struct ib_cq *send_cq ____cacheline_aligned_in_smp;
+ struct srp_target_port *target ____cacheline_aligned_in_smp;
+ struct ib_cq *send_cq;
struct ib_cq *recv_cq;
struct ib_qp *qp;
union {
struct ib_fmr_pool *fmr_pool;
struct srp_fr_pool *fr_pool;
};
+
+ /* Everything above this point is used in the hot path of
+ * command processing. Try to keep them packed into cachelines.
+ */
+
+ struct completion done;
+ int status;
+
+ struct ib_sa_path_rec path;
+ struct ib_sa_query *path_query;
+ int path_query_id;
+
+ struct ib_cm_id *cm_id;
+ struct srp_iu **tx_ring;
+ struct srp_iu **rx_ring;
+ struct srp_request *req_ring;
+ int max_ti_iu_len;
+ int comp_vector;
+
+ struct completion tsk_mgmt_done;
+ u8 tsk_mgmt_status;
+};
+
+/**
+ * struct srp_target_port
+ * @comp_vector: Completion vector used by the first RDMA channel created for
+ * this target port.
+ */
+struct srp_target_port {
+ /* read and written in the hot path */
+ spinlock_t lock;
+
+ /* read only in the hot path */
+ struct srp_rdma_ch *ch;
+ u32 ch_count;
u32 lkey;
u32 rkey;
enum srp_target_state state;
unsigned int indirect_size;
bool allow_ext_sg;
- /* Everything above this point is used in the hot path of
- * command processing. Try to keep them packed into cachelines.
- */
-
+ /* other member variables */
+ union ib_gid sgid;
__be64 id_ext;
__be64 ioc_guid;
__be64 service_id;
int comp_vector;
int tl_retry_count;
- struct ib_sa_path_rec path;
- __be16 orig_dgid[8];
- struct ib_sa_query *path_query;
- int path_query_id;
+ union ib_gid orig_dgid;
+ __be16 pkey;
u32 rq_tmo_jiffies;
bool connected;
- struct ib_cm_id *cm_id;
-
- int max_ti_iu_len;
-
int zero_req_lim;
- struct srp_iu **tx_ring;
- struct srp_iu **rx_ring;
- struct srp_request *req_ring;
-
struct work_struct tl_err_work;
struct work_struct remove_work;
struct list_head list;
- struct completion done;
- int status;
bool qp_in_error;
-
- struct completion tsk_mgmt_done;
- u8 tsk_mgmt_status;
};
struct srp_iu {
if (!qp_init)
goto out;
+retry:
ch->cq = ib_create_cq(sdev->device, srpt_completion, NULL, ch,
ch->rq_size + srp_sq_size, 0);
if (IS_ERR(ch->cq)) {
ch->qp = ib_create_qp(sdev->pd, qp_init);
if (IS_ERR(ch->qp)) {
ret = PTR_ERR(ch->qp);
+ if (ret == -ENOMEM) {
+ srp_sq_size /= 2;
+ if (srp_sq_size >= MIN_SRPT_SQ_SIZE) {
+ ib_destroy_cq(ch->cq);
+ goto retry;
+ }
+ }
printk(KERN_ERR "failed to create_qp ret= %d\n", ret);
goto err_destroy_cq;
}
err_free_client:
evdev_detach_client(evdev, client);
- kfree(client);
+ kvfree(client);
return error;
}
}
ep_irq_in = &intf->cur_altsetting->endpoint[1].desc;
- usb_fill_bulk_urb(xpad->bulk_out, udev,
- usb_sndbulkpipe(udev, ep_irq_in->bEndpointAddress),
- xpad->bdata, XPAD_PKT_LEN, xpad_bulk_out, xpad);
+ if (usb_endpoint_is_bulk_out(ep_irq_in)) {
+ usb_fill_bulk_urb(xpad->bulk_out, udev,
+ usb_sndbulkpipe(udev,
+ ep_irq_in->bEndpointAddress),
+ xpad->bdata, XPAD_PKT_LEN,
+ xpad_bulk_out, xpad);
+ } else {
+ usb_fill_int_urb(xpad->bulk_out, udev,
+ usb_sndintpipe(udev,
+ ep_irq_in->bEndpointAddress),
+ xpad->bdata, XPAD_PKT_LEN,
+ xpad_bulk_out, xpad, 0);
+ }
/*
* Submit the int URB immediately rather than waiting for open
}
platform_set_drvdata(pdev, pwr);
+ device_init_wakeup(&pdev->dev, true);
return 0;
}
{
struct alps_data *priv = psmouse->private;
- if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */
+ /*
+ * Check if we are dealing with a bare PS/2 packet, presumably from
+ * a device connected to the external PS/2 port. Because bare PS/2
+ * protocol does not have enough constant bits to self-synchronize
+ * properly we only do this if the device is fully synchronized.
+ */
+ if (!psmouse->out_of_sync_cnt && (psmouse->packet[0] & 0xc8) == 0x08) {
if (psmouse->pktcnt == 3) {
alps_report_bare_ps2_packet(psmouse, psmouse->packet,
true);
}
/* Bytes 2 - pktsize should have 0 in the highest bit */
- if ((priv->proto_version < ALPS_PROTO_V5) &&
+ if (priv->proto_version < ALPS_PROTO_V5 &&
psmouse->pktcnt >= 2 && psmouse->pktcnt <= psmouse->pktsize &&
(psmouse->packet[psmouse->pktcnt - 1] & 0x80)) {
psmouse_dbg(psmouse, "refusing packet[%i] = %x\n",
psmouse->pktcnt - 1,
psmouse->packet[psmouse->pktcnt - 1]);
+
+ if (priv->proto_version == ALPS_PROTO_V3 &&
+ psmouse->pktcnt == psmouse->pktsize) {
+ /*
+ * Some Dell boxes, such as Latitude E6440 or E7440
+ * with closed lid, quite often smash last byte of
+ * otherwise valid packet with 0xff. Given that the
+ * next packet is very likely to be valid let's
+ * report PSMOUSE_FULL_PACKET but not process data,
+ * rather than reporting PSMOUSE_BAD_DATA and
+ * filling the logs.
+ */
+ return PSMOUSE_FULL_PACKET;
+ }
+
return PSMOUSE_BAD_DATA;
}
/* We are having trouble resyncing ALPS touchpads so disable it for now */
psmouse->resync_time = 0;
+ /* Allow 2 invalid packets without resetting device */
+ psmouse->resetafter = psmouse->pktsize * 2;
+
return 0;
init_fail:
int x, y;
u32 t;
- if (dev_WARN_ONCE(&psmouse->ps2dev.serio->dev,
- !tp_dev,
- psmouse_fmt("Unexpected trackpoint message\n"))) {
- if (etd->debug == 1)
- elantech_packet_dump(psmouse);
- return;
- }
-
t = get_unaligned_le32(&packet[0]);
switch (t & ~7U) {
} else {
input_report_key(dev, BTN_LEFT, packet[0] & 0x01);
input_report_key(dev, BTN_RIGHT, packet[0] & 0x02);
+ input_report_key(dev, BTN_MIDDLE, packet[0] & 0x04);
}
input_mt_report_pointer_emulation(dev, true);
unsigned char packet_type = packet[3] & 0x03;
bool sanity_check;
+ if (etd->tp_dev && (packet[3] & 0x0f) == 0x06)
+ return PACKET_TRACKPOINT;
+
/*
* Sanity check based on the constant bits of a packet.
* The constant bits change depending on the value of
case 4:
packet_type = elantech_packet_check_v4(psmouse);
- if (packet_type == PACKET_UNKNOWN)
+ switch (packet_type) {
+ case PACKET_UNKNOWN:
return PSMOUSE_BAD_DATA;
- elantech_report_absolute_v4(psmouse, packet_type);
+ case PACKET_TRACKPOINT:
+ elantech_report_trackpoint(psmouse, packet_type);
+ break;
+
+ default:
+ elantech_report_absolute_v4(psmouse, packet_type);
+ break;
+ }
+
break;
}
}
}
+/*
+ * Some hw_version 4 models do have a middle button
+ */
+static const struct dmi_system_id elantech_dmi_has_middle_button[] = {
+#if defined(CONFIG_DMI) && defined(CONFIG_X86)
+ {
+ /* Fujitsu H730 has a middle button */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H730"),
+ },
+ },
+#endif
+ { }
+};
+
/*
* Set the appropriate event bits for the input subsystem
*/
__clear_bit(EV_REL, dev->evbit);
__set_bit(BTN_LEFT, dev->keybit);
+ if (dmi_check_system(elantech_dmi_has_middle_button))
+ __set_bit(BTN_MIDDLE, dev->keybit);
__set_bit(BTN_RIGHT, dev->keybit);
__set_bit(BTN_TOUCH, dev->keybit);
ELANTECH_INT_ATTR(reg_26, 0x26);
ELANTECH_INT_ATTR(debug, 0);
ELANTECH_INT_ATTR(paritycheck, 0);
+ELANTECH_INT_ATTR(crc_enabled, 0);
static struct attribute *elantech_attrs[] = {
&psmouse_attr_reg_07.dattr.attr,
&psmouse_attr_reg_26.dattr.attr,
&psmouse_attr_debug.dattr.attr,
&psmouse_attr_paritycheck.dattr.attr,
+ &psmouse_attr_crc_enabled.dattr.attr,
NULL
};
return 0;
}
+/*
+ * Some hw_version 4 models do not work with crc_disabled
+ */
+static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = {
+#if defined(CONFIG_DMI) && defined(CONFIG_X86)
+ {
+ /* Fujitsu H730 does not work with crc_enabled == 0 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H730"),
+ },
+ },
+#endif
+ { }
+};
+
/*
* Some hw_version 3 models go into error state when we try to set
* bit 3 and/or bit 1 of r10.
* The signatures of v3 and v4 packets change depending on the
* value of this hardware flag.
*/
- etd->crc_enabled = ((etd->fw_version & 0x4000) == 0x4000);
+ etd->crc_enabled = (etd->fw_version & 0x4000) == 0x4000 ||
+ dmi_check_system(elantech_dmi_force_crc_enabled);
/* Enable real hardware resolution on hw_version 3 ? */
etd->set_hw_resolution = !dmi_check_system(no_hw_res_dmi_table);
1232, 5710, 1156, 4696
},
{
- (const char * const []){"LEN0034", "LEN0036", "LEN2002",
- "LEN2004", NULL},
+ (const char * const []){"LEN0034", "LEN0036", "LEN0039",
+ "LEN2002", "LEN2004", NULL},
1024, 5112, 2024, 4832
},
{
(const char * const []){"LEN2001", NULL},
1024, 5022, 2508, 4832
},
+ {
+ (const char * const []){"LEN2006", NULL},
+ 1264, 5675, 1171, 4688
+ },
{ }
};
"LEN0036", /* T440 */
"LEN0037",
"LEN0038",
+ "LEN0039", /* T440s */
"LEN0041",
"LEN0042", /* Yoga */
"LEN0045",
#define ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS (0x34)
#define ARMADA_370_XP_INT_SOURCE_CTL(irq) (0x100 + irq*4)
#define ARMADA_370_XP_INT_SOURCE_CPU_MASK 0xF
+#define ARMADA_370_XP_INT_IRQ_FIQ_MASK(cpuid) ((BIT(0) | BIT(8)) << cpuid)
#define ARMADA_370_XP_CPU_INTACK_OFFS (0x44)
#define ARMADA_375_PPI_CAUSE (0x10)
struct irq_desc *desc)
{
struct irq_chip *chip = irq_get_chip(irq);
- unsigned long irqmap, irqn;
+ unsigned long irqmap, irqn, irqsrc, cpuid;
unsigned int cascade_irq;
chained_irq_enter(chip, desc);
irqmap = readl_relaxed(per_cpu_int_base + ARMADA_375_PPI_CAUSE);
-
- if (irqmap & BIT(0)) {
- armada_370_xp_handle_msi_irq(NULL, true);
- irqmap &= ~BIT(0);
- }
+ cpuid = cpu_logical_map(smp_processor_id());
for_each_set_bit(irqn, &irqmap, BITS_PER_LONG) {
+ irqsrc = readl_relaxed(main_int_base +
+ ARMADA_370_XP_INT_SOURCE_CTL(irqn));
+
+ /* Check if the interrupt is not masked on current CPU.
+ * Test IRQ (0-1) and FIQ (8-9) mask bits.
+ */
+ if (!(irqsrc & ARMADA_370_XP_INT_IRQ_FIQ_MASK(cpuid)))
+ continue;
+
+ if (irqn == 1) {
+ armada_370_xp_handle_msi_irq(NULL, true);
+ continue;
+ }
+
cascade_irq = irq_find_mapping(armada_370_xp_mpic_domain, irqn);
generic_handle_irq(cascade_irq);
}
}
ret = irq_alloc_domain_generic_chips(domain, 32, 1, name,
- handle_level_irq, 0, 0,
- IRQCHIP_SKIP_SET_WAKE);
+ handle_fasteoi_irq,
+ IRQ_NOREQUEST | IRQ_NOPROBE |
+ IRQ_NOAUTOEN, 0, 0);
if (ret)
goto err_domain_remove;
gc->unused = 0;
gc->wake_enabled = ~0;
gc->chip_types[0].type = IRQ_TYPE_SENSE_MASK;
- gc->chip_types[0].handler = handle_fasteoi_irq;
gc->chip_types[0].chip.irq_eoi = irq_gc_eoi;
gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake;
gc->chip_types[0].chip.irq_shutdown = aic_common_shutdown;
int parent_irq;
parent_irq = irq_of_parse_and_map(dn, irq);
- if (parent_irq < 0) {
+ if (!parent_irq) {
pr_err("failed to map interrupt %d\n", irq);
- return parent_irq;
+ return -EINVAL;
}
data->irq_map_mask |= be32_to_cpup(map_mask + irq);
__raw_writel(0xffffffff, data->base + CPU_CLEAR);
data->parent_irq = irq_of_parse_and_map(np, 0);
- if (data->parent_irq < 0) {
+ if (!data->parent_irq) {
pr_err("failed to find parent interrupt\n");
- ret = data->parent_irq;
+ ret = -EINVAL;
goto out_unmap;
}
/*----------------------------------------------------------------*/
-struct bucket {
- spinlock_t lock;
- struct hlist_head cells;
-};
+#define MIN_CELLS 1024
struct dm_bio_prison {
+ spinlock_t lock;
mempool_t *cell_pool;
-
- unsigned nr_buckets;
- unsigned hash_mask;
- struct bucket *buckets;
+ struct rb_root cells;
};
-/*----------------------------------------------------------------*/
-
-static uint32_t calc_nr_buckets(unsigned nr_cells)
-{
- uint32_t n = 128;
-
- nr_cells /= 4;
- nr_cells = min(nr_cells, 8192u);
-
- while (n < nr_cells)
- n <<= 1;
-
- return n;
-}
-
static struct kmem_cache *_cell_cache;
-static void init_bucket(struct bucket *b)
-{
- spin_lock_init(&b->lock);
- INIT_HLIST_HEAD(&b->cells);
-}
+/*----------------------------------------------------------------*/
/*
* @nr_cells should be the number of cells you want in use _concurrently_.
* Don't confuse it with the number of distinct keys.
*/
-struct dm_bio_prison *dm_bio_prison_create(unsigned nr_cells)
+struct dm_bio_prison *dm_bio_prison_create(void)
{
- unsigned i;
- uint32_t nr_buckets = calc_nr_buckets(nr_cells);
- size_t len = sizeof(struct dm_bio_prison) +
- (sizeof(struct bucket) * nr_buckets);
- struct dm_bio_prison *prison = kmalloc(len, GFP_KERNEL);
+ struct dm_bio_prison *prison = kmalloc(sizeof(*prison), GFP_KERNEL);
if (!prison)
return NULL;
- prison->cell_pool = mempool_create_slab_pool(nr_cells, _cell_cache);
+ spin_lock_init(&prison->lock);
+
+ prison->cell_pool = mempool_create_slab_pool(MIN_CELLS, _cell_cache);
if (!prison->cell_pool) {
kfree(prison);
return NULL;
}
- prison->nr_buckets = nr_buckets;
- prison->hash_mask = nr_buckets - 1;
- prison->buckets = (struct bucket *) (prison + 1);
- for (i = 0; i < nr_buckets; i++)
- init_bucket(prison->buckets + i);
+ prison->cells = RB_ROOT;
return prison;
}
}
EXPORT_SYMBOL_GPL(dm_bio_prison_free_cell);
-static uint32_t hash_key(struct dm_bio_prison *prison, struct dm_cell_key *key)
+static void __setup_new_cell(struct dm_cell_key *key,
+ struct bio *holder,
+ struct dm_bio_prison_cell *cell)
{
- const unsigned long BIG_PRIME = 4294967291UL;
- uint64_t hash = key->block * BIG_PRIME;
-
- return (uint32_t) (hash & prison->hash_mask);
+ memcpy(&cell->key, key, sizeof(cell->key));
+ cell->holder = holder;
+ bio_list_init(&cell->bios);
}
-static int keys_equal(struct dm_cell_key *lhs, struct dm_cell_key *rhs)
+static int cmp_keys(struct dm_cell_key *lhs,
+ struct dm_cell_key *rhs)
{
- return (lhs->virtual == rhs->virtual) &&
- (lhs->dev == rhs->dev) &&
- (lhs->block == rhs->block);
-}
+ if (lhs->virtual < rhs->virtual)
+ return -1;
-static struct bucket *get_bucket(struct dm_bio_prison *prison,
- struct dm_cell_key *key)
-{
- return prison->buckets + hash_key(prison, key);
-}
+ if (lhs->virtual > rhs->virtual)
+ return 1;
-static struct dm_bio_prison_cell *__search_bucket(struct bucket *b,
- struct dm_cell_key *key)
-{
- struct dm_bio_prison_cell *cell;
+ if (lhs->dev < rhs->dev)
+ return -1;
- hlist_for_each_entry(cell, &b->cells, list)
- if (keys_equal(&cell->key, key))
- return cell;
+ if (lhs->dev > rhs->dev)
+ return 1;
- return NULL;
-}
+ if (lhs->block_end <= rhs->block_begin)
+ return -1;
-static void __setup_new_cell(struct bucket *b,
- struct dm_cell_key *key,
- struct bio *holder,
- struct dm_bio_prison_cell *cell)
-{
- memcpy(&cell->key, key, sizeof(cell->key));
- cell->holder = holder;
- bio_list_init(&cell->bios);
- hlist_add_head(&cell->list, &b->cells);
+ if (lhs->block_begin >= rhs->block_end)
+ return 1;
+
+ return 0;
}
-static int __bio_detain(struct bucket *b,
+static int __bio_detain(struct dm_bio_prison *prison,
struct dm_cell_key *key,
struct bio *inmate,
struct dm_bio_prison_cell *cell_prealloc,
struct dm_bio_prison_cell **cell_result)
{
- struct dm_bio_prison_cell *cell;
-
- cell = __search_bucket(b, key);
- if (cell) {
- if (inmate)
- bio_list_add(&cell->bios, inmate);
- *cell_result = cell;
- return 1;
+ int r;
+ struct rb_node **new = &prison->cells.rb_node, *parent = NULL;
+
+ while (*new) {
+ struct dm_bio_prison_cell *cell =
+ container_of(*new, struct dm_bio_prison_cell, node);
+
+ r = cmp_keys(key, &cell->key);
+
+ parent = *new;
+ if (r < 0)
+ new = &((*new)->rb_left);
+ else if (r > 0)
+ new = &((*new)->rb_right);
+ else {
+ if (inmate)
+ bio_list_add(&cell->bios, inmate);
+ *cell_result = cell;
+ return 1;
+ }
}
- __setup_new_cell(b, key, inmate, cell_prealloc);
+ __setup_new_cell(key, inmate, cell_prealloc);
*cell_result = cell_prealloc;
+
+ rb_link_node(&cell_prealloc->node, parent, new);
+ rb_insert_color(&cell_prealloc->node, &prison->cells);
+
return 0;
}
{
int r;
unsigned long flags;
- struct bucket *b = get_bucket(prison, key);
- spin_lock_irqsave(&b->lock, flags);
- r = __bio_detain(b, key, inmate, cell_prealloc, cell_result);
- spin_unlock_irqrestore(&b->lock, flags);
+ spin_lock_irqsave(&prison->lock, flags);
+ r = __bio_detain(prison, key, inmate, cell_prealloc, cell_result);
+ spin_unlock_irqrestore(&prison->lock, flags);
return r;
}
/*
* @inmates must have been initialised prior to this call
*/
-static void __cell_release(struct dm_bio_prison_cell *cell,
+static void __cell_release(struct dm_bio_prison *prison,
+ struct dm_bio_prison_cell *cell,
struct bio_list *inmates)
{
- hlist_del(&cell->list);
+ rb_erase(&cell->node, &prison->cells);
if (inmates) {
if (cell->holder)
struct bio_list *bios)
{
unsigned long flags;
- struct bucket *b = get_bucket(prison, &cell->key);
- spin_lock_irqsave(&b->lock, flags);
- __cell_release(cell, bios);
- spin_unlock_irqrestore(&b->lock, flags);
+ spin_lock_irqsave(&prison->lock, flags);
+ __cell_release(prison, cell, bios);
+ spin_unlock_irqrestore(&prison->lock, flags);
}
EXPORT_SYMBOL_GPL(dm_cell_release);
/*
* Sometimes we don't want the holder, just the additional bios.
*/
-static void __cell_release_no_holder(struct dm_bio_prison_cell *cell,
+static void __cell_release_no_holder(struct dm_bio_prison *prison,
+ struct dm_bio_prison_cell *cell,
struct bio_list *inmates)
{
- hlist_del(&cell->list);
+ rb_erase(&cell->node, &prison->cells);
bio_list_merge(inmates, &cell->bios);
}
struct bio_list *inmates)
{
unsigned long flags;
- struct bucket *b = get_bucket(prison, &cell->key);
- spin_lock_irqsave(&b->lock, flags);
- __cell_release_no_holder(cell, inmates);
- spin_unlock_irqrestore(&b->lock, flags);
+ spin_lock_irqsave(&prison->lock, flags);
+ __cell_release_no_holder(prison, cell, inmates);
+ spin_unlock_irqrestore(&prison->lock, flags);
}
EXPORT_SYMBOL_GPL(dm_cell_release_no_holder);
}
EXPORT_SYMBOL_GPL(dm_cell_error);
+void dm_cell_visit_release(struct dm_bio_prison *prison,
+ void (*visit_fn)(void *, struct dm_bio_prison_cell *),
+ void *context,
+ struct dm_bio_prison_cell *cell)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&prison->lock, flags);
+ visit_fn(context, cell);
+ rb_erase(&cell->node, &prison->cells);
+ spin_unlock_irqrestore(&prison->lock, flags);
+}
+EXPORT_SYMBOL_GPL(dm_cell_visit_release);
+
/*----------------------------------------------------------------*/
#define DEFERRED_SET_SIZE 64
#include "persistent-data/dm-block-manager.h" /* FIXME: for dm_block_t */
#include "dm-thin-metadata.h" /* FIXME: for dm_thin_id */
-#include <linux/list.h>
#include <linux/bio.h>
+#include <linux/rbtree.h>
/*----------------------------------------------------------------*/
*/
struct dm_bio_prison;
-/* FIXME: this needs to be more abstract */
+/*
+ * Keys define a range of blocks within either a virtual or physical
+ * device.
+ */
struct dm_cell_key {
int virtual;
dm_thin_id dev;
- dm_block_t block;
+ dm_block_t block_begin, block_end;
};
/*
* themselves.
*/
struct dm_bio_prison_cell {
- struct hlist_node list;
+ struct list_head user_list; /* for client use */
+ struct rb_node node;
+
struct dm_cell_key key;
struct bio *holder;
struct bio_list bios;
};
-struct dm_bio_prison *dm_bio_prison_create(unsigned nr_cells);
+struct dm_bio_prison *dm_bio_prison_create(void);
void dm_bio_prison_destroy(struct dm_bio_prison *prison);
/*
struct dm_bio_prison_cell *cell);
/*
- * Creates, or retrieves a cell for the given key.
+ * Creates, or retrieves a cell that overlaps the given key.
*
* Returns 1 if pre-existing cell returned, zero if new cell created using
* @cell_prealloc.
struct dm_bio_prison_cell **cell_result);
/*
- * An atomic op that combines retrieving a cell, and adding a bio to it.
+ * An atomic op that combines retrieving or creating a cell, and adding a
+ * bio to it.
*
* Returns 1 if the cell was already held, 0 if @inmate is the new holder.
*/
void dm_cell_error(struct dm_bio_prison *prison,
struct dm_bio_prison_cell *cell, int error);
+/*
+ * Visits the cell and then releases. Guarantees no new inmates are
+ * inserted between the visit and release.
+ */
+void dm_cell_visit_release(struct dm_bio_prison *prison,
+ void (*visit_fn)(void *, struct dm_bio_prison_cell *),
+ void *context, struct dm_bio_prison_cell *cell);
+
/*----------------------------------------------------------------*/
/*
#include <linux/vmalloc.h>
#include <linux/shrinker.h>
#include <linux/module.h>
+#include <linux/rbtree.h>
#define DM_MSG_PREFIX "bufio"
/*
* Check buffer ages in this interval (seconds)
*/
-#define DM_BUFIO_WORK_TIMER_SECS 10
+#define DM_BUFIO_WORK_TIMER_SECS 30
/*
* Free buffers when they are older than this (seconds)
*/
-#define DM_BUFIO_DEFAULT_AGE_SECS 60
+#define DM_BUFIO_DEFAULT_AGE_SECS 300
/*
- * The number of bvec entries that are embedded directly in the buffer.
- * If the chunk size is larger, dm-io is used to do the io.
+ * The nr of bytes of cached data to keep around.
*/
-#define DM_BUFIO_INLINE_VECS 16
+#define DM_BUFIO_DEFAULT_RETAIN_BYTES (256 * 1024)
/*
- * Buffer hash
+ * The number of bvec entries that are embedded directly in the buffer.
+ * If the chunk size is larger, dm-io is used to do the io.
*/
-#define DM_BUFIO_HASH_BITS 20
-#define DM_BUFIO_HASH(block) \
- ((((block) >> DM_BUFIO_HASH_BITS) ^ (block)) & \
- ((1 << DM_BUFIO_HASH_BITS) - 1))
+#define DM_BUFIO_INLINE_VECS 16
/*
* Don't try to use kmem_cache_alloc for blocks larger than this.
unsigned minimum_buffers;
- struct hlist_head *cache_hash;
+ struct rb_root buffer_tree;
wait_queue_head_t free_buffer_wait;
int async_write_error;
};
struct dm_buffer {
- struct hlist_node hash_list;
+ struct rb_node node;
struct list_head lru_list;
sector_t block;
void *data;
* Buffers are freed after this timeout
*/
static unsigned dm_bufio_max_age = DM_BUFIO_DEFAULT_AGE_SECS;
+static unsigned dm_bufio_retain_bytes = DM_BUFIO_DEFAULT_RETAIN_BYTES;
static unsigned long dm_bufio_peak_allocated;
static unsigned long dm_bufio_allocated_kmem_cache;
*/
static DEFINE_MUTEX(dm_bufio_clients_lock);
+/*----------------------------------------------------------------
+ * A red/black tree acts as an index for all the buffers.
+ *--------------------------------------------------------------*/
+static struct dm_buffer *__find(struct dm_bufio_client *c, sector_t block)
+{
+ struct rb_node *n = c->buffer_tree.rb_node;
+ struct dm_buffer *b;
+
+ while (n) {
+ b = container_of(n, struct dm_buffer, node);
+
+ if (b->block == block)
+ return b;
+
+ n = (b->block < block) ? n->rb_left : n->rb_right;
+ }
+
+ return NULL;
+}
+
+static void __insert(struct dm_bufio_client *c, struct dm_buffer *b)
+{
+ struct rb_node **new = &c->buffer_tree.rb_node, *parent = NULL;
+ struct dm_buffer *found;
+
+ while (*new) {
+ found = container_of(*new, struct dm_buffer, node);
+
+ if (found->block == b->block) {
+ BUG_ON(found != b);
+ return;
+ }
+
+ parent = *new;
+ new = (found->block < b->block) ?
+ &((*new)->rb_left) : &((*new)->rb_right);
+ }
+
+ rb_link_node(&b->node, parent, new);
+ rb_insert_color(&b->node, &c->buffer_tree);
+}
+
+static void __remove(struct dm_bufio_client *c, struct dm_buffer *b)
+{
+ rb_erase(&b->node, &c->buffer_tree);
+}
+
/*----------------------------------------------------------------*/
static void adjust_total_allocated(enum data_mode data_mode, long diff)
b->block = block;
b->list_mode = dirty;
list_add(&b->lru_list, &c->lru[dirty]);
- hlist_add_head(&b->hash_list, &c->cache_hash[DM_BUFIO_HASH(block)]);
+ __insert(b->c, b);
b->last_accessed = jiffies;
}
BUG_ON(!c->n_buffers[b->list_mode]);
c->n_buffers[b->list_mode]--;
- hlist_del(&b->hash_list);
+ __remove(b->c, b);
list_del(&b->lru_list);
}
end_io(&b->bio, r);
}
+static void inline_endio(struct bio *bio, int error)
+{
+ bio_end_io_t *end_fn = bio->bi_private;
+
+ /*
+ * Reset the bio to free any attached resources
+ * (e.g. bio integrity profiles).
+ */
+ bio_reset(bio);
+
+ end_fn(bio, error);
+}
+
static void use_inline_bio(struct dm_buffer *b, int rw, sector_t block,
bio_end_io_t *end_io)
{
b->bio.bi_max_vecs = DM_BUFIO_INLINE_VECS;
b->bio.bi_iter.bi_sector = block << b->c->sectors_per_block_bits;
b->bio.bi_bdev = b->c->bdev;
- b->bio.bi_end_io = end_io;
+ b->bio.bi_end_io = inline_endio;
+ /*
+ * Use of .bi_private isn't a problem here because
+ * the dm_buffer's inline bio is local to bufio.
+ */
+ b->bio.bi_private = end_io;
/*
* We assume that if len >= PAGE_SIZE ptr is page-aligned.
__write_dirty_buffers_async(c, 1, write_list);
}
-/*
- * Find a buffer in the hash.
- */
-static struct dm_buffer *__find(struct dm_bufio_client *c, sector_t block)
-{
- struct dm_buffer *b;
-
- hlist_for_each_entry(b, &c->cache_hash[DM_BUFIO_HASH(block)],
- hash_list) {
- dm_bufio_cond_resched();
- if (b->block == block)
- return b;
- }
-
- return NULL;
-}
-
/*----------------------------------------------------------------
* Getting a buffer
*--------------------------------------------------------------*/
}
/*
- * Test if the buffer is unused and too old, and commit it.
- * At if noio is set, we must not do any I/O because we hold
- * dm_bufio_clients_lock and we would risk deadlock if the I/O gets rerouted to
- * different bufio client.
+ * We may not be able to evict this buffer if IO pending or the client
+ * is still using it. Caller is expected to know buffer is too old.
+ *
+ * And if GFP_NOFS is used, we must not do any I/O because we hold
+ * dm_bufio_clients_lock and we would risk deadlock if the I/O gets
+ * rerouted to different bufio client.
*/
-static int __cleanup_old_buffer(struct dm_buffer *b, gfp_t gfp,
- unsigned long max_jiffies)
+static bool __try_evict_buffer(struct dm_buffer *b, gfp_t gfp)
{
- if (jiffies - b->last_accessed < max_jiffies)
- return 0;
-
- if (!(gfp & __GFP_IO)) {
+ if (!(gfp & __GFP_FS)) {
if (test_bit(B_READING, &b->state) ||
test_bit(B_WRITING, &b->state) ||
test_bit(B_DIRTY, &b->state))
- return 0;
+ return false;
}
if (b->hold_count)
- return 0;
+ return false;
__make_buffer_clean(b);
__unlink_buffer(b);
__free_buffer_wake(b);
- return 1;
+ return true;
}
-static long __scan(struct dm_bufio_client *c, unsigned long nr_to_scan,
- gfp_t gfp_mask)
+static unsigned get_retain_buffers(struct dm_bufio_client *c)
+{
+ unsigned retain_bytes = ACCESS_ONCE(dm_bufio_retain_bytes);
+ return retain_bytes / c->block_size;
+}
+
+static unsigned long __scan(struct dm_bufio_client *c, unsigned long nr_to_scan,
+ gfp_t gfp_mask)
{
int l;
struct dm_buffer *b, *tmp;
- long freed = 0;
+ unsigned long freed = 0;
+ unsigned long count = nr_to_scan;
+ unsigned retain_target = get_retain_buffers(c);
for (l = 0; l < LIST_SIZE; l++) {
list_for_each_entry_safe_reverse(b, tmp, &c->lru[l], lru_list) {
- freed += __cleanup_old_buffer(b, gfp_mask, 0);
- if (!--nr_to_scan)
+ if (__try_evict_buffer(b, gfp_mask))
+ freed++;
+ if (!--nr_to_scan || ((count - freed) <= retain_target))
return freed;
dm_bufio_cond_resched();
}
unsigned long freed;
c = container_of(shrink, struct dm_bufio_client, shrinker);
- if (sc->gfp_mask & __GFP_IO)
+ if (sc->gfp_mask & __GFP_FS)
dm_bufio_lock(c);
else if (!dm_bufio_trylock(c))
return SHRINK_STOP;
unsigned long count;
c = container_of(shrink, struct dm_bufio_client, shrinker);
- if (sc->gfp_mask & __GFP_IO)
+ if (sc->gfp_mask & __GFP_FS)
dm_bufio_lock(c);
else if (!dm_bufio_trylock(c))
return 0;
r = -ENOMEM;
goto bad_client;
}
- c->cache_hash = vmalloc(sizeof(struct hlist_head) << DM_BUFIO_HASH_BITS);
- if (!c->cache_hash) {
- r = -ENOMEM;
- goto bad_hash;
- }
+ c->buffer_tree = RB_ROOT;
c->bdev = bdev;
c->block_size = block_size;
c->n_buffers[i] = 0;
}
- for (i = 0; i < 1 << DM_BUFIO_HASH_BITS; i++)
- INIT_HLIST_HEAD(&c->cache_hash[i]);
-
mutex_init(&c->lock);
INIT_LIST_HEAD(&c->reserved_buffers);
c->need_reserved_buffers = reserved_buffers;
}
dm_io_client_destroy(c->dm_io);
bad_dm_io:
- vfree(c->cache_hash);
-bad_hash:
kfree(c);
bad_client:
return ERR_PTR(r);
mutex_unlock(&dm_bufio_clients_lock);
- for (i = 0; i < 1 << DM_BUFIO_HASH_BITS; i++)
- BUG_ON(!hlist_empty(&c->cache_hash[i]));
-
+ BUG_ON(!RB_EMPTY_ROOT(&c->buffer_tree));
BUG_ON(c->need_reserved_buffers);
while (!list_empty(&c->reserved_buffers)) {
BUG_ON(c->n_buffers[i]);
dm_io_client_destroy(c->dm_io);
- vfree(c->cache_hash);
kfree(c);
}
EXPORT_SYMBOL_GPL(dm_bufio_client_destroy);
-static void cleanup_old_buffers(void)
+static unsigned get_max_age_hz(void)
{
- unsigned long max_age = ACCESS_ONCE(dm_bufio_max_age);
- struct dm_bufio_client *c;
+ unsigned max_age = ACCESS_ONCE(dm_bufio_max_age);
- if (max_age > ULONG_MAX / HZ)
- max_age = ULONG_MAX / HZ;
+ if (max_age > UINT_MAX / HZ)
+ max_age = UINT_MAX / HZ;
- mutex_lock(&dm_bufio_clients_lock);
- list_for_each_entry(c, &dm_bufio_all_clients, client_list) {
- if (!dm_bufio_trylock(c))
- continue;
+ return max_age * HZ;
+}
- while (!list_empty(&c->lru[LIST_CLEAN])) {
- struct dm_buffer *b;
- b = list_entry(c->lru[LIST_CLEAN].prev,
- struct dm_buffer, lru_list);
- if (!__cleanup_old_buffer(b, 0, max_age * HZ))
- break;
- dm_bufio_cond_resched();
- }
+static bool older_than(struct dm_buffer *b, unsigned long age_hz)
+{
+ return (jiffies - b->last_accessed) >= age_hz;
+}
+
+static void __evict_old_buffers(struct dm_bufio_client *c, unsigned long age_hz)
+{
+ struct dm_buffer *b, *tmp;
+ unsigned retain_target = get_retain_buffers(c);
+ unsigned count;
+
+ dm_bufio_lock(c);
+
+ count = c->n_buffers[LIST_CLEAN] + c->n_buffers[LIST_DIRTY];
+ list_for_each_entry_safe_reverse(b, tmp, &c->lru[LIST_CLEAN], lru_list) {
+ if (count <= retain_target)
+ break;
+
+ if (!older_than(b, age_hz))
+ break;
+
+ if (__try_evict_buffer(b, 0))
+ count--;
- dm_bufio_unlock(c);
dm_bufio_cond_resched();
}
+
+ dm_bufio_unlock(c);
+}
+
+static void cleanup_old_buffers(void)
+{
+ unsigned long max_age_hz = get_max_age_hz();
+ struct dm_bufio_client *c;
+
+ mutex_lock(&dm_bufio_clients_lock);
+
+ list_for_each_entry(c, &dm_bufio_all_clients, client_list)
+ __evict_old_buffers(c, max_age_hz);
+
mutex_unlock(&dm_bufio_clients_lock);
}
module_param_named(max_age_seconds, dm_bufio_max_age, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(max_age_seconds, "Max age of a buffer in seconds");
+module_param_named(retain_bytes, dm_bufio_retain_bytes, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(retain_bytes, "Try to keep at least this many bytes cached in memory");
+
module_param_named(peak_allocated_bytes, dm_bufio_peak_allocated, ulong, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(peak_allocated_bytes, "Tracks the maximum allocated memory");
typedef dm_block_t __bitwise__ dm_oblock_t;
typedef uint32_t __bitwise__ dm_cblock_t;
+typedef dm_block_t __bitwise__ dm_dblock_t;
static inline dm_oblock_t to_oblock(dm_block_t b)
{
return (__force uint32_t) b;
}
+static inline dm_dblock_t to_dblock(dm_block_t b)
+{
+ return (__force dm_dblock_t) b;
+}
+
+static inline dm_block_t from_dblock(dm_dblock_t b)
+{
+ return (__force dm_block_t) b;
+}
+
#endif /* DM_CACHE_BLOCK_TYPES_H */
dm_block_t discard_root;
sector_t discard_block_size;
- dm_oblock_t discard_nr_blocks;
+ dm_dblock_t discard_nr_blocks;
sector_t data_block_size;
dm_cblock_t cache_blocks;
disk_super->hint_root = cpu_to_le64(cmd->hint_root);
disk_super->discard_root = cpu_to_le64(cmd->discard_root);
disk_super->discard_block_size = cpu_to_le64(cmd->discard_block_size);
- disk_super->discard_nr_blocks = cpu_to_le64(from_oblock(cmd->discard_nr_blocks));
+ disk_super->discard_nr_blocks = cpu_to_le64(from_dblock(cmd->discard_nr_blocks));
disk_super->metadata_block_size = cpu_to_le32(DM_CACHE_METADATA_BLOCK_SIZE);
disk_super->data_block_size = cpu_to_le32(cmd->data_block_size);
disk_super->cache_blocks = cpu_to_le32(0);
cmd->hint_root = le64_to_cpu(disk_super->hint_root);
cmd->discard_root = le64_to_cpu(disk_super->discard_root);
cmd->discard_block_size = le64_to_cpu(disk_super->discard_block_size);
- cmd->discard_nr_blocks = to_oblock(le64_to_cpu(disk_super->discard_nr_blocks));
+ cmd->discard_nr_blocks = to_dblock(le64_to_cpu(disk_super->discard_nr_blocks));
cmd->data_block_size = le32_to_cpu(disk_super->data_block_size);
cmd->cache_blocks = to_cblock(le32_to_cpu(disk_super->cache_blocks));
strncpy(cmd->policy_name, disk_super->policy_name, sizeof(cmd->policy_name));
disk_super->hint_root = cpu_to_le64(cmd->hint_root);
disk_super->discard_root = cpu_to_le64(cmd->discard_root);
disk_super->discard_block_size = cpu_to_le64(cmd->discard_block_size);
- disk_super->discard_nr_blocks = cpu_to_le64(from_oblock(cmd->discard_nr_blocks));
+ disk_super->discard_nr_blocks = cpu_to_le64(from_dblock(cmd->discard_nr_blocks));
disk_super->cache_blocks = cpu_to_le32(from_cblock(cmd->cache_blocks));
strncpy(disk_super->policy_name, cmd->policy_name, sizeof(disk_super->policy_name));
disk_super->policy_version[0] = cpu_to_le32(cmd->policy_version[0]);
int dm_cache_discard_bitset_resize(struct dm_cache_metadata *cmd,
sector_t discard_block_size,
- dm_oblock_t new_nr_entries)
+ dm_dblock_t new_nr_entries)
{
int r;
down_write(&cmd->root_lock);
r = dm_bitset_resize(&cmd->discard_info,
cmd->discard_root,
- from_oblock(cmd->discard_nr_blocks),
- from_oblock(new_nr_entries),
+ from_dblock(cmd->discard_nr_blocks),
+ from_dblock(new_nr_entries),
false, &cmd->discard_root);
if (!r) {
cmd->discard_block_size = discard_block_size;
return r;
}
-static int __set_discard(struct dm_cache_metadata *cmd, dm_oblock_t b)
+static int __set_discard(struct dm_cache_metadata *cmd, dm_dblock_t b)
{
return dm_bitset_set_bit(&cmd->discard_info, cmd->discard_root,
- from_oblock(b), &cmd->discard_root);
+ from_dblock(b), &cmd->discard_root);
}
-static int __clear_discard(struct dm_cache_metadata *cmd, dm_oblock_t b)
+static int __clear_discard(struct dm_cache_metadata *cmd, dm_dblock_t b)
{
return dm_bitset_clear_bit(&cmd->discard_info, cmd->discard_root,
- from_oblock(b), &cmd->discard_root);
+ from_dblock(b), &cmd->discard_root);
}
-static int __is_discarded(struct dm_cache_metadata *cmd, dm_oblock_t b,
+static int __is_discarded(struct dm_cache_metadata *cmd, dm_dblock_t b,
bool *is_discarded)
{
return dm_bitset_test_bit(&cmd->discard_info, cmd->discard_root,
- from_oblock(b), &cmd->discard_root,
+ from_dblock(b), &cmd->discard_root,
is_discarded);
}
static int __discard(struct dm_cache_metadata *cmd,
- dm_oblock_t dblock, bool discard)
+ dm_dblock_t dblock, bool discard)
{
int r;
}
int dm_cache_set_discard(struct dm_cache_metadata *cmd,
- dm_oblock_t dblock, bool discard)
+ dm_dblock_t dblock, bool discard)
{
int r;
dm_block_t b;
bool discard;
- for (b = 0; b < from_oblock(cmd->discard_nr_blocks); b++) {
- dm_oblock_t dblock = to_oblock(b);
+ for (b = 0; b < from_dblock(cmd->discard_nr_blocks); b++) {
+ dm_dblock_t dblock = to_dblock(b);
if (cmd->clean_when_opened) {
r = __is_discarded(cmd, dblock, &discard);
int dm_cache_discard_bitset_resize(struct dm_cache_metadata *cmd,
sector_t discard_block_size,
- dm_oblock_t new_nr_entries);
+ dm_dblock_t new_nr_entries);
typedef int (*load_discard_fn)(void *context, sector_t discard_block_size,
- dm_oblock_t dblock, bool discarded);
+ dm_dblock_t dblock, bool discarded);
int dm_cache_load_discards(struct dm_cache_metadata *cmd,
load_discard_fn fn, void *context);
-int dm_cache_set_discard(struct dm_cache_metadata *cmd, dm_oblock_t dblock, bool discard);
+int dm_cache_set_discard(struct dm_cache_metadata *cmd, dm_dblock_t dblock, bool discard);
int dm_cache_remove_mapping(struct dm_cache_metadata *cmd, dm_cblock_t cblock);
int dm_cache_insert_mapping(struct dm_cache_metadata *cmd, dm_cblock_t cblock, dm_oblock_t oblock);
* Gives us the oldest entry of the lowest popoulated level. If the first
* level is emptied then we shift down one level.
*/
-static struct list_head *queue_pop(struct queue *q)
+static struct list_head *queue_peek(struct queue *q)
{
unsigned level;
- struct list_head *r;
for (level = 0; level < NR_QUEUE_LEVELS; level++)
- if (!list_empty(q->qs + level)) {
- r = q->qs[level].next;
- list_del(r);
+ if (!list_empty(q->qs + level))
+ return q->qs[level].next;
- /* have we just emptied the bottom level? */
- if (level == 0 && list_empty(q->qs))
- queue_shift_down(q);
+ return NULL;
+}
- return r;
- }
+static struct list_head *queue_pop(struct queue *q)
+{
+ struct list_head *r = queue_peek(q);
- return NULL;
+ if (r) {
+ list_del(r);
+
+ /* have we just emptied the bottom level? */
+ if (list_empty(q->qs))
+ queue_shift_down(q);
+ }
+
+ return r;
}
static struct list_head *list_pop(struct list_head *lh)
unsigned generation;
unsigned generation_period; /* in lookups (will probably change) */
- /*
- * Entries in the pre_cache whose hit count passes the promotion
- * threshold move to the cache proper. Working out the correct
- * value for the promotion_threshold is crucial to this policy.
- */
- unsigned promote_threshold;
-
unsigned discard_promote_adjustment;
unsigned read_promote_adjustment;
unsigned write_promote_adjustment;
#define DEFAULT_DISCARD_PROMOTE_ADJUSTMENT 1
#define DEFAULT_READ_PROMOTE_ADJUSTMENT 4
#define DEFAULT_WRITE_PROMOTE_ADJUSTMENT 8
+#define DISCOURAGE_DEMOTING_DIRTY_THRESHOLD 128
/*----------------------------------------------------------------*/
return e;
}
+static struct entry *peek(struct queue *q)
+{
+ struct list_head *h = queue_peek(q);
+ return h ? container_of(h, struct entry, list) : NULL;
+}
+
/*
* Has this entry already been updated?
*/
break;
}
}
-
- mq->promote_threshold = nr ? total / nr : 1;
- if (mq->promote_threshold * nr < total)
- mq->promote_threshold++;
}
}
return 0;
}
+/*
+ * Entries in the pre_cache whose hit count passes the promotion
+ * threshold move to the cache proper. Working out the correct
+ * value for the promotion_threshold is crucial to this policy.
+ */
+static unsigned promote_threshold(struct mq_policy *mq)
+{
+ struct entry *e;
+
+ if (any_free_cblocks(mq))
+ return 0;
+
+ e = peek(&mq->cache_clean);
+ if (e)
+ return e->hit_count;
+
+ e = peek(&mq->cache_dirty);
+ if (e)
+ return e->hit_count + DISCOURAGE_DEMOTING_DIRTY_THRESHOLD;
+
+ /* This should never happen */
+ return 0;
+}
+
/*
* We modify the basic promotion_threshold depending on the specific io.
*
bool discarded_oblock, int data_dir)
{
if (data_dir == READ)
- return mq->promote_threshold + mq->read_promote_adjustment;
+ return promote_threshold(mq) + mq->read_promote_adjustment;
if (discarded_oblock && (any_free_cblocks(mq) || any_clean_cblocks(mq))) {
/*
return mq->discard_promote_adjustment;
}
- return mq->promote_threshold + mq->write_promote_adjustment;
+ return promote_threshold(mq) + mq->write_promote_adjustment;
}
static bool should_promote(struct mq_policy *mq, struct entry *e,
if (e && in_cache(mq, e))
r = cache_entry_found(mq, e, result);
- else if (iot_pattern(&mq->tracker) == PATTERN_SEQUENTIAL)
+ else if (mq->tracker.thresholds[PATTERN_SEQUENTIAL] &&
+ iot_pattern(&mq->tracker) == PATTERN_SEQUENTIAL)
result->op = POLICY_MISS;
else if (e)
mq->tick = 0;
mq->hit_count = 0;
mq->generation = 0;
- mq->promote_threshold = 0;
mq->discard_promote_adjustment = DEFAULT_DISCARD_PROMOTE_ADJUSTMENT;
mq->read_promote_adjustment = DEFAULT_READ_PROMOTE_ADJUSTMENT;
mq->write_promote_adjustment = DEFAULT_WRITE_PROMOTE_ADJUSTMENT;
static struct dm_cache_policy_type mq_policy_type = {
.name = "mq",
- .version = {1, 2, 0},
+ .version = {1, 3, 0},
.hint_size = 4,
.owner = THIS_MODULE,
.create = mq_create
static struct dm_cache_policy_type default_policy_type = {
.name = "default",
- .version = {1, 2, 0},
+ .version = {1, 3, 0},
.hint_size = 4,
.owner = THIS_MODULE,
.create = mq_create,
/*----------------------------------------------------------------*/
-#define PRISON_CELLS 1024
#define MIGRATION_POOL_SIZE 128
#define COMMIT_PERIOD HZ
#define MIGRATION_COUNT_WINDOW 10
/*
* origin_blocks entries, discarded if set.
*/
- dm_oblock_t discard_nr_blocks;
+ dm_dblock_t discard_nr_blocks;
unsigned long *discard_bitset;
+ uint32_t discard_block_size; /* a power of 2 times sectors per block */
/*
* Rather than reconstructing the table line for the status we just
dm_cblock_t cblock;
bool err:1;
+ bool discard:1;
bool writeback:1;
bool demote:1;
bool promote:1;
/*----------------------------------------------------------------*/
-static void build_key(dm_oblock_t oblock, struct dm_cell_key *key)
+static void build_key(dm_oblock_t begin, dm_oblock_t end, struct dm_cell_key *key)
{
key->virtual = 0;
key->dev = 0;
- key->block = from_oblock(oblock);
+ key->block_begin = from_oblock(begin);
+ key->block_end = from_oblock(end);
}
/*
*/
typedef void (*cell_free_fn)(void *context, struct dm_bio_prison_cell *cell);
-static int bio_detain(struct cache *cache, dm_oblock_t oblock,
- struct bio *bio, struct dm_bio_prison_cell *cell_prealloc,
- cell_free_fn free_fn, void *free_context,
- struct dm_bio_prison_cell **cell_result)
+static int bio_detain_range(struct cache *cache, dm_oblock_t oblock_begin, dm_oblock_t oblock_end,
+ struct bio *bio, struct dm_bio_prison_cell *cell_prealloc,
+ cell_free_fn free_fn, void *free_context,
+ struct dm_bio_prison_cell **cell_result)
{
int r;
struct dm_cell_key key;
- build_key(oblock, &key);
+ build_key(oblock_begin, oblock_end, &key);
r = dm_bio_detain(cache->prison, &key, bio, cell_prealloc, cell_result);
if (r)
free_fn(free_context, cell_prealloc);
return r;
}
+static int bio_detain(struct cache *cache, dm_oblock_t oblock,
+ struct bio *bio, struct dm_bio_prison_cell *cell_prealloc,
+ cell_free_fn free_fn, void *free_context,
+ struct dm_bio_prison_cell **cell_result)
+{
+ dm_oblock_t end = to_oblock(from_oblock(oblock) + 1ULL);
+ return bio_detain_range(cache, oblock, end, bio,
+ cell_prealloc, free_fn, free_context, cell_result);
+}
+
static int get_cell(struct cache *cache,
dm_oblock_t oblock,
struct prealloc *structs,
cell_prealloc = prealloc_get_cell(structs);
- build_key(oblock, &key);
+ build_key(oblock, to_oblock(from_oblock(oblock) + 1ULL), &key);
r = dm_get_cell(cache->prison, &key, cell_prealloc, cell_result);
if (r)
prealloc_put_cell(structs, cell_prealloc);
return b;
}
-static void set_discard(struct cache *cache, dm_oblock_t b)
+static dm_block_t oblocks_per_dblock(struct cache *cache)
+{
+ dm_block_t oblocks = cache->discard_block_size;
+
+ if (block_size_is_power_of_two(cache))
+ oblocks >>= cache->sectors_per_block_shift;
+ else
+ oblocks = block_div(oblocks, cache->sectors_per_block);
+
+ return oblocks;
+}
+
+static dm_dblock_t oblock_to_dblock(struct cache *cache, dm_oblock_t oblock)
+{
+ return to_dblock(block_div(from_oblock(oblock),
+ oblocks_per_dblock(cache)));
+}
+
+static dm_oblock_t dblock_to_oblock(struct cache *cache, dm_dblock_t dblock)
+{
+ return to_oblock(from_dblock(dblock) * oblocks_per_dblock(cache));
+}
+
+static void set_discard(struct cache *cache, dm_dblock_t b)
{
unsigned long flags;
+ BUG_ON(from_dblock(b) >= from_dblock(cache->discard_nr_blocks));
atomic_inc(&cache->stats.discard_count);
spin_lock_irqsave(&cache->lock, flags);
- set_bit(from_oblock(b), cache->discard_bitset);
+ set_bit(from_dblock(b), cache->discard_bitset);
spin_unlock_irqrestore(&cache->lock, flags);
}
-static void clear_discard(struct cache *cache, dm_oblock_t b)
+static void clear_discard(struct cache *cache, dm_dblock_t b)
{
unsigned long flags;
spin_lock_irqsave(&cache->lock, flags);
- clear_bit(from_oblock(b), cache->discard_bitset);
+ clear_bit(from_dblock(b), cache->discard_bitset);
spin_unlock_irqrestore(&cache->lock, flags);
}
-static bool is_discarded(struct cache *cache, dm_oblock_t b)
+static bool is_discarded(struct cache *cache, dm_dblock_t b)
{
int r;
unsigned long flags;
spin_lock_irqsave(&cache->lock, flags);
- r = test_bit(from_oblock(b), cache->discard_bitset);
+ r = test_bit(from_dblock(b), cache->discard_bitset);
spin_unlock_irqrestore(&cache->lock, flags);
return r;
unsigned long flags;
spin_lock_irqsave(&cache->lock, flags);
- r = test_bit(from_oblock(b), cache->discard_bitset);
+ r = test_bit(from_dblock(oblock_to_dblock(cache, b)),
+ cache->discard_bitset);
spin_unlock_irqrestore(&cache->lock, flags);
return r;
check_if_tick_bio_needed(cache, bio);
remap_to_origin(cache, bio);
if (bio_data_dir(bio) == WRITE)
- clear_discard(cache, oblock);
+ clear_discard(cache, oblock_to_dblock(cache, oblock));
}
static void remap_to_cache_dirty(struct cache *cache, struct bio *bio,
remap_to_cache(cache, bio, cblock);
if (bio_data_dir(bio) == WRITE) {
set_dirty(cache, oblock, cblock);
- clear_discard(cache, oblock);
+ clear_discard(cache, oblock_to_dblock(cache, oblock));
}
}
}
} else {
- clear_dirty(cache, mg->new_oblock, mg->cblock);
- if (mg->requeue_holder)
+ if (mg->requeue_holder) {
+ clear_dirty(cache, mg->new_oblock, mg->cblock);
cell_defer(cache, mg->new_ocell, true);
- else {
+ } else {
+ /*
+ * The block was promoted via an overwrite, so it's dirty.
+ */
+ set_dirty(cache, mg->new_oblock, mg->cblock);
bio_endio(mg->new_ocell->holder, 0);
cell_defer(cache, mg->new_ocell, false);
}
wake_worker(cache);
}
-static void issue_copy_real(struct dm_cache_migration *mg)
+static void issue_copy(struct dm_cache_migration *mg)
{
int r;
struct dm_io_region o_region, c_region;
migration_success_pre_commit(mg);
}
-static void issue_copy(struct dm_cache_migration *mg)
+static void calc_discard_block_range(struct cache *cache, struct bio *bio,
+ dm_dblock_t *b, dm_dblock_t *e)
+{
+ sector_t sb = bio->bi_iter.bi_sector;
+ sector_t se = bio_end_sector(bio);
+
+ *b = to_dblock(dm_sector_div_up(sb, cache->discard_block_size));
+
+ if (se - sb < cache->discard_block_size)
+ *e = *b;
+ else
+ *e = to_dblock(block_div(se, cache->discard_block_size));
+}
+
+static void issue_discard(struct dm_cache_migration *mg)
+{
+ dm_dblock_t b, e;
+ struct bio *bio = mg->new_ocell->holder;
+
+ calc_discard_block_range(mg->cache, bio, &b, &e);
+ while (b != e) {
+ set_discard(mg->cache, b);
+ b = to_dblock(from_dblock(b) + 1);
+ }
+
+ bio_endio(bio, 0);
+ cell_defer(mg->cache, mg->new_ocell, false);
+ free_migration(mg);
+}
+
+static void issue_copy_or_discard(struct dm_cache_migration *mg)
{
bool avoid;
struct cache *cache = mg->cache;
+ if (mg->discard) {
+ issue_discard(mg);
+ return;
+ }
+
if (mg->writeback || mg->demote)
avoid = !is_dirty(cache, mg->cblock) ||
is_discarded_oblock(cache, mg->old_oblock);
avoid = is_discarded_oblock(cache, mg->new_oblock);
- if (!avoid && bio_writes_complete_block(cache, bio)) {
+ if (writeback_mode(&cache->features) &&
+ !avoid && bio_writes_complete_block(cache, bio)) {
issue_overwrite(mg, bio);
return;
}
}
- avoid ? avoid_copy(mg) : issue_copy_real(mg);
+ avoid ? avoid_copy(mg) : issue_copy(mg);
}
static void complete_migration(struct dm_cache_migration *mg)
struct dm_cache_migration *mg = prealloc_get_migration(structs);
mg->err = false;
+ mg->discard = false;
mg->writeback = false;
mg->demote = false;
mg->promote = true;
struct dm_cache_migration *mg = prealloc_get_migration(structs);
mg->err = false;
+ mg->discard = false;
mg->writeback = true;
mg->demote = false;
mg->promote = false;
struct dm_cache_migration *mg = prealloc_get_migration(structs);
mg->err = false;
+ mg->discard = false;
mg->writeback = false;
mg->demote = true;
mg->promote = true;
struct dm_cache_migration *mg = prealloc_get_migration(structs);
mg->err = false;
+ mg->discard = false;
mg->writeback = false;
mg->demote = true;
mg->promote = false;
quiesce_migration(mg);
}
+static void discard(struct cache *cache, struct prealloc *structs,
+ struct dm_bio_prison_cell *cell)
+{
+ struct dm_cache_migration *mg = prealloc_get_migration(structs);
+
+ mg->err = false;
+ mg->discard = true;
+ mg->writeback = false;
+ mg->demote = false;
+ mg->promote = false;
+ mg->requeue_holder = false;
+ mg->invalidate = false;
+ mg->cache = cache;
+ mg->old_ocell = NULL;
+ mg->new_ocell = cell;
+ mg->start_jiffies = jiffies;
+
+ quiesce_migration(mg);
+}
+
/*----------------------------------------------------------------
* bio processing
*--------------------------------------------------------------*/
issue(cache, bio);
}
-/*
- * People generally discard large parts of a device, eg, the whole device
- * when formatting. Splitting these large discards up into cache block
- * sized ios and then quiescing (always neccessary for discard) takes too
- * long.
- *
- * We keep it simple, and allow any size of discard to come in, and just
- * mark off blocks on the discard bitset. No passdown occurs!
- *
- * To implement passdown we need to change the bio_prison such that a cell
- * can have a key that spans many blocks.
- */
-static void process_discard_bio(struct cache *cache, struct bio *bio)
+static void process_discard_bio(struct cache *cache, struct prealloc *structs,
+ struct bio *bio)
{
- dm_block_t start_block = dm_sector_div_up(bio->bi_iter.bi_sector,
- cache->sectors_per_block);
- dm_block_t end_block = bio_end_sector(bio);
- dm_block_t b;
+ int r;
+ dm_dblock_t b, e;
+ struct dm_bio_prison_cell *cell_prealloc, *new_ocell;
- end_block = block_div(end_block, cache->sectors_per_block);
+ calc_discard_block_range(cache, bio, &b, &e);
+ if (b == e) {
+ bio_endio(bio, 0);
+ return;
+ }
- for (b = start_block; b < end_block; b++)
- set_discard(cache, to_oblock(b));
+ cell_prealloc = prealloc_get_cell(structs);
+ r = bio_detain_range(cache, dblock_to_oblock(cache, b), dblock_to_oblock(cache, e), bio, cell_prealloc,
+ (cell_free_fn) prealloc_put_cell,
+ structs, &new_ocell);
+ if (r > 0)
+ return;
- bio_endio(bio, 0);
+ discard(cache, structs, new_ocell);
}
static bool spare_migration_bandwidth(struct cache *cache)
dm_oblock_t block = get_bio_block(cache, bio);
struct dm_bio_prison_cell *cell_prealloc, *old_ocell, *new_ocell;
struct policy_result lookup_result;
- bool discarded_block = is_discarded_oblock(cache, block);
bool passthrough = passthrough_mode(&cache->features);
- bool can_migrate = !passthrough && (discarded_block || spare_migration_bandwidth(cache));
+ bool discarded_block, can_migrate;
/*
* Check to see if that block is currently migrating.
if (r > 0)
return;
+ discarded_block = is_discarded_oblock(cache, block);
+ can_migrate = !passthrough && (discarded_block || spare_migration_bandwidth(cache));
+
r = policy_map(cache->policy, block, true, can_migrate, discarded_block,
bio, &lookup_result);
if (bio->bi_rw & REQ_FLUSH)
process_flush_bio(cache, bio);
else if (bio->bi_rw & REQ_DISCARD)
- process_discard_bio(cache, bio);
+ process_discard_bio(cache, &structs, bio);
else
process_bio(cache, &structs, bio);
}
process_invalidation_requests(cache);
}
- process_migrations(cache, &cache->quiesced_migrations, issue_copy);
+ process_migrations(cache, &cache->quiesced_migrations, issue_copy_or_discard);
process_migrations(cache, &cache->completed_migrations, complete_migration);
if (commit_if_needed(cache)) {
return 0;
}
+/*
+ * We want the discard block size to be at least the size of the cache
+ * block size and have no more than 2^14 discard blocks across the origin.
+ */
+#define MAX_DISCARD_BLOCKS (1 << 14)
+
+static bool too_many_discard_blocks(sector_t discard_block_size,
+ sector_t origin_size)
+{
+ (void) sector_div(origin_size, discard_block_size);
+
+ return origin_size > MAX_DISCARD_BLOCKS;
+}
+
+static sector_t calculate_discard_block_size(sector_t cache_block_size,
+ sector_t origin_size)
+{
+ sector_t discard_block_size = cache_block_size;
+
+ if (origin_size)
+ while (too_many_discard_blocks(discard_block_size, origin_size))
+ discard_block_size *= 2;
+
+ return discard_block_size;
+}
+
+static void set_cache_size(struct cache *cache, dm_cblock_t size)
+{
+ dm_block_t nr_blocks = from_cblock(size);
+
+ if (nr_blocks > (1 << 20) && cache->cache_size != size)
+ DMWARN_LIMIT("You have created a cache device with a lot of individual cache blocks (%llu)\n"
+ "All these mappings can consume a lot of kernel memory, and take some time to read/write.\n"
+ "Please consider increasing the cache block size to reduce the overall cache block count.",
+ (unsigned long long) nr_blocks);
+
+ cache->cache_size = size;
+}
+
#define DEFAULT_MIGRATION_THRESHOLD 2048
static int cache_create(struct cache_args *ca, struct cache **result)
ti->num_discard_bios = 1;
ti->discards_supported = true;
ti->discard_zeroes_data_unsupported = true;
- /* Discard bios must be split on a block boundary */
- ti->split_discard_bios = true;
+ ti->split_discard_bios = false;
cache->features = ca->features;
ti->per_bio_data_size = get_per_bio_data_size(cache);
cache->sectors_per_block_shift = -1;
cache_size = block_div(cache_size, ca->block_size);
- cache->cache_size = to_cblock(cache_size);
+ set_cache_size(cache, to_cblock(cache_size));
} else {
cache->sectors_per_block_shift = __ffs(ca->block_size);
- cache->cache_size = to_cblock(ca->cache_sectors >> cache->sectors_per_block_shift);
+ set_cache_size(cache, to_cblock(ca->cache_sectors >> cache->sectors_per_block_shift));
}
r = create_cache_policy(cache, ca, error);
}
clear_bitset(cache->dirty_bitset, from_cblock(cache->cache_size));
- cache->discard_nr_blocks = cache->origin_blocks;
- cache->discard_bitset = alloc_bitset(from_oblock(cache->discard_nr_blocks));
+ cache->discard_block_size =
+ calculate_discard_block_size(cache->sectors_per_block,
+ cache->origin_sectors);
+ cache->discard_nr_blocks = to_dblock(dm_sector_div_up(cache->origin_sectors,
+ cache->discard_block_size));
+ cache->discard_bitset = alloc_bitset(from_dblock(cache->discard_nr_blocks));
if (!cache->discard_bitset) {
*error = "could not allocate discard bitset";
goto bad;
}
- clear_bitset(cache->discard_bitset, from_oblock(cache->discard_nr_blocks));
+ clear_bitset(cache->discard_bitset, from_dblock(cache->discard_nr_blocks));
cache->copier = dm_kcopyd_client_create(&dm_kcopyd_throttle);
if (IS_ERR(cache->copier)) {
INIT_DELAYED_WORK(&cache->waker, do_waker);
cache->last_commit_jiffies = jiffies;
- cache->prison = dm_bio_prison_create(PRISON_CELLS);
+ cache->prison = dm_bio_prison_create();
if (!cache->prison) {
*error = "could not create bio prison";
goto bad;
static int cache_map(struct dm_target *ti, struct bio *bio)
{
int r;
- struct dm_bio_prison_cell *cell;
+ struct dm_bio_prison_cell *cell = NULL;
struct cache *cache = ti->private;
r = __cache_map(cache, bio, &cell);
- if (r == DM_MAPIO_REMAPPED) {
+ if (r == DM_MAPIO_REMAPPED && cell) {
inc_ds(cache, bio, cell);
cell_defer(cache, cell, false);
}
{
unsigned i, r;
- r = dm_cache_discard_bitset_resize(cache->cmd, cache->sectors_per_block,
- cache->origin_blocks);
+ r = dm_cache_discard_bitset_resize(cache->cmd, cache->discard_block_size,
+ cache->discard_nr_blocks);
if (r) {
DMERR("could not resize on-disk discard bitset");
return r;
}
- for (i = 0; i < from_oblock(cache->discard_nr_blocks); i++) {
- r = dm_cache_set_discard(cache->cmd, to_oblock(i),
- is_discarded(cache, to_oblock(i)));
+ for (i = 0; i < from_dblock(cache->discard_nr_blocks); i++) {
+ r = dm_cache_set_discard(cache->cmd, to_dblock(i),
+ is_discarded(cache, to_dblock(i)));
if (r)
return r;
}
return 0;
}
+/*
+ * The discard block size in the on disk metadata is not
+ * neccessarily the same as we're currently using. So we have to
+ * be careful to only set the discarded attribute if we know it
+ * covers a complete block of the new size.
+ */
+struct discard_load_info {
+ struct cache *cache;
+
+ /*
+ * These blocks are sized using the on disk dblock size, rather
+ * than the current one.
+ */
+ dm_block_t block_size;
+ dm_block_t discard_begin, discard_end;
+};
+
+static void discard_load_info_init(struct cache *cache,
+ struct discard_load_info *li)
+{
+ li->cache = cache;
+ li->discard_begin = li->discard_end = 0;
+}
+
+static void set_discard_range(struct discard_load_info *li)
+{
+ sector_t b, e;
+
+ if (li->discard_begin == li->discard_end)
+ return;
+
+ /*
+ * Convert to sectors.
+ */
+ b = li->discard_begin * li->block_size;
+ e = li->discard_end * li->block_size;
+
+ /*
+ * Then convert back to the current dblock size.
+ */
+ b = dm_sector_div_up(b, li->cache->discard_block_size);
+ sector_div(e, li->cache->discard_block_size);
+
+ /*
+ * The origin may have shrunk, so we need to check we're still in
+ * bounds.
+ */
+ if (e > from_dblock(li->cache->discard_nr_blocks))
+ e = from_dblock(li->cache->discard_nr_blocks);
+
+ for (; b < e; b++)
+ set_discard(li->cache, to_dblock(b));
+}
+
static int load_discard(void *context, sector_t discard_block_size,
- dm_oblock_t oblock, bool discard)
+ dm_dblock_t dblock, bool discard)
{
- struct cache *cache = context;
+ struct discard_load_info *li = context;
- if (discard)
- set_discard(cache, oblock);
- else
- clear_discard(cache, oblock);
+ li->block_size = discard_block_size;
+
+ if (discard) {
+ if (from_dblock(dblock) == li->discard_end)
+ /*
+ * We're already in a discard range, just extend it.
+ */
+ li->discard_end = li->discard_end + 1ULL;
+
+ else {
+ /*
+ * Emit the old range and start a new one.
+ */
+ set_discard_range(li);
+ li->discard_begin = from_dblock(dblock);
+ li->discard_end = li->discard_begin + 1ULL;
+ }
+ } else {
+ set_discard_range(li);
+ li->discard_begin = li->discard_end = 0;
+ }
return 0;
}
return r;
}
- cache->cache_size = new_size;
+ set_cache_size(cache, new_size);
return 0;
}
}
if (!cache->loaded_discards) {
- r = dm_cache_load_discards(cache->cmd, load_discard, cache);
+ struct discard_load_info li;
+
+ /*
+ * The discard bitset could have been resized, or the
+ * discard block size changed. To be safe we start by
+ * setting every dblock to not discarded.
+ */
+ clear_bitset(cache->discard_bitset, from_dblock(cache->discard_nr_blocks));
+
+ discard_load_info_init(cache, &li);
+ r = dm_cache_load_discards(cache->cmd, load_discard, &li);
if (r) {
DMERR("could not load origin discards");
return r;
}
+ set_discard_range(&li);
cache->loaded_discards = true;
}
/*
* FIXME: these limits may be incompatible with the cache device
*/
- limits->max_discard_sectors = cache->sectors_per_block;
- limits->discard_granularity = cache->sectors_per_block << SECTOR_SHIFT;
+ limits->max_discard_sectors = min_t(sector_t, cache->discard_block_size * 1024,
+ cache->origin_sectors);
+ limits->discard_granularity = cache->discard_block_size << SECTOR_SHIFT;
}
static void cache_io_hints(struct dm_target *ti, struct queue_limits *limits)
static struct target_type cache_target = {
.name = "cache",
- .version = {1, 5, 0},
+ .version = {1, 6, 0},
.module = THIS_MODULE,
.ctr = cache_ctr,
.dtr = cache_dtr,
for (i = 0; i < ((1 << SECTOR_SHIFT) / 8); i++)
crypto_xor(data + i * 8, buf, 8);
out:
- memset(buf, 0, sizeof(buf));
+ memzero_explicit(buf, sizeof(buf));
return r;
}
int srcu_idx;
param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG |
- DM_ACTIVE_PRESENT_FLAG);
+ DM_ACTIVE_PRESENT_FLAG | DM_INTERNAL_SUSPEND_FLAG);
if (dm_suspended_md(md))
param->flags |= DM_SUSPEND_FLAG;
+ if (dm_suspended_internally_md(md))
+ param->flags |= DM_INTERNAL_SUSPEND_FLAG;
+
if (dm_test_deferred_remove_flag(md))
param->flags |= DM_DEFERRED_REMOVE;
__le32 layout;
__le32 stripe_sectors;
- __u8 pad[452]; /* Round struct to 512 bytes. */
- /* Always set to 0 when writing. */
+ /* Remainder of a logical block is zero-filled when writing (see super_sync()). */
} __packed;
static int read_disk_sb(struct md_rdev *rdev, int size)
test_bit(Faulty, &(rs->dev[i].rdev.flags)))
failed_devices |= (1ULL << i);
- memset(sb, 0, sizeof(*sb));
+ memset(sb + 1, 0, rdev->sb_size - sizeof(*sb));
sb->magic = cpu_to_le32(DM_RAID_MAGIC);
sb->features = cpu_to_le32(0); /* No features yet */
uint64_t events_sb, events_refsb;
rdev->sb_start = 0;
- rdev->sb_size = sizeof(*sb);
+ rdev->sb_size = bdev_logical_block_size(rdev->meta_bdev);
+ if (rdev->sb_size < sizeof(*sb) || rdev->sb_size > PAGE_SIZE) {
+ DMERR("superblock size of a logical block is no longer valid");
+ return -EINVAL;
+ }
ret = read_disk_sb(rdev, rdev->sb_size);
if (ret)
raid456 = (rs->md.level == 4 || rs->md.level == 5 || rs->md.level == 6);
for (i = 0; i < rs->md.raid_disks; i++) {
- struct request_queue *q = bdev_get_queue(rs->dev[i].rdev.bdev);
+ struct request_queue *q;
+
+ if (!rs->dev[i].rdev.bdev)
+ continue;
+ q = bdev_get_queue(rs->dev[i].rdev.bdev);
if (!q || !blk_queue_discard(q))
return;
return 1;
id = dm_stats_create(dm_get_stats(md), start, end, step, program_id, aux_data,
- dm_internal_suspend, dm_internal_resume, md);
+ dm_internal_suspend_fast, dm_internal_resume_fast, md);
if (id < 0)
return id;
sc->stripes_shift = __ffs(stripes);
r = dm_set_target_max_io_len(ti, chunk_size);
- if (r)
+ if (r) {
+ kfree(sc);
return r;
+ }
ti->num_flush_bios = stripes;
ti->num_discard_bios = stripes;
}
EXPORT_SYMBOL(dm_table_get_mode);
-static void suspend_targets(struct dm_table *t, unsigned postsuspend)
+enum suspend_mode {
+ PRESUSPEND,
+ PRESUSPEND_UNDO,
+ POSTSUSPEND,
+};
+
+static void suspend_targets(struct dm_table *t, enum suspend_mode mode)
{
int i = t->num_targets;
struct dm_target *ti = t->targets;
while (i--) {
- if (postsuspend) {
+ switch (mode) {
+ case PRESUSPEND:
+ if (ti->type->presuspend)
+ ti->type->presuspend(ti);
+ break;
+ case PRESUSPEND_UNDO:
+ if (ti->type->presuspend_undo)
+ ti->type->presuspend_undo(ti);
+ break;
+ case POSTSUSPEND:
if (ti->type->postsuspend)
ti->type->postsuspend(ti);
- } else if (ti->type->presuspend)
- ti->type->presuspend(ti);
-
+ break;
+ }
ti++;
}
}
if (!t)
return;
- suspend_targets(t, 0);
+ suspend_targets(t, PRESUSPEND);
+}
+
+void dm_table_presuspend_undo_targets(struct dm_table *t)
+{
+ if (!t)
+ return;
+
+ suspend_targets(t, PRESUSPEND_UNDO);
}
void dm_table_postsuspend_targets(struct dm_table *t)
if (!t)
return;
- suspend_targets(t, 1);
+ suspend_targets(t, POSTSUSPEND);
}
int dm_table_resume_targets(struct dm_table *t)
}
int dm_thin_find_block(struct dm_thin_device *td, dm_block_t block,
- int can_block, struct dm_thin_lookup_result *result)
+ int can_issue_io, struct dm_thin_lookup_result *result)
{
- int r = -EINVAL;
- uint64_t block_time = 0;
+ int r;
__le64 value;
struct dm_pool_metadata *pmd = td->pmd;
dm_block_t keys[2] = { td->id, block };
struct dm_btree_info *info;
- if (can_block) {
- down_read(&pmd->root_lock);
- info = &pmd->info;
- } else if (down_read_trylock(&pmd->root_lock))
- info = &pmd->nb_info;
- else
- return -EWOULDBLOCK;
-
if (pmd->fail_io)
- goto out;
+ return -EINVAL;
- r = dm_btree_lookup(info, pmd->root, keys, &value);
- if (!r)
- block_time = le64_to_cpu(value);
+ down_read(&pmd->root_lock);
-out:
- up_read(&pmd->root_lock);
+ if (can_issue_io) {
+ info = &pmd->info;
+ } else
+ info = &pmd->nb_info;
+ r = dm_btree_lookup(info, pmd->root, keys, &value);
if (!r) {
+ uint64_t block_time = 0;
dm_block_t exception_block;
uint32_t exception_time;
+
+ block_time = le64_to_cpu(value);
unpack_block_time(block_time, &exception_block,
&exception_time);
result->block = exception_block;
result->shared = __snapshotted_since(td, exception_time);
}
+ up_read(&pmd->root_lock);
return r;
}
return needs_check;
}
+
+void dm_pool_issue_prefetches(struct dm_pool_metadata *pmd)
+{
+ dm_tm_issue_prefetches(pmd->tm);
+}
/*
* Returns:
- * -EWOULDBLOCK iff @can_block is set and would block.
+ * -EWOULDBLOCK iff @can_issue_io is set and would issue IO
* -ENODATA iff that mapping is not present.
* 0 success
*/
int dm_thin_find_block(struct dm_thin_device *td, dm_block_t block,
- int can_block, struct dm_thin_lookup_result *result);
+ int can_issue_io, struct dm_thin_lookup_result *result);
/*
* Obtain an unused block.
int dm_pool_metadata_set_needs_check(struct dm_pool_metadata *pmd);
bool dm_pool_metadata_needs_check(struct dm_pool_metadata *pmd);
+/*
+ * Issue any prefetches that may be useful.
+ */
+void dm_pool_issue_prefetches(struct dm_pool_metadata *pmd);
+
/*----------------------------------------------------------------*/
#endif
#include <linux/device-mapper.h>
#include <linux/dm-io.h>
#include <linux/dm-kcopyd.h>
+#include <linux/log2.h>
#include <linux/list.h>
#include <linux/rculist.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/sort.h>
#include <linux/rbtree.h>
#define DM_MSG_PREFIX "thin"
*/
#define ENDIO_HOOK_POOL_SIZE 1024
#define MAPPING_POOL_SIZE 1024
-#define PRISON_CELLS 1024
#define COMMIT_PERIOD HZ
#define NO_SPACE_TIMEOUT_SECS 60
{
key->virtual = 0;
key->dev = dm_thin_dev_id(td);
- key->block = b;
+ key->block_begin = b;
+ key->block_end = b + 1ULL;
}
static void build_virtual_key(struct dm_thin_device *td, dm_block_t b,
{
key->virtual = 1;
key->dev = dm_thin_dev_id(td);
- key->block = b;
+ key->block_begin = b;
+ key->block_end = b + 1ULL;
+}
+
+/*----------------------------------------------------------------*/
+
+#define THROTTLE_THRESHOLD (1 * HZ)
+
+struct throttle {
+ struct rw_semaphore lock;
+ unsigned long threshold;
+ bool throttle_applied;
+};
+
+static void throttle_init(struct throttle *t)
+{
+ init_rwsem(&t->lock);
+ t->throttle_applied = false;
+}
+
+static void throttle_work_start(struct throttle *t)
+{
+ t->threshold = jiffies + THROTTLE_THRESHOLD;
+}
+
+static void throttle_work_update(struct throttle *t)
+{
+ if (!t->throttle_applied && jiffies > t->threshold) {
+ down_write(&t->lock);
+ t->throttle_applied = true;
+ }
+}
+
+static void throttle_work_complete(struct throttle *t)
+{
+ if (t->throttle_applied) {
+ t->throttle_applied = false;
+ up_write(&t->lock);
+ }
+}
+
+static void throttle_lock(struct throttle *t)
+{
+ down_read(&t->lock);
+}
+
+static void throttle_unlock(struct throttle *t)
+{
+ up_read(&t->lock);
}
/*----------------------------------------------------------------*/
struct thin_c;
typedef void (*process_bio_fn)(struct thin_c *tc, struct bio *bio);
+typedef void (*process_cell_fn)(struct thin_c *tc, struct dm_bio_prison_cell *cell);
typedef void (*process_mapping_fn)(struct dm_thin_new_mapping *m);
+#define CELL_SORT_ARRAY_SIZE 8192
+
struct pool {
struct list_head list;
struct dm_target *ti; /* Only set if a pool target is bound */
struct pool_features pf;
bool low_water_triggered:1; /* A dm event has been sent */
+ bool suspended:1;
struct dm_bio_prison *prison;
struct dm_kcopyd_client *copier;
struct workqueue_struct *wq;
+ struct throttle throttle;
struct work_struct worker;
struct delayed_work waker;
struct delayed_work no_space_timeout;
process_bio_fn process_bio;
process_bio_fn process_discard;
+ process_cell_fn process_cell;
+ process_cell_fn process_discard_cell;
+
process_mapping_fn process_prepared_mapping;
process_mapping_fn process_prepared_discard;
+
+ struct dm_bio_prison_cell *cell_sort_array[CELL_SORT_ARRAY_SIZE];
};
static enum pool_mode get_pool_mode(struct pool *pool);
struct pool *pool;
struct dm_thin_device *td;
+ struct mapped_device *thin_md;
+
bool requeue_mode:1;
spinlock_t lock;
+ struct list_head deferred_cells;
struct bio_list deferred_bio_list;
struct bio_list retry_on_resume_list;
struct rb_root sort_bio_list; /* sorted list of deferred bios */
dm_bio_prison_free_cell(pool->prison, cell);
}
+static void cell_visit_release(struct pool *pool,
+ void (*fn)(void *, struct dm_bio_prison_cell *),
+ void *context,
+ struct dm_bio_prison_cell *cell)
+{
+ dm_cell_visit_release(pool->prison, fn, context, cell);
+ dm_bio_prison_free_cell(pool->prison, cell);
+}
+
static void cell_release_no_holder(struct pool *pool,
struct dm_bio_prison_cell *cell,
struct bio_list *bios)
dm_bio_prison_free_cell(pool->prison, cell);
}
-static void cell_defer_no_holder_no_free(struct thin_c *tc,
- struct dm_bio_prison_cell *cell)
-{
- struct pool *pool = tc->pool;
- unsigned long flags;
-
- spin_lock_irqsave(&tc->lock, flags);
- dm_cell_release_no_holder(pool->prison, cell, &tc->deferred_bio_list);
- spin_unlock_irqrestore(&tc->lock, flags);
-
- wake_worker(pool);
-}
-
static void cell_error_with_code(struct pool *pool,
struct dm_bio_prison_cell *cell, int error_code)
{
cell_error_with_code(pool, cell, -EIO);
}
+static void cell_success(struct pool *pool, struct dm_bio_prison_cell *cell)
+{
+ cell_error_with_code(pool, cell, 0);
+}
+
+static void cell_requeue(struct pool *pool, struct dm_bio_prison_cell *cell)
+{
+ cell_error_with_code(pool, cell, DM_ENDIO_REQUEUE);
+}
+
/*----------------------------------------------------------------*/
/*
struct rb_node rb_node;
};
-static void requeue_bio_list(struct thin_c *tc, struct bio_list *master)
+static void __merge_bio_list(struct bio_list *bios, struct bio_list *master)
+{
+ bio_list_merge(bios, master);
+ bio_list_init(master);
+}
+
+static void error_bio_list(struct bio_list *bios, int error)
{
struct bio *bio;
+
+ while ((bio = bio_list_pop(bios)))
+ bio_endio(bio, error);
+}
+
+static void error_thin_bio_list(struct thin_c *tc, struct bio_list *master, int error)
+{
struct bio_list bios;
unsigned long flags;
bio_list_init(&bios);
spin_lock_irqsave(&tc->lock, flags);
- bio_list_merge(&bios, master);
- bio_list_init(master);
+ __merge_bio_list(&bios, master);
spin_unlock_irqrestore(&tc->lock, flags);
- while ((bio = bio_list_pop(&bios)))
- bio_endio(bio, DM_ENDIO_REQUEUE);
+ error_bio_list(&bios, error);
}
-static void requeue_io(struct thin_c *tc)
+static void requeue_deferred_cells(struct thin_c *tc)
{
- requeue_bio_list(tc, &tc->deferred_bio_list);
- requeue_bio_list(tc, &tc->retry_on_resume_list);
+ struct pool *pool = tc->pool;
+ unsigned long flags;
+ struct list_head cells;
+ struct dm_bio_prison_cell *cell, *tmp;
+
+ INIT_LIST_HEAD(&cells);
+
+ spin_lock_irqsave(&tc->lock, flags);
+ list_splice_init(&tc->deferred_cells, &cells);
+ spin_unlock_irqrestore(&tc->lock, flags);
+
+ list_for_each_entry_safe(cell, tmp, &cells, user_list)
+ cell_requeue(pool, cell);
}
-static void error_thin_retry_list(struct thin_c *tc)
+static void requeue_io(struct thin_c *tc)
{
- struct bio *bio;
- unsigned long flags;
struct bio_list bios;
+ unsigned long flags;
bio_list_init(&bios);
spin_lock_irqsave(&tc->lock, flags);
- bio_list_merge(&bios, &tc->retry_on_resume_list);
- bio_list_init(&tc->retry_on_resume_list);
+ __merge_bio_list(&bios, &tc->deferred_bio_list);
+ __merge_bio_list(&bios, &tc->retry_on_resume_list);
spin_unlock_irqrestore(&tc->lock, flags);
- while ((bio = bio_list_pop(&bios)))
- bio_io_error(bio);
+ error_bio_list(&bios, DM_ENDIO_REQUEUE);
+ requeue_deferred_cells(tc);
}
static void error_retry_list(struct pool *pool)
rcu_read_lock();
list_for_each_entry_rcu(tc, &pool->active_thins, list)
- error_thin_retry_list(tc);
+ error_thin_bio_list(tc, &tc->retry_on_resume_list, -EIO);
rcu_read_unlock();
}
*/
/*
- * This sends the bios in the cell back to the deferred_bios list.
+ * This sends the bios in the cell, except the original holder, back
+ * to the deferred_bios list.
*/
-static void cell_defer(struct thin_c *tc, struct dm_bio_prison_cell *cell)
+static void cell_defer_no_holder(struct thin_c *tc, struct dm_bio_prison_cell *cell)
{
struct pool *pool = tc->pool;
unsigned long flags;
spin_lock_irqsave(&tc->lock, flags);
- cell_release(pool, cell, &tc->deferred_bio_list);
+ cell_release_no_holder(pool, cell, &tc->deferred_bio_list);
spin_unlock_irqrestore(&tc->lock, flags);
wake_worker(pool);
}
-/*
- * Same as cell_defer above, except it omits the original holder of the cell.
- */
-static void cell_defer_no_holder(struct thin_c *tc, struct dm_bio_prison_cell *cell)
+static void thin_defer_bio(struct thin_c *tc, struct bio *bio);
+
+struct remap_info {
+ struct thin_c *tc;
+ struct bio_list defer_bios;
+ struct bio_list issue_bios;
+};
+
+static void __inc_remap_and_issue_cell(void *context,
+ struct dm_bio_prison_cell *cell)
{
- struct pool *pool = tc->pool;
- unsigned long flags;
+ struct remap_info *info = context;
+ struct bio *bio;
- spin_lock_irqsave(&tc->lock, flags);
- cell_release_no_holder(pool, cell, &tc->deferred_bio_list);
- spin_unlock_irqrestore(&tc->lock, flags);
+ while ((bio = bio_list_pop(&cell->bios))) {
+ if (bio->bi_rw & (REQ_DISCARD | REQ_FLUSH | REQ_FUA))
+ bio_list_add(&info->defer_bios, bio);
+ else {
+ inc_all_io_entry(info->tc->pool, bio);
- wake_worker(pool);
+ /*
+ * We can't issue the bios with the bio prison lock
+ * held, so we add them to a list to issue on
+ * return from this function.
+ */
+ bio_list_add(&info->issue_bios, bio);
+ }
+ }
+}
+
+static void inc_remap_and_issue_cell(struct thin_c *tc,
+ struct dm_bio_prison_cell *cell,
+ dm_block_t block)
+{
+ struct bio *bio;
+ struct remap_info info;
+
+ info.tc = tc;
+ bio_list_init(&info.defer_bios);
+ bio_list_init(&info.issue_bios);
+
+ /*
+ * We have to be careful to inc any bios we're about to issue
+ * before the cell is released, and avoid a race with new bios
+ * being added to the cell.
+ */
+ cell_visit_release(tc->pool, __inc_remap_and_issue_cell,
+ &info, cell);
+
+ while ((bio = bio_list_pop(&info.defer_bios)))
+ thin_defer_bio(tc, bio);
+
+ while ((bio = bio_list_pop(&info.issue_bios)))
+ remap_and_issue(info.tc, bio, block);
}
static void process_prepared_mapping_fail(struct dm_thin_new_mapping *m)
* the bios in the cell.
*/
if (bio) {
- cell_defer_no_holder(tc, m->cell);
+ inc_remap_and_issue_cell(tc, m->cell, m->data_block);
bio_endio(bio, 0);
- } else
- cell_defer(tc, m->cell);
+ } else {
+ inc_all_io_entry(tc->pool, m->cell->holder);
+ remap_and_issue(tc, m->cell->holder, m->data_block);
+ inc_remap_and_issue_cell(tc, m->cell, m->data_block);
+ }
out:
list_del(&m->list);
}
}
+static void remap_and_issue_overwrite(struct thin_c *tc, struct bio *bio,
+ dm_block_t data_block,
+ struct dm_thin_new_mapping *m)
+{
+ struct pool *pool = tc->pool;
+ struct dm_thin_endio_hook *h = dm_per_bio_data(bio, sizeof(struct dm_thin_endio_hook));
+
+ h->overwrite_mapping = m;
+ m->bio = bio;
+ save_and_set_endio(bio, &m->saved_bi_end_io, overwrite_endio);
+ inc_all_io_entry(pool, bio);
+ remap_and_issue(tc, bio, data_block);
+}
+
/*
* A partial copy also needs to zero the uncopied region.
*/
* If the whole block of data is being overwritten, we can issue the
* bio immediately. Otherwise we use kcopyd to clone the data first.
*/
- if (io_overwrites_block(pool, bio)) {
- struct dm_thin_endio_hook *h = dm_per_bio_data(bio, sizeof(struct dm_thin_endio_hook));
-
- h->overwrite_mapping = m;
- m->bio = bio;
- save_and_set_endio(bio, &m->saved_bi_end_io, overwrite_endio);
- inc_all_io_entry(pool, bio);
- remap_and_issue(tc, bio, data_dest);
- } else {
+ if (io_overwrites_block(pool, bio))
+ remap_and_issue_overwrite(tc, bio, data_dest, m);
+ else {
struct dm_io_region from, to;
from.bdev = origin->bdev;
if (!pool->pf.zero_new_blocks)
process_prepared_mapping(m);
- else if (io_overwrites_block(pool, bio)) {
- struct dm_thin_endio_hook *h = dm_per_bio_data(bio, sizeof(struct dm_thin_endio_hook));
-
- h->overwrite_mapping = m;
- m->bio = bio;
- save_and_set_endio(bio, &m->saved_bi_end_io, overwrite_endio);
- inc_all_io_entry(pool, bio);
- remap_and_issue(tc, bio, data_block);
+ else if (io_overwrites_block(pool, bio))
+ remap_and_issue_overwrite(tc, bio, data_block, m);
- } else
+ else
ll_zero(tc, m,
data_block * pool->sectors_per_block,
(data_block + 1) * pool->sectors_per_block);
bio_list_init(&bios);
cell_release(pool, cell, &bios);
- error = should_error_unserviceable_bio(pool);
- if (error)
- while ((bio = bio_list_pop(&bios)))
- bio_endio(bio, error);
- else
- while ((bio = bio_list_pop(&bios)))
- retry_on_resume(bio);
+ while ((bio = bio_list_pop(&bios)))
+ retry_on_resume(bio);
}
-static void process_discard(struct thin_c *tc, struct bio *bio)
+static void process_discard_cell(struct thin_c *tc, struct dm_bio_prison_cell *cell)
{
int r;
- unsigned long flags;
+ struct bio *bio = cell->holder;
struct pool *pool = tc->pool;
- struct dm_bio_prison_cell *cell, *cell2;
- struct dm_cell_key key, key2;
+ struct dm_bio_prison_cell *cell2;
+ struct dm_cell_key key2;
dm_block_t block = get_bio_block(tc, bio);
struct dm_thin_lookup_result lookup_result;
struct dm_thin_new_mapping *m;
- build_virtual_key(tc->td, block, &key);
- if (bio_detain(tc->pool, &key, bio, &cell))
+ if (tc->requeue_mode) {
+ cell_requeue(pool, cell);
return;
+ }
r = dm_thin_find_block(tc->td, block, 1, &lookup_result);
switch (r) {
m->cell2 = cell2;
m->bio = bio;
- if (!dm_deferred_set_add_work(pool->all_io_ds, &m->list)) {
- spin_lock_irqsave(&pool->lock, flags);
- list_add_tail(&m->list, &pool->prepared_discards);
- spin_unlock_irqrestore(&pool->lock, flags);
- wake_worker(pool);
- }
+ if (!dm_deferred_set_add_work(pool->all_io_ds, &m->list))
+ pool->process_prepared_discard(m);
+
} else {
inc_all_io_entry(pool, bio);
cell_defer_no_holder(tc, cell);
}
}
+static void process_discard_bio(struct thin_c *tc, struct bio *bio)
+{
+ struct dm_bio_prison_cell *cell;
+ struct dm_cell_key key;
+ dm_block_t block = get_bio_block(tc, bio);
+
+ build_virtual_key(tc->td, block, &key);
+ if (bio_detain(tc->pool, &key, bio, &cell))
+ return;
+
+ process_discard_cell(tc, cell);
+}
+
static void break_sharing(struct thin_c *tc, struct bio *bio, dm_block_t block,
struct dm_cell_key *key,
struct dm_thin_lookup_result *lookup_result,
}
}
+static void __remap_and_issue_shared_cell(void *context,
+ struct dm_bio_prison_cell *cell)
+{
+ struct remap_info *info = context;
+ struct bio *bio;
+
+ while ((bio = bio_list_pop(&cell->bios))) {
+ if ((bio_data_dir(bio) == WRITE) ||
+ (bio->bi_rw & (REQ_DISCARD | REQ_FLUSH | REQ_FUA)))
+ bio_list_add(&info->defer_bios, bio);
+ else {
+ struct dm_thin_endio_hook *h = dm_per_bio_data(bio, sizeof(struct dm_thin_endio_hook));;
+
+ h->shared_read_entry = dm_deferred_entry_inc(info->tc->pool->shared_read_ds);
+ inc_all_io_entry(info->tc->pool, bio);
+ bio_list_add(&info->issue_bios, bio);
+ }
+ }
+}
+
+static void remap_and_issue_shared_cell(struct thin_c *tc,
+ struct dm_bio_prison_cell *cell,
+ dm_block_t block)
+{
+ struct bio *bio;
+ struct remap_info info;
+
+ info.tc = tc;
+ bio_list_init(&info.defer_bios);
+ bio_list_init(&info.issue_bios);
+
+ cell_visit_release(tc->pool, __remap_and_issue_shared_cell,
+ &info, cell);
+
+ while ((bio = bio_list_pop(&info.defer_bios)))
+ thin_defer_bio(tc, bio);
+
+ while ((bio = bio_list_pop(&info.issue_bios)))
+ remap_and_issue(tc, bio, block);
+}
+
static void process_shared_bio(struct thin_c *tc, struct bio *bio,
dm_block_t block,
- struct dm_thin_lookup_result *lookup_result)
+ struct dm_thin_lookup_result *lookup_result,
+ struct dm_bio_prison_cell *virt_cell)
{
- struct dm_bio_prison_cell *cell;
+ struct dm_bio_prison_cell *data_cell;
struct pool *pool = tc->pool;
struct dm_cell_key key;
* of being broken so we have nothing further to do here.
*/
build_data_key(tc->td, lookup_result->block, &key);
- if (bio_detain(pool, &key, bio, &cell))
+ if (bio_detain(pool, &key, bio, &data_cell)) {
+ cell_defer_no_holder(tc, virt_cell);
return;
+ }
- if (bio_data_dir(bio) == WRITE && bio->bi_iter.bi_size)
- break_sharing(tc, bio, block, &key, lookup_result, cell);
- else {
+ if (bio_data_dir(bio) == WRITE && bio->bi_iter.bi_size) {
+ break_sharing(tc, bio, block, &key, lookup_result, data_cell);
+ cell_defer_no_holder(tc, virt_cell);
+ } else {
struct dm_thin_endio_hook *h = dm_per_bio_data(bio, sizeof(struct dm_thin_endio_hook));
h->shared_read_entry = dm_deferred_entry_inc(pool->shared_read_ds);
inc_all_io_entry(pool, bio);
- cell_defer_no_holder(tc, cell);
-
remap_and_issue(tc, bio, lookup_result->block);
+
+ remap_and_issue_shared_cell(tc, data_cell, lookup_result->block);
+ remap_and_issue_shared_cell(tc, virt_cell, lookup_result->block);
}
}
}
}
-static void process_bio(struct thin_c *tc, struct bio *bio)
+static void process_cell(struct thin_c *tc, struct dm_bio_prison_cell *cell)
{
int r;
struct pool *pool = tc->pool;
+ struct bio *bio = cell->holder;
dm_block_t block = get_bio_block(tc, bio);
- struct dm_bio_prison_cell *cell;
- struct dm_cell_key key;
struct dm_thin_lookup_result lookup_result;
- /*
- * If cell is already occupied, then the block is already
- * being provisioned so we have nothing further to do here.
- */
- build_virtual_key(tc->td, block, &key);
- if (bio_detain(pool, &key, bio, &cell))
+ if (tc->requeue_mode) {
+ cell_requeue(pool, cell);
return;
+ }
r = dm_thin_find_block(tc->td, block, 1, &lookup_result);
switch (r) {
case 0:
- if (lookup_result.shared) {
- process_shared_bio(tc, bio, block, &lookup_result);
- cell_defer_no_holder(tc, cell); /* FIXME: pass this cell into process_shared? */
- } else {
+ if (lookup_result.shared)
+ process_shared_bio(tc, bio, block, &lookup_result, cell);
+ else {
inc_all_io_entry(pool, bio);
- cell_defer_no_holder(tc, cell);
-
remap_and_issue(tc, bio, lookup_result.block);
+ inc_remap_and_issue_cell(tc, cell, lookup_result.block);
}
break;
}
}
-static void process_bio_read_only(struct thin_c *tc, struct bio *bio)
+static void process_bio(struct thin_c *tc, struct bio *bio)
+{
+ struct pool *pool = tc->pool;
+ dm_block_t block = get_bio_block(tc, bio);
+ struct dm_bio_prison_cell *cell;
+ struct dm_cell_key key;
+
+ /*
+ * If cell is already occupied, then the block is already
+ * being provisioned so we have nothing further to do here.
+ */
+ build_virtual_key(tc->td, block, &key);
+ if (bio_detain(pool, &key, bio, &cell))
+ return;
+
+ process_cell(tc, cell);
+}
+
+static void __process_bio_read_only(struct thin_c *tc, struct bio *bio,
+ struct dm_bio_prison_cell *cell)
{
int r;
int rw = bio_data_dir(bio);
r = dm_thin_find_block(tc->td, block, 1, &lookup_result);
switch (r) {
case 0:
- if (lookup_result.shared && (rw == WRITE) && bio->bi_iter.bi_size)
+ if (lookup_result.shared && (rw == WRITE) && bio->bi_iter.bi_size) {
handle_unserviceable_bio(tc->pool, bio);
- else {
+ if (cell)
+ cell_defer_no_holder(tc, cell);
+ } else {
inc_all_io_entry(tc->pool, bio);
remap_and_issue(tc, bio, lookup_result.block);
+ if (cell)
+ inc_remap_and_issue_cell(tc, cell, lookup_result.block);
}
break;
case -ENODATA:
+ if (cell)
+ cell_defer_no_holder(tc, cell);
if (rw != READ) {
handle_unserviceable_bio(tc->pool, bio);
break;
default:
DMERR_LIMIT("%s: dm_thin_find_block() failed: error = %d",
__func__, r);
+ if (cell)
+ cell_defer_no_holder(tc, cell);
bio_io_error(bio);
break;
}
}
+static void process_bio_read_only(struct thin_c *tc, struct bio *bio)
+{
+ __process_bio_read_only(tc, bio, NULL);
+}
+
+static void process_cell_read_only(struct thin_c *tc, struct dm_bio_prison_cell *cell)
+{
+ __process_bio_read_only(tc, cell->holder, cell);
+}
+
static void process_bio_success(struct thin_c *tc, struct bio *bio)
{
bio_endio(bio, 0);
bio_io_error(bio);
}
+static void process_cell_success(struct thin_c *tc, struct dm_bio_prison_cell *cell)
+{
+ cell_success(tc->pool, cell);
+}
+
+static void process_cell_fail(struct thin_c *tc, struct dm_bio_prison_cell *cell)
+{
+ cell_error(tc->pool, cell);
+}
+
/*
* FIXME: should we also commit due to size of transaction, measured in
* metadata blocks?
struct bio *bio;
struct bio_list bios;
struct blk_plug plug;
+ unsigned count = 0;
if (tc->requeue_mode) {
- requeue_bio_list(tc, &tc->deferred_bio_list);
+ error_thin_bio_list(tc, &tc->deferred_bio_list, DM_ENDIO_REQUEUE);
return;
}
pool->process_discard(tc, bio);
else
pool->process_bio(tc, bio);
+
+ if ((count++ & 127) == 0) {
+ throttle_work_update(&pool->throttle);
+ dm_pool_issue_prefetches(pool->pmd);
+ }
}
blk_finish_plug(&plug);
}
+static int cmp_cells(const void *lhs, const void *rhs)
+{
+ struct dm_bio_prison_cell *lhs_cell = *((struct dm_bio_prison_cell **) lhs);
+ struct dm_bio_prison_cell *rhs_cell = *((struct dm_bio_prison_cell **) rhs);
+
+ BUG_ON(!lhs_cell->holder);
+ BUG_ON(!rhs_cell->holder);
+
+ if (lhs_cell->holder->bi_iter.bi_sector < rhs_cell->holder->bi_iter.bi_sector)
+ return -1;
+
+ if (lhs_cell->holder->bi_iter.bi_sector > rhs_cell->holder->bi_iter.bi_sector)
+ return 1;
+
+ return 0;
+}
+
+static unsigned sort_cells(struct pool *pool, struct list_head *cells)
+{
+ unsigned count = 0;
+ struct dm_bio_prison_cell *cell, *tmp;
+
+ list_for_each_entry_safe(cell, tmp, cells, user_list) {
+ if (count >= CELL_SORT_ARRAY_SIZE)
+ break;
+
+ pool->cell_sort_array[count++] = cell;
+ list_del(&cell->user_list);
+ }
+
+ sort(pool->cell_sort_array, count, sizeof(cell), cmp_cells, NULL);
+
+ return count;
+}
+
+static void process_thin_deferred_cells(struct thin_c *tc)
+{
+ struct pool *pool = tc->pool;
+ unsigned long flags;
+ struct list_head cells;
+ struct dm_bio_prison_cell *cell;
+ unsigned i, j, count;
+
+ INIT_LIST_HEAD(&cells);
+
+ spin_lock_irqsave(&tc->lock, flags);
+ list_splice_init(&tc->deferred_cells, &cells);
+ spin_unlock_irqrestore(&tc->lock, flags);
+
+ if (list_empty(&cells))
+ return;
+
+ do {
+ count = sort_cells(tc->pool, &cells);
+
+ for (i = 0; i < count; i++) {
+ cell = pool->cell_sort_array[i];
+ BUG_ON(!cell->holder);
+
+ /*
+ * If we've got no free new_mapping structs, and processing
+ * this bio might require one, we pause until there are some
+ * prepared mappings to process.
+ */
+ if (ensure_next_mapping(pool)) {
+ for (j = i; j < count; j++)
+ list_add(&pool->cell_sort_array[j]->user_list, &cells);
+
+ spin_lock_irqsave(&tc->lock, flags);
+ list_splice(&cells, &tc->deferred_cells);
+ spin_unlock_irqrestore(&tc->lock, flags);
+ return;
+ }
+
+ if (cell->holder->bi_rw & REQ_DISCARD)
+ pool->process_discard_cell(tc, cell);
+ else
+ pool->process_cell(tc, cell);
+ }
+ } while (!list_empty(&cells));
+}
+
static void thin_get(struct thin_c *tc);
static void thin_put(struct thin_c *tc);
tc = get_first_thin(pool);
while (tc) {
+ process_thin_deferred_cells(tc);
process_thin_deferred_bios(tc);
tc = get_next_thin(pool, tc);
}
{
struct pool *pool = container_of(ws, struct pool, worker);
+ throttle_work_start(&pool->throttle);
+ dm_pool_issue_prefetches(pool->pmd);
+ throttle_work_update(&pool->throttle);
process_prepared(pool, &pool->prepared_mappings, &pool->process_prepared_mapping);
+ throttle_work_update(&pool->throttle);
process_prepared(pool, &pool->prepared_discards, &pool->process_prepared_discard);
+ throttle_work_update(&pool->throttle);
process_deferred_bios(pool);
+ throttle_work_complete(&pool->throttle);
}
/*
dm_pool_metadata_read_only(pool->pmd);
pool->process_bio = process_bio_fail;
pool->process_discard = process_bio_fail;
+ pool->process_cell = process_cell_fail;
+ pool->process_discard_cell = process_cell_fail;
pool->process_prepared_mapping = process_prepared_mapping_fail;
pool->process_prepared_discard = process_prepared_discard_fail;
dm_pool_metadata_read_only(pool->pmd);
pool->process_bio = process_bio_read_only;
pool->process_discard = process_bio_success;
+ pool->process_cell = process_cell_read_only;
+ pool->process_discard_cell = process_cell_success;
pool->process_prepared_mapping = process_prepared_mapping_fail;
pool->process_prepared_discard = process_prepared_discard_passdown;
if (old_mode != new_mode)
notify_of_pool_mode_change(pool, "out-of-data-space");
pool->process_bio = process_bio_read_only;
- pool->process_discard = process_discard;
+ pool->process_discard = process_discard_bio;
+ pool->process_cell = process_cell_read_only;
+ pool->process_discard_cell = process_discard_cell;
pool->process_prepared_mapping = process_prepared_mapping;
pool->process_prepared_discard = process_prepared_discard_passdown;
notify_of_pool_mode_change(pool, "write");
dm_pool_metadata_read_write(pool->pmd);
pool->process_bio = process_bio;
- pool->process_discard = process_discard;
+ pool->process_discard = process_discard_bio;
+ pool->process_cell = process_cell;
+ pool->process_discard_cell = process_discard_cell;
pool->process_prepared_mapping = process_prepared_mapping;
pool->process_prepared_discard = process_prepared_discard;
break;
wake_worker(pool);
}
+static void thin_defer_bio_with_throttle(struct thin_c *tc, struct bio *bio)
+{
+ struct pool *pool = tc->pool;
+
+ throttle_lock(&pool->throttle);
+ thin_defer_bio(tc, bio);
+ throttle_unlock(&pool->throttle);
+}
+
+static void thin_defer_cell(struct thin_c *tc, struct dm_bio_prison_cell *cell)
+{
+ unsigned long flags;
+ struct pool *pool = tc->pool;
+
+ throttle_lock(&pool->throttle);
+ spin_lock_irqsave(&tc->lock, flags);
+ list_add_tail(&cell->user_list, &tc->deferred_cells);
+ spin_unlock_irqrestore(&tc->lock, flags);
+ throttle_unlock(&pool->throttle);
+
+ wake_worker(pool);
+}
+
static void thin_hook_bio(struct thin_c *tc, struct bio *bio)
{
struct dm_thin_endio_hook *h = dm_per_bio_data(bio, sizeof(struct dm_thin_endio_hook));
dm_block_t block = get_bio_block(tc, bio);
struct dm_thin_device *td = tc->td;
struct dm_thin_lookup_result result;
- struct dm_bio_prison_cell cell1, cell2;
- struct dm_bio_prison_cell *cell_result;
+ struct dm_bio_prison_cell *virt_cell, *data_cell;
struct dm_cell_key key;
thin_hook_bio(tc, bio);
}
if (bio->bi_rw & (REQ_DISCARD | REQ_FLUSH | REQ_FUA)) {
- thin_defer_bio(tc, bio);
+ thin_defer_bio_with_throttle(tc, bio);
return DM_MAPIO_SUBMITTED;
}
+ /*
+ * We must hold the virtual cell before doing the lookup, otherwise
+ * there's a race with discard.
+ */
+ build_virtual_key(tc->td, block, &key);
+ if (bio_detain(tc->pool, &key, bio, &virt_cell))
+ return DM_MAPIO_SUBMITTED;
+
r = dm_thin_find_block(td, block, 0, &result);
/*
* More distant ancestors are irrelevant. The
* shared flag will be set in their case.
*/
- thin_defer_bio(tc, bio);
+ thin_defer_cell(tc, virt_cell);
return DM_MAPIO_SUBMITTED;
}
- build_virtual_key(tc->td, block, &key);
- if (dm_bio_detain(tc->pool->prison, &key, bio, &cell1, &cell_result))
- return DM_MAPIO_SUBMITTED;
-
build_data_key(tc->td, result.block, &key);
- if (dm_bio_detain(tc->pool->prison, &key, bio, &cell2, &cell_result)) {
- cell_defer_no_holder_no_free(tc, &cell1);
+ if (bio_detain(tc->pool, &key, bio, &data_cell)) {
+ cell_defer_no_holder(tc, virt_cell);
return DM_MAPIO_SUBMITTED;
}
inc_all_io_entry(tc->pool, bio);
- cell_defer_no_holder_no_free(tc, &cell2);
- cell_defer_no_holder_no_free(tc, &cell1);
+ cell_defer_no_holder(tc, data_cell);
+ cell_defer_no_holder(tc, virt_cell);
remap(tc, bio, result.block);
return DM_MAPIO_REMAPPED;
* of doing so.
*/
handle_unserviceable_bio(tc->pool, bio);
+ cell_defer_no_holder(tc, virt_cell);
return DM_MAPIO_SUBMITTED;
}
/* fall through */
case -EWOULDBLOCK:
- /*
- * In future, the failed dm_thin_find_block above could
- * provide the hint to load the metadata into cache.
- */
- thin_defer_bio(tc, bio);
+ thin_defer_cell(tc, virt_cell);
return DM_MAPIO_SUBMITTED;
default:
* pool is switched to fail-io mode.
*/
bio_io_error(bio);
+ cell_defer_no_holder(tc, virt_cell);
return DM_MAPIO_SUBMITTED;
}
}
pool->sectors_per_block_shift = __ffs(block_size);
pool->low_water_blocks = 0;
pool_features_init(&pool->pf);
- pool->prison = dm_bio_prison_create(PRISON_CELLS);
+ pool->prison = dm_bio_prison_create();
if (!pool->prison) {
*error = "Error creating pool's bio prison";
err_p = ERR_PTR(-ENOMEM);
goto bad_wq;
}
+ throttle_init(&pool->throttle);
INIT_WORK(&pool->worker, do_worker);
INIT_DELAYED_WORK(&pool->waker, do_waker);
INIT_DELAYED_WORK(&pool->no_space_timeout, do_no_space_timeout);
INIT_LIST_HEAD(&pool->prepared_discards);
INIT_LIST_HEAD(&pool->active_thins);
pool->low_water_triggered = false;
+ pool->suspended = true;
pool->shared_read_ds = dm_deferred_set_create();
if (!pool->shared_read_ds) {
return 0;
}
+static void pool_suspend_active_thins(struct pool *pool)
+{
+ struct thin_c *tc;
+
+ /* Suspend all active thin devices */
+ tc = get_first_thin(pool);
+ while (tc) {
+ dm_internal_suspend_noflush(tc->thin_md);
+ tc = get_next_thin(pool, tc);
+ }
+}
+
+static void pool_resume_active_thins(struct pool *pool)
+{
+ struct thin_c *tc;
+
+ /* Resume all active thin devices */
+ tc = get_first_thin(pool);
+ while (tc) {
+ dm_internal_resume(tc->thin_md);
+ tc = get_next_thin(pool, tc);
+ }
+}
+
static void pool_resume(struct dm_target *ti)
{
struct pool_c *pt = ti->private;
struct pool *pool = pt->pool;
unsigned long flags;
+ /*
+ * Must requeue active_thins' bios and then resume
+ * active_thins _before_ clearing 'suspend' flag.
+ */
+ requeue_bios(pool);
+ pool_resume_active_thins(pool);
+
spin_lock_irqsave(&pool->lock, flags);
pool->low_water_triggered = false;
+ pool->suspended = false;
spin_unlock_irqrestore(&pool->lock, flags);
- requeue_bios(pool);
do_waker(&pool->waker.work);
}
+static void pool_presuspend(struct dm_target *ti)
+{
+ struct pool_c *pt = ti->private;
+ struct pool *pool = pt->pool;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pool->lock, flags);
+ pool->suspended = true;
+ spin_unlock_irqrestore(&pool->lock, flags);
+
+ pool_suspend_active_thins(pool);
+}
+
+static void pool_presuspend_undo(struct dm_target *ti)
+{
+ struct pool_c *pt = ti->private;
+ struct pool *pool = pt->pool;
+ unsigned long flags;
+
+ pool_resume_active_thins(pool);
+
+ spin_lock_irqsave(&pool->lock, flags);
+ pool->suspended = false;
+ spin_unlock_irqrestore(&pool->lock, flags);
+}
+
static void pool_postsuspend(struct dm_target *ti)
{
struct pool_c *pt = ti->private;
* create_thin <dev_id>
* create_snap <dev_id> <origin_id>
* delete <dev_id>
- * trim <dev_id> <new_size_in_sectors>
* set_transaction_id <current_trans_id> <new_trans_id>
* reserve_metadata_snap
* release_metadata_snap
{
struct pool_c *pt = ti->private;
struct pool *pool = pt->pool;
- uint64_t io_opt_sectors = limits->io_opt >> SECTOR_SHIFT;
+ sector_t io_opt_sectors = limits->io_opt >> SECTOR_SHIFT;
+
+ /*
+ * If max_sectors is smaller than pool->sectors_per_block adjust it
+ * to the highest possible power-of-2 factor of pool->sectors_per_block.
+ * This is especially beneficial when the pool's data device is a RAID
+ * device that has a full stripe width that matches pool->sectors_per_block
+ * -- because even though partial RAID stripe-sized IOs will be issued to a
+ * single RAID stripe; when aggregated they will end on a full RAID stripe
+ * boundary.. which avoids additional partial RAID stripe writes cascading
+ */
+ if (limits->max_sectors < pool->sectors_per_block) {
+ while (!is_factor(pool->sectors_per_block, limits->max_sectors)) {
+ if ((limits->max_sectors & (limits->max_sectors - 1)) == 0)
+ limits->max_sectors--;
+ limits->max_sectors = rounddown_pow_of_two(limits->max_sectors);
+ }
+ }
/*
* If the system-determined stacked limits are compatible with the
* pool's blocksize (io_opt is a factor) do not override them.
*/
if (io_opt_sectors < pool->sectors_per_block ||
- do_div(io_opt_sectors, pool->sectors_per_block)) {
- blk_limits_io_min(limits, pool->sectors_per_block << SECTOR_SHIFT);
+ !is_factor(io_opt_sectors, pool->sectors_per_block)) {
+ if (is_factor(pool->sectors_per_block, limits->max_sectors))
+ blk_limits_io_min(limits, limits->max_sectors << SECTOR_SHIFT);
+ else
+ blk_limits_io_min(limits, pool->sectors_per_block << SECTOR_SHIFT);
blk_limits_io_opt(limits, pool->sectors_per_block << SECTOR_SHIFT);
}
.name = "thin-pool",
.features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE |
DM_TARGET_IMMUTABLE,
- .version = {1, 13, 0},
+ .version = {1, 14, 0},
.module = THIS_MODULE,
.ctr = pool_ctr,
.dtr = pool_dtr,
.map = pool_map,
+ .presuspend = pool_presuspend,
+ .presuspend_undo = pool_presuspend_undo,
.postsuspend = pool_postsuspend,
.preresume = pool_preresume,
.resume = pool_resume,
struct thin_c *tc = ti->private;
unsigned long flags;
- thin_put(tc);
- wait_for_completion(&tc->can_destroy);
-
spin_lock_irqsave(&tc->pool->lock, flags);
list_del_rcu(&tc->list);
spin_unlock_irqrestore(&tc->pool->lock, flags);
synchronize_rcu();
+ thin_put(tc);
+ wait_for_completion(&tc->can_destroy);
+
mutex_lock(&dm_thin_pool_table.mutex);
__pool_dec(tc->pool);
r = -ENOMEM;
goto out_unlock;
}
+ tc->thin_md = dm_table_get_md(ti->table);
spin_lock_init(&tc->lock);
+ INIT_LIST_HEAD(&tc->deferred_cells);
bio_list_init(&tc->deferred_bio_list);
bio_list_init(&tc->retry_on_resume_list);
tc->sort_bio_list = RB_ROOT;
if (get_pool_mode(tc->pool) == PM_FAIL) {
ti->error = "Couldn't open thin device, Pool is in fail mode";
r = -EINVAL;
- goto bad_thin_open;
+ goto bad_pool;
}
r = dm_pool_open_thin_device(tc->pool->pmd, tc->dev_id, &tc->td);
if (r) {
ti->error = "Couldn't open thin internal device";
- goto bad_thin_open;
+ goto bad_pool;
}
r = dm_set_target_max_io_len(ti, tc->pool->sectors_per_block);
if (r)
- goto bad_target_max_io_len;
+ goto bad;
ti->num_flush_bios = 1;
ti->flush_supported = true;
ti->split_discard_bios = true;
}
- dm_put(pool_md);
-
mutex_unlock(&dm_thin_pool_table.mutex);
- atomic_set(&tc->refcount, 1);
- init_completion(&tc->can_destroy);
-
spin_lock_irqsave(&tc->pool->lock, flags);
+ if (tc->pool->suspended) {
+ spin_unlock_irqrestore(&tc->pool->lock, flags);
+ mutex_lock(&dm_thin_pool_table.mutex); /* reacquire for __pool_dec */
+ ti->error = "Unable to activate thin device while pool is suspended";
+ r = -EINVAL;
+ goto bad;
+ }
list_add_tail_rcu(&tc->list, &tc->pool->active_thins);
spin_unlock_irqrestore(&tc->pool->lock, flags);
/*
*/
synchronize_rcu();
+ dm_put(pool_md);
+
+ atomic_set(&tc->refcount, 1);
+ init_completion(&tc->can_destroy);
+
return 0;
-bad_target_max_io_len:
+bad:
dm_pool_close_thin_device(tc->td);
-bad_thin_open:
+bad_pool:
__pool_dec(tc->pool);
bad_pool_lookup:
dm_put(pool_md);
DMEMIT("Error");
}
+static int thin_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
+ struct bio_vec *biovec, int max_size)
+{
+ struct thin_c *tc = ti->private;
+ struct request_queue *q = bdev_get_queue(tc->pool_dev->bdev);
+
+ if (!q->merge_bvec_fn)
+ return max_size;
+
+ bvm->bi_bdev = tc->pool_dev->bdev;
+ bvm->bi_sector = dm_target_offset(ti, bvm->bi_sector);
+
+ return min(max_size, q->merge_bvec_fn(q, bvm, biovec));
+}
+
static int thin_iterate_devices(struct dm_target *ti,
iterate_devices_callout_fn fn, void *data)
{
static struct target_type thin_target = {
.name = "thin",
- .version = {1, 13, 0},
+ .version = {1, 14, 0},
.module = THIS_MODULE,
.ctr = thin_ctr,
.dtr = thin_dtr,
.presuspend = thin_presuspend,
.postsuspend = thin_postsuspend,
.status = thin_status,
+ .merge = thin_merge,
.iterate_devices = thin_iterate_devices,
};
#include <linux/idr.h>
#include <linux/hdreg.h>
#include <linux/delay.h>
+#include <linux/wait.h>
#include <trace/events/block.h>
#define DMF_NOFLUSH_SUSPENDING 5
#define DMF_MERGE_IS_OPTIONAL 6
#define DMF_DEFERRED_REMOVE 7
+#define DMF_SUSPENDED_INTERNALLY 8
/*
* A dummy definition to make RCU happy.
* Use dm_get_live_table{_fast} or take suspend_lock for
* dereference.
*/
- struct dm_table *map;
+ struct dm_table __rcu *map;
struct list_head table_devices;
struct mutex table_devices_lock;
goto out;
tgt = dm_table_get_target(map, 0);
+ if (!tgt->type->ioctl)
+ goto out;
if (dm_suspended_md(md)) {
r = -EAGAIN;
goto out;
}
- if (tgt->type->ioctl)
- r = tgt->type->ioctl(tgt, cmd, arg);
+ r = tgt->type->ioctl(tgt, cmd, arg);
out:
dm_put_live_table(md, srcu_idx);
* Find maximum amount of I/O that won't need splitting
*/
max_sectors = min(max_io_len(bvm->bi_sector, ti),
- (sector_t) BIO_MAX_SECTORS);
+ (sector_t) queue_max_sectors(q));
max_size = (max_sectors << SECTOR_SHIFT) - bvm->bi_size;
- if (max_size < 0)
+ if (unlikely(max_size < 0)) /* this shouldn't _ever_ happen */
max_size = 0;
/*
max_size = ti->type->merge(ti, bvm, biovec, max_size);
/*
* If the target doesn't support merge method and some of the devices
- * provided their merge_bvec method (we know this by looking at
- * queue_max_hw_sectors), then we can't allow bios with multiple vector
- * entries. So always set max_size to 0, and the code below allows
- * just one page.
+ * provided their merge_bvec method (we know this by looking for the
+ * max_hw_sectors that dm_set_device_limits may set), then we can't
+ * allow bios with multiple vector entries. So always set max_size
+ * to 0, and the code below allows just one page.
*/
else if (queue_max_hw_sectors(q) <= PAGE_SIZE >> 9)
max_size = 0;
merge_is_optional = dm_table_merge_is_optional(t);
- old_map = md->map;
+ old_map = rcu_dereference_protected(md->map, lockdep_is_held(&md->suspend_lock));
rcu_assign_pointer(md->map, t);
md->immutable_target_type = dm_table_get_immutable_target_type(t);
set_bit(DMF_MERGE_IS_OPTIONAL, &md->flags);
else
clear_bit(DMF_MERGE_IS_OPTIONAL, &md->flags);
- dm_sync_table(md);
+ if (old_map)
+ dm_sync_table(md);
return old_map;
}
*/
static struct dm_table *__unbind(struct mapped_device *md)
{
- struct dm_table *map = md->map;
+ struct dm_table *map = rcu_dereference_protected(md->map, 1);
if (!map)
return NULL;
}
/*
- * We need to be able to change a mapping table under a mounted
- * filesystem. For example we might want to move some data in
- * the background. Before the table can be swapped with
- * dm_bind_table, dm_suspend must be called to flush any in
- * flight bios and ensure that any further io gets deferred.
- */
-/*
- * Suspend mechanism in request-based dm.
+ * If __dm_suspend returns 0, the device is completely quiescent
+ * now. There is no request-processing activity. All new requests
+ * are being added to md->deferred list.
*
- * 1. Flush all I/Os by lock_fs() if needed.
- * 2. Stop dispatching any I/O by stopping the request_queue.
- * 3. Wait for all in-flight I/Os to be completed or requeued.
- *
- * To abort suspend, start the request_queue.
+ * Caller must hold md->suspend_lock
*/
-int dm_suspend(struct mapped_device *md, unsigned suspend_flags)
+static int __dm_suspend(struct mapped_device *md, struct dm_table *map,
+ unsigned suspend_flags, int interruptible)
{
- struct dm_table *map = NULL;
- int r = 0;
- int do_lockfs = suspend_flags & DM_SUSPEND_LOCKFS_FLAG ? 1 : 0;
- int noflush = suspend_flags & DM_SUSPEND_NOFLUSH_FLAG ? 1 : 0;
-
- mutex_lock(&md->suspend_lock);
-
- if (dm_suspended_md(md)) {
- r = -EINVAL;
- goto out_unlock;
- }
-
- map = md->map;
+ bool do_lockfs = suspend_flags & DM_SUSPEND_LOCKFS_FLAG;
+ bool noflush = suspend_flags & DM_SUSPEND_NOFLUSH_FLAG;
+ int r;
/*
* DMF_NOFLUSH_SUSPENDING must be set before presuspend.
if (noflush)
set_bit(DMF_NOFLUSH_SUSPENDING, &md->flags);
- /* This does not get reverted if there's an error later. */
+ /*
+ * This gets reverted if there's an error later and the targets
+ * provide the .presuspend_undo hook.
+ */
dm_table_presuspend_targets(map);
/*
*/
if (!noflush && do_lockfs) {
r = lock_fs(md);
- if (r)
- goto out_unlock;
+ if (r) {
+ dm_table_presuspend_undo_targets(map);
+ return r;
+ }
}
/*
* flush_workqueue(md->wq).
*/
set_bit(DMF_BLOCK_IO_FOR_SUSPEND, &md->flags);
- synchronize_srcu(&md->io_barrier);
+ if (map)
+ synchronize_srcu(&md->io_barrier);
/*
* Stop md->queue before flushing md->wq in case request-based
* We call dm_wait_for_completion to wait for all existing requests
* to finish.
*/
- r = dm_wait_for_completion(md, TASK_INTERRUPTIBLE);
+ r = dm_wait_for_completion(md, interruptible);
if (noflush)
clear_bit(DMF_NOFLUSH_SUSPENDING, &md->flags);
- synchronize_srcu(&md->io_barrier);
+ if (map)
+ synchronize_srcu(&md->io_barrier);
/* were we interrupted ? */
if (r < 0) {
start_queue(md->queue);
unlock_fs(md);
- goto out_unlock; /* pushback list is already flushed, so skip flush */
+ dm_table_presuspend_undo_targets(map);
+ /* pushback list is already flushed, so skip flush */
}
- /*
- * If dm_wait_for_completion returned 0, the device is completely
- * quiescent now. There is no request-processing activity. All new
- * requests are being added to md->deferred list.
- */
+ return r;
+}
+
+/*
+ * We need to be able to change a mapping table under a mounted
+ * filesystem. For example we might want to move some data in
+ * the background. Before the table can be swapped with
+ * dm_bind_table, dm_suspend must be called to flush any in
+ * flight bios and ensure that any further io gets deferred.
+ */
+/*
+ * Suspend mechanism in request-based dm.
+ *
+ * 1. Flush all I/Os by lock_fs() if needed.
+ * 2. Stop dispatching any I/O by stopping the request_queue.
+ * 3. Wait for all in-flight I/Os to be completed or requeued.
+ *
+ * To abort suspend, start the request_queue.
+ */
+int dm_suspend(struct mapped_device *md, unsigned suspend_flags)
+{
+ struct dm_table *map = NULL;
+ int r = 0;
+
+retry:
+ mutex_lock_nested(&md->suspend_lock, SINGLE_DEPTH_NESTING);
+
+ if (dm_suspended_md(md)) {
+ r = -EINVAL;
+ goto out_unlock;
+ }
+
+ if (dm_suspended_internally_md(md)) {
+ /* already internally suspended, wait for internal resume */
+ mutex_unlock(&md->suspend_lock);
+ r = wait_on_bit(&md->flags, DMF_SUSPENDED_INTERNALLY, TASK_INTERRUPTIBLE);
+ if (r)
+ return r;
+ goto retry;
+ }
+
+ map = rcu_dereference_protected(md->map, lockdep_is_held(&md->suspend_lock));
+
+ r = __dm_suspend(md, map, suspend_flags, TASK_INTERRUPTIBLE);
+ if (r)
+ goto out_unlock;
set_bit(DMF_SUSPENDED, &md->flags);
return r;
}
-int dm_resume(struct mapped_device *md)
+static int __dm_resume(struct mapped_device *md, struct dm_table *map)
{
- int r = -EINVAL;
- struct dm_table *map = NULL;
-
- mutex_lock(&md->suspend_lock);
- if (!dm_suspended_md(md))
- goto out;
-
- map = md->map;
- if (!map || !dm_table_get_size(map))
- goto out;
-
- r = dm_table_resume_targets(map);
- if (r)
- goto out;
+ if (map) {
+ int r = dm_table_resume_targets(map);
+ if (r)
+ return r;
+ }
dm_queue_flush(md);
unlock_fs(md);
+ return 0;
+}
+
+int dm_resume(struct mapped_device *md)
+{
+ int r = -EINVAL;
+ struct dm_table *map = NULL;
+
+retry:
+ mutex_lock_nested(&md->suspend_lock, SINGLE_DEPTH_NESTING);
+
+ if (!dm_suspended_md(md))
+ goto out;
+
+ if (dm_suspended_internally_md(md)) {
+ /* already internally suspended, wait for internal resume */
+ mutex_unlock(&md->suspend_lock);
+ r = wait_on_bit(&md->flags, DMF_SUSPENDED_INTERNALLY, TASK_INTERRUPTIBLE);
+ if (r)
+ return r;
+ goto retry;
+ }
+
+ map = rcu_dereference_protected(md->map, lockdep_is_held(&md->suspend_lock));
+ if (!map || !dm_table_get_size(map))
+ goto out;
+
+ r = __dm_resume(md, map);
+ if (r)
+ goto out;
+
clear_bit(DMF_SUSPENDED, &md->flags);
r = 0;
* Internal suspend/resume works like userspace-driven suspend. It waits
* until all bios finish and prevents issuing new bios to the target drivers.
* It may be used only from the kernel.
- *
- * Internal suspend holds md->suspend_lock, which prevents interaction with
- * userspace-driven suspend.
*/
-void dm_internal_suspend(struct mapped_device *md)
+static void __dm_internal_suspend(struct mapped_device *md, unsigned suspend_flags)
{
- mutex_lock(&md->suspend_lock);
+ struct dm_table *map = NULL;
+
+ if (dm_suspended_internally_md(md))
+ return; /* nested internal suspend */
+
+ if (dm_suspended_md(md)) {
+ set_bit(DMF_SUSPENDED_INTERNALLY, &md->flags);
+ return; /* nest suspend */
+ }
+
+ map = rcu_dereference_protected(md->map, lockdep_is_held(&md->suspend_lock));
+
+ /*
+ * Using TASK_UNINTERRUPTIBLE because only NOFLUSH internal suspend is
+ * supported. Properly supporting a TASK_INTERRUPTIBLE internal suspend
+ * would require changing .presuspend to return an error -- avoid this
+ * until there is a need for more elaborate variants of internal suspend.
+ */
+ (void) __dm_suspend(md, map, suspend_flags, TASK_UNINTERRUPTIBLE);
+
+ set_bit(DMF_SUSPENDED_INTERNALLY, &md->flags);
+
+ dm_table_postsuspend_targets(map);
+}
+
+static void __dm_internal_resume(struct mapped_device *md)
+{
+ if (!dm_suspended_internally_md(md))
+ return; /* resume from nested internal suspend */
+
if (dm_suspended_md(md))
+ goto done; /* resume from nested suspend */
+
+ /*
+ * NOTE: existing callers don't need to call dm_table_resume_targets
+ * (which may fail -- so best to avoid it for now by passing NULL map)
+ */
+ (void) __dm_resume(md, NULL);
+
+done:
+ clear_bit(DMF_SUSPENDED_INTERNALLY, &md->flags);
+ smp_mb__after_atomic();
+ wake_up_bit(&md->flags, DMF_SUSPENDED_INTERNALLY);
+}
+
+void dm_internal_suspend_noflush(struct mapped_device *md)
+{
+ mutex_lock(&md->suspend_lock);
+ __dm_internal_suspend(md, DM_SUSPEND_NOFLUSH_FLAG);
+ mutex_unlock(&md->suspend_lock);
+}
+EXPORT_SYMBOL_GPL(dm_internal_suspend_noflush);
+
+void dm_internal_resume(struct mapped_device *md)
+{
+ mutex_lock(&md->suspend_lock);
+ __dm_internal_resume(md);
+ mutex_unlock(&md->suspend_lock);
+}
+EXPORT_SYMBOL_GPL(dm_internal_resume);
+
+/*
+ * Fast variants of internal suspend/resume hold md->suspend_lock,
+ * which prevents interaction with userspace-driven suspend.
+ */
+
+void dm_internal_suspend_fast(struct mapped_device *md)
+{
+ mutex_lock(&md->suspend_lock);
+ if (dm_suspended_md(md) || dm_suspended_internally_md(md))
return;
set_bit(DMF_BLOCK_IO_FOR_SUSPEND, &md->flags);
dm_wait_for_completion(md, TASK_UNINTERRUPTIBLE);
}
-void dm_internal_resume(struct mapped_device *md)
+void dm_internal_resume_fast(struct mapped_device *md)
{
- if (dm_suspended_md(md))
+ if (dm_suspended_md(md) || dm_suspended_internally_md(md))
goto done;
dm_queue_flush(md);
return test_bit(DMF_SUSPENDED, &md->flags);
}
+int dm_suspended_internally_md(struct mapped_device *md)
+{
+ return test_bit(DMF_SUSPENDED_INTERNALLY, &md->flags);
+}
+
int dm_test_deferred_remove_flag(struct mapped_device *md)
{
return test_bit(DMF_DEFERRED_REMOVE, &md->flags);
struct queue_limits *limits);
struct list_head *dm_table_get_devices(struct dm_table *t);
void dm_table_presuspend_targets(struct dm_table *t);
+void dm_table_presuspend_undo_targets(struct dm_table *t);
void dm_table_postsuspend_targets(struct dm_table *t);
int dm_table_resume_targets(struct dm_table *t);
int dm_table_any_congested(struct dm_table *t, int bdi_bits);
*/
int dm_suspended_md(struct mapped_device *md);
+/*
+ * Internal suspend and resume methods.
+ */
+int dm_suspended_internally_md(struct mapped_device *md);
+void dm_internal_suspend_fast(struct mapped_device *md);
+void dm_internal_resume_fast(struct mapped_device *md);
+void dm_internal_suspend_noflush(struct mapped_device *md);
+void dm_internal_resume(struct mapped_device *md);
+
/*
* Test if the device is scheduled for deferred remove.
*/
printk("md: %s still in use.\n",mdname(mddev));
if (did_freeze) {
clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
+ set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
md_wakeup_thread(mddev->thread);
}
err = -EBUSY;
mddev->ro = 1;
set_disk_ro(mddev->gendisk, 1);
clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
+ set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
+ md_wakeup_thread(mddev->thread);
sysfs_notify_dirent_safe(mddev->sysfs_state);
err = 0;
}
mutex_unlock(&mddev->open_mutex);
if (did_freeze) {
clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
+ set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
md_wakeup_thread(mddev->thread);
}
return -EBUSY;
int r;
struct resize resize;
- if (old_size == new_size)
+ if (old_size == new_size) {
+ *new_root = root;
return 0;
+ }
resize.info = info;
resize.root = root;
} __packed;
+/*
+ * Locks a block using the btree node validator.
+ */
+int bn_read_lock(struct dm_btree_info *info, dm_block_t b,
+ struct dm_block **result);
+
void inc_children(struct dm_transaction_manager *tm, struct btree_node *n,
struct dm_btree_value_type *vt);
/*----------------------------------------------------------------*/
-static int bn_read_lock(struct dm_btree_info *info, dm_block_t b,
+int bn_read_lock(struct dm_btree_info *info, dm_block_t b,
struct dm_block **result)
{
return dm_tm_read_lock(info->tm, b, &btree_node_validator, result);
* FIXME: We shouldn't use a recursive algorithm when we have limited stack
* space. Also this only works for single level trees.
*/
-static int walk_node(struct ro_spine *s, dm_block_t block,
+static int walk_node(struct dm_btree_info *info, dm_block_t block,
int (*fn)(void *context, uint64_t *keys, void *leaf),
void *context)
{
int r;
unsigned i, nr;
+ struct dm_block *node;
struct btree_node *n;
uint64_t keys;
- r = ro_step(s, block);
- n = ro_node(s);
+ r = bn_read_lock(info, block, &node);
+ if (r)
+ return r;
+
+ n = dm_block_data(node);
nr = le32_to_cpu(n->header.nr_entries);
for (i = 0; i < nr; i++) {
if (le32_to_cpu(n->header.flags) & INTERNAL_NODE) {
- r = walk_node(s, value64(n, i), fn, context);
+ r = walk_node(info, value64(n, i), fn, context);
if (r)
goto out;
} else {
}
out:
- ro_pop(s);
+ dm_tm_unlock(info->tm, node);
return r;
}
int (*fn)(void *context, uint64_t *keys, void *leaf),
void *context)
{
- int r;
- struct ro_spine spine;
-
BUG_ON(info->levels > 1);
-
- init_ro_spine(&spine, info);
- r = walk_node(&spine, root, fn, context);
- exit_ro_spine(&spine);
-
- return r;
+ return walk_node(info, root, fn, context);
}
EXPORT_SYMBOL_GPL(dm_btree_walk);
{
struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
- return smm->ll.nr_blocks;
+ *count = smm->ll.nr_blocks;
+
+ return 0;
}
static int sm_bootstrap_get_nr_free(struct dm_space_map *sm, dm_block_t *count)
{
struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
- return b < smm->begin ? 1 : 0;
+ *result = (b < smm->begin) ? 1 : 0;
+
+ return 0;
}
static int sm_bootstrap_count_is_more_than_one(struct dm_space_map *sm,
#include "dm-persistent-data-internal.h"
#include <linux/export.h>
+#include <linux/mutex.h>
+#include <linux/hash.h>
#include <linux/slab.h>
#include <linux/device-mapper.h>
/*----------------------------------------------------------------*/
+#define PREFETCH_SIZE 128
+#define PREFETCH_BITS 7
+#define PREFETCH_SENTINEL ((dm_block_t) -1ULL)
+
+struct prefetch_set {
+ struct mutex lock;
+ dm_block_t blocks[PREFETCH_SIZE];
+};
+
+static unsigned prefetch_hash(dm_block_t b)
+{
+ return hash_64(b, PREFETCH_BITS);
+}
+
+static void prefetch_wipe(struct prefetch_set *p)
+{
+ unsigned i;
+ for (i = 0; i < PREFETCH_SIZE; i++)
+ p->blocks[i] = PREFETCH_SENTINEL;
+}
+
+static void prefetch_init(struct prefetch_set *p)
+{
+ mutex_init(&p->lock);
+ prefetch_wipe(p);
+}
+
+static void prefetch_add(struct prefetch_set *p, dm_block_t b)
+{
+ unsigned h = prefetch_hash(b);
+
+ mutex_lock(&p->lock);
+ if (p->blocks[h] == PREFETCH_SENTINEL)
+ p->blocks[h] = b;
+
+ mutex_unlock(&p->lock);
+}
+
+static void prefetch_issue(struct prefetch_set *p, struct dm_block_manager *bm)
+{
+ unsigned i;
+
+ mutex_lock(&p->lock);
+
+ for (i = 0; i < PREFETCH_SIZE; i++)
+ if (p->blocks[i] != PREFETCH_SENTINEL) {
+ dm_bm_prefetch(bm, p->blocks[i]);
+ p->blocks[i] = PREFETCH_SENTINEL;
+ }
+
+ mutex_unlock(&p->lock);
+}
+
+/*----------------------------------------------------------------*/
+
struct shadow_info {
struct hlist_node hlist;
dm_block_t where;
spinlock_t lock;
struct hlist_head buckets[DM_HASH_SIZE];
+
+ struct prefetch_set prefetches;
};
/*----------------------------------------------------------------*/
for (i = 0; i < DM_HASH_SIZE; i++)
INIT_HLIST_HEAD(tm->buckets + i);
+ prefetch_init(&tm->prefetches);
+
return tm;
}
struct dm_block_validator *v,
struct dm_block **blk)
{
- if (tm->is_clone)
- return dm_bm_read_try_lock(tm->real->bm, b, v, blk);
+ if (tm->is_clone) {
+ int r = dm_bm_read_try_lock(tm->real->bm, b, v, blk);
+
+ if (r == -EWOULDBLOCK)
+ prefetch_add(&tm->real->prefetches, b);
+
+ return r;
+ }
return dm_bm_read_lock(tm->bm, b, v, blk);
}
return tm->bm;
}
+void dm_tm_issue_prefetches(struct dm_transaction_manager *tm)
+{
+ prefetch_issue(&tm->prefetches, tm->bm);
+}
+EXPORT_SYMBOL_GPL(dm_tm_issue_prefetches);
+
/*----------------------------------------------------------------*/
static int dm_tm_create_internal(struct dm_block_manager *bm,
struct dm_block_manager *dm_tm_get_bm(struct dm_transaction_manager *tm);
+/*
+ * If you're using a non-blocking clone the tm will build up a list of
+ * requested blocks that weren't in core. This call will request those
+ * blocks to be prefetched.
+ */
+void dm_tm_issue_prefetches(struct dm_transaction_manager *tm);
+
/*
* A little utility that ties the knot by producing a transaction manager
* that has a space map managed by the transaction manager...
case SYS_ATSC:
c->modulation = VSB_8;
break;
+ case SYS_ISDBS:
+ c->symbol_rate = 28860000;
+ c->rolloff = ROLLOFF_35;
+ c->bandwidth_hz = c->symbol_rate / 100 * 135;
+ break;
default:
c->modulation = QAM_AUTO;
break;
break;
case SYS_DVBS:
case SYS_TURBO:
+ case SYS_ISDBS:
rolloff = 135;
break;
case SYS_DVBS2:
memcpy(&state->frontend.ops, &ds3000_ops,
sizeof(struct dvb_frontend_ops));
state->frontend.demodulator_priv = state;
+
+ /*
+ * Some devices like T480 starts with voltage on. Be sure
+ * to turn voltage off during init, as this can otherwise
+ * interfere with Unicable SCR systems.
+ */
+ ds3000_set_voltage(&state->frontend, SEC_VOLTAGE_OFF);
return &state->frontend;
error3:
return s->status;
}
-int sp2_init(struct sp2 *s)
+static int sp2_init(struct sp2 *s)
{
int ret = 0;
u8 buf;
return ret;
}
-int sp2_exit(struct i2c_client *client)
+static int sp2_exit(struct i2c_client *client)
{
struct sp2 *s;
c->delivery_system = SYS_ISDBS;
layers = 0;
- ret = reg_read(state, 0xe8, val, 3);
+ ret = reg_read(state, 0xe6, val, 5);
if (ret == 0) {
- int slots;
u8 v;
+ c->stream_id = val[0] << 8 | val[1];
+
/* high/single layer */
- v = (val[0] & 0x70) >> 4;
+ v = (val[2] & 0x70) >> 4;
c->modulation = (v == 7) ? PSK_8 : QPSK;
c->fec_inner = fec_conv_sat[v];
c->layer[0].fec = c->fec_inner;
c->layer[0].modulation = c->modulation;
- c->layer[0].segment_count = val[1] & 0x3f; /* slots */
+ c->layer[0].segment_count = val[3] & 0x3f; /* slots */
/* low layer */
- v = (val[0] & 0x07);
+ v = (val[2] & 0x07);
c->layer[1].fec = fec_conv_sat[v];
if (v == 0) /* no low layer */
c->layer[1].segment_count = 0;
else
- c->layer[1].segment_count = val[2] & 0x3f; /* slots */
+ c->layer[1].segment_count = val[4] & 0x3f; /* slots */
/* actually, BPSK if v==1, but not defined in fe_modulation_t */
c->layer[1].modulation = QPSK;
layers = (v > 0) ? 2 : 1;
-
- slots = c->layer[0].segment_count + c->layer[1].segment_count;
- c->symbol_rate = 28860000 * slots / 48;
}
/* statistics */
u8 v;
c->isdbt_partial_reception = val[0] & 0x01;
- c->isdbt_sb_mode = (val[0] & 0xc0) == 0x01;
+ c->isdbt_sb_mode = (val[0] & 0xc0) == 0x40;
/* layer A */
v = (val[2] & 0x78) >> 3;
ret = smiapp_set_compose(subdev, fh, sel);
break;
default:
- BUG();
+ ret = -EINVAL;
}
mutex_unlock(&sensor->mutex);
for (line = 0; line < lines; line++) {
while (offset && offset >= sg_dma_len(sg)) {
offset -= sg_dma_len(sg);
- sg++;
+ sg = sg_next(sg);
}
if (lpi && line > 0 && !(line % lpi))
*(rp++) = cpu_to_le32(0); /* bits 63-32 */
todo -= (sg_dma_len(sg)-offset);
offset = 0;
- sg++;
+ sg = sg_next(sg);
while (todo > sg_dma_len(sg)) {
*(rp++) = cpu_to_le32(RISC_WRITE|
sg_dma_len(sg));
*(rp++) = cpu_to_le32(sg_dma_address(sg));
*(rp++) = cpu_to_le32(0); /* bits 63-32 */
todo -= sg_dma_len(sg);
- sg++;
+ sg = sg_next(sg);
}
*(rp++) = cpu_to_le32(RISC_WRITE|RISC_EOL|todo);
*(rp++) = cpu_to_le32(sg_dma_address(sg));
if (!status)
return IRQ_NONE;
- if (status & ~solo_dev->irq_mask) {
- solo_reg_write(solo_dev, SOLO_IRQ_STAT,
- status & ~solo_dev->irq_mask);
- status &= solo_dev->irq_mask;
- }
+ /* Acknowledge all interrupts immediately */
+ solo_reg_write(solo_dev, SOLO_IRQ_STAT, status);
if (status & SOLO_IRQ_PCI_ERR)
solo_p2m_error_isr(solo_dev);
if (status & SOLO_IRQ_G723)
solo_g723_isr(solo_dev);
- /* Clear all interrupts handled */
- solo_reg_write(solo_dev, SOLO_IRQ_STAT, status);
-
return IRQ_HANDLED;
}
"\t\t bit 0=crop, 1=compose, 2=scale,\n"
"\t\t -1=user-controlled (default)");
-static unsigned multiplanar[VIVID_MAX_DEVS];
+static unsigned multiplanar[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 1 };
module_param_array(multiplanar, uint, NULL, 0444);
-MODULE_PARM_DESC(multiplanar, " 0 (default) is alternating single and multiplanar devices,\n"
- "\t\t 1 is single planar devices,\n"
- "\t\t 2 is multiplanar devices");
+MODULE_PARM_DESC(multiplanar, " 1 (default) creates a single planar device, 2 creates a multiplanar device.");
/* Default: video + vbi-cap (raw and sliced) + radio rx + radio tx + sdr + vbi-out + vid-out */
static unsigned node_types[VIVID_MAX_DEVS] = { [0 ... (VIVID_MAX_DEVS - 1)] = 0x1d3d };
/* start detecting feature set */
/* do we use single- or multi-planar? */
- if (multiplanar[inst] == 0)
- dev->multiplanar = inst & 1;
- else
- dev->multiplanar = multiplanar[inst] > 1;
+ dev->multiplanar = multiplanar[inst] > 1;
v4l2_info(&dev->v4l2_dev, "using %splanar format API\n",
dev->multiplanar ? "multi" : "single ");
if (press_type == 0)
rc_keyup(ictx->rdev);
else {
- if (ictx->rc_type == RC_BIT_RC6_MCE)
+ if (ictx->rc_type == RC_BIT_RC6_MCE ||
+ ictx->rc_type == RC_BIT_OTHER)
rc_keydown(ictx->rdev,
ictx->rc_type == RC_BIT_RC6_MCE ? RC_TYPE_RC6_MCE : RC_TYPE_OTHER,
ictx->rc_scancode, ictx->rc_toggle);
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int hix5hd2_ir_suspend(struct device *dev)
{
struct hix5hd2_ir_priv *priv = dev_get_drvdata(dev);
u32 scancode;
enum rc_type protocol;
- if (!(dev->enabled_protocols & (RC_BIT_RC5 | RC_BIT_RC5X)))
+ if (!(dev->enabled_protocols & (RC_BIT_RC5 | RC_BIT_RC5X | RC_BIT_RC5_SZ)))
return 0;
if (!is_timing_event(ev)) {
case 32:
if ((scancode & RC6_6A_LCC_MASK) == RC6_6A_MCE_CC) {
protocol = RC_TYPE_RC6_MCE;
- scancode &= ~RC6_6A_MCE_TOGGLE_MASK;
toggle = !!(scancode & RC6_6A_MCE_TOGGLE_MASK);
+ scancode &= ~RC6_6A_MCE_TOGGLE_MASK;
} else {
protocol = RC_BIT_RC6_6A_32;
toggle = 0;
return -ENOMEM;
dev->raw->dev = dev;
- dev->enabled_protocols = ~0;
dev->change_protocol = change_protocol;
rc = kfifo_alloc(&dev->raw->kfifo,
sizeof(struct ir_raw_event) * MAX_IR_EVENT_SIZE,
if (dev->change_protocol) {
u64 rc_type = (1 << rc_map->rc_type);
+ if (dev->driver_type == RC_DRIVER_IR_RAW)
+ rc_type |= RC_BIT_LIRC;
rc = dev->change_protocol(dev, &rc_type);
if (rc < 0)
goto out_raw;
break;
case V4L2_PIX_FMT_JPEG:
case V4L2_PIX_FMT_MJPEG:
- buf->vb.v4l2_buf.length = jpgsize;
+ vb2_set_plane_payload(&buf->vb, 0, jpgsize);
memcpy(vbuf, tmpbuf, jpgsize);
break;
case V4L2_PIX_FMT_YUV422P:
.cmd_per_lun = 7,
.use_clustering = ENABLE_CLUSTERING,
.shost_attrs = mptscsih_host_attrs,
+ .use_blk_tags = 1,
};
static int mptsas_get_linkerrors(struct sas_phy *phy)
* mptscsih_change_queue_depth - This function will set a devices queue depth
* @sdev: per scsi_device pointer
* @qdepth: requested queue depth
- * @reason: calling context
*
* Adding support for new 'change_queue_depth' api.
*/
int
-mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
+mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
{
MPT_SCSI_HOST *hd = shost_priv(sdev->host);
VirtTarget *vtarget;
struct scsi_target *starget;
int max_depth;
- int tagged;
MPT_ADAPTER *ioc = hd->ioc;
starget = scsi_target(sdev);
vtarget = starget->hostdata;
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
-
if (ioc->bus_type == SPI) {
if (!(vtarget->tflags & MPT_TARGET_FLAGS_Q_YES))
max_depth = 1;
if (qdepth > max_depth)
qdepth = max_depth;
- if (qdepth == 1)
- tagged = 0;
- else
- tagged = MSG_SIMPLE_TAG;
- scsi_adjust_queue_depth(sdev, tagged, qdepth);
- return sdev->queue_depth;
+ return scsi_change_queue_depth(sdev, qdepth);
}
/*
ioc->name, vtarget->negoFlags, vtarget->maxOffset,
vtarget->minSyncFactor));
- mptscsih_change_queue_depth(sdev, MPT_SCSI_CMD_PER_DEV_HIGH,
- SCSI_QDEPTH_DEFAULT);
+ mptscsih_change_queue_depth(sdev, MPT_SCSI_CMD_PER_DEV_HIGH);
dsprintk(ioc, printk(MYIOC_s_DEBUG_FMT
- "tagged %d, simple %d, ordered %d\n",
- ioc->name,sdev->tagged_supported, sdev->simple_tags,
- sdev->ordered_tags));
+ "tagged %d, simple %d\n",
+ ioc->name,sdev->tagged_supported, sdev->simple_tags));
blk_queue_dma_alignment (sdev->request_queue, 512 - 1);
extern int mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
extern int mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply);
extern int mptscsih_ioc_reset(MPT_ADAPTER *ioc, int post_reset);
-extern int mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth,
- int reason);
+extern int mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth);
extern u8 mptscsih_raid_id_to_num(MPT_ADAPTER *ioc, u8 channel, u8 id);
extern int mptscsih_is_phys_disk(MPT_ADAPTER *ioc, u8 channel, u8 id);
extern struct device_attribute *mptscsih_host_attrs[];
additional drivers must be enabled in order to use the
functionality of the device.
+config MFD_ATMEL_HLCDC
+ tristate "Atmel HLCDC (High-end LCD Controller)"
+ select MFD_CORE
+ select REGMAP_MMIO
+ depends on OF
+ help
+ If you say yes here you get support for the HLCDC block.
+ This driver provides common support for accessing the device,
+ additional drivers must be enabled in order to use the
+ functionality of the device.
+
config MFD_BCM590XX
tristate "Broadcom BCM590xx PMUs"
select MFD_CORE
select REGMAP_IRQ
depends on I2C=y
help
- If you say Y here you get support for the X-Powers AXP202 and AXP209.
+ If you say Y here you get support for the X-Powers AXP202, AXP209 and
+ AXP288 power management IC (PMIC).
This driver include only the core APIs. You have to select individual
components like regulators or the PEK (Power Enable Key) under the
corresponding menus.
Additional drivers must be enabled in order to use the functionality
of the device.
+config MFD_DLN2
+ tristate "Diolan DLN2 support"
+ select MFD_CORE
+ depends on USB
+ help
+ This adds support for Diolan USB-I2C/SPI/GPIO Master Adapter
+ DLN-2. Additional drivers such as I2C_DLN2, GPIO_DLN2,
+ etc. must be enabled in order to use the functionality of
+ the device.
+
config MFD_MC13XXX
tristate
depends on (SPI_MASTER || I2C)
select MFD_CORE
select REGMAP_I2C
select REGMAP_IRQ
- select REGULATOR
help
Support for the Samsung Electronics MFD series.
This driver provides common support for accessing the device,
obj-$(CONFIG_MFD_CROS_EC_I2C) += cros_ec_i2c.o
obj-$(CONFIG_MFD_CROS_EC_SPI) += cros_ec_spi.o
-rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o
+rtsx_pci-objs := rtsx_pcr.o rtsx_gops.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o
obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o
obj-$(CONFIG_MFD_RTSX_USB) += rtsx_usb.o
obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o
obj-$(CONFIG_MFD_TPS65090) += tps65090.o
obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o
+obj-$(CONFIG_MFD_ATMEL_HLCDC) += atmel-hlcdc.o
obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o
obj-$(CONFIG_MFD_PALMAS) += palmas.o
obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o
obj-$(CONFIG_MFD_IPAQ_MICRO) += ipaq-micro.o
obj-$(CONFIG_MFD_MENF21BMC) += menf21bmc.o
obj-$(CONFIG_MFD_HI6421_PMIC) += hi6421-pmic-core.o
+obj-$(CONFIG_MFD_DLN2) += dln2.o
intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o
obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o
}
}
-/*
- * Use the AB WD to reset the platform. It will perform a hard
- * reset instead of a soft reset. Write the reset reason to
- * the AB before reset, which can be read upon restart.
- */
-void ab8500_restart(char mode, const char *cmd)
-{
- struct ab8500_platform_data *plat;
- struct ab8500_sysctrl_platform_data *pdata;
- u16 reason = 0;
- u8 val;
-
- if (sysctrl_dev == NULL) {
- pr_err("%s: sysctrl not initialized\n", __func__);
- return;
- }
-
- plat = dev_get_platdata(sysctrl_dev->parent);
- pdata = plat->sysctrl;
- if (pdata && pdata->reboot_reason_code)
- reason = pdata->reboot_reason_code(cmd);
- else
- pr_warn("[%s] No reboot reason set. Default reason %d\n",
- __func__, reason);
-
- /*
- * Disable RTC alarm, just a precaution so that no alarm
- * is running when WD reset is executed.
- */
- abx500_get_register_interruptible(sysctrl_dev, AB8500_RTC,
- RTC_CTRL , &val);
- abx500_set_register_interruptible(sysctrl_dev, AB8500_RTC,
- RTC_CTRL , (val & ~RTC_ALARM_ENABLE));
-
- /*
- * Android is not using the RTC alarm registers during reboot
- * so we borrow them for writing the reason of reset
- */
-
- /* reason[8 LSB] */
- val = reason & 0xFF;
- abx500_set_register_interruptible(sysctrl_dev, AB8500_RTC,
- AB8500_ALARM_MIN_LOW , val);
-
- /* reason[8 MSB] */
- val = (reason>>8) & 0xFF;
- abx500_set_register_interruptible(sysctrl_dev, AB8500_RTC,
- AB8500_ALARM_MIN_MID , val);
-
- /* Setting WD timeout to 0 */
- ab8500_sysctrl_write(AB8500_MAINWDOGTIMER, 0xFF, 0x0);
-
- /* Setting the parameters to AB8500 WD*/
- ab8500_sysctrl_write(AB8500_MAINWDOGCTRL, 0xFF, (AB8500_ENABLE_WD |
- AB8500_WD_RESTART_ON_EXPIRE | AB8500_KICK_WD));
-}
-
static inline bool valid_bank(u8 bank)
{
return ((bank == AB8500_SYS_CTRL1_BLOCK) ||
static int arizona_spi_remove(struct spi_device *spi)
{
struct arizona *arizona = spi_get_drvdata(spi);
+
arizona_dev_exit(arizona);
+
return 0;
}
--- /dev/null
+/*
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/mfd/atmel-hlcdc.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define ATMEL_HLCDC_REG_MAX (0x4000 - 0x4)
+
+static const struct mfd_cell atmel_hlcdc_cells[] = {
+ {
+ .name = "atmel-hlcdc-pwm",
+ .of_compatible = "atmel,hlcdc-pwm",
+ },
+ {
+ .name = "atmel-hlcdc-dc",
+ .of_compatible = "atmel,hlcdc-display-controller",
+ },
+};
+
+static const struct regmap_config atmel_hlcdc_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = ATMEL_HLCDC_REG_MAX,
+};
+
+static int atmel_hlcdc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct atmel_hlcdc *hlcdc;
+ struct resource *res;
+ void __iomem *regs;
+
+ hlcdc = devm_kzalloc(dev, sizeof(*hlcdc), GFP_KERNEL);
+ if (!hlcdc)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ hlcdc->irq = platform_get_irq(pdev, 0);
+ if (hlcdc->irq < 0)
+ return hlcdc->irq;
+
+ hlcdc->periph_clk = devm_clk_get(dev, "periph_clk");
+ if (IS_ERR(hlcdc->periph_clk)) {
+ dev_err(dev, "failed to get peripheral clock\n");
+ return PTR_ERR(hlcdc->periph_clk);
+ }
+
+ hlcdc->sys_clk = devm_clk_get(dev, "sys_clk");
+ if (IS_ERR(hlcdc->sys_clk)) {
+ dev_err(dev, "failed to get system clock\n");
+ return PTR_ERR(hlcdc->sys_clk);
+ }
+
+ hlcdc->slow_clk = devm_clk_get(dev, "slow_clk");
+ if (IS_ERR(hlcdc->slow_clk)) {
+ dev_err(dev, "failed to get slow clock\n");
+ return PTR_ERR(hlcdc->slow_clk);
+ }
+
+ hlcdc->regmap = devm_regmap_init_mmio(dev, regs,
+ &atmel_hlcdc_regmap_config);
+ if (IS_ERR(hlcdc->regmap))
+ return PTR_ERR(hlcdc->regmap);
+
+ dev_set_drvdata(dev, hlcdc);
+
+ return mfd_add_devices(dev, -1, atmel_hlcdc_cells,
+ ARRAY_SIZE(atmel_hlcdc_cells),
+ NULL, 0, NULL);
+}
+
+static int atmel_hlcdc_remove(struct platform_device *pdev)
+{
+ mfd_remove_devices(&pdev->dev);
+
+ return 0;
+}
+
+static const struct of_device_id atmel_hlcdc_match[] = {
+ { .compatible = "atmel,sama5d3-hlcdc" },
+ { /* sentinel */ },
+};
+
+static struct platform_driver atmel_hlcdc_driver = {
+ .probe = atmel_hlcdc_probe,
+ .remove = atmel_hlcdc_remove,
+ .driver = {
+ .name = "atmel-hlcdc",
+ .of_match_table = atmel_hlcdc_match,
+ },
+};
+module_platform_driver(atmel_hlcdc_driver);
+
+MODULE_ALIAS("platform:atmel-hlcdc");
+MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
+MODULE_DESCRIPTION("Atmel HLCDC driver");
+MODULE_LICENSE("GPL v2");
/*
- * axp20x.c - MFD core driver for the X-Powers AXP202 and AXP209
+ * axp20x.c - MFD core driver for the X-Powers' Power Management ICs
*
- * AXP20x comprises an adaptive USB-Compatible PWM charger, 2 BUCK DC-DC
- * converters, 5 LDOs, multiple 12-bit ADCs of voltage, current and temperature
- * as well as 4 configurable GPIOs.
+ * AXP20x typically comprises an adaptive USB-Compatible PWM charger, BUCK DC-DC
+ * converters, LDOs, multiple 12-bit ADCs of voltage, current and temperature
+ * as well as configurable GPIOs.
*
* Author: Carlo Caione <carlo@caione.org>
*
#include <linux/mfd/core.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
+#include <linux/acpi.h>
#define AXP20X_OFF 0x80
+static const char const *axp20x_model_names[] = {
+ "AXP202",
+ "AXP209",
+ "AXP288",
+};
+
static const struct regmap_range axp20x_writeable_ranges[] = {
regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE),
regmap_reg_range(AXP20X_DCDC_MODE, AXP20X_FG_RES),
.n_yes_ranges = ARRAY_SIZE(axp20x_volatile_ranges),
};
+static const struct regmap_range axp288_writeable_ranges[] = {
+ regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ6_STATE),
+ regmap_reg_range(AXP20X_DCDC_MODE, AXP288_FG_TUNE5),
+};
+
+static const struct regmap_range axp288_volatile_ranges[] = {
+ regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IPSOUT_V_HIGH_L),
+};
+
+static const struct regmap_access_table axp288_writeable_table = {
+ .yes_ranges = axp288_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(axp288_writeable_ranges),
+};
+
+static const struct regmap_access_table axp288_volatile_table = {
+ .yes_ranges = axp288_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(axp288_volatile_ranges),
+};
+
static struct resource axp20x_pek_resources[] = {
{
.name = "PEK_DBR",
},
};
+static struct resource axp288_battery_resources[] = {
+ {
+ .start = AXP288_IRQ_QWBTU,
+ .end = AXP288_IRQ_QWBTU,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = AXP288_IRQ_WBTU,
+ .end = AXP288_IRQ_WBTU,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = AXP288_IRQ_QWBTO,
+ .end = AXP288_IRQ_QWBTO,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = AXP288_IRQ_WBTO,
+ .end = AXP288_IRQ_WBTO,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = AXP288_IRQ_WL2,
+ .end = AXP288_IRQ_WL2,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = AXP288_IRQ_WL1,
+ .end = AXP288_IRQ_WL1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
static const struct regmap_config axp20x_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.cache_type = REGCACHE_RBTREE,
};
-#define AXP20X_IRQ(_irq, _off, _mask) \
- [AXP20X_IRQ_##_irq] = { .reg_offset = (_off), .mask = BIT(_mask) }
+static const struct regmap_config axp288_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .wr_table = &axp288_writeable_table,
+ .volatile_table = &axp288_volatile_table,
+ .max_register = AXP288_FG_TUNE5,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+#define INIT_REGMAP_IRQ(_variant, _irq, _off, _mask) \
+ [_variant##_IRQ_##_irq] = { .reg_offset = (_off), .mask = BIT(_mask) }
static const struct regmap_irq axp20x_regmap_irqs[] = {
- AXP20X_IRQ(ACIN_OVER_V, 0, 7),
- AXP20X_IRQ(ACIN_PLUGIN, 0, 6),
- AXP20X_IRQ(ACIN_REMOVAL, 0, 5),
- AXP20X_IRQ(VBUS_OVER_V, 0, 4),
- AXP20X_IRQ(VBUS_PLUGIN, 0, 3),
- AXP20X_IRQ(VBUS_REMOVAL, 0, 2),
- AXP20X_IRQ(VBUS_V_LOW, 0, 1),
- AXP20X_IRQ(BATT_PLUGIN, 1, 7),
- AXP20X_IRQ(BATT_REMOVAL, 1, 6),
- AXP20X_IRQ(BATT_ENT_ACT_MODE, 1, 5),
- AXP20X_IRQ(BATT_EXIT_ACT_MODE, 1, 4),
- AXP20X_IRQ(CHARG, 1, 3),
- AXP20X_IRQ(CHARG_DONE, 1, 2),
- AXP20X_IRQ(BATT_TEMP_HIGH, 1, 1),
- AXP20X_IRQ(BATT_TEMP_LOW, 1, 0),
- AXP20X_IRQ(DIE_TEMP_HIGH, 2, 7),
- AXP20X_IRQ(CHARG_I_LOW, 2, 6),
- AXP20X_IRQ(DCDC1_V_LONG, 2, 5),
- AXP20X_IRQ(DCDC2_V_LONG, 2, 4),
- AXP20X_IRQ(DCDC3_V_LONG, 2, 3),
- AXP20X_IRQ(PEK_SHORT, 2, 1),
- AXP20X_IRQ(PEK_LONG, 2, 0),
- AXP20X_IRQ(N_OE_PWR_ON, 3, 7),
- AXP20X_IRQ(N_OE_PWR_OFF, 3, 6),
- AXP20X_IRQ(VBUS_VALID, 3, 5),
- AXP20X_IRQ(VBUS_NOT_VALID, 3, 4),
- AXP20X_IRQ(VBUS_SESS_VALID, 3, 3),
- AXP20X_IRQ(VBUS_SESS_END, 3, 2),
- AXP20X_IRQ(LOW_PWR_LVL1, 3, 1),
- AXP20X_IRQ(LOW_PWR_LVL2, 3, 0),
- AXP20X_IRQ(TIMER, 4, 7),
- AXP20X_IRQ(PEK_RIS_EDGE, 4, 6),
- AXP20X_IRQ(PEK_FAL_EDGE, 4, 5),
- AXP20X_IRQ(GPIO3_INPUT, 4, 3),
- AXP20X_IRQ(GPIO2_INPUT, 4, 2),
- AXP20X_IRQ(GPIO1_INPUT, 4, 1),
- AXP20X_IRQ(GPIO0_INPUT, 4, 0),
+ INIT_REGMAP_IRQ(AXP20X, ACIN_OVER_V, 0, 7),
+ INIT_REGMAP_IRQ(AXP20X, ACIN_PLUGIN, 0, 6),
+ INIT_REGMAP_IRQ(AXP20X, ACIN_REMOVAL, 0, 5),
+ INIT_REGMAP_IRQ(AXP20X, VBUS_OVER_V, 0, 4),
+ INIT_REGMAP_IRQ(AXP20X, VBUS_PLUGIN, 0, 3),
+ INIT_REGMAP_IRQ(AXP20X, VBUS_REMOVAL, 0, 2),
+ INIT_REGMAP_IRQ(AXP20X, VBUS_V_LOW, 0, 1),
+ INIT_REGMAP_IRQ(AXP20X, BATT_PLUGIN, 1, 7),
+ INIT_REGMAP_IRQ(AXP20X, BATT_REMOVAL, 1, 6),
+ INIT_REGMAP_IRQ(AXP20X, BATT_ENT_ACT_MODE, 1, 5),
+ INIT_REGMAP_IRQ(AXP20X, BATT_EXIT_ACT_MODE, 1, 4),
+ INIT_REGMAP_IRQ(AXP20X, CHARG, 1, 3),
+ INIT_REGMAP_IRQ(AXP20X, CHARG_DONE, 1, 2),
+ INIT_REGMAP_IRQ(AXP20X, BATT_TEMP_HIGH, 1, 1),
+ INIT_REGMAP_IRQ(AXP20X, BATT_TEMP_LOW, 1, 0),
+ INIT_REGMAP_IRQ(AXP20X, DIE_TEMP_HIGH, 2, 7),
+ INIT_REGMAP_IRQ(AXP20X, CHARG_I_LOW, 2, 6),
+ INIT_REGMAP_IRQ(AXP20X, DCDC1_V_LONG, 2, 5),
+ INIT_REGMAP_IRQ(AXP20X, DCDC2_V_LONG, 2, 4),
+ INIT_REGMAP_IRQ(AXP20X, DCDC3_V_LONG, 2, 3),
+ INIT_REGMAP_IRQ(AXP20X, PEK_SHORT, 2, 1),
+ INIT_REGMAP_IRQ(AXP20X, PEK_LONG, 2, 0),
+ INIT_REGMAP_IRQ(AXP20X, N_OE_PWR_ON, 3, 7),
+ INIT_REGMAP_IRQ(AXP20X, N_OE_PWR_OFF, 3, 6),
+ INIT_REGMAP_IRQ(AXP20X, VBUS_VALID, 3, 5),
+ INIT_REGMAP_IRQ(AXP20X, VBUS_NOT_VALID, 3, 4),
+ INIT_REGMAP_IRQ(AXP20X, VBUS_SESS_VALID, 3, 3),
+ INIT_REGMAP_IRQ(AXP20X, VBUS_SESS_END, 3, 2),
+ INIT_REGMAP_IRQ(AXP20X, LOW_PWR_LVL1, 3, 1),
+ INIT_REGMAP_IRQ(AXP20X, LOW_PWR_LVL2, 3, 0),
+ INIT_REGMAP_IRQ(AXP20X, TIMER, 4, 7),
+ INIT_REGMAP_IRQ(AXP20X, PEK_RIS_EDGE, 4, 6),
+ INIT_REGMAP_IRQ(AXP20X, PEK_FAL_EDGE, 4, 5),
+ INIT_REGMAP_IRQ(AXP20X, GPIO3_INPUT, 4, 3),
+ INIT_REGMAP_IRQ(AXP20X, GPIO2_INPUT, 4, 2),
+ INIT_REGMAP_IRQ(AXP20X, GPIO1_INPUT, 4, 1),
+ INIT_REGMAP_IRQ(AXP20X, GPIO0_INPUT, 4, 0),
+};
+
+/* some IRQs are compatible with axp20x models */
+static const struct regmap_irq axp288_regmap_irqs[] = {
+ INIT_REGMAP_IRQ(AXP288, VBUS_FALL, 0, 2),
+ INIT_REGMAP_IRQ(AXP288, VBUS_RISE, 0, 3),
+ INIT_REGMAP_IRQ(AXP288, OV, 0, 4),
+
+ INIT_REGMAP_IRQ(AXP288, DONE, 1, 2),
+ INIT_REGMAP_IRQ(AXP288, CHARGING, 1, 3),
+ INIT_REGMAP_IRQ(AXP288, SAFE_QUIT, 1, 4),
+ INIT_REGMAP_IRQ(AXP288, SAFE_ENTER, 1, 5),
+ INIT_REGMAP_IRQ(AXP288, ABSENT, 1, 6),
+ INIT_REGMAP_IRQ(AXP288, APPEND, 1, 7),
+
+ INIT_REGMAP_IRQ(AXP288, QWBTU, 2, 0),
+ INIT_REGMAP_IRQ(AXP288, WBTU, 2, 1),
+ INIT_REGMAP_IRQ(AXP288, QWBTO, 2, 2),
+ INIT_REGMAP_IRQ(AXP288, WBTO, 2, 3),
+ INIT_REGMAP_IRQ(AXP288, QCBTU, 2, 4),
+ INIT_REGMAP_IRQ(AXP288, CBTU, 2, 5),
+ INIT_REGMAP_IRQ(AXP288, QCBTO, 2, 6),
+ INIT_REGMAP_IRQ(AXP288, CBTO, 2, 7),
+
+ INIT_REGMAP_IRQ(AXP288, WL2, 3, 0),
+ INIT_REGMAP_IRQ(AXP288, WL1, 3, 1),
+ INIT_REGMAP_IRQ(AXP288, GPADC, 3, 2),
+ INIT_REGMAP_IRQ(AXP288, OT, 3, 7),
+
+ INIT_REGMAP_IRQ(AXP288, GPIO0, 4, 0),
+ INIT_REGMAP_IRQ(AXP288, GPIO1, 4, 1),
+ INIT_REGMAP_IRQ(AXP288, POKO, 4, 2),
+ INIT_REGMAP_IRQ(AXP288, POKL, 4, 3),
+ INIT_REGMAP_IRQ(AXP288, POKS, 4, 4),
+ INIT_REGMAP_IRQ(AXP288, POKN, 4, 5),
+ INIT_REGMAP_IRQ(AXP288, POKP, 4, 6),
+ INIT_REGMAP_IRQ(AXP288, TIMER, 4, 7),
+
+ INIT_REGMAP_IRQ(AXP288, MV_CHNG, 5, 0),
+ INIT_REGMAP_IRQ(AXP288, BC_USB_CHNG, 5, 1),
};
static const struct of_device_id axp20x_of_match[] = {
};
MODULE_DEVICE_TABLE(i2c, axp20x_i2c_id);
+static const struct acpi_device_id axp20x_acpi_match[] = {
+ {
+ .id = "INT33F4",
+ .driver_data = AXP288_ID,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, axp20x_acpi_match);
+
static const struct regmap_irq_chip axp20x_regmap_irq_chip = {
.name = "axp20x_irq_chip",
.status_base = AXP20X_IRQ1_STATE,
.ack_base = AXP20X_IRQ1_STATE,
.mask_base = AXP20X_IRQ1_EN,
- .num_regs = 5,
+ .mask_invert = true,
+ .init_ack_masked = true,
.irqs = axp20x_regmap_irqs,
.num_irqs = ARRAY_SIZE(axp20x_regmap_irqs),
+ .num_regs = 5,
+
+};
+
+static const struct regmap_irq_chip axp288_regmap_irq_chip = {
+ .name = "axp288_irq_chip",
+ .status_base = AXP20X_IRQ1_STATE,
+ .ack_base = AXP20X_IRQ1_STATE,
+ .mask_base = AXP20X_IRQ1_EN,
.mask_invert = true,
.init_ack_masked = true,
+ .irqs = axp288_regmap_irqs,
+ .num_irqs = ARRAY_SIZE(axp288_regmap_irqs),
+ .num_regs = 6,
+
};
static struct mfd_cell axp20x_cells[] = {
},
};
+static struct resource axp288_adc_resources[] = {
+ {
+ .name = "GPADC",
+ .start = AXP288_IRQ_GPADC,
+ .end = AXP288_IRQ_GPADC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource axp288_charger_resources[] = {
+ {
+ .start = AXP288_IRQ_OV,
+ .end = AXP288_IRQ_OV,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = AXP288_IRQ_DONE,
+ .end = AXP288_IRQ_DONE,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = AXP288_IRQ_CHARGING,
+ .end = AXP288_IRQ_CHARGING,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = AXP288_IRQ_SAFE_QUIT,
+ .end = AXP288_IRQ_SAFE_QUIT,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = AXP288_IRQ_SAFE_ENTER,
+ .end = AXP288_IRQ_SAFE_ENTER,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = AXP288_IRQ_QCBTU,
+ .end = AXP288_IRQ_QCBTU,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = AXP288_IRQ_CBTU,
+ .end = AXP288_IRQ_CBTU,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = AXP288_IRQ_QCBTO,
+ .end = AXP288_IRQ_QCBTO,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .start = AXP288_IRQ_CBTO,
+ .end = AXP288_IRQ_CBTO,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mfd_cell axp288_cells[] = {
+ {
+ .name = "axp288_adc",
+ .num_resources = ARRAY_SIZE(axp288_adc_resources),
+ .resources = axp288_adc_resources,
+ },
+ {
+ .name = "axp288_charger",
+ .num_resources = ARRAY_SIZE(axp288_charger_resources),
+ .resources = axp288_charger_resources,
+ },
+ {
+ .name = "axp288_battery",
+ .num_resources = ARRAY_SIZE(axp288_battery_resources),
+ .resources = axp288_battery_resources,
+ },
+};
+
static struct axp20x_dev *axp20x_pm_power_off;
static void axp20x_power_off(void)
{
+ if (axp20x_pm_power_off->variant == AXP288_ID)
+ return;
+
regmap_write(axp20x_pm_power_off->regmap, AXP20X_OFF_CTRL,
AXP20X_OFF);
}
+static int axp20x_match_device(struct axp20x_dev *axp20x, struct device *dev)
+{
+ const struct acpi_device_id *acpi_id;
+ const struct of_device_id *of_id;
+
+ if (dev->of_node) {
+ of_id = of_match_device(axp20x_of_match, dev);
+ if (!of_id) {
+ dev_err(dev, "Unable to match OF ID\n");
+ return -ENODEV;
+ }
+ axp20x->variant = (long) of_id->data;
+ } else {
+ acpi_id = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (!acpi_id || !acpi_id->driver_data) {
+ dev_err(dev, "Unable to match ACPI ID and data\n");
+ return -ENODEV;
+ }
+ axp20x->variant = (long) acpi_id->driver_data;
+ }
+
+ switch (axp20x->variant) {
+ case AXP202_ID:
+ case AXP209_ID:
+ axp20x->nr_cells = ARRAY_SIZE(axp20x_cells);
+ axp20x->cells = axp20x_cells;
+ axp20x->regmap_cfg = &axp20x_regmap_config;
+ axp20x->regmap_irq_chip = &axp20x_regmap_irq_chip;
+ break;
+ case AXP288_ID:
+ axp20x->cells = axp288_cells;
+ axp20x->nr_cells = ARRAY_SIZE(axp288_cells);
+ axp20x->regmap_cfg = &axp288_regmap_config;
+ axp20x->regmap_irq_chip = &axp288_regmap_irq_chip;
+ break;
+ default:
+ dev_err(dev, "unsupported AXP20X ID %lu\n", axp20x->variant);
+ return -EINVAL;
+ }
+ dev_info(dev, "AXP20x variant %s found\n",
+ axp20x_model_names[axp20x->variant]);
+
+ return 0;
+}
+
static int axp20x_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct axp20x_dev *axp20x;
- const struct of_device_id *of_id;
int ret;
axp20x = devm_kzalloc(&i2c->dev, sizeof(*axp20x), GFP_KERNEL);
if (!axp20x)
return -ENOMEM;
- of_id = of_match_device(axp20x_of_match, &i2c->dev);
- if (!of_id) {
- dev_err(&i2c->dev, "Unable to setup AXP20X data\n");
- return -ENODEV;
- }
- axp20x->variant = (long) of_id->data;
+ ret = axp20x_match_device(axp20x, &i2c->dev);
+ if (ret)
+ return ret;
axp20x->i2c_client = i2c;
axp20x->dev = &i2c->dev;
dev_set_drvdata(axp20x->dev, axp20x);
- axp20x->regmap = devm_regmap_init_i2c(i2c, &axp20x_regmap_config);
+ axp20x->regmap = devm_regmap_init_i2c(i2c, axp20x->regmap_cfg);
if (IS_ERR(axp20x->regmap)) {
ret = PTR_ERR(axp20x->regmap);
dev_err(&i2c->dev, "regmap init failed: %d\n", ret);
ret = regmap_add_irq_chip(axp20x->regmap, i2c->irq,
IRQF_ONESHOT | IRQF_SHARED, -1,
- &axp20x_regmap_irq_chip,
+ axp20x->regmap_irq_chip,
&axp20x->regmap_irqc);
if (ret) {
dev_err(&i2c->dev, "failed to add irq chip: %d\n", ret);
return ret;
}
- ret = mfd_add_devices(axp20x->dev, -1, axp20x_cells,
- ARRAY_SIZE(axp20x_cells), NULL, 0, NULL);
+ ret = mfd_add_devices(axp20x->dev, -1, axp20x->cells,
+ axp20x->nr_cells, NULL, 0, NULL);
if (ret) {
dev_err(&i2c->dev, "failed to add MFD devices: %d\n", ret);
.name = "axp20x",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(axp20x_of_match),
+ .acpi_match_table = ACPI_PTR(axp20x_acpi_match),
},
.probe = axp20x_i2c_probe,
.remove = axp20x_i2c_remove,
da9063->irq_base = pdata->irq_base;
} else {
da9063->flags = 0;
- da9063->irq_base = 0;
+ da9063->irq_base = -1;
}
da9063->chip_irq = irq;
return ret;
}
+ da9063->irq_base = regmap_irq_chip_get_base(da9063->regmap_irq);
+
ret = mfd_add_devices(da9063->dev, -1, da9063_devs,
ARRAY_SIZE(da9063_devs), NULL, da9063->irq_base,
NULL);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcmu");
if (!res) {
dev_err(&pdev->dev, "no prcmu memory region provided\n");
- return -ENOENT;
+ return -EINVAL;
}
prcmu_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!prcmu_base) {
dev_err(&pdev->dev,
"failed to ioremap prcmu register memory\n");
- return -ENOENT;
+ return -ENOMEM;
}
init_prcm_registers();
dbx500_fw_version_init(pdev, pdata->version_offset);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcmu-tcdm");
if (!res) {
dev_err(&pdev->dev, "no prcmu tcdm region provided\n");
- return -ENOENT;
+ return -EINVAL;
}
tcdm_base = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
+ if (!tcdm_base) {
+ dev_err(&pdev->dev,
+ "failed to ioremap prcmu-tcdm register memory\n");
+ return -ENOMEM;
+ }
/* Clean up the mailbox interrupts after pre-kernel code. */
writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR);
irq = platform_get_irq(pdev, 0);
if (irq <= 0) {
dev_err(&pdev->dev, "no prcmu irq provided\n");
- return -ENOENT;
+ return irq;
}
err = request_threaded_irq(irq, prcmu_irq_handler,
prcmu_irq_thread_fn, IRQF_NO_SUSPEND, "prcmu", NULL);
if (err < 0) {
pr_err("prcmu: Failed to allocate IRQ_DB8500_PRCMU1.\n");
- err = -EBUSY;
- goto no_irq_return;
+ return err;
}
db8500_irq_init(np);
if (err) {
mfd_remove_devices(&pdev->dev);
pr_err("prcmu: Failed to add subdevices\n");
- goto no_irq_return;
+ return err;
}
}
if (err) {
mfd_remove_devices(&pdev->dev);
pr_err("prcmu: Failed to add ab8500 subdevice\n");
- goto no_irq_return;
+ return err;
}
pr_info("DB8500 PRCMU initialized\n");
-
-no_irq_return:
return err;
}
static const struct of_device_id db8500_prcmu_match[] = {
--- /dev/null
+/*
+ * Driver for the Diolan DLN-2 USB adapter
+ *
+ * Copyright (c) 2014 Intel Corporation
+ *
+ * Derived from:
+ * i2c-diolan-u2c.c
+ * Copyright (c) 2010-2011 Ericsson AB
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/dln2.h>
+#include <linux/rculist.h>
+
+struct dln2_header {
+ __le16 size;
+ __le16 id;
+ __le16 echo;
+ __le16 handle;
+};
+
+struct dln2_response {
+ struct dln2_header hdr;
+ __le16 result;
+};
+
+#define DLN2_GENERIC_MODULE_ID 0x00
+#define DLN2_GENERIC_CMD(cmd) DLN2_CMD(cmd, DLN2_GENERIC_MODULE_ID)
+#define CMD_GET_DEVICE_VER DLN2_GENERIC_CMD(0x30)
+#define CMD_GET_DEVICE_SN DLN2_GENERIC_CMD(0x31)
+
+#define DLN2_HW_ID 0x200
+#define DLN2_USB_TIMEOUT 200 /* in ms */
+#define DLN2_MAX_RX_SLOTS 16
+#define DLN2_MAX_URBS 16
+#define DLN2_RX_BUF_SIZE 512
+
+enum dln2_handle {
+ DLN2_HANDLE_EVENT = 0, /* don't change, hardware defined */
+ DLN2_HANDLE_CTRL,
+ DLN2_HANDLE_GPIO,
+ DLN2_HANDLE_I2C,
+ DLN2_HANDLE_SPI,
+ DLN2_HANDLES
+};
+
+/*
+ * Receive context used between the receive demultiplexer and the transfer
+ * routine. While sending a request the transfer routine will look for a free
+ * receive context and use it to wait for a response and to receive the URB and
+ * thus the response data.
+ */
+struct dln2_rx_context {
+ /* completion used to wait for a response */
+ struct completion done;
+
+ /* if non-NULL the URB contains the response */
+ struct urb *urb;
+
+ /* if true then this context is used to wait for a response */
+ bool in_use;
+};
+
+/*
+ * Receive contexts for a particular DLN2 module (i2c, gpio, etc.). We use the
+ * handle header field to identify the module in dln2_dev.mod_rx_slots and then
+ * the echo header field to index the slots field and find the receive context
+ * for a particular request.
+ */
+struct dln2_mod_rx_slots {
+ /* RX slots bitmap */
+ DECLARE_BITMAP(bmap, DLN2_MAX_RX_SLOTS);
+
+ /* used to wait for a free RX slot */
+ wait_queue_head_t wq;
+
+ /* used to wait for an RX operation to complete */
+ struct dln2_rx_context slots[DLN2_MAX_RX_SLOTS];
+
+ /* avoid races between alloc/free_rx_slot and dln2_rx_transfer */
+ spinlock_t lock;
+};
+
+struct dln2_dev {
+ struct usb_device *usb_dev;
+ struct usb_interface *interface;
+ u8 ep_in;
+ u8 ep_out;
+
+ struct urb *rx_urb[DLN2_MAX_URBS];
+ void *rx_buf[DLN2_MAX_URBS];
+
+ struct dln2_mod_rx_slots mod_rx_slots[DLN2_HANDLES];
+
+ struct list_head event_cb_list;
+ spinlock_t event_cb_lock;
+
+ bool disconnect;
+ int active_transfers;
+ wait_queue_head_t disconnect_wq;
+ spinlock_t disconnect_lock;
+};
+
+struct dln2_event_cb_entry {
+ struct list_head list;
+ u16 id;
+ struct platform_device *pdev;
+ dln2_event_cb_t callback;
+};
+
+int dln2_register_event_cb(struct platform_device *pdev, u16 id,
+ dln2_event_cb_t event_cb)
+{
+ struct dln2_dev *dln2 = dev_get_drvdata(pdev->dev.parent);
+ struct dln2_event_cb_entry *i, *entry;
+ unsigned long flags;
+ int ret = 0;
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ entry->id = id;
+ entry->callback = event_cb;
+ entry->pdev = pdev;
+
+ spin_lock_irqsave(&dln2->event_cb_lock, flags);
+
+ list_for_each_entry(i, &dln2->event_cb_list, list) {
+ if (i->id == id) {
+ ret = -EBUSY;
+ break;
+ }
+ }
+
+ if (!ret)
+ list_add_rcu(&entry->list, &dln2->event_cb_list);
+
+ spin_unlock_irqrestore(&dln2->event_cb_lock, flags);
+
+ if (ret)
+ kfree(entry);
+
+ return ret;
+}
+EXPORT_SYMBOL(dln2_register_event_cb);
+
+void dln2_unregister_event_cb(struct platform_device *pdev, u16 id)
+{
+ struct dln2_dev *dln2 = dev_get_drvdata(pdev->dev.parent);
+ struct dln2_event_cb_entry *i;
+ unsigned long flags;
+ bool found = false;
+
+ spin_lock_irqsave(&dln2->event_cb_lock, flags);
+
+ list_for_each_entry(i, &dln2->event_cb_list, list) {
+ if (i->id == id) {
+ list_del_rcu(&i->list);
+ found = true;
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&dln2->event_cb_lock, flags);
+
+ if (found) {
+ synchronize_rcu();
+ kfree(i);
+ }
+}
+EXPORT_SYMBOL(dln2_unregister_event_cb);
+
+/*
+ * Returns true if a valid transfer slot is found. In this case the URB must not
+ * be resubmitted immediately in dln2_rx as we need the data when dln2_transfer
+ * is woke up. It will be resubmitted there.
+ */
+static bool dln2_transfer_complete(struct dln2_dev *dln2, struct urb *urb,
+ u16 handle, u16 rx_slot)
+{
+ struct device *dev = &dln2->interface->dev;
+ struct dln2_mod_rx_slots *rxs = &dln2->mod_rx_slots[handle];
+ struct dln2_rx_context *rxc;
+ bool valid_slot = false;
+
+ if (rx_slot >= DLN2_MAX_RX_SLOTS)
+ goto out;
+
+ rxc = &rxs->slots[rx_slot];
+
+ /*
+ * No need to disable interrupts as this lock is not taken in interrupt
+ * context elsewhere in this driver. This function (or its callers) are
+ * also not exported to other modules.
+ */
+ spin_lock(&rxs->lock);
+ if (rxc->in_use && !rxc->urb) {
+ rxc->urb = urb;
+ complete(&rxc->done);
+ valid_slot = true;
+ }
+ spin_unlock(&rxs->lock);
+
+out:
+ if (!valid_slot)
+ dev_warn(dev, "bad/late response %d/%d\n", handle, rx_slot);
+
+ return valid_slot;
+}
+
+static void dln2_run_event_callbacks(struct dln2_dev *dln2, u16 id, u16 echo,
+ void *data, int len)
+{
+ struct dln2_event_cb_entry *i;
+
+ rcu_read_lock();
+
+ list_for_each_entry_rcu(i, &dln2->event_cb_list, list) {
+ if (i->id == id) {
+ i->callback(i->pdev, echo, data, len);
+ break;
+ }
+ }
+
+ rcu_read_unlock();
+}
+
+static void dln2_rx(struct urb *urb)
+{
+ struct dln2_dev *dln2 = urb->context;
+ struct dln2_header *hdr = urb->transfer_buffer;
+ struct device *dev = &dln2->interface->dev;
+ u16 id, echo, handle, size;
+ u8 *data;
+ int len;
+ int err;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ case -EPIPE:
+ /* this urb is terminated, clean up */
+ dev_dbg(dev, "urb shutting down with status %d\n", urb->status);
+ return;
+ default:
+ dev_dbg(dev, "nonzero urb status received %d\n", urb->status);
+ goto out;
+ }
+
+ if (urb->actual_length < sizeof(struct dln2_header)) {
+ dev_err(dev, "short response: %d\n", urb->actual_length);
+ goto out;
+ }
+
+ handle = le16_to_cpu(hdr->handle);
+ id = le16_to_cpu(hdr->id);
+ echo = le16_to_cpu(hdr->echo);
+ size = le16_to_cpu(hdr->size);
+
+ if (size != urb->actual_length) {
+ dev_err(dev, "size mismatch: handle %x cmd %x echo %x size %d actual %d\n",
+ handle, id, echo, size, urb->actual_length);
+ goto out;
+ }
+
+ if (handle >= DLN2_HANDLES) {
+ dev_warn(dev, "invalid handle %d\n", handle);
+ goto out;
+ }
+
+ data = urb->transfer_buffer + sizeof(struct dln2_header);
+ len = urb->actual_length - sizeof(struct dln2_header);
+
+ if (handle == DLN2_HANDLE_EVENT) {
+ dln2_run_event_callbacks(dln2, id, echo, data, len);
+ } else {
+ /* URB will be re-submitted in _dln2_transfer (free_rx_slot) */
+ if (dln2_transfer_complete(dln2, urb, handle, echo))
+ return;
+ }
+
+out:
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0)
+ dev_err(dev, "failed to resubmit RX URB: %d\n", err);
+}
+
+static void *dln2_prep_buf(u16 handle, u16 cmd, u16 echo, const void *obuf,
+ int *obuf_len, gfp_t gfp)
+{
+ int len;
+ void *buf;
+ struct dln2_header *hdr;
+
+ len = *obuf_len + sizeof(*hdr);
+ buf = kmalloc(len, gfp);
+ if (!buf)
+ return NULL;
+
+ hdr = (struct dln2_header *)buf;
+ hdr->id = cpu_to_le16(cmd);
+ hdr->size = cpu_to_le16(len);
+ hdr->echo = cpu_to_le16(echo);
+ hdr->handle = cpu_to_le16(handle);
+
+ memcpy(buf + sizeof(*hdr), obuf, *obuf_len);
+
+ *obuf_len = len;
+
+ return buf;
+}
+
+static int dln2_send_wait(struct dln2_dev *dln2, u16 handle, u16 cmd, u16 echo,
+ const void *obuf, int obuf_len)
+{
+ int ret = 0;
+ int len = obuf_len;
+ void *buf;
+ int actual;
+
+ buf = dln2_prep_buf(handle, cmd, echo, obuf, &len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = usb_bulk_msg(dln2->usb_dev,
+ usb_sndbulkpipe(dln2->usb_dev, dln2->ep_out),
+ buf, len, &actual, DLN2_USB_TIMEOUT);
+
+ kfree(buf);
+
+ return ret;
+}
+
+static bool find_free_slot(struct dln2_dev *dln2, u16 handle, int *slot)
+{
+ struct dln2_mod_rx_slots *rxs;
+ unsigned long flags;
+
+ if (dln2->disconnect) {
+ *slot = -ENODEV;
+ return true;
+ }
+
+ rxs = &dln2->mod_rx_slots[handle];
+
+ spin_lock_irqsave(&rxs->lock, flags);
+
+ *slot = find_first_zero_bit(rxs->bmap, DLN2_MAX_RX_SLOTS);
+
+ if (*slot < DLN2_MAX_RX_SLOTS) {
+ struct dln2_rx_context *rxc = &rxs->slots[*slot];
+
+ set_bit(*slot, rxs->bmap);
+ rxc->in_use = true;
+ }
+
+ spin_unlock_irqrestore(&rxs->lock, flags);
+
+ return *slot < DLN2_MAX_RX_SLOTS;
+}
+
+static int alloc_rx_slot(struct dln2_dev *dln2, u16 handle)
+{
+ int ret;
+ int slot;
+
+ /*
+ * No need to timeout here, the wait is bounded by the timeout in
+ * _dln2_transfer.
+ */
+ ret = wait_event_interruptible(dln2->mod_rx_slots[handle].wq,
+ find_free_slot(dln2, handle, &slot));
+ if (ret < 0)
+ return ret;
+
+ return slot;
+}
+
+static void free_rx_slot(struct dln2_dev *dln2, u16 handle, int slot)
+{
+ struct dln2_mod_rx_slots *rxs;
+ struct urb *urb = NULL;
+ unsigned long flags;
+ struct dln2_rx_context *rxc;
+
+ rxs = &dln2->mod_rx_slots[handle];
+
+ spin_lock_irqsave(&rxs->lock, flags);
+
+ clear_bit(slot, rxs->bmap);
+
+ rxc = &rxs->slots[slot];
+ rxc->in_use = false;
+ urb = rxc->urb;
+ rxc->urb = NULL;
+ reinit_completion(&rxc->done);
+
+ spin_unlock_irqrestore(&rxs->lock, flags);
+
+ if (urb) {
+ int err;
+ struct device *dev = &dln2->interface->dev;
+
+ err = usb_submit_urb(urb, GFP_KERNEL);
+ if (err < 0)
+ dev_err(dev, "failed to resubmit RX URB: %d\n", err);
+ }
+
+ wake_up_interruptible(&rxs->wq);
+}
+
+static int _dln2_transfer(struct dln2_dev *dln2, u16 handle, u16 cmd,
+ const void *obuf, unsigned obuf_len,
+ void *ibuf, unsigned *ibuf_len)
+{
+ int ret = 0;
+ int rx_slot;
+ struct dln2_response *rsp;
+ struct dln2_rx_context *rxc;
+ struct device *dev = &dln2->interface->dev;
+ const unsigned long timeout = DLN2_USB_TIMEOUT * HZ / 1000;
+ struct dln2_mod_rx_slots *rxs = &dln2->mod_rx_slots[handle];
+ int size;
+
+ spin_lock(&dln2->disconnect_lock);
+ if (!dln2->disconnect)
+ dln2->active_transfers++;
+ else
+ ret = -ENODEV;
+ spin_unlock(&dln2->disconnect_lock);
+
+ if (ret)
+ return ret;
+
+ rx_slot = alloc_rx_slot(dln2, handle);
+ if (rx_slot < 0) {
+ ret = rx_slot;
+ goto out_decr;
+ }
+
+ ret = dln2_send_wait(dln2, handle, cmd, rx_slot, obuf, obuf_len);
+ if (ret < 0) {
+ dev_err(dev, "USB write failed: %d\n", ret);
+ goto out_free_rx_slot;
+ }
+
+ rxc = &rxs->slots[rx_slot];
+
+ ret = wait_for_completion_interruptible_timeout(&rxc->done, timeout);
+ if (ret <= 0) {
+ if (!ret)
+ ret = -ETIMEDOUT;
+ goto out_free_rx_slot;
+ } else {
+ ret = 0;
+ }
+
+ if (dln2->disconnect) {
+ ret = -ENODEV;
+ goto out_free_rx_slot;
+ }
+
+ /* if we got here we know that the response header has been checked */
+ rsp = rxc->urb->transfer_buffer;
+ size = le16_to_cpu(rsp->hdr.size);
+
+ if (size < sizeof(*rsp)) {
+ ret = -EPROTO;
+ goto out_free_rx_slot;
+ }
+
+ if (le16_to_cpu(rsp->result) > 0x80) {
+ dev_dbg(dev, "%d received response with error %d\n",
+ handle, le16_to_cpu(rsp->result));
+ ret = -EREMOTEIO;
+ goto out_free_rx_slot;
+ }
+
+ if (!ibuf)
+ goto out_free_rx_slot;
+
+ if (*ibuf_len > size - sizeof(*rsp))
+ *ibuf_len = size - sizeof(*rsp);
+
+ memcpy(ibuf, rsp + 1, *ibuf_len);
+
+out_free_rx_slot:
+ free_rx_slot(dln2, handle, rx_slot);
+out_decr:
+ spin_lock(&dln2->disconnect_lock);
+ dln2->active_transfers--;
+ spin_unlock(&dln2->disconnect_lock);
+ if (dln2->disconnect)
+ wake_up(&dln2->disconnect_wq);
+
+ return ret;
+}
+
+int dln2_transfer(struct platform_device *pdev, u16 cmd,
+ const void *obuf, unsigned obuf_len,
+ void *ibuf, unsigned *ibuf_len)
+{
+ struct dln2_platform_data *dln2_pdata;
+ struct dln2_dev *dln2;
+ u16 handle;
+
+ dln2 = dev_get_drvdata(pdev->dev.parent);
+ dln2_pdata = dev_get_platdata(&pdev->dev);
+ handle = dln2_pdata->handle;
+
+ return _dln2_transfer(dln2, handle, cmd, obuf, obuf_len, ibuf,
+ ibuf_len);
+}
+EXPORT_SYMBOL(dln2_transfer);
+
+static int dln2_check_hw(struct dln2_dev *dln2)
+{
+ int ret;
+ __le32 hw_type;
+ int len = sizeof(hw_type);
+
+ ret = _dln2_transfer(dln2, DLN2_HANDLE_CTRL, CMD_GET_DEVICE_VER,
+ NULL, 0, &hw_type, &len);
+ if (ret < 0)
+ return ret;
+ if (len < sizeof(hw_type))
+ return -EREMOTEIO;
+
+ if (le32_to_cpu(hw_type) != DLN2_HW_ID) {
+ dev_err(&dln2->interface->dev, "Device ID 0x%x not supported\n",
+ le32_to_cpu(hw_type));
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int dln2_print_serialno(struct dln2_dev *dln2)
+{
+ int ret;
+ __le32 serial_no;
+ int len = sizeof(serial_no);
+ struct device *dev = &dln2->interface->dev;
+
+ ret = _dln2_transfer(dln2, DLN2_HANDLE_CTRL, CMD_GET_DEVICE_SN, NULL, 0,
+ &serial_no, &len);
+ if (ret < 0)
+ return ret;
+ if (len < sizeof(serial_no))
+ return -EREMOTEIO;
+
+ dev_info(dev, "Diolan DLN2 serial %u\n", le32_to_cpu(serial_no));
+
+ return 0;
+}
+
+static int dln2_hw_init(struct dln2_dev *dln2)
+{
+ int ret;
+
+ ret = dln2_check_hw(dln2);
+ if (ret < 0)
+ return ret;
+
+ return dln2_print_serialno(dln2);
+}
+
+static void dln2_free_rx_urbs(struct dln2_dev *dln2)
+{
+ int i;
+
+ for (i = 0; i < DLN2_MAX_URBS; i++) {
+ usb_kill_urb(dln2->rx_urb[i]);
+ usb_free_urb(dln2->rx_urb[i]);
+ kfree(dln2->rx_buf[i]);
+ }
+}
+
+static void dln2_free(struct dln2_dev *dln2)
+{
+ dln2_free_rx_urbs(dln2);
+ usb_put_dev(dln2->usb_dev);
+ kfree(dln2);
+}
+
+static int dln2_setup_rx_urbs(struct dln2_dev *dln2,
+ struct usb_host_interface *hostif)
+{
+ int i;
+ int ret;
+ const int rx_max_size = DLN2_RX_BUF_SIZE;
+ struct device *dev = &dln2->interface->dev;
+
+ for (i = 0; i < DLN2_MAX_URBS; i++) {
+ dln2->rx_buf[i] = kmalloc(rx_max_size, GFP_KERNEL);
+ if (!dln2->rx_buf[i])
+ return -ENOMEM;
+
+ dln2->rx_urb[i] = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dln2->rx_urb[i])
+ return -ENOMEM;
+
+ usb_fill_bulk_urb(dln2->rx_urb[i], dln2->usb_dev,
+ usb_rcvbulkpipe(dln2->usb_dev, dln2->ep_in),
+ dln2->rx_buf[i], rx_max_size, dln2_rx, dln2);
+
+ ret = usb_submit_urb(dln2->rx_urb[i], GFP_KERNEL);
+ if (ret < 0) {
+ dev_err(dev, "failed to submit RX URB: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static struct dln2_platform_data dln2_pdata_gpio = {
+ .handle = DLN2_HANDLE_GPIO,
+};
+
+/* Only one I2C port seems to be supported on current hardware */
+static struct dln2_platform_data dln2_pdata_i2c = {
+ .handle = DLN2_HANDLE_I2C,
+ .port = 0,
+};
+
+/* Only one SPI port supported */
+static struct dln2_platform_data dln2_pdata_spi = {
+ .handle = DLN2_HANDLE_SPI,
+ .port = 0,
+};
+
+static const struct mfd_cell dln2_devs[] = {
+ {
+ .name = "dln2-gpio",
+ .platform_data = &dln2_pdata_gpio,
+ .pdata_size = sizeof(struct dln2_platform_data),
+ },
+ {
+ .name = "dln2-i2c",
+ .platform_data = &dln2_pdata_i2c,
+ .pdata_size = sizeof(struct dln2_platform_data),
+ },
+ {
+ .name = "dln2-spi",
+ .platform_data = &dln2_pdata_spi,
+ .pdata_size = sizeof(struct dln2_platform_data),
+ },
+};
+
+static void dln2_disconnect(struct usb_interface *interface)
+{
+ struct dln2_dev *dln2 = usb_get_intfdata(interface);
+ int i, j;
+
+ /* don't allow starting new transfers */
+ spin_lock(&dln2->disconnect_lock);
+ dln2->disconnect = true;
+ spin_unlock(&dln2->disconnect_lock);
+
+ /* cancel in progress transfers */
+ for (i = 0; i < DLN2_HANDLES; i++) {
+ struct dln2_mod_rx_slots *rxs = &dln2->mod_rx_slots[i];
+ unsigned long flags;
+
+ spin_lock_irqsave(&rxs->lock, flags);
+
+ /* cancel all response waiters */
+ for (j = 0; j < DLN2_MAX_RX_SLOTS; j++) {
+ struct dln2_rx_context *rxc = &rxs->slots[j];
+
+ if (rxc->in_use)
+ complete(&rxc->done);
+ }
+
+ spin_unlock_irqrestore(&rxs->lock, flags);
+ }
+
+ /* wait for transfers to end */
+ wait_event(dln2->disconnect_wq, !dln2->active_transfers);
+
+ mfd_remove_devices(&interface->dev);
+
+ dln2_free(dln2);
+}
+
+static int dln2_probe(struct usb_interface *interface,
+ const struct usb_device_id *usb_id)
+{
+ struct usb_host_interface *hostif = interface->cur_altsetting;
+ struct device *dev = &interface->dev;
+ struct dln2_dev *dln2;
+ int ret;
+ int i, j;
+
+ if (hostif->desc.bInterfaceNumber != 0 ||
+ hostif->desc.bNumEndpoints < 2)
+ return -ENODEV;
+
+ dln2 = kzalloc(sizeof(*dln2), GFP_KERNEL);
+ if (!dln2)
+ return -ENOMEM;
+
+ dln2->ep_out = hostif->endpoint[0].desc.bEndpointAddress;
+ dln2->ep_in = hostif->endpoint[1].desc.bEndpointAddress;
+ dln2->usb_dev = usb_get_dev(interface_to_usbdev(interface));
+ dln2->interface = interface;
+ usb_set_intfdata(interface, dln2);
+ init_waitqueue_head(&dln2->disconnect_wq);
+
+ for (i = 0; i < DLN2_HANDLES; i++) {
+ init_waitqueue_head(&dln2->mod_rx_slots[i].wq);
+ spin_lock_init(&dln2->mod_rx_slots[i].lock);
+ for (j = 0; j < DLN2_MAX_RX_SLOTS; j++)
+ init_completion(&dln2->mod_rx_slots[i].slots[j].done);
+ }
+
+ spin_lock_init(&dln2->event_cb_lock);
+ spin_lock_init(&dln2->disconnect_lock);
+ INIT_LIST_HEAD(&dln2->event_cb_list);
+
+ ret = dln2_setup_rx_urbs(dln2, hostif);
+ if (ret)
+ goto out_cleanup;
+
+ ret = dln2_hw_init(dln2);
+ if (ret < 0) {
+ dev_err(dev, "failed to initialize hardware\n");
+ goto out_cleanup;
+ }
+
+ ret = mfd_add_hotplug_devices(dev, dln2_devs, ARRAY_SIZE(dln2_devs));
+ if (ret != 0) {
+ dev_err(dev, "failed to add mfd devices to core\n");
+ goto out_cleanup;
+ }
+
+ return 0;
+
+out_cleanup:
+ dln2_free(dln2);
+
+ return ret;
+}
+
+static const struct usb_device_id dln2_table[] = {
+ { USB_DEVICE(0xa257, 0x2013) },
+ { }
+};
+
+MODULE_DEVICE_TABLE(usb, dln2_table);
+
+static struct usb_driver dln2_driver = {
+ .name = "dln2",
+ .probe = dln2_probe,
+ .disconnect = dln2_disconnect,
+ .id_table = dln2_table,
+};
+
+module_usb_driver(dln2_driver);
+
+MODULE_AUTHOR("Octavian Purdila <octavian.purdila@intel.com>");
+MODULE_DESCRIPTION("Core driver for the Diolan DLN2 interface adapter");
+MODULE_LICENSE("GPL v2");
return -ENODEV;
}
- ret = mfd_add_devices(&dev->dev, 0, lpc_sch_cells, cells, NULL, 0, NULL);
- if (ret)
- mfd_remove_devices(&dev->dev);
-
- return ret;
+ return mfd_add_devices(&dev->dev, 0, lpc_sch_cells, cells, NULL, 0, NULL);
}
static void lpc_sch_remove(struct pci_dev *dev)
/*
* max14577.c - mfd core driver for the Maxim 14577/77836
*
- * Copyright (C) 2014 Samsung Electrnoics
+ * Copyright (C) 2014 Samsung Electronics
* Chanwoo Choi <cw00.choi@samsung.com>
* Krzysztof Kozlowski <k.kozlowski@samsung.com>
*
static const struct mfd_cell max77693_devs[] = {
{ .name = "max77693-pmic", },
- { .name = "max77693-charger", },
+ {
+ .name = "max77693-charger",
+ .of_compatible = "maxim,max77693-charger",
+ },
{ .name = "max77693-muic", },
- { .name = "max77693-haptic", },
+ {
+ .name = "max77693-haptic",
+ .of_compatible = "maxim,max77693-haptic",
+ },
{
.name = "max77693-flash",
.of_compatible = "maxim,max77693-flash",
.num_irqs = ARRAY_SIZE(max77693_muic_irqs),
};
+static const struct regmap_config max77693_regmap_haptic_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX77693_HAPTIC_REG_END,
+};
+
static int max77693_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
}
i2c_set_clientdata(max77693->haptic, max77693);
+ max77693->regmap_haptic = devm_regmap_init_i2c(max77693->haptic,
+ &max77693_regmap_haptic_config);
+ if (IS_ERR(max77693->regmap_haptic)) {
+ ret = PTR_ERR(max77693->regmap_haptic);
+ dev_err(max77693->dev,
+ "failed to initialize haptic register map: %d\n", ret);
+ goto err_regmap;
+ }
+
/*
* Initialize register map for MUIC device because use regmap-muic
* instance of MUIC device when irq of max77693 is initialized
ret = PTR_ERR(max77693->regmap_muic);
dev_err(max77693->dev,
"failed to allocate register map: %d\n", ret);
- goto err_regmap_muic;
+ goto err_regmap;
}
ret = regmap_add_irq_chip(max77693->regmap, max77693->irq,
&max77693->irq_data_led);
if (ret) {
dev_err(max77693->dev, "failed to add irq chip: %d\n", ret);
- goto err_regmap_muic;
+ goto err_regmap;
}
ret = regmap_add_irq_chip(max77693->regmap, max77693->irq,
goto err_irq_charger;
}
- ret = regmap_add_irq_chip(max77693->regmap, max77693->irq,
+ ret = regmap_add_irq_chip(max77693->regmap_muic, max77693->irq,
IRQF_ONESHOT | IRQF_SHARED |
IRQF_TRIGGER_FALLING, 0,
&max77693_muic_irq_chip,
goto err_irq_muic;
}
+ /* Unmask interrupts from all blocks in interrupt source register */
+ ret = regmap_update_bits(max77693->regmap,
+ MAX77693_PMIC_REG_INTSRC_MASK,
+ SRC_IRQ_ALL, (unsigned int)~SRC_IRQ_ALL);
+ if (ret < 0) {
+ dev_err(max77693->dev,
+ "Could not unmask interrupts in INTSRC: %d\n",
+ ret);
+ goto err_intsrc;
+ }
+
pm_runtime_set_active(max77693->dev);
ret = mfd_add_devices(max77693->dev, -1, max77693_devs,
err_mfd:
mfd_remove_devices(max77693->dev);
+err_intsrc:
regmap_del_irq_chip(max77693->irq, max77693->irq_data_muic);
err_irq_muic:
regmap_del_irq_chip(max77693->irq, max77693->irq_data_charger);
regmap_del_irq_chip(max77693->irq, max77693->irq_data_topsys);
err_irq_topsys:
regmap_del_irq_chip(max77693->irq, max77693->irq_data_led);
-err_regmap_muic:
+err_regmap:
i2c_unregister_device(max77693->haptic);
err_i2c_haptic:
i2c_unregister_device(max77693->muic);
struct platform_device *pdev;
struct device_node *np = NULL;
int ret = -ENOMEM;
+ int platform_id;
int r;
- pdev = platform_device_alloc(cell->name, id + cell->id);
+ if (id < 0)
+ platform_id = id;
+ else
+ platform_id = id + cell->id;
+
+ pdev = platform_device_alloc(cell->name, platform_id);
if (!pdev)
goto fail_alloc;
static int rts5227_optimize_phy(struct rtsx_pcr *pcr)
{
+ int err;
+
+ err = rtsx_gops_pm_reset(pcr);
+ if (err < 0)
+ return err;
+
/* Optimize RX sensitivity */
return rtsx_pci_write_phy_register(pcr, 0x00, 0xBA42);
}
{
int err;
+ err = rtsx_gops_pm_reset(pcr);
+ if (err < 0)
+ return err;
+
err = rtsx_pci_write_phy_register(pcr, PHY_REG_REV,
PHY_REG_REV_RESV | PHY_REG_REV_RXIDLE_LATCHED |
PHY_REG_REV_P1_EN | PHY_REG_REV_RXIDLE_EN |
--- /dev/null
+/* Driver for Realtek PCI-Express card reader
+ *
+ * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Micky Ching <micky_ching@realsil.com.cn>
+ */
+
+#include <linux/mfd/rtsx_pci.h>
+#include "rtsx_pcr.h"
+
+int rtsx_gops_pm_reset(struct rtsx_pcr *pcr)
+{
+ int err;
+
+ /* init aspm */
+ rtsx_pci_write_register(pcr, ASPM_FORCE_CTL, 0xFF, 0x00);
+ err = rtsx_pci_update_cfg_byte(pcr, LCTLR, ~LCTLR_ASPM_CTL_MASK, 0x00);
+ if (err < 0)
+ return err;
+
+ /* reset PM_CTRL3 before send buffer cmd */
+ return rtsx_pci_write_register(pcr, PM_CTRL3, D3_DELINK_MODE_EN, 0x00);
+}
mutex_unlock(&pcr->pcr_mutex);
}
+#ifdef CONFIG_PM
static void rtsx_pci_power_off(struct rtsx_pcr *pcr, u8 pm_state)
{
if (pcr->ops->turn_off_led)
if (pcr->ops->force_power_down)
pcr->ops->force_power_down(pcr, pm_state);
}
+#endif
static int rtsx_pci_init_hw(struct rtsx_pcr *pcr)
{
pcr->ms_pull_ctl_disable_tbl = __device##_ms_pull_ctl_disable_tbl; \
} while (0)
+/* generic operations */
+int rtsx_gops_pm_reset(struct rtsx_pcr *pcr);
+
#endif
/* initialize USB SG transfer timer */
setup_timer(&ucr->sg_timer, rtsx_usb_sg_timed_out, (unsigned long) ucr);
- ret = mfd_add_devices(&intf->dev, usb_dev->devnum, rtsx_usb_cells,
- ARRAY_SIZE(rtsx_usb_cells), NULL, 0, NULL);
+ ret = mfd_add_hotplug_devices(&intf->dev, rtsx_usb_cells,
+ ARRAY_SIZE(rtsx_usb_cells));
if (ret)
goto out_init_fail;
#include <linux/mfd/samsung/irq.h>
#include <linux/mfd/samsung/s2mpa01.h>
#include <linux/mfd/samsung/s2mps11.h>
+#include <linux/mfd/samsung/s2mps13.h>
#include <linux/mfd/samsung/s2mps14.h>
#include <linux/mfd/samsung/s2mpu02.h>
#include <linux/mfd/samsung/s5m8763.h>
#include <linux/mfd/samsung/s5m8767.h>
-#include <linux/regulator/machine.h>
#include <linux/regmap.h>
static const struct mfd_cell s5m8751_devs[] = {
}
};
+static const struct mfd_cell s2mps13_devs[] = {
+ { .name = "s2mps13-pmic", },
+ { .name = "s2mps13-rtc", },
+ {
+ .name = "s2mps13-clk",
+ .of_compatible = "samsung,s2mps13-clk",
+ },
+};
+
static const struct mfd_cell s2mps14_devs[] = {
{
.name = "s2mps14-pmic",
}, {
.compatible = "samsung,s2mps11-pmic",
.data = (void *)S2MPS11X,
+ }, {
+ .compatible = "samsung,s2mps13-pmic",
+ .data = (void *)S2MPS13X,
}, {
.compatible = "samsung,s2mps14-pmic",
.data = (void *)S2MPS14X,
.cache_type = REGCACHE_FLAT,
};
+static const struct regmap_config s2mps13_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = S2MPS13_REG_LDODSCH5,
+ .volatile_reg = s2mps11_volatile,
+ .cache_type = REGCACHE_FLAT,
+};
+
static const struct regmap_config s2mps14_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
case S2MPS11X:
regmap = &s2mps11_regmap_config;
break;
+ case S2MPS13X:
+ regmap = &s2mps13_regmap_config;
+ break;
case S2MPS14X:
regmap = &s2mps14_regmap_config;
break;
sec_devs = s2mps11_devs;
num_sec_devs = ARRAY_SIZE(s2mps11_devs);
break;
+ case S2MPS13X:
+ sec_devs = s2mps13_devs;
+ num_sec_devs = ARRAY_SIZE(s2mps13_devs);
+ break;
case S2MPS14X:
sec_devs = s2mps14_devs;
num_sec_devs = ARRAY_SIZE(s2mps14_devs);
*/
disable_irq(sec_pmic->irq);
- switch (sec_pmic->device_type) {
- case S2MPS14X:
- case S2MPU02:
- regulator_suspend_prepare(PM_SUSPEND_MEM);
- break;
- default:
- break;
- }
-
return 0;
}
.ack_base = S2MPS11_REG_INT1,
};
+#define S2MPS1X_IRQ_CHIP_COMMON_DATA \
+ .irqs = s2mps14_irqs, \
+ .num_irqs = ARRAY_SIZE(s2mps14_irqs), \
+ .num_regs = 3, \
+ .status_base = S2MPS14_REG_INT1, \
+ .mask_base = S2MPS14_REG_INT1M, \
+ .ack_base = S2MPS14_REG_INT1 \
+
+static const struct regmap_irq_chip s2mps13_irq_chip = {
+ .name = "s2mps13",
+ S2MPS1X_IRQ_CHIP_COMMON_DATA,
+};
+
static const struct regmap_irq_chip s2mps14_irq_chip = {
.name = "s2mps14",
- .irqs = s2mps14_irqs,
- .num_irqs = ARRAY_SIZE(s2mps14_irqs),
- .num_regs = 3,
- .status_base = S2MPS14_REG_INT1,
- .mask_base = S2MPS14_REG_INT1M,
- .ack_base = S2MPS14_REG_INT1,
+ S2MPS1X_IRQ_CHIP_COMMON_DATA,
};
static const struct regmap_irq_chip s2mpu02_irq_chip = {
case S2MPS11X:
sec_irq_chip = &s2mps11_irq_chip;
break;
+ case S2MPS13X:
+ sec_irq_chip = &s2mps13_irq_chip;
+ break;
case S2MPS14X:
sec_irq_chip = &s2mps14_irq_chip;
break;
#define STMPE24XX_REG_CHIP_ID 0x80
#define STMPE24XX_REG_IEGPIOR_LSB 0x18
#define STMPE24XX_REG_ISGPIOR_MSB 0x19
-#define STMPE24XX_REG_GPMR_LSB 0xA5
+#define STMPE24XX_REG_GPMR_LSB 0xA4
#define STMPE24XX_REG_GPSR_LSB 0x85
#define STMPE24XX_REG_GPCR_LSB 0x88
#define STMPE24XX_REG_GPDR_LSB 0x8B
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/list.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
+#include <linux/slab.h>
static struct platform_driver syscon_driver;
+static DEFINE_SPINLOCK(syscon_list_slock);
+static LIST_HEAD(syscon_list);
+
struct syscon {
+ struct device_node *np;
struct regmap *regmap;
+ struct list_head list;
+};
+
+static struct regmap_config syscon_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
};
-static int syscon_match_node(struct device *dev, void *data)
+static struct syscon *of_syscon_register(struct device_node *np)
{
- struct device_node *dn = data;
+ struct syscon *syscon;
+ struct regmap *regmap;
+ void __iomem *base;
+ int ret;
+ struct regmap_config syscon_config = syscon_regmap_config;
+
+ if (!of_device_is_compatible(np, "syscon"))
+ return ERR_PTR(-EINVAL);
+
+ syscon = kzalloc(sizeof(*syscon), GFP_KERNEL);
+ if (!syscon)
+ return ERR_PTR(-ENOMEM);
+
+ base = of_iomap(np, 0);
+ if (!base) {
+ ret = -ENOMEM;
+ goto err_map;
+ }
+
+ /* Parse the device's DT node for an endianness specification */
+ if (of_property_read_bool(np, "big-endian"))
+ syscon_config.val_format_endian = REGMAP_ENDIAN_BIG;
+ else if (of_property_read_bool(np, "little-endian"))
+ syscon_config.val_format_endian = REGMAP_ENDIAN_LITTLE;
+
+ regmap = regmap_init_mmio(NULL, base, &syscon_config);
+ if (IS_ERR(regmap)) {
+ pr_err("regmap init failed\n");
+ ret = PTR_ERR(regmap);
+ goto err_regmap;
+ }
+
+ syscon->regmap = regmap;
+ syscon->np = np;
+
+ spin_lock(&syscon_list_slock);
+ list_add_tail(&syscon->list, &syscon_list);
+ spin_unlock(&syscon_list_slock);
- return (dev->of_node == dn) ? 1 : 0;
+ return syscon;
+
+err_regmap:
+ iounmap(base);
+err_map:
+ kfree(syscon);
+ return ERR_PTR(ret);
}
struct regmap *syscon_node_to_regmap(struct device_node *np)
{
- struct syscon *syscon;
- struct device *dev;
+ struct syscon *entry, *syscon = NULL;
- dev = driver_find_device(&syscon_driver.driver, NULL, np,
- syscon_match_node);
- if (!dev)
- return ERR_PTR(-EPROBE_DEFER);
+ spin_lock(&syscon_list_slock);
- syscon = dev_get_drvdata(dev);
+ list_for_each_entry(entry, &syscon_list, list)
+ if (entry->np == np) {
+ syscon = entry;
+ break;
+ }
+
+ spin_unlock(&syscon_list_slock);
+
+ if (!syscon)
+ syscon = of_syscon_register(np);
+
+ if (IS_ERR(syscon))
+ return ERR_CAST(syscon);
return syscon->regmap;
}
}
EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle);
-static const struct of_device_id of_syscon_match[] = {
- { .compatible = "syscon", },
- { },
-};
-
-static struct regmap_config syscon_regmap_config = {
- .reg_bits = 32,
- .val_bits = 32,
- .reg_stride = 4,
-};
-
static int syscon_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
.driver = {
.name = "syscon",
.owner = THIS_MODULE,
- .of_match_table = of_syscon_match,
},
.probe = syscon_probe,
.id_table = syscon_ids,
unsigned long flags;
u8 dev_ctl;
- clk_enable(t7l66xb->clk32k);
+ clk_prepare_enable(t7l66xb->clk32k);
spin_lock_irqsave(&t7l66xb->lock, flags);
spin_unlock_irqrestore(&t7l66xb->lock, flags);
- clk_disable(t7l66xb->clk32k);
+ clk_disable_unprepare(t7l66xb->clk32k);
return 0;
}
if (pdata && pdata->suspend)
pdata->suspend(dev);
- clk_disable(t7l66xb->clk48m);
+ clk_disable_unprepare(t7l66xb->clk48m);
return 0;
}
struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
struct t7l66xb_platform_data *pdata = dev_get_platdata(&dev->dev);
- clk_enable(t7l66xb->clk48m);
+ clk_prepare_enable(t7l66xb->clk48m);
if (pdata && pdata->resume)
pdata->resume(dev);
goto err_ioremap;
}
- clk_enable(t7l66xb->clk48m);
+ clk_prepare_enable(t7l66xb->clk48m);
if (pdata && pdata->enable)
pdata->enable(dev);
int ret;
ret = pdata->disable(dev);
- clk_disable(t7l66xb->clk48m);
+ clk_disable_unprepare(t7l66xb->clk48m);
clk_put(t7l66xb->clk48m);
- clk_disable(t7l66xb->clk32k);
+ clk_disable_unprepare(t7l66xb->clk32k);
clk_put(t7l66xb->clk32k);
t7l66xb_detach_irq(dev);
iounmap(t7l66xb->scr);
static int tc3589x_irq_init(struct tc3589x *tc3589x, struct device_node *np)
{
- int base = tc3589x->irq_base;
-
tc3589x->domain = irq_domain_add_simple(
- np, TC3589x_NR_INTERNAL_IRQS, base,
+ np, TC3589x_NR_INTERNAL_IRQS, 0,
&tc3589x_irq_ops, tc3589x);
if (!tc3589x->domain) {
if (blocks & TC3589x_BLOCK_GPIO) {
ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_gpio,
ARRAY_SIZE(tc3589x_dev_gpio), NULL,
- tc3589x->irq_base, tc3589x->domain);
+ 0, tc3589x->domain);
if (ret) {
dev_err(tc3589x->dev, "failed to add gpio child\n");
return ret;
if (blocks & TC3589x_BLOCK_KEYPAD) {
ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_keypad,
ARRAY_SIZE(tc3589x_dev_keypad), NULL,
- tc3589x->irq_base, tc3589x->domain);
+ 0, tc3589x->domain);
if (ret) {
dev_err(tc3589x->dev, "failed to keypad child\n");
return ret;
tc3589x->dev = &i2c->dev;
tc3589x->i2c = i2c;
tc3589x->pdata = pdata;
- tc3589x->irq_base = pdata->irq_base;
switch (version) {
case TC3589X_TC35893:
if (pdata && pdata->suspend)
pdata->suspend(dev);
- clk_disable(tc6387xb->clk32k);
+ clk_disable_unprepare(tc6387xb->clk32k);
return 0;
}
struct tc6387xb *tc6387xb = platform_get_drvdata(dev);
struct tc6387xb_platform_data *pdata = dev_get_platdata(&dev->dev);
- clk_enable(tc6387xb->clk32k);
+ clk_prepare_enable(tc6387xb->clk32k);
if (pdata && pdata->resume)
pdata->resume(dev);
struct platform_device *dev = to_platform_device(mmc->dev.parent);
struct tc6387xb *tc6387xb = platform_get_drvdata(dev);
- clk_enable(tc6387xb->clk32k);
+ clk_prepare_enable(tc6387xb->clk32k);
tmio_core_mmc_enable(tc6387xb->scr + 0x200, 0,
tc6387xb_mmc_resources[0].start & 0xfffe);
struct platform_device *dev = to_platform_device(mmc->dev.parent);
struct tc6387xb *tc6387xb = platform_get_drvdata(dev);
- clk_disable(tc6387xb->clk32k);
+ clk_disable_unprepare(tc6387xb->clk32k);
return 0;
}
mfd_remove_devices(&dev->dev);
iounmap(tc6387xb->scr);
release_resource(&tc6387xb->rscr);
- clk_disable(tc6387xb->clk32k);
+ clk_disable_unprepare(tc6387xb->clk32k);
clk_put(tc6387xb->clk32k);
kfree(tc6387xb);
return 0;
}
+static int tc6393xb_ohci_suspend(struct platform_device *dev)
+{
+ struct tc6393xb_platform_data *tcpd = dev_get_platdata(dev->dev.parent);
+
+ /* We can't properly store/restore OHCI state, so fail here */
+ if (tcpd->resume_restore)
+ return -EBUSY;
+
+ return tc6393xb_ohci_disable(dev);
+}
+
static int tc6393xb_fb_enable(struct platform_device *dev)
{
struct tc6393xb *tc6393xb = dev_get_drvdata(dev->dev.parent);
.num_resources = ARRAY_SIZE(tc6393xb_ohci_resources),
.resources = tc6393xb_ohci_resources,
.enable = tc6393xb_ohci_enable,
- .suspend = tc6393xb_ohci_disable,
+ .suspend = tc6393xb_ohci_suspend,
.resume = tc6393xb_ohci_enable,
.disable = tc6393xb_ohci_disable,
},
goto err_ioremap;
}
- ret = clk_enable(tc6393xb->clk);
+ ret = clk_prepare_enable(tc6393xb->clk);
if (ret)
goto err_clk_enable;
gpiochip_remove(&tc6393xb->gpio);
tcpd->disable(dev);
err_enable:
- clk_disable(tc6393xb->clk);
+ clk_disable_unprepare(tc6393xb->clk);
err_clk_enable:
iounmap(tc6393xb->scr);
err_ioremap:
gpiochip_remove(&tc6393xb->gpio);
ret = tcpd->disable(dev);
- clk_disable(tc6393xb->clk);
+ clk_disable_unprepare(tc6393xb->clk);
iounmap(tc6393xb->scr);
release_resource(&tc6393xb->rscr);
clk_put(tc6393xb->clk);
ioread8(tc6393xb->scr + SCR_GPI_BCR(i));
}
ret = tcpd->suspend(dev);
- clk_disable(tc6393xb->clk);
+ clk_disable_unprepare(tc6393xb->clk);
return ret;
}
int ret;
int i;
- clk_enable(tc6393xb->clk);
+ clk_prepare_enable(tc6393xb->clk);
ret = tcpd->resume(dev);
if (ret)
static const struct regmap_irq tps65090_irqs[] = {
/* INT1 IRQs*/
[TPS65090_IRQ_VAC_STATUS_CHANGE] = {
- .mask = TPS65090_INT1_MASK_VAC_STATUS_CHANGE,
+ .mask = TPS65090_INT1_MASK_VAC_STATUS_CHANGE,
},
[TPS65090_IRQ_VSYS_STATUS_CHANGE] = {
- .mask = TPS65090_INT1_MASK_VSYS_STATUS_CHANGE,
+ .mask = TPS65090_INT1_MASK_VSYS_STATUS_CHANGE,
},
[TPS65090_IRQ_BAT_STATUS_CHANGE] = {
- .mask = TPS65090_INT1_MASK_BAT_STATUS_CHANGE,
+ .mask = TPS65090_INT1_MASK_BAT_STATUS_CHANGE,
},
[TPS65090_IRQ_CHARGING_STATUS_CHANGE] = {
- .mask = TPS65090_INT1_MASK_CHARGING_STATUS_CHANGE,
+ .mask = TPS65090_INT1_MASK_CHARGING_STATUS_CHANGE,
},
[TPS65090_IRQ_CHARGING_COMPLETE] = {
- .mask = TPS65090_INT1_MASK_CHARGING_COMPLETE,
+ .mask = TPS65090_INT1_MASK_CHARGING_COMPLETE,
},
[TPS65090_IRQ_OVERLOAD_DCDC1] = {
- .mask = TPS65090_INT1_MASK_OVERLOAD_DCDC1,
+ .mask = TPS65090_INT1_MASK_OVERLOAD_DCDC1,
},
[TPS65090_IRQ_OVERLOAD_DCDC2] = {
- .mask = TPS65090_INT1_MASK_OVERLOAD_DCDC2,
+ .mask = TPS65090_INT1_MASK_OVERLOAD_DCDC2,
},
/* INT2 IRQs*/
[TPS65090_IRQ_OVERLOAD_DCDC3] = {
- .reg_offset = 1,
- .mask = TPS65090_INT2_MASK_OVERLOAD_DCDC3,
+ .reg_offset = 1,
+ .mask = TPS65090_INT2_MASK_OVERLOAD_DCDC3,
},
[TPS65090_IRQ_OVERLOAD_FET1] = {
- .reg_offset = 1,
- .mask = TPS65090_INT2_MASK_OVERLOAD_FET1,
+ .reg_offset = 1,
+ .mask = TPS65090_INT2_MASK_OVERLOAD_FET1,
},
[TPS65090_IRQ_OVERLOAD_FET2] = {
- .reg_offset = 1,
- .mask = TPS65090_INT2_MASK_OVERLOAD_FET2,
+ .reg_offset = 1,
+ .mask = TPS65090_INT2_MASK_OVERLOAD_FET2,
},
[TPS65090_IRQ_OVERLOAD_FET3] = {
- .reg_offset = 1,
- .mask = TPS65090_INT2_MASK_OVERLOAD_FET3,
+ .reg_offset = 1,
+ .mask = TPS65090_INT2_MASK_OVERLOAD_FET3,
},
[TPS65090_IRQ_OVERLOAD_FET4] = {
- .reg_offset = 1,
- .mask = TPS65090_INT2_MASK_OVERLOAD_FET4,
+ .reg_offset = 1,
+ .mask = TPS65090_INT2_MASK_OVERLOAD_FET4,
},
[TPS65090_IRQ_OVERLOAD_FET5] = {
- .reg_offset = 1,
- .mask = TPS65090_INT2_MASK_OVERLOAD_FET5,
+ .reg_offset = 1,
+ .mask = TPS65090_INT2_MASK_OVERLOAD_FET5,
},
[TPS65090_IRQ_OVERLOAD_FET6] = {
- .reg_offset = 1,
- .mask = TPS65090_INT2_MASK_OVERLOAD_FET6,
+ .reg_offset = 1,
+ .mask = TPS65090_INT2_MASK_OVERLOAD_FET6,
},
[TPS65090_IRQ_OVERLOAD_FET7] = {
- .reg_offset = 1,
- .mask = TPS65090_INT2_MASK_OVERLOAD_FET7,
+ .reg_offset = 1,
+ .mask = TPS65090_INT2_MASK_OVERLOAD_FET7,
},
};
#endif
static int tps65090_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+ const struct i2c_device_id *id)
{
struct tps65090_platform_data *pdata = dev_get_platdata(&client->dev);
int irq_base = 0;
if (client->irq) {
ret = regmap_add_irq_chip(tps65090->rmap, client->irq,
- IRQF_ONESHOT | IRQF_TRIGGER_LOW, irq_base,
- &tps65090_irq_chip, &tps65090->irq_data);
- if (ret) {
- dev_err(&client->dev,
- "IRQ init failed with err: %d\n", ret);
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW, irq_base,
+ &tps65090_irq_chip, &tps65090->irq_data);
+ if (ret) {
+ dev_err(&client->dev,
+ "IRQ init failed with err: %d\n", ret);
return ret;
}
} else {
}
ret = mfd_add_devices(tps65090->dev, -1, tps65090s,
- ARRAY_SIZE(tps65090s), NULL,
- 0, regmap_irq_get_domain(tps65090->irq_data));
+ ARRAY_SIZE(tps65090s), NULL,
+ 0, regmap_irq_get_domain(tps65090->irq_data));
if (ret) {
dev_err(&client->dev, "add mfd devices failed with err: %d\n",
ret);
static const struct mfd_cell tps65217s[] = {
{
.name = "tps65217-pmic",
+ .of_compatible = "ti,tps65217-pmic",
},
{
.name = "tps65217-bl",
+ .of_compatible = "ti,tps65217-bl",
},
};
#define PWR_DEVSLP BIT(1)
#define PWR_DEVOFF BIT(0)
+/* Register bits for CFG_P1_TRANSITION (also for P2 and P3) */
+#define STARTON_SWBUG BIT(7) /* Start on watchdog */
+#define STARTON_VBUS BIT(5) /* Start on VBUS */
+#define STARTON_VBAT BIT(4) /* Start on battery insert */
+#define STARTON_RTC BIT(3) /* Start on RTC */
+#define STARTON_USB BIT(2) /* Start on USB host */
+#define STARTON_CHG BIT(1) /* Start on charger */
+#define STARTON_PWON BIT(0) /* Start on PWRON button */
+
#define SEQ_OFFSYNC (1 << 0)
#define PHY_TO_OFF_PM_MASTER(p) (p - 0x36)
return 0;
}
+static int twl4030_starton_mask_and_set(u8 bitmask, u8 bitvalues)
+{
+ u8 regs[3] = { TWL4030_PM_MASTER_CFG_P1_TRANSITION,
+ TWL4030_PM_MASTER_CFG_P2_TRANSITION,
+ TWL4030_PM_MASTER_CFG_P3_TRANSITION, };
+ u8 val;
+ int i, err;
+
+ err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG1,
+ TWL4030_PM_MASTER_PROTECT_KEY);
+ if (err)
+ goto relock;
+ err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER,
+ TWL4030_PM_MASTER_KEY_CFG2,
+ TWL4030_PM_MASTER_PROTECT_KEY);
+ if (err)
+ goto relock;
+
+ for (i = 0; i < sizeof(regs); i++) {
+ err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER,
+ &val, regs[i]);
+ if (err)
+ break;
+ val = (~bitmask & val) | (bitmask & bitvalues);
+ err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER,
+ val, regs[i]);
+ if (err)
+ break;
+ }
+
+ if (err)
+ pr_err("TWL4030 Register access failed: %i\n", err);
+
+relock:
+ return twl_i2c_write_u8(TWL_MODULE_PM_MASTER, 0,
+ TWL4030_PM_MASTER_PROTECT_KEY);
+}
+
/*
* In master mode, start the power off sequence.
* After a successful execution, TWL shuts down the power to the SoC
{
int err;
+ /* Disable start on charger or VBUS as it can break poweroff */
+ err = twl4030_starton_mask_and_set(STARTON_VBUS | STARTON_CHG, 0);
+ if (err)
+ pr_err("TWL4030 Unable to configure start-up\n");
+
err = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, PWR_DEVOFF,
TWL4030_PM_MASTER_P1_SW_EVENTS);
if (err)
};
static struct of_device_id twl4030_power_of_match[] = {
+ {
+ .compatible = "ti,twl4030-power",
+ },
{
.compatible = "ti,twl4030-power-reset",
.data = &omap3_reset,
version >> 8, version & 0xff,
vb->usb_dev->bus->busnum, vb->usb_dev->devnum);
- ret = mfd_add_devices(&interface->dev, -1, vprbrd_devs,
- ARRAY_SIZE(vprbrd_devs), NULL, 0, NULL);
+ ret = mfd_add_hotplug_devices(&interface->dev, vprbrd_devs,
+ ARRAY_SIZE(vprbrd_devs));
if (ret != 0) {
dev_err(&interface->dev, "Failed to add mfd devices to core.");
goto error;
{ 0x00000218, 0x01A6 }, /* R536 - Mic Bias Ctrl 1 */
{ 0x00000219, 0x01A6 }, /* R537 - Mic Bias Ctrl 2 */
{ 0x0000021A, 0x01A6 }, /* R538 - Mic Bias Ctrl 3 */
- { 0x00000225, 0x0400 }, /* R549 - HP Ctrl 1L */
- { 0x00000226, 0x0400 }, /* R550 - HP Ctrl 1R */
{ 0x00000293, 0x0000 }, /* R659 - Accessory Detect Mode 1 */
{ 0x0000029B, 0x0020 }, /* R667 - Headphone Detect 1 */
{ 0x0000029C, 0x0000 }, /* R668 - Headphone Detect 2 */
case ARIZONA_MIC_BIAS_CTRL_1:
case ARIZONA_MIC_BIAS_CTRL_2:
case ARIZONA_MIC_BIAS_CTRL_3:
+ case ARIZONA_HP_CTRL_1L:
+ case ARIZONA_HP_CTRL_1R:
case ARIZONA_ACCESSORY_DETECT_MODE_1:
case ARIZONA_HEADPHONE_DETECT_1:
case ARIZONA_HEADPHONE_DETECT_2:
case ARIZONA_DSP1_SCRATCH_1:
case ARIZONA_DSP1_SCRATCH_2:
case ARIZONA_DSP1_SCRATCH_3:
+ case ARIZONA_HP_CTRL_1L:
+ case ARIZONA_HP_CTRL_1R:
case ARIZONA_HEADPHONE_DETECT_2:
case ARIZONA_HP_DACVAL:
case ARIZONA_MIC_DETECT_3:
{ 0x00000548, 0x1818 }, /* R1352 - AIF2 Frame Ctrl 2 */
{ 0x00000549, 0x0000 }, /* R1353 - AIF2 Frame Ctrl 3 */
{ 0x0000054A, 0x0001 }, /* R1354 - AIF2 Frame Ctrl 4 */
+ { 0x0000054B, 0x0002 }, /* R1355 - AIF2 Frame Ctrl 5 */
+ { 0x0000054C, 0x0003 }, /* R1356 - AIF2 Frame Ctrl 6 */
+ { 0x0000054D, 0x0004 }, /* R1357 - AIF2 Frame Ctrl 7 */
+ { 0x0000054E, 0x0005 }, /* R1358 - AIF2 Frame Ctrl 8 */
{ 0x00000551, 0x0000 }, /* R1361 - AIF2 Frame Ctrl 11 */
{ 0x00000552, 0x0001 }, /* R1362 - AIF2 Frame Ctrl 12 */
+ { 0x00000553, 0x0002 }, /* R1363 - AIF2 Frame Ctrl 13 */
+ { 0x00000554, 0x0003 }, /* R1364 - AIF2 Frame Ctrl 14 */
+ { 0x00000555, 0x0004 }, /* R1365 - AIF2 Frame Ctrl 15 */
+ { 0x00000556, 0x0005 }, /* R1366 - AIF2 Frame Ctrl 16 */
{ 0x00000559, 0x0000 }, /* R1369 - AIF2 Tx Enables */
{ 0x0000055A, 0x0000 }, /* R1370 - AIF2 Rx Enables */
{ 0x00000580, 0x000C }, /* R1408 - AIF3 BCLK Ctrl */
case ARIZONA_MIC_BIAS_CTRL_1:
case ARIZONA_MIC_BIAS_CTRL_2:
case ARIZONA_MIC_BIAS_CTRL_3:
+ case ARIZONA_HP_CTRL_1L:
+ case ARIZONA_HP_CTRL_1R:
case ARIZONA_ACCESSORY_DETECT_MODE_1:
case ARIZONA_HEADPHONE_DETECT_1:
case ARIZONA_HEADPHONE_DETECT_2:
case ARIZONA_AIF2_FRAME_CTRL_2:
case ARIZONA_AIF2_FRAME_CTRL_3:
case ARIZONA_AIF2_FRAME_CTRL_4:
+ case ARIZONA_AIF2_FRAME_CTRL_5:
+ case ARIZONA_AIF2_FRAME_CTRL_6:
+ case ARIZONA_AIF2_FRAME_CTRL_7:
+ case ARIZONA_AIF2_FRAME_CTRL_8:
case ARIZONA_AIF2_FRAME_CTRL_11:
case ARIZONA_AIF2_FRAME_CTRL_12:
+ case ARIZONA_AIF2_FRAME_CTRL_13:
+ case ARIZONA_AIF2_FRAME_CTRL_14:
+ case ARIZONA_AIF2_FRAME_CTRL_15:
+ case ARIZONA_AIF2_FRAME_CTRL_16:
case ARIZONA_AIF2_TX_ENABLES:
case ARIZONA_AIF2_RX_ENABLES:
case ARIZONA_AIF3_BCLK_CTRL:
case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS:
case ARIZONA_ASYNC_SAMPLE_RATE_2_STATUS:
case ARIZONA_MIC_DETECT_3:
+ case ARIZONA_HP_CTRL_1L:
+ case ARIZONA_HP_CTRL_1R:
case ARIZONA_HEADPHONE_DETECT_2:
case ARIZONA_INPUT_ENABLES_STATUS:
case ARIZONA_OUTPUT_STATUS_1:
goto err;
}
- mode = id2 & WM8350_CONF_STS_MASK >> 10;
+ mode = (id2 & WM8350_CONF_STS_MASK) >> 10;
cust_id = id2 & WM8350_CUST_ID_MASK;
chip_rev = (id2 & WM8350_CHIP_REV_MASK) >> 12;
dev_info(wm8350->dev,
{ 0x00000C23, 0x0000 }, /* R3107 - Misc Pad Ctrl 4 */
{ 0x00000C24, 0x0000 }, /* R3108 - Misc Pad Ctrl 5 */
{ 0x00000D08, 0xFFFF }, /* R3336 - Interrupt Status 1 Mask */
+ { 0x00000D09, 0xFFFF }, /* R3337 - Interrupt Status 2 Mask */
{ 0x00000D0A, 0xFFFF }, /* R3338 - Interrupt Status 3 Mask */
{ 0x00000D0B, 0xFFFF }, /* R3339 - Interrupt Status 4 Mask */
{ 0x00000D0C, 0xFEFF }, /* R3340 - Interrupt Status 5 Mask */
case ARIZONA_MIC_BIAS_CTRL_1:
case ARIZONA_MIC_BIAS_CTRL_2:
case ARIZONA_MIC_BIAS_CTRL_3:
+ case ARIZONA_HP_CTRL_1L:
+ case ARIZONA_HP_CTRL_1R:
case ARIZONA_ACCESSORY_DETECT_MODE_1:
case ARIZONA_HEADPHONE_DETECT_1:
case ARIZONA_HEADPHONE_DETECT_2:
case ARIZONA_INTERRUPT_STATUS_4:
case ARIZONA_INTERRUPT_STATUS_5:
case ARIZONA_INTERRUPT_STATUS_1_MASK:
+ case ARIZONA_INTERRUPT_STATUS_2_MASK:
case ARIZONA_INTERRUPT_STATUS_3_MASK:
case ARIZONA_INTERRUPT_STATUS_4_MASK:
case ARIZONA_INTERRUPT_STATUS_5_MASK:
case ARIZONA_SAMPLE_RATE_3_STATUS:
case ARIZONA_ASYNC_SAMPLE_RATE_1_STATUS:
case ARIZONA_MIC_DETECT_3:
+ case ARIZONA_HP_CTRL_1L:
+ case ARIZONA_HP_CTRL_1R:
case ARIZONA_HEADPHONE_DETECT_2:
case ARIZONA_INPUT_ENABLES_STATUS:
case ARIZONA_OUTPUT_STATUS_1:
}
/**
- * eeprom_93cx6_read - Read multiple words from eeprom
+ * eeprom_93cx6_read - Read a word from eeprom
* @eeprom: Pointer to eeprom structure
* @word: Word index from where we should start reading
* @data: target pointer where the information will have to be stored
}
EXPORT_SYMBOL_GPL(eeprom_93cx6_multiread);
+/**
+ * eeprom_93cx6_readb - Read a byte from eeprom
+ * @eeprom: Pointer to eeprom structure
+ * @word: Byte index from where we should start reading
+ * @data: target pointer where the information will have to be stored
+ *
+ * This function will read a byte of the eeprom data
+ * into the given data pointer.
+ */
+void eeprom_93cx6_readb(struct eeprom_93cx6 *eeprom, const u8 byte,
+ u8 *data)
+{
+ u16 command;
+ u16 tmp;
+
+ /*
+ * Initialize the eeprom register
+ */
+ eeprom_93cx6_startup(eeprom);
+
+ /*
+ * Select the read opcode and the byte to be read.
+ */
+ command = (PCI_EEPROM_READ_OPCODE << (eeprom->width + 1)) | byte;
+ eeprom_93cx6_write_bits(eeprom, command,
+ PCI_EEPROM_WIDTH_OPCODE + eeprom->width + 1);
+
+ /*
+ * Read the requested 8 bits.
+ */
+ eeprom_93cx6_read_bits(eeprom, &tmp, 8);
+ *data = tmp & 0xff;
+
+ /*
+ * Cleanup eeprom register.
+ */
+ eeprom_93cx6_cleanup(eeprom);
+}
+EXPORT_SYMBOL_GPL(eeprom_93cx6_readb);
+
+/**
+ * eeprom_93cx6_multireadb - Read multiple bytes from eeprom
+ * @eeprom: Pointer to eeprom structure
+ * @byte: Index from where we should start reading
+ * @data: target pointer where the information will have to be stored
+ * @words: Number of bytes that should be read.
+ *
+ * This function will read all requested bytes from the eeprom,
+ * this is done by calling eeprom_93cx6_readb() multiple times.
+ */
+void eeprom_93cx6_multireadb(struct eeprom_93cx6 *eeprom, const u8 byte,
+ u8 *data, const u16 bytes)
+{
+ unsigned int i;
+
+ for (i = 0; i < bytes; i++)
+ eeprom_93cx6_readb(eeprom, byte + i, &data[i]);
+}
+EXPORT_SYMBOL_GPL(eeprom_93cx6_multireadb);
+
/**
* eeprom_93cx6_wren - set the write enable state
* @eeprom: Pointer to eeprom structure
EXPORT_SYMBOL_GPL(enclosure_unregister);
#define ENCLOSURE_NAME_SIZE 64
+#define COMPONENT_NAME_SIZE 64
static void enclosure_link_name(struct enclosure_component *cdev, char *name)
{
put_device(dev->parent);
}
+static struct enclosure_component *
+enclosure_component_find_by_name(struct enclosure_device *edev,
+ const char *name)
+{
+ int i;
+ const char *cname;
+ struct enclosure_component *ecomp;
+
+ if (!edev || !name || !name[0])
+ return NULL;
+
+ for (i = 0; i < edev->components; i++) {
+ ecomp = &edev->component[i];
+ cname = dev_name(&ecomp->cdev);
+ if (ecomp->number != -1 &&
+ cname && cname[0] &&
+ !strcmp(cname, name))
+ return ecomp;
+ }
+
+ return NULL;
+}
+
static const struct attribute_group *enclosure_component_groups[];
/**
{
struct enclosure_component *ecomp;
struct device *cdev;
- int err;
+ int err, i;
+ char newname[COMPONENT_NAME_SIZE];
if (number >= edev->components)
return ERR_PTR(-EINVAL);
ecomp->number = number;
cdev = &ecomp->cdev;
cdev->parent = get_device(&edev->edev);
- if (name && name[0])
- dev_set_name(cdev, "%s", name);
- else
+
+ if (name && name[0]) {
+ /* Some hardware (e.g. enclosure in RX300 S6) has components
+ * with non unique names. Registering duplicates in sysfs
+ * will lead to warnings during bootup. So make the names
+ * unique by appending consecutive numbers -1, -2, ... */
+ i = 1;
+ snprintf(newname, COMPONENT_NAME_SIZE,
+ "%s", name);
+ while (enclosure_component_find_by_name(edev, newname))
+ snprintf(newname, COMPONENT_NAME_SIZE,
+ "%s-%i", name, i++);
+ dev_set_name(cdev, "%s", newname);
+ } else
dev_set_name(cdev, "%u", number);
cdev->release = enclosure_component_release;
/*
* We've only got one major, so number of mmcblk devices is
- * limited to 256 / number of minors per device.
+ * limited to (1 << 20) / number of minors per device. It is also
+ * currently limited by the size of the static bitmaps below.
*/
static int max_devices;
-/* 256 minors, so at most 256 separate devices */
-static DECLARE_BITMAP(dev_use, 256);
-static DECLARE_BITMAP(name_use, 256);
+#define MAX_DEVICES 256
+
+/* TODO: Replace these with struct ida */
+static DECLARE_BITMAP(dev_use, MAX_DEVICES);
+static DECLARE_BITMAP(name_use, MAX_DEVICES);
/*
* There is one mmc_blk_data per slot.
/*
* Only set in main mmc_blk_data associated
- * with mmc_card with mmc_set_drvdata, and keeps
+ * with mmc_card with dev_set_drvdata, and keeps
* track of the current selected device partition.
*/
unsigned int part_curr;
int ret;
struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
- ret = snprintf(buf, PAGE_SIZE, "%d",
+ ret = snprintf(buf, PAGE_SIZE, "%d\n",
get_disk_ro(dev_to_disk(dev)) ^
md->read_only);
mmc_blk_put(md);
struct mmc_blk_data *md)
{
int ret;
- struct mmc_blk_data *main_md = mmc_get_drvdata(card);
+ struct mmc_blk_data *main_md = dev_get_drvdata(&card->dev);
if (main_md->part_curr == md->part_type)
return 0;
err = mmc_hw_reset(host);
/* Ensure we switch back to the correct partition */
if (err != -EOPNOTSUPP) {
- struct mmc_blk_data *main_md = mmc_get_drvdata(host->card);
+ struct mmc_blk_data *main_md =
+ dev_get_drvdata(&host->card->dev);
int part_err;
main_md->part_curr = main_md->part_type;
}
if (status & R1_EXCEPTION_EVENT) {
- ext_csd = kzalloc(512, GFP_KERNEL);
- if (!ext_csd) {
- pr_err("%s: unable to allocate buffer for ext_csd\n",
- req->rq_disk->disk_name);
- return -ENOMEM;
- }
-
- err = mmc_send_ext_csd(card, ext_csd);
+ err = mmc_get_ext_csd(card, &ext_csd);
if (err) {
pr_err("%s: error %d sending ext_csd\n",
req->rq_disk->disk_name, err);
- check = MMC_BLK_ABORT;
- goto free;
+ return MMC_BLK_ABORT;
}
if ((ext_csd[EXT_CSD_EXP_EVENTS_STATUS] &
req->rq_disk->disk_name, packed->nr_entries,
packed->blocks, packed->idx_failure);
}
-free:
kfree(ext_csd);
}
/*
* !subname implies we are creating main mmc_blk_data that will be
- * associated with mmc_card with mmc_set_drvdata. Due to device
+ * associated with mmc_card with dev_set_drvdata. Due to device
* partitions, devidx will not coincide with a per-physical card
* index anymore so we keep track of a name index.
*/
END_FIXUP
};
-static int mmc_blk_probe(struct mmc_card *card)
+static int mmc_blk_probe(struct device *dev)
{
+ struct mmc_card *card = mmc_dev_to_card(dev);
struct mmc_blk_data *md, *part_md;
char cap_str[10];
if (mmc_blk_alloc_parts(card, md))
goto out;
- mmc_set_drvdata(card, md);
+ dev_set_drvdata(dev, md);
if (mmc_add_disk(md))
goto out;
return 0;
}
-static void mmc_blk_remove(struct mmc_card *card)
+static int mmc_blk_remove(struct device *dev)
{
- struct mmc_blk_data *md = mmc_get_drvdata(card);
+ struct mmc_card *card = mmc_dev_to_card(dev);
+ struct mmc_blk_data *md = dev_get_drvdata(dev);
mmc_blk_remove_parts(card, md);
pm_runtime_get_sync(&card->dev);
pm_runtime_disable(&card->dev);
pm_runtime_put_noidle(&card->dev);
mmc_blk_remove_req(md);
- mmc_set_drvdata(card, NULL);
+ dev_set_drvdata(dev, NULL);
+
+ return 0;
}
-static int _mmc_blk_suspend(struct mmc_card *card)
+static int _mmc_blk_suspend(struct device *dev)
{
struct mmc_blk_data *part_md;
- struct mmc_blk_data *md = mmc_get_drvdata(card);
+ struct mmc_blk_data *md = dev_get_drvdata(dev);
if (md) {
mmc_queue_suspend(&md->queue);
return 0;
}
-static void mmc_blk_shutdown(struct mmc_card *card)
+static void mmc_blk_shutdown(struct device *dev)
{
- _mmc_blk_suspend(card);
+ _mmc_blk_suspend(dev);
}
-#ifdef CONFIG_PM
-static int mmc_blk_suspend(struct mmc_card *card)
+#ifdef CONFIG_PM_SLEEP
+static int mmc_blk_suspend(struct device *dev)
{
- return _mmc_blk_suspend(card);
+ return _mmc_blk_suspend(dev);
}
-static int mmc_blk_resume(struct mmc_card *card)
+static int mmc_blk_resume(struct device *dev)
{
struct mmc_blk_data *part_md;
- struct mmc_blk_data *md = mmc_get_drvdata(card);
+ struct mmc_blk_data *md = dev_get_drvdata(dev);
if (md) {
/*
}
return 0;
}
-#else
-#define mmc_blk_suspend NULL
-#define mmc_blk_resume NULL
#endif
-static struct mmc_driver mmc_driver = {
- .drv = {
- .name = "mmcblk",
- },
+static SIMPLE_DEV_PM_OPS(mmc_blk_pm_ops, mmc_blk_suspend, mmc_blk_resume);
+
+static struct device_driver mmc_driver = {
+ .name = "mmcblk",
+ .pm = &mmc_blk_pm_ops,
.probe = mmc_blk_probe,
.remove = mmc_blk_remove,
- .suspend = mmc_blk_suspend,
- .resume = mmc_blk_resume,
.shutdown = mmc_blk_shutdown,
};
if (perdev_minors != CONFIG_MMC_BLOCK_MINORS)
pr_info("mmcblk: using %d minors per device\n", perdev_minors);
- max_devices = 256 / perdev_minors;
+ max_devices = min(MAX_DEVICES, (1 << MINORBITS) / perdev_minors);
res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");
if (res)
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/slab.h>
+#include <linux/device.h>
#include <linux/scatterlist.h>
#include <linux/swap.h> /* For nr_free_buffer_pages() */
#define BUFFER_ORDER 2
#define BUFFER_SIZE (PAGE_SIZE << BUFFER_ORDER)
+#define TEST_ALIGN_END 8
+
/*
* Limit the test area size to the maximum MMC HC erase group size. Note that
* the maximum SD allocation unit size is just 4MiB.
int ret, i;
struct scatterlist sg;
- for (i = 1;i < 4;i++) {
+ for (i = 1; i < TEST_ALIGN_END; i++) {
sg_init_one(&sg, test->buffer + i, 512);
ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
if (ret)
int ret, i;
struct scatterlist sg;
- for (i = 1;i < 4;i++) {
+ for (i = 1; i < TEST_ALIGN_END; i++) {
sg_init_one(&sg, test->buffer + i, 512);
ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
if (ret)
if (size < 1024)
return RESULT_UNSUP_HOST;
- for (i = 1;i < 4;i++) {
+ for (i = 1; i < TEST_ALIGN_END; i++) {
sg_init_one(&sg, test->buffer + i, size);
ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
if (ret)
if (size < 1024)
return RESULT_UNSUP_HOST;
- for (i = 1;i < 4;i++) {
+ for (i = 1; i < TEST_ALIGN_END; i++) {
sg_init_one(&sg, test->buffer + i, size);
ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
if (ret)
return ret;
}
-static int mmc_test_probe(struct mmc_card *card)
+static int mmc_test_probe(struct device *dev)
{
+ struct mmc_card *card = mmc_dev_to_card(dev);
int ret;
if (!mmc_card_mmc(card) && !mmc_card_sd(card))
return 0;
}
-static void mmc_test_remove(struct mmc_card *card)
+static int mmc_test_remove(struct device *dev)
{
+ struct mmc_card *card = mmc_dev_to_card(dev);
+
mmc_test_free_result(card);
mmc_test_free_dbgfs_file(card);
+
+ return 0;
}
-static void mmc_test_shutdown(struct mmc_card *card)
+static void mmc_test_shutdown(struct device *dev)
{
}
-static struct mmc_driver mmc_driver = {
- .drv = {
- .name = "mmc_test",
- },
+static struct device_driver mmc_driver = {
+ .name = "mmc_test",
.probe = mmc_test_probe,
.remove = mmc_test_remove,
.shutdown = mmc_test_shutdown,
if (!mqrq_cur->bounce_buf) {
pr_warn("%s: unable to allocate bounce cur buffer\n",
mmc_card_name(card));
- }
- mqrq_prev->bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
- if (!mqrq_prev->bounce_buf) {
- pr_warn("%s: unable to allocate bounce prev buffer\n",
- mmc_card_name(card));
- kfree(mqrq_cur->bounce_buf);
- mqrq_cur->bounce_buf = NULL;
+ } else {
+ mqrq_prev->bounce_buf =
+ kmalloc(bouncesz, GFP_KERNEL);
+ if (!mqrq_prev->bounce_buf) {
+ pr_warn("%s: unable to allocate bounce prev buffer\n",
+ mmc_card_name(card));
+ kfree(mqrq_cur->bounce_buf);
+ mqrq_cur->bounce_buf = NULL;
+ }
}
}
#include "sdio_cis.h"
#include "bus.h"
-#define to_mmc_driver(d) container_of(d, struct mmc_driver, drv)
-
static ssize_t type_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return retval;
}
-static int mmc_bus_probe(struct device *dev)
-{
- struct mmc_driver *drv = to_mmc_driver(dev->driver);
- struct mmc_card *card = mmc_dev_to_card(dev);
-
- return drv->probe(card);
-}
-
-static int mmc_bus_remove(struct device *dev)
-{
- struct mmc_driver *drv = to_mmc_driver(dev->driver);
- struct mmc_card *card = mmc_dev_to_card(dev);
-
- drv->remove(card);
-
- return 0;
-}
-
static void mmc_bus_shutdown(struct device *dev)
{
- struct mmc_driver *drv = to_mmc_driver(dev->driver);
struct mmc_card *card = mmc_dev_to_card(dev);
struct mmc_host *host = card->host;
int ret;
- if (dev->driver && drv->shutdown)
- drv->shutdown(card);
+ if (dev->driver && dev->driver->shutdown)
+ dev->driver->shutdown(dev);
if (host->bus_ops->shutdown) {
ret = host->bus_ops->shutdown(host);
#ifdef CONFIG_PM_SLEEP
static int mmc_bus_suspend(struct device *dev)
{
- struct mmc_driver *drv = to_mmc_driver(dev->driver);
struct mmc_card *card = mmc_dev_to_card(dev);
struct mmc_host *host = card->host;
int ret;
- if (dev->driver && drv->suspend) {
- ret = drv->suspend(card);
- if (ret)
- return ret;
- }
+ ret = pm_generic_suspend(dev);
+ if (ret)
+ return ret;
ret = host->bus_ops->suspend(host);
return ret;
static int mmc_bus_resume(struct device *dev)
{
- struct mmc_driver *drv = to_mmc_driver(dev->driver);
struct mmc_card *card = mmc_dev_to_card(dev);
struct mmc_host *host = card->host;
int ret;
pr_warn("%s: error %d during resume (card was removed?)\n",
mmc_hostname(host), ret);
- if (dev->driver && drv->resume)
- ret = drv->resume(card);
-
+ ret = pm_generic_resume(dev);
return ret;
}
#endif
.dev_groups = mmc_dev_groups,
.match = mmc_bus_match,
.uevent = mmc_bus_uevent,
- .probe = mmc_bus_probe,
- .remove = mmc_bus_remove,
.shutdown = mmc_bus_shutdown,
.pm = &mmc_bus_pm_ops,
};
* mmc_register_driver - register a media driver
* @drv: MMC media driver
*/
-int mmc_register_driver(struct mmc_driver *drv)
+int mmc_register_driver(struct device_driver *drv)
{
- drv->drv.bus = &mmc_bus_type;
- return driver_register(&drv->drv);
+ drv->bus = &mmc_bus_type;
+ return driver_register(drv);
}
-
EXPORT_SYMBOL(mmc_register_driver);
/**
* mmc_unregister_driver - unregister a media driver
* @drv: MMC media driver
*/
-void mmc_unregister_driver(struct mmc_driver *drv)
+void mmc_unregister_driver(struct device_driver *drv)
{
- drv->drv.bus = &mmc_bus_type;
- driver_unregister(&drv->drv);
+ drv->bus = &mmc_bus_type;
+ driver_unregister(drv);
}
-
EXPORT_SYMBOL(mmc_unregister_driver);
static void mmc_release_card(struct device *dev)
led_trigger_event(host->led, LED_OFF);
+ if (mrq->sbc) {
+ pr_debug("%s: req done <CMD%u>: %d: %08x %08x %08x %08x\n",
+ mmc_hostname(host), mrq->sbc->opcode,
+ mrq->sbc->error,
+ mrq->sbc->resp[0], mrq->sbc->resp[1],
+ mrq->sbc->resp[2], mrq->sbc->resp[3]);
+ }
+
pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n",
mmc_hostname(host), cmd->opcode, err,
cmd->resp[0], cmd->resp[1],
mrq->cmd->error = 0;
mrq->cmd->mrq = mrq;
+ if (mrq->sbc) {
+ mrq->sbc->error = 0;
+ mrq->sbc->mrq = mrq;
+ }
if (mrq->data) {
BUG_ON(mrq->data->blksz > host->max_blk_size);
BUG_ON(mrq->data->blocks > host->max_blk_count);
if (host->card && mmc_card_mmc(host->card) &&
((mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1) ||
(mmc_resp_type(host->areq->mrq->cmd) == MMC_RSP_R1B)) &&
- (host->areq->mrq->cmd->resp[0] & R1_EXCEPTION_EVENT))
+ (host->areq->mrq->cmd->resp[0] & R1_EXCEPTION_EVENT)) {
+
+ /* Cancel the prepared request */
+ if (areq)
+ mmc_post_req(host, areq->mrq, -EINVAL);
+
mmc_start_bkops(host->card, true);
+
+ /* prepare the request again */
+ if (areq)
+ mmc_pre_req(host, areq->mrq, !host->areq);
+ }
}
if (!err && areq)
int err;
u8 *ext_csd;
- /*
- * In future work, we should consider storing the entire ext_csd.
- */
- ext_csd = kmalloc(512, GFP_KERNEL);
- if (!ext_csd) {
- pr_err("%s: could not allocate buffer to receive the ext_csd.\n",
- mmc_hostname(card->host));
- return -ENOMEM;
- }
-
mmc_claim_host(card->host);
- err = mmc_send_ext_csd(card, ext_csd);
+ err = mmc_get_ext_csd(card, &ext_csd);
mmc_release_host(card->host);
if (err)
- goto out;
+ return err;
card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS];
card->ext_csd.raw_exception_status = ext_csd[EXT_CSD_EXP_EVENTS_STATUS];
-out:
kfree(ext_csd);
- return err;
+ return 0;
}
EXPORT_SYMBOL(mmc_read_bkops_status);
mmc_host_clk_release(host);
}
+/*
+ * Set initial state after a power cycle or a hw_reset.
+ */
+void mmc_set_initial_state(struct mmc_host *host)
+{
+ if (mmc_host_is_spi(host))
+ host->ios.chip_select = MMC_CS_HIGH;
+ else
+ host->ios.chip_select = MMC_CS_DONTCARE;
+ host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
+ host->ios.bus_width = MMC_BUS_WIDTH_1;
+ host->ios.timing = MMC_TIMING_LEGACY;
+
+ mmc_set_ios(host);
+}
+
/**
* mmc_vdd_to_ocrbitnum - Convert a voltage to the OCR bit number
* @vdd: voltage (mV)
pr_warn("%s: cannot verify signal voltage switch\n",
mmc_hostname(host));
+ mmc_host_clk_hold(host);
+
cmd.opcode = SD_SWITCH_VOLTAGE;
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err)
- return err;
+ goto err_command;
- if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR))
- return -EIO;
-
- mmc_host_clk_hold(host);
+ if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR)) {
+ err = -EIO;
+ goto err_command;
+ }
/*
* The card should drive cmd and dat[0:3] low immediately
* after the response of cmd11, but wait 1 ms to be sure
mmc_power_cycle(host, ocr);
}
+err_command:
mmc_host_clk_release(host);
return err;
mmc_host_clk_hold(host);
host->ios.vdd = fls(ocr) - 1;
- if (mmc_host_is_spi(host))
- host->ios.chip_select = MMC_CS_HIGH;
- else
- host->ios.chip_select = MMC_CS_DONTCARE;
- host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
host->ios.power_mode = MMC_POWER_UP;
- host->ios.bus_width = MMC_BUS_WIDTH_1;
- host->ios.timing = MMC_TIMING_LEGACY;
- mmc_set_ios(host);
+ /* Set initial state and call mmc_set_ios */
+ mmc_set_initial_state(host);
/* Try to set signal voltage to 3.3V but fall back to 1.8v or 1.2v */
if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330) == 0)
host->ios.clock = 0;
host->ios.vdd = 0;
- if (!mmc_host_is_spi(host)) {
- host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
- host->ios.chip_select = MMC_CS_DONTCARE;
- }
host->ios.power_mode = MMC_POWER_OFF;
- host->ios.bus_width = MMC_BUS_WIDTH_1;
- host->ios.timing = MMC_TIMING_LEGACY;
- mmc_set_ios(host);
+ /* Set initial state and call mmc_set_ios */
+ mmc_set_initial_state(host);
/*
* Some configurations, such as the 802.11 SDIO card in the OLPC
/* If the reset has happened, then a status command will fail */
if (check) {
- struct mmc_command cmd = {0};
- int err;
+ u32 status;
- cmd.opcode = MMC_SEND_STATUS;
- if (!mmc_host_is_spi(card->host))
- cmd.arg = card->rca << 16;
- cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
- err = mmc_wait_for_cmd(card->host, &cmd, 0);
- if (!err) {
+ if (!mmc_send_status(card, &status)) {
mmc_host_clk_release(host);
return -ENOSYS;
}
}
- if (mmc_host_is_spi(host)) {
- host->ios.chip_select = MMC_CS_HIGH;
- host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
- } else {
- host->ios.chip_select = MMC_CS_DONTCARE;
- host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
- }
- host->ios.bus_width = MMC_BUS_WIDTH_1;
- host->ios.timing = MMC_TIMING_LEGACY;
- mmc_set_ios(host);
+ /* Set initial state and call mmc_set_ios */
+ mmc_set_initial_state(host);
mmc_host_clk_release(host);
void mmc_power_up(struct mmc_host *host, u32 ocr);
void mmc_power_off(struct mmc_host *host);
void mmc_power_cycle(struct mmc_host *host, u32 ocr);
+void mmc_set_initial_state(struct mmc_host *host);
static inline void mmc_delay(unsigned int ms)
{
if (!buf)
return -ENOMEM;
- ext_csd = kmalloc(512, GFP_KERNEL);
- if (!ext_csd) {
- err = -ENOMEM;
- goto out_free;
- }
-
mmc_get_card(card);
- err = mmc_send_ext_csd(card, ext_csd);
+ err = mmc_get_ext_csd(card, &ext_csd);
mmc_put_card(card);
if (err)
goto out_free;
out_free:
kfree(buf);
- kfree(ext_csd);
return err;
}
struct device_node *np;
u32 bus_width;
int len, ret;
- bool cap_invert, gpio_invert;
+ bool cd_cap_invert, cd_gpio_invert = false;
+ bool ro_cap_invert, ro_gpio_invert = false;
if (!host->parent || !host->parent->of_node)
return 0;
if (of_find_property(np, "non-removable", &len)) {
host->caps |= MMC_CAP_NONREMOVABLE;
} else {
- if (of_property_read_bool(np, "cd-inverted"))
- cap_invert = true;
- else
- cap_invert = false;
+ cd_cap_invert = of_property_read_bool(np, "cd-inverted");
if (of_find_property(np, "broken-cd", &len))
host->caps |= MMC_CAP_NEEDS_POLL;
ret = mmc_gpiod_request_cd(host, "cd", 0, true,
- 0, &gpio_invert);
+ 0, &cd_gpio_invert);
if (ret) {
if (ret == -EPROBE_DEFER)
return ret;
* both inverted, the end result is that the CD line is
* not inverted.
*/
- if (cap_invert ^ gpio_invert)
+ if (cd_cap_invert ^ cd_gpio_invert)
host->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
}
/* Parse Write Protection */
- if (of_property_read_bool(np, "wp-inverted"))
- cap_invert = true;
- else
- cap_invert = false;
+ ro_cap_invert = of_property_read_bool(np, "wp-inverted");
- ret = mmc_gpiod_request_ro(host, "wp", 0, false, 0, &gpio_invert);
+ ret = mmc_gpiod_request_ro(host, "wp", 0, false, 0, &ro_gpio_invert);
if (ret) {
if (ret == -EPROBE_DEFER)
goto out;
dev_info(host->parent, "Got WP GPIO\n");
/* See the comment on CD inversion above */
- if (cap_invert ^ gpio_invert)
+ if (ro_cap_invert ^ ro_gpio_invert)
host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
if (of_find_property(np, "cap-sd-highspeed", &len))
return 0;
}
-/*
- * Read extended CSD.
- */
-static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
-{
- int err;
- u8 *ext_csd;
-
- BUG_ON(!card);
- BUG_ON(!new_ext_csd);
-
- *new_ext_csd = NULL;
-
- if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
- return 0;
-
- /*
- * As the ext_csd is so large and mostly unused, we don't store the
- * raw block in mmc_card.
- */
- ext_csd = kmalloc(512, GFP_KERNEL);
- if (!ext_csd) {
- pr_err("%s: could not allocate a buffer to "
- "receive the ext_csd.\n", mmc_hostname(card->host));
- return -ENOMEM;
- }
-
- err = mmc_send_ext_csd(card, ext_csd);
- if (err) {
- kfree(ext_csd);
- *new_ext_csd = NULL;
-
- /* If the host or the card can't do the switch,
- * fail more gracefully. */
- if ((err != -EINVAL)
- && (err != -ENOSYS)
- && (err != -EFAULT))
- return err;
-
- /*
- * High capacity cards should have this "magic" size
- * stored in their CSD.
- */
- if (card->csd.capacity == (4096 * 512)) {
- pr_err("%s: unable to read EXT_CSD "
- "on a possible high capacity card. "
- "Card will be ignored.\n",
- mmc_hostname(card->host));
- } else {
- pr_warn("%s: unable to read EXT_CSD, performance might suffer\n",
- mmc_hostname(card->host));
- err = 0;
- }
- } else
- *new_ext_csd = ext_csd;
-
- return err;
-}
-
static void mmc_select_card_type(struct mmc_card *card)
{
struct mmc_host *host = card->host;
/*
* Decode extended CSD.
*/
-static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
+static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
{
int err = 0, idx;
unsigned int part_size;
- BUG_ON(!card);
-
- if (!ext_csd)
- return 0;
-
/* Version is coded in the CSD_STRUCTURE byte in the EXT_CSD register */
card->ext_csd.raw_ext_csd_structure = ext_csd[EXT_CSD_STRUCTURE];
if (card->csd.structure == 3) {
card->ext_csd.data_sector_size = 512;
}
+ /* eMMC v5 or later */
+ if (card->ext_csd.rev >= 7) {
+ memcpy(card->ext_csd.fwrev, &ext_csd[EXT_CSD_FIRMWARE_VERSION],
+ MMC_FIRMWARE_LEN);
+ card->ext_csd.ffu_capable =
+ (ext_csd[EXT_CSD_SUPPORTED_MODE] & 0x1) &&
+ !(ext_csd[EXT_CSD_FW_CONFIG] & 0x1);
+ }
out:
return err;
}
-static inline void mmc_free_ext_csd(u8 *ext_csd)
+static int mmc_read_ext_csd(struct mmc_card *card)
{
+ u8 *ext_csd;
+ int err;
+
+ if (!mmc_can_ext_csd(card))
+ return 0;
+
+ err = mmc_get_ext_csd(card, &ext_csd);
+ if (err) {
+ /* If the host or the card can't do the switch,
+ * fail more gracefully. */
+ if ((err != -EINVAL)
+ && (err != -ENOSYS)
+ && (err != -EFAULT))
+ return err;
+
+ /*
+ * High capacity cards should have this "magic" size
+ * stored in their CSD.
+ */
+ if (card->csd.capacity == (4096 * 512)) {
+ pr_err("%s: unable to read EXT_CSD on a possible high capacity card. Card will be ignored.\n",
+ mmc_hostname(card->host));
+ } else {
+ pr_warn("%s: unable to read EXT_CSD, performance might suffer\n",
+ mmc_hostname(card->host));
+ err = 0;
+ }
+
+ return err;
+ }
+
+ err = mmc_decode_ext_csd(card, ext_csd);
kfree(ext_csd);
+ return err;
}
-
static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width)
{
u8 *bw_ext_csd;
return 0;
err = mmc_get_ext_csd(card, &bw_ext_csd);
-
- if (err || bw_ext_csd == NULL) {
- err = -EINVAL;
- goto out;
- }
+ if (err)
+ return err;
/* only compare read only fields */
err = !((card->ext_csd.raw_partition_support ==
if (err)
err = -EINVAL;
-out:
- mmc_free_ext_csd(bw_ext_csd);
+ kfree(bw_ext_csd);
return err;
}
MMC_DEV_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year);
MMC_DEV_ATTR(erase_size, "%u\n", card->erase_size << 9);
MMC_DEV_ATTR(preferred_erase_size, "%u\n", card->pref_erase << 9);
-MMC_DEV_ATTR(fwrev, "0x%x\n", card->cid.fwrev);
+MMC_DEV_ATTR(ffu_capable, "%d\n", card->ext_csd.ffu_capable);
MMC_DEV_ATTR(hwrev, "0x%x\n", card->cid.hwrev);
MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid);
MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name);
MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult);
MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors);
+static ssize_t mmc_fwrev_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct mmc_card *card = mmc_dev_to_card(dev);
+
+ if (card->ext_csd.rev < 7) {
+ return sprintf(buf, "0x%x\n", card->cid.fwrev);
+ } else {
+ return sprintf(buf, "0x%*phN\n", MMC_FIRMWARE_LEN,
+ card->ext_csd.fwrev);
+ }
+}
+
+static DEVICE_ATTR(fwrev, S_IRUGO, mmc_fwrev_show, NULL);
+
static struct attribute *mmc_std_attrs[] = {
&dev_attr_cid.attr,
&dev_attr_csd.attr,
&dev_attr_erase_size.attr,
&dev_attr_preferred_erase_size.attr,
&dev_attr_fwrev.attr,
+ &dev_attr_ffu_capable.attr,
&dev_attr_hwrev.attr,
&dev_attr_manfid.attr,
&dev_attr_name.attr,
unsigned int pwrclass_val = 0;
int err = 0;
- /* Power class selection is supported for versions >= 4.0 */
- if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
- return 0;
-
- /* Power class values are defined only for 4/8 bit bus */
- if (bus_width == EXT_CSD_BUS_WIDTH_1)
- return 0;
-
switch (1 << host->ios.vdd) {
case MMC_VDD_165_195:
if (host->ios.clock <= MMC_HIGH_26_MAX_DTR)
int err, ddr;
/* Power class selection is supported for versions >= 4.0 */
- if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
+ if (!mmc_can_ext_csd(card))
return 0;
bus_width = host->ios.bus_width;
unsigned idx, bus_width = 0;
int err = 0;
- if ((card->csd.mmca_vsn < CSD_SPEC_VER_4) &&
+ if (!mmc_can_ext_csd(card) &&
!(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)))
return 0;
ext_csd_bits,
card->ext_csd.generic_cmd6_time);
if (err) {
- pr_warn("%s: switch to bus width %d ddr failed\n",
+ pr_err("%s: switch to bus width %d ddr failed\n",
mmc_hostname(host), 1 << bus_width);
return err;
}
card->ext_csd.generic_cmd6_time,
true, true, true);
if (err) {
- pr_warn("%s: switch to high-speed from hs200 failed, err:%d\n",
+ pr_err("%s: switch to high-speed from hs200 failed, err:%d\n",
mmc_hostname(host), err);
return err;
}
EXT_CSD_DDR_BUS_WIDTH_8,
card->ext_csd.generic_cmd6_time);
if (err) {
- pr_warn("%s: switch to bus width for hs400 failed, err:%d\n",
+ pr_err("%s: switch to bus width for hs400 failed, err:%d\n",
mmc_hostname(host), err);
return err;
}
card->ext_csd.generic_cmd6_time,
true, true, true);
if (err) {
- pr_warn("%s: switch to hs400 failed, err:%d\n",
+ pr_err("%s: switch to hs400 failed, err:%d\n",
mmc_hostname(host), err);
return err;
}
{
int err = 0;
- if ((card->csd.mmca_vsn < CSD_SPEC_VER_4 &&
- card->ext_csd.hs_max_dtr == 0))
+ if (!mmc_can_ext_csd(card))
goto bus_speed;
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200)
mmc_host_clk_release(host);
if (err)
- pr_warn("%s: tuning execution failed\n",
+ pr_err("%s: tuning execution failed\n",
mmc_hostname(host));
}
int err;
u32 cid[4];
u32 rocr;
- u8 *ext_csd = NULL;
BUG_ON(!host);
WARN_ON(!host->claimed);
}
if (!oldcard) {
- /*
- * Fetch and process extended CSD.
- */
-
- err = mmc_get_ext_csd(card, &ext_csd);
- if (err)
- goto free_card;
- err = mmc_read_ext_csd(card, ext_csd);
+ /* Read extended CSD. */
+ err = mmc_read_ext_csd(card);
if (err)
goto free_card;
if (mmc_card_hs200(card)) {
err = mmc_hs200_tuning(card);
if (err)
- goto err;
+ goto free_card;
err = mmc_select_hs400(card);
if (err)
- goto err;
+ goto free_card;
} else if (mmc_card_hs(card)) {
/* Select the desired bus width optionally */
err = mmc_select_bus_width(card);
if (!IS_ERR_VALUE(err)) {
err = mmc_select_hs_ddr(card);
if (err)
- goto err;
+ goto free_card;
}
}
if (!oldcard)
host->card = card;
- mmc_free_ext_csd(ext_csd);
return 0;
free_card:
if (!oldcard)
mmc_remove_card(card);
err:
- mmc_free_ext_csd(ext_csd);
-
return err;
}
struct mmc_command cmd = {0};
struct mmc_data data = {0};
struct scatterlist sg;
- void *data_buf;
- int is_on_stack;
-
- is_on_stack = object_is_on_stack(buf);
- if (is_on_stack) {
- /*
- * dma onto stack is unsafe/nonportable, but callers to this
- * routine normally provide temporary on-stack buffers ...
- */
- data_buf = kmalloc(len, GFP_KERNEL);
- if (!data_buf)
- return -ENOMEM;
- } else
- data_buf = buf;
mrq.cmd = &cmd;
mrq.data = &data;
data.sg = &sg;
data.sg_len = 1;
- sg_init_one(&sg, data_buf, len);
+ sg_init_one(&sg, buf, len);
if (opcode == MMC_SEND_CSD || opcode == MMC_SEND_CID) {
/*
mmc_wait_for_req(host, &mrq);
- if (is_on_stack) {
- memcpy(buf, data_buf, len);
- kfree(data_buf);
- }
-
if (cmd.error)
return cmd.error;
if (data.error)
return mmc_send_cxd_native(card->host, card->rca << 16,
csd, MMC_SEND_CSD);
- csd_tmp = kmalloc(16, GFP_KERNEL);
+ csd_tmp = kzalloc(16, GFP_KERNEL);
if (!csd_tmp)
return -ENOMEM;
cid, MMC_SEND_CID);
}
- cid_tmp = kmalloc(16, GFP_KERNEL);
+ cid_tmp = kzalloc(16, GFP_KERNEL);
if (!cid_tmp)
return -ENOMEM;
return ret;
}
-int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
+int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
{
- return mmc_send_cxd_data(card, card->host, MMC_SEND_EXT_CSD,
- ext_csd, 512);
+ int err;
+ u8 *ext_csd;
+
+ if (!card || !new_ext_csd)
+ return -EINVAL;
+
+ if (!mmc_can_ext_csd(card))
+ return -EOPNOTSUPP;
+
+ /*
+ * As the ext_csd is so large and mostly unused, we don't store the
+ * raw block in mmc_card.
+ */
+ ext_csd = kzalloc(512, GFP_KERNEL);
+ if (!ext_csd)
+ return -ENOMEM;
+
+ err = mmc_send_cxd_data(card, card->host, MMC_SEND_EXT_CSD, ext_csd,
+ 512);
+ if (err)
+ kfree(ext_csd);
+ else
+ *new_ext_csd = ext_csd;
+
+ return err;
}
-EXPORT_SYMBOL_GPL(mmc_send_ext_csd);
+EXPORT_SYMBOL_GPL(mmc_get_ext_csd);
int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp)
{
}
EXPORT_SYMBOL_GPL(mmc_switch);
+int mmc_send_tuning(struct mmc_host *host)
+{
+ struct mmc_request mrq = {NULL};
+ struct mmc_command cmd = {0};
+ struct mmc_data data = {0};
+ struct scatterlist sg;
+ struct mmc_ios *ios = &host->ios;
+ const u8 *tuning_block_pattern;
+ int size, err = 0;
+ u8 *data_buf;
+ u32 opcode;
+
+ if (ios->bus_width == MMC_BUS_WIDTH_8) {
+ tuning_block_pattern = tuning_blk_pattern_8bit;
+ size = sizeof(tuning_blk_pattern_8bit);
+ opcode = MMC_SEND_TUNING_BLOCK_HS200;
+ } else if (ios->bus_width == MMC_BUS_WIDTH_4) {
+ tuning_block_pattern = tuning_blk_pattern_4bit;
+ size = sizeof(tuning_blk_pattern_4bit);
+ opcode = MMC_SEND_TUNING_BLOCK;
+ } else
+ return -EINVAL;
+
+ data_buf = kzalloc(size, GFP_KERNEL);
+ if (!data_buf)
+ return -ENOMEM;
+
+ mrq.cmd = &cmd;
+ mrq.data = &data;
+
+ cmd.opcode = opcode;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+ data.blksz = size;
+ data.blocks = 1;
+ data.flags = MMC_DATA_READ;
+
+ /*
+ * According to the tuning specs, Tuning process
+ * is normally shorter 40 executions of CMD19,
+ * and timeout value should be shorter than 150 ms
+ */
+ data.timeout_ns = 150 * NSEC_PER_MSEC;
+
+ data.sg = &sg;
+ data.sg_len = 1;
+ sg_init_one(&sg, data_buf, size);
+
+ mmc_wait_for_req(host, &mrq);
+
+ if (cmd.error) {
+ err = cmd.error;
+ goto out;
+ }
+
+ if (data.error) {
+ err = data.error;
+ goto out;
+ }
+
+ if (memcmp(data_buf, tuning_block_pattern, size))
+ err = -EIO;
+
+out:
+ kfree(data_buf);
+ return err;
+}
+EXPORT_SYMBOL_GPL(mmc_send_tuning);
+
static int
mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode,
u8 len)
return 0;
}
+
+int mmc_can_ext_csd(struct mmc_card *card)
+{
+ return (card && card->csd.mmca_vsn > CSD_SPEC_VER_3);
+}
int mmc_all_send_cid(struct mmc_host *host, u32 *cid);
int mmc_set_relative_addr(struct mmc_card *card);
int mmc_send_csd(struct mmc_card *card, u32 *csd);
-int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd);
int mmc_send_status(struct mmc_card *card, u32 *status);
int mmc_send_cid(struct mmc_host *host, u32 *cid);
int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
int mmc_bus_test(struct mmc_card *card, u8 bus_width);
int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status);
+int mmc_can_ext_csd(struct mmc_card *card);
#endif
if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) {
sdio_reset(host);
mmc_go_idle(host);
- err = mmc_sdio_init_card(host, host->card->ocr, host->card,
- mmc_card_keep_power(host));
+ mmc_send_if_cond(host, host->card->ocr);
+ err = mmc_send_io_op_cond(host, 0, NULL);
+ if (!err)
+ err = mmc_sdio_init_card(host, host->card->ocr,
+ host->card,
+ mmc_card_keep_power(host));
} else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) {
/* We may have switched to 1-bit mode during suspend */
err = sdio_enable_4bit_bus(host->card);
sdio_reset(host);
mmc_go_idle(host);
- mmc_send_if_cond(host, host->ocr_avail);
+ mmc_send_if_cond(host, host->card->ocr);
ret = mmc_send_io_op_cond(host, 0, NULL);
if (ret)
#include "sdio_cis.h"
#include "sdio_bus.h"
+#define to_sdio_driver(d) container_of(d, struct sdio_driver, drv)
+
/* show configuration fields */
#define sdio_config_attr(field, format_string) \
static ssize_t \
return ret;
}
-#ifdef CONFIG_PM
-
static const struct dev_pm_ops sdio_bus_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pm_generic_suspend, pm_generic_resume)
SET_RUNTIME_PM_OPS(
)
};
-#define SDIO_PM_OPS_PTR (&sdio_bus_pm_ops)
-
-#else /* !CONFIG_PM */
-
-#define SDIO_PM_OPS_PTR NULL
-
-#endif /* !CONFIG_PM */
-
static struct bus_type sdio_bus_type = {
.name = "sdio",
.dev_groups = sdio_dev_groups,
.uevent = sdio_bus_uevent,
.probe = sdio_bus_probe,
.remove = sdio_bus_remove,
- .pm = SDIO_PM_OPS_PTR,
+ .pm = &sdio_bus_pm_ops,
};
int sdio_register_bus(void)
static void sdio_acpi_set_handle(struct sdio_func *func)
{
struct mmc_host *host = func->card->host;
- u64 addr = (host->slotno << 16) | func->num;
+ u64 addr = ((u64)host->slotno << 16) | func->num;
acpi_preset_companion(&func->dev, ACPI_COMPANION(host->parent), addr);
}
config MMC_DW
tristate "Synopsys DesignWare Memory Card Interface"
depends on HAS_DMA
- depends on ARC || ARM || MIPS || COMPILE_TEST
+ depends on ARC || ARM || ARM64 || MIPS || COMPILE_TEST
help
This selects support for the Synopsys DesignWare Mobile Storage IP
block, this provides host support for SD and MMC interfaces, in both
help
This selects support for the SD/MMC Host Controller on
Allwinner sunxi SoCs.
+
+config MMC_TOSHIBA_PCI
+ tristate "Toshiba Type A SD/MMC Card Interface Driver"
+ depends on PCI
+ help
obj-$(CONFIG_MMC_MOXART) += moxart-mmc.o
obj-$(CONFIG_MMC_SUNXI) += sunxi-mmc.o
obj-$(CONFIG_MMC_USDHI6ROL0) += usdhi6rol0.o
+obj-$(CONFIG_MMC_TOSHIBA_PCI) += toshsd.o
obj-$(CONFIG_MMC_REALTEK_PCI) += rtsx_pci_sdmmc.o
obj-$(CONFIG_MMC_REALTEK_USB) += rtsx_usb_sdmmc.o
#include <linux/stat.h>
#include <linux/types.h>
#include <linux/platform_data/atmel.h>
+#include <linux/platform_data/mmc-atmel-mci.h>
#include <linux/mmc/host.h>
#include <linux/mmc/sdio.h>
-#include <mach/atmel-mci.h>
#include <linux/atmel-mci.h>
#include <linux/atmel_pdc.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/pinctrl/consumer.h>
#include <asm/cacheflush.h>
#include <asm/io.h>
#include "atmel-mci-regs.h"
+#define AUTOSUSPEND_DELAY 50
+
#define ATMCI_DATA_ERROR_FLAGS (ATMCI_DCRCE | ATMCI_DTOE | ATMCI_OVRE | ATMCI_UNRE)
#define ATMCI_DMA_THRESHOLD 16
if (!buf)
return -ENOMEM;
+ pm_runtime_get_sync(&host->pdev->dev);
+
/*
* Grab a more or less consistent snapshot. Note that we're
* not disabling interrupts, so IMR and SR may not be
* consistent.
*/
- ret = clk_prepare_enable(host->mck);
- if (ret)
- goto out;
-
spin_lock_bh(&host->lock);
memcpy_fromio(buf, host->regs, ATMCI_REGS_SIZE);
spin_unlock_bh(&host->lock);
- clk_disable_unprepare(host->mck);
+ pm_runtime_mark_last_busy(&host->pdev->dev);
+ pm_runtime_put_autosuspend(&host->pdev->dev);
seq_printf(s, "MR:\t0x%08x%s%s ",
buf[ATMCI_MR / 4],
val & ATMCI_CFG_LSYNC ? " LSYNC" : "");
}
-out:
kfree(buf);
return ret;
pdata->slot[slot_id].detect_is_active_high =
of_property_read_bool(cnp, "cd-inverted");
+ pdata->slot[slot_id].non_removable =
+ of_property_read_bool(cnp, "non-removable");
+
pdata->slot[slot_id].wp_pin =
of_get_named_gpio(cnp, "wp-gpios", 0);
}
WARN_ON(slot->mrq);
dev_dbg(&host->pdev->dev, "MRQ: cmd %u\n", mrq->cmd->opcode);
+ pm_runtime_get_sync(&host->pdev->dev);
+
/*
* We may "know" the card is gone even though there's still an
* electrical connection. If so, we really need to communicate
struct atmel_mci_slot *slot = mmc_priv(mmc);
struct atmel_mci *host = slot->host;
unsigned int i;
- bool unprepare_clk;
+
+ pm_runtime_get_sync(&host->pdev->dev);
slot->sdc_reg &= ~ATMCI_SDCBUS_MASK;
switch (ios->bus_width) {
unsigned int clock_min = ~0U;
u32 clkdiv;
- clk_prepare(host->mck);
- unprepare_clk = true;
-
spin_lock_bh(&host->lock);
if (!host->mode_reg) {
- clk_enable(host->mck);
- unprepare_clk = false;
atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST);
atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIEN);
if (host->caps.has_cfg_reg)
} else {
bool any_slot_active = false;
- unprepare_clk = false;
-
spin_lock_bh(&host->lock);
slot->clock = 0;
for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIDIS);
if (host->mode_reg) {
atmci_readl(host, ATMCI_MR);
- clk_disable(host->mck);
- unprepare_clk = true;
}
host->mode_reg = 0;
}
spin_unlock_bh(&host->lock);
}
- if (unprepare_clk)
- clk_unprepare(host->mck);
-
switch (ios->power_mode) {
case MMC_POWER_OFF:
if (!IS_ERR(mmc->supply.vmmc))
*/
break;
}
+
+ pm_runtime_mark_last_busy(&host->pdev->dev);
+ pm_runtime_put_autosuspend(&host->pdev->dev);
}
static int atmci_get_ro(struct mmc_host *mmc)
spin_unlock(&host->lock);
mmc_request_done(prev_mmc, mrq);
spin_lock(&host->lock);
+
+ pm_runtime_mark_last_busy(&host->pdev->dev);
+ pm_runtime_put_autosuspend(&host->pdev->dev);
}
static void atmci_command_complete(struct atmel_mci *host,
return IRQ_HANDLED;
}
-static int __init atmci_init_slot(struct atmel_mci *host,
+static int atmci_init_slot(struct atmel_mci *host,
struct mci_slot_pdata *slot_data, unsigned int id,
u32 sdc_reg, u32 sdio_irq)
{
}
}
- if (!gpio_is_valid(slot->detect_pin))
- mmc->caps |= MMC_CAP_NEEDS_POLL;
+ if (!gpio_is_valid(slot->detect_pin)) {
+ if (slot_data->non_removable)
+ mmc->caps |= MMC_CAP_NONREMOVABLE;
+ else
+ mmc->caps |= MMC_CAP_NEEDS_POLL;
+ }
if (gpio_is_valid(slot->wp_pin)) {
if (devm_gpio_request(&host->pdev->dev, slot->wp_pin,
mmc_free_host(slot->mmc);
}
-static bool atmci_filter(struct dma_chan *chan, void *pdata)
+static int atmci_configure_dma(struct atmel_mci *host)
{
- struct mci_platform_data *sl_pdata = pdata;
- struct mci_dma_data *sl;
-
- if (!sl_pdata)
- return false;
+ host->dma.chan = dma_request_slave_channel_reason(&host->pdev->dev,
+ "rxtx");
+ if (IS_ERR(host->dma.chan))
+ return PTR_ERR(host->dma.chan);
+
+ dev_info(&host->pdev->dev, "using %s for DMA transfers\n",
+ dma_chan_name(host->dma.chan));
+
+ host->dma_conf.src_addr = host->mapbase + ATMCI_RDR;
+ host->dma_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ host->dma_conf.src_maxburst = 1;
+ host->dma_conf.dst_addr = host->mapbase + ATMCI_TDR;
+ host->dma_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ host->dma_conf.dst_maxburst = 1;
+ host->dma_conf.device_fc = false;
- sl = sl_pdata->dma_slave;
- if (sl && find_slave_dev(sl) == chan->device->dev) {
- chan->private = slave_data_ptr(sl);
- return true;
- } else {
- return false;
- }
-}
-
-static bool atmci_configure_dma(struct atmel_mci *host)
-{
- struct mci_platform_data *pdata;
- dma_cap_mask_t mask;
-
- if (host == NULL)
- return false;
-
- pdata = host->pdev->dev.platform_data;
-
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
-
- host->dma.chan = dma_request_slave_channel_compat(mask, atmci_filter, pdata,
- &host->pdev->dev, "rxtx");
- if (!host->dma.chan) {
- dev_warn(&host->pdev->dev, "no DMA channel available\n");
- return false;
- } else {
- dev_info(&host->pdev->dev,
- "using %s for DMA transfers\n",
- dma_chan_name(host->dma.chan));
-
- host->dma_conf.src_addr = host->mapbase + ATMCI_RDR;
- host->dma_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- host->dma_conf.src_maxburst = 1;
- host->dma_conf.dst_addr = host->mapbase + ATMCI_TDR;
- host->dma_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- host->dma_conf.dst_maxburst = 1;
- host->dma_conf.device_fc = false;
- return true;
- }
+ return 0;
}
/*
* HSMCI provides DMA support and a new config register but no more supports
* PDC.
*/
-static void __init atmci_get_cap(struct atmel_mci *host)
+static void atmci_get_cap(struct atmel_mci *host)
{
unsigned int version;
}
}
-static int __init atmci_probe(struct platform_device *pdev)
+static int atmci_probe(struct platform_device *pdev)
{
struct mci_platform_data *pdata;
struct atmel_mci *host;
atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST);
host->bus_hz = clk_get_rate(host->mck);
- clk_disable_unprepare(host->mck);
host->mapbase = regs->start;
tasklet_init(&host->tasklet, atmci_tasklet_func, (unsigned long)host);
ret = request_irq(irq, atmci_interrupt, 0, dev_name(&pdev->dev), host);
- if (ret)
+ if (ret) {
+ clk_disable_unprepare(host->mck);
return ret;
+ }
/* Get MCI capabilities and set operations according to it */
atmci_get_cap(host);
- if (atmci_configure_dma(host)) {
+ ret = atmci_configure_dma(host);
+ if (ret == -EPROBE_DEFER)
+ goto err_dma_probe_defer;
+ if (ret == 0) {
host->prepare_data = &atmci_prepare_data_dma;
host->submit_data = &atmci_submit_data_dma;
host->stop_transfer = &atmci_stop_transfer_dma;
setup_timer(&host->timer, atmci_timeout_timer, (unsigned long)host);
+ pm_runtime_get_noresume(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, AUTOSUSPEND_DELAY);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
/* We need at least one slot to succeed */
nr_slots = 0;
ret = -ENODEV;
"Atmel MCI controller at 0x%08lx irq %d, %u slots\n",
host->mapbase, irq, nr_slots);
+ pm_runtime_mark_last_busy(&host->pdev->dev);
+ pm_runtime_put_autosuspend(&pdev->dev);
+
return 0;
err_dma_alloc:
atmci_cleanup_slot(host->slot[i], i);
}
err_init_slot:
+ clk_disable_unprepare(host->mck);
+
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+
del_timer_sync(&host->timer);
- if (host->dma.chan)
+ if (!IS_ERR(host->dma.chan))
dma_release_channel(host->dma.chan);
+err_dma_probe_defer:
free_irq(irq, host);
return ret;
}
-static int __exit atmci_remove(struct platform_device *pdev)
+static int atmci_remove(struct platform_device *pdev)
{
struct atmel_mci *host = platform_get_drvdata(pdev);
unsigned int i;
+ pm_runtime_get_sync(&pdev->dev);
+
if (host->buffer)
dma_free_coherent(&pdev->dev, host->buf_size,
host->buffer, host->buf_phys_addr);
atmci_cleanup_slot(host->slot[i], i);
}
- clk_prepare_enable(host->mck);
atmci_writel(host, ATMCI_IDR, ~0UL);
atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIDIS);
atmci_readl(host, ATMCI_SR);
- clk_disable_unprepare(host->mck);
del_timer_sync(&host->timer);
- if (host->dma.chan)
+ if (!IS_ERR(host->dma.chan))
dma_release_channel(host->dma.chan);
free_irq(platform_get_irq(pdev, 0), host);
+ clk_disable_unprepare(host->mck);
+
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+
return 0;
}
-static struct platform_driver atmci_driver = {
- .remove = __exit_p(atmci_remove),
- .driver = {
- .name = "atmel_mci",
- .of_match_table = of_match_ptr(atmci_dt_ids),
- },
-};
-
-static int __init atmci_init(void)
+#ifdef CONFIG_PM
+static int atmci_runtime_suspend(struct device *dev)
{
- return platform_driver_probe(&atmci_driver, atmci_probe);
+ struct atmel_mci *host = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(host->mck);
+
+ pinctrl_pm_select_sleep_state(dev);
+
+ return 0;
}
-static void __exit atmci_exit(void)
+static int atmci_runtime_resume(struct device *dev)
{
- platform_driver_unregister(&atmci_driver);
+ struct atmel_mci *host = dev_get_drvdata(dev);
+
+ pinctrl_pm_select_default_state(dev);
+
+ return clk_prepare_enable(host->mck);
}
+#endif
-late_initcall(atmci_init); /* try to load after dma driver when built-in */
-module_exit(atmci_exit);
+static const struct dev_pm_ops atmci_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_PM_RUNTIME_PM_OPS(atmci_runtime_suspend, atmci_runtime_resume, NULL)
+};
+
+static struct platform_driver atmci_driver = {
+ .probe = atmci_probe,
+ .remove = atmci_remove,
+ .driver = {
+ .name = "atmel_mci",
+ .of_match_table = of_match_ptr(atmci_dt_ids),
+ .pm = &atmci_dev_pm_ops,
+ },
+};
+module_platform_driver(atmci_driver);
MODULE_DESCRIPTION("Atmel Multimedia Card Interface driver");
MODULE_AUTHOR("Haavard Skinnemoen (Atmel)");
#define NUM_PINS(x) (x + 2)
#define SDMMC_CLKSEL 0x09C
+#define SDMMC_CLKSEL64 0x0A8
#define SDMMC_CLKSEL_CCLK_SAMPLE(x) (((x) & 7) << 0)
#define SDMMC_CLKSEL_CCLK_DRIVE(x) (((x) & 7) << 16)
#define SDMMC_CLKSEL_CCLK_DIVIDER(x) (((x) & 7) << 24)
DW_MCI_TYPE_EXYNOS5250,
DW_MCI_TYPE_EXYNOS5420,
DW_MCI_TYPE_EXYNOS5420_SMU,
+ DW_MCI_TYPE_EXYNOS7,
+ DW_MCI_TYPE_EXYNOS7_SMU,
};
/* Exynos implementation specific driver private data */
}, {
.compatible = "samsung,exynos5420-dw-mshc-smu",
.ctrl_type = DW_MCI_TYPE_EXYNOS5420_SMU,
+ }, {
+ .compatible = "samsung,exynos7-dw-mshc",
+ .ctrl_type = DW_MCI_TYPE_EXYNOS7,
+ }, {
+ .compatible = "samsung,exynos7-dw-mshc-smu",
+ .ctrl_type = DW_MCI_TYPE_EXYNOS7_SMU,
},
};
{
struct dw_mci_exynos_priv_data *priv = host->priv;
- if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU) {
+ if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) {
mci_writel(host, MPSBEGIN0, 0);
mci_writel(host, MPSEND0, DWMCI_BLOCK_NUM);
mci_writel(host, MPSCTRL0, DWMCI_MPSCTRL_SECURE_WRITE_BIT |
static int dw_mci_exynos_resume_noirq(struct device *dev)
{
struct dw_mci *host = dev_get_drvdata(dev);
+ struct dw_mci_exynos_priv_data *priv = host->priv;
u32 clksel;
- clksel = mci_readl(host, CLKSEL);
- if (clksel & SDMMC_CLKSEL_WAKEUP_INT)
- mci_writel(host, CLKSEL, clksel);
+ if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
+ clksel = mci_readl(host, CLKSEL64);
+ else
+ clksel = mci_readl(host, CLKSEL);
+
+ if (clksel & SDMMC_CLKSEL_WAKEUP_INT) {
+ if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
+ mci_writel(host, CLKSEL64, clksel);
+ else
+ mci_writel(host, CLKSEL, clksel);
+ }
return 0;
}
static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)
{
+ struct dw_mci_exynos_priv_data *priv = host->priv;
/*
* Exynos4412 and Exynos5250 extends the use of CMD register with the
* use of bit 29 (which is reserved on standard MSHC controllers) for
* HOLD register should be bypassed in case there is no phase shift
* applied on CMD/DATA that is sent to the card.
*/
- if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL)))
- *cmdr |= SDMMC_CMD_USE_HOLD_REG;
+ if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) {
+ if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL64)))
+ *cmdr |= SDMMC_CMD_USE_HOLD_REG;
+ } else {
+ if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL)))
+ *cmdr |= SDMMC_CMD_USE_HOLD_REG;
+ }
}
static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
u8 div = priv->ciu_div + 1;
if (ios->timing == MMC_TIMING_MMC_DDR52) {
- mci_writel(host, CLKSEL, priv->ddr_timing);
+ if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
+ mci_writel(host, CLKSEL64, priv->ddr_timing);
+ else
+ mci_writel(host, CLKSEL, priv->ddr_timing);
/* Should be double rate for DDR mode */
if (ios->bus_width == MMC_BUS_WIDTH_8)
wanted <<= 1;
} else {
- mci_writel(host, CLKSEL, priv->sdr_timing);
+ if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
+ mci_writel(host, CLKSEL64, priv->sdr_timing);
+ else
+ mci_writel(host, CLKSEL, priv->sdr_timing);
}
/* Don't care if wanted clock is zero */
static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host)
{
- return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL));
+ struct dw_mci_exynos_priv_data *priv = host->priv;
+
+ if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
+ return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL64));
+ else
+ return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL));
}
static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample)
{
u32 clksel;
- clksel = mci_readl(host, CLKSEL);
+ struct dw_mci_exynos_priv_data *priv = host->priv;
+
+ if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
+ clksel = mci_readl(host, CLKSEL64);
+ else
+ clksel = mci_readl(host, CLKSEL);
clksel = (clksel & ~0x7) | SDMMC_CLKSEL_CCLK_SAMPLE(sample);
- mci_writel(host, CLKSEL, clksel);
+ if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
+ mci_writel(host, CLKSEL64, clksel);
+ else
+ mci_writel(host, CLKSEL, clksel);
}
static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host)
{
+ struct dw_mci_exynos_priv_data *priv = host->priv;
u32 clksel;
u8 sample;
- clksel = mci_readl(host, CLKSEL);
+ if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
+ clksel = mci_readl(host, CLKSEL64);
+ else
+ clksel = mci_readl(host, CLKSEL);
sample = (clksel + 1) & 0x7;
clksel = (clksel & ~0x7) | sample;
- mci_writel(host, CLKSEL, clksel);
+ if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
+ priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
+ mci_writel(host, CLKSEL64, clksel);
+ else
+ mci_writel(host, CLKSEL, clksel);
return sample;
}
.data = &exynos_drv_data, },
{ .compatible = "samsung,exynos5420-dw-mshc-smu",
.data = &exynos_drv_data, },
+ { .compatible = "samsung,exynos7-dw-mshc",
+ .data = &exynos_drv_data, },
+ { .compatible = "samsung,exynos7-dw-mshc-smu",
+ .data = &exynos_drv_data, },
{},
};
MODULE_DEVICE_TABLE(of, dw_mci_exynos_match);
.prepare_command = dw_mci_pltfm_prepare_command,
};
+static const struct dw_mci_drv_data pistachio_drv_data = {
+ .prepare_command = dw_mci_pltfm_prepare_command,
+};
+
int dw_mci_pltfm_register(struct platform_device *pdev,
const struct dw_mci_drv_data *drv_data)
{
{ .compatible = "snps,dw-mshc", },
{ .compatible = "altr,socfpga-dw-mshc",
.data = &socfpga_drv_data },
+ { .compatible = "img,pistachio-dw-mshc",
+ .data = &pistachio_drv_data },
{},
};
MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
unsigned int cclkin;
u32 bus_hz;
+ if (ios->clock == 0)
+ return;
+
/*
* cclkin: source clock of mmc controller
* bus_hz: card interface clock generated by CLKGEN
}
}
+static int dw_mci_rockchip_init(struct dw_mci *host)
+{
+ /* It is slot 8 on Rockchip SoCs */
+ host->sdio_id0 = 8;
+
+ return 0;
+}
+
static const struct dw_mci_drv_data rk2928_drv_data = {
.prepare_command = dw_mci_rockchip_prepare_command,
+ .init = dw_mci_rockchip_init,
};
static const struct dw_mci_drv_data rk3288_drv_data = {
.prepare_command = dw_mci_rockchip_prepare_command,
.set_ios = dw_mci_rk3288_set_ios,
.setup_clock = dw_mci_rk3288_setup_clock,
+ .init = dw_mci_rockchip_init,
};
static const struct of_device_id dw_mci_rockchip_match[] = {
#include <linux/mmc/dw_mmc.h>
#include <linux/bitops.h>
#include <linux/regulator/consumer.h>
-#include <linux/workqueue.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/mmc/slot-gpio.h>
SDMMC_IDMAC_INT_FBE | SDMMC_IDMAC_INT_RI | \
SDMMC_IDMAC_INT_TI)
+struct idmac_desc_64addr {
+ u32 des0; /* Control Descriptor */
+
+ u32 des1; /* Reserved */
+
+ u32 des2; /*Buffer sizes */
+#define IDMAC_64ADDR_SET_BUFFER1_SIZE(d, s) \
+ ((d)->des2 = ((d)->des2 & 0x03ffe000) | ((s) & 0x1fff))
+
+ u32 des3; /* Reserved */
+
+ u32 des4; /* Lower 32-bits of Buffer Address Pointer 1*/
+ u32 des5; /* Upper 32-bits of Buffer Address Pointer 1*/
+
+ u32 des6; /* Lower 32-bits of Next Descriptor Address */
+ u32 des7; /* Upper 32-bits of Next Descriptor Address */
+};
+
struct idmac_desc {
u32 des0; /* Control Descriptor */
#define IDMAC_DES0_DIC BIT(1)
#endif /* CONFIG_MMC_DW_IDMAC */
static bool dw_mci_reset(struct dw_mci *host);
+static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset);
#if defined(CONFIG_DEBUG_FS)
static int dw_mci_req_show(struct seq_file *s, void *v)
unsigned int sg_len)
{
int i;
- struct idmac_desc *desc = host->sg_cpu;
+ if (host->dma_64bit_address == 1) {
+ struct idmac_desc_64addr *desc = host->sg_cpu;
- for (i = 0; i < sg_len; i++, desc++) {
- unsigned int length = sg_dma_len(&data->sg[i]);
- u32 mem_addr = sg_dma_address(&data->sg[i]);
+ for (i = 0; i < sg_len; i++, desc++) {
+ unsigned int length = sg_dma_len(&data->sg[i]);
+ u64 mem_addr = sg_dma_address(&data->sg[i]);
- /* Set the OWN bit and disable interrupts for this descriptor */
- desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH;
+ /*
+ * Set the OWN bit and disable interrupts for this
+ * descriptor
+ */
+ desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC |
+ IDMAC_DES0_CH;
+ /* Buffer length */
+ IDMAC_64ADDR_SET_BUFFER1_SIZE(desc, length);
+
+ /* Physical address to DMA to/from */
+ desc->des4 = mem_addr & 0xffffffff;
+ desc->des5 = mem_addr >> 32;
+ }
- /* Buffer length */
- IDMAC_SET_BUFFER1_SIZE(desc, length);
+ /* Set first descriptor */
+ desc = host->sg_cpu;
+ desc->des0 |= IDMAC_DES0_FD;
- /* Physical address to DMA to/from */
- desc->des2 = mem_addr;
- }
+ /* Set last descriptor */
+ desc = host->sg_cpu + (i - 1) *
+ sizeof(struct idmac_desc_64addr);
+ desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
+ desc->des0 |= IDMAC_DES0_LD;
- /* Set first descriptor */
- desc = host->sg_cpu;
- desc->des0 |= IDMAC_DES0_FD;
+ } else {
+ struct idmac_desc *desc = host->sg_cpu;
+
+ for (i = 0; i < sg_len; i++, desc++) {
+ unsigned int length = sg_dma_len(&data->sg[i]);
+ u32 mem_addr = sg_dma_address(&data->sg[i]);
+
+ /*
+ * Set the OWN bit and disable interrupts for this
+ * descriptor
+ */
+ desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC |
+ IDMAC_DES0_CH;
+ /* Buffer length */
+ IDMAC_SET_BUFFER1_SIZE(desc, length);
- /* Set last descriptor */
- desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc);
- desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
- desc->des0 |= IDMAC_DES0_LD;
+ /* Physical address to DMA to/from */
+ desc->des2 = mem_addr;
+ }
+
+ /* Set first descriptor */
+ desc = host->sg_cpu;
+ desc->des0 |= IDMAC_DES0_FD;
+
+ /* Set last descriptor */
+ desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc);
+ desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
+ desc->des0 |= IDMAC_DES0_LD;
+ }
wmb();
}
dw_mci_translate_sglist(host, host->data, sg_len);
+ /* Make sure to reset DMA in case we did PIO before this */
+ dw_mci_ctrl_reset(host, SDMMC_CTRL_DMA_RESET);
+ dw_mci_idmac_reset(host);
+
/* Select IDMAC interface */
temp = mci_readl(host, CTRL);
temp |= SDMMC_CTRL_USE_IDMAC;
static int dw_mci_idmac_init(struct dw_mci *host)
{
- struct idmac_desc *p;
int i;
- /* Number of descriptors in the ring buffer */
- host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc);
+ if (host->dma_64bit_address == 1) {
+ struct idmac_desc_64addr *p;
+ /* Number of descriptors in the ring buffer */
+ host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc_64addr);
+
+ /* Forward link the descriptor list */
+ for (i = 0, p = host->sg_cpu; i < host->ring_size - 1;
+ i++, p++) {
+ p->des6 = (host->sg_dma +
+ (sizeof(struct idmac_desc_64addr) *
+ (i + 1))) & 0xffffffff;
+
+ p->des7 = (u64)(host->sg_dma +
+ (sizeof(struct idmac_desc_64addr) *
+ (i + 1))) >> 32;
+ /* Initialize reserved and buffer size fields to "0" */
+ p->des1 = 0;
+ p->des2 = 0;
+ p->des3 = 0;
+ }
+
+ /* Set the last descriptor as the end-of-ring descriptor */
+ p->des6 = host->sg_dma & 0xffffffff;
+ p->des7 = (u64)host->sg_dma >> 32;
+ p->des0 = IDMAC_DES0_ER;
+
+ } else {
+ struct idmac_desc *p;
+ /* Number of descriptors in the ring buffer */
+ host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc);
- /* Forward link the descriptor list */
- for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++)
- p->des3 = host->sg_dma + (sizeof(struct idmac_desc) * (i + 1));
+ /* Forward link the descriptor list */
+ for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++)
+ p->des3 = host->sg_dma + (sizeof(struct idmac_desc) *
+ (i + 1));
- /* Set the last descriptor as the end-of-ring descriptor */
- p->des3 = host->sg_dma;
- p->des0 = IDMAC_DES0_ER;
+ /* Set the last descriptor as the end-of-ring descriptor */
+ p->des3 = host->sg_dma;
+ p->des0 = IDMAC_DES0_ER;
+ }
dw_mci_idmac_reset(host);
- /* Mask out interrupts - get Tx & Rx complete only */
- mci_writel(host, IDSTS, IDMAC_INT_CLR);
- mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | SDMMC_IDMAC_INT_RI |
- SDMMC_IDMAC_INT_TI);
+ if (host->dma_64bit_address == 1) {
+ /* Mask out interrupts - get Tx & Rx complete only */
+ mci_writel(host, IDSTS64, IDMAC_INT_CLR);
+ mci_writel(host, IDINTEN64, SDMMC_IDMAC_INT_NI |
+ SDMMC_IDMAC_INT_RI | SDMMC_IDMAC_INT_TI);
+
+ /* Set the descriptor base address */
+ mci_writel(host, DBADDRL, host->sg_dma & 0xffffffff);
+ mci_writel(host, DBADDRU, (u64)host->sg_dma >> 32);
+
+ } else {
+ /* Mask out interrupts - get Tx & Rx complete only */
+ mci_writel(host, IDSTS, IDMAC_INT_CLR);
+ mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI |
+ SDMMC_IDMAC_INT_RI | SDMMC_IDMAC_INT_TI);
+
+ /* Set the descriptor base address */
+ mci_writel(host, DBADDR, host->sg_dma);
+ }
- /* Set the descriptor base address */
- mci_writel(host, DBADDR, host->sg_dma);
return 0;
}
WARN_ON(!(data->flags & MMC_DATA_READ));
+ /*
+ * CDTHRCTL doesn't exist prior to 240A (in fact that register offset is
+ * in the FIFO region, so we really shouldn't access it).
+ */
+ if (host->verid < DW_MMC_240A)
+ return;
+
if (host->timing != MMC_TIMING_MMC_HS200 &&
host->timing != MMC_TIMING_UHS_SDR104)
goto disable;
/* enable clock; only low power if no SDIO */
clk_en_a = SDMMC_CLKEN_ENABLE << slot->id;
- if (!(mci_readl(host, INTMASK) & SDMMC_INT_SDIO(slot->id)))
+ if (!(mci_readl(host, INTMASK) & SDMMC_INT_SDIO(slot->sdio_id)))
clk_en_a |= SDMMC_CLKEN_LOW_PWR << slot->id;
mci_writel(host, CLKENA, clk_en_a);
ret = regulator_set_voltage(mmc->supply.vqmmc, min_uv, max_uv);
if (ret) {
- dev_err(&mmc->class_dev,
+ dev_dbg(&mmc->class_dev,
"Regulator set error %d: %d - %d\n",
ret, min_uv, max_uv);
return ret;
dw_mci_disable_low_power(slot);
mci_writel(host, INTMASK,
- (int_mask | SDMMC_INT_SDIO(slot->id)));
+ (int_mask | SDMMC_INT_SDIO(slot->sdio_id)));
} else {
mci_writel(host, INTMASK,
- (int_mask & ~SDMMC_INT_SDIO(slot->id)));
+ (int_mask & ~SDMMC_INT_SDIO(slot->sdio_id)));
}
}
tasklet_schedule(&host->tasklet);
}
+static void dw_mci_handle_cd(struct dw_mci *host)
+{
+ int i;
+
+ for (i = 0; i < host->num_slots; i++) {
+ struct dw_mci_slot *slot = host->slot[i];
+
+ if (!slot)
+ continue;
+
+ if (slot->mmc->ops->card_event)
+ slot->mmc->ops->card_event(slot->mmc);
+ mmc_detect_change(slot->mmc,
+ msecs_to_jiffies(host->pdata->detect_delay_ms));
+ }
+}
+
static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
{
struct dw_mci *host = dev_id;
if (pending & SDMMC_INT_CD) {
mci_writel(host, RINTSTS, SDMMC_INT_CD);
- queue_work(host->card_workqueue, &host->card_work);
+ dw_mci_handle_cd(host);
}
/* Handle SDIO Interrupts */
for (i = 0; i < host->num_slots; i++) {
struct dw_mci_slot *slot = host->slot[i];
- if (pending & SDMMC_INT_SDIO(i)) {
- mci_writel(host, RINTSTS, SDMMC_INT_SDIO(i));
+ if (pending & SDMMC_INT_SDIO(slot->sdio_id)) {
+ mci_writel(host, RINTSTS,
+ SDMMC_INT_SDIO(slot->sdio_id));
mmc_signal_sdio_irq(slot->mmc);
}
}
#ifdef CONFIG_MMC_DW_IDMAC
/* Handle DMA interrupts */
- pending = mci_readl(host, IDSTS);
- if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) {
- mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI);
- mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI);
- host->dma_ops->complete(host);
+ if (host->dma_64bit_address == 1) {
+ pending = mci_readl(host, IDSTS64);
+ if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) {
+ mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_TI |
+ SDMMC_IDMAC_INT_RI);
+ mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_NI);
+ host->dma_ops->complete(host);
+ }
+ } else {
+ pending = mci_readl(host, IDSTS);
+ if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) {
+ mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI |
+ SDMMC_IDMAC_INT_RI);
+ mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI);
+ host->dma_ops->complete(host);
+ }
}
#endif
return IRQ_HANDLED;
}
-static void dw_mci_work_routine_card(struct work_struct *work)
-{
- struct dw_mci *host = container_of(work, struct dw_mci, card_work);
- int i;
-
- for (i = 0; i < host->num_slots; i++) {
- struct dw_mci_slot *slot = host->slot[i];
- struct mmc_host *mmc = slot->mmc;
- struct mmc_request *mrq;
- int present;
-
- present = dw_mci_get_cd(mmc);
- while (present != slot->last_detect_state) {
- dev_dbg(&slot->mmc->class_dev, "card %s\n",
- present ? "inserted" : "removed");
-
- spin_lock_bh(&host->lock);
-
- /* Card change detected */
- slot->last_detect_state = present;
-
- /* Clean up queue if present */
- mrq = slot->mrq;
- if (mrq) {
- if (mrq == host->mrq) {
- host->data = NULL;
- host->cmd = NULL;
-
- switch (host->state) {
- case STATE_IDLE:
- case STATE_WAITING_CMD11_DONE:
- break;
- case STATE_SENDING_CMD11:
- case STATE_SENDING_CMD:
- mrq->cmd->error = -ENOMEDIUM;
- if (!mrq->data)
- break;
- /* fall through */
- case STATE_SENDING_DATA:
- mrq->data->error = -ENOMEDIUM;
- dw_mci_stop_dma(host);
- break;
- case STATE_DATA_BUSY:
- case STATE_DATA_ERROR:
- if (mrq->data->error == -EINPROGRESS)
- mrq->data->error = -ENOMEDIUM;
- /* fall through */
- case STATE_SENDING_STOP:
- if (mrq->stop)
- mrq->stop->error = -ENOMEDIUM;
- break;
- }
-
- dw_mci_request_end(host, mrq);
- } else {
- list_del(&slot->queue_node);
- mrq->cmd->error = -ENOMEDIUM;
- if (mrq->data)
- mrq->data->error = -ENOMEDIUM;
- if (mrq->stop)
- mrq->stop->error = -ENOMEDIUM;
-
- spin_unlock(&host->lock);
- mmc_request_done(slot->mmc, mrq);
- spin_lock(&host->lock);
- }
- }
-
- /* Power down slot */
- if (present == 0)
- dw_mci_reset(host);
-
- spin_unlock_bh(&host->lock);
-
- present = dw_mci_get_cd(mmc);
- }
-
- mmc_detect_change(slot->mmc,
- msecs_to_jiffies(host->pdata->detect_delay_ms));
- }
-}
-
#ifdef CONFIG_OF
/* given a slot id, find out the device node representing that slot */
static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot)
slot = mmc_priv(mmc);
slot->id = id;
+ slot->sdio_id = host->sdio_id0 + id;
slot->mmc = mmc;
slot->host = host;
host->slot[id] = slot;
dw_mci_init_debugfs(slot);
#endif
- /* Card initially undetected */
- slot->last_detect_state = 0;
-
return 0;
err_host_allocated:
static void dw_mci_init_dma(struct dw_mci *host)
{
+ int addr_config;
+ /* Check ADDR_CONFIG bit in HCON to find IDMAC address bus width */
+ addr_config = (mci_readl(host, HCON) >> 27) & 0x01;
+
+ if (addr_config == 1) {
+ /* host supports IDMAC in 64-bit address mode */
+ host->dma_64bit_address = 1;
+ dev_info(host->dev, "IDMAC supports 64-bit address mode.\n");
+ if (!dma_set_mask(host->dev, DMA_BIT_MASK(64)))
+ dma_set_coherent_mask(host->dev, DMA_BIT_MASK(64));
+ } else {
+ /* host supports IDMAC in 32-bit address mode */
+ host->dma_64bit_address = 0;
+ dev_info(host->dev, "IDMAC supports 32-bit address mode.\n");
+ }
+
/* Alloc memory for sg translation */
host->sg_cpu = dmam_alloc_coherent(host->dev, PAGE_SIZE,
&host->sg_dma, GFP_KERNEL);
host->data_offset = DATA_240A_OFFSET;
tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
- host->card_workqueue = alloc_workqueue("dw-mci-card",
- WQ_MEM_RECLAIM, 1);
- if (!host->card_workqueue) {
- ret = -ENOMEM;
- goto err_dmaunmap;
- }
- INIT_WORK(&host->card_work, dw_mci_work_routine_card);
ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt,
host->irq_flags, "dw-mci", host);
if (ret)
- goto err_workqueue;
+ goto err_dmaunmap;
if (host->pdata->num_slots)
host->num_slots = host->pdata->num_slots;
} else {
dev_dbg(host->dev, "attempted to initialize %d slots, "
"but failed on all\n", host->num_slots);
- goto err_workqueue;
+ goto err_dmaunmap;
}
if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO)
return 0;
-err_workqueue:
- destroy_workqueue(host->card_workqueue);
-
err_dmaunmap:
if (host->use_dma && host->dma_ops->exit)
host->dma_ops->exit(host);
mci_writel(host, CLKENA, 0);
mci_writel(host, CLKSRC, 0);
- destroy_workqueue(host->card_workqueue);
-
if (host->use_dma && host->dma_ops->exit)
host->dma_ops->exit(host);
#define SDMMC_BUFADDR 0x098
#define SDMMC_CDTHRCTL 0x100
#define SDMMC_DATA(x) (x)
+/*
+* Registers to support idmac 64-bit address mode
+*/
+#define SDMMC_DBADDRL 0x088
+#define SDMMC_DBADDRU 0x08c
+#define SDMMC_IDSTS64 0x090
+#define SDMMC_IDINTEN64 0x094
+#define SDMMC_DSCADDRL 0x098
+#define SDMMC_DSCADDRU 0x09c
+#define SDMMC_BUFADDRL 0x0A0
+#define SDMMC_BUFADDRU 0x0A4
/*
* Data offset is difference according to Version
* with CONFIG_MMC_CLKGATE.
* @flags: Random state bits associated with the slot.
* @id: Number of this slot.
- * @last_detect_state: Most recently observed card detect state.
+ * @sdio_id: Number of this slot in the SDIO interrupt registers.
*/
struct dw_mci_slot {
struct mmc_host *mmc;
#define DW_MMC_CARD_PRESENT 0
#define DW_MMC_CARD_NEED_INIT 1
int id;
- int last_detect_state;
+ int sdio_id;
};
struct dw_mci_tuning_data {
chan = host->dma_tx_channel;
dmaengine_terminate_all(chan);
+ if (host->dma_desc_current == next->dma_desc)
+ host->dma_desc_current = NULL;
+
+ if (host->dma_current == next->dma_chan)
+ host->dma_current = NULL;
+
next->dma_desc = NULL;
next->dma_chan = NULL;
+ data->host_cookie = 0;
}
}
if (ret)
goto cmd_irq_free;
- mmc_set_drvdata(pdev, mmc);
+ platform_set_drvdata(pdev, mmc);
mmc_add_host(mmc);
pr_info("%s: Qualcomm MSM SDCC at 0x%016llx irq %d,%d dma %d\n",
static int
msmsdcc_suspend(struct platform_device *dev, pm_message_t state)
{
- struct mmc_host *mmc = mmc_get_drvdata(dev);
+ struct mmc_host *mmc = platform_get_drvdata(dev);
if (mmc) {
struct msmsdcc_host *host = mmc_priv(mmc);
static int
msmsdcc_resume(struct platform_device *dev)
{
- struct mmc_host *mmc = mmc_get_drvdata(dev);
+ struct mmc_host *mmc = platform_get_drvdata(dev);
if (mmc) {
struct msmsdcc_host *host = mmc_priv(mmc);
mvsd_write(MVSD_BLK_COUNT, data->blocks);
mvsd_write(MVSD_BLK_SIZE, data->blksz);
- if (nodma || (data->blksz | data->sg->offset) & 3) {
+ if (nodma || (data->blksz | data->sg->offset) & 3 ||
+ ((!(data->flags & MMC_DATA_READ) && data->sg->offset & 0x3f))) {
/*
* We cannot do DMA on a buffer which offset or size
* is not aligned on a 4-byte boundary.
+ *
+ * It also appears the host to card DMA can corrupt
+ * data when the buffer is not aligned on a 64 byte
+ * boundary.
*/
host->pio_size = data->blocks * data->blksz;
host->pio_ptr = sg_virt(data->sg);
del_timer(&host->watchdog);
stat = mxcmci_readl(host, MMC_REG_STATUS);
- mxcmci_writel(host, stat & ~STATUS_DATA_TRANS_DONE, MMC_REG_STATUS);
dev_dbg(mmc_dev(host->mmc), "%s: 0x%08x\n", __func__, stat);
- if (stat & STATUS_READ_OP_DONE)
- mxcmci_writel(host, STATUS_READ_OP_DONE, MMC_REG_STATUS);
-
mxcmci_data_done(host, stat);
}
sdio_irq = (stat & STATUS_SDIO_INT_ACTIVE) && host->use_sdio;
spin_unlock_irqrestore(&host->lock, flags);
- if (mxcmci_use_dma(host) &&
- (stat & (STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE)))
- mxcmci_writel(host, STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE,
- MMC_REG_STATUS);
+ if (mxcmci_use_dma(host) && (stat & (STATUS_WRITE_OP_DONE)))
+ mxcmci_writel(host, STATUS_WRITE_OP_DONE, MMC_REG_STATUS);
if (sdio_irq) {
mxcmci_writel(host, STATUS_SDIO_INT_ACTIVE, MMC_REG_STATUS);
if (stat & STATUS_END_CMD_RESP)
mxcmci_cmd_done(host, stat);
- if (mxcmci_use_dma(host) &&
- (stat & (STATUS_DATA_TRANS_DONE | STATUS_WRITE_OP_DONE))) {
+ if (mxcmci_use_dma(host) && (stat & STATUS_WRITE_OP_DONE)) {
del_timer(&host->watchdog);
mxcmci_data_done(host, stat);
}
dat3_card_detect = true;
ret = mmc_regulator_get_supply(mmc);
- if (ret) {
- if (pdata && ret != -EPROBE_DEFER)
- mmc->ocr_avail = pdata->ocr_avail ? :
- MMC_VDD_32_33 | MMC_VDD_33_34;
+ if (ret == -EPROBE_DEFER)
+ goto out_free;
+
+ if (!mmc->ocr_avail) {
+ if (pdata && pdata->ocr_avail)
+ mmc->ocr_avail = pdata->ocr_avail;
else
- goto out_free;
+ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
}
if (dat3_card_detect)
struct regulator *reg_vmmc;
struct mxs_ssp *ssp;
- iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq_err = platform_get_irq(pdev, 0);
- if (!iores || irq_err < 0)
- return -EINVAL;
+ if (irq_err < 0)
+ return irq_err;
mmc = mmc_alloc_host(sizeof(struct mxs_mmc_host), &pdev->dev);
if (!mmc)
host = mmc_priv(mmc);
ssp = &host->ssp;
ssp->dev = &pdev->dev;
+ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ssp->base = devm_ioremap_resource(&pdev->dev, iores);
if (IS_ERR(ssp->base)) {
ret = PTR_ERR(ssp->base);
ret = PTR_ERR(ssp->clk);
goto out_mmc_free;
}
- clk_prepare_enable(ssp->clk);
+ ret = clk_prepare_enable(ssp->clk);
+ if (ret)
+ goto out_mmc_free;
ret = mxs_mmc_reset(host);
if (ret) {
platform_set_drvdata(pdev, mmc);
ret = devm_request_irq(&pdev->dev, irq_err, mxs_mmc_irq_handler, 0,
- DRIVER_NAME, host);
+ dev_name(&pdev->dev), host);
if (ret)
goto out_free_dma;
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int mxs_mmc_suspend(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
struct mxs_mmc_host *host = mmc_priv(mmc);
struct mxs_ssp *ssp = &host->ssp;
- clk_prepare_enable(ssp->clk);
- return 0;
+ return clk_prepare_enable(ssp->clk);
}
-
-static const struct dev_pm_ops mxs_mmc_pm_ops = {
- .suspend = mxs_mmc_suspend,
- .resume = mxs_mmc_resume,
-};
#endif
+static SIMPLE_DEV_PM_OPS(mxs_mmc_pm_ops, mxs_mmc_suspend, mxs_mmc_resume);
+
static struct platform_driver mxs_mmc_driver = {
.probe = mxs_mmc_probe,
.remove = mxs_mmc_remove,
.id_table = mxs_ssp_ids,
.driver = {
.name = DRIVER_NAME,
-#ifdef CONFIG_PM
.pm = &mxs_mmc_pm_ops,
-#endif
.of_match_table = mxs_mmc_dt_ids,
},
};
#include <linux/regulator/consumer.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm_runtime.h>
-#include <linux/platform_data/mmc-omap.h>
+#include <linux/platform_data/hsmmc-omap.h>
/* OMAP HSMMC Host Controller Registers */
#define OMAP_HSMMC_SYSSTATUS 0x0014
* omap.c controller driver. Luckily this is not currently done on any known
* omap_hsmmc.c device.
*/
-#define mmc_slot(host) (host->pdata->slots[host->slot_id])
+#define mmc_pdata(host) host->pdata
/*
* MMC Host controller read/write API's
int use_dma, dma_ch;
struct dma_chan *tx_chan;
struct dma_chan *rx_chan;
- int slot_id;
int response_busy;
int context_loss;
int protect_card;
#define HSMMC_SDIO_IRQ_ENABLED (1 << 1) /* SDIO irq enabled */
#define HSMMC_WAKE_IRQ_ENABLED (1 << 2)
struct omap_hsmmc_next next_data;
- struct omap_mmc_platform_data *pdata;
+ struct omap_hsmmc_platform_data *pdata;
+
+ /* To handle board related suspend/resume functionality for MMC */
+ int (*suspend)(struct device *dev);
+ int (*resume)(struct device *dev);
+
+ /* return MMC cover switch state, can be NULL if not supported.
+ *
+ * possible return values:
+ * 0 - closed
+ * 1 - open
+ */
+ int (*get_cover_state)(struct device *dev);
+
+ /* Card detection IRQs */
+ int card_detect_irq;
+
+ int (*card_detect)(struct device *dev);
+ int (*get_ro)(struct device *dev);
+
};
struct omap_mmc_of_data {
static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host);
-static int omap_hsmmc_card_detect(struct device *dev, int slot)
+static int omap_hsmmc_card_detect(struct device *dev)
{
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
- struct omap_mmc_platform_data *mmc = host->pdata;
+ struct omap_hsmmc_platform_data *mmc = host->pdata;
/* NOTE: assumes card detect signal is active-low */
- return !gpio_get_value_cansleep(mmc->slots[0].switch_pin);
+ return !gpio_get_value_cansleep(mmc->switch_pin);
}
-static int omap_hsmmc_get_wp(struct device *dev, int slot)
+static int omap_hsmmc_get_wp(struct device *dev)
{
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
- struct omap_mmc_platform_data *mmc = host->pdata;
+ struct omap_hsmmc_platform_data *mmc = host->pdata;
/* NOTE: assumes write protect signal is active-high */
- return gpio_get_value_cansleep(mmc->slots[0].gpio_wp);
+ return gpio_get_value_cansleep(mmc->gpio_wp);
}
-static int omap_hsmmc_get_cover_state(struct device *dev, int slot)
+static int omap_hsmmc_get_cover_state(struct device *dev)
{
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
- struct omap_mmc_platform_data *mmc = host->pdata;
+ struct omap_hsmmc_platform_data *mmc = host->pdata;
/* NOTE: assumes card detect signal is active-low */
- return !gpio_get_value_cansleep(mmc->slots[0].switch_pin);
+ return !gpio_get_value_cansleep(mmc->switch_pin);
}
#ifdef CONFIG_PM
-static int omap_hsmmc_suspend_cdirq(struct device *dev, int slot)
+static int omap_hsmmc_suspend_cdirq(struct device *dev)
{
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
- struct omap_mmc_platform_data *mmc = host->pdata;
- disable_irq(mmc->slots[0].card_detect_irq);
+ disable_irq(host->card_detect_irq);
return 0;
}
-static int omap_hsmmc_resume_cdirq(struct device *dev, int slot)
+static int omap_hsmmc_resume_cdirq(struct device *dev)
{
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
- struct omap_mmc_platform_data *mmc = host->pdata;
- enable_irq(mmc->slots[0].card_detect_irq);
+ enable_irq(host->card_detect_irq);
return 0;
}
#ifdef CONFIG_REGULATOR
-static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on,
- int vdd)
+static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd)
{
struct omap_hsmmc_host *host =
platform_get_drvdata(to_platform_device(dev));
if (!host->vcc)
return 0;
- if (mmc_slot(host).before_set_reg)
- mmc_slot(host).before_set_reg(dev, slot, power_on, vdd);
+ if (mmc_pdata(host)->before_set_reg)
+ mmc_pdata(host)->before_set_reg(dev, power_on, vdd);
if (host->pbias) {
if (host->pbias_enabled == 1) {
}
}
- if (mmc_slot(host).after_set_reg)
- mmc_slot(host).after_set_reg(dev, slot, power_on, vdd);
+ if (mmc_pdata(host)->after_set_reg)
+ mmc_pdata(host)->after_set_reg(dev, power_on, vdd);
error_set_power:
return ret;
} else {
host->vcc = reg;
ocr_value = mmc_regulator_get_ocrmask(reg);
- if (!mmc_slot(host).ocr_mask) {
- mmc_slot(host).ocr_mask = ocr_value;
+ if (!mmc_pdata(host)->ocr_mask) {
+ mmc_pdata(host)->ocr_mask = ocr_value;
} else {
- if (!(mmc_slot(host).ocr_mask & ocr_value)) {
+ if (!(mmc_pdata(host)->ocr_mask & ocr_value)) {
dev_err(host->dev, "ocrmask %x is not supported\n",
- mmc_slot(host).ocr_mask);
- mmc_slot(host).ocr_mask = 0;
+ mmc_pdata(host)->ocr_mask);
+ mmc_pdata(host)->ocr_mask = 0;
return -EINVAL;
}
}
}
- mmc_slot(host).set_power = omap_hsmmc_set_power;
+ mmc_pdata(host)->set_power = omap_hsmmc_set_power;
/* Allow an aux regulator */
reg = devm_regulator_get_optional(host->dev, "vmmc_aux");
host->pbias = IS_ERR(reg) ? NULL : reg;
/* For eMMC do not power off when not in sleep state */
- if (mmc_slot(host).no_regulator_off_init)
+ if (mmc_pdata(host)->no_regulator_off_init)
return 0;
/*
* To disable boot_on regulator, enable regulator
*/
if ((host->vcc && regulator_is_enabled(host->vcc) > 0) ||
(host->vcc_aux && regulator_is_enabled(host->vcc_aux))) {
- int vdd = ffs(mmc_slot(host).ocr_mask) - 1;
+ int vdd = ffs(mmc_pdata(host)->ocr_mask) - 1;
- mmc_slot(host).set_power(host->dev, host->slot_id, 1, vdd);
- mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
+ mmc_pdata(host)->set_power(host->dev, 1, vdd);
+ mmc_pdata(host)->set_power(host->dev, 0, 0);
}
return 0;
static void omap_hsmmc_reg_put(struct omap_hsmmc_host *host)
{
- mmc_slot(host).set_power = NULL;
+ mmc_pdata(host)->set_power = NULL;
}
static inline int omap_hsmmc_have_reg(void)
#endif
-static int omap_hsmmc_gpio_init(struct omap_mmc_platform_data *pdata)
+static int omap_hsmmc_gpio_init(struct omap_hsmmc_host *host,
+ struct omap_hsmmc_platform_data *pdata)
{
int ret;
- if (gpio_is_valid(pdata->slots[0].switch_pin)) {
- if (pdata->slots[0].cover)
- pdata->slots[0].get_cover_state =
- omap_hsmmc_get_cover_state;
+ if (gpio_is_valid(pdata->switch_pin)) {
+ if (pdata->cover)
+ host->get_cover_state =
+ omap_hsmmc_get_cover_state;
else
- pdata->slots[0].card_detect = omap_hsmmc_card_detect;
- pdata->slots[0].card_detect_irq =
- gpio_to_irq(pdata->slots[0].switch_pin);
- ret = gpio_request(pdata->slots[0].switch_pin, "mmc_cd");
+ host->card_detect = omap_hsmmc_card_detect;
+ host->card_detect_irq =
+ gpio_to_irq(pdata->switch_pin);
+ ret = gpio_request(pdata->switch_pin, "mmc_cd");
if (ret)
return ret;
- ret = gpio_direction_input(pdata->slots[0].switch_pin);
+ ret = gpio_direction_input(pdata->switch_pin);
if (ret)
goto err_free_sp;
- } else
- pdata->slots[0].switch_pin = -EINVAL;
+ } else {
+ pdata->switch_pin = -EINVAL;
+ }
- if (gpio_is_valid(pdata->slots[0].gpio_wp)) {
- pdata->slots[0].get_ro = omap_hsmmc_get_wp;
- ret = gpio_request(pdata->slots[0].gpio_wp, "mmc_wp");
+ if (gpio_is_valid(pdata->gpio_wp)) {
+ host->get_ro = omap_hsmmc_get_wp;
+ ret = gpio_request(pdata->gpio_wp, "mmc_wp");
if (ret)
goto err_free_cd;
- ret = gpio_direction_input(pdata->slots[0].gpio_wp);
+ ret = gpio_direction_input(pdata->gpio_wp);
if (ret)
goto err_free_wp;
- } else
- pdata->slots[0].gpio_wp = -EINVAL;
+ } else {
+ pdata->gpio_wp = -EINVAL;
+ }
return 0;
err_free_wp:
- gpio_free(pdata->slots[0].gpio_wp);
+ gpio_free(pdata->gpio_wp);
err_free_cd:
- if (gpio_is_valid(pdata->slots[0].switch_pin))
+ if (gpio_is_valid(pdata->switch_pin))
err_free_sp:
- gpio_free(pdata->slots[0].switch_pin);
+ gpio_free(pdata->switch_pin);
return ret;
}
-static void omap_hsmmc_gpio_free(struct omap_mmc_platform_data *pdata)
+static void omap_hsmmc_gpio_free(struct omap_hsmmc_host *host,
+ struct omap_hsmmc_platform_data *pdata)
{
- if (gpio_is_valid(pdata->slots[0].gpio_wp))
- gpio_free(pdata->slots[0].gpio_wp);
- if (gpio_is_valid(pdata->slots[0].switch_pin))
- gpio_free(pdata->slots[0].switch_pin);
+ if (gpio_is_valid(pdata->gpio_wp))
+ gpio_free(pdata->gpio_wp);
+ if (gpio_is_valid(pdata->switch_pin))
+ gpio_free(pdata->switch_pin);
}
/*
* in capabilities register
* - MMC/SD clock coming out of controller > 25MHz
*/
- if ((mmc_slot(host).features & HSMMC_HAS_HSPE_SUPPORT) &&
+ if ((mmc_pdata(host)->features & HSMMC_HAS_HSPE_SUPPORT) &&
(ios->timing != MMC_TIMING_MMC_DDR52) &&
+ (ios->timing != MMC_TIMING_UHS_DDR50) &&
((OMAP_HSMMC_READ(host->base, CAPA) & HSS) == HSS)) {
regval = OMAP_HSMMC_READ(host->base, HCTL);
if (clkdiv && (clk_get_rate(host->fclk)/clkdiv) > 25000000)
u32 con;
con = OMAP_HSMMC_READ(host->base, CON);
- if (ios->timing == MMC_TIMING_MMC_DDR52)
+ if (ios->timing == MMC_TIMING_MMC_DDR52 ||
+ ios->timing == MMC_TIMING_UHS_DDR50)
con |= DDR; /* configure in DDR mode */
else
con &= ~DDR;
{
int r = 1;
- if (mmc_slot(host).get_cover_state)
- r = mmc_slot(host).get_cover_state(host->dev, host->slot_id);
+ if (host->get_cover_state)
+ r = host->get_cover_state(host->dev);
return r;
}
struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev);
struct omap_hsmmc_host *host = mmc_priv(mmc);
- return sprintf(buf, "%s\n", mmc_slot(host).name);
+ return sprintf(buf, "%s\n", mmc_pdata(host)->name);
}
static DEVICE_ATTR(slot_name, S_IRUGO, omap_hsmmc_show_slot_name, NULL);
* OMAP4 ES2 and greater has an updated reset logic.
* Monitor a 0->1 transition first
*/
- if (mmc_slot(host).features & HSMMC_HAS_UPDATED_RESET) {
+ if (mmc_pdata(host)->features & HSMMC_HAS_UPDATED_RESET) {
while ((!(OMAP_HSMMC_READ(host->base, SYSCTL) & bit))
&& (i++ < limit))
udelay(1);
clk_disable_unprepare(host->dbclk);
/* Turn the power off */
- ret = mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
+ ret = mmc_pdata(host)->set_power(host->dev, 0, 0);
/* Turn the power ON with given VDD 1.8 or 3.0v */
if (!ret)
- ret = mmc_slot(host).set_power(host->dev, host->slot_id, 1,
- vdd);
+ ret = mmc_pdata(host)->set_power(host->dev, 1, vdd);
pm_runtime_get_sync(host->dev);
if (host->dbclk)
clk_prepare_enable(host->dbclk);
/* Protect the card while the cover is open */
static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host)
{
- if (!mmc_slot(host).get_cover_state)
+ if (!host->get_cover_state)
return;
host->reqs_blocked = 0;
- if (mmc_slot(host).get_cover_state(host->dev, host->slot_id)) {
+ if (host->get_cover_state(host->dev)) {
if (host->protect_card) {
dev_info(host->dev, "%s: cover is closed, "
"card is now accessible\n",
static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id)
{
struct omap_hsmmc_host *host = dev_id;
- struct omap_mmc_slot_data *slot = &mmc_slot(host);
int carddetect;
sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");
- if (slot->card_detect)
- carddetect = slot->card_detect(host->dev, host->slot_id);
+ if (host->card_detect)
+ carddetect = host->card_detect(host->dev);
else {
omap_hsmmc_protect_card(host);
carddetect = -ENOSYS;
if (ios->power_mode != host->power_mode) {
switch (ios->power_mode) {
case MMC_POWER_OFF:
- mmc_slot(host).set_power(host->dev, host->slot_id,
- 0, 0);
+ mmc_pdata(host)->set_power(host->dev, 0, 0);
break;
case MMC_POWER_UP:
- mmc_slot(host).set_power(host->dev, host->slot_id,
- 1, ios->vdd);
+ mmc_pdata(host)->set_power(host->dev, 1, ios->vdd);
break;
case MMC_POWER_ON:
do_send_init_stream = 1;
{
struct omap_hsmmc_host *host = mmc_priv(mmc);
- if (!mmc_slot(host).card_detect)
+ if (!host->card_detect)
return -ENOSYS;
- return mmc_slot(host).card_detect(host->dev, host->slot_id);
+ return host->card_detect(host->dev);
}
static int omap_hsmmc_get_ro(struct mmc_host *mmc)
{
struct omap_hsmmc_host *host = mmc_priv(mmc);
- if (!mmc_slot(host).get_ro)
+ if (!host->get_ro)
return -ENOSYS;
- return mmc_slot(host).get_ro(host->dev, 0);
+ return host->get_ro(host->dev);
}
static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_card *card)
{
struct omap_hsmmc_host *host = mmc_priv(mmc);
- if (mmc_slot(host).init_card)
- mmc_slot(host).init_card(card);
+ if (mmc_pdata(host)->init_card)
+ mmc_pdata(host)->init_card(card);
}
static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
};
MODULE_DEVICE_TABLE(of, omap_mmc_of_match);
-static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
+static struct omap_hsmmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
{
- struct omap_mmc_platform_data *pdata;
+ struct omap_hsmmc_platform_data *pdata;
struct device_node *np = dev->of_node;
u32 bus_width, max_freq;
int cd_gpio, wp_gpio;
if (of_find_property(np, "ti,dual-volt", NULL))
pdata->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT;
- /* This driver only supports 1 slot */
- pdata->nr_slots = 1;
- pdata->slots[0].switch_pin = cd_gpio;
- pdata->slots[0].gpio_wp = wp_gpio;
+ pdata->switch_pin = cd_gpio;
+ pdata->gpio_wp = wp_gpio;
if (of_find_property(np, "ti,non-removable", NULL)) {
- pdata->slots[0].nonremovable = true;
- pdata->slots[0].no_regulator_off_init = true;
+ pdata->nonremovable = true;
+ pdata->no_regulator_off_init = true;
}
of_property_read_u32(np, "bus-width", &bus_width);
if (bus_width == 4)
- pdata->slots[0].caps |= MMC_CAP_4_BIT_DATA;
+ pdata->caps |= MMC_CAP_4_BIT_DATA;
else if (bus_width == 8)
- pdata->slots[0].caps |= MMC_CAP_8_BIT_DATA;
+ pdata->caps |= MMC_CAP_8_BIT_DATA;
if (of_find_property(np, "ti,needs-special-reset", NULL))
- pdata->slots[0].features |= HSMMC_HAS_UPDATED_RESET;
+ pdata->features |= HSMMC_HAS_UPDATED_RESET;
if (!of_property_read_u32(np, "max-frequency", &max_freq))
pdata->max_freq = max_freq;
if (of_find_property(np, "ti,needs-special-hs-handling", NULL))
- pdata->slots[0].features |= HSMMC_HAS_HSPE_SUPPORT;
+ pdata->features |= HSMMC_HAS_HSPE_SUPPORT;
if (of_find_property(np, "keep-power-in-suspend", NULL))
- pdata->slots[0].pm_caps |= MMC_PM_KEEP_POWER;
+ pdata->pm_caps |= MMC_PM_KEEP_POWER;
if (of_find_property(np, "enable-sdio-wakeup", NULL))
- pdata->slots[0].pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
+ pdata->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
return pdata;
}
#else
-static inline struct omap_mmc_platform_data
+static inline struct omap_hsmmc_platform_data
*of_get_hsmmc_pdata(struct device *dev)
{
return ERR_PTR(-EINVAL);
static int omap_hsmmc_probe(struct platform_device *pdev)
{
- struct omap_mmc_platform_data *pdata = pdev->dev.platform_data;
+ struct omap_hsmmc_platform_data *pdata = pdev->dev.platform_data;
struct mmc_host *mmc;
struct omap_hsmmc_host *host = NULL;
struct resource *res;
return -ENXIO;
}
- if (pdata->nr_slots == 0) {
- dev_err(&pdev->dev, "No Slots\n");
- return -ENXIO;
- }
-
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
if (res == NULL || irq < 0)
if (IS_ERR(base))
return PTR_ERR(base);
- ret = omap_hsmmc_gpio_init(pdata);
- if (ret)
- goto err;
-
mmc = mmc_alloc_host(sizeof(struct omap_hsmmc_host), &pdev->dev);
if (!mmc) {
ret = -ENOMEM;
- goto err_alloc;
+ goto err;
}
host = mmc_priv(mmc);
host->use_dma = 1;
host->dma_ch = -1;
host->irq = irq;
- host->slot_id = 0;
host->mapbase = res->start + pdata->reg_offset;
host->base = base + pdata->reg_offset;
host->power_mode = MMC_POWER_OFF;
host->next_data.cookie = 1;
host->pbias_enabled = 0;
+ ret = omap_hsmmc_gpio_init(host, pdata);
+ if (ret)
+ goto err_gpio;
+
platform_set_drvdata(pdev, host);
if (pdev->dev.of_node)
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE;
- mmc->caps |= mmc_slot(host).caps;
+ mmc->caps |= mmc_pdata(host)->caps;
if (mmc->caps & MMC_CAP_8_BIT_DATA)
mmc->caps |= MMC_CAP_4_BIT_DATA;
- if (mmc_slot(host).nonremovable)
+ if (mmc_pdata(host)->nonremovable)
mmc->caps |= MMC_CAP_NONREMOVABLE;
- mmc->pm_caps = mmc_slot(host).pm_caps;
+ mmc->pm_caps = mmc_pdata(host)->pm_caps;
omap_hsmmc_conf_bus_power(host);
goto err_irq;
}
- if (pdata->init != NULL) {
- if (pdata->init(&pdev->dev) != 0) {
- dev_err(mmc_dev(host->mmc),
- "Unable to configure MMC IRQs\n");
- goto err_irq;
- }
- }
-
- if (omap_hsmmc_have_reg() && !mmc_slot(host).set_power) {
+ if (omap_hsmmc_have_reg() && !mmc_pdata(host)->set_power) {
ret = omap_hsmmc_reg_get(host);
if (ret)
- goto err_reg;
+ goto err_irq;
host->use_reg = 1;
}
- mmc->ocr_avail = mmc_slot(host).ocr_mask;
+ mmc->ocr_avail = mmc_pdata(host)->ocr_mask;
/* Request IRQ for card detect */
- if ((mmc_slot(host).card_detect_irq)) {
+ if (host->card_detect_irq) {
ret = devm_request_threaded_irq(&pdev->dev,
- mmc_slot(host).card_detect_irq,
+ host->card_detect_irq,
NULL, omap_hsmmc_detect,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
mmc_hostname(mmc), host);
"Unable to grab MMC CD IRQ\n");
goto err_irq_cd;
}
- pdata->suspend = omap_hsmmc_suspend_cdirq;
- pdata->resume = omap_hsmmc_resume_cdirq;
+ host->suspend = omap_hsmmc_suspend_cdirq;
+ host->resume = omap_hsmmc_resume_cdirq;
}
omap_hsmmc_disable_irq(host);
mmc_add_host(mmc);
- if (mmc_slot(host).name != NULL) {
+ if (mmc_pdata(host)->name != NULL) {
ret = device_create_file(&mmc->class_dev, &dev_attr_slot_name);
if (ret < 0)
goto err_slot_name;
}
- if (mmc_slot(host).card_detect_irq && mmc_slot(host).get_cover_state) {
+ if (host->card_detect_irq && host->get_cover_state) {
ret = device_create_file(&mmc->class_dev,
&dev_attr_cover_switch);
if (ret < 0)
err_irq_cd:
if (host->use_reg)
omap_hsmmc_reg_put(host);
-err_reg:
- if (host->pdata->cleanup)
- host->pdata->cleanup(&pdev->dev);
err_irq:
if (host->tx_chan)
dma_release_channel(host->tx_chan);
if (host->dbclk)
clk_disable_unprepare(host->dbclk);
err1:
+ omap_hsmmc_gpio_free(host, pdata);
+err_gpio:
mmc_free_host(mmc);
-err_alloc:
- omap_hsmmc_gpio_free(pdata);
err:
return ret;
}
mmc_remove_host(host->mmc);
if (host->use_reg)
omap_hsmmc_reg_put(host);
- if (host->pdata->cleanup)
- host->pdata->cleanup(&pdev->dev);
if (host->tx_chan)
dma_release_channel(host->tx_chan);
if (host->dbclk)
clk_disable_unprepare(host->dbclk);
- omap_hsmmc_gpio_free(host->pdata);
+ omap_hsmmc_gpio_free(host, host->pdata);
mmc_free_host(host->mmc);
return 0;
{
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
- if (host->pdata->suspend)
- return host->pdata->suspend(dev, host->slot_id);
+ if (host->suspend)
+ return host->suspend(dev);
return 0;
}
{
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
- if (host->pdata->resume)
- host->pdata->resume(dev, host->slot_id);
+ if (host->resume)
+ host->resume(dev);
}
const struct sdhci_acpi_slot *slot;
struct platform_device *pdev;
bool use_runtime_pm;
+ bool dma_setup;
};
static inline bool sdhci_acpi_flag(struct sdhci_acpi_host *c, unsigned int flag)
static int sdhci_acpi_enable_dma(struct sdhci_host *host)
{
- return 0;
+ struct sdhci_acpi_host *c = sdhci_priv(host);
+ struct device *dev = &c->pdev->dev;
+ int err = -1;
+
+ if (c->dma_setup)
+ return 0;
+
+ if (host->flags & SDHCI_USE_64_BIT_DMA) {
+ if (host->quirks2 & SDHCI_QUIRK2_BROKEN_64_BIT_DMA) {
+ host->flags &= ~SDHCI_USE_64_BIT_DMA;
+ } else {
+ err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
+ if (err)
+ dev_warn(dev, "Failed to set 64-bit DMA mask\n");
+ }
+ }
+
+ if (err)
+ err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+
+ c->dma_setup = !err;
+
+ return err;
}
static void sdhci_acpi_int_hw_reset(struct sdhci_host *host)
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = {
.chip = &sdhci_acpi_chip_int,
.caps = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
- MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR,
+ MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR |
+ MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_WAIT_WHILE_BUSY,
.caps2 = MMC_CAP2_HC_ERASE_SZ,
.flags = SDHCI_ACPI_RUNTIME_PM,
+ .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_STOP_WITH_TC,
.probe_slot = sdhci_acpi_emmc_probe_slot,
};
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
- .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION,
+ .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
+ SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON,
- .caps = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD,
+ .caps = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD |
+ MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_WAIT_WHILE_BUSY,
.flags = SDHCI_ACPI_RUNTIME_PM,
.pm_caps = MMC_PM_KEEP_POWER,
.probe_slot = sdhci_acpi_sdio_probe_slot,
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = {
.flags = SDHCI_ACPI_SD_CD | SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL |
SDHCI_ACPI_RUNTIME_PM,
+ .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON |
SDHCI_QUIRK2_STOP_WITH_TC,
+ .caps = MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_WAIT_WHILE_BUSY,
.probe_slot = sdhci_acpi_sd_probe_slot,
};
goto err_free;
}
- if (!dev->dma_mask) {
- u64 dma_mask;
-
- if (sdhci_readl(host, SDHCI_CAPABILITIES) & SDHCI_CAN_64BIT) {
- /* 64-bit DMA is not supported at present */
- dma_mask = DMA_BIT_MASK(32);
- } else {
- dma_mask = DMA_BIT_MASK(32);
- }
-
- err = dma_coerce_mask_and_coherent(dev, dma_mask);
- if (err)
- goto err_free;
- }
-
if (c->slot) {
if (c->slot->probe_slot) {
err = c->slot->probe_slot(pdev, hid, uid);
/* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
#define ESDHC_TUNING_START_TAP 0x1
-#define ESDHC_TUNING_BLOCK_PATTERN_LEN 64
-
/* pinctrl state */
#define ESDHC_PINCTRL_STATE_100MHZ "state_100mhz"
#define ESDHC_PINCTRL_STATE_200MHZ "state_200mhz"
/* FIXME: delay a bit for card to be ready for next tuning due to errors */
mdelay(1);
- /* This is balanced by the runtime put in sdhci_tasklet_finish */
- pm_runtime_get_sync(host->mmc->parent);
reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
ESDHC_MIX_CTRL_FBCLK_SEL;
val, readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS));
}
-static void esdhc_request_done(struct mmc_request *mrq)
-{
- complete(&mrq->completion);
-}
-
-static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode,
- struct scatterlist *sg)
-{
- struct mmc_command cmd = {0};
- struct mmc_request mrq = {NULL};
- struct mmc_data data = {0};
-
- cmd.opcode = opcode;
- cmd.arg = 0;
- cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
-
- data.blksz = ESDHC_TUNING_BLOCK_PATTERN_LEN;
- data.blocks = 1;
- data.flags = MMC_DATA_READ;
- data.sg = sg;
- data.sg_len = 1;
-
- mrq.cmd = &cmd;
- mrq.cmd->mrq = &mrq;
- mrq.data = &data;
- mrq.data->mrq = &mrq;
- mrq.cmd->data = mrq.data;
-
- mrq.done = esdhc_request_done;
- init_completion(&(mrq.completion));
-
- spin_lock_irq(&host->lock);
- host->mrq = &mrq;
-
- sdhci_send_command(host, mrq.cmd);
-
- spin_unlock_irq(&host->lock);
-
- wait_for_completion(&mrq.completion);
-
- if (cmd.error)
- return cmd.error;
- if (data.error)
- return data.error;
-
- return 0;
-}
-
static void esdhc_post_tuning(struct sdhci_host *host)
{
u32 reg;
static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
{
- struct scatterlist sg;
- char *tuning_pattern;
int min, max, avg, ret;
- tuning_pattern = kmalloc(ESDHC_TUNING_BLOCK_PATTERN_LEN, GFP_KERNEL);
- if (!tuning_pattern)
- return -ENOMEM;
-
- sg_init_one(&sg, tuning_pattern, ESDHC_TUNING_BLOCK_PATTERN_LEN);
-
/* find the mininum delay first which can pass tuning */
min = ESDHC_TUNE_CTRL_MIN;
while (min < ESDHC_TUNE_CTRL_MAX) {
esdhc_prepare_tuning(host, min);
- if (!esdhc_send_tuning_cmd(host, opcode, &sg))
+ if (!mmc_send_tuning(host->mmc))
break;
min += ESDHC_TUNE_CTRL_STEP;
}
max = min + ESDHC_TUNE_CTRL_STEP;
while (max < ESDHC_TUNE_CTRL_MAX) {
esdhc_prepare_tuning(host, max);
- if (esdhc_send_tuning_cmd(host, opcode, &sg)) {
+ if (mmc_send_tuning(host->mmc)) {
max -= ESDHC_TUNE_CTRL_STEP;
break;
}
/* use average delay to get the best timing */
avg = (min + max) / 2;
esdhc_prepare_tuning(host, avg);
- ret = esdhc_send_tuning_cmd(host, opcode, &sg);
+ ret = mmc_send_tuning(host->mmc);
esdhc_post_tuning(host);
- kfree(tuning_pattern);
-
dev_dbg(mmc_dev(host->mmc), "tunning %s at 0x%x ret %d\n",
ret ? "failed" : "passed", avg, ret);
imx_data->pins_default = pinctrl_lookup_state(imx_data->pinctrl,
PINCTRL_STATE_DEFAULT);
- if (IS_ERR(imx_data->pins_default)) {
- err = PTR_ERR(imx_data->pins_default);
- dev_err(mmc_dev(host->mmc), "could not get default state\n");
- goto disable_clk;
- }
+ if (IS_ERR(imx_data->pins_default))
+ dev_warn(mmc_dev(host->mmc), "could not get default state\n");
host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
}
/* sdr50 and sdr104 needs work on 1.8v signal voltage */
- if ((boarddata->support_vsel) && esdhc_is_usdhc(imx_data)) {
+ if ((boarddata->support_vsel) && esdhc_is_usdhc(imx_data) &&
+ !IS_ERR(imx_data->pins_default)) {
imx_data->pins_100mhz = pinctrl_lookup_state(imx_data->pinctrl,
ESDHC_PINCTRL_STATE_100MHZ);
imx_data->pins_200mhz = pinctrl_lookup_state(imx_data->pinctrl,
static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
{
int tuning_seq_cnt = 3;
- u8 phase, *data_buf, tuned_phases[16], tuned_phase_cnt = 0;
- const u8 *tuning_block_pattern = tuning_blk_pattern_4bit;
- int size = sizeof(tuning_blk_pattern_4bit);
+ u8 phase, tuned_phases[16], tuned_phase_cnt = 0;
int rc;
struct mmc_host *mmc = host->mmc;
struct mmc_ios ios = host->mmc->ios;
(ios.timing == MMC_TIMING_UHS_SDR104)))
return 0;
- if ((opcode == MMC_SEND_TUNING_BLOCK_HS200) &&
- (mmc->ios.bus_width == MMC_BUS_WIDTH_8)) {
- tuning_block_pattern = tuning_blk_pattern_8bit;
- size = sizeof(tuning_blk_pattern_8bit);
- }
-
- data_buf = kmalloc(size, GFP_KERNEL);
- if (!data_buf)
- return -ENOMEM;
-
retry:
/* First of all reset the tuning block */
rc = msm_init_cm_dll(host);
if (rc)
- goto out;
+ return rc;
phase = 0;
do {
- struct mmc_command cmd = { 0 };
- struct mmc_data data = { 0 };
- struct mmc_request mrq = {
- .cmd = &cmd,
- .data = &data
- };
- struct scatterlist sg;
-
/* Set the phase in delay line hw block */
rc = msm_config_cm_dll_phase(host, phase);
if (rc)
- goto out;
+ return rc;
- cmd.opcode = opcode;
- cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
-
- data.blksz = size;
- data.blocks = 1;
- data.flags = MMC_DATA_READ;
- data.timeout_ns = NSEC_PER_SEC; /* 1 second */
-
- data.sg = &sg;
- data.sg_len = 1;
- sg_init_one(&sg, data_buf, size);
- memset(data_buf, 0, size);
- mmc_wait_for_req(mmc, &mrq);
-
- if (!cmd.error && !data.error &&
- !memcmp(data_buf, tuning_block_pattern, size)) {
+ rc = mmc_send_tuning(mmc);
+ if (!rc) {
/* Tuning is successful at this tuning point */
tuned_phases[tuned_phase_cnt++] = phase;
dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n",
rc = msm_find_most_appropriate_phase(host, tuned_phases,
tuned_phase_cnt);
if (rc < 0)
- goto out;
+ return rc;
else
phase = rc;
*/
rc = msm_config_cm_dll_phase(host, phase);
if (rc)
- goto out;
+ return rc;
dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n",
mmc_hostname(mmc), phase);
} else {
rc = -EIO;
}
-out:
- kfree(data_buf);
return rc;
}
host = sdhci_pltfm_init(pdev, &sdhci_arasan_pdata, 0);
if (IS_ERR(host)) {
ret = PTR_ERR(host);
- dev_err(&pdev->dev, "platform init failed (%u)\n", ret);
goto clk_disable_all;
}
pltfm_host->clk = clk_xin;
ret = sdhci_add_host(host);
- if (ret) {
- dev_err(&pdev->dev, "platform register failed (%u)\n", ret);
+ if (ret)
goto err_pltfm_free;
- }
return 0;
return;
scratch_32 &= ~((1 << 21) | (1 << 30));
- /* Set RTD3 function disabled */
- scratch_32 |= ((1 << 29) | (1 << 28));
pci_write_config_dword(chip->pdev, O2_SD_FUNC_REG3, scratch_32);
/* Set L1 Entrance Timer */
static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
{
slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
- MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR;
+ MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR |
+ MMC_CAP_BUS_WIDTH_TEST |
+ MMC_CAP_WAIT_WHILE_BUSY;
slot->host->mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ;
slot->hw_reset = sdhci_pci_int_hw_reset;
if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BSW_EMMC)
static int byt_sdio_probe_slot(struct sdhci_pci_slot *slot)
{
- slot->host->mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_NONREMOVABLE;
+ slot->host->mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_NONREMOVABLE |
+ MMC_CAP_BUS_WIDTH_TEST |
+ MMC_CAP_WAIT_WHILE_BUSY;
return 0;
}
static int byt_sd_probe_slot(struct sdhci_pci_slot *slot)
{
+ slot->host->mmc->caps |= MMC_CAP_BUS_WIDTH_TEST |
+ MMC_CAP_WAIT_WHILE_BUSY;
slot->cd_con_id = NULL;
slot->cd_idx = 0;
slot->cd_override_level = true;
static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = {
.allow_runtime_pm = true,
.probe_slot = byt_emmc_probe_slot,
+ .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_STOP_WITH_TC,
};
static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = {
+ .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON |
SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
.allow_runtime_pm = true,
};
static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
+ .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON |
SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_STOP_WITH_TC,
.probe_slot = rtsx_probe_slot,
};
+static int amd_probe(struct sdhci_pci_chip *chip)
+{
+ struct pci_dev *smbus_dev;
+
+ smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
+ PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, NULL);
+
+ if (smbus_dev && (smbus_dev->revision < 0x51)) {
+ chip->quirks2 |= SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD;
+ chip->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
+ }
+
+ return 0;
+}
+
+static const struct sdhci_pci_fixes sdhci_amd = {
+ .probe = amd_probe,
+};
+
static const struct pci_device_id pci_ids[] = {
{
.vendor = PCI_VENDOR_ID_RICOH,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_o2,
},
-
+ {
+ .vendor = PCI_VENDOR_ID_AMD,
+ .device = PCI_ANY_ID,
+ .class = PCI_CLASS_SYSTEM_SDHCI << 8,
+ .class_mask = 0xFFFF00,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)&sdhci_amd,
+ },
{ /* Generic SD host controller */
PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)
},
{
struct sdhci_pci_slot *slot;
struct pci_dev *pdev;
- int ret;
+ int ret = -1;
slot = sdhci_priv(host);
pdev = slot->chip->pdev;
"doesn't fully claim to support it.\n");
}
- ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (host->flags & SDHCI_USE_64_BIT_DMA) {
+ if (host->quirks2 & SDHCI_QUIRK2_BROKEN_64_BIT_DMA) {
+ host->flags &= ~SDHCI_USE_64_BIT_DMA;
+ } else {
+ ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
+ if (ret)
+ dev_warn(&pdev->dev, "Failed to set 64-bit DMA mask\n");
+ }
+ }
+ if (ret)
+ ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
if (ret)
return ret;
struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
struct device *dev = &pdev->dev;
struct sdhci_host *host = NULL;
- struct sdhci_pxa *pxa = NULL;
const struct of_device_id *match;
int ret;
struct clk *clk;
- pxa = kzalloc(sizeof(struct sdhci_pxa), GFP_KERNEL);
- if (!pxa)
- return -ENOMEM;
-
host = sdhci_pltfm_init(pdev, NULL, 0);
- if (IS_ERR(host)) {
- kfree(pxa);
+ if (IS_ERR(host))
return PTR_ERR(host);
- }
+
pltfm_host = sdhci_priv(host);
- pltfm_host->priv = pxa;
+ pltfm_host->priv = NULL;
clk = clk_get(dev, "PXA-SDHCLK");
if (IS_ERR(clk)) {
clk_put(clk);
err_clk_get:
sdhci_pltfm_free(pdev);
- kfree(pxa);
return ret;
}
{
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct sdhci_pxa *pxa = pltfm_host->priv;
sdhci_remove_host(host, 1);
clk_disable_unprepare(pltfm_host->clk);
clk_put(pltfm_host->clk);
sdhci_pltfm_free(pdev);
- kfree(pxa);
return 0;
}
#define SDCE_MISC_INT (1<<2)
#define SDCE_MISC_INT_EN (1<<1)
+struct sdhci_pxa {
+ struct clk *clk_core;
+ struct clk *clk_io;
+ u8 power_mode;
+};
+
/*
* These registers are relative to the second register region, for the
* MBus bridge.
case MMC_TIMING_UHS_SDR104:
ctrl_2 |= SDHCI_CTRL_UHS_SDR104 | SDHCI_CTRL_VDD_180;
break;
+ case MMC_TIMING_MMC_DDR52:
case MMC_TIMING_UHS_DDR50:
ctrl_2 |= SDHCI_CTRL_UHS_DDR50 | SDHCI_CTRL_VDD_180;
break;
struct sdhci_host *host = NULL;
struct sdhci_pxa *pxa = NULL;
const struct of_device_id *match;
-
int ret;
- struct clk *clk;
pxa = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_pxa), GFP_KERNEL);
if (!pxa)
pltfm_host = sdhci_priv(host);
pltfm_host->priv = pxa;
- clk = devm_clk_get(dev, NULL);
- if (IS_ERR(clk)) {
+ pxa->clk_io = devm_clk_get(dev, "io");
+ if (IS_ERR(pxa->clk_io))
+ pxa->clk_io = devm_clk_get(dev, NULL);
+ if (IS_ERR(pxa->clk_io)) {
dev_err(dev, "failed to get io clock\n");
- ret = PTR_ERR(clk);
+ ret = PTR_ERR(pxa->clk_io);
goto err_clk_get;
}
- pltfm_host->clk = clk;
- clk_prepare_enable(clk);
+ pltfm_host->clk = pxa->clk_io;
+ clk_prepare_enable(pxa->clk_io);
+
+ pxa->clk_core = devm_clk_get(dev, "core");
+ if (!IS_ERR(pxa->clk_core))
+ clk_prepare_enable(pxa->clk_core);
/* enable 1/8V DDR capable */
host->mmc->caps |= MMC_CAP_1_8V_DDR;
pm_runtime_disable(&pdev->dev);
err_of_parse:
err_cd_req:
- clk_disable_unprepare(clk);
+ clk_disable_unprepare(pxa->clk_io);
+ if (!IS_ERR(pxa->clk_core))
+ clk_disable_unprepare(pxa->clk_core);
err_clk_get:
err_mbus_win:
sdhci_pltfm_free(pdev);
{
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_pxa *pxa = pltfm_host->priv;
pm_runtime_get_sync(&pdev->dev);
sdhci_remove_host(host, 1);
pm_runtime_disable(&pdev->dev);
- clk_disable_unprepare(pltfm_host->clk);
+ clk_disable_unprepare(pxa->clk_io);
+ if (!IS_ERR(pxa->clk_core))
+ clk_disable_unprepare(pxa->clk_core);
sdhci_pltfm_free(pdev);
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_pxa *pxa = pltfm_host->priv;
unsigned long flags;
- if (pltfm_host->clk) {
- spin_lock_irqsave(&host->lock, flags);
- host->runtime_suspended = true;
- spin_unlock_irqrestore(&host->lock, flags);
+ spin_lock_irqsave(&host->lock, flags);
+ host->runtime_suspended = true;
+ spin_unlock_irqrestore(&host->lock, flags);
- clk_disable_unprepare(pltfm_host->clk);
- }
+ clk_disable_unprepare(pxa->clk_io);
+ if (!IS_ERR(pxa->clk_core))
+ clk_disable_unprepare(pxa->clk_core);
return 0;
}
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_pxa *pxa = pltfm_host->priv;
unsigned long flags;
- if (pltfm_host->clk) {
- clk_prepare_enable(pltfm_host->clk);
+ clk_prepare_enable(pxa->clk_io);
+ if (!IS_ERR(pxa->clk_core))
+ clk_prepare_enable(pxa->clk_core);
- spin_lock_irqsave(&host->lock, flags);
- host->runtime_suspended = false;
- spin_unlock_irqrestore(&host->lock, flags);
- }
+ spin_lock_irqsave(&host->lock, flags);
+ host->runtime_suspended = false;
+ spin_unlock_irqrestore(&host->lock, flags);
return 0;
}
struct device *dev = &ourhost->pdev->dev;
unsigned long timeout;
u16 clk = 0;
+ int ret;
host->mmc->actual_clock = 0;
sdhci_s3c_set_clock(host, clock);
- clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock);
+ ret = clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock);
+ if (ret != 0) {
+ dev_err(dev, "%s: failed to set clock rate %uHz\n",
+ mmc_hostname(host->mmc), clock);
+ return;
+ }
clk = SDHCI_CLOCK_INT_EN;
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
#define MAX_TUNING_LOOP 40
-#define ADMA_SIZE ((128 * 2 + 1) * 4)
-
static unsigned int debug_quirks = 0;
static unsigned int debug_quirks2;
pr_debug(DRIVER_NAME ": Host ctl2: 0x%08x\n",
sdhci_readw(host, SDHCI_HOST_CONTROL2));
- if (host->flags & SDHCI_USE_ADMA)
- pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n",
- readl(host->ioaddr + SDHCI_ADMA_ERROR),
- readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
+ if (host->flags & SDHCI_USE_ADMA) {
+ if (host->flags & SDHCI_USE_64_BIT_DMA)
+ pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x%08x\n",
+ readl(host->ioaddr + SDHCI_ADMA_ERROR),
+ readl(host->ioaddr + SDHCI_ADMA_ADDRESS_HI),
+ readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
+ else
+ pr_debug(DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n",
+ readl(host->ioaddr + SDHCI_ADMA_ERROR),
+ readl(host->ioaddr + SDHCI_ADMA_ADDRESS));
+ }
pr_debug(DRIVER_NAME ": ===========================================\n");
}
local_irq_restore(*flags);
}
-static void sdhci_set_adma_desc(u8 *desc, u32 addr, int len, unsigned cmd)
+static void sdhci_adma_write_desc(struct sdhci_host *host, void *desc,
+ dma_addr_t addr, int len, unsigned cmd)
{
- __le32 *dataddr = (__le32 __force *)(desc + 4);
- __le16 *cmdlen = (__le16 __force *)desc;
+ struct sdhci_adma2_64_desc *dma_desc = desc;
+
+ /* 32-bit and 64-bit descriptors have these members in same position */
+ dma_desc->cmd = cpu_to_le16(cmd);
+ dma_desc->len = cpu_to_le16(len);
+ dma_desc->addr_lo = cpu_to_le32((u32)addr);
- /* SDHCI specification says ADMA descriptors should be 4 byte
- * aligned, so using 16 or 32bit operations should be safe. */
+ if (host->flags & SDHCI_USE_64_BIT_DMA)
+ dma_desc->addr_hi = cpu_to_le32((u64)addr >> 32);
+}
- cmdlen[0] = cpu_to_le16(cmd);
- cmdlen[1] = cpu_to_le16(len);
+static void sdhci_adma_mark_end(void *desc)
+{
+ struct sdhci_adma2_64_desc *dma_desc = desc;
- dataddr[0] = cpu_to_le32(addr);
+ /* 32-bit and 64-bit descriptors have 'cmd' in same position */
+ dma_desc->cmd |= cpu_to_le16(ADMA2_END);
}
static int sdhci_adma_table_pre(struct sdhci_host *host,
{
int direction;
- u8 *desc;
- u8 *align;
+ void *desc;
+ void *align;
dma_addr_t addr;
dma_addr_t align_addr;
int len, offset;
direction = DMA_TO_DEVICE;
host->align_addr = dma_map_single(mmc_dev(host->mmc),
- host->align_buffer, 128 * 4, direction);
+ host->align_buffer, host->align_buffer_sz, direction);
if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr))
goto fail;
- BUG_ON(host->align_addr & 0x3);
+ BUG_ON(host->align_addr & host->align_mask);
host->sg_count = dma_map_sg(mmc_dev(host->mmc),
data->sg, data->sg_len, direction);
if (host->sg_count == 0)
goto unmap_align;
- desc = host->adma_desc;
+ desc = host->adma_table;
align = host->align_buffer;
align_addr = host->align_addr;
* the (up to three) bytes that screw up the
* alignment.
*/
- offset = (4 - (addr & 0x3)) & 0x3;
+ offset = (host->align_sz - (addr & host->align_mask)) &
+ host->align_mask;
if (offset) {
if (data->flags & MMC_DATA_WRITE) {
buffer = sdhci_kmap_atomic(sg, &flags);
- WARN_ON(((long)buffer & PAGE_MASK) > (PAGE_SIZE - 3));
+ WARN_ON(((long)buffer & (PAGE_SIZE - 1)) >
+ (PAGE_SIZE - offset));
memcpy(align, buffer, offset);
sdhci_kunmap_atomic(buffer, &flags);
}
/* tran, valid */
- sdhci_set_adma_desc(desc, align_addr, offset, 0x21);
+ sdhci_adma_write_desc(host, desc, align_addr, offset,
+ ADMA2_TRAN_VALID);
BUG_ON(offset > 65536);
- align += 4;
- align_addr += 4;
+ align += host->align_sz;
+ align_addr += host->align_sz;
- desc += 8;
+ desc += host->desc_sz;
addr += offset;
len -= offset;
BUG_ON(len > 65536);
/* tran, valid */
- sdhci_set_adma_desc(desc, addr, len, 0x21);
- desc += 8;
+ sdhci_adma_write_desc(host, desc, addr, len, ADMA2_TRAN_VALID);
+ desc += host->desc_sz;
/*
* If this triggers then we have a calculation bug
* somewhere. :/
*/
- WARN_ON((desc - host->adma_desc) > ADMA_SIZE);
+ WARN_ON((desc - host->adma_table) >= host->adma_table_sz);
}
if (host->quirks & SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC) {
/*
* Mark the last descriptor as the terminating descriptor
*/
- if (desc != host->adma_desc) {
- desc -= 8;
- desc[0] |= 0x2; /* end */
+ if (desc != host->adma_table) {
+ desc -= host->desc_sz;
+ sdhci_adma_mark_end(desc);
}
} else {
/*
*/
/* nop, end, valid */
- sdhci_set_adma_desc(desc, 0, 0, 0x3);
+ sdhci_adma_write_desc(host, desc, 0, 0, ADMA2_NOP_END_VALID);
}
/*
*/
if (data->flags & MMC_DATA_WRITE) {
dma_sync_single_for_device(mmc_dev(host->mmc),
- host->align_addr, 128 * 4, direction);
+ host->align_addr, host->align_buffer_sz, direction);
}
return 0;
unmap_align:
dma_unmap_single(mmc_dev(host->mmc), host->align_addr,
- 128 * 4, direction);
+ host->align_buffer_sz, direction);
fail:
return -EINVAL;
}
struct scatterlist *sg;
int i, size;
- u8 *align;
+ void *align;
char *buffer;
unsigned long flags;
bool has_unaligned;
direction = DMA_TO_DEVICE;
dma_unmap_single(mmc_dev(host->mmc), host->align_addr,
- 128 * 4, direction);
+ host->align_buffer_sz, direction);
/* Do a quick scan of the SG list for any unaligned mappings */
has_unaligned = false;
for_each_sg(data->sg, sg, host->sg_count, i)
- if (sg_dma_address(sg) & 3) {
+ if (sg_dma_address(sg) & host->align_mask) {
has_unaligned = true;
break;
}
align = host->align_buffer;
for_each_sg(data->sg, sg, host->sg_count, i) {
- if (sg_dma_address(sg) & 0x3) {
- size = 4 - (sg_dma_address(sg) & 0x3);
+ if (sg_dma_address(sg) & host->align_mask) {
+ size = host->align_sz -
+ (sg_dma_address(sg) & host->align_mask);
buffer = sdhci_kmap_atomic(sg, &flags);
- WARN_ON(((long)buffer & PAGE_MASK) > (PAGE_SIZE - 3));
+ WARN_ON(((long)buffer & (PAGE_SIZE - 1)) >
+ (PAGE_SIZE - size));
memcpy(buffer, align, size);
sdhci_kunmap_atomic(buffer, &flags);
- align += 4;
+ align += host->align_sz;
}
}
}
} else {
sdhci_writel(host, host->adma_addr,
SDHCI_ADMA_ADDRESS);
+ if (host->flags & SDHCI_USE_64_BIT_DMA)
+ sdhci_writel(host,
+ (u64)host->adma_addr >> 32,
+ SDHCI_ADMA_ADDRESS_HI);
}
} else {
int sg_cnt;
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
ctrl &= ~SDHCI_CTRL_DMA_MASK;
if ((host->flags & SDHCI_REQ_USE_DMA) &&
- (host->flags & SDHCI_USE_ADMA))
- ctrl |= SDHCI_CTRL_ADMA32;
- else
+ (host->flags & SDHCI_USE_ADMA)) {
+ if (host->flags & SDHCI_USE_64_BIT_DMA)
+ ctrl |= SDHCI_CTRL_ADMA64;
+ else
+ ctrl |= SDHCI_CTRL_ADMA32;
+ } else {
ctrl |= SDHCI_CTRL_SDMA;
+ }
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
}
struct mmc_data *data = cmd->data;
if (data == NULL) {
+ if (host->quirks2 &
+ SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD) {
+ sdhci_writew(host, 0x0, SDHCI_TRANSFER_MODE);
+ } else {
/* clear Auto CMD settings for no data CMDs */
- mode = sdhci_readw(host, SDHCI_TRANSFER_MODE);
- sdhci_writew(host, mode & ~(SDHCI_TRNS_AUTO_CMD12 |
+ mode = sdhci_readw(host, SDHCI_TRANSFER_MODE);
+ sdhci_writew(host, mode & ~(SDHCI_TRNS_AUTO_CMD12 |
SDHCI_TRNS_AUTO_CMD23), SDHCI_TRANSFER_MODE);
+ }
return;
}
case MMC_TIMING_UHS_DDR50:
preset = sdhci_readw(host, SDHCI_PRESET_FOR_DDR50);
break;
+ case MMC_TIMING_MMC_HS400:
+ preset = sdhci_readw(host, SDHCI_PRESET_FOR_HS400);
+ break;
default:
pr_warn("%s: Invalid UHS-I mode selected\n",
mmc_hostname(host->mmc));
else if ((timing == MMC_TIMING_UHS_DDR50) ||
(timing == MMC_TIMING_MMC_DDR52))
ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
+ else if (timing == MMC_TIMING_MMC_HS400)
+ ctrl_2 |= SDHCI_CTRL_HS400; /* Non-standard */
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
}
EXPORT_SYMBOL_GPL(sdhci_set_uhs_signaling);
u16 clk, ctrl_2;
/* In case of UHS-I modes, set High Speed Enable */
- if ((ios->timing == MMC_TIMING_MMC_HS200) ||
+ if ((ios->timing == MMC_TIMING_MMC_HS400) ||
+ (ios->timing == MMC_TIMING_MMC_HS200) ||
(ios->timing == MMC_TIMING_MMC_DDR52) ||
(ios->timing == MMC_TIMING_UHS_SDR50) ||
(ios->timing == MMC_TIMING_UHS_SDR104) ||
* tuning function has to be executed.
*/
switch (host->timing) {
+ case MMC_TIMING_MMC_HS400:
case MMC_TIMING_MMC_HS200:
case MMC_TIMING_UHS_SDR104:
break;
*/
if (!(host->flags & SDHCI_DEVICE_DEAD) &&
((mrq->cmd && mrq->cmd->error) ||
- (mrq->data && (mrq->data->error ||
- (mrq->data->stop && mrq->data->stop->error))) ||
- (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))) {
+ (mrq->sbc && mrq->sbc->error) ||
+ (mrq->data && ((mrq->data->error && !mrq->data->stop) ||
+ (mrq->data->stop && mrq->data->stop->error))) ||
+ (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))) {
/* Some controllers need this kick or reset won't work here */
if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET)
}
#ifdef CONFIG_MMC_DEBUG
-static void sdhci_show_adma_error(struct sdhci_host *host)
+static void sdhci_adma_show_error(struct sdhci_host *host)
{
const char *name = mmc_hostname(host->mmc);
- u8 *desc = host->adma_desc;
- __le32 *dma;
- __le16 *len;
- u8 attr;
+ void *desc = host->adma_table;
sdhci_dumpregs(host);
while (true) {
- dma = (__le32 *)(desc + 4);
- len = (__le16 *)(desc + 2);
- attr = *desc;
-
- DBG("%s: %p: DMA 0x%08x, LEN 0x%04x, Attr=0x%02x\n",
- name, desc, le32_to_cpu(*dma), le16_to_cpu(*len), attr);
+ struct sdhci_adma2_64_desc *dma_desc = desc;
+
+ if (host->flags & SDHCI_USE_64_BIT_DMA)
+ DBG("%s: %p: DMA 0x%08x%08x, LEN 0x%04x, Attr=0x%02x\n",
+ name, desc, le32_to_cpu(dma_desc->addr_hi),
+ le32_to_cpu(dma_desc->addr_lo),
+ le16_to_cpu(dma_desc->len),
+ le16_to_cpu(dma_desc->cmd));
+ else
+ DBG("%s: %p: DMA 0x%08x, LEN 0x%04x, Attr=0x%02x\n",
+ name, desc, le32_to_cpu(dma_desc->addr_lo),
+ le16_to_cpu(dma_desc->len),
+ le16_to_cpu(dma_desc->cmd));
- desc += 8;
+ desc += host->desc_sz;
- if (attr & 2)
+ if (dma_desc->cmd & cpu_to_le16(ADMA2_END))
break;
}
}
#else
-static void sdhci_show_adma_error(struct sdhci_host *host) { }
+static void sdhci_adma_show_error(struct sdhci_host *host) { }
#endif
static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
host->data->error = -EILSEQ;
else if (intmask & SDHCI_INT_ADMA_ERROR) {
pr_err("%s: ADMA error\n", mmc_hostname(host->mmc));
- sdhci_show_adma_error(host);
+ sdhci_adma_show_error(host);
host->data->error = -EIO;
if (host->ops->adma_workaround)
host->ops->adma_workaround(host, intmask);
host->flags &= ~SDHCI_USE_ADMA;
}
+ /*
+ * It is assumed that a 64-bit capable device has set a 64-bit DMA mask
+ * and *must* do 64-bit DMA. A driver has the opportunity to change
+ * that during the first call to ->enable_dma(). Similarly
+ * SDHCI_QUIRK2_BROKEN_64_BIT_DMA must be left to the drivers to
+ * implement.
+ */
+ if (sdhci_readl(host, SDHCI_CAPABILITIES) & SDHCI_CAN_64BIT)
+ host->flags |= SDHCI_USE_64_BIT_DMA;
+
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
if (host->ops->enable_dma) {
if (host->ops->enable_dma(host)) {
}
}
+ /* SDMA does not support 64-bit DMA */
+ if (host->flags & SDHCI_USE_64_BIT_DMA)
+ host->flags &= ~SDHCI_USE_SDMA;
+
if (host->flags & SDHCI_USE_ADMA) {
/*
- * We need to allocate descriptors for all sg entries
- * (128) and potentially one alignment transfer for
- * each of those entries.
+ * The DMA descriptor table size is calculated as the maximum
+ * number of segments times 2, to allow for an alignment
+ * descriptor for each segment, plus 1 for a nop end descriptor,
+ * all multipled by the descriptor size.
*/
- host->adma_desc = dma_alloc_coherent(mmc_dev(mmc),
- ADMA_SIZE, &host->adma_addr,
- GFP_KERNEL);
- host->align_buffer = kmalloc(128 * 4, GFP_KERNEL);
- if (!host->adma_desc || !host->align_buffer) {
- dma_free_coherent(mmc_dev(mmc), ADMA_SIZE,
- host->adma_desc, host->adma_addr);
+ if (host->flags & SDHCI_USE_64_BIT_DMA) {
+ host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) *
+ SDHCI_ADMA2_64_DESC_SZ;
+ host->align_buffer_sz = SDHCI_MAX_SEGS *
+ SDHCI_ADMA2_64_ALIGN;
+ host->desc_sz = SDHCI_ADMA2_64_DESC_SZ;
+ host->align_sz = SDHCI_ADMA2_64_ALIGN;
+ host->align_mask = SDHCI_ADMA2_64_ALIGN - 1;
+ } else {
+ host->adma_table_sz = (SDHCI_MAX_SEGS * 2 + 1) *
+ SDHCI_ADMA2_32_DESC_SZ;
+ host->align_buffer_sz = SDHCI_MAX_SEGS *
+ SDHCI_ADMA2_32_ALIGN;
+ host->desc_sz = SDHCI_ADMA2_32_DESC_SZ;
+ host->align_sz = SDHCI_ADMA2_32_ALIGN;
+ host->align_mask = SDHCI_ADMA2_32_ALIGN - 1;
+ }
+ host->adma_table = dma_alloc_coherent(mmc_dev(mmc),
+ host->adma_table_sz,
+ &host->adma_addr,
+ GFP_KERNEL);
+ host->align_buffer = kmalloc(host->align_buffer_sz, GFP_KERNEL);
+ if (!host->adma_table || !host->align_buffer) {
+ dma_free_coherent(mmc_dev(mmc), host->adma_table_sz,
+ host->adma_table, host->adma_addr);
kfree(host->align_buffer);
pr_warn("%s: Unable to allocate ADMA buffers - falling back to standard DMA\n",
mmc_hostname(mmc));
host->flags &= ~SDHCI_USE_ADMA;
- host->adma_desc = NULL;
+ host->adma_table = NULL;
host->align_buffer = NULL;
- } else if (host->adma_addr & 3) {
+ } else if (host->adma_addr & host->align_mask) {
pr_warn("%s: unable to allocate aligned ADMA descriptor\n",
mmc_hostname(mmc));
host->flags &= ~SDHCI_USE_ADMA;
- dma_free_coherent(mmc_dev(mmc), ADMA_SIZE,
- host->adma_desc, host->adma_addr);
+ dma_free_coherent(mmc_dev(mmc), host->adma_table_sz,
+ host->adma_table, host->adma_addr);
kfree(host->align_buffer);
- host->adma_desc = NULL;
+ host->adma_table = NULL;
host->align_buffer = NULL;
}
}
if (ret) {
pr_warn("%s: Failed to enable vqmmc regulator: %d\n",
mmc_hostname(mmc), ret);
- mmc->supply.vqmmc = NULL;
+ mmc->supply.vqmmc = ERR_PTR(-EINVAL);
}
}
/* SD3.0: SDR104 is supported so (for eMMC) the caps2
* field can be promoted to support HS200.
*/
- if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200)) {
+ if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200))
mmc->caps2 |= MMC_CAP2_HS200;
- if (IS_ERR(mmc->supply.vqmmc) ||
- !regulator_is_supported_voltage
- (mmc->supply.vqmmc, 1100000, 1300000))
- mmc->caps2 &= ~MMC_CAP2_HS200_1_2V_SDR;
- }
} else if (caps[1] & SDHCI_SUPPORT_SDR50)
mmc->caps |= MMC_CAP_UHS_SDR50;
+ if (host->quirks2 & SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 &&
+ (caps[1] & SDHCI_SUPPORT_HS400))
+ mmc->caps2 |= MMC_CAP2_HS400;
+
+ if ((mmc->caps2 & MMC_CAP2_HSX00_1_2V) &&
+ (IS_ERR(mmc->supply.vqmmc) ||
+ !regulator_is_supported_voltage(mmc->supply.vqmmc, 1100000,
+ 1300000)))
+ mmc->caps2 &= ~MMC_CAP2_HSX00_1_2V;
+
if ((caps[1] & SDHCI_SUPPORT_DDR50) &&
!(host->quirks2 & SDHCI_QUIRK2_BROKEN_DDR50))
mmc->caps |= MMC_CAP_UHS_DDR50;
* can do scatter/gather or not.
*/
if (host->flags & SDHCI_USE_ADMA)
- mmc->max_segs = 128;
+ mmc->max_segs = SDHCI_MAX_SEGS;
else if (host->flags & SDHCI_USE_SDMA)
mmc->max_segs = 1;
else /* PIO */
- mmc->max_segs = 128;
+ mmc->max_segs = SDHCI_MAX_SEGS;
/*
* Maximum number of sectors in one transfer. Limited by DMA boundary
pr_info("%s: SDHCI controller on %s [%s] using %s\n",
mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)),
- (host->flags & SDHCI_USE_ADMA) ? "ADMA" :
+ (host->flags & SDHCI_USE_ADMA) ?
+ (host->flags & SDHCI_USE_64_BIT_DMA) ? "ADMA 64-bit" : "ADMA" :
(host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO");
sdhci_enable_card_detection(host);
tasklet_kill(&host->finish_tasklet);
- if (!IS_ERR(mmc->supply.vmmc))
- regulator_disable(mmc->supply.vmmc);
-
if (!IS_ERR(mmc->supply.vqmmc))
regulator_disable(mmc->supply.vqmmc);
- if (host->adma_desc)
- dma_free_coherent(mmc_dev(mmc), ADMA_SIZE,
- host->adma_desc, host->adma_addr);
+ if (host->adma_table)
+ dma_free_coherent(mmc_dev(mmc), host->adma_table_sz,
+ host->adma_table, host->adma_addr);
kfree(host->align_buffer);
- host->adma_desc = NULL;
+ host->adma_table = NULL;
host->align_buffer = NULL;
}
#define SDHCI_CTRL_UHS_SDR50 0x0002
#define SDHCI_CTRL_UHS_SDR104 0x0003
#define SDHCI_CTRL_UHS_DDR50 0x0004
-#define SDHCI_CTRL_HS_SDR200 0x0005 /* reserved value in SDIO spec */
+#define SDHCI_CTRL_HS400 0x0005 /* Non-standard */
#define SDHCI_CTRL_VDD_180 0x0008
#define SDHCI_CTRL_DRV_TYPE_MASK 0x0030
#define SDHCI_CTRL_DRV_TYPE_B 0x0000
#define SDHCI_RETUNING_MODE_SHIFT 14
#define SDHCI_CLOCK_MUL_MASK 0x00FF0000
#define SDHCI_CLOCK_MUL_SHIFT 16
+#define SDHCI_SUPPORT_HS400 0x80000000 /* Non-standard */
#define SDHCI_CAPABILITIES_1 0x44
/* 55-57 reserved */
#define SDHCI_ADMA_ADDRESS 0x58
+#define SDHCI_ADMA_ADDRESS_HI 0x5C
/* 60-FB reserved */
#define SDHCI_PRESET_FOR_SDR50 0x6A
#define SDHCI_PRESET_FOR_SDR104 0x6C
#define SDHCI_PRESET_FOR_DDR50 0x6E
+#define SDHCI_PRESET_FOR_HS400 0x74 /* Non-standard */
#define SDHCI_PRESET_DRV_MASK 0xC000
#define SDHCI_PRESET_DRV_SHIFT 14
#define SDHCI_PRESET_CLKGEN_SEL_MASK 0x400
#define SDHCI_DEFAULT_BOUNDARY_SIZE (512 * 1024)
#define SDHCI_DEFAULT_BOUNDARY_ARG (ilog2(SDHCI_DEFAULT_BOUNDARY_SIZE) - 12)
+/* ADMA2 32-bit DMA descriptor size */
+#define SDHCI_ADMA2_32_DESC_SZ 8
+
+/* ADMA2 32-bit DMA alignment */
+#define SDHCI_ADMA2_32_ALIGN 4
+
+/* ADMA2 32-bit descriptor */
+struct sdhci_adma2_32_desc {
+ __le16 cmd;
+ __le16 len;
+ __le32 addr;
+} __packed __aligned(SDHCI_ADMA2_32_ALIGN);
+
+/* ADMA2 64-bit DMA descriptor size */
+#define SDHCI_ADMA2_64_DESC_SZ 12
+
+/* ADMA2 64-bit DMA alignment */
+#define SDHCI_ADMA2_64_ALIGN 8
+
+/*
+ * ADMA2 64-bit descriptor. Note 12-byte descriptor can't always be 8-byte
+ * aligned.
+ */
+struct sdhci_adma2_64_desc {
+ __le16 cmd;
+ __le16 len;
+ __le32 addr_lo;
+ __le32 addr_hi;
+} __packed __aligned(4);
+
+#define ADMA2_TRAN_VALID 0x21
+#define ADMA2_NOP_END_VALID 0x3
+#define ADMA2_END 0x2
+
+/*
+ * Maximum segments assuming a 512KiB maximum requisition size and a minimum
+ * 4KiB page size.
+ */
+#define SDHCI_MAX_SEGS 128
+
struct sdhci_ops {
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
u32 (*read_l)(struct sdhci_host *host, int reg);
#include <linux/err.h>
#include <linux/clk.h>
-#include <linux/clk-private.h>
#include <linux/clk/sunxi.h>
#include <linux/gpio.h>
--- /dev/null
+/*
+ * Toshiba PCI Secure Digital Host Controller Interface driver
+ *
+ * Copyright (C) 2014 Ondrej Zary
+ * Copyright (C) 2007 Richard Betts, All Rights Reserved.
+ *
+ * Based on asic3_mmc.c, copyright (c) 2005 SDG Systems, LLC and,
+ * sdhci.c, copyright (C) 2005-2006 Pierre Ossman
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/scatterlist.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/pm.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+
+#include "toshsd.h"
+
+#define DRIVER_NAME "toshsd"
+
+static const struct pci_device_id pci_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA, 0x0805) },
+ { /* end: all zeroes */ },
+};
+
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+static void toshsd_init(struct toshsd_host *host)
+{
+ /* enable clock */
+ pci_write_config_byte(host->pdev, SD_PCICFG_CLKSTOP,
+ SD_PCICFG_CLKSTOP_ENABLE_ALL);
+ pci_write_config_byte(host->pdev, SD_PCICFG_CARDDETECT, 2);
+
+ /* reset */
+ iowrite16(0, host->ioaddr + SD_SOFTWARERESET); /* assert */
+ mdelay(2);
+ iowrite16(1, host->ioaddr + SD_SOFTWARERESET); /* deassert */
+ mdelay(2);
+
+ /* Clear card registers */
+ iowrite16(0, host->ioaddr + SD_CARDCLOCKCTRL);
+ iowrite32(0, host->ioaddr + SD_CARDSTATUS);
+ iowrite32(0, host->ioaddr + SD_ERRORSTATUS0);
+ iowrite16(0, host->ioaddr + SD_STOPINTERNAL);
+
+ /* SDIO clock? */
+ iowrite16(0x100, host->ioaddr + SDIO_BASE + SDIO_CLOCKNWAITCTRL);
+
+ /* enable LED */
+ pci_write_config_byte(host->pdev, SD_PCICFG_SDLED_ENABLE1,
+ SD_PCICFG_LED_ENABLE1_START);
+ pci_write_config_byte(host->pdev, SD_PCICFG_SDLED_ENABLE2,
+ SD_PCICFG_LED_ENABLE2_START);
+
+ /* set interrupt masks */
+ iowrite32(~(u32)(SD_CARD_RESP_END | SD_CARD_RW_END
+ | SD_CARD_CARD_REMOVED_0 | SD_CARD_CARD_INSERTED_0
+ | SD_BUF_READ_ENABLE | SD_BUF_WRITE_ENABLE
+ | SD_BUF_CMD_TIMEOUT),
+ host->ioaddr + SD_INTMASKCARD);
+
+ iowrite16(0x1000, host->ioaddr + SD_TRANSACTIONCTRL);
+}
+
+/* Set MMC clock / power.
+ * Note: This controller uses a simple divider scheme therefore it cannot run
+ * SD/MMC cards at full speed (24/20MHz). HCLK (=33MHz PCI clock?) is too high
+ * and the next slowest is 16MHz (div=2).
+ */
+static void __toshsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct toshsd_host *host = mmc_priv(mmc);
+
+ if (ios->clock) {
+ u16 clk;
+ int div = 1;
+
+ while (ios->clock < HCLK / div)
+ div *= 2;
+
+ clk = div >> 2;
+
+ if (div == 1) { /* disable the divider */
+ pci_write_config_byte(host->pdev, SD_PCICFG_CLKMODE,
+ SD_PCICFG_CLKMODE_DIV_DISABLE);
+ clk |= SD_CARDCLK_DIV_DISABLE;
+ } else
+ pci_write_config_byte(host->pdev, SD_PCICFG_CLKMODE, 0);
+
+ clk |= SD_CARDCLK_ENABLE_CLOCK;
+ iowrite16(clk, host->ioaddr + SD_CARDCLOCKCTRL);
+
+ mdelay(10);
+ } else
+ iowrite16(0, host->ioaddr + SD_CARDCLOCKCTRL);
+
+ switch (ios->power_mode) {
+ case MMC_POWER_OFF:
+ pci_write_config_byte(host->pdev, SD_PCICFG_POWER1,
+ SD_PCICFG_PWR1_OFF);
+ mdelay(1);
+ break;
+ case MMC_POWER_UP:
+ break;
+ case MMC_POWER_ON:
+ pci_write_config_byte(host->pdev, SD_PCICFG_POWER1,
+ SD_PCICFG_PWR1_33V);
+ pci_write_config_byte(host->pdev, SD_PCICFG_POWER2,
+ SD_PCICFG_PWR2_AUTO);
+ mdelay(20);
+ break;
+ }
+
+ switch (ios->bus_width) {
+ case MMC_BUS_WIDTH_1:
+ iowrite16(SD_CARDOPT_REQUIRED | SD_CARDOPT_DATA_RESP_TIMEOUT(14)
+ | SD_CARDOPT_C2_MODULE_ABSENT
+ | SD_CARDOPT_DATA_XFR_WIDTH_1,
+ host->ioaddr + SD_CARDOPTIONSETUP);
+ break;
+ case MMC_BUS_WIDTH_4:
+ iowrite16(SD_CARDOPT_REQUIRED | SD_CARDOPT_DATA_RESP_TIMEOUT(14)
+ | SD_CARDOPT_C2_MODULE_ABSENT
+ | SD_CARDOPT_DATA_XFR_WIDTH_4,
+ host->ioaddr + SD_CARDOPTIONSETUP);
+ break;
+ }
+}
+
+static void toshsd_set_led(struct toshsd_host *host, unsigned char state)
+{
+ iowrite16(state, host->ioaddr + SDIO_BASE + SDIO_LEDCTRL);
+}
+
+static void toshsd_finish_request(struct toshsd_host *host)
+{
+ struct mmc_request *mrq = host->mrq;
+
+ /* Write something to end the command */
+ host->mrq = NULL;
+ host->cmd = NULL;
+ host->data = NULL;
+
+ toshsd_set_led(host, 0);
+ mmc_request_done(host->mmc, mrq);
+}
+
+static irqreturn_t toshsd_thread_irq(int irq, void *dev_id)
+{
+ struct toshsd_host *host = dev_id;
+ struct mmc_data *data = host->data;
+ struct sg_mapping_iter *sg_miter = &host->sg_miter;
+ unsigned short *buf;
+ int count;
+ unsigned long flags;
+
+ if (!data) {
+ dev_warn(&host->pdev->dev, "Spurious Data IRQ\n");
+ if (host->cmd) {
+ host->cmd->error = -EIO;
+ toshsd_finish_request(host);
+ }
+ return IRQ_NONE;
+ }
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (!sg_miter_next(sg_miter))
+ return IRQ_HANDLED;
+ buf = sg_miter->addr;
+
+ /* Ensure we dont read more than one block. The chip will interrupt us
+ * When the next block is available.
+ */
+ count = sg_miter->length;
+ if (count > data->blksz)
+ count = data->blksz;
+
+ dev_dbg(&host->pdev->dev, "count: %08x, flags %08x\n", count,
+ data->flags);
+
+ /* Transfer the data */
+ if (data->flags & MMC_DATA_READ)
+ ioread32_rep(host->ioaddr + SD_DATAPORT, buf, count >> 2);
+ else
+ iowrite32_rep(host->ioaddr + SD_DATAPORT, buf, count >> 2);
+
+ sg_miter->consumed = count;
+ sg_miter_stop(sg_miter);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static void toshsd_cmd_irq(struct toshsd_host *host)
+{
+ struct mmc_command *cmd = host->cmd;
+ u8 *buf;
+ u16 data;
+
+ if (!host->cmd) {
+ dev_warn(&host->pdev->dev, "Spurious CMD irq\n");
+ return;
+ }
+ buf = (u8 *)cmd->resp;
+ host->cmd = NULL;
+
+ if (cmd->flags & MMC_RSP_PRESENT && cmd->flags & MMC_RSP_136) {
+ /* R2 */
+ buf[12] = 0xff;
+ data = ioread16(host->ioaddr + SD_RESPONSE0);
+ buf[13] = data & 0xff;
+ buf[14] = data >> 8;
+ data = ioread16(host->ioaddr + SD_RESPONSE1);
+ buf[15] = data & 0xff;
+ buf[8] = data >> 8;
+ data = ioread16(host->ioaddr + SD_RESPONSE2);
+ buf[9] = data & 0xff;
+ buf[10] = data >> 8;
+ data = ioread16(host->ioaddr + SD_RESPONSE3);
+ buf[11] = data & 0xff;
+ buf[4] = data >> 8;
+ data = ioread16(host->ioaddr + SD_RESPONSE4);
+ buf[5] = data & 0xff;
+ buf[6] = data >> 8;
+ data = ioread16(host->ioaddr + SD_RESPONSE5);
+ buf[7] = data & 0xff;
+ buf[0] = data >> 8;
+ data = ioread16(host->ioaddr + SD_RESPONSE6);
+ buf[1] = data & 0xff;
+ buf[2] = data >> 8;
+ data = ioread16(host->ioaddr + SD_RESPONSE7);
+ buf[3] = data & 0xff;
+ } else if (cmd->flags & MMC_RSP_PRESENT) {
+ /* R1, R1B, R3, R6, R7 */
+ data = ioread16(host->ioaddr + SD_RESPONSE0);
+ buf[0] = data & 0xff;
+ buf[1] = data >> 8;
+ data = ioread16(host->ioaddr + SD_RESPONSE1);
+ buf[2] = data & 0xff;
+ buf[3] = data >> 8;
+ }
+
+ dev_dbg(&host->pdev->dev, "Command IRQ complete %d %d %x\n",
+ cmd->opcode, cmd->error, cmd->flags);
+
+ /* If there is data to handle we will
+ * finish the request in the mmc_data_end_irq handler.*/
+ if (host->data)
+ return;
+
+ toshsd_finish_request(host);
+}
+
+static void toshsd_data_end_irq(struct toshsd_host *host)
+{
+ struct mmc_data *data = host->data;
+
+ host->data = NULL;
+
+ if (!data) {
+ dev_warn(&host->pdev->dev, "Spurious data end IRQ\n");
+ return;
+ }
+
+ if (data->error == 0)
+ data->bytes_xfered = data->blocks * data->blksz;
+ else
+ data->bytes_xfered = 0;
+
+ dev_dbg(&host->pdev->dev, "Completed data request xfr=%d\n",
+ data->bytes_xfered);
+
+ iowrite16(0, host->ioaddr + SD_STOPINTERNAL);
+
+ toshsd_finish_request(host);
+}
+
+static irqreturn_t toshsd_irq(int irq, void *dev_id)
+{
+ struct toshsd_host *host = dev_id;
+ u32 int_reg, int_mask, int_status, detail;
+ int error = 0, ret = IRQ_HANDLED;
+
+ spin_lock(&host->lock);
+ int_status = ioread32(host->ioaddr + SD_CARDSTATUS);
+ int_mask = ioread32(host->ioaddr + SD_INTMASKCARD);
+ int_reg = int_status & ~int_mask & ~IRQ_DONT_CARE_BITS;
+
+ dev_dbg(&host->pdev->dev, "IRQ status:%x mask:%x\n",
+ int_status, int_mask);
+
+ /* nothing to do: it's not our IRQ */
+ if (!int_reg) {
+ ret = IRQ_NONE;
+ goto irq_end;
+ }
+
+ if (int_reg & SD_BUF_CMD_TIMEOUT) {
+ error = -ETIMEDOUT;
+ dev_dbg(&host->pdev->dev, "Timeout\n");
+ } else if (int_reg & SD_BUF_CRC_ERR) {
+ error = -EILSEQ;
+ dev_err(&host->pdev->dev, "BadCRC\n");
+ } else if (int_reg & (SD_BUF_ILLEGAL_ACCESS
+ | SD_BUF_CMD_INDEX_ERR
+ | SD_BUF_STOP_BIT_END_ERR
+ | SD_BUF_OVERFLOW
+ | SD_BUF_UNDERFLOW
+ | SD_BUF_DATA_TIMEOUT)) {
+ dev_err(&host->pdev->dev, "Buffer status error: { %s%s%s%s%s%s}\n",
+ int_reg & SD_BUF_ILLEGAL_ACCESS ? "ILLEGAL_ACC " : "",
+ int_reg & SD_BUF_CMD_INDEX_ERR ? "CMD_INDEX " : "",
+ int_reg & SD_BUF_STOP_BIT_END_ERR ? "STOPBIT_END " : "",
+ int_reg & SD_BUF_OVERFLOW ? "OVERFLOW " : "",
+ int_reg & SD_BUF_UNDERFLOW ? "UNDERFLOW " : "",
+ int_reg & SD_BUF_DATA_TIMEOUT ? "DATA_TIMEOUT " : "");
+
+ detail = ioread32(host->ioaddr + SD_ERRORSTATUS0);
+ dev_err(&host->pdev->dev, "detail error status { %s%s%s%s%s%s%s%s%s%s%s%s%s}\n",
+ detail & SD_ERR0_RESP_CMD_ERR ? "RESP_CMD " : "",
+ detail & SD_ERR0_RESP_NON_CMD12_END_BIT_ERR ? "RESP_END_BIT " : "",
+ detail & SD_ERR0_RESP_CMD12_END_BIT_ERR ? "RESP_END_BIT " : "",
+ detail & SD_ERR0_READ_DATA_END_BIT_ERR ? "READ_DATA_END_BIT " : "",
+ detail & SD_ERR0_WRITE_CRC_STATUS_END_BIT_ERR ? "WRITE_CMD_END_BIT " : "",
+ detail & SD_ERR0_RESP_NON_CMD12_CRC_ERR ? "RESP_CRC " : "",
+ detail & SD_ERR0_RESP_CMD12_CRC_ERR ? "RESP_CRC " : "",
+ detail & SD_ERR0_READ_DATA_CRC_ERR ? "READ_DATA_CRC " : "",
+ detail & SD_ERR0_WRITE_CMD_CRC_ERR ? "WRITE_CMD_CRC " : "",
+ detail & SD_ERR1_NO_CMD_RESP ? "NO_CMD_RESP " : "",
+ detail & SD_ERR1_TIMEOUT_READ_DATA ? "READ_DATA_TIMEOUT " : "",
+ detail & SD_ERR1_TIMEOUT_CRS_STATUS ? "CRS_STATUS_TIMEOUT " : "",
+ detail & SD_ERR1_TIMEOUT_CRC_BUSY ? "CRC_BUSY_TIMEOUT " : "");
+ error = -EIO;
+ }
+
+ if (error) {
+ if (host->cmd)
+ host->cmd->error = error;
+
+ if (error == -ETIMEDOUT) {
+ iowrite32(int_status &
+ ~(SD_BUF_CMD_TIMEOUT | SD_CARD_RESP_END),
+ host->ioaddr + SD_CARDSTATUS);
+ } else {
+ toshsd_init(host);
+ __toshsd_set_ios(host->mmc, &host->mmc->ios);
+ goto irq_end;
+ }
+ }
+
+ /* Card insert/remove. The mmc controlling code is stateless. */
+ if (int_reg & (SD_CARD_CARD_INSERTED_0 | SD_CARD_CARD_REMOVED_0)) {
+ iowrite32(int_status &
+ ~(SD_CARD_CARD_REMOVED_0 | SD_CARD_CARD_INSERTED_0),
+ host->ioaddr + SD_CARDSTATUS);
+
+ if (int_reg & SD_CARD_CARD_INSERTED_0)
+ toshsd_init(host);
+
+ mmc_detect_change(host->mmc, 1);
+ }
+
+ /* Data transfer */
+ if (int_reg & (SD_BUF_READ_ENABLE | SD_BUF_WRITE_ENABLE)) {
+ iowrite32(int_status &
+ ~(SD_BUF_WRITE_ENABLE | SD_BUF_READ_ENABLE),
+ host->ioaddr + SD_CARDSTATUS);
+
+ ret = IRQ_WAKE_THREAD;
+ goto irq_end;
+ }
+
+ /* Command completion */
+ if (int_reg & SD_CARD_RESP_END) {
+ iowrite32(int_status & ~(SD_CARD_RESP_END),
+ host->ioaddr + SD_CARDSTATUS);
+ toshsd_cmd_irq(host);
+ }
+
+ /* Data transfer completion */
+ if (int_reg & SD_CARD_RW_END) {
+ iowrite32(int_status & ~(SD_CARD_RW_END),
+ host->ioaddr + SD_CARDSTATUS);
+ toshsd_data_end_irq(host);
+ }
+irq_end:
+ spin_unlock(&host->lock);
+ return ret;
+}
+
+static void toshsd_start_cmd(struct toshsd_host *host, struct mmc_command *cmd)
+{
+ struct mmc_data *data = host->data;
+ int c = cmd->opcode;
+
+ dev_dbg(&host->pdev->dev, "Command opcode: %d\n", cmd->opcode);
+
+ if (cmd->opcode == MMC_STOP_TRANSMISSION) {
+ iowrite16(SD_STOPINT_ISSUE_CMD12,
+ host->ioaddr + SD_STOPINTERNAL);
+
+ cmd->resp[0] = cmd->opcode;
+ cmd->resp[1] = 0;
+ cmd->resp[2] = 0;
+ cmd->resp[3] = 0;
+
+ toshsd_finish_request(host);
+ return;
+ }
+
+ switch (mmc_resp_type(cmd)) {
+ case MMC_RSP_NONE:
+ c |= SD_CMD_RESP_TYPE_NONE;
+ break;
+
+ case MMC_RSP_R1:
+ c |= SD_CMD_RESP_TYPE_EXT_R1;
+ break;
+ case MMC_RSP_R1B:
+ c |= SD_CMD_RESP_TYPE_EXT_R1B;
+ break;
+ case MMC_RSP_R2:
+ c |= SD_CMD_RESP_TYPE_EXT_R2;
+ break;
+ case MMC_RSP_R3:
+ c |= SD_CMD_RESP_TYPE_EXT_R3;
+ break;
+
+ default:
+ dev_err(&host->pdev->dev, "Unknown response type %d\n",
+ mmc_resp_type(cmd));
+ break;
+ }
+
+ host->cmd = cmd;
+
+ if (cmd->opcode == MMC_APP_CMD)
+ c |= SD_CMD_TYPE_ACMD;
+
+ if (cmd->opcode == MMC_GO_IDLE_STATE)
+ c |= (3 << 8); /* removed from ipaq-asic3.h for some reason */
+
+ if (data) {
+ c |= SD_CMD_DATA_PRESENT;
+
+ if (data->blocks > 1) {
+ iowrite16(SD_STOPINT_AUTO_ISSUE_CMD12,
+ host->ioaddr + SD_STOPINTERNAL);
+ c |= SD_CMD_MULTI_BLOCK;
+ }
+
+ if (data->flags & MMC_DATA_READ)
+ c |= SD_CMD_TRANSFER_READ;
+
+ /* MMC_DATA_WRITE does not require a bit to be set */
+ }
+
+ /* Send the command */
+ iowrite32(cmd->arg, host->ioaddr + SD_ARG0);
+ iowrite16(c, host->ioaddr + SD_CMD);
+}
+
+static void toshsd_start_data(struct toshsd_host *host, struct mmc_data *data)
+{
+ unsigned int flags = SG_MITER_ATOMIC;
+
+ dev_dbg(&host->pdev->dev, "setup data transfer: blocksize %08x nr_blocks %d, offset: %08x\n",
+ data->blksz, data->blocks, data->sg->offset);
+
+ host->data = data;
+
+ if (data->flags & MMC_DATA_READ)
+ flags |= SG_MITER_TO_SG;
+ else
+ flags |= SG_MITER_FROM_SG;
+
+ sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags);
+
+ /* Set transfer length and blocksize */
+ iowrite16(data->blocks, host->ioaddr + SD_BLOCKCOUNT);
+ iowrite16(data->blksz, host->ioaddr + SD_CARDXFERDATALEN);
+}
+
+/* Process requests from the MMC layer */
+static void toshsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct toshsd_host *host = mmc_priv(mmc);
+ unsigned long flags;
+
+ /* abort if card not present */
+ if (!(ioread16(host->ioaddr + SD_CARDSTATUS) & SD_CARD_PRESENT_0)) {
+ mrq->cmd->error = -ENOMEDIUM;
+ mmc_request_done(mmc, mrq);
+ return;
+ }
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ WARN_ON(host->mrq != NULL);
+
+ host->mrq = mrq;
+
+ if (mrq->data)
+ toshsd_start_data(host, mrq->data);
+
+ toshsd_set_led(host, 1);
+
+ toshsd_start_cmd(host, mrq->cmd);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void toshsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct toshsd_host *host = mmc_priv(mmc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+ __toshsd_set_ios(mmc, ios);
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static int toshsd_get_ro(struct mmc_host *mmc)
+{
+ struct toshsd_host *host = mmc_priv(mmc);
+
+ /* active low */
+ return !(ioread16(host->ioaddr + SD_CARDSTATUS) & SD_CARD_WRITE_PROTECT);
+}
+
+static int toshsd_get_cd(struct mmc_host *mmc)
+{
+ struct toshsd_host *host = mmc_priv(mmc);
+
+ return !!(ioread16(host->ioaddr + SD_CARDSTATUS) & SD_CARD_PRESENT_0);
+}
+
+static struct mmc_host_ops toshsd_ops = {
+ .request = toshsd_request,
+ .set_ios = toshsd_set_ios,
+ .get_ro = toshsd_get_ro,
+ .get_cd = toshsd_get_cd,
+};
+
+
+static void toshsd_powerdown(struct toshsd_host *host)
+{
+ /* mask all interrupts */
+ iowrite32(0xffffffff, host->ioaddr + SD_INTMASKCARD);
+ /* disable card clock */
+ iowrite16(0x000, host->ioaddr + SDIO_BASE + SDIO_CLOCKNWAITCTRL);
+ iowrite16(0, host->ioaddr + SD_CARDCLOCKCTRL);
+ /* power down card */
+ pci_write_config_byte(host->pdev, SD_PCICFG_POWER1, SD_PCICFG_PWR1_OFF);
+ /* disable clock */
+ pci_write_config_byte(host->pdev, SD_PCICFG_CLKSTOP, 0);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int toshsd_pm_suspend(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct toshsd_host *host = pci_get_drvdata(pdev);
+
+ toshsd_powerdown(host);
+
+ pci_save_state(pdev);
+ pci_enable_wake(pdev, PCI_D3hot, 0);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, PCI_D3hot);
+
+ return 0;
+}
+
+static int toshsd_pm_resume(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct toshsd_host *host = pci_get_drvdata(pdev);
+ int ret;
+
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+ ret = pci_enable_device(pdev);
+ if (ret)
+ return ret;
+
+ toshsd_init(host);
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static int toshsd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ int ret;
+ struct toshsd_host *host;
+ struct mmc_host *mmc;
+ resource_size_t base;
+
+ ret = pci_enable_device(pdev);
+ if (ret)
+ return ret;
+
+ mmc = mmc_alloc_host(sizeof(struct toshsd_host), &pdev->dev);
+ if (!mmc) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ host = mmc_priv(mmc);
+ host->mmc = mmc;
+
+ host->pdev = pdev;
+ pci_set_drvdata(pdev, host);
+
+ ret = pci_request_regions(pdev, DRIVER_NAME);
+ if (ret)
+ goto free;
+
+ host->ioaddr = pci_iomap(pdev, 0, 0);
+ if (!host->ioaddr) {
+ ret = -ENOMEM;
+ goto release;
+ }
+
+ /* Set MMC host parameters */
+ mmc->ops = &toshsd_ops;
+ mmc->caps = MMC_CAP_4_BIT_DATA;
+ mmc->ocr_avail = MMC_VDD_32_33;
+
+ mmc->f_min = HCLK / 512;
+ mmc->f_max = HCLK;
+
+ spin_lock_init(&host->lock);
+
+ toshsd_init(host);
+
+ ret = request_threaded_irq(pdev->irq, toshsd_irq, toshsd_thread_irq,
+ IRQF_SHARED, DRIVER_NAME, host);
+ if (ret)
+ goto unmap;
+
+ mmc_add_host(mmc);
+
+ base = pci_resource_start(pdev, 0);
+ dev_dbg(&pdev->dev, "MMIO %pa, IRQ %d\n", &base, pdev->irq);
+
+ pm_suspend_ignore_children(&pdev->dev, 1);
+
+ return 0;
+
+unmap:
+ pci_iounmap(pdev, host->ioaddr);
+release:
+ pci_release_regions(pdev);
+free:
+ mmc_free_host(mmc);
+ pci_set_drvdata(pdev, NULL);
+err:
+ pci_disable_device(pdev);
+ return ret;
+}
+
+static void toshsd_remove(struct pci_dev *pdev)
+{
+ struct toshsd_host *host = pci_get_drvdata(pdev);
+
+ mmc_remove_host(host->mmc);
+ toshsd_powerdown(host);
+ free_irq(pdev->irq, host);
+ pci_iounmap(pdev, host->ioaddr);
+ pci_release_regions(pdev);
+ mmc_free_host(host->mmc);
+ pci_set_drvdata(pdev, NULL);
+ pci_disable_device(pdev);
+}
+
+static const struct dev_pm_ops toshsd_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(toshsd_pm_suspend, toshsd_pm_resume)
+};
+
+static struct pci_driver toshsd_driver = {
+ .name = DRIVER_NAME,
+ .id_table = pci_ids,
+ .probe = toshsd_probe,
+ .remove = toshsd_remove,
+ .driver.pm = &toshsd_pm_ops,
+};
+
+static int __init toshsd_drv_init(void)
+{
+ return pci_register_driver(&toshsd_driver);
+}
+
+static void __exit toshsd_drv_exit(void)
+{
+ pci_unregister_driver(&toshsd_driver);
+}
+
+module_init(toshsd_drv_init);
+module_exit(toshsd_drv_exit);
+
+MODULE_AUTHOR("Ondrej Zary, Richard Betts");
+MODULE_DESCRIPTION("Toshiba PCI Secure Digital Host Controller Interface driver");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * Toshiba PCI Secure Digital Host Controller Interface driver
+ *
+ * Copyright (C) 2014 Ondrej Zary
+ * Copyright (C) 2007 Richard Betts, All Rights Reserved.
+ *
+ * Based on asic3_mmc.c Copyright (c) 2005 SDG Systems, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+#define HCLK 33000000 /* 33 MHz (PCI clock) */
+
+#define SD_PCICFG_CLKSTOP 0x40 /* 0x1f = clock controller, 0 = stop */
+#define SD_PCICFG_GATEDCLK 0x41 /* Gated clock */
+#define SD_PCICFG_CLKMODE 0x42 /* Control clock of SD controller */
+#define SD_PCICFG_PINSTATUS 0x44 /* R/O: read status of SD pins */
+#define SD_PCICFG_POWER1 0x48
+#define SD_PCICFG_POWER2 0x49
+#define SD_PCICFG_POWER3 0x4a
+#define SD_PCICFG_CARDDETECT 0x4c
+#define SD_PCICFG_SLOTS 0x50 /* R/O: define support slot number */
+#define SD_PCICFG_EXTGATECLK1 0xf0 /* Could be used for gated clock */
+#define SD_PCICFG_EXTGATECLK2 0xf1 /* Could be used for gated clock */
+#define SD_PCICFG_EXTGATECLK3 0xf9 /* Bit 1: double buffer/single buffer */
+#define SD_PCICFG_SDLED_ENABLE1 0xfa
+#define SD_PCICFG_SDLED_ENABLE2 0xfe
+
+#define SD_PCICFG_CLKMODE_DIV_DISABLE BIT(0)
+#define SD_PCICFG_CLKSTOP_ENABLE_ALL 0x1f
+#define SD_PCICFG_LED_ENABLE1_START 0x12
+#define SD_PCICFG_LED_ENABLE2_START 0x80
+
+#define SD_PCICFG_PWR1_33V 0x08 /* Set for 3.3 volts */
+#define SD_PCICFG_PWR1_OFF 0x00 /* Turn off power */
+#define SD_PCICFG_PWR2_AUTO 0x02
+
+#define SD_CMD 0x00 /* also for SDIO */
+#define SD_ARG0 0x04 /* also for SDIO */
+#define SD_ARG1 0x06 /* also for SDIO */
+#define SD_STOPINTERNAL 0x08
+#define SD_BLOCKCOUNT 0x0a /* also for SDIO */
+#define SD_RESPONSE0 0x0c /* also for SDIO */
+#define SD_RESPONSE1 0x0e /* also for SDIO */
+#define SD_RESPONSE2 0x10 /* also for SDIO */
+#define SD_RESPONSE3 0x12 /* also for SDIO */
+#define SD_RESPONSE4 0x14 /* also for SDIO */
+#define SD_RESPONSE5 0x16 /* also for SDIO */
+#define SD_RESPONSE6 0x18 /* also for SDIO */
+#define SD_RESPONSE7 0x1a /* also for SDIO */
+#define SD_CARDSTATUS 0x1c /* also for SDIO */
+#define SD_BUFFERCTRL 0x1e /* also for SDIO */
+#define SD_INTMASKCARD 0x20 /* also for SDIO */
+#define SD_INTMASKBUFFER 0x22 /* also for SDIO */
+#define SD_CARDCLOCKCTRL 0x24
+#define SD_CARDXFERDATALEN 0x26 /* also for SDIO */
+#define SD_CARDOPTIONSETUP 0x28 /* also for SDIO */
+#define SD_ERRORSTATUS0 0x2c /* also for SDIO */
+#define SD_ERRORSTATUS1 0x2e /* also for SDIO */
+#define SD_DATAPORT 0x30 /* also for SDIO */
+#define SD_TRANSACTIONCTRL 0x34 /* also for SDIO */
+#define SD_SOFTWARERESET 0xe0 /* also for SDIO */
+
+/* registers above marked "also for SDIO" and all SDIO registers below can be
+ * accessed at SDIO_BASE + reg address */
+#define SDIO_BASE 0x100
+
+#define SDIO_CARDPORTSEL 0x02
+#define SDIO_CARDINTCTRL 0x36
+#define SDIO_CLOCKNWAITCTRL 0x38
+#define SDIO_HOSTINFORMATION 0x3a
+#define SDIO_ERRORCTRL 0x3c
+#define SDIO_LEDCTRL 0x3e
+
+#define SD_TRANSCTL_SET BIT(8)
+
+#define SD_CARDCLK_DIV_DISABLE BIT(15)
+#define SD_CARDCLK_ENABLE_CLOCK BIT(8)
+#define SD_CARDCLK_CLK_DIV_512 BIT(7)
+#define SD_CARDCLK_CLK_DIV_256 BIT(6)
+#define SD_CARDCLK_CLK_DIV_128 BIT(5)
+#define SD_CARDCLK_CLK_DIV_64 BIT(4)
+#define SD_CARDCLK_CLK_DIV_32 BIT(3)
+#define SD_CARDCLK_CLK_DIV_16 BIT(2)
+#define SD_CARDCLK_CLK_DIV_8 BIT(1)
+#define SD_CARDCLK_CLK_DIV_4 BIT(0)
+#define SD_CARDCLK_CLK_DIV_2 0
+
+#define SD_CARDOPT_REQUIRED 0x000e
+#define SD_CARDOPT_DATA_RESP_TIMEOUT(x) (((x) & 0x0f) << 4) /* 4 bits */
+#define SD_CARDOPT_C2_MODULE_ABSENT BIT(14)
+#define SD_CARDOPT_DATA_XFR_WIDTH_1 (1 << 15)
+#define SD_CARDOPT_DATA_XFR_WIDTH_4 (0 << 15)
+
+#define SD_CMD_TYPE_CMD (0 << 6)
+#define SD_CMD_TYPE_ACMD (1 << 6)
+#define SD_CMD_TYPE_AUTHEN (2 << 6)
+#define SD_CMD_RESP_TYPE_NONE (3 << 8)
+#define SD_CMD_RESP_TYPE_EXT_R1 (4 << 8)
+#define SD_CMD_RESP_TYPE_EXT_R1B (5 << 8)
+#define SD_CMD_RESP_TYPE_EXT_R2 (6 << 8)
+#define SD_CMD_RESP_TYPE_EXT_R3 (7 << 8)
+#define SD_CMD_RESP_TYPE_EXT_R6 (4 << 8)
+#define SD_CMD_RESP_TYPE_EXT_R7 (4 << 8)
+#define SD_CMD_DATA_PRESENT BIT(11)
+#define SD_CMD_TRANSFER_READ BIT(12)
+#define SD_CMD_MULTI_BLOCK BIT(13)
+#define SD_CMD_SECURITY_CMD BIT(14)
+
+#define SD_STOPINT_ISSUE_CMD12 BIT(0)
+#define SD_STOPINT_AUTO_ISSUE_CMD12 BIT(8)
+
+#define SD_CARD_RESP_END BIT(0)
+#define SD_CARD_RW_END BIT(2)
+#define SD_CARD_CARD_REMOVED_0 BIT(3)
+#define SD_CARD_CARD_INSERTED_0 BIT(4)
+#define SD_CARD_PRESENT_0 BIT(5)
+#define SD_CARD_UNK6 BIT(6)
+#define SD_CARD_WRITE_PROTECT BIT(7)
+#define SD_CARD_CARD_REMOVED_3 BIT(8)
+#define SD_CARD_CARD_INSERTED_3 BIT(9)
+#define SD_CARD_PRESENT_3 BIT(10)
+
+#define SD_BUF_CMD_INDEX_ERR BIT(16)
+#define SD_BUF_CRC_ERR BIT(17)
+#define SD_BUF_STOP_BIT_END_ERR BIT(18)
+#define SD_BUF_DATA_TIMEOUT BIT(19)
+#define SD_BUF_OVERFLOW BIT(20)
+#define SD_BUF_UNDERFLOW BIT(21)
+#define SD_BUF_CMD_TIMEOUT BIT(22)
+#define SD_BUF_UNK7 BIT(23)
+#define SD_BUF_READ_ENABLE BIT(24)
+#define SD_BUF_WRITE_ENABLE BIT(25)
+#define SD_BUF_ILLEGAL_FUNCTION BIT(29)
+#define SD_BUF_CMD_BUSY BIT(30)
+#define SD_BUF_ILLEGAL_ACCESS BIT(31)
+
+#define SD_ERR0_RESP_CMD_ERR BIT(0)
+#define SD_ERR0_RESP_NON_CMD12_END_BIT_ERR BIT(2)
+#define SD_ERR0_RESP_CMD12_END_BIT_ERR BIT(3)
+#define SD_ERR0_READ_DATA_END_BIT_ERR BIT(4)
+#define SD_ERR0_WRITE_CRC_STATUS_END_BIT_ERR BIT(5)
+#define SD_ERR0_RESP_NON_CMD12_CRC_ERR BIT(8)
+#define SD_ERR0_RESP_CMD12_CRC_ERR BIT(9)
+#define SD_ERR0_READ_DATA_CRC_ERR BIT(10)
+#define SD_ERR0_WRITE_CMD_CRC_ERR BIT(11)
+
+#define SD_ERR1_NO_CMD_RESP BIT(16)
+#define SD_ERR1_TIMEOUT_READ_DATA BIT(20)
+#define SD_ERR1_TIMEOUT_CRS_STATUS BIT(21)
+#define SD_ERR1_TIMEOUT_CRC_BUSY BIT(22)
+
+#define IRQ_DONT_CARE_BITS (SD_CARD_PRESENT_3 \
+ | SD_CARD_WRITE_PROTECT \
+ | SD_CARD_UNK6 \
+ | SD_CARD_PRESENT_0 \
+ | SD_BUF_UNK7 \
+ | SD_BUF_CMD_BUSY)
+
+struct toshsd_host {
+ struct pci_dev *pdev;
+ struct mmc_host *mmc;
+
+ spinlock_t lock;
+
+ struct mmc_request *mrq;/* Current request */
+ struct mmc_command *cmd;/* Current command */
+ struct mmc_data *data; /* Current data request */
+
+ struct sg_mapping_iter sg_miter; /* for PIO */
+
+ void __iomem *ioaddr; /* mapped address */
+};
bond_slave_state_change(bond);
if (BOND_MODE(bond) == BOND_MODE_XOR)
bond_update_slave_arr(bond, NULL);
- } else if (do_failover) {
+ }
+ if (do_failover) {
block_netpoll_tx();
bond_select_active_slave(bond);
unblock_netpoll_tx();
bond_option_arp_ip_targets_clear(bond);
nla_for_each_nested(attr, data[IFLA_BOND_ARP_IP_TARGET], rem) {
- __be32 target = nla_get_be32(attr);
+ __be32 target;
+
+ if (nla_len(attr) < sizeof(target))
+ return -EINVAL;
+
+ target = nla_get_be32(attr);
bond_opt_initval(&newval, (__force u64)target);
err = __bond_opt_set(bond, BOND_OPT_ARP_TARGETS,
long rate;
u64 v64;
- /* Use CIA recommended sample points */
+ /* Use CiA recommended sample points */
if (bt->sample_point) {
sampl_pt = bt->sample_point;
} else {
BUG_ON(idx >= priv->echo_skb_max);
if (priv->echo_skb[idx]) {
- kfree_skb(priv->echo_skb[idx]);
+ dev_kfree_skb_any(priv->echo_skb[idx]);
priv->echo_skb[idx] = NULL;
}
}
config CAN_M_CAN
+ depends on HAS_IOMEM
tristate "Bosch M_CAN devices"
---help---
Say Y here if you want to support for Bosch M_CAN controller.
MRAM_CFG_NUM,
};
+/* Fast Bit Timing & Prescaler Register (FBTP) */
+#define FBTR_FBRP_MASK 0x1f
+#define FBTR_FBRP_SHIFT 16
+#define FBTR_FTSEG1_SHIFT 8
+#define FBTR_FTSEG1_MASK (0xf << FBTR_FTSEG1_SHIFT)
+#define FBTR_FTSEG2_SHIFT 4
+#define FBTR_FTSEG2_MASK (0x7 << FBTR_FTSEG2_SHIFT)
+#define FBTR_FSJW_SHIFT 0
+#define FBTR_FSJW_MASK 0x3
+
/* Test Register (TEST) */
#define TEST_LBCK BIT(4)
/* CC Control Register(CCCR) */
-#define CCCR_TEST BIT(7)
-#define CCCR_MON BIT(5)
-#define CCCR_CCE BIT(1)
-#define CCCR_INIT BIT(0)
+#define CCCR_TEST BIT(7)
+#define CCCR_CMR_MASK 0x3
+#define CCCR_CMR_SHIFT 10
+#define CCCR_CMR_CANFD 0x1
+#define CCCR_CMR_CANFD_BRS 0x2
+#define CCCR_CMR_CAN 0x3
+#define CCCR_CME_MASK 0x3
+#define CCCR_CME_SHIFT 8
+#define CCCR_CME_CAN 0
+#define CCCR_CME_CANFD 0x1
+#define CCCR_CME_CANFD_BRS 0x2
+#define CCCR_TEST BIT(7)
+#define CCCR_MON BIT(5)
+#define CCCR_CCE BIT(1)
+#define CCCR_INIT BIT(0)
+#define CCCR_CANFD 0x10
/* Bit Timing & Prescaler Register (BTP) */
#define BTR_BRP_MASK 0x3ff
/* Rx Buffer / FIFO Element Size Configuration (RXESC) */
#define M_CAN_RXESC_8BYTES 0x0
+#define M_CAN_RXESC_64BYTES 0x777
/* Tx Buffer Configuration(TXBC) */
#define TXBC_NDTB_OFF 16
/* Tx Buffer Element Size Configuration(TXESC) */
#define TXESC_TBDS_8BYTES 0x0
+#define TXESC_TBDS_64BYTES 0x7
/* Tx Event FIFO Con.guration (TXEFC) */
#define TXEFC_EFS_OFF 16
/* Message RAM Configuration (in bytes) */
#define SIDF_ELEMENT_SIZE 4
#define XIDF_ELEMENT_SIZE 8
-#define RXF0_ELEMENT_SIZE 16
-#define RXF1_ELEMENT_SIZE 16
+#define RXF0_ELEMENT_SIZE 72
+#define RXF1_ELEMENT_SIZE 72
#define RXB_ELEMENT_SIZE 16
#define TXE_ELEMENT_SIZE 8
-#define TXB_ELEMENT_SIZE 16
+#define TXB_ELEMENT_SIZE 72
/* Message RAM Elements */
#define M_CAN_FIFO_ID 0x0
#define M_CAN_FIFO_DATA(n) (0x8 + ((n) << 2))
/* Rx Buffer Element */
+/* R0 */
#define RX_BUF_ESI BIT(31)
#define RX_BUF_XTD BIT(30)
#define RX_BUF_RTR BIT(29)
+/* R1 */
+#define RX_BUF_ANMF BIT(31)
+#define RX_BUF_EDL BIT(21)
+#define RX_BUF_BRS BIT(20)
/* Tx Buffer Element */
+/* R0 */
#define TX_BUF_XTD BIT(30)
#define TX_BUF_RTR BIT(29)
if (enable) {
/* enable m_can configuration */
m_can_write(priv, M_CAN_CCCR, cccr | CCCR_INIT);
+ udelay(5);
/* CCCR.CCE can only be set/reset while CCCR.INIT = '1' */
m_can_write(priv, M_CAN_CCCR, cccr | CCCR_INIT | CCCR_CCE);
} else {
m_can_write(priv, M_CAN_ILE, 0x0);
}
-static void m_can_read_fifo(const struct net_device *dev, struct can_frame *cf,
- u32 rxfs)
+static void m_can_read_fifo(struct net_device *dev, u32 rxfs)
{
+ struct net_device_stats *stats = &dev->stats;
struct m_can_priv *priv = netdev_priv(dev);
- u32 id, fgi;
+ struct canfd_frame *cf;
+ struct sk_buff *skb;
+ u32 id, fgi, dlc;
+ int i;
/* calculate the fifo get index for where to read data */
fgi = (rxfs & RXFS_FGI_MASK) >> RXFS_FGI_OFF;
+ dlc = m_can_fifo_read(priv, fgi, M_CAN_FIFO_DLC);
+ if (dlc & RX_BUF_EDL)
+ skb = alloc_canfd_skb(dev, &cf);
+ else
+ skb = alloc_can_skb(dev, (struct can_frame **)&cf);
+ if (!skb) {
+ stats->rx_dropped++;
+ return;
+ }
+
+ if (dlc & RX_BUF_EDL)
+ cf->len = can_dlc2len((dlc >> 16) & 0x0F);
+ else
+ cf->len = get_can_dlc((dlc >> 16) & 0x0F);
+
id = m_can_fifo_read(priv, fgi, M_CAN_FIFO_ID);
if (id & RX_BUF_XTD)
cf->can_id = (id & CAN_EFF_MASK) | CAN_EFF_FLAG;
else
cf->can_id = (id >> 18) & CAN_SFF_MASK;
- if (id & RX_BUF_RTR) {
+ if (id & RX_BUF_ESI) {
+ cf->flags |= CANFD_ESI;
+ netdev_dbg(dev, "ESI Error\n");
+ }
+
+ if (!(dlc & RX_BUF_EDL) && (id & RX_BUF_RTR)) {
cf->can_id |= CAN_RTR_FLAG;
} else {
- id = m_can_fifo_read(priv, fgi, M_CAN_FIFO_DLC);
- cf->can_dlc = get_can_dlc((id >> 16) & 0x0F);
- *(u32 *)(cf->data + 0) = m_can_fifo_read(priv, fgi,
- M_CAN_FIFO_DATA(0));
- *(u32 *)(cf->data + 4) = m_can_fifo_read(priv, fgi,
- M_CAN_FIFO_DATA(1));
+ if (dlc & RX_BUF_BRS)
+ cf->flags |= CANFD_BRS;
+
+ for (i = 0; i < cf->len; i += 4)
+ *(u32 *)(cf->data + i) =
+ m_can_fifo_read(priv, fgi,
+ M_CAN_FIFO_DATA(i / 4));
}
/* acknowledge rx fifo 0 */
m_can_write(priv, M_CAN_RXF0A, fgi);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->len;
+
+ netif_receive_skb(skb);
}
static int m_can_do_rx_poll(struct net_device *dev, int quota)
{
struct m_can_priv *priv = netdev_priv(dev);
- struct net_device_stats *stats = &dev->stats;
- struct sk_buff *skb;
- struct can_frame *frame;
u32 pkts = 0;
u32 rxfs;
if (rxfs & RXFS_RFL)
netdev_warn(dev, "Rx FIFO 0 Message Lost\n");
- skb = alloc_can_skb(dev, &frame);
- if (!skb) {
- stats->rx_dropped++;
- return pkts;
- }
-
- m_can_read_fifo(dev, frame, rxfs);
-
- stats->rx_packets++;
- stats->rx_bytes += frame->can_dlc;
-
- netif_receive_skb(skb);
+ m_can_read_fifo(dev, rxfs);
quota--;
pkts++;
return 1;
}
+static int __m_can_get_berr_counter(const struct net_device *dev,
+ struct can_berr_counter *bec)
+{
+ struct m_can_priv *priv = netdev_priv(dev);
+ unsigned int ecr;
+
+ ecr = m_can_read(priv, M_CAN_ECR);
+ bec->rxerr = (ecr & ECR_REC_MASK) >> ECR_REC_SHIFT;
+ bec->txerr = ecr & ECR_TEC_MASK;
+
+ return 0;
+}
+
static int m_can_get_berr_counter(const struct net_device *dev,
struct can_berr_counter *bec)
{
struct m_can_priv *priv = netdev_priv(dev);
- unsigned int ecr;
int err;
err = clk_prepare_enable(priv->hclk);
return err;
}
- ecr = m_can_read(priv, M_CAN_ECR);
- bec->rxerr = (ecr & ECR_REC_MASK) >> ECR_REC_SHIFT;
- bec->txerr = ecr & ECR_TEC_MASK;
+ __m_can_get_berr_counter(dev, bec);
clk_disable_unprepare(priv->cclk);
clk_disable_unprepare(priv->hclk);
if (unlikely(!skb))
return 0;
- m_can_get_berr_counter(dev, &bec);
+ __m_can_get_berr_counter(dev, &bec);
switch (new_state) {
case CAN_STATE_ERROR_ACTIVE:
if ((psr & PSR_EP) &&
(priv->can.state != CAN_STATE_ERROR_PASSIVE)) {
- netdev_dbg(dev, "entered error warning state\n");
+ netdev_dbg(dev, "entered error passive state\n");
work_done += m_can_handle_state_change(dev,
CAN_STATE_ERROR_PASSIVE);
}
if ((psr & PSR_BO) &&
(priv->can.state != CAN_STATE_BUS_OFF)) {
- netdev_dbg(dev, "entered error warning state\n");
+ netdev_dbg(dev, "entered error bus off state\n");
work_done += m_can_handle_state_change(dev,
CAN_STATE_BUS_OFF);
}
{
if (irqstatus & IR_WDI)
netdev_err(dev, "Message RAM Watchdog event due to missing READY\n");
- if (irqstatus & IR_BEU)
+ if (irqstatus & IR_ELO)
netdev_err(dev, "Error Logging Overflow\n");
if (irqstatus & IR_BEU)
netdev_err(dev, "Bit Error Uncorrected\n");
.brp_inc = 1,
};
+static const struct can_bittiming_const m_can_data_bittiming_const = {
+ .name = KBUILD_MODNAME,
+ .tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */
+ .tseg1_max = 16,
+ .tseg2_min = 1, /* Time segment 2 = phase_seg2 */
+ .tseg2_max = 8,
+ .sjw_max = 4,
+ .brp_min = 1,
+ .brp_max = 32,
+ .brp_inc = 1,
+};
+
static int m_can_set_bittiming(struct net_device *dev)
{
struct m_can_priv *priv = netdev_priv(dev);
const struct can_bittiming *bt = &priv->can.bittiming;
+ const struct can_bittiming *dbt = &priv->can.data_bittiming;
u16 brp, sjw, tseg1, tseg2;
u32 reg_btp;
reg_btp = (brp << BTR_BRP_SHIFT) | (sjw << BTR_SJW_SHIFT) |
(tseg1 << BTR_TSEG1_SHIFT) | (tseg2 << BTR_TSEG2_SHIFT);
m_can_write(priv, M_CAN_BTP, reg_btp);
- netdev_dbg(dev, "setting BTP 0x%x\n", reg_btp);
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_FD) {
+ brp = dbt->brp - 1;
+ sjw = dbt->sjw - 1;
+ tseg1 = dbt->prop_seg + dbt->phase_seg1 - 1;
+ tseg2 = dbt->phase_seg2 - 1;
+ reg_btp = (brp << FBTR_FBRP_SHIFT) | (sjw << FBTR_FSJW_SHIFT) |
+ (tseg1 << FBTR_FTSEG1_SHIFT) |
+ (tseg2 << FBTR_FTSEG2_SHIFT);
+ m_can_write(priv, M_CAN_FBTP, reg_btp);
+ }
return 0;
}
m_can_config_endisable(priv, true);
- /* RX Buffer/FIFO Element Size 8 bytes data field */
- m_can_write(priv, M_CAN_RXESC, M_CAN_RXESC_8BYTES);
+ /* RX Buffer/FIFO Element Size 64 bytes data field */
+ m_can_write(priv, M_CAN_RXESC, M_CAN_RXESC_64BYTES);
/* Accept Non-matching Frames Into FIFO 0 */
m_can_write(priv, M_CAN_GFC, 0x0);
m_can_write(priv, M_CAN_TXBC, (1 << TXBC_NDTB_OFF) |
priv->mcfg[MRAM_TXB].off);
- /* only support 8 bytes firstly */
- m_can_write(priv, M_CAN_TXESC, TXESC_TBDS_8BYTES);
+ /* support 64 bytes payload */
+ m_can_write(priv, M_CAN_TXESC, TXESC_TBDS_64BYTES);
m_can_write(priv, M_CAN_TXEFC, (1 << TXEFC_EFS_OFF) |
priv->mcfg[MRAM_TXE].off);
RXFC_FWM_1 | priv->mcfg[MRAM_RXF1].off);
cccr = m_can_read(priv, M_CAN_CCCR);
- cccr &= ~(CCCR_TEST | CCCR_MON);
+ cccr &= ~(CCCR_TEST | CCCR_MON | (CCCR_CMR_MASK << CCCR_CMR_SHIFT) |
+ (CCCR_CME_MASK << CCCR_CME_SHIFT));
test = m_can_read(priv, M_CAN_TEST);
test &= ~TEST_LBCK;
test |= TEST_LBCK;
}
+ if (priv->can.ctrlmode & CAN_CTRLMODE_FD)
+ cccr |= CCCR_CME_CANFD_BRS << CCCR_CME_SHIFT;
+
m_can_write(priv, M_CAN_CCCR, cccr);
m_can_write(priv, M_CAN_TEST, test);
priv->dev = dev;
priv->can.bittiming_const = &m_can_bittiming_const;
+ priv->can.data_bittiming_const = &m_can_data_bittiming_const;
priv->can.do_set_mode = m_can_set_mode;
priv->can.do_get_berr_counter = m_can_get_berr_counter;
priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
CAN_CTRLMODE_LISTENONLY |
- CAN_CTRLMODE_BERR_REPORTING;
+ CAN_CTRLMODE_BERR_REPORTING |
+ CAN_CTRLMODE_FD;
return dev;
}
struct net_device *dev)
{
struct m_can_priv *priv = netdev_priv(dev);
- struct can_frame *cf = (struct can_frame *)skb->data;
- u32 id;
+ struct canfd_frame *cf = (struct canfd_frame *)skb->data;
+ u32 id, cccr;
+ int i;
if (can_dropped_invalid_skb(dev, skb))
return NETDEV_TX_OK;
/* message ram configuration */
m_can_fifo_write(priv, 0, M_CAN_FIFO_ID, id);
- m_can_fifo_write(priv, 0, M_CAN_FIFO_DLC, cf->can_dlc << 16);
- m_can_fifo_write(priv, 0, M_CAN_FIFO_DATA(0), *(u32 *)(cf->data + 0));
- m_can_fifo_write(priv, 0, M_CAN_FIFO_DATA(1), *(u32 *)(cf->data + 4));
+ m_can_fifo_write(priv, 0, M_CAN_FIFO_DLC, can_len2dlc(cf->len) << 16);
+
+ for (i = 0; i < cf->len; i += 4)
+ m_can_fifo_write(priv, 0, M_CAN_FIFO_DATA(i / 4),
+ *(u32 *)(cf->data + i));
+
can_put_echo_skb(skb, dev, 0);
+ if (priv->can.ctrlmode & CAN_CTRLMODE_FD) {
+ cccr = m_can_read(priv, M_CAN_CCCR);
+ cccr &= ~(CCCR_CMR_MASK << CCCR_CMR_SHIFT);
+ if (can_is_canfd_skb(skb)) {
+ if (cf->flags & CANFD_BRS)
+ cccr |= CCCR_CMR_CANFD_BRS << CCCR_CMR_SHIFT;
+ else
+ cccr |= CCCR_CMR_CANFD << CCCR_CMR_SHIFT;
+ } else {
+ cccr |= CCCR_CMR_CAN << CCCR_CMR_SHIFT;
+ }
+ m_can_write(priv, M_CAN_CCCR, cccr);
+ }
+
/* enable first TX buffer to start transfer */
m_can_write(priv, M_CAN_TXBTIE, 0x1);
m_can_write(priv, M_CAN_TXBAR, 0x1);
.ndo_open = m_can_open,
.ndo_stop = m_can_close,
.ndo_start_xmit = m_can_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
};
static int register_m_can_dev(struct net_device *dev)
struct resource *res;
void __iomem *addr;
u32 out_val[MRAM_CFG_LEN];
- int ret;
+ int i, start, end, ret;
/* message ram could be shared */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram");
priv->mcfg[MRAM_TXE].off, priv->mcfg[MRAM_TXE].num,
priv->mcfg[MRAM_TXB].off, priv->mcfg[MRAM_TXB].num);
+ /* initialize the entire Message RAM in use to avoid possible
+ * ECC/parity checksum errors when reading an uninitialized buffer
+ */
+ start = priv->mcfg[MRAM_SIDF].off;
+ end = priv->mcfg[MRAM_TXB].off +
+ priv->mcfg[MRAM_TXB].num * TXB_ELEMENT_SIZE;
+ for (i = start; i < end; i += 4)
+ writel(0x0, priv->mram_base + i);
+
return 0;
}
.ndo_open = rcar_can_open,
.ndo_stop = rcar_can_close,
.ndo_start_xmit = rcar_can_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
};
static void rcar_can_rx_pkt(struct rcar_can_priv *priv)
struct net_device *dev;
struct sja1000_priv *priv;
struct kvaser_pci *board;
- int err, init_step;
+ int err;
dev = alloc_sja1000dev(sizeof(struct kvaser_pci));
if (dev == NULL)
if (channel == 0) {
board->xilinx_ver =
ioread8(board->res_addr + XILINX_VERINT) >> 4;
- init_step = 2;
/* Assert PTADR# - we're in passive mode so the other bits are
not important */
priv->irq_flags = IRQF_SHARED;
dev->irq = pdev->irq;
- init_step = 4;
-
dev_info(&pdev->dev, "reg_base=%p conf_addr=%p irq=%d\n",
priv->reg_base, board->conf_addr, dev->irq);
if (urb->actual_length > CPC_HEADER_SIZE) {
struct ems_cpc_msg *msg;
u8 *ibuf = urb->transfer_buffer;
- u8 msg_count, again, start;
+ u8 msg_count, start;
msg_count = ibuf[0] & ~0x80;
- again = ibuf[0] & 0x80;
start = CPC_HEADER_SIZE;
{
struct esd_tx_urb_context *context = urb->context;
struct esd_usb2_net_priv *priv;
- struct esd_usb2 *dev;
struct net_device *netdev;
size_t size = sizeof(struct esd_usb2_msg);
priv = context->priv;
netdev = priv->netdev;
- dev = priv->usb2;
/* free up our allocated buffer */
usb_free_coherent(urb->dev, size,
}
}
unlink_all_urbs(dev);
+ kfree(dev);
}
}
.ndo_open = gs_can_open,
.ndo_stop = gs_can_close,
.ndo_start_xmit = gs_can_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
};
static struct gs_can *gs_make_candev(unsigned int channel, struct usb_interface *intf)
static int xcan_chip_start(struct net_device *ndev)
{
struct xcan_priv *priv = netdev_priv(ndev);
- u32 err, reg_msr, reg_sr_mask;
+ u32 reg_msr, reg_sr_mask;
+ int err;
unsigned long timeout;
/* Check if it is in reset mode */
.ndo_open = xcan_open,
.ndo_stop = xcan_close,
.ndo_start_xmit = xcan_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
};
/**
return IRQ_HANDLED;
}
+static int bcm_sf2_sw_rst(struct bcm_sf2_priv *priv)
+{
+ unsigned int timeout = 1000;
+ u32 reg;
+
+ reg = core_readl(priv, CORE_WATCHDOG_CTRL);
+ reg |= SOFTWARE_RESET | EN_CHIP_RST | EN_SW_RESET;
+ core_writel(priv, reg, CORE_WATCHDOG_CTRL);
+
+ do {
+ reg = core_readl(priv, CORE_WATCHDOG_CTRL);
+ if (!(reg & SOFTWARE_RESET))
+ break;
+
+ usleep_range(1000, 2000);
+ } while (timeout-- > 0);
+
+ if (timeout == 0)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
static int bcm_sf2_sw_setup(struct dsa_switch *ds)
{
const char *reg_names[BCM_SF2_REGS_NUM] = BCM_SF2_REGS_NAME;
*base = of_iomap(dn, i);
if (*base == NULL) {
pr_err("unable to find register: %s\n", reg_names[i]);
- return -ENODEV;
+ ret = -ENOMEM;
+ goto out_unmap;
}
base++;
}
+ ret = bcm_sf2_sw_rst(priv);
+ if (ret) {
+ pr_err("unable to software reset switch: %d\n", ret);
+ goto out_unmap;
+ }
+
/* Disable all interrupts and request them */
intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_MASK_SET);
intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR);
out_unmap:
base = &priv->core;
for (i = 0; i < BCM_SF2_REGS_NUM; i++) {
- iounmap(*base);
+ if (*base)
+ iounmap(*base);
base++;
}
return ret;
return 0;
}
-static int bcm_sf2_sw_rst(struct bcm_sf2_priv *priv)
-{
- unsigned int timeout = 1000;
- u32 reg;
-
- reg = core_readl(priv, CORE_WATCHDOG_CTRL);
- reg |= SOFTWARE_RESET | EN_CHIP_RST | EN_SW_RESET;
- core_writel(priv, reg, CORE_WATCHDOG_CTRL);
-
- do {
- reg = core_readl(priv, CORE_WATCHDOG_CTRL);
- if (!(reg & SOFTWARE_RESET))
- break;
-
- usleep_range(1000, 2000);
- } while (timeout-- > 0);
-
- if (timeout == 0)
- return -ETIMEDOUT;
-
- return 0;
-}
-
static int bcm_sf2_sw_resume(struct dsa_switch *ds)
{
struct bcm_sf2_priv *priv = ds_to_priv(ds);
xgene_enet_wr_mcx_mac(pdata, MAC_CONFIG_1_ADDR, data & ~TX_EN);
}
-static void xgene_enet_reset(struct xgene_enet_pdata *pdata)
+bool xgene_ring_mgr_init(struct xgene_enet_pdata *p)
+{
+ if (!ioread32(p->ring_csr_addr + CLKEN_ADDR))
+ return false;
+
+ if (ioread32(p->ring_csr_addr + SRST_ADDR))
+ return false;
+
+ return true;
+}
+
+static int xgene_enet_reset(struct xgene_enet_pdata *pdata)
{
u32 val;
+ if (!xgene_ring_mgr_init(pdata))
+ return -ENODEV;
+
clk_prepare_enable(pdata->clk);
clk_disable_unprepare(pdata->clk);
clk_prepare_enable(pdata->clk);
val |= SCAN_AUTO_INCR;
MGMT_CLOCK_SEL_SET(&val, 1);
xgene_enet_wr_mcx_mac(pdata, MII_MGMT_CONFIG_ADDR, val);
+
+ return 0;
}
static void xgene_gport_shutdown(struct xgene_enet_pdata *pdata)
#define BLOCK_ETH_MAC_OFFSET 0x0000
#define BLOCK_ETH_MAC_CSR_OFFSET 0x2800
+#define CLKEN_ADDR 0xc208
+#define SRST_ADDR 0xc200
+
#define MAC_ADDR_REG_OFFSET 0x00
#define MAC_COMMAND_REG_OFFSET 0x04
#define MAC_WRITE_REG_OFFSET 0x08
int xgene_enet_mdio_config(struct xgene_enet_pdata *pdata);
void xgene_enet_mdio_remove(struct xgene_enet_pdata *pdata);
+bool xgene_ring_mgr_init(struct xgene_enet_pdata *p);
extern struct xgene_mac_ops xgene_gmac_ops;
extern struct xgene_port_ops xgene_gport_ops;
struct device *dev = ndev_to_dev(ndev);
struct xgene_enet_desc_ring *rx_ring, *tx_ring, *cp_ring;
struct xgene_enet_desc_ring *buf_pool = NULL;
- u8 cpu_bufnum = 0, eth_bufnum = 0;
- u8 bp_bufnum = 0x20;
- u16 ring_id, ring_num = 0;
+ u8 cpu_bufnum = 0, eth_bufnum = START_ETH_BUFNUM;
+ u8 bp_bufnum = START_BP_BUFNUM;
+ u16 ring_id, ring_num = START_RING_NUM;
int ret;
/* allocate rx descriptor ring */
u16 dst_ring_num;
int ret;
- pdata->port_ops->reset(pdata);
+ ret = pdata->port_ops->reset(pdata);
+ if (ret)
+ return ret;
ret = xgene_enet_create_desc_rings(ndev);
if (ret) {
return ret;
err:
+ unregister_netdev(ndev);
free_netdev(ndev);
return ret;
}
#define SKB_BUFFER_SIZE (XGENE_ENET_MAX_MTU - NET_IP_ALIGN)
#define NUM_PKT_BUF 64
#define NUM_BUFPOOL 32
+#define START_ETH_BUFNUM 2
+#define START_BP_BUFNUM 0x22
+#define START_RING_NUM 8
#define PHY_POLL_LINK_ON (10 * HZ)
#define PHY_POLL_LINK_OFF (PHY_POLL_LINK_ON / 5)
};
struct xgene_port_ops {
- void (*reset)(struct xgene_enet_pdata *pdata);
+ int (*reset)(struct xgene_enet_pdata *pdata);
void (*cle_bypass)(struct xgene_enet_pdata *pdata,
u32 dst_ring_num, u16 bufpool_id);
void (*shutdown)(struct xgene_enet_pdata *pdata);
xgene_sgmac_rxtx(p, TX_EN, false);
}
-static void xgene_enet_reset(struct xgene_enet_pdata *p)
+static int xgene_enet_reset(struct xgene_enet_pdata *p)
{
+ if (!xgene_ring_mgr_init(p))
+ return -ENODEV;
+
clk_prepare_enable(p->clk);
clk_disable_unprepare(p->clk);
clk_prepare_enable(p->clk);
xgene_enet_ecc_init(p);
xgene_enet_config_ring_if_assoc(p);
+
+ return 0;
}
static void xgene_enet_cle_bypass(struct xgene_enet_pdata *p,
xgene_enet_wr_mac(pdata, AXGMAC_CONFIG_1, data & ~HSTTFEN);
}
-static void xgene_enet_reset(struct xgene_enet_pdata *pdata)
+static int xgene_enet_reset(struct xgene_enet_pdata *pdata)
{
+ if (!xgene_ring_mgr_init(pdata))
+ return -ENODEV;
+
clk_prepare_enable(pdata->clk);
clk_disable_unprepare(pdata->clk);
clk_prepare_enable(pdata->clk);
xgene_enet_ecc_init(pdata);
xgene_enet_config_ring_if_assoc(pdata);
+
+ return 0;
}
static void xgene_enet_xgcle_bypass(struct xgene_enet_pdata *pdata,
/* We just need one DMA descriptor which is DMA-able, since writing to
* the port will allocate a new descriptor in its internal linked-list
*/
- p = dma_zalloc_coherent(kdev, 1, &ring->desc_dma, GFP_KERNEL);
+ p = dma_zalloc_coherent(kdev, sizeof(struct dma_desc), &ring->desc_dma,
+ GFP_KERNEL);
if (!p) {
netif_err(priv, hw, priv->netdev, "DMA alloc failed\n");
return -ENOMEM;
if (!(reg & TDMA_DISABLED))
netdev_warn(priv->netdev, "TDMA not stopped!\n");
+ /* ring->cbs is the last part in bcm_sysport_init_tx_ring which could
+ * fail, so by checking this pointer we know whether the TX ring was
+ * fully initialized or not.
+ */
+ if (!ring->cbs)
+ return;
+
napi_disable(&ring->napi);
netif_napi_del(&ring->napi);
ring->cbs = NULL;
if (ring->desc_dma) {
- dma_free_coherent(kdev, 1, ring->desc_cpu, ring->desc_dma);
+ dma_free_coherent(kdev, sizeof(struct dma_desc),
+ ring->desc_cpu, ring->desc_dma);
ring->desc_dma = 0;
}
ring->size = 0;
goto err_irq0;
}
+ /* Re-configure the port multiplexer towards the PHY device */
+ bcmgenet_mii_config(priv->dev, false);
+
+ phy_connect_direct(dev, priv->phydev, bcmgenet_mii_setup,
+ priv->phy_interface);
+
bcmgenet_netif_start(dev);
return 0;
bcmgenet_netif_stop(dev);
+ /* Really kill the PHY state machine and disconnect from it */
+ phy_disconnect(priv->phydev);
+
/* Disable MAC receive */
umac_enable_set(priv, CMD_RX_EN, false);
phy_init_hw(priv->phydev);
/* Speed settings must be restored */
- bcmgenet_mii_config(priv->dev);
+ bcmgenet_mii_config(priv->dev, false);
/* disable ethernet MAC while updating its registers */
umac_enable_set(priv, CMD_TX_EN | CMD_RX_EN, false);
/* MDIO routines */
int bcmgenet_mii_init(struct net_device *dev);
-int bcmgenet_mii_config(struct net_device *dev);
+int bcmgenet_mii_config(struct net_device *dev, bool init);
void bcmgenet_mii_exit(struct net_device *dev);
void bcmgenet_mii_reset(struct net_device *dev);
+void bcmgenet_mii_setup(struct net_device *dev);
/* Wake-on-LAN routines */
void bcmgenet_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol);
/* setup netdev link state when PHY link status change and
* update UMAC and RGMII block when link up
*/
-static void bcmgenet_mii_setup(struct net_device *dev)
+void bcmgenet_mii_setup(struct net_device *dev)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct phy_device *phydev = priv->phydev;
bcmgenet_sys_writel(priv, reg, SYS_PORT_CTRL);
}
-int bcmgenet_mii_config(struct net_device *dev)
+int bcmgenet_mii_config(struct net_device *dev, bool init)
{
struct bcmgenet_priv *priv = netdev_priv(dev);
struct phy_device *phydev = priv->phydev;
return -EINVAL;
}
- dev_info(kdev, "configuring instance for %s\n", phy_name);
+ if (init)
+ dev_info(kdev, "configuring instance for %s\n", phy_name);
return 0;
}
* PHY speed which is needed for bcmgenet_mii_config() to configure
* things appropriately.
*/
- ret = bcmgenet_mii_config(dev);
+ ret = bcmgenet_mii_config(dev, true);
if (ret) {
phy_disconnect(priv->phydev);
return ret;
if (tnapi->rx_rcb)
memset(tnapi->rx_rcb, 0, TG3_RX_RCB_RING_BYTES(tp));
- if (tg3_rx_prodring_alloc(tp, &tnapi->prodring)) {
+ if (tnapi->prodring.rx_std &&
+ tg3_rx_prodring_alloc(tp, &tnapi->prodring)) {
tg3_free_rings(tp);
return -ENOMEM;
}
app.protocol = dcb->app_priority[i].protocolid;
if (dcb->dcb_version == FW_PORT_DCB_VER_IEEE) {
+ app.priority = dcb->app_priority[i].user_prio_map;
app.selector = dcb->app_priority[i].sel_field + 1;
- err = dcb_ieee_setapp(dev, &app);
+ err = dcb_ieee_delapp(dev, &app);
} else {
app.selector = !!(dcb->app_priority[i].sel_field);
err = dcb_setapp(dev, &app);
case CXGB4_DCB_INPUT_FW_ENABLED: {
/* we're going to use Firmware DCB */
dcb->state = CXGB4_DCB_STATE_FW_INCOMPLETE;
- dcb->supported = CXGB4_DCBX_FW_SUPPORT;
+ dcb->supported = DCB_CAP_DCBX_LLD_MANAGED;
+ if (dcb->dcb_version == FW_PORT_DCB_VER_IEEE)
+ dcb->supported |= DCB_CAP_DCBX_VER_IEEE;
+ else
+ dcb->supported |= DCB_CAP_DCBX_VER_CEE;
break;
}
*up_tc_map = (1 << tc);
/* prio_type is link strict */
- *prio_type = 0x2;
+ if (*pgid != 0xF)
+ *prio_type = 0x2;
}
static void cxgb4_getpgtccfg_tx(struct net_device *dev, int tc,
u8 *prio_type, u8 *pgid, u8 *bw_per,
u8 *up_tc_map)
{
- return cxgb4_getpgtccfg(dev, tc, prio_type, pgid, bw_per, up_tc_map, 1);
+ /* tc 0 is written at MSB position */
+ return cxgb4_getpgtccfg(dev, (7 - tc), prio_type, pgid, bw_per,
+ up_tc_map, 1);
}
u8 *prio_type, u8 *pgid, u8 *bw_per,
u8 *up_tc_map)
{
- return cxgb4_getpgtccfg(dev, tc, prio_type, pgid, bw_per, up_tc_map, 0);
+ /* tc 0 is written at MSB position */
+ return cxgb4_getpgtccfg(dev, (7 - tc), prio_type, pgid, bw_per,
+ up_tc_map, 0);
}
static void cxgb4_setpgtccfg_tx(struct net_device *dev, int tc,
struct fw_port_cmd pcmd;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = pi->adapter;
+ int fw_tc = 7 - tc;
u32 _pgid;
int err;
}
_pgid = be32_to_cpu(pcmd.u.dcb.pgid.pgid);
- _pgid &= ~(0xF << (tc * 4));
- _pgid |= pgid << (tc * 4);
+ _pgid &= ~(0xF << (fw_tc * 4));
+ _pgid |= pgid << (fw_tc * 4);
pcmd.u.dcb.pgid.pgid = cpu_to_be32(_pgid);
INIT_PORT_DCB_WRITE_CMD(pcmd, pi->port_id);
priority >= CXGB4_MAX_PRIORITY)
*pfccfg = 0;
else
- *pfccfg = (pi->dcb.pfcen >> priority) & 1;
+ *pfccfg = (pi->dcb.pfcen >> (7 - priority)) & 1;
}
/* Enable/disable Priority Pause Frames for the specified Traffic Class
pcmd.u.dcb.pfc.pfcen = pi->dcb.pfcen;
if (pfccfg)
- pcmd.u.dcb.pfc.pfcen |= (1 << priority);
+ pcmd.u.dcb.pfc.pfcen |= (1 << (7 - priority));
else
- pcmd.u.dcb.pfc.pfcen &= (~(1 << priority));
+ pcmd.u.dcb.pfc.pfcen &= (~(1 << (7 - priority)));
err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd);
if (err != FW_PORT_DCB_CFG_SUCCESS) {
pgid = be32_to_cpu(pcmd.u.dcb.pgid.pgid);
for (i = 0; i < CXGB4_MAX_PRIORITY; i++)
- pg->prio_pg[i] = (pgid >> (i * 4)) & 0xF;
+ pg->prio_pg[7 - i] = (pgid >> (i * 4)) & 0xF;
INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id);
pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE;
SUPPORTED_10000baseKR_Full | SUPPORTED_1000baseKX_Full |
SUPPORTED_10000baseKX4_Full;
else if (type == FW_PORT_TYPE_FIBER_XFI ||
- type == FW_PORT_TYPE_FIBER_XAUI || type == FW_PORT_TYPE_SFP)
+ type == FW_PORT_TYPE_FIBER_XAUI || type == FW_PORT_TYPE_SFP) {
v |= SUPPORTED_FIBRE;
- else if (type == FW_PORT_TYPE_BP40_BA)
+ if (caps & FW_PORT_CAP_SPEED_1G)
+ v |= SUPPORTED_1000baseT_Full;
+ if (caps & FW_PORT_CAP_SPEED_10G)
+ v |= SUPPORTED_10000baseT_Full;
+ } else if (type == FW_PORT_TYPE_BP40_BA)
v |= SUPPORTED_40000baseSR4_Full;
if (caps & FW_PORT_CAP_ANEG)
int t4_sge_init(struct adapter *adap)
{
struct sge *s = &adap->sge;
- u32 sge_control, sge_conm_ctrl;
+ u32 sge_control, sge_control2, sge_conm_ctrl;
+ unsigned int ingpadboundary, ingpackboundary;
int ret, egress_threshold;
/*
sge_control = t4_read_reg(adap, SGE_CONTROL);
s->pktshift = PKTSHIFT_GET(sge_control);
s->stat_len = (sge_control & EGRSTATUSPAGESIZE_MASK) ? 128 : 64;
- s->fl_align = 1 << (INGPADBOUNDARY_GET(sge_control) +
- X_INGPADBOUNDARY_SHIFT);
+
+ /* T4 uses a single control field to specify both the PCIe Padding and
+ * Packing Boundary. T5 introduced the ability to specify these
+ * separately. The actual Ingress Packet Data alignment boundary
+ * within Packed Buffer Mode is the maximum of these two
+ * specifications.
+ */
+ ingpadboundary = 1 << (INGPADBOUNDARY_GET(sge_control) +
+ X_INGPADBOUNDARY_SHIFT);
+ if (is_t4(adap->params.chip)) {
+ s->fl_align = ingpadboundary;
+ } else {
+ /* T5 has a different interpretation of one of the PCIe Packing
+ * Boundary values.
+ */
+ sge_control2 = t4_read_reg(adap, SGE_CONTROL2_A);
+ ingpackboundary = INGPACKBOUNDARY_G(sge_control2);
+ if (ingpackboundary == INGPACKBOUNDARY_16B_X)
+ ingpackboundary = 16;
+ else
+ ingpackboundary = 1 << (ingpackboundary +
+ INGPACKBOUNDARY_SHIFT_X);
+
+ s->fl_align = max(ingpadboundary, ingpackboundary);
+ }
if (adap->flags & USING_SOFT_PARAMS)
ret = t4_sge_init_soft(adap);
HOSTPAGESIZEPF6(sge_hps) |
HOSTPAGESIZEPF7(sge_hps));
- t4_set_reg_field(adap, SGE_CONTROL,
- INGPADBOUNDARY_MASK |
- EGRSTATUSPAGESIZE_MASK,
- INGPADBOUNDARY(fl_align_log - 5) |
- EGRSTATUSPAGESIZE(stat_len != 64));
-
+ if (is_t4(adap->params.chip)) {
+ t4_set_reg_field(adap, SGE_CONTROL,
+ INGPADBOUNDARY_MASK |
+ EGRSTATUSPAGESIZE_MASK,
+ INGPADBOUNDARY(fl_align_log - 5) |
+ EGRSTATUSPAGESIZE(stat_len != 64));
+ } else {
+ /* T5 introduced the separation of the Free List Padding and
+ * Packing Boundaries. Thus, we can select a smaller Padding
+ * Boundary to avoid uselessly chewing up PCIe Link and Memory
+ * Bandwidth, and use a Packing Boundary which is large enough
+ * to avoid false sharing between CPUs, etc.
+ *
+ * For the PCI Link, the smaller the Padding Boundary the
+ * better. For the Memory Controller, a smaller Padding
+ * Boundary is better until we cross under the Memory Line
+ * Size (the minimum unit of transfer to/from Memory). If we
+ * have a Padding Boundary which is smaller than the Memory
+ * Line Size, that'll involve a Read-Modify-Write cycle on the
+ * Memory Controller which is never good. For T5 the smallest
+ * Padding Boundary which we can select is 32 bytes which is
+ * larger than any known Memory Controller Line Size so we'll
+ * use that.
+ *
+ * T5 has a different interpretation of the "0" value for the
+ * Packing Boundary. This corresponds to 16 bytes instead of
+ * the expected 32 bytes. We never have a Packing Boundary
+ * less than 32 bytes so we can't use that special value but
+ * on the other hand, if we wanted 32 bytes, the best we can
+ * really do is 64 bytes.
+ */
+ if (fl_align <= 32) {
+ fl_align = 64;
+ fl_align_log = 6;
+ }
+ t4_set_reg_field(adap, SGE_CONTROL,
+ INGPADBOUNDARY_MASK |
+ EGRSTATUSPAGESIZE_MASK,
+ INGPADBOUNDARY(INGPCIEBOUNDARY_32B_X) |
+ EGRSTATUSPAGESIZE(stat_len != 64));
+ t4_set_reg_field(adap, SGE_CONTROL2_A,
+ INGPACKBOUNDARY_V(INGPACKBOUNDARY_M),
+ INGPACKBOUNDARY_V(fl_align_log -
+ INGPACKBOUNDARY_SHIFT_X));
+ }
/*
* Adjust various SGE Free List Host Buffer Sizes.
*
#define X_INGPADBOUNDARY_SHIFT 5
#define SGE_CONTROL 0x1008
+#define SGE_CONTROL2_A 0x1124
#define DCASYSTYPE 0x00080000U
#define RXPKTCPLMODE_MASK 0x00040000U
#define RXPKTCPLMODE_SHIFT 18
#define PKTSHIFT_SHIFT 10
#define PKTSHIFT(x) ((x) << PKTSHIFT_SHIFT)
#define PKTSHIFT_GET(x) (((x) & PKTSHIFT_MASK) >> PKTSHIFT_SHIFT)
+#define INGPCIEBOUNDARY_32B_X 0
#define INGPCIEBOUNDARY_MASK 0x00000380U
#define INGPCIEBOUNDARY_SHIFT 7
#define INGPCIEBOUNDARY(x) ((x) << INGPCIEBOUNDARY_SHIFT)
#define INGPADBOUNDARY(x) ((x) << INGPADBOUNDARY_SHIFT)
#define INGPADBOUNDARY_GET(x) (((x) & INGPADBOUNDARY_MASK) \
>> INGPADBOUNDARY_SHIFT)
+#define INGPACKBOUNDARY_16B_X 0
+#define INGPACKBOUNDARY_SHIFT_X 5
+
+#define INGPACKBOUNDARY_S 16
+#define INGPACKBOUNDARY_M 0x7U
+#define INGPACKBOUNDARY_V(x) ((x) << INGPACKBOUNDARY_S)
+#define INGPACKBOUNDARY_G(x) (((x) >> INGPACKBOUNDARY_S) \
+ & INGPACKBOUNDARY_M)
#define EGRPCIEBOUNDARY_MASK 0x0000000eU
#define EGRPCIEBOUNDARY_SHIFT 1
#define EGRPCIEBOUNDARY(x) ((x) << EGRPCIEBOUNDARY_SHIFT)
u16 timer_val[SGE_NTIMERS]; /* interrupt holdoff timer array */
u8 counter_val[SGE_NCOUNTERS]; /* interrupt RX threshold array */
+ /* Decoded Adapter Parameters.
+ */
+ u32 fl_pg_order; /* large page allocation size */
+ u32 stat_len; /* length of status page at ring end */
+ u32 pktshift; /* padding between CPL & packet data */
+ u32 fl_align; /* response queue message alignment */
+ u32 fl_starve_thres; /* Free List starvation threshold */
+
/*
* Reverse maps from Absolute Queue IDs to associated queue pointers.
* The absolute Queue IDs are in a compact range which start at a
#include "../cxgb4/t4fw_api.h"
#include "../cxgb4/t4_msg.h"
-/*
- * Decoded Adapter Parameters.
- */
-static u32 FL_PG_ORDER; /* large page allocation size */
-static u32 STAT_LEN; /* length of status page at ring end */
-static u32 PKTSHIFT; /* padding between CPL and packet data */
-static u32 FL_ALIGN; /* response queue message alignment */
-
/*
* Constants ...
*/
TX_QCHECK_PERIOD = (HZ / 2),
MAX_TIMER_TX_RECLAIM = 100,
- /*
- * An FL with <= FL_STARVE_THRES buffers is starving and a periodic
- * timer will attempt to refill it.
- */
- FL_STARVE_THRES = 4,
-
/*
* Suspend an Ethernet TX queue with fewer available descriptors than
* this. We always want to have room for a maximum sized packet:
/**
* fl_starving - return whether a Free List is starving.
+ * @adapter: pointer to the adapter
* @fl: the Free List
*
* Tests specified Free List to see whether the number of buffers
* available to the hardware has falled below our "starvation"
* threshold.
*/
-static inline bool fl_starving(const struct sge_fl *fl)
+static inline bool fl_starving(const struct adapter *adapter,
+ const struct sge_fl *fl)
{
- return fl->avail - fl->pend_cred <= FL_STARVE_THRES;
+ const struct sge *s = &adapter->sge;
+
+ return fl->avail - fl->pend_cred <= s->fl_starve_thres;
}
/**
/**
* get_buf_size - return the size of an RX Free List buffer.
+ * @adapter: pointer to the associated adapter
* @sdesc: pointer to the software buffer descriptor
*/
-static inline int get_buf_size(const struct rx_sw_desc *sdesc)
+static inline int get_buf_size(const struct adapter *adapter,
+ const struct rx_sw_desc *sdesc)
{
- return FL_PG_ORDER > 0 && (sdesc->dma_addr & RX_LARGE_BUF)
- ? (PAGE_SIZE << FL_PG_ORDER)
- : PAGE_SIZE;
+ const struct sge *s = &adapter->sge;
+
+ return (s->fl_pg_order > 0 && (sdesc->dma_addr & RX_LARGE_BUF)
+ ? (PAGE_SIZE << s->fl_pg_order) : PAGE_SIZE);
}
/**
if (is_buf_mapped(sdesc))
dma_unmap_page(adapter->pdev_dev, get_buf_addr(sdesc),
- get_buf_size(sdesc), PCI_DMA_FROMDEVICE);
+ get_buf_size(adapter, sdesc),
+ PCI_DMA_FROMDEVICE);
put_page(sdesc->page);
sdesc->page = NULL;
if (++fl->cidx == fl->size)
if (is_buf_mapped(sdesc))
dma_unmap_page(adapter->pdev_dev, get_buf_addr(sdesc),
- get_buf_size(sdesc), PCI_DMA_FROMDEVICE);
+ get_buf_size(adapter, sdesc),
+ PCI_DMA_FROMDEVICE);
sdesc->page = NULL;
if (++fl->cidx == fl->size)
fl->cidx = 0;
static unsigned int refill_fl(struct adapter *adapter, struct sge_fl *fl,
int n, gfp_t gfp)
{
+ struct sge *s = &adapter->sge;
struct page *page;
dma_addr_t dma_addr;
unsigned int cred = fl->avail;
* If we don't support large pages, drop directly into the small page
* allocation code.
*/
- if (FL_PG_ORDER == 0)
+ if (s->fl_pg_order == 0)
goto alloc_small_pages;
while (n) {
page = alloc_pages(gfp | __GFP_COMP | __GFP_NOWARN,
- FL_PG_ORDER);
+ s->fl_pg_order);
if (unlikely(!page)) {
/*
* We've failed inour attempt to allocate a "large
fl->large_alloc_failed++;
break;
}
- poison_buf(page, PAGE_SIZE << FL_PG_ORDER);
+ poison_buf(page, PAGE_SIZE << s->fl_pg_order);
dma_addr = dma_map_page(adapter->pdev_dev, page, 0,
- PAGE_SIZE << FL_PG_ORDER,
+ PAGE_SIZE << s->fl_pg_order,
PCI_DMA_FROMDEVICE);
if (unlikely(dma_mapping_error(adapter->pdev_dev, dma_addr))) {
/*
* because DMA mapping resources are typically
* critical resources once they become scarse.
*/
- __free_pages(page, FL_PG_ORDER);
+ __free_pages(page, s->fl_pg_order);
goto out;
}
dma_addr |= RX_LARGE_BUF;
fl->pend_cred += cred;
ring_fl_db(adapter, fl);
- if (unlikely(fl_starving(fl))) {
+ if (unlikely(fl_starving(adapter, fl))) {
smp_wmb();
set_bit(fl->cntxt_id, adapter->sge.starving_fl);
}
static void do_gro(struct sge_eth_rxq *rxq, const struct pkt_gl *gl,
const struct cpl_rx_pkt *pkt)
{
+ struct adapter *adapter = rxq->rspq.adapter;
+ struct sge *s = &adapter->sge;
int ret;
struct sk_buff *skb;
return;
}
- copy_frags(skb, gl, PKTSHIFT);
- skb->len = gl->tot_len - PKTSHIFT;
+ copy_frags(skb, gl, s->pktshift);
+ skb->len = gl->tot_len - s->pktshift;
skb->data_len = skb->len;
skb->truesize += skb->data_len;
skb->ip_summed = CHECKSUM_UNNECESSARY;
bool csum_ok = pkt->csum_calc && !pkt->err_vec &&
(rspq->netdev->features & NETIF_F_RXCSUM);
struct sge_eth_rxq *rxq = container_of(rspq, struct sge_eth_rxq, rspq);
+ struct adapter *adapter = rspq->adapter;
+ struct sge *s = &adapter->sge;
/*
* If this is a good TCP packet and we have Generic Receive Offload
rxq->stats.rx_drops++;
return 0;
}
- __skb_pull(skb, PKTSHIFT);
+ __skb_pull(skb, s->pktshift);
skb->protocol = eth_type_trans(skb, rspq->netdev);
skb_record_rx_queue(skb, rspq->idx);
rxq->stats.pkts++;
static int process_responses(struct sge_rspq *rspq, int budget)
{
struct sge_eth_rxq *rxq = container_of(rspq, struct sge_eth_rxq, rspq);
+ struct adapter *adapter = rspq->adapter;
+ struct sge *s = &adapter->sge;
int budget_left = budget;
while (likely(budget_left)) {
BUG_ON(frag >= MAX_SKB_FRAGS);
BUG_ON(rxq->fl.avail == 0);
sdesc = &rxq->fl.sdesc[rxq->fl.cidx];
- bufsz = get_buf_size(sdesc);
+ bufsz = get_buf_size(adapter, sdesc);
fp->page = sdesc->page;
fp->offset = rspq->offset;
fp->size = min(bufsz, len);
*/
ret = rspq->handler(rspq, rspq->cur_desc, &gl);
if (likely(ret == 0))
- rspq->offset += ALIGN(fp->size, FL_ALIGN);
+ rspq->offset += ALIGN(fp->size, s->fl_align);
else
restore_rx_bufs(&gl, &rxq->fl, frag);
} else if (likely(rsp_type == RSP_TYPE_CPL)) {
* schedule napi but the FL is no longer starving.
* No biggie.
*/
- if (fl_starving(fl)) {
+ if (fl_starving(adapter, fl)) {
struct sge_eth_rxq *rxq;
rxq = container_of(fl, struct sge_eth_rxq, fl);
int intr_dest,
struct sge_fl *fl, rspq_handler_t hnd)
{
+ struct sge *s = &adapter->sge;
struct port_info *pi = netdev_priv(dev);
struct fw_iq_cmd cmd, rpl;
int ret, iqandst, flsz = 0;
fl->size = roundup(fl->size, FL_PER_EQ_UNIT);
fl->desc = alloc_ring(adapter->pdev_dev, fl->size,
sizeof(__be64), sizeof(struct rx_sw_desc),
- &fl->addr, &fl->sdesc, STAT_LEN);
+ &fl->addr, &fl->sdesc, s->stat_len);
if (!fl->desc) {
ret = -ENOMEM;
goto err;
* free list ring) in Egress Queue Units.
*/
flsz = (fl->size / FL_PER_EQ_UNIT +
- STAT_LEN / EQ_UNIT);
+ s->stat_len / EQ_UNIT);
/*
* Fill in all the relevant firmware Ingress Queue Command
struct net_device *dev, struct netdev_queue *devq,
unsigned int iqid)
{
+ struct sge *s = &adapter->sge;
int ret, nentries;
struct fw_eq_eth_cmd cmd, rpl;
struct port_info *pi = netdev_priv(dev);
* Calculate the size of the hardware TX Queue (including the Status
* Page on the end of the TX Queue) in units of TX Descriptors.
*/
- nentries = txq->q.size + STAT_LEN / sizeof(struct tx_desc);
+ nentries = txq->q.size + s->stat_len / sizeof(struct tx_desc);
/*
* Allocate the hardware ring for the TX ring (with space for its
txq->q.desc = alloc_ring(adapter->pdev_dev, txq->q.size,
sizeof(struct tx_desc),
sizeof(struct tx_sw_desc),
- &txq->q.phys_addr, &txq->q.sdesc, STAT_LEN);
+ &txq->q.phys_addr, &txq->q.sdesc, s->stat_len);
if (!txq->q.desc)
return -ENOMEM;
*/
static void free_txq(struct adapter *adapter, struct sge_txq *tq)
{
+ struct sge *s = &adapter->sge;
+
dma_free_coherent(adapter->pdev_dev,
- tq->size * sizeof(*tq->desc) + STAT_LEN,
+ tq->size * sizeof(*tq->desc) + s->stat_len,
tq->desc, tq->phys_addr);
tq->cntxt_id = 0;
tq->sdesc = NULL;
static void free_rspq_fl(struct adapter *adapter, struct sge_rspq *rspq,
struct sge_fl *fl)
{
+ struct sge *s = &adapter->sge;
unsigned int flid = fl ? fl->cntxt_id : 0xffff;
t4vf_iq_free(adapter, FW_IQ_TYPE_FL_INT_CAP,
if (fl) {
free_rx_bufs(adapter, fl, fl->avail);
dma_free_coherent(adapter->pdev_dev,
- fl->size * sizeof(*fl->desc) + STAT_LEN,
+ fl->size * sizeof(*fl->desc) + s->stat_len,
fl->desc, fl->addr);
kfree(fl->sdesc);
fl->sdesc = NULL;
u32 fl0 = sge_params->sge_fl_buffer_size[0];
u32 fl1 = sge_params->sge_fl_buffer_size[1];
struct sge *s = &adapter->sge;
+ unsigned int ingpadboundary, ingpackboundary;
/*
* Start by vetting the basic SGE parameters which have been set up by
* Now translate the adapter parameters into our internal forms.
*/
if (fl1)
- FL_PG_ORDER = ilog2(fl1) - PAGE_SHIFT;
- STAT_LEN = ((sge_params->sge_control & EGRSTATUSPAGESIZE_MASK)
- ? 128 : 64);
- PKTSHIFT = PKTSHIFT_GET(sge_params->sge_control);
- FL_ALIGN = 1 << (INGPADBOUNDARY_GET(sge_params->sge_control) +
- SGE_INGPADBOUNDARY_SHIFT);
+ s->fl_pg_order = ilog2(fl1) - PAGE_SHIFT;
+ s->stat_len = ((sge_params->sge_control & EGRSTATUSPAGESIZE_MASK)
+ ? 128 : 64);
+ s->pktshift = PKTSHIFT_GET(sge_params->sge_control);
+
+ /* T4 uses a single control field to specify both the PCIe Padding and
+ * Packing Boundary. T5 introduced the ability to specify these
+ * separately. The actual Ingress Packet Data alignment boundary
+ * within Packed Buffer Mode is the maximum of these two
+ * specifications. (Note that it makes no real practical sense to
+ * have the Pading Boudary be larger than the Packing Boundary but you
+ * could set the chip up that way and, in fact, legacy T4 code would
+ * end doing this because it would initialize the Padding Boundary and
+ * leave the Packing Boundary initialized to 0 (16 bytes).)
+ */
+ ingpadboundary = 1 << (INGPADBOUNDARY_GET(sge_params->sge_control) +
+ X_INGPADBOUNDARY_SHIFT);
+ if (is_t4(adapter->params.chip)) {
+ s->fl_align = ingpadboundary;
+ } else {
+ /* T5 has a different interpretation of one of the PCIe Packing
+ * Boundary values.
+ */
+ ingpackboundary = INGPACKBOUNDARY_G(sge_params->sge_control2);
+ if (ingpackboundary == INGPACKBOUNDARY_16B_X)
+ ingpackboundary = 16;
+ else
+ ingpackboundary = 1 << (ingpackboundary +
+ INGPACKBOUNDARY_SHIFT_X);
+
+ s->fl_align = max(ingpadboundary, ingpackboundary);
+ }
+
+ /* A FL with <= fl_starve_thres buffers is starving and a periodic
+ * timer will attempt to refill it. This needs to be larger than the
+ * SGE's Egress Congestion Threshold. If it isn't, then we can get
+ * stuck waiting for new packets while the SGE is waiting for us to
+ * give it more Free List entries. (Note that the SGE's Egress
+ * Congestion Threshold is in units of 2 Free List pointers.)
+ */
+ s->fl_starve_thres
+ = EGRTHRESHOLD_GET(sge_params->sge_congestion_control)*2 + 1;
/*
* Set up tasklet timers.
*/
struct sge_params {
u32 sge_control; /* padding, boundaries, lengths, etc. */
+ u32 sge_control2; /* T5: more of the same */
u32 sge_host_page_size; /* RDMA page sizes */
u32 sge_queues_per_page; /* RDMA queues/page */
u32 sge_user_mode_limits; /* limits for BAR2 user mode accesses */
u32 sge_fl_buffer_size[16]; /* free list buffer sizes */
u32 sge_ingress_rx_threshold; /* RX counter interrupt threshold[4] */
+ u32 sge_congestion_control; /* congestion thresholds, etc. */
u32 sge_timer_value_0_and_1; /* interrupt coalescing timer values */
u32 sge_timer_value_2_and_3;
u32 sge_timer_value_4_and_5;
sge_params->sge_timer_value_2_and_3 = vals[5];
sge_params->sge_timer_value_4_and_5 = vals[6];
+ /* T4 uses a single control field to specify both the PCIe Padding and
+ * Packing Boundary. T5 introduced the ability to specify these
+ * separately with the Padding Boundary in SGE_CONTROL and and Packing
+ * Boundary in SGE_CONTROL2. So for T5 and later we need to grab
+ * SGE_CONTROL in order to determine how ingress packet data will be
+ * laid out in Packed Buffer Mode. Unfortunately, older versions of
+ * the firmware won't let us retrieve SGE_CONTROL2 so if we get a
+ * failure grabbing it we throw an error since we can't figure out the
+ * right value.
+ */
+ if (!is_t4(adapter->params.chip)) {
+ params[0] = (FW_PARAMS_MNEM(FW_PARAMS_MNEM_REG) |
+ FW_PARAMS_PARAM_XYZ(SGE_CONTROL2_A));
+ v = t4vf_query_params(adapter, 1, params, vals);
+ if (v != FW_SUCCESS) {
+ dev_err(adapter->pdev_dev,
+ "Unable to get SGE Control2; "
+ "probably old firmware.\n");
+ return v;
+ }
+ sge_params->sge_control2 = vals[0];
+ }
+
params[0] = (FW_PARAMS_MNEM(FW_PARAMS_MNEM_REG) |
FW_PARAMS_PARAM_XYZ(SGE_INGRESS_RX_THRESHOLD));
- v = t4vf_query_params(adapter, 1, params, vals);
+ params[1] = (FW_PARAMS_MNEM(FW_PARAMS_MNEM_REG) |
+ FW_PARAMS_PARAM_XYZ(SGE_CONM_CTRL));
+ v = t4vf_query_params(adapter, 2, params, vals);
if (v)
return v;
sge_params->sge_ingress_rx_threshold = vals[0];
+ sge_params->sge_congestion_control = vals[1];
return 0;
}
struct vnic_rq_buf *buf = rq->to_use;
if (buf->os_buf) {
- buf = buf->next;
- rq->to_use = buf;
- rq->ring.desc_avail--;
- if ((buf->index & VNIC_RQ_RETURN_RATE) == 0) {
- /* Adding write memory barrier prevents compiler and/or
- * CPU reordering, thus avoiding descriptor posting
- * before descriptor is initialized. Otherwise, hardware
- * can read stale descriptor fields.
- */
- wmb();
- iowrite32(buf->index, &rq->ctrl->posted_index);
- }
+ enic_queue_rq_desc(rq, buf->os_buf, os_buf_index, buf->dma_addr,
+ buf->len);
return 0;
}
enic->rq_truncated_pkts++;
}
+ pci_unmap_single(enic->pdev, buf->dma_addr, buf->len,
+ PCI_DMA_FROMDEVICE);
dev_kfree_skb_any(skb);
+ buf->os_buf = NULL;
return;
}
/* Buffer overflow
*/
+ pci_unmap_single(enic->pdev, buf->dma_addr, buf->len,
+ PCI_DMA_FROMDEVICE);
dev_kfree_skb_any(skb);
+ buf->os_buf = NULL;
}
}
return -EOPNOTSUPP;
br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC);
+ if (!br_spec)
+ return -EINVAL;
nla_for_each_nested(attr, br_spec, rem) {
if (nla_type(attr) != IFLA_BRIDGE_MODE)
continue;
+ if (nla_len(attr) < sizeof(mode))
+ return -EINVAL;
+
mode = nla_get_u16(attr);
if (mode != BRIDGE_MODE_VEPA && mode != BRIDGE_MODE_VEB)
return -EINVAL;
"Disabled VxLAN offloads for UDP port %d\n",
be16_to_cpu(port));
}
+
+static bool be_gso_check(struct sk_buff *skb, struct net_device *dev)
+{
+ return vxlan_gso_check(skb);
+}
#endif
static const struct net_device_ops be_netdev_ops = {
#ifdef CONFIG_BE2NET_VXLAN
.ndo_add_vxlan_port = be_add_vxlan_port,
.ndo_del_vxlan_port = be_del_vxlan_port,
+ .ndo_gso_check = be_gso_check,
#endif
};
return bufaddr;
}
+static void swap_buffer2(void *dst_buf, void *src_buf, int len)
+{
+ int i;
+ unsigned int *src = src_buf;
+ unsigned int *dst = dst_buf;
+
+ for (i = 0; i < len; i += 4, src++, dst++)
+ *dst = swab32p(src);
+}
+
static void fec_dump(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
}
static bool fec_enet_copybreak(struct net_device *ndev, struct sk_buff **skb,
- struct bufdesc *bdp, u32 length)
+ struct bufdesc *bdp, u32 length, bool swap)
{
struct fec_enet_private *fep = netdev_priv(ndev);
struct sk_buff *new_skb;
dma_sync_single_for_cpu(&fep->pdev->dev, bdp->cbd_bufaddr,
FEC_ENET_RX_FRSIZE - fep->rx_align,
DMA_FROM_DEVICE);
- memcpy(new_skb->data, (*skb)->data, length);
+ if (!swap)
+ memcpy(new_skb->data, (*skb)->data, length);
+ else
+ swap_buffer2(new_skb->data, (*skb)->data, length);
*skb = new_skb;
return true;
u16 vlan_tag;
int index = 0;
bool is_copybreak;
+ bool need_swap = id_entry->driver_data & FEC_QUIRK_SWAP_FRAME;
#ifdef CONFIG_M532x
flush_cache_all();
* include that when passing upstream as it messes up
* bridging applications.
*/
- is_copybreak = fec_enet_copybreak(ndev, &skb, bdp, pkt_len - 4);
+ is_copybreak = fec_enet_copybreak(ndev, &skb, bdp, pkt_len - 4,
+ need_swap);
if (!is_copybreak) {
skb_new = netdev_alloc_skb(ndev, FEC_ENET_RX_FRSIZE);
if (unlikely(!skb_new)) {
prefetch(skb->data - NET_IP_ALIGN);
skb_put(skb, pkt_len - 4);
data = skb->data;
- if (id_entry->driver_data & FEC_QUIRK_SWAP_FRAME)
+ if (!is_copybreak && need_swap)
swap_buffer(data, pkt_len);
/* Extract the enhanced buffer descriptor */
netif_device_detach(ndev);
netif_tx_unlock_bh(ndev);
fec_stop(ndev);
+ fec_enet_clk_enable(ndev, false);
+ pinctrl_pm_select_sleep_state(&fep->pdev->dev);
}
rtnl_unlock();
- fec_enet_clk_enable(ndev, false);
- pinctrl_pm_select_sleep_state(&fep->pdev->dev);
-
if (fep->reg_phy)
regulator_disable(fep->reg_phy);
return ret;
}
- pinctrl_pm_select_default_state(&fep->pdev->dev);
- ret = fec_enet_clk_enable(ndev, true);
- if (ret)
- goto failed_clk;
-
rtnl_lock();
if (netif_running(ndev)) {
+ pinctrl_pm_select_default_state(&fep->pdev->dev);
+ ret = fec_enet_clk_enable(ndev, true);
+ if (ret) {
+ rtnl_unlock();
+ goto failed_clk;
+ }
fec_restart(ndev);
netif_tx_lock_bh(ndev);
netif_device_attach(ndev);
/* igb_get_stats64() might access the rings on this vector,
* we must wait a grace period before freeing it.
*/
- kfree_rcu(q_vector, rcu);
+ if (q_vector)
+ kfree_rcu(q_vector, rcu);
}
/**
adapter->flags &= ~IGB_FLAG_NEED_LINK_UPDATE;
for (i = 0; i < adapter->num_q_vectors; i++) {
- napi_synchronize(&(adapter->q_vector[i]->napi));
- napi_disable(&(adapter->q_vector[i]->napi));
+ if (adapter->q_vector[i]) {
+ napi_synchronize(&adapter->q_vector[i]->napi);
+ napi_disable(&adapter->q_vector[i]->napi);
+ }
}
int i;
for (i = 0; i < adapter->num_tx_queues; i++)
- igb_free_tx_resources(adapter->tx_ring[i]);
+ if (adapter->tx_ring[i])
+ igb_free_tx_resources(adapter->tx_ring[i]);
}
void igb_unmap_and_free_tx_resource(struct igb_ring *ring,
int i;
for (i = 0; i < adapter->num_tx_queues; i++)
- igb_clean_tx_ring(adapter->tx_ring[i]);
+ if (adapter->tx_ring[i])
+ igb_clean_tx_ring(adapter->tx_ring[i]);
}
/**
int i;
for (i = 0; i < adapter->num_rx_queues; i++)
- igb_free_rx_resources(adapter->rx_ring[i]);
+ if (adapter->rx_ring[i])
+ igb_free_rx_resources(adapter->rx_ring[i]);
}
/**
int i;
for (i = 0; i < adapter->num_rx_queues; i++)
- igb_clean_rx_ring(adapter->rx_ring[i]);
+ if (adapter->rx_ring[i])
+ igb_clean_rx_ring(adapter->rx_ring[i]);
}
/**
pci_restore_state(pdev);
pci_save_state(pdev);
+ if (!pci_device_is_present(pdev))
+ return -ENODEV;
err = pci_enable_device_mem(pdev);
if (err) {
dev_err(&pdev->dev,
* if SR-IOV and VMDQ are disabled - otherwise ensure
* that hardware VLAN filters remain enabled.
*/
- if (!(adapter->flags & (IXGBE_FLAG_VMDQ_ENABLED |
- IXGBE_FLAG_SRIOV_ENABLED)))
+ if (adapter->flags & (IXGBE_FLAG_VMDQ_ENABLED |
+ IXGBE_FLAG_SRIOV_ENABLED))
vlnctrl |= (IXGBE_VLNCTRL_VFE | IXGBE_VLNCTRL_CFIEN);
} else {
if (netdev->flags & IFF_ALLMULTI) {
return -EOPNOTSUPP;
br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC);
+ if (!br_spec)
+ return -EINVAL;
nla_for_each_nested(attr, br_spec, rem) {
__u16 mode;
if (nla_type(attr) != IFLA_BRIDGE_MODE)
continue;
+ if (nla_len(attr) < sizeof(mode))
+ return -EINVAL;
+
mode = nla_get_u16(attr);
if (mode == BRIDGE_MODE_VEPA) {
reg = 0;
int i, err, pci_using_dac, expected_gts;
unsigned int indices = MAX_TX_QUEUES;
u8 part_str[IXGBE_PBANUM_LENGTH];
+ bool disable_dev = false;
#ifdef IXGBE_FCOE
u16 device_caps;
#endif
iounmap(adapter->io_addr);
kfree(adapter->mac_table);
err_ioremap:
+ disable_dev = !test_and_set_bit(__IXGBE_DISABLED, &adapter->state);
free_netdev(netdev);
err_alloc_etherdev:
pci_release_selected_regions(pdev,
pci_select_bars(pdev, IORESOURCE_MEM));
err_pci_reg:
err_dma:
- if (!adapter || !test_and_set_bit(__IXGBE_DISABLED, &adapter->state))
+ if (!adapter || disable_dev)
pci_disable_device(pdev);
return err;
}
{
struct ixgbe_adapter *adapter = pci_get_drvdata(pdev);
struct net_device *netdev = adapter->netdev;
+ bool disable_dev;
ixgbe_dbg_adapter_exit(adapter);
e_dev_info("complete\n");
kfree(adapter->mac_table);
+ disable_dev = !test_and_set_bit(__IXGBE_DISABLED, &adapter->state);
free_netdev(netdev);
pci_disable_pcie_error_reporting(pdev);
- if (!test_and_set_bit(__IXGBE_DISABLED, &adapter->state))
+ if (disable_dev)
pci_disable_device(pdev);
}
**/
s32 ixgbe_setup_phy_link_tnx(struct ixgbe_hw *hw)
{
- s32 status;
u16 autoneg_reg = IXGBE_MII_AUTONEG_REG;
bool autoneg = false;
ixgbe_link_speed speed;
hw->phy.ops.write_reg(hw, MDIO_CTRL1,
MDIO_MMD_AN, autoneg_reg);
-
- return status;
+ return 0;
}
/**
int tx_index;
struct tx_desc *desc;
u32 cmd_sts;
- struct sk_buff *skb;
tx_index = txq->tx_used_desc;
desc = &txq->tx_desc_area[tx_index];
reclaimed++;
txq->tx_desc_count--;
- skb = NULL;
- if (cmd_sts & TX_LAST_DESC)
- skb = __skb_dequeue(&txq->tx_skb);
+ if (!IS_TSO_HEADER(txq, desc->buf_ptr))
+ dma_unmap_single(mp->dev->dev.parent, desc->buf_ptr,
+ desc->byte_cnt, DMA_TO_DEVICE);
+
+ if (cmd_sts & TX_ENABLE_INTERRUPT) {
+ struct sk_buff *skb = __skb_dequeue(&txq->tx_skb);
+
+ if (!WARN_ON(!skb))
+ dev_kfree_skb(skb);
+ }
if (cmd_sts & ERROR_SUMMARY) {
netdev_info(mp->dev, "tx error\n");
mp->dev->stats.tx_errors++;
}
- if (!IS_TSO_HEADER(txq, desc->buf_ptr))
- dma_unmap_single(mp->dev->dev.parent, desc->buf_ptr,
- desc->byte_cnt, DMA_TO_DEVICE);
- dev_kfree_skb(skb);
}
__netif_tx_unlock_bh(nq);
{
struct mvpp2_prs_entry *pe;
int tid_aux, tid;
+ int ret = 0;
pe = mvpp2_prs_vlan_find(priv, tpid, ai);
break;
}
- if (tid <= tid_aux)
- return -EINVAL;
+ if (tid <= tid_aux) {
+ ret = -EINVAL;
+ goto error;
+ }
memset(pe, 0 , sizeof(struct mvpp2_prs_entry));
mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_VLAN);
mvpp2_prs_hw_write(priv, pe);
+error:
kfree(pe);
- return 0;
+ return ret;
}
/* Get first free double vlan ai number */
unsigned int port_map)
{
struct mvpp2_prs_entry *pe;
- int tid_aux, tid, ai;
+ int tid_aux, tid, ai, ret = 0;
pe = mvpp2_prs_double_vlan_find(priv, tpid1, tpid2);
/* Set ai value for new double vlan entry */
ai = mvpp2_prs_double_vlan_ai_free_get(priv);
- if (ai < 0)
- return ai;
+ if (ai < 0) {
+ ret = ai;
+ goto error;
+ }
/* Get first single/triple vlan tid */
for (tid_aux = MVPP2_PE_FIRST_FREE_TID;
break;
}
- if (tid >= tid_aux)
- return -ERANGE;
+ if (tid >= tid_aux) {
+ ret = -ERANGE;
+ goto error;
+ }
memset(pe, 0, sizeof(struct mvpp2_prs_entry));
mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_VLAN);
mvpp2_prs_tcam_port_map_set(pe, port_map);
mvpp2_prs_hw_write(priv, pe);
+error:
kfree(pe);
- return 0;
+ return ret;
}
/* IPv4 header parsing for fragmentation and L4 offset */
mlx4_set_stats_bitmap(mdev->dev, &priv->stats_bitmap);
#ifdef CONFIG_MLX4_EN_VXLAN
- if (priv->mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_VXLAN_OFFLOADS)
+ if (priv->mdev->dev->caps.tunnel_offload_mode == MLX4_TUNNEL_OFFLOAD_MODE_VXLAN)
vxlan_get_rx_port(dev);
#endif
priv->port_up = true;
ret = mlx4_SET_PORT_VXLAN(priv->mdev->dev, priv->port,
VXLAN_STEER_BY_OUTER_MAC, 1);
out:
- if (ret)
+ if (ret) {
en_err(priv, "failed setting L2 tunnel configuration ret %d\n", ret);
+ return;
+ }
+
+ /* set offloads */
+ priv->dev->hw_enc_features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
+ NETIF_F_TSO | NETIF_F_GSO_UDP_TUNNEL;
+ priv->dev->hw_features |= NETIF_F_GSO_UDP_TUNNEL;
+ priv->dev->features |= NETIF_F_GSO_UDP_TUNNEL;
}
static void mlx4_en_del_vxlan_offloads(struct work_struct *work)
int ret;
struct mlx4_en_priv *priv = container_of(work, struct mlx4_en_priv,
vxlan_del_task);
+ /* unset offloads */
+ priv->dev->hw_enc_features &= ~(NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
+ NETIF_F_TSO | NETIF_F_GSO_UDP_TUNNEL);
+ priv->dev->hw_features &= ~NETIF_F_GSO_UDP_TUNNEL;
+ priv->dev->features &= ~NETIF_F_GSO_UDP_TUNNEL;
ret = mlx4_SET_PORT_VXLAN(priv->mdev->dev, priv->port,
VXLAN_STEER_BY_OUTER_MAC, 0);
queue_work(priv->mdev->workqueue, &priv->vxlan_del_task);
}
+
+static bool mlx4_en_gso_check(struct sk_buff *skb, struct net_device *dev)
+{
+ return vxlan_gso_check(skb);
+}
#endif
static const struct net_device_ops mlx4_netdev_ops = {
#ifdef CONFIG_MLX4_EN_VXLAN
.ndo_add_vxlan_port = mlx4_en_add_vxlan_port,
.ndo_del_vxlan_port = mlx4_en_del_vxlan_port,
+ .ndo_gso_check = mlx4_en_gso_check,
#endif
};
.ndo_rx_flow_steer = mlx4_en_filter_rfs,
#endif
.ndo_get_phys_port_id = mlx4_en_get_phys_port_id,
+#ifdef CONFIG_MLX4_EN_VXLAN
+ .ndo_add_vxlan_port = mlx4_en_add_vxlan_port,
+ .ndo_del_vxlan_port = mlx4_en_del_vxlan_port,
+ .ndo_gso_check = mlx4_en_gso_check,
+#endif
};
int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port,
if (mdev->dev->caps.steering_mode != MLX4_STEERING_MODE_A0)
dev->priv_flags |= IFF_UNICAST_FLT;
- if (mdev->dev->caps.tunnel_offload_mode == MLX4_TUNNEL_OFFLOAD_MODE_VXLAN) {
- dev->hw_enc_features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
- NETIF_F_TSO | NETIF_F_GSO_UDP_TUNNEL;
- dev->hw_features |= NETIF_F_GSO_UDP_TUNNEL;
- dev->features |= NETIF_F_GSO_UDP_TUNNEL;
- }
-
mdev->pndev[port] = dev;
netif_carrier_off(dev);
switch (op) {
case RES_OP_RESERVE:
- count = get_param_l(&in_param);
+ count = get_param_l(&in_param) & 0xffffff;
align = get_param_h(&in_param);
err = mlx4_grant_resource(dev, slave, RES_QP, count, 0);
if (err)
snprintf(eq->name, MLX5_MAX_EQ_NAME, "%s@pci:%s",
name, pci_name(dev->pdev));
eq->eqn = out.eq_number;
+ eq->irqn = vecidx;
+ eq->dev = dev;
+ eq->doorbell = uar->map + MLX5_EQ_DOORBEL_OFFSET;
err = request_irq(table->msix_arr[vecidx].vector, mlx5_msix_handler, 0,
eq->name, eq);
if (err)
goto err_eq;
- eq->irqn = vecidx;
- eq->dev = dev;
- eq->doorbell = uar->map + MLX5_EQ_DOORBEL_OFFSET;
-
err = mlx5_debug_eq_add(dev, eq);
if (err)
goto err_irq;
dev->profile = &profile[prof_sel];
dev->event = mlx5_core_event;
+ INIT_LIST_HEAD(&priv->ctx_list);
+ spin_lock_init(&priv->ctx_lock);
err = mlx5_dev_init(dev, pdev);
if (err) {
dev_err(&pdev->dev, "mlx5_dev_init failed %d\n", err);
goto out;
}
- INIT_LIST_HEAD(&priv->ctx_list);
- spin_lock_init(&priv->ctx_lock);
err = mlx5_register_device(dev);
if (err) {
dev_err(&pdev->dev, "mlx5_register_device failed %d\n", err);
if (test_bit(__NX_RESETTING, &adapter->state))
goto reschedule;
- if (test_bit(__NX_DEV_UP, &adapter->state)) {
+ if (test_bit(__NX_DEV_UP, &adapter->state) &&
+ !(adapter->capabilities & NX_FW_CAPABILITY_LINK_NOTIFICATION)) {
if (!adapter->has_link_events) {
netxen_nic_handle_phy_intr(adapter);
adapter->flags |= QLCNIC_DEL_VXLAN_PORT;
}
+
+static bool qlcnic_gso_check(struct sk_buff *skb, struct net_device *dev)
+{
+ return vxlan_gso_check(skb);
+}
#endif
static const struct net_device_ops qlcnic_netdev_ops = {
#ifdef CONFIG_QLCNIC_VXLAN
.ndo_add_vxlan_port = qlcnic_add_vxlan_port,
.ndo_del_vxlan_port = qlcnic_del_vxlan_port,
+ .ndo_gso_check = qlcnic_gso_check,
#endif
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = qlcnic_poll_controller,
config NET_VENDOR_QUALCOMM
bool "Qualcomm devices"
default y
- depends on SPI_MASTER && OF_GPIO
---help---
If you have a network (Ethernet) card belonging to this class, say Y
and read the Ethernet-HOWTO, available from
config QCA7000
tristate "Qualcomm Atheros QCA7000 support"
- depends on SPI_MASTER && OF_GPIO
+ depends on SPI_MASTER && OF
---help---
This SPI protocol driver supports the Qualcomm Atheros QCA7000.
return ret;
}
-#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
static void sh_eth_set_receive_align(struct sk_buff *skb)
{
- int reserve;
+ uintptr_t reserve = (uintptr_t)skb->data & (SH_ETH_RX_ALIGN - 1);
- reserve = SH4_SKB_RX_ALIGN - ((u32)skb->data & (SH4_SKB_RX_ALIGN - 1));
if (reserve)
- skb_reserve(skb, reserve);
+ skb_reserve(skb, SH_ETH_RX_ALIGN - reserve);
}
-#else
-static void sh_eth_set_receive_align(struct sk_buff *skb)
-{
- skb_reserve(skb, SH2_SH3_SKB_RX_ALIGN);
-}
-#endif
/* CPU <-> EDMAC endian convert */
struct sh_eth_txdesc *txdesc = NULL;
int rx_ringsize = sizeof(*rxdesc) * mdp->num_rx_ring;
int tx_ringsize = sizeof(*txdesc) * mdp->num_tx_ring;
+ int skbuff_size = mdp->rx_buf_sz + SH_ETH_RX_ALIGN - 1;
mdp->cur_rx = 0;
mdp->cur_tx = 0;
for (i = 0; i < mdp->num_rx_ring; i++) {
/* skb */
mdp->rx_skbuff[i] = NULL;
- skb = netdev_alloc_skb(ndev, mdp->rx_buf_sz);
+ skb = netdev_alloc_skb(ndev, skbuff_size);
mdp->rx_skbuff[i] = skb;
if (skb == NULL)
break;
- dma_map_single(&ndev->dev, skb->data, mdp->rx_buf_sz,
- DMA_FROM_DEVICE);
sh_eth_set_receive_align(skb);
/* RX descriptor */
rxdesc = &mdp->rx_ring[i];
+ /* The size of the buffer is a multiple of 16 bytes. */
+ rxdesc->buffer_length = ALIGN(mdp->rx_buf_sz, 16);
+ dma_map_single(&ndev->dev, skb->data, rxdesc->buffer_length,
+ DMA_FROM_DEVICE);
rxdesc->addr = virt_to_phys(PTR_ALIGN(skb->data, 4));
rxdesc->status = cpu_to_edmac(mdp, RD_RACT | RD_RFP);
- /* The size of the buffer is 16 byte boundary. */
- rxdesc->buffer_length = ALIGN(mdp->rx_buf_sz, 16);
/* Rx descriptor address set */
if (i == 0) {
sh_eth_write(ndev, mdp->rx_desc_dma, RDLAR);
struct sk_buff *skb;
u16 pkt_len = 0;
u32 desc_status;
+ int skbuff_size = mdp->rx_buf_sz + SH_ETH_RX_ALIGN - 1;
rxdesc = &mdp->rx_ring[entry];
while (!(rxdesc->status & cpu_to_edmac(mdp, RD_RACT))) {
if (mdp->cd->rpadir)
skb_reserve(skb, NET_IP_ALIGN);
dma_sync_single_for_cpu(&ndev->dev, rxdesc->addr,
- mdp->rx_buf_sz,
+ ALIGN(mdp->rx_buf_sz, 16),
DMA_FROM_DEVICE);
skb_put(skb, pkt_len);
skb->protocol = eth_type_trans(skb, ndev);
rxdesc->buffer_length = ALIGN(mdp->rx_buf_sz, 16);
if (mdp->rx_skbuff[entry] == NULL) {
- skb = netdev_alloc_skb(ndev, mdp->rx_buf_sz);
+ skb = netdev_alloc_skb(ndev, skbuff_size);
mdp->rx_skbuff[entry] = skb;
if (skb == NULL)
break; /* Better luck next round. */
- dma_map_single(&ndev->dev, skb->data, mdp->rx_buf_sz,
- DMA_FROM_DEVICE);
sh_eth_set_receive_align(skb);
+ dma_map_single(&ndev->dev, skb->data,
+ rxdesc->buffer_length, DMA_FROM_DEVICE);
skb_checksum_none_assert(skb);
rxdesc->addr = virt_to_phys(PTR_ALIGN(skb->data, 4));
if (ret)
goto out_free_irq;
+ mdp->is_opened = 1;
+
return ret;
out_free_irq:
return NETDEV_TX_OK;
}
+static struct net_device_stats *sh_eth_get_stats(struct net_device *ndev)
+{
+ struct sh_eth_private *mdp = netdev_priv(ndev);
+
+ if (sh_eth_is_rz_fast_ether(mdp))
+ return &ndev->stats;
+
+ if (!mdp->is_opened)
+ return &ndev->stats;
+
+ ndev->stats.tx_dropped += sh_eth_read(ndev, TROCR);
+ sh_eth_write(ndev, 0, TROCR); /* (write clear) */
+ ndev->stats.collisions += sh_eth_read(ndev, CDCR);
+ sh_eth_write(ndev, 0, CDCR); /* (write clear) */
+ ndev->stats.tx_carrier_errors += sh_eth_read(ndev, LCCR);
+ sh_eth_write(ndev, 0, LCCR); /* (write clear) */
+
+ if (sh_eth_is_gether(mdp)) {
+ ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CERCR);
+ sh_eth_write(ndev, 0, CERCR); /* (write clear) */
+ ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CEECR);
+ sh_eth_write(ndev, 0, CEECR); /* (write clear) */
+ } else {
+ ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CNDCR);
+ sh_eth_write(ndev, 0, CNDCR); /* (write clear) */
+ }
+
+ return &ndev->stats;
+}
+
/* device close function */
static int sh_eth_close(struct net_device *ndev)
{
sh_eth_write(ndev, 0, EDTRR);
sh_eth_write(ndev, 0, EDRRR);
+ sh_eth_get_stats(ndev);
/* PHY Disconnect */
if (mdp->phydev) {
phy_stop(mdp->phydev);
pm_runtime_put_sync(&mdp->pdev->dev);
- return 0;
-}
-
-static struct net_device_stats *sh_eth_get_stats(struct net_device *ndev)
-{
- struct sh_eth_private *mdp = netdev_priv(ndev);
-
- if (sh_eth_is_rz_fast_ether(mdp))
- return &ndev->stats;
+ mdp->is_opened = 0;
- pm_runtime_get_sync(&mdp->pdev->dev);
-
- ndev->stats.tx_dropped += sh_eth_read(ndev, TROCR);
- sh_eth_write(ndev, 0, TROCR); /* (write clear) */
- ndev->stats.collisions += sh_eth_read(ndev, CDCR);
- sh_eth_write(ndev, 0, CDCR); /* (write clear) */
- ndev->stats.tx_carrier_errors += sh_eth_read(ndev, LCCR);
- sh_eth_write(ndev, 0, LCCR); /* (write clear) */
- if (sh_eth_is_gether(mdp)) {
- ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CERCR);
- sh_eth_write(ndev, 0, CERCR); /* (write clear) */
- ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CEECR);
- sh_eth_write(ndev, 0, CEECR); /* (write clear) */
- } else {
- ndev->stats.tx_carrier_errors += sh_eth_read(ndev, CNDCR);
- sh_eth_write(ndev, 0, CNDCR); /* (write clear) */
- }
- pm_runtime_put_sync(&mdp->pdev->dev);
-
- return &ndev->stats;
+ return 0;
}
/* ioctl to device function */
/* Driver's parameters */
#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
-#define SH4_SKB_RX_ALIGN 32
+#define SH_ETH_RX_ALIGN 32
#else
-#define SH2_SH3_SKB_RX_ALIGN 2
+#define SH_ETH_RX_ALIGN 2
#endif
/* Register's bits
unsigned no_ether_link:1;
unsigned ether_link_active_low:1;
+ unsigned is_opened:1;
};
static inline void sh_eth_soft_swap(char *src, int len)
EFX_MAX_CHANNELS,
resource_size(&efx->pci_dev->resource[EFX_MEM_BAR]) /
(EFX_VI_PAGE_SIZE * EFX_TXQ_TYPES));
- BUG_ON(efx->max_channels == 0);
+ if (WARN_ON(efx->max_channels == 0))
+ return -EIO;
nic_data = kzalloc(sizeof(*nic_data), GFP_KERNEL);
if (!nic_data)
const struct of_device_id *match = NULL;
struct smc_local *lp;
struct net_device *ndev;
- struct resource *res, *ires;
+ struct resource *res;
unsigned int __iomem *addr;
unsigned long irq_flags = SMC_IRQ_FLAGS;
+ unsigned long irq_resflags;
int ret;
ndev = alloc_etherdev(sizeof(struct smc_local));
goto out_free_netdev;
}
- ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!ires) {
+ ndev->irq = platform_get_irq(pdev, 0);
+ if (ndev->irq <= 0) {
ret = -ENODEV;
goto out_release_io;
}
-
- ndev->irq = ires->start;
-
- if (irq_flags == -1 || ires->flags & IRQF_TRIGGER_MASK)
- irq_flags = ires->flags & IRQF_TRIGGER_MASK;
+ /*
+ * If this platform does not specify any special irqflags, or if
+ * the resource supplies a trigger, override the irqflags with
+ * the trigger flags from the resource.
+ */
+ irq_resflags = irqd_get_trigger_type(irq_get_irq_data(ndev->irq));
+ if (irq_flags == -1 || irq_resflags & IRQF_TRIGGER_MASK)
+ irq_flags = irq_resflags & IRQF_TRIGGER_MASK;
ret = smc_request_attrib(pdev, ndev);
if (ret)
spin_unlock(&pdata->mac_lock);
}
+static int smsc911x_phy_general_power_up(struct smsc911x_data *pdata)
+{
+ int rc = 0;
+
+ if (!pdata->phy_dev)
+ return rc;
+
+ /* If the internal PHY is in General Power-Down mode, all, except the
+ * management interface, is powered-down and stays in that condition as
+ * long as Phy register bit 0.11 is HIGH.
+ *
+ * In that case, clear the bit 0.11, so the PHY powers up and we can
+ * access to the phy registers.
+ */
+ rc = phy_read(pdata->phy_dev, MII_BMCR);
+ if (rc < 0) {
+ SMSC_WARN(pdata, drv, "Failed reading PHY control reg");
+ return rc;
+ }
+
+ /* If the PHY general power-down bit is not set is not necessary to
+ * disable the general power down-mode.
+ */
+ if (rc & BMCR_PDOWN) {
+ rc = phy_write(pdata->phy_dev, MII_BMCR, rc & ~BMCR_PDOWN);
+ if (rc < 0) {
+ SMSC_WARN(pdata, drv, "Failed writing PHY control reg");
+ return rc;
+ }
+
+ usleep_range(1000, 1500);
+ }
+
+ return 0;
+}
+
static int smsc911x_phy_disable_energy_detect(struct smsc911x_data *pdata)
{
int rc = 0;
return rc;
}
- /*
- * If energy is detected the PHY is already awake so is not necessary
- * to disable the energy detect power-down mode.
- */
- if ((rc & MII_LAN83C185_EDPWRDOWN) &&
- !(rc & MII_LAN83C185_ENERGYON)) {
+ /* Only disable if energy detect mode is already enabled */
+ if (rc & MII_LAN83C185_EDPWRDOWN) {
/* Disable energy detect mode for this SMSC Transceivers */
rc = phy_write(pdata->phy_dev, MII_LAN83C185_CTRL_STATUS,
rc & (~MII_LAN83C185_EDPWRDOWN));
SMSC_WARN(pdata, drv, "Failed writing PHY control reg");
return rc;
}
-
- mdelay(1);
+ /* Allow PHY to wakeup */
+ mdelay(2);
}
return 0;
/* Only enable if energy detect mode is already disabled */
if (!(rc & MII_LAN83C185_EDPWRDOWN)) {
- mdelay(100);
/* Enable energy detect mode for this SMSC Transceivers */
rc = phy_write(pdata->phy_dev, MII_LAN83C185_CTRL_STATUS,
rc | MII_LAN83C185_EDPWRDOWN);
SMSC_WARN(pdata, drv, "Failed writing PHY control reg");
return rc;
}
-
- mdelay(1);
}
return 0;
}
unsigned int temp;
int ret;
+ /*
+ * Make sure to power-up the PHY chip before doing a reset, otherwise
+ * the reset fails.
+ */
+ ret = smsc911x_phy_general_power_up(pdata);
+ if (ret) {
+ SMSC_WARN(pdata, drv, "Failed to power-up the PHY chip");
+ return ret;
+ }
+
/*
* LAN9210/LAN9211/LAN9220/LAN9221 chips have an internal PHY that
* are initialized in a Energy Detect Power-Down mode that prevents
bool stmmac_eee_init(struct stmmac_priv *priv)
{
char *phy_bus_name = priv->plat->phy_bus_name;
+ unsigned long flags;
bool ret = false;
/* Using PCS we cannot dial with the phy registers at this stage
* changed).
* In that case the driver disable own timers.
*/
+ spin_lock_irqsave(&priv->lock, flags);
if (priv->eee_active) {
pr_debug("stmmac: disable EEE\n");
del_timer_sync(&priv->eee_ctrl_timer);
tx_lpi_timer);
}
priv->eee_active = 0;
+ spin_unlock_irqrestore(&priv->lock, flags);
goto out;
}
/* Activate the EEE and start timers */
+ spin_lock_irqsave(&priv->lock, flags);
if (!priv->eee_active) {
priv->eee_active = 1;
init_timer(&priv->eee_ctrl_timer);
/* Set HW EEE according to the speed */
priv->hw->mac->set_eee_pls(priv->hw, priv->phydev->link);
- pr_debug("stmmac: Energy-Efficient Ethernet initialized\n");
-
ret = true;
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ pr_debug("stmmac: Energy-Efficient Ethernet initialized\n");
}
out:
return ret;
if (new_state && netif_msg_link(priv))
phy_print_status(phydev);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
/* At this stage, it could be needed to setup the EEE or adjust some
* MAC related HW registers.
*/
priv->eee_enabled = stmmac_eee_init(priv);
-
- spin_unlock_irqrestore(&priv->lock, flags);
}
/**
}
static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p,
- int i)
+ int i, gfp_t flags)
{
struct sk_buff *skb;
skb = __netdev_alloc_skb(priv->dev, priv->dma_buf_sz + NET_IP_ALIGN,
- GFP_KERNEL);
+ flags);
if (!skb) {
pr_err("%s: Rx init fails; skb is NULL\n", __func__);
return -ENOMEM;
* and allocates the socket buffers. It suppors the chained and ring
* modes.
*/
-static int init_dma_desc_rings(struct net_device *dev)
+static int init_dma_desc_rings(struct net_device *dev, gfp_t flags)
{
int i;
struct stmmac_priv *priv = netdev_priv(dev);
else
p = priv->dma_rx + i;
- ret = stmmac_init_rx_buffers(priv, p, i);
+ ret = stmmac_init_rx_buffers(priv, p, i, flags);
if (ret)
goto err_init_rx_buffers;
struct stmmac_priv *priv = netdev_priv(dev);
int ret;
- ret = init_dma_desc_rings(dev);
- if (ret < 0) {
- pr_err("%s: DMA descriptors initialization failed\n", __func__);
- return ret;
- }
/* DMA initialization and SW reset */
ret = stmmac_init_dma_engine(priv);
if (ret < 0) {
}
priv->tx_lpi_timer = STMMAC_DEFAULT_TWT_LS;
- priv->eee_enabled = stmmac_eee_init(priv);
-
- stmmac_init_tx_coalesce(priv);
-
if ((priv->use_riwt) && (priv->hw->dma->rx_watchdog)) {
priv->rx_riwt = MAX_DMA_RIWT;
priv->hw->dma->rx_watchdog(priv->ioaddr, MAX_DMA_RIWT);
goto dma_desc_error;
}
+ ret = init_dma_desc_rings(dev, GFP_KERNEL);
+ if (ret < 0) {
+ pr_err("%s: DMA descriptors initialization failed\n", __func__);
+ goto init_error;
+ }
+
ret = stmmac_hw_setup(dev);
if (ret < 0) {
pr_err("%s: Hw setup failed\n", __func__);
goto init_error;
}
+ stmmac_init_tx_coalesce(priv);
+
if (priv->phydev)
phy_start(priv->phydev);
unsigned int nopaged_len = skb_headlen(skb);
unsigned int enh_desc = priv->plat->enh_desc;
+ spin_lock(&priv->tx_lock);
+
if (unlikely(stmmac_tx_avail(priv) < nfrags + 1)) {
+ spin_unlock(&priv->tx_lock);
if (!netif_queue_stopped(dev)) {
netif_stop_queue(dev);
/* This is a hard error, log it. */
return NETDEV_TX_BUSY;
}
- spin_lock(&priv->tx_lock);
-
if (priv->tx_path_in_lpi_mode)
stmmac_disable_eee_mode(priv);
return NETDEV_TX_OK;
dma_map_err:
+ spin_unlock(&priv->tx_lock);
dev_err(priv->device, "Tx dma map failed\n");
dev_kfree_skb(skb);
priv->dev->stats.tx_dropped++;
{
struct stmmac_priv *priv = netdev_priv(dev);
- spin_lock(&priv->lock);
priv->hw->mac->set_filter(priv->hw, dev);
- spin_unlock(&priv->lock);
}
/**
stmmac_set_mac(priv->ioaddr, false);
pinctrl_pm_select_sleep_state(priv->device);
/* Disable clock in case of PWM is off */
- clk_disable_unprepare(priv->stmmac_clk);
+ clk_disable(priv->stmmac_clk);
}
spin_unlock_irqrestore(&priv->lock, flags);
} else {
pinctrl_pm_select_default_state(priv->device);
/* enable the clk prevously disabled */
- clk_prepare_enable(priv->stmmac_clk);
+ clk_enable(priv->stmmac_clk);
/* reset the phy so that it's ready */
if (priv->mii)
stmmac_mdio_reset(priv->mii);
netif_device_attach(ndev);
+ init_dma_desc_rings(ndev, GFP_ATOMIC);
stmmac_hw_setup(ndev);
+ stmmac_init_tx_coalesce(priv);
napi_enable(&priv->napi);
*/
plat->maxmtu = JUMBO_LEN;
- /* Set default value for multicast hash bins */
- plat->multicast_filter_bins = HASH_TABLE_SIZE;
-
- /* Set default value for unicast filter entries */
- plat->unicast_filter_entries = 1;
-
/*
* Currently only the properties needed on SPEAr600
* are provided. All other properties should be added
return PTR_ERR(addr);
plat_dat = dev_get_platdata(&pdev->dev);
- if (pdev->dev.of_node) {
- if (!plat_dat)
- plat_dat = devm_kzalloc(&pdev->dev,
+
+ if (!plat_dat)
+ plat_dat = devm_kzalloc(&pdev->dev,
sizeof(struct plat_stmmacenet_data),
GFP_KERNEL);
- if (!plat_dat) {
- pr_err("%s: ERROR: no memory", __func__);
- return -ENOMEM;
- }
+ if (!plat_dat) {
+ pr_err("%s: ERROR: no memory", __func__);
+ return -ENOMEM;
+ }
+
+ /* Set default value for multicast hash bins */
+ plat_dat->multicast_filter_bins = HASH_TABLE_SIZE;
+ /* Set default value for unicast filter entries */
+ plat_dat->unicast_filter_entries = 1;
+
+ if (pdev->dev.of_node) {
ret = stmmac_probe_config_dt(pdev, plat_dat, &mac);
if (ret) {
pr_err("%s: main dt probe failed", __func__);
HMD(("init rxring, "));
for (i = 0; i < RX_RING_SIZE; i++) {
struct sk_buff *skb;
+ u32 mapping;
skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC);
if (!skb) {
/* Because we reserve afterwards. */
skb_put(skb, (ETH_FRAME_LEN + RX_OFFSET + 4));
+ mapping = dma_map_single(hp->dma_dev, skb->data, RX_BUF_ALLOC_SIZE,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(hp->dma_dev, mapping)) {
+ dev_kfree_skb_any(skb);
+ hme_write_rxd(hp, &hb->happy_meal_rxd[i], 0, 0);
+ continue;
+ }
hme_write_rxd(hp, &hb->happy_meal_rxd[i],
(RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16)),
- dma_map_single(hp->dma_dev, skb->data, RX_BUF_ALLOC_SIZE,
- DMA_FROM_DEVICE));
+ mapping);
skb_reserve(skb, RX_OFFSET);
}
skb = hp->rx_skbs[elem];
if (len > RX_COPY_THRESHOLD) {
struct sk_buff *new_skb;
+ u32 mapping;
/* Now refill the entry, if we can. */
new_skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC);
drops++;
goto drop_it;
}
+ skb_put(new_skb, (ETH_FRAME_LEN + RX_OFFSET + 4));
+ mapping = dma_map_single(hp->dma_dev, new_skb->data,
+ RX_BUF_ALLOC_SIZE,
+ DMA_FROM_DEVICE);
+ if (unlikely(dma_mapping_error(hp->dma_dev, mapping))) {
+ dev_kfree_skb_any(new_skb);
+ drops++;
+ goto drop_it;
+ }
+
dma_unmap_single(hp->dma_dev, dma_addr, RX_BUF_ALLOC_SIZE, DMA_FROM_DEVICE);
hp->rx_skbs[elem] = new_skb;
- skb_put(new_skb, (ETH_FRAME_LEN + RX_OFFSET + 4));
hme_write_rxd(hp, this,
(RXFLAG_OWN|((RX_BUF_ALLOC_SIZE-RX_OFFSET)<<16)),
- dma_map_single(hp->dma_dev, new_skb->data, RX_BUF_ALLOC_SIZE,
- DMA_FROM_DEVICE));
+ mapping);
skb_reserve(new_skb, RX_OFFSET);
/* Trim the original skb for the netif. */
netif_wake_queue(dev);
}
+static void unmap_partial_tx_skb(struct happy_meal *hp, u32 first_mapping,
+ u32 first_len, u32 first_entry, u32 entry)
+{
+ struct happy_meal_txd *txbase = &hp->happy_block->happy_meal_txd[0];
+
+ dma_unmap_single(hp->dma_dev, first_mapping, first_len, DMA_TO_DEVICE);
+
+ first_entry = NEXT_TX(first_entry);
+ while (first_entry != entry) {
+ struct happy_meal_txd *this = &txbase[first_entry];
+ u32 addr, len;
+
+ addr = hme_read_desc32(hp, &this->tx_addr);
+ len = hme_read_desc32(hp, &this->tx_flags);
+ len &= TXFLAG_SIZE;
+ dma_unmap_page(hp->dma_dev, addr, len, DMA_TO_DEVICE);
+ }
+}
+
static netdev_tx_t happy_meal_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
len = skb->len;
mapping = dma_map_single(hp->dma_dev, skb->data, len, DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(hp->dma_dev, mapping)))
+ goto out_dma_error;
tx_flags |= (TXFLAG_SOP | TXFLAG_EOP);
hme_write_txd(hp, &hp->happy_block->happy_meal_txd[entry],
(tx_flags | (len & TXFLAG_SIZE)),
first_len = skb_headlen(skb);
first_mapping = dma_map_single(hp->dma_dev, skb->data, first_len,
DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(hp->dma_dev, first_mapping)))
+ goto out_dma_error;
entry = NEXT_TX(entry);
for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) {
len = skb_frag_size(this_frag);
mapping = skb_frag_dma_map(hp->dma_dev, this_frag,
0, len, DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(hp->dma_dev, mapping))) {
+ unmap_partial_tx_skb(hp, first_mapping, first_len,
+ first_entry, entry);
+ goto out_dma_error;
+ }
this_txflags = tx_flags;
if (frag == skb_shinfo(skb)->nr_frags - 1)
this_txflags |= TXFLAG_EOP;
tx_add_log(hp, TXLOG_ACTION_TXMIT, 0);
return NETDEV_TX_OK;
+
+out_dma_error:
+ hp->tx_skbs[hp->tx_new] = NULL;
+ spin_unlock_irq(&hp->happy_lock);
+
+ dev_kfree_skb_any(skb);
+ dev->stats.tx_dropped++;
+ return NETDEV_TX_OK;
}
static struct net_device_stats *happy_meal_get_stats(struct net_device *dev)
#define CPSW_VLAN_AWARE BIT(1)
#define CPSW_ALE_VLAN_AWARE 1
-#define CPSW_FIFO_NORMAL_MODE (0 << 15)
-#define CPSW_FIFO_DUAL_MAC_MODE (1 << 15)
-#define CPSW_FIFO_RATE_LIMIT_MODE (2 << 15)
+#define CPSW_FIFO_NORMAL_MODE (0 << 16)
+#define CPSW_FIFO_DUAL_MAC_MODE (1 << 16)
+#define CPSW_FIFO_RATE_LIMIT_MODE (2 << 16)
#define CPSW_INTPACEEN (0x3f << 16)
#define CPSW_INTPRESCALE_MASK (0x7FF << 0)
{
if (!ale)
return -EINVAL;
- cpsw_ale_stop(ale);
cpsw_ale_control_set(ale, 0, ALE_ENABLE, 0);
kfree(ale);
return 0;
switch (ptp_class & PTP_CLASS_PMASK) {
case PTP_CLASS_IPV4:
- offset += ETH_HLEN + IPV4_HLEN(data) + UDP_HLEN;
+ offset += ETH_HLEN + IPV4_HLEN(data + offset) + UDP_HLEN;
break;
case PTP_CLASS_IPV6:
offset += ETH_HLEN + IP6_HLEN + UDP_HLEN;
err = wpan_phy_register(phy);
if (err)
- goto out;
+ goto err_phy_reg;
err = register_netdev(dev);
- if (err < 0)
- goto out;
+ if (err)
+ goto err_netdev_reg;
dev_info(&pdev->dev, "Added ieee802154 HardMAC hardware\n");
return 0;
-out:
- unregister_netdev(dev);
+err_netdev_reg:
+ wpan_phy_unregister(phy);
+err_phy_reg:
+ free_netdev(dev);
+ wpan_phy_free(phy);
return err;
}
if (skb->ip_summed == CHECKSUM_PARTIAL) {
vnet_hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
vnet_hdr->csum_start = skb_checksum_start_offset(skb);
+ if (vlan_tx_tag_present(skb))
+ vnet_hdr->csum_start += VLAN_HLEN;
vnet_hdr->csum_offset = skb->csum_offset;
} else if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
vnet_hdr->flags = VIRTIO_NET_HDR_F_DATA_VALID;
switch (type & PTP_CLASS_PMASK) {
case PTP_CLASS_IPV4:
- offset += ETH_HLEN + IPV4_HLEN(data) + UDP_HLEN;
+ offset += ETH_HLEN + IPV4_HLEN(data + offset) + UDP_HLEN;
break;
case PTP_CLASS_IPV6:
offset += ETH_HLEN + IP6_HLEN + UDP_HLEN;
switch (type & PTP_CLASS_PMASK) {
case PTP_CLASS_IPV4:
- offset += ETH_HLEN + IPV4_HLEN(data) + UDP_HLEN;
+ offset += ETH_HLEN + IPV4_HLEN(data + offset) + UDP_HLEN;
break;
case PTP_CLASS_IPV6:
offset += ETH_HLEN + IP6_HLEN + UDP_HLEN;
{
struct mii_ioctl_data *mii_data = if_mii(ifr);
u16 val = mii_data->val_in;
+ bool change_autoneg = false;
switch (cmd) {
case SIOCGMIIPHY:
if (mii_data->phy_id == phydev->addr) {
switch (mii_data->reg_num) {
case MII_BMCR:
- if ((val & (BMCR_RESET | BMCR_ANENABLE)) == 0)
+ if ((val & (BMCR_RESET | BMCR_ANENABLE)) == 0) {
+ if (phydev->autoneg == AUTONEG_ENABLE)
+ change_autoneg = true;
phydev->autoneg = AUTONEG_DISABLE;
- else
+ if (val & BMCR_FULLDPLX)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+ if (val & BMCR_SPEED1000)
+ phydev->speed = SPEED_1000;
+ else if (val & BMCR_SPEED100)
+ phydev->speed = SPEED_100;
+ else phydev->speed = SPEED_10;
+ }
+ else {
+ if (phydev->autoneg == AUTONEG_DISABLE)
+ change_autoneg = true;
phydev->autoneg = AUTONEG_ENABLE;
- if (!phydev->autoneg && (val & BMCR_FULLDPLX))
- phydev->duplex = DUPLEX_FULL;
- else
- phydev->duplex = DUPLEX_HALF;
- if (!phydev->autoneg && (val & BMCR_SPEED1000))
- phydev->speed = SPEED_1000;
- else if (!phydev->autoneg &&
- (val & BMCR_SPEED100))
- phydev->speed = SPEED_100;
+ }
break;
case MII_ADVERTISE:
- phydev->advertising = val;
+ phydev->advertising = mii_adv_to_ethtool_adv_t(val);
+ change_autoneg = true;
break;
default:
/* do nothing */
if (mii_data->reg_num == MII_BMCR &&
val & BMCR_RESET)
return phy_init_hw(phydev);
+
+ if (change_autoneg)
+ return phy_start_aneg(phydev);
+
return 0;
case SIOCSHWTSTAMP:
err = get_filter(argp, &code);
if (err >= 0) {
+ struct bpf_prog *pass_filter = NULL;
struct sock_fprog_kern fprog = {
.len = err,
.filter = code,
};
- ppp_lock(ppp);
- if (ppp->pass_filter) {
- bpf_prog_destroy(ppp->pass_filter);
- ppp->pass_filter = NULL;
+ err = 0;
+ if (fprog.filter)
+ err = bpf_prog_create(&pass_filter, &fprog);
+ if (!err) {
+ ppp_lock(ppp);
+ if (ppp->pass_filter)
+ bpf_prog_destroy(ppp->pass_filter);
+ ppp->pass_filter = pass_filter;
+ ppp_unlock(ppp);
}
- if (fprog.filter != NULL)
- err = bpf_prog_create(&ppp->pass_filter,
- &fprog);
- else
- err = 0;
kfree(code);
- ppp_unlock(ppp);
}
break;
}
err = get_filter(argp, &code);
if (err >= 0) {
+ struct bpf_prog *active_filter = NULL;
struct sock_fprog_kern fprog = {
.len = err,
.filter = code,
};
- ppp_lock(ppp);
- if (ppp->active_filter) {
- bpf_prog_destroy(ppp->active_filter);
- ppp->active_filter = NULL;
+ err = 0;
+ if (fprog.filter)
+ err = bpf_prog_create(&active_filter, &fprog);
+ if (!err) {
+ ppp_lock(ppp);
+ if (ppp->active_filter)
+ bpf_prog_destroy(ppp->active_filter);
+ ppp->active_filter = active_filter;
+ ppp_unlock(ppp);
}
- if (fprog.filter != NULL)
- err = bpf_prog_create(&ppp->active_filter,
- &fprog);
- else
- err = 0;
kfree(code);
- ppp_unlock(ppp);
}
break;
}
int len = sizeof(struct sockaddr_pppox);
struct sockaddr_pppox sp;
- sp.sa_family = AF_PPPOX;
+ memset(&sp.sa_addr, 0, sizeof(sp.sa_addr));
+
+ sp.sa_family = AF_PPPOX;
sp.sa_protocol = PX_PROTO_PPTP;
sp.sa_addr.pptp = pppox_sk(sock->sk)->proto.pptp.src_addr;
struct tun_pi pi = { 0, skb->protocol };
ssize_t total = 0;
int vlan_offset = 0, copied;
+ int vlan_hlen = 0;
+ int vnet_hdr_sz = 0;
+
+ if (vlan_tx_tag_present(skb))
+ vlan_hlen = VLAN_HLEN;
+
+ if (tun->flags & TUN_VNET_HDR)
+ vnet_hdr_sz = tun->vnet_hdr_sz;
if (!(tun->flags & TUN_NO_PI)) {
if ((len -= sizeof(pi)) < 0)
return -EINVAL;
- if (len < skb->len) {
+ if (len < skb->len + vlan_hlen + vnet_hdr_sz) {
/* Packet will be striped */
pi.flags |= TUN_PKT_STRIP;
}
total += sizeof(pi);
}
- if (tun->flags & TUN_VNET_HDR) {
+ if (vnet_hdr_sz) {
struct virtio_net_hdr gso = { 0 }; /* no info leak */
- if ((len -= tun->vnet_hdr_sz) < 0)
+ if ((len -= vnet_hdr_sz) < 0)
return -EINVAL;
if (skb_is_gso(skb)) {
if (skb->ip_summed == CHECKSUM_PARTIAL) {
gso.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
- gso.csum_start = skb_checksum_start_offset(skb);
+ gso.csum_start = skb_checksum_start_offset(skb) +
+ vlan_hlen;
gso.csum_offset = skb->csum_offset;
} else if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
gso.flags = VIRTIO_NET_HDR_F_DATA_VALID;
if (unlikely(memcpy_toiovecend(iv, (void *)&gso, total,
sizeof(gso))))
return -EFAULT;
- total += tun->vnet_hdr_sz;
+ total += vnet_hdr_sz;
}
copied = total;
- total += skb->len;
- if (!vlan_tx_tag_present(skb)) {
- len = min_t(int, skb->len, len);
- } else {
+ len = min_t(int, skb->len + vlan_hlen, len);
+ total += skb->len + vlan_hlen;
+ if (vlan_hlen) {
int copy, ret;
struct {
__be16 h_vlan_proto;
veth.h_vlan_TCI = htons(vlan_tx_tag_get(skb));
vlan_offset = offsetof(struct vlan_ethhdr, h_vlan_proto);
- len = min_t(int, skb->len + VLAN_HLEN, len);
- total += VLAN_HLEN;
copy = min_t(int, vlan_offset, len);
ret = skb_copy_datagram_const_iovec(skb, 0, iv, copied, copy);
return ret;
}
- ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL);
- if (ret < 0)
- return ret;
-
- msleep(150);
-
- ret = asix_sw_reset(dev, AX_SWRESET_CLEAR);
- if (ret < 0)
- return ret;
-
- msleep(150);
-
- ret = asix_sw_reset(dev, embd_phy ? AX_SWRESET_IPRL : AX_SWRESET_PRTE);
+ ax88772_reset(dev);
/* Read PHYID register *AFTER* the PHY was reset properly */
phyid = asix_get_phyid(dev);
{QMI_FIXED_INTF(0x413c, 0x81a4, 8)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */
{QMI_FIXED_INTF(0x413c, 0x81a8, 8)}, /* Dell Wireless 5808 Gobi(TM) 4G LTE Mobile Broadband Card */
{QMI_FIXED_INTF(0x413c, 0x81a9, 8)}, /* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card */
+ {QMI_FIXED_INTF(0x03f0, 0x581d, 4)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Module (Huawei me906e) */
/* 4. Gobi 1000 devices */
{QMI_GOBI1K_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */
};
#endif
+static bool virtnet_fail_on_feature(struct virtio_device *vdev,
+ unsigned int fbit,
+ const char *fname, const char *dname)
+{
+ if (!virtio_has_feature(vdev, fbit))
+ return false;
+
+ dev_err(&vdev->dev, "device advertises feature %s but not %s",
+ fname, dname);
+
+ return true;
+}
+
+#define VIRTNET_FAIL_ON(vdev, fbit, dbit) \
+ virtnet_fail_on_feature(vdev, fbit, #fbit, dbit)
+
+static bool virtnet_validate_features(struct virtio_device *vdev)
+{
+ if (!virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ) &&
+ (VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_CTRL_RX,
+ "VIRTIO_NET_F_CTRL_VQ") ||
+ VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_CTRL_VLAN,
+ "VIRTIO_NET_F_CTRL_VQ") ||
+ VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE,
+ "VIRTIO_NET_F_CTRL_VQ") ||
+ VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_MQ, "VIRTIO_NET_F_CTRL_VQ") ||
+ VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_CTRL_MAC_ADDR,
+ "VIRTIO_NET_F_CTRL_VQ"))) {
+ return false;
+ }
+
+ return true;
+}
+
static int virtnet_probe(struct virtio_device *vdev)
{
int i, err;
struct virtnet_info *vi;
u16 max_queue_pairs;
+ if (!virtnet_validate_features(vdev))
+ return -EINVAL;
+
/* Find if host supports multiqueue virtio_net device */
err = virtio_cread_feature(vdev, VIRTIO_NET_F_MQ,
struct virtio_net_config,
#define VXLAN_FLAGS 0x08000000 /* struct vxlanhdr.vx_flags required value. */
-/* VXLAN protocol header */
-struct vxlanhdr {
- __be32 vx_flags;
- __be32 vx_vni;
-};
-
/* UDP port for VXLAN traffic.
* The IANA assigned port is 4789, but the Linux default is 8472
* for compatibility with early adopters.
return list_first_entry(&fdb->remotes, struct vxlan_rdst, list);
}
-/* Find VXLAN socket based on network namespace and UDP port */
-static struct vxlan_sock *vxlan_find_sock(struct net *net, __be16 port)
+/* Find VXLAN socket based on network namespace, address family and UDP port */
+static struct vxlan_sock *vxlan_find_sock(struct net *net,
+ sa_family_t family, __be16 port)
{
struct vxlan_sock *vs;
hlist_for_each_entry_rcu(vs, vs_head(net, port), hlist) {
- if (inet_sk(vs->sock->sk)->inet_sport == port)
+ if (inet_sk(vs->sock->sk)->inet_sport == port &&
+ inet_sk(vs->sock->sk)->sk.sk_family == family)
return vs;
}
return NULL;
}
/* Look up VNI in a per net namespace table */
-static struct vxlan_dev *vxlan_find_vni(struct net *net, u32 id, __be16 port)
+static struct vxlan_dev *vxlan_find_vni(struct net *net, u32 id,
+ sa_family_t family, __be16 port)
{
struct vxlan_sock *vs;
- vs = vxlan_find_sock(net, port);
+ vs = vxlan_find_sock(net, family, port);
if (!vs)
return NULL;
int vxlan_len = sizeof(struct vxlanhdr) + sizeof(struct ethhdr);
int err = -ENOSYS;
+ udp_tunnel_gro_complete(skb, nhoff);
+
eh = (struct ethhdr *)(skb->data + nhoff + sizeof(struct vxlanhdr));
type = eh->h_proto;
struct vxlan_dev *dst_vxlan;
ip_rt_put(rt);
- dst_vxlan = vxlan_find_vni(vxlan->net, vni, dst_port);
+ dst_vxlan = vxlan_find_vni(vxlan->net, vni,
+ dst->sa.sa_family, dst_port);
if (!dst_vxlan)
goto tx_error;
vxlan_encap_bypass(skb, vxlan, dst_vxlan);
struct vxlan_dev *dst_vxlan;
dst_release(ndst);
- dst_vxlan = vxlan_find_vni(vxlan->net, vni, dst_port);
+ dst_vxlan = vxlan_find_vni(vxlan->net, vni,
+ dst->sa.sa_family, dst_port);
if (!dst_vxlan)
goto tx_error;
vxlan_encap_bypass(skb, vxlan, dst_vxlan);
struct vxlan_dev *vxlan = netdev_priv(dev);
struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id);
struct vxlan_sock *vs;
+ bool ipv6 = vxlan->flags & VXLAN_F_IPV6;
dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
if (!dev->tstats)
return -ENOMEM;
spin_lock(&vn->sock_lock);
- vs = vxlan_find_sock(vxlan->net, vxlan->dst_port);
+ vs = vxlan_find_sock(vxlan->net, ipv6 ? AF_INET6 : AF_INET,
+ vxlan->dst_port);
if (vs) {
/* If we have a socket with same port already, reuse it */
atomic_inc(&vs->refcnt);
if (ipv6) {
udp_conf.family = AF_INET6;
udp_conf.use_udp6_tx_checksums =
- !!(flags & VXLAN_F_UDP_ZERO_CSUM6_TX);
+ !(flags & VXLAN_F_UDP_ZERO_CSUM6_TX);
udp_conf.use_udp6_rx_checksums =
- !!(flags & VXLAN_F_UDP_ZERO_CSUM6_RX);
+ !(flags & VXLAN_F_UDP_ZERO_CSUM6_RX);
} else {
udp_conf.family = AF_INET;
udp_conf.local_ip.s_addr = INADDR_ANY;
{
struct vxlan_net *vn = net_generic(net, vxlan_net_id);
struct vxlan_sock *vs;
+ bool ipv6 = flags & VXLAN_F_IPV6;
vs = vxlan_socket_create(net, port, rcv, data, flags);
if (!IS_ERR(vs))
return vs;
spin_lock(&vn->sock_lock);
- vs = vxlan_find_sock(net, port);
+ vs = vxlan_find_sock(net, ipv6 ? AF_INET6 : AF_INET, port);
if (vs) {
if (vs->rcv == rcv)
atomic_inc(&vs->refcnt);
nla_get_u8(data[IFLA_VXLAN_UDP_ZERO_CSUM6_RX]))
vxlan->flags |= VXLAN_F_UDP_ZERO_CSUM6_RX;
- if (vxlan_find_vni(net, vni, vxlan->dst_port)) {
+ if (vxlan_find_vni(net, vni, use_ipv6 ? AF_INET6 : AF_INET,
+ vxlan->dst_port)) {
pr_info("duplicate VNI %u\n", vni);
return -EEXIST;
}
ah->enabled_cals |= TX_CL_CAL;
else
ah->enabled_cals &= ~TX_CL_CAL;
+
+ if (AR_SREV_9340(ah) || AR_SREV_9531(ah) || AR_SREV_9550(ah)) {
+ if (ah->is_clk_25mhz) {
+ REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x17c << 1);
+ REG_WRITE(ah, AR_SLP32_MODE, 0x0010f3d7);
+ REG_WRITE(ah, AR_SLP32_INC, 0x0001e7ae);
+ } else {
+ REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x261 << 1);
+ REG_WRITE(ah, AR_SLP32_MODE, 0x0010f400);
+ REG_WRITE(ah, AR_SLP32_INC, 0x0001e800);
+ }
+ udelay(100);
+ }
}
static void ar9003_hw_prog_ini(struct ath_hw *ah,
udelay(RTC_PLL_SETTLE_DELAY);
REG_WRITE(ah, AR_RTC_SLEEP_CLK, AR_RTC_FORCE_DERIVED_CLK);
-
- if (AR_SREV_9340(ah) || AR_SREV_9550(ah)) {
- if (ah->is_clk_25mhz) {
- REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x17c << 1);
- REG_WRITE(ah, AR_SLP32_MODE, 0x0010f3d7);
- REG_WRITE(ah, AR_SLP32_INC, 0x0001e7ae);
- } else {
- REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x261 << 1);
- REG_WRITE(ah, AR_SLP32_MODE, 0x0010f400);
- REG_WRITE(ah, AR_SLP32_INC, 0x0001e800);
- }
- udelay(100);
- }
}
static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah,
struct ath_vif *avp;
/*
- * Pick the MAC address of the first interface as the new hardware
- * MAC address. The hardware will use it together with the BSSID mask
- * when matching addresses.
+ * The hardware will use primary station addr together with the
+ * BSSID mask when matching addresses.
*/
memset(iter_data, 0, sizeof(*iter_data));
memset(&iter_data->mask, 0xff, ETH_ALEN);
list_add_tail(&avp->list, &avp->chanctx->vifs);
}
+ ath9k_calculate_summary_state(sc, avp->chanctx);
+
ath9k_assign_hw_queues(hw, vif);
an->sc = sc;
ath_tx_node_cleanup(sc, &avp->mcast_node);
+ ath9k_calculate_summary_state(sc, avp->chanctx);
+
mutex_unlock(&sc->mutex);
}
void b43_phy_copy(struct b43_wldev *dev, u16 destreg, u16 srcreg)
{
- assert_mac_suspended(dev);
- dev->phy.ops->phy_write(dev, destreg,
- dev->phy.ops->phy_read(dev, srcreg));
+ b43_phy_write(dev, destreg, b43_phy_read(dev, srcreg));
}
void b43_phy_mask(struct b43_wldev *dev, u16 offset, u16 mask)
return;
irq = irq_of_parse_and_map(np, 0);
- if (irq < 0) {
- brcmf_err("interrupt could not be mapped: err=%d\n", irq);
+ if (!irq) {
+ brcmf_err("interrupt could not be mapped\n");
devm_kfree(dev, sdiodev->pdata);
return;
}
#include <linux/pci.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
-#include <linux/unaligned/access_ok.h>
#include <linux/interrupt.h>
#include <linux/bcma/bcma.h>
#include <linux/sched.h>
+#include <asm/unaligned.h>
#include <soc.h>
#include <chipcommon.h>
goto finalize;
}
- if (!brcmf_usb_ioctl_resp_wait(devinfo))
+ if (!brcmf_usb_ioctl_resp_wait(devinfo)) {
+ usb_kill_urb(devinfo->ctl_urb);
ret = -ETIMEDOUT;
- else
+ } else {
memcpy(buffer, tmpbuf, buflen);
+ }
finalize:
kfree(tmpbuf);
primary_offset = ch->center_freq1 - ch->chan->center_freq;
switch (ch->width) {
case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_20_NOHT:
ch_inf.bw = BRCMU_CHAN_BW_20;
WARN_ON(primary_offset != 0);
break;
ch_inf.sb = BRCMU_CHAN_SB_LU;
}
break;
+ case NL80211_CHAN_WIDTH_80P80:
+ case NL80211_CHAN_WIDTH_160:
+ case NL80211_CHAN_WIDTH_5:
+ case NL80211_CHAN_WIDTH_10:
default:
WARN_ON_ONCE(1);
}
case IEEE80211_BAND_5GHZ:
ch_inf.band = BRCMU_CHAN_BAND_5G;
break;
+ case IEEE80211_BAND_60GHZ:
default:
WARN_ON_ONCE(1);
}
* @IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT: supports Quiet Period requests
* @IWL_UCODE_TLV_CAPA_DQA_SUPPORT: supports dynamic queue allocation (DQA),
* which also implies support for the scheduler configuration command
+ * @IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT: supports Hot Spot Command
*/
enum iwl_ucode_tlv_capa {
IWL_UCODE_TLV_CAPA_D0I3_SUPPORT = BIT(0),
IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT = BIT(10),
IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT = BIT(11),
IWL_UCODE_TLV_CAPA_DQA_SUPPORT = BIT(12),
+ IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT = BIT(18),
};
/* The default calibrate table size if not specified by firmware file */
lockdep_assert_held(&mvm->mutex);
- if (WARN_ON_ONCE(mvm->init_ucode_complete))
+ if (WARN_ON_ONCE(mvm->init_ucode_complete || mvm->calibrating))
return 0;
iwl_init_notification_wait(&mvm->notif_wait,
goto out;
}
+ mvm->calibrating = true;
+
/* Send TX valid antennas before triggering calibrations */
ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant);
if (ret)
MVM_UCODE_CALIB_TIMEOUT);
if (!ret)
mvm->init_ucode_complete = true;
+
+ if (ret && iwl_mvm_is_radio_killed(mvm)) {
+ IWL_DEBUG_RF_KILL(mvm, "RFKILL while calibrating.\n");
+ ret = 1;
+ }
goto out;
error:
iwl_remove_notification(&mvm->notif_wait, &calib_wait);
out:
+ mvm->calibrating = false;
if (iwlmvm_mod_params.init_dbg && !mvm->nvm_data) {
/* we want to debug INIT and we have no NVM - fake */
mvm->nvm_data = kzalloc(sizeof(struct iwl_nvm_data) +
mvm->scan_status = IWL_MVM_SCAN_NONE;
mvm->ps_disabled = false;
+ mvm->calibrating = false;
/* just in case one was running */
ieee80211_remain_on_channel_expired(mvm->hw);
switch (vif->type) {
case NL80211_IFTYPE_STATION:
- /* Use aux roc framework (HS20) */
- ret = iwl_mvm_send_aux_roc_cmd(mvm, channel,
- vif, duration);
+ if (mvm->fw->ucode_capa.capa[0] &
+ IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT) {
+ /* Use aux roc framework (HS20) */
+ ret = iwl_mvm_send_aux_roc_cmd(mvm, channel,
+ vif, duration);
+ goto out_unlock;
+ }
+ IWL_ERR(mvm, "hotspot not supported\n");
+ ret = -EINVAL;
goto out_unlock;
case NL80211_IFTYPE_P2P_DEVICE:
/* handle below */
enum iwl_ucode_type cur_ucode;
bool ucode_loaded;
bool init_ucode_complete;
+ bool calibrating;
u32 error_event_table;
u32 log_event_table;
u32 umac_error_event_table;
}
mvm->sf_state = SF_UNINIT;
mvm->low_latency_agg_frame_limit = 6;
+ mvm->cur_ucode = IWL_UCODE_INIT;
mutex_init(&mvm->mutex);
mutex_init(&mvm->d0i3_suspend_mutex);
static bool iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
{
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+ bool calibrating = ACCESS_ONCE(mvm->calibrating);
if (state)
set_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status);
wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm));
- return state && mvm->cur_ucode != IWL_UCODE_INIT;
+ /* iwl_run_init_mvm_ucode is waiting for results, abort it */
+ if (calibrating)
+ iwl_abort_notification_waits(&mvm->notif_wait);
+
+ /*
+ * Stop the device if we run OPERATIONAL firmware or if we are in the
+ * middle of the calibrations.
+ */
+ return state && (mvm->cur_ucode != IWL_UCODE_INIT || calibrating);
}
static void iwl_mvm_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb)
SCAN_COMPLETE_NOTIFICATION };
int ret;
- if (mvm->scan_status == IWL_MVM_SCAN_NONE)
- return 0;
-
- if (iwl_mvm_is_radio_killed(mvm)) {
- ieee80211_scan_completed(mvm->hw, true);
- iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
- mvm->scan_status = IWL_MVM_SCAN_NONE;
- return 0;
- }
-
iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_abort,
scan_abort_notif,
ARRAY_SIZE(scan_abort_notif),
int iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
{
+ if (mvm->scan_status == IWL_MVM_SCAN_NONE)
+ return 0;
+
+ if (iwl_mvm_is_radio_killed(mvm)) {
+ ieee80211_scan_completed(mvm->hw, true);
+ iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
+ mvm->scan_status = IWL_MVM_SCAN_NONE;
+ return 0;
+ }
+
if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
return iwl_mvm_scan_offload_stop(mvm, true);
return iwl_mvm_cancel_regular_scan(mvm);
* restart. So don't process again if the device is
* already dead.
*/
- if (test_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
+ if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
+ IWL_DEBUG_INFO(trans, "DEVICE_ENABLED bit was set and is now cleared\n");
iwl_pcie_tx_stop(trans);
iwl_pcie_rx_stop(trans);
/* clear all status bits */
clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
clear_bit(STATUS_INT_ENABLED, &trans->status);
- clear_bit(STATUS_DEVICE_ENABLED, &trans->status);
clear_bit(STATUS_TPOWER_PMI, &trans->status);
clear_bit(STATUS_RFKILL, &trans->status);
int reg;
__le32 *val;
- prph_len += sizeof(*data) + sizeof(*prph) +
- num_bytes_in_chunk;
+ prph_len += sizeof(**data) + sizeof(*prph) + num_bytes_in_chunk;
(*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH);
(*data)->len = cpu_to_le32(sizeof(*prph) +
if (err != 0) {
printk(KERN_DEBUG "mac80211_hwsim: device_bind_driver failed (%d)\n",
err);
- goto failed_hw;
+ goto failed_bind;
}
skb_queue_head_init(&data->pending);
return idx;
failed_hw:
+ device_release_driver(data->dev);
+failed_bind:
device_unregister(data->dev);
failed_drvdata:
ieee80211_free_hw(hw);
skb_trim(skb, frame_length);
}
-void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int header_length)
+/*
+ * H/W needs L2 padding between the header and the paylod if header size
+ * is not 4 bytes aligned.
+ */
+void rt2x00queue_insert_l2pad(struct sk_buff *skb, unsigned int hdr_len)
{
- unsigned int payload_length = skb->len - header_length;
- unsigned int header_align = ALIGN_SIZE(skb, 0);
- unsigned int payload_align = ALIGN_SIZE(skb, header_length);
- unsigned int l2pad = payload_length ? L2PAD_SIZE(header_length) : 0;
+ unsigned int l2pad = (skb->len > hdr_len) ? L2PAD_SIZE(hdr_len) : 0;
- /*
- * Adjust the header alignment if the payload needs to be moved more
- * than the header.
- */
- if (payload_align > header_align)
- header_align += 4;
-
- /* There is nothing to do if no alignment is needed */
- if (!header_align)
+ if (!l2pad)
return;
- /* Reserve the amount of space needed in front of the frame */
- skb_push(skb, header_align);
-
- /*
- * Move the header.
- */
- memmove(skb->data, skb->data + header_align, header_length);
-
- /* Move the payload, if present and if required */
- if (payload_length && payload_align)
- memmove(skb->data + header_length + l2pad,
- skb->data + header_length + l2pad + payload_align,
- payload_length);
-
- /* Trim the skb to the correct size */
- skb_trim(skb, header_length + l2pad + payload_length);
+ skb_push(skb, l2pad);
+ memmove(skb->data, skb->data + l2pad, hdr_len);
}
-void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int header_length)
+void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int hdr_len)
{
- /*
- * L2 padding is only present if the skb contains more than just the
- * IEEE 802.11 header.
- */
- unsigned int l2pad = (skb->len > header_length) ?
- L2PAD_SIZE(header_length) : 0;
+ unsigned int l2pad = (skb->len > hdr_len) ? L2PAD_SIZE(hdr_len) : 0;
if (!l2pad)
return;
- memmove(skb->data + l2pad, skb->data, header_length);
+ memmove(skb->data + l2pad, skb->data, hdr_len);
skb_pull(skb, l2pad);
}
break;
}
/* handle command packet here */
- if (rtlpriv->cfg->ops->rx_command_packet(hw, stats, skb)) {
+ if (rtlpriv->cfg->ops->rx_command_packet &&
+ rtlpriv->cfg->ops->rx_command_packet(hw, stats, skb)) {
dev_kfree_skb_any(skb);
goto end;
}
__skb_queue_tail(&ring->queue, pskb);
- rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, true, HW_DESC_OWN,
- &temp_one);
-
+ if (rtlpriv->use_new_trx_flow) {
+ temp_one = 4;
+ rtlpriv->cfg->ops->set_desc(hw, (u8 *)pbuffer_desc, true,
+ HW_DESC_OWN, (u8 *)&temp_one);
+ } else {
+ rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, true, HW_DESC_OWN,
+ &temp_one);
+ }
return;
}
ring->desc = NULL;
if (rtlpriv->use_new_trx_flow) {
pci_free_consistent(rtlpci->pdev,
- sizeof(*ring->desc) * ring->entries,
+ sizeof(*ring->buffer_desc) * ring->entries,
ring->buffer_desc, ring->buffer_desc_dma);
- ring->desc = NULL;
+ ring->buffer_desc = NULL;
}
}
true,
HW_DESC_TXBUFF_ADDR),
skb->len, PCI_DMA_TODEVICE);
- ring->idx = (ring->idx + 1) % ring->entries;
kfree_skb(skb);
ring->idx = (ring->idx + 1) % ring->entries;
}
/*like read eeprom and so on */
rtlpriv->cfg->ops->read_eeprom_info(hw);
+ if (rtlpriv->cfg->ops->init_sw_vars(hw)) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Can't init_sw_vars\n");
+ err = -ENODEV;
+ goto fail3;
+ }
+ rtlpriv->cfg->ops->init_sw_leds(hw);
+
+ /*aspm */
+ rtl_pci_init_aspm(hw);
+
/* Init mac80211 sw */
err = rtl_init_core(hw);
if (err) {
goto fail3;
}
- if (rtlpriv->cfg->ops->init_sw_vars(hw)) {
- RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Can't init_sw_vars\n");
- err = -ENODEV;
- goto fail3;
- }
- rtlpriv->cfg->ops->init_sw_leds(hw);
-
- /*aspm */
- rtl_pci_init_aspm(hw);
-
err = ieee80211_register_hw(hw);
if (err) {
RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
}
+ if (type != NL80211_IFTYPE_AP &&
+ rtlpriv->mac80211.link_state < MAC80211_LINKED)
+ bt_msr = rtl_read_byte(rtlpriv, MSR) & ~MSR_LINK_MASK;
rtl_write_byte(rtlpriv, (MSR), bt_msr);
temp = rtl_read_dword(rtlpriv, TCR);
rtl_write_dword(rtlpriv, INTA_MASK, rtlpci->irq_mask[0]);
/* Support Bit 32-37(Assign as Bit 0-5) interrupt setting now */
rtl_write_dword(rtlpriv, INTA_MASK + 4, rtlpci->irq_mask[1] & 0x3F);
+ rtlpci->irq_enabled = true;
}
void rtl92se_disable_interrupt(struct ieee80211_hw *hw)
rtlpci = rtl_pcidev(rtl_pcipriv(hw));
rtl_write_dword(rtlpriv, INTA_MASK, 0);
rtl_write_dword(rtlpriv, INTA_MASK + 4, 0);
-
- synchronize_irq(rtlpci->pdev->irq);
+ rtlpci->irq_enabled = false;
}
static u8 _rtl92s_set_sysclk(struct ieee80211_hw *hw, u8 data)
case 2:
currentcmd = &postcommoncmd[*step];
break;
+ default:
+ return true;
}
if (currentcmd->cmdid == CMDID_END) {
}
}
+static bool rtl92se_is_tx_desc_closed(struct ieee80211_hw *hw, u8 hw_queue,
+ u16 index)
+{
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ struct rtl8192_tx_ring *ring = &rtlpci->tx_ring[hw_queue];
+ u8 *entry = (u8 *)(&ring->desc[ring->idx]);
+ u8 own = (u8)rtl92se_get_desc(entry, true, HW_DESC_OWN);
+
+ if (own)
+ return false;
+ return true;
+}
+
static struct rtl_hal_ops rtl8192se_hal_ops = {
.init_sw_vars = rtl92s_init_sw_vars,
.deinit_sw_vars = rtl92s_deinit_sw_vars,
.led_control = rtl92se_led_control,
.set_desc = rtl92se_set_desc,
.get_desc = rtl92se_get_desc,
+ .is_tx_desc_closed = rtl92se_is_tx_desc_closed,
.tx_polling = rtl92se_tx_polling,
.enable_hw_sec = rtl92se_enable_hw_security_config,
.set_key = rtl92se_set_key,
.maps[MAC_RCR_ACRC32] = RCR_ACRC32,
.maps[MAC_RCR_ACF] = RCR_ACF,
.maps[MAC_RCR_AAP] = RCR_AAP,
+ .maps[MAC_HIMR] = INTA_MASK,
+ .maps[MAC_HIMRE] = INTA_MASK + 4,
.maps[EFUSE_TEST] = REG_EFUSE_TEST,
.maps[EFUSE_CTRL] = REG_EFUSE_CTRL,
mac->opmode == NL80211_IFTYPE_ADHOC)
macid = sta->aid + 1;
if (wirelessmode == WIRELESS_MODE_N_5G ||
- wirelessmode == WIRELESS_MODE_AC_5G)
- ratr_bitmap = sta->supp_rates[NL80211_BAND_5GHZ];
+ wirelessmode == WIRELESS_MODE_AC_5G ||
+ wirelessmode == WIRELESS_MODE_A)
+ ratr_bitmap = sta->supp_rates[NL80211_BAND_5GHZ] << 4;
else
ratr_bitmap = sta->supp_rates[NL80211_BAND_2GHZ];
static int connect_rings(struct backend_info *be, struct xenvif_queue *queue);
static void connect(struct backend_info *be);
static int read_xenbus_vif_flags(struct backend_info *be);
-static void backend_create_xenvif(struct backend_info *be);
+static int backend_create_xenvif(struct backend_info *be);
static void unregister_hotplug_status_watch(struct backend_info *be);
static void set_backend_state(struct backend_info *be,
enum xenbus_state state);
be->state = XenbusStateInitWait;
/* This kicks hotplug scripts, so do it immediately. */
- backend_create_xenvif(be);
+ err = backend_create_xenvif(be);
+ if (err)
+ goto fail;
return 0;
}
-static void backend_create_xenvif(struct backend_info *be)
+static int backend_create_xenvif(struct backend_info *be)
{
int err;
long handle;
struct xenbus_device *dev = be->dev;
if (be->vif != NULL)
- return;
+ return 0;
err = xenbus_scanf(XBT_NIL, dev->nodename, "handle", "%li", &handle);
if (err != 1) {
xenbus_dev_fatal(dev, err, "reading handle");
- return;
+ return (err < 0) ? err : -EINVAL;
}
be->vif = xenvif_alloc(&dev->dev, dev->otherend_id, handle);
err = PTR_ERR(be->vif);
be->vif = NULL;
xenbus_dev_fatal(dev, err, "creating interface");
- return;
+ return err;
}
kobject_uevent(&dev->dev.kobj, KOBJ_ONLINE);
+ return 0;
}
static void backend_disconnect(struct backend_info *be)
len = skb_frag_size(frag);
offset = frag->page_offset;
- /* Data must not cross a page boundary. */
- BUG_ON(len + offset > PAGE_SIZE<<compound_order(page));
-
/* Skip unused frames from start of page */
page += offset >> PAGE_SHIFT;
offset &= ~PAGE_MASK;
while (len > 0) {
unsigned long bytes;
- BUG_ON(offset >= PAGE_SIZE);
-
bytes = PAGE_SIZE - offset;
if (bytes > len)
bytes = len;
rp = nubus_rom_addr(slot);
for(i = 4; i; i--)
{
- unsigned long flags;
int card_present;
rp--;
- local_irq_save(flags);
card_present = hwreg_present(rp);
- local_irq_restore(flags);
-
if (!card_present)
continue;
return NULL;
}
+static int of_empty_ranges_quirk(void)
+{
+ if (IS_ENABLED(CONFIG_PPC)) {
+ /* To save cycles, we cache the result */
+ static int quirk_state = -1;
+
+ if (quirk_state < 0)
+ quirk_state =
+ of_machine_is_compatible("Power Macintosh") ||
+ of_machine_is_compatible("MacRISC");
+ return quirk_state;
+ }
+ return false;
+}
+
static int of_translate_one(struct device_node *parent, struct of_bus *bus,
struct of_bus *pbus, __be32 *addr,
int na, int ns, int pna, const char *rprop)
* This code is only enabled on powerpc. --gcl
*/
ranges = of_get_property(parent, rprop, &rlen);
-#if !defined(CONFIG_PPC)
- if (ranges == NULL) {
+ if (ranges == NULL && !of_empty_ranges_quirk()) {
pr_err("OF: no ranges; cannot translate\n");
return 1;
}
-#endif /* !defined(CONFIG_PPC) */
if (ranges == NULL || rlen == 0) {
offset = of_read_number(addr, na);
memset(addr, 0, pna * 4);
}
EXPORT_SYMBOL_GPL(of_property_read_string);
-/**
- * of_property_read_string_index - Find and read a string from a multiple
- * strings property.
- * @np: device node from which the property value is to be read.
- * @propname: name of the property to be searched.
- * @index: index of the string in the list of strings
- * @out_string: pointer to null terminated return string, modified only if
- * return value is 0.
- *
- * Search for a property in a device tree node and retrieve a null
- * terminated string value (pointer to data, not a copy) in the list of strings
- * contained in that property.
- * Returns 0 on success, -EINVAL if the property does not exist, -ENODATA if
- * property does not have a value, and -EILSEQ if the string is not
- * null-terminated within the length of the property data.
- *
- * The out_string pointer is modified only if a valid string can be decoded.
- */
-int of_property_read_string_index(struct device_node *np, const char *propname,
- int index, const char **output)
-{
- struct property *prop = of_find_property(np, propname, NULL);
- int i = 0;
- size_t l = 0, total = 0;
- const char *p;
-
- if (!prop)
- return -EINVAL;
- if (!prop->value)
- return -ENODATA;
- if (strnlen(prop->value, prop->length) >= prop->length)
- return -EILSEQ;
-
- p = prop->value;
-
- for (i = 0; total < prop->length; total += l, p += l) {
- l = strlen(p) + 1;
- if (i++ == index) {
- *output = p;
- return 0;
- }
- }
- return -ENODATA;
-}
-EXPORT_SYMBOL_GPL(of_property_read_string_index);
-
/**
* of_property_match_string() - Find string in a list and return index
* @np: pointer to node containing string list property
end = p + prop->length;
for (i = 0; p < end; i++, p += l) {
- l = strlen(p) + 1;
+ l = strnlen(p, end - p) + 1;
if (p + l > end)
return -EILSEQ;
pr_debug("comparing %s with %s\n", string, p);
EXPORT_SYMBOL_GPL(of_property_match_string);
/**
- * of_property_count_strings - Find and return the number of strings from a
- * multiple strings property.
+ * of_property_read_string_util() - Utility helper for parsing string properties
* @np: device node from which the property value is to be read.
* @propname: name of the property to be searched.
+ * @out_strs: output array of string pointers.
+ * @sz: number of array elements to read.
+ * @skip: Number of strings to skip over at beginning of list.
*
- * Search for a property in a device tree node and retrieve the number of null
- * terminated string contain in it. Returns the number of strings on
- * success, -EINVAL if the property does not exist, -ENODATA if property
- * does not have a value, and -EILSEQ if the string is not null-terminated
- * within the length of the property data.
+ * Don't call this function directly. It is a utility helper for the
+ * of_property_read_string*() family of functions.
*/
-int of_property_count_strings(struct device_node *np, const char *propname)
+int of_property_read_string_helper(struct device_node *np, const char *propname,
+ const char **out_strs, size_t sz, int skip)
{
struct property *prop = of_find_property(np, propname, NULL);
- int i = 0;
- size_t l = 0, total = 0;
- const char *p;
+ int l = 0, i = 0;
+ const char *p, *end;
if (!prop)
return -EINVAL;
if (!prop->value)
return -ENODATA;
- if (strnlen(prop->value, prop->length) >= prop->length)
- return -EILSEQ;
-
p = prop->value;
+ end = p + prop->length;
- for (i = 0; total < prop->length; total += l, p += l, i++)
- l = strlen(p) + 1;
-
- return i;
+ for (i = 0; p < end && (!out_strs || i < skip + sz); i++, p += l) {
+ l = strnlen(p, end - p) + 1;
+ if (p + l > end)
+ return -EILSEQ;
+ if (out_strs && i >= skip)
+ *out_strs++ = p;
+ }
+ i -= skip;
+ return i <= 0 ? -ENODATA : i;
}
-EXPORT_SYMBOL_GPL(of_property_count_strings);
+EXPORT_SYMBOL_GPL(of_property_read_string_helper);
void of_print_phandle_args(const char *msg, const struct of_phandle_args *args)
{
* @allocflags: Allocation flags (typically pass GFP_KERNEL)
*
* Copy a property by dynamically allocating the memory of both the
- * property stucture and the property name & contents. The property's
+ * property structure and the property name & contents. The property's
* flags have the OF_DYNAMIC bit set so that we can differentiate between
* dynamically allocated properties and not.
* Returns the newly allocated property or NULL on out of memory error.
if (offset < 0)
return -ENODEV;
- while (match->compatible) {
+ while (match->compatible[0]) {
unsigned long addr;
if (fdt_node_check_compatible(fdt, offset, match->compatible)) {
match++;
int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base,
phys_addr_t size, bool nomap)
{
- if (memblock_is_region_reserved(base, size))
- return -EBUSY;
if (nomap)
return memblock_remove(base, size);
return memblock_reserve(base, size);
selftest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
}
-static void __init of_selftest_property_match_string(void)
+static void __init of_selftest_property_string(void)
{
+ const char *strings[4];
struct device_node *np;
int rc;
rc = of_property_match_string(np, "phandle-list-names", "third");
selftest(rc == 2, "third expected:0 got:%i\n", rc);
rc = of_property_match_string(np, "phandle-list-names", "fourth");
- selftest(rc == -ENODATA, "unmatched string; rc=%i", rc);
+ selftest(rc == -ENODATA, "unmatched string; rc=%i\n", rc);
rc = of_property_match_string(np, "missing-property", "blah");
- selftest(rc == -EINVAL, "missing property; rc=%i", rc);
+ selftest(rc == -EINVAL, "missing property; rc=%i\n", rc);
rc = of_property_match_string(np, "empty-property", "blah");
- selftest(rc == -ENODATA, "empty property; rc=%i", rc);
+ selftest(rc == -ENODATA, "empty property; rc=%i\n", rc);
rc = of_property_match_string(np, "unterminated-string", "blah");
- selftest(rc == -EILSEQ, "unterminated string; rc=%i", rc);
+ selftest(rc == -EILSEQ, "unterminated string; rc=%i\n", rc);
+
+ /* of_property_count_strings() tests */
+ rc = of_property_count_strings(np, "string-property");
+ selftest(rc == 1, "Incorrect string count; rc=%i\n", rc);
+ rc = of_property_count_strings(np, "phandle-list-names");
+ selftest(rc == 3, "Incorrect string count; rc=%i\n", rc);
+ rc = of_property_count_strings(np, "unterminated-string");
+ selftest(rc == -EILSEQ, "unterminated string; rc=%i\n", rc);
+ rc = of_property_count_strings(np, "unterminated-string-list");
+ selftest(rc == -EILSEQ, "unterminated string array; rc=%i\n", rc);
+
+ /* of_property_read_string_index() tests */
+ rc = of_property_read_string_index(np, "string-property", 0, strings);
+ selftest(rc == 0 && !strcmp(strings[0], "foobar"), "of_property_read_string_index() failure; rc=%i\n", rc);
+ strings[0] = NULL;
+ rc = of_property_read_string_index(np, "string-property", 1, strings);
+ selftest(rc == -ENODATA && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc);
+ rc = of_property_read_string_index(np, "phandle-list-names", 0, strings);
+ selftest(rc == 0 && !strcmp(strings[0], "first"), "of_property_read_string_index() failure; rc=%i\n", rc);
+ rc = of_property_read_string_index(np, "phandle-list-names", 1, strings);
+ selftest(rc == 0 && !strcmp(strings[0], "second"), "of_property_read_string_index() failure; rc=%i\n", rc);
+ rc = of_property_read_string_index(np, "phandle-list-names", 2, strings);
+ selftest(rc == 0 && !strcmp(strings[0], "third"), "of_property_read_string_index() failure; rc=%i\n", rc);
+ strings[0] = NULL;
+ rc = of_property_read_string_index(np, "phandle-list-names", 3, strings);
+ selftest(rc == -ENODATA && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc);
+ strings[0] = NULL;
+ rc = of_property_read_string_index(np, "unterminated-string", 0, strings);
+ selftest(rc == -EILSEQ && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc);
+ rc = of_property_read_string_index(np, "unterminated-string-list", 0, strings);
+ selftest(rc == 0 && !strcmp(strings[0], "first"), "of_property_read_string_index() failure; rc=%i\n", rc);
+ strings[0] = NULL;
+ rc = of_property_read_string_index(np, "unterminated-string-list", 2, strings); /* should fail */
+ selftest(rc == -EILSEQ && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc);
+ strings[1] = NULL;
+
+ /* of_property_read_string_array() tests */
+ rc = of_property_read_string_array(np, "string-property", strings, 4);
+ selftest(rc == 1, "Incorrect string count; rc=%i\n", rc);
+ rc = of_property_read_string_array(np, "phandle-list-names", strings, 4);
+ selftest(rc == 3, "Incorrect string count; rc=%i\n", rc);
+ rc = of_property_read_string_array(np, "unterminated-string", strings, 4);
+ selftest(rc == -EILSEQ, "unterminated string; rc=%i\n", rc);
+ /* -- An incorrectly formed string should cause a failure */
+ rc = of_property_read_string_array(np, "unterminated-string-list", strings, 4);
+ selftest(rc == -EILSEQ, "unterminated string array; rc=%i\n", rc);
+ /* -- parsing the correctly formed strings should still work: */
+ strings[2] = NULL;
+ rc = of_property_read_string_array(np, "unterminated-string-list", strings, 2);
+ selftest(rc == 2 && strings[2] == NULL, "of_property_read_string_array() failure; rc=%i\n", rc);
+ strings[1] = NULL;
+ rc = of_property_read_string_array(np, "phandle-list-names", strings, 1);
+ selftest(rc == 1 && strings[1] == NULL, "Overwrote end of string array; rc=%i, str='%s'\n", rc, strings[1]);
}
#define propcmp(p1, p2) (((p1)->length == (p2)->length) && \
return;
}
- while (last_node_index >= 0) {
+ while (last_node_index-- > 0) {
if (nodes[last_node_index]) {
np = of_find_node_by_path(nodes[last_node_index]->full_name);
- if (strcmp(np->full_name, "/aliases") != 0) {
+ if (np == nodes[last_node_index]) {
+ if (of_aliases == np) {
+ of_node_put(of_aliases);
+ of_aliases = NULL;
+ }
detach_node_and_children(np);
} else {
for_each_property_of_node(np, prop) {
}
}
}
- last_node_index--;
}
}
res = selftest_data_add();
if (res)
return res;
+ if (!of_aliases)
+ of_aliases = of_find_node_by_path("/aliases");
np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a");
if (!np) {
of_selftest_find_node_by_name();
of_selftest_dynamic();
of_selftest_parse_phandle_with_args();
- of_selftest_property_match_string();
+ of_selftest_property_string();
of_selftest_property_copy();
of_selftest_changeset();
of_selftest_parse_interrupts();
phandle-list-bad-args = <&provider2 1 0>,
<&provider3 0>;
empty-property;
+ string-property = "foobar";
unterminated-string = [40 41 42 43];
+ unterminated-string-list = "first", "second", [40 41 42 43];
};
};
};
return pcie_caps_reg(dev) & PCI_EXP_FLAGS_VERS;
}
-static inline bool pcie_cap_has_lnkctl(const struct pci_dev *dev)
+bool pcie_cap_has_lnkctl(const struct pci_dev *dev)
{
int type = pci_pcie_type(dev);
struct resource all;
struct resource io;
+ struct resource pio;
struct resource mem;
struct resource prefetch;
struct resource busn;
{
struct tegra_pcie *pcie = sys_to_pcie(sys);
int err;
- phys_addr_t io_start;
err = devm_request_resource(pcie->dev, &pcie->all, &pcie->mem);
if (err < 0)
if (err)
return err;
- io_start = pci_pio_to_address(pcie->io.start);
-
pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset);
pci_add_resource_offset(&sys->resources, &pcie->prefetch,
sys->mem_offset);
pci_add_resource(&sys->resources, &pcie->busn);
- pci_ioremap_io(nr * SZ_64K, io_start);
+ pci_ioremap_io(pcie->pio.start, pcie->io.start);
return 1;
}
static void tegra_pcie_setup_translations(struct tegra_pcie *pcie)
{
u32 fpci_bar, size, axi_address;
- phys_addr_t io_start = pci_pio_to_address(pcie->io.start);
/* Bar 0: type 1 extended configuration space */
fpci_bar = 0xfe100000;
/* Bar 1: downstream IO bar */
fpci_bar = 0xfdfc0000;
size = resource_size(&pcie->io);
- axi_address = io_start;
+ axi_address = pcie->io.start;
afi_writel(pcie, axi_address, AFI_AXI_BAR1_START);
afi_writel(pcie, size >> 12, AFI_AXI_BAR1_SZ);
afi_writel(pcie, fpci_bar, AFI_FPCI_BAR1);
switch (res.flags & IORESOURCE_TYPE_BITS) {
case IORESOURCE_IO:
- memcpy(&pcie->io, &res, sizeof(res));
- pcie->io.name = np->full_name;
+ memcpy(&pcie->pio, &res, sizeof(res));
+ pcie->pio.name = np->full_name;
+
+ /*
+ * The Tegra PCIe host bridge uses this to program the
+ * mapping of the I/O space to the physical address,
+ * so we override the .start and .end fields here that
+ * of_pci_range_to_resource() converted to I/O space.
+ * We also set the IORESOURCE_MEM type to clarify that
+ * the resource is in the physical memory space.
+ */
+ pcie->io.start = range.cpu_addr;
+ pcie->io.end = range.cpu_addr + range.size - 1;
+ pcie->io.flags = IORESOURCE_MEM;
+ pcie->io.name = "I/O";
+
+ memcpy(&res, &pcie->io, sizeof(res));
break;
case IORESOURCE_MEM:
if (ret)
return ret;
- bus = pci_scan_root_bus(&pdev->dev, 0, &xgene_pcie_ops, port, &res);
+ bus = pci_create_root_bus(&pdev->dev, 0,
+ &xgene_pcie_ops, port, &res);
if (!bus)
return -ENOMEM;
+ pci_scan_child_bus(bus);
+ pci_assign_unassigned_bus_resources(bus);
+ pci_bus_add_devices(bus);
+
platform_set_drvdata(pdev, port);
return 0;
}
return entry;
}
+static int msi_verify_entries(struct pci_dev *dev)
+{
+ struct msi_desc *entry;
+
+ list_for_each_entry(entry, &dev->msi_list, list) {
+ if (!dev->no_64bit_msi || !entry->msg.address_hi)
+ continue;
+ dev_err(&dev->dev, "Device has broken 64-bit MSI but arch"
+ " tried to assign one above 4G\n");
+ return -EIO;
+ }
+ return 0;
+}
+
/**
* msi_capability_init - configure device's MSI capability structure
* @dev: pointer to the pci_dev data structure of MSI device function
return ret;
}
+ ret = msi_verify_entries(dev);
+ if (ret) {
+ msi_mask_irq(entry, mask, ~mask);
+ free_msi_irqs(dev);
+ return ret;
+ }
+
ret = populate_msi_sysfs(dev);
if (ret) {
msi_mask_irq(entry, mask, ~mask);
if (ret)
goto out_avail;
+ /* Check if all MSI entries honor device restrictions */
+ ret = msi_verify_entries(dev);
+ if (ret)
+ goto out_free;
+
/*
* Some devices require MSI-X to be enabled before we can touch the
* MSI-X registers. We need to mask all the vectors to prevent
extern const unsigned char pcie_link_speed[];
+bool pcie_cap_has_lnkctl(const struct pci_dev *dev);
+
/* Functions internal to the PCI core code */
int pci_create_sysfs_dev_files(struct pci_dev *pdev);
{
struct pci_dev *dev = child->self;
u16 mem_base_lo, mem_limit_lo;
- unsigned long base, limit;
+ u64 base64, limit64;
+ dma_addr_t base, limit;
struct pci_bus_region region;
struct resource *res;
res = child->resource[2];
pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo);
pci_read_config_word(dev, PCI_PREF_MEMORY_LIMIT, &mem_limit_lo);
- base = ((unsigned long) mem_base_lo & PCI_PREF_RANGE_MASK) << 16;
- limit = ((unsigned long) mem_limit_lo & PCI_PREF_RANGE_MASK) << 16;
+ base64 = (mem_base_lo & PCI_PREF_RANGE_MASK) << 16;
+ limit64 = (mem_limit_lo & PCI_PREF_RANGE_MASK) << 16;
if ((mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) {
u32 mem_base_hi, mem_limit_hi;
* this, just assume they are not being used.
*/
if (mem_base_hi <= mem_limit_hi) {
-#if BITS_PER_LONG == 64
- base |= ((unsigned long) mem_base_hi) << 32;
- limit |= ((unsigned long) mem_limit_hi) << 32;
-#else
- if (mem_base_hi || mem_limit_hi) {
- dev_err(&dev->dev, "can't handle 64-bit address space for bridge\n");
- return;
- }
-#endif
+ base64 |= (u64) mem_base_hi << 32;
+ limit64 |= (u64) mem_limit_hi << 32;
}
}
+
+ base = (dma_addr_t) base64;
+ limit = (dma_addr_t) limit64;
+
+ if (base != base64) {
+ dev_err(&dev->dev, "can't handle bridge window above 4GB (bus address %#010llx)\n",
+ (unsigned long long) base64);
+ return;
+ }
+
if (base <= limit) {
res->flags = (mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) |
IORESOURCE_MEM | IORESOURCE_PREFETCH;
~hpp->pci_exp_devctl_and, hpp->pci_exp_devctl_or);
/* Initialize Link Control Register */
- if (dev->subordinate)
+ if (pcie_cap_has_lnkctl(dev))
pcie_capability_clear_and_set_word(dev, PCI_EXP_LNKCTL,
~hpp->pci_exp_lnkctl_and, hpp->pci_exp_lnkctl_or);
otg->phy = &phy->phy;
platform_set_drvdata(pdev, phy);
+ pm_runtime_enable(phy->dev);
generic_phy = devm_phy_create(phy->dev, NULL, &ops, NULL);
- if (IS_ERR(generic_phy))
+ if (IS_ERR(generic_phy)) {
+ pm_runtime_disable(phy->dev);
return PTR_ERR(generic_phy);
+ }
phy_set_drvdata(generic_phy, phy);
- pm_runtime_enable(phy->dev);
phy_provider = devm_of_phy_provider_register(phy->dev,
of_phy_simple_xlate);
if (IS_ERR(phy_provider)) {
spin_lock_irqsave(&vg->lock, flags);
value = readl(reg);
+ WARN(value & BYT_DIRECT_IRQ_EN,
+ "Bad pad config for io mode, force direct_irq_en bit clearing");
+
/* For level trigges the BYT_TRIG_POS and BYT_TRIG_NEG bits
* are used to indicate high and low level triggering
*/
- value &= ~(BYT_TRIG_POS | BYT_TRIG_NEG | BYT_TRIG_LVL);
+ value &= ~(BYT_DIRECT_IRQ_EN | BYT_TRIG_POS | BYT_TRIG_NEG |
+ BYT_TRIG_LVL);
switch (type) {
case IRQ_TYPE_LEVEL_HIGH:
"Potential Error: Setting GPIO with direct_irq_en to output");
reg_val = readl(reg) | BYT_DIR_MASK;
- reg_val &= ~BYT_OUTPUT_EN;
+ reg_val &= ~(BYT_OUTPUT_EN | BYT_INPUT_EN);
if (value)
writel(reg_val | BYT_LEVEL, reg);
config HP_ACCEL
tristate "HP laptop accelerometer"
depends on INPUT && ACPI
+ depends on SERIO_I8042
select SENSORS_LIS3LV02D
select NEW_LEDS
select LEDS_CLASS
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5741"),
},
},
+ {
+ /*
+ * Note no video_set_backlight_video_vendor, we must use the
+ * acer interface, as there is no native backlight interface.
+ */
+ .ident = "Acer KAV80",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "KAV80"),
+ },
+ },
{}
};
},
.driver_data = &quirk_asus_wapf4,
},
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X550VB",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X550VB"),
+ },
+ .driver_data = &quirk_asus_wapf4,
+ },
{
.callback = dmi_matched,
.ident = "ASUSTeK COMPUTER INC. X55A",
#include <linux/leds.h>
#include <linux/atomic.h>
#include <linux/acpi.h>
+#include <linux/i8042.h>
+#include <linux/serio.h>
#include "../../misc/lis3lv02d/lis3lv02d.h"
#define DRIVER_NAME "hp_accel"
/* HP-specific accelerometer driver ------------------------------------ */
+/* e0 25, e0 26, e0 27, e0 28 are scan codes that the accelerometer with acpi id
+ * HPQ6000 sends through the keyboard bus */
+#define ACCEL_1 0x25
+#define ACCEL_2 0x26
+#define ACCEL_3 0x27
+#define ACCEL_4 0x28
+
/* For automatic insertion of the module */
static const struct acpi_device_id lis3lv02d_device_ids[] = {
{"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */
printk(KERN_DEBUG DRIVER_NAME ": Error getting resources\n");
}
+static bool hp_accel_i8042_filter(unsigned char data, unsigned char str,
+ struct serio *port)
+{
+ static bool extended;
+
+ if (str & I8042_STR_AUXDATA)
+ return false;
+
+ if (data == 0xe0) {
+ extended = true;
+ return true;
+ } else if (unlikely(extended)) {
+ extended = false;
+
+ switch (data) {
+ case ACCEL_1:
+ case ACCEL_2:
+ case ACCEL_3:
+ case ACCEL_4:
+ return true;
+ default:
+ serio_interrupt(port, 0xe0, 0);
+ return false;
+ }
+ }
+
+ return false;
+}
+
static int lis3lv02d_add(struct acpi_device *device)
{
int ret;
if (ret)
return ret;
+ /* filter to remove HPQ6000 accelerometer data
+ * from keyboard bus stream */
+ if (strstr(dev_name(&device->dev), "HPQ6000"))
+ i8042_install_filter(hp_accel_i8042_filter);
+
INIT_WORK(&hpled_led.work, delayed_set_status_worker);
ret = led_classdev_register(NULL, &hpled_led.led_classdev);
if (ret) {
if (!device)
return -EINVAL;
+ i8042_remove_filter(hp_accel_i8042_filter);
lis3lv02d_joystick_disable(&lis3_dev);
lis3lv02d_poweroff(&lis3_dev);
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 2"),
},
},
+ {
+ .ident = "Lenovo Yoga 3 Pro 1370",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 3 Pro-1370"),
+ },
+ },
{}
};
},
.driver_data = &samsung_broken_acpi_video,
},
+ {
+ .callback = samsung_dmi_matched,
+ .ident = "NC210",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "NC210/NC110"),
+ DMI_MATCH(DMI_BOARD_NAME, "NC210/NC110"),
+ },
+ .driver_data = &samsung_broken_acpi_video,
+ },
{
.callback = samsung_dmi_matched,
.ident = "730U3E/740U3E",
DMI_MATCH(DMI_PRODUCT_NAME, "Qosmio X75-A"),
},
},
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TECRA A50-A"),
+ },
+ },
{}
};
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/time.h>
+#include <linux/time64.h>
#include <linux/of.h>
#include <linux/completion.h>
#include <linux/mfd/core.h>
struct ab8500_fg_avg_cap {
int avg;
int samples[NBR_AVG_SAMPLES];
- __kernel_time_t time_stamps[NBR_AVG_SAMPLES];
+ time64_t time_stamps[NBR_AVG_SAMPLES];
int pos;
int nbr_samples;
int sum;
*/
static int ab8500_fg_add_cap_sample(struct ab8500_fg *di, int sample)
{
- struct timespec ts;
+ struct timespec64 ts64;
struct ab8500_fg_avg_cap *avg = &di->avg_cap;
- getnstimeofday(&ts);
+ getnstimeofday64(&ts64);
do {
avg->sum += sample - avg->samples[avg->pos];
avg->samples[avg->pos] = sample;
- avg->time_stamps[avg->pos] = ts.tv_sec;
+ avg->time_stamps[avg->pos] = ts64.tv_sec;
avg->pos++;
if (avg->pos == NBR_AVG_SAMPLES)
* Check the time stamp for each sample. If too old,
* replace with latest sample
*/
- } while (ts.tv_sec - VALID_CAPACITY_SEC > avg->time_stamps[avg->pos]);
+ } while (ts64.tv_sec - VALID_CAPACITY_SEC > avg->time_stamps[avg->pos]);
avg->avg = avg->sum / avg->nbr_samples;
static void ab8500_fg_fill_cap_sample(struct ab8500_fg *di, int sample)
{
int i;
- struct timespec ts;
+ struct timespec64 ts64;
struct ab8500_fg_avg_cap *avg = &di->avg_cap;
- getnstimeofday(&ts);
+ getnstimeofday64(&ts64);
for (i = 0; i < NBR_AVG_SAMPLES; i++) {
avg->samples[i] = sample;
- avg->time_stamps[i] = ts.tv_sec;
+ avg->time_stamps[i] = ts64.tv_sec;
}
avg->pos = 0;
if (np) {
bq->notify_psy = power_supply_get_by_phandle(np, "ti,usb-charger-detection");
- if (!bq->notify_psy)
- return -EPROBE_DEFER;
+ if (IS_ERR(bq->notify_psy)) {
+ dev_info(&client->dev,
+ "no 'ti,usb-charger-detection' property (err=%ld)\n",
+ PTR_ERR(bq->notify_psy));
+ bq->notify_psy = NULL;
+ } else if (!bq->notify_psy) {
+ ret = -EPROBE_DEFER;
+ goto error_2;
+ }
}
else if (pdata->notify_device)
bq->notify_psy = power_supply_get_by_name(pdata->notify_device);
ret = of_property_read_u32(np, "ti,current-limit",
&bq->init_data.current_limit);
if (ret)
- return ret;
+ goto error_2;
ret = of_property_read_u32(np, "ti,weak-battery-voltage",
&bq->init_data.weak_battery_voltage);
if (ret)
- return ret;
+ goto error_2;
ret = of_property_read_u32(np, "ti,battery-regulation-voltage",
&bq->init_data.battery_regulation_voltage);
if (ret)
- return ret;
+ goto error_2;
ret = of_property_read_u32(np, "ti,charge-current",
&bq->init_data.charge_current);
if (ret)
- return ret;
+ goto error_2;
ret = of_property_read_u32(np, "ti,termination-current",
&bq->init_data.termination_current);
if (ret)
- return ret;
+ goto error_2;
ret = of_property_read_u32(np, "ti,resistor-sense",
&bq->init_data.resistor_sense);
if (ret)
- return ret;
+ goto error_2;
} else {
memcpy(&bq->init_data, pdata, sizeof(bq->init_data));
}
static bool is_batt_present(struct charger_manager *cm)
{
union power_supply_propval val;
+ struct power_supply *psy;
bool present = false;
int i, ret;
case CM_NO_BATTERY:
break;
case CM_FUEL_GAUGE:
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+ psy = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
+ if (!psy)
+ break;
+
+ ret = psy->get_property(psy,
POWER_SUPPLY_PROP_PRESENT, &val);
if (ret == 0 && val.intval)
present = true;
break;
case CM_CHARGER_STAT:
- for (i = 0; cm->charger_stat[i]; i++) {
- ret = cm->charger_stat[i]->get_property(
- cm->charger_stat[i],
- POWER_SUPPLY_PROP_PRESENT, &val);
+ for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
+ psy = power_supply_get_by_name(
+ cm->desc->psy_charger_stat[i]);
+ if (!psy) {
+ dev_err(cm->dev, "Cannot find power supply \"%s\"\n",
+ cm->desc->psy_charger_stat[i]);
+ continue;
+ }
+
+ ret = psy->get_property(psy, POWER_SUPPLY_PROP_PRESENT,
+ &val);
if (ret == 0 && val.intval) {
present = true;
break;
static bool is_ext_pwr_online(struct charger_manager *cm)
{
union power_supply_propval val;
+ struct power_supply *psy;
bool online = false;
int i, ret;
/* If at least one of them has one, it's yes. */
- for (i = 0; cm->charger_stat[i]; i++) {
- ret = cm->charger_stat[i]->get_property(
- cm->charger_stat[i],
- POWER_SUPPLY_PROP_ONLINE, &val);
+ for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
+ psy = power_supply_get_by_name(cm->desc->psy_charger_stat[i]);
+ if (!psy) {
+ dev_err(cm->dev, "Cannot find power supply \"%s\"\n",
+ cm->desc->psy_charger_stat[i]);
+ continue;
+ }
+
+ ret = psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &val);
if (ret == 0 && val.intval) {
online = true;
break;
static int get_batt_uV(struct charger_manager *cm, int *uV)
{
union power_supply_propval val;
+ struct power_supply *fuel_gauge;
int ret;
- if (!cm->fuel_gauge)
+ fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
+ if (!fuel_gauge)
return -ENODEV;
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+ ret = fuel_gauge->get_property(fuel_gauge,
POWER_SUPPLY_PROP_VOLTAGE_NOW, &val);
if (ret)
return ret;
{
int i, ret;
bool charging = false;
+ struct power_supply *psy;
union power_supply_propval val;
/* If there is no battery, it cannot be charged */
return false;
/* If at least one of the charger is charging, return yes */
- for (i = 0; cm->charger_stat[i]; i++) {
+ for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
/* 1. The charger sholuld not be DISABLED */
if (cm->emergency_stop)
continue;
if (!cm->charger_enabled)
continue;
+ psy = power_supply_get_by_name(cm->desc->psy_charger_stat[i]);
+ if (!psy) {
+ dev_err(cm->dev, "Cannot find power supply \"%s\"\n",
+ cm->desc->psy_charger_stat[i]);
+ continue;
+ }
+
/* 2. The charger should be online (ext-power) */
- ret = cm->charger_stat[i]->get_property(
- cm->charger_stat[i],
- POWER_SUPPLY_PROP_ONLINE, &val);
+ ret = psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &val);
if (ret) {
dev_warn(cm->dev, "Cannot read ONLINE value from %s\n",
cm->desc->psy_charger_stat[i]);
* 3. The charger should not be FULL, DISCHARGING,
* or NOT_CHARGING.
*/
- ret = cm->charger_stat[i]->get_property(
- cm->charger_stat[i],
- POWER_SUPPLY_PROP_STATUS, &val);
+ ret = psy->get_property(psy, POWER_SUPPLY_PROP_STATUS, &val);
if (ret) {
dev_warn(cm->dev, "Cannot read STATUS value from %s\n",
cm->desc->psy_charger_stat[i]);
{
struct charger_desc *desc = cm->desc;
union power_supply_propval val;
+ struct power_supply *fuel_gauge;
int ret = 0;
int uV;
if (!is_batt_present(cm))
return false;
- if (cm->fuel_gauge && desc->fullbatt_full_capacity > 0) {
+ fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
+ if (!fuel_gauge)
+ return false;
+
+ if (desc->fullbatt_full_capacity > 0) {
val.intval = 0;
/* Not full if capacity of fuel gauge isn't full */
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+ ret = fuel_gauge->get_property(fuel_gauge,
POWER_SUPPLY_PROP_CHARGE_FULL, &val);
if (!ret && val.intval > desc->fullbatt_full_capacity)
return true;
}
/* Full, if the capacity is more than fullbatt_soc */
- if (cm->fuel_gauge && desc->fullbatt_soc > 0) {
+ if (desc->fullbatt_soc > 0) {
val.intval = 0;
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+ ret = fuel_gauge->get_property(fuel_gauge,
POWER_SUPPLY_PROP_CAPACITY, &val);
if (!ret && val.intval >= desc->fullbatt_soc)
return true;
return ret;
}
+static int cm_get_battery_temperature_by_psy(struct charger_manager *cm,
+ int *temp)
+{
+ struct power_supply *fuel_gauge;
+
+ fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
+ if (!fuel_gauge)
+ return -ENODEV;
+
+ return fuel_gauge->get_property(fuel_gauge,
+ POWER_SUPPLY_PROP_TEMP,
+ (union power_supply_propval *)temp);
+}
+
static int cm_get_battery_temperature(struct charger_manager *cm,
int *temp)
{
return -ENODEV;
#ifdef CONFIG_THERMAL
- ret = thermal_zone_get_temp(cm->tzd_batt, (unsigned long *)temp);
- if (!ret)
- /* Calibrate temperature unit */
- *temp /= 100;
-#else
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
- POWER_SUPPLY_PROP_TEMP,
- (union power_supply_propval *)temp);
+ if (cm->tzd_batt) {
+ ret = thermal_zone_get_temp(cm->tzd_batt, (unsigned long *)temp);
+ if (!ret)
+ /* Calibrate temperature unit */
+ *temp /= 100;
+ } else
#endif
+ {
+ /* if-else continued from CONFIG_THERMAL */
+ ret = cm_get_battery_temperature_by_psy(cm, temp);
+ }
+
return ret;
}
struct charger_manager *cm = container_of(psy,
struct charger_manager, charger_psy);
struct charger_desc *desc = cm->desc;
+ struct power_supply *fuel_gauge;
int ret = 0;
int uV;
ret = get_batt_uV(cm, &val->intval);
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+ fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
+ if (!fuel_gauge) {
+ ret = -ENODEV;
+ break;
+ }
+ ret = fuel_gauge->get_property(fuel_gauge,
POWER_SUPPLY_PROP_CURRENT_NOW, val);
break;
case POWER_SUPPLY_PROP_TEMP:
case POWER_SUPPLY_PROP_TEMP_AMBIENT:
return cm_get_battery_temperature(cm, &val->intval);
case POWER_SUPPLY_PROP_CAPACITY:
- if (!cm->fuel_gauge) {
+ fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge);
+ if (!fuel_gauge) {
ret = -ENODEV;
break;
}
break;
}
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+ ret = fuel_gauge->get_property(fuel_gauge,
POWER_SUPPLY_PROP_CAPACITY, val);
if (ret)
break;
break;
case POWER_SUPPLY_PROP_CHARGE_NOW:
if (is_charging(cm)) {
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+ fuel_gauge = power_supply_get_by_name(
+ cm->desc->psy_fuel_gauge);
+ if (!fuel_gauge) {
+ ret = -ENODEV;
+ break;
+ }
+
+ ret = fuel_gauge->get_property(fuel_gauge,
POWER_SUPPLY_PROP_CHARGE_NOW,
val);
if (ret) {
.properties = default_charger_props,
.num_properties = ARRAY_SIZE(default_charger_props),
.get_property = charger_get_property,
+ .no_thermal = true,
};
/**
return ret;
}
-static int cm_init_thermal_data(struct charger_manager *cm)
+static int cm_init_thermal_data(struct charger_manager *cm,
+ struct power_supply *fuel_gauge)
{
struct charger_desc *desc = cm->desc;
union power_supply_propval val;
int ret;
/* Verify whether fuel gauge provides battery temperature */
- ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+ ret = fuel_gauge->get_property(fuel_gauge,
POWER_SUPPLY_PROP_TEMP, &val);
if (!ret) {
cm->desc->measure_battery_temp = true;
}
#ifdef CONFIG_THERMAL
- cm->tzd_batt = cm->fuel_gauge->tzd;
-
if (ret && desc->thermal_zone) {
cm->tzd_batt =
thermal_zone_get_zone_by_name(desc->thermal_zone);
int ret = 0, i = 0;
int j = 0;
union power_supply_propval val;
+ struct power_supply *fuel_gauge;
if (g_desc && !rtc_dev && g_desc->rtc_name) {
rtc_dev = rtc_class_open(g_desc->rtc_name);
while (desc->psy_charger_stat[i])
i++;
- cm->charger_stat = devm_kzalloc(&pdev->dev,
- sizeof(struct power_supply *) * i, GFP_KERNEL);
- if (!cm->charger_stat)
- return -ENOMEM;
-
+ /* Check if charger's supplies are present at probe */
for (i = 0; desc->psy_charger_stat[i]; i++) {
- cm->charger_stat[i] = power_supply_get_by_name(
- desc->psy_charger_stat[i]);
- if (!cm->charger_stat[i]) {
+ struct power_supply *psy;
+
+ psy = power_supply_get_by_name(desc->psy_charger_stat[i]);
+ if (!psy) {
dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n",
desc->psy_charger_stat[i]);
return -ENODEV;
}
}
- cm->fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge);
- if (!cm->fuel_gauge) {
+ fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge);
+ if (!fuel_gauge) {
dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n",
desc->psy_fuel_gauge);
return -ENODEV;
cm->charger_psy.num_properties = psy_default.num_properties;
/* Find which optional psy-properties are available */
- if (!cm->fuel_gauge->get_property(cm->fuel_gauge,
+ if (!fuel_gauge->get_property(fuel_gauge,
POWER_SUPPLY_PROP_CHARGE_NOW, &val)) {
cm->charger_psy.properties[cm->charger_psy.num_properties] =
POWER_SUPPLY_PROP_CHARGE_NOW;
cm->charger_psy.num_properties++;
}
- if (!cm->fuel_gauge->get_property(cm->fuel_gauge,
+ if (!fuel_gauge->get_property(fuel_gauge,
POWER_SUPPLY_PROP_CURRENT_NOW,
&val)) {
cm->charger_psy.properties[cm->charger_psy.num_properties] =
cm->charger_psy.num_properties++;
}
- ret = cm_init_thermal_data(cm);
+ ret = cm_init_thermal_data(cm, fuel_gauge);
if (ret) {
dev_err(&pdev->dev, "Failed to initialize thermal data\n");
cm->desc->measure_battery_temp = false;
int i;
bool found = false;
- for (i = 0; cm->charger_stat[i]; i++) {
- if (psy == cm->charger_stat[i]) {
+ for (i = 0; cm->desc->psy_charger_stat[i]; i++) {
+ if (!strcmp(psy->name, cm->desc->psy_charger_stat[i])) {
found = true;
break;
}
{
int i;
+ if (psy->no_thermal)
+ return 0;
+
/* Register battery zone device psy reports temperature */
for (i = 0; i < psy->num_properties; i++) {
if (psy->properties[i] == POWER_SUPPLY_PROP_TEMP) {
for_each_child_of_node(nproot, np) {
if (!of_node_cmp(np->name, info->desc.name)) {
config->init_data =
- of_get_regulator_init_data(&pdev->dev, np);
+ of_get_regulator_init_data(&pdev->dev, np,
+ &info->desc);
config->of_node = np;
break;
}
help
Say y here to support the regulators found on Ricoh RN5T618 PMIC.
+config REGULATOR_RT5033
+ tristate "Richtek RT5033 Regulators"
+ depends on MFD_RT5033
+ help
+ This adds support for voltage and current regulators in Richtek
+ RT5033 PMIC. The device supports multiple regulators like
+ current source, LDO and Buck.
+
config REGULATOR_S2MPA01
tristate "Samsung S2MPA01 voltage regulator"
depends on MFD_SEC_CORE
via I2C bus. S2MPA01 has 10 Bucks and 26 LDO outputs.
config REGULATOR_S2MPS11
- tristate "Samsung S2MPS11/S2MPS14/S2MPU02 voltage regulator"
+ tristate "Samsung S2MPS11/S2MPS13/S2MPS14/S2MPU02 voltage regulator"
depends on MFD_SEC_CORE
help
- This driver supports a Samsung S2MPS11/S2MPS14/S2MPU02 voltage output
- regulator via I2C bus. The chip is comprised of high efficient Buck
- converters including Dual-Phase Buck converter, Buck-Boost converter,
- various LDOs.
+ This driver supports a Samsung S2MPS11/S2MPS13/S2MPS14/S2MPU02 voltage
+ output regulator via I2C bus. The chip is comprised of high efficient
+ Buck converters including Dual-Phase Buck converter, Buck-Boost
+ converter, various LDOs.
config REGULATOR_S5M8767
tristate "Samsung S5M8767A voltage regulator"
config REGULATOR_SKY81452
tristate "Skyworks Solutions SKY81452 voltage regulator"
- depends on SKY81452
+ depends on MFD_SKY81452
help
This driver supports Skyworks SKY81452 voltage output regulator
via I2C bus. SKY81452 has one voltage linear regulator can be
obj-$(CONFIG_REGULATOR_RC5T583) += rc5t583-regulator.o
obj-$(CONFIG_REGULATOR_RK808) += rk808-regulator.o
obj-$(CONFIG_REGULATOR_RN5T618) += rn5t618-regulator.o
+obj-$(CONFIG_REGULATOR_RT5033) += rt5033-regulator.o
obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o
obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o
obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o
#define ACT8846_REG12_VSET 0xa0
#define ACT8846_REG12_CTRL 0xa1
#define ACT8846_REG13_CTRL 0xb1
+#define ACT8846_GLB_OFF_CTRL 0xc3
+#define ACT8846_OFF_SYSMASK 0x18
/*
* ACT8865 Global Register Map.
#define ACT8865_LDO3_CTRL 0x61
#define ACT8865_LDO4_VSET 0x64
#define ACT8865_LDO4_CTRL 0x65
+#define ACT8865_MSTROFF 0x20
/*
* Field Definitions.
struct act8865 {
struct regmap *regmap;
+ int off_reg;
+ int off_mask;
};
static const struct regmap_config act8865_regmap_config = {
return NULL;
}
+static struct i2c_client *act8865_i2c_client;
+static void act8865_power_off(void)
+{
+ struct act8865 *act8865;
+
+ act8865 = i2c_get_clientdata(act8865_i2c_client);
+ regmap_write(act8865->regmap, act8865->off_reg, act8865->off_mask);
+ while (1);
+}
+
static int act8865_pmic_probe(struct i2c_client *client,
const struct i2c_device_id *i2c_id)
{
int i, ret, num_regulators;
struct act8865 *act8865;
unsigned long type;
+ int off_reg, off_mask;
pdata = dev_get_platdata(dev);
case ACT8846:
regulators = act8846_regulators;
num_regulators = ARRAY_SIZE(act8846_regulators);
+ off_reg = ACT8846_GLB_OFF_CTRL;
+ off_mask = ACT8846_OFF_SYSMASK;
break;
case ACT8865:
regulators = act8865_regulators;
num_regulators = ARRAY_SIZE(act8865_regulators);
+ off_reg = ACT8865_SYS_CTRL;
+ off_mask = ACT8865_MSTROFF;
break;
default:
dev_err(dev, "invalid device id %lu\n", type);
return ret;
}
+ if (of_device_is_system_power_controller(dev->of_node)) {
+ if (!pm_power_off) {
+ act8865_i2c_client = client;
+ act8865->off_reg = off_reg;
+ act8865->off_mask = off_mask;
+ pm_power_off = act8865_power_off;
+ } else {
+ dev_err(dev, "Failed to set poweroff capability, already defined\n");
+ }
+ }
+
/* Finally register devices */
for (i = 0; i < num_regulators; i++) {
const struct regulator_desc *desc = ®ulators[i];
int ret = 0;
u32 val;
- initdata = of_get_regulator_init_data(dev, np);
sreg = devm_kzalloc(dev, sizeof(*sreg), GFP_KERNEL);
if (!sreg)
return -ENOMEM;
- sreg->initdata = initdata;
sreg->name = of_get_property(np, "regulator-name", NULL);
rdesc = &sreg->rdesc;
rdesc->name = sreg->name;
rdesc->type = REGULATOR_VOLTAGE;
rdesc->owner = THIS_MODULE;
+ initdata = of_get_regulator_init_data(dev, np, rdesc);
+ sreg->initdata = initdata;
+
anatop_np = of_get_parent(np);
if (!anatop_np)
return -ENODEV;
sreg->sel = 0;
sreg->bypass = true;
}
+
+ /*
+ * In case vddpu was disabled by the bootloader, we need to set
+ * a sane default until imx6-cpufreq was probed and changes the
+ * voltage to the correct value. In this case we set 1.25V.
+ */
+ if (!sreg->sel && !strcmp(sreg->name, "vddpu"))
+ sreg->sel = 22;
+
+ if (!sreg->sel) {
+ dev_err(&pdev->dev, "Failed to read a valid default voltage selector.\n");
+ return -EINVAL;
+ }
} else {
rdesc->ops = &anatop_rops;
}
};
static int arizona_ldo1_of_get_pdata(struct arizona *arizona,
- struct regulator_config *config)
+ struct regulator_config *config,
+ const struct regulator_desc *desc)
{
struct arizona_pdata *pdata = &arizona->pdata;
struct arizona_ldo1 *ldo1 = config->driver_data;
if (init_node) {
config->of_node = init_node;
- init_data = of_get_regulator_init_data(arizona->dev, init_node);
+ init_data = of_get_regulator_init_data(arizona->dev, init_node,
+ desc);
if (init_data) {
init_data->consumer_supplies = &ldo1->supply;
if (IS_ENABLED(CONFIG_OF)) {
if (!dev_get_platdata(arizona->dev)) {
- ret = arizona_ldo1_of_get_pdata(arizona, &config);
+ ret = arizona_ldo1_of_get_pdata(arizona, &config, desc);
if (ret < 0)
return ret;
+
+ config.ena_gpio_initialized = true;
}
}
};
static int arizona_micsupp_of_get_pdata(struct arizona *arizona,
- struct regulator_config *config)
+ struct regulator_config *config,
+ const struct regulator_desc *desc)
{
struct arizona_pdata *pdata = &arizona->pdata;
struct arizona_micsupp *micsupp = config->driver_data;
if (np) {
config->of_node = np;
- init_data = of_get_regulator_init_data(arizona->dev, np);
+ init_data = of_get_regulator_init_data(arizona->dev, np, desc);
if (init_data) {
init_data->consumer_supplies = &micsupp->supply;
if (IS_ENABLED(CONFIG_OF)) {
if (!dev_get_platdata(arizona->dev)) {
- ret = arizona_micsupp_of_get_pdata(arizona, &config);
+ ret = arizona_micsupp_of_get_pdata(arizona, &config,
+ desc);
if (ret < 0)
return ret;
}
if (!count)
sprintf(buf, "no parameters");
- rdev_info(rdev, "%s\n", buf);
+ rdev_dbg(rdev, "%s\n", buf);
if ((constraints->min_uV != constraints->max_uV) &&
!(constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE))
gpiod_put(pin->gpiod);
list_del(&pin->list);
kfree(pin);
+ rdev->ena_pin = NULL;
+ return;
} else {
pin->request_count--;
}
/* we are last user */
if (_regulator_can_change_status(rdev)) {
+ ret = _notifier_call_chain(rdev,
+ REGULATOR_EVENT_PRE_DISABLE,
+ NULL);
+ if (ret & NOTIFY_STOP_MASK)
+ return -EINVAL;
+
ret = _regulator_do_disable(rdev);
if (ret < 0) {
rdev_err(rdev, "failed to disable\n");
+ _notifier_call_chain(rdev,
+ REGULATOR_EVENT_ABORT_DISABLE,
+ NULL);
return ret;
}
_notifier_call_chain(rdev, REGULATOR_EVENT_DISABLE,
{
int ret = 0;
+ ret = _notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE |
+ REGULATOR_EVENT_PRE_DISABLE, NULL);
+ if (ret & NOTIFY_STOP_MASK)
+ return -EINVAL;
+
ret = _regulator_do_disable(rdev);
if (ret < 0) {
rdev_err(rdev, "failed to force disable\n");
+ _notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE |
+ REGULATOR_EVENT_ABORT_DISABLE, NULL);
return ret;
}
dev_set_drvdata(&rdev->dev, rdev);
- if (config->ena_gpio && gpio_is_valid(config->ena_gpio)) {
+ if ((config->ena_gpio || config->ena_gpio_initialized) &&
+ gpio_is_valid(config->ena_gpio)) {
ret = regulator_ena_gpio_request(rdev, config);
if (ret != 0) {
rdev_err(rdev, "Failed to request enable GPIO%d: %d\n",
if (!of_node_cmp(np->name,
regulator->info->reg_desc.name)) {
config.init_data = of_get_regulator_init_data(
- &pdev->dev, np);
+ &pdev->dev, np,
+ ®ulator->info->reg_desc);
config.of_node = np;
break;
}
return irq;
}
- regulators->irq_ldo_lim = regmap_irq_get_virq(da9063->regmap_irq, irq);
- if (regulators->irq_ldo_lim >= 0) {
- ret = request_threaded_irq(regulators->irq_ldo_lim,
- NULL, da9063_ldo_lim_event,
- IRQF_TRIGGER_LOW | IRQF_ONESHOT,
- "LDO_LIM", regulators);
- if (ret) {
- dev_err(&pdev->dev,
- "Failed to request LDO_LIM IRQ.\n");
- regulators->irq_ldo_lim = -ENXIO;
- }
+ ret = request_threaded_irq(irq,
+ NULL, da9063_ldo_lim_event,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "LDO_LIM", regulators);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to request LDO_LIM IRQ.\n");
+ regulators->irq_ldo_lim = -ENXIO;
}
return 0;
config.dev = &i2c->dev;
config.init_data = pdata ? &pdata->da9210_constraints :
- of_get_regulator_init_data(dev, dev->of_node);
+ of_get_regulator_init_data(dev, dev->of_node, &da9210_reg);
config.driver_data = chip;
config.regmap = chip->regmap;
config.of_node = dev->of_node;
static struct regulator_ops dummy_ops;
-static struct regulator_desc dummy_desc = {
+static const struct regulator_desc dummy_desc = {
.name = "regulator-dummy",
.id = -1,
.type = REGULATOR_VOLTAGE,
};
static struct fan53555_platform_data *fan53555_parse_dt(struct device *dev,
- struct device_node *np)
+ struct device_node *np,
+ const struct regulator_desc *desc)
{
struct fan53555_platform_data *pdata;
int ret;
if (!pdata)
return NULL;
- pdata->regulator = of_get_regulator_init_data(dev, np);
+ pdata->regulator = of_get_regulator_init_data(dev, np, desc);
ret = of_property_read_u32(np, "fcs,suspend-voltage-selector",
&tmp);
unsigned int val;
int ret;
+ di = devm_kzalloc(&client->dev, sizeof(struct fan53555_device_info),
+ GFP_KERNEL);
+ if (!di)
+ return -ENOMEM;
+
pdata = dev_get_platdata(&client->dev);
if (!pdata)
- pdata = fan53555_parse_dt(&client->dev, np);
+ pdata = fan53555_parse_dt(&client->dev, np, &di->desc);
if (!pdata || !pdata->regulator) {
dev_err(&client->dev, "Platform data not found!\n");
return -ENODEV;
}
- di = devm_kzalloc(&client->dev, sizeof(struct fan53555_device_info),
- GFP_KERNEL);
- if (!di)
- return -ENOMEM;
-
di->regulator = pdata->regulator;
if (client->dev.of_node) {
const struct of_device_id *match;
/**
* of_get_fixed_voltage_config - extract fixed_voltage_config structure info
* @dev: device requesting for fixed_voltage_config
+ * @desc: regulator description
*
* Populates fixed_voltage_config structure by extracting data from device
* tree node, returns a pointer to the populated structure of NULL if memory
* alloc fails.
*/
static struct fixed_voltage_config *
-of_get_fixed_voltage_config(struct device *dev)
+of_get_fixed_voltage_config(struct device *dev,
+ const struct regulator_desc *desc)
{
struct fixed_voltage_config *config;
struct device_node *np = dev->of_node;
if (!config)
return ERR_PTR(-ENOMEM);
- config->init_data = of_get_regulator_init_data(dev, dev->of_node);
+ config->init_data = of_get_regulator_init_data(dev, dev->of_node, desc);
if (!config->init_data)
return ERR_PTR(-EINVAL);
struct regulator_config cfg = { };
int ret;
+ drvdata = devm_kzalloc(&pdev->dev, sizeof(struct fixed_voltage_data),
+ GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+
if (pdev->dev.of_node) {
- config = of_get_fixed_voltage_config(&pdev->dev);
+ config = of_get_fixed_voltage_config(&pdev->dev,
+ &drvdata->desc);
if (IS_ERR(config))
return PTR_ERR(config);
} else {
if (!config)
return -ENOMEM;
- drvdata = devm_kzalloc(&pdev->dev, sizeof(struct fixed_voltage_data),
- GFP_KERNEL);
- if (!drvdata)
- return -ENOMEM;
-
drvdata->desc.name = devm_kstrdup(&pdev->dev,
config->supply_name,
GFP_KERNEL);
drvdata->desc.fixed_uV = config->microvolts;
- if (config->gpio >= 0)
+ if (gpio_is_valid(config->gpio)) {
cfg.ena_gpio = config->gpio;
+ if (pdev->dev.of_node)
+ cfg.ena_gpio_initialized = true;
+ }
cfg.ena_gpio_invert = !config->enable_high;
if (config->enabled_at_boot) {
if (config->enable_high)
};
static struct gpio_regulator_config *
-of_get_gpio_regulator_config(struct device *dev, struct device_node *np)
+of_get_gpio_regulator_config(struct device *dev, struct device_node *np,
+ const struct regulator_desc *desc)
{
struct gpio_regulator_config *config;
const char *regtype;
if (!config)
return ERR_PTR(-ENOMEM);
- config->init_data = of_get_regulator_init_data(dev, np);
+ config->init_data = of_get_regulator_init_data(dev, np, desc);
if (!config->init_data)
return ERR_PTR(-EINVAL);
config->enable_gpio = of_get_named_gpio(np, "enable-gpio", 0);
- /* Fetch GPIOs. */
- config->nr_gpios = of_gpio_count(np);
-
- config->gpios = devm_kzalloc(dev,
- sizeof(struct gpio) * config->nr_gpios,
- GFP_KERNEL);
- if (!config->gpios)
- return ERR_PTR(-ENOMEM);
-
- proplen = of_property_count_u32_elems(np, "gpios-states");
- /* optional property */
- if (proplen < 0)
- proplen = 0;
-
- if (proplen > 0 && proplen != config->nr_gpios) {
- dev_warn(dev, "gpios <-> gpios-states mismatch\n");
- proplen = 0;
- }
+ /* Fetch GPIOs. - optional property*/
+ ret = of_gpio_count(np);
+ if ((ret < 0) && (ret != -ENOENT))
+ return ERR_PTR(ret);
+
+ if (ret > 0) {
+ config->nr_gpios = ret;
+ config->gpios = devm_kzalloc(dev,
+ sizeof(struct gpio) * config->nr_gpios,
+ GFP_KERNEL);
+ if (!config->gpios)
+ return ERR_PTR(-ENOMEM);
+
+ proplen = of_property_count_u32_elems(np, "gpios-states");
+ /* optional property */
+ if (proplen < 0)
+ proplen = 0;
+
+ if (proplen > 0 && proplen != config->nr_gpios) {
+ dev_warn(dev, "gpios <-> gpios-states mismatch\n");
+ proplen = 0;
+ }
- for (i = 0; i < config->nr_gpios; i++) {
- gpio = of_get_named_gpio(np, "gpios", i);
- if (gpio < 0)
- break;
- config->gpios[i].gpio = gpio;
- if (proplen > 0) {
- of_property_read_u32_index(np, "gpios-states", i, &ret);
- if (ret)
- config->gpios[i].flags = GPIOF_OUT_INIT_HIGH;
+ for (i = 0; i < config->nr_gpios; i++) {
+ gpio = of_get_named_gpio(np, "gpios", i);
+ if (gpio < 0)
+ break;
+ config->gpios[i].gpio = gpio;
+ if (proplen > 0) {
+ of_property_read_u32_index(np, "gpios-states",
+ i, &ret);
+ if (ret)
+ config->gpios[i].flags =
+ GPIOF_OUT_INIT_HIGH;
+ }
}
}
struct regulator_config cfg = { };
int ptr, ret, state;
- if (np) {
- config = of_get_gpio_regulator_config(&pdev->dev, np);
- if (IS_ERR(config))
- return PTR_ERR(config);
- }
-
drvdata = devm_kzalloc(&pdev->dev, sizeof(struct gpio_regulator_data),
GFP_KERNEL);
if (drvdata == NULL)
return -ENOMEM;
+ if (np) {
+ config = of_get_gpio_regulator_config(&pdev->dev, np,
+ &drvdata->desc);
+ if (IS_ERR(config))
+ return PTR_ERR(config);
+ }
+
drvdata->desc.name = kstrdup(config->supply_name, GFP_KERNEL);
if (drvdata->desc.name == NULL) {
dev_err(&pdev->dev, "Failed to allocate supply name\n");
goto err;
}
- drvdata->gpios = kmemdup(config->gpios,
- config->nr_gpios * sizeof(struct gpio),
- GFP_KERNEL);
- if (drvdata->gpios == NULL) {
- dev_err(&pdev->dev, "Failed to allocate gpio data\n");
- ret = -ENOMEM;
- goto err_name;
+ if (config->nr_gpios != 0) {
+ drvdata->gpios = kmemdup(config->gpios,
+ config->nr_gpios * sizeof(struct gpio),
+ GFP_KERNEL);
+ if (drvdata->gpios == NULL) {
+ dev_err(&pdev->dev, "Failed to allocate gpio data\n");
+ ret = -ENOMEM;
+ goto err_name;
+ }
+
+ drvdata->nr_gpios = config->nr_gpios;
+ ret = gpio_request_array(drvdata->gpios, drvdata->nr_gpios);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Could not obtain regulator setting GPIOs: %d\n", ret);
+ goto err_memstate;
+ }
}
drvdata->states = kmemdup(config->states,
goto err_memgpio;
}
- drvdata->nr_gpios = config->nr_gpios;
- ret = gpio_request_array(drvdata->gpios, drvdata->nr_gpios);
- if (ret) {
- dev_err(&pdev->dev,
- "Could not obtain regulator setting GPIOs: %d\n", ret);
- goto err_memstate;
- }
-
/* build initial state from gpio init data. */
state = 0;
for (ptr = 0; ptr < drvdata->nr_gpios; ptr++) {
cfg.driver_data = drvdata;
cfg.of_node = np;
- if (config->enable_gpio >= 0)
+ if (gpio_is_valid(config->enable_gpio)) {
cfg.ena_gpio = config->enable_gpio;
+ cfg.ena_gpio_initialized = true;
+ }
cfg.ena_gpio_invert = !config->enable_high;
if (config->enabled_at_boot) {
if (config->enable_high)
struct max1586_platform_data *pdata)
{
struct max1586_subdev_data *sub;
- struct of_regulator_match rmatch[ARRAY_SIZE(max1586_reg)];
+ struct of_regulator_match rmatch[ARRAY_SIZE(max1586_reg)] = { };
struct device_node *np = dev->of_node;
int i, matched;
#define MAX77686_DVS_MINUV 600000
#define MAX77686_DVS_UVSTEP 12500
+/*
+ * Values used for configuring LDOs and bucks.
+ * Forcing low power mode: LDO1, 3-5, 9, 13, 17-26
+ */
+#define MAX77686_LDO_LOWPOWER 0x1
+/*
+ * On/off controlled by PWRREQ:
+ * - LDO2, 6-8, 10-12, 14-16
+ * - buck[1234]
+ */
+#define MAX77686_OFF_PWRREQ 0x1
+/* Low power mode controlled by PWRREQ: All LDOs */
+#define MAX77686_LDO_LOWPOWER_PWRREQ 0x2
+/* Forcing low power mode: buck[234] */
+#define MAX77686_BUCK_LOWPOWER 0x2
+#define MAX77686_NORMAL 0x3
+
#define MAX77686_OPMODE_SHIFT 6
#define MAX77686_OPMODE_BUCK234_SHIFT 4
#define MAX77686_OPMODE_MASK 0x3
};
struct max77686_data {
+ /* Array indexed by regulator id */
unsigned int opmode[MAX77686_REGULATORS];
};
-/* Some BUCKS supports Normal[ON/OFF] mode during suspend */
-static int max77686_buck_set_suspend_disable(struct regulator_dev *rdev)
+static unsigned int max77686_get_opmode_shift(int id)
{
- unsigned int val;
+ switch (id) {
+ case MAX77686_BUCK1:
+ case MAX77686_BUCK5 ... MAX77686_BUCK9:
+ return 0;
+ case MAX77686_BUCK2 ... MAX77686_BUCK4:
+ return MAX77686_OPMODE_BUCK234_SHIFT;
+ default:
+ /* all LDOs */
+ return MAX77686_OPMODE_SHIFT;
+ }
+}
+
+/* Some BUCKs and LDOs supports Normal[ON/OFF] mode during suspend */
+static int max77686_set_suspend_disable(struct regulator_dev *rdev)
+{
+ unsigned int val, shift;
struct max77686_data *max77686 = rdev_get_drvdata(rdev);
int ret, id = rdev_get_id(rdev);
- if (id == MAX77686_BUCK1)
- val = 0x1;
- else
- val = 0x1 << MAX77686_OPMODE_BUCK234_SHIFT;
+ shift = max77686_get_opmode_shift(id);
+ val = MAX77686_OFF_PWRREQ;
ret = regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
- rdev->desc->enable_mask, val);
+ rdev->desc->enable_mask, val << shift);
if (ret)
return ret;
switch (mode) {
case REGULATOR_MODE_IDLE: /* ON in LP Mode */
- val = 0x2 << MAX77686_OPMODE_SHIFT;
+ val = MAX77686_LDO_LOWPOWER_PWRREQ;
break;
case REGULATOR_MODE_NORMAL: /* ON in Normal Mode */
- val = 0x3 << MAX77686_OPMODE_SHIFT;
+ val = MAX77686_NORMAL;
break;
default:
pr_warn("%s: regulator_suspend_mode : 0x%x not supported\n",
}
ret = regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
- rdev->desc->enable_mask, val);
+ rdev->desc->enable_mask,
+ val << MAX77686_OPMODE_SHIFT);
if (ret)
return ret;
switch (mode) {
case REGULATOR_MODE_STANDBY: /* switch off */
- val = 0x1 << MAX77686_OPMODE_SHIFT;
+ val = MAX77686_OFF_PWRREQ;
break;
case REGULATOR_MODE_IDLE: /* ON in LP Mode */
- val = 0x2 << MAX77686_OPMODE_SHIFT;
+ val = MAX77686_LDO_LOWPOWER_PWRREQ;
break;
case REGULATOR_MODE_NORMAL: /* ON in Normal Mode */
- val = 0x3 << MAX77686_OPMODE_SHIFT;
+ val = MAX77686_NORMAL;
break;
default:
pr_warn("%s: regulator_suspend_mode : 0x%x not supported\n",
}
ret = regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
- rdev->desc->enable_mask, val);
+ rdev->desc->enable_mask,
+ val << MAX77686_OPMODE_SHIFT);
if (ret)
return ret;
static int max77686_enable(struct regulator_dev *rdev)
{
struct max77686_data *max77686 = rdev_get_drvdata(rdev);
+ unsigned int shift;
+ int id = rdev_get_id(rdev);
+
+ shift = max77686_get_opmode_shift(id);
+
+ if (max77686->opmode[id] == MAX77686_OFF_PWRREQ)
+ max77686->opmode[id] = MAX77686_NORMAL;
return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
rdev->desc->enable_mask,
- max77686->opmode[rdev_get_id(rdev)]);
+ max77686->opmode[id] << shift);
}
static int max77686_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.set_voltage_time_sel = regulator_set_voltage_time_sel,
.set_suspend_mode = max77686_ldo_set_suspend_mode,
+ .set_suspend_disable = max77686_set_suspend_disable,
};
static struct regulator_ops max77686_buck1_ops = {
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.set_voltage_time_sel = regulator_set_voltage_time_sel,
- .set_suspend_disable = max77686_buck_set_suspend_disable,
+ .set_suspend_disable = max77686_set_suspend_disable,
};
static struct regulator_ops max77686_buck_dvs_ops = {
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.set_voltage_time_sel = regulator_set_voltage_time_sel,
.set_ramp_delay = max77686_set_ramp_delay,
- .set_suspend_disable = max77686_buck_set_suspend_disable,
+ .set_suspend_disable = max77686_set_suspend_disable,
};
#define regulator_desc_ldo(num) { \
.name = "LDO"#num, \
+ .of_match = of_match_ptr("LDO"#num), \
+ .regulators_node = of_match_ptr("voltage-regulators"), \
.id = MAX77686_LDO##num, \
.ops = &max77686_ops, \
.type = REGULATOR_VOLTAGE, \
}
#define regulator_desc_lpm_ldo(num) { \
.name = "LDO"#num, \
+ .of_match = of_match_ptr("LDO"#num), \
+ .regulators_node = of_match_ptr("voltage-regulators"), \
.id = MAX77686_LDO##num, \
.ops = &max77686_ldo_ops, \
.type = REGULATOR_VOLTAGE, \
}
#define regulator_desc_ldo_low(num) { \
.name = "LDO"#num, \
+ .of_match = of_match_ptr("LDO"#num), \
+ .regulators_node = of_match_ptr("voltage-regulators"), \
.id = MAX77686_LDO##num, \
.ops = &max77686_ldo_ops, \
.type = REGULATOR_VOLTAGE, \
}
#define regulator_desc_ldo1_low(num) { \
.name = "LDO"#num, \
+ .of_match = of_match_ptr("LDO"#num), \
+ .regulators_node = of_match_ptr("voltage-regulators"), \
.id = MAX77686_LDO##num, \
.ops = &max77686_ops, \
.type = REGULATOR_VOLTAGE, \
}
#define regulator_desc_buck(num) { \
.name = "BUCK"#num, \
+ .of_match = of_match_ptr("BUCK"#num), \
+ .regulators_node = of_match_ptr("voltage-regulators"), \
.id = MAX77686_BUCK##num, \
.ops = &max77686_ops, \
.type = REGULATOR_VOLTAGE, \
}
#define regulator_desc_buck1(num) { \
.name = "BUCK"#num, \
+ .of_match = of_match_ptr("BUCK"#num), \
+ .regulators_node = of_match_ptr("voltage-regulators"), \
.id = MAX77686_BUCK##num, \
.ops = &max77686_buck1_ops, \
.type = REGULATOR_VOLTAGE, \
}
#define regulator_desc_buck_dvs(num) { \
.name = "BUCK"#num, \
+ .of_match = of_match_ptr("BUCK"#num), \
+ .regulators_node = of_match_ptr("voltage-regulators"), \
.id = MAX77686_BUCK##num, \
.ops = &max77686_buck_dvs_ops, \
.type = REGULATOR_VOLTAGE, \
<< MAX77686_OPMODE_BUCK234_SHIFT, \
}
-static struct regulator_desc regulators[] = {
+static const struct regulator_desc regulators[] = {
regulator_desc_ldo1_low(1),
regulator_desc_ldo_low(2),
regulator_desc_ldo(3),
regulator_desc_buck(9),
};
-#ifdef CONFIG_OF
-static int max77686_pmic_dt_parse_pdata(struct platform_device *pdev,
- struct max77686_platform_data *pdata)
-{
- struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
- struct device_node *pmic_np, *regulators_np;
- struct max77686_regulator_data *rdata;
- struct of_regulator_match rmatch;
- unsigned int i;
-
- pmic_np = iodev->dev->of_node;
- regulators_np = of_get_child_by_name(pmic_np, "voltage-regulators");
- if (!regulators_np) {
- dev_err(&pdev->dev, "could not find regulators sub-node\n");
- return -EINVAL;
- }
-
- pdata->num_regulators = ARRAY_SIZE(regulators);
- rdata = devm_kzalloc(&pdev->dev, sizeof(*rdata) *
- pdata->num_regulators, GFP_KERNEL);
- if (!rdata) {
- of_node_put(regulators_np);
- return -ENOMEM;
- }
-
- for (i = 0; i < pdata->num_regulators; i++) {
- rmatch.name = regulators[i].name;
- rmatch.init_data = NULL;
- rmatch.of_node = NULL;
- of_regulator_match(&pdev->dev, regulators_np, &rmatch, 1);
- rdata[i].initdata = rmatch.init_data;
- rdata[i].of_node = rmatch.of_node;
- }
-
- pdata->regulators = rdata;
- of_node_put(regulators_np);
-
- return 0;
-}
-#else
-static int max77686_pmic_dt_parse_pdata(struct platform_device *pdev,
- struct max77686_platform_data *pdata)
-{
- return 0;
-}
-#endif /* CONFIG_OF */
-
static int max77686_pmic_probe(struct platform_device *pdev)
{
struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
- struct max77686_platform_data *pdata = dev_get_platdata(iodev->dev);
struct max77686_data *max77686;
- int i, ret = 0;
+ int i;
struct regulator_config config = { };
dev_dbg(&pdev->dev, "%s\n", __func__);
- if (!pdata) {
- dev_err(&pdev->dev, "no platform data found for regulator\n");
- return -ENODEV;
- }
-
- if (iodev->dev->of_node) {
- ret = max77686_pmic_dt_parse_pdata(pdev, pdata);
- if (ret)
- return ret;
- }
-
- if (pdata->num_regulators != MAX77686_REGULATORS) {
- dev_err(&pdev->dev,
- "Invalid initial data for regulator's initialiation\n");
- return -EINVAL;
- }
-
max77686 = devm_kzalloc(&pdev->dev, sizeof(struct max77686_data),
GFP_KERNEL);
if (!max77686)
return -ENOMEM;
- config.dev = &pdev->dev;
+ config.dev = iodev->dev;
config.regmap = iodev->regmap;
config.driver_data = max77686;
platform_set_drvdata(pdev, max77686);
for (i = 0; i < MAX77686_REGULATORS; i++) {
struct regulator_dev *rdev;
+ int id = regulators[i].id;
- config.init_data = pdata->regulators[i].initdata;
- config.of_node = pdata->regulators[i].of_node;
-
- max77686->opmode[i] = regulators[i].enable_mask;
+ max77686->opmode[id] = MAX77686_NORMAL;
rdev = devm_regulator_register(&pdev->dev,
®ulators[i], &config);
if (IS_ERR(rdev)) {
+ int ret = PTR_ERR(rdev);
dev_err(&pdev->dev,
- "regulator init failed for %d\n", i);
- return PTR_ERR(rdev);
+ "regulator init failed for %d: %d\n", i, ret);
+ return ret;
}
}
.enable_mask = SAFEOUT_CTRL_ENSAFEOUT##_num##_MASK , \
}
-static struct regulator_desc regulators[] = {
+static const struct regulator_desc regulators[] = {
regulator_desc_esafeout(1),
regulator_desc_esafeout(2),
{
struct max77693_dev *iodev = dev_get_drvdata(pdev->dev.parent);
struct max77693_regulator_data *rdata = NULL;
int num_rdata, i;
- struct regulator_config config;
+ struct regulator_config config = { };
num_rdata = max77693_pmic_init_rdata(&pdev->dev, &rdata);
if (!rdata || num_rdata <= 0) {
#include <linux/regulator/of_regulator.h>
#include <linux/mfd/max77686.h>
#include <linux/mfd/max77686-private.h>
+#include <dt-bindings/regulator/maxim,max77802.h>
/* Default ramp delay in case it is not manually set */
#define MAX77802_RAMP_DELAY 100000 /* uV/us */
#define MAX77802_RAMP_RATE_MASK_4BIT 0xF0
#define MAX77802_RAMP_RATE_SHIFT_4BIT 4
+#define MAX77802_STATUS_OFF 0x0
+#define MAX77802_OFF_PWRREQ 0x1
+#define MAX77802_LP_PWRREQ 0x2
+
/* MAX77802 has two register formats: 2-bit and 4-bit */
static const unsigned int ramp_table_77802_2bit[] = {
12500,
};
struct max77802_regulator_prv {
+ /* Array indexed by regulator id */
unsigned int opmode[MAX77802_REG_MAX];
};
+static inline unsigned int max77802_map_mode(unsigned int mode)
+{
+ return mode == MAX77802_OPMODE_NORMAL ?
+ REGULATOR_MODE_NORMAL : REGULATOR_MODE_STANDBY;
+}
+
static int max77802_get_opmode_shift(int id)
{
if (id == MAX77802_BUCK1 || (id >= MAX77802_BUCK5 &&
return -EINVAL;
}
-/*
- * Some BUCKS supports Normal[ON/OFF] mode during suspend
+/**
+ * max77802_set_suspend_disable - Disable the regulator during system suspend
+ * @rdev: regulator to mark as disabled
*
- * BUCK 1, 6, 2-4, 5, 7-10 (all)
- *
- * The other mode (0x02) will make PWRREQ switch between normal
- * and low power.
+ * All regulators expect LDO 1, 3, 20 and 21 support OFF by PWRREQ.
+ * Configure the regulator so the PMIC will turn it OFF during system suspend.
*/
-static int max77802_buck_set_suspend_disable(struct regulator_dev *rdev)
+static int max77802_set_suspend_disable(struct regulator_dev *rdev)
{
- unsigned int val = MAX77802_OPMODE_STANDBY;
+ unsigned int val = MAX77802_OFF_PWRREQ;
struct max77802_regulator_prv *max77802 = rdev_get_drvdata(rdev);
int id = rdev_get_id(rdev);
int shift = max77802_get_opmode_shift(id);
}
/*
- * Some LDOs supports LPM-ON/OFF/Normal-ON mode during suspend state
- * (Enable Control Logic1 by PWRREQ)
- *
- * LDOs 2, 4-19, 22-35.
+ * Some LDOs support Low Power Mode while the system is running.
*
+ * LDOs 1, 3, 20, 21.
*/
-static int max77802_ldo_set_suspend_mode_logic1(struct regulator_dev *rdev,
- unsigned int mode)
+static int max77802_set_mode(struct regulator_dev *rdev, unsigned int mode)
{
struct max77802_regulator_prv *max77802 = rdev_get_drvdata(rdev);
int id = rdev_get_id(rdev);
int shift = max77802_get_opmode_shift(id);
switch (mode) {
- case REGULATOR_MODE_IDLE: /* ON in LP Mode */
- val = MAX77802_OPMODE_LP;
+ case REGULATOR_MODE_STANDBY:
+ val = MAX77802_OPMODE_LP; /* ON in Low Power Mode */
break;
- case REGULATOR_MODE_NORMAL: /* ON in Normal Mode */
- val = MAX77802_OPMODE_NORMAL;
- break;
- case REGULATOR_MODE_STANDBY: /* ON/OFF by PWRREQ */
- val = MAX77802_OPMODE_STANDBY;
+ case REGULATOR_MODE_NORMAL:
+ val = MAX77802_OPMODE_NORMAL; /* ON in Normal Mode */
break;
default:
dev_warn(&rdev->dev, "%s: regulator mode: 0x%x not supported\n",
rdev->desc->enable_mask, val << shift);
}
-/*
- * Mode 1 (Output[ON/OFF] by PWRREQ) is not supported on some LDOs
- * (Enable Control Logic2 by PWRREQ)
+static unsigned max77802_get_mode(struct regulator_dev *rdev)
+{
+ struct max77802_regulator_prv *max77802 = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+
+ return max77802_map_mode(max77802->opmode[id]);
+}
+
+/**
+ * max77802_set_suspend_mode - set regulator opmode when the system is suspended
+ * @rdev: regulator to change mode
+ * @mode: operating mode to be set
*
- * LDOs 1, 20, 21, and 3,
+ * Will set the operating mode for the regulators during system suspend.
+ * This function is valid for the three different enable control logics:
*
+ * Enable Control Logic1 by PWRREQ (BUCK 2-4 and LDOs 2, 4-19, 22-35)
+ * Enable Control Logic2 by PWRREQ (LDOs 1, 20, 21)
+ * Enable Control Logic3 by PWRREQ (LDO 3)
+ *
+ * If setting the regulator mode fails, the function only warns but does
+ * not return an error code to avoid the regulator core to stop setting
+ * the operating mode for the remaining regulators.
*/
-static int max77802_ldo_set_suspend_mode_logic2(struct regulator_dev *rdev,
- unsigned int mode)
+static int max77802_set_suspend_mode(struct regulator_dev *rdev,
+ unsigned int mode)
{
struct max77802_regulator_prv *max77802 = rdev_get_drvdata(rdev);
int id = rdev_get_id(rdev);
unsigned int val;
int shift = max77802_get_opmode_shift(id);
+ /*
+ * If the regulator has been disabled for suspend
+ * then is invalid to try setting a suspend mode.
+ */
+ if (max77802->opmode[id] == MAX77802_OFF_PWRREQ) {
+ dev_warn(&rdev->dev, "%s: is disabled, mode: 0x%x not set\n",
+ rdev->desc->name, mode);
+ return 0;
+ }
+
switch (mode) {
- case REGULATOR_MODE_IDLE: /* ON in LP Mode */
- val = MAX77802_OPMODE_LP;
- break;
- case REGULATOR_MODE_NORMAL: /* ON in Normal Mode */
- val = MAX77802_OPMODE_NORMAL;
+ case REGULATOR_MODE_STANDBY:
+ /*
+ * If the regulator opmode is normal then enable
+ * ON in Low Power Mode by PWRREQ. If the mode is
+ * already Low Power then no action is required.
+ */
+ if (max77802->opmode[id] == MAX77802_OPMODE_NORMAL)
+ val = MAX77802_LP_PWRREQ;
+ else
+ return 0;
break;
+ case REGULATOR_MODE_NORMAL:
+ /*
+ * If the regulator operating mode is Low Power then
+ * normal is not a valid opmode in suspend. If the
+ * mode is already normal then no action is required.
+ */
+ if (max77802->opmode[id] == MAX77802_OPMODE_LP)
+ dev_warn(&rdev->dev, "%s: in Low Power: 0x%x invalid\n",
+ rdev->desc->name, mode);
+ return 0;
default:
dev_warn(&rdev->dev, "%s: regulator mode: 0x%x not supported\n",
rdev->desc->name, mode);
return -EINVAL;
}
- max77802->opmode[id] = val;
return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
rdev->desc->enable_mask, val << shift);
}
int id = rdev_get_id(rdev);
int shift = max77802_get_opmode_shift(id);
+ if (max77802->opmode[id] == MAX77802_OFF_PWRREQ)
+ max77802->opmode[id] = MAX77802_OPMODE_NORMAL;
+
return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
rdev->desc->enable_mask,
max77802->opmode[id] << shift);
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.set_voltage_time_sel = regulator_set_voltage_time_sel,
- .set_suspend_mode = max77802_ldo_set_suspend_mode_logic1,
+ .set_suspend_disable = max77802_set_suspend_disable,
+ .set_suspend_mode = max77802_set_suspend_mode,
};
/*
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.set_voltage_time_sel = regulator_set_voltage_time_sel,
- .set_suspend_mode = max77802_ldo_set_suspend_mode_logic2,
+ .set_mode = max77802_set_mode,
+ .get_mode = max77802_get_mode,
+ .set_suspend_mode = max77802_set_suspend_mode,
};
/* BUCKS 1, 6 */
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.set_voltage_time_sel = regulator_set_voltage_time_sel,
.set_ramp_delay = max77802_set_ramp_delay_4bit,
- .set_suspend_disable = max77802_buck_set_suspend_disable,
+ .set_suspend_disable = max77802_set_suspend_disable,
};
-/* BUCKs 2-4, 5, 7-10 */
+/* BUCKs 2-4 */
+static struct regulator_ops max77802_buck_234_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = max77802_enable,
+ .disable = regulator_disable_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_ramp_delay = max77802_set_ramp_delay_2bit,
+ .set_suspend_disable = max77802_set_suspend_disable,
+ .set_suspend_mode = max77802_set_suspend_mode,
+};
+
+/* BUCKs 5, 7-10 */
static struct regulator_ops max77802_buck_dvs_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.set_voltage_time_sel = regulator_set_voltage_time_sel,
.set_ramp_delay = max77802_set_ramp_delay_2bit,
- .set_suspend_disable = max77802_buck_set_suspend_disable,
+ .set_suspend_disable = max77802_set_suspend_disable,
};
/* LDOs 3-7, 9-14, 18-26, 28, 29, 32-34 */
#define regulator_77802_desc_p_ldo(num, supply, log) { \
.name = "LDO"#num, \
+ .of_match = of_match_ptr("LDO"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
.id = MAX77802_LDO##num, \
.supply_name = "inl"#supply, \
.ops = &max77802_ldo_ops_logic##log, \
.vsel_mask = MAX77802_VSEL_MASK, \
.enable_reg = MAX77802_REG_LDO1CTRL1 + num - 1, \
.enable_mask = MAX77802_OPMODE_MASK << MAX77802_OPMODE_SHIFT_LDO, \
+ .of_map_mode = max77802_map_mode, \
}
/* LDOs 1, 2, 8, 15, 17, 27, 30, 35 */
#define regulator_77802_desc_n_ldo(num, supply, log) { \
.name = "LDO"#num, \
+ .of_match = of_match_ptr("LDO"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
.id = MAX77802_LDO##num, \
.supply_name = "inl"#supply, \
.ops = &max77802_ldo_ops_logic##log, \
.vsel_mask = MAX77802_VSEL_MASK, \
.enable_reg = MAX77802_REG_LDO1CTRL1 + num - 1, \
.enable_mask = MAX77802_OPMODE_MASK << MAX77802_OPMODE_SHIFT_LDO, \
+ .of_map_mode = max77802_map_mode, \
}
/* BUCKs 1, 6 */
#define regulator_77802_desc_16_buck(num) { \
.name = "BUCK"#num, \
+ .of_match = of_match_ptr("BUCK"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
.id = MAX77802_BUCK##num, \
.supply_name = "inb"#num, \
.ops = &max77802_buck_16_dvs_ops, \
.vsel_mask = MAX77802_DVS_VSEL_MASK, \
.enable_reg = MAX77802_REG_BUCK ## num ## CTRL, \
.enable_mask = MAX77802_OPMODE_MASK, \
+ .of_map_mode = max77802_map_mode, \
}
/* BUCKS 2-4 */
#define regulator_77802_desc_234_buck(num) { \
.name = "BUCK"#num, \
+ .of_match = of_match_ptr("BUCK"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
.id = MAX77802_BUCK##num, \
.supply_name = "inb"#num, \
- .ops = &max77802_buck_dvs_ops, \
+ .ops = &max77802_buck_234_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
.min_uV = 600000, \
.enable_reg = MAX77802_REG_BUCK ## num ## CTRL1, \
.enable_mask = MAX77802_OPMODE_MASK << \
MAX77802_OPMODE_BUCK234_SHIFT, \
+ .of_map_mode = max77802_map_mode, \
}
/* BUCK 5 */
#define regulator_77802_desc_buck5(num) { \
.name = "BUCK"#num, \
+ .of_match = of_match_ptr("BUCK"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
.id = MAX77802_BUCK##num, \
.supply_name = "inb"#num, \
.ops = &max77802_buck_dvs_ops, \
.vsel_mask = MAX77802_VSEL_MASK, \
.enable_reg = MAX77802_REG_BUCK5CTRL, \
.enable_mask = MAX77802_OPMODE_MASK, \
+ .of_map_mode = max77802_map_mode, \
}
/* BUCKs 7-10 */
#define regulator_77802_desc_buck7_10(num) { \
.name = "BUCK"#num, \
+ .of_match = of_match_ptr("BUCK"#num), \
+ .regulators_node = of_match_ptr("regulators"), \
.id = MAX77802_BUCK##num, \
.supply_name = "inb"#num, \
.ops = &max77802_buck_dvs_ops, \
.vsel_mask = MAX77802_VSEL_MASK, \
.enable_reg = MAX77802_REG_BUCK7CTRL + (num - 7) * 3, \
.enable_mask = MAX77802_OPMODE_MASK, \
+ .of_map_mode = max77802_map_mode, \
}
-static struct regulator_desc regulators[] = {
+static const struct regulator_desc regulators[] = {
regulator_77802_desc_16_buck(1),
regulator_77802_desc_234_buck(2),
regulator_77802_desc_234_buck(3),
regulator_77802_desc_n_ldo(35, 2, 1),
};
-#ifdef CONFIG_OF
-static int max77802_pmic_dt_parse_pdata(struct platform_device *pdev,
- struct max77686_platform_data *pdata)
-{
- struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
- struct device_node *pmic_np, *regulators_np;
- struct max77686_regulator_data *rdata;
- struct of_regulator_match rmatch;
- unsigned int i;
-
- pmic_np = iodev->dev->of_node;
- regulators_np = of_get_child_by_name(pmic_np, "regulators");
- if (!regulators_np) {
- dev_err(&pdev->dev, "could not find regulators sub-node\n");
- return -EINVAL;
- }
-
- pdata->num_regulators = ARRAY_SIZE(regulators);
- rdata = devm_kzalloc(&pdev->dev, sizeof(*rdata) *
- pdata->num_regulators, GFP_KERNEL);
- if (!rdata) {
- of_node_put(regulators_np);
- return -ENOMEM;
- }
-
- for (i = 0; i < pdata->num_regulators; i++) {
- rmatch.name = regulators[i].name;
- rmatch.init_data = NULL;
- rmatch.of_node = NULL;
- if (of_regulator_match(&pdev->dev, regulators_np, &rmatch,
- 1) != 1) {
- dev_warn(&pdev->dev, "No matching regulator for '%s'\n",
- rmatch.name);
- continue;
- }
- rdata[i].initdata = rmatch.init_data;
- rdata[i].of_node = rmatch.of_node;
- rdata[i].id = regulators[i].id;
- }
-
- pdata->regulators = rdata;
- of_node_put(regulators_np);
-
- return 0;
-}
-#else
-static int max77802_pmic_dt_parse_pdata(struct platform_device *pdev,
- struct max77686_platform_data *pdata)
-{
- return 0;
-}
-#endif /* CONFIG_OF */
-
static int max77802_pmic_probe(struct platform_device *pdev)
{
struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
- struct max77686_platform_data *pdata = dev_get_platdata(iodev->dev);
struct max77802_regulator_prv *max77802;
- int i, ret = 0, val;
+ int i, val;
struct regulator_config config = { };
- /* This is allocated by the MFD driver */
- if (!pdata) {
- dev_err(&pdev->dev, "no platform data found for regulator\n");
- return -ENODEV;
- }
-
max77802 = devm_kzalloc(&pdev->dev,
sizeof(struct max77802_regulator_prv),
GFP_KERNEL);
if (!max77802)
return -ENOMEM;
- if (iodev->dev->of_node) {
- ret = max77802_pmic_dt_parse_pdata(pdev, pdata);
- if (ret)
- return ret;
- }
-
config.dev = iodev->dev;
config.regmap = iodev->regmap;
config.driver_data = max77802;
for (i = 0; i < MAX77802_REG_MAX; i++) {
struct regulator_dev *rdev;
- int id = pdata->regulators[i].id;
+ int id = regulators[i].id;
int shift = max77802_get_opmode_shift(id);
-
- config.init_data = pdata->regulators[i].initdata;
- config.of_node = pdata->regulators[i].of_node;
+ int ret;
ret = regmap_read(iodev->regmap, regulators[i].enable_reg, &val);
- val = val >> shift & MAX77802_OPMODE_MASK;
+ if (ret < 0) {
+ dev_warn(&pdev->dev,
+ "cannot read current mode for %d\n", i);
+ val = MAX77802_OPMODE_NORMAL;
+ } else {
+ val = val >> shift & MAX77802_OPMODE_MASK;
+ }
/*
* If the regulator is disabled and the system warm rebooted,
* the hardware reports OFF as the regulator operating mode.
* Default to operating mode NORMAL in that case.
*/
- if (val == MAX77802_OPMODE_OFF)
+ if (val == MAX77802_STATUS_OFF)
max77802->opmode[id] = MAX77802_OPMODE_NORMAL;
else
max77802->opmode[id] = val;
rdev = devm_regulator_register(&pdev->dev,
®ulators[i], &config);
if (IS_ERR(rdev)) {
+ ret = PTR_ERR(rdev);
dev_err(&pdev->dev,
- "regulator init failed for %d\n", i);
- return PTR_ERR(rdev);
+ "regulator init failed for %d: %d\n", i, ret);
+ return ret;
}
}
int matched, i;
struct device_node *np;
struct max8660_subdev_data *sub;
- struct of_regulator_match rmatch[ARRAY_SIZE(max8660_reg)];
+ struct of_regulator_match rmatch[ARRAY_SIZE(max8660_reg)] = { };
np = of_get_child_by_name(dev->of_node, "regulators");
if (!np) {
if (of_property_read_u32(np, "max8952,ramp-speed", &pd->ramp_speed))
dev_warn(dev, "max8952,ramp-speed property not specified, defaulting to 32mV/us\n");
- pd->reg_data = of_get_regulator_init_data(dev, np);
+ pd->reg_data = of_get_regulator_init_data(dev, np, ®ulator);
if (!pd->reg_data) {
dev_err(dev, "Failed to parse regulator init data\n");
return NULL;
config.of_node = client->dev.of_node;
config.ena_gpio = pdata->gpio_en;
+ if (client->dev.of_node)
+ config.ena_gpio_initialized = true;
if (pdata->reg_data->constraints.boot_on)
config.ena_gpio_flags |= GPIOF_OUT_INIT_HIGH;
config.dev = &client->dev;
config.init_data = pdata ? pdata->reg_init_data :
- of_get_regulator_init_data(&client->dev, client->dev.of_node);
+ of_get_regulator_init_data(&client->dev, client->dev.of_node,
+ &max->desc);
config.driver_data = max;
config.of_node = client->dev.of_node;
config.regmap = max->regmap;
rdata->id = i;
rdata->initdata = of_get_regulator_init_data(&pdev->dev,
- reg_np);
+ reg_np,
+ ®ulators[i]);
rdata->reg_node = reg_np;
rdata++;
}
continue;
rdata->id = regulators[i].id;
- rdata->initdata = of_get_regulator_init_data(
- iodev->dev, reg_np);
+ rdata->initdata = of_get_regulator_init_data(iodev->dev,
+ reg_np,
+ ®ulators[i]);
rdata->reg_node = reg_np;
++rdata;
}
regulators[i].desc.name)) {
p->id = i;
p->init_data = of_get_regulator_init_data(
- &pdev->dev, child);
+ &pdev->dev, child,
+ ®ulators[i].desc);
p->node = child;
p++;
#include "internal.h"
+static const char *const regulator_states[PM_SUSPEND_MAX + 1] = {
+ [PM_SUSPEND_MEM] = "regulator-state-mem",
+ [PM_SUSPEND_MAX] = "regulator-state-disk",
+};
+
static void of_get_regulation_constraints(struct device_node *np,
- struct regulator_init_data **init_data)
+ struct regulator_init_data **init_data,
+ const struct regulator_desc *desc)
{
const __be32 *min_uV, *max_uV;
struct regulation_constraints *constraints = &(*init_data)->constraints;
- int ret;
+ struct regulator_state *suspend_state;
+ struct device_node *suspend_np;
+ int ret, i;
u32 pval;
constraints->name = of_get_property(np, "regulator-name", NULL);
ret = of_property_read_u32(np, "regulator-enable-ramp-delay", &pval);
if (!ret)
constraints->enable_time = pval;
+
+ if (!of_property_read_u32(np, "regulator-initial-mode", &pval)) {
+ if (desc && desc->of_map_mode) {
+ ret = desc->of_map_mode(pval);
+ if (ret == -EINVAL)
+ pr_err("%s: invalid mode %u\n", np->name, pval);
+ else
+ constraints->initial_mode = ret;
+ } else {
+ pr_warn("%s: mapping for mode %d not defined\n",
+ np->name, pval);
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(regulator_states); i++) {
+ switch (i) {
+ case PM_SUSPEND_MEM:
+ suspend_state = &constraints->state_mem;
+ break;
+ case PM_SUSPEND_MAX:
+ suspend_state = &constraints->state_disk;
+ break;
+ case PM_SUSPEND_ON:
+ case PM_SUSPEND_FREEZE:
+ case PM_SUSPEND_STANDBY:
+ default:
+ continue;
+ };
+
+ suspend_np = of_get_child_by_name(np, regulator_states[i]);
+ if (!suspend_np || !suspend_state)
+ continue;
+
+ if (!of_property_read_u32(suspend_np, "regulator-mode",
+ &pval)) {
+ if (desc && desc->of_map_mode) {
+ ret = desc->of_map_mode(pval);
+ if (ret == -EINVAL)
+ pr_err("%s: invalid mode %u\n",
+ np->name, pval);
+ else
+ suspend_state->mode = ret;
+ } else {
+ pr_warn("%s: mapping for mode %d not defined\n",
+ np->name, pval);
+ }
+ }
+
+ if (of_property_read_bool(suspend_np,
+ "regulator-on-in-suspend"))
+ suspend_state->enabled = true;
+ else if (of_property_read_bool(suspend_np,
+ "regulator-off-in-suspend"))
+ suspend_state->disabled = true;
+
+ if (!of_property_read_u32(suspend_np,
+ "regulator-suspend-microvolt", &pval))
+ suspend_state->uV = pval;
+
+ of_node_put(suspend_np);
+ suspend_state = NULL;
+ suspend_np = NULL;
+ }
}
/**
* of_get_regulator_init_data - extract regulator_init_data structure info
* @dev: device requesting for regulator_init_data
+ * @node: regulator device node
+ * @desc: regulator description
*
* Populates regulator_init_data structure by extracting data from device
* tree node, returns a pointer to the populated struture or NULL if memory
* alloc fails.
*/
struct regulator_init_data *of_get_regulator_init_data(struct device *dev,
- struct device_node *node)
+ struct device_node *node,
+ const struct regulator_desc *desc)
{
struct regulator_init_data *init_data;
if (!init_data)
return NULL; /* Out of memory? */
- of_get_regulation_constraints(node, &init_data);
+ of_get_regulation_constraints(node, &init_data, desc);
return init_data;
}
EXPORT_SYMBOL_GPL(of_get_regulator_init_data);
continue;
match->init_data =
- of_get_regulator_init_data(dev, child);
+ of_get_regulator_init_data(dev, child,
+ match->desc);
if (!match->init_data) {
dev_err(dev,
"failed to parse DT for regulator %s\n",
search = dev->of_node;
if (!search) {
- dev_err(dev, "Failed to find regulator container node\n");
+ dev_dbg(dev, "Failed to find regulator container node '%s'\n",
+ desc->regulators_node);
return NULL;
}
if (strcmp(desc->of_match, name))
continue;
- init_data = of_get_regulator_init_data(dev, child);
+ init_data = of_get_regulator_init_data(dev, child, desc);
if (!init_data) {
dev_err(dev,
"failed to parse DT for regulator %s\n",
return ret;
}
- config.init_data = of_get_regulator_init_data(&pdev->dev, np);
+ config.init_data = of_get_regulator_init_data(&pdev->dev, np,
+ &drvdata->desc);
if (!config.init_data)
return -ENOMEM;
REGULATOR_LINEAR_RANGE(1500000, 64, 100, 50000),
};
+static const struct regulator_linear_range smb208_ranges[] = {
+ REGULATOR_LINEAR_RANGE( 375000, 0, 29, 12500),
+ REGULATOR_LINEAR_RANGE( 750000, 30, 89, 12500),
+ REGULATOR_LINEAR_RANGE(1500000, 90, 153, 25000),
+ REGULATOR_LINEAR_RANGE(3100000, 154, 234, 25000),
+};
+
static const struct regulator_linear_range ncp_ranges[] = {
REGULATOR_LINEAR_RANGE(1500000, 0, 31, 50000),
};
.parts = &rpm8960_switch_parts,
};
+static const struct qcom_rpm_reg smb208_smps = {
+ .desc.linear_ranges = smb208_ranges,
+ .desc.n_linear_ranges = ARRAY_SIZE(smb208_ranges),
+ .desc.n_voltages = 235,
+ .desc.ops = &uV_ops,
+ .parts = &rpm8960_smps_parts,
+ .supports_force_mode_auto = false,
+ .supports_force_mode_bypass = false,
+};
+
static const struct of_device_id rpm_of_match[] = {
{ .compatible = "qcom,rpm-pm8058-pldo", .data = &pm8058_pldo },
{ .compatible = "qcom,rpm-pm8058-nldo", .data = &pm8058_nldo },
{ .compatible = "qcom,rpm-pm8921-ftsmps", .data = &pm8921_ftsmps },
{ .compatible = "qcom,rpm-pm8921-ncp", .data = &pm8921_ncp },
{ .compatible = "qcom,rpm-pm8921-switch", .data = &pm8921_switch },
+
+ { .compatible = "qcom,rpm-smb208", .data = &smb208_smps },
{ }
};
MODULE_DEVICE_TABLE(of, rpm_of_match);
match = of_match_device(rpm_of_match, &pdev->dev);
template = match->data;
- initdata = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node);
- if (!initdata)
- return -EINVAL;
-
vreg = devm_kmalloc(&pdev->dev, sizeof(*vreg), GFP_KERNEL);
if (!vreg) {
dev_err(&pdev->dev, "failed to allocate vreg\n");
return -ENODEV;
}
+ initdata = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node,
+ &vreg->desc);
+ if (!initdata)
+ return -EINVAL;
+
key = "reg";
ret = of_property_read_u32(pdev->dev.of_node, key, &val);
if (ret) {
#define RK808_RAMP_RATE_6MV_PER_US (2 << RK808_RAMP_RATE_OFFSET)
#define RK808_RAMP_RATE_10MV_PER_US (3 << RK808_RAMP_RATE_OFFSET)
+/* Offset from XXX_ON_VSEL to XXX_SLP_VSEL */
+#define RK808_SLP_REG_OFFSET 1
+
+/* Offset from XXX_EN_REG to SLEEP_SET_OFF_XXX */
+#define RK808_SLP_SET_OFF_REG_OFFSET 2
+
static const int rk808_buck_config_regs[] = {
RK808_BUCK1_CONFIG_REG,
RK808_BUCK2_CONFIG_REG,
RK808_RAMP_RATE_MASK, ramp_value);
}
+int rk808_set_suspend_voltage(struct regulator_dev *rdev, int uv)
+{
+ unsigned int reg;
+ int sel = regulator_map_voltage_linear_range(rdev, uv, uv);
+
+ if (sel < 0)
+ return -EINVAL;
+
+ reg = rdev->desc->vsel_reg + RK808_SLP_REG_OFFSET;
+
+ return regmap_update_bits(rdev->regmap, reg,
+ rdev->desc->vsel_mask,
+ sel);
+}
+
+int rk808_set_suspend_enable(struct regulator_dev *rdev)
+{
+ unsigned int reg;
+
+ reg = rdev->desc->enable_reg + RK808_SLP_SET_OFF_REG_OFFSET;
+
+ return regmap_update_bits(rdev->regmap, reg,
+ rdev->desc->enable_mask,
+ 0);
+}
+
+int rk808_set_suspend_disable(struct regulator_dev *rdev)
+{
+ unsigned int reg;
+
+ reg = rdev->desc->enable_reg + RK808_SLP_SET_OFF_REG_OFFSET;
+
+ return regmap_update_bits(rdev->regmap, reg,
+ rdev->desc->enable_mask,
+ rdev->desc->enable_mask);
+}
+
static struct regulator_ops rk808_buck1_2_ops = {
.list_voltage = regulator_list_voltage_linear_range,
.map_voltage = regulator_map_voltage_linear_range,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.set_ramp_delay = rk808_set_ramp_delay,
+ .set_suspend_voltage = rk808_set_suspend_voltage,
+ .set_suspend_enable = rk808_set_suspend_enable,
+ .set_suspend_disable = rk808_set_suspend_disable,
};
static struct regulator_ops rk808_reg_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
+ .set_suspend_voltage = rk808_set_suspend_voltage,
+ .set_suspend_enable = rk808_set_suspend_enable,
+ .set_suspend_disable = rk808_set_suspend_disable,
};
static struct regulator_ops rk808_switch_ops = {
- .enable = regulator_enable_regmap,
- .disable = regulator_disable_regmap,
- .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_suspend_enable = rk808_set_suspend_enable,
+ .set_suspend_disable = rk808_set_suspend_disable,
};
static const struct regulator_desc rk808_reg[] = {
#define REG(rid, ereg, emask, vreg, vmask, min, max, step) \
[RN5T618_##rid] = { \
.name = #rid, \
+ .of_match = of_match_ptr(#rid), \
+ .regulators_node = of_match_ptr("regulators"), \
.id = RN5T618_##rid, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
REG(LDORTC2, LDOEN2, BIT(5), LDORTC2DAC, 0x7f, 900000, 3500000, 25000),
};
-static struct of_regulator_match rn5t618_matches[] = {
- [RN5T618_DCDC1] = { .name = "DCDC1" },
- [RN5T618_DCDC2] = { .name = "DCDC2" },
- [RN5T618_DCDC3] = { .name = "DCDC3" },
- [RN5T618_LDO1] = { .name = "LDO1" },
- [RN5T618_LDO2] = { .name = "LDO2" },
- [RN5T618_LDO3] = { .name = "LDO3" },
- [RN5T618_LDO4] = { .name = "LDO4" },
- [RN5T618_LDO5] = { .name = "LDO5" },
- [RN5T618_LDORTC1] = { .name = "LDORTC1" },
- [RN5T618_LDORTC2] = { .name = "LDORTC2" },
-};
-
-static int rn5t618_regulator_parse_dt(struct platform_device *pdev)
-{
- struct device_node *np, *regulators;
- int ret;
-
- np = of_node_get(pdev->dev.parent->of_node);
- if (!np)
- return 0;
-
- regulators = of_get_child_by_name(np, "regulators");
- if (!regulators) {
- dev_err(&pdev->dev, "regulators node not found\n");
- return -EINVAL;
- }
-
- ret = of_regulator_match(&pdev->dev, regulators, rn5t618_matches,
- ARRAY_SIZE(rn5t618_matches));
- of_node_put(regulators);
- if (ret < 0) {
- dev_err(&pdev->dev, "error parsing regulator init data: %d\n",
- ret);
- }
-
- return 0;
-}
-
static int rn5t618_regulator_probe(struct platform_device *pdev)
{
struct rn5t618 *rn5t618 = dev_get_drvdata(pdev->dev.parent);
struct regulator_config config = { };
struct regulator_dev *rdev;
- int ret, i;
-
- ret = rn5t618_regulator_parse_dt(pdev);
- if (ret)
- return ret;
+ int i;
for (i = 0; i < RN5T618_REG_NUM; i++) {
- config.dev = &pdev->dev;
- config.init_data = rn5t618_matches[i].init_data;
- config.of_node = rn5t618_matches[i].of_node;
+ config.dev = pdev->dev.parent;
config.regmap = rn5t618->regmap;
rdev = devm_regulator_register(&pdev->dev,
--- /dev/null
+/*
+ * Regulator driver for the Richtek RT5033
+ *
+ * Copyright (C) 2014 Samsung Electronics, Co., Ltd.
+ * Author: Beomho Seo <beomho.seo@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published bythe Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/mfd/rt5033.h>
+#include <linux/mfd/rt5033-private.h>
+#include <linux/regulator/of_regulator.h>
+
+static struct regulator_ops rt5033_safe_ldo_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+static struct regulator_ops rt5033_buck_ops = {
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+};
+
+static const struct regulator_desc rt5033_supported_regulators[] = {
+ [RT5033_BUCK] = {
+ .name = "BUCK",
+ .id = RT5033_BUCK,
+ .ops = &rt5033_buck_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = RT5033_REGULATOR_BUCK_VOLTAGE_STEP_NUM,
+ .min_uV = RT5033_REGULATOR_BUCK_VOLTAGE_MIN,
+ .uV_step = RT5033_REGULATOR_BUCK_VOLTAGE_STEP,
+ .enable_reg = RT5033_REG_CTRL,
+ .enable_mask = RT5033_CTRL_EN_BUCK_MASK,
+ .vsel_reg = RT5033_REG_BUCK_CTRL,
+ .vsel_mask = RT5033_BUCK_CTRL_MASK,
+ },
+ [RT5033_LDO] = {
+ .name = "LDO",
+ .id = RT5033_LDO,
+ .ops = &rt5033_buck_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = RT5033_REGULATOR_LDO_VOLTAGE_STEP_NUM,
+ .min_uV = RT5033_REGULATOR_LDO_VOLTAGE_MIN,
+ .uV_step = RT5033_REGULATOR_LDO_VOLTAGE_STEP,
+ .enable_reg = RT5033_REG_CTRL,
+ .enable_mask = RT5033_CTRL_EN_LDO_MASK,
+ .vsel_reg = RT5033_REG_LDO_CTRL,
+ .vsel_mask = RT5033_LDO_CTRL_MASK,
+ },
+ [RT5033_SAFE_LDO] = {
+ .name = "SAFE_LDO",
+ .id = RT5033_SAFE_LDO,
+ .ops = &rt5033_safe_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ .min_uV = RT5033_REGULATOR_SAFE_LDO_VOLTAGE,
+ .enable_reg = RT5033_REG_CTRL,
+ .enable_mask = RT5033_CTRL_EN_SAFE_LDO_MASK,
+ },
+};
+
+static int rt5033_regulator_probe(struct platform_device *pdev)
+{
+ struct rt5033_dev *rt5033 = dev_get_drvdata(pdev->dev.parent);
+ int ret, i;
+ struct regulator_config config = {};
+
+ config.dev = &pdev->dev;
+ config.driver_data = rt5033;
+
+ for (i = 0; i < ARRAY_SIZE(rt5033_supported_regulators); i++) {
+ struct regulator_dev *regulator;
+
+ config.regmap = rt5033->regmap;
+
+ regulator = devm_regulator_register(&pdev->dev,
+ &rt5033_supported_regulators[i], &config);
+ if (IS_ERR(regulator)) {
+ ret = PTR_ERR(regulator);
+ dev_err(&pdev->dev,
+ "Regulator init failed %d: with error: %d\n",
+ i, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id rt5033_regulator_id[] = {
+ { "rt5033-regulator", },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, rt5033_regulator_id);
+
+static struct platform_driver rt5033_regulator_driver = {
+ .driver = {
+ .name = "rt5033-regulator",
+ },
+ .probe = rt5033_regulator_probe,
+ .id_table = rt5033_regulator_id,
+};
+module_platform_driver(rt5033_regulator_driver);
+
+MODULE_DESCRIPTION("Richtek RT5033 Regulator driver");
+MODULE_AUTHOR("Beomho Seo <beomho.seo@samsung.com>");
+MODULE_LICENSE("GPL");
.enable_mask = S2MPA01_ENABLE_MASK \
}
-static struct regulator_desc regulators[] = {
+static const struct regulator_desc regulators[] = {
regulator_desc_ldo(1, STEP_25_MV),
regulator_desc_ldo(2, STEP_50_MV),
regulator_desc_ldo(3, STEP_50_MV),
{
struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
struct sec_platform_data *pdata = dev_get_platdata(iodev->dev);
- struct of_regulator_match rdata[S2MPA01_REGULATOR_MAX];
+ struct of_regulator_match rdata[S2MPA01_REGULATOR_MAX] = { };
struct device_node *reg_np = NULL;
struct regulator_config config = { };
struct s2mpa01_info *s2mpa01;
#include <linux/of_gpio.h>
#include <linux/mfd/samsung/core.h>
#include <linux/mfd/samsung/s2mps11.h>
+#include <linux/mfd/samsung/s2mps13.h>
#include <linux/mfd/samsung/s2mps14.h>
#include <linux/mfd/samsung/s2mpu02.h>
enum sec_device_type dev_type;
/*
- * One bit for each S2MPS14/S2MPU02 regulator whether the suspend mode
- * was enabled.
+ * One bit for each S2MPS13/S2MPS14/S2MPU02 regulator whether
+ * the suspend mode was enabled.
*/
- unsigned long long s2mps14_suspend_state:35;
+ unsigned long long s2mps14_suspend_state:50;
/* Array of size rdev_num with GPIO-s for external sleep control */
int *ext_control_gpio;
regulator_desc_s2mps11_buck6_10(10, MIN_750_MV, STEP_12_5_MV),
};
+static struct regulator_ops s2mps14_reg_ops;
+
+#define regulator_desc_s2mps13_ldo(num, min, step, min_sel) { \
+ .name = "LDO"#num, \
+ .id = S2MPS13_LDO##num, \
+ .ops = &s2mps14_reg_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = min, \
+ .uV_step = step, \
+ .linear_min_sel = min_sel, \
+ .n_voltages = S2MPS14_LDO_N_VOLTAGES, \
+ .vsel_reg = S2MPS13_REG_L1CTRL + num - 1, \
+ .vsel_mask = S2MPS14_LDO_VSEL_MASK, \
+ .enable_reg = S2MPS13_REG_L1CTRL + num - 1, \
+ .enable_mask = S2MPS14_ENABLE_MASK \
+}
+
+#define regulator_desc_s2mps13_buck(num, min, step, min_sel) { \
+ .name = "BUCK"#num, \
+ .id = S2MPS13_BUCK##num, \
+ .ops = &s2mps14_reg_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = min, \
+ .uV_step = step, \
+ .linear_min_sel = min_sel, \
+ .n_voltages = S2MPS14_BUCK_N_VOLTAGES, \
+ .ramp_delay = S2MPS13_BUCK_RAMP_DELAY, \
+ .vsel_reg = S2MPS13_REG_B1OUT + (num - 1) * 2, \
+ .vsel_mask = S2MPS14_BUCK_VSEL_MASK, \
+ .enable_reg = S2MPS13_REG_B1CTRL + (num - 1) * 2, \
+ .enable_mask = S2MPS14_ENABLE_MASK \
+}
+
+static const struct regulator_desc s2mps13_regulators[] = {
+ regulator_desc_s2mps13_ldo(1, MIN_800_MV, STEP_12_5_MV, 0x00),
+ regulator_desc_s2mps13_ldo(2, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_ldo(3, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(4, MIN_800_MV, STEP_12_5_MV, 0x00),
+ regulator_desc_s2mps13_ldo(5, MIN_800_MV, STEP_12_5_MV, 0x00),
+ regulator_desc_s2mps13_ldo(6, MIN_800_MV, STEP_12_5_MV, 0x00),
+ regulator_desc_s2mps13_ldo(7, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(8, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(9, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(10, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_ldo(11, MIN_800_MV, STEP_25_MV, 0x10),
+ regulator_desc_s2mps13_ldo(12, MIN_800_MV, STEP_25_MV, 0x10),
+ regulator_desc_s2mps13_ldo(13, MIN_800_MV, STEP_25_MV, 0x10),
+ regulator_desc_s2mps13_ldo(14, MIN_800_MV, STEP_12_5_MV, 0x00),
+ regulator_desc_s2mps13_ldo(15, MIN_800_MV, STEP_12_5_MV, 0x00),
+ regulator_desc_s2mps13_ldo(16, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_ldo(17, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_ldo(18, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(19, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(20, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_ldo(21, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(22, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(23, MIN_800_MV, STEP_12_5_MV, 0x00),
+ regulator_desc_s2mps13_ldo(24, MIN_800_MV, STEP_12_5_MV, 0x00),
+ regulator_desc_s2mps13_ldo(25, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_ldo(26, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_ldo(27, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_ldo(28, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(29, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_ldo(30, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_ldo(31, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(32, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(33, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_ldo(34, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(35, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_ldo(36, MIN_800_MV, STEP_12_5_MV, 0x00),
+ regulator_desc_s2mps13_ldo(37, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(38, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_ldo(39, MIN_1000_MV, STEP_25_MV, 0x08),
+ regulator_desc_s2mps13_ldo(40, MIN_1400_MV, STEP_50_MV, 0x0C),
+ regulator_desc_s2mps13_buck(1, MIN_500_MV, STEP_6_25_MV, 0x10),
+ regulator_desc_s2mps13_buck(2, MIN_500_MV, STEP_6_25_MV, 0x10),
+ regulator_desc_s2mps13_buck(3, MIN_500_MV, STEP_6_25_MV, 0x10),
+ regulator_desc_s2mps13_buck(4, MIN_500_MV, STEP_6_25_MV, 0x10),
+ regulator_desc_s2mps13_buck(5, MIN_500_MV, STEP_6_25_MV, 0x10),
+ regulator_desc_s2mps13_buck(6, MIN_500_MV, STEP_6_25_MV, 0x10),
+ regulator_desc_s2mps13_buck(7, MIN_500_MV, STEP_6_25_MV, 0x10),
+ regulator_desc_s2mps13_buck(8, MIN_1000_MV, STEP_12_5_MV, 0x20),
+ regulator_desc_s2mps13_buck(9, MIN_1000_MV, STEP_12_5_MV, 0x20),
+ regulator_desc_s2mps13_buck(10, MIN_500_MV, STEP_6_25_MV, 0x10),
+};
+
static int s2mps14_regulator_enable(struct regulator_dev *rdev)
{
struct s2mps11_info *s2mps11 = rdev_get_drvdata(rdev);
unsigned int val;
switch (s2mps11->dev_type) {
+ case S2MPS13X:
case S2MPS14X:
if (s2mps11->s2mps14_suspend_state & (1 << rdev_get_id(rdev)))
val = S2MPS14_ENABLE_SUSPEND;
/* Below LDO should be always on or does not support suspend mode. */
switch (s2mps11->dev_type) {
+ case S2MPS13X:
case S2MPS14X:
switch (rdev_id) {
case S2MPS14_LDO3:
s2mps11->rdev_num = ARRAY_SIZE(s2mps11_regulators);
regulators = s2mps11_regulators;
break;
+ case S2MPS13X:
+ s2mps11->rdev_num = ARRAY_SIZE(s2mps13_regulators);
+ regulators = s2mps13_regulators;
+ break;
case S2MPS14X:
s2mps11->rdev_num = ARRAY_SIZE(s2mps14_regulators);
regulators = s2mps14_regulators;
return -EINVAL;
};
- s2mps11->ext_control_gpio = devm_kzalloc(&pdev->dev,
+ s2mps11->ext_control_gpio = devm_kmalloc(&pdev->dev,
sizeof(*s2mps11->ext_control_gpio) * s2mps11->rdev_num,
GFP_KERNEL);
if (!s2mps11->ext_control_gpio)
config.regmap = iodev->regmap_pmic;
config.driver_data = s2mps11;
config.ena_gpio_flags = GPIOF_OUT_INIT_HIGH;
+ config.ena_gpio_initialized = true;
for (i = 0; i < s2mps11->rdev_num; i++) {
struct regulator_dev *regulator;
static const struct platform_device_id s2mps11_pmic_id[] = {
{ "s2mps11-pmic", S2MPS11X},
+ { "s2mps13-pmic", S2MPS13X},
{ "s2mps14-pmic", S2MPS14X},
{ "s2mpu02-pmic", S2MPU02},
{ },
rdata->id = i;
rdata->initdata = of_get_regulator_init_data(
- &pdev->dev, reg_np);
+ &pdev->dev, reg_np,
+ ®ulators[i]);
rdata->reg_node = reg_np;
rdata++;
rmode->id = i;
config.of_node = pdata->regulators[i].reg_node;
config.ena_gpio = -EINVAL;
config.ena_gpio_flags = 0;
+ config.ena_gpio_initialized = true;
if (gpio_is_valid(pdata->regulators[i].ext_control_gpio))
s5m8767_regulator_config_ext_control(s5m8767,
&pdata->regulators[i], &config);
* Author : Gyungoh Yoo <jack.yoo@skyworksinc.com>
*
* This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any
- * later version.
+ * under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
static const struct regulator_desc sky81452_reg = {
.name = "LOUT",
+ .of_match = of_match_ptr("lout"),
+ .regulators_node = of_match_ptr("regulator"),
.ops = &sky81452_reg_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_mask = SKY81452_LEN,
};
-#ifdef CONFIG_OF
-static struct regulator_init_data *sky81452_reg_parse_dt(struct device *dev)
-{
- struct regulator_init_data *init_data;
- struct device_node *np;
-
- np = of_get_child_by_name(dev->parent->of_node, "regulator");
- if (unlikely(!np)) {
- dev_err(dev, "regulator node not found");
- return NULL;
- }
-
- init_data = of_get_regulator_init_data(dev, np);
-
- of_node_put(np);
- return init_data;
-}
-#else
-static struct regulator_init_data *sky81452_reg_parse_dt(struct device *dev)
-{
- return ERR_PTR(-EINVAL);
-}
-#endif
-
static int sky81452_reg_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct regulator_config config = { };
struct regulator_dev *rdev;
- if (!init_data) {
- init_data = sky81452_reg_parse_dt(dev);
- if (IS_ERR(init_data))
- return PTR_ERR(init_data);
- }
-
- config.dev = dev;
+ config.dev = dev->parent;
config.init_data = init_data;
config.of_node = dev->of_node;
config.regmap = dev_get_drvdata(dev->parent);
rdev = devm_regulator_register(dev, &sky81452_reg, &config);
- if (IS_ERR(rdev))
+ if (IS_ERR(rdev)) {
+ dev_err(dev, "failed to register. err=%ld\n", PTR_ERR(rdev));
return PTR_ERR(rdev);
+ }
platform_set_drvdata(pdev, rdev);
MODULE_DESCRIPTION("Skyworks SKY81452 Regulator driver");
MODULE_AUTHOR("Gyungoh Yoo <jack.yoo@skyworksinc.com>");
-MODULE_LICENSE("GPL");
-MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
config.regmap = stw481x->map;
config.of_node = pdev->dev.of_node;
config.init_data = of_get_regulator_init_data(&pdev->dev,
- pdev->dev.of_node);
+ pdev->dev.of_node,
+ &vmmc_regulator);
stw481x->vmmc_regulator = devm_regulator_register(&pdev->dev,
&vmmc_regulator, &config);
return -EINVAL;
}
- initdata = of_get_regulator_init_data(dev, pdev->dev.of_node);
+ initdata = of_get_regulator_init_data(dev, pdev->dev.of_node,
+ &abb->rdesc);
if (!initdata) {
dev_err(dev, "%s: Unable to alloc regulator init data\n",
__func__);
MODULE_DEVICE_TABLE(of, tps51632_of_match);
static struct tps51632_regulator_platform_data *
- of_get_tps51632_platform_data(struct device *dev)
+ of_get_tps51632_platform_data(struct device *dev,
+ const struct regulator_desc *desc)
{
struct tps51632_regulator_platform_data *pdata;
struct device_node *np = dev->of_node;
if (!pdata)
return NULL;
- pdata->reg_init_data = of_get_regulator_init_data(dev, dev->of_node);
+ pdata->reg_init_data = of_get_regulator_init_data(dev, dev->of_node,
+ desc);
if (!pdata->reg_init_data) {
dev_err(dev, "Not able to get OF regulator init data\n");
return NULL;
}
#else
static struct tps51632_regulator_platform_data *
- of_get_tps51632_platform_data(struct device *dev)
+ of_get_tps51632_platform_data(struct device *dev,
+ const struct regulator_desc *desc)
{
return NULL;
}
}
}
+ tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL);
+ if (!tps)
+ return -ENOMEM;
+
+ tps->dev = &client->dev;
+ tps->desc.name = client->name;
+ tps->desc.id = 0;
+ tps->desc.ramp_delay = TPS51632_DEFAULT_RAMP_DELAY;
+ tps->desc.min_uV = TPS51632_MIN_VOLTAGE;
+ tps->desc.uV_step = TPS51632_VOLTAGE_STEP_10mV;
+ tps->desc.linear_min_sel = TPS51632_MIN_VSEL;
+ tps->desc.n_voltages = TPS51632_MAX_VSEL + 1;
+ tps->desc.ops = &tps51632_dcdc_ops;
+ tps->desc.type = REGULATOR_VOLTAGE;
+ tps->desc.owner = THIS_MODULE;
+
pdata = dev_get_platdata(&client->dev);
if (!pdata && client->dev.of_node)
- pdata = of_get_tps51632_platform_data(&client->dev);
+ pdata = of_get_tps51632_platform_data(&client->dev, &tps->desc);
if (!pdata) {
dev_err(&client->dev, "No Platform data\n");
return -EINVAL;
}
}
- tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL);
- if (!tps)
- return -ENOMEM;
-
- tps->dev = &client->dev;
- tps->desc.name = client->name;
- tps->desc.id = 0;
- tps->desc.ramp_delay = TPS51632_DEFAULT_RAMP_DELAY;
- tps->desc.min_uV = TPS51632_MIN_VOLTAGE;
- tps->desc.uV_step = TPS51632_VOLTAGE_STEP_10mV;
- tps->desc.linear_min_sel = TPS51632_MIN_VSEL;
- tps->desc.n_voltages = TPS51632_MAX_VSEL + 1;
- tps->desc.ops = &tps51632_dcdc_ops;
- tps->desc.type = REGULATOR_VOLTAGE;
- tps->desc.owner = THIS_MODULE;
-
if (pdata->enable_pwm_dvfs)
tps->desc.vsel_reg = TPS51632_VOLTAGE_BASE_REG;
else
};
static struct tps62360_regulator_platform_data *
- of_get_tps62360_platform_data(struct device *dev)
+ of_get_tps62360_platform_data(struct device *dev,
+ const struct regulator_desc *desc)
{
struct tps62360_regulator_platform_data *pdata;
struct device_node *np = dev->of_node;
if (!pdata)
return NULL;
- pdata->reg_init_data = of_get_regulator_init_data(dev, dev->of_node);
+ pdata->reg_init_data = of_get_regulator_init_data(dev, dev->of_node,
+ desc);
if (!pdata->reg_init_data) {
dev_err(dev, "Not able to get OF regulator init data\n");
return NULL;
pdata = dev_get_platdata(&client->dev);
+ tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL);
+ if (!tps)
+ return -ENOMEM;
+
+ tps->desc.name = client->name;
+ tps->desc.id = 0;
+ tps->desc.ops = &tps62360_dcdc_ops;
+ tps->desc.type = REGULATOR_VOLTAGE;
+ tps->desc.owner = THIS_MODULE;
+ tps->desc.uV_step = 10000;
+
if (client->dev.of_node) {
const struct of_device_id *match;
match = of_match_device(of_match_ptr(tps62360_of_match),
}
chip_id = (int)(long)match->data;
if (!pdata)
- pdata = of_get_tps62360_platform_data(&client->dev);
+ pdata = of_get_tps62360_platform_data(&client->dev,
+ &tps->desc);
} else if (id) {
chip_id = id->driver_data;
} else {
return -EIO;
}
- tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL);
- if (!tps)
- return -ENOMEM;
-
tps->en_discharge = pdata->en_discharge;
tps->en_internal_pulldn = pdata->en_internal_pulldn;
tps->vsel0_gpio = pdata->vsel0_gpio;
return -ENODEV;
}
- tps->desc.name = client->name;
- tps->desc.id = 0;
- tps->desc.ops = &tps62360_dcdc_ops;
- tps->desc.type = REGULATOR_VOLTAGE;
- tps->desc.owner = THIS_MODULE;
- tps->desc.uV_step = 10000;
-
tps->regmap = devm_regmap_init_i2c(client, &tps62360_regmap_config);
if (IS_ERR(tps->regmap)) {
ret = PTR_ERR(tps->regmap);
gpio_flag = GPIOF_OUT_INIT_HIGH;
config->ena_gpio = tps_pdata->gpio;
+ config->ena_gpio_initialized = true;
config->ena_gpio_flags = gpio_flag;
+ } else {
+ config->ena_gpio = -EINVAL;
+ config->ena_gpio_initialized = false;
}
}
template = match->data;
id = template->id;
- init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node);
+ init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node,
+ ®ulators[id]);
platform_set_drvdata(pdev, tps);
template = match->data;
id = template->desc.id;
initdata = of_get_regulator_init_data(&pdev->dev,
- pdev->dev.of_node);
+ pdev->dev.of_node,
+ &template->desc);
drvdata = NULL;
} else {
id = pdev->id;
reg->desc.owner = THIS_MODULE;
reg->desc.continuous_voltage_range = true;
- init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node);
+ init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node,
+ ®->desc);
if (!init_data)
return -EINVAL;
config.driver_data = ldo;
config.regmap = wm8994->regmap;
config.init_data = &ldo->init_data;
- if (pdata)
+ if (pdata) {
config.ena_gpio = pdata->ldo[id].enable;
- else if (wm8994->dev->of_node)
+ } else if (wm8994->dev->of_node) {
config.ena_gpio = wm8994->pdata.ldo[id].enable;
+ config.ena_gpio_initialized = true;
+ }
/* Use default constraints if none set up */
if (!pdata || !pdata->ldo[id].init_data || wm8994->dev->of_node) {
struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev);
int i;
struct virtqueue *vq;
- struct virtio_driver *drv;
if (!vcdev)
return;
adapter->ccw_device = ccw_device;
INIT_WORK(&adapter->stat_work, _zfcp_status_read_scheduler);
- INIT_WORK(&adapter->scan_work, zfcp_fc_scan_ports);
+ INIT_DELAYED_WORK(&adapter->scan_work, zfcp_fc_scan_ports);
INIT_WORK(&adapter->ns_up_work, zfcp_fc_sym_name_update);
+ adapter->next_port_scan = jiffies;
+
if (zfcp_qdio_setup(adapter))
goto failed;
{
struct ccw_device *cdev = adapter->ccw_device;
- cancel_work_sync(&adapter->scan_work);
+ cancel_delayed_work_sync(&adapter->scan_work);
cancel_work_sync(&adapter->stat_work);
cancel_work_sync(&adapter->ns_up_work);
zfcp_destroy_adapter_work_queue(adapter);
zfcp_erp_set_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING);
zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED,
tag);
+
+ /*
+ * We want to scan ports here, with some random backoff and without
+ * rate limit. Recovery has already scheduled a port scan for us,
+ * but with both random delay and rate limit. Nevertheless we get
+ * what we want here by flushing the scheduled work after sleeping
+ * an equivalent random time.
+ * Let the port scan random delay elapse first. If recovery finishes
+ * up to that point in time, that would be perfect for both recovery
+ * and port scan. If not, i.e. recovery takes ages, there was no
+ * point in waiting a random delay on top of the time consumed by
+ * recovery.
+ */
+ msleep(zfcp_fc_port_scan_backoff());
zfcp_erp_wait(adapter);
- flush_work(&adapter->scan_work); /* ok to call even if nothing queued */
+ flush_delayed_work(&adapter->scan_work);
zfcp_ccw_adapter_put(adapter);
adapter->req_no = 0;
zfcp_ccw_activate(cdev, 0, "ccsonl1");
- /* scan for remote ports
- either at the end of any successful adapter recovery
- or only after the adapter recovery for setting a device online */
+
+ /*
+ * We want to scan ports here, always, with some random delay and
+ * without rate limit - basically what zfcp_ccw_activate() has
+ * achieved for us. Not quite! That port scan depended on
+ * !no_auto_port_rescan. So let's cover the no_auto_port_rescan
+ * case here to make sure a port scan is done unconditionally.
+ * Since zfcp_ccw_activate() has waited the desired random time,
+ * we can immediately schedule and flush a port scan for the
+ * remaining cases.
+ */
zfcp_fc_inverse_conditional_port_scan(adapter);
- flush_work(&adapter->scan_work); /* ok to call even if nothing queued */
+ flush_delayed_work(&adapter->scan_work);
zfcp_ccw_adapter_put(adapter);
return 0;
}
struct fc_host_statistics *fc_stats;
struct fsf_qtcb_bottom_port *stats_reset_data;
unsigned long stats_reset;
- struct work_struct scan_work;
+ struct delayed_work scan_work;
struct work_struct ns_up_work;
struct service_level service_level;
struct workqueue_struct *work_queue;
struct device_dma_parameters dma_parms;
struct zfcp_fc_events events;
+ unsigned long next_port_scan;
};
struct zfcp_port {
return ZFCP_ERP_CONTINUES;
}
-static void zfcp_erp_port_strategy_clearstati(struct zfcp_port *port)
-{
- atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED, &port->status);
-}
-
static int zfcp_erp_port_forced_strategy(struct zfcp_erp_action *erp_action)
{
struct zfcp_port *port = erp_action->port;
switch (erp_action->step) {
case ZFCP_ERP_STEP_UNINITIALIZED:
- zfcp_erp_port_strategy_clearstati(port);
if ((status & ZFCP_STATUS_PORT_PHYS_OPEN) &&
(status & ZFCP_STATUS_COMMON_OPEN))
return zfcp_erp_port_forced_strategy_close(erp_action);
switch (erp_action->step) {
case ZFCP_ERP_STEP_UNINITIALIZED:
- zfcp_erp_port_strategy_clearstati(port);
if (p_status & ZFCP_STATUS_COMMON_OPEN)
return zfcp_erp_port_strategy_close(erp_action);
break;
extern int zfcp_fc_exec_bsg_job(struct fc_bsg_job *);
extern int zfcp_fc_timeout_bsg_job(struct fc_bsg_job *);
extern void zfcp_fc_sym_name_update(struct work_struct *);
+extern unsigned int zfcp_fc_port_scan_backoff(void);
extern void zfcp_fc_conditional_port_scan(struct zfcp_adapter *);
extern void zfcp_fc_inverse_conditional_port_scan(struct zfcp_adapter *);
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/utsname.h>
+#include <linux/random.h>
#include <scsi/fc/fc_els.h>
#include <scsi/libfc.h>
#include "zfcp_ext.h"
MODULE_PARM_DESC(no_auto_port_rescan,
"no automatic port_rescan (default off)");
+static unsigned int port_scan_backoff = 500;
+module_param(port_scan_backoff, uint, 0600);
+MODULE_PARM_DESC(port_scan_backoff,
+ "upper limit of port scan random backoff in msecs (default 500)");
+
+static unsigned int port_scan_ratelimit = 60000;
+module_param(port_scan_ratelimit, uint, 0600);
+MODULE_PARM_DESC(port_scan_ratelimit,
+ "minimum interval between port scans in msecs (default 60000)");
+
+unsigned int zfcp_fc_port_scan_backoff(void)
+{
+ if (!port_scan_backoff)
+ return 0;
+ return get_random_int() % port_scan_backoff;
+}
+
+static void zfcp_fc_port_scan_time(struct zfcp_adapter *adapter)
+{
+ unsigned long interval = msecs_to_jiffies(port_scan_ratelimit);
+ unsigned long backoff = msecs_to_jiffies(zfcp_fc_port_scan_backoff());
+
+ adapter->next_port_scan = jiffies + interval + backoff;
+}
+
+static void zfcp_fc_port_scan(struct zfcp_adapter *adapter)
+{
+ unsigned long now = jiffies;
+ unsigned long next = adapter->next_port_scan;
+ unsigned long delay = 0, max;
+
+ /* delay only needed within waiting period */
+ if (time_before(now, next)) {
+ delay = next - now;
+ /* paranoia: never ever delay scans longer than specified */
+ max = msecs_to_jiffies(port_scan_ratelimit + port_scan_backoff);
+ delay = min(delay, max);
+ }
+
+ queue_delayed_work(adapter->work_queue, &adapter->scan_work, delay);
+}
+
void zfcp_fc_conditional_port_scan(struct zfcp_adapter *adapter)
{
if (no_auto_port_rescan)
return;
- queue_work(adapter->work_queue, &adapter->scan_work);
+ zfcp_fc_port_scan(adapter);
}
void zfcp_fc_inverse_conditional_port_scan(struct zfcp_adapter *adapter)
if (!no_auto_port_rescan)
return;
- queue_work(adapter->work_queue, &adapter->scan_work);
+ zfcp_fc_port_scan(adapter);
}
/**
*/
void zfcp_fc_scan_ports(struct work_struct *work)
{
- struct zfcp_adapter *adapter = container_of(work, struct zfcp_adapter,
+ struct delayed_work *dw = to_delayed_work(work);
+ struct zfcp_adapter *adapter = container_of(dw, struct zfcp_adapter,
scan_work);
int ret, i;
struct zfcp_fc_req *fc_req;
int chain, max_entries, buf_num, max_bytes;
+ zfcp_fc_port_scan_time(adapter);
+
chain = adapter->adapter_features & FSF_FEATURE_ELS_CT_CHAINED_SBALS;
buf_num = chain ? ZFCP_FC_GPN_FT_NUM_BUFS : 1;
max_entries = chain ? ZFCP_FC_GPN_FT_MAX_ENT : ZFCP_FC_GPN_FT_ENT_PAGE;
void zfcp_fc_scsi_to_fcp(struct fcp_cmnd *fcp, struct scsi_cmnd *scsi,
u8 tm_flags)
{
- char tag[2];
-
int_to_scsilun(scsi->device->lun, (struct scsi_lun *) &fcp->fc_lun);
if (unlikely(tm_flags)) {
return;
}
- if (scsi_populate_tag_msg(scsi, tag)) {
- switch (tag[0]) {
- case MSG_ORDERED_TAG:
- fcp->fc_pri_ta |= FCP_PTA_ORDERED;
- break;
- case MSG_SIMPLE_TAG:
- fcp->fc_pri_ta |= FCP_PTA_SIMPLE;
- break;
- };
- } else
- fcp->fc_pri_ta = FCP_PTA_SIMPLE;
+ fcp->fc_pri_ta = FCP_PTA_SIMPLE;
if (scsi->sc_data_direction == DMA_FROM_DEVICE)
fcp->fc_flags |= FCP_CFL_RDDATA;
port->handle = header->port_handle;
atomic_set_mask(ZFCP_STATUS_COMMON_OPEN |
ZFCP_STATUS_PORT_PHYS_OPEN, &port->status);
- atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_DENIED |
- ZFCP_STATUS_COMMON_ACCESS_BOXED,
+ atomic_clear_mask(ZFCP_STATUS_COMMON_ACCESS_BOXED,
&port->status);
/* check whether D_ID has changed during open */
/*
module_param(allow_lun_scan, bool, 0600);
MODULE_PARM_DESC(allow_lun_scan, "For NPIV, scan and attach all storage LUNs");
-static int zfcp_scsi_change_queue_depth(struct scsi_device *sdev, int depth,
- int reason)
-{
- switch (reason) {
- case SCSI_QDEPTH_DEFAULT:
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
- break;
- case SCSI_QDEPTH_QFULL:
- scsi_track_queue_full(sdev, depth);
- break;
- case SCSI_QDEPTH_RAMP_UP:
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
- break;
- default:
- return -EOPNOTSUPP;
- }
- return sdev->queue_depth;
-}
-
static void zfcp_scsi_slave_destroy(struct scsi_device *sdev)
{
struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev);
static int zfcp_scsi_slave_configure(struct scsi_device *sdp)
{
if (sdp->tagged_supported)
- scsi_adjust_queue_depth(sdp, MSG_SIMPLE_TAG, default_depth);
- else
- scsi_adjust_queue_depth(sdp, 0, 1);
+ scsi_change_queue_depth(sdp, default_depth);
return 0;
}
.slave_alloc = zfcp_scsi_slave_alloc,
.slave_configure = zfcp_scsi_slave_configure,
.slave_destroy = zfcp_scsi_slave_destroy,
- .change_queue_depth = zfcp_scsi_change_queue_depth,
+ .change_queue_depth = scsi_change_queue_depth,
.proc_name = "zfcp",
.can_queue = 4096,
.this_id = -1,
.use_clustering = 1,
.shost_attrs = zfcp_sysfs_shost_attrs,
.sdev_attrs = zfcp_sysfs_sdev_attrs,
+ .track_queue_depth = 1,
};
/**
ZFCP_DEFINE_ATTR(zfcp_port, port, in_recovery, "%d\n",
(atomic_read(&port->status) &
ZFCP_STATUS_COMMON_ERP_INUSE) != 0);
-ZFCP_DEFINE_ATTR(zfcp_port, port, access_denied, "%d\n",
- (atomic_read(&port->status) &
- ZFCP_STATUS_COMMON_ACCESS_DENIED) != 0);
+ZFCP_DEFINE_ATTR_CONST(port, access_denied, "%d\n", 0);
ZFCP_DEFINE_ATTR(zfcp_unit, unit, status, "0x%08x\n",
zfcp_unit_sdev_status(unit));
if (!adapter)
return -ENODEV;
- /* sync the user-space- with the kernel-invocation of scan_work */
- queue_work(adapter->work_queue, &adapter->scan_work);
- flush_work(&adapter->scan_work);
+ /*
+ * Users wish is our command: immediately schedule and flush a
+ * worker to conduct a synchronous port scan, that is, neither
+ * a random delay nor a rate limit is applied here.
+ */
+ queue_delayed_work(adapter->work_queue, &adapter->scan_work, 0);
+ flush_delayed_work(&adapter->scan_work);
zfcp_ccw_adapter_put(adapter);
return (ssize_t) count;
{ \
struct scsi_device *sdev = to_scsi_device(dev); \
struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); \
- struct zfcp_port *port = zfcp_sdev->port; \
\
return sprintf(buf, _format, _value); \
} \
static DEVICE_ATTR(_name, S_IRUGO, zfcp_sysfs_scsi_##_name##_show, NULL);
ZFCP_DEFINE_SCSI_ATTR(hba_id, "%s\n",
- dev_name(&port->adapter->ccw_device->dev));
+ dev_name(&zfcp_sdev->port->adapter->ccw_device->dev));
ZFCP_DEFINE_SCSI_ATTR(wwpn, "0x%016llx\n",
- (unsigned long long) port->wwpn);
+ (unsigned long long) zfcp_sdev->port->wwpn);
static ssize_t zfcp_sysfs_scsi_fcp_lun_show(struct device *dev,
struct device_attribute *attr,
}
static DEVICE_ATTR(fcp_lun, S_IRUGO, zfcp_sysfs_scsi_fcp_lun_show, NULL);
+ZFCP_DEFINE_SCSI_ATTR(zfcp_access_denied, "%d\n",
+ (atomic_read(&zfcp_sdev->status) &
+ ZFCP_STATUS_COMMON_ACCESS_DENIED) != 0);
+
+static ssize_t zfcp_sysfs_scsi_zfcp_failed_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ unsigned int status = atomic_read(&sdev_to_zfcp(sdev)->status);
+ unsigned int failed = status & ZFCP_STATUS_COMMON_ERP_FAILED ? 1 : 0;
+
+ return sprintf(buf, "%d\n", failed);
+}
+
+static ssize_t zfcp_sysfs_scsi_zfcp_failed_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ unsigned long val;
+
+ if (kstrtoul(buf, 0, &val) || val != 0)
+ return -EINVAL;
+
+ zfcp_erp_set_lun_status(sdev, ZFCP_STATUS_COMMON_RUNNING);
+ zfcp_erp_lun_reopen(sdev, ZFCP_STATUS_COMMON_ERP_FAILED,
+ "syufai3");
+ zfcp_erp_wait(sdev_to_zfcp(sdev)->port->adapter);
+
+ return count;
+}
+static DEVICE_ATTR(zfcp_failed, S_IWUSR | S_IRUGO,
+ zfcp_sysfs_scsi_zfcp_failed_show,
+ zfcp_sysfs_scsi_zfcp_failed_store);
+
+ZFCP_DEFINE_SCSI_ATTR(zfcp_in_recovery, "%d\n",
+ (atomic_read(&zfcp_sdev->status) &
+ ZFCP_STATUS_COMMON_ERP_INUSE) != 0);
+
+ZFCP_DEFINE_SCSI_ATTR(zfcp_status, "0x%08x\n",
+ atomic_read(&zfcp_sdev->status));
+
struct device_attribute *zfcp_sysfs_sdev_attrs[] = {
&dev_attr_fcp_lun,
&dev_attr_wwpn,
&dev_attr_read_latency,
&dev_attr_write_latency,
&dev_attr_cmd_latency,
+ &dev_attr_zfcp_access_denied,
+ &dev_attr_zfcp_failed,
+ &dev_attr_zfcp_in_recovery,
+ &dev_attr_zfcp_status,
NULL
};
return len;
} /* End twa_show_stats() */
-/* This function will set a devices queue depth */
-static int twa_change_queue_depth(struct scsi_device *sdev, int queue_depth,
- int reason)
-{
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
-
- if (queue_depth > TW_Q_LENGTH-2)
- queue_depth = TW_Q_LENGTH-2;
- scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
- return queue_depth;
-} /* End twa_change_queue_depth() */
-
/* Create sysfs 'stats' entry */
static struct device_attribute twa_host_stats_attr = {
.attr = {
.queuecommand = twa_scsi_queue,
.eh_host_reset_handler = twa_scsi_eh_reset,
.bios_param = twa_scsi_biosparam,
- .change_queue_depth = twa_change_queue_depth,
+ .change_queue_depth = scsi_change_queue_depth,
.can_queue = TW_Q_LENGTH-2,
.slave_configure = twa_slave_configure,
.this_id = -1,
return len;
} /* End twl_show_stats() */
-/* This function will set a devices queue depth */
-static int twl_change_queue_depth(struct scsi_device *sdev, int queue_depth,
- int reason)
-{
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
-
- if (queue_depth > TW_Q_LENGTH-2)
- queue_depth = TW_Q_LENGTH-2;
- scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
- return queue_depth;
-} /* End twl_change_queue_depth() */
-
/* stats sysfs attribute initializer */
static struct device_attribute twl_host_stats_attr = {
.attr = {
.queuecommand = twl_scsi_queue,
.eh_host_reset_handler = twl_scsi_eh_reset,
.bios_param = twl_scsi_biosparam,
- .change_queue_depth = twl_change_queue_depth,
+ .change_queue_depth = scsi_change_queue_depth,
.can_queue = TW_Q_LENGTH-2,
.slave_configure = twl_slave_configure,
.this_id = -1,
return len;
} /* End tw_show_stats() */
-/* This function will set a devices queue depth */
-static int tw_change_queue_depth(struct scsi_device *sdev, int queue_depth,
- int reason)
-{
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
-
- if (queue_depth > TW_Q_LENGTH-2)
- queue_depth = TW_Q_LENGTH-2;
- scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
- return queue_depth;
-} /* End tw_change_queue_depth() */
-
/* Create sysfs 'stats' entry */
static struct device_attribute tw_host_stats_attr = {
.attr = {
.queuecommand = tw_scsi_queue,
.eh_host_reset_handler = tw_scsi_eh_reset,
.bios_param = tw_scsi_biosparam,
- .change_queue_depth = tw_change_queue_depth,
+ .change_queue_depth = scsi_change_queue_depth,
.can_queue = TW_Q_LENGTH-2,
.slave_configure = tw_slave_configure,
.this_id = -1,
STATIC int NCR_700_slave_alloc(struct scsi_device *SDpnt);
STATIC int NCR_700_slave_configure(struct scsi_device *SDpnt);
STATIC void NCR_700_slave_destroy(struct scsi_device *SDpnt);
-static int NCR_700_change_queue_depth(struct scsi_device *SDpnt, int depth, int reason);
+static int NCR_700_change_queue_depth(struct scsi_device *SDpnt, int depth);
static int NCR_700_change_queue_type(struct scsi_device *SDpnt, int depth);
STATIC struct device_attribute *NCR_700_dev_attrs[];
tpnt->slave_alloc = NCR_700_slave_alloc;
tpnt->change_queue_depth = NCR_700_change_queue_depth;
tpnt->change_queue_type = NCR_700_change_queue_type;
+ tpnt->use_blk_tags = 1;
if(tpnt->name == NULL)
tpnt->name = "53c700";
hostdata->cmd = NULL;
if(SCp != NULL) {
- struct NCR_700_command_slot *slot =
+ struct NCR_700_command_slot *slot =
(struct NCR_700_command_slot *)SCp->host_scribble;
-
+
dma_unmap_single(hostdata->dev, slot->pCmd,
MAX_COMMAND_SIZE, DMA_TO_DEVICE);
if (slot->flags == NCR_700_FLAG_AUTOSENSE) {
char *cmnd = NCR_700_get_sense_cmnd(SCp->device);
-#ifdef NCR_700_DEBUG
- printk(" ORIGINAL CMD %p RETURNED %d, new return is %d sense is\n",
- SCp, SCp->cmnd[7], result);
- scsi_print_sense("53c700", SCp);
-#endif
dma_unmap_single(hostdata->dev, slot->dma_handle,
SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
/* restore the old result if the request sense was
/* we're done negotiating */
NCR_700_set_tag_neg_state(SCp->device, NCR_700_FINISHED_TAG_NEGOTIATION);
hostdata->tag_negotiated &= ~(1<<scmd_id(SCp));
+
SCp->device->tagged_supported = 0;
- scsi_deactivate_tcq(SCp->device, host->cmd_per_lun);
+ scsi_change_queue_depth(SCp->device, host->cmd_per_lun);
+ scsi_set_tag_type(SCp->device, 0);
} else {
shost_printk(KERN_WARNING, host,
"(%d:%d) Unexpected REJECT Message %s\n",
if((hostdata->tag_negotiated & (1<<scmd_id(SCp)))
&& (slot->tag != SCSI_NO_TAG && SCp->cmnd[0] != REQUEST_SENSE &&
slot->flags != NCR_700_FLAG_AUTOSENSE)) {
- count += scsi_populate_tag_msg(SCp, &hostdata->msgout[count]);
+ count += spi_populate_tag_msg(&hostdata->msgout[count], SCp);
}
if(hostdata->fast &&
*/
if(NCR_700_get_depth(SCp->device) != 0
&& (!(hostdata->tag_negotiated & (1<<scmd_id(SCp)))
- || !blk_rq_tagged(SCp->request))) {
+ || !(SCp->flags & SCMD_TAGGED))) {
CDEBUG(KERN_ERR, SCp, "has non zero depth %d\n",
NCR_700_get_depth(SCp->device));
return SCSI_MLQUEUE_DEVICE_BUSY;
printk("53c700: scsi%d, command ", SCp->device->host->host_no);
scsi_print_command(SCp);
#endif
- if(blk_rq_tagged(SCp->request)
+ if ((SCp->flags & SCMD_TAGGED)
&& (hostdata->tag_negotiated &(1<<scmd_id(SCp))) == 0
&& NCR_700_get_tag_neg_state(SCp->device) == NCR_700_START_TAG_NEGOTIATION) {
scmd_printk(KERN_ERR, SCp, "Enabling Tag Command Queuing\n");
*
* FIXME: This will royally screw up on multiple LUN devices
* */
- if(!blk_rq_tagged(SCp->request)
+ if (!(SCp->flags & SCMD_TAGGED)
&& (hostdata->tag_negotiated &(1<<scmd_id(SCp)))) {
scmd_printk(KERN_INFO, SCp, "Disabling Tag Command Queuing\n");
hostdata->tag_negotiated &= ~(1<<scmd_id(SCp));
{
struct NCR_700_command_slot *slot;
- scmd_printk(KERN_INFO, SCp,
- "New error handler wants to abort command\n\t");
- scsi_print_command(SCp);
+ scmd_printk(KERN_INFO, SCp, "abort command\n");
slot = (struct NCR_700_command_slot *)SCp->host_scribble;
/* to do here: allocate memory; build a queue_full list */
if(SDp->tagged_supported) {
- scsi_set_tag_type(SDp, MSG_ORDERED_TAG);
- scsi_activate_tcq(SDp, NCR_700_DEFAULT_TAGS);
+ scsi_change_queue_depth(SDp, NCR_700_DEFAULT_TAGS);
NCR_700_set_tag_neg_state(SDp, NCR_700_START_TAG_NEGOTIATION);
- } else {
- /* initialise to default depth */
- scsi_adjust_queue_depth(SDp, 0, SDp->host->cmd_per_lun);
}
+
if(hostdata->fast) {
/* Find the correct offset and period via domain validation */
if (!spi_initial_dv(SDp->sdev_target))
}
static int
-NCR_700_change_queue_depth(struct scsi_device *SDp, int depth, int reason)
+NCR_700_change_queue_depth(struct scsi_device *SDp, int depth)
{
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
-
if (depth > NCR_700_MAX_TAGS)
depth = NCR_700_MAX_TAGS;
-
- scsi_adjust_queue_depth(SDp, scsi_get_tag_type(SDp), depth);
- return depth;
+ return scsi_change_queue_depth(SDp, depth);
}
static int NCR_700_change_queue_type(struct scsi_device *SDp, int tag_type)
struct NCR_700_Host_Parameters *hostdata =
(struct NCR_700_Host_Parameters *)SDp->host->hostdata[0];
- scsi_set_tag_type(SDp, tag_type);
-
/* We have a global (per target) flag to track whether TCQ is
* enabled, so we'll be turning it off for the entire target here.
* our tag algorithm will fail if we mix tagged and untagged commands,
if (change_tag)
scsi_target_quiesce(SDp->sdev_target);
+ scsi_set_tag_type(SDp, tag_type);
if (!tag_type) {
/* shift back to the default unqueued number of commands
* (the user can still raise this) */
- scsi_deactivate_tcq(SDp, SDp->host->cmd_per_lun);
+ scsi_change_queue_depth(SDp, SDp->host->cmd_per_lun);
hostdata->tag_negotiated &= ~(1 << sdev_id(SDp));
} else {
/* Here, we cleared the negotiation flag above, so this
* will force the driver to renegotiate */
- scsi_activate_tcq(SDp, SDp->queue_depth);
+ scsi_change_queue_depth(SDp, SDp->queue_depth);
if (change_tag)
NCR_700_set_tag_neg_state(SDp, NCR_700_START_TAG_NEGOTIATION);
}
if (qdepth == 0)
qdepth = BLOGIC_MAX_AUTO_TAG_DEPTH;
adapter->qdepth[tgt_id] = qdepth;
- scsi_adjust_queue_depth(dev, MSG_SIMPLE_TAG, qdepth);
+ scsi_change_queue_depth(dev, qdepth);
} else {
adapter->tagq_ok &= ~(1 << tgt_id);
qdepth = adapter->untag_qdepth;
adapter->qdepth[tgt_id] = qdepth;
- scsi_adjust_queue_depth(dev, 0, qdepth);
+ scsi_change_queue_depth(dev, qdepth);
}
qdepth = 0;
for (tgt_id = 0; tgt_id < adapter->maxdev; tgt_id++)
To compile this driver as a module, choose M here: the
module will be called dc395x.
-config SCSI_DC390T
- tristate "Tekram DC390(T) and Am53/79C974 SCSI support"
+config SCSI_AM53C974
+ tristate "Tekram DC390(T) and Am53/79C974 SCSI support (new driver)"
depends on PCI && SCSI
+ select SCSI_SPI_ATTRS
---help---
This driver supports PCI SCSI host adapters based on the Am53C974A
chip, e.g. Tekram DC390(T), DawiControl 2974 and some onboard
PCscsi/PCnet (Am53/79C974) solutions.
+ This is a new implementation base on the generic esp_scsi driver.
Documentation can be found in <file:Documentation/scsi/tmscsim.txt>.
based on NCR/Symbios chips. Use "NCR53C8XX SCSI support" for those.
To compile this driver as a module, choose M here: the
- module will be called tmscsim.
+ module will be called am53c974.
config SCSI_T128
tristate "Trantor T128/T128F/T228 SCSI support"
To compile this driver as a module, choose M here: the
module will be called nsp32.
+config SCSI_WD719X
+ tristate "Western Digital WD7193/7197/7296 support"
+ depends on PCI && SCSI
+ select EEPROM_93CX6
+ ---help---
+ This is a driver for Western Digital WD7193, WD7197 and WD7296 PCI
+ SCSI controllers (based on WD33C296A chip).
+
config SCSI_DEBUG
tristate "SCSI debugging host simulator"
depends on SCSI
that leave the devices with SCSI operations partway completed.
config MAC_SCSI
- bool "Macintosh NCR5380 SCSI"
+ tristate "Macintosh NCR5380 SCSI"
depends on MAC && SCSI=y
select SCSI_SPI_ATTRS
help
obj-$(CONFIG_SCSI_7000FASST) += wd7000.o
obj-$(CONFIG_SCSI_EATA) += eata.o
obj-$(CONFIG_SCSI_DC395x) += dc395x.o
-obj-$(CONFIG_SCSI_DC390T) += tmscsim.o
+obj-$(CONFIG_SCSI_AM53C974) += esp_scsi.o am53c974.o
obj-$(CONFIG_MEGARAID_LEGACY) += megaraid.o
obj-$(CONFIG_MEGARAID_NEWGEN) += megaraid/
obj-$(CONFIG_MEGARAID_SAS) += megaraid/
obj-$(CONFIG_VMWARE_PVSCSI) += vmw_pvscsi.o
obj-$(CONFIG_XEN_SCSI_FRONTEND) += xen-scsifront.o
obj-$(CONFIG_HYPERV_STORAGE) += hv_storvsc.o
+obj-$(CONFIG_SCSI_WD719X) += wd719x.o
obj-$(CONFIG_ARM) += arm/
* drew@colorado.edu
* +1 (303) 666-5836
*
- * DISTRIBUTION RELEASE 6.
- *
* For more information, please consult
*
* NCR 5380 Family
* Set up the internal fields in the SCSI command.
*/
-static __inline__ void initialize_SCp(Scsi_Cmnd * cmd)
+static inline void initialize_SCp(struct scsi_cmnd *cmd)
{
/*
* Initialize the Scsi Pointer field so that all of the commands in the
int trying_irqs, i, mask;
NCR5380_setup(instance);
- for (trying_irqs = i = 0, mask = 1; i < 16; ++i, mask <<= 1)
+ for (trying_irqs = 0, i = 1, mask = 2; i < 16; ++i, mask <<= 1)
if ((mask & possible) && (request_irq(i, &probe_intr, 0, "NCR-probe", NULL) == 0))
trying_irqs |= mask;
timeout = jiffies + (250 * HZ / 1000);
- probe_irq = SCSI_IRQ_NONE;
+ probe_irq = NO_IRQ;
/*
* A interrupt is triggered whenever BSY = false, SEL = true
NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask);
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA | ICR_ASSERT_SEL);
- while (probe_irq == SCSI_IRQ_NONE && time_before(jiffies, timeout))
+ while (probe_irq == NO_IRQ && time_before(jiffies, timeout))
schedule_timeout_uninterruptible(1);
NCR5380_write(SELECT_ENABLE_REG, 0);
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- for (i = 0, mask = 1; i < 16; ++i, mask <<= 1)
+ for (i = 1, mask = 2; i < 16; ++i, mask <<= 1)
if (trying_irqs & mask)
free_irq(i, NULL);
}
/**
- * NCR58380_print_options - show options
- * @instance: unused for now
+ * NCR58380_info - report driver and host information
+ * @instance: relevant scsi host instance
*
- * Called by probe code indicating the NCR5380 driver options that
- * were selected. At some point this will switch to runtime options
- * read from the adapter in question
+ * For use as the host template info() handler.
*
* Locks: none
*/
-static void __init __maybe_unused
-NCR5380_print_options(struct Scsi_Host *instance)
+static const char *NCR5380_info(struct Scsi_Host *instance)
{
- printk(" generic options"
-#ifdef AUTOPROBE_IRQ
- " AUTOPROBE_IRQ"
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+
+ return hostdata->info;
+}
+
+static void prepare_info(struct Scsi_Host *instance)
+{
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+
+ snprintf(hostdata->info, sizeof(hostdata->info),
+ "%s, io_port 0x%lx, n_io_port %d, "
+ "base 0x%lx, irq %d, "
+ "can_queue %d, cmd_per_lun %d, "
+ "sg_tablesize %d, this_id %d, "
+ "flags { %s%s%s}, "
+#if defined(USLEEP_POLL) && defined(USLEEP_WAITLONG)
+ "USLEEP_POLL %d, USLEEP_WAITLONG %d, "
#endif
-#ifdef AUTOSENSE
- " AUTOSENSE"
+ "options { %s} ",
+ instance->hostt->name, instance->io_port, instance->n_io_port,
+ instance->base, instance->irq,
+ instance->can_queue, instance->cmd_per_lun,
+ instance->sg_tablesize, instance->this_id,
+ hostdata->flags & FLAG_NCR53C400 ? "NCR53C400 " : "",
+ hostdata->flags & FLAG_DTC3181E ? "DTC3181E " : "",
+ hostdata->flags & FLAG_NO_PSEUDO_DMA ? "NO_PSEUDO_DMA " : "",
+#if defined(USLEEP_POLL) && defined(USLEEP_WAITLONG)
+ USLEEP_POLL, USLEEP_WAITLONG,
+#endif
+#ifdef AUTOPROBE_IRQ
+ "AUTOPROBE_IRQ "
#endif
#ifdef DIFFERENTIAL
- " DIFFERENTIAL"
+ "DIFFERENTIAL "
#endif
#ifdef REAL_DMA
- " REAL DMA"
+ "REAL_DMA "
#endif
#ifdef REAL_DMA_POLL
- " REAL DMA POLL"
+ "REAL_DMA_POLL "
#endif
#ifdef PARITY
- " PARITY"
+ "PARITY "
#endif
#ifdef PSEUDO_DMA
- " PSEUDO DMA"
+ "PSEUDO_DMA "
#endif
#ifdef UNSAFE
- " UNSAFE "
+ "UNSAFE "
#endif
- );
- printk(" USLEEP, USLEEP_POLL=%d USLEEP_SLEEP=%d", USLEEP_POLL, USLEEP_SLEEP);
- printk(" generic release=%d", NCR5380_PUBLIC_RELEASE);
- if (((struct NCR5380_hostdata *) instance->hostdata)->flags & FLAG_NCR53C400) {
- printk(" ncr53c400 release=%d", NCR53C400_PUBLIC_RELEASE);
- }
+#ifdef NCR53C400
+ "NCR53C400 "
+#endif
+ "");
}
/**
NCR5380_dprint_phase(NDEBUG_ANY, instance);
}
+#ifdef PSEUDO_DMA
/******************************************/
/*
* /proc/scsi/[dtc pas16 t128 generic]/[0-ASC_NUM_BOARD_SUPPORTED]
static int __maybe_unused NCR5380_write_info(struct Scsi_Host *instance,
char *buffer, int length)
{
-#ifdef DTC_PUBLIC_RELEASE
- dtc_wmaxi = dtc_maxi = 0;
-#endif
-#ifdef PAS16_PUBLIC_RELEASE
- pas_wmaxi = pas_maxi = 0;
-#endif
- return (-ENOSYS); /* Currently this is a no-op */
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+
+ hostdata->spin_max_r = 0;
+ hostdata->spin_max_w = 0;
+ return 0;
}
+#endif
#undef SPRINTF
#define SPRINTF(args...) seq_printf(m, ## args)
static
-void lprint_Scsi_Cmnd(Scsi_Cmnd * cmd, struct seq_file *m);
+void lprint_Scsi_Cmnd(struct scsi_cmnd *cmd, struct seq_file *m);
static
void lprint_command(unsigned char *cmd, struct seq_file *m);
static
struct Scsi_Host *instance)
{
struct NCR5380_hostdata *hostdata;
- Scsi_Cmnd *ptr;
+ struct scsi_cmnd *ptr;
hostdata = (struct NCR5380_hostdata *) instance->hostdata;
- SPRINTF("NCR5380 core release=%d. ", NCR5380_PUBLIC_RELEASE);
- if (((struct NCR5380_hostdata *) instance->hostdata)->flags & FLAG_NCR53C400)
- SPRINTF("ncr53c400 release=%d. ", NCR53C400_PUBLIC_RELEASE);
-#ifdef DTC_PUBLIC_RELEASE
- SPRINTF("DTC 3180/3280 release %d", DTC_PUBLIC_RELEASE);
-#endif
-#ifdef T128_PUBLIC_RELEASE
- SPRINTF("T128 release %d", T128_PUBLIC_RELEASE);
-#endif
-#ifdef GENERIC_NCR5380_PUBLIC_RELEASE
- SPRINTF("Generic5380 release %d", GENERIC_NCR5380_PUBLIC_RELEASE);
-#endif
-#ifdef PAS16_PUBLIC_RELEASE
- SPRINTF("PAS16 release=%d", PAS16_PUBLIC_RELEASE);
-#endif
-
- SPRINTF("\nBase Addr: 0x%05lX ", (long) instance->base);
- SPRINTF("io_port: %04x ", (int) instance->io_port);
- if (instance->irq == SCSI_IRQ_NONE)
- SPRINTF("IRQ: None.\n");
- else
- SPRINTF("IRQ: %d.\n", instance->irq);
-
-#ifdef DTC_PUBLIC_RELEASE
- SPRINTF("Highwater I/O busy_spin_counts -- write: %d read: %d\n", dtc_wmaxi, dtc_maxi);
-#endif
-#ifdef PAS16_PUBLIC_RELEASE
- SPRINTF("Highwater I/O busy_spin_counts -- write: %d read: %d\n", pas_wmaxi, pas_maxi);
+#ifdef PSEUDO_DMA
+ SPRINTF("Highwater I/O busy spin counts: write %d, read %d\n",
+ hostdata->spin_max_w, hostdata->spin_max_r);
#endif
spin_lock_irq(instance->host_lock);
if (!hostdata->connected)
SPRINTF("scsi%d: no currently connected command\n", instance->host_no);
else
- lprint_Scsi_Cmnd((Scsi_Cmnd *) hostdata->connected, m);
+ lprint_Scsi_Cmnd((struct scsi_cmnd *) hostdata->connected, m);
SPRINTF("scsi%d: issue_queue\n", instance->host_no);
- for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ for (ptr = (struct scsi_cmnd *) hostdata->issue_queue; ptr; ptr = (struct scsi_cmnd *) ptr->host_scribble)
lprint_Scsi_Cmnd(ptr, m);
SPRINTF("scsi%d: disconnected_queue\n", instance->host_no);
- for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ for (ptr = (struct scsi_cmnd *) hostdata->disconnected_queue; ptr; ptr = (struct scsi_cmnd *) ptr->host_scribble)
lprint_Scsi_Cmnd(ptr, m);
spin_unlock_irq(instance->host_lock);
return 0;
}
-static void lprint_Scsi_Cmnd(Scsi_Cmnd * cmd, struct seq_file *m)
+static void lprint_Scsi_Cmnd(struct scsi_cmnd *cmd, struct seq_file *m)
{
SPRINTF("scsi%d : destination target %d, lun %llu\n", cmd->device->host->host_no, cmd->device->id, cmd->device->lun);
SPRINTF(" command = ");
INIT_DELAYED_WORK(&hostdata->coroutine, NCR5380_main);
-#ifdef NCR5380_STATS
- for (i = 0; i < 8; ++i) {
- hostdata->time_read[i] = 0;
- hostdata->time_write[i] = 0;
- hostdata->bytes_read[i] = 0;
- hostdata->bytes_write[i] = 0;
- }
- hostdata->timebase = 0;
- hostdata->pendingw = 0;
- hostdata->pendingr = 0;
-#endif
-
/* The CHECK code seems to break the 53C400. Will check it later maybe */
if (flags & FLAG_NCR53C400)
hostdata->flags = FLAG_HAS_LAST_BYTE_SENT | flags;
hostdata->host = instance;
hostdata->time_expires = 0;
-#ifndef AUTOSENSE
- if ((instance->cmd_per_lun > 1) || instance->can_queue > 1)
- printk(KERN_WARNING "scsi%d : WARNING : support for multiple outstanding commands enabled\n" " without AUTOSENSE option, contingent allegiance conditions may\n"
- " be incorrectly cleared.\n", instance->host_no);
-#endif /* def AUTOSENSE */
+ prepare_info(instance);
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
NCR5380_write(MODE_REG, MR_BASE);
* Locks: host lock taken by caller
*/
-static int NCR5380_queue_command_lck(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+static int NCR5380_queue_command_lck(struct scsi_cmnd *cmd, void (*done) (struct scsi_cmnd *))
{
struct Scsi_Host *instance = cmd->device->host;
struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata;
- Scsi_Cmnd *tmp;
+ struct scsi_cmnd *tmp;
#if (NDEBUG & NDEBUG_NO_WRITE)
switch (cmd->cmnd[0]) {
}
#endif /* (NDEBUG & NDEBUG_NO_WRITE) */
-#ifdef NCR5380_STATS
- switch (cmd->cmnd[0]) {
- case WRITE:
- case WRITE_6:
- case WRITE_10:
- hostdata->time_write[cmd->device->id] -= (jiffies - hostdata->timebase);
- hostdata->bytes_write[cmd->device->id] += scsi_bufflen(cmd);
- hostdata->pendingw++;
- break;
- case READ:
- case READ_6:
- case READ_10:
- hostdata->time_read[cmd->device->id] -= (jiffies - hostdata->timebase);
- hostdata->bytes_read[cmd->device->id] += scsi_bufflen(cmd);
- hostdata->pendingr++;
- break;
- }
-#endif
-
/*
* We use the host_scribble field as a pointer to the next command
* in a queue
cmd->host_scribble = (unsigned char *) hostdata->issue_queue;
hostdata->issue_queue = cmd;
} else {
- for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp->host_scribble; tmp = (Scsi_Cmnd *) tmp->host_scribble);
+ for (tmp = (struct scsi_cmnd *) hostdata->issue_queue; tmp->host_scribble; tmp = (struct scsi_cmnd *) tmp->host_scribble);
LIST(cmd, tmp);
tmp->host_scribble = (unsigned char *) cmd;
}
struct NCR5380_hostdata *hostdata =
container_of(work, struct NCR5380_hostdata, coroutine.work);
struct Scsi_Host *instance = hostdata->host;
- Scsi_Cmnd *tmp, *prev;
+ struct scsi_cmnd *tmp, *prev;
int done;
spin_lock_irq(instance->host_lock);
* Search through the issue_queue for a command destined
* for a target that's not busy.
*/
- for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *) tmp->host_scribble)
+ for (tmp = (struct scsi_cmnd *) hostdata->issue_queue, prev = NULL; tmp; prev = tmp, tmp = (struct scsi_cmnd *) tmp->host_scribble)
{
if (prev != tmp)
dprintk(NDEBUG_LISTS, "MAIN tmp=%p target=%d busy=%d lun=%llu\n", tmp, tmp->device->id, hostdata->busy[tmp->device->id], tmp->device->lun);
prev->host_scribble = tmp->host_scribble;
} else {
REMOVE(-1, hostdata->issue_queue, tmp, tmp->host_scribble);
- hostdata->issue_queue = (Scsi_Cmnd *) tmp->host_scribble;
+ hostdata->issue_queue = (struct scsi_cmnd *) tmp->host_scribble;
}
tmp->host_scribble = NULL;
hostdata->selecting = NULL;
/* RvC: have to preset this to indicate a new command is being performed */
- if (!NCR5380_select(instance, tmp,
- /*
- * REQUEST SENSE commands are issued without tagged
- * queueing, even on SCSI-II devices because the
- * contingent allegiance condition exists for the
- * entire unit.
- */
- (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE : TAG_NEXT)) {
+ /*
+ * REQUEST SENSE commands are issued without tagged
+ * queueing, even on SCSI-II devices because the
+ * contingent allegiance condition exists for the
+ * entire unit.
+ */
+
+ if (!NCR5380_select(instance, tmp)) {
break;
} else {
LIST(tmp, hostdata->issue_queue);
/* exited locked */
} /* if (!hostdata->connected) */
if (hostdata->selecting) {
- tmp = (Scsi_Cmnd *) hostdata->selecting;
+ tmp = (struct scsi_cmnd *) hostdata->selecting;
/* Selection will drop and retake the lock */
- if (!NCR5380_select(instance, tmp, (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE : TAG_NEXT)) {
+ if (!NCR5380_select(instance, tmp)) {
/* Ok ?? */
} else {
/* RvC: device failed, so we wait a long time
#endif
-/**
- * collect_stats - collect stats on a scsi command
- * @hostdata: adapter
- * @cmd: command being issued
- *
- * Update the statistical data by parsing the command in question
- */
-
-static void collect_stats(struct NCR5380_hostdata *hostdata, Scsi_Cmnd * cmd)
-{
-#ifdef NCR5380_STATS
- switch (cmd->cmnd[0]) {
- case WRITE:
- case WRITE_6:
- case WRITE_10:
- hostdata->time_write[scmd_id(cmd)] += (jiffies - hostdata->timebase);
- hostdata->pendingw--;
- break;
- case READ:
- case READ_6:
- case READ_10:
- hostdata->time_read[scmd_id(cmd)] += (jiffies - hostdata->timebase);
- hostdata->pendingr--;
- break;
- }
-#endif
-}
-
-
/*
- * Function : int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd,
- * int tag);
+ * Function : int NCR5380_select(struct Scsi_Host *instance,
+ * struct scsi_cmnd *cmd)
*
* Purpose : establishes I_T_L or I_T_L_Q nexus for new or existing command,
* including ARBITRATION, SELECTION, and initial message out for
* IDENTIFY and queue messages.
*
* Inputs : instance - instantiation of the 5380 driver on which this
- * target lives, cmd - SCSI command to execute, tag - set to TAG_NEXT for
- * new tag, TAG_NONE for untagged queueing, otherwise set to the tag for
- * the command that is presently connected.
+ * target lives, cmd - SCSI command to execute.
*
* Returns : -1 if selection could not execute for some reason,
* 0 if selection succeeded or failed because the target
* Locks: caller holds hostdata lock in IRQ mode
*/
-static int NCR5380_select(struct Scsi_Host *instance, Scsi_Cmnd * cmd, int tag)
+static int NCR5380_select(struct Scsi_Host *instance, struct scsi_cmnd *cmd)
{
NCR5380_local_declare();
struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata;
return -1;
}
cmd->result = DID_BAD_TARGET << 16;
- collect_stats(hostdata, cmd);
cmd->scsi_done(cmd);
NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
dprintk(NDEBUG_SELECTION, "scsi%d : target did not respond within 250ms\n", instance->host_no);
}
dprintk(NDEBUG_SELECTION, "scsi%d : target %d selected, going into MESSAGE OUT phase.\n", instance->host_no, cmd->device->id);
- tmp[0] = IDENTIFY(((instance->irq == SCSI_IRQ_NONE) ? 0 : 1), cmd->device->lun);
+ tmp[0] = IDENTIFY(((instance->irq == NO_IRQ) ? 0 : 1), cmd->device->lun);
len = 1;
cmd->tag = 0;
#endif
unsigned char *data;
unsigned char phase, tmp, extended_msg[10], old_phase = 0xff;
- Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected;
+ struct scsi_cmnd *cmd = (struct scsi_cmnd *) hostdata->connected;
/* RvC: we need to set the end of the polling time */
unsigned long poll_time = jiffies + USLEEP_POLL;
cmd->next_link->tag = cmd->tag;
cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
dprintk(NDEBUG_LINKED, "scsi%d : target %d lun %llu linked request done, calling scsi_done().\n", instance->host_no, cmd->device->id, cmd->device->lun);
- collect_stats(hostdata, cmd);
cmd->scsi_done(cmd);
cmd = hostdata->connected;
break;
else if (status_byte(cmd->SCp.Status) != GOOD)
cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
-#ifdef AUTOSENSE
if ((cmd->cmnd[0] == REQUEST_SENSE) &&
hostdata->ses.cmd_len) {
scsi_eh_restore_cmnd(cmd, &hostdata->ses);
LIST(cmd, hostdata->issue_queue);
cmd->host_scribble = (unsigned char *)
hostdata->issue_queue;
- hostdata->issue_queue = (Scsi_Cmnd *) cmd;
+ hostdata->issue_queue = (struct scsi_cmnd *) cmd;
dprintk(NDEBUG_QUEUES, "scsi%d : REQUEST SENSE added to head of issue queue\n", instance->host_no);
- } else
-#endif /* def AUTOSENSE */
- {
- collect_stats(hostdata, cmd);
+ } else {
cmd->scsi_done(cmd);
}
hostdata->busy[cmd->device->id] &= ~(1 << (cmd->device->lun & 0xFF));
hostdata->connected = NULL;
cmd->result = DID_ERROR << 16;
- collect_stats(hostdata, cmd);
cmd->scsi_done(cmd);
NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
return;
* Function : void NCR5380_reselect (struct Scsi_Host *instance)
*
* Purpose : does reselection, initializing the instance->connected
- * field to point to the Scsi_Cmnd for which the I_T_L or I_T_L_Q
+ * field to point to the scsi_cmnd for which the I_T_L or I_T_L_Q
* nexus has been reestablished,
*
* Inputs : instance - this instance of the NCR5380.
int len;
unsigned char msg[3];
unsigned char *data;
- Scsi_Cmnd *tmp = NULL, *prev;
+ struct scsi_cmnd *tmp = NULL, *prev;
int abort = 0;
NCR5380_setup(instance);
*/
- for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue, prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *) tmp->host_scribble)
+ for (tmp = (struct scsi_cmnd *) hostdata->disconnected_queue, prev = NULL; tmp; prev = tmp, tmp = (struct scsi_cmnd *) tmp->host_scribble)
if ((target_mask == (1 << tmp->device->id)) && (lun == (u8)tmp->device->lun)
) {
if (prev) {
prev->host_scribble = tmp->host_scribble;
} else {
REMOVE(-1, hostdata->disconnected_queue, tmp, tmp->host_scribble);
- hostdata->disconnected_queue = (Scsi_Cmnd *) tmp->host_scribble;
+ hostdata->disconnected_queue = (struct scsi_cmnd *) tmp->host_scribble;
}
tmp->host_scribble = NULL;
break;
*
* Inputs : instance - this instance of the NCR5380.
*
- * Returns : pointer to the Scsi_Cmnd structure for which the I_T_L
+ * Returns : pointer to the scsi_cmnd structure for which the I_T_L
* nexus has been reestablished, on failure NULL is returned.
*/
#endif /* def REAL_DMA */
/*
- * Function : int NCR5380_abort (Scsi_Cmnd *cmd)
+ * Function : int NCR5380_abort (struct scsi_cmnd *cmd)
*
* Purpose : abort a command
*
- * Inputs : cmd - the Scsi_Cmnd to abort, code - code to set the
- * host byte of the result field to, if zero DID_ABORTED is
+ * Inputs : cmd - the scsi_cmnd to abort, code - code to set the
+ * host byte of the result field to, if zero DID_ABORTED is
* used.
*
- * Returns : 0 - success, -1 on failure.
+ * Returns : SUCCESS - success, FAILED on failure.
*
- * XXX - there is no way to abort the command that is currently
- * connected, you have to wait for it to complete. If this is
+ * XXX - there is no way to abort the command that is currently
+ * connected, you have to wait for it to complete. If this is
* a problem, we could implement longjmp() / setjmp(), setjmp()
* called where the loop started in NCR5380_main().
*
* Locks: host lock taken by caller
*/
-static int NCR5380_abort(Scsi_Cmnd * cmd) {
+static int NCR5380_abort(struct scsi_cmnd *cmd)
+{
NCR5380_local_declare();
struct Scsi_Host *instance = cmd->device->host;
struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata;
- Scsi_Cmnd *tmp, **prev;
-
- printk(KERN_WARNING "scsi%d : aborting command\n", instance->host_no);
- scsi_print_command(cmd);
+ struct scsi_cmnd *tmp, **prev;
+
+ scmd_printk(KERN_WARNING, cmd, "aborting command\n");
NCR5380_print_status(instance);
* aborted flag and get back into our main loop.
*/
- return 0;
+ return SUCCESS;
}
#endif
*/
dprintk(NDEBUG_ABORT, "scsi%d : abort going into loop.\n", instance->host_no);
- for (prev = (Scsi_Cmnd **) & (hostdata->issue_queue), tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp; prev = (Scsi_Cmnd **) & (tmp->host_scribble), tmp = (Scsi_Cmnd *) tmp->host_scribble)
+ for (prev = (struct scsi_cmnd **) &(hostdata->issue_queue), tmp = (struct scsi_cmnd *) hostdata->issue_queue; tmp; prev = (struct scsi_cmnd **) &(tmp->host_scribble), tmp = (struct scsi_cmnd *) tmp->host_scribble)
if (cmd == tmp) {
REMOVE(5, *prev, tmp, tmp->host_scribble);
- (*prev) = (Scsi_Cmnd *) tmp->host_scribble;
+ (*prev) = (struct scsi_cmnd *) tmp->host_scribble;
tmp->host_scribble = NULL;
tmp->result = DID_ABORT << 16;
dprintk(NDEBUG_ABORT, "scsi%d : abort removed command from issue queue.\n", instance->host_no);
* it from the disconnected queue.
*/
- for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp; tmp = (Scsi_Cmnd *) tmp->host_scribble)
+ for (tmp = (struct scsi_cmnd *) hostdata->disconnected_queue; tmp; tmp = (struct scsi_cmnd *) tmp->host_scribble)
if (cmd == tmp) {
dprintk(NDEBUG_ABORT, "scsi%d : aborting disconnected command.\n", instance->host_no);
- if (NCR5380_select(instance, cmd, (int) cmd->tag))
+ if (NCR5380_select(instance, cmd))
return FAILED;
dprintk(NDEBUG_ABORT, "scsi%d : nexus reestablished.\n", instance->host_no);
do_abort(instance);
- for (prev = (Scsi_Cmnd **) & (hostdata->disconnected_queue), tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp; prev = (Scsi_Cmnd **) & (tmp->host_scribble), tmp = (Scsi_Cmnd *) tmp->host_scribble)
+ for (prev = (struct scsi_cmnd **) &(hostdata->disconnected_queue), tmp = (struct scsi_cmnd *) hostdata->disconnected_queue; tmp; prev = (struct scsi_cmnd **) &(tmp->host_scribble), tmp = (struct scsi_cmnd *) tmp->host_scribble)
if (cmd == tmp) {
REMOVE(5, *prev, tmp, tmp->host_scribble);
- *prev = (Scsi_Cmnd *) tmp->host_scribble;
+ *prev = (struct scsi_cmnd *) tmp->host_scribble;
tmp->host_scribble = NULL;
tmp->result = DID_ABORT << 16;
tmp->scsi_done(tmp);
/*
- * Function : int NCR5380_bus_reset (Scsi_Cmnd *cmd)
+ * Function : int NCR5380_bus_reset (struct scsi_cmnd *cmd)
*
* Purpose : reset the SCSI bus.
*
* Locks: host lock taken by caller
*/
-static int NCR5380_bus_reset(Scsi_Cmnd * cmd)
+static int NCR5380_bus_reset(struct scsi_cmnd *cmd)
{
struct Scsi_Host *instance = cmd->device->host;
* drew@colorado.edu
* +1 (303) 666-5836
*
- * DISTRIBUTION RELEASE 7
- *
* For more information, please consult
*
* NCR 5380 Family
#define NCR5380_H
#include <linux/interrupt.h>
-
-#ifdef AUTOSENSE
#include <scsi/scsi_eh.h>
-#endif
-
-#define NCR5380_PUBLIC_RELEASE 7
-#define NCR53C400_PUBLIC_RELEASE 2
#define NDEBUG_ARBITRATION 0x1
#define NDEBUG_AUTOSENSE 0x2
#define DISCONNECT_LONG 2
/*
- * These are "special" values for the tag parameter passed to NCR5380_select.
+ * "Special" value for the (unsigned char) command tag, to indicate
+ * I_T_L nexus instead of I_T_L_Q.
*/
-#define TAG_NEXT -1 /* Use next free tag */
-#define TAG_NONE -2 /*
- * Establish I_T_L nexus instead of I_T_L_Q
- * even on SCSI-II devices.
- */
+#define TAG_NONE 0xff
/*
* These are "special" values for the irq and dma_channel fields of the
* Scsi_Host structure
*/
-#define SCSI_IRQ_NONE 255
#define DMA_NONE 255
#define IRQ_AUTO 254
#define DMA_AUTO 254
#define PORT_AUTO 0xffff /* autoprobe io port for 53c400a */
+#ifndef NO_IRQ
+#define NO_IRQ 0
+#endif
+
#define FLAG_HAS_LAST_BYTE_SENT 1 /* NCR53c81 or better */
#define FLAG_CHECK_LAST_BYTE_SENT 2 /* Only test once */
#define FLAG_NCR53C400 4 /* NCR53c400 */
#define FLAG_NO_PSEUDO_DMA 8 /* Inhibit DMA */
#define FLAG_DTC3181E 16 /* DTC3181E */
+#define FLAG_LATE_DMA_SETUP 32 /* Setup NCR before DMA H/W */
+#define FLAG_TAGGED_QUEUING 64 /* as X3T9.2 spelled it */
#ifndef ASM
+
+#ifdef SUPPORT_TAGS
+struct tag_alloc {
+ DECLARE_BITMAP(allocated, MAX_TAGS);
+ int nr_allocated;
+ int queue_size;
+};
+#endif
+
struct NCR5380_hostdata {
NCR5380_implementation_fields; /* implementation specific */
struct Scsi_Host *host; /* Host backpointer */
volatile int dma_len; /* requested length of DMA */
#endif
volatile unsigned char last_message; /* last message OUT */
- volatile Scsi_Cmnd *connected; /* currently connected command */
- volatile Scsi_Cmnd *issue_queue; /* waiting to be issued */
- volatile Scsi_Cmnd *disconnected_queue; /* waiting for reconnect */
+ volatile struct scsi_cmnd *connected; /* currently connected command */
+ volatile struct scsi_cmnd *issue_queue; /* waiting to be issued */
+ volatile struct scsi_cmnd *disconnected_queue; /* waiting for reconnect */
volatile int restart_select; /* we have disconnected,
used to restart
NCR5380_select() */
int flags;
unsigned long time_expires; /* in jiffies, set prior to sleeping */
int select_time; /* timer in select for target response */
- volatile Scsi_Cmnd *selecting;
+ volatile struct scsi_cmnd *selecting;
struct delayed_work coroutine; /* our co-routine */
-#ifdef NCR5380_STATS
- unsigned timebase; /* Base for time calcs */
- long time_read[8]; /* time to do reads */
- long time_write[8]; /* time to do writes */
- unsigned long bytes_read[8]; /* bytes read */
- unsigned long bytes_write[8]; /* bytes written */
- unsigned pendingr;
- unsigned pendingw;
-#endif
-#ifdef AUTOSENSE
struct scsi_eh_save ses;
+ char info[256];
+ int read_overruns; /* number of bytes to cut from a
+ * transfer to handle chip overruns */
+ int retain_dma_intr;
+ struct work_struct main_task;
+ volatile int main_running;
+#ifdef SUPPORT_TAGS
+ struct tag_alloc TagAlloc[8][8]; /* 8 targets and 8 LUNs */
+#endif
+#ifdef PSEUDO_DMA
+ unsigned spin_max_r;
+ unsigned spin_max_w;
#endif
};
#endif
#define dprintk(flg, fmt, ...) \
- do { if ((NDEBUG) & (flg)) pr_debug(fmt, ## __VA_ARGS__); } while (0)
+ do { if ((NDEBUG) & (flg)) \
+ printk(KERN_DEBUG fmt, ## __VA_ARGS__); } while (0)
#if NDEBUG
#define NCR5380_dprint(flg, arg) \
static irqreturn_t NCR5380_intr(int irq, void *dev_id);
#endif
static void NCR5380_main(struct work_struct *work);
-static void __maybe_unused NCR5380_print_options(struct Scsi_Host *instance);
-static int NCR5380_abort(Scsi_Cmnd * cmd);
-static int NCR5380_bus_reset(Scsi_Cmnd * cmd);
-static int NCR5380_queue_command(struct Scsi_Host *, struct scsi_cmnd *);
-static int __maybe_unused NCR5380_show_info(struct seq_file *,
- struct Scsi_Host *);
-static int __maybe_unused NCR5380_write_info(struct Scsi_Host *instance,
- char *buffer, int length);
-
+static const char *NCR5380_info(struct Scsi_Host *instance);
static void NCR5380_reselect(struct Scsi_Host *instance);
-static int NCR5380_select(struct Scsi_Host *instance, Scsi_Cmnd * cmd, int tag);
+static int NCR5380_select(struct Scsi_Host *instance, struct scsi_cmnd *cmd);
#if defined(PSEUDO_DMA) || defined(REAL_DMA) || defined(REAL_DMA_POLL)
static int NCR5380_transfer_dma(struct Scsi_Host *instance, unsigned char *phase, int *count, unsigned char **data);
#endif
(fsa_dev_ptr[cid].sense_data.sense_key ==
NOT_READY)) {
switch (scsicmd->cmnd[0]) {
- case SERVICE_ACTION_IN:
+ case SERVICE_ACTION_IN_16:
if (!(dev->raw_io_interface) ||
!(dev->raw_io_64) ||
((scsicmd->cmnd[1] & 0x1f) != SAI_READ_CAPACITY_16))
scsi_sg_copy_from_buffer(scsicmd, &inq_data, sizeof(inq_data));
return aac_get_container_name(scsicmd);
}
- case SERVICE_ACTION_IN:
+ case SERVICE_ACTION_IN_16:
if (!(dev->raw_io_interface) ||
!(dev->raw_io_64) ||
((scsicmd->cmnd[1] & 0x1f) != SAI_READ_CAPACITY_16))
depth = 256;
else if (depth < 2)
depth = 2;
- scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, depth);
+ scsi_change_queue_depth(sdev, depth);
} else
- scsi_adjust_queue_depth(sdev, 0, 1);
+ scsi_change_queue_depth(sdev, 1);
return 0;
}
* total capacity and the queue depth supported by the target device.
*/
-static int aac_change_queue_depth(struct scsi_device *sdev, int depth,
- int reason)
+static int aac_change_queue_depth(struct scsi_device *sdev, int depth)
{
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
-
if (sdev->tagged_supported && (sdev->type == TYPE_DISK) &&
(sdev_channel(sdev) == CONTAINER_CHANNEL)) {
struct scsi_device * dev;
depth = 256;
else if (depth < 2)
depth = 2;
- scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, depth);
- } else
- scsi_adjust_queue_depth(sdev, 0, 1);
- return sdev->queue_depth;
+ return scsi_change_queue_depth(sdev, depth);
+ }
+
+ return scsi_change_queue_depth(sdev, 1);
}
static ssize_t aac_show_raid_level(struct device *dev, struct device_attribute *attr, char *buf)
AAC_DRIVERNAME,
host->host_no, sdev_channel(dev), sdev_id(dev), dev->lun);
switch (cmd->cmnd[0]) {
- case SERVICE_ACTION_IN:
+ case SERVICE_ACTION_IN_16:
if (!(aac->raw_io_interface) ||
!(aac->raw_io_64) ||
((cmd->cmnd[1] & 0x1f) != SAI_READ_CAPACITY_16))
asc_dvc->cfg->can_tagged_qng |= tid_bit;
asc_dvc->use_tagged_qng |= tid_bit;
}
- scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG,
+ scsi_change_queue_depth(sdev,
asc_dvc->max_dvc_qng[sdev->id]);
}
} else {
asc_dvc->cfg->can_tagged_qng &= ~tid_bit;
asc_dvc->use_tagged_qng &= ~tid_bit;
}
- scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun);
}
if ((sdev->lun == 0) &&
}
}
- if ((adv_dvc->tagqng_able & tidmask) && sdev->tagged_supported) {
- scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG,
- adv_dvc->max_dvc_qng);
- } else {
- scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun);
- }
+ if ((adv_dvc->tagqng_able & tidmask) && sdev->tagged_supported)
+ scsi_change_queue_depth(sdev, adv_dvc->max_dvc_qng);
}
/*
*
*
**************************************************************************
-
+
see Documentation/scsi/aha152x.txt for configuration details
**************************************************************************/
#error define AUTOCONF or SETUP0
#endif
-#if defined(AHA152X_DEBUG)
-#define DEBUG_DEFAULT debug_eh
-
-#define DPRINTK(when,msgs...) \
- do { if(HOSTDATA(shpnt)->debug & (when)) printk(msgs); } while(0)
-
-#define DO_LOCK(flags) \
- do { \
- if(spin_is_locked(&QLOCK)) { \
- DPRINTK(debug_intr, DEBUG_LEAD "(%s:%d) already locked at %s:%d\n", CMDINFO(CURRENT_SC), __func__, __LINE__, QLOCKER, QLOCKERL); \
- } \
- DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) locking\n", CMDINFO(CURRENT_SC), __func__, __LINE__); \
- spin_lock_irqsave(&QLOCK,flags); \
- DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) locked\n", CMDINFO(CURRENT_SC), __func__, __LINE__); \
- QLOCKER=__func__; \
- QLOCKERL=__LINE__; \
- } while(0)
-
-#define DO_UNLOCK(flags) \
- do { \
- DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) unlocking (locked at %s:%d)\n", CMDINFO(CURRENT_SC), __func__, __LINE__, QLOCKER, QLOCKERL); \
- spin_unlock_irqrestore(&QLOCK,flags); \
- DPRINTK(debug_locking, DEBUG_LEAD "(%s:%d) unlocked\n", CMDINFO(CURRENT_SC), __func__, __LINE__); \
- QLOCKER="(not locked)"; \
- QLOCKERL=0; \
- } while(0)
-
-#else
-#define DPRINTK(when,msgs...)
#define DO_LOCK(flags) spin_lock_irqsave(&QLOCK,flags)
#define DO_UNLOCK(flags) spin_unlock_irqrestore(&QLOCK,flags)
-#endif
#define LEAD "(scsi%d:%d:%d) "
-#define WARN_LEAD KERN_WARNING LEAD
#define INFO_LEAD KERN_INFO LEAD
-#define NOTE_LEAD KERN_NOTICE LEAD
-#define ERR_LEAD KERN_ERR LEAD
-#define DEBUG_LEAD KERN_DEBUG LEAD
#define CMDINFO(cmd) \
(cmd) ? ((cmd)->device->host->host_no) : -1, \
(cmd) ? ((cmd)->device->id & 0x0f) : -1, \
enum {
not_issued = 0x0001, /* command not yet issued */
- selecting = 0x0002, /* target is beeing selected */
+ selecting = 0x0002, /* target is being selected */
identified = 0x0004, /* IDENTIFY was sent */
disconnected = 0x0008, /* target disconnected */
- completed = 0x0010, /* target sent COMMAND COMPLETE */
+ completed = 0x0010, /* target sent COMMAND COMPLETE */
aborted = 0x0020, /* ABORT was sent */
resetted = 0x0040, /* BUS DEVICE RESET was sent */
spiordy = 0x0080, /* waiting for SPIORDY to raise */
module_param_array(exttrans, int, NULL, 0);
MODULE_PARM_DESC(exttrans,"use extended translation");
-#if !defined(AHA152X_DEBUG)
static int aha152x[] = {0, 11, 7, 1, 1, 0, DELAY_DEFAULT, 0};
module_param_array(aha152x, int, NULL, 0);
MODULE_PARM_DESC(aha152x, "parameters for first controller");
static int aha152x1[] = {0, 11, 7, 1, 1, 0, DELAY_DEFAULT, 0};
module_param_array(aha152x1, int, NULL, 0);
MODULE_PARM_DESC(aha152x1, "parameters for second controller");
-#else
-static int debug[] = {DEBUG_DEFAULT, DEBUG_DEFAULT};
-module_param_array(debug, int, NULL, 0);
-MODULE_PARM_DESC(debug, "flags for driver debugging");
-
-static int aha152x[] = {0, 11, 7, 1, 1, 1, DELAY_DEFAULT, 0, DEBUG_DEFAULT};
-module_param_array(aha152x, int, NULL, 0);
-MODULE_PARM_DESC(aha152x, "parameters for first controller");
-
-static int aha152x1[] = {0, 11, 7, 1, 1, 1, DELAY_DEFAULT, 0, DEBUG_DEFAULT};
-module_param_array(aha152x1, int, NULL, 0);
-MODULE_PARM_DESC(aha152x1, "parameters for second controller");
-#endif /* !defined(AHA152X_DEBUG) */
#endif /* MODULE */
#ifdef __ISAPNP__
/*
* internal states of the host
*
- */
+ */
enum aha152x_state {
idle=0,
unknown,
spinlock_t lock;
/* host lock */
-#if defined(AHA152X_DEBUG)
- const char *locker;
- /* which function has the lock */
- int lockerl; /* where did it get it */
-
- int debug; /* current debugging setting */
-#endif
-
#if defined(AHA152X_STAT)
- int total_commands;
+ int total_commands;
int disconnections;
int busfree_without_any_action;
int busfree_without_old_command;
int busfree_without_new_command;
int busfree_without_done_command;
int busfree_with_check_condition;
- int count[maxstate];
- int count_trans[maxstate];
+ int count[maxstate];
+ int count_trans[maxstate];
unsigned long time[maxstate];
#endif
int delay; /* reset out delay */
int ext_trans; /* extended translation enabled */
- int swint; /* software-interrupt was fired during detect() */
+ int swint; /* software-interrupt was fired during detect() */
int service; /* bh needs to be run */
int in_intr; /* bh is running */
unsigned char msgi[256];
/* received message bytes */
- int msgo_i, msgo_len;
+ int msgo_i, msgo_len;
/* number of sent bytes and length of current messages */
unsigned char msgo[256];
/* pending messages */
static void done(struct Scsi_Host *shpnt, int error);
/* diagnostics */
-static void disp_ports(struct Scsi_Host *shpnt);
static void show_command(Scsi_Cmnd * ptr);
static void show_queues(struct Scsi_Host *shpnt);
static void disp_enintr(struct Scsi_Host *shpnt);
DELAY = setup->delay;
EXT_TRANS = setup->ext_trans;
-#if defined(AHA152X_DEBUG)
- HOSTDATA(shpnt)->debug = setup->debug;
-#endif
-
SETPORT(SCSIID, setup->scsiid << 4);
shpnt->this_id = setup->scsiid;
* setup controller to generate interrupts depending
* on current state (lock has to be acquired)
*
- */
+ */
static int setup_expected_interrupts(struct Scsi_Host *shpnt)
{
if(CURRENT_SC) {
CURRENT_SC->SCp.phase |= 1 << 16;
-
+
if(CURRENT_SC->SCp.phase & selecting) {
- DPRINTK(debug_intr, DEBUG_LEAD "expecting: (seldo) (seltimo) (seldi)\n", CMDINFO(CURRENT_SC));
SETPORT(SSTAT1, SELTO);
SETPORT(SIMODE0, ENSELDO | (DISCONNECTED_SC ? ENSELDI : 0));
SETPORT(SIMODE1, ENSELTIMO);
} else {
- DPRINTK(debug_intr, DEBUG_LEAD "expecting: (phase change) (busfree) %s\n", CMDINFO(CURRENT_SC), CURRENT_SC->SCp.phase & spiordy ? "(spiordy)" : "");
SETPORT(SIMODE0, (CURRENT_SC->SCp.phase & spiordy) ? ENSPIORDY : 0);
- SETPORT(SIMODE1, ENPHASEMIS | ENSCSIRST | ENSCSIPERR | ENBUSFREE);
+ SETPORT(SIMODE1, ENPHASEMIS | ENSCSIRST | ENSCSIPERR | ENBUSFREE);
}
} else if(STATE==seldi) {
- DPRINTK(debug_intr, DEBUG_LEAD "expecting: (phase change) (identify)\n", CMDINFO(CURRENT_SC));
SETPORT(SIMODE0, 0);
- SETPORT(SIMODE1, ENPHASEMIS | ENSCSIRST | ENSCSIPERR | ENBUSFREE);
+ SETPORT(SIMODE1, ENPHASEMIS | ENSCSIRST | ENSCSIPERR | ENBUSFREE);
} else {
- DPRINTK(debug_intr, DEBUG_LEAD "expecting: %s %s\n",
- CMDINFO(CURRENT_SC),
- DISCONNECTED_SC ? "(reselection)" : "",
- ISSUE_SC ? "(busfree)" : "");
SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
SETPORT(SIMODE1, ENSCSIRST | ( (ISSUE_SC||DONE_SC) ? ENBUSFREE : 0));
}
}
-/*
+/*
* Queue a command and setup interrupts for a free bus.
*/
static int aha152x_internal_queue(Scsi_Cmnd *SCpnt, struct completion *complete,
struct Scsi_Host *shpnt = SCpnt->device->host;
unsigned long flags;
-#if defined(AHA152X_DEBUG)
- if (HOSTDATA(shpnt)->debug & debug_queue) {
- printk(INFO_LEAD "queue: %p; cmd_len=%d pieces=%d size=%u cmnd=",
- CMDINFO(SCpnt), SCpnt, SCpnt->cmd_len,
- scsi_sg_count(SCpnt), scsi_bufflen(SCpnt));
- __scsi_print_command(SCpnt->cmnd);
- }
-#endif
-
SCpnt->scsi_done = done;
SCpnt->SCp.phase = not_issued | phase;
SCpnt->SCp.Status = 0x1; /* Ilegal status by SCSI standard */
if(SCpnt->SCp.phase & (resetting|check_condition)) {
if (!SCpnt->host_scribble || SCSEM(SCpnt) || SCNEXT(SCpnt)) {
- printk(ERR_LEAD "cannot reuse command\n", CMDINFO(SCpnt));
+ scmd_printk(KERN_ERR, SCpnt, "cannot reuse command\n");
return FAILED;
}
} else {
SCpnt->host_scribble = kmalloc(sizeof(struct aha152x_scdata), GFP_ATOMIC);
if(!SCpnt->host_scribble) {
- printk(ERR_LEAD "allocation failed\n", CMDINFO(SCpnt));
+ scmd_printk(KERN_ERR, SCpnt, "allocation failed\n");
return FAILED;
}
}
*/
static int aha152x_queue_lck(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
{
-#if 0
- if(*SCpnt->cmnd == REQUEST_SENSE) {
- SCpnt->result = 0;
- done(SCpnt);
-
- return 0;
- }
-#endif
-
return aha152x_internal_queue(SCpnt, NULL, 0, done);
}
/*
- *
*
*/
static void reset_done(Scsi_Cmnd *SCpnt)
{
-#if 0
- struct Scsi_Host *shpnt = SCpnt->host;
- DPRINTK(debug_eh, INFO_LEAD "reset_done called\n", CMDINFO(SCpnt));
-#endif
if(SCSEM(SCpnt)) {
complete(SCSEM(SCpnt));
} else {
Scsi_Cmnd *ptr;
unsigned long flags;
-#if defined(AHA152X_DEBUG)
- if(HOSTDATA(shpnt)->debug & debug_eh) {
- printk(DEBUG_LEAD "abort(%p)", CMDINFO(SCpnt), SCpnt);
- show_queues(shpnt);
- }
-#endif
-
DO_LOCK(flags);
ptr=remove_SC(&ISSUE_SC, SCpnt);
if(ptr) {
- DPRINTK(debug_eh, DEBUG_LEAD "not yet issued - SUCCESS\n", CMDINFO(SCpnt));
-
HOSTDATA(shpnt)->commands--;
if (!HOSTDATA(shpnt)->commands)
SETPORT(PORTA, 0);
SCpnt->host_scribble=NULL;
return SUCCESS;
- }
+ }
DO_UNLOCK(flags);
*
*/
- printk(ERR_LEAD "cannot abort running or disconnected command\n", CMDINFO(SCpnt));
+ scmd_printk(KERN_ERR, SCpnt,
+ "cannot abort running or disconnected command\n");
return FAILED;
}
unsigned long flags;
unsigned long timeleft;
-#if defined(AHA152X_DEBUG)
- if(HOSTDATA(shpnt)->debug & debug_eh) {
- printk(INFO_LEAD "aha152x_device_reset(%p)", CMDINFO(SCpnt), SCpnt);
- show_queues(shpnt);
- }
-#endif
-
if(CURRENT_SC==SCpnt) {
- printk(ERR_LEAD "cannot reset current device\n", CMDINFO(SCpnt));
+ scmd_printk(KERN_ERR, SCpnt, "cannot reset current device\n");
return FAILED;
}
} else if(disconnected) {
append_SC(&DISCONNECTED_SC, SCpnt);
}
-
+
ret = FAILED;
}
if(SCDATA(ptr)) {
next = SCNEXT(ptr);
} else {
- printk(DEBUG_LEAD "queue corrupted at %p\n", CMDINFO(ptr), ptr);
+ scmd_printk(KERN_DEBUG, ptr,
+ "queue corrupted at %p\n", ptr);
next = NULL;
}
if (!ptr->device->soft_reset) {
- DPRINTK(debug_eh, DEBUG_LEAD "disconnected command %p removed\n", CMDINFO(ptr), ptr);
remove_SC(SCs, ptr);
HOSTDATA(shpnt)->commands--;
kfree(ptr->host_scribble);
DO_LOCK(flags);
-#if defined(AHA152X_DEBUG)
- if(HOSTDATA(shpnt)->debug & debug_eh) {
- printk(KERN_DEBUG "scsi%d: bus reset", shpnt->host_no);
- show_queues(shpnt);
- }
-#endif
-
free_hard_reset_SCs(shpnt, &ISSUE_SC);
free_hard_reset_SCs(shpnt, &DISCONNECTED_SC);
- DPRINTK(debug_eh, KERN_DEBUG "scsi%d: resetting bus\n", shpnt->host_no);
-
SETPORT(SCSISEQ, SCSIRSTO);
mdelay(256);
SETPORT(SCSISEQ, 0);
mdelay(DELAY);
- DPRINTK(debug_eh, KERN_DEBUG "scsi%d: bus resetted\n", shpnt->host_no);
-
setup_expected_interrupts(shpnt);
if(HOSTDATA(shpnt)->commands==0)
SETPORT(PORTA, 0);
*/
int aha152x_host_reset_host(struct Scsi_Host *shpnt)
{
- DPRINTK(debug_eh, KERN_DEBUG "scsi%d: host reset\n", shpnt->host_no);
-
aha152x_bus_reset_host(shpnt);
-
- DPRINTK(debug_eh, KERN_DEBUG "scsi%d: resetting ports\n", shpnt->host_no);
reset_ports(shpnt);
return SUCCESS;
/*
* Reset the host (bus and controller)
- *
+ *
*/
static int aha152x_host_reset(Scsi_Cmnd *SCpnt)
{
{
if (CURRENT_SC) {
if(DONE_SC)
- printk(ERR_LEAD "there's already a completed command %p - will cause abort\n", CMDINFO(CURRENT_SC), DONE_SC);
+ scmd_printk(KERN_ERR, CURRENT_SC,
+ "there's already a completed command %p "
+ "- will cause abort\n", DONE_SC);
DONE_SC = CURRENT_SC;
CURRENT_SC = NULL;
return IRQ_NONE;
if( TESTLO(DMASTAT, INTSTAT) )
- return IRQ_NONE;
+ return IRQ_NONE;
/* no more interrupts from the controller, while we're busy.
INTEN is restored by the BH handler */
SETPORT(SXFRCTL0, CH1);
SETPORT(SSTAT1, CLRBUSFREE);
-
+
if(CURRENT_SC) {
#if defined(AHA152X_STAT)
action++;
done(shpnt, (CURRENT_SC->SCp.Status & 0xff) | ((CURRENT_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16));
} else if(CURRENT_SC->SCp.phase & aborted) {
- DPRINTK(debug_eh, DEBUG_LEAD "ABORT sent\n", CMDINFO(CURRENT_SC));
done(shpnt, (CURRENT_SC->SCp.Status & 0xff) | ((CURRENT_SC->SCp.Message & 0xff) << 8) | (DID_ABORT << 16));
} else if(CURRENT_SC->SCp.phase & resetted) {
- DPRINTK(debug_eh, DEBUG_LEAD "BUS DEVICE RESET sent\n", CMDINFO(CURRENT_SC));
done(shpnt, (CURRENT_SC->SCp.Status & 0xff) | ((CURRENT_SC->SCp.Message & 0xff) << 8) | (DID_RESET << 16));
} else if(CURRENT_SC->SCp.phase & disconnected) {
/* target sent DISCONNECT */
- DPRINTK(debug_selection, DEBUG_LEAD "target disconnected at %d/%d\n",
- CMDINFO(CURRENT_SC),
- scsi_get_resid(CURRENT_SC),
- scsi_bufflen(CURRENT_SC));
#if defined(AHA152X_STAT)
HOSTDATA(shpnt)->disconnections++;
#endif
struct scsi_cmnd *cmd = HOSTDATA(shpnt)->done_SC;
struct aha152x_scdata *sc = SCDATA(cmd);
-#if 0
- if(HOSTDATA(shpnt)->debug & debug_eh) {
- printk(ERR_LEAD "received sense: ", CMDINFO(DONE_SC));
- scsi_print_sense("bh", DONE_SC);
- }
-#endif
-
scsi_eh_restore_cmnd(cmd, &sc->ses);
cmd->SCp.Status = SAM_STAT_CHECK_CONDITION;
#if defined(AHA152X_STAT)
HOSTDATA(shpnt)->busfree_with_check_condition++;
#endif
-#if 0
- DPRINTK(debug_eh, ERR_LEAD "CHECK CONDITION found\n", CMDINFO(DONE_SC));
-#endif
if(!(DONE_SC->SCp.phase & not_issued)) {
struct aha152x_scdata *sc;
Scsi_Cmnd *ptr = DONE_SC;
DONE_SC=NULL;
-#if 0
- DPRINTK(debug_eh, ERR_LEAD "requesting sense\n", CMDINFO(ptr));
-#endif
sc = SCDATA(ptr);
/* It was allocated in aha152x_internal_queue? */
DO_UNLOCK(flags);
aha152x_internal_queue(ptr, NULL, check_condition, ptr->scsi_done);
DO_LOCK(flags);
-#if 0
- } else {
- DPRINTK(debug_eh, ERR_LEAD "command not issued - CHECK CONDITION ignored\n", CMDINFO(DONE_SC));
-#endif
}
}
if(DONE_SC && DONE_SC->scsi_done) {
-#if defined(AHA152X_DEBUG)
- int hostno=DONE_SC->device->host->host_no;
- int id=DONE_SC->device->id & 0xf;
- int lun=((u8)DONE_SC->device->lun) & 0x7;
-#endif
Scsi_Cmnd *ptr = DONE_SC;
DONE_SC=NULL;
}
DO_UNLOCK(flags);
- DPRINTK(debug_done, DEBUG_LEAD "calling scsi_done(%p)\n", hostno, id, lun, ptr);
- ptr->scsi_done(ptr);
- DPRINTK(debug_done, DEBUG_LEAD "scsi_done(%p) returned\n", hostno, id, lun, ptr);
+ ptr->scsi_done(ptr);
DO_LOCK(flags);
}
#if defined(AHA152X_STAT)
action++;
#endif
- CURRENT_SC->SCp.phase |= selecting;
-
- DPRINTK(debug_selection, DEBUG_LEAD "selecting target\n", CMDINFO(CURRENT_SC));
+ CURRENT_SC->SCp.phase |= selecting;
/* clear selection timeout */
SETPORT(SSTAT1, SELTO);
SETPORT(SSTAT1, CLRBUSFREE);
SETPORT(SSTAT1, CLRPHASECHG);
- CURRENT_SC->SCp.phase &= ~(selecting|not_issued);
+ CURRENT_SC->SCp.phase &= ~(selecting|not_issued);
SETPORT(SCSISEQ, 0);
if (TESTLO(SSTAT0, SELDO)) {
- printk(ERR_LEAD "aha152x: passing bus free condition\n", CMDINFO(CURRENT_SC));
+ scmd_printk(KERN_ERR, CURRENT_SC,
+ "aha152x: passing bus free condition\n");
done(shpnt, DID_NO_CONNECT << 16);
return;
}
SETPORT(SSTAT0, CLRSELDO);
-
+
ADDMSGO(IDENTIFY(RECONNECT, CURRENT_SC->device->lun));
if (CURRENT_SC->SCp.phase & aborting) {
} else if (CURRENT_SC->SCp.phase & resetting) {
ADDMSGO(BUS_DEVICE_RESET);
} else if (SYNCNEG==0 && SYNCHRONOUS) {
- CURRENT_SC->SCp.phase |= syncneg;
+ CURRENT_SC->SCp.phase |= syncneg;
MSGOLEN += spi_populate_sync_msg(&MSGO(MSGOLEN), 50, 8);
SYNCNEG=1; /* negotiation in progress */
}
*/
static void selto_run(struct Scsi_Host *shpnt)
{
- SETPORT(SCSISEQ, 0);
+ SETPORT(SCSISEQ, 0);
SETPORT(SSTAT1, CLRSELTIMO);
- DPRINTK(debug_selection, DEBUG_LEAD "selection timeout\n", CMDINFO(CURRENT_SC));
-
- if(!CURRENT_SC) {
- DPRINTK(debug_selection, DEBUG_LEAD "!CURRENT_SC\n", CMDINFO(CURRENT_SC));
+ if (!CURRENT_SC)
return;
- }
- CURRENT_SC->SCp.phase &= ~selecting;
+ CURRENT_SC->SCp.phase &= ~selecting;
- if (CURRENT_SC->SCp.phase & aborted) {
- DPRINTK(debug_selection, DEBUG_LEAD "aborted\n", CMDINFO(CURRENT_SC));
+ if (CURRENT_SC->SCp.phase & aborted)
done(shpnt, DID_ABORT << 16);
- } else if (TESTLO(SSTAT0, SELINGO)) {
- DPRINTK(debug_selection, DEBUG_LEAD "arbitration not won\n", CMDINFO(CURRENT_SC));
+ else if (TESTLO(SSTAT0, SELINGO))
done(shpnt, DID_BUS_BUSY << 16);
- } else {
+ else
/* ARBITRATION won, but SELECTION failed */
- DPRINTK(debug_selection, DEBUG_LEAD "selection failed\n", CMDINFO(CURRENT_SC));
done(shpnt, DID_NO_CONNECT << 16);
- }
}
/*
if(CURRENT_SC) {
if(!(CURRENT_SC->SCp.phase & not_issued))
- printk(ERR_LEAD "command should not have been issued yet\n", CMDINFO(CURRENT_SC));
-
- DPRINTK(debug_selection, ERR_LEAD "command requeued - reselection\n", CMDINFO(CURRENT_SC));
+ scmd_printk(KERN_ERR, CURRENT_SC,
+ "command should not have been issued yet\n");
DO_LOCK(flags);
append_SC(&ISSUE_SC, CURRENT_SC);
CURRENT_SC = NULL;
}
- if(!DISCONNECTED_SC) {
- DPRINTK(debug_selection, DEBUG_LEAD "unexpected SELDI ", CMDINFO(CURRENT_SC));
+ if (!DISCONNECTED_SC)
return;
- }
RECONN_TARGET=-1;
selid = GETPORT(SELID) & ~(1 << shpnt->this_id);
if (selid==0) {
- printk("aha152x%d: target id unknown (%02x)\n", HOSTNO, selid);
+ shost_printk(KERN_INFO, shpnt,
+ "target id unknown (%02x)\n", selid);
return;
}
;
if(selid & ~(1 << target)) {
- printk("aha152x%d: multiple targets reconnected (%02x)\n",
- HOSTNO, selid);
+ shost_printk(KERN_INFO, shpnt,
+ "multiple targets reconnected (%02x)\n", selid);
}
SETRATE(HOSTDATA(shpnt)->syncrate[target]);
RECONN_TARGET=target;
- DPRINTK(debug_selection, DEBUG_LEAD "target %d reselected (%02x).\n", CMDINFO(CURRENT_SC), target, selid);
}
/*
if(sstat1 & (PHASECHG|PHASEMIS|BUSFREE) || !(sstat1 & REQINIT))
return;
- if(TESTLO(SSTAT0,SPIORDY)) {
- DPRINTK(debug_msgi, DEBUG_LEAD "!SPIORDY\n", CMDINFO(CURRENT_SC));
+ if (TESTLO(SSTAT0, SPIORDY))
return;
- }
ADDMSGI(GETPORT(SCSIDAT));
-#if defined(AHA152X_DEBUG)
- if (HOSTDATA(shpnt)->debug & debug_msgi) {
- printk(INFO_LEAD "inbound message %02x ", CMDINFO(CURRENT_SC), MSGI(0));
- spi_print_msg(&MSGI(0));
- printk("\n");
- }
-#endif
-
if(!CURRENT_SC) {
if(LASTSTATE!=seldi) {
- printk(KERN_ERR "aha152x%d: message in w/o current command not after reselection\n", HOSTNO);
+ shost_printk(KERN_ERR, shpnt,
+ "message in w/o current command"
+ " not after reselection\n");
}
/*
- * Handle reselection
- */
+ * Handle reselection
+ */
if(!(MSGI(0) & IDENTIFY_BASE)) {
- printk(KERN_ERR "aha152x%d: target didn't identify after reselection\n", HOSTNO);
+ shost_printk(KERN_ERR, shpnt,
+ "target didn't identify after reselection\n");
continue;
}
if (!CURRENT_SC) {
show_queues(shpnt);
- printk(KERN_ERR "aha152x%d: no disconnected command for target %d/%d\n", HOSTNO, RECONN_TARGET, MSGI(0) & 0x3f);
+ shost_printk(KERN_ERR, shpnt,
+ "no disconnected command"
+ " for target %d/%d\n",
+ RECONN_TARGET, MSGI(0) & 0x3f);
continue;
}
- DPRINTK(debug_msgi, DEBUG_LEAD "target reconnected\n", CMDINFO(CURRENT_SC));
-
CURRENT_SC->SCp.Message = MSGI(0);
CURRENT_SC->SCp.phase &= ~disconnected;
/* next message if any */
continue;
- }
+ }
CURRENT_SC->SCp.Message = MSGI(0);
switch (MSGI(0)) {
case DISCONNECT:
if (!RECONNECT)
- printk(WARN_LEAD "target was not allowed to disconnect\n", CMDINFO(CURRENT_SC));
+ scmd_printk(KERN_WARNING, CURRENT_SC,
+ "target was not allowed to disconnect\n");
CURRENT_SC->SCp.phase |= disconnected;
break;
case COMMAND_COMPLETE:
- if(CURRENT_SC->SCp.phase & completed)
- DPRINTK(debug_msgi, DEBUG_LEAD "again COMMAND COMPLETE\n", CMDINFO(CURRENT_SC));
-
CURRENT_SC->SCp.phase |= completed;
break;
case MESSAGE_REJECT:
if (SYNCNEG==1) {
- printk(INFO_LEAD "Synchronous Data Transfer Request was rejected\n", CMDINFO(CURRENT_SC));
+ scmd_printk(KERN_INFO, CURRENT_SC,
+ "Synchronous Data Transfer Request"
+ " was rejected\n");
SYNCNEG=2; /* negotiation completed */
} else
- printk(INFO_LEAD "inbound message (MESSAGE REJECT)\n", CMDINFO(CURRENT_SC));
+ scmd_printk(KERN_INFO, CURRENT_SC,
+ "inbound message (MESSAGE REJECT)\n");
break;
case SAVE_POINTERS:
long ticks;
if (MSGI(1) != 3) {
- printk(ERR_LEAD "SDTR message length!=3\n", CMDINFO(CURRENT_SC));
+ scmd_printk(KERN_ERR, CURRENT_SC,
+ "SDTR message length!=3\n");
break;
}
/* negotiation in progress */
if (ticks > 9 || MSGI(4) < 1 || MSGI(4) > 8) {
ADDMSGO(MESSAGE_REJECT);
- printk(INFO_LEAD "received Synchronous Data Transfer Request invalid - rejected\n", CMDINFO(CURRENT_SC));
+ scmd_printk(KERN_INFO,
+ CURRENT_SC,
+ "received Synchronous Data Transfer Request invalid - rejected\n");
break;
}
-
+
SYNCRATE |= ((ticks - 2) << 4) + MSGI(4);
} else if (ticks <= 9 && MSGI(4) >= 1) {
ADDMSGO(EXTENDED_MESSAGE);
SYNCRATE |= ((ticks - 2) << 4) + MSGI(4);
} else {
/* requested SDTR is too slow, do it asynchronously */
- printk(INFO_LEAD "Synchronous Data Transfer Request too slow - Rejecting\n", CMDINFO(CURRENT_SC));
+ scmd_printk(KERN_INFO,
+ CURRENT_SC,
+ "Synchronous Data Transfer Request too slow - Rejecting\n");
ADDMSGO(MESSAGE_REJECT);
}
- SYNCNEG=2; /* negotiation completed */
+ /* negotiation completed */
+ SYNCNEG=2;
SETRATE(SYNCRATE);
}
break;
static void msgi_end(struct Scsi_Host *shpnt)
{
if(MSGILEN>0)
- printk(WARN_LEAD "target left before message completed (%d)\n", CMDINFO(CURRENT_SC), MSGILEN);
+ scmd_printk(KERN_WARNING, CURRENT_SC,
+ "target left before message completed (%d)\n",
+ MSGILEN);
- if (MSGOLEN > 0 && !(GETPORT(SSTAT1) & BUSFREE)) {
- DPRINTK(debug_msgi, DEBUG_LEAD "msgo pending\n", CMDINFO(CURRENT_SC));
+ if (MSGOLEN > 0 && !(GETPORT(SSTAT1) & BUSFREE))
SETPORT(SCSISIG, P_MSGI | SIG_ATNO);
- }
}
/*
if((CURRENT_SC->SCp.phase & syncneg) && SYNCNEG==2 && SYNCRATE==0) {
ADDMSGO(IDENTIFY(RECONNECT, CURRENT_SC->device->lun));
} else {
- printk(INFO_LEAD "unexpected MESSAGE OUT phase; rejecting\n", CMDINFO(CURRENT_SC));
+ scmd_printk(KERN_INFO, CURRENT_SC,
+ "unexpected MESSAGE OUT phase; rejecting\n");
ADDMSGO(MESSAGE_REJECT);
}
}
-#if defined(AHA152X_DEBUG)
- if(HOSTDATA(shpnt)->debug & debug_msgo) {
- int i;
-
- printk(DEBUG_LEAD "messages( ", CMDINFO(CURRENT_SC));
- for (i=0; i<MSGOLEN; i+=spi_print_msg(&MSGO(i)), printk(" "))
- ;
- printk(")\n");
- }
-#endif
}
/*
*/
static void msgo_run(struct Scsi_Host *shpnt)
{
- if(MSGO_I==MSGOLEN)
- DPRINTK(debug_msgo, DEBUG_LEAD "messages all sent (%d/%d)\n", CMDINFO(CURRENT_SC), MSGO_I, MSGOLEN);
-
while(MSGO_I<MSGOLEN) {
- DPRINTK(debug_msgo, DEBUG_LEAD "message byte %02x (%d/%d)\n", CMDINFO(CURRENT_SC), MSGO(MSGO_I), MSGO_I, MSGOLEN);
-
- if(TESTLO(SSTAT0, SPIORDY)) {
- DPRINTK(debug_msgo, DEBUG_LEAD "!SPIORDY\n", CMDINFO(CURRENT_SC));
+ if (TESTLO(SSTAT0, SPIORDY))
return;
- }
if (MSGO_I==MSGOLEN-1) {
/* Leave MESSAGE OUT after transfer */
static void msgo_end(struct Scsi_Host *shpnt)
{
if(MSGO_I<MSGOLEN) {
- printk(ERR_LEAD "message sent incompletely (%d/%d)\n", CMDINFO(CURRENT_SC), MSGO_I, MSGOLEN);
+ scmd_printk(KERN_ERR, CURRENT_SC,
+ "message sent incompletely (%d/%d)\n",
+ MSGO_I, MSGOLEN);
if(SYNCNEG==1) {
- printk(INFO_LEAD "Synchronous Data Transfer Request was rejected\n", CMDINFO(CURRENT_SC));
+ scmd_printk(KERN_INFO, CURRENT_SC,
+ "Synchronous Data Transfer Request was rejected\n");
SYNCNEG=2;
}
}
-
+
MSGO_I = 0;
MSGOLEN = 0;
}
-/*
+/*
* command phase
*
*/
static void cmd_init(struct Scsi_Host *shpnt)
{
if (CURRENT_SC->SCp.sent_command) {
- printk(ERR_LEAD "command already sent\n", CMDINFO(CURRENT_SC));
+ scmd_printk(KERN_ERR, CURRENT_SC,
+ "command already sent\n");
done(shpnt, DID_ERROR << 16);
return;
}
-#if defined(AHA152X_DEBUG)
- if (HOSTDATA(shpnt)->debug & debug_cmd) {
- printk(DEBUG_LEAD "cmd_init: ", CMDINFO(CURRENT_SC));
- __scsi_print_command(CURRENT_SC->cmnd);
- }
-#endif
-
CMD_I=0;
}
*/
static void cmd_run(struct Scsi_Host *shpnt)
{
- if(CMD_I==CURRENT_SC->cmd_len) {
- DPRINTK(debug_cmd, DEBUG_LEAD "command already completely sent (%d/%d)", CMDINFO(CURRENT_SC), CMD_I, CURRENT_SC->cmd_len);
- disp_ports(shpnt);
- }
-
while(CMD_I<CURRENT_SC->cmd_len) {
- DPRINTK(debug_cmd, DEBUG_LEAD "command byte %02x (%d/%d)\n", CMDINFO(CURRENT_SC), CURRENT_SC->cmnd[CMD_I], CMD_I, CURRENT_SC->cmd_len);
-
- if(TESTLO(SSTAT0, SPIORDY)) {
- DPRINTK(debug_cmd, DEBUG_LEAD "!SPIORDY\n", CMDINFO(CURRENT_SC));
+ if (TESTLO(SSTAT0, SPIORDY))
return;
- }
SETPORT(SCSIDAT, CURRENT_SC->cmnd[CMD_I++]);
}
static void cmd_end(struct Scsi_Host *shpnt)
{
if(CMD_I<CURRENT_SC->cmd_len)
- printk(ERR_LEAD "command sent incompletely (%d/%d)\n", CMDINFO(CURRENT_SC), CMD_I, CURRENT_SC->cmd_len);
+ scmd_printk(KERN_ERR, CURRENT_SC,
+ "command sent incompletely (%d/%d)\n",
+ CMD_I, CURRENT_SC->cmd_len);
else
CURRENT_SC->SCp.sent_command++;
}
*/
static void status_run(struct Scsi_Host *shpnt)
{
- if(TESTLO(SSTAT0,SPIORDY)) {
- DPRINTK(debug_status, DEBUG_LEAD "!SPIORDY\n", CMDINFO(CURRENT_SC));
+ if (TESTLO(SSTAT0, SPIORDY))
return;
- }
CURRENT_SC->SCp.Status = GETPORT(SCSIDAT);
-#if defined(AHA152X_DEBUG)
- if (HOSTDATA(shpnt)->debug & debug_status) {
- printk(DEBUG_LEAD "inbound status %02x ", CMDINFO(CURRENT_SC), CURRENT_SC->SCp.Status);
- scsi_print_status(CURRENT_SC->SCp.Status);
- printk("\n");
- }
-#endif
}
/*
SETPORT(SIMODE1, ENSCSIPERR | ENSCSIRST | ENPHASEMIS | ENBUSFREE);
DATA_LEN=0;
- DPRINTK(debug_datai,
- DEBUG_LEAD "datai_init: request_bufflen=%d resid=%d\n",
- CMDINFO(CURRENT_SC), scsi_bufflen(CURRENT_SC),
- scsi_get_resid(CURRENT_SC));
}
static void datai_run(struct Scsi_Host *shpnt)
barrier();
if(TESTLO(DMASTAT, DFIFOFULL|INTSTAT)) {
- printk(ERR_LEAD "datai timeout", CMDINFO(CURRENT_SC));
- disp_ports(shpnt);
+ scmd_printk(KERN_ERR, CURRENT_SC, "datai timeout\n");
break;
}
barrier();
if(TESTLO(SSTAT2, SEMPTY)) {
- printk(ERR_LEAD "datai sempty timeout", CMDINFO(CURRENT_SC));
- disp_ports(shpnt);
+ scmd_printk(KERN_ERR, CURRENT_SC,
+ "datai sempty timeout");
break;
}
if(CURRENT_SC->SCp.this_residual>0) {
while(fifodata>0 && CURRENT_SC->SCp.this_residual>0) {
- data_count = fifodata>CURRENT_SC->SCp.this_residual ?
+ data_count = fifodata > CURRENT_SC->SCp.this_residual ?
CURRENT_SC->SCp.this_residual :
fifodata;
fifodata -= data_count;
- if(data_count & 1) {
- DPRINTK(debug_datai, DEBUG_LEAD "8bit\n", CMDINFO(CURRENT_SC));
- SETPORT(DMACNTRL0, ENDMA|_8BIT);
- *CURRENT_SC->SCp.ptr++ = GETPORT(DATAPORT);
- CURRENT_SC->SCp.this_residual--;
- DATA_LEN++;
- SETPORT(DMACNTRL0, ENDMA);
- }
-
- if(data_count > 1) {
- DPRINTK(debug_datai, DEBUG_LEAD "16bit(%d)\n", CMDINFO(CURRENT_SC), data_count);
- data_count >>= 1;
- insw(DATAPORT, CURRENT_SC->SCp.ptr, data_count);
- CURRENT_SC->SCp.ptr += 2 * data_count;
- CURRENT_SC->SCp.this_residual -= 2 * data_count;
- DATA_LEN += 2 * data_count;
- }
-
- if(CURRENT_SC->SCp.this_residual==0 && CURRENT_SC->SCp.buffers_residual>0) {
- /* advance to next buffer */
- CURRENT_SC->SCp.buffers_residual--;
- CURRENT_SC->SCp.buffer++;
- CURRENT_SC->SCp.ptr = SG_ADDRESS(CURRENT_SC->SCp.buffer);
- CURRENT_SC->SCp.this_residual = CURRENT_SC->SCp.buffer->length;
- }
- }
- } else if(fifodata>0) {
- printk(ERR_LEAD "no buffers left for %d(%d) bytes (data overrun!?)\n", CMDINFO(CURRENT_SC), fifodata, GETPORT(FIFOSTAT));
- SETPORT(DMACNTRL0, ENDMA|_8BIT);
+ if (data_count & 1) {
+ SETPORT(DMACNTRL0, ENDMA|_8BIT);
+ *CURRENT_SC->SCp.ptr++ = GETPORT(DATAPORT);
+ CURRENT_SC->SCp.this_residual--;
+ DATA_LEN++;
+ SETPORT(DMACNTRL0, ENDMA);
+ }
+
+ if (data_count > 1) {
+ data_count >>= 1;
+ insw(DATAPORT, CURRENT_SC->SCp.ptr, data_count);
+ CURRENT_SC->SCp.ptr += 2 * data_count;
+ CURRENT_SC->SCp.this_residual -= 2 * data_count;
+ DATA_LEN += 2 * data_count;
+ }
+
+ if (CURRENT_SC->SCp.this_residual == 0 &&
+ CURRENT_SC->SCp.buffers_residual > 0) {
+ /* advance to next buffer */
+ CURRENT_SC->SCp.buffers_residual--;
+ CURRENT_SC->SCp.buffer++;
+ CURRENT_SC->SCp.ptr = SG_ADDRESS(CURRENT_SC->SCp.buffer);
+ CURRENT_SC->SCp.this_residual = CURRENT_SC->SCp.buffer->length;
+ }
+ }
+ } else if (fifodata > 0) {
+ scmd_printk(KERN_ERR, CURRENT_SC,
+ "no buffers left for %d(%d) bytes"
+ " (data overrun!?)\n",
+ fifodata, GETPORT(FIFOSTAT));
+ SETPORT(DMACNTRL0, ENDMA|_8BIT);
while(fifodata>0) {
int data;
data=GETPORT(DATAPORT);
- DPRINTK(debug_datai, DEBUG_LEAD "data=%02x\n", CMDINFO(CURRENT_SC), data);
fifodata--;
DATA_LEN++;
}
- SETPORT(DMACNTRL0, ENDMA|_8BIT);
+ SETPORT(DMACNTRL0, ENDMA|_8BIT);
}
}
TESTLO(DMASTAT, DFIFOEMP) ||
TESTLO(SSTAT2, SEMPTY) ||
GETPORT(FIFOSTAT)>0) {
- /*
+ /*
* something went wrong, if there's something left in the fifos
* or the phase didn't change
*/
- printk(ERR_LEAD "fifos should be empty and phase should have changed\n", CMDINFO(CURRENT_SC));
- disp_ports(shpnt);
+ scmd_printk(KERN_ERR, CURRENT_SC,
+ "fifos should be empty and phase should have changed\n");
}
if(DATA_LEN!=GETSTCNT()) {
- printk(ERR_LEAD
- "manual transfer count differs from automatic (count=%d;stcnt=%d;diff=%d;fifostat=%d)",
- CMDINFO(CURRENT_SC), DATA_LEN, GETSTCNT(), GETSTCNT()-DATA_LEN, GETPORT(FIFOSTAT));
- disp_ports(shpnt);
+ scmd_printk(KERN_ERR, CURRENT_SC,
+ "manual transfer count differs from automatic "
+ "(count=%d;stcnt=%d;diff=%d;fifostat=%d)",
+ DATA_LEN, GETSTCNT(), GETSTCNT()-DATA_LEN,
+ GETPORT(FIFOSTAT));
mdelay(10000);
}
}
{
CMD_INC_RESID(CURRENT_SC, -GETSTCNT());
- DPRINTK(debug_datai,
- DEBUG_LEAD "datai_end: request_bufflen=%d resid=%d stcnt=%d\n",
- CMDINFO(CURRENT_SC), scsi_bufflen(CURRENT_SC),
- scsi_get_resid(CURRENT_SC), GETSTCNT());
-
SETPORT(SXFRCTL0, CH1|CLRSTCNT);
SETPORT(DMACNTRL0, 0);
}
SETPORT(SIMODE1, ENSCSIPERR | ENSCSIRST | ENPHASEMIS | ENBUSFREE );
DATA_LEN = scsi_get_resid(CURRENT_SC);
-
- DPRINTK(debug_datao,
- DEBUG_LEAD "datao_init: request_bufflen=%d; resid=%d\n",
- CMDINFO(CURRENT_SC), scsi_bufflen(CURRENT_SC),
- scsi_get_resid(CURRENT_SC));
}
static void datao_run(struct Scsi_Host *shpnt)
data_count=CURRENT_SC->SCp.this_residual;
if(TESTLO(DMASTAT, DFIFOEMP)) {
- printk(ERR_LEAD "datao fifo not empty (%d)", CMDINFO(CURRENT_SC), GETPORT(FIFOSTAT));
- disp_ports(shpnt);
+ scmd_printk(KERN_ERR, CURRENT_SC,
+ "datao fifo not empty (%d)",
+ GETPORT(FIFOSTAT));
break;
}
CURRENT_SC->SCp.ptr += 2 * data_count;
CURRENT_SC->SCp.this_residual -= 2 * data_count;
CMD_INC_RESID(CURRENT_SC, -2 * data_count);
- }
+ }
if(CURRENT_SC->SCp.this_residual==0 && CURRENT_SC->SCp.buffers_residual>0) {
/* advance to next buffer */
barrier();
if(TESTLO(DMASTAT, DFIFOEMP|INTSTAT)) {
- printk(ERR_LEAD "dataout timeout", CMDINFO(CURRENT_SC));
- disp_ports(shpnt);
+ scmd_printk(KERN_ERR, CURRENT_SC, "dataout timeout\n");
break;
}
}
{
if(TESTLO(DMASTAT, DFIFOEMP)) {
int data_count = (DATA_LEN - scsi_get_resid(CURRENT_SC)) -
- GETSTCNT();
-
- DPRINTK(debug_datao, DEBUG_LEAD "datao: %d bytes to resend (%d written, %d transferred)\n",
- CMDINFO(CURRENT_SC),
- data_count,
- DATA_LEN - scsi_get_resid(CURRENT_SC),
- GETSTCNT());
+ GETSTCNT();
CMD_INC_RESID(CURRENT_SC, data_count);
data_count -= CURRENT_SC->SCp.ptr -
- SG_ADDRESS(CURRENT_SC->SCp.buffer);
+ SG_ADDRESS(CURRENT_SC->SCp.buffer);
while(data_count>0) {
CURRENT_SC->SCp.buffer--;
CURRENT_SC->SCp.buffers_residual++;
data_count -= CURRENT_SC->SCp.buffer->length;
}
CURRENT_SC->SCp.ptr = SG_ADDRESS(CURRENT_SC->SCp.buffer) -
- data_count;
+ data_count;
CURRENT_SC->SCp.this_residual = CURRENT_SC->SCp.buffer->length +
- data_count;
+ data_count;
}
- DPRINTK(debug_datao, DEBUG_LEAD "datao_end: request_bufflen=%d; resid=%d; stcnt=%d\n",
- CMDINFO(CURRENT_SC),
- scsi_bufflen(CURRENT_SC),
- scsi_get_resid(CURRENT_SC),
- GETSTCNT());
-
SETPORT(SXFRCTL0, CH1|CLRCH1|CLRSTCNT);
SETPORT(SXFRCTL0, CH1);
STATE=rsti;
SETPORT(SCSISEQ,0);
SETPORT(SSTAT1,SCSIRSTI);
- } else if(stat0 & SELDI && PREVSTATE==busfree) {
+ } else if (stat0 & SELDI && PREVSTATE == busfree) {
STATE=seldi;
} else if(stat0 & SELDO && CURRENT_SC && (CURRENT_SC->SCp.phase & selecting)) {
STATE=seldo;
}
if((stat0 & SELDI) && STATE!=seldi && !dataphase) {
- printk(INFO_LEAD "reselection missed?", CMDINFO(CURRENT_SC));
- disp_ports(shpnt);
+ scmd_printk(KERN_INFO, CURRENT_SC, "reselection missed?");
}
if(STATE!=PREVSTATE) {
*/
static void parerr_run(struct Scsi_Host *shpnt)
{
- printk(ERR_LEAD "parity error\n", CMDINFO(CURRENT_SC));
+ scmd_printk(KERN_ERR, CURRENT_SC, "parity error\n");
done(shpnt, DID_PARITY << 16);
}
{
Scsi_Cmnd *ptr;
- printk(KERN_NOTICE "aha152x%d: scsi reset in\n", HOSTNO);
-
+ shost_printk(KERN_NOTICE, shpnt, "scsi reset in\n");
+
ptr=DISCONNECTED_SC;
while(ptr) {
Scsi_Cmnd *next = SCNEXT(ptr);
dataphase=update_state(shpnt);
- DPRINTK(debug_phases, LEAD "start %s %s(%s)\n", CMDINFO(CURRENT_SC), states[STATE].name, states[PREVSTATE].name, states[LASTSTATE].name);
-
/*
* end previous state
*
if(dataphase) {
SETPORT(SSTAT0, REQINIT);
SETPORT(SCSISIG, GETPORT(SCSISIG) & P_MASK);
- SETPORT(SSTAT1, PHASECHG);
+ SETPORT(SSTAT1, PHASECHG);
}
-
+
/*
* enable SPIO mode if previous didn't use it
* and this one does
if(CURRENT_SC)
CURRENT_SC->SCp.phase |= spiordy;
}
-
+
/*
* initialize for new state
*
*/
if(PREVSTATE!=STATE && states[STATE].init)
states[STATE].init(shpnt);
-
+
/*
* handle current state
*
if(states[STATE].run)
states[STATE].run(shpnt);
else
- printk(ERR_LEAD "unexpected state (%x)\n", CMDINFO(CURRENT_SC), STATE);
-
+ scmd_printk(KERN_ERR, CURRENT_SC,
+ "unexpected state (%x)\n", STATE);
+
/*
* setup controller to interrupt on
* the next expected condition and
HOSTDATA(shpnt)->time[STATE] += jiffies-start;
#endif
- DPRINTK(debug_phases, LEAD "end %s %s(%s)\n", CMDINFO(CURRENT_SC), states[STATE].name, states[PREVSTATE].name, states[LASTSTATE].name);
} while(pending);
/*
}
-/*
+/*
* Dump the current driver status and panic
*/
static void aha152x_error(struct Scsi_Host *shpnt, char *msg)
{
- printk(KERN_EMERG "\naha152x%d: %s\n", HOSTNO, msg);
+ shost_printk(KERN_EMERG, shpnt, "%s\n", msg);
show_queues(shpnt);
panic("aha152x panic\n");
}
-/*
- * Display registers of AIC-6260
- */
-static void disp_ports(struct Scsi_Host *shpnt)
-{
-#if defined(AHA152X_DEBUG)
- int s;
-
- printk("\n%s: %s(%s) ",
- CURRENT_SC ? "busy" : "waiting",
- states[STATE].name,
- states[PREVSTATE].name);
-
- s = GETPORT(SCSISEQ);
- printk("SCSISEQ( ");
- if (s & TEMODEO)
- printk("TARGET MODE ");
- if (s & ENSELO)
- printk("SELO ");
- if (s & ENSELI)
- printk("SELI ");
- if (s & ENRESELI)
- printk("RESELI ");
- if (s & ENAUTOATNO)
- printk("AUTOATNO ");
- if (s & ENAUTOATNI)
- printk("AUTOATNI ");
- if (s & ENAUTOATNP)
- printk("AUTOATNP ");
- if (s & SCSIRSTO)
- printk("SCSIRSTO ");
- printk(");");
-
- printk(" SCSISIG(");
- s = GETPORT(SCSISIG);
- switch (s & P_MASK) {
- case P_DATAO:
- printk("DATA OUT");
- break;
- case P_DATAI:
- printk("DATA IN");
- break;
- case P_CMD:
- printk("COMMAND");
- break;
- case P_STATUS:
- printk("STATUS");
- break;
- case P_MSGO:
- printk("MESSAGE OUT");
- break;
- case P_MSGI:
- printk("MESSAGE IN");
- break;
- default:
- printk("*invalid*");
- break;
- }
-
- printk("); ");
-
- printk("INTSTAT (%s); ", TESTHI(DMASTAT, INTSTAT) ? "hi" : "lo");
-
- printk("SSTAT( ");
- s = GETPORT(SSTAT0);
- if (s & TARGET)
- printk("TARGET ");
- if (s & SELDO)
- printk("SELDO ");
- if (s & SELDI)
- printk("SELDI ");
- if (s & SELINGO)
- printk("SELINGO ");
- if (s & SWRAP)
- printk("SWRAP ");
- if (s & SDONE)
- printk("SDONE ");
- if (s & SPIORDY)
- printk("SPIORDY ");
- if (s & DMADONE)
- printk("DMADONE ");
-
- s = GETPORT(SSTAT1);
- if (s & SELTO)
- printk("SELTO ");
- if (s & ATNTARG)
- printk("ATNTARG ");
- if (s & SCSIRSTI)
- printk("SCSIRSTI ");
- if (s & PHASEMIS)
- printk("PHASEMIS ");
- if (s & BUSFREE)
- printk("BUSFREE ");
- if (s & SCSIPERR)
- printk("SCSIPERR ");
- if (s & PHASECHG)
- printk("PHASECHG ");
- if (s & REQINIT)
- printk("REQINIT ");
- printk("); ");
-
-
- printk("SSTAT( ");
-
- s = GETPORT(SSTAT0) & GETPORT(SIMODE0);
-
- if (s & TARGET)
- printk("TARGET ");
- if (s & SELDO)
- printk("SELDO ");
- if (s & SELDI)
- printk("SELDI ");
- if (s & SELINGO)
- printk("SELINGO ");
- if (s & SWRAP)
- printk("SWRAP ");
- if (s & SDONE)
- printk("SDONE ");
- if (s & SPIORDY)
- printk("SPIORDY ");
- if (s & DMADONE)
- printk("DMADONE ");
-
- s = GETPORT(SSTAT1) & GETPORT(SIMODE1);
-
- if (s & SELTO)
- printk("SELTO ");
- if (s & ATNTARG)
- printk("ATNTARG ");
- if (s & SCSIRSTI)
- printk("SCSIRSTI ");
- if (s & PHASEMIS)
- printk("PHASEMIS ");
- if (s & BUSFREE)
- printk("BUSFREE ");
- if (s & SCSIPERR)
- printk("SCSIPERR ");
- if (s & PHASECHG)
- printk("PHASECHG ");
- if (s & REQINIT)
- printk("REQINIT ");
- printk("); ");
-
- printk("SXFRCTL0( ");
-
- s = GETPORT(SXFRCTL0);
- if (s & SCSIEN)
- printk("SCSIEN ");
- if (s & DMAEN)
- printk("DMAEN ");
- if (s & CH1)
- printk("CH1 ");
- if (s & CLRSTCNT)
- printk("CLRSTCNT ");
- if (s & SPIOEN)
- printk("SPIOEN ");
- if (s & CLRCH1)
- printk("CLRCH1 ");
- printk("); ");
-
- printk("SIGNAL( ");
-
- s = GETPORT(SCSISIG);
- if (s & SIG_ATNI)
- printk("ATNI ");
- if (s & SIG_SELI)
- printk("SELI ");
- if (s & SIG_BSYI)
- printk("BSYI ");
- if (s & SIG_REQI)
- printk("REQI ");
- if (s & SIG_ACKI)
- printk("ACKI ");
- printk("); ");
-
- printk("SELID (%02x), ", GETPORT(SELID));
-
- printk("STCNT (%d), ", GETSTCNT());
-
- printk("SSTAT2( ");
-
- s = GETPORT(SSTAT2);
- if (s & SOFFSET)
- printk("SOFFSET ");
- if (s & SEMPTY)
- printk("SEMPTY ");
- if (s & SFULL)
- printk("SFULL ");
- printk("); SFCNT (%d); ", s & (SFULL | SFCNT));
-
- s = GETPORT(SSTAT3);
- printk("SCSICNT (%d), OFFCNT(%d), ", (s & 0xf0) >> 4, s & 0x0f);
-
- printk("SSTAT4( ");
- s = GETPORT(SSTAT4);
- if (s & SYNCERR)
- printk("SYNCERR ");
- if (s & FWERR)
- printk("FWERR ");
- if (s & FRERR)
- printk("FRERR ");
- printk("); ");
-
- printk("DMACNTRL0( ");
- s = GETPORT(DMACNTRL0);
- printk("%s ", s & _8BIT ? "8BIT" : "16BIT");
- printk("%s ", s & DMA ? "DMA" : "PIO");
- printk("%s ", s & WRITE_READ ? "WRITE" : "READ");
- if (s & ENDMA)
- printk("ENDMA ");
- if (s & INTEN)
- printk("INTEN ");
- if (s & RSTFIFO)
- printk("RSTFIFO ");
- if (s & SWINT)
- printk("SWINT ");
- printk("); ");
-
- printk("DMASTAT( ");
- s = GETPORT(DMASTAT);
- if (s & ATDONE)
- printk("ATDONE ");
- if (s & WORDRDY)
- printk("WORDRDY ");
- if (s & DFIFOFULL)
- printk("DFIFOFULL ");
- if (s & DFIFOEMP)
- printk("DFIFOEMP ");
- printk(")\n");
-#endif
-}
-
/*
* display enabled interrupts
*/
static void disp_enintr(struct Scsi_Host *shpnt)
{
- int s;
-
- printk(KERN_DEBUG "enabled interrupts ( ");
-
- s = GETPORT(SIMODE0);
- if (s & ENSELDO)
- printk("ENSELDO ");
- if (s & ENSELDI)
- printk("ENSELDI ");
- if (s & ENSELINGO)
- printk("ENSELINGO ");
- if (s & ENSWRAP)
- printk("ENSWRAP ");
- if (s & ENSDONE)
- printk("ENSDONE ");
- if (s & ENSPIORDY)
- printk("ENSPIORDY ");
- if (s & ENDMADONE)
- printk("ENDMADONE ");
-
- s = GETPORT(SIMODE1);
- if (s & ENSELTIMO)
- printk("ENSELTIMO ");
- if (s & ENATNTARG)
- printk("ENATNTARG ");
- if (s & ENPHASEMIS)
- printk("ENPHASEMIS ");
- if (s & ENBUSFREE)
- printk("ENBUSFREE ");
- if (s & ENSCSIPERR)
- printk("ENSCSIPERR ");
- if (s & ENPHASECHG)
- printk("ENPHASECHG ");
- if (s & ENREQINIT)
- printk("ENREQINIT ");
- printk(")\n");
+ int s0, s1;
+
+ s0 = GETPORT(SIMODE0);
+ s1 = GETPORT(SIMODE1);
+
+ shost_printk(KERN_DEBUG, shpnt,
+ "enabled interrupts (%s%s%s%s%s%s%s%s%s%s%s%s%s%s)\n",
+ (s0 & ENSELDO) ? "ENSELDO " : "",
+ (s0 & ENSELDI) ? "ENSELDI " : "",
+ (s0 & ENSELINGO) ? "ENSELINGO " : "",
+ (s0 & ENSWRAP) ? "ENSWRAP " : "",
+ (s0 & ENSDONE) ? "ENSDONE " : "",
+ (s0 & ENSPIORDY) ? "ENSPIORDY " : "",
+ (s0 & ENDMADONE) ? "ENDMADONE " : "",
+ (s1 & ENSELTIMO) ? "ENSELTIMO " : "",
+ (s1 & ENATNTARG) ? "ENATNTARG " : "",
+ (s1 & ENPHASEMIS) ? "ENPHASEMIS " : "",
+ (s1 & ENBUSFREE) ? "ENBUSFREE " : "",
+ (s1 & ENSCSIPERR) ? "ENSCSIPERR " : "",
+ (s1 & ENPHASECHG) ? "ENPHASECHG " : "",
+ (s1 & ENREQINIT) ? "ENREQINIT " : "");
}
/*
*/
static void show_command(Scsi_Cmnd *ptr)
{
- scmd_printk(KERN_DEBUG, ptr, "%p: cmnd=(", ptr);
-
- __scsi_print_command(ptr->cmnd);
-
- printk(KERN_DEBUG "); request_bufflen=%d; resid=%d; phase |",
- scsi_bufflen(ptr), scsi_get_resid(ptr));
-
- if (ptr->SCp.phase & not_issued)
- printk("not issued|");
- if (ptr->SCp.phase & selecting)
- printk("selecting|");
- if (ptr->SCp.phase & identified)
- printk("identified|");
- if (ptr->SCp.phase & disconnected)
- printk("disconnected|");
- if (ptr->SCp.phase & completed)
- printk("completed|");
- if (ptr->SCp.phase & spiordy)
- printk("spiordy|");
- if (ptr->SCp.phase & syncneg)
- printk("syncneg|");
- if (ptr->SCp.phase & aborted)
- printk("aborted|");
- if (ptr->SCp.phase & resetted)
- printk("resetted|");
- if( SCDATA(ptr) ) {
- printk("; next=0x%p\n", SCNEXT(ptr));
- } else {
- printk("; next=(host scribble NULL)\n");
- }
+ scsi_print_command(ptr);
+ scmd_printk(KERN_DEBUG, ptr,
+ "request_bufflen=%d; resid=%d; "
+ "phase |%s%s%s%s%s%s%s%s%s; next=0x%p",
+ scsi_bufflen(ptr), scsi_get_resid(ptr),
+ (ptr->SCp.phase & not_issued) ? "not issued|" : "",
+ (ptr->SCp.phase & selecting) ? "selecting|" : "",
+ (ptr->SCp.phase & identified) ? "identified|" : "",
+ (ptr->SCp.phase & disconnected) ? "disconnected|" : "",
+ (ptr->SCp.phase & completed) ? "completed|" : "",
+ (ptr->SCp.phase & spiordy) ? "spiordy|" : "",
+ (ptr->SCp.phase & syncneg) ? "syncneg|" : "",
+ (ptr->SCp.phase & aborted) ? "aborted|" : "",
+ (ptr->SCp.phase & resetted) ? "resetted|" : "",
+ (SCDATA(ptr)) ? SCNEXT(ptr) : NULL);
}
/*
for (ptr = DISCONNECTED_SC; ptr; ptr = SCDATA(ptr) ? SCNEXT(ptr) : NULL)
show_command(ptr);
- disp_ports(shpnt);
disp_enintr(shpnt);
}
if(!shpnt || !buffer || length<8 || strncmp("aha152x ", buffer, 8)!=0)
return -EINVAL;
-#if defined(AHA152X_DEBUG)
- if(length>14 && strncmp("debug ", buffer+8, 6)==0) {
- int debug = HOSTDATA(shpnt)->debug;
-
- HOSTDATA(shpnt)->debug = simple_strtoul(buffer+14, NULL, 0);
-
- printk(KERN_INFO "aha152x%d: debugging options set to 0x%04x (were 0x%04x)\n", HOSTNO, HOSTDATA(shpnt)->debug, debug);
- } else
-#endif
#if defined(AHA152X_STAT)
if(length>13 && strncmp("reset", buffer+8, 5)==0) {
int i;
HOSTDATA(shpnt)->time[i]=0;
}
- printk(KERN_INFO "aha152x%d: stats reseted.\n", HOSTNO);
+ shost_printk(KERN_INFO, shpnt, "aha152x: stats reset.\n");
} else
#endif
(((HOSTDATA(shpnt)->syncrate[i] & 0x70) >> 4) + 2) * 50,
HOSTDATA(shpnt)->syncrate[i] & 0x0f);
}
-#if defined(AHA152X_DEBUG)
-#define PDEBUG(flags,txt) \
- if(HOSTDATA(shpnt)->debug & flags) SPRINTF("(%s) ", txt);
-
- SPRINTF("enabled debugging options: ");
-
- PDEBUG(debug_procinfo, "procinfo");
- PDEBUG(debug_queue, "queue");
- PDEBUG(debug_intr, "interrupt");
- PDEBUG(debug_selection, "selection");
- PDEBUG(debug_msgo, "message out");
- PDEBUG(debug_msgi, "message in");
- PDEBUG(debug_status, "status");
- PDEBUG(debug_cmd, "command");
- PDEBUG(debug_datai, "data in");
- PDEBUG(debug_datao, "data out");
- PDEBUG(debug_eh, "eh");
- PDEBUG(debug_locking, "locks");
- PDEBUG(debug_phases, "phases");
-
- SPRINTF("\n");
-#endif
-
SPRINTF("\nqueue status:\n");
DO_LOCK(flags);
if (ISSUE_SC) {
#if defined(AHA152X_STAT)
SPRINTF("statistics:\n"
- "total commands: %d\n"
- "disconnections: %d\n"
+ "total commands: %d\n"
+ "disconnections: %d\n"
"busfree with check condition: %d\n"
"busfree without old command: %d\n"
"busfree without new command: %d\n"
HOSTDATA(shpnt)->busfree_without_any_action);
for(i=0; i<maxstate; i++) {
SPRINTF("%-10s %-12d %-12d %-12ld\n",
- states[i].name,
+ states[i].name,
HOSTDATA(shpnt)->count_trans[i],
HOSTDATA(shpnt)->count[i],
HOSTDATA(shpnt)->time[i]);
setup[setup_count].synchronous = aha152x[5];
setup[setup_count].delay = aha152x[6];
setup[setup_count].ext_trans = aha152x[7];
-#if defined(AHA152X_DEBUG)
- setup[setup_count].debug = aha152x[8];
-#endif
- } else if(io[0]!=0 || irq[0]!=0) {
+ } else if (io[0] != 0 || irq[0] != 0) {
if(io[0]!=0) setup[setup_count].io_port = io[0];
if(irq[0]!=0) setup[setup_count].irq = irq[0];
- setup[setup_count].scsiid = scsiid[0];
- setup[setup_count].reconnect = reconnect[0];
- setup[setup_count].parity = parity[0];
- setup[setup_count].synchronous = sync[0];
- setup[setup_count].delay = delay[0];
- setup[setup_count].ext_trans = exttrans[0];
-#if defined(AHA152X_DEBUG)
- setup[setup_count].debug = debug[0];
-#endif
+ setup[setup_count].scsiid = scsiid[0];
+ setup[setup_count].reconnect = reconnect[0];
+ setup[setup_count].parity = parity[0];
+ setup[setup_count].synchronous = sync[0];
+ setup[setup_count].delay = delay[0];
+ setup[setup_count].ext_trans = exttrans[0];
}
- if (checksetup(&setup[setup_count]))
+ if (checksetup(&setup[setup_count]))
setup_count++;
else
printk(KERN_ERR "aha152x: invalid module params io=0x%x, irq=%d,scsiid=%d,reconnect=%d,parity=%d,sync=%d,delay=%d,exttrans=%d\n",
setup[setup_count].synchronous = aha152x1[5];
setup[setup_count].delay = aha152x1[6];
setup[setup_count].ext_trans = aha152x1[7];
-#if defined(AHA152X_DEBUG)
- setup[setup_count].debug = aha152x1[8];
-#endif
- } else if(io[1]!=0 || irq[1]!=0) {
+ } else if (io[1] != 0 || irq[1] != 0) {
if(io[1]!=0) setup[setup_count].io_port = io[1];
if(irq[1]!=0) setup[setup_count].irq = irq[1];
- setup[setup_count].scsiid = scsiid[1];
- setup[setup_count].reconnect = reconnect[1];
- setup[setup_count].parity = parity[1];
- setup[setup_count].synchronous = sync[1];
- setup[setup_count].delay = delay[1];
- setup[setup_count].ext_trans = exttrans[1];
-#if defined(AHA152X_DEBUG)
- setup[setup_count].debug = debug[1];
-#endif
+ setup[setup_count].scsiid = scsiid[1];
+ setup[setup_count].reconnect = reconnect[1];
+ setup[setup_count].parity = parity[1];
+ setup[setup_count].synchronous = sync[1];
+ setup[setup_count].delay = delay[1];
+ setup[setup_count].ext_trans = exttrans[1];
}
if (checksetup(&setup[setup_count]))
setup_count++;
setup[setup_count].synchronous = 1;
setup[setup_count].delay = DELAY_DEFAULT;
setup[setup_count].ext_trans = 0;
-#if defined(AHA152X_DEBUG)
- setup[setup_count].debug = DEBUG_DEFAULT;
-#endif
#if defined(__ISAPNP__)
pnpdev[setup_count] = dev;
#endif
setup[setup_count].synchronous = conf.cf_syncneg;
setup[setup_count].delay = DELAY_DEFAULT;
setup[setup_count].ext_trans = 0;
-#if defined(AHA152X_DEBUG)
- setup[setup_count].debug = DEBUG_DEFAULT;
-#endif
setup_count++;
}
#if !defined(MODULE)
static int __init aha152x_setup(char *str)
{
-#if defined(AHA152X_DEBUG)
- int ints[11];
-#else
int ints[10];
-#endif
+
get_options(str, ARRAY_SIZE(ints), ints);
if(setup_count>=ARRAY_SIZE(setup)) {
setup[setup_count].synchronous = ints[0] >= 6 ? ints[6] : 1;
setup[setup_count].delay = ints[0] >= 7 ? ints[7] : DELAY_DEFAULT;
setup[setup_count].ext_trans = ints[0] >= 8 ? ints[8] : 0;
-#if defined(AHA152X_DEBUG)
- setup[setup_count].debug = ints[0] >= 9 ? ints[9] : DEBUG_DEFAULT;
- if (ints[0] > 9) {
- printk(KERN_NOTICE "aha152x: usage: aha152x=<IOBASE>[,<IRQ>[,<SCSI ID>"
- "[,<RECONNECT>[,<PARITY>[,<SYNCHRONOUS>[,<DELAY>[,<EXT_TRANS>[,<DEBUG>]]]]]]]]\n");
-#else
if (ints[0] > 8) { /*}*/
printk(KERN_NOTICE "aha152x: usage: aha152x=<IOBASE>[,<IRQ>[,<SCSI ID>"
"[,<RECONNECT>[,<PARITY>[,<SYNCHRONOUS>[,<DELAY>[,<EXT_TRANS>]]]]]]]\n");
-#endif
} else {
setup_count++;
return 0;
* quiet as possible...
*/
- return 0;
+ return SUCCESS;
}
static struct scsi_host_template aha1740_template = {
.slave_configure = ahd_linux_slave_configure,
.target_alloc = ahd_linux_target_alloc,
.target_destroy = ahd_linux_target_destroy,
+ .use_blk_tags = 1,
};
/******************************** Bus DMA *************************************/
switch ((dev->flags & (AHD_DEV_Q_BASIC|AHD_DEV_Q_TAGGED))) {
case AHD_DEV_Q_BASIC:
- scsi_set_tag_type(sdev, MSG_SIMPLE_TASK);
- scsi_activate_tcq(sdev, dev->openings + dev->active);
- break;
case AHD_DEV_Q_TAGGED:
- scsi_set_tag_type(sdev, MSG_ORDERED_TASK);
- scsi_activate_tcq(sdev, dev->openings + dev->active);
+ scsi_change_queue_depth(sdev,
+ dev->openings + dev->active);
break;
default:
/*
* serially on the controller/device. This should
* remove some latency.
*/
- scsi_deactivate_tcq(sdev, 1);
+ scsi_change_queue_depth(sdev, 1);
break;
}
}
}
if ((dev->flags & (AHD_DEV_Q_TAGGED|AHD_DEV_Q_BASIC)) != 0) {
- int msg_bytes;
- uint8_t tag_msgs[2];
-
- msg_bytes = scsi_populate_tag_msg(cmd, tag_msgs);
- if (msg_bytes && tag_msgs[0] != MSG_SIMPLE_TASK) {
- hscb->control |= tag_msgs[0];
- if (tag_msgs[0] == MSG_ORDERED_TASK)
- dev->commands_since_idle_or_otag = 0;
- } else
if (dev->commands_since_idle_or_otag == AHD_OTAG_THRESH
&& (dev->flags & AHD_DEV_Q_TAGGED) != 0) {
hscb->control |= MSG_ORDERED_TASK;
.slave_configure = ahc_linux_slave_configure,
.target_alloc = ahc_linux_target_alloc,
.target_destroy = ahc_linux_target_destroy,
+ .use_blk_tags = 1,
};
/**************************** Tasklet Handler *********************************/
}
switch ((dev->flags & (AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED))) {
case AHC_DEV_Q_BASIC:
- scsi_set_tag_type(sdev, MSG_SIMPLE_TAG);
- scsi_activate_tcq(sdev, dev->openings + dev->active);
- break;
case AHC_DEV_Q_TAGGED:
- scsi_set_tag_type(sdev, MSG_ORDERED_TAG);
- scsi_activate_tcq(sdev, dev->openings + dev->active);
- break;
+ scsi_change_queue_depth(sdev,
+ dev->openings + dev->active);
default:
/*
* We allow the OS to queue 2 untagged transactions to
* serially on the controller/device. This should
* remove some latency.
*/
- scsi_deactivate_tcq(sdev, 2);
+ scsi_change_queue_depth(sdev, 2);
break;
}
}
* we are storing a full busy target *lun*
* table in SCB space.
*/
- if (!blk_rq_tagged(cmd->request)
+ if (!(cmd->flags & SCMD_TAGGED)
&& (ahc->features & AHC_SCB_BTT) == 0) {
int target_offset;
}
if ((dev->flags & (AHC_DEV_Q_TAGGED|AHC_DEV_Q_BASIC)) != 0) {
- int msg_bytes;
- uint8_t tag_msgs[2];
-
- msg_bytes = scsi_populate_tag_msg(cmd, tag_msgs);
- if (msg_bytes && tag_msgs[0] != MSG_SIMPLE_TASK) {
- hscb->control |= tag_msgs[0];
- if (tag_msgs[0] == MSG_ORDERED_TASK)
- dev->commands_since_idle_or_otag = 0;
- } else if (dev->commands_since_idle_or_otag == AHC_OTAG_THRESH
+ if (dev->commands_since_idle_or_otag == AHC_OTAG_THRESH
&& (dev->flags & AHC_DEV_Q_TAGGED) != 0) {
hscb->control |= MSG_ORDERED_TASK;
dev->commands_since_idle_or_otag = 0;
void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id);
-int asd_execute_task(struct sas_task *, int num, gfp_t gfp_flags);
+int asd_execute_task(struct sas_task *task, gfp_t gfp_flags);
void asd_set_dmamode(struct domain_device *dev);
* Case A: we can send the whole batch at once. Increment "pending"
* in the beginning of this function, when it is checked, in order to
* eliminate races when this function is called by multiple processes.
- * Case B: should never happen if the managing layer considers
- * lldd_queue_size.
+ * Case B: should never happen.
*/
int asd_post_ascb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb,
int num)
"\tEnable(1) or disable(0) using PCI MSI.\n"
"\tDefault: 0");
-static int lldd_max_execute_num = 0;
-module_param_named(collector, lldd_max_execute_num, int, S_IRUGO);
-MODULE_PARM_DESC(collector, "\n"
- "\tIf greater than one, tells the SAS Layer to run in Task Collector\n"
- "\tMode. If 1 or 0, tells the SAS Layer to run in Direct Mode.\n"
- "\tThe aic94xx SAS LLDD supports both modes.\n"
- "\tDefault: 0 (Direct Mode).\n");
-
static struct scsi_transport_template *aic94xx_transport_template;
static int asd_scan_finished(struct Scsi_Host *, unsigned long);
static void asd_scan_start(struct Scsi_Host *);
.eh_bus_reset_handler = sas_eh_bus_reset_handler,
.target_destroy = sas_target_destroy,
.ioctl = sas_ioctl,
+ .use_blk_tags = 1,
+ .track_queue_depth = 1,
};
static int asd_map_memio(struct asd_ha_struct *asd_ha)
asd_ha->sas_ha.sas_port= sas_ports;
asd_ha->sas_ha.num_phys= ASD_MAX_PHYS;
- asd_ha->sas_ha.lldd_queue_size = asd_ha->seq.can_queue;
- asd_ha->sas_ha.lldd_max_execute_num = lldd_max_execute_num;
-
return sas_register_ha(&asd_ha->sas_ha);
}
return res;
}
-int asd_execute_task(struct sas_task *task, const int num,
- gfp_t gfp_flags)
+int asd_execute_task(struct sas_task *task, gfp_t gfp_flags)
{
int res = 0;
LIST_HEAD(alist);
struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha;
unsigned long flags;
- res = asd_can_queue(asd_ha, num);
+ res = asd_can_queue(asd_ha, 1);
if (res)
return res;
- res = num;
+ res = 1;
ascb = asd_ascb_alloc_list(asd_ha, &res, gfp_flags);
if (res) {
res = -ENOMEM;
list_for_each_entry(a, &alist, list) {
a->uldd_task = t;
t->lldd_task = a;
- t = list_entry(t->list.next, struct sas_task, list);
+ break;
}
list_for_each_entry(a, &alist, list) {
t = a->uldd_task;
}
list_del_init(&alist);
- res = asd_post_ascb_list(asd_ha, ascb, num);
+ res = asd_post_ascb_list(asd_ha, ascb, 1);
if (unlikely(res)) {
a = NULL;
__list_add(&alist, ascb->list.prev, &ascb->list);
out_err:
if (ascb)
asd_ascb_free_list(ascb);
- asd_can_dequeue(asd_ha, num);
+ asd_can_dequeue(asd_ha, 1);
return res;
}
--- /dev/null
+/*
+ * AMD am53c974 driver.
+ * Copyright (c) 2014 Hannes Reinecke, SUSE Linux GmbH
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+#include <scsi/scsi_host.h>
+
+#include "esp_scsi.h"
+
+#define DRV_MODULE_NAME "am53c974"
+#define DRV_MODULE_VERSION "1.00"
+
+static bool am53c974_debug;
+static bool am53c974_fenab = true;
+
+#define esp_dma_log(f, a...) \
+ do { \
+ if (am53c974_debug) \
+ shost_printk(KERN_DEBUG, esp->host, f, ##a); \
+ } while (0)
+
+#define ESP_DMA_CMD 0x10
+#define ESP_DMA_STC 0x11
+#define ESP_DMA_SPA 0x12
+#define ESP_DMA_WBC 0x13
+#define ESP_DMA_WAC 0x14
+#define ESP_DMA_STATUS 0x15
+#define ESP_DMA_SMDLA 0x16
+#define ESP_DMA_WMAC 0x17
+
+#define ESP_DMA_CMD_IDLE 0x00
+#define ESP_DMA_CMD_BLAST 0x01
+#define ESP_DMA_CMD_ABORT 0x02
+#define ESP_DMA_CMD_START 0x03
+#define ESP_DMA_CMD_MASK 0x03
+#define ESP_DMA_CMD_DIAG 0x04
+#define ESP_DMA_CMD_MDL 0x10
+#define ESP_DMA_CMD_INTE_P 0x20
+#define ESP_DMA_CMD_INTE_D 0x40
+#define ESP_DMA_CMD_DIR 0x80
+
+#define ESP_DMA_STAT_PWDN 0x01
+#define ESP_DMA_STAT_ERROR 0x02
+#define ESP_DMA_STAT_ABORT 0x04
+#define ESP_DMA_STAT_DONE 0x08
+#define ESP_DMA_STAT_SCSIINT 0x10
+#define ESP_DMA_STAT_BCMPLT 0x20
+
+/* EEPROM is accessed with 16-bit values */
+#define DC390_EEPROM_READ 0x80
+#define DC390_EEPROM_LEN 0x40
+
+/*
+ * DC390 EEPROM
+ *
+ * 8 * 4 bytes of per-device options
+ * followed by HBA specific options
+ */
+
+/* Per-device options */
+#define DC390_EE_MODE1 0x00
+#define DC390_EE_SPEED 0x01
+
+/* HBA-specific options */
+#define DC390_EE_ADAPT_SCSI_ID 0x40
+#define DC390_EE_MODE2 0x41
+#define DC390_EE_DELAY 0x42
+#define DC390_EE_TAG_CMD_NUM 0x43
+
+#define DC390_EE_MODE1_PARITY_CHK 0x01
+#define DC390_EE_MODE1_SYNC_NEGO 0x02
+#define DC390_EE_MODE1_EN_DISC 0x04
+#define DC390_EE_MODE1_SEND_START 0x08
+#define DC390_EE_MODE1_TCQ 0x10
+
+#define DC390_EE_MODE2_MORE_2DRV 0x01
+#define DC390_EE_MODE2_GREATER_1G 0x02
+#define DC390_EE_MODE2_RST_SCSI_BUS 0x04
+#define DC390_EE_MODE2_ACTIVE_NEGATION 0x08
+#define DC390_EE_MODE2_NO_SEEK 0x10
+#define DC390_EE_MODE2_LUN_CHECK 0x20
+
+struct pci_esp_priv {
+ struct esp *esp;
+ u8 dma_status;
+};
+
+static void pci_esp_dma_drain(struct esp *esp);
+
+static inline struct pci_esp_priv *pci_esp_get_priv(struct esp *esp)
+{
+ struct pci_dev *pdev = esp->dev;
+
+ return pci_get_drvdata(pdev);
+}
+
+static void pci_esp_write8(struct esp *esp, u8 val, unsigned long reg)
+{
+ iowrite8(val, esp->regs + (reg * 4UL));
+}
+
+static u8 pci_esp_read8(struct esp *esp, unsigned long reg)
+{
+ return ioread8(esp->regs + (reg * 4UL));
+}
+
+static void pci_esp_write32(struct esp *esp, u32 val, unsigned long reg)
+{
+ return iowrite32(val, esp->regs + (reg * 4UL));
+}
+
+static dma_addr_t pci_esp_map_single(struct esp *esp, void *buf,
+ size_t sz, int dir)
+{
+ return pci_map_single(esp->dev, buf, sz, dir);
+}
+
+static int pci_esp_map_sg(struct esp *esp, struct scatterlist *sg,
+ int num_sg, int dir)
+{
+ return pci_map_sg(esp->dev, sg, num_sg, dir);
+}
+
+static void pci_esp_unmap_single(struct esp *esp, dma_addr_t addr,
+ size_t sz, int dir)
+{
+ pci_unmap_single(esp->dev, addr, sz, dir);
+}
+
+static void pci_esp_unmap_sg(struct esp *esp, struct scatterlist *sg,
+ int num_sg, int dir)
+{
+ pci_unmap_sg(esp->dev, sg, num_sg, dir);
+}
+
+static int pci_esp_irq_pending(struct esp *esp)
+{
+ struct pci_esp_priv *pep = pci_esp_get_priv(esp);
+
+ pep->dma_status = pci_esp_read8(esp, ESP_DMA_STATUS);
+ esp_dma_log("dma intr dreg[%02x]\n", pep->dma_status);
+
+ if (pep->dma_status & (ESP_DMA_STAT_ERROR |
+ ESP_DMA_STAT_ABORT |
+ ESP_DMA_STAT_DONE |
+ ESP_DMA_STAT_SCSIINT))
+ return 1;
+
+ return 0;
+}
+
+static void pci_esp_reset_dma(struct esp *esp)
+{
+ /* Nothing to do ? */
+}
+
+static void pci_esp_dma_drain(struct esp *esp)
+{
+ u8 resid;
+ int lim = 1000;
+
+
+ if ((esp->sreg & ESP_STAT_PMASK) == ESP_DOP ||
+ (esp->sreg & ESP_STAT_PMASK) == ESP_DIP)
+ /* Data-In or Data-Out, nothing to be done */
+ return;
+
+ while (--lim > 0) {
+ resid = pci_esp_read8(esp, ESP_FFLAGS) & ESP_FF_FBYTES;
+ if (resid <= 1)
+ break;
+ cpu_relax();
+ }
+ if (resid > 1) {
+ /* FIFO not cleared */
+ shost_printk(KERN_INFO, esp->host,
+ "FIFO not cleared, %d bytes left\n",
+ resid);
+ }
+
+ /*
+ * When there is a residual BCMPLT will never be set
+ * (obviously). But we still have to issue the BLAST
+ * command, otherwise the data will not being transferred.
+ * But we'll never know when the BLAST operation is
+ * finished. So check for some time and give up eventually.
+ */
+ lim = 1000;
+ pci_esp_write8(esp, ESP_DMA_CMD_DIR | ESP_DMA_CMD_BLAST, ESP_DMA_CMD);
+ while (pci_esp_read8(esp, ESP_DMA_STATUS) & ESP_DMA_STAT_BCMPLT) {
+ if (--lim == 0)
+ break;
+ cpu_relax();
+ }
+ pci_esp_write8(esp, ESP_DMA_CMD_DIR | ESP_DMA_CMD_IDLE, ESP_DMA_CMD);
+ esp_dma_log("DMA blast done (%d tries, %d bytes left)\n", lim, resid);
+ /* BLAST residual handling is currently untested */
+ if (WARN_ON_ONCE(resid == 1)) {
+ struct esp_cmd_entry *ent = esp->active_cmd;
+
+ ent->flags |= ESP_CMD_FLAG_RESIDUAL;
+ }
+}
+
+static void pci_esp_dma_invalidate(struct esp *esp)
+{
+ struct pci_esp_priv *pep = pci_esp_get_priv(esp);
+
+ esp_dma_log("invalidate DMA\n");
+
+ pci_esp_write8(esp, ESP_DMA_CMD_IDLE, ESP_DMA_CMD);
+ pep->dma_status = 0;
+}
+
+static int pci_esp_dma_error(struct esp *esp)
+{
+ struct pci_esp_priv *pep = pci_esp_get_priv(esp);
+
+ if (pep->dma_status & ESP_DMA_STAT_ERROR) {
+ u8 dma_cmd = pci_esp_read8(esp, ESP_DMA_CMD);
+
+ if ((dma_cmd & ESP_DMA_CMD_MASK) == ESP_DMA_CMD_START)
+ pci_esp_write8(esp, ESP_DMA_CMD_ABORT, ESP_DMA_CMD);
+
+ return 1;
+ }
+ if (pep->dma_status & ESP_DMA_STAT_ABORT) {
+ pci_esp_write8(esp, ESP_DMA_CMD_IDLE, ESP_DMA_CMD);
+ pep->dma_status = pci_esp_read8(esp, ESP_DMA_CMD);
+ return 1;
+ }
+ return 0;
+}
+
+static void pci_esp_send_dma_cmd(struct esp *esp, u32 addr, u32 esp_count,
+ u32 dma_count, int write, u8 cmd)
+{
+ struct pci_esp_priv *pep = pci_esp_get_priv(esp);
+ u32 val = 0;
+
+ BUG_ON(!(cmd & ESP_CMD_DMA));
+
+ pep->dma_status = 0;
+
+ /* Set DMA engine to IDLE */
+ if (write)
+ /* DMA write direction logic is inverted */
+ val |= ESP_DMA_CMD_DIR;
+ pci_esp_write8(esp, ESP_DMA_CMD_IDLE | val, ESP_DMA_CMD);
+
+ pci_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW);
+ pci_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED);
+ if (esp->config2 & ESP_CONFIG2_FENAB)
+ pci_esp_write8(esp, (esp_count >> 16) & 0xff, ESP_TCHI);
+
+ pci_esp_write32(esp, esp_count, ESP_DMA_STC);
+ pci_esp_write32(esp, addr, ESP_DMA_SPA);
+
+ esp_dma_log("start dma addr[%x] count[%d:%d]\n",
+ addr, esp_count, dma_count);
+
+ scsi_esp_cmd(esp, cmd);
+ /* Send DMA Start command */
+ pci_esp_write8(esp, ESP_DMA_CMD_START | val, ESP_DMA_CMD);
+}
+
+static u32 pci_esp_dma_length_limit(struct esp *esp, u32 dma_addr, u32 dma_len)
+{
+ int dma_limit = 16;
+ u32 base, end;
+
+ /*
+ * If CONFIG2_FENAB is set we can
+ * handle up to 24 bit addresses
+ */
+ if (esp->config2 & ESP_CONFIG2_FENAB)
+ dma_limit = 24;
+
+ if (dma_len > (1U << dma_limit))
+ dma_len = (1U << dma_limit);
+
+ /*
+ * Prevent crossing a 24-bit address boundary.
+ */
+ base = dma_addr & ((1U << 24) - 1U);
+ end = base + dma_len;
+ if (end > (1U << 24))
+ end = (1U <<24);
+ dma_len = end - base;
+
+ return dma_len;
+}
+
+static const struct esp_driver_ops pci_esp_ops = {
+ .esp_write8 = pci_esp_write8,
+ .esp_read8 = pci_esp_read8,
+ .map_single = pci_esp_map_single,
+ .map_sg = pci_esp_map_sg,
+ .unmap_single = pci_esp_unmap_single,
+ .unmap_sg = pci_esp_unmap_sg,
+ .irq_pending = pci_esp_irq_pending,
+ .reset_dma = pci_esp_reset_dma,
+ .dma_drain = pci_esp_dma_drain,
+ .dma_invalidate = pci_esp_dma_invalidate,
+ .send_dma_cmd = pci_esp_send_dma_cmd,
+ .dma_error = pci_esp_dma_error,
+ .dma_length_limit = pci_esp_dma_length_limit,
+};
+
+/*
+ * Read DC-390 eeprom
+ */
+static void dc390_eeprom_prepare_read(struct pci_dev *pdev, u8 cmd)
+{
+ u8 carry_flag = 1, j = 0x80, bval;
+ int i;
+
+ for (i = 0; i < 9; i++) {
+ if (carry_flag) {
+ pci_write_config_byte(pdev, 0x80, 0x40);
+ bval = 0xc0;
+ } else
+ bval = 0x80;
+
+ udelay(160);
+ pci_write_config_byte(pdev, 0x80, bval);
+ udelay(160);
+ pci_write_config_byte(pdev, 0x80, 0);
+ udelay(160);
+
+ carry_flag = (cmd & j) ? 1 : 0;
+ j >>= 1;
+ }
+}
+
+static u16 dc390_eeprom_get_data(struct pci_dev *pdev)
+{
+ int i;
+ u16 wval = 0;
+ u8 bval;
+
+ for (i = 0; i < 16; i++) {
+ wval <<= 1;
+
+ pci_write_config_byte(pdev, 0x80, 0x80);
+ udelay(160);
+ pci_write_config_byte(pdev, 0x80, 0x40);
+ udelay(160);
+ pci_read_config_byte(pdev, 0x00, &bval);
+
+ if (bval == 0x22)
+ wval |= 1;
+ }
+
+ return wval;
+}
+
+static void dc390_read_eeprom(struct pci_dev *pdev, u16 *ptr)
+{
+ u8 cmd = DC390_EEPROM_READ, i;
+
+ for (i = 0; i < DC390_EEPROM_LEN; i++) {
+ pci_write_config_byte(pdev, 0xc0, 0);
+ udelay(160);
+
+ dc390_eeprom_prepare_read(pdev, cmd++);
+ *ptr++ = dc390_eeprom_get_data(pdev);
+
+ pci_write_config_byte(pdev, 0x80, 0);
+ pci_write_config_byte(pdev, 0x80, 0);
+ udelay(160);
+ }
+}
+
+static void dc390_check_eeprom(struct esp *esp)
+{
+ u8 EEbuf[128];
+ u16 *ptr = (u16 *)EEbuf, wval = 0;
+ int i;
+
+ dc390_read_eeprom((struct pci_dev *)esp->dev, ptr);
+
+ for (i = 0; i < DC390_EEPROM_LEN; i++, ptr++)
+ wval += *ptr;
+
+ /* no Tekram EEprom found */
+ if (wval != 0x1234) {
+ struct pci_dev *pdev = esp->dev;
+ dev_printk(KERN_INFO, &pdev->dev,
+ "No valid Tekram EEprom found\n");
+ return;
+ }
+ esp->scsi_id = EEbuf[DC390_EE_ADAPT_SCSI_ID];
+ esp->num_tags = 2 << EEbuf[DC390_EE_TAG_CMD_NUM];
+ if (EEbuf[DC390_EE_MODE2] & DC390_EE_MODE2_ACTIVE_NEGATION)
+ esp->config4 |= ESP_CONFIG4_RADE | ESP_CONFIG4_RAE;
+}
+
+static int pci_esp_probe_one(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct scsi_host_template *hostt = &scsi_esp_template;
+ int err = -ENODEV;
+ struct Scsi_Host *shost;
+ struct esp *esp;
+ struct pci_esp_priv *pep;
+
+ if (pci_enable_device(pdev)) {
+ dev_printk(KERN_INFO, &pdev->dev, "cannot enable device\n");
+ return -ENODEV;
+ }
+
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ dev_printk(KERN_INFO, &pdev->dev,
+ "failed to set 32bit DMA mask\n");
+ goto fail_disable_device;
+ }
+
+ shost = scsi_host_alloc(hostt, sizeof(struct esp));
+ if (!shost) {
+ dev_printk(KERN_INFO, &pdev->dev,
+ "failed to allocate scsi host\n");
+ err = -ENOMEM;
+ goto fail_disable_device;
+ }
+
+ pep = kzalloc(sizeof(struct pci_esp_priv), GFP_KERNEL);
+ if (!pep) {
+ dev_printk(KERN_INFO, &pdev->dev,
+ "failed to allocate esp_priv\n");
+ err = -ENOMEM;
+ goto fail_host_alloc;
+ }
+
+ esp = shost_priv(shost);
+ esp->host = shost;
+ esp->dev = pdev;
+ esp->ops = &pci_esp_ops;
+ /*
+ * The am53c974 HBA has a design flaw of generating
+ * spurious DMA completion interrupts when using
+ * DMA for command submission.
+ */
+ esp->flags |= ESP_FLAG_USE_FIFO;
+ /*
+ * Enable CONFIG2_FENAB to allow for large DMA transfers
+ */
+ if (am53c974_fenab)
+ esp->config2 |= ESP_CONFIG2_FENAB;
+
+ pep->esp = esp;
+
+ if (pci_request_regions(pdev, DRV_MODULE_NAME)) {
+ dev_printk(KERN_ERR, &pdev->dev,
+ "pci memory selection failed\n");
+ goto fail_priv_alloc;
+ }
+
+ esp->regs = pci_iomap(pdev, 0, pci_resource_len(pdev, 0));
+ if (!esp->regs) {
+ dev_printk(KERN_ERR, &pdev->dev, "pci I/O map failed\n");
+ err = -EINVAL;
+ goto fail_release_regions;
+ }
+ esp->dma_regs = esp->regs;
+
+ pci_set_master(pdev);
+
+ esp->command_block = pci_alloc_consistent(pdev, 16,
+ &esp->command_block_dma);
+ if (!esp->command_block) {
+ dev_printk(KERN_ERR, &pdev->dev,
+ "failed to allocate command block\n");
+ err = -ENOMEM;
+ goto fail_unmap_regs;
+ }
+
+ err = request_irq(pdev->irq, scsi_esp_intr, IRQF_SHARED,
+ DRV_MODULE_NAME, esp);
+ if (err < 0) {
+ dev_printk(KERN_ERR, &pdev->dev, "failed to register IRQ\n");
+ goto fail_unmap_command_block;
+ }
+
+ esp->scsi_id = 7;
+ dc390_check_eeprom(esp);
+
+ shost->this_id = esp->scsi_id;
+ shost->max_id = 8;
+ shost->irq = pdev->irq;
+ shost->io_port = pci_resource_start(pdev, 0);
+ shost->n_io_port = pci_resource_len(pdev, 0);
+ shost->unique_id = shost->io_port;
+ esp->scsi_id_mask = (1 << esp->scsi_id);
+ /* Assume 40MHz clock */
+ esp->cfreq = 40000000;
+
+ pci_set_drvdata(pdev, pep);
+
+ err = scsi_esp_register(esp, &pdev->dev);
+ if (err)
+ goto fail_free_irq;
+
+ return 0;
+
+fail_free_irq:
+ free_irq(pdev->irq, esp);
+fail_unmap_command_block:
+ pci_free_consistent(pdev, 16, esp->command_block,
+ esp->command_block_dma);
+fail_unmap_regs:
+ pci_iounmap(pdev, esp->regs);
+fail_release_regions:
+ pci_release_regions(pdev);
+fail_priv_alloc:
+ kfree(pep);
+fail_host_alloc:
+ scsi_host_put(shost);
+fail_disable_device:
+ pci_disable_device(pdev);
+
+ return err;
+}
+
+static void pci_esp_remove_one(struct pci_dev *pdev)
+{
+ struct pci_esp_priv *pep = pci_get_drvdata(pdev);
+ struct esp *esp = pep->esp;
+
+ scsi_esp_unregister(esp);
+ free_irq(pdev->irq, esp);
+ pci_free_consistent(pdev, 16, esp->command_block,
+ esp->command_block_dma);
+ pci_iounmap(pdev, esp->regs);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ kfree(pep);
+
+ scsi_host_put(esp->host);
+}
+
+static struct pci_device_id am53c974_pci_tbl[] = {
+ { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_SCSI,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(pci, am53c974_pci_tbl);
+
+static struct pci_driver am53c974_driver = {
+ .name = DRV_MODULE_NAME,
+ .id_table = am53c974_pci_tbl,
+ .probe = pci_esp_probe_one,
+ .remove = pci_esp_remove_one,
+};
+
+static int __init am53c974_module_init(void)
+{
+ return pci_register_driver(&am53c974_driver);
+}
+
+static void __exit am53c974_module_exit(void)
+{
+ pci_unregister_driver(&am53c974_driver);
+}
+
+MODULE_DESCRIPTION("AM53C974 SCSI driver");
+MODULE_AUTHOR("Hannes Reinecke <hare@suse.de>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_MODULE_VERSION);
+MODULE_ALIAS("tmscsim");
+
+module_param(am53c974_debug, bool, 0644);
+MODULE_PARM_DESC(am53c974_debug, "Enable debugging");
+
+module_param(am53c974_fenab, bool, 0444);
+MODULE_PARM_DESC(am53c974_fenab, "Enable 24-bit DMA transfer sizes");
+
+module_init(am53c974_module_init);
+module_exit(am53c974_module_exit);
static const char *arcmsr_info(struct Scsi_Host *);
static irqreturn_t arcmsr_interrupt(struct AdapterControlBlock *acb);
static void arcmsr_free_irq(struct pci_dev *, struct AdapterControlBlock *);
-static int arcmsr_adjust_disk_queue_depth(struct scsi_device *sdev,
- int queue_depth, int reason)
+static int arcmsr_adjust_disk_queue_depth(struct scsi_device *sdev, int queue_depth)
{
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
-
if (queue_depth > ARCMSR_MAX_CMD_PERLUN)
queue_depth = ARCMSR_MAX_CMD_PERLUN;
- scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
- return queue_depth;
+ return scsi_change_queue_depth(sdev, queue_depth);
}
static struct scsi_host_template arcmsr_scsi_host_template = {
break;
default:
- printk(KERN_ERR "scsi%d.H: incomplete data transfer detected: result=%08X command=",
- host->host->host_no, SCpnt->result);
- __scsi_print_command(SCpnt->cmnd);
+ scmd_printk(KERN_ERR, SCpnt,
+ "incomplete data transfer detected: "
+ "result=%08X", SCpnt->result);
+ scsi_print_command(SCpnt);
acornscsi_dumpdma(host, "done");
- acornscsi_dumplog(host, SCpnt->device->id);
- SCpnt->result &= 0xffff;
- SCpnt->result |= DID_ERROR << 16;
+ acornscsi_dumplog(host, SCpnt->device->id);
+ set_host_byte(SCpnt, DID_ERROR);
}
}
}
#include <asm/ecard.h>
#include <asm/io.h>
-#include "../scsi.h"
#include <scsi/scsi_host.h>
#include <scsi/scsicam.h>
-#define AUTOSENSE
#define PSEUDO_DMA
-#define CUMANASCSI_PUBLIC_RELEASE 1
-
#define priv(host) ((struct NCR5380_hostdata *)(host)->hostdata)
#define NCR5380_local_declare() struct Scsi_Host *_instance
#define NCR5380_setup(instance) _instance = instance
#define NCR5380_write(reg, value) cumanascsi_write(_instance, reg, value)
#define NCR5380_intr cumanascsi_intr
#define NCR5380_queue_command cumanascsi_queue_command
+#define NCR5380_info cumanascsi_info
#define NCR5380_implementation_fields \
unsigned ctrl; \
{
}
-const char *cumanascsi_info(struct Scsi_Host *spnt)
-{
- return "";
-}
-
#define CTRL 0x16fc
#define STAT 0x2004
#define L(v) (((v)<<16)|((v) & 0x0000ffff))
goto out_unmap;
}
- printk("scsi%d: at port 0x%08lx irq %d",
- host->host_no, host->io_port, host->irq);
- printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
- host->can_queue, host->cmd_per_lun, CUMANASCSI_PUBLIC_RELEASE);
- printk("\nscsi%d:", host->host_no);
- NCR5380_print_options(host);
- printk("\n");
-
ret = scsi_add_host(host, &ec->dev);
if (ret)
goto out_free_irq;
fas216_do_log(info, '0' + SCpnt->device->id, fmt, args);
va_end(args);
- printk(" CDB: ");
- __scsi_print_command(SCpnt->cmnd);
+ scsi_print_command(SCpnt);
}
static void
break;
default:
- printk(KERN_ERR "scsi%d.%c: incomplete data transfer "
- "detected: res=%08X ptr=%p len=%X CDB: ",
- info->host->host_no, '0' + SCpnt->device->id,
- SCpnt->result, info->scsi.SCp.ptr,
- info->scsi.SCp.this_residual);
- __scsi_print_command(SCpnt->cmnd);
- SCpnt->result &= ~(255 << 16);
- SCpnt->result |= DID_BAD_TARGET << 16;
+ scmd_printk(KERN_ERR, SCpnt,
+ "incomplete data transfer detected: res=%08X ptr=%p len=%X\n",
+ SCpnt->result, info->scsi.SCp.ptr,
+ info->scsi.SCp.this_residual);
+ scsi_print_command(SCpnt);
+ set_host_byte(SCpnt, DID_ERROR);
goto request_sense;
}
}
* to transfer, we should not have a valid pointer.
*/
if (info->scsi.SCp.ptr && info->scsi.SCp.this_residual == 0) {
- printk("scsi%d.%c: zero bytes left to transfer, but "
- "buffer pointer still valid: ptr=%p len=%08x CDB: ",
- info->host->host_no, '0' + SCpnt->device->id,
- info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
+ scmd_printk(KERN_INFO, SCpnt,
+ "zero bytes left to transfer, but buffer pointer still valid: ptr=%p len=%08x\n",
+ info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
info->scsi.SCp.ptr = NULL;
- __scsi_print_command(SCpnt->cmnd);
+ scsi_print_command(SCpnt);
}
/*
info->stats.aborts += 1;
- printk(KERN_WARNING "scsi%d: abort command ", info->host->host_no);
- __scsi_print_command(SCpnt->cmnd);
+ scmd_printk(KERN_WARNING, SCpnt, "abort command\n");
print_debug_list();
fas216_dumpstate(info);
- printk(KERN_WARNING "scsi%d: abort %p ", info->host->host_no, SCpnt);
-
switch (fas216_find_command(info, SCpnt)) {
/*
* We found the command, and cleared it out. Either
* target, or the busylun bit is not set.
*/
case res_success:
- printk("success\n");
+ scmd_printk(KERN_WARNING, SCpnt, "abort %p success\n", SCpnt);
result = SUCCESS;
break;
* if the bus is free.
*/
case res_hw_abort:
-
/*
* We are unable to abort the command for some reason.
*/
default:
case res_failed:
- printk("failed\n");
+ scmd_printk(KERN_WARNING, SCpnt, "abort %p failed\n", SCpnt);
break;
}
fas216_checkmagic(info);
- printk("scsi%d.%c: %s: resetting host\n",
- info->host->host_no, '0' + SCpnt->device->id, __func__);
+ fas216_log(info, LOG_ERROR, "resetting host");
/*
* Reset the SCSI chip.
#include <asm/ecard.h>
#include <asm/io.h>
-#include "../scsi.h"
#include <scsi/scsi_host.h>
-#define AUTOSENSE
/*#define PSEUDO_DMA*/
-
-#define OAKSCSI_PUBLIC_RELEASE 1
#define DONT_USE_INTR
#define priv(host) ((struct NCR5380_hostdata *)(host)->hostdata)
#define NCR5380_read(reg) readb(_base + ((reg) << 2))
#define NCR5380_write(reg, value) writeb(value, _base + ((reg) << 2))
-#define NCR5380_intr oakscsi_intr
#define NCR5380_queue_command oakscsi_queue_command
+#define NCR5380_info oakscsi_info
#define NCR5380_show_info oakscsi_show_info
-#define NCR5380_write_info oakscsi_write_info
#define NCR5380_implementation_fields \
void __iomem *base
#undef START_DMA_INITIATOR_RECEIVE_REG
#define START_DMA_INITIATOR_RECEIVE_REG (128 + 7)
-const char * oakscsi_info (struct Scsi_Host *spnt)
-{
- return "";
-}
-
#define STAT ((128 + 16) << 2)
#define DATA ((128 + 8) << 2)
static struct scsi_host_template oakscsi_template = {
.module = THIS_MODULE,
.show_info = oakscsi_show_info,
- .write_info = oakscsi_write_info,
.name = "Oak 16-bit SCSI",
.info = oakscsi_info,
.queuecommand = oakscsi_queue_command,
goto unreg;
}
- host->irq = IRQ_NONE;
+ host->irq = NO_IRQ;
host->n_io_port = 255;
NCR5380_init(host, 0);
- printk("scsi%d: at port 0x%08lx irqs disabled",
- host->host_no, host->io_port);
- printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
- host->can_queue, host->cmd_per_lun, OAKSCSI_PUBLIC_RELEASE);
- printk("\nscsi%d:", host->host_no);
- NCR5380_print_options(host);
- printk("\n");
-
ret = scsi_add_host(host, &ec->dev);
if (ret)
goto out_unmap;
* drew@colorado.edu
* +1 (303) 666-5836
*
- * DISTRIBUTION RELEASE 6.
- *
* For more information, please consult
*
* NCR 5380 Family
* 1. Test linked command handling code after Eric is ready with
* the high level code.
*/
+
+/* Adapted for the sun3 by Sam Creasey. */
+
#include <scsi/scsi_dbg.h>
#include <scsi/scsi_transport_spi.h>
/*
* Design
- * Issues :
- *
- * The other Linux SCSI drivers were written when Linux was Intel PC-only,
- * and specifically for each board rather than each chip. This makes their
- * adaptation to platforms like the Mac (Some of which use NCR5380's)
- * more difficult than it has to be.
*
- * Also, many of the SCSI drivers were written before the command queuing
- * routines were implemented, meaning their implementations of queued
- * commands were hacked on rather than designed in from the start.
- *
- * When I designed the Linux SCSI drivers I figured that
- * while having two different SCSI boards in a system might be useful
- * for debugging things, two of the same type wouldn't be used.
- * Well, I was wrong and a number of users have mailed me about running
- * multiple high-performance SCSI boards in a server.
- *
- * Finally, when I get questions from users, I have no idea what
- * revision of my driver they are running.
- *
- * This driver attempts to address these problems :
* This is a generic 5380 driver. To use it on a different platform,
* one simply writes appropriate system specific macros (ie, data
* transfer - some PC's will use the I/O bus, 68K's must use
* allowing multiple commands to propagate all the way to a SCSI-II device
* while a command is already executing.
*
- * To solve the multiple-boards-in-the-same-system problem,
- * there is a separate instance structure for each instance
- * of a 5380 in the system. So, multiple NCR5380 drivers will
- * be able to coexist with appropriate changes to the high level
- * SCSI code.
- *
- * A NCR5380_PUBLIC_REVISION macro is provided, with the release
- * number (updated for each public release) printed by the
- * NCR5380_print_options command, which should be called from the
- * wrapper detect function, so that I know what release of the driver
- * users are using.
*
* Issues specific to the NCR5380 :
*
* Architecture :
*
* At the heart of the design is a coroutine, NCR5380_main,
- * which is started when not running by the interrupt handler,
- * timer, and queue command function. It attempts to establish
- * I_T_L or I_T_L_Q nexuses by removing the commands from the
- * issue queue and calling NCR5380_select() if a nexus
- * is not established.
+ * which is started from a workqueue for each NCR5380 host in the
+ * system. It attempts to establish I_T_L or I_T_L_Q nexuses by
+ * removing the commands from the issue queue and calling
+ * NCR5380_select() if a nexus is not established.
*
* Once a nexus is established, the NCR5380_information_transfer()
* phase goes through the various phases as instructed by the target.
* if the target goes into MSG IN and sends a DISCONNECT message,
* the command structure is placed into the per instance disconnected
- * queue, and NCR5380_main tries to find more work. If USLEEP
- * was defined, and the target is idle for too long, the system
- * will try to sleep.
+ * queue, and NCR5380_main tries to find more work. If the target is
+ * idle for too long, the system will try to sleep.
*
* If a command has disconnected, eventually an interrupt will trigger,
* calling NCR5380_intr() which will in turn call NCR5380_reselect
* AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
* for commands that return with a CHECK CONDITION status.
*
+ * DIFFERENTIAL - if defined, NCR53c81 chips will use external differential
+ * transceivers.
+ *
* LINKED - if defined, linked commands are supported.
*
* REAL_DMA - if defined, REAL DMA is used during the data transfer phases.
*
* NCR5380_write(register, value) - write to the specific register
*
+ * NCR5380_implementation_fields - additional fields needed for this
+ * specific implementation of the NCR5380
+ *
* Either real DMA *or* pseudo DMA may be implemented
* REAL functions :
* NCR5380_REAL_DMA should be defined if real DMA is to be used.
* NCR5380_pwrite(instance, src, count)
* NCR5380_pread(instance, dst, count);
*
- * If nothing specific to this implementation needs doing (ie, with external
- * hardware), you must also define
- *
- * NCR5380_queue_command
- * NCR5380_reset
- * NCR5380_abort
- * NCR5380_proc_info
- *
- * to be the global entry points into the specific driver, ie
- * #define NCR5380_queue_command t128_queue_command.
- *
- * If this is not done, the routines will be defined as static functions
- * with the NCR5380* names and the user must provide a globally
- * accessible wrapper function.
- *
* The generic driver is initialized by calling NCR5380_init(instance),
* after setting the appropriate host specific fields and ID. If the
* driver wishes to autoprobe for an IRQ line, the NCR5380_probe_irq(instance,
- * possible) function may be used. Before the specific driver initialization
- * code finishes, NCR5380_print_options should be called.
+ * possible) function may be used.
*/
-static struct Scsi_Host *first_instance = NULL;
-static struct scsi_host_template *the_template = NULL;
-
/* Macros ease life... :-) */
#define SETUP_HOSTDATA(in) \
struct NCR5380_hostdata *hostdata = \
(struct NCR5380_hostdata *)(in)->hostdata
#define HOSTDATA(in) ((struct NCR5380_hostdata *)(in)->hostdata)
-#define NEXT(cmd) ((Scsi_Cmnd *)(cmd)->host_scribble)
+#define NEXT(cmd) ((struct scsi_cmnd *)(cmd)->host_scribble)
#define SET_NEXT(cmd,next) ((cmd)->host_scribble = (void *)(next))
-#define NEXTADDR(cmd) ((Scsi_Cmnd **)&(cmd)->host_scribble)
+#define NEXTADDR(cmd) ((struct scsi_cmnd **)&(cmd)->host_scribble)
#define HOSTNO instance->host_no
#define H_NO(cmd) (cmd)->device->host->host_no
* important: the tag bit must be cleared before 'nr_allocated' is decreased.
*/
-/* -1 for TAG_NONE is not possible with unsigned char cmd->tag */
-#undef TAG_NONE
-#define TAG_NONE 0xff
-
-typedef struct {
- DECLARE_BITMAP(allocated, MAX_TAGS);
- int nr_allocated;
- int queue_size;
-} TAG_ALLOC;
-
-static TAG_ALLOC TagAlloc[8][8]; /* 8 targets and 8 LUNs */
-
-
-static void __init init_tags(void)
+static void __init init_tags(struct NCR5380_hostdata *hostdata)
{
int target, lun;
- TAG_ALLOC *ta;
+ struct tag_alloc *ta;
- if (!setup_use_tagged_queuing)
+ if (!(hostdata->flags & FLAG_TAGGED_QUEUING))
return;
for (target = 0; target < 8; ++target) {
for (lun = 0; lun < 8; ++lun) {
- ta = &TagAlloc[target][lun];
+ ta = &hostdata->TagAlloc[target][lun];
bitmap_zero(ta->allocated, MAX_TAGS);
ta->nr_allocated = 0;
/* At the beginning, assume the maximum queue size we could
* conditions.
*/
-static int is_lun_busy(Scsi_Cmnd *cmd, int should_be_tagged)
+static int is_lun_busy(struct scsi_cmnd *cmd, int should_be_tagged)
{
u8 lun = cmd->device->lun;
SETUP_HOSTDATA(cmd->device->host);
if (hostdata->busy[cmd->device->id] & (1 << lun))
return 1;
if (!should_be_tagged ||
- !setup_use_tagged_queuing || !cmd->device->tagged_supported)
+ !(hostdata->flags & FLAG_TAGGED_QUEUING) ||
+ !cmd->device->tagged_supported)
return 0;
- if (TagAlloc[cmd->device->id][lun].nr_allocated >=
- TagAlloc[cmd->device->id][lun].queue_size) {
+ if (hostdata->TagAlloc[scmd_id(cmd)][lun].nr_allocated >=
+ hostdata->TagAlloc[scmd_id(cmd)][lun].queue_size) {
dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %d: no free tags\n",
H_NO(cmd), cmd->device->id, lun);
return 1;
* untagged.
*/
-static void cmd_get_tag(Scsi_Cmnd *cmd, int should_be_tagged)
+static void cmd_get_tag(struct scsi_cmnd *cmd, int should_be_tagged)
{
u8 lun = cmd->device->lun;
SETUP_HOSTDATA(cmd->device->host);
* an untagged command.
*/
if (!should_be_tagged ||
- !setup_use_tagged_queuing || !cmd->device->tagged_supported) {
+ !(hostdata->flags & FLAG_TAGGED_QUEUING) ||
+ !cmd->device->tagged_supported) {
cmd->tag = TAG_NONE;
hostdata->busy[cmd->device->id] |= (1 << lun);
dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %d now allocated by untagged "
"command\n", H_NO(cmd), cmd->device->id, lun);
} else {
- TAG_ALLOC *ta = &TagAlloc[cmd->device->id][lun];
+ struct tag_alloc *ta = &hostdata->TagAlloc[scmd_id(cmd)][lun];
cmd->tag = find_first_zero_bit(ta->allocated, MAX_TAGS);
set_bit(cmd->tag, ta->allocated);
* unlock the LUN.
*/
-static void cmd_free_tag(Scsi_Cmnd *cmd)
+static void cmd_free_tag(struct scsi_cmnd *cmd)
{
u8 lun = cmd->device->lun;
SETUP_HOSTDATA(cmd->device->host);
printk(KERN_NOTICE "scsi%d: trying to free bad tag %d!\n",
H_NO(cmd), cmd->tag);
} else {
- TAG_ALLOC *ta = &TagAlloc[cmd->device->id][lun];
+ struct tag_alloc *ta = &hostdata->TagAlloc[scmd_id(cmd)][lun];
clear_bit(cmd->tag, ta->allocated);
ta->nr_allocated--;
dprintk(NDEBUG_TAGS, "scsi%d: freed tag %d for target %d lun %d\n",
}
-static void free_all_tags(void)
+static void free_all_tags(struct NCR5380_hostdata *hostdata)
{
int target, lun;
- TAG_ALLOC *ta;
+ struct tag_alloc *ta;
- if (!setup_use_tagged_queuing)
+ if (!(hostdata->flags & FLAG_TAGGED_QUEUING))
return;
for (target = 0; target < 8; ++target) {
for (lun = 0; lun < 8; ++lun) {
- ta = &TagAlloc[target][lun];
+ ta = &hostdata->TagAlloc[target][lun];
bitmap_zero(ta->allocated, MAX_TAGS);
ta->nr_allocated = 0;
}
/*
- * Function: void merge_contiguous_buffers( Scsi_Cmnd *cmd )
+ * Function: void merge_contiguous_buffers( struct scsi_cmnd *cmd )
*
* Purpose: Try to merge several scatter-gather requests into one DMA
* transfer. This is possible if the scatter buffers lie on
* physical contiguous addresses.
*
- * Parameters: Scsi_Cmnd *cmd
+ * Parameters: struct scsi_cmnd *cmd
* The command to work on. The first scatter buffer's data are
* assumed to be already transferred into ptr/this_residual.
*/
-static void merge_contiguous_buffers(Scsi_Cmnd *cmd)
+static void merge_contiguous_buffers(struct scsi_cmnd *cmd)
{
+#if !defined(CONFIG_SUN3)
unsigned long endaddr;
#if (NDEBUG & NDEBUG_MERGING)
unsigned long oldlen = cmd->SCp.this_residual;
dprintk(NDEBUG_MERGING, "merged %d buffers from %p, new length %08x\n",
cnt, cmd->SCp.ptr, cmd->SCp.this_residual);
#endif
+#endif /* !defined(CONFIG_SUN3) */
}
-/*
- * Function : void initialize_SCp(Scsi_Cmnd *cmd)
+/**
+ * initialize_SCp - init the scsi pointer field
+ * @cmd: command block to set up
*
- * Purpose : initialize the saved data pointers for cmd to point to the
- * start of the buffer.
- *
- * Inputs : cmd - Scsi_Cmnd structure to have pointers reset.
+ * Set up the internal fields in the SCSI command.
*/
-static inline void initialize_SCp(Scsi_Cmnd *cmd)
+static inline void initialize_SCp(struct scsi_cmnd *cmd)
{
/*
* Initialize the Scsi Pointer field so that all of the commands in the
{0, NULL}
};
-/*
- * Function : void NCR5380_print(struct Scsi_Host *instance)
+/**
+ * NCR5380_print - print scsi bus signals
+ * @instance: adapter state to dump
*
- * Purpose : print the SCSI bus signals for debugging purposes
- *
- * Input : instance - which NCR5380
+ * Print the SCSI bus signals for debugging purposes
*/
static void NCR5380_print(struct Scsi_Host *instance)
{PHASE_UNKNOWN, "UNKNOWN"}
};
-/*
- * Function : void NCR5380_print_phase(struct Scsi_Host *instance)
+/**
+ * NCR5380_print_phase - show SCSI phase
+ * @instance: adapter to dump
*
- * Purpose : print the current SCSI phase for debugging purposes
+ * Print the current SCSI phase for debugging purposes
*
- * Input : instance - which NCR5380
+ * Locks: none
*/
static void NCR5380_print_phase(struct Scsi_Host *instance)
#include <linux/workqueue.h>
#include <linux/interrupt.h>
-static volatile int main_running;
-static DECLARE_WORK(NCR5380_tqueue, NCR5380_main);
-
-static inline void queue_main(void)
+static inline void queue_main(struct NCR5380_hostdata *hostdata)
{
- if (!main_running) {
+ if (!hostdata->main_running) {
/* If in interrupt and NCR5380_main() not already running,
queue it on the 'immediate' task queue, to be processed
immediately after the current interrupt processing has
finished. */
- schedule_work(&NCR5380_tqueue);
+ schedule_work(&hostdata->main_task);
}
/* else: nothing to do: the running NCR5380_main() will pick up
any newly queued command. */
}
-
-static inline void NCR5380_all_init(void)
-{
- static int done = 0;
- if (!done) {
- dprintk(NDEBUG_INIT, "scsi : NCR5380_all_init()\n");
- done = 1;
- }
-}
-
-
-/*
- * Function : void NCR58380_print_options (struct Scsi_Host *instance)
+/**
+ * NCR58380_info - report driver and host information
+ * @instance: relevant scsi host instance
*
- * Purpose : called by probe code indicating the NCR5380 driver
- * options that were selected.
+ * For use as the host template info() handler.
*
- * Inputs : instance, pointer to this instance. Unused.
+ * Locks: none
*/
-static void __init NCR5380_print_options(struct Scsi_Host *instance)
+static const char *NCR5380_info(struct Scsi_Host *instance)
+{
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+
+ return hostdata->info;
+}
+
+static void prepare_info(struct Scsi_Host *instance)
{
- printk(" generic options"
-#ifdef AUTOSENSE
- " AUTOSENSE"
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+
+ snprintf(hostdata->info, sizeof(hostdata->info),
+ "%s, io_port 0x%lx, n_io_port %d, "
+ "base 0x%lx, irq %d, "
+ "can_queue %d, cmd_per_lun %d, "
+ "sg_tablesize %d, this_id %d, "
+ "flags { %s}, "
+ "options { %s} ",
+ instance->hostt->name, instance->io_port, instance->n_io_port,
+ instance->base, instance->irq,
+ instance->can_queue, instance->cmd_per_lun,
+ instance->sg_tablesize, instance->this_id,
+ hostdata->flags & FLAG_TAGGED_QUEUING ? "TAGGED_QUEUING " : "",
+#ifdef DIFFERENTIAL
+ "DIFFERENTIAL "
#endif
#ifdef REAL_DMA
- " REAL DMA"
+ "REAL_DMA "
#endif
#ifdef PARITY
- " PARITY"
+ "PARITY "
#endif
#ifdef SUPPORT_TAGS
- " SCSI-2 TAGGED QUEUING"
+ "SUPPORT_TAGS "
#endif
- );
- printk(" generic release=%d", NCR5380_PUBLIC_RELEASE);
+ "");
}
-/*
- * Function : void NCR5380_print_status (struct Scsi_Host *instance)
+/**
+ * NCR5380_print_status - dump controller info
+ * @instance: controller to dump
*
- * Purpose : print commands in the various queues, called from
- * NCR5380_abort and NCR5380_debug to aid debugging.
- *
- * Inputs : instance, pointer to this instance.
+ * Print commands in the various queues, called from NCR5380_abort
+ * to aid debugging.
*/
-static void lprint_Scsi_Cmnd(Scsi_Cmnd *cmd)
+static void lprint_Scsi_Cmnd(struct scsi_cmnd *cmd)
{
int i, s;
unsigned char *command;
static void NCR5380_print_status(struct Scsi_Host *instance)
{
struct NCR5380_hostdata *hostdata;
- Scsi_Cmnd *ptr;
+ struct scsi_cmnd *ptr;
unsigned long flags;
NCR5380_dprint(NDEBUG_ANY, instance);
hostdata = (struct NCR5380_hostdata *)instance->hostdata;
- printk("\nNCR5380 core release=%d.\n", NCR5380_PUBLIC_RELEASE);
local_irq_save(flags);
printk("NCR5380: coroutine is%s running.\n",
- main_running ? "" : "n't");
+ hostdata->main_running ? "" : "n't");
if (!hostdata->connected)
printk("scsi%d: no currently connected command\n", HOSTNO);
else
- lprint_Scsi_Cmnd((Scsi_Cmnd *) hostdata->connected);
+ lprint_Scsi_Cmnd((struct scsi_cmnd *) hostdata->connected);
printk("scsi%d: issue_queue\n", HOSTNO);
- for (ptr = (Scsi_Cmnd *)hostdata->issue_queue; ptr; ptr = NEXT(ptr))
+ for (ptr = (struct scsi_cmnd *)hostdata->issue_queue; ptr; ptr = NEXT(ptr))
lprint_Scsi_Cmnd(ptr);
printk("scsi%d: disconnected_queue\n", HOSTNO);
- for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr;
+ for (ptr = (struct scsi_cmnd *) hostdata->disconnected_queue; ptr;
ptr = NEXT(ptr))
lprint_Scsi_Cmnd(ptr);
printk("\n");
}
-static void show_Scsi_Cmnd(Scsi_Cmnd *cmd, struct seq_file *m)
+static void show_Scsi_Cmnd(struct scsi_cmnd *cmd, struct seq_file *m)
{
int i, s;
unsigned char *command;
seq_printf(m, "\n");
}
-static int NCR5380_show_info(struct seq_file *m, struct Scsi_Host *instance)
+static int __maybe_unused NCR5380_show_info(struct seq_file *m,
+ struct Scsi_Host *instance)
{
struct NCR5380_hostdata *hostdata;
- Scsi_Cmnd *ptr;
+ struct scsi_cmnd *ptr;
unsigned long flags;
hostdata = (struct NCR5380_hostdata *)instance->hostdata;
- seq_printf(m, "NCR5380 core release=%d.\n", NCR5380_PUBLIC_RELEASE);
local_irq_save(flags);
seq_printf(m, "NCR5380: coroutine is%s running.\n",
- main_running ? "" : "n't");
+ hostdata->main_running ? "" : "n't");
if (!hostdata->connected)
seq_printf(m, "scsi%d: no currently connected command\n", HOSTNO);
else
- show_Scsi_Cmnd((Scsi_Cmnd *) hostdata->connected, m);
+ show_Scsi_Cmnd((struct scsi_cmnd *) hostdata->connected, m);
seq_printf(m, "scsi%d: issue_queue\n", HOSTNO);
- for (ptr = (Scsi_Cmnd *)hostdata->issue_queue; ptr; ptr = NEXT(ptr))
+ for (ptr = (struct scsi_cmnd *)hostdata->issue_queue; ptr; ptr = NEXT(ptr))
show_Scsi_Cmnd(ptr, m);
seq_printf(m, "scsi%d: disconnected_queue\n", HOSTNO);
- for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr;
+ for (ptr = (struct scsi_cmnd *) hostdata->disconnected_queue; ptr;
ptr = NEXT(ptr))
show_Scsi_Cmnd(ptr, m);
return 0;
}
-/*
- * Function : void NCR5380_init (struct Scsi_Host *instance)
+/**
+ * NCR5380_init - initialise an NCR5380
+ * @instance: adapter to configure
+ * @flags: control flags
*
- * Purpose : initializes *instance and corresponding 5380 chip.
- *
- * Inputs : instance - instantiation of the 5380 driver.
+ * Initializes *instance and corresponding 5380 chip,
+ * with flags OR'd into the initial flags value.
*
* Notes : I assume that the host, hostno, and id bits have been
- * set correctly. I don't care about the irq and other fields.
+ * set correctly. I don't care about the irq and other fields.
*
+ * Returns 0 for success
*/
static int __init NCR5380_init(struct Scsi_Host *instance, int flags)
int i;
SETUP_HOSTDATA(instance);
- NCR5380_all_init();
-
+ hostdata->host = instance;
hostdata->aborted = 0;
hostdata->id_mask = 1 << instance->this_id;
hostdata->id_higher_mask = 0;
for (i = 0; i < 8; ++i)
hostdata->busy[i] = 0;
#ifdef SUPPORT_TAGS
- init_tags();
+ init_tags(hostdata);
#endif
#if defined (REAL_DMA)
hostdata->dma_len = 0;
hostdata->connected = NULL;
hostdata->issue_queue = NULL;
hostdata->disconnected_queue = NULL;
- hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT;
+ hostdata->flags = flags;
- if (!the_template) {
- the_template = instance->hostt;
- first_instance = instance;
- }
+ INIT_WORK(&hostdata->main_task, NCR5380_main);
-#ifndef AUTOSENSE
- if ((instance->cmd_per_lun > 1) || (instance->can_queue > 1))
- printk("scsi%d: WARNING : support for multiple outstanding commands enabled\n"
- " without AUTOSENSE option, contingent allegiance conditions may\n"
- " be incorrectly cleared.\n", HOSTNO);
-#endif /* def AUTOSENSE */
+ prepare_info(instance);
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
NCR5380_write(MODE_REG, MR_BASE);
return 0;
}
+/**
+ * NCR5380_exit - remove an NCR5380
+ * @instance: adapter to remove
+ *
+ * Assumes that no more work can be queued (e.g. by NCR5380_intr).
+ */
+
static void NCR5380_exit(struct Scsi_Host *instance)
{
- /* Empty, as we didn't schedule any delayed work */
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+
+ cancel_work_sync(&hostdata->main_task);
}
-/*
- * Function : int NCR5380_queue_command (Scsi_Cmnd *cmd,
- * void (*done)(Scsi_Cmnd *))
- *
- * Purpose : enqueues a SCSI command
- *
- * Inputs : cmd - SCSI command, done - function called on completion, with
- * a pointer to the command descriptor.
- *
- * Returns : 0
- *
- * Side effects :
- * cmd is added to the per instance issue_queue, with minor
- * twiddling done to the host specific fields of cmd. If the
- * main coroutine is not running, it is restarted.
+/**
+ * NCR5380_queue_command - queue a command
+ * @instance: the relevant SCSI adapter
+ * @cmd: SCSI command
*
+ * cmd is added to the per instance issue_queue, with minor
+ * twiddling done to the host specific fields of cmd. If the
+ * main coroutine is not running, it is restarted.
*/
-static int NCR5380_queue_command_lck(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+static int NCR5380_queue_command(struct Scsi_Host *instance,
+ struct scsi_cmnd *cmd)
{
- SETUP_HOSTDATA(cmd->device->host);
- Scsi_Cmnd *tmp;
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+ struct scsi_cmnd *tmp;
unsigned long flags;
#if (NDEBUG & NDEBUG_NO_WRITE)
printk(KERN_NOTICE "scsi%d: WRITE attempted with NO_WRITE debugging flag set\n",
H_NO(cmd));
cmd->result = (DID_ERROR << 16);
- done(cmd);
+ cmd->scsi_done(cmd);
return 0;
}
#endif /* (NDEBUG & NDEBUG_NO_WRITE) */
-#ifdef NCR5380_STATS
-# if 0
- if (!hostdata->connected && !hostdata->issue_queue &&
- !hostdata->disconnected_queue) {
- hostdata->timebase = jiffies;
- }
-# endif
-# ifdef NCR5380_STAT_LIMIT
- if (scsi_bufflen(cmd) > NCR5380_STAT_LIMIT)
-# endif
- switch (cmd->cmnd[0]) {
- case WRITE:
- case WRITE_6:
- case WRITE_10:
- hostdata->time_write[cmd->device->id] -= (jiffies - hostdata->timebase);
- hostdata->bytes_write[cmd->device->id] += scsi_bufflen(cmd);
- hostdata->pendingw++;
- break;
- case READ:
- case READ_6:
- case READ_10:
- hostdata->time_read[cmd->device->id] -= (jiffies - hostdata->timebase);
- hostdata->bytes_read[cmd->device->id] += scsi_bufflen(cmd);
- hostdata->pendingr++;
- break;
- }
-#endif
-
/*
* We use the host_scribble field as a pointer to the next command
* in a queue
*/
SET_NEXT(cmd, NULL);
- cmd->scsi_done = done;
-
cmd->result = 0;
/*
* sense data is only guaranteed to be valid while the condition exists.
*/
- local_irq_save(flags);
/* ++guenther: now that the issue queue is being set up, we can lock ST-DMA.
* Otherwise a running NCR5380_main may steal the lock.
* Lock before actually inserting due to fairness reasons explained in
* because also a timer int can trigger an abort or reset, which would
* alter queues and touch the lock.
*/
- if (!IS_A_TT()) {
- /* perhaps stop command timer here */
- falcon_get_lock();
- /* perhaps restart command timer here */
- }
+ if (!NCR5380_acquire_dma_irq(instance))
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ local_irq_save(flags);
+
+ /*
+ * Insert the cmd into the issue queue. Note that REQUEST SENSE
+ * commands are added to the head of the queue since any command will
+ * clear the contingent allegiance condition that exists and the
+ * sense data is only guaranteed to be valid while the condition exists.
+ */
+
if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
LIST(cmd, hostdata->issue_queue);
SET_NEXT(cmd, hostdata->issue_queue);
hostdata->issue_queue = cmd;
} else {
- for (tmp = (Scsi_Cmnd *)hostdata->issue_queue;
+ for (tmp = (struct scsi_cmnd *)hostdata->issue_queue;
NEXT(tmp); tmp = NEXT(tmp))
;
LIST(cmd, tmp);
* If we're not in an interrupt, we can call NCR5380_main()
* unconditionally, because it cannot be already running.
*/
- if (in_interrupt() || ((flags >> 8) & 7) >= 6)
- queue_main();
+ if (in_interrupt() || irqs_disabled())
+ queue_main(hostdata);
else
- NCR5380_main(NULL);
+ NCR5380_main(&hostdata->main_task);
return 0;
}
-static DEF_SCSI_QCMD(NCR5380_queue_command)
+static inline void maybe_release_dma_irq(struct Scsi_Host *instance)
+{
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+
+ /* Caller does the locking needed to set & test these data atomically */
+ if (!hostdata->disconnected_queue &&
+ !hostdata->issue_queue &&
+ !hostdata->connected &&
+ !hostdata->retain_dma_intr)
+ NCR5380_release_dma_irq(instance);
+}
-/*
- * Function : NCR5380_main (void)
+/**
+ * NCR5380_main - NCR state machines
*
- * Purpose : NCR5380_main is a coroutine that runs as long as more work can
- * be done on the NCR5380 host adapters in a system. Both
- * NCR5380_queue_command() and NCR5380_intr() will try to start it
- * in case it is not running.
+ * NCR5380_main is a coroutine that runs as long as more work can
+ * be done on the NCR5380 host adapters in a system. Both
+ * NCR5380_queue_command() and NCR5380_intr() will try to start it
+ * in case it is not running.
*
- * NOTE : NCR5380_main exits with interrupts *disabled*, the caller should
- * reenable them. This prevents reentrancy and kernel stack overflow.
+ * Locks: called as its own thread with no locks held.
*/
static void NCR5380_main(struct work_struct *work)
{
- Scsi_Cmnd *tmp, *prev;
- struct Scsi_Host *instance = first_instance;
- struct NCR5380_hostdata *hostdata = HOSTDATA(instance);
+ struct NCR5380_hostdata *hostdata =
+ container_of(work, struct NCR5380_hostdata, main_task);
+ struct Scsi_Host *instance = hostdata->host;
+ struct scsi_cmnd *tmp, *prev;
int done;
unsigned long flags;
'main_running' is set here, and queues/executes main via the
task queue, it doesn't do any harm, just this instance of main
won't find any work left to do. */
- if (main_running)
+ if (hostdata->main_running)
return;
- main_running = 1;
+ hostdata->main_running = 1;
local_save_flags(flags);
do {
* for a target that's not busy.
*/
#if (NDEBUG & NDEBUG_LISTS)
- for (tmp = (Scsi_Cmnd *) hostdata->issue_queue, prev = NULL;
+ for (tmp = (struct scsi_cmnd *) hostdata->issue_queue, prev = NULL;
tmp && (tmp != prev); prev = tmp, tmp = NEXT(tmp))
;
/*printk("%p ", tmp);*/
printk(" LOOP\n");
/* else printk("\n"); */
#endif
- for (tmp = (Scsi_Cmnd *) hostdata->issue_queue,
+ for (tmp = (struct scsi_cmnd *) hostdata->issue_queue,
prev = NULL; tmp; prev = tmp, tmp = NEXT(tmp)) {
u8 lun = tmp->device->lun;
-#if (NDEBUG & NDEBUG_LISTS)
- if (prev != tmp)
- printk("MAIN tmp=%p target=%d busy=%d lun=%llu\n",
- tmp, tmp->device->id, hostdata->busy[tmp->device->id],
- lun);
-#endif
+ dprintk(NDEBUG_LISTS,
+ "MAIN tmp=%p target=%d busy=%d lun=%d\n",
+ tmp, scmd_id(tmp), hostdata->busy[scmd_id(tmp)],
+ lun);
/* When we find one, remove it from the issue queue. */
/* ++guenther: possible race with Falcon locking */
if (
hostdata->issue_queue = NEXT(tmp);
}
SET_NEXT(tmp, NULL);
- falcon_dont_release++;
+ hostdata->retain_dma_intr++;
/* reenable interrupts after finding one */
local_irq_restore(flags);
#ifdef SUPPORT_TAGS
cmd_get_tag(tmp, tmp->cmnd[0] != REQUEST_SENSE);
#endif
- if (!NCR5380_select(instance, tmp,
- (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE :
- TAG_NEXT)) {
- falcon_dont_release--;
+ if (!NCR5380_select(instance, tmp)) {
+ local_irq_disable();
+ hostdata->retain_dma_intr--;
/* release if target did not response! */
- falcon_release_lock_if_possible(hostdata);
+ maybe_release_dma_irq(instance);
+ local_irq_restore(flags);
break;
} else {
local_irq_disable();
#ifdef SUPPORT_TAGS
cmd_free_tag(tmp);
#endif
- falcon_dont_release--;
+ hostdata->retain_dma_intr--;
local_irq_restore(flags);
dprintk(NDEBUG_MAIN, "scsi%d: main(): select() failed, "
"returned to issue_queue\n", HOSTNO);
/* Better allow ints _after_ 'main_running' has been cleared, else
an interrupt could believe we'll pick up the work it left for
us, but we won't see it anymore here... */
- main_running = 0;
+ hostdata->main_running = 0;
local_irq_restore(flags);
}
static void NCR5380_dma_complete(struct Scsi_Host *instance)
{
SETUP_HOSTDATA(instance);
- int transfered, saved_data = 0, overrun = 0, cnt, toPIO;
- unsigned char **data, p;
+ int transferred;
+ unsigned char **data;
volatile int *count;
+ int saved_data = 0, overrun = 0;
+ unsigned char p;
if (!hostdata->connected) {
printk(KERN_WARNING "scsi%d: received end of DMA interrupt with "
return;
}
- if (atari_read_overruns) {
+ if (hostdata->read_overruns) {
p = hostdata->connected->SCp.phase;
if (p & SR_IO) {
udelay(10);
HOSTNO, NCR5380_read(BUS_AND_STATUS_REG),
NCR5380_read(STATUS_REG));
+#if defined(CONFIG_SUN3)
+ if ((sun3scsi_dma_finish(rq_data_dir(hostdata->connected->request)))) {
+ pr_err("scsi%d: overrun in UDC counter -- not prepared to deal with this!\n",
+ instance->host_no);
+ BUG();
+ }
+
+ /* make sure we're not stuck in a data phase */
+ if ((NCR5380_read(BUS_AND_STATUS_REG) & (BASR_PHASE_MATCH | BASR_ACK)) ==
+ (BASR_PHASE_MATCH | BASR_ACK)) {
+ pr_err("scsi%d: BASR %02x\n", instance->host_no,
+ NCR5380_read(BUS_AND_STATUS_REG));
+ pr_err("scsi%d: bus stuck in data phase -- probably a single byte overrun!\n",
+ instance->host_no);
+ BUG();
+ }
+#endif
+
(void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
NCR5380_write(MODE_REG, MR_BASE);
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- transfered = hostdata->dma_len - NCR5380_dma_residual(instance);
+ transferred = hostdata->dma_len - NCR5380_dma_residual(instance);
hostdata->dma_len = 0;
data = (unsigned char **)&hostdata->connected->SCp.ptr;
count = &hostdata->connected->SCp.this_residual;
- *data += transfered;
- *count -= transfered;
+ *data += transferred;
+ *count -= transferred;
+
+ if (hostdata->read_overruns) {
+ int cnt, toPIO;
- if (atari_read_overruns) {
if ((NCR5380_read(STATUS_REG) & PHASE_MASK) == p && (p & SR_IO)) {
- cnt = toPIO = atari_read_overruns;
+ cnt = toPIO = hostdata->read_overruns;
if (overrun) {
dprintk(NDEBUG_DMA, "Got an input overrun, using saved byte\n");
*(*data)++ = saved_data;
#endif /* REAL_DMA */
-/*
- * Function : void NCR5380_intr (int irq)
- *
- * Purpose : handle interrupts, reestablishing I_T_L or I_T_L_Q nexuses
- * from the disconnected queue, and restarting NCR5380_main()
- * as required.
- *
- * Inputs : int irq, irq that caused this interrupt.
+/**
+ * NCR5380_intr - generic NCR5380 irq handler
+ * @irq: interrupt number
+ * @dev_id: device info
*
+ * Handle interrupts, reestablishing I_T_L or I_T_L_Q nexuses
+ * from the disconnected queue, and restarting NCR5380_main()
+ * as required.
*/
static irqreturn_t NCR5380_intr(int irq, void *dev_id)
{
- struct Scsi_Host *instance = first_instance;
+ struct Scsi_Host *instance = dev_id;
int done = 1, handled = 0;
unsigned char basr;
NCR5380_dprint(NDEBUG_INTR, instance);
if ((NCR5380_read(STATUS_REG) & (SR_SEL|SR_IO)) == (SR_SEL|SR_IO)) {
done = 0;
- ENABLE_IRQ();
dprintk(NDEBUG_INTR, "scsi%d: SEL interrupt\n", HOSTNO);
NCR5380_reselect(instance);
(void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
dprintk(NDEBUG_INTR, "scsi%d: PHASE MISM or EOP interrupt\n", HOSTNO);
NCR5380_dma_complete( instance );
done = 0;
- ENABLE_IRQ();
} else
#endif /* REAL_DMA */
{
/* MS: Ignore unknown phase mismatch interrupts (caused by EOP interrupt) */
if (basr & BASR_PHASE_MATCH)
- printk(KERN_NOTICE "scsi%d: unknown interrupt, "
+ dprintk(NDEBUG_INTR, "scsi%d: unknown interrupt, "
"BASR 0x%x, MR 0x%x, SR 0x%x\n",
HOSTNO, basr, NCR5380_read(MODE_REG),
NCR5380_read(STATUS_REG));
(void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+#ifdef SUN3_SCSI_VME
+ dregs->csr |= CSR_DMA_ENABLE;
+#endif
}
} /* if !(SELECTION || PARITY) */
handled = 1;
"BASR 0x%X, MR 0x%X, SR 0x%x\n", HOSTNO, basr,
NCR5380_read(MODE_REG), NCR5380_read(STATUS_REG));
(void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+#ifdef SUN3_SCSI_VME
+ dregs->csr |= CSR_DMA_ENABLE;
+#endif
}
if (!done) {
dprintk(NDEBUG_INTR, "scsi%d: in int routine, calling main\n", HOSTNO);
/* Put a call to NCR5380_main() on the queue... */
- queue_main();
+ queue_main(shost_priv(instance));
}
return IRQ_RETVAL(handled);
}
-#ifdef NCR5380_STATS
-static void collect_stats(struct NCR5380_hostdata* hostdata, Scsi_Cmnd *cmd)
-{
-# ifdef NCR5380_STAT_LIMIT
- if (scsi_bufflen(cmd) > NCR5380_STAT_LIMIT)
-# endif
- switch (cmd->cmnd[0]) {
- case WRITE:
- case WRITE_6:
- case WRITE_10:
- hostdata->time_write[cmd->device->id] += (jiffies - hostdata->timebase);
- /*hostdata->bytes_write[cmd->device->id] += scsi_bufflen(cmd);*/
- hostdata->pendingw--;
- break;
- case READ:
- case READ_6:
- case READ_10:
- hostdata->time_read[cmd->device->id] += (jiffies - hostdata->timebase);
- /*hostdata->bytes_read[cmd->device->id] += scsi_bufflen(cmd);*/
- hostdata->pendingr--;
- break;
- }
-}
-#endif
-
/*
- * Function : int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd,
- * int tag);
+ * Function : int NCR5380_select(struct Scsi_Host *instance,
+ * struct scsi_cmnd *cmd)
*
* Purpose : establishes I_T_L or I_T_L_Q nexus for new or existing command,
* including ARBITRATION, SELECTION, and initial message out for
* IDENTIFY and queue messages.
*
* Inputs : instance - instantiation of the 5380 driver on which this
- * target lives, cmd - SCSI command to execute, tag - set to TAG_NEXT for
- * new tag, TAG_NONE for untagged queueing, otherwise set to the tag for
- * the command that is presently connected.
+ * target lives, cmd - SCSI command to execute.
*
* Returns : -1 if selection could not execute for some reason,
* 0 if selection succeeded or failed because the target
* cmd->result host byte set to DID_BAD_TARGET.
*/
-static int NCR5380_select(struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag)
+static int NCR5380_select(struct Scsi_Host *instance, struct scsi_cmnd *cmd)
{
SETUP_HOSTDATA(instance);
unsigned char tmp[3], phase;
* selection.
*/
- timeout = jiffies + 25;
+ timeout = jiffies + (250 * HZ / 1000);
/*
* XXX very interesting - we're seeing a bounce where the BSY we
return -1;
}
cmd->result = DID_BAD_TARGET << 16;
-#ifdef NCR5380_STATS
- collect_stats(hostdata, cmd);
-#endif
#ifdef SUPPORT_TAGS
cmd_free_tag(cmd);
#endif
#ifndef SUPPORT_TAGS
hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun);
#endif
+#ifdef SUN3_SCSI_VME
+ dregs->csr |= CSR_INTR;
+#endif
initialize_SCp(cmd);
* Returns : 0 on success, -1 on failure.
*/
-static int do_abort(struct Scsi_Host *host)
+static int do_abort(struct Scsi_Host *instance)
{
unsigned char tmp, *msgptr, phase;
int len;
msgptr = &tmp;
len = 1;
phase = PHASE_MSGOUT;
- NCR5380_transfer_pio(host, &phase, &len, &msgptr);
+ NCR5380_transfer_pio(instance, &phase, &len, &msgptr);
/*
* If we got here, and the command completed successfully,
SETUP_HOSTDATA(instance);
register int c = *count;
register unsigned char p = *phase;
+ unsigned long flags;
+
+#if defined(CONFIG_SUN3)
+ /* sanity check */
+ if (!sun3_dma_setup_done) {
+ pr_err("scsi%d: transfer_dma without setup!\n",
+ instance->host_no);
+ BUG();
+ }
+ hostdata->dma_len = c;
+
+ dprintk(NDEBUG_DMA, "scsi%d: initializing DMA for %s, %d bytes %s %p\n",
+ instance->host_no, (p & SR_IO) ? "reading" : "writing",
+ c, (p & SR_IO) ? "to" : "from", *data);
+
+ /* netbsd turns off ints here, why not be safe and do it too */
+ local_irq_save(flags);
+
+ /* send start chain */
+ sun3scsi_dma_start(c, *data);
+
+ if (p & SR_IO) {
+ NCR5380_write(TARGET_COMMAND_REG, 1);
+ NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ NCR5380_write(INITIATOR_COMMAND_REG, 0);
+ NCR5380_write(MODE_REG,
+ (NCR5380_read(MODE_REG) | MR_DMA_MODE | MR_ENABLE_EOP_INTR));
+ NCR5380_write(START_DMA_INITIATOR_RECEIVE_REG, 0);
+ } else {
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+ NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_DATA);
+ NCR5380_write(MODE_REG,
+ (NCR5380_read(MODE_REG) | MR_DMA_MODE | MR_ENABLE_EOP_INTR));
+ NCR5380_write(START_DMA_SEND_REG, 0);
+ }
+
+#ifdef SUN3_SCSI_VME
+ dregs->csr |= CSR_DMA_ENABLE;
+#endif
+
+ local_irq_restore(flags);
+
+ sun3_dma_active = 1;
+
+#else /* !defined(CONFIG_SUN3) */
register unsigned char *d = *data;
unsigned char tmp;
- unsigned long flags;
if ((tmp = (NCR5380_read(STATUS_REG) & PHASE_MASK)) != p) {
*phase = tmp;
return -1;
}
- if (atari_read_overruns && (p & SR_IO))
- c -= atari_read_overruns;
+ if (hostdata->read_overruns && (p & SR_IO))
+ c -= hostdata->read_overruns;
dprintk(NDEBUG_DMA, "scsi%d: initializing DMA for %s, %d bytes %s %p\n",
HOSTNO, (p & SR_IO) ? "reading" : "writing",
NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_ENABLE_EOP_INTR | MR_MONITOR_BSY);
#endif /* def REAL_DMA */
- if (IS_A_TT()) {
+ if (!(hostdata->flags & FLAG_LATE_DMA_SETUP)) {
/* On the Medusa, it is a must to initialize the DMA before
* starting the NCR. This is also the cleaner way for the TT.
*/
NCR5380_write(START_DMA_SEND_REG, 0);
}
- if (!IS_A_TT()) {
+ if (hostdata->flags & FLAG_LATE_DMA_SETUP) {
/* On the Falcon, the DMA setup must be done after the last */
/* NCR access, else the DMA setup gets trashed!
*/
NCR5380_dma_write_setup(instance, d, c);
local_irq_restore(flags);
}
+#endif /* !defined(CONFIG_SUN3) */
+
return 0;
}
#endif /* defined(REAL_DMA) */
#endif
unsigned char *data;
unsigned char phase, tmp, extended_msg[10], old_phase = 0xff;
- Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected;
+ struct scsi_cmnd *cmd = (struct scsi_cmnd *) hostdata->connected;
+
+#ifdef SUN3_SCSI_VME
+ dregs->csr |= CSR_INTR;
+#endif
while (1) {
tmp = NCR5380_read(STATUS_REG);
old_phase = phase;
NCR5380_dprint_phase(NDEBUG_INFORMATION, instance);
}
+#if defined(CONFIG_SUN3)
+ if (phase == PHASE_CMDOUT) {
+#if defined(REAL_DMA)
+ void *d;
+ unsigned long count;
+
+ if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) {
+ count = cmd->SCp.buffer->length;
+ d = sg_virt(cmd->SCp.buffer);
+ } else {
+ count = cmd->SCp.this_residual;
+ d = cmd->SCp.ptr;
+ }
+ /* this command setup for dma yet? */
+ if ((count >= DMA_MIN_SIZE) && (sun3_dma_setup_done != cmd)) {
+ if (cmd->request->cmd_type == REQ_TYPE_FS) {
+ sun3scsi_dma_setup(d, count,
+ rq_data_dir(cmd->request));
+ sun3_dma_setup_done = cmd;
+ }
+ }
+#endif
+#ifdef SUN3_SCSI_VME
+ dregs->csr |= CSR_INTR;
+#endif
+ }
+#endif /* CONFIG_SUN3 */
if (sink && (phase != PHASE_MSGOUT)) {
NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));
*/
#if defined(REAL_DMA)
- if (!cmd->device->borken &&
- (transfersize = NCR5380_dma_xfer_len(instance,cmd,phase)) > 31) {
+ if (
+#if !defined(CONFIG_SUN3)
+ !cmd->device->borken &&
+#endif
+ (transfersize = NCR5380_dma_xfer_len(instance, cmd, phase)) >= DMA_MIN_SIZE) {
len = transfersize;
cmd->SCp.phase = phase;
if (NCR5380_transfer_dma(instance, &phase,
* If the watchdog timer fires, all future
* accesses to this device will use the
* polled-IO. */
- printk(KERN_NOTICE "scsi%d: switching target %d "
- "lun %llu to slow handshake\n", HOSTNO,
- cmd->device->id, cmd->device->lun);
+ scmd_printk(KERN_INFO, cmd,
+ "switching to slow handshake\n");
cmd->device->borken = 1;
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
ICR_ASSERT_ATN);
NCR5380_transfer_pio(instance, &phase,
(int *)&cmd->SCp.this_residual,
(unsigned char **)&cmd->SCp.ptr);
+#if defined(CONFIG_SUN3) && defined(REAL_DMA)
+ /* if we had intended to dma that command clear it */
+ if (sun3_dma_setup_done == cmd)
+ sun3_dma_setup_done = NULL;
+#endif
break;
case PHASE_MSGIN:
len = 1;
dprintk(NDEBUG_LINKED, "scsi%d: target %d lun %llu linked request "
"done, calling scsi_done().\n",
HOSTNO, cmd->device->id, cmd->device->lun);
-#ifdef NCR5380_STATS
- collect_stats(hostdata, cmd);
-#endif
cmd->scsi_done(cmd);
cmd = hostdata->connected;
break;
case COMMAND_COMPLETE:
/* Accept message by clearing ACK */
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- /* ++guenther: possible race with Falcon locking */
- falcon_dont_release++;
- hostdata->connected = NULL;
dprintk(NDEBUG_QUEUES, "scsi%d: command for target %d, lun %llu "
"completed\n", HOSTNO, cmd->device->id, cmd->device->lun);
+
+ local_irq_save(flags);
+ hostdata->retain_dma_intr++;
+ hostdata->connected = NULL;
#ifdef SUPPORT_TAGS
cmd_free_tag(cmd);
if (status_byte(cmd->SCp.Status) == QUEUE_FULL) {
*/
/* ++Andreas: the mid level code knows about
QUEUE_FULL now. */
- TAG_ALLOC *ta = &TagAlloc[cmd->device->id][cmd->device->lun];
+ struct tag_alloc *ta = &hostdata->TagAlloc[scmd_id(cmd)][cmd->device->lun];
dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %llu returned "
"QUEUE_FULL after %d commands\n",
HOSTNO, cmd->device->id, cmd->device->lun,
else if (status_byte(cmd->SCp.Status) != GOOD)
cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
-#ifdef AUTOSENSE
if ((cmd->cmnd[0] == REQUEST_SENSE) &&
hostdata->ses.cmd_len) {
scsi_eh_restore_cmnd(cmd, &hostdata->ses);
dprintk(NDEBUG_AUTOSENSE, "scsi%d: performing request sense\n", HOSTNO);
- local_irq_save(flags);
LIST(cmd,hostdata->issue_queue);
SET_NEXT(cmd, hostdata->issue_queue);
- hostdata->issue_queue = (Scsi_Cmnd *) cmd;
- local_irq_restore(flags);
+ hostdata->issue_queue = (struct scsi_cmnd *) cmd;
dprintk(NDEBUG_QUEUES, "scsi%d: REQUEST SENSE added to head of "
"issue queue\n", H_NO(cmd));
- } else
-#endif /* def AUTOSENSE */
- {
-#ifdef NCR5380_STATS
- collect_stats(hostdata, cmd);
-#endif
+ } else {
cmd->scsi_done(cmd);
}
+ local_irq_restore(flags);
+
NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
/*
* Restore phase bits to 0 so an interrupted selection,
while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
barrier();
- falcon_dont_release--;
+ local_irq_save(flags);
+ hostdata->retain_dma_intr--;
/* ++roman: For Falcon SCSI, release the lock on the
* ST-DMA here if no other commands are waiting on the
* disconnected queue.
*/
- falcon_release_lock_if_possible(hostdata);
+ maybe_release_dma_irq(instance);
+ local_irq_restore(flags);
return;
case MESSAGE_REJECT:
/* Accept message by clearing ACK */
/* Wait for bus free to avoid nasty timeouts */
while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
barrier();
+#ifdef SUN3_SCSI_VME
+ dregs->csr |= CSR_DMA_ENABLE;
+#endif
return;
/*
* The SCSI data pointer is *IMPLICITLY* saved on a disconnect
*/
default:
if (!tmp) {
- printk(KERN_DEBUG "scsi%d: rejecting message ", HOSTNO);
+ printk(KERN_INFO "scsi%d: rejecting message ",
+ instance->host_no);
spi_print_msg(extended_msg);
printk("\n");
} else if (tmp != EXTENDED_MESSAGE)
- printk(KERN_DEBUG "scsi%d: rejecting unknown "
- "message %02x from target %d, lun %llu\n",
- HOSTNO, tmp, cmd->device->id, cmd->device->lun);
+ scmd_printk(KERN_INFO, cmd,
+ "rejecting unknown message %02x\n",
+ tmp);
else
- printk(KERN_DEBUG "scsi%d: rejecting unknown "
- "extended message "
- "code %02x, length %d from target %d, lun %llu\n",
- HOSTNO, extended_msg[1], extended_msg[0],
- cmd->device->id, cmd->device->lun);
-
+ scmd_printk(KERN_INFO, cmd,
+ "rejecting unknown extended message code %02x, length %d\n",
+ extended_msg[1], extended_msg[0]);
msgout = MESSAGE_REJECT;
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
hostdata->last_message = msgout;
NCR5380_transfer_pio(instance, &phase, &len, &data);
if (msgout == ABORT) {
+ local_irq_save(flags);
#ifdef SUPPORT_TAGS
cmd_free_tag(cmd);
#else
#endif
hostdata->connected = NULL;
cmd->result = DID_ERROR << 16;
-#ifdef NCR5380_STATS
- collect_stats(hostdata, cmd);
-#endif
- cmd->scsi_done(cmd);
NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- falcon_release_lock_if_possible(hostdata);
+ maybe_release_dma_irq(instance);
+ local_irq_restore(flags);
+ cmd->scsi_done(cmd);
return;
}
msgout = NOP;
* Function : void NCR5380_reselect (struct Scsi_Host *instance)
*
* Purpose : does reselection, initializing the instance->connected
- * field to point to the Scsi_Cmnd for which the I_T_L or I_T_L_Q
+ * field to point to the scsi_cmnd for which the I_T_L or I_T_L_Q
* nexus has been reestablished,
*
* Inputs : instance - this instance of the NCR5380.
*/
+/* it might eventually prove necessary to do a dma setup on
+ reselection, but it doesn't seem to be needed now -- sam */
+
static void NCR5380_reselect(struct Scsi_Host *instance)
{
SETUP_HOSTDATA(instance);
unsigned char target_mask;
- unsigned char lun, phase;
- int len;
+ unsigned char lun;
#ifdef SUPPORT_TAGS
unsigned char tag;
#endif
unsigned char msg[3];
- unsigned char *data;
- Scsi_Cmnd *tmp = NULL, *prev;
-/* unsigned long flags; */
+ int __maybe_unused len;
+ unsigned char __maybe_unused *data, __maybe_unused phase;
+ struct scsi_cmnd *tmp = NULL, *prev;
/*
* Disable arbitration, etc. since the host adapter obviously
while (!(NCR5380_read(STATUS_REG) & SR_REQ))
;
+#if defined(CONFIG_SUN3) && defined(REAL_DMA)
+ /* acknowledge toggle to MSGIN */
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(PHASE_MSGIN));
+
+ /* peek at the byte without really hitting the bus */
+ msg[0] = NCR5380_read(CURRENT_SCSI_DATA_REG);
+#else
len = 1;
data = msg;
phase = PHASE_MSGIN;
NCR5380_transfer_pio(instance, &phase, &len, &data);
+#endif
if (!(msg[0] & 0x80)) {
printk(KERN_DEBUG "scsi%d: expecting IDENTIFY message, got ", HOSTNO);
}
lun = (msg[0] & 0x07);
-#ifdef SUPPORT_TAGS
+#if defined(SUPPORT_TAGS) && !defined(CONFIG_SUN3)
/* If the phase is still MSGIN, the target wants to send some more
* messages. In case it supports tagged queuing, this is probably a
* SIMPLE_QUEUE_TAG for the I_T_L_Q nexus.
*/
tag = TAG_NONE;
- if (phase == PHASE_MSGIN && setup_use_tagged_queuing) {
+ if (phase == PHASE_MSGIN && (hostdata->flags & FLAG_TAGGED_QUEUING)) {
/* Accept previous IDENTIFY message by clearing ACK */
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
len = 2;
* just reestablished, and remove it from the disconnected queue.
*/
- for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue, prev = NULL;
+ for (tmp = (struct scsi_cmnd *) hostdata->disconnected_queue, prev = NULL;
tmp; prev = tmp, tmp = NEXT(tmp)) {
if ((target_mask == (1 << tmp->device->id)) && (lun == tmp->device->lun)
#ifdef SUPPORT_TAGS
&& (tag == tmp->tag)
#endif
) {
- /* ++guenther: prevent race with falcon_release_lock */
- falcon_dont_release++;
if (prev) {
REMOVE(prev, NEXT(prev), tmp, NEXT(tmp));
SET_NEXT(prev, NEXT(tmp));
return;
}
+#if defined(CONFIG_SUN3) && defined(REAL_DMA)
+ /* engage dma setup for the command we just saw */
+ {
+ void *d;
+ unsigned long count;
+
+ if (!tmp->SCp.this_residual && tmp->SCp.buffers_residual) {
+ count = tmp->SCp.buffer->length;
+ d = sg_virt(tmp->SCp.buffer);
+ } else {
+ count = tmp->SCp.this_residual;
+ d = tmp->SCp.ptr;
+ }
+ /* setup this command for dma if not already */
+ if ((count >= DMA_MIN_SIZE) && (sun3_dma_setup_done != tmp)) {
+ sun3scsi_dma_setup(d, count, rq_data_dir(tmp->request));
+ sun3_dma_setup_done = tmp;
+ }
+ }
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK);
+#endif
+
/* Accept message by clearing ACK */
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+#if defined(SUPPORT_TAGS) && defined(CONFIG_SUN3)
+ /* If the phase is still MSGIN, the target wants to send some more
+ * messages. In case it supports tagged queuing, this is probably a
+ * SIMPLE_QUEUE_TAG for the I_T_L_Q nexus.
+ */
+ tag = TAG_NONE;
+ if (phase == PHASE_MSGIN && setup_use_tagged_queuing) {
+ /* Accept previous IDENTIFY message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ len = 2;
+ data = msg + 1;
+ if (!NCR5380_transfer_pio(instance, &phase, &len, &data) &&
+ msg[1] == SIMPLE_QUEUE_TAG)
+ tag = msg[2];
+ dprintk(NDEBUG_TAGS, "scsi%d: target mask %02x, lun %d sent tag %d at reselection\n"
+ HOSTNO, target_mask, lun, tag);
+ }
+#endif
+
hostdata->connected = tmp;
dprintk(NDEBUG_RESELECTION, "scsi%d: nexus established, target = %d, lun = %llu, tag = %d\n",
HOSTNO, tmp->device->id, tmp->device->lun, tmp->tag);
- falcon_dont_release--;
}
/*
- * Function : int NCR5380_abort (Scsi_Cmnd *cmd)
+ * Function : int NCR5380_abort (struct scsi_cmnd *cmd)
*
* Purpose : abort a command
*
- * Inputs : cmd - the Scsi_Cmnd to abort, code - code to set the
+ * Inputs : cmd - the scsi_cmnd to abort, code - code to set the
* host byte of the result field to, if zero DID_ABORTED is
* used.
*
- * Returns : 0 - success, -1 on failure.
+ * Returns : SUCCESS - success, FAILED on failure.
*
* XXX - there is no way to abort the command that is currently
* connected, you have to wait for it to complete. If this is
*/
static
-int NCR5380_abort(Scsi_Cmnd *cmd)
+int NCR5380_abort(struct scsi_cmnd *cmd)
{
struct Scsi_Host *instance = cmd->device->host;
SETUP_HOSTDATA(instance);
- Scsi_Cmnd *tmp, **prev;
+ struct scsi_cmnd *tmp, **prev;
unsigned long flags;
- printk(KERN_NOTICE "scsi%d: aborting command\n", HOSTNO);
- scsi_print_command(cmd);
+ scmd_printk(KERN_NOTICE, cmd, "aborting command\n");
NCR5380_print_status(instance);
local_irq_save(flags);
- if (!IS_A_TT() && !falcon_got_lock)
- printk(KERN_ERR "scsi%d: !!BINGO!! Falcon has no lock in NCR5380_abort\n",
- HOSTNO);
-
dprintk(NDEBUG_ABORT, "scsi%d: abort called basr 0x%02x, sr 0x%02x\n", HOSTNO,
NCR5380_read(BUS_AND_STATUS_REG),
NCR5380_read(STATUS_REG));
#else
hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
#endif
+ maybe_release_dma_irq(instance);
local_irq_restore(flags);
cmd->scsi_done(cmd);
- falcon_release_lock_if_possible(hostdata);
return SUCCESS;
} else {
-/* local_irq_restore(flags); */
+ local_irq_restore(flags);
printk("scsi%d: abort of connected command failed!\n", HOSTNO);
return FAILED;
}
* Case 2 : If the command hasn't been issued yet, we simply remove it
* from the issue queue.
*/
- for (prev = (Scsi_Cmnd **)&(hostdata->issue_queue),
- tmp = (Scsi_Cmnd *)hostdata->issue_queue;
+ for (prev = (struct scsi_cmnd **)&(hostdata->issue_queue),
+ tmp = (struct scsi_cmnd *)hostdata->issue_queue;
tmp; prev = NEXTADDR(tmp), tmp = NEXT(tmp)) {
if (cmd == tmp) {
REMOVE(5, *prev, tmp, NEXT(tmp));
(*prev) = NEXT(tmp);
SET_NEXT(tmp, NULL);
tmp->result = DID_ABORT << 16;
+ maybe_release_dma_irq(instance);
local_irq_restore(flags);
dprintk(NDEBUG_ABORT, "scsi%d: abort removed command from issue queue.\n",
HOSTNO);
/* Tagged queuing note: no tag to free here, hasn't been assigned
* yet... */
tmp->scsi_done(tmp);
- falcon_release_lock_if_possible(hostdata);
return SUCCESS;
}
}
* it from the disconnected queue.
*/
- for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp;
+ for (tmp = (struct scsi_cmnd *) hostdata->disconnected_queue; tmp;
tmp = NEXT(tmp)) {
if (cmd == tmp) {
local_irq_restore(flags);
dprintk(NDEBUG_ABORT, "scsi%d: aborting disconnected command.\n", HOSTNO);
- if (NCR5380_select(instance, cmd, (int)cmd->tag))
+ if (NCR5380_select(instance, cmd))
return FAILED;
dprintk(NDEBUG_ABORT, "scsi%d: nexus reestablished.\n", HOSTNO);
do_abort(instance);
local_irq_save(flags);
- for (prev = (Scsi_Cmnd **)&(hostdata->disconnected_queue),
- tmp = (Scsi_Cmnd *)hostdata->disconnected_queue;
+ for (prev = (struct scsi_cmnd **)&(hostdata->disconnected_queue),
+ tmp = (struct scsi_cmnd *)hostdata->disconnected_queue;
tmp; prev = NEXTADDR(tmp), tmp = NEXT(tmp)) {
if (cmd == tmp) {
REMOVE(5, *prev, tmp, NEXT(tmp));
#else
hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
#endif
+ maybe_release_dma_irq(instance);
local_irq_restore(flags);
tmp->scsi_done(tmp);
- falcon_release_lock_if_possible(hostdata);
return SUCCESS;
}
}
}
}
+ /* Maybe it is sufficient just to release the ST-DMA lock... (if
+ * possible at all) At least, we should check if the lock could be
+ * released after the abort, in case it is kept due to some bug.
+ */
+ maybe_release_dma_irq(instance);
+ local_irq_restore(flags);
+
/*
* Case 5 : If we reached this point, the command was not found in any of
* the queues.
* broke.
*/
- local_irq_restore(flags);
printk(KERN_INFO "scsi%d: warning : SCSI command probably completed successfully before abortion\n", HOSTNO);
- /* Maybe it is sufficient just to release the ST-DMA lock... (if
- * possible at all) At least, we should check if the lock could be
- * released after the abort, in case it is kept due to some bug.
- */
- falcon_release_lock_if_possible(hostdata);
-
return FAILED;
}
/*
- * Function : int NCR5380_reset (Scsi_Cmnd *cmd)
+ * Function : int NCR5380_reset (struct scsi_cmnd *cmd)
*
* Purpose : reset the SCSI bus.
*
*
*/
-static int NCR5380_bus_reset(Scsi_Cmnd *cmd)
+static int NCR5380_bus_reset(struct scsi_cmnd *cmd)
{
- SETUP_HOSTDATA(cmd->device->host);
+ struct Scsi_Host *instance = cmd->device->host;
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
int i;
unsigned long flags;
-#if defined(RESET_RUN_DONE)
- Scsi_Cmnd *connected, *disconnected_queue;
-#endif
-
- if (!IS_A_TT() && !falcon_got_lock)
- printk(KERN_ERR "scsi%d: !!BINGO!! Falcon has no lock in NCR5380_reset\n",
- H_NO(cmd));
- NCR5380_print_status(cmd->device->host);
+ NCR5380_print_status(instance);
/* get in phase */
NCR5380_write(TARGET_COMMAND_REG,
* through anymore ... */
(void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
- /* MSch 20140115 - looking at the generic NCR5380 driver, all of this
- * should go.
- * Catch-22: if we don't clear all queues, the SCSI driver lock will
- * not be reset by atari_scsi_reset()!
- */
-
-#if defined(RESET_RUN_DONE)
- /* XXX Should now be done by midlevel code, but it's broken XXX */
- /* XXX see below XXX */
-
- /* MSch: old-style reset: actually abort all command processing here */
-
- /* After the reset, there are no more connected or disconnected commands
- * and no busy units; to avoid problems with re-inserting the commands
- * into the issue_queue (via scsi_done()), the aborted commands are
- * remembered in local variables first.
- */
- local_irq_save(flags);
- connected = (Scsi_Cmnd *)hostdata->connected;
- hostdata->connected = NULL;
- disconnected_queue = (Scsi_Cmnd *)hostdata->disconnected_queue;
- hostdata->disconnected_queue = NULL;
-#ifdef SUPPORT_TAGS
- free_all_tags();
-#endif
- for (i = 0; i < 8; ++i)
- hostdata->busy[i] = 0;
-#ifdef REAL_DMA
- hostdata->dma_len = 0;
-#endif
- local_irq_restore(flags);
-
- /* In order to tell the mid-level code which commands were aborted,
- * set the command status to DID_RESET and call scsi_done() !!!
- * This ultimately aborts processing of these commands in the mid-level.
- */
-
- if ((cmd = connected)) {
- dprintk(NDEBUG_ABORT, "scsi%d: reset aborted a connected command\n", H_NO(cmd));
- cmd->result = (cmd->result & 0xffff) | (DID_RESET << 16);
- cmd->scsi_done(cmd);
- }
-
- for (i = 0; (cmd = disconnected_queue); ++i) {
- disconnected_queue = NEXT(cmd);
- SET_NEXT(cmd, NULL);
- cmd->result = (cmd->result & 0xffff) | (DID_RESET << 16);
- cmd->scsi_done(cmd);
- }
- if (i > 0)
- dprintk(NDEBUG_ABORT, "scsi: reset aborted %d disconnected command(s)\n", i);
-
- /* The Falcon lock should be released after a reset...
- */
- /* ++guenther: moved to atari_scsi_reset(), to prevent a race between
- * unlocking and enabling dma interrupt.
- */
-/* falcon_release_lock_if_possible( hostdata );*/
-
- /* since all commands have been explicitly terminated, we need to tell
- * the midlevel code that the reset was SUCCESSFUL, and there is no
- * need to 'wake up' the commands by a request_sense
- */
- return SUCCESS;
-#else /* 1 */
-
- /* MSch: new-style reset handling: let the mid-level do what it can */
-
- /* ++guenther: MID-LEVEL IS STILL BROKEN.
- * Mid-level is supposed to requeue all commands that were active on the
- * various low-level queues. In fact it does this, but that's not enough
- * because all these commands are subject to timeout. And if a timeout
- * happens for any removed command, *_abort() is called but all queues
- * are now empty. Abort then gives up the falcon lock, which is fatal,
- * since the mid-level will queue more commands and must have the lock
- * (it's all happening inside timer interrupt handler!!).
- * Even worse, abort will return NOT_RUNNING for all those commands not
- * on any queue, so they won't be retried ...
- *
- * Conclusion: either scsi.c disables timeout for all resetted commands
- * immediately, or we lose! As of linux-2.0.20 it doesn't.
- */
-
/* After the reset, there are no more connected or disconnected commands
* and no busy units; so clear the low-level status here to avoid
* conflicts when the mid-level code tries to wake up the affected
hostdata->connected = NULL;
hostdata->disconnected_queue = NULL;
#ifdef SUPPORT_TAGS
- free_all_tags();
+ free_all_tags(hostdata);
#endif
for (i = 0; i < 8; ++i)
hostdata->busy[i] = 0;
#ifdef REAL_DMA
hostdata->dma_len = 0;
#endif
+
+ maybe_release_dma_irq(instance);
local_irq_restore(flags);
- /* we did no complete reset of all commands, so a wakeup is required */
return SUCCESS;
-#endif /* 1 */
}
/**************************************************************************/
-
#include <linux/module.h>
-
-#define AUTOSENSE
-/* For the Atari version, use only polled IO or REAL_DMA */
-#define REAL_DMA
-/* Support tagged queuing? (on devices that are able to... :-) */
-#define SUPPORT_TAGS
-#define MAX_TAGS 32
-
#include <linux/types.h>
-#include <linux/stddef.h>
-#include <linux/ctype.h>
#include <linux/delay.h>
-#include <linux/mm.h>
#include <linux/blkdev.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/nvram.h>
#include <linux/bitops.h>
#include <linux/wait.h>
+#include <linux/platform_device.h>
#include <asm/setup.h>
#include <asm/atarihw.h>
#include <asm/atariints.h>
-#include <asm/page.h>
-#include <asm/pgtable.h>
-#include <asm/irq.h>
-#include <asm/traps.h>
-
-#include "scsi.h"
-#include <scsi/scsi_host.h>
-#include "atari_scsi.h"
-#include "NCR5380.h"
#include <asm/atari_stdma.h>
#include <asm/atari_stram.h>
#include <asm/io.h>
-#include <linux/stat.h>
+#include <scsi/scsi_host.h>
+
+/* Definitions for the core NCR5380 driver. */
+
+#define REAL_DMA
+#define SUPPORT_TAGS
+#define MAX_TAGS 32
+#define DMA_MIN_SIZE 32
+
+#define NCR5380_implementation_fields /* none */
+
+#define NCR5380_read(reg) atari_scsi_reg_read(reg)
+#define NCR5380_write(reg, value) atari_scsi_reg_write(reg, value)
+
+#define NCR5380_queue_command atari_scsi_queue_command
+#define NCR5380_abort atari_scsi_abort
+#define NCR5380_show_info atari_scsi_show_info
+#define NCR5380_info atari_scsi_info
+
+#define NCR5380_dma_read_setup(instance, data, count) \
+ atari_scsi_dma_setup(instance, data, count, 0)
+#define NCR5380_dma_write_setup(instance, data, count) \
+ atari_scsi_dma_setup(instance, data, count, 1)
+#define NCR5380_dma_residual(instance) \
+ atari_scsi_dma_residual(instance)
+#define NCR5380_dma_xfer_len(instance, cmd, phase) \
+ atari_dma_xfer_len(cmd->SCp.this_residual, cmd, !((phase) & SR_IO))
+
+#define NCR5380_acquire_dma_irq(instance) falcon_get_lock(instance)
+#define NCR5380_release_dma_irq(instance) falcon_release_lock()
+
+#include "NCR5380.h"
+
#define IS_A_TT() ATARIHW_PRESENT(TT_SCSI)
return adr;
}
-static inline void ENABLE_IRQ(void)
-{
- if (IS_A_TT())
- atari_enable_irq(IRQ_TT_MFP_SCSI);
- else
- atari_enable_irq(IRQ_MFP_FSCSI);
-}
-
-static inline void DISABLE_IRQ(void)
-{
- if (IS_A_TT())
- atari_disable_irq(IRQ_TT_MFP_SCSI);
- else
- atari_disable_irq(IRQ_MFP_FSCSI);
-}
-
-
#define HOSTDATA_DMALEN (((struct NCR5380_hostdata *) \
(atari_scsi_host->hostdata))->dma_len)
#define AFTER_RESET_DELAY (5*HZ/2)
#endif
-/***************************** Prototypes *****************************/
-
#ifdef REAL_DMA
-static int scsi_dma_is_ignored_buserr(unsigned char dma_stat);
static void atari_scsi_fetch_restbytes(void);
-static long atari_scsi_dma_residual(struct Scsi_Host *instance);
-static int falcon_classify_cmd(Scsi_Cmnd *cmd);
-static unsigned long atari_dma_xfer_len(unsigned long wanted_len,
- Scsi_Cmnd *cmd, int write_flag);
-#endif
-static irqreturn_t scsi_tt_intr(int irq, void *dummy);
-static irqreturn_t scsi_falcon_intr(int irq, void *dummy);
-static void falcon_release_lock_if_possible(struct NCR5380_hostdata *hostdata);
-static void falcon_get_lock(void);
-#ifdef CONFIG_ATARI_SCSI_RESET_BOOT
-static void atari_scsi_reset_boot(void);
#endif
-static unsigned char atari_scsi_tt_reg_read(unsigned char reg);
-static void atari_scsi_tt_reg_write(unsigned char reg, unsigned char value);
-static unsigned char atari_scsi_falcon_reg_read(unsigned char reg);
-static void atari_scsi_falcon_reg_write(unsigned char reg, unsigned char value);
-
-/************************* End of Prototypes **************************/
-
static struct Scsi_Host *atari_scsi_host;
static unsigned char (*atari_scsi_reg_read)(unsigned char reg);
/* mask for address bits that can't be used with the ST-DMA */
static unsigned long atari_dma_stram_mask;
#define STRAM_ADDR(a) (((a) & atari_dma_stram_mask) == 0)
-/* number of bytes to cut from a transfer to handle NCR overruns */
-static int atari_read_overruns;
#endif
static int setup_can_queue = -1;
NCR5380_intr(irq, dummy);
-#if 0
- /* To be sure the int is not masked */
- atari_enable_irq(IRQ_TT_MFP_SCSI);
-#endif
return IRQ_HANDLED;
}
#endif /* REAL_DMA */
-static int falcon_got_lock = 0;
-static DECLARE_WAIT_QUEUE_HEAD(falcon_fairness_wait);
-static int falcon_trying_lock = 0;
-static DECLARE_WAIT_QUEUE_HEAD(falcon_try_wait);
-static int falcon_dont_release = 0;
-
/* This function releases the lock on the DMA chip if there is no
- * connected command and the disconnected queue is empty. On
- * releasing, instances of falcon_get_lock are awoken, that put
- * themselves to sleep for fairness. They can now try to get the lock
- * again (but others waiting longer more probably will win).
+ * connected command and the disconnected queue is empty.
*/
-static void falcon_release_lock_if_possible(struct NCR5380_hostdata *hostdata)
+static void falcon_release_lock(void)
{
- unsigned long flags;
-
if (IS_A_TT())
return;
- local_irq_save(flags);
-
- if (falcon_got_lock && !hostdata->disconnected_queue &&
- !hostdata->issue_queue && !hostdata->connected) {
-
- if (falcon_dont_release) {
-#if 0
- printk("WARNING: Lock release not allowed. Ignored\n");
-#endif
- local_irq_restore(flags);
- return;
- }
- falcon_got_lock = 0;
+ if (stdma_is_locked_by(scsi_falcon_intr))
stdma_release();
- wake_up(&falcon_fairness_wait);
- }
-
- local_irq_restore(flags);
}
/* This function manages the locking of the ST-DMA.
* If the DMA isn't locked already for SCSI, it tries to lock it by
* calling stdma_lock(). But if the DMA is locked by the SCSI code and
* there are other drivers waiting for the chip, we do not issue the
- * command immediately but wait on 'falcon_fairness_queue'. We will be
- * waked up when the DMA is unlocked by some SCSI interrupt. After that
- * we try to get the lock again.
- * But we must be prepared that more than one instance of
- * falcon_get_lock() is waiting on the fairness queue. They should not
- * try all at once to call stdma_lock(), one is enough! For that, the
- * first one sets 'falcon_trying_lock', others that see that variable
- * set wait on the queue 'falcon_try_wait'.
- * Complicated, complicated.... Sigh...
+ * command immediately but tell the SCSI mid-layer to defer.
*/
-static void falcon_get_lock(void)
+static int falcon_get_lock(struct Scsi_Host *instance)
{
- unsigned long flags;
-
if (IS_A_TT())
- return;
-
- local_irq_save(flags);
-
- wait_event_cmd(falcon_fairness_wait,
- in_interrupt() || !falcon_got_lock || !stdma_others_waiting(),
- local_irq_restore(flags),
- local_irq_save(flags));
-
- while (!falcon_got_lock) {
- if (in_irq())
- panic("Falcon SCSI hasn't ST-DMA lock in interrupt");
- if (!falcon_trying_lock) {
- falcon_trying_lock = 1;
- stdma_lock(scsi_falcon_intr, NULL);
- falcon_got_lock = 1;
- falcon_trying_lock = 0;
- wake_up(&falcon_try_wait);
- } else {
- wait_event_cmd(falcon_try_wait,
- falcon_got_lock && !falcon_trying_lock,
- local_irq_restore(flags),
- local_irq_save(flags));
- }
- }
-
- local_irq_restore(flags);
- if (!falcon_got_lock)
- panic("Falcon SCSI: someone stole the lock :-(\n");
-}
-
-
-static int __init atari_scsi_detect(struct scsi_host_template *host)
-{
- static int called = 0;
- struct Scsi_Host *instance;
-
- if (!MACH_IS_ATARI ||
- (!ATARIHW_PRESENT(ST_SCSI) && !ATARIHW_PRESENT(TT_SCSI)) ||
- called)
- return 0;
-
- host->proc_name = "Atari";
-
- atari_scsi_reg_read = IS_A_TT() ? atari_scsi_tt_reg_read :
- atari_scsi_falcon_reg_read;
- atari_scsi_reg_write = IS_A_TT() ? atari_scsi_tt_reg_write :
- atari_scsi_falcon_reg_write;
-
- /* setup variables */
- host->can_queue =
- (setup_can_queue > 0) ? setup_can_queue :
- IS_A_TT() ? ATARI_TT_CAN_QUEUE : ATARI_FALCON_CAN_QUEUE;
- host->cmd_per_lun =
- (setup_cmd_per_lun > 0) ? setup_cmd_per_lun :
- IS_A_TT() ? ATARI_TT_CMD_PER_LUN : ATARI_FALCON_CMD_PER_LUN;
- /* Force sg_tablesize to 0 on a Falcon! */
- host->sg_tablesize =
- !IS_A_TT() ? ATARI_FALCON_SG_TABLESIZE :
- (setup_sg_tablesize >= 0) ? setup_sg_tablesize : ATARI_TT_SG_TABLESIZE;
-
- if (setup_hostid >= 0)
- host->this_id = setup_hostid;
- else {
- /* use 7 as default */
- host->this_id = 7;
- /* Test if a host id is set in the NVRam */
- if (ATARIHW_PRESENT(TT_CLK) && nvram_check_checksum()) {
- unsigned char b = nvram_read_byte( 14 );
- /* Arbitration enabled? (for TOS) If yes, use configured host ID */
- if (b & 0x80)
- host->this_id = b & 7;
- }
- }
+ return 1;
-#ifdef SUPPORT_TAGS
- if (setup_use_tagged_queuing < 0)
- setup_use_tagged_queuing = DEFAULT_USE_TAGGED_QUEUING;
-#endif
-#ifdef REAL_DMA
- /* If running on a Falcon and if there's TT-Ram (i.e., more than one
- * memory block, since there's always ST-Ram in a Falcon), then allocate a
- * STRAM_BUFFER_SIZE byte dribble buffer for transfers from/to alternative
- * Ram.
- */
- if (MACH_IS_ATARI && ATARIHW_PRESENT(ST_SCSI) &&
- !ATARIHW_PRESENT(EXTD_DMA) && m68k_num_memory > 1) {
- atari_dma_buffer = atari_stram_alloc(STRAM_BUFFER_SIZE, "SCSI");
- if (!atari_dma_buffer) {
- printk(KERN_ERR "atari_scsi_detect: can't allocate ST-RAM "
- "double buffer\n");
- return 0;
- }
- atari_dma_phys_buffer = atari_stram_to_phys(atari_dma_buffer);
- atari_dma_orig_addr = 0;
- }
-#endif
- instance = scsi_register(host, sizeof(struct NCR5380_hostdata));
- if (instance == NULL) {
- atari_stram_free(atari_dma_buffer);
- atari_dma_buffer = 0;
- return 0;
- }
- atari_scsi_host = instance;
- /*
- * Set irq to 0, to avoid that the mid-level code disables our interrupt
- * during queue_command calls. This is completely unnecessary, and even
- * worse causes bad problems on the Falcon, where the int is shared with
- * IDE and floppy!
- */
- instance->irq = 0;
-
-#ifdef CONFIG_ATARI_SCSI_RESET_BOOT
- atari_scsi_reset_boot();
-#endif
- NCR5380_init(instance, 0);
-
- if (IS_A_TT()) {
-
- /* This int is actually "pseudo-slow", i.e. it acts like a slow
- * interrupt after having cleared the pending flag for the DMA
- * interrupt. */
- if (request_irq(IRQ_TT_MFP_SCSI, scsi_tt_intr, IRQ_TYPE_SLOW,
- "SCSI NCR5380", instance)) {
- printk(KERN_ERR "atari_scsi_detect: cannot allocate irq %d, aborting",IRQ_TT_MFP_SCSI);
- scsi_unregister(atari_scsi_host);
- atari_stram_free(atari_dma_buffer);
- atari_dma_buffer = 0;
- return 0;
- }
- tt_mfp.active_edge |= 0x80; /* SCSI int on L->H */
-#ifdef REAL_DMA
- tt_scsi_dma.dma_ctrl = 0;
- atari_dma_residual = 0;
-
- if (MACH_IS_MEDUSA) {
- /* While the read overruns (described by Drew Eckhardt in
- * NCR5380.c) never happened on TTs, they do in fact on the Medusa
- * (This was the cause why SCSI didn't work right for so long
- * there.) Since handling the overruns slows down a bit, I turned
- * the #ifdef's into a runtime condition.
- *
- * In principle it should be sufficient to do max. 1 byte with
- * PIO, but there is another problem on the Medusa with the DMA
- * rest data register. So 'atari_read_overruns' is currently set
- * to 4 to avoid having transfers that aren't a multiple of 4. If
- * the rest data bug is fixed, this can be lowered to 1.
- */
- atari_read_overruns = 4;
- }
-#endif /*REAL_DMA*/
- } else { /* ! IS_A_TT */
-
- /* Nothing to do for the interrupt: the ST-DMA is initialized
- * already by atari_init_INTS()
- */
-
-#ifdef REAL_DMA
- atari_dma_residual = 0;
- atari_dma_active = 0;
- atari_dma_stram_mask = (ATARIHW_PRESENT(EXTD_DMA) ? 0x00000000
- : 0xff000000);
-#endif
- }
+ if (in_interrupt())
+ return stdma_try_lock(scsi_falcon_intr, instance);
- printk(KERN_INFO "scsi%d: options CAN_QUEUE=%d CMD_PER_LUN=%d SCAT-GAT=%d "
-#ifdef SUPPORT_TAGS
- "TAGGED-QUEUING=%s "
-#endif
- "HOSTID=%d",
- instance->host_no, instance->hostt->can_queue,
- instance->hostt->cmd_per_lun,
- instance->hostt->sg_tablesize,
-#ifdef SUPPORT_TAGS
- setup_use_tagged_queuing ? "yes" : "no",
-#endif
- instance->hostt->this_id );
- NCR5380_print_options(instance);
- printk("\n");
-
- called = 1;
- return 1;
-}
-
-static int atari_scsi_release(struct Scsi_Host *sh)
-{
- if (IS_A_TT())
- free_irq(IRQ_TT_MFP_SCSI, sh);
- if (atari_dma_buffer)
- atari_stram_free(atari_dma_buffer);
- NCR5380_exit(sh);
+ stdma_lock(scsi_falcon_intr, instance);
return 1;
}
{
/* Format of atascsi parameter is:
* atascsi=<can_queue>,<cmd_per_lun>,<sg_tablesize>,<hostid>,<use_tags>
- * Defaults depend on TT or Falcon, hostid determined at run time.
+ * Defaults depend on TT or Falcon, determined at run time.
* Negative values mean don't change.
*/
int ints[6];
printk("atari_scsi_setup: no arguments!\n");
return 0;
}
-
- if (ints[0] >= 1) {
- if (ints[1] > 0)
- /* no limits on this, just > 0 */
- setup_can_queue = ints[1];
- }
- if (ints[0] >= 2) {
- if (ints[2] > 0)
- setup_cmd_per_lun = ints[2];
- }
- if (ints[0] >= 3) {
- if (ints[3] >= 0) {
- setup_sg_tablesize = ints[3];
- /* Must be <= SG_ALL (255) */
- if (setup_sg_tablesize > SG_ALL)
- setup_sg_tablesize = SG_ALL;
- }
- }
- if (ints[0] >= 4) {
- /* Must be between 0 and 7 */
- if (ints[4] >= 0 && ints[4] <= 7)
- setup_hostid = ints[4];
- else if (ints[4] > 7)
- printk("atari_scsi_setup: invalid host ID %d !\n", ints[4]);
- }
+ if (ints[0] >= 1)
+ setup_can_queue = ints[1];
+ if (ints[0] >= 2)
+ setup_cmd_per_lun = ints[2];
+ if (ints[0] >= 3)
+ setup_sg_tablesize = ints[3];
+ if (ints[0] >= 4)
+ setup_hostid = ints[4];
#ifdef SUPPORT_TAGS
- if (ints[0] >= 5) {
- if (ints[5] >= 0)
- setup_use_tagged_queuing = !!ints[5];
- }
+ if (ints[0] >= 5)
+ setup_use_tagged_queuing = ints[5];
#endif
return 1;
__setup("atascsi=", atari_scsi_setup);
#endif /* !MODULE */
-static int atari_scsi_bus_reset(Scsi_Cmnd *cmd)
-{
- int rv;
- struct NCR5380_hostdata *hostdata =
- (struct NCR5380_hostdata *)cmd->device->host->hostdata;
-
- /* For doing the reset, SCSI interrupts must be disabled first,
- * since the 5380 raises its IRQ line while _RST is active and we
- * can't disable interrupts completely, since we need the timer.
- */
- /* And abort a maybe active DMA transfer */
- if (IS_A_TT()) {
- atari_turnoff_irq(IRQ_TT_MFP_SCSI);
-#ifdef REAL_DMA
- tt_scsi_dma.dma_ctrl = 0;
-#endif /* REAL_DMA */
- } else {
- atari_turnoff_irq(IRQ_MFP_FSCSI);
-#ifdef REAL_DMA
- st_dma.dma_mode_status = 0x90;
- atari_dma_active = 0;
- atari_dma_orig_addr = NULL;
-#endif /* REAL_DMA */
- }
-
- rv = NCR5380_bus_reset(cmd);
-
- /* Re-enable ints */
- if (IS_A_TT()) {
- atari_turnon_irq(IRQ_TT_MFP_SCSI);
- } else {
- atari_turnon_irq(IRQ_MFP_FSCSI);
- }
- if (rv == SUCCESS)
- falcon_release_lock_if_possible(hostdata);
-
- return rv;
-}
-
#ifdef CONFIG_ATARI_SCSI_RESET_BOOT
static void __init atari_scsi_reset_boot(void)
}
#endif
-
-static const char *atari_scsi_info(struct Scsi_Host *host)
-{
- /* atari_scsi_detect() is verbose enough... */
- static const char string[] = "Atari native SCSI";
- return string;
-}
-
-
#if defined(REAL_DMA)
static unsigned long atari_scsi_dma_setup(struct Scsi_Host *instance,
#define CMD_SURELY_BYTE_MODE 1
#define CMD_MODE_UNKNOWN 2
-static int falcon_classify_cmd(Scsi_Cmnd *cmd)
+static int falcon_classify_cmd(struct scsi_cmnd *cmd)
{
unsigned char opcode = cmd->cmnd[0];
*/
static unsigned long atari_dma_xfer_len(unsigned long wanted_len,
- Scsi_Cmnd *cmd, int write_flag)
+ struct scsi_cmnd *cmd, int write_flag)
{
unsigned long possible_len, limit;
#include "atari_NCR5380.c"
-static struct scsi_host_template driver_template = {
+static int atari_scsi_bus_reset(struct scsi_cmnd *cmd)
+{
+ int rv;
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+#ifdef REAL_DMA
+ /* Abort a maybe active DMA transfer */
+ if (IS_A_TT()) {
+ tt_scsi_dma.dma_ctrl = 0;
+ } else {
+ st_dma.dma_mode_status = 0x90;
+ atari_dma_active = 0;
+ atari_dma_orig_addr = NULL;
+ }
+#endif
+
+ rv = NCR5380_bus_reset(cmd);
+
+ /* The 5380 raises its IRQ line while _RST is active but the ST DMA
+ * "lock" has been released so this interrupt may end up handled by
+ * floppy or IDE driver (if one of them holds the lock). The NCR5380
+ * interrupt flag has been cleared already.
+ */
+
+ local_irq_restore(flags);
+
+ return rv;
+}
+
+#define DRV_MODULE_NAME "atari_scsi"
+#define PFX DRV_MODULE_NAME ": "
+
+static struct scsi_host_template atari_scsi_template = {
+ .module = THIS_MODULE,
+ .proc_name = DRV_MODULE_NAME,
.show_info = atari_scsi_show_info,
.name = "Atari native SCSI",
- .detect = atari_scsi_detect,
- .release = atari_scsi_release,
.info = atari_scsi_info,
.queuecommand = atari_scsi_queue_command,
.eh_abort_handler = atari_scsi_abort,
.eh_bus_reset_handler = atari_scsi_bus_reset,
- .can_queue = 0, /* initialized at run-time */
- .this_id = 0, /* initialized at run-time */
- .sg_tablesize = 0, /* initialized at run-time */
- .cmd_per_lun = 0, /* initialized at run-time */
+ .this_id = 7,
.use_clustering = DISABLE_CLUSTERING
};
+static int __init atari_scsi_probe(struct platform_device *pdev)
+{
+ struct Scsi_Host *instance;
+ int error;
+ struct resource *irq;
+ int host_flags = 0;
+
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!irq)
+ return -ENODEV;
+
+ if (ATARIHW_PRESENT(TT_SCSI)) {
+ atari_scsi_reg_read = atari_scsi_tt_reg_read;
+ atari_scsi_reg_write = atari_scsi_tt_reg_write;
+ } else {
+ atari_scsi_reg_read = atari_scsi_falcon_reg_read;
+ atari_scsi_reg_write = atari_scsi_falcon_reg_write;
+ }
+
+ /* The values for CMD_PER_LUN and CAN_QUEUE are somehow arbitrary.
+ * Higher values should work, too; try it!
+ * (But cmd_per_lun costs memory!)
+ *
+ * But there seems to be a bug somewhere that requires CAN_QUEUE to be
+ * 2*CMD_PER_LUN. At least on a TT, no spurious timeouts seen since
+ * changed CMD_PER_LUN...
+ *
+ * Note: The Falcon currently uses 8/1 setting due to unsolved problems
+ * with cmd_per_lun != 1
+ */
+ if (ATARIHW_PRESENT(TT_SCSI)) {
+ atari_scsi_template.can_queue = 16;
+ atari_scsi_template.cmd_per_lun = 8;
+ atari_scsi_template.sg_tablesize = SG_ALL;
+ } else {
+ atari_scsi_template.can_queue = 8;
+ atari_scsi_template.cmd_per_lun = 1;
+ atari_scsi_template.sg_tablesize = SG_NONE;
+ }
+
+ if (setup_can_queue > 0)
+ atari_scsi_template.can_queue = setup_can_queue;
+
+ if (setup_cmd_per_lun > 0)
+ atari_scsi_template.cmd_per_lun = setup_cmd_per_lun;
+
+ /* Leave sg_tablesize at 0 on a Falcon! */
+ if (ATARIHW_PRESENT(TT_SCSI) && setup_sg_tablesize >= 0)
+ atari_scsi_template.sg_tablesize = setup_sg_tablesize;
+
+ if (setup_hostid >= 0) {
+ atari_scsi_template.this_id = setup_hostid & 7;
+ } else {
+ /* Test if a host id is set in the NVRam */
+ if (ATARIHW_PRESENT(TT_CLK) && nvram_check_checksum()) {
+ unsigned char b = nvram_read_byte(14);
+
+ /* Arbitration enabled? (for TOS)
+ * If yes, use configured host ID
+ */
+ if (b & 0x80)
+ atari_scsi_template.this_id = b & 7;
+ }
+ }
+
+
+#ifdef REAL_DMA
+ /* If running on a Falcon and if there's TT-Ram (i.e., more than one
+ * memory block, since there's always ST-Ram in a Falcon), then
+ * allocate a STRAM_BUFFER_SIZE byte dribble buffer for transfers
+ * from/to alternative Ram.
+ */
+ if (ATARIHW_PRESENT(ST_SCSI) && !ATARIHW_PRESENT(EXTD_DMA) &&
+ m68k_num_memory > 1) {
+ atari_dma_buffer = atari_stram_alloc(STRAM_BUFFER_SIZE, "SCSI");
+ if (!atari_dma_buffer) {
+ pr_err(PFX "can't allocate ST-RAM double buffer\n");
+ return -ENOMEM;
+ }
+ atari_dma_phys_buffer = atari_stram_to_phys(atari_dma_buffer);
+ atari_dma_orig_addr = 0;
+ }
+#endif
+
+ instance = scsi_host_alloc(&atari_scsi_template,
+ sizeof(struct NCR5380_hostdata));
+ if (!instance) {
+ error = -ENOMEM;
+ goto fail_alloc;
+ }
+ atari_scsi_host = instance;
+
+#ifdef CONFIG_ATARI_SCSI_RESET_BOOT
+ atari_scsi_reset_boot();
+#endif
+
+ instance->irq = irq->start;
+
+ host_flags |= IS_A_TT() ? 0 : FLAG_LATE_DMA_SETUP;
+
+#ifdef SUPPORT_TAGS
+ host_flags |= setup_use_tagged_queuing > 0 ? FLAG_TAGGED_QUEUING : 0;
+#endif
+
+ NCR5380_init(instance, host_flags);
+
+ if (IS_A_TT()) {
+ error = request_irq(instance->irq, scsi_tt_intr, 0,
+ "NCR5380", instance);
+ if (error) {
+ pr_err(PFX "request irq %d failed, aborting\n",
+ instance->irq);
+ goto fail_irq;
+ }
+ tt_mfp.active_edge |= 0x80; /* SCSI int on L->H */
+#ifdef REAL_DMA
+ tt_scsi_dma.dma_ctrl = 0;
+ atari_dma_residual = 0;
+
+ /* While the read overruns (described by Drew Eckhardt in
+ * NCR5380.c) never happened on TTs, they do in fact on the
+ * Medusa (This was the cause why SCSI didn't work right for
+ * so long there.) Since handling the overruns slows down
+ * a bit, I turned the #ifdef's into a runtime condition.
+ *
+ * In principle it should be sufficient to do max. 1 byte with
+ * PIO, but there is another problem on the Medusa with the DMA
+ * rest data register. So read_overruns is currently set
+ * to 4 to avoid having transfers that aren't a multiple of 4.
+ * If the rest data bug is fixed, this can be lowered to 1.
+ */
+ if (MACH_IS_MEDUSA) {
+ struct NCR5380_hostdata *hostdata =
+ shost_priv(instance);
+
+ hostdata->read_overruns = 4;
+ }
+#endif
+ } else {
+ /* Nothing to do for the interrupt: the ST-DMA is initialized
+ * already.
+ */
+#ifdef REAL_DMA
+ atari_dma_residual = 0;
+ atari_dma_active = 0;
+ atari_dma_stram_mask = (ATARIHW_PRESENT(EXTD_DMA) ? 0x00000000
+ : 0xff000000);
+#endif
+ }
+
+ error = scsi_add_host(instance, NULL);
+ if (error)
+ goto fail_host;
+
+ platform_set_drvdata(pdev, instance);
+
+ scsi_scan_host(instance);
+ return 0;
+
+fail_host:
+ if (IS_A_TT())
+ free_irq(instance->irq, instance);
+fail_irq:
+ NCR5380_exit(instance);
+ scsi_host_put(instance);
+fail_alloc:
+ if (atari_dma_buffer)
+ atari_stram_free(atari_dma_buffer);
+ return error;
+}
+
+static int __exit atari_scsi_remove(struct platform_device *pdev)
+{
+ struct Scsi_Host *instance = platform_get_drvdata(pdev);
+
+ scsi_remove_host(instance);
+ if (IS_A_TT())
+ free_irq(instance->irq, instance);
+ NCR5380_exit(instance);
+ scsi_host_put(instance);
+ if (atari_dma_buffer)
+ atari_stram_free(atari_dma_buffer);
+ return 0;
+}
+
+static struct platform_driver atari_scsi_driver = {
+ .remove = __exit_p(atari_scsi_remove),
+ .driver = {
+ .name = DRV_MODULE_NAME,
+ .owner = THIS_MODULE,
+ },
+};
-#include "scsi_module.c"
+module_platform_driver_probe(atari_scsi_driver, atari_scsi_probe);
+MODULE_ALIAS("platform:" DRV_MODULE_NAME);
MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * atari_scsi.h -- Header file for the Atari native SCSI driver
- *
- * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
- *
- * (Loosely based on the work of Robert De Vries' team)
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file COPYING in the main directory of this archive
- * for more details.
- *
- */
-
-
-#ifndef ATARI_SCSI_H
-#define ATARI_SCSI_H
-
-/* (I_HAVE_OVERRUNS stuff removed) */
-
-#ifndef ASM
-/* The values for CMD_PER_LUN and CAN_QUEUE are somehow arbitrary. Higher
- * values should work, too; try it! (but cmd_per_lun costs memory!) */
-
-/* But there seems to be a bug somewhere that requires CAN_QUEUE to be
- * 2*CMD_PER_LUN. At least on a TT, no spurious timeouts seen since
- * changed CMD_PER_LUN... */
-
-/* Note: The Falcon currently uses 8/1 setting due to unsolved problems with
- * cmd_per_lun != 1 */
-
-#define ATARI_TT_CAN_QUEUE 16
-#define ATARI_TT_CMD_PER_LUN 8
-#define ATARI_TT_SG_TABLESIZE SG_ALL
-
-#define ATARI_FALCON_CAN_QUEUE 8
-#define ATARI_FALCON_CMD_PER_LUN 1
-#define ATARI_FALCON_SG_TABLESIZE SG_NONE
-
-#define DEFAULT_USE_TAGGED_QUEUING 0
-
-
-#define NCR5380_implementation_fields /* none */
-
-#define NCR5380_read(reg) atari_scsi_reg_read( reg )
-#define NCR5380_write(reg, value) atari_scsi_reg_write( reg, value )
-
-#define NCR5380_intr atari_scsi_intr
-#define NCR5380_queue_command atari_scsi_queue_command
-#define NCR5380_abort atari_scsi_abort
-#define NCR5380_show_info atari_scsi_show_info
-#define NCR5380_dma_read_setup(inst,d,c) atari_scsi_dma_setup (inst, d, c, 0)
-#define NCR5380_dma_write_setup(inst,d,c) atari_scsi_dma_setup (inst, d, c, 1)
-#define NCR5380_dma_residual(inst) atari_scsi_dma_residual( inst )
-#define NCR5380_dma_xfer_len(i,cmd,phase) \
- atari_dma_xfer_len(cmd->SCp.this_residual,cmd,((phase) & SR_IO) ? 0 : 1)
-
-#endif /* ndef ASM */
-#endif /* ATARI_SCSI_H */
-
-
.name = "Emulex 10Gbe open-iscsi Initiator Driver",
.proc_name = DRV_NAME,
.queuecommand = iscsi_queuecommand,
- .change_queue_depth = iscsi_change_queue_depth,
+ .change_queue_depth = scsi_change_queue_depth,
.slave_configure = beiscsi_slave_configure,
.target_alloc = iscsi_target_alloc,
.eh_abort_handler = beiscsi_eh_abort,
.cmd_per_lun = BEISCSI_CMD_PER_LUN,
.use_clustering = ENABLE_CLUSTERING,
.vendor_id = SCSI_NL_VID_TYPE_PCI | BE_VENDOR_ID,
-
+ .track_queue_depth = 1,
};
static struct scsi_transport_template *beiscsi_scsi_transport;
unsigned long flags;
void *kern_buf;
- kern_buf = kzalloc(nbytes, GFP_KERNEL);
-
- if (!kern_buf) {
- printk(KERN_INFO "bfad[%d]: Failed to allocate buffer\n",
- bfad->inst_no);
- return -ENOMEM;
- }
-
- if (copy_from_user(kern_buf, (void __user *)buf, nbytes)) {
- kfree(kern_buf);
- return -ENOMEM;
- }
+ kern_buf = memdup_user(buf, nbytes);
+ if (IS_ERR(kern_buf))
+ return PTR_ERR(kern_buf);
rc = sscanf(kern_buf, "%x:%x", &addr, &len);
if (rc < 2) {
unsigned long flags;
void *kern_buf;
- kern_buf = kzalloc(nbytes, GFP_KERNEL);
-
- if (!kern_buf) {
- printk(KERN_INFO "bfad[%d]: Failed to allocate buffer\n",
- bfad->inst_no);
- return -ENOMEM;
- }
-
- if (copy_from_user(kern_buf, (void __user *)buf, nbytes)) {
- kfree(kern_buf);
- return -ENOMEM;
- }
+ kern_buf = memdup_user(buf, nbytes);
+ if (IS_ERR(kern_buf))
+ return PTR_ERR(kern_buf);
rc = sscanf(kern_buf, "%x:%x", &addr, &val);
if (rc < 2) {
static int
bfad_im_slave_configure(struct scsi_device *sdev)
{
- if (sdev->tagged_supported)
- scsi_activate_tcq(sdev, bfa_lun_queue_depth);
- else
- scsi_deactivate_tcq(sdev, bfa_lun_queue_depth);
-
+ scsi_change_queue_depth(sdev, bfa_lun_queue_depth);
return 0;
}
.shost_attrs = bfad_im_host_attrs,
.max_sectors = BFAD_MAX_SECTORS,
.vendor_id = BFA_PCI_VENDOR_ID_BROCADE,
+ .use_blk_tags = 1,
};
struct scsi_host_template bfad_im_vport_template = {
.use_clustering = ENABLE_CLUSTERING,
.shost_attrs = bfad_im_vport_attrs,
.max_sectors = BFAD_MAX_SECTORS,
+ .use_blk_tags = 1,
};
bfa_status_t
if (bfa_lun_queue_depth > tmp_sdev->queue_depth) {
if (tmp_sdev->id != sdev->id)
continue;
- if (tmp_sdev->ordered_tags)
- scsi_adjust_queue_depth(tmp_sdev,
- MSG_ORDERED_TAG,
- tmp_sdev->queue_depth + 1);
- else
- scsi_adjust_queue_depth(tmp_sdev,
- MSG_SIMPLE_TAG,
- tmp_sdev->queue_depth + 1);
+ scsi_change_queue_depth(tmp_sdev,
+ tmp_sdev->queue_depth + 1);
itnim->last_ramp_up_time = jiffies;
}
bnx2fc_initiate_cleanup(orig_io_req);
/* Post a new IO req with the same sc_cmd */
BNX2FC_IO_DBG(rec_req, "Post IO request again\n");
- spin_unlock_bh(&tgt->tgt_lock);
rc = bnx2fc_post_io_req(tgt, new_io_req);
- spin_lock_bh(&tgt->tgt_lock);
if (!rc)
goto free_frame;
BNX2FC_IO_DBG(rec_req, "REC: io post err\n");
struct fc_frame_header *fh;
struct fcoe_rcv_info *fr;
struct fcoe_percpu_s *bg;
+ struct sk_buff *tmp_skb;
unsigned short oxid;
interface = container_of(ptype, struct bnx2fc_interface,
goto err;
}
+ tmp_skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!tmp_skb)
+ goto err;
+
+ skb = tmp_skb;
+
if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) {
printk(KERN_ERR PFX "bnx2fc_rcv: Wrong FC type frame\n");
goto err;
mutex_unlock(&bnx2fc_dev_lock);
rtnl_unlock();
- if (IS_ERR(vn_port)) {
+ if (!vn_port) {
printk(KERN_ERR PFX "bnx2fc_vport_create (%s) failed\n",
netdev->name);
return -EIO;
interface = bnx2fc_interface_create(hba, netdev, fip_mode);
if (!interface) {
printk(KERN_ERR PFX "bnx2fc_interface_create failed\n");
+ rc = -ENOMEM;
goto ifput_err;
}
.eh_target_reset_handler = bnx2fc_eh_target_reset, /* tgt reset */
.eh_host_reset_handler = fc_eh_host_reset,
.slave_alloc = fc_slave_alloc,
- .change_queue_depth = fc_change_queue_depth,
- .change_queue_type = fc_change_queue_type,
+ .change_queue_depth = scsi_change_queue_depth,
+ .change_queue_type = scsi_change_queue_type,
.this_id = -1,
.cmd_per_lun = 3,
.use_clustering = ENABLE_CLUSTERING,
.sg_tablesize = BNX2FC_MAX_BDS_PER_CMD,
.max_sectors = 1024,
+ .use_blk_tags = 1,
+ .track_queue_depth = 1,
};
static struct libfc_function_template bnx2fc_libfc_fcn_templ = {
struct fcp_cmnd *fcp_cmnd)
{
struct scsi_cmnd *sc_cmd = io_req->sc_cmd;
- char tag[2];
memset(fcp_cmnd, 0, sizeof(struct fcp_cmnd));
fcp_cmnd->fc_tm_flags = io_req->mp_req.tm_flags;
fcp_cmnd->fc_flags = io_req->io_req_flags;
- if (scsi_populate_tag_msg(sc_cmd, tag)) {
- switch (tag[0]) {
- case HEAD_OF_QUEUE_TAG:
- fcp_cmnd->fc_pri_ta = FCP_PTA_HEADQ;
- break;
- case ORDERED_QUEUE_TAG:
- fcp_cmnd->fc_pri_ta = FCP_PTA_ORDERED;
- break;
- default:
- fcp_cmnd->fc_pri_ta = FCP_PTA_SIMPLE;
- break;
- }
- } else {
+ if (sc_cmd->flags & SCMD_TAGGED)
+ fcp_cmnd->fc_pri_ta = FCP_PTA_SIMPLE;
+ else
fcp_cmnd->fc_pri_ta = 0;
- }
}
static void bnx2fc_parse_fcp_rsp(struct bnx2fc_cmd *io_req,
goto exit_qcmd;
}
}
+
+ spin_lock_bh(&tgt->tgt_lock);
+
io_req = bnx2fc_cmd_alloc(tgt);
if (!io_req) {
rc = SCSI_MLQUEUE_HOST_BUSY;
- goto exit_qcmd;
+ goto exit_qcmd_tgtlock;
}
io_req->sc_cmd = sc_cmd;
if (bnx2fc_post_io_req(tgt, io_req)) {
printk(KERN_ERR PFX "Unable to post io_req\n");
rc = SCSI_MLQUEUE_HOST_BUSY;
- goto exit_qcmd;
+ goto exit_qcmd_tgtlock;
}
+
+exit_qcmd_tgtlock:
+ spin_unlock_bh(&tgt->tgt_lock);
exit_qcmd:
return rc;
}
int task_idx, index;
u16 xid;
+ /* bnx2fc_post_io_req() is called with the tgt_lock held */
+
/* Initialize rest of io_req fields */
io_req->cmd_type = BNX2FC_SCSI_CMD;
io_req->port = port;
/* Build buffer descriptor list for firmware from sg list */
if (bnx2fc_build_bd_list_from_sg(io_req)) {
printk(KERN_ERR PFX "BD list creation failed\n");
- spin_lock_bh(&tgt->tgt_lock);
kref_put(&io_req->refcount, bnx2fc_cmd_release);
- spin_unlock_bh(&tgt->tgt_lock);
return -EAGAIN;
}
task = &(task_page[index]);
bnx2fc_init_task(io_req, task);
- spin_lock_bh(&tgt->tgt_lock);
-
if (tgt->flush_in_prog) {
printk(KERN_ERR PFX "Flush in progress..Host Busy\n");
kref_put(&io_req->refcount, bnx2fc_cmd_release);
- spin_unlock_bh(&tgt->tgt_lock);
return -EAGAIN;
}
if (!test_bit(BNX2FC_FLAG_SESSION_READY, &tgt->flags)) {
printk(KERN_ERR PFX "Session not ready...post_io\n");
kref_put(&io_req->refcount, bnx2fc_cmd_release);
- spin_unlock_bh(&tgt->tgt_lock);
return -EAGAIN;
}
/* Ring doorbell */
bnx2fc_ring_doorbell(tgt);
- spin_unlock_bh(&tgt->tgt_lock);
return 0;
}
.eh_abort_handler = iscsi_eh_abort,
.eh_device_reset_handler = iscsi_eh_device_reset,
.eh_target_reset_handler = iscsi_eh_recover_target,
- .change_queue_depth = iscsi_change_queue_depth,
+ .change_queue_depth = scsi_change_queue_depth,
.target_alloc = iscsi_target_alloc,
.can_queue = 2048,
.max_sectors = 127,
.use_clustering = ENABLE_CLUSTERING,
.sg_tablesize = ISCSI_MAX_BDS_PER_CMD,
.shost_attrs = bnx2i_dev_attributes,
+ .track_queue_depth = 1,
};
struct iscsi_transport bnx2i_iscsi_transport = {
// module_param_string_array(vendor_labels, NULL, 0444);
#define ch_printk(prefix, ch, fmt, a...) \
- sdev_printk(prefix, (ch)->device, "[%s] " fmt, \
- (ch)->name, ##a)
+ sdev_prefix_printk(prefix, (ch)->device, (ch)->name, fmt, ##a)
#define DPRINTK(fmt, arg...) \
do { \
}
static int
-ch_do_scsi(scsi_changer *ch, unsigned char *cmd,
+ch_do_scsi(scsi_changer *ch, unsigned char *cmd, int cmd_len,
void *buffer, unsigned buflength,
enum dma_data_direction direction)
{
errno = 0;
if (debug) {
DPRINTK("command: ");
- __scsi_print_command(cmd);
+ __scsi_print_command(cmd, cmd_len);
}
result = scsi_execute_req(ch->device, cmd, direction, buffer,
DPRINTK("result: 0x%x\n",result);
if (driver_byte(result) & DRIVER_SENSE) {
if (debug)
- scsi_print_sense_hdr(ch->name, &sshdr);
+ scsi_print_sense_hdr(ch->device, ch->name, &sshdr);
errno = ch_find_errno(&sshdr);
switch(sshdr.sense_key) {
cmd[3] = elem & 0xff;
cmd[5] = 1;
cmd[9] = 255;
- if (0 == (result = ch_do_scsi(ch, cmd, buffer, 256, DMA_FROM_DEVICE))) {
+ if (0 == (result = ch_do_scsi(ch, cmd, 12,
+ buffer, 256, DMA_FROM_DEVICE))) {
if (((buffer[16] << 8) | buffer[17]) != elem) {
DPRINTK("asked for element 0x%02x, got 0x%02x\n",
elem,(buffer[16] << 8) | buffer[17]);
memset(cmd,0,sizeof(cmd));
cmd[0] = INITIALIZE_ELEMENT_STATUS;
cmd[1] = (ch->device->lun & 0x7) << 5;
- err = ch_do_scsi(ch, cmd, NULL, 0, DMA_NONE);
+ err = ch_do_scsi(ch, cmd, 6, NULL, 0, DMA_NONE);
VPRINTK(KERN_INFO, "... finished\n");
return err;
}
cmd[1] = (ch->device->lun & 0x7) << 5;
cmd[2] = 0x1d;
cmd[4] = 255;
- result = ch_do_scsi(ch, cmd, buffer, 255, DMA_FROM_DEVICE);
+ result = ch_do_scsi(ch, cmd, 10, buffer, 255, DMA_FROM_DEVICE);
if (0 != result) {
cmd[1] |= (1<<3);
- result = ch_do_scsi(ch, cmd, buffer, 255, DMA_FROM_DEVICE);
+ result = ch_do_scsi(ch, cmd, 10, buffer, 255, DMA_FROM_DEVICE);
}
if (0 == result) {
ch->firsts[CHET_MT] =
cmd[4] = (elem >> 8) & 0xff;
cmd[5] = elem & 0xff;
cmd[8] = rotate ? 1 : 0;
- return ch_do_scsi(ch, cmd, NULL, 0, DMA_NONE);
+ return ch_do_scsi(ch, cmd, 10, NULL, 0, DMA_NONE);
}
static int
cmd[6] = (dest >> 8) & 0xff;
cmd[7] = dest & 0xff;
cmd[10] = rotate ? 1 : 0;
- return ch_do_scsi(ch, cmd, NULL,0, DMA_NONE);
+ return ch_do_scsi(ch, cmd, 12, NULL,0, DMA_NONE);
}
static int
cmd[9] = dest2 & 0xff;
cmd[10] = (rotate1 ? 1 : 0) | (rotate2 ? 2 : 0);
- return ch_do_scsi(ch, cmd, NULL,0, DMA_NONE);
+ return ch_do_scsi(ch, cmd, 12, NULL, 0, DMA_NONE);
}
static void
memcpy(buffer,tag,32);
ch_check_voltag(buffer);
- result = ch_do_scsi(ch, cmd, buffer, 256, DMA_TO_DEVICE);
+ result = ch_do_scsi(ch, cmd, 12, buffer, 256, DMA_TO_DEVICE);
kfree(buffer);
return result;
}
int retval;
void __user *argp = (void __user *)arg;
+ retval = scsi_ioctl_block_when_processing_errors(ch->device, cmd,
+ file->f_flags & O_NDELAY);
+ if (retval)
+ return retval;
+
switch (cmd) {
case CHIOGPARAMS:
{
ch_cmd[5] = 1;
ch_cmd[9] = 255;
- result = ch_do_scsi(ch, ch_cmd, buffer, 256, DMA_FROM_DEVICE);
+ result = ch_do_scsi(ch, ch_cmd, 12,
+ buffer, 256, DMA_FROM_DEVICE);
if (!result) {
cge.cge_status = buffer[18];
cge.cge_flags = 0;
}
static struct scsi_driver ch_template = {
- .owner = THIS_MODULE,
.gendrv = {
.name = "ch",
+ .owner = THIS_MODULE,
.probe = ch_probe,
.remove = ch_remove,
},
/* Commands with service actions that change the command name */
-#define SERVICE_ACTION_IN_12 0xab
-#define SERVICE_ACTION_OUT_12 0xa9
-#define SERVICE_ACTION_BIDIRECTIONAL 0x9d
-#define SERVICE_ACTION_IN_16 0x9e
-#define SERVICE_ACTION_OUT_16 0x9f
#define THIRD_PARTY_COPY_OUT 0x83
#define THIRD_PARTY_COPY_IN 0x84
+#define VENDOR_SPECIFIC_CDB 0xc0
+struct sa_name_list {
+ int opcode;
+ const struct value_name_pair *arr;
+ int arr_sz;
+};
+
+struct value_name_pair {
+ int value;
+ const char * name;
+};
#ifdef CONFIG_SCSI_CONSTANTS
static const char * cdb_byte0_names[] = {
"Volume set (out), Send DVD structure",
};
-struct value_name_pair {
- int value;
- const char * name;
-};
-
static const struct value_name_pair maint_in_arr[] = {
{0x5, "Report identifying information"},
{0xa, "Report target port groups"},
};
#define VARIABLE_LENGTH_SZ ARRAY_SIZE(variable_length_arr)
-static const char * get_sa_name(const struct value_name_pair * arr,
- int arr_sz, int service_action)
-{
- int k;
+static struct sa_name_list sa_names_arr[] = {
+ {VARIABLE_LENGTH_CMD, variable_length_arr, VARIABLE_LENGTH_SZ},
+ {MAINTENANCE_IN, maint_in_arr, MAINT_IN_SZ},
+ {MAINTENANCE_OUT, maint_out_arr, MAINT_OUT_SZ},
+ {PERSISTENT_RESERVE_IN, pr_in_arr, PR_IN_SZ},
+ {PERSISTENT_RESERVE_OUT, pr_out_arr, PR_OUT_SZ},
+ {SERVICE_ACTION_IN_12, serv_in12_arr, SERV_IN12_SZ},
+ {SERVICE_ACTION_OUT_12, serv_out12_arr, SERV_OUT12_SZ},
+ {SERVICE_ACTION_BIDIRECTIONAL, serv_bidi_arr, SERV_BIDI_SZ},
+ {SERVICE_ACTION_IN_16, serv_in16_arr, SERV_IN16_SZ},
+ {SERVICE_ACTION_OUT_16, serv_out16_arr, SERV_OUT16_SZ},
+ {THIRD_PARTY_COPY_IN, tpc_in_arr, TPC_IN_SZ},
+ {THIRD_PARTY_COPY_OUT, tpc_out_arr, TPC_OUT_SZ},
+ {0, NULL, 0},
+};
- for (k = 0; k < arr_sz; ++k, ++arr) {
- if (service_action == arr->value)
- break;
- }
- return (k < arr_sz) ? arr->name : NULL;
-}
+#else /* ifndef CONFIG_SCSI_CONSTANTS */
+static const char *cdb_byte0_names[0];
+
+static struct sa_name_list sa_names_arr[] = {
+ {VARIABLE_LENGTH_CMD, NULL, 0},
+ {MAINTENANCE_IN, NULL, 0},
+ {MAINTENANCE_OUT, NULL, 0},
+ {PERSISTENT_RESERVE_IN, NULL, 0},
+ {PERSISTENT_RESERVE_OUT, NULL, 0},
+ {SERVICE_ACTION_IN_12, NULL, 0},
+ {SERVICE_ACTION_OUT_12, NULL, 0},
+ {SERVICE_ACTION_BIDIRECTIONAL, NULL, 0},
+ {SERVICE_ACTION_IN_16, NULL, 0},
+ {SERVICE_ACTION_OUT_16, NULL, 0},
+ {THIRD_PARTY_COPY_IN, NULL, 0},
+ {THIRD_PARTY_COPY_OUT, NULL, 0},
+ {0, NULL, 0},
+};
+#endif /* CONFIG_SCSI_CONSTANTS */
-/* attempt to guess cdb length if cdb_len==0 . No trailing linefeed. */
-static void print_opcode_name(unsigned char * cdbp, int cdb_len)
+static bool scsi_opcode_sa_name(int opcode, int service_action,
+ const char **cdb_name, const char **sa_name)
{
- int sa, len, cdb0;
- int fin_name = 0;
- const char * name;
+ struct sa_name_list *sa_name_ptr;
+ const struct value_name_pair *arr = NULL;
+ int arr_sz, k;
- cdb0 = cdbp[0];
- switch(cdb0) {
- case VARIABLE_LENGTH_CMD:
- len = scsi_varlen_cdb_length(cdbp);
- if (len < 10) {
- printk("short variable length command, "
- "len=%d ext_len=%d", len, cdb_len);
- break;
- }
- sa = (cdbp[8] << 8) + cdbp[9];
- name = get_sa_name(variable_length_arr, VARIABLE_LENGTH_SZ,
- sa);
- if (name)
- printk("%s", name);
- else
- printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa);
+ *cdb_name = NULL;
+ if (opcode >= VENDOR_SPECIFIC_CDB)
+ return false;
- if ((cdb_len > 0) && (len != cdb_len))
- printk(", in_cdb_len=%d, ext_len=%d", len, cdb_len);
+ if (opcode < ARRAY_SIZE(cdb_byte0_names))
+ *cdb_name = cdb_byte0_names[opcode];
- break;
- case MAINTENANCE_IN:
- sa = cdbp[1] & 0x1f;
- name = get_sa_name(maint_in_arr, MAINT_IN_SZ, sa);
- fin_name = 1;
- break;
- case MAINTENANCE_OUT:
- sa = cdbp[1] & 0x1f;
- name = get_sa_name(maint_out_arr, MAINT_OUT_SZ, sa);
- fin_name = 1;
- break;
- case PERSISTENT_RESERVE_IN:
- sa = cdbp[1] & 0x1f;
- name = get_sa_name(pr_in_arr, PR_IN_SZ, sa);
- fin_name = 1;
- break;
- case PERSISTENT_RESERVE_OUT:
- sa = cdbp[1] & 0x1f;
- name = get_sa_name(pr_out_arr, PR_OUT_SZ, sa);
- fin_name = 1;
- break;
- case SERVICE_ACTION_IN_12:
- sa = cdbp[1] & 0x1f;
- name = get_sa_name(serv_in12_arr, SERV_IN12_SZ, sa);
- fin_name = 1;
- break;
- case SERVICE_ACTION_OUT_12:
- sa = cdbp[1] & 0x1f;
- name = get_sa_name(serv_out12_arr, SERV_OUT12_SZ, sa);
- fin_name = 1;
- break;
- case SERVICE_ACTION_BIDIRECTIONAL:
- sa = cdbp[1] & 0x1f;
- name = get_sa_name(serv_bidi_arr, SERV_BIDI_SZ, sa);
- fin_name = 1;
- break;
- case SERVICE_ACTION_IN_16:
- sa = cdbp[1] & 0x1f;
- name = get_sa_name(serv_in16_arr, SERV_IN16_SZ, sa);
- fin_name = 1;
- break;
- case SERVICE_ACTION_OUT_16:
- sa = cdbp[1] & 0x1f;
- name = get_sa_name(serv_out16_arr, SERV_OUT16_SZ, sa);
- fin_name = 1;
- break;
- case THIRD_PARTY_COPY_IN:
- sa = cdbp[1] & 0x1f;
- name = get_sa_name(tpc_in_arr, TPC_IN_SZ, sa);
- fin_name = 1;
- break;
- case THIRD_PARTY_COPY_OUT:
- sa = cdbp[1] & 0x1f;
- name = get_sa_name(tpc_out_arr, TPC_OUT_SZ, sa);
- fin_name = 1;
- break;
- default:
- if (cdb0 < 0xc0) {
- name = cdb_byte0_names[cdb0];
- if (name)
- printk("%s", name);
- else
- printk("cdb[0]=0x%x (reserved)", cdb0);
- } else
- printk("cdb[0]=0x%x (vendor)", cdb0);
- break;
+ for (sa_name_ptr = sa_names_arr; sa_name_ptr->arr; ++sa_name_ptr) {
+ if (sa_name_ptr->opcode == opcode) {
+ arr = sa_name_ptr->arr;
+ arr_sz = sa_name_ptr->arr_sz;
+ break;
+ }
}
- if (fin_name) {
- if (name)
- printk("%s", name);
- else
- printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa);
+ if (!arr)
+ return false;
+
+ for (k = 0; k < arr_sz; ++k, ++arr) {
+ if (service_action == arr->value)
+ break;
}
-}
+ if (k < arr_sz)
+ *sa_name = arr->name;
-#else /* ifndef CONFIG_SCSI_CONSTANTS */
+ return true;
+}
-static void print_opcode_name(unsigned char * cdbp, int cdb_len)
+static void print_opcode_name(const unsigned char *cdbp, size_t cdb_len)
{
- int sa, len, cdb0;
+ int sa, cdb0;
+ const char *cdb_name = NULL, *sa_name = NULL;
cdb0 = cdbp[0];
- switch(cdb0) {
- case VARIABLE_LENGTH_CMD:
- len = scsi_varlen_cdb_length(cdbp);
- if (len < 10) {
- printk("short opcode=0x%x command, len=%d "
- "ext_len=%d", cdb0, len, cdb_len);
- break;
+ if (cdb0 == VARIABLE_LENGTH_CMD) {
+ if (cdb_len < 10) {
+ printk("short variable length command, len=%zu",
+ cdb_len);
+ return;
}
sa = (cdbp[8] << 8) + cdbp[9];
- printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa);
- if (len != cdb_len)
- printk(", in_cdb_len=%d, ext_len=%d", len, cdb_len);
- break;
- case MAINTENANCE_IN:
- case MAINTENANCE_OUT:
- case PERSISTENT_RESERVE_IN:
- case PERSISTENT_RESERVE_OUT:
- case SERVICE_ACTION_IN_12:
- case SERVICE_ACTION_OUT_12:
- case SERVICE_ACTION_BIDIRECTIONAL:
- case SERVICE_ACTION_IN_16:
- case SERVICE_ACTION_OUT_16:
- case THIRD_PARTY_COPY_IN:
- case THIRD_PARTY_COPY_OUT:
+ } else
sa = cdbp[1] & 0x1f;
- printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa);
- break;
- default:
- if (cdb0 < 0xc0)
+
+ if (!scsi_opcode_sa_name(cdb0, sa, &cdb_name, &sa_name)) {
+ if (cdb_name)
+ printk("%s", cdb_name);
+ else if (cdb0 >= VENDOR_SPECIFIC_CDB)
+ printk("cdb[0]=0x%x (vendor)", cdb0);
+ else if (cdb0 >= 0x60 && cdb0 < 0x7e)
+ printk("cdb[0]=0x%x (reserved)", cdb0);
+ else
printk("cdb[0]=0x%x", cdb0);
+ } else {
+ if (sa_name)
+ printk("%s", sa_name);
+ else if (cdb_name)
+ printk("%s, sa=0x%x", cdb_name, sa);
else
- printk("cdb[0]=0x%x (vendor)", cdb0);
- break;
+ printk("cdb[0]=0x%x, sa=0x%x", cdb0, sa);
}
}
-#endif
-void __scsi_print_command(unsigned char *cdb)
+void __scsi_print_command(const unsigned char *cdb, size_t cdb_len)
{
int k, len;
- print_opcode_name(cdb, 0);
+ print_opcode_name(cdb, cdb_len);
len = scsi_command_size(cdb);
+ if (cdb_len < len)
+ len = cdb_len;
/* print out all bytes in cdb */
for (k = 0; k < len; ++k)
printk(" %02x", cdb[k]);
}
EXPORT_SYMBOL(scsi_print_command);
-/**
- * scsi_print_status - print scsi status description
- * @scsi_status: scsi status value
- *
- * If the status is recognized, the description is printed.
- * Otherwise "Unknown status" is output. No trailing space.
- * If CONFIG_SCSI_CONSTANTS is not set, then print status in hex
- * (e.g. "0x2" for Check Condition).
- **/
-void
-scsi_print_status(unsigned char scsi_status) {
-#ifdef CONFIG_SCSI_CONSTANTS
- const char * ccp;
-
- switch (scsi_status) {
- case 0: ccp = "Good"; break;
- case 0x2: ccp = "Check Condition"; break;
- case 0x4: ccp = "Condition Met"; break;
- case 0x8: ccp = "Busy"; break;
- case 0x10: ccp = "Intermediate"; break;
- case 0x14: ccp = "Intermediate-Condition Met"; break;
- case 0x18: ccp = "Reservation Conflict"; break;
- case 0x22: ccp = "Command Terminated"; break; /* obsolete */
- case 0x28: ccp = "Task set Full"; break; /* was: Queue Full */
- case 0x30: ccp = "ACA Active"; break;
- case 0x40: ccp = "Task Aborted"; break;
- default: ccp = "Unknown status";
- }
- printk(KERN_INFO "%s", ccp);
-#else
- printk(KERN_INFO "0x%0x", scsi_status);
-#endif
-}
-EXPORT_SYMBOL(scsi_print_status);
-
#ifdef CONFIG_SCSI_CONSTANTS
struct error_info {
struct error_info2 {
unsigned char code1, code2_min, code2_max;
+ const char * str;
const char * fmt;
};
static const struct error_info2 additional2[] =
{
- {0x40, 0x00, 0x7f, "Ram failure (%x)"},
- {0x40, 0x80, 0xff, "Diagnostic failure on component (%x)"},
- {0x41, 0x00, 0xff, "Data path failure (%x)"},
- {0x42, 0x00, 0xff, "Power-on or self-test failure (%x)"},
- {0x4D, 0x00, 0xff, "Tagged overlapped commands (task tag %x)"},
- {0x70, 0x00, 0xff, "Decompression exception short algorithm id of %x"},
- {0, 0, 0, NULL}
+ {0x40, 0x00, 0x7f, "Ram failure", ""},
+ {0x40, 0x80, 0xff, "Diagnostic failure on component", ""},
+ {0x41, 0x00, 0xff, "Data path failure", ""},
+ {0x42, 0x00, 0xff, "Power-on or self-test failure", ""},
+ {0x4D, 0x00, 0xff, "Tagged overlapped commands", "task tag "},
+ {0x70, 0x00, 0xff, "Decompression exception", "short algorithm id of "},
+ {0, 0, 0, NULL, NULL}
};
/* description of the sense key values */
* This string may contain a "%x" and should be printed with ascq as arg.
*/
const char *
-scsi_extd_sense_format(unsigned char asc, unsigned char ascq) {
+scsi_extd_sense_format(unsigned char asc, unsigned char ascq, const char **fmt)
+{
#ifdef CONFIG_SCSI_CONSTANTS
int i;
unsigned short code = ((asc << 8) | ascq);
+ *fmt = NULL;
for (i = 0; additional[i].text; i++)
if (additional[i].code12 == code)
return additional[i].text;
for (i = 0; additional2[i].fmt; i++) {
if (additional2[i].code1 == asc &&
ascq >= additional2[i].code2_min &&
- ascq <= additional2[i].code2_max)
- return additional2[i].fmt;
+ ascq <= additional2[i].code2_max) {
+ *fmt = additional2[i].fmt;
+ return additional2[i].str;
+ }
}
+#else
+ *fmt = NULL;
#endif
return NULL;
}
EXPORT_SYMBOL(scsi_extd_sense_format);
void
-scsi_show_extd_sense(unsigned char asc, unsigned char ascq)
+scsi_show_extd_sense(const struct scsi_device *sdev, const char *name,
+ unsigned char asc, unsigned char ascq)
{
- const char *extd_sense_fmt = scsi_extd_sense_format(asc, ascq);
-
- if (extd_sense_fmt) {
- if (strstr(extd_sense_fmt, "%x")) {
- printk("Add. Sense: ");
- printk(extd_sense_fmt, ascq);
- } else
- printk("Add. Sense: %s", extd_sense_fmt);
- } else {
- if (asc >= 0x80)
- printk("<<vendor>> ASC=0x%x ASCQ=0x%x", asc,
- ascq);
- if (ascq >= 0x80)
- printk("ASC=0x%x <<vendor>> ASCQ=0x%x", asc,
- ascq);
+ const char *extd_sense_fmt = NULL;
+ const char *extd_sense_str = scsi_extd_sense_format(asc, ascq,
+ &extd_sense_fmt);
+
+ if (extd_sense_str) {
+ if (extd_sense_fmt)
+ sdev_prefix_printk(KERN_INFO, sdev, name,
+ "Add. Sense: %s (%s%x)",
+ extd_sense_str, extd_sense_fmt,
+ ascq);
else
- printk("ASC=0x%x ASCQ=0x%x", asc, ascq);
- }
+ sdev_prefix_printk(KERN_INFO, sdev, name,
+ "Add. Sense: %s", extd_sense_str);
- printk("\n");
+ } else {
+ sdev_prefix_printk(KERN_INFO, sdev, name,
+ "%sASC=0x%x %sASCQ=0x%x\n",
+ asc >= 0x80 ? "<<vendor>> " : "", asc,
+ ascq >= 0x80 ? "<<vendor>> " : "", ascq);
+ }
}
EXPORT_SYMBOL(scsi_show_extd_sense);
void
-scsi_show_sense_hdr(struct scsi_sense_hdr *sshdr)
+scsi_show_sense_hdr(const struct scsi_device *sdev, const char *name,
+ const struct scsi_sense_hdr *sshdr)
{
const char *sense_txt;
sense_txt = scsi_sense_key_string(sshdr->sense_key);
if (sense_txt)
- printk("Sense Key : %s ", sense_txt);
+ sdev_prefix_printk(KERN_INFO, sdev, name,
+ "Sense Key : %s [%s]%s\n", sense_txt,
+ scsi_sense_is_deferred(sshdr) ?
+ "deferred" : "current",
+ sshdr->response_code >= 0x72 ?
+ " [descriptor]" : "");
else
- printk("Sense Key : 0x%x ", sshdr->sense_key);
-
- printk("%s", scsi_sense_is_deferred(sshdr) ? "[deferred] " :
- "[current] ");
-
- if (sshdr->response_code >= 0x72)
- printk("[descriptor]");
-
- printk("\n");
+ sdev_prefix_printk(KERN_INFO, sdev, name,
+ "Sense Key : 0x%x [%s]%s", sshdr->sense_key,
+ scsi_sense_is_deferred(sshdr) ?
+ "deferred" : "current",
+ sshdr->response_code >= 0x72 ?
+ " [descriptor]" : "");
}
EXPORT_SYMBOL(scsi_show_sense_hdr);
* Print normalized SCSI sense header with a prefix.
*/
void
-scsi_print_sense_hdr(const char *name, struct scsi_sense_hdr *sshdr)
+scsi_print_sense_hdr(const struct scsi_device *sdev, const char *name,
+ const struct scsi_sense_hdr *sshdr)
{
- printk(KERN_INFO "%s: ", name);
- scsi_show_sense_hdr(sshdr);
- printk(KERN_INFO "%s: ", name);
- scsi_show_extd_sense(sshdr->asc, sshdr->ascq);
+ scsi_show_sense_hdr(sdev, name, sshdr);
+ scsi_show_extd_sense(sdev, name, sshdr->asc, sshdr->ascq);
}
EXPORT_SYMBOL(scsi_print_sense_hdr);
-/*
- * Print normalized SCSI sense header with device information and a prefix.
- */
-void
-scsi_cmd_print_sense_hdr(struct scsi_cmnd *scmd, const char *desc,
- struct scsi_sense_hdr *sshdr)
-{
- scmd_printk(KERN_INFO, scmd, "%s: ", desc);
- scsi_show_sense_hdr(sshdr);
- scmd_printk(KERN_INFO, scmd, "%s: ", desc);
- scsi_show_extd_sense(sshdr->asc, sshdr->ascq);
-}
-EXPORT_SYMBOL(scsi_cmd_print_sense_hdr);
-
static void
-scsi_decode_sense_buffer(const unsigned char *sense_buffer, int sense_len,
- struct scsi_sense_hdr *sshdr)
+scsi_dump_sense_buffer(const unsigned char *sense_buffer, int sense_len)
{
- int k, num, res;
-
- res = scsi_normalize_sense(sense_buffer, sense_len, sshdr);
- if (0 == res) {
- /* this may be SCSI-1 sense data */
- num = (sense_len < 32) ? sense_len : 32;
- printk("Unrecognized sense data (in hex):");
- for (k = 0; k < num; ++k) {
- if (0 == (k % 16)) {
- printk("\n");
- printk(KERN_INFO " ");
- }
- printk("%02x ", sense_buffer[k]);
+ int k, num;
+
+ num = (sense_len < 32) ? sense_len : 32;
+ printk("Unrecognized sense data (in hex):");
+ for (k = 0; k < num; ++k) {
+ if (0 == (k % 16)) {
+ printk("\n");
+ printk(KERN_INFO " ");
}
- printk("\n");
- return;
+ printk("%02x ", sense_buffer[k]);
}
-}
-
-static void
-scsi_decode_sense_extras(const unsigned char *sense_buffer, int sense_len,
- struct scsi_sense_hdr *sshdr)
-{
- int k, num, res;
-
- if (sshdr->response_code < 0x72)
- {
- /* only decode extras for "fixed" format now */
- char buff[80];
- int blen, fixed_valid;
- unsigned int info;
-
- fixed_valid = sense_buffer[0] & 0x80;
- info = ((sense_buffer[3] << 24) | (sense_buffer[4] << 16) |
- (sense_buffer[5] << 8) | sense_buffer[6]);
- res = 0;
- memset(buff, 0, sizeof(buff));
- blen = sizeof(buff) - 1;
- if (fixed_valid)
- res += snprintf(buff + res, blen - res,
- "Info fld=0x%x", info);
- if (sense_buffer[2] & 0x80) {
- /* current command has read a filemark */
- if (res > 0)
- res += snprintf(buff + res, blen - res, ", ");
- res += snprintf(buff + res, blen - res, "FMK");
- }
- if (sense_buffer[2] & 0x40) {
- /* end-of-medium condition exists */
- if (res > 0)
- res += snprintf(buff + res, blen - res, ", ");
- res += snprintf(buff + res, blen - res, "EOM");
- }
- if (sense_buffer[2] & 0x20) {
- /* incorrect block length requested */
- if (res > 0)
- res += snprintf(buff + res, blen - res, ", ");
- res += snprintf(buff + res, blen - res, "ILI");
- }
- if (res > 0)
- printk("%s\n", buff);
- } else if (sshdr->additional_length > 0) {
- /* descriptor format with sense descriptors */
- num = 8 + sshdr->additional_length;
- num = (sense_len < num) ? sense_len : num;
- printk("Descriptor sense data with sense descriptors "
- "(in hex):");
- for (k = 0; k < num; ++k) {
- if (0 == (k % 16)) {
- printk("\n");
- printk(KERN_INFO " ");
- }
- printk("%02x ", sense_buffer[k]);
- }
-
- printk("\n");
- }
-
+ printk("\n");
+ return;
}
/* Normalize and print sense buffer with name prefix */
-void __scsi_print_sense(const char *name, const unsigned char *sense_buffer,
- int sense_len)
+void __scsi_print_sense(const struct scsi_device *sdev, const char *name,
+ const unsigned char *sense_buffer, int sense_len)
{
struct scsi_sense_hdr sshdr;
- printk(KERN_INFO "%s: ", name);
- scsi_decode_sense_buffer(sense_buffer, sense_len, &sshdr);
- scsi_show_sense_hdr(&sshdr);
- scsi_decode_sense_extras(sense_buffer, sense_len, &sshdr);
- printk(KERN_INFO "%s: ", name);
- scsi_show_extd_sense(sshdr.asc, sshdr.ascq);
+ if (!scsi_normalize_sense(sense_buffer, sense_len, &sshdr)) {
+ scsi_dump_sense_buffer(sense_buffer, sense_len);
+ return;
+ }
+ scsi_show_sense_hdr(sdev, name, &sshdr);
+ scsi_show_extd_sense(sdev, name, sshdr.asc, sshdr.ascq);
}
EXPORT_SYMBOL(__scsi_print_sense);
/* Normalize and print sense buffer in SCSI command */
-void scsi_print_sense(char *name, struct scsi_cmnd *cmd)
+void scsi_print_sense(const struct scsi_cmnd *cmd)
{
- struct scsi_sense_hdr sshdr;
+ struct gendisk *disk = cmd->request->rq_disk;
+ const char *disk_name = disk ? disk->disk_name : NULL;
- scmd_printk(KERN_INFO, cmd, " ");
- scsi_decode_sense_buffer(cmd->sense_buffer, SCSI_SENSE_BUFFERSIZE,
- &sshdr);
- scsi_show_sense_hdr(&sshdr);
- scsi_decode_sense_extras(cmd->sense_buffer, SCSI_SENSE_BUFFERSIZE,
- &sshdr);
- scmd_printk(KERN_INFO, cmd, " ");
- scsi_show_extd_sense(sshdr.asc, sshdr.ascq);
+ __scsi_print_sense(cmd->device, disk_name, cmd->sense_buffer,
+ SCSI_SENSE_BUFFERSIZE);
}
EXPORT_SYMBOL(scsi_print_sense);
"DID_PASSTHROUGH", "DID_SOFT_ERROR", "DID_IMM_RETRY", "DID_REQUEUE",
"DID_TRANSPORT_DISRUPTED", "DID_TRANSPORT_FAILFAST", "DID_TARGET_FAILURE",
"DID_NEXUS_FAILURE" };
-#define NUM_HOSTBYTE_STRS ARRAY_SIZE(hostbyte_table)
static const char * const driverbyte_table[]={
"DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT", "DRIVER_MEDIA", "DRIVER_ERROR",
"DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD", "DRIVER_SENSE"};
-#define NUM_DRIVERBYTE_STRS ARRAY_SIZE(driverbyte_table)
-void scsi_show_result(int result)
+#endif
+
+const char *scsi_hostbyte_string(int result)
{
+ const char *hb_string = NULL;
+#ifdef CONFIG_SCSI_CONSTANTS
int hb = host_byte(result);
- int db = driver_byte(result);
- printk("Result: hostbyte=%s driverbyte=%s\n",
- (hb < NUM_HOSTBYTE_STRS ? hostbyte_table[hb] : "invalid"),
- (db < NUM_DRIVERBYTE_STRS ? driverbyte_table[db] : "invalid"));
+ if (hb < ARRAY_SIZE(hostbyte_table))
+ hb_string = hostbyte_table[hb];
+#endif
+ return hb_string;
}
+EXPORT_SYMBOL(scsi_hostbyte_string);
-#else
-
-void scsi_show_result(int result)
+const char *scsi_driverbyte_string(int result)
{
- printk("Result: hostbyte=0x%02x driverbyte=0x%02x\n",
- host_byte(result), driver_byte(result));
+ const char *db_string = NULL;
+#ifdef CONFIG_SCSI_CONSTANTS
+ int db = driver_byte(result);
+
+ if (db < ARRAY_SIZE(driverbyte_table))
+ db_string = driverbyte_table[db];
+#endif
+ return db_string;
}
+EXPORT_SYMBOL(scsi_driverbyte_string);
+#ifdef CONFIG_SCSI_CONSTANTS
+#define scsi_mlreturn_name(result) { result, #result }
+static const struct value_name_pair scsi_mlreturn_arr[] = {
+ scsi_mlreturn_name(NEEDS_RETRY),
+ scsi_mlreturn_name(SUCCESS),
+ scsi_mlreturn_name(FAILED),
+ scsi_mlreturn_name(QUEUED),
+ scsi_mlreturn_name(SOFT_ERROR),
+ scsi_mlreturn_name(ADD_TO_MLQUEUE),
+ scsi_mlreturn_name(TIMEOUT_ERROR),
+ scsi_mlreturn_name(SCSI_RETURN_NOT_HANDLED),
+ scsi_mlreturn_name(FAST_IO_FAIL)
+};
#endif
-EXPORT_SYMBOL(scsi_show_result);
+const char *scsi_mlreturn_string(int result)
+{
+#ifdef CONFIG_SCSI_CONSTANTS
+ const struct value_name_pair *arr = scsi_mlreturn_arr;
+ int k;
-void scsi_print_result(struct scsi_cmnd *cmd)
+ for (k = 0; k < ARRAY_SIZE(scsi_mlreturn_arr); ++k, ++arr) {
+ if (result == arr->value)
+ return arr->name;
+ }
+#endif
+ return NULL;
+}
+EXPORT_SYMBOL(scsi_mlreturn_string);
+
+void scsi_print_result(struct scsi_cmnd *cmd, const char *msg, int disposition)
{
- scmd_printk(KERN_INFO, cmd, " ");
- scsi_show_result(cmd->result);
+ const char *mlret_string = scsi_mlreturn_string(disposition);
+ const char *hb_string = scsi_hostbyte_string(cmd->result);
+ const char *db_string = scsi_driverbyte_string(cmd->result);
+
+ if (hb_string || db_string)
+ scmd_printk(KERN_INFO, cmd,
+ "%s%s Result: hostbyte=%s driverbyte=%s",
+ msg ? msg : "",
+ mlret_string ? mlret_string : "UNKNOWN",
+ hb_string ? hb_string : "invalid",
+ db_string ? db_string : "invalid");
+ else
+ scmd_printk(KERN_INFO, cmd,
+ "%s%s Result: hostbyte=0x%02x driverbyte=0x%02x",
+ msg ? msg : "",
+ mlret_string ? mlret_string : "UNKNOWN",
+ host_byte(cmd->result), driver_byte(cmd->result));
}
EXPORT_SYMBOL(scsi_print_result);
return 0;
}
-static inline void
-csio_scsi_tag(struct scsi_cmnd *scmnd, uint8_t *tag, uint8_t hq,
- uint8_t oq, uint8_t sq)
-{
- char stag[2];
-
- if (scsi_populate_tag_msg(scmnd, stag)) {
- switch (stag[0]) {
- case HEAD_OF_QUEUE_TAG:
- *tag = hq;
- break;
- case ORDERED_QUEUE_TAG:
- *tag = oq;
- break;
- default:
- *tag = sq;
- break;
- }
- } else
- *tag = 0;
-}
-
/*
* csio_scsi_fcp_cmnd - Frame the SCSI FCP command paylod.
* @req: IO req structure.
int_to_scsilun(scmnd->device->lun, &fcp_cmnd->fc_lun);
fcp_cmnd->fc_tm_flags = 0;
fcp_cmnd->fc_cmdref = 0;
- fcp_cmnd->fc_pri_ta = 0;
memcpy(fcp_cmnd->fc_cdb, scmnd->cmnd, 16);
- csio_scsi_tag(scmnd, &fcp_cmnd->fc_pri_ta,
- FCP_PTA_HEADQ, FCP_PTA_ORDERED, FCP_PTA_SIMPLE);
+ if (scmnd->flags & SCMD_TAGGED)
+ fcp_cmnd->fc_pri_ta = FCP_PTA_SIMPLE;
+ else
+ fcp_cmnd->fc_pri_ta = 0;
fcp_cmnd->fc_dl = cpu_to_be32(scsi_bufflen(scmnd));
if (req->nsge)
static int
csio_slave_configure(struct scsi_device *sdev)
{
- if (sdev->tagged_supported)
- scsi_activate_tcq(sdev, csio_lun_qdepth);
- else
- scsi_deactivate_tcq(sdev, csio_lun_qdepth);
-
+ scsi_change_queue_depth(sdev, csio_lun_qdepth);
return 0;
}
.use_clustering = ENABLE_CLUSTERING,
.shost_attrs = csio_fcoe_lport_attrs,
.max_sectors = CSIO_MAX_SECTOR_SIZE,
+ .use_blk_tags = 1,
};
struct scsi_host_template csio_fcoe_shost_vport_template = {
.use_clustering = ENABLE_CLUSTERING,
.shost_attrs = csio_fcoe_vport_attrs,
.max_sectors = CSIO_MAX_SECTOR_SIZE,
+ .use_blk_tags = 1,
};
/*
.proc_name = DRV_MODULE_NAME,
.can_queue = CXGB3I_SCSI_HOST_QDEPTH,
.queuecommand = iscsi_queuecommand,
- .change_queue_depth = iscsi_change_queue_depth,
+ .change_queue_depth = scsi_change_queue_depth,
.sg_tablesize = SG_ALL,
.max_sectors = 0xFFFF,
.cmd_per_lun = ISCSI_DEF_CMD_PER_LUN,
.target_alloc = iscsi_target_alloc,
.use_clustering = DISABLE_CLUSTERING,
.this_id = -1,
+ .track_queue_depth = 1,
};
static struct iscsi_transport cxgb3i_iscsi_transport = {
.proc_name = DRV_MODULE_NAME,
.can_queue = CXGB4I_SCSI_HOST_QDEPTH,
.queuecommand = iscsi_queuecommand,
- .change_queue_depth = iscsi_change_queue_depth,
+ .change_queue_depth = scsi_change_queue_depth,
.sg_tablesize = SG_ALL,
.max_sectors = 0xFFFF,
.cmd_per_lun = ISCSI_DEF_CMD_PER_LUN,
.target_alloc = iscsi_target_alloc,
.use_clustering = DISABLE_CLUSTERING,
.this_id = -1,
+ .track_queue_depth = 1,
};
static struct iscsi_transport cxgb4i_iscsi_transport = {
if (status == CPL_ERR_RTX_NEG_ADVICE)
goto rel_skb;
+ module_put(THIS_MODULE);
+
if (status && status != CPL_ERR_TCAM_FULL &&
status != CPL_ERR_CONN_EXIST &&
status != CPL_ERR_ARP_MISS)
cxgbi_sock_get(csk);
spin_lock_bh(&csk->lock);
- if (!cxgbi_sock_flag(csk, CTPF_ABORT_REQ_RCVD)) {
- cxgbi_sock_set_flag(csk, CTPF_ABORT_REQ_RCVD);
- cxgbi_sock_set_state(csk, CTP_ABORTING);
- goto done;
+ cxgbi_sock_clear_flag(csk, CTPF_ABORT_REQ_RCVD);
+
+ if (!cxgbi_sock_flag(csk, CTPF_TX_DATA_SENT)) {
+ send_tx_flowc_wr(csk);
+ cxgbi_sock_set_flag(csk, CTPF_TX_DATA_SENT);
}
- cxgbi_sock_clear_flag(csk, CTPF_ABORT_REQ_RCVD);
+ cxgbi_sock_set_flag(csk, CTPF_ABORT_REQ_RCVD);
+ cxgbi_sock_set_state(csk, CTP_ABORTING);
+
send_abort_rpl(csk, rst_status);
if (!cxgbi_sock_flag(csk, CTPF_ABORT_RPL_PENDING)) {
csk->err = abort_status_to_errno(csk, req->status, &rst_status);
cxgbi_sock_closed(csk);
}
-done:
+
spin_unlock_bh(&csk->lock);
cxgbi_sock_put(csk);
rel_skb:
read_lock_bh(&csk->callback_lock);
if (csk->user_data)
iscsi_conn_failure(csk->user_data,
- ISCSI_ERR_CONN_FAILED);
+ ISCSI_ERR_TCP_CONN_CLOSE);
read_unlock_bh(&csk->callback_lock);
}
}
{
cxgbi_sock_get(csk);
spin_lock_bh(&csk->lock);
+
+ cxgbi_sock_set_flag(csk, CTPF_ABORT_RPL_RCVD);
if (cxgbi_sock_flag(csk, CTPF_ABORT_RPL_PENDING)) {
- if (!cxgbi_sock_flag(csk, CTPF_ABORT_RPL_RCVD))
- cxgbi_sock_set_flag(csk, CTPF_ABORT_RPL_RCVD);
- else {
- cxgbi_sock_clear_flag(csk, CTPF_ABORT_RPL_RCVD);
- cxgbi_sock_clear_flag(csk, CTPF_ABORT_RPL_PENDING);
- if (cxgbi_sock_flag(csk, CTPF_ABORT_REQ_RCVD))
- pr_err("csk 0x%p,%u,0x%lx,%u,ABT_RPL_RSS.\n",
- csk, csk->state, csk->flags, csk->tid);
- cxgbi_sock_closed(csk);
- }
+ cxgbi_sock_clear_flag(csk, CTPF_ABORT_RPL_PENDING);
+ if (cxgbi_sock_flag(csk, CTPF_ABORT_REQ_RCVD))
+ pr_err("csk 0x%p,%u,0x%lx,%u,ABT_RPL_RSS.\n",
+ csk, csk->state, csk->flags, csk->tid);
+ cxgbi_sock_closed(csk);
}
+
spin_unlock_bh(&csk->lock);
cxgbi_sock_put(csk);
}
static int scsi_dh_handler_attach(struct scsi_device *sdev,
struct scsi_device_handler *scsi_dh)
{
- int err = 0;
+ struct scsi_dh_data *d;
if (sdev->scsi_dh_data) {
if (sdev->scsi_dh_data->scsi_dh != scsi_dh)
- err = -EBUSY;
- else
- kref_get(&sdev->scsi_dh_data->kref);
- } else if (scsi_dh->attach) {
- err = scsi_dh->attach(sdev);
- if (!err) {
- kref_init(&sdev->scsi_dh_data->kref);
- sdev->scsi_dh_data->sdev = sdev;
- }
+ return -EBUSY;
+
+ kref_get(&sdev->scsi_dh_data->kref);
+ return 0;
}
- return err;
+
+ if (!try_module_get(scsi_dh->module))
+ return -EINVAL;
+
+ d = scsi_dh->attach(sdev);
+ if (IS_ERR(d)) {
+ sdev_printk(KERN_ERR, sdev, "%s: Attach failed (%ld)\n",
+ scsi_dh->name, PTR_ERR(d));
+ module_put(scsi_dh->module);
+ return PTR_ERR(d);
+ }
+
+ d->scsi_dh = scsi_dh;
+ kref_init(&d->kref);
+ d->sdev = sdev;
+
+ spin_lock_irq(sdev->request_queue->queue_lock);
+ sdev->scsi_dh_data = d;
+ spin_unlock_irq(sdev->request_queue->queue_lock);
+ return 0;
}
static void __detach_handler (struct kref *kref)
{
- struct scsi_dh_data *scsi_dh_data = container_of(kref, struct scsi_dh_data, kref);
- scsi_dh_data->scsi_dh->detach(scsi_dh_data->sdev);
+ struct scsi_dh_data *scsi_dh_data =
+ container_of(kref, struct scsi_dh_data, kref);
+ struct scsi_device_handler *scsi_dh = scsi_dh_data->scsi_dh;
+ struct scsi_device *sdev = scsi_dh_data->sdev;
+
+ spin_lock_irq(sdev->request_queue->queue_lock);
+ sdev->scsi_dh_data = NULL;
+ spin_unlock_irq(sdev->request_queue->queue_lock);
+
+ scsi_dh->detach(sdev);
+ sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", scsi_dh->name);
+ module_put(scsi_dh->module);
}
/*
if (!scsi_dh)
scsi_dh = sdev->scsi_dh_data->scsi_dh;
- if (scsi_dh && scsi_dh->detach)
+ if (scsi_dh)
kref_put(&sdev->scsi_dh_data->kref, __detach_handler);
}
if (get_device_handler(scsi_dh->name))
return -EBUSY;
+ if (!scsi_dh->attach || !scsi_dh->detach)
+ return -EINVAL;
+
spin_lock(&list_lock);
list_add(&scsi_dh->list, &scsi_dh_list);
spin_unlock(&list_lock);
#define ALUA_OPTIMIZE_STPG 1
struct alua_dh_data {
+ struct scsi_dh_data dh_data;
int group_id;
int rel_port;
int tpgs;
static inline struct alua_dh_data *get_alua_data(struct scsi_device *sdev)
{
- struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
- BUG_ON(scsi_dh_data == NULL);
- return ((struct alua_dh_data *) scsi_dh_data->buf);
+ return container_of(sdev->scsi_dh_data, struct alua_dh_data, dh_data);
}
static int realloc_buffer(struct alua_dh_data *h, unsigned len)
* LUN Not Ready -- Offline
*/
return SUCCESS;
+ if (sdev->allow_restart &&
+ sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x02)
+ /*
+ * if the device is not started, we need to wake
+ * the error handler to start the motor
+ */
+ return FAILED;
break;
case UNIT_ATTENTION:
if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00)
return (scsi_device_tpgs(sdev) != 0);
}
-static int alua_bus_attach(struct scsi_device *sdev);
-static void alua_bus_detach(struct scsi_device *sdev);
-
-static struct scsi_device_handler alua_dh = {
- .name = ALUA_DH_NAME,
- .module = THIS_MODULE,
- .attach = alua_bus_attach,
- .detach = alua_bus_detach,
- .prep_fn = alua_prep_fn,
- .check_sense = alua_check_sense,
- .activate = alua_activate,
- .set_params = alua_set_params,
- .match = alua_match,
-};
-
/*
* alua_bus_attach - Attach device handler
* @sdev: device to be attached to
*/
-static int alua_bus_attach(struct scsi_device *sdev)
+static struct scsi_dh_data *alua_bus_attach(struct scsi_device *sdev)
{
- struct scsi_dh_data *scsi_dh_data;
struct alua_dh_data *h;
- unsigned long flags;
- int err = SCSI_DH_OK;
-
- scsi_dh_data = kzalloc(sizeof(*scsi_dh_data)
- + sizeof(*h) , GFP_KERNEL);
- if (!scsi_dh_data) {
- sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n",
- ALUA_DH_NAME);
- return -ENOMEM;
- }
+ int err;
- scsi_dh_data->scsi_dh = &alua_dh;
- h = (struct alua_dh_data *) scsi_dh_data->buf;
+ h = kzalloc(sizeof(*h) , GFP_KERNEL);
+ if (!h)
+ return ERR_PTR(-ENOMEM);
h->tpgs = TPGS_MODE_UNINITIALIZED;
h->state = TPGS_STATE_OPTIMIZED;
h->group_id = -1;
h->sdev = sdev;
err = alua_initialize(sdev, h);
- if ((err != SCSI_DH_OK) && (err != SCSI_DH_DEV_OFFLINED))
+ if (err != SCSI_DH_OK && err != SCSI_DH_DEV_OFFLINED)
goto failed;
- if (!try_module_get(THIS_MODULE))
- goto failed;
-
- spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
- sdev->scsi_dh_data = scsi_dh_data;
- spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
sdev_printk(KERN_NOTICE, sdev, "%s: Attached\n", ALUA_DH_NAME);
-
- return 0;
-
+ return &h->dh_data;
failed:
- kfree(scsi_dh_data);
- sdev_printk(KERN_ERR, sdev, "%s: not attached\n", ALUA_DH_NAME);
- return -EINVAL;
+ kfree(h);
+ return ERR_PTR(-EINVAL);
}
/*
*/
static void alua_bus_detach(struct scsi_device *sdev)
{
- struct scsi_dh_data *scsi_dh_data;
- struct alua_dh_data *h;
- unsigned long flags;
-
- spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
- scsi_dh_data = sdev->scsi_dh_data;
- sdev->scsi_dh_data = NULL;
- spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+ struct alua_dh_data *h = get_alua_data(sdev);
- h = (struct alua_dh_data *) scsi_dh_data->buf;
if (h->buff && h->inq != h->buff)
kfree(h->buff);
- kfree(scsi_dh_data);
- module_put(THIS_MODULE);
- sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", ALUA_DH_NAME);
+ kfree(h);
}
+static struct scsi_device_handler alua_dh = {
+ .name = ALUA_DH_NAME,
+ .module = THIS_MODULE,
+ .attach = alua_bus_attach,
+ .detach = alua_bus_detach,
+ .prep_fn = alua_prep_fn,
+ .check_sense = alua_check_sense,
+ .activate = alua_activate,
+ .set_params = alua_set_params,
+ .match = alua_match,
+};
+
static int __init alua_init(void)
{
int r;
};
struct clariion_dh_data {
+ struct scsi_dh_data dh_data;
/*
* Flags:
* CLARIION_SHORT_TRESPASS
static inline struct clariion_dh_data
*get_clariion_data(struct scsi_device *sdev)
{
- struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
- BUG_ON(scsi_dh_data == NULL);
- return ((struct clariion_dh_data *) scsi_dh_data->buf);
+ return container_of(sdev->scsi_dh_data, struct clariion_dh_data,
+ dh_data);
}
/*
return result;
}
-static const struct scsi_dh_devlist clariion_dev_list[] = {
+static const struct {
+ char *vendor;
+ char *model;
+} clariion_dev_list[] = {
{"DGC", "RAID"},
{"DGC", "DISK"},
{"DGC", "VRAID"},
return false;
}
-static int clariion_bus_attach(struct scsi_device *sdev);
-static void clariion_bus_detach(struct scsi_device *sdev);
-
-static struct scsi_device_handler clariion_dh = {
- .name = CLARIION_NAME,
- .module = THIS_MODULE,
- .devlist = clariion_dev_list,
- .attach = clariion_bus_attach,
- .detach = clariion_bus_detach,
- .check_sense = clariion_check_sense,
- .activate = clariion_activate,
- .prep_fn = clariion_prep_fn,
- .set_params = clariion_set_params,
- .match = clariion_match,
-};
-
-static int clariion_bus_attach(struct scsi_device *sdev)
+static struct scsi_dh_data *clariion_bus_attach(struct scsi_device *sdev)
{
- struct scsi_dh_data *scsi_dh_data;
struct clariion_dh_data *h;
- unsigned long flags;
int err;
- scsi_dh_data = kzalloc(sizeof(*scsi_dh_data)
- + sizeof(*h) , GFP_KERNEL);
- if (!scsi_dh_data) {
- sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n",
- CLARIION_NAME);
- return -ENOMEM;
- }
-
- scsi_dh_data->scsi_dh = &clariion_dh;
- h = (struct clariion_dh_data *) scsi_dh_data->buf;
+ h = kzalloc(sizeof(*h) , GFP_KERNEL);
+ if (!h)
+ return ERR_PTR(-ENOMEM);
h->lun_state = CLARIION_LUN_UNINITIALIZED;
h->default_sp = CLARIION_UNBOUND_LU;
h->current_sp = CLARIION_UNBOUND_LU;
if (err != SCSI_DH_OK)
goto failed;
- if (!try_module_get(THIS_MODULE))
- goto failed;
-
- spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
- sdev->scsi_dh_data = scsi_dh_data;
- spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
-
sdev_printk(KERN_INFO, sdev,
"%s: connected to SP %c Port %d (%s, default SP %c)\n",
CLARIION_NAME, h->current_sp + 'A',
h->port, lun_state[h->lun_state],
h->default_sp + 'A');
-
- return 0;
+ return &h->dh_data;
failed:
- kfree(scsi_dh_data);
- sdev_printk(KERN_ERR, sdev, "%s: not attached\n",
- CLARIION_NAME);
- return -EINVAL;
+ kfree(h);
+ return ERR_PTR(-EINVAL);
}
static void clariion_bus_detach(struct scsi_device *sdev)
{
- struct scsi_dh_data *scsi_dh_data;
- unsigned long flags;
-
- spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
- scsi_dh_data = sdev->scsi_dh_data;
- sdev->scsi_dh_data = NULL;
- spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
-
- sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n",
- CLARIION_NAME);
+ struct clariion_dh_data *h = get_clariion_data(sdev);
- kfree(scsi_dh_data);
- module_put(THIS_MODULE);
+ kfree(h);
}
+static struct scsi_device_handler clariion_dh = {
+ .name = CLARIION_NAME,
+ .module = THIS_MODULE,
+ .attach = clariion_bus_attach,
+ .detach = clariion_bus_detach,
+ .check_sense = clariion_check_sense,
+ .activate = clariion_activate,
+ .prep_fn = clariion_prep_fn,
+ .set_params = clariion_set_params,
+ .match = clariion_match,
+};
+
static int __init clariion_init(void)
{
int r;
#define HP_SW_PATH_PASSIVE 1
struct hp_sw_dh_data {
+ struct scsi_dh_data dh_data;
unsigned char sense[SCSI_SENSE_BUFFERSIZE];
int path_state;
int retries;
static inline struct hp_sw_dh_data *get_hp_sw_data(struct scsi_device *sdev)
{
- struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
- BUG_ON(scsi_dh_data == NULL);
- return ((struct hp_sw_dh_data *) scsi_dh_data->buf);
+ return container_of(sdev->scsi_dh_data, struct hp_sw_dh_data, dh_data);
}
/*
return 0;
}
-static const struct scsi_dh_devlist hp_sw_dh_data_list[] = {
+static const struct {
+ char *vendor;
+ char *model;
+} hp_sw_dh_data_list[] = {
{"COMPAQ", "MSA1000 VOLUME"},
{"COMPAQ", "HSV110"},
{"HP", "HSV100"},
return false;
}
-static int hp_sw_bus_attach(struct scsi_device *sdev);
-static void hp_sw_bus_detach(struct scsi_device *sdev);
-
-static struct scsi_device_handler hp_sw_dh = {
- .name = HP_SW_NAME,
- .module = THIS_MODULE,
- .devlist = hp_sw_dh_data_list,
- .attach = hp_sw_bus_attach,
- .detach = hp_sw_bus_detach,
- .activate = hp_sw_activate,
- .prep_fn = hp_sw_prep_fn,
- .match = hp_sw_match,
-};
-
-static int hp_sw_bus_attach(struct scsi_device *sdev)
+static struct scsi_dh_data *hp_sw_bus_attach(struct scsi_device *sdev)
{
- struct scsi_dh_data *scsi_dh_data;
struct hp_sw_dh_data *h;
- unsigned long flags;
int ret;
- scsi_dh_data = kzalloc(sizeof(*scsi_dh_data)
- + sizeof(*h) , GFP_KERNEL);
- if (!scsi_dh_data) {
- sdev_printk(KERN_ERR, sdev, "%s: Attach Failed\n",
- HP_SW_NAME);
- return 0;
- }
-
- scsi_dh_data->scsi_dh = &hp_sw_dh;
- h = (struct hp_sw_dh_data *) scsi_dh_data->buf;
+ h = kzalloc(sizeof(*h), GFP_KERNEL);
+ if (!h)
+ return ERR_PTR(-ENOMEM);
h->path_state = HP_SW_PATH_UNINITIALIZED;
h->retries = HP_SW_RETRIES;
h->sdev = sdev;
if (ret != SCSI_DH_OK || h->path_state == HP_SW_PATH_UNINITIALIZED)
goto failed;
- if (!try_module_get(THIS_MODULE))
- goto failed;
-
- spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
- sdev->scsi_dh_data = scsi_dh_data;
- spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
-
sdev_printk(KERN_INFO, sdev, "%s: attached to %s path\n",
HP_SW_NAME, h->path_state == HP_SW_PATH_ACTIVE?
"active":"passive");
-
- return 0;
-
+ return &h->dh_data;
failed:
- kfree(scsi_dh_data);
- sdev_printk(KERN_ERR, sdev, "%s: not attached\n",
- HP_SW_NAME);
- return -EINVAL;
+ kfree(h);
+ return ERR_PTR(-EINVAL);
}
static void hp_sw_bus_detach( struct scsi_device *sdev )
{
- struct scsi_dh_data *scsi_dh_data;
- unsigned long flags;
-
- spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
- scsi_dh_data = sdev->scsi_dh_data;
- sdev->scsi_dh_data = NULL;
- spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
- module_put(THIS_MODULE);
-
- sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", HP_SW_NAME);
+ struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
- kfree(scsi_dh_data);
+ kfree(h);
}
+static struct scsi_device_handler hp_sw_dh = {
+ .name = HP_SW_NAME,
+ .module = THIS_MODULE,
+ .attach = hp_sw_bus_attach,
+ .detach = hp_sw_bus_detach,
+ .activate = hp_sw_activate,
+ .prep_fn = hp_sw_prep_fn,
+ .match = hp_sw_match,
+};
+
static int __init hp_sw_init(void)
{
return scsi_register_device_handler(&hp_sw_dh);
};
struct rdac_dh_data {
+ struct scsi_dh_data dh_data;
struct rdac_controller *ctlr;
#define UNINITIALIZED_LUN (1 << 8)
unsigned lun;
static inline struct rdac_dh_data *get_rdac_data(struct scsi_device *sdev)
{
- struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
- BUG_ON(scsi_dh_data == NULL);
- return ((struct rdac_dh_data *) scsi_dh_data->buf);
+ return container_of(sdev->scsi_dh_data, struct rdac_dh_data, dh_data);
}
static struct request *get_rdac_req(struct scsi_device *sdev,
return SCSI_RETURN_NOT_HANDLED;
}
-static const struct scsi_dh_devlist rdac_dev_list[] = {
+static const struct {
+ char *vendor;
+ char *model;
+} rdac_dev_list[] = {
{"IBM", "1722"},
{"IBM", "1724"},
{"IBM", "1726"},
return false;
}
-static int rdac_bus_attach(struct scsi_device *sdev);
-static void rdac_bus_detach(struct scsi_device *sdev);
-
-static struct scsi_device_handler rdac_dh = {
- .name = RDAC_NAME,
- .module = THIS_MODULE,
- .devlist = rdac_dev_list,
- .prep_fn = rdac_prep_fn,
- .check_sense = rdac_check_sense,
- .attach = rdac_bus_attach,
- .detach = rdac_bus_detach,
- .activate = rdac_activate,
- .match = rdac_match,
-};
-
-static int rdac_bus_attach(struct scsi_device *sdev)
+static struct scsi_dh_data *rdac_bus_attach(struct scsi_device *sdev)
{
- struct scsi_dh_data *scsi_dh_data;
struct rdac_dh_data *h;
- unsigned long flags;
int err;
char array_name[ARRAY_LABEL_LEN];
char array_id[UNIQUE_ID_LEN];
- scsi_dh_data = kzalloc(sizeof(*scsi_dh_data)
- + sizeof(*h) , GFP_KERNEL);
- if (!scsi_dh_data) {
- sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n",
- RDAC_NAME);
- return -ENOMEM;
- }
-
- scsi_dh_data->scsi_dh = &rdac_dh;
- h = (struct rdac_dh_data *) scsi_dh_data->buf;
+ h = kzalloc(sizeof(*h) , GFP_KERNEL);
+ if (!h)
+ return ERR_PTR(-ENOMEM);
h->lun = UNINITIALIZED_LUN;
h->state = RDAC_STATE_ACTIVE;
if (err != SCSI_DH_OK)
goto clean_ctlr;
- if (!try_module_get(THIS_MODULE))
- goto clean_ctlr;
-
- spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
- sdev->scsi_dh_data = scsi_dh_data;
- spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
-
sdev_printk(KERN_NOTICE, sdev,
"%s: LUN %d (%s) (%s)\n",
RDAC_NAME, h->lun, mode[(int)h->mode],
lun_state[(int)h->lun_state]);
- return 0;
+ return &h->dh_data;
clean_ctlr:
spin_lock(&list_lock);
spin_unlock(&list_lock);
failed:
- kfree(scsi_dh_data);
- sdev_printk(KERN_ERR, sdev, "%s: not attached\n",
- RDAC_NAME);
- return -EINVAL;
+ kfree(h);
+ return ERR_PTR(-EINVAL);
}
static void rdac_bus_detach( struct scsi_device *sdev )
{
- struct scsi_dh_data *scsi_dh_data;
- struct rdac_dh_data *h;
- unsigned long flags;
+ struct rdac_dh_data *h = get_rdac_data(sdev);
- scsi_dh_data = sdev->scsi_dh_data;
- h = (struct rdac_dh_data *) scsi_dh_data->buf;
if (h->ctlr && h->ctlr->ms_queued)
flush_workqueue(kmpath_rdacd);
- spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
- sdev->scsi_dh_data = NULL;
- spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
-
spin_lock(&list_lock);
if (h->ctlr)
kref_put(&h->ctlr->kref, release_controller);
spin_unlock(&list_lock);
- kfree(scsi_dh_data);
- module_put(THIS_MODULE);
- sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", RDAC_NAME);
+ kfree(h);
}
-
+static struct scsi_device_handler rdac_dh = {
+ .name = RDAC_NAME,
+ .module = THIS_MODULE,
+ .prep_fn = rdac_prep_fn,
+ .check_sense = rdac_check_sense,
+ .attach = rdac_bus_attach,
+ .detach = rdac_bus_detach,
+ .activate = rdac_activate,
+ .match = rdac_match,
+};
static int __init rdac_init(void)
{
/*
* Definitions for the generic 5380 driver.
*/
-#define AUTOSENSE
+
+#define DONT_USE_INTR
#define NCR5380_read(reg) inb(port + reg)
#define NCR5380_write(reg, value) outb(value, port + reg)
-#define NCR5380_implementation_fields unsigned int port
-#define NCR5380_local_declare() NCR5380_implementation_fields
+#define NCR5380_implementation_fields /* none */
+#define NCR5380_local_declare() unsigned int port
#define NCR5380_setup(instance) port = instance->io_port
/*
* Includes needed for NCR5380.[ch] (XXX: Move them to NCR5380.h)
*/
#include <linux/delay.h>
-#include "scsi.h"
#include "NCR5380.h"
#include "NCR5380.c"
static struct scsi_host_template dmx3191d_driver_template = {
.proc_name = DMX3191D_DRIVER_NAME,
.name = "Domex DMX3191D",
+ .info = NCR5380_info,
.queuecommand = NCR5380_queue_command,
.eh_abort_handler = NCR5380_abort,
.eh_bus_reset_handler = NCR5380_bus_reset,
if (!shost)
goto out_release_region;
shost->io_port = io;
- shost->irq = pdev->irq;
- NCR5380_init(shost, FLAG_NO_PSEUDO_DMA | FLAG_DTC3181E);
+ /* This card does not seem to raise an interrupt on pdev->irq.
+ * Steam-powered SCSI controllers run without an IRQ anyway.
+ */
+ shost->irq = NO_IRQ;
- if (request_irq(pdev->irq, NCR5380_intr, IRQF_SHARED,
- DMX3191D_DRIVER_NAME, shost)) {
- /*
- * Steam powered scsi controllers run without an IRQ anyway
- */
- printk(KERN_WARNING "dmx3191: IRQ %d not available - "
- "switching to polled mode.\n", pdev->irq);
- shost->irq = SCSI_IRQ_NONE;
- }
+ NCR5380_init(shost, FLAG_NO_PSEUDO_DMA | FLAG_DTC3181E);
pci_set_drvdata(pdev, shost);
error = scsi_add_host(shost, &pdev->dev);
if (error)
- goto out_free_irq;
+ goto out_release_region;
scsi_scan_host(shost);
return 0;
- out_free_irq:
- free_irq(shost->irq, shost);
out_release_region:
release_region(io, DMX3191D_REGION_LEN);
out_disable_device:
NCR5380_exit(shost);
- if (shost->irq != SCSI_IRQ_NONE)
- free_irq(shost->irq, shost);
release_region(shost->io_port, DMX3191D_REGION_LEN);
pci_disable_device(pdev);
pHba = (adpt_hba *) host->hostdata[0];
if (host->can_queue && device->tagged_supported) {
- scsi_adjust_queue_depth(device, MSG_SIMPLE_TAG,
+ scsi_change_queue_depth(device,
host->can_queue - 1);
- } else {
- scsi_adjust_queue_depth(device, 0, 1);
}
return 0;
}
-#define AUTOSENSE
#define PSEUDO_DMA
#define DONT_USE_INTR
#define UNSAFE /* Leave interrupts enabled during pseudo-dma I/O */
* (Unix and Linux consulting and custom programming)
* drew@colorado.edu
* +1 (303) 440-4894
- *
- * DISTRIBUTION RELEASE 1.
- *
- * For more information, please consult
- *
- * NCR 5380 Family
- * SCSI Protocol Controller
- * Databook
-*/
+ */
/*
- * Options :
- * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
- * for commands that return with a CHECK CONDITION status.
- *
- * PSEUDO_DMA - enables PSEUDO-DMA hardware, should give a 3-4X performance
- * increase compared to polled I/O.
- *
- * PARITY - enable parity checking. Not supported.
- *
- * UNSAFE - leave interrupts enabled during pseudo-DMA transfers.
- * You probably want this.
- *
* The card is detected and initialized in one of several ways :
* 1. Autoprobe (default) - since the board is memory mapped,
* a BIOS signature is scanned for to locate the registers.
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
-#include "scsi.h"
#include <scsi/scsi_host.h>
#include "dtc.h"
#define AUTOPROBE_IRQ
#include "NCR5380.h"
-
-#define DTC_PUBLIC_RELEASE 2
-
/*
* The DTC3180 & 3280 boards are memory mapped.
*
*
*/
-static void __init dtc_setup(char *str, int *ints)
+static int __init dtc_setup(char *str)
{
static int commandline_current = 0;
int i;
+ int ints[10];
+
+ get_options(str, ARRAY_SIZE(ints), ints);
if (ints[0] != 2)
printk("dtc_setup: usage dtc=address,irq\n");
else if (commandline_current < NO_OVERRIDES) {
}
++commandline_current;
}
+ return 1;
}
+
+__setup("dtc=", dtc_setup);
#endif
/*
void __iomem *base;
int sig, count;
- tpnt->proc_name = "dtc3x80";
- tpnt->show_info = dtc_show_info;
- tpnt->write_info = dtc_write_info;
-
for (count = 0; current_override < NO_OVERRIDES; ++current_override) {
addr = 0;
base = NULL;
else
instance->irq = NCR5380_probe_irq(instance, DTC_IRQS);
+ /* Compatibility with documented NCR5380 kernel parameters */
+ if (instance->irq == 255)
+ instance->irq = NO_IRQ;
+
#ifndef DONT_USE_INTR
/* With interrupts enabled, it will sometimes hang when doing heavy
* reads. So better not enable them until I finger it out. */
- if (instance->irq != SCSI_IRQ_NONE)
+ if (instance->irq != NO_IRQ)
if (request_irq(instance->irq, dtc_intr, 0,
"dtc", instance)) {
printk(KERN_ERR "scsi%d : IRQ%d not free, interrupts disabled\n", instance->host_no, instance->irq);
- instance->irq = SCSI_IRQ_NONE;
+ instance->irq = NO_IRQ;
}
- if (instance->irq == SCSI_IRQ_NONE) {
+ if (instance->irq == NO_IRQ) {
printk(KERN_WARNING "scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no);
printk(KERN_WARNING "scsi%d : please jumper the board for a free IRQ.\n", instance->host_no);
}
#else
- if (instance->irq != SCSI_IRQ_NONE)
+ if (instance->irq != NO_IRQ)
printk(KERN_WARNING "scsi%d : interrupts not used. Might as well not jumper it.\n", instance->host_no);
- instance->irq = SCSI_IRQ_NONE;
+ instance->irq = NO_IRQ;
#endif
#if defined(DTCDEBUG) && (DTCDEBUG & DTCDEBUG_INIT)
printk("scsi%d : irq = %d\n", instance->host_no, instance->irq);
#endif
- printk(KERN_INFO "scsi%d : at 0x%05X", instance->host_no, (int) instance->base);
- if (instance->irq == SCSI_IRQ_NONE)
- printk(" interrupts disabled");
- else
- printk(" irq %d", instance->irq);
- printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", CAN_QUEUE, CMD_PER_LUN, DTC_PUBLIC_RELEASE);
- NCR5380_print_options(instance);
- printk("\n");
-
++current_override;
++count;
}
* timeout.
*/
-static int dtc_maxi = 0;
-static int dtc_wmaxi = 0;
-
static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *dst, int len)
{
unsigned char *d = dst;
int i; /* For counting time spent in the poll-loop */
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
NCR5380_local_declare();
NCR5380_setup(instance);
i = 0;
NCR5380_read(RESET_PARITY_INTERRUPT_REG);
NCR5380_write(MODE_REG, MR_ENABLE_EOP_INTR | MR_DMA_MODE);
- if (instance->irq == SCSI_IRQ_NONE)
+ if (instance->irq == NO_IRQ)
NCR5380_write(DTC_CONTROL_REG, CSR_DIR_READ);
else
NCR5380_write(DTC_CONTROL_REG, CSR_DIR_READ | CSR_INT_BASE);
NCR5380_write(MODE_REG, 0); /* Clear the operating mode */
rtrc(0);
NCR5380_read(RESET_PARITY_INTERRUPT_REG);
- if (i > dtc_maxi)
- dtc_maxi = i;
+ if (i > hostdata->spin_max_r)
+ hostdata->spin_max_r = i;
return (0);
}
static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *src, int len)
{
int i;
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
NCR5380_local_declare();
NCR5380_setup(instance);
NCR5380_read(RESET_PARITY_INTERRUPT_REG);
NCR5380_write(MODE_REG, MR_ENABLE_EOP_INTR | MR_DMA_MODE);
/* set direction (write) */
- if (instance->irq == SCSI_IRQ_NONE)
+ if (instance->irq == NO_IRQ)
NCR5380_write(DTC_CONTROL_REG, 0);
else
NCR5380_write(DTC_CONTROL_REG, CSR_5380_INTR);
/* Check for parity error here. fixme. */
NCR5380_write(MODE_REG, 0); /* Clear the operating mode */
rtrc(0);
- if (i > dtc_wmaxi)
- dtc_wmaxi = i;
+ if (i > hostdata->spin_max_w)
+ hostdata->spin_max_w = i;
return (0);
}
{
NCR5380_local_declare();
NCR5380_setup(shost);
- if (shost->irq)
+ if (shost->irq != NO_IRQ)
free_irq(shost->irq, shost);
NCR5380_exit(shost);
if (shost->io_port && shost->n_io_port)
.name = "DTC 3180/3280 ",
.detect = dtc_detect,
.release = dtc_release,
+ .proc_name = "dtc3x80",
+ .show_info = dtc_show_info,
+ .write_info = dtc_write_info,
+ .info = dtc_info,
.queuecommand = dtc_queue_command,
.eh_abort_handler = dtc_abort,
.eh_bus_reset_handler = dtc_bus_reset,
* (Unix and Linux consulting and custom programming)
* drew@colorado.edu
* +1 (303) 440-4894
- *
- * DISTRIBUTION RELEASE 2.
- *
- * For more information, please consult
- *
- *
- *
- * and
- *
- * NCR 5380 Family
- * SCSI Protocol Controller
- * Databook
- *
- * NCR Microelectronics
- * 1635 Aeroplaza Drive
- * Colorado Springs, CO 80916
- * 1+ (719) 578-3400
- * 1+ (800) 334-5454
*/
#ifndef DTC3280_H
#define DTCDEBUG_INIT 0x1
#define DTCDEBUG_TRANSFER 0x2
-static int dtc_abort(Scsi_Cmnd *);
-static int dtc_biosparam(struct scsi_device *, struct block_device *,
- sector_t, int*);
-static int dtc_detect(struct scsi_host_template *);
-static int dtc_queue_command(struct Scsi_Host *, struct scsi_cmnd *);
-static int dtc_bus_reset(Scsi_Cmnd *);
-
#ifndef CMD_PER_LUN
#define CMD_PER_LUN 2
#endif
#define NCR5380_queue_command dtc_queue_command
#define NCR5380_abort dtc_abort
#define NCR5380_bus_reset dtc_bus_reset
+#define NCR5380_info dtc_info
#define NCR5380_show_info dtc_show_info
#define NCR5380_write_info dtc_write_info
if (TLDEV(dev->type) && dev->tagged_supported) {
if (tag_mode == TAG_SIMPLE) {
- scsi_adjust_queue_depth(dev, MSG_SIMPLE_TAG, tqd);
tag_suffix = ", simple tags";
} else if (tag_mode == TAG_ORDERED) {
- scsi_adjust_queue_depth(dev, MSG_ORDERED_TAG, tqd);
tag_suffix = ", ordered tags";
} else {
- scsi_adjust_queue_depth(dev, 0, tqd);
tag_suffix = ", no tags";
}
+ scsi_change_queue_depth(dev, tqd);
} else if (TLDEV(dev->type) && linked_comm) {
- scsi_adjust_queue_depth(dev, 0, tqd);
+ scsi_change_queue_depth(dev, tqd);
tag_suffix = ", untagged";
} else {
- scsi_adjust_queue_depth(dev, 0, utqd);
+ scsi_change_queue_depth(dev, utqd);
tag_suffix = "";
}
struct atto_ioctl *ioctl_hba);
int esas2r_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd);
int esas2r_show_info(struct seq_file *m, struct Scsi_Host *sh);
-int esas2r_slave_alloc(struct scsi_device *dev);
-int esas2r_slave_configure(struct scsi_device *dev);
-void esas2r_slave_destroy(struct scsi_device *dev);
-int esas2r_change_queue_depth(struct scsi_device *dev, int depth, int reason);
-int esas2r_change_queue_type(struct scsi_device *dev, int type);
long esas2r_proc_ioctl(struct file *fp, unsigned int cmd, unsigned long arg);
/* SCSI error handler (eh) functions */
rq = esas2r_alloc_request(a);
if (rq == NULL) {
- up(&a->fm_api_semaphore);
fi->status = FI_STAT_BUSY;
- return;
+ goto free_sem;
}
if (fi == &a->firmware.header) {
if (a->firmware.header_buff == NULL) {
esas2r_debug("failed to allocate header buffer!");
fi->status = FI_STAT_BUSY;
- return;
+ goto free_req;
}
memcpy(a->firmware.header_buff, fi,
a->firmware.header_buff,
(dma_addr_t)a->firmware.header_buff_phys);
}
-
- up(&a->fm_api_semaphore);
+free_req:
esas2r_free_request(a, (struct esas2r_request *)rq);
+free_sem:
+ up(&a->fm_api_semaphore);
return;
}
rq = esas2r_alloc_request(a);
if (rq == NULL) {
- up(&a->nvram_semaphore);
- ioctl->data.prw.code = 0;
- break;
+ kfree(ioctl);
+ esas2r_log(ESAS2R_LOG_WARN,
+ "could not allocate an internal request");
+ return -ENOMEM;
}
code = esas2r_write_params(a, rq,
case -EINVAL:
ioctl->header.return_code = IOCTL_INVALID_PARAM;
break;
+
+ default:
+ ioctl->header.return_code = IOCTL_GENERAL_ERROR;
+ break;
}
- ioctl->header.return_code = IOCTL_GENERAL_ERROR;
}
/* Always copy the buffer back, if only to pick up the status */
.use_clustering = ENABLE_CLUSTERING,
.emulated = 0,
.proc_name = ESAS2R_DRVR_NAME,
- .slave_configure = esas2r_slave_configure,
- .slave_alloc = esas2r_slave_alloc,
- .slave_destroy = esas2r_slave_destroy,
- .change_queue_depth = esas2r_change_queue_depth,
- .change_queue_type = esas2r_change_queue_type,
+ .change_queue_depth = scsi_change_queue_depth,
+ .change_queue_type = scsi_change_queue_type,
.max_sectors = 0xFFFF,
+ .use_blk_tags = 1,
};
int sgl_page_size = 512;
cmd->scsi_done(cmd);
- return 0;
+ return SUCCESS;
}
spin_lock_irqsave(&a->queue_lock, flags);
return esas2r_dev_targ_reset(cmd, true);
}
-int esas2r_change_queue_depth(struct scsi_device *dev, int depth, int reason)
-{
- esas2r_log(ESAS2R_LOG_INFO, "change_queue_depth %p, %d", dev, depth);
-
- scsi_adjust_queue_depth(dev, scsi_get_tag_type(dev), depth);
-
- return dev->queue_depth;
-}
-
-int esas2r_change_queue_type(struct scsi_device *dev, int type)
-{
- esas2r_log(ESAS2R_LOG_INFO, "change_queue_type %p, %d", dev, type);
-
- if (dev->tagged_supported) {
- scsi_set_tag_type(dev, type);
-
- if (type)
- scsi_activate_tcq(dev, dev->queue_depth);
- else
- scsi_deactivate_tcq(dev, dev->queue_depth);
- } else {
- type = 0;
- }
-
- return type;
-}
-
-int esas2r_slave_alloc(struct scsi_device *dev)
-{
- return 0;
-}
-
-int esas2r_slave_configure(struct scsi_device *dev)
-{
- esas2r_log_dev(ESAS2R_LOG_INFO, &(dev->sdev_gendev),
- "esas2r_slave_configure()");
-
- if (dev->tagged_supported) {
- scsi_set_tag_type(dev, MSG_SIMPLE_TAG);
- scsi_activate_tcq(dev, cmd_per_lun);
- } else {
- scsi_set_tag_type(dev, 0);
- scsi_deactivate_tcq(dev, cmd_per_lun);
- }
-
- return 0;
-}
-
-void esas2r_slave_destroy(struct scsi_device *dev)
-{
- esas2r_log_dev(ESAS2R_LOG_INFO, &(dev->sdev_gendev),
- "esas2r_slave_destroy()");
-}
-
void esas2r_log_request_failure(struct esas2r_adapter *a,
struct esas2r_request *rq)
{
#define ESP_DEBUG_DATADONE 0x00000100
#define ESP_DEBUG_RECONNECT 0x00000200
#define ESP_DEBUG_AUTOSENSE 0x00000400
+#define ESP_DEBUG_EVENT 0x00000800
+#define ESP_DEBUG_COMMAND 0x00001000
#define esp_log_intr(f, a...) \
do { if (esp_debug & ESP_DEBUG_INTR) \
- printk(f, ## a); \
+ shost_printk(KERN_DEBUG, esp->host, f, ## a); \
} while (0)
#define esp_log_reset(f, a...) \
do { if (esp_debug & ESP_DEBUG_RESET) \
- printk(f, ## a); \
+ shost_printk(KERN_DEBUG, esp->host, f, ## a); \
} while (0)
#define esp_log_msgin(f, a...) \
do { if (esp_debug & ESP_DEBUG_MSGIN) \
- printk(f, ## a); \
+ shost_printk(KERN_DEBUG, esp->host, f, ## a); \
} while (0)
#define esp_log_msgout(f, a...) \
do { if (esp_debug & ESP_DEBUG_MSGOUT) \
- printk(f, ## a); \
+ shost_printk(KERN_DEBUG, esp->host, f, ## a); \
} while (0)
#define esp_log_cmddone(f, a...) \
do { if (esp_debug & ESP_DEBUG_CMDDONE) \
- printk(f, ## a); \
+ shost_printk(KERN_DEBUG, esp->host, f, ## a); \
} while (0)
#define esp_log_disconnect(f, a...) \
do { if (esp_debug & ESP_DEBUG_DISCONNECT) \
- printk(f, ## a); \
+ shost_printk(KERN_DEBUG, esp->host, f, ## a); \
} while (0)
#define esp_log_datastart(f, a...) \
do { if (esp_debug & ESP_DEBUG_DATASTART) \
- printk(f, ## a); \
+ shost_printk(KERN_DEBUG, esp->host, f, ## a); \
} while (0)
#define esp_log_datadone(f, a...) \
do { if (esp_debug & ESP_DEBUG_DATADONE) \
- printk(f, ## a); \
+ shost_printk(KERN_DEBUG, esp->host, f, ## a); \
} while (0)
#define esp_log_reconnect(f, a...) \
do { if (esp_debug & ESP_DEBUG_RECONNECT) \
- printk(f, ## a); \
+ shost_printk(KERN_DEBUG, esp->host, f, ## a); \
} while (0)
#define esp_log_autosense(f, a...) \
do { if (esp_debug & ESP_DEBUG_AUTOSENSE) \
- printk(f, ## a); \
+ shost_printk(KERN_DEBUG, esp->host, f, ## a); \
+} while (0)
+
+#define esp_log_event(f, a...) \
+do { if (esp_debug & ESP_DEBUG_EVENT) \
+ shost_printk(KERN_DEBUG, esp->host, f, ## a); \
+} while (0)
+
+#define esp_log_command(f, a...) \
+do { if (esp_debug & ESP_DEBUG_COMMAND) \
+ shost_printk(KERN_DEBUG, esp->host, f, ## a); \
} while (0)
#define esp_read8(REG) esp->ops->esp_read8(esp, REG)
esp->esp_event_cur = (idx + 1) & (ESP_EVENT_LOG_SZ - 1);
+ esp_log_command("cmd[%02x]\n", val);
esp_write8(val, ESP_CMD);
}
EXPORT_SYMBOL(scsi_esp_cmd);
+static void esp_send_dma_cmd(struct esp *esp, int len, int max_len, int cmd)
+{
+ if (esp->flags & ESP_FLAG_USE_FIFO) {
+ int i;
+
+ scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+ for (i = 0; i < len; i++)
+ esp_write8(esp->command_block[i], ESP_FDATA);
+ scsi_esp_cmd(esp, cmd);
+ } else {
+ if (esp->rev == FASHME)
+ scsi_esp_cmd(esp, ESP_CMD_FLUSH);
+ cmd |= ESP_CMD_DMA;
+ esp->ops->send_dma_cmd(esp, esp->command_block_dma,
+ len, max_len, 0, cmd);
+ }
+}
+
static void esp_event(struct esp *esp, u8 val)
{
struct esp_event_ent *p;
int idx = esp->esp_event_cur;
int stop = idx;
- printk(KERN_INFO PFX "esp%d: Dumping command log\n",
- esp->host->unique_id);
+ shost_printk(KERN_INFO, esp->host, "Dumping command log\n");
do {
struct esp_event_ent *p = &esp->esp_event_log[idx];
- printk(KERN_INFO PFX "esp%d: ent[%d] %s ",
- esp->host->unique_id, idx,
- p->type == ESP_EVENT_TYPE_CMD ? "CMD" : "EVENT");
-
- printk("val[%02x] sreg[%02x] seqreg[%02x] "
- "sreg2[%02x] ireg[%02x] ss[%02x] event[%02x]\n",
- p->val, p->sreg, p->seqreg,
- p->sreg2, p->ireg, p->select_state, p->event);
+ shost_printk(KERN_INFO, esp->host,
+ "ent[%d] %s val[%02x] sreg[%02x] seqreg[%02x] "
+ "sreg2[%02x] ireg[%02x] ss[%02x] event[%02x]\n",
+ idx,
+ p->type == ESP_EVENT_TYPE_CMD ? "CMD" : "EVENT",
+ p->val, p->sreg, p->seqreg,
+ p->sreg2, p->ireg, p->select_state, p->event);
idx = (idx + 1) & (ESP_EVENT_LOG_SZ - 1);
} while (idx != stop);
while (esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES) {
if (--lim == 0) {
- printk(KERN_ALERT PFX "esp%d: ESP_FF_BYTES "
- "will not clear!\n",
- esp->host->unique_id);
+ shost_printk(KERN_ALERT, esp->host,
+ "ESP_FF_BYTES will not clear!\n");
break;
}
udelay(1);
} else {
esp->min_period = ((5 * esp->ccycle) / 1000);
}
+ if (esp->rev == FAS236) {
+ /*
+ * The AM53c974 chip returns the same ID as FAS236;
+ * try to configure glitch eater.
+ */
+ u8 config4 = ESP_CONFIG4_GE1;
+ esp_write8(config4, ESP_CFG4);
+ config4 = esp_read8(ESP_CFG4);
+ if (config4 & ESP_CONFIG4_GE1) {
+ esp->rev = PCSCSI;
+ esp_write8(esp->config4, ESP_CFG4);
+ }
+ }
esp->max_period = (esp->max_period + 3)>>2;
esp->min_period = (esp->min_period + 3)>>2;
/* fallthrough... */
case FAS236:
- /* Fast 236 or HME */
+ case PCSCSI:
+ /* Fast 236, AM53c974 or HME */
esp_write8(esp->config2, ESP_CFG2);
if (esp->rev == FASHME) {
u8 cfg3 = esp->target[0].esp_config3;
p->cur_residue -= len;
p->tot_residue -= len;
if (p->cur_residue < 0 || p->tot_residue < 0) {
- printk(KERN_ERR PFX "esp%d: Data transfer overflow.\n",
- esp->host->unique_id);
- printk(KERN_ERR PFX "esp%d: cur_residue[%d] tot_residue[%d] "
- "len[%u]\n",
- esp->host->unique_id,
- p->cur_residue, p->tot_residue, len);
+ shost_printk(KERN_ERR, esp->host,
+ "Data transfer overflow.\n");
+ shost_printk(KERN_ERR, esp->host,
+ "cur_residue[%d] tot_residue[%d] len[%u]\n",
+ p->cur_residue, p->tot_residue, len);
p->cur_residue = 0;
p->tot_residue = 0;
}
if (!ent->sense_ptr) {
- esp_log_autosense("esp%d: Doing auto-sense for "
- "tgt[%d] lun[%d]\n",
- esp->host->unique_id, tgt, lun);
+ esp_log_autosense("Doing auto-sense for tgt[%d] lun[%d]\n",
+ tgt, lun);
ent->sense_ptr = cmd->sense_buffer;
ent->sense_dma = esp->ops->map_single(esp,
val = (p - esp->command_block);
- if (esp->rev == FASHME)
- scsi_esp_cmd(esp, ESP_CMD_FLUSH);
- esp->ops->send_dma_cmd(esp, esp->command_block_dma,
- val, 16, 0, ESP_CMD_DMA | ESP_CMD_SELA);
+ esp_send_dma_cmd(esp, val, 16, ESP_CMD_SELA);
}
static struct esp_cmd_entry *find_and_prep_issuable_command(struct esp *esp)
return ent;
}
- if (!scsi_populate_tag_msg(cmd, &ent->tag[0])) {
+ if (!spi_populate_tag_msg(&ent->tag[0], cmd)) {
ent->tag[0] = 0;
ent->tag[1] = 0;
}
}
if (!(esp->flags & ESP_FLAG_DOING_SLOWCMD)) {
- start_cmd = ESP_CMD_DMA | ESP_CMD_SELA;
+ start_cmd = ESP_CMD_SELA;
if (ent->tag[0]) {
*p++ = ent->tag[0];
*p++ = ent->tag[1];
- start_cmd = ESP_CMD_DMA | ESP_CMD_SA3;
+ start_cmd = ESP_CMD_SA3;
}
for (i = 0; i < cmd->cmd_len; i++)
esp->msg_out_len += 2;
}
- start_cmd = ESP_CMD_DMA | ESP_CMD_SELAS;
+ start_cmd = ESP_CMD_SELAS;
esp->select_state = ESP_SELECT_MSGOUT;
}
val = tgt;
printk("]\n");
}
- if (esp->rev == FASHME)
- scsi_esp_cmd(esp, ESP_CMD_FLUSH);
- esp->ops->send_dma_cmd(esp, esp->command_block_dma,
- val, 16, 0, start_cmd);
+ esp_send_dma_cmd(esp, val, 16, start_cmd);
}
static struct esp_cmd_entry *esp_get_ent(struct esp *esp)
* - DMA programmed with wrong direction
* - improper phase change
*/
- printk(KERN_ERR PFX "esp%d: Gross error sreg[%02x]\n",
- esp->host->unique_id, esp->sreg);
+ shost_printk(KERN_ERR, esp->host,
+ "Gross error sreg[%02x]\n", esp->sreg);
/* XXX Reset the chip. XXX */
return 1;
}
default:
if (!(esp->sreg & ESP_STAT_INTR)) {
- esp->ireg = esp_read8(ESP_INTRPT);
if (esp->ireg & ESP_INTR_SR)
return 1;
* ESP is not, the only possibility is a DMA error.
*/
if (!esp->ops->dma_error(esp)) {
- printk(KERN_ERR PFX "esp%d: Spurious irq, "
- "sreg=%02x.\n",
- esp->host->unique_id, esp->sreg);
+ shost_printk(KERN_ERR, esp->host,
+ "Spurious irq, sreg=%02x.\n",
+ esp->sreg);
return -1;
}
- printk(KERN_ERR PFX "esp%d: DMA error\n",
- esp->host->unique_id);
+ shost_printk(KERN_ERR, esp->host, "DMA error\n");
/* XXX Reset the chip. XXX */
return -1;
static void esp_schedule_reset(struct esp *esp)
{
- esp_log_reset("ESP: esp_schedule_reset() from %pf\n",
+ esp_log_reset("esp_schedule_reset() from %pf\n",
__builtin_return_address(0));
esp->flags |= ESP_FLAG_RESETTING;
esp_event(esp, ESP_EVENT_RESET);
int i;
if (!lp->num_tagged) {
- printk(KERN_ERR PFX "esp%d: Reconnect w/num_tagged==0\n",
- esp->host->unique_id);
+ shost_printk(KERN_ERR, esp->host,
+ "Reconnect w/num_tagged==0\n");
return NULL;
}
- esp_log_reconnect("ESP: reconnect tag, ");
+ esp_log_reconnect("reconnect tag, ");
for (i = 0; i < ESP_QUICKIRQ_LIMIT; i++) {
if (esp->ops->irq_pending(esp))
break;
}
if (i == ESP_QUICKIRQ_LIMIT) {
- printk(KERN_ERR PFX "esp%d: Reconnect IRQ1 timeout\n",
- esp->host->unique_id);
+ shost_printk(KERN_ERR, esp->host,
+ "Reconnect IRQ1 timeout\n");
return NULL;
}
i, esp->ireg, esp->sreg);
if (esp->ireg & ESP_INTR_DC) {
- printk(KERN_ERR PFX "esp%d: Reconnect, got disconnect.\n",
- esp->host->unique_id);
+ shost_printk(KERN_ERR, esp->host,
+ "Reconnect, got disconnect.\n");
return NULL;
}
if ((esp->sreg & ESP_STAT_PMASK) != ESP_MIP) {
- printk(KERN_ERR PFX "esp%d: Reconnect, not MIP sreg[%02x].\n",
- esp->host->unique_id, esp->sreg);
+ shost_printk(KERN_ERR, esp->host,
+ "Reconnect, not MIP sreg[%02x].\n", esp->sreg);
return NULL;
}
udelay(1);
}
if (i == ESP_RESELECT_TAG_LIMIT) {
- printk(KERN_ERR PFX "esp%d: Reconnect IRQ2 timeout\n",
- esp->host->unique_id);
+ shost_printk(KERN_ERR, esp->host, "Reconnect IRQ2 timeout\n");
return NULL;
}
esp->ops->dma_drain(esp);
if (esp->command_block[0] < SIMPLE_QUEUE_TAG ||
esp->command_block[0] > ORDERED_QUEUE_TAG) {
- printk(KERN_ERR PFX "esp%d: Reconnect, bad tag "
- "type %02x.\n",
- esp->host->unique_id, esp->command_block[0]);
+ shost_printk(KERN_ERR, esp->host,
+ "Reconnect, bad tag type %02x.\n",
+ esp->command_block[0]);
return NULL;
}
ent = lp->tagged_cmds[esp->command_block[1]];
if (!ent) {
- printk(KERN_ERR PFX "esp%d: Reconnect, no entry for "
- "tag %02x.\n",
- esp->host->unique_id, esp->command_block[1]);
+ shost_printk(KERN_ERR, esp->host,
+ "Reconnect, no entry for tag %02x.\n",
+ esp->command_block[1]);
return NULL;
}
tp = &esp->target[target];
dev = __scsi_device_lookup_by_target(tp->starget, lun);
if (!dev) {
- printk(KERN_ERR PFX "esp%d: Reconnect, no lp "
- "tgt[%u] lun[%u]\n",
- esp->host->unique_id, target, lun);
+ shost_printk(KERN_ERR, esp->host,
+ "Reconnect, no lp tgt[%u] lun[%u]\n",
+ target, lun);
goto do_reset;
}
lp = dev->hostdata;
return 0;
}
- printk("ESP: Unexpected selection completion ireg[%x].\n",
- esp->ireg);
+ shost_printk(KERN_INFO, esp->host,
+ "Unexpected selection completion ireg[%x]\n", esp->ireg);
esp_schedule_reset(esp);
return 0;
}
(((unsigned int)esp_read8(ESP_TCMED)) << 8));
if (esp->rev == FASHME)
ecount |= ((unsigned int)esp_read8(FAS_RLO)) << 16;
+ if (esp->rev == PCSCSI && (esp->config2 & ESP_CONFIG2_FENAB))
+ ecount |= ((unsigned int)esp_read8(ESP_TCHI)) << 16;
}
bytes_sent = esp->data_dma_len;
bytes_sent -= ecount;
+ /*
+ * The am53c974 has a DMA 'pecularity'. The doc states:
+ * In some odd byte conditions, one residual byte will
+ * be left in the SCSI FIFO, and the FIFO Flags will
+ * never count to '0 '. When this happens, the residual
+ * byte should be retrieved via PIO following completion
+ * of the BLAST operation.
+ */
+ if (fifo_cnt == 1 && ent->flags & ESP_CMD_FLAG_RESIDUAL) {
+ size_t count = 1;
+ size_t offset = bytes_sent;
+ u8 bval = esp_read8(ESP_FDATA);
+
+ if (ent->flags & ESP_CMD_FLAG_AUTOSENSE)
+ ent->sense_ptr[bytes_sent] = bval;
+ else {
+ struct esp_cmd_priv *p = ESP_CMD_PRIV(cmd);
+ u8 *ptr;
+
+ ptr = scsi_kmap_atomic_sg(p->cur_sg, p->u.num_sg,
+ &offset, &count);
+ if (likely(ptr)) {
+ *(ptr + offset) = bval;
+ scsi_kunmap_atomic_sg(ptr);
+ }
+ }
+ bytes_sent += fifo_cnt;
+ ent->flags &= ~ESP_CMD_FLAG_RESIDUAL;
+ }
if (!(ent->flags & ESP_CMD_FLAG_WRITE))
bytes_sent -= fifo_cnt;
return;
}
- printk("ESP: Unexpected extended msg type %x\n",
- esp->msg_in[2]);
+ shost_printk(KERN_INFO, esp->host,
+ "Unexpected extended msg type %x\n", esp->msg_in[2]);
esp->msg_out[0] = ABORT_TASK_SET;
esp->msg_out_len = 1;
if (msg0 & 0x80) {
/* Identify */
- printk("ESP: Unexpected msgin identify\n");
+ shost_printk(KERN_INFO, esp->host,
+ "Unexpected msgin identify\n");
return 0;
}
static int esp_process_event(struct esp *esp)
{
- int write;
+ int write, i;
again:
write = 0;
+ esp_log_event("process event %d phase %x\n",
+ esp->event, esp->sreg & ESP_STAT_PMASK);
switch (esp->event) {
case ESP_EVENT_CHECK_PHASE:
switch (esp->sreg & ESP_STAT_PMASK) {
break;
default:
- printk("ESP: Unexpected phase, sreg=%02x\n",
- esp->sreg);
+ shost_printk(KERN_INFO, esp->host,
+ "Unexpected phase, sreg=%02x\n",
+ esp->sreg);
esp_schedule_reset(esp);
return 0;
}
esp->data_dma_len = dma_len;
if (!dma_len) {
- printk(KERN_ERR PFX "esp%d: DMA length is zero!\n",
- esp->host->unique_id);
- printk(KERN_ERR PFX "esp%d: cur adr[%08llx] len[%08x]\n",
- esp->host->unique_id,
- (unsigned long long)esp_cur_dma_addr(ent, cmd),
- esp_cur_dma_len(ent, cmd));
+ shost_printk(KERN_ERR, esp->host,
+ "DMA length is zero!\n");
+ shost_printk(KERN_ERR, esp->host,
+ "cur adr[%08llx] len[%08x]\n",
+ (unsigned long long)esp_cur_dma_addr(ent, cmd),
+ esp_cur_dma_len(ent, cmd));
esp_schedule_reset(esp);
return 0;
}
- esp_log_datastart("ESP: start data addr[%08llx] len[%u] "
- "write(%d)\n",
+ esp_log_datastart("start data addr[%08llx] len[%u] write(%d)\n",
(unsigned long long)dma_addr, dma_len, write);
esp->ops->send_dma_cmd(esp, dma_addr, dma_len, dma_len,
int bytes_sent;
if (esp->ops->dma_error(esp)) {
- printk("ESP: data done, DMA error, resetting\n");
+ shost_printk(KERN_INFO, esp->host,
+ "data done, DMA error, resetting\n");
esp_schedule_reset(esp);
return 0;
}
/* We should always see exactly a bus-service
* interrupt at the end of a successful transfer.
*/
- printk("ESP: data done, not BSERV, resetting\n");
+ shost_printk(KERN_INFO, esp->host,
+ "data done, not BSERV, resetting\n");
esp_schedule_reset(esp);
return 0;
}
bytes_sent = esp_data_bytes_sent(esp, ent, cmd);
- esp_log_datadone("ESP: data done flgs[%x] sent[%d]\n",
+ esp_log_datadone("data done flgs[%x] sent[%d]\n",
ent->flags, bytes_sent);
if (bytes_sent < 0) {
}
if (ent->message != COMMAND_COMPLETE) {
- printk("ESP: Unexpected message %x in status\n",
- ent->message);
+ shost_printk(KERN_INFO, esp->host,
+ "Unexpected message %x in status\n",
+ ent->message);
esp_schedule_reset(esp);
return 0;
}
scsi_esp_cmd(esp, ESP_CMD_ESEL);
if (ent->message == COMMAND_COMPLETE) {
- esp_log_cmddone("ESP: Command done status[%x] "
- "message[%x]\n",
+ esp_log_cmddone("Command done status[%x] message[%x]\n",
ent->status, ent->message);
if (ent->status == SAM_STAT_TASK_SET_FULL)
esp_event_queue_full(esp, ent);
DID_OK));
}
} else if (ent->message == DISCONNECT) {
- esp_log_disconnect("ESP: Disconnecting tgt[%d] "
- "tag[%x:%x]\n",
+ esp_log_disconnect("Disconnecting tgt[%d] tag[%x:%x]\n",
cmd->device->id,
ent->tag[0], ent->tag[1]);
esp->active_cmd = NULL;
esp_maybe_execute_command(esp);
} else {
- printk("ESP: Unexpected message %x in freebus\n",
- ent->message);
+ shost_printk(KERN_INFO, esp->host,
+ "Unexpected message %x in freebus\n",
+ ent->message);
esp_schedule_reset(esp);
return 0;
}
if (esp->msg_out_len == 1) {
esp_write8(esp->msg_out[0], ESP_FDATA);
scsi_esp_cmd(esp, ESP_CMD_TI);
+ } else if (esp->flags & ESP_FLAG_USE_FIFO) {
+ for (i = 0; i < esp->msg_out_len; i++)
+ esp_write8(esp->msg_out[i], ESP_FDATA);
+ scsi_esp_cmd(esp, ESP_CMD_TI);
} else {
/* Use DMA. */
memcpy(esp->command_block,
val = esp_read8(ESP_FDATA);
esp->msg_in[esp->msg_in_len++] = val;
- esp_log_msgin("ESP: Got msgin byte %x\n", val);
+ esp_log_msgin("Got msgin byte %x\n", val);
if (!esp_msgin_process(esp))
esp->msg_in_len = 0;
if (esp->event != ESP_EVENT_FREE_BUS)
esp_event(esp, ESP_EVENT_CHECK_PHASE);
} else {
- printk("ESP: MSGIN neither BSERV not FDON, resetting");
+ shost_printk(KERN_INFO, esp->host,
+ "MSGIN neither BSERV not FDON, resetting");
esp_schedule_reset(esp);
return 0;
}
case ESP_EVENT_CMD_START:
memcpy(esp->command_block, esp->cmd_bytes_ptr,
esp->cmd_bytes_left);
- if (esp->rev == FASHME)
- scsi_esp_cmd(esp, ESP_CMD_FLUSH);
- esp->ops->send_dma_cmd(esp, esp->command_block_dma,
- esp->cmd_bytes_left, 16, 0,
- ESP_CMD_DMA | ESP_CMD_TI);
+ esp_send_dma_cmd(esp, esp->cmd_bytes_left, 16, ESP_CMD_TI);
esp_event(esp, ESP_EVENT_CMD_DONE);
esp->flags |= ESP_FLAG_QUICKIRQ_CHECK;
break;
break;
default:
- printk("ESP: Unexpected event %x, resetting\n",
- esp->event);
+ shost_printk(KERN_INFO, esp->host,
+ "Unexpected event %x, resetting\n", esp->event);
esp_schedule_reset(esp);
return 0;
break;
int finish_reset, intr_done;
u8 phase;
+ /*
+ * Once INTRPT is read STATUS and SSTEP are cleared.
+ */
esp->sreg = esp_read8(ESP_STATUS);
+ esp->seqreg = esp_read8(ESP_SSTEP);
+ esp->ireg = esp_read8(ESP_INTRPT);
if (esp->flags & ESP_FLAG_RESETTING) {
finish_reset = 1;
return;
}
- esp->ireg = esp_read8(ESP_INTRPT);
-
if (esp->ireg & ESP_INTR_SR)
finish_reset = 1;
}
}
- esp_log_intr("ESP: intr sreg[%02x] seqreg[%02x] "
+ esp_log_intr("intr sreg[%02x] seqreg[%02x] "
"sreg2[%02x] ireg[%02x]\n",
esp->sreg, esp->seqreg, esp->sreg2, esp->ireg);
intr_done = 0;
if (esp->ireg & (ESP_INTR_S | ESP_INTR_SATN | ESP_INTR_IC)) {
- printk("ESP: unexpected IREG %02x\n", esp->ireg);
+ shost_printk(KERN_INFO, esp->host,
+ "unexpected IREG %02x\n", esp->ireg);
if (esp->ireg & ESP_INTR_IC)
esp_dump_cmd_log(esp);
u8 val;
esp->config1 = (ESP_CONFIG1_PENABLE | (esp->scsi_id & 7));
- esp->config2 = (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY);
+ if (esp->config2 == 0) {
+ esp->config2 = (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY);
+ esp_write8(esp->config2, ESP_CFG2);
+
+ val = esp_read8(ESP_CFG2);
+ val &= ~ESP_CONFIG2_MAGIC;
+
+ esp->config2 = 0;
+ if (val != (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) {
+ /*
+ * If what we write to cfg2 does not come back,
+ * cfg2 is not implemented.
+ * Therefore this must be a plain esp100.
+ */
+ esp->rev = ESP100;
+ return;
+ }
+ }
+
+ esp_set_all_config3(esp, 5);
+ esp->prev_cfg3 = 5;
esp_write8(esp->config2, ESP_CFG2);
+ esp_write8(0, ESP_CFG3);
+ esp_write8(esp->prev_cfg3, ESP_CFG3);
- val = esp_read8(ESP_CFG2);
- val &= ~ESP_CONFIG2_MAGIC;
- if (val != (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) {
- /* If what we write to cfg2 does not come back, cfg2 is not
- * implemented, therefore this must be a plain esp100.
+ val = esp_read8(ESP_CFG3);
+ if (val != 5) {
+ /* The cfg2 register is implemented, however
+ * cfg3 is not, must be esp100a.
*/
- esp->rev = ESP100;
+ esp->rev = ESP100A;
} else {
- esp->config2 = 0;
- esp_set_all_config3(esp, 5);
- esp->prev_cfg3 = 5;
- esp_write8(esp->config2, ESP_CFG2);
- esp_write8(0, ESP_CFG3);
+ esp_set_all_config3(esp, 0);
+ esp->prev_cfg3 = 0;
esp_write8(esp->prev_cfg3, ESP_CFG3);
- val = esp_read8(ESP_CFG3);
- if (val != 5) {
- /* The cfg2 register is implemented, however
- * cfg3 is not, must be esp100a.
- */
- esp->rev = ESP100A;
+ /* All of cfg{1,2,3} implemented, must be one of
+ * the fas variants, figure out which one.
+ */
+ if (esp->cfact == 0 || esp->cfact > ESP_CCF_F5) {
+ esp->rev = FAST;
+ esp->sync_defp = SYNC_DEFP_FAST;
} else {
- esp_set_all_config3(esp, 0);
- esp->prev_cfg3 = 0;
- esp_write8(esp->prev_cfg3, ESP_CFG3);
-
- /* All of cfg{1,2,3} implemented, must be one of
- * the fas variants, figure out which one.
- */
- if (esp->cfact == 0 || esp->cfact > ESP_CCF_F5) {
- esp->rev = FAST;
- esp->sync_defp = SYNC_DEFP_FAST;
- } else {
- esp->rev = ESP236;
- }
- esp->config2 = 0;
- esp_write8(esp->config2, ESP_CFG2);
+ esp->rev = ESP236;
}
}
}
"FAS100A",
"FAST",
"FASHME",
+ "AM53C974",
};
static struct scsi_transport_template *esp_transport_template;
static int instance;
int err;
+ if (!esp->num_tags)
+ esp->num_tags = ESP_DEFAULT_TAGS;
+ else if (esp->num_tags >= ESP_MAX_TAG)
+ esp->num_tags = ESP_MAX_TAG - 1;
esp->host->transportt = esp_transport_template;
esp->host->max_lun = ESP_MAX_LUN;
esp->host->cmd_per_lun = 2;
esp_bootup_reset(esp);
- printk(KERN_INFO PFX "esp%u, regs[%1p:%1p] irq[%u]\n",
- esp->host->unique_id, esp->regs, esp->dma_regs,
- esp->host->irq);
- printk(KERN_INFO PFX "esp%u is a %s, %u MHz (ccf=%u), SCSI ID %u\n",
- esp->host->unique_id, esp_chip_names[esp->rev],
- esp->cfreq / 1000000, esp->cfact, esp->scsi_id);
+ dev_printk(KERN_INFO, dev, "esp%u: regs[%1p:%1p] irq[%u]\n",
+ esp->host->unique_id, esp->regs, esp->dma_regs,
+ esp->host->irq);
+ dev_printk(KERN_INFO, dev,
+ "esp%u: is a %s, %u MHz (ccf=%u), SCSI ID %u\n",
+ esp->host->unique_id, esp_chip_names[esp->rev],
+ esp->cfreq / 1000000, esp->cfact, esp->scsi_id);
/* Let the SCSI bus reset settle. */
ssleep(esp_bus_reset_settle);
{
struct esp *esp = shost_priv(dev->host);
struct esp_target_data *tp = &esp->target[dev->id];
- int goal_tags, queue_depth;
-
- goal_tags = 0;
- if (dev->tagged_supported) {
- /* XXX make this configurable somehow XXX */
- goal_tags = ESP_DEFAULT_TAGS;
-
- if (goal_tags > ESP_MAX_TAG)
- goal_tags = ESP_MAX_TAG;
- }
+ if (dev->tagged_supported)
+ scsi_change_queue_depth(dev, esp->num_tags);
- queue_depth = goal_tags;
- if (queue_depth < dev->host->cmd_per_lun)
- queue_depth = dev->host->cmd_per_lun;
-
- if (goal_tags) {
- scsi_set_tag_type(dev, MSG_ORDERED_TAG);
- scsi_activate_tcq(dev, queue_depth);
- } else {
- scsi_deactivate_tcq(dev, queue_depth);
- }
tp->flags |= ESP_TGT_DISCONNECT;
if (!spi_initial_dv(dev->sdev_target))
* XXX much for the final driver.
*/
spin_lock_irqsave(esp->host->host_lock, flags);
- printk(KERN_ERR PFX "esp%d: Aborting command [%p:%02x]\n",
- esp->host->unique_id, cmd, cmd->cmnd[0]);
+ shost_printk(KERN_ERR, esp->host, "Aborting command [%p:%02x]\n",
+ cmd, cmd->cmnd[0]);
ent = esp->active_cmd;
if (ent)
- printk(KERN_ERR PFX "esp%d: Current command [%p:%02x]\n",
- esp->host->unique_id, ent->cmd, ent->cmd->cmnd[0]);
+ shost_printk(KERN_ERR, esp->host,
+ "Current command [%p:%02x]\n",
+ ent->cmd, ent->cmd->cmnd[0]);
list_for_each_entry(ent, &esp->queued_cmds, list) {
- printk(KERN_ERR PFX "esp%d: Queued command [%p:%02x]\n",
- esp->host->unique_id, ent->cmd, ent->cmd->cmnd[0]);
+ shost_printk(KERN_ERR, esp->host, "Queued command [%p:%02x]\n",
+ ent->cmd, ent->cmd->cmnd[0]);
}
list_for_each_entry(ent, &esp->active_cmds, list) {
- printk(KERN_ERR PFX "esp%d: Active command [%p:%02x]\n",
- esp->host->unique_id, ent->cmd, ent->cmd->cmnd[0]);
+ shost_printk(KERN_ERR, esp->host, " Active command [%p:%02x]\n",
+ ent->cmd, ent->cmd->cmnd[0]);
}
esp_dump_cmd_log(esp);
spin_unlock_irqrestore(esp->host->host_lock, flags);
.use_clustering = ENABLE_CLUSTERING,
.max_sectors = 0xffff,
.skip_settle_delay = 1,
+ .use_blk_tags = 1,
};
EXPORT_SYMBOL(scsi_esp_template);
-/* esp_scsi.h: Defines and structures for the ESP drier.
+/* esp_scsi.h: Defines and structures for the ESP driver.
*
* Copyright (C) 2007 David S. Miller (davem@davemloft.net)
*/
#define ESP_CTEST 0x0aUL /* wo Chip test register 0x28 */
#define ESP_CFG2 0x0bUL /* rw Second cfg register 0x2c */
#define ESP_CFG3 0x0cUL /* rw Third cfg register 0x30 */
+#define ESP_CFG4 0x0dUL /* rw Fourth cfg register 0x34 */
#define ESP_TCHI 0x0eUL /* rw High bits transf count 0x38 */
#define ESP_UID ESP_TCHI /* ro Unique ID code 0x38 */
#define FAS_RLO ESP_TCHI /* rw HME extended counter 0x38 */
#define ESP_CONFIG3_IMS 0x80 /* ID msg chk'ng (esp/fas236) */
#define ESP_CONFIG3_OBPUSH 0x80 /* Push odd-byte to dma (hme) */
+/* ESP config register 4 read-write, found only on am53c974 chips */
+#define ESP_CONFIG4_RADE 0x04 /* Active negation */
+#define ESP_CONFIG4_RAE 0x08 /* Active negation on REQ and ACK */
+#define ESP_CONFIG4_PWD 0x20 /* Reduced power feature */
+#define ESP_CONFIG4_GE0 0x40 /* Glitch eater bit 0 */
+#define ESP_CONFIG4_GE1 0x80 /* Glitch eater bit 1 */
+
+#define ESP_CONFIG_GE_12NS (0)
+#define ESP_CONFIG_GE_25NS (ESP_CONFIG_GE1)
+#define ESP_CONFIG_GE_35NS (ESP_CONFIG_GE0)
+#define ESP_CONFIG_GE_0NS (ESP_CONFIG_GE0 | ESP_CONFIG_GE1)
+
/* ESP command register read-write */
/* Group 1 commands: These may be sent at any point in time to the ESP
* chip. None of them can generate interrupts 'cept
FAS100A = 0x04,
FAST = 0x05,
FASHME = 0x06,
+ PCSCSI = 0x07, /* AM53c974 */
};
struct esp_cmd_entry {
#define ESP_CMD_FLAG_WRITE 0x01 /* DMA is a write */
#define ESP_CMD_FLAG_ABORT 0x02 /* being aborted */
#define ESP_CMD_FLAG_AUTOSENSE 0x04 /* Doing automatic REQUEST_SENSE */
+#define ESP_CMD_FLAG_RESIDUAL 0x08 /* AM53c974 BLAST residual */
u8 tag[2];
u8 orig_tag[2];
struct completion *eh_done;
};
-/* XXX make this configurable somehow XXX */
#define ESP_DEFAULT_TAGS 16
#define ESP_MAX_TARGET 16
u8 prev_soff;
u8 prev_stp;
u8 prev_cfg3;
- u8 __pad;
+ u8 num_tags;
struct list_head esp_cmd_pool;
u8 bursts;
u8 config1;
u8 config2;
+ u8 config4;
u8 scsi_id;
u32 scsi_id_mask;
#define ESP_FLAG_WIDE_CAPABLE 0x00000008
#define ESP_FLAG_QUICKIRQ_CHECK 0x00000010
#define ESP_FLAG_DISABLE_SYNC 0x00000020
+#define ESP_FLAG_USE_FIFO 0x00000040
u8 select_state;
#define ESP_SELECT_NONE 0x00 /* Not selecting */
.eh_device_reset_handler = fc_eh_device_reset,
.eh_host_reset_handler = fc_eh_host_reset,
.slave_alloc = fc_slave_alloc,
- .change_queue_depth = fc_change_queue_depth,
- .change_queue_type = fc_change_queue_type,
+ .change_queue_depth = scsi_change_queue_depth,
+ .change_queue_type = scsi_change_queue_type,
.this_id = -1,
.cmd_per_lun = 3,
.can_queue = FCOE_MAX_OUTSTANDING_COMMANDS,
.use_clustering = ENABLE_CLUSTERING,
.sg_tablesize = SG_ALL,
.max_sectors = 0xffff,
+ .use_blk_tags = 1,
+ .track_queue_depth = 1,
};
/**
#define DRV_NAME "fnic"
#define DRV_DESCRIPTION "Cisco FCoE HBA Driver"
-#define DRV_VERSION "1.6.0.11"
+#define DRV_VERSION "1.6.0.16"
#define PFX DRV_NAME ": "
#define DFX DRV_NAME "%d: "
fnic->lport->host->host_no, FNIC_FC_LE,
"Link Status: UP_DOWN",
strlen("Link Status: UP_DOWN"));
+ if (fnic->config.flags & VFCF_FIP_CAPABLE) {
+ FNIC_FCS_DBG(KERN_DEBUG, fnic->lport->host,
+ "deleting fip-timer during link-down\n");
+ del_timer_sync(&fnic->fip_timer);
+ }
fcoe_ctlr_link_down(&fnic->ctlr);
}
{
struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
- sdev->tagged_supported = 1;
-
if (!rport || fc_remote_port_chkready(rport))
return -ENXIO;
- scsi_activate_tcq(sdev, fnic_max_qdepth);
+ scsi_change_queue_depth(sdev, fnic_max_qdepth);
return 0;
}
.eh_device_reset_handler = fnic_device_reset,
.eh_host_reset_handler = fnic_host_reset,
.slave_alloc = fnic_slave_alloc,
- .change_queue_depth = fc_change_queue_depth,
- .change_queue_type = fc_change_queue_type,
+ .change_queue_depth = scsi_change_queue_depth,
+ .change_queue_type = scsi_change_queue_type,
.this_id = -1,
.cmd_per_lun = 3,
.can_queue = FNIC_DFLT_IO_REQ,
.sg_tablesize = FNIC_MAX_SG_DESC_CNT,
.max_sectors = 0xffff,
.shost_attrs = fnic_attrs,
+ .use_blk_tags = 1,
+ .track_queue_depth = 1,
};
static void
unsigned long time;
int done;
int err;
+ int count;
+
+ count = 0;
err = start(vdev, arg);
if (err)
return err;
- /* Wait for func to complete...2 seconds max */
+ /* Wait for func to complete.
+ * Sometime schedule_timeout_uninterruptible take long time
+ * to wake up so we do not retry as we are only waiting for
+ * 2 seconds in while loop. By adding count, we make sure
+ * we try atleast three times before returning -ETIMEDOUT
+ */
time = jiffies + (HZ * 2);
do {
err = finished(vdev, &done);
+ count++;
if (err)
return err;
if (done)
return 0;
schedule_timeout_uninterruptible(HZ / 10);
- } while (time_after(time, jiffies));
+ } while (time_after(time, jiffies) || (count < 3));
return -ETIMEDOUT;
}
struct fc_rport_libfc_priv *rp = rport->dd_data;
struct host_sg_desc *desc;
struct misc_stats *misc_stats = &fnic->fnic_stats.misc_stats;
- u8 pri_tag = 0;
unsigned int i;
unsigned long intr_flags;
int flags;
u8 exch_flags;
struct scsi_lun fc_lun;
- char msg[2];
if (sg_count) {
/* For each SGE, create a device desc entry */
int_to_scsilun(sc->device->lun, &fc_lun);
- pri_tag = FCPIO_ICMND_PTA_SIMPLE;
- msg[0] = MSG_SIMPLE_TAG;
- scsi_populate_tag_msg(sc, msg);
- if (msg[0] == MSG_ORDERED_TAG)
- pri_tag = FCPIO_ICMND_PTA_ORDERED;
-
/* Enqueue the descriptor in the Copy WQ */
spin_lock_irqsave(&fnic->wq_copy_lock[0], intr_flags);
io_req->sgl_list_pa,
io_req->sense_buf_pa,
0, /* scsi cmd ref, always 0 */
- pri_tag, /* scsi pri and tag */
+ FCPIO_ICMND_PTA_SIMPLE,
+ /* scsi pri and tag */
flags, /* command flags */
sc->cmnd, sc->cmd_len,
scsi_bufflen(sc),
int ret;
u64 cmd_trace;
int sg_count = 0;
- unsigned long flags;
+ unsigned long flags = 0;
unsigned long ptr;
+ struct fc_rport_priv *rdata;
+ spinlock_t *io_lock = NULL;
if (unlikely(fnic_chk_state_flags_locked(fnic, FNIC_FLAGS_IO_BLOCKED)))
return SCSI_MLQUEUE_HOST_BUSY;
return 0;
}
+ rdata = lp->tt.rport_lookup(lp, rport->port_id);
+ if (!rdata || (rdata->rp_state == RPORT_ST_DELETE)) {
+ FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host,
+ "returning IO as rport is removed\n");
+ atomic64_inc(&fnic_stats->misc_stats.rport_not_ready);
+ sc->result = DID_NO_CONNECT;
+ done(sc);
+ return 0;
+ }
+
if (lp->state != LPORT_ST_READY || !(lp->link_up))
return SCSI_MLQUEUE_HOST_BUSY;
}
}
+ /*
+ * Will acquire lock defore setting to IO initialized.
+ */
+
+ io_lock = fnic_io_lock_hash(fnic, sc);
+ spin_lock_irqsave(io_lock, flags);
+
/* initialize rest of io_req */
io_req->port_id = rport->port_id;
io_req->start_time = jiffies;
* In case another thread cancelled the request,
* refetch the pointer under the lock.
*/
- spinlock_t *io_lock = fnic_io_lock_hash(fnic, sc);
FNIC_TRACE(fnic_queuecommand, sc->device->host->host_no,
sc->request->tag, sc, 0, 0, 0,
(((u64)CMD_FLAGS(sc) << 32) | CMD_STATE(sc)));
- spin_lock_irqsave(io_lock, flags);
io_req = (struct fnic_io_req *)CMD_SP(sc);
CMD_SP(sc) = NULL;
CMD_STATE(sc) = FNIC_IOREQ_CMD_COMPLETE;
fnic_release_ioreq_buf(fnic, io_req, sc);
mempool_free(io_req, fnic->io_req_pool);
}
+ atomic_dec(&fnic->in_flight);
+ /* acquire host lock before returning to SCSI */
+ spin_lock(lp->host->host_lock);
+ return ret;
} else {
atomic64_inc(&fnic_stats->io_stats.active_ios);
atomic64_inc(&fnic_stats->io_stats.num_ios);
sc->request->tag, sc, io_req,
sg_count, cmd_trace,
(((u64)CMD_FLAGS(sc) >> 32) | CMD_STATE(sc)));
+
+ /* if only we issued IO, will we have the io lock */
+ if (CMD_FLAGS(sc) & FNIC_IO_INITIALIZED)
+ spin_unlock_irqrestore(io_lock, flags);
+
atomic_dec(&fnic->in_flight);
/* acquire host lock before returning to SCSI */
spin_lock(lp->host->host_lock);
if (frame_type == FNIC_FC_RECV) {
eth_fcoe_hdr_len = sizeof(struct ethhdr) +
sizeof(struct fcoe_hdr);
- fc_trc_frame_len = fc_trc_frame_len + eth_fcoe_hdr_len;
memset((char *)fc_trace, 0xff, eth_fcoe_hdr_len);
/* Copy the rest of data frame */
memcpy((char *)(fc_trace + eth_fcoe_hdr_len), (void *)frame,
min_t(u8, fc_trc_frame_len,
- (u8)(FC_TRC_SIZE_BYTES - FC_TRC_HEADER_SIZE)));
+ (u8)(FC_TRC_SIZE_BYTES - FC_TRC_HEADER_SIZE
+ - eth_fcoe_hdr_len)));
} else {
memcpy((char *)fc_trace, (void *)frame,
min_t(u8, fc_trc_frame_len,
*
* Added ISAPNP support for DTC436 adapters,
* Thomas Sailer, sailer@ife.ee.ethz.ch
- *
- * ALPHA RELEASE 1.
- *
- * For more information, please consult
- *
- * NCR 5380 Family
- * SCSI Protocol Controller
- * Databook
- *
- * NCR Microelectronics
- * 1635 Aeroplaza Drive
- * Colorado Springs, CO 80916
- * 1+ (719) 578-3400
- * 1+ (800) 334-5454
*/
/*
*/
/*
- * Options :
- *
- * PARITY - enable parity checking. Not supported.
- *
- * SCSI2 - enable support for SCSI-II tagged queueing. Untested.
- *
- * USLEEP - enable support for devices that don't disconnect. Untested.
- *
* The card is detected and initialized in one of several ways :
* 1. With command line overrides - NCR5380=port,irq may be
* used on the LILO command line to override the defaults.
*/
/* settings for DTC3181E card with only Mustek scanner attached */
-#define USLEEP
#define USLEEP_POLL 1
#define USLEEP_SLEEP 20
#define USLEEP_WAITLONG 500
#define AUTOPROBE_IRQ
-#define AUTOSENSE
-
#ifdef CONFIG_SCSI_GENERIC_NCR53C400
#define NCR53C400_PSEUDO_DMA 1
#define PSEUDO_DMA
#define NCR53C400
-#define NCR5380_STATS
-#undef NCR5380_STAT_LIMIT
#endif
#include <asm/io.h>
#include <linux/signal.h>
#include <linux/blkdev.h>
-#include "scsi.h"
#include <scsi/scsi_host.h>
#include "g_NCR5380.h"
#include "NCR5380.h"
* Locks: none
*/
-int __init generic_NCR5380_detect(struct scsi_host_template * tpnt)
+static int __init generic_NCR5380_detect(struct scsi_host_template *tpnt)
{
static int current_override = 0;
int count;
if (pnp_irq_valid(dev, 0))
overrides[count].irq = pnp_irq(dev, 0);
else
- overrides[count].irq = SCSI_IRQ_NONE;
+ overrides[count].irq = NO_IRQ;
if (pnp_dma_valid(dev, 0))
overrides[count].dma = pnp_dma(dev, 0);
else
else
instance->irq = NCR5380_probe_irq(instance, 0xffff);
- if (instance->irq != SCSI_IRQ_NONE)
+ /* Compatibility with documented NCR5380 kernel parameters */
+ if (instance->irq == 255)
+ instance->irq = NO_IRQ;
+
+ if (instance->irq != NO_IRQ)
if (request_irq(instance->irq, generic_NCR5380_intr,
0, "NCR5380", instance)) {
printk(KERN_WARNING "scsi%d : IRQ%d not free, interrupts disabled\n", instance->host_no, instance->irq);
- instance->irq = SCSI_IRQ_NONE;
+ instance->irq = NO_IRQ;
}
- if (instance->irq == SCSI_IRQ_NONE) {
+ if (instance->irq == NO_IRQ) {
printk(KERN_INFO "scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no);
printk(KERN_INFO "scsi%d : please jumper the board for a free IRQ.\n", instance->host_no);
}
- printk(KERN_INFO "scsi%d : at " STRVAL(NCR5380_map_name) " 0x%x", instance->host_no, (unsigned int) instance->NCR5380_instance_name);
- if (instance->irq == SCSI_IRQ_NONE)
- printk(" interrupts disabled");
- else
- printk(" irq %d", instance->irq);
- printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", CAN_QUEUE, CMD_PER_LUN, GENERIC_NCR5380_PUBLIC_RELEASE);
- NCR5380_print_options(instance);
- printk("\n");
-
++current_override;
++count;
}
return count;
}
-/**
- * generic_NCR5380_info - reporting string
- * @host: NCR5380 to report on
- *
- * Report driver information for the NCR5380
- */
-
-const char *generic_NCR5380_info(struct Scsi_Host *host)
-{
- static const char string[] = "Generic NCR5380/53C400 Driver";
- return string;
-}
-
/**
* generic_NCR5380_release_resources - free resources
* @instance: host adapter to clean up
* Locks: none
*/
-int generic_NCR5380_release_resources(struct Scsi_Host *instance)
+static int generic_NCR5380_release_resources(struct Scsi_Host *instance)
{
NCR5380_local_declare();
NCR5380_setup(instance);
- if (instance->irq != SCSI_IRQ_NONE)
+ if (instance->irq != NO_IRQ)
free_irq(instance->irq, instance);
NCR5380_exit(instance);
#include "NCR5380.c"
-#define PRINTP(x) seq_printf(m, x)
-#define ANDP ,
-
-static void sprint_opcode(struct seq_file *m, int opcode)
-{
- PRINTP("0x%02x " ANDP opcode);
-}
-
-static void sprint_command(struct seq_file *m, unsigned char *command)
-{
- int i, s;
- sprint_opcode(m, command[0]);
- for (i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i)
- PRINTP("%02x " ANDP command[i]);
- PRINTP("\n");
-}
-
-/**
- * sprintf_Scsi_Cmnd - print a scsi command
- * @m: seq_fil to print into
- * @cmd: SCSI command block
- *
- * Print out the target and command data in hex
- */
-
-static void sprint_Scsi_Cmnd(struct seq_file *m, Scsi_Cmnd * cmd)
-{
- PRINTP("host number %d destination target %d, lun %llu\n" ANDP cmd->device->host->host_no ANDP cmd->device->id ANDP cmd->device->lun);
- PRINTP(" command = ");
- sprint_command(m, cmd->cmnd);
-}
-
-/**
- * generic_NCR5380_proc_info - /proc for NCR5380 driver
- * @buffer: buffer to print into
- * @start: start position
- * @offset: offset into buffer
- * @len: length
- * @hostno: instance to affect
- * @inout: read/write
- *
- * Provide the procfs information for the 5380 controller. We fill
- * this with useful debugging information including the commands
- * being executed, disconnected command queue and the statistical
- * data
- *
- * Locks: global cli/lock for queue walk
- */
-
-static int generic_NCR5380_show_info(struct seq_file *m, struct Scsi_Host *scsi_ptr)
-{
- NCR5380_local_declare();
- unsigned long flags;
- unsigned char status;
- int i;
- Scsi_Cmnd *ptr;
- struct NCR5380_hostdata *hostdata;
-#ifdef NCR5380_STATS
- struct scsi_device *dev;
-#endif
-
- NCR5380_setup(scsi_ptr);
- hostdata = (struct NCR5380_hostdata *) scsi_ptr->hostdata;
-
- spin_lock_irqsave(scsi_ptr->host_lock, flags);
- PRINTP("SCSI host number %d : %s\n" ANDP scsi_ptr->host_no ANDP scsi_ptr->hostt->name);
- PRINTP("Generic NCR5380 driver version %d\n" ANDP GENERIC_NCR5380_PUBLIC_RELEASE);
- PRINTP("NCR5380 core version %d\n" ANDP NCR5380_PUBLIC_RELEASE);
-#ifdef NCR53C400
- PRINTP("NCR53C400 extension version %d\n" ANDP NCR53C400_PUBLIC_RELEASE);
- PRINTP("NCR53C400 card%s detected\n" ANDP(((struct NCR5380_hostdata *) scsi_ptr->hostdata)->flags & FLAG_NCR53C400) ? "" : " not");
-# if NCR53C400_PSEUDO_DMA
- PRINTP("NCR53C400 pseudo DMA used\n");
-# endif
-#else
- PRINTP("NO NCR53C400 driver extensions\n");
-#endif
- PRINTP("Using %s mapping at %s 0x%lx, " ANDP STRVAL(NCR5380_map_config) ANDP STRVAL(NCR5380_map_name) ANDP scsi_ptr->NCR5380_instance_name);
- if (scsi_ptr->irq == SCSI_IRQ_NONE)
- PRINTP("no interrupt\n");
- else
- PRINTP("on interrupt %d\n" ANDP scsi_ptr->irq);
-
-#ifdef NCR5380_STATS
- if (hostdata->connected || hostdata->issue_queue || hostdata->disconnected_queue)
- PRINTP("There are commands pending, transfer rates may be crud\n");
- if (hostdata->pendingr)
- PRINTP(" %d pending reads" ANDP hostdata->pendingr);
- if (hostdata->pendingw)
- PRINTP(" %d pending writes" ANDP hostdata->pendingw);
- if (hostdata->pendingr || hostdata->pendingw)
- PRINTP("\n");
- shost_for_each_device(dev, scsi_ptr) {
- unsigned long br = hostdata->bytes_read[dev->id];
- unsigned long bw = hostdata->bytes_write[dev->id];
- long tr = hostdata->time_read[dev->id] / HZ;
- long tw = hostdata->time_write[dev->id] / HZ;
-
- PRINTP(" T:%d %s " ANDP dev->id ANDP scsi_device_type(dev->type));
- for (i = 0; i < 8; i++)
- if (dev->vendor[i] >= 0x20)
- seq_putc(m, dev->vendor[i]);
- seq_putc(m, ' ');
- for (i = 0; i < 16; i++)
- if (dev->model[i] >= 0x20)
- seq_putc(m, dev->model[i]);
- seq_putc(m, ' ');
- for (i = 0; i < 4; i++)
- if (dev->rev[i] >= 0x20)
- seq_putc(m, dev->rev[i]);
- seq_putc(m, ' ');
-
- PRINTP("\n%10ld kb read in %5ld secs" ANDP br / 1024 ANDP tr);
- if (tr)
- PRINTP(" @ %5ld bps" ANDP br / tr);
-
- PRINTP("\n%10ld kb written in %5ld secs" ANDP bw / 1024 ANDP tw);
- if (tw)
- PRINTP(" @ %5ld bps" ANDP bw / tw);
- PRINTP("\n");
- }
-#endif
-
- status = NCR5380_read(STATUS_REG);
- if (!(status & SR_REQ))
- PRINTP("REQ not asserted, phase unknown.\n");
- else {
- for (i = 0; (phases[i].value != PHASE_UNKNOWN) && (phases[i].value != (status & PHASE_MASK)); ++i);
- PRINTP("Phase %s\n" ANDP phases[i].name);
- }
-
- if (!hostdata->connected) {
- PRINTP("No currently connected command\n");
- } else {
- sprint_Scsi_Cmnd(m, (Scsi_Cmnd *) hostdata->connected);
- }
-
- PRINTP("issue_queue\n");
-
- for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
- sprint_Scsi_Cmnd(m, ptr);
-
- PRINTP("disconnected_queue\n");
-
- for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
- sprint_Scsi_Cmnd(m, ptr);
-
- spin_unlock_irqrestore(scsi_ptr->host_lock, flags);
- return 0;
-}
-
-#undef PRINTP
-#undef ANDP
-
static struct scsi_host_template driver_template = {
.show_info = generic_NCR5380_show_info,
- .name = "Generic NCR5380/NCR53C400 Scsi Driver",
+ .name = "Generic NCR5380/NCR53C400 SCSI",
.detect = generic_NCR5380_detect,
.release = generic_NCR5380_release_resources,
.info = generic_NCR5380_info,
*
* NCR53C400 extensions (c) 1994,1995,1996, Kevin Lentin
* K.Lentin@cs.monash.edu.au
- *
- * ALPHA RELEASE 1.
- *
- * For more information, please consult
- *
- * NCR 5380 Family
- * SCSI Protocol Controller
- * Databook
- *
- * NCR Microelectronics
- * 1635 Aeroplaza Drive
- * Colorado Springs, CO 80916
- * 1+ (719) 578-3400
- * 1+ (800) 334-5454
*/
#ifndef GENERIC_NCR5380_H
#define GENERIC_NCR5380_H
-
-#define GENERIC_NCR5380_PUBLIC_RELEASE 1
-
#ifdef NCR53C400
#define BIOSPARAM
#define NCR5380_BIOSPARAM generic_NCR5380_biosparam
#endif
#ifndef ASM
-static int generic_NCR5380_abort(Scsi_Cmnd *);
-static int generic_NCR5380_detect(struct scsi_host_template *);
-static int generic_NCR5380_release_resources(struct Scsi_Host *);
-static int generic_NCR5380_queue_command(struct Scsi_Host *, struct scsi_cmnd *);
-static int generic_NCR5380_bus_reset(Scsi_Cmnd *);
-static const char* generic_NCR5380_info(struct Scsi_Host *);
#ifndef CMD_PER_LUN
#define CMD_PER_LUN 2
#define NCR5380_bus_reset generic_NCR5380_bus_reset
#define NCR5380_pread generic_NCR5380_pread
#define NCR5380_pwrite generic_NCR5380_pwrite
-#define NCR5380_proc_info notyet_generic_proc_info
+#define NCR5380_info generic_NCR5380_info
+#define NCR5380_show_info generic_NCR5380_show_info
#define BOARD_NCR5380 0
#define BOARD_NCR53C400 1
case VERIFY:
case START_STOP:
case MODE_SENSE:
- case SERVICE_ACTION_IN:
+ case SERVICE_ACTION_IN_16:
TRACE(("cache cmd %x/%x/%x/%x/%x/%x\n",nscp->cmnd[0],
nscp->cmnd[1],nscp->cmnd[2],nscp->cmnd[3],
nscp->cmnd[4],nscp->cmnd[5]));
gdth_copy_internal_data(ha, scp, (char*)&rdc, sizeof(gdth_rdcap_data));
break;
- case SERVICE_ACTION_IN:
+ case SERVICE_ACTION_IN_16:
if ((scp->cmnd[1] & 0x1f) == SAI_READ_CAPACITY_16 &&
(ha->cache_feat & GDT_64BIT)) {
gdth_rdcap16_data rdc16;
/* configure lun */
static int gdth_slave_configure(struct scsi_device *sdev)
{
- scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun);
sdev->skip_ms_page_3f = 1;
sdev->skip_ms_page_8 = 1;
return 0;
shost->cmd_per_lun = sht->cmd_per_lun;
shost->unchecked_isa_dma = sht->unchecked_isa_dma;
shost->use_clustering = sht->use_clustering;
- shost->ordered_tag = sht->ordered_tag;
shost->no_write_same = sht->no_write_same;
if (shost_eh_deadline == -1 || !sht->eh_host_reset_handler)
WQ_UNBOUND | WQ_MEM_RECLAIM,
1, shost->host_no);
if (!shost->tmf_work_q) {
- printk(KERN_WARNING "scsi%d: failed to create tmf workq\n",
- shost->host_no);
+ shost_printk(KERN_WARNING, shost,
+ "failed to create tmf workq\n");
goto fail_kthread;
}
scsi_proc_hostdir_add(shost->hostt);
#include <linux/bitmap.h>
#include <linux/atomic.h>
#include <linux/jiffies.h>
+#include <linux/percpu-defs.h>
#include <linux/percpu.h>
#include <asm/div64.h>
#include "hpsa_cmd.h"
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSH, 0x103C, 0x1922},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSH, 0x103C, 0x1923},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSH, 0x103C, 0x1924},
- {PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSH, 0x103C, 0x1925},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSH, 0x103C, 0x1926},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSH, 0x103C, 0x1928},
{PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_CISSH, 0x103C, 0x1929},
{0x3249103C, "Smart Array P812", &SA5_access},
{0x324A103C, "Smart Array P712m", &SA5_access},
{0x324B103C, "Smart Array P711m", &SA5_access},
+ {0x3233103C, "HP StorageWorks 1210m", &SA5_access}, /* alias of 333f */
{0x3350103C, "Smart Array P222", &SA5_access},
{0x3351103C, "Smart Array P420", &SA5_access},
{0x3352103C, "Smart Array P421", &SA5_access},
static irqreturn_t do_hpsa_intr_intx(int irq, void *dev_id);
static irqreturn_t do_hpsa_intr_msi(int irq, void *dev_id);
-static int hpsa_ioctl(struct scsi_device *dev, int cmd, void *arg);
+static int hpsa_ioctl(struct scsi_device *dev, int cmd, void __user *arg);
static void lock_and_start_io(struct ctlr_info *h);
static void start_io(struct ctlr_info *h, unsigned long *flags);
#ifdef CONFIG_COMPAT
-static int hpsa_compat_ioctl(struct scsi_device *dev, int cmd, void *arg);
+static int hpsa_compat_ioctl(struct scsi_device *dev, int cmd,
+ void __user *arg);
#endif
static void cmd_free(struct ctlr_info *h, struct CommandList *c);
static void hpsa_scan_start(struct Scsi_Host *);
static int hpsa_scan_finished(struct Scsi_Host *sh,
unsigned long elapsed_time);
-static int hpsa_change_queue_depth(struct scsi_device *sdev,
- int qdepth, int reason);
static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd);
static int hpsa_eh_abort_handler(struct scsi_cmnd *scsicmd);
"detected, command retried\n", h->ctlr);
break;
case LUN_FAILED:
- dev_warn(&h->pdev->dev, HPSA "%d: LUN failure "
- "detected, action required\n", h->ctlr);
+ dev_warn(&h->pdev->dev,
+ HPSA "%d: LUN failure detected\n", h->ctlr);
break;
case REPORT_LUNS_CHANGED:
- dev_warn(&h->pdev->dev, HPSA "%d: report LUN data "
- "changed, action required\n", h->ctlr);
+ dev_warn(&h->pdev->dev,
+ HPSA "%d: report LUN data changed\n", h->ctlr);
/*
* Note: this REPORT_LUNS_CHANGED condition only occurs on the external
* target (array) devices.
struct Scsi_Host *shost = class_to_shost(dev);
struct ctlr_info *h = shost_to_hba(shost);
- return snprintf(buf, 20, "%d\n", h->commands_outstanding);
+ return snprintf(buf, 20, "%d\n",
+ atomic_read(&h->commands_outstanding));
}
static ssize_t host_show_transport_mode(struct device *dev,
.queuecommand = hpsa_scsi_queue_command,
.scan_start = hpsa_scan_start,
.scan_finished = hpsa_scan_finished,
- .change_queue_depth = hpsa_change_queue_depth,
+ .change_queue_depth = scsi_change_queue_depth,
.this_id = -1,
.use_clustering = ENABLE_CLUSTERING,
.eh_abort_handler = hpsa_eh_abort_handler,
{
u32 a;
struct reply_queue_buffer *rq = &h->reply_queue[q];
- unsigned long flags;
if (h->transMethod & CFGTBL_Trans_io_accel1)
return h->access.command_completed(h, q);
if ((rq->head[rq->current_entry] & 1) == rq->wraparound) {
a = rq->head[rq->current_entry];
rq->current_entry++;
- spin_lock_irqsave(&h->lock, flags);
- h->commands_outstanding--;
- spin_unlock_irqrestore(&h->lock, flags);
+ atomic_dec(&h->commands_outstanding);
} else {
a = FIFO_EMPTY;
}
{
struct SGDescriptor *chain_sg, *chain_block;
u64 temp64;
+ u32 chain_len;
chain_sg = &c->SG[h->max_cmd_sg_entries - 1];
chain_block = h->cmd_sg_list[c->cmdindex];
- chain_sg->Ext = HPSA_SG_CHAIN;
- chain_sg->Len = sizeof(*chain_sg) *
+ chain_sg->Ext = cpu_to_le32(HPSA_SG_CHAIN);
+ chain_len = sizeof(*chain_sg) *
(c->Header.SGTotal - h->max_cmd_sg_entries);
- temp64 = pci_map_single(h->pdev, chain_block, chain_sg->Len,
+ chain_sg->Len = cpu_to_le32(chain_len);
+ temp64 = pci_map_single(h->pdev, chain_block, chain_len,
PCI_DMA_TODEVICE);
if (dma_mapping_error(&h->pdev->dev, temp64)) {
/* prevent subsequent unmapping */
- chain_sg->Addr.lower = 0;
- chain_sg->Addr.upper = 0;
+ chain_sg->Addr = cpu_to_le64(0);
return -1;
}
- chain_sg->Addr.lower = (u32) (temp64 & 0x0FFFFFFFFULL);
- chain_sg->Addr.upper = (u32) ((temp64 >> 32) & 0x0FFFFFFFFULL);
+ chain_sg->Addr = cpu_to_le64(temp64);
return 0;
}
struct CommandList *c)
{
struct SGDescriptor *chain_sg;
- union u64bit temp64;
- if (c->Header.SGTotal <= h->max_cmd_sg_entries)
+ if (le16_to_cpu(c->Header.SGTotal) <= h->max_cmd_sg_entries)
return;
chain_sg = &c->SG[h->max_cmd_sg_entries - 1];
- temp64.val32.lower = chain_sg->Addr.lower;
- temp64.val32.upper = chain_sg->Addr.upper;
- pci_unmap_single(h->pdev, temp64.val, chain_sg->Len, PCI_DMA_TODEVICE);
+ pci_unmap_single(h->pdev, le64_to_cpu(chain_sg->Addr),
+ le32_to_cpu(chain_sg->Len), PCI_DMA_TODEVICE);
}
struct io_accel1_cmd *c = &h->ioaccel_cmd_pool[cp->cmdindex];
cp->Header.SGList = cp->Header.SGTotal = scsi_sg_count(cmd);
cp->Request.CDBLen = c->io_flags & IOACCEL1_IOFLAGS_CDBLEN_MASK;
- cp->Header.Tag.lower = c->Tag.lower;
- cp->Header.Tag.upper = c->Tag.upper;
+ cp->Header.tag = c->tag;
memcpy(cp->Header.LUN.LunAddrBytes, c->CISS_LUN, 8);
memcpy(cp->Request.CDB, c->CDB, cp->Request.CDBLen);
/* Get addition sense code qualifier */
ascq = ei->SenseInfo[13];
}
-
if (ei->ScsiStatus == SAM_STAT_CHECK_CONDITION) {
- if (check_for_unit_attention(h, cp))
- break;
- if (sense_key == ILLEGAL_REQUEST) {
- /*
- * SCSI REPORT_LUNS is commonly unsupported on
- * Smart Array. Suppress noisy complaint.
- */
- if (cp->Request.CDB[0] == REPORT_LUNS)
- break;
-
- /* If ASC/ASCQ indicate Logical Unit
- * Not Supported condition,
- */
- if ((asc == 0x25) && (ascq == 0x0)) {
- dev_warn(&h->pdev->dev, "cp %p "
- "has check condition\n", cp);
- break;
- }
- }
-
- if (sense_key == NOT_READY) {
- /* If Sense is Not Ready, Logical Unit
- * Not ready, Manual Intervention
- * required
- */
- if ((asc == 0x04) && (ascq == 0x03)) {
- dev_warn(&h->pdev->dev, "cp %p "
- "has check condition: unit "
- "not ready, manual "
- "intervention required\n", cp);
- break;
- }
- }
if (sense_key == ABORTED_COMMAND) {
- /* Aborted command is retryable */
- dev_warn(&h->pdev->dev, "cp %p "
- "has check condition: aborted command: "
- "ASC: 0x%x, ASCQ: 0x%x\n",
- cp, asc, ascq);
cmd->result |= DID_SOFT_ERROR << 16;
break;
}
- /* Must be some other type of check condition */
- dev_dbg(&h->pdev->dev, "cp %p has check condition: "
- "unknown type: "
- "Sense: 0x%x, ASC: 0x%x, ASCQ: 0x%x, "
- "Returning result: 0x%x, "
- "cmd=[%02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x]\n",
- cp, sense_key, asc, ascq,
- cmd->result,
- cmd->cmnd[0], cmd->cmnd[1],
- cmd->cmnd[2], cmd->cmnd[3],
- cmd->cmnd[4], cmd->cmnd[5],
- cmd->cmnd[6], cmd->cmnd[7],
- cmd->cmnd[8], cmd->cmnd[9],
- cmd->cmnd[10], cmd->cmnd[11],
- cmd->cmnd[12], cmd->cmnd[13],
- cmd->cmnd[14], cmd->cmnd[15]);
break;
}
-
-
/* Problem was not a check condition
* Pass it up to the upper layers...
*/
struct CommandList *c, int sg_used, int data_direction)
{
int i;
- union u64bit addr64;
- for (i = 0; i < sg_used; i++) {
- addr64.val32.lower = c->SG[i].Addr.lower;
- addr64.val32.upper = c->SG[i].Addr.upper;
- pci_unmap_single(pdev, (dma_addr_t) addr64.val, c->SG[i].Len,
- data_direction);
- }
+ for (i = 0; i < sg_used; i++)
+ pci_unmap_single(pdev, (dma_addr_t) le64_to_cpu(c->SG[i].Addr),
+ le32_to_cpu(c->SG[i].Len),
+ data_direction);
}
static int hpsa_map_one(struct pci_dev *pdev,
if (buflen == 0 || data_direction == PCI_DMA_NONE) {
cp->Header.SGList = 0;
- cp->Header.SGTotal = 0;
+ cp->Header.SGTotal = cpu_to_le16(0);
return 0;
}
- addr64 = (u64) pci_map_single(pdev, buf, buflen, data_direction);
+ addr64 = pci_map_single(pdev, buf, buflen, data_direction);
if (dma_mapping_error(&pdev->dev, addr64)) {
/* Prevent subsequent unmap of something never mapped */
cp->Header.SGList = 0;
- cp->Header.SGTotal = 0;
+ cp->Header.SGTotal = cpu_to_le16(0);
return -1;
}
- cp->SG[0].Addr.lower =
- (u32) (addr64 & (u64) 0x00000000FFFFFFFF);
- cp->SG[0].Addr.upper =
- (u32) ((addr64 >> 32) & (u64) 0x00000000FFFFFFFF);
- cp->SG[0].Len = buflen;
- cp->SG[0].Ext = HPSA_SG_LAST; /* we are not chaining */
- cp->Header.SGList = (u8) 1; /* no. SGs contig in this cmd */
- cp->Header.SGTotal = (u16) 1; /* total sgs in this cmd list */
+ cp->SG[0].Addr = cpu_to_le64(addr64);
+ cp->SG[0].Len = cpu_to_le32(buflen);
+ cp->SG[0].Ext = cpu_to_le32(HPSA_SG_LAST); /* we are not chaining */
+ cp->Header.SGList = 1; /* no. SGs contig in this cmd */
+ cp->Header.SGTotal = cpu_to_le16(1); /* total sgs in cmd list */
return 0;
}
if (d == NULL)
return 0; /* no match */
- it_nexus = cpu_to_le32((u32) d->ioaccel_handle);
- scsi_nexus = cpu_to_le32((u32) c2a->scsi_nexus);
+ it_nexus = cpu_to_le32(d->ioaccel_handle);
+ scsi_nexus = cpu_to_le32(c2a->scsi_nexus);
find = c2a->scsi_nexus;
if (h->raid_offload_debug > 0)
* Returns 0 on success, -1 otherwise.
*/
static int hpsa_gather_lun_info(struct ctlr_info *h,
- int reportlunsize,
+ int reportphyslunsize, int reportloglunsize,
struct ReportLUNdata *physdev, u32 *nphysicals, int *physical_mode,
struct ReportLUNdata *logdev, u32 *nlogicals)
{
*physical_mode = HPSA_REPORT_PHYS_EXTENDED;
physical_entry_size = 24;
}
- if (hpsa_scsi_do_report_phys_luns(h, physdev, reportlunsize,
+ if (hpsa_scsi_do_report_phys_luns(h, physdev, reportphyslunsize,
*physical_mode)) {
dev_err(&h->pdev->dev, "report physical LUNs failed.\n");
return -1;
*nphysicals - HPSA_MAX_PHYS_LUN);
*nphysicals = HPSA_MAX_PHYS_LUN;
}
- if (hpsa_scsi_do_report_log_luns(h, logdev, reportlunsize)) {
+ if (hpsa_scsi_do_report_log_luns(h, logdev, reportloglunsize)) {
dev_err(&h->pdev->dev, "report logical LUNs failed.\n");
return -1;
}
return 0;
}
-u8 *figure_lunaddrbytes(struct ctlr_info *h, int raid_ctlr_position, int i,
- int nphysicals, int nlogicals,
+static u8 *figure_lunaddrbytes(struct ctlr_info *h, int raid_ctlr_position,
+ int i, int nphysicals, int nlogicals,
struct ReportExtendedLUNdata *physdev_list,
struct ReportLUNdata *logdev_list)
{
u32 ndev_allocated = 0;
struct hpsa_scsi_dev_t **currentsd, *this_device, *tmpdevice;
int ncurrent = 0;
- int reportlunsize = sizeof(*physdev_list) + HPSA_MAX_PHYS_LUN * 24;
int i, n_ext_target_devs, ndevs_to_allocate;
int raid_ctlr_position;
int rescan_hba_mode;
DECLARE_BITMAP(lunzerobits, MAX_EXT_TARGETS);
currentsd = kzalloc(sizeof(*currentsd) * HPSA_MAX_DEVICES, GFP_KERNEL);
- physdev_list = kzalloc(reportlunsize, GFP_KERNEL);
- logdev_list = kzalloc(reportlunsize, GFP_KERNEL);
+ physdev_list = kzalloc(sizeof(*physdev_list), GFP_KERNEL);
+ logdev_list = kzalloc(sizeof(*logdev_list), GFP_KERNEL);
tmpdevice = kzalloc(sizeof(*tmpdevice), GFP_KERNEL);
if (!currentsd || !physdev_list || !logdev_list || !tmpdevice) {
h->hba_mode_enabled = rescan_hba_mode;
- if (hpsa_gather_lun_info(h, reportlunsize,
+ if (hpsa_gather_lun_info(h,
+ sizeof(*physdev_list), sizeof(*logdev_list),
(struct ReportLUNdata *) physdev_list, &nphysicals,
&physical_mode, logdev_list, &nlogicals))
goto out;
}
addr64 = (u64) sg_dma_address(sg);
len = sg_dma_len(sg);
- curr_sg->Addr.lower = (u32) (addr64 & 0x0FFFFFFFFULL);
- curr_sg->Addr.upper = (u32) ((addr64 >> 32) & 0x0FFFFFFFFULL);
- curr_sg->Len = len;
- curr_sg->Ext = (i < scsi_sg_count(cmd) - 1) ? 0 : HPSA_SG_LAST;
+ curr_sg->Addr = cpu_to_le64(addr64);
+ curr_sg->Len = cpu_to_le32(len);
+ curr_sg->Ext = cpu_to_le32(0);
curr_sg++;
}
+ (--curr_sg)->Ext = cpu_to_le32(HPSA_SG_LAST);
if (use_sg + chained > h->maxSG)
h->maxSG = use_sg + chained;
if (chained) {
cp->Header.SGList = h->max_cmd_sg_entries;
- cp->Header.SGTotal = (u16) (use_sg + 1);
+ cp->Header.SGTotal = cpu_to_le16(use_sg + 1);
if (hpsa_map_sg_chain_block(h, cp)) {
scsi_dma_unmap(cmd);
return -1;
sglist_finished:
cp->Header.SGList = (u8) use_sg; /* no. SGs contig in this cmd */
- cp->Header.SGTotal = (u16) use_sg; /* total sgs in this cmd list */
+ cp->Header.SGTotal = cpu_to_le16(use_sg); /* total sgs in this cmd list */
return 0;
}
addr64 = (u64) sg_dma_address(sg);
len = sg_dma_len(sg);
total_len += len;
- curr_sg->Addr.lower = (u32) (addr64 & 0x0FFFFFFFFULL);
- curr_sg->Addr.upper =
- (u32) ((addr64 >> 32) & 0x0FFFFFFFFULL);
- curr_sg->Len = len;
-
- if (i == (scsi_sg_count(cmd) - 1))
- curr_sg->Ext = HPSA_SG_LAST;
- else
- curr_sg->Ext = 0; /* we are not chaining */
+ curr_sg->Addr = cpu_to_le64(addr64);
+ curr_sg->Len = cpu_to_le32(len);
+ curr_sg->Ext = cpu_to_le32(0);
curr_sg++;
}
+ (--curr_sg)->Ext = cpu_to_le32(HPSA_SG_LAST);
switch (cmd->sc_data_direction) {
case DMA_TO_DEVICE:
cp->data_len = cpu_to_le32(total_len);
cp->err_ptr = cpu_to_le64(c->busaddr +
offsetof(struct io_accel2_cmd, error_data));
- cp->err_len = cpu_to_le32((u32) sizeof(cp->error_data));
+ cp->err_len = cpu_to_le32(sizeof(cp->error_data));
enqueue_cmd_and_start_io(h, c);
return 0;
offload_to_mirror =
(offload_to_mirror >= map->layout_map_count - 1)
? 0 : offload_to_mirror + 1;
- /* FIXME: remove after debug/dev */
- BUG_ON(offload_to_mirror >= map->layout_map_count);
- dev_warn(&h->pdev->dev,
- "DEBUG: Using physical disk map index %d from mirror group %d\n",
- map_index, offload_to_mirror);
dev->offload_to_mirror = offload_to_mirror;
/* Avoid direct use of dev->offload_to_mirror within this
* function since multiple threads might simultaneously
dev->scsi3addr);
}
-static int hpsa_scsi_queue_command_lck(struct scsi_cmnd *cmd,
- void (*done)(struct scsi_cmnd *))
+/*
+ * Running in struct Scsi_Host->host_lock less mode using LLD internal
+ * struct ctlr_info *h->lock w/ spin_lock_irqsave() protection.
+ */
+static int hpsa_scsi_queue_command(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
{
struct ctlr_info *h;
struct hpsa_scsi_dev_t *dev;
dev = cmd->device->hostdata;
if (!dev) {
cmd->result = DID_NO_CONNECT << 16;
- done(cmd);
+ cmd->scsi_done(cmd);
return 0;
}
memcpy(scsi3addr, dev->scsi3addr, sizeof(scsi3addr));
if (unlikely(lockup_detected(h))) {
cmd->result = DID_ERROR << 16;
- done(cmd);
+ cmd->scsi_done(cmd);
return 0;
}
c = cmd_alloc(h);
}
/* Fill in the command list header */
-
- cmd->scsi_done = done; /* save this for use by completion code */
-
/* save c in case we have to abort it */
cmd->host_scribble = (unsigned char *) c;
c->Header.ReplyQueue = 0; /* unused in simple mode */
memcpy(&c->Header.LUN.LunAddrBytes[0], &scsi3addr[0], 8);
- c->Header.Tag.lower = (c->cmdindex << DIRECT_LOOKUP_SHIFT);
- c->Header.Tag.lower |= DIRECT_LOOKUP_BIT;
+ c->Header.tag = cpu_to_le64((c->cmdindex << DIRECT_LOOKUP_SHIFT) |
+ DIRECT_LOOKUP_BIT);
/* Fill in the request block... */
BUG_ON(cmd->cmd_len > sizeof(c->Request.CDB));
c->Request.CDBLen = cmd->cmd_len;
memcpy(c->Request.CDB, cmd->cmnd, cmd->cmd_len);
- c->Request.Type.Type = TYPE_CMD;
- c->Request.Type.Attribute = ATTR_SIMPLE;
switch (cmd->sc_data_direction) {
case DMA_TO_DEVICE:
- c->Request.Type.Direction = XFER_WRITE;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(TYPE_CMD, ATTR_SIMPLE, XFER_WRITE);
break;
case DMA_FROM_DEVICE:
- c->Request.Type.Direction = XFER_READ;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(TYPE_CMD, ATTR_SIMPLE, XFER_READ);
break;
case DMA_NONE:
- c->Request.Type.Direction = XFER_NONE;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(TYPE_CMD, ATTR_SIMPLE, XFER_NONE);
break;
case DMA_BIDIRECTIONAL:
/* This can happen if a buggy application does a scsi passthru
* ../scsi/scsi_ioctl.c:scsi_ioctl_send_command() )
*/
- c->Request.Type.Direction = XFER_RSVD;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(TYPE_CMD, ATTR_SIMPLE, XFER_RSVD);
/* This is technically wrong, and hpsa controllers should
* reject it with CMD_INVALID, which is the most correct
* response, but non-fibre backends appear to let it
return 0;
}
-static DEF_SCSI_QCMD(hpsa_scsi_queue_command)
-
static int do_not_scan_if_controller_locked_up(struct ctlr_info *h)
{
unsigned long flags;
return finished;
}
-static int hpsa_change_queue_depth(struct scsi_device *sdev,
- int qdepth, int reason)
-{
- struct ctlr_info *h = sdev_to_hba(sdev);
-
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -ENOTSUPP;
-
- if (qdepth < 1)
- qdepth = 1;
- else
- if (qdepth > h->nr_cmds)
- qdepth = h->nr_cmds;
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
- return sdev->queue_depth;
-}
-
static void hpsa_unregister_scsi(struct ctlr_info *h)
{
/* we are being forcibly unloaded, and may not refuse. */
if (c->cmd_type == CMD_IOACCEL1) {
struct io_accel1_cmd *cm1 = (struct io_accel1_cmd *)
&h->ioaccel_cmd_pool[c->cmdindex];
- *tagupper = cm1->Tag.upper;
- *taglower = cm1->Tag.lower;
+ *tagupper = (u32) (cm1->tag >> 32);
+ *taglower = (u32) (cm1->tag & 0x0ffffffffULL);
return;
}
if (c->cmd_type == CMD_IOACCEL2) {
*taglower = cm2->Tag;
return;
}
- *tagupper = c->Header.Tag.upper;
- *taglower = c->Header.Tag.lower;
+ *tagupper = (u32) (c->Header.tag >> 32);
+ *taglower = (u32) (c->Header.tag & 0x0ffffffffULL);
}
-
static int hpsa_send_abort(struct ctlr_info *h, unsigned char *scsi3addr,
struct CommandList *abort, int swizzle)
{
struct CommandList *c = NULL; /* ptr into cmpQ */
if (!find)
- return 0;
+ return NULL;
spin_lock_irqsave(&h->lock, flags);
list_for_each_entry(c, queue_head, list) {
if (c->scsi_cmd == NULL) /* e.g.: passthru ioctl */
spin_lock_irqsave(&h->lock, flags);
list_for_each_entry(c, queue_head, list) {
- if (memcmp(&c->Header.Tag, tag, 8) != 0)
+ if (memcmp(&c->Header.tag, tag, 8) != 0)
continue;
spin_unlock_irqrestore(&h->lock, flags);
return c;
int i;
union u64bit temp64;
dma_addr_t cmd_dma_handle, err_dma_handle;
- unsigned long flags;
+ int loopcount;
+
+ /* There is some *extremely* small but non-zero chance that that
+ * multiple threads could get in here, and one thread could
+ * be scanning through the list of bits looking for a free
+ * one, but the free ones are always behind him, and other
+ * threads sneak in behind him and eat them before he can
+ * get to them, so that while there is always a free one, a
+ * very unlucky thread might be starved anyway, never able to
+ * beat the other threads. In reality, this happens so
+ * infrequently as to be indistinguishable from never.
+ */
- spin_lock_irqsave(&h->lock, flags);
+ loopcount = 0;
do {
i = find_first_zero_bit(h->cmd_pool_bits, h->nr_cmds);
- if (i == h->nr_cmds) {
- spin_unlock_irqrestore(&h->lock, flags);
- return NULL;
- }
- } while (test_and_set_bit
- (i & (BITS_PER_LONG - 1),
- h->cmd_pool_bits + (i / BITS_PER_LONG)) != 0);
- spin_unlock_irqrestore(&h->lock, flags);
+ if (i == h->nr_cmds)
+ i = 0;
+ loopcount++;
+ } while (test_and_set_bit(i & (BITS_PER_LONG - 1),
+ h->cmd_pool_bits + (i / BITS_PER_LONG)) != 0 &&
+ loopcount < 10);
+
+ /* Thread got starved? We do not expect this to ever happen. */
+ if (loopcount >= 10)
+ return NULL;
c = h->cmd_pool + i;
memset(c, 0, sizeof(*c));
INIT_LIST_HEAD(&c->list);
c->busaddr = (u32) cmd_dma_handle;
temp64.val = (u64) err_dma_handle;
- c->ErrDesc.Addr.lower = temp64.val32.lower;
- c->ErrDesc.Addr.upper = temp64.val32.upper;
- c->ErrDesc.Len = sizeof(*c->err_info);
+ c->ErrDesc.Addr = cpu_to_le64(err_dma_handle);
+ c->ErrDesc.Len = cpu_to_le32(sizeof(*c->err_info));
c->h = h;
return c;
static struct CommandList *cmd_special_alloc(struct ctlr_info *h)
{
struct CommandList *c;
- union u64bit temp64;
dma_addr_t cmd_dma_handle, err_dma_handle;
c = pci_zalloc_consistent(h->pdev, sizeof(*c), &cmd_dma_handle);
INIT_LIST_HEAD(&c->list);
c->busaddr = (u32) cmd_dma_handle;
- temp64.val = (u64) err_dma_handle;
- c->ErrDesc.Addr.lower = temp64.val32.lower;
- c->ErrDesc.Addr.upper = temp64.val32.upper;
- c->ErrDesc.Len = sizeof(*c->err_info);
+ c->ErrDesc.Addr = cpu_to_le64(err_dma_handle);
+ c->ErrDesc.Len = cpu_to_le32(sizeof(*c->err_info));
c->h = h;
return c;
static void cmd_free(struct ctlr_info *h, struct CommandList *c)
{
int i;
- unsigned long flags;
i = c - h->cmd_pool;
- spin_lock_irqsave(&h->lock, flags);
clear_bit(i & (BITS_PER_LONG - 1),
h->cmd_pool_bits + (i / BITS_PER_LONG));
- spin_unlock_irqrestore(&h->lock, flags);
}
static void cmd_special_free(struct ctlr_info *h, struct CommandList *c)
{
- union u64bit temp64;
-
- temp64.val32.lower = c->ErrDesc.Addr.lower;
- temp64.val32.upper = c->ErrDesc.Addr.upper;
pci_free_consistent(h->pdev, sizeof(*c->err_info),
- c->err_info, (dma_addr_t) temp64.val);
+ c->err_info,
+ (dma_addr_t) le64_to_cpu(c->ErrDesc.Addr));
pci_free_consistent(h->pdev, sizeof(*c),
c, (dma_addr_t) (c->busaddr & DIRECT_LOOKUP_MASK));
}
#ifdef CONFIG_COMPAT
-static int hpsa_ioctl32_passthru(struct scsi_device *dev, int cmd, void *arg)
+static int hpsa_ioctl32_passthru(struct scsi_device *dev, int cmd,
+ void __user *arg)
{
IOCTL32_Command_struct __user *arg32 =
(IOCTL32_Command_struct __user *) arg;
if (err)
return -EFAULT;
- err = hpsa_ioctl(dev, CCISS_PASSTHRU, (void *)p);
+ err = hpsa_ioctl(dev, CCISS_PASSTHRU, p);
if (err)
return err;
err |= copy_in_user(&arg32->error_info, &p->error_info,
}
static int hpsa_ioctl32_big_passthru(struct scsi_device *dev,
- int cmd, void *arg)
+ int cmd, void __user *arg)
{
BIG_IOCTL32_Command_struct __user *arg32 =
(BIG_IOCTL32_Command_struct __user *) arg;
if (err)
return -EFAULT;
- err = hpsa_ioctl(dev, CCISS_BIG_PASSTHRU, (void *)p);
+ err = hpsa_ioctl(dev, CCISS_BIG_PASSTHRU, p);
if (err)
return err;
err |= copy_in_user(&arg32->error_info, &p->error_info,
return err;
}
-static int hpsa_compat_ioctl(struct scsi_device *dev, int cmd, void *arg)
+static int hpsa_compat_ioctl(struct scsi_device *dev, int cmd, void __user *arg)
{
switch (cmd) {
case CCISS_GETPCIINFO:
IOCTL_Command_struct iocommand;
struct CommandList *c;
char *buff = NULL;
- union u64bit temp64;
+ u64 temp64;
int rc = 0;
if (!argp)
c->Header.ReplyQueue = 0; /* unused in simple mode */
if (iocommand.buf_size > 0) { /* buffer to fill */
c->Header.SGList = 1;
- c->Header.SGTotal = 1;
+ c->Header.SGTotal = cpu_to_le16(1);
} else { /* no buffers to fill */
c->Header.SGList = 0;
- c->Header.SGTotal = 0;
+ c->Header.SGTotal = cpu_to_le16(0);
}
memcpy(&c->Header.LUN, &iocommand.LUN_info, sizeof(c->Header.LUN));
/* use the kernel address the cmd block for tag */
- c->Header.Tag.lower = c->busaddr;
+ c->Header.tag = c->busaddr;
/* Fill in Request block */
memcpy(&c->Request, &iocommand.Request,
/* Fill in the scatter gather information */
if (iocommand.buf_size > 0) {
- temp64.val = pci_map_single(h->pdev, buff,
+ temp64 = pci_map_single(h->pdev, buff,
iocommand.buf_size, PCI_DMA_BIDIRECTIONAL);
- if (dma_mapping_error(&h->pdev->dev, temp64.val)) {
- c->SG[0].Addr.lower = 0;
- c->SG[0].Addr.upper = 0;
- c->SG[0].Len = 0;
+ if (dma_mapping_error(&h->pdev->dev, (dma_addr_t) temp64)) {
+ c->SG[0].Addr = cpu_to_le64(0);
+ c->SG[0].Len = cpu_to_le32(0);
rc = -ENOMEM;
goto out;
}
- c->SG[0].Addr.lower = temp64.val32.lower;
- c->SG[0].Addr.upper = temp64.val32.upper;
- c->SG[0].Len = iocommand.buf_size;
- c->SG[0].Ext = HPSA_SG_LAST; /* we are not chaining*/
+ c->SG[0].Addr = cpu_to_le64(temp64);
+ c->SG[0].Len = cpu_to_le32(iocommand.buf_size);
+ c->SG[0].Ext = cpu_to_le32(HPSA_SG_LAST); /* not chaining */
}
hpsa_scsi_do_simple_cmd_core_if_no_lockup(h, c);
if (iocommand.buf_size > 0)
struct CommandList *c;
unsigned char **buff = NULL;
int *buff_size = NULL;
- union u64bit temp64;
+ u64 temp64;
BYTE sg_used = 0;
int status = 0;
int i;
}
c->cmd_type = CMD_IOCTL_PEND;
c->Header.ReplyQueue = 0;
- c->Header.SGList = c->Header.SGTotal = sg_used;
+ c->Header.SGList = (u8) sg_used;
+ c->Header.SGTotal = cpu_to_le16(sg_used);
memcpy(&c->Header.LUN, &ioc->LUN_info, sizeof(c->Header.LUN));
- c->Header.Tag.lower = c->busaddr;
+ c->Header.tag = c->busaddr;
memcpy(&c->Request, &ioc->Request, sizeof(c->Request));
if (ioc->buf_size > 0) {
int i;
for (i = 0; i < sg_used; i++) {
- temp64.val = pci_map_single(h->pdev, buff[i],
+ temp64 = pci_map_single(h->pdev, buff[i],
buff_size[i], PCI_DMA_BIDIRECTIONAL);
- if (dma_mapping_error(&h->pdev->dev, temp64.val)) {
- c->SG[i].Addr.lower = 0;
- c->SG[i].Addr.upper = 0;
- c->SG[i].Len = 0;
+ if (dma_mapping_error(&h->pdev->dev,
+ (dma_addr_t) temp64)) {
+ c->SG[i].Addr = cpu_to_le64(0);
+ c->SG[i].Len = cpu_to_le32(0);
hpsa_pci_unmap(h->pdev, c, i,
PCI_DMA_BIDIRECTIONAL);
status = -ENOMEM;
goto cleanup0;
}
- c->SG[i].Addr.lower = temp64.val32.lower;
- c->SG[i].Addr.upper = temp64.val32.upper;
- c->SG[i].Len = buff_size[i];
- c->SG[i].Ext = i < sg_used - 1 ? 0 : HPSA_SG_LAST;
+ c->SG[i].Addr = cpu_to_le64(temp64);
+ c->SG[i].Len = cpu_to_le32(buff_size[i]);
+ c->SG[i].Ext = cpu_to_le32(0);
}
+ c->SG[--i].Ext = cpu_to_le32(HPSA_SG_LAST);
}
hpsa_scsi_do_simple_cmd_core_if_no_lockup(h, c);
if (sg_used)
/*
* ioctl
*/
-static int hpsa_ioctl(struct scsi_device *dev, int cmd, void *arg)
+static int hpsa_ioctl(struct scsi_device *dev, int cmd, void __user *arg)
{
struct ctlr_info *h;
void __user *argp = (void __user *)arg;
{
int pci_dir = XFER_NONE;
struct CommandList *a; /* for commands to be aborted */
+ u32 tupper, tlower;
c->cmd_type = CMD_IOCTL_PEND;
c->Header.ReplyQueue = 0;
if (buff != NULL && size > 0) {
c->Header.SGList = 1;
- c->Header.SGTotal = 1;
+ c->Header.SGTotal = cpu_to_le16(1);
} else {
c->Header.SGList = 0;
- c->Header.SGTotal = 0;
+ c->Header.SGTotal = cpu_to_le16(0);
}
- c->Header.Tag.lower = c->busaddr;
+ c->Header.tag = c->busaddr;
memcpy(c->Header.LUN.LunAddrBytes, scsi3addr, 8);
- c->Request.Type.Type = cmd_type;
if (cmd_type == TYPE_CMD) {
switch (cmd) {
case HPSA_INQUIRY:
c->Request.CDB[2] = (page_code & 0xff);
}
c->Request.CDBLen = 6;
- c->Request.Type.Attribute = ATTR_SIMPLE;
- c->Request.Type.Direction = XFER_READ;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_READ);
c->Request.Timeout = 0;
c->Request.CDB[0] = HPSA_INQUIRY;
c->Request.CDB[4] = size & 0xFF;
mode = 00 target = 0. Nothing to write.
*/
c->Request.CDBLen = 12;
- c->Request.Type.Attribute = ATTR_SIMPLE;
- c->Request.Type.Direction = XFER_READ;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_READ);
c->Request.Timeout = 0;
c->Request.CDB[0] = cmd;
c->Request.CDB[6] = (size >> 24) & 0xFF; /* MSB */
break;
case HPSA_CACHE_FLUSH:
c->Request.CDBLen = 12;
- c->Request.Type.Attribute = ATTR_SIMPLE;
- c->Request.Type.Direction = XFER_WRITE;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(cmd_type,
+ ATTR_SIMPLE, XFER_WRITE);
c->Request.Timeout = 0;
c->Request.CDB[0] = BMIC_WRITE;
c->Request.CDB[6] = BMIC_CACHE_FLUSH;
break;
case TEST_UNIT_READY:
c->Request.CDBLen = 6;
- c->Request.Type.Attribute = ATTR_SIMPLE;
- c->Request.Type.Direction = XFER_NONE;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_NONE);
c->Request.Timeout = 0;
break;
case HPSA_GET_RAID_MAP:
c->Request.CDBLen = 12;
- c->Request.Type.Attribute = ATTR_SIMPLE;
- c->Request.Type.Direction = XFER_READ;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_READ);
c->Request.Timeout = 0;
c->Request.CDB[0] = HPSA_CISS_READ;
c->Request.CDB[1] = cmd;
break;
case BMIC_SENSE_CONTROLLER_PARAMETERS:
c->Request.CDBLen = 10;
- c->Request.Type.Attribute = ATTR_SIMPLE;
- c->Request.Type.Direction = XFER_READ;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_READ);
c->Request.Timeout = 0;
c->Request.CDB[0] = BMIC_READ;
c->Request.CDB[6] = BMIC_SENSE_CONTROLLER_PARAMETERS;
case HPSA_DEVICE_RESET_MSG:
c->Request.CDBLen = 16;
- c->Request.Type.Type = 1; /* It is a MSG not a CMD */
- c->Request.Type.Attribute = ATTR_SIMPLE;
- c->Request.Type.Direction = XFER_NONE;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_NONE);
c->Request.Timeout = 0; /* Don't time out */
memset(&c->Request.CDB[0], 0, sizeof(c->Request.CDB));
c->Request.CDB[0] = cmd;
break;
case HPSA_ABORT_MSG:
a = buff; /* point to command to be aborted */
- dev_dbg(&h->pdev->dev, "Abort Tag:0x%08x:%08x using request Tag:0x%08x:%08x\n",
- a->Header.Tag.upper, a->Header.Tag.lower,
- c->Header.Tag.upper, c->Header.Tag.lower);
+ dev_dbg(&h->pdev->dev, "Abort Tag:0x%016llx using request Tag:0x%016llx",
+ a->Header.tag, c->Header.tag);
+ tlower = (u32) (a->Header.tag >> 32);
+ tupper = (u32) (a->Header.tag & 0x0ffffffffULL);
c->Request.CDBLen = 16;
- c->Request.Type.Type = TYPE_MSG;
- c->Request.Type.Attribute = ATTR_SIMPLE;
- c->Request.Type.Direction = XFER_WRITE;
+ c->Request.type_attr_dir =
+ TYPE_ATTR_DIR(cmd_type,
+ ATTR_SIMPLE, XFER_WRITE);
c->Request.Timeout = 0; /* Don't time out */
c->Request.CDB[0] = HPSA_TASK_MANAGEMENT;
c->Request.CDB[1] = HPSA_TMF_ABORT_TASK;
c->Request.CDB[2] = 0x00; /* reserved */
c->Request.CDB[3] = 0x00; /* reserved */
/* Tag to abort goes in CDB[4]-CDB[11] */
- c->Request.CDB[4] = a->Header.Tag.lower & 0xFF;
- c->Request.CDB[5] = (a->Header.Tag.lower >> 8) & 0xFF;
- c->Request.CDB[6] = (a->Header.Tag.lower >> 16) & 0xFF;
- c->Request.CDB[7] = (a->Header.Tag.lower >> 24) & 0xFF;
- c->Request.CDB[8] = a->Header.Tag.upper & 0xFF;
- c->Request.CDB[9] = (a->Header.Tag.upper >> 8) & 0xFF;
- c->Request.CDB[10] = (a->Header.Tag.upper >> 16) & 0xFF;
- c->Request.CDB[11] = (a->Header.Tag.upper >> 24) & 0xFF;
+ c->Request.CDB[4] = tlower & 0xFF;
+ c->Request.CDB[5] = (tlower >> 8) & 0xFF;
+ c->Request.CDB[6] = (tlower >> 16) & 0xFF;
+ c->Request.CDB[7] = (tlower >> 24) & 0xFF;
+ c->Request.CDB[8] = tupper & 0xFF;
+ c->Request.CDB[9] = (tupper >> 8) & 0xFF;
+ c->Request.CDB[10] = (tupper >> 16) & 0xFF;
+ c->Request.CDB[11] = (tupper >> 24) & 0xFF;
c->Request.CDB[12] = 0x00; /* reserved */
c->Request.CDB[13] = 0x00; /* reserved */
c->Request.CDB[14] = 0x00; /* reserved */
BUG();
}
- switch (c->Request.Type.Direction) {
+ switch (GET_DIR(c->Request.type_attr_dir)) {
case XFER_READ:
pci_dir = PCI_DMA_FROMDEVICE;
break;
/* Put job onto the completed Q */
addQ(&h->cmpQ, c);
-
- /* Must increment commands_outstanding before unlocking
- * and submitting to avoid race checking for fifo full
- * condition.
- */
- h->commands_outstanding++;
-
- /* Tell the controller execute command */
+ atomic_inc(&h->commands_outstanding);
spin_unlock_irqrestore(&h->lock, *flags);
+ /* Tell the controller execute command */
h->access.submit_command(h, c);
spin_lock_irqsave(&h->lock, *flags);
}
unsigned long flags;
int io_may_be_stalled = 0;
struct ctlr_info *h = c->h;
+ int count;
spin_lock_irqsave(&h->lock, flags);
removeQ(c);
* want to get in a cycle where we call start_io every time
* through here.
*/
- if (unlikely(h->fifo_recently_full) &&
- h->commands_outstanding < 5)
- io_may_be_stalled = 1;
-
+ count = atomic_read(&h->commands_outstanding);
spin_unlock_irqrestore(&h->lock, flags);
+ if (unlikely(h->fifo_recently_full) && count < 5)
+ io_may_be_stalled = 1;
dial_up_lockup_detection_on_fw_flash_complete(c->h, c);
if (likely(c->cmd_type == CMD_IOACCEL1 || c->cmd_type == CMD_SCSI
cmd->CommandHeader.ReplyQueue = 0;
cmd->CommandHeader.SGList = 0;
- cmd->CommandHeader.SGTotal = 0;
- cmd->CommandHeader.Tag.lower = paddr32;
- cmd->CommandHeader.Tag.upper = 0;
+ cmd->CommandHeader.SGTotal = cpu_to_le16(0);
+ cmd->CommandHeader.tag = paddr32;
memset(&cmd->CommandHeader.LUN.LunAddrBytes, 0, 8);
cmd->Request.CDBLen = 16;
- cmd->Request.Type.Type = TYPE_MSG;
- cmd->Request.Type.Attribute = ATTR_HEADOFQUEUE;
- cmd->Request.Type.Direction = XFER_NONE;
+ cmd->Request.type_attr_dir =
+ TYPE_ATTR_DIR(TYPE_MSG, ATTR_HEADOFQUEUE, XFER_NONE);
cmd->Request.Timeout = 0; /* Don't time out */
cmd->Request.CDB[0] = opcode;
cmd->Request.CDB[1] = type;
memset(&cmd->Request.CDB[2], 0, 14); /* rest of the CDB is reserved */
- cmd->ErrorDescriptor.Addr.lower = paddr32 + sizeof(*cmd);
- cmd->ErrorDescriptor.Addr.upper = 0;
- cmd->ErrorDescriptor.Len = sizeof(struct ErrorInfo);
+ cmd->ErrorDescriptor.Addr =
+ cpu_to_le64((paddr32 + sizeof(*cmd)));
+ cmd->ErrorDescriptor.Len = cpu_to_le32(sizeof(struct ErrorInfo));
writel(paddr32, vaddr + SA5_REQUEST_PORT_OFFSET);
#define hpsa_noop(p) hpsa_message(p, 3, 0)
static int hpsa_controller_hard_reset(struct pci_dev *pdev,
- void * __iomem vaddr, u32 use_doorbell)
+ void __iomem *vaddr, u32 use_doorbell)
{
u16 pmcsr;
int pos;
* the io functions.
* This is for debug only.
*/
-static void print_cfg_table(struct device *dev, struct CfgTable *tb)
+static void print_cfg_table(struct device *dev, struct CfgTable __iomem *tb)
{
#ifdef HPSA_DEBUG
int i;
h->max_cmd_sg_entries = 31;
if (h->maxsgentries > 512) {
h->max_cmd_sg_entries = 32;
- h->chainsize = h->maxsgentries - h->max_cmd_sg_entries + 1;
+ h->chainsize = h->maxsgentries - h->max_cmd_sg_entries;
h->maxsgentries--; /* save one for chain pointer */
} else {
- h->maxsgentries = 31; /* default to traditional values */
h->chainsize = 0;
+ h->maxsgentries = 31; /* default to traditional values */
}
/* Find out what task management functions are supported and cache */
return err;
}
- /* Enable bus mastering (pci_disable_device may disable this) */
- pci_set_master(h->pdev);
-
err = pci_request_regions(h->pdev, HPSA);
if (err) {
dev_err(&h->pdev->dev,
"cannot obtain PCI resources, aborting\n");
return err;
}
+
+ pci_set_master(h->pdev);
+
hpsa_interrupt_mode(h);
err = hpsa_pci_find_memory_BAR(h->pdev, &h->paddr);
if (err)
dev_warn(&pdev->dev, "failed to enable device.\n");
return -ENODEV;
}
+
pci_set_master(pdev);
+
/* Reset the controller with a PCI power-cycle or via doorbell */
rc = hpsa_kdump_hard_reset_controller(pdev);
cp->host_context_flags = IOACCEL1_HCFLAGS_CISS_FORMAT;
cp->timeout_sec = 0;
cp->ReplyQueue = 0;
- cp->Tag.lower = (i << DIRECT_LOOKUP_SHIFT) |
- DIRECT_LOOKUP_BIT;
- cp->Tag.upper = 0;
- cp->host_addr.lower =
- (u32) (h->ioaccel_cmd_pool_dhandle +
+ cp->tag =
+ cpu_to_le64((i << DIRECT_LOOKUP_SHIFT) |
+ DIRECT_LOOKUP_BIT);
+ cp->host_addr =
+ cpu_to_le64(h->ioaccel_cmd_pool_dhandle +
(i * sizeof(struct io_accel1_cmd)));
- cp->host_addr.upper = 0;
}
} else if (trans_support & CFGTBL_Trans_io_accel2) {
u64 cfg_offset, cfg_base_addr_index;
VERIFY_OFFSET(timeout_sec, 0x62);
VERIFY_OFFSET(ReplyQueue, 0x64);
VERIFY_OFFSET(reserved9, 0x65);
- VERIFY_OFFSET(Tag, 0x68);
+ VERIFY_OFFSET(tag, 0x68);
VERIFY_OFFSET(host_addr, 0x70);
VERIFY_OFFSET(CISS_LUN, 0x78);
VERIFY_OFFSET(SG, 0x78 + 8);
struct CfgTable __iomem *cfgtable;
int interrupts_enabled;
int max_commands;
- int commands_outstanding;
+ atomic_t commands_outstanding;
# define PERF_MODE_INT 0
# define DOORBELL_INT 1
# define SIMPLE_MODE_INT 2
*/
u32 trans_support;
u32 trans_offset;
- struct TransTable_struct *transtable;
+ struct TransTable_struct __iomem *transtable;
unsigned long transMethod;
/* cap concurrent passthrus at some reasonable maximum */
u32 *blockFetchTable;
u32 *ioaccel1_blockFetchTable;
u32 *ioaccel2_blockFetchTable;
- u32 *ioaccel2_bft2_regs;
+ u32 __iomem *ioaccel2_bft2_regs;
unsigned char *hba_inquiry_data;
u32 driver_support;
u32 fw_support;
u64 last_heartbeat_timestamp;
u32 heartbeat_sample_interval;
atomic_t firmware_flash_in_progress;
- u32 *lockup_detected;
+ u32 __percpu *lockup_detected;
struct delayed_work monitor_ctlr_work;
int remove_in_progress;
u32 fifo_recently_full;
static unsigned long SA5_performant_completed(struct ctlr_info *h, u8 q)
{
struct reply_queue_buffer *rq = &h->reply_queue[q];
- unsigned long flags, register_value = FIFO_EMPTY;
+ unsigned long register_value = FIFO_EMPTY;
/* msi auto clears the interrupt pending bit. */
if (!(h->msi_vector || h->msix_vector)) {
if ((rq->head[rq->current_entry] & 1) == rq->wraparound) {
register_value = rq->head[rq->current_entry];
rq->current_entry++;
- spin_lock_irqsave(&h->lock, flags);
- h->commands_outstanding--;
- spin_unlock_irqrestore(&h->lock, flags);
+ atomic_dec(&h->commands_outstanding);
} else {
register_value = FIFO_EMPTY;
}
*/
static unsigned long SA5_fifo_full(struct ctlr_info *h)
{
- if (h->commands_outstanding >= h->max_commands)
- return 1;
- else
- return 0;
-
+ return atomic_read(&h->commands_outstanding) >= h->max_commands;
}
/*
* returns value read from hardware.
{
unsigned long register_value
= readl(h->vaddr + SA5_REPLY_PORT_OFFSET);
- unsigned long flags;
- if (register_value != FIFO_EMPTY) {
- spin_lock_irqsave(&h->lock, flags);
- h->commands_outstanding--;
- spin_unlock_irqrestore(&h->lock, flags);
- }
+ if (register_value != FIFO_EMPTY)
+ atomic_dec(&h->commands_outstanding);
#ifdef HPSA_DEBUG
if (register_value != FIFO_EMPTY)
{
u64 register_value;
struct reply_queue_buffer *rq = &h->reply_queue[q];
- unsigned long flags;
BUG_ON(q >= h->nreply_queues);
wmb();
writel((q << 24) | rq->current_entry, h->vaddr +
IOACCEL_MODE1_CONSUMER_INDEX);
- spin_lock_irqsave(&h->lock, flags);
- h->commands_outstanding--;
- spin_unlock_irqrestore(&h->lock, flags);
+ atomic_dec(&h->commands_outstanding);
}
return (unsigned long) register_value;
}
u8 LUNListLength[4];
u8 extended_response_flag;
u8 reserved[3];
- struct ext_report_lun_entry LUN[HPSA_MAX_LUN];
+ struct ext_report_lun_entry LUN[HPSA_MAX_PHYS_LUN];
};
struct SenseSubsystem_info {
u8 ReplyQueue;
u8 SGList;
u16 SGTotal;
- struct vals32 Tag;
+ u64 tag;
union LUNAddr LUN;
};
struct RequestBlock {
u8 CDBLen;
- struct {
- u8 Type:3;
- u8 Attribute:3;
- u8 Direction:2;
- } Type;
+ /*
+ * type_attr_dir:
+ * type: low 3 bits
+ * attr: middle 3 bits
+ * dir: high 2 bits
+ */
+ u8 type_attr_dir;
+#define TYPE_ATTR_DIR(t, a, d) ((((d) & 0x03) << 6) |\
+ (((a) & 0x07) << 3) |\
+ ((t) & 0x07))
+#define GET_TYPE(tad) ((tad) & 0x07)
+#define GET_ATTR(tad) (((tad) >> 3) & 0x07)
+#define GET_DIR(tad) (((tad) >> 6) & 0x03)
u16 Timeout;
u8 CDB[16];
};
struct ErrDescriptor {
- struct vals32 Addr;
+ u64 Addr;
u32 Len;
};
struct SGDescriptor {
- struct vals32 Addr;
+ u64 Addr;
u32 Len;
u32 Ext;
};
u16 timeout_sec; /* 0x62 - 0x63 */
u8 ReplyQueue; /* 0x64 */
u8 reserved9[3]; /* 0x65 - 0x67 */
- struct vals32 Tag; /* 0x68 - 0x6F */
- struct vals32 host_addr; /* 0x70 - 0x77 */
+ u64 tag; /* 0x68 - 0x6F */
+ u64 host_addr; /* 0x70 - 0x77 */
u8 CISS_LUN[8]; /* 0x78 - 0x7F */
struct SGDescriptor SG[IOACCEL1_MAXSGENTRIES];
} __aligned(IOACCEL1_COMMANDLIST_ALIGNMENT);
u8 reserved1; /* byte 3 Reserved */
u32 it_nexus; /* SCSI I-T Nexus */
u8 lun_id[8]; /* LUN ID for TMF request */
- struct vals32 Tag; /* cciss tag associated w/ request */
- struct vals32 abort_tag;/* cciss tag of SCSI cmd or task to abort */
+ u64 tag; /* cciss tag associated w/ request */
+ u64 abort_tag; /* cciss tag of SCSI cmd or task to abort */
u64 error_ptr; /* Error Pointer */
u32 error_len; /* Error Length */
};
}
static int hptiop_adjust_disk_queue_depth(struct scsi_device *sdev,
- int queue_depth, int reason)
+ int queue_depth)
{
struct hptiop_hba *hba = (struct hptiop_hba *)sdev->host->hostdata;
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
-
if (queue_depth > hba->max_requests)
queue_depth = hba->max_requests;
- scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, queue_depth);
- return queue_depth;
+ return scsi_change_queue_depth(sdev, queue_depth);
}
static ssize_t hptiop_show_version(struct device *dev,
int_to_scsilun(cmnd->device->lun, &vfc_cmd->iu.lun);
memcpy(vfc_cmd->iu.cdb, cmnd->cmnd, cmnd->cmd_len);
- if (scsi_populate_tag_msg(cmnd, tag)) {
- vfc_cmd->task_tag = cpu_to_be64(tag[1]);
- switch (tag[0]) {
- case MSG_SIMPLE_TAG:
- vfc_cmd->iu.pri_task_attr = IBMVFC_SIMPLE_TASK;
- break;
- case MSG_HEAD_TAG:
- vfc_cmd->iu.pri_task_attr = IBMVFC_HEAD_OF_QUEUE;
- break;
- case MSG_ORDERED_TAG:
- vfc_cmd->iu.pri_task_attr = IBMVFC_ORDERED_TASK;
- break;
- };
+ if (cmnd->flags & SCMD_TAGGED) {
+ vfc_cmd->task_tag = cpu_to_be64(cmnd->tag);
+ vfc_cmd->iu.pri_task_attr = IBMVFC_SIMPLE_TASK;
}
if (likely(!(rc = ibmvfc_map_sg_data(cmnd, evt, vfc_cmd, vhost->dev))))
spin_lock_irqsave(shost->host_lock, flags);
if (sdev->type == TYPE_DISK)
sdev->allow_restart = 1;
-
- if (sdev->tagged_supported) {
- scsi_set_tag_type(sdev, MSG_SIMPLE_TAG);
- scsi_activate_tcq(sdev, sdev->queue_depth);
- } else
- scsi_deactivate_tcq(sdev, sdev->queue_depth);
spin_unlock_irqrestore(shost->host_lock, flags);
return 0;
}
* Return value:
* actual depth set
**/
-static int ibmvfc_change_queue_depth(struct scsi_device *sdev, int qdepth,
- int reason)
+static int ibmvfc_change_queue_depth(struct scsi_device *sdev, int qdepth)
{
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
-
if (qdepth > IBMVFC_MAX_CMDS_PER_LUN)
qdepth = IBMVFC_MAX_CMDS_PER_LUN;
- scsi_adjust_queue_depth(sdev, 0, qdepth);
- return sdev->queue_depth;
-}
-
-/**
- * ibmvfc_change_queue_type - Change the device's queue type
- * @sdev: scsi device struct
- * @tag_type: type of tags to use
- *
- * Return value:
- * actual queue type set
- **/
-static int ibmvfc_change_queue_type(struct scsi_device *sdev, int tag_type)
-{
- if (sdev->tagged_supported) {
- scsi_set_tag_type(sdev, tag_type);
-
- if (tag_type)
- scsi_activate_tcq(sdev, sdev->queue_depth);
- else
- scsi_deactivate_tcq(sdev, sdev->queue_depth);
- } else
- tag_type = 0;
-
- return tag_type;
+ return scsi_change_queue_depth(sdev, qdepth);
}
static ssize_t ibmvfc_show_host_partition_name(struct device *dev,
.target_alloc = ibmvfc_target_alloc,
.scan_finished = ibmvfc_scan_finished,
.change_queue_depth = ibmvfc_change_queue_depth,
- .change_queue_type = ibmvfc_change_queue_type,
+ .change_queue_type = scsi_change_queue_type,
.cmd_per_lun = 16,
.can_queue = IBMVFC_MAX_REQUESTS_DEFAULT,
.this_id = -1,
.max_sectors = IBMVFC_MAX_SECTORS,
.use_clustering = ENABLE_CLUSTERING,
.shost_attrs = ibmvfc_attrs,
+ .use_blk_tags = 1,
+ .track_queue_depth = 1,
};
/**
blk_queue_rq_timeout(sdev->request_queue, 120 * HZ);
}
spin_unlock_irqrestore(shost->host_lock, lock_flags);
- scsi_adjust_queue_depth(sdev, 0, shost->cmd_per_lun);
return 0;
}
* Return value:
* actual depth set
**/
-static int ibmvscsi_change_queue_depth(struct scsi_device *sdev, int qdepth,
- int reason)
+static int ibmvscsi_change_queue_depth(struct scsi_device *sdev, int qdepth)
{
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
-
if (qdepth > IBMVSCSI_MAX_CMDS_PER_LUN)
qdepth = IBMVSCSI_MAX_CMDS_PER_LUN;
-
- scsi_adjust_queue_depth(sdev, 0, qdepth);
- return sdev->queue_depth;
+ return scsi_change_queue_depth(sdev, qdepth);
}
/* ------------------------------------------------------------
return -EIO;
}
- sglist->num_dma_sg = pci_map_sg(ioa_cfg->pdev, sglist->scatterlist,
- sglist->num_sg, DMA_TO_DEVICE);
+ sglist->num_dma_sg = dma_map_sg(&ioa_cfg->pdev->dev,
+ sglist->scatterlist, sglist->num_sg,
+ DMA_TO_DEVICE);
if (!sglist->num_dma_sg) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
* Return value:
* actual depth set
**/
-static int ipr_change_queue_depth(struct scsi_device *sdev, int qdepth,
- int reason)
+static int ipr_change_queue_depth(struct scsi_device *sdev, int qdepth)
{
struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)sdev->host->hostdata;
struct ipr_resource_entry *res;
unsigned long lock_flags = 0;
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
-
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
res = (struct ipr_resource_entry *)sdev->hostdata;
qdepth = IPR_MAX_CMD_PER_ATA_LUN;
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
+ scsi_change_queue_depth(sdev, qdepth);
return sdev->queue_depth;
}
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
res = (struct ipr_resource_entry *)sdev->hostdata;
-
- if (res) {
- if (ipr_is_gscsi(res) && sdev->tagged_supported) {
- /*
- * We don't bother quiescing the device here since the
- * adapter firmware does it for us.
- */
- scsi_set_tag_type(sdev, tag_type);
-
- if (tag_type)
- scsi_activate_tcq(sdev, sdev->queue_depth);
- else
- scsi_deactivate_tcq(sdev, sdev->queue_depth);
- } else
- tag_type = 0;
- } else
+ if (res && ipr_is_gscsi(res))
+ tag_type = scsi_change_queue_type(sdev, tag_type);
+ else
tag_type = 0;
-
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
return tag_type;
}
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
if (ap) {
- scsi_adjust_queue_depth(sdev, 0, IPR_MAX_CMD_PER_ATA_LUN);
+ scsi_change_queue_depth(sdev, IPR_MAX_CMD_PER_ATA_LUN);
ata_sas_slave_configure(sdev, ap);
- } else
- scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun);
+ }
+
if (ioa_cfg->sis64)
sdev_printk(KERN_INFO, sdev, "Resource path: %s\n",
ipr_format_res_path(ioa_cfg,
nseg = scsi_dma_map(scsi_cmd);
if (nseg < 0) {
if (printk_ratelimit())
- dev_err(&ioa_cfg->pdev->dev, "pci_map_sg failed!\n");
+ dev_err(&ioa_cfg->pdev->dev, "scsi_dma_map failed!\n");
return -1;
}
nseg = scsi_dma_map(scsi_cmd);
if (nseg < 0) {
- dev_err(&ioa_cfg->pdev->dev, "pci_map_sg failed!\n");
+ dev_err(&ioa_cfg->pdev->dev, "scsi_dma_map failed!\n");
return -1;
}
return 0;
}
-/**
- * ipr_get_task_attributes - Translate SPI Q-Tag to task attributes
- * @scsi_cmd: scsi command struct
- *
- * Return value:
- * task attributes
- **/
-static u8 ipr_get_task_attributes(struct scsi_cmnd *scsi_cmd)
-{
- u8 tag[2];
- u8 rc = IPR_FLAGS_LO_UNTAGGED_TASK;
-
- if (scsi_populate_tag_msg(scsi_cmd, tag)) {
- switch (tag[0]) {
- case MSG_SIMPLE_TAG:
- rc = IPR_FLAGS_LO_SIMPLE_TASK;
- break;
- case MSG_HEAD_TAG:
- rc = IPR_FLAGS_LO_HEAD_OF_Q_TASK;
- break;
- case MSG_ORDERED_TAG:
- rc = IPR_FLAGS_LO_ORDERED_TASK;
- break;
- };
- }
-
- return rc;
-}
-
/**
* ipr_erp_done - Process completion of ERP for a device
* @ipr_cmd: ipr command struct
ioarcb->cmd_pkt.flags_lo |= IPR_FLAGS_LO_DELAY_AFTER_RST;
}
ioarcb->cmd_pkt.flags_lo |= IPR_FLAGS_LO_ALIGNED_BFR;
- ioarcb->cmd_pkt.flags_lo |= ipr_get_task_attributes(scsi_cmd);
+ if (scsi_cmd->flags & SCMD_TAGGED)
+ ioarcb->cmd_pkt.flags_lo |= IPR_FLAGS_LO_SIMPLE_TASK;
+ else
+ ioarcb->cmd_pkt.flags_lo |= IPR_FLAGS_LO_UNTAGGED_TASK;
}
if (scsi_cmd->cmnd[0] >= 0xC0 &&
.sdev_attrs = ipr_dev_attrs,
.proc_name = IPR_NAME,
.no_write_same = 1,
+ .use_blk_tags = 1,
};
/**
struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg;
struct ipr_sglist *sglist = ioa_cfg->ucode_sglist;
- pci_unmap_sg(ioa_cfg->pdev, sglist->scatterlist,
+ dma_unmap_sg(&ioa_cfg->pdev->dev, sglist->scatterlist,
sglist->num_sg, DMA_TO_DEVICE);
ipr_cmd->job_step = ipr_reset_alert;
for (i = 0; i < IPR_NUM_CMD_BLKS; i++) {
if (ioa_cfg->ipr_cmnd_list[i])
- pci_pool_free(ioa_cfg->ipr_cmd_pool,
+ dma_pool_free(ioa_cfg->ipr_cmd_pool,
ioa_cfg->ipr_cmnd_list[i],
ioa_cfg->ipr_cmnd_list_dma[i]);
}
if (ioa_cfg->ipr_cmd_pool)
- pci_pool_destroy(ioa_cfg->ipr_cmd_pool);
+ dma_pool_destroy(ioa_cfg->ipr_cmd_pool);
kfree(ioa_cfg->ipr_cmnd_list);
kfree(ioa_cfg->ipr_cmnd_list_dma);
int i;
kfree(ioa_cfg->res_entries);
- pci_free_consistent(ioa_cfg->pdev, sizeof(struct ipr_misc_cbs),
- ioa_cfg->vpd_cbs, ioa_cfg->vpd_cbs_dma);
+ dma_free_coherent(&ioa_cfg->pdev->dev, sizeof(struct ipr_misc_cbs),
+ ioa_cfg->vpd_cbs, ioa_cfg->vpd_cbs_dma);
ipr_free_cmd_blks(ioa_cfg);
for (i = 0; i < ioa_cfg->hrrq_num; i++)
- pci_free_consistent(ioa_cfg->pdev,
- sizeof(u32) * ioa_cfg->hrrq[i].size,
- ioa_cfg->hrrq[i].host_rrq,
- ioa_cfg->hrrq[i].host_rrq_dma);
+ dma_free_coherent(&ioa_cfg->pdev->dev,
+ sizeof(u32) * ioa_cfg->hrrq[i].size,
+ ioa_cfg->hrrq[i].host_rrq,
+ ioa_cfg->hrrq[i].host_rrq_dma);
- pci_free_consistent(ioa_cfg->pdev, ioa_cfg->cfg_table_size,
- ioa_cfg->u.cfg_table,
- ioa_cfg->cfg_table_dma);
+ dma_free_coherent(&ioa_cfg->pdev->dev, ioa_cfg->cfg_table_size,
+ ioa_cfg->u.cfg_table, ioa_cfg->cfg_table_dma);
for (i = 0; i < IPR_NUM_HCAMS; i++) {
- pci_free_consistent(ioa_cfg->pdev,
- sizeof(struct ipr_hostrcb),
- ioa_cfg->hostrcb[i],
- ioa_cfg->hostrcb_dma[i]);
+ dma_free_coherent(&ioa_cfg->pdev->dev,
+ sizeof(struct ipr_hostrcb),
+ ioa_cfg->hostrcb[i],
+ ioa_cfg->hostrcb_dma[i]);
}
ipr_free_dump(ioa_cfg);
dma_addr_t dma_addr;
int i, entries_each_hrrq, hrrq_id = 0;
- ioa_cfg->ipr_cmd_pool = pci_pool_create(IPR_NAME, ioa_cfg->pdev,
+ ioa_cfg->ipr_cmd_pool = dma_pool_create(IPR_NAME, &ioa_cfg->pdev->dev,
sizeof(struct ipr_cmnd), 512, 0);
if (!ioa_cfg->ipr_cmd_pool)
}
for (i = 0; i < IPR_NUM_CMD_BLKS; i++) {
- ipr_cmd = pci_pool_alloc(ioa_cfg->ipr_cmd_pool, GFP_KERNEL, &dma_addr);
+ ipr_cmd = dma_pool_alloc(ioa_cfg->ipr_cmd_pool, GFP_KERNEL, &dma_addr);
if (!ipr_cmd) {
ipr_free_cmd_blks(ioa_cfg);
ioa_cfg->res_entries[i].ioa_cfg = ioa_cfg;
}
- ioa_cfg->vpd_cbs = pci_alloc_consistent(ioa_cfg->pdev,
- sizeof(struct ipr_misc_cbs),
- &ioa_cfg->vpd_cbs_dma);
+ ioa_cfg->vpd_cbs = dma_alloc_coherent(&pdev->dev,
+ sizeof(struct ipr_misc_cbs),
+ &ioa_cfg->vpd_cbs_dma,
+ GFP_KERNEL);
if (!ioa_cfg->vpd_cbs)
goto out_free_res_entries;
goto out_free_vpd_cbs;
for (i = 0; i < ioa_cfg->hrrq_num; i++) {
- ioa_cfg->hrrq[i].host_rrq = pci_alloc_consistent(ioa_cfg->pdev,
+ ioa_cfg->hrrq[i].host_rrq = dma_alloc_coherent(&pdev->dev,
sizeof(u32) * ioa_cfg->hrrq[i].size,
- &ioa_cfg->hrrq[i].host_rrq_dma);
+ &ioa_cfg->hrrq[i].host_rrq_dma,
+ GFP_KERNEL);
if (!ioa_cfg->hrrq[i].host_rrq) {
while (--i > 0)
- pci_free_consistent(pdev,
+ dma_free_coherent(&pdev->dev,
sizeof(u32) * ioa_cfg->hrrq[i].size,
ioa_cfg->hrrq[i].host_rrq,
ioa_cfg->hrrq[i].host_rrq_dma);
ioa_cfg->hrrq[i].ioa_cfg = ioa_cfg;
}
- ioa_cfg->u.cfg_table = pci_alloc_consistent(ioa_cfg->pdev,
- ioa_cfg->cfg_table_size,
- &ioa_cfg->cfg_table_dma);
+ ioa_cfg->u.cfg_table = dma_alloc_coherent(&pdev->dev,
+ ioa_cfg->cfg_table_size,
+ &ioa_cfg->cfg_table_dma,
+ GFP_KERNEL);
if (!ioa_cfg->u.cfg_table)
goto out_free_host_rrq;
for (i = 0; i < IPR_NUM_HCAMS; i++) {
- ioa_cfg->hostrcb[i] = pci_alloc_consistent(ioa_cfg->pdev,
- sizeof(struct ipr_hostrcb),
- &ioa_cfg->hostrcb_dma[i]);
+ ioa_cfg->hostrcb[i] = dma_alloc_coherent(&pdev->dev,
+ sizeof(struct ipr_hostrcb),
+ &ioa_cfg->hostrcb_dma[i],
+ GFP_KERNEL);
if (!ioa_cfg->hostrcb[i])
goto out_free_hostrcb_dma;
out_free_hostrcb_dma:
while (i-- > 0) {
- pci_free_consistent(pdev, sizeof(struct ipr_hostrcb),
- ioa_cfg->hostrcb[i],
- ioa_cfg->hostrcb_dma[i]);
+ dma_free_coherent(&pdev->dev, sizeof(struct ipr_hostrcb),
+ ioa_cfg->hostrcb[i],
+ ioa_cfg->hostrcb_dma[i]);
}
- pci_free_consistent(pdev, ioa_cfg->cfg_table_size,
- ioa_cfg->u.cfg_table,
- ioa_cfg->cfg_table_dma);
+ dma_free_coherent(&pdev->dev, ioa_cfg->cfg_table_size,
+ ioa_cfg->u.cfg_table, ioa_cfg->cfg_table_dma);
out_free_host_rrq:
for (i = 0; i < ioa_cfg->hrrq_num; i++) {
- pci_free_consistent(pdev,
- sizeof(u32) * ioa_cfg->hrrq[i].size,
- ioa_cfg->hrrq[i].host_rrq,
- ioa_cfg->hrrq[i].host_rrq_dma);
+ dma_free_coherent(&pdev->dev,
+ sizeof(u32) * ioa_cfg->hrrq[i].size,
+ ioa_cfg->hrrq[i].host_rrq,
+ ioa_cfg->hrrq[i].host_rrq_dma);
}
out_ipr_free_cmd_blocks:
ipr_free_cmd_blks(ioa_cfg);
out_free_vpd_cbs:
- pci_free_consistent(pdev, sizeof(struct ipr_misc_cbs),
- ioa_cfg->vpd_cbs, ioa_cfg->vpd_cbs_dma);
+ dma_free_coherent(&pdev->dev, sizeof(struct ipr_misc_cbs),
+ ioa_cfg->vpd_cbs, ioa_cfg->vpd_cbs_dma);
out_free_res_entries:
kfree(ioa_cfg->res_entries);
goto out;
ipr_init_regs(ioa_cfg);
if (ioa_cfg->sis64) {
- rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
+ rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (rc < 0) {
- dev_dbg(&pdev->dev, "Failed to set 64 bit PCI DMA mask\n");
- rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ dev_dbg(&pdev->dev, "Failed to set 64 bit DMA mask\n");
+ rc = dma_set_mask_and_coherent(&pdev->dev,
+ DMA_BIT_MASK(32));
}
} else
- rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
if (rc < 0) {
- dev_err(&pdev->dev, "Failed to set PCI DMA mask\n");
+ dev_err(&pdev->dev, "Failed to set DMA mask\n");
goto cleanup_nomem;
}
struct ipr_misc_cbs *vpd_cbs;
dma_addr_t vpd_cbs_dma;
- struct pci_pool *ipr_cmd_pool;
+ struct dma_pool *ipr_cmd_pool;
struct ipr_cmnd *reset_cmd;
int (*reset) (struct ipr_cmnd *);
min = ha->max_cmds / 2;
if (ha->enq->ucLogDriveCount <= 2)
min = ha->max_cmds - 1;
- scsi_adjust_queue_depth(SDptr, MSG_ORDERED_TAG, min);
+ scsi_change_queue_depth(SDptr, min);
}
SDptr->skip_ms_page_8 = 1;
.target_destroy = sas_target_destroy,
.ioctl = sas_ioctl,
.shost_attrs = isci_host_attrs,
+ .use_blk_tags = 1,
+ .track_queue_depth = 1,
};
static struct sas_domain_function_template isci_transport_ops = {
sas_ha->sas_port = sas_ports;
sas_ha->num_phys = SCI_MAX_PHYS;
- sas_ha->lldd_queue_size = ISCI_CAN_QUEUE_VAL;
- sas_ha->lldd_max_execute_num = 1;
sas_ha->strict_wide_ports = 1;
sas_register_ha(sas_ha);
* functions. This function is called by libsas to send a task down to
* hardware.
* @task: This parameter specifies the SAS task to send.
- * @num: This parameter specifies the number of tasks to queue.
* @gfp_flags: This parameter specifies the context of this call.
*
* status, zero indicates success.
*/
-int isci_task_execute_task(struct sas_task *task, int num, gfp_t gfp_flags)
+int isci_task_execute_task(struct sas_task *task, gfp_t gfp_flags)
{
struct isci_host *ihost = dev_to_ihost(task->dev);
struct isci_remote_device *idev;
unsigned long flags;
+ enum sci_status status = SCI_FAILURE;
bool io_ready;
u16 tag;
- dev_dbg(&ihost->pdev->dev, "%s: num=%d\n", __func__, num);
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+ idev = isci_lookup_device(task->dev);
+ io_ready = isci_device_io_ready(idev, task);
+ tag = isci_alloc_tag(ihost);
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
- for_each_sas_task(num, task) {
- enum sci_status status = SCI_FAILURE;
+ dev_dbg(&ihost->pdev->dev,
+ "task: %p, dev: %p idev: %p:%#lx cmd = %p\n",
+ task, task->dev, idev, idev ? idev->flags : 0,
+ task->uldd_task);
- spin_lock_irqsave(&ihost->scic_lock, flags);
- idev = isci_lookup_device(task->dev);
- io_ready = isci_device_io_ready(idev, task);
- tag = isci_alloc_tag(ihost);
- spin_unlock_irqrestore(&ihost->scic_lock, flags);
+ if (!idev) {
+ isci_task_refuse(ihost, task, SAS_TASK_UNDELIVERED,
+ SAS_DEVICE_UNKNOWN);
+ } else if (!io_ready || tag == SCI_CONTROLLER_INVALID_IO_TAG) {
+ /* Indicate QUEUE_FULL so that the scsi midlayer
+ * retries.
+ */
+ isci_task_refuse(ihost, task, SAS_TASK_COMPLETE,
+ SAS_QUEUE_FULL);
+ } else {
+ /* There is a device and it's ready for I/O. */
+ spin_lock_irqsave(&task->task_state_lock, flags);
- dev_dbg(&ihost->pdev->dev,
- "task: %p, num: %d dev: %p idev: %p:%#lx cmd = %p\n",
- task, num, task->dev, idev, idev ? idev->flags : 0,
- task->uldd_task);
-
- if (!idev) {
- isci_task_refuse(ihost, task, SAS_TASK_UNDELIVERED,
- SAS_DEVICE_UNKNOWN);
- } else if (!io_ready || tag == SCI_CONTROLLER_INVALID_IO_TAG) {
- /* Indicate QUEUE_FULL so that the scsi midlayer
- * retries.
- */
- isci_task_refuse(ihost, task, SAS_TASK_COMPLETE,
- SAS_QUEUE_FULL);
+ if (task->task_state_flags & SAS_TASK_STATE_ABORTED) {
+ /* The I/O was aborted. */
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+
+ isci_task_refuse(ihost, task,
+ SAS_TASK_UNDELIVERED,
+ SAM_STAT_TASK_ABORTED);
} else {
- /* There is a device and it's ready for I/O. */
- spin_lock_irqsave(&task->task_state_lock, flags);
-
- if (task->task_state_flags & SAS_TASK_STATE_ABORTED) {
- /* The I/O was aborted. */
- spin_unlock_irqrestore(&task->task_state_lock,
- flags);
-
- isci_task_refuse(ihost, task,
- SAS_TASK_UNDELIVERED,
- SAM_STAT_TASK_ABORTED);
- } else {
- task->task_state_flags |= SAS_TASK_AT_INITIATOR;
+ task->task_state_flags |= SAS_TASK_AT_INITIATOR;
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+
+ /* build and send the request. */
+ status = isci_request_execute(ihost, idev, task, tag);
+
+ if (status != SCI_SUCCESS) {
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ /* Did not really start this command. */
+ task->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
spin_unlock_irqrestore(&task->task_state_lock, flags);
- /* build and send the request. */
- status = isci_request_execute(ihost, idev, task, tag);
-
- if (status != SCI_SUCCESS) {
-
- spin_lock_irqsave(&task->task_state_lock, flags);
- /* Did not really start this command. */
- task->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
- spin_unlock_irqrestore(&task->task_state_lock, flags);
-
- if (test_bit(IDEV_GONE, &idev->flags)) {
-
- /* Indicate that the device
- * is gone.
- */
- isci_task_refuse(ihost, task,
- SAS_TASK_UNDELIVERED,
- SAS_DEVICE_UNKNOWN);
- } else {
- /* Indicate QUEUE_FULL so that
- * the scsi midlayer retries.
- * If the request failed for
- * remote device reasons, it
- * gets returned as
- * SAS_TASK_UNDELIVERED next
- * time through.
- */
- isci_task_refuse(ihost, task,
- SAS_TASK_COMPLETE,
- SAS_QUEUE_FULL);
- }
+ if (test_bit(IDEV_GONE, &idev->flags)) {
+ /* Indicate that the device
+ * is gone.
+ */
+ isci_task_refuse(ihost, task,
+ SAS_TASK_UNDELIVERED,
+ SAS_DEVICE_UNKNOWN);
+ } else {
+ /* Indicate QUEUE_FULL so that
+ * the scsi midlayer retries.
+ * If the request failed for
+ * remote device reasons, it
+ * gets returned as
+ * SAS_TASK_UNDELIVERED next
+ * time through.
+ */
+ isci_task_refuse(ihost, task,
+ SAS_TASK_COMPLETE,
+ SAS_QUEUE_FULL);
}
}
}
- if (status != SCI_SUCCESS && tag != SCI_CONTROLLER_INVALID_IO_TAG) {
- spin_lock_irqsave(&ihost->scic_lock, flags);
- /* command never hit the device, so just free
- * the tci and skip the sequence increment
- */
- isci_tci_free(ihost, ISCI_TAG_TCI(tag));
- spin_unlock_irqrestore(&ihost->scic_lock, flags);
- }
- isci_put_device(idev);
}
+
+ if (status != SCI_SUCCESS && tag != SCI_CONTROLLER_INVALID_IO_TAG) {
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+ /* command never hit the device, so just free
+ * the tci and skip the sequence increment
+ */
+ isci_tci_free(ihost, ISCI_TAG_TCI(tag));
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+ }
+
+ isci_put_device(idev);
return 0;
}
int isci_task_execute_task(
struct sas_task *task,
- int num,
gfp_t gfp_flags);
int isci_task_abort_task(
.module = THIS_MODULE,
.name = "iSCSI Initiator over TCP/IP",
.queuecommand = iscsi_queuecommand,
- .change_queue_depth = iscsi_change_queue_depth,
+ .change_queue_depth = scsi_change_queue_depth,
.can_queue = ISCSI_DEF_XMIT_CMDS_MAX - 1,
.sg_tablesize = 4096,
.max_sectors = 0xFFFF,
.target_alloc = iscsi_target_alloc,
.proc_name = "iscsi_tcp",
.this_id = -1,
+ .track_queue_depth = 1,
};
static struct iscsi_transport iscsi_sw_tcp_transport = {
if (!rport || fc_remote_port_chkready(rport))
return -ENXIO;
- if (sdev->tagged_supported)
- scsi_activate_tcq(sdev, FC_FCP_DFLT_QUEUE_DEPTH);
- else
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev),
- FC_FCP_DFLT_QUEUE_DEPTH);
-
+ scsi_change_queue_depth(sdev, FC_FCP_DFLT_QUEUE_DEPTH);
return 0;
}
EXPORT_SYMBOL(fc_slave_alloc);
-/**
- * fc_change_queue_depth() - Change a device's queue depth
- * @sdev: The SCSI device whose queue depth is to change
- * @qdepth: The new queue depth
- * @reason: The resason for the change
- */
-int fc_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
-{
- switch (reason) {
- case SCSI_QDEPTH_DEFAULT:
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
- break;
- case SCSI_QDEPTH_QFULL:
- scsi_track_queue_full(sdev, qdepth);
- break;
- case SCSI_QDEPTH_RAMP_UP:
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
- break;
- default:
- return -EOPNOTSUPP;
- }
- return sdev->queue_depth;
-}
-EXPORT_SYMBOL(fc_change_queue_depth);
-
-/**
- * fc_change_queue_type() - Change a device's queue type
- * @sdev: The SCSI device whose queue depth is to change
- * @tag_type: Identifier for queue type
- */
-int fc_change_queue_type(struct scsi_device *sdev, int tag_type)
-{
- if (sdev->tagged_supported) {
- scsi_set_tag_type(sdev, tag_type);
- if (tag_type)
- scsi_activate_tcq(sdev, sdev->queue_depth);
- else
- scsi_deactivate_tcq(sdev, sdev->queue_depth);
- } else
- tag_type = 0;
-
- return tag_type;
-}
-EXPORT_SYMBOL(fc_change_queue_type);
-
/**
* fc_fcp_destory() - Tear down the FCP layer for a given local port
* @lport: The local port that no longer needs the FCP layer
}
EXPORT_SYMBOL_GPL(iscsi_queuecommand);
-int iscsi_change_queue_depth(struct scsi_device *sdev, int depth, int reason)
-{
- switch (reason) {
- case SCSI_QDEPTH_DEFAULT:
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
- break;
- case SCSI_QDEPTH_QFULL:
- scsi_track_queue_full(sdev, depth);
- break;
- case SCSI_QDEPTH_RAMP_UP:
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
- break;
- default:
- return -EOPNOTSUPP;
- }
- return sdev->queue_depth;
-}
-EXPORT_SYMBOL_GPL(iscsi_change_queue_depth);
-
int iscsi_target_alloc(struct scsi_target *starget)
{
struct iscsi_cls_session *cls_session = starget_to_session(starget);
spin_unlock_irqrestore(ap->lock, flags);
qc_already_gone:
- list_del_init(&task->list);
sas_free_task(task);
}
if (qc->scsicmd)
ASSIGN_SAS_TASK(qc->scsicmd, task);
- if (sas_ha->lldd_max_execute_num < 2)
- ret = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC);
- else
- ret = sas_queue_up(task);
-
- /* Examine */
+ ret = i->dft->lldd_execute_task(task, GFP_ATOMIC);
if (ret) {
SAS_DPRINTK("lldd_execute_task returned: %d\n", ret);
return;
out:
- list_del_init(&task->list);
sas_free_task(task);
}
task->slow_task->timer.expires = jiffies + SMP_TIMEOUT*HZ;
add_timer(&task->slow_task->timer);
- res = i->dft->lldd_execute_task(task, 1, GFP_KERNEL);
+ res = i->dft->lldd_execute_task(task, GFP_KERNEL);
if (res) {
del_timer(&task->slow_task->timer);
struct sas_task *task = kmem_cache_zalloc(sas_task_cache, flags);
if (task) {
- INIT_LIST_HEAD(&task->list);
spin_lock_init(&task->task_state_lock);
task->task_state_flags = SAS_TASK_STATE_PENDING;
}
void sas_free_task(struct sas_task *task)
{
if (task) {
- BUG_ON(!list_empty(&task->list));
kfree(task->slow_task);
kmem_cache_free(sas_task_cache, task);
}
spin_lock_init(&sas_ha->phy_port_lock);
sas_hash_addr(sas_ha->hashed_sas_addr, sas_ha->sas_addr);
- if (sas_ha->lldd_queue_size == 0)
- sas_ha->lldd_queue_size = 1;
- else if (sas_ha->lldd_queue_size == -1)
- sas_ha->lldd_queue_size = 128; /* Sanity */
-
set_bit(SAS_HA_REGISTERED, &sas_ha->state);
spin_lock_init(&sas_ha->lock);
mutex_init(&sas_ha->drain_mutex);
goto Undo_ports;
}
- if (sas_ha->lldd_max_execute_num > 1) {
- error = sas_init_queue(sas_ha);
- if (error) {
- printk(KERN_NOTICE "couldn't start queue thread:%d, "
- "running in direct mode\n", error);
- sas_ha->lldd_max_execute_num = 1;
- }
- }
-
INIT_LIST_HEAD(&sas_ha->eh_done_q);
INIT_LIST_HEAD(&sas_ha->eh_ata_q);
__sas_drain_work(sas_ha);
mutex_unlock(&sas_ha->drain_mutex);
- if (sas_ha->lldd_max_execute_num > 1) {
- sas_shutdown_queue(sas_ha);
- sas_ha->lldd_max_execute_num = 1;
- }
-
return 0;
}
enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *);
-int sas_init_queue(struct sas_ha_struct *sas_ha);
int sas_init_events(struct sas_ha_struct *sas_ha);
-void sas_shutdown_queue(struct sas_ha_struct *sas_ha);
void sas_disable_revalidation(struct sas_ha_struct *ha);
void sas_enable_revalidation(struct sas_ha_struct *ha);
void __sas_drain_work(struct sas_ha_struct *ha);
sc->result = (hs << 16) | stat;
ASSIGN_SAS_TASK(sc, NULL);
- list_del_init(&task->list);
sas_free_task(task);
}
if (unlikely(!sc)) {
SAS_DPRINTK("task_done called with non existing SCSI cmnd!\n");
- list_del_init(&task->list);
sas_free_task(task);
return;
}
return task;
}
-int sas_queue_up(struct sas_task *task)
-{
- struct sas_ha_struct *sas_ha = task->dev->port->ha;
- struct scsi_core *core = &sas_ha->core;
- unsigned long flags;
- LIST_HEAD(list);
-
- spin_lock_irqsave(&core->task_queue_lock, flags);
- if (sas_ha->lldd_queue_size < core->task_queue_size + 1) {
- spin_unlock_irqrestore(&core->task_queue_lock, flags);
- return -SAS_QUEUE_FULL;
- }
- list_add_tail(&task->list, &core->task_queue);
- core->task_queue_size += 1;
- spin_unlock_irqrestore(&core->task_queue_lock, flags);
- wake_up_process(core->queue_thread);
-
- return 0;
-}
-
int sas_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
{
struct sas_internal *i = to_sas_internal(host->transportt);
struct domain_device *dev = cmd_to_domain_dev(cmd);
- struct sas_ha_struct *sas_ha = dev->port->ha;
struct sas_task *task;
int res = 0;
if (!task)
return SCSI_MLQUEUE_HOST_BUSY;
- /* Queue up, Direct Mode or Task Collector Mode. */
- if (sas_ha->lldd_max_execute_num < 2)
- res = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC);
- else
- res = sas_queue_up(task);
-
+ res = i->dft->lldd_execute_task(task, GFP_ATOMIC);
if (res)
goto out_free_task;
return 0;
TASK_IS_DONE,
TASK_IS_ABORTED,
TASK_IS_AT_LU,
- TASK_IS_NOT_AT_HA,
TASK_IS_NOT_AT_LU,
TASK_ABORT_FAILED,
};
static enum task_disposition sas_scsi_find_task(struct sas_task *task)
{
- struct sas_ha_struct *ha = task->dev->port->ha;
unsigned long flags;
int i, res;
struct sas_internal *si =
to_sas_internal(task->dev->port->ha->core.shost->transportt);
- if (ha->lldd_max_execute_num > 1) {
- struct scsi_core *core = &ha->core;
- struct sas_task *t, *n;
-
- mutex_lock(&core->task_queue_flush);
- spin_lock_irqsave(&core->task_queue_lock, flags);
- list_for_each_entry_safe(t, n, &core->task_queue, list)
- if (task == t) {
- list_del_init(&t->list);
- break;
- }
- spin_unlock_irqrestore(&core->task_queue_lock, flags);
- mutex_unlock(&core->task_queue_flush);
-
- if (task == t)
- return TASK_IS_NOT_AT_HA;
- }
-
for (i = 0; i < 5; i++) {
SAS_DPRINTK("%s: aborting task 0x%p\n", __func__, task);
res = si->dft->lldd_abort_task(task);
cmd->eh_eflags = 0;
switch (res) {
- case TASK_IS_NOT_AT_HA:
- SAS_DPRINTK("%s: task 0x%p is not at ha: %s\n",
- __func__, task,
- cmd->retries ? "retry" : "aborted");
- if (cmd->retries)
- cmd->retries--;
- sas_eh_finish_cmd(cmd);
- continue;
case TASK_IS_DONE:
SAS_DPRINTK("%s: task 0x%p is done\n", __func__,
task);
scsi_eh_ready_devs(shost, &eh_work_q, &ha->eh_done_q);
out:
- if (ha->lldd_max_execute_num > 1)
- wake_up_process(ha->core.queue_thread);
-
sas_eh_handle_resets(shost);
/* now link into libata eh --- if we have any ata devices */
sas_read_port_mode_page(scsi_dev);
if (scsi_dev->tagged_supported) {
- scsi_set_tag_type(scsi_dev, MSG_SIMPLE_TAG);
- scsi_activate_tcq(scsi_dev, SAS_DEF_QD);
+ scsi_change_queue_depth(scsi_dev, SAS_DEF_QD);
} else {
SAS_DPRINTK("device %llx, LUN %llx doesn't support "
"TCQ\n", SAS_ADDR(dev->sas_addr),
scsi_dev->lun);
- scsi_dev->tagged_supported = 0;
- scsi_set_tag_type(scsi_dev, 0);
- scsi_deactivate_tcq(scsi_dev, 1);
+ scsi_change_queue_depth(scsi_dev, 1);
}
scsi_dev->allow_restart = 1;
return 0;
}
-int sas_change_queue_depth(struct scsi_device *sdev, int depth, int reason)
+int sas_change_queue_depth(struct scsi_device *sdev, int depth)
{
struct domain_device *dev = sdev_to_domain_dev(sdev);
if (dev_is_sata(dev))
- return __ata_change_queue_depth(dev->sata_dev.ap, sdev, depth,
- reason);
-
- switch (reason) {
- case SCSI_QDEPTH_DEFAULT:
- case SCSI_QDEPTH_RAMP_UP:
- if (!sdev->tagged_supported)
- depth = 1;
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
- break;
- case SCSI_QDEPTH_QFULL:
- scsi_track_queue_full(sdev, depth);
- break;
- default:
- return -EOPNOTSUPP;
- }
+ return __ata_change_queue_depth(dev->sata_dev.ap, sdev, depth);
- return depth;
+ if (!sdev->tagged_supported)
+ depth = 1;
+ return scsi_change_queue_depth(sdev, depth);
}
-int sas_change_queue_type(struct scsi_device *scsi_dev, int qt)
+int sas_change_queue_type(struct scsi_device *scsi_dev, int type)
{
- struct domain_device *dev = sdev_to_domain_dev(scsi_dev);
-
- if (dev_is_sata(dev))
+ if (dev_is_sata(sdev_to_domain_dev(scsi_dev)))
return -EINVAL;
-
- if (!scsi_dev->tagged_supported)
- return 0;
-
- scsi_deactivate_tcq(scsi_dev, 1);
-
- scsi_set_tag_type(scsi_dev, qt);
- scsi_activate_tcq(scsi_dev, scsi_dev->queue_depth);
-
- return qt;
+ return scsi_change_queue_type(scsi_dev, type);
}
int sas_bios_param(struct scsi_device *scsi_dev,
return 0;
}
-/* ---------- Task Collector Thread implementation ---------- */
-
-static void sas_queue(struct sas_ha_struct *sas_ha)
-{
- struct scsi_core *core = &sas_ha->core;
- unsigned long flags;
- LIST_HEAD(q);
- int can_queue;
- int res;
- struct sas_internal *i = to_sas_internal(core->shost->transportt);
-
- mutex_lock(&core->task_queue_flush);
- spin_lock_irqsave(&core->task_queue_lock, flags);
- while (!kthread_should_stop() &&
- !list_empty(&core->task_queue) &&
- !test_bit(SAS_HA_FROZEN, &sas_ha->state)) {
-
- can_queue = sas_ha->lldd_queue_size - core->task_queue_size;
- if (can_queue >= 0) {
- can_queue = core->task_queue_size;
- list_splice_init(&core->task_queue, &q);
- } else {
- struct list_head *a, *n;
-
- can_queue = sas_ha->lldd_queue_size;
- list_for_each_safe(a, n, &core->task_queue) {
- list_move_tail(a, &q);
- if (--can_queue == 0)
- break;
- }
- can_queue = sas_ha->lldd_queue_size;
- }
- core->task_queue_size -= can_queue;
- spin_unlock_irqrestore(&core->task_queue_lock, flags);
- {
- struct sas_task *task = list_entry(q.next,
- struct sas_task,
- list);
- list_del_init(&q);
- res = i->dft->lldd_execute_task(task, can_queue,
- GFP_KERNEL);
- if (unlikely(res))
- __list_add(&q, task->list.prev, &task->list);
- }
- spin_lock_irqsave(&core->task_queue_lock, flags);
- if (res) {
- list_splice_init(&q, &core->task_queue); /*at head*/
- core->task_queue_size += can_queue;
- }
- }
- spin_unlock_irqrestore(&core->task_queue_lock, flags);
- mutex_unlock(&core->task_queue_flush);
-}
-
-/**
- * sas_queue_thread -- The Task Collector thread
- * @_sas_ha: pointer to struct sas_ha
- */
-static int sas_queue_thread(void *_sas_ha)
-{
- struct sas_ha_struct *sas_ha = _sas_ha;
-
- while (1) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule();
- sas_queue(sas_ha);
- if (kthread_should_stop())
- break;
- }
-
- return 0;
-}
-
-int sas_init_queue(struct sas_ha_struct *sas_ha)
-{
- struct scsi_core *core = &sas_ha->core;
-
- spin_lock_init(&core->task_queue_lock);
- mutex_init(&core->task_queue_flush);
- core->task_queue_size = 0;
- INIT_LIST_HEAD(&core->task_queue);
-
- core->queue_thread = kthread_run(sas_queue_thread, sas_ha,
- "sas_queue_%d", core->shost->host_no);
- if (IS_ERR(core->queue_thread))
- return PTR_ERR(core->queue_thread);
- return 0;
-}
-
-void sas_shutdown_queue(struct sas_ha_struct *sas_ha)
-{
- unsigned long flags;
- struct scsi_core *core = &sas_ha->core;
- struct sas_task *task, *n;
-
- kthread_stop(core->queue_thread);
-
- if (!list_empty(&core->task_queue))
- SAS_DPRINTK("HA: %llx: scsi core task queue is NOT empty!?\n",
- SAS_ADDR(sas_ha->sas_addr));
-
- spin_lock_irqsave(&core->task_queue_lock, flags);
- list_for_each_entry_safe(task, n, &core->task_queue, list) {
- struct scsi_cmnd *cmd = task->uldd_task;
-
- list_del_init(&task->list);
-
- ASSIGN_SAS_TASK(cmd, NULL);
- sas_free_task(task);
- cmd->result = DID_ABORT << 16;
- cmd->scsi_done(cmd);
- }
- spin_unlock_irqrestore(&core->task_queue_lock, flags);
-}
-
/*
* Tell an upper layer that it needs to initiate an abort for a given task.
* This should only ever be called by an LLDD.
spin_unlock_irqrestore(shost->host_lock, flags);
}
-/**
- * lpfc_send_sdev_queuedepth_change_event - Posts a queuedepth change event
- * @phba: Pointer to HBA context object.
- * @vport: Pointer to vport object.
- * @ndlp: Pointer to FC node associated with the target.
- * @lun: Lun number of the scsi device.
- * @old_val: Old value of the queue depth.
- * @new_val: New value of the queue depth.
- *
- * This function sends an event to the mgmt application indicating
- * there is a change in the scsi device queue depth.
- **/
-static void
-lpfc_send_sdev_queuedepth_change_event(struct lpfc_hba *phba,
- struct lpfc_vport *vport,
- struct lpfc_nodelist *ndlp,
- uint64_t lun,
- uint32_t old_val,
- uint32_t new_val)
-{
- struct lpfc_fast_path_event *fast_path_evt;
- unsigned long flags;
-
- fast_path_evt = lpfc_alloc_fast_evt(phba);
- if (!fast_path_evt)
- return;
-
- fast_path_evt->un.queue_depth_evt.scsi_event.event_type =
- FC_REG_SCSI_EVENT;
- fast_path_evt->un.queue_depth_evt.scsi_event.subcategory =
- LPFC_EVENT_VARQUEDEPTH;
-
- /* Report all luns with change in queue depth */
- fast_path_evt->un.queue_depth_evt.scsi_event.lun = lun;
- if (ndlp && NLP_CHK_NODE_ACT(ndlp)) {
- memcpy(&fast_path_evt->un.queue_depth_evt.scsi_event.wwpn,
- &ndlp->nlp_portname, sizeof(struct lpfc_name));
- memcpy(&fast_path_evt->un.queue_depth_evt.scsi_event.wwnn,
- &ndlp->nlp_nodename, sizeof(struct lpfc_name));
- }
-
- fast_path_evt->un.queue_depth_evt.oldval = old_val;
- fast_path_evt->un.queue_depth_evt.newval = new_val;
- fast_path_evt->vport = vport;
-
- fast_path_evt->work_evt.evt = LPFC_EVT_FASTPATH_MGMT_EVT;
- spin_lock_irqsave(&phba->hbalock, flags);
- list_add_tail(&fast_path_evt->work_evt.evt_listp, &phba->work_list);
- spin_unlock_irqrestore(&phba->hbalock, flags);
- lpfc_worker_wake_up(phba);
-
- return;
-}
-
-/**
- * lpfc_change_queue_depth - Alter scsi device queue depth
- * @sdev: Pointer the scsi device on which to change the queue depth.
- * @qdepth: New queue depth to set the sdev to.
- * @reason: The reason for the queue depth change.
- *
- * This function is called by the midlayer and the LLD to alter the queue
- * depth for a scsi device. This function sets the queue depth to the new
- * value and sends an event out to log the queue depth change.
- **/
-static int
-lpfc_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
-{
- struct lpfc_vport *vport = (struct lpfc_vport *) sdev->host->hostdata;
- struct lpfc_hba *phba = vport->phba;
- struct lpfc_rport_data *rdata;
- unsigned long new_queue_depth, old_queue_depth;
-
- old_queue_depth = sdev->queue_depth;
-
- switch (reason) {
- case SCSI_QDEPTH_DEFAULT:
- /* change request from sysfs, fall through */
- case SCSI_QDEPTH_RAMP_UP:
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
- break;
- case SCSI_QDEPTH_QFULL:
- if (scsi_track_queue_full(sdev, qdepth) == 0)
- return sdev->queue_depth;
-
- lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
- "0711 detected queue full - lun queue "
- "depth adjusted to %d.\n", sdev->queue_depth);
- break;
- default:
- return -EOPNOTSUPP;
- }
-
- new_queue_depth = sdev->queue_depth;
- rdata = lpfc_rport_data_from_scsi_device(sdev);
- if (rdata)
- lpfc_send_sdev_queuedepth_change_event(phba, vport,
- rdata->pnode, sdev->lun,
- old_queue_depth,
- new_queue_depth);
- return sdev->queue_depth;
-}
-
-/**
- * lpfc_change_queue_type() - Change a device's scsi tag queuing type
- * @sdev: Pointer the scsi device whose queue depth is to change
- * @tag_type: Identifier for queue tag type
- */
-static int
-lpfc_change_queue_type(struct scsi_device *sdev, int tag_type)
-{
- if (sdev->tagged_supported) {
- scsi_set_tag_type(sdev, tag_type);
- if (tag_type)
- scsi_activate_tcq(sdev, sdev->queue_depth);
- else
- scsi_deactivate_tcq(sdev, sdev->queue_depth);
- } else
- tag_type = 0;
-
- return tag_type;
-}
-
/**
* lpfc_rampdown_queue_depth - Post RAMP_DOWN_QUEUE event to worker thread
* @phba: The Hba for which this call is being executed.
else
new_queue_depth = sdev->queue_depth -
new_queue_depth;
- lpfc_change_queue_depth(sdev, new_queue_depth,
- SCSI_QDEPTH_DEFAULT);
+ scsi_change_queue_depth(sdev, new_queue_depth);
}
}
lpfc_destroy_vport_work_array(phba, vports);
IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb;
struct lpfc_iocbq *piocbq = &(lpfc_cmd->cur_iocbq);
int datadir = scsi_cmnd->sc_data_direction;
- char tag[2];
uint8_t *ptr;
bool sli4;
uint32_t fcpdl;
memset(ptr, 0, (LPFC_FCP_CDB_LEN - scsi_cmnd->cmd_len));
}
- if (scsi_populate_tag_msg(scsi_cmnd, tag)) {
- switch (tag[0]) {
- case HEAD_OF_QUEUE_TAG:
- fcp_cmnd->fcpCntl1 = HEAD_OF_Q;
- break;
- case ORDERED_QUEUE_TAG:
- fcp_cmnd->fcpCntl1 = ORDERED_Q;
- break;
- default:
- fcp_cmnd->fcpCntl1 = SIMPLE_Q;
- break;
- }
- } else
- fcp_cmnd->fcpCntl1 = SIMPLE_Q;
+ fcp_cmnd->fcpCntl1 = SIMPLE_Q;
sli4 = (phba->sli_rev == LPFC_SLI_REV4);
piocbq->iocb.un.fcpi.fcpi_XRdy = 0;
struct lpfc_vport *vport = (struct lpfc_vport *) sdev->host->hostdata;
struct lpfc_hba *phba = vport->phba;
- if (sdev->tagged_supported)
- scsi_activate_tcq(sdev, vport->cfg_lun_queue_depth);
- else
- scsi_deactivate_tcq(sdev, vport->cfg_lun_queue_depth);
+ scsi_change_queue_depth(sdev, vport->cfg_lun_queue_depth);
if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) {
lpfc_sli_handle_fast_ring_event(phba,
.shost_attrs = lpfc_hba_attrs,
.max_sectors = 0xFFFF,
.vendor_id = LPFC_NL_VENDOR_ID,
- .change_queue_depth = lpfc_change_queue_depth,
- .change_queue_type = lpfc_change_queue_type,
+ .change_queue_depth = scsi_change_queue_depth,
+ .change_queue_type = scsi_change_queue_type,
+ .use_blk_tags = 1,
+ .track_queue_depth = 1,
};
struct scsi_host_template lpfc_vport_template = {
.use_clustering = ENABLE_CLUSTERING,
.shost_attrs = lpfc_vport_attrs,
.max_sectors = 0xFFFF,
- .change_queue_depth = lpfc_change_queue_depth,
- .change_queue_type = lpfc_change_queue_type,
+ .change_queue_depth = scsi_change_queue_depth,
+ .change_queue_type = scsi_change_queue_type,
+ .use_blk_tags = 1,
+ .track_queue_depth = 1,
};
* Generic Generic NCR5380 driver
*
* Copyright 1995, Russell King
- *
- * ALPHA RELEASE 1.
- *
- * For more information, please consult
- *
- * NCR 5380 Family
- * SCSI Protocol Controller
- * Databook
- *
- * NCR Microelectronics
- * 1635 Aeroplaza Drive
- * Colorado Springs, CO 80916
- * 1+ (719) 578-3400
- * 1+ (800) 334-5454
*/
#include <linux/types.h>
-#include <linux/stddef.h>
-#include <linux/ctype.h>
#include <linux/delay.h>
-
#include <linux/module.h>
-#include <linux/signal.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/blkdev.h>
#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <asm/hwtest.h>
#include <asm/io.h>
-#include <asm/irq.h>
-
-#include <asm/macintosh.h>
#include <asm/macints.h>
-#include <asm/mac_via.h>
+#include <asm/setup.h>
-#include "scsi.h"
#include <scsi/scsi_host.h>
-#include "mac_scsi.h"
-/* These control the behaviour of the generic 5380 core */
-#define AUTOSENSE
+/* Definitions for the core NCR5380 driver. */
+
#define PSEUDO_DMA
-#include "NCR5380.h"
+#define NCR5380_implementation_fields unsigned char *pdma_base
+#define NCR5380_local_declare() struct Scsi_Host *_instance
+#define NCR5380_setup(instance) _instance = instance
-#define RESET_BOOT
-#define DRIVER_SETUP
+#define NCR5380_read(reg) macscsi_read(_instance, reg)
+#define NCR5380_write(reg, value) macscsi_write(_instance, reg, value)
-extern void via_scsi_clear(void);
+#define NCR5380_pread macscsi_pread
+#define NCR5380_pwrite macscsi_pwrite
-#ifdef RESET_BOOT
-static void mac_scsi_reset_boot(struct Scsi_Host *instance);
-#endif
+#define NCR5380_intr macscsi_intr
+#define NCR5380_queue_command macscsi_queue_command
+#define NCR5380_abort macscsi_abort
+#define NCR5380_bus_reset macscsi_bus_reset
+#define NCR5380_info macscsi_info
+#define NCR5380_show_info macscsi_show_info
+#define NCR5380_write_info macscsi_write_info
+
+#include "NCR5380.h"
+
+#define RESET_BOOT
-static int setup_called = 0;
static int setup_can_queue = -1;
+module_param(setup_can_queue, int, 0);
static int setup_cmd_per_lun = -1;
+module_param(setup_cmd_per_lun, int, 0);
static int setup_sg_tablesize = -1;
+module_param(setup_sg_tablesize, int, 0);
static int setup_use_pdma = -1;
-#ifdef SUPPORT_TAGS
+module_param(setup_use_pdma, int, 0);
static int setup_use_tagged_queuing = -1;
-#endif
+module_param(setup_use_tagged_queuing, int, 0);
static int setup_hostid = -1;
+module_param(setup_hostid, int, 0);
/* Time (in jiffies) to wait after a reset; the SCSI standard calls for 250ms,
* we usually do 0.5s to be on the safe side. But Toshiba CD-ROMs once more
#define AFTER_RESET_DELAY (HZ/2)
#endif
-static volatile unsigned char *mac_scsi_regp = NULL;
-static volatile unsigned char *mac_scsi_drq = NULL;
-static volatile unsigned char *mac_scsi_nodrq = NULL;
-
-
/*
* NCR 5380 register access functions
*/
-#if 0
-/* Debug versions */
-#define CTRL(p,v) (*ctrl = (v))
-
-static char macscsi_read(struct Scsi_Host *instance, int reg)
+static inline char macscsi_read(struct Scsi_Host *instance, int reg)
{
- int iobase = instance->io_port;
- int i;
- int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
-
- CTRL(iobase, 0);
- i = in_8(iobase + (reg<<4));
- CTRL(iobase, 0x40);
-
- return i;
+ return in_8(instance->base + (reg << 4));
}
-static void macscsi_write(struct Scsi_Host *instance, int reg, int value)
-{
- int iobase = instance->io_port;
- int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl;
-
- CTRL(iobase, 0);
- out_8(iobase + (reg<<4), value);
- CTRL(iobase, 0x40);
-}
-#else
-
-/* Fast versions */
-static __inline__ char macscsi_read(struct Scsi_Host *instance, int reg)
+static inline void macscsi_write(struct Scsi_Host *instance, int reg, int value)
{
- return in_8(instance->io_port + (reg<<4));
+ out_8(instance->base + (reg << 4), value);
}
-static __inline__ void macscsi_write(struct Scsi_Host *instance, int reg, int value)
+#ifndef MODULE
+static int __init mac_scsi_setup(char *str)
{
- out_8(instance->io_port + (reg<<4), value);
-}
-#endif
-
+ int ints[7];
-/*
- * Function : mac_scsi_setup(char *str)
- *
- * Purpose : booter command line initialization of the overrides array,
- *
- * Inputs : str - comma delimited list of options
- *
- */
+ (void)get_options(str, ARRAY_SIZE(ints), ints);
-static int __init mac_scsi_setup(char *str) {
-#ifdef DRIVER_SETUP
- int ints[7];
-
- (void)get_options( str, ARRAY_SIZE(ints), ints);
-
- if (setup_called++ || ints[0] < 1 || ints[0] > 6) {
- printk(KERN_WARNING "scsi: <mac5380>"
- " Usage: mac5380=<can_queue>[,<cmd_per_lun>,<sg_tablesize>,<hostid>,<use_tags>,<use_pdma>]\n");
- printk(KERN_ALERT "scsi: <mac5380> Bad Penguin parameters?\n");
- return 0;
- }
-
- if (ints[0] >= 1) {
- if (ints[1] > 0)
- /* no limits on this, just > 0 */
- setup_can_queue = ints[1];
- }
- if (ints[0] >= 2) {
- if (ints[2] > 0)
- setup_cmd_per_lun = ints[2];
- }
- if (ints[0] >= 3) {
- if (ints[3] >= 0) {
- setup_sg_tablesize = ints[3];
- /* Must be <= SG_ALL (255) */
- if (setup_sg_tablesize > SG_ALL)
- setup_sg_tablesize = SG_ALL;
- }
- }
- if (ints[0] >= 4) {
- /* Must be between 0 and 7 */
- if (ints[4] >= 0 && ints[4] <= 7)
- setup_hostid = ints[4];
- else if (ints[4] > 7)
- printk(KERN_WARNING "mac_scsi_setup: invalid host ID %d !\n", ints[4] );
- }
-#ifdef SUPPORT_TAGS
- if (ints[0] >= 5) {
- if (ints[5] >= 0)
- setup_use_tagged_queuing = !!ints[5];
+ if (ints[0] < 1 || ints[0] > 6) {
+ pr_err("Usage: mac5380=<can_queue>[,<cmd_per_lun>[,<sg_tablesize>[,<hostid>[,<use_tags>[,<use_pdma>]]]]]\n");
+ return 0;
}
-
- if (ints[0] == 6) {
- if (ints[6] >= 0)
+ if (ints[0] >= 1)
+ setup_can_queue = ints[1];
+ if (ints[0] >= 2)
+ setup_cmd_per_lun = ints[2];
+ if (ints[0] >= 3)
+ setup_sg_tablesize = ints[3];
+ if (ints[0] >= 4)
+ setup_hostid = ints[4];
+ if (ints[0] >= 5)
+ setup_use_tagged_queuing = ints[5];
+ if (ints[0] >= 6)
setup_use_pdma = ints[6];
- }
-#else
- if (ints[0] == 5) {
- if (ints[5] >= 0)
- setup_use_pdma = ints[5];
- }
-#endif /* SUPPORT_TAGS */
-
-#endif /* DRIVER_SETUP */
return 1;
}
__setup("mac5380=", mac_scsi_setup);
-
-/*
- * Function : int macscsi_detect(struct scsi_host_template * tpnt)
- *
- * Purpose : initializes mac NCR5380 driver based on the
- * command line / compile time port and irq definitions.
- *
- * Inputs : tpnt - template for this SCSI adapter.
- *
- * Returns : 1 if a host adapter was found, 0 if not.
- *
- */
-
-int __init macscsi_detect(struct scsi_host_template * tpnt)
-{
- static int called = 0;
- int flags = 0;
- struct Scsi_Host *instance;
-
- if (!MACH_IS_MAC || called)
- return( 0 );
-
- if (macintosh_config->scsi_type != MAC_SCSI_OLD)
- return( 0 );
-
- /* setup variables */
- tpnt->can_queue =
- (setup_can_queue > 0) ? setup_can_queue : CAN_QUEUE;
- tpnt->cmd_per_lun =
- (setup_cmd_per_lun > 0) ? setup_cmd_per_lun : CMD_PER_LUN;
- tpnt->sg_tablesize =
- (setup_sg_tablesize >= 0) ? setup_sg_tablesize : SG_TABLESIZE;
-
- if (setup_hostid >= 0)
- tpnt->this_id = setup_hostid;
- else {
- /* use 7 as default */
- tpnt->this_id = 7;
- }
-
-#ifdef SUPPORT_TAGS
- if (setup_use_tagged_queuing < 0)
- setup_use_tagged_queuing = USE_TAGGED_QUEUING;
-#endif
-
- /* Once we support multiple 5380s (e.g. DuoDock) we'll do
- something different here */
- instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
- if (instance == NULL)
- return 0;
-
- if (macintosh_config->ident == MAC_MODEL_IIFX) {
- mac_scsi_regp = via1+0x8000;
- mac_scsi_drq = via1+0xE000;
- mac_scsi_nodrq = via1+0xC000;
- /* The IIFX should be able to do true DMA, but pseudo-dma doesn't work */
- flags = FLAG_NO_PSEUDO_DMA;
- } else {
- mac_scsi_regp = via1+0x10000;
- mac_scsi_drq = via1+0x6000;
- mac_scsi_nodrq = via1+0x12000;
- }
-
- if (! setup_use_pdma)
- flags = FLAG_NO_PSEUDO_DMA;
-
- instance->io_port = (unsigned long) mac_scsi_regp;
- instance->irq = IRQ_MAC_SCSI;
-
-#ifdef RESET_BOOT
- mac_scsi_reset_boot(instance);
-#endif
-
- NCR5380_init(instance, flags);
-
- instance->n_io_port = 255;
-
- ((struct NCR5380_hostdata *)instance->hostdata)->ctrl = 0;
-
- if (instance->irq != SCSI_IRQ_NONE)
- if (request_irq(instance->irq, NCR5380_intr, 0, "ncr5380", instance)) {
- printk(KERN_WARNING "scsi%d: IRQ%d not free, interrupts disabled\n",
- instance->host_no, instance->irq);
- instance->irq = SCSI_IRQ_NONE;
- }
-
- printk(KERN_INFO "scsi%d: generic 5380 at port %lX irq", instance->host_no, instance->io_port);
- if (instance->irq == SCSI_IRQ_NONE)
- printk (KERN_INFO "s disabled");
- else
- printk (KERN_INFO " %d", instance->irq);
- printk(KERN_INFO " options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
- instance->can_queue, instance->cmd_per_lun, MACSCSI_PUBLIC_RELEASE);
- printk(KERN_INFO "\nscsi%d:", instance->host_no);
- NCR5380_print_options(instance);
- printk("\n");
- called = 1;
- return 1;
-}
-
-int macscsi_release (struct Scsi_Host *shpnt)
-{
- if (shpnt->irq != SCSI_IRQ_NONE)
- free_irq(shpnt->irq, shpnt);
- NCR5380_exit(shpnt);
-
- return 0;
-}
+#endif /* !MODULE */
#ifdef RESET_BOOT
/*
}
#endif
-const char * macscsi_info (struct Scsi_Host *spnt) {
- return "";
-}
-
+#ifdef PSEUDO_DMA
/*
Pseudo-DMA: (Ove Edlund)
The code attempts to catch bus errors that occur if one for example
: "0"(s), "1"(d), "2"(len) \
: "d0")
-
-static int macscsi_pread (struct Scsi_Host *instance,
- unsigned char *dst, int len)
+static int macscsi_pread(struct Scsi_Host *instance,
+ unsigned char *dst, int len)
{
- unsigned char *d;
- volatile unsigned char *s;
-
- NCR5380_local_declare();
- NCR5380_setup(instance);
-
- s = mac_scsi_drq+0x60;
- d = dst;
-
-/* These conditions are derived from MacOS */
-
- while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ)
- && !(NCR5380_read(STATUS_REG) & SR_REQ))
- ;
- if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ)
- && (NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH)) {
- printk(KERN_ERR "Error in macscsi_pread\n");
- return -1;
- }
-
- CP_IO_TO_MEM(s, d, len);
-
- if (len != 0) {
- printk(KERN_NOTICE "Bus error in macscsi_pread\n");
- return -1;
- }
-
- return 0;
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+ unsigned char *d;
+ unsigned char *s;
+
+ NCR5380_local_declare();
+ NCR5380_setup(instance);
+
+ s = hostdata->pdma_base + (INPUT_DATA_REG << 4);
+ d = dst;
+
+ /* These conditions are derived from MacOS */
+
+ while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ) &&
+ !(NCR5380_read(STATUS_REG) & SR_REQ))
+ ;
+
+ if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ) &&
+ (NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH)) {
+ pr_err("Error in macscsi_pread\n");
+ return -1;
+ }
+
+ CP_IO_TO_MEM(s, d, len);
+
+ if (len != 0) {
+ pr_notice("Bus error in macscsi_pread\n");
+ return -1;
+ }
+
+ return 0;
}
: "0"(s), "1"(d), "2"(len) \
: "d0")
-static int macscsi_pwrite (struct Scsi_Host *instance,
- unsigned char *src, int len)
+static int macscsi_pwrite(struct Scsi_Host *instance,
+ unsigned char *src, int len)
{
- unsigned char *s;
- volatile unsigned char *d;
-
- NCR5380_local_declare();
- NCR5380_setup(instance);
-
- s = src;
- d = mac_scsi_drq;
-
-/* These conditions are derived from MacOS */
-
- while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ)
- && (!(NCR5380_read(STATUS_REG) & SR_REQ)
- || (NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH)))
- ;
- if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ)) {
- printk(KERN_ERR "Error in macscsi_pwrite\n");
- return -1;
- }
-
- CP_MEM_TO_IO(s, d, len);
-
- if (len != 0) {
- printk(KERN_NOTICE "Bus error in macscsi_pwrite\n");
- return -1;
- }
-
- return 0;
-}
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+ unsigned char *s;
+ unsigned char *d;
+
+ NCR5380_local_declare();
+ NCR5380_setup(instance);
+
+ s = src;
+ d = hostdata->pdma_base + (OUTPUT_DATA_REG << 4);
+
+ /* These conditions are derived from MacOS */
+
+ while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ) &&
+ (!(NCR5380_read(STATUS_REG) & SR_REQ) ||
+ (NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH)))
+ ;
+
+ if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ)) {
+ pr_err("Error in macscsi_pwrite\n");
+ return -1;
+ }
+
+ CP_MEM_TO_IO(s, d, len);
+
+ if (len != 0) {
+ pr_notice("Bus error in macscsi_pwrite\n");
+ return -1;
+ }
+ return 0;
+}
+#endif
#include "NCR5380.c"
-static struct scsi_host_template driver_template = {
- .proc_name = "Mac5380",
+#define DRV_MODULE_NAME "mac_scsi"
+#define PFX DRV_MODULE_NAME ": "
+
+static struct scsi_host_template mac_scsi_template = {
+ .module = THIS_MODULE,
+ .proc_name = DRV_MODULE_NAME,
.show_info = macscsi_show_info,
.write_info = macscsi_write_info,
.name = "Macintosh NCR5380 SCSI",
- .detect = macscsi_detect,
- .release = macscsi_release,
.info = macscsi_info,
.queuecommand = macscsi_queue_command,
.eh_abort_handler = macscsi_abort,
.eh_bus_reset_handler = macscsi_bus_reset,
- .can_queue = CAN_QUEUE,
+ .can_queue = 16,
.this_id = 7,
.sg_tablesize = SG_ALL,
- .cmd_per_lun = CMD_PER_LUN,
+ .cmd_per_lun = 2,
.use_clustering = DISABLE_CLUSTERING
};
+static int __init mac_scsi_probe(struct platform_device *pdev)
+{
+ struct Scsi_Host *instance;
+ int error;
+ int host_flags = 0;
+ struct resource *irq, *pio_mem, *pdma_mem = NULL;
+
+ pio_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!pio_mem)
+ return -ENODEV;
+
+#ifdef PSEUDO_DMA
+ pdma_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+#endif
+
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+
+ if (!hwreg_present((unsigned char *)pio_mem->start +
+ (STATUS_REG << 4))) {
+ pr_info(PFX "no device detected at %pap\n", &pio_mem->start);
+ return -ENODEV;
+ }
+
+ if (setup_can_queue > 0)
+ mac_scsi_template.can_queue = setup_can_queue;
+ if (setup_cmd_per_lun > 0)
+ mac_scsi_template.cmd_per_lun = setup_cmd_per_lun;
+ if (setup_sg_tablesize >= 0)
+ mac_scsi_template.sg_tablesize = setup_sg_tablesize;
+ if (setup_hostid >= 0)
+ mac_scsi_template.this_id = setup_hostid & 7;
+ if (setup_use_pdma < 0)
+ setup_use_pdma = 0;
+
+ instance = scsi_host_alloc(&mac_scsi_template,
+ sizeof(struct NCR5380_hostdata));
+ if (!instance)
+ return -ENOMEM;
+
+ instance->base = pio_mem->start;
+ if (irq)
+ instance->irq = irq->start;
+ else
+ instance->irq = NO_IRQ;
+
+ if (pdma_mem && setup_use_pdma) {
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
+
+ hostdata->pdma_base = (unsigned char *)pdma_mem->start;
+ } else
+ host_flags |= FLAG_NO_PSEUDO_DMA;
+
+#ifdef RESET_BOOT
+ mac_scsi_reset_boot(instance);
+#endif
+
+#ifdef SUPPORT_TAGS
+ host_flags |= setup_use_tagged_queuing > 0 ? FLAG_TAGGED_QUEUING : 0;
+#endif
+
+ NCR5380_init(instance, host_flags);
+
+ if (instance->irq != NO_IRQ) {
+ error = request_irq(instance->irq, macscsi_intr, IRQF_SHARED,
+ "NCR5380", instance);
+ if (error)
+ goto fail_irq;
+ }
+
+ error = scsi_add_host(instance, NULL);
+ if (error)
+ goto fail_host;
+
+ platform_set_drvdata(pdev, instance);
+
+ scsi_scan_host(instance);
+ return 0;
+
+fail_host:
+ if (instance->irq != NO_IRQ)
+ free_irq(instance->irq, instance);
+fail_irq:
+ NCR5380_exit(instance);
+ scsi_host_put(instance);
+ return error;
+}
+
+static int __exit mac_scsi_remove(struct platform_device *pdev)
+{
+ struct Scsi_Host *instance = platform_get_drvdata(pdev);
+
+ scsi_remove_host(instance);
+ if (instance->irq != NO_IRQ)
+ free_irq(instance->irq, instance);
+ NCR5380_exit(instance);
+ scsi_host_put(instance);
+ return 0;
+}
+
+static struct platform_driver mac_scsi_driver = {
+ .remove = __exit_p(mac_scsi_remove),
+ .driver = {
+ .name = DRV_MODULE_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver_probe(mac_scsi_driver, mac_scsi_probe);
-#include "scsi_module.c"
+MODULE_ALIAS("platform:" DRV_MODULE_NAME);
+MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * Cumana Generic NCR5380 driver defines
- *
- * Copyright 1993, Drew Eckhardt
- * Visionary Computing
- * (Unix and Linux consulting and custom programming)
- * drew@colorado.edu
- * +1 (303) 440-4894
- *
- * ALPHA RELEASE 1.
- *
- * For more information, please consult
- *
- * NCR 5380 Family
- * SCSI Protocol Controller
- * Databook
- *
- * NCR Microelectronics
- * 1635 Aeroplaza Drive
- * Colorado Springs, CO 80916
- * 1+ (719) 578-3400
- * 1+ (800) 334-5454
- */
-
-#ifndef MAC_NCR5380_H
-#define MAC_NCR5380_H
-
-#define MACSCSI_PUBLIC_RELEASE 2
-
-#ifndef ASM
-
-#ifndef CMD_PER_LUN
-#define CMD_PER_LUN 2
-#endif
-
-#ifndef CAN_QUEUE
-#define CAN_QUEUE 16
-#endif
-
-#ifndef SG_TABLESIZE
-#define SG_TABLESIZE SG_NONE
-#endif
-
-#ifndef USE_TAGGED_QUEUING
-#define USE_TAGGED_QUEUING 0
-#endif
-
-#include <scsi/scsicam.h>
-
-#define NCR5380_implementation_fields \
- int port, ctrl
-
-#define NCR5380_local_declare() \
- struct Scsi_Host *_instance
-
-#define NCR5380_setup(instance) \
- _instance = instance
-
-#define NCR5380_read(reg) macscsi_read(_instance, reg)
-#define NCR5380_write(reg, value) macscsi_write(_instance, reg, value)
-
-#define NCR5380_pread macscsi_pread
-#define NCR5380_pwrite macscsi_pwrite
-
-#define NCR5380_intr macscsi_intr
-#define NCR5380_queue_command macscsi_queue_command
-#define NCR5380_abort macscsi_abort
-#define NCR5380_bus_reset macscsi_bus_reset
-#define NCR5380_show_info macscsi_show_info
-#define NCR5380_write_info macscsi_write_info
-
-#endif /* ndef ASM */
-#endif /* MAC_NCR5380_H */
-
cmd->device->id, (u32)cmd->device->lun);
if(list_empty(&adapter->pending_list))
- return FALSE;
+ return FAILED;
list_for_each_safe(pos, next, &adapter->pending_list) {
(aor==SCB_ABORT) ? "ABORTING":"RESET",
scb->idx);
- return FALSE;
+ return FAILED;
}
else {
list_add_tail(SCSI_LIST(cmd),
&adapter->completed_list);
- return TRUE;
+ return SUCCESS;
}
}
}
- return FALSE;
+ return FAILED;
}
static inline int
NULL,
};
-/**
- * megaraid_change_queue_depth - Change the device's queue depth
- * @sdev: scsi device struct
- * @qdepth: depth to set
- * @reason: calling context
- *
- * Return value:
- * actual depth set
- */
-static int megaraid_change_queue_depth(struct scsi_device *sdev, int qdepth,
- int reason)
-{
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
-
- if (qdepth > MBOX_MAX_SCSI_CMDS)
- qdepth = MBOX_MAX_SCSI_CMDS;
- scsi_adjust_queue_depth(sdev, 0, qdepth);
- return sdev->queue_depth;
-}
-
/*
* Scsi host template for megaraid unified driver
*/
.eh_device_reset_handler = megaraid_reset_handler,
.eh_bus_reset_handler = megaraid_reset_handler,
.eh_host_reset_handler = megaraid_reset_handler,
- .change_queue_depth = megaraid_change_queue_depth,
+ .change_queue_depth = scsi_change_queue_depth,
.use_clustering = ENABLE_CLUSTERING,
.no_write_same = 1,
.sdev_attrs = megaraid_sdev_attrs,
/*
* Linux MegaRAID driver for SAS based RAID controllers
*
- * Copyright (c) 2003-2012 LSI Corporation.
+ * Copyright (c) 2003-2013 LSI Corporation
+ * Copyright (c) 2013-2014 Avago Technologies
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* FILE: megaraid_sas.h
*
- * Authors: LSI Corporation
+ * Authors: Avago Technologies
+ * Kashyap Desai <kashyap.desai@avagotech.com>
+ * Sumit Saxena <sumit.saxena@avagotech.com>
*
- * Send feedback to: <megaraidlinux@lsi.com>
+ * Send feedback to: megaraidlinux.pdl@avagotech.com
*
- * Mail to: LSI Corporation, 1621 Barber Lane, Milpitas, CA 95035
- * ATTN: Linuxraid
+ * Mail to: Avago Technologies, 350 West Trimble Road, Building 90,
+ * San Jose, California 95131
*/
#ifndef LSI_MEGARAID_SAS_H
/*
* MegaRAID SAS Driver meta data
*/
-#define MEGASAS_VERSION "06.805.06.00-rc1"
-#define MEGASAS_RELDATE "Sep. 4, 2014"
-#define MEGASAS_EXT_VERSION "Thu. Sep. 4 17:00:00 PDT 2014"
+#define MEGASAS_VERSION "06.805.06.01-rc1"
/*
* Device IDs
struct LD_LOAD_BALANCE_INFO *lbInfo, struct IO_REQUEST_INFO *in_info);
void mr_update_load_balance_params(struct MR_DRV_RAID_MAP_ALL *map,
struct LD_LOAD_BALANCE_INFO *lbInfo);
-int megasas_get_ctrl_info(struct megasas_instance *instance,
- struct megasas_ctrl_info *ctrl_info);
+int megasas_get_ctrl_info(struct megasas_instance *instance);
int megasas_set_crash_dump_params(struct megasas_instance *instance,
u8 crash_buf_state);
void megasas_free_host_crash_buffer(struct megasas_instance *instance);
/*
* Linux MegaRAID driver for SAS based RAID controllers
*
- * Copyright (c) 2003-2012 LSI Corporation.
+ * Copyright (c) 2003-2013 LSI Corporation
+ * Copyright (c) 2013-2014 Avago Technologies
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*
- * FILE: megaraid_sas_base.c
- * Version : 06.805.06.00-rc1
- *
- * Authors: LSI Corporation
+ * Authors: Avago Technologies
* Sreenivas Bagalkote
* Sumant Patro
* Bo Yang
- * Adam Radford <linuxraid@lsi.com>
+ * Adam Radford
+ * Kashyap Desai <kashyap.desai@avagotech.com>
+ * Sumit Saxena <sumit.saxena@avagotech.com>
*
- * Send feedback to: <megaraidlinux@lsi.com>
+ * Send feedback to: megaraidlinux.pdl@avagotech.com
*
- * Mail to: LSI Corporation, 1621 Barber Lane, Milpitas, CA 95035
- * ATTN: Linuxraid
+ * Mail to: Avago Technologies, 350 West Trimble Road, Building 90,
+ * San Jose, California 95131
*/
#include <linux/kernel.h>
cpu_to_le32(upper_32_bits(cmd_to_abort->frame_phys_addr));
cmd->sync_cmd = 1;
- cmd->cmd_status = 0xFF;
+ cmd->cmd_status = ENODATA;
instance->instancet->issue_dcmd(instance, cmd);
instance = (struct megasas_instance *)
scmd->device->host->hostdata;
+ if (instance->unload == 1) {
+ scmd->result = DID_NO_CONNECT << 16;
+ scmd->scsi_done(scmd);
+ return 0;
+ }
+
if (instance->issuepend_done == 0)
return SCSI_MLQUEUE_HOST_BUSY;
}
}
-static int megasas_change_queue_depth(struct scsi_device *sdev,
- int queue_depth, int reason)
-{
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
-
- if (queue_depth > sdev->host->can_queue)
- queue_depth = sdev->host->can_queue;
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev),
- queue_depth);
-
- return queue_depth;
-}
-
static ssize_t
megasas_fw_crash_buffer_store(struct device *cdev,
struct device_attribute *attr, const char *buf, size_t count)
.shost_attrs = megaraid_host_attrs,
.bios_param = megasas_bios_param,
.use_clustering = ENABLE_CLUSTERING,
- .change_queue_depth = megasas_change_queue_depth,
+ .change_queue_depth = scsi_change_queue_depth,
.no_write_same = 1,
};
return ret;
}
+/*
+ * megasas_update_ext_vd_details : Update details w.r.t Extended VD
+ * instance : Controller's instance
+*/
+static void megasas_update_ext_vd_details(struct megasas_instance *instance)
+{
+ struct fusion_context *fusion;
+ u32 old_map_sz;
+ u32 new_map_sz;
+
+ fusion = instance->ctrl_context;
+ /* For MFI based controllers return dummy success */
+ if (!fusion)
+ return;
+
+ instance->supportmax256vd =
+ instance->ctrl_info->adapterOperations3.supportMaxExtLDs;
+ /* Below is additional check to address future FW enhancement */
+ if (instance->ctrl_info->max_lds > 64)
+ instance->supportmax256vd = 1;
+
+ instance->drv_supported_vd_count = MEGASAS_MAX_LD_CHANNELS
+ * MEGASAS_MAX_DEV_PER_CHANNEL;
+ instance->drv_supported_pd_count = MEGASAS_MAX_PD_CHANNELS
+ * MEGASAS_MAX_DEV_PER_CHANNEL;
+ if (instance->supportmax256vd) {
+ instance->fw_supported_vd_count = MAX_LOGICAL_DRIVES_EXT;
+ instance->fw_supported_pd_count = MAX_PHYSICAL_DEVICES;
+ } else {
+ instance->fw_supported_vd_count = MAX_LOGICAL_DRIVES;
+ instance->fw_supported_pd_count = MAX_PHYSICAL_DEVICES;
+ }
+ dev_info(&instance->pdev->dev, "Firmware supports %d VD %d PD\n",
+ instance->fw_supported_vd_count,
+ instance->fw_supported_pd_count);
+ dev_info(&instance->pdev->dev, "Driver supports %d VD %d PD\n",
+ instance->drv_supported_vd_count,
+ instance->drv_supported_pd_count);
+
+ old_map_sz = sizeof(struct MR_FW_RAID_MAP) +
+ (sizeof(struct MR_LD_SPAN_MAP) *
+ (instance->fw_supported_vd_count - 1));
+ new_map_sz = sizeof(struct MR_FW_RAID_MAP_EXT);
+ fusion->drv_map_sz = sizeof(struct MR_DRV_RAID_MAP) +
+ (sizeof(struct MR_LD_SPAN_MAP) *
+ (instance->drv_supported_vd_count - 1));
+
+ fusion->max_map_sz = max(old_map_sz, new_map_sz);
+
+
+ if (instance->supportmax256vd)
+ fusion->current_map_sz = new_map_sz;
+ else
+ fusion->current_map_sz = old_map_sz;
+
+}
+
/**
* megasas_get_controller_info - Returns FW's controller structure
* @instance: Adapter soft state
- * @ctrl_info: Controller information structure
*
* Issues an internal command (DCMD) to get the FW's controller structure.
* This information is mainly used to find out the maximum IO transfer per
* command supported by the FW.
*/
int
-megasas_get_ctrl_info(struct megasas_instance *instance,
- struct megasas_ctrl_info *ctrl_info)
+megasas_get_ctrl_info(struct megasas_instance *instance)
{
int ret = 0;
struct megasas_cmd *cmd;
struct megasas_dcmd_frame *dcmd;
struct megasas_ctrl_info *ci;
+ struct megasas_ctrl_info *ctrl_info;
dma_addr_t ci_h = 0;
+ ctrl_info = instance->ctrl_info;
+
cmd = megasas_get_cmd(instance);
if (!cmd) {
else
ret = megasas_issue_polled(instance, cmd);
- if (!ret)
+ if (!ret) {
memcpy(ctrl_info, ci, sizeof(struct megasas_ctrl_info));
+ le32_to_cpus((u32 *)&ctrl_info->properties.OnOffProperties);
+ le32_to_cpus((u32 *)&ctrl_info->adapterOperations2);
+ le32_to_cpus((u32 *)&ctrl_info->adapterOperations3);
+ megasas_update_ext_vd_details(instance);
+ }
pci_free_consistent(instance->pdev, sizeof(struct megasas_ctrl_info),
ci, ci_h);
if (megasas_issue_init_mfi(instance))
goto fail_fw_init;
- if (megasas_get_ctrl_info(instance, instance->ctrl_info)) {
+ if (megasas_get_ctrl_info(instance)) {
dev_err(&instance->pdev->dev, "(%d): Could get controller info "
"Fail from %s %d\n", instance->unique_id,
__func__, __LINE__);
instance->msixentry[i].entry = i;
i = pci_enable_msix_range(instance->pdev, instance->msixentry,
1, instance->msix_vectors);
- if (i)
+ if (i > 0)
instance->msix_vectors = i;
else
instance->msix_vectors = 0;
dev_info(&instance->pdev->dev,
"Controller type: iMR\n");
}
- /* OnOffProperties are converted into CPU arch*/
- le32_to_cpus((u32 *)&ctrl_info->properties.OnOffProperties);
instance->disableOnlineCtrlReset =
ctrl_info->properties.OnOffProperties.disableOnlineCtrlReset;
- /* adapterOperations2 are converted into CPU arch*/
- le32_to_cpus((u32 *)&ctrl_info->adapterOperations2);
instance->mpio = ctrl_info->adapterOperations2.mpio;
instance->UnevenSpanSupport =
ctrl_info->adapterOperations2.supportUnevenSpans;
"requestorId %d\n", instance->requestorId);
}
- le32_to_cpus((u32 *)&ctrl_info->adapterOperations3);
instance->crash_dump_fw_support =
ctrl_info->adapterOperations3.supportCrashDump;
instance->crash_dump_drv_support =
if (tmp_sectors && (instance->max_sectors_per_req > tmp_sectors))
instance->max_sectors_per_req = tmp_sectors;
- kfree(ctrl_info);
-
/* Check for valid throttlequeuedepth module parameter */
if (instance->is_imr) {
if (throttlequeuedepth > (instance->max_fw_cmds -
return -ENODEV;
}
- /*
- * Trigger SCSI to scan our drives
- */
- scsi_scan_host(host);
return 0;
}
goto fail_alloc_dma_buf;
}
fusion = instance->ctrl_context;
+ memset(fusion, 0,
+ ((1 << PAGE_SHIFT) << instance->ctrl_context_pages));
INIT_LIST_HEAD(&fusion->cmd_pool);
spin_lock_init(&fusion->mpt_pool_lock);
memset(fusion->load_balance_info, 0,
goto fail_io_attach;
instance->unload = 0;
+ /*
+ * Trigger SCSI to scan our drives
+ */
+ scsi_scan_host(host);
/*
* Initiate AEN (Asynchronous Event Notification)
megasas_issue_blocked_cmd(instance, cmd, 0);
cmd->sync_cmd = 0;
+ if (instance->unload == 1) {
+ dev_info(&instance->pdev->dev, "Driver unload is in progress "
+ "don't submit data to application\n");
+ goto out;
+ }
/*
* copy out the kernel buffers to user buffers
*/
static DRIVER_ATTR(version, S_IRUGO, megasas_sysfs_show_version, NULL);
-static ssize_t
-megasas_sysfs_show_release_date(struct device_driver *dd, char *buf)
-{
- return snprintf(buf, strlen(MEGASAS_RELDATE) + 2, "%s\n",
- MEGASAS_RELDATE);
-}
-
-static DRIVER_ATTR(release_date, S_IRUGO, megasas_sysfs_show_release_date,
- NULL);
-
static ssize_t
megasas_sysfs_show_support_poll_for_event(struct device_driver *dd, char *buf)
{
/*
* Announce driver version and other information
*/
- printk(KERN_INFO "megasas: %s %s\n", MEGASAS_VERSION,
- MEGASAS_EXT_VERSION);
+ pr_info("megasas: %s\n", MEGASAS_VERSION);
spin_lock_init(&poll_aen_lock);
&driver_attr_version);
if (rval)
goto err_dcf_attr_ver;
- rval = driver_create_file(&megasas_pci_driver.driver,
- &driver_attr_release_date);
- if (rval)
- goto err_dcf_rel_date;
rval = driver_create_file(&megasas_pci_driver.driver,
&driver_attr_support_poll_for_event);
err_dcf_dbg_lvl:
driver_remove_file(&megasas_pci_driver.driver,
&driver_attr_support_poll_for_event);
-
err_dcf_support_poll_for_event:
- driver_remove_file(&megasas_pci_driver.driver,
- &driver_attr_release_date);
-
-err_dcf_rel_date:
driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version);
err_dcf_attr_ver:
pci_unregister_driver(&megasas_pci_driver);
&driver_attr_support_poll_for_event);
driver_remove_file(&megasas_pci_driver.driver,
&driver_attr_support_device_change);
- driver_remove_file(&megasas_pci_driver.driver,
- &driver_attr_release_date);
driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version);
pci_unregister_driver(&megasas_pci_driver);
/*
* Linux MegaRAID driver for SAS based RAID controllers
*
- * Copyright (c) 2009-2012 LSI Corporation.
+ * Copyright (c) 2009-2013 LSI Corporation
+ * Copyright (c) 2013-2014 Avago Technologies
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* FILE: megaraid_sas_fp.c
*
- * Authors: LSI Corporation
+ * Authors: Avago Technologies
* Sumant Patro
* Varad Talamacki
* Manoj Jose
+ * Kashyap Desai <kashyap.desai@avagotech.com>
+ * Sumit Saxena <sumit.saxena@avagotech.com>
*
- * Send feedback to: <megaraidlinux@lsi.com>
+ * Send feedback to: megaraidlinux.pdl@avagotech.com
*
- * Mail to: LSI Corporation, 1621 Barber Lane, Milpitas, CA 95035
- * ATTN: Linuxraid
+ * Mail to: Avago Technologies, 350 West Trimble Road, Building 90,
+ * San Jose, California 95131
*/
#include <linux/kernel.h>
/* New Raid map will not set totalSize, so keep expected value
* for legacy code in ValidateMapInfo
*/
- pDrvRaidMap->totalSize = sizeof(struct MR_FW_RAID_MAP_EXT);
+ pDrvRaidMap->totalSize =
+ cpu_to_le32(sizeof(struct MR_FW_RAID_MAP_EXT));
} else {
fw_map_old = (struct MR_FW_RAID_MAP_ALL *)
fusion->ld_map[(instance->map_id & 1)];
pFwRaidMap = &fw_map_old->raidMap;
#if VD_EXT_DEBUG
- for (i = 0; i < pFwRaidMap->ldCount; i++) {
+ for (i = 0; i < le16_to_cpu(pFwRaidMap->ldCount); i++) {
dev_dbg(&instance->pdev->dev, "(%d) :Index 0x%x "
"Target Id 0x%x Seq Num 0x%x Size 0/%llx\n",
instance->unique_id, i,
memset(drv_map, 0, fusion->drv_map_sz);
pDrvRaidMap->totalSize = pFwRaidMap->totalSize;
- pDrvRaidMap->ldCount = pFwRaidMap->ldCount;
+ pDrvRaidMap->ldCount = (__le16)pFwRaidMap->ldCount;
pDrvRaidMap->fpPdIoTimeoutSec = pFwRaidMap->fpPdIoTimeoutSec;
for (i = 0; i < MAX_RAIDMAP_LOGICAL_DRIVES + MAX_RAIDMAP_VIEWS; i++)
pDrvRaidMap->ldTgtIdToLd[i] =
(u8)pFwRaidMap->ldTgtIdToLd[i];
- for (i = 0; i < pDrvRaidMap->ldCount; i++) {
+ for (i = 0; i < le16_to_cpu(pDrvRaidMap->ldCount); i++) {
pDrvRaidMap->ldSpanMap[i] = pFwRaidMap->ldSpanMap[i];
#if VD_EXT_DEBUG
dev_dbg(&instance->pdev->dev,
else
expected_size =
(sizeof(struct MR_FW_RAID_MAP) - sizeof(struct MR_LD_SPAN_MAP) +
- (sizeof(struct MR_LD_SPAN_MAP) * le32_to_cpu(pDrvRaidMap->ldCount)));
+ (sizeof(struct MR_LD_SPAN_MAP) * le16_to_cpu(pDrvRaidMap->ldCount)));
if (le32_to_cpu(pDrvRaidMap->totalSize) != expected_size) {
dev_err(&instance->pdev->dev, "map info structure size 0x%x is not matching with ld count\n",
mr_update_load_balance_params(drv_map, lbInfo);
- num_lds = le32_to_cpu(drv_map->raidMap.ldCount);
+ num_lds = le16_to_cpu(drv_map->raidMap.ldCount);
/*Convert Raid capability values to CPU arch */
for (ldCount = 0; ldCount < num_lds; ldCount++) {
quad = &map->raidMap.ldSpanMap[ld].
spanBlock[span].
block_span_info.quad[info];
- if (le32_to_cpu(quad->diff == 0))
+ if (le32_to_cpu(quad->diff) == 0)
return SPAN_INVALID;
if (le64_to_cpu(quad->logStart) <= row &&
row <= le64_to_cpu(quad->logEnd) &&
span_set->span_row_data_width) * span_set->diff;
for (span = 0, span_offset = 0; span < raid->spanDepth; span++)
if (le32_to_cpu(map->raidMap.ldSpanMap[ld].spanBlock[span].
- block_span_info.noElements >= info+1)) {
+ block_span_info.noElements) >= info+1) {
if (strip_offset >=
span_set->strip_offset[span])
span_offset++;
/*
* Linux MegaRAID driver for SAS based RAID controllers
*
- * Copyright (c) 2009-2012 LSI Corporation.
+ * Copyright (c) 2009-2013 LSI Corporation
+ * Copyright (c) 2013-2014 Avago Technologies
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* FILE: megaraid_sas_fusion.c
*
- * Authors: LSI Corporation
+ * Authors: Avago Technologies
* Sumant Patro
- * Adam Radford <linuxraid@lsi.com>
+ * Adam Radford
+ * Kashyap Desai <kashyap.desai@avagotech.com>
+ * Sumit Saxena <sumit.saxena@avagotech.com>
*
- * Send feedback to: <megaraidlinux@lsi.com>
+ * Send feedback to: megaraidlinux.pdl@avagotech.com
*
- * Mail to: LSI Corporation, 1621 Barber Lane, Milpitas, CA 95035
- * ATTN: Linuxraid
+ * Mail to: Avago Technologies, 350 West Trimble Road, Building 90,
+ * San Jose, California 95131
*/
#include <linux/kernel.h>
map = fusion->ld_drv_map[instance->map_id & 1];
- num_lds = le32_to_cpu(map->raidMap.ldCount);
+ num_lds = le16_to_cpu(map->raidMap.ldCount);
dcmd = &cmd->frame->dcmd;
goto fail_ioc_init;
megasas_display_intel_branding(instance);
- if (megasas_get_ctrl_info(instance, instance->ctrl_info)) {
+ if (megasas_get_ctrl_info(instance)) {
dev_err(&instance->pdev->dev,
"Could not get controller info. Fail from %s %d\n",
__func__, __LINE__);
goto fail_ioc_init;
}
- instance->supportmax256vd =
- instance->ctrl_info->adapterOperations3.supportMaxExtLDs;
- /* Below is additional check to address future FW enhancement */
- if (instance->ctrl_info->max_lds > 64)
- instance->supportmax256vd = 1;
- instance->drv_supported_vd_count = MEGASAS_MAX_LD_CHANNELS
- * MEGASAS_MAX_DEV_PER_CHANNEL;
- instance->drv_supported_pd_count = MEGASAS_MAX_PD_CHANNELS
- * MEGASAS_MAX_DEV_PER_CHANNEL;
- if (instance->supportmax256vd) {
- instance->fw_supported_vd_count = MAX_LOGICAL_DRIVES_EXT;
- instance->fw_supported_pd_count = MAX_PHYSICAL_DEVICES;
- } else {
- instance->fw_supported_vd_count = MAX_LOGICAL_DRIVES;
- instance->fw_supported_pd_count = MAX_PHYSICAL_DEVICES;
- }
- dev_info(&instance->pdev->dev, "Firmware supports %d VDs %d PDs\n"
- "Driver supports %d VDs %d PDs\n",
- instance->fw_supported_vd_count,
- instance->fw_supported_pd_count,
- instance->drv_supported_vd_count,
- instance->drv_supported_pd_count);
-
instance->flag_ieee = 1;
fusion->fast_path_io = 0;
- fusion->old_map_sz =
- sizeof(struct MR_FW_RAID_MAP) + (sizeof(struct MR_LD_SPAN_MAP) *
- (instance->fw_supported_vd_count - 1));
- fusion->new_map_sz =
- sizeof(struct MR_FW_RAID_MAP_EXT);
- fusion->drv_map_sz =
- sizeof(struct MR_DRV_RAID_MAP) + (sizeof(struct MR_LD_SPAN_MAP) *
- (instance->drv_supported_vd_count - 1));
-
fusion->drv_map_pages = get_order(fusion->drv_map_sz);
for (i = 0; i < 2; i++) {
fusion->ld_map[i] = NULL;
fusion->drv_map_pages);
goto fail_ioc_init;
}
+ memset(fusion->ld_drv_map[i], 0,
+ ((1 << PAGE_SHIFT) << fusion->drv_map_pages));
}
- fusion->max_map_sz = max(fusion->old_map_sz, fusion->new_map_sz);
-
- if (instance->supportmax256vd)
- fusion->current_map_sz = fusion->new_map_sz;
- else
- fusion->current_map_sz = fusion->old_map_sz;
-
-
for (i = 0; i < 2; i++) {
fusion->ld_map[i] = dma_alloc_coherent(&instance->pdev->dev,
fusion->max_map_sz,
struct megasas_register_set __iomem *regs)
{
#if defined(writeq) && defined(CONFIG_64BIT)
- u64 req_data = (((u64)req_desc_hi << 32) | (u32)req_desc_lo);
+ u64 req_data = (((u64)le32_to_cpu(req_desc_hi) << 32) |
+ le32_to_cpu(req_desc_lo));
- writeq(le64_to_cpu(req_data), &(regs)->inbound_low_queue_port);
+ writeq(req_data, &(regs)->inbound_low_queue_port);
#else
unsigned long flags;
/* Logical block reference tag */
io_request->CDB.EEDP32.PrimaryReferenceTag =
cpu_to_be32(ref_tag);
- io_request->CDB.EEDP32.PrimaryApplicationTagMask = 0xffff;
+ io_request->CDB.EEDP32.PrimaryApplicationTagMask = cpu_to_be16(0xffff);
io_request->IoFlags = cpu_to_le16(32); /* Specify 32-byte cdb */
/* Transfer length */
/* set RAID context values */
pRAID_Context->regLockFlags = REGION_TYPE_SHARED_READ;
- pRAID_Context->timeoutValue = raid->fpIoTimeoutForLd;
+ pRAID_Context->timeoutValue = cpu_to_le16(raid->fpIoTimeoutForLd);
pRAID_Context->VirtualDiskTgtId = cpu_to_le16(device_id);
pRAID_Context->regLockRowLBA = 0;
pRAID_Context->regLockLength = 0;
* megasas_complete_cmd
*/
- if (frame_hdr->flags & MFI_FRAME_DONT_POST_IN_REPLY_QUEUE)
+ if (frame_hdr->flags & cpu_to_le16(MFI_FRAME_DONT_POST_IN_REPLY_QUEUE))
cmd->flags = MFI_FRAME_DONT_POST_IN_REPLY_QUEUE;
fusion = instance->ctrl_context;
"memory allocation failed at index %d\n", i);
break;
}
+ memset(instance->crash_buf[i], 0,
+ ((1 << PAGE_SHIFT) << instance->crash_buf_pages));
}
instance->drv_buf_alloc = i;
}
}
}
- clear_bit(MEGASAS_FUSION_IN_RESET,
- &instance->reset_flags);
- instance->instancet->enable_intr(instance);
- instance->adprecovery = MEGASAS_HBA_OPERATIONAL;
-
+ if (megasas_get_ctrl_info(instance)) {
+ dev_info(&instance->pdev->dev,
+ "Failed from %s %d\n",
+ __func__, __LINE__);
+ instance->adprecovery =
+ MEGASAS_HW_CRITICAL_ERROR;
+ megaraid_sas_kill_hba(instance);
+ retval = FAILED;
+ }
/* Reset load balance info */
memset(fusion->load_balance_info, 0,
sizeof(struct LD_LOAD_BALANCE_INFO)
if (!megasas_get_map_info(instance))
megasas_sync_map_info(instance);
+ clear_bit(MEGASAS_FUSION_IN_RESET,
+ &instance->reset_flags);
+ instance->instancet->enable_intr(instance);
+ instance->adprecovery = MEGASAS_HBA_OPERATIONAL;
+
/* Restart SR-IOV heartbeat */
if (instance->requestorId) {
if (!megasas_sriov_start_heartbeat(instance, 0))
"successful for scsi%d.\n",
instance->host->host_no);
- if (instance->crash_dump_drv_support) {
- if (instance->crash_dump_app_support)
- megasas_set_crash_dump_params(instance,
- MR_CRASH_BUF_TURN_ON);
- else
- megasas_set_crash_dump_params(instance,
- MR_CRASH_BUF_TURN_OFF);
- }
+ if (instance->crash_dump_drv_support &&
+ instance->crash_dump_app_support)
+ megasas_set_crash_dump_params(instance,
+ MR_CRASH_BUF_TURN_ON);
+ else
+ megasas_set_crash_dump_params(instance,
+ MR_CRASH_BUF_TURN_OFF);
+
retval = SUCCESS;
goto out;
}
/*
* Linux MegaRAID driver for SAS based RAID controllers
*
- * Copyright (c) 2009-2012 LSI Corporation.
+ * Copyright (c) 2009-2013 LSI Corporation
+ * Copyright (c) 2013-2014 Avago Technologies
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* FILE: megaraid_sas_fusion.h
*
- * Authors: LSI Corporation
+ * Authors: Avago Technologies
* Manoj Jose
* Sumant Patro
+ * Kashyap Desai <kashyap.desai@avagotech.com>
+ * Sumit Saxena <sumit.saxena@avagotech.com>
*
- * Send feedback to: <megaraidlinux@lsi.com>
+ * Send feedback to: megaraidlinux.pdl@avagotech.com
*
- * Mail to: LSI Corporation, 1621 Barber Lane, Milpitas, CA 95035
- * ATTN: Linuxraid
+ * Mail to: Avago Technologies, 350 West Trimble Road, Building 90,
+ * San Jose, California 95131
*/
#ifndef _MEGARAID_SAS_FUSION_H_
u32 max_map_sz;
u32 current_map_sz;
- u32 old_map_sz;
- u32 new_map_sz;
u32 drv_map_sz;
u32 drv_map_pages;
u8 fast_path_io;
}
/**
- * _scsih_adjust_queue_depth - setting device queue depth
+ * _scsih_change_queue_depth - setting device queue depth
* @sdev: scsi device struct
* @qdepth: requested queue depth
*
- *
- * Returns nothing
+ * Returns queue depth.
*/
-static void
-_scsih_adjust_queue_depth(struct scsi_device *sdev, int qdepth)
+static int
+_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
{
struct Scsi_Host *shost = sdev->host;
int max_depth;
spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
not_sata:
-
if (!sdev->tagged_supported)
max_depth = 1;
if (qdepth > max_depth)
qdepth = max_depth;
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
-}
-
-/**
- * _scsih_change_queue_depth - setting device queue depth
- * @sdev: scsi device struct
- * @qdepth: requested queue depth
- * @reason: SCSI_QDEPTH_DEFAULT/SCSI_QDEPTH_QFULL/SCSI_QDEPTH_RAMP_UP
- * (see include/scsi/scsi_host.h for definition)
- *
- * Returns queue depth.
- */
-static int
-_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
-{
- if (reason == SCSI_QDEPTH_DEFAULT || reason == SCSI_QDEPTH_RAMP_UP)
- _scsih_adjust_queue_depth(sdev, qdepth);
- else if (reason == SCSI_QDEPTH_QFULL)
- scsi_track_queue_full(sdev, qdepth);
- else
- return -EOPNOTSUPP;
-
- if (sdev->inquiry_len > 7)
- sdev_printk(KERN_INFO, sdev, "qdepth(%d), tagged(%d), "
- "simple(%d), ordered(%d), scsi_level(%d), cmd_que(%d)\n",
- sdev->queue_depth, sdev->tagged_supported, sdev->simple_tags,
- sdev->ordered_tags, sdev->scsi_level,
- (sdev->inquiry[7] & 2) >> 1);
-
- return sdev->queue_depth;
-}
-
-/**
- * _scsih_change_queue_type - changing device queue tag type
- * @sdev: scsi device struct
- * @tag_type: requested tag type
- *
- * Returns queue tag type.
- */
-static int
-_scsih_change_queue_type(struct scsi_device *sdev, int tag_type)
-{
- if (sdev->tagged_supported) {
- scsi_set_tag_type(sdev, tag_type);
- if (tag_type)
- scsi_activate_tcq(sdev, sdev->queue_depth);
- else
- scsi_deactivate_tcq(sdev, sdev->queue_depth);
- } else
- tag_type = 0;
-
- return tag_type;
+ return scsi_change_queue_depth(sdev, qdepth);
}
/**
r_level, raid_device->handle,
(unsigned long long)raid_device->wwid,
raid_device->num_pds, ds);
- _scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT);
+ _scsih_change_queue_depth(sdev, qdepth);
/* raid transport support */
if (!ioc->is_warpdrive)
_scsih_set_level(sdev, raid_device->volume_type);
_scsih_display_sata_capabilities(ioc, handle, sdev);
- _scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT);
+ _scsih_change_queue_depth(sdev, qdepth);
if (ssp_target) {
sas_read_port_mode_page(sdev);
mpi_control = MPI2_SCSIIO_CONTROL_NODATATRANSFER;
/* set tags */
- if (!(sas_device_priv_data->flags & MPT_DEVICE_FLAGS_INIT)) {
- if (scmd->device->tagged_supported) {
- if (scmd->device->ordered_tags)
- mpi_control |= MPI2_SCSIIO_CONTROL_ORDEREDQ;
- else
- mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ;
- } else
- mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ;
- } else
- mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ;
+ mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ;
+
/* Make sure Device is not raid volume.
* We do not expose raid functionality to upper layer for warpdrive.
*/
.scan_finished = _scsih_scan_finished,
.scan_start = _scsih_scan_start,
.change_queue_depth = _scsih_change_queue_depth,
- .change_queue_type = _scsih_change_queue_type,
+ .change_queue_type = scsi_change_queue_type,
.eh_abort_handler = _scsih_abort,
.eh_device_reset_handler = _scsih_dev_reset,
.eh_target_reset_handler = _scsih_target_reset,
.use_clustering = ENABLE_CLUSTERING,
.shost_attrs = mpt2sas_host_attrs,
.sdev_attrs = mpt2sas_dev_attrs,
+ .track_queue_depth = 1,
};
/**
return found;
}
-
-static void
-_scsih_adjust_queue_depth(struct scsi_device *sdev, int qdepth)
+/**
+ * _scsih_change_queue_depth - setting device queue depth
+ * @sdev: scsi device struct
+ * @qdepth: requested queue depth
+ *
+ * Returns queue depth.
+ */
+static int
+_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
{
struct Scsi_Host *shost = sdev->host;
int max_depth;
max_depth = 1;
if (qdepth > max_depth)
qdepth = max_depth;
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
+ return scsi_change_queue_depth(sdev, qdepth);
}
-/**
- * _scsih_change_queue_depth - setting device queue depth
- * @sdev: scsi device struct
- * @qdepth: requested queue depth
- * @reason: SCSI_QDEPTH_DEFAULT/SCSI_QDEPTH_QFULL/SCSI_QDEPTH_RAMP_UP
- * (see include/scsi/scsi_host.h for definition)
- *
- * Returns queue depth.
- */
-static int
-_scsih_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
-{
- if (reason == SCSI_QDEPTH_DEFAULT || reason == SCSI_QDEPTH_RAMP_UP)
- _scsih_adjust_queue_depth(sdev, qdepth);
- else if (reason == SCSI_QDEPTH_QFULL)
- scsi_track_queue_full(sdev, qdepth);
- else
- return -EOPNOTSUPP;
-
- if (sdev->inquiry_len > 7)
- sdev_printk(KERN_INFO, sdev, "qdepth(%d), tagged(%d), " \
- "simple(%d), ordered(%d), scsi_level(%d), cmd_que(%d)\n",
- sdev->queue_depth, sdev->tagged_supported, sdev->simple_tags,
- sdev->ordered_tags, sdev->scsi_level,
- (sdev->inquiry[7] & 2) >> 1);
-
- return sdev->queue_depth;
-}
-
-/**
- * _scsih_change_queue_type - changing device queue tag type
- * @sdev: scsi device struct
- * @tag_type: requested tag type
- *
- * Returns queue tag type.
- */
-static int
-_scsih_change_queue_type(struct scsi_device *sdev, int tag_type)
-{
- if (sdev->tagged_supported) {
- scsi_set_tag_type(sdev, tag_type);
- if (tag_type)
- scsi_activate_tcq(sdev, sdev->queue_depth);
- else
- scsi_deactivate_tcq(sdev, sdev->queue_depth);
- } else
- tag_type = 0;
-
- return tag_type;
-}
-
-
/**
* _scsih_target_alloc - target add routine
* @starget: scsi target struct
raid_device->num_pds, ds);
- _scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT);
+ _scsih_change_queue_depth(sdev, qdepth);
/* raid transport support */
_scsih_set_level(sdev, raid_device->volume_type);
_scsih_display_sata_capabilities(ioc, handle, sdev);
- _scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT);
+ _scsih_change_queue_depth(sdev, qdepth);
if (ssp_target) {
sas_read_port_mode_page(sdev);
mpi_control = MPI2_SCSIIO_CONTROL_NODATATRANSFER;
/* set tags */
- if (!(sas_device_priv_data->flags & MPT_DEVICE_FLAGS_INIT)) {
- if (scmd->device->tagged_supported) {
- if (scmd->device->ordered_tags)
- mpi_control |= MPI2_SCSIIO_CONTROL_ORDEREDQ;
- else
- mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ;
- } else
- mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ;
- } else
- mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ;
+ mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ;
if ((sas_device_priv_data->flags & MPT_DEVICE_TLR_ON) &&
scmd->cmd_len != 32)
.scan_finished = _scsih_scan_finished,
.scan_start = _scsih_scan_start,
.change_queue_depth = _scsih_change_queue_depth,
- .change_queue_type = _scsih_change_queue_type,
+ .change_queue_type = scsi_change_queue_type,
.eh_abort_handler = _scsih_abort,
.eh_device_reset_handler = _scsih_dev_reset,
.eh_target_reset_handler = _scsih_target_reset,
.use_clustering = ENABLE_CLUSTERING,
.shost_attrs = mpt3sas_host_attrs,
.sdev_attrs = mpt3sas_dev_attrs,
+ .track_queue_depth = 1,
};
/**
#include "mv_sas.h"
-static int lldd_max_execute_num = 1;
-module_param_named(collector, lldd_max_execute_num, int, S_IRUGO);
-MODULE_PARM_DESC(collector, "\n"
- "\tIf greater than one, tells the SAS Layer to run in Task Collector\n"
- "\tMode. If 1 or 0, tells the SAS Layer to run in Direct Mode.\n"
- "\tThe mvsas SAS LLDD supports both modes.\n"
- "\tDefault: 1 (Direct Mode).\n");
-
int interrupt_coalescing = 0x80;
static struct scsi_transport_template *mvs_stt;
-struct kmem_cache *mvs_task_list_cache;
static const struct mvs_chip_info mvs_chips[] = {
[chip_6320] = { 1, 2, 0x400, 17, 16, 6, 9, &mvs_64xx_dispatch, },
[chip_6440] = { 1, 4, 0x400, 17, 16, 6, 9, &mvs_64xx_dispatch, },
.target_destroy = sas_target_destroy,
.ioctl = sas_ioctl,
.shost_attrs = mvst_host_attrs,
+ .use_blk_tags = 1,
+ .track_queue_depth = 1,
};
static struct sas_domain_function_template mvs_transport_ops = {
sha->num_phys = nr_core * chip_info->n_phy;
- sha->lldd_max_execute_num = lldd_max_execute_num;
-
if (mvi->flags & MVF_FLAG_SOC)
can_queue = MVS_SOC_CAN_QUEUE;
else
can_queue = MVS_CHIP_SLOT_SZ;
- sha->lldd_queue_size = can_queue;
shost->sg_tablesize = min_t(u16, SG_ALL, MVS_MAX_SG);
shost->can_queue = can_queue;
mvi->shost->cmd_per_lun = MVS_QUEUE_SIZE;
if (!mvs_stt)
return -ENOMEM;
- mvs_task_list_cache = kmem_cache_create("mvs_task_list", sizeof(struct mvs_task_list),
- 0, SLAB_HWCACHE_ALIGN, NULL);
- if (!mvs_task_list_cache) {
- rc = -ENOMEM;
- mv_printk("%s: mvs_task_list_cache alloc failed! \n", __func__);
- goto err_out;
- }
-
rc = pci_register_driver(&mvs_pci_driver);
-
if (rc)
goto err_out;
{
pci_unregister_driver(&mvs_pci_driver);
sas_release_transport(mvs_stt);
- kmem_cache_destroy(mvs_task_list_cache);
}
struct device_attribute *mvst_host_attrs[] = {
return rc;
}
-static struct mvs_task_list *mvs_task_alloc_list(int *num, gfp_t gfp_flags)
-{
- struct mvs_task_list *first = NULL;
-
- for (; *num > 0; --*num) {
- struct mvs_task_list *mvs_list = kmem_cache_zalloc(mvs_task_list_cache, gfp_flags);
-
- if (!mvs_list)
- break;
-
- INIT_LIST_HEAD(&mvs_list->list);
- if (!first)
- first = mvs_list;
- else
- list_add_tail(&mvs_list->list, &first->list);
-
- }
-
- return first;
-}
-
-static inline void mvs_task_free_list(struct mvs_task_list *mvs_list)
-{
- LIST_HEAD(list);
- struct list_head *pos, *a;
- struct mvs_task_list *mlist = NULL;
-
- __list_add(&list, mvs_list->list.prev, &mvs_list->list);
-
- list_for_each_safe(pos, a, &list) {
- list_del_init(pos);
- mlist = list_entry(pos, struct mvs_task_list, list);
- kmem_cache_free(mvs_task_list_cache, mlist);
- }
-}
-
-static int mvs_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags,
+static int mvs_task_exec(struct sas_task *task, gfp_t gfp_flags,
struct completion *completion, int is_tmf,
struct mvs_tmf_task *tmf)
{
return rc;
}
-static int mvs_collector_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags,
- struct completion *completion, int is_tmf,
- struct mvs_tmf_task *tmf)
+int mvs_queue_command(struct sas_task *task, gfp_t gfp_flags)
{
- struct domain_device *dev = task->dev;
- struct mvs_prv_info *mpi = dev->port->ha->lldd_ha;
- struct mvs_info *mvi = NULL;
- struct sas_task *t = task;
- struct mvs_task_list *mvs_list = NULL, *a;
- LIST_HEAD(q);
- int pass[2] = {0};
- u32 rc = 0;
- u32 n = num;
- unsigned long flags = 0;
-
- mvs_list = mvs_task_alloc_list(&n, gfp_flags);
- if (n) {
- printk(KERN_ERR "%s: mvs alloc list failed.\n", __func__);
- rc = -ENOMEM;
- goto free_list;
- }
-
- __list_add(&q, mvs_list->list.prev, &mvs_list->list);
-
- list_for_each_entry(a, &q, list) {
- a->task = t;
- t = list_entry(t->list.next, struct sas_task, list);
- }
-
- list_for_each_entry(a, &q , list) {
-
- t = a->task;
- mvi = ((struct mvs_device *)t->dev->lldd_dev)->mvi_info;
-
- spin_lock_irqsave(&mvi->lock, flags);
- rc = mvs_task_prep(t, mvi, is_tmf, tmf, &pass[mvi->id]);
- if (rc)
- dev_printk(KERN_ERR, mvi->dev, "mvsas exec failed[%d]!\n", rc);
- spin_unlock_irqrestore(&mvi->lock, flags);
- }
-
- if (likely(pass[0]))
- MVS_CHIP_DISP->start_delivery(mpi->mvi[0],
- (mpi->mvi[0]->tx_prod - 1) & (MVS_CHIP_SLOT_SZ - 1));
-
- if (likely(pass[1]))
- MVS_CHIP_DISP->start_delivery(mpi->mvi[1],
- (mpi->mvi[1]->tx_prod - 1) & (MVS_CHIP_SLOT_SZ - 1));
-
- list_del_init(&q);
-
-free_list:
- if (mvs_list)
- mvs_task_free_list(mvs_list);
-
- return rc;
-}
-
-int mvs_queue_command(struct sas_task *task, const int num,
- gfp_t gfp_flags)
-{
- struct mvs_device *mvi_dev = task->dev->lldd_dev;
- struct sas_ha_struct *sas = mvi_dev->mvi_info->sas;
-
- if (sas->lldd_max_execute_num < 2)
- return mvs_task_exec(task, num, gfp_flags, NULL, 0, NULL);
- else
- return mvs_collector_task_exec(task, num, gfp_flags, NULL, 0, NULL);
+ return mvs_task_exec(task, gfp_flags, NULL, 0, NULL);
}
static void mvs_slot_free(struct mvs_info *mvi, u32 rx_desc)
task->slow_task->timer.expires = jiffies + MVS_TASK_TIMEOUT*HZ;
add_timer(&task->slow_task->timer);
- res = mvs_task_exec(task, 1, GFP_KERNEL, NULL, 1, tmf);
+ res = mvs_task_exec(task, GFP_KERNEL, NULL, 1, tmf);
if (res) {
del_timer(&task->slow_task->timer);
extern struct mvs_info *tgt_mvi;
extern const struct mvs_dispatch mvs_64xx_dispatch;
extern const struct mvs_dispatch mvs_94xx_dispatch;
-extern struct kmem_cache *mvs_task_list_cache;
#define DEV_IS_EXPANDER(type) \
((type == SAS_EDGE_EXPANDER_DEVICE) || (type == SAS_FANOUT_EXPANDER_DEVICE))
int n_elem;
};
-struct mvs_task_list {
- struct sas_task *task;
- struct list_head list;
-};
-
-
/******************** function prototype *********************/
void mvs_get_sas_addr(void *buf, u32 buflen);
void mvs_tag_clear(struct mvs_info *mvi, u32 tag);
u32 off_hi, u64 sas_addr);
void mvs_scan_start(struct Scsi_Host *shost);
int mvs_scan_finished(struct Scsi_Host *shost, unsigned long time);
-int mvs_queue_command(struct sas_task *task, const int num,
- gfp_t gfp_flags);
+int mvs_queue_command(struct sas_task *task, gfp_t gfp_flags);
int mvs_abort_task(struct sas_task *task);
int mvs_abort_task_set(struct domain_device *dev, u8 *lun);
int mvs_clear_aca(struct domain_device *dev, u8 *lun);
if (depth_to_use > MAX_TAGS)
depth_to_use = MAX_TAGS;
- scsi_adjust_queue_depth(device,
- (device->tagged_supported ?
- MSG_SIMPLE_TAG : 0),
- depth_to_use);
+ scsi_change_queue_depth(device, depth_to_use);
/*
** Since the queue depth is not tunable under Linux,
*/
static struct scsi_driver osd_driver = {
- .owner = THIS_MODULE,
.gendrv = {
.name = osd_name,
+ .owner = THIS_MODULE,
.probe = osd_probe,
.remove = osd_remove,
}
static int osst_remove(struct device *);
static struct scsi_driver osst_template = {
- .owner = THIS_MODULE,
.gendrv = {
.name = "osst",
+ .owner = THIS_MODULE,
.probe = osst_probe,
.remove = osst_remove,
}
SRpnt->cmd[0], SRpnt->cmd[1], SRpnt->cmd[2],
SRpnt->cmd[3], SRpnt->cmd[4], SRpnt->cmd[5]);
if (scode) printk(OSST_DEB_MSG "%s:D: Sense: %02x, ASC: %02x, ASCQ: %02x\n",
- name, scode, sense[12], sense[13]);
+ name, scode, sense[12], sense[13]);
if (cmdstatp->have_sense)
- __scsi_print_sense("osst ", SRpnt->sense, SCSI_SENSE_BUFFERSIZE);
+ __scsi_print_sense(STp->device, name,
+ SRpnt->sense, SCSI_SENSE_BUFFERSIZE);
}
else
#endif
SRpnt->cmd[0] != TEST_UNIT_READY)) { /* Abnormal conditions for tape */
if (cmdstatp->have_sense) {
printk(KERN_WARNING "%s:W: Command with sense data:\n", name);
- __scsi_print_sense("osst ", SRpnt->sense, SCSI_SENSE_BUFFERSIZE);
+ __scsi_print_sense(STp->device, name,
+ SRpnt->sense, SCSI_SENSE_BUFFERSIZE);
}
else {
static int notyetprinted = 1;
/* Lock or unlock the drive door. Don't use when struct osst_request allocated. */
static int do_door_lock(struct osst_tape * STp, int do_lock)
{
- int retval, cmd;
+ int retval;
- cmd = do_lock ? SCSI_IOCTL_DOORLOCK : SCSI_IOCTL_DOORUNLOCK;
#if DEBUG
printk(OSST_DEB_MSG "%s:D: %socking drive door.\n", tape_name(STp), do_lock ? "L" : "Unl");
#endif
- retval = scsi_ioctl(STp->device, cmd, NULL);
- if (!retval) {
+
+ retval = scsi_set_medium_removal(STp->device,
+ do_lock ? SCSI_REMOVAL_PREVENT : SCSI_REMOVAL_ALLOW);
+ if (!retval)
STp->door_locked = do_lock ? ST_LOCKED_EXPLICIT : ST_UNLOCKED;
- }
- else {
+ else
STp->door_locked = ST_LOCK_FAILS;
- }
return retval;
}
* may try and take the device offline, in which case all further
* access to the device is prohibited.
*/
- if( !scsi_block_when_processing_errors(STp->device) ) {
- retval = (-ENXIO);
+ retval = scsi_ioctl_block_when_processing_errors(STp->device, cmd_in,
+ file->f_flags & O_NDELAY);
+ if (retval)
goto out;
- }
cmd_type = _IOC_TYPE(cmd_in);
cmd_nr = _IOC_NR(cmd_in);
-#define AUTOSENSE
#define PSEUDO_DMA
-#define FOO
#define UNSAFE /* Not unsafe for PAS16 -- use it */
#define PDEBUG 0
* Media Vision
* (510) 770-8600
* (800) 348-7116
- *
- * and
- *
- * NCR 5380 Family
- * SCSI Protocol Controller
- * Databook
- *
- * NCR Microelectronics
- * 1635 Aeroplaza Drive
- * Colorado Springs, CO 80916
- * 1+ (719) 578-3400
- * 1+ (800) 334-5454
*/
/*
- * Options :
- * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
- * for commands that return with a CHECK CONDITION status.
- *
- * LIMIT_TRANSFERSIZE - if defined, limit the pseudo-dma transfers to 512
- * bytes at a time. Since interrupts are disabled by default during
- * these transfers, we might need this to give reasonable interrupt
- * service time if the transfer size gets too large.
- *
- * PSEUDO_DMA - enables PSEUDO-DMA hardware, should give a 3-4X performance
- * increase compared to polled I/O.
- *
- * PARITY - enable parity checking. Not supported.
- *
- * SCSI2 - enable support for SCSI-II tagged queueing. Untested.
- *
- * UNSAFE - leave interrupts enabled during pseudo-DMA transfers. This
- * parameter comes from the NCR5380 code. It is NOT unsafe with
- * the PAS16 and you should use it. If you don't you will have
- * a problem with dropped characters during high speed
- * communications during SCSI transfers. If you really don't
- * want to use UNSAFE you can try defining LIMIT_TRANSFERSIZE or
- * twiddle with the transfer size in the high level code.
- *
- * USLEEP - enable support for devices that don't disconnect. Untested.
- *
* The card is detected and initialized in one of several ways :
* 1. Autoprobe (default) - There are many different models of
* the Pro Audio Spectrum/Studio 16, and I only have one of
* If you have problems with your card not being recognized, use
* the LILO command line override. Try to get it recognized without
* interrupts. Ie, for a board at the default 0x388 base port,
- * boot: linux pas16=0x388,255
+ * boot: linux pas16=0x388,0
*
- * SCSI_IRQ_NONE (255) should be specified for no interrupt,
+ * NO_IRQ (0) should be specified for no interrupt,
* IRQ_AUTO (254) to autoprobe for an IRQ line if overridden
* on the command line.
- *
- * (IRQ_AUTO == 254, SCSI_IRQ_NONE == 255 in NCR5380.h)
*/
#include <linux/module.h>
#include <linux/stat.h>
#include <linux/init.h>
-#include "scsi.h"
#include <scsi/scsi_host.h>
#include "pas16.h"
#define AUTOPROBE_IRQ
#include "NCR5380.h"
-static int pas_maxi = 0;
-static int pas_wmaxi = 0;
static unsigned short pas16_addr = 0;
static int pas16_irq = 0;
}
+#ifndef MODULE
/*
* Function : pas16_setup(char *str, int *ints)
*
*
*/
-void __init pas16_setup(char *str, int *ints)
+static int __init pas16_setup(char *str)
{
static int commandline_current = 0;
int i;
+ int ints[10];
+
+ get_options(str, ARRAY_SIZE(ints), ints);
if (ints[0] != 2)
printk("pas16_setup : usage pas16=io_port,irq\n");
else
}
++commandline_current;
}
+ return 1;
}
+__setup("pas16=", pas16_setup);
+#endif
+
/*
* Function : int pas16_detect(struct scsi_host_template * tpnt)
*
*
*/
-int __init pas16_detect(struct scsi_host_template * tpnt)
+static int __init pas16_detect(struct scsi_host_template *tpnt)
{
static int current_override = 0;
static unsigned short current_base = 0;
unsigned short io_port;
int count;
- tpnt->proc_name = "pas16";
- tpnt->show_info = pas16_show_info;
- tpnt->write_info = pas16_write_info;
-
if (pas16_addr != 0) {
overrides[0].io_port = pas16_addr;
/*
else
instance->irq = NCR5380_probe_irq(instance, PAS16_IRQS);
- if (instance->irq != SCSI_IRQ_NONE)
+ /* Compatibility with documented NCR5380 kernel parameters */
+ if (instance->irq == 255)
+ instance->irq = NO_IRQ;
+
+ if (instance->irq != NO_IRQ)
if (request_irq(instance->irq, pas16_intr, 0,
"pas16", instance)) {
printk("scsi%d : IRQ%d not free, interrupts disabled\n",
instance->host_no, instance->irq);
- instance->irq = SCSI_IRQ_NONE;
+ instance->irq = NO_IRQ;
}
- if (instance->irq == SCSI_IRQ_NONE) {
+ if (instance->irq == NO_IRQ) {
printk("scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no);
printk("scsi%d : please jumper the board for a free IRQ.\n", instance->host_no);
/* Disable 5380 interrupts, leave drive params the same */
printk("scsi%d : irq = %d\n", instance->host_no, instance->irq);
#endif
- printk("scsi%d : at 0x%04x", instance->host_no, (int)
- instance->io_port);
- if (instance->irq == SCSI_IRQ_NONE)
- printk (" interrupts disabled");
- else
- printk (" irq %d", instance->irq);
- printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
- CAN_QUEUE, CMD_PER_LUN, PAS16_PUBLIC_RELEASE);
- NCR5380_print_options(instance);
- printk("\n");
-
++current_override;
++count;
}
* and matching the H_C_S coordinates to what DOS uses.
*/
-int pas16_biosparam(struct scsi_device *sdev, struct block_device *dev,
- sector_t capacity, int * ip)
+static int pas16_biosparam(struct scsi_device *sdev, struct block_device *dev,
+ sector_t capacity, int *ip)
{
int size = capacity;
ip[0] = 64;
P_DATA_REG_OFFSET);
register int i = len;
int ii = 0;
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
while ( !(inb(instance->io_port + P_STATUS_REG_OFFSET) & P_ST_RDY) )
++ii;
instance->host_no);
return -1;
}
- if (ii > pas_maxi)
- pas_maxi = ii;
+ if (ii > hostdata->spin_max_r)
+ hostdata->spin_max_r = ii;
return 0;
}
register unsigned short reg = (instance->io_port + P_DATA_REG_OFFSET);
register int i = len;
int ii = 0;
+ struct NCR5380_hostdata *hostdata = shost_priv(instance);
while ( !((inb(instance->io_port + P_STATUS_REG_OFFSET)) & P_ST_RDY) )
++ii;
instance->host_no);
return -1;
}
- if (ii > pas_maxi)
- pas_wmaxi = ii;
+ if (ii > hostdata->spin_max_w)
+ hostdata->spin_max_w = ii;
return 0;
}
static int pas16_release(struct Scsi_Host *shost)
{
- if (shost->irq)
+ if (shost->irq != NO_IRQ)
free_irq(shost->irq, shost);
NCR5380_exit(shost);
if (shost->io_port && shost->n_io_port)
.name = "Pro Audio Spectrum-16 SCSI",
.detect = pas16_detect,
.release = pas16_release,
+ .proc_name = "pas16",
+ .show_info = pas16_show_info,
+ .write_info = pas16_write_info,
+ .info = pas16_info,
.queuecommand = pas16_queue_command,
.eh_abort_handler = pas16_abort,
.eh_bus_reset_handler = pas16_bus_reset,
* Media Vision
* (510) 770-8600
* (800) 348-7116
- *
- * and
- *
- * NCR 5380 Family
- * SCSI Protocol Controller
- * Databook
- *
- * NCR Microelectronics
- * 1635 Aeroplaza Drive
- * Colorado Springs, CO 80916
- * 1+ (719) 578-3400
- * 1+ (800) 334-5454
*/
#ifndef PAS16_H
#define PAS16_H
-#define PAS16_PUBLIC_RELEASE 3
-
#define PDEBUG_INIT 0x1
#define PDEBUG_TRANSFER 0x2
#ifndef ASM
-static int pas16_abort(Scsi_Cmnd *);
-static int pas16_biosparam(struct scsi_device *, struct block_device *,
- sector_t, int*);
-static int pas16_detect(struct scsi_host_template *);
-static int pas16_queue_command(struct Scsi_Host *, struct scsi_cmnd *);
-static int pas16_bus_reset(Scsi_Cmnd *);
#ifndef CMD_PER_LUN
#define CMD_PER_LUN 2
#define NCR5380_queue_command pas16_queue_command
#define NCR5380_abort pas16_abort
#define NCR5380_bus_reset pas16_bus_reset
+#define NCR5380_info pas16_info
#define NCR5380_show_info pas16_show_info
#define NCR5380_write_info pas16_write_info
.target_destroy = sas_target_destroy,
.ioctl = sas_ioctl,
.shost_attrs = pm8001_host_attrs,
+ .use_blk_tags = 1,
+ .track_queue_depth = 1,
};
/**
sha->lldd_module = THIS_MODULE;
sha->sas_addr = &pm8001_ha->sas_addr[0];
sha->num_phys = chip_info->n_phy;
- sha->lldd_max_execute_num = 1;
- sha->lldd_queue_size = PM8001_CAN_QUEUE;
sha->core.shost = shost;
}
*/
#define DEV_IS_GONE(pm8001_dev) \
((!pm8001_dev || (pm8001_dev->dev_type == SAS_PHY_UNUSED)))
-static int pm8001_task_exec(struct sas_task *task, const int num,
+static int pm8001_task_exec(struct sas_task *task,
gfp_t gfp_flags, int is_tmf, struct pm8001_tmf_task *tmf)
{
struct domain_device *dev = task->dev;
struct sas_task *t = task;
struct pm8001_ccb_info *ccb;
u32 tag = 0xdeadbeef, rc, n_elem = 0;
- u32 n = num;
unsigned long flags = 0;
if (!dev->port) {
spin_unlock_irqrestore(&pm8001_ha->lock, flags);
t->task_done(t);
spin_lock_irqsave(&pm8001_ha->lock, flags);
- if (n > 1)
- t = list_entry(t->list.next,
- struct sas_task, list);
continue;
} else {
struct task_status_struct *ts = &t->task_status;
ts->resp = SAS_TASK_UNDELIVERED;
ts->stat = SAS_PHY_DOWN;
t->task_done(t);
- if (n > 1)
- t = list_entry(t->list.next,
- struct sas_task, list);
continue;
}
}
t->task_state_flags |= SAS_TASK_AT_INITIATOR;
spin_unlock(&t->task_state_lock);
pm8001_dev->running_req++;
- if (n > 1)
- t = list_entry(t->list.next, struct sas_task, list);
- } while (--n);
+ } while (0);
rc = 0;
goto out_done;
* pm8001_queue_command - register for upper layer used, all IO commands sent
* to HBA are from this interface.
* @task: the task to be execute.
- * @num: if can_queue great than 1, the task can be queued up. for SMP task,
- * we always execute one one time
* @gfp_flags: gfp_flags
*/
-int pm8001_queue_command(struct sas_task *task, const int num,
- gfp_t gfp_flags)
+int pm8001_queue_command(struct sas_task *task, gfp_t gfp_flags)
{
- return pm8001_task_exec(task, num, gfp_flags, 0, NULL);
+ return pm8001_task_exec(task, gfp_flags, 0, NULL);
}
/**
task->slow_task->timer.expires = jiffies + PM8001_TASK_TIMEOUT*HZ;
add_timer(&task->slow_task->timer);
- res = pm8001_task_exec(task, 1, GFP_KERNEL, 1, tmf);
+ res = pm8001_task_exec(task, GFP_KERNEL, 1, tmf);
if (res) {
del_timer(&task->slow_task->timer);
void *funcdata);
void pm8001_scan_start(struct Scsi_Host *shost);
int pm8001_scan_finished(struct Scsi_Host *shost, unsigned long time);
-int pm8001_queue_command(struct sas_task *task, const int num,
- gfp_t gfp_flags);
+int pm8001_queue_command(struct sas_task *task, gfp_t gfp_flags);
int pm8001_abort_task(struct sas_task *task);
int pm8001_abort_task_set(struct domain_device *dev, u8 *lun);
int pm8001_clear_aca(struct domain_device *dev, u8 *lun);
PMCRAID_VSET_MAX_SECTORS);
}
- if (scsi_dev->tagged_supported &&
- (RES_IS_GSCSI(res->cfg_entry) || RES_IS_VSET(res->cfg_entry))) {
- scsi_activate_tcq(scsi_dev, scsi_dev->queue_depth);
- scsi_adjust_queue_depth(scsi_dev, MSG_SIMPLE_TAG,
- scsi_dev->host->cmd_per_lun);
- } else {
- scsi_adjust_queue_depth(scsi_dev, 0,
- scsi_dev->host->cmd_per_lun);
- }
+ /*
+ * We never want to report TCQ support for these types of devices.
+ */
+ if (!RES_IS_GSCSI(res->cfg_entry) && !RES_IS_VSET(res->cfg_entry))
+ scsi_dev->tagged_supported = 0;
return 0;
}
* pmcraid_change_queue_depth - Change the device's queue depth
* @scsi_dev: scsi device struct
* @depth: depth to set
- * @reason: calling context
*
* Return value
* actual depth set
*/
-static int pmcraid_change_queue_depth(struct scsi_device *scsi_dev, int depth,
- int reason)
+static int pmcraid_change_queue_depth(struct scsi_device *scsi_dev, int depth)
{
- if (reason != SCSI_QDEPTH_DEFAULT)
- return -EOPNOTSUPP;
-
if (depth > PMCRAID_MAX_CMD_PER_LUN)
depth = PMCRAID_MAX_CMD_PER_LUN;
-
- scsi_adjust_queue_depth(scsi_dev, scsi_get_tag_type(scsi_dev), depth);
-
- return scsi_dev->queue_depth;
+ return scsi_change_queue_depth(scsi_dev, depth);
}
-/**
- * pmcraid_change_queue_type - Change the device's queue type
- * @scsi_dev: scsi device struct
- * @tag: type of tags to use
- *
- * Return value:
- * actual queue type set
- */
-static int pmcraid_change_queue_type(struct scsi_device *scsi_dev, int tag)
-{
- struct pmcraid_resource_entry *res;
-
- res = (struct pmcraid_resource_entry *)scsi_dev->hostdata;
-
- if ((res) && scsi_dev->tagged_supported &&
- (RES_IS_GSCSI(res->cfg_entry) || RES_IS_VSET(res->cfg_entry))) {
- scsi_set_tag_type(scsi_dev, tag);
-
- if (tag)
- scsi_activate_tcq(scsi_dev, scsi_dev->queue_depth);
- else
- scsi_deactivate_tcq(scsi_dev, scsi_dev->queue_depth);
- } else
- tag = 0;
-
- return tag;
-}
-
-
/**
* pmcraid_init_cmdblk - initializes a command block
*
return pmcraid_reset_bringup(pinstance) == 0 ? SUCCESS : FAILED;
}
-/**
- * pmcraid_task_attributes - Translate SPI Q-Tags to task attributes
- * @scsi_cmd: scsi command struct
- *
- * Return value
- * number of tags or 0 if the task is not tagged
- */
-static u8 pmcraid_task_attributes(struct scsi_cmnd *scsi_cmd)
-{
- char tag[2];
- u8 rc = 0;
-
- if (scsi_populate_tag_msg(scsi_cmd, tag)) {
- switch (tag[0]) {
- case MSG_SIMPLE_TAG:
- rc = TASK_TAG_SIMPLE;
- break;
- case MSG_HEAD_TAG:
- rc = TASK_TAG_QUEUE_HEAD;
- break;
- case MSG_ORDERED_TAG:
- rc = TASK_TAG_ORDERED;
- break;
- };
- }
-
- return rc;
-}
-
-
/**
* pmcraid_init_ioadls - initializes IOADL related fields in IOARCB
* @cmd: pmcraid command struct
}
ioarcb->request_flags0 |= NO_LINK_DESCS;
- ioarcb->request_flags1 |= pmcraid_task_attributes(scsi_cmd);
+
+ if (scsi_cmd->flags & SCMD_TAGGED)
+ ioarcb->request_flags1 |= TASK_TAG_SIMPLE;
if (RES_IS_GSCSI(res->cfg_entry))
ioarcb->request_flags1 |= DELAY_AFTER_RESET;
.slave_configure = pmcraid_slave_configure,
.slave_destroy = pmcraid_slave_destroy,
.change_queue_depth = pmcraid_change_queue_depth,
- .change_queue_type = pmcraid_change_queue_type,
+ .change_queue_type = scsi_change_queue_type,
.can_queue = PMCRAID_MAX_IO_CMD,
.this_id = -1,
.sg_tablesize = PMCRAID_MAX_IOADLS,
.cmd_per_lun = PMCRAID_MAX_CMD_PER_LUN,
.use_clustering = ENABLE_CLUSTERING,
.shost_attrs = pmcraid_host_attrs,
- .proc_name = PMCRAID_DRIVER_NAME
+ .proc_name = PMCRAID_DRIVER_NAME,
+ .use_blk_tags = 1,
};
/*
unsigned char opcode;
int res;
-#ifdef DEBUG
- scsi_print_command(cmd);
-#endif
-
priv->curr_cmd = cmd;
cmd->scsi_done = done;
if (device->tagged_supported &&
(ha->bus_settings[bus].qtag_enables & (BIT_0 << target))) {
- scsi_adjust_queue_depth(device, MSG_ORDERED_TAG,
- ha->bus_settings[bus].hiwat);
+ scsi_change_queue_depth(device, ha->bus_settings[bus].hiwat);
} else {
- scsi_adjust_queue_depth(device, 0, default_depth);
+ scsi_change_queue_depth(device, default_depth);
}
nv->bus[bus].target[target].parameter.enable_sync = device->sdtr;
extern int qla8044_rd_direct(struct scsi_qla_host *vha, const uint32_t crb_reg);
extern void qla8044_wr_direct(struct scsi_qla_host *vha,
const uint32_t crb_reg, const uint32_t value);
-extern inline void qla8044_set_qsnt_ready(struct scsi_qla_host *vha);
-extern inline void qla8044_need_reset_handler(struct scsi_qla_host *vha);
extern int qla8044_device_state_handler(struct scsi_qla_host *vha);
extern void qla8044_clear_qsnt_ready(struct scsi_qla_host *vha);
extern void qla8044_clear_drv_active(struct qla_hw_data *);
struct qla_hw_data *ha;
struct req_que *req;
struct rsp_que *rsp;
- char tag[2];
/* Setup device pointers. */
ret = 0;
/* Set target ID and LUN number*/
SET_TARGET_ID(ha, cmd_pkt->target, sp->fcport->loop_id);
cmd_pkt->lun = cpu_to_le16(cmd->device->lun);
-
- /* Update tagged queuing modifier */
- if (scsi_populate_tag_msg(cmd, tag)) {
- switch (tag[0]) {
- case HEAD_OF_QUEUE_TAG:
- cmd_pkt->control_flags =
- __constant_cpu_to_le16(CF_HEAD_TAG);
- break;
- case ORDERED_QUEUE_TAG:
- cmd_pkt->control_flags =
- __constant_cpu_to_le16(CF_ORDERED_TAG);
- break;
- default:
- cmd_pkt->control_flags =
- __constant_cpu_to_le16(CF_SIMPLE_TAG);
- break;
- }
- } else {
- cmd_pkt->control_flags = __constant_cpu_to_le16(CF_SIMPLE_TAG);
- }
+ cmd_pkt->control_flags = __constant_cpu_to_le16(CF_SIMPLE_TAG);
/* Load SCSI command packet. */
memcpy(cmd_pkt->scsi_cdb, cmd->cmnd, cmd->cmd_len);
uint16_t fcp_cmnd_len;
struct fcp_cmnd *fcp_cmnd;
dma_addr_t crc_ctx_dma;
- char tag[2];
cmd = GET_CMD_SP(sp);
cmd_pkt->fcp_cmnd_dseg_address[1] = cpu_to_le32(
MSD(crc_ctx_dma + CRC_CONTEXT_FCPCMND_OFF));
fcp_cmnd->task_management = 0;
-
- /*
- * Update tagged queuing modifier if using command tag queuing
- */
- if (scsi_populate_tag_msg(cmd, tag)) {
- switch (tag[0]) {
- case HEAD_OF_QUEUE_TAG:
- fcp_cmnd->task_attribute = TSK_HEAD_OF_QUEUE;
- break;
- case ORDERED_QUEUE_TAG:
- fcp_cmnd->task_attribute = TSK_ORDERED;
- break;
- default:
- fcp_cmnd->task_attribute = TSK_SIMPLE;
- break;
- }
- } else {
- fcp_cmnd->task_attribute = TSK_SIMPLE;
- }
+ fcp_cmnd->task_attribute = TSK_SIMPLE;
cmd_pkt->fcp_rsp_dseg_len = 0; /* Let response come in status iocb */
struct scsi_cmnd *cmd = GET_CMD_SP(sp);
struct scsi_qla_host *vha = sp->fcport->vha;
struct qla_hw_data *ha = vha->hw;
- char tag[2];
/* Setup device pointers. */
ret = 0;
int_to_scsilun(cmd->device->lun, &cmd_pkt->lun);
host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun));
- /* Update tagged queuing modifier -- default is TSK_SIMPLE (0). */
- if (scsi_populate_tag_msg(cmd, tag)) {
- switch (tag[0]) {
- case HEAD_OF_QUEUE_TAG:
- cmd_pkt->task = TSK_HEAD_OF_QUEUE;
- break;
- case ORDERED_QUEUE_TAG:
- cmd_pkt->task = TSK_ORDERED;
- break;
- default:
- cmd_pkt->task = TSK_SIMPLE;
- break;
- }
- } else {
- cmd_pkt->task = TSK_SIMPLE;
- }
+ cmd_pkt->task = TSK_SIMPLE;
/* Load SCSI command packet. */
memcpy(cmd_pkt->fcp_cdb, cmd->cmnd, cmd->cmd_len);
struct qla_hw_data *ha = vha->hw;
struct req_que *req = NULL;
struct rsp_que *rsp = NULL;
- char tag[2];
/* Setup device pointers. */
ret = 0;
else if (cmd->sc_data_direction == DMA_FROM_DEVICE)
ctx->fcp_cmnd->additional_cdb_len |= 2;
- /*
- * Update tagged queuing modifier -- default is TSK_SIMPLE (0).
- */
- if (scsi_populate_tag_msg(cmd, tag)) {
- switch (tag[0]) {
- case HEAD_OF_QUEUE_TAG:
- ctx->fcp_cmnd->task_attribute =
- TSK_HEAD_OF_QUEUE;
- break;
- case ORDERED_QUEUE_TAG:
- ctx->fcp_cmnd->task_attribute =
- TSK_ORDERED;
- break;
- }
- }
-
/* Populate the FCP_PRIO. */
if (ha->flags.fcp_prio_enabled)
ctx->fcp_cmnd->task_attribute |=
host_to_fcp_swap((uint8_t *)&cmd_pkt->lun,
sizeof(cmd_pkt->lun));
- /*
- * Update tagged queuing modifier -- default is TSK_SIMPLE (0).
- */
- if (scsi_populate_tag_msg(cmd, tag)) {
- switch (tag[0]) {
- case HEAD_OF_QUEUE_TAG:
- cmd_pkt->task = TSK_HEAD_OF_QUEUE;
- break;
- case ORDERED_QUEUE_TAG:
- cmd_pkt->task = TSK_ORDERED;
- break;
- }
- }
-
/* Populate the FCP_PRIO. */
if (ha->flags.fcp_prio_enabled)
cmd_pkt->task |= sp->fcport->fcp_prio << 3;
struct cmd_type_7_fx00 *cmd_pkt;
struct cmd_type_7_fx00 lcmd_pkt;
struct scsi_lun llun;
- char tag[2];
/* Setup device pointers. */
ret = 0;
host_to_adap((uint8_t *)&llun, (uint8_t *)&lcmd_pkt.lun,
sizeof(lcmd_pkt.lun));
- /* Update tagged queuing modifier -- default is TSK_SIMPLE (0). */
- if (scsi_populate_tag_msg(cmd, tag)) {
- switch (tag[0]) {
- case HEAD_OF_QUEUE_TAG:
- lcmd_pkt.task = TSK_HEAD_OF_QUEUE;
- break;
- case ORDERED_QUEUE_TAG:
- lcmd_pkt.task = TSK_ORDERED;
- break;
- }
- }
-
/* Load SCSI command packet. */
host_to_adap(cmd->cmnd, lcmd_pkt.fcp_cdb, sizeof(lcmd_pkt.fcp_cdb));
lcmd_pkt.byte_count = cpu_to_le32((uint32_t)scsi_bufflen(cmd));
return;
}
-inline void
+static inline void
qla8044_set_qsnt_ready(struct scsi_qla_host *vha)
{
uint32_t qsnt_state;
static int qla2xxx_eh_bus_reset(struct scsi_cmnd *);
static int qla2xxx_eh_host_reset(struct scsi_cmnd *);
-static int qla2x00_change_queue_depth(struct scsi_device *, int, int);
-static int qla2x00_change_queue_type(struct scsi_device *, int);
static void qla2x00_clear_drv_active(struct qla_hw_data *);
static void qla2x00_free_device(scsi_qla_host_t *);
static void qla83xx_disable_laser(scsi_qla_host_t *vha);
.slave_destroy = qla2xxx_slave_destroy,
.scan_finished = qla2xxx_scan_finished,
.scan_start = qla2xxx_scan_start,
- .change_queue_depth = qla2x00_change_queue_depth,
- .change_queue_type = qla2x00_change_queue_type,
+ .change_queue_depth = scsi_change_queue_depth,
+ .change_queue_type = scsi_change_queue_type,
.this_id = -1,
.cmd_per_lun = 3,
.use_clustering = ENABLE_CLUSTERING,
.shost_attrs = qla2x00_host_attrs,
.supported_mode = MODE_INITIATOR,
+ .use_blk_tags = 1,
+ .track_queue_depth = 1,
};
static struct scsi_transport_template *qla2xxx_transport_template = NULL;
if (IS_T10_PI_CAPABLE(vha->hw))
blk_queue_update_dma_alignment(sdev->request_queue, 0x7);
- if (sdev->tagged_supported)
- scsi_activate_tcq(sdev, req->max_q_depth);
- else
- scsi_deactivate_tcq(sdev, req->max_q_depth);
+ scsi_change_queue_depth(sdev, req->max_q_depth);
return 0;
}
sdev->hostdata = NULL;
}
-static void qla2x00_handle_queue_full(struct scsi_device *sdev, int qdepth)
-{
- fc_port_t *fcport = (struct fc_port *) sdev->hostdata;
-
- if (!scsi_track_queue_full(sdev, qdepth))
- return;
-
- ql_dbg(ql_dbg_io, fcport->vha, 0x3029,
- "Queue depth adjusted-down to %d for nexus=%ld:%d:%llu.\n",
- sdev->queue_depth, fcport->vha->host_no, sdev->id, sdev->lun);
-}
-
-static void qla2x00_adjust_sdev_qdepth_up(struct scsi_device *sdev, int qdepth)
-{
- fc_port_t *fcport = sdev->hostdata;
- struct scsi_qla_host *vha = fcport->vha;
- struct req_que *req = NULL;
-
- req = vha->req;
- if (!req)
- return;
-
- if (req->max_q_depth <= sdev->queue_depth || req->max_q_depth < qdepth)
- return;
-
- if (sdev->ordered_tags)
- scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, qdepth);
- else
- scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, qdepth);
-
- ql_dbg(ql_dbg_io, vha, 0x302a,
- "Queue depth adjusted-up to %d for nexus=%ld:%d:%llu.\n",
- sdev->queue_depth, fcport->vha->host_no, sdev->id, sdev->lun);
-}
-
-static int
-qla2x00_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
-{
- switch (reason) {
- case SCSI_QDEPTH_DEFAULT:
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
- break;
- case SCSI_QDEPTH_QFULL:
- qla2x00_handle_queue_full(sdev, qdepth);
- break;
- case SCSI_QDEPTH_RAMP_UP:
- qla2x00_adjust_sdev_qdepth_up(sdev, qdepth);
- break;
- default:
- return -EOPNOTSUPP;
- }
-
- return sdev->queue_depth;
-}
-
-static int
-qla2x00_change_queue_type(struct scsi_device *sdev, int tag_type)
-{
- if (sdev->tagged_supported) {
- scsi_set_tag_type(sdev, tag_type);
- if (tag_type)
- scsi_activate_tcq(sdev, sdev->queue_depth);
- else
- scsi_deactivate_tcq(sdev, sdev->queue_depth);
- } else
- tag_type = 0;
-
- return tag_type;
-}
-
/**
* qla2x00_config_dma_addressing() - Configure OS DMA addressing method.
* @ha: HA context
uint16_t req_cnt;
unsigned long flags;
uint32_t index;
- char tag[2];
/* Get real lun and adapter */
ddb_entry = srb->ddb;
/* Set tagged queueing control flags */
cmd_entry->control_flags |= CF_SIMPLE_TAG;
- if (scsi_populate_tag_msg(cmd, tag))
- switch (tag[0]) {
- case MSG_HEAD_TAG:
- cmd_entry->control_flags |= CF_HEAD_TAG;
- break;
- case MSG_ORDERED_TAG:
- cmd_entry->control_flags |= CF_ORDERED_TAG;
- break;
- }
qla4xxx_advance_req_ring_ptr(ha);
qla4xxx_build_scsi_iocbs(srb, cmd_entry, tot_dsds);
static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd);
static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd);
static int qla4xxx_slave_alloc(struct scsi_device *device);
-static int qla4xxx_slave_configure(struct scsi_device *device);
-static void qla4xxx_slave_destroy(struct scsi_device *sdev);
static umode_t qla4_attr_is_visible(int param_type, int param);
static int qla4xxx_host_reset(struct Scsi_Host *shost, int reset_type);
-static int qla4xxx_change_queue_depth(struct scsi_device *sdev, int qdepth,
- int reason);
/*
* iSCSI Flash DDB sysfs entry points
.eh_host_reset_handler = qla4xxx_eh_host_reset,
.eh_timed_out = qla4xxx_eh_cmd_timed_out,
- .slave_configure = qla4xxx_slave_configure,
.slave_alloc = qla4xxx_slave_alloc,
- .slave_destroy = qla4xxx_slave_destroy,
- .change_queue_depth = qla4xxx_change_queue_depth,
+ .change_queue_depth = scsi_change_queue_depth,
.this_id = -1,
.cmd_per_lun = 3,
.shost_attrs = qla4xxx_host_attrs,
.host_reset = qla4xxx_host_reset,
.vendor_id = SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_QLOGIC,
+ .use_blk_tags = 1,
};
static struct iscsi_transport qla4xxx_iscsi_transport = {
ddb = sess->dd_data;
sdev->hostdata = ddb;
- sdev->tagged_supported = 1;
if (ql4xmaxqdepth != 0 && ql4xmaxqdepth <= 0xffffU)
queue_depth = ql4xmaxqdepth;
- scsi_activate_tcq(sdev, queue_depth);
+ scsi_change_queue_depth(sdev, queue_depth);
return 0;
}
-static int qla4xxx_slave_configure(struct scsi_device *sdev)
-{
- sdev->tagged_supported = 1;
- return 0;
-}
-
-static void qla4xxx_slave_destroy(struct scsi_device *sdev)
-{
- scsi_deactivate_tcq(sdev, 1);
-}
-
-static int qla4xxx_change_queue_depth(struct scsi_device *sdev, int qdepth,
- int reason)
-{
- if (!ql4xqfulltracking)
- return -EOPNOTSUPP;
-
- return iscsi_change_queue_depth(sdev, qdepth, reason);
-}
-
/**
* qla4xxx_del_from_active_array - returns an active srb
* @ha: Pointer to host adapter structure.
{
int ret;
+ if (ql4xqfulltracking)
+ qla4xxx_driver_template.track_queue_depth = 1;
+
/* Allocate cache for SRBs. */
srb_cachep = kmem_cache_create("qla4xxx_srbs", sizeof(struct srb), 0,
SLAB_HWCACHE_ALIGN, NULL);
*
* 1: nothing (match completion)
*
- * 2: log opcode + command of all commands
+ * 2: log opcode + command of all commands + cmd address
*
- * 3: same as 2 plus dump cmd address
+ * 3: same as 2
*
* 4: same as 3 plus dump extra junk
*/
level = SCSI_LOG_LEVEL(SCSI_LOG_MLQUEUE_SHIFT,
SCSI_LOG_MLQUEUE_BITS);
if (level > 1) {
- scmd_printk(KERN_INFO, cmd, "Send: ");
- if (level > 2)
- printk("0x%p ", cmd);
- printk("\n");
+ scmd_printk(KERN_INFO, cmd,
+ "Send: scmd 0x%p\n", cmd);
scsi_print_command(cmd);
if (level > 3) {
printk(KERN_INFO "buffer = 0x%p, bufflen = %d,"
*
* 2: same as 1 but for all command completions.
*
- * 3: same as 2 plus dump cmd address
+ * 3: same as 2
*
* 4: same as 3 plus dump extra junk
*/
SCSI_LOG_MLCOMPLETE_BITS);
if (((level > 0) && (cmd->result || disposition != SUCCESS)) ||
(level > 1)) {
- scmd_printk(KERN_INFO, cmd, "Done: ");
- if (level > 2)
- printk("0x%p ", cmd);
- /*
- * Dump truncated values, so we usually fit within
- * 80 chars.
- */
- switch (disposition) {
- case SUCCESS:
- printk("SUCCESS\n");
- break;
- case NEEDS_RETRY:
- printk("RETRY\n");
- break;
- case ADD_TO_MLQUEUE:
- printk("MLQUEUE\n");
- break;
- case FAILED:
- printk("FAILED\n");
- break;
- case TIMEOUT_ERROR:
- /*
- * If called via scsi_times_out.
- */
- printk("TIMEOUT\n");
- break;
- default:
- printk("UNKNOWN\n");
- }
- scsi_print_result(cmd);
+ scsi_print_result(cmd, "Done: ", disposition);
scsi_print_command(cmd);
if (status_byte(cmd->result) & CHECK_CONDITION)
- scsi_print_sense("", cmd);
+ scsi_print_sense(cmd);
if (level > 3)
scmd_printk(KERN_INFO, cmd,
"scsi host busy %d failed %d\n",
}
EXPORT_SYMBOL(scsi_cmd_get_serial);
-/**
- * scsi_dispatch_command - Dispatch a command to the low-level driver.
- * @cmd: command block we are dispatching.
- *
- * Return: nonzero return request was rejected and device's queue needs to be
- * plugged.
- */
-int scsi_dispatch_cmd(struct scsi_cmnd *cmd)
-{
- struct Scsi_Host *host = cmd->device->host;
- int rtn = 0;
-
- atomic_inc(&cmd->device->iorequest_cnt);
-
- /* check if the device is still usable */
- if (unlikely(cmd->device->sdev_state == SDEV_DEL)) {
- /* in SDEV_DEL we error all commands. DID_NO_CONNECT
- * returns an immediate error upwards, and signals
- * that the device is no longer present */
- cmd->result = DID_NO_CONNECT << 16;
- goto done;
- }
-
- /* Check to see if the scsi lld made this device blocked. */
- if (unlikely(scsi_device_blocked(cmd->device))) {
- /*
- * in blocked state, the command is just put back on
- * the device queue. The suspend state has already
- * blocked the queue so future requests should not
- * occur until the device transitions out of the
- * suspend state.
- */
- SCSI_LOG_MLQUEUE(3, scmd_printk(KERN_INFO, cmd,
- "queuecommand : device blocked\n"));
- return SCSI_MLQUEUE_DEVICE_BUSY;
- }
-
- /* Store the LUN value in cmnd, if needed. */
- if (cmd->device->lun_in_cdb)
- cmd->cmnd[1] = (cmd->cmnd[1] & 0x1f) |
- (cmd->device->lun << 5 & 0xe0);
-
- scsi_log_send(cmd);
-
- /*
- * Before we queue this command, check if the command
- * length exceeds what the host adapter can handle.
- */
- if (cmd->cmd_len > cmd->device->host->max_cmd_len) {
- SCSI_LOG_MLQUEUE(3, scmd_printk(KERN_INFO, cmd,
- "queuecommand : command too long. "
- "cdb_size=%d host->max_cmd_len=%d\n",
- cmd->cmd_len, cmd->device->host->max_cmd_len));
- cmd->result = (DID_ABORT << 16);
- goto done;
- }
-
- if (unlikely(host->shost_state == SHOST_DEL)) {
- cmd->result = (DID_NO_CONNECT << 16);
- goto done;
-
- }
-
- trace_scsi_dispatch_cmd_start(cmd);
- rtn = host->hostt->queuecommand(host, cmd);
- if (rtn) {
- trace_scsi_dispatch_cmd_error(cmd, rtn);
- if (rtn != SCSI_MLQUEUE_DEVICE_BUSY &&
- rtn != SCSI_MLQUEUE_TARGET_BUSY)
- rtn = SCSI_MLQUEUE_HOST_BUSY;
-
- SCSI_LOG_MLQUEUE(3, scmd_printk(KERN_INFO, cmd,
- "queuecommand : request rejected\n"));
- }
-
- return rtn;
- done:
- cmd->scsi_done(cmd);
- return 0;
-}
-
/**
* scsi_finish_command - cleanup and pass command back to upper layer
* @cmd: the command
}
/**
- * scsi_adjust_queue_depth - Let low level drivers change a device's queue depth
+ * scsi_change_queue_depth - change a device's queue depth
* @sdev: SCSI Device in question
- * @tagged: Do we use tagged queueing (non-0) or do we treat
- * this device as an untagged device (0)
- * @tags: Number of tags allowed if tagged queueing enabled,
- * or number of commands the low level driver can
- * queue up in non-tagged mode (as per cmd_per_lun).
- *
- * Returns: Nothing
+ * @depth: number of commands allowed to be queued to the driver
*
- * Lock Status: None held on entry
- *
- * Notes: Low level drivers may call this at any time and we will do
- * the right thing depending on whether or not the device is
- * currently active and whether or not it even has the
- * command blocks built yet.
+ * Sets the device queue depth and returns the new value.
*/
-void scsi_adjust_queue_depth(struct scsi_device *sdev, int tagged, int tags)
+int scsi_change_queue_depth(struct scsi_device *sdev, int depth)
{
unsigned long flags;
- /*
- * refuse to set tagged depth to an unworkable size
- */
- if (tags <= 0)
- return;
+ if (depth <= 0)
+ goto out;
spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
*/
if (!shost_use_blk_mq(sdev->host) && !sdev->host->bqt) {
if (blk_queue_tagged(sdev->request_queue) &&
- blk_queue_resize_tags(sdev->request_queue, tags) != 0)
- goto out;
+ blk_queue_resize_tags(sdev->request_queue, depth) != 0)
+ goto out_unlock;
}
- sdev->queue_depth = tags;
- switch (tagged) {
- case 0:
- sdev->ordered_tags = 0;
- sdev->simple_tags = 0;
- break;
- case MSG_ORDERED_TAG:
- sdev->ordered_tags = 1;
- sdev->simple_tags = 1;
- break;
- case MSG_SIMPLE_TAG:
- sdev->ordered_tags = 0;
- sdev->simple_tags = 1;
- break;
- default:
- sdev->ordered_tags = 0;
- sdev->simple_tags = 0;
- sdev_printk(KERN_WARNING, sdev,
- "scsi_adjust_queue_depth, bad queue type, "
- "disabled\n");
- }
- out:
+ sdev->queue_depth = depth;
+out_unlock:
spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+out:
+ return sdev->queue_depth;
}
-EXPORT_SYMBOL(scsi_adjust_queue_depth);
+EXPORT_SYMBOL(scsi_change_queue_depth);
/**
* scsi_track_queue_full - track QUEUE_FULL events to adjust queue depth
return 0;
if (sdev->last_queue_full_depth < 8) {
/* Drop back to untagged */
- scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun);
+ scsi_set_tag_type(sdev, 0);
+ scsi_change_queue_depth(sdev, sdev->host->cmd_per_lun);
return -1;
}
-
- if (sdev->ordered_tags)
- scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, depth);
- else
- scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, depth);
- return depth;
+
+ return scsi_change_queue_depth(sdev, depth);
}
EXPORT_SYMBOL(scsi_track_queue_full);
+/**
+ * scsi_change_queue_type() - Change a device's queue type
+ * @sdev: The SCSI device whose queue depth is to change
+ * @tag_type: Identifier for queue type
+ */
+int scsi_change_queue_type(struct scsi_device *sdev, int tag_type)
+{
+ if (!sdev->tagged_supported)
+ return 0;
+
+ scsi_set_tag_type(sdev, tag_type);
+ return tag_type;
+
+}
+EXPORT_SYMBOL(scsi_change_queue_type);
+
/**
* scsi_vpd_inquiry - Request a device provide us with a VPD page
* @sdev: The device to ask
#include "sd.h"
#include "scsi_logging.h"
-#define SCSI_DEBUG_VERSION "1.84"
-static const char *scsi_debug_version_date = "20140706";
+#define SCSI_DEBUG_VERSION "1.85"
+static const char *scsi_debug_version_date = "20141022";
#define MY_NAME "scsi_debug"
#define UNRECOVERED_READ_ERR 0x11
#define PARAMETER_LIST_LENGTH_ERR 0x1a
#define INVALID_OPCODE 0x20
-#define ADDR_OUT_OF_RANGE 0x21
-#define INVALID_COMMAND_OPCODE 0x20
+#define LBA_OUT_OF_RANGE 0x21
#define INVALID_FIELD_IN_CDB 0x24
#define INVALID_FIELD_IN_PARAM_LIST 0x26
#define UA_RESET_ASC 0x29
#define UA_CHANGED_ASC 0x2a
+#define INSUFF_RES_ASC 0x55
+#define INSUFF_RES_ASCQ 0x3
#define POWER_ON_RESET_ASCQ 0x0
#define BUS_RESET_ASCQ 0x2 /* scsi bus reset occurred */
#define MODE_CHANGED_ASCQ 0x1 /* mode parameters changed */
+#define CAPACITY_CHANGED_ASCQ 0x9
#define SAVING_PARAMS_UNSUP 0x39
#define TRANSPORT_PROBLEM 0x4b
#define THRESHOLD_EXCEEDED 0x5d
#define LOW_POWER_COND_ON 0x5e
+#define MISCOMPARE_VERIFY_ASC 0x1d
/* Additional Sense Code Qualifier (ASCQ) */
#define ACK_NAK_TO 0x3
#define DEF_VIRTUAL_GB 0
#define DEF_VPD_USE_HOSTNO 1
#define DEF_WRITESAME_LENGTH 0xFFFF
+#define DEF_STRICT 0
#define DELAY_OVERRIDDEN -9999
/* bit mask values for scsi_debug_opts */
#define SDEBUG_UA_POR 0 /* Power on, reset, or bus device reset */
#define SDEBUG_UA_BUS_RESET 1
#define SDEBUG_UA_MODE_CHANGED 2
-#define SDEBUG_NUM_UAS 3
+#define SDEBUG_UA_CAPACITY_CHANGED 3
+#define SDEBUG_NUM_UAS 4
/* for check_readiness() */
-#define UAS_ONLY 1
-#define UAS_TUR 0
+#define UAS_ONLY 1 /* check for UAs only */
+#define UAS_TUR 0 /* if no UAs then check if media access possible */
/* when 1==SCSI_DEBUG_OPT_MEDIUM_ERR, a medium error is simulated at this
* sector on read commands: */
#warning "Expect DEF_CMD_PER_LUN <= SCSI_DEBUG_CANQUEUE"
#endif
+/* SCSI opcodes (first byte of cdb) mapped onto these indexes */
+enum sdeb_opcode_index {
+ SDEB_I_INVALID_OPCODE = 0,
+ SDEB_I_INQUIRY = 1,
+ SDEB_I_REPORT_LUNS = 2,
+ SDEB_I_REQUEST_SENSE = 3,
+ SDEB_I_TEST_UNIT_READY = 4,
+ SDEB_I_MODE_SENSE = 5, /* 6, 10 */
+ SDEB_I_MODE_SELECT = 6, /* 6, 10 */
+ SDEB_I_LOG_SENSE = 7,
+ SDEB_I_READ_CAPACITY = 8, /* 10; 16 is in SA_IN(16) */
+ SDEB_I_READ = 9, /* 6, 10, 12, 16 */
+ SDEB_I_WRITE = 10, /* 6, 10, 12, 16 */
+ SDEB_I_START_STOP = 11,
+ SDEB_I_SERV_ACT_IN = 12, /* 12, 16 */
+ SDEB_I_SERV_ACT_OUT = 13, /* 12, 16 */
+ SDEB_I_MAINT_IN = 14,
+ SDEB_I_MAINT_OUT = 15,
+ SDEB_I_VERIFY = 16, /* 10 only */
+ SDEB_I_VARIABLE_LEN = 17,
+ SDEB_I_RESERVE = 18, /* 6, 10 */
+ SDEB_I_RELEASE = 19, /* 6, 10 */
+ SDEB_I_ALLOW_REMOVAL = 20, /* PREVENT ALLOW MEDIUM REMOVAL */
+ SDEB_I_REZERO_UNIT = 21, /* REWIND in SSC */
+ SDEB_I_ATA_PT = 22, /* 12, 16 */
+ SDEB_I_SEND_DIAG = 23,
+ SDEB_I_UNMAP = 24,
+ SDEB_I_XDWRITEREAD = 25, /* 10 only */
+ SDEB_I_WRITE_BUFFER = 26,
+ SDEB_I_WRITE_SAME = 27, /* 10, 16 */
+ SDEB_I_SYNC_CACHE = 28, /* 10 only */
+ SDEB_I_COMP_WRITE = 29,
+ SDEB_I_LAST_ELEMENT = 30, /* keep this last */
+};
+
+static const unsigned char opcode_ind_arr[256] = {
+/* 0x0; 0x0->0x1f: 6 byte cdbs */
+ SDEB_I_TEST_UNIT_READY, SDEB_I_REZERO_UNIT, 0, SDEB_I_REQUEST_SENSE,
+ 0, 0, 0, 0,
+ SDEB_I_READ, 0, SDEB_I_WRITE, 0, 0, 0, 0, 0,
+ 0, 0, SDEB_I_INQUIRY, 0, 0, SDEB_I_MODE_SELECT, SDEB_I_RESERVE,
+ SDEB_I_RELEASE,
+ 0, 0, SDEB_I_MODE_SENSE, SDEB_I_START_STOP, 0, SDEB_I_SEND_DIAG,
+ SDEB_I_ALLOW_REMOVAL, 0,
+/* 0x20; 0x20->0x3f: 10 byte cdbs */
+ 0, 0, 0, 0, 0, SDEB_I_READ_CAPACITY, 0, 0,
+ SDEB_I_READ, 0, SDEB_I_WRITE, 0, 0, 0, 0, SDEB_I_VERIFY,
+ 0, 0, 0, 0, 0, SDEB_I_SYNC_CACHE, 0, 0,
+ 0, 0, 0, SDEB_I_WRITE_BUFFER, 0, 0, 0, 0,
+/* 0x40; 0x40->0x5f: 10 byte cdbs */
+ 0, SDEB_I_WRITE_SAME, SDEB_I_UNMAP, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, SDEB_I_LOG_SENSE, 0, 0,
+ 0, 0, 0, SDEB_I_XDWRITEREAD, 0, SDEB_I_MODE_SELECT, SDEB_I_RESERVE,
+ SDEB_I_RELEASE,
+ 0, 0, SDEB_I_MODE_SENSE, 0, 0, 0, 0, 0,
+/* 0x60; 0x60->0x7d are reserved */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, SDEB_I_VARIABLE_LEN,
+/* 0x80; 0x80->0x9f: 16 byte cdbs */
+ 0, 0, 0, 0, 0, SDEB_I_ATA_PT, 0, 0,
+ SDEB_I_READ, SDEB_I_COMP_WRITE, SDEB_I_WRITE, 0, 0, 0, 0, 0,
+ 0, 0, 0, SDEB_I_WRITE_SAME, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, SDEB_I_SERV_ACT_IN, SDEB_I_SERV_ACT_OUT,
+/* 0xa0; 0xa0->0xbf: 12 byte cdbs */
+ SDEB_I_REPORT_LUNS, SDEB_I_ATA_PT, 0, SDEB_I_MAINT_IN,
+ SDEB_I_MAINT_OUT, 0, 0, 0,
+ SDEB_I_READ, SDEB_I_SERV_ACT_OUT, SDEB_I_WRITE, SDEB_I_SERV_ACT_IN,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 0xc0; 0xc0->0xff: vendor specific */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+#define F_D_IN 1
+#define F_D_OUT 2
+#define F_D_OUT_MAYBE 4 /* WRITE SAME, NDOB bit */
+#define F_D_UNKN 8
+#define F_RL_WLUN_OK 0x10
+#define F_SKIP_UA 0x20
+#define F_DELAY_OVERR 0x40
+#define F_SA_LOW 0x80 /* cdb byte 1, bits 4 to 0 */
+#define F_SA_HIGH 0x100 /* as used by variable length cdbs */
+#define F_INV_OP 0x200
+#define F_FAKE_RW 0x400
+#define F_M_ACCESS 0x800 /* media access */
+
+#define FF_RESPOND (F_RL_WLUN_OK | F_SKIP_UA | F_DELAY_OVERR)
+#define FF_DIRECT_IO (F_M_ACCESS | F_FAKE_RW)
+#define FF_SA (F_SA_HIGH | F_SA_LOW)
+
+struct sdebug_dev_info;
+static int resp_inquiry(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_report_luns(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_requests(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_mode_sense(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_mode_select(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_log_sense(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_readcap(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_read_dt0(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_write_dt0(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_start_stop(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_readcap16(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_get_lba_status(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_report_tgtpgs(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_unmap(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_rsup_opcodes(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_rsup_tmfs(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_write_same_10(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_write_same_16(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_xdwriteread_10(struct scsi_cmnd *, struct sdebug_dev_info *);
+static int resp_comp_write(struct scsi_cmnd *, struct sdebug_dev_info *);
+
+struct opcode_info_t {
+ u8 num_attached; /* 0 if this is it (i.e. a leaf); use 0xff
+ * for terminating element */
+ u8 opcode; /* if num_attached > 0, preferred */
+ u16 sa; /* service action */
+ u32 flags; /* OR-ed set of SDEB_F_* */
+ int (*pfp)(struct scsi_cmnd *, struct sdebug_dev_info *);
+ const struct opcode_info_t *arrp; /* num_attached elements or NULL */
+ u8 len_mask[16]; /* len=len_mask[0], then mask for cdb[1]... */
+ /* ignore cdb bytes after position 15 */
+};
+
+static const struct opcode_info_t msense_iarr[1] = {
+ {0, 0x1a, 0, F_D_IN, NULL, NULL,
+ {6, 0xe8, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+};
+
+static const struct opcode_info_t mselect_iarr[1] = {
+ {0, 0x15, 0, F_D_OUT, NULL, NULL,
+ {6, 0xf1, 0, 0, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+};
+
+static const struct opcode_info_t read_iarr[3] = {
+ {0, 0x28, 0, F_D_IN | FF_DIRECT_IO, resp_read_dt0, NULL,/* READ(10) */
+ {10, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xc7, 0, 0,
+ 0, 0, 0, 0} },
+ {0, 0x8, 0, F_D_IN | FF_DIRECT_IO, resp_read_dt0, NULL, /* READ(6) */
+ {6, 0xff, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+ {0, 0xa8, 0, F_D_IN | FF_DIRECT_IO, resp_read_dt0, NULL,/* READ(12) */
+ {12, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f,
+ 0xc7, 0, 0, 0, 0} },
+};
+
+static const struct opcode_info_t write_iarr[3] = {
+ {0, 0x2a, 0, F_D_OUT | FF_DIRECT_IO, resp_write_dt0, NULL, /* 10 */
+ {10, 0xfb, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xc7, 0, 0,
+ 0, 0, 0, 0} },
+ {0, 0xa, 0, F_D_OUT | FF_DIRECT_IO, resp_write_dt0, NULL, /* 6 */
+ {6, 0xff, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+ {0, 0xaa, 0, F_D_OUT | FF_DIRECT_IO, resp_write_dt0, NULL, /* 12 */
+ {12, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f,
+ 0xc7, 0, 0, 0, 0} },
+};
+
+static const struct opcode_info_t sa_in_iarr[1] = {
+ {0, 0x9e, 0x12, F_SA_LOW | F_D_IN, resp_get_lba_status, NULL,
+ {16, 0x12, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0, 0xc7} },
+};
+
+static const struct opcode_info_t vl_iarr[1] = { /* VARIABLE LENGTH */
+ {0, 0x7f, 0xb, F_SA_HIGH | F_D_OUT | FF_DIRECT_IO, resp_write_dt0,
+ NULL, {32, 0xc7, 0, 0, 0, 0, 0x1f, 0x18, 0x0, 0xb, 0xfa,
+ 0, 0xff, 0xff, 0xff, 0xff} }, /* WRITE(32) */
+};
+
+static const struct opcode_info_t maint_in_iarr[2] = {
+ {0, 0xa3, 0xc, F_SA_LOW | F_D_IN, resp_rsup_opcodes, NULL,
+ {12, 0xc, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0,
+ 0xc7, 0, 0, 0, 0} },
+ {0, 0xa3, 0xd, F_SA_LOW | F_D_IN, resp_rsup_tmfs, NULL,
+ {12, 0xd, 0x80, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0xc7, 0, 0,
+ 0, 0} },
+};
+
+static const struct opcode_info_t write_same_iarr[1] = {
+ {0, 0x93, 0, F_D_OUT_MAYBE | FF_DIRECT_IO, resp_write_same_16, NULL,
+ {16, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x1f, 0xc7} },
+};
+
+static const struct opcode_info_t reserve_iarr[1] = {
+ {0, 0x16, 0, F_D_OUT, NULL, NULL, /* RESERVE(6) */
+ {6, 0x1f, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+};
+
+static const struct opcode_info_t release_iarr[1] = {
+ {0, 0x17, 0, F_D_OUT, NULL, NULL, /* RELEASE(6) */
+ {6, 0x1f, 0xff, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+};
+
+
+/* This array is accessed via SDEB_I_* values. Make sure all are mapped,
+ * plus the terminating elements for logic that scans this table such as
+ * REPORT SUPPORTED OPERATION CODES. */
+static const struct opcode_info_t opcode_info_arr[SDEB_I_LAST_ELEMENT + 1] = {
+/* 0 */
+ {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL,
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+ {0, 0x12, 0, FF_RESPOND | F_D_IN, resp_inquiry, NULL,
+ {6, 0xe3, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+ {0, 0xa0, 0, FF_RESPOND | F_D_IN, resp_report_luns, NULL,
+ {12, 0xe3, 0xff, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0xc7, 0, 0,
+ 0, 0} },
+ {0, 0x3, 0, FF_RESPOND | F_D_IN, resp_requests, NULL,
+ {6, 0xe1, 0, 0, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+ {0, 0x0, 0, F_M_ACCESS | F_RL_WLUN_OK, NULL, NULL,/* TEST UNIT READY */
+ {6, 0, 0, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+ {1, 0x5a, 0, F_D_IN, resp_mode_sense, msense_iarr,
+ {10, 0xf8, 0xff, 0xff, 0, 0, 0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0,
+ 0} },
+ {1, 0x55, 0, F_D_OUT, resp_mode_select, mselect_iarr,
+ {10, 0xf1, 0, 0, 0, 0, 0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0} },
+ {0, 0x4d, 0, F_D_IN, resp_log_sense, NULL,
+ {10, 0xe3, 0xff, 0xff, 0, 0xff, 0xff, 0xff, 0xff, 0xc7, 0, 0, 0,
+ 0, 0, 0} },
+ {0, 0x25, 0, F_D_IN, resp_readcap, NULL,
+ {10, 0xe1, 0xff, 0xff, 0xff, 0xff, 0, 0, 0x1, 0xc7, 0, 0, 0, 0,
+ 0, 0} },
+ {3, 0x88, 0, F_D_IN | FF_DIRECT_IO, resp_read_dt0, read_iarr,
+ {16, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x9f, 0xc7} }, /* READ(16) */
+/* 10 */
+ {3, 0x8a, 0, F_D_OUT | FF_DIRECT_IO, resp_write_dt0, write_iarr,
+ {16, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x9f, 0xc7} }, /* WRITE(16) */
+ {0, 0x1b, 0, 0, resp_start_stop, NULL, /* START STOP UNIT */
+ {6, 0x1, 0, 0xf, 0xf7, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+ {1, 0x9e, 0x10, F_SA_LOW | F_D_IN, resp_readcap16, sa_in_iarr,
+ {16, 0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x1, 0xc7} }, /* READ CAPACITY(16) */
+ {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* SA OUT */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+ {2, 0xa3, 0xa, F_SA_LOW | F_D_IN, resp_report_tgtpgs, maint_in_iarr,
+ {12, 0xea, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, 0xc7, 0, 0, 0,
+ 0} },
+ {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* MAINT OUT */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+ {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* VERIFY */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+ {1, 0x7f, 0x9, F_SA_HIGH | F_D_IN | FF_DIRECT_IO, resp_read_dt0,
+ vl_iarr, {32, 0xc7, 0, 0, 0, 0, 0x1f, 0x18, 0x0, 0x9, 0xfe, 0,
+ 0xff, 0xff, 0xff, 0xff} },/* VARIABLE LENGTH, READ(32) */
+ {1, 0x56, 0, F_D_OUT, NULL, reserve_iarr, /* RESERVE(10) */
+ {10, 0xff, 0xff, 0xff, 0, 0, 0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0,
+ 0} },
+ {1, 0x57, 0, F_D_OUT, NULL, release_iarr, /* RELEASE(10) */
+ {10, 0x13, 0xff, 0xff, 0, 0, 0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0,
+ 0} },
+/* 20 */
+ {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* ALLOW REMOVAL */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+ {0, 0x1, 0, 0, resp_start_stop, NULL, /* REWIND ?? */
+ {6, 0x1, 0, 0, 0, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+ {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* ATA_PT */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+ {0, 0x1d, F_D_OUT, 0, NULL, NULL, /* SEND DIAGNOSTIC */
+ {6, 0xf7, 0, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+ {0, 0x42, 0, F_D_OUT | FF_DIRECT_IO, resp_unmap, NULL, /* UNMAP */
+ {10, 0x1, 0, 0, 0, 0, 0x1f, 0xff, 0xff, 0xc7, 0, 0, 0, 0, 0, 0} },
+ {0, 0x53, 0, F_D_IN | F_D_OUT | FF_DIRECT_IO, resp_xdwriteread_10,
+ NULL, {10, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xc7,
+ 0, 0, 0, 0, 0, 0} },
+ {0, 0, 0, F_INV_OP | FF_RESPOND, NULL, NULL, /* WRITE_BUFFER */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+ {1, 0x41, 0, F_D_OUT_MAYBE | FF_DIRECT_IO, resp_write_same_10,
+ write_same_iarr, {10, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff,
+ 0xff, 0xc7, 0, 0, 0, 0, 0, 0} },
+ {0, 0x35, 0, F_DELAY_OVERR | FF_DIRECT_IO, NULL, NULL, /* SYNC_CACHE */
+ {10, 0x7, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xc7, 0, 0,
+ 0, 0, 0, 0} },
+ {0, 0x89, 0, F_D_OUT | FF_DIRECT_IO, resp_comp_write, NULL,
+ {16, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0,
+ 0, 0xff, 0x1f, 0xc7} }, /* COMPARE AND WRITE */
+
+/* 30 */
+ {0xff, 0, 0, 0, NULL, NULL, /* terminating element */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
+};
+
+struct sdebug_scmd_extra_t {
+ bool inj_recovered;
+ bool inj_transport;
+ bool inj_dif;
+ bool inj_dix;
+ bool inj_short;
+};
+
static int scsi_debug_add_host = DEF_NUM_HOST;
static int scsi_debug_ato = DEF_ATO;
static int scsi_debug_delay = DEF_DELAY;
static bool scsi_debug_removable = DEF_REMOVABLE;
static bool scsi_debug_clustering;
static bool scsi_debug_host_lock = DEF_HOST_LOCK;
+static bool scsi_debug_strict = DEF_STRICT;
+static bool sdebug_any_injecting_opt;
static atomic_t sdebug_cmnd_count;
static atomic_t sdebug_completions;
unsigned int target;
u64 lun;
struct sdebug_host_info *sdbg_host;
- u64 wlun;
unsigned long uas_bm[1];
atomic_t num_in_q;
- char stopped;
- char used;
+ char stopped; /* TODO: should be atomic */
+ bool used;
};
struct sdebug_host_info {
spin_unlock(&sdebug_host_list_lock);
}
+enum sdeb_cmd_data {SDEB_IN_DATA = 0, SDEB_IN_CDB = 1};
+
+/* Set in_bit to -1 to indicate no bit position of invalid field */
+static void
+mk_sense_invalid_fld(struct scsi_cmnd *scp, enum sdeb_cmd_data c_d,
+ int in_byte, int in_bit)
+{
+ unsigned char *sbuff;
+ u8 sks[4];
+ int sl, asc;
+
+ sbuff = scp->sense_buffer;
+ if (!sbuff) {
+ sdev_printk(KERN_ERR, scp->device,
+ "%s: sense_buffer is NULL\n", __func__);
+ return;
+ }
+ asc = c_d ? INVALID_FIELD_IN_CDB : INVALID_FIELD_IN_PARAM_LIST;
+ memset(sbuff, 0, SCSI_SENSE_BUFFERSIZE);
+ scsi_build_sense_buffer(scsi_debug_dsense, sbuff, ILLEGAL_REQUEST,
+ asc, 0);
+ memset(sks, 0, sizeof(sks));
+ sks[0] = 0x80;
+ if (c_d)
+ sks[0] |= 0x40;
+ if (in_bit >= 0) {
+ sks[0] |= 0x8;
+ sks[0] |= 0x7 & in_bit;
+ }
+ put_unaligned_be16(in_byte, sks + 1);
+ if (scsi_debug_dsense) {
+ sl = sbuff[7] + 8;
+ sbuff[7] = sl;
+ sbuff[sl] = 0x2;
+ sbuff[sl + 1] = 0x6;
+ memcpy(sbuff + sl + 4, sks, 3);
+ } else
+ memcpy(sbuff + 15, sks, 3);
+ if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
+ sdev_printk(KERN_INFO, scp->device, "%s: [sense_key,asc,ascq"
+ "]: [0x5,0x%x,0x0] %c byte=%d, bit=%d\n",
+ my_name, asc, c_d ? 'C' : 'D', in_byte, in_bit);
+}
+
static void mk_sense_buffer(struct scsi_cmnd *scp, int key, int asc, int asq)
{
unsigned char *sbuff;
my_name, key, asc, asq);
}
-static void get_data_transfer_info(unsigned char *cmd,
- unsigned long long *lba, unsigned int *num,
- u32 *ei_lba)
+static void
+mk_sense_invalid_opcode(struct scsi_cmnd *scp)
{
- *ei_lba = 0;
-
- switch (*cmd) {
- case VARIABLE_LENGTH_CMD:
- *lba = (u64)cmd[19] | (u64)cmd[18] << 8 |
- (u64)cmd[17] << 16 | (u64)cmd[16] << 24 |
- (u64)cmd[15] << 32 | (u64)cmd[14] << 40 |
- (u64)cmd[13] << 48 | (u64)cmd[12] << 56;
-
- *ei_lba = (u32)cmd[23] | (u32)cmd[22] << 8 |
- (u32)cmd[21] << 16 | (u32)cmd[20] << 24;
-
- *num = (u32)cmd[31] | (u32)cmd[30] << 8 | (u32)cmd[29] << 16 |
- (u32)cmd[28] << 24;
- break;
-
- case WRITE_SAME_16:
- case WRITE_16:
- case READ_16:
- *lba = (u64)cmd[9] | (u64)cmd[8] << 8 |
- (u64)cmd[7] << 16 | (u64)cmd[6] << 24 |
- (u64)cmd[5] << 32 | (u64)cmd[4] << 40 |
- (u64)cmd[3] << 48 | (u64)cmd[2] << 56;
-
- *num = (u32)cmd[13] | (u32)cmd[12] << 8 | (u32)cmd[11] << 16 |
- (u32)cmd[10] << 24;
- break;
- case WRITE_12:
- case READ_12:
- *lba = (u32)cmd[5] | (u32)cmd[4] << 8 | (u32)cmd[3] << 16 |
- (u32)cmd[2] << 24;
-
- *num = (u32)cmd[9] | (u32)cmd[8] << 8 | (u32)cmd[7] << 16 |
- (u32)cmd[6] << 24;
- break;
- case WRITE_SAME:
- case WRITE_10:
- case READ_10:
- case XDWRITEREAD_10:
- *lba = (u32)cmd[5] | (u32)cmd[4] << 8 | (u32)cmd[3] << 16 |
- (u32)cmd[2] << 24;
-
- *num = (u32)cmd[8] | (u32)cmd[7] << 8;
- break;
- case WRITE_6:
- case READ_6:
- *lba = (u32)cmd[3] | (u32)cmd[2] << 8 |
- (u32)(cmd[1] & 0x1f) << 16;
- *num = (0 == cmd[4]) ? 256 : cmd[4];
- break;
- default:
- break;
- }
+ mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_OPCODE, 0);
}
static int scsi_debug_ioctl(struct scsi_device *dev, int cmd, void __user *arg)
if (debug)
cp = "mode parameters changed";
break;
+ case SDEBUG_UA_CAPACITY_CHANGED:
+ mk_sense_buffer(SCpnt, UNIT_ATTENTION,
+ UA_CHANGED_ASC, CAPACITY_CHANGED_ASCQ);
+ if (debug)
+ cp = "capacity data changed";
default:
pr_warn("%s: unexpected unit attention code=%d\n",
__func__, k);
#define SDEBUG_LONG_INQ_SZ 96
#define SDEBUG_MAX_INQ_ARR_SZ 584
-static int resp_inquiry(struct scsi_cmnd *scp, int target,
- struct sdebug_dev_info * devip)
+static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
{
unsigned char pq_pdt;
unsigned char * arr;
unsigned char *cmd = scp->cmnd;
int alloc_len, n, ret;
+ bool have_wlun;
alloc_len = (cmd[3] << 8) + cmd[4];
arr = kzalloc(SDEBUG_MAX_INQ_ARR_SZ, GFP_ATOMIC);
if (! arr)
return DID_REQUEUE << 16;
- if (devip->wlun)
+ have_wlun = (scp->device->lun == SAM2_WLUN_REPORT_LUNS);
+ if (have_wlun)
pq_pdt = 0x1e; /* present, wlun */
else if (scsi_debug_no_lun_0 && (0 == devip->lun))
pq_pdt = 0x7f; /* not present, no device type */
pq_pdt = (scsi_debug_ptype & 0x1f);
arr[0] = pq_pdt;
if (0x2 & cmd[1]) { /* CMDDT bit set */
- mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
- 0);
+ mk_sense_invalid_fld(scp, SDEB_IN_CDB, 1, 1);
kfree(arr);
return check_condition_result;
} else if (0x1 & cmd[1]) { /* EVPD bit set */
(devip->channel & 0x7f);
if (0 == scsi_debug_vpd_use_hostno)
host_no = 0;
- lu_id_num = devip->wlun ? -1 : (((host_no + 1) * 2000) +
+ lu_id_num = have_wlun ? -1 : (((host_no + 1) * 2000) +
(devip->target * 1000) + devip->lun);
target_dev_id = ((host_no + 1) * 2000) +
(devip->target * 1000) - 3;
arr[1] = cmd[2]; /*sanity */
arr[3] = inquiry_evpd_b2(&arr[4]);
} else {
- /* Illegal request, invalid field in cdb */
- mk_sense_buffer(scp, ILLEGAL_REQUEST,
- INVALID_FIELD_IN_CDB, 0);
+ mk_sense_invalid_fld(scp, SDEB_IN_CDB, 2, -1);
kfree(arr);
return check_condition_result;
}
unsigned char * sbuff;
unsigned char *cmd = scp->cmnd;
unsigned char arr[SCSI_SENSE_BUFFERSIZE];
- int want_dsense;
+ bool dsense, want_dsense;
int len = 18;
memset(arr, 0, sizeof(arr));
- want_dsense = !!(cmd[1] & 1) || scsi_debug_dsense;
+ dsense = !!(cmd[1] & 1);
+ want_dsense = dsense || scsi_debug_dsense;
sbuff = scp->sense_buffer;
if ((iec_m_pg[2] & 0x4) && (6 == (iec_m_pg[3] & 0xf))) {
- if (want_dsense) {
+ if (dsense) {
arr[0] = 0x72;
arr[1] = 0x0; /* NO_SENSE in sense_key */
arr[2] = THRESHOLD_EXCEEDED;
arr[3] = 0xff; /* TEST set and MRIE==6 */
+ len = 8;
} else {
arr[0] = 0x70;
arr[2] = 0x0; /* NO_SENSE in sense_key */
}
} else {
memcpy(arr, sbuff, SCSI_SENSE_BUFFERSIZE);
- if ((cmd[1] & 1) && (! scsi_debug_dsense)) {
- /* DESC bit set and sense_buff in fixed format */
- memset(arr, 0, sizeof(arr));
+ if (arr[0] >= 0x70 && dsense == scsi_debug_dsense)
+ ; /* have sense and formats match */
+ else if (arr[0] <= 0x70) {
+ if (dsense) {
+ memset(arr, 0, 8);
+ arr[0] = 0x72;
+ len = 8;
+ } else {
+ memset(arr, 0, 18);
+ arr[0] = 0x70;
+ arr[7] = 0xa;
+ }
+ } else if (dsense) {
+ memset(arr, 0, 8);
arr[0] = 0x72;
arr[1] = sbuff[2]; /* sense key */
arr[2] = sbuff[12]; /* asc */
arr[3] = sbuff[13]; /* ascq */
len = 8;
+ } else {
+ memset(arr, 0, 18);
+ arr[0] = 0x70;
+ arr[2] = sbuff[1];
+ arr[7] = 0xa;
+ arr[12] = sbuff[1];
+ arr[13] = sbuff[3];
}
+
}
mk_sense_buffer(scp, 0, NO_ADDITIONAL_SENSE, 0);
return fill_from_dev_buffer(scp, arr, len);
struct sdebug_dev_info * devip)
{
unsigned char *cmd = scp->cmnd;
- int power_cond, errsts, start;
+ int power_cond, start;
- errsts = check_readiness(scp, UAS_ONLY, devip);
- if (errsts)
- return errsts;
power_cond = (cmd[4] & 0xf0) >> 4;
if (power_cond) {
- mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
- 0);
+ mk_sense_invalid_fld(scp, SDEB_IN_CDB, 4, 7);
return check_condition_result;
}
start = cmd[4] & 1;
{
unsigned char arr[SDEBUG_READCAP_ARR_SZ];
unsigned int capac;
- int errsts;
- errsts = check_readiness(scp, UAS_ONLY, devip);
- if (errsts)
- return errsts;
/* following just in case virtual_gb changed */
sdebug_capacity = get_sdebug_capacity();
memset(arr, 0, SDEBUG_READCAP_ARR_SZ);
unsigned char *cmd = scp->cmnd;
unsigned char arr[SDEBUG_READCAP16_ARR_SZ];
unsigned long long capac;
- int errsts, k, alloc_len;
+ int k, alloc_len;
- errsts = check_readiness(scp, UAS_ONLY, devip);
- if (errsts)
- return errsts;
alloc_len = ((cmd[10] << 24) + (cmd[11] << 16) + (cmd[12] << 8)
+ cmd[13]);
/* following just in case virtual_gb changed */
return ret;
}
+static int
+resp_rsup_opcodes(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
+{
+ bool rctd;
+ u8 reporting_opts, req_opcode, sdeb_i, supp;
+ u16 req_sa, u;
+ u32 alloc_len, a_len;
+ int k, offset, len, errsts, count, bump, na;
+ const struct opcode_info_t *oip;
+ const struct opcode_info_t *r_oip;
+ u8 *arr;
+ u8 *cmd = scp->cmnd;
+
+ rctd = !!(cmd[2] & 0x80);
+ reporting_opts = cmd[2] & 0x7;
+ req_opcode = cmd[3];
+ req_sa = get_unaligned_be16(cmd + 4);
+ alloc_len = get_unaligned_be32(cmd + 6);
+ if (alloc_len < 4 && alloc_len > 0xffff) {
+ mk_sense_invalid_fld(scp, SDEB_IN_CDB, 6, -1);
+ return check_condition_result;
+ }
+ if (alloc_len > 8192)
+ a_len = 8192;
+ else
+ a_len = alloc_len;
+ arr = kzalloc((a_len < 256) ? 320 : a_len + 64, GFP_KERNEL);
+ if (NULL == arr) {
+ mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC,
+ INSUFF_RES_ASCQ);
+ return check_condition_result;
+ }
+ switch (reporting_opts) {
+ case 0: /* all commands */
+ /* count number of commands */
+ for (count = 0, oip = opcode_info_arr;
+ oip->num_attached != 0xff; ++oip) {
+ if (F_INV_OP & oip->flags)
+ continue;
+ count += (oip->num_attached + 1);
+ }
+ bump = rctd ? 20 : 8;
+ put_unaligned_be32(count * bump, arr);
+ for (offset = 4, oip = opcode_info_arr;
+ oip->num_attached != 0xff && offset < a_len; ++oip) {
+ if (F_INV_OP & oip->flags)
+ continue;
+ na = oip->num_attached;
+ arr[offset] = oip->opcode;
+ put_unaligned_be16(oip->sa, arr + offset + 2);
+ if (rctd)
+ arr[offset + 5] |= 0x2;
+ if (FF_SA & oip->flags)
+ arr[offset + 5] |= 0x1;
+ put_unaligned_be16(oip->len_mask[0], arr + offset + 6);
+ if (rctd)
+ put_unaligned_be16(0xa, arr + offset + 8);
+ r_oip = oip;
+ for (k = 0, oip = oip->arrp; k < na; ++k, ++oip) {
+ if (F_INV_OP & oip->flags)
+ continue;
+ offset += bump;
+ arr[offset] = oip->opcode;
+ put_unaligned_be16(oip->sa, arr + offset + 2);
+ if (rctd)
+ arr[offset + 5] |= 0x2;
+ if (FF_SA & oip->flags)
+ arr[offset + 5] |= 0x1;
+ put_unaligned_be16(oip->len_mask[0],
+ arr + offset + 6);
+ if (rctd)
+ put_unaligned_be16(0xa,
+ arr + offset + 8);
+ }
+ oip = r_oip;
+ offset += bump;
+ }
+ break;
+ case 1: /* one command: opcode only */
+ case 2: /* one command: opcode plus service action */
+ case 3: /* one command: if sa==0 then opcode only else opcode+sa */
+ sdeb_i = opcode_ind_arr[req_opcode];
+ oip = &opcode_info_arr[sdeb_i];
+ if (F_INV_OP & oip->flags) {
+ supp = 1;
+ offset = 4;
+ } else {
+ if (1 == reporting_opts) {
+ if (FF_SA & oip->flags) {
+ mk_sense_invalid_fld(scp, SDEB_IN_CDB,
+ 2, 2);
+ kfree(arr);
+ return check_condition_result;
+ }
+ req_sa = 0;
+ } else if (2 == reporting_opts &&
+ 0 == (FF_SA & oip->flags)) {
+ mk_sense_invalid_fld(scp, SDEB_IN_CDB, 4, -1);
+ kfree(arr); /* point at requested sa */
+ return check_condition_result;
+ }
+ if (0 == (FF_SA & oip->flags) &&
+ req_opcode == oip->opcode)
+ supp = 3;
+ else if (0 == (FF_SA & oip->flags)) {
+ na = oip->num_attached;
+ for (k = 0, oip = oip->arrp; k < na;
+ ++k, ++oip) {
+ if (req_opcode == oip->opcode)
+ break;
+ }
+ supp = (k >= na) ? 1 : 3;
+ } else if (req_sa != oip->sa) {
+ na = oip->num_attached;
+ for (k = 0, oip = oip->arrp; k < na;
+ ++k, ++oip) {
+ if (req_sa == oip->sa)
+ break;
+ }
+ supp = (k >= na) ? 1 : 3;
+ } else
+ supp = 3;
+ if (3 == supp) {
+ u = oip->len_mask[0];
+ put_unaligned_be16(u, arr + 2);
+ arr[4] = oip->opcode;
+ for (k = 1; k < u; ++k)
+ arr[4 + k] = (k < 16) ?
+ oip->len_mask[k] : 0xff;
+ offset = 4 + u;
+ } else
+ offset = 4;
+ }
+ arr[1] = (rctd ? 0x80 : 0) | supp;
+ if (rctd) {
+ put_unaligned_be16(0xa, arr + offset);
+ offset += 12;
+ }
+ break;
+ default:
+ mk_sense_invalid_fld(scp, SDEB_IN_CDB, 2, 2);
+ kfree(arr);
+ return check_condition_result;
+ }
+ offset = (offset < a_len) ? offset : a_len;
+ len = (offset < alloc_len) ? offset : alloc_len;
+ errsts = fill_from_dev_buffer(scp, arr, len);
+ kfree(arr);
+ return errsts;
+}
+
+static int
+resp_rsup_tmfs(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
+{
+ bool repd;
+ u32 alloc_len, len;
+ u8 arr[16];
+ u8 *cmd = scp->cmnd;
+
+ memset(arr, 0, sizeof(arr));
+ repd = !!(cmd[2] & 0x80);
+ alloc_len = get_unaligned_be32(cmd + 6);
+ if (alloc_len < 4) {
+ mk_sense_invalid_fld(scp, SDEB_IN_CDB, 6, -1);
+ return check_condition_result;
+ }
+ arr[0] = 0xc8; /* ATS | ATSS | LURS */
+ arr[1] = 0x1; /* ITNRS */
+ if (repd) {
+ arr[3] = 0xc;
+ len = 16;
+ } else
+ len = 4;
+
+ len = (len < alloc_len) ? len : alloc_len;
+ return fill_from_dev_buffer(scp, arr, len);
+}
+
/* <<Following mode page info copied from ST318451LW>> */
static int resp_err_recov_pg(unsigned char * p, int pcontrol, int target)
#define SDEBUG_MAX_MSENSE_SZ 256
-static int resp_mode_sense(struct scsi_cmnd * scp, int target,
- struct sdebug_dev_info * devip)
+static int
+resp_mode_sense(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
{
unsigned char dbd, llbaa;
int pcontrol, pcode, subpcode, bd_len;
unsigned char dev_spec;
- int k, alloc_len, msense_6, offset, len, errsts, target_dev_id;
+ int k, alloc_len, msense_6, offset, len, target_dev_id;
+ int target = scp->device->id;
unsigned char * ap;
unsigned char arr[SDEBUG_MAX_MSENSE_SZ];
unsigned char *cmd = scp->cmnd;
- errsts = check_readiness(scp, UAS_ONLY, devip);
- if (errsts)
- return errsts;
dbd = !!(cmd[1] & 0x8);
pcontrol = (cmd[2] & 0xc0) >> 6;
pcode = cmd[2] & 0x3f;
if ((subpcode > 0x0) && (subpcode < 0xff) && (0x19 != pcode)) {
/* TODO: Control Extension page */
- mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
- 0);
+ mk_sense_invalid_fld(scp, SDEB_IN_CDB, 3, -1);
return check_condition_result;
}
switch (pcode) {
break;
case 0x19: /* if spc==1 then sas phy, control+discover */
if ((subpcode > 0x2) && (subpcode < 0xff)) {
- mk_sense_buffer(scp, ILLEGAL_REQUEST,
- INVALID_FIELD_IN_CDB, 0);
+ mk_sense_invalid_fld(scp, SDEB_IN_CDB, 3, -1);
return check_condition_result;
}
len = 0;
}
len += resp_iec_m_pg(ap + len, pcontrol, target);
} else {
- mk_sense_buffer(scp, ILLEGAL_REQUEST,
- INVALID_FIELD_IN_CDB, 0);
+ mk_sense_invalid_fld(scp, SDEB_IN_CDB, 3, -1);
return check_condition_result;
}
offset += len;
break;
default:
- mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
- 0);
+ mk_sense_invalid_fld(scp, SDEB_IN_CDB, 2, 5);
return check_condition_result;
}
if (msense_6)
#define SDEBUG_MAX_MSELECT_SZ 512
-static int resp_mode_select(struct scsi_cmnd * scp, int mselect6,
- struct sdebug_dev_info * devip)
+static int
+resp_mode_select(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
{
int pf, sp, ps, md_len, bd_len, off, spf, pg_len;
- int param_len, res, errsts, mpage;
+ int param_len, res, mpage;
unsigned char arr[SDEBUG_MAX_MSELECT_SZ];
unsigned char *cmd = scp->cmnd;
+ int mselect6 = (MODE_SELECT == cmd[0]);
- errsts = check_readiness(scp, UAS_ONLY, devip);
- if (errsts)
- return errsts;
memset(arr, 0, sizeof(arr));
pf = cmd[1] & 0x10;
sp = cmd[1] & 0x1;
param_len = mselect6 ? cmd[4] : ((cmd[7] << 8) + cmd[8]);
if ((0 == pf) || sp || (param_len > SDEBUG_MAX_MSELECT_SZ)) {
- mk_sense_buffer(scp, ILLEGAL_REQUEST,
- INVALID_FIELD_IN_CDB, 0);
+ mk_sense_invalid_fld(scp, SDEB_IN_CDB, mselect6 ? 4 : 7, -1);
return check_condition_result;
}
res = fetch_to_dev_buffer(scp, arr, param_len);
md_len = mselect6 ? (arr[0] + 1) : ((arr[0] << 8) + arr[1] + 2);
bd_len = mselect6 ? arr[3] : ((arr[6] << 8) + arr[7]);
if (md_len > 2) {
- mk_sense_buffer(scp, ILLEGAL_REQUEST,
- INVALID_FIELD_IN_PARAM_LIST, 0);
+ mk_sense_invalid_fld(scp, SDEB_IN_DATA, 0, -1);
return check_condition_result;
}
off = bd_len + (mselect6 ? 4 : 8);
mpage = arr[off] & 0x3f;
ps = !!(arr[off] & 0x80);
if (ps) {
- mk_sense_buffer(scp, ILLEGAL_REQUEST,
- INVALID_FIELD_IN_PARAM_LIST, 0);
+ mk_sense_invalid_fld(scp, SDEB_IN_DATA, off, 7);
return check_condition_result;
}
spf = !!(arr[off] & 0x40);
default:
break;
}
- mk_sense_buffer(scp, ILLEGAL_REQUEST,
- INVALID_FIELD_IN_PARAM_LIST, 0);
+ mk_sense_invalid_fld(scp, SDEB_IN_DATA, off, 5);
return check_condition_result;
set_mode_changed_ua:
set_bit(SDEBUG_UA_MODE_CHANGED, devip->uas_bm);
static int resp_log_sense(struct scsi_cmnd * scp,
struct sdebug_dev_info * devip)
{
- int ppc, sp, pcontrol, pcode, subpcode, alloc_len, errsts, len, n;
+ int ppc, sp, pcontrol, pcode, subpcode, alloc_len, len, n;
unsigned char arr[SDEBUG_MAX_LSENSE_SZ];
unsigned char *cmd = scp->cmnd;
- errsts = check_readiness(scp, UAS_ONLY, devip);
- if (errsts)
- return errsts;
memset(arr, 0, sizeof(arr));
ppc = cmd[1] & 0x2;
sp = cmd[1] & 0x1;
if (ppc || sp) {
- mk_sense_buffer(scp, ILLEGAL_REQUEST,
- INVALID_FIELD_IN_CDB, 0);
+ mk_sense_invalid_fld(scp, SDEB_IN_CDB, 1, ppc ? 1 : 0);
return check_condition_result;
}
pcontrol = (cmd[2] & 0xc0) >> 6;
arr[3] = resp_ie_l_pg(arr + 4);
break;
default:
- mk_sense_buffer(scp, ILLEGAL_REQUEST,
- INVALID_FIELD_IN_CDB, 0);
+ mk_sense_invalid_fld(scp, SDEB_IN_CDB, 2, 5);
return check_condition_result;
}
} else if (0xff == subpcode) {
arr[3] = n - 4;
break;
default:
- mk_sense_buffer(scp, ILLEGAL_REQUEST,
- INVALID_FIELD_IN_CDB, 0);
+ mk_sense_invalid_fld(scp, SDEB_IN_CDB, 2, 5);
return check_condition_result;
}
} else {
- mk_sense_buffer(scp, ILLEGAL_REQUEST,
- INVALID_FIELD_IN_CDB, 0);
+ mk_sense_invalid_fld(scp, SDEB_IN_CDB, 3, -1);
return check_condition_result;
}
len = min(((arr[2] << 8) + arr[3]) + 4, alloc_len);
unsigned long long lba, unsigned int num)
{
if (lba + num > sdebug_capacity) {
- mk_sense_buffer(scp, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE, 0);
+ mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0);
return check_condition_result;
}
/* transfer length excessive (tie in to block limits VPD page) */
if (num > sdebug_store_sectors) {
+ /* needs work to find which cdb byte 'num' comes from */
mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
return check_condition_result;
}
}
/* Returns number of bytes copied or -1 if error. */
-static int do_device_access(struct scsi_cmnd *scmd,
- unsigned long long lba, unsigned int num, int write)
+static int
+do_device_access(struct scsi_cmnd *scmd, u64 lba, u32 num, bool do_write)
{
int ret;
- unsigned long long block, rest = 0;
+ u64 block, rest = 0;
struct scsi_data_buffer *sdb;
enum dma_data_direction dir;
size_t (*func)(struct scatterlist *, unsigned int, void *, size_t,
off_t);
- if (write) {
+ if (do_write) {
sdb = scsi_out(scmd);
dir = DMA_TO_DEVICE;
func = sg_pcopy_to_buffer;
return ret;
}
+/* If fake_store(lba,num) compares equal to arr(num), then copy top half of
+ * arr into fake_store(lba,num) and return true. If comparison fails then
+ * return false. */
+static bool
+comp_write_worker(u64 lba, u32 num, const u8 *arr)
+{
+ bool res;
+ u64 block, rest = 0;
+ u32 store_blks = sdebug_store_sectors;
+ u32 lb_size = scsi_debug_sector_size;
+
+ block = do_div(lba, store_blks);
+ if (block + num > store_blks)
+ rest = block + num - store_blks;
+
+ res = !memcmp(fake_storep + (block * lb_size), arr,
+ (num - rest) * lb_size);
+ if (!res)
+ return res;
+ if (rest)
+ res = memcmp(fake_storep, arr + ((num - rest) * lb_size),
+ rest * lb_size);
+ if (!res)
+ return res;
+ arr += num * lb_size;
+ memcpy(fake_storep + (block * lb_size), arr, (num - rest) * lb_size);
+ if (rest)
+ memcpy(fake_storep, arr + ((num - rest) * lb_size),
+ rest * lb_size);
+ return res;
+}
+
static __be16 dif_compute_csum(const void *buf, int len)
{
__be16 csum;
return 0;
}
-static int resp_read(struct scsi_cmnd *SCpnt, unsigned long long lba,
- unsigned int num, u32 ei_lba)
+static int
+resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
{
+ u8 *cmd = scp->cmnd;
+ u64 lba;
+ u32 num;
+ u32 ei_lba;
unsigned long iflags;
int ret;
+ bool check_prot;
- ret = check_device_access_params(SCpnt, lba, num);
- if (ret)
- return ret;
+ switch (cmd[0]) {
+ case READ_16:
+ ei_lba = 0;
+ lba = get_unaligned_be64(cmd + 2);
+ num = get_unaligned_be32(cmd + 10);
+ check_prot = true;
+ break;
+ case READ_10:
+ ei_lba = 0;
+ lba = get_unaligned_be32(cmd + 2);
+ num = get_unaligned_be16(cmd + 7);
+ check_prot = true;
+ break;
+ case READ_6:
+ ei_lba = 0;
+ lba = (u32)cmd[3] | (u32)cmd[2] << 8 |
+ (u32)(cmd[1] & 0x1f) << 16;
+ num = (0 == cmd[4]) ? 256 : cmd[4];
+ check_prot = true;
+ break;
+ case READ_12:
+ ei_lba = 0;
+ lba = get_unaligned_be32(cmd + 2);
+ num = get_unaligned_be32(cmd + 6);
+ check_prot = true;
+ break;
+ case XDWRITEREAD_10:
+ ei_lba = 0;
+ lba = get_unaligned_be32(cmd + 2);
+ num = get_unaligned_be16(cmd + 7);
+ check_prot = false;
+ break;
+ default: /* assume READ(32) */
+ lba = get_unaligned_be64(cmd + 12);
+ ei_lba = get_unaligned_be32(cmd + 20);
+ num = get_unaligned_be32(cmd + 28);
+ check_prot = false;
+ break;
+ }
+ if (check_prot) {
+ if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION &&
+ (cmd[1] & 0xe0)) {
+ mk_sense_invalid_opcode(scp);
+ return check_condition_result;
+ }
+ if ((scsi_debug_dif == SD_DIF_TYPE1_PROTECTION ||
+ scsi_debug_dif == SD_DIF_TYPE3_PROTECTION) &&
+ (cmd[1] & 0xe0) == 0)
+ sdev_printk(KERN_ERR, scp->device, "Unprotected RD "
+ "to DIF device\n");
+ }
+ if (sdebug_any_injecting_opt) {
+ struct sdebug_scmd_extra_t *ep = scsi_cmd_priv(scp);
+
+ if (ep->inj_short)
+ num /= 2;
+ }
+
+ /* inline check_device_access_params() */
+ if (lba + num > sdebug_capacity) {
+ mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0);
+ return check_condition_result;
+ }
+ /* transfer length excessive (tie in to block limits VPD page) */
+ if (num > sdebug_store_sectors) {
+ /* needs work to find which cdb byte 'num' comes from */
+ mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
+ return check_condition_result;
+ }
if ((SCSI_DEBUG_OPT_MEDIUM_ERR & scsi_debug_opts) &&
(lba <= (OPT_MEDIUM_ERR_ADDR + OPT_MEDIUM_ERR_NUM - 1)) &&
((lba + num) > OPT_MEDIUM_ERR_ADDR)) {
/* claim unrecoverable read error */
- mk_sense_buffer(SCpnt, MEDIUM_ERROR, UNRECOVERED_READ_ERR, 0);
+ mk_sense_buffer(scp, MEDIUM_ERROR, UNRECOVERED_READ_ERR, 0);
/* set info field and valid bit for fixed descriptor */
- if (0x70 == (SCpnt->sense_buffer[0] & 0x7f)) {
- SCpnt->sense_buffer[0] |= 0x80; /* Valid bit */
+ if (0x70 == (scp->sense_buffer[0] & 0x7f)) {
+ scp->sense_buffer[0] |= 0x80; /* Valid bit */
ret = (lba < OPT_MEDIUM_ERR_ADDR)
? OPT_MEDIUM_ERR_ADDR : (int)lba;
- SCpnt->sense_buffer[3] = (ret >> 24) & 0xff;
- SCpnt->sense_buffer[4] = (ret >> 16) & 0xff;
- SCpnt->sense_buffer[5] = (ret >> 8) & 0xff;
- SCpnt->sense_buffer[6] = ret & 0xff;
+ put_unaligned_be32(ret, scp->sense_buffer + 3);
}
- scsi_set_resid(SCpnt, scsi_bufflen(SCpnt));
+ scsi_set_resid(scp, scsi_bufflen(scp));
return check_condition_result;
}
read_lock_irqsave(&atomic_rw, iflags);
/* DIX + T10 DIF */
- if (scsi_debug_dix && scsi_prot_sg_count(SCpnt)) {
- int prot_ret = prot_verify_read(SCpnt, lba, num, ei_lba);
+ if (scsi_debug_dix && scsi_prot_sg_count(scp)) {
+ int prot_ret = prot_verify_read(scp, lba, num, ei_lba);
if (prot_ret) {
read_unlock_irqrestore(&atomic_rw, iflags);
- mk_sense_buffer(SCpnt, ABORTED_COMMAND, 0x10, prot_ret);
+ mk_sense_buffer(scp, ABORTED_COMMAND, 0x10, prot_ret);
return illegal_condition_result;
}
}
- ret = do_device_access(SCpnt, lba, num, 0);
+ ret = do_device_access(scp, lba, num, false);
read_unlock_irqrestore(&atomic_rw, iflags);
if (ret == -1)
return DID_ERROR << 16;
- scsi_in(SCpnt)->resid = scsi_bufflen(SCpnt) - ret;
+ scsi_in(scp)->resid = scsi_bufflen(scp) - ret;
+
+ if (sdebug_any_injecting_opt) {
+ struct sdebug_scmd_extra_t *ep = scsi_cmd_priv(scp);
+ if (ep->inj_recovered) {
+ mk_sense_buffer(scp, RECOVERED_ERROR,
+ THRESHOLD_EXCEEDED, 0);
+ return check_condition_result;
+ } else if (ep->inj_transport) {
+ mk_sense_buffer(scp, ABORTED_COMMAND,
+ TRANSPORT_PROBLEM, ACK_NAK_TO);
+ return check_condition_result;
+ } else if (ep->inj_dif) {
+ /* Logical block guard check failed */
+ mk_sense_buffer(scp, ABORTED_COMMAND, 0x10, 1);
+ return illegal_condition_result;
+ } else if (ep->inj_dix) {
+ mk_sense_buffer(scp, ILLEGAL_REQUEST, 0x10, 1);
+ return illegal_condition_result;
+ }
+ }
return 0;
}
}
}
-static int resp_write(struct scsi_cmnd *SCpnt, unsigned long long lba,
- unsigned int num, u32 ei_lba)
+static int
+resp_write_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
{
+ u8 *cmd = scp->cmnd;
+ u64 lba;
+ u32 num;
+ u32 ei_lba;
unsigned long iflags;
int ret;
+ bool check_prot;
- ret = check_device_access_params(SCpnt, lba, num);
- if (ret)
- return ret;
+ switch (cmd[0]) {
+ case WRITE_16:
+ ei_lba = 0;
+ lba = get_unaligned_be64(cmd + 2);
+ num = get_unaligned_be32(cmd + 10);
+ check_prot = true;
+ break;
+ case WRITE_10:
+ ei_lba = 0;
+ lba = get_unaligned_be32(cmd + 2);
+ num = get_unaligned_be16(cmd + 7);
+ check_prot = true;
+ break;
+ case WRITE_6:
+ ei_lba = 0;
+ lba = (u32)cmd[3] | (u32)cmd[2] << 8 |
+ (u32)(cmd[1] & 0x1f) << 16;
+ num = (0 == cmd[4]) ? 256 : cmd[4];
+ check_prot = true;
+ break;
+ case WRITE_12:
+ ei_lba = 0;
+ lba = get_unaligned_be32(cmd + 2);
+ num = get_unaligned_be32(cmd + 6);
+ check_prot = true;
+ break;
+ case 0x53: /* XDWRITEREAD(10) */
+ ei_lba = 0;
+ lba = get_unaligned_be32(cmd + 2);
+ num = get_unaligned_be16(cmd + 7);
+ check_prot = false;
+ break;
+ default: /* assume WRITE(32) */
+ lba = get_unaligned_be64(cmd + 12);
+ ei_lba = get_unaligned_be32(cmd + 20);
+ num = get_unaligned_be32(cmd + 28);
+ check_prot = false;
+ break;
+ }
+ if (check_prot) {
+ if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION &&
+ (cmd[1] & 0xe0)) {
+ mk_sense_invalid_opcode(scp);
+ return check_condition_result;
+ }
+ if ((scsi_debug_dif == SD_DIF_TYPE1_PROTECTION ||
+ scsi_debug_dif == SD_DIF_TYPE3_PROTECTION) &&
+ (cmd[1] & 0xe0) == 0)
+ sdev_printk(KERN_ERR, scp->device, "Unprotected WR "
+ "to DIF device\n");
+ }
+
+ /* inline check_device_access_params() */
+ if (lba + num > sdebug_capacity) {
+ mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0);
+ return check_condition_result;
+ }
+ /* transfer length excessive (tie in to block limits VPD page) */
+ if (num > sdebug_store_sectors) {
+ /* needs work to find which cdb byte 'num' comes from */
+ mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
+ return check_condition_result;
+ }
write_lock_irqsave(&atomic_rw, iflags);
/* DIX + T10 DIF */
- if (scsi_debug_dix && scsi_prot_sg_count(SCpnt)) {
- int prot_ret = prot_verify_write(SCpnt, lba, num, ei_lba);
+ if (scsi_debug_dix && scsi_prot_sg_count(scp)) {
+ int prot_ret = prot_verify_write(scp, lba, num, ei_lba);
if (prot_ret) {
write_unlock_irqrestore(&atomic_rw, iflags);
- mk_sense_buffer(SCpnt, ILLEGAL_REQUEST, 0x10,
- prot_ret);
+ mk_sense_buffer(scp, ILLEGAL_REQUEST, 0x10, prot_ret);
return illegal_condition_result;
}
}
- ret = do_device_access(SCpnt, lba, num, 1);
+ ret = do_device_access(scp, lba, num, true);
if (scsi_debug_lbp())
map_region(lba, num);
write_unlock_irqrestore(&atomic_rw, iflags);
return (DID_ERROR << 16);
else if ((ret < (num * scsi_debug_sector_size)) &&
(SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
- sdev_printk(KERN_INFO, SCpnt->device,
+ sdev_printk(KERN_INFO, scp->device,
"%s: write: cdb indicated=%u, IO sent=%d bytes\n",
my_name, num * scsi_debug_sector_size, ret);
+ if (sdebug_any_injecting_opt) {
+ struct sdebug_scmd_extra_t *ep = scsi_cmd_priv(scp);
+
+ if (ep->inj_recovered) {
+ mk_sense_buffer(scp, RECOVERED_ERROR,
+ THRESHOLD_EXCEEDED, 0);
+ return check_condition_result;
+ } else if (ep->inj_dif) {
+ /* Logical block guard check failed */
+ mk_sense_buffer(scp, ABORTED_COMMAND, 0x10, 1);
+ return illegal_condition_result;
+ } else if (ep->inj_dix) {
+ mk_sense_buffer(scp, ILLEGAL_REQUEST, 0x10, 1);
+ return illegal_condition_result;
+ }
+ }
return 0;
}
-static int resp_write_same(struct scsi_cmnd *scmd, unsigned long long lba,
- unsigned int num, u32 ei_lba, unsigned int unmap)
+static int
+resp_write_same(struct scsi_cmnd *scp, u64 lba, u32 num, u32 ei_lba,
+ bool unmap, bool ndob)
{
unsigned long iflags;
unsigned long long i;
int ret;
- ret = check_device_access_params(scmd, lba, num);
+ ret = check_device_access_params(scp, lba, num);
if (ret)
return ret;
- if (num > scsi_debug_write_same_length) {
- mk_sense_buffer(scmd, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
- 0);
- return check_condition_result;
- }
-
write_lock_irqsave(&atomic_rw, iflags);
if (unmap && scsi_debug_lbp()) {
goto out;
}
- /* Else fetch one logical block */
- ret = fetch_to_dev_buffer(scmd,
- fake_storep + (lba * scsi_debug_sector_size),
- scsi_debug_sector_size);
+ /* if ndob then zero 1 logical block, else fetch 1 logical block */
+ if (ndob) {
+ memset(fake_storep + (lba * scsi_debug_sector_size), 0,
+ scsi_debug_sector_size);
+ ret = 0;
+ } else
+ ret = fetch_to_dev_buffer(scp, fake_storep +
+ (lba * scsi_debug_sector_size),
+ scsi_debug_sector_size);
if (-1 == ret) {
write_unlock_irqrestore(&atomic_rw, iflags);
return (DID_ERROR << 16);
} else if ((ret < (num * scsi_debug_sector_size)) &&
(SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
- sdev_printk(KERN_INFO, scmd->device,
+ sdev_printk(KERN_INFO, scp->device,
"%s: %s: cdb indicated=%u, IO sent=%d bytes\n",
my_name, "write same",
num * scsi_debug_sector_size, ret);
return 0;
}
-struct unmap_block_desc {
- __be64 lba;
- __be32 blocks;
- __be32 __reserved;
-};
+static int
+resp_write_same_10(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
+{
+ u8 *cmd = scp->cmnd;
+ u32 lba;
+ u16 num;
+ u32 ei_lba = 0;
+ bool unmap = false;
+
+ if (cmd[1] & 0x8) {
+ if (scsi_debug_lbpws10 == 0) {
+ mk_sense_invalid_fld(scp, SDEB_IN_CDB, 1, 3);
+ return check_condition_result;
+ } else
+ unmap = true;
+ }
+ lba = get_unaligned_be32(cmd + 2);
+ num = get_unaligned_be16(cmd + 7);
+ if (num > scsi_debug_write_same_length) {
+ mk_sense_invalid_fld(scp, SDEB_IN_CDB, 7, -1);
+ return check_condition_result;
+ }
+ return resp_write_same(scp, lba, num, ei_lba, unmap, false);
+}
-static int resp_unmap(struct scsi_cmnd * scmd, struct sdebug_dev_info * devip)
+static int
+resp_write_same_16(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
{
- unsigned char *buf;
+ u8 *cmd = scp->cmnd;
+ u64 lba;
+ u32 num;
+ u32 ei_lba = 0;
+ bool unmap = false;
+ bool ndob = false;
+
+ if (cmd[1] & 0x8) { /* UNMAP */
+ if (scsi_debug_lbpws == 0) {
+ mk_sense_invalid_fld(scp, SDEB_IN_CDB, 1, 3);
+ return check_condition_result;
+ } else
+ unmap = true;
+ }
+ if (cmd[1] & 0x1) /* NDOB (no data-out buffer, assumes zeroes) */
+ ndob = true;
+ lba = get_unaligned_be64(cmd + 2);
+ num = get_unaligned_be32(cmd + 10);
+ if (num > scsi_debug_write_same_length) {
+ mk_sense_invalid_fld(scp, SDEB_IN_CDB, 10, -1);
+ return check_condition_result;
+ }
+ return resp_write_same(scp, lba, num, ei_lba, unmap, ndob);
+}
+
+static int
+resp_comp_write(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
+{
+ u8 *cmd = scp->cmnd;
+ u8 *arr;
+ u8 *fake_storep_hold;
+ u64 lba;
+ u32 dnum;
+ u32 lb_size = scsi_debug_sector_size;
+ u8 num;
+ unsigned long iflags;
+ int ret;
+
+ lba = get_unaligned_be32(cmd + 2);
+ num = cmd[13]; /* 1 to a maximum of 255 logical blocks */
+ if (0 == num)
+ return 0; /* degenerate case, not an error */
+ dnum = 2 * num;
+ arr = kzalloc(dnum * lb_size, GFP_ATOMIC);
+ if (NULL == arr) {
+ mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC,
+ INSUFF_RES_ASCQ);
+ return check_condition_result;
+ }
+ if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION &&
+ (cmd[1] & 0xe0)) {
+ mk_sense_invalid_opcode(scp);
+ return check_condition_result;
+ }
+ if ((scsi_debug_dif == SD_DIF_TYPE1_PROTECTION ||
+ scsi_debug_dif == SD_DIF_TYPE3_PROTECTION) &&
+ (cmd[1] & 0xe0) == 0)
+ sdev_printk(KERN_ERR, scp->device, "Unprotected WR "
+ "to DIF device\n");
+
+ /* inline check_device_access_params() */
+ if (lba + num > sdebug_capacity) {
+ mk_sense_buffer(scp, ILLEGAL_REQUEST, LBA_OUT_OF_RANGE, 0);
+ return check_condition_result;
+ }
+ /* transfer length excessive (tie in to block limits VPD page) */
+ if (num > sdebug_store_sectors) {
+ /* needs work to find which cdb byte 'num' comes from */
+ mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
+ return check_condition_result;
+ }
+
+ write_lock_irqsave(&atomic_rw, iflags);
+
+ /* trick do_device_access() to fetch both compare and write buffers
+ * from data-in into arr. Safe (atomic) since write_lock held. */
+ fake_storep_hold = fake_storep;
+ fake_storep = arr;
+ ret = do_device_access(scp, 0, dnum, true);
+ fake_storep = fake_storep_hold;
+ if (ret == -1) {
+ write_unlock_irqrestore(&atomic_rw, iflags);
+ kfree(arr);
+ return DID_ERROR << 16;
+ } else if ((ret < (dnum * lb_size)) &&
+ (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
+ sdev_printk(KERN_INFO, scp->device, "%s: compare_write: cdb "
+ "indicated=%u, IO sent=%d bytes\n", my_name,
+ dnum * lb_size, ret);
+ if (!comp_write_worker(lba, num, arr)) {
+ write_unlock_irqrestore(&atomic_rw, iflags);
+ kfree(arr);
+ mk_sense_buffer(scp, MISCOMPARE, MISCOMPARE_VERIFY_ASC, 0);
+ return check_condition_result;
+ }
+ if (scsi_debug_lbp())
+ map_region(lba, num);
+ write_unlock_irqrestore(&atomic_rw, iflags);
+ return 0;
+}
+
+struct unmap_block_desc {
+ __be64 lba;
+ __be32 blocks;
+ __be32 __reserved;
+};
+
+static int
+resp_unmap(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
+{
+ unsigned char *buf;
struct unmap_block_desc *desc;
unsigned int i, payload_len, descriptors;
int ret;
unsigned long iflags;
- ret = check_readiness(scmd, UAS_ONLY, devip);
- if (ret)
- return ret;
- payload_len = get_unaligned_be16(&scmd->cmnd[7]);
- BUG_ON(scsi_bufflen(scmd) != payload_len);
+ if (!scsi_debug_lbp())
+ return 0; /* fib and say its done */
+ payload_len = get_unaligned_be16(scp->cmnd + 7);
+ BUG_ON(scsi_bufflen(scp) != payload_len);
descriptors = (payload_len - 8) / 16;
+ if (descriptors > scsi_debug_unmap_max_desc) {
+ mk_sense_invalid_fld(scp, SDEB_IN_CDB, 7, -1);
+ return check_condition_result;
+ }
- buf = kmalloc(scsi_bufflen(scmd), GFP_ATOMIC);
- if (!buf)
+ buf = kmalloc(scsi_bufflen(scp), GFP_ATOMIC);
+ if (!buf) {
+ mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC,
+ INSUFF_RES_ASCQ);
return check_condition_result;
+ }
- scsi_sg_copy_to_buffer(scmd, buf, scsi_bufflen(scmd));
+ scsi_sg_copy_to_buffer(scp, buf, scsi_bufflen(scp));
BUG_ON(get_unaligned_be16(&buf[0]) != payload_len - 2);
BUG_ON(get_unaligned_be16(&buf[2]) != descriptors * 16);
unsigned long long lba = get_unaligned_be64(&desc[i].lba);
unsigned int num = get_unaligned_be32(&desc[i].blocks);
- ret = check_device_access_params(scmd, lba, num);
+ ret = check_device_access_params(scp, lba, num);
if (ret)
goto out;
#define SDEBUG_GET_LBA_STATUS_LEN 32
-static int resp_get_lba_status(struct scsi_cmnd * scmd,
- struct sdebug_dev_info * devip)
+static int
+resp_get_lba_status(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
{
- unsigned long long lba;
- unsigned int alloc_len, mapped, num;
- unsigned char arr[SDEBUG_GET_LBA_STATUS_LEN];
+ u8 *cmd = scp->cmnd;
+ u64 lba;
+ u32 alloc_len, mapped, num;
+ u8 arr[SDEBUG_GET_LBA_STATUS_LEN];
int ret;
- ret = check_readiness(scmd, UAS_ONLY, devip);
- if (ret)
- return ret;
-
- lba = get_unaligned_be64(&scmd->cmnd[2]);
- alloc_len = get_unaligned_be32(&scmd->cmnd[10]);
+ lba = get_unaligned_be64(cmd + 2);
+ alloc_len = get_unaligned_be32(cmd + 10);
if (alloc_len < 24)
return 0;
- ret = check_device_access_params(scmd, lba, 1);
+ ret = check_device_access_params(scp, lba, 1);
if (ret)
return ret;
- mapped = map_state(lba, &num);
+ if (scsi_debug_lbp())
+ mapped = map_state(lba, &num);
+ else {
+ mapped = 1;
+ /* following just in case virtual_gb changed */
+ sdebug_capacity = get_sdebug_capacity();
+ if (sdebug_capacity - lba <= 0xffffffff)
+ num = sdebug_capacity - lba;
+ else
+ num = 0xffffffff;
+ }
memset(arr, 0, SDEBUG_GET_LBA_STATUS_LEN);
- put_unaligned_be32(20, &arr[0]); /* Parameter Data Length */
- put_unaligned_be64(lba, &arr[8]); /* LBA */
- put_unaligned_be32(num, &arr[16]); /* Number of blocks */
- arr[20] = !mapped; /* mapped = 0, unmapped = 1 */
+ put_unaligned_be32(20, arr); /* Parameter Data Length */
+ put_unaligned_be64(lba, arr + 8); /* LBA */
+ put_unaligned_be32(num, arr + 16); /* Number of blocks */
+ arr[20] = !mapped; /* prov_stat=0: mapped; 1: dealloc */
- return fill_from_dev_buffer(scmd, arr, SDEBUG_GET_LBA_STATUS_LEN);
+ return fill_from_dev_buffer(scp, arr, SDEBUG_GET_LBA_STATUS_LEN);
}
#define SDEBUG_RLUN_ARR_SZ 256
struct sdebug_dev_info * devip)
{
unsigned int alloc_len;
- int lun_cnt, i, upper, num, n;
- u64 wlun, lun;
+ int lun_cnt, i, upper, num, n, want_wlun, shortish;
+ u64 lun;
unsigned char *cmd = scp->cmnd;
int select_report = (int)cmd[2];
struct scsi_lun *one_lun;
unsigned char * max_addr;
alloc_len = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24);
- if ((alloc_len < 4) || (select_report > 2)) {
- mk_sense_buffer(scp, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
- 0);
+ shortish = (alloc_len < 4);
+ if (shortish || (select_report > 2)) {
+ mk_sense_invalid_fld(scp, SDEB_IN_CDB, shortish ? 6 : 2, -1);
return check_condition_result;
}
/* can produce response with up to 16k luns (lun 0 to lun 16383) */
lun_cnt = 0;
else if (scsi_debug_no_lun_0 && (lun_cnt > 0))
--lun_cnt;
- wlun = (select_report > 0) ? 1 : 0;
- num = lun_cnt + wlun;
+ want_wlun = (select_report > 0) ? 1 : 0;
+ num = lun_cnt + want_wlun;
arr[2] = ((sizeof(struct scsi_lun) * num) >> 8) & 0xff;
arr[3] = (sizeof(struct scsi_lun) * num) & 0xff;
n = min((int)((SDEBUG_RLUN_ARR_SZ - 8) /
sizeof(struct scsi_lun)), num);
if (n < num) {
- wlun = 0;
+ want_wlun = 0;
lun_cnt = n;
}
one_lun = (struct scsi_lun *) &arr[8];
(upper | (SAM2_LUN_ADDRESS_METHOD << 6));
one_lun[i].scsi_lun[1] = lun & 0xff;
}
- if (wlun) {
+ if (want_wlun) {
one_lun[i].scsi_lun[0] = (SAM2_WLUN_REPORT_LUNS >> 8) & 0xff;
one_lun[i].scsi_lun[1] = SAM2_WLUN_REPORT_LUNS & 0xff;
i++;
/* better not to use temporary buffer. */
buf = kmalloc(scsi_bufflen(scp), GFP_ATOMIC);
if (!buf) {
- mk_sense_buffer(scp, NOT_READY,
- LOGICAL_UNIT_COMMUNICATION_FAILURE, 0);
+ mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC,
+ INSUFF_RES_ASCQ);
return check_condition_result;
}
return 0;
}
+static int
+resp_xdwriteread_10(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
+{
+ u8 *cmd = scp->cmnd;
+ u64 lba;
+ u32 num;
+ int errsts;
+
+ if (!scsi_bidi_cmnd(scp)) {
+ mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC,
+ INSUFF_RES_ASCQ);
+ return check_condition_result;
+ }
+ errsts = resp_read_dt0(scp, devip);
+ if (errsts)
+ return errsts;
+ if (!(cmd[1] & 0x4)) { /* DISABLE_WRITE is not set */
+ errsts = resp_write_dt0(scp, devip);
+ if (errsts)
+ return errsts;
+ }
+ lba = get_unaligned_be32(cmd + 2);
+ num = get_unaligned_be16(cmd + 7);
+ return resp_xdwriteread(scp, lba, num, devip);
+}
+
/* When timer or tasklet goes off this function is called. */
static void sdebug_q_cmd_complete(unsigned long indx)
{
open_devip->sdbg_host = sdbg_host;
atomic_set(&open_devip->num_in_q, 0);
set_bit(SDEBUG_UA_POR, open_devip->uas_bm);
- open_devip->used = 1;
- if (sdev->lun == SAM2_WLUN_REPORT_LUNS)
- open_devip->wlun = SAM2_WLUN_REPORT_LUNS & 0xff;
-
+ open_devip->used = true;
return open_devip;
}
if (NULL == devip)
return 1; /* no resources, will be marked offline */
sdp->hostdata = devip;
- sdp->tagged_supported = 1;
- if (sdp->host->cmd_per_lun)
- scsi_adjust_queue_depth(sdp, DEF_TAGGED_QUEUING,
- DEF_CMD_PER_LUN);
blk_queue_max_segment_size(sdp->request_queue, -1U);
if (scsi_debug_no_uld)
sdp->no_uld_attach = 1;
sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
if (devip) {
/* make this slot available for re-use */
- devip->used = 0;
+ devip->used = false;
sdp->hostdata = NULL;
}
}
module_param_named(removable, scsi_debug_removable, bool, S_IRUGO | S_IWUSR);
module_param_named(scsi_level, scsi_debug_scsi_level, int, S_IRUGO);
module_param_named(sector_size, scsi_debug_sector_size, int, S_IRUGO);
+module_param_named(strict, scsi_debug_strict, bool, S_IRUGO | S_IWUSR);
module_param_named(unmap_alignment, scsi_debug_unmap_alignment, int, S_IRUGO);
module_param_named(unmap_granularity, scsi_debug_unmap_granularity, int, S_IRUGO);
module_param_named(unmap_max_blocks, scsi_debug_unmap_max_blocks, int, S_IRUGO);
MODULE_PARM_DESC(ato, "application tag ownership: 0=disk 1=host (def=1)");
MODULE_PARM_DESC(clustering, "when set enables larger transfers (def=0)");
MODULE_PARM_DESC(delay, "response delay (def=1 jiffy); 0:imm, -1,-2:tiny");
-MODULE_PARM_DESC(dev_size_mb, "size in MB of ram shared by devs(def=8)");
+MODULE_PARM_DESC(dev_size_mb, "size in MiB of ram shared by devs(def=8)");
MODULE_PARM_DESC(dif, "data integrity field type: 0-3 (def=0)");
MODULE_PARM_DESC(dix, "data integrity extensions mask (def=0)");
MODULE_PARM_DESC(dsense, "use descriptor sense format(def=0 -> fixed)");
MODULE_PARM_DESC(removable, "claim to have removable media (def=0)");
MODULE_PARM_DESC(scsi_level, "SCSI level to simulate(def=6[SPC-4])");
MODULE_PARM_DESC(sector_size, "logical block size in bytes (def=512)");
+MODULE_PARM_DESC(strict, "stricter checks: reserved field in cdb (def=0)");
MODULE_PARM_DESC(unmap_alignment, "lowest aligned thin provisioning lba (def=0)");
MODULE_PARM_DESC(unmap_granularity, "thin provisioning granularity in blocks (def=1)");
MODULE_PARM_DESC(unmap_max_blocks, "max # of blocks can be unmapped in one cmd (def=0xffffffff)");
MODULE_PARM_DESC(unmap_max_desc, "max # of ranges that can be unmapped in one cmd (def=256)");
-MODULE_PARM_DESC(virtual_gb, "virtual gigabyte size (def=0 -> use dev_size_mb)");
+MODULE_PARM_DESC(virtual_gb, "virtual gigabyte (GiB) size (def=0 -> use dev_size_mb)");
MODULE_PARM_DESC(vpd_use_hostno, "0 -> dev ids ignore hostno (def=1 -> unique dev ids)");
MODULE_PARM_DESC(write_same_length, "Maximum blocks per WRITE SAME cmd (def=0xffff)");
return -EINVAL;
opts_done:
scsi_debug_opts = opts;
+ if (SCSI_DEBUG_OPT_RECOVERED_ERR & opts)
+ sdebug_any_injecting_opt = true;
+ else if (SCSI_DEBUG_OPT_TRANSPORT_ERR & opts)
+ sdebug_any_injecting_opt = true;
+ else if (SCSI_DEBUG_OPT_DIF_ERR & opts)
+ sdebug_any_injecting_opt = true;
+ else if (SCSI_DEBUG_OPT_DIX_ERR & opts)
+ sdebug_any_injecting_opt = true;
+ else if (SCSI_DEBUG_OPT_SHORT_TRANSFER & opts)
+ sdebug_any_injecting_opt = true;
atomic_set(&sdebug_cmnd_count, 0);
atomic_set(&sdebug_a_tsf, 0);
return count;
size_t count)
{
int n;
+ bool changed;
if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
+ changed = (scsi_debug_virtual_gb != n);
scsi_debug_virtual_gb = n;
-
sdebug_capacity = get_sdebug_capacity();
-
+ if (changed) {
+ struct sdebug_host_info *sdhp;
+ struct sdebug_dev_info *dp;
+
+ list_for_each_entry(sdhp, &sdebug_host_list,
+ host_list) {
+ list_for_each_entry(dp, &sdhp->dev_info_list,
+ dev_list) {
+ set_bit(SDEBUG_UA_CAPACITY_CHANGED,
+ dp->uas_bm);
+ }
+ }
+ }
return count;
}
return -EINVAL;
}
static DRIVER_ATTR_RW(host_lock);
+static ssize_t strict_show(struct device_driver *ddp, char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%d\n", !!scsi_debug_strict);
+}
+static ssize_t strict_store(struct device_driver *ddp, const char *buf,
+ size_t count)
+{
+ int n;
+
+ if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
+ scsi_debug_strict = (n > 0);
+ return count;
+ }
+ return -EINVAL;
+}
+static DRIVER_ATTR_RW(strict);
+
/* Note: The following array creates attribute files in the
/sys/bus/pseudo/drivers/scsi_debug directory. The advantage of these
&driver_attr_removable.attr,
&driver_attr_host_lock.attr,
&driver_attr_ndelay.attr,
+ &driver_attr_strict.attr,
NULL,
};
ATTRIBUTE_GROUPS(sdebug_drv);
}
static int
-scsi_debug_queuecommand(struct scsi_cmnd *SCpnt)
-{
- unsigned char *cmd = SCpnt->cmnd;
- int len, k;
- unsigned int num;
- unsigned long long lba;
- u32 ei_lba;
- int errsts = 0;
- int target = SCpnt->device->id;
- struct sdebug_dev_info *devip = NULL;
- int inj_recovered = 0;
- int inj_transport = 0;
- int inj_dif = 0;
- int inj_dix = 0;
- int inj_short = 0;
- int delay_override = 0;
- int unmap = 0;
-
- scsi_set_resid(SCpnt, 0);
- if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) &&
- !(SCSI_DEBUG_OPT_NO_CDB_NOISE & scsi_debug_opts)) {
- char b[120];
- int n;
-
- len = SCpnt->cmd_len;
- if (len > 32)
- strcpy(b, "too long, over 32 bytes");
- else {
- for (k = 0, n = 0; k < len; ++k)
- n += scnprintf(b + n, sizeof(b) - n, "%02x ",
- (unsigned int)cmd[k]);
- }
- sdev_printk(KERN_INFO, SCpnt->device, "%s: cmd %s\n", my_name,
- b);
- }
-
- if ((SCpnt->device->lun >= scsi_debug_max_luns) &&
- (SCpnt->device->lun != SAM2_WLUN_REPORT_LUNS))
- return schedule_resp(SCpnt, NULL, DID_NO_CONNECT << 16, 0);
- devip = devInfoReg(SCpnt->device);
- if (NULL == devip)
- return schedule_resp(SCpnt, NULL, DID_NO_CONNECT << 16, 0);
-
- if ((scsi_debug_every_nth != 0) &&
- (atomic_inc_return(&sdebug_cmnd_count) >=
- abs(scsi_debug_every_nth))) {
- atomic_set(&sdebug_cmnd_count, 0);
- if (scsi_debug_every_nth < -1)
- scsi_debug_every_nth = -1;
- if (SCSI_DEBUG_OPT_TIMEOUT & scsi_debug_opts)
- return 0; /* ignore command causing timeout */
- else if (SCSI_DEBUG_OPT_MAC_TIMEOUT & scsi_debug_opts &&
- scsi_medium_access_command(SCpnt))
- return 0; /* time out reads and writes */
- else if (SCSI_DEBUG_OPT_RECOVERED_ERR & scsi_debug_opts)
- inj_recovered = 1; /* to reads and writes below */
- else if (SCSI_DEBUG_OPT_TRANSPORT_ERR & scsi_debug_opts)
- inj_transport = 1; /* to reads and writes below */
- else if (SCSI_DEBUG_OPT_DIF_ERR & scsi_debug_opts)
- inj_dif = 1; /* to reads and writes below */
- else if (SCSI_DEBUG_OPT_DIX_ERR & scsi_debug_opts)
- inj_dix = 1; /* to reads and writes below */
- else if (SCSI_DEBUG_OPT_SHORT_TRANSFER & scsi_debug_opts)
- inj_short = 1;
- }
-
- if (devip->wlun) {
- switch (*cmd) {
- case INQUIRY:
- case REQUEST_SENSE:
- case TEST_UNIT_READY:
- case REPORT_LUNS:
- break; /* only allowable wlun commands */
- default:
- if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
- printk(KERN_INFO "scsi_debug: Opcode: 0x%x "
- "not supported for wlun\n", *cmd);
- mk_sense_buffer(SCpnt, ILLEGAL_REQUEST,
- INVALID_OPCODE, 0);
- errsts = check_condition_result;
- return schedule_resp(SCpnt, devip, errsts, 0);
- }
- }
-
- switch (*cmd) {
- case INQUIRY: /* mandatory, ignore unit attention */
- delay_override = 1;
- errsts = resp_inquiry(SCpnt, target, devip);
- break;
- case REQUEST_SENSE: /* mandatory, ignore unit attention */
- delay_override = 1;
- errsts = resp_requests(SCpnt, devip);
- break;
- case REZERO_UNIT: /* actually this is REWIND for SSC */
- case START_STOP:
- errsts = resp_start_stop(SCpnt, devip);
- break;
- case ALLOW_MEDIUM_REMOVAL:
- errsts = check_readiness(SCpnt, UAS_ONLY, devip);
- if (errsts)
- break;
- if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
- printk(KERN_INFO "scsi_debug: Medium removal %s\n",
- cmd[4] ? "inhibited" : "enabled");
- break;
- case SEND_DIAGNOSTIC: /* mandatory */
- errsts = check_readiness(SCpnt, UAS_ONLY, devip);
- break;
- case TEST_UNIT_READY: /* mandatory */
- /* delay_override = 1; */
- errsts = check_readiness(SCpnt, UAS_TUR, devip);
- break;
- case RESERVE:
- errsts = check_readiness(SCpnt, UAS_ONLY, devip);
- break;
- case RESERVE_10:
- errsts = check_readiness(SCpnt, UAS_ONLY, devip);
- break;
- case RELEASE:
- errsts = check_readiness(SCpnt, UAS_ONLY, devip);
- break;
- case RELEASE_10:
- errsts = check_readiness(SCpnt, UAS_ONLY, devip);
- break;
- case READ_CAPACITY:
- errsts = resp_readcap(SCpnt, devip);
- break;
- case SERVICE_ACTION_IN:
- if (cmd[1] == SAI_READ_CAPACITY_16)
- errsts = resp_readcap16(SCpnt, devip);
- else if (cmd[1] == SAI_GET_LBA_STATUS) {
-
- if (scsi_debug_lbp() == 0) {
- mk_sense_buffer(SCpnt, ILLEGAL_REQUEST,
- INVALID_COMMAND_OPCODE, 0);
- errsts = check_condition_result;
- } else
- errsts = resp_get_lba_status(SCpnt, devip);
- } else {
- mk_sense_buffer(SCpnt, ILLEGAL_REQUEST,
- INVALID_OPCODE, 0);
- errsts = check_condition_result;
- }
- break;
- case MAINTENANCE_IN:
- if (MI_REPORT_TARGET_PGS != cmd[1]) {
- mk_sense_buffer(SCpnt, ILLEGAL_REQUEST,
- INVALID_OPCODE, 0);
- errsts = check_condition_result;
- break;
- }
- errsts = resp_report_tgtpgs(SCpnt, devip);
- break;
- case READ_16:
- case READ_12:
- case READ_10:
- /* READ{10,12,16} and DIF Type 2 are natural enemies */
- if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION &&
- cmd[1] & 0xe0) {
- mk_sense_buffer(SCpnt, ILLEGAL_REQUEST,
- INVALID_COMMAND_OPCODE, 0);
- errsts = check_condition_result;
- break;
- }
-
- if ((scsi_debug_dif == SD_DIF_TYPE1_PROTECTION ||
- scsi_debug_dif == SD_DIF_TYPE3_PROTECTION) &&
- (cmd[1] & 0xe0) == 0)
- printk(KERN_ERR "Unprotected RD/WR to DIF device\n");
-
- /* fall through */
- case READ_6:
-read:
- errsts = check_readiness(SCpnt, UAS_TUR, devip);
- if (errsts)
- break;
- if (scsi_debug_fake_rw)
- break;
- get_data_transfer_info(cmd, &lba, &num, &ei_lba);
-
- if (inj_short)
- num /= 2;
-
- errsts = resp_read(SCpnt, lba, num, ei_lba);
- if (inj_recovered && (0 == errsts)) {
- mk_sense_buffer(SCpnt, RECOVERED_ERROR,
- THRESHOLD_EXCEEDED, 0);
- errsts = check_condition_result;
- } else if (inj_transport && (0 == errsts)) {
- mk_sense_buffer(SCpnt, ABORTED_COMMAND,
- TRANSPORT_PROBLEM, ACK_NAK_TO);
- errsts = check_condition_result;
- } else if (inj_dif && (0 == errsts)) {
- /* Logical block guard check failed */
- mk_sense_buffer(SCpnt, ABORTED_COMMAND, 0x10, 1);
- errsts = illegal_condition_result;
- } else if (inj_dix && (0 == errsts)) {
- mk_sense_buffer(SCpnt, ILLEGAL_REQUEST, 0x10, 1);
- errsts = illegal_condition_result;
- }
- break;
- case REPORT_LUNS: /* mandatory, ignore unit attention */
- delay_override = 1;
- errsts = resp_report_luns(SCpnt, devip);
- break;
- case VERIFY: /* 10 byte SBC-2 command */
- errsts = check_readiness(SCpnt, UAS_TUR, devip);
- break;
- case WRITE_16:
- case WRITE_12:
- case WRITE_10:
- /* WRITE{10,12,16} and DIF Type 2 are natural enemies */
- if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION &&
- cmd[1] & 0xe0) {
- mk_sense_buffer(SCpnt, ILLEGAL_REQUEST,
- INVALID_COMMAND_OPCODE, 0);
- errsts = check_condition_result;
- break;
- }
-
- if ((scsi_debug_dif == SD_DIF_TYPE1_PROTECTION ||
- scsi_debug_dif == SD_DIF_TYPE3_PROTECTION) &&
- (cmd[1] & 0xe0) == 0)
- printk(KERN_ERR "Unprotected RD/WR to DIF device\n");
-
- /* fall through */
- case WRITE_6:
-write:
- errsts = check_readiness(SCpnt, UAS_TUR, devip);
- if (errsts)
- break;
- if (scsi_debug_fake_rw)
- break;
- get_data_transfer_info(cmd, &lba, &num, &ei_lba);
- errsts = resp_write(SCpnt, lba, num, ei_lba);
- if (inj_recovered && (0 == errsts)) {
- mk_sense_buffer(SCpnt, RECOVERED_ERROR,
- THRESHOLD_EXCEEDED, 0);
- errsts = check_condition_result;
- } else if (inj_dif && (0 == errsts)) {
- mk_sense_buffer(SCpnt, ABORTED_COMMAND, 0x10, 1);
- errsts = illegal_condition_result;
- } else if (inj_dix && (0 == errsts)) {
- mk_sense_buffer(SCpnt, ILLEGAL_REQUEST, 0x10, 1);
- errsts = illegal_condition_result;
- }
- break;
- case WRITE_SAME_16:
- case WRITE_SAME:
- if (cmd[1] & 0x8) {
- if ((*cmd == WRITE_SAME_16 && scsi_debug_lbpws == 0) ||
- (*cmd == WRITE_SAME && scsi_debug_lbpws10 == 0)) {
- mk_sense_buffer(SCpnt, ILLEGAL_REQUEST,
- INVALID_FIELD_IN_CDB, 0);
- errsts = check_condition_result;
- } else
- unmap = 1;
- }
- if (errsts)
- break;
- errsts = check_readiness(SCpnt, UAS_TUR, devip);
- if (errsts)
- break;
- if (scsi_debug_fake_rw)
- break;
- get_data_transfer_info(cmd, &lba, &num, &ei_lba);
- errsts = resp_write_same(SCpnt, lba, num, ei_lba, unmap);
- break;
- case UNMAP:
- errsts = check_readiness(SCpnt, UAS_TUR, devip);
- if (errsts)
- break;
- if (scsi_debug_fake_rw)
- break;
-
- if (scsi_debug_unmap_max_desc == 0 || scsi_debug_lbpu == 0) {
- mk_sense_buffer(SCpnt, ILLEGAL_REQUEST,
- INVALID_COMMAND_OPCODE, 0);
- errsts = check_condition_result;
- } else
- errsts = resp_unmap(SCpnt, devip);
- break;
- case MODE_SENSE:
- case MODE_SENSE_10:
- errsts = resp_mode_sense(SCpnt, target, devip);
- break;
- case MODE_SELECT:
- errsts = resp_mode_select(SCpnt, 1, devip);
- break;
- case MODE_SELECT_10:
- errsts = resp_mode_select(SCpnt, 0, devip);
- break;
- case LOG_SENSE:
- errsts = resp_log_sense(SCpnt, devip);
- break;
- case SYNCHRONIZE_CACHE:
- delay_override = 1;
- errsts = check_readiness(SCpnt, UAS_TUR, devip);
- break;
- case WRITE_BUFFER:
- errsts = check_readiness(SCpnt, UAS_ONLY, devip);
- break;
- case XDWRITEREAD_10:
- if (!scsi_bidi_cmnd(SCpnt)) {
- mk_sense_buffer(SCpnt, ILLEGAL_REQUEST,
- INVALID_FIELD_IN_CDB, 0);
- errsts = check_condition_result;
- break;
- }
-
- errsts = check_readiness(SCpnt, UAS_TUR, devip);
- if (errsts)
- break;
- if (scsi_debug_fake_rw)
- break;
- get_data_transfer_info(cmd, &lba, &num, &ei_lba);
- errsts = resp_read(SCpnt, lba, num, ei_lba);
- if (errsts)
- break;
- errsts = resp_write(SCpnt, lba, num, ei_lba);
- if (errsts)
- break;
- errsts = resp_xdwriteread(SCpnt, lba, num, devip);
- break;
- case VARIABLE_LENGTH_CMD:
- if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION) {
-
- if ((cmd[10] & 0xe0) == 0)
- printk(KERN_ERR
- "Unprotected RD/WR to DIF device\n");
-
- if (cmd[9] == READ_32) {
- BUG_ON(SCpnt->cmd_len < 32);
- goto read;
- }
-
- if (cmd[9] == WRITE_32) {
- BUG_ON(SCpnt->cmd_len < 32);
- goto write;
- }
- }
-
- mk_sense_buffer(SCpnt, ILLEGAL_REQUEST,
- INVALID_FIELD_IN_CDB, 0);
- errsts = check_condition_result;
- break;
- case 0x85:
- if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
- sdev_printk(KERN_INFO, SCpnt->device,
- "%s: ATA PASS-THROUGH(16) not supported\n", my_name);
- mk_sense_buffer(SCpnt, ILLEGAL_REQUEST,
- INVALID_OPCODE, 0);
- errsts = check_condition_result;
- break;
- default:
- if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
- sdev_printk(KERN_INFO, SCpnt->device,
- "%s: Opcode: 0x%x not supported\n",
- my_name, *cmd);
- errsts = check_readiness(SCpnt, UAS_ONLY, devip);
- if (errsts)
- break; /* Unit attention takes precedence */
- mk_sense_buffer(SCpnt, ILLEGAL_REQUEST, INVALID_OPCODE, 0);
- errsts = check_condition_result;
- break;
- }
- return schedule_resp(SCpnt, devip, errsts,
- (delay_override ? 0 : scsi_debug_delay));
-}
-
-static int
-sdebug_queuecommand_lock_or_not(struct Scsi_Host *shost, struct scsi_cmnd *cmd)
-{
- if (scsi_debug_host_lock) {
- unsigned long iflags;
- int rc;
-
- spin_lock_irqsave(shost->host_lock, iflags);
- rc = scsi_debug_queuecommand(cmd);
- spin_unlock_irqrestore(shost->host_lock, iflags);
- return rc;
- } else
- return scsi_debug_queuecommand(cmd);
-}
-
-static int
-sdebug_change_qdepth(struct scsi_device *sdev, int qdepth, int reason)
+sdebug_change_qdepth(struct scsi_device *sdev, int qdepth)
{
int num_in_q = 0;
- int bad = 0;
unsigned long iflags;
struct sdebug_dev_info *devip;
}
num_in_q = atomic_read(&devip->num_in_q);
spin_unlock_irqrestore(&queued_arr_lock, iflags);
- if (reason == SCSI_QDEPTH_DEFAULT || reason == SCSI_QDEPTH_RAMP_UP) {
- if (qdepth < 1)
- qdepth = 1;
- /* allow to exceed max host queued_arr elements for testing */
- if (qdepth > SCSI_DEBUG_CANQUEUE + 10)
- qdepth = SCSI_DEBUG_CANQUEUE + 10;
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
- } else if (reason == SCSI_QDEPTH_QFULL)
- scsi_track_queue_full(sdev, qdepth);
- else
- bad = 1;
- if (bad)
- sdev_printk(KERN_WARNING, sdev,
- "%s: unknown reason=0x%x\n", __func__, reason);
+
+ if (qdepth < 1)
+ qdepth = 1;
+ /* allow to exceed max host queued_arr elements for testing */
+ if (qdepth > SCSI_DEBUG_CANQUEUE + 10)
+ qdepth = SCSI_DEBUG_CANQUEUE + 10;
+ scsi_change_queue_depth(sdev, qdepth);
+
if (SCSI_DEBUG_OPT_Q_NOISE & scsi_debug_opts) {
- if (SCSI_QDEPTH_QFULL == reason)
- sdev_printk(KERN_INFO, sdev,
- "%s: -> %d, num_in_q=%d, reason: queue full\n",
- __func__, qdepth, num_in_q);
- else {
- const char *cp;
-
- switch (reason) {
- case SCSI_QDEPTH_DEFAULT:
- cp = "default (sysfs ?)";
- break;
- case SCSI_QDEPTH_RAMP_UP:
- cp = "ramp up";
- break;
- default:
- cp = "unknown";
- break;
- }
- sdev_printk(KERN_INFO, sdev,
- "%s: qdepth=%d, num_in_q=%d, reason: %s\n",
- __func__, qdepth, num_in_q, cp);
- }
+ sdev_printk(KERN_INFO, sdev,
+ "%s: qdepth=%d, num_in_q=%d\n",
+ __func__, qdepth, num_in_q);
}
return sdev->queue_depth;
}
static int
sdebug_change_qtype(struct scsi_device *sdev, int qtype)
{
- if (sdev->tagged_supported) {
- scsi_set_tag_type(sdev, qtype);
- if (qtype)
- scsi_activate_tcq(sdev, sdev->queue_depth);
- else
- scsi_deactivate_tcq(sdev, sdev->queue_depth);
- } else
- qtype = 0;
+ qtype = scsi_change_queue_type(sdev, qtype);
if (SCSI_DEBUG_OPT_Q_NOISE & scsi_debug_opts) {
const char *cp;
return qtype;
}
+static int
+check_inject(struct scsi_cmnd *scp)
+{
+ struct sdebug_scmd_extra_t *ep = scsi_cmd_priv(scp);
+
+ memset(ep, 0, sizeof(struct sdebug_scmd_extra_t));
+
+ if (atomic_inc_return(&sdebug_cmnd_count) >=
+ abs(scsi_debug_every_nth)) {
+ atomic_set(&sdebug_cmnd_count, 0);
+ if (scsi_debug_every_nth < -1)
+ scsi_debug_every_nth = -1;
+ if (SCSI_DEBUG_OPT_TIMEOUT & scsi_debug_opts)
+ return 1; /* ignore command causing timeout */
+ else if (SCSI_DEBUG_OPT_MAC_TIMEOUT & scsi_debug_opts &&
+ scsi_medium_access_command(scp))
+ return 1; /* time out reads and writes */
+ if (sdebug_any_injecting_opt) {
+ int opts = scsi_debug_opts;
+
+ if (SCSI_DEBUG_OPT_RECOVERED_ERR & opts)
+ ep->inj_recovered = true;
+ else if (SCSI_DEBUG_OPT_TRANSPORT_ERR & opts)
+ ep->inj_transport = true;
+ else if (SCSI_DEBUG_OPT_DIF_ERR & opts)
+ ep->inj_dif = true;
+ else if (SCSI_DEBUG_OPT_DIX_ERR & opts)
+ ep->inj_dix = true;
+ else if (SCSI_DEBUG_OPT_SHORT_TRANSFER & opts)
+ ep->inj_short = true;
+ }
+ }
+ return 0;
+}
+
+static int
+scsi_debug_queuecommand(struct scsi_cmnd *scp)
+{
+ u8 sdeb_i;
+ struct scsi_device *sdp = scp->device;
+ const struct opcode_info_t *oip;
+ const struct opcode_info_t *r_oip;
+ struct sdebug_dev_info *devip;
+ u8 *cmd = scp->cmnd;
+ int (*r_pfp)(struct scsi_cmnd *, struct sdebug_dev_info *);
+ int k, na;
+ int errsts = 0;
+ int errsts_no_connect = DID_NO_CONNECT << 16;
+ u32 flags;
+ u16 sa;
+ u8 opcode = cmd[0];
+ bool has_wlun_rl;
+ bool debug = !!(SCSI_DEBUG_OPT_NOISE & scsi_debug_opts);
+
+ scsi_set_resid(scp, 0);
+ if (debug && !(SCSI_DEBUG_OPT_NO_CDB_NOISE & scsi_debug_opts)) {
+ char b[120];
+ int n, len, sb;
+
+ len = scp->cmd_len;
+ sb = (int)sizeof(b);
+ if (len > 32)
+ strcpy(b, "too long, over 32 bytes");
+ else {
+ for (k = 0, n = 0; k < len && n < sb; ++k)
+ n += scnprintf(b + n, sb - n, "%02x ",
+ (u32)cmd[k]);
+ }
+ sdev_printk(KERN_INFO, sdp, "%s: cmd %s\n", my_name, b);
+ }
+ has_wlun_rl = (sdp->lun == SAM2_WLUN_REPORT_LUNS);
+ if ((sdp->lun >= scsi_debug_max_luns) && !has_wlun_rl)
+ return schedule_resp(scp, NULL, errsts_no_connect, 0);
+
+ sdeb_i = opcode_ind_arr[opcode]; /* fully mapped */
+ oip = &opcode_info_arr[sdeb_i]; /* safe if table consistent */
+ devip = (struct sdebug_dev_info *)sdp->hostdata;
+ if (!devip) {
+ devip = devInfoReg(sdp);
+ if (NULL == devip)
+ return schedule_resp(scp, NULL, errsts_no_connect, 0);
+ }
+ na = oip->num_attached;
+ r_pfp = oip->pfp;
+ if (na) { /* multiple commands with this opcode */
+ r_oip = oip;
+ if (FF_SA & r_oip->flags) {
+ if (F_SA_LOW & oip->flags)
+ sa = 0x1f & cmd[1];
+ else
+ sa = get_unaligned_be16(cmd + 8);
+ for (k = 0; k <= na; oip = r_oip->arrp + k++) {
+ if (opcode == oip->opcode && sa == oip->sa)
+ break;
+ }
+ } else { /* since no service action only check opcode */
+ for (k = 0; k <= na; oip = r_oip->arrp + k++) {
+ if (opcode == oip->opcode)
+ break;
+ }
+ }
+ if (k > na) {
+ if (F_SA_LOW & r_oip->flags)
+ mk_sense_invalid_fld(scp, SDEB_IN_CDB, 1, 4);
+ else if (F_SA_HIGH & r_oip->flags)
+ mk_sense_invalid_fld(scp, SDEB_IN_CDB, 8, 7);
+ else
+ mk_sense_invalid_opcode(scp);
+ goto check_cond;
+ }
+ } /* else (when na==0) we assume the oip is a match */
+ flags = oip->flags;
+ if (F_INV_OP & flags) {
+ mk_sense_invalid_opcode(scp);
+ goto check_cond;
+ }
+ if (has_wlun_rl && !(F_RL_WLUN_OK & flags)) {
+ if (debug)
+ sdev_printk(KERN_INFO, sdp, "scsi_debug: Opcode: "
+ "0x%x not supported for wlun\n", opcode);
+ mk_sense_invalid_opcode(scp);
+ goto check_cond;
+ }
+ if (scsi_debug_strict) { /* check cdb against mask */
+ u8 rem;
+ int j;
+
+ for (k = 1; k < oip->len_mask[0] && k < 16; ++k) {
+ rem = ~oip->len_mask[k] & cmd[k];
+ if (rem) {
+ for (j = 7; j >= 0; --j, rem <<= 1) {
+ if (0x80 & rem)
+ break;
+ }
+ mk_sense_invalid_fld(scp, SDEB_IN_CDB, k, j);
+ goto check_cond;
+ }
+ }
+ }
+ if (!(F_SKIP_UA & flags) &&
+ SDEBUG_NUM_UAS != find_first_bit(devip->uas_bm, SDEBUG_NUM_UAS)) {
+ errsts = check_readiness(scp, UAS_ONLY, devip);
+ if (errsts)
+ goto check_cond;
+ }
+ if ((F_M_ACCESS & flags) && devip->stopped) {
+ mk_sense_buffer(scp, NOT_READY, LOGICAL_UNIT_NOT_READY, 0x2);
+ if (debug)
+ sdev_printk(KERN_INFO, sdp, "%s reports: Not ready: "
+ "%s\n", my_name, "initializing command "
+ "required");
+ errsts = check_condition_result;
+ goto fini;
+ }
+ if (scsi_debug_fake_rw && (F_FAKE_RW & flags))
+ goto fini;
+ if (scsi_debug_every_nth) {
+ if (check_inject(scp))
+ return 0; /* ignore command: make trouble */
+ }
+ if (oip->pfp) /* if this command has a resp_* function, call it */
+ errsts = oip->pfp(scp, devip);
+ else if (r_pfp) /* if leaf function ptr NULL, try the root's */
+ errsts = r_pfp(scp, devip);
+
+fini:
+ return schedule_resp(scp, devip, errsts,
+ ((F_DELAY_OVERR & flags) ? 0 : scsi_debug_delay));
+check_cond:
+ return schedule_resp(scp, devip, check_condition_result, 0);
+}
+
+static int
+sdebug_queuecommand_lock_or_not(struct Scsi_Host *shost, struct scsi_cmnd *cmd)
+{
+ if (scsi_debug_host_lock) {
+ unsigned long iflags;
+ int rc;
+
+ spin_lock_irqsave(shost->host_lock, iflags);
+ rc = scsi_debug_queuecommand(cmd);
+ spin_unlock_irqrestore(shost->host_lock, iflags);
+ return rc;
+ } else
+ return scsi_debug_queuecommand(cmd);
+}
+
static struct scsi_host_template sdebug_driver_template = {
.show_info = scsi_debug_show_info,
.write_info = scsi_debug_write_info,
.max_sectors = -1U,
.use_clustering = DISABLE_CLUSTERING,
.module = THIS_MODULE,
+ .track_queue_depth = 1,
+ .cmd_size = sizeof(struct sdebug_scmd_extra_t),
};
static int sdebug_driver_probe(struct device * dev)
{
- int error = 0;
- struct sdebug_host_info *sdbg_host;
- struct Scsi_Host *hpnt;
+ int error = 0;
+ int opts;
+ struct sdebug_host_info *sdbg_host;
+ struct Scsi_Host *hpnt;
int host_prot;
sdbg_host = to_sdebug_host(dev);
sdebug_driver_template.use_clustering = ENABLE_CLUSTERING;
hpnt = scsi_host_alloc(&sdebug_driver_template, sizeof(sdbg_host));
if (NULL == hpnt) {
- printk(KERN_ERR "%s: scsi_register failed\n", __func__);
+ pr_err("%s: scsi_host_alloc failed\n", __func__);
error = -ENODEV;
return error;
}
else
scsi_host_set_guard(hpnt, SHOST_DIX_GUARD_CRC);
+ opts = scsi_debug_opts;
+ if (SCSI_DEBUG_OPT_RECOVERED_ERR & opts)
+ sdebug_any_injecting_opt = true;
+ else if (SCSI_DEBUG_OPT_TRANSPORT_ERR & opts)
+ sdebug_any_injecting_opt = true;
+ else if (SCSI_DEBUG_OPT_DIF_ERR & opts)
+ sdebug_any_injecting_opt = true;
+ else if (SCSI_DEBUG_OPT_DIX_ERR & opts)
+ sdebug_any_injecting_opt = true;
+ else if (SCSI_DEBUG_OPT_SHORT_TRANSFER & opts)
+ sdebug_any_injecting_opt = true;
+
error = scsi_add_host(hpnt, &sdbg_host->dev);
if (error) {
printk(KERN_ERR "%s: scsi_add_host failed\n", __func__);
{"IOMEGA", "Io20S *F", NULL, BLIST_KEY},
{"INSITE", "Floptical F*8I", NULL, BLIST_KEY},
{"INSITE", "I325VM", NULL, BLIST_KEY},
+ {"Intel", "Multi-Flex", NULL, BLIST_NO_RSOC},
{"iRiver", "iFP Mass Driver", NULL, BLIST_NOT_LOCKABLE | BLIST_INQUIRY_36},
{"LASOUND", "CDX7405", "3.10", BLIST_MAX5LUN | BLIST_SINGLELUN},
{"MATSHITA", "PD-1", NULL, BLIST_FORCELUN | BLIST_SINGLELUN},
#include <scsi/scsi_transport.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_ioctl.h>
+#include <scsi/sg.h>
#include "scsi_priv.h"
#include "scsi_logging.h"
} else {
SCSI_LOG_ERROR_RECOVERY(3,
scmd_printk(KERN_INFO, scmd,
- "scmd %p abort failed, rtn %d\n",
- scmd, rtn));
+ "scmd %p abort %s\n", scmd,
+ (rtn == FAST_IO_FAIL) ?
+ "not send" : "failed"));
}
}
if (cmd_cancel || cmd_failed) {
SCSI_LOG_ERROR_RECOVERY(3,
- sdev_printk(KERN_INFO, sdev,
+ shost_printk(KERN_INFO, shost,
"%s: cmds failed: %d, cancel: %d\n",
__func__, cmd_failed,
cmd_cancel));
if (! scsi_command_normalize_sense(scmd, &sshdr))
return FAILED; /* no valid sense data */
- if (scmd->cmnd[0] == TEST_UNIT_READY && scmd->scsi_done != scsi_eh_done)
- /*
- * nasty: for mid-layer issued TURs, we need to return the
- * actual sense data without any recovery attempt. For eh
- * issued ones, we need to try to recover and interpret
- */
- return SUCCESS;
-
scsi_report_sense(sdev, &sshdr);
if (scsi_sense_is_deferred(&sshdr))
/* handler does not care. Drop down to default handling */
}
+ if (scmd->cmnd[0] == TEST_UNIT_READY && scmd->scsi_done != scsi_eh_done)
+ /*
+ * nasty: for mid-layer issued TURs, we need to return the
+ * actual sense data without any recovery attempt. For eh
+ * issued ones, we need to try to recover and interpret
+ */
+ return SUCCESS;
+
/*
* Previous logic looked for FILEMARK, EOM or ILI which are
* mainly associated with tapes and returned SUCCESS.
struct scsi_host_template *sht = sdev->host->hostt;
struct scsi_device *tmp_sdev;
- if (!sht->change_queue_depth ||
+ if (!sht->track_queue_depth ||
sdev->queue_depth >= sdev->max_queue_depth)
return;
tmp_sdev->id != sdev->id ||
tmp_sdev->queue_depth == sdev->max_queue_depth)
continue;
- /*
- * call back into LLD to increase queue_depth by one
- * with ramp up reason code.
- */
- sht->change_queue_depth(tmp_sdev, tmp_sdev->queue_depth + 1,
- SCSI_QDEPTH_RAMP_UP);
+
+ scsi_change_queue_depth(tmp_sdev, tmp_sdev->queue_depth + 1);
sdev->last_queue_ramp_up = jiffies;
}
}
struct scsi_host_template *sht = sdev->host->hostt;
struct scsi_device *tmp_sdev;
- if (!sht->change_queue_depth)
+ if (!sht->track_queue_depth)
return;
shost_for_each_device(tmp_sdev, sdev->host) {
* the device when we got the queue full so we start
* from the highest possible value and work our way down.
*/
- sht->change_queue_depth(tmp_sdev, tmp_sdev->queue_depth - 1,
- SCSI_QDEPTH_QFULL);
+ scsi_track_queue_full(tmp_sdev, tmp_sdev->queue_depth - 1);
}
}
return rtn;
}
-static int scsi_try_to_abort_cmd(struct scsi_host_template *hostt, struct scsi_cmnd *scmd)
+/**
+ * scsi_try_to_abort_cmd - Ask host to abort a SCSI command
+ * @scmd: SCSI cmd used to send a target reset
+ *
+ * Return value:
+ * SUCCESS, FAILED, or FAST_IO_FAIL
+ *
+ * Notes:
+ * SUCCESS does not necessarily indicate that the command
+ * has been aborted; it only indicates that the LLDDs
+ * has cleared all references to that command.
+ * LLDDs should return FAILED only if an abort was required
+ * but could not be executed. LLDDs should return FAST_IO_FAIL
+ * if the device is temporarily unavailable (eg due to a
+ * link down on FibreChannel)
+ */
+static int scsi_try_to_abort_cmd(struct scsi_host_template *hostt,
+ struct scsi_cmnd *scmd)
{
if (!hostt->eh_abort_handler)
return FAILED;
shost = scmd->device->host;
if (scsi_host_eh_past_deadline(shost)) {
SCSI_LOG_ERROR_RECOVERY(3,
- shost_printk(KERN_INFO, shost,
- "skip %s, past eh deadline\n",
- __func__));
+ scmd_printk(KERN_INFO, scmd,
+ "%s: skip request sense, past eh deadline\n",
+ current->comm));
break;
}
if (status_byte(scmd->result) != CHECK_CONDITION)
SCSI_LOG_ERROR_RECOVERY(3, scmd_printk(KERN_INFO, scmd,
"sense requested for %p result %x\n",
scmd, scmd->result));
- SCSI_LOG_ERROR_RECOVERY(3, scsi_print_sense("bh", scmd));
+ SCSI_LOG_ERROR_RECOVERY(3, scsi_print_sense(scmd));
rtn = scsi_decide_disposition(scmd);
/* Push items back onto work_q */
list_splice_init(cmd_list, work_q);
SCSI_LOG_ERROR_RECOVERY(3,
- shost_printk(KERN_INFO, sdev->host,
- "skip %s, past eh deadline",
- __func__));
+ sdev_printk(KERN_INFO, sdev,
+ "%s: skip test device, past eh deadline",
+ current->comm));
break;
}
}
if (scsi_host_eh_past_deadline(shost)) {
list_splice_init(&check_list, work_q);
SCSI_LOG_ERROR_RECOVERY(3,
- shost_printk(KERN_INFO, shost,
- "skip %s, past eh deadline\n",
- __func__));
+ scmd_printk(KERN_INFO, scmd,
+ "%s: skip aborting cmd, past eh deadline\n",
+ current->comm));
return list_empty(work_q);
}
SCSI_LOG_ERROR_RECOVERY(3,
- shost_printk(KERN_INFO, shost,
- "%s: aborting cmd: 0x%p\n",
- current->comm, scmd));
+ scmd_printk(KERN_INFO, scmd,
+ "%s: aborting cmd\n", current->comm));
rtn = scsi_try_to_abort_cmd(shost->hostt, scmd);
if (rtn == FAILED) {
SCSI_LOG_ERROR_RECOVERY(3,
- shost_printk(KERN_INFO, shost,
- "%s: aborting cmd failed: 0x%p\n",
- current->comm, scmd));
+ scmd_printk(KERN_INFO, scmd,
+ "%s: aborting cmd failed\n",
+ current->comm));
list_splice_init(&check_list, work_q);
return list_empty(work_q);
}
shost_for_each_device(sdev, shost) {
if (scsi_host_eh_past_deadline(shost)) {
SCSI_LOG_ERROR_RECOVERY(3,
- shost_printk(KERN_INFO, shost,
- "skip %s, past eh deadline\n",
- __func__));
+ sdev_printk(KERN_INFO, sdev,
+ "%s: skip START_UNIT, past eh deadline\n",
+ current->comm));
break;
}
stu_scmd = NULL;
continue;
SCSI_LOG_ERROR_RECOVERY(3,
- shost_printk(KERN_INFO, shost,
- "%s: Sending START_UNIT to sdev: 0x%p\n",
- current->comm, sdev));
+ sdev_printk(KERN_INFO, sdev,
+ "%s: Sending START_UNIT\n",
+ current->comm));
if (!scsi_eh_try_stu(stu_scmd)) {
if (!scsi_device_online(sdev) ||
}
} else {
SCSI_LOG_ERROR_RECOVERY(3,
- shost_printk(KERN_INFO, shost,
- "%s: START_UNIT failed to sdev:"
- " 0x%p\n", current->comm, sdev));
+ sdev_printk(KERN_INFO, sdev,
+ "%s: START_UNIT failed\n",
+ current->comm));
}
}
shost_for_each_device(sdev, shost) {
if (scsi_host_eh_past_deadline(shost)) {
SCSI_LOG_ERROR_RECOVERY(3,
- shost_printk(KERN_INFO, shost,
- "skip %s, past eh deadline\n",
- __func__));
+ sdev_printk(KERN_INFO, sdev,
+ "%s: skip BDR, past eh deadline\n",
+ current->comm));
break;
}
bdr_scmd = NULL;
continue;
SCSI_LOG_ERROR_RECOVERY(3,
- shost_printk(KERN_INFO, shost,
- "%s: Sending BDR sdev: 0x%p\n",
- current->comm, sdev));
+ sdev_printk(KERN_INFO, sdev,
+ "%s: Sending BDR\n", current->comm));
rtn = scsi_try_bus_device_reset(bdr_scmd);
if (rtn == SUCCESS || rtn == FAST_IO_FAIL) {
if (!scsi_device_online(sdev) ||
}
} else {
SCSI_LOG_ERROR_RECOVERY(3,
- shost_printk(KERN_INFO, shost,
- "%s: BDR failed sdev: 0x%p\n",
- current->comm, sdev));
+ sdev_printk(KERN_INFO, sdev,
+ "%s: BDR failed\n", current->comm));
}
}
list_splice_init(&tmp_list, work_q);
SCSI_LOG_ERROR_RECOVERY(3,
shost_printk(KERN_INFO, shost,
- "skip %s, past eh deadline\n",
- __func__));
+ "%s: Skip target reset, past eh deadline\n",
+ current->comm));
return list_empty(work_q);
}
list_splice_init(&check_list, work_q);
SCSI_LOG_ERROR_RECOVERY(3,
shost_printk(KERN_INFO, shost,
- "skip %s, past eh deadline\n",
- __func__));
+ "%s: skip BRST, past eh deadline\n",
+ current->comm));
return list_empty(work_q);
}
* is no point trying to lock the door of an off-line device.
*/
shost_for_each_device(sdev, shost) {
- if (scsi_device_online(sdev) && sdev->locked)
+ if (scsi_device_online(sdev) && sdev->was_reset && sdev->locked) {
scsi_eh_lock_door(sdev);
+ sdev->was_reset = 0;
+ }
}
/*
*/
if (!shost->eh_noresume && scsi_autopm_get_host(shost) != 0) {
SCSI_LOG_ERROR_RECOVERY(1,
- printk(KERN_ERR "Error handler scsi_eh_%d "
- "unable to autoresume\n",
- shost->host_no));
+ shost_printk(KERN_ERR, shost,
+ "scsi_eh_%d: unable to autoresume\n",
+ shost->host_no));
continue;
}
{
}
-/*
- * Function: scsi_reset_provider
- *
- * Purpose: Send requested reset to a bus or device at any phase.
- *
- * Arguments: device - device to send reset to
- * flag - reset type (see scsi.h)
- *
- * Returns: SUCCESS/FAILURE.
- *
- * Notes: This is used by the SCSI Generic driver to provide
- * Bus/Device reset capability.
+/**
+ * scsi_ioctl_reset: explicitly reset a host/bus/target/device
+ * @dev: scsi_device to operate on
+ * @arg: reset type (see sg.h)
*/
int
-scsi_reset_provider(struct scsi_device *dev, int flag)
+scsi_ioctl_reset(struct scsi_device *dev, int __user *arg)
{
struct scsi_cmnd *scmd;
struct Scsi_Host *shost = dev->host;
struct request req;
unsigned long flags;
- int rtn;
+ int error = 0, rtn, val;
- if (scsi_autopm_get_host(shost) < 0)
- return FAILED;
+ if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
+ return -EACCES;
- if (!get_device(&dev->sdev_gendev)) {
- rtn = FAILED;
- goto out_put_autopm_host;
- }
+ error = get_user(val, arg);
+ if (error)
+ return error;
+ if (scsi_autopm_get_host(shost) < 0)
+ return -EIO;
+
+ error = -EIO;
scmd = scsi_get_command(dev, GFP_KERNEL);
- if (!scmd) {
- rtn = FAILED;
- put_device(&dev->sdev_gendev);
+ if (!scmd)
goto out_put_autopm_host;
- }
blk_rq_init(NULL, &req);
scmd->request = &req;
shost->tmf_in_progress = 1;
spin_unlock_irqrestore(shost->host_lock, flags);
- switch (flag) {
- case SCSI_TRY_RESET_DEVICE:
+ switch (val & ~SG_SCSI_RESET_NO_ESCALATE) {
+ case SG_SCSI_RESET_NOTHING:
+ rtn = SUCCESS;
+ break;
+ case SG_SCSI_RESET_DEVICE:
rtn = scsi_try_bus_device_reset(scmd);
- if (rtn == SUCCESS)
+ if (rtn == SUCCESS || (val & SG_SCSI_RESET_NO_ESCALATE))
break;
/* FALLTHROUGH */
- case SCSI_TRY_RESET_TARGET:
+ case SG_SCSI_RESET_TARGET:
rtn = scsi_try_target_reset(scmd);
- if (rtn == SUCCESS)
+ if (rtn == SUCCESS || (val & SG_SCSI_RESET_NO_ESCALATE))
break;
/* FALLTHROUGH */
- case SCSI_TRY_RESET_BUS:
+ case SG_SCSI_RESET_BUS:
rtn = scsi_try_bus_reset(scmd);
- if (rtn == SUCCESS)
+ if (rtn == SUCCESS || (val & SG_SCSI_RESET_NO_ESCALATE))
break;
/* FALLTHROUGH */
- case SCSI_TRY_RESET_HOST:
+ case SG_SCSI_RESET_HOST:
rtn = scsi_try_host_reset(scmd);
- break;
+ if (rtn == SUCCESS)
+ break;
default:
+ /* FALLTHROUGH */
rtn = FAILED;
+ break;
}
+ error = (rtn == SUCCESS) ? 0 : -EIO;
+
spin_lock_irqsave(shost->host_lock, flags);
shost->tmf_in_progress = 0;
spin_unlock_irqrestore(shost->host_lock, flags);
"waking up host to restart after TMF\n"));
wake_up(&shost->host_wait);
-
scsi_run_host_queues(shost);
- scsi_next_command(scmd);
+ scsi_put_command(scmd);
+
out_put_autopm_host:
scsi_autopm_put_host(shost);
- return rtn;
+ return error;
}
-EXPORT_SYMBOL(scsi_reset_provider);
+EXPORT_SYMBOL(scsi_ioctl_reset);
/**
* scsi_normalize_sense - normalize main elements from either fixed or
* responded to a SCSI command with the CHECK_CONDITION status.
*
* Return value:
- * 1 if valid sense data information found, else 0;
+ * true if valid sense data information found, else false;
*/
-int scsi_normalize_sense(const u8 *sense_buffer, int sb_len,
- struct scsi_sense_hdr *sshdr)
+bool scsi_normalize_sense(const u8 *sense_buffer, int sb_len,
+ struct scsi_sense_hdr *sshdr)
{
if (!sense_buffer || !sb_len)
- return 0;
+ return false;
memset(sshdr, 0, sizeof(struct scsi_sense_hdr));
sshdr->response_code = (sense_buffer[0] & 0x7f);
if (!scsi_sense_valid(sshdr))
- return 0;
+ return false;
if (sshdr->response_code >= 0x72) {
/*
}
}
- return 1;
+ return true;
}
EXPORT_SYMBOL(scsi_normalize_sense);
-int scsi_command_normalize_sense(struct scsi_cmnd *cmd,
- struct scsi_sense_hdr *sshdr)
+bool scsi_command_normalize_sense(const struct scsi_cmnd *cmd,
+ struct scsi_sense_hdr *sshdr)
{
return scsi_normalize_sense(cmd->sense_buffer,
SCSI_SENSE_BUFFERSIZE, sshdr);
sdev_printk(KERN_INFO, sdev,
"ioctl_internal_command return code = %x\n",
result);
- scsi_print_sense_hdr(" ", &sshdr);
+ scsi_print_sense_hdr(sdev, NULL, &sshdr);
break;
}
}
{
char scsi_cmd[MAX_COMMAND_SIZE];
- /* No idea how this happens.... */
- if (!sdev)
- return -ENXIO;
-
- /*
- * If we are in the middle of error recovery, don't let anyone
- * else try and use this device. Also, if error recovery fails, it
- * may try and take the device offline, in which case all further
- * access to the device is prohibited.
- */
- if (!scsi_block_when_processing_errors(sdev))
- return -ENODEV;
-
/* Check for deprecated ioctls ... all the ioctls which don't
* follow the new unique numbering scheme are deprecated */
switch (cmd) {
START_STOP_TIMEOUT, NORMAL_RETRIES);
case SCSI_IOCTL_GET_PCI:
return scsi_ioctl_get_pci(sdev, arg);
+ case SG_SCSI_RESET:
+ return scsi_ioctl_reset(sdev, arg);
default:
if (sdev->host->hostt->ioctl)
return sdev->host->hostt->ioctl(sdev, cmd, arg);
}
EXPORT_SYMBOL(scsi_ioctl);
-/**
- * scsi_nonblockable_ioctl() - Handle SG_SCSI_RESET
- * @sdev: scsi device receiving ioctl
- * @cmd: Must be SC_SCSI_RESET
- * @arg: pointer to int containing SG_SCSI_RESET_{DEVICE,BUS,HOST}
- * @ndelay: file mode O_NDELAY flag
+/*
+ * We can process a reset even when a device isn't fully operable.
*/
-int scsi_nonblockable_ioctl(struct scsi_device *sdev, int cmd,
- void __user *arg, int ndelay)
+int scsi_ioctl_block_when_processing_errors(struct scsi_device *sdev, int cmd,
+ bool ndelay)
{
- int val, result;
-
- /* The first set of iocts may be executed even if we're doing
- * error processing, as long as the device was opened
- * non-blocking */
- if (ndelay) {
+ if (cmd == SG_SCSI_RESET && ndelay) {
if (scsi_host_in_recovery(sdev->host))
+ return -EAGAIN;
+ } else {
+ if (!scsi_block_when_processing_errors(sdev))
return -ENODEV;
- } else if (!scsi_block_when_processing_errors(sdev))
- return -ENODEV;
-
- switch (cmd) {
- case SG_SCSI_RESET:
- result = get_user(val, (int __user *)arg);
- if (result)
- return result;
- if (val == SG_SCSI_RESET_NOTHING)
- return 0;
- switch (val) {
- case SG_SCSI_RESET_DEVICE:
- val = SCSI_TRY_RESET_DEVICE;
- break;
- case SG_SCSI_RESET_TARGET:
- val = SCSI_TRY_RESET_TARGET;
- break;
- case SG_SCSI_RESET_BUS:
- val = SCSI_TRY_RESET_BUS;
- break;
- case SG_SCSI_RESET_HOST:
- val = SCSI_TRY_RESET_HOST;
- break;
- default:
- return -EINVAL;
- }
- if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
- return -EACCES;
- return (scsi_reset_provider(sdev, val) ==
- SUCCESS) ? 0 : -EIO;
}
- return -ENODEV;
+
+ return 0;
}
-EXPORT_SYMBOL(scsi_nonblockable_ioctl);
+EXPORT_SYMBOL_GPL(scsi_ioctl_block_when_processing_errors);
#include <linux/hardirq.h>
#include <linux/scatterlist.h>
#include <linux/blk-mq.h>
+#include <linux/ratelimit.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
mempool_t *pool;
};
-#define SP(x) { x, "sgpool-" __stringify(x) }
+#define SP(x) { .size = x, "sgpool-" __stringify(x) }
#if (SCSI_MAX_SG_SEGMENTS < 32)
#error SCSI_MAX_SG_SEGMENTS is too small (must be 32 or greater)
#endif
put_device(&sdev->sdev_gendev);
}
-void scsi_next_command(struct scsi_cmnd *cmd)
-{
- struct scsi_device *sdev = cmd->device;
- struct request_queue *q = sdev->request_queue;
-
- scsi_put_command(cmd);
- scsi_run_queue(q);
-
- put_device(&sdev->sdev_gendev);
-}
-
void scsi_run_host_queues(struct Scsi_Host *shost)
{
struct scsi_device *sdev;
__sg_free_table(&sdb->table, SCSI_MAX_SG_SEGMENTS, mq, scsi_sg_free);
}
-static int scsi_alloc_sgtable(struct scsi_data_buffer *sdb, int nents,
- gfp_t gfp_mask, bool mq)
+static int scsi_alloc_sgtable(struct scsi_data_buffer *sdb, int nents, bool mq)
{
struct scatterlist *first_chunk = NULL;
+ gfp_t gfp_mask = mq ? GFP_NOIO : GFP_ATOMIC;
int ret;
BUG_ON(!nents);
kblockd_schedule_work(&sdev->requeue_work);
else
blk_mq_start_stopped_hw_queues(q, true);
-
- put_device(&sdev->sdev_gendev);
} else {
unsigned long flags;
spin_unlock_irqrestore(q->queue_lock, flags);
scsi_release_buffers(cmd);
- scsi_next_command(cmd);
+
+ scsi_put_command(cmd);
+ scsi_run_queue(q);
}
+ put_device(&sdev->sdev_gendev);
return false;
}
struct request *req = cmd->request;
int error = 0;
struct scsi_sense_hdr sshdr;
- int sense_valid = 0;
- int sense_deferred = 0;
+ bool sense_valid = false;
+ int sense_deferred = 0, level = 0;
enum {ACTION_FAIL, ACTION_REPREP, ACTION_RETRY,
ACTION_DELAYED_RETRY} action;
unsigned long wait_for = (cmd->allowed + 1) * req->timeout;
if ((sshdr.asc == 0x0) && (sshdr.ascq == 0x1d))
;
else if (!(req->cmd_flags & REQ_QUIET))
- scsi_print_sense("", cmd);
+ scsi_print_sense(cmd);
result = 0;
/* BLOCK_PC may have set error */
error = 0;
case ACTION_FAIL:
/* Give up and fail the remainder of the request */
if (!(req->cmd_flags & REQ_QUIET)) {
- scsi_print_result(cmd);
- if (driver_byte(result) & DRIVER_SENSE)
- scsi_print_sense("", cmd);
- scsi_print_command(cmd);
+ static DEFINE_RATELIMIT_STATE(_rs,
+ DEFAULT_RATELIMIT_INTERVAL,
+ DEFAULT_RATELIMIT_BURST);
+
+ if (unlikely(scsi_logging_level))
+ level = SCSI_LOG_LEVEL(SCSI_LOG_MLCOMPLETE_SHIFT,
+ SCSI_LOG_MLCOMPLETE_BITS);
+
+ /*
+ * if logging is enabled the failure will be printed
+ * in scsi_log_completion(), so avoid duplicate messages
+ */
+ if (!level && __ratelimit(&_rs)) {
+ scsi_print_result(cmd, NULL, FAILED);
+ if (driver_byte(result) & DRIVER_SENSE)
+ scsi_print_sense(cmd);
+ scsi_print_command(cmd);
+ }
}
if (!scsi_end_request(req, error, blk_rq_err_bytes(req), 0))
return;
}
}
-static int scsi_init_sgtable(struct request *req, struct scsi_data_buffer *sdb,
- gfp_t gfp_mask)
+static int scsi_init_sgtable(struct request *req, struct scsi_data_buffer *sdb)
{
int count;
* If sg table allocation fails, requeue request later.
*/
if (unlikely(scsi_alloc_sgtable(sdb, req->nr_phys_segments,
- gfp_mask, req->mq_ctx != NULL)))
+ req->mq_ctx != NULL)))
return BLKPREP_DEFER;
/*
* BLKPREP_DEFER if the failure is retryable
* BLKPREP_KILL if the failure is fatal
*/
-int scsi_init_io(struct scsi_cmnd *cmd, gfp_t gfp_mask)
+int scsi_init_io(struct scsi_cmnd *cmd)
{
struct scsi_device *sdev = cmd->device;
struct request *rq = cmd->request;
BUG_ON(!rq->nr_phys_segments);
- error = scsi_init_sgtable(rq, &cmd->sdb, gfp_mask);
+ error = scsi_init_sgtable(rq, &cmd->sdb);
if (error)
goto err_exit;
rq->next_rq->special = bidi_sdb;
}
- error = scsi_init_sgtable(rq->next_rq, rq->next_rq->special,
- GFP_ATOMIC);
+ error = scsi_init_sgtable(rq->next_rq, rq->next_rq->special);
if (error)
goto err_exit;
}
BUG_ON(prot_sdb == NULL);
ivecs = blk_rq_count_integrity_sg(rq->q, rq->bio);
- if (scsi_alloc_sgtable(prot_sdb, ivecs, gfp_mask, is_mq)) {
+ if (scsi_alloc_sgtable(prot_sdb, ivecs, is_mq)) {
error = BLKPREP_DEFER;
goto err_exit;
}
* submit a request without an attached bio.
*/
if (req->bio) {
- int ret = scsi_init_io(cmd, GFP_ATOMIC);
+ int ret = scsi_init_io(cmd);
if (unlikely(ret))
return ret;
} else {
}
}
+/**
+ * scsi_dispatch_command - Dispatch a command to the low-level driver.
+ * @cmd: command block we are dispatching.
+ *
+ * Return: nonzero return request was rejected and device's queue needs to be
+ * plugged.
+ */
+static int scsi_dispatch_cmd(struct scsi_cmnd *cmd)
+{
+ struct Scsi_Host *host = cmd->device->host;
+ int rtn = 0;
+
+ atomic_inc(&cmd->device->iorequest_cnt);
+
+ /* check if the device is still usable */
+ if (unlikely(cmd->device->sdev_state == SDEV_DEL)) {
+ /* in SDEV_DEL we error all commands. DID_NO_CONNECT
+ * returns an immediate error upwards, and signals
+ * that the device is no longer present */
+ cmd->result = DID_NO_CONNECT << 16;
+ goto done;
+ }
+
+ /* Check to see if the scsi lld made this device blocked. */
+ if (unlikely(scsi_device_blocked(cmd->device))) {
+ /*
+ * in blocked state, the command is just put back on
+ * the device queue. The suspend state has already
+ * blocked the queue so future requests should not
+ * occur until the device transitions out of the
+ * suspend state.
+ */
+ SCSI_LOG_MLQUEUE(3, scmd_printk(KERN_INFO, cmd,
+ "queuecommand : device blocked\n"));
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+ }
+
+ /* Store the LUN value in cmnd, if needed. */
+ if (cmd->device->lun_in_cdb)
+ cmd->cmnd[1] = (cmd->cmnd[1] & 0x1f) |
+ (cmd->device->lun << 5 & 0xe0);
+
+ scsi_log_send(cmd);
+
+ /*
+ * Before we queue this command, check if the command
+ * length exceeds what the host adapter can handle.
+ */
+ if (cmd->cmd_len > cmd->device->host->max_cmd_len) {
+ SCSI_LOG_MLQUEUE(3, scmd_printk(KERN_INFO, cmd,
+ "queuecommand : command too long. "
+ "cdb_size=%d host->max_cmd_len=%d\n",
+ cmd->cmd_len, cmd->device->host->max_cmd_len));
+ cmd->result = (DID_ABORT << 16);
+ goto done;
+ }
+
+ if (unlikely(host->shost_state == SHOST_DEL)) {
+ cmd->result = (DID_NO_CONNECT << 16);
+ goto done;
+
+ }
+
+ trace_scsi_dispatch_cmd_start(cmd);
+ rtn = host->hostt->queuecommand(host, cmd);
+ if (rtn) {
+ trace_scsi_dispatch_cmd_error(cmd, rtn);
+ if (rtn != SCSI_MLQUEUE_DEVICE_BUSY &&
+ rtn != SCSI_MLQUEUE_TARGET_BUSY)
+ rtn = SCSI_MLQUEUE_HOST_BUSY;
+
+ SCSI_LOG_MLQUEUE(3, scmd_printk(KERN_INFO, cmd,
+ "queuecommand : request rejected\n"));
+ }
+
+ return rtn;
+ done:
+ cmd->scsi_done(cmd);
+ return 0;
+}
+
/**
* scsi_done - Invoke completion on finished SCSI command.
* @cmd: The SCSI Command for which a low-level device driver (LLDD) gives
* we add the dev to the starved list so it eventually gets
* a run when a tag is freed.
*/
- if (blk_queue_tagged(q) && !blk_rq_tagged(req)) {
+ if (blk_queue_tagged(q) && !(req->cmd_flags & REQ_QUEUED)) {
spin_lock_irq(shost->host_lock);
if (list_empty(&sdev->starved_entry))
list_add_tail(&sdev->starved_entry,
if (!scsi_host_queue_ready(q, shost, sdev))
goto host_not_ready;
+
+ if (sdev->simple_tags)
+ cmd->flags |= SCMD_TAGGED;
+ else
+ cmd->flags &= ~SCMD_TAGGED;
/*
* Finally, initialize any error handling parameters, and set up
blk_mq_start_request(req);
}
- if (blk_queue_tagged(q))
- req->cmd_flags |= REQ_QUEUED;
+ if (sdev->simple_tags)
+ cmd->flags |= SCMD_TAGGED;
else
- req->cmd_flags &= ~REQ_QUEUED;
+ cmd->flags &= ~SCMD_TAGGED;
scsi_init_cmd_errh(cmd);
cmd->scsi_done = scsi_mq_done;
memset(&shost->tag_set, 0, sizeof(shost->tag_set));
shost->tag_set.ops = &scsi_mq_ops;
- shost->tag_set.nr_hw_queues = 1;
+ shost->tag_set.nr_hw_queues = shost->nr_hw_queues ? : 1;
shost->tag_set.queue_depth = shost->can_queue;
shost->tag_set.cmd_size = cmd_size;
shost->tag_set.numa_node = NUMA_NO_NODE;
} while (0); \
} while (0)
#else
+#define SCSI_LOG_LEVEL(SHIFT, BITS) 0
#define SCSI_CHECK_LOGGING(SHIFT, BITS, LEVEL, CMD)
#endif /* CONFIG_SCSI_LOGGING */
extern void scsi_exit_hosts(void);
/* scsi.c */
-extern int scsi_dispatch_cmd(struct scsi_cmnd *cmd);
extern int scsi_setup_command_freelist(struct Scsi_Host *shost);
extern void scsi_destroy_command_freelist(struct Scsi_Host *shost);
#ifdef CONFIG_SCSI_LOGGING
extern int scsi_maybe_unblock_host(struct scsi_device *sdev);
extern void scsi_device_unbusy(struct scsi_device *sdev);
extern void scsi_queue_insert(struct scsi_cmnd *cmd, int reason);
-extern void scsi_next_command(struct scsi_cmnd *cmd);
extern void scsi_io_completion(struct scsi_cmnd *, unsigned int);
extern void scsi_run_host_queues(struct Scsi_Host *shost);
extern struct request_queue *scsi_alloc_queue(struct scsi_device *sdev);
}
WARN_ON_ONCE(!blk_get_queue(sdev->request_queue));
sdev->request_queue->queuedata = sdev;
- scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun);
+
+ if (!shost_use_blk_mq(sdev->host) &&
+ (shost->bqt || shost->hostt->use_blk_tags)) {
+ blk_queue_init_tags(sdev->request_queue,
+ sdev->host->cmd_per_lun, shost->bqt);
+ }
+ scsi_change_queue_depth(sdev, sdev->host->cmd_per_lun);
scsi_sysfs_device_initialize(sdev);
(inq_result[3] & 0x0f) == 1 ? " CCS" : "");
if ((sdev->scsi_level >= SCSI_2) && (inq_result[7] & 2) &&
- !(*bflags & BLIST_NOTQ))
+ !(*bflags & BLIST_NOTQ)) {
sdev->tagged_supported = 1;
+ sdev->simple_tags = 1;
+ }
/*
* Some devices (Texel CD ROM drives) have handshaking problems
sparse_lun = 0;
/*
- * If less than SCSI_1_CSS, and no special lun scaning, stop
+ * If less than SCSI_1_CCS, and no special lun scanning, stop
* scanning; this matches 2.4 behaviour, but could just be a bug
- * (to continue scanning a SCSI_1_CSS device).
+ * (to continue scanning a SCSI_1_CCS device).
*
* This test is broken. We might not have any device on lun0 for
* a sparselun device, and if that's the case then how would we
void scsi_rescan_device(struct device *dev)
{
- struct scsi_driver *drv;
-
if (!dev->driver)
return;
- drv = to_scsi_driver(dev->driver);
- if (try_module_get(drv->owner)) {
+ if (try_module_get(dev->driver->owner)) {
+ struct scsi_driver *drv = to_scsi_driver(dev->driver);
+
if (drv->rescan)
drv->rescan(dev);
- module_put(drv->owner);
+ module_put(dev->driver->owner);
}
}
EXPORT_SYMBOL(scsi_rescan_device);
if (((channel != SCAN_WILD_CARD) && (channel > shost->max_channel)) ||
((id != SCAN_WILD_CARD) && (id >= shost->max_id)) ||
- ((lun != SCAN_WILD_CARD) && (lun > shost->max_lun)))
+ ((lun != SCAN_WILD_CARD) && (lun >= shost->max_lun)))
return -EINVAL;
mutex_lock(&shost->scan_mutex);
struct scsi_device *sdev = to_scsi_device(dev);
const char *name = "none";
- if (sdev->ordered_tags)
- name = "ordered";
- else if (sdev->simple_tags)
+ if (sdev->simple_tags)
name = "simple";
return snprintf(buf, 20, "%s\n", name);
if (!sdev->tagged_supported || !sht->change_queue_type)
return -EINVAL;
- if (strncmp(buf, "ordered", 7) == 0)
- tag_type = MSG_ORDERED_TAG;
- else if (strncmp(buf, "simple", 6) == 0)
+ /*
+ * We're never issueing order tags these days, but allow the value
+ * for backwards compatibility.
+ */
+ if (strncmp(buf, "ordered", 7) == 0 ||
+ strncmp(buf, "simple", 6) == 0)
tag_type = MSG_SIMPLE_TAG;
else if (strncmp(buf, "none", 4) != 0)
return -EINVAL;
depth = simple_strtoul(buf, NULL, 0);
- if (depth < 1)
+ if (depth < 1 || depth > sht->can_queue)
return -EINVAL;
- retval = sht->change_queue_depth(sdev, depth,
- SCSI_QDEPTH_DEFAULT);
+ retval = sht->change_queue_depth(sdev, depth);
if (retval < 0)
return retval;
return scsi_trace_rw16(p, cdb, len);
case UNMAP:
return scsi_trace_unmap(p, cdb, len);
- case SERVICE_ACTION_IN:
+ case SERVICE_ACTION_IN_16:
return scsi_trace_service_action_in(p, cdb, len);
case VARIABLE_LENGTH_CMD:
return scsi_trace_varlen(p, cdb, len);
#include <scsi/scsi_host.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_eh.h>
+#include <scsi/scsi_tcq.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_spi.h>
}
EXPORT_SYMBOL_GPL(spi_populate_ppr_msg);
+/**
+ * spi_populate_tag_msg - place a tag message in a buffer
+ * @msg: pointer to the area to place the tag
+ * @cmd: pointer to the scsi command for the tag
+ *
+ * Notes:
+ * designed to create the correct type of tag message for the
+ * particular request. Returns the size of the tag message.
+ * May return 0 if TCQ is disabled for this device.
+ **/
+int spi_populate_tag_msg(unsigned char *msg, struct scsi_cmnd *cmd)
+{
+ if (cmd->flags & SCMD_TAGGED) {
+ *msg++ = MSG_SIMPLE_TAG;
+ *msg++ = cmd->request->tag;
+ return 2;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(spi_populate_tag_msg);
+
#ifdef CONFIG_SCSI_CONSTANTS
static const char * const one_byte_msgs[] = {
/* 0x00 */ "Task Complete", NULL /* Extended Message */, "Save Pointers",
end_head * end_sector + end_sector;
/* This is the actual _sector_ number at the end */
- logical_end = get_unaligned(&largest->start_sect)
- + get_unaligned(&largest->nr_sects);
+ logical_end = get_unaligned_le32(&largest->start_sect)
+ + get_unaligned_le32(&largest->nr_sects);
/* This is for >1023 cylinders */
ext_cyl = (logical_end - (end_head * end_sector + end_sector))
static void sd_read_capacity(struct scsi_disk *sdkp, unsigned char *buffer);
static void scsi_disk_release(struct device *cdev);
static void sd_print_sense_hdr(struct scsi_disk *, struct scsi_sense_hdr *);
-static void sd_print_result(struct scsi_disk *, int);
+static void sd_print_result(const struct scsi_disk *, const char *, int);
static DEFINE_SPINLOCK(sd_index_lock);
static DEFINE_IDA(sd_index_ida);
};
static struct scsi_driver sd_template = {
- .owner = THIS_MODULE,
.gendrv = {
.name = "sd",
+ .owner = THIS_MODULE,
.probe = sd_probe,
.remove = sd_remove,
.shutdown = sd_shutdown,
unsigned int logical_block_size = sdkp->device->sector_size;
unsigned int max_blocks = 0;
- q->limits.discard_zeroes_data = sdkp->lbprz;
+ q->limits.discard_zeroes_data = 0;
q->limits.discard_alignment = sdkp->unmap_alignment *
logical_block_size;
q->limits.discard_granularity =
case SD_LBP_WS16:
max_blocks = min_not_zero(sdkp->max_ws_blocks,
(u32)SD_MAX_WS16_BLOCKS);
+ q->limits.discard_zeroes_data = sdkp->lbprz;
break;
case SD_LBP_WS10:
max_blocks = min_not_zero(sdkp->max_ws_blocks,
(u32)SD_MAX_WS10_BLOCKS);
+ q->limits.discard_zeroes_data = sdkp->lbprz;
break;
case SD_LBP_ZERO:
* amount of blocks described by the request.
*/
blk_add_request_payload(rq, page, len);
- ret = scsi_init_io(cmd, GFP_ATOMIC);
+ ret = scsi_init_io(cmd);
rq->__data_len = nr_bytes;
out:
* knows how much to actually write.
*/
rq->__data_len = sdp->sector_size;
- ret = scsi_init_io(cmd, GFP_ATOMIC);
+ ret = scsi_init_io(cmd);
rq->__data_len = nr_bytes;
return ret;
}
int ret;
unsigned char protect;
- ret = scsi_init_io(SCpnt, GFP_ATOMIC);
+ ret = scsi_init_io(SCpnt);
if (ret != BLKPREP_OK)
goto out;
SCpnt = rq->special;
* may try and take the device offline, in which case all further
* access to the device is prohibited.
*/
- error = scsi_nonblockable_ioctl(sdp, cmd, p,
- (mode & FMODE_NDELAY) != 0);
- if (!scsi_block_when_processing_errors(sdp) || !error)
+ error = scsi_ioctl_block_when_processing_errors(sdp, cmd,
+ (mode & FMODE_NDELAY) != 0);
+ if (error)
goto out;
/*
}
if (res) {
- sd_print_result(sdkp, res);
+ sd_print_result(sdkp, "Synchronize Cache(10) failed", res);
if (driver_byte(res) & DRIVER_SENSE)
sd_print_sense_hdr(sdkp, &sshdr);
unsigned int cmd, unsigned long arg)
{
struct scsi_device *sdev = scsi_disk(bdev->bd_disk)->device;
- int ret;
-
- ret = scsi_verify_blk_ioctl(bdev, cmd);
- if (ret < 0)
- return ret;
+ int error;
- /*
- * If we are in the middle of error recovery, don't let anyone
- * else try and use this device. Also, if error recovery fails, it
- * may try and take the device offline, in which case all further
- * access to the device is prohibited.
- */
- if (!scsi_block_when_processing_errors(sdev))
- return -ENODEV;
+ error = scsi_ioctl_block_when_processing_errors(sdev, cmd,
+ (mode & FMODE_NDELAY) != 0);
+ if (error)
+ return error;
- if (sdev->host->hostt->compat_ioctl) {
- ret = sdev->host->hostt->compat_ioctl(sdev, cmd, (void __user *)arg);
-
- return ret;
- }
-
/*
* Let the static ioctl translation table take care of it.
*/
- return -ENOIOCTLCMD;
+ if (!sdev->host->hostt->compat_ioctl)
+ return -ENOIOCTLCMD;
+ return sdev->host->hostt->compat_ioctl(sdev, cmd, (void __user *)arg);
}
#endif
if (sense_valid)
sense_deferred = scsi_sense_is_deferred(&sshdr);
}
-#ifdef CONFIG_SCSI_LOGGING
- SCSI_LOG_HLCOMPLETE(1, scsi_print_result(SCpnt));
- if (sense_valid) {
- SCSI_LOG_HLCOMPLETE(1, scmd_printk(KERN_INFO, SCpnt,
- "sd_done: sb[respc,sk,asc,"
- "ascq]=%x,%x,%x,%x\n",
- sshdr.response_code,
- sshdr.sense_key, sshdr.asc,
- sshdr.ascq));
- }
-#endif
sdkp->medium_access_timed_out = 0;
if (driver_byte(result) != DRIVER_SENSE &&
* unknown amount of data was transferred so treat it as an
* error.
*/
- scsi_print_sense("sd", SCpnt);
SCpnt->result = 0;
memset(SCpnt->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
break;
break;
}
out:
+ SCSI_LOG_HLCOMPLETE(1, scmd_printk(KERN_INFO, SCpnt,
+ "sd_done: completed %d of %d bytes\n",
+ good_bytes, scsi_bufflen(SCpnt)));
+
if (rq_data_dir(SCpnt->request) == READ && scsi_prot_sg_count(SCpnt))
sd_dif_complete(SCpnt, good_bytes);
/* no sense, TUR either succeeded or failed
* with a status error */
if(!spintime && !scsi_status_is_good(the_result)) {
- sd_printk(KERN_NOTICE, sdkp, "Unit Not Ready\n");
- sd_print_result(sdkp, the_result);
+ sd_print_result(sdkp, "Test Unit Ready failed",
+ the_result);
}
break;
}
-
+
/*
* The device does not want the automatic start to be issued.
*/
struct scsi_sense_hdr *sshdr, int sense_valid,
int the_result)
{
- sd_print_result(sdkp, the_result);
if (driver_byte(the_result) & DRIVER_SENSE)
sd_print_sense_hdr(sdkp, sshdr);
else
do {
memset(cmd, 0, 16);
- cmd[0] = SERVICE_ACTION_IN;
+ cmd[0] = SERVICE_ACTION_IN_16;
cmd[1] = SAI_READ_CAPACITY_16;
cmd[13] = RC16_LEN;
memset(buffer, 0, RC16_LEN);
} while (the_result && retries);
if (the_result) {
- sd_printk(KERN_NOTICE, sdkp, "READ CAPACITY(16) failed\n");
+ sd_print_result(sdkp, "Read Capacity(16) failed", the_result);
read_capacity_error(sdkp, sdp, &sshdr, sense_valid, the_result);
return -EINVAL;
}
} while (the_result && retries);
if (the_result) {
- sd_printk(KERN_NOTICE, sdkp, "READ CAPACITY failed\n");
+ sd_print_result(sdkp, "Read Capacity(10) failed", the_result);
read_capacity_error(sdkp, sdp, &sshdr, sense_valid, the_result);
return -EINVAL;
}
} else { /* LBP VPD page tells us what to use */
- if (sdkp->lbpu && sdkp->max_unmap_blocks)
- sd_config_discard(sdkp, SD_LBP_UNMAP);
- else if (sdkp->lbpws)
+ if (sdkp->lbpws)
sd_config_discard(sdkp, SD_LBP_WS16);
else if (sdkp->lbpws10)
sd_config_discard(sdkp, SD_LBP_WS10);
+ else if (sdkp->lbpu && sdkp->max_unmap_blocks)
+ sd_config_discard(sdkp, SD_LBP_UNMAP);
else
sd_config_discard(sdkp, SD_LBP_DISABLE);
}
res = scsi_execute_req_flags(sdp, cmd, DMA_NONE, NULL, 0, &sshdr,
SD_TIMEOUT, SD_MAX_RETRIES, NULL, REQ_PM);
if (res) {
- sd_printk(KERN_WARNING, sdkp, "START_STOP FAILED\n");
- sd_print_result(sdkp, res);
+ sd_print_result(sdkp, "Start/Stop Unit failed", res);
if (driver_byte(res) & DRIVER_SENSE)
sd_print_sense_hdr(sdkp, &sshdr);
if (scsi_sense_valid(&sshdr) &&
static void sd_print_sense_hdr(struct scsi_disk *sdkp,
struct scsi_sense_hdr *sshdr)
{
- sd_printk(KERN_INFO, sdkp, " ");
- scsi_show_sense_hdr(sshdr);
- sd_printk(KERN_INFO, sdkp, " ");
- scsi_show_extd_sense(sshdr->asc, sshdr->ascq);
+ scsi_show_sense_hdr(sdkp->device,
+ sdkp->disk ? sdkp->disk->disk_name : NULL, sshdr);
+ scsi_show_extd_sense(sdkp->device,
+ sdkp->disk ? sdkp->disk->disk_name : NULL,
+ sshdr->asc, sshdr->ascq);
}
-static void sd_print_result(struct scsi_disk *sdkp, int result)
+static void sd_print_result(const struct scsi_disk *sdkp, const char *msg,
+ int result)
{
- sd_printk(KERN_INFO, sdkp, " ");
- scsi_show_result(result);
+ const char *hb_string = scsi_hostbyte_string(result);
+ const char *db_string = scsi_driverbyte_string(result);
+
+ if (hb_string || db_string)
+ sd_printk(KERN_INFO, sdkp,
+ "%s: Result: hostbyte=%s driverbyte=%s\n", msg,
+ hb_string ? hb_string : "invalid",
+ db_string ? db_string : "invalid");
+ else
+ sd_printk(KERN_INFO, sdkp,
+ "%s: Result: hostbyte=0x%02x driverbyte=0x%02x\n",
+ msg, host_byte(result), driver_byte(result));
}
#define sd_printk(prefix, sdsk, fmt, a...) \
(sdsk)->disk ? \
- sdev_printk(prefix, (sdsk)->device, "[%s] " fmt, \
- (sdsk)->disk->disk_name, ##a) : \
- sdev_printk(prefix, (sdsk)->device, fmt, ##a)
+ sdev_prefix_printk(prefix, (sdsk)->device, \
+ (sdsk)->disk->disk_name, fmt, ##a) : \
+ sdev_printk(prefix, (sdsk)->device, fmt, ##a)
#define sd_first_printk(prefix, sdsk, fmt, a...) \
do { \
};
static struct scsi_driver ses_template = {
- .owner = THIS_MODULE,
.gendrv = {
.name = "ses",
+ .owner = THIS_MODULE,
.probe = ses_probe,
.remove = ses_remove,
},
#define SZ_SG_REQ_INFO sizeof(sg_req_info_t)
#define sg_printk(prefix, sdp, fmt, a...) \
- sdev_printk(prefix, (sdp)->device, "[%s] " fmt, \
- (sdp)->disk->disk_name, ##a)
+ sdev_prefix_printk(prefix, (sdp)->device, \
+ (sdp)->disk->disk_name, fmt, ##a)
static int sg_allow_access(struct file *filp, unsigned char *cmd)
{
if (atomic_read(&sdp->detaching))
return -ENODEV;
return put_user(sdp->device->host->hostt->emulated, ip);
- case SG_SCSI_RESET:
- if (atomic_read(&sdp->detaching))
- return -ENODEV;
- if (filp->f_flags & O_NONBLOCK) {
- if (scsi_host_in_recovery(sdp->device->host))
- return -EBUSY;
- } else if (!scsi_block_when_processing_errors(sdp->device))
- return -EBUSY;
- result = get_user(val, ip);
- if (result)
- return result;
- if (SG_SCSI_RESET_NOTHING == val)
- return 0;
- switch (val) {
- case SG_SCSI_RESET_DEVICE:
- val = SCSI_TRY_RESET_DEVICE;
- break;
- case SG_SCSI_RESET_TARGET:
- val = SCSI_TRY_RESET_TARGET;
- break;
- case SG_SCSI_RESET_BUS:
- val = SCSI_TRY_RESET_BUS;
- break;
- case SG_SCSI_RESET_HOST:
- val = SCSI_TRY_RESET_HOST;
- break;
- default:
- return -EINVAL;
- }
- if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
- return -EACCES;
- return (scsi_reset_provider(sdp->device, val) ==
- SUCCESS) ? 0 : -EIO;
case SCSI_IOCTL_SEND_COMMAND:
if (atomic_read(&sdp->detaching))
return -ENODEV;
return result;
sdp->sgdebug = (char) val;
return 0;
- case SCSI_IOCTL_GET_IDLUN:
- case SCSI_IOCTL_GET_BUS_NUMBER:
- case SCSI_IOCTL_PROBE_HOST:
- case SG_GET_TRANSFORM:
- if (atomic_read(&sdp->detaching))
- return -ENODEV;
- return scsi_ioctl(sdp->device, cmd_in, p);
case BLKSECTGET:
return put_user(max_sectors_bytes(sdp->device->request_queue),
ip);
return blk_trace_startstop(sdp->device->request_queue, 0);
case BLKTRACETEARDOWN:
return blk_trace_remove(sdp->device->request_queue);
+ case SCSI_IOCTL_GET_IDLUN:
+ case SCSI_IOCTL_GET_BUS_NUMBER:
+ case SCSI_IOCTL_PROBE_HOST:
+ case SG_GET_TRANSFORM:
+ case SG_SCSI_RESET:
+ if (atomic_read(&sdp->detaching))
+ return -ENODEV;
+ break;
default:
if (read_only)
return -EPERM; /* don't know so take safe approach */
- return scsi_ioctl(sdp->device, cmd_in, p);
+ break;
}
+
+ result = scsi_ioctl_block_when_processing_errors(sdp->device,
+ cmd_in, filp->f_flags & O_NDELAY);
+ if (result)
+ return result;
+ return scsi_ioctl(sdp->device, cmd_in, p);
}
#ifdef CONFIG_COMPAT
if ((sdp->sgdebug > 0) &&
((CHECK_CONDITION == srp->header.masked_status) ||
(COMMAND_TERMINATED == srp->header.masked_status)))
- __scsi_print_sense(__func__, sense,
+ __scsi_print_sense(sdp->device, __func__, sense,
SCSI_SENSE_BUFFERSIZE);
/* Following if statement is a patch supplied by Eric Youngdale */
};
static struct scsi_driver sr_template = {
- .owner = THIS_MODULE,
.gendrv = {
.name = "sr",
+ .owner = THIS_MODULE,
.probe = sr_probe,
.remove = sr_remove,
.pm = &sr_pm_ops,
struct request *rq = SCpnt->request;
int ret;
- ret = scsi_init_io(SCpnt, GFP_ATOMIC);
+ ret = scsi_init_io(SCpnt);
if (ret != BLKPREP_OK)
goto out;
SCpnt = rq->special;
mutex_lock(&sr_mutex);
+ ret = scsi_ioctl_block_when_processing_errors(sdev, cmd,
+ (mode & FMODE_NDELAY) != 0);
+ if (ret)
+ goto out;
+
/*
* Send SCSI addressing ioctls directly to mid level, send other
* ioctls to cdrom/block level.
if (ret != -ENOSYS)
goto out;
- /*
- * ENODEV means that we didn't recognise the ioctl, or that we
- * cannot execute it in the current device state. In either
- * case fall through to scsi_ioctl, which will return ENDOEV again
- * if it doesn't recognise the ioctl
- */
- ret = scsi_nonblockable_ioctl(sdev, cmd, argp,
- (mode & FMODE_NDELAY) != 0);
- if (ret != -ENODEV)
- goto out;
ret = scsi_ioctl(sdev, cmd, argp);
out:
} Scsi_CD;
#define sr_printk(prefix, cd, fmt, a...) \
- sdev_printk(prefix, (cd)->device, "[%s] " fmt, \
- (cd)->cdi.name, ##a)
+ sdev_prefix_printk(prefix, (cd)->device, (cd)->cdi.name, fmt, ##a)
int sr_do_ioctl(Scsi_CD *, struct packet_command *);
"CDROM not ready. Make sure there "
"is a disc in the drive.\n");
#ifdef DEBUG
- scsi_print_sense_hdr("sr", &sshdr);
+ scsi_print_sense_hdr(cd->device, cd->cdi.name, &sshdr);
#endif
err = -ENOMEDIUM;
break;
/* sense: Invalid command operation code */
err = -EDRIVE_CANT_DO_THIS;
#ifdef DEBUG
- __scsi_print_command(cgc->cmd);
- scsi_print_sense_hdr("sr", &sshdr);
+ __scsi_print_command(cgc->cmd, CDROM_PACKET_SIZE);
+ scsi_print_sense_hdr(cd->device, cd->cdi.name, &sshdr);
#endif
break;
default:
sr_printk(KERN_ERR, cd,
"CDROM (ioctl) error, command: ");
- __scsi_print_command(cgc->cmd);
- scsi_print_sense_hdr("sr", &sshdr);
+ __scsi_print_command(cgc->cmd, CDROM_PACKET_SIZE);
+ scsi_print_sense_hdr(cd->device, cd->cdi.name, &sshdr);
err = -EIO;
}
}
/* The driver prints some debugging information on the console if DEBUG
is defined and non-zero. */
-#define DEBUG 0
+#define DEBUG 1
+#define NO_DEBUG 0
#define ST_DEB_MSG KERN_NOTICE
#if DEBUG
static int try_direct_io = TRY_DIRECT_IO;
static int try_rdio = 1;
static int try_wdio = 1;
+static int debug_flag;
static struct class st_sysfs_class;
static const struct attribute_group *st_dev_groups[];
MODULE_PARM_DESC(max_sg_segs, "Maximum number of scatter/gather segments to use (256)");
module_param_named(try_direct_io, try_direct_io, int, 0);
MODULE_PARM_DESC(try_direct_io, "Try direct I/O between user buffer and tape drive (1)");
+module_param_named(debug_flag, debug_flag, int, 0);
+MODULE_PARM_DESC(debug_flag, "Enable DEBUG, same as setting debugging=1");
+
/* Extra parameters for testing */
module_param_named(try_rdio, try_rdio, int, 0);
},
{
"try_direct_io", &try_direct_io
+ },
+ {
+ "debug_flag", &debug_flag
}
};
#endif
static void do_remove_sysfs_files(void);
static struct scsi_driver st_template = {
- .owner = THIS_MODULE,
.gendrv = {
.name = "st",
+ .owner = THIS_MODULE,
.probe = st_probe,
.remove = st_remove,
},
}
#define st_printk(prefix, t, fmt, a...) \
- sdev_printk(prefix, (t)->device, "%s: " fmt, \
- tape_name(t), ##a)
+ sdev_prefix_printk(prefix, (t)->device, tape_name(t), fmt, ##a)
#ifdef DEBUG
#define DEBC_printk(t, fmt, a...) \
if (debugging) { st_printk(ST_DEB_MSG, t, fmt, ##a ); }
SRpnt->cmd[0], SRpnt->cmd[1], SRpnt->cmd[2],
SRpnt->cmd[3], SRpnt->cmd[4], SRpnt->cmd[5]);
if (cmdstatp->have_sense)
- __scsi_print_sense(name, SRpnt->sense, SCSI_SENSE_BUFFERSIZE);
+ __scsi_print_sense(STp->device, name,
+ SRpnt->sense, SCSI_SENSE_BUFFERSIZE);
} ) /* end DEB */
if (!debugging) { /* Abnormal conditions for tape */
if (!cmdstatp->have_sense)
SRpnt->cmd[0] != MODE_SENSE &&
SRpnt->cmd[0] != TEST_UNIT_READY) {
- __scsi_print_sense(name, SRpnt->sense, SCSI_SENSE_BUFFERSIZE);
+ __scsi_print_sense(STp->device, name,
+ SRpnt->sense, SCSI_SENSE_BUFFERSIZE);
}
}
/* Lock or unlock the drive door. Don't use when st_request allocated. */
static int do_door_lock(struct scsi_tape * STp, int do_lock)
{
- int retval, cmd;
+ int retval;
- cmd = do_lock ? SCSI_IOCTL_DOORLOCK : SCSI_IOCTL_DOORUNLOCK;
DEBC_printk(STp, "%socking drive door.\n", do_lock ? "L" : "Unl");
- retval = scsi_ioctl(STp->device, cmd, NULL);
- if (!retval) {
+
+ retval = scsi_set_medium_removal(STp->device,
+ do_lock ? SCSI_REMOVAL_PREVENT : SCSI_REMOVAL_ALLOW);
+ if (!retval)
STp->door_locked = do_lock ? ST_LOCKED_EXPLICIT : ST_UNLOCKED;
- }
- else {
+ else
STp->door_locked = ST_LOCK_FAILS;
- }
return retval;
}
* may try and take the device offline, in which case all further
* access to the device is prohibited.
*/
- retval = scsi_nonblockable_ioctl(STp->device, cmd_in, p,
- file->f_flags & O_NDELAY);
- if (!scsi_block_when_processing_errors(STp->device) || retval != -ENODEV)
+ retval = scsi_ioctl_block_when_processing_errors(STp->device, cmd_in,
+ file->f_flags & O_NDELAY);
+ if (retval)
goto out;
- retval = 0;
cmd_type = _IOC_TYPE(cmd_in);
cmd_nr = _IOC_NR(cmd_in);
printk(KERN_INFO "st: Version %s, fixed bufsize %d, s/g segs %d\n",
verstr, st_fixed_buffer_size, st_max_sg_segs);
+ debugging = (debug_flag > 0) ? debug_flag : NO_DEBUG;
+ if (debugging) {
+ printk(KERN_INFO "st: Debugging enabled debug_flag = %d\n",
+ debugging);
+ }
+
err = class_register(&st_sysfs_class);
if (err) {
pr_err("Unable register sysfs class for SCSI tapes\n");
readl(hba->mmio_base + YH2I_REQ); /* flush */
}
-static int
-stex_slave_alloc(struct scsi_device *sdev)
-{
- /* Cheat: usually extracted from Inquiry data */
- sdev->tagged_supported = 1;
-
- scsi_activate_tcq(sdev, sdev->host->can_queue);
-
- return 0;
-}
-
static int
stex_slave_config(struct scsi_device *sdev)
{
sdev->use_10_for_rw = 1;
sdev->use_10_for_ms = 1;
blk_queue_rq_timeout(sdev->request_queue, 60 * HZ);
- sdev->tagged_supported = 1;
return 0;
}
-static void
-stex_slave_destroy(struct scsi_device *sdev)
-{
- scsi_deactivate_tcq(sdev, 1);
-}
-
static int
stex_queuecommand_lck(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
{
int result = SUCCESS;
unsigned long flags;
- printk(KERN_INFO DRV_NAME
- "(%s): aborting command\n", pci_name(hba->pdev));
- scsi_print_command(cmd);
+ scmd_printk(KERN_INFO, cmd, "aborting command\n");
base = hba->mmio_base;
spin_lock_irqsave(host->host_lock, flags);
hba = (struct st_hba *) &cmd->device->host->hostdata[0];
- printk(KERN_INFO DRV_NAME
- "(%s): resetting host\n", pci_name(hba->pdev));
- scsi_print_command(cmd);
+ shost_printk(KERN_INFO, cmd->device->host,
+ "resetting host\n");
return stex_do_reset(hba) ? FAILED : SUCCESS;
}
.proc_name = DRV_NAME,
.bios_param = stex_biosparam,
.queuecommand = stex_queuecommand,
- .slave_alloc = stex_slave_alloc,
.slave_configure = stex_slave_config,
- .slave_destroy = stex_slave_destroy,
.eh_abort_handler = stex_abort,
.eh_host_reset_handler = stex_reset,
.this_id = -1,
+ .use_blk_tags = 1,
};
static struct pci_device_id stex_pci_tbl[] = {
if (scmnd->result) {
if (scsi_normalize_sense(scmnd->sense_buffer,
SCSI_SENSE_BUFFERSIZE, &sense_hdr))
- scsi_print_sense_hdr("storvsc", &sense_hdr);
+ scsi_print_sense_hdr(scmnd->device, "storvsc",
+ &sense_hdr);
}
if (vm_srb->srb_status != SRB_STATUS_SUCCESS)
static int storvsc_device_configure(struct scsi_device *sdevice)
{
- scsi_adjust_queue_depth(sdevice, MSG_SIMPLE_TAG,
- STORVSC_MAX_IO_REQUESTS);
+ scsi_change_queue_depth(sdevice, STORVSC_MAX_IO_REQUESTS);
blk_queue_max_segment_size(sdevice->request_queue, PAGE_SIZE);
+++ /dev/null
-/* sun3_NCR5380.c -- adapted from atari_NCR5380.c for the sun3 by
- Sam Creasey. */
-/*
- * NCR 5380 generic driver routines. These should make it *trivial*
- * to implement 5380 SCSI drivers under Linux with a non-trantor
- * architecture.
- *
- * Note that these routines also work with NR53c400 family chips.
- *
- * Copyright 1993, Drew Eckhardt
- * Visionary Computing
- * (Unix and Linux consulting and custom programming)
- * drew@colorado.edu
- * +1 (303) 666-5836
- *
- * DISTRIBUTION RELEASE 6.
- *
- * For more information, please consult
- *
- * NCR 5380 Family
- * SCSI Protocol Controller
- * Databook
- *
- * NCR Microelectronics
- * 1635 Aeroplaza Drive
- * Colorado Springs, CO 80916
- * 1+ (719) 578-3400
- * 1+ (800) 334-5454
- */
-
-/*
- * ++roman: To port the 5380 driver to the Atari, I had to do some changes in
- * this file, too:
- *
- * - Some of the debug statements were incorrect (undefined variables and the
- * like). I fixed that.
- *
- * - In information_transfer(), I think a #ifdef was wrong. Looking at the
- * possible DMA transfer size should also happen for REAL_DMA. I added this
- * in the #if statement.
- *
- * - When using real DMA, information_transfer() should return in a DATAOUT
- * phase after starting the DMA. It has nothing more to do.
- *
- * - The interrupt service routine should run main after end of DMA, too (not
- * only after RESELECTION interrupts). Additionally, it should _not_ test
- * for more interrupts after running main, since a DMA process may have
- * been started and interrupts are turned on now. The new int could happen
- * inside the execution of NCR5380_intr(), leading to recursive
- * calls.
- *
- * - I've deleted all the stuff for AUTOPROBE_IRQ, REAL_DMA_POLL, PSEUDO_DMA
- * and USLEEP, because these were messing up readability and will never be
- * needed for Atari SCSI.
- *
- * - I've revised the NCR5380_main() calling scheme (relax the 'main_running'
- * stuff), and 'main' is executed in a bottom half if awoken by an
- * interrupt.
- *
- * - The code was quite cluttered up by "#if (NDEBUG & NDEBUG_*) printk..."
- * constructs. In my eyes, this made the source rather unreadable, so I
- * finally replaced that by the *_PRINTK() macros.
- *
- */
-#include <scsi/scsi_dbg.h>
-#include <scsi/scsi_transport_spi.h>
-
-/*
- * Further development / testing that should be done :
- * 1. Test linked command handling code after Eric is ready with
- * the high level code.
- */
-
-#if (NDEBUG & NDEBUG_LISTS)
-#define LIST(x,y) \
- { printk("LINE:%d Adding %p to %p\n", __LINE__, (void*)(x), (void*)(y)); \
- if ((x)==(y)) udelay(5); }
-#define REMOVE(w,x,y,z) \
- { printk("LINE:%d Removing: %p->%p %p->%p \n", __LINE__, \
- (void*)(w), (void*)(x), (void*)(y), (void*)(z)); \
- if ((x)==(y)) udelay(5); }
-#else
-#define LIST(x,y)
-#define REMOVE(w,x,y,z)
-#endif
-
-#ifndef notyet
-#undef LINKED
-#endif
-
-/*
- * Design
- * Issues :
- *
- * The other Linux SCSI drivers were written when Linux was Intel PC-only,
- * and specifically for each board rather than each chip. This makes their
- * adaptation to platforms like the Mac (Some of which use NCR5380's)
- * more difficult than it has to be.
- *
- * Also, many of the SCSI drivers were written before the command queuing
- * routines were implemented, meaning their implementations of queued
- * commands were hacked on rather than designed in from the start.
- *
- * When I designed the Linux SCSI drivers I figured that
- * while having two different SCSI boards in a system might be useful
- * for debugging things, two of the same type wouldn't be used.
- * Well, I was wrong and a number of users have mailed me about running
- * multiple high-performance SCSI boards in a server.
- *
- * Finally, when I get questions from users, I have no idea what
- * revision of my driver they are running.
- *
- * This driver attempts to address these problems :
- * This is a generic 5380 driver. To use it on a different platform,
- * one simply writes appropriate system specific macros (ie, data
- * transfer - some PC's will use the I/O bus, 68K's must use
- * memory mapped) and drops this file in their 'C' wrapper.
- *
- * As far as command queueing, two queues are maintained for
- * each 5380 in the system - commands that haven't been issued yet,
- * and commands that are currently executing. This means that an
- * unlimited number of commands may be queued, letting
- * more commands propagate from the higher driver levels giving higher
- * throughput. Note that both I_T_L and I_T_L_Q nexuses are supported,
- * allowing multiple commands to propagate all the way to a SCSI-II device
- * while a command is already executing.
- *
- * To solve the multiple-boards-in-the-same-system problem,
- * there is a separate instance structure for each instance
- * of a 5380 in the system. So, multiple NCR5380 drivers will
- * be able to coexist with appropriate changes to the high level
- * SCSI code.
- *
- * A NCR5380_PUBLIC_REVISION macro is provided, with the release
- * number (updated for each public release) printed by the
- * NCR5380_print_options command, which should be called from the
- * wrapper detect function, so that I know what release of the driver
- * users are using.
- *
- * Issues specific to the NCR5380 :
- *
- * When used in a PIO or pseudo-dma mode, the NCR5380 is a braindead
- * piece of hardware that requires you to sit in a loop polling for
- * the REQ signal as long as you are connected. Some devices are
- * brain dead (ie, many TEXEL CD ROM drives) and won't disconnect
- * while doing long seek operations.
- *
- * The workaround for this is to keep track of devices that have
- * disconnected. If the device hasn't disconnected, for commands that
- * should disconnect, we do something like
- *
- * while (!REQ is asserted) { sleep for N usecs; poll for M usecs }
- *
- * Some tweaking of N and M needs to be done. An algorithm based
- * on "time to data" would give the best results as long as short time
- * to datas (ie, on the same track) were considered, however these
- * broken devices are the exception rather than the rule and I'd rather
- * spend my time optimizing for the normal case.
- *
- * Architecture :
- *
- * At the heart of the design is a coroutine, NCR5380_main,
- * which is started when not running by the interrupt handler,
- * timer, and queue command function. It attempts to establish
- * I_T_L or I_T_L_Q nexuses by removing the commands from the
- * issue queue and calling NCR5380_select() if a nexus
- * is not established.
- *
- * Once a nexus is established, the NCR5380_information_transfer()
- * phase goes through the various phases as instructed by the target.
- * if the target goes into MSG IN and sends a DISCONNECT message,
- * the command structure is placed into the per instance disconnected
- * queue, and NCR5380_main tries to find more work. If USLEEP
- * was defined, and the target is idle for too long, the system
- * will try to sleep.
- *
- * If a command has disconnected, eventually an interrupt will trigger,
- * calling NCR5380_intr() which will in turn call NCR5380_reselect
- * to reestablish a nexus. This will run main if necessary.
- *
- * On command termination, the done function will be called as
- * appropriate.
- *
- * SCSI pointers are maintained in the SCp field of SCSI command
- * structures, being initialized after the command is connected
- * in NCR5380_select, and set as appropriate in NCR5380_information_transfer.
- * Note that in violation of the standard, an implicit SAVE POINTERS operation
- * is done, since some BROKEN disks fail to issue an explicit SAVE POINTERS.
- */
-
-/*
- * Using this file :
- * This file a skeleton Linux SCSI driver for the NCR 5380 series
- * of chips. To use it, you write an architecture specific functions
- * and macros and include this file in your driver.
- *
- * These macros control options :
- * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
- * for commands that return with a CHECK CONDITION status.
- *
- * LINKED - if defined, linked commands are supported.
- *
- * REAL_DMA - if defined, REAL DMA is used during the data transfer phases.
- *
- * SUPPORT_TAGS - if defined, SCSI-2 tagged queuing is used where possible
- *
- * These macros MUST be defined :
- *
- * NCR5380_read(register) - read from the specified register
- *
- * NCR5380_write(register, value) - write to the specific register
- *
- * Either real DMA *or* pseudo DMA may be implemented
- * REAL functions :
- * NCR5380_REAL_DMA should be defined if real DMA is to be used.
- * Note that the DMA setup functions should return the number of bytes
- * that they were able to program the controller for.
- *
- * Also note that generic i386/PC versions of these macros are
- * available as NCR5380_i386_dma_write_setup,
- * NCR5380_i386_dma_read_setup, and NCR5380_i386_dma_residual.
- *
- * NCR5380_dma_write_setup(instance, src, count) - initialize
- * NCR5380_dma_read_setup(instance, dst, count) - initialize
- * NCR5380_dma_residual(instance); - residual count
- *
- * PSEUDO functions :
- * NCR5380_pwrite(instance, src, count)
- * NCR5380_pread(instance, dst, count);
- *
- * If nothing specific to this implementation needs doing (ie, with external
- * hardware), you must also define
- *
- * NCR5380_queue_command
- * NCR5380_reset
- * NCR5380_abort
- * NCR5380_proc_info
- *
- * to be the global entry points into the specific driver, ie
- * #define NCR5380_queue_command t128_queue_command.
- *
- * If this is not done, the routines will be defined as static functions
- * with the NCR5380* names and the user must provide a globally
- * accessible wrapper function.
- *
- * The generic driver is initialized by calling NCR5380_init(instance),
- * after setting the appropriate host specific fields and ID. If the
- * driver wishes to autoprobe for an IRQ line, the NCR5380_probe_irq(instance,
- * possible) function may be used. Before the specific driver initialization
- * code finishes, NCR5380_print_options should be called.
- */
-
-static struct Scsi_Host *first_instance = NULL;
-static struct scsi_host_template *the_template = NULL;
-
-/* Macros ease life... :-) */
-#define SETUP_HOSTDATA(in) \
- struct NCR5380_hostdata *hostdata = \
- (struct NCR5380_hostdata *)(in)->hostdata
-#define HOSTDATA(in) ((struct NCR5380_hostdata *)(in)->hostdata)
-
-#define NEXT(cmd) ((struct scsi_cmnd *)(cmd)->host_scribble)
-#define SET_NEXT(cmd, next) ((cmd)->host_scribble = (void *)(next))
-#define NEXTADDR(cmd) ((struct scsi_cmnd **)&((cmd)->host_scribble))
-
-#define HOSTNO instance->host_no
-#define H_NO(cmd) (cmd)->device->host->host_no
-
-#define SGADDR(buffer) (void *)(((unsigned long)sg_virt(((buffer)))))
-
-#ifdef SUPPORT_TAGS
-
-/*
- * Functions for handling tagged queuing
- * =====================================
- *
- * ++roman (01/96): Now I've implemented SCSI-2 tagged queuing. Some notes:
- *
- * Using consecutive numbers for the tags is no good idea in my eyes. There
- * could be wrong re-usings if the counter (8 bit!) wraps and some early
- * command has been preempted for a long time. My solution: a bitfield for
- * remembering used tags.
- *
- * There's also the problem that each target has a certain queue size, but we
- * cannot know it in advance :-( We just see a QUEUE_FULL status being
- * returned. So, in this case, the driver internal queue size assumption is
- * reduced to the number of active tags if QUEUE_FULL is returned by the
- * target. The command is returned to the mid-level, but with status changed
- * to BUSY, since --as I've seen-- the mid-level can't handle QUEUE_FULL
- * correctly.
- *
- * We're also not allowed running tagged commands as long as an untagged
- * command is active. And REQUEST SENSE commands after a contingent allegiance
- * condition _must_ be untagged. To keep track whether an untagged command has
- * been issued, the host->busy array is still employed, as it is without
- * support for tagged queuing.
- *
- * One could suspect that there are possible race conditions between
- * is_lun_busy(), cmd_get_tag() and cmd_free_tag(). But I think this isn't the
- * case: is_lun_busy() and cmd_get_tag() are both called from NCR5380_main(),
- * which already guaranteed to be running at most once. It is also the only
- * place where tags/LUNs are allocated. So no other allocation can slip
- * between that pair, there could only happen a reselection, which can free a
- * tag, but that doesn't hurt. Only the sequence in cmd_free_tag() becomes
- * important: the tag bit must be cleared before 'nr_allocated' is decreased.
- */
-
-/* -1 for TAG_NONE is not possible with unsigned char cmd->tag */
-#undef TAG_NONE
-#define TAG_NONE 0xff
-
-/* For the m68k, the number of bits in 'allocated' must be a multiple of 32! */
-#if (MAX_TAGS % 32) != 0
-#error "MAX_TAGS must be a multiple of 32!"
-#endif
-
-typedef struct {
- char allocated[MAX_TAGS/8];
- int nr_allocated;
- int queue_size;
-} TAG_ALLOC;
-
-static TAG_ALLOC TagAlloc[8][8]; /* 8 targets and 8 LUNs */
-
-
-static void __init init_tags( void )
-{
- int target, lun;
- TAG_ALLOC *ta;
-
- if (!setup_use_tagged_queuing)
- return;
-
- for( target = 0; target < 8; ++target ) {
- for( lun = 0; lun < 8; ++lun ) {
- ta = &TagAlloc[target][lun];
- memset( &ta->allocated, 0, MAX_TAGS/8 );
- ta->nr_allocated = 0;
- /* At the beginning, assume the maximum queue size we could
- * support (MAX_TAGS). This value will be decreased if the target
- * returns QUEUE_FULL status.
- */
- ta->queue_size = MAX_TAGS;
- }
- }
-}
-
-
-/* Check if we can issue a command to this LUN: First see if the LUN is marked
- * busy by an untagged command. If the command should use tagged queuing, also
- * check that there is a free tag and the target's queue won't overflow. This
- * function should be called with interrupts disabled to avoid race
- * conditions.
- */
-
-static int is_lun_busy(struct scsi_cmnd *cmd, int should_be_tagged)
-{
- u8 lun = cmd->device->lun;
- SETUP_HOSTDATA(cmd->device->host);
-
- if (hostdata->busy[cmd->device->id] & (1 << lun))
- return( 1 );
- if (!should_be_tagged ||
- !setup_use_tagged_queuing || !cmd->device->tagged_supported)
- return( 0 );
- if (TagAlloc[cmd->device->id][lun].nr_allocated >=
- TagAlloc[cmd->device->id][lun].queue_size ) {
- dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %d: no free tags\n",
- H_NO(cmd), cmd->device->id, lun );
- return( 1 );
- }
- return( 0 );
-}
-
-
-/* Allocate a tag for a command (there are no checks anymore, check_lun_busy()
- * must be called before!), or reserve the LUN in 'busy' if the command is
- * untagged.
- */
-
-static void cmd_get_tag(struct scsi_cmnd *cmd, int should_be_tagged)
-{
- u8 lun = cmd->device->lun;
- SETUP_HOSTDATA(cmd->device->host);
-
- /* If we or the target don't support tagged queuing, allocate the LUN for
- * an untagged command.
- */
- if (!should_be_tagged ||
- !setup_use_tagged_queuing || !cmd->device->tagged_supported) {
- cmd->tag = TAG_NONE;
- hostdata->busy[cmd->device->id] |= (1 << lun);
- dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %d now allocated by untagged "
- "command\n", H_NO(cmd), cmd->device->id, lun );
- }
- else {
- TAG_ALLOC *ta = &TagAlloc[cmd->device->id][lun];
-
- cmd->tag = find_first_zero_bit( &ta->allocated, MAX_TAGS );
- set_bit( cmd->tag, &ta->allocated );
- ta->nr_allocated++;
- dprintk(NDEBUG_TAGS, "scsi%d: using tag %d for target %d lun %d "
- "(now %d tags in use)\n",
- H_NO(cmd), cmd->tag, cmd->device->id, lun,
- ta->nr_allocated );
- }
-}
-
-
-/* Mark the tag of command 'cmd' as free, or in case of an untagged command,
- * unlock the LUN.
- */
-
-static void cmd_free_tag(struct scsi_cmnd *cmd)
-{
- u8 lun = cmd->device->lun;
- SETUP_HOSTDATA(cmd->device->host);
-
- if (cmd->tag == TAG_NONE) {
- hostdata->busy[cmd->device->id] &= ~(1 << lun);
- dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %d untagged cmd finished\n",
- H_NO(cmd), cmd->device->id, lun );
- }
- else if (cmd->tag >= MAX_TAGS) {
- printk(KERN_NOTICE "scsi%d: trying to free bad tag %d!\n",
- H_NO(cmd), cmd->tag );
- }
- else {
- TAG_ALLOC *ta = &TagAlloc[cmd->device->id][lun];
- clear_bit( cmd->tag, &ta->allocated );
- ta->nr_allocated--;
- dprintk(NDEBUG_TAGS, "scsi%d: freed tag %d for target %d lun %d\n",
- H_NO(cmd), cmd->tag, cmd->device->id, lun );
- }
-}
-
-
-static void free_all_tags( void )
-{
- int target, lun;
- TAG_ALLOC *ta;
-
- if (!setup_use_tagged_queuing)
- return;
-
- for( target = 0; target < 8; ++target ) {
- for( lun = 0; lun < 8; ++lun ) {
- ta = &TagAlloc[target][lun];
- memset( &ta->allocated, 0, MAX_TAGS/8 );
- ta->nr_allocated = 0;
- }
- }
-}
-
-#endif /* SUPPORT_TAGS */
-
-
-/*
- * Function : void initialize_SCp(struct scsi_cmnd *cmd)
- *
- * Purpose : initialize the saved data pointers for cmd to point to the
- * start of the buffer.
- *
- * Inputs : cmd - struct scsi_cmnd structure to have pointers reset.
- */
-
-static __inline__ void initialize_SCp(struct scsi_cmnd *cmd)
-{
- /*
- * Initialize the Scsi Pointer field so that all of the commands in the
- * various queues are valid.
- */
-
- if (scsi_bufflen(cmd)) {
- cmd->SCp.buffer = scsi_sglist(cmd);
- cmd->SCp.buffers_residual = scsi_sg_count(cmd) - 1;
- cmd->SCp.ptr = (char *) SGADDR(cmd->SCp.buffer);
- cmd->SCp.this_residual = cmd->SCp.buffer->length;
- } else {
- cmd->SCp.buffer = NULL;
- cmd->SCp.buffers_residual = 0;
- cmd->SCp.ptr = NULL;
- cmd->SCp.this_residual = 0;
- }
-
-}
-
-#include <linux/delay.h>
-
-#if NDEBUG
-static struct {
- unsigned char mask;
- const char * name;}
-signals[] = {{ SR_DBP, "PARITY"}, { SR_RST, "RST" }, { SR_BSY, "BSY" },
- { SR_REQ, "REQ" }, { SR_MSG, "MSG" }, { SR_CD, "CD" }, { SR_IO, "IO" },
- { SR_SEL, "SEL" }, {0, NULL}},
-basrs[] = {{BASR_ATN, "ATN"}, {BASR_ACK, "ACK"}, {0, NULL}},
-icrs[] = {{ICR_ASSERT_RST, "ASSERT RST"},{ICR_ASSERT_ACK, "ASSERT ACK"},
- {ICR_ASSERT_BSY, "ASSERT BSY"}, {ICR_ASSERT_SEL, "ASSERT SEL"},
- {ICR_ASSERT_ATN, "ASSERT ATN"}, {ICR_ASSERT_DATA, "ASSERT DATA"},
- {0, NULL}},
-mrs[] = {{MR_BLOCK_DMA_MODE, "MODE BLOCK DMA"}, {MR_TARGET, "MODE TARGET"},
- {MR_ENABLE_PAR_CHECK, "MODE PARITY CHECK"}, {MR_ENABLE_PAR_INTR,
- "MODE PARITY INTR"}, {MR_ENABLE_EOP_INTR,"MODE EOP INTR"},
- {MR_MONITOR_BSY, "MODE MONITOR BSY"},
- {MR_DMA_MODE, "MODE DMA"}, {MR_ARBITRATE, "MODE ARBITRATION"},
- {0, NULL}};
-
-/*
- * Function : void NCR5380_print(struct Scsi_Host *instance)
- *
- * Purpose : print the SCSI bus signals for debugging purposes
- *
- * Input : instance - which NCR5380
- */
-
-static void NCR5380_print(struct Scsi_Host *instance) {
- unsigned char status, data, basr, mr, icr, i;
- unsigned long flags;
-
- local_irq_save(flags);
- data = NCR5380_read(CURRENT_SCSI_DATA_REG);
- status = NCR5380_read(STATUS_REG);
- mr = NCR5380_read(MODE_REG);
- icr = NCR5380_read(INITIATOR_COMMAND_REG);
- basr = NCR5380_read(BUS_AND_STATUS_REG);
- local_irq_restore(flags);
- printk("STATUS_REG: %02x ", status);
- for (i = 0; signals[i].mask ; ++i)
- if (status & signals[i].mask)
- printk(",%s", signals[i].name);
- printk("\nBASR: %02x ", basr);
- for (i = 0; basrs[i].mask ; ++i)
- if (basr & basrs[i].mask)
- printk(",%s", basrs[i].name);
- printk("\nICR: %02x ", icr);
- for (i = 0; icrs[i].mask; ++i)
- if (icr & icrs[i].mask)
- printk(",%s", icrs[i].name);
- printk("\nMODE: %02x ", mr);
- for (i = 0; mrs[i].mask; ++i)
- if (mr & mrs[i].mask)
- printk(",%s", mrs[i].name);
- printk("\n");
-}
-
-static struct {
- unsigned char value;
- const char *name;
-} phases[] = {
- {PHASE_DATAOUT, "DATAOUT"}, {PHASE_DATAIN, "DATAIN"}, {PHASE_CMDOUT, "CMDOUT"},
- {PHASE_STATIN, "STATIN"}, {PHASE_MSGOUT, "MSGOUT"}, {PHASE_MSGIN, "MSGIN"},
- {PHASE_UNKNOWN, "UNKNOWN"}};
-
-/*
- * Function : void NCR5380_print_phase(struct Scsi_Host *instance)
- *
- * Purpose : print the current SCSI phase for debugging purposes
- *
- * Input : instance - which NCR5380
- */
-
-static void NCR5380_print_phase(struct Scsi_Host *instance)
-{
- unsigned char status;
- int i;
-
- status = NCR5380_read(STATUS_REG);
- if (!(status & SR_REQ))
- printk(KERN_DEBUG "scsi%d: REQ not asserted, phase unknown.\n", HOSTNO);
- else {
- for (i = 0; (phases[i].value != PHASE_UNKNOWN) &&
- (phases[i].value != (status & PHASE_MASK)); ++i);
- printk(KERN_DEBUG "scsi%d: phase %s\n", HOSTNO, phases[i].name);
- }
-}
-
-#endif
-
-/*
- * ++roman: New scheme of calling NCR5380_main()
- *
- * If we're not in an interrupt, we can call our main directly, it cannot be
- * already running. Else, we queue it on a task queue, if not 'main_running'
- * tells us that a lower level is already executing it. This way,
- * 'main_running' needs not be protected in a special way.
- *
- * queue_main() is a utility function for putting our main onto the task
- * queue, if main_running is false. It should be called only from a
- * interrupt or bottom half.
- */
-
-#include <linux/gfp.h>
-#include <linux/workqueue.h>
-#include <linux/interrupt.h>
-
-static volatile int main_running = 0;
-static DECLARE_WORK(NCR5380_tqueue, NCR5380_main);
-
-static __inline__ void queue_main(void)
-{
- if (!main_running) {
- /* If in interrupt and NCR5380_main() not already running,
- queue it on the 'immediate' task queue, to be processed
- immediately after the current interrupt processing has
- finished. */
- schedule_work(&NCR5380_tqueue);
- }
- /* else: nothing to do: the running NCR5380_main() will pick up
- any newly queued command. */
-}
-
-
-static inline void NCR5380_all_init (void)
-{
- static int done = 0;
- if (!done) {
- dprintk(NDEBUG_INIT, "scsi : NCR5380_all_init()\n");
- done = 1;
- }
-}
-
-
-/*
- * Function : void NCR58380_print_options (struct Scsi_Host *instance)
- *
- * Purpose : called by probe code indicating the NCR5380 driver
- * options that were selected.
- *
- * Inputs : instance, pointer to this instance. Unused.
- */
-
-static void __init NCR5380_print_options (struct Scsi_Host *instance)
-{
- printk(" generic options"
-#ifdef AUTOSENSE
- " AUTOSENSE"
-#endif
-#ifdef REAL_DMA
- " REAL DMA"
-#endif
-#ifdef PARITY
- " PARITY"
-#endif
-#ifdef SUPPORT_TAGS
- " SCSI-2 TAGGED QUEUING"
-#endif
- );
- printk(" generic release=%d", NCR5380_PUBLIC_RELEASE);
-}
-
-/*
- * Function : void NCR5380_print_status (struct Scsi_Host *instance)
- *
- * Purpose : print commands in the various queues, called from
- * NCR5380_abort and NCR5380_debug to aid debugging.
- *
- * Inputs : instance, pointer to this instance.
- */
-
-static void lprint_Scsi_Cmnd(Scsi_Cmnd *cmd)
-{
- int i, s;
- unsigned char *command;
- printk("scsi%d: destination target %d, lun %llu\n",
- H_NO(cmd), cmd->device->id, cmd->device->lun);
- printk(KERN_CONT " command = ");
- command = cmd->cmnd;
- printk(KERN_CONT "%2d (0x%02x)", command[0], command[0]);
- for (i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i)
- printk(KERN_CONT " %02x", command[i]);
- printk("\n");
-}
-
-static void NCR5380_print_status(struct Scsi_Host *instance)
-{
- struct NCR5380_hostdata *hostdata;
- Scsi_Cmnd *ptr;
- unsigned long flags;
-
- NCR5380_dprint(NDEBUG_ANY, instance);
- NCR5380_dprint_phase(NDEBUG_ANY, instance);
-
- hostdata = (struct NCR5380_hostdata *)instance->hostdata;
-
- printk("\nNCR5380 core release=%d.\n", NCR5380_PUBLIC_RELEASE);
- local_irq_save(flags);
- printk("NCR5380: coroutine is%s running.\n",
- main_running ? "" : "n't");
- if (!hostdata->connected)
- printk("scsi%d: no currently connected command\n", HOSTNO);
- else
- lprint_Scsi_Cmnd((Scsi_Cmnd *) hostdata->connected);
- printk("scsi%d: issue_queue\n", HOSTNO);
- for (ptr = (Scsi_Cmnd *)hostdata->issue_queue; ptr; ptr = NEXT(ptr))
- lprint_Scsi_Cmnd(ptr);
-
- printk("scsi%d: disconnected_queue\n", HOSTNO);
- for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr;
- ptr = NEXT(ptr))
- lprint_Scsi_Cmnd(ptr);
-
- local_irq_restore(flags);
- printk("\n");
-}
-
-static void show_Scsi_Cmnd(Scsi_Cmnd *cmd, struct seq_file *m)
-{
- int i, s;
- unsigned char *command;
- seq_printf(m, "scsi%d: destination target %d, lun %llu\n",
- H_NO(cmd), cmd->device->id, cmd->device->lun);
- seq_printf(m, " command = ");
- command = cmd->cmnd;
- seq_printf(m, "%2d (0x%02x)", command[0], command[0]);
- for (i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i)
- seq_printf(m, " %02x", command[i]);
- seq_printf(m, "\n");
-}
-
-static int NCR5380_show_info(struct seq_file *m, struct Scsi_Host *instance)
-{
- struct NCR5380_hostdata *hostdata;
- Scsi_Cmnd *ptr;
- unsigned long flags;
-
- hostdata = (struct NCR5380_hostdata *)instance->hostdata;
-
- seq_printf(m, "NCR5380 core release=%d.\n", NCR5380_PUBLIC_RELEASE);
- local_irq_save(flags);
- seq_printf(m, "NCR5380: coroutine is%s running.\n",
- main_running ? "" : "n't");
- if (!hostdata->connected)
- seq_printf(m, "scsi%d: no currently connected command\n", HOSTNO);
- else
- show_Scsi_Cmnd((Scsi_Cmnd *) hostdata->connected, m);
- seq_printf(m, "scsi%d: issue_queue\n", HOSTNO);
- for (ptr = (Scsi_Cmnd *)hostdata->issue_queue; ptr; ptr = NEXT(ptr))
- show_Scsi_Cmnd(ptr, m);
-
- seq_printf(m, "scsi%d: disconnected_queue\n", HOSTNO);
- for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr;
- ptr = NEXT(ptr))
- show_Scsi_Cmnd(ptr, m);
-
- local_irq_restore(flags);
- return 0;
-}
-
-/*
- * Function : void NCR5380_init (struct Scsi_Host *instance)
- *
- * Purpose : initializes *instance and corresponding 5380 chip.
- *
- * Inputs : instance - instantiation of the 5380 driver.
- *
- * Notes : I assume that the host, hostno, and id bits have been
- * set correctly. I don't care about the irq and other fields.
- *
- */
-
-static int __init NCR5380_init(struct Scsi_Host *instance, int flags)
-{
- int i;
- SETUP_HOSTDATA(instance);
-
- NCR5380_all_init();
-
- hostdata->aborted = 0;
- hostdata->id_mask = 1 << instance->this_id;
- hostdata->id_higher_mask = 0;
- for (i = hostdata->id_mask; i <= 0x80; i <<= 1)
- if (i > hostdata->id_mask)
- hostdata->id_higher_mask |= i;
- for (i = 0; i < 8; ++i)
- hostdata->busy[i] = 0;
-#ifdef SUPPORT_TAGS
- init_tags();
-#endif
-#if defined (REAL_DMA)
- hostdata->dma_len = 0;
-#endif
- hostdata->targets_present = 0;
- hostdata->connected = NULL;
- hostdata->issue_queue = NULL;
- hostdata->disconnected_queue = NULL;
- hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT;
-
- if (!the_template) {
- the_template = instance->hostt;
- first_instance = instance;
- }
-
-
-#ifndef AUTOSENSE
- if ((instance->cmd_per_lun > 1) || (instance->can_queue > 1))
- printk("scsi%d: WARNING : support for multiple outstanding commands enabled\n"
- " without AUTOSENSE option, contingent allegiance conditions may\n"
- " be incorrectly cleared.\n", HOSTNO);
-#endif /* def AUTOSENSE */
-
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- NCR5380_write(MODE_REG, MR_BASE);
- NCR5380_write(TARGET_COMMAND_REG, 0);
- NCR5380_write(SELECT_ENABLE_REG, 0);
-
- return 0;
-}
-
-static void NCR5380_exit(struct Scsi_Host *instance)
-{
- /* Empty, as we didn't schedule any delayed work */
-}
-
-/*
- * Function : int NCR5380_queue_command (struct scsi_cmnd *cmd,
- * void (*done)(struct scsi_cmnd *))
- *
- * Purpose : enqueues a SCSI command
- *
- * Inputs : cmd - SCSI command, done - function called on completion, with
- * a pointer to the command descriptor.
- *
- * Returns : 0
- *
- * Side effects :
- * cmd is added to the per instance issue_queue, with minor
- * twiddling done to the host specific fields of cmd. If the
- * main coroutine is not running, it is restarted.
- *
- */
-
-/* Only make static if a wrapper function is used */
-static int NCR5380_queue_command_lck(struct scsi_cmnd *cmd,
- void (*done)(struct scsi_cmnd *))
-{
- SETUP_HOSTDATA(cmd->device->host);
- struct scsi_cmnd *tmp;
- unsigned long flags;
-
-#if (NDEBUG & NDEBUG_NO_WRITE)
- switch (cmd->cmnd[0]) {
- case WRITE_6:
- case WRITE_10:
- printk(KERN_NOTICE "scsi%d: WRITE attempted with NO_WRITE debugging flag set\n",
- H_NO(cmd));
- cmd->result = (DID_ERROR << 16);
- done(cmd);
- return 0;
- }
-#endif /* (NDEBUG & NDEBUG_NO_WRITE) */
-
-
-#ifdef NCR5380_STATS
-# if 0
- if (!hostdata->connected && !hostdata->issue_queue &&
- !hostdata->disconnected_queue) {
- hostdata->timebase = jiffies;
- }
-# endif
-# ifdef NCR5380_STAT_LIMIT
- if (scsi_bufflen(cmd) > NCR5380_STAT_LIMIT)
-# endif
- switch (cmd->cmnd[0])
- {
- case WRITE:
- case WRITE_6:
- case WRITE_10:
- hostdata->time_write[cmd->device->id] -= (jiffies - hostdata->timebase);
- hostdata->bytes_write[cmd->device->id] += scsi_bufflen(cmd);
- hostdata->pendingw++;
- break;
- case READ:
- case READ_6:
- case READ_10:
- hostdata->time_read[cmd->device->id] -= (jiffies - hostdata->timebase);
- hostdata->bytes_read[cmd->device->id] += scsi_bufflen(cmd);
- hostdata->pendingr++;
- break;
- }
-#endif
-
- /*
- * We use the host_scribble field as a pointer to the next command
- * in a queue
- */
-
- SET_NEXT(cmd, NULL);
- cmd->scsi_done = done;
-
- cmd->result = 0;
-
-
- /*
- * Insert the cmd into the issue queue. Note that REQUEST SENSE
- * commands are added to the head of the queue since any command will
- * clear the contingent allegiance condition that exists and the
- * sense data is only guaranteed to be valid while the condition exists.
- */
-
- local_irq_save(flags);
- /* ++guenther: now that the issue queue is being set up, we can lock ST-DMA.
- * Otherwise a running NCR5380_main may steal the lock.
- * Lock before actually inserting due to fairness reasons explained in
- * atari_scsi.c. If we insert first, then it's impossible for this driver
- * to release the lock.
- * Stop timer for this command while waiting for the lock, or timeouts
- * may happen (and they really do), and it's no good if the command doesn't
- * appear in any of the queues.
- * ++roman: Just disabling the NCR interrupt isn't sufficient here,
- * because also a timer int can trigger an abort or reset, which would
- * alter queues and touch the lock.
- */
- if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
- LIST(cmd, hostdata->issue_queue);
- SET_NEXT(cmd, hostdata->issue_queue);
- hostdata->issue_queue = cmd;
- } else {
- for (tmp = (struct scsi_cmnd *)hostdata->issue_queue;
- NEXT(tmp); tmp = NEXT(tmp))
- ;
- LIST(cmd, tmp);
- SET_NEXT(tmp, cmd);
- }
-
- local_irq_restore(flags);
-
- dprintk(NDEBUG_QUEUES, "scsi%d: command added to %s of queue\n", H_NO(cmd),
- (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail");
-
- /* If queue_command() is called from an interrupt (real one or bottom
- * half), we let queue_main() do the job of taking care about main. If it
- * is already running, this is a no-op, else main will be queued.
- *
- * If we're not in an interrupt, we can call NCR5380_main()
- * unconditionally, because it cannot be already running.
- */
- if (in_interrupt() || ((flags >> 8) & 7) >= 6)
- queue_main();
- else
- NCR5380_main(NULL);
- return 0;
-}
-
-static DEF_SCSI_QCMD(NCR5380_queue_command)
-
-/*
- * Function : NCR5380_main (void)
- *
- * Purpose : NCR5380_main is a coroutine that runs as long as more work can
- * be done on the NCR5380 host adapters in a system. Both
- * NCR5380_queue_command() and NCR5380_intr() will try to start it
- * in case it is not running.
- *
- * NOTE : NCR5380_main exits with interrupts *disabled*, the caller should
- * reenable them. This prevents reentrancy and kernel stack overflow.
- */
-
-static void NCR5380_main (struct work_struct *bl)
-{
- struct scsi_cmnd *tmp, *prev;
- struct Scsi_Host *instance = first_instance;
- struct NCR5380_hostdata *hostdata = HOSTDATA(instance);
- int done;
- unsigned long flags;
-
- /*
- * We run (with interrupts disabled) until we're sure that none of
- * the host adapters have anything that can be done, at which point
- * we set main_running to 0 and exit.
- *
- * Interrupts are enabled before doing various other internal
- * instructions, after we've decided that we need to run through
- * the loop again.
- *
- * this should prevent any race conditions.
- *
- * ++roman: Just disabling the NCR interrupt isn't sufficient here,
- * because also a timer int can trigger an abort or reset, which can
- * alter queues and touch the Falcon lock.
- */
-
- /* Tell int handlers main() is now already executing. Note that
- no races are possible here. If an int comes in before
- 'main_running' is set here, and queues/executes main via the
- task queue, it doesn't do any harm, just this instance of main
- won't find any work left to do. */
- if (main_running)
- return;
- main_running = 1;
-
- local_save_flags(flags);
- do {
- local_irq_disable(); /* Freeze request queues */
- done = 1;
-
- if (!hostdata->connected) {
- dprintk(NDEBUG_MAIN, "scsi%d: not connected\n", HOSTNO );
- /*
- * Search through the issue_queue for a command destined
- * for a target that's not busy.
- */
-#if (NDEBUG & NDEBUG_LISTS)
- for (tmp = (struct scsi_cmnd *) hostdata->issue_queue, prev = NULL;
- tmp && (tmp != prev); prev = tmp, tmp = NEXT(tmp))
- ;
- if ((tmp == prev) && tmp) printk(" LOOP\n");/* else printk("\n");*/
-#endif
- for (tmp = (struct scsi_cmnd *) hostdata->issue_queue,
- prev = NULL; tmp; prev = tmp, tmp = NEXT(tmp) ) {
-
- if (prev != tmp)
- dprintk(NDEBUG_LISTS, "MAIN tmp=%p target=%d busy=%d lun=%llu\n", tmp, tmp->device->id, hostdata->busy[tmp->device->id], tmp->device->lun);
- /* When we find one, remove it from the issue queue. */
- /* ++guenther: possible race with Falcon locking */
- if (
-#ifdef SUPPORT_TAGS
- !is_lun_busy( tmp, tmp->cmnd[0] != REQUEST_SENSE)
-#else
- !(hostdata->busy[tmp->device->id] & (1 << tmp->device->lun))
-#endif
- ) {
- /* ++guenther: just to be sure, this must be atomic */
- local_irq_disable();
- if (prev) {
- REMOVE(prev, NEXT(prev), tmp, NEXT(tmp));
- SET_NEXT(prev, NEXT(tmp));
- } else {
- REMOVE(-1, hostdata->issue_queue, tmp, NEXT(tmp));
- hostdata->issue_queue = NEXT(tmp);
- }
- SET_NEXT(tmp, NULL);
-
- /* reenable interrupts after finding one */
- local_irq_restore(flags);
-
- /*
- * Attempt to establish an I_T_L nexus here.
- * On success, instance->hostdata->connected is set.
- * On failure, we must add the command back to the
- * issue queue so we can keep trying.
- */
- dprintk(NDEBUG_MAIN, "scsi%d: main(): command for target %d "
- "lun %llu removed from issue_queue\n",
- HOSTNO, tmp->device->id, tmp->device->lun);
- /*
- * REQUEST SENSE commands are issued without tagged
- * queueing, even on SCSI-II devices because the
- * contingent allegiance condition exists for the
- * entire unit.
- */
- /* ++roman: ...and the standard also requires that
- * REQUEST SENSE command are untagged.
- */
-
-#ifdef SUPPORT_TAGS
- cmd_get_tag( tmp, tmp->cmnd[0] != REQUEST_SENSE );
-#endif
- if (!NCR5380_select(instance, tmp,
- (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE :
- TAG_NEXT)) {
- break;
- } else {
- local_irq_disable();
- LIST(tmp, hostdata->issue_queue);
- SET_NEXT(tmp, hostdata->issue_queue);
- hostdata->issue_queue = tmp;
-#ifdef SUPPORT_TAGS
- cmd_free_tag( tmp );
-#endif
- local_irq_restore(flags);
- dprintk(NDEBUG_MAIN, "scsi%d: main(): select() failed, "
- "returned to issue_queue\n", HOSTNO);
- if (hostdata->connected)
- break;
- }
- } /* if target/lun/target queue is not busy */
- } /* for issue_queue */
- } /* if (!hostdata->connected) */
- if (hostdata->connected
-#ifdef REAL_DMA
- && !hostdata->dma_len
-#endif
- ) {
- local_irq_restore(flags);
- dprintk(NDEBUG_MAIN, "scsi%d: main: performing information transfer\n",
- HOSTNO);
- NCR5380_information_transfer(instance);
- dprintk(NDEBUG_MAIN, "scsi%d: main: done set false\n", HOSTNO);
- done = 0;
- }
- } while (!done);
-
- /* Better allow ints _after_ 'main_running' has been cleared, else
- an interrupt could believe we'll pick up the work it left for
- us, but we won't see it anymore here... */
- main_running = 0;
- local_irq_restore(flags);
-}
-
-
-#ifdef REAL_DMA
-/*
- * Function : void NCR5380_dma_complete (struct Scsi_Host *instance)
- *
- * Purpose : Called by interrupt handler when DMA finishes or a phase
- * mismatch occurs (which would finish the DMA transfer).
- *
- * Inputs : instance - this instance of the NCR5380.
- *
- */
-
-static void NCR5380_dma_complete( struct Scsi_Host *instance )
-{
- SETUP_HOSTDATA(instance);
- int transfered;
- unsigned char **data;
- volatile int *count;
-
- if (!hostdata->connected) {
- printk(KERN_WARNING "scsi%d: received end of DMA interrupt with "
- "no connected cmd\n", HOSTNO);
- return;
- }
-
- dprintk(NDEBUG_DMA, "scsi%d: real DMA transfer complete, basr 0x%X, sr 0x%X\n",
- HOSTNO, NCR5380_read(BUS_AND_STATUS_REG),
- NCR5380_read(STATUS_REG));
-
- if((sun3scsi_dma_finish(rq_data_dir(hostdata->connected->request)))) {
- printk("scsi%d: overrun in UDC counter -- not prepared to deal with this!\n", HOSTNO);
- printk("please e-mail sammy@sammy.net with a description of how this\n");
- printk("error was produced.\n");
- BUG();
- }
-
- /* make sure we're not stuck in a data phase */
- if((NCR5380_read(BUS_AND_STATUS_REG) & (BASR_PHASE_MATCH |
- BASR_ACK)) ==
- (BASR_PHASE_MATCH | BASR_ACK)) {
- printk("scsi%d: BASR %02x\n", HOSTNO, NCR5380_read(BUS_AND_STATUS_REG));
- printk("scsi%d: bus stuck in data phase -- probably a single byte "
- "overrun!\n", HOSTNO);
- printk("not prepared for this error!\n");
- printk("please e-mail sammy@sammy.net with a description of how this\n");
- printk("error was produced.\n");
- BUG();
- }
-
-
-
- (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
- NCR5380_write(MODE_REG, MR_BASE);
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-
- transfered = hostdata->dma_len - NCR5380_dma_residual(instance);
- hostdata->dma_len = 0;
-
- data = (unsigned char **) &(hostdata->connected->SCp.ptr);
- count = &(hostdata->connected->SCp.this_residual);
- *data += transfered;
- *count -= transfered;
-
-}
-#endif /* REAL_DMA */
-
-
-/*
- * Function : void NCR5380_intr (int irq)
- *
- * Purpose : handle interrupts, reestablishing I_T_L or I_T_L_Q nexuses
- * from the disconnected queue, and restarting NCR5380_main()
- * as required.
- *
- * Inputs : int irq, irq that caused this interrupt.
- *
- */
-
-static irqreturn_t NCR5380_intr (int irq, void *dev_id)
-{
- struct Scsi_Host *instance = first_instance;
- int done = 1, handled = 0;
- unsigned char basr;
-
- dprintk(NDEBUG_INTR, "scsi%d: NCR5380 irq triggered\n", HOSTNO);
-
- /* Look for pending interrupts */
- basr = NCR5380_read(BUS_AND_STATUS_REG);
- dprintk(NDEBUG_INTR, "scsi%d: BASR=%02x\n", HOSTNO, basr);
- /* dispatch to appropriate routine if found and done=0 */
- if (basr & BASR_IRQ) {
- NCR5380_dprint(NDEBUG_INTR, instance);
- if ((NCR5380_read(STATUS_REG) & (SR_SEL|SR_IO)) == (SR_SEL|SR_IO)) {
- done = 0;
-// ENABLE_IRQ();
- dprintk(NDEBUG_INTR, "scsi%d: SEL interrupt\n", HOSTNO);
- NCR5380_reselect(instance);
- (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
- }
- else if (basr & BASR_PARITY_ERROR) {
- dprintk(NDEBUG_INTR, "scsi%d: PARITY interrupt\n", HOSTNO);
- (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
- }
- else if ((NCR5380_read(STATUS_REG) & SR_RST) == SR_RST) {
- dprintk(NDEBUG_INTR, "scsi%d: RESET interrupt\n", HOSTNO);
- (void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
- }
- else {
- /*
- * The rest of the interrupt conditions can occur only during a
- * DMA transfer
- */
-
-#if defined(REAL_DMA)
- /*
- * We should only get PHASE MISMATCH and EOP interrupts if we have
- * DMA enabled, so do a sanity check based on the current setting
- * of the MODE register.
- */
-
- if ((NCR5380_read(MODE_REG) & MR_DMA_MODE) &&
- ((basr & BASR_END_DMA_TRANSFER) ||
- !(basr & BASR_PHASE_MATCH))) {
-
- dprintk(NDEBUG_INTR, "scsi%d: PHASE MISM or EOP interrupt\n", HOSTNO);
- NCR5380_dma_complete( instance );
- done = 0;
-// ENABLE_IRQ();
- } else
-#endif /* REAL_DMA */
- {
-/* MS: Ignore unknown phase mismatch interrupts (caused by EOP interrupt) */
- if (basr & BASR_PHASE_MATCH)
- dprintk(NDEBUG_INTR, "scsi%d: unknown interrupt, "
- "BASR 0x%x, MR 0x%x, SR 0x%x\n",
- HOSTNO, basr, NCR5380_read(MODE_REG),
- NCR5380_read(STATUS_REG));
- (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
-#ifdef SUN3_SCSI_VME
- dregs->csr |= CSR_DMA_ENABLE;
-#endif
- }
- } /* if !(SELECTION || PARITY) */
- handled = 1;
- } /* BASR & IRQ */
- else {
-
- printk(KERN_NOTICE "scsi%d: interrupt without IRQ bit set in BASR, "
- "BASR 0x%X, MR 0x%X, SR 0x%x\n", HOSTNO, basr,
- NCR5380_read(MODE_REG), NCR5380_read(STATUS_REG));
- (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
-#ifdef SUN3_SCSI_VME
- dregs->csr |= CSR_DMA_ENABLE;
-#endif
- }
-
- if (!done) {
- dprintk(NDEBUG_INTR, "scsi%d: in int routine, calling main\n", HOSTNO);
- /* Put a call to NCR5380_main() on the queue... */
- queue_main();
- }
- return IRQ_RETVAL(handled);
-}
-
-#ifdef NCR5380_STATS
-static void collect_stats(struct NCR5380_hostdata *hostdata,
- struct scsi_cmnd *cmd)
-{
-# ifdef NCR5380_STAT_LIMIT
- if (scsi_bufflen(cmd) > NCR5380_STAT_LIMIT)
-# endif
- switch (cmd->cmnd[0])
- {
- case WRITE:
- case WRITE_6:
- case WRITE_10:
- hostdata->time_write[cmd->device->id] += (jiffies - hostdata->timebase);
- /*hostdata->bytes_write[cmd->device->id] += scsi_bufflen(cmd);*/
- hostdata->pendingw--;
- break;
- case READ:
- case READ_6:
- case READ_10:
- hostdata->time_read[cmd->device->id] += (jiffies - hostdata->timebase);
- /*hostdata->bytes_read[cmd->device->id] += scsi_bufflen(cmd);*/
- hostdata->pendingr--;
- break;
- }
-}
-#endif
-
-/*
- * Function : int NCR5380_select(struct Scsi_Host *instance,
- * struct scsi_cmnd *cmd, int tag);
- *
- * Purpose : establishes I_T_L or I_T_L_Q nexus for new or existing command,
- * including ARBITRATION, SELECTION, and initial message out for
- * IDENTIFY and queue messages.
- *
- * Inputs : instance - instantiation of the 5380 driver on which this
- * target lives, cmd - SCSI command to execute, tag - set to TAG_NEXT for
- * new tag, TAG_NONE for untagged queueing, otherwise set to the tag for
- * the command that is presently connected.
- *
- * Returns : -1 if selection could not execute for some reason,
- * 0 if selection succeeded or failed because the target
- * did not respond.
- *
- * Side effects :
- * If bus busy, arbitration failed, etc, NCR5380_select() will exit
- * with registers as they should have been on entry - ie
- * SELECT_ENABLE will be set appropriately, the NCR5380
- * will cease to drive any SCSI bus signals.
- *
- * If successful : I_T_L or I_T_L_Q nexus will be established,
- * instance->connected will be set to cmd.
- * SELECT interrupt will be disabled.
- *
- * If failed (no target) : cmd->scsi_done() will be called, and the
- * cmd->result host byte set to DID_BAD_TARGET.
- */
-
-static int NCR5380_select(struct Scsi_Host *instance, struct scsi_cmnd *cmd,
- int tag)
-{
- SETUP_HOSTDATA(instance);
- unsigned char tmp[3], phase;
- unsigned char *data;
- int len;
- unsigned long timeout;
- unsigned long flags;
-
- hostdata->restart_select = 0;
- NCR5380_dprint(NDEBUG_ARBITRATION, instance);
- dprintk(NDEBUG_ARBITRATION, "scsi%d: starting arbitration, id = %d\n", HOSTNO,
- instance->this_id);
-
- /*
- * Set the phase bits to 0, otherwise the NCR5380 won't drive the
- * data bus during SELECTION.
- */
-
- local_irq_save(flags);
- if (hostdata->connected) {
- local_irq_restore(flags);
- return -1;
- }
- NCR5380_write(TARGET_COMMAND_REG, 0);
-
-
- /*
- * Start arbitration.
- */
-
- NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask);
- NCR5380_write(MODE_REG, MR_ARBITRATE);
-
- local_irq_restore(flags);
-
- /* Wait for arbitration logic to complete */
-#ifdef NCR_TIMEOUT
- {
- unsigned long timeout = jiffies + 2*NCR_TIMEOUT;
-
- while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS)
- && time_before(jiffies, timeout) && !hostdata->connected)
- ;
- if (time_after_eq(jiffies, timeout))
- {
- printk("scsi : arbitration timeout at %d\n", __LINE__);
- NCR5380_write(MODE_REG, MR_BASE);
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- return -1;
- }
- }
-#else /* NCR_TIMEOUT */
- while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS)
- && !hostdata->connected);
-#endif
-
- dprintk(NDEBUG_ARBITRATION, "scsi%d: arbitration complete\n", HOSTNO);
-
- if (hostdata->connected) {
- NCR5380_write(MODE_REG, MR_BASE);
- return -1;
- }
- /*
- * The arbitration delay is 2.2us, but this is a minimum and there is
- * no maximum so we can safely sleep for ceil(2.2) usecs to accommodate
- * the integral nature of udelay().
- *
- */
-
- udelay(3);
-
- /* Check for lost arbitration */
- if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
- (NCR5380_read(CURRENT_SCSI_DATA_REG) & hostdata->id_higher_mask) ||
- (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
- hostdata->connected) {
- NCR5380_write(MODE_REG, MR_BASE);
- dprintk(NDEBUG_ARBITRATION, "scsi%d: lost arbitration, deasserting MR_ARBITRATE\n",
- HOSTNO);
- return -1;
- }
-
- /* after/during arbitration, BSY should be asserted.
- IBM DPES-31080 Version S31Q works now */
- /* Tnx to Thomas_Roesch@m2.maus.de for finding this! (Roman) */
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_SEL |
- ICR_ASSERT_BSY ) ;
-
- if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
- hostdata->connected) {
- NCR5380_write(MODE_REG, MR_BASE);
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- dprintk(NDEBUG_ARBITRATION, "scsi%d: lost arbitration, deasserting ICR_ASSERT_SEL\n",
- HOSTNO);
- return -1;
- }
-
- /*
- * Again, bus clear + bus settle time is 1.2us, however, this is
- * a minimum so we'll udelay ceil(1.2)
- */
-
-#ifdef CONFIG_ATARI_SCSI_TOSHIBA_DELAY
- /* ++roman: But some targets (see above :-) seem to need a bit more... */
- udelay(15);
-#else
- udelay(2);
-#endif
-
- if (hostdata->connected) {
- NCR5380_write(MODE_REG, MR_BASE);
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- return -1;
- }
-
- dprintk(NDEBUG_ARBITRATION, "scsi%d: won arbitration\n", HOSTNO);
-
- /*
- * Now that we have won arbitration, start Selection process, asserting
- * the host and target ID's on the SCSI bus.
- */
-
- NCR5380_write(OUTPUT_DATA_REG, (hostdata->id_mask | (1 << cmd->device->id)));
-
- /*
- * Raise ATN while SEL is true before BSY goes false from arbitration,
- * since this is the only way to guarantee that we'll get a MESSAGE OUT
- * phase immediately after selection.
- */
-
- NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_BSY |
- ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_SEL ));
- NCR5380_write(MODE_REG, MR_BASE);
-
- /*
- * Reselect interrupts must be turned off prior to the dropping of BSY,
- * otherwise we will trigger an interrupt.
- */
-
- if (hostdata->connected) {
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- return -1;
- }
-
- NCR5380_write(SELECT_ENABLE_REG, 0);
-
- /*
- * The initiator shall then wait at least two deskew delays and release
- * the BSY signal.
- */
- udelay(1); /* wingel -- wait two bus deskew delay >2*45ns */
-
- /* Reset BSY */
- NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_DATA |
- ICR_ASSERT_ATN | ICR_ASSERT_SEL));
-
- /*
- * Something weird happens when we cease to drive BSY - looks
- * like the board/chip is letting us do another read before the
- * appropriate propagation delay has expired, and we're confusing
- * a BSY signal from ourselves as the target's response to SELECTION.
- *
- * A small delay (the 'C++' frontend breaks the pipeline with an
- * unnecessary jump, making it work on my 386-33/Trantor T128, the
- * tighter 'C' code breaks and requires this) solves the problem -
- * the 1 us delay is arbitrary, and only used because this delay will
- * be the same on other platforms and since it works here, it should
- * work there.
- *
- * wingel suggests that this could be due to failing to wait
- * one deskew delay.
- */
-
- udelay(1);
-
- dprintk(NDEBUG_SELECTION, "scsi%d: selecting target %d\n", HOSTNO, cmd->device->id);
-
- /*
- * The SCSI specification calls for a 250 ms timeout for the actual
- * selection.
- */
-
- timeout = jiffies + 25;
-
- /*
- * XXX very interesting - we're seeing a bounce where the BSY we
- * asserted is being reflected / still asserted (propagation delay?)
- * and it's detecting as true. Sigh.
- */
-
-#if 0
- /* ++roman: If a target conformed to the SCSI standard, it wouldn't assert
- * IO while SEL is true. But again, there are some disks out the in the
- * world that do that nevertheless. (Somebody claimed that this announces
- * reselection capability of the target.) So we better skip that test and
- * only wait for BSY... (Famous german words: Der Klügere gibt nach :-)
- */
-
- while (time_before(jiffies, timeout) && !(NCR5380_read(STATUS_REG) &
- (SR_BSY | SR_IO)));
-
- if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) ==
- (SR_SEL | SR_IO)) {
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- NCR5380_reselect(instance);
- printk (KERN_ERR "scsi%d: reselection after won arbitration?\n",
- HOSTNO);
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- return -1;
- }
-#else
- while (time_before(jiffies, timeout) && !(NCR5380_read(STATUS_REG) & SR_BSY));
-#endif
-
- /*
- * No less than two deskew delays after the initiator detects the
- * BSY signal is true, it shall release the SEL signal and may
- * change the DATA BUS. -wingel
- */
-
- udelay(1);
-
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
-
- if (!(NCR5380_read(STATUS_REG) & SR_BSY)) {
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- if (hostdata->targets_present & (1 << cmd->device->id)) {
- printk(KERN_ERR "scsi%d: weirdness\n", HOSTNO);
- if (hostdata->restart_select)
- printk(KERN_NOTICE "\trestart select\n");
- NCR5380_dprint(NDEBUG_ANY, instance);
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- return -1;
- }
- cmd->result = DID_BAD_TARGET << 16;
-#ifdef NCR5380_STATS
- collect_stats(hostdata, cmd);
-#endif
-#ifdef SUPPORT_TAGS
- cmd_free_tag( cmd );
-#endif
- cmd->scsi_done(cmd);
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- dprintk(NDEBUG_SELECTION, "scsi%d: target did not respond within 250ms\n", HOSTNO);
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- return 0;
- }
-
- hostdata->targets_present |= (1 << cmd->device->id);
-
- /*
- * Since we followed the SCSI spec, and raised ATN while SEL
- * was true but before BSY was false during selection, the information
- * transfer phase should be a MESSAGE OUT phase so that we can send the
- * IDENTIFY message.
- *
- * If SCSI-II tagged queuing is enabled, we also send a SIMPLE_QUEUE_TAG
- * message (2 bytes) with a tag ID that we increment with every command
- * until it wraps back to 0.
- *
- * XXX - it turns out that there are some broken SCSI-II devices,
- * which claim to support tagged queuing but fail when more than
- * some number of commands are issued at once.
- */
-
- /* Wait for start of REQ/ACK handshake */
- while (!(NCR5380_read(STATUS_REG) & SR_REQ));
-
- dprintk(NDEBUG_SELECTION, "scsi%d: target %d selected, going into MESSAGE OUT phase.\n",
- HOSTNO, cmd->device->id);
- tmp[0] = IDENTIFY(1, cmd->device->lun);
-
-#ifdef SUPPORT_TAGS
- if (cmd->tag != TAG_NONE) {
- tmp[1] = hostdata->last_message = SIMPLE_QUEUE_TAG;
- tmp[2] = cmd->tag;
- len = 3;
- } else
- len = 1;
-#else
- len = 1;
- cmd->tag=0;
-#endif /* SUPPORT_TAGS */
-
- /* Send message(s) */
- data = tmp;
- phase = PHASE_MSGOUT;
- NCR5380_transfer_pio(instance, &phase, &len, &data);
- dprintk(NDEBUG_SELECTION, "scsi%d: nexus established.\n", HOSTNO);
- /* XXX need to handle errors here */
- hostdata->connected = cmd;
-#ifndef SUPPORT_TAGS
- hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun);
-#endif
-#ifdef SUN3_SCSI_VME
- dregs->csr |= CSR_INTR;
-#endif
- initialize_SCp(cmd);
-
-
- return 0;
-}
-
-/*
- * Function : int NCR5380_transfer_pio (struct Scsi_Host *instance,
- * unsigned char *phase, int *count, unsigned char **data)
- *
- * Purpose : transfers data in given phase using polled I/O
- *
- * Inputs : instance - instance of driver, *phase - pointer to
- * what phase is expected, *count - pointer to number of
- * bytes to transfer, **data - pointer to data pointer.
- *
- * Returns : -1 when different phase is entered without transferring
- * maximum number of bytes, 0 if all bytes are transferred or exit
- * is in same phase.
- *
- * Also, *phase, *count, *data are modified in place.
- *
- * XXX Note : handling for bus free may be useful.
- */
-
-/*
- * Note : this code is not as quick as it could be, however it
- * IS 100% reliable, and for the actual data transfer where speed
- * counts, we will always do a pseudo DMA or DMA transfer.
- */
-
-static int NCR5380_transfer_pio( struct Scsi_Host *instance,
- unsigned char *phase, int *count,
- unsigned char **data)
-{
- register unsigned char p = *phase, tmp;
- register int c = *count;
- register unsigned char *d = *data;
-
- /*
- * The NCR5380 chip will only drive the SCSI bus when the
- * phase specified in the appropriate bits of the TARGET COMMAND
- * REGISTER match the STATUS REGISTER
- */
-
- NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
-
- do {
- /*
- * Wait for assertion of REQ, after which the phase bits will be
- * valid
- */
- while (!((tmp = NCR5380_read(STATUS_REG)) & SR_REQ));
-
- dprintk(NDEBUG_HANDSHAKE, "scsi%d: REQ detected\n", HOSTNO);
-
- /* Check for phase mismatch */
- if ((tmp & PHASE_MASK) != p) {
- dprintk(NDEBUG_PIO, "scsi%d: phase mismatch\n", HOSTNO);
- NCR5380_dprint_phase(NDEBUG_PIO, instance);
- break;
- }
-
- /* Do actual transfer from SCSI bus to / from memory */
- if (!(p & SR_IO))
- NCR5380_write(OUTPUT_DATA_REG, *d);
- else
- *d = NCR5380_read(CURRENT_SCSI_DATA_REG);
-
- ++d;
-
- /*
- * The SCSI standard suggests that in MSGOUT phase, the initiator
- * should drop ATN on the last byte of the message phase
- * after REQ has been asserted for the handshake but before
- * the initiator raises ACK.
- */
-
- if (!(p & SR_IO)) {
- if (!((p & SR_MSG) && c > 1)) {
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
- ICR_ASSERT_DATA);
- NCR5380_dprint(NDEBUG_PIO, instance);
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
- ICR_ASSERT_DATA | ICR_ASSERT_ACK);
- } else {
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
- ICR_ASSERT_DATA | ICR_ASSERT_ATN);
- NCR5380_dprint(NDEBUG_PIO, instance);
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
- ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_ACK);
- }
- } else {
- NCR5380_dprint(NDEBUG_PIO, instance);
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK);
- }
-
- while (NCR5380_read(STATUS_REG) & SR_REQ);
-
- dprintk(NDEBUG_HANDSHAKE, "scsi%d: req false, handshake complete\n", HOSTNO);
-
-/*
- * We have several special cases to consider during REQ/ACK handshaking :
- * 1. We were in MSGOUT phase, and we are on the last byte of the
- * message. ATN must be dropped as ACK is dropped.
- *
- * 2. We are in a MSGIN phase, and we are on the last byte of the
- * message. We must exit with ACK asserted, so that the calling
- * code may raise ATN before dropping ACK to reject the message.
- *
- * 3. ACK and ATN are clear and the target may proceed as normal.
- */
- if (!(p == PHASE_MSGIN && c == 1)) {
- if (p == PHASE_MSGOUT && c > 1)
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
- else
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- }
- } while (--c);
-
- dprintk(NDEBUG_PIO, "scsi%d: residual %d\n", HOSTNO, c);
-
- *count = c;
- *data = d;
- tmp = NCR5380_read(STATUS_REG);
- /* The phase read from the bus is valid if either REQ is (already)
- * asserted or if ACK hasn't been released yet. The latter is the case if
- * we're in MSGIN and all wanted bytes have been received. */
- if ((tmp & SR_REQ) || (p == PHASE_MSGIN && c == 0))
- *phase = tmp & PHASE_MASK;
- else
- *phase = PHASE_UNKNOWN;
-
- if (!c || (*phase == p))
- return 0;
- else
- return -1;
-}
-
-/*
- * Function : do_abort (Scsi_Host *host)
- *
- * Purpose : abort the currently established nexus. Should only be
- * called from a routine which can drop into a
- *
- * Returns : 0 on success, -1 on failure.
- */
-
-static int do_abort (struct Scsi_Host *host)
-{
- unsigned char tmp, *msgptr, phase;
- int len;
-
- /* Request message out phase */
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
-
- /*
- * Wait for the target to indicate a valid phase by asserting
- * REQ. Once this happens, we'll have either a MSGOUT phase
- * and can immediately send the ABORT message, or we'll have some
- * other phase and will have to source/sink data.
- *
- * We really don't care what value was on the bus or what value
- * the target sees, so we just handshake.
- */
-
- while (!((tmp = NCR5380_read(STATUS_REG)) & SR_REQ));
-
- NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));
-
- if ((tmp & PHASE_MASK) != PHASE_MSGOUT) {
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN |
- ICR_ASSERT_ACK);
- while (NCR5380_read(STATUS_REG) & SR_REQ);
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
- }
-
- tmp = ABORT;
- msgptr = &tmp;
- len = 1;
- phase = PHASE_MSGOUT;
- NCR5380_transfer_pio (host, &phase, &len, &msgptr);
-
- /*
- * If we got here, and the command completed successfully,
- * we're about to go into bus free state.
- */
-
- return len ? -1 : 0;
-}
-
-#if defined(REAL_DMA)
-/*
- * Function : int NCR5380_transfer_dma (struct Scsi_Host *instance,
- * unsigned char *phase, int *count, unsigned char **data)
- *
- * Purpose : transfers data in given phase using either real
- * or pseudo DMA.
- *
- * Inputs : instance - instance of driver, *phase - pointer to
- * what phase is expected, *count - pointer to number of
- * bytes to transfer, **data - pointer to data pointer.
- *
- * Returns : -1 when different phase is entered without transferring
- * maximum number of bytes, 0 if all bytes or transferred or exit
- * is in same phase.
- *
- * Also, *phase, *count, *data are modified in place.
- *
- */
-
-
-static int NCR5380_transfer_dma( struct Scsi_Host *instance,
- unsigned char *phase, int *count,
- unsigned char **data)
-{
- SETUP_HOSTDATA(instance);
- register int c = *count;
- register unsigned char p = *phase;
- unsigned long flags;
-
- /* sanity check */
- if(!sun3_dma_setup_done) {
- printk("scsi%d: transfer_dma without setup!\n", HOSTNO);
- BUG();
- }
- hostdata->dma_len = c;
-
- dprintk(NDEBUG_DMA, "scsi%d: initializing DMA for %s, %d bytes %s %p\n",
- HOSTNO, (p & SR_IO) ? "reading" : "writing",
- c, (p & SR_IO) ? "to" : "from", *data);
-
- /* netbsd turns off ints here, why not be safe and do it too */
- local_irq_save(flags);
-
- /* send start chain */
- sun3scsi_dma_start(c, *data);
-
- if (p & SR_IO) {
- NCR5380_write(TARGET_COMMAND_REG, 1);
- NCR5380_read(RESET_PARITY_INTERRUPT_REG);
- NCR5380_write(INITIATOR_COMMAND_REG, 0);
- NCR5380_write(MODE_REG, (NCR5380_read(MODE_REG) | MR_DMA_MODE | MR_ENABLE_EOP_INTR));
- NCR5380_write(START_DMA_INITIATOR_RECEIVE_REG, 0);
- } else {
- NCR5380_write(TARGET_COMMAND_REG, 0);
- NCR5380_read(RESET_PARITY_INTERRUPT_REG);
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_DATA);
- NCR5380_write(MODE_REG, (NCR5380_read(MODE_REG) | MR_DMA_MODE | MR_ENABLE_EOP_INTR));
- NCR5380_write(START_DMA_SEND_REG, 0);
- }
-
-#ifdef SUN3_SCSI_VME
- dregs->csr |= CSR_DMA_ENABLE;
-#endif
-
- local_irq_restore(flags);
-
- sun3_dma_active = 1;
- return 0;
-}
-#endif /* defined(REAL_DMA) */
-
-/*
- * Function : NCR5380_information_transfer (struct Scsi_Host *instance)
- *
- * Purpose : run through the various SCSI phases and do as the target
- * directs us to. Operates on the currently connected command,
- * instance->connected.
- *
- * Inputs : instance, instance for which we are doing commands
- *
- * Side effects : SCSI things happen, the disconnected queue will be
- * modified if a command disconnects, *instance->connected will
- * change.
- *
- * XXX Note : we need to watch for bus free or a reset condition here
- * to recover from an unexpected bus free condition.
- */
-
-static void NCR5380_information_transfer (struct Scsi_Host *instance)
-{
- SETUP_HOSTDATA(instance);
- unsigned long flags;
- unsigned char msgout = NOP;
- int sink = 0;
- int len;
-#if defined(REAL_DMA)
- int transfersize;
-#endif
- unsigned char *data;
- unsigned char phase, tmp, extended_msg[10], old_phase=0xff;
- struct scsi_cmnd *cmd = (struct scsi_cmnd *) hostdata->connected;
-
-#ifdef SUN3_SCSI_VME
- dregs->csr |= CSR_INTR;
-#endif
-
- while (1) {
- tmp = NCR5380_read(STATUS_REG);
- /* We only have a valid SCSI phase when REQ is asserted */
- if (tmp & SR_REQ) {
- phase = (tmp & PHASE_MASK);
- if (phase != old_phase) {
- old_phase = phase;
- NCR5380_dprint_phase(NDEBUG_INFORMATION, instance);
- }
-
- if(phase == PHASE_CMDOUT) {
- void *d;
- unsigned long count;
-
- if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) {
- count = cmd->SCp.buffer->length;
- d = SGADDR(cmd->SCp.buffer);
- } else {
- count = cmd->SCp.this_residual;
- d = cmd->SCp.ptr;
- }
-#ifdef REAL_DMA
- /* this command setup for dma yet? */
- if((count > SUN3_DMA_MINSIZE) && (sun3_dma_setup_done
- != cmd))
- {
- if (cmd->request->cmd_type == REQ_TYPE_FS) {
- sun3scsi_dma_setup(d, count,
- rq_data_dir(cmd->request));
- sun3_dma_setup_done = cmd;
- }
- }
-#endif
-#ifdef SUN3_SCSI_VME
- dregs->csr |= CSR_INTR;
-#endif
- }
-
-
- if (sink && (phase != PHASE_MSGOUT)) {
- NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));
-
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN |
- ICR_ASSERT_ACK);
- while (NCR5380_read(STATUS_REG) & SR_REQ);
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
- ICR_ASSERT_ATN);
- sink = 0;
- continue;
- }
-
- switch (phase) {
- case PHASE_DATAOUT:
-#if (NDEBUG & NDEBUG_NO_DATAOUT)
- printk("scsi%d: NDEBUG_NO_DATAOUT set, attempted DATAOUT "
- "aborted\n", HOSTNO);
- sink = 1;
- do_abort(instance);
- cmd->result = DID_ERROR << 16;
- cmd->scsi_done(cmd);
- return;
-#endif
- case PHASE_DATAIN:
- /*
- * If there is no room left in the current buffer in the
- * scatter-gather list, move onto the next one.
- */
- if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) {
- ++cmd->SCp.buffer;
- --cmd->SCp.buffers_residual;
- cmd->SCp.this_residual = cmd->SCp.buffer->length;
- cmd->SCp.ptr = SGADDR(cmd->SCp.buffer);
- dprintk(NDEBUG_INFORMATION, "scsi%d: %d bytes and %d buffers left\n",
- HOSTNO, cmd->SCp.this_residual,
- cmd->SCp.buffers_residual);
- }
-
- /*
- * The preferred transfer method is going to be
- * PSEUDO-DMA for systems that are strictly PIO,
- * since we can let the hardware do the handshaking.
- *
- * For this to work, we need to know the transfersize
- * ahead of time, since the pseudo-DMA code will sit
- * in an unconditional loop.
- */
-
-/* ++roman: I suggest, this should be
- * #if def(REAL_DMA)
- * instead of leaving REAL_DMA out.
- */
-
-#if defined(REAL_DMA)
-// if (!cmd->device->borken &&
- if((transfersize =
- NCR5380_dma_xfer_len(instance,cmd,phase)) > SUN3_DMA_MINSIZE) {
- len = transfersize;
- cmd->SCp.phase = phase;
-
- if (NCR5380_transfer_dma(instance, &phase,
- &len, (unsigned char **) &cmd->SCp.ptr)) {
- /*
- * If the watchdog timer fires, all future
- * accesses to this device will use the
- * polled-IO. */
- printk(KERN_NOTICE "scsi%d: switching target %d "
- "lun %llu to slow handshake\n", HOSTNO,
- cmd->device->id, cmd->device->lun);
- cmd->device->borken = 1;
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
- ICR_ASSERT_ATN);
- sink = 1;
- do_abort(instance);
- cmd->result = DID_ERROR << 16;
- cmd->scsi_done(cmd);
- /* XXX - need to source or sink data here, as appropriate */
- } else {
-#ifdef REAL_DMA
- /* ++roman: When using real DMA,
- * information_transfer() should return after
- * starting DMA since it has nothing more to
- * do.
- */
- return;
-#else
- cmd->SCp.this_residual -= transfersize - len;
-#endif
- }
- } else
-#endif /* defined(REAL_DMA) */
- NCR5380_transfer_pio(instance, &phase,
- (int *) &cmd->SCp.this_residual, (unsigned char **)
- &cmd->SCp.ptr);
-#ifdef REAL_DMA
- /* if we had intended to dma that command clear it */
- if(sun3_dma_setup_done == cmd)
- sun3_dma_setup_done = NULL;
-#endif
-
- break;
- case PHASE_MSGIN:
- len = 1;
- data = &tmp;
- NCR5380_write(SELECT_ENABLE_REG, 0); /* disable reselects */
- NCR5380_transfer_pio(instance, &phase, &len, &data);
- cmd->SCp.Message = tmp;
-
- switch (tmp) {
- /*
- * Linking lets us reduce the time required to get the
- * next command out to the device, hopefully this will
- * mean we don't waste another revolution due to the delays
- * required by ARBITRATION and another SELECTION.
- *
- * In the current implementation proposal, low level drivers
- * merely have to start the next command, pointed to by
- * next_link, done() is called as with unlinked commands.
- */
-#ifdef LINKED
- case LINKED_CMD_COMPLETE:
- case LINKED_FLG_CMD_COMPLETE:
- /* Accept message by clearing ACK */
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-
- dprintk(NDEBUG_LINKED, "scsi%d: target %d lun %llu linked command "
- "complete.\n", HOSTNO, cmd->device->id, cmd->device->lun);
-
- /* Enable reselect interrupts */
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- /*
- * Sanity check : A linked command should only terminate
- * with one of these messages if there are more linked
- * commands available.
- */
-
- if (!cmd->next_link) {
- printk(KERN_NOTICE "scsi%d: target %d lun %llu "
- "linked command complete, no next_link\n",
- HOSTNO, cmd->device->id, cmd->device->lun);
- sink = 1;
- do_abort (instance);
- return;
- }
-
- initialize_SCp(cmd->next_link);
- /* The next command is still part of this process; copy it
- * and don't free it! */
- cmd->next_link->tag = cmd->tag;
- cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
- dprintk(NDEBUG_LINKED, "scsi%d: target %d lun %llu linked request "
- "done, calling scsi_done().\n",
- HOSTNO, cmd->device->id, cmd->device->lun);
-#ifdef NCR5380_STATS
- collect_stats(hostdata, cmd);
-#endif
- cmd->scsi_done(cmd);
- cmd = hostdata->connected;
- break;
-#endif /* def LINKED */
- case ABORT:
- case COMMAND_COMPLETE:
- /* Accept message by clearing ACK */
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- hostdata->connected = NULL;
- dprintk(NDEBUG_QUEUES, "scsi%d: command for target %d, lun %llu "
- "completed\n", HOSTNO, cmd->device->id, cmd->device->lun);
-#ifdef SUPPORT_TAGS
- cmd_free_tag( cmd );
- if (status_byte(cmd->SCp.Status) == QUEUE_FULL) {
- /* Turn a QUEUE FULL status into BUSY, I think the
- * mid level cannot handle QUEUE FULL :-( (The
- * command is retried after BUSY). Also update our
- * queue size to the number of currently issued
- * commands now.
- */
- /* ++Andreas: the mid level code knows about
- QUEUE_FULL now. */
- TAG_ALLOC *ta = &TagAlloc[cmd->device->id][cmd->device->lun];
- dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %llu returned "
- "QUEUE_FULL after %d commands\n",
- HOSTNO, cmd->device->id, cmd->device->lun,
- ta->nr_allocated);
- if (ta->queue_size > ta->nr_allocated)
- ta->nr_allocated = ta->queue_size;
- }
-#else
- hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
-#endif
- /* Enable reselect interrupts */
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
-
- /*
- * I'm not sure what the correct thing to do here is :
- *
- * If the command that just executed is NOT a request
- * sense, the obvious thing to do is to set the result
- * code to the values of the stored parameters.
- *
- * If it was a REQUEST SENSE command, we need some way to
- * differentiate between the failure code of the original
- * and the failure code of the REQUEST sense - the obvious
- * case is success, where we fall through and leave the
- * result code unchanged.
- *
- * The non-obvious place is where the REQUEST SENSE failed
- */
-
- if (cmd->cmnd[0] != REQUEST_SENSE)
- cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
- else if (status_byte(cmd->SCp.Status) != GOOD)
- cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
-
-#ifdef AUTOSENSE
- if ((cmd->cmnd[0] == REQUEST_SENSE) &&
- hostdata->ses.cmd_len) {
- scsi_eh_restore_cmnd(cmd, &hostdata->ses);
- hostdata->ses.cmd_len = 0 ;
- }
-
- if ((cmd->cmnd[0] != REQUEST_SENSE) &&
- (status_byte(cmd->SCp.Status) == CHECK_CONDITION)) {
- scsi_eh_prep_cmnd(cmd, &hostdata->ses, NULL, 0, ~0);
- dprintk(NDEBUG_AUTOSENSE, "scsi%d: performing request sense\n",
- HOSTNO);
- /* this is initialized from initialize_SCp
- cmd->SCp.buffer = NULL;
- cmd->SCp.buffers_residual = 0;
- */
-
- local_irq_save(flags);
- LIST(cmd,hostdata->issue_queue);
- SET_NEXT(cmd, hostdata->issue_queue);
- hostdata->issue_queue = (struct scsi_cmnd *) cmd;
- local_irq_restore(flags);
- dprintk(NDEBUG_QUEUES, "scsi%d: REQUEST SENSE added to head of "
- "issue queue\n", H_NO(cmd));
- } else
-#endif /* def AUTOSENSE */
- {
-#ifdef NCR5380_STATS
- collect_stats(hostdata, cmd);
-#endif
- cmd->scsi_done(cmd);
- }
-
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- /*
- * Restore phase bits to 0 so an interrupted selection,
- * arbitration can resume.
- */
- NCR5380_write(TARGET_COMMAND_REG, 0);
-
- while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
- barrier();
-
- return;
- case MESSAGE_REJECT:
- /* Accept message by clearing ACK */
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- /* Enable reselect interrupts */
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- switch (hostdata->last_message) {
- case HEAD_OF_QUEUE_TAG:
- case ORDERED_QUEUE_TAG:
- case SIMPLE_QUEUE_TAG:
- /* The target obviously doesn't support tagged
- * queuing, even though it announced this ability in
- * its INQUIRY data ?!? (maybe only this LUN?) Ok,
- * clear 'tagged_supported' and lock the LUN, since
- * the command is treated as untagged further on.
- */
- cmd->device->tagged_supported = 0;
- hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun);
- cmd->tag = TAG_NONE;
- dprintk(NDEBUG_TAGS, "scsi%d: target %d lun %llu rejected "
- "QUEUE_TAG message; tagged queuing "
- "disabled\n",
- HOSTNO, cmd->device->id, cmd->device->lun);
- break;
- }
- break;
- case DISCONNECT:
- /* Accept message by clearing ACK */
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- local_irq_save(flags);
- cmd->device->disconnect = 1;
- LIST(cmd,hostdata->disconnected_queue);
- SET_NEXT(cmd, hostdata->disconnected_queue);
- hostdata->connected = NULL;
- hostdata->disconnected_queue = cmd;
- local_irq_restore(flags);
- dprintk(NDEBUG_QUEUES, "scsi%d: command for target %d lun %llu was "
- "moved from connected to the "
- "disconnected_queue\n", HOSTNO,
- cmd->device->id, cmd->device->lun);
- /*
- * Restore phase bits to 0 so an interrupted selection,
- * arbitration can resume.
- */
- NCR5380_write(TARGET_COMMAND_REG, 0);
-
- /* Enable reselect interrupts */
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- /* Wait for bus free to avoid nasty timeouts */
- while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
- barrier();
-#ifdef SUN3_SCSI_VME
- dregs->csr |= CSR_DMA_ENABLE;
-#endif
- return;
- /*
- * The SCSI data pointer is *IMPLICITLY* saved on a disconnect
- * operation, in violation of the SCSI spec so we can safely
- * ignore SAVE/RESTORE pointers calls.
- *
- * Unfortunately, some disks violate the SCSI spec and
- * don't issue the required SAVE_POINTERS message before
- * disconnecting, and we have to break spec to remain
- * compatible.
- */
- case SAVE_POINTERS:
- case RESTORE_POINTERS:
- /* Accept message by clearing ACK */
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- /* Enable reselect interrupts */
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- break;
- case EXTENDED_MESSAGE:
-/*
- * Extended messages are sent in the following format :
- * Byte
- * 0 EXTENDED_MESSAGE == 1
- * 1 length (includes one byte for code, doesn't
- * include first two bytes)
- * 2 code
- * 3..length+1 arguments
- *
- * Start the extended message buffer with the EXTENDED_MESSAGE
- * byte, since spi_print_msg() wants the whole thing.
- */
- extended_msg[0] = EXTENDED_MESSAGE;
- /* Accept first byte by clearing ACK */
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-
- dprintk(NDEBUG_EXTENDED, "scsi%d: receiving extended message\n", HOSTNO);
-
- len = 2;
- data = extended_msg + 1;
- phase = PHASE_MSGIN;
- NCR5380_transfer_pio(instance, &phase, &len, &data);
- dprintk(NDEBUG_EXTENDED, "scsi%d: length=%d, code=0x%02x\n", HOSTNO,
- (int)extended_msg[1], (int)extended_msg[2]);
-
- if (!len && extended_msg[1] <=
- (sizeof (extended_msg) - 1)) {
- /* Accept third byte by clearing ACK */
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
- len = extended_msg[1] - 1;
- data = extended_msg + 3;
- phase = PHASE_MSGIN;
-
- NCR5380_transfer_pio(instance, &phase, &len, &data);
- dprintk(NDEBUG_EXTENDED, "scsi%d: message received, residual %d\n",
- HOSTNO, len);
-
- switch (extended_msg[2]) {
- case EXTENDED_SDTR:
- case EXTENDED_WDTR:
- case EXTENDED_MODIFY_DATA_POINTER:
- case EXTENDED_EXTENDED_IDENTIFY:
- tmp = 0;
- }
- } else if (len) {
- printk(KERN_NOTICE "scsi%d: error receiving "
- "extended message\n", HOSTNO);
- tmp = 0;
- } else {
- printk(KERN_NOTICE "scsi%d: extended message "
- "code %02x length %d is too long\n",
- HOSTNO, extended_msg[2], extended_msg[1]);
- tmp = 0;
- }
- /* Fall through to reject message */
-
- /*
- * If we get something weird that we aren't expecting,
- * reject it.
- */
- default:
- if (!tmp) {
- printk(KERN_DEBUG "scsi%d: rejecting message ", HOSTNO);
- spi_print_msg(extended_msg);
- printk("\n");
- } else if (tmp != EXTENDED_MESSAGE)
- printk(KERN_DEBUG "scsi%d: rejecting unknown "
- "message %02x from target %d, lun %llu\n",
- HOSTNO, tmp, cmd->device->id, cmd->device->lun);
- else
- printk(KERN_DEBUG "scsi%d: rejecting unknown "
- "extended message "
- "code %02x, length %d from target %d, lun %llu\n",
- HOSTNO, extended_msg[1], extended_msg[0],
- cmd->device->id, cmd->device->lun);
-
-
- msgout = MESSAGE_REJECT;
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
- ICR_ASSERT_ATN);
- break;
- } /* switch (tmp) */
- break;
- case PHASE_MSGOUT:
- len = 1;
- data = &msgout;
- hostdata->last_message = msgout;
- NCR5380_transfer_pio(instance, &phase, &len, &data);
- if (msgout == ABORT) {
-#ifdef SUPPORT_TAGS
- cmd_free_tag( cmd );
-#else
- hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
-#endif
- hostdata->connected = NULL;
- cmd->result = DID_ERROR << 16;
-#ifdef NCR5380_STATS
- collect_stats(hostdata, cmd);
-#endif
- cmd->scsi_done(cmd);
- NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
- return;
- }
- msgout = NOP;
- break;
- case PHASE_CMDOUT:
- len = cmd->cmd_len;
- data = cmd->cmnd;
- /*
- * XXX for performance reasons, on machines with a
- * PSEUDO-DMA architecture we should probably
- * use the dma transfer function.
- */
- NCR5380_transfer_pio(instance, &phase, &len,
- &data);
- break;
- case PHASE_STATIN:
- len = 1;
- data = &tmp;
- NCR5380_transfer_pio(instance, &phase, &len, &data);
- cmd->SCp.Status = tmp;
- break;
- default:
- printk("scsi%d: unknown phase\n", HOSTNO);
- NCR5380_dprint(NDEBUG_ANY, instance);
- } /* switch(phase) */
- } /* if (tmp * SR_REQ) */
- } /* while (1) */
-}
-
-/*
- * Function : void NCR5380_reselect (struct Scsi_Host *instance)
- *
- * Purpose : does reselection, initializing the instance->connected
- * field to point to the struct scsi_cmnd for which the I_T_L or I_T_L_Q
- * nexus has been reestablished,
- *
- * Inputs : instance - this instance of the NCR5380.
- *
- */
-
-/* it might eventually prove necessary to do a dma setup on
- reselection, but it doesn't seem to be needed now -- sam */
-
-static void NCR5380_reselect (struct Scsi_Host *instance)
-{
- SETUP_HOSTDATA(instance);
- unsigned char target_mask;
- unsigned char lun;
-#ifdef SUPPORT_TAGS
- unsigned char tag;
-#endif
- unsigned char msg[3];
- struct scsi_cmnd *tmp = NULL, *prev;
-/* unsigned long flags; */
-
- /*
- * Disable arbitration, etc. since the host adapter obviously
- * lost, and tell an interrupted NCR5380_select() to restart.
- */
-
- NCR5380_write(MODE_REG, MR_BASE);
- hostdata->restart_select = 1;
-
- target_mask = NCR5380_read(CURRENT_SCSI_DATA_REG) & ~(hostdata->id_mask);
-
- dprintk(NDEBUG_RESELECTION, "scsi%d: reselect\n", HOSTNO);
-
- /*
- * At this point, we have detected that our SCSI ID is on the bus,
- * SEL is true and BSY was false for at least one bus settle delay
- * (400 ns).
- *
- * We must assert BSY ourselves, until the target drops the SEL
- * signal.
- */
-
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_BSY);
-
- while (NCR5380_read(STATUS_REG) & SR_SEL);
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-
- /*
- * Wait for target to go into MSGIN.
- */
-
- while (!(NCR5380_read(STATUS_REG) & SR_REQ));
-
-#if 1
- // acknowledge toggle to MSGIN
- NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(PHASE_MSGIN));
-
- // peek at the byte without really hitting the bus
- msg[0] = NCR5380_read(CURRENT_SCSI_DATA_REG);
-#endif
-
- if (!(msg[0] & 0x80)) {
- printk(KERN_DEBUG "scsi%d: expecting IDENTIFY message, got ", HOSTNO);
- spi_print_msg(msg);
- do_abort(instance);
- return;
- }
- lun = (msg[0] & 0x07);
-
- /*
- * Find the command corresponding to the I_T_L or I_T_L_Q nexus we
- * just reestablished, and remove it from the disconnected queue.
- */
-
- for (tmp = (struct scsi_cmnd *) hostdata->disconnected_queue, prev = NULL;
- tmp; prev = tmp, tmp = NEXT(tmp) ) {
- if ((target_mask == (1 << tmp->device->id)) && (lun == tmp->device->lun)
-#ifdef SUPPORT_TAGS
- && (tag == tmp->tag)
-#endif
- ) {
- if (prev) {
- REMOVE(prev, NEXT(prev), tmp, NEXT(tmp));
- SET_NEXT(prev, NEXT(tmp));
- } else {
- REMOVE(-1, hostdata->disconnected_queue, tmp, NEXT(tmp));
- hostdata->disconnected_queue = NEXT(tmp);
- }
- SET_NEXT(tmp, NULL);
- break;
- }
- }
-
- if (!tmp) {
- printk(KERN_WARNING "scsi%d: warning: target bitmask %02x lun %d "
-#ifdef SUPPORT_TAGS
- "tag %d "
-#endif
- "not in disconnected_queue.\n",
- HOSTNO, target_mask, lun
-#ifdef SUPPORT_TAGS
- , tag
-#endif
- );
- /*
- * Since we have an established nexus that we can't do anything
- * with, we must abort it.
- */
- do_abort(instance);
- return;
- }
-#if 1
- /* engage dma setup for the command we just saw */
- {
- void *d;
- unsigned long count;
-
- if (!tmp->SCp.this_residual && tmp->SCp.buffers_residual) {
- count = tmp->SCp.buffer->length;
- d = SGADDR(tmp->SCp.buffer);
- } else {
- count = tmp->SCp.this_residual;
- d = tmp->SCp.ptr;
- }
-#ifdef REAL_DMA
- /* setup this command for dma if not already */
- if((count > SUN3_DMA_MINSIZE) && (sun3_dma_setup_done != tmp))
- {
- sun3scsi_dma_setup(d, count, rq_data_dir(tmp->request));
- sun3_dma_setup_done = tmp;
- }
-#endif
- }
-#endif
-
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK);
- /* Accept message by clearing ACK */
- NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
-
-#ifdef SUPPORT_TAGS
- /* If the phase is still MSGIN, the target wants to send some more
- * messages. In case it supports tagged queuing, this is probably a
- * SIMPLE_QUEUE_TAG for the I_T_L_Q nexus.
- */
- tag = TAG_NONE;
- if (phase == PHASE_MSGIN && setup_use_tagged_queuing) {
- /* Accept previous IDENTIFY message by clearing ACK */
- NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE );
- len = 2;
- data = msg+1;
- if (!NCR5380_transfer_pio(instance, &phase, &len, &data) &&
- msg[1] == SIMPLE_QUEUE_TAG)
- tag = msg[2];
- dprintk(NDEBUG_TAGS, "scsi%d: target mask %02x, lun %d sent tag %d at "
- "reselection\n", HOSTNO, target_mask, lun, tag);
- }
-#endif
-
- hostdata->connected = tmp;
- dprintk(NDEBUG_RESELECTION, "scsi%d: nexus established, target = %d, lun = %llu, tag = %d\n",
- HOSTNO, tmp->device->id, tmp->device->lun, tmp->tag);
-}
-
-
-/*
- * Function : int NCR5380_abort(struct scsi_cmnd *cmd)
- *
- * Purpose : abort a command
- *
- * Inputs : cmd - the struct scsi_cmnd to abort, code - code to set the
- * host byte of the result field to, if zero DID_ABORTED is
- * used.
- *
- * Returns : 0 - success, -1 on failure.
- *
- * XXX - there is no way to abort the command that is currently
- * connected, you have to wait for it to complete. If this is
- * a problem, we could implement longjmp() / setjmp(), setjmp()
- * called where the loop started in NCR5380_main().
- */
-
-static int NCR5380_abort(struct scsi_cmnd *cmd)
-{
- struct Scsi_Host *instance = cmd->device->host;
- SETUP_HOSTDATA(instance);
- struct scsi_cmnd *tmp, **prev;
- unsigned long flags;
-
- printk(KERN_NOTICE "scsi%d: aborting command\n", HOSTNO);
- scsi_print_command(cmd);
-
- NCR5380_print_status (instance);
-
- local_irq_save(flags);
-
- dprintk(NDEBUG_ABORT, "scsi%d: abort called basr 0x%02x, sr 0x%02x\n", HOSTNO,
- NCR5380_read(BUS_AND_STATUS_REG),
- NCR5380_read(STATUS_REG));
-
-#if 1
-/*
- * Case 1 : If the command is the currently executing command,
- * we'll set the aborted flag and return control so that
- * information transfer routine can exit cleanly.
- */
-
- if (hostdata->connected == cmd) {
-
- dprintk(NDEBUG_ABORT, "scsi%d: aborting connected command\n", HOSTNO);
-/*
- * We should perform BSY checking, and make sure we haven't slipped
- * into BUS FREE.
- */
-
-/* NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_ATN); */
-/*
- * Since we can't change phases until we've completed the current
- * handshake, we have to source or sink a byte of data if the current
- * phase is not MSGOUT.
- */
-
-/*
- * Return control to the executing NCR drive so we can clear the
- * aborted flag and get back into our main loop.
- */
-
- if (do_abort(instance) == 0) {
- hostdata->aborted = 1;
- hostdata->connected = NULL;
- cmd->result = DID_ABORT << 16;
-#ifdef SUPPORT_TAGS
- cmd_free_tag( cmd );
-#else
- hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
-#endif
- local_irq_restore(flags);
- cmd->scsi_done(cmd);
- return SUCCESS;
- } else {
-/* local_irq_restore(flags); */
- printk("scsi%d: abort of connected command failed!\n", HOSTNO);
- return FAILED;
- }
- }
-#endif
-
-/*
- * Case 2 : If the command hasn't been issued yet, we simply remove it
- * from the issue queue.
- */
- for (prev = (struct scsi_cmnd **) &(hostdata->issue_queue),
- tmp = (struct scsi_cmnd *) hostdata->issue_queue;
- tmp; prev = NEXTADDR(tmp), tmp = NEXT(tmp))
- if (cmd == tmp) {
- REMOVE(5, *prev, tmp, NEXT(tmp));
- (*prev) = NEXT(tmp);
- SET_NEXT(tmp, NULL);
- tmp->result = DID_ABORT << 16;
- local_irq_restore(flags);
- dprintk(NDEBUG_ABORT, "scsi%d: abort removed command from issue queue.\n",
- HOSTNO);
- /* Tagged queuing note: no tag to free here, hasn't been assigned
- * yet... */
- tmp->scsi_done(tmp);
- return SUCCESS;
- }
-
-/*
- * Case 3 : If any commands are connected, we're going to fail the abort
- * and let the high level SCSI driver retry at a later time or
- * issue a reset.
- *
- * Timeouts, and therefore aborted commands, will be highly unlikely
- * and handling them cleanly in this situation would make the common
- * case of noresets less efficient, and would pollute our code. So,
- * we fail.
- */
-
- if (hostdata->connected) {
- local_irq_restore(flags);
- dprintk(NDEBUG_ABORT, "scsi%d: abort failed, command connected.\n", HOSTNO);
- return FAILED;
- }
-
-/*
- * Case 4: If the command is currently disconnected from the bus, and
- * there are no connected commands, we reconnect the I_T_L or
- * I_T_L_Q nexus associated with it, go into message out, and send
- * an abort message.
- *
- * This case is especially ugly. In order to reestablish the nexus, we
- * need to call NCR5380_select(). The easiest way to implement this
- * function was to abort if the bus was busy, and let the interrupt
- * handler triggered on the SEL for reselect take care of lost arbitrations
- * where necessary, meaning interrupts need to be enabled.
- *
- * When interrupts are enabled, the queues may change - so we
- * can't remove it from the disconnected queue before selecting it
- * because that could cause a failure in hashing the nexus if that
- * device reselected.
- *
- * Since the queues may change, we can't use the pointers from when we
- * first locate it.
- *
- * So, we must first locate the command, and if NCR5380_select()
- * succeeds, then issue the abort, relocate the command and remove
- * it from the disconnected queue.
- */
-
- for (tmp = (struct scsi_cmnd *) hostdata->disconnected_queue; tmp;
- tmp = NEXT(tmp))
- if (cmd == tmp) {
- local_irq_restore(flags);
- dprintk(NDEBUG_ABORT, "scsi%d: aborting disconnected command.\n", HOSTNO);
-
- if (NCR5380_select (instance, cmd, (int) cmd->tag))
- return FAILED;
-
- dprintk(NDEBUG_ABORT, "scsi%d: nexus reestablished.\n", HOSTNO);
-
- do_abort (instance);
-
- local_irq_save(flags);
- for (prev = (struct scsi_cmnd **) &(hostdata->disconnected_queue),
- tmp = (struct scsi_cmnd *) hostdata->disconnected_queue;
- tmp; prev = NEXTADDR(tmp), tmp = NEXT(tmp) )
- if (cmd == tmp) {
- REMOVE(5, *prev, tmp, NEXT(tmp));
- *prev = NEXT(tmp);
- SET_NEXT(tmp, NULL);
- tmp->result = DID_ABORT << 16;
- /* We must unlock the tag/LUN immediately here, since the
- * target goes to BUS FREE and doesn't send us another
- * message (COMMAND_COMPLETE or the like)
- */
-#ifdef SUPPORT_TAGS
- cmd_free_tag( tmp );
-#else
- hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
-#endif
- local_irq_restore(flags);
- tmp->scsi_done(tmp);
- return SUCCESS;
- }
- }
-
-/*
- * Case 5 : If we reached this point, the command was not found in any of
- * the queues.
- *
- * We probably reached this point because of an unlikely race condition
- * between the command completing successfully and the abortion code,
- * so we won't panic, but we will notify the user in case something really
- * broke.
- */
-
- local_irq_restore(flags);
- printk(KERN_INFO "scsi%d: warning : SCSI command probably completed successfully before abortion\n", HOSTNO);
-
- return FAILED;
-}
-
-
-/*
- * Function : int NCR5380_bus_reset(struct scsi_cmnd *cmd)
- *
- * Purpose : reset the SCSI bus.
- *
- * Returns : SUCCESS or FAILURE
- *
- */
-
-static int NCR5380_bus_reset(struct scsi_cmnd *cmd)
-{
- SETUP_HOSTDATA(cmd->device->host);
- int i;
- unsigned long flags;
-#if defined(RESET_RUN_DONE)
- struct scsi_cmnd *connected, *disconnected_queue;
-#endif
-
-
- NCR5380_print_status (cmd->device->host);
-
- /* get in phase */
- NCR5380_write( TARGET_COMMAND_REG,
- PHASE_SR_TO_TCR( NCR5380_read(STATUS_REG) ));
- /* assert RST */
- NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST );
- udelay (40);
- /* reset NCR registers */
- NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE );
- NCR5380_write( MODE_REG, MR_BASE );
- NCR5380_write( TARGET_COMMAND_REG, 0 );
- NCR5380_write( SELECT_ENABLE_REG, 0 );
- /* ++roman: reset interrupt condition! otherwise no interrupts don't get
- * through anymore ... */
- (void)NCR5380_read( RESET_PARITY_INTERRUPT_REG );
-
- /* MSch 20140115 - looking at the generic NCR5380 driver, all of this
- * should go.
- * Catch-22: if we don't clear all queues, the SCSI driver lock will
- * not be released by atari_scsi_reset()!
- */
-
-#if defined(RESET_RUN_DONE)
- /* XXX Should now be done by midlevel code, but it's broken XXX */
- /* XXX see below XXX */
-
- /* MSch: old-style reset: actually abort all command processing here */
-
- /* After the reset, there are no more connected or disconnected commands
- * and no busy units; to avoid problems with re-inserting the commands
- * into the issue_queue (via scsi_done()), the aborted commands are
- * remembered in local variables first.
- */
- local_irq_save(flags);
- connected = (struct scsi_cmnd *)hostdata->connected;
- hostdata->connected = NULL;
- disconnected_queue = (struct scsi_cmnd *)hostdata->disconnected_queue;
- hostdata->disconnected_queue = NULL;
-#ifdef SUPPORT_TAGS
- free_all_tags();
-#endif
- for( i = 0; i < 8; ++i )
- hostdata->busy[i] = 0;
-#ifdef REAL_DMA
- hostdata->dma_len = 0;
-#endif
- local_irq_restore(flags);
-
- /* In order to tell the mid-level code which commands were aborted,
- * set the command status to DID_RESET and call scsi_done() !!!
- * This ultimately aborts processing of these commands in the mid-level.
- */
-
- if ((cmd = connected)) {
- dprintk(NDEBUG_ABORT, "scsi%d: reset aborted a connected command\n", H_NO(cmd));
- cmd->result = (cmd->result & 0xffff) | (DID_RESET << 16);
- cmd->scsi_done( cmd );
- }
-
- for (i = 0; (cmd = disconnected_queue); ++i) {
- disconnected_queue = NEXT(cmd);
- SET_NEXT(cmd, NULL);
- cmd->result = (cmd->result & 0xffff) | (DID_RESET << 16);
- cmd->scsi_done( cmd );
- }
- if (i > 0)
- dprintk(NDEBUG_ABORT, "scsi: reset aborted %d disconnected command(s)\n", i);
-
-
- /* since all commands have been explicitly terminated, we need to tell
- * the midlevel code that the reset was SUCCESSFUL, and there is no
- * need to 'wake up' the commands by a request_sense
- */
- return SUCCESS;
-#else /* 1 */
-
- /* MSch: new-style reset handling: let the mid-level do what it can */
-
- /* ++guenther: MID-LEVEL IS STILL BROKEN.
- * Mid-level is supposed to requeue all commands that were active on the
- * various low-level queues. In fact it does this, but that's not enough
- * because all these commands are subject to timeout. And if a timeout
- * happens for any removed command, *_abort() is called but all queues
- * are now empty. Abort then gives up the falcon lock, which is fatal,
- * since the mid-level will queue more commands and must have the lock
- * (it's all happening inside timer interrupt handler!!).
- * Even worse, abort will return NOT_RUNNING for all those commands not
- * on any queue, so they won't be retried ...
- *
- * Conclusion: either scsi.c disables timeout for all resetted commands
- * immediately, or we lose! As of linux-2.0.20 it doesn't.
- */
-
- /* After the reset, there are no more connected or disconnected commands
- * and no busy units; so clear the low-level status here to avoid
- * conflicts when the mid-level code tries to wake up the affected
- * commands!
- */
-
- if (hostdata->issue_queue)
- dprintk(NDEBUG_ABORT, "scsi%d: reset aborted issued command(s)\n", H_NO(cmd));
- if (hostdata->connected)
- dprintk(NDEBUG_ABORT, "scsi%d: reset aborted a connected command\n", H_NO(cmd));
- if (hostdata->disconnected_queue)
- dprintk(NDEBUG_ABORT, "scsi%d: reset aborted disconnected command(s)\n", H_NO(cmd));
-
- local_irq_save(flags);
- hostdata->issue_queue = NULL;
- hostdata->connected = NULL;
- hostdata->disconnected_queue = NULL;
-#ifdef SUPPORT_TAGS
- free_all_tags();
-#endif
- for( i = 0; i < 8; ++i )
- hostdata->busy[i] = 0;
-#ifdef REAL_DMA
- hostdata->dma_len = 0;
-#endif
- local_irq_restore(flags);
-
- /* we did no complete reset of all commands, so a wakeup is required */
- return SUCCESS;
-#endif /* 1 */
-}
-
-/* Local Variables: */
-/* tab-width: 8 */
-/* End: */
* Generic Generic NCR5380 driver
*
* Copyright 1995, Russell King
- *
- * ALPHA RELEASE 1.
- *
- * For more information, please consult
- *
- * NCR 5380 Family
- * SCSI Protocol Controller
- * Databook
- *
- * NCR Microelectronics
- * 1635 Aeroplaza Drive
- * Colorado Springs, CO 80916
- * 1+ (719) 578-3400
- * 1+ (800) 334-5454
*/
-
-/*
- * This is from mac_scsi.h, but hey, maybe this is useful for Sun3 too! :)
- *
- * Options :
- *
- * PARITY - enable parity checking. Not supported.
- *
- * SCSI2 - enable support for SCSI-II tagged queueing. Untested.
- *
- * USLEEP - enable support for devices that don't disconnect. Untested.
- */
-
-#define AUTOSENSE
-
#include <linux/types.h>
-#include <linux/stddef.h>
-#include <linux/ctype.h>
#include <linux/delay.h>
-
#include <linux/module.h>
-#include <linux/signal.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/blkdev.h>
+#include <linux/platform_device.h>
#include <asm/io.h>
-
-#include <asm/sun3ints.h>
#include <asm/dvma.h>
-#include <asm/idprom.h>
-#include <asm/machines.h>
-/* dma on! */
-#define REAL_DMA
-
-#include "scsi.h"
#include <scsi/scsi_host.h>
#include "sun3_scsi.h"
-#include "NCR5380.h"
-extern int sun3_map_test(unsigned long, char *);
+/* Definitions for the core NCR5380 driver. */
-#define USE_WRAPPER
-/*#define RESET_BOOT */
-#define DRIVER_SETUP
+#define REAL_DMA
+/* #define SUPPORT_TAGS */
+/* minimum number of bytes to do dma on */
+#define DMA_MIN_SIZE 129
-/*
- * BUG can be used to trigger a strange code-size related hang on 2.1 kernels
- */
-#ifdef BUG
-#undef RESET_BOOT
-#undef DRIVER_SETUP
-#endif
+/* #define MAX_TAGS 32 */
-/* #define SUPPORT_TAGS */
+#define NCR5380_implementation_fields /* none */
-#ifdef SUN3_SCSI_VME
-#define ENABLE_IRQ()
-#else
-#define ENABLE_IRQ() enable_irq( IRQ_SUN3_SCSI );
-#endif
+#define NCR5380_read(reg) sun3scsi_read(reg)
+#define NCR5380_write(reg, value) sun3scsi_write(reg, value)
+
+#define NCR5380_queue_command sun3scsi_queue_command
+#define NCR5380_bus_reset sun3scsi_bus_reset
+#define NCR5380_abort sun3scsi_abort
+#define NCR5380_show_info sun3scsi_show_info
+#define NCR5380_info sun3scsi_info
+#define NCR5380_dma_read_setup(instance, data, count) \
+ sun3scsi_dma_setup(data, count, 0)
+#define NCR5380_dma_write_setup(instance, data, count) \
+ sun3scsi_dma_setup(data, count, 1)
+#define NCR5380_dma_residual(instance) \
+ sun3scsi_dma_residual(instance)
+#define NCR5380_dma_xfer_len(instance, cmd, phase) \
+ sun3scsi_dma_xfer_len(cmd->SCp.this_residual, cmd, !((phase) & SR_IO))
-static irqreturn_t scsi_sun3_intr(int irq, void *dummy);
-static inline unsigned char sun3scsi_read(int reg);
-static inline void sun3scsi_write(int reg, int value);
+#define NCR5380_acquire_dma_irq(instance) (1)
+#define NCR5380_release_dma_irq(instance)
+
+#include "NCR5380.h"
+
+
+extern int sun3_map_test(unsigned long, char *);
static int setup_can_queue = -1;
module_param(setup_can_queue, int, 0);
static int setup_hostid = -1;
module_param(setup_hostid, int, 0);
-static struct scsi_cmnd *sun3_dma_setup_done = NULL;
-
-#define RESET_RUN_DONE
+/* #define RESET_BOOT */
#define AFTER_RESET_DELAY (HZ/2)
/* dvma buffer to allocate -- 32k should hopefully be more than sufficient */
#define SUN3_DVMA_BUFSIZE 0xe000
-/* minimum number of bytes to do dma on */
-#define SUN3_DMA_MINSIZE 128
-
-static volatile unsigned char *sun3_scsi_regp;
+static struct scsi_cmnd *sun3_dma_setup_done;
+static unsigned char *sun3_scsi_regp;
static volatile struct sun3_dma_regs *dregs;
-#ifndef SUN3_SCSI_VME
-static struct sun3_udc_regs *udc_regs = NULL;
-#endif
+static struct sun3_udc_regs *udc_regs;
static unsigned char *sun3_dma_orig_addr = NULL;
static unsigned long sun3_dma_orig_count = 0;
static int sun3_dma_active = 0;
static unsigned long last_residual = 0;
+static struct Scsi_Host *default_instance;
/*
* NCR 5380 register access functions
static inline unsigned char sun3scsi_read(int reg)
{
- return( sun3_scsi_regp[reg] );
+ return in_8(sun3_scsi_regp + reg);
}
static inline void sun3scsi_write(int reg, int value)
{
- sun3_scsi_regp[reg] = value;
+ out_8(sun3_scsi_regp + reg, value);
}
#ifndef SUN3_SCSI_VME
}
#endif
-/*
- * XXX: status debug
- */
-static struct Scsi_Host *default_instance;
-
-/*
- * Function : int sun3scsi_detect(struct scsi_host_template * tpnt)
- *
- * Purpose : initializes mac NCR5380 driver based on the
- * command line / compile time port and irq definitions.
- *
- * Inputs : tpnt - template for this SCSI adapter.
- *
- * Returns : 1 if a host adapter was found, 0 if not.
- *
- */
-
-static int __init sun3scsi_detect(struct scsi_host_template *tpnt)
-{
- unsigned long ioaddr, irq;
- static int called = 0;
- struct Scsi_Host *instance;
-#ifdef SUN3_SCSI_VME
- int i;
- unsigned long addrs[3] = { IOBASE_SUN3_VMESCSI,
- IOBASE_SUN3_VMESCSI + 0x4000,
- 0 };
- unsigned long vecs[3] = { SUN3_VEC_VMESCSI0,
- SUN3_VEC_VMESCSI1,
- 0 };
-#endif
-
- /* check that this machine has an onboard 5380 */
- switch(idprom->id_machtype) {
-#ifdef SUN3_SCSI_VME
- case SM_SUN3|SM_3_160:
- case SM_SUN3|SM_3_260:
- break;
-#else
- case SM_SUN3|SM_3_50:
- case SM_SUN3|SM_3_60:
- break;
-#endif
-
- default:
- return 0;
- }
-
- if(called)
- return 0;
-
-#ifdef SUN3_SCSI_VME
- tpnt->proc_name = "Sun3 5380 VME SCSI";
-#else
- tpnt->proc_name = "Sun3 5380 SCSI";
-#endif
-
- /* setup variables */
- tpnt->can_queue =
- (setup_can_queue > 0) ? setup_can_queue : CAN_QUEUE;
- tpnt->cmd_per_lun =
- (setup_cmd_per_lun > 0) ? setup_cmd_per_lun : CMD_PER_LUN;
- tpnt->sg_tablesize =
- (setup_sg_tablesize >= 0) ? setup_sg_tablesize : SG_TABLESIZE;
-
- if (setup_hostid >= 0)
- tpnt->this_id = setup_hostid;
- else {
- /* use 7 as default */
- tpnt->this_id = 7;
- }
-
-#ifdef SUN3_SCSI_VME
- ioaddr = 0;
- for (i = 0; addrs[i] != 0; i++) {
- unsigned char x;
-
- ioaddr = (unsigned long)sun3_ioremap(addrs[i], PAGE_SIZE,
- SUN3_PAGE_TYPE_VME16);
- irq = vecs[i];
- sun3_scsi_regp = (unsigned char *)ioaddr;
-
- dregs = (struct sun3_dma_regs *)(((unsigned char *)ioaddr) + 8);
-
- if (sun3_map_test((unsigned long)dregs, &x)) {
- unsigned short oldcsr;
-
- oldcsr = dregs->csr;
- dregs->csr = 0;
- udelay(SUN3_DMA_DELAY);
- if (dregs->csr == 0x1400)
- break;
-
- dregs->csr = oldcsr;
- }
-
- iounmap((void *)ioaddr);
- ioaddr = 0;
- }
-
- if (!ioaddr)
- return 0;
-#else
- irq = IRQ_SUN3_SCSI;
- ioaddr = (unsigned long)ioremap(IOBASE_SUN3_SCSI, PAGE_SIZE);
- sun3_scsi_regp = (unsigned char *)ioaddr;
-
- dregs = (struct sun3_dma_regs *)(((unsigned char *)ioaddr) + 8);
-
- if((udc_regs = dvma_malloc(sizeof(struct sun3_udc_regs)))
- == NULL) {
- printk("SUN3 Scsi couldn't allocate DVMA memory!\n");
- return 0;
- }
-#endif
-#ifdef SUPPORT_TAGS
- if (setup_use_tagged_queuing < 0)
- setup_use_tagged_queuing = USE_TAGGED_QUEUING;
-#endif
-
- instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
- if(instance == NULL)
- return 0;
-
- default_instance = instance;
-
- instance->io_port = (unsigned long) ioaddr;
- instance->irq = irq;
-
- NCR5380_init(instance, 0);
-
- instance->n_io_port = 32;
-
- ((struct NCR5380_hostdata *)instance->hostdata)->ctrl = 0;
-
- if (request_irq(instance->irq, scsi_sun3_intr,
- 0, "Sun3SCSI-5380", instance)) {
-#ifndef REAL_DMA
- printk("scsi%d: IRQ%d not free, interrupts disabled\n",
- instance->host_no, instance->irq);
- instance->irq = SCSI_IRQ_NONE;
-#else
- printk("scsi%d: IRQ%d not free, bailing out\n",
- instance->host_no, instance->irq);
- return 0;
-#endif
- }
-
- pr_info("scsi%d: %s at port %lX irq", instance->host_no,
- tpnt->proc_name, instance->io_port);
- if (instance->irq == SCSI_IRQ_NONE)
- printk ("s disabled");
- else
- printk (" %d", instance->irq);
- printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
- instance->can_queue, instance->cmd_per_lun,
- SUN3SCSI_PUBLIC_RELEASE);
- printk("\nscsi%d:", instance->host_no);
- NCR5380_print_options(instance);
- printk("\n");
-
- dregs->csr = 0;
- udelay(SUN3_DMA_DELAY);
- dregs->csr = CSR_SCSI | CSR_FIFO | CSR_INTR;
- udelay(SUN3_DMA_DELAY);
- dregs->fifo_count = 0;
-#ifdef SUN3_SCSI_VME
- dregs->fifo_count_hi = 0;
- dregs->dma_addr_hi = 0;
- dregs->dma_addr_lo = 0;
- dregs->dma_count_hi = 0;
- dregs->dma_count_lo = 0;
-
- dregs->ivect = VME_DATA24 | (instance->irq & 0xff);
-#endif
-
- called = 1;
-
-#ifdef RESET_BOOT
- sun3_scsi_reset_boot(instance);
-#endif
-
- return 1;
-}
-
-int sun3scsi_release (struct Scsi_Host *shpnt)
-{
- if (shpnt->irq != SCSI_IRQ_NONE)
- free_irq(shpnt->irq, shpnt);
-
- iounmap((void *)sun3_scsi_regp);
-
- NCR5380_exit(shpnt);
- return 0;
-}
-
#ifdef RESET_BOOT
-/*
- * Our 'bus reset on boot' function
- */
-
static void sun3_scsi_reset_boot(struct Scsi_Host *instance)
{
unsigned long end;
-
- NCR5380_local_declare();
- NCR5380_setup(instance);
/*
* Do a SCSI reset to clean up the bus during initialization. No
}
#endif
-static const char *sun3scsi_info(struct Scsi_Host *spnt)
-{
- return "";
-}
-
// safe bits for the CSR
#define CSR_GOOD 0x060f
void sun3_sun3_debug (void)
{
unsigned long flags;
- NCR5380_local_declare();
if (default_instance) {
local_irq_save(flags);
}
-#include "sun3_NCR5380.c"
+#include "atari_NCR5380.c"
-static struct scsi_host_template driver_template = {
+#ifdef SUN3_SCSI_VME
+#define SUN3_SCSI_NAME "Sun3 NCR5380 VME SCSI"
+#define DRV_MODULE_NAME "sun3_scsi_vme"
+#else
+#define SUN3_SCSI_NAME "Sun3 NCR5380 SCSI"
+#define DRV_MODULE_NAME "sun3_scsi"
+#endif
+
+#define PFX DRV_MODULE_NAME ": "
+
+static struct scsi_host_template sun3_scsi_template = {
+ .module = THIS_MODULE,
+ .proc_name = DRV_MODULE_NAME,
.show_info = sun3scsi_show_info,
.name = SUN3_SCSI_NAME,
- .detect = sun3scsi_detect,
- .release = sun3scsi_release,
.info = sun3scsi_info,
.queuecommand = sun3scsi_queue_command,
.eh_abort_handler = sun3scsi_abort,
.eh_bus_reset_handler = sun3scsi_bus_reset,
- .can_queue = CAN_QUEUE,
+ .can_queue = 16,
.this_id = 7,
- .sg_tablesize = SG_TABLESIZE,
- .cmd_per_lun = CMD_PER_LUN,
+ .sg_tablesize = SG_NONE,
+ .cmd_per_lun = 2,
.use_clustering = DISABLE_CLUSTERING
};
+static int __init sun3_scsi_probe(struct platform_device *pdev)
+{
+ struct Scsi_Host *instance;
+ int error;
+ struct resource *irq, *mem;
+ unsigned char *ioaddr;
+ int host_flags = 0;
+#ifdef SUN3_SCSI_VME
+ int i;
+#endif
+
+ if (setup_can_queue > 0)
+ sun3_scsi_template.can_queue = setup_can_queue;
+ if (setup_cmd_per_lun > 0)
+ sun3_scsi_template.cmd_per_lun = setup_cmd_per_lun;
+ if (setup_sg_tablesize >= 0)
+ sun3_scsi_template.sg_tablesize = setup_sg_tablesize;
+ if (setup_hostid >= 0)
+ sun3_scsi_template.this_id = setup_hostid & 7;
+
+#ifdef SUN3_SCSI_VME
+ ioaddr = NULL;
+ for (i = 0; i < 2; i++) {
+ unsigned char x;
+
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, i);
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ if (!irq || !mem)
+ break;
+
+ ioaddr = sun3_ioremap(mem->start, resource_size(mem),
+ SUN3_PAGE_TYPE_VME16);
+ dregs = (struct sun3_dma_regs *)(ioaddr + 8);
+
+ if (sun3_map_test((unsigned long)dregs, &x)) {
+ unsigned short oldcsr;
+
+ oldcsr = dregs->csr;
+ dregs->csr = 0;
+ udelay(SUN3_DMA_DELAY);
+ if (dregs->csr == 0x1400)
+ break;
+
+ dregs->csr = oldcsr;
+ }
+
+ iounmap(ioaddr);
+ ioaddr = NULL;
+ }
+ if (!ioaddr)
+ return -ENODEV;
+#else
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!irq || !mem)
+ return -ENODEV;
+
+ ioaddr = ioremap(mem->start, resource_size(mem));
+ dregs = (struct sun3_dma_regs *)(ioaddr + 8);
+
+ udc_regs = dvma_malloc(sizeof(struct sun3_udc_regs));
+ if (!udc_regs) {
+ pr_err(PFX "couldn't allocate DVMA memory!\n");
+ iounmap(ioaddr);
+ return -ENOMEM;
+ }
+#endif
+
+ sun3_scsi_regp = ioaddr;
+
+ instance = scsi_host_alloc(&sun3_scsi_template,
+ sizeof(struct NCR5380_hostdata));
+ if (!instance) {
+ error = -ENOMEM;
+ goto fail_alloc;
+ }
+ default_instance = instance;
+
+ instance->io_port = (unsigned long)ioaddr;
+ instance->irq = irq->start;
+
+#ifdef SUPPORT_TAGS
+ host_flags |= setup_use_tagged_queuing > 0 ? FLAG_TAGGED_QUEUING : 0;
+#endif
+
+ NCR5380_init(instance, host_flags);
+
+ error = request_irq(instance->irq, scsi_sun3_intr, 0,
+ "NCR5380", instance);
+ if (error) {
+#ifdef REAL_DMA
+ pr_err(PFX "scsi%d: IRQ %d not free, bailing out\n",
+ instance->host_no, instance->irq);
+ goto fail_irq;
+#else
+ pr_warn(PFX "scsi%d: IRQ %d not free, interrupts disabled\n",
+ instance->host_no, instance->irq);
+ instance->irq = NO_IRQ;
+#endif
+ }
+
+ dregs->csr = 0;
+ udelay(SUN3_DMA_DELAY);
+ dregs->csr = CSR_SCSI | CSR_FIFO | CSR_INTR;
+ udelay(SUN3_DMA_DELAY);
+ dregs->fifo_count = 0;
+#ifdef SUN3_SCSI_VME
+ dregs->fifo_count_hi = 0;
+ dregs->dma_addr_hi = 0;
+ dregs->dma_addr_lo = 0;
+ dregs->dma_count_hi = 0;
+ dregs->dma_count_lo = 0;
+
+ dregs->ivect = VME_DATA24 | (instance->irq & 0xff);
+#endif
+
+#ifdef RESET_BOOT
+ sun3_scsi_reset_boot(instance);
+#endif
+
+ error = scsi_add_host(instance, NULL);
+ if (error)
+ goto fail_host;
+
+ platform_set_drvdata(pdev, instance);
+
+ scsi_scan_host(instance);
+ return 0;
+
+fail_host:
+ if (instance->irq != NO_IRQ)
+ free_irq(instance->irq, instance);
+fail_irq:
+ NCR5380_exit(instance);
+ scsi_host_put(instance);
+fail_alloc:
+ if (udc_regs)
+ dvma_free(udc_regs);
+ iounmap(sun3_scsi_regp);
+ return error;
+}
+
+static int __exit sun3_scsi_remove(struct platform_device *pdev)
+{
+ struct Scsi_Host *instance = platform_get_drvdata(pdev);
+
+ scsi_remove_host(instance);
+ if (instance->irq != NO_IRQ)
+ free_irq(instance->irq, instance);
+ NCR5380_exit(instance);
+ scsi_host_put(instance);
+ if (udc_regs)
+ dvma_free(udc_regs);
+ iounmap(sun3_scsi_regp);
+ return 0;
+}
+
+static struct platform_driver sun3_scsi_driver = {
+ .remove = __exit_p(sun3_scsi_remove),
+ .driver = {
+ .name = DRV_MODULE_NAME,
+ .owner = THIS_MODULE,
+ },
+};
-#include "scsi_module.c"
+module_platform_driver_probe(sun3_scsi_driver, sun3_scsi_probe);
+MODULE_ALIAS("platform:" DRV_MODULE_NAME);
MODULE_LICENSE("GPL");
* (Unix and Linux consulting and custom programming)
* drew@colorado.edu
* +1 (303) 440-4894
- *
- * ALPHA RELEASE 1.
- *
- * For more information, please consult
- *
- * NCR 5380 Family
- * SCSI Protocol Controller
- * Databook
- *
- * NCR Microelectronics
- * 1635 Aeroplaza Drive
- * Colorado Springs, CO 80916
- * 1+ (719) 578-3400
- * 1+ (800) 334-5454
*/
#ifndef SUN3_SCSI_H
#define SUN3_SCSI_H
-#define SUN3SCSI_PUBLIC_RELEASE 1
-
-/*
- * Int: level 2 autovector
- * IO: type 1, base 0x00140000, 5 bits phys space: A<4..0>
- */
-#define IRQ_SUN3_SCSI 2
-#define IOBASE_SUN3_SCSI 0x00140000
-
-#define IOBASE_SUN3_VMESCSI 0xff200000
-
-static int sun3scsi_abort(struct scsi_cmnd *);
-static int sun3scsi_detect (struct scsi_host_template *);
-static const char *sun3scsi_info (struct Scsi_Host *);
-static int sun3scsi_bus_reset(struct scsi_cmnd *);
-static int sun3scsi_queue_command(struct Scsi_Host *, struct scsi_cmnd *);
-static int sun3scsi_release (struct Scsi_Host *);
-
-#ifndef CMD_PER_LUN
-#define CMD_PER_LUN 2
-#endif
-
-#ifndef CAN_QUEUE
-#define CAN_QUEUE 16
-#endif
-
-#ifndef SG_TABLESIZE
-#define SG_TABLESIZE SG_NONE
-#endif
-
-#ifndef MAX_TAGS
-#define MAX_TAGS 32
-#endif
-
-#ifndef USE_TAGGED_QUEUING
-#define USE_TAGGED_QUEUING 1
-#endif
-
-#include <scsi/scsicam.h>
-
-#ifdef SUN3_SCSI_VME
-#define SUN3_SCSI_NAME "Sun3 NCR5380 VME SCSI"
-#else
-#define SUN3_SCSI_NAME "Sun3 NCR5380 SCSI"
-#endif
-
-#define NCR5380_implementation_fields \
- int port, ctrl
-
-#define NCR5380_local_declare() \
- struct Scsi_Host *_instance
-
-#define NCR5380_setup(instance) \
- _instance = instance
-
-#define NCR5380_read(reg) sun3scsi_read(reg)
-#define NCR5380_write(reg, value) sun3scsi_write(reg, value)
-
-#define NCR5380_intr sun3scsi_intr
-#define NCR5380_queue_command sun3scsi_queue_command
-#define NCR5380_bus_reset sun3scsi_bus_reset
-#define NCR5380_abort sun3scsi_abort
-#define NCR5380_show_info sun3scsi_show_info
-#define NCR5380_dma_xfer_len(i, cmd, phase) \
- sun3scsi_dma_xfer_len(cmd->SCp.this_residual,cmd,((phase) & SR_IO) ? 0 : 1)
-
-#define NCR5380_dma_write_setup(instance, data, count) sun3scsi_dma_setup(data, count, 1)
-#define NCR5380_dma_read_setup(instance, data, count) sun3scsi_dma_setup(data, count, 0)
-#define NCR5380_dma_residual sun3scsi_dma_residual
-
/* additional registers - mainly DMA control regs */
/* these start at regbase + 8 -- directly after the NCR regs */
struct sun3_dma_regs {
if (reqtags > SYM_CONF_MAX_TAG)
reqtags = SYM_CONF_MAX_TAG;
depth_to_use = reqtags ? reqtags : 1;
- scsi_adjust_queue_depth(sdev,
- sdev->tagged_supported ? MSG_SIMPLE_TAG : 0,
- depth_to_use);
+ scsi_change_queue_depth(sdev, depth_to_use);
lp->s.scdev_depth = depth_to_use;
sym_tune_dev_queuing(tp, sdev->lun, reqtags);
-#define AUTOSENSE
#define PSEUDO_DMA
/*
* drew@colorado.edu
* +1 (303) 440-4894
*
- * DISTRIBUTION RELEASE 3.
- *
* For more information, please consult
*
* Trantor Systems, Ltd.
* 5415 Randall Place
* Fremont, CA 94538
* 1+ (415) 770-1400, FAX 1+ (415) 770-9910
- *
- * and
- *
- * NCR 5380 Family
- * SCSI Protocol Controller
- * Databook
- *
- * NCR Microelectronics
- * 1635 Aeroplaza Drive
- * Colorado Springs, CO 80916
- * 1+ (719) 578-3400
- * 1+ (800) 334-5454
*/
/*
- * Options :
- * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
- * for commands that return with a CHECK CONDITION status.
- *
- * PSEUDO_DMA - enables PSEUDO-DMA hardware, should give a 3-4X performance
- * increase compared to polled I/O.
- *
- * PARITY - enable parity checking. Not supported.
- *
- * SCSI2 - enable support for SCSI-II tagged queueing. Untested.
- *
- *
- * UNSAFE - leave interrupts enabled during pseudo-DMA transfers. You
- * only really want to use this if you're having a problem with
- * dropped characters during high speed communications, and even
- * then, you're going to be better off twiddling with transfersize.
- *
- * USLEEP - enable support for devices that don't disconnect. Untested.
- *
* The card is detected and initialized in one of several ways :
* 1. Autoprobe (default) - since the board is memory mapped,
* a BIOS signature is scanned for to locate the registers.
#include <linux/module.h>
#include <linux/delay.h>
-#include "scsi.h"
#include <scsi/scsi_host.h>
#include "t128.h"
#define AUTOPROBE_IRQ
#define NO_SIGNATURES ARRAY_SIZE(signatures)
+#ifndef MODULE
/*
* Function : t128_setup(char *str, int *ints)
*
*
*/
-void __init t128_setup(char *str, int *ints){
+static int __init t128_setup(char *str)
+{
static int commandline_current = 0;
int i;
+ int ints[10];
+
+ get_options(str, ARRAY_SIZE(ints), ints);
if (ints[0] != 2)
printk("t128_setup : usage t128=address,irq\n");
else
}
++commandline_current;
}
+ return 1;
}
+__setup("t128=", t128_setup);
+#endif
+
/*
* Function : int t128_detect(struct scsi_host_template * tpnt)
*
*
*/
-int __init t128_detect(struct scsi_host_template * tpnt){
+static int __init t128_detect(struct scsi_host_template *tpnt)
+{
static int current_override = 0, current_base = 0;
struct Scsi_Host *instance;
unsigned long base;
void __iomem *p;
int sig, count;
- tpnt->proc_name = "t128";
- tpnt->show_info = t128_show_info;
- tpnt->write_info = t128_write_info;
-
for (count = 0; current_override < NO_OVERRIDES; ++current_override) {
base = 0;
p = NULL;
else
instance->irq = NCR5380_probe_irq(instance, T128_IRQS);
- if (instance->irq != SCSI_IRQ_NONE)
+ /* Compatibility with documented NCR5380 kernel parameters */
+ if (instance->irq == 255)
+ instance->irq = NO_IRQ;
+
+ if (instance->irq != NO_IRQ)
if (request_irq(instance->irq, t128_intr, 0, "t128",
instance)) {
printk("scsi%d : IRQ%d not free, interrupts disabled\n",
instance->host_no, instance->irq);
- instance->irq = SCSI_IRQ_NONE;
+ instance->irq = NO_IRQ;
}
- if (instance->irq == SCSI_IRQ_NONE) {
+ if (instance->irq == NO_IRQ) {
printk("scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no);
printk("scsi%d : please jumper the board for a free IRQ.\n", instance->host_no);
}
printk("scsi%d : irq = %d\n", instance->host_no, instance->irq);
#endif
- printk("scsi%d : at 0x%08lx", instance->host_no, instance->base);
- if (instance->irq == SCSI_IRQ_NONE)
- printk (" interrupts disabled");
- else
- printk (" irq %d", instance->irq);
- printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
- CAN_QUEUE, CMD_PER_LUN, T128_PUBLIC_RELEASE);
- NCR5380_print_options(instance);
- printk("\n");
-
++current_override;
++count;
}
{
NCR5380_local_declare();
NCR5380_setup(shost);
- if (shost->irq)
+ if (shost->irq != NO_IRQ)
free_irq(shost->irq, shost);
NCR5380_exit(shost);
if (shost->io_port && shost->n_io_port)
* and matching the H_C_S coordinates to what DOS uses.
*/
-int t128_biosparam(struct scsi_device *sdev, struct block_device *bdev,
- sector_t capacity, int * ip)
+static int t128_biosparam(struct scsi_device *sdev, struct block_device *bdev,
+ sector_t capacity, int *ip)
{
ip[0] = 64;
ip[1] = 32;
.name = "Trantor T128/T128F/T228",
.detect = t128_detect,
.release = t128_release,
+ .proc_name = "t128",
+ .show_info = t128_show_info,
+ .write_info = t128_write_info,
+ .info = t128_info,
.queuecommand = t128_queue_command,
.eh_abort_handler = t128_abort,
.eh_bus_reset_handler = t128_bus_reset,
* drew@colorado.edu
* +1 (303) 440-4894
*
- * DISTRIBUTION RELEASE 3.
- *
* For more information, please consult
*
* Trantor Systems, Ltd.
* 5415 Randall Place
* Fremont, CA 94538
* 1+ (415) 770-1400, FAX 1+ (415) 770-9910
- *
- * and
- *
- * NCR 5380 Family
- * SCSI Protocol Controller
- * Databook
- *
- * NCR Microelectronics
- * 1635 Aeroplaza Drive
- * Colorado Springs, CO 80916
- * 1+ (719) 578-3400
- * 1+ (800) 334-5454
*/
#ifndef T128_H
#define T128_H
-#define T128_PUBLIC_RELEASE 3
-
#define TDEBUG 0
#define TDEBUG_INIT 0x1
#define TDEBUG_TRANSFER 0x2
#define T_DATA_REG_OFFSET 0x1e00 /* rw 512 bytes long */
#ifndef ASM
-static int t128_abort(struct scsi_cmnd *);
-static int t128_biosparam(struct scsi_device *, struct block_device *,
- sector_t, int*);
-static int t128_detect(struct scsi_host_template *);
-static int t128_queue_command(struct Scsi_Host *, struct scsi_cmnd *);
-static int t128_bus_reset(struct scsi_cmnd *);
#ifndef CMD_PER_LUN
#define CMD_PER_LUN 2
#define NCR5380_queue_command t128_queue_command
#define NCR5380_abort t128_abort
#define NCR5380_bus_reset t128_bus_reset
+#define NCR5380_info t128_info
#define NCR5380_show_info t128_show_info
#define NCR5380_write_info t128_write_info
+++ /dev/null
-/************************************************************************
- * FILE NAME : TMSCSIM.C *
- * BY : C.L. Huang, ching@tekram.com.tw *
- * Description: Device Driver for Tekram DC-390(T) PCI SCSI *
- * Bus Master Host Adapter *
- * (C)Copyright 1995-1996 Tekram Technology Co., Ltd. *
- ************************************************************************
- * (C) Copyright: put under GNU GPL in 10/96 *
- * (see Documentation/scsi/tmscsim.txt) *
- ************************************************************************
- * $Id: tmscsim.c,v 2.60.2.30 2000/12/20 01:07:12 garloff Exp $ *
- * Enhancements and bugfixes by *
- * Kurt Garloff <kurt@garloff.de> <garloff@suse.de> *
- ************************************************************************
- * HISTORY: *
- * *
- * REV# DATE NAME DESCRIPTION *
- * 1.00 96/04/24 CLH First release *
- * 1.01 96/06/12 CLH Fixed bug of Media Change for Removable *
- * Device, scan all LUN. Support Pre2.0.10 *
- * 1.02 96/06/18 CLH Fixed bug of Command timeout ... *
- * 1.03 96/09/25 KG Added tmscsim_proc_info() *
- * 1.04 96/10/11 CLH Updating for support KV 2.0.x *
- * 1.05 96/10/18 KG Fixed bug in DC390_abort(null ptr deref)*
- * 1.06 96/10/25 KG Fixed module support *
- * 1.07 96/11/09 KG Fixed tmscsim_proc_info() *
- * 1.08 96/11/18 KG Fixed null ptr in DC390_Disconnect() *
- * 1.09 96/11/30 KG Added register the allocated IO space *
- * 1.10 96/12/05 CLH Modified tmscsim_proc_info(), and reset *
- * pending interrupt in DC390_detect() *
- * 1.11 97/02/05 KG/CLH Fixeds problem with partitions greater *
- * than 1GB *
- * 1.12 98/02/15 MJ Rewritten PCI probing *
- * 1.13 98/04/08 KG Support for non DC390, __initfunc decls,*
- * changed max devs from 10 to 16 *
- * 1.14a 98/05/05 KG Dynamic DCB allocation, add-single-dev *
- * for LUNs if LUN_SCAN (BIOS) not set *
- * runtime config using /proc interface *
- * 1.14b 98/05/06 KG eliminated cli (); sti (); spinlocks *
- * 1.14c 98/05/07 KG 2.0.x compatibility *
- * 1.20a 98/05/07 KG changed names of funcs to be consistent *
- * DC390_ (entry points), dc390_ (internal)*
- * reworked locking *
- * 1.20b 98/05/12 KG bugs: version, kfree, _ctmp *
- * debug output *
- * 1.20c 98/05/12 KG bugs: kfree, parsing, EEpromDefaults *
- * 1.20d 98/05/14 KG bugs: list linkage, clear flag after *
- * reset on startup, code cleanup *
- * 1.20e 98/05/15 KG spinlock comments, name space cleanup *
- * pLastDCB now part of ACB structure *
- * added stats, timeout for 2.1, TagQ bug *
- * RESET and INQUIRY interface commands *
- * 1.20f 98/05/18 KG spinlocks fixes, max_lun fix, free DCBs *
- * for missing LUNs, pending int *
- * 1.20g 98/05/19 KG Clean up: Avoid short *
- * 1.20h 98/05/21 KG Remove AdaptSCSIID, max_lun ... *
- * 1.20i 98/05/21 KG Aiiie: Bug with TagQMask *
- * 1.20j 98/05/24 KG Handle STAT_BUSY, handle pACB->pLinkDCB *
- * == 0 in remove_dev and DoingSRB_Done *
- * 1.20k 98/05/25 KG DMA_INT (experimental) *
- * 1.20l 98/05/27 KG remove DMA_INT; DMA_IDLE cmds added; *
- * 1.20m 98/06/10 KG glitch configurable; made some global *
- * vars part of ACB; use DC390_readX *
- * 1.20n 98/06/11 KG startup params *
- * 1.20o 98/06/15 KG added TagMaxNum to boot/module params *
- * Device Nr -> Idx, TagMaxNum power of 2 *
- * 1.20p 98/06/17 KG Docu updates. Reset depends on settings *
- * pci_set_master added; 2.0.xx: pcibios_* *
- * used instead of MechNum things ... *
- * 1.20q 98/06/23 KG Changed defaults. Added debug code for *
- * removable media and fixed it. TagMaxNum *
- * fixed for DC390. Locking: ACB, DRV for *
- * better IRQ sharing. Spelling: Queueing *
- * Parsing and glitch_cfg changes. Display *
- * real SyncSpeed value. Made DisConn *
- * functional (!) *
- * 1.20r 98/06/30 KG Debug macros, allow disabling DsCn, set *
- * BIT4 in CtrlR4, EN_PAGE_INT, 2.0 module *
- * param -1 fixed. *
- * 1.20s 98/08/20 KG Debug info on abort(), try to check PCI,*
- * phys_to_bus instead of phys_to_virt, *
- * fixed sel. process, fixed locking, *
- * added MODULE_XXX infos, changed IRQ *
- * request flags, disable DMA_INT *
- * 1.20t 98/09/07 KG TagQ report fixed; Write Erase DMA Stat;*
- * initfunc -> __init; better abort; *
- * Timeout for XFER_DONE & BLAST_COMPLETE; *
- * Allow up to 33 commands being processed *
- * 2.0a 98/10/14 KG Max Cmnds back to 17. DMA_Stat clearing *
- * all flags. Clear within while() loops *
- * in DataIn_0/Out_0. Null ptr in dumpinfo *
- * for pSRB==0. Better locking during init.*
- * bios_param() now respects part. table. *
- * 2.0b 98/10/24 KG Docu fixes. Timeout Msg in DMA Blast. *
- * Disallow illegal idx in INQUIRY/REMOVE *
- * 2.0c 98/11/19 KG Cleaned up detect/init for SMP boxes, *
- * Write Erase DMA (1.20t) caused problems *
- * 2.0d 98/12/25 KG Christmas release ;-) Message handling *
- * completely reworked. Handle target ini- *
- * tiated SDTR correctly. *
- * 2.0d1 99/01/25 KG Try to handle RESTORE_PTR *
- * 2.0d2 99/02/08 KG Check for failure of kmalloc, correct *
- * inclusion of scsicam.h, DelayReset *
- * 2.0d3 99/05/31 KG DRIVER_OK -> DID_OK, DID_NO_CONNECT, *
- * detect Target mode and warn. *
- * pcmd->result handling cleaned up. *
- * 2.0d4 99/06/01 KG Cleaned selection process. Found bug *
- * which prevented more than 16 tags. Now: *
- * 24. SDTR cleanup. Cleaner multi-LUN *
- * handling. Don't modify ControlRegs/FIFO *
- * when connected. *
- * 2.0d5 99/06/01 KG Clear DevID, Fix INQUIRY after cfg chg. *
- * 2.0d6 99/06/02 KG Added ADD special command to allow cfg. *
- * before detection. Reset SYNC_NEGO_DONE *
- * after a bus reset. *
- * 2.0d7 99/06/03 KG Fixed bugs wrt add,remove commands *
- * 2.0d8 99/06/04 KG Removed copying of cmnd into CmdBlock. *
- * Fixed Oops in _release(). *
- * 2.0d9 99/06/06 KG Also tag queue INQUIRY, T_U_R, ... *
- * Allow arb. no. of Tagged Cmnds. Max 32 *
- * 2.0d1099/06/20 KG TagMaxNo changes now honoured! Queueing *
- * clearified (renamed ..) TagMask handling*
- * cleaned. *
- * 2.0d1199/06/28 KG cmd->result now identical to 2.0d2 *
- * 2.0d1299/07/04 KG Changed order of processing in IRQ *
- * 2.0d1399/07/05 KG Don't update DCB fields if removed *
- * 2.0d1499/07/05 KG remove_dev: Move kfree() to the end *
- * 2.0d1599/07/12 KG use_new_eh_code: 0, ULONG -> UINT where *
- * appropriate *
- * 2.0d1699/07/13 KG Reenable StartSCSI interrupt, Retry msg *
- * 2.0d1799/07/15 KG Remove debug msg. Disable recfg. when *
- * there are queued cmnds *
- * 2.0d1899/07/18 KG Selection timeout: Don't requeue *
- * 2.0d1999/07/18 KG Abort: Only call scsi_done if dequeued *
- * 2.0d2099/07/19 KG Rst_Detect: DoingSRB_Done *
- * 2.0d2199/08/15 KG dev_id for request/free_irq, cmnd[0] for*
- * RETRY, SRBdone does DID_ABORT for the *
- * cmd passed by DC390_reset() *
- * 2.0d2299/08/25 KG dev_id fixed. can_queue: 42 *
- * 2.0d2399/08/25 KG Removed some debugging code. dev_id *
- * now is set to pACB. Use u8,u16,u32. *
- * 2.0d2499/11/14 KG Unreg. I/O if failed IRQ alloc. Call *
- * done () w/ DID_BAD_TARGET in case of *
- * missing DCB. We are old EH!! *
- * 2.0d2500/01/15 KG 2.3.3x compat from Andreas Schultz *
- * set unique_id. Disable RETRY message. *
- * 2.0d2600/01/29 KG Go to new EH. *
- * 2.0d2700/01/31 KG ... but maintain 2.0 compat. *
- * and fix DCB freeing *
- * 2.0d2800/02/14 KG Queue statistics fixed, dump special cmd*
- * Waiting_Timer for failed StartSCSI *
- * New EH: Don't return cmnds to ML on RST *
- * Use old EH (don't have new EH fns yet) *
- * Reset: Unlock, but refuse to queue *
- * 2.3 __setup function *
- * 2.0e 00/05/22 KG Return residual for 2.3 *
- * 2.0e1 00/05/25 KG Compile fixes for 2.3.99 *
- * 2.0e2 00/05/27 KG Jeff Garzik's pci_enable_device() *
- * 2.0e3 00/09/29 KG Some 2.4 changes. Don't try Sync Nego *
- * before INQUIRY has reported ability. *
- * Recognise INQUIRY as scanning command. *
- * 2.0e4 00/10/13 KG Allow compilation into 2.4 kernel *
- * 2.0e5 00/11/17 KG Store Inq.flags in DCB *
- * 2.0e6 00/11/22 KG 2.4 init function (Thx to O.Schumann) *
- * 2.4 PCI device table (Thx to A.Richter) *
- * 2.0e7 00/11/28 KG Allow overriding of BIOS settings *
- * 2.0f 00/12/20 KG Handle failed INQUIRYs during scan *
- * 2.1a 03/11/29 GL, KG Initial fixing for 2.6. Convert to *
- * use the current PCI-mapping API, update *
- * command-queuing. *
- * 2.1b 04/04/13 GL Fix for 64-bit platforms *
- * 2.1b1 04/01/31 GL (applied 05.04) Remove internal *
- * command-queuing. *
- * 2.1b2 04/02/01 CH (applied 05.04) Fix error-handling *
- * 2.1c 04/05/23 GL Update to use the new pci_driver API, *
- * some scsi EH updates, more cleanup. *
- * 2.1d 04/05/27 GL Moved setting of scan_devices to *
- * slave_alloc/_configure/_destroy, as *
- * suggested by CH. *
- ***********************************************************************/
-
-/* DEBUG options */
-//#define DC390_DEBUG0
-//#define DC390_DEBUG1
-//#define DC390_DCBDEBUG
-//#define DC390_PARSEDEBUG
-//#define DC390_REMOVABLEDEBUG
-//#define DC390_LOCKDEBUG
-
-//#define NOP do{}while(0)
-#define C_NOP
-
-/* Debug definitions */
-#ifdef DC390_DEBUG0
-# define DEBUG0(x) x
-#else
-# define DEBUG0(x) C_NOP
-#endif
-#ifdef DC390_DEBUG1
-# define DEBUG1(x) x
-#else
-# define DEBUG1(x) C_NOP
-#endif
-#ifdef DC390_DCBDEBUG
-# define DCBDEBUG(x) x
-#else
-# define DCBDEBUG(x) C_NOP
-#endif
-#ifdef DC390_PARSEDEBUG
-# define PARSEDEBUG(x) x
-#else
-# define PARSEDEBUG(x) C_NOP
-#endif
-#ifdef DC390_REMOVABLEDEBUG
-# define REMOVABLEDEBUG(x) x
-#else
-# define REMOVABLEDEBUG(x) C_NOP
-#endif
-#define DCBDEBUG1(x) C_NOP
-
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/signal.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/ioport.h>
-#include <linux/pci.h>
-#include <linux/proc_fs.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/blkdev.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-#include <asm/io.h>
-
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
-#include <scsi/scsi_device.h>
-#include <scsi/scsi_host.h>
-#include <scsi/scsicam.h>
-#include <scsi/scsi_tcq.h>
-
-
-#define DC390_BANNER "Tekram DC390/AM53C974"
-#define DC390_VERSION "2.1d 2004-05-27"
-
-#define PCI_DEVICE_ID_AMD53C974 PCI_DEVICE_ID_AMD_SCSI
-
-#include "tmscsim.h"
-
-
-static void dc390_DataOut_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
-static void dc390_DataIn_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
-static void dc390_Command_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
-static void dc390_Status_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
-static void dc390_MsgOut_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
-static void dc390_MsgIn_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
-static void dc390_DataOutPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
-static void dc390_DataInPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
-static void dc390_CommandPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
-static void dc390_StatusPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
-static void dc390_MsgOutPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
-static void dc390_MsgInPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
-static void dc390_Nop_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
-static void dc390_Nop_1( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus);
-
-static void dc390_SetXferRate( struct dc390_acb* pACB, struct dc390_dcb* pDCB );
-static void dc390_Disconnect( struct dc390_acb* pACB );
-static void dc390_Reselect( struct dc390_acb* pACB );
-static void dc390_SRBdone( struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb* pSRB );
-static void dc390_ScsiRstDetect( struct dc390_acb* pACB );
-static void dc390_EnableMsgOut_Abort(struct dc390_acb*, struct dc390_srb*);
-static void dc390_dumpinfo(struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb* pSRB);
-static void dc390_ResetDevParam(struct dc390_acb* pACB);
-
-static u32 dc390_laststatus = 0;
-static u8 dc390_adapterCnt = 0;
-
-static int disable_clustering;
-module_param(disable_clustering, int, S_IRUGO);
-MODULE_PARM_DESC(disable_clustering, "If you experience problems with your devices, try setting to 1");
-
-/* Startup values, to be overriden on the commandline */
-static int tmscsim[] = {-2, -2, -2, -2, -2, -2};
-
-module_param_array(tmscsim, int, NULL, 0);
-MODULE_PARM_DESC(tmscsim, "Host SCSI ID, Speed (0=10MHz), Device Flags, Adapter Flags, Max Tags (log2(tags)-1), DelayReset (s)");
-MODULE_AUTHOR("C.L. Huang / Kurt Garloff");
-MODULE_DESCRIPTION("SCSI host adapter driver for Tekram DC390 and other AMD53C974A based PCI SCSI adapters");
-MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("sd,sr,sg,st");
-
-static void *dc390_phase0[]={
- dc390_DataOut_0,
- dc390_DataIn_0,
- dc390_Command_0,
- dc390_Status_0,
- dc390_Nop_0,
- dc390_Nop_0,
- dc390_MsgOut_0,
- dc390_MsgIn_0,
- dc390_Nop_1
- };
-
-static void *dc390_phase1[]={
- dc390_DataOutPhase,
- dc390_DataInPhase,
- dc390_CommandPhase,
- dc390_StatusPhase,
- dc390_Nop_0,
- dc390_Nop_0,
- dc390_MsgOutPhase,
- dc390_MsgInPhase,
- dc390_Nop_1
- };
-
-#ifdef DC390_DEBUG1
-static char* dc390_p0_str[] = {
- "dc390_DataOut_0",
- "dc390_DataIn_0",
- "dc390_Command_0",
- "dc390_Status_0",
- "dc390_Nop_0",
- "dc390_Nop_0",
- "dc390_MsgOut_0",
- "dc390_MsgIn_0",
- "dc390_Nop_1"
- };
-
-static char* dc390_p1_str[] = {
- "dc390_DataOutPhase",
- "dc390_DataInPhase",
- "dc390_CommandPhase",
- "dc390_StatusPhase",
- "dc390_Nop_0",
- "dc390_Nop_0",
- "dc390_MsgOutPhase",
- "dc390_MsgInPhase",
- "dc390_Nop_1"
- };
-#endif
-
-static u8 dc390_eepromBuf[MAX_ADAPTER_NUM][EE_LEN];
-static u8 dc390_clock_period1[] = {4, 5, 6, 7, 8, 10, 13, 20};
-static u8 dc390_clock_speed[] = {100,80,67,57,50, 40, 31, 20};
-
-/***********************************************************************
- * Functions for the management of the internal structures
- * (DCBs, SRBs, Queueing)
- *
- **********************************************************************/
-static void inline dc390_start_segment(struct dc390_srb* pSRB)
-{
- struct scatterlist *psgl = pSRB->pSegmentList;
-
- /* start new sg segment */
- pSRB->SGBusAddr = sg_dma_address(psgl);
- pSRB->SGToBeXferLen = sg_dma_len(psgl);
-}
-
-static unsigned long inline dc390_advance_segment(struct dc390_srb* pSRB, u32 residue)
-{
- unsigned long xfer = pSRB->SGToBeXferLen - residue;
-
- /* xfer more bytes transferred */
- pSRB->SGBusAddr += xfer;
- pSRB->TotalXferredLen += xfer;
- pSRB->SGToBeXferLen = residue;
-
- return xfer;
-}
-
-static struct dc390_dcb __inline__ *dc390_findDCB ( struct dc390_acb* pACB, u8 id, u8 lun)
-{
- struct dc390_dcb* pDCB = pACB->pLinkDCB; if (!pDCB) return NULL;
- while (pDCB->TargetID != id || pDCB->TargetLUN != lun)
- {
- pDCB = pDCB->pNextDCB;
- if (pDCB == pACB->pLinkDCB)
- return NULL;
- }
- DCBDEBUG1( printk (KERN_DEBUG "DCB %p (%02x,%02x) found.\n", \
- pDCB, pDCB->TargetID, pDCB->TargetLUN));
- return pDCB;
-}
-
-/* Insert SRB oin top of free list */
-static __inline__ void dc390_Free_insert (struct dc390_acb* pACB, struct dc390_srb* pSRB)
-{
- DEBUG0(printk ("DC390: Free SRB %p\n", pSRB));
- pSRB->pNextSRB = pACB->pFreeSRB;
- pACB->pFreeSRB = pSRB;
-}
-
-static __inline__ void dc390_Going_append (struct dc390_dcb* pDCB, struct dc390_srb* pSRB)
-{
- pDCB->GoingSRBCnt++;
- DEBUG0(printk("DC390: Append SRB %p to Going\n", pSRB));
- /* Append to the list of Going commands */
- if( pDCB->pGoingSRB )
- pDCB->pGoingLast->pNextSRB = pSRB;
- else
- pDCB->pGoingSRB = pSRB;
-
- pDCB->pGoingLast = pSRB;
- /* No next one in sent list */
- pSRB->pNextSRB = NULL;
-}
-
-static __inline__ void dc390_Going_remove (struct dc390_dcb* pDCB, struct dc390_srb* pSRB)
-{
- DEBUG0(printk("DC390: Remove SRB %p from Going\n", pSRB));
- if (pSRB == pDCB->pGoingSRB)
- pDCB->pGoingSRB = pSRB->pNextSRB;
- else
- {
- struct dc390_srb* psrb = pDCB->pGoingSRB;
- while (psrb && psrb->pNextSRB != pSRB)
- psrb = psrb->pNextSRB;
- if (!psrb)
- { printk (KERN_ERR "DC390: Remove non-ex. SRB %p from Going!\n", pSRB); return; }
- psrb->pNextSRB = pSRB->pNextSRB;
- if (pSRB == pDCB->pGoingLast)
- pDCB->pGoingLast = psrb;
- }
- pDCB->GoingSRBCnt--;
-}
-
-static struct scatterlist* dc390_sg_build_single(struct scatterlist *sg, void *addr, unsigned int length)
-{
- sg_init_one(sg, addr, length);
- return sg;
-}
-
-/* Create pci mapping */
-static int dc390_pci_map (struct dc390_srb* pSRB)
-{
- int error = 0;
- struct scsi_cmnd *pcmd = pSRB->pcmd;
- struct pci_dev *pdev = pSRB->pSRBDCB->pDCBACB->pdev;
- dc390_cmd_scp_t* cmdp = ((dc390_cmd_scp_t*)(&pcmd->SCp));
-
- /* Map sense buffer */
- if (pSRB->SRBFlag & AUTO_REQSENSE) {
- pSRB->pSegmentList = dc390_sg_build_single(&pSRB->Segmentx, pcmd->sense_buffer, SCSI_SENSE_BUFFERSIZE);
- pSRB->SGcount = pci_map_sg(pdev, pSRB->pSegmentList, 1,
- DMA_FROM_DEVICE);
- cmdp->saved_dma_handle = sg_dma_address(pSRB->pSegmentList);
-
- /* TODO: error handling */
- if (pSRB->SGcount != 1)
- error = 1;
- DEBUG1(printk("%s(): Mapped sense buffer %p at %x\n", __func__, pcmd->sense_buffer, cmdp->saved_dma_handle));
- /* Map SG list */
- } else if (scsi_sg_count(pcmd)) {
- int nseg;
-
- nseg = scsi_dma_map(pcmd);
-
- pSRB->pSegmentList = scsi_sglist(pcmd);
- pSRB->SGcount = nseg;
-
- /* TODO: error handling */
- if (nseg < 0)
- error = 1;
- DEBUG1(printk("%s(): Mapped SG %p with %d (%d) elements\n",\
- __func__, scsi_sglist(pcmd), nseg, scsi_sg_count(pcmd)));
- /* Map single segment */
- } else
- pSRB->SGcount = 0;
-
- return error;
-}
-
-/* Remove pci mapping */
-static void dc390_pci_unmap (struct dc390_srb* pSRB)
-{
- struct scsi_cmnd *pcmd = pSRB->pcmd;
- struct pci_dev *pdev = pSRB->pSRBDCB->pDCBACB->pdev;
- DEBUG1(dc390_cmd_scp_t* cmdp = ((dc390_cmd_scp_t*)(&pcmd->SCp)));
-
- if (pSRB->SRBFlag) {
- pci_unmap_sg(pdev, &pSRB->Segmentx, 1, DMA_FROM_DEVICE);
- DEBUG1(printk("%s(): Unmapped sense buffer at %x\n", __func__, cmdp->saved_dma_handle));
- } else {
- scsi_dma_unmap(pcmd);
- DEBUG1(printk("%s(): Unmapped SG at %p with %d elements\n",
- __func__, scsi_sglist(pcmd), scsi_sg_count(pcmd)));
- }
-}
-
-static void __inline__
-dc390_freetag (struct dc390_dcb* pDCB, struct dc390_srb* pSRB)
-{
- if (pSRB->TagNumber != SCSI_NO_TAG) {
- pDCB->TagMask &= ~(1 << pSRB->TagNumber); /* free tag mask */
- pSRB->TagNumber = SCSI_NO_TAG;
- }
-}
-
-
-static int
-dc390_StartSCSI( struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb* pSRB )
-{
- struct scsi_cmnd *scmd = pSRB->pcmd;
- struct scsi_device *sdev = scmd->device;
- u8 cmd, disc_allowed, try_sync_nego;
- char tag[2];
-
- pSRB->ScsiPhase = SCSI_NOP0;
-
- if (pACB->Connected)
- {
- // Should not happen normally
- printk (KERN_WARNING "DC390: Can't select when connected! (%08x,%02x)\n",
- pSRB->SRBState, pSRB->SRBFlag);
- pSRB->SRBState = SRB_READY;
- pACB->SelConn++;
- return 1;
- }
- if (time_before (jiffies, pACB->last_reset))
- {
- DEBUG0(printk ("DC390: We were just reset and don't accept commands yet!\n"));
- return 1;
- }
- /* KG: Moved pci mapping here */
- dc390_pci_map(pSRB);
- /* TODO: error handling */
- DC390_write8 (Scsi_Dest_ID, pDCB->TargetID);
- DC390_write8 (Sync_Period, pDCB->SyncPeriod);
- DC390_write8 (Sync_Offset, pDCB->SyncOffset);
- DC390_write8 (CtrlReg1, pDCB->CtrlR1);
- DC390_write8 (CtrlReg3, pDCB->CtrlR3);
- DC390_write8 (CtrlReg4, pDCB->CtrlR4);
- DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD); /* Flush FIFO */
- DEBUG1(printk (KERN_INFO "DC390: Start SCSI command: %02x (Sync:%02x)\n",\
- scmd->cmnd[0], pDCB->SyncMode));
-
- /* Don't disconnect on AUTO_REQSENSE, cause it might be an
- * Contingent Allegiance Condition (6.6), where no tags should be used.
- * All other have to be allowed to disconnect to prevent Incorrect
- * Initiator Connection (6.8.2/6.5.2) */
- /* Changed KG, 99/06/06 */
- if (! (pSRB->SRBFlag & AUTO_REQSENSE))
- disc_allowed = pDCB->DevMode & EN_DISCONNECT_;
- else
- disc_allowed = 0;
-
- if ((pDCB->SyncMode & SYNC_ENABLE) && pDCB->TargetLUN == 0 && sdev->sdtr &&
- (((scmd->cmnd[0] == REQUEST_SENSE || (pSRB->SRBFlag & AUTO_REQSENSE)) &&
- !(pDCB->SyncMode & SYNC_NEGO_DONE)) || scmd->cmnd[0] == INQUIRY))
- try_sync_nego = 1;
- else
- try_sync_nego = 0;
-
- pSRB->MsgCnt = 0;
- cmd = SEL_W_ATN;
- DC390_write8 (ScsiFifo, IDENTIFY(disc_allowed, pDCB->TargetLUN));
- /* Change 99/05/31: Don't use tags when not disconnecting (BUSY) */
- if ((pDCB->SyncMode & EN_TAG_QUEUEING) && disc_allowed && scsi_populate_tag_msg(scmd, tag)) {
- DC390_write8(ScsiFifo, tag[0]);
- pDCB->TagMask |= 1 << tag[1];
- pSRB->TagNumber = tag[1];
- DC390_write8(ScsiFifo, tag[1]);
- DEBUG1(printk(KERN_INFO "DC390: Select w/DisCn for SRB %p, block tag %02x\n", pSRB, tag[1]));
- cmd = SEL_W_ATN3;
- } else {
- /* No TagQ */
-//no_tag:
- DEBUG1(printk(KERN_INFO "DC390: Select w%s/DisCn for SRB %p, No TagQ\n", disc_allowed ? "" : "o", pSRB));
- }
-
- pSRB->SRBState = SRB_START_;
-
- if (try_sync_nego)
- {
- u8 Sync_Off = pDCB->SyncOffset;
- DEBUG0(printk (KERN_INFO "DC390: NEW Sync Nego code triggered (%i %i)\n", pDCB->TargetID, pDCB->TargetLUN));
- pSRB->MsgOutBuf[0] = EXTENDED_MESSAGE;
- pSRB->MsgOutBuf[1] = 3;
- pSRB->MsgOutBuf[2] = EXTENDED_SDTR;
- pSRB->MsgOutBuf[3] = pDCB->NegoPeriod;
- if (!(Sync_Off & 0x0f)) Sync_Off = SYNC_NEGO_OFFSET;
- pSRB->MsgOutBuf[4] = Sync_Off;
- pSRB->MsgCnt = 5;
- //pSRB->SRBState = SRB_MSGOUT_;
- pSRB->SRBState |= DO_SYNC_NEGO;
- cmd = SEL_W_ATN_STOP;
- }
-
- /* Command is written in CommandPhase, if SEL_W_ATN_STOP ... */
- if (cmd != SEL_W_ATN_STOP)
- {
- if( pSRB->SRBFlag & AUTO_REQSENSE )
- {
- DC390_write8 (ScsiFifo, REQUEST_SENSE);
- DC390_write8 (ScsiFifo, pDCB->TargetLUN << 5);
- DC390_write8 (ScsiFifo, 0);
- DC390_write8 (ScsiFifo, 0);
- DC390_write8 (ScsiFifo, SCSI_SENSE_BUFFERSIZE);
- DC390_write8 (ScsiFifo, 0);
- DEBUG1(printk (KERN_DEBUG "DC390: AutoReqSense !\n"));
- }
- else /* write cmnd to bus */
- {
- u8 *ptr; u8 i;
- ptr = (u8 *)scmd->cmnd;
- for (i = 0; i < scmd->cmd_len; i++)
- DC390_write8 (ScsiFifo, *(ptr++));
- }
- }
- DEBUG0(if (pACB->pActiveDCB) \
- printk (KERN_WARNING "DC390: ActiveDCB != 0\n"));
- DEBUG0(if (pDCB->pActiveSRB) \
- printk (KERN_WARNING "DC390: ActiveSRB != 0\n"));
- //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
- if (DC390_read8 (Scsi_Status) & INTERRUPT)
- {
- dc390_freetag (pDCB, pSRB);
- DEBUG0(printk ("DC390: Interrupt during Start SCSI (target %02i-%02i)\n",
- scmd->device->id, (u8)scmd->device->lun));
- pSRB->SRBState = SRB_READY;
- //DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
- pACB->SelLost++;
- return 1;
- }
- DC390_write8 (ScsiCmd, cmd);
- pACB->pActiveDCB = pDCB;
- pDCB->pActiveSRB = pSRB;
- pACB->Connected = 1;
- pSRB->ScsiPhase = SCSI_NOP1;
- return 0;
-}
-
-
-static void __inline__
-dc390_InvalidCmd(struct dc390_acb* pACB)
-{
- if (pACB->pActiveDCB->pActiveSRB->SRBState & (SRB_START_ | SRB_MSGOUT))
- DC390_write8(ScsiCmd, CLEAR_FIFO_CMD);
-}
-
-
-static irqreturn_t __inline__
-DC390_Interrupt(void *dev_id)
-{
- struct dc390_acb *pACB = dev_id;
- struct dc390_dcb *pDCB;
- struct dc390_srb *pSRB;
- u8 sstatus=0;
- u8 phase;
- void (*stateV)( struct dc390_acb*, struct dc390_srb*, u8 *);
- u8 istate, istatus;
-
- sstatus = DC390_read8 (Scsi_Status);
- if( !(sstatus & INTERRUPT) )
- return IRQ_NONE;
-
- DEBUG1(printk (KERN_DEBUG "sstatus=%02x,", sstatus));
-
- //DC390_write32 (DMA_ScsiBusCtrl, WRT_ERASE_DMA_STAT | EN_INT_ON_PCI_ABORT);
- //dstatus = DC390_read8 (DMA_Status);
- //DC390_write32 (DMA_ScsiBusCtrl, EN_INT_ON_PCI_ABORT);
-
- spin_lock_irq(pACB->pScsiHost->host_lock);
-
- istate = DC390_read8 (Intern_State);
- istatus = DC390_read8 (INT_Status); /* This clears Scsi_Status, Intern_State and INT_Status ! */
-
- DEBUG1(printk (KERN_INFO "Istatus(Res,Inv,Dis,Serv,Succ,ReS,SelA,Sel)=%02x,",istatus));
- dc390_laststatus &= ~0x00ffffff;
- dc390_laststatus |= /* dstatus<<24 | */ sstatus<<16 | istate<<8 | istatus;
-
- if (sstatus & ILLEGAL_OP_ERR)
- {
- printk ("DC390: Illegal Operation detected (%08x)!\n", dc390_laststatus);
- dc390_dumpinfo (pACB, pACB->pActiveDCB, pACB->pActiveDCB->pActiveSRB);
- }
-
- else if (istatus & INVALID_CMD)
- {
- printk ("DC390: Invalid Command detected (%08x)!\n", dc390_laststatus);
- dc390_InvalidCmd( pACB );
- goto unlock;
- }
-
- if (istatus & SCSI_RESET)
- {
- dc390_ScsiRstDetect( pACB );
- goto unlock;
- }
-
- if (istatus & DISCONNECTED)
- {
- dc390_Disconnect( pACB );
- goto unlock;
- }
-
- if (istatus & RESELECTED)
- {
- dc390_Reselect( pACB );
- goto unlock;
- }
-
- else if (istatus & (SELECTED | SEL_ATTENTION))
- {
- printk (KERN_ERR "DC390: Target mode not supported!\n");
- goto unlock;
- }
-
- if (istatus & (SUCCESSFUL_OP|SERVICE_REQUEST) )
- {
- pDCB = pACB->pActiveDCB;
- if (!pDCB)
- {
- printk (KERN_ERR "DC390: Suc. op/ Serv. req: pActiveDCB = 0!\n");
- goto unlock;
- }
- pSRB = pDCB->pActiveSRB;
- if( pDCB->DCBFlag & ABORT_DEV_ )
- dc390_EnableMsgOut_Abort (pACB, pSRB);
-
- phase = pSRB->ScsiPhase;
- DEBUG1(printk (KERN_INFO "DC390: [%i]%s(0) (%02x)\n", phase, dc390_p0_str[phase], sstatus));
- stateV = (void *) dc390_phase0[phase];
- ( *stateV )( pACB, pSRB, &sstatus );
-
- pSRB->ScsiPhase = sstatus & 7;
- phase = (u8) sstatus & 7;
- DEBUG1(printk (KERN_INFO "DC390: [%i]%s(1) (%02x)\n", phase, dc390_p1_str[phase], sstatus));
- stateV = (void *) dc390_phase1[phase];
- ( *stateV )( pACB, pSRB, &sstatus );
- }
-
- unlock:
- spin_unlock_irq(pACB->pScsiHost->host_lock);
- return IRQ_HANDLED;
-}
-
-static irqreturn_t do_DC390_Interrupt(int irq, void *dev_id)
-{
- irqreturn_t ret;
- DEBUG1(printk (KERN_INFO "DC390: Irq (%i) caught: ", irq));
- /* Locking is done in DC390_Interrupt */
- ret = DC390_Interrupt(dev_id);
- DEBUG1(printk (".. IRQ returned\n"));
- return ret;
-}
-
-static void
-dc390_DataOut_0(struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
-{
- u8 sstatus;
- u32 ResidCnt;
- u8 dstate = 0;
-
- sstatus = *psstatus;
-
- if( !(pSRB->SRBState & SRB_XFERPAD) )
- {
- if( sstatus & (PARITY_ERR | ILLEGAL_OP_ERR) )
- pSRB->SRBStatus |= PARITY_ERROR;
-
- if( sstatus & COUNT_2_ZERO )
- {
- unsigned long timeout = jiffies + HZ;
-
- /* Function called from the ISR with the host_lock held and interrupts disabled */
- if (pSRB->SGToBeXferLen)
- while (time_before(jiffies, timeout) && !((dstate = DC390_read8 (DMA_Status)) & DMA_XFER_DONE)) {
- spin_unlock_irq(pACB->pScsiHost->host_lock);
- udelay(50);
- spin_lock_irq(pACB->pScsiHost->host_lock);
- }
- if (!time_before(jiffies, timeout))
- printk (KERN_CRIT "DC390: Deadlock in DataOut_0: DMA aborted unfinished: %06x bytes remain!!\n",
- DC390_read32 (DMA_Wk_ByteCntr));
- dc390_laststatus &= ~0xff000000;
- dc390_laststatus |= dstate << 24;
- pSRB->TotalXferredLen += pSRB->SGToBeXferLen;
- pSRB->SGIndex++;
- if( pSRB->SGIndex < pSRB->SGcount )
- {
- pSRB->pSegmentList++;
-
- dc390_start_segment(pSRB);
- }
- else
- pSRB->SGToBeXferLen = 0;
- }
- else
- {
- ResidCnt = ((u32) DC390_read8 (Current_Fifo) & 0x1f) +
- (((u32) DC390_read8 (CtcReg_High) << 16) |
- ((u32) DC390_read8 (CtcReg_Mid) << 8) |
- (u32) DC390_read8 (CtcReg_Low));
-
- dc390_advance_segment(pSRB, ResidCnt);
- }
- }
- if ((*psstatus & 7) != SCSI_DATA_OUT)
- {
- DC390_write8 (DMA_Cmd, WRITE_DIRECTION+DMA_IDLE_CMD);
- DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
- }
-}
-
-static void
-dc390_DataIn_0(struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
-{
- u8 sstatus, residual, bval;
- u32 ResidCnt, i;
- unsigned long xferCnt;
-
- sstatus = *psstatus;
-
- if( !(pSRB->SRBState & SRB_XFERPAD) )
- {
- if( sstatus & (PARITY_ERR | ILLEGAL_OP_ERR))
- pSRB->SRBStatus |= PARITY_ERROR;
-
- if( sstatus & COUNT_2_ZERO )
- {
- int dstate = 0;
- unsigned long timeout = jiffies + HZ;
-
- /* Function called from the ISR with the host_lock held and interrupts disabled */
- if (pSRB->SGToBeXferLen)
- while (time_before(jiffies, timeout) && !((dstate = DC390_read8 (DMA_Status)) & DMA_XFER_DONE)) {
- spin_unlock_irq(pACB->pScsiHost->host_lock);
- udelay(50);
- spin_lock_irq(pACB->pScsiHost->host_lock);
- }
- if (!time_before(jiffies, timeout)) {
- printk (KERN_CRIT "DC390: Deadlock in DataIn_0: DMA aborted unfinished: %06x bytes remain!!\n",
- DC390_read32 (DMA_Wk_ByteCntr));
- printk (KERN_CRIT "DC390: DataIn_0: DMA State: %i\n", dstate);
- }
- dc390_laststatus &= ~0xff000000;
- dc390_laststatus |= dstate << 24;
- DEBUG1(ResidCnt = ((unsigned long) DC390_read8 (CtcReg_High) << 16) \
- + ((unsigned long) DC390_read8 (CtcReg_Mid) << 8) \
- + ((unsigned long) DC390_read8 (CtcReg_Low)));
- DEBUG1(printk (KERN_DEBUG "Count_2_Zero (ResidCnt=%u,ToBeXfer=%lu),", ResidCnt, pSRB->SGToBeXferLen));
-
- DC390_write8 (DMA_Cmd, READ_DIRECTION+DMA_IDLE_CMD);
-
- pSRB->TotalXferredLen += pSRB->SGToBeXferLen;
- pSRB->SGIndex++;
- if( pSRB->SGIndex < pSRB->SGcount )
- {
- pSRB->pSegmentList++;
-
- dc390_start_segment(pSRB);
- }
- else
- pSRB->SGToBeXferLen = 0;
- }
- else /* phase changed */
- {
- residual = 0;
- bval = DC390_read8 (Current_Fifo);
- while( bval & 0x1f )
- {
- DEBUG1(printk (KERN_DEBUG "Check for residuals,"));
- if( (bval & 0x1f) == 1 )
- {
- for(i=0; i < 0x100; i++)
- {
- bval = DC390_read8 (Current_Fifo);
- if( !(bval & 0x1f) )
- goto din_1;
- else if( i == 0x0ff )
- {
- residual = 1; /* ;1 residual byte */
- goto din_1;
- }
- }
- }
- else
- bval = DC390_read8 (Current_Fifo);
- }
-din_1:
- DC390_write8 (DMA_Cmd, READ_DIRECTION+DMA_BLAST_CMD);
- for (i = 0xa000; i; i--)
- {
- bval = DC390_read8 (DMA_Status);
- if (bval & BLAST_COMPLETE)
- break;
- }
- /* It seems a DMA Blast abort isn't that bad ... */
- if (!i) printk (KERN_ERR "DC390: DMA Blast aborted unfinished!\n");
- //DC390_write8 (DMA_Cmd, READ_DIRECTION+DMA_IDLE_CMD);
- dc390_laststatus &= ~0xff000000;
- dc390_laststatus |= bval << 24;
-
- DEBUG1(printk (KERN_DEBUG "Blast: Read %i times DMA_Status %02x", 0xa000-i, bval));
- ResidCnt = (((u32) DC390_read8 (CtcReg_High) << 16) |
- ((u32) DC390_read8 (CtcReg_Mid) << 8)) |
- (u32) DC390_read8 (CtcReg_Low);
-
- xferCnt = dc390_advance_segment(pSRB, ResidCnt);
-
- if (residual) {
- size_t count = 1;
- size_t offset = pSRB->SGBusAddr - sg_dma_address(pSRB->pSegmentList);
- unsigned long flags;
- u8 *ptr;
-
- bval = DC390_read8 (ScsiFifo); /* get one residual byte */
-
- local_irq_save(flags);
- ptr = scsi_kmap_atomic_sg(pSRB->pSegmentList, pSRB->SGcount, &offset, &count);
- if (likely(ptr)) {
- *(ptr + offset) = bval;
- scsi_kunmap_atomic_sg(ptr);
- }
- local_irq_restore(flags);
- WARN_ON(!ptr);
-
- /* 1 more byte read */
- xferCnt += dc390_advance_segment(pSRB, pSRB->SGToBeXferLen - 1);
- }
- DEBUG1(printk (KERN_DEBUG "Xfered: %lu, Total: %lu, Remaining: %lu\n", xferCnt,\
- pSRB->TotalXferredLen, pSRB->SGToBeXferLen));
- }
- }
- if ((*psstatus & 7) != SCSI_DATA_IN)
- {
- DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
- DC390_write8 (DMA_Cmd, READ_DIRECTION+DMA_IDLE_CMD);
- }
-}
-
-static void
-dc390_Command_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
-{
-}
-
-static void
-dc390_Status_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
-{
-
- pSRB->TargetStatus = DC390_read8 (ScsiFifo);
- //udelay (1);
- pSRB->EndMessage = DC390_read8 (ScsiFifo); /* get message */
-
- *psstatus = SCSI_NOP0;
- pSRB->SRBState = SRB_COMPLETED;
- DC390_write8 (ScsiCmd, MSG_ACCEPTED_CMD);
-}
-
-static void
-dc390_MsgOut_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
-{
- if( pSRB->SRBState & (SRB_UNEXPECT_RESEL+SRB_ABORT_SENT) )
- *psstatus = SCSI_NOP0;
- //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
-}
-
-
-static void __inline__
-dc390_reprog (struct dc390_acb* pACB, struct dc390_dcb* pDCB)
-{
- DC390_write8 (Sync_Period, pDCB->SyncPeriod);
- DC390_write8 (Sync_Offset, pDCB->SyncOffset);
- DC390_write8 (CtrlReg3, pDCB->CtrlR3);
- DC390_write8 (CtrlReg4, pDCB->CtrlR4);
- dc390_SetXferRate (pACB, pDCB);
-}
-
-
-#ifdef DC390_DEBUG0
-static void
-dc390_printMsg (u8 *MsgBuf, u8 len)
-{
- int i;
- printk (" %02x", MsgBuf[0]);
- for (i = 1; i < len; i++)
- printk (" %02x", MsgBuf[i]);
- printk ("\n");
-}
-#endif
-
-#define DC390_ENABLE_MSGOUT DC390_write8 (ScsiCmd, SET_ATN_CMD)
-
-/* reject_msg */
-static void __inline__
-dc390_MsgIn_reject (struct dc390_acb* pACB, struct dc390_srb* pSRB)
-{
- pSRB->MsgOutBuf[0] = MESSAGE_REJECT;
- pSRB->MsgCnt = 1;
- DC390_ENABLE_MSGOUT;
- DEBUG0 (printk (KERN_INFO "DC390: Reject message\n"));
-}
-
-/* abort command */
-static void
-dc390_EnableMsgOut_Abort ( struct dc390_acb* pACB, struct dc390_srb* pSRB )
-{
- pSRB->MsgOutBuf[0] = ABORT;
- pSRB->MsgCnt = 1; DC390_ENABLE_MSGOUT;
- pSRB->pSRBDCB->DCBFlag &= ~ABORT_DEV_;
-}
-
-static struct dc390_srb*
-dc390_MsgIn_QTag (struct dc390_acb* pACB, struct dc390_dcb* pDCB, s8 tag)
-{
- struct dc390_srb* pSRB = pDCB->pGoingSRB;
-
- if (pSRB)
- {
- struct scsi_cmnd *scmd = scsi_find_tag(pSRB->pcmd->device, tag);
- pSRB = (struct dc390_srb *)scmd->host_scribble;
-
- if (pDCB->DCBFlag & ABORT_DEV_)
- {
- pSRB->SRBState = SRB_ABORT_SENT;
- dc390_EnableMsgOut_Abort( pACB, pSRB );
- }
-
- if (!(pSRB->SRBState & SRB_DISCONNECT))
- goto mingx0;
-
- pDCB->pActiveSRB = pSRB;
- pSRB->SRBState = SRB_DATA_XFER;
- }
- else
- {
- mingx0:
- pSRB = pACB->pTmpSRB;
- pSRB->SRBState = SRB_UNEXPECT_RESEL;
- pDCB->pActiveSRB = pSRB;
- pSRB->MsgOutBuf[0] = ABORT_TAG;
- pSRB->MsgCnt = 1; DC390_ENABLE_MSGOUT;
- }
- return pSRB;
-}
-
-
-/* set async transfer mode */
-static void
-dc390_MsgIn_set_async (struct dc390_acb* pACB, struct dc390_srb* pSRB)
-{
- struct dc390_dcb* pDCB = pSRB->pSRBDCB;
- if (!(pSRB->SRBState & DO_SYNC_NEGO))
- printk (KERN_INFO "DC390: Target %i initiates Non-Sync?\n", pDCB->TargetID);
- pSRB->SRBState &= ~DO_SYNC_NEGO;
- pDCB->SyncMode &= ~(SYNC_ENABLE+SYNC_NEGO_DONE);
- pDCB->SyncPeriod = 0;
- pDCB->SyncOffset = 0;
- //pDCB->NegoPeriod = 50; /* 200ns <=> 5 MHz */
- pDCB->CtrlR3 = FAST_CLK; /* fast clock / normal scsi */
- pDCB->CtrlR4 &= 0x3f;
- pDCB->CtrlR4 |= pACB->glitch_cfg; /* glitch eater */
- dc390_reprog (pACB, pDCB);
-}
-
-/* set sync transfer mode */
-static void
-dc390_MsgIn_set_sync (struct dc390_acb* pACB, struct dc390_srb* pSRB)
-{
- u8 bval;
- u16 wval, wval1;
- struct dc390_dcb* pDCB = pSRB->pSRBDCB;
- u8 oldsyncperiod = pDCB->SyncPeriod;
- u8 oldsyncoffset = pDCB->SyncOffset;
-
- if (!(pSRB->SRBState & DO_SYNC_NEGO))
- {
- printk (KERN_INFO "DC390: Target %i initiates Sync: %ins %i ... answer ...\n",
- pDCB->TargetID, pSRB->MsgInBuf[3]<<2, pSRB->MsgInBuf[4]);
-
- /* reject */
- //dc390_MsgIn_reject (pACB, pSRB);
- //return dc390_MsgIn_set_async (pACB, pSRB);
-
- /* Reply with corrected SDTR Message */
- if (pSRB->MsgInBuf[4] > 15)
- {
- printk (KERN_INFO "DC390: Lower Sync Offset to 15\n");
- pSRB->MsgInBuf[4] = 15;
- }
- if (pSRB->MsgInBuf[3] < pDCB->NegoPeriod)
- {
- printk (KERN_INFO "DC390: Set sync nego period to %ins\n", pDCB->NegoPeriod << 2);
- pSRB->MsgInBuf[3] = pDCB->NegoPeriod;
- }
- memcpy (pSRB->MsgOutBuf, pSRB->MsgInBuf, 5);
- pSRB->MsgCnt = 5;
- DC390_ENABLE_MSGOUT;
- }
-
- pSRB->SRBState &= ~DO_SYNC_NEGO;
- pDCB->SyncMode |= SYNC_ENABLE+SYNC_NEGO_DONE;
- pDCB->SyncOffset &= 0x0f0;
- pDCB->SyncOffset |= pSRB->MsgInBuf[4];
- pDCB->NegoPeriod = pSRB->MsgInBuf[3];
-
- wval = (u16) pSRB->MsgInBuf[3];
- wval = wval << 2; wval -= 3; wval1 = wval / 25; /* compute speed */
- if( (wval1 * 25) != wval) wval1++;
- bval = FAST_CLK+FAST_SCSI; /* fast clock / fast scsi */
-
- pDCB->CtrlR4 &= 0x3f; /* Glitch eater: 12ns less than normal */
- if (pACB->glitch_cfg != NS_TO_GLITCH(0))
- pDCB->CtrlR4 |= NS_TO_GLITCH(((GLITCH_TO_NS(pACB->glitch_cfg)) - 1));
- else
- pDCB->CtrlR4 |= NS_TO_GLITCH(0);
- if (wval1 < 4) pDCB->CtrlR4 |= NS_TO_GLITCH(0); /* Ultra */
-
- if (wval1 >= 8)
- {
- wval1--; /* Timing computation differs by 1 from FAST_SCSI */
- bval = FAST_CLK; /* fast clock / normal scsi */
- pDCB->CtrlR4 |= pACB->glitch_cfg; /* glitch eater */
- }
-
- pDCB->CtrlR3 = bval;
- pDCB->SyncPeriod = (u8)wval1;
-
- if ((oldsyncperiod != wval1 || oldsyncoffset != pDCB->SyncOffset) && pDCB->TargetLUN == 0)
- {
- if (! (bval & FAST_SCSI)) wval1++;
- printk (KERN_INFO "DC390: Target %i: Sync transfer %i.%1i MHz, Offset %i\n", pDCB->TargetID,
- 40/wval1, ((40%wval1)*10+wval1/2)/wval1, pDCB->SyncOffset & 0x0f);
- }
-
- dc390_reprog (pACB, pDCB);
-}
-
-
-/* handle RESTORE_PTR */
-/* This doesn't look very healthy... to-be-fixed */
-static void
-dc390_restore_ptr (struct dc390_acb* pACB, struct dc390_srb* pSRB)
-{
- struct scsi_cmnd *pcmd = pSRB->pcmd;
- struct scatterlist *psgl;
- pSRB->TotalXferredLen = 0;
- pSRB->SGIndex = 0;
- if (scsi_sg_count(pcmd)) {
- size_t saved;
- pSRB->pSegmentList = scsi_sglist(pcmd);
- psgl = pSRB->pSegmentList;
- //dc390_pci_sync(pSRB);
-
- while (pSRB->TotalXferredLen + (unsigned long) sg_dma_len(psgl) < pSRB->Saved_Ptr)
- {
- pSRB->TotalXferredLen += (unsigned long) sg_dma_len(psgl);
- pSRB->SGIndex++;
- if( pSRB->SGIndex < pSRB->SGcount )
- {
- pSRB->pSegmentList++;
-
- dc390_start_segment(pSRB);
- }
- else
- pSRB->SGToBeXferLen = 0;
- }
-
- saved = pSRB->Saved_Ptr - pSRB->TotalXferredLen;
- pSRB->SGToBeXferLen -= saved;
- pSRB->SGBusAddr += saved;
- printk (KERN_INFO "DC390: Pointer restored. Segment %i, Total %li, Bus %08lx\n",
- pSRB->SGIndex, pSRB->Saved_Ptr, pSRB->SGBusAddr);
-
- } else {
- pSRB->SGcount = 0;
- printk (KERN_INFO "DC390: RESTORE_PTR message for Transfer without Scatter-Gather ??\n");
- }
-
- pSRB->TotalXferredLen = pSRB->Saved_Ptr;
-}
-
-
-/* According to the docs, the AM53C974 reads the message and
- * generates a Successful Operation IRQ before asserting ACK for
- * the last byte (how does it know whether it's the last ?) */
-/* The old code handled it in another way, indicating, that on
- * every message byte an IRQ is generated and every byte has to
- * be manually ACKed. Hmmm ? (KG, 98/11/28) */
-/* The old implementation was correct. Sigh! */
-
-/* Check if the message is complete */
-static u8 __inline__
-dc390_MsgIn_complete (u8 *msgbuf, u32 len)
-{
- if (*msgbuf == EXTENDED_MESSAGE)
- {
- if (len < 2) return 0;
- if (len < msgbuf[1] + 2) return 0;
- }
- else if (*msgbuf >= 0x20 && *msgbuf <= 0x2f) // two byte messages
- if (len < 2) return 0;
- return 1;
-}
-
-
-
-/* read and eval received messages */
-static void
-dc390_MsgIn_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
-{
- struct dc390_dcb* pDCB = pACB->pActiveDCB;
-
- /* Read the msg */
-
- pSRB->MsgInBuf[pACB->MsgLen++] = DC390_read8 (ScsiFifo);
- //pSRB->SRBState = 0;
-
- /* Msg complete ? */
- if (dc390_MsgIn_complete (pSRB->MsgInBuf, pACB->MsgLen))
- {
- DEBUG0 (printk (KERN_INFO "DC390: MsgIn:"); dc390_printMsg (pSRB->MsgInBuf, pACB->MsgLen));
- /* Now eval the msg */
- switch (pSRB->MsgInBuf[0])
- {
- case DISCONNECT:
- pSRB->SRBState = SRB_DISCONNECT; break;
-
- case SIMPLE_QUEUE_TAG:
- case HEAD_OF_QUEUE_TAG:
- case ORDERED_QUEUE_TAG:
- pSRB = dc390_MsgIn_QTag (pACB, pDCB, pSRB->MsgInBuf[1]);
- break;
-
- case MESSAGE_REJECT:
- DC390_write8 (ScsiCmd, RESET_ATN_CMD);
- pDCB->NegoPeriod = 50; /* 200ns <=> 5 MHz */
- if( pSRB->SRBState & DO_SYNC_NEGO)
- dc390_MsgIn_set_async (pACB, pSRB);
- break;
-
- case EXTENDED_MESSAGE:
- /* reject every extended msg but SDTR */
- if (pSRB->MsgInBuf[1] != 3 || pSRB->MsgInBuf[2] != EXTENDED_SDTR)
- dc390_MsgIn_reject (pACB, pSRB);
- else
- {
- if (pSRB->MsgInBuf[3] == 0 || pSRB->MsgInBuf[4] == 0)
- dc390_MsgIn_set_async (pACB, pSRB);
- else
- dc390_MsgIn_set_sync (pACB, pSRB);
- }
-
- // nothing has to be done
- case COMMAND_COMPLETE: break;
-
- // SAVE POINTER may be ignored as we have the struct dc390_srb* associated with the
- // scsi command. Thanks, Gerard, for pointing it out.
- case SAVE_POINTERS:
- pSRB->Saved_Ptr = pSRB->TotalXferredLen;
- break;
- // The device might want to restart transfer with a RESTORE
- case RESTORE_POINTERS:
- DEBUG0(printk ("DC390: RESTORE POINTER message received ... try to handle\n"));
- dc390_restore_ptr (pACB, pSRB);
- break;
-
- // reject unknown messages
- default: dc390_MsgIn_reject (pACB, pSRB);
- }
-
- /* Clear counter and MsgIn state */
- pSRB->SRBState &= ~SRB_MSGIN;
- pACB->MsgLen = 0;
- }
-
- *psstatus = SCSI_NOP0;
- DC390_write8 (ScsiCmd, MSG_ACCEPTED_CMD);
- //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
-}
-
-
-static void
-dc390_DataIO_Comm( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 ioDir)
-{
- unsigned long lval;
- struct dc390_dcb* pDCB = pACB->pActiveDCB;
-
- if (pSRB == pACB->pTmpSRB)
- {
- if (pDCB)
- printk(KERN_ERR "DC390: pSRB == pTmpSRB! (TagQ Error?) (%02i-%i)\n", pDCB->TargetID, pDCB->TargetLUN);
- else
- printk(KERN_ERR "DC390: pSRB == pTmpSRB! (TagQ Error?) (DCB 0!)\n");
-
- /* Try to recover - some broken disks react badly to tagged INQUIRY */
- if (pDCB && pACB->scan_devices && pDCB->GoingSRBCnt == 1) {
- pSRB = pDCB->pGoingSRB;
- pDCB->pActiveSRB = pSRB;
- } else {
- pSRB->pSRBDCB = pDCB;
- dc390_EnableMsgOut_Abort(pACB, pSRB);
- if (pDCB)
- pDCB->DCBFlag |= ABORT_DEV;
- return;
- }
- }
-
- if( pSRB->SGIndex < pSRB->SGcount )
- {
- DC390_write8 (DMA_Cmd, DMA_IDLE_CMD | ioDir);
- if( !pSRB->SGToBeXferLen )
- {
- dc390_start_segment(pSRB);
-
- DEBUG1(printk (KERN_DEBUG " DC390: Next SG segment."));
- }
- lval = pSRB->SGToBeXferLen;
- DEBUG1(printk (KERN_DEBUG " DC390: Start transfer: %li bytes (address %08lx)\n", lval, pSRB->SGBusAddr));
- DC390_write8 (CtcReg_Low, (u8) lval);
- lval >>= 8;
- DC390_write8 (CtcReg_Mid, (u8) lval);
- lval >>= 8;
- DC390_write8 (CtcReg_High, (u8) lval);
-
- DC390_write32 (DMA_XferCnt, pSRB->SGToBeXferLen);
- DC390_write32 (DMA_XferAddr, pSRB->SGBusAddr);
-
- //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD | ioDir);
- pSRB->SRBState = SRB_DATA_XFER;
-
- DC390_write8 (ScsiCmd, DMA_COMMAND+INFO_XFER_CMD);
-
- DC390_write8 (DMA_Cmd, DMA_START_CMD | ioDir);
- //DEBUG1(DC390_write32 (DMA_ScsiBusCtrl, WRT_ERASE_DMA_STAT | EN_INT_ON_PCI_ABORT));
- //DEBUG1(printk (KERN_DEBUG "DC390: DMA_Status: %02x\n", DC390_read8 (DMA_Status)));
- //DEBUG1(DC390_write32 (DMA_ScsiBusCtrl, EN_INT_ON_PCI_ABORT));
- }
- else /* xfer pad */
- {
- if( pSRB->SGcount )
- {
- pSRB->AdaptStatus = H_OVER_UNDER_RUN;
- pSRB->SRBStatus |= OVER_RUN;
- DEBUG0(printk (KERN_WARNING " DC390: Overrun -"));
- }
- DEBUG0(printk (KERN_WARNING " Clear transfer pad \n"));
- DC390_write8 (CtcReg_Low, 0);
- DC390_write8 (CtcReg_Mid, 0);
- DC390_write8 (CtcReg_High, 0);
-
- pSRB->SRBState |= SRB_XFERPAD;
- DC390_write8 (ScsiCmd, DMA_COMMAND+XFER_PAD_BYTE);
-/*
- DC390_write8 (DMA_Cmd, DMA_IDLE_CMD | ioDir);
- DC390_write8 (DMA_Cmd, DMA_START_CMD | ioDir);
-*/
- }
-}
-
-
-static void
-dc390_DataOutPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
-{
- dc390_DataIO_Comm (pACB, pSRB, WRITE_DIRECTION);
-}
-
-static void
-dc390_DataInPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
-{
- dc390_DataIO_Comm (pACB, pSRB, READ_DIRECTION);
-}
-
-static void
-dc390_CommandPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
-{
- struct dc390_dcb* pDCB;
- u8 i, cnt;
- u8 *ptr;
-
- DC390_write8 (ScsiCmd, RESET_ATN_CMD);
- DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
- if( !(pSRB->SRBFlag & AUTO_REQSENSE) )
- {
- cnt = (u8) pSRB->pcmd->cmd_len;
- ptr = (u8 *) pSRB->pcmd->cmnd;
- for(i=0; i < cnt; i++)
- DC390_write8 (ScsiFifo, *(ptr++));
- }
- else
- {
- DC390_write8 (ScsiFifo, REQUEST_SENSE);
- pDCB = pACB->pActiveDCB;
- DC390_write8 (ScsiFifo, pDCB->TargetLUN << 5);
- DC390_write8 (ScsiFifo, 0);
- DC390_write8 (ScsiFifo, 0);
- DC390_write8 (ScsiFifo, SCSI_SENSE_BUFFERSIZE);
- DC390_write8 (ScsiFifo, 0);
- DEBUG0(printk(KERN_DEBUG "DC390: AutoReqSense (CmndPhase)!\n"));
- }
- pSRB->SRBState = SRB_COMMAND;
- DC390_write8 (ScsiCmd, INFO_XFER_CMD);
-}
-
-static void
-dc390_StatusPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
-{
- DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
- pSRB->SRBState = SRB_STATUS;
- DC390_write8 (ScsiCmd, INITIATOR_CMD_CMPLTE);
- //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
-}
-
-static void
-dc390_MsgOutPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
-{
- u8 bval, i, cnt;
- u8 *ptr;
- struct dc390_dcb* pDCB;
-
- DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
- pDCB = pACB->pActiveDCB;
- if( !(pSRB->SRBState & SRB_MSGOUT) )
- {
- cnt = pSRB->MsgCnt;
- if( cnt )
- {
- ptr = (u8 *) pSRB->MsgOutBuf;
- for(i=0; i < cnt; i++)
- DC390_write8 (ScsiFifo, *(ptr++));
- pSRB->MsgCnt = 0;
- if( (pDCB->DCBFlag & ABORT_DEV_) &&
- (pSRB->MsgOutBuf[0] == ABORT) )
- pSRB->SRBState = SRB_ABORT_SENT;
- }
- else
- {
- bval = ABORT; /* ??? MSG_NOP */
- if( (pSRB->pcmd->cmnd[0] == INQUIRY ) ||
- (pSRB->pcmd->cmnd[0] == REQUEST_SENSE) ||
- (pSRB->SRBFlag & AUTO_REQSENSE) )
- {
- if( pDCB->SyncMode & SYNC_ENABLE )
- goto mop1;
- }
- DC390_write8 (ScsiFifo, bval);
- }
- DC390_write8 (ScsiCmd, INFO_XFER_CMD);
- }
- else
- {
-mop1:
- printk (KERN_ERR "DC390: OLD Sync Nego code triggered! (%i %i)\n", pDCB->TargetID, pDCB->TargetLUN);
- DC390_write8 (ScsiFifo, EXTENDED_MESSAGE);
- DC390_write8 (ScsiFifo, 3); /* ;length of extended msg */
- DC390_write8 (ScsiFifo, EXTENDED_SDTR); /* ; sync nego */
- DC390_write8 (ScsiFifo, pDCB->NegoPeriod);
- if (pDCB->SyncOffset & 0x0f)
- DC390_write8 (ScsiFifo, pDCB->SyncOffset);
- else
- DC390_write8 (ScsiFifo, SYNC_NEGO_OFFSET);
- pSRB->SRBState |= DO_SYNC_NEGO;
- DC390_write8 (ScsiCmd, INFO_XFER_CMD);
- }
-}
-
-static void
-dc390_MsgInPhase( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
-{
- DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
- if( !(pSRB->SRBState & SRB_MSGIN) )
- {
- pSRB->SRBState &= ~SRB_DISCONNECT;
- pSRB->SRBState |= SRB_MSGIN;
- }
- DC390_write8 (ScsiCmd, INFO_XFER_CMD);
- //DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
-}
-
-static void
-dc390_Nop_0( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
-{
-}
-
-static void
-dc390_Nop_1( struct dc390_acb* pACB, struct dc390_srb* pSRB, u8 *psstatus)
-{
-}
-
-
-static void
-dc390_SetXferRate( struct dc390_acb* pACB, struct dc390_dcb* pDCB )
-{
- u8 bval, i, cnt;
- struct dc390_dcb* ptr;
-
- if( !(pDCB->TargetLUN) )
- {
- if( !pACB->scan_devices )
- {
- ptr = pACB->pLinkDCB;
- cnt = pACB->DCBCnt;
- bval = pDCB->TargetID;
- for(i=0; i<cnt; i++)
- {
- if( ptr->TargetID == bval )
- {
- ptr->SyncPeriod = pDCB->SyncPeriod;
- ptr->SyncOffset = pDCB->SyncOffset;
- ptr->CtrlR3 = pDCB->CtrlR3;
- ptr->CtrlR4 = pDCB->CtrlR4;
- ptr->SyncMode = pDCB->SyncMode;
- }
- ptr = ptr->pNextDCB;
- }
- }
- }
- return;
-}
-
-
-static void
-dc390_Disconnect( struct dc390_acb* pACB )
-{
- struct dc390_dcb *pDCB;
- struct dc390_srb *pSRB, *psrb;
- u8 i, cnt;
-
- DEBUG0(printk(KERN_INFO "DISC,"));
-
- if (!pACB->Connected) printk(KERN_ERR "DC390: Disconnect not-connected bus?\n");
- pACB->Connected = 0;
- pDCB = pACB->pActiveDCB;
- if (!pDCB)
- {
- DEBUG0(printk(KERN_ERR "ACB:%p->ActiveDCB:%p IOPort:%04x IRQ:%02x !\n",\
- pACB, pDCB, pACB->IOPortBase, pACB->IRQLevel));
- mdelay(400);
- DC390_read8 (INT_Status); /* Reset Pending INT */
- DC390_write8 (ScsiCmd, EN_SEL_RESEL);
- return;
- }
- DC390_write8 (ScsiCmd, EN_SEL_RESEL);
- pSRB = pDCB->pActiveSRB;
- pACB->pActiveDCB = NULL;
- pSRB->ScsiPhase = SCSI_NOP0;
- if( pSRB->SRBState & SRB_UNEXPECT_RESEL )
- pSRB->SRBState = 0;
- else if( pSRB->SRBState & SRB_ABORT_SENT )
- {
- pDCB->TagMask = 0;
- pDCB->DCBFlag = 0;
- cnt = pDCB->GoingSRBCnt;
- pDCB->GoingSRBCnt = 0;
- pSRB = pDCB->pGoingSRB;
- for( i=0; i < cnt; i++)
- {
- psrb = pSRB->pNextSRB;
- dc390_Free_insert (pACB, pSRB);
- pSRB = psrb;
- }
- pDCB->pGoingSRB = NULL;
- }
- else
- {
- if( (pSRB->SRBState & (SRB_START_+SRB_MSGOUT)) ||
- !(pSRB->SRBState & (SRB_DISCONNECT+SRB_COMPLETED)) )
- { /* Selection time out */
- pSRB->AdaptStatus = H_SEL_TIMEOUT;
- pSRB->TargetStatus = 0;
- goto disc1;
- }
- else if (!(pSRB->SRBState & SRB_DISCONNECT) && (pSRB->SRBState & SRB_COMPLETED))
- {
-disc1:
- dc390_freetag (pDCB, pSRB);
- pDCB->pActiveSRB = NULL;
- pSRB->SRBState = SRB_FREE;
- dc390_SRBdone( pACB, pDCB, pSRB);
- }
- }
- pACB->MsgLen = 0;
-}
-
-
-static void
-dc390_Reselect( struct dc390_acb* pACB )
-{
- struct dc390_dcb* pDCB;
- struct dc390_srb* pSRB;
- u8 id, lun;
-
- DEBUG0(printk(KERN_INFO "RSEL,"));
- pACB->Connected = 1;
- pDCB = pACB->pActiveDCB;
- if( pDCB )
- { /* Arbitration lost but Reselection won */
- DEBUG0(printk ("DC390: (ActiveDCB != 0: Arb. lost but resel. won)!\n"));
- pSRB = pDCB->pActiveSRB;
- if( !( pACB->scan_devices ) )
- {
- struct scsi_cmnd *pcmd = pSRB->pcmd;
- scsi_set_resid(pcmd, scsi_bufflen(pcmd));
- SET_RES_DID(pcmd->result, DID_SOFT_ERROR);
- dc390_Going_remove(pDCB, pSRB);
- dc390_Free_insert(pACB, pSRB);
- pcmd->scsi_done (pcmd);
- DEBUG0(printk(KERN_DEBUG"DC390: Return SRB %p to free\n", pSRB));
- }
- }
- /* Get ID */
- lun = DC390_read8 (ScsiFifo);
- DEBUG0(printk ("Dev %02x,", lun));
- if (!(lun & (1 << pACB->pScsiHost->this_id)))
- printk (KERN_ERR "DC390: Reselection must select host adapter: %02x!\n", lun);
- else
- lun ^= 1 << pACB->pScsiHost->this_id; /* Mask AdapterID */
- id = 0; while (lun >>= 1) id++;
- /* Get LUN */
- lun = DC390_read8 (ScsiFifo);
- if (!(lun & IDENTIFY_BASE)) printk (KERN_ERR "DC390: Resel: Expect identify message!\n");
- lun &= 7;
- DEBUG0(printk ("(%02i-%i),", id, lun));
- pDCB = dc390_findDCB (pACB, id, lun);
- if (!pDCB)
- {
- printk (KERN_ERR "DC390: Reselect from non existing device (%02i-%i)\n",
- id, lun);
- return;
- }
- pACB->pActiveDCB = pDCB;
- /* TagQ: We expect a message soon, so never mind the exact SRB */
- if( pDCB->SyncMode & EN_TAG_QUEUEING )
- {
- pSRB = pACB->pTmpSRB;
- pDCB->pActiveSRB = pSRB;
- }
- else
- {
- pSRB = pDCB->pActiveSRB;
- if( !pSRB || !(pSRB->SRBState & SRB_DISCONNECT) )
- {
- pSRB= pACB->pTmpSRB;
- pSRB->SRBState = SRB_UNEXPECT_RESEL;
- printk (KERN_ERR "DC390: Reselect without outstanding cmnd (%02i-%i)\n",
- id, lun);
- pDCB->pActiveSRB = pSRB;
- dc390_EnableMsgOut_Abort ( pACB, pSRB );
- }
- else
- {
- if( pDCB->DCBFlag & ABORT_DEV_ )
- {
- pSRB->SRBState = SRB_ABORT_SENT;
- printk (KERN_INFO "DC390: Reselect: Abort (%02i-%i)\n",
- id, lun);
- dc390_EnableMsgOut_Abort( pACB, pSRB );
- }
- else
- pSRB->SRBState = SRB_DATA_XFER;
- }
- }
-
- DEBUG1(printk (KERN_DEBUG "Resel SRB(%p): TagNum (%02x)\n", pSRB, pSRB->TagNumber));
- pSRB->ScsiPhase = SCSI_NOP0;
- DC390_write8 (Scsi_Dest_ID, pDCB->TargetID);
- DC390_write8 (Sync_Period, pDCB->SyncPeriod);
- DC390_write8 (Sync_Offset, pDCB->SyncOffset);
- DC390_write8 (CtrlReg1, pDCB->CtrlR1);
- DC390_write8 (CtrlReg3, pDCB->CtrlR3);
- DC390_write8 (CtrlReg4, pDCB->CtrlR4); /* ; Glitch eater */
- DC390_write8 (ScsiCmd, MSG_ACCEPTED_CMD); /* ;to release the /ACK signal */
-}
-
-static int __inline__
-dc390_RequestSense(struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb* pSRB)
-{
- struct scsi_cmnd *pcmd;
-
- pcmd = pSRB->pcmd;
-
- REMOVABLEDEBUG(printk(KERN_INFO "DC390: RequestSense(Cmd %02x, Id %02x, LUN %02x)\n",\
- pcmd->cmnd[0], pDCB->TargetID, pDCB->TargetLUN));
-
- pSRB->SRBFlag |= AUTO_REQSENSE;
- pSRB->SavedTotXLen = pSRB->TotalXferredLen;
- pSRB->AdaptStatus = 0;
- pSRB->TargetStatus = 0; /* CHECK_CONDITION<<1; */
-
- /* We are called from SRBdone, original PCI mapping has been removed
- * already, new one is set up from StartSCSI */
- pSRB->SGIndex = 0;
-
- pSRB->TotalXferredLen = 0;
- pSRB->SGToBeXferLen = 0;
- return dc390_StartSCSI(pACB, pDCB, pSRB);
-}
-
-
-static void
-dc390_SRBdone( struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb* pSRB )
-{
- u8 status;
- struct scsi_cmnd *pcmd;
-
- pcmd = pSRB->pcmd;
- /* KG: Moved pci_unmap here */
- dc390_pci_unmap(pSRB);
-
- status = pSRB->TargetStatus;
-
- DEBUG0(printk (" SRBdone (%02x,%08x), SRB %p\n", status, pcmd->result, pSRB));
- if(pSRB->SRBFlag & AUTO_REQSENSE)
- { /* Last command was a Request Sense */
- pSRB->SRBFlag &= ~AUTO_REQSENSE;
- pSRB->AdaptStatus = 0;
- pSRB->TargetStatus = SAM_STAT_CHECK_CONDITION;
-
- //pcmd->result = MK_RES(DRIVER_SENSE,DID_OK,0,status);
- if (status == SAM_STAT_CHECK_CONDITION)
- pcmd->result = MK_RES_LNX(0, DID_BAD_TARGET, 0, /*CHECK_CONDITION*/0);
- else /* Retry */
- {
- if( pSRB->pcmd->cmnd[0] == TEST_UNIT_READY /* || pSRB->pcmd->cmnd[0] == START_STOP */)
- {
- /* Don't retry on TEST_UNIT_READY */
- pcmd->result = MK_RES_LNX(DRIVER_SENSE, DID_OK, 0, SAM_STAT_CHECK_CONDITION);
- REMOVABLEDEBUG(printk(KERN_INFO "Cmd=%02x, Result=%08x, XferL=%08x\n",pSRB->pcmd->cmnd[0],\
- (u32) pcmd->result, (u32) pSRB->TotalXferredLen));
- } else {
- SET_RES_DRV(pcmd->result, DRIVER_SENSE);
- //pSRB->ScsiCmdLen = (u8) (pSRB->Segment1[0] >> 8);
- DEBUG0 (printk ("DC390: RETRY (%02x), target %02i-%02i\n", pcmd->cmnd[0], pcmd->device->id, (u8)pcmd->device->lun));
- pSRB->TotalXferredLen = 0;
- SET_RES_DID(pcmd->result, DID_SOFT_ERROR);
- }
- }
- goto cmd_done;
- }
- if( status )
- {
- if (status == SAM_STAT_CHECK_CONDITION)
- {
- if (dc390_RequestSense(pACB, pDCB, pSRB)) {
- SET_RES_DID(pcmd->result, DID_ERROR);
- goto cmd_done;
- }
- return;
- }
- else if (status == SAM_STAT_TASK_SET_FULL)
- {
- scsi_track_queue_full(pcmd->device, pDCB->GoingSRBCnt - 1);
- DEBUG0 (printk ("DC390: RETRY (%02x), target %02i-%02i\n", pcmd->cmnd[0], pcmd->device->id, (u8)pcmd->device->lun));
- pSRB->TotalXferredLen = 0;
- SET_RES_DID(pcmd->result, DID_SOFT_ERROR);
- }
- else if (status == SAM_STAT_BUSY &&
- (pcmd->cmnd[0] == TEST_UNIT_READY || pcmd->cmnd[0] == INQUIRY) &&
- pACB->scan_devices)
- {
- pSRB->AdaptStatus = 0;
- pSRB->TargetStatus = status;
- pcmd->result = MK_RES(0,0,pSRB->EndMessage,/*status*/0);
- }
- else
- { /* Another error */
- pSRB->TotalXferredLen = 0;
- SET_RES_DID(pcmd->result, DID_SOFT_ERROR);
- goto cmd_done;
- }
- }
- else
- { /* Target status == 0 */
- status = pSRB->AdaptStatus;
- if (status == H_OVER_UNDER_RUN)
- {
- pSRB->TargetStatus = 0;
- SET_RES_DID(pcmd->result,DID_OK);
- SET_RES_MSG(pcmd->result,pSRB->EndMessage);
- }
- else if (status == H_SEL_TIMEOUT)
- {
- pcmd->result = MK_RES(0, DID_NO_CONNECT, 0, 0);
- /* Devices are removed below ... */
- }
- else if( pSRB->SRBStatus & PARITY_ERROR)
- {
- //pcmd->result = MK_RES(0,DID_PARITY,pSRB->EndMessage,0);
- SET_RES_DID(pcmd->result,DID_PARITY);
- SET_RES_MSG(pcmd->result,pSRB->EndMessage);
- }
- else /* No error */
- {
- pSRB->AdaptStatus = 0;
- pSRB->TargetStatus = 0;
- SET_RES_DID(pcmd->result,DID_OK);
- }
- }
-
-cmd_done:
- scsi_set_resid(pcmd, scsi_bufflen(pcmd) - pSRB->TotalXferredLen);
-
- dc390_Going_remove (pDCB, pSRB);
- /* Add to free list */
- dc390_Free_insert (pACB, pSRB);
-
- DEBUG0(printk (KERN_DEBUG "DC390: SRBdone: done\n"));
- pcmd->scsi_done (pcmd);
-
- return;
-}
-
-
-/* Remove all SRBs from Going list and inform midlevel */
-static void
-dc390_DoingSRB_Done(struct dc390_acb* pACB, struct scsi_cmnd *cmd)
-{
- struct dc390_dcb *pDCB, *pdcb;
- struct dc390_srb *psrb, *psrb2;
- int i;
- struct scsi_cmnd *pcmd;
-
- pDCB = pACB->pLinkDCB;
- pdcb = pDCB;
- if (! pdcb) return;
- do
- {
- psrb = pdcb->pGoingSRB;
- for (i = 0; i < pdcb->GoingSRBCnt; i++)
- {
- psrb2 = psrb->pNextSRB;
- pcmd = psrb->pcmd;
- dc390_Free_insert (pACB, psrb);
- psrb = psrb2;
- }
- pdcb->GoingSRBCnt = 0;
- pdcb->pGoingSRB = NULL;
- pdcb->TagMask = 0;
- pdcb = pdcb->pNextDCB;
- } while( pdcb != pDCB );
-}
-
-
-static void
-dc390_ResetSCSIBus( struct dc390_acb* pACB )
-{
- //DC390_write8 (ScsiCmd, RST_DEVICE_CMD);
- //udelay (250);
- //DC390_write8 (ScsiCmd, NOP_CMD);
-
- DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
- DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
- DC390_write8 (ScsiCmd, RST_SCSI_BUS_CMD);
- pACB->Connected = 0;
-
- return;
-}
-
-static void
-dc390_ScsiRstDetect( struct dc390_acb* pACB )
-{
- printk ("DC390: Rst_Detect: laststat = %08x\n", dc390_laststatus);
- //DEBUG0(printk(KERN_INFO "RST_DETECT,"));
-
- DC390_write8 (DMA_Cmd, DMA_IDLE_CMD);
- /* Unlock before ? */
- /* delay half a second */
- udelay (1000);
- DC390_write8 (ScsiCmd, CLEAR_FIFO_CMD);
- pACB->last_reset = jiffies + 5*HZ/2
- + HZ * dc390_eepromBuf[pACB->AdapterIndex][EE_DELAY];
- pACB->Connected = 0;
-
- if( pACB->ACBFlag & RESET_DEV )
- pACB->ACBFlag |= RESET_DONE;
- else
- { /* Reset was issued by sb else */
- pACB->ACBFlag |= RESET_DETECT;
-
- dc390_ResetDevParam( pACB );
- dc390_DoingSRB_Done( pACB, NULL);
- //dc390_RecoverSRB( pACB );
- pACB->pActiveDCB = NULL;
- pACB->ACBFlag = 0;
- }
- return;
-}
-
-static int DC390_queuecommand_lck(struct scsi_cmnd *cmd,
- void (*done)(struct scsi_cmnd *))
-{
- struct scsi_device *sdev = cmd->device;
- struct dc390_acb *acb = (struct dc390_acb *)sdev->host->hostdata;
- struct dc390_dcb *dcb = sdev->hostdata;
- struct dc390_srb *srb;
-
- if (sdev->queue_depth <= dcb->GoingSRBCnt)
- goto device_busy;
- if (acb->pActiveDCB)
- goto host_busy;
- if (acb->ACBFlag & (RESET_DETECT|RESET_DONE|RESET_DEV))
- goto host_busy;
-
- srb = acb->pFreeSRB;
- if (unlikely(srb == NULL))
- goto host_busy;
-
- cmd->scsi_done = done;
- cmd->result = 0;
- acb->Cmds++;
-
- acb->pFreeSRB = srb->pNextSRB;
- srb->pNextSRB = NULL;
-
- srb->pSRBDCB = dcb;
- srb->pcmd = cmd;
- cmd->host_scribble = (char *)srb;
-
- srb->SGIndex = 0;
- srb->AdaptStatus = 0;
- srb->TargetStatus = 0;
- srb->MsgCnt = 0;
-
- srb->SRBStatus = 0;
- srb->SRBFlag = 0;
- srb->SRBState = 0;
- srb->TotalXferredLen = 0;
- srb->SGBusAddr = 0;
- srb->SGToBeXferLen = 0;
- srb->ScsiPhase = 0;
- srb->EndMessage = 0;
- srb->TagNumber = SCSI_NO_TAG;
-
- if (dc390_StartSCSI(acb, dcb, srb)) {
- dc390_Free_insert(acb, srb);
- goto host_busy;
- }
-
- dc390_Going_append(dcb, srb);
-
- return 0;
-
- host_busy:
- return SCSI_MLQUEUE_HOST_BUSY;
-
- device_busy:
- return SCSI_MLQUEUE_DEVICE_BUSY;
-}
-
-static DEF_SCSI_QCMD(DC390_queuecommand)
-
-static void dc390_dumpinfo (struct dc390_acb* pACB, struct dc390_dcb* pDCB, struct dc390_srb* pSRB)
-{
- struct pci_dev *pdev;
- u16 pstat;
-
- if (!pDCB) pDCB = pACB->pActiveDCB;
- if (!pSRB && pDCB) pSRB = pDCB->pActiveSRB;
-
- if (pSRB)
- {
- printk ("DC390: SRB: Xferred %08lx, Remain %08lx, State %08x, Phase %02x\n",
- pSRB->TotalXferredLen, pSRB->SGToBeXferLen, pSRB->SRBState,
- pSRB->ScsiPhase);
- printk ("DC390: AdpaterStatus: %02x, SRB Status %02x\n", pSRB->AdaptStatus, pSRB->SRBStatus);
- }
- printk ("DC390: Status of last IRQ (DMA/SC/Int/IRQ): %08x\n", dc390_laststatus);
- printk ("DC390: Register dump: SCSI block:\n");
- printk ("DC390: XferCnt Cmd Stat IntS IRQS FFIS Ctl1 Ctl2 Ctl3 Ctl4\n");
- printk ("DC390: %06x %02x %02x %02x",
- DC390_read8(CtcReg_Low) + (DC390_read8(CtcReg_Mid) << 8) + (DC390_read8(CtcReg_High) << 16),
- DC390_read8(ScsiCmd), DC390_read8(Scsi_Status), DC390_read8(Intern_State));
- printk (" %02x %02x %02x %02x %02x %02x\n",
- DC390_read8(INT_Status), DC390_read8(Current_Fifo), DC390_read8(CtrlReg1),
- DC390_read8(CtrlReg2), DC390_read8(CtrlReg3), DC390_read8(CtrlReg4));
- DC390_write32 (DMA_ScsiBusCtrl, WRT_ERASE_DMA_STAT | EN_INT_ON_PCI_ABORT);
- if (DC390_read8(Current_Fifo) & 0x1f)
- {
- printk ("DC390: FIFO:");
- while (DC390_read8(Current_Fifo) & 0x1f) printk (" %02x", DC390_read8(ScsiFifo));
- printk ("\n");
- }
- printk ("DC390: Register dump: DMA engine:\n");
- printk ("DC390: Cmd STrCnt SBusA WrkBC WrkAC Stat SBusCtrl\n");
- printk ("DC390: %02x %08x %08x %08x %08x %02x %08x\n",
- DC390_read8(DMA_Cmd), DC390_read32(DMA_XferCnt), DC390_read32(DMA_XferAddr),
- DC390_read32(DMA_Wk_ByteCntr), DC390_read32(DMA_Wk_AddrCntr),
- DC390_read8(DMA_Status), DC390_read32(DMA_ScsiBusCtrl));
- DC390_write32 (DMA_ScsiBusCtrl, EN_INT_ON_PCI_ABORT);
-
- pdev = pACB->pdev;
- pci_read_config_word(pdev, PCI_STATUS, &pstat);
- printk ("DC390: Register dump: PCI Status: %04x\n", pstat);
- printk ("DC390: In case of driver trouble read Documentation/scsi/tmscsim.txt\n");
-}
-
-
-static int DC390_abort(struct scsi_cmnd *cmd)
-{
- struct dc390_acb *pACB = (struct dc390_acb*) cmd->device->host->hostdata;
- struct dc390_dcb *pDCB = (struct dc390_dcb*) cmd->device->hostdata;
-
- scmd_printk(KERN_WARNING, cmd, "DC390: Abort command\n");
-
- /* abort() is too stupid for already sent commands at the moment.
- * If it's called we are in trouble anyway, so let's dump some info
- * into the syslog at least. (KG, 98/08/20,99/06/20) */
- dc390_dumpinfo(pACB, pDCB, NULL);
-
- pDCB->DCBFlag |= ABORT_DEV_;
- printk(KERN_INFO "DC390: Aborted.\n");
-
- return FAILED;
-}
-
-
-static void dc390_ResetDevParam( struct dc390_acb* pACB )
-{
- struct dc390_dcb *pDCB, *pdcb;
-
- pDCB = pACB->pLinkDCB;
- if (! pDCB) return;
- pdcb = pDCB;
- do
- {
- pDCB->SyncMode &= ~SYNC_NEGO_DONE;
- pDCB->SyncPeriod = 0;
- pDCB->SyncOffset = 0;
- pDCB->TagMask = 0;
- pDCB->CtrlR3 = FAST_CLK;
- pDCB->CtrlR4 &= NEGATE_REQACKDATA | CTRL4_RESERVED | NEGATE_REQACK;
- pDCB->CtrlR4 |= pACB->glitch_cfg;
- pDCB = pDCB->pNextDCB;
- }
- while( pdcb != pDCB );
- pACB->ACBFlag &= ~(RESET_DEV | RESET_DONE | RESET_DETECT);
-
-}
-
-static int DC390_bus_reset (struct scsi_cmnd *cmd)
-{
- struct dc390_acb* pACB = (struct dc390_acb*) cmd->device->host->hostdata;
- u8 bval;
-
- spin_lock_irq(cmd->device->host->host_lock);
-
- bval = DC390_read8(CtrlReg1) | DIS_INT_ON_SCSI_RST;
- DC390_write8(CtrlReg1, bval); /* disable IRQ on bus reset */
-
- pACB->ACBFlag |= RESET_DEV;
- dc390_ResetSCSIBus(pACB);
-
- dc390_ResetDevParam(pACB);
- mdelay(1);
- pACB->last_reset = jiffies + 3*HZ/2
- + HZ * dc390_eepromBuf[pACB->AdapterIndex][EE_DELAY];
-
- DC390_write8(ScsiCmd, CLEAR_FIFO_CMD);
- DC390_read8(INT_Status); /* Reset Pending INT */
-
- dc390_DoingSRB_Done(pACB, cmd);
-
- pACB->pActiveDCB = NULL;
- pACB->ACBFlag = 0;
-
- bval = DC390_read8(CtrlReg1) & ~DIS_INT_ON_SCSI_RST;
- DC390_write8(CtrlReg1, bval); /* re-enable interrupt */
-
- spin_unlock_irq(cmd->device->host->host_lock);
-
- return SUCCESS;
-}
-
-/**
- * dc390_slave_alloc - Called by the scsi mid layer to tell us about a new
- * scsi device that we need to deal with.
- *
- * @scsi_device: The new scsi device that we need to handle.
- */
-static int dc390_slave_alloc(struct scsi_device *scsi_device)
-{
- struct dc390_acb *pACB = (struct dc390_acb*) scsi_device->host->hostdata;
- struct dc390_dcb *pDCB, *pDCB2 = NULL;
- uint id = scsi_device->id;
- uint lun = scsi_device->lun;
-
- pDCB = kzalloc(sizeof(struct dc390_dcb), GFP_KERNEL);
- if (!pDCB)
- return -ENOMEM;
-
- if (!pACB->DCBCnt++) {
- pACB->pLinkDCB = pDCB;
- pACB->pDCBRunRobin = pDCB;
- } else {
- pACB->pLastDCB->pNextDCB = pDCB;
- }
-
- pDCB->pNextDCB = pACB->pLinkDCB;
- pACB->pLastDCB = pDCB;
-
- pDCB->pDCBACB = pACB;
- pDCB->TargetID = id;
- pDCB->TargetLUN = lun;
-
- /*
- * Some values are for all LUNs: Copy them
- * In a clean way: We would have an own structure for a SCSI-ID
- */
- if (lun && (pDCB2 = dc390_findDCB(pACB, id, 0))) {
- pDCB->DevMode = pDCB2->DevMode;
- pDCB->SyncMode = pDCB2->SyncMode & SYNC_NEGO_DONE;
- pDCB->SyncPeriod = pDCB2->SyncPeriod;
- pDCB->SyncOffset = pDCB2->SyncOffset;
- pDCB->NegoPeriod = pDCB2->NegoPeriod;
-
- pDCB->CtrlR3 = pDCB2->CtrlR3;
- pDCB->CtrlR4 = pDCB2->CtrlR4;
- } else {
- u8 index = pACB->AdapterIndex;
- PEEprom prom = (PEEprom) &dc390_eepromBuf[index][id << 2];
-
- pDCB->DevMode = prom->EE_MODE1;
- pDCB->NegoPeriod =
- (dc390_clock_period1[prom->EE_SPEED] * 25) >> 2;
- pDCB->CtrlR3 = FAST_CLK;
- pDCB->CtrlR4 = pACB->glitch_cfg | CTRL4_RESERVED;
- if (dc390_eepromBuf[index][EE_MODE2] & ACTIVE_NEGATION)
- pDCB->CtrlR4 |= NEGATE_REQACKDATA | NEGATE_REQACK;
- }
-
- if (pDCB->DevMode & SYNC_NEGO_)
- pDCB->SyncMode |= SYNC_ENABLE;
- else {
- pDCB->SyncMode = 0;
- pDCB->SyncOffset &= ~0x0f;
- }
-
- pDCB->CtrlR1 = pACB->pScsiHost->this_id;
- if (pDCB->DevMode & PARITY_CHK_)
- pDCB->CtrlR1 |= PARITY_ERR_REPO;
-
- pACB->scan_devices = 1;
- scsi_device->hostdata = pDCB;
- return 0;
-}
-
-/**
- * dc390_slave_destroy - Called by the scsi mid layer to tell us about a
- * device that is going away.
- *
- * @scsi_device: The scsi device that we need to remove.
- */
-static void dc390_slave_destroy(struct scsi_device *scsi_device)
-{
- struct dc390_acb* pACB = (struct dc390_acb*) scsi_device->host->hostdata;
- struct dc390_dcb* pDCB = (struct dc390_dcb*) scsi_device->hostdata;
- struct dc390_dcb* pPrevDCB = pACB->pLinkDCB;
-
- pACB->scan_devices = 0;
-
- BUG_ON(pDCB->GoingSRBCnt > 1);
-
- if (pDCB == pACB->pLinkDCB) {
- if (pACB->pLastDCB == pDCB) {
- pDCB->pNextDCB = NULL;
- pACB->pLastDCB = NULL;
- }
- pACB->pLinkDCB = pDCB->pNextDCB;
- } else {
- while (pPrevDCB->pNextDCB != pDCB)
- pPrevDCB = pPrevDCB->pNextDCB;
- pPrevDCB->pNextDCB = pDCB->pNextDCB;
- if (pDCB == pACB->pLastDCB)
- pACB->pLastDCB = pPrevDCB;
- }
-
- if (pDCB == pACB->pActiveDCB)
- pACB->pActiveDCB = NULL;
- if (pDCB == pACB->pLinkDCB)
- pACB->pLinkDCB = pDCB->pNextDCB;
- if (pDCB == pACB->pDCBRunRobin)
- pACB->pDCBRunRobin = pDCB->pNextDCB;
- kfree(pDCB);
-
- pACB->DCBCnt--;
-}
-
-static int dc390_slave_configure(struct scsi_device *sdev)
-{
- struct dc390_acb *acb = (struct dc390_acb *)sdev->host->hostdata;
- struct dc390_dcb *dcb = (struct dc390_dcb *)sdev->hostdata;
-
- acb->scan_devices = 0;
- if (sdev->tagged_supported && (dcb->DevMode & TAG_QUEUEING_)) {
- dcb->SyncMode |= EN_TAG_QUEUEING;
- scsi_activate_tcq(sdev, acb->TagMaxNum);
- }
-
- return 0;
-}
-
-static struct scsi_host_template driver_template = {
- .module = THIS_MODULE,
- .proc_name = "tmscsim",
- .name = DC390_BANNER " V" DC390_VERSION,
- .slave_alloc = dc390_slave_alloc,
- .slave_configure = dc390_slave_configure,
- .slave_destroy = dc390_slave_destroy,
- .queuecommand = DC390_queuecommand,
- .eh_abort_handler = DC390_abort,
- .eh_bus_reset_handler = DC390_bus_reset,
- .can_queue = 1,
- .this_id = 7,
- .sg_tablesize = SG_ALL,
- .cmd_per_lun = 1,
- .use_clustering = ENABLE_CLUSTERING,
- .max_sectors = 0x4000, /* 8MiB = 16 * 1024 * 512 */
-};
-
-/***********************************************************************
- * Functions for access to DC390 EEPROM
- * and some to emulate it
- *
- **********************************************************************/
-
-static void dc390_eeprom_prepare_read(struct pci_dev *pdev, u8 cmd)
-{
- u8 carryFlag = 1, j = 0x80, bval;
- int i;
-
- for (i = 0; i < 9; i++) {
- if (carryFlag) {
- pci_write_config_byte(pdev, 0x80, 0x40);
- bval = 0xc0;
- } else
- bval = 0x80;
-
- udelay(160);
- pci_write_config_byte(pdev, 0x80, bval);
- udelay(160);
- pci_write_config_byte(pdev, 0x80, 0);
- udelay(160);
-
- carryFlag = (cmd & j) ? 1 : 0;
- j >>= 1;
- }
-}
-
-static u16 dc390_eeprom_get_data(struct pci_dev *pdev)
-{
- int i;
- u16 wval = 0;
- u8 bval;
-
- for (i = 0; i < 16; i++) {
- wval <<= 1;
-
- pci_write_config_byte(pdev, 0x80, 0x80);
- udelay(160);
- pci_write_config_byte(pdev, 0x80, 0x40);
- udelay(160);
- pci_read_config_byte(pdev, 0x00, &bval);
-
- if (bval == 0x22)
- wval |= 1;
- }
-
- return wval;
-}
-
-static void dc390_read_eeprom(struct pci_dev *pdev, u16 *ptr)
-{
- u8 cmd = EEPROM_READ, i;
-
- for (i = 0; i < 0x40; i++) {
- pci_write_config_byte(pdev, 0xc0, 0);
- udelay(160);
-
- dc390_eeprom_prepare_read(pdev, cmd++);
- *ptr++ = dc390_eeprom_get_data(pdev);
-
- pci_write_config_byte(pdev, 0x80, 0);
- pci_write_config_byte(pdev, 0x80, 0);
- udelay(160);
- }
-}
-
-/* Override EEprom values with explicitly set values */
-static void dc390_eeprom_override(u8 index)
-{
- u8 *ptr = (u8 *) dc390_eepromBuf[index], id;
-
- /* Adapter Settings */
- if (tmscsim[0] != -2)
- ptr[EE_ADAPT_SCSI_ID] = (u8)tmscsim[0]; /* Adapter ID */
- if (tmscsim[3] != -2)
- ptr[EE_MODE2] = (u8)tmscsim[3];
- if (tmscsim[5] != -2)
- ptr[EE_DELAY] = tmscsim[5]; /* Reset delay */
- if (tmscsim[4] != -2)
- ptr[EE_TAG_CMD_NUM] = (u8)tmscsim[4]; /* Tagged Cmds */
-
- /* Device Settings */
- for (id = 0; id < MAX_SCSI_ID; id++) {
- if (tmscsim[2] != -2)
- ptr[id << 2] = (u8)tmscsim[2]; /* EE_MODE1 */
- if (tmscsim[1] != -2)
- ptr[(id << 2) + 1] = (u8)tmscsim[1]; /* EE_Speed */
- }
-}
-
-static int tmscsim_def[] = {
- 7,
- 0 /* 10MHz */,
- PARITY_CHK_ | SEND_START_ | EN_DISCONNECT_ | SYNC_NEGO_ | TAG_QUEUEING_,
- MORE2_DRV | GREATER_1G | RST_SCSI_BUS | ACTIVE_NEGATION | LUN_CHECK,
- 3 /* 16 Tags per LUN */,
- 1 /* s delay after Reset */,
-};
-
-/* Copy defaults over set values where missing */
-static void dc390_fill_with_defaults (void)
-{
- int i;
-
- for (i = 0; i < 6; i++) {
- if (tmscsim[i] < 0 || tmscsim[i] > 255)
- tmscsim[i] = tmscsim_def[i];
- }
-
- /* Sanity checks */
- if (tmscsim[0] > 7)
- tmscsim[0] = 7;
- if (tmscsim[1] > 7)
- tmscsim[1] = 4;
- if (tmscsim[4] > 5)
- tmscsim[4] = 4;
- if (tmscsim[5] > 180)
- tmscsim[5] = 180;
-}
-
-static void dc390_check_eeprom(struct pci_dev *pdev, u8 index)
-{
- u8 interpd[] = {1, 3, 5, 10, 16, 30, 60, 120};
- u8 EEbuf[128];
- u16 *ptr = (u16 *)EEbuf, wval = 0;
- int i;
-
- dc390_read_eeprom(pdev, ptr);
- memcpy(dc390_eepromBuf[index], EEbuf, EE_ADAPT_SCSI_ID);
- memcpy(&dc390_eepromBuf[index][EE_ADAPT_SCSI_ID],
- &EEbuf[REAL_EE_ADAPT_SCSI_ID], EE_LEN - EE_ADAPT_SCSI_ID);
-
- dc390_eepromBuf[index][EE_DELAY] = interpd[dc390_eepromBuf[index][EE_DELAY]];
-
- for (i = 0; i < 0x40; i++, ptr++)
- wval += *ptr;
-
- /* no Tekram EEprom found */
- if (wval != 0x1234) {
- int speed;
-
- printk(KERN_INFO "DC390_init: No EEPROM found! Trying default settings ...\n");
-
- /*
- * XXX(hch): bogus, because we might have tekram and
- * non-tekram hbas in a single machine.
- */
- dc390_fill_with_defaults();
-
- speed = dc390_clock_speed[tmscsim[1]];
- printk(KERN_INFO "DC390: Used defaults: AdaptID=%i, SpeedIdx=%i (%i.%i MHz), "
- "DevMode=0x%02x, AdaptMode=0x%02x, TaggedCmnds=%i (%i), DelayReset=%is\n",
- tmscsim[0], tmscsim[1], speed / 10, speed % 10,
- (u8)tmscsim[2], (u8)tmscsim[3], tmscsim[4], 2 << (tmscsim[4]), tmscsim[5]);
- }
-}
-
-static void dc390_init_hw(struct dc390_acb *pACB, u8 index)
-{
- struct Scsi_Host *shost = pACB->pScsiHost;
- u8 dstate;
-
- /* Disable SCSI bus reset interrupt */
- DC390_write8(CtrlReg1, DIS_INT_ON_SCSI_RST | shost->this_id);
-
- if (pACB->Gmode2 & RST_SCSI_BUS) {
- dc390_ResetSCSIBus(pACB);
- udelay(1000);
- pACB->last_reset = jiffies + HZ/2 +
- HZ * dc390_eepromBuf[pACB->AdapterIndex][EE_DELAY];
- }
-
- pACB->ACBFlag = 0;
-
- /* Reset Pending INT */
- DC390_read8(INT_Status);
-
- /* 250ms selection timeout */
- DC390_write8(Scsi_TimeOut, SEL_TIMEOUT);
-
- /* Conversion factor = 0 , 40MHz clock */
- DC390_write8(Clk_Factor, CLK_FREQ_40MHZ);
-
- /* NOP cmd - clear command register */
- DC390_write8(ScsiCmd, NOP_CMD);
-
- /* Enable Feature and SCSI-2 */
- DC390_write8(CtrlReg2, EN_FEATURE+EN_SCSI2_CMD);
-
- /* Fast clock */
- DC390_write8(CtrlReg3, FAST_CLK);
-
- /* Negation */
- DC390_write8(CtrlReg4, pACB->glitch_cfg | /* glitch eater */
- (dc390_eepromBuf[index][EE_MODE2] & ACTIVE_NEGATION) ?
- NEGATE_REQACKDATA : 0);
-
- /* Clear Transfer Count High: ID */
- DC390_write8(CtcReg_High, 0);
- DC390_write8(DMA_Cmd, DMA_IDLE_CMD);
- DC390_write8(ScsiCmd, CLEAR_FIFO_CMD);
- DC390_write32(DMA_ScsiBusCtrl, EN_INT_ON_PCI_ABORT);
-
- dstate = DC390_read8(DMA_Status);
- DC390_write8(DMA_Status, dstate);
-}
-
-static int dc390_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
-{
- struct dc390_acb *pACB;
- struct Scsi_Host *shost;
- unsigned long io_port;
- int error = -ENODEV, i;
-
- if (pci_enable_device(pdev))
- goto out;
-
- pci_set_master(pdev);
-
- error = -ENOMEM;
- if (disable_clustering)
- driver_template.use_clustering = DISABLE_CLUSTERING;
- shost = scsi_host_alloc(&driver_template, sizeof(struct dc390_acb));
- if (!shost)
- goto out_disable_device;
-
- pACB = (struct dc390_acb *)shost->hostdata;
- memset(pACB, 0, sizeof(struct dc390_acb));
-
- dc390_check_eeprom(pdev, dc390_adapterCnt);
- dc390_eeprom_override(dc390_adapterCnt);
-
- io_port = pci_resource_start(pdev, 0);
-
- shost->this_id = dc390_eepromBuf[dc390_adapterCnt][EE_ADAPT_SCSI_ID];
- shost->io_port = io_port;
- shost->n_io_port = 0x80;
- shost->irq = pdev->irq;
- shost->base = io_port;
- shost->unique_id = io_port;
-
- pACB->last_reset = jiffies;
- pACB->pScsiHost = shost;
- pACB->IOPortBase = (u16) io_port;
- pACB->IRQLevel = pdev->irq;
-
- shost->max_id = 8;
-
- if (shost->max_id - 1 ==
- dc390_eepromBuf[dc390_adapterCnt][EE_ADAPT_SCSI_ID])
- shost->max_id--;
-
- if (dc390_eepromBuf[dc390_adapterCnt][EE_MODE2] & LUN_CHECK)
- shost->max_lun = 8;
- else
- shost->max_lun = 1;
-
- pACB->pFreeSRB = pACB->SRB_array;
- pACB->SRBCount = MAX_SRB_CNT;
- pACB->AdapterIndex = dc390_adapterCnt;
- pACB->TagMaxNum =
- 2 << dc390_eepromBuf[dc390_adapterCnt][EE_TAG_CMD_NUM];
- pACB->Gmode2 = dc390_eepromBuf[dc390_adapterCnt][EE_MODE2];
-
- for (i = 0; i < pACB->SRBCount-1; i++)
- pACB->SRB_array[i].pNextSRB = &pACB->SRB_array[i+1];
- pACB->SRB_array[pACB->SRBCount-1].pNextSRB = NULL;
- pACB->pTmpSRB = &pACB->TmpSRB;
-
- pACB->sel_timeout = SEL_TIMEOUT;
- pACB->glitch_cfg = EATER_25NS;
- pACB->pdev = pdev;
-
- if (!request_region(io_port, shost->n_io_port, "tmscsim")) {
- printk(KERN_ERR "DC390: register IO ports error!\n");
- goto out_host_put;
- }
-
- /* Reset Pending INT */
- DC390_read8_(INT_Status, io_port);
-
- if (request_irq(pdev->irq, do_DC390_Interrupt, IRQF_SHARED,
- "tmscsim", pACB)) {
- printk(KERN_ERR "DC390: register IRQ error!\n");
- goto out_release_region;
- }
-
- dc390_init_hw(pACB, dc390_adapterCnt);
-
- dc390_adapterCnt++;
-
- pci_set_drvdata(pdev, shost);
-
- error = scsi_add_host(shost, &pdev->dev);
- if (error)
- goto out_free_irq;
- scsi_scan_host(shost);
- return 0;
-
- out_free_irq:
- free_irq(pdev->irq, pACB);
- out_release_region:
- release_region(io_port, shost->n_io_port);
- out_host_put:
- scsi_host_put(shost);
- out_disable_device:
- pci_disable_device(pdev);
- out:
- return error;
-}
-
-/**
- * dc390_remove_one - Called to remove a single instance of the adapter.
- *
- * @dev: The PCI device to remove.
- */
-static void dc390_remove_one(struct pci_dev *dev)
-{
- struct Scsi_Host *scsi_host = pci_get_drvdata(dev);
- unsigned long iflags;
- struct dc390_acb* pACB = (struct dc390_acb*) scsi_host->hostdata;
- u8 bval;
-
- scsi_remove_host(scsi_host);
-
- spin_lock_irqsave(scsi_host->host_lock, iflags);
- pACB->ACBFlag = RESET_DEV;
- bval = DC390_read8(CtrlReg1) | DIS_INT_ON_SCSI_RST;
- DC390_write8 (CtrlReg1, bval); /* disable interrupt */
- if (pACB->Gmode2 & RST_SCSI_BUS)
- dc390_ResetSCSIBus(pACB);
- spin_unlock_irqrestore(scsi_host->host_lock, iflags);
-
- free_irq(scsi_host->irq, pACB);
- release_region(scsi_host->io_port, scsi_host->n_io_port);
-
- pci_disable_device(dev);
- scsi_host_put(scsi_host);
-}
-
-static struct pci_device_id tmscsim_pci_tbl[] = {
- { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD53C974,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
- { }
-};
-MODULE_DEVICE_TABLE(pci, tmscsim_pci_tbl);
-
-static struct pci_driver dc390_driver = {
- .name = "tmscsim",
- .id_table = tmscsim_pci_tbl,
- .probe = dc390_probe_one,
- .remove = dc390_remove_one,
-};
-
-static int __init dc390_module_init(void)
-{
- if (!disable_clustering) {
- printk(KERN_INFO "DC390: clustering now enabled by default. If you get problems load\n");
- printk(KERN_INFO " with \"disable_clustering=1\" and report to maintainers\n");
- }
-
- if (tmscsim[0] == -1 || tmscsim[0] > 15) {
- tmscsim[0] = 7;
- tmscsim[1] = 4;
- tmscsim[2] = PARITY_CHK_ | TAG_QUEUEING_;
- tmscsim[3] = MORE2_DRV | GREATER_1G | RST_SCSI_BUS | ACTIVE_NEGATION;
- tmscsim[4] = 2;
- tmscsim[5] = 10;
- printk (KERN_INFO "DC390: Using safe settings.\n");
- }
-
- return pci_register_driver(&dc390_driver);
-}
-
-static void __exit dc390_module_exit(void)
-{
- pci_unregister_driver(&dc390_driver);
-}
-
-module_init(dc390_module_init);
-module_exit(dc390_module_exit);
-
-#ifndef MODULE
-static int __init dc390_setup (char *str)
-{
- int ints[8],i, im;
-
- get_options(str, ARRAY_SIZE(ints), ints);
- im = ints[0];
-
- if (im > 6) {
- printk (KERN_NOTICE "DC390: ignore extra params!\n");
- im = 6;
- }
-
- for (i = 0; i < im; i++)
- tmscsim[i] = ints[i+1];
- /* dc390_checkparams (); */
- return 1;
-}
-
-__setup("tmscsim=", dc390_setup);
-#endif
+++ /dev/null
-/***********************************************************************
-;* File Name : TMSCSIM.H *
-;* TEKRAM DC-390(T) PCI SCSI Bus Master Host Adapter *
-;* Device Driver *
-;***********************************************************************/
-/* $Id: tmscsim.h,v 2.15.2.3 2000/11/17 20:52:27 garloff Exp $ */
-
-#ifndef _TMSCSIM_H
-#define _TMSCSIM_H
-
-#include <linux/types.h>
-
-#define SCSI_IRQ_NONE 255
-
-#define MAX_ADAPTER_NUM 4
-#define MAX_SG_LIST_BUF 16 /* Not used */
-#define MAX_SCSI_ID 8
-#define MAX_SRB_CNT 50 /* Max number of started commands */
-
-#define SEL_TIMEOUT 153 /* 250 ms selection timeout (@ 40 MHz) */
-
-/*
-;-----------------------------------------------------------------------
-; SCSI Request Block
-;-----------------------------------------------------------------------
-*/
-struct dc390_srb
-{
-//u8 CmdBlock[12];
-
-struct dc390_srb *pNextSRB;
-struct dc390_dcb *pSRBDCB;
-struct scsi_cmnd *pcmd;
-struct scatterlist *pSegmentList;
-
-struct scatterlist Segmentx; /* make a one entry of S/G list table */
-
-unsigned long SGBusAddr; /*;a segment starting address as seen by AM53C974A
- in CPU endianness. We're only getting 32-bit bus
- addresses by default */
-unsigned long SGToBeXferLen; /*; to be xfer length */
-unsigned long TotalXferredLen;
-unsigned long SavedTotXLen;
-unsigned long Saved_Ptr;
-u32 SRBState;
-
-u8 SRBStatus;
-u8 SRBFlag; /*; b0-AutoReqSense,b6-Read,b7-write */
- /*; b4-settimeout,b5-Residual valid */
-u8 AdaptStatus;
-u8 TargetStatus;
-
-u8 ScsiPhase;
-s8 TagNumber;
-u8 SGIndex;
-u8 SGcount;
-
-u8 MsgCnt;
-u8 EndMessage;
-
-u8 MsgInBuf[6];
-u8 MsgOutBuf[6];
-
-//u8 IORBFlag; /*;81h-Reset, 2-retry */
-};
-
-
-/*
-;-----------------------------------------------------------------------
-; Device Control Block
-;-----------------------------------------------------------------------
-*/
-struct dc390_dcb
-{
-struct dc390_dcb *pNextDCB;
-struct dc390_acb *pDCBACB;
-
-/* Queued SRBs */
-struct dc390_srb *pGoingSRB;
-struct dc390_srb *pGoingLast;
-struct dc390_srb *pActiveSRB;
-u8 GoingSRBCnt;
-
-u32 TagMask;
-
-u8 TargetID; /*; SCSI Target ID (SCSI Only) */
-u8 TargetLUN; /*; SCSI Log. Unit (SCSI Only) */
-u8 DevMode;
-u8 DCBFlag;
-
-u8 CtrlR1;
-u8 CtrlR3;
-u8 CtrlR4;
-
-u8 SyncMode; /*; 0:async mode */
-u8 NegoPeriod; /*;for nego. */
-u8 SyncPeriod; /*;for reg. */
-u8 SyncOffset; /*;for reg. and nego.(low nibble) */
-};
-
-
-/*
-;-----------------------------------------------------------------------
-; Adapter Control Block
-;-----------------------------------------------------------------------
-*/
-struct dc390_acb
-{
-struct Scsi_Host *pScsiHost;
-u16 IOPortBase;
-u8 IRQLevel;
-u8 status;
-
-u8 SRBCount;
-u8 AdapterIndex; /*; nth Adapter this driver */
-u8 DCBCnt;
-
-u8 TagMaxNum;
-u8 ACBFlag;
-u8 Gmode2;
-u8 scan_devices;
-
-struct dc390_dcb *pLinkDCB;
-struct dc390_dcb *pLastDCB;
-struct dc390_dcb *pDCBRunRobin;
-
-struct dc390_dcb *pActiveDCB;
-struct dc390_srb *pFreeSRB;
-struct dc390_srb *pTmpSRB;
-
-u8 msgin123[4];
-u8 Connected;
-u8 pad;
-
-#if defined(USE_SPINLOCKS) && USE_SPINLOCKS > 1 && (defined(CONFIG_SMP) || DEBUG_SPINLOCKS > 0)
-spinlock_t lock;
-#endif
-u8 sel_timeout;
-u8 glitch_cfg;
-
-u8 MsgLen;
-u8 Ignore_IRQ; /* Not used */
-
-struct pci_dev *pdev;
-
-unsigned long last_reset;
-unsigned long Cmds;
-u32 SelLost;
-u32 SelConn;
-u32 CmdInQ;
-u32 CmdOutOfSRB;
-
-struct dc390_srb TmpSRB;
-struct dc390_srb SRB_array[MAX_SRB_CNT]; /* 50 SRBs */
-};
-
-
-/*;-----------------------------------------------------------------------*/
-
-
-#define BIT31 0x80000000
-#define BIT30 0x40000000
-#define BIT29 0x20000000
-#define BIT28 0x10000000
-#define BIT27 0x08000000
-#define BIT26 0x04000000
-#define BIT25 0x02000000
-#define BIT24 0x01000000
-#define BIT23 0x00800000
-#define BIT22 0x00400000
-#define BIT21 0x00200000
-#define BIT20 0x00100000
-#define BIT19 0x00080000
-#define BIT18 0x00040000
-#define BIT17 0x00020000
-#define BIT16 0x00010000
-#define BIT15 0x00008000
-#define BIT14 0x00004000
-#define BIT13 0x00002000
-#define BIT12 0x00001000
-#define BIT11 0x00000800
-#define BIT10 0x00000400
-#define BIT9 0x00000200
-#define BIT8 0x00000100
-#define BIT7 0x00000080
-#define BIT6 0x00000040
-#define BIT5 0x00000020
-#define BIT4 0x00000010
-#define BIT3 0x00000008
-#define BIT2 0x00000004
-#define BIT1 0x00000002
-#define BIT0 0x00000001
-
-/*;---UnitCtrlFlag */
-#define UNIT_ALLOCATED BIT0
-#define UNIT_INFO_CHANGED BIT1
-#define FORMATING_MEDIA BIT2
-#define UNIT_RETRY BIT3
-
-/*;---UnitFlags */
-#define DASD_SUPPORT BIT0
-#define SCSI_SUPPORT BIT1
-#define ASPI_SUPPORT BIT2
-
-/*;----SRBState machine definition */
-#define SRB_FREE 0
-#define SRB_WAIT BIT0
-#define SRB_READY BIT1
-#define SRB_MSGOUT BIT2 /*;arbitration+msg_out 1st byte*/
-#define SRB_MSGIN BIT3
-#define SRB_MSGIN_MULTI BIT4
-#define SRB_COMMAND BIT5
-#define SRB_START_ BIT6 /*;arbitration+msg_out+command_out*/
-#define SRB_DISCONNECT BIT7
-#define SRB_DATA_XFER BIT8
-#define SRB_XFERPAD BIT9
-#define SRB_STATUS BIT10
-#define SRB_COMPLETED BIT11
-#define SRB_ABORT_SENT BIT12
-#define DO_SYNC_NEGO BIT13
-#define SRB_UNEXPECT_RESEL BIT14
-
-/*;---SRBstatus */
-#define SRB_OK BIT0
-#define ABORTION BIT1
-#define OVER_RUN BIT2
-#define UNDER_RUN BIT3
-#define PARITY_ERROR BIT4
-#define SRB_ERROR BIT5
-
-/*;---ACBFlag */
-#define RESET_DEV BIT0
-#define RESET_DETECT BIT1
-#define RESET_DONE BIT2
-
-/*;---DCBFlag */
-#define ABORT_DEV_ BIT0
-
-/*;---SRBFlag */
-#define DATAOUT BIT7
-#define DATAIN BIT6
-#define RESIDUAL_VALID BIT5
-#define ENABLE_TIMER BIT4
-#define RESET_DEV0 BIT2
-#define ABORT_DEV BIT1
-#define AUTO_REQSENSE BIT0
-
-/*;---Adapter status */
-#define H_STATUS_GOOD 0
-#define H_SEL_TIMEOUT 0x11
-#define H_OVER_UNDER_RUN 0x12
-#define H_UNEXP_BUS_FREE 0x13
-#define H_TARGET_PHASE_F 0x14
-#define H_INVALID_CCB_OP 0x16
-#define H_LINK_CCB_BAD 0x17
-#define H_BAD_TARGET_DIR 0x18
-#define H_DUPLICATE_CCB 0x19
-#define H_BAD_CCB_OR_SG 0x1A
-#define H_ABORT 0x0FF
-
-/* cmd->result */
-#define RES_TARGET 0x000000FF /* Target State */
-#define RES_TARGET_LNX STATUS_MASK /* Only official ... */
-#define RES_ENDMSG 0x0000FF00 /* End Message */
-#define RES_DID 0x00FF0000 /* DID_ codes */
-#define RES_DRV 0xFF000000 /* DRIVER_ codes */
-
-#define MK_RES(drv,did,msg,tgt) ((int)(drv)<<24 | (int)(did)<<16 | (int)(msg)<<8 | (int)(tgt))
-#define MK_RES_LNX(drv,did,msg,tgt) ((int)(drv)<<24 | (int)(did)<<16 | (int)(msg)<<8 | (int)(tgt))
-
-#define SET_RES_TARGET(who, tgt) do { who &= ~RES_TARGET; who |= (int)(tgt); } while (0)
-#define SET_RES_TARGET_LNX(who, tgt) do { who &= ~RES_TARGET_LNX; who |= (int)(tgt) << 1; } while (0)
-#define SET_RES_MSG(who, msg) do { who &= ~RES_ENDMSG; who |= (int)(msg) << 8; } while (0)
-#define SET_RES_DID(who, did) do { who &= ~RES_DID; who |= (int)(did) << 16; } while (0)
-#define SET_RES_DRV(who, drv) do { who &= ~RES_DRV; who |= (int)(drv) << 24; } while (0)
-
-/*;---Sync_Mode */
-#define SYNC_DISABLE 0
-#define SYNC_ENABLE BIT0
-#define SYNC_NEGO_DONE BIT1
-#define WIDE_ENABLE BIT2 /* Not used ;-) */
-#define WIDE_NEGO_DONE BIT3 /* Not used ;-) */
-#define EN_TAG_QUEUEING BIT4
-#define EN_ATN_STOP BIT5
-
-#define SYNC_NEGO_OFFSET 15
-
-/*;---SCSI bus phase*/
-#define SCSI_DATA_OUT 0
-#define SCSI_DATA_IN 1
-#define SCSI_COMMAND 2
-#define SCSI_STATUS_ 3
-#define SCSI_NOP0 4
-#define SCSI_NOP1 5
-#define SCSI_MSG_OUT 6
-#define SCSI_MSG_IN 7
-
-/*;----SCSI MSG BYTE*/ /* see scsi/scsi.h */ /* One is missing ! */
-#define ABORT_TAG 0x0d
-
-/*
- * SISC query queue
- */
-typedef struct {
- dma_addr_t saved_dma_handle;
-} dc390_cmd_scp_t;
-
-/*
-;==========================================================
-; EEPROM byte offset
-;==========================================================
-*/
-typedef struct _EEprom
-{
-u8 EE_MODE1;
-u8 EE_SPEED;
-u8 xx1;
-u8 xx2;
-} EEprom, *PEEprom;
-
-#define REAL_EE_ADAPT_SCSI_ID 64
-#define REAL_EE_MODE2 65
-#define REAL_EE_DELAY 66
-#define REAL_EE_TAG_CMD_NUM 67
-
-#define EE_ADAPT_SCSI_ID 32
-#define EE_MODE2 33
-#define EE_DELAY 34
-#define EE_TAG_CMD_NUM 35
-
-#define EE_LEN 40
-
-/*; EE_MODE1 bits definition*/
-#define PARITY_CHK_ BIT0
-#define SYNC_NEGO_ BIT1
-#define EN_DISCONNECT_ BIT2
-#define SEND_START_ BIT3
-#define TAG_QUEUEING_ BIT4
-
-/*; EE_MODE2 bits definition*/
-#define MORE2_DRV BIT0
-#define GREATER_1G BIT1
-#define RST_SCSI_BUS BIT2
-#define ACTIVE_NEGATION BIT3
-#define NO_SEEK BIT4
-#define LUN_CHECK BIT5
-
-#define ENABLE_CE 1
-#define DISABLE_CE 0
-#define EEPROM_READ 0x80
-
-/*
-;==========================================================
-; AMD 53C974 Registers bit Definition
-;==========================================================
-*/
-/*
-;====================
-; SCSI Register
-;====================
-*/
-
-/*; Command Reg.(+0CH) (rw) */
-#define DMA_COMMAND BIT7
-#define NOP_CMD 0
-#define CLEAR_FIFO_CMD 1
-#define RST_DEVICE_CMD 2
-#define RST_SCSI_BUS_CMD 3
-
-#define INFO_XFER_CMD 0x10
-#define INITIATOR_CMD_CMPLTE 0x11
-#define MSG_ACCEPTED_CMD 0x12
-#define XFER_PAD_BYTE 0x18
-#define SET_ATN_CMD 0x1A
-#define RESET_ATN_CMD 0x1B
-
-#define SEL_WO_ATN 0x41 /* currently not used */
-#define SEL_W_ATN 0x42
-#define SEL_W_ATN_STOP 0x43
-#define SEL_W_ATN3 0x46
-#define EN_SEL_RESEL 0x44
-#define DIS_SEL_RESEL 0x45 /* currently not used */
-#define RESEL 0x40 /* " */
-#define RESEL_ATN3 0x47 /* " */
-
-#define DATA_XFER_CMD INFO_XFER_CMD
-
-
-/*; SCSI Status Reg.(+10H) (r) */
-#define INTERRUPT BIT7
-#define ILLEGAL_OP_ERR BIT6
-#define PARITY_ERR BIT5
-#define COUNT_2_ZERO BIT4
-#define GROUP_CODE_VALID BIT3
-#define SCSI_PHASE_MASK (BIT2+BIT1+BIT0)
-/* BIT2: MSG phase; BIT1: C/D physe; BIT0: I/O phase */
-
-/*; Interrupt Status Reg.(+14H) (r) */
-#define SCSI_RESET BIT7
-#define INVALID_CMD BIT6
-#define DISCONNECTED BIT5
-#define SERVICE_REQUEST BIT4
-#define SUCCESSFUL_OP BIT3
-#define RESELECTED BIT2
-#define SEL_ATTENTION BIT1
-#define SELECTED BIT0
-
-/*; Internal State Reg.(+18H) (r) */
-#define SYNC_OFFSET_FLAG BIT3
-#define INTRN_STATE_MASK (BIT2+BIT1+BIT0)
-/* 0x04: Sel. successful (w/o stop), 0x01: Sel. successful (w/ stop) */
-
-/*; Clock Factor Reg.(+24H) (w) */
-#define CLK_FREQ_40MHZ 0
-#define CLK_FREQ_35MHZ (BIT2+BIT1+BIT0)
-#define CLK_FREQ_30MHZ (BIT2+BIT1)
-#define CLK_FREQ_25MHZ (BIT2+BIT0)
-#define CLK_FREQ_20MHZ BIT2
-#define CLK_FREQ_15MHZ (BIT1+BIT0)
-#define CLK_FREQ_10MHZ BIT1
-
-/*; Control Reg. 1(+20H) (rw) */
-#define EXTENDED_TIMING BIT7
-#define DIS_INT_ON_SCSI_RST BIT6
-#define PARITY_ERR_REPO BIT4
-#define SCSI_ID_ON_BUS (BIT2+BIT1+BIT0) /* host adapter ID */
-
-/*; Control Reg. 2(+2CH) (rw) */
-#define EN_FEATURE BIT6
-#define EN_SCSI2_CMD BIT3
-
-/*; Control Reg. 3(+30H) (rw) */
-#define ID_MSG_CHECK BIT7
-#define EN_QTAG_MSG BIT6
-#define EN_GRP2_CMD BIT5
-#define FAST_SCSI BIT4 /* ;10MB/SEC */
-#define FAST_CLK BIT3 /* ;25 - 40 MHZ */
-
-/*; Control Reg. 4(+34H) (rw) */
-#define EATER_12NS 0
-#define EATER_25NS BIT7
-#define EATER_35NS BIT6
-#define EATER_0NS (BIT7+BIT6)
-#define REDUCED_POWER BIT5
-#define CTRL4_RESERVED BIT4 /* must be 1 acc. to AM53C974.c */
-#define NEGATE_REQACKDATA BIT2
-#define NEGATE_REQACK BIT3
-
-#define GLITCH_TO_NS(x) (((~x>>6 & 2) >> 1) | ((x>>6 & 1) << 1 ^ (x>>6 & 2)))
-#define NS_TO_GLITCH(y) (((~y<<7) | ~((y<<6) ^ ((y<<5 & 1<<6) | ~0x40))) & 0xc0)
-
-/*
-;====================
-; DMA Register
-;====================
-*/
-/*; DMA Command Reg.(+40H) (rw) */
-#define READ_DIRECTION BIT7
-#define WRITE_DIRECTION 0
-#define EN_DMA_INT BIT6
-#define EN_PAGE_INT BIT5 /* page transfer interrupt enable */
-#define MAP_TO_MDL BIT4
-#define DIAGNOSTIC BIT2
-#define DMA_IDLE_CMD 0
-#define DMA_BLAST_CMD BIT0
-#define DMA_ABORT_CMD BIT1
-#define DMA_START_CMD (BIT1+BIT0)
-
-/*; DMA Status Reg.(+54H) (r) */
-#define PCI_MS_ABORT BIT6
-#define BLAST_COMPLETE BIT5
-#define SCSI_INTERRUPT BIT4
-#define DMA_XFER_DONE BIT3
-#define DMA_XFER_ABORT BIT2
-#define DMA_XFER_ERROR BIT1
-#define POWER_DOWN BIT0
-
-/*; DMA SCSI Bus and Ctrl.(+70H) */
-#define EN_INT_ON_PCI_ABORT BIT25
-#define WRT_ERASE_DMA_STAT BIT24
-#define PW_DOWN_CTRL BIT21
-#define SCSI_BUSY BIT20
-#define SCLK BIT19
-#define SCAM BIT18
-#define SCSI_LINES 0x0003ffff
-
-/*
-;==========================================================
-; SCSI Chip register address offset
-;==========================================================
-;Registers are rw unless declared otherwise
-*/
-#define CtcReg_Low 0x00 /* r curr. transfer count */
-#define CtcReg_Mid 0x04 /* r */
-#define CtcReg_High 0x38 /* r */
-#define ScsiFifo 0x08
-#define ScsiCmd 0x0C
-#define Scsi_Status 0x10 /* r */
-#define INT_Status 0x14 /* r */
-#define Sync_Period 0x18 /* w */
-#define Sync_Offset 0x1C /* w */
-#define Clk_Factor 0x24 /* w */
-#define CtrlReg1 0x20
-#define CtrlReg2 0x2C
-#define CtrlReg3 0x30
-#define CtrlReg4 0x34
-#define DMA_Cmd 0x40
-#define DMA_XferCnt 0x44 /* rw starting transfer count (32 bit) */
-#define DMA_XferAddr 0x48 /* rw starting physical address (32 bit) */
-#define DMA_Wk_ByteCntr 0x4C /* r working byte counter */
-#define DMA_Wk_AddrCntr 0x50 /* r working address counter */
-#define DMA_Status 0x54 /* r */
-#define DMA_MDL_Addr 0x58 /* rw starting MDL address */
-#define DMA_Wk_MDL_Cntr 0x5C /* r working MDL counter */
-#define DMA_ScsiBusCtrl 0x70 /* rw SCSI Bus, PCI/DMA Ctrl */
-
-#define StcReg_Low CtcReg_Low /* w start transfer count */
-#define StcReg_Mid CtcReg_Mid /* w */
-#define StcReg_High CtcReg_High /* w */
-#define Scsi_Dest_ID Scsi_Status /* w */
-#define Scsi_TimeOut INT_Status /* w */
-#define Intern_State Sync_Period /* r */
-#define Current_Fifo Sync_Offset /* r Curr. FIFO / int. state */
-
-
-#define DC390_read8(address) \
- (inb (pACB->IOPortBase + (address)))
-
-#define DC390_read8_(address, base) \
- (inb ((u16)(base) + (address)))
-
-#define DC390_read16(address) \
- (inw (pACB->IOPortBase + (address)))
-
-#define DC390_read32(address) \
- (inl (pACB->IOPortBase + (address)))
-
-#define DC390_write8(address,value) \
- outb ((value), pACB->IOPortBase + (address))
-
-#define DC390_write8_(address,value,base) \
- outb ((value), (u16)(base) + (address))
-
-#define DC390_write16(address,value) \
- outw ((value), pACB->IOPortBase + (address))
-
-#define DC390_write32(address,value) \
- outl ((value), pACB->IOPortBase + (address))
-
-
-#endif /* _TMSCSIM_H */
if (TLDEV(dev->type) && dev->tagged_supported)
if (tag_mode == TAG_SIMPLE) {
- scsi_adjust_queue_depth(dev, MSG_SIMPLE_TAG, tqd);
+ scsi_change_queue_depth(dev, tqd);
tag_suffix = ", simple tags";
}
else if (tag_mode == TAG_ORDERED) {
- scsi_adjust_queue_depth(dev, MSG_ORDERED_TAG, tqd);
+ scsi_change_queue_depth(dev, tqd);
tag_suffix = ", ordered tags";
}
else {
- scsi_adjust_queue_depth(dev, 0, tqd);
+ scsi_change_queue_depth(dev, tqd);
tag_suffix = ", no tags";
}
else if (TLDEV(dev->type) && linked_comm) {
- scsi_adjust_queue_depth(dev, 0, tqd);
+ scsi_change_queue_depth(dev, tqd);
tag_suffix = ", untagged";
}
else {
- scsi_adjust_queue_depth(dev, 0, utqd);
+ scsi_change_queue_depth(dev, utqd);
tag_suffix = "";
}
clkfreq = devm_kzalloc(dev, sz * sizeof(*clkfreq),
GFP_KERNEL);
if (!clkfreq) {
- dev_err(dev, "%s: no memory\n", "freq-table-hz");
ret = -ENOMEM;
goto out;
}
if (ret && (ret != -EINVAL)) {
dev_err(dev, "%s: error reading array %d\n",
"freq-table-hz", ret);
- goto free_clkfreq;
+ return ret;
}
for (i = 0; i < sz; i += 2) {
ret = of_property_read_string_index(np,
"clock-names", i/2, (const char **)&name);
if (ret)
- goto free_clkfreq;
+ goto out;
clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL);
if (!clki) {
ret = -ENOMEM;
- goto free_clkfreq;
+ goto out;
}
clki->min_freq = clkfreq[i];
clki->min_freq, clki->max_freq, clki->name);
list_add_tail(&clki->list, &hba->clk_list_head);
}
-free_clkfreq:
- kfree(clkfreq);
out:
return ret;
}
}
vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
- if (!vreg) {
- dev_err(dev, "No memory for %s regulator\n", name);
- goto out;
- }
+ if (!vreg)
+ return -ENOMEM;
vreg->name = kstrdup(name, GFP_KERNEL);
if (!ufshcd_is_clkgating_allowed(hba))
return;
device_remove_file(hba->dev, &hba->clk_gating.delay_attr);
+ cancel_work_sync(&hba->clk_gating.ungate_work);
+ cancel_delayed_work_sync(&hba->clk_gating.gate_work);
}
/* Must be called with host lock acquired */
return ret;
}
+ /**
+ * ufshcd_init_pwr_info - setting the POR (power on reset)
+ * values in hba power info
+ * @hba: per-adapter instance
+ */
+static void ufshcd_init_pwr_info(struct ufs_hba *hba)
+{
+ hba->pwr_info.gear_rx = UFS_PWM_G1;
+ hba->pwr_info.gear_tx = UFS_PWM_G1;
+ hba->pwr_info.lane_rx = 1;
+ hba->pwr_info.lane_tx = 1;
+ hba->pwr_info.pwr_rx = SLOWAUTO_MODE;
+ hba->pwr_info.pwr_tx = SLOWAUTO_MODE;
+ hba->pwr_info.hs_rate = 0;
+}
+
/**
* ufshcd_get_max_pwr_mode - reads the max power mode negotiated with device
* @hba: per-adapter instance
dev_dbg(hba->dev, "%s: activate tcq with queue depth %d\n",
__func__, lun_qdepth);
- scsi_activate_tcq(sdev, lun_qdepth);
+ scsi_change_queue_depth(sdev, lun_qdepth);
}
/*
struct ufs_hba *hba;
hba = shost_priv(sdev->host);
- sdev->tagged_supported = 1;
/* Mode sense(6) is not supported by UFS, so use Mode sense(10) */
sdev->use_10_for_ms = 1;
- scsi_set_tag_type(sdev, MSG_SIMPLE_TAG);
/* allow SCSI layer to restart the device in case of errors */
sdev->allow_restart = 1;
* ufshcd_change_queue_depth - change queue depth
* @sdev: pointer to SCSI device
* @depth: required depth to set
- * @reason: reason for changing the depth
*
- * Change queue depth according to the reason and make sure
- * the max. limits are not crossed.
+ * Change queue depth and make sure the max. limits are not crossed.
*/
-static int ufshcd_change_queue_depth(struct scsi_device *sdev,
- int depth, int reason)
+static int ufshcd_change_queue_depth(struct scsi_device *sdev, int depth)
{
struct ufs_hba *hba = shost_priv(sdev->host);
if (depth > hba->nutrs)
depth = hba->nutrs;
-
- switch (reason) {
- case SCSI_QDEPTH_DEFAULT:
- case SCSI_QDEPTH_RAMP_UP:
- if (!sdev->tagged_supported)
- depth = 1;
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
- break;
- case SCSI_QDEPTH_QFULL:
- scsi_track_queue_full(sdev, depth);
- break;
- default:
- return -EOPNOTSUPP;
- }
-
- return depth;
+ return scsi_change_queue_depth(sdev, depth);
}
/**
struct ufs_hba *hba;
hba = shost_priv(sdev->host);
- scsi_deactivate_tcq(sdev, hba->nutrs);
/* Drop the reference as it won't be needed anymore */
- if (ufshcd_scsi_to_upiu_lun(sdev->lun) == UFS_UPIU_UFS_DEVICE_WLUN)
+ if (ufshcd_scsi_to_upiu_lun(sdev->lun) == UFS_UPIU_UFS_DEVICE_WLUN) {
+ unsigned long flags;
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
hba->sdev_ufs_device = NULL;
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ }
}
/**
static int ufshcd_scsi_add_wlus(struct ufs_hba *hba)
{
int ret = 0;
+ struct scsi_device *sdev_rpmb;
+ struct scsi_device *sdev_boot;
hba->sdev_ufs_device = __scsi_add_device(hba->host, 0, 0,
ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_UFS_DEVICE_WLUN), NULL);
hba->sdev_ufs_device = NULL;
goto out;
}
+ scsi_device_put(hba->sdev_ufs_device);
- hba->sdev_boot = __scsi_add_device(hba->host, 0, 0,
+ sdev_boot = __scsi_add_device(hba->host, 0, 0,
ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_BOOT_WLUN), NULL);
- if (IS_ERR(hba->sdev_boot)) {
- ret = PTR_ERR(hba->sdev_boot);
- hba->sdev_boot = NULL;
+ if (IS_ERR(sdev_boot)) {
+ ret = PTR_ERR(sdev_boot);
goto remove_sdev_ufs_device;
}
+ scsi_device_put(sdev_boot);
- hba->sdev_rpmb = __scsi_add_device(hba->host, 0, 0,
+ sdev_rpmb = __scsi_add_device(hba->host, 0, 0,
ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_RPMB_WLUN), NULL);
- if (IS_ERR(hba->sdev_rpmb)) {
- ret = PTR_ERR(hba->sdev_rpmb);
- hba->sdev_rpmb = NULL;
+ if (IS_ERR(sdev_rpmb)) {
+ ret = PTR_ERR(sdev_rpmb);
goto remove_sdev_boot;
}
+ scsi_device_put(sdev_rpmb);
goto out;
remove_sdev_boot:
- scsi_remove_device(hba->sdev_boot);
+ scsi_remove_device(sdev_boot);
remove_sdev_ufs_device:
scsi_remove_device(hba->sdev_ufs_device);
out:
return ret;
}
-/**
- * ufshcd_scsi_remove_wlus - Removes the W-LUs which were added by
- * ufshcd_scsi_add_wlus()
- * @hba: per-adapter instance
- *
- */
-static void ufshcd_scsi_remove_wlus(struct ufs_hba *hba)
-{
- if (hba->sdev_ufs_device) {
- scsi_remove_device(hba->sdev_ufs_device);
- hba->sdev_ufs_device = NULL;
- }
-
- if (hba->sdev_boot) {
- scsi_remove_device(hba->sdev_boot);
- hba->sdev_boot = NULL;
- }
-
- if (hba->sdev_rpmb) {
- scsi_remove_device(hba->sdev_rpmb);
- hba->sdev_rpmb = NULL;
- }
-}
-
/**
* ufshcd_probe_hba - probe hba to detect device and initialize
* @hba: per-adapter instance
if (ret)
goto out;
+ ufshcd_init_pwr_info(hba);
+
/* UniPro link is active now */
ufshcd_set_link_active(hba);
.cmd_per_lun = UFSHCD_CMD_PER_LUN,
.can_queue = UFSHCD_CAN_QUEUE,
.max_host_blocked = 1,
+ .use_blk_tags = 1,
+ .track_queue_depth = 1,
};
static int ufshcd_config_vreg_load(struct device *dev, struct ufs_vreg *vreg,
static inline int ufshcd_config_vreg_lpm(struct ufs_hba *hba,
struct ufs_vreg *vreg)
{
+ if (!vreg)
+ return 0;
+
return ufshcd_config_vreg_load(hba->dev, vreg, UFS_VREG_LPM_LOAD_UA);
}
static inline int ufshcd_config_vreg_hpm(struct ufs_hba *hba,
struct ufs_vreg *vreg)
{
+ if (!vreg)
+ return 0;
+
return ufshcd_config_vreg_load(hba->dev, vreg, vreg->max_uA);
}
if (!IS_ERR_OR_NULL(clki->clk) && clki->enabled)
clk_disable_unprepare(clki->clk);
}
- } else if (!ret && on) {
+ } else if (on) {
spin_lock_irqsave(hba->host->host_lock, flags);
hba->clk_gating.state = CLKS_ON;
spin_unlock_irqrestore(hba->host->host_lock, flags);
{
unsigned char cmd[6] = { START_STOP };
struct scsi_sense_hdr sshdr;
- struct scsi_device *sdp = hba->sdev_ufs_device;
+ struct scsi_device *sdp;
+ unsigned long flags;
int ret;
- if (!sdp || !scsi_device_online(sdp))
- return -ENODEV;
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ sdp = hba->sdev_ufs_device;
+ if (sdp) {
+ ret = scsi_device_get(sdp);
+ if (!ret && !scsi_device_online(sdp)) {
+ ret = -ENODEV;
+ scsi_device_put(sdp);
+ }
+ } else {
+ ret = -ENODEV;
+ }
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+ if (ret)
+ return ret;
/*
* If scsi commands fail, the scsi mid-layer schedules scsi error-
START_STOP_TIMEOUT, 0, NULL, REQ_PM);
if (ret) {
sdev_printk(KERN_WARNING, sdp,
- "START_STOP failed for power mode: %d\n", pwr_mode);
- scsi_show_result(ret);
+ "START_STOP failed for power mode: %d, result %x\n",
+ pwr_mode, ret);
if (driver_byte(ret) & DRIVER_SENSE) {
- scsi_show_sense_hdr(&sshdr);
- scsi_show_extd_sense(sshdr.asc, sshdr.ascq);
+ scsi_show_sense_hdr(sdp, NULL, &sshdr);
+ scsi_show_extd_sense(sdp, NULL, sshdr.asc, sshdr.ascq);
}
}
if (!ret)
hba->curr_dev_pwr_mode = pwr_mode;
out:
+ scsi_device_put(sdp);
hba->host->eh_noresume = 0;
return ret;
}
int ret = 0;
if (!hba || !hba->is_powered)
- goto out;
+ return 0;
if (pm_runtime_suspended(hba->dev)) {
if (hba->rpm_lvl == hba->spm_lvl)
void ufshcd_remove(struct ufs_hba *hba)
{
scsi_remove_host(hba->host);
- ufshcd_scsi_remove_wlus(hba);
/* disable interrupts */
ufshcd_disable_intr(hba, hba->intr_mask);
ufshcd_hba_stop(hba);
* "UFS device" W-LU.
*/
struct scsi_device *sdev_ufs_device;
- struct scsi_device *sdev_rpmb;
- struct scsi_device *sdev_boot;
enum ufs_dev_pwr_mode curr_dev_pwr_mode;
enum uic_link_state uic_link_state;
return virtscsi_queuecommand(vscsi, &vscsi->req_vqs[0], sc);
}
+static struct virtio_scsi_vq *virtscsi_pick_vq_mq(struct virtio_scsi *vscsi,
+ struct scsi_cmnd *sc)
+{
+ u32 tag = blk_mq_unique_tag(sc->request);
+ u16 hwq = blk_mq_unique_tag_to_hwq(tag);
+
+ return &vscsi->req_vqs[hwq];
+}
+
static struct virtio_scsi_vq *virtscsi_pick_vq(struct virtio_scsi *vscsi,
struct virtio_scsi_target_state *tgt)
{
struct virtio_scsi *vscsi = shost_priv(sh);
struct virtio_scsi_target_state *tgt =
scsi_target(sc->device)->hostdata;
- struct virtio_scsi_vq *req_vq = virtscsi_pick_vq(vscsi, tgt);
+ struct virtio_scsi_vq *req_vq;
+
+ if (shost_use_blk_mq(sh))
+ req_vq = virtscsi_pick_vq_mq(vscsi, sc);
+ else
+ req_vq = virtscsi_pick_vq(vscsi, tgt);
return virtscsi_queuecommand(vscsi, req_vq, sc);
}
* virtscsi_change_queue_depth() - Change a virtscsi target's queue depth
* @sdev: Virtscsi target whose queue depth to change
* @qdepth: New queue depth
- * @reason: Reason for the queue depth change.
*/
-static int virtscsi_change_queue_depth(struct scsi_device *sdev,
- int qdepth,
- int reason)
+static int virtscsi_change_queue_depth(struct scsi_device *sdev, int qdepth)
{
struct Scsi_Host *shost = sdev->host;
int max_depth = shost->cmd_per_lun;
- switch (reason) {
- case SCSI_QDEPTH_QFULL: /* Drop qdepth in response to BUSY state */
- scsi_track_queue_full(sdev, qdepth);
- break;
- case SCSI_QDEPTH_RAMP_UP: /* Raise qdepth after BUSY state resolved */
- case SCSI_QDEPTH_DEFAULT: /* Manual change via sysfs */
- scsi_adjust_queue_depth(sdev,
- scsi_get_tag_type(sdev),
- min(max_depth, qdepth));
- break;
- default:
- return -EOPNOTSUPP;
- }
-
- return sdev->queue_depth;
+ return scsi_change_queue_depth(sdev, min(max_depth, qdepth));
}
static int virtscsi_abort(struct scsi_cmnd *sc)
.use_clustering = ENABLE_CLUSTERING,
.target_alloc = virtscsi_target_alloc,
.target_destroy = virtscsi_target_destroy,
+ .track_queue_depth = 1,
};
static struct scsi_host_template virtscsi_host_template_multi = {
.use_clustering = ENABLE_CLUSTERING,
.target_alloc = virtscsi_target_alloc,
.target_destroy = virtscsi_target_destroy,
+ .track_queue_depth = 1,
};
#define virtscsi_config_get(vdev, fld) \
shost->max_id = num_targets;
shost->max_channel = 0;
shost->max_cmd_len = VIRTIO_SCSI_CDB_SIZE;
+ shost->nr_hw_queues = num_queues;
if (virtio_has_feature(vdev, VIRTIO_SCSI_F_T10_PI)) {
host_prot = SHOST_DIF_TYPE1_PROTECTION | SHOST_DIF_TYPE2_PROTECTION |
}
}
-static int pvscsi_change_queue_depth(struct scsi_device *sdev,
- int qdepth,
- int reason)
+static int pvscsi_change_queue_depth(struct scsi_device *sdev, int qdepth)
{
- int max_depth;
- struct Scsi_Host *shost = sdev->host;
-
- if (reason != SCSI_QDEPTH_DEFAULT)
- /*
- * We support only changing default.
- */
- return -EOPNOTSUPP;
-
- max_depth = shost->can_queue;
if (!sdev->tagged_supported)
- max_depth = 1;
- if (qdepth > max_depth)
- qdepth = max_depth;
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
-
- if (sdev->inquiry_len > 7)
- sdev_printk(KERN_INFO, sdev,
- "qdepth(%d), tagged(%d), simple(%d), ordered(%d), scsi_level(%d), cmd_que(%d)\n",
- sdev->queue_depth, sdev->tagged_supported,
- sdev->simple_tags, sdev->ordered_tags,
- sdev->scsi_level, (sdev->inquiry[7] & 2) >> 1);
- return sdev->queue_depth;
+ qdepth = 1;
+ return scsi_change_queue_depth(sdev, qdepth);
}
/*
memcpy(e->cdb, cmd->cmnd, e->cdbLen);
e->tag = SIMPLE_QUEUE_TAG;
- if (sdev->tagged_supported &&
- (cmd->tag == HEAD_OF_QUEUE_TAG ||
- cmd->tag == ORDERED_QUEUE_TAG))
- e->tag = cmd->tag;
if (cmd->sc_data_direction == DMA_FROM_DEVICE)
e->flags = PVSCSI_FLAG_CMD_DIR_TOHOST;
.can_queue = WD7000_Q,
.this_id = 7,
.sg_tablesize = WD7000_SG,
- .cmd_per_lun = 1,
.unchecked_isa_dma = 1,
.use_clustering = ENABLE_CLUSTERING,
};
--- /dev/null
+/*
+ * Driver for Western Digital WD7193, WD7197 and WD7296 SCSI cards
+ * Copyright 2013 Ondrej Zary
+ *
+ * Original driver by
+ * Aaron Dewell <dewell@woods.net>
+ * Gaerti <Juergen.Gaertner@mbox.si.uni-hannover.de>
+ *
+ * HW documentation available in book:
+ *
+ * SPIDER Command Protocol
+ * by Chandru M. Sippy
+ * SCSI Storage Products (MCP)
+ * Western Digital Corporation
+ * 09-15-95
+ *
+ * http://web.archive.org/web/20070717175254/http://sun1.rrzn.uni-hannover.de/gaertner.juergen/wd719x/Linux/Docu/Spider/
+ */
+
+/*
+ * Driver workflow:
+ * 1. SCSI command is transformed to SCB (Spider Control Block) by the
+ * queuecommand function.
+ * 2. The address of the SCB is stored in a list to be able to access it, if
+ * something goes wrong.
+ * 3. The address of the SCB is written to the Controller, which loads the SCB
+ * via BM-DMA and processes it.
+ * 4. After it has finished, it generates an interrupt, and sets registers.
+ *
+ * flaws:
+ * - abort/reset functions
+ *
+ * ToDo:
+ * - tagged queueing
+ */
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/firmware.h>
+#include <linux/eeprom_93cx6.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include "wd719x.h"
+
+/* low-level register access */
+static inline u8 wd719x_readb(struct wd719x *wd, u8 reg)
+{
+ return ioread8(wd->base + reg);
+}
+
+static inline u32 wd719x_readl(struct wd719x *wd, u8 reg)
+{
+ return ioread32(wd->base + reg);
+}
+
+static inline void wd719x_writeb(struct wd719x *wd, u8 reg, u8 val)
+{
+ iowrite8(val, wd->base + reg);
+}
+
+static inline void wd719x_writew(struct wd719x *wd, u8 reg, u16 val)
+{
+ iowrite16(val, wd->base + reg);
+}
+
+static inline void wd719x_writel(struct wd719x *wd, u8 reg, u32 val)
+{
+ iowrite32(val, wd->base + reg);
+}
+
+/* wait until the command register is ready */
+static inline int wd719x_wait_ready(struct wd719x *wd)
+{
+ int i = 0;
+
+ do {
+ if (wd719x_readb(wd, WD719X_AMR_COMMAND) == WD719X_CMD_READY)
+ return 0;
+ udelay(1);
+ } while (i++ < WD719X_WAIT_FOR_CMD_READY);
+
+ dev_err(&wd->pdev->dev, "command register is not ready: 0x%02x\n",
+ wd719x_readb(wd, WD719X_AMR_COMMAND));
+
+ return -ETIMEDOUT;
+}
+
+/* poll interrupt status register until command finishes */
+static inline int wd719x_wait_done(struct wd719x *wd, int timeout)
+{
+ u8 status;
+
+ while (timeout > 0) {
+ status = wd719x_readb(wd, WD719X_AMR_INT_STATUS);
+ if (status)
+ break;
+ timeout--;
+ udelay(1);
+ }
+
+ if (timeout <= 0) {
+ dev_err(&wd->pdev->dev, "direct command timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ if (status != WD719X_INT_NOERRORS) {
+ dev_err(&wd->pdev->dev, "direct command failed, status 0x%02x, SUE 0x%02x\n",
+ status, wd719x_readb(wd, WD719X_AMR_SCB_ERROR));
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int wd719x_direct_cmd(struct wd719x *wd, u8 opcode, u8 dev, u8 lun,
+ u8 tag, dma_addr_t data, int timeout)
+{
+ int ret = 0;
+
+ /* clear interrupt status register (allow command register to clear) */
+ wd719x_writeb(wd, WD719X_AMR_INT_STATUS, WD719X_INT_NONE);
+
+ /* Wait for the Command register to become free */
+ if (wd719x_wait_ready(wd))
+ return -ETIMEDOUT;
+
+ /* make sure we get NO interrupts */
+ dev |= WD719X_DISABLE_INT;
+ wd719x_writeb(wd, WD719X_AMR_CMD_PARAM, dev);
+ wd719x_writeb(wd, WD719X_AMR_CMD_PARAM_2, lun);
+ wd719x_writeb(wd, WD719X_AMR_CMD_PARAM_3, tag);
+ if (data)
+ wd719x_writel(wd, WD719X_AMR_SCB_IN, data);
+
+ /* clear interrupt status register again */
+ wd719x_writeb(wd, WD719X_AMR_INT_STATUS, WD719X_INT_NONE);
+
+ /* Now, write the command */
+ wd719x_writeb(wd, WD719X_AMR_COMMAND, opcode);
+
+ if (timeout) /* wait for the command to complete */
+ ret = wd719x_wait_done(wd, timeout);
+
+ /* clear interrupt status register (clean up) */
+ if (opcode != WD719X_CMD_READ_FIRMVER)
+ wd719x_writeb(wd, WD719X_AMR_INT_STATUS, WD719X_INT_NONE);
+
+ return ret;
+}
+
+static void wd719x_destroy(struct wd719x *wd)
+{
+ struct wd719x_scb *scb;
+
+ /* stop the RISC */
+ if (wd719x_direct_cmd(wd, WD719X_CMD_SLEEP, 0, 0, 0, 0,
+ WD719X_WAIT_FOR_RISC))
+ dev_warn(&wd->pdev->dev, "RISC sleep command failed\n");
+ /* disable RISC */
+ wd719x_writeb(wd, WD719X_PCI_MODE_SELECT, 0);
+
+ /* free all SCBs */
+ list_for_each_entry(scb, &wd->active_scbs, list)
+ pci_free_consistent(wd->pdev, sizeof(struct wd719x_scb), scb,
+ scb->phys);
+ list_for_each_entry(scb, &wd->free_scbs, list)
+ pci_free_consistent(wd->pdev, sizeof(struct wd719x_scb), scb,
+ scb->phys);
+ /* free internal buffers */
+ pci_free_consistent(wd->pdev, wd->fw_size, wd->fw_virt, wd->fw_phys);
+ wd->fw_virt = NULL;
+ pci_free_consistent(wd->pdev, WD719X_HASH_TABLE_SIZE, wd->hash_virt,
+ wd->hash_phys);
+ wd->hash_virt = NULL;
+ pci_free_consistent(wd->pdev, sizeof(struct wd719x_host_param),
+ wd->params, wd->params_phys);
+ wd->params = NULL;
+ free_irq(wd->pdev->irq, wd);
+}
+
+/* finish a SCSI command, mark SCB (if any) as free, unmap buffers */
+static void wd719x_finish_cmd(struct scsi_cmnd *cmd, int result)
+{
+ struct wd719x *wd = shost_priv(cmd->device->host);
+ struct wd719x_scb *scb = (struct wd719x_scb *) cmd->host_scribble;
+
+ if (scb) {
+ list_move(&scb->list, &wd->free_scbs);
+ dma_unmap_single(&wd->pdev->dev, cmd->SCp.dma_handle,
+ SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
+ scsi_dma_unmap(cmd);
+ }
+ cmd->result = result << 16;
+ cmd->scsi_done(cmd);
+}
+
+/* Build a SCB and send it to the card */
+static int wd719x_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
+{
+ int i, count_sg;
+ unsigned long flags;
+ struct wd719x_scb *scb;
+ struct wd719x *wd = shost_priv(sh);
+ dma_addr_t phys;
+
+ cmd->host_scribble = NULL;
+
+ /* get a free SCB - either from existing ones or allocate a new one */
+ spin_lock_irqsave(wd->sh->host_lock, flags);
+ scb = list_first_entry_or_null(&wd->free_scbs, struct wd719x_scb, list);
+ if (scb) {
+ list_del(&scb->list);
+ phys = scb->phys;
+ } else {
+ spin_unlock_irqrestore(wd->sh->host_lock, flags);
+ scb = pci_alloc_consistent(wd->pdev, sizeof(struct wd719x_scb),
+ &phys);
+ spin_lock_irqsave(wd->sh->host_lock, flags);
+ if (!scb) {
+ dev_err(&wd->pdev->dev, "unable to allocate SCB\n");
+ wd719x_finish_cmd(cmd, DID_ERROR);
+ spin_unlock_irqrestore(wd->sh->host_lock, flags);
+ return 0;
+ }
+ }
+ memset(scb, 0, sizeof(struct wd719x_scb));
+ list_add(&scb->list, &wd->active_scbs);
+
+ scb->phys = phys;
+ scb->cmd = cmd;
+ cmd->host_scribble = (char *) scb;
+
+ scb->CDB_tag = 0; /* Tagged queueing not supported yet */
+ scb->devid = cmd->device->id;
+ scb->lun = cmd->device->lun;
+
+ /* copy the command */
+ memcpy(scb->CDB, cmd->cmnd, cmd->cmd_len);
+
+ /* map sense buffer */
+ scb->sense_buf_length = SCSI_SENSE_BUFFERSIZE;
+ cmd->SCp.dma_handle = dma_map_single(&wd->pdev->dev, cmd->sense_buffer,
+ SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
+ scb->sense_buf = cpu_to_le32(cmd->SCp.dma_handle);
+
+ /* request autosense */
+ scb->SCB_options |= WD719X_SCB_FLAGS_AUTO_REQUEST_SENSE;
+
+ /* check direction */
+ if (cmd->sc_data_direction == DMA_TO_DEVICE)
+ scb->SCB_options |= WD719X_SCB_FLAGS_CHECK_DIRECTION
+ | WD719X_SCB_FLAGS_PCI_TO_SCSI;
+ else if (cmd->sc_data_direction == DMA_FROM_DEVICE)
+ scb->SCB_options |= WD719X_SCB_FLAGS_CHECK_DIRECTION;
+
+ /* Scather/gather */
+ count_sg = scsi_dma_map(cmd);
+ if (count_sg < 0) {
+ wd719x_finish_cmd(cmd, DID_ERROR);
+ spin_unlock_irqrestore(wd->sh->host_lock, flags);
+ return 0;
+ }
+ BUG_ON(count_sg > WD719X_SG);
+
+ if (count_sg) {
+ struct scatterlist *sg;
+
+ scb->data_length = cpu_to_le32(count_sg *
+ sizeof(struct wd719x_sglist));
+ scb->data_p = cpu_to_le32(scb->phys +
+ offsetof(struct wd719x_scb, sg_list));
+
+ scsi_for_each_sg(cmd, sg, count_sg, i) {
+ scb->sg_list[i].ptr = cpu_to_le32(sg_dma_address(sg));
+ scb->sg_list[i].length = cpu_to_le32(sg_dma_len(sg));
+ }
+ scb->SCB_options |= WD719X_SCB_FLAGS_DO_SCATTER_GATHER;
+ } else { /* zero length */
+ scb->data_length = 0;
+ scb->data_p = 0;
+ }
+
+ /* check if the Command register is free */
+ if (wd719x_readb(wd, WD719X_AMR_COMMAND) != WD719X_CMD_READY) {
+ spin_unlock_irqrestore(wd->sh->host_lock, flags);
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+
+ /* write pointer to the AMR */
+ wd719x_writel(wd, WD719X_AMR_SCB_IN, scb->phys);
+ /* send SCB opcode */
+ wd719x_writeb(wd, WD719X_AMR_COMMAND, WD719X_CMD_PROCESS_SCB);
+
+ spin_unlock_irqrestore(wd->sh->host_lock, flags);
+
+ return 0;
+}
+
+static int wd719x_chip_init(struct wd719x *wd)
+{
+ int i, ret;
+ u32 risc_init[3];
+ const struct firmware *fw_wcs, *fw_risc;
+ const char fwname_wcs[] = "wd719x-wcs.bin";
+ const char fwname_risc[] = "wd719x-risc.bin";
+
+ memset(wd->hash_virt, 0, WD719X_HASH_TABLE_SIZE);
+
+ /* WCS (sequencer) firmware */
+ ret = request_firmware(&fw_wcs, fwname_wcs, &wd->pdev->dev);
+ if (ret) {
+ dev_err(&wd->pdev->dev, "Unable to load firmware %s: %d\n",
+ fwname_wcs, ret);
+ return ret;
+ }
+ /* RISC firmware */
+ ret = request_firmware(&fw_risc, fwname_risc, &wd->pdev->dev);
+ if (ret) {
+ dev_err(&wd->pdev->dev, "Unable to load firmware %s: %d\n",
+ fwname_risc, ret);
+ release_firmware(fw_wcs);
+ return ret;
+ }
+ wd->fw_size = ALIGN(fw_wcs->size, 4) + fw_risc->size;
+
+ if (!wd->fw_virt)
+ wd->fw_virt = pci_alloc_consistent(wd->pdev, wd->fw_size,
+ &wd->fw_phys);
+ if (!wd->fw_virt) {
+ ret = -ENOMEM;
+ goto wd719x_init_end;
+ }
+
+ /* make a fresh copy of WCS and RISC code */
+ memcpy(wd->fw_virt, fw_wcs->data, fw_wcs->size);
+ memcpy(wd->fw_virt + ALIGN(fw_wcs->size, 4), fw_risc->data,
+ fw_risc->size);
+
+ /* Reset the Spider Chip and adapter itself */
+ wd719x_writeb(wd, WD719X_PCI_PORT_RESET, WD719X_PCI_RESET);
+ udelay(WD719X_WAIT_FOR_RISC);
+ /* Clear PIO mode bits set by BIOS */
+ wd719x_writeb(wd, WD719X_AMR_CMD_PARAM, 0);
+ /* ensure RISC is not running */
+ wd719x_writeb(wd, WD719X_PCI_MODE_SELECT, 0);
+ /* ensure command port is ready */
+ wd719x_writeb(wd, WD719X_AMR_COMMAND, 0);
+ if (wd719x_wait_ready(wd)) {
+ ret = -ETIMEDOUT;
+ goto wd719x_init_end;
+ }
+
+ /* Transfer the first 2K words of RISC code to kick start the uP */
+ risc_init[0] = wd->fw_phys; /* WCS FW */
+ risc_init[1] = wd->fw_phys + ALIGN(fw_wcs->size, 4); /* RISC FW */
+ risc_init[2] = wd->hash_phys; /* hash table */
+
+ /* clear DMA status */
+ wd719x_writeb(wd, WD719X_PCI_CHANNEL2_3STATUS, 0);
+
+ /* address to read firmware from */
+ wd719x_writel(wd, WD719X_PCI_EXTERNAL_ADDR, risc_init[1]);
+ /* base address to write firmware to (on card) */
+ wd719x_writew(wd, WD719X_PCI_INTERNAL_ADDR, WD719X_PRAM_BASE_ADDR);
+ /* size: first 2K words */
+ wd719x_writew(wd, WD719X_PCI_DMA_TRANSFER_SIZE, 2048 * 2);
+ /* start DMA */
+ wd719x_writeb(wd, WD719X_PCI_CHANNEL2_3CMD, WD719X_START_CHANNEL2_3DMA);
+
+ /* wait for DMA to complete */
+ i = WD719X_WAIT_FOR_RISC;
+ while (i-- > 0) {
+ u8 status = wd719x_readb(wd, WD719X_PCI_CHANNEL2_3STATUS);
+ if (status == WD719X_START_CHANNEL2_3DONE)
+ break;
+ if (status == WD719X_START_CHANNEL2_3ABORT) {
+ dev_warn(&wd->pdev->dev, "RISC bootstrap failed: DMA aborted\n");
+ ret = -EIO;
+ goto wd719x_init_end;
+ }
+ udelay(1);
+ }
+ if (i < 1) {
+ dev_warn(&wd->pdev->dev, "RISC bootstrap failed: DMA timeout\n");
+ ret = -ETIMEDOUT;
+ goto wd719x_init_end;
+ }
+
+ /* firmware is loaded, now initialize and wake up the RISC */
+ /* write RISC initialization long words to Spider */
+ wd719x_writel(wd, WD719X_AMR_SCB_IN, risc_init[0]);
+ wd719x_writel(wd, WD719X_AMR_SCB_IN + 4, risc_init[1]);
+ wd719x_writel(wd, WD719X_AMR_SCB_IN + 8, risc_init[2]);
+
+ /* disable interrupts during initialization of RISC */
+ wd719x_writeb(wd, WD719X_AMR_CMD_PARAM, WD719X_DISABLE_INT);
+
+ /* issue INITIALIZE RISC comand */
+ wd719x_writeb(wd, WD719X_AMR_COMMAND, WD719X_CMD_INIT_RISC);
+ /* enable advanced mode (wake up RISC) */
+ wd719x_writeb(wd, WD719X_PCI_MODE_SELECT, WD719X_ENABLE_ADVANCE_MODE);
+ udelay(WD719X_WAIT_FOR_RISC);
+
+ ret = wd719x_wait_done(wd, WD719X_WAIT_FOR_RISC);
+ /* clear interrupt status register */
+ wd719x_writeb(wd, WD719X_AMR_INT_STATUS, WD719X_INT_NONE);
+ if (ret) {
+ dev_warn(&wd->pdev->dev, "Unable to initialize RISC\n");
+ goto wd719x_init_end;
+ }
+ /* RISC is up and running */
+
+ /* Read FW version from RISC */
+ ret = wd719x_direct_cmd(wd, WD719X_CMD_READ_FIRMVER, 0, 0, 0, 0,
+ WD719X_WAIT_FOR_RISC);
+ if (ret) {
+ dev_warn(&wd->pdev->dev, "Unable to read firmware version\n");
+ goto wd719x_init_end;
+ }
+ dev_info(&wd->pdev->dev, "RISC initialized with firmware version %.2x.%.2x\n",
+ wd719x_readb(wd, WD719X_AMR_SCB_OUT + 1),
+ wd719x_readb(wd, WD719X_AMR_SCB_OUT));
+
+ /* RESET SCSI bus */
+ ret = wd719x_direct_cmd(wd, WD719X_CMD_BUSRESET, 0, 0, 0, 0,
+ WD719X_WAIT_FOR_SCSI_RESET);
+ if (ret) {
+ dev_warn(&wd->pdev->dev, "SCSI bus reset failed\n");
+ goto wd719x_init_end;
+ }
+
+ /* use HostParameter structure to set Spider's Host Parameter Block */
+ ret = wd719x_direct_cmd(wd, WD719X_CMD_SET_PARAM, 0,
+ sizeof(struct wd719x_host_param), 0,
+ wd->params_phys, WD719X_WAIT_FOR_RISC);
+ if (ret) {
+ dev_warn(&wd->pdev->dev, "Failed to set HOST PARAMETERS\n");
+ goto wd719x_init_end;
+ }
+
+ /* initiate SCAM (does nothing if disabled in BIOS) */
+ /* bug?: we should pass a mask of static IDs which we don't have */
+ ret = wd719x_direct_cmd(wd, WD719X_CMD_INIT_SCAM, 0, 0, 0, 0,
+ WD719X_WAIT_FOR_SCSI_RESET);
+ if (ret) {
+ dev_warn(&wd->pdev->dev, "SCAM initialization failed\n");
+ goto wd719x_init_end;
+ }
+
+ /* clear AMR_BIOS_SHARE_INT register */
+ wd719x_writeb(wd, WD719X_AMR_BIOS_SHARE_INT, 0);
+
+wd719x_init_end:
+ release_firmware(fw_wcs);
+ release_firmware(fw_risc);
+
+ return ret;
+}
+
+static int wd719x_abort(struct scsi_cmnd *cmd)
+{
+ int action, result;
+ unsigned long flags;
+ struct wd719x_scb *scb = (struct wd719x_scb *)cmd->host_scribble;
+ struct wd719x *wd = shost_priv(cmd->device->host);
+
+ dev_info(&wd->pdev->dev, "abort command, tag: %x\n", cmd->tag);
+
+ action = /*cmd->tag ? WD719X_CMD_ABORT_TAG : */WD719X_CMD_ABORT;
+
+ spin_lock_irqsave(wd->sh->host_lock, flags);
+ result = wd719x_direct_cmd(wd, action, cmd->device->id,
+ cmd->device->lun, cmd->tag, scb->phys, 0);
+ spin_unlock_irqrestore(wd->sh->host_lock, flags);
+ if (result)
+ return FAILED;
+
+ return SUCCESS;
+}
+
+static int wd719x_reset(struct scsi_cmnd *cmd, u8 opcode, u8 device)
+{
+ int result;
+ unsigned long flags;
+ struct wd719x *wd = shost_priv(cmd->device->host);
+
+ dev_info(&wd->pdev->dev, "%s reset requested\n",
+ (opcode == WD719X_CMD_BUSRESET) ? "bus" : "device");
+
+ spin_lock_irqsave(wd->sh->host_lock, flags);
+ result = wd719x_direct_cmd(wd, opcode, device, 0, 0, 0,
+ WD719X_WAIT_FOR_SCSI_RESET);
+ spin_unlock_irqrestore(wd->sh->host_lock, flags);
+ if (result)
+ return FAILED;
+
+ return SUCCESS;
+}
+
+static int wd719x_dev_reset(struct scsi_cmnd *cmd)
+{
+ return wd719x_reset(cmd, WD719X_CMD_RESET, cmd->device->id);
+}
+
+static int wd719x_bus_reset(struct scsi_cmnd *cmd)
+{
+ return wd719x_reset(cmd, WD719X_CMD_BUSRESET, 0);
+}
+
+static int wd719x_host_reset(struct scsi_cmnd *cmd)
+{
+ struct wd719x *wd = shost_priv(cmd->device->host);
+ struct wd719x_scb *scb, *tmp;
+ unsigned long flags;
+ int result;
+
+ dev_info(&wd->pdev->dev, "host reset requested\n");
+ spin_lock_irqsave(wd->sh->host_lock, flags);
+ /* Try to reinit the RISC */
+ if (wd719x_chip_init(wd) == 0)
+ result = SUCCESS;
+ else
+ result = FAILED;
+
+ /* flush all SCBs */
+ list_for_each_entry_safe(scb, tmp, &wd->active_scbs, list) {
+ struct scsi_cmnd *tmp_cmd = scb->cmd;
+ wd719x_finish_cmd(tmp_cmd, result);
+ }
+ spin_unlock_irqrestore(wd->sh->host_lock, flags);
+
+ return result;
+}
+
+static int wd719x_biosparam(struct scsi_device *sdev, struct block_device *bdev,
+ sector_t capacity, int geom[])
+{
+ if (capacity >= 0x200000) {
+ geom[0] = 255; /* heads */
+ geom[1] = 63; /* sectors */
+ } else {
+ geom[0] = 64; /* heads */
+ geom[1] = 32; /* sectors */
+ }
+ geom[2] = sector_div(capacity, geom[0] * geom[1]); /* cylinders */
+
+ return 0;
+}
+
+/* process a SCB-completion interrupt */
+static inline void wd719x_interrupt_SCB(struct wd719x *wd,
+ union wd719x_regs regs,
+ struct wd719x_scb *scb)
+{
+ struct scsi_cmnd *cmd;
+ int result;
+
+ /* now have to find result from card */
+ switch (regs.bytes.SUE) {
+ case WD719X_SUE_NOERRORS:
+ result = DID_OK;
+ break;
+ case WD719X_SUE_REJECTED:
+ dev_err(&wd->pdev->dev, "command rejected\n");
+ result = DID_ERROR;
+ break;
+ case WD719X_SUE_SCBQFULL:
+ dev_err(&wd->pdev->dev, "SCB queue is full\n");
+ result = DID_ERROR;
+ break;
+ case WD719X_SUE_TERM:
+ dev_dbg(&wd->pdev->dev, "SCB terminated by direct command\n");
+ result = DID_ABORT; /* or DID_RESET? */
+ break;
+ case WD719X_SUE_CHAN1ABORT:
+ case WD719X_SUE_CHAN23ABORT:
+ result = DID_ABORT;
+ dev_err(&wd->pdev->dev, "DMA abort\n");
+ break;
+ case WD719X_SUE_CHAN1PAR:
+ case WD719X_SUE_CHAN23PAR:
+ result = DID_PARITY;
+ dev_err(&wd->pdev->dev, "DMA parity error\n");
+ break;
+ case WD719X_SUE_TIMEOUT:
+ result = DID_TIME_OUT;
+ dev_dbg(&wd->pdev->dev, "selection timeout\n");
+ break;
+ case WD719X_SUE_RESET:
+ dev_dbg(&wd->pdev->dev, "bus reset occured\n");
+ result = DID_RESET;
+ break;
+ case WD719X_SUE_BUSERROR:
+ dev_dbg(&wd->pdev->dev, "SCSI bus error\n");
+ result = DID_ERROR;
+ break;
+ case WD719X_SUE_WRONGWAY:
+ dev_err(&wd->pdev->dev, "wrong data transfer direction\n");
+ result = DID_ERROR;
+ break;
+ case WD719X_SUE_BADPHASE:
+ dev_err(&wd->pdev->dev, "invalid SCSI phase\n");
+ result = DID_ERROR;
+ break;
+ case WD719X_SUE_TOOLONG:
+ dev_err(&wd->pdev->dev, "record too long\n");
+ result = DID_ERROR;
+ break;
+ case WD719X_SUE_BUSFREE:
+ dev_err(&wd->pdev->dev, "unexpected bus free\n");
+ result = DID_NO_CONNECT; /* or DID_ERROR ???*/
+ break;
+ case WD719X_SUE_ARSDONE:
+ dev_dbg(&wd->pdev->dev, "auto request sense\n");
+ if (regs.bytes.SCSI == 0)
+ result = DID_OK;
+ else
+ result = DID_PARITY;
+ break;
+ case WD719X_SUE_IGNORED:
+ dev_err(&wd->pdev->dev, "target id %d ignored command\n",
+ scb->cmd->device->id);
+ result = DID_NO_CONNECT;
+ break;
+ case WD719X_SUE_WRONGTAGS:
+ dev_err(&wd->pdev->dev, "reversed tags\n");
+ result = DID_ERROR;
+ break;
+ case WD719X_SUE_BADTAGS:
+ dev_err(&wd->pdev->dev, "tag type not supported by target\n");
+ result = DID_ERROR;
+ break;
+ case WD719X_SUE_NOSCAMID:
+ dev_err(&wd->pdev->dev, "no SCAM soft ID available\n");
+ result = DID_ERROR;
+ break;
+ default:
+ dev_warn(&wd->pdev->dev, "unknown SUE error code: 0x%x\n",
+ regs.bytes.SUE);
+ result = DID_ERROR;
+ break;
+ }
+ cmd = scb->cmd;
+
+ wd719x_finish_cmd(cmd, result);
+}
+
+static irqreturn_t wd719x_interrupt(int irq, void *dev_id)
+{
+ struct wd719x *wd = dev_id;
+ union wd719x_regs regs;
+ unsigned long flags;
+ u32 SCB_out;
+
+ spin_lock_irqsave(wd->sh->host_lock, flags);
+ /* read SCB pointer back from card */
+ SCB_out = wd719x_readl(wd, WD719X_AMR_SCB_OUT);
+ /* read all status info at once */
+ regs.all = cpu_to_le32(wd719x_readl(wd, WD719X_AMR_OP_CODE));
+
+ switch (regs.bytes.INT) {
+ case WD719X_INT_NONE:
+ spin_unlock_irqrestore(wd->sh->host_lock, flags);
+ return IRQ_NONE;
+ case WD719X_INT_LINKNOSTATUS:
+ dev_err(&wd->pdev->dev, "linked command completed with no status\n");
+ break;
+ case WD719X_INT_BADINT:
+ dev_err(&wd->pdev->dev, "unsolicited interrupt\n");
+ break;
+ case WD719X_INT_NOERRORS:
+ case WD719X_INT_LINKNOERRORS:
+ case WD719X_INT_ERRORSLOGGED:
+ case WD719X_INT_SPIDERFAILED:
+ /* was the cmd completed a direct or SCB command? */
+ if (regs.bytes.OPC == WD719X_CMD_PROCESS_SCB) {
+ struct wd719x_scb *scb;
+ list_for_each_entry(scb, &wd->active_scbs, list)
+ if (SCB_out == scb->phys)
+ break;
+ if (SCB_out == scb->phys)
+ wd719x_interrupt_SCB(wd, regs, scb);
+ else
+ dev_err(&wd->pdev->dev, "card returned invalid SCB pointer\n");
+ } else
+ dev_warn(&wd->pdev->dev, "direct command 0x%x completed\n",
+ regs.bytes.OPC);
+ break;
+ case WD719X_INT_PIOREADY:
+ dev_err(&wd->pdev->dev, "card indicates PIO data ready but we never use PIO\n");
+ /* interrupt will not be cleared until all data is read */
+ break;
+ default:
+ dev_err(&wd->pdev->dev, "unknown interrupt reason: %d\n",
+ regs.bytes.INT);
+
+ }
+ /* clear interrupt so another can happen */
+ wd719x_writeb(wd, WD719X_AMR_INT_STATUS, WD719X_INT_NONE);
+ spin_unlock_irqrestore(wd->sh->host_lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static void wd719x_eeprom_reg_read(struct eeprom_93cx6 *eeprom)
+{
+ struct wd719x *wd = eeprom->data;
+ u8 reg = wd719x_readb(wd, WD719X_PCI_GPIO_DATA);
+
+ eeprom->reg_data_out = reg & WD719X_EE_DO;
+}
+
+static void wd719x_eeprom_reg_write(struct eeprom_93cx6 *eeprom)
+{
+ struct wd719x *wd = eeprom->data;
+ u8 reg = 0;
+
+ if (eeprom->reg_data_in)
+ reg |= WD719X_EE_DI;
+ if (eeprom->reg_data_clock)
+ reg |= WD719X_EE_CLK;
+ if (eeprom->reg_chip_select)
+ reg |= WD719X_EE_CS;
+
+ wd719x_writeb(wd, WD719X_PCI_GPIO_DATA, reg);
+}
+
+/* read config from EEPROM so it can be downloaded by the RISC on (re-)init */
+static void wd719x_read_eeprom(struct wd719x *wd)
+{
+ struct eeprom_93cx6 eeprom;
+ u8 gpio;
+ struct wd719x_eeprom_header header;
+
+ eeprom.data = wd;
+ eeprom.register_read = wd719x_eeprom_reg_read;
+ eeprom.register_write = wd719x_eeprom_reg_write;
+ eeprom.width = PCI_EEPROM_WIDTH_93C46;
+
+ /* set all outputs to low */
+ wd719x_writeb(wd, WD719X_PCI_GPIO_DATA, 0);
+ /* configure GPIO pins */
+ gpio = wd719x_readb(wd, WD719X_PCI_GPIO_CONTROL);
+ /* GPIO outputs */
+ gpio &= (~(WD719X_EE_CLK | WD719X_EE_DI | WD719X_EE_CS));
+ /* GPIO input */
+ gpio |= WD719X_EE_DO;
+ wd719x_writeb(wd, WD719X_PCI_GPIO_CONTROL, gpio);
+
+ /* read EEPROM header */
+ eeprom_93cx6_multireadb(&eeprom, 0, (u8 *)&header, sizeof(header));
+
+ if (header.sig1 == 'W' && header.sig2 == 'D')
+ eeprom_93cx6_multireadb(&eeprom, header.cfg_offset,
+ (u8 *)wd->params,
+ sizeof(struct wd719x_host_param));
+ else { /* default EEPROM values */
+ dev_warn(&wd->pdev->dev, "EEPROM signature is invalid (0x%02x 0x%02x), using default values\n",
+ header.sig1, header.sig2);
+ wd->params->ch_1_th = 0x10; /* 16 DWs = 64 B */
+ wd->params->scsi_conf = 0x4c; /* 48ma, spue, parity check */
+ wd->params->own_scsi_id = 0x07; /* ID 7, SCAM disabled */
+ wd->params->sel_timeout = 0x4d; /* 250 ms */
+ wd->params->sleep_timer = 0x01;
+ wd->params->cdb_size = cpu_to_le16(0x5555); /* all 6 B */
+ wd->params->scsi_pad = 0x1b;
+ if (wd->type == WD719X_TYPE_7193) /* narrow card - disable */
+ wd->params->wide = cpu_to_le32(0x00000000);
+ else /* initiate & respond to WIDE messages */
+ wd->params->wide = cpu_to_le32(0xffffffff);
+ wd->params->sync = cpu_to_le32(0xffffffff);
+ wd->params->soft_mask = 0x00; /* all disabled */
+ wd->params->unsol_mask = 0x00; /* all disabled */
+ }
+ /* disable TAGGED messages */
+ wd->params->tag_en = cpu_to_le16(0x0000);
+}
+
+/* Read card type from GPIO bits 1 and 3 */
+static enum wd719x_card_type wd719x_detect_type(struct wd719x *wd)
+{
+ u8 card = wd719x_readb(wd, WD719X_PCI_GPIO_CONTROL);
+
+ card |= WD719X_GPIO_ID_BITS;
+ wd719x_writeb(wd, WD719X_PCI_GPIO_CONTROL, card);
+ card = wd719x_readb(wd, WD719X_PCI_GPIO_DATA) & WD719X_GPIO_ID_BITS;
+ switch (card) {
+ case 0x08:
+ return WD719X_TYPE_7193;
+ case 0x02:
+ return WD719X_TYPE_7197;
+ case 0x00:
+ return WD719X_TYPE_7296;
+ default:
+ dev_warn(&wd->pdev->dev, "unknown card type 0x%x\n", card);
+ return WD719X_TYPE_UNKNOWN;
+ }
+}
+
+static int wd719x_board_found(struct Scsi_Host *sh)
+{
+ struct wd719x *wd = shost_priv(sh);
+ char *card_types[] = { "Unknown card", "WD7193", "WD7197", "WD7296" };
+ int ret;
+
+ INIT_LIST_HEAD(&wd->active_scbs);
+ INIT_LIST_HEAD(&wd->free_scbs);
+
+ sh->base = pci_resource_start(wd->pdev, 0);
+
+ wd->type = wd719x_detect_type(wd);
+
+ wd->sh = sh;
+ sh->irq = wd->pdev->irq;
+ wd->fw_virt = NULL;
+
+ /* memory area for host (EEPROM) parameters */
+ wd->params = pci_alloc_consistent(wd->pdev,
+ sizeof(struct wd719x_host_param),
+ &wd->params_phys);
+ if (!wd->params) {
+ dev_warn(&wd->pdev->dev, "unable to allocate parameter buffer\n");
+ return -ENOMEM;
+ }
+
+ /* memory area for the RISC for hash table of outstanding requests */
+ wd->hash_virt = pci_alloc_consistent(wd->pdev, WD719X_HASH_TABLE_SIZE,
+ &wd->hash_phys);
+ if (!wd->hash_virt) {
+ dev_warn(&wd->pdev->dev, "unable to allocate hash buffer\n");
+ ret = -ENOMEM;
+ goto fail_free_params;
+ }
+
+ ret = request_irq(wd->pdev->irq, wd719x_interrupt, IRQF_SHARED,
+ "wd719x", wd);
+ if (ret) {
+ dev_warn(&wd->pdev->dev, "unable to assign IRQ %d\n",
+ wd->pdev->irq);
+ goto fail_free_hash;
+ }
+
+ /* read parameters from EEPROM */
+ wd719x_read_eeprom(wd);
+
+ ret = wd719x_chip_init(wd);
+ if (ret)
+ goto fail_free_irq;
+
+ sh->this_id = wd->params->own_scsi_id & WD719X_EE_SCSI_ID_MASK;
+
+ dev_info(&wd->pdev->dev, "%s at I/O 0x%lx, IRQ %u, SCSI ID %d\n",
+ card_types[wd->type], sh->base, sh->irq, sh->this_id);
+
+ return 0;
+
+fail_free_irq:
+ free_irq(wd->pdev->irq, wd);
+fail_free_hash:
+ pci_free_consistent(wd->pdev, WD719X_HASH_TABLE_SIZE, wd->hash_virt,
+ wd->hash_phys);
+fail_free_params:
+ pci_free_consistent(wd->pdev, sizeof(struct wd719x_host_param),
+ wd->params, wd->params_phys);
+
+ return ret;
+}
+
+static struct scsi_host_template wd719x_template = {
+ .name = "Western Digital 719x",
+ .queuecommand = wd719x_queuecommand,
+ .eh_abort_handler = wd719x_abort,
+ .eh_device_reset_handler = wd719x_dev_reset,
+ .eh_bus_reset_handler = wd719x_bus_reset,
+ .eh_host_reset_handler = wd719x_host_reset,
+ .bios_param = wd719x_biosparam,
+ .proc_name = "wd719x",
+ .can_queue = 255,
+ .this_id = 7,
+ .sg_tablesize = WD719X_SG,
+ .cmd_per_lun = WD719X_CMD_PER_LUN,
+ .use_clustering = ENABLE_CLUSTERING,
+};
+
+static int wd719x_pci_probe(struct pci_dev *pdev, const struct pci_device_id *d)
+{
+ int err;
+ struct Scsi_Host *sh;
+ struct wd719x *wd;
+
+ err = pci_enable_device(pdev);
+ if (err)
+ goto fail;
+
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ dev_warn(&pdev->dev, "Unable to set 32-bit DMA mask\n");
+ goto disable_device;
+ }
+
+ err = pci_request_regions(pdev, "wd719x");
+ if (err)
+ goto disable_device;
+ pci_set_master(pdev);
+
+ err = -ENODEV;
+ if (pci_resource_len(pdev, 0) == 0)
+ goto release_region;
+
+ err = -ENOMEM;
+ sh = scsi_host_alloc(&wd719x_template, sizeof(struct wd719x));
+ if (!sh)
+ goto release_region;
+
+ wd = shost_priv(sh);
+ wd->base = pci_iomap(pdev, 0, 0);
+ if (!wd->base)
+ goto free_host;
+ wd->pdev = pdev;
+
+ err = wd719x_board_found(sh);
+ if (err)
+ goto unmap;
+
+ err = scsi_add_host(sh, &wd->pdev->dev);
+ if (err)
+ goto destroy;
+
+ scsi_scan_host(sh);
+
+ pci_set_drvdata(pdev, sh);
+ return 0;
+
+destroy:
+ wd719x_destroy(wd);
+unmap:
+ pci_iounmap(pdev, wd->base);
+free_host:
+ scsi_host_put(sh);
+release_region:
+ pci_release_regions(pdev);
+disable_device:
+ pci_disable_device(pdev);
+fail:
+ return err;
+}
+
+
+static void wd719x_pci_remove(struct pci_dev *pdev)
+{
+ struct Scsi_Host *sh = pci_get_drvdata(pdev);
+ struct wd719x *wd = shost_priv(sh);
+
+ scsi_remove_host(sh);
+ wd719x_destroy(wd);
+ pci_iounmap(pdev, wd->base);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+
+ scsi_host_put(sh);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(wd719x_pci_table) = {
+ { PCI_DEVICE(PCI_VENDOR_ID_WD, 0x3296) },
+ {}
+};
+
+MODULE_DEVICE_TABLE(pci, wd719x_pci_table);
+
+static struct pci_driver wd719x_pci_driver = {
+ .name = "wd719x",
+ .id_table = wd719x_pci_table,
+ .probe = wd719x_pci_probe,
+ .remove = wd719x_pci_remove,
+};
+
+static int __init wd719x_init(void)
+{
+ return pci_register_driver(&wd719x_pci_driver);
+}
+
+static void __exit wd719x_exit(void)
+{
+ pci_unregister_driver(&wd719x_pci_driver);
+}
+
+module_init(wd719x_init);
+module_exit(wd719x_exit);
+
+MODULE_DESCRIPTION("Western Digital WD7193/7197/7296 SCSI driver");
+MODULE_AUTHOR("Ondrej Zary, Aaron Dewell, Juergen Gaertner");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("wd719x-wcs.bin");
+MODULE_FIRMWARE("wd719x-risc.bin");
--- /dev/null
+#ifndef _WD719X_H_
+#define _WD719X_H_
+
+#define WD719X_SG 255 /* Scatter/gather size */
+#define WD719X_CMD_PER_LUN 1 /* We should be able to do linked commands, but
+ * this is 1 for now to be safe. */
+
+struct wd719x_sglist {
+ __le32 ptr;
+ __le32 length;
+} __packed;
+
+enum wd719x_card_type {
+ WD719X_TYPE_UNKNOWN = 0,
+ WD719X_TYPE_7193,
+ WD719X_TYPE_7197,
+ WD719X_TYPE_7296,
+};
+
+union wd719x_regs {
+ __le32 all; /* All Status at once */
+ struct {
+ u8 OPC; /* Opcode register */
+ u8 SCSI; /* SCSI Errors */
+ u8 SUE; /* Spider unique Errors */
+ u8 INT; /* Interrupt Status */
+ } bytes;
+};
+
+/* Spider Command Block (SCB) */
+struct wd719x_scb {
+ __le32 Int_SCB; /* 00-03 Internal SCB link pointer (must be cleared) */
+ u8 SCB_opcode; /* 04 SCB Command opcode */
+ u8 CDB_tag; /* 05 SCSI Tag byte for CDB queues (0 if untagged) */
+ u8 lun; /* 06 SCSI LUN */
+ u8 devid; /* 07 SCSI Device ID */
+ u8 CDB[16]; /* 08-23 SCSI CDB (16 bytes as defined by ANSI spec. */
+ __le32 data_p; /* 24-27 Data transfer address (or SG list address) */
+ __le32 data_length; /* 28-31 Data transfer Length (or SG list length) */
+ __le32 CDB_link; /* 32-35 SCSI CDB Link Ptr */
+ __le32 sense_buf; /* 36-39 Auto request sense buffer address */
+ u8 sense_buf_length;/* 40 Auto request sense transfer length */
+ u8 reserved; /* 41 reserved */
+ u8 SCB_options; /* 42 SCB-options */
+ u8 SCB_tag_msg; /* 43 Tagged messages options */
+ /* Not filled in by host */
+ __le32 req_ptr; /* 44-47 Ptr to Host Request returned on interrupt */
+ u8 host_opcode; /* 48 Host Command Opcode (same as AMR_00) */
+ u8 scsi_stat; /* 49 SCSI Status returned */
+ u8 ret_error; /* 50 SPIDER Unique Error Code returned (SUE) */
+ u8 int_stat; /* 51 Message u8 / Interrupt Status byte returned */
+ __le32 transferred; /* 52-55 Bytes Transferred */
+ u8 last_trans[3]; /* 56-58 Bytes Transferred in last session */
+ u8 length; /* 59 SCSI Messages Length (1-8) */
+ u8 sync_offset; /* 60 Synchronous offset */
+ u8 sync_rate; /* 61 Synchronous rate */
+ u8 flags[2]; /* 62-63 SCB specific flags (local to each thread) */
+ /* everything below is for driver use (not used by card) */
+ dma_addr_t phys; /* bus address of the SCB */
+ struct scsi_cmnd *cmd; /* a copy of the pointer we were passed */
+ struct list_head list;
+ struct wd719x_sglist sg_list[WD719X_SG] __aligned(8); /* SG list */
+} __packed;
+
+struct wd719x {
+ struct Scsi_Host *sh; /* pointer to host structure */
+ struct pci_dev *pdev;
+ void __iomem *base;
+ enum wd719x_card_type type; /* type of card */
+ void *fw_virt; /* firmware buffer CPU address */
+ dma_addr_t fw_phys; /* firmware buffer bus address */
+ size_t fw_size; /* firmware buffer size */
+ struct wd719x_host_param *params; /* host parameters (EEPROM) */
+ dma_addr_t params_phys; /* host parameters bus address */
+ void *hash_virt; /* hash table CPU address */
+ dma_addr_t hash_phys; /* hash table bus address */
+ struct list_head active_scbs;
+ struct list_head free_scbs;
+};
+
+/* timeout delays in microsecs */
+#define WD719X_WAIT_FOR_CMD_READY 500
+#define WD719X_WAIT_FOR_RISC 2000
+#define WD719X_WAIT_FOR_SCSI_RESET 3000000
+
+/* All commands except 0x00 generate an interrupt */
+#define WD719X_CMD_READY 0x00 /* Command register ready (or noop) */
+#define WD719X_CMD_INIT_RISC 0x01 /* Initialize RISC */
+/* 0x02 is reserved */
+#define WD719X_CMD_BUSRESET 0x03 /* Assert SCSI bus reset */
+#define WD719X_CMD_READ_FIRMVER 0x04 /* Read the Firmware Revision */
+#define WD719X_CMD_ECHO_BYTES 0x05 /* Echo command bytes (DW) */
+/* 0x06 is reserved */
+/* 0x07 is reserved */
+#define WD719X_CMD_GET_PARAM 0x08 /* Get programmable parameters */
+#define WD719X_CMD_SET_PARAM 0x09 /* Set programmable parameters */
+#define WD719X_CMD_SLEEP 0x0a /* Put SPIDER to sleep */
+#define WD719X_CMD_READ_INIT 0x0b /* Read initialization parameters */
+#define WD719X_CMD_RESTORE_INIT 0x0c /* Restore initialization parameters */
+/* 0x0d is reserved */
+/* 0x0e is reserved */
+/* 0x0f is reserved */
+#define WD719X_CMD_ABORT_TAG 0x10 /* Send Abort tag message to target */
+#define WD719X_CMD_ABORT 0x11 /* Send Abort message to target */
+#define WD719X_CMD_RESET 0x12 /* Send Reset message to target */
+#define WD719X_CMD_INIT_SCAM 0x13 /* Initiate SCAM */
+#define WD719X_CMD_GET_SYNC 0x14 /* Get synchronous rates */
+#define WD719X_CMD_SET_SYNC 0x15 /* Set synchronous rates */
+#define WD719X_CMD_GET_WIDTH 0x16 /* Get SCSI bus width */
+#define WD719X_CMD_SET_WIDTH 0x17 /* Set SCSI bus width */
+#define WD719X_CMD_GET_TAGS 0x18 /* Get tag flags */
+#define WD719X_CMD_SET_TAGS 0x19 /* Set tag flags */
+#define WD719X_CMD_GET_PARAM2 0x1a /* Get programmable params (format 2) */
+#define WD719X_CMD_SET_PARAM2 0x1b /* Set programmable params (format 2) */
+/* Commands with request pointers (mailbox) */
+#define WD719X_CMD_PROCESS_SCB 0x80 /* Process SCSI Control Block (SCB) */
+/* No interrupt generated on acceptance of SCB pointer */
+
+/* interrupt status defines */
+#define WD719X_INT_NONE 0x00 /* No interrupt pending */
+#define WD719X_INT_NOERRORS 0x01 /* Command completed with no errors */
+#define WD719X_INT_LINKNOERRORS 0x02 /* link cmd completed with no errors */
+#define WD719X_INT_LINKNOSTATUS 0x03 /* link cmd completed with no flag set */
+#define WD719X_INT_ERRORSLOGGED 0x04 /* cmd completed with errors logged */
+#define WD719X_INT_SPIDERFAILED 0x05 /* cmd failed without valid SCSI status */
+#define WD719X_INT_BADINT 0x80 /* unsolicited interrupt */
+#define WD719X_INT_PIOREADY 0xf0 /* data ready for PIO output */
+
+/* Spider Unique Error Codes (SUE) */
+#define WD719X_SUE_NOERRORS 0x00 /* No errors detected by SPIDER */
+#define WD719X_SUE_REJECTED 0x01 /* Command Rejected (bad opcode/param) */
+#define WD719X_SUE_SCBQFULL 0x02 /* SCB queue full */
+/* 0x03 is reserved */
+#define WD719X_SUE_TERM 0x04 /* Host terminated SCB via primative cmd */
+#define WD719X_SUE_CHAN1PAR 0x05 /* PCI Channel 1 parity error occurred */
+#define WD719X_SUE_CHAN1ABORT 0x06 /* PCI Channel 1 system abort occurred */
+#define WD719X_SUE_CHAN23PAR 0x07 /* PCI Channel 2/3 parity error occurred */
+#define WD719X_SUE_CHAN23ABORT 0x08 /* PCI Channel 2/3 system abort occurred */
+#define WD719X_SUE_TIMEOUT 0x10 /* Selection/reselection timeout */
+#define WD719X_SUE_RESET 0x11 /* SCSI bus reset occurred */
+#define WD719X_SUE_BUSERROR 0x12 /* SCSI bus error */
+#define WD719X_SUE_WRONGWAY 0x13 /* Wrong data transfer dir set by target */
+#define WD719X_SUE_BADPHASE 0x14 /* SCSI phase illegal or unexpected */
+#define WD719X_SUE_TOOLONG 0x15 /* target requested too much data */
+#define WD719X_SUE_BUSFREE 0x16 /* Unexpected SCSI bus free */
+#define WD719X_SUE_ARSDONE 0x17 /* Auto request sense executed */
+#define WD719X_SUE_IGNORED 0x18 /* SCSI message was ignored by target */
+#define WD719X_SUE_WRONGTAGS 0x19 /* Tagged SCB & tags off (or vice versa) */
+#define WD719X_SUE_BADTAGS 0x1a /* Wrong tag message type for target */
+#define WD719X_SUE_NOSCAMID 0x1b /* No SCAM soft ID available */
+
+/* code sizes */
+#define WD719X_HASH_TABLE_SIZE 4096
+
+/* Advanced Mode Registers */
+/* Regs 0x00..0x1f are for Advanced Mode of the card (RISC is running). */
+#define WD719X_AMR_COMMAND 0x00
+#define WD719X_AMR_CMD_PARAM 0x01
+#define WD719X_AMR_CMD_PARAM_2 0x02
+#define WD719X_AMR_CMD_PARAM_3 0x03
+#define WD719X_AMR_SCB_IN 0x04
+
+#define WD719X_AMR_BIOS_SHARE_INT 0x0f
+
+#define WD719X_AMR_SCB_OUT 0x18
+#define WD719X_AMR_OP_CODE 0x1c
+#define WD719X_AMR_SCSI_STATUS 0x1d
+#define WD719X_AMR_SCB_ERROR 0x1e
+#define WD719X_AMR_INT_STATUS 0x1f
+
+#define WD719X_DISABLE_INT 0x80
+
+/* SCB flags */
+#define WD719X_SCB_FLAGS_CHECK_DIRECTION 0x01
+#define WD719X_SCB_FLAGS_PCI_TO_SCSI 0x02
+#define WD719X_SCB_FLAGS_AUTO_REQUEST_SENSE 0x10
+#define WD719X_SCB_FLAGS_DO_SCATTER_GATHER 0x20
+#define WD719X_SCB_FLAGS_NO_DISCONNECT 0x40
+
+/* PCI Registers used for reset, initial code download */
+/* Regs 0x20..0x3f are for Normal (DOS) mode (RISC is asleep). */
+#define WD719X_PCI_GPIO_CONTROL 0x3C
+#define WD719X_PCI_GPIO_DATA 0x3D
+#define WD719X_PCI_PORT_RESET 0x3E
+#define WD719X_PCI_MODE_SELECT 0x3F
+
+#define WD719X_PCI_EXTERNAL_ADDR 0x60
+#define WD719X_PCI_INTERNAL_ADDR 0x64
+#define WD719X_PCI_DMA_TRANSFER_SIZE 0x66
+#define WD719X_PCI_CHANNEL2_3CMD 0x68
+#define WD719X_PCI_CHANNEL2_3STATUS 0x69
+
+#define WD719X_GPIO_ID_BITS 0x0a
+#define WD719X_PRAM_BASE_ADDR 0x00
+
+/* codes written to or read from the card */
+#define WD719X_PCI_RESET 0x01
+#define WD719X_ENABLE_ADVANCE_MODE 0x01
+
+#define WD719X_START_CHANNEL2_3DMA 0x17
+#define WD719X_START_CHANNEL2_3DONE 0x01
+#define WD719X_START_CHANNEL2_3ABORT 0x20
+
+/* 33C296 GPIO bits for EEPROM pins */
+#define WD719X_EE_DI (1 << 1)
+#define WD719X_EE_CS (1 << 2)
+#define WD719X_EE_CLK (1 << 3)
+#define WD719X_EE_DO (1 << 4)
+
+/* EEPROM contents */
+struct wd719x_eeprom_header {
+ u8 sig1;
+ u8 sig2;
+ u8 version;
+ u8 checksum;
+ u8 cfg_offset;
+ u8 cfg_size;
+ u8 setup_offset;
+ u8 setup_size;
+} __packed;
+
+#define WD719X_EE_SIG1 0
+#define WD719X_EE_SIG2 1
+#define WD719X_EE_VERSION 2
+#define WD719X_EE_CHECKSUM 3
+#define WD719X_EE_CFG_OFFSET 4
+#define WD719X_EE_CFG_SIZE 5
+#define WD719X_EE_SETUP_OFFSET 6
+#define WD719X_EE_SETUP_SIZE 7
+
+#define WD719X_EE_SCSI_ID_MASK 0xf
+
+/* SPIDER Host Parameters Block (=EEPROM configuration block) */
+struct wd719x_host_param {
+ u8 ch_1_th; /* FIFO threshold */
+ u8 scsi_conf; /* SCSI configuration */
+ u8 own_scsi_id; /* controller SCSI ID */
+ u8 sel_timeout; /* selection timeout*/
+ u8 sleep_timer; /* seep timer */
+ __le16 cdb_size;/* CDB size groups */
+ __le16 tag_en; /* Tag msg enables (ID 0-15) */
+ u8 scsi_pad; /* SCSI pad control */
+ __le32 wide; /* WIDE msg options (ID 0-15) */
+ __le32 sync; /* SYNC msg options (ID 0-15) */
+ u8 soft_mask; /* soft error mask */
+ u8 unsol_mask; /* unsolicited error mask */
+} __packed;
+
+#endif /* _WD719X_H_ */
{ .compatible = "arm,realview-pb11mp-soc", },
{ .compatible = "arm,realview-pba8-soc", },
{ .compatible = "arm,realview-pbx-soc", },
+ { }
};
static u32 realview_coreid;
chip = dws->cur_chip;
spi = message->spi;
- if (unlikely(!chip->clk_div))
- chip->clk_div = dws->max_freq / chip->speed_hz;
-
if (message->state == ERROR_STATE) {
message->status = -EIO;
goto early_exit;
if (transfer->speed_hz) {
speed = chip->speed_hz;
- if (transfer->speed_hz != speed) {
+ if ((transfer->speed_hz != speed) || (!chip->clk_div)) {
speed = transfer->speed_hz;
/* clk_div doesn't support odd number */
dev_err(&spi->dev, "No max speed HZ parameter\n");
return -EINVAL;
}
- chip->speed_hz = spi->max_speed_hz;
chip->tmode = 0; /* Tx & Rx */
/* Default SPI mode is SCPOL = 0, SCPH = 0 */
#define SPI_TCR 0x08
-#define SPI_CTAR(x) (0x0c + (x * 4))
+#define SPI_CTAR(x) (0x0c + (((x) & 0x3) * 4))
#define SPI_CTAR_FMSZ(x) (((x) & 0x0000000f) << 27)
#define SPI_CTAR_CPOL(x) ((x) << 26)
#define SPI_CTAR_CPHA(x) ((x) << 25)
#define SPI_PUSHR 0x34
#define SPI_PUSHR_CONT (1 << 31)
-#define SPI_PUSHR_CTAS(x) (((x) & 0x00000007) << 28)
+#define SPI_PUSHR_CTAS(x) (((x) & 0x00000003) << 28)
#define SPI_PUSHR_EOQ (1 << 27)
#define SPI_PUSHR_CTCNT (1 << 26)
#define SPI_PUSHR_PCS(x) (((1 << x) & 0x0000003f) << 16)
if (status != 0)
return status;
write_SSCR0(0, drv_data->ioaddr);
- clk_disable_unprepare(ssp->clk);
+
+ if (!pm_runtime_suspended(dev))
+ clk_disable_unprepare(ssp->clk);
return 0;
}
pxa2xx_spi_dma_resume(drv_data);
/* Enable the SSP clock */
- clk_prepare_enable(ssp->clk);
+ if (!pm_runtime_suspended(dev))
+ clk_prepare_enable(ssp->clk);
/* Restore LPSS private register bits */
lpss_ssp_setup(drv_data);
sspi->word_width = DIV_ROUND_UP(bits_per_word, 8);
txfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
- sspi->word_width;
+ (sspi->word_width >> 1);
rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
- sspi->word_width;
+ (sspi->word_width >> 1);
if (!(spi->mode & SPI_CS_HIGH))
regval |= SIRFSOC_SPI_CS_IDLE_STAT;
sg_free_table(sgt);
return -ENOMEM;
}
- sg_buf = page_address(vm_page) +
- ((size_t)buf & ~PAGE_MASK);
+ sg_set_page(&sgt->sgl[i], vm_page,
+ min, offset_in_page(buf));
} else {
sg_buf = buf;
+ sg_set_buf(&sgt->sgl[i], sg_buf, min);
}
- sg_set_buf(&sgt->sgl[i], sg_buf, min);
buf += min;
len -= min;
u8 *tx;
u8 *rx;
struct mutex buf_lock;
- const struct iio_chan_spec *ade7758_ring_channels;
struct spi_transfer ring_xfer[4];
struct spi_message ring_msg;
/*
.type = IIO_VOLTAGE,
.indexed = 1,
.channel = 0,
- .extend_name = "raw",
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
.address = AD7758_WT(AD7758_PHASE_A, AD7758_VOLTAGE),
.scan_index = 0,
.scan_type = {
.type = IIO_CURRENT,
.indexed = 1,
.channel = 0,
- .extend_name = "raw",
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
.address = AD7758_WT(AD7758_PHASE_A, AD7758_CURRENT),
.scan_index = 1,
.scan_type = {
.type = IIO_POWER,
.indexed = 1,
.channel = 0,
- .extend_name = "apparent_raw",
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .extend_name = "apparent",
.address = AD7758_WT(AD7758_PHASE_A, AD7758_APP_PWR),
.scan_index = 2,
.scan_type = {
.type = IIO_POWER,
.indexed = 1,
.channel = 0,
- .extend_name = "active_raw",
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .extend_name = "active",
.address = AD7758_WT(AD7758_PHASE_A, AD7758_ACT_PWR),
.scan_index = 3,
.scan_type = {
.type = IIO_POWER,
.indexed = 1,
.channel = 0,
- .extend_name = "reactive_raw",
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .extend_name = "reactive",
.address = AD7758_WT(AD7758_PHASE_A, AD7758_REACT_PWR),
.scan_index = 4,
.scan_type = {
.type = IIO_VOLTAGE,
.indexed = 1,
.channel = 1,
- .extend_name = "raw",
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
.address = AD7758_WT(AD7758_PHASE_B, AD7758_VOLTAGE),
.scan_index = 5,
.scan_type = {
.type = IIO_CURRENT,
.indexed = 1,
.channel = 1,
- .extend_name = "raw",
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
.address = AD7758_WT(AD7758_PHASE_B, AD7758_CURRENT),
.scan_index = 6,
.scan_type = {
.type = IIO_POWER,
.indexed = 1,
.channel = 1,
- .extend_name = "apparent_raw",
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .extend_name = "apparent",
.address = AD7758_WT(AD7758_PHASE_B, AD7758_APP_PWR),
.scan_index = 7,
.scan_type = {
.type = IIO_POWER,
.indexed = 1,
.channel = 1,
- .extend_name = "active_raw",
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .extend_name = "active",
.address = AD7758_WT(AD7758_PHASE_B, AD7758_ACT_PWR),
.scan_index = 8,
.scan_type = {
.type = IIO_POWER,
.indexed = 1,
.channel = 1,
- .extend_name = "reactive_raw",
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .extend_name = "reactive",
.address = AD7758_WT(AD7758_PHASE_B, AD7758_REACT_PWR),
.scan_index = 9,
.scan_type = {
.type = IIO_VOLTAGE,
.indexed = 1,
.channel = 2,
- .extend_name = "raw",
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
.address = AD7758_WT(AD7758_PHASE_C, AD7758_VOLTAGE),
.scan_index = 10,
.scan_type = {
.type = IIO_CURRENT,
.indexed = 1,
.channel = 2,
- .extend_name = "raw",
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
.address = AD7758_WT(AD7758_PHASE_C, AD7758_CURRENT),
.scan_index = 11,
.scan_type = {
.type = IIO_POWER,
.indexed = 1,
.channel = 2,
- .extend_name = "apparent_raw",
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .extend_name = "apparent",
.address = AD7758_WT(AD7758_PHASE_C, AD7758_APP_PWR),
.scan_index = 12,
.scan_type = {
.type = IIO_POWER,
.indexed = 1,
.channel = 2,
- .extend_name = "active_raw",
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .extend_name = "active",
.address = AD7758_WT(AD7758_PHASE_C, AD7758_ACT_PWR),
.scan_index = 13,
.scan_type = {
.type = IIO_POWER,
.indexed = 1,
.channel = 2,
- .extend_name = "reactive_raw",
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .extend_name = "reactive",
.address = AD7758_WT(AD7758_PHASE_C, AD7758_REACT_PWR),
.scan_index = 14,
.scan_type = {
goto error_free_rx;
}
st->us = spi;
- st->ade7758_ring_channels = &ade7758_channels[0];
mutex_init(&st->buf_lock);
indio_dev->name = spi->dev.driver->name;
indio_dev->dev.parent = &spi->dev;
indio_dev->info = &ade7758_info;
indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ade7758_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ade7758_channels);
ret = ade7758_configure_ring(indio_dev);
if (ret)
**/
static int ade7758_ring_preenable(struct iio_dev *indio_dev)
{
- struct ade7758_state *st = iio_priv(indio_dev);
unsigned channel;
- if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength))
+ if (bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength))
return -EINVAL;
channel = find_first_bit(indio_dev->active_scan_mask,
indio_dev->masklength);
ade7758_write_waveform_type(&indio_dev->dev,
- st->ade7758_ring_channels[channel].address);
+ indio_dev->channels[channel].address);
return 0;
}
if (check_fwstate(pmlmepriv, _FW_LINKED) == true)
rtw_lps_ctrl_wk_cmd(padapter, LPS_CTRL_SCAN, 1);
- ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
if (ph2c == NULL)
return _FAIL;
- psurveyPara = kzalloc(sizeof(struct sitesurvey_parm), GFP_KERNEL);
+ psurveyPara = kzalloc(sizeof(struct sitesurvey_parm), GFP_ATOMIC);
if (psurveyPara == NULL) {
kfree(ph2c);
return _FAIL;
else
RT_TRACE(_module_rtl871x_cmd_c_, _drv_notice_, ("+Join cmd: SSid =[%s]\n", pmlmepriv->assoc_ssid.Ssid));
- pcmd = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ pcmd = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
if (pcmd == NULL) {
res = _FAIL;
RT_TRACE(_module_rtl871x_cmd_c_, _drv_err_, ("rtw_joinbss_cmd: memory allocate for cmd_obj fail!!!\n"));
u8 res = _SUCCESS;
- ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
if (ph2c == NULL) {
res = _FAIL;
goto exit;
}
- pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_KERNEL);
+ pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_ATOMIC);
if (pdrvextra_cmd_parm == NULL) {
kfree(ph2c);
res = _FAIL;
u8 res = _SUCCESS;
if (enqueue) {
- ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
if (ph2c == NULL) {
res = _FAIL;
goto exit;
}
- pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_KERNEL);
+ pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_ATOMIC);
if (pdrvextra_cmd_parm == NULL) {
kfree(ph2c);
res = _FAIL;
u8 res = _SUCCESS;
- ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
if (ph2c == NULL) {
res = _FAIL;
goto exit;
}
- pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_KERNEL);
+ pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_ATOMIC);
if (pdrvextra_cmd_parm == NULL) {
kfree(ph2c);
res = _FAIL;
u8 res = _SUCCESS;
- ppscmd = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ ppscmd = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
if (ppscmd == NULL) {
res = _FAIL;
goto exit;
}
- pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_KERNEL);
+ pdrvextra_cmd_parm = kzalloc(sizeof(struct drvextra_cmd_parm), GFP_ATOMIC);
if (pdrvextra_cmd_parm == NULL) {
kfree(ppscmd);
res = _FAIL;
pcmdpriv = &padapter->cmdpriv;
- pcmd_obj = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ pcmd_obj = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
if (pcmd_obj == NULL)
return;
cmdsz = (sizeof(struct survey_event) + sizeof(struct C2HEvent_Header));
- pevtcmd = kzalloc(cmdsz, GFP_KERNEL);
+ pevtcmd = kzalloc(cmdsz, GFP_ATOMIC);
if (pevtcmd == NULL) {
kfree(pcmd_obj);
return;
struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
struct cmd_priv *pcmdpriv = &padapter->cmdpriv;
- pcmd_obj = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ pcmd_obj = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
if (pcmd_obj == NULL)
return;
cmdsz = (sizeof(struct joinbss_event) + sizeof(struct C2HEvent_Header));
- pevtcmd = kzalloc(cmdsz, GFP_KERNEL);
+ pevtcmd = kzalloc(cmdsz, GFP_ATOMIC);
if (pevtcmd == NULL) {
kfree(pcmd_obj);
return;
pmlmeext->scan_abort = false;/* reset */
}
- ph2c = kzalloc(sizeof(struct cmd_obj), GFP_KERNEL);
+ ph2c = kzalloc(sizeof(struct cmd_obj), GFP_ATOMIC);
if (ph2c == NULL)
goto exit_survey_timer_hdl;
- psurveyPara = kzalloc(sizeof(struct sitesurvey_parm), GFP_KERNEL);
+ psurveyPara = kzalloc(sizeof(struct sitesurvey_parm), GFP_ATOMIC);
if (psurveyPara == NULL) {
kfree(ph2c);
goto exit_survey_timer_hdl;
return true;
}
- bssid = kzalloc(sizeof(struct wlan_bssid_ex), GFP_KERNEL);
+ bssid = kzalloc(sizeof(struct wlan_bssid_ex), GFP_ATOMIC);
subtype = GetFrameSubType(pframe) >> 4;
{USB_DEVICE(0x07b8, 0x8179)}, /* Abocom - Abocom */
{USB_DEVICE(0x2001, 0x330F)}, /* DLink DWA-125 REV D1 */
{USB_DEVICE(0x2001, 0x3310)}, /* Dlink DWA-123 REV D1 */
+ {USB_DEVICE(0x2001, 0x3311)}, /* DLink GO-USB-N150 REV B1 */
{USB_DEVICE(0x0df6, 0x0076)}, /* Sitecom N150 v2 */
{} /* Terminating entry */
};
len = sprintf(buf, "TargetAddress="
"%s:%hu,%hu",
inaddr_any ? conn->local_ip : np->np_ip,
- inaddr_any ? conn->local_port : np->np_port,
+ np->np_port,
tpg->tpgt);
len += 1;
*/
struct device *tcm_loop_primary;
-/*
- * Copied from drivers/scsi/libfc/fc_fcp.c:fc_change_queue_depth() and
- * drivers/scsi/libiscsi.c:iscsi_change_queue_depth()
- */
-static int tcm_loop_change_queue_depth(
- struct scsi_device *sdev,
- int depth,
- int reason)
-{
- switch (reason) {
- case SCSI_QDEPTH_DEFAULT:
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
- break;
- case SCSI_QDEPTH_QFULL:
- scsi_track_queue_full(sdev, depth);
- break;
- case SCSI_QDEPTH_RAMP_UP:
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
- break;
- default:
- return -EOPNOTSUPP;
- }
- return sdev->queue_depth;
-}
-
-static int tcm_loop_change_queue_type(struct scsi_device *sdev, int tag)
-{
- if (sdev->tagged_supported) {
- scsi_set_tag_type(sdev, tag);
-
- if (tag)
- scsi_activate_tcq(sdev, sdev->queue_depth);
- else
- scsi_deactivate_tcq(sdev, sdev->queue_depth);
- } else
- tag = 0;
-
- return tag;
-}
-
-/*
- * Locate the SAM Task Attr from struct scsi_cmnd *
- */
-static int tcm_loop_sam_attr(struct scsi_cmnd *sc, int tag)
-{
- if (sc->device->tagged_supported &&
- sc->device->ordered_tags && tag >= 0)
- return MSG_ORDERED_TAG;
-
- return MSG_SIMPLE_TAG;
-}
-
static void tcm_loop_submission_work(struct work_struct *work)
{
struct tcm_loop_cmd *tl_cmd =
rc = target_submit_cmd_map_sgls(se_cmd, tl_nexus->se_sess, sc->cmnd,
&tl_cmd->tl_sense_buf[0], tl_cmd->sc->device->lun,
- transfer_length, tcm_loop_sam_attr(sc, tl_cmd->sc_cmd_tag),
+ transfer_length, MSG_SIMPLE_TAG,
sc->sc_data_direction, 0,
scsi_sglist(sc), scsi_sg_count(sc),
sgl_bidi, sgl_bidi_count,
return 0;
}
-static int tcm_loop_slave_configure(struct scsi_device *sd)
-{
- if (sd->tagged_supported) {
- scsi_activate_tcq(sd, sd->queue_depth);
- scsi_adjust_queue_depth(sd, MSG_SIMPLE_TAG,
- sd->host->cmd_per_lun);
- } else {
- scsi_adjust_queue_depth(sd, 0,
- sd->host->cmd_per_lun);
- }
-
- return 0;
-}
-
static struct scsi_host_template tcm_loop_driver_template = {
.show_info = tcm_loop_show_info,
.proc_name = "tcm_loopback",
.name = "TCM_Loopback",
.queuecommand = tcm_loop_queuecommand,
- .change_queue_depth = tcm_loop_change_queue_depth,
- .change_queue_type = tcm_loop_change_queue_type,
+ .change_queue_depth = scsi_change_queue_depth,
+ .change_queue_type = scsi_change_queue_type,
.eh_abort_handler = tcm_loop_abort_task,
.eh_device_reset_handler = tcm_loop_device_reset,
.eh_target_reset_handler = tcm_loop_target_reset,
.max_sectors = 0xFFFF,
.use_clustering = DISABLE_CLUSTERING,
.slave_alloc = tcm_loop_slave_alloc,
- .slave_configure = tcm_loop_slave_configure,
.module = THIS_MODULE,
+ .use_blk_tags = 1,
+ .track_queue_depth = 1,
};
static int tcm_loop_driver_probe(struct device *dev)
case SEND_DIAGNOSTIC:
case READ_CAPACITY:
return 0;
- case SERVICE_ACTION_IN:
+ case SERVICE_ACTION_IN_16:
switch (cdb[1] & 0x1f) {
case SAI_READ_CAPACITY_16:
return 0;
case ACCESS_CONTROL_OUT:
case INQUIRY:
case LOG_SENSE:
- case READ_MEDIA_SERIAL_NUMBER:
+ case SERVICE_ACTION_IN_12:
case REPORT_LUNS:
case REQUEST_SENSE:
case PERSISTENT_RESERVE_IN:
struct t10_pr_registration *pr_reg, *pr_reg_tmp, *pr_reg_n, *pr_res_holder;
struct t10_reservation *pr_tmpl = &dev->t10_pr;
u32 pr_res_mapped_lun = 0;
- int all_reg = 0, calling_it_nexus = 0, released_regs = 0;
+ int all_reg = 0, calling_it_nexus = 0;
+ bool sa_res_key_unmatched = sa_res_key != 0;
int prh_type = 0, prh_scope = 0;
if (!se_sess)
if (!all_reg) {
if (pr_reg->pr_res_key != sa_res_key)
continue;
+ sa_res_key_unmatched = false;
calling_it_nexus = (pr_reg_n == pr_reg) ? 1 : 0;
pr_reg_nacl = pr_reg->pr_reg_nacl;
__core_scsi3_free_registration(dev, pr_reg,
(preempt_type == PREEMPT_AND_ABORT) ? &preempt_and_abort_list :
NULL, calling_it_nexus);
- released_regs++;
} else {
/*
* Case for any existing all registrants type
if ((sa_res_key) &&
(pr_reg->pr_res_key != sa_res_key))
continue;
+ sa_res_key_unmatched = false;
calling_it_nexus = (pr_reg_n == pr_reg) ? 1 : 0;
if (calling_it_nexus)
__core_scsi3_free_registration(dev, pr_reg,
(preempt_type == PREEMPT_AND_ABORT) ? &preempt_and_abort_list :
NULL, 0);
- released_regs++;
}
if (!calling_it_nexus)
core_scsi3_ua_allocate(pr_reg_nacl,
* registered reservation key, then the device server shall
* complete the command with RESERVATION CONFLICT status.
*/
- if (!released_regs) {
+ if (sa_res_key_unmatched) {
spin_unlock(&dev->dev_reservation_lock);
core_scsi3_put_pr_reg(pr_reg_n);
return TCM_RESERVATION_CONFLICT;
size = READ_CAP_LEN;
cmd->execute_cmd = sbc_emulate_readcapacity;
break;
- case SERVICE_ACTION_IN:
+ case SERVICE_ACTION_IN_16:
switch (cmd->t_task_cdb[1] & 0x1f) {
case SAI_READ_CAPACITY_16:
cmd->execute_cmd = sbc_emulate_readcapacity_16;
* and let it call back once the write buffers are ready.
*/
target_add_to_state_list(cmd);
- if (cmd->data_direction != DMA_TO_DEVICE) {
+ if (cmd->data_direction != DMA_TO_DEVICE || cmd->data_length == 0) {
target_execute_cmd(cmd);
return 0;
}
unsigned int cpufreq_state;
unsigned int cpufreq_val;
struct cpumask allowed_cpus;
+ struct list_head node;
};
static DEFINE_IDR(cpufreq_idr);
static DEFINE_MUTEX(cooling_cpufreq_lock);
static unsigned int cpufreq_dev_count;
-/* notify_table passes value to the CPUFREQ_ADJUST callback function. */
-#define NOTIFY_INVALID NULL
-static struct cpufreq_cooling_device *notify_device;
+static LIST_HEAD(cpufreq_dev_list);
/**
* get_idr - function to get a unique id.
cpufreq_device->cpufreq_state = cooling_state;
cpufreq_device->cpufreq_val = clip_freq;
- notify_device = cpufreq_device;
for_each_cpu(cpuid, mask) {
if (is_cpufreq_valid(cpuid))
cpufreq_update_policy(cpuid);
}
- notify_device = NOTIFY_INVALID;
-
return 0;
}
{
struct cpufreq_policy *policy = data;
unsigned long max_freq = 0;
+ struct cpufreq_cooling_device *cpufreq_dev;
- if (event != CPUFREQ_ADJUST || notify_device == NOTIFY_INVALID)
+ if (event != CPUFREQ_ADJUST)
return 0;
- if (cpumask_test_cpu(policy->cpu, ¬ify_device->allowed_cpus))
- max_freq = notify_device->cpufreq_val;
- else
- return 0;
+ mutex_lock(&cooling_cpufreq_lock);
+ list_for_each_entry(cpufreq_dev, &cpufreq_dev_list, node) {
+ if (!cpumask_test_cpu(policy->cpu,
+ &cpufreq_dev->allowed_cpus))
+ continue;
+
+ if (!cpufreq_dev->cpufreq_val)
+ cpufreq_dev->cpufreq_val = get_cpu_frequency(
+ cpumask_any(&cpufreq_dev->allowed_cpus),
+ cpufreq_dev->cpufreq_state);
- /* Never exceed user_policy.max */
- if (max_freq > policy->user_policy.max)
- max_freq = policy->user_policy.max;
+ max_freq = cpufreq_dev->cpufreq_val;
- if (policy->max != max_freq)
- cpufreq_verify_within_limits(policy, 0, max_freq);
+ if (policy->max != max_freq)
+ cpufreq_verify_within_limits(policy, 0, max_freq);
+ }
+ mutex_unlock(&cooling_cpufreq_lock);
return 0;
}
cpufreq_register_notifier(&thermal_cpufreq_notifier_block,
CPUFREQ_POLICY_NOTIFIER);
cpufreq_dev_count++;
+ list_add(&cpufreq_dev->node, &cpufreq_dev_list);
mutex_unlock(&cooling_cpufreq_lock);
cpufreq_dev = cdev->devdata;
mutex_lock(&cooling_cpufreq_lock);
+ list_del(&cpufreq_dev->node);
cpufreq_dev_count--;
/* Unregister the notifier for the last cpufreq cooling device */
int measure_freq;
int ret;
+ if (!cpufreq_get_current_driver()) {
+ dev_dbg(&pdev->dev, "no cpufreq driver!");
+ return -EPROBE_DEFER;
+ }
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
return ret;
}
+ data->thermal_clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(data->thermal_clk)) {
+ ret = PTR_ERR(data->thermal_clk);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev,
+ "failed to get thermal clk: %d\n", ret);
+ cpufreq_cooling_unregister(data->cdev);
+ return ret;
+ }
+
+ /*
+ * Thermal sensor needs clk on to get correct value, normally
+ * we should enable its clk before taking measurement and disable
+ * clk after measurement is done, but if alarm function is enabled,
+ * hardware will auto measure the temperature periodically, so we
+ * need to keep the clk always on for alarm function.
+ */
+ ret = clk_prepare_enable(data->thermal_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable thermal clk: %d\n", ret);
+ cpufreq_cooling_unregister(data->cdev);
+ return ret;
+ }
+
data->tz = thermal_zone_device_register("imx_thermal_zone",
IMX_TRIP_NUM,
BIT(IMX_TRIP_PASSIVE), data,
ret = PTR_ERR(data->tz);
dev_err(&pdev->dev,
"failed to register thermal zone device %d\n", ret);
+ clk_disable_unprepare(data->thermal_clk);
cpufreq_cooling_unregister(data->cdev);
return ret;
}
- data->thermal_clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(data->thermal_clk)) {
- dev_warn(&pdev->dev, "failed to get thermal clk!\n");
- } else {
- /*
- * Thermal sensor needs clk on to get correct value, normally
- * we should enable its clk before taking measurement and disable
- * clk after measurement is done, but if alarm function is enabled,
- * hardware will auto measure the temperature periodically, so we
- * need to keep the clk always on for alarm function.
- */
- ret = clk_prepare_enable(data->thermal_clk);
- if (ret)
- dev_warn(&pdev->dev, "failed to enable thermal clk: %d\n", ret);
- }
-
/* Enable measurements at ~ 10 Hz */
regmap_write(map, TEMPSENSE1 + REG_CLR, TEMPSENSE1_MEASURE_FREQ);
measure_freq = DIV_ROUND_UP(32768, 10); /* 10 Hz */
if (ACPI_FAILURE(status))
return -EIO;
- *temp = DECI_KELVIN_TO_MILLI_CELSIUS(hyst, KELVIN_OFFSET);
+ /*
+ * Thermal hysteresis represents a temperature difference.
+ * Kelvin and Celsius have same degree size. So the
+ * conversion here between tenths of degree Kelvin unit
+ * and Milli-Celsius unit is just to multiply 100.
+ */
+ *temp = hyst * 100;
return 0;
}
int (*get_trend)(void *, long *))
{
struct device_node *np, *child, *sensor_np;
+ struct thermal_zone_device *tzd = ERR_PTR(-ENODEV);
np = of_find_node_by_name(NULL, "thermal-zones");
if (!np)
return ERR_PTR(-ENODEV);
- if (!dev || !dev->of_node)
+ if (!dev || !dev->of_node) {
+ of_node_put(np);
return ERR_PTR(-EINVAL);
+ }
- sensor_np = dev->of_node;
+ sensor_np = of_node_get(dev->of_node);
for_each_child_of_node(np, child) {
struct of_phandle_args sensor_specs;
}
if (sensor_specs.np == sensor_np && id == sensor_id) {
- of_node_put(np);
- return thermal_zone_of_add_sensor(child, sensor_np,
- data,
- get_temp,
- get_trend);
+ tzd = thermal_zone_of_add_sensor(child, sensor_np,
+ data,
+ get_temp,
+ get_trend);
+ of_node_put(sensor_specs.np);
+ of_node_put(child);
+ goto exit;
}
+ of_node_put(sensor_specs.np);
}
+exit:
+ of_node_put(sensor_np);
of_node_put(np);
- return ERR_PTR(-ENODEV);
+ return tzd;
}
EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_register);
/* Required for cooling map matching */
trip->np = np;
+ of_node_get(np);
return 0;
}
return tz;
free_tbps:
+ for (i = 0; i < tz->num_tbps; i++)
+ of_node_put(tz->tbps[i].cooling_device);
kfree(tz->tbps);
free_trips:
+ for (i = 0; i < tz->ntrips; i++)
+ of_node_put(tz->trips[i].np);
kfree(tz->trips);
+ of_node_put(gchild);
free_tz:
kfree(tz);
of_node_put(child);
static inline void of_thermal_free_zone(struct __thermal_zone *tz)
{
+ int i;
+
+ for (i = 0; i < tz->num_tbps; i++)
+ of_node_put(tz->tbps[i].cooling_device);
kfree(tz->tbps);
+ for (i = 0; i < tz->ntrips; i++)
+ of_node_put(tz->trips[i].np);
kfree(tz->trips);
kfree(tz);
}
/* attempting to build remaining zones still */
}
}
+ of_node_put(np);
return 0;
exit_free:
+ of_node_put(child);
+ of_node_put(np);
of_thermal_free_zone(tz);
/* no memory available, so free what we have built */
kfree(zone->ops);
of_thermal_free_zone(zone->devdata);
}
+ of_node_put(np);
}
th_zone = sensor_conf->pzone_data;
- if (th_zone->therm_dev)
- thermal_zone_device_unregister(th_zone->therm_dev);
+ thermal_zone_device_unregister(th_zone->therm_dev);
- for (i = 0; i < th_zone->cool_dev_size; i++) {
- if (th_zone->cool_dev[i])
- cpufreq_cooling_unregister(th_zone->cool_dev[i]);
- }
+ for (i = 0; i < th_zone->cool_dev_size; ++i)
+ cpufreq_cooling_unregister(th_zone->cool_dev[i]);
dev_info(sensor_conf->dev,
"Exynos: Kernel Thermal management unregistered\n");
#define SENSOR_NAME_LEN 16
#define MAX_TRIP_COUNT 8
#define MAX_COOLING_DEVICE 4
-#define MAX_THRESHOLD_LEVS 5
+#define MAX_TRIMINFO_CTRL_REG 2
#define ACTIVE_INTERVAL 500
#define IDLE_INTERVAL 10000
struct exynos_tmu_platform_data *pdata = data->pdata;
int temp_code;
- if (pdata->cal_mode == HW_MODE)
- return temp;
-
- if (data->soc == SOC_ARCH_EXYNOS4210)
- /* temp should range between 25 and 125 */
- if (temp < 25 || temp > 125) {
- temp_code = -EINVAL;
- goto out;
- }
-
switch (pdata->cal_type) {
case TYPE_TWO_POINT_TRIMMING:
temp_code = (temp - pdata->first_point_trim) *
temp_code = temp + pdata->default_temp_offset;
break;
}
-out:
+
return temp_code;
}
struct exynos_tmu_platform_data *pdata = data->pdata;
int temp;
- if (pdata->cal_mode == HW_MODE)
- return temp_code;
-
- if (data->soc == SOC_ARCH_EXYNOS4210)
- /* temp_code should range between 75 and 175 */
- if (temp_code < 75 || temp_code > 175) {
- temp = -ENODATA;
- goto out;
- }
-
switch (pdata->cal_type) {
case TYPE_TWO_POINT_TRIMMING:
temp = (temp_code - data->temp_error1) *
temp = temp_code - pdata->default_temp_offset;
break;
}
-out:
+
return temp;
}
+static void exynos_tmu_clear_irqs(struct exynos_tmu_data *data)
+{
+ const struct exynos_tmu_registers *reg = data->pdata->registers;
+ unsigned int val_irq;
+
+ val_irq = readl(data->base + reg->tmu_intstat);
+ /*
+ * Clear the interrupts. Please note that the documentation for
+ * Exynos3250, Exynos4412, Exynos5250 and Exynos5260 incorrectly
+ * states that INTCLEAR register has a different placing of bits
+ * responsible for FALL IRQs than INTSTAT register. Exynos5420
+ * and Exynos5440 documentation is correct (Exynos4210 doesn't
+ * support FALL IRQs at all).
+ */
+ writel(val_irq, data->base + reg->tmu_intclear);
+}
+
static int exynos_tmu_initialize(struct platform_device *pdev)
{
struct exynos_tmu_data *data = platform_get_drvdata(pdev);
struct exynos_tmu_platform_data *pdata = data->pdata;
const struct exynos_tmu_registers *reg = pdata->registers;
- unsigned int status, trim_info = 0, con;
+ unsigned int status, trim_info = 0, con, ctrl;
unsigned int rising_threshold = 0, falling_threshold = 0;
- int ret = 0, threshold_code, i, trigger_levs = 0;
+ int ret = 0, threshold_code, i;
mutex_lock(&data->lock);
clk_enable(data->clk);
}
}
- if (TMU_SUPPORTS(pdata, TRIM_RELOAD))
- __raw_writel(1, data->base + reg->triminfo_ctrl);
-
- if (pdata->cal_mode == HW_MODE)
- goto skip_calib_data;
+ if (TMU_SUPPORTS(pdata, TRIM_RELOAD)) {
+ for (i = 0; i < reg->triminfo_ctrl_count; i++) {
+ if (pdata->triminfo_reload[i]) {
+ ctrl = readl(data->base +
+ reg->triminfo_ctrl[i]);
+ ctrl |= pdata->triminfo_reload[i];
+ writel(ctrl, data->base +
+ reg->triminfo_ctrl[i]);
+ }
+ }
+ }
/* Save trimming info in order to perform calibration */
if (data->soc == SOC_ARCH_EXYNOS5440) {
trim_info = readl(data->base + reg->triminfo_data);
}
data->temp_error1 = trim_info & EXYNOS_TMU_TEMP_MASK;
- data->temp_error2 = ((trim_info >> reg->triminfo_85_shift) &
+ data->temp_error2 = ((trim_info >> EXYNOS_TRIMINFO_85_SHIFT) &
EXYNOS_TMU_TEMP_MASK);
if (!data->temp_error1 ||
if (!data->temp_error2)
data->temp_error2 =
- (pdata->efuse_value >> reg->triminfo_85_shift) &
+ (pdata->efuse_value >> EXYNOS_TRIMINFO_85_SHIFT) &
EXYNOS_TMU_TEMP_MASK;
-skip_calib_data:
- if (pdata->max_trigger_level > MAX_THRESHOLD_LEVS) {
- dev_err(&pdev->dev, "Invalid max trigger level\n");
- ret = -EINVAL;
- goto out;
- }
-
- for (i = 0; i < pdata->max_trigger_level; i++) {
- if (!pdata->trigger_levels[i])
- continue;
-
- if ((pdata->trigger_type[i] == HW_TRIP) &&
- (!pdata->trigger_levels[pdata->max_trigger_level - 1])) {
- dev_err(&pdev->dev, "Invalid hw trigger level\n");
- ret = -EINVAL;
- goto out;
- }
-
- /* Count trigger levels except the HW trip*/
- if (!(pdata->trigger_type[i] == HW_TRIP))
- trigger_levs++;
- }
-
rising_threshold = readl(data->base + reg->threshold_th0);
if (data->soc == SOC_ARCH_EXYNOS4210) {
/* Write temperature code for threshold */
threshold_code = temp_to_code(data, pdata->threshold);
- if (threshold_code < 0) {
- ret = threshold_code;
- goto out;
- }
writeb(threshold_code,
data->base + reg->threshold_temp);
- for (i = 0; i < trigger_levs; i++)
+ for (i = 0; i < pdata->non_hw_trigger_levels; i++)
writeb(pdata->trigger_levels[i], data->base +
reg->threshold_th0 + i * sizeof(reg->threshold_th0));
- writel(reg->intclr_rise_mask, data->base + reg->tmu_intclear);
+ exynos_tmu_clear_irqs(data);
} else {
/* Write temperature code for rising and falling threshold */
- for (i = 0;
- i < trigger_levs && i < EXYNOS_MAX_TRIGGER_PER_REG; i++) {
+ for (i = 0; i < pdata->non_hw_trigger_levels; i++) {
threshold_code = temp_to_code(data,
pdata->trigger_levels[i]);
- if (threshold_code < 0) {
- ret = threshold_code;
- goto out;
- }
rising_threshold &= ~(0xff << 8 * i);
rising_threshold |= threshold_code << 8 * i;
if (pdata->threshold_falling) {
threshold_code = temp_to_code(data,
pdata->trigger_levels[i] -
pdata->threshold_falling);
- if (threshold_code > 0)
- falling_threshold |=
- threshold_code << 8 * i;
+ falling_threshold |= threshold_code << 8 * i;
}
}
writel(falling_threshold,
data->base + reg->threshold_th1);
- writel((reg->intclr_rise_mask << reg->intclr_rise_shift) |
- (reg->intclr_fall_mask << reg->intclr_fall_shift),
- data->base + reg->tmu_intclear);
+ exynos_tmu_clear_irqs(data);
/* if last threshold limit is also present */
i = pdata->max_trigger_level - 1;
(pdata->trigger_type[i] == HW_TRIP)) {
threshold_code = temp_to_code(data,
pdata->trigger_levels[i]);
- if (threshold_code < 0) {
- ret = threshold_code;
- goto out;
- }
if (i == EXYNOS_MAX_TRIGGER_PER_REG - 1) {
/* 1-4 level to be assigned in th0 reg */
rising_threshold &= ~(0xff << 8 * i);
struct exynos_tmu_data *data = platform_get_drvdata(pdev);
struct exynos_tmu_platform_data *pdata = data->pdata;
const struct exynos_tmu_registers *reg = pdata->registers;
- unsigned int con, interrupt_en, cal_val;
+ unsigned int con, interrupt_en;
mutex_lock(&data->lock);
clk_enable(data->clk);
if (pdata->test_mux)
con |= (pdata->test_mux << reg->test_mux_addr_shift);
- if (pdata->reference_voltage) {
- con &= ~(reg->buf_vref_sel_mask << reg->buf_vref_sel_shift);
- con |= pdata->reference_voltage << reg->buf_vref_sel_shift;
- }
+ con &= ~(EXYNOS_TMU_REF_VOLTAGE_MASK << EXYNOS_TMU_REF_VOLTAGE_SHIFT);
+ con |= pdata->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT;
- if (pdata->gain) {
- con &= ~(reg->buf_slope_sel_mask << reg->buf_slope_sel_shift);
- con |= (pdata->gain << reg->buf_slope_sel_shift);
- }
+ con &= ~(EXYNOS_TMU_BUF_SLOPE_SEL_MASK << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT);
+ con |= (pdata->gain << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT);
if (pdata->noise_cancel_mode) {
con &= ~(reg->therm_trip_mode_mask <<
con |= (pdata->noise_cancel_mode << reg->therm_trip_mode_shift);
}
- if (pdata->cal_mode == HW_MODE) {
- con &= ~(reg->calib_mode_mask << reg->calib_mode_shift);
- cal_val = 0;
- switch (pdata->cal_type) {
- case TYPE_TWO_POINT_TRIMMING:
- cal_val = 3;
- break;
- case TYPE_ONE_POINT_TRIMMING_85:
- cal_val = 2;
- break;
- case TYPE_ONE_POINT_TRIMMING_25:
- cal_val = 1;
- break;
- case TYPE_NONE:
- break;
- default:
- dev_err(&pdev->dev, "Invalid calibration type, using none\n");
- }
- con |= cal_val << reg->calib_mode_shift;
- }
-
if (on) {
- con |= (1 << reg->core_en_shift);
+ con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT);
interrupt_en =
pdata->trigger_enable[3] << reg->inten_rise3_shift |
pdata->trigger_enable[2] << reg->inten_rise2_shift |
interrupt_en |=
interrupt_en << reg->inten_fall0_shift;
} else {
- con &= ~(1 << reg->core_en_shift);
+ con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT);
interrupt_en = 0; /* Disable all interrupts */
}
writel(interrupt_en, data->base + reg->tmu_inten);
clk_enable(data->clk);
temp_code = readb(data->base + reg->tmu_cur_temp);
- temp = code_to_temp(data, temp_code);
+ if (data->soc == SOC_ARCH_EXYNOS4210)
+ /* temp_code should range between 75 and 175 */
+ if (temp_code < 75 || temp_code > 175) {
+ temp = -ENODATA;
+ goto out;
+ }
+
+ temp = code_to_temp(data, temp_code);
+out:
clk_disable(data->clk);
mutex_unlock(&data->lock);
struct exynos_tmu_data, irq_work);
struct exynos_tmu_platform_data *pdata = data->pdata;
const struct exynos_tmu_registers *reg = pdata->registers;
- unsigned int val_irq, val_type;
+ unsigned int val_type;
if (!IS_ERR(data->clk_sec))
clk_enable(data->clk_sec);
clk_enable(data->clk);
/* TODO: take action based on particular interrupt */
- val_irq = readl(data->base + reg->tmu_intstat);
- /* clear the interrupts */
- writel(val_irq, data->base + reg->tmu_intclear);
+ exynos_tmu_clear_irqs(data);
clk_disable(data->clk);
mutex_unlock(&data->lock);
TYPE_NONE,
};
-enum calibration_mode {
- SW_MODE,
- HW_MODE,
-};
-
enum soc_type {
SOC_ARCH_EXYNOS3250 = 1,
SOC_ARCH_EXYNOS4210,
* bitfields. The register validity, offsets and bitfield values may vary
* slightly across different exynos SOC's.
* @triminfo_data: register containing 2 pont trimming data
- * @triminfo_25_shift: shift bit of the 25 C trim value in triminfo_data reg.
- * @triminfo_85_shift: shift bit of the 85 C trim value in triminfo_data reg.
* @triminfo_ctrl: trim info controller register.
- * @triminfo_reload_shift: shift of triminfo reload enable bit in triminfo_ctrl
- reg.
+ * @triminfo_ctrl_count: the number of trim info controller register.
* @tmu_ctrl: TMU main controller register.
* @test_mux_addr_shift: shift bits of test mux address.
- * @buf_vref_sel_shift: shift bits of reference voltage in tmu_ctrl register.
- * @buf_vref_sel_mask: mask bits of reference voltage in tmu_ctrl register.
* @therm_trip_mode_shift: shift bits of tripping mode in tmu_ctrl register.
* @therm_trip_mode_mask: mask bits of tripping mode in tmu_ctrl register.
* @therm_trip_en_shift: shift bits of tripping enable in tmu_ctrl register.
- * @buf_slope_sel_shift: shift bits of amplifier gain value in tmu_ctrl
- register.
- * @buf_slope_sel_mask: mask bits of amplifier gain value in tmu_ctrl register.
- * @calib_mode_shift: shift bits of calibration mode value in tmu_ctrl
- register.
- * @calib_mode_mask: mask bits of calibration mode value in tmu_ctrl
- register.
- * @therm_trip_tq_en_shift: shift bits of thermal trip enable by TQ pin in
- tmu_ctrl register.
- * @core_en_shift: shift bits of TMU core enable bit in tmu_ctrl register.
* @tmu_status: register drescribing the TMU status.
* @tmu_cur_temp: register containing the current temperature of the TMU.
- * @tmu_cur_temp_shift: shift bits of current temp value in tmu_cur_temp
- register.
* @threshold_temp: register containing the base threshold level.
* @threshold_th0: Register containing first set of rising levels.
- * @threshold_th0_l0_shift: shift bits of level0 threshold temperature.
- * @threshold_th0_l1_shift: shift bits of level1 threshold temperature.
- * @threshold_th0_l2_shift: shift bits of level2 threshold temperature.
- * @threshold_th0_l3_shift: shift bits of level3 threshold temperature.
* @threshold_th1: Register containing second set of rising levels.
- * @threshold_th1_l0_shift: shift bits of level0 threshold temperature.
- * @threshold_th1_l1_shift: shift bits of level1 threshold temperature.
- * @threshold_th1_l2_shift: shift bits of level2 threshold temperature.
- * @threshold_th1_l3_shift: shift bits of level3 threshold temperature.
* @threshold_th2: Register containing third set of rising levels.
- * @threshold_th2_l0_shift: shift bits of level0 threshold temperature.
- * @threshold_th3: Register containing fourth set of rising levels.
* @threshold_th3_l0_shift: shift bits of level0 threshold temperature.
* @tmu_inten: register containing the different threshold interrupt
enable bits.
* @inten_rise2_shift: shift bits of rising 2 interrupt bits.
* @inten_rise3_shift: shift bits of rising 3 interrupt bits.
* @inten_fall0_shift: shift bits of falling 0 interrupt bits.
- * @inten_fall1_shift: shift bits of falling 1 interrupt bits.
- * @inten_fall2_shift: shift bits of falling 2 interrupt bits.
- * @inten_fall3_shift: shift bits of falling 3 interrupt bits.
* @tmu_intstat: Register containing the interrupt status values.
* @tmu_intclear: Register for clearing the raised interrupt status.
- * @intclr_fall_shift: shift bits for interrupt clear fall 0
- * @intclr_rise_shift: shift bits of all rising interrupt bits.
- * @intclr_rise_mask: mask bits of all rising interrupt bits.
- * @intclr_fall_mask: mask bits of all rising interrupt bits.
* @emul_con: TMU emulation controller register.
* @emul_temp_shift: shift bits of emulation temperature.
* @emul_time_shift: shift bits of emulation time.
- * @emul_time_mask: mask bits of emulation time.
* @tmu_irqstatus: register to find which TMU generated interrupts.
* @tmu_pmin: register to get/set the Pmin value.
*/
struct exynos_tmu_registers {
u32 triminfo_data;
- u32 triminfo_25_shift;
- u32 triminfo_85_shift;
- u32 triminfo_ctrl;
- u32 triminfo_ctrl1;
- u32 triminfo_reload_shift;
+ u32 triminfo_ctrl[MAX_TRIMINFO_CTRL_REG];
+ u32 triminfo_ctrl_count;
u32 tmu_ctrl;
u32 test_mux_addr_shift;
- u32 buf_vref_sel_shift;
- u32 buf_vref_sel_mask;
u32 therm_trip_mode_shift;
u32 therm_trip_mode_mask;
u32 therm_trip_en_shift;
- u32 buf_slope_sel_shift;
- u32 buf_slope_sel_mask;
- u32 calib_mode_shift;
- u32 calib_mode_mask;
- u32 therm_trip_tq_en_shift;
- u32 core_en_shift;
u32 tmu_status;
u32 tmu_cur_temp;
- u32 tmu_cur_temp_shift;
u32 threshold_temp;
u32 threshold_th0;
- u32 threshold_th0_l0_shift;
- u32 threshold_th0_l1_shift;
- u32 threshold_th0_l2_shift;
- u32 threshold_th0_l3_shift;
-
u32 threshold_th1;
- u32 threshold_th1_l0_shift;
- u32 threshold_th1_l1_shift;
- u32 threshold_th1_l2_shift;
- u32 threshold_th1_l3_shift;
-
u32 threshold_th2;
- u32 threshold_th2_l0_shift;
-
- u32 threshold_th3;
u32 threshold_th3_l0_shift;
u32 tmu_inten;
u32 inten_rise2_shift;
u32 inten_rise3_shift;
u32 inten_fall0_shift;
- u32 inten_fall1_shift;
- u32 inten_fall2_shift;
- u32 inten_fall3_shift;
u32 tmu_intstat;
u32 tmu_intclear;
- u32 intclr_fall_shift;
- u32 intclr_rise_shift;
- u32 intclr_fall_mask;
- u32 intclr_rise_mask;
u32 emul_con;
u32 emul_temp_shift;
u32 emul_time_shift;
- u32 emul_time_mask;
u32 tmu_irqstatus;
u32 tmu_pmin;
* 1 = enable trigger_level[] interrupt,
* 0 = disable trigger_level[] interrupt
* @max_trigger_level: max trigger level supported by the TMU
+ * @non_hw_trigger_levels: number of defined non-hardware trigger levels
* @gain: gain of amplifier in the positive-TC generator block
- * 0 <= gain <= 15
+ * 0 < gain <= 15
* @reference_voltage: reference voltage of amplifier
* in the positive-TC generator block
- * 0 <= reference_voltage <= 31
+ * 0 < reference_voltage <= 31
* @noise_cancel_mode: noise cancellation mode
* 000, 100, 101, 110 and 111 can be different modes
* @type: determines the type of SOC
* @second_point_trim: temp value of the second point trimming
* @default_temp_offset: default temperature offset in case of no trimming
* @test_mux; information if SoC supports test MUX
+ * @triminfo_reload: reload value to read TRIMINFO register
* @cal_type: calibration type for temperature
- * @cal_mode: calibration mode for temperature
* @freq_clip_table: Table representing frequency reduction percentage.
* @freq_tab_count: Count of the above table as frequency reduction may
* applicable to only some of the trigger levels.
enum trigger_type trigger_type[MAX_TRIP_COUNT];
bool trigger_enable[MAX_TRIP_COUNT];
u8 max_trigger_level;
+ u8 non_hw_trigger_levels;
u8 gain;
u8 reference_voltage;
u8 noise_cancel_mode;
u8 second_point_trim;
u8 default_temp_offset;
u8 test_mux;
+ u8 triminfo_reload[MAX_TRIMINFO_CTRL_REG];
enum calibration_type cal_type;
- enum calibration_mode cal_mode;
enum soc_type type;
struct freq_clip_table freq_tab[4];
unsigned int freq_tab_count;
#if defined(CONFIG_CPU_EXYNOS4210)
static const struct exynos_tmu_registers exynos4210_tmu_registers = {
.triminfo_data = EXYNOS_TMU_REG_TRIMINFO,
- .triminfo_25_shift = EXYNOS_TRIMINFO_25_SHIFT,
- .triminfo_85_shift = EXYNOS_TRIMINFO_85_SHIFT,
.tmu_ctrl = EXYNOS_TMU_REG_CONTROL,
- .buf_vref_sel_shift = EXYNOS_TMU_REF_VOLTAGE_SHIFT,
- .buf_vref_sel_mask = EXYNOS_TMU_REF_VOLTAGE_MASK,
- .buf_slope_sel_shift = EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT,
- .buf_slope_sel_mask = EXYNOS_TMU_BUF_SLOPE_SEL_MASK,
- .core_en_shift = EXYNOS_TMU_CORE_EN_SHIFT,
.tmu_status = EXYNOS_TMU_REG_STATUS,
.tmu_cur_temp = EXYNOS_TMU_REG_CURRENT_TEMP,
.threshold_temp = EXYNOS4210_TMU_REG_THRESHOLD_TEMP,
.inten_rise3_shift = EXYNOS_TMU_INTEN_RISE3_SHIFT,
.tmu_intstat = EXYNOS_TMU_REG_INTSTAT,
.tmu_intclear = EXYNOS_TMU_REG_INTCLEAR,
- .intclr_rise_mask = EXYNOS4210_TMU_TRIG_LEVEL_MASK,
};
struct exynos_tmu_init_data const exynos4210_default_tmu_data = {
.trigger_type[1] = THROTTLE_ACTIVE,
.trigger_type[2] = SW_TRIP,
.max_trigger_level = 4,
+ .non_hw_trigger_levels = 3,
.gain = 15,
.reference_voltage = 7,
.cal_type = TYPE_ONE_POINT_TRIMMING,
#if defined(CONFIG_SOC_EXYNOS3250)
static const struct exynos_tmu_registers exynos3250_tmu_registers = {
.triminfo_data = EXYNOS_TMU_REG_TRIMINFO,
- .triminfo_25_shift = EXYNOS_TRIMINFO_25_SHIFT,
- .triminfo_85_shift = EXYNOS_TRIMINFO_85_SHIFT,
+ .triminfo_ctrl[0] = EXYNOS_TMU_TRIMINFO_CON1,
+ .triminfo_ctrl[1] = EXYNOS_TMU_TRIMINFO_CON2,
+ .triminfo_ctrl_count = 2,
.tmu_ctrl = EXYNOS_TMU_REG_CONTROL,
.test_mux_addr_shift = EXYNOS4412_MUX_ADDR_SHIFT,
- .buf_vref_sel_shift = EXYNOS_TMU_REF_VOLTAGE_SHIFT,
- .buf_vref_sel_mask = EXYNOS_TMU_REF_VOLTAGE_MASK,
.therm_trip_mode_shift = EXYNOS_TMU_TRIP_MODE_SHIFT,
.therm_trip_mode_mask = EXYNOS_TMU_TRIP_MODE_MASK,
.therm_trip_en_shift = EXYNOS_TMU_THERM_TRIP_EN_SHIFT,
- .buf_slope_sel_shift = EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT,
- .buf_slope_sel_mask = EXYNOS_TMU_BUF_SLOPE_SEL_MASK,
- .core_en_shift = EXYNOS_TMU_CORE_EN_SHIFT,
.tmu_status = EXYNOS_TMU_REG_STATUS,
.tmu_cur_temp = EXYNOS_TMU_REG_CURRENT_TEMP,
.threshold_th0 = EXYNOS_THD_TEMP_RISE,
.inten_fall0_shift = EXYNOS_TMU_INTEN_FALL0_SHIFT,
.tmu_intstat = EXYNOS_TMU_REG_INTSTAT,
.tmu_intclear = EXYNOS_TMU_REG_INTCLEAR,
- .intclr_fall_shift = EXYNOS_TMU_CLEAR_FALL_INT_SHIFT,
- .intclr_rise_shift = EXYNOS_TMU_RISE_INT_SHIFT,
- .intclr_rise_mask = EXYNOS_TMU_RISE_INT_MASK,
- .intclr_fall_mask = EXYNOS_TMU_FALL_INT_MASK,
.emul_con = EXYNOS_EMUL_CON,
.emul_temp_shift = EXYNOS_EMUL_DATA_SHIFT,
.emul_time_shift = EXYNOS_EMUL_TIME_SHIFT,
- .emul_time_mask = EXYNOS_EMUL_TIME_MASK,
};
#define EXYNOS3250_TMU_DATA \
.trigger_type[2] = SW_TRIP, \
.trigger_type[3] = HW_TRIP, \
.max_trigger_level = 4, \
+ .non_hw_trigger_levels = 3, \
.gain = 8, \
.reference_voltage = 16, \
.noise_cancel_mode = 4, \
.temp_level = 95, \
}, \
.freq_tab_count = 2, \
+ .triminfo_reload[0] = EXYNOS_TRIMINFO_RELOAD_ENABLE, \
+ .triminfo_reload[1] = EXYNOS_TRIMINFO_RELOAD_ENABLE, \
.registers = &exynos3250_tmu_registers, \
- .features = (TMU_SUPPORT_EMULATION | \
+ .features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_TRIM_RELOAD | \
TMU_SUPPORT_FALLING_TRIP | TMU_SUPPORT_READY_STATUS | \
TMU_SUPPORT_EMUL_TIME)
#endif
#if defined(CONFIG_SOC_EXYNOS4412) || defined(CONFIG_SOC_EXYNOS5250)
static const struct exynos_tmu_registers exynos4412_tmu_registers = {
.triminfo_data = EXYNOS_TMU_REG_TRIMINFO,
- .triminfo_25_shift = EXYNOS_TRIMINFO_25_SHIFT,
- .triminfo_85_shift = EXYNOS_TRIMINFO_85_SHIFT,
- .triminfo_ctrl = EXYNOS_TMU_TRIMINFO_CON,
- .triminfo_reload_shift = EXYNOS_TRIMINFO_RELOAD_SHIFT,
+ .triminfo_ctrl[0] = EXYNOS_TMU_TRIMINFO_CON2,
+ .triminfo_ctrl_count = 1,
.tmu_ctrl = EXYNOS_TMU_REG_CONTROL,
.test_mux_addr_shift = EXYNOS4412_MUX_ADDR_SHIFT,
- .buf_vref_sel_shift = EXYNOS_TMU_REF_VOLTAGE_SHIFT,
- .buf_vref_sel_mask = EXYNOS_TMU_REF_VOLTAGE_MASK,
.therm_trip_mode_shift = EXYNOS_TMU_TRIP_MODE_SHIFT,
.therm_trip_mode_mask = EXYNOS_TMU_TRIP_MODE_MASK,
.therm_trip_en_shift = EXYNOS_TMU_THERM_TRIP_EN_SHIFT,
- .buf_slope_sel_shift = EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT,
- .buf_slope_sel_mask = EXYNOS_TMU_BUF_SLOPE_SEL_MASK,
- .core_en_shift = EXYNOS_TMU_CORE_EN_SHIFT,
.tmu_status = EXYNOS_TMU_REG_STATUS,
.tmu_cur_temp = EXYNOS_TMU_REG_CURRENT_TEMP,
.threshold_th0 = EXYNOS_THD_TEMP_RISE,
.inten_fall0_shift = EXYNOS_TMU_INTEN_FALL0_SHIFT,
.tmu_intstat = EXYNOS_TMU_REG_INTSTAT,
.tmu_intclear = EXYNOS_TMU_REG_INTCLEAR,
- .intclr_fall_shift = EXYNOS_TMU_CLEAR_FALL_INT_SHIFT,
- .intclr_rise_shift = EXYNOS_TMU_RISE_INT_SHIFT,
- .intclr_rise_mask = EXYNOS_TMU_RISE_INT_MASK,
- .intclr_fall_mask = EXYNOS_TMU_FALL_INT_MASK,
.emul_con = EXYNOS_EMUL_CON,
.emul_temp_shift = EXYNOS_EMUL_DATA_SHIFT,
.emul_time_shift = EXYNOS_EMUL_TIME_SHIFT,
- .emul_time_mask = EXYNOS_EMUL_TIME_MASK,
};
#define EXYNOS4412_TMU_DATA \
.trigger_type[2] = SW_TRIP, \
.trigger_type[3] = HW_TRIP, \
.max_trigger_level = 4, \
+ .non_hw_trigger_levels = 3, \
.gain = 8, \
.reference_voltage = 16, \
.noise_cancel_mode = 4, \
.temp_level = 95, \
}, \
.freq_tab_count = 2, \
+ .triminfo_reload[0] = EXYNOS_TRIMINFO_RELOAD_ENABLE, \
.registers = &exynos4412_tmu_registers, \
.features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_TRIM_RELOAD | \
TMU_SUPPORT_FALLING_TRIP | TMU_SUPPORT_READY_STATUS | \
#if defined(CONFIG_SOC_EXYNOS5260)
static const struct exynos_tmu_registers exynos5260_tmu_registers = {
.triminfo_data = EXYNOS_TMU_REG_TRIMINFO,
- .triminfo_25_shift = EXYNOS_TRIMINFO_25_SHIFT,
- .triminfo_85_shift = EXYNOS_TRIMINFO_85_SHIFT,
.tmu_ctrl = EXYNOS_TMU_REG_CONTROL,
- .tmu_ctrl = EXYNOS_TMU_REG_CONTROL1,
- .buf_vref_sel_shift = EXYNOS_TMU_REF_VOLTAGE_SHIFT,
- .buf_vref_sel_mask = EXYNOS_TMU_REF_VOLTAGE_MASK,
.therm_trip_mode_shift = EXYNOS_TMU_TRIP_MODE_SHIFT,
.therm_trip_mode_mask = EXYNOS_TMU_TRIP_MODE_MASK,
.therm_trip_en_shift = EXYNOS_TMU_THERM_TRIP_EN_SHIFT,
- .buf_slope_sel_shift = EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT,
- .buf_slope_sel_mask = EXYNOS_TMU_BUF_SLOPE_SEL_MASK,
- .core_en_shift = EXYNOS_TMU_CORE_EN_SHIFT,
.tmu_status = EXYNOS_TMU_REG_STATUS,
.tmu_cur_temp = EXYNOS_TMU_REG_CURRENT_TEMP,
.threshold_th0 = EXYNOS_THD_TEMP_RISE,
.inten_fall0_shift = EXYNOS_TMU_INTEN_FALL0_SHIFT,
.tmu_intstat = EXYNOS5260_TMU_REG_INTSTAT,
.tmu_intclear = EXYNOS5260_TMU_REG_INTCLEAR,
- .intclr_fall_shift = EXYNOS5420_TMU_CLEAR_FALL_INT_SHIFT,
- .intclr_rise_shift = EXYNOS_TMU_RISE_INT_SHIFT,
- .intclr_rise_mask = EXYNOS5260_TMU_RISE_INT_MASK,
- .intclr_fall_mask = EXYNOS5260_TMU_FALL_INT_MASK,
.emul_con = EXYNOS5260_EMUL_CON,
.emul_temp_shift = EXYNOS_EMUL_DATA_SHIFT,
.emul_time_shift = EXYNOS_EMUL_TIME_SHIFT,
- .emul_time_mask = EXYNOS_EMUL_TIME_MASK,
};
#define __EXYNOS5260_TMU_DATA \
.trigger_type[2] = SW_TRIP, \
.trigger_type[3] = HW_TRIP, \
.max_trigger_level = 4, \
+ .non_hw_trigger_levels = 3, \
.gain = 8, \
.reference_voltage = 16, \
.noise_cancel_mode = 4, \
#define EXYNOS5260_TMU_DATA \
__EXYNOS5260_TMU_DATA \
.type = SOC_ARCH_EXYNOS5260, \
- .features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_TRIM_RELOAD | \
- TMU_SUPPORT_FALLING_TRIP | TMU_SUPPORT_READY_STATUS | \
- TMU_SUPPORT_EMUL_TIME)
+ .features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_FALLING_TRIP | \
+ TMU_SUPPORT_READY_STATUS | TMU_SUPPORT_EMUL_TIME)
struct exynos_tmu_init_data const exynos5260_default_tmu_data = {
.tmu_data = {
#if defined(CONFIG_SOC_EXYNOS5420)
static const struct exynos_tmu_registers exynos5420_tmu_registers = {
.triminfo_data = EXYNOS_TMU_REG_TRIMINFO,
- .triminfo_25_shift = EXYNOS_TRIMINFO_25_SHIFT,
- .triminfo_85_shift = EXYNOS_TRIMINFO_85_SHIFT,
.tmu_ctrl = EXYNOS_TMU_REG_CONTROL,
- .buf_vref_sel_shift = EXYNOS_TMU_REF_VOLTAGE_SHIFT,
- .buf_vref_sel_mask = EXYNOS_TMU_REF_VOLTAGE_MASK,
.therm_trip_mode_shift = EXYNOS_TMU_TRIP_MODE_SHIFT,
.therm_trip_mode_mask = EXYNOS_TMU_TRIP_MODE_MASK,
.therm_trip_en_shift = EXYNOS_TMU_THERM_TRIP_EN_SHIFT,
- .buf_slope_sel_shift = EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT,
- .buf_slope_sel_mask = EXYNOS_TMU_BUF_SLOPE_SEL_MASK,
- .core_en_shift = EXYNOS_TMU_CORE_EN_SHIFT,
.tmu_status = EXYNOS_TMU_REG_STATUS,
.tmu_cur_temp = EXYNOS_TMU_REG_CURRENT_TEMP,
.threshold_th0 = EXYNOS_THD_TEMP_RISE,
.inten_fall0_shift = EXYNOS_TMU_INTEN_FALL0_SHIFT,
.tmu_intstat = EXYNOS_TMU_REG_INTSTAT,
.tmu_intclear = EXYNOS_TMU_REG_INTCLEAR,
- .intclr_fall_shift = EXYNOS5420_TMU_CLEAR_FALL_INT_SHIFT,
- .intclr_rise_shift = EXYNOS_TMU_RISE_INT_SHIFT,
- .intclr_rise_mask = EXYNOS_TMU_RISE_INT_MASK,
- .intclr_fall_mask = EXYNOS_TMU_FALL_INT_MASK,
.emul_con = EXYNOS_EMUL_CON,
.emul_temp_shift = EXYNOS_EMUL_DATA_SHIFT,
.emul_time_shift = EXYNOS_EMUL_TIME_SHIFT,
- .emul_time_mask = EXYNOS_EMUL_TIME_MASK,
};
#define __EXYNOS5420_TMU_DATA \
.trigger_type[2] = SW_TRIP, \
.trigger_type[3] = HW_TRIP, \
.max_trigger_level = 4, \
+ .non_hw_trigger_levels = 3, \
.gain = 8, \
.reference_voltage = 16, \
.noise_cancel_mode = 4, \
#define EXYNOS5420_TMU_DATA \
__EXYNOS5420_TMU_DATA \
.type = SOC_ARCH_EXYNOS5250, \
- .features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_TRIM_RELOAD | \
- TMU_SUPPORT_FALLING_TRIP | TMU_SUPPORT_READY_STATUS | \
- TMU_SUPPORT_EMUL_TIME)
+ .features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_FALLING_TRIP | \
+ TMU_SUPPORT_READY_STATUS | TMU_SUPPORT_EMUL_TIME)
#define EXYNOS5420_TMU_DATA_SHARED \
__EXYNOS5420_TMU_DATA \
.type = SOC_ARCH_EXYNOS5420_TRIMINFO, \
- .features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_TRIM_RELOAD | \
- TMU_SUPPORT_FALLING_TRIP | TMU_SUPPORT_READY_STATUS | \
- TMU_SUPPORT_EMUL_TIME | TMU_SUPPORT_ADDRESS_MULTIPLE)
+ .features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_FALLING_TRIP | \
+ TMU_SUPPORT_READY_STATUS | TMU_SUPPORT_EMUL_TIME | \
+ TMU_SUPPORT_ADDRESS_MULTIPLE)
struct exynos_tmu_init_data const exynos5420_default_tmu_data = {
.tmu_data = {
#if defined(CONFIG_SOC_EXYNOS5440)
static const struct exynos_tmu_registers exynos5440_tmu_registers = {
.triminfo_data = EXYNOS5440_TMU_S0_7_TRIM,
- .triminfo_25_shift = EXYNOS_TRIMINFO_25_SHIFT,
- .triminfo_85_shift = EXYNOS_TRIMINFO_85_SHIFT,
.tmu_ctrl = EXYNOS5440_TMU_S0_7_CTRL,
- .buf_vref_sel_shift = EXYNOS_TMU_REF_VOLTAGE_SHIFT,
- .buf_vref_sel_mask = EXYNOS_TMU_REF_VOLTAGE_MASK,
.therm_trip_mode_shift = EXYNOS_TMU_TRIP_MODE_SHIFT,
.therm_trip_mode_mask = EXYNOS_TMU_TRIP_MODE_MASK,
.therm_trip_en_shift = EXYNOS_TMU_THERM_TRIP_EN_SHIFT,
- .buf_slope_sel_shift = EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT,
- .buf_slope_sel_mask = EXYNOS_TMU_BUF_SLOPE_SEL_MASK,
- .calib_mode_shift = EXYNOS_TMU_CALIB_MODE_SHIFT,
- .calib_mode_mask = EXYNOS_TMU_CALIB_MODE_MASK,
- .core_en_shift = EXYNOS_TMU_CORE_EN_SHIFT,
.tmu_status = EXYNOS5440_TMU_S0_7_STATUS,
.tmu_cur_temp = EXYNOS5440_TMU_S0_7_TEMP,
.threshold_th0 = EXYNOS5440_TMU_S0_7_TH0,
.inten_fall0_shift = EXYNOS5440_TMU_INTEN_FALL0_SHIFT,
.tmu_intstat = EXYNOS5440_TMU_S0_7_IRQ,
.tmu_intclear = EXYNOS5440_TMU_S0_7_IRQ,
- .intclr_fall_shift = EXYNOS5440_TMU_CLEAR_FALL_INT_SHIFT,
- .intclr_rise_shift = EXYNOS5440_TMU_RISE_INT_SHIFT,
- .intclr_rise_mask = EXYNOS5440_TMU_RISE_INT_MASK,
- .intclr_fall_mask = EXYNOS5440_TMU_FALL_INT_MASK,
.tmu_irqstatus = EXYNOS5440_TMU_IRQ_STATUS,
.emul_con = EXYNOS5440_TMU_S0_7_DEBUG,
.emul_temp_shift = EXYNOS_EMUL_DATA_SHIFT,
.trigger_type[0] = SW_TRIP, \
.trigger_type[4] = HW_TRIP, \
.max_trigger_level = 5, \
+ .non_hw_trigger_levels = 1, \
.gain = 5, \
.reference_voltage = 16, \
.noise_cancel_mode = 4, \
.cal_type = TYPE_ONE_POINT_TRIMMING, \
- .cal_mode = 0, \
.efuse_value = 0x5b2d, \
.min_efuse_value = 16, \
.max_efuse_value = 76, \
#define EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT 8
#define EXYNOS_TMU_CORE_EN_SHIFT 0
+/* Exynos3250 specific registers */
+#define EXYNOS_TMU_TRIMINFO_CON1 0x10
+
/* Exynos4210 specific registers */
#define EXYNOS4210_TMU_REG_THRESHOLD_TEMP 0x44
#define EXYNOS4210_TMU_REG_TRIG_LEVEL0 0x50
-#define EXYNOS4210_TMU_REG_TRIG_LEVEL1 0x54
-#define EXYNOS4210_TMU_REG_TRIG_LEVEL2 0x58
-#define EXYNOS4210_TMU_REG_TRIG_LEVEL3 0x5C
-#define EXYNOS4210_TMU_REG_PAST_TEMP0 0x60
-#define EXYNOS4210_TMU_REG_PAST_TEMP1 0x64
-#define EXYNOS4210_TMU_REG_PAST_TEMP2 0x68
-#define EXYNOS4210_TMU_REG_PAST_TEMP3 0x6C
-
-#define EXYNOS4210_TMU_TRIG_LEVEL0_MASK 0x1
-#define EXYNOS4210_TMU_TRIG_LEVEL1_MASK 0x10
-#define EXYNOS4210_TMU_TRIG_LEVEL2_MASK 0x100
-#define EXYNOS4210_TMU_TRIG_LEVEL3_MASK 0x1000
-#define EXYNOS4210_TMU_TRIG_LEVEL_MASK 0x1111
-#define EXYNOS4210_TMU_INTCLEAR_VAL 0x1111
-
-/* Exynos5250 and Exynos4412 specific registers */
-#define EXYNOS_TMU_TRIMINFO_CON 0x14
+
+/* Exynos5250, Exynos4412, Exynos3250 specific registers */
+#define EXYNOS_TMU_TRIMINFO_CON2 0x14
#define EXYNOS_THD_TEMP_RISE 0x50
#define EXYNOS_THD_TEMP_FALL 0x54
#define EXYNOS_EMUL_CON 0x80
-#define EXYNOS_TRIMINFO_RELOAD_SHIFT 1
+#define EXYNOS_TRIMINFO_RELOAD_ENABLE 1
#define EXYNOS_TRIMINFO_25_SHIFT 0
#define EXYNOS_TRIMINFO_85_SHIFT 8
-#define EXYNOS_TMU_RISE_INT_MASK 0x111
-#define EXYNOS_TMU_RISE_INT_SHIFT 0
-#define EXYNOS_TMU_FALL_INT_MASK 0x111
-#define EXYNOS_TMU_CLEAR_RISE_INT 0x111
-#define EXYNOS_TMU_CLEAR_FALL_INT (0x111 << 12)
-#define EXYNOS_TMU_CLEAR_FALL_INT_SHIFT 12
-#define EXYNOS5420_TMU_CLEAR_FALL_INT_SHIFT 16
-#define EXYNOS5440_TMU_CLEAR_FALL_INT_SHIFT 4
#define EXYNOS_TMU_TRIP_MODE_SHIFT 13
#define EXYNOS_TMU_TRIP_MODE_MASK 0x7
#define EXYNOS_TMU_THERM_TRIP_EN_SHIFT 12
-#define EXYNOS_TMU_CALIB_MODE_SHIFT 4
-#define EXYNOS_TMU_CALIB_MODE_MASK 0x3
#define EXYNOS_TMU_INTEN_RISE0_SHIFT 0
#define EXYNOS_TMU_INTEN_RISE1_SHIFT 4
#define EXYNOS_TMU_INTEN_RISE2_SHIFT 8
#define EXYNOS_TMU_INTEN_RISE3_SHIFT 12
#define EXYNOS_TMU_INTEN_FALL0_SHIFT 16
-#define EXYNOS_TMU_INTEN_FALL1_SHIFT 20
-#define EXYNOS_TMU_INTEN_FALL2_SHIFT 24
-#define EXYNOS_TMU_INTEN_FALL3_SHIFT 28
#define EXYNOS_EMUL_TIME 0x57F0
#define EXYNOS_EMUL_TIME_MASK 0xffff
#define EXYNOS_MAX_TRIGGER_PER_REG 4
/* Exynos5260 specific */
-#define EXYNOS_TMU_REG_CONTROL1 0x24
#define EXYNOS5260_TMU_REG_INTEN 0xC0
#define EXYNOS5260_TMU_REG_INTSTAT 0xC4
#define EXYNOS5260_TMU_REG_INTCLEAR 0xC8
-#define EXYNOS5260_TMU_CLEAR_RISE_INT 0x1111
-#define EXYNOS5260_TMU_CLEAR_FALL_INT (0x1111 << 16)
-#define EXYNOS5260_TMU_RISE_INT_MASK 0x1111
-#define EXYNOS5260_TMU_FALL_INT_MASK 0x1111
#define EXYNOS5260_EMUL_CON 0x100
/* Exynos4412 specific */
#define EXYNOS5440_TMU_S0_7_TH0 0x110
#define EXYNOS5440_TMU_S0_7_TH1 0x130
#define EXYNOS5440_TMU_S0_7_TH2 0x150
-#define EXYNOS5440_TMU_S0_7_EVTEN 0x1F0
#define EXYNOS5440_TMU_S0_7_IRQEN 0x210
#define EXYNOS5440_TMU_S0_7_IRQ 0x230
/* exynos5440 common registers */
#define EXYNOS5440_TMU_IRQ_STATUS 0x000
#define EXYNOS5440_TMU_PMIN 0x004
-#define EXYNOS5440_TMU_TEMP 0x008
-#define EXYNOS5440_TMU_RISE_INT_MASK 0xf
-#define EXYNOS5440_TMU_RISE_INT_SHIFT 0
-#define EXYNOS5440_TMU_FALL_INT_MASK 0xf
#define EXYNOS5440_TMU_INTEN_RISE0_SHIFT 0
#define EXYNOS5440_TMU_INTEN_RISE1_SHIFT 1
#define EXYNOS5440_TMU_INTEN_RISE2_SHIFT 2
#define EXYNOS5440_TMU_INTEN_RISE3_SHIFT 3
#define EXYNOS5440_TMU_INTEN_FALL0_SHIFT 4
-#define EXYNOS5440_TMU_INTEN_FALL1_SHIFT 5
-#define EXYNOS5440_TMU_INTEN_FALL2_SHIFT 6
-#define EXYNOS5440_TMU_INTEN_FALL3_SHIFT 7
-#define EXYNOS5440_TMU_TH_RISE0_SHIFT 0
-#define EXYNOS5440_TMU_TH_RISE1_SHIFT 8
-#define EXYNOS5440_TMU_TH_RISE2_SHIFT 16
-#define EXYNOS5440_TMU_TH_RISE3_SHIFT 24
#define EXYNOS5440_TMU_TH_RISE4_SHIFT 24
#define EXYNOS5440_EFUSE_SWAP_OFFSET 8
}
EXPORT_SYMBOL_GPL(st_thermal_unregister);
+#ifdef CONFIG_PM_SLEEP
static int st_thermal_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
return 0;
}
+#endif
+
SIMPLE_DEV_PM_OPS(st_thermal_pm_ops, st_thermal_suspend, st_thermal_resume);
EXPORT_SYMBOL_GPL(st_thermal_pm_ops);
thermal_zone_device_update(tz);
- if (!result)
- return tz;
+ return tz;
unregister:
release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
poll_wait(file, &tty->read_wait, wait);
poll_wait(file, &tty->write_wait, wait);
+ if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
+ mask |= POLLHUP;
if (input_available_p(tty, 1))
mask |= POLLIN | POLLRDNORM;
+ else if (mask & POLLHUP) {
+ tty_flush_to_ldisc(tty);
+ if (input_available_p(tty, 1))
+ mask |= POLLIN | POLLRDNORM;
+ }
if (tty->packet && tty->link->ctrl_status)
mask |= POLLPRI | POLLIN | POLLRDNORM;
- if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
- mask |= POLLHUP;
if (tty_hung_up_p(file))
mask |= POLLHUP;
if (!(mask & (POLLHUP | POLLIN | POLLRDNORM))) {
/* Set to highest baudrate supported */
if (baud >= 1152000)
baud = 921600;
- quot = DIV_ROUND_CLOSEST(port->uartclk, 256 * baud);
+ quot = (port->uartclk / (256 * baud)) + 1;
}
/*
if (of_find_property(ofdev->dev.of_node, "used-by-rtas", NULL))
return -EBUSY;
- info = kmalloc(sizeof(*info), GFP_KERNEL);
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
if (info == NULL)
return -ENOMEM;
return 0;
}
-#ifdef CONFIG_PM_SLEEP
-static int of_serial_suspend(struct device *dev)
-{
- struct of_serial_info *info = dev_get_drvdata(dev);
-
- serial8250_suspend_port(info->line);
- if (info->clk)
- clk_disable_unprepare(info->clk);
-
- return 0;
-}
-
-static int of_serial_resume(struct device *dev)
-{
- struct of_serial_info *info = dev_get_drvdata(dev);
-
- if (info->clk)
- clk_prepare_enable(info->clk);
-
- serial8250_resume_port(info->line);
-
- return 0;
-}
-#endif
-static SIMPLE_DEV_PM_OPS(of_serial_pm_ops, of_serial_suspend, of_serial_resume);
-
/*
* A few common types, add more as needed.
*/
.name = "of_serial",
.owner = THIS_MODULE,
.of_match_table = of_platform_serial_table,
- .pm = &of_serial_pm_ops,
},
.probe = of_platform_serial_probe,
.remove = of_platform_serial_remove,
* The spd_hi, spd_vhi, spd_shi, spd_warp kludge...
* Die! Die! Die!
*/
- if (baud == 38400)
+ if (try == 0 && baud == 38400)
baud = altbaud;
/*
int pty_master, tty_closing, o_tty_closing, do_sleep;
int idx;
char buf[64];
+ long timeout = 0;
+ int once = 1;
if (tty_paranoia_check(tty, inode, __func__))
return 0;
if (!do_sleep)
break;
- printk(KERN_WARNING "%s: %s: read/write wait queue active!\n",
- __func__, tty_name(tty, buf));
+ if (once) {
+ once = 0;
+ printk(KERN_WARNING "%s: %s: read/write wait queue active!\n",
+ __func__, tty_name(tty, buf));
+ }
tty_unlock_pair(tty, o_tty);
mutex_unlock(&tty_mutex);
- schedule();
+ schedule_timeout_killable(timeout);
+ if (timeout < 120 * HZ)
+ timeout = 2 * timeout + 1;
+ else
+ timeout = MAX_SCHEDULE_TIMEOUT;
}
/*
/* Save original vc_unipagdir_loc in case we allocate a new one */
p = *vc->vc_uni_pagedir_loc;
+
+ if (!p) {
+ err = -EINVAL;
+
+ goto out_unlock;
+ }
if (p->refcount > 1) {
int j, k;
set_inverse_transl(vc, p, i); /* Update inverse translations */
set_inverse_trans_unicode(vc, p);
+out_unlock:
console_unlock();
return err;
}
static DEFINE_MUTEX(acm_table_lock);
+static void acm_tty_set_termios(struct tty_struct *tty,
+ struct ktermios *termios_old);
+
/*
* acm_table accessors
*/
/* devices aren't required to support these requests.
* the cdc acm descriptor tells whether they do...
*/
-#define acm_set_control(acm, control) \
- acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0)
+static inline int acm_set_control(struct acm *acm, int control)
+{
+ if (acm->quirks & QUIRK_CONTROL_LINE_STATE)
+ return -EOPNOTSUPP;
+
+ return acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE,
+ control, NULL, 0);
+}
+
#define acm_set_line(acm, line) \
acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line))
#define acm_send_break(acm, ms) \
goto error_submit_urb;
}
+ acm_tty_set_termios(tty, NULL);
+
/*
* Unthrottle device in case the TTY was closed while throttled.
*/
/* FIXME: Needs to clear unsupported bits in the termios */
acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
- if (!newline.dwDTERate) {
+ if (C_BAUD(tty) == B0) {
newline.dwDTERate = acm->line.dwDTERate;
newctrl &= ~ACM_CTRL_DTR;
- } else
+ } else if (termios_old && (termios_old->c_cflag & CBAUD) == B0) {
newctrl |= ACM_CTRL_DTR;
+ }
if (newctrl != acm->ctrlout)
acm_set_control(acm, acm->ctrlout = newctrl);
tty_port_init(&acm->port);
acm->port.ops = &acm_port_ops;
init_usb_anchor(&acm->delayed);
+ acm->quirks = quirks;
buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
if (!buf) {
{ USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
.driver_info = NO_UNION_NORMAL, /* has no union descriptor */
},
+ { USB_DEVICE(0x20df, 0x0001), /* Simtec Electronics Entropy Key */
+ .driver_info = QUIRK_CONTROL_LINE_STATE, },
+ { USB_DEVICE(0x2184, 0x001c) }, /* GW Instek AFG-2225 */
{ USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
},
/* Motorola H24 HSPA module: */
unsigned int throttle_req:1; /* throttle requested */
u8 bInterval;
struct usb_anchor delayed; /* writes queued for a device about to be woken */
+ unsigned long quirks;
};
#define CDC_DATA_INTERFACE_TYPE 0x0a
#define NOT_A_MODEM BIT(3)
#define NO_DATA_INTERFACE BIT(4)
#define IGNORE_DEVICE BIT(5)
+#define QUIRK_CONTROL_LINE_STATE BIT(6)
return -EINVAL;
if (dev->speed != USB_SPEED_SUPER)
return -EINVAL;
+ if (dev->state < USB_STATE_CONFIGURED)
+ return -ENODEV;
for (i = 0; i < num_eps; i++) {
/* Streams only apply to bulk endpoints. */
if (retval)
goto fail;
- if (hcd->usb_phy && !hdev->parent)
- usb_phy_notify_connect(hcd->usb_phy, udev->speed);
-
/*
* Some superspeed devices have finished the link training process
* and attached to a superspeed hub port, but the device descriptor
/* Disconnect any existing devices under this port */
if (udev) {
- if (hcd->usb_phy && !hdev->parent &&
- !(portstatus & USB_PORT_STAT_CONNECTION))
+ if (hcd->usb_phy && !hdev->parent)
usb_phy_notify_disconnect(hcd->usb_phy, udev->speed);
usb_disconnect(&port_dev->child);
}
port_dev->child = NULL;
spin_unlock_irq(&device_state_lock);
mutex_unlock(&usb_port_peer_mutex);
+ } else {
+ if (hcd->usb_phy && !hdev->parent)
+ usb_phy_notify_connect(hcd->usb_phy,
+ udev->speed);
}
}
/* Creative SB Audigy 2 NX */
{ USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* Microsoft Wireless Laser Mouse 6000 Receiver */
+ { USB_DEVICE(0x045e, 0x00e1), .driver_info = USB_QUIRK_RESET_RESUME },
+
/* Microsoft LifeCam-VX700 v2.0 */
{ USB_DEVICE(0x045e, 0x0770), .driver_info = USB_QUIRK_RESET_RESUME },
{ USB_DEVICE(0x04f3, 0x0089), .driver_info =
USB_QUIRK_DEVICE_QUALIFIER },
+ { USB_DEVICE(0x04f3, 0x009b), .driver_info =
+ USB_QUIRK_DEVICE_QUALIFIER },
+
+ { USB_DEVICE(0x04f3, 0x016f), .driver_info =
+ USB_QUIRK_DEVICE_QUALIFIER },
+
/* Roland SC-8820 */
{ USB_DEVICE(0x0582, 0x0007), .driver_info = USB_QUIRK_RESET_RESUME },
u32 usb_status = readl(hsotg->regs + GOTGCTL);
- dev_info(hsotg->dev, "%s: USBRst\n", __func__);
+ dev_dbg(hsotg->dev, "%s: USBRst\n", __func__);
dev_dbg(hsotg->dev, "GNPTXSTS=%08x\n",
readl(hsotg->regs + GNPTXSTS));
trb = dwc->ep0_trb;
+ r = next_request(&ep0->request_list);
+ if (!r)
+ return;
+
status = DWC3_TRB_SIZE_TRBSTS(trb->size);
if (status == DWC3_TRBSTS_SETUP_PENDING) {
dwc3_trace(trace_dwc3_ep0, "Setup Pending received");
return;
}
- r = next_request(&ep0->request_list);
- if (!r)
- return;
-
ur = &r->request;
length = trb->size & DWC3_TRB_SIZE_MASK;
case INQUIRY:
case MODE_SENSE:
case MODE_SENSE_10:
- case SERVICE_ACTION_IN:
+ case SERVICE_ACTION_IN_16:
case MAINTENANCE_IN:
case PERSISTENT_RESERVE_IN:
case SECURITY_PROTOCOL_IN:
config USB_EHCI_EXYNOS
tristate "EHCI support for Samsung S5P/EXYNOS SoC Series"
- depends on PLAT_S5P || ARCH_EXYNOS
+ depends on ARCH_S5PV210 || ARCH_EXYNOS
help
Enable support for the Samsung Exynos SOC's on-chip EHCI controller.
config USB_OHCI_EXYNOS
tristate "OHCI support for Samsung S5P/EXYNOS SoC Series"
- depends on PLAT_S5P || ARCH_EXYNOS
+ depends on ARCH_S5PV210 || ARCH_EXYNOS
help
Enable support for the Samsung Exynos SOC's on-chip OHCI controller.
wa->wa_descr = wa_descr = (struct usb_wa_descriptor *) hdr;
if (le16_to_cpu(wa_descr->bcdWAVersion) > 0x0100)
dev_warn(dev, "Wire Adapter v%d.%d newer than groked v1.0\n",
- le16_to_cpu(wa_descr->bcdWAVersion) & 0xff00 >> 8,
+ (le16_to_cpu(wa_descr->bcdWAVersion) & 0xff00) >> 8,
le16_to_cpu(wa_descr->bcdWAVersion) & 0x00ff);
result = 0;
error:
#include <linux/slab.h>
-#include <linux/device.h>
#include <asm/unaligned.h>
#include "xhci.h"
* including the USB 3.0 roothub, but only if CONFIG_PM_RUNTIME
* is enabled, so also enable remote wake here.
*/
- if (hcd->self.root_hub->do_remote_wakeup
- && device_may_wakeup(hcd->self.controller)) {
-
+ if (hcd->self.root_hub->do_remote_wakeup) {
if (t1 & PORT_CONNECT) {
t2 |= PORT_WKOC_E | PORT_WKDISC_E;
t2 &= ~PORT_WKCONN_E;
xhci->quirks |= XHCI_SPURIOUS_REBOOT;
xhci->quirks |= XHCI_AVOID_BEI;
}
- if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
- (pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI ||
- pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI)) {
- /* Workaround for occasional spurious wakeups from S5 (or
- * any other sleep) on Haswell machines with LPT and LPT-LP
- * with the new Intel BIOS
- */
- /* Limit the quirk to only known vendors, as this triggers
- * yet another BIOS bug on some other machines
- * https://bugzilla.kernel.org/show_bug.cgi?id=66171
- */
- if (pdev->subsystem_vendor == PCI_VENDOR_ID_HP)
- xhci->quirks |= XHCI_SPURIOUS_WAKEUP;
- }
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI) {
xhci->quirks |= XHCI_SPURIOUS_REBOOT;
pdev->device == 0x3432)
xhci->quirks |= XHCI_BROKEN_STREAMS;
+ if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA &&
+ pdev->device == 0x1042)
+ xhci->quirks |= XHCI_BROKEN_STREAMS;
+
if (xhci->quirks & XHCI_RESET_ON_RESUME)
xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
"QUIRK: Resetting on resume");
if (xhci->quirks & XHCI_COMP_MODE_QUIRK)
pdev->no_d3cold = true;
- return xhci_suspend(xhci);
+ return xhci_suspend(xhci, do_wakeup);
}
static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- return xhci_suspend(xhci);
+ /*
+ * xhci_suspend() needs `do_wakeup` to know whether host is allowed
+ * to do wakeup during suspend. Since xhci_plat_suspend is currently
+ * only designed for system suspend, device_may_wakeup() is enough
+ * to dertermine whether host is allowed to do wakeup. Need to
+ * reconsider this when xhci_plat_suspend enlarges its scope, e.g.,
+ * also applies to runtime suspend.
+ */
+ return xhci_suspend(xhci, device_may_wakeup(dev));
}
static int xhci_plat_resume(struct device *dev)
false);
xhci_ring_cmd_db(xhci);
} else {
- /* Clear our internal halted state and restart the ring(s) */
+ /* Clear our internal halted state */
xhci->devs[slot_id]->eps[ep_index].ep_state &= ~EP_HALTED;
- ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
}
}
ep->stopped_td = td;
return 0;
} else {
- if (trb_comp_code == COMP_STALL) {
- /* The transfer is completed from the driver's
- * perspective, but we need to issue a set dequeue
- * command for this stalled endpoint to move the dequeue
- * pointer past the TD. We can't do that here because
- * the halt condition must be cleared first. Let the
- * USB class driver clear the stall later.
- */
- ep->stopped_td = td;
- ep->stopped_stream = ep_ring->stream_id;
- } else if (xhci_requires_manual_halt_cleanup(xhci,
- ep_ctx, trb_comp_code)) {
- /* Other types of errors halt the endpoint, but the
- * class driver doesn't call usb_reset_endpoint() unless
- * the error is -EPIPE. Clear the halted status in the
- * xHCI hardware manually.
+ if (trb_comp_code == COMP_STALL ||
+ xhci_requires_manual_halt_cleanup(xhci, ep_ctx,
+ trb_comp_code)) {
+ /* Issue a reset endpoint command to clear the host side
+ * halt, followed by a set dequeue command to move the
+ * dequeue pointer past the TD.
+ * The class driver clears the device side halt later.
*/
xhci_cleanup_halted_endpoint(xhci,
slot_id, ep_index, ep_ring->stream_id,
else
td->urb->actual_length = 0;
- xhci_cleanup_halted_endpoint(xhci,
- slot_id, ep_index, 0, td, event_trb);
- return finish_td(xhci, td, event_trb, event, ep, status, true);
+ return finish_td(xhci, td, event_trb, event, ep, status, false);
}
/*
* Did we transfer any data, despite the errors that might have
if (ret) {
urb = td->urb;
urb_priv = urb->hcpriv;
- /* Leave the TD around for the reset endpoint function
- * to use(but only if it's not a control endpoint,
- * since we already queued the Set TR dequeue pointer
- * command for stalled control endpoints).
- */
- if (usb_endpoint_xfer_control(&urb->ep->desc) ||
- (trb_comp_code != COMP_STALL &&
- trb_comp_code != COMP_BABBLE))
- xhci_urb_free_priv(xhci, urb_priv);
- else
- kfree(urb_priv);
+
+ xhci_urb_free_priv(xhci, urb_priv);
usb_hcd_unlink_urb_from_ep(bus_to_hcd(urb->dev->bus), urb);
if ((urb->actual_length != urb->transfer_buffer_length &&
#define DRIVER_AUTHOR "Sarah Sharp"
#define DRIVER_DESC "'eXtensible' Host Controller (xHC) Driver"
+#define PORT_WAKE_BITS (PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E)
+
/* Some 0.95 hardware can't handle the chain bit on a Link TRB being cleared */
static int link_quirk;
module_param(link_quirk, int, S_IRUGO | S_IWUSR);
xhci_set_cmd_ring_deq(xhci);
}
+static void xhci_disable_port_wake_on_bits(struct xhci_hcd *xhci)
+{
+ int port_index;
+ __le32 __iomem **port_array;
+ unsigned long flags;
+ u32 t1, t2;
+
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ /* disble usb3 ports Wake bits*/
+ port_index = xhci->num_usb3_ports;
+ port_array = xhci->usb3_ports;
+ while (port_index--) {
+ t1 = readl(port_array[port_index]);
+ t1 = xhci_port_state_to_neutral(t1);
+ t2 = t1 & ~PORT_WAKE_BITS;
+ if (t1 != t2)
+ writel(t2, port_array[port_index]);
+ }
+
+ /* disble usb2 ports Wake bits*/
+ port_index = xhci->num_usb2_ports;
+ port_array = xhci->usb2_ports;
+ while (port_index--) {
+ t1 = readl(port_array[port_index]);
+ t1 = xhci_port_state_to_neutral(t1);
+ t2 = t1 & ~PORT_WAKE_BITS;
+ if (t1 != t2)
+ writel(t2, port_array[port_index]);
+ }
+
+ spin_unlock_irqrestore(&xhci->lock, flags);
+}
+
/*
* Stop HC (not bus-specific)
*
* This is called when the machine transition into S3/S4 mode.
*
*/
-int xhci_suspend(struct xhci_hcd *xhci)
+int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
{
int rc = 0;
unsigned int delay = XHCI_MAX_HALT_USEC;
xhci->shared_hcd->state != HC_STATE_SUSPENDED)
return -EINVAL;
+ /* Clear root port wake on bits if wakeup not allowed. */
+ if (!do_wakeup)
+ xhci_disable_port_wake_on_bits(xhci);
+
/* Don't poll the roothubs on bus suspend. */
xhci_dbg(xhci, "%s: stopping port polling.\n", __func__);
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
}
}
-/* Deal with stalled endpoints. The core should have sent the control message
- * to clear the halt condition. However, we need to make the xHCI hardware
- * reset its sequence number, since a device will expect a sequence number of
- * zero after the halt condition is cleared.
+/* Called when clearing halted device. The core should have sent the control
+ * message to clear the device halt condition. The host side of the halt should
+ * already be cleared with a reset endpoint command issued when the STALL tx
+ * event was received.
+ *
* Context: in_interrupt
*/
+
void xhci_endpoint_reset(struct usb_hcd *hcd,
struct usb_host_endpoint *ep)
{
struct xhci_hcd *xhci;
- struct usb_device *udev;
- unsigned int ep_index;
- unsigned long flags;
- int ret;
- struct xhci_virt_ep *virt_ep;
- struct xhci_command *command;
xhci = hcd_to_xhci(hcd);
- udev = (struct usb_device *) ep->hcpriv;
- /* Called with a root hub endpoint (or an endpoint that wasn't added
- * with xhci_add_endpoint()
- */
- if (!ep->hcpriv)
- return;
- ep_index = xhci_get_endpoint_index(&ep->desc);
- virt_ep = &xhci->devs[udev->slot_id]->eps[ep_index];
- if (!virt_ep->stopped_td) {
- xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep,
- "Endpoint 0x%x not halted, refusing to reset.",
- ep->desc.bEndpointAddress);
- return;
- }
- if (usb_endpoint_xfer_control(&ep->desc)) {
- xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep,
- "Control endpoint stall already handled.");
- return;
- }
- command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC);
- if (!command)
- return;
-
- xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep,
- "Queueing reset endpoint command");
- spin_lock_irqsave(&xhci->lock, flags);
- ret = xhci_queue_reset_ep(xhci, command, udev->slot_id, ep_index);
/*
- * Can't change the ring dequeue pointer until it's transitioned to the
- * stopped state, which is only upon a successful reset endpoint
- * command. Better hope that last command worked!
+ * We might need to implement the config ep cmd in xhci 4.8.1 note:
+ * The Reset Endpoint Command may only be issued to endpoints in the
+ * Halted state. If software wishes reset the Data Toggle or Sequence
+ * Number of an endpoint that isn't in the Halted state, then software
+ * may issue a Configure Endpoint Command with the Drop and Add bits set
+ * for the target endpoint. that is in the Stopped state.
*/
- if (!ret) {
- xhci_cleanup_stalled_ring(xhci, udev, ep_index);
- kfree(virt_ep->stopped_td);
- xhci_ring_cmd_db(xhci);
- }
- virt_ep->stopped_td = NULL;
- virt_ep->stopped_stream = 0;
- spin_unlock_irqrestore(&xhci->lock, flags);
- if (ret)
- xhci_warn(xhci, "FIXME allocate a new ring segment\n");
+ /* For now just print debug to follow the situation */
+ xhci_dbg(xhci, "Endpoint 0x%x ep reset callback called\n",
+ ep->desc.bEndpointAddress);
}
static int xhci_check_streams_endpoint(struct xhci_hcd *xhci,
void xhci_init_driver(struct hc_driver *drv, int (*setup_fn)(struct usb_hcd *));
#ifdef CONFIG_PM
-int xhci_suspend(struct xhci_hcd *xhci);
+int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup);
int xhci_resume(struct xhci_hcd *xhci, bool hibernated);
#else
#define xhci_suspend NULL
{ USB_DEVICE(0x10C4, 0x85F8) }, /* Virtenio Preon32 */
{ USB_DEVICE(0x10C4, 0x8664) }, /* AC-Services CAN-IF */
{ USB_DEVICE(0x10C4, 0x8665) }, /* AC-Services OBD-IF */
+ { USB_DEVICE(0x10C4, 0x8875) }, /* CEL MeshConnect USB Stick */
{ USB_DEVICE(0x10C4, 0x88A4) }, /* MMB Networks ZigBee USB Device */
{ USB_DEVICE(0x10C4, 0x88A5) }, /* Planet Innovation Ingeni ZigBee USB Device */
{ USB_DEVICE(0x10C4, 0x8946) }, /* Ketra N1 Wireless Interface */
{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FD_PID) },
{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FE_PID) },
{ USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_01FF_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_4701_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9300_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9301_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9302_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9303_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9304_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9305_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9306_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9307_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9308_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9309_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930A_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930B_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930C_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930D_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930E_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_930F_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9310_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9311_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9312_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9313_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9314_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9315_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9316_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9317_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9318_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_9319_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931A_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931B_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931C_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931D_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931E_PID) },
+ { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_931F_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_PERLE_ULTRAPORT_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_PIEGROUP_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_TNC_X_PID) },
#define BAYER_CONTOUR_CABLE_PID 0x6001
/*
- * The following are the values for the Matrix Orbital FTDI Range
- * Anything in this range will use an FT232RL.
+ * Matrix Orbital Intelligent USB displays.
+ * http://www.matrixorbital.com
*/
#define MTXORB_VID 0x1B3D
#define MTXORB_FTDI_RANGE_0100_PID 0x0100
#define MTXORB_FTDI_RANGE_01FD_PID 0x01FD
#define MTXORB_FTDI_RANGE_01FE_PID 0x01FE
#define MTXORB_FTDI_RANGE_01FF_PID 0x01FF
-
-
+#define MTXORB_FTDI_RANGE_4701_PID 0x4701
+#define MTXORB_FTDI_RANGE_9300_PID 0x9300
+#define MTXORB_FTDI_RANGE_9301_PID 0x9301
+#define MTXORB_FTDI_RANGE_9302_PID 0x9302
+#define MTXORB_FTDI_RANGE_9303_PID 0x9303
+#define MTXORB_FTDI_RANGE_9304_PID 0x9304
+#define MTXORB_FTDI_RANGE_9305_PID 0x9305
+#define MTXORB_FTDI_RANGE_9306_PID 0x9306
+#define MTXORB_FTDI_RANGE_9307_PID 0x9307
+#define MTXORB_FTDI_RANGE_9308_PID 0x9308
+#define MTXORB_FTDI_RANGE_9309_PID 0x9309
+#define MTXORB_FTDI_RANGE_930A_PID 0x930A
+#define MTXORB_FTDI_RANGE_930B_PID 0x930B
+#define MTXORB_FTDI_RANGE_930C_PID 0x930C
+#define MTXORB_FTDI_RANGE_930D_PID 0x930D
+#define MTXORB_FTDI_RANGE_930E_PID 0x930E
+#define MTXORB_FTDI_RANGE_930F_PID 0x930F
+#define MTXORB_FTDI_RANGE_9310_PID 0x9310
+#define MTXORB_FTDI_RANGE_9311_PID 0x9311
+#define MTXORB_FTDI_RANGE_9312_PID 0x9312
+#define MTXORB_FTDI_RANGE_9313_PID 0x9313
+#define MTXORB_FTDI_RANGE_9314_PID 0x9314
+#define MTXORB_FTDI_RANGE_9315_PID 0x9315
+#define MTXORB_FTDI_RANGE_9316_PID 0x9316
+#define MTXORB_FTDI_RANGE_9317_PID 0x9317
+#define MTXORB_FTDI_RANGE_9318_PID 0x9318
+#define MTXORB_FTDI_RANGE_9319_PID 0x9319
+#define MTXORB_FTDI_RANGE_931A_PID 0x931A
+#define MTXORB_FTDI_RANGE_931B_PID 0x931B
+#define MTXORB_FTDI_RANGE_931C_PID 0x931C
+#define MTXORB_FTDI_RANGE_931D_PID 0x931D
+#define MTXORB_FTDI_RANGE_931E_PID 0x931E
+#define MTXORB_FTDI_RANGE_931F_PID 0x931F
/*
* The Mobility Lab (TML)
if ((data[0] & 0x80) == 0) {
/* no errors on individual bytes, only
possible overrun err */
- if (data[0] & RXERROR_OVERRUN)
- err = TTY_OVERRUN;
- else
- err = 0;
+ if (data[0] & RXERROR_OVERRUN) {
+ tty_insert_flip_char(&port->port, 0,
+ TTY_OVERRUN);
+ }
for (i = 1; i < urb->actual_length ; ++i)
- tty_insert_flip_char(&port->port, data[i], err);
+ tty_insert_flip_char(&port->port, data[i],
+ TTY_NORMAL);
} else {
/* some bytes had errors, every byte has status */
dev_dbg(&port->dev, "%s - RX error!!!!\n", __func__);
for (i = 0; i + 1 < urb->actual_length; i += 2) {
- int stat = data[i], flag = 0;
- if (stat & RXERROR_OVERRUN)
- flag |= TTY_OVERRUN;
- if (stat & RXERROR_FRAMING)
- flag |= TTY_FRAME;
- if (stat & RXERROR_PARITY)
- flag |= TTY_PARITY;
+ int stat = data[i];
+ int flag = TTY_NORMAL;
+
+ if (stat & RXERROR_OVERRUN) {
+ tty_insert_flip_char(&port->port, 0,
+ TTY_OVERRUN);
+ }
/* XXX should handle break (0x10) */
+ if (stat & RXERROR_PARITY)
+ flag = TTY_PARITY;
+ else if (stat & RXERROR_FRAMING)
+ flag = TTY_FRAME;
+
tty_insert_flip_char(&port->port, data[i+1],
flag);
}
} else {
/* some bytes had errors, every byte has status */
for (i = 0; i + 1 < urb->actual_length; i += 2) {
- int stat = data[i], flag = 0;
- if (stat & RXERROR_OVERRUN)
- flag |= TTY_OVERRUN;
- if (stat & RXERROR_FRAMING)
- flag |= TTY_FRAME;
- if (stat & RXERROR_PARITY)
- flag |= TTY_PARITY;
+ int stat = data[i];
+ int flag = TTY_NORMAL;
+
+ if (stat & RXERROR_OVERRUN) {
+ tty_insert_flip_char(&port->port, 0,
+ TTY_OVERRUN);
+ }
/* XXX should handle break (0x10) */
+ if (stat & RXERROR_PARITY)
+ flag = TTY_PARITY;
+ else if (stat & RXERROR_FRAMING)
+ flag = TTY_FRAME;
+
tty_insert_flip_char(&port->port, data[i+1],
flag);
}
*/
for (x = 0; x + 1 < len &&
i + 1 < urb->actual_length; x += 2) {
- int stat = data[i], flag = 0;
+ int stat = data[i];
+ int flag = TTY_NORMAL;
- if (stat & RXERROR_OVERRUN)
- flag |= TTY_OVERRUN;
- if (stat & RXERROR_FRAMING)
- flag |= TTY_FRAME;
- if (stat & RXERROR_PARITY)
- flag |= TTY_PARITY;
+ if (stat & RXERROR_OVERRUN) {
+ tty_insert_flip_char(&port->port, 0,
+ TTY_OVERRUN);
+ }
/* XXX should handle break (0x10) */
+ if (stat & RXERROR_PARITY)
+ flag = TTY_PARITY;
+ else if (stat & RXERROR_FRAMING)
+ flag = TTY_FRAME;
+
tty_insert_flip_char(&port->port, data[i+1],
flag);
i += 2;
if ((data[0] & 0x80) == 0) {
/* no errors on individual bytes, only
possible overrun err*/
- if (data[0] & RXERROR_OVERRUN)
- err = TTY_OVERRUN;
- else
- err = 0;
+ if (data[0] & RXERROR_OVERRUN) {
+ tty_insert_flip_char(&port->port, 0,
+ TTY_OVERRUN);
+ }
for (i = 1; i < urb->actual_length ; ++i)
tty_insert_flip_char(&port->port,
- data[i], err);
+ data[i], TTY_NORMAL);
} else {
/* some bytes had errors, every byte has status */
dev_dbg(&port->dev, "%s - RX error!!!!\n", __func__);
for (i = 0; i + 1 < urb->actual_length; i += 2) {
- int stat = data[i], flag = 0;
- if (stat & RXERROR_OVERRUN)
- flag |= TTY_OVERRUN;
- if (stat & RXERROR_FRAMING)
- flag |= TTY_FRAME;
- if (stat & RXERROR_PARITY)
- flag |= TTY_PARITY;
+ int stat = data[i];
+ int flag = TTY_NORMAL;
+
+ if (stat & RXERROR_OVERRUN) {
+ tty_insert_flip_char(
+ &port->port, 0,
+ TTY_OVERRUN);
+ }
/* XXX should handle break (0x10) */
+ if (stat & RXERROR_PARITY)
+ flag = TTY_PARITY;
+ else if (stat & RXERROR_FRAMING)
+ flag = TTY_FRAME;
+
tty_insert_flip_char(&port->port,
data[i+1], flag);
}
port->interrupt_out_urb->transfer_buffer_length = length;
priv->cur_pos = priv->cur_pos + length;
- result = usb_submit_urb(port->interrupt_out_urb, GFP_NOIO);
+ result = usb_submit_urb(port->interrupt_out_urb,
+ GFP_ATOMIC);
dev_dbg(&port->dev, "%s - Send write URB returns: %i\n", __func__, result);
todo = priv->filled - priv->cur_pos;
if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID ||
priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) {
result = usb_submit_urb(port->interrupt_in_urb,
- GFP_NOIO);
+ GFP_ATOMIC);
dev_dbg(&port->dev, "%s - Send read URB returns: %i\n", __func__, result);
}
}
/* The connected devices do not have a bulk write endpoint,
* to transmit data to de barcode device the control endpoint is used */
- dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO);
+ dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
if (!dr) {
count = -ENOMEM;
goto error_no_dr;
if (*tty_flag == TTY_NORMAL)
*tty_flag = TTY_FRAME;
}
- if (lsr & UART_LSR_OE){
+ if (lsr & UART_LSR_OE) {
port->icount.overrun++;
- if (*tty_flag == TTY_NORMAL)
- *tty_flag = TTY_OVERRUN;
+ tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
}
}
if ((len >= 4) &&
(packet[0] == 0x1b) && (packet[1] == 0x1b) &&
((packet[2] == 0x00) || (packet[2] == 0x01))) {
- if (packet[2] == 0x00) {
+ if (packet[2] == 0x00)
ssu100_update_lsr(port, packet[3], &flag);
- if (flag == TTY_OVERRUN)
- tty_insert_flip_char(&port->port, 0,
- TTY_OVERRUN);
- }
if (packet[2] == 0x01)
ssu100_update_msr(port, packet[3]);
unsigned char asc,
unsigned char ascq)
{
- const char *what, *keystr;
+ const char *what, *keystr, *fmt;
keystr = scsi_sense_key_string(key);
- what = scsi_extd_sense_format(asc, ascq);
+ what = scsi_extd_sense_format(asc, ascq, &fmt);
if (keystr == NULL)
keystr = "(Unknown Key)";
what = "(unknown ASC/ASCQ)";
usb_stor_dbg(us, "%s: ", keystr);
- US_DEBUGPX(what, ascq);
- US_DEBUGPX("\n");
+ if (fmt)
+ US_DEBUGPX("%s (%s%x)\n", what, fmt, ascq);
+ else
+ US_DEBUGPX("%s\n", what);
}
int usb_stor_dbg(const struct us_data *us, const char *fmt, ...)
us->iobuf[0] = 0x1;
result = usb_stor_control_msg(us, us->send_ctrl_pipe,
0x0C, USB_RECIP_INTERFACE | USB_TYPE_VENDOR,
- 0x01, 0x0, us->iobuf, 0x1, USB_CTRL_SET_TIMEOUT);
+ 0x01, 0x0, us->iobuf, 0x1, 5 * HZ);
usb_stor_dbg(us, "-- result is %d\n", result);
return 0;
result = usb_stor_control_msg(us, us->send_ctrl_pipe,
USB_REQ_SET_FEATURE,
USB_TYPE_STANDARD | USB_RECIP_DEVICE,
- 0x01, 0x0, NULL, 0x0, 1000);
+ 0x01, 0x0, NULL, 0x0, 1 * HZ);
usb_stor_dbg(us, "Huawei mode set result is %d\n", result);
return 0;
}
return 0;
}
+#ifdef CONFIG_PM
static int config_autodelink_before_power_down(struct us_data *us)
{
struct rts51x_chip *chip = (struct rts51x_chip *)(us->extra);
}
}
}
+#endif
#ifdef CONFIG_REALTEK_AUTOPM
static void fw5895_set_mmc_wp(struct us_data *us)
*/
if (result == USB_STOR_XFER_LONG)
fake_sense = 1;
+
+ /*
+ * Sometimes a device will mistakenly skip the data phase
+ * and go directly to the status phase without sending a
+ * zero-length packet. If we get a 13-byte response here,
+ * check whether it really is a CSW.
+ */
+ if (result == USB_STOR_XFER_SHORT &&
+ srb->sc_data_direction == DMA_FROM_DEVICE &&
+ transfer_length - scsi_get_resid(srb) ==
+ US_BULK_CS_WRAP_LEN) {
+ struct scatterlist *sg = NULL;
+ unsigned int offset = 0;
+
+ if (usb_stor_access_xfer_buf((unsigned char *) bcs,
+ US_BULK_CS_WRAP_LEN, srb, &sg,
+ &offset, FROM_XFER_BUF) ==
+ US_BULK_CS_WRAP_LEN &&
+ bcs->Signature ==
+ cpu_to_le32(US_BULK_CS_SIGN)) {
+ usb_stor_dbg(us, "Device skipped data phase\n");
+ scsi_set_resid(srb, transfer_length);
+ goto skipped_data_phase;
+ }
+ }
}
/* See flow chart on pg 15 of the Bulk Only Transport spec for
if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_ERROR;
+ skipped_data_phase:
/* check bulk status */
residue = le32_to_cpu(bcs->Residue);
usb_stor_dbg(us, "Bulk Status S 0x%x T 0x%x R %u Stat 0x%x\n",
{
int tag;
- if (blk_rq_tagged(cmnd->request))
+ if (cmnd->flags & SCMD_TAGGED)
tag = cmnd->request->tag + 2;
else
tag = 1;
if (devinfo->flags & US_FL_NO_REPORT_OPCODES)
sdev->no_report_opcodes = 1;
- scsi_set_tag_type(sdev, MSG_ORDERED_TAG);
- scsi_activate_tcq(sdev, devinfo->qdepth - 2);
+ scsi_change_queue_depth(sdev, devinfo->qdepth - 2);
return 0;
}
.sg_tablesize = SG_NONE,
.cmd_per_lun = 1, /* until we override it */
.skip_settle_delay = 1,
- .ordered_tag = 1,
/*
* The uas drivers expects tags not to be bigger than the maximum
* allocator.
*/
.disable_blk_mq = true,
+ .use_blk_tags = 1,
};
#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_NO_ATA_1X),
+/* Reported-by: Hans de Goede <hdegoede@redhat.com> */
+UNUSUAL_DEV(0x0bc2, 0x3320, 0x0000, 0x9999,
+ "Seagate",
+ "Expansion Desk",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_NO_ATA_1X),
+
+/* Reported-by: Bogdan Mihalcea <bogdan.mihalcea@infim.ro> */
+UNUSUAL_DEV(0x0bc2, 0xa003, 0x0000, 0x9999,
+ "Seagate",
+ "Backup Plus",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_NO_ATA_1X),
+
/* https://bbs.archlinux.org/viewtopic.php?id=183190 */
UNUSUAL_DEV(0x0bc2, 0xab20, 0x0000, 0x9999,
"Seagate",
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_NO_ATA_1X),
+/* https://bbs.archlinux.org/viewtopic.php?id=183190 */
+UNUSUAL_DEV(0x0bc2, 0xab21, 0x0000, 0x9999,
+ "Seagate",
+ "Backup+ BK",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_NO_ATA_1X),
+
/* Reported-by: Claudio Bizzarri <claudio.bizzarri@gmail.com> */
UNUSUAL_DEV(0x152d, 0x0567, 0x0000, 0x9999,
"JMicron",
"ASM1051",
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_IGNORE_UAS),
+
+/* Reported-by: Hans de Goede <hdegoede@redhat.com> */
+UNUSUAL_DEV(0x2109, 0x0711, 0x0000, 0x9999,
+ "VIA",
+ "VL711",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_NO_ATA_1X),
+
+/* Reported-by: Hans de Goede <hdegoede@redhat.com> */
+UNUSUAL_DEV(0x4971, 0x1012, 0x0000, 0x9999,
+ "Hitachi",
+ "External HDD",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_IGNORE_UAS),
vhost_scsi_set_endpoint(struct vhost_scsi *vs,
struct vhost_scsi_target *t)
{
+ struct se_portal_group *se_tpg;
struct tcm_vhost_tport *tv_tport;
struct tcm_vhost_tpg *tpg;
struct tcm_vhost_tpg **vs_tpg;
ret = -EEXIST;
goto out;
}
+ /*
+ * In order to ensure individual vhost-scsi configfs
+ * groups cannot be removed while in use by vhost ioctl,
+ * go ahead and take an explicit se_tpg->tpg_group.cg_item
+ * dependency now.
+ */
+ se_tpg = &tpg->se_tpg;
+ ret = configfs_depend_item(se_tpg->se_tpg_tfo->tf_subsys,
+ &se_tpg->tpg_group.cg_item);
+ if (ret) {
+ pr_warn("configfs_depend_item() failed: %d\n", ret);
+ kfree(vs_tpg);
+ mutex_unlock(&tpg->tv_tpg_mutex);
+ goto out;
+ }
tpg->tv_tpg_vhost_count++;
tpg->vhost_scsi = vs;
vs_tpg[tpg->tport_tpgt] = tpg;
vhost_scsi_clear_endpoint(struct vhost_scsi *vs,
struct vhost_scsi_target *t)
{
+ struct se_portal_group *se_tpg;
struct tcm_vhost_tport *tv_tport;
struct tcm_vhost_tpg *tpg;
struct vhost_virtqueue *vq;
vs->vs_tpg[target] = NULL;
match = true;
mutex_unlock(&tpg->tv_tpg_mutex);
+ /*
+ * Release se_tpg->tpg_group.cg_item configfs dependency now
+ * to allow vhost-scsi WWPN se_tpg->tpg_group shutdown to occur.
+ */
+ se_tpg = &tpg->se_tpg;
+ configfs_undepend_item(se_tpg->se_tpg_tfo->tf_subsys,
+ &se_tpg->tpg_group.cg_item);
}
if (match) {
for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) {
static const struct s3c2410_wdt_variant drv_data_exynos7 = {
.disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET,
.mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET,
- .mask_bit = 0,
+ .mask_bit = 23,
.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
.rst_stat_bit = 23, /* A57 WDTRESET */
.quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT,
.acpi = EFI_INVALID_TABLE_ADDR,
.acpi20 = EFI_INVALID_TABLE_ADDR,
.smbios = EFI_INVALID_TABLE_ADDR,
+ .smbios3 = EFI_INVALID_TABLE_ADDR,
.sal_systab = EFI_INVALID_TABLE_ADDR,
.boot_info = EFI_INVALID_TABLE_ADDR,
.hcdp = EFI_INVALID_TABLE_ADDR,
tpg->tport->tport_name, pending_req->v2p->lun,
pending_req->cmnd[0], status_byte(errors), msg_byte(errors),
host_byte(errors), driver_byte(errors));
-
- if (CHECK_CONDITION & status_byte(errors))
- __scsi_print_sense("xen-pvscsi", sense_buffer,
- SCSI_SENSE_BUFFERSIZE);
}
static void scsiback_fast_flush_area(struct vscsibk_pend *req)
obj-$(CONFIG_AUTOFS4_FS) += autofs4/
obj-$(CONFIG_ADFS_FS) += adfs/
obj-$(CONFIG_FUSE_FS) += fuse/
-obj-$(CONFIG_OVERLAYFS_FS) += overlayfs/
+obj-$(CONFIG_OVERLAY_FS) += overlayfs/
obj-$(CONFIG_UDF_FS) += udf/
obj-$(CONFIG_SUN_OPENPROMFS) += openpromfs/
obj-$(CONFIG_OMFS_FS) += omfs/
static const struct file_operations aio_ring_fops;
static const struct address_space_operations aio_ctx_aops;
+/* Backing dev info for aio fs.
+ * -no dirty page accounting or writeback happens
+ */
+static struct backing_dev_info aio_fs_backing_dev_info = {
+ .name = "aiofs",
+ .state = 0,
+ .capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK | BDI_CAP_MAP_COPY,
+};
+
static struct file *aio_private_file(struct kioctx *ctx, loff_t nr_pages)
{
struct qstr this = QSTR_INIT("[aio]", 5);
inode->i_mapping->a_ops = &aio_ctx_aops;
inode->i_mapping->private_data = ctx;
+ inode->i_mapping->backing_dev_info = &aio_fs_backing_dev_info;
inode->i_size = PAGE_SIZE * nr_pages;
path.dentry = d_alloc_pseudo(aio_mnt->mnt_sb, &this);
if (IS_ERR(aio_mnt))
panic("Failed to create aio fs mount.");
+ if (bdi_init(&aio_fs_backing_dev_info))
+ panic("Failed to init aio fs backing dev info.");
+
kiocb_cachep = KMEM_CACHE(kiocb, SLAB_HWCACHE_ALIGN|SLAB_PANIC);
kioctx_cachep = KMEM_CACHE(kioctx,SLAB_HWCACHE_ALIGN|SLAB_PANIC);
.mmap = aio_ring_mmap,
};
-static int aio_set_page_dirty(struct page *page)
-{
- return 0;
-}
-
#if IS_ENABLED(CONFIG_MIGRATION)
static int aio_migratepage(struct address_space *mapping, struct page *new,
struct page *old, enum migrate_mode mode)
#endif
static const struct address_space_operations aio_ctx_aops = {
- .set_page_dirty = aio_set_page_dirty,
+ .set_page_dirty = __set_page_dirty_no_writeback,
#if IS_ENABLED(CONFIG_MIGRATION)
.migratepage = aio_migratepage,
#endif
pr_debug("pid(%d) page[%d]->count=%d\n",
current->pid, i, page_count(page));
SetPageUptodate(page);
- SetPageDirty(page);
unlock_page(page);
ctx->ring_pages[i] = page;
bytes = min(bytes, working_bytes);
kaddr = kmap_atomic(page_out);
memcpy(kaddr + *pg_offset, buf + buf_offset, bytes);
- if (*pg_index == (vcnt - 1) && *pg_offset == 0)
- memset(kaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
kunmap_atomic(kaddr);
flush_dcache_page(page_out);
return 1;
}
+
+/*
+ * When uncompressing data, we need to make sure and zero any parts of
+ * the biovec that were not filled in by the decompression code. pg_index
+ * and pg_offset indicate the last page and the last offset of that page
+ * that have been filled in. This will zero everything remaining in the
+ * biovec.
+ */
+void btrfs_clear_biovec_end(struct bio_vec *bvec, int vcnt,
+ unsigned long pg_index,
+ unsigned long pg_offset)
+{
+ while (pg_index < vcnt) {
+ struct page *page = bvec[pg_index].bv_page;
+ unsigned long off = bvec[pg_index].bv_offset;
+ unsigned long len = bvec[pg_index].bv_len;
+
+ if (pg_offset < off)
+ pg_offset = off;
+ if (pg_offset < off + len) {
+ unsigned long bytes = off + len - pg_offset;
+ char *kaddr;
+
+ kaddr = kmap_atomic(page);
+ memset(kaddr + pg_offset, 0, bytes);
+ kunmap_atomic(kaddr);
+ }
+ pg_index++;
+ pg_offset = 0;
+ }
+}
unsigned long nr_pages);
int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,
int mirror_num, unsigned long bio_flags);
-
+void btrfs_clear_biovec_end(struct bio_vec *bvec, int vcnt,
+ unsigned long pg_index,
+ unsigned long pg_offset);
struct btrfs_compress_op {
struct list_head *(*alloc_workspace)(void);
{
int i;
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
- /* lockdep really cares that we take all of these spinlocks
- * in the right order. If any of the locks in the path are not
- * currently blocking, it is going to complain. So, make really
- * really sure by forcing the path to blocking before we clear
- * the path blocking.
- */
if (held) {
btrfs_set_lock_blocking_rw(held, held_rw);
if (held_rw == BTRFS_WRITE_LOCK)
held_rw = BTRFS_READ_LOCK_BLOCKING;
}
btrfs_set_path_blocking(p);
-#endif
for (i = BTRFS_MAX_LEVEL - 1; i >= 0; i--) {
if (p->nodes[i] && p->locks[i]) {
}
}
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
if (held)
btrfs_clear_lock_blocking_rw(held, held_rw);
-#endif
}
/* this also releases the path */
}
p->locks[level] = BTRFS_WRITE_LOCK;
} else {
- err = btrfs_try_tree_read_lock(b);
+ err = btrfs_tree_read_lock_atomic(b);
if (!err) {
btrfs_set_path_blocking(p);
btrfs_tree_read_lock(b);
}
level = btrfs_header_level(b);
- err = btrfs_try_tree_read_lock(b);
+ err = btrfs_tree_read_lock_atomic(b);
if (!err) {
btrfs_set_path_blocking(p);
btrfs_tree_read_lock(b);
ret = 0;
fail:
while (ret < 0 && !list_empty(&tmplist)) {
- sums = list_entry(&tmplist, struct btrfs_ordered_sum, list);
+ sums = list_entry(tmplist.next, struct btrfs_ordered_sum, list);
list_del(&sums->list);
kfree(sums);
}
atomic_inc(&eb->spinning_readers);
}
+/*
+ * take a spinning read lock.
+ * returns 1 if we get the read lock and 0 if we don't
+ * this won't wait for blocking writers
+ */
+int btrfs_tree_read_lock_atomic(struct extent_buffer *eb)
+{
+ if (atomic_read(&eb->blocking_writers))
+ return 0;
+
+ read_lock(&eb->lock);
+ if (atomic_read(&eb->blocking_writers)) {
+ read_unlock(&eb->lock);
+ return 0;
+ }
+ atomic_inc(&eb->read_locks);
+ atomic_inc(&eb->spinning_readers);
+ return 1;
+}
+
/*
* returns 1 if we get the read lock and 0 if we don't
* this won't wait for blocking writers
atomic_read(&eb->blocking_readers))
return 0;
- if (!write_trylock(&eb->lock))
- return 0;
-
+ write_lock(&eb->lock);
if (atomic_read(&eb->blocking_writers) ||
atomic_read(&eb->blocking_readers)) {
write_unlock(&eb->lock);
void btrfs_assert_tree_locked(struct extent_buffer *eb);
int btrfs_try_tree_read_lock(struct extent_buffer *eb);
int btrfs_try_tree_write_lock(struct extent_buffer *eb);
+int btrfs_tree_read_lock_atomic(struct extent_buffer *eb);
+
static inline void btrfs_tree_unlock_rw(struct extent_buffer *eb, int rw)
{
}
done:
kunmap(pages_in[page_in_index]);
+ if (!ret)
+ btrfs_clear_biovec_end(bvec, vcnt, page_out_index, pg_offset);
return ret;
}
goto out;
}
+ /*
+ * the caller is already checking against PAGE_SIZE, but lets
+ * move this check closer to the memcpy/memset
+ */
+ destlen = min_t(unsigned long, destlen, PAGE_SIZE);
bytes = min_t(unsigned long, destlen, out_len - start_byte);
kaddr = kmap_atomic(dest_page);
memcpy(kaddr, workspace->buf + start_byte, bytes);
+
+ /*
+ * btrfs_getblock is doing a zero on the tail of the page too,
+ * but this will cover anything missing from the decompressed
+ * data.
+ */
+ if (bytes < destlen)
+ memset(kaddr+bytes, 0, destlen-bytes);
kunmap_atomic(kaddr);
out:
return ret;
zlib_inflateEnd(&workspace->strm);
if (data_in)
kunmap(pages_in[page_in_index]);
+ if (!ret)
+ btrfs_clear_biovec_end(bvec, vcnt, page_out_index, pg_offset);
return ret;
}
struct workspace *workspace = list_entry(ws, struct workspace, list);
int ret = 0;
int wbits = MAX_WBITS;
- unsigned long bytes_left = destlen;
+ unsigned long bytes_left;
unsigned long total_out = 0;
+ unsigned long pg_offset = 0;
char *kaddr;
+ destlen = min_t(unsigned long, destlen, PAGE_SIZE);
+ bytes_left = destlen;
+
workspace->strm.next_in = data_in;
workspace->strm.avail_in = srclen;
workspace->strm.total_in = 0;
unsigned long buf_start;
unsigned long buf_offset;
unsigned long bytes;
- unsigned long pg_offset = 0;
ret = zlib_inflate(&workspace->strm, Z_NO_FLUSH);
if (ret != Z_OK && ret != Z_STREAM_END)
ret = 0;
zlib_inflateEnd(&workspace->strm);
+
+ /*
+ * this should only happen if zlib returned fewer bytes than we
+ * expected. btrfs_get_block is responsible for zeroing from the
+ * end of the inline extent (destlen) to the end of the page
+ */
+ if (pg_offset < destlen) {
+ kaddr = kmap_atomic(dest_page);
+ memset(kaddr + pg_offset, 0, destlen - pg_offset);
+ kunmap_atomic(kaddr);
+ }
return ret;
}
for (i = 0; i < CEPH_CAP_BITS; i++)
if ((dirty & (1 << i)) &&
- flush_tid == ci->i_cap_flush_tid[i])
+ (u16)flush_tid == ci->i_cap_flush_tid[i])
cleaned |= 1 << i;
dout("handle_cap_flush_ack inode %p mds%d seq %d on %s cleaned %s,"
struct dentry *parent = lock_parent(dentry);
if (likely(!dentry->d_lockref.count)) {
__dentry_kill(dentry);
+ dput(parent);
goto restart;
}
if (parent)
}
alias = d_find_alias(inode);
- if (alias && !vfat_d_anon_disconn(alias)) {
+ /*
+ * Checking "alias->d_parent == dentry->d_parent" to make sure
+ * FS is not corrupted (especially double linked dir).
+ */
+ if (alias && alias->d_parent == dentry->d_parent &&
+ !vfat_d_anon_disconn(alias)) {
/*
* This inode has non anonymous-DCACHE_DISCONNECTED
* dentry. This means, the user did ->lookup() by an
out:
mutex_unlock(&MSDOS_SB(sb)->s_lock);
- dentry->d_time = dentry->d_parent->d_inode->i_version;
- dentry = d_splice_alias(inode, dentry);
- if (dentry)
- dentry->d_time = dentry->d_parent->d_inode->i_version;
- return dentry;
-
+ if (!inode)
+ dentry->d_time = dir->i_version;
+ return d_splice_alias(inode, dentry);
error:
mutex_unlock(&MSDOS_SB(sb)->s_lock);
return ERR_PTR(err);
inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
/* timestamp is already written, so mark_inode_dirty() is unneeded. */
- dentry->d_time = dentry->d_parent->d_inode->i_version;
d_instantiate(dentry, inode);
out:
mutex_unlock(&MSDOS_SB(sb)->s_lock);
clear_nlink(inode);
inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
fat_detach(inode);
+ dentry->d_time = dir->i_version;
out:
mutex_unlock(&MSDOS_SB(sb)->s_lock);
clear_nlink(inode);
inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
fat_detach(inode);
+ dentry->d_time = dir->i_version;
out:
mutex_unlock(&MSDOS_SB(sb)->s_lock);
inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
/* timestamp is already written, so mark_inode_dirty() is unneeded. */
- dentry->d_time = dentry->d_parent->d_inode->i_version;
d_instantiate(dentry, inode);
mutex_unlock(&MSDOS_SB(sb)->s_lock);
s32 sbsector;
};
-/*
- * Compute the hash for the isofs name corresponding to the dentry.
- */
-static int
-isofs_hash_common(struct qstr *qstr, int ms)
-{
- const char *name;
- int len;
-
- len = qstr->len;
- name = qstr->name;
- if (ms) {
- while (len && name[len-1] == '.')
- len--;
- }
-
- qstr->hash = full_name_hash(name, len);
-
- return 0;
-}
-
/*
* Compute the hash for the isofs name corresponding to the dentry.
*/
}
#ifdef CONFIG_JOLIET
+/*
+ * Compute the hash for the isofs name corresponding to the dentry.
+ */
+static int
+isofs_hash_common(struct qstr *qstr, int ms)
+{
+ const char *name;
+ int len;
+
+ len = qstr->len;
+ name = qstr->name;
+ if (ms) {
+ while (len && name[len-1] == '.')
+ len--;
+ }
+
+ qstr->hash = full_name_hash(name, len);
+
+ return 0;
+}
+
static int
isofs_hash_ms(const struct dentry *dentry, struct qstr *qstr)
{
journal->j_chksum_driver = NULL;
return 0;
}
- }
- /* Precompute checksum seed for all metadata */
- if (jbd2_journal_has_csum_v2or3(journal))
+ /* Precompute checksum seed for all metadata */
journal->j_csum_seed = jbd2_chksum(journal, ~0,
sb->s_uuid,
sizeof(sb->s_uuid));
+ }
}
/* If enabling v1 checksums, downgrade superblock */
loff_t offset = header->args.offset;
size_t count = header->args.count;
struct page **pages = header->args.pages;
- int pg_index = pg_index = header->args.pgbase >> PAGE_CACHE_SHIFT;
+ int pg_index = header->args.pgbase >> PAGE_CACHE_SHIFT;
unsigned int pg_len;
struct blk_plug plug;
int i;
dprintk("%s CREATING PIPEFS MESSAGE\n", __func__);
+ mutex_lock(&nn->bl_mutex);
bl_pipe_msg.bl_wq = &nn->bl_wq;
b->simple.len += 4; /* single volume */
if (b->simple.len > PAGE_SIZE)
- return -EIO;
+ goto out_unlock;
memset(msg, 0, sizeof(*msg));
msg->len = sizeof(*bl_msg) + b->simple.len;
msg->data = kzalloc(msg->len, gfp_mask);
if (!msg->data)
- goto out;
+ goto out_free_data;
bl_msg = msg->data;
bl_msg->type = BL_DEVICE_MOUNT,
rc = rpc_queue_upcall(nn->bl_device_pipe, msg);
if (rc < 0) {
remove_wait_queue(&nn->bl_wq, &wq);
- goto out;
+ goto out_free_data;
}
set_current_state(TASK_UNINTERRUPTIBLE);
if (reply->status != BL_DEVICE_REQUEST_PROC) {
printk(KERN_WARNING "%s failed to decode device: %d\n",
__func__, reply->status);
- goto out;
+ goto out_free_data;
}
dev = MKDEV(reply->major, reply->minor);
-out:
+out_free_data:
kfree(msg->data);
+out_unlock:
+ mutex_unlock(&nn->bl_mutex);
return dev;
}
struct nfs_net *nn = net_generic(net, nfs_net_id);
struct dentry *dentry;
+ mutex_init(&nn->bl_mutex);
init_waitqueue_head(&nn->bl_wq);
nn->bl_device_pipe = rpc_mkpipe_data(&bl_upcall_ops, 0);
if (IS_ERR(nn->bl_device_pipe))
continue;
if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
continue;
+ if (!nfs4_valid_open_stateid(state))
+ continue;
if (!nfs4_stateid_match(&state->stateid, stateid))
continue;
get_nfs_open_context(ctx);
{
int res = 0;
- res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid, issync);
+ if (!test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
+ res = nfs4_proc_delegreturn(inode,
+ delegation->cred,
+ &delegation->stateid,
+ issync);
nfs_free_delegation(delegation);
return res;
}
{
struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
struct nfs_inode *nfsi = NFS_I(inode);
- int err;
+ int err = 0;
if (delegation == NULL)
return 0;
do {
+ if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
+ break;
err = nfs_delegation_claim_opens(inode, &delegation->stateid);
if (!issync || err != -EAGAIN)
break;
rcu_read_unlock();
}
+static void nfs_revoke_delegation(struct inode *inode)
+{
+ struct nfs_delegation *delegation;
+ rcu_read_lock();
+ delegation = rcu_dereference(NFS_I(inode)->delegation);
+ if (delegation != NULL) {
+ set_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
+ nfs_mark_return_delegation(NFS_SERVER(inode), delegation);
+ }
+ rcu_read_unlock();
+}
+
void nfs_remove_bad_delegation(struct inode *inode)
{
struct nfs_delegation *delegation;
+ nfs_revoke_delegation(inode);
delegation = nfs_inode_detach_delegation(inode);
if (delegation) {
nfs_inode_find_state_and_recover(inode, &delegation->stateid);
NFS_DELEGATION_RETURN_IF_CLOSED,
NFS_DELEGATION_REFERENCED,
NFS_DELEGATION_RETURNING,
+ NFS_DELEGATION_REVOKED,
};
int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
case -ENOENT:
d_drop(dentry);
d_add(dentry, NULL);
+ nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
break;
case -EISDIR:
case -ENOTDIR:
{
struct nfs_direct_req *dreq = container_of(kref, struct nfs_direct_req, kref);
+ nfs_free_pnfs_ds_cinfo(&dreq->ds_cinfo);
if (dreq->l_ctx != NULL)
nfs_put_lock_context(dreq->l_ctx);
if (dreq->ctx != NULL)
case -NFS4ERR_DELEG_REVOKED:
case -NFS4ERR_ADMIN_REVOKED:
case -NFS4ERR_BAD_STATEID:
- if (state == NULL)
- break;
- nfs_remove_bad_delegation(state->inode);
case -NFS4ERR_OPENMODE:
if (state == NULL)
break;
{
struct inode *inode = dentry->d_inode;
int need_atime = NFS_I(inode)->cache_validity & NFS_INO_INVALID_ATIME;
- int err;
+ int err = 0;
trace_nfs_getattr_enter(inode);
/* Flush out writes to the server in order to update c/mtime. */
struct rpc_pipe *bl_device_pipe;
struct bl_dev_msg bl_mount_reply;
wait_queue_head_t bl_wq;
+ struct mutex bl_mutex;
struct list_head nfs_client_list;
struct list_head nfs_volume_list;
#if IS_ENABLED(CONFIG_NFS_V4)
case -NFS4ERR_DELEG_REVOKED:
case -NFS4ERR_ADMIN_REVOKED:
case -NFS4ERR_BAD_STATEID:
- if (inode != NULL && nfs4_have_delegation(inode, FMODE_READ)) {
- nfs_remove_bad_delegation(inode);
- exception->retry = 1;
- break;
- }
if (state == NULL)
break;
ret = nfs4_schedule_stateid_recovery(server, state);
nfs_inode_find_state_and_recover(state->inode,
stateid);
nfs4_schedule_stateid_recovery(server, state);
- return 0;
+ return -EAGAIN;
case -NFS4ERR_DELAY:
case -NFS4ERR_GRACE:
set_bit(NFS_DELEGATED_STATE, &state->flags);
return ret;
}
+static void nfs_finish_clear_delegation_stateid(struct nfs4_state *state)
+{
+ nfs_remove_bad_delegation(state->inode);
+ write_seqlock(&state->seqlock);
+ nfs4_stateid_copy(&state->stateid, &state->open_stateid);
+ write_sequnlock(&state->seqlock);
+ clear_bit(NFS_DELEGATED_STATE, &state->flags);
+}
+
+static void nfs40_clear_delegation_stateid(struct nfs4_state *state)
+{
+ if (rcu_access_pointer(NFS_I(state->inode)->delegation) != NULL)
+ nfs_finish_clear_delegation_stateid(state);
+}
+
+static int nfs40_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state)
+{
+ /* NFSv4.0 doesn't allow for delegation recovery on open expire */
+ nfs40_clear_delegation_stateid(state);
+ return nfs4_open_expired(sp, state);
+}
+
#if defined(CONFIG_NFS_V4_1)
-static void nfs41_clear_delegation_stateid(struct nfs4_state *state)
+static void nfs41_check_delegation_stateid(struct nfs4_state *state)
{
struct nfs_server *server = NFS_SERVER(state->inode);
- nfs4_stateid *stateid = &state->stateid;
+ nfs4_stateid stateid;
struct nfs_delegation *delegation;
- struct rpc_cred *cred = NULL;
- int status = -NFS4ERR_BAD_STATEID;
-
- /* If a state reset has been done, test_stateid is unneeded */
- if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
- return;
+ struct rpc_cred *cred;
+ int status;
/* Get the delegation credential for use by test/free_stateid */
rcu_read_lock();
delegation = rcu_dereference(NFS_I(state->inode)->delegation);
- if (delegation != NULL &&
- nfs4_stateid_match(&delegation->stateid, stateid)) {
- cred = get_rpccred(delegation->cred);
- rcu_read_unlock();
- status = nfs41_test_stateid(server, stateid, cred);
- trace_nfs4_test_delegation_stateid(state, NULL, status);
- } else
+ if (delegation == NULL) {
rcu_read_unlock();
+ return;
+ }
+
+ nfs4_stateid_copy(&stateid, &delegation->stateid);
+ cred = get_rpccred(delegation->cred);
+ rcu_read_unlock();
+ status = nfs41_test_stateid(server, &stateid, cred);
+ trace_nfs4_test_delegation_stateid(state, NULL, status);
if (status != NFS_OK) {
/* Free the stateid unless the server explicitly
* informs us the stateid is unrecognized. */
if (status != -NFS4ERR_BAD_STATEID)
- nfs41_free_stateid(server, stateid, cred);
- nfs_remove_bad_delegation(state->inode);
-
- write_seqlock(&state->seqlock);
- nfs4_stateid_copy(&state->stateid, &state->open_stateid);
- write_sequnlock(&state->seqlock);
- clear_bit(NFS_DELEGATED_STATE, &state->flags);
+ nfs41_free_stateid(server, &stateid, cred);
+ nfs_finish_clear_delegation_stateid(state);
}
- if (cred != NULL)
- put_rpccred(cred);
+ put_rpccred(cred);
}
/**
{
int status;
- nfs41_clear_delegation_stateid(state);
+ nfs41_check_delegation_stateid(state);
status = nfs41_check_open_stateid(state);
if (status != NFS_OK)
status = nfs4_open_expired(sp, state);
seq = raw_seqcount_begin(&sp->so_reclaim_seqcount);
ret = _nfs4_proc_open(opendata);
- if (ret != 0) {
- if (ret == -ENOENT) {
- dentry = opendata->dentry;
- if (dentry->d_inode)
- d_delete(dentry);
- else if (d_unhashed(dentry))
- d_add(dentry, NULL);
-
- nfs_set_verifier(dentry,
- nfs_save_change_attribute(opendata->dir->d_inode));
- }
+ if (ret != 0)
goto out;
- }
state = nfs4_opendata_to_nfs4_state(opendata);
ret = PTR_ERR(state);
case -NFS4ERR_DELEG_REVOKED:
case -NFS4ERR_ADMIN_REVOKED:
case -NFS4ERR_BAD_STATEID:
- if (state == NULL)
- break;
- nfs_remove_bad_delegation(state->inode);
case -NFS4ERR_OPENMODE:
if (state == NULL)
break;
static const struct nfs4_state_recovery_ops nfs40_nograce_recovery_ops = {
.owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE,
.state_flag_bit = NFS_STATE_RECLAIM_NOGRACE,
- .recover_open = nfs4_open_expired,
+ .recover_open = nfs40_open_expired,
.recover_lock = nfs4_lock_expired,
.establish_clid = nfs4_init_clientid,
};
| NFS_CAP_CHANGE_ATTR
| NFS_CAP_POSIX_LOCK
| NFS_CAP_STATEID_NFSV41
- | NFS_CAP_ATOMIC_OPEN_V1
- | NFS_CAP_SEEK,
+ | NFS_CAP_ATOMIC_OPEN_V1,
.init_client = nfs41_init_client,
.shutdown_client = nfs41_shutdown_client,
.match_stateid = nfs41_match_stateid,
| NFS_CAP_CHANGE_ATTR
| NFS_CAP_POSIX_LOCK
| NFS_CAP_STATEID_NFSV41
- | NFS_CAP_ATOMIC_OPEN_V1,
+ | NFS_CAP_ATOMIC_OPEN_V1
+ | NFS_CAP_SEEK,
.init_client = nfs41_init_client,
.shutdown_client = nfs41_shutdown_client,
.match_stateid = nfs41_match_stateid,
if (test_and_clear_bit(PG_INODE_REF, &req->wb_flags))
nfs_release_request(req);
- else
- WARN_ON_ONCE(1);
}
static void
{
if (test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) {
rpc_sleep_on(&clp->cl_cb_waitq, task, NULL);
- dprintk("%s slot is busy\n", __func__);
- return false;
+ /* Race breaker */
+ if (test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) {
+ dprintk("%s slot is busy\n", __func__);
+ return false;
+ }
+ rpc_wake_up_queued_task(&clp->cl_cb_waitq, task);
}
return true;
}
(NFSD4_SUPPORTED_ATTRS_WORD2 | FATTR4_WORD2_SUPPATTR_EXCLCREAT)
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
-#define NFSD4_2_SUPPORTED_ATTRS_WORD2 \
- (NFSD4_1_SUPPORTED_ATTRS_WORD2 | FATTR4_WORD2_SECURITY_LABEL)
+#define NFSD4_2_SECURITY_ATTRS FATTR4_WORD2_SECURITY_LABEL
#else
-#define NFSD4_2_SUPPORTED_ATTRS_WORD2 0
+#define NFSD4_2_SECURITY_ATTRS 0
#endif
+#define NFSD4_2_SUPPORTED_ATTRS_WORD2 \
+ (NFSD4_1_SUPPORTED_ATTRS_WORD2 | \
+ NFSD4_2_SECURITY_ATTRS)
+
static inline u32 nfsd_suppattrs0(u32 minorversion)
{
return minorversion ? NFSD4_1_SUPPORTED_ATTRS_WORD0
&fsnotify_mark_srcu);
}
+ /*
+ * We need to merge inode & vfsmount mark lists so that inode mark
+ * ignore masks are properly reflected for mount mark notifications.
+ * That's why this traversal is so complicated...
+ */
while (inode_node || vfsmount_node) {
- inode_group = vfsmount_group = NULL;
+ inode_group = NULL;
+ inode_mark = NULL;
+ vfsmount_group = NULL;
+ vfsmount_mark = NULL;
if (inode_node) {
inode_mark = hlist_entry(srcu_dereference(inode_node, &fsnotify_mark_srcu),
vfsmount_group = vfsmount_mark->group;
}
- if (inode_group > vfsmount_group) {
- /* handle inode */
- ret = send_to_group(to_tell, inode_mark, NULL, mask,
- data, data_is, cookie, file_name);
- /* we didn't use the vfsmount_mark */
- vfsmount_group = NULL;
- } else if (vfsmount_group > inode_group) {
- ret = send_to_group(to_tell, NULL, vfsmount_mark, mask,
- data, data_is, cookie, file_name);
- inode_group = NULL;
- } else {
- ret = send_to_group(to_tell, inode_mark, vfsmount_mark,
- mask, data, data_is, cookie,
- file_name);
+ if (inode_group && vfsmount_group) {
+ int cmp = fsnotify_compare_groups(inode_group,
+ vfsmount_group);
+ if (cmp > 0) {
+ inode_group = NULL;
+ inode_mark = NULL;
+ } else if (cmp < 0) {
+ vfsmount_group = NULL;
+ vfsmount_mark = NULL;
+ }
}
+ ret = send_to_group(to_tell, inode_mark, vfsmount_mark, mask,
+ data, data_is, cookie, file_name);
if (ret && (mask & ALL_FSNOTIFY_PERM_EVENTS))
goto out;
/* protects reads of inode and vfsmount marks list */
extern struct srcu_struct fsnotify_mark_srcu;
+/* compare two groups for sorting of marks lists */
+extern int fsnotify_compare_groups(struct fsnotify_group *a,
+ struct fsnotify_group *b);
+
extern void fsnotify_set_inode_mark_mask_locked(struct fsnotify_mark *fsn_mark,
__u32 mask);
/* add a mark to an inode */
{
struct fsnotify_mark *lmark, *last = NULL;
int ret = 0;
+ int cmp;
mark->flags |= FSNOTIFY_MARK_FLAG_INODE;
goto out;
}
- if (mark->group->priority < lmark->group->priority)
- continue;
-
- if ((mark->group->priority == lmark->group->priority) &&
- (mark->group < lmark->group))
+ cmp = fsnotify_compare_groups(lmark->group, mark->group);
+ if (cmp < 0)
continue;
hlist_add_before_rcu(&mark->i.i_list, &lmark->i.i_list);
mark->ignored_mask = mask;
}
+/*
+ * Sorting function for lists of fsnotify marks.
+ *
+ * Fanotify supports different notification classes (reflected as priority of
+ * notification group). Events shall be passed to notification groups in
+ * decreasing priority order. To achieve this marks in notification lists for
+ * inodes and vfsmounts are sorted so that priorities of corresponding groups
+ * are descending.
+ *
+ * Furthermore correct handling of the ignore mask requires processing inode
+ * and vfsmount marks of each group together. Using the group address as
+ * further sort criterion provides a unique sorting order and thus we can
+ * merge inode and vfsmount lists of marks in linear time and find groups
+ * present in both lists.
+ *
+ * A return value of 1 signifies that b has priority over a.
+ * A return value of 0 signifies that the two marks have to be handled together.
+ * A return value of -1 signifies that a has priority over b.
+ */
+int fsnotify_compare_groups(struct fsnotify_group *a, struct fsnotify_group *b)
+{
+ if (a == b)
+ return 0;
+ if (!a)
+ return 1;
+ if (!b)
+ return -1;
+ if (a->priority < b->priority)
+ return 1;
+ if (a->priority > b->priority)
+ return -1;
+ if (a < b)
+ return 1;
+ return -1;
+}
+
/*
* Attach an initialized mark to a given group and fs object.
* These marks may be used for the fsnotify backend to determine which
struct mount *m = real_mount(mnt);
struct fsnotify_mark *lmark, *last = NULL;
int ret = 0;
+ int cmp;
mark->flags |= FSNOTIFY_MARK_FLAG_VFSMOUNT;
goto out;
}
- if (mark->group->priority < lmark->group->priority)
- continue;
-
- if ((mark->group->priority == lmark->group->priority) &&
- (mark->group < lmark->group))
+ cmp = fsnotify_compare_groups(lmark->group, mark->group);
+ if (cmp < 0)
continue;
hlist_add_before_rcu(&mark->m.m_list, &lmark->m.m_list);
size_t veclen, size_t total)
{
int ret;
- struct msghdr msg;
+ struct msghdr msg = {.msg_flags = 0,};
if (sock == NULL) {
ret = -EINVAL;
-config OVERLAYFS_FS
+config OVERLAY_FS
tristate "Overlay filesystem support"
help
An overlay filesystem combines two filesystems - an 'upper' filesystem
# Makefile for the overlay filesystem.
#
-obj-$(CONFIG_OVERLAYFS_FS) += overlayfs.o
+obj-$(CONFIG_OVERLAY_FS) += overlay.o
-overlayfs-objs := super.o inode.o dir.o readdir.o copy_up.o
+overlay-objs := super.o inode.o dir.o readdir.o copy_up.o
return ERR_PTR(err);
}
-static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry,
- enum ovl_path_type type)
+static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry)
{
int err;
struct dentry *ret = NULL;
err = ovl_check_empty_dir(dentry, &list);
if (err)
ret = ERR_PTR(err);
- else if (type == OVL_PATH_MERGE)
- ret = ovl_clear_empty(dentry, &list);
+ else {
+ /*
+ * If no upperdentry then skip clearing whiteouts.
+ *
+ * Can race with copy-up, since we don't hold the upperdir
+ * mutex. Doesn't matter, since copy-up can't create a
+ * non-empty directory from an empty one.
+ */
+ if (ovl_dentry_upper(dentry))
+ ret = ovl_clear_empty(dentry, &list);
+ }
ovl_cache_free(&list);
return err;
}
-static int ovl_remove_and_whiteout(struct dentry *dentry,
- enum ovl_path_type type, bool is_dir)
+static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir)
{
struct dentry *workdir = ovl_workdir(dentry);
struct inode *wdir = workdir->d_inode;
int err;
if (is_dir) {
- opaquedir = ovl_check_empty_and_clear(dentry, type);
+ opaquedir = ovl_check_empty_and_clear(dentry);
err = PTR_ERR(opaquedir);
if (IS_ERR(opaquedir))
goto out;
if (IS_ERR(whiteout))
goto out_unlock;
- if (type == OVL_PATH_LOWER) {
+ upper = ovl_dentry_upper(dentry);
+ if (!upper) {
upper = lookup_one_len(dentry->d_name.name, upperdir,
- dentry->d_name.len);
+ dentry->d_name.len);
err = PTR_ERR(upper);
if (IS_ERR(upper))
goto kill_whiteout;
} else {
int flags = 0;
- upper = ovl_dentry_upper(dentry);
if (opaquedir)
upper = opaquedir;
err = -ESTALE;
cap_raise(override_cred->cap_effective, CAP_CHOWN);
old_cred = override_creds(override_cred);
- err = ovl_remove_and_whiteout(dentry, type, is_dir);
+ err = ovl_remove_and_whiteout(dentry, is_dir);
revert_creds(old_cred);
put_cred(override_cred);
}
if (overwrite && (new_type == OVL_PATH_LOWER || new_type == OVL_PATH_MERGE) && new_is_dir) {
- opaquedir = ovl_check_empty_and_clear(new, new_type);
+ opaquedir = ovl_check_empty_and_clear(new);
err = PTR_ERR(opaquedir);
if (IS_ERR(opaquedir)) {
opaquedir = NULL;
return err;
}
+static bool ovl_need_xattr_filter(struct dentry *dentry,
+ enum ovl_path_type type)
+{
+ return type == OVL_PATH_UPPER && S_ISDIR(dentry->d_inode->i_mode);
+}
+
ssize_t ovl_getxattr(struct dentry *dentry, const char *name,
void *value, size_t size)
{
- if (ovl_path_type(dentry->d_parent) == OVL_PATH_MERGE &&
- ovl_is_private_xattr(name))
+ struct path realpath;
+ enum ovl_path_type type = ovl_path_real(dentry, &realpath);
+
+ if (ovl_need_xattr_filter(dentry, type) && ovl_is_private_xattr(name))
return -ENODATA;
- return vfs_getxattr(ovl_dentry_real(dentry), name, value, size);
+ return vfs_getxattr(realpath.dentry, name, value, size);
}
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
{
+ struct path realpath;
+ enum ovl_path_type type = ovl_path_real(dentry, &realpath);
ssize_t res;
int off;
- res = vfs_listxattr(ovl_dentry_real(dentry), list, size);
+ res = vfs_listxattr(realpath.dentry, list, size);
if (res <= 0 || size == 0)
return res;
- if (ovl_path_type(dentry->d_parent) != OVL_PATH_MERGE)
+ if (!ovl_need_xattr_filter(dentry, type))
return res;
/* filter out private xattrs */
{
int err;
struct path realpath;
- enum ovl_path_type type;
+ enum ovl_path_type type = ovl_path_real(dentry, &realpath);
err = ovl_want_write(dentry);
if (err)
goto out;
- if (ovl_path_type(dentry->d_parent) == OVL_PATH_MERGE &&
- ovl_is_private_xattr(name))
+ err = -ENODATA;
+ if (ovl_need_xattr_filter(dentry, type) && ovl_is_private_xattr(name))
goto out_drop_write;
- type = ovl_path_real(dentry, &realpath);
if (type == OVL_PATH_LOWER) {
err = vfs_getxattr(realpath.dentry, name, NULL, 0);
if (err < 0)
{
struct ovl_dir_cache *cache = od->cache;
- list_del(&od->cursor.l_node);
+ list_del_init(&od->cursor.l_node);
WARN_ON(cache->refcount <= 0);
cache->refcount--;
if (!cache->refcount) {
return 0;
}
-static inline int ovl_dir_read_merged(struct path *upperpath,
- struct path *lowerpath,
- struct list_head *list)
+static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list)
{
int err;
+ struct path lowerpath;
+ struct path upperpath;
struct ovl_readdir_data rdd = {
.ctx.actor = ovl_fill_merge,
.list = list,
.is_merge = false,
};
- if (upperpath->dentry) {
- err = ovl_dir_read(upperpath, &rdd);
+ ovl_path_lower(dentry, &lowerpath);
+ ovl_path_upper(dentry, &upperpath);
+
+ if (upperpath.dentry) {
+ err = ovl_dir_read(&upperpath, &rdd);
if (err)
goto out;
- if (lowerpath->dentry) {
- err = ovl_dir_mark_whiteouts(upperpath->dentry, &rdd);
+ if (lowerpath.dentry) {
+ err = ovl_dir_mark_whiteouts(upperpath.dentry, &rdd);
if (err)
goto out;
}
}
- if (lowerpath->dentry) {
+ if (lowerpath.dentry) {
/*
* Insert lowerpath entries before upperpath ones, this allows
* offsets to be reasonably constant
*/
list_add(&rdd.middle, rdd.list);
rdd.is_merge = true;
- err = ovl_dir_read(lowerpath, &rdd);
+ err = ovl_dir_read(&lowerpath, &rdd);
list_del(&rdd.middle);
}
out:
static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry)
{
int res;
- struct path lowerpath;
- struct path upperpath;
struct ovl_dir_cache *cache;
cache = ovl_dir_cache(dentry);
cache->refcount = 1;
INIT_LIST_HEAD(&cache->entries);
- ovl_path_lower(dentry, &lowerpath);
- ovl_path_upper(dentry, &upperpath);
-
- res = ovl_dir_read_merged(&upperpath, &lowerpath, &cache->entries);
+ res = ovl_dir_read_merged(dentry, &cache->entries);
if (res) {
ovl_cache_free(&cache->entries);
kfree(cache);
/*
* Need to check if we started out being a lower dir, but got copied up
*/
- if (!od->is_upper && ovl_path_type(dentry) == OVL_PATH_MERGE) {
+ if (!od->is_upper && ovl_path_type(dentry) != OVL_PATH_LOWER) {
struct inode *inode = file_inode(file);
- realfile =lockless_dereference(od->upperfile);
+ realfile = lockless_dereference(od->upperfile);
if (!realfile) {
struct path upperpath;
int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list)
{
int err;
- struct path lowerpath;
- struct path upperpath;
struct ovl_cache_entry *p;
- ovl_path_upper(dentry, &upperpath);
- ovl_path_lower(dentry, &lowerpath);
-
- err = ovl_dir_read_merged(&upperpath, &lowerpath, list);
+ err = ovl_dir_read_merged(dentry, list);
if (err)
return err;
MODULE_DESCRIPTION("Overlay filesystem");
MODULE_LICENSE("GPL");
-#define OVERLAYFS_SUPER_MAGIC 0x794c764f
+#define OVERLAYFS_SUPER_MAGIC 0x794c7630
struct ovl_config {
char *lowerdir;
static struct dentry *ovl_upperdentry_dereference(struct ovl_entry *oe)
{
- struct dentry *upperdentry = ACCESS_ONCE(oe->__upperdentry);
- /*
- * Make sure to order reads to upperdentry wrt ovl_dentry_update()
- */
- smp_read_barrier_depends();
- return upperdentry;
+ return lockless_dereference(oe->__upperdentry);
}
void ovl_path_upper(struct dentry *dentry, struct path *path)
{OPT_ERR, NULL}
};
+static char *ovl_next_opt(char **s)
+{
+ char *sbegin = *s;
+ char *p;
+
+ if (sbegin == NULL)
+ return NULL;
+
+ for (p = sbegin; *p; p++) {
+ if (*p == '\\') {
+ p++;
+ if (!*p)
+ break;
+ } else if (*p == ',') {
+ *p = '\0';
+ *s = p + 1;
+ return sbegin;
+ }
+ }
+ *s = NULL;
+ return sbegin;
+}
+
static int ovl_parse_opt(char *opt, struct ovl_config *config)
{
char *p;
- while ((p = strsep(&opt, ",")) != NULL) {
+ while ((p = ovl_next_opt(&opt)) != NULL) {
int token;
substring_t args[MAX_OPT_ARGS];
goto out_unlock;
}
+static void ovl_unescape(char *s)
+{
+ char *d = s;
+
+ for (;; s++, d++) {
+ if (*s == '\\')
+ s++;
+ *d = *s;
+ if (!*s)
+ break;
+ }
+}
+
static int ovl_mount_dir(const char *name, struct path *path)
{
int err;
+ char *tmp = kstrdup(name, GFP_KERNEL);
+
+ if (!tmp)
+ return -ENOMEM;
- err = kern_path(name, LOOKUP_FOLLOW, path);
+ ovl_unescape(tmp);
+ err = kern_path(tmp, LOOKUP_FOLLOW, path);
if (err) {
- pr_err("overlayfs: failed to resolve '%s': %i\n", name, err);
+ pr_err("overlayfs: failed to resolve '%s': %i\n", tmp, err);
err = -EINVAL;
}
+ kfree(tmp);
return err;
}
static struct file_system_type ovl_fs_type = {
.owner = THIS_MODULE,
- .name = "overlayfs",
+ .name = "overlay",
.mount = ovl_mount,
.kill_sb = kill_anon_super,
};
-MODULE_ALIAS_FS("overlayfs");
+MODULE_ALIAS_FS("overlay");
static int __init ovl_init(void)
{
goto out;
}
-
+/*
+ * Preallocate and zero a range of a file. This mechanism has the allocation
+ * semantics of fallocate and in addition converts data in the range to zeroes.
+ */
int
xfs_zero_file_space(
struct xfs_inode *ip,
xfs_off_t len)
{
struct xfs_mount *mp = ip->i_mount;
- uint granularity;
- xfs_off_t start_boundary;
- xfs_off_t end_boundary;
+ uint blksize;
int error;
trace_xfs_zero_file_space(ip);
- granularity = max_t(uint, 1 << mp->m_sb.sb_blocklog, PAGE_CACHE_SIZE);
+ blksize = 1 << mp->m_sb.sb_blocklog;
/*
- * Round the range of extents we are going to convert inwards. If the
- * offset is aligned, then it doesn't get changed so we zero from the
- * start of the block offset points to.
+ * Punch a hole and prealloc the range. We use hole punch rather than
+ * unwritten extent conversion for two reasons:
+ *
+ * 1.) Hole punch handles partial block zeroing for us.
+ *
+ * 2.) If prealloc returns ENOSPC, the file range is still zero-valued
+ * by virtue of the hole punch.
*/
- start_boundary = round_up(offset, granularity);
- end_boundary = round_down(offset + len, granularity);
-
- ASSERT(start_boundary >= offset);
- ASSERT(end_boundary <= offset + len);
-
- if (start_boundary < end_boundary - 1) {
- /*
- * Writeback the range to ensure any inode size updates due to
- * appending writes make it to disk (otherwise we could just
- * punch out the delalloc blocks).
- */
- error = filemap_write_and_wait_range(VFS_I(ip)->i_mapping,
- start_boundary, end_boundary - 1);
- if (error)
- goto out;
- truncate_pagecache_range(VFS_I(ip), start_boundary,
- end_boundary - 1);
-
- /* convert the blocks */
- error = xfs_alloc_file_space(ip, start_boundary,
- end_boundary - start_boundary - 1,
- XFS_BMAPI_PREALLOC | XFS_BMAPI_CONVERT);
- if (error)
- goto out;
-
- /* We've handled the interior of the range, now for the edges */
- if (start_boundary != offset) {
- error = xfs_iozero(ip, offset, start_boundary - offset);
- if (error)
- goto out;
- }
-
- if (end_boundary != offset + len)
- error = xfs_iozero(ip, end_boundary,
- offset + len - end_boundary);
-
- } else {
- /*
- * It's either a sub-granularity range or the range spanned lies
- * partially across two adjacent blocks.
- */
- error = xfs_iozero(ip, offset, len);
- }
+ error = xfs_free_file_space(ip, offset, len);
+ if (error)
+ goto out;
+ error = xfs_alloc_file_space(ip, round_down(offset, blksize),
+ round_up(offset + len, blksize) -
+ round_down(offset, blksize),
+ XFS_BMAPI_PREALLOC);
out:
return error;
XFS_WANT_CORRUPTED_RETURN(stat == 1);
/* Check if the record contains the inode in request */
- if (irec->ir_startino + XFS_INODES_PER_CHUNK <= agino)
- return -EINVAL;
+ if (irec->ir_startino + XFS_INODES_PER_CHUNK <= agino) {
+ *icount = 0;
+ return 0;
+ }
idx = agino - irec->ir_startino + 1;
if (idx < XFS_INODES_PER_CHUNK &&
#define XFS_BULKSTAT_UBLEFT(ubleft) ((ubleft) >= statstruct_size)
+struct xfs_bulkstat_agichunk {
+ char __user **ac_ubuffer;/* pointer into user's buffer */
+ int ac_ubleft; /* bytes left in user's buffer */
+ int ac_ubelem; /* spaces used in user's buffer */
+};
+
/*
* Process inodes in chunk with a pointer to a formatter function
* that will iget the inode and fill in the appropriate structure.
*/
-int
+static int
xfs_bulkstat_ag_ichunk(
struct xfs_mount *mp,
xfs_agnumber_t agno,
struct xfs_inobt_rec_incore *irbp,
bulkstat_one_pf formatter,
size_t statstruct_size,
- struct xfs_bulkstat_agichunk *acp)
+ struct xfs_bulkstat_agichunk *acp,
+ xfs_agino_t *last_agino)
{
- xfs_ino_t lastino = acp->ac_lastino;
char __user **ubufp = acp->ac_ubuffer;
- int ubleft = acp->ac_ubleft;
- int ubelem = acp->ac_ubelem;
- int chunkidx, clustidx;
+ int chunkidx;
int error = 0;
- xfs_agino_t agino;
+ xfs_agino_t agino = irbp->ir_startino;
- for (agino = irbp->ir_startino, chunkidx = clustidx = 0;
- XFS_BULKSTAT_UBLEFT(ubleft) &&
- irbp->ir_freecount < XFS_INODES_PER_CHUNK;
- chunkidx++, clustidx++, agino++) {
- int fmterror; /* bulkstat formatter result */
+ for (chunkidx = 0; chunkidx < XFS_INODES_PER_CHUNK;
+ chunkidx++, agino++) {
+ int fmterror;
int ubused;
- xfs_ino_t ino = XFS_AGINO_TO_INO(mp, agno, agino);
- ASSERT(chunkidx < XFS_INODES_PER_CHUNK);
+ /* inode won't fit in buffer, we are done */
+ if (acp->ac_ubleft < statstruct_size)
+ break;
/* Skip if this inode is free */
- if (XFS_INOBT_MASK(chunkidx) & irbp->ir_free) {
- lastino = ino;
+ if (XFS_INOBT_MASK(chunkidx) & irbp->ir_free)
continue;
- }
-
- /*
- * Count used inodes as free so we can tell when the
- * chunk is used up.
- */
- irbp->ir_freecount++;
/* Get the inode and fill in a single buffer */
ubused = statstruct_size;
- error = formatter(mp, ino, *ubufp, ubleft, &ubused, &fmterror);
- if (fmterror == BULKSTAT_RV_NOTHING) {
- if (error && error != -ENOENT && error != -EINVAL) {
- ubleft = 0;
- break;
- }
- lastino = ino;
- continue;
- }
- if (fmterror == BULKSTAT_RV_GIVEUP) {
- ubleft = 0;
+ error = formatter(mp, XFS_AGINO_TO_INO(mp, agno, agino),
+ *ubufp, acp->ac_ubleft, &ubused, &fmterror);
+
+ if (fmterror == BULKSTAT_RV_GIVEUP ||
+ (error && error != -ENOENT && error != -EINVAL)) {
+ acp->ac_ubleft = 0;
ASSERT(error);
break;
}
- if (*ubufp)
- *ubufp += ubused;
- ubleft -= ubused;
- ubelem++;
- lastino = ino;
+
+ /* be careful not to leak error if at end of chunk */
+ if (fmterror == BULKSTAT_RV_NOTHING || error) {
+ error = 0;
+ continue;
+ }
+
+ *ubufp += ubused;
+ acp->ac_ubleft -= ubused;
+ acp->ac_ubelem++;
}
- acp->ac_lastino = lastino;
- acp->ac_ubleft = ubleft;
- acp->ac_ubelem = ubelem;
+ /*
+ * Post-update *last_agino. At this point, agino will always point one
+ * inode past the last inode we processed successfully. Hence we
+ * substract that inode when setting the *last_agino cursor so that we
+ * return the correct cookie to userspace. On the next bulkstat call,
+ * the inode under the lastino cookie will be skipped as we have already
+ * processed it here.
+ */
+ *last_agino = agino - 1;
return error;
}
xfs_agino_t agino; /* inode # in allocation group */
xfs_agnumber_t agno; /* allocation group number */
xfs_btree_cur_t *cur; /* btree cursor for ialloc btree */
- int end_of_ag; /* set if we've seen the ag end */
- int error; /* error code */
- int fmterror;/* bulkstat formatter result */
- int i; /* loop index */
- int icount; /* count of inodes good in irbuf */
size_t irbsize; /* size of irec buffer in bytes */
- xfs_ino_t ino; /* inode number (filesystem) */
- xfs_inobt_rec_incore_t *irbp; /* current irec buffer pointer */
xfs_inobt_rec_incore_t *irbuf; /* start of irec buffer */
- xfs_inobt_rec_incore_t *irbufend; /* end of good irec buffer entries */
- xfs_ino_t lastino; /* last inode number returned */
int nirbuf; /* size of irbuf */
- int rval; /* return value error code */
- int tmp; /* result value from btree calls */
int ubcount; /* size of user's buffer */
- int ubleft; /* bytes left in user's buffer */
- char __user *ubufp; /* pointer into user's buffer */
- int ubelem; /* spaces used in user's buffer */
+ struct xfs_bulkstat_agichunk ac;
+ int error = 0;
/*
* Get the last inode value, see if there's nothing to do.
*/
- ino = (xfs_ino_t)*lastinop;
- lastino = ino;
- agno = XFS_INO_TO_AGNO(mp, ino);
- agino = XFS_INO_TO_AGINO(mp, ino);
+ agno = XFS_INO_TO_AGNO(mp, *lastinop);
+ agino = XFS_INO_TO_AGINO(mp, *lastinop);
if (agno >= mp->m_sb.sb_agcount ||
- ino != XFS_AGINO_TO_INO(mp, agno, agino)) {
+ *lastinop != XFS_AGINO_TO_INO(mp, agno, agino)) {
*done = 1;
*ubcountp = 0;
return 0;
}
ubcount = *ubcountp; /* statstruct's */
- ubleft = ubcount * statstruct_size; /* bytes */
- *ubcountp = ubelem = 0;
+ ac.ac_ubuffer = &ubuffer;
+ ac.ac_ubleft = ubcount * statstruct_size; /* bytes */;
+ ac.ac_ubelem = 0;
+
+ *ubcountp = 0;
*done = 0;
- fmterror = 0;
- ubufp = ubuffer;
+
irbuf = kmem_zalloc_greedy(&irbsize, PAGE_SIZE, PAGE_SIZE * 4);
if (!irbuf)
return -ENOMEM;
* Loop over the allocation groups, starting from the last
* inode returned; 0 means start of the allocation group.
*/
- rval = 0;
- while (XFS_BULKSTAT_UBLEFT(ubleft) && agno < mp->m_sb.sb_agcount) {
- cond_resched();
+ while (agno < mp->m_sb.sb_agcount) {
+ struct xfs_inobt_rec_incore *irbp = irbuf;
+ struct xfs_inobt_rec_incore *irbufend = irbuf + nirbuf;
+ bool end_of_ag = false;
+ int icount = 0;
+ int stat;
+
error = xfs_ialloc_read_agi(mp, NULL, agno, &agbp);
if (error)
break;
*/
cur = xfs_inobt_init_cursor(mp, NULL, agbp, agno,
XFS_BTNUM_INO);
- irbp = irbuf;
- irbufend = irbuf + nirbuf;
- end_of_ag = 0;
- icount = 0;
if (agino > 0) {
/*
* In the middle of an allocation group, we need to get
error = xfs_bulkstat_grab_ichunk(cur, agino, &icount, &r);
if (error)
- break;
+ goto del_cursor;
if (icount) {
irbp->ir_startino = r.ir_startino;
irbp->ir_freecount = r.ir_freecount;
irbp->ir_free = r.ir_free;
irbp++;
- agino = r.ir_startino + XFS_INODES_PER_CHUNK;
}
/* Increment to the next record */
- error = xfs_btree_increment(cur, 0, &tmp);
+ error = xfs_btree_increment(cur, 0, &stat);
} else {
/* Start of ag. Lookup the first inode chunk */
- error = xfs_inobt_lookup(cur, 0, XFS_LOOKUP_GE, &tmp);
+ error = xfs_inobt_lookup(cur, 0, XFS_LOOKUP_GE, &stat);
+ }
+ if (error || stat == 0) {
+ end_of_ag = true;
+ goto del_cursor;
}
- if (error)
- break;
/*
* Loop through inode btree records in this ag,
while (irbp < irbufend && icount < ubcount) {
struct xfs_inobt_rec_incore r;
- error = xfs_inobt_get_rec(cur, &r, &i);
- if (error || i == 0) {
- end_of_ag = 1;
- break;
+ error = xfs_inobt_get_rec(cur, &r, &stat);
+ if (error || stat == 0) {
+ end_of_ag = true;
+ goto del_cursor;
}
/*
irbp++;
icount += XFS_INODES_PER_CHUNK - r.ir_freecount;
}
- /*
- * Set agino to after this chunk and bump the cursor.
- */
- agino = r.ir_startino + XFS_INODES_PER_CHUNK;
- error = xfs_btree_increment(cur, 0, &tmp);
+ error = xfs_btree_increment(cur, 0, &stat);
+ if (error || stat == 0) {
+ end_of_ag = true;
+ goto del_cursor;
+ }
cond_resched();
}
+
/*
- * Drop the btree buffers and the agi buffer.
- * We can't hold any of the locks these represent
- * when calling iget.
+ * Drop the btree buffers and the agi buffer as we can't hold any
+ * of the locks these represent when calling iget. If there is a
+ * pending error, then we are done.
*/
+del_cursor:
xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
xfs_buf_relse(agbp);
+ if (error)
+ break;
/*
- * Now format all the good inodes into the user's buffer.
+ * Now format all the good inodes into the user's buffer. The
+ * call to xfs_bulkstat_ag_ichunk() sets up the agino pointer
+ * for the next loop iteration.
*/
irbufend = irbp;
for (irbp = irbuf;
- irbp < irbufend && XFS_BULKSTAT_UBLEFT(ubleft); irbp++) {
- struct xfs_bulkstat_agichunk ac;
-
- ac.ac_lastino = lastino;
- ac.ac_ubuffer = &ubuffer;
- ac.ac_ubleft = ubleft;
- ac.ac_ubelem = ubelem;
+ irbp < irbufend && ac.ac_ubleft >= statstruct_size;
+ irbp++) {
error = xfs_bulkstat_ag_ichunk(mp, agno, irbp,
- formatter, statstruct_size, &ac);
+ formatter, statstruct_size, &ac,
+ &agino);
if (error)
- rval = error;
-
- lastino = ac.ac_lastino;
- ubleft = ac.ac_ubleft;
- ubelem = ac.ac_ubelem;
+ break;
cond_resched();
}
+
/*
- * Set up for the next loop iteration.
+ * If we've run out of space or had a formatting error, we
+ * are now done
*/
- if (XFS_BULKSTAT_UBLEFT(ubleft)) {
- if (end_of_ag) {
- agno++;
- agino = 0;
- } else
- agino = XFS_INO_TO_AGINO(mp, lastino);
- } else
+ if (ac.ac_ubleft < statstruct_size || error)
break;
+
+ if (end_of_ag) {
+ agno++;
+ agino = 0;
+ }
}
/*
* Done, we're either out of filesystem or space to put the data.
*/
kmem_free(irbuf);
- *ubcountp = ubelem;
+ *ubcountp = ac.ac_ubelem;
+
/*
- * Found some inodes, return them now and return the error next time.
+ * We found some inodes, so clear the error status and return them.
+ * The lastino pointer will point directly at the inode that triggered
+ * any error that occurred, so on the next call the error will be
+ * triggered again and propagated to userspace as there will be no
+ * formatted inodes in the buffer.
*/
- if (ubelem)
- rval = 0;
- if (agno >= mp->m_sb.sb_agcount) {
- /*
- * If we ran out of filesystem, mark lastino as off
- * the end of the filesystem, so the next call
- * will return immediately.
- */
- *lastinop = (xfs_ino_t)XFS_AGINO_TO_INO(mp, agno, 0);
+ if (ac.ac_ubelem)
+ error = 0;
+
+ /*
+ * If we ran out of filesystem, lastino will point off the end of
+ * the filesystem so the next call will return immediately.
+ */
+ *lastinop = XFS_AGINO_TO_INO(mp, agno, agino);
+ if (agno >= mp->m_sb.sb_agcount)
*done = 1;
- } else
- *lastinop = (xfs_ino_t)lastino;
- return rval;
+ return error;
}
int
int *ubused,
int *stat);
-struct xfs_bulkstat_agichunk {
- xfs_ino_t ac_lastino; /* last inode returned */
- char __user **ac_ubuffer;/* pointer into user's buffer */
- int ac_ubleft; /* bytes left in user's buffer */
- int ac_ubelem; /* spaces used in user's buffer */
-};
-
-int
-xfs_bulkstat_ag_ichunk(
- struct xfs_mount *mp,
- xfs_agnumber_t agno,
- struct xfs_inobt_rec_incore *irbp,
- bulkstat_one_pf formatter,
- size_t statstruct_size,
- struct xfs_bulkstat_agichunk *acp);
-
/*
* Values for stat return value.
*/
#include <linux/uaccess.h>
#include <asm/errno.h>
+#ifndef CONFIG_SMP
+/*
+ * The following implementation only for uniprocessor machines.
+ * For UP, it's relies on the fact that pagefault_disable() also disables
+ * preemption to ensure mutual exclusion.
+ *
+ */
+
+/**
+ * futex_atomic_op_inuser() - Atomic arithmetic operation with constant
+ * argument and comparison of the previous
+ * futex value with another constant.
+ *
+ * @encoded_op: encoded operation to execute
+ * @uaddr: pointer to user space address
+ *
+ * Return:
+ * 0 - On success
+ * <0 - On error
+ */
+static inline int
+futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr)
+{
+ int op = (encoded_op >> 28) & 7;
+ int cmp = (encoded_op >> 24) & 15;
+ int oparg = (encoded_op << 8) >> 20;
+ int cmparg = (encoded_op << 20) >> 20;
+ int oldval, ret;
+ u32 tmp;
+
+ if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
+ oparg = 1 << oparg;
+
+ pagefault_disable();
+
+ ret = -EFAULT;
+ if (unlikely(get_user(oldval, uaddr) != 0))
+ goto out_pagefault_enable;
+
+ ret = 0;
+ tmp = oldval;
+
+ switch (op) {
+ case FUTEX_OP_SET:
+ tmp = oparg;
+ break;
+ case FUTEX_OP_ADD:
+ tmp += oparg;
+ break;
+ case FUTEX_OP_OR:
+ tmp |= oparg;
+ break;
+ case FUTEX_OP_ANDN:
+ tmp &= ~oparg;
+ break;
+ case FUTEX_OP_XOR:
+ tmp ^= oparg;
+ break;
+ default:
+ ret = -ENOSYS;
+ }
+
+ if (ret == 0 && unlikely(put_user(tmp, uaddr) != 0))
+ ret = -EFAULT;
+
+out_pagefault_enable:
+ pagefault_enable();
+
+ if (ret == 0) {
+ switch (cmp) {
+ case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
+ case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
+ case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
+ case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
+ case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
+ case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
+ default: ret = -ENOSYS;
+ }
+ }
+ return ret;
+}
+
+/**
+ * futex_atomic_cmpxchg_inatomic() - Compare and exchange the content of the
+ * uaddr with newval if the current value is
+ * oldval.
+ * @uval: pointer to store content of @uaddr
+ * @uaddr: pointer to user space address
+ * @oldval: old value
+ * @newval: new value to store to @uaddr
+ *
+ * Return:
+ * 0 - On success
+ * <0 - On error
+ */
+static inline int
+futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
+ u32 oldval, u32 newval)
+{
+ u32 val;
+
+ if (unlikely(get_user(val, uaddr) != 0))
+ return -EFAULT;
+
+ if (val == oldval && unlikely(put_user(newval, uaddr) != 0))
+ return -EFAULT;
+
+ *uval = val;
+
+ return 0;
+}
+
+#else
static inline int
futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
{
return -ENOSYS;
}
+#endif /* CONFIG_SMP */
#endif
--- /dev/null
+/*
+ * include/asm-generic/seccomp.h
+ *
+ * Copyright (C) 2014 Linaro Limited
+ * Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef _ASM_GENERIC_SECCOMP_H
+#define _ASM_GENERIC_SECCOMP_H
+
+#include <linux/unistd.h>
+
+#if defined(CONFIG_COMPAT) && !defined(__NR_seccomp_read_32)
+#define __NR_seccomp_read_32 __NR_read
+#define __NR_seccomp_write_32 __NR_write
+#define __NR_seccomp_exit_32 __NR_exit
+#define __NR_seccomp_sigreturn_32 __NR_rt_sigreturn
+#endif /* CONFIG_COMPAT && ! already defined */
+
+#define __NR_seccomp_read __NR_read
+#define __NR_seccomp_write __NR_write
+#define __NR_seccomp_exit __NR_exit
+#ifndef __NR_seccomp_sigreturn
+#define __NR_seccomp_sigreturn __NR_rt_sigreturn
+#endif
+
+#endif /* _ASM_GENERIC_SECCOMP_H */
#endif
unsigned long start;
unsigned long end;
- unsigned int need_flush : 1, /* Did free PTEs */
/* we are in the middle of an operation to clear
* a full mm and can make some optimizations */
- fullmm : 1,
+ unsigned int fullmm : 1,
/* we have performed an operation which
* requires a complete flush of the tlb */
need_flush_all : 1;
tlb_flush_mmu(tlb);
}
+static inline void __tlb_adjust_range(struct mmu_gather *tlb,
+ unsigned long address)
+{
+ tlb->start = min(tlb->start, address);
+ tlb->end = max(tlb->end, address + PAGE_SIZE);
+}
+
+static inline void __tlb_reset_range(struct mmu_gather *tlb)
+{
+ tlb->start = TASK_SIZE;
+ tlb->end = 0;
+}
+
+/*
+ * In the case of tlb vma handling, we can optimise these away in the
+ * case where we're doing a full MM flush. When we're doing a munmap,
+ * the vmas are adjusted to only cover the region to be torn down.
+ */
+#ifndef tlb_start_vma
+#define tlb_start_vma(tlb, vma) do { } while (0)
+#endif
+
+#define __tlb_end_vma(tlb, vma) \
+ do { \
+ if (!tlb->fullmm && tlb->end) { \
+ tlb_flush(tlb); \
+ __tlb_reset_range(tlb); \
+ } \
+ } while (0)
+
+#ifndef tlb_end_vma
+#define tlb_end_vma __tlb_end_vma
+#endif
+
+#ifndef __tlb_remove_tlb_entry
+#define __tlb_remove_tlb_entry(tlb, ptep, address) do { } while (0)
+#endif
+
/**
* tlb_remove_tlb_entry - remember a pte unmapping for later tlb invalidation.
*
- * Record the fact that pte's were really umapped in ->need_flush, so we can
- * later optimise away the tlb invalidate. This helps when userspace is
- * unmapping already-unmapped pages, which happens quite a lot.
+ * Record the fact that pte's were really unmapped by updating the range,
+ * so we can later optimise away the tlb invalidate. This helps when
+ * userspace is unmapping already-unmapped pages, which happens quite a lot.
*/
#define tlb_remove_tlb_entry(tlb, ptep, address) \
do { \
- tlb->need_flush = 1; \
+ __tlb_adjust_range(tlb, address); \
__tlb_remove_tlb_entry(tlb, ptep, address); \
} while (0)
#define tlb_remove_pmd_tlb_entry(tlb, pmdp, address) \
do { \
- tlb->need_flush = 1; \
+ __tlb_adjust_range(tlb, address); \
__tlb_remove_pmd_tlb_entry(tlb, pmdp, address); \
} while (0)
#define pte_free_tlb(tlb, ptep, address) \
do { \
- tlb->need_flush = 1; \
+ __tlb_adjust_range(tlb, address); \
__pte_free_tlb(tlb, ptep, address); \
} while (0)
#ifndef __ARCH_HAS_4LEVEL_HACK
#define pud_free_tlb(tlb, pudp, address) \
do { \
- tlb->need_flush = 1; \
+ __tlb_adjust_range(tlb, address); \
__pud_free_tlb(tlb, pudp, address); \
} while (0)
#endif
#define pmd_free_tlb(tlb, pmdp, address) \
do { \
- tlb->need_flush = 1; \
+ __tlb_adjust_range(tlb, address); \
__pmd_free_tlb(tlb, pmdp, address); \
} while (0)
#define ESC1_CLK_SRC 43
#define HDMI_CLK_SRC 44
#define VSYNC_CLK_SRC 45
-#define RBCPR_CLK_SRC 46
+#define MMSS_RBCPR_CLK_SRC 46
#define RBBMTIMER_CLK_SRC 47
#define MAPLE_CLK_SRC 48
#define VDP_CLK_SRC 49
#define VF610_CLK_FASK_CLK_SEL 8
#define VF610_CLK_AUDIO_EXT 9
#define VF610_CLK_ENET_EXT 10
-#define VF610_CLK_PLL1_MAIN 11
+#define VF610_CLK_PLL1_SYS 11
#define VF610_CLK_PLL1_PFD1 12
#define VF610_CLK_PLL1_PFD2 13
#define VF610_CLK_PLL1_PFD3 14
#define VF610_CLK_PLL1_PFD4 15
-#define VF610_CLK_PLL2_MAIN 16
+#define VF610_CLK_PLL2_BUS 16
#define VF610_CLK_PLL2_PFD1 17
#define VF610_CLK_PLL2_PFD2 18
#define VF610_CLK_PLL2_PFD3 19
#define VF610_CLK_PLL2_PFD4 20
-#define VF610_CLK_PLL3_MAIN 21
+#define VF610_CLK_PLL3_USB_OTG 21
#define VF610_CLK_PLL3_PFD1 22
#define VF610_CLK_PLL3_PFD2 23
#define VF610_CLK_PLL3_PFD3 24
#define VF610_CLK_PLL3_PFD4 25
-#define VF610_CLK_PLL4_MAIN 26
-#define VF610_CLK_PLL5_MAIN 27
-#define VF610_CLK_PLL6_MAIN 28
+#define VF610_CLK_PLL4_AUDIO 26
+#define VF610_CLK_PLL5_ENET 27
+#define VF610_CLK_PLL6_VIDEO 28
#define VF610_CLK_PLL3_MAIN_DIV 29
#define VF610_CLK_PLL4_MAIN_DIV 30
#define VF610_CLK_PLL6_MAIN_DIV 31
#define VF610_CLK_DMAMUX3 153
#define VF610_CLK_FLEXCAN0_EN 154
#define VF610_CLK_FLEXCAN1_EN 155
-#define VF610_CLK_PLL7_MAIN 156
+#define VF610_CLK_PLL7_USB_HOST 156
#define VF610_CLK_USBPHY0 157
#define VF610_CLK_USBPHY1 158
-#define VF610_CLK_END 159
+#define VF610_CLK_LVDS1_IN 159
+#define VF610_CLK_ANACLK1 160
+#define VF610_CLK_PLL1_BYPASS_SRC 161
+#define VF610_CLK_PLL2_BYPASS_SRC 162
+#define VF610_CLK_PLL3_BYPASS_SRC 163
+#define VF610_CLK_PLL4_BYPASS_SRC 164
+#define VF610_CLK_PLL5_BYPASS_SRC 165
+#define VF610_CLK_PLL6_BYPASS_SRC 166
+#define VF610_CLK_PLL7_BYPASS_SRC 167
+#define VF610_CLK_PLL1 168
+#define VF610_CLK_PLL2 169
+#define VF610_CLK_PLL3 170
+#define VF610_CLK_PLL4 171
+#define VF610_CLK_PLL5 172
+#define VF610_CLK_PLL6 173
+#define VF610_CLK_PLL7 174
+#define VF610_PLL1_BYPASS 175
+#define VF610_PLL2_BYPASS 176
+#define VF610_PLL3_BYPASS 177
+#define VF610_PLL4_BYPASS 178
+#define VF610_PLL5_BYPASS 179
+#define VF610_PLL6_BYPASS 180
+#define VF610_PLL7_BYPASS 181
+#define VF610_CLK_END 182
#endif /* __DT_BINDINGS_CLOCK_VF610_H */
/* Active pin states */
#define PIN_OUTPUT (0 | PULL_DIS)
-#define PIN_OUTPUT_PULLUP (PIN_OUTPUT | PULL_ENA | PULL_UP)
-#define PIN_OUTPUT_PULLDOWN (PIN_OUTPUT | PULL_ENA)
+#define PIN_OUTPUT_PULLUP (PULL_UP)
+#define PIN_OUTPUT_PULLDOWN (0)
#define PIN_INPUT (INPUT_EN | PULL_DIS)
#define PIN_INPUT_SLEW (INPUT_EN | SLEWCONTROL)
#define PIN_INPUT_PULLUP (PULL_ENA | INPUT_EN | PULL_UP)
--- /dev/null
+/*
+ * Copyright (C) 2014 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Device Tree binding constants for the Maxim 77802 PMIC regulators
+ */
+
+#ifndef _DT_BINDINGS_REGULATOR_MAXIM_MAX77802_H
+#define _DT_BINDINGS_REGULATOR_MAXIM_MAX77802_H
+
+/* Regulator operating modes */
+#define MAX77802_OPMODE_LP 1
+#define MAX77802_OPMODE_NORMAL 3
+
+#endif /* _DT_BINDINGS_REGULATOR_MAXIM_MAX77802_H */
* @detect_pin: GPIO pin wired to the card detect switch
* @wp_pin: GPIO pin wired to the write protect sensor
* @detect_is_active_high: The state of the detect pin when it is active
+ * @non_removable: The slot is not removable, only detect once
*
* If a given slot is not present on the board, @bus_width should be
* set to 0. The other fields are ignored in this case.
int detect_pin;
int wp_pin;
bool detect_is_active_high;
+ bool non_removable;
};
/**
* position @h. For example
* GENMASK_ULL(39, 21) gives us the 64bit vector 0x000000ffffe00000.
*/
-#define GENMASK(h, l) (((U32_C(1) << ((h) - (l) + 1)) - 1) << (l))
-#define GENMASK_ULL(h, l) (((U64_C(1) << ((h) - (l) + 1)) - 1) << (l))
+#define GENMASK(h, l) \
+ (((~0UL) << (l)) & (~0UL >> (BITS_PER_LONG - 1 - (h))))
+
+#define GENMASK_ULL(h, l) \
+ (((~0ULL) << (l)) & (~0ULL >> (BITS_PER_LONG_LONG - 1 - (h))))
extern unsigned int __sw_hweight8(unsigned int w);
extern unsigned int __sw_hweight16(unsigned int w);
gfp_t gfp, bool reserved);
struct request *blk_mq_tag_to_rq(struct blk_mq_tags *tags, unsigned int tag);
+enum {
+ BLK_MQ_UNIQUE_TAG_BITS = 16,
+ BLK_MQ_UNIQUE_TAG_MASK = (1 << BLK_MQ_UNIQUE_TAG_BITS) - 1,
+};
+
+u32 blk_mq_unique_tag(struct request *rq);
+
+static inline u16 blk_mq_unique_tag_to_hwq(u32 unique_tag)
+{
+ return unique_tag >> BLK_MQ_UNIQUE_TAG_BITS;
+}
+
+static inline u16 blk_mq_unique_tag_to_tag(u32 unique_tag)
+{
+ return unique_tag & BLK_MQ_UNIQUE_TAG_MASK;
+}
+
struct blk_mq_hw_ctx *blk_mq_map_queue(struct request_queue *, const int ctx_index);
struct blk_mq_hw_ctx *blk_mq_alloc_single_hw_queue(struct blk_mq_tag_set *, unsigned int, int);
/*
* tag stuff
*/
-#define blk_rq_tagged(rq) ((rq)->cmd_flags & REQ_QUEUED)
extern int blk_queue_start_tag(struct request_queue *, struct request *);
extern struct request *blk_queue_find_tag(struct request_queue *, int);
extern void blk_queue_end_tag(struct request_queue *, struct request *);
extern unsigned long init_bootmem(unsigned long addr, unsigned long memend);
extern unsigned long free_all_bootmem(void);
+extern void reset_node_managed_pages(pg_data_t *pgdat);
extern void reset_all_zones_managed_pages(void);
extern void free_bootmem_node(pg_data_t *pgdat,
return 1;
}
+static inline bool can_is_canfd_skb(const struct sk_buff *skb)
+{
+ /* the CAN specific type of skb is identified by its data length */
+ return skb->len == CANFD_MTU;
+}
+
/* get data length from can_dlc with sanitized can_dlc */
u8 can_dlc2len(u8 can_dlc);
#define CLK_DIVIDER_READ_ONLY BIT(5)
extern const struct clk_ops clk_divider_ops;
-extern const struct clk_ops clk_divider_ro_ops;
struct clk *clk_register_divider(struct device *dev, const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 shift, u8 width,
extern phys_addr_t cma_get_base(struct cma *cma);
extern unsigned long cma_get_size(struct cma *cma);
-extern int __init cma_declare_contiguous(phys_addr_t size,
- phys_addr_t base, phys_addr_t limit,
+extern int __init cma_declare_contiguous(phys_addr_t base,
+ phys_addr_t size, phys_addr_t limit,
phys_addr_t alignment, unsigned int order_per_bit,
bool fixed, struct cma **res_cma);
-extern int cma_init_reserved_mem(phys_addr_t size,
- phys_addr_t base, int order_per_bit,
+extern int cma_init_reserved_mem(phys_addr_t base,
+ phys_addr_t size, int order_per_bit,
struct cma **res_cma);
extern struct page *cma_alloc(struct cma *cma, int count, unsigned int align);
extern bool cma_release(struct cma *cma, struct page *pages, int count);
union map_info *map_context);
typedef void (*dm_presuspend_fn) (struct dm_target *ti);
+typedef void (*dm_presuspend_undo_fn) (struct dm_target *ti);
typedef void (*dm_postsuspend_fn) (struct dm_target *ti);
typedef int (*dm_preresume_fn) (struct dm_target *ti);
typedef void (*dm_resume_fn) (struct dm_target *ti);
dm_endio_fn end_io;
dm_request_endio_fn rq_end_io;
dm_presuspend_fn presuspend;
+ dm_presuspend_undo_fn presuspend_undo;
dm_postsuspend_fn postsuspend;
dm_preresume_fn preresume;
dm_resume_fn resume;
* @MEM_DDR3: DDR3 RAM
* @MEM_RDDR3: Registered DDR3 RAM
* This is a variant of the DDR3 memories.
- * @MEM_DDR4: DDR4 RAM
+ * @MEM_LRDDR3 Load-Reduced DDR3 memory.
+ * @MEM_DDR4: Unbuffered DDR4 RAM
* @MEM_RDDR4: Registered DDR4 RAM
* This is a variant of the DDR4 memories.
*/
MEM_XDR,
MEM_DDR3,
MEM_RDDR3,
+ MEM_LRDDR3,
MEM_DDR4,
MEM_RDDR4,
};
const u8 word, u16 *data);
extern void eeprom_93cx6_multiread(struct eeprom_93cx6 *eeprom,
const u8 word, __le16 *data, const u16 words);
+extern void eeprom_93cx6_readb(struct eeprom_93cx6 *eeprom,
+ const u8 byte, u8 *data);
+extern void eeprom_93cx6_multireadb(struct eeprom_93cx6 *eeprom,
+ const u8 byte, u8 *data, const u16 bytes);
extern void eeprom_93cx6_wren(struct eeprom_93cx6 *eeprom, bool enable);
#define SMBIOS_TABLE_GUID \
EFI_GUID( 0xeb9d2d31, 0x2d88, 0x11d3, 0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d )
+#define SMBIOS3_TABLE_GUID \
+ EFI_GUID( 0xf2fd1544, 0x9794, 0x4a2c, 0x99, 0x2e, 0xe5, 0xbb, 0xcf, 0x20, 0xe3, 0x94 )
+
#define SAL_SYSTEM_TABLE_GUID \
EFI_GUID( 0xeb9d2d32, 0x2d88, 0x11d3, 0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d )
unsigned long mps; /* MPS table */
unsigned long acpi; /* ACPI table (IA64 ext 0.71) */
unsigned long acpi20; /* ACPI table (ACPI 2.0) */
- unsigned long smbios; /* SM BIOS table */
+ unsigned long smbios; /* SMBIOS table (32 bit entry point) */
+ unsigned long smbios3; /* SMBIOS table (64 bit entry point) */
unsigned long sal_systab; /* SAL system table */
unsigned long boot_info; /* boot info table */
unsigned long hcdp; /* HCDP table */
struct pmbus_platform_data {
u32 flags; /* Device specific flags */
+
+ /* regulator support */
+ int num_regulators;
+ struct regulator_init_data *reg_init_data;
};
#endif /* _PMBUS_H_ */
#define IIO_EVENT_CODE_EXTRACT_TYPE(mask) ((mask >> 56) & 0xFF)
-#define IIO_EVENT_CODE_EXTRACT_DIR(mask) ((mask >> 48) & 0xCF)
+#define IIO_EVENT_CODE_EXTRACT_DIR(mask) ((mask >> 48) & 0x7F)
#define IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(mask) ((mask >> 32) & 0xFF)
static __inline__ __be32 inet_make_mask(int logmask)
{
if (logmask)
- return htonl(~((1<<(32-logmask))-1));
+ return htonl(~((1U<<(32-logmask))-1));
return 0;
}
return kstat_cpu(cpu).irqs_sum;
}
-/*
- * Lock/unlock the current runqueue - to extract task statistics:
- */
-extern unsigned long long task_delta_exec(struct task_struct *);
-
extern void account_user_time(struct task_struct *, cputime_t, cputime_t);
extern void account_system_time(struct task_struct *, int, cputime_t, cputime_t);
extern void account_steal_time(cputime_t);
int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu);
void kvm_vcpu_kick(struct kvm_vcpu *vcpu);
-bool kvm_is_mmio_pfn(pfn_t pfn);
+bool kvm_is_reserved_pfn(pfn_t pfn);
struct kvm_irq_ack_notifier {
struct hlist_node link;
extern int ata_scsi_slave_config(struct scsi_device *sdev);
extern void ata_scsi_slave_destroy(struct scsi_device *sdev);
extern int ata_scsi_change_queue_depth(struct scsi_device *sdev,
- int queue_depth, int reason);
+ int queue_depth);
extern int __ata_change_queue_depth(struct ata_port *ap, struct scsi_device *sdev,
- int queue_depth, int reason);
+ int queue_depth);
extern struct ata_device *ata_dev_pair(struct ata_device *adev);
extern int ata_do_set_mode(struct ata_link *link, struct ata_device **r_failed_dev);
extern void ata_scsi_port_error_handler(struct Scsi_Host *host, struct ata_port *ap);
int ab8500_sysctrl_read(u16 reg, u8 *value);
int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value);
-void ab8500_restart(char mode, const char *cmd);
#else
#define ARIZONA_MIC_BIAS_CTRL_1 0x218
#define ARIZONA_MIC_BIAS_CTRL_2 0x219
#define ARIZONA_MIC_BIAS_CTRL_3 0x21A
+#define ARIZONA_HP_CTRL_1L 0x225
+#define ARIZONA_HP_CTRL_1R 0x226
#define ARIZONA_ACCESSORY_DETECT_MODE_1 0x293
#define ARIZONA_HEADPHONE_DETECT_1 0x29B
#define ARIZONA_HEADPHONE_DETECT_2 0x29C
#define ARIZONA_AIF2_FRAME_CTRL_2 0x548
#define ARIZONA_AIF2_FRAME_CTRL_3 0x549
#define ARIZONA_AIF2_FRAME_CTRL_4 0x54A
+#define ARIZONA_AIF2_FRAME_CTRL_5 0x54B
+#define ARIZONA_AIF2_FRAME_CTRL_6 0x54C
+#define ARIZONA_AIF2_FRAME_CTRL_7 0x54D
+#define ARIZONA_AIF2_FRAME_CTRL_8 0x54E
#define ARIZONA_AIF2_FRAME_CTRL_11 0x551
#define ARIZONA_AIF2_FRAME_CTRL_12 0x552
+#define ARIZONA_AIF2_FRAME_CTRL_13 0x553
+#define ARIZONA_AIF2_FRAME_CTRL_14 0x554
+#define ARIZONA_AIF2_FRAME_CTRL_15 0x555
+#define ARIZONA_AIF2_FRAME_CTRL_16 0x556
#define ARIZONA_AIF2_TX_ENABLES 0x559
#define ARIZONA_AIF2_RX_ENABLES 0x55A
#define ARIZONA_AIF2_FORCE_WRITE 0x55B
#define ARIZONA_MICB3_ENA_SHIFT 0 /* MICB3_ENA */
#define ARIZONA_MICB3_ENA_WIDTH 1 /* MICB3_ENA */
+/*
+ * R549 (0x225) - HP Ctrl 1L
+ */
+#define ARIZONA_RMV_SHRT_HP1L 0x4000 /* RMV_SHRT_HP1L */
+#define ARIZONA_RMV_SHRT_HP1L_MASK 0x4000 /* RMV_SHRT_HP1L */
+#define ARIZONA_RMV_SHRT_HP1L_SHIFT 14 /* RMV_SHRT_HP1L */
+#define ARIZONA_RMV_SHRT_HP1L_WIDTH 1 /* RMV_SHRT_HP1L */
+#define ARIZONA_HP1L_FLWR 0x0004 /* HP1L_FLWR */
+#define ARIZONA_HP1L_FLWR_MASK 0x0004 /* HP1L_FLWR */
+#define ARIZONA_HP1L_FLWR_SHIFT 2 /* HP1L_FLWR */
+#define ARIZONA_HP1L_FLWR_WIDTH 1 /* HP1L_FLWR */
+#define ARIZONA_HP1L_SHRTI 0x0002 /* HP1L_SHRTI */
+#define ARIZONA_HP1L_SHRTI_MASK 0x0002 /* HP1L_SHRTI */
+#define ARIZONA_HP1L_SHRTI_SHIFT 1 /* HP1L_SHRTI */
+#define ARIZONA_HP1L_SHRTI_WIDTH 1 /* HP1L_SHRTI */
+#define ARIZONA_HP1L_SHRTO 0x0001 /* HP1L_SHRTO */
+#define ARIZONA_HP1L_SHRTO_MASK 0x0001 /* HP1L_SHRTO */
+#define ARIZONA_HP1L_SHRTO_SHIFT 0 /* HP1L_SHRTO */
+#define ARIZONA_HP1L_SHRTO_WIDTH 1 /* HP1L_SHRTO */
+
+/*
+ * R550 (0x226) - HP Ctrl 1R
+ */
+#define ARIZONA_RMV_SHRT_HP1R 0x4000 /* RMV_SHRT_HP1R */
+#define ARIZONA_RMV_SHRT_HP1R_MASK 0x4000 /* RMV_SHRT_HP1R */
+#define ARIZONA_RMV_SHRT_HP1R_SHIFT 14 /* RMV_SHRT_HP1R */
+#define ARIZONA_RMV_SHRT_HP1R_WIDTH 1 /* RMV_SHRT_HP1R */
+#define ARIZONA_HP1R_FLWR 0x0004 /* HP1R_FLWR */
+#define ARIZONA_HP1R_FLWR_MASK 0x0004 /* HP1R_FLWR */
+#define ARIZONA_HP1R_FLWR_SHIFT 2 /* HP1R_FLWR */
+#define ARIZONA_HP1R_FLWR_WIDTH 1 /* HP1R_FLWR */
+#define ARIZONA_HP1R_SHRTI 0x0002 /* HP1R_SHRTI */
+#define ARIZONA_HP1R_SHRTI_MASK 0x0002 /* HP1R_SHRTI */
+#define ARIZONA_HP1R_SHRTI_SHIFT 1 /* HP1R_SHRTI */
+#define ARIZONA_HP1R_SHRTI_WIDTH 1 /* HP1R_SHRTI */
+#define ARIZONA_HP1R_SHRTO 0x0001 /* HP1R_SHRTO */
+#define ARIZONA_HP1R_SHRTO_MASK 0x0001 /* HP1R_SHRTO */
+#define ARIZONA_HP1R_SHRTO_SHIFT 0 /* HP1R_SHRTO */
+#define ARIZONA_HP1R_SHRTO_WIDTH 1 /* HP1R_SHRTO */
+
/*
* R659 (0x293) - Accessory Detect Mode 1
*/
--- /dev/null
+/*
+ * Copyright (C) 2014 Free Electrons
+ * Copyright (C) 2014 Atmel
+ *
+ * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LINUX_MFD_HLCDC_H
+#define __LINUX_MFD_HLCDC_H
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+
+#define ATMEL_HLCDC_CFG(i) ((i) * 0x4)
+#define ATMEL_HLCDC_SIG_CFG LCDCFG(5)
+#define ATMEL_HLCDC_HSPOL BIT(0)
+#define ATMEL_HLCDC_VSPOL BIT(1)
+#define ATMEL_HLCDC_VSPDLYS BIT(2)
+#define ATMEL_HLCDC_VSPDLYE BIT(3)
+#define ATMEL_HLCDC_DISPPOL BIT(4)
+#define ATMEL_HLCDC_DITHER BIT(6)
+#define ATMEL_HLCDC_DISPDLY BIT(7)
+#define ATMEL_HLCDC_MODE_MASK GENMASK(9, 8)
+#define ATMEL_HLCDC_PP BIT(10)
+#define ATMEL_HLCDC_VSPSU BIT(12)
+#define ATMEL_HLCDC_VSPHO BIT(13)
+#define ATMEL_HLCDC_GUARDTIME_MASK GENMASK(20, 16)
+
+#define ATMEL_HLCDC_EN 0x20
+#define ATMEL_HLCDC_DIS 0x24
+#define ATMEL_HLCDC_SR 0x28
+#define ATMEL_HLCDC_IER 0x2c
+#define ATMEL_HLCDC_IDR 0x30
+#define ATMEL_HLCDC_IMR 0x34
+#define ATMEL_HLCDC_ISR 0x38
+
+#define ATMEL_HLCDC_CLKPOL BIT(0)
+#define ATMEL_HLCDC_CLKSEL BIT(2)
+#define ATMEL_HLCDC_CLKPWMSEL BIT(3)
+#define ATMEL_HLCDC_CGDIS(i) BIT(8 + (i))
+#define ATMEL_HLCDC_CLKDIV_SHFT 16
+#define ATMEL_HLCDC_CLKDIV_MASK GENMASK(23, 16)
+#define ATMEL_HLCDC_CLKDIV(div) ((div - 2) << ATMEL_HLCDC_CLKDIV_SHFT)
+
+#define ATMEL_HLCDC_PIXEL_CLK BIT(0)
+#define ATMEL_HLCDC_SYNC BIT(1)
+#define ATMEL_HLCDC_DISP BIT(2)
+#define ATMEL_HLCDC_PWM BIT(3)
+#define ATMEL_HLCDC_SIP BIT(4)
+
+#define ATMEL_HLCDC_SOF BIT(0)
+#define ATMEL_HLCDC_SYNCDIS BIT(1)
+#define ATMEL_HLCDC_FIFOERR BIT(4)
+#define ATMEL_HLCDC_LAYER_STATUS(x) BIT((x) + 8)
+
+/**
+ * Structure shared by the MFD device and its subdevices.
+ *
+ * @regmap: register map used to access HLCDC IP registers
+ * @periph_clk: the hlcdc peripheral clock
+ * @sys_clk: the hlcdc system clock
+ * @slow_clk: the system slow clk
+ * @irq: the hlcdc irq
+ */
+struct atmel_hlcdc {
+ struct regmap *regmap;
+ struct clk *periph_clk;
+ struct clk *sys_clk;
+ struct clk *slow_clk;
+ int irq;
+};
+
+#endif /* __LINUX_MFD_HLCDC_H */
enum {
AXP202_ID = 0,
AXP209_ID,
+ AXP288_ID,
+ NR_AXP20X_VARIANTS,
};
#define AXP20X_DATACACHE(m) (0x04 + (m))
#define AXP20X_IRQ3_EN 0x42
#define AXP20X_IRQ4_EN 0x43
#define AXP20X_IRQ5_EN 0x44
+#define AXP20X_IRQ6_EN 0x45
#define AXP20X_IRQ1_STATE 0x48
#define AXP20X_IRQ2_STATE 0x49
#define AXP20X_IRQ3_STATE 0x4a
#define AXP20X_IRQ4_STATE 0x4b
#define AXP20X_IRQ5_STATE 0x4c
+#define AXP20X_IRQ6_STATE 0x4d
/* ADC */
#define AXP20X_ACIN_V_ADC_H 0x56
#define AXP20X_CC_CTRL 0xb8
#define AXP20X_FG_RES 0xb9
+/* AXP288 specific registers */
+#define AXP288_PMIC_ADC_H 0x56
+#define AXP288_PMIC_ADC_L 0x57
+#define AXP288_ADC_TS_PIN_CTRL 0x84
+
+#define AXP288_PMIC_ADC_EN 0x84
+#define AXP288_FG_TUNE5 0xed
+
+
/* Regulators IDs */
enum {
AXP20X_LDO1 = 0,
AXP20X_IRQ_GPIO0_INPUT,
};
+enum axp288_irqs {
+ AXP288_IRQ_VBUS_FALL = 2,
+ AXP288_IRQ_VBUS_RISE,
+ AXP288_IRQ_OV,
+ AXP288_IRQ_FALLING_ALT,
+ AXP288_IRQ_RISING_ALT,
+ AXP288_IRQ_OV_ALT,
+ AXP288_IRQ_DONE = 10,
+ AXP288_IRQ_CHARGING,
+ AXP288_IRQ_SAFE_QUIT,
+ AXP288_IRQ_SAFE_ENTER,
+ AXP288_IRQ_ABSENT,
+ AXP288_IRQ_APPEND,
+ AXP288_IRQ_QWBTU,
+ AXP288_IRQ_WBTU,
+ AXP288_IRQ_QWBTO,
+ AXP288_IRQ_WBTO,
+ AXP288_IRQ_QCBTU,
+ AXP288_IRQ_CBTU,
+ AXP288_IRQ_QCBTO,
+ AXP288_IRQ_CBTO,
+ AXP288_IRQ_WL2,
+ AXP288_IRQ_WL1,
+ AXP288_IRQ_GPADC,
+ AXP288_IRQ_OT = 31,
+ AXP288_IRQ_GPIO0,
+ AXP288_IRQ_GPIO1,
+ AXP288_IRQ_POKO,
+ AXP288_IRQ_POKL,
+ AXP288_IRQ_POKS,
+ AXP288_IRQ_POKN,
+ AXP288_IRQ_POKP,
+ AXP288_IRQ_TIMER,
+ AXP288_IRQ_MV_CHNG,
+ AXP288_IRQ_BC_USB_CHNG,
+};
+
+#define AXP288_TS_ADC_H 0x58
+#define AXP288_TS_ADC_L 0x59
+#define AXP288_GP_ADC_H 0x5a
+#define AXP288_GP_ADC_L 0x5b
+
struct axp20x_dev {
struct device *dev;
struct i2c_client *i2c_client;
struct regmap *regmap;
struct regmap_irq_chip_data *regmap_irqc;
long variant;
+ int nr_cells;
+ struct mfd_cell *cells;
+ const struct regmap_config *regmap_cfg;
+ const struct regmap_irq_chip *regmap_irq_chip;
};
#endif /* __LINUX_MFD_AXP20X_H */
struct resource *mem_base,
int irq_base, struct irq_domain *irq_domain);
+static inline int mfd_add_hotplug_devices(struct device *parent,
+ const struct mfd_cell *cells, int n_devs)
+{
+ return mfd_add_devices(parent, PLATFORM_DEVID_AUTO, cells, n_devs,
+ NULL, 0, NULL);
+}
+
extern void mfd_remove_devices(struct device *parent);
#endif
--- /dev/null
+#ifndef __LINUX_USB_DLN2_H
+#define __LINUX_USB_DLN2_H
+
+#define DLN2_CMD(cmd, id) ((cmd) | ((id) << 8))
+
+struct dln2_platform_data {
+ u16 handle; /* sub-driver handle (internally used only) */
+ u8 port; /* I2C/SPI port */
+};
+
+/**
+ * dln2_event_cb_t - event callback function signature
+ *
+ * @pdev - the sub-device that registered this callback
+ * @echo - the echo header field received in the message
+ * @data - the data payload
+ * @len - the data payload length
+ *
+ * The callback function is called in interrupt context and the data payload is
+ * only valid during the call. If the user needs later access of the data, it
+ * must copy it.
+ */
+
+typedef void (*dln2_event_cb_t)(struct platform_device *pdev, u16 echo,
+ const void *data, int len);
+
+/**
+ * dl2n_register_event_cb - register a callback function for an event
+ *
+ * @pdev - the sub-device that registers the callback
+ * @event - the event for which to register a callback
+ * @event_cb - the callback function
+ *
+ * @return 0 in case of success, negative value in case of error
+ */
+int dln2_register_event_cb(struct platform_device *pdev, u16 event,
+ dln2_event_cb_t event_cb);
+
+/**
+ * dln2_unregister_event_cb - unregister the callback function for an event
+ *
+ * @pdev - the sub-device that registered the callback
+ * @event - the event for which to register a callback
+ */
+void dln2_unregister_event_cb(struct platform_device *pdev, u16 event);
+
+/**
+ * dln2_transfer - issue a DLN2 command and wait for a response and the
+ * associated data
+ *
+ * @pdev - the sub-device which is issuing this transfer
+ * @cmd - the command to be sent to the device
+ * @obuf - the buffer to be sent to the device; it can be NULL if the user
+ * doesn't need to transmit data with this command
+ * @obuf_len - the size of the buffer to be sent to the device
+ * @ibuf - any data associated with the response will be copied here; it can be
+ * NULL if the user doesn't need the response data
+ * @ibuf_len - must be initialized to the input buffer size; it will be modified
+ * to indicate the actual data transferred;
+ *
+ * @return 0 for success, negative value for errors
+ */
+int dln2_transfer(struct platform_device *pdev, u16 cmd,
+ const void *obuf, unsigned obuf_len,
+ void *ibuf, unsigned *ibuf_len);
+
+/**
+ * dln2_transfer_rx - variant of @dln2_transfer() where TX buffer is not needed
+ *
+ * @pdev - the sub-device which is issuing this transfer
+ * @cmd - the command to be sent to the device
+ * @ibuf - any data associated with the response will be copied here; it can be
+ * NULL if the user doesn't need the response data
+ * @ibuf_len - must be initialized to the input buffer size; it will be modified
+ * to indicate the actual data transferred;
+ *
+ * @return 0 for success, negative value for errors
+ */
+
+static inline int dln2_transfer_rx(struct platform_device *pdev, u16 cmd,
+ void *ibuf, unsigned *ibuf_len)
+{
+ return dln2_transfer(pdev, cmd, NULL, 0, ibuf, ibuf_len);
+}
+
+/**
+ * dln2_transfer_tx - variant of @dln2_transfer() where RX buffer is not needed
+ *
+ * @pdev - the sub-device which is issuing this transfer
+ * @cmd - the command to be sent to the device
+ * @obuf - the buffer to be sent to the device; it can be NULL if the
+ * user doesn't need to transmit data with this command
+ * @obuf_len - the size of the buffer to be sent to the device
+ *
+ * @return 0 for success, negative value for errors
+ */
+static inline int dln2_transfer_tx(struct platform_device *pdev, u16 cmd,
+ const void *obuf, unsigned obuf_len)
+{
+ return dln2_transfer(pdev, cmd, obuf, obuf_len, NULL, NULL);
+}
+
+#endif
MAX77686_OPMODE_STANDBY,
};
-enum max77802_opmode {
- MAX77802_OPMODE_OFF,
- MAX77802_OPMODE_STANDBY,
- MAX77802_OPMODE_LP,
- MAX77802_OPMODE_NORMAL,
-};
-
struct max77686_opmode_data {
int id;
int mode;
#include <linux/i2c.h>
-#define MAX77693_NUM_IRQ_MUIC_REGS 3
#define MAX77693_REG_INVALID (0xff)
/* Slave addr = 0xCC: PMIC, Charger, Flash LED */
MAX77693_IRQ_GROUP_NR,
};
+#define SRC_IRQ_CHARGER BIT(0)
+#define SRC_IRQ_TOP BIT(1)
+#define SRC_IRQ_FLASH BIT(2)
+#define SRC_IRQ_MUIC BIT(3)
+#define SRC_IRQ_ALL (SRC_IRQ_CHARGER | SRC_IRQ_TOP \
+ | SRC_IRQ_FLASH | SRC_IRQ_MUIC)
+
#define LED_IRQ_FLED2_OPEN BIT(0)
#define LED_IRQ_FLED2_SHORT BIT(1)
#define LED_IRQ_FLED1_OPEN BIT(2)
#define SD_SAMPLE_POINT_CTL 0xFDA7
#define SD_PUSH_POINT_CTL 0xFDA8
#define SD_CMD0 0xFDA9
+#define SD_CMD_START 0x40
#define SD_CMD1 0xFDAA
#define SD_CMD2 0xFDAB
#define SD_CMD3 0xFDAC
#define PM_CTRL1 0xFF44
#define PM_CTRL2 0xFF45
#define PM_CTRL3 0xFF46
+#define SDIO_SEND_PME_EN 0x80
+#define FORCE_RC_MODE_ON 0x40
+#define FORCE_RX50_LINK_ON 0x20
+#define D3_DELINK_MODE_EN 0x10
+#define USE_PESRTB_CTL_DELINK 0x08
+#define DELAY_PIN_WAKE 0x04
+#define RESET_PIN_WAKE 0x02
+#define PM_WAKE_EN 0x01
#define PM_CTRL4 0xFF47
/* Memory mapping */
#define PHY_DUM_REG 0x1F
#define LCTLR 0x80
+#define LCTLR_EXT_SYNC 0x80
+#define LCTLR_COMMON_CLOCK_CFG 0x40
+#define LCTLR_RETRAIN_LINK 0x20
+#define LCTLR_LINK_DISABLE 0x10
+#define LCTLR_RCB 0x08
+#define LCTLR_RESERVED 0x04
+#define LCTLR_ASPM_CTL_MASK 0x03
+
#define PCR_SETTING_REG1 0x724
#define PCR_SETTING_REG2 0x814
#define PCR_SETTING_REG3 0x747
return (u8 *)(pcr->host_cmds_ptr);
}
+static inline int rtsx_pci_update_cfg_byte(struct rtsx_pcr *pcr, int addr,
+ u8 mask, u8 append)
+{
+ int err;
+ u8 val;
+
+ err = pci_read_config_byte(pcr->pci, addr, &val);
+ if (err < 0)
+ return err;
+ return pci_write_config_byte(pcr->pci, addr, (val & mask) | append);
+}
+
+static inline void rtsx_pci_write_be32(struct rtsx_pcr *pcr, u16 reg, u32 val)
+{
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, reg, 0xFF, val >> 24);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, reg + 1, 0xFF, val >> 16);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, reg + 2, 0xFF, val >> 8);
+ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, reg + 3, 0xFF, val);
+}
+
#endif
#define MIN_800_MV 800000
#define MIN_750_MV 750000
#define MIN_600_MV 600000
+#define MIN_500_MV 500000
/* Macros to represent steps for LDO/BUCK */
#define STEP_50_MV 50000
S5M8767X,
S2MPA01,
S2MPS11X,
+ S2MPS13X,
S2MPS14X,
S2MPU02,
};
--- /dev/null
+/*
+ * s2mps13.h
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd
+ * http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __LINUX_MFD_S2MPS13_H
+#define __LINUX_MFD_S2MPS13_H
+
+/* S2MPS13 registers */
+enum s2mps13_reg {
+ S2MPS13_REG_ID,
+ S2MPS13_REG_INT1,
+ S2MPS13_REG_INT2,
+ S2MPS13_REG_INT3,
+ S2MPS13_REG_INT1M,
+ S2MPS13_REG_INT2M,
+ S2MPS13_REG_INT3M,
+ S2MPS13_REG_ST1,
+ S2MPS13_REG_ST2,
+ S2MPS13_REG_PWRONSRC,
+ S2MPS13_REG_OFFSRC,
+ S2MPS13_REG_BU_CHG,
+ S2MPS13_REG_RTCCTRL,
+ S2MPS13_REG_CTRL1,
+ S2MPS13_REG_CTRL2,
+ S2MPS13_REG_RSVD1,
+ S2MPS13_REG_RSVD2,
+ S2MPS13_REG_RSVD3,
+ S2MPS13_REG_RSVD4,
+ S2MPS13_REG_RSVD5,
+ S2MPS13_REG_RSVD6,
+ S2MPS13_REG_CTRL3,
+ S2MPS13_REG_RSVD7,
+ S2MPS13_REG_RSVD8,
+ S2MPS13_REG_WRSTBI,
+ S2MPS13_REG_B1CTRL,
+ S2MPS13_REG_B1OUT,
+ S2MPS13_REG_B2CTRL,
+ S2MPS13_REG_B2OUT,
+ S2MPS13_REG_B3CTRL,
+ S2MPS13_REG_B3OUT,
+ S2MPS13_REG_B4CTRL,
+ S2MPS13_REG_B4OUT,
+ S2MPS13_REG_B5CTRL,
+ S2MPS13_REG_B5OUT,
+ S2MPS13_REG_B6CTRL,
+ S2MPS13_REG_B6OUT,
+ S2MPS13_REG_B7CTRL,
+ S2MPS13_REG_B7OUT,
+ S2MPS13_REG_B8CTRL,
+ S2MPS13_REG_B8OUT,
+ S2MPS13_REG_B9CTRL,
+ S2MPS13_REG_B9OUT,
+ S2MPS13_REG_B10CTRL,
+ S2MPS13_REG_B10OUT,
+ S2MPS13_REG_BB1CTRL,
+ S2MPS13_REG_BB1OUT,
+ S2MPS13_REG_BUCK_RAMP1,
+ S2MPS13_REG_BUCK_RAMP2,
+ S2MPS13_REG_LDO_DVS1,
+ S2MPS13_REG_LDO_DVS2,
+ S2MPS13_REG_LDO_DVS3,
+ S2MPS13_REG_B6OUT2,
+ S2MPS13_REG_L1CTRL,
+ S2MPS13_REG_L2CTRL,
+ S2MPS13_REG_L3CTRL,
+ S2MPS13_REG_L4CTRL,
+ S2MPS13_REG_L5CTRL,
+ S2MPS13_REG_L6CTRL,
+ S2MPS13_REG_L7CTRL,
+ S2MPS13_REG_L8CTRL,
+ S2MPS13_REG_L9CTRL,
+ S2MPS13_REG_L10CTRL,
+ S2MPS13_REG_L11CTRL,
+ S2MPS13_REG_L12CTRL,
+ S2MPS13_REG_L13CTRL,
+ S2MPS13_REG_L14CTRL,
+ S2MPS13_REG_L15CTRL,
+ S2MPS13_REG_L16CTRL,
+ S2MPS13_REG_L17CTRL,
+ S2MPS13_REG_L18CTRL,
+ S2MPS13_REG_L19CTRL,
+ S2MPS13_REG_L20CTRL,
+ S2MPS13_REG_L21CTRL,
+ S2MPS13_REG_L22CTRL,
+ S2MPS13_REG_L23CTRL,
+ S2MPS13_REG_L24CTRL,
+ S2MPS13_REG_L25CTRL,
+ S2MPS13_REG_L26CTRL,
+ S2MPS13_REG_L27CTRL,
+ S2MPS13_REG_L28CTRL,
+ S2MPS13_REG_L30CTRL,
+ S2MPS13_REG_L31CTRL,
+ S2MPS13_REG_L32CTRL,
+ S2MPS13_REG_L33CTRL,
+ S2MPS13_REG_L34CTRL,
+ S2MPS13_REG_L35CTRL,
+ S2MPS13_REG_L36CTRL,
+ S2MPS13_REG_L37CTRL,
+ S2MPS13_REG_L38CTRL,
+ S2MPS13_REG_L39CTRL,
+ S2MPS13_REG_L40CTRL,
+ S2MPS13_REG_LDODSCH1,
+ S2MPS13_REG_LDODSCH2,
+ S2MPS13_REG_LDODSCH3,
+ S2MPS13_REG_LDODSCH4,
+ S2MPS13_REG_LDODSCH5,
+};
+
+/* regulator ids */
+enum s2mps13_regulators {
+ S2MPS13_LDO1,
+ S2MPS13_LDO2,
+ S2MPS13_LDO3,
+ S2MPS13_LDO4,
+ S2MPS13_LDO5,
+ S2MPS13_LDO6,
+ S2MPS13_LDO7,
+ S2MPS13_LDO8,
+ S2MPS13_LDO9,
+ S2MPS13_LDO10,
+ S2MPS13_LDO11,
+ S2MPS13_LDO12,
+ S2MPS13_LDO13,
+ S2MPS13_LDO14,
+ S2MPS13_LDO15,
+ S2MPS13_LDO16,
+ S2MPS13_LDO17,
+ S2MPS13_LDO18,
+ S2MPS13_LDO19,
+ S2MPS13_LDO20,
+ S2MPS13_LDO21,
+ S2MPS13_LDO22,
+ S2MPS13_LDO23,
+ S2MPS13_LDO24,
+ S2MPS13_LDO25,
+ S2MPS13_LDO26,
+ S2MPS13_LDO27,
+ S2MPS13_LDO28,
+ S2MPS13_LDO29,
+ S2MPS13_LDO30,
+ S2MPS13_LDO31,
+ S2MPS13_LDO32,
+ S2MPS13_LDO33,
+ S2MPS13_LDO34,
+ S2MPS13_LDO35,
+ S2MPS13_LDO36,
+ S2MPS13_LDO37,
+ S2MPS13_LDO38,
+ S2MPS13_LDO39,
+ S2MPS13_LDO40,
+ S2MPS13_BUCK1,
+ S2MPS13_BUCK2,
+ S2MPS13_BUCK3,
+ S2MPS13_BUCK4,
+ S2MPS13_BUCK5,
+ S2MPS13_BUCK6,
+ S2MPS13_BUCK7,
+ S2MPS13_BUCK8,
+ S2MPS13_BUCK9,
+ S2MPS13_BUCK10,
+
+ S2MPS13_REGULATOR_MAX,
+};
+
+/*
+ * Default ramp delay in uv/us. Datasheet says that ramp delay can be
+ * controlled however it does not specify which register is used for that.
+ * Let's assume that default value will be set.
+ */
+#define S2MPS13_BUCK_RAMP_DELAY 12500
+
+#endif /* __LINUX_MFD_S2MPS13_H */
/**
* struct tc3589x_gpio_platform_data - TC3589x GPIO platform data
- * @gpio_base: first gpio number assigned to TC3589x. A maximum of
- * %TC3589x_NR_GPIOS GPIOs will be allocated.
* @setup: callback for board-specific initialization
* @remove: callback for board-specific teardown
*/
struct tc3589x_gpio_platform_data {
- int gpio_base;
void (*setup)(struct tc3589x *tc3589x, unsigned gpio_base);
void (*remove)(struct tc3589x *tc3589x, unsigned gpio_base);
};
/**
* struct tc3589x_platform_data - TC3589x platform data
* @block: bitmask of blocks to enable (use TC3589x_BLOCK_*)
- * @irq_base: base IRQ number. %TC3589x_NR_IRQS irqs will be used.
* @gpio: GPIO-specific platform data
* @keypad: keypad-specific platform data
*/
struct tc3589x_platform_data {
unsigned int block;
- int irq_base;
struct tc3589x_gpio_platform_data *gpio;
const struct tc3589x_keypad_platform_data *keypad;
};
-#define TC3589x_NR_GPIOS 24
-#define TC3589x_NR_IRQS TC3589x_INT_GPIO(TC3589x_NR_GPIOS)
-
#endif
unsigned int data_tag_unit_size; /* DATA TAG UNIT size */
unsigned int boot_ro_lock; /* ro lock support */
bool boot_ro_lockable;
+ bool ffu_capable; /* Firmware upgrade support */
+#define MMC_FIRMWARE_LEN 8
+ u8 fwrev[MMC_FIRMWARE_LEN]; /* FW version */
u8 raw_exception_status; /* 54 */
u8 raw_partition_support; /* 160 */
u8 raw_rpmb_size_mult; /* 168 */
#define mmc_dev_to_card(d) container_of(d, struct mmc_card, dev)
-#define mmc_list_to_card(l) container_of(l, struct mmc_card, node)
-#define mmc_get_drvdata(c) dev_get_drvdata(&(c)->dev)
-#define mmc_set_drvdata(c,d) dev_set_drvdata(&(c)->dev, d)
-
-/*
- * MMC device driver (e.g., Flash card, I/O card...)
- */
-struct mmc_driver {
- struct device_driver drv;
- int (*probe)(struct mmc_card *);
- void (*remove)(struct mmc_card *);
- int (*suspend)(struct mmc_card *);
- int (*resume)(struct mmc_card *);
- void (*shutdown)(struct mmc_card *);
-};
-
-extern int mmc_register_driver(struct mmc_driver *);
-extern void mmc_unregister_driver(struct mmc_driver *);
+extern int mmc_register_driver(struct device_driver *);
+extern void mmc_unregister_driver(struct device_driver *);
extern void mmc_fixup_device(struct mmc_card *card,
const struct mmc_fixup *table);
extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, bool,
bool, bool);
extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
-extern int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd);
+extern int mmc_send_tuning(struct mmc_host *host);
+extern int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd);
#define MMC_ERASE_ARG 0x00000000
#define MMC_SECURE_ERASE_ARG 0x80000000
* transfer is in progress.
* @use_dma: Whether DMA channel is initialized or not.
* @using_dma: Whether DMA is in use for the current transfer.
+ * @dma_64bit_address: Whether DMA supports 64-bit address mode or not.
* @sg_dma: Bus address of DMA buffer.
* @sg_cpu: Virtual address of DMA buffer.
* @dma_ops: Pointer to platform-specific DMA callbacks.
* @quirks: Set of quirks that apply to specific versions of the IP.
* @irq_flags: The flags to be passed to request_irq.
* @irq: The irq value to be passed to request_irq.
+ * @sdio_id0: Number of slot0 in the SDIO interrupt registers.
*
* Locking
* =======
struct mmc_command stop_abort;
unsigned int prev_blksz;
unsigned char timing;
- struct workqueue_struct *card_workqueue;
/* DMA interface members*/
int use_dma;
int using_dma;
+ int dma_64bit_address;
dma_addr_t sg_dma;
void *sg_cpu;
u32 stop_cmdr;
u32 dir_status;
struct tasklet_struct tasklet;
- struct work_struct card_work;
unsigned long pending_events;
unsigned long completed_events;
enum dw_mci_state state;
bool vqmmc_enabled;
unsigned long irq_flags; /* IRQ flags */
int irq;
+
+ int sdio_id0;
};
/* DMA ops for Internal/External DMAC interface */
#define MMC_CAP2_HS400_1_2V (1 << 16) /* Can support HS400 1.2V */
#define MMC_CAP2_HS400 (MMC_CAP2_HS400_1_8V | \
MMC_CAP2_HS400_1_2V)
+#define MMC_CAP2_HSX00_1_2V (MMC_CAP2_HS200_1_2V_SDR | MMC_CAP2_HS400_1_2V)
#define MMC_CAP2_SDIO_IRQ_NOTHREAD (1 << 17)
mmc_pm_flag_t pm_caps; /* supported pm features */
#define EXT_CSD_SANITIZE_START 165 /* W */
#define EXT_CSD_WR_REL_PARAM 166 /* RO */
#define EXT_CSD_RPMB_MULT 168 /* RO */
+#define EXT_CSD_FW_CONFIG 169 /* R/W */
#define EXT_CSD_BOOT_WP 173 /* R/W */
#define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */
#define EXT_CSD_PART_CONFIG 179 /* R/W */
#define EXT_CSD_GENERIC_CMD6_TIME 248 /* RO */
#define EXT_CSD_CACHE_SIZE 249 /* RO, 4 bytes */
#define EXT_CSD_PWR_CL_DDR_200_360 253 /* RO */
+#define EXT_CSD_FIRMWARE_VERSION 254 /* RO, 8 bytes */
+#define EXT_CSD_SUPPORTED_MODE 493 /* RO */
#define EXT_CSD_TAG_UNIT_SIZE 498 /* RO */
#define EXT_CSD_DATA_TAG_SUPPORT 499 /* RO */
#define EXT_CSD_MAX_PACKED_WRITES 500 /* RO */
#define SDHCI_QUIRK2_BROKEN_DDR50 (1<<7)
/* Stop command (CMD12) can set Transfer Complete when not using MMC_RSP_BUSY */
#define SDHCI_QUIRK2_STOP_WITH_TC (1<<8)
+/* Controller does not support 64-bit DMA */
+#define SDHCI_QUIRK2_BROKEN_64_BIT_DMA (1<<9)
+/* need clear transfer mode register before send cmd */
+#define SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD (1<<10)
+/* Capability register bit-63 indicates HS400 support */
+#define SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 (1<<11)
int irq; /* Device IRQ */
void __iomem *ioaddr; /* Mapped address */
#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */
#define SDHCI_SDR104_NEEDS_TUNING (1<<10) /* SDR104/HS200 needs tuning */
#define SDHCI_USING_RETUNING_TIMER (1<<11) /* Host is using a retuning timer for the card */
+#define SDHCI_USE_64_BIT_DMA (1<<12) /* Use 64-bit DMA */
unsigned int version; /* SDHCI spec. version */
int sg_count; /* Mapped sg entries */
- u8 *adma_desc; /* ADMA descriptor table */
- u8 *align_buffer; /* Bounce buffer */
+ void *adma_table; /* ADMA descriptor table */
+ void *align_buffer; /* Bounce buffer */
+
+ size_t adma_table_sz; /* ADMA descriptor table size */
+ size_t align_buffer_sz; /* Bounce buffer size */
dma_addr_t adma_addr; /* Mapped ADMA descr. table */
dma_addr_t align_addr; /* Mapped bounce buffer */
+ unsigned int desc_sz; /* ADMA descriptor size */
+ unsigned int align_sz; /* ADMA alignment */
+ unsigned int align_mask; /* ADMA alignment mask */
+
struct tasklet_struct finish_tasklet; /* Tasklet structures */
struct timer_list timer; /* Timer for timeouts */
struct device_driver drv;
};
-#define to_sdio_driver(d) container_of(d, struct sdio_driver, drv)
-
/**
* SDIO_DEVICE - macro used to describe a specific SDIO device
* @vend: the 16 bit manufacturer code
*/
int nr_migrate_reserve_block;
+#ifdef CONFIG_MEMORY_ISOLATION
+ /*
+ * Number of isolated pageblock. It is used to solve incorrect
+ * freepage counting problem due to racy retrieving migratetype
+ * of pageblock. Protected by zone->lock.
+ */
+ unsigned long nr_isolate_pageblock;
+#endif
+
#ifdef CONFIG_MEMORY_HOTPLUG
/* see spanned/present_pages for more description */
seqlock_t span_seqlock;
unsigned int status;
};
+static inline void
+nfs_free_pnfs_ds_cinfo(struct pnfs_ds_commit_info *cinfo)
+{
+ kfree(cinfo->buckets);
+}
+
#else
struct pnfs_ds_commit_info {
};
+static inline void
+nfs_free_pnfs_ds_cinfo(struct pnfs_ds_commit_info *cinfo)
+{
+}
+
#endif /* CONFIG_NFS_V4_1 */
#ifdef CONFIG_NFS_V4_2
extern int of_property_read_string(struct device_node *np,
const char *propname,
const char **out_string);
-extern int of_property_read_string_index(struct device_node *np,
- const char *propname,
- int index, const char **output);
extern int of_property_match_string(struct device_node *np,
const char *propname,
const char *string);
-extern int of_property_count_strings(struct device_node *np,
- const char *propname);
+extern int of_property_read_string_helper(struct device_node *np,
+ const char *propname,
+ const char **out_strs, size_t sz, int index);
extern int of_device_is_compatible(const struct device_node *device,
const char *);
extern int of_device_is_available(const struct device_node *device);
return -ENOSYS;
}
-static inline int of_property_read_string_index(struct device_node *np,
- const char *propname, int index,
- const char **out_string)
-{
- return -ENOSYS;
-}
-
-static inline int of_property_count_strings(struct device_node *np,
- const char *propname)
+static inline int of_property_read_string_helper(struct device_node *np,
+ const char *propname,
+ const char **out_strs, size_t sz, int index)
{
return -ENOSYS;
}
return of_property_count_elems_of_size(np, propname, sizeof(u64));
}
+/**
+ * of_property_read_string_array() - Read an array of strings from a multiple
+ * strings property.
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @out_strs: output array of string pointers.
+ * @sz: number of array elements to read.
+ *
+ * Search for a property in a device tree node and retrieve a list of
+ * terminated string values (pointer to data, not a copy) in that property.
+ *
+ * If @out_strs is NULL, the number of strings in the property is returned.
+ */
+static inline int of_property_read_string_array(struct device_node *np,
+ const char *propname, const char **out_strs,
+ size_t sz)
+{
+ return of_property_read_string_helper(np, propname, out_strs, sz, 0);
+}
+
+/**
+ * of_property_count_strings() - Find and return the number of strings from a
+ * multiple strings property.
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ *
+ * Search for a property in a device tree node and retrieve the number of null
+ * terminated string contain in it. Returns the number of strings on
+ * success, -EINVAL if the property does not exist, -ENODATA if property
+ * does not have a value, and -EILSEQ if the string is not null-terminated
+ * within the length of the property data.
+ */
+static inline int of_property_count_strings(struct device_node *np,
+ const char *propname)
+{
+ return of_property_read_string_helper(np, propname, NULL, 0, 0);
+}
+
+/**
+ * of_property_read_string_index() - Find and read a string from a multiple
+ * strings property.
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @index: index of the string in the list of strings
+ * @out_string: pointer to null terminated return string, modified only if
+ * return value is 0.
+ *
+ * Search for a property in a device tree node and retrieve a null
+ * terminated string value (pointer to data, not a copy) in the list of strings
+ * contained in that property.
+ * Returns 0 on success, -EINVAL if the property does not exist, -ENODATA if
+ * property does not have a value, and -EILSEQ if the string is not
+ * null-terminated within the length of the property data.
+ *
+ * The out_string pointer is modified only if a valid string can be decoded.
+ */
+static inline int of_property_read_string_index(struct device_node *np,
+ const char *propname,
+ int index, const char **output)
+{
+ int rc = of_property_read_string_helper(np, propname, output, 1, index);
+ return rc < 0 ? rc : 0;
+}
+
/**
* of_property_read_bool - Findfrom a property
* @np: device node from which the property value is to be read.
/* CONFIG_OF_RESOLVE api */
extern int of_resolve_phandles(struct device_node *tree);
+/**
+ * of_device_is_system_power_controller - Tells if system-power-controller is found for device_node
+ * @np: Pointer to the given device_node
+ *
+ * return true if present false otherwise
+ */
+static inline bool of_device_is_system_power_controller(const struct device_node *np)
+{
+ return of_property_read_bool(np, "system-power-controller");
+}
+
#endif /* _LINUX_OF_H */
#define __LINUX_PAGEISOLATION_H
#ifdef CONFIG_MEMORY_ISOLATION
+static inline bool has_isolate_pageblock(struct zone *zone)
+{
+ return zone->nr_isolate_pageblock;
+}
static inline bool is_migrate_isolate_page(struct page *page)
{
return get_pageblock_migratetype(page) == MIGRATE_ISOLATE;
return migratetype == MIGRATE_ISOLATE;
}
#else
+static inline bool has_isolate_pageblock(struct zone *zone)
+{
+ return false;
+}
static inline bool is_migrate_isolate_page(struct page *page)
{
return false;
if (pci_is_root_bus(pbus))
dev = pbus->bridge;
- else
+ else {
+ /* If pbus is a virtual bus, there is no bridge to it */
+ if (!pbus->self)
+ return NULL;
+
dev = &pbus->self->dev;
+ }
return ACPI_HANDLE(dev);
}
unsigned int is_added:1;
unsigned int is_busmaster:1; /* device is busmaster */
unsigned int no_msi:1; /* device may not use msi */
+ unsigned int no_64bit_msi:1; /* device may only use 32-bit MSIs */
unsigned int block_cfg_access:1; /* config space access is blocked */
unsigned int broken_parity_status:1; /* Device generates false positive parity */
unsigned int irq_reroute_variant:2; /* device needs IRQ rerouting variant */
#define PCI_DEVICE_ID_AMD_15H_M10H_F3 0x1403
#define PCI_DEVICE_ID_AMD_15H_M30H_NB_F3 0x141d
#define PCI_DEVICE_ID_AMD_15H_M30H_NB_F4 0x141e
+#define PCI_DEVICE_ID_AMD_15H_M60H_NB_F3 0x1573
+#define PCI_DEVICE_ID_AMD_15H_M60H_NB_F4 0x1574
#define PCI_DEVICE_ID_AMD_15H_NB_F0 0x1600
#define PCI_DEVICE_ID_AMD_15H_NB_F1 0x1601
#define PCI_DEVICE_ID_AMD_15H_NB_F2 0x1602
/* paired with smp_store_release() in percpu_ref_reinit() */
smp_read_barrier_depends();
- if (unlikely(percpu_ptr & __PERCPU_REF_ATOMIC))
+ /*
+ * Theoretically, the following could test just ATOMIC; however,
+ * then we'd have to mask off DEAD separately as DEAD may be
+ * visible without ATOMIC if we race with percpu_ref_kill(). DEAD
+ * implies ATOMIC anyway. Test them together.
+ */
+ if (unlikely(percpu_ptr & __PERCPU_REF_ATOMIC_DEAD))
return false;
*percpu_countp = (unsigned long __percpu *)percpu_ptr;
--- /dev/null
+/*
+ * MMC definitions for OMAP2
+ *
+ * Copyright (C) 2006 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * struct omap_hsmmc_dev_attr.flags possibilities
+ *
+ * OMAP_HSMMC_SUPPORTS_DUAL_VOLT: Some HSMMC controller instances can
+ * operate with either 1.8Vdc or 3.0Vdc card voltages; this flag
+ * should be set if this is the case. See for example Section 22.5.3
+ * "MMC/SD/SDIO1 Bus Voltage Selection" of the OMAP34xx Multimedia
+ * Device Silicon Revision 3.1.x Revision ZR (July 2011) (SWPU223R).
+ *
+ * OMAP_HSMMC_BROKEN_MULTIBLOCK_READ: Multiple-block read transfers
+ * don't work correctly on some MMC controller instances on some
+ * OMAP3 SoCs; this flag should be set if this is the case. See
+ * for example Advisory 2.1.1.128 "MMC: Multiple Block Read
+ * Operation Issue" in _OMAP3530/3525/3515/3503 Silicon Errata_
+ * Revision F (October 2010) (SPRZ278F).
+ */
+#define OMAP_HSMMC_SUPPORTS_DUAL_VOLT BIT(0)
+#define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ BIT(1)
+#define OMAP_HSMMC_SWAKEUP_MISSING BIT(2)
+
+struct omap_hsmmc_dev_attr {
+ u8 flags;
+};
+
+struct mmc_card;
+
+struct omap_hsmmc_platform_data {
+ /* back-link to device */
+ struct device *dev;
+
+ /* set if your board has components or wiring that limits the
+ * maximum frequency on the MMC bus */
+ unsigned int max_freq;
+
+ /* Integrating attributes from the omap_hwmod layer */
+ u8 controller_flags;
+
+ /* Register offset deviation */
+ u16 reg_offset;
+
+ /*
+ * 4/8 wires and any additional host capabilities
+ * need to OR'd all capabilities (ref. linux/mmc/host.h)
+ */
+ u32 caps; /* Used for the MMC driver on 2430 and later */
+ u32 pm_caps; /* PM capabilities of the mmc */
+
+ /* switch pin can be for card detect (default) or card cover */
+ unsigned cover:1;
+
+ /* use the internal clock */
+ unsigned internal_clock:1;
+
+ /* nonremovable e.g. eMMC */
+ unsigned nonremovable:1;
+
+ /* eMMC does not handle power off when not in sleep state */
+ unsigned no_regulator_off_init:1;
+
+ /* we can put the features above into this variable */
+#define HSMMC_HAS_PBIAS (1 << 0)
+#define HSMMC_HAS_UPDATED_RESET (1 << 1)
+#define HSMMC_HAS_HSPE_SUPPORT (1 << 2)
+ unsigned features;
+
+ int switch_pin; /* gpio (card detect) */
+ int gpio_wp; /* gpio (write protect) */
+
+ int (*set_power)(struct device *dev, int power_on, int vdd);
+ void (*remux)(struct device *dev, int power_on);
+ /* Call back before enabling / disabling regulators */
+ void (*before_set_reg)(struct device *dev, int power_on, int vdd);
+ /* Call back after enabling / disabling regulators */
+ void (*after_set_reg)(struct device *dev, int power_on, int vdd);
+ /* if we have special card, init it using this callback */
+ void (*init_card)(struct mmc_card *card);
+
+ const char *name;
+ u32 ocr_mask;
+};
--- /dev/null
+#ifndef __MMC_ATMEL_MCI_H
+#define __MMC_ATMEL_MCI_H
+
+#include <linux/platform_data/dma-atmel.h>
+#include <linux/platform_data/dma-dw.h>
+
+/**
+ * struct mci_dma_data - DMA data for MCI interface
+ */
+struct mci_dma_data {
+#ifdef CONFIG_ARM
+ struct at_dma_slave sdata;
+#else
+ struct dw_dma_slave sdata;
+#endif
+};
+
+/* accessor macros */
+#define slave_data_ptr(s) (&(s)->sdata)
+#define find_slave_dev(s) ((s)->sdata.dma_dev)
+
+#endif /* __MMC_ATMEL_MCI_H */
#define OMAP_MMC_MAX_SLOTS 2
-/*
- * struct omap_mmc_dev_attr.flags possibilities
- *
- * OMAP_HSMMC_SUPPORTS_DUAL_VOLT: Some HSMMC controller instances can
- * operate with either 1.8Vdc or 3.0Vdc card voltages; this flag
- * should be set if this is the case. See for example Section 22.5.3
- * "MMC/SD/SDIO1 Bus Voltage Selection" of the OMAP34xx Multimedia
- * Device Silicon Revision 3.1.x Revision ZR (July 2011) (SWPU223R).
- *
- * OMAP_HSMMC_BROKEN_MULTIBLOCK_READ: Multiple-block read transfers
- * don't work correctly on some MMC controller instances on some
- * OMAP3 SoCs; this flag should be set if this is the case. See
- * for example Advisory 2.1.1.128 "MMC: Multiple Block Read
- * Operation Issue" in _OMAP3530/3525/3515/3503 Silicon Errata_
- * Revision F (October 2010) (SPRZ278F).
- */
-#define OMAP_HSMMC_SUPPORTS_DUAL_VOLT BIT(0)
-#define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ BIT(1)
-#define OMAP_HSMMC_SWAKEUP_MISSING BIT(2)
-
struct mmc_card;
-struct omap_mmc_dev_attr {
- u8 flags;
-};
-
struct omap_mmc_platform_data {
/* back-link to device */
struct device *dev;
unsigned vcc_aux_disable_is_sleep:1;
/* we can put the features above into this variable */
-#define HSMMC_HAS_PBIAS (1 << 0)
-#define HSMMC_HAS_UPDATED_RESET (1 << 1)
-#define HSMMC_HAS_HSPE_SUPPORT (1 << 2)
#define MMC_OMAP7XX (1 << 3)
#define MMC_OMAP15XX (1 << 4)
#define MMC_OMAP16XX (1 << 5)
unsigned int quirks2;
unsigned int pm_caps;
};
-
-struct sdhci_pxa {
- u8 clk_enable;
- u8 power_mode;
-};
#endif /* _PXA_SDHCI_H_ */
unsigned int dma_rx_timeout;
unsigned int autosuspend_timeout;
unsigned int dma_rx_poll_rate;
- int DTR_gpio;
- int DTR_inverted;
- int DTR_present;
int (*get_context_loss_count)(struct device *);
void (*enable_wakeup)(struct device *, bool);
bool max_off_time_changed;
bool cached_power_down_ok;
struct gpd_cpuidle_data *cpuidle_data;
- void (*attach_dev)(struct device *dev);
- void (*detach_dev)(struct device *dev);
+ int (*attach_dev)(struct generic_pm_domain *domain,
+ struct device *dev);
+ void (*detach_dev)(struct generic_pm_domain *domain,
+ struct device *dev);
};
static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)
struct notifier_block nb;
struct mutex lock;
unsigned int refcount;
- bool need_restore;
+ int need_restore;
};
#ifdef CONFIG_PM_GENERIC_DOMAINS
struct device *dev;
struct charger_desc *desc;
- struct power_supply *fuel_gauge;
- struct power_supply **charger_stat;
-
#ifdef CONFIG_THERMAL
struct thermal_zone_device *tzd_batt;
#endif
void (*external_power_changed)(struct power_supply *psy);
void (*set_charged)(struct power_supply *psy);
+ /*
+ * Set if thermal zone should not be created for this power supply.
+ * For example for virtual supplies forwarding calls to actual
+ * sensors or other supplies.
+ */
+ bool no_thermal;
/* For APM emulation, think legacy userspace. */
int use_for_apm;
struct regmap;
struct regmap_range_cfg;
struct regmap_field;
+struct snd_ac97;
/* An enum of all the supported cache types */
enum regcache_type {
struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id,
void __iomem *regs,
const struct regmap_config *config);
+struct regmap *regmap_init_ac97(struct snd_ac97 *ac97,
+ const struct regmap_config *config);
struct regmap *devm_regmap_init(struct device *dev,
const struct regmap_bus *bus,
struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id,
void __iomem *regs,
const struct regmap_config *config);
+struct regmap *devm_regmap_init_ac97(struct snd_ac97 *ac97,
+ const struct regmap_config *config);
+
+bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg);
/**
* regmap_init_mmio(): Initialise register map
* Data passed is "struct pre_voltage_change_data"
* ABORT_VOLTAGE_CHANGE Regulator voltage change failed for some reason.
* Data passed is old voltage cast to (void *).
+ * PRE_DISABLE Regulator is about to be disabled
+ * ABORT_DISABLE Regulator disable failed for some reason
*
* NOTE: These events can be OR'ed together when passed into handler.
*/
#define REGULATOR_EVENT_DISABLE 0x80
#define REGULATOR_EVENT_PRE_VOLTAGE_CHANGE 0x100
#define REGULATOR_EVENT_ABORT_VOLTAGE_CHANGE 0x200
+#define REGULATOR_EVENT_PRE_DISABLE 0x400
+#define REGULATOR_EVENT_ABORT_DISABLE 0x800
/**
* struct pre_voltage_change_data - Data sent with PRE_VOLTAGE_CHANGE event
static inline struct regulator *__must_check
regulator_get_exclusive(struct device *dev, const char *id)
{
- return NULL;
+ return ERR_PTR(-ENODEV);
}
static inline struct regulator *__must_check
*
* @enable_time: Time taken for initial enable of regulator (in uS).
* @off_on_delay: guard time (in uS), before re-enabling a regulator
+ *
+ * @of_map_mode: Maps a hardware mode defined in a DeviceTree to a standard mode
*/
struct regulator_desc {
const char *name;
unsigned int enable_time;
unsigned int off_on_delay;
+
+ unsigned int (*of_map_mode)(unsigned int mode);
};
/**
* NULL).
* @regmap: regmap to use for core regmap helpers if dev_get_regulator() is
* insufficient.
+ * @ena_gpio_initialized: GPIO controlling regulator enable was properly
+ * initialized, meaning that >= 0 is a valid gpio
+ * identifier and < 0 is a non existent gpio.
* @ena_gpio: GPIO controlling regulator enable.
* @ena_gpio_invert: Sense for GPIO enable control.
* @ena_gpio_flags: Flags to use when calling gpio_request_one()
struct device_node *of_node;
struct regmap *regmap;
+ bool ena_gpio_initialized;
int ena_gpio;
unsigned int ena_gpio_invert:1;
unsigned int ena_gpio_flags;
#ifndef __LINUX_OF_REG_H
#define __LINUX_OF_REG_H
+struct regulator_desc;
+
struct of_regulator_match {
const char *name;
void *driver_data;
struct regulator_init_data *init_data;
struct device_node *of_node;
+ const struct regulator_desc *desc;
};
#if defined(CONFIG_OF)
extern struct regulator_init_data
*of_get_regulator_init_data(struct device *dev,
- struct device_node *node);
+ struct device_node *node,
+ const struct regulator_desc *desc);
extern int of_regulator_match(struct device *dev, struct device_node *node,
struct of_regulator_match *matches,
unsigned int num_matches);
#else
static inline struct regulator_init_data
*of_get_regulator_init_data(struct device *dev,
- struct device_node *node)
+ struct device_node *node,
+ const struct regulator_desc *desc)
{
return NULL;
}
__ring_buffer_alloc((size), (flags), &__key); \
})
-int ring_buffer_wait(struct ring_buffer *buffer, int cpu);
+int ring_buffer_wait(struct ring_buffer *buffer, int cpu, bool full);
int ring_buffer_poll_wait(struct ring_buffer *buffer, int cpu,
struct file *filp, poll_table *poll_table);
#define MSG_EOF MSG_FIN
#define MSG_FASTOPEN 0x20000000 /* Send data in TCP SYN */
-#define MSG_CMSG_CLOEXEC 0x40000000 /* Set close_on_exit for file
+#define MSG_CMSG_CLOEXEC 0x40000000 /* Set close_on_exec for file
descriptor received through
SCM_RIGHTS */
#if defined(CONFIG_COMPAT)
* @list: used to maintain a list of currently available transports
* @name: the human-readable name of the transport
* @maxsize: transport provided maximum packet size
- * @pref: Preferences of this transport
* @def: set if this transport should be considered the default
* @create: member function to create a new connection on this transport
* @close: member function to discard a connection on this transport
int inet_ctl_sock_create(struct sock **sk, unsigned short family,
unsigned short type, unsigned char protocol,
struct net *net);
+int inet_recv_error(struct sock *sk, struct msghdr *msg, int len,
+ int *addr_len);
static inline void inet_ctl_sock_destroy(struct sock *sk)
{
/**
* struct nft_trans - nf_tables object update in transaction
*
- * @rcu_head: rcu head to defer release of transaction data
* @list: used internally
* @msg_type: message type
* @ctx: transaction context
* @data: internal information related to the transaction
*/
struct nft_trans {
- struct rcu_head rcu_head;
struct list_head list;
int msg_type;
struct nft_ctx ctx;
return iptunnel_handle_offloads(skb, udp_csum, type);
}
+static inline void udp_tunnel_gro_complete(struct sk_buff *skb, int nhoff)
+{
+ struct udphdr *uh;
+
+ uh = (struct udphdr *)(skb->data + nhoff - sizeof(struct udphdr));
+ skb_shinfo(skb)->gso_type |= uh->check ?
+ SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL;
+}
+
static inline void udp_tunnel_encap_enable(struct socket *sock)
{
#if IS_ENABLED(CONFIG_IPV6)
#define VNI_HASH_BITS 10
#define VNI_HASH_SIZE (1<<VNI_HASH_BITS)
+/* VXLAN protocol header */
+struct vxlanhdr {
+ __be32 vx_flags;
+ __be32 vx_vni;
+};
+
struct vxlan_sock;
typedef void (vxlan_rcv_t)(struct vxlan_sock *vh, struct sk_buff *skb, __be32 key);
__be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df,
__be16 src_port, __be16 dst_port, __be32 vni, bool xnet);
+static inline bool vxlan_gso_check(struct sk_buff *skb)
+{
+ if ((skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL) &&
+ (skb->inner_protocol_type != ENCAP_TYPE_ETHER ||
+ skb->inner_protocol != htons(ETH_P_TEB) ||
+ (skb_inner_mac_header(skb) - skb_transport_header(skb) !=
+ sizeof(struct udphdr) + sizeof(struct vxlanhdr))))
+ return false;
+
+ return true;
+}
+
/* IP header + UDP + VXLAN + Ethernet header */
#define VXLAN_HEADROOM (20 + 8 + 8 + 14)
/* IPv6 header + UDP + VXLAN + Ethernet header */
int fc_eh_device_reset(struct scsi_cmnd *);
int fc_eh_host_reset(struct scsi_cmnd *);
int fc_slave_alloc(struct scsi_device *);
-int fc_change_queue_depth(struct scsi_device *, int qdepth, int reason);
-int fc_change_queue_type(struct scsi_device *, int tag_type);
/*
* ELS/CT interface
/*
* scsi host template
*/
-extern int iscsi_change_queue_depth(struct scsi_device *sdev, int depth,
- int reason);
extern int iscsi_eh_abort(struct scsi_cmnd *sc);
extern int iscsi_eh_recover_target(struct scsi_cmnd *sc);
extern int iscsi_eh_session_reset(struct scsi_cmnd *sc);
struct scsi_core {
struct Scsi_Host *shost;
- struct mutex task_queue_flush;
- spinlock_t task_queue_lock;
- struct list_head task_queue;
- int task_queue_size;
-
- struct task_struct *queue_thread;
};
struct sas_ha_event {
struct asd_sas_port **sas_port; /* array of valid pointers, must be set */
int num_phys; /* must be set, gt 0, static */
- /* The class calls this to send a task for execution. */
- int lldd_max_execute_num;
- int lldd_queue_size;
int strict_wide_ports; /* both sas_addr and attached_sas_addr must match
* their siblings when forming wide ports */
struct sas_task {
struct domain_device *dev;
- struct list_head list;
spinlock_t task_state_lock;
unsigned task_state_flags;
int (*lldd_dev_found)(struct domain_device *);
void (*lldd_dev_gone)(struct domain_device *);
- int (*lldd_execute_task)(struct sas_task *, int num,
- gfp_t gfp_flags);
+ int (*lldd_execute_task)(struct sas_task *, gfp_t gfp_flags);
/* Task Management Functions. Must be called from process context. */
int (*lldd_abort_task)(struct sas_task *);
int sas_set_phy_speed(struct sas_phy *phy,
struct sas_phy_linkrates *rates);
int sas_phy_reset(struct sas_phy *phy, int hard_reset);
-int sas_queue_up(struct sas_task *task);
extern int sas_queuecommand(struct Scsi_Host * ,struct scsi_cmnd *);
extern int sas_target_alloc(struct scsi_target *);
extern int sas_slave_configure(struct scsi_device *);
-extern int sas_change_queue_depth(struct scsi_device *, int new_depth,
- int reason);
+extern int sas_change_queue_depth(struct scsi_device *, int new_depth);
extern int sas_change_queue_type(struct scsi_device *, int qt);
extern int sas_bios_param(struct scsi_device *,
struct block_device *,
#define MOVE_MEDIUM 0xa5
#define EXCHANGE_MEDIUM 0xa6
#define READ_12 0xa8
+#define SERVICE_ACTION_OUT_12 0xa9
#define WRITE_12 0xaa
-#define READ_MEDIA_SERIAL_NUMBER 0xab
+#define READ_MEDIA_SERIAL_NUMBER 0xab /* Obsolete with SPC-2 */
+#define SERVICE_ACTION_IN_12 0xab
#define WRITE_VERIFY_12 0xae
#define VERIFY_12 0xaf
#define SEARCH_HIGH_12 0xb0
#define VERIFY_16 0x8f
#define SYNCHRONIZE_CACHE_16 0x91
#define WRITE_SAME_16 0x93
-#define SERVICE_ACTION_IN 0x9e
+#define SERVICE_ACTION_BIDIRECTIONAL 0x9d
+#define SERVICE_ACTION_IN_16 0x9e
+#define SERVICE_ACTION_OUT_16 0x9f
/* values for service action in */
#define SAI_READ_CAPACITY_16 0x10
#define SAI_GET_LBA_STATUS 0x12
#define MI_REPORT_ALIASES 0x0b
#define MI_REPORT_SUPPORTED_OPERATION_CODES 0x0c
#define MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS 0x0d
-#define MI_REPORT_PRIORITY 0x0e
-#define MI_REPORT_TIMESTAMP 0x0f
+#define MI_REPORT_PRIORITY 0x0e
+#define MI_REPORT_TIMESTAMP 0x0f
#define MI_MANAGEMENT_PROTOCOL_IN 0x10
/* value for MI_REPORT_TARGET_PGS ext header */
#define MI_EXT_HDR_PARAM_FMT 0x20
volatile int phase;
};
+/* for scmd->flags */
+#define SCMD_TAGGED (1 << 0)
+
struct scsi_cmnd {
struct scsi_device *device;
struct list_head list; /* scsi_cmnd participates in queue lists */
* to be at an address < 16Mb). */
int result; /* Status code from lower level driver */
+ int flags; /* Command flags */
unsigned char tag; /* SCSI-II queued command tag */
};
size_t *offset, size_t *len);
extern void scsi_kunmap_atomic_sg(void *virt);
-extern int scsi_init_io(struct scsi_cmnd *cmd, gfp_t gfp_mask);
+extern int scsi_init_io(struct scsi_cmnd *cmd);
extern int scsi_dma_map(struct scsi_cmnd *cmd);
extern void scsi_dma_unmap(struct scsi_cmnd *cmd);
#define _SCSI_SCSI_DBG_H
struct scsi_cmnd;
+struct scsi_device;
struct scsi_sense_hdr;
extern void scsi_print_command(struct scsi_cmnd *);
-extern void __scsi_print_command(unsigned char *);
-extern void scsi_show_extd_sense(unsigned char, unsigned char);
-extern void scsi_show_sense_hdr(struct scsi_sense_hdr *);
-extern void scsi_print_sense_hdr(const char *, struct scsi_sense_hdr *);
-extern void scsi_cmd_print_sense_hdr(struct scsi_cmnd *, const char *,
- struct scsi_sense_hdr *);
-extern void scsi_print_sense(char *, struct scsi_cmnd *);
-extern void __scsi_print_sense(const char *name,
+extern void __scsi_print_command(const unsigned char *, size_t);
+extern void scsi_show_extd_sense(const struct scsi_device *, const char *,
+ unsigned char, unsigned char);
+extern void scsi_show_sense_hdr(const struct scsi_device *, const char *,
+ const struct scsi_sense_hdr *);
+extern void scsi_print_sense_hdr(const struct scsi_device *, const char *,
+ const struct scsi_sense_hdr *);
+extern void scsi_print_sense(const struct scsi_cmnd *);
+extern void __scsi_print_sense(const struct scsi_device *, const char *name,
const unsigned char *sense_buffer,
int sense_len);
-extern void scsi_show_result(int);
-extern void scsi_print_result(struct scsi_cmnd *);
-extern void scsi_print_status(unsigned char);
+extern void scsi_print_result(struct scsi_cmnd *, const char *, int);
+extern const char *scsi_hostbyte_string(int);
+extern const char *scsi_driverbyte_string(int);
+extern const char *scsi_mlreturn_string(int);
extern const char *scsi_sense_key_string(unsigned char);
-extern const char *scsi_extd_sense_format(unsigned char, unsigned char);
+extern const char *scsi_extd_sense_format(unsigned char, unsigned char,
+ const char **);
#endif /* _SCSI_SCSI_DBG_H */
unsigned ppr:1; /* Device supports PPR messages */
unsigned tagged_supported:1; /* Supports SCSI-II tagged queuing */
unsigned simple_tags:1; /* simple queue tag messages are enabled */
- unsigned ordered_tags:1;/* ordered queue tag messages are enabled */
unsigned was_reset:1; /* There was a bus reset on the bus for
* this device */
unsigned expecting_cc_ua:1; /* Expecting a CHECK_CONDITION/UNIT_ATTN
unsigned long sdev_data[0];
} __attribute__((aligned(sizeof(unsigned long))));
-struct scsi_dh_devlist {
- char *vendor;
- char *model;
-};
-
typedef void (*activate_complete)(void *, int);
struct scsi_device_handler {
/* Used by the infrastructure */
/* Filled by the hardware handler */
struct module *module;
const char *name;
- const struct scsi_dh_devlist *devlist;
int (*check_sense)(struct scsi_device *, struct scsi_sense_hdr *);
- int (*attach)(struct scsi_device *);
+ struct scsi_dh_data *(*attach)(struct scsi_device *);
void (*detach)(struct scsi_device *);
int (*activate)(struct scsi_device *, activate_complete, void *);
int (*prep_fn)(struct scsi_device *, struct request *);
struct scsi_device_handler *scsi_dh;
struct scsi_device *sdev;
struct kref kref;
- char buf[0];
};
#define to_scsi_device(d) \
#define sdev_dbg(sdev, fmt, a...) \
dev_dbg(&(sdev)->sdev_gendev, fmt, ##a)
+/*
+ * like scmd_printk, but the device name is passed in
+ * as a string pointer
+ */
+#define sdev_prefix_printk(l, sdev, p, fmt, a...) \
+ (p) ? \
+ sdev_printk(l, sdev, "[%s] " fmt, p, ##a) : \
+ sdev_printk(l, sdev, fmt, ##a)
+
#define scmd_printk(prefix, scmd, fmt, a...) \
(scmd)->request->rq_disk ? \
sdev_printk(prefix, (scmd)->device, "[%s] " fmt, \
#define __shost_for_each_device(sdev, shost) \
list_for_each_entry((sdev), &((shost)->__devices), siblings)
-extern void scsi_adjust_queue_depth(struct scsi_device *, int, int);
+extern int scsi_change_queue_depth(struct scsi_device *, int);
extern int scsi_track_queue_full(struct scsi_device *, int);
extern int scsi_set_medium_removal(struct scsi_device *, char);
struct scsi_device;
struct scsi_driver {
- struct module *owner;
struct device_driver gendrv;
void (*rescan)(struct device *);
u8 additional_length; /* always 0 for fixed sense format */
};
-static inline int scsi_sense_valid(struct scsi_sense_hdr *sshdr)
+static inline bool scsi_sense_valid(const struct scsi_sense_hdr *sshdr)
{
if (!sshdr)
- return 0;
+ return false;
return (sshdr->response_code & 0x70) == 0x70;
}
extern void scsi_report_bus_reset(struct Scsi_Host *, int);
extern void scsi_report_device_reset(struct Scsi_Host *, int, int);
extern int scsi_block_when_processing_errors(struct scsi_device *);
-extern int scsi_normalize_sense(const u8 *sense_buffer, int sb_len,
- struct scsi_sense_hdr *sshdr);
-extern int scsi_command_normalize_sense(struct scsi_cmnd *cmd,
- struct scsi_sense_hdr *sshdr);
+extern bool scsi_normalize_sense(const u8 *sense_buffer, int sb_len,
+ struct scsi_sense_hdr *sshdr);
+extern bool scsi_command_normalize_sense(const struct scsi_cmnd *cmd,
+ struct scsi_sense_hdr *sshdr);
-static inline int scsi_sense_is_deferred(struct scsi_sense_hdr *sshdr)
+static inline bool scsi_sense_is_deferred(const struct scsi_sense_hdr *sshdr)
{
return ((sshdr->response_code >= 0x70) && (sshdr->response_code & 1));
}
extern void scsi_build_sense_buffer(int desc, u8 *buf, u8 key, u8 asc, u8 ascq);
-/*
- * Reset request from external source
- */
-#define SCSI_TRY_RESET_DEVICE 1
-#define SCSI_TRY_RESET_BUS 2
-#define SCSI_TRY_RESET_HOST 3
-#define SCSI_TRY_RESET_TARGET 4
-
-extern int scsi_reset_provider(struct scsi_device *, int);
+extern int scsi_ioctl_reset(struct scsi_device *, int __user *);
struct scsi_eh_save {
/* saved state */
#define DISABLE_CLUSTERING 0
#define ENABLE_CLUSTERING 1
-enum {
- SCSI_QDEPTH_DEFAULT, /* default requested change, e.g. from sysfs */
- SCSI_QDEPTH_QFULL, /* scsi-ml requested due to queue full */
- SCSI_QDEPTH_RAMP_UP, /* scsi-ml requested due to threshold event */
-};
-
struct scsi_host_template {
struct module *module;
const char *name;
* Things currently recommended to be handled at this time include:
*
* 1. Setting the device queue depth. Proper setting of this is
- * described in the comments for scsi_adjust_queue_depth.
+ * described in the comments for scsi_change_queue_depth.
* 2. Determining if the device supports the various synchronous
* negotiation protocols. The device struct will already have
* responded to INQUIRY and the results of the standard items
*
* Status: OPTIONAL
*/
- int (* change_queue_depth)(struct scsi_device *, int, int);
+ int (* change_queue_depth)(struct scsi_device *, int);
/*
* Fill in this function to allow the changing of tag types
*/
unsigned char present;
+ /*
+ * Let the block layer assigns tags to all commands.
+ */
+ unsigned use_blk_tags:1;
+
+ /*
+ * Track QUEUE_FULL events and reduce queue depth on demand.
+ */
+ unsigned track_queue_depth:1;
+
/*
* This specifies the mode that a LLD supports.
*/
*/
unsigned skip_settle_delay:1;
- /*
- * True if we are using ordered write support.
- */
- unsigned ordered_tag:1;
-
/* True if the controller does not support WRITE SAME */
unsigned no_write_same:1;
short unsigned int sg_prot_tablesize;
unsigned int max_sectors;
unsigned long dma_boundary;
+ /*
+ * In scsi-mq mode, the number of hardware queues supported by the LLD.
+ *
+ * Note: it is assumed that each hardware queue has a queue depth of
+ * can_queue. In other words, the total queue depth per host
+ * is nr_hw_queues * can_queue.
+ */
+ unsigned nr_hw_queues;
/*
* Used to assign serial numbers to the cmds.
* Protected by the host lock.
unsigned active_mode:2;
unsigned unchecked_isa_dma:1;
unsigned use_clustering:1;
- unsigned use_blk_tcq:1;
/*
* Host has requested that no further requests come through for the
*/
unsigned reverse_ordering:1;
- /*
- * Ordered write support
- */
- unsigned ordered_tag:1;
-
/* Task mgmt function in progress */
unsigned tmf_in_progress:1;
unsigned char host_wwn[8]; // include NULL term.
} Scsi_FCTargAddress;
+int scsi_ioctl_block_when_processing_errors(struct scsi_device *sdev,
+ int cmd, bool ndelay);
extern int scsi_ioctl(struct scsi_device *, int, void __user *);
-extern int scsi_nonblockable_ioctl(struct scsi_device *sdev, int cmd,
- void __user *arg, int ndelay);
#endif /* __KERNEL__ */
#endif /* _SCSI_IOCTL_H */
#ifdef CONFIG_BLOCK
+int scsi_change_queue_type(struct scsi_device *sdev, int tag_type);
+
/**
* scsi_get_tag_type - get the type of tag the device supports
* @sdev: the scsi device
- *
- * Notes:
- * If the drive only supports simple tags, returns MSG_SIMPLE_TAG
- * if it supports all tag types, returns MSG_ORDERED_TAG.
*/
static inline int scsi_get_tag_type(struct scsi_device *sdev)
{
if (!sdev->tagged_supported)
return 0;
- if (sdev->ordered_tags)
- return MSG_ORDERED_TAG;
if (sdev->simple_tags)
return MSG_SIMPLE_TAG;
return 0;
{
switch (tag) {
case MSG_ORDERED_TAG:
- sdev->ordered_tags = 1;
- /* fall through */
case MSG_SIMPLE_TAG:
sdev->simple_tags = 1;
break;
case 0:
/* fall through */
default:
- sdev->ordered_tags = 0;
sdev->simple_tags = 0;
break;
}
}
-/**
- * scsi_activate_tcq - turn on tag command queueing
- * @SDpnt: device to turn on TCQ for
- * @depth: queue depth
- *
- * Notes:
- * Eventually, I hope depth would be the maximum depth
- * the device could cope with and the real queue depth
- * would be adjustable from 0 to depth.
- **/
-static inline void scsi_activate_tcq(struct scsi_device *sdev, int depth)
-{
- if (!sdev->tagged_supported)
- return;
-
- if (shost_use_blk_mq(sdev->host))
- queue_flag_set_unlocked(QUEUE_FLAG_QUEUED, sdev->request_queue);
- else if (!blk_queue_tagged(sdev->request_queue))
- blk_queue_init_tags(sdev->request_queue, depth,
- sdev->host->bqt);
-
- scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
-}
-
-/**
- * scsi_deactivate_tcq - turn off tag command queueing
- * @SDpnt: device to turn off TCQ for
- **/
-static inline void scsi_deactivate_tcq(struct scsi_device *sdev, int depth)
-{
- if (blk_queue_tagged(sdev->request_queue))
- blk_queue_free_tags(sdev->request_queue);
- scsi_adjust_queue_depth(sdev, 0, depth);
-}
-
-/**
- * scsi_populate_tag_msg - place a tag message in a buffer
- * @SCpnt: pointer to the Scsi_Cmnd for the tag
- * @msg: pointer to the area to place the tag
- *
- * Notes:
- * designed to create the correct type of tag message for the
- * particular request. Returns the size of the tag message.
- * May return 0 if TCQ is disabled for this device.
- **/
-static inline int scsi_populate_tag_msg(struct scsi_cmnd *cmd, char *msg)
-{
- struct request *req = cmd->request;
-
- if (blk_rq_tagged(req)) {
- *msg++ = MSG_SIMPLE_TAG;
- *msg++ = req->tag;
- return 2;
- }
-
- return 0;
-}
static inline struct scsi_cmnd *scsi_mq_find_tag(struct Scsi_Host *shost,
- unsigned int hw_ctx, int tag)
+ int unique_tag)
{
- struct request *req;
+ u16 hwq = blk_mq_unique_tag_to_hwq(unique_tag);
+ struct request *req = NULL;
- req = blk_mq_tag_to_rq(shost->tag_set.tags[hw_ctx], tag);
+ if (hwq < shost->tag_set.nr_hw_queues)
+ req = blk_mq_tag_to_rq(shost->tag_set.tags[hwq],
+ blk_mq_unique_tag_to_tag(unique_tag));
return req ? (struct scsi_cmnd *)req->special : NULL;
}
/**
* scsi_find_tag - find a tagged command by device
* @SDpnt: pointer to the ScSI device
- * @tag: the tag number
+ * @tag: tag generated by blk_mq_unique_tag()
*
* Notes:
* Only works with tags allocated by the generic blk layer.
if (tag != SCSI_NO_TAG) {
if (shost_use_blk_mq(sdev->host))
- return scsi_mq_find_tag(sdev->host, 0, tag);
+ return scsi_mq_find_tag(sdev->host, tag);
- req = blk_queue_find_tag(sdev->request_queue, tag);
+ req = blk_queue_find_tag(sdev->request_queue, tag);
return req ? (struct scsi_cmnd *)req->special : NULL;
}
/**
* scsi_host_find_tag - find the tagged command by host
* @shost: pointer to scsi_host
- * @tag: tag of the scsi_cmnd
+ * @tag: tag generated by blk_mq_unique_tag()
*
* Notes:
* Only works with tags allocated by the generic blk layer.
if (tag != SCSI_NO_TAG) {
if (shost_use_blk_mq(shost))
- return scsi_mq_find_tag(shost, 0, tag);
+ return scsi_mq_find_tag(shost, tag);
req = blk_map_queue_find_tag(shost->bqt, tag);
return req ? (struct scsi_cmnd *)req->special : NULL;
}
int spi_populate_sync_msg(unsigned char *msg, int period, int offset);
int spi_populate_ppr_msg(unsigned char *msg, int period, int offset, int width,
int options);
+int spi_populate_tag_msg(unsigned char *msg, struct scsi_cmnd *cmd);
#endif /* SCSI_TRANSPORT_SPI_H */
/* Returns -EBUSY if occupied. 3rd argument pointer to int (see next) */
#define SG_SCSI_RESET 0x2284
-/* Associated values that can be given to SG_SCSI_RESET follow */
+/* Associated values that can be given to SG_SCSI_RESET follow.
+ * SG_SCSI_RESET_NO_ESCALATE may be OR-ed to the _DEVICE, _TARGET, _BUS
+ * or _HOST reset value so only that action is attempted. */
#define SG_SCSI_RESET_NOTHING 0
#define SG_SCSI_RESET_DEVICE 1
#define SG_SCSI_RESET_BUS 2
#define SG_SCSI_RESET_HOST 3
#define SG_SCSI_RESET_TARGET 4
+#define SG_SCSI_RESET_NO_ESCALATE 0x100
/* synchronous SCSI command ioctl, (only in version 3 interface) */
#define SG_IO 0x2285 /* similar effect as write() followed by read() */
#define SNDRV_PCM_FMTBIT_DSD_U8 _SNDRV_PCM_FMTBIT(DSD_U8)
#define SNDRV_PCM_FMTBIT_DSD_U16_LE _SNDRV_PCM_FMTBIT(DSD_U16_LE)
#define SNDRV_PCM_FMTBIT_DSD_U32_LE _SNDRV_PCM_FMTBIT(DSD_U32_LE)
+#define SNDRV_PCM_FMTBIT_DSD_U16_BE _SNDRV_PCM_FMTBIT(DSD_U16_BE)
+#define SNDRV_PCM_FMTBIT_DSD_U32_BE _SNDRV_PCM_FMTBIT(DSD_U32_BE)
#ifdef SNDRV_LITTLE_ENDIAN
#define SNDRV_PCM_FMTBIT_S16 SNDRV_PCM_FMTBIT_S16_LE
/* state and update */
enum snd_soc_dpcm_update runtime_update;
enum snd_soc_dpcm_state state;
+
+ int trigger_pending; /* trigger cmd + 1 if pending, 0 if not */
};
/* can this BE stop and free */
scsi_opcode_name(WRITE_16), \
scsi_opcode_name(VERIFY_16), \
scsi_opcode_name(WRITE_SAME_16), \
- scsi_opcode_name(SERVICE_ACTION_IN), \
+ scsi_opcode_name(SERVICE_ACTION_IN_16), \
scsi_opcode_name(SAI_READ_CAPACITY_16), \
scsi_opcode_name(SAI_GET_LBA_STATUS), \
scsi_opcode_name(MI_REPORT_TARGET_PGS), \
scsi_opcode_name(WRITE_16), \
scsi_opcode_name(VERIFY_16), \
scsi_opcode_name(WRITE_SAME_16), \
- scsi_opcode_name(SERVICE_ACTION_IN), \
+ scsi_opcode_name(SERVICE_ACTION_IN_16), \
scsi_opcode_name(SAI_READ_CAPACITY_16), \
scsi_opcode_name(SAI_GET_LBA_STATUS), \
scsi_opcode_name(MI_REPORT_TARGET_PGS), \
header-y += firewire-cdev.h
header-y += firewire-constants.h
header-y += flat.h
+header-y += fou.h
header-y += fs.h
header-y += fsl_hypervisor.h
header-y += fuse.h
header-y += hiddev.h
header-y += hidraw.h
header-y += hpet.h
+header-y += hsr_netlink.h
header-y += hyperv.h
header-y += hysdn_if.h
header-y += i2c-dev.h
header-y += minix_fs.h
header-y += mman.h
header-y += mmtimer.h
+header-y += mpls.h
header-y += mqueue.h
header-y += mroute.h
header-y += mroute6.h
header-y += virtio_pci.h
header-y += virtio_ring.h
header-y += virtio_rng.h
+header-y += vm_sockets.h
header-y += vt.h
header-y += wait.h
header-y += wanrouter.h
#define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl)
#define DM_VERSION_MAJOR 4
-#define DM_VERSION_MINOR 28
+#define DM_VERSION_MINOR 29
#define DM_VERSION_PATCHLEVEL 0
-#define DM_VERSION_EXTRA "-ioctl (2014-09-17)"
+#define DM_VERSION_EXTRA "-ioctl (2014-10-28)"
/* Status bits */
#define DM_READONLY_FLAG (1 << 0) /* In/Out */
*/
#define DM_DEFERRED_REMOVE (1 << 17) /* In/Out */
+/*
+ * If set, the device is suspended internally.
+ */
+#define DM_INTERNAL_SUSPEND_FLAG (1 << 18) /* Out */
+
#endif /* _LINUX_DM_IOCTL_H */
#define EM_MN10300 89 /* Panasonic/MEI MN10300, AM33 */
#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */
#define EM_BLACKFIN 106 /* ADI Blackfin Processor */
+#define EM_ALTERA_NIOS2 113 /* Altera Nios II soft-core processor */
#define EM_TI_C6000 140 /* TI C6X DSPs */
#define EM_AARCH64 183 /* ARM 64 bit */
#define EM_FRV 0x5441 /* Fujitsu FR-V */
#define NT_ARM_TLS 0x401 /* ARM TLS register */
#define NT_ARM_HW_BREAK 0x402 /* ARM hardware breakpoint registers */
#define NT_ARM_HW_WATCH 0x403 /* ARM hardware watchpoint registers */
+#define NT_ARM_SYSTEM_CALL 0x404 /* ARM system call number */
#define NT_METAG_CBUF 0x500 /* Metag catch buffer registers */
#define NT_METAG_RPIPE 0x501 /* Metag read pipeline state */
#define NT_METAG_TLS 0x502 /* Metag TLS pointer */
#include <linux/types.h>
#include <linux/if_ether.h>
+#include <linux/in6.h>
#define SYSFS_BRIDGE_ATTR "bridge"
#define SYSFS_BRIDGE_FDB "brforward"
#define SNDRV_PCM_FORMAT_DSD_U8 ((__force snd_pcm_format_t) 48) /* DSD, 1-byte samples DSD (x8) */
#define SNDRV_PCM_FORMAT_DSD_U16_LE ((__force snd_pcm_format_t) 49) /* DSD, 2-byte samples DSD (x16), little endian */
#define SNDRV_PCM_FORMAT_DSD_U32_LE ((__force snd_pcm_format_t) 50) /* DSD, 4-byte samples DSD (x32), little endian */
-#define SNDRV_PCM_FORMAT_LAST SNDRV_PCM_FORMAT_DSD_U32_LE
+#define SNDRV_PCM_FORMAT_DSD_U16_BE ((__force snd_pcm_format_t) 51) /* DSD, 2-byte samples DSD (x16), big endian */
+#define SNDRV_PCM_FORMAT_DSD_U32_BE ((__force snd_pcm_format_t) 52) /* DSD, 4-byte samples DSD (x32), big endian */
+#define SNDRV_PCM_FORMAT_LAST SNDRV_PCM_FORMAT_DSD_U32_BE
#ifdef SNDRV_LITTLE_ENDIAN
#define SNDRV_PCM_FORMAT_S16 SNDRV_PCM_FORMAT_S16_LE
static_command_line, __start___param,
__stop___param - __start___param,
-1, -1, &unknown_bootoption);
- if (after_dashes)
+ if (!IS_ERR_OR_NULL(after_dashes))
parse_args("Setting init args", after_dashes, NULL, 0, -1, -1,
set_init_arg);
return retval;
}
- id = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni);
- if (id < 0) {
- ipc_rcu_putref(sma, sem_rcu_free);
- return id;
- }
- ns->used_sems += nsems;
-
sma->sem_base = (struct sem *) &sma[1];
for (i = 0; i < nsems; i++) {
INIT_LIST_HEAD(&sma->list_id);
sma->sem_nsems = nsems;
sma->sem_ctime = get_seconds();
+
+ id = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni);
+ if (id < 0) {
+ ipc_rcu_putref(sma, sem_rcu_free);
+ return id;
+ }
+ ns->used_sems += nsems;
+
sem_unlock(sma, -1);
rcu_read_unlock();
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_FEATURE_CHANGE);
audit_log_task_info(ab, current);
- audit_log_format(ab, "feature=%s old=%u new=%u old_lock=%u new_lock=%u res=%d",
+ audit_log_format(ab, " feature=%s old=%u new=%u old_lock=%u new_lock=%u res=%d",
audit_feature_names[which], !!old_feature, !!new_feature,
!!old_lock, !!new_lock, res);
audit_log_end(ab);
chunk->owners[i].index = i;
}
fsnotify_init_mark(&chunk->mark, audit_tree_destroy_watch);
+ chunk->mark.mask = FS_IN_IGNORED;
return chunk;
}
if (!task) {
/*
- * Per cpu events are removed via an smp call and
- * the removal is always successful.
+ * Per cpu events are removed via an smp call. The removal can
+ * fail if the CPU is currently offline, but in that case we
+ * already called __perf_remove_from_context from
+ * perf_event_exit_cpu.
*/
cpu_function_call(event->cpu, __perf_remove_from_context, &re);
return;
static void __perf_event_exit_context(void *__info)
{
- struct remove_event re = { .detach_group = false };
+ struct remove_event re = { .detach_group = true };
struct perf_event_context *ctx = __info;
perf_pmu_rotate_stop(ctx->pmu);
if (__fatal_signal_pending(t) || arch_uprobe_xol_was_trapped(t)) {
utask->state = UTASK_SSTEP_TRAPPED;
set_tsk_thread_flag(t, TIF_UPROBE);
- set_tsk_thread_flag(t, TIF_NOTIFY_RESUME);
}
}
* 'I' - Working around severe firmware bug.
* 'O' - Out-of-tree module has been loaded.
* 'E' - Unsigned module has been loaded.
+ * 'L' - A soft lockup has previously occurred.
*
* The string is overwritten by the next call to print_tainted().
*/
static int platform_suspend_prepare_late(suspend_state_t state)
{
- return state == PM_SUSPEND_FREEZE && freeze_ops->prepare ?
+ return state == PM_SUSPEND_FREEZE && freeze_ops && freeze_ops->prepare ?
freeze_ops->prepare() : 0;
}
static void platform_resume_early(suspend_state_t state)
{
- if (state == PM_SUSPEND_FREEZE && freeze_ops->restore)
+ if (state == PM_SUSPEND_FREEZE && freeze_ops && freeze_ops->restore)
freeze_ops->restore();
}
EXPORT_PER_CPU_SYMBOL(kstat);
EXPORT_PER_CPU_SYMBOL(kernel_cpustat);
-/*
- * Return any ns on the sched_clock that have not yet been accounted in
- * @p in case that task is currently running.
- *
- * Called with task_rq_lock() held on @rq.
- */
-static u64 do_task_delta_exec(struct task_struct *p, struct rq *rq)
-{
- u64 ns = 0;
-
- /*
- * Must be ->curr _and_ ->on_rq. If dequeued, we would
- * project cycles that may never be accounted to this
- * thread, breaking clock_gettime().
- */
- if (task_current(rq, p) && task_on_rq_queued(p)) {
- update_rq_clock(rq);
- ns = rq_clock_task(rq) - p->se.exec_start;
- if ((s64)ns < 0)
- ns = 0;
- }
-
- return ns;
-}
-
-unsigned long long task_delta_exec(struct task_struct *p)
-{
- unsigned long flags;
- struct rq *rq;
- u64 ns = 0;
-
- rq = task_rq_lock(p, &flags);
- ns = do_task_delta_exec(p, rq);
- task_rq_unlock(rq, p, &flags);
-
- return ns;
-}
-
/*
* Return accounted runtime for the task.
* In case the task is currently running, return the runtime plus current's
{
unsigned long flags;
struct rq *rq;
- u64 ns = 0;
+ u64 ns;
#if defined(CONFIG_64BIT) && defined(CONFIG_SMP)
/*
#endif
rq = task_rq_lock(p, &flags);
- ns = p->se.sum_exec_runtime + do_task_delta_exec(p, rq);
+ /*
+ * Must be ->curr _and_ ->on_rq. If dequeued, we would
+ * project cycles that may never be accounted to this
+ * thread, breaking clock_gettime().
+ */
+ if (task_current(rq, p) && task_on_rq_queued(p)) {
+ update_rq_clock(rq);
+ p->sched_class->update_curr(rq);
+ }
+ ns = p->se.sum_exec_runtime;
task_rq_unlock(rq, p, &flags);
return ns;
* or we have been woken up remotely but the IPI has not yet arrived,
* we haven't yet exited the RCU idle mode. Do it here manually until
* we find a better solution.
+ *
+ * NB: There are buggy callers of this function. Ideally we
+ * should warn if prev_state != IN_USER, but that will trigger
+ * too frequently to make sense yet.
*/
- user_exit();
+ enum ctx_state prev_state = exception_enter();
schedule();
- user_enter();
+ exception_exit(prev_state);
}
#endif
if (!sched_debug())
break;
}
+
+ if (!level)
+ return;
+
/*
* 'level' contains the number of unique distances, excluding the
* identity distance node_distance(i,i).
if (unlikely(running))
put_prev_task(rq, tsk);
- tg = container_of(task_css_check(tsk, cpu_cgrp_id,
- lockdep_is_held(&tsk->sighand->siglock)),
+ /*
+ * All callers are synchronized by task_rq_lock(); we do not use RCU
+ * which is pointless here. Thus, we pass "true" to task_css_check()
+ * to prevent lockdep warnings.
+ */
+ tg = container_of(task_css_check(tsk, cpu_cgrp_id, true),
struct task_group, css);
tg = autogroup_task_group(tsk, tg);
tsk->sched_task_group = tg;
.prio_changed = prio_changed_dl,
.switched_from = switched_from_dl,
.switched_to = switched_to_dl,
+
+ .update_curr = update_curr_dl,
};
account_cfs_rq_runtime(cfs_rq, delta_exec);
}
+static void update_curr_fair(struct rq *rq)
+{
+ update_curr(cfs_rq_of(&rq->curr->se));
+}
+
static inline void
update_stats_wait_start(struct cfs_rq *cfs_rq, struct sched_entity *se)
{
cur = NULL;
raw_spin_unlock_irq(&dst_rq->lock);
+ /*
+ * Because we have preemption enabled we can get migrated around and
+ * end try selecting ourselves (current == env->p) as a swap candidate.
+ */
+ if (cur == env->p)
+ goto unlock;
+
/*
* "imp" is the fault differential for the source task between the
* source and destination node. Calculate the total differential for
.get_rr_interval = get_rr_interval_fair,
+ .update_curr = update_curr_fair,
+
#ifdef CONFIG_FAIR_GROUP_SCHED
.task_move_group = task_move_group_fair,
#endif
return 0;
}
+static void update_curr_idle(struct rq *rq)
+{
+}
+
/*
* Simple, special scheduling class for the per-CPU idle tasks:
*/
.prio_changed = prio_changed_idle,
.switched_to = switched_to_idle,
+ .update_curr = update_curr_idle,
};
.prio_changed = prio_changed_rt,
.switched_to = switched_to_rt,
+
+ .update_curr = update_curr_rt,
};
#ifdef CONFIG_SCHED_DEBUG
unsigned int (*get_rr_interval) (struct rq *rq,
struct task_struct *task);
+ void (*update_curr) (struct rq *rq);
+
#ifdef CONFIG_FAIR_GROUP_SCHED
void (*task_move_group) (struct task_struct *p, int on_rq);
#endif
return 0;
}
+static void update_curr_stop(struct rq *rq)
+{
+}
+
/*
* Simple, special scheduling class for the per-CPU stop tasks:
*/
.prio_changed = prio_changed_stop,
.switched_to = switched_to_stop,
+ .update_curr = update_curr_stop,
};
*sample = cputime_to_expires(cputime.utime);
break;
case CPUCLOCK_SCHED:
- *sample = cputime.sum_exec_runtime + task_delta_exec(p);
+ *sample = cputime.sum_exec_runtime;
break;
}
return 0;
* ring_buffer_wait - wait for input to the ring buffer
* @buffer: buffer to wait on
* @cpu: the cpu buffer to wait on
+ * @full: wait until a full page is available, if @cpu != RING_BUFFER_ALL_CPUS
*
* If @cpu == RING_BUFFER_ALL_CPUS then the task will wake up as soon
* as data is added to any of the @buffer's cpu buffers. Otherwise
* it will wait for data to be added to a specific cpu buffer.
*/
-int ring_buffer_wait(struct ring_buffer *buffer, int cpu)
+int ring_buffer_wait(struct ring_buffer *buffer, int cpu, bool full)
{
- struct ring_buffer_per_cpu *cpu_buffer;
+ struct ring_buffer_per_cpu *uninitialized_var(cpu_buffer);
DEFINE_WAIT(wait);
struct rb_irq_work *work;
+ int ret = 0;
/*
* Depending on what the caller is waiting for, either any
}
- prepare_to_wait(&work->waiters, &wait, TASK_INTERRUPTIBLE);
+ while (true) {
+ prepare_to_wait(&work->waiters, &wait, TASK_INTERRUPTIBLE);
- /*
- * The events can happen in critical sections where
- * checking a work queue can cause deadlocks.
- * After adding a task to the queue, this flag is set
- * only to notify events to try to wake up the queue
- * using irq_work.
- *
- * We don't clear it even if the buffer is no longer
- * empty. The flag only causes the next event to run
- * irq_work to do the work queue wake up. The worse
- * that can happen if we race with !trace_empty() is that
- * an event will cause an irq_work to try to wake up
- * an empty queue.
- *
- * There's no reason to protect this flag either, as
- * the work queue and irq_work logic will do the necessary
- * synchronization for the wake ups. The only thing
- * that is necessary is that the wake up happens after
- * a task has been queued. It's OK for spurious wake ups.
- */
- work->waiters_pending = true;
+ /*
+ * The events can happen in critical sections where
+ * checking a work queue can cause deadlocks.
+ * After adding a task to the queue, this flag is set
+ * only to notify events to try to wake up the queue
+ * using irq_work.
+ *
+ * We don't clear it even if the buffer is no longer
+ * empty. The flag only causes the next event to run
+ * irq_work to do the work queue wake up. The worse
+ * that can happen if we race with !trace_empty() is that
+ * an event will cause an irq_work to try to wake up
+ * an empty queue.
+ *
+ * There's no reason to protect this flag either, as
+ * the work queue and irq_work logic will do the necessary
+ * synchronization for the wake ups. The only thing
+ * that is necessary is that the wake up happens after
+ * a task has been queued. It's OK for spurious wake ups.
+ */
+ work->waiters_pending = true;
+
+ if (signal_pending(current)) {
+ ret = -EINTR;
+ break;
+ }
+
+ if (cpu == RING_BUFFER_ALL_CPUS && !ring_buffer_empty(buffer))
+ break;
+
+ if (cpu != RING_BUFFER_ALL_CPUS &&
+ !ring_buffer_empty_cpu(buffer, cpu)) {
+ unsigned long flags;
+ bool pagebusy;
+
+ if (!full)
+ break;
+
+ raw_spin_lock_irqsave(&cpu_buffer->reader_lock, flags);
+ pagebusy = cpu_buffer->reader_page == cpu_buffer->commit_page;
+ raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags);
+
+ if (!pagebusy)
+ break;
+ }
- if ((cpu == RING_BUFFER_ALL_CPUS && ring_buffer_empty(buffer)) ||
- (cpu != RING_BUFFER_ALL_CPUS && ring_buffer_empty_cpu(buffer, cpu)))
schedule();
+ }
finish_wait(&work->waiters, &wait);
- return 0;
+
+ return ret;
}
/**
}
#endif /* CONFIG_TRACER_MAX_TRACE */
-static int wait_on_pipe(struct trace_iterator *iter)
+static int wait_on_pipe(struct trace_iterator *iter, bool full)
{
/* Iterators are static, they should be filled or empty */
if (trace_buffer_iter(iter, iter->cpu_file))
return 0;
- return ring_buffer_wait(iter->trace_buffer->buffer, iter->cpu_file);
+ return ring_buffer_wait(iter->trace_buffer->buffer, iter->cpu_file,
+ full);
}
#ifdef CONFIG_FTRACE_STARTUP_TEST
mutex_unlock(&iter->mutex);
- ret = wait_on_pipe(iter);
+ ret = wait_on_pipe(iter, false);
mutex_lock(&iter->mutex);
if (ret)
return ret;
-
- if (signal_pending(current))
- return -EINTR;
}
return 1;
goto out_unlock;
}
mutex_unlock(&trace_types_lock);
- ret = wait_on_pipe(iter);
+ ret = wait_on_pipe(iter, false);
mutex_lock(&trace_types_lock);
if (ret) {
size = ret;
goto out_unlock;
}
- if (signal_pending(current)) {
- size = -EINTR;
- goto out_unlock;
- }
goto again;
}
size = 0;
};
struct buffer_ref *ref;
int entries, size, i;
- ssize_t ret;
+ ssize_t ret = 0;
mutex_lock(&trace_types_lock);
int r;
ref = kzalloc(sizeof(*ref), GFP_KERNEL);
- if (!ref)
+ if (!ref) {
+ ret = -ENOMEM;
break;
+ }
ref->ref = 1;
ref->buffer = iter->trace_buffer->buffer;
ref->page = ring_buffer_alloc_read_page(ref->buffer, iter->cpu_file);
if (!ref->page) {
+ ret = -ENOMEM;
kfree(ref);
break;
}
/* did we read anything? */
if (!spd.nr_pages) {
+ if (ret)
+ goto out;
+
if ((file->f_flags & O_NONBLOCK) || (flags & SPLICE_F_NONBLOCK)) {
ret = -EAGAIN;
goto out;
}
mutex_unlock(&trace_types_lock);
- ret = wait_on_pipe(iter);
+ ret = wait_on_pipe(iter, true);
mutex_lock(&trace_types_lock);
if (ret)
goto out;
- if (signal_pending(current)) {
- ret = -EINTR;
- goto out;
- }
+
goto again;
}
lib-y := ctype.o string.o vsprintf.o cmdline.o \
rbtree.o radix-tree.o dump_stack.o timerqueue.o\
idr.o int_sqrt.o extable.o \
- sha1.o md5.o irq_regs.o reciprocal_div.o argv_split.o \
+ sha1.o md5.o irq_regs.o argv_split.o \
proportions.o flex_proportions.o ratelimit.o show_mem.o \
is_single_threaded.o plist.o decompress.o kobject_uevent.o \
earlycpio.o
bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \
gcd.o lcm.o list_sort.o uuid.o flex_array.o iovec.o clz_ctz.o \
bsearch.o find_last_bit.o find_next_bit.o llist.o memweight.o kfifo.o \
- percpu-refcount.o percpu_ida.o hash.o rhashtable.o
+ percpu-refcount.o percpu_ida.o hash.o rhashtable.o reciprocal_div.o
obj-y += string_helpers.o
obj-$(CONFIG_TEST_STRING_HELPERS) += test-string_helpers.o
obj-y += kstrtox.o
return pool;
}
+EXPORT_SYMBOL(devm_gen_pool_create);
/**
* dev_get_gen_pool - Obtain the gen_pool (if any) for a device
ht->shift++;
/* For each new bucket, search the corresponding old bucket
- * for the first entry that hashes to the new bucket, and
+ * for the first entry that hashes to the new bucket, and
* link the new bucket to that entry. Since all the entries
* which will end up in the new bucket appear in the same
* old bucket, this constructs an entirely valid new hash
}
/* Publish the new table pointer. Lookups may now traverse
- * the new table, but they will not benefit from any
- * additional efficiency until later steps unzip the buckets.
+ * the new table, but they will not benefit from any
+ * additional efficiency until later steps unzip the buckets.
*/
rcu_assign_pointer(ht->tbl, new_tbl);
ht->shift--;
- /* Link each bucket in the new table to the first bucket
+ /* Link each bucket in the new table to the first bucket
* in the old table that contains entries which will hash
* to the new bucket.
*/
for (i = 0; i < ntbl->size; i++) {
ntbl->buckets[i] = tbl->buckets[i];
- /* Link each bucket in the new table to the first bucket
+ /* Link each bucket in the new table to the first bucket
* in the old table that contains entries which will hash
* to the new bucket.
*/
continue;
total += zone->present_pages;
- reserved = zone->present_pages - zone->managed_pages;
+ reserved += zone->present_pages - zone->managed_pages;
if (is_highmem_idx(zoneid))
highmem += zone->present_pages;
static int reset_managed_pages_done __initdata;
-static inline void __init reset_node_managed_pages(pg_data_t *pgdat)
+void reset_node_managed_pages(pg_data_t *pgdat)
{
struct zone *z;
- if (reset_managed_pages_done)
- return;
-
for (z = pgdat->node_zones; z < pgdat->node_zones + MAX_NR_ZONES; z++)
z->managed_pages = 0;
}
{
struct pglist_data *pgdat;
+ if (reset_managed_pages_done)
+ return;
+
for_each_online_pgdat(pgdat)
reset_node_managed_pages(pgdat);
+
reset_managed_pages_done = 1;
}
err:
kfree(cma->bitmap);
+ cma->count = 0;
return -EINVAL;
}
phys_addr_t highmem_start = __pa(high_memory);
int ret = 0;
- pr_debug("%s(size %lx, base %08lx, limit %08lx alignment %08lx)\n",
- __func__, (unsigned long)size, (unsigned long)base,
- (unsigned long)limit, (unsigned long)alignment);
+ pr_debug("%s(size %pa, base %pa, limit %pa alignment %pa)\n",
+ __func__, &size, &base, &limit, &alignment);
if (cma_area_count == ARRAY_SIZE(cma_areas)) {
pr_err("Not enough slots for CMA reserved regions!\n");
size = ALIGN(size, alignment);
limit &= ~(alignment - 1);
+ if (!base)
+ fixed = false;
+
/* size should be aligned with order_per_bit */
if (!IS_ALIGNED(size >> PAGE_SHIFT, 1 << order_per_bit))
return -EINVAL;
/*
- * adjust limit to avoid crossing low/high memory boundary for
- * automatically allocated regions
+ * If allocating at a fixed base the request region must not cross the
+ * low/high memory boundary.
*/
- if (((limit == 0 || limit > memblock_end) &&
- (memblock_end - size < highmem_start &&
- memblock_end > highmem_start)) ||
- (!fixed && limit > highmem_start && limit - size < highmem_start)) {
- limit = highmem_start;
- }
-
- if (fixed && base < highmem_start && base+size > highmem_start) {
+ if (fixed && base < highmem_start && base + size > highmem_start) {
ret = -EINVAL;
- pr_err("Region at %08lx defined on low/high memory boundary (%08lx)\n",
- (unsigned long)base, (unsigned long)highmem_start);
+ pr_err("Region at %pa defined on low/high memory boundary (%pa)\n",
+ &base, &highmem_start);
goto err;
}
+ /*
+ * If the limit is unspecified or above the memblock end, its effective
+ * value will be the memblock end. Set it explicitly to simplify further
+ * checks.
+ */
+ if (limit == 0 || limit > memblock_end)
+ limit = memblock_end;
+
/* Reserve memory */
- if (base && fixed) {
+ if (fixed) {
if (memblock_is_region_reserved(base, size) ||
memblock_reserve(base, size) < 0) {
ret = -EBUSY;
goto err;
}
} else {
- phys_addr_t addr = memblock_alloc_range(size, alignment, base,
- limit);
+ phys_addr_t addr = 0;
+
+ /*
+ * All pages in the reserved area must come from the same zone.
+ * If the requested region crosses the low/high memory boundary,
+ * try allocating from high memory first and fall back to low
+ * memory in case of failure.
+ */
+ if (base < highmem_start && limit > highmem_start) {
+ addr = memblock_alloc_range(size, alignment,
+ highmem_start, limit);
+ limit = highmem_start;
+ }
+
if (!addr) {
- ret = -ENOMEM;
- goto err;
- } else {
- base = addr;
+ addr = memblock_alloc_range(size, alignment, base,
+ limit);
+ if (!addr) {
+ ret = -ENOMEM;
+ goto err;
+ }
}
+
+ base = addr;
}
ret = cma_init_reserved_mem(base, size, order_per_bit, res_cma);
if (ret)
goto err;
- pr_info("Reserved %ld MiB at %08lx\n", (unsigned long)size / SZ_1M,
- (unsigned long)base);
+ pr_info("Reserved %ld MiB at %pa\n", (unsigned long)size / SZ_1M,
+ &base);
return 0;
err:
block_end_pfn = min(block_end_pfn, end_pfn);
+ /*
+ * pfn could pass the block_end_pfn if isolated freepage
+ * is more than pageblock order. In this case, we adjust
+ * scanning range to right one.
+ */
+ if (pfn >= block_end_pfn) {
+ block_end_pfn = ALIGN(pfn + 1, pageblock_nr_pages);
+ block_end_pfn = min(block_end_pfn, end_pfn);
+ }
+
if (!pageblock_pfn_to_page(pfn, block_end_pfn, cc->zone))
break;
}
acct_isolated(zone, cc);
- /* Record where migration scanner will be restarted */
- cc->migrate_pfn = low_pfn;
+ /*
+ * Record where migration scanner will be restarted. If we end up in
+ * the same pageblock as the free scanner, make the scanners fully
+ * meet so that compact_finished() terminates compaction.
+ */
+ cc->migrate_pfn = (end_pfn <= cc->free_pfn) ? low_pfn : cc->free_pfn;
return cc->nr_migratepages ? ISOLATE_SUCCESS : ISOLATE_NONE;
}
the (older) page from frontswap
*/
inc_frontswap_failed_stores();
- if (dup)
+ if (dup) {
__frontswap_clear(sis, offset);
+ frontswap_ops->invalidate_page(type, offset);
+ }
}
if (frontswap_writethrough_enabled)
/* report failure so swap also writes to swap device */
/*
* in mm/page_alloc.c
*/
+
+/*
+ * Locate the struct page for both the matching buddy in our
+ * pair (buddy1) and the combined O(n+1) page they form (page).
+ *
+ * 1) Any buddy B1 will have an order O twin B2 which satisfies
+ * the following equation:
+ * B2 = B1 ^ (1 << O)
+ * For example, if the starting buddy (buddy2) is #8 its order
+ * 1 buddy is #10:
+ * B2 = 8 ^ (1 << 1) = 8 ^ 2 = 10
+ *
+ * 2) Any buddy B will have an order O+1 parent P which
+ * satisfies the following equation:
+ * P = B & ~(1 << O)
+ *
+ * Assumption: *_mem_map is contiguous at least up to MAX_ORDER
+ */
+static inline unsigned long
+__find_buddy_index(unsigned long page_idx, unsigned int order)
+{
+ return page_idx ^ (1 << order);
+}
+
+extern int __isolate_free_page(struct page *page, unsigned int order);
extern void __free_pages_bootmem(struct page *page, unsigned int order);
extern void prep_compound_page(struct page *page, unsigned long order);
#ifdef CONFIG_MEMORY_FAILURE
if (i->nr_segs == 1)
return i->count;
else if (i->type & ITER_BVEC)
- return min(i->count, i->iov->iov_len - i->iov_offset);
- else
return min(i->count, i->bvec->bv_len - i->iov_offset);
+ else
+ return min(i->count, i->iov->iov_len - i->iov_offset);
}
EXPORT_SYMBOL(iov_iter_single_seg_count);
/* Is it from 0 to ~0? */
tlb->fullmm = !(start | (end+1));
tlb->need_flush_all = 0;
- tlb->start = start;
- tlb->end = end;
- tlb->need_flush = 0;
tlb->local.next = NULL;
tlb->local.nr = 0;
tlb->local.max = ARRAY_SIZE(tlb->__pages);
#ifdef CONFIG_HAVE_RCU_TABLE_FREE
tlb->batch = NULL;
#endif
+
+ __tlb_reset_range(tlb);
}
static void tlb_flush_mmu_tlbonly(struct mmu_gather *tlb)
{
- tlb->need_flush = 0;
+ if (!tlb->end)
+ return;
+
tlb_flush(tlb);
#ifdef CONFIG_HAVE_RCU_TABLE_FREE
tlb_table_flush(tlb);
#endif
+ __tlb_reset_range(tlb);
}
static void tlb_flush_mmu_free(struct mmu_gather *tlb)
void tlb_flush_mmu(struct mmu_gather *tlb)
{
- if (!tlb->need_flush)
- return;
tlb_flush_mmu_tlbonly(tlb);
tlb_flush_mmu_free(tlb);
}
{
struct mmu_gather_batch *batch;
- VM_BUG_ON(!tlb->need_flush);
+ VM_BUG_ON(!tlb->end);
batch = tlb->active;
batch->pages[batch->nr++] = page;
{
struct mmu_table_batch **batch = &tlb->batch;
- tlb->need_flush = 1;
-
/*
* When there's less then two users of this mm there cannot be a
* concurrent page-table walk.
if (!pte_file(pte)) {
swp_entry_t entry = pte_to_swp_entry(pte);
- if (swap_duplicate(entry) < 0)
- return entry.val;
-
- /* make sure dst_mm is on swapoff's mmlist. */
- if (unlikely(list_empty(&dst_mm->mmlist))) {
- spin_lock(&mmlist_lock);
- if (list_empty(&dst_mm->mmlist))
- list_add(&dst_mm->mmlist,
- &src_mm->mmlist);
- spin_unlock(&mmlist_lock);
- }
- if (likely(!non_swap_entry(entry)))
+ if (likely(!non_swap_entry(entry))) {
+ if (swap_duplicate(entry) < 0)
+ return entry.val;
+
+ /* make sure dst_mm is on swapoff's mmlist. */
+ if (unlikely(list_empty(&dst_mm->mmlist))) {
+ spin_lock(&mmlist_lock);
+ if (list_empty(&dst_mm->mmlist))
+ list_add(&dst_mm->mmlist,
+ &src_mm->mmlist);
+ spin_unlock(&mmlist_lock);
+ }
rss[MM_SWAPENTS]++;
- else if (is_migration_entry(entry)) {
+ } else if (is_migration_entry(entry)) {
page = migration_entry_to_page(entry);
if (PageAnon(page))
arch_leave_lazy_mmu_mode();
/* Do the actual TLB flush before dropping ptl */
- if (force_flush) {
- unsigned long old_end;
-
- /*
- * Flush the TLB just for the previous segment,
- * then update the range to be the remaining
- * TLB range.
- */
- old_end = tlb->end;
- tlb->end = addr;
+ if (force_flush)
tlb_flush_mmu_tlbonly(tlb);
- tlb->start = addr;
- tlb->end = old_end;
- }
pte_unmap_unlock(start_pte, ptl);
/*
#include <linux/stop_machine.h>
#include <linux/hugetlb.h>
#include <linux/memblock.h>
+#include <linux/bootmem.h>
#include <asm/tlbflush.h>
}
#endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */
+static void reset_node_present_pages(pg_data_t *pgdat)
+{
+ struct zone *z;
+
+ for (z = pgdat->node_zones; z < pgdat->node_zones + MAX_NR_ZONES; z++)
+ z->present_pages = 0;
+
+ pgdat->node_present_pages = 0;
+}
+
/* we are OK calling __meminit stuff here - we have CONFIG_MEMORY_HOTPLUG */
static pg_data_t __ref *hotadd_new_pgdat(int nid, u64 start)
{
build_all_zonelists(pgdat, NULL);
mutex_unlock(&zonelists_mutex);
+ /*
+ * zone->managed_pages is set to an approximate value in
+ * free_area_init_core(), which will cause
+ * /sys/device/system/node/nodeX/meminfo has wrong data.
+ * So reset it to 0 before any memory is onlined.
+ */
+ reset_node_managed_pages(pgdat);
+
+ /*
+ * When memory is hot-added, all the memory is in offline state. So
+ * clear all zones' present_pages because they will be updated in
+ * online_pages() and offline_pages().
+ */
+ reset_node_present_pages(pgdat);
+
return pgdat;
}
* shrinking vma had, to cover any anon pages imported.
*/
if (exporter && exporter->anon_vma && !importer->anon_vma) {
- if (anon_vma_clone(importer, exporter))
- return -ENOMEM;
+ int error;
+
+ error = anon_vma_clone(importer, exporter);
+ if (error)
+ return error;
importer->anon_vma = exporter->anon_vma;
}
}
if (err)
goto out_free_vma;
- if (anon_vma_clone(new, vma))
+ err = anon_vma_clone(new, vma);
+ if (err)
goto out_free_mpol;
if (new->vm_file)
static int reset_managed_pages_done __initdata;
-static inline void __init reset_node_managed_pages(pg_data_t *pgdat)
+void reset_node_managed_pages(pg_data_t *pgdat)
{
struct zone *z;
- if (reset_managed_pages_done)
- return;
for (z = pgdat->node_zones; z < pgdat->node_zones + MAX_NR_ZONES; z++)
z->managed_pages = 0;
}
{
struct pglist_data *pgdat;
+ if (reset_managed_pages_done)
+ return;
+
for_each_online_pgdat(pgdat)
reset_node_managed_pages(pgdat);
+
reset_managed_pages_done = 1;
}
set_page_private(page, 0);
}
-/*
- * Locate the struct page for both the matching buddy in our
- * pair (buddy1) and the combined O(n+1) page they form (page).
- *
- * 1) Any buddy B1 will have an order O twin B2 which satisfies
- * the following equation:
- * B2 = B1 ^ (1 << O)
- * For example, if the starting buddy (buddy2) is #8 its order
- * 1 buddy is #10:
- * B2 = 8 ^ (1 << 1) = 8 ^ 2 = 10
- *
- * 2) Any buddy B will have an order O+1 parent P which
- * satisfies the following equation:
- * P = B & ~(1 << O)
- *
- * Assumption: *_mem_map is contiguous at least up to MAX_ORDER
- */
-static inline unsigned long
-__find_buddy_index(unsigned long page_idx, unsigned int order)
-{
- return page_idx ^ (1 << order);
-}
-
/*
* This function checks whether a page is free && is the buddy
* we can do coalesce a page and its buddy if
unsigned long combined_idx;
unsigned long uninitialized_var(buddy_idx);
struct page *buddy;
+ int max_order = MAX_ORDER;
VM_BUG_ON(!zone_is_initialized(zone));
return;
VM_BUG_ON(migratetype == -1);
+ if (is_migrate_isolate(migratetype)) {
+ /*
+ * We restrict max order of merging to prevent merge
+ * between freepages on isolate pageblock and normal
+ * pageblock. Without this, pageblock isolation
+ * could cause incorrect freepage accounting.
+ */
+ max_order = min(MAX_ORDER, pageblock_order + 1);
+ } else {
+ __mod_zone_freepage_state(zone, 1 << order, migratetype);
+ }
- page_idx = pfn & ((1 << MAX_ORDER) - 1);
+ page_idx = pfn & ((1 << max_order) - 1);
VM_BUG_ON_PAGE(page_idx & ((1 << order) - 1), page);
VM_BUG_ON_PAGE(bad_range(zone, page), page);
- while (order < MAX_ORDER-1) {
+ while (order < max_order - 1) {
buddy_idx = __find_buddy_index(page_idx, order);
buddy = page + (buddy_idx - page_idx);
if (!page_is_buddy(page, buddy, order))
*/
if (page_is_guard(buddy)) {
clear_page_guard_flag(buddy);
- set_page_private(page, 0);
- __mod_zone_freepage_state(zone, 1 << order,
- migratetype);
+ set_page_private(buddy, 0);
+ if (!is_migrate_isolate(migratetype)) {
+ __mod_zone_freepage_state(zone, 1 << order,
+ migratetype);
+ }
} else {
list_del(&buddy->lru);
zone->free_area[order].nr_free--;
/* must delete as __free_one_page list manipulates */
list_del(&page->lru);
mt = get_freepage_migratetype(page);
+ if (unlikely(has_isolate_pageblock(zone)))
+ mt = get_pageblock_migratetype(page);
+
/* MIGRATE_MOVABLE list may include MIGRATE_RESERVEs */
__free_one_page(page, page_to_pfn(page), zone, 0, mt);
trace_mm_page_pcpu_drain(page, 0, mt);
- if (likely(!is_migrate_isolate_page(page))) {
- __mod_zone_page_state(zone, NR_FREE_PAGES, 1);
- if (is_migrate_cma(mt))
- __mod_zone_page_state(zone, NR_FREE_CMA_PAGES, 1);
- }
} while (--to_free && --batch_free && !list_empty(list));
}
spin_unlock(&zone->lock);
if (nr_scanned)
__mod_zone_page_state(zone, NR_PAGES_SCANNED, -nr_scanned);
+ if (unlikely(has_isolate_pageblock(zone) ||
+ is_migrate_isolate(migratetype))) {
+ migratetype = get_pfnblock_migratetype(page, pfn);
+ }
__free_one_page(page, pfn, zone, order, migratetype);
- if (unlikely(!is_migrate_isolate(migratetype)))
- __mod_zone_freepage_state(zone, 1 << order, migratetype);
spin_unlock(&zone->lock);
}
}
EXPORT_SYMBOL_GPL(split_page);
-static int __isolate_free_page(struct page *page, unsigned int order)
+int __isolate_free_page(struct page *page, unsigned int order)
{
unsigned long watermark;
struct zone *zone;
/* Make sure the range is really isolated. */
if (test_pages_isolated(outer_start, end, false)) {
- pr_warn("alloc_contig_range test_pages_isolated(%lx, %lx) failed\n",
- outer_start, end);
+ pr_info("%s: [%lx, %lx) PFNs busy\n",
+ __func__, outer_start, end);
ret = -EBUSY;
goto done;
}
-
/* Grab isolated pages from freelists. */
outer_end = isolate_freepages_range(&cc, outer_start, end);
if (!outer_end) {
int migratetype = get_pageblock_migratetype(page);
set_pageblock_migratetype(page, MIGRATE_ISOLATE);
+ zone->nr_isolate_pageblock++;
nr_pages = move_freepages_block(zone, page, MIGRATE_ISOLATE);
__mod_zone_freepage_state(zone, -nr_pages, migratetype);
{
struct zone *zone;
unsigned long flags, nr_pages;
+ struct page *isolated_page = NULL;
+ unsigned int order;
+ unsigned long page_idx, buddy_idx;
+ struct page *buddy;
zone = page_zone(page);
spin_lock_irqsave(&zone->lock, flags);
if (get_pageblock_migratetype(page) != MIGRATE_ISOLATE)
goto out;
- nr_pages = move_freepages_block(zone, page, migratetype);
- __mod_zone_freepage_state(zone, nr_pages, migratetype);
+
+ /*
+ * Because freepage with more than pageblock_order on isolated
+ * pageblock is restricted to merge due to freepage counting problem,
+ * it is possible that there is free buddy page.
+ * move_freepages_block() doesn't care of merge so we need other
+ * approach in order to merge them. Isolation and free will make
+ * these pages to be merged.
+ */
+ if (PageBuddy(page)) {
+ order = page_order(page);
+ if (order >= pageblock_order) {
+ page_idx = page_to_pfn(page) & ((1 << MAX_ORDER) - 1);
+ buddy_idx = __find_buddy_index(page_idx, order);
+ buddy = page + (buddy_idx - page_idx);
+
+ if (!is_migrate_isolate_page(buddy)) {
+ __isolate_free_page(page, order);
+ set_page_refcounted(page);
+ isolated_page = page;
+ }
+ }
+ }
+
+ /*
+ * If we isolate freepage with more than pageblock_order, there
+ * should be no freepage in the range, so we could avoid costly
+ * pageblock scanning for freepage moving.
+ */
+ if (!isolated_page) {
+ nr_pages = move_freepages_block(zone, page, migratetype);
+ __mod_zone_freepage_state(zone, nr_pages, migratetype);
+ }
set_pageblock_migratetype(page, migratetype);
+ zone->nr_isolate_pageblock--;
out:
spin_unlock_irqrestore(&zone->lock, flags);
+ if (isolated_page)
+ __free_pages(isolated_page, order);
}
static inline struct page *
{
struct anon_vma_chain *avc;
struct anon_vma *anon_vma;
+ int error;
/* Don't bother if the parent process has no anon_vma here. */
if (!pvma->anon_vma)
* First, attach the new VMA to the parent VMA's anon_vmas,
* so rmap can find non-COWed pages in child processes.
*/
- if (anon_vma_clone(vma, pvma))
- return -ENOMEM;
+ error = anon_vma_clone(vma, pvma);
+ if (error)
+ return error;
/* Then add our own anon_vma. */
anon_vma = anon_vma_alloc();
void *obj;
int x;
- VM_BUG_ON(nodeid > num_online_nodes());
+ VM_BUG_ON(nodeid < 0 || nodeid >= MAX_NUMNODES);
n = get_node(cachep, nodeid);
BUG_ON(!n);
if (s->size - size >= sizeof(void *))
continue;
+ if (IS_ENABLED(CONFIG_SLAB) && align &&
+ (align > s->align || s->align % align))
+ continue;
+
return s;
}
return NULL;
* necessary) to @newsize. It will be typically be called from the filesystem's
* setattr function when ATTR_SIZE is passed in.
*
- * Must be called with inode_mutex held and before all filesystem specific
- * block truncation has been performed.
+ * Must be called with a lock serializing truncates and writes (generally
+ * i_mutex but e.g. xfs uses a different lock) and before all filesystem
+ * specific block truncation has been performed.
*/
void truncate_setsize(struct inode *inode, loff_t newsize)
{
struct page *page;
pgoff_t index;
- WARN_ON(!mutex_is_locked(&inode->i_mutex));
WARN_ON(to > inode->i_size);
if (from >= to || bsize == PAGE_CACHE_SIZE)
unsigned long scanned;
unsigned long reclaimed;
+ spin_lock(&vmpr->sr_lock);
/*
* Several contexts might be calling vmpressure(), so it is
* possible that the work was rescheduled again before the old
* here. No need for any locks here since we don't care if
* vmpr->reclaimed is in sync.
*/
- if (!vmpr->scanned)
+ scanned = vmpr->scanned;
+ if (!scanned) {
+ spin_unlock(&vmpr->sr_lock);
return;
+ }
- spin_lock(&vmpr->sr_lock);
- scanned = vmpr->scanned;
reclaimed = vmpr->reclaimed;
vmpr->scanned = 0;
vmpr->reclaimed = 0;
return;
if (port) {
- __skb_push(skb, sizeof(struct ethhdr));
skb->dev = port->dev;
NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
- dev_queue_xmit);
+ br_dev_queue_push_xmit);
} else {
br_multicast_select_own_querier(br, ip, skb);
netif_rx(skb);
[IFLA_BRPORT_MODE] = { .type = NLA_U8 },
[IFLA_BRPORT_GUARD] = { .type = NLA_U8 },
[IFLA_BRPORT_PROTECT] = { .type = NLA_U8 },
+ [IFLA_BRPORT_FAST_LEAVE]= { .type = NLA_U8 },
[IFLA_BRPORT_LEARNING] = { .type = NLA_U8 },
[IFLA_BRPORT_UNICAST_FLOOD] = { .type = NLA_U8 },
};
#include <net/netfilter/ipv6/nf_reject.h>
#include <linux/ip.h>
#include <net/ip.h>
+#include <net/ip6_checksum.h>
#include <linux/netfilter_bridge.h>
#include "../br_private.h"
struct ceph_crypto_key old_key;
void *ticket_buf = NULL;
void *tp, *tpend;
+ void **ptp;
struct ceph_timespec new_validity;
struct ceph_crypto_key new_session_key;
struct ceph_buffer *new_ticket_blob;
goto out;
}
tp = ticket_buf;
- dlen = ceph_decode_32(&tp);
+ ptp = &tp;
+ tpend = *ptp + dlen;
} else {
/* unencrypted */
- ceph_decode_32_safe(p, end, dlen, bad);
- ticket_buf = kmalloc(dlen, GFP_NOFS);
- if (!ticket_buf) {
- ret = -ENOMEM;
- goto out;
- }
- tp = ticket_buf;
- ceph_decode_need(p, end, dlen, bad);
- ceph_decode_copy(p, ticket_buf, dlen);
+ ptp = p;
+ tpend = end;
}
- tpend = tp + dlen;
+ ceph_decode_32_safe(ptp, tpend, dlen, bad);
dout(" ticket blob is %d bytes\n", dlen);
- ceph_decode_need(&tp, tpend, 1 + sizeof(u64), bad);
- blob_struct_v = ceph_decode_8(&tp);
- new_secret_id = ceph_decode_64(&tp);
- ret = ceph_decode_buffer(&new_ticket_blob, &tp, tpend);
+ ceph_decode_need(ptp, tpend, 1 + sizeof(u64), bad);
+ blob_struct_v = ceph_decode_8(ptp);
+ new_secret_id = ceph_decode_64(ptp);
+ ret = ceph_decode_buffer(&new_ticket_blob, ptp, tpend);
if (ret)
goto out;
static const u8 *aes_iv = (u8 *)CEPH_AES_IV;
+/*
+ * Should be used for buffers allocated with ceph_kvmalloc().
+ * Currently these are encrypt out-buffer (ceph_buffer) and decrypt
+ * in-buffer (msg front).
+ *
+ * Dispose of @sgt with teardown_sgtable().
+ *
+ * @prealloc_sg is to avoid memory allocation inside sg_alloc_table()
+ * in cases where a single sg is sufficient. No attempt to reduce the
+ * number of sgs by squeezing physically contiguous pages together is
+ * made though, for simplicity.
+ */
+static int setup_sgtable(struct sg_table *sgt, struct scatterlist *prealloc_sg,
+ const void *buf, unsigned int buf_len)
+{
+ struct scatterlist *sg;
+ const bool is_vmalloc = is_vmalloc_addr(buf);
+ unsigned int off = offset_in_page(buf);
+ unsigned int chunk_cnt = 1;
+ unsigned int chunk_len = PAGE_ALIGN(off + buf_len);
+ int i;
+ int ret;
+
+ if (buf_len == 0) {
+ memset(sgt, 0, sizeof(*sgt));
+ return -EINVAL;
+ }
+
+ if (is_vmalloc) {
+ chunk_cnt = chunk_len >> PAGE_SHIFT;
+ chunk_len = PAGE_SIZE;
+ }
+
+ if (chunk_cnt > 1) {
+ ret = sg_alloc_table(sgt, chunk_cnt, GFP_NOFS);
+ if (ret)
+ return ret;
+ } else {
+ WARN_ON(chunk_cnt != 1);
+ sg_init_table(prealloc_sg, 1);
+ sgt->sgl = prealloc_sg;
+ sgt->nents = sgt->orig_nents = 1;
+ }
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *page;
+ unsigned int len = min(chunk_len - off, buf_len);
+
+ if (is_vmalloc)
+ page = vmalloc_to_page(buf);
+ else
+ page = virt_to_page(buf);
+
+ sg_set_page(sg, page, len, off);
+
+ off = 0;
+ buf += len;
+ buf_len -= len;
+ }
+ WARN_ON(buf_len != 0);
+
+ return 0;
+}
+
+static void teardown_sgtable(struct sg_table *sgt)
+{
+ if (sgt->orig_nents > 1)
+ sg_free_table(sgt);
+}
+
static int ceph_aes_encrypt(const void *key, int key_len,
void *dst, size_t *dst_len,
const void *src, size_t src_len)
{
- struct scatterlist sg_in[2], sg_out[1];
+ struct scatterlist sg_in[2], prealloc_sg;
+ struct sg_table sg_out;
struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
struct blkcipher_desc desc = { .tfm = tfm, .flags = 0 };
int ret;
*dst_len = src_len + zero_padding;
- crypto_blkcipher_setkey((void *)tfm, key, key_len);
sg_init_table(sg_in, 2);
sg_set_buf(&sg_in[0], src, src_len);
sg_set_buf(&sg_in[1], pad, zero_padding);
- sg_init_table(sg_out, 1);
- sg_set_buf(sg_out, dst, *dst_len);
+ ret = setup_sgtable(&sg_out, &prealloc_sg, dst, *dst_len);
+ if (ret)
+ goto out_tfm;
+
+ crypto_blkcipher_setkey((void *)tfm, key, key_len);
iv = crypto_blkcipher_crt(tfm)->iv;
ivsize = crypto_blkcipher_ivsize(tfm);
-
memcpy(iv, aes_iv, ivsize);
+
/*
print_hex_dump(KERN_ERR, "enc key: ", DUMP_PREFIX_NONE, 16, 1,
key, key_len, 1);
print_hex_dump(KERN_ERR, "enc pad: ", DUMP_PREFIX_NONE, 16, 1,
pad, zero_padding, 1);
*/
- ret = crypto_blkcipher_encrypt(&desc, sg_out, sg_in,
+ ret = crypto_blkcipher_encrypt(&desc, sg_out.sgl, sg_in,
src_len + zero_padding);
- crypto_free_blkcipher(tfm);
- if (ret < 0)
+ if (ret < 0) {
pr_err("ceph_aes_crypt failed %d\n", ret);
+ goto out_sg;
+ }
/*
print_hex_dump(KERN_ERR, "enc out: ", DUMP_PREFIX_NONE, 16, 1,
dst, *dst_len, 1);
*/
- return 0;
+
+out_sg:
+ teardown_sgtable(&sg_out);
+out_tfm:
+ crypto_free_blkcipher(tfm);
+ return ret;
}
static int ceph_aes_encrypt2(const void *key, int key_len, void *dst,
const void *src1, size_t src1_len,
const void *src2, size_t src2_len)
{
- struct scatterlist sg_in[3], sg_out[1];
+ struct scatterlist sg_in[3], prealloc_sg;
+ struct sg_table sg_out;
struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
struct blkcipher_desc desc = { .tfm = tfm, .flags = 0 };
int ret;
*dst_len = src1_len + src2_len + zero_padding;
- crypto_blkcipher_setkey((void *)tfm, key, key_len);
sg_init_table(sg_in, 3);
sg_set_buf(&sg_in[0], src1, src1_len);
sg_set_buf(&sg_in[1], src2, src2_len);
sg_set_buf(&sg_in[2], pad, zero_padding);
- sg_init_table(sg_out, 1);
- sg_set_buf(sg_out, dst, *dst_len);
+ ret = setup_sgtable(&sg_out, &prealloc_sg, dst, *dst_len);
+ if (ret)
+ goto out_tfm;
+
+ crypto_blkcipher_setkey((void *)tfm, key, key_len);
iv = crypto_blkcipher_crt(tfm)->iv;
ivsize = crypto_blkcipher_ivsize(tfm);
-
memcpy(iv, aes_iv, ivsize);
+
/*
print_hex_dump(KERN_ERR, "enc key: ", DUMP_PREFIX_NONE, 16, 1,
key, key_len, 1);
print_hex_dump(KERN_ERR, "enc pad: ", DUMP_PREFIX_NONE, 16, 1,
pad, zero_padding, 1);
*/
- ret = crypto_blkcipher_encrypt(&desc, sg_out, sg_in,
+ ret = crypto_blkcipher_encrypt(&desc, sg_out.sgl, sg_in,
src1_len + src2_len + zero_padding);
- crypto_free_blkcipher(tfm);
- if (ret < 0)
+ if (ret < 0) {
pr_err("ceph_aes_crypt2 failed %d\n", ret);
+ goto out_sg;
+ }
/*
print_hex_dump(KERN_ERR, "enc out: ", DUMP_PREFIX_NONE, 16, 1,
dst, *dst_len, 1);
*/
- return 0;
+
+out_sg:
+ teardown_sgtable(&sg_out);
+out_tfm:
+ crypto_free_blkcipher(tfm);
+ return ret;
}
static int ceph_aes_decrypt(const void *key, int key_len,
void *dst, size_t *dst_len,
const void *src, size_t src_len)
{
- struct scatterlist sg_in[1], sg_out[2];
+ struct sg_table sg_in;
+ struct scatterlist sg_out[2], prealloc_sg;
struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
struct blkcipher_desc desc = { .tfm = tfm };
char pad[16];
if (IS_ERR(tfm))
return PTR_ERR(tfm);
- crypto_blkcipher_setkey((void *)tfm, key, key_len);
- sg_init_table(sg_in, 1);
sg_init_table(sg_out, 2);
- sg_set_buf(sg_in, src, src_len);
sg_set_buf(&sg_out[0], dst, *dst_len);
sg_set_buf(&sg_out[1], pad, sizeof(pad));
+ ret = setup_sgtable(&sg_in, &prealloc_sg, src, src_len);
+ if (ret)
+ goto out_tfm;
+ crypto_blkcipher_setkey((void *)tfm, key, key_len);
iv = crypto_blkcipher_crt(tfm)->iv;
ivsize = crypto_blkcipher_ivsize(tfm);
-
memcpy(iv, aes_iv, ivsize);
/*
print_hex_dump(KERN_ERR, "dec in: ", DUMP_PREFIX_NONE, 16, 1,
src, src_len, 1);
*/
-
- ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in, src_len);
- crypto_free_blkcipher(tfm);
+ ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in.sgl, src_len);
if (ret < 0) {
pr_err("ceph_aes_decrypt failed %d\n", ret);
- return ret;
+ goto out_sg;
}
if (src_len <= *dst_len)
print_hex_dump(KERN_ERR, "dec out: ", DUMP_PREFIX_NONE, 16, 1,
dst, *dst_len, 1);
*/
- return 0;
+
+out_sg:
+ teardown_sgtable(&sg_in);
+out_tfm:
+ crypto_free_blkcipher(tfm);
+ return ret;
}
static int ceph_aes_decrypt2(const void *key, int key_len,
void *dst2, size_t *dst2_len,
const void *src, size_t src_len)
{
- struct scatterlist sg_in[1], sg_out[3];
+ struct sg_table sg_in;
+ struct scatterlist sg_out[3], prealloc_sg;
struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
struct blkcipher_desc desc = { .tfm = tfm };
char pad[16];
if (IS_ERR(tfm))
return PTR_ERR(tfm);
- sg_init_table(sg_in, 1);
- sg_set_buf(sg_in, src, src_len);
sg_init_table(sg_out, 3);
sg_set_buf(&sg_out[0], dst1, *dst1_len);
sg_set_buf(&sg_out[1], dst2, *dst2_len);
sg_set_buf(&sg_out[2], pad, sizeof(pad));
+ ret = setup_sgtable(&sg_in, &prealloc_sg, src, src_len);
+ if (ret)
+ goto out_tfm;
crypto_blkcipher_setkey((void *)tfm, key, key_len);
iv = crypto_blkcipher_crt(tfm)->iv;
ivsize = crypto_blkcipher_ivsize(tfm);
-
memcpy(iv, aes_iv, ivsize);
/*
print_hex_dump(KERN_ERR, "dec in: ", DUMP_PREFIX_NONE, 16, 1,
src, src_len, 1);
*/
-
- ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in, src_len);
- crypto_free_blkcipher(tfm);
+ ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in.sgl, src_len);
if (ret < 0) {
pr_err("ceph_aes_decrypt failed %d\n", ret);
- return ret;
+ goto out_sg;
}
if (src_len <= *dst1_len)
dst2, *dst2_len, 1);
*/
- return 0;
+out_sg:
+ teardown_sgtable(&sg_in);
+out_tfm:
+ crypto_free_blkcipher(tfm);
+ return ret;
}
IPPROTO_TCP, &sock);
if (ret)
return ret;
- sock->sk->sk_allocation = GFP_NOFS;
+ sock->sk->sk_allocation = GFP_NOFS | __GFP_MEMALLOC;
#ifdef CONFIG_LOCKDEP
lockdep_set_class(&sock->sk->sk_lock, &socket_class);
return ret;
}
+
+ sk_set_memalloc(sock->sk);
+
con->sock = sock;
return 0;
}
{
struct ceph_connection *con = container_of(work, struct ceph_connection,
work.work);
+ unsigned long pflags = current->flags;
bool fault;
+ current->flags |= PF_MEMALLOC;
+
mutex_lock(&con->mutex);
while (true) {
int ret;
con_fault_finish(con);
con->ops->put(con);
+
+ tsk_restore_flags(current, pflags, PF_MEMALLOC);
}
/*
static void __remove_osd(struct ceph_osd_client *osdc, struct ceph_osd *osd)
{
dout("__remove_osd %p\n", osd);
- BUG_ON(!list_empty(&osd->o_requests));
- BUG_ON(!list_empty(&osd->o_linger_requests));
+ WARN_ON(!list_empty(&osd->o_requests));
+ WARN_ON(!list_empty(&osd->o_linger_requests));
rb_erase(&osd->o_node, &osdc->osds);
list_del_init(&osd->o_osd_lru);
if (list_empty(&req->r_osd_item))
req->r_osd = NULL;
}
+
+ list_del_init(&req->r_req_lru_item); /* can be on notarget */
ceph_osdc_put_request(req);
}
if (req->r_osd) {
__cancel_request(req);
list_del_init(&req->r_osd_item);
+ list_del_init(&req->r_linger_osd_item);
req->r_osd = NULL;
}
goto errout;
}
if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) {
+ put_net(net);
err = -EPERM;
goto errout;
}
int idx = 0;
u32 portid = NETLINK_CB(cb->skb).portid;
u32 seq = cb->nlh->nlmsg_seq;
- struct nlattr *extfilt;
u32 filter_mask = 0;
- extfilt = nlmsg_find_attr(cb->nlh, sizeof(struct ifinfomsg),
- IFLA_EXT_MASK);
- if (extfilt)
- filter_mask = nla_get_u32(extfilt);
+ if (nlmsg_len(cb->nlh) > sizeof(struct ifinfomsg)) {
+ struct nlattr *extfilt;
+
+ extfilt = nlmsg_find_attr(cb->nlh, sizeof(struct ifinfomsg),
+ IFLA_EXT_MASK);
+ if (extfilt) {
+ if (nla_len(extfilt) < sizeof(filter_mask))
+ return -EINVAL;
+
+ filter_mask = nla_get_u32(extfilt);
+ }
+ }
rcu_read_lock();
for_each_netdev_rcu(net, dev) {
if (br_spec) {
nla_for_each_nested(attr, br_spec, rem) {
if (nla_type(attr) == IFLA_BRIDGE_FLAGS) {
+ if (nla_len(attr) < sizeof(flags))
+ return -EINVAL;
+
have_flags = true;
flags = nla_get_u16(attr);
break;
if (br_spec) {
nla_for_each_nested(attr, br_spec, rem) {
if (nla_type(attr) == IFLA_BRIDGE_FLAGS) {
+ if (nla_len(attr) < sizeof(flags))
+ return -EINVAL;
+
have_flags = true;
flags = nla_get_u16(attr);
break;
case SKB_FCLONE_CLONE:
fclones = container_of(skb, struct sk_buff_fclones, skb2);
- /* Warning : We must perform the atomic_dec_and_test() before
- * setting skb->fclone back to SKB_FCLONE_FREE, otherwise
- * skb_clone() could set clone_ref to 2 before our decrement.
- * Anyway, if we are going to free the structure, no need to
- * rewrite skb->fclone.
+ /* The clone portion is available for
+ * fast-cloning again.
*/
- if (atomic_dec_and_test(&fclones->fclone_ref)) {
+ skb->fclone = SKB_FCLONE_FREE;
+
+ if (atomic_dec_and_test(&fclones->fclone_ref))
kmem_cache_free(skbuff_fclone_cache, fclones);
- } else {
- /* The clone portion is available for
- * fast-cloning again.
- */
- skb->fclone = SKB_FCLONE_FREE;
- }
break;
}
}
if (skb->fclone == SKB_FCLONE_ORIG &&
n->fclone == SKB_FCLONE_FREE) {
n->fclone = SKB_FCLONE_CLONE;
- /* As our fastclone was free, clone_ref must be 1 at this point.
- * We could use atomic_inc() here, but it is faster
- * to set the final value.
- */
- atomic_set(&fclones->fclone_ref, 2);
+ atomic_inc(&fclones->fclone_ref);
} else {
if (skb_pfmemalloc(skb))
gfp_mask |= __GFP_MEMALLOC;
if (!app)
return -EMSGSIZE;
- spin_lock(&dcb_lock);
+ spin_lock_bh(&dcb_lock);
list_for_each_entry(itr, &dcb_app_list, list) {
if (itr->ifindex == netdev->ifindex) {
err = nla_put(skb, DCB_ATTR_IEEE_APP, sizeof(itr->app),
&itr->app);
if (err) {
- spin_unlock(&dcb_lock);
+ spin_unlock_bh(&dcb_lock);
return -EMSGSIZE;
}
}
else
dcbx = -EOPNOTSUPP;
- spin_unlock(&dcb_lock);
+ spin_unlock_bh(&dcb_lock);
nla_nest_end(skb, app);
/* get peer info if available */
}
/* local app */
- spin_lock(&dcb_lock);
+ spin_lock_bh(&dcb_lock);
app = nla_nest_start(skb, DCB_ATTR_CEE_APP_TABLE);
if (!app)
goto dcb_unlock;
else
dcbx = -EOPNOTSUPP;
- spin_unlock(&dcb_lock);
+ spin_unlock_bh(&dcb_lock);
/* features flags */
if (ops->getfeatcfg) {
return 0;
dcb_unlock:
- spin_unlock(&dcb_lock);
+ spin_unlock_bh(&dcb_lock);
nla_put_failure:
return err;
}
struct dcb_app_type *itr;
u8 prio = 0;
- spin_lock(&dcb_lock);
+ spin_lock_bh(&dcb_lock);
if ((itr = dcb_app_lookup(app, dev->ifindex, 0)))
prio = itr->app.priority;
- spin_unlock(&dcb_lock);
+ spin_unlock_bh(&dcb_lock);
return prio;
}
if (dev->dcbnl_ops->getdcbx)
event.dcbx = dev->dcbnl_ops->getdcbx(dev);
- spin_lock(&dcb_lock);
+ spin_lock_bh(&dcb_lock);
/* Search for existing match and replace */
if ((itr = dcb_app_lookup(new, dev->ifindex, 0))) {
if (new->priority)
if (new->priority)
err = dcb_app_add(new, dev->ifindex);
out:
- spin_unlock(&dcb_lock);
+ spin_unlock_bh(&dcb_lock);
if (!err)
call_dcbevent_notifiers(DCB_APP_EVENT, &event);
return err;
struct dcb_app_type *itr;
u8 prio = 0;
- spin_lock(&dcb_lock);
+ spin_lock_bh(&dcb_lock);
if ((itr = dcb_app_lookup(app, dev->ifindex, 0)))
prio |= 1 << itr->app.priority;
- spin_unlock(&dcb_lock);
+ spin_unlock_bh(&dcb_lock);
return prio;
}
if (dev->dcbnl_ops->getdcbx)
event.dcbx = dev->dcbnl_ops->getdcbx(dev);
- spin_lock(&dcb_lock);
+ spin_lock_bh(&dcb_lock);
/* Search for existing match and abort if found */
if (dcb_app_lookup(new, dev->ifindex, new->priority)) {
err = -EEXIST;
err = dcb_app_add(new, dev->ifindex);
out:
- spin_unlock(&dcb_lock);
+ spin_unlock_bh(&dcb_lock);
if (!err)
call_dcbevent_notifiers(DCB_APP_EVENT, &event);
return err;
if (dev->dcbnl_ops->getdcbx)
event.dcbx = dev->dcbnl_ops->getdcbx(dev);
- spin_lock(&dcb_lock);
+ spin_lock_bh(&dcb_lock);
/* Search for existing match and remove it. */
if ((itr = dcb_app_lookup(del, dev->ifindex, del->priority))) {
list_del(&itr->list);
err = 0;
}
- spin_unlock(&dcb_lock);
+ spin_unlock_bh(&dcb_lock);
if (!err)
call_dcbevent_notifiers(DCB_APP_EVENT, &event);
return err;
struct dcb_app_type *app;
struct dcb_app_type *tmp;
- spin_lock(&dcb_lock);
+ spin_lock_bh(&dcb_lock);
list_for_each_entry_safe(app, tmp, &dcb_app_list, list) {
list_del(&app->list);
kfree(app);
}
- spin_unlock(&dcb_lock);
+ spin_unlock_bh(&dcb_lock);
}
static int __init dcbnl_init(void)
/* We could not connect to a designated PHY, so use the switch internal
* MDIO bus instead
*/
- if (!p->phy)
+ if (!p->phy) {
p->phy = ds->slave_mii_bus->phy_map[p->port];
- else
+ phy_connect_direct(slave_dev, p->phy, dsa_slave_adjust_link,
+ p->phy_interface);
+ } else {
pr_info("attached PHY at address %d [%s]\n",
p->phy->addr, p->phy->drv->name);
+ }
}
int dsa_slave_suspend(struct net_device *slave_dev)
return pp;
}
+int inet_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
+{
+ if (sk->sk_family == AF_INET)
+ return ip_recv_error(sk, msg, len, addr_len);
+#if IS_ENABLED(CONFIG_IPV6)
+ if (sk->sk_family == AF_INET6)
+ return pingv6_ops.ipv6_recv_error(sk, msg, len, addr_len);
+#endif
+ return -EINVAL;
+}
+
static int inet_gro_complete(struct sk_buff *skb, int nhoff)
{
__be16 newlen = htons(skb->len - nhoff);
else
res->tclassid = 0;
#endif
+
+ if (err == -ESRCH)
+ err = -ENETUNREACH;
+
return err;
}
EXPORT_SYMBOL_GPL(__fib_lookup);
int err = -ENOSYS;
const struct net_offload **offloads;
+ udp_tunnel_gro_complete(skb, nhoff);
+
rcu_read_lock();
offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
ops = rcu_dereference(offloads[proto]);
gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len);
geneve_build_header(gnvh, tun_flags, vni, opt_len, opt);
+ skb_set_inner_protocol(skb, htons(ETH_P_TEB));
+
return udp_tunnel_xmit_skb(gs->sock, rt, skb, src, dst,
tos, ttl, df, src_port, dst_port, xnet);
}
static void __exit geneve_cleanup_module(void)
{
destroy_workqueue(geneve_wq);
+ unregister_pernet_subsys(&geneve_net_ops);
}
module_exit(geneve_cleanup_module);
return scount;
}
-#define igmp_skb_size(skb) (*(unsigned int *)((skb)->cb))
-
-static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size)
+static struct sk_buff *igmpv3_newpack(struct net_device *dev, unsigned int mtu)
{
struct sk_buff *skb;
struct rtable *rt;
struct flowi4 fl4;
int hlen = LL_RESERVED_SPACE(dev);
int tlen = dev->needed_tailroom;
+ unsigned int size = mtu;
while (1) {
skb = alloc_skb(size + hlen + tlen,
return NULL;
}
skb->priority = TC_PRIO_CONTROL;
- igmp_skb_size(skb) = size;
rt = ip_route_output_ports(net, &fl4, NULL, IGMPV3_ALL_MCR, 0,
0, 0,
skb_dst_set(skb, &rt->dst);
skb->dev = dev;
+ skb->reserved_tailroom = skb_end_offset(skb) -
+ min(mtu, skb_end_offset(skb));
skb_reserve(skb, hlen);
skb_reset_network_header(skb);
return skb;
}
-#define AVAILABLE(skb) ((skb) ? ((skb)->dev ? igmp_skb_size(skb) - (skb)->len : \
- skb_tailroom(skb)) : 0)
+#define AVAILABLE(skb) ((skb) ? skb_availroom(skb) : 0)
static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
int type, int gdeleted, int sdeleted)
for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
if (!CMSG_OK(msg, cmsg))
return -EINVAL;
-#if defined(CONFIG_IPV6)
+#if IS_ENABLED(CONFIG_IPV6)
if (allow_ipv6 &&
cmsg->cmsg_level == SOL_IPV6 &&
cmsg->cmsg_type == IPV6_PKTINFO) {
.validate = vti_tunnel_validate,
.newlink = vti_newlink,
.changelink = vti_changelink,
+ .dellink = ip_tunnel_dellink,
.get_size = vti_get_size,
.fill_info = vti_fill_info,
};
struct nf_nat_range range;
unsigned int verdict;
+ memset(&range, 0, sizeof(range));
range.flags = priv->flags;
verdict = nf_nat_masquerade_ipv4(pkt->skb, pkt->ops->hooknum,
&ipv6_hdr(skb)->daddr))
continue;
#endif
+ } else {
+ continue;
}
if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif)
if (flags & MSG_OOB)
goto out;
- if (flags & MSG_ERRQUEUE) {
- if (family == AF_INET) {
- return ip_recv_error(sk, msg, len, addr_len);
-#if IS_ENABLED(CONFIG_IPV6)
- } else if (family == AF_INET6) {
- return pingv6_ops.ipv6_recv_error(sk, msg, len,
- addr_len);
-#endif
- }
- }
+ if (flags & MSG_ERRQUEUE)
+ return inet_recv_error(sk, msg, len, addr_len);
skb = skb_recv_datagram(sk, flags, noblock, &err);
if (!skb)
u32 urg_hole = 0;
if (unlikely(flags & MSG_ERRQUEUE))
- return ip_recv_error(sk, msg, len, addr_len);
+ return inet_recv_error(sk, msg, len, addr_len);
if (sk_can_busy_loop(sk) && skb_queue_empty(&sk->sk_receive_queue) &&
(sk->sk_state == TCP_ESTABLISHED))
/* Undo procedures. */
+/* We can clear retrans_stamp when there are no retransmissions in the
+ * window. It would seem that it is trivially available for us in
+ * tp->retrans_out, however, that kind of assumptions doesn't consider
+ * what will happen if errors occur when sending retransmission for the
+ * second time. ...It could the that such segment has only
+ * TCPCB_EVER_RETRANS set at the present time. It seems that checking
+ * the head skb is enough except for some reneging corner cases that
+ * are not worth the effort.
+ *
+ * Main reason for all this complexity is the fact that connection dying
+ * time now depends on the validity of the retrans_stamp, in particular,
+ * that successive retransmissions of a segment must not advance
+ * retrans_stamp under any conditions.
+ */
+static bool tcp_any_retrans_done(const struct sock *sk)
+{
+ const struct tcp_sock *tp = tcp_sk(sk);
+ struct sk_buff *skb;
+
+ if (tp->retrans_out)
+ return true;
+
+ skb = tcp_write_queue_head(sk);
+ if (unlikely(skb && TCP_SKB_CB(skb)->sacked & TCPCB_EVER_RETRANS))
+ return true;
+
+ return false;
+}
+
#if FASTRETRANS_DEBUG > 1
static void DBGUNDO(struct sock *sk, const char *msg)
{
* is ACKed. For Reno it is MUST to prevent false
* fast retransmits (RFC2582). SACK TCP is safe. */
tcp_moderate_cwnd(tp);
+ if (!tcp_any_retrans_done(sk))
+ tp->retrans_stamp = 0;
return true;
}
tcp_set_ca_state(sk, TCP_CA_Open);
return false;
}
-/* We can clear retrans_stamp when there are no retransmissions in the
- * window. It would seem that it is trivially available for us in
- * tp->retrans_out, however, that kind of assumptions doesn't consider
- * what will happen if errors occur when sending retransmission for the
- * second time. ...It could the that such segment has only
- * TCPCB_EVER_RETRANS set at the present time. It seems that checking
- * the head skb is enough except for some reneging corner cases that
- * are not worth the effort.
- *
- * Main reason for all this complexity is the fact that connection dying
- * time now depends on the validity of the retrans_stamp, in particular,
- * that successive retransmissions of a segment must not advance
- * retrans_stamp under any conditions.
- */
-static bool tcp_any_retrans_done(const struct sock *sk)
-{
- const struct tcp_sock *tp = tcp_sk(sk);
- struct sk_buff *skb;
-
- if (tp->retrans_out)
- return true;
-
- skb = tcp_write_queue_head(sk);
- if (unlikely(skb && TCP_SKB_CB(skb)->sacked & TCPCB_EVER_RETRANS))
- return true;
-
- return false;
-}
-
/* Undo during loss recovery after partial ACK or using F-RTO. */
static bool tcp_try_undo_loss(struct sock *sk, bool frto_undo)
{
if (len < (th->doff << 2) || tcp_checksum_complete_user(sk, skb))
goto csum_error;
- if (!th->ack && !th->rst)
+ if (!th->ack && !th->rst && !th->syn)
goto discard;
/*
goto discard;
}
- if (!th->ack && !th->rst)
+ if (!th->ack && !th->rst && !th->syn)
goto discard;
if (!tcp_validate_incoming(sk, skb, th, 0))
if (th->rst)
return;
- if (skb_rtable(skb)->rt_type != RTN_LOCAL)
+ /* If sk not NULL, it means we did a successful lookup and incoming
+ * route had to be correct. prequeue might have dropped our dst.
+ */
+ if (!sk && skb_rtable(skb)->rt_type != RTN_LOCAL)
return;
/* Swap the send and the receive. */
skb->protocol = gre_proto;
/* WCCP version 1 and 2 protocol decoding.
- * - Change protocol to IP
+ * - Change protocol to IPv6
* - When dealing with WCCPv2, Skip extra 4 bytes in GRE header
*/
if (flags == 0 && gre_proto == htons(ETH_P_WCCP)) {
- skb->protocol = htons(ETH_P_IP);
+ skb->protocol = htons(ETH_P_IPV6);
if ((*(h + offset) & 0xF0) != 0x40)
offset += 4;
}
else
dev->flags &= ~IFF_POINTOPOINT;
- dev->iflink = p->link;
-
/* Precalculate GRE options length */
if (t->parms.o_flags&(GRE_CSUM|GRE_KEY|GRE_SEQ)) {
if (t->parms.o_flags&GRE_CSUM)
u64_stats_init(&ip6gre_tunnel_stats->syncp);
}
+ dev->iflink = tunnel->parms.link;
return 0;
}
if (!dev->tstats)
return -ENOMEM;
+ dev->iflink = tunnel->parms.link;
+
return 0;
}
int nhoff;
if (unlikely(skb_shinfo(skb)->gso_type &
- ~(SKB_GSO_UDP |
+ ~(SKB_GSO_TCPV4 |
+ SKB_GSO_UDP |
SKB_GSO_DODGY |
SKB_GSO_TCP_ECN |
SKB_GSO_GRE |
int err;
t = netdev_priv(dev);
- err = ip6_tnl_dev_init(dev);
- if (err < 0)
- goto out;
err = register_netdevice(dev);
if (err < 0)
static const struct net_device_ops ip6_tnl_netdev_ops = {
+ .ndo_init = ip6_tnl_dev_init,
.ndo_uninit = ip6_tnl_dev_uninit,
.ndo_start_xmit = ip6_tnl_xmit,
.ndo_do_ioctl = ip6_tnl_ioctl,
struct ip6_tnl *t = netdev_priv(dev);
struct net *net = dev_net(dev);
struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
- int err = ip6_tnl_dev_init_gen(dev);
-
- if (err)
- return err;
t->parms.proto = IPPROTO_IPV6;
dev_hold(dev);
- ip6_tnl_link_config(t);
-
rcu_assign_pointer(ip6n->tnls_wc[0], t);
return 0;
}
uh->source = src_port;
uh->len = htons(skb->len);
- uh->check = 0;
memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED
| IPSKB_REROUTED);
skb_dst_set(skb, dst);
- udp6_set_csum(udp_get_no_check6_tx(sk), skb, &inet6_sk(sk)->saddr,
- &sk->sk_v6_daddr, skb->len);
+ udp6_set_csum(udp_get_no_check6_tx(sk), skb, saddr, daddr, skb->len);
__skb_push(skb, sizeof(*ip6h));
skb_reset_network_header(skb);
struct vti6_net *ip6n = net_generic(net, vti6_net_id);
int err;
- err = vti6_dev_init(dev);
- if (err < 0)
- goto out;
-
err = register_netdevice(dev);
if (err < 0)
goto out;
}
static const struct net_device_ops vti6_netdev_ops = {
+ .ndo_init = vti6_dev_init,
.ndo_uninit = vti6_dev_uninit,
.ndo_start_xmit = vti6_tnl_xmit,
.ndo_do_ioctl = vti6_ioctl,
struct ip6_tnl *t = netdev_priv(dev);
struct net *net = dev_net(dev);
struct vti6_net *ip6n = net_generic(net, vti6_net_id);
- int err = vti6_dev_init_gen(dev);
-
- if (err)
- return err;
t->parms.proto = IPPROTO_IPV6;
dev_hold(dev);
- vti6_link_config(t);
-
rcu_assign_pointer(ip6n->tnls_wc[0], t);
return 0;
}
return vti6_tnl_create2(dev);
}
+static void vti6_dellink(struct net_device *dev, struct list_head *head)
+{
+ struct net *net = dev_net(dev);
+ struct vti6_net *ip6n = net_generic(net, vti6_net_id);
+
+ if (dev != ip6n->fb_tnl_dev)
+ unregister_netdevice_queue(dev, head);
+}
+
static int vti6_changelink(struct net_device *dev, struct nlattr *tb[],
struct nlattr *data[])
{
.setup = vti6_dev_setup,
.validate = vti6_validate,
.newlink = vti6_newlink,
+ .dellink = vti6_dellink,
.changelink = vti6_changelink,
.get_size = vti6_get_size,
.fill_info = vti6_fill_info,
if (!ip6n->fb_tnl_dev)
goto err_alloc_dev;
dev_net_set(ip6n->fb_tnl_dev, net);
+ ip6n->fb_tnl_dev->rtnl_link_ops = &vti6_link_ops;
err = vti6_fb_tnl_dev_init(ip6n->fb_tnl_dev);
if (err < 0)
void ip6_mr_cleanup(void)
{
+ rtnl_unregister(RTNL_FAMILY_IP6MR, RTM_GETROUTE);
+#ifdef CONFIG_IPV6_PIMSM_V2
+ inet6_del_protocol(&pim6_protocol, IPPROTO_PIM);
+#endif
unregister_netdevice_notifier(&ip6_mr_notifier);
unregister_pernet_subsys(&ip6mr_net_ops);
kmem_cache_destroy(mrt_cachep);
hdr->daddr = *daddr;
}
-static struct sk_buff *mld_newpack(struct inet6_dev *idev, int size)
+static struct sk_buff *mld_newpack(struct inet6_dev *idev, unsigned int mtu)
{
struct net_device *dev = idev->dev;
struct net *net = dev_net(dev);
const struct in6_addr *saddr;
int hlen = LL_RESERVED_SPACE(dev);
int tlen = dev->needed_tailroom;
+ unsigned int size = mtu + hlen + tlen;
int err;
u8 ra[8] = { IPPROTO_ICMPV6, 0,
IPV6_TLV_ROUTERALERT, 2, 0, 0,
IPV6_TLV_PADN, 0 };
/* we assume size > sizeof(ra) here */
- size += hlen + tlen;
/* limit our allocations to order-0 page */
size = min_t(int, size, SKB_MAX_ORDER(0, 0));
skb = sock_alloc_send_skb(sk, size, 1, &err);
return NULL;
skb->priority = TC_PRIO_CONTROL;
+ skb->reserved_tailroom = skb_end_offset(skb) -
+ min(mtu, skb_end_offset(skb));
skb_reserve(skb, hlen);
if (__ipv6_get_lladdr(idev, &addr_buf, IFA_F_TENTATIVE)) {
return skb;
}
-#define AVAILABLE(skb) ((skb) ? ((skb)->dev ? (skb)->dev->mtu - (skb)->len : \
- skb_tailroom(skb)) : 0)
+#define AVAILABLE(skb) ((skb) ? skb_availroom(skb) : 0)
static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc,
int type, int gdeleted, int sdeleted, int crsend)
struct nf_nat_range range;
unsigned int verdict;
+ memset(&range, 0, sizeof(range));
range.flags = priv->flags;
verdict = nf_nat_masquerade_ipv6(pkt->skb, &range, pkt->out);
struct sit_net *sitn = net_generic(net, sit_net_id);
int err;
- err = ipip6_tunnel_init(dev);
- if (err < 0)
- goto out;
- ipip6_tunnel_clone_6rd(dev, sitn);
+ memcpy(dev->dev_addr, &t->parms.iph.saddr, 4);
+ memcpy(dev->broadcast, &t->parms.iph.daddr, 4);
if ((__force u16)t->parms.i_flags & SIT_ISATAP)
dev->priv_flags |= IFF_ISATAP;
if (err < 0)
goto out;
- strcpy(t->parms.name, dev->name);
+ ipip6_tunnel_clone_6rd(dev, sitn);
+
dev->rtnl_link_ops = &sit_link_ops;
dev_hold(dev);
}
static const struct net_device_ops ipip6_netdev_ops = {
+ .ndo_init = ipip6_tunnel_init,
.ndo_uninit = ipip6_tunnel_uninit,
.ndo_start_xmit = sit_tunnel_xmit,
.ndo_do_ioctl = ipip6_tunnel_ioctl,
tunnel->dev = dev;
tunnel->net = dev_net(dev);
-
- memcpy(dev->dev_addr, &tunnel->parms.iph.saddr, 4);
- memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);
+ strcpy(tunnel->parms.name, dev->name);
ipip6_tunnel_bind_dev(dev);
dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
tunnel->dev = dev;
tunnel->net = dev_net(dev);
- strcpy(tunnel->parms.name, dev->name);
iph->version = 4;
iph->protocol = IPPROTO_IPV6;
if (th->rst)
return;
- if (!ipv6_unicast_destination(skb))
+ /* If sk not NULL, it means we did a successful lookup and incoming
+ * route had to be correct. prequeue might have dropped our dst.
+ */
+ if (!sk && !ipv6_unicast_destination(skb))
return;
#ifdef CONFIG_TCP_MD5SIG
struct ipxhdr *ipx = NULL;
struct sk_buff *skb;
int copied, rc;
+ bool locked = true;
lock_sock(sk);
/* put the autobinding in */
if (sock_flag(sk, SOCK_ZAPPED))
goto out;
+ release_sock(sk);
+ locked = false;
skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
flags & MSG_DONTWAIT, &rc);
if (!skb) {
out_free:
skb_free_datagram(sk, skb);
out:
- release_sock(sk);
+ if (locked)
+ release_sock(sk);
return rc;
}
__aligned(__alignof__(struct aead_request));
struct aead_request *aead_req = (void *) aead_req_data;
+ if (data_len == 0)
+ return -EINVAL;
+
memset(aead_req, 0, sizeof(aead_req_data));
sg_init_one(&pt, data, data_len);
memset(¶ms, 0, sizeof(params));
memset(&csa_ie, 0, sizeof(csa_ie));
- err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon,
+ err = ieee80211_parse_ch_switch_ie(sdata, elems,
ifibss->chandef.chan->band,
sta_flags, ifibss->bssid, &csa_ie);
/* can't switch to destination channel, fail */
* ieee80211_parse_ch_switch_ie - parses channel switch IEs
* @sdata: the sdata of the interface which has received the frame
* @elems: parsed 802.11 elements received with the frame
- * @beacon: indicates if the frame was a beacon or probe response
* @current_band: indicates the current band
* @sta_flags: contains information about own capabilities and restrictions
* to decide which channel switch announcements can be accepted. Only the
* Return: 0 on success, <0 on error and >0 if there is nothing to parse.
*/
int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
- struct ieee802_11_elems *elems, bool beacon,
+ struct ieee802_11_elems *elems,
enum ieee80211_band current_band,
u32 sta_flags, u8 *bssid,
struct ieee80211_csa_ie *csa_ie);
int i, flushed;
struct ps_data *ps;
struct cfg80211_chan_def chandef;
+ bool cancel_scan;
clear_bit(SDATA_STATE_RUNNING, &sdata->state);
- if (rcu_access_pointer(local->scan_sdata) == sdata)
+ cancel_scan = rcu_access_pointer(local->scan_sdata) == sdata;
+ if (cancel_scan)
ieee80211_scan_cancel(local);
/*
list_del(&sdata->u.vlan.list);
mutex_unlock(&local->mtx);
RCU_INIT_POINTER(sdata->vif.chanctx_conf, NULL);
+ /* see comment in the default case below */
+ ieee80211_free_keys(sdata, true);
/* no need to tell driver */
break;
case NL80211_IFTYPE_MONITOR:
/*
* When we get here, the interface is marked down.
* Free the remaining keys, if there are any
- * (shouldn't be, except maybe in WDS mode?)
+ * (which can happen in AP mode if userspace sets
+ * keys before the interface is operating, and maybe
+ * also in WDS mode)
*
* Force the key freeing to always synchronize_net()
* to wait for the RX path in case it is using this
- * interface enqueuing frames * at this very time on
+ * interface enqueuing frames at this very time on
* another CPU.
*/
ieee80211_free_keys(sdata, true);
-
- /* fall through */
- case NL80211_IFTYPE_AP:
skb_queue_purge(&sdata->skb_queue);
}
ieee80211_recalc_ps(local, -1);
+ if (cancel_scan)
+ flush_delayed_work(&local->scan_work);
+
if (local->open_count == 0) {
ieee80211_stop_device(local);
memset(¶ms, 0, sizeof(params));
memset(&csa_ie, 0, sizeof(csa_ie));
- err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, band,
+ err = ieee80211_parse_ch_switch_ie(sdata, elems, band,
sta_flags, sdata->vif.addr,
&csa_ie);
if (err < 0)
current_band = cbss->channel->band;
memset(&csa_ie, 0, sizeof(csa_ie));
- res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band,
+ res = ieee80211_parse_ch_switch_ie(sdata, elems, current_band,
ifmgd->flags,
ifmgd->associated->bssid, &csa_ie);
if (res < 0)
ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work);
else
mod_timer(&ifmgd->chswitch_timer,
- TU_TO_EXP_TIME(csa_ie.count * cbss->beacon_interval));
+ TU_TO_EXP_TIME((csa_ie.count - 1) *
+ cbss->beacon_interval));
}
static bool
cur_thr = mi->groups[cur_group].rates[cur_idx].cur_tp;
cur_prob = mi->groups[cur_group].rates[cur_idx].probability;
- tmp_group = tp_list[j - 1] / MCS_GROUP_RATES;
- tmp_idx = tp_list[j - 1] % MCS_GROUP_RATES;
- tmp_thr = mi->groups[tmp_group].rates[tmp_idx].cur_tp;
- tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability;
-
- while (j > 0 && (cur_thr > tmp_thr ||
- (cur_thr == tmp_thr && cur_prob > tmp_prob))) {
- j--;
+ do {
tmp_group = tp_list[j - 1] / MCS_GROUP_RATES;
tmp_idx = tp_list[j - 1] % MCS_GROUP_RATES;
tmp_thr = mi->groups[tmp_group].rates[tmp_idx].cur_tp;
tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability;
- }
+ if (cur_thr < tmp_thr ||
+ (cur_thr == tmp_thr && cur_prob <= tmp_prob))
+ break;
+ j--;
+ } while (j > 0);
if (j < MAX_THR_RATES - 1) {
memmove(&tp_list[j + 1], &tp_list[j], (sizeof(*tp_list) *
sc = le16_to_cpu(hdr->seq_ctrl);
frag = sc & IEEE80211_SCTL_FRAG;
- if (likely((!ieee80211_has_morefrags(fc) && frag == 0) ||
- is_multicast_ether_addr(hdr->addr1))) {
- /* not fragmented */
+ if (likely(!ieee80211_has_morefrags(fc) && frag == 0))
+ goto out;
+
+ if (is_multicast_ether_addr(hdr->addr1)) {
+ rx->local->dot11MulticastReceivedFrameCount++;
goto out;
}
+
I802_DEBUG_INC(rx->local->rx_handlers_fragments);
if (skb_linearize(rx->skb))
out:
if (rx->sta)
rx->sta->rx_packets++;
- if (is_multicast_ether_addr(hdr->addr1))
- rx->local->dot11MulticastReceivedFrameCount++;
- else
- ieee80211_led_rx(rx->local);
+ ieee80211_led_rx(rx->local);
return RX_CONTINUE;
}
#include "wme.h"
int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
- struct ieee802_11_elems *elems, bool beacon,
+ struct ieee802_11_elems *elems,
enum ieee80211_band current_band,
u32 sta_flags, u8 *bssid,
struct ieee80211_csa_ie *csa_ie)
return -EINVAL;
}
- if (!beacon && sec_chan_offs) {
+ if (sec_chan_offs) {
secondary_channel_offset = sec_chan_offs->sec_chan_offs;
- } else if (beacon && ht_oper) {
- secondary_channel_offset =
- ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
} else if (!(sta_flags & IEEE80211_STA_DISABLE_HT)) {
- /* If it's not a beacon, HT is enabled and the IE not present,
- * it's 20 MHz, 802.11-2012 8.5.2.6:
- * This element [the Secondary Channel Offset Element] is
- * present when switching to a 40 MHz channel. It may be
- * present when switching to a 20 MHz channel (in which
- * case the secondary channel offset is set to SCN).
- */
+ /* If the secondary channel offset IE is not present,
+ * we can't know what's the post-CSA offset, so the
+ * best we can do is use 20MHz.
+ */
secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
}
if (*op < IP_SET_OP_VERSION) {
/* Check the version at the beginning of operations */
struct ip_set_req_version *req_version = data;
+
+ if (*len < sizeof(struct ip_set_req_version)) {
+ ret = -EINVAL;
+ goto done;
+ }
+
if (req_version->version != IPSET_PROTOCOL) {
ret = -EPROTO;
goto done;
new_skb = skb_realloc_headroom(skb, max_headroom);
if (!new_skb)
goto error;
+ if (skb->sk)
+ skb_set_owner_w(new_skb, skb->sk);
consume_skb(skb);
skb = new_skb;
}
}
}
-/* Schedule objects for release via rcu to make sure no packets are accesing
- * removed rules.
- */
-static void nf_tables_commit_release_rcu(struct rcu_head *rt)
+static void nf_tables_commit_release(struct nft_trans *trans)
{
- struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head);
-
switch (trans->msg_type) {
case NFT_MSG_DELTABLE:
nf_tables_table_destroy(&trans->ctx);
}
}
+ synchronize_rcu();
+
list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
list_del(&trans->list);
- trans->ctx.nla = NULL;
- call_rcu(&trans->rcu_head, nf_tables_commit_release_rcu);
+ nf_tables_commit_release(trans);
}
nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN);
return 0;
}
-/* Schedule objects for release via rcu to make sure no packets are accesing
- * aborted rules.
- */
-static void nf_tables_abort_release_rcu(struct rcu_head *rt)
+static void nf_tables_abort_release(struct nft_trans *trans)
{
- struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head);
-
switch (trans->msg_type) {
case NFT_MSG_NEWTABLE:
nf_tables_table_destroy(&trans->ctx);
}
}
+ synchronize_rcu();
+
list_for_each_entry_safe_reverse(trans, next,
&net->nft.commit_list, list) {
list_del(&trans->list);
- trans->ctx.nla = NULL;
- call_rcu(&trans->rcu_head, nf_tables_abort_release_rcu);
+ nf_tables_abort_release(trans);
}
return 0;
[NFNLGRP_CONNTRACK_EXP_NEW] = NFNL_SUBSYS_CTNETLINK_EXP,
[NFNLGRP_CONNTRACK_EXP_UPDATE] = NFNL_SUBSYS_CTNETLINK_EXP,
[NFNLGRP_CONNTRACK_EXP_DESTROY] = NFNL_SUBSYS_CTNETLINK_EXP,
+ [NFNLGRP_NFTABLES] = NFNL_SUBSYS_NFTABLES,
+ [NFNLGRP_ACCT_QUOTA] = NFNL_SUBSYS_ACCT,
};
void nfnl_lock(__u8 subsys_id)
static int nfnetlink_bind(int group)
{
const struct nfnetlink_subsystem *ss;
- int type = nfnl_group2type[group];
+ int type;
+
+ if (group <= NFNLGRP_NONE || group > NFNLGRP_MAX)
+ return -EINVAL;
+
+ type = nfnl_group2type[group];
rcu_read_lock();
ss = nfnetlink_get_subsys(type);
{
int i;
+ for (i = NFNLGRP_NONE + 1; i <= NFNLGRP_MAX; i++)
+ BUG_ON(nfnl_group2type[i] == NFNL_SUBSYS_NONE);
+
for (i=0; i<NFNL_SUBSYS_COUNT; i++)
mutex_init(&table[i].mutex);
#include <linux/netfilter_ipv6/ip6_tables.h>
#include <net/netfilter/nf_tables.h>
-static const struct {
- const char *name;
- u8 type;
-} table_to_chaintype[] = {
- { "filter", NFT_CHAIN_T_DEFAULT },
- { "raw", NFT_CHAIN_T_DEFAULT },
- { "security", NFT_CHAIN_T_DEFAULT },
- { "mangle", NFT_CHAIN_T_ROUTE },
- { "nat", NFT_CHAIN_T_NAT },
- { },
-};
-
-static int nft_compat_table_to_chaintype(const char *table)
-{
- int i;
-
- for (i = 0; table_to_chaintype[i].name != NULL; i++) {
- if (strcmp(table_to_chaintype[i].name, table) == 0)
- return table_to_chaintype[i].type;
- }
-
- return -1;
-}
-
static int nft_compat_chain_validate_dependency(const char *tablename,
const struct nft_chain *chain)
{
- enum nft_chain_type type;
const struct nft_base_chain *basechain;
if (!tablename || !(chain->flags & NFT_BASE_CHAIN))
return 0;
- type = nft_compat_table_to_chaintype(tablename);
- if (type < 0)
- return -EINVAL;
-
basechain = nft_base_chain(chain);
- if (basechain->type->type != type)
+ if (strcmp(tablename, "nat") == 0 &&
+ basechain->type->type != NFT_CHAIN_T_NAT)
return -EINVAL;
return 0;
struct xt_target *target, void *info,
union nft_entry *entry, u8 proto, bool inv)
{
- par->net = &init_net;
+ par->net = ctx->net;
par->table = ctx->table->name;
switch (ctx->afi->family) {
case AF_INET:
struct xt_match *match, void *info,
union nft_entry *entry, u8 proto, bool inv)
{
- par->net = &init_net;
+ par->net = ctx->net;
par->table = ctx->table->name;
switch (ctx->afi->family) {
case AF_INET:
union nft_entry e = {};
int ret;
- ret = nft_compat_chain_validate_dependency(match->name, ctx->chain);
+ ret = nft_compat_chain_validate_dependency(match->table, ctx->chain);
if (ret < 0)
goto err;
if (!(hook_mask & match->hooks))
return -EINVAL;
- ret = nft_compat_chain_validate_dependency(match->name,
+ ret = nft_compat_chain_validate_dependency(match->table,
ctx->chain);
if (ret < 0)
return ret;
return;
for (undo = 0; undo < group; undo++)
- if (test_bit(group, &groups))
+ if (test_bit(undo, &groups))
nlk->netlink_unbind(undo);
}
netlink_insert(sk, net, nladdr->nl_pid) :
netlink_autobind(sock);
if (err) {
- netlink_unbind(nlk->ngroups - 1, groups, nlk);
+ netlink_unbind(nlk->ngroups, groups, nlk);
return err;
}
}
nl_table[unit].module = module;
if (cfg) {
nl_table[unit].bind = cfg->bind;
+ nl_table[unit].unbind = cfg->unbind;
nl_table[unit].flags = cfg->flags;
if (cfg->compare)
nl_table[unit].compare = cfg->compare;
{
int transport_len = skb->len - skb_transport_offset(skb);
- if (l4_proto == IPPROTO_TCP) {
+ if (l4_proto == NEXTHDR_TCP) {
if (likely(transport_len >= sizeof(struct tcphdr)))
inet_proto_csum_replace16(&tcp_hdr(skb)->check, skb,
addr, new_addr, 1);
- } else if (l4_proto == IPPROTO_UDP) {
+ } else if (l4_proto == NEXTHDR_UDP) {
if (likely(transport_len >= sizeof(struct udphdr))) {
struct udphdr *uh = udp_hdr(skb);
uh->check = CSUM_MANGLED_0;
}
}
+ } else if (l4_proto == NEXTHDR_ICMP) {
+ if (likely(transport_len >= sizeof(struct icmp6hdr)))
+ inet_proto_csum_replace16(&icmp6_hdr(skb)->icmp6_cksum,
+ skb, addr, new_addr, 1);
}
}
case OVS_ACTION_ATTR_SAMPLE:
err = sample(dp, skb, key, a);
- if (unlikely(err)) /* skb already freed. */
- return err;
break;
}
return msgsize;
}
-/* Called with ovs_mutex or RCU read lock. */
+/* Called with ovs_mutex. */
static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb,
u32 portid, u32 seq, u32 flags, u8 cmd)
{
if (!reply)
return -ENOMEM;
- rcu_read_lock();
+ ovs_lock();
dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs);
if (IS_ERR(dp)) {
err = PTR_ERR(dp);
err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid,
info->snd_seq, 0, OVS_DP_CMD_NEW);
BUG_ON(err < 0);
- rcu_read_unlock();
+ ovs_unlock();
return genlmsg_reply(reply, info);
err_unlock_free:
- rcu_read_unlock();
+ ovs_unlock();
kfree_skb(reply);
return err;
}
int skip = cb->args[0];
int i = 0;
- rcu_read_lock();
- list_for_each_entry_rcu(dp, &ovs_net->dps, list_node) {
+ ovs_lock();
+ list_for_each_entry(dp, &ovs_net->dps, list_node) {
if (i >= skip &&
ovs_dp_cmd_fill_info(dp, skb, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI,
break;
i++;
}
- rcu_read_unlock();
+ ovs_unlock();
cb->args[0] = i;
if (match->key->eth.type == htons(ETH_P_ARP)
|| match->key->eth.type == htons(ETH_P_RARP)) {
key_expected |= 1 << OVS_KEY_ATTR_ARP;
- if (match->mask && (match->mask->key.eth.type == htons(0xffff)))
+ if (match->mask && (match->mask->key.tp.src == htons(0xff)))
mask_allowed |= 1 << OVS_KEY_ATTR_ARP;
}
ipv6_key->ipv6_frag, OVS_FRAG_TYPE_MAX);
return -EINVAL;
}
+
+ if (!is_mask && ipv6_key->ipv6_label & htonl(0xFFF00000)) {
+ OVS_NLERR("IPv6 flow label %x is out of range (max=%x).\n",
+ ntohl(ipv6_key->ipv6_label), (1 << 20) - 1);
+ return -EINVAL;
+ }
+
SW_FLOW_KEY_PUT(match, ipv6.label,
ipv6_key->ipv6_label, is_mask);
SW_FLOW_KEY_PUT(match, ip.proto,
__unregister_prot_hook(sk, sync);
}
-static inline __pure struct page *pgv_to_page(void *addr)
+static inline struct page * __pure pgv_to_page(void *addr)
{
if (is_vmalloc_addr(addr))
return vmalloc_to_page(addr);
list_add(&cur_key->key_list, sh_keys);
cur_key->key = key;
- sctp_auth_key_hold(key);
-
return 0;
nomem:
if (!replace)
addr_param = param.v + sizeof(sctp_addip_param_t);
af = sctp_get_af_specific(param_type2af(param.p->type));
+ if (af == NULL)
+ break;
+
af->from_addr_param(&addr, addr_param,
htons(asoc->peer.port), 0);
char *string = NULL;
struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
struct gss_cl_ctx *ctx;
+ unsigned int len;
struct xdr_netobj *acceptor;
rcu_read_lock();
if (!ctx)
goto out;
- acceptor = &ctx->gc_acceptor;
+ len = ctx->gc_acceptor.len;
+ rcu_read_unlock();
/* no point if there's no string */
- if (!acceptor->len)
- goto out;
-
- string = kmalloc(acceptor->len + 1, GFP_KERNEL);
+ if (!len)
+ return NULL;
+realloc:
+ string = kmalloc(len + 1, GFP_KERNEL);
if (!string)
+ return NULL;
+
+ rcu_read_lock();
+ ctx = rcu_dereference(gss_cred->gc_ctx);
+
+ /* did the ctx disappear or was it replaced by one with no acceptor? */
+ if (!ctx || !ctx->gc_acceptor.len) {
+ kfree(string);
+ string = NULL;
goto out;
+ }
+
+ acceptor = &ctx->gc_acceptor;
+
+ /*
+ * Did we find a new acceptor that's longer than the original? Allocate
+ * a longer buffer and try again.
+ */
+ if (len < acceptor->len) {
+ len = acceptor->len;
+ rcu_read_unlock();
+ kfree(string);
+ goto realloc;
+ }
memcpy(string, acceptor->data, acceptor->len);
string[acceptor->len] = '\0';
xid = *p++;
calldir = *p;
- if (bc_xprt)
- req = xprt_lookup_rqst(bc_xprt, xid);
-
- if (!req) {
- printk(KERN_NOTICE
- "%s: Got unrecognized reply: "
- "calldir 0x%x xpt_bc_xprt %p xid %08x\n",
- __func__, ntohl(calldir),
- bc_xprt, ntohl(xid));
+ if (!bc_xprt)
return -EAGAIN;
- }
+ spin_lock_bh(&bc_xprt->transport_lock);
+ req = xprt_lookup_rqst(bc_xprt, xid);
+ if (!req)
+ goto unlock_notfound;
memcpy(&req->rq_private_buf, &req->rq_rcv_buf, sizeof(struct xdr_buf));
/*
dst = &req->rq_private_buf.head[0];
src = &rqstp->rq_arg.head[0];
if (dst->iov_len < src->iov_len)
- return -EAGAIN; /* whatever; just giving up. */
+ goto unlock_eagain; /* whatever; just giving up. */
memcpy(dst->iov_base, src->iov_base, src->iov_len);
xprt_complete_rqst(req->rq_task, rqstp->rq_arg.len);
rqstp->rq_arg.len = 0;
+ spin_unlock_bh(&bc_xprt->transport_lock);
return 0;
+unlock_notfound:
+ printk(KERN_NOTICE
+ "%s: Got unrecognized reply: "
+ "calldir 0x%x xpt_bc_xprt %p xid %08x\n",
+ __func__, ntohl(calldir),
+ bc_xprt, ntohl(xid));
+unlock_eagain:
+ spin_unlock_bh(&bc_xprt->transport_lock);
+ return -EAGAIN;
}
static int copy_pages_to_kvecs(struct kvec *vec, struct page **pages, int len)
#define KEYRING_SEARCH_NO_UPDATE_TIME 0x0004 /* Don't update times */
#define KEYRING_SEARCH_NO_CHECK_PERM 0x0008 /* Don't check permissions */
#define KEYRING_SEARCH_DETECT_TOO_DEEP 0x0010 /* Give an error on excessive depth */
+#define KEYRING_SEARCH_SKIP_EXPIRED 0x0020 /* Ignore expired keys (intention to replace) */
int (*iterator)(const void *object, void *iterator_data);
#include <asm/uaccess.h>
#include "internal.h"
+#define KEY_MAX_DESC_SIZE 4096
+
static int key_get_type_from_user(char *type,
const char __user *_type,
unsigned len)
description = NULL;
if (_description) {
- description = strndup_user(_description, PAGE_SIZE);
+ description = strndup_user(_description, KEY_MAX_DESC_SIZE);
if (IS_ERR(description)) {
ret = PTR_ERR(description);
goto error;
goto error;
/* pull the description into kernel space */
- description = strndup_user(_description, PAGE_SIZE);
+ description = strndup_user(_description, KEY_MAX_DESC_SIZE);
if (IS_ERR(description)) {
ret = PTR_ERR(description);
goto error;
/* fetch the name from userspace */
name = NULL;
if (_name) {
- name = strndup_user(_name, PAGE_SIZE);
+ name = strndup_user(_name, KEY_MAX_DESC_SIZE);
if (IS_ERR(name)) {
ret = PTR_ERR(name);
goto error;
{
struct key *key, *instkey;
key_ref_t key_ref;
- char *tmpbuf;
+ char *infobuf;
long ret;
+ int desclen, infolen;
key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_NEED_VIEW);
if (IS_ERR(key_ref)) {
}
okay:
- /* calculate how much description we're going to return */
- ret = -ENOMEM;
- tmpbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
- if (!tmpbuf)
- goto error2;
-
key = key_ref_to_ptr(key_ref);
+ desclen = strlen(key->description);
- ret = snprintf(tmpbuf, PAGE_SIZE - 1,
- "%s;%d;%d;%08x;%s",
- key->type->name,
- from_kuid_munged(current_user_ns(), key->uid),
- from_kgid_munged(current_user_ns(), key->gid),
- key->perm,
- key->description ?: "");
-
- /* include a NUL char at the end of the data */
- if (ret > PAGE_SIZE - 1)
- ret = PAGE_SIZE - 1;
- tmpbuf[ret] = 0;
- ret++;
+ /* calculate how much information we're going to return */
+ ret = -ENOMEM;
+ infobuf = kasprintf(GFP_KERNEL,
+ "%s;%d;%d;%08x;",
+ key->type->name,
+ from_kuid_munged(current_user_ns(), key->uid),
+ from_kgid_munged(current_user_ns(), key->gid),
+ key->perm);
+ if (!infobuf)
+ goto error2;
+ infolen = strlen(infobuf);
+ ret = infolen + desclen + 1;
/* consider returning the data */
- if (buffer && buflen > 0) {
- if (buflen > ret)
- buflen = ret;
-
- if (copy_to_user(buffer, tmpbuf, buflen) != 0)
+ if (buffer && buflen >= ret) {
+ if (copy_to_user(buffer, infobuf, infolen) != 0 ||
+ copy_to_user(buffer + infolen, key->description,
+ desclen + 1) != 0)
ret = -EFAULT;
}
- kfree(tmpbuf);
+ kfree(infobuf);
error2:
key_ref_put(key_ref);
error:
if (ret < 0)
goto error;
- description = strndup_user(_description, PAGE_SIZE);
+ description = strndup_user(_description, KEY_MAX_DESC_SIZE);
if (IS_ERR(description)) {
ret = PTR_ERR(description);
goto error;
}
if (key->expiry && ctx->now.tv_sec >= key->expiry) {
- ctx->result = ERR_PTR(-EKEYEXPIRED);
+ if (!(ctx->flags & KEYRING_SEARCH_SKIP_EXPIRED))
+ ctx->result = ERR_PTR(-EKEYEXPIRED);
kleave(" = %d [expire]", ctx->skipped_ret);
goto skipped;
}
ctx->index_key.type->name,
ctx->index_key.description);
+#define STATE_CHECKS (KEYRING_SEARCH_NO_STATE_CHECK | KEYRING_SEARCH_DO_STATE_CHECK)
+ BUG_ON((ctx->flags & STATE_CHECKS) == 0 ||
+ (ctx->flags & STATE_CHECKS) == STATE_CHECKS);
+
if (ctx->index_key.description)
ctx->index_key.desc_len = strlen(ctx->index_key.description);
if (ctx->match_data.lookup_type == KEYRING_SEARCH_LOOKUP_ITERATE ||
keyring_compare_object(keyring, &ctx->index_key)) {
ctx->skipped_ret = 2;
- ctx->flags |= KEYRING_SEARCH_DO_STATE_CHECK;
switch (ctx->iterator(keyring_key_to_ptr(keyring), ctx)) {
case 1:
goto found;
}
ctx->skipped_ret = 0;
- if (ctx->flags & KEYRING_SEARCH_NO_STATE_CHECK)
- ctx->flags &= ~KEYRING_SEARCH_DO_STATE_CHECK;
/* Start processing a new keyring */
descend_to_keyring:
.match_data.cmp = key_default_cmp,
.match_data.raw_data = description,
.match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
+ .flags = (KEYRING_SEARCH_DO_STATE_CHECK |
+ KEYRING_SEARCH_SKIP_EXPIRED),
};
struct key *key;
key_ref_t key_ref;
.match_data.cmp = key_default_cmp,
.match_data.raw_data = description,
.match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
+ .flags = KEYRING_SEARCH_DO_STATE_CHECK,
};
struct key *authkey;
key_ref_t authkey_ref;
err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm);
if (err) {
if (err == -EINVAL) {
- WARN_ONCE(1, "selinux_nlmsg_perm: unrecognized netlink message:"
- " protocol=%hu nlmsg_type=%hu sclass=%hu\n",
- sk->sk_protocol, nlh->nlmsg_type, sksec->sclass);
+ printk(KERN_WARNING
+ "SELinux: unrecognized netlink message:"
+ " protocol=%hu nlmsg_type=%hu sclass=%hu\n",
+ sk->sk_protocol, nlh->nlmsg_type, sksec->sclass);
if (!selinux_enforcing || security_get_allow_unknown())
err = 0;
}
FORMAT(DSD_U8),
FORMAT(DSD_U16_LE),
FORMAT(DSD_U32_LE),
+ FORMAT(DSD_U16_BE),
+ FORMAT(DSD_U32_BE),
};
const char *snd_pcm_format_name(snd_pcm_format_t format)
.width = 32, .phys = 32, .le = 1, .signd = 0,
.silence = { 0x69, 0x69, 0x69, 0x69 },
},
+ [SNDRV_PCM_FORMAT_DSD_U16_BE] = {
+ .width = 16, .phys = 16, .le = 0, .signd = 0,
+ .silence = { 0x69, 0x69 },
+ },
+ [SNDRV_PCM_FORMAT_DSD_U32_BE] = {
+ .width = 32, .phys = 32, .le = 0, .signd = 0,
+ .silence = { 0x69, 0x69, 0x69, 0x69 },
+ },
/* FIXME: the following three formats are not defined properly yet */
[SNDRV_PCM_FORMAT_MPEG] = {
.le = -1, .signd = -1,
"{Intel, LPT_LP},"
"{Intel, WPT_LP},"
"{Intel, SPT},"
+ "{Intel, SPT_LP},"
"{Intel, HPT},"
"{Intel, PBG},"
"{Intel, SCH},"
/* quirks for ATI/AMD HDMI */
#define AZX_DCAPS_PRESET_ATI_HDMI \
- (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_SYNC_WRITE | AZX_DCAPS_POSFIX_LPIB)
+ (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_SYNC_WRITE | AZX_DCAPS_POSFIX_LPIB|\
+ AZX_DCAPS_NO_MSI64)
/* quirks for Nvidia */
#define AZX_DCAPS_PRESET_NVIDIA \
struct snd_card *card = chip->card;
int err;
unsigned short gcap;
+ unsigned int dma_bits = 64;
#if BITS_PER_LONG != 64
/* Fix up base address on ULI M5461 */
return -ENXIO;
}
- if (chip->msi)
+ if (chip->msi) {
+ if (chip->driver_caps & AZX_DCAPS_NO_MSI64) {
+ dev_dbg(card->dev, "Disabling 64bit MSI\n");
+ pci->no_64bit_msi = true;
+ }
if (pci_enable_msi(pci) < 0)
chip->msi = 0;
+ }
if (azx_acquire_irq(chip, 0) < 0)
return -EBUSY;
gcap = azx_readw(chip, GCAP);
dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap);
+ /* AMD devices support 40 or 48bit DMA, take the safe one */
+ if (chip->pci->vendor == PCI_VENDOR_ID_AMD)
+ dma_bits = 40;
+
/* disable SB600 64bit support for safety */
if (chip->pci->vendor == PCI_VENDOR_ID_ATI) {
struct pci_dev *p_smbus;
+ dma_bits = 40;
p_smbus = pci_get_device(PCI_VENDOR_ID_ATI,
PCI_DEVICE_ID_ATI_SBX00_SMBUS,
NULL);
}
/* allow 64bit DMA address if supported by H/W */
- if ((gcap & AZX_GCAP_64OK) && !pci_set_dma_mask(pci, DMA_BIT_MASK(64)))
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(64));
- else {
+ if (!(gcap & AZX_GCAP_64OK))
+ dma_bits = 32;
+ if (!pci_set_dma_mask(pci, DMA_BIT_MASK(dma_bits))) {
+ pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(dma_bits));
+ } else {
pci_set_dma_mask(pci, DMA_BIT_MASK(32));
pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32));
}
/* Sunrise Point */
{ PCI_DEVICE(0x8086, 0xa170),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ /* Sunrise Point-LP */
+ { PCI_DEVICE(0x8086, 0x9d70),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
/* Haswell */
{ PCI_DEVICE(0x8086, 0x0a0c),
.driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL },
#define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */
#define AZX_DCAPS_I915_POWERWELL (1 << 27) /* HSW i915 powerwell support */
#define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28) /* CORBRP clears itself after reset */
+#define AZX_DCAPS_NO_MSI64 (1 << 29) /* Stick to 32-bit MSIs */
/* HD Audio class code */
#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
unsigned int num_eapds;
hda_nid_t eapds[4];
bool dynamic_eapd;
+ hda_nid_t mute_led_eapd;
unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled);
}
+/* turn on/off EAPD according to Master switch (inversely!) for mute LED */
+static void cx_auto_vmaster_hook_mute_led(void *private_data, int enabled)
+{
+ struct hda_codec *codec = private_data;
+ struct conexant_spec *spec = codec->spec;
+
+ snd_hda_codec_write(codec, spec->mute_led_eapd, 0,
+ AC_VERB_SET_EAPD_BTLENABLE,
+ enabled ? 0x00 : 0x02);
+}
+
static int cx_auto_build_controls(struct hda_codec *codec)
{
int err;
CXT_FIXUP_TOSHIBA_P105,
CXT_FIXUP_HP_530,
CXT_FIXUP_CAP_MIX_AMP_5047,
+ CXT_FIXUP_MUTE_LED_EAPD,
};
/* for hda_fixup_thinkpad_acpi() */
}
}
+static void cxt_fixup_mute_led_eapd(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct conexant_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mute_led_eapd = 0x1b;
+ spec->dynamic_eapd = 1;
+ spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook_mute_led;
+ }
+}
+
/*
* Fix max input level on mixer widget to 0dB
* (originally it has 0x2b steps with 0dB offset 0x14)
.type = HDA_FIXUP_FUNC,
.v.func = cxt_fixup_cap_mix_amp_5047,
},
+ [CXT_FIXUP_MUTE_LED_EAPD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_mute_led_eapd,
+ },
};
static const struct snd_pci_quirk cxt5045_fixups[] = {
SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo IdeaPad Z560", CXT_FIXUP_MUTE_LED_EAPD),
SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC),
{ .id = CXT_PINCFG_LEMOTE_A1004, .name = "lemote-a1004" },
{ .id = CXT_PINCFG_LEMOTE_A1205, .name = "lemote-a1205" },
{ .id = CXT_FIXUP_OLPC_XO, .name = "olpc-xo" },
+ { .id = CXT_FIXUP_MUTE_LED_EAPD, .name = "mute-led-eapd" },
{}
};
snd_hda_jack_unsol_event(codec, res >> 2);
}
-/* additional initialization for ALC888 variants */
-static void alc888_coef_init(struct hda_codec *codec)
+/* Change EAPD to verb control */
+static void alc_fill_eapd_coef(struct hda_codec *codec)
{
- if (alc_get_coef0(codec) == 0x20)
- /* alc888S-VC */
- alc_write_coef_idx(codec, 7, 0x830);
- else
- /* alc888-VB */
- alc_write_coef_idx(codec, 7, 0x3030);
+ int coef;
+
+ coef = alc_get_coef0(codec);
+
+ switch (codec->vendor_id) {
+ case 0x10ec0262:
+ alc_update_coef_idx(codec, 0x7, 0, 1<<5);
+ break;
+ case 0x10ec0267:
+ case 0x10ec0268:
+ alc_update_coef_idx(codec, 0x7, 0, 1<<13);
+ break;
+ case 0x10ec0269:
+ if ((coef & 0x00f0) == 0x0010)
+ alc_update_coef_idx(codec, 0xd, 0, 1<<14);
+ if ((coef & 0x00f0) == 0x0020)
+ alc_update_coef_idx(codec, 0x4, 1<<15, 0);
+ if ((coef & 0x00f0) == 0x0030)
+ alc_update_coef_idx(codec, 0x10, 1<<9, 0);
+ break;
+ case 0x10ec0280:
+ case 0x10ec0284:
+ case 0x10ec0290:
+ case 0x10ec0292:
+ alc_update_coef_idx(codec, 0x4, 1<<15, 0);
+ break;
+ case 0x10ec0233:
+ case 0x10ec0255:
+ case 0x10ec0282:
+ case 0x10ec0283:
+ case 0x10ec0286:
+ case 0x10ec0288:
+ alc_update_coef_idx(codec, 0x10, 1<<9, 0);
+ break;
+ case 0x10ec0285:
+ case 0x10ec0293:
+ alc_update_coef_idx(codec, 0xa, 1<<13, 0);
+ break;
+ case 0x10ec0662:
+ if ((coef & 0x00f0) == 0x0030)
+ alc_update_coef_idx(codec, 0x4, 1<<10, 0); /* EAPD Ctrl */
+ break;
+ case 0x10ec0272:
+ case 0x10ec0273:
+ case 0x10ec0663:
+ case 0x10ec0665:
+ case 0x10ec0670:
+ case 0x10ec0671:
+ case 0x10ec0672:
+ alc_update_coef_idx(codec, 0xd, 0, 1<<14); /* EAPD Ctrl */
+ break;
+ case 0x10ec0668:
+ alc_update_coef_idx(codec, 0x7, 3<<13, 0);
+ break;
+ case 0x10ec0867:
+ alc_update_coef_idx(codec, 0x4, 1<<10, 0);
+ break;
+ case 0x10ec0888:
+ if ((coef & 0x00f0) == 0x0020 || (coef & 0x00f0) == 0x0030)
+ alc_update_coef_idx(codec, 0x7, 1<<5, 0);
+ break;
+ case 0x10ec0892:
+ alc_update_coef_idx(codec, 0x7, 1<<5, 0);
+ break;
+ case 0x10ec0899:
+ case 0x10ec0900:
+ alc_update_coef_idx(codec, 0x7, 1<<1, 0);
+ break;
+ }
}
-/* additional initialization for ALC889 variants */
-static void alc889_coef_init(struct hda_codec *codec)
+/* additional initialization for ALC888 variants */
+static void alc888_coef_init(struct hda_codec *codec)
{
- alc_update_coef_idx(codec, 7, 0, 0x2010);
+ switch (alc_get_coef0(codec) & 0x00f0) {
+ /* alc888-VA */
+ case 0x00:
+ /* alc888-VB */
+ case 0x10:
+ alc_update_coef_idx(codec, 7, 0, 0x2030); /* Turn EAPD to High */
+ break;
+ }
}
/* turn on/off EAPD control (only if available) */
/* generic EAPD initialization */
static void alc_auto_init_amp(struct hda_codec *codec, int type)
{
+ alc_fill_eapd_coef(codec);
alc_auto_setup_eapd(codec, true);
switch (type) {
case ALC_INIT_GPIO1:
case 0x10ec0260:
alc_update_coefex_idx(codec, 0x1a, 7, 0, 0x2010);
break;
- case 0x10ec0262:
case 0x10ec0880:
case 0x10ec0882:
case 0x10ec0883:
case 0x10ec0885:
- case 0x10ec0887:
- /*case 0x10ec0889:*/ /* this causes an SPDIF problem */
- case 0x10ec0900:
- alc889_coef_init(codec);
+ alc_update_coef_idx(codec, 7, 0, 0x2030);
break;
case 0x10ec0888:
alc888_coef_init(codec);
break;
-#if 0 /* XXX: This may cause the silent output on speaker on some machines */
- case 0x10ec0267:
- case 0x10ec0268:
- alc_update_coef_idx(codec, 7, 0, 0x3000);
- break;
-#endif /* XXX */
}
break;
}
{
if (action != HDA_FIXUP_ACT_INIT)
return;
- alc889_coef_init(codec);
+ alc_update_coef_idx(codec, 7, 0, 0x2030);
}
/* toggle speaker-output according to the hp-jack state */
}
}
+static void alc280_fixup_hp_gpio4(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ /* Like hp_gpio_mic1_led, but also needs GPIO4 low to enable headphone amp */
+ struct alc_spec *spec = codec->spec;
+ static const struct hda_verb gpio_init[] = {
+ { 0x01, AC_VERB_SET_GPIO_MASK, 0x18 },
+ { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x18 },
+ {}
+ };
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gen.vmaster_mute.hook = alc269_fixup_hp_gpio_mute_hook;
+ spec->gen.cap_sync_hook = alc269_fixup_hp_cap_mic_mute_hook;
+ spec->gpio_led = 0;
+ spec->cap_mute_led_nid = 0x18;
+ snd_hda_add_verbs(codec, gpio_init);
+ codec->power_filter = led_power_filter;
+ }
+}
+
static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
ALC283_FIXUP_BXBT2807_MIC,
ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED,
ALC282_FIXUP_ASPIRE_V5_PINS,
+ ALC280_FIXUP_HP_GPIO4,
};
static const struct hda_fixup alc269_fixups[] = {
[ALC269_FIXUP_HEADSET_MODE] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_headset_mode,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED
},
[ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC] = {
.type = HDA_FIXUP_FUNC,
[ALC255_FIXUP_HEADSET_MODE] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_headset_mode_alc255,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED
},
[ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC] = {
.type = HDA_FIXUP_FUNC,
[ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_dell_wmi,
- .chained_before = true,
- .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE
},
[ALC282_FIXUP_ASPIRE_V5_PINS] = {
.type = HDA_FIXUP_PINS,
{ },
},
},
-
+ [ALC280_FIXUP_HP_GPIO4] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc280_fixup_hp_gpio4,
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x05f4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x05f5, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x05f6, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
- SND_PCI_QUIRK(0x1028, 0x0610, "Dell", ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED),
SND_PCI_QUIRK(0x1028, 0x0615, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK),
SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK),
- SND_PCI_QUIRK(0x1028, 0x061f, "Dell", ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED),
SND_PCI_QUIRK(0x1028, 0x0638, "Dell Inspiron 5439", ALC290_FIXUP_MONO_SPEAKERS_HSJACK),
SND_PCI_QUIRK(0x1028, 0x064a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x064b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x06d9, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x06da, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
SND_PCI_QUIRK(0x103c, 0x22cf, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x22dc, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x22fb, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
- SND_PCI_QUIRK(0x103c, 0x8004, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
/* ALC290 */
SND_PCI_QUIRK(0x103c, 0x221b, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x2221, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x2225, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
- SND_PCI_QUIRK(0x103c, 0x2246, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
- SND_PCI_QUIRK(0x103c, 0x2247, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
- SND_PCI_QUIRK(0x103c, 0x2248, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
- SND_PCI_QUIRK(0x103c, 0x2249, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x2253, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x2254, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x2255, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x2256, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x2257, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
- SND_PCI_QUIRK(0x103c, 0x2258, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x2259, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x225a, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x2260, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x2265, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x2272, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x2273, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
- SND_PCI_QUIRK(0x103c, 0x2277, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x2278, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x227f, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x2282, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x17aa, 0x220e, "Thinkpad T440p", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2210, "Thinkpad T540p", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad T440", ALC292_FIXUP_TPT440_DOCK),
- SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad X240", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP),
SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
{0x17, 0x40000000},
{0x1d, 0x40700001},
{0x21, 0x02211040}),
+ SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC280_FIXUP_HP_GPIO4,
+ {0x12, 0x90a60130},
+ {0x13, 0x40000000},
+ {0x14, 0x90170110},
+ {0x15, 0x0421101f},
+ {0x16, 0x411111f0},
+ {0x17, 0x411111f0},
+ {0x18, 0x411111f0},
+ {0x19, 0x411111f0},
+ {0x1a, 0x04a11020},
+ {0x1b, 0x411111f0},
+ {0x1d, 0x40748605},
+ {0x1e, 0x411111f0}),
SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED,
{0x12, 0x90a60140},
{0x13, 0x40000000},
}
}
- /* Class D */
- alc_update_coef_idx(codec, 0xd, 0, 1<<14);
-
/* HP */
alc_update_coef_idx(codec, 0x4, 0, 1<<11);
}
}
}
+static struct coef_fw alc668_coefs[] = {
+ WRITE_COEF(0x01, 0xbebe), WRITE_COEF(0x02, 0xaaaa), WRITE_COEF(0x03, 0x0),
+ WRITE_COEF(0x04, 0x0180), WRITE_COEF(0x06, 0x0), WRITE_COEF(0x07, 0x0f80),
+ WRITE_COEF(0x08, 0x0031), WRITE_COEF(0x0a, 0x0060), WRITE_COEF(0x0b, 0x0),
+ WRITE_COEF(0x0c, 0x7cf7), WRITE_COEF(0x0d, 0x1080), WRITE_COEF(0x0e, 0x7f7f),
+ WRITE_COEF(0x0f, 0xcccc), WRITE_COEF(0x10, 0xddcc), WRITE_COEF(0x11, 0x0001),
+ WRITE_COEF(0x13, 0x0), WRITE_COEF(0x14, 0x2aa0), WRITE_COEF(0x17, 0xa940),
+ WRITE_COEF(0x19, 0x0), WRITE_COEF(0x1a, 0x0), WRITE_COEF(0x1b, 0x0),
+ WRITE_COEF(0x1c, 0x0), WRITE_COEF(0x1d, 0x0), WRITE_COEF(0x1e, 0x7418),
+ WRITE_COEF(0x1f, 0x0804), WRITE_COEF(0x20, 0x4200), WRITE_COEF(0x21, 0x0468),
+ WRITE_COEF(0x22, 0x8ccc), WRITE_COEF(0x23, 0x0250), WRITE_COEF(0x24, 0x7418),
+ WRITE_COEF(0x27, 0x0), WRITE_COEF(0x28, 0x8ccc), WRITE_COEF(0x2a, 0xff00),
+ WRITE_COEF(0x2b, 0x8000), WRITE_COEF(0xa7, 0xff00), WRITE_COEF(0xa8, 0x8000),
+ WRITE_COEF(0xaa, 0x2e17), WRITE_COEF(0xab, 0xa0c0), WRITE_COEF(0xac, 0x0),
+ WRITE_COEF(0xad, 0x0), WRITE_COEF(0xae, 0x2ac6), WRITE_COEF(0xaf, 0xa480),
+ WRITE_COEF(0xb0, 0x0), WRITE_COEF(0xb1, 0x0), WRITE_COEF(0xb2, 0x0),
+ WRITE_COEF(0xb3, 0x0), WRITE_COEF(0xb4, 0x0), WRITE_COEF(0xb5, 0x1040),
+ WRITE_COEF(0xb6, 0xd697), WRITE_COEF(0xb7, 0x902b), WRITE_COEF(0xb8, 0xd697),
+ WRITE_COEF(0xb9, 0x902b), WRITE_COEF(0xba, 0xb8ba), WRITE_COEF(0xbb, 0xaaab),
+ WRITE_COEF(0xbc, 0xaaaf), WRITE_COEF(0xbd, 0x6aaa), WRITE_COEF(0xbe, 0x1c02),
+ WRITE_COEF(0xc0, 0x00ff), WRITE_COEF(0xc1, 0x0fa6),
+ {}
+};
+
+static void alc668_restore_default_value(struct hda_codec *codec)
+{
+ alc_process_coef_fw(codec, alc668_coefs);
+}
+
enum {
ALC662_FIXUP_ASPIRE,
ALC662_FIXUP_LED_GPIO1,
{}
};
-static void alc662_fill_coef(struct hda_codec *codec)
-{
- int coef;
-
- coef = alc_get_coef0(codec);
-
- switch (codec->vendor_id) {
- case 0x10ec0662:
- if ((coef & 0x00f0) == 0x0030)
- alc_update_coef_idx(codec, 0x4, 1<<10, 0); /* EAPD Ctrl */
- break;
- case 0x10ec0272:
- case 0x10ec0273:
- case 0x10ec0663:
- case 0x10ec0665:
- case 0x10ec0670:
- case 0x10ec0671:
- case 0x10ec0672:
- alc_update_coef_idx(codec, 0xd, 0, 1<<14); /* EAPD Ctrl */
- break;
- }
-}
-
/*
*/
static int patch_alc662(struct hda_codec *codec)
alc_fix_pll_init(codec, 0x20, 0x04, 15);
- spec->init_hook = alc662_fill_coef;
- alc662_fill_coef(codec);
+ switch (codec->vendor_id) {
+ case 0x10ec0668:
+ spec->init_hook = alc668_restore_default_value;
+ break;
+ }
snd_hda_pick_fixup(codec, alc662_fixup_models,
alc662_fixup_tbl, alc662_fixups);
.driver = {
.name = "cs42l51",
.owner = THIS_MODULE,
+ .of_match_table = cs42l51_of_match,
},
.probe = cs42l51_i2c_probe,
.remove = cs42l51_i2c_remove,
}
EXPORT_SYMBOL_GPL(cs42l51_probe);
-static const struct of_device_id cs42l51_of_match[] = {
+const struct of_device_id cs42l51_of_match[] = {
{ .compatible = "cirrus,cs42l51", },
{ }
};
MODULE_DEVICE_TABLE(of, cs42l51_of_match);
+EXPORT_SYMBOL_GPL(cs42l51_of_match);
+
MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
MODULE_DESCRIPTION("Cirrus Logic CS42L51 ALSA SoC Codec Driver");
MODULE_LICENSE("GPL");
extern const struct regmap_config cs42l51_regmap;
int cs42l51_probe(struct device *dev, struct regmap *regmap);
+extern const struct of_device_id cs42l51_of_match[];
#define CS42L51_CHIP_ID 0x1B
#define CS42L51_CHIP_REV_A 0x00
#include "es8328.h"
static const struct i2c_device_id es8328_id[] = {
- { "everest,es8328", 0 },
+ { "es8328", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, es8328_id);
* 0x02 (when master clk is 20MHz to 40MHz)..
* 0x03 (when master clk is 40MHz to 60MHz)..
*/
- if ((freq >= 10000000) && (freq < 20000000)) {
+ if ((freq >= 10000000) && (freq <= 20000000)) {
snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
M98090_PSCLK_DIV1);
- } else if ((freq >= 20000000) && (freq < 40000000)) {
+ } else if ((freq > 20000000) && (freq <= 40000000)) {
snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
M98090_PSCLK_DIV2);
- } else if ((freq >= 40000000) && (freq < 60000000)) {
+ } else if ((freq > 40000000) && (freq <= 60000000)) {
snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
M98090_PSCLK_DIV4);
} else {
{ 0x76, 0x000a },
{ 0x77, 0x0c00 },
{ 0x78, 0x0000 },
+ { 0x79, 0x0123 },
{ 0x80, 0x0000 },
{ 0x81, 0x0000 },
{ 0x82, 0x0000 },
case RT5645_DMIC_CTRL2:
case RT5645_TDM_CTRL_1:
case RT5645_TDM_CTRL_2:
+ case RT5645_TDM_CTRL_3:
case RT5645_GLB_CLK:
case RT5645_PLL_CTRL1:
case RT5645_PLL_CTRL2:
{ 0x4c, 0x5380 },
{ 0x4f, 0x0073 },
{ 0x52, 0x00d3 },
- { 0x53, 0xf0f0 },
+ { 0x53, 0xf000 },
{ 0x61, 0x0000 },
{ 0x62, 0x0001 },
{ 0x63, 0x00c3 },
{ 0x64, 0x0000 },
- { 0x65, 0x0000 },
+ { 0x65, 0x0001 },
{ 0x66, 0x0000 },
{ 0x6f, 0x8000 },
{ 0x70, 0x8000 },
{ 0x71, 0x8000 },
{ 0x72, 0x8000 },
- { 0x73, 0x1110 },
+ { 0x73, 0x7770 },
{ 0x74, 0x0e00 },
{ 0x75, 0x1505 },
{ 0x76, 0x0015 },
{ 0x83, 0x0000 },
{ 0x84, 0x0000 },
{ 0x85, 0x0000 },
- { 0x86, 0x0008 },
+ { 0x86, 0x0004 },
{ 0x87, 0x0000 },
{ 0x88, 0x0000 },
{ 0x89, 0x0000 },
{ 0x8a, 0x0000 },
{ 0x8b, 0x0000 },
- { 0x8c, 0x0007 },
+ { 0x8c, 0x0003 },
{ 0x8d, 0x0000 },
{ 0x8e, 0x0004 },
{ 0x8f, 0x1100 },
{ 0x90, 0x0646 },
{ 0x91, 0x0c06 },
{ 0x93, 0x0000 },
- { 0x94, 0x0000 },
- { 0x95, 0x0000 },
+ { 0x94, 0x1270 },
+ { 0x95, 0x1000 },
{ 0x97, 0x0000 },
{ 0x98, 0x0000 },
{ 0x99, 0x0000 },
{ 0x9e, 0x0400 },
{ 0xae, 0x7000 },
{ 0xaf, 0x0000 },
- { 0xb0, 0x6000 },
+ { 0xb0, 0x7000 },
{ 0xb1, 0x0000 },
{ 0xb2, 0x0000 },
{ 0xb3, 0x001f },
- { 0xb4, 0x2206 },
+ { 0xb4, 0x220c },
{ 0xb5, 0x1f00 },
{ 0xb6, 0x0000 },
{ 0xb7, 0x0000 },
{ 0xcf, 0x1813 },
{ 0xd0, 0x0690 },
{ 0xd1, 0x1c17 },
- { 0xd3, 0xb320 },
+ { 0xd3, 0xa220 },
{ 0xd4, 0x0000 },
{ 0xd6, 0x0400 },
{ 0xd9, 0x0809 },
{ 0xda, 0x0000 },
{ 0xdb, 0x0001 },
{ 0xdc, 0x0049 },
- { 0xdd, 0x0009 },
+ { 0xdd, 0x0024 },
{ 0xe6, 0x8000 },
{ 0xe7, 0x0000 },
- { 0xec, 0xb300 },
+ { 0xec, 0xa200 },
{ 0xed, 0x0000 },
- { 0xee, 0xb300 },
+ { 0xee, 0xa200 },
{ 0xef, 0x0000 },
{ 0xf8, 0x0000 },
{ 0xf9, 0x0000 },
{ 0xfa, 0x8010 },
{ 0xfb, 0x0033 },
- { 0xfc, 0x0080 },
+ { 0xfc, 0x0100 },
};
static bool rt5670_volatile_register(struct device *dev, unsigned int reg)
{ "DAC1 MIXR", "DAC1 Switch", "DAC1 R Mux" },
{ "DAC1 MIXR", NULL, "DAC Stereo1 Filter" },
+ { "DAC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll },
+ { "DAC Mono Left Filter", NULL, "PLL1", is_sys_clk_from_pll },
+ { "DAC Mono Right Filter", NULL, "PLL1", is_sys_clk_from_pll },
+
{ "DAC MIX", NULL, "DAC1 MIXL" },
{ "DAC MIX", NULL, "DAC1 MIXR" },
{ "DAC L1", NULL, "DAC L1 Power" },
{ "DAC L1", NULL, "Stereo DAC MIXL" },
- { "DAC L1", NULL, "PLL1", is_sys_clk_from_pll },
{ "DAC R1", NULL, "DAC R1 Power" },
{ "DAC R1", NULL, "Stereo DAC MIXR" },
- { "DAC R1", NULL, "PLL1", is_sys_clk_from_pll },
{ "DAC L2", NULL, "Mono DAC MIXL" },
- { "DAC L2", NULL, "PLL1", is_sys_clk_from_pll },
{ "DAC R2", NULL, "Mono DAC MIXR" },
- { "DAC R2", NULL, "PLL1", is_sys_clk_from_pll },
{ "OUT MIXL", "BST1 Switch", "BST1" },
{ "OUT MIXL", "INL Switch", "INL VOL" },
/* enable small pop, introduce 400ms delay in turning off */
snd_soc_update_bits(codec, SGTL5000_CHIP_REF_CTRL,
- SGTL5000_SMALL_POP,
- SGTL5000_SMALL_POP);
+ SGTL5000_SMALL_POP, 1);
/* disable short cut detector */
snd_soc_write(codec, SGTL5000_CHIP_SHORT_CTRL, 0);
#define SGTL5000_BIAS_CTRL_MASK 0x000e
#define SGTL5000_BIAS_CTRL_SHIFT 1
#define SGTL5000_BIAS_CTRL_WIDTH 3
-#define SGTL5000_SMALL_POP 0x0001
+#define SGTL5000_SMALL_POP 0
/*
* SGTL5000_CHIP_MIC_CTRL
file, blocks, pos - firmware->size);
out_fw:
+ regmap_async_complete(regmap);
release_firmware(firmware);
wm_adsp_buf_free(&buf_list);
out:
}
}
+static struct reg_default fsl_asrc_reg[] = {
+ { REG_ASRCTR, 0x0000 }, { REG_ASRIER, 0x0000 },
+ { REG_ASRCNCR, 0x0000 }, { REG_ASRCFG, 0x0000 },
+ { REG_ASRCSR, 0x0000 }, { REG_ASRCDR1, 0x0000 },
+ { REG_ASRCDR2, 0x0000 }, { REG_ASRSTR, 0x0000 },
+ { REG_ASRRA, 0x0000 }, { REG_ASRRB, 0x0000 },
+ { REG_ASRRC, 0x0000 }, { REG_ASRPM1, 0x0000 },
+ { REG_ASRPM2, 0x0000 }, { REG_ASRPM3, 0x0000 },
+ { REG_ASRPM4, 0x0000 }, { REG_ASRPM5, 0x0000 },
+ { REG_ASRTFR1, 0x0000 }, { REG_ASRCCR, 0x0000 },
+ { REG_ASRDIA, 0x0000 }, { REG_ASRDOA, 0x0000 },
+ { REG_ASRDIB, 0x0000 }, { REG_ASRDOB, 0x0000 },
+ { REG_ASRDIC, 0x0000 }, { REG_ASRDOC, 0x0000 },
+ { REG_ASRIDRHA, 0x0000 }, { REG_ASRIDRLA, 0x0000 },
+ { REG_ASRIDRHB, 0x0000 }, { REG_ASRIDRLB, 0x0000 },
+ { REG_ASRIDRHC, 0x0000 }, { REG_ASRIDRLC, 0x0000 },
+ { REG_ASR76K, 0x0A47 }, { REG_ASR56K, 0x0DF3 },
+ { REG_ASRMCRA, 0x0000 }, { REG_ASRFSTA, 0x0000 },
+ { REG_ASRMCRB, 0x0000 }, { REG_ASRFSTB, 0x0000 },
+ { REG_ASRMCRC, 0x0000 }, { REG_ASRFSTC, 0x0000 },
+ { REG_ASRMCR1A, 0x0000 }, { REG_ASRMCR1B, 0x0000 },
+ { REG_ASRMCR1C, 0x0000 },
+};
+
static const struct regmap_config fsl_asrc_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = REG_ASRMCR1C,
+ .reg_defaults = fsl_asrc_reg,
+ .num_reg_defaults = ARRAY_SIZE(fsl_asrc_reg),
.readable_reg = fsl_asrc_readable_reg,
.volatile_reg = fsl_asrc_volatile_reg,
.writeable_reg = fsl_asrc_writeable_reg,
while (val) {
regmap_read(i2s->regmap, I2S_CLR, &val);
retry--;
- if (!retry)
+ if (!retry) {
dev_warn(i2s->dev, "fail to clear\n");
+ break;
+ }
}
}
}
{ .compatible = "google,snow-audio-max98095", },
{},
};
+MODULE_DEVICE_TABLE(of, snow_of_match);
static struct platform_driver snow_driver = {
.driver = {
static struct snd_pcm_hardware fsi_pcm_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE,
+ SNDRV_PCM_INFO_MMAP_VALID,
.buffer_bytes_max = 64 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8192,
static struct snd_pcm_hardware rsnd_pcm_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE,
+ SNDRV_PCM_INFO_MMAP_VALID,
.buffer_bytes_max = 64 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8192,
list_for_each_entry(component, &component_list, list) {
if (dlc->of_node && component->dev->of_node != dlc->of_node)
continue;
- if (dlc->name && strcmp(dev_name(component->dev), dlc->name))
+ if (dlc->name && strcmp(component->name, dlc->name))
continue;
list_for_each_entry(dai, &component->dai_list, list) {
if (dlc->dai_name && strcmp(dai->name, dlc->dai_name))
dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture);
}
+static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd);
+
+/* Set FE's runtime_update state; the state is protected via PCM stream lock
+ * for avoiding the race with trigger callback.
+ * If the state is unset and a trigger is pending while the previous operation,
+ * process the pending trigger action here.
+ */
+static void dpcm_set_fe_update_state(struct snd_soc_pcm_runtime *fe,
+ int stream, enum snd_soc_dpcm_update state)
+{
+ struct snd_pcm_substream *substream =
+ snd_soc_dpcm_get_substream(fe, stream);
+
+ snd_pcm_stream_lock_irq(substream);
+ if (state == SND_SOC_DPCM_UPDATE_NO && fe->dpcm[stream].trigger_pending) {
+ dpcm_fe_dai_do_trigger(substream,
+ fe->dpcm[stream].trigger_pending - 1);
+ fe->dpcm[stream].trigger_pending = 0;
+ }
+ fe->dpcm[stream].runtime_update = state;
+ snd_pcm_stream_unlock_irq(substream);
+}
+
static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
{
struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
struct snd_pcm_runtime *runtime = fe_substream->runtime;
int stream = fe_substream->stream, ret = 0;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
ret = dpcm_be_dai_startup(fe, fe_substream->stream);
if (ret < 0) {
dpcm_set_fe_runtime(fe_substream);
snd_pcm_limit_hw_rates(runtime);
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
return 0;
unwind:
dpcm_be_dai_startup_unwind(fe, fe_substream->stream);
be_err:
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
return ret;
}
struct snd_soc_pcm_runtime *fe = substream->private_data;
int stream = substream->stream;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
/* shutdown the BEs */
dpcm_be_dai_shutdown(fe, substream->stream);
dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
return 0;
}
int err, stream = substream->stream;
mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
dev_dbg(fe->dev, "ASoC: hw_free FE %s\n", fe->dai_link->name);
err = dpcm_be_dai_hw_free(fe, stream);
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
mutex_unlock(&fe->card->mutex);
return 0;
int ret, stream = substream->stream;
mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
memcpy(&fe->dpcm[substream->stream].hw_params, params,
sizeof(struct snd_pcm_hw_params));
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS;
out:
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
mutex_unlock(&fe->card->mutex);
return ret;
}
}
EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger);
-static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd)
+static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *fe = substream->private_data;
int stream = substream->stream, ret;
return ret;
}
+static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *fe = substream->private_data;
+ int stream = substream->stream;
+
+ /* if FE's runtime_update is already set, we're in race;
+ * process this trigger later at exit
+ */
+ if (fe->dpcm[stream].runtime_update != SND_SOC_DPCM_UPDATE_NO) {
+ fe->dpcm[stream].trigger_pending = cmd + 1;
+ return 0; /* delayed, assuming it's successful */
+ }
+
+ /* we're alone, let's trigger */
+ return dpcm_fe_dai_do_trigger(substream, cmd);
+}
+
int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream)
{
struct snd_soc_dpcm *dpcm;
dev_dbg(fe->dev, "ASoC: prepare FE %s\n", fe->dai_link->name);
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
/* there is no point preparing this FE if there are no BEs */
if (list_empty(&fe->dpcm[stream].be_clients)) {
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
out:
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
mutex_unlock(&fe->card->mutex);
return ret;
{
int ret;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE);
ret = dpcm_run_update_startup(fe, stream);
if (ret < 0)
dev_err(fe->dev, "ASoC: failed to startup some BEs\n");
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
return ret;
}
{
int ret;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE);
ret = dpcm_run_update_shutdown(fe, stream);
if (ret < 0)
dev_err(fe->dev, "ASoC: failed to shutdown some BEs\n");
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
return ret;
}
{
struct snd_card *card;
struct list_head *p;
+ bool was_shutdown;
if (chip == (void *)-1L)
return;
card = chip->card;
down_write(&chip->shutdown_rwsem);
+ was_shutdown = chip->shutdown;
chip->shutdown = 1;
up_write(&chip->shutdown_rwsem);
mutex_lock(®ister_mutex);
- chip->num_interfaces--;
- if (chip->num_interfaces <= 0) {
+ if (!was_shutdown) {
struct snd_usb_endpoint *ep;
snd_card_disconnect(card);
list_for_each(p, &chip->mixer_list) {
snd_usb_mixer_disconnect(p);
}
+ }
+
+ chip->num_interfaces--;
+ if (chip->num_interfaces <= 0) {
usb_chip[chip->index] = NULL;
mutex_unlock(®ister_mutex);
snd_card_free_when_closed(card);
cval->res = 1;
cval->initialized = 1;
- if (desc->bDescriptorSubtype == UAC2_CLOCK_SELECTOR)
- cval->control = UAC2_CX_CLOCK_SELECTOR;
- else
+ if (state->mixer->protocol == UAC_VERSION_1)
cval->control = 0;
+ else /* UAC_VERSION_2 */
+ cval->control = (desc->bDescriptorSubtype == UAC2_CLOCK_SELECTOR) ?
+ UAC2_CX_CLOCK_SELECTOR : UAC2_SU_SELECTOR;
namelist = kmalloc(sizeof(char *) * desc->bNrInPins, GFP_KERNEL);
if (!namelist) {
if (mixer->chip->shutdown)
ret = -ENODEV;
else
- ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), bRequest,
+ ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), bRequest,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
0, wIndex,
- &tmp, sizeof(tmp), 1000);
+ &tmp, sizeof(tmp));
up_read(&mixer->chip->shutdown_rwsem);
if (ret < 0) {
return changed;
}
+static void kctl_private_value_free(struct snd_kcontrol *kctl)
+{
+ kfree((void *)kctl->private_value);
+}
+
static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer,
int validx, int bUnitID)
{
return -ENOMEM;
}
+ kctl->private_free = kctl_private_value_free;
err = snd_ctl_add(mixer->chip->card, kctl);
if (err < 0)
return err;
if ((le16_to_cpu(dev->descriptor.idVendor) == 0x23ba) &&
(requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
mdelay(20);
+
+ /* Marantz/Denon devices with USB DAC functionality need a delay
+ * after each class compliant request
+ */
+ if ((le16_to_cpu(dev->descriptor.idVendor) == 0x154e) &&
+ (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) {
+
+ switch (le16_to_cpu(dev->descriptor.idProduct)) {
+ case 0x3005: /* Marantz HD-DAC1 */
+ case 0x3006: /* Marantz SA-14S1 */
+ mdelay(20);
+ break;
+ }
+ }
}
/*
/* iFi Audio micro/nano iDSD */
case USB_ID(0x20b1, 0x3008):
if (fp->altsetting == 2)
- return SNDRV_PCM_FMTBIT_DSD_U32_LE;
+ return SNDRV_PCM_FMTBIT_DSD_U32_BE;
break;
/* DIYINHK DSD DXD 384kHz USB to I2S/DSD */
case USB_ID(0x20b1, 0x2009):
if (fp->altsetting == 3)
- return SNDRV_PCM_FMTBIT_DSD_U32_LE;
+ return SNDRV_PCM_FMTBIT_DSD_U32_BE;
break;
default:
break;
#define MOVE_MEDIUM 0xa5
#define EXCHANGE_MEDIUM 0xa6
#define READ_12 0xa8
+#define SERVICE_ACTION_OUT_12 0xa9
#define WRITE_12 0xaa
-#define READ_MEDIA_SERIAL_NUMBER 0xab
+#define SERVICE_ACTION_IN_12 0xab
#define WRITE_VERIFY_12 0xae
#define VERIFY_12 0xaf
#define SEARCH_HIGH_12 0xb0
#define VERIFY_16 0x8f
#define SYNCHRONIZE_CACHE_16 0x91
#define WRITE_SAME_16 0x93
-#define SERVICE_ACTION_IN 0x9e
+#define SERVICE_ACTION_BIDIRECTIONAL 0x9d
+#define SERVICE_ACTION_IN_16 0x9e
+#define SERVICE_ACTION_OUT_16 0x9f
/* values for service action in */
#define SAI_READ_CAPACITY_16 0x10
#define SAI_GET_LBA_STATUS 0x12
return scsi_trace_rw16(p, cdb, len);
case UNMAP:
return scsi_trace_unmap(p, cdb, len);
- case SERVICE_ACTION_IN:
+ case SERVICE_ACTION_IN_16:
return scsi_trace_service_action_in(p, cdb, len);
case VARIABLE_LENGTH_CMD:
return scsi_trace_varlen(p, cdb, len);
}
# Parameters
-DEBUGFS_DIR=`grep debugfs /proc/mounts | cut -f2 -d' '`
+DEBUGFS_DIR=`grep debugfs /proc/mounts | cut -f2 -d' ' | head -1`
TRACING_DIR=$DEBUGFS_DIR/tracing
TOP_DIR=`absdir $0`
TEST_DIR=$TOP_DIR/test.d
struct tpacket2_hdr *header = ring;
int count = 0;
- while (header->tp_status & TP_STATUS_USER && count < RING_NUM_FRAMES) {
+ while (count < RING_NUM_FRAMES && header->tp_status & TP_STATUS_USER) {
count++;
header = ring + (count * getpagesize());
}
int kvm_vgic_create(struct kvm *kvm)
{
- int i, vcpu_lock_idx = -1, ret = 0;
+ int i, vcpu_lock_idx = -1, ret;
struct kvm_vcpu *vcpu;
mutex_lock(&kvm->lock);
* vcpu->mutex. By grabbing the vcpu->mutex of all VCPUs we ensure
* that no other VCPUs are run while we create the vgic.
*/
+ ret = -EBUSY;
kvm_for_each_vcpu(i, vcpu, kvm) {
if (!mutex_trylock(&vcpu->mutex))
goto out_unlock;
}
kvm_for_each_vcpu(i, vcpu, kvm) {
- if (vcpu->arch.has_run_once) {
- ret = -EBUSY;
+ if (vcpu->arch.has_run_once)
goto out_unlock;
- }
}
+ ret = 0;
spin_lock_init(&kvm->arch.vgic.lock);
kvm->arch.vgic.in_kernel = true;
static bool largepages_enabled = true;
-bool kvm_is_mmio_pfn(pfn_t pfn)
+bool kvm_is_reserved_pfn(pfn_t pfn)
{
if (pfn_valid(pfn))
- return !is_zero_pfn(pfn) && PageReserved(pfn_to_page(pfn));
+ return PageReserved(pfn_to_page(pfn));
return true;
}
else if ((vma->vm_flags & VM_PFNMAP)) {
pfn = ((addr - vma->vm_start) >> PAGE_SHIFT) +
vma->vm_pgoff;
- BUG_ON(!kvm_is_mmio_pfn(pfn));
+ BUG_ON(!kvm_is_reserved_pfn(pfn));
} else {
if (async && vma_is_valid(vma, write_fault))
*async = true;
if (is_error_noslot_pfn(pfn))
return KVM_ERR_PTR_BAD_PAGE;
- if (kvm_is_mmio_pfn(pfn)) {
+ if (kvm_is_reserved_pfn(pfn)) {
WARN_ON(1);
return KVM_ERR_PTR_BAD_PAGE;
}
void kvm_release_pfn_clean(pfn_t pfn)
{
- if (!is_error_noslot_pfn(pfn) && !kvm_is_mmio_pfn(pfn))
+ if (!is_error_noslot_pfn(pfn) && !kvm_is_reserved_pfn(pfn))
put_page(pfn_to_page(pfn));
}
EXPORT_SYMBOL_GPL(kvm_release_pfn_clean);
void kvm_set_pfn_dirty(pfn_t pfn)
{
- if (!kvm_is_mmio_pfn(pfn)) {
+ if (!kvm_is_reserved_pfn(pfn)) {
struct page *page = pfn_to_page(pfn);
if (!PageReserved(page))
SetPageDirty(page);
void kvm_set_pfn_accessed(pfn_t pfn)
{
- if (!kvm_is_mmio_pfn(pfn))
+ if (!kvm_is_reserved_pfn(pfn))
mark_page_accessed(pfn_to_page(pfn));
}
EXPORT_SYMBOL_GPL(kvm_set_pfn_accessed);
void kvm_get_pfn(pfn_t pfn)
{
- if (!kvm_is_mmio_pfn(pfn))
+ if (!kvm_is_reserved_pfn(pfn))
get_page(pfn_to_page(pfn));
}
EXPORT_SYMBOL_GPL(kvm_get_pfn);